package pl.edu.fuw.fid.signalanalysis.stft;
import java.io.IOException;
import java.net.URL;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.Event;
import javafx.event.EventHandler;
import javafx.fxml.FXMLLoader;
import javafx.scene.chart.NumberAxis;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Slider;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
import org.signalml.app.view.book.wignermap.WignerMapPalette;
import org.signalml.math.fft.WindowType;
import org.signalml.plugin.export.NoActiveObjectException;
import org.signalml.plugin.export.signal.ChannelSamples;
import org.signalml.plugin.export.signal.ExportedSignalSelection;
import org.signalml.plugin.export.signal.SvarogAccessSignal;
import pl.edu.fuw.fid.signalanalysis.SignalAnalysisTools;
import pl.edu.fuw.fid.signalanalysis.SimpleSingleSignal;
import pl.edu.fuw.fid.signalanalysis.waveform.ImageChart;
import pl.edu.fuw.fid.signalanalysis.SingleSignal;
import pl.edu.fuw.fid.signalanalysis.waveform.ImageRefresher;
import pl.edu.fuw.fid.signalanalysis.waveform.SignalChart;
import pl.edu.fuw.fid.signalanalysis.waveform.TimeFrequency;
/**
* User interface for interactive Short-Time Fourier Transform.
*
* @author ptr@mimuw.edu.pl
*/
public class PaneForSTFT {
final BorderPane root;
final ObservableList<WindowType> windowTypeItems;
final ObservableList<Integer> windowSizeItems;
final ObservableList<WignerMapPalette> paletteTypeItems;
final ImageChart chart;
final ImageRendererForSTFT renderer;
public PaneForSTFT(final SvarogAccessSignal signalAccess, ExportedSignalSelection selection) throws IOException, NoActiveObjectException {
root = new BorderPane();
windowTypeItems = FXCollections.observableArrayList(
WindowType.BARTLETT,
WindowType.GAUSSIAN,
WindowType.HAMMING,
WindowType.HANN,
WindowType.RECTANGULAR,
WindowType.WELCH
);
windowSizeItems = FXCollections.observableArrayList(
32, 64, 128, 256, 512, 1024
);
paletteTypeItems = FXCollections.observableArrayList(
WignerMapPalette.RAINBOW,
WignerMapPalette.GRAYSCALE
);
URL url = getClass().getResource("SettingsPanelForSTFT.fxml");
FXMLLoader fxmlLoader = new FXMLLoader(url);
VBox settingsPanel = (VBox) fxmlLoader.load();
final ComboBox windowType = (ComboBox) fxmlLoader.getNamespace().get("windowTypeComboBox");
windowType.setItems(windowTypeItems);
final ComboBox windowSize = (ComboBox) fxmlLoader.getNamespace().get("windowSizeComboBox");
windowSize.setItems(windowSizeItems);
windowSize.setEditable(true);
final ComboBox paletteType = (ComboBox) fxmlLoader.getNamespace().get("paletteTypeComboBox");
paletteType.setItems(paletteTypeItems);
final int selectedChannel = Math.max(selection.getChannel(), 0);
final ChannelSamples samples = signalAccess.getActiveProcessedSignalSamples(selectedChannel);
final double samplingFrequency = samples.getSamplingFrequency();
final double nyquistFrequency = 0.5 * samplingFrequency;
double tMin = selection.getPosition();
double tMax = selection.getEndPosition();
final NumberAxis xAxis = new NumberAxis(tMin, tMax, 1.0);
final NumberAxis yAxis = new NumberAxis(0.0, nyquistFrequency, 10.0);
yAxis.setLabel("frequency [Hz]");
yAxis.setPrefWidth(50);
final Slider maxFrequency = (Slider) fxmlLoader.getNamespace().get("frequencySlider");
maxFrequency.setMax(nyquistFrequency);
maxFrequency.setValue(nyquistFrequency);
maxFrequency.valueProperty().addListener(new ChangeListener<Number>() {
@Override
public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
double maxFreq = Math.round(newValue.doubleValue());
yAxis.setUpperBound(maxFreq);
}
});
SingleSignal signal = new SimpleSingleSignal(samples);
renderer = new ImageRendererForSTFT(signal);
final ImageRefresher refresher = new ImageRefresher(renderer);
Runnable onResize = new Runnable() {
@Override
public void run() {
refresher.refreshChartImage(chart);
}
};
chart = new ImageChart(xAxis, yAxis, renderer, onResize);
windowType.getSelectionModel().select(renderer.getWindowType());
windowSize.getSelectionModel().select(renderer.getWindowLength());
paletteType.getSelectionModel().select(renderer.getPaletteType());
final NumberAxis xAxisSignal = new NumberAxis(tMin, tMax, 1.0);
final NumberAxis yAxisSignal = new NumberAxis();
xAxisSignal.setLabel("time [s]");
yAxisSignal.setLabel("value [µV]");
yAxisSignal.setPrefWidth(50);
final SignalChart signalChart = new SignalChart(signal, tMin, tMax, xAxisSignal, yAxisSignal);
windowType.setOnAction(new EventHandler() {
@Override
public void handle(Event event) {
WindowType wt = (WindowType) windowType.getSelectionModel().getSelectedItem();
if (wt != null) {
renderer.setWindowType(wt);
refresher.refreshChartImage(chart);
}
}
});
windowSize.setOnAction(new EventHandler() {
@Override
public void handle(Event event) {
Object item = windowSize.getSelectionModel().getSelectedItem();
Integer ws = SignalAnalysisTools.parsePositiveInteger(item);
if (ws != null) {
renderer.setWindowLength(ws);
refresher.refreshChartImage(chart);
}
}
});
paletteType.setOnAction(new EventHandler() {
@Override
public void handle(Event event) {
WignerMapPalette pt = (WignerMapPalette) paletteType.getSelectionModel().getSelectedItem();
if (pt != null) {
renderer.setPaletteType(pt);
refresher.refreshChartImage(chart);
}
}
});
final CheckBox padToHeight = (CheckBox) fxmlLoader.getNamespace().get("padToHeightCheckBox");
padToHeight.setSelected(renderer.getPadToHeight());
padToHeight.selectedProperty().addListener(new ChangeListener<Boolean>() {
@Override
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
renderer.setPadToHeight(newValue);
refresher.refreshChartImage(chart);
}
});
chart.setOnCursorOffChart(new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent event) {
signalChart.clearWaveform();
}
});
chart.setOnCursorOnChart(new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent event) {
Object item = windowSize.getSelectionModel().getSelectedItem();
Integer ws = SignalAnalysisTools.parsePositiveInteger(item);
TimeFrequency tf = chart.getTimeFrequency((int)event.getX(), (int)event.getY());
if (ws != null && tf != null) {
WindowType wt = (WindowType) windowType.getSelectionModel().getSelectedItem();
signalChart.setWaveform(new SineWaveform(tf.f, 0.5*ws/samplingFrequency), wt, tf.t, tf.v);
}
}
});
BorderPane main = new BorderPane();
main.setCenter(chart);
main.setBottom(signalChart);
root.setCenter(main);
root.setLeft(settingsPanel);
}
public Pane getPane() {
return root;
}
}