/* TimeDomainFilterResponseGraphsPanel.java created 2011-02-07 * */ package org.signalml.app.view.montage.filters.charts; import static org.signalml.app.util.i18n.SvarogI18n._; import static org.signalml.app.util.i18n.SvarogI18n._R; import java.awt.GridLayout; import java.util.ArrayList; import java.util.List; import javax.swing.JPanel; import org.signalml.app.view.montage.filters.charts.elements.FilterResponseChartPanelsWithGraphScaleSpinner; import org.signalml.app.view.montage.filters.charts.elements.GroupDelayResponseChartPanel; import org.signalml.app.view.montage.filters.charts.elements.ImpulseResponseChartPanel; import org.signalml.app.view.montage.filters.charts.elements.ResponseChartPanel; import org.signalml.app.view.montage.filters.charts.elements.StepResponseChartPanel; import org.signalml.app.view.montage.filters.charts.elements.TimeDomainFilterFrequencyResponseChartPanel; import org.signalml.domain.montage.filter.TimeDomainSampleFilter; import org.signalml.math.ArrayOperations; import org.signalml.math.iirdesigner.BadFilterParametersException; import org.signalml.math.iirdesigner.FilterCoefficients; import org.signalml.math.iirdesigner.FilterFrequencyResponse; import org.signalml.math.iirdesigner.FilterFrequencyResponseCalculator; import org.signalml.math.iirdesigner.FilterNotStableException; import org.signalml.math.iirdesigner.FilterTimeDomainResponse; import org.signalml.math.iirdesigner.FilterTimeDomainResponseCalculator; import org.signalml.math.iirdesigner.IIRDesigner; /** * This class represents a panel containing all the components needed for visualizing * a {@link TimeDomainSampleFilter}, that is: filter frequency response, group * delay response, impulse response, step response and associated spinners * to control the maximum x-axis values shown on the charts. * * @author Piotr Szachewicz */ public class TimeDomainFilterResponseChartGroupPanel extends FilterResponseChartGroupPanel<TimeDomainSampleFilter> { /** * The size of the calculated time domain responses (impulse and step responses) * in seconds. Impulse and step response is calculated for the given * number of seconds. */ protected static int TIME_DOMAIN_RESPONSES_SIZE_IN_SECONDS = 100; /** * The initial value of the impulse and step response graph scale spinner. */ protected static int INITIAL_TIME_DOMAIN_RESPONSES_MAXIMUM_TIME_VALUE_IN_SECONDS = 4; /** * A chart panel containing the filter frequency response chart. */ protected TimeDomainFilterFrequencyResponseChartPanel frequencyResponseChartPanel; /** * A chart panel containing the filter group delay response chart. */ protected GroupDelayResponseChartPanel groupDelayResponseChartPanel; /** * A chart panel containing the filter impulse response chart. */ protected ImpulseResponseChartPanel impulseResponseChartPanel; /** * A chart panel containing the filter step response chart. */ protected StepResponseChartPanel stepResponseChartPanel; /** * A panel containing frequency response chart panels (frequencyResponseChartPanel * and groupDelayResponseChartPanel) with an associated spinner. */ protected FilterResponseChartPanelsWithGraphScaleSpinner frequencyResponseChartPanelWithSpinner; /** * A panel containing time domain response chart panels * (impulseResponseChartPanel and stepResponseChartPanel) with * with an associated spinner. */ protected FilterResponseChartPanelsWithGraphScaleSpinner timeDomainResponseChartPanelWithSpinner; /** * Calculator for calculating the frequency responses of a filter. */ protected FilterFrequencyResponseCalculator frequencyResponseCalculator; /** * Calculator for calculating the time domain responses of a filter. */ protected FilterTimeDomainResponseCalculator timeDomainResponseCalculator; /** * Constructor. * @param currentFilter the filter to be visualized */ public TimeDomainFilterResponseChartGroupPanel(TimeDomainSampleFilter currentFilter) { super(currentFilter); } @Override public void setSamplingFrequency(double samplingFrequency) { super.setSamplingFrequency(samplingFrequency); frequencyResponseChartPanelWithSpinner.setMaximumSpinnerValue(samplingFrequency / 2); frequencyResponseChartPanelWithSpinner.setCurrentSpinnerValue(samplingFrequency / 2); } @Override protected JPanel createChartGroupPanel() { JPanel panel = new JPanel(new GridLayout(1, 2, 5, 5)); frequencyResponseChartPanelWithSpinner = createFrequencyResponsesPanel(); timeDomainResponseChartPanelWithSpinner = createTimeDomainResponsesPanel(); panel.add(frequencyResponseChartPanelWithSpinner); panel.add(timeDomainResponseChartPanelWithSpinner); return panel; } /** * Creates the panel containing the filter frequency responses * (frequency response and group delay). * @return panel containing the filter frequency responses */ protected FilterResponseChartPanelsWithGraphScaleSpinner createFrequencyResponsesPanel() { frequencyResponseChartPanel = new TimeDomainFilterFrequencyResponseChartPanel(); groupDelayResponseChartPanel = new GroupDelayResponseChartPanel(); List<ResponseChartPanel> chartsList = new ArrayList<ResponseChartPanel>(); chartsList.add(frequencyResponseChartPanel); chartsList.add(groupDelayResponseChartPanel); return new FilterResponseChartPanelsWithGraphScaleSpinner(chartsList, _("Maximum graph frequency [Hz]")); } /** * Creates a panel containing the filter time domain responses * (impulse response and step response). * @return panel containing time domain responses */ protected FilterResponseChartPanelsWithGraphScaleSpinner createTimeDomainResponsesPanel() { impulseResponseChartPanel = new ImpulseResponseChartPanel(); stepResponseChartPanel = new StepResponseChartPanel(); List<ResponseChartPanel> chartsList = new ArrayList<ResponseChartPanel>(); chartsList.add(impulseResponseChartPanel); chartsList.add(stepResponseChartPanel); FilterResponseChartPanelsWithGraphScaleSpinner chartPanel = new FilterResponseChartPanelsWithGraphScaleSpinner(chartsList, _("Maximum graph time value [s]")); chartPanel.setMaximumSpinnerValue(TIME_DOMAIN_RESPONSES_SIZE_IN_SECONDS); chartPanel.setCurrentSpinnerValue(INITIAL_TIME_DOMAIN_RESPONSES_MAXIMUM_TIME_VALUE_IN_SECONDS); return chartPanel; } /** * Updates the filter responses shown on the charts. * @param currentFilter the filter to be visualized * @throws BadFilterParametersException thrown when the filter cannot * be designed * @throws FilterNotStableException when the filter designed is not stable. */ public void updateGraphs(TimeDomainSampleFilter currentFilter) throws BadFilterParametersException, FilterNotStableException { FilterCoefficients coeffs = IIRDesigner.designDigitalFilter(currentFilter); frequencyResponseCalculator = new FilterFrequencyResponseCalculator(512, getSamplingFrequency(), coeffs); calculateAndDrawFilterFrequencyResponse(); calculateAndDrawGroupDelayResponse(); timeDomainResponseCalculator = new FilterTimeDomainResponseCalculator(samplingFrequency, coeffs); FilterTimeDomainResponse impulseResponse = timeDomainResponseCalculator.getImpulseResponse(getNumberOfPointsForTimeDomainResponse()); impulseResponseChartPanel.setData(impulseResponse); FilterTimeDomainResponse stepResponse = timeDomainResponseCalculator.getStepResponse(getNumberOfPointsForTimeDomainResponse()); stepResponseChartPanel.setData(stepResponse); if (!impulseResponse.isStable() || !stepResponse.isStable()) { adaptTheTimeSpinnerToInstabilityTime(impulseResponse, stepResponse); throw new FilterNotStableException(); } } /** * Changes the current time spinner value for the time response charts so that the * first sample above instability threshold is on the right of the chart. * @param impulseResponse * @param stepResponse */ protected void adaptTheTimeSpinnerToInstabilityTime(FilterTimeDomainResponse impulseResponse, FilterTimeDomainResponse stepResponse) { int index1 = impulseResponse.getIndexOfFirstSampleAboveInstabilityThreshold(); int index2 = stepResponse.getIndexOfFirstSampleAboveInstabilityThreshold(); int index = Math.max(index1, index2); double instabilityTime = (index) / getSamplingFrequency(); instabilityTime = Math.ceil(instabilityTime); timeDomainResponseChartPanelWithSpinner.setCurrentSpinnerValue(instabilityTime); } /** * Calculates and plots current filter frequency response. */ protected void calculateAndDrawFilterFrequencyResponse() { FilterFrequencyResponse frequencyResponse = frequencyResponseCalculator.getMagnitudeResponse(); double[] frequencies = frequencyResponse.getFrequencies(); double[] values = frequencyResponse.getValues(); frequencyResponseChartPanel.setData(frequencies, values); int filterOrder = frequencyResponseCalculator.getFilterCoefficients().getFilterOrder(); String subtitleText = _R("filter order = {0}", filterOrder); frequencyResponseChartPanel.setSubtitle(subtitleText); } /** * Calculates and plots current filter group delay response. */ protected void calculateAndDrawGroupDelayResponse() { FilterFrequencyResponse groupDelayResponse = frequencyResponseCalculator.getGroupDelayResponse(); double[] frequencies = groupDelayResponse.getFrequencies(); double[] values = groupDelayResponse.getValues(); // removing the first element which is (very often) a singularity and spoils the plot frequencies = ArrayOperations.removeFirstElements(frequencies, 1); values = ArrayOperations.removeFirstElements(values, 1); groupDelayResponseChartPanel.setData(frequencies, values); } /** * Returns the number of points for which the time domain responses * should be calculated. * @return number of points which should be calculated for the time * domain responses */ protected int getNumberOfPointsForTimeDomainResponse() { return (int)(TIME_DOMAIN_RESPONSES_SIZE_IN_SECONDS * samplingFrequency); } @Override protected String getChartGroupPanelTitle() { return _("Filter design graphs"); } }