/* TimeDomainFilterParametersPanel.java created 2011-02-17
*
*/
package org.signalml.app.view.montage.filters;
import static org.signalml.app.util.i18n.SvarogI18n._;
import org.signalml.app.model.components.validation.ValidationErrors;
import org.signalml.app.view.common.components.ResolvableComboBox;
import org.signalml.app.view.common.components.spinners.DoubleSpinner;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import javax.swing.DefaultComboBoxModel;
import javax.swing.GroupLayout;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSpinner;
import javax.swing.SpinnerNumberModel;
import javax.swing.GroupLayout.Alignment;
import javax.swing.JTextField;
import javax.swing.border.EmptyBorder;
import org.signalml.domain.montage.filter.TimeDomainSampleFilter;
import org.signalml.math.iirdesigner.ApproximationFunctionType;
import org.signalml.math.iirdesigner.FilterType;
import org.signalml.util.Util;
import org.springframework.validation.Errors;
/**
* Panel consisting of a controls capable of editing the time domain filter parameters
* (filter type, filter approximation function, passband and stopband frequencies,
* attenuation etc.)
*
* @author Piotr Szachewicz
*/
public class TimeDomainFilterParametersPanel extends JPanel {
/**
* A value of step size for passband and stop band edge frequency
* spinners.
*/
private final double FREQUENCY_SPINNER_STEP_SIZE = 0.1;
/**
* The value of step size for passband ripple and stopband attenuation
* spinners.
*/
private final double DECIBELS_SPINNER_STEP_SIZE = 0.1;
/**
* The maximum value which can be set using the decibels spinners
* (passband ripple and stopband attenuation spinners).
*/
private final double DECIBELS_SPINNER_MAXIMUM_VALUE = 100.0;
/**
* The minimum value which can be set using the decibels spinners.
*/
private final double DECIBELS_SPINNER_MINIMUM_VALUE = 0.0;
/**
* The currently edited filter.
*/
private TimeDomainSampleFilter currentFilter;
/**
* The sampling frequency of the signal.
*/
private double samplingFrequency;
/**
* A {@link JTextField} which can be used to edit the filter's description.
*/
private JTextField descriptionTextField;
/**
* A {@link ResolvableComboBox} to select filter's {@link FilterType} from.
*/
private ResolvableComboBox filterTypeComboBox;
/**
* A {@link ResolvableComboBox} which can be used to select filter's
* {@link ApproximationFunctionType} from.
*/
private ResolvableComboBox filterFamilyComboBox;
/**
* The spinner controlling the value of the first passband edge
* frequency of the filter.
*/
private DoubleSpinner passbandEdgeFrequency1Spinner;
/**
* A {@link JSpinner} controlling the value of the second passband edge
* frequency of the filter.
*/
private DoubleSpinner passbandEdgeFrequency2Spinner;
/**
* A {@link JSpinner} controlling the value of the first stopband edge
* frequency of the filter.
*/
private DoubleSpinner stopbandEdgeFrequency1Spinner;
/**
* A spinner controlling the value of the second stopband edge
* frequency of the filter.
*/
private DoubleSpinner stopbandEdgeFrequency2Spinner;
/**
* A {@link JSpinner} controlling the value of filter's maximum ripple
* in the passband.
*/
private DoubleSpinner passbandRippleSpinner;
/**
* A {@link JSpinner} controlling the value of filter's minimum
* attenuation in the stopband.
*/
private DoubleSpinner stopbandAttenuationSpinner;
/**
* Constructor.
*/
public TimeDomainFilterParametersPanel() {
createInterface();
}
/**
* Creates all components and puts them in appropriate places on this panel.
*/
protected void createInterface() {
JPanel filterParametersPanel = new JPanel(null);
filterParametersPanel.setBorder(new EmptyBorder(3, 3, 3, 3));
GroupLayout layout = new GroupLayout(filterParametersPanel);
filterParametersPanel.setLayout(layout);
layout.setAutoCreateContainerGaps(false);
layout.setAutoCreateGaps(true);
JLabel descriptionLabel = new JLabel(_("Filter description"));
JLabel filterTypeLabel = new JLabel(_("Filter type"));
JLabel filterFamilyLabel = new JLabel(_("Filter family"));
JLabel passbandEdgeFrequency1Label = new JLabel(_("Passband edge frequency 1 [Hz]"));
JLabel passbandEdgeFrequency2Label = new JLabel(_("Passband edge frequency 2 [Hz]"));
JLabel stopbandEdgeFrequency1Label = new JLabel(_("Stopband edge frequency 1 [Hz]"));
JLabel stopbandEdgeFrequency2Label = new JLabel(_("Stopband edge frequency 2 [Hz]"));
JLabel passbandRippleLabel = new JLabel(_("Passband ripple [dB]"));
JLabel stopbandAttenuationLabel = new JLabel(_("Stopband attenuation [dB]"));
GroupLayout.SequentialGroup hGroup = layout.createSequentialGroup();
hGroup.addGroup(
layout.createParallelGroup().addComponent(descriptionLabel).addComponent(filterTypeLabel).addComponent(passbandEdgeFrequency1Label).addComponent(stopbandEdgeFrequency1Label).addComponent(passbandRippleLabel));
hGroup.addGroup(
layout.createParallelGroup().addComponent(getDescriptionTextField()).addComponent(getFilterTypeComboBox()).addComponent(getPassbandEdgeFrequency1Spinner()).addComponent(getStopbandEdgeFrequency1Spinner()).addComponent(getPassbandRippleSpinner()));
hGroup.addGroup(
layout.createParallelGroup().addComponent(filterFamilyLabel).addComponent(passbandEdgeFrequency2Label).addComponent(stopbandEdgeFrequency2Label).addComponent(stopbandAttenuationLabel));
hGroup.addGroup(
layout.createParallelGroup().addComponent(getFilterFamilyComboBox()).addComponent(getPassbandEdgeFrequency2Spinner()).addComponent(getStopbandEdgeFrequency2Spinner()).addComponent(getStopbandAttenuationSpinner()));
layout.setHorizontalGroup(hGroup);
GroupLayout.SequentialGroup vGroup = layout.createSequentialGroup();
vGroup.addGroup(
layout.createParallelGroup(Alignment.BASELINE).addComponent(descriptionLabel).addComponent(getDescriptionTextField()));
vGroup.addGroup(
layout.createParallelGroup(Alignment.BASELINE).addComponent(filterTypeLabel).addComponent(getFilterTypeComboBox()).addComponent(filterFamilyLabel).addComponent(getFilterFamilyComboBox()));
vGroup.addGroup(
layout.createParallelGroup(Alignment.BASELINE).addComponent(passbandEdgeFrequency1Label).addComponent(getPassbandEdgeFrequency1Spinner()).addComponent(passbandEdgeFrequency2Label).addComponent(getPassbandEdgeFrequency2Spinner()));
vGroup.addGroup(
layout.createParallelGroup(Alignment.BASELINE).addComponent(stopbandEdgeFrequency1Label).addComponent(getStopbandEdgeFrequency1Spinner()).addComponent(stopbandEdgeFrequency2Label).addComponent(getStopbandEdgeFrequency2Spinner()));
vGroup.addGroup(
layout.createParallelGroup(Alignment.BASELINE).addComponent(passbandRippleLabel).addComponent(getPassbandRippleSpinner()).addComponent(stopbandAttenuationLabel).addComponent(getStopbandAttenuationSpinner()));
layout.setVerticalGroup(vGroup);
this.setLayout(new GridLayout(1, 1));
this.add(filterParametersPanel);
}
/**
* Returns the {@link JTextField} which is shown in this panel and
* can be used to edit the filter's description.
* @return the {@link JTextField} to edit the filter's description
*/
public JTextField getDescriptionTextField() {
if (descriptionTextField == null) {
descriptionTextField = new JTextField();
descriptionTextField.setPreferredSize(new Dimension(200, 25));
}
return descriptionTextField;
}
/**
* Returns the {@link ResolvableComboBox} which can be used to set
* the type of the filter (i.e. whether the filter is low-pass, high-pass,
* band-pass or band-stop).
* @return a {@link ResolvableComboBox} used to set the type of this
* filter
*/
public ResolvableComboBox getFilterTypeComboBox() {
if (filterTypeComboBox == null) {
filterTypeComboBox = new ResolvableComboBox();
filterTypeComboBox.setPreferredSize(new Dimension(200, 25));
FilterType[] filterTypes = FilterType.values();
DefaultComboBoxModel model = new DefaultComboBoxModel(filterTypes);
filterTypeComboBox.setModel(model);
filterTypeComboBox.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
if (((FilterType) filterTypeComboBox.getSelectedItem()).isHighpass() || ((FilterType) filterTypeComboBox.getSelectedItem()).isLowpass()) {
getPassbandEdgeFrequency2Spinner().setEnabled(false);
getStopbandEdgeFrequency2Spinner().setEnabled(false);
} else {
getPassbandEdgeFrequency2Spinner().setEnabled(true);
getStopbandEdgeFrequency2Spinner().setEnabled(true);
}
}
});
}
return filterTypeComboBox;
}
/**
* Returns the {@link ResolvableComboBox} which can be used to set the
* filter family ({@link ApproximationFunctionType}).
* @return the {@link ResolvableComboBox} which can be used to set the
* filter family
*/
public ResolvableComboBox getFilterFamilyComboBox() {
if (filterFamilyComboBox == null) {
filterFamilyComboBox = new ResolvableComboBox();
filterFamilyComboBox.setPreferredSize(new Dimension(200, 25));
ApproximationFunctionType[] filterFamilyTypes = ApproximationFunctionType.values();
DefaultComboBoxModel model = new DefaultComboBoxModel(filterFamilyTypes);
filterFamilyComboBox.setModel(model);
}
return filterFamilyComboBox;
}
/**
* Returns the {@link JSpinner} allowing to set the first passband
* edge frequency of this filter.
* @return the {@link JSpinner} allowing to set the first passband
* edge frequency of this filter
*/
public DoubleSpinner getPassbandEdgeFrequency1Spinner() {
if (passbandEdgeFrequency1Spinner == null) {
passbandEdgeFrequency1Spinner = new DoubleSpinner(createFrequencySpinnerNumberModel());
passbandEdgeFrequency1Spinner.setPreferredSize(new Dimension(80, 25));
passbandEdgeFrequency1Spinner.setEditor(new JSpinner.NumberEditor(passbandEdgeFrequency1Spinner, "0.00"));
passbandEdgeFrequency1Spinner.setFont(passbandEdgeFrequency1Spinner.getFont().deriveFont(Font.PLAIN));
}
return passbandEdgeFrequency1Spinner;
}
/**
* Returns the {@link JSpinner} allowing to set the second passband
* edge frequency of this filter.
* @return the {@link JSpinner} allowing to set the second passband
* edge frequency of this filter
*/
public DoubleSpinner getPassbandEdgeFrequency2Spinner() {
if (passbandEdgeFrequency2Spinner == null) {
passbandEdgeFrequency2Spinner = new DoubleSpinner(createFrequencySpinnerNumberModel());
passbandEdgeFrequency2Spinner.setPreferredSize(new Dimension(80, 25));
passbandEdgeFrequency2Spinner.setEditor(new JSpinner.NumberEditor(passbandEdgeFrequency2Spinner, "0.00"));
passbandEdgeFrequency2Spinner.setFont(passbandEdgeFrequency2Spinner.getFont().deriveFont(Font.PLAIN));
}
return passbandEdgeFrequency2Spinner;
}
/**
* Returns the {@link JSpinner} allowing to set the first stopband
* edge frequency of this filter.
* @return the {@link JSpinner} allowing to set the first stopband
* edge frequency of this filter
*/
public DoubleSpinner getStopbandEdgeFrequency1Spinner() {
if (stopbandEdgeFrequency1Spinner == null) {
stopbandEdgeFrequency1Spinner = new DoubleSpinner(createFrequencySpinnerNumberModel());
stopbandEdgeFrequency1Spinner.setPreferredSize(new Dimension(80, 25));
stopbandEdgeFrequency1Spinner.setEditor(new JSpinner.NumberEditor(stopbandEdgeFrequency1Spinner, "0.00"));
stopbandEdgeFrequency1Spinner.setFont(stopbandEdgeFrequency1Spinner.getFont().deriveFont(Font.PLAIN));
}
return stopbandEdgeFrequency1Spinner;
}
/**
* Returns the {@link JSpinner} allowing to set the second stopband
* edge frequency of this filter.
* @return the {@link JSpinner} allowing to set the second stopband
* edge frequency of this filter
*/
public DoubleSpinner getStopbandEdgeFrequency2Spinner() {
if (stopbandEdgeFrequency2Spinner == null) {
stopbandEdgeFrequency2Spinner = new DoubleSpinner(createFrequencySpinnerNumberModel());
stopbandEdgeFrequency2Spinner.setPreferredSize(new Dimension(80, 25));
stopbandEdgeFrequency2Spinner.setEditor(new JSpinner.NumberEditor(stopbandEdgeFrequency2Spinner, "0.00"));
stopbandEdgeFrequency2Spinner.setFont(stopbandEdgeFrequency2Spinner.getFont().deriveFont(Font.PLAIN));
}
return stopbandEdgeFrequency2Spinner;
}
/**
* Returns the {@link JSpinner} used in this window to control the
* maximum passband ripple for this filter.
* @return the {@link JSpinner} for this window, which can be used
* to control the passband ripple of this filter
*/
public DoubleSpinner getPassbandRippleSpinner() {
if (passbandRippleSpinner == null) {
passbandRippleSpinner = new DoubleSpinner(new SpinnerNumberModel(3.0, DECIBELS_SPINNER_MINIMUM_VALUE, DECIBELS_SPINNER_MAXIMUM_VALUE, DECIBELS_SPINNER_STEP_SIZE));
passbandRippleSpinner.setPreferredSize(new Dimension(80, 25));
passbandRippleSpinner.setEditor(new JSpinner.NumberEditor(passbandRippleSpinner, "0.00"));
passbandRippleSpinner.setFont(passbandRippleSpinner.getFont().deriveFont(Font.PLAIN));
}
return passbandRippleSpinner;
}
/**
* Returns the {@link JSpinner} used in this window to control the
* maximum passband ripple for this filter.
* @return the {@link JSpinner} for this window, which can be used
* to control the passband ripple of this filter
*/
public DoubleSpinner getStopbandAttenuationSpinner() {
if (stopbandAttenuationSpinner == null) {
stopbandAttenuationSpinner = new DoubleSpinner(new SpinnerNumberModel(30.0, DECIBELS_SPINNER_MINIMUM_VALUE, DECIBELS_SPINNER_MAXIMUM_VALUE, DECIBELS_SPINNER_STEP_SIZE));
stopbandAttenuationSpinner.setPreferredSize(new Dimension(80, 25));
stopbandAttenuationSpinner.setEditor(new JSpinner.NumberEditor(stopbandAttenuationSpinner, "0.00"));
stopbandAttenuationSpinner.setFont(stopbandAttenuationSpinner.getFont().deriveFont(Font.PLAIN));
}
return stopbandAttenuationSpinner;
}
/**
* Returns a {@link SpinnerNumberModel} which should be used for
* passband and stopband edge frequency spinners.
* @return a {@link SpinnerNumberModel} to be used with frequency
* spinners
*/
protected SpinnerNumberModel createFrequencySpinnerNumberModel() {
return new SpinnerNumberModel(0.0, 0.0, getCurrentSamplingFrequency() / 2, FREQUENCY_SPINNER_STEP_SIZE);
}
/**
* Sets all of the controls to the values describing the given filter.
* @param model the filter to be 'shown' on the controls
*/
public void fillPanelFromModel(TimeDomainSampleFilter model) {
currentFilter = new TimeDomainSampleFilter(model);
currentFilter.setSamplingFrequency(getCurrentSamplingFrequency());
getDescriptionTextField().setText(currentFilter.getDescription());
getFilterTypeComboBox().setSelectedItem(currentFilter.getFilterType());
getFilterFamilyComboBox().setSelectedItem(currentFilter.getApproximationFunctionType());
getPassbandEdgeFrequency1Spinner().setValue((double) currentFilter.getPassbandEdgeFrequencies()[0]);
getPassbandEdgeFrequency2Spinner().setValue((double) currentFilter.getPassbandEdgeFrequencies()[1]);
getStopbandEdgeFrequency1Spinner().setValue((double) currentFilter.getStopbandEdgeFrequencies()[0]);
getStopbandEdgeFrequency2Spinner().setValue((double) currentFilter.getStopbandEdgeFrequencies()[1]);
getPassbandRippleSpinner().setValue((double) currentFilter.getPassbandRipple());
getStopbandAttenuationSpinner().setValue((double) currentFilter.getStopbandAttenuation());
}
/**
* Sets all of the vales in the given filter to match the values
* set by the controls.
* @param model the model to be filled
*/
public void fillModelFromPanel(TimeDomainSampleFilter model) {
currentFilter.setDescription(getDescriptionTextField().getText());
currentFilter.setFilterType((FilterType) getFilterTypeComboBox().getSelectedItem());
currentFilter.setApproximationFunctionType((ApproximationFunctionType) getFilterFamilyComboBox().getSelectedItem());
currentFilter.setPassbandEdgeFrequencies(
new double[] {
getPassbandEdgeFrequency1Spinner().getValue(),
getPassbandEdgeFrequency2Spinner().getValue()
});
currentFilter.setStopbandEdgeFrequencies(
new double[] {
getStopbandEdgeFrequency1Spinner().getValue(),
getStopbandEdgeFrequency2Spinner().getValue()
});
currentFilter.setPassbandRipple(getPassbandRippleSpinner().getValue());
currentFilter.setStopbandAttenuation(getStopbandAttenuationSpinner().getValue());
currentFilter.setSamplingFrequency(getCurrentSamplingFrequency());
model.copyFrom(currentFilter);
}
/**
* Sets the sampling frequency which should be used by this panel
* (it determines the maximum frequency which can be set
* when using the frequency spinners).
* @param samplingFrequency the current value of the sampling frequency
*/
public void setSamplingFrequency(double samplingFrequency) {
this.samplingFrequency = samplingFrequency;
updateFrequencySpinnersMaximumValues();
}
/**
* Returns the maximum frequency which can be set using
* the frequency spinners.
* @return maximum frequency which can be set in this panel
*/
private double getMaximumFrequencyValue() {
return samplingFrequency / 2;
}
/**
* Updates the frequency spinners so that the maximum frequency which
* can be set using them is appropriate (half of the sampling frequency).
*/
private void updateFrequencySpinnersMaximumValues() {
double maximumFrequencyValue = getMaximumFrequencyValue();
getPassbandEdgeFrequency1Spinner().setMaximumValue(maximumFrequencyValue);
getPassbandEdgeFrequency2Spinner().setMaximumValue(maximumFrequencyValue);
getStopbandEdgeFrequency1Spinner().setMaximumValue(maximumFrequencyValue);
getStopbandEdgeFrequency2Spinner().setMaximumValue(maximumFrequencyValue);
}
/**
* Returns the current sampling frequency.
* @return the current sampling frequency
*/
private double getCurrentSamplingFrequency() {
return samplingFrequency;
}
/**
* Validates if data entered in this panel is correct.
* @param model the model for this dialog
* @param errors the object in which errors are stored
*/
public void validatePanel(Object model, ValidationErrors errors) {
String description = getDescriptionTextField().getText();
if (description == null || description.isEmpty()) {
errors.addError(_("A filter must have a description"));
} else if (Util.hasSpecialChars(description)) {
errors.addError(_("Filter description must not contain control characters"));
}
}
}