Adds phase viewer and recent files menu item to viewer desktop.

This commit is contained in:
Denny
2018-02-12 12:34:42 +00:00
parent 1406d5576a
commit 956bf2d76a
7 changed files with 305 additions and 6 deletions

View File

@@ -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 <http://www.gnu.org/licenses/>
*
******************************************************************************/
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<ComplexBuffer>
{
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<Data<Integer,Float>> 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<Integer,Float> phaseSeries = new Series<>("Phase", mPhaseValues);
ObservableList<Series<Integer,Float>> observableList = FXCollections.observableArrayList(phaseSeries);
setData(observableList);
init(length);
}
private void init(int length)
{
mLengthProperty.addListener(new ChangeListener<Number>()
{
@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());
}
}
}
}

View File

@@ -37,7 +37,7 @@ public class ComplexSampleLineChart extends LineChart implements Listener<Comple
private final static Logger mLog = LoggerFactory.getLogger(ComplexSampleLineChart.class);
private ComplexCircularBuffer mComplexCircularBuffer;
private ComplexGain mComplexGain = new ComplexGain(1000.0f);
private ComplexGain mComplexGain = new ComplexGain(500.0f);
private ObservableList<XYChart.Data<Integer,Float>> mISamples = FXCollections.observableArrayList();
private ObservableList<XYChart.Data<Integer,Float>> mQSamples = FXCollections.observableArrayList();
private IntegerProperty mLengthProperty = new SimpleIntegerProperty(40);
@@ -94,8 +94,9 @@ public class ComplexSampleLineChart extends LineChart implements Listener<Comple
for(int x = 0; x < samples.length; x += 2)
{
sample = new Complex(samples[x], samples[x + 1]);
mComplexGain.apply(sample);
mComplexCircularBuffer.put(sample);
Complex copy = sample.copy();
mComplexGain.apply(copy);
mComplexCircularBuffer.put(copy);
}
updateChart();
@@ -111,6 +112,7 @@ public class ComplexSampleLineChart extends LineChart implements Listener<Comple
if(sample != null)
{
sample.normalize();
mISamples.get(x).setYValue(sample.inphase());
mQSamples.get(x).setYValue(sample.quadrature());
}

View File

@@ -37,6 +37,7 @@ public class DemodulatorViewerFX extends Application
{
private Stage mStage;
private MenuBar mMenuBar;
private RecentFilesMenuItem mRecentFilesMenuItem;
private ViewerDesktop mViewerDesktop;
@Override
@@ -82,6 +83,7 @@ public class DemodulatorViewerFX extends Application
fileChooser.setTitle("Open an I/Q recording file");
File file = fileChooser.showOpenDialog(mStage);
getViewerDesktop().load(file);
getRecentFilesMenuItem().add(file);
}
});
@@ -105,7 +107,10 @@ public class DemodulatorViewerFX extends Application
}
});
menuFile.getItems().addAll(menuItemOpen, menuItemClose, new SeparatorMenuItem(), menuItemExit);
mRecentFilesMenuItem = new RecentFilesMenuItem("demodulator.viewer", 5);
menuFile.getItems().addAll(menuItemOpen, getRecentFilesMenuItem(), menuItemClose, new SeparatorMenuItem(),
menuItemExit);
mMenuBar.getMenus().addAll(menuFile);
}
@@ -113,6 +118,24 @@ public class DemodulatorViewerFX extends Application
return mMenuBar;
}
private RecentFilesMenuItem getRecentFilesMenuItem()
{
if(mRecentFilesMenuItem == null)
{
mRecentFilesMenuItem = new RecentFilesMenuItem("demodulator.viewer", 5);
mRecentFilesMenuItem.setFileSelectionListener(new RecentFilesMenuItem.IFileSelectionListener()
{
@Override
public void fileSelected(File file)
{
getViewerDesktop().load(file);
}
});
}
return mRecentFilesMenuItem;
}
public static void main(String[] args)
{
launch(args);

View File

@@ -99,6 +99,21 @@ public class PlaybackController extends HBox implements IFrameLocationListener,
}
}
public void close()
{
if(mControllableFileSource != null)
{
try
{
mControllableFileSource.close();
}
catch(IOException ioe)
{
mLog.error("Error closing file", ioe);
}
}
}
/**
* Adds listener to receive complex buffers from this playback
*/

View File

@@ -0,0 +1,106 @@
package io.github.dsheirer.instrument.gui.viewer;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.control.Menu;
import javafx.scene.control.MenuItem;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.util.prefs.Preferences;
public class RecentFilesMenuItem extends Menu implements EventHandler<ActionEvent>
{
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);
}
}

View File

@@ -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;
}
}

View File

@@ -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.
*