mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-12 16:47:31 -05:00
Implements a complex sample delay buffer integrated into the tuner
channel manager so that channel source requests can have a start timestamp specified and the channel can be preloaded with samples from the delay buffer. If no timestamp is specified, current time is assumed. This feature requires corresponding updates to any decoder that wishes to implement the timestamped sample preloading functionality.
This commit is contained in:
@@ -34,6 +34,7 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.prefs.Preferences;
|
||||
|
||||
/**
|
||||
* Application for viewing and working with demodulators and recording files.
|
||||
@@ -42,6 +43,9 @@ public class DemodulatorViewerFX extends Application
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger(DemodulatorViewerFX.class);
|
||||
|
||||
private static final String PREFERENCE_FILE_OPEN_DEFAULT_FOLDER = "default.folder";
|
||||
|
||||
private Preferences mPreferences = Preferences.userNodeForPackage(DemodulatorViewerFX.class);
|
||||
private Stage mStage;
|
||||
private MenuBar mMenuBar;
|
||||
private RecentFilesMenu mRecentFilesMenu;
|
||||
@@ -117,9 +121,22 @@ public class DemodulatorViewerFX extends Application
|
||||
{
|
||||
FileChooser fileChooser = new FileChooser();
|
||||
fileChooser.setTitle("Open an I/Q recording file");
|
||||
|
||||
String directory = mPreferences.get(PREFERENCE_FILE_OPEN_DEFAULT_FOLDER, null);
|
||||
|
||||
if(directory != null)
|
||||
{
|
||||
fileChooser.setInitialDirectory(new File(directory));
|
||||
}
|
||||
|
||||
File file = fileChooser.showOpenDialog(mStage);
|
||||
getViewerDesktop().load(file);
|
||||
getRecentFilesMenu().add(file);
|
||||
|
||||
if(file != null)
|
||||
{
|
||||
getViewerDesktop().load(file);
|
||||
getRecentFilesMenu().add(file);
|
||||
mPreferences.put(PREFERENCE_FILE_OPEN_DEFAULT_FOLDER, file.getParent());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ import java.util.prefs.Preferences;
|
||||
|
||||
public class RecentFilesMenu extends Menu implements EventHandler<ActionEvent>
|
||||
{
|
||||
private static final String PREFERENCE_RECENT_FILE = ".recent.file.";
|
||||
private Preferences mPreferences = Preferences.userNodeForPackage(RecentFilesMenu.class);
|
||||
private IFileSelectionListener mFileSelectionListener;
|
||||
private String mIdentifier;
|
||||
@@ -111,7 +112,7 @@ public class RecentFilesMenu extends Menu implements EventHandler<ActionEvent>
|
||||
*/
|
||||
private String getKey(int index)
|
||||
{
|
||||
return mIdentifier + ".recent.file." + index;
|
||||
return mIdentifier + PREFERENCE_RECENT_FILE + index;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -21,6 +21,7 @@ import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.scene.chart.LineChart;
|
||||
import javafx.scene.chart.NumberAxis;
|
||||
import javafx.scene.chart.ValueAxis;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -42,6 +43,24 @@ public class DoubleLineChart extends LineChart implements Listener<Double>
|
||||
init(length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the minimum displayable value for the y axis
|
||||
* @param min
|
||||
*/
|
||||
public void setMin(double min)
|
||||
{
|
||||
((ValueAxis)getYAxis()).setLowerBound(min);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the maximum displayable value for the y axis
|
||||
* @param max
|
||||
*/
|
||||
public void setMax(double max)
|
||||
{
|
||||
((ValueAxis)getYAxis()).setUpperBound(max);
|
||||
}
|
||||
|
||||
private void init(int length)
|
||||
{
|
||||
mDoubleCircularBuffer = new DoubleCircularBuffer(length);
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
/*******************************************************************************
|
||||
* 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.gui.instrument.chart;
|
||||
|
||||
import javafx.collections.ObservableList;
|
||||
|
||||
public class SamplesPerSymbolChart extends DoubleLineChart
|
||||
{
|
||||
private double mFractionalSamplesPerSymbol;
|
||||
private double mPreviousSamplesPerSymbol;
|
||||
|
||||
public SamplesPerSymbolChart(int length, double samplesPerSymbol)
|
||||
{
|
||||
super("Samples Per Symbol", -1.0, 1.0, 0.1, length);
|
||||
setSamplesPerSymbol(samplesPerSymbol);
|
||||
}
|
||||
|
||||
public void setSamplesPerSymbol(double samplesPerSymbol)
|
||||
{
|
||||
mFractionalSamplesPerSymbol = samplesPerSymbol - ((int)samplesPerSymbol);
|
||||
mPreviousSamplesPerSymbol = samplesPerSymbol;
|
||||
|
||||
ObservableList<Series> seriesList = getData();
|
||||
seriesList.get(0).setName("Samples Per Symbol:" + samplesPerSymbol);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive(Double samplesPerSymbol)
|
||||
{
|
||||
double delta = samplesPerSymbol - mPreviousSamplesPerSymbol - mFractionalSamplesPerSymbol;
|
||||
|
||||
while(delta < (-1.0 + mFractionalSamplesPerSymbol))
|
||||
{
|
||||
delta++;
|
||||
}
|
||||
|
||||
while(delta > 1.0)
|
||||
{
|
||||
delta--;
|
||||
}
|
||||
|
||||
super.receive(delta);
|
||||
|
||||
mPreviousSamplesPerSymbol = samplesPerSymbol;
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,7 @@ import io.github.dsheirer.gui.instrument.chart.ComplexSampleLineChart;
|
||||
import io.github.dsheirer.gui.instrument.chart.DoubleLineChart;
|
||||
import io.github.dsheirer.gui.instrument.chart.EyeDiagramChart;
|
||||
import io.github.dsheirer.gui.instrument.chart.PhaseLineChart;
|
||||
import io.github.dsheirer.gui.instrument.chart.SamplesPerSymbolChart;
|
||||
import io.github.dsheirer.gui.instrument.chart.SymbolChart;
|
||||
import io.github.dsheirer.message.Message;
|
||||
import io.github.dsheirer.module.decode.DecoderType;
|
||||
@@ -34,14 +35,14 @@ public class P25Phase1LSMPane extends ComplexDecoderPane
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger(P25Phase1LSMPane.class);
|
||||
|
||||
private HBox mSampleChartBox;
|
||||
private ComplexSampleLineChart mSampleLineChart;
|
||||
private HBox mTopChartBox;
|
||||
private ComplexSampleLineChart mDifferentialDemodulatedSamplesChartBox;
|
||||
private EyeDiagramChart mEyeDiagramChart;
|
||||
private HBox mDecoderChartBox;
|
||||
private SymbolChart mSymbolChart;
|
||||
private HBox mBottomChartBox;
|
||||
private SymbolChart mSymbolConstellationChart;
|
||||
private PhaseLineChart mPLLPhaseErrorLineChart;
|
||||
private DoubleLineChart mPLLFrequencyLineChart;
|
||||
private DoubleLineChart mSamplesPerSymbolLineChart;
|
||||
private SamplesPerSymbolChart mSamplesPerSymbolLineChart;
|
||||
private ReusableBufferBroadcaster mFilteredBufferBroadcaster = new ReusableBufferBroadcaster();
|
||||
private P25DecoderLSMInstrumented mDecoder = new P25DecoderLSMInstrumented(null);
|
||||
|
||||
@@ -56,16 +57,16 @@ public class P25Phase1LSMPane extends ComplexDecoderPane
|
||||
addListener(getDecoder());
|
||||
|
||||
getDecoder().setFilteredBufferListener(mFilteredBufferBroadcaster);
|
||||
getDecoder().setComplexSymbolListener(getSymbolChart());
|
||||
getDecoder().setComplexSymbolListener(getSymbolConstellationChart());
|
||||
getDecoder().setPLLPhaseErrorListener(getPLLPhaseErrorLineChart());
|
||||
getDecoder().setPLLFrequencyListener(getPLLFrequencyLineChart());
|
||||
getDecoder().setSymbolDecisionDataListener(getEyeDiagramChart());
|
||||
getDecoder().setSamplesPerSymbolListener(getSamplesPerSymbolLineChart());
|
||||
mFilteredBufferBroadcaster.addListener(getSampleLineChart());
|
||||
mFilteredBufferBroadcaster.addListener(getDifferentialDemodulatedSamplesChartBox());
|
||||
|
||||
HBox.setHgrow(getSampleChartBox(), Priority.ALWAYS);
|
||||
HBox.setHgrow(getDecoderChartBox(), Priority.ALWAYS);
|
||||
getChildren().addAll(getSampleChartBox(), getDecoderChartBox());
|
||||
HBox.setHgrow(getTopChartBox(), Priority.ALWAYS);
|
||||
HBox.setHgrow(getBottomChartBox(), Priority.ALWAYS);
|
||||
getChildren().addAll(getTopChartBox(), getBottomChartBox());
|
||||
|
||||
mDecoder.setMessageListener(new Listener<Message>()
|
||||
{
|
||||
@@ -86,7 +87,8 @@ public class P25Phase1LSMPane extends ComplexDecoderPane
|
||||
mDecoder.setSampleRate(sampleRate);
|
||||
double samplesPerSymbol = sampleRate / 4800.0;
|
||||
|
||||
getSampleLineChart().setSamplesPerSymbol((int)samplesPerSymbol);
|
||||
getDifferentialDemodulatedSamplesChartBox().setSamplesPerSymbol((int)samplesPerSymbol);
|
||||
getSamplesPerSymbolLineChart().setSamplesPerSymbol(samplesPerSymbol);
|
||||
}
|
||||
|
||||
private P25DecoderLSMInstrumented getDecoder()
|
||||
@@ -94,69 +96,69 @@ public class P25Phase1LSMPane extends ComplexDecoderPane
|
||||
return mDecoder;
|
||||
}
|
||||
|
||||
private SymbolChart getSymbolChart()
|
||||
private SymbolChart getSymbolConstellationChart()
|
||||
{
|
||||
if(mSymbolChart == null)
|
||||
if(mSymbolConstellationChart == null)
|
||||
{
|
||||
mSymbolChart = new SymbolChart(10);
|
||||
mSymbolConstellationChart = new SymbolChart(10);
|
||||
}
|
||||
|
||||
return mSymbolChart;
|
||||
return mSymbolConstellationChart;
|
||||
}
|
||||
|
||||
private HBox getDecoderChartBox()
|
||||
private HBox getBottomChartBox()
|
||||
{
|
||||
if(mDecoderChartBox == null)
|
||||
if(mBottomChartBox == null)
|
||||
{
|
||||
mDecoderChartBox = new HBox();
|
||||
mDecoderChartBox.setMaxHeight(Double.MAX_VALUE);
|
||||
getSymbolChart().setMaxWidth(Double.MAX_VALUE);
|
||||
mBottomChartBox = new HBox();
|
||||
mBottomChartBox.setMaxHeight(Double.MAX_VALUE);
|
||||
getSymbolConstellationChart().setMaxWidth(Double.MAX_VALUE);
|
||||
getPLLPhaseErrorLineChart().setMaxWidth(Double.MAX_VALUE);
|
||||
getPLLFrequencyLineChart().setMaxWidth(Double.MAX_VALUE);
|
||||
HBox.setHgrow(getSymbolChart(), Priority.ALWAYS);
|
||||
HBox.setHgrow(getSymbolConstellationChart(), Priority.ALWAYS);
|
||||
HBox.setHgrow(getPLLPhaseErrorLineChart(), Priority.ALWAYS);
|
||||
HBox.setHgrow(getPLLFrequencyLineChart(), Priority.ALWAYS);
|
||||
mDecoderChartBox.getChildren().addAll(getSymbolChart(), getPLLPhaseErrorLineChart(),
|
||||
mBottomChartBox.getChildren().addAll(getSymbolConstellationChart(), getPLLPhaseErrorLineChart(),
|
||||
getPLLFrequencyLineChart());
|
||||
}
|
||||
|
||||
return mDecoderChartBox;
|
||||
return mBottomChartBox;
|
||||
}
|
||||
|
||||
private HBox getSampleChartBox()
|
||||
private HBox getTopChartBox()
|
||||
{
|
||||
if(mSampleChartBox == null)
|
||||
if(mTopChartBox == null)
|
||||
{
|
||||
mSampleChartBox = new HBox();
|
||||
mSampleChartBox.setMaxHeight(Double.MAX_VALUE);
|
||||
getSampleLineChart().setMaxWidth(Double.MAX_VALUE);
|
||||
mTopChartBox = new HBox();
|
||||
mTopChartBox.setMaxHeight(Double.MAX_VALUE);
|
||||
getDifferentialDemodulatedSamplesChartBox().setMaxWidth(Double.MAX_VALUE);
|
||||
getEyeDiagramChart().setMaxWidth(Double.MAX_VALUE);
|
||||
getSamplesPerSymbolLineChart().setMaxWidth(Double.MAX_VALUE);
|
||||
HBox.setHgrow(getSampleLineChart(), Priority.ALWAYS);
|
||||
HBox.setHgrow(getDifferentialDemodulatedSamplesChartBox(), Priority.ALWAYS);
|
||||
HBox.setHgrow(getEyeDiagramChart(), Priority.ALWAYS);
|
||||
HBox.setHgrow(getSamplesPerSymbolLineChart(), Priority.ALWAYS);
|
||||
mSampleChartBox.getChildren().addAll(getSampleLineChart(), getEyeDiagramChart(),
|
||||
mTopChartBox.getChildren().addAll(getDifferentialDemodulatedSamplesChartBox(), getEyeDiagramChart(),
|
||||
getSamplesPerSymbolLineChart());
|
||||
}
|
||||
|
||||
return mSampleChartBox;
|
||||
return mTopChartBox;
|
||||
}
|
||||
|
||||
private ComplexSampleLineChart getSampleLineChart()
|
||||
private ComplexSampleLineChart getDifferentialDemodulatedSamplesChartBox()
|
||||
{
|
||||
if(mSampleLineChart == null)
|
||||
if(mDifferentialDemodulatedSamplesChartBox == null)
|
||||
{
|
||||
mSampleLineChart = new ComplexSampleLineChart(100, 10);
|
||||
mDifferentialDemodulatedSamplesChartBox = new ComplexSampleLineChart(100, 10);
|
||||
}
|
||||
|
||||
return mSampleLineChart;
|
||||
return mDifferentialDemodulatedSamplesChartBox;
|
||||
}
|
||||
|
||||
private EyeDiagramChart getEyeDiagramChart()
|
||||
{
|
||||
if(mEyeDiagramChart == null)
|
||||
{
|
||||
mEyeDiagramChart = new EyeDiagramChart(10, "Middle: 3-4 / Symbol: 8-9");
|
||||
mEyeDiagramChart = new EyeDiagramChart(10, "Symbol/Eye Diagram");
|
||||
}
|
||||
|
||||
return mEyeDiagramChart;
|
||||
@@ -182,11 +184,11 @@ public class P25Phase1LSMPane extends ComplexDecoderPane
|
||||
return mPLLFrequencyLineChart;
|
||||
}
|
||||
|
||||
private DoubleLineChart getSamplesPerSymbolLineChart()
|
||||
private SamplesPerSymbolChart getSamplesPerSymbolLineChart()
|
||||
{
|
||||
if(mSamplesPerSymbolLineChart == null)
|
||||
{
|
||||
mSamplesPerSymbolLineChart = new DoubleLineChart( "Sample Point", 4.5, 7.0, 0.1, 40);
|
||||
mSamplesPerSymbolLineChart = new SamplesPerSymbolChart(40, 6.25);
|
||||
}
|
||||
|
||||
return mSamplesPerSymbolLineChart;
|
||||
|
||||
Reference in New Issue
Block a user