mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-12 08:37:25 -05:00
Adds phase viewer and recent files menu item to viewer desktop.
This commit is contained in:
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user