#39 WIP - initial efforts to restructure the way tuners and tuner

controllers manager sample buffer processing and channel management.
General cleanup across all modules to remove the executor from each
of the start() methods.  Removed the B100 tuner class since it's not
yet implemented.
This commit is contained in:
Denny
2017-12-03 11:10:50 -05:00
parent bf65e10286
commit 57fab2b7ca
88 changed files with 2112 additions and 2547 deletions

View File

@@ -117,7 +117,7 @@ public class AudioModule extends Module implements IAudioPacketProvider, IFilter
}
@Override
public void start(ScheduledExecutorService executor)
public void start()
{
/* No start operations provided */
}

View File

@@ -47,11 +47,6 @@ public class AlwaysUnsquelchedDecoderState extends DecoderState
mChannelName = channelName;
}
@Override
public void init()
{
}
@Override
public void reset()
{
@@ -94,14 +89,9 @@ public class AlwaysUnsquelchedDecoderState extends DecoderState
}
@Override
public void start(ScheduledExecutorService executor)
public void start()
{
broadcast(new AttributeChangeRequest<String>(Attribute.PRIMARY_ADDRESS_TO, mChannelName));
broadcast(new DecoderStateEvent(this, Event.ALWAYS_UNSQUELCH, State.IDLE));
}
@Override
public void stop()
{
}
}

View File

@@ -20,8 +20,8 @@ package channel.state;
import audio.squelch.ISquelchStateProvider;
import audio.squelch.SquelchState;
import channel.heartbeat.Heartbeat;
import channel.heartbeat.IHeartbeatListener;
import source.heartbeat.Heartbeat;
import source.heartbeat.IHeartbeatListener;
import channel.metadata.Attribute;
import channel.metadata.AttributeChangeRequest;
import channel.metadata.IAttributeChangeRequestListener;
@@ -124,7 +124,7 @@ public class ChannelState extends Module implements ICallEventProvider, IDecoder
}
@Override
public void start(ScheduledExecutorService executor)
public void start()
{
mMutableMetadata.receive(new AttributeChangeRequest<State>(Attribute.CHANNEL_STATE, mState));

View File

@@ -71,7 +71,20 @@ public abstract class DecoderState extends Module implements ActivitySummaryProv
/**
* Allow the decoder to perform any setup actions
*/
public abstract void init();
public void init()
{
}
@Override
public void start()
{
}
@Override
public void stop()
{
}
/**
* Implements the IDecoderStateEventListener interface to receive state

View File

@@ -36,7 +36,7 @@ import org.slf4j.LoggerFactory;
import record.config.RecordConfiguration;
import sample.Listener;
import source.config.SourceConfigTuner;
import source.tuner.TunerChannel;
import source.tuner.channel.TunerChannel;
import java.util.ArrayList;
import java.util.List;
@@ -330,7 +330,7 @@ public class TrafficChannelManager extends Module implements ICallEventProvider,
}
@Override
public void start(ScheduledExecutorService executor)
public void start()
{
}

View File

@@ -33,7 +33,7 @@ import source.config.SourceConfigFactory;
import source.config.SourceConfigRecording;
import source.config.SourceConfigTuner;
import source.config.SourceConfiguration;
import source.tuner.TunerChannel;
import source.tuner.channel.TunerChannel;
import source.SourceEvent;
import source.SourceEvent.Event;

View File

@@ -20,7 +20,7 @@ package dsp.filter.channelizer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import source.tuner.TunerChannel;
import source.tuner.channel.TunerChannel;
import java.text.DecimalFormat;
import java.util.ArrayList;

View File

@@ -28,7 +28,8 @@ import settings.SettingsManager;
import source.ISourceEventProcessor;
import source.SourceEvent;
import source.SourceException;
import source.tuner.TunerChannel;
import source.tuner.channel.TunerChannel;
import source.tuner.channel.TunerChannelSource;
import source.tuner.configuration.TunerConfigurationModel;
import source.tuner.test.TestTuner;
import spectrum.DFTProcessor;
@@ -70,12 +71,12 @@ public class ChannelizerViewer extends JFrame
{
mTestTuner = new TestTuner();
mChannelCount = mTestTuner.getSampleRate() / CHANNEL_BANDWIDTH;
mChannelCount = mTestTuner.getTunerController().getSampleRate() / CHANNEL_BANDWIDTH;
mChannelsPerRow = channelsPerRow;
mToneFrequency = mTestTuner.getTunerController().getFrequency() + 19200;
mTestTuner.setToneFrequency(mToneFrequency);
mTestTuner.getTunerController().setToneFrequency(mToneFrequency);
init();
}
@@ -108,10 +109,11 @@ public class ChannelizerViewer extends JFrame
{
if(mPrimarySpectrumPanel == null)
{
mPrimarySpectrumPanel = new PrimarySpectrumPanel(mSettingsManager, mTestTuner.getSampleRate());
mPrimarySpectrumPanel = new PrimarySpectrumPanel(mSettingsManager,
mTestTuner.getTunerController().getSampleRate());
mPrimarySpectrumPanel.setPreferredSize(new Dimension(1000, 200));
mPrimarySpectrumPanel.setDFTSize(mMainPanelDFTSize);
mTestTuner.addListener(mPrimarySpectrumPanel);
mTestTuner.getSourceManager().addComplexBufferListener(mPrimarySpectrumPanel);
}
return mPrimarySpectrumPanel;
@@ -125,7 +127,7 @@ public class ChannelizerViewer extends JFrame
mControlPanel.setLayout(new MigLayout("insets 0 0 0 0", "", ""));
mControlPanel.add(new JLabel("Tone:"), "align left");
int maximumFrequencyOffset = mTestTuner.getSampleRate() / 2;
int maximumFrequencyOffset = mTestTuner.getTunerController().getSampleRate() / 2;
long minimumFrequency = mTestTuner.getTunerController().getFrequency() - maximumFrequencyOffset;
long maximumFrequency = mTestTuner.getTunerController().getFrequency() + maximumFrequencyOffset;
@@ -138,7 +140,7 @@ public class ChannelizerViewer extends JFrame
public void stateChanged(ChangeEvent e)
{
int toneFrequency = model.getNumber().intValue();
mTestTuner.setToneFrequency(toneFrequency);
mTestTuner.getTunerController().setToneFrequency(toneFrequency);
}
});
@@ -147,9 +149,9 @@ public class ChannelizerViewer extends JFrame
mControlPanel.add(spinner);
mControlPanel.add(new JLabel("Hz"));
mControlPanel.add(new JLabel("Sample Rate: " + mTestTuner.getSampleRate() + " Hz"), "push,align right");
mControlPanel.add(new JLabel("Sample Rate: " + mTestTuner.getTunerController().getSampleRate() + " Hz"), "push,align right");
mControlPanel.add(new JLabel("Max: " + (mTestTuner.getSampleRate() / 2) + " Hz"), "push,align right");
mControlPanel.add(new JLabel("Max: " + (mTestTuner.getTunerController().getSampleRate() / 2) + " Hz"), "push,align right");
mControlPanel.add(new JLabel("Channels: " + mChannelCount), "push,align right");
}
@@ -183,7 +185,7 @@ public class ChannelizerViewer extends JFrame
{
setLayout(new MigLayout("insets 0 0 0 0", "fill", "fill"));
long spectralBandwidth = mTestTuner.getSampleRate();
long spectralBandwidth = mTestTuner.getTunerController().getSampleRate();
long halfSpectralBandwidth = spectralBandwidth / 2;
int channelToLog = -1;
@@ -248,7 +250,7 @@ public class ChannelizerViewer extends JFrame
public class ChannelPanel extends JPanel implements Listener<ComplexBuffer>, ISourceEventProcessor
{
private PolyphaseChannelSource mSource;
private TunerChannelSource mSource;
private DFTProcessor mDFTProcessor = new DFTProcessor(SampleType.COMPLEX);
private ComplexDecibelConverter mComplexDecibelConverter = new ComplexDecibelConverter();
private SpectrumPanel mSpectrumPanel;
@@ -268,7 +270,7 @@ public class ChannelizerViewer extends JFrame
mFrequency = frequency;
TunerChannel tunerChannel = new TunerChannel(frequency, bandwidth);
mSource = (PolyphaseChannelSource)mTestTuner.getChannelSource(tunerChannel);
mSource = mTestTuner.getSourceManager().getSource(tunerChannel);
if(mSource != null)
{
@@ -288,7 +290,8 @@ public class ChannelizerViewer extends JFrame
{
mSource.setListener(mDFTProcessor);
}
mSource.start(ThreadPool.SCHEDULED);
mSource.start();
}
else
{
@@ -296,7 +299,7 @@ public class ChannelizerViewer extends JFrame
}
}
public PolyphaseChannelSource getSource()
public TunerChannelSource getSource()
{
return mSource;
}

View File

@@ -25,11 +25,11 @@ import dsp.filter.design.FilterDesignException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sample.Listener;
import sample.complex.IComplexBufferProvider;
import source.Source;
import source.SourceEvent;
import source.SourceException;
import source.tuner.Tuner;
import source.tuner.TunerChannel;
import source.tuner.channel.TunerChannel;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
@@ -42,7 +42,7 @@ public class PolyphaseChannelManager implements Listener<SourceEvent>
private static final int CHANNEL_SAMPLE_RATE = (int)(CHANNEL_BANDWIDTH * CHANNEL_OVERSAMPLING);
private static final int POLYPHASE_FILTER_TAPS_PER_CHANNEL = 17;
private Tuner mTuner;
private IComplexBufferProvider mComplexBufferProvider;
private List<PolyphaseChannelSource> mChannelSources = new CopyOnWriteArrayList<>();
private ChannelCalculator mChannelCalculator;
private ComplexPolyphaseChannelizerM2 mPolyphaseChannelizer;
@@ -57,27 +57,29 @@ public class PolyphaseChannelManager implements Listener<SourceEvent>
* active DDC polyphase channel sources accordingly. This class also monitors source event requests and
* notifications received from active DDC polyphase channel sources to adjust sample streams as required.
*
* @param tuner providing complex sample buffers
* Note: add this channel manager as a source event listener to the complex buffer provider to ensure this manager
* adapts to changes in source frequency and sample rate.
*
* @param complexBufferProvider that supports register/deregister of this channel manager
* @param frequency of the provided complex buffer samples
* @param sampleRate of the provided complex buffer samples
*/
public PolyphaseChannelManager(Tuner tuner)
public PolyphaseChannelManager(IComplexBufferProvider complexBufferProvider, long frequency, int sampleRate)
{
if(tuner == null)
if(complexBufferProvider == null)
{
throw new IllegalArgumentException("Tuner argument cannot be null");
throw new IllegalArgumentException("Complex buffer provider argument cannot be null");
}
mTuner = tuner;
mComplexBufferProvider = complexBufferProvider;
int sampleRate = mTuner.getTunerController().getSampleRate();
//Ensure that tuner sample rate is a multiple of channel sample rate since polyphase channelizer is M2
//Ensure that sample rate is a multiple of channel sample rate since polyphase channelizer is M2
if(sampleRate % (CHANNEL_SAMPLE_RATE) != 0)
{
throw new IllegalArgumentException("Tuner sample rate [" + sampleRate + "] must be an even multiple of " +
CHANNEL_SAMPLE_RATE + " Hz");
}
long frequency = mTuner.getTunerController().getFrequency();
int channelCount = sampleRate / CHANNEL_BANDWIDTH;
mChannelCalculator = new ChannelCalculator(sampleRate, channelCount, frequency, CHANNEL_OVERSAMPLING);
@@ -85,9 +87,6 @@ public class PolyphaseChannelManager implements Listener<SourceEvent>
mBufferProcessor = new ScheduledBufferProcessor(500, 100, 50, 50);
updateChannelizer(sampleRate, null);
mTuner.addSourceEventListener(this);
}
/**
@@ -159,7 +158,7 @@ public class PolyphaseChannelManager implements Listener<SourceEvent>
if(mPolyphaseChannelizer.getRegisteredChannelCount() == 1)
{
mTuner.addListener(mBufferProcessor);
mComplexBufferProvider.addComplexBufferListener(mBufferProcessor);
mBufferProcessor.start();
}
}
@@ -179,7 +178,7 @@ public class PolyphaseChannelManager implements Listener<SourceEvent>
if(mPolyphaseChannelizer.getRegisteredChannelCount() == 0)
{
mTuner.removeListener(mBufferProcessor);
mComplexBufferProvider.removeComplexBufferListener(mBufferProcessor);
mBufferProcessor.stop();
}
}

View File

@@ -18,7 +18,7 @@
******************************************************************************/
package dsp.filter.channelizer;
import channel.heartbeat.Heartbeat;
import source.heartbeat.Heartbeat;
import dsp.filter.channelizer.output.IPolyphaseChannelOutputProcessor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -27,12 +27,10 @@ import sample.complex.ComplexBuffer;
import sample.complex.ComplexToComplexBufferAssembler;
import source.ComplexSource;
import source.ISourceEventProcessor;
import source.Source;
import source.SourceEvent;
import source.SourceException;
import source.tuner.TunerChannel;
import source.tuner.channel.TunerChannel;
import java.util.Arrays;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

View File

@@ -59,9 +59,9 @@ public abstract class Module
public abstract void reset();
/**
* Start processing. Provides an executor to use for scheduling tasks
* Start processing.
*/
public abstract void start( ScheduledExecutorService executor );
public abstract void start();
/**
* Stop processing

View File

@@ -24,9 +24,9 @@ import audio.IAudioPacketProvider;
import audio.squelch.ISquelchStateListener;
import audio.squelch.ISquelchStateProvider;
import audio.squelch.SquelchState;
import channel.heartbeat.Heartbeat;
import channel.heartbeat.IHeartbeatListener;
import channel.heartbeat.IHeartbeatProvider;
import source.heartbeat.Heartbeat;
import source.heartbeat.IHeartbeatListener;
import source.heartbeat.IHeartbeatProvider;
import channel.metadata.AttributeChangeRequest;
import channel.metadata.IAttributeChangeRequestListener;
import channel.metadata.IAttributeChangeRequestProvider;
@@ -454,7 +454,7 @@ public class ProcessingChain implements IChannelEventListener
if(module instanceof IHeartbeatProvider)
{
((IHeartbeatProvider)module).setHeartbeatListener(mHeartbeatBroadcaster);
((IHeartbeatProvider)module).getHeartbeatManager().addHeartbeatListener(mHeartbeatBroadcaster);
}
if(module instanceof IMessageProvider)
@@ -521,7 +521,7 @@ public class ProcessingChain implements IChannelEventListener
if(module instanceof IHeartbeatProvider)
{
((IHeartbeatProvider)module).removeHeartbeatListener();
((IHeartbeatProvider)module).getHeartbeatManager().removeHeartbeatListener(mHeartbeatBroadcaster);
}
if(module instanceof IMessageProvider)
@@ -595,7 +595,7 @@ public class ProcessingChain implements IChannelEventListener
{
try
{
module.start(ThreadPool.SCHEDULED);
module.start();
}
catch(Exception e)
{

View File

@@ -1,19 +1,20 @@
/*******************************************************************************
* 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/>
* 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.
*
* 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 module.decode;
@@ -22,8 +23,7 @@ import message.Message;
import module.Module;
import sample.Listener;
public abstract class Decoder extends Module
implements IMessageProvider, Listener<Message>
public abstract class Decoder extends Module implements IMessageProvider, Listener<Message>
{
/* This has to be a broadcaster in order for references to persist */
protected Listener<Message> mMessageListener;
@@ -82,4 +82,19 @@ public abstract class Decoder extends Module
{
broadcast( message );
}
@Override
public void reset()
{
}
@Override
public void start()
{
}
@Override
public void stop()
{
}
}

View File

@@ -174,19 +174,4 @@ public class Fleetsync2Decoder extends Decoder implements IFilteredRealBufferLis
{
return mDecimationFilter;
}
@Override
public void reset()
{
}
@Override
public void start( ScheduledExecutorService executor )
{
}
@Override
public void stop()
{
}
}

View File

@@ -63,27 +63,12 @@ public class Fleetsync2DecoderState extends DecoderState
return DecoderType.FLEETSYNC2;
}
@Override
public void start(ScheduledExecutorService executor)
{
}
@Override
public void stop()
{
}
@Override
public void dispose()
{
super.dispose();
}
@Override
public void init()
{
}
/**
* Resets the overall decoder state and clears any accumulated event details
*/

View File

@@ -192,19 +192,4 @@ public class LJ1200Decoder extends Decoder implements IFilteredRealBufferListene
{
return mDecimationFilter;
}
@Override
public void reset()
{
}
@Override
public void start( ScheduledExecutorService executor )
{
}
@Override
public void stop()
{
}
}

View File

@@ -63,16 +63,6 @@ public class LJ1200DecoderState extends DecoderState
return DecoderType.LJ_1200;
}
@Override
public void start(ScheduledExecutorService executor)
{
}
@Override
public void stop()
{
}
@Override
public void receive(Message message)
{
@@ -125,12 +115,6 @@ public class LJ1200DecoderState extends DecoderState
return sb.toString();
}
@Override
public void init()
{
/* No initialization steps */
}
@Override
public void receiveDecoderStateEvent(DecoderStateEvent event)
{

View File

@@ -120,14 +120,4 @@ public class LTRNetDecoder extends Decoder
{
mLTRMessageFramer.reset();
}
@Override
public void start( ScheduledExecutorService executor )
{
}
@Override
public void stop()
{
}
}

View File

@@ -765,12 +765,6 @@ public class LTRNetDecoderState extends DecoderState
}
}
@Override
public void init()
{
/* No initialization steps required */
}
@Override
public void receiveDecoderStateEvent(DecoderStateEvent event)
{
@@ -786,14 +780,4 @@ public class LTRNetDecoderState extends DecoderState
break;
}
}
@Override
public void start(ScheduledExecutorService executor)
{
}
@Override
public void stop()
{
}
}

View File

@@ -84,14 +84,4 @@ public class LTRStandardDecoder extends Decoder implements IUnFilteredRealBuffer
{
mLTRMessageFramer.reset();
}
@Override
public void start( ScheduledExecutorService executor )
{
}
@Override
public void stop()
{
}
}

View File

@@ -76,18 +76,6 @@ public class LTRStandardDecoderState extends DecoderState
return DecoderType.LTR_STANDARD;
}
@Override
public void start(ScheduledExecutorService executor)
{
// TODO Auto-generated method stub
}
@Override
public void stop()
{
}
@Override
public void receive(Message message)
{
@@ -257,11 +245,6 @@ public class LTRStandardDecoderState extends DecoderState
}
}
@Override
public void init()
{
}
@Override
public void receiveDecoderStateEvent(DecoderStateEvent event)
{

View File

@@ -202,25 +202,4 @@ public class MDCDecoder extends Decoder implements IFilteredRealBufferListener,
{
return mDecimationFilter;
}
@Override
public void reset()
{
// TODO Auto-generated method stub
}
@Override
public void start( ScheduledExecutorService executor )
{
// TODO Auto-generated method stub
}
@Override
public void stop()
{
// TODO Auto-generated method stub
}
}

View File

@@ -59,16 +59,6 @@ public class MDCDecoderState extends DecoderState
return DecoderType.MDC1200;
}
@Override
public void start(ScheduledExecutorService executor)
{
}
@Override
public void stop()
{
}
@Override
public void reset()
{
@@ -245,12 +235,6 @@ public class MDCDecoderState extends DecoderState
return sb.toString();
}
@Override
public void init()
{
/* No initialization required */
}
/**
* Responds to reset events issued by the channel state
*/

View File

@@ -297,16 +297,4 @@ public class MPT1327Decoder extends Decoder implements IFilteredRealBufferListen
mControlMessageFramer.reset();
mTrafficMessageFramer.reset();
}
@Override
public void start(ScheduledExecutorService executor)
{
// TODO Auto-generated method stub
}
@Override
public void stop()
{
// TODO Auto-generated method stub
}
}

View File

@@ -87,16 +87,6 @@ public class MPT1327DecoderState extends DecoderState
return DecoderType.MPT1327;
}
@Override
public void start(ScheduledExecutorService executor)
{
}
@Override
public void stop()
{
}
public ChannelType getChannelType()
{
return mChannelType;
@@ -501,12 +491,6 @@ public class MPT1327DecoderState extends DecoderState
return sb.toString();
}
@Override
public void init()
{
/* No initialization required */
}
@Override
public void receiveDecoderStateEvent(DecoderStateEvent event)
{

View File

@@ -221,11 +221,6 @@ public class P25DecoderState extends DecoderState
return DecoderType.P25_PHASE1;
}
@Override
public void stop()
{
}
/**
* Performs a full reset to prepare this object for reuse on a new channel
*/
@@ -257,14 +252,6 @@ public class P25DecoderState extends DecoderState
mCurrentCallEvent = null;
}
/**
* Performs any initialization operations to prepare for use
*/
@Override
public void init()
{
}
/**
* Indicates if there a call event exists for the specified channel and
* from and to talkgroup identifiers.
@@ -3624,7 +3611,7 @@ public class P25DecoderState extends DecoderState
}
@Override
public void start(ScheduledExecutorService executor)
public void start()
{
//Change the default (45-second) traffic channel timeout to 1 second
if(mChannelType == ChannelType.TRAFFIC)

View File

@@ -194,27 +194,6 @@ public class P25_C4FMDecoder extends P25Decoder implements IFilteredRealBufferLi
}
}
@Override
public void reset()
{
// TODO Auto-generated method stub
}
@Override
public void start( ScheduledExecutorService executor )
{
// TODO Auto-generated method stub
}
@Override
public void stop()
{
// TODO Auto-generated method stub
}
@Override
public void setSourceEventListener(Listener<SourceEvent> listener )
{

View File

@@ -237,25 +237,4 @@ public class P25_LSMDecoder extends P25Decoder implements IComplexBufferListener
tap.getName() );
}
}
@Override
public void reset()
{
// TODO Auto-generated method stub
}
@Override
public void start( ScheduledExecutorService executor )
{
// TODO Auto-generated method stub
}
@Override
public void stop()
{
// TODO Auto-generated method stub
}
}

View File

@@ -71,7 +71,7 @@ public class P25AudioModule extends Module implements Listener<Message>, IAudioP
}
@Override
public void start(ScheduledExecutorService executor)
public void start()
{
}

View File

@@ -109,14 +109,4 @@ public class PassportDecoder extends Decoder
{
mPassportMessageFramer.reset();
}
@Override
public void start( ScheduledExecutorService executor )
{
}
@Override
public void stop()
{
}
}

View File

@@ -78,21 +78,6 @@ public class PassportDecoderState extends DecoderState
return DecoderType.PASSPORT;
}
@Override
public void start(ScheduledExecutorService executor)
{
}
@Override
public void init()
{
}
@Override
public void stop()
{
}
private void logTalkgroup(String talkgroup)
{
if(mTalkgroupsFirstHeard.contains(talkgroup))

View File

@@ -190,25 +190,4 @@ public class Tait1200Decoder extends Decoder implements IFilteredRealBufferListe
{
return DecoderType.TAIT_1200;
}
@Override
public void reset()
{
// TODO Auto-generated method stub
}
@Override
public void start( ScheduledExecutorService executor )
{
// TODO Auto-generated method stub
}
@Override
public void stop()
{
// TODO Auto-generated method stub
}
}

View File

@@ -65,16 +65,6 @@ public class Tait1200DecoderState extends DecoderState
return DecoderType.TAIT_1200;
}
@Override
public void start(ScheduledExecutorService executor)
{
}
@Override
public void stop()
{
}
@Override
public void reset()
{
@@ -176,12 +166,6 @@ public class Tait1200DecoderState extends DecoderState
return sb.toString();
}
@Override
public void init()
{
/* No initialization required */
}
/**
* Responds to reset events issued by the channel state
*/

View File

@@ -1,124 +1,110 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014,2015 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 module.demodulate.am;
import java.util.concurrent.ScheduledExecutorService;
import module.Module;
import sample.Listener;
import sample.complex.ComplexBuffer;
import sample.complex.IComplexBufferListener;
import sample.real.IFilteredRealBufferProvider;
import sample.real.RealBuffer;
import dsp.am.AMDemodulator_CB;
import dsp.filter.FilterFactory;
import dsp.filter.Window.WindowType;
import dsp.filter.fir.complex.ComplexFIRFilter_CB_CB;
import dsp.filter.fir.real.RealFIRFilter_RB_RB;
import dsp.gain.AutomaticGainControl_RB;
import module.Module;
import sample.Listener;
import sample.complex.ComplexBuffer;
import sample.complex.IComplexBufferListener;
import sample.real.IFilteredRealBufferProvider;
import sample.real.RealBuffer;
public class AMDemodulatorModule extends Module
implements IComplexBufferListener, IFilteredRealBufferProvider
public class AMDemodulatorModule extends Module
implements IComplexBufferListener, IFilteredRealBufferProvider
{
private static final int SAMPLE_RATE = 48000;
private ComplexFIRFilter_CB_CB mIQFilter;
private AMDemodulator_CB mDemodulator;
private RealFIRFilter_RB_RB mLowPassFilter;
private static final int SAMPLE_RATE = 48000;
private ComplexFIRFilter_CB_CB mIQFilter;
private AMDemodulator_CB mDemodulator;
private RealFIRFilter_RB_RB mLowPassFilter;
private AutomaticGainControl_RB mAGC;
/**
* FM Demodulator with integrated DC removal and automatic frequency
* correction control.
*
* @param pass - pass frequency for IQ filtering prior to demodulation. This
* frequency should be half of the signal bandwidth since the filter will
* be applied against each of the inphase and quadrature signals and the
* combined pass bandwidth will be twice this value.
*
* @param stop - stop frequency for IQ filtering prior to demodulation.
*
* @param maxFrequencyCorrection - defines the maximum frequency +/- that
* the correction controller can adjust from the center tuned frequency.
* Set this value to 0 for no frequency correction control.
*/
public AMDemodulatorModule()
{
mIQFilter = new ComplexFIRFilter_CB_CB( FilterFactory.getLowPass(
SAMPLE_RATE, 5000, 73, WindowType.HAMMING ), 1.0f );
mDemodulator = new AMDemodulator_CB( 500.0f );
mIQFilter.setListener( mDemodulator );
/**
* AM Demodulator Module
*/
public AMDemodulatorModule()
{
mIQFilter = new ComplexFIRFilter_CB_CB(FilterFactory.getLowPass(
SAMPLE_RATE, 5000, 73, WindowType.HAMMING), 1.0f);
mLowPassFilter = new RealFIRFilter_RB_RB(
FilterFactory.getLowPass( 48000, 3000, 31, WindowType.COSINE ), 1.0f );
mDemodulator.setListener( mLowPassFilter );
mAGC = new AutomaticGainControl_RB();
mLowPassFilter.setListener( mAGC );
}
mDemodulator = new AMDemodulator_CB(500.0f);
mIQFilter.setListener(mDemodulator);
@Override
public Listener<ComplexBuffer> getComplexBufferListener()
{
return mIQFilter;
}
mLowPassFilter = new RealFIRFilter_RB_RB(
FilterFactory.getLowPass(48000, 3000, 31, WindowType.COSINE), 1.0f);
mDemodulator.setListener(mLowPassFilter);
@Override
public void dispose()
{
mIQFilter.dispose();
mIQFilter = null;
mDemodulator.dispose();
mDemodulator = null;
mLowPassFilter.dispose();
mLowPassFilter = null;
mAGC.dispose();
mAGC = null;
}
mAGC = new AutomaticGainControl_RB();
mLowPassFilter.setListener(mAGC);
}
@Override
public void reset()
{
}
@Override
public Listener<ComplexBuffer> getComplexBufferListener()
{
return mIQFilter;
}
@Override
public void setFilteredRealBufferListener( Listener<RealBuffer> listener )
{
mAGC.setListener( listener );
}
@Override
public void dispose()
{
mIQFilter.dispose();
mIQFilter = null;
@Override
public void removeFilteredRealBufferListener()
{
mAGC.removeListener();
}
mDemodulator.dispose();
mDemodulator = null;
@Override
public void start( ScheduledExecutorService executor )
{
}
mLowPassFilter.dispose();
mLowPassFilter = null;
@Override
public void stop()
{
}
mAGC.dispose();
mAGC = null;
}
@Override
public void reset()
{
}
@Override
public void setFilteredRealBufferListener(Listener<RealBuffer> listener)
{
mAGC.setListener(listener);
}
@Override
public void removeFilteredRealBufferListener()
{
mAGC.removeListener();
}
@Override
public void start()
{
}
@Override
public void stop()
{
}
}

View File

@@ -97,7 +97,7 @@ public class DemodulatedAudioFilterModule extends Module implements IUnFilteredR
}
@Override
public void start(ScheduledExecutorService executor)
public void start()
{
}

View File

@@ -104,7 +104,7 @@ public class FMDemodulatorModule extends Module implements IComplexBufferListene
}
@Override
public void start(ScheduledExecutorService executor)
public void start()
{
}

View File

@@ -65,7 +65,7 @@ public abstract class EventLogger extends Module
public abstract String getHeader();
@Override
public void start(ScheduledExecutorService executor)
public void start()
{
if(mLogFile == null)
{

View File

@@ -25,6 +25,7 @@ import module.Module;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sample.Listener;
import util.ThreadPool;
import java.io.FileOutputStream;
import java.io.IOException;
@@ -196,7 +197,7 @@ public abstract class AudioRecorder extends Module implements Listener<AudioPack
*
* @param executor to use in scheduling audio conversion and file writes.
*/
public void start(ScheduledExecutorService executor)
public void start()
{
if(mRunning.compareAndSet(false, true))
{
@@ -212,7 +213,7 @@ public abstract class AudioRecorder extends Module implements Listener<AudioPack
mFileOutputStream = new FileOutputStream(mPath.toFile());
/* Schedule the handler to run every half second */
mProcessorHandle = executor.scheduleAtFixedRate(mBufferProcessor, 0, 500, TimeUnit.MILLISECONDS);
mProcessorHandle = ThreadPool.SCHEDULED.scheduleAtFixedRate(mBufferProcessor, 0, 500, TimeUnit.MILLISECONDS);
}
catch(IOException io)
{

View File

@@ -1,221 +1,218 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014,2015 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 record.wave;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.sound.sampled.AudioFormat;
import module.Module;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sample.Buffer;
import sample.ConversionUtils;
import sample.Listener;
import sample.complex.ComplexBuffer;
import sample.complex.IComplexBufferListener;
import util.ThreadPool;
import util.TimeStamp;
import javax.sound.sampled.AudioFormat;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* WAVE audio recorder module for recording complex (I&Q) samples to a wave file
*/
public class ComplexBufferWaveRecorder extends Module
implements IComplexBufferListener, Listener<ComplexBuffer>
public class ComplexBufferWaveRecorder extends Module
implements IComplexBufferListener, Listener<ComplexBuffer>
{
private final static Logger mLog =
LoggerFactory.getLogger( ComplexBufferWaveRecorder.class );
private final static Logger mLog =
LoggerFactory.getLogger(ComplexBufferWaveRecorder.class);
private WaveWriter mWriter;
private String mFilePrefix;
private Path mFile;
private AudioFormat mAudioFormat;
private BufferProcessor mBufferProcessor;
private ScheduledFuture<?> mProcessorHandle;
private LinkedBlockingQueue<ComplexBuffer> mBuffers = new LinkedBlockingQueue<>( 500 );
private AtomicBoolean mRunning = new AtomicBoolean();
public ComplexBufferWaveRecorder( int sampleRate, String filePrefix )
{
mAudioFormat = new AudioFormat( sampleRate, //SampleRate
16, //Sample Size
2, //Channels
true, //Signed
false ); //Little Endian
private AudioFormat mAudioFormat;
mFilePrefix = filePrefix;
}
private BufferProcessor mBufferProcessor;
private ScheduledFuture<?> mProcessorHandle;
private LinkedBlockingQueue<ComplexBuffer> mBuffers = new LinkedBlockingQueue<>(500);
public Path getFile()
{
return mFile;
}
public void start( ScheduledExecutorService executor )
{
if( mRunning.compareAndSet( false, true ) )
{
if( mBufferProcessor == null )
{
mBufferProcessor = new BufferProcessor();
}
private AtomicBoolean mRunning = new AtomicBoolean();
try
{
StringBuilder sb = new StringBuilder();
sb.append( mFilePrefix );
sb.append( "_" );
sb.append( TimeStamp.getTimeStamp( "_" ) );
sb.append( ".wav" );
mFile = Paths.get( sb.toString() );
public ComplexBufferWaveRecorder(int sampleRate, String filePrefix)
{
mAudioFormat = new AudioFormat(sampleRate, //SampleRate
16, //Sample Size
2, //Channels
true, //Signed
false); //Little Endian
mWriter = new WaveWriter( mAudioFormat, mFile );
mFilePrefix = filePrefix;
}
public Path getFile()
{
return mFile;
}
public void start()
{
if(mRunning.compareAndSet(false, true))
{
if(mBufferProcessor == null)
{
mBufferProcessor = new BufferProcessor();
}
try
{
StringBuilder sb = new StringBuilder();
sb.append(mFilePrefix);
sb.append("_");
sb.append(TimeStamp.getTimeStamp("_"));
sb.append(".wav");
mFile = Paths.get(sb.toString());
mWriter = new WaveWriter(mAudioFormat, mFile);
/* Schedule the processor to run every 500 milliseconds */
mProcessorHandle = executor.scheduleAtFixedRate(
mBufferProcessor, 0, 500, TimeUnit.MILLISECONDS );
}
catch( IOException io )
{
mLog.error( "Error starting complex baseband recorder", io );
}
}
}
public void stop()
{
if( mRunning.compareAndSet( true, false ) )
{
receive( new PoisonPill() );
}
}
@Override
public void receive( ComplexBuffer buffer )
{
if( mRunning.get() )
{
boolean success = mBuffers.offer( buffer );
if( !success )
{
mLog.error( "recorder buffer overflow - purging [" +
mFile.toFile().getAbsolutePath() + "]" );
mBuffers.clear();
}
}
}
@Override
public Listener<ComplexBuffer> getComplexBufferListener()
{
return this;
}
@Override
public void dispose()
{
stop();
}
@Override
public void reset()
{
}
public class BufferProcessor implements Runnable
{
public void run()
{
try
{
Buffer buffer = mBuffers.poll();
while( buffer != null )
{
if( buffer instanceof PoisonPill )
{
buffer = null;
mBuffers.clear();
if( mWriter != null )
{
try
{
mWriter.close();
mWriter = null;
}
catch( IOException io )
{
mLog.error( "Error stopping complex wave recorder [" +
getFile() + "]", io );
}
}
mFile = null;
if( mProcessorHandle != null )
{
mProcessorHandle.cancel( true );
}
mProcessorHandle = null;
}
else
{
mWriter.write( ConversionUtils.convertToSigned16BitSamples( buffer ) );
buffer = mBuffers.poll();
}
}
mProcessorHandle = ThreadPool.SCHEDULED.scheduleAtFixedRate(
mBufferProcessor, 0, 500, TimeUnit.MILLISECONDS);
}
catch ( IOException ioe )
{
/* Stop this module if/when we get an IO exception */
mBuffers.clear();
stop();
mLog.error( "IOException while trying to write to the wave "
+ "writer", ioe );
}
}
catch(IOException io)
{
mLog.error("Error starting complex baseband recorder", io);
}
}
}
/**
* This is used as a sentinel value to signal the buffer processor to end
*/
public class PoisonPill extends ComplexBuffer
{
public PoisonPill()
{
super( new float[ 1 ] );
}
}
public void stop()
{
if(mRunning.compareAndSet(true, false))
{
receive(new PoisonPill());
}
}
@Override
public void receive(ComplexBuffer buffer)
{
if(mRunning.get())
{
boolean success = mBuffers.offer(buffer);
if(!success)
{
mLog.error("recorder buffer overflow - purging [" +
mFile.toFile().getAbsolutePath() + "]");
mBuffers.clear();
}
}
}
@Override
public Listener<ComplexBuffer> getComplexBufferListener()
{
return this;
}
@Override
public void dispose()
{
stop();
}
@Override
public void reset()
{
}
public class BufferProcessor implements Runnable
{
public void run()
{
try
{
Buffer buffer = mBuffers.poll();
while(buffer != null)
{
if(buffer instanceof PoisonPill)
{
buffer = null;
mBuffers.clear();
if(mWriter != null)
{
try
{
mWriter.close();
mWriter = null;
}
catch(IOException io)
{
mLog.error("Error stopping complex wave recorder [" +
getFile() + "]", io);
}
}
mFile = null;
if(mProcessorHandle != null)
{
mProcessorHandle.cancel(true);
}
mProcessorHandle = null;
}
else
{
mWriter.write(ConversionUtils.convertToSigned16BitSamples(buffer));
buffer = mBuffers.poll();
}
}
}
catch(IOException ioe)
{
/* Stop this module if/when we get an IO exception */
mBuffers.clear();
stop();
mLog.error("IOException while trying to write to the wave "
+ "writer", ioe);
}
}
}
/**
* This is used as a sentinel value to signal the buffer processor to end
*/
public class PoisonPill extends ComplexBuffer
{
public PoisonPill()
{
super(new float[1]);
}
}
}

View File

@@ -25,6 +25,7 @@ import sample.ConversionUtils;
import sample.Listener;
import sample.real.IFilteredRealBufferListener;
import sample.real.RealBuffer;
import util.ThreadPool;
import util.TimeStamp;
import javax.sound.sampled.AudioFormat;
@@ -89,7 +90,7 @@ public class RealBufferWaveRecorder extends Module
return mFile;
}
public void start(ScheduledExecutorService executor)
public void start()
{
if(mRunning.compareAndSet(false, true))
{
@@ -111,7 +112,7 @@ public class RealBufferWaveRecorder extends Module
mWriter = new WaveWriter(mAudioFormat, mFile);
/* Schedule the processor to run every 500 milliseconds */
mProcessorHandle = executor.scheduleAtFixedRate(mBufferProcessor, 0, 500, TimeUnit.MILLISECONDS);
mProcessorHandle = ThreadPool.SCHEDULED.scheduleAtFixedRate(mBufferProcessor, 0, 500, TimeUnit.MILLISECONDS);
}
catch(IOException io)
{

View File

@@ -21,7 +21,6 @@ package sample;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sample.real.IOverflowListener;
import source.tuner.TunerChannelSource;
import java.util.Collection;
import java.util.concurrent.LinkedTransferQueue;

View File

@@ -0,0 +1,39 @@
/*******************************************************************************
* 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.
*
* 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 sample.complex;
import sample.Listener;
public interface IComplexBufferProvider
{
/**
* Adds the listener to receive complex buffer samples
*/
void addComplexBufferListener(Listener<ComplexBuffer> listener);
/**
* Removes the listener from receiving complex buffer samples
*/
void removeComplexBufferListener(Listener<ComplexBuffer> listener);
/**
* Indicates if there are any complex buffer listeners registered
*/
boolean hasComplexBufferListeners();
}

View File

@@ -18,7 +18,8 @@
******************************************************************************/
package source;
import channel.heartbeat.IHeartbeatProvider;
import source.heartbeat.HeartbeatManager;
import source.heartbeat.IHeartbeatProvider;
import module.Module;
import sample.SampleType;
import sample.real.IOverflowListener;
@@ -28,6 +29,17 @@ import sample.real.IOverflowListener;
*/
public abstract class Source extends Module implements ISourceEventListener, ISourceEventProvider, IHeartbeatProvider
{
private HeartbeatManager mHeartbeatManager = new HeartbeatManager();
/**
* Heartbeat manager for this source
*/
public HeartbeatManager getHeartbeatManager()
{
return mHeartbeatManager;
}
protected IOverflowListener mOverflowListener;
/**

View File

@@ -31,6 +31,7 @@ public class SourceEvent
REQUEST_CHANNEL_FREQUENCY_CORRECTION_CHANGE,
REQUEST_FREQUENCY_CHANGE,
REQUEST_SOURCE_DISPOSE,
REQUEST_START_SAMPLE_STREAM,
REQUEST_STOP_SAMPLE_STREAM;
}
@@ -189,4 +190,13 @@ public class SourceEvent
{
return new SourceEvent(Event.REQUEST_STOP_SAMPLE_STREAM, source);
}
/**
* Creates a request to dispose of the source. This is normally used for TunerChannelSource to notify the
* parent source manager that the channel is no longer needed
*/
public static SourceEvent sourceDisposeRequest(Source source)
{
return new SourceEvent(Event.REQUEST_SOURCE_DISPOSE, source);
}
}

View File

@@ -19,7 +19,7 @@
package source.config;
import source.SourceType;
import source.tuner.TunerChannel;
import source.tuner.channel.TunerChannel;
import javax.xml.bind.annotation.XmlAttribute;

View File

@@ -19,7 +19,7 @@
package source.config;
import source.SourceType;
import source.tuner.TunerChannel;
import source.tuner.channel.TunerChannel;
import javax.xml.bind.annotation.XmlAttribute;
import java.text.DecimalFormat;

View File

@@ -16,7 +16,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
******************************************************************************/
package channel.heartbeat;
package source.heartbeat;
public class Heartbeat
{

View File

@@ -0,0 +1,58 @@
/*******************************************************************************
* 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.
*
* 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 source.heartbeat;
import sample.Broadcaster;
import sample.Listener;
public class HeartbeatManager
{
private static final Heartbeat HEARTBEAT = new Heartbeat();
private Broadcaster<Heartbeat> mHeartbeatBroadcaster = new Broadcaster<>();
public HeartbeatManager()
{
}
/**
* Sends a heartbeat to all registered listeners
*/
public void broadcast()
{
mHeartbeatBroadcaster.broadcast(HEARTBEAT);
}
/**
* Adds the listener to receive heartbeats
*/
public void addHeartbeatListener(Listener<Heartbeat> listener)
{
mHeartbeatBroadcaster.addListener(listener);
}
/**
* Removes the listener from receiving heartbeats
* @param listener
*/
public void removeHeartbeatListener(Listener<Heartbeat> listener)
{
mHeartbeatBroadcaster.removeListener(listener);
}
}

View File

@@ -16,7 +16,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
******************************************************************************/
package channel.heartbeat;
package source.heartbeat;
import sample.Listener;

View File

@@ -16,13 +16,9 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
******************************************************************************/
package channel.heartbeat;
import sample.Listener;
package source.heartbeat;
public interface IHeartbeatProvider
{
public void setHeartbeatListener(Listener<Heartbeat> listener);
public void removeHeartbeatListener();
public HeartbeatManager getHeartbeatManager();
}

View File

@@ -17,8 +17,7 @@
******************************************************************************/
package source.mixer;
import channel.heartbeat.Heartbeat;
import dsp.gain.AutomaticGainControl;
import source.heartbeat.Heartbeat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sample.Listener;

View File

@@ -7,8 +7,8 @@ import java.util.concurrent.RejectedExecutionException;
import settings.SettingsManager;
import source.SourceException;
import source.tuner.ITunerChannelProvider;
import source.tuner.TunerChannel;
import source.tuner.TunerChannelSource;
import source.tuner.channel.TunerChannel;
import source.tuner.channel.TunerChannelSource;
import source.SourceEvent;
import source.SourceEvent.Event;
import source.ISourceEventProcessor;

View File

@@ -29,8 +29,8 @@ import source.Source;
import source.SourceException;
import source.config.SourceConfigRecording;
import source.config.SourceConfiguration;
import source.tuner.TunerChannel;
import source.tuner.TunerChannelSource;
import source.tuner.channel.TunerChannel;
import source.tuner.channel.TunerChannelSource;
public class RecordingSourceManager
{

View File

@@ -20,6 +20,8 @@ package source.tuner;
import java.util.concurrent.RejectedExecutionException;
import source.SourceException;
import source.tuner.channel.TunerChannel;
import source.tuner.channel.TunerChannelSource;
public interface ITunerChannelProvider
{
@@ -30,7 +32,7 @@ public interface ITunerChannelProvider
*
* @return - source for 48k sample rate
*/
public abstract TunerChannelSource getChannel( TunerChannel channel )
public abstract TunerChannelSource getChannel(TunerChannel channel )
throws RejectedExecutionException, SourceException;
/**

View File

@@ -1,131 +0,0 @@
/*******************************************************************************
* 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/>
******************************************************************************/
package source.tuner;
import java.util.ArrayList;
import javax.sound.sampled.TargetDataLine;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sample.Listener;
import sample.adapter.ISampleAdapter;
import sample.complex.ComplexBuffer;
import sample.complex.ComplexSampleListListener;
import source.mixer.ComplexMixer;
public abstract class MixerTuner extends Tuner implements Listener<ComplexBuffer>
{
private final static Logger mLog =
LoggerFactory.getLogger( MixerTuner.class );
protected MixerTunerType mMixerTunerType;
protected ComplexMixer mComplexMixer;
protected ArrayList<ComplexSampleListListener> mListeners =
new ArrayList<ComplexSampleListListener>();
/**
* Wrapper class to add basic Tuner functionality to the ComplexMixer.
* Subclasses should couple this functionality with a tuner controller class
* to support tuning.
*/
public MixerTuner( String name,
TunerController tunerController,
MixerTunerType mixerTunerType,
TargetDataLine targetDataLine,
ISampleAdapter sampleAdapter )
{
super( name, tunerController );
mMixerTunerType = mixerTunerType;
mComplexMixer = new ComplexMixer( targetDataLine,
mMixerTunerType.getAudioFormat(),
name,
sampleAdapter,
(Listener<ComplexBuffer>)this );
}
public MixerTuner( String name,
TunerController tunerController,
MixerTunerDataLine mixerTuner,
ISampleAdapter sampleAdapter )
{
this( name,
tunerController,
mixerTuner.getMixerTunerType(),
mixerTuner.getTargetDataLine(),
sampleAdapter );
}
public void dispose()
{
mComplexMixer.stop();
super.dispose();
}
public MixerTunerType getMixerTunerType()
{
return mMixerTunerType;
}
public TargetDataLine getTargetDataLine()
{
return mComplexMixer.getTargetDataLine();
}
/**
* Dispatches the received complex buffers from the mComplexMixer to all
* listeners registered on the parent Tuner class
*/
@Override
public void receive( ComplexBuffer buffer )
{
super.broadcast( buffer );
}
/**
* Overrides the parent Tuner class listener management method to allow
* starting the buffer processing thread
*/
@Override
public void addListener( Listener<ComplexBuffer> listener )
{
super.addListener( listener );
/* We can call start multiple times -- it ignores additional requests */
mComplexMixer.start();
}
/**
* Overrides the parent Tuner class listener management method to allow
* stopping the buffer processing thread when there are no more listeners
*/
@Override
public void removeListener( Listener<ComplexBuffer> listener )
{
super.removeListener( listener );
if( !hasListeners() )
{
mComplexMixer.stop();
}
}
}

View File

@@ -23,9 +23,12 @@ import org.slf4j.LoggerFactory;
import sample.Broadcaster;
import sample.Listener;
import sample.complex.ComplexBuffer;
import sample.complex.IComplexBufferProvider;
import source.ISourceEventProcessor;
import source.SourceEvent;
import source.SourceException;
import source.tuner.manager.AbstractSourceManager;
import source.tuner.manager.TunerSourceManager;
import java.util.ArrayList;
import java.util.List;
@@ -35,21 +38,27 @@ import java.util.concurrent.RejectedExecutionException;
/**
* Tuner - provides tuner channel sources, representing a channel frequency
*/
public abstract class Tuner implements ITunerChannelProvider
public abstract class Tuner
{
private final static Logger mLog = LoggerFactory.getLogger(Tuner.class);
private String mName;
protected Broadcaster<TunerEvent> mTunerEventBroadcaster = new Broadcaster<>();
private TunerController mTunerController;
private Broadcaster<ComplexBuffer> mSampleBroadcaster = new Broadcaster<>();
protected List<Listener<TunerEvent>> mTunerChangeListeners = new ArrayList<>();
protected Broadcaster<SourceEvent> mSourceEventBroadcaster = new Broadcaster<>();
private String mName;
private AbstractSourceManager mTunerSourceManager;
/**
* Abstract tuner class.
* @param name of the tuner
* @param tunerController for the tuner
*/
public Tuner(String name, TunerController tunerController)
{
mName = name;
mTunerController = tunerController;
mTunerSourceManager = new TunerSourceManager(mTunerController);
//Rebroadcast frequency and sample rate change events as tuner events
mTunerController.addListener(new ISourceEventProcessor()
{
@@ -59,12 +68,10 @@ public abstract class Tuner implements ITunerChannelProvider
switch(event.getEvent())
{
case NOTIFICATION_FREQUENCY_CHANGE:
broadcast(new TunerEvent(Tuner.this,
source.tuner.TunerEvent.Event.FREQUENCY));
broadcast(new TunerEvent(Tuner.this, source.tuner.TunerEvent.Event.FREQUENCY));
break;
case NOTIFICATION_SAMPLE_RATE_CHANGE:
broadcast(new TunerEvent(Tuner.this,
source.tuner.TunerEvent.Event.SAMPLE_RATE));
broadcast(new TunerEvent(Tuner.this, source.tuner.TunerEvent.Event.SAMPLE_RATE));
break;
default:
break;
@@ -73,6 +80,14 @@ public abstract class Tuner implements ITunerChannelProvider
});
}
/**
* Source Manager. Provides access to registering for complex buffer samples and source event notifications.
*/
public AbstractSourceManager getSourceManager()
{
return mTunerSourceManager;
}
public TunerController getTunerController()
{
return mTunerController;
@@ -85,7 +100,6 @@ public abstract class Tuner implements ITunerChannelProvider
public void dispose()
{
mSampleBroadcaster.clear();
}
public void setName(String name)
@@ -129,93 +143,12 @@ public abstract class Tuner implements ITunerChannelProvider
*/
public abstract double getSampleSize();
/**
* Returns a tuner frequency channel source, tuned to the correct frequency
*
* Note: ensure the concrete implementation registers the returned
* tuner channel source as a listener on the tuner, to receive frequency
* change events.
*
* @param channel - desired frequency and bandwidth
* @return - source for 48k sample rate
*/
public abstract TunerChannelSource getChannel(TunerChannel channel)
throws RejectedExecutionException, SourceException;
/**
* Releases the tuned channel resources
*
* Note: ensure the concrete implementation unregisters the tuner channel
* source from the tuner as a frequency change listener
*
* @param source - previously obtained tuner channel
*/
public abstract void releaseChannel(TunerChannelSource source);
/**
* Registers the listener to receive complex float sample arrays
*/
public void addListener(Listener<ComplexBuffer> listener)
{
mSampleBroadcaster.addListener(listener);
}
/**
* Removes the registered listener
*/
public void removeListener(Listener<ComplexBuffer> listener)
{
mSampleBroadcaster.removeListener(listener);
}
/**
* Number of registered complex sample buffer listeners
*/
public int getListenerCount()
{
return mSampleBroadcaster.getListenerCount();
}
/**
* Provides access to the internal sample broadcaster for sub-class use (ie protected).
*/
protected Broadcaster<ComplexBuffer> getSampleBroadcaster()
{
return mSampleBroadcaster;
}
/**
* Indicates if the listener is currently registered with this tuner
* @param listener of complex buffer samples
* @return true if the listener is registered
*/
public boolean hasListener(Listener<ComplexBuffer> listener)
{
return mSampleBroadcaster.hasListener(listener);
}
/**
* Indicates if this tuner has any complex buffer listeners registered
*/
public boolean hasListeners()
{
return mSampleBroadcaster.hasListeners();
}
/**
* Broadcasts the samples to all registered listeners
*/
public void broadcast(ComplexBuffer sampleBuffer)
{
mSampleBroadcaster.broadcast(sampleBuffer);
}
/**
* Registers the listener
*/
public void addTunerChangeListener(Listener<TunerEvent> listener)
{
mTunerChangeListeners.add(listener);
mTunerEventBroadcaster.addListener(listener);
}
/**
@@ -223,7 +156,7 @@ public abstract class Tuner implements ITunerChannelProvider
*/
public void removeTunerChangeListener(Listener<TunerEvent> listener)
{
mTunerChangeListeners.remove(listener);
mTunerEventBroadcaster.removeListener(listener);
}
/**
@@ -231,36 +164,6 @@ public abstract class Tuner implements ITunerChannelProvider
*/
public void broadcast(TunerEvent tunerEvent)
{
for(Listener<TunerEvent> listener : mTunerChangeListeners)
{
listener.receive(tunerEvent);
}
//Re-broadcast frequency and sample rate change events as source events
switch(tunerEvent.getEvent())
{
case FREQUENCY:
mSourceEventBroadcaster.broadcast(SourceEvent.frequencyChange(getTunerController().getFrequency()));
break;
case SAMPLE_RATE:
mSourceEventBroadcaster.broadcast(SourceEvent.sampleRateChange(getTunerController().getSampleRate()));
break;
}
}
/**
* Adds the listener to receive source events
*/
public void addSourceEventListener(Listener<SourceEvent> listener)
{
mSourceEventBroadcaster.addListener(listener);
}
/**
* Removes the listener from receiving source events
*/
public void removeSourceEventListener(Listener<SourceEvent> listener)
{
mSourceEventBroadcaster.removeListener(listener);
mTunerEventBroadcaster.broadcast(tunerEvent);
}
}

View File

@@ -1,27 +0,0 @@
package source.tuner;
import java.util.concurrent.RejectedExecutionException;
import source.SourceException;
public interface TunerChannelProvider
{
/**
* Returns a tuner frequency channel source, tuned to the correct frequency
*
* @param frequency - desired frequency
*
* @return - source for 48k sample rate
*/
public abstract TunerChannelSource getChannel( TunerChannel channel )
throws RejectedExecutionException, SourceException;
/**
* Releases the tuned channel resources
*
* @param channel - previously obtained tuner channel
*/
public abstract void releaseChannel( TunerChannelSource source );
}

View File

@@ -19,10 +19,16 @@ package source.tuner;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sample.Broadcaster;
import sample.Listener;
import sample.complex.ComplexBuffer;
import sample.complex.IComplexBufferProvider;
import source.ISourceEventProcessor;
import source.SourceEvent;
import source.SourceEvent.Event;
import source.SourceException;
import source.tuner.channel.TunerChannel;
import source.tuner.channel.TunerChannelSource;
import source.tuner.configuration.TunerConfiguration;
import source.tuner.frequency.FrequencyController;
import source.tuner.frequency.FrequencyController.Tunable;
@@ -31,9 +37,11 @@ import java.util.SortedSet;
import java.util.concurrent.ConcurrentSkipListSet;
import java.util.concurrent.RejectedExecutionException;
public abstract class TunerController implements Tunable, ISourceEventProcessor
public abstract class TunerController implements Tunable, ISourceEventProcessor,
IComplexBufferProvider, Listener<ComplexBuffer>
{
private final static Logger mLog = LoggerFactory.getLogger(TunerController.class);
protected Broadcaster<ComplexBuffer> mSampleBroadcaster = new Broadcaster<>();
/* List of currently tuned channels being served to demod channels */
private SortedSet<TunerChannel> mTunedChannels = new ConcurrentSkipListSet<>();
@@ -102,6 +110,12 @@ public abstract class TunerController implements Tunable, ISourceEventProcessor
return mFrequencyController.getFrequency();
}
@Override
public boolean canTune(long frequency)
{
return mFrequencyController.canTune(frequency);
}
public int getSampleRate()
{
return mFrequencyController.getSampleRate();
@@ -369,4 +383,48 @@ public abstract class TunerController implements Tunable, ISourceEventProcessor
{
mFrequencyController.removeFrequencyChangeProcessor(processor);
}
/**
* Adds the listener to receive complex buffer samples
*/
@Override
public void addComplexBufferListener(Listener<ComplexBuffer> listener)
{
mSampleBroadcaster.addListener(listener);
}
/**
* Removes the listener from receiving complex buffer samples
*/
@Override
public void removeComplexBufferListener(Listener<ComplexBuffer> listener)
{
mSampleBroadcaster.removeListener(listener);
}
/**
* Indicates if there are any complex buffer listeners registered on this controller
*/
@Override
public boolean hasComplexBufferListeners()
{
return mSampleBroadcaster.hasListeners();
}
/**
* Broadcasts the buffer to any registered listeners
*/
protected void broadcast(ComplexBuffer complexBuffer)
{
mSampleBroadcaster.broadcast(complexBuffer);
}
/**
* Implements the Listener<T> interface to receive and distribute complex buffers from subclass implementations
*/
@Override
public void receive(ComplexBuffer complexBuffer)
{
broadcast(complexBuffer);
}
}

View File

@@ -246,25 +246,21 @@ public class TunerManager
+ "supported");
}
private TunerInitStatus initFuncubeProTuner(Device device,
DeviceDescriptor descriptor)
private TunerInitStatus initFuncubeProTuner(Device device, DeviceDescriptor descriptor)
{
String reason = "NOT LOADED";
MixerTunerDataLine dataline = getMixerTunerDataLine(
TunerClass.FUNCUBE_DONGLE_PRO.getTunerType());
MixerTunerDataLine dataline = getMixerTunerDataLine(TunerClass.FUNCUBE_DONGLE_PRO.getTunerType());
if(dataline != null)
{
FCD1TunerController controller =
new FCD1TunerController(device, descriptor);
FCD1TunerController controller = new FCD1TunerController(dataline, device, descriptor);
try
{
controller.init();
FCDTuner tuner =
new FCDTuner(dataline, controller);
FCDTuner tuner = new FCDTuner(controller);
return new TunerInitStatus(tuner, "LOADED");
}
@@ -284,25 +280,21 @@ public class TunerManager
+ "loaded - " + reason);
}
private TunerInitStatus initFuncubeProPlusTuner(Device device,
DeviceDescriptor descriptor)
private TunerInitStatus initFuncubeProPlusTuner(Device device, DeviceDescriptor descriptor)
{
String reason = "NOT LOADED";
MixerTunerDataLine dataline = getMixerTunerDataLine(
TunerClass.FUNCUBE_DONGLE_PRO_PLUS.getTunerType());
MixerTunerDataLine dataline = getMixerTunerDataLine(TunerClass.FUNCUBE_DONGLE_PRO_PLUS.getTunerType());
if(dataline != null)
{
FCD2TunerController controller =
new FCD2TunerController(device, descriptor);
FCD2TunerController controller = new FCD2TunerController(dataline, device, descriptor);
try
{
controller.init();
FCDTuner tuner =
new FCDTuner(dataline, controller);
FCDTuner tuner = new FCDTuner(controller);
return new TunerInitStatus(tuner, "LOADED");
}
@@ -323,8 +315,7 @@ public class TunerManager
+ "loaded - " + reason);
}
private TunerInitStatus initHackRFTuner(Device device,
DeviceDescriptor descriptor)
private TunerInitStatus initHackRFTuner(Device device, DeviceDescriptor descriptor)
{
try
{

View File

@@ -1,298 +1,292 @@
package source.tuner;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.RejectedExecutionException;
import javax.swing.table.AbstractTableModel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sample.Listener;
import source.Source;
import source.SourceException;
import source.config.SourceConfigTuner;
import source.tuner.TunerEvent.Event;
import source.tuner.channel.TunerChannel;
import source.tuner.channel.TunerChannelSource;
import source.tuner.configuration.TunerConfiguration;
import source.tuner.configuration.TunerConfigurationModel;
import javax.swing.table.AbstractTableModel;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class TunerModel extends AbstractTableModel implements Listener<TunerEvent>
{
private static final long serialVersionUID = 1L;
private final static Logger mLog = LoggerFactory.getLogger( TunerModel.class );
private static final long serialVersionUID = 1L;
public static final int TUNER_TYPE = 0;
public static final int TUNER_ID = 1;
public static final int SAMPLE_RATE = 2;
public static final int FREQUENCY = 3;
public static final int CHANNEL_COUNT = 4;
public static final int SPECTRAL_DISPLAY_MAIN = 5;
public static final int SPECTRAL_DISPLAY_NEW = 6;
private static final String MHZ = " MHz";
private static final String[] COLUMNS =
{ "Tuner", "ID", "Sample Rate", "Frequency", "Channels", "Spectral", "Display" };
private List<Tuner> mTuners = new ArrayList<>();
private List<Listener<TunerEvent>> mTunerEventListeners = new ArrayList<>();
private DecimalFormat mFrequencyFormat = new DecimalFormat( "0.00000" );
private DecimalFormat mSampleRateFormat = new DecimalFormat( "0.000" );
private TunerConfigurationModel mTunerConfigurationModel;
private final static Logger mLog = LoggerFactory.getLogger(TunerModel.class);
public TunerModel( TunerConfigurationModel tunerConfigurationModel )
{
mTunerConfigurationModel = tunerConfigurationModel;
}
public TunerConfigurationModel getTunerConfigurationModel()
{
return mTunerConfigurationModel;
}
public static final int TUNER_TYPE = 0;
public static final int TUNER_ID = 1;
public static final int SAMPLE_RATE = 2;
public static final int FREQUENCY = 3;
public static final int CHANNEL_COUNT = 4;
public static final int SPECTRAL_DISPLAY_MAIN = 5;
public static final int SPECTRAL_DISPLAY_NEW = 6;
/**
* List of Tuners currently in the model
*/
public List<Tuner> getTuners()
{
return mTuners;
}
public Tuner getTuner( int index )
{
if( index < mTuners.size() )
{
return mTuners.get( index );
}
return null;
}
private static final String MHZ = " MHz";
private static final String[] COLUMNS =
{"Tuner", "ID", "Sample Rate", "Frequency", "Channels", "Spectral", "Display"};
public void addTuners( List<Tuner> Tuners )
{
for( Tuner tuner: Tuners )
{
addTuner( tuner );
}
}
private List<Tuner> mTuners = new ArrayList<>();
private List<Listener<TunerEvent>> mTunerEventListeners = new ArrayList<>();
private DecimalFormat mFrequencyFormat = new DecimalFormat("0.00000");
private DecimalFormat mSampleRateFormat = new DecimalFormat("0.000");
/**
* Adds the Tuner to this model
*/
public void addTuner( Tuner tuner )
{
if( !mTuners.contains( tuner ) )
{
//Get the tuner configuration and apply it to the tuner - this
//call should always produce a tuner configuration
TunerConfiguration config = mTunerConfigurationModel
.getTunerConfiguration( tuner.getTunerType(), tuner.getUniqueID() );
try
{
tuner.getTunerController().apply( config );
mTuners.add( tuner );
int index = mTuners.indexOf( tuner );
fireTableRowsInserted( index, index );
tuner.addTunerChangeListener( this );
}
catch( SourceException se )
{
mLog.error( "Couldn't apply tuner configuration to tuner - ["
+ tuner.getTunerType().name() + "] with id ["
+ tuner.getUniqueID() + "] - tuner will not be included" );
}
}
}
private TunerConfigurationModel mTunerConfigurationModel;
/**
* Removes the Tuner from this model
*/
public void removeTuner( Tuner tuner )
{
if( mTuners.contains( tuner ) )
{
tuner.removeTunerChangeListener( this );
int index = mTuners.indexOf( tuner );
mTuners.remove( tuner );
fireTableRowsDeleted( index, index );
}
}
public void addListener( Listener<TunerEvent> listener )
{
mTunerEventListeners.add( listener );
}
public TunerModel(TunerConfigurationModel tunerConfigurationModel)
{
mTunerConfigurationModel = tunerConfigurationModel;
}
public void removeListener( Listener<TunerEvent> listener )
{
mTunerEventListeners.remove( listener );
}
public TunerConfigurationModel getTunerConfigurationModel()
{
return mTunerConfigurationModel;
}
public void broadcast( TunerEvent event )
{
for( Listener<TunerEvent> listener: mTunerEventListeners )
{
listener.receive( event );
}
}
/**
* List of Tuners currently in the model
*/
public List<Tuner> getTuners()
{
return mTuners;
}
/**
* Requests to display the first tuner in this model. Invoke this method
* after all listeners have registered and tuners have been added to this
* model, in order to inform the primary display to use the first tuner.
*/
public void requestFirstTunerDisplay()
{
if( mTuners.size() > 0 )
{
broadcast( new TunerEvent( mTuners.get( 0 ),
Event.REQUEST_MAIN_SPECTRAL_DISPLAY ) );
}
}
@Override
public void receive( TunerEvent event )
{
if( event.getTuner() != null )
{
int index = mTuners.indexOf( event.getTuner() );
if( index >= 0 )
{
switch( event.getEvent() )
{
case CHANNEL_COUNT:
fireTableCellUpdated( index, CHANNEL_COUNT );
break;
case FREQUENCY:
fireTableCellUpdated( index, FREQUENCY );
mTunerConfigurationModel.tunerFrequencyChanged( event.getTuner() );
break;
case SAMPLE_RATE:
fireTableCellUpdated( index, SAMPLE_RATE );
break;
case REQUEST_MAIN_SPECTRAL_DISPLAY:
fireTableCellUpdated( index, SPECTRAL_DISPLAY_MAIN );
default:
break;
}
}
}
broadcast( event );
}
public Tuner getTuner(int index)
{
if(index < mTuners.size())
{
return mTuners.get(index);
}
@Override
public int getRowCount()
{
return mTuners.size();
}
return null;
}
@Override
public int getColumnCount()
{
return COLUMNS.length;
}
public void addTuners(List<Tuner> Tuners)
{
for(Tuner tuner : Tuners)
{
addTuner(tuner);
}
}
@Override
public Object getValueAt( int rowIndex, int columnIndex )
{
if( rowIndex < mTuners.size() )
{
Tuner tuner = mTuners.get( rowIndex );
/**
* Adds the Tuner to this model
*/
public void addTuner(Tuner tuner)
{
if(!mTuners.contains(tuner))
{
//Get the tuner configuration and apply it to the tuner - this
//call should always produce a tuner configuration
TunerConfiguration config = mTunerConfigurationModel
.getTunerConfiguration(tuner.getTunerType(), tuner.getUniqueID());
switch( columnIndex )
{
case TUNER_TYPE:
return tuner.getTunerType().getLabel();
case TUNER_ID:
return tuner.getUniqueID();
case SAMPLE_RATE:
int sampleRate = tuner.getTunerController().getSampleRate();
try
{
tuner.getTunerController().apply(config);
return mSampleRateFormat.format( sampleRate / 1E6D ) + MHZ;
case FREQUENCY:
try
{
long frequency = tuner.getTunerController().getFrequency();
return mFrequencyFormat.format( frequency / 1E6D ) + MHZ;
}
catch( Exception e )
{
return 0;
}
case CHANNEL_COUNT:
return tuner.getTunerController().getChannelCount();
case SPECTRAL_DISPLAY_MAIN:
return "Main";
case SPECTRAL_DISPLAY_NEW:
return "New";
default:
break;
}
}
mTuners.add(tuner);
int index = mTuners.indexOf(tuner);
fireTableRowsInserted(index, index);
tuner.addTunerChangeListener(this);
}
catch(SourceException se)
{
mLog.error("Couldn't apply tuner configuration to tuner - ["
+ tuner.getTunerType().name() + "] with id ["
+ tuner.getUniqueID() + "] - tuner will not be included");
}
}
}
/**
* Removes the Tuner from this model
*/
public void removeTuner(Tuner tuner)
{
if(mTuners.contains(tuner))
{
tuner.removeTunerChangeListener(this);
int index = mTuners.indexOf(tuner);
mTuners.remove(tuner);
fireTableRowsDeleted(index, index);
}
}
public void addListener(Listener<TunerEvent> listener)
{
mTunerEventListeners.add(listener);
}
public void removeListener(Listener<TunerEvent> listener)
{
mTunerEventListeners.remove(listener);
}
public void broadcast(TunerEvent event)
{
for(Listener<TunerEvent> listener : mTunerEventListeners)
{
listener.receive(event);
}
}
/**
* Requests to display the first tuner in this model. Invoke this method
* after all listeners have registered and tuners have been added to this
* model, in order to inform the primary display to use the first tuner.
*/
public void requestFirstTunerDisplay()
{
if(mTuners.size() > 0)
{
broadcast(new TunerEvent(mTuners.get(0),
Event.REQUEST_MAIN_SPECTRAL_DISPLAY));
}
}
@Override
public void receive(TunerEvent event)
{
if(event.getTuner() != null)
{
int index = mTuners.indexOf(event.getTuner());
if(index >= 0)
{
switch(event.getEvent())
{
case CHANNEL_COUNT:
fireTableCellUpdated(index, CHANNEL_COUNT);
break;
case FREQUENCY:
fireTableCellUpdated(index, FREQUENCY);
mTunerConfigurationModel.tunerFrequencyChanged(event.getTuner());
break;
case SAMPLE_RATE:
fireTableCellUpdated(index, SAMPLE_RATE);
break;
case REQUEST_MAIN_SPECTRAL_DISPLAY:
fireTableCellUpdated(index, SPECTRAL_DISPLAY_MAIN);
default:
break;
}
}
}
broadcast(event);
}
@Override
public int getRowCount()
{
return mTuners.size();
}
@Override
public int getColumnCount()
{
return COLUMNS.length;
}
@Override
public Object getValueAt(int rowIndex, int columnIndex)
{
if(rowIndex < mTuners.size())
{
Tuner tuner = mTuners.get(rowIndex);
switch(columnIndex)
{
case TUNER_TYPE:
return tuner.getTunerType().getLabel();
case TUNER_ID:
return tuner.getUniqueID();
case SAMPLE_RATE:
int sampleRate = tuner.getTunerController().getSampleRate();
return mSampleRateFormat.format(sampleRate / 1E6D) + MHZ;
case FREQUENCY:
try
{
long frequency = tuner.getTunerController().getFrequency();
return mFrequencyFormat.format(frequency / 1E6D) + MHZ;
}
catch(Exception e)
{
return 0;
}
case CHANNEL_COUNT:
return tuner.getTunerController().getChannelCount();
case SPECTRAL_DISPLAY_MAIN:
return "Main";
case SPECTRAL_DISPLAY_NEW:
return "New";
default:
break;
}
}
return null;
}
@Override
public String getColumnName(int columnIndex)
{
return COLUMNS[columnIndex];
}
return null;
}
@Override
public String getColumnName( int columnIndex )
{
return COLUMNS[ columnIndex ];
}
/**
* Iterates current tuners to get a tuner channel source for the frequency
* specified in the channel config's source config object.
*
*
* Returns null if no tuner can source the channel
*/
public Source getSource( SourceConfigTuner config, int bandwidth )
public Source getSource(SourceConfigTuner config, int bandwidth)
{
TunerChannelSource retVal = null;
TunerChannel tunerChannel = config.getTunerChannel();
tunerChannel.setBandwidth( bandwidth );
Iterator<Tuner> it = mTuners.iterator();
Tuner tuner;
while( it.hasNext() && retVal == null )
{
tuner = it.next();
try
TunerChannelSource retVal = null;
TunerChannel tunerChannel = config.getTunerChannel();
tunerChannel.setBandwidth(bandwidth);
Iterator<Tuner> it = mTuners.iterator();
Tuner tuner;
while(it.hasNext() && retVal == null)
{
tuner = it.next();
try
{
retVal = tuner.getChannel( tunerChannel );
retVal = tuner.getSourceManager().getSource(tunerChannel);
}
catch ( RejectedExecutionException ree )
{
mLog.error( "couldn't provide tuner channel source", ree );
}
catch ( SourceException e )
catch(Exception e)
{
mLog.error( "error obtaining channel from tuner [" +
tuner.getName() + "]", e );
mLog.error("Error obtaining channel source from tuner [" + tuner.getName() + "]", e);
}
}
return retVal;
}
return retVal;
}
}

View File

@@ -17,19 +17,11 @@
******************************************************************************/
package source.tuner.airspy;
import java.util.concurrent.RejectedExecutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sample.Listener;
import sample.complex.ComplexBuffer;
import source.SourceException;
import source.tuner.Tuner;
import source.tuner.TunerChannel;
import source.tuner.TunerChannelSource;
import source.tuner.TunerClass;
import source.tuner.TunerEvent;
import source.tuner.TunerType;
import source.tuner.airspy.AirspyTunerController.BoardID;
@@ -79,50 +71,4 @@ public class AirspyTuner extends Tuner
{
return 13.0;
}
@Override
public TunerChannelSource getChannel( TunerChannel channel )
throws RejectedExecutionException, SourceException
{
//TODO: this channel has a decimated sample rate of:
// 10.0 MSps = 10,000,000 / 208 = 48076.923
// 5.0 MSps = 5,000,000 / 104 = 48076.923
// 2.5 MSps = 2,500,000 / 52 = 48076.923
//Consider implementing a fractional resampler to get a correct 48 kHz
//output sample rate
TunerChannelSource source = getController().getChannel( this, channel );
if( source != null )
{
broadcast( new TunerEvent( this, TunerEvent.Event.CHANNEL_COUNT ) );
}
return source;
}
@Override
public void releaseChannel( TunerChannelSource source )
{
/* Unregister for receiving samples */
removeListener( (Listener<ComplexBuffer>)source );
/* Tell the controller to release the channel and cleanup */
if( source != null )
{
getController().releaseChannel( source );
}
}
@Override
public void addListener( Listener<ComplexBuffer> listener )
{
getController().addListener( listener );
}
@Override
public void removeListener( Listener<ComplexBuffer> listener )
{
getController().removeListener( listener );
}
}

View File

@@ -1,7 +1,5 @@
package source.tuner.airspy;
import dsp.filter.dc.DCRemovalFilter_RB;
import dsp.filter.hilbert.HilbertTransform;
import org.apache.commons.io.EndianUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -9,16 +7,11 @@ import org.usb4java.Device;
import org.usb4java.DeviceHandle;
import org.usb4java.LibUsb;
import org.usb4java.LibUsbException;
import org.usb4java.Transfer;
import org.usb4java.TransferCallback;
import sample.Broadcaster;
import sample.Listener;
import sample.complex.ComplexBuffer;
import source.SourceException;
import source.tuner.TunerController;
import source.tuner.configuration.TunerConfiguration;
import source.tuner.usb.USBTransferProcessor;
import util.ThreadPool;
import source.tuner.usb.USBTunerController;
import javax.usb.UsbException;
import java.nio.ByteBuffer;
@@ -27,10 +20,6 @@ import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import java.util.concurrent.LinkedTransferQueue;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* SDR Trunk
@@ -72,7 +61,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
* POSSIBILITY OF SUCH DAMAGE.
*/
public class AirspyTunerController extends TunerController
public class AirspyTunerController extends USBTunerController
{
public static final Gain LINEARITY_GAIN_DEFAULT = Gain.LINEARITY_14;
public static final Gain SENSITIVITY_GAIN_DEFAULT = Gain.SENSITIVITY_10;
@@ -193,6 +182,14 @@ public class AirspyTunerController extends TunerController
USB_TRANSFER_BUFFER_SIZE);
}
@Override
protected USBTransferProcessor getUSBTransferProcessor()
{
return mUSBTransferProcessor;
}
/**
* Claims the USB interface. If another application currently has
* the interface claimed, the USB_FORCE_CLAIM_INTERFACE setting
@@ -1193,34 +1190,4 @@ public class AirspyTunerController extends TunerController
return mValue;
}
}
/**
* Adds the IQ buffer listener and automatically starts buffer transfer processing, if not already started.
*/
public void addListener(Listener<ComplexBuffer> listener)
{
if(mUSBTransferProcessor != null)
{
mUSBTransferProcessor.addListener(listener);
}
else
{
mLog.error("Couldn't add IQ buffer listener to Airspy tuner - processor is null");
}
}
/**
* Removes the IQ buffer listener and stops buffer transfer processing if there are no more listeners.
*/
public void removeListener(Listener<ComplexBuffer> listener)
{
if(mUSBTransferProcessor != null)
{
mUSBTransferProcessor.removeListener(listener);
}
else
{
mLog.error("Couldn't remove IQ buffer listener from Airspy tuner - processor is null");
}
}
}

View File

@@ -16,12 +16,10 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
******************************************************************************/
package source.tuner;
package source.tuner.channel;
import channel.heartbeat.Heartbeat;
import channel.heartbeat.IHeartbeatProvider;
import dsp.filter.FilterFactory;
import dsp.filter.Window.WindowType;
import dsp.filter.Window;
import dsp.filter.cic.ComplexPrimeCICDecimate;
import dsp.mixer.Oscillator;
import org.slf4j.Logger;
@@ -32,12 +30,13 @@ import sample.OverflowableTransferQueue;
import sample.complex.Complex;
import sample.complex.ComplexBuffer;
import sample.real.IOverflowListener;
import source.ComplexSource;
import source.SourceException;
import source.ISourceEventListener;
import source.ISourceEventProvider;
import source.SourceEvent;
import source.ISourceEventProcessor;
import source.ISourceEventProvider;
import source.Source;
import source.SourceEvent;
import source.SourceException;
import source.tuner.Tuner;
import java.util.ArrayList;
import java.util.List;
@@ -47,9 +46,9 @@ import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
public class TunerChannelSource extends ComplexSource implements ISourceEventProcessor, Listener<ComplexBuffer>
public class DecimatingTunerChannelSource extends TunerChannelSource
{
private final static Logger mLog = LoggerFactory.getLogger(TunerChannelSource.class);
private final static Logger mLog = LoggerFactory.getLogger(DecimatingTunerChannelSource.class);
//Maximum number of filled buffers for the blocking queue
private static final int BUFFER_MAX_CAPACITY = 300;
@@ -59,17 +58,14 @@ public class TunerChannelSource extends ComplexSource implements ISourceEventPro
private static int CHANNEL_RATE = 48000;
private static int CHANNEL_PASS_FREQUENCY = 12000;
private static final Heartbeat HEARTBEAT = new Heartbeat();
private OverflowableTransferQueue<ComplexBuffer> mBuffer;
private Tuner mTuner;
private TunerChannel mTunerChannel;
private Oscillator mMixer;
private ComplexPrimeCICDecimate mDecimationFilter;
private Listener<ComplexBuffer> mListener;
private ISourceEventProcessor mFrequencyChangeProcessor;
private Listener<Heartbeat> mHeartbeatListener;
private DownstreamProcessor mDownstreamFrequencyEventProcessor = new DownstreamProcessor();
private ScheduledFuture<?> mTaskHandle;
@@ -93,28 +89,50 @@ public class TunerChannelSource extends ComplexSource implements ISourceEventPro
* and thus the tuner might no longer be able to source this channel once it
* has been stopped.
*
* @param tuner to obtain wideband IQ samples from
* @param tunerChannel specifying the center frequency for the DDC
* @throws RejectedExecutionException if the thread pool manager cannot
* accept the decimation processing task
* @throws SourceException if the tuner has an issue providing IQ samples
*/
public TunerChannelSource(Tuner tuner, TunerChannel tunerChannel) throws RejectedExecutionException, SourceException
public DecimatingTunerChannelSource(Listener<SourceEvent> listener, TunerChannel tunerChannel, int inputSampleRate,
long frequency)
{
mTuner = tuner;
super(listener, tunerChannel);
mTunerChannel = tunerChannel;
mTuner.getTunerController().addListener((ISourceEventProcessor) this);
mTunerFrequency = mTuner.getTunerController().getFrequency();
mTunerFrequency = frequency;
mBuffer = new OverflowableTransferQueue<>(BUFFER_MAX_CAPACITY, BUFFER_OVERFLOW_RESET_THRESHOLD);
/* Setup the frequency translator to the current source frequency */
long frequencyOffset = mTunerFrequency - mTunerChannel.getFrequency();
mMixer = new Oscillator(frequencyOffset, mTuner.getTunerController().getSampleRate());
mMixer = new Oscillator(frequencyOffset, inputSampleRate);
/* Fire a sample rate change event to setup the decimation chain */
process(SourceEvent.sampleRateChange(mTuner.getTunerController().getSampleRate()));
try
{
process(SourceEvent.sampleRateChange(inputSampleRate));
}
catch(SourceException se)
{
mLog.error("Error", se);
}
}
/**
* Changes the frequency correction value and broadcasts the change to the registered downstream listener.
* @param correction current frequency correction value.
*/
private void setFrequencyCorrection(int correction)
{
mChannelFrequencyCorrection = correction;
updateMixerFrequencyOffset();
mDownstreamFrequencyEventProcessor.broadcast(
SourceEvent.channelFrequencyCorrectionChange(mChannelFrequencyCorrection));
}
/**
@@ -145,7 +163,7 @@ public class TunerChannelSource extends ComplexSource implements ISourceEventPro
mTaskHandle = executor.scheduleAtFixedRate(mDecimationProcessor, 0, 9, TimeUnit.MILLISECONDS);
/* Finally, register to receive samples from the tuner */
mTuner.addListener((Listener<ComplexBuffer>) this);
mSourceEventListener.receive(SourceEvent.startSampleStream(this));
}
else
{
@@ -163,7 +181,7 @@ public class TunerChannelSource extends ComplexSource implements ISourceEventPro
{
if(mRunning.compareAndSet(true, false))
{
mTuner.releaseChannel(this);
mSourceEventListener.receive(SourceEvent.stopSampleStream(this));
mDecimationProcessor.shutdown();
if(mTaskHandle != null)
@@ -186,30 +204,6 @@ public class TunerChannelSource extends ComplexSource implements ISourceEventPro
@Override
public void dispose()
{
if(!mRunning.get())
{
/* Tell the tuner to release/unregister our resources */
mTuner.getTunerController().removeListener(this);
}
}
/**
* Changes the frequency correction value and broadcasts the change to the registered downstream listener.
* @param correction current frequency correction value.
*/
private void setFrequencyCorrection(int correction)
{
mChannelFrequencyCorrection = correction;
updateMixerFrequencyOffset();
mDownstreamFrequencyEventProcessor.broadcast(
SourceEvent.channelFrequencyCorrectionChange(mChannelFrequencyCorrection));
}
public Tuner getTuner()
{
return mTuner;
}
public TunerChannel getTunerChannel()
@@ -234,7 +228,7 @@ public class TunerChannelSource extends ComplexSource implements ISourceEventPro
@Override
public void setListener(Listener<ComplexBuffer> listener)
{
/* Save a pointer to the listener so that if we have to change the
/* Save a pointer to the listener so that if we have to change the
* decimation filter, we can re-add the listener */
mListener = listener;
@@ -290,7 +284,7 @@ public class TunerChannelSource extends ComplexSource implements ISourceEventPro
/* Get new decimation filter */
mDecimationFilter = FilterFactory.getDecimationFilter(sampleRate, CHANNEL_RATE, 1,
CHANNEL_PASS_FREQUENCY, 60, WindowType.HAMMING);
CHANNEL_PASS_FREQUENCY, 60, Window.WindowType.HAMMING);
/* re-add the original output listener */
mDecimationFilter.setListener(mListener);
@@ -336,7 +330,7 @@ public class TunerChannelSource extends ComplexSource implements ISourceEventPro
/**
* Implements ISourceEventProvider to remove the frequency change listener from receiving down-stream frequency
* change events.
*/
*/
@Override
public void removeSourceEventListener()
{
@@ -354,25 +348,6 @@ public class TunerChannelSource extends ComplexSource implements ISourceEventPro
return mDownstreamFrequencyEventProcessor.getSourceEventListener();
}
/**
* Registers the listener to receive heartbeats from this source.
* @param listener to receive heartbeats
*/
@Override
public void setHeartbeatListener(Listener<Heartbeat> listener)
{
mHeartbeatListener = listener;
}
/**
* Removes the currently registered heartbeat listener
*/
@Override
public void removeHeartbeatListener()
{
mHeartbeatListener = null;
}
/**
* Managers frequency change requests and notifications from/to any downstream component. Downstream
* components are those that receive samples from this tuner channel source. These downstream components will be
@@ -500,10 +475,7 @@ public class TunerChannelSource extends ComplexSource implements ISourceEventPro
{
//Send a heartbeat every time this runs to allow downstream components to perform periodic
//state monitoring functions on this thread
if(mHeartbeatListener != null)
{
mHeartbeatListener.receive(HEARTBEAT);
}
getHeartbeatManager().broadcast();
mBuffer.drainTo(mSampleBuffers, 20);

View File

@@ -16,7 +16,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
******************************************************************************/
package source.tuner;
package source.tuner.channel;
public class TunerChannel implements Comparable<TunerChannel>
{

View File

@@ -0,0 +1,45 @@
/*******************************************************************************
* 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.
*
* 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 source.tuner.channel;
import java.util.concurrent.RejectedExecutionException;
import source.SourceException;
public interface TunerChannelProvider
{
/**
* Returns a tuner frequency channel source, tuned to the correct frequency
*
* @param frequency - desired frequency
*
* @return - source for 48k sample rate
*/
public abstract TunerChannelSource getChannel( TunerChannel channel )
throws RejectedExecutionException, SourceException;
/**
* Releases the tuned channel resources
*
* @param channel - previously obtained tuner channel
*/
public abstract void releaseChannel( TunerChannelSource source );
}

View File

@@ -0,0 +1,105 @@
/*******************************************************************************
* 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.
*
* 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 source.tuner.channel;
import dsp.filter.FilterFactory;
import dsp.filter.Window.WindowType;
import dsp.filter.cic.ComplexPrimeCICDecimate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sample.Buffer;
import sample.Listener;
import sample.complex.Complex;
import sample.complex.ComplexBuffer;
import sample.real.IOverflowListener;
import source.ComplexSource;
import source.SourceException;
import source.ISourceEventListener;
import source.ISourceEventProvider;
import source.SourceEvent;
import source.ISourceEventProcessor;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public abstract class TunerChannelSource extends ComplexSource implements ISourceEventProcessor, Listener<ComplexBuffer>
{
private final static Logger mLog = LoggerFactory.getLogger(TunerChannelSource.class);
private TunerChannel mTunerChannel;
protected Listener<SourceEvent> mSourceEventListener;
/**
* Requirements:
* 1. Provide complex buffer samples
* 2. Fetch complex buffer samples from subclass implementations (for distribution)
* 3. Provide heartbeat at each interval
* 4. Provide start/stop running
* 5. Provide support for downstream consumer source events (delegate to the sub class)
* 6. Support buffer overflow listeners
*
* @param sourceEventListener to receive source dispose request
* @param tunerChannel
*/
public TunerChannelSource(Listener<SourceEvent> sourceEventListener, TunerChannel tunerChannel)
{
mSourceEventListener = sourceEventListener;
}
/**
* Tuner channel for this tuner channel source
*/
public TunerChannel getTunerChannel()
{
return mTunerChannel;
}
/**
* Starts this tuner channel source producing sample stream.
*/
public void start()
{
if(mSourceEventListener != null)
{
mSourceEventListener.receive(SourceEvent.startSampleStream(this));
}
//TODO: broadcast center frequency and sample rate to downstream listeners.
}
/**
* Stops this tuner channel source from producing a sample stream. Note: tuner channel sources are one-time usage
* only. Invoking this method will also tell the source manager to dispose of this source.
*/
public void stop()
{
if(mSourceEventListener != null)
{
mSourceEventListener.receive(SourceEvent.stopSampleStream(this));
mSourceEventListener.receive(SourceEvent.sourceDisposeRequest(this));
}
}
@Override
public void reset()
{
//Reset is not valid for a tuner channel source - ignored
}
}

View File

@@ -1,83 +0,0 @@
/*******************************************************************************
* 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/>
******************************************************************************/
package source.tuner.ettus;
import java.util.concurrent.RejectedExecutionException;
import source.SourceException;
import source.tuner.Tuner;
import source.tuner.TunerChannel;
import source.tuner.TunerChannelSource;
import source.tuner.TunerClass;
import source.tuner.TunerType;
import source.tuner.usb.USBTunerDevice;
public class B100Tuner extends Tuner
{
public B100Tuner( USBTunerDevice device )
{
//TODO: this should extend a USBTuner class, so that we can push the
//complex sample listener and the frequency change listener up to that class
super( "Ettus B100 with WBX Tuner", null );
}
public void dispose()
{
}
@Override
public TunerClass getTunerClass()
{
return TunerClass.ETTUS_USRP_B100;
}
@Override
public TunerType getTunerType()
{
return TunerType.ETTUS_WBX;
}
@Override
public double getSampleSize()
{
return 16.0;
}
@Override
public TunerChannelSource getChannel( TunerChannel channel )
throws RejectedExecutionException, SourceException
{
//TODO: pass this call to the tuner controller
return null;
}
@Override
public void releaseChannel( TunerChannelSource source )
{
//TODO: pass this call to the tuner controller
}
@Override
public String getUniqueID()
{
// TODO Auto-generated method stub
return null;
}
}

View File

@@ -1,44 +0,0 @@
/*******************************************************************************
* 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/>
******************************************************************************/
package source.tuner.ettus;
import javax.swing.BorderFactory;
import javax.swing.JLabel;
import javax.swing.JPanel;
import net.miginfocom.swing.MigLayout;
import settings.SettingsManager;
public class B100TunerEditorPanel extends JPanel
{
private static final long serialVersionUID = 1L;
public B100TunerEditorPanel( B100Tuner tuner, SettingsManager settingsManager )
{
init();
}
private void init()
{
setLayout( new MigLayout( "", "[grow,fill]", "[grow,fill]" ) );
setBorder( BorderFactory.createTitledBorder( "Ettus B100 Tuner" ) );
add( new JLabel( "Note: SDRTrunk can only recognize the B100 at this time. It cannot use this tuner") );
}
}

View File

@@ -17,35 +17,19 @@
******************************************************************************/
package source.tuner.fcd;
import java.util.concurrent.RejectedExecutionException;
import sample.Listener;
import sample.adapter.ShortAdapter;
import sample.complex.ComplexBuffer;
import source.SourceException;
import source.tuner.MixerTuner;
import source.tuner.MixerTunerDataLine;
import source.tuner.TunerChannel;
import source.tuner.TunerChannelSource;
import source.tuner.Tuner;
import source.tuner.TunerClass;
import source.tuner.TunerEvent;
import source.tuner.TunerType;
public class FCDTuner extends MixerTuner
public class FCDTuner extends Tuner
{
public FCDTuner( MixerTunerDataLine mixerTDL,
FCDTunerController controller )
public FCDTuner(FCDTunerController controller)
{
super( controller.getConfiguration().toString(),
controller,
mixerTDL,
new ShortAdapter() );
super( controller.getConfiguration().toString(), controller);
}
public void dispose()
{
//TODO: release the mixer tuner data line as well
getController().dispose();
}
@@ -77,36 +61,4 @@ public class FCDTuner extends MixerTuner
{
return 16.0;
}
@Override
public TunerChannelSource getChannel( TunerChannel tunerChannel )
throws RejectedExecutionException, SourceException
{
TunerChannelSource source = getController().getChannel( this, tunerChannel );
if( source != null )
{
broadcast( new TunerEvent( this, TunerEvent.Event.CHANNEL_COUNT ) );
}
return source;
}
/**
* Releases the tuned channel so that the tuner controller can tune to
* other frequencies as needed.
*/
@Override
public void releaseChannel( TunerChannelSource source )
{
if( source != null )
{
/* Unregister for receiving samples */
removeListener( (Listener<ComplexBuffer>)source );
/* Tell the controller to release the channel and cleanup */
/* This will release the channel as a frequency change listener */
getController().releaseChannel( source );
}
}
}

View File

@@ -25,13 +25,19 @@ import org.usb4java.DeviceDescriptor;
import org.usb4java.DeviceHandle;
import org.usb4java.LibUsb;
import org.usb4java.LibUsbException;
import sample.Listener;
import sample.adapter.ShortAdapter;
import sample.complex.ComplexBuffer;
import source.SourceException;
import source.mixer.ComplexMixer;
import source.tuner.MixerTunerDataLine;
import source.tuner.TunerClass;
import source.tuner.TunerController;
import source.tuner.TunerType;
import source.tuner.configuration.TunerConfiguration;
import source.tuner.fcd.proV1.FCD1TunerController.Block;
import javax.sound.sampled.AudioFormat;
import javax.usb.UsbClaimException;
import javax.usb.UsbException;
import java.nio.ByteBuffer;
@@ -54,6 +60,8 @@ public abstract class FCDTunerController extends TunerController
private DeviceHandle mDeviceHandle;
private FCDConfiguration mConfiguration = new FCDConfiguration();
protected ComplexMixer mComplexMixer;
/**
* Generic FCD tuner controller - contains functionality common across both
@@ -64,11 +72,8 @@ public abstract class FCDTunerController extends TunerController
* @param minTunableFrequency
* @param maxTunableFrequency
*/
public FCDTunerController(Device device,
DeviceDescriptor descriptor,
int sampleRate,
int minTunableFrequency,
int maxTunableFrequency)
public FCDTunerController(MixerTunerDataLine mixerTDL, Device device, DeviceDescriptor descriptor, String tunerName,
int minTunableFrequency, int maxTunableFrequency, AudioFormat audioFormat)
{
super(minTunableFrequency, maxTunableFrequency, DC_SPIKE_AVOID_BUFFER, USABLE_BANDWIDTH_PERCENT);
mDevice = device;
@@ -76,11 +81,45 @@ public abstract class FCDTunerController extends TunerController
try
{
mFrequencyController.setSampleRate(sampleRate);
mFrequencyController.setSampleRate((int)audioFormat.getSampleRate());
}
catch(SourceException se)
{
mLog.error("Error setting sample rate to [" + sampleRate + "]", se);
mLog.error("Error setting sample rate to [" + audioFormat.getSampleRate() + "]", se);
}
mComplexMixer = new ComplexMixer( mixerTDL.getTargetDataLine(), audioFormat, tunerName, new ShortAdapter(),
mSampleBroadcaster);
}
/**
* Overrides the super class functionality to auto-start the complex mixer and provide samples to listeners
*/
@Override
public void addComplexBufferListener(Listener<ComplexBuffer> listener)
{
boolean hasExistingListeners = hasComplexBufferListeners();
super.addComplexBufferListener(listener);
if(!hasExistingListeners)
{
mComplexMixer.start();
}
}
/**
* Overrides the super class functionality to auto-stop the complex mixer and stop sample stream when there are no
* more registered listeners
*/
@Override
public void removeComplexBufferListener(Listener<ComplexBuffer> listener)
{
super.removeComplexBufferListener(listener);
if(!hasComplexBufferListeners())
{
mComplexMixer.stop();
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -23,6 +23,7 @@ import org.slf4j.LoggerFactory;
import org.usb4java.Device;
import org.usb4java.DeviceDescriptor;
import source.SourceException;
import source.tuner.MixerTunerDataLine;
import source.tuner.MixerTunerType;
import source.tuner.TunerClass;
import source.tuner.TunerType;
@@ -41,11 +42,10 @@ public class FCD2TunerController extends FCDTunerController
public static final int MAXIMUM_TUNABLE_FREQUENCY = 2050000000;
public static final int SAMPLE_RATE = 192000;
public FCD2TunerController(Device device, DeviceDescriptor descriptor)
public FCD2TunerController(MixerTunerDataLine mixerTDL, Device device, DeviceDescriptor descriptor)
{
super(device, descriptor,
(int)MixerTunerType.FUNCUBE_DONGLE_PRO_PLUS.getAudioFormat().getSampleRate(),
MINIMUM_TUNABLE_FREQUENCY, MAXIMUM_TUNABLE_FREQUENCY);
super(mixerTDL, device, descriptor, MixerTunerType.FUNCUBE_DONGLE_PRO_PLUS.getDisplayString(),
MINIMUM_TUNABLE_FREQUENCY, MAXIMUM_TUNABLE_FREQUENCY, MixerTunerType.FUNCUBE_DONGLE_PRO.getAudioFormat());
}
public void init() throws SourceException

View File

@@ -113,7 +113,7 @@ public class DCTrackingSourceControl extends Module
}
@Override
public void start(ScheduledExecutorService executor)
public void start()
{
}

View File

@@ -95,6 +95,16 @@ public class FrequencyController
setFrequency(frequency, true);
}
/**
* Indicates if the specified frequency can be tuned (ie is within min/max frequency) by this controller.
* @param frequency to evaluate
* @return true if the frequency falls within the tuning range of this controller
*/
public boolean canTune(long frequency)
{
return getMinimumFrequency() <= frequency && frequency <= getMaximumFrequency();
}
/**
* Set frequency with optional broadcast of frequency change event. This
* method supports changing the frequency correction value without
@@ -257,5 +267,10 @@ public class FrequencyController
* Gets the current bandwidth setting of the device
*/
public int getCurrentSampleRate() throws SourceException;
/**
* Indicates if this tunable can tune the frequency
*/
public boolean canTune(long frequency);
}
}

View File

@@ -17,19 +17,12 @@
******************************************************************************/
package source.tuner.hackrf;
import java.util.concurrent.RejectedExecutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sample.Listener;
import sample.complex.ComplexBuffer;
import source.SourceException;
import source.tuner.Tuner;
import source.tuner.TunerChannel;
import source.tuner.TunerChannelSource;
import source.tuner.TunerClass;
import source.tuner.TunerEvent;
import source.tuner.TunerType;
import source.tuner.hackrf.HackRFTunerController.BoardID;
@@ -49,7 +42,6 @@ public class HackRFTuner extends Tuner
public void dispose()
{
//TODO: dispose of something here
}
@Override
@@ -70,33 +62,6 @@ public class HackRFTuner extends Tuner
return 11.0;
}
@Override
public TunerChannelSource getChannel( TunerChannel channel )
throws RejectedExecutionException, SourceException
{
TunerChannelSource source = getController().getChannel( this, channel );
if( source != null )
{
broadcast( new TunerEvent( this, TunerEvent.Event.CHANNEL_COUNT ) );
}
return source;
}
@Override
public void releaseChannel( TunerChannelSource source )
{
/* Unregister for receiving samples */
removeListener( (Listener<ComplexBuffer>)source );
/* Tell the controller to release the channel and cleanup */
if( source != null )
{
getController().releaseChannel( source );
}
}
@Override
public String getUniqueID()
{
@@ -111,16 +76,4 @@ public class HackRFTuner extends Tuner
return BoardID.HACKRF_ONE.getLabel();
}
@Override
public void addListener( Listener<ComplexBuffer> listener )
{
getController().addListener( listener );
}
@Override
public void removeListener( Listener<ComplexBuffer> listener )
{
getController().removeListener( listener );
}
}

View File

@@ -69,6 +69,7 @@ import source.SourceException;
import source.tuner.TunerController;
import source.tuner.configuration.TunerConfiguration;
import source.tuner.usb.USBTransferProcessor;
import source.tuner.usb.USBTunerController;
import javax.usb.UsbException;
import java.nio.ByteBuffer;
@@ -76,7 +77,7 @@ import java.nio.ByteOrder;
import java.util.Arrays;
import java.util.concurrent.LinkedTransferQueue;
public class HackRFTunerController extends TunerController
public class HackRFTunerController extends USBTunerController
{
private final static Logger mLog = LoggerFactory.getLogger(HackRFTunerController.class);
@@ -160,6 +161,12 @@ public class HackRFTunerController extends TunerController
mUSBTransferProcessor = new USBTransferProcessor(name, mDeviceHandle, mSampleAdapter, USB_TRANSFER_BUFFER_SIZE);
}
@Override
protected USBTransferProcessor getUSBTransferProcessor()
{
return mUSBTransferProcessor;
}
/**
* Claims the USB interface. If another application currently has
* the interface claimed, the USB_FORCE_CLAIM_INTERFACE setting
@@ -874,23 +881,4 @@ public class HackRFTunerController extends TunerController
return sb.toString();
}
}
/**
* Adds a sample listener. If the buffer processing thread is
* not currently running, starts it running in a new thread.
*/
public void addListener(Listener<ComplexBuffer> listener)
{
mUSBTransferProcessor.addListener(listener);
}
/**
* Removes the sample listener. If this is the last registered listener,
* shuts down the buffer processing thread.
*/
public void removeListener(Listener<ComplexBuffer> listener)
{
mUSBTransferProcessor.removeListener(listener);
}
}

View File

@@ -0,0 +1,111 @@
/*******************************************************************************
* 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.
*
* 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 source.tuner.manager;
import sample.Broadcaster;
import sample.Listener;
import sample.complex.ComplexBuffer;
import sample.complex.IComplexBufferProvider;
import source.SourceEvent;
import source.tuner.channel.TunerChannel;
import source.tuner.channel.TunerChannelSource;
import java.util.List;
/**
* Interface to define the functionality of a source manager for handling tuner channel management, complex buffer
* listeners, and source event listeners.
*/
public abstract class AbstractSourceManager implements IComplexBufferProvider, Listener<SourceEvent>
{
protected Broadcaster<ComplexBuffer> mComplexBufferBroadcaster = new Broadcaster<>();
/**
* Indicates if this source manager can tune (ie provide a source for) the specified tuner channel
*/
public abstract boolean canTune(TunerChannel tunerChannel);
/**
* List of tuner channels being sourced by this source manager
*/
public abstract List<TunerChannel> getChannels();
/**
* Obtains a source for the tuner channel or returns null if the channel cannot be sourced by this tuner.
* Note: use the canTune() method to check if a tuner channel can be sourced/tuned prior to accessing this method.
*
* @param tunerChannel for requested source
* @return tuner channel source or null
*/
public abstract TunerChannelSource getSource(TunerChannel tunerChannel);
/**
* Releases the source and all related resources
*/
public abstract void releaseSource(TunerChannelSource source);
/**
* Adds the listener to receive source events from this source manager
*
* @param sourceEventListener to add
*/
public abstract void addSourceEventListener(Listener<SourceEvent> sourceEventListener);
/**
* Removes the listener from receiving source events from this source manager
* @param sourceEventListener to remove
*/
public abstract void removeSourceEventListener(Listener<SourceEvent> sourceEventListener);
/**
* Adds the listener to receive complex buffer samples
*/
@Override
public void addComplexBufferListener(Listener<ComplexBuffer> listener)
{
mComplexBufferBroadcaster.addListener(listener);
}
/**
* Removes the listener from receiving complex buffer samples
*/
@Override
public void removeComplexBufferListener(Listener<ComplexBuffer> listener)
{
mComplexBufferBroadcaster.removeListener(listener);
}
/**
* Indicates if there are any complex buffer listeners registered with this source manager
*/
@Override
public boolean hasComplexBufferListeners()
{
return mComplexBufferBroadcaster.hasListeners();
}
/**
* Broadcasts the buffer to any registered listeners
*/
protected void broadcast(ComplexBuffer complexBuffer)
{
mComplexBufferBroadcaster.broadcast(complexBuffer);
}
}

View File

@@ -0,0 +1,83 @@
/*******************************************************************************
* 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.
*
* 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 source.tuner.manager;
import dsp.filter.channelizer.PolyphaseChannelManager;
import sample.Listener;
import source.SourceEvent;
import source.tuner.channel.TunerChannel;
import source.tuner.channel.TunerChannelSource;
import source.tuner.TunerController;
import java.util.List;
public class TunerSourceManager extends AbstractSourceManager
{
private PolyphaseChannelManager mPolyphaseChannelManager;
private TunerController mTunerController;
public TunerSourceManager(TunerController tunerController)
{
mTunerController = tunerController;
}
//TODO: broadcast a tuner event each time we add a new channel to the polyphase manager
@Override
public boolean canTune(TunerChannel tunerChannel)
{
return false;
}
@Override
public List<TunerChannel> getChannels()
{
return null;
}
@Override
public TunerChannelSource getSource(TunerChannel tunerChannel)
{
return null;
}
@Override
public void releaseSource(TunerChannelSource source)
{
}
@Override
public void addSourceEventListener(Listener<SourceEvent> sourceEventListener)
{
}
@Override
public void removeSourceEventListener(Listener<SourceEvent> sourceEventListener)
{
}
@Override
public void receive(SourceEvent sourceEvent)
{
}
}

View File

@@ -1,161 +1,75 @@
/*******************************************************************************
* 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/>
******************************************************************************/
package source.tuner.rtl;
import java.util.concurrent.RejectedExecutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.usb4java.LibUsbException;
import sample.Listener;
import sample.complex.ComplexBuffer;
import source.SourceException;
import source.tuner.Tuner;
import source.tuner.TunerChannel;
import source.tuner.TunerChannelSource;
import source.tuner.TunerClass;
import source.tuner.TunerEvent;
import source.tuner.TunerType;
import source.tuner.rtl.RTL2832TunerController.SampleRate;
public class RTL2832Tuner extends Tuner
{
private final static Logger mLog =
LoggerFactory.getLogger( RTL2832Tuner.class );
private final static Logger mLog = LoggerFactory.getLogger(RTL2832Tuner.class);
private TunerClass mTunerClass;
private TunerClass mTunerClass;
public RTL2832Tuner( TunerClass tunerClass,
RTL2832TunerController controller )
throws SourceException
{
super( tunerClass.getVendorDeviceLabel() + "/" +
controller.getTunerType().getLabel() + " " +
controller.getUniqueID(), controller );
mTunerClass = tunerClass;
}
public void dispose()
{
//TODO: dispose of something here
}
public RTL2832TunerController getController()
{
return (RTL2832TunerController)getTunerController();
}
public RTL2832Tuner(TunerClass tunerClass, RTL2832TunerController controller) throws SourceException
{
super(tunerClass.getVendorDeviceLabel() + "/" + controller.getTunerType().getLabel() + " " +
controller.getUniqueID(), controller);
@Override
mTunerClass = tunerClass;
}
public void dispose()
{
}
public RTL2832TunerController getController()
{
return (RTL2832TunerController)getTunerController();
}
@Override
public String getUniqueID()
{
return getController().getUniqueID();
return getController().getUniqueID();
}
@Override
@Override
public TunerClass getTunerClass()
{
return mTunerClass;
return mTunerClass;
}
@Override
@Override
public TunerType getTunerType()
{
return getController().getTunerType();
return getController().getTunerType();
}
public void setSampleRate( SampleRate sampleRate ) throws SourceException
{
try
{
getController().setSampleRate( sampleRate );
}
catch( LibUsbException e )
{
throw new SourceException( "RTL2832 Tuner - error setting "
+ "sample rate", e );
}
}
@Override
public double getSampleSize()
{
//Note: although sample size is 8, we set it to 11 to align with the
//actual noise floor.
return 11.0;
}
public void setFrequency( int frequency ) throws SourceException
{
getController().setFrequency( frequency );
}
@Override
public TunerChannelSource getChannel( TunerChannel channel )
throws RejectedExecutionException,
SourceException
{
TunerChannelSource source = null;
try
{
source = getController().getChannel( this, channel );
if( source != null )
{
broadcast( new TunerEvent( this, TunerEvent.Event.CHANNEL_COUNT ) );
}
}
catch( Exception e )
{
mLog.error( "couldn't provide source channel", e );
}
return source;
}
/**
* Releases the tuned channel so that the tuner controller can tune to
* other frequencies as needed.
*/
@Override
public void releaseChannel( TunerChannelSource source )
@Override
public double getSampleSize()
{
/* Unregister for receiving samples */
removeListener( (Listener<ComplexBuffer>)source );
/* Tell the controller to release the channel and cleanup */
if( source != null )
{
getController().releaseChannel( source );
}
//Note: although sample size is 8, we set it to 11 to align with the
//actual noise floor.
return 11.0;
}
@Override
public void addListener( Listener<ComplexBuffer> listener )
{
getController().addListener( listener );
}
@Override
public void removeListener( Listener<ComplexBuffer> listener )
{
getController().removeListener( listener );
}
}

View File

@@ -38,6 +38,7 @@ import source.tuner.TunerController;
import source.tuner.TunerManager;
import source.tuner.TunerType;
import source.tuner.usb.USBTransferProcessor;
import source.tuner.usb.USBTunerController;
import javax.usb.UsbDisconnectedException;
import javax.usb.UsbException;
@@ -47,7 +48,7 @@ import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
public abstract class RTL2832TunerController extends TunerController
public abstract class RTL2832TunerController extends USBTunerController
{
private final static Logger mLog = LoggerFactory.getLogger(RTL2832TunerController.class);
@@ -163,6 +164,12 @@ public abstract class RTL2832TunerController extends TunerController
USB_TRANSFER_BUFFER_SIZE);
}
@Override
protected USBTransferProcessor getUSBTransferProcessor()
{
return mUSBTransferProcessor;
}
/**
* Claims the USB interface. Attempts to detach the active kernel driver
* if one is currently attached.
@@ -449,7 +456,7 @@ public abstract class RTL2832TunerController extends TunerController
{
if(mUSBTransferProcessor != null)
{
mUSBTransferProcessor.removeAllListeners();
mUSBTransferProcessor.removeListener();
TunerManager.LIBUSB_TRANSFER_PROCESSOR.unregisterTransferProcessor(mUSBTransferProcessor);
}
@@ -1490,20 +1497,33 @@ public abstract class RTL2832TunerController extends TunerController
}
/**
* Adds a sample listener. If the USB transfer processor is not currently running, it will auto-start
* Adds the IQ buffer listener and automatically starts buffer transfer processing, if not already started.
*/
public void addListener(Listener<ComplexBuffer> listener)
@Override
public void addComplexBufferListener(Listener<ComplexBuffer> listener)
{
mUSBTransferProcessor.addListener(listener);
boolean hasExistingListeners = hasComplexBufferListeners();
super.addComplexBufferListener(listener);
if(!hasExistingListeners)
{
mUSBTransferProcessor.setListener(this);
}
}
/**
* Removes the sample listener. If this is the last registered listener, the USB transfer processor will
* halt buffer processing
* Removes the IQ buffer listener and stops buffer transfer processing if there are no more listeners.
*/
public void removeListener(Listener<ComplexBuffer> listener)
@Override
public void removeComplexBufferListener(Listener<ComplexBuffer> listener)
{
mUSBTransferProcessor.removeListener(listener);
super.removeComplexBufferListener(listener);
if(!hasComplexBufferListeners())
{
mUSBTransferProcessor.removeListener();
}
}
/**

View File

@@ -18,21 +18,12 @@
******************************************************************************/
package source.tuner.test;
import dsp.filter.channelizer.PolyphaseChannelManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sample.Listener;
import sample.complex.ComplexBuffer;
import source.Source;
import source.SourceException;
import source.tuner.Tuner;
import source.tuner.TunerChannel;
import source.tuner.TunerChannelSource;
import source.tuner.TunerClass;
import source.tuner.TunerType;
import java.util.concurrent.RejectedExecutionException;
/**
* Testing tuner that implements an internal oscillator to output a unity gain tone at a specified frequency offset
* from a configurable center tune frequency and sample rate
@@ -43,13 +34,9 @@ public class TestTuner extends Tuner
private static int mInstanceCounter = 1;
private final int mInstanceID = mInstanceCounter++;
private PolyphaseChannelManager mPolyphaseChannelManager;
public TestTuner()
{
super("Test Tuner", new TestTunerController());
mPolyphaseChannelManager = new PolyphaseChannelManager(this);
}
/**
@@ -58,41 +45,11 @@ public class TestTuner extends Tuner
* Note: this is a temporary method until the tuner controller interface can be updated with the start(listener)
* and stop() methods.
*/
private TestTunerController getTestTunerController()
public TestTunerController getTunerController()
{
return (TestTunerController)getTunerController();
}
/**
* Sets the center tuned frequency for this tuner
* @param frequency
* @throws SourceException
*/
public void setFrequency(long frequency) throws SourceException
{
getTunerController().setFrequency(frequency);
}
public void setToneFrequency(long frequency)
{
getTestTunerController().setToneFrequency(frequency);
}
public long getToneFrequency()
{
return getTestTunerController().getToneFrequency();
}
public void setSampleRate(int sampleRate) throws SourceException
{
getTestTunerController().setSampleRate(sampleRate);
}
public int getSampleRate()
{
return getTestTunerController().getSampleRate();
}
@Override
public String getUniqueID()
{
@@ -116,62 +73,4 @@ public class TestTuner extends Tuner
{
return 16.0;
}
/**
* Creates a polyphase channel source. The provided channel is a one-use channel that can be commanded to start
* and stop processing via the source event interface. Once stopped, the channel is deregistered from this tuner
* and is no longer usable -- a new channel source must be requested from the tuner.
*
* @param tunerChannel identifying the channel center frequency and bandwidth
* @return channel source or null if the channel source cannot be provided by this tuner
*/
public Source getChannelSource(TunerChannel tunerChannel)
{
return mPolyphaseChannelManager.getChannel(tunerChannel);
}
@Override
public TunerChannelSource getChannel(TunerChannel channel) throws RejectedExecutionException, SourceException
{
mLog.error("This method is deprecated in this test tuner implementation");
throw new IllegalStateException("Method deprecated - this test tuner cannot provide a TunerChannelSource");
}
@Override
public void releaseChannel(TunerChannelSource source)
{
mLog.error("This method is deprecated in this test tuner implementation");
}
/**
* Adds the listener to receive complex sample buffers and auto-starts the sample stream if this is the first
* registered listener.
*/
@Override
public void addListener(Listener<ComplexBuffer> listener)
{
boolean isRunning = hasListeners();
super.addListener(listener);
if(!isRunning)
{
getTestTunerController().start(getSampleBroadcaster());
}
}
/**
* Removes the listener from receiving complex sample buffers and auto-stops the sample stream when there are
* no more listeners.
*/
@Override
public void removeListener(Listener<ComplexBuffer> listener)
{
super.removeListener(listener);
if(!hasListeners())
{
getTestTunerController().stop();
}
}
}

View File

@@ -70,18 +70,28 @@ public class TestTunerController extends TunerController
* Starts the tuner controller producing samples that will be delivered to the specified listener
*/
public void start(Listener<ComplexBuffer> listener)
{
}
@Override
public void addComplexBufferListener(Listener<ComplexBuffer> listener)
{
mSampleGenerator.setListener(listener);
mSampleGenerator.start();
}
@Override
public void removeComplexBufferListener(Listener<ComplexBuffer> listener)
{
mSampleGenerator.stop();
mSampleGenerator.setListener(null);
}
/**
* Stops the tuner controller sample generator and deregisters the listener
*/
public void stop()
{
mSampleGenerator.stop();
mSampleGenerator.setListener(null);
}
@Override

View File

@@ -70,10 +70,10 @@ public class USBTransferProcessor implements TransferCallback
//Byte array transfer buffers size in bytes
private int mBufferSize;
private Broadcaster<ComplexBuffer> mComplexBufferBroadcaster = new Broadcaster<>();
private OverflowableTransferQueue<byte[]> mFilledBuffers;
private LinkedTransferQueue<Transfer> mAvailableTransfers = new LinkedTransferQueue<>();
private LinkedTransferQueue<Transfer> mTransfersInProgress = new LinkedTransferQueue<>();
private Listener<ComplexBuffer> mComplexBufferListener;
private AtomicBoolean mRunning = new AtomicBoolean();
@@ -234,36 +234,26 @@ public class USBTransferProcessor implements TransferCallback
}
/**
* Indicates if there are complex buffer listeners registered with this processor
* Sets the listener and auto-starts the buffer processor
*/
public boolean hasListeners()
public void setListener(Listener<ComplexBuffer> listener)
{
return mComplexBufferBroadcaster.hasListeners();
}
public void addListener(Listener<ComplexBuffer> listener)
{
mComplexBufferBroadcaster.addListener(listener);
mComplexBufferListener = listener;
start();
}
public void removeListener(Listener<ComplexBuffer> listener)
/**
* Auto-stops the buffer processor and removes the listener
*/
public void removeListener()
{
mComplexBufferBroadcaster.removeListener(listener);
if(!hasListeners())
if(mComplexBufferListener != null)
{
stop();
mComplexBufferListener = null;
}
}
public void removeAllListeners()
{
mComplexBufferBroadcaster.clear();
stop();
}
/**
* Prepares (allocates) a set of transfer buffers for use in transferring data from the USB device via the bulk
* interface. Since we're using direct allocation (native), buffers are retained and reused across multiple
@@ -425,7 +415,10 @@ public class USBTransferProcessor implements TransferCallback
{
float[] complexSamples = mSampleAdapter.convert(buffer);
mComplexBufferBroadcaster.broadcast(new ComplexBuffer(complexSamples));
if(mComplexBufferListener != null)
{
mComplexBufferListener.receive(new ComplexBuffer(complexSamples));
}
}
}
catch(Exception e)

View File

@@ -0,0 +1,72 @@
/*******************************************************************************
* 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.
*
* 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 source.tuner.usb;
import sample.Listener;
import sample.complex.ComplexBuffer;
import source.tuner.TunerController;
public abstract class USBTunerController extends TunerController
{
/**
* USB tuner controller class. Provides auto-start and auto-stop function when complex buffer listeners are added
* or removed from this tuner controller.
*
* @param minimumFrequency minimum uncorrected tunable frequency
* @param maximumFrequency maximum uncorrected tunable frequency
* @param middleUnusable is the +/- value center DC spike to avoid for channels
* @param usableBandwidth percentage of usable bandwidth relative to space at the extreme ends of the spectrum
*/
public USBTunerController(long minimumFrequency, long maximumFrequency, int middleUnusable, double usableBandwidth)
{
super(minimumFrequency, maximumFrequency, middleUnusable, usableBandwidth);
}
protected abstract USBTransferProcessor getUSBTransferProcessor();
/**
* Adds the IQ buffer listener and automatically starts buffer transfer processing, if not already started.
*/
@Override
public void addComplexBufferListener(Listener<ComplexBuffer> listener)
{
boolean hasExistingListeners = hasComplexBufferListeners();
super.addComplexBufferListener(listener);
if(!hasExistingListeners)
{
getUSBTransferProcessor().setListener(this);
}
}
/**
* Removes the IQ buffer listener and stops buffer transfer processing if there are no more listeners.
*/
@Override
public void removeComplexBufferListener(Listener<ComplexBuffer> listener)
{
super.removeComplexBufferListener(listener);
if(!hasComplexBufferListeners())
{
getUSBTransferProcessor().removeListener();
}
}
}

View File

@@ -18,7 +18,7 @@
******************************************************************************/
package source.wave;
import channel.heartbeat.Heartbeat;
import source.heartbeat.Heartbeat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sample.ConversionUtils;

View File

@@ -17,7 +17,7 @@
******************************************************************************/
package source.wave;
import channel.heartbeat.Heartbeat;
import source.heartbeat.Heartbeat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sample.ConversionUtils;

View File

@@ -30,7 +30,7 @@ import settings.ColorSetting.ColorSettingName;
import settings.Setting;
import settings.SettingChangeListener;
import settings.SettingsManager;
import source.tuner.TunerChannel;
import source.tuner.channel.TunerChannel;
import source.SourceEvent;
import source.ISourceEventProcessor;

View File

@@ -424,7 +424,7 @@ public class SpectralDisplayPanel extends JPanel implements Listener<ComplexBuff
mTuner.getTunerController().addListener(this);
//Register the dft processor to receive samples from the tuner
mTuner.addListener((Listener<ComplexBuffer>)mDFTProcessor);
mTuner.getSourceManager().addComplexBufferListener(mDFTProcessor);
mSpectrumPanel.setSampleSize(mTuner.getSampleSize());