org.rdv.viz.spectrum.SpectrumViz.java Source code

Java tutorial

Introduction

Here is the source code for org.rdv.viz.spectrum.SpectrumViz.java

Source

/*
 * RDV
 * Real-time Data Viewer
 * http://rdv.googlecode.com/
 * 
 * Copyright (c) 2008 Palta Software
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 * 
 * $URL$
 * $Revision$
 * $Date$
 * $Author$
 */

package org.rdv.viz.spectrum;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.EnumSet;

import javax.swing.BorderFactory;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JTextField;
import javax.swing.SpringLayout;
import javax.swing.SwingUtilities;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.rdv.data.Channel;
import org.rdv.datapanel.AbstractDataPanel;
import org.rdv.ui.SpringUtilities;

import com.rbnb.sapi.ChannelMap;

/**
 * A spectrum analyzer.
 * 
 * @author Jason P. Hanley
 */
public class SpectrumViz extends AbstractDataPanel {

    /** the logger for this class */
    static Log log = LogFactory.getLog(SpectrumViz.class.getName());

    /** the data panel property for the sample rate */
    private static final String DATA_PANEL_PROPERTY_SAMPLE_RATE = "sampleRate";

    /** the data panel property for the number of samples */
    private static final String DATA_PANEL_PROPERTY_NUMBER_OF_SAMPLES = "numberOfSamples";

    /** the data panel property for the window function */
    private static final String DATA_PANEL_PROPERTY_WINDOW_FUNCTION = "windowFunction";

    /** the data panel property for the segment size */
    private static final String DATA_PANEL_PROPERTY_SEGMENT_SIZE = "segmentSize";

    /** the data panel property for the overlap */
    private static final String DATA_PANEL_PROPERTY_OVERLAP = "overlap";

    /** the data panel property to control the visiblity of the properties panel */
    private static final String DATA_PANEL_PROPERTY_SHOW_PROPERTIES = "showProperties";

    /** the time for the last data point displayed */
    private double lastTimeDisplayed;

    /** the main panel */
    private JPanel panel;

    /** the spectrum analyzer panel */
    private SpectrumAnalyzerPanel spectrumAnalyzerPanel;

    /** the properties panel */
    private JPanel propertiesPanel;

    /** the sample rate text field */
    private JTextField sampleRateTextField;

    /** the number of samples text field */
    private JTextField numberOfSamplesTextField;

    /** the window function combo box */
    private JComboBox windowFunctionComboBox;

    /** the segment size text field */
    private JTextField segmentSizeTextField;;

    /** the overlap text field */
    private JTextField overlapTextField;

    /** the show properties menu item */
    private JCheckBoxMenuItem showPropertiesMenuItem;

    /**
     * Creates the spectrum analyzer data panel.
     */
    public SpectrumViz() {
        super();

        lastTimeDisplayed = -1;

        initComponents();

        setDataComponent(panel);
    }

    /**
     * Initializes the UI components.
     */
    private void initComponents() {
        panel = new JPanel();
        panel.setLayout(new BorderLayout());

        initSpectrumAnalyzerPanel();

        initPropertiesPanel();

        initPopupMenu();
    }

    /**
     * Initializes the spectrum analyzer panel.
     */
    private void initSpectrumAnalyzerPanel() {
        spectrumAnalyzerPanel = new SpectrumAnalyzerPanel();
        spectrumAnalyzerPanel.addPropertyChangeListener(new PropertyChangeListener() {
            public void propertyChange(PropertyChangeEvent pce) {
                hanelSpectrumAnalyzerPanelPropertyChanges(pce);
            }
        });

        panel.add(spectrumAnalyzerPanel, BorderLayout.CENTER);
    }

    /**
     * Initializes the properties panel.
     */
    private void initPropertiesPanel() {
        propertiesPanel = new JPanel();
        propertiesPanel.setLayout(new SpringLayout());
        propertiesPanel.setBorder(BorderFactory.createTitledBorder("Properties"));

        ActionListener actionListener = new ActionListener() {
            public void actionPerformed(ActionEvent ae) {
                handlePropertiesUpdate((Component) ae.getSource());
            }
        };

        FocusAdapter focusListener = new FocusAdapter() {
            public void focusLost(FocusEvent fe) {
                handlePropertiesUpdate(fe.getComponent());
            }
        };

        propertiesPanel.add(new JLabel("Sample rate: "));
        sampleRateTextField = new JTextField(Double.toString(spectrumAnalyzerPanel.getSampleRate()));
        sampleRateTextField.addActionListener(actionListener);
        sampleRateTextField.addFocusListener(focusListener);
        propertiesPanel.add(sampleRateTextField);

        propertiesPanel.add(new JLabel("Number of points: "));
        numberOfSamplesTextField = new JTextField(Integer.toString(spectrumAnalyzerPanel.getNumberOfSamples()));
        numberOfSamplesTextField.addActionListener(actionListener);
        numberOfSamplesTextField.addFocusListener(focusListener);
        propertiesPanel.add(numberOfSamplesTextField);

        propertiesPanel.add(new JLabel("Window: "));
        Object[] windowTypes = EnumSet.allOf(WindowFunction.class).toArray();
        windowFunctionComboBox = new JComboBox(windowTypes);
        windowFunctionComboBox.setSelectedItem(spectrumAnalyzerPanel.getWindowFunction());
        windowFunctionComboBox.addActionListener(actionListener);
        propertiesPanel.add(windowFunctionComboBox);

        propertiesPanel.add(new JLabel("Size: "));
        segmentSizeTextField = new JTextField(Integer.toString(spectrumAnalyzerPanel.getSegmentSize()));
        segmentSizeTextField.addActionListener(actionListener);
        segmentSizeTextField.addFocusListener(focusListener);
        propertiesPanel.add(segmentSizeTextField);

        propertiesPanel.add(new JLabel("Overlap: "));
        overlapTextField = new JTextField(Integer.toString(spectrumAnalyzerPanel.getOverlap()));
        overlapTextField.addActionListener(actionListener);
        overlapTextField.addFocusListener(focusListener);
        propertiesPanel.add(overlapTextField);

        SpringUtilities.makeCompactGrid(propertiesPanel, 5, 2, 5, 5, 5, 5);

        panel.add(propertiesPanel, BorderLayout.EAST);
    }

    /**
     * Initializes the popup menu. This takes the popup menu from the chart and
     * adds on to it.
     */
    private void initPopupMenu() {
        JPopupMenu popupMenu = spectrumAnalyzerPanel.getPopupMenu();

        JMenuItem propertiesMenuItem = (JMenuItem) popupMenu.getComponent(0);
        propertiesMenuItem.setText("Chart Properties...");

        popupMenu.add(new JPopupMenu.Separator());

        showPropertiesMenuItem = new JCheckBoxMenuItem("Show properties", true);
        showPropertiesMenuItem.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                setPropertiesVisible(showPropertiesMenuItem.isSelected());
            }
        });
        popupMenu.add(showPropertiesMenuItem);

        panel.setComponentPopupMenu(popupMenu);
    }

    /**
     * Handle events from the properties panel.
     * 
     * @param source  the source of the event
     */
    private void handlePropertiesUpdate(Component source) {
        if (source == null)
            return;

        if (source.equals(sampleRateTextField)) {
            handleSampleRateUpdate();
        } else if (source.equals(numberOfSamplesTextField)) {
            handleNumberOfSamplesUpdate();
        } else if (source.equals(windowFunctionComboBox)) {
            handleWindowFunctionUpdate();
        } else if (source.equals(segmentSizeTextField)) {
            handleSegmentSizeUpdate();
        } else if (source.equals(overlapTextField)) {
            handleOverlapUpdate();
        }
    }

    /**
     * Handles events from the sample rate component.
     */
    private void handleSampleRateUpdate() {
        try {
            double sampleRate = Double.parseDouble(sampleRateTextField.getText());
            spectrumAnalyzerPanel.setSampleRate(sampleRate);
        } catch (Exception e) {
            sampleRateTextField.setText(Double.toString(spectrumAnalyzerPanel.getSampleRate()));
            return;
        }
    }

    /**
     * Handles events from the number of samples component.
     */
    private void handleNumberOfSamplesUpdate() {
        try {
            int numberOfSamples = Integer.parseInt(numberOfSamplesTextField.getText());
            spectrumAnalyzerPanel.setNumberOfSamples(numberOfSamples);
        } catch (Exception e) {
            numberOfSamplesTextField.setText(Integer.toString(spectrumAnalyzerPanel.getNumberOfSamples()));
            return;
        }
    }

    /**
     * Handles events from the window function component.
     */
    private void handleWindowFunctionUpdate() {
        WindowFunction windowFunction = (WindowFunction) windowFunctionComboBox.getSelectedItem();
        spectrumAnalyzerPanel.setWindowFunction(windowFunction);
    }

    /**
     * Handles events from the segment size component.
     */
    private void handleSegmentSizeUpdate() {
        try {
            int segmentSize = Integer.parseInt(segmentSizeTextField.getText());
            spectrumAnalyzerPanel.setSegmentSize(segmentSize);
        } catch (Exception e) {
            segmentSizeTextField.setText(Integer.toString(spectrumAnalyzerPanel.getSegmentSize()));
            return;
        }
    }

    /**
     * Handles events from the overlap component.
     */
    private void handleOverlapUpdate() {
        try {
            int overlap = Integer.parseInt(overlapTextField.getText());
            spectrumAnalyzerPanel.setOverlap(overlap);
        } catch (Exception e) {
            overlapTextField.setText(Integer.toString(spectrumAnalyzerPanel.getOverlap()));
            return;
        }
    }

    /**
     * Hanles property change events from the spectrum analyzer panel.
     * 
     * @param pce  the property change event
     */
    private void hanelSpectrumAnalyzerPanelPropertyChanges(PropertyChangeEvent pce) {
        String propertyName = pce.getPropertyName();
        if (propertyName.equals(SpectrumAnalyzerPanel.SAMPLE_RATE_PROPERTY)) {
            String sampleRate = Double.toString(spectrumAnalyzerPanel.getSampleRate());
            sampleRateTextField.setText(sampleRate);
            properties.setProperty(DATA_PANEL_PROPERTY_SAMPLE_RATE, sampleRate);
        } else if (propertyName.equals(SpectrumAnalyzerPanel.NUMBER_OF_SAMPLES_PROPERTY)) {
            String numberOfSamples = Integer.toString(spectrumAnalyzerPanel.getNumberOfSamples());
            numberOfSamplesTextField.setText(numberOfSamples);
            properties.setProperty(DATA_PANEL_PROPERTY_NUMBER_OF_SAMPLES, numberOfSamples);
        } else if (propertyName.equals(SpectrumAnalyzerPanel.WINDOW_FUNCTION_PROPERTY)) {
            WindowFunction windowFunction = spectrumAnalyzerPanel.getWindowFunction();
            windowFunctionComboBox.setSelectedItem(windowFunction);
            String windowFunctionString = windowFunction.toString();
            properties.setProperty(DATA_PANEL_PROPERTY_WINDOW_FUNCTION, windowFunctionString);
        } else if (propertyName.equals(SpectrumAnalyzerPanel.SEGMENT_SIZE_PROPERTY)) {
            String segmentSize = Integer.toString(spectrumAnalyzerPanel.getSegmentSize());
            segmentSizeTextField.setText(segmentSize);
            properties.setProperty(DATA_PANEL_PROPERTY_SEGMENT_SIZE, segmentSize);
        } else if (propertyName.equals(SpectrumAnalyzerPanel.OVERLAP_PROPERTY)) {
            String overlap = Integer.toString(spectrumAnalyzerPanel.getOverlap());
            overlapTextField.setText(overlap);
            properties.setProperty(DATA_PANEL_PROPERTY_OVERLAP, overlap);
        }
    }

    /**
     * Gets the properties panel visibility.
     * 
     * @return  true if visible, false otherwise
     */
    public boolean isPropertiesVisible() {
        return propertiesPanel.isVisible();
    }

    /**
     * Sets the properties panel visibility.
     * 
     * @param visible  true to make the panel visible, false to make it invisible
     */
    public void setPropertiesVisible(boolean visible) {
        propertiesPanel.setVisible(visible);
        showPropertiesMenuItem.setSelected(visible);

        if (visible) {
            properties.remove(DATA_PANEL_PROPERTY_SHOW_PROPERTIES);
        } else {
            properties.setProperty(DATA_PANEL_PROPERTY_SHOW_PROPERTIES, "false");
        }
    }

    public boolean supportsMultipleChannels() {
        return false;
    }

    @Override
    protected void channelAdded(String channelName) {
        Channel channel = rbnbController.getChannel(channelName);
        if (channel == null) {
            return;
        }

        // get the sample rate and send it to the spectrum analzyer panel
        String sampleRateString = channel.getMetadata("sampleRate");
        if (sampleRateString == null) {
            return;
        }

        double sampleRate;
        try {
            sampleRate = Double.parseDouble(sampleRateString);
        } catch (NumberFormatException e) {
            return;
        }

        if (sampleRate > 0) {
            spectrumAnalyzerPanel.setSampleRate(sampleRate);

            sampleRateTextField.setEnabled(false);
        }
    }

    @Override
    protected void channelRemoved(String channelName) {
        clearData();

        sampleRateTextField.setEnabled(true);
    }

    @Override
    public void postData(ChannelMap channelMap) {
        this.channelMap = channelMap;
    }

    @Override
    public void postTime(double time) {
        if (time < this.time) {
            clearData();
        }

        super.postTime(time);

        if (channelMap == null) {
            //no data to display yet
            return;
        }

        //loop over all channels and see if there is data for them
        for (String channelName : channels) {
            int channelIndex = channelMap.GetIndex(channelName);

            //if there is data for channel, post it
            if (channelIndex != -1) {
                postTime(channelName, channelIndex);
            }
        }
    }

    /**
     * Looks through the data for the current time range and post it if any is
     * dound.
     * 
     * @param channelName   the name for the channel
     * @param channelIndex  the index for the channel
     */
    private void postTime(String channelName, int channelIndex) {
        double[] times = channelMap.GetTimes(channelIndex);

        int startIndex = -1;

        // determine what time we should load data from
        double dataStartTime;
        if (lastTimeDisplayed == time) {
            dataStartTime = time - timeScale;
        } else {
            dataStartTime = lastTimeDisplayed;
        }

        for (int i = 0; i < times.length; i++) {
            if (times[i] > dataStartTime && times[i] <= time) {
                startIndex = i;
                break;
            }
        }

        //see if there is no data in the time range we are looking at
        if (startIndex == -1) {
            return;
        }

        int endIndex = startIndex;

        for (int i = times.length - 1; i > startIndex; i--) {
            if (times[i] <= time) {
                endIndex = i;
                break;
            }
        }

        postData(channelMap, channelIndex, startIndex, endIndex);

        lastTimeDisplayed = times[endIndex];
    }

    /**
     * Post the data to the spectrum analyzer. This will method will queue the
     * post on the EDT thread and return immediately.
     * 
     * @param channelMap    the channel map with the data
     * @param channelIndex  the index to the channel
     * @param startIndex    the start index of the data
     * @param endIndex      the end index of the data
     */
    private void postData(final ChannelMap channelMap, final int channelIndex, final int startIndex,
            final int endIndex) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                int typeID = channelMap.GetType(channelIndex);

                switch (typeID) {
                case ChannelMap.TYPE_FLOAT64:
                    double[] doubleData = channelMap.GetDataAsFloat64(channelIndex);
                    spectrumAnalyzerPanel.addData(doubleData, startIndex, endIndex);
                    break;
                case ChannelMap.TYPE_FLOAT32:
                    float[] floatData = channelMap.GetDataAsFloat32(channelIndex);
                    spectrumAnalyzerPanel.addData(floatData, startIndex, endIndex);
                    break;
                case ChannelMap.TYPE_INT64:
                    long[] longData = channelMap.GetDataAsInt64(channelIndex);
                    for (int i = startIndex; i <= endIndex; i++) {
                        spectrumAnalyzerPanel.addData(longData[i]);
                    }
                    spectrumAnalyzerPanel.finishedAddingData();
                    break;
                case ChannelMap.TYPE_INT32:
                    int[] intData = channelMap.GetDataAsInt32(channelIndex);
                    for (int i = startIndex; i <= endIndex; i++) {
                        spectrumAnalyzerPanel.addData(intData[i]);
                    }
                    spectrumAnalyzerPanel.finishedAddingData();
                    break;
                case ChannelMap.TYPE_INT16:
                    short[] shortData = channelMap.GetDataAsInt16(channelIndex);
                    for (int i = startIndex; i <= endIndex; i++) {
                        spectrumAnalyzerPanel.addData(shortData[i]);
                    }
                    spectrumAnalyzerPanel.finishedAddingData();
                    break;
                case ChannelMap.TYPE_INT8:
                    byte[] byteData = channelMap.GetDataAsInt8(channelIndex);
                    for (int i = startIndex; i <= endIndex; i++) {
                        spectrumAnalyzerPanel.addData(byteData[i]);
                    }
                    spectrumAnalyzerPanel.finishedAddingData();
                    break;
                }
            }
        });
    }

    /**
     * Clear the current stored data. This will queue a call on the EDT to clear
     * the data in the spectrum analyzer and return immediatly.
     */
    private void clearData() {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                spectrumAnalyzerPanel.clearData();
            }
        });

        lastTimeDisplayed = -1;
    }

    @Override
    public void setProperty(String key, String value) {
        super.setProperty(key, value);

        if (key == null) {
            return;
        }

        properties.setProperty(key, value);

        if (key.equals(DATA_PANEL_PROPERTY_SAMPLE_RATE)) {
            try {
                double sampleRate = Double.parseDouble(value);
                spectrumAnalyzerPanel.setSampleRate(sampleRate);
            } catch (Exception e) {
                log.warn("Unable to set sample rate: " + value + ".");
            }
        } else if (key.equals(DATA_PANEL_PROPERTY_NUMBER_OF_SAMPLES)) {
            try {
                int numberOfSamples = Integer.parseInt(value);
                spectrumAnalyzerPanel.setNumberOfSamples(numberOfSamples);
            } catch (Exception e) {
                log.warn("Unable to set number of samples: " + value + ".");
            }
        } else if (key.equals(DATA_PANEL_PROPERTY_WINDOW_FUNCTION)) {
            try {
                WindowFunction windowFunction = WindowFunction.valueOf(value);
                spectrumAnalyzerPanel.setWindowFunction(windowFunction);
            } catch (Exception e) {
                log.warn("Unable to set window function: " + value + ".");
            }
        } else if (key.equals(DATA_PANEL_PROPERTY_SEGMENT_SIZE)) {
            try {
                int segmentSize = Integer.parseInt(value);
                spectrumAnalyzerPanel.setSegmentSize(segmentSize);
            } catch (Exception e) {
                log.warn("Unable to set segment size: " + value + ".");
            }
        } else if (key.equals(DATA_PANEL_PROPERTY_OVERLAP)) {
            try {
                int overlap = Integer.parseInt(value);
                spectrumAnalyzerPanel.setOverlap(overlap);
            } catch (Exception e) {
                log.warn("Unable to set overlap: " + value + ".");
            }
        } else if (key.equals(DATA_PANEL_PROPERTY_SHOW_PROPERTIES) && !Boolean.parseBoolean(value)) {
            setPropertiesVisible(false);
        }
    }

    @Override
    public String toString() {
        return "Spectrum Analyzer";
    }

}