com.epiq.bitshark.ui.FrequencyDomainPanel.java Source code

Java tutorial

Introduction

Here is the source code for com.epiq.bitshark.ui.FrequencyDomainPanel.java

Source

/*  +--------------------------+
 *  | FMC-1RX Graphing Utility |
 *  +--------------------------+
 *
 *  Copyright (C) 2011 Epiq Solutions
 *                     epiq-solutions.com
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License along
 *  with this program; if not, write to the Free Software Foundation, Inc.,
 *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 *  ===========================================================================
 *
 *  Revision History
 *  ----------------
 *  01/01/2011 : Created file
 *  01/09/2011 : Changed package name
 */

package com.epiq.bitshark.ui;

import com.epiq.bitshark.Common;
import com.epiq.bitshark.client.FMCUartClient;
import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.MouseInfo;
import java.awt.Point;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.awt.geom.Rectangle2D;
import javax.swing.DefaultComboBoxModel;
import javax.swing.DefaultListCellRenderer;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.plaf.basic.BasicLabelUI;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartMouseEvent;
import org.jfree.chart.ChartMouseListener;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.annotations.XYTitleAnnotation;
import org.jfree.chart.block.BlockBorder;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.PlotRenderingInfo;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYItemRenderer;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.chart.title.TextTitle;
import org.jfree.data.Range;
import org.jfree.data.xy.XYDataItem;
import org.jfree.data.xy.XYSeriesCollection;
import org.jfree.ui.RectangleAnchor;
import org.jfree.ui.RectangleEdge;
import org.jfree.ui.RectangleInsets;

/**
 *
 * @author Epiq Solutions
 */
public class FrequencyDomainPanel extends JPanel {

    private javax.swing.JLabel alphaLabel;
    private javax.swing.JSlider alphaSlider;
    private javax.swing.JPanel chartLabelSpacerPanel;
    private javax.swing.JLabel graphLabel;
    private org.jdesktop.swingx.JXPanel headerPanel;
    private javax.swing.JPanel leftSpacerPanel;
    private javax.swing.JPanel mainPanel;
    private javax.swing.JPanel rightSpacerPanel;
    private javax.swing.JPanel topLeftHeaderPanel;
    private javax.swing.JPanel topPanel;
    private javax.swing.JPanel topRightControlPanel;
    private javax.swing.JPanel topRightHeaderPanel;
    private javax.swing.JComboBox windowComboBox;
    private javax.swing.JLabel windowLabel;

    private ChartPanel chartPanel = null;
    private BasicSeries series;
    private FrequencyAxis frequencyAxis = null;
    private PowerAxis powerAxis = null;

    private XYPlot plot = null;
    private FrequencyDomainMouseMarker markerAnnotation = new FrequencyDomainMouseMarker();

    private TextTitle markerTitle = new TextTitle("Test Annotation");
    private XYTitleAnnotation markerTextAnnotation = new XYTitleAnnotation(.01, .95, markerTitle,
            RectangleAnchor.TOP_LEFT);

    private long centerFrequency = 1;
    private int sampleRate = 1;

    private WindowFunction currentWindow = WindowFunction.RECTANGULAR;

    private double previousMarkerX = 0;
    private float[] averageFft = null;
    private float currentAlpha = 1.0f;
    private int fftSize = 1;

    private boolean markerLocked = false;

    /** Creates new form FrequencyDomainPanel */
    public FrequencyDomainPanel() {
        initComponents();

        initGraph();

        headerPanel.setBackgroundPainter(Common.getHeaderPainter());

        JPanel holderPanel = new JPanel() {
            @Override
            public void paint(Graphics g) {

                if (plot.getDomainAxis().isAutoRange()) {
                    plot.getDomainAxis().setAutoRange(false);
                    Range newDomain = new Range(0, FMCUartClient.BLOCK_SIZE - 1);
                    plot.getDomainAxis().setRange(newDomain, true, false);
                }

                if (plot.getRangeAxis().isAutoRange()) {
                    plot.getRangeAxis().setAutoRange(false);
                    Range newRange = new Range(-50, 70);
                    plot.getRangeAxis().setRange(newRange, true, false);
                }

                super.paint(g);
            }
        };
        holderPanel.setLayout(new BorderLayout());
        holderPanel.add(chartPanel, BorderLayout.CENTER);
        this.mainPanel.add(holderPanel, BorderLayout.CENTER);

        windowComboBox.setModel(new DefaultComboBoxModel(WindowFunction.values()));
        windowComboBox.setSelectedItem(WindowFunction.BLACKMAN_HARRIS);
        windowComboBox.setRenderer(new DefaultListCellRenderer() {

            @Override
            public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected,
                    boolean cellHasFocus) {

                Component c = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
                if (c instanceof JLabel && value instanceof WindowFunction) {
                    JLabel l = (JLabel) c;
                    WindowFunction cw = (WindowFunction) value;
                    l.setText(cw.getName());
                }

                return c;
            }

        });

        graphLabel.setUI(new BasicLabelUI());
        alphaLabel.setUI(new BasicLabelUI());
        windowLabel.setUI(new BasicLabelUI());
    }

    /**
     * Clears the graph
     */
    public void clear() {
        this.series.clear();
    }

    /**
     * Updates the center frequency and sample rate of the data
     * @param centerFrequency
     * @param sampleRate
     */
    public void setParameters(final long centerFrequency, final int sampleRate) {
        frequencyAxis.setParameters(centerFrequency, sampleRate);

        this.centerFrequency = centerFrequency;
        this.sampleRate = sampleRate;
    }

    /**
     *
     * @param iq
     */
    public void setIq(short[] iq) {

        float[] powers = null;

        if (currentWindow != null) {
            float[] windowed = currentWindow.apply(iq);
            powers = Common.calculateFftPowers(windowed);
        } else {
            powers = Common.calculateFftPowers(WindowFunction.RECTANGULAR.apply(iq));

        }

        setFFT(powers);
    }

    /**
     *
     * @param fft
     */
    public void setFFT(float[] fft) {

        if (averageFft != null) {
            Common.calculateFftAveragePowers(fft, averageFft, currentAlpha);
        } else {
            averageFft = fft;
        }

        this.series.setData(averageFft);
        this.fftSize = fft.length;

        updateMouseMarker(); // update marker for new powers
    }

    /**
     * Initialize the graph
     */
    private void initGraph() {

        this.series = new BasicSeries("FFT");
        XYSeriesCollection dataset = new XYSeriesCollection(series);

        JFreeChart graph = ChartFactory.createXYLineChart(null, // title
                "", // no x-axis label
                "", // no y-axis label
                dataset, // data
                PlotOrientation.VERTICAL, false, // no legend
                false, // no tooltips
                false // no URLs
        );

        graph.setBorderVisible(false);
        graph.setPadding(new RectangleInsets(-5, 0, 0, 0));
        graph.setBackgroundPaint(null);
        graph.setBackgroundImageAlpha(0.0f);
        graph.setAntiAlias(true);

        plot = (XYPlot) graph.getPlot();

        powerAxis = new PowerAxis();
        plot.setRangeAxis(powerAxis);

        frequencyAxis = new FrequencyAxis();
        frequencyAxis.setTickLabelInsets(new RectangleInsets(10, 0, 0, 0));
        plot.setDomainAxis(frequencyAxis);

        XYItemRenderer r = plot.getRenderer();
        if (r instanceof XYLineAndShapeRenderer) {
            XYLineAndShapeRenderer renderer = (XYLineAndShapeRenderer) r;
            renderer.setBaseShapesVisible(false);
            renderer.setBaseShapesFilled(true);
            renderer.setSeriesPaint(0, Common.EPIQ_GREEN);
            renderer.setSeriesStroke(0, new BasicStroke(1.0f));
        }

        plot.setBackgroundAlpha(0.0f);
        plot.setBackgroundPaint(null);
        plot.setDomainGridlinePaint(Color.LIGHT_GRAY);
        plot.setRangeGridlinePaint(Color.LIGHT_GRAY);
        plot.setAxisOffset(new RectangleInsets(0, 0, 0, 0));

        // X-axis setup
        plot.getDomainAxis().setAutoRange(false);
        plot.getDomainAxis().setVisible(true);
        plot.getDomainAxis().setRange(0, FMCUartClient.BLOCK_SIZE - 1);
        plot.setDomainCrosshairVisible(false);
        plot.setDomainCrosshairPaint(new Color(46, 46, 46));

        // Y-axis setup
        plot.getRangeAxis().setAutoRange(false);
        plot.getRangeAxis().setVisible(true);
        plot.getRangeAxis().setRange(-50, 70);
        plot.setRangeCrosshairVisible(false);

        markerAnnotation.setVisible(false);
        plot.addAnnotation(markerAnnotation);
        plot.addAnnotation(markerTextAnnotation);

        this.markerTitle.setFont(new Font("Tahoma", Font.BOLD, 12));
        this.markerTitle.setFrame(new BlockBorder(new Color(46, 46, 46)));
        this.markerTitle.setBackgroundPaint(new Color(200, 200, 255, 100));
        this.markerTitle.setText("");
        this.markerTitle.setPadding(5, 15, 5, 15);
        this.markerTitle.setExpandToFitSpace(false);
        this.markerTitle.setBounds(new Rectangle2D.Double(0, 0, 100, 30));

        chartPanel = new ChartPanel(graph, false);
        chartPanel.setMaximumDrawWidth(Integer.MAX_VALUE);
        chartPanel.setMaximumDrawHeight(Integer.MAX_VALUE);
        chartPanel.setMinimumDrawWidth(0);
        chartPanel.setMinimumDrawHeight(0);
        chartPanel.setMouseZoomable(true);
        chartPanel.setOpaque(false);

        chartPanel.addChartMouseListener(new ChartMouseListener() {

            @Override
            public void chartMouseClicked(ChartMouseEvent event) {
                markerLocked = !markerLocked;
            }

            @Override
            public void chartMouseMoved(ChartMouseEvent event) {
                updateMouseMarker(event);
            }

        });

        chartPanel.addMouseListener(new MouseListener() {
            @Override
            public void mouseClicked(MouseEvent e) {
            }

            @Override
            public void mousePressed(MouseEvent e) {
            }

            @Override
            public void mouseReleased(MouseEvent e) {
            }

            @Override
            public void mouseEntered(MouseEvent e) {
            }

            @Override
            public void mouseExited(MouseEvent e) {
                if (!markerLocked) {
                    markerAnnotation.setVisible(false);
                    plot.setDomainCrosshairVisible(false);
                    markerTitle.setVisible(false);
                    markerTitle.setText("");
                }
            }

        });

        chartPanel.addMouseWheelListener(new MouseWheelListener() {
            @Override
            public void mouseWheelMoved(MouseWheelEvent e) {
                int clicks = e.getWheelRotation();
                plot.getRangeAxis().setUpperBound(plot.getRangeAxis().getUpperBound() + clicks);
                plot.getRangeAxis().setLowerBound(plot.getRangeAxis().getLowerBound() + clicks);
            }
        });

    }

    /**
     *
     */
    protected void updateMouseMarker() {
        if (markerAnnotation.isVisible()) {
            Point screenPoint = MouseInfo.getPointerInfo().getLocation();
            SwingUtilities.convertPointFromScreen(screenPoint, chartPanel);
            updateMouseMarker(screenPoint);
        }
    }

    /**
     *
     * @param event
     */
    protected void updateMouseMarker(ChartMouseEvent event) {
        updateMouseMarker(event.getTrigger().getPoint());
    }

    /**
     * 
     * @param point
     */
    protected void updateMouseMarker(Point point) {

        double xValue = 0;

        if (!markerLocked) {
            double pointerX = point.getX();

            PlotRenderingInfo renderingInfo = chartPanel.getChartRenderingInfo().getPlotInfo();
            Rectangle2D dataArea = renderingInfo.getDataArea();
            Rectangle2D plotArea = renderingInfo.getPlotArea();
            RectangleEdge domainEdge = plot.getDomainAxisEdge();

            xValue = frequencyAxis.java2DToValue(pointerX, dataArea, domainEdge);

            previousMarkerX = xValue;

        } else {
            xValue = previousMarkerX;
        }

        int bin = (int) Math.round(xValue);

        try {
            XYDataItem item = series.getDataItem(bin);
            if (item != null) {
                double yValue = item.getYValue();
                markerAnnotation.setXY(xValue, yValue);
                markerAnnotation.setVisible(true);

                plot.setDomainCrosshairValue(xValue);
                plot.setDomainCrosshairVisible(true);

                markerTitle.setText("Frequency: " + binToFrequencyStr(bin) + " \nPower: "
                        + String.format("%5.1f", PowerAxis.valueToDb(yValue)) + " dB");

                markerTitle.setVisible(true);
            } else {
                markerAnnotation.setVisible(false);
                plot.setDomainCrosshairVisible(false);
            }
        } catch (Exception e) {
            markerAnnotation.setVisible(false);
            plot.setDomainCrosshairVisible(false);
        }
    }

    /**
     *
     * @param bin
     * @return
     */
    public long binToFrequency(int bin) {
        double binWidth = (double) sampleRate / (double) fftSize;
        long freq = (long) (centerFrequency - (sampleRate / 2.0) + (binWidth * bin));
        return freq;
    }

    /**
     *
     * @param bin
     * @return
     */
    public String binToFrequencyStr(int bin) {
        double freqMhz = (double) (binToFrequency(bin) / Math.pow(10, 6));
        return String.format("%.6f MHz", freqMhz);
    }

    /**
     * This was originally generated code so it still
     * needs some cleanup.
     */
    private void initComponents() {

        topPanel = new javax.swing.JPanel();
        headerPanel = new org.jdesktop.swingx.JXPanel();
        topLeftHeaderPanel = new javax.swing.JPanel();
        graphLabel = new javax.swing.JLabel();
        chartLabelSpacerPanel = new javax.swing.JPanel();
        topRightHeaderPanel = new javax.swing.JPanel();
        topRightControlPanel = new javax.swing.JPanel();
        alphaLabel = new javax.swing.JLabel();
        alphaSlider = new javax.swing.JSlider();
        windowLabel = new javax.swing.JLabel();
        windowComboBox = new javax.swing.JComboBox();
        rightSpacerPanel = new javax.swing.JPanel();
        leftSpacerPanel = new javax.swing.JPanel();
        mainPanel = new javax.swing.JPanel();

        setLayout(new java.awt.BorderLayout());

        topPanel.setPreferredSize(new java.awt.Dimension(0, 30));
        topPanel.setLayout(new java.awt.BorderLayout());

        headerPanel.setBackground(new java.awt.Color(0, 0, 0));
        headerPanel.setBorder(javax.swing.BorderFactory.createLineBorder(new java.awt.Color(121, 123, 125)));
        headerPanel.setPreferredSize(new java.awt.Dimension(0, 30));
        headerPanel.setLayout(new java.awt.BorderLayout());

        topLeftHeaderPanel.setOpaque(false);
        topLeftHeaderPanel.setLayout(new java.awt.BorderLayout());

        graphLabel.setFont(new java.awt.Font("Tahoma", 1, 12));
        graphLabel.setText("Frequency Domain");
        graphLabel.setForeground(new java.awt.Color(255, 255, 255));
        topLeftHeaderPanel.add(graphLabel, java.awt.BorderLayout.CENTER);

        chartLabelSpacerPanel.setOpaque(false);
        chartLabelSpacerPanel.setPreferredSize(new java.awt.Dimension(10, 0));
        topLeftHeaderPanel.add(chartLabelSpacerPanel, java.awt.BorderLayout.WEST);

        headerPanel.add(topLeftHeaderPanel, java.awt.BorderLayout.WEST);

        topRightHeaderPanel.setOpaque(false);
        topRightHeaderPanel.setLayout(new java.awt.BorderLayout());

        topRightControlPanel.setOpaque(false);
        topRightControlPanel.setLayout(new java.awt.FlowLayout(java.awt.FlowLayout.CENTER, 10, 2));

        alphaLabel.setText("Alpha:");
        alphaLabel.setForeground(new java.awt.Color(255, 255, 255));
        topRightControlPanel.add(alphaLabel);

        alphaSlider.setMaximum(10000);
        alphaSlider.setOpaque(false);
        alphaSlider.addChangeListener(new javax.swing.event.ChangeListener() {
            public void stateChanged(javax.swing.event.ChangeEvent evt) {
                alphaSliderStateChanged(evt);
            }
        });
        topRightControlPanel.add(alphaSlider);

        windowLabel.setText("Window:");
        windowLabel.setForeground(new java.awt.Color(255, 255, 255));
        topRightControlPanel.add(windowLabel);

        windowComboBox.setModel(
                new javax.swing.DefaultComboBoxModel(new String[] { "Item 1", "Item 2", "Item 3", "Item 4" }));
        windowComboBox.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                windowComboBoxActionPerformed(evt);
            }
        });
        topRightControlPanel.add(windowComboBox);

        topRightHeaderPanel.add(topRightControlPanel, java.awt.BorderLayout.EAST);

        headerPanel.add(topRightHeaderPanel, java.awt.BorderLayout.EAST);

        topPanel.add(headerPanel, java.awt.BorderLayout.CENTER);

        rightSpacerPanel.setPreferredSize(new java.awt.Dimension(7, 0));
        topPanel.add(rightSpacerPanel, java.awt.BorderLayout.EAST);

        leftSpacerPanel.setOpaque(false);
        leftSpacerPanel.setPreferredSize(new java.awt.Dimension(44, 0));
        topPanel.add(leftSpacerPanel, java.awt.BorderLayout.WEST);

        add(topPanel, java.awt.BorderLayout.NORTH);

        mainPanel.setLayout(new java.awt.BorderLayout());
        add(mainPanel, java.awt.BorderLayout.CENTER);
    }

    private void windowComboBoxActionPerformed(java.awt.event.ActionEvent evt) {
        currentWindow = (WindowFunction) windowComboBox.getSelectedItem();
    }

    private void alphaSliderStateChanged(javax.swing.event.ChangeEvent evt) {
        currentAlpha = 1.0f - (float) ((float) alphaSlider.getValue() / (float) alphaSlider.getMaximum());
    }
}