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 extends Number> 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.
*