#296 APCO25 Phase II Decoder Implementation (#589)

P25 Phase 2 Decoder implementation.
This commit is contained in:
Denny Sheirer
2019-11-17 06:56:52 -05:00
committed by GitHub
parent d5b802e794
commit f934676bd0
561 changed files with 35609 additions and 6215 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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" )

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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