diff --git a/src/main/java/io/github/dsheirer/instrument/gui/viewer/ComplexPhaseLineChart.java b/src/main/java/io/github/dsheirer/instrument/gui/viewer/ComplexPhaseLineChart.java new file mode 100644 index 00000000..08189c93 --- /dev/null +++ b/src/main/java/io/github/dsheirer/instrument/gui/viewer/ComplexPhaseLineChart.java @@ -0,0 +1,122 @@ +/******************************************************************************* + * sdr-trunk + * Copyright (C) 2014-2018 Dennis Sheirer + * + * 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 3 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, see + * + ******************************************************************************/ +package io.github.dsheirer.instrument.gui.viewer; + +import io.github.dsheirer.buffer.ComplexCircularBuffer; +import io.github.dsheirer.dsp.gain.ComplexGain; +import io.github.dsheirer.sample.Listener; +import io.github.dsheirer.sample.complex.Complex; +import io.github.dsheirer.sample.complex.ComplexBuffer; +import javafx.beans.property.IntegerProperty; +import javafx.beans.property.SimpleIntegerProperty; +import javafx.beans.value.ChangeListener; +import javafx.beans.value.ObservableValue; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.scene.chart.LineChart; +import javafx.scene.chart.NumberAxis; +import org.apache.commons.math3.complex.ComplexUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ComplexPhaseLineChart extends LineChart implements Listener +{ + private final static Logger mLog = LoggerFactory.getLogger(ComplexPhaseLineChart.class); + + private static final Complex ANGLE_OFFSET_45_DEGREES = Complex.fromAngle((float)(Math.PI / 4.0)); + private ComplexCircularBuffer mComplexCircularBuffer; + private ComplexGain mComplexGain = new ComplexGain(600.0f); + private ObservableList> mPhaseValues = FXCollections.observableArrayList(); + private IntegerProperty mLengthProperty = new SimpleIntegerProperty(40); + + public ComplexPhaseLineChart(int length) + { + super(new NumberAxis("Time", 1, length - 10, 1), + new NumberAxis("Phase", -Math.PI, Math.PI, 1.0)); + + Series phaseSeries = new Series<>("Phase", mPhaseValues); + ObservableList> observableList = FXCollections.observableArrayList(phaseSeries); + + setData(observableList); + init(length); + } + + private void init(int length) + { + mLengthProperty.addListener(new ChangeListener() + { + @Override + public void changed(ObservableValue observable, Number oldValue, Number newValue) + { + mComplexCircularBuffer = new ComplexCircularBuffer(newValue.intValue()); + } + }); + mComplexCircularBuffer = new ComplexCircularBuffer(length); + length().setValue(length); + + for(int x = 10; x <= length; x++) + { + mPhaseValues.add(new Data(x - 10, 0.0f)); + } + } + + /** + * Length of the x-axis for displaying samples + */ + public IntegerProperty length() + { + return mLengthProperty; + } + + @Override + public void receive(ComplexBuffer complexBuffer) + { + float[] samples = complexBuffer.getSamples(); + + Complex sample; + + for(int x = 0; x < samples.length; x += 2) + { + sample = new Complex(samples[x], samples[x + 1]); + Complex copy = sample.copy(); + mComplexGain.apply(copy); + mComplexCircularBuffer.put(copy); + } + + updateChart(); + } + + private void updateChart() + { + Complex[] samples = mComplexCircularBuffer.getAll(); + + for(int x = 10; x < samples.length; x++) + { + Complex previous = samples[x - 10].copy(); + Complex sample = samples[x].copy(); + + if(previous != null && sample != null) + { +// previous.normalize(); +// sample.normalize(); + previous.add(ANGLE_OFFSET_45_DEGREES); + sample.multiply(previous.conjugate()); +// sample.normalize(); + mPhaseValues.get(x - 10).setYValue(sample.angle()); + } + } + } +} diff --git a/src/main/java/io/github/dsheirer/instrument/gui/viewer/ComplexSampleLineChart.java b/src/main/java/io/github/dsheirer/instrument/gui/viewer/ComplexSampleLineChart.java index c141a3d3..7044bdac 100644 --- a/src/main/java/io/github/dsheirer/instrument/gui/viewer/ComplexSampleLineChart.java +++ b/src/main/java/io/github/dsheirer/instrument/gui/viewer/ComplexSampleLineChart.java @@ -37,7 +37,7 @@ public class ComplexSampleLineChart extends LineChart implements Listener> mISamples = FXCollections.observableArrayList(); private ObservableList> mQSamples = FXCollections.observableArrayList(); private IntegerProperty mLengthProperty = new SimpleIntegerProperty(40); @@ -94,8 +94,9 @@ public class ComplexSampleLineChart extends LineChart implements Listener +{ + private final static Logger mLog = LoggerFactory.getLogger(RecentFilesMenuItem.class); + + private Preferences mPreferences = Preferences.userNodeForPackage(RecentFilesMenuItem.class); + private IFileSelectionListener mFileSelectionListener; + private String mIdentifier; + private int mSize; + + public RecentFilesMenuItem(String identifier, int size) + { + super("Recent"); + mIdentifier = identifier; + mSize = size; + + load(); + } + + public void setFileSelectionListener(IFileSelectionListener listener) + { + mFileSelectionListener = listener; + } + + public void add(File file) + { + if(file != null) + { + int index = mSize - 1; + + while(index > 0) + { + String newerItem = mPreferences.get(getKey(index - 1), null); + + if(newerItem != null) + { + mPreferences.put(getKey(index), newerItem); + } + + index--; + } + + mPreferences.put(getKey(0), file.getAbsolutePath()); + } + + load(); + } + + private String getKey(int index) + { + return mIdentifier + ".recent." + index; + } + + private void load() + { + getItems().clear(); + + for(int x = 0; x < mSize; x++) + { + String path = mPreferences.get(getKey(x), null); + + if(path != null) + { + File file = new File(path); + + if(file.isFile()) + { + MenuItem menuItem = new MenuItem(file.getName()); + menuItem.setUserData(file); + menuItem.setOnAction(this); + getItems().add(menuItem); + } + } + } + } + + @Override + public void handle(ActionEvent event) + { + if(mFileSelectionListener != null) + { + Object fileObject = ((MenuItem)event.getSource()).getUserData(); + + if(fileObject instanceof File) + { + mFileSelectionListener.fileSelected((File)fileObject); + } + } + } + + public interface IFileSelectionListener + { + void fileSelected(File file); + } +} diff --git a/src/main/java/io/github/dsheirer/instrument/gui/viewer/ViewerDesktop.java b/src/main/java/io/github/dsheirer/instrument/gui/viewer/ViewerDesktop.java index 2c9449bf..38e84acc 100644 --- a/src/main/java/io/github/dsheirer/instrument/gui/viewer/ViewerDesktop.java +++ b/src/main/java/io/github/dsheirer/instrument/gui/viewer/ViewerDesktop.java @@ -16,29 +16,35 @@ package io.github.dsheirer.instrument.gui.viewer; import javafx.scene.layout.BorderPane; +import javafx.scene.layout.HBox; +import javafx.scene.layout.VBox; import java.io.File; public class ViewerDesktop extends BorderPane { private PlaybackController mPlaybackController; + private VBox mChartBox; private ComplexSampleLineChart mSampleLineChart; + private ComplexPhaseLineChart mPhaseLineChart; public ViewerDesktop() { setBottom(getPlaybackController()); getPlaybackController().addListener(getSampleLineChart()); - setCenter(getSampleLineChart()); + getPlaybackController().addListener(getPhaseLineChart()); + setCenter(getChartBox()); } public void load(File file) { + close(); getPlaybackController().load(file); } public void close() { - + getPlaybackController().close(); } private PlaybackController getPlaybackController() @@ -51,6 +57,16 @@ public class ViewerDesktop extends BorderPane return mPlaybackController; } + private VBox getChartBox() + { + if(mChartBox == null) + { + mChartBox = new VBox(); + mChartBox.getChildren().addAll(getSampleLineChart(), getPhaseLineChart()); + } + + return mChartBox; + } private ComplexSampleLineChart getSampleLineChart() { @@ -61,4 +77,14 @@ public class ViewerDesktop extends BorderPane return mSampleLineChart; } + + private ComplexPhaseLineChart getPhaseLineChart() + { + if(mPhaseLineChart == null) + { + mPhaseLineChart = new ComplexPhaseLineChart(50); + } + + return mPhaseLineChart; + } } diff --git a/src/main/java/io/github/dsheirer/sample/complex/Complex.java b/src/main/java/io/github/dsheirer/sample/complex/Complex.java index e9a7fdc0..afeaee9f 100644 --- a/src/main/java/io/github/dsheirer/sample/complex/Complex.java +++ b/src/main/java/io/github/dsheirer/sample/complex/Complex.java @@ -358,6 +358,11 @@ public class Complex implements Serializable return (float)Math.atan2( y(), x() ); } + public float angleDegrees() + { + return (float)Math.toDegrees(angle()); + } + /** * Constrains the i and q quantities to +/- value. *