mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-12 16:47:31 -05:00
P25 Phase 2 Decoder implementation.
This commit is contained in:
@@ -36,6 +36,7 @@ public class SourceEvent
|
||||
NOTIFICATION_FREQUENCY_ROTATION_FAILURE,
|
||||
NOTIFICATION_MEASURED_FREQUENCY_ERROR,
|
||||
NOTIFICATION_MEASURED_FREQUENCY_ERROR_SYNC_LOCKED,
|
||||
NOTIFICATION_RECORDING_FILE_LOADED,
|
||||
NOTIFICATION_SAMPLE_RATE_CHANGE,
|
||||
NOTIFICATION_STOP_SAMPLE_STREAM,
|
||||
|
||||
@@ -276,6 +277,14 @@ public class SourceEvent
|
||||
return new SourceEvent(Event.NOTIFICATION_CHANNEL_SAMPLE_RATE_CHANGE, sampleRate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Notification that a baseband recording file has been loaded
|
||||
*/
|
||||
public static SourceEvent recordingFileLoaded()
|
||||
{
|
||||
return new SourceEvent(Event.NOTIFICATION_RECORDING_FILE_LOADED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new channel count change event
|
||||
*
|
||||
|
||||
@@ -46,39 +46,52 @@ public abstract class Tuner implements ISourceEventProcessor
|
||||
private TunerFrequencyErrorMonitor mTunerFrequencyErrorMonitor;
|
||||
private String mName;
|
||||
|
||||
/**
|
||||
* Abstract tuner class.
|
||||
* @param name of the tuner
|
||||
* @param tunerController for the tuner
|
||||
*/
|
||||
public Tuner(String name, TunerController tunerController, UserPreferences userPreferences)
|
||||
public Tuner(String name, TunerController tunerController)
|
||||
{
|
||||
mName = name;
|
||||
mTunerController = tunerController;
|
||||
//Register to receive frequency and sample rate change notifications
|
||||
mTunerController.addListener(this::process);
|
||||
|
||||
ChannelizerType channelizerType = userPreferences.getTunerPreference().getChannelizerType();
|
||||
mTunerFrequencyErrorMonitor = new TunerFrequencyErrorMonitor(this);
|
||||
mTunerFrequencyErrorMonitor.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Abstract tuner class.
|
||||
* @param name of the tuner
|
||||
* @param tunerController for the tuner
|
||||
* @param userPreferences to discover preferred channelizer type
|
||||
*/
|
||||
public Tuner(String name, TunerController tunerController, UserPreferences userPreferences)
|
||||
{
|
||||
this(name, tunerController);
|
||||
|
||||
ChannelizerType channelizerType = userPreferences.getTunerPreference().getChannelizerType();
|
||||
if(channelizerType == ChannelizerType.POLYPHASE)
|
||||
{
|
||||
mChannelSourceManager = new PolyphaseChannelSourceManager(mTunerController);
|
||||
setChannelSourceManager(new PolyphaseChannelSourceManager(mTunerController));
|
||||
}
|
||||
else if(channelizerType == ChannelizerType.HETERODYNE)
|
||||
{
|
||||
mChannelSourceManager = new HeterodyneChannelSourceManager(mTunerController);
|
||||
setChannelSourceManager(new HeterodyneChannelSourceManager(mTunerController));
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new IllegalArgumentException("Unrecognized channelizer type: " + channelizerType);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the channel source manager
|
||||
* @param manager to use
|
||||
*/
|
||||
protected void setChannelSourceManager(ChannelSourceManager manager)
|
||||
{
|
||||
mChannelSourceManager = manager;
|
||||
|
||||
//Register to receive channel count change notifications
|
||||
mChannelSourceManager.addSourceEventListener(this::process);
|
||||
|
||||
mTunerFrequencyErrorMonitor = new TunerFrequencyErrorMonitor(this);
|
||||
mTunerFrequencyErrorMonitor.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -105,6 +118,9 @@ public abstract class Tuner implements ISourceEventProcessor
|
||||
case NOTIFICATION_MEASURED_FREQUENCY_ERROR_SYNC_LOCKED:
|
||||
mTunerFrequencyErrorMonitor.receive(event);
|
||||
break;
|
||||
case NOTIFICATION_RECORDING_FILE_LOADED:
|
||||
//ignore
|
||||
break;
|
||||
default:
|
||||
mLog.debug("Unrecognized Source Event: " + event.toString());
|
||||
break;
|
||||
|
||||
@@ -1,20 +1,24 @@
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* Copyright (C) 2014 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/>
|
||||
******************************************************************************/
|
||||
/*
|
||||
*
|
||||
* * ******************************************************************************
|
||||
* * Copyright (C) 2014-2019 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.source.tuner;
|
||||
|
||||
import javax.usb.UsbDeviceDescriptor;
|
||||
@@ -53,6 +57,7 @@ public enum TunerClass
|
||||
TWINTECH_UT40( TunerType.FITIPOWER_FC0013, "1B80", "D3A4", "Twintech", "UT-40" ),
|
||||
ZAAPA_ZTMINDVBZP( TunerType.FITIPOWER_FC0012, "1B80", "D398", "Zaapa", "ZT-MINDVBZP" ),
|
||||
TEST_TUNER(TunerType.TEST, "0", "0", "ABC Tuners Inc.", "Model XYZ"),
|
||||
RECORDING_TUNER(TunerType.RECORDING, "0", "0", "Recording Tuner", "Recording"),
|
||||
UNKNOWN( TunerType.UNKNOWN, "0", "0", "Unknown Manufacturer", "Unknown Device" );
|
||||
|
||||
private TunerType mTunerType;
|
||||
|
||||
@@ -1,22 +1,28 @@
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* Copyright (C) 2014-2017 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.
|
||||
* * ******************************************************************************
|
||||
* * Copyright (C) 2014-2019 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/>
|
||||
* * *****************************************************************************
|
||||
*
|
||||
* 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.source.tuner;
|
||||
|
||||
import io.github.dsheirer.record.RecorderManager;
|
||||
import io.github.dsheirer.record.wave.ComplexBufferWaveRecorder;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.sample.buffer.IReusableComplexBufferProvider;
|
||||
import io.github.dsheirer.sample.buffer.ReusableBufferBroadcaster;
|
||||
@@ -45,6 +51,7 @@ public abstract class TunerController implements Tunable, ISourceEventProcessor,
|
||||
private double mUsableBandwidthPercentage;
|
||||
private Listener<SourceEvent> mSourceEventListener;
|
||||
private int mMeasuredFrequencyError;
|
||||
private ComplexBufferWaveRecorder mRecorder;
|
||||
|
||||
/**
|
||||
* Abstract tuner controller class. The tuner controller manages frequency bandwidth and currently tuned channels
|
||||
@@ -445,4 +452,40 @@ public abstract class TunerController implements Tunable, ISourceEventProcessor,
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Records the complex I/Q buffers produced by the tuner
|
||||
* @param recorderManager to obtain a baseband recorder
|
||||
*/
|
||||
public void startRecorder(RecorderManager recorderManager)
|
||||
{
|
||||
if(!isRecording())
|
||||
{
|
||||
mRecorder = recorderManager.getBasebandRecorder("TUNER_" + getFrequency());
|
||||
mRecorder.setSampleRate((float)getSampleRate());
|
||||
mRecorder.start();
|
||||
addBufferListener(mRecorder);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the recording of complex I/Q buffers
|
||||
*/
|
||||
public void stopRecorder()
|
||||
{
|
||||
if(isRecording())
|
||||
{
|
||||
removeBufferListener(mRecorder);
|
||||
mRecorder.stop();
|
||||
mRecorder = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if this tuner controller is currently reocording the complex I/Q sample buffers produced by this tuner
|
||||
*/
|
||||
public boolean isRecording()
|
||||
{
|
||||
return mRecorder != null;
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,24 @@
|
||||
/*******************************************************************************
|
||||
* 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.
|
||||
* * ******************************************************************************
|
||||
* * Copyright (C) 2014-2019 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/>
|
||||
* * *****************************************************************************
|
||||
*
|
||||
* 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.source.tuner;
|
||||
|
||||
@@ -20,6 +26,7 @@ import com.jidesoft.swing.JideSplitPane;
|
||||
import io.github.dsheirer.gui.control.JFrequencyControl;
|
||||
import io.github.dsheirer.gui.editor.Editor;
|
||||
import io.github.dsheirer.gui.editor.EmptyEditor;
|
||||
import io.github.dsheirer.record.RecorderManager;
|
||||
import io.github.dsheirer.source.tuner.configuration.TunerConfiguration;
|
||||
import io.github.dsheirer.source.tuner.configuration.TunerConfigurationEditor;
|
||||
import io.github.dsheirer.source.tuner.configuration.TunerConfigurationFactory;
|
||||
@@ -34,6 +41,7 @@ import javax.swing.JOptionPane;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JTable;
|
||||
import javax.swing.JToggleButton;
|
||||
import javax.swing.RowFilter;
|
||||
import javax.swing.ScrollPaneConstants;
|
||||
import javax.swing.event.ListSelectionEvent;
|
||||
@@ -53,6 +61,7 @@ public class TunerEditor extends Editor<Tuner>
|
||||
private JTable mTunerConfigurationTable;
|
||||
private TableRowSorter<TunerConfigurationModel> mRowSorter;
|
||||
private JFrequencyControl mFrequencyControl = new JFrequencyControl();
|
||||
private JToggleButton mRecordButton;
|
||||
private JLabel mSelectedTunerType = new JLabel("No Tuner Selected");
|
||||
private JButton mNewConfigurationButton = new JButton("New");
|
||||
private JButton mDeleteConfigurationButton = new JButton("Delete");
|
||||
@@ -60,10 +69,12 @@ public class TunerEditor extends Editor<Tuner>
|
||||
private JScrollPane mEditorScroller;
|
||||
private Editor<TunerConfiguration> mEditor = new EmptyEditor<>("a tuner");
|
||||
private JideSplitPane mEditorSplitPane = new JideSplitPane();
|
||||
private RecorderManager mRecorderManager;
|
||||
|
||||
public TunerEditor(TunerConfigurationModel tunerConfigurationModel)
|
||||
public TunerEditor(TunerConfigurationModel tunerConfigurationModel, RecorderManager recorderManager)
|
||||
{
|
||||
mTunerConfigurationModel = tunerConfigurationModel;
|
||||
mRecorderManager = recorderManager;
|
||||
init();
|
||||
}
|
||||
|
||||
@@ -88,12 +99,38 @@ public class TunerEditor extends Editor<Tuner>
|
||||
setLayout(new MigLayout("insets 0 0 0 0", "[grow,fill]", "[grow,fill]"));
|
||||
|
||||
JPanel listPanel = new JPanel();
|
||||
listPanel.setLayout(new MigLayout("fill,wrap 3", "[grow,fill]", "[][][][grow,fill][]"));
|
||||
listPanel.setLayout(new MigLayout("fill,wrap 3", "[grow,fill][grow,fill][grow,fill]", "[][][][grow," +
|
||||
"fill][]"));
|
||||
|
||||
listPanel.add(mSelectedTunerType, "span");
|
||||
|
||||
mFrequencyControl.setEnabled(false);
|
||||
listPanel.add(mFrequencyControl, "span");
|
||||
listPanel.add(mFrequencyControl, "span 2");
|
||||
|
||||
mRecordButton = new JToggleButton("Record");
|
||||
mRecordButton.setEnabled(false);
|
||||
mRecordButton.addActionListener(new ActionListener()
|
||||
{
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e)
|
||||
{
|
||||
if(mRecordButton.isSelected())
|
||||
{
|
||||
if(hasItem())
|
||||
{
|
||||
getItem().getTunerController().startRecorder(mRecorderManager);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(hasItem())
|
||||
{
|
||||
getItem().getTunerController().stopRecorder();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
listPanel.add(mRecordButton, "wrap");
|
||||
|
||||
mRowSorter = new TableRowSorter<>(mTunerConfigurationModel);
|
||||
mTunerConfigurationTable = new JTable(mTunerConfigurationModel);
|
||||
@@ -274,6 +311,9 @@ public class TunerEditor extends Editor<Tuner>
|
||||
|
||||
//Set the displayed frequency without adjusting the tuner's frequency
|
||||
mFrequencyControl.setFrequency(tuner.getTunerController().getFrequency(), false);
|
||||
|
||||
mRecordButton.setEnabled(true);
|
||||
mRecordButton.setSelected(getItem().getTunerController().isRecording());
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -283,6 +323,8 @@ public class TunerEditor extends Editor<Tuner>
|
||||
mNewConfigurationButton.setEnabled(false);
|
||||
mRowSorter.setRowFilter(null);
|
||||
mEditor = new EmptyEditor<TunerConfiguration>();
|
||||
mRecordButton.setEnabled(false);
|
||||
mRecordButton.setSelected(false);
|
||||
}
|
||||
|
||||
//Swap out the editor
|
||||
|
||||
@@ -1,21 +1,23 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2019 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.
|
||||
* * ******************************************************************************
|
||||
* * Copyright (C) 2014-2019 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/>
|
||||
* * *****************************************************************************
|
||||
*
|
||||
* 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.source.tuner;
|
||||
|
||||
@@ -29,6 +31,7 @@ import io.github.dsheirer.source.tuner.channel.TunerChannel;
|
||||
import io.github.dsheirer.source.tuner.channel.TunerChannelSource;
|
||||
import io.github.dsheirer.source.tuner.configuration.TunerConfiguration;
|
||||
import io.github.dsheirer.source.tuner.configuration.TunerConfigurationModel;
|
||||
import io.github.dsheirer.source.tuner.recording.RecordingTuner;
|
||||
import io.github.dsheirer.spectrum.SpectralDisplayPanel;
|
||||
import io.github.dsheirer.util.ThreadPool;
|
||||
import org.slf4j.Logger;
|
||||
@@ -56,6 +59,8 @@ public class TunerModel extends AbstractTableModel implements Listener<TunerEven
|
||||
public static final int MEASURED_FREQUENCY_ERROR = 6;
|
||||
public static final int SPECTRAL_DISPLAY_NEW = 7;
|
||||
|
||||
private static final int MAXIMUM_RECORDING_TUNERS_ALLOWED = 1;
|
||||
|
||||
private static final String MHZ = " MHz";
|
||||
private static final String[] COLUMNS = {"Tuner", "ID", "Sample Rate", "Frequency", "Channels", "Current PPM",
|
||||
"Measured Error", "Display"};
|
||||
@@ -177,6 +182,26 @@ public class TunerModel extends AbstractTableModel implements Listener<TunerEven
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if a recording tuner can be added to the set of tuners. This is constrained by the maximum
|
||||
* allowed limit.
|
||||
* @return true if a recording tuner can be added.
|
||||
*/
|
||||
public boolean canAddRecordingTuner()
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
for(Tuner tuner: mTuners)
|
||||
{
|
||||
if(tuner instanceof RecordingTuner)
|
||||
{
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
return count < MAXIMUM_RECORDING_TUNERS_ALLOWED;
|
||||
}
|
||||
|
||||
public void addListener(Listener<TunerEvent> listener)
|
||||
{
|
||||
mTunerEventListeners.add(listener);
|
||||
|
||||
@@ -1,20 +1,24 @@
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* Copyright (C) 2014 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/>
|
||||
******************************************************************************/
|
||||
/*
|
||||
*
|
||||
* * ******************************************************************************
|
||||
* * Copyright (C) 2014-2019 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.source.tuner;
|
||||
|
||||
public enum TunerType
|
||||
@@ -33,6 +37,7 @@ public enum TunerType
|
||||
RAFAELMICRO_R828D( "R828D" ),
|
||||
RTL2832_VARIOUS( "Generic" ),
|
||||
TEST("Test"),
|
||||
RECORDING("Recording"),
|
||||
UNKNOWN( "Unknown" );
|
||||
|
||||
private String mLabel;
|
||||
|
||||
@@ -1,21 +1,23 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2019 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.
|
||||
* * ******************************************************************************
|
||||
* * Copyright (C) 2014-2019 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/>
|
||||
* * *****************************************************************************
|
||||
*
|
||||
* 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.source.tuner;
|
||||
@@ -23,12 +25,15 @@ package io.github.dsheirer.source.tuner;
|
||||
import com.jidesoft.swing.JideSplitPane;
|
||||
import io.github.dsheirer.preference.UserPreferences;
|
||||
import io.github.dsheirer.preference.swing.JTableColumnWidthMonitor;
|
||||
import io.github.dsheirer.record.RecorderManager;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.source.tuner.TunerEvent.Event;
|
||||
import io.github.dsheirer.source.tuner.recording.RecordingTuner;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JScrollPane;
|
||||
@@ -44,6 +49,8 @@ import javax.swing.table.TableRowSorter;
|
||||
import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.util.ArrayList;
|
||||
@@ -63,10 +70,10 @@ public class TunerViewPanel extends JPanel
|
||||
private TunerEditor mTunerEditor;
|
||||
private UserPreferences mUserPreferences;
|
||||
|
||||
public TunerViewPanel(TunerModel tunerModel, UserPreferences userPreferences)
|
||||
public TunerViewPanel(TunerModel tunerModel, UserPreferences userPreferences, RecorderManager recorderManager)
|
||||
{
|
||||
mTunerModel = tunerModel;
|
||||
mTunerEditor = new TunerEditor(mTunerModel.getTunerConfigurationModel());
|
||||
mTunerEditor = new TunerEditor(mTunerModel.getTunerConfigurationModel(), recorderManager);
|
||||
mUserPreferences = userPreferences;
|
||||
|
||||
init();
|
||||
@@ -158,15 +165,36 @@ public class TunerViewPanel extends JPanel
|
||||
mTunerTable.getColumnModel().getColumn(TunerModel.SPECTRAL_DISPLAY_NEW).setCellRenderer(renderer);
|
||||
|
||||
mColumnWidthMonitor = new JTableColumnWidthMonitor(mUserPreferences, mTunerTable, TABLE_PREFERENCE_KEY);
|
||||
JScrollPane listScroller = new JScrollPane(mTunerTable);
|
||||
listScroller.setPreferredSize(new Dimension(400, 20));
|
||||
JScrollPane tunerTableScroller = new JScrollPane(mTunerTable);
|
||||
tunerTableScroller.setPreferredSize(new Dimension(400, 20));
|
||||
|
||||
JPanel tunerTablePanel = new JPanel();
|
||||
tunerTablePanel.setLayout(new MigLayout("insets 0 0 0 0", "[fill,grow][]", "[fill,grow][]"));
|
||||
tunerTablePanel.add(tunerTableScroller, "span");
|
||||
|
||||
tunerTablePanel.add(new JLabel("")); //Empty spacer
|
||||
JButton addRecordingTunerButton = new JButton("Add Recording Tuner");
|
||||
addRecordingTunerButton.setEnabled(mTunerModel.canAddRecordingTuner());
|
||||
addRecordingTunerButton.addActionListener(new ActionListener()
|
||||
{
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e)
|
||||
{
|
||||
RecordingTuner recordingTuner = new RecordingTuner(mUserPreferences);
|
||||
mTunerModel.addTuner(recordingTuner);
|
||||
addRecordingTunerButton.setEnabled(mTunerModel.canAddRecordingTuner());
|
||||
}
|
||||
});
|
||||
tunerTablePanel.add(addRecordingTunerButton);
|
||||
|
||||
tunerTablePanel.add(addRecordingTunerButton);
|
||||
|
||||
JScrollPane editorScroller = new JScrollPane(mTunerEditor);
|
||||
editorScroller.setPreferredSize(new Dimension(400, 80));
|
||||
|
||||
mSplitPane = new JideSplitPane();
|
||||
mSplitPane.setOrientation(JideSplitPane.VERTICAL_SPLIT);
|
||||
mSplitPane.add(listScroller);
|
||||
mSplitPane.add(tunerTablePanel);
|
||||
mSplitPane.add(editorScroller);
|
||||
|
||||
add(mSplitPane);
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
/*******************************************************************************
|
||||
* sdr-trunk
|
||||
* Copyright (C) 2014-2019 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.source.tuner.channel;
|
||||
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.sample.buffer.OverflowableReusableBufferTransferQueue;
|
||||
import io.github.dsheirer.sample.buffer.ReusableComplexBuffer;
|
||||
import io.github.dsheirer.source.ISourceEventListener;
|
||||
import io.github.dsheirer.source.SourceEvent;
|
||||
import io.github.dsheirer.source.tuner.TunerController;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Pass-through channel source that simply passes complex sample buffers from the tuner controller
|
||||
* directly to the registered listener
|
||||
*/
|
||||
public class PassThroughChannelSource extends TunerChannelSource implements ISourceEventListener,
|
||||
Listener<ReusableComplexBuffer>
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger(PassThroughChannelSource.class);
|
||||
private TunerController mTunerController;
|
||||
private OverflowableReusableBufferTransferQueue<ReusableComplexBuffer> mBufferQueue =
|
||||
new OverflowableReusableBufferTransferQueue<>(500, 100);
|
||||
private List<ReusableComplexBuffer> mBuffersToProcess = new ArrayList<>();
|
||||
private Listener<ReusableComplexBuffer> mComplexBufferListener;
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
*
|
||||
* @param listener to receive source events
|
||||
* @param tunerController that provides current sample rate
|
||||
* @param tunerChannel requested for this channel
|
||||
*/
|
||||
public PassThroughChannelSource(Listener<SourceEvent> listener, TunerController tunerController,
|
||||
TunerChannel tunerChannel)
|
||||
{
|
||||
super(listener, tunerChannel);
|
||||
mTunerController = tunerController;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFrequency(long frequency)
|
||||
{
|
||||
mLog.debug("Request to set frequency: " + frequency);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setSampleRate(double sampleRate)
|
||||
{
|
||||
mLog.debug("Request to set sample rate: " + sampleRate);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setChannelFrequencyCorrection(long correction)
|
||||
{
|
||||
mLog.debug("Request to set frequency correction: " + correction);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getChannelFrequencyCorrection()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setListener(Listener<ReusableComplexBuffer> complexBufferListener)
|
||||
{
|
||||
mComplexBufferListener = complexBufferListener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeListener(Listener<ReusableComplexBuffer> listener)
|
||||
{
|
||||
mComplexBufferListener = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void processSamples()
|
||||
{
|
||||
mBufferQueue.drainTo(mBuffersToProcess);
|
||||
|
||||
for(ReusableComplexBuffer buffer: mBuffersToProcess)
|
||||
{
|
||||
if(mComplexBufferListener != null)
|
||||
{
|
||||
mComplexBufferListener.receive(buffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer.decrementUserCount();
|
||||
}
|
||||
}
|
||||
|
||||
mBuffersToProcess.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getSampleRate()
|
||||
{
|
||||
return mTunerController.getSampleRate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive(ReusableComplexBuffer reusableComplexBuffer)
|
||||
{
|
||||
mBufferQueue.offer(reusableComplexBuffer);
|
||||
}
|
||||
}
|
||||
@@ -1,21 +1,23 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2019 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.
|
||||
* * ******************************************************************************
|
||||
* * Copyright (C) 2014-2019 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/>
|
||||
* * *****************************************************************************
|
||||
*
|
||||
* 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.source.tuner.channel;
|
||||
|
||||
@@ -289,49 +291,56 @@ public abstract class TunerChannelSource extends ComplexSource implements ISourc
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
if(!mStopped)
|
||||
try
|
||||
{
|
||||
try
|
||||
if(!mStopped)
|
||||
{
|
||||
getHeartbeatManager().broadcast();
|
||||
try
|
||||
{
|
||||
getHeartbeatManager().broadcast();
|
||||
}
|
||||
catch(Throwable t)
|
||||
{
|
||||
mLog.error("Error while sending heartbeat", t);
|
||||
}
|
||||
}
|
||||
catch(Throwable t)
|
||||
|
||||
if(!mStopped)
|
||||
{
|
||||
mLog.error("Error while sending heartbeat", t);
|
||||
try
|
||||
{
|
||||
processSamples();
|
||||
}
|
||||
catch(Throwable t)
|
||||
{
|
||||
mLog.error("Error while processing samples", t);
|
||||
}
|
||||
}
|
||||
|
||||
if(mStopped)
|
||||
{
|
||||
if(mScheduledFuture != null)
|
||||
{
|
||||
//Set may-interrupt to false so that we can complete this iteration
|
||||
mScheduledFuture.cancel(false);
|
||||
}
|
||||
|
||||
mScheduledFuture = null;
|
||||
|
||||
try
|
||||
{
|
||||
getHeartbeatManager().broadcast();
|
||||
performDisposal();
|
||||
}
|
||||
catch(Throwable t)
|
||||
{
|
||||
mLog.error("Error during final shutdown processing of samples", t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!mStopped)
|
||||
catch(Throwable t)
|
||||
{
|
||||
try
|
||||
{
|
||||
processSamples();
|
||||
}
|
||||
catch(Throwable t)
|
||||
{
|
||||
mLog.error("Error while processing samples", t);
|
||||
}
|
||||
}
|
||||
|
||||
if(mStopped)
|
||||
{
|
||||
if(mScheduledFuture != null)
|
||||
{
|
||||
//Set may-interrupt to false so that we can complete this iteration
|
||||
mScheduledFuture.cancel(false);
|
||||
}
|
||||
|
||||
mScheduledFuture = null;
|
||||
|
||||
try
|
||||
{
|
||||
getHeartbeatManager().broadcast();
|
||||
performDisposal();
|
||||
}
|
||||
catch(Throwable t)
|
||||
{
|
||||
mLog.error("Error during final shutdown processing of samples", t);
|
||||
}
|
||||
mLog.error("Error during heartbeat processing", t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,23 @@
|
||||
/*
|
||||
* *********************************************************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2017 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.
|
||||
* * ******************************************************************************
|
||||
* * Copyright (C) 2014-2019 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/>
|
||||
* * *****************************************************************************
|
||||
*
|
||||
* 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.source.tuner.configuration;
|
||||
|
||||
@@ -26,6 +31,7 @@ import io.github.dsheirer.source.tuner.airspy.AirspyTunerConfiguration;
|
||||
import io.github.dsheirer.source.tuner.fcd.proV1.FCD1TunerConfiguration;
|
||||
import io.github.dsheirer.source.tuner.fcd.proplusV2.FCD2TunerConfiguration;
|
||||
import io.github.dsheirer.source.tuner.hackrf.HackRFTunerConfiguration;
|
||||
import io.github.dsheirer.source.tuner.recording.RecordingTunerConfiguration;
|
||||
import io.github.dsheirer.source.tuner.rtl.e4k.E4KTunerConfiguration;
|
||||
import io.github.dsheirer.source.tuner.rtl.r820t.R820TTunerConfiguration;
|
||||
|
||||
@@ -39,6 +45,7 @@ import io.github.dsheirer.source.tuner.rtl.r820t.R820TTunerConfiguration;
|
||||
@JsonSubTypes.Type(value=FCD1TunerConfiguration.class, name="fcd1TunerConfiguration"),
|
||||
@JsonSubTypes.Type(value=FCD2TunerConfiguration.class, name="fcd2TunerConfiguration"),
|
||||
@JsonSubTypes.Type(value=HackRFTunerConfiguration.class, name="hackRFTunerConfiguration"),
|
||||
@JsonSubTypes.Type(value= RecordingTunerConfiguration.class, name="recordingTunerConfiguration"),
|
||||
@JsonSubTypes.Type(value=R820TTunerConfiguration.class, name="r820TTunerConfiguration"),
|
||||
})
|
||||
@JacksonXmlRootElement( localName = "tuner_configuration" )
|
||||
|
||||
@@ -1,20 +1,24 @@
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* Copyright (C) 2014-2016 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/>
|
||||
******************************************************************************/
|
||||
/*
|
||||
*
|
||||
* * ******************************************************************************
|
||||
* * Copyright (C) 2014-2019 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.source.tuner.configuration;
|
||||
|
||||
@@ -32,6 +36,9 @@ import io.github.dsheirer.source.tuner.fcd.proplusV2.FCD2TunerEditor;
|
||||
import io.github.dsheirer.source.tuner.hackrf.HackRFTuner;
|
||||
import io.github.dsheirer.source.tuner.hackrf.HackRFTunerConfiguration;
|
||||
import io.github.dsheirer.source.tuner.hackrf.HackRFTunerEditor;
|
||||
import io.github.dsheirer.source.tuner.recording.RecordingTuner;
|
||||
import io.github.dsheirer.source.tuner.recording.RecordingTunerConfiguration;
|
||||
import io.github.dsheirer.source.tuner.recording.RecordingTunerConfigurationEditor;
|
||||
import io.github.dsheirer.source.tuner.rtl.RTL2832Tuner;
|
||||
import io.github.dsheirer.source.tuner.rtl.e4k.E4KTunerConfiguration;
|
||||
import io.github.dsheirer.source.tuner.rtl.e4k.E4KTunerEditor;
|
||||
@@ -40,55 +47,59 @@ import io.github.dsheirer.source.tuner.rtl.r820t.R820TTunerEditor;
|
||||
|
||||
public class TunerConfigurationFactory
|
||||
{
|
||||
/**
|
||||
* Creates a tuner configuration for the specified tuner type, unique ID and name
|
||||
*/
|
||||
public static TunerConfiguration getTunerConfiguration(TunerType type, String uniqueID,
|
||||
String name )
|
||||
{
|
||||
switch( type )
|
||||
{
|
||||
case AIRSPY_R820T:
|
||||
return new AirspyTunerConfiguration( uniqueID, name );
|
||||
case ELONICS_E4000:
|
||||
return new E4KTunerConfiguration( uniqueID, name );
|
||||
case FUNCUBE_DONGLE_PRO:
|
||||
return new FCD1TunerConfiguration( uniqueID, name );
|
||||
case FUNCUBE_DONGLE_PRO_PLUS:
|
||||
return new FCD2TunerConfiguration( uniqueID, name );
|
||||
case HACKRF:
|
||||
return new HackRFTunerConfiguration( uniqueID, name );
|
||||
case RAFAELMICRO_R820T:
|
||||
return new R820TTunerConfiguration( uniqueID, name );
|
||||
default:
|
||||
throw new IllegalArgumentException( "Unrecognized tuner type ["
|
||||
+ type.name() + "] - can't create named [" + name + "] tuner"
|
||||
+ " configuration" );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a tuner editor gui for the specified tuner
|
||||
*/
|
||||
public static Editor<TunerConfiguration> getEditor(Tuner tuner, TunerConfigurationModel model )
|
||||
/**
|
||||
* Creates a tuner configuration for the specified tuner type, unique ID and name
|
||||
*/
|
||||
public static TunerConfiguration getTunerConfiguration(TunerType type, String uniqueID,
|
||||
String name)
|
||||
{
|
||||
switch( tuner.getTunerType() )
|
||||
{
|
||||
case AIRSPY_R820T:
|
||||
return new AirspyTunerEditor( model, (AirspyTuner)tuner );
|
||||
case ELONICS_E4000:
|
||||
return new E4KTunerEditor( model, (RTL2832Tuner)tuner );
|
||||
case FUNCUBE_DONGLE_PRO:
|
||||
return new FCD1TunerEditor( model, (FCDTuner)tuner );
|
||||
case FUNCUBE_DONGLE_PRO_PLUS:
|
||||
return new FCD2TunerEditor( model, (FCDTuner)tuner );
|
||||
case HACKRF:
|
||||
return new HackRFTunerEditor( model, (HackRFTuner)tuner );
|
||||
case RAFAELMICRO_R820T:
|
||||
return new R820TTunerEditor( model, (RTL2832Tuner)tuner );
|
||||
case UNKNOWN:
|
||||
default:
|
||||
throw new IllegalArgumentException( "Unrecognized Tuner: " + tuner.getName() );
|
||||
}
|
||||
switch(type)
|
||||
{
|
||||
case AIRSPY_R820T:
|
||||
return new AirspyTunerConfiguration(uniqueID, name);
|
||||
case ELONICS_E4000:
|
||||
return new E4KTunerConfiguration(uniqueID, name);
|
||||
case FUNCUBE_DONGLE_PRO:
|
||||
return new FCD1TunerConfiguration(uniqueID, name);
|
||||
case FUNCUBE_DONGLE_PRO_PLUS:
|
||||
return new FCD2TunerConfiguration(uniqueID, name);
|
||||
case HACKRF:
|
||||
return new HackRFTunerConfiguration(uniqueID, name);
|
||||
case RAFAELMICRO_R820T:
|
||||
return new R820TTunerConfiguration(uniqueID, name);
|
||||
case RECORDING:
|
||||
return new RecordingTunerConfiguration(uniqueID, name);
|
||||
default:
|
||||
throw new IllegalArgumentException("Unrecognized tuner type ["
|
||||
+ type.name() + "] - can't create named [" + name + "] tuner"
|
||||
+ " configuration");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a tuner editor gui for the specified tuner
|
||||
*/
|
||||
public static Editor<TunerConfiguration> getEditor(Tuner tuner, TunerConfigurationModel model)
|
||||
{
|
||||
switch(tuner.getTunerType())
|
||||
{
|
||||
case AIRSPY_R820T:
|
||||
return new AirspyTunerEditor(model, (AirspyTuner)tuner);
|
||||
case ELONICS_E4000:
|
||||
return new E4KTunerEditor(model, (RTL2832Tuner)tuner);
|
||||
case FUNCUBE_DONGLE_PRO:
|
||||
return new FCD1TunerEditor(model, (FCDTuner)tuner);
|
||||
case FUNCUBE_DONGLE_PRO_PLUS:
|
||||
return new FCD2TunerEditor(model, (FCDTuner)tuner);
|
||||
case HACKRF:
|
||||
return new HackRFTunerEditor(model, (HackRFTuner)tuner);
|
||||
case RAFAELMICRO_R820T:
|
||||
return new R820TTunerEditor(model, (RTL2832Tuner)tuner);
|
||||
case RECORDING:
|
||||
return new RecordingTunerConfigurationEditor(model, (RecordingTuner)tuner);
|
||||
case UNKNOWN:
|
||||
default:
|
||||
throw new IllegalArgumentException("Unrecognized Tuner: " + tuner.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
/*******************************************************************************
|
||||
* sdr-trunk
|
||||
* Copyright (C) 2014-2019 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.source.tuner.manager;
|
||||
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.source.SourceEvent;
|
||||
import io.github.dsheirer.source.SourceException;
|
||||
import io.github.dsheirer.source.tuner.TunerController;
|
||||
import io.github.dsheirer.source.tuner.channel.ChannelSpecification;
|
||||
import io.github.dsheirer.source.tuner.channel.PassThroughChannelSource;
|
||||
import io.github.dsheirer.source.tuner.channel.TunerChannel;
|
||||
import io.github.dsheirer.source.tuner.channel.TunerChannelSource;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.SortedSet;
|
||||
import java.util.TreeSet;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class PassThroughSourceManager extends ChannelSourceManager
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger(PassThroughSourceManager.class);
|
||||
private TunerController mTunerController;
|
||||
private SortedSet<TunerChannel> mTunerChannels = new TreeSet<>();
|
||||
|
||||
public PassThroughSourceManager(TunerController tunerController)
|
||||
{
|
||||
mTunerController = tunerController;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SortedSet<TunerChannel> getTunerChannels()
|
||||
{
|
||||
return mTunerChannels;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTunerChannelCount()
|
||||
{
|
||||
return mTunerChannels.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TunerChannelSource getSource(TunerChannel tunerChannel, ChannelSpecification channelSpecification)
|
||||
{
|
||||
PassThroughChannelSource channelSource = new PassThroughChannelSource(new SourceEventProxy(),
|
||||
mTunerController, tunerChannel);
|
||||
|
||||
mLog.debug("Returning a pass through channel");
|
||||
mTunerChannels.add(tunerChannel);
|
||||
|
||||
return channelSource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void process(SourceEvent event) throws SourceException
|
||||
{
|
||||
switch(event.getEvent())
|
||||
{
|
||||
case REQUEST_START_SAMPLE_STREAM:
|
||||
if(event.hasSource() && event.getSource() instanceof PassThroughChannelSource)
|
||||
{
|
||||
mTunerController.addBufferListener((PassThroughChannelSource)event.getSource());
|
||||
}
|
||||
break;
|
||||
case REQUEST_STOP_SAMPLE_STREAM:
|
||||
if(event.hasSource() && event.getSource() instanceof PassThroughChannelSource)
|
||||
{
|
||||
mTunerController.removeBufferListener((PassThroughChannelSource)event.getSource());
|
||||
}
|
||||
break;
|
||||
default:
|
||||
mLog.debug("Got source event: " + event);
|
||||
}
|
||||
}
|
||||
|
||||
public class SourceEventProxy implements Listener<SourceEvent>
|
||||
{
|
||||
@Override
|
||||
public void receive(SourceEvent sourceEvent)
|
||||
{
|
||||
try
|
||||
{
|
||||
process(sourceEvent);
|
||||
}
|
||||
catch(SourceException se)
|
||||
{
|
||||
mLog.error("Error", se);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
*
|
||||
* * ******************************************************************************
|
||||
* * Copyright (C) 2014-2019 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.source.tuner.recording;
|
||||
|
||||
import io.github.dsheirer.preference.UserPreferences;
|
||||
import io.github.dsheirer.preference.source.ChannelizerType;
|
||||
import io.github.dsheirer.source.SourceEvent;
|
||||
import io.github.dsheirer.source.tuner.Tuner;
|
||||
import io.github.dsheirer.source.tuner.TunerClass;
|
||||
import io.github.dsheirer.source.tuner.TunerType;
|
||||
import io.github.dsheirer.source.tuner.manager.ChannelSourceManager;
|
||||
import io.github.dsheirer.source.tuner.manager.HeterodyneChannelSourceManager;
|
||||
import io.github.dsheirer.source.tuner.manager.PassThroughSourceManager;
|
||||
import io.github.dsheirer.source.tuner.manager.PolyphaseChannelSourceManager;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Tuner that replays a recorded I/Q baseband recording
|
||||
*/
|
||||
public class RecordingTuner extends Tuner
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger(RecordingTuner.class);
|
||||
private static int mInstanceCounter = 1;
|
||||
private final int mInstanceID = mInstanceCounter++;
|
||||
private UserPreferences mUserPreferences;
|
||||
|
||||
public RecordingTuner(UserPreferences userPreferences)
|
||||
{
|
||||
super("Recording Tuner", new RecordingTunerController());
|
||||
|
||||
mUserPreferences = userPreferences;
|
||||
|
||||
ChannelSourceManager channelSourceManager = null;
|
||||
|
||||
if(getTunerController().getCurrentSampleRate() < 100000.0d)
|
||||
{
|
||||
setChannelSourceManager(new PassThroughSourceManager(getTunerController()));
|
||||
}
|
||||
else
|
||||
{
|
||||
ChannelizerType channelizerType = userPreferences.getTunerPreference().getChannelizerType();
|
||||
|
||||
mLog.debug("Using Channelizer Type: " + channelizerType);
|
||||
|
||||
if(channelizerType == ChannelizerType.POLYPHASE)
|
||||
{
|
||||
setChannelSourceManager(new PolyphaseChannelSourceManager(getTunerController()));
|
||||
}
|
||||
else if(channelizerType == ChannelizerType.HETERODYNE)
|
||||
{
|
||||
setChannelSourceManager(new HeterodyneChannelSourceManager(getTunerController()));
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new IllegalArgumentException("Unrecognized channelizer type: " + channelizerType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the tuner controller cast as a test tuner controller.
|
||||
*/
|
||||
public RecordingTunerController getTunerController()
|
||||
{
|
||||
return (RecordingTunerController)super.getTunerController();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUniqueID()
|
||||
{
|
||||
return getName() + "-" + mInstanceID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TunerClass getTunerClass()
|
||||
{
|
||||
return TunerClass.RECORDING_TUNER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TunerType getTunerType()
|
||||
{
|
||||
return TunerClass.RECORDING_TUNER.getTunerType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getSampleSize()
|
||||
{
|
||||
return 16.0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void process(SourceEvent event)
|
||||
{
|
||||
super.process(event);
|
||||
|
||||
if(event.getEvent() == SourceEvent.Event.NOTIFICATION_RECORDING_FILE_LOADED)
|
||||
{
|
||||
if(getTunerController().getCurrentSampleRate() < 100000.0d)
|
||||
{
|
||||
setChannelSourceManager(new PassThroughSourceManager(getTunerController()));
|
||||
}
|
||||
else
|
||||
{
|
||||
ChannelizerType channelizerType = mUserPreferences.getTunerPreference().getChannelizerType();
|
||||
|
||||
mLog.debug("Using Channelizer Type: " + channelizerType);
|
||||
|
||||
if(channelizerType == ChannelizerType.POLYPHASE)
|
||||
{
|
||||
setChannelSourceManager(new PolyphaseChannelSourceManager(getTunerController()));
|
||||
}
|
||||
else if(channelizerType == ChannelizerType.HETERODYNE)
|
||||
{
|
||||
setChannelSourceManager(new HeterodyneChannelSourceManager(getTunerController()));
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new IllegalArgumentException("Unrecognized channelizer type: " + channelizerType);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
*
|
||||
* * ******************************************************************************
|
||||
* * Copyright (C) 2014-2019 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.source.tuner.recording;
|
||||
|
||||
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
|
||||
import io.github.dsheirer.source.tuner.TunerType;
|
||||
import io.github.dsheirer.source.tuner.configuration.TunerConfiguration;
|
||||
|
||||
/**
|
||||
* Recording tuner configuration for using baseband I/Q recordings as a tuner source
|
||||
*/
|
||||
public class RecordingTunerConfiguration extends TunerConfiguration
|
||||
{
|
||||
private String mPath;
|
||||
|
||||
public RecordingTunerConfiguration()
|
||||
{
|
||||
//Empty jackson constructor
|
||||
}
|
||||
|
||||
public RecordingTunerConfiguration(String uniqueId, String name)
|
||||
{
|
||||
super(uniqueId, name);
|
||||
}
|
||||
|
||||
@JacksonXmlProperty(isAttribute = true, localName = "type", namespace = "http://www.w3.org/2001/XMLSchema-instance")
|
||||
@Override
|
||||
public TunerType getTunerType()
|
||||
{
|
||||
return TunerType.RECORDING;
|
||||
}
|
||||
|
||||
@JacksonXmlProperty(isAttribute = true, localName = "path")
|
||||
public String getPath()
|
||||
{
|
||||
return mPath;
|
||||
}
|
||||
|
||||
public void setPath(String path)
|
||||
{
|
||||
mPath = path;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,222 @@
|
||||
/*
|
||||
*
|
||||
* * ******************************************************************************
|
||||
* * Copyright (C) 2014-2019 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.source.tuner.recording;
|
||||
|
||||
import io.github.dsheirer.source.SourceException;
|
||||
import io.github.dsheirer.source.tuner.configuration.TunerConfiguration;
|
||||
import io.github.dsheirer.source.tuner.configuration.TunerConfigurationEditor;
|
||||
import io.github.dsheirer.source.tuner.configuration.TunerConfigurationEvent;
|
||||
import io.github.dsheirer.source.tuner.configuration.TunerConfigurationModel;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JFileChooser;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JTextField;
|
||||
import javax.swing.filechooser.FileFilter;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.FocusEvent;
|
||||
import java.awt.event.FocusListener;
|
||||
import java.io.File;
|
||||
|
||||
public class RecordingTunerConfigurationEditor extends TunerConfigurationEditor
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final static Logger mLog = LoggerFactory.getLogger(RecordingTunerConfigurationEditor.class);
|
||||
|
||||
private JTextField mConfigurationName;
|
||||
private JButton mRecordingChooserButton;
|
||||
private JLabel mRecordingPath;
|
||||
private boolean mLoading;
|
||||
|
||||
private RecordingTunerController mController;
|
||||
|
||||
public RecordingTunerConfigurationEditor(TunerConfigurationModel tunerConfigurationModel, RecordingTuner tuner)
|
||||
{
|
||||
super(tunerConfigurationModel);
|
||||
|
||||
mController = tuner.getTunerController();
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTunerLockState(boolean locked)
|
||||
{
|
||||
setControlsEnabled(!locked);
|
||||
}
|
||||
|
||||
private RecordingTunerConfiguration getConfiguration()
|
||||
{
|
||||
if(hasItem())
|
||||
{
|
||||
return (RecordingTunerConfiguration)getItem();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void init()
|
||||
{
|
||||
setLayout(new MigLayout("fill,wrap 4", "[right][grow,fill][right][grow,fill]",
|
||||
"[][][][][][][grow]"));
|
||||
|
||||
add(new JLabel("Recording Tuner Configuration"), "span,align center");
|
||||
|
||||
mConfigurationName = new JTextField();
|
||||
mConfigurationName.setEnabled(false);
|
||||
mConfigurationName.addFocusListener(new FocusListener()
|
||||
{
|
||||
@Override
|
||||
public void focusLost(FocusEvent e)
|
||||
{
|
||||
save();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void focusGained(FocusEvent e)
|
||||
{
|
||||
}
|
||||
});
|
||||
|
||||
add(new JLabel("Name:"));
|
||||
add(mConfigurationName, "span, wrap");
|
||||
|
||||
mRecordingChooserButton = new JButton("Recording");
|
||||
mRecordingChooserButton.setEnabled(false);
|
||||
mRecordingChooserButton.addActionListener(new ActionListener()
|
||||
{
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e)
|
||||
{
|
||||
final JFileChooser chooser = new JFileChooser();
|
||||
chooser.setFileFilter(new FileFilter()
|
||||
{
|
||||
@Override
|
||||
public boolean accept(File f)
|
||||
{
|
||||
return f.isDirectory() || f.getName().matches(".*.wav");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription()
|
||||
{
|
||||
return "Baseband Recordings (*.wav)";
|
||||
}
|
||||
});
|
||||
int returnVal = chooser.showOpenDialog(RecordingTunerConfigurationEditor.this);
|
||||
|
||||
if (returnVal == JFileChooser.APPROVE_OPTION)
|
||||
{
|
||||
File file = chooser.getSelectedFile();
|
||||
|
||||
if(file != null)
|
||||
{
|
||||
mRecordingPath.setText(file.getAbsolutePath());
|
||||
save();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
add(mRecordingChooserButton);
|
||||
|
||||
mRecordingPath = new JLabel();
|
||||
add(mRecordingPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets each of the tuner configuration controls to the enabled argument state
|
||||
*/
|
||||
private void setControlsEnabled(boolean enabled)
|
||||
{
|
||||
if(mConfigurationName.isEnabled() != enabled)
|
||||
{
|
||||
mConfigurationName.setEnabled(enabled);
|
||||
}
|
||||
|
||||
if(mRecordingChooserButton.isEnabled() != enabled)
|
||||
{
|
||||
mRecordingChooserButton.setEnabled(enabled);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItem(TunerConfiguration tunerConfiguration)
|
||||
{
|
||||
super.setItem(tunerConfiguration);
|
||||
|
||||
//Toggle loading so that we don't fire a change event and schedule a settings file save
|
||||
mLoading = true;
|
||||
|
||||
if(hasItem())
|
||||
{
|
||||
RecordingTunerConfiguration config = getConfiguration();
|
||||
|
||||
setControlsEnabled(tunerConfiguration.isAssigned());
|
||||
mConfigurationName.setText(config.getName());
|
||||
mRecordingPath.setText(config.getPath());
|
||||
}
|
||||
else
|
||||
{
|
||||
setControlsEnabled(false);
|
||||
mConfigurationName.setText("");
|
||||
mRecordingPath.setText("");
|
||||
}
|
||||
|
||||
mLoading = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save()
|
||||
{
|
||||
if(hasItem() && !mLoading)
|
||||
{
|
||||
RecordingTunerConfiguration config = getConfiguration();
|
||||
|
||||
config.setName(mConfigurationName.getText());
|
||||
|
||||
String path = mRecordingPath.getText();
|
||||
|
||||
if(path != null && !path.isEmpty())
|
||||
{
|
||||
config.setPath(path);
|
||||
|
||||
mLog.debug("Saving and setting controller with path: " + path);
|
||||
try
|
||||
{
|
||||
mController.apply(config);
|
||||
}
|
||||
catch(SourceException se)
|
||||
{
|
||||
mLog.error("Error while applying recording tuner configuration", se);
|
||||
}
|
||||
}
|
||||
|
||||
getTunerConfigurationModel().broadcast(
|
||||
new TunerConfigurationEvent(getConfiguration(), TunerConfigurationEvent.Event.CHANGE));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,233 @@
|
||||
/*
|
||||
*
|
||||
* * ******************************************************************************
|
||||
* * Copyright (C) 2014-2019 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.source.tuner.recording;
|
||||
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.sample.buffer.ReusableComplexBuffer;
|
||||
import io.github.dsheirer.source.SourceEvent;
|
||||
import io.github.dsheirer.source.SourceException;
|
||||
import io.github.dsheirer.source.tuner.TunerController;
|
||||
import io.github.dsheirer.source.tuner.configuration.TunerConfiguration;
|
||||
import io.github.dsheirer.source.wave.ComplexWaveSource;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.sound.sampled.UnsupportedAudioFileException;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
public class RecordingTunerController extends TunerController
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger(RecordingTunerController.class);
|
||||
|
||||
public static final int DC_NOISE_BANDWIDTH = 0;
|
||||
public static final double USABLE_BANDWIDTH_PERCENTAGE = 1.00;
|
||||
private ComplexWaveSource mComplexWaveSource;
|
||||
private long mCenterFrequency;
|
||||
private boolean mRunning;
|
||||
|
||||
/**
|
||||
* Tuner controller testing implementation.
|
||||
*/
|
||||
public RecordingTunerController()
|
||||
{
|
||||
super(1000000l, 3000000000l, DC_NOISE_BANDWIDTH, USABLE_BANDWIDTH_PERCENTAGE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the recording file and center frequency for this controller
|
||||
* @param recordingPath to play
|
||||
* @param centerFrequency of the recording
|
||||
* @throws IOException if there are any errors
|
||||
*/
|
||||
private void setRecording(String recordingPath, long centerFrequency) throws IOException
|
||||
{
|
||||
if(mComplexWaveSource != null)
|
||||
{
|
||||
mComplexWaveSource.close();
|
||||
mComplexWaveSource = null;
|
||||
}
|
||||
|
||||
if(recordingPath == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
mComplexWaveSource = new ComplexWaveSource(new File(recordingPath), true);
|
||||
mComplexWaveSource.setListener(new Listener<ReusableComplexBuffer>()
|
||||
{
|
||||
@Override
|
||||
public void receive(ReusableComplexBuffer reusableComplexBuffer)
|
||||
{
|
||||
//Send to parent class to broadcast to listeners
|
||||
broadcast(reusableComplexBuffer);
|
||||
}
|
||||
});
|
||||
|
||||
try
|
||||
{
|
||||
mComplexWaveSource.open();
|
||||
mLog.info("Tuner Recording Loaded: " + recordingPath);
|
||||
}
|
||||
catch(UnsupportedAudioFileException e)
|
||||
{
|
||||
mLog.error("Unsupported audio format", e);
|
||||
}
|
||||
|
||||
|
||||
mCenterFrequency = centerFrequency;
|
||||
|
||||
mLog.debug("Set recording center frequency to: " + mCenterFrequency);
|
||||
|
||||
if(mCenterFrequency == 0)
|
||||
{
|
||||
mCenterFrequency = 100000000;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
mFrequencyController.setFrequency(mCenterFrequency);
|
||||
mFrequencyController.setSampleRate((int)mComplexWaveSource.getSampleRate());
|
||||
mFrequencyController.broadcast(SourceEvent.recordingFileLoaded());
|
||||
}
|
||||
catch(SourceException e)
|
||||
{
|
||||
throw new IOException("Can't set frequency or sample rate", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBufferSampleCount()
|
||||
{
|
||||
if(mComplexWaveSource != null)
|
||||
{
|
||||
return mComplexWaveSource.getBufferSampleCount();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void dispose()
|
||||
{
|
||||
//no-op
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBufferListener(Listener<ReusableComplexBuffer> listener)
|
||||
{
|
||||
super.addBufferListener(listener);
|
||||
|
||||
if(mComplexWaveSource != null)
|
||||
{
|
||||
if(!mRunning)
|
||||
{
|
||||
mComplexWaveSource.start();
|
||||
mRunning = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeBufferListener(Listener<ReusableComplexBuffer> listener)
|
||||
{
|
||||
super.removeBufferListener(listener);
|
||||
|
||||
if(!mReusableBufferBroadcaster.hasListeners() && mComplexWaveSource != null)
|
||||
{
|
||||
mComplexWaveSource.setListener((Listener<ReusableComplexBuffer>)null);
|
||||
mComplexWaveSource.stop();
|
||||
mRunning = false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply(TunerConfiguration config) throws SourceException
|
||||
{
|
||||
if(config instanceof RecordingTunerConfiguration)
|
||||
{
|
||||
RecordingTunerConfiguration rtc = (RecordingTunerConfiguration)config;
|
||||
|
||||
mCenterFrequency = rtc.getFrequency();
|
||||
|
||||
try
|
||||
{
|
||||
setRecording(rtc.getPath(), rtc.getFrequency());
|
||||
}
|
||||
catch(IOException ioe)
|
||||
{
|
||||
mLog.debug("Error loading recording tuner baseband recording: " + rtc.getPath(), ioe);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFrequency(long frequency) throws SourceException
|
||||
{
|
||||
// if(hasBufferListeners())
|
||||
// {
|
||||
mLog.debug("Set frequency [" + frequency + "] request ignored");
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// super.setFrequency(frequency);
|
||||
// }
|
||||
}
|
||||
|
||||
/**
|
||||
* Current center frequency for this tuner
|
||||
* @throws SourceException
|
||||
*/
|
||||
@Override
|
||||
public long getTunedFrequency() throws SourceException
|
||||
{
|
||||
return mCenterFrequency;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the center frequency for this tuner
|
||||
* @param frequency in hertz
|
||||
* @throws SourceException
|
||||
*/
|
||||
@Override
|
||||
public void setTunedFrequency(long frequency) throws SourceException
|
||||
{
|
||||
mLog.debug("Set frequency [" + frequency + "] request ignored");
|
||||
// mCenterFrequency = frequency;
|
||||
}
|
||||
|
||||
/**
|
||||
* Current sample rate for this tuner controller
|
||||
*/
|
||||
@Override
|
||||
public double getCurrentSampleRate()
|
||||
{
|
||||
if(mComplexWaveSource != null)
|
||||
{
|
||||
return mComplexWaveSource.getSampleRate();
|
||||
}
|
||||
|
||||
return 0d;
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,24 @@
|
||||
/*******************************************************************************
|
||||
* 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.
|
||||
* * ******************************************************************************
|
||||
* * Copyright (C) 2014-2019 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/>
|
||||
* * *****************************************************************************
|
||||
*
|
||||
* 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.source.wave;
|
||||
|
||||
import io.github.dsheirer.sample.ConversionUtils;
|
||||
@@ -23,6 +29,7 @@ import io.github.dsheirer.source.ComplexSource;
|
||||
import io.github.dsheirer.source.IControllableFileSource;
|
||||
import io.github.dsheirer.source.IFrameLocationListener;
|
||||
import io.github.dsheirer.source.SourceEvent;
|
||||
import io.github.dsheirer.util.ThreadPool;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -33,6 +40,8 @@ import javax.sound.sampled.UnsupportedAudioFileException;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class ComplexWaveSource extends ComplexSource implements IControllableFileSource, AutoCloseable
|
||||
{
|
||||
@@ -46,10 +55,28 @@ public class ComplexWaveSource extends ComplexSource implements IControllableFil
|
||||
private AudioInputStream mInputStream;
|
||||
private File mFile;
|
||||
private ReusableComplexBufferQueue mReusableComplexBufferQueue = new ReusableComplexBufferQueue("ComplexWaveSource");
|
||||
private boolean mAutoReplay;
|
||||
private ScheduledFuture<?> mReplayController;
|
||||
|
||||
/**
|
||||
* Constructs an instance with optional auto-replay at near real time.
|
||||
* @param file containing complex I/Q sample data
|
||||
* @param autoReplay to enable continuous looping, real-time playback of sample data
|
||||
*/
|
||||
public ComplexWaveSource(File file, boolean autoReplay) throws IOException
|
||||
{
|
||||
if(file == null || !file.exists() || !supports(file))
|
||||
{
|
||||
throw new IOException("Empty or Unsupported file format");
|
||||
}
|
||||
|
||||
mFile = file;
|
||||
mAutoReplay = autoReplay;
|
||||
}
|
||||
|
||||
public ComplexWaveSource(File file) throws IOException
|
||||
{
|
||||
mFile = file;
|
||||
this(file, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -75,19 +102,41 @@ public class ComplexWaveSource extends ComplexSource implements IControllableFil
|
||||
public void reset()
|
||||
{
|
||||
stop();
|
||||
mFrameCounter = 0;
|
||||
start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Number of samples per buffer
|
||||
*/
|
||||
public int getBufferSampleCount()
|
||||
{
|
||||
return (int)(getSampleRate() / 20.0d);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start()
|
||||
{
|
||||
try
|
||||
if(mInputStream == null)
|
||||
{
|
||||
open();
|
||||
try
|
||||
{
|
||||
open();
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
mLog.error("Error", e);
|
||||
}
|
||||
}
|
||||
catch(IOException | UnsupportedAudioFileException e)
|
||||
|
||||
if(mAutoReplay)
|
||||
{
|
||||
mLog.error("Error starting complex wave source");
|
||||
long intervalMilliseconds = 50; //20 intervals per second
|
||||
double framesPerInterval = getSampleRate() / 20.0d;
|
||||
mReplayController = ThreadPool.SCHEDULED.scheduleAtFixedRate(new ReplayController(framesPerInterval),
|
||||
0, intervalMilliseconds, TimeUnit.MILLISECONDS);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,6 +145,11 @@ public class ComplexWaveSource extends ComplexSource implements IControllableFil
|
||||
{
|
||||
try
|
||||
{
|
||||
if(mReplayController != null)
|
||||
{
|
||||
mReplayController.cancel(true);
|
||||
}
|
||||
|
||||
close();
|
||||
}
|
||||
catch(IOException e)
|
||||
@@ -159,27 +213,22 @@ public class ComplexWaveSource extends ComplexSource implements IControllableFil
|
||||
if(mInputStream == null)
|
||||
{
|
||||
mInputStream = AudioSystem.getAudioInputStream(mFile);
|
||||
|
||||
AudioFormat format = mInputStream.getFormat();
|
||||
|
||||
mBytesPerFrame = format.getFrameSize();
|
||||
|
||||
if(format.getChannels() != 2 || format.getSampleSizeInBits() != 16)
|
||||
{
|
||||
throw new IOException("Unsupported Wave Format - EXPECTED: 2 " +
|
||||
"channels 16-bit samples FOUND: " +
|
||||
mInputStream.getFormat().getChannels() + " channels " +
|
||||
mInputStream.getFormat().getSampleSizeInBits() + "-bit samples");
|
||||
}
|
||||
|
||||
/* Broadcast that we're at frame location 0 */
|
||||
broadcast(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new IOException("Can't open wave source - is already opened");
|
||||
}
|
||||
|
||||
|
||||
AudioFormat format = mInputStream.getFormat();
|
||||
|
||||
mBytesPerFrame = format.getFrameSize();
|
||||
|
||||
if(format.getChannels() != 2 || format.getSampleSizeInBits() != 16)
|
||||
{
|
||||
throw new IOException("Unsupported Wave Format - EXPECTED: 2 " +
|
||||
"channels 16-bit samples FOUND: " +
|
||||
mInputStream.getFormat().getChannels() + " channels " +
|
||||
mInputStream.getFormat().getSampleSizeInBits() + "-bit samples");
|
||||
}
|
||||
|
||||
/* Broadcast that we're at frame location 0 */
|
||||
broadcast(0);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -192,7 +241,7 @@ public class ComplexWaveSource extends ComplexSource implements IControllableFil
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the number of frames and optionally sends the buffer to the listener
|
||||
* Reads the number of frames and optionally sends the buffer(s) to the listener
|
||||
*/
|
||||
public void next(int frames, boolean broadcast) throws IOException
|
||||
{
|
||||
@@ -302,4 +351,34 @@ public class ComplexWaveSource extends ComplexSource implements IControllableFil
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public class ReplayController implements Runnable
|
||||
{
|
||||
private double mFramesPerInterval;
|
||||
private int mFramesRead;
|
||||
private int mIntervals;
|
||||
|
||||
public ReplayController(double framesPerInterval)
|
||||
{
|
||||
mFramesPerInterval = framesPerInterval;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
mIntervals++;
|
||||
int framesToRead = (int)Math.floor((mIntervals * mFramesPerInterval) - mFramesRead);
|
||||
|
||||
try
|
||||
{
|
||||
next(framesToRead, true);
|
||||
mFramesRead += framesToRead;
|
||||
}
|
||||
catch(IOException ioe)
|
||||
{
|
||||
mLog.debug("End of Recording - looping [" + ioe.getLocalizedMessage() + "]");
|
||||
reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user