From 57fab2b7cade444b278debe30e3067db38f95a3d Mon Sep 17 00:00:00 2001 From: Denny Date: Sun, 3 Dec 2017 11:10:50 -0500 Subject: [PATCH] #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. --- src/audio/AudioModule.java | 2 +- .../state/AlwaysUnsquelchedDecoderState.java | 12 +- src/channel/state/ChannelState.java | 6 +- src/channel/state/DecoderState.java | 15 +- .../traffic/TrafficChannelManager.java | 4 +- src/controller/channel/Channel.java | 2 +- .../filter/channelizer/ChannelCalculator.java | 2 +- .../filter/channelizer/ChannelizerViewer.java | 31 +- .../channelizer/PolyphaseChannelManager.java | 33 +- .../channelizer/PolyphaseChannelSource.java | 6 +- src/module/Module.java | 4 +- src/module/ProcessingChain.java | 12 +- src/module/decode/Decoder.java | 49 +- .../decode/fleetsync2/Fleetsync2Decoder.java | 15 - .../fleetsync2/Fleetsync2DecoderState.java | 15 - src/module/decode/lj1200/LJ1200Decoder.java | 15 - .../decode/lj1200/LJ1200DecoderState.java | 16 - src/module/decode/ltrnet/LTRNetDecoder.java | 10 - .../decode/ltrnet/LTRNetDecoderState.java | 16 - .../ltrstandard/LTRStandardDecoder.java | 10 - .../ltrstandard/LTRStandardDecoderState.java | 17 - src/module/decode/mdc1200/MDCDecoder.java | 21 - .../decode/mdc1200/MDCDecoderState.java | 16 - src/module/decode/mpt1327/MPT1327Decoder.java | 12 - .../decode/mpt1327/MPT1327DecoderState.java | 16 - src/module/decode/p25/P25DecoderState.java | 15 +- src/module/decode/p25/P25_C4FMDecoder.java | 21 - src/module/decode/p25/P25_LSMDecoder.java | 21 - .../decode/p25/audio/P25AudioModule.java | 2 +- .../decode/passport/PassportDecoder.java | 10 - .../decode/passport/PassportDecoderState.java | 15 - src/module/decode/tait/Tait1200Decoder.java | 21 - .../decode/tait/Tait1200DecoderState.java | 16 - .../demodulate/am/AMDemodulatorModule.java | 164 +-- .../audio/DemodulatedAudioFilterModule.java | 2 +- .../demodulate/fm/FMDemodulatorModule.java | 2 +- src/module/log/EventLogger.java | 2 +- src/record/AudioRecorder.java | 5 +- .../wave/ComplexBufferWaveRecorder.java | 351 +++-- src/record/wave/RealBufferWaveRecorder.java | 5 +- src/sample/OverflowableTransferQueue.java | 1 - .../complex/IComplexBufferProvider.java | 39 + src/source/Source.java | 14 +- src/source/SourceEvent.java | 10 + src/source/config/SourceConfigRecording.java | 2 +- src/source/config/SourceConfigTuner.java | 2 +- .../heartbeat/Heartbeat.java | 2 +- src/source/heartbeat/HeartbeatManager.java | 58 + .../heartbeat/IHeartbeatListener.java | 2 +- .../heartbeat/IHeartbeatProvider.java | 8 +- src/source/mixer/RealMixerSource.java | 3 +- src/source/recording/Recording.java | 4 +- .../recording/RecordingSourceManager.java | 4 +- src/source/tuner/ITunerChannelProvider.java | 4 +- src/source/tuner/MixerTuner.java | 131 -- src/source/tuner/Tuner.java | 151 +- src/source/tuner/TunerChannelProvider.java | 27 - src/source/tuner/TunerController.java | 60 +- src/source/tuner/TunerManager.java | 27 +- src/source/tuner/TunerModel.java | 508 ++++--- src/source/tuner/airspy/AirspyTuner.java | 54 - .../tuner/airspy/AirspyTunerController.java | 53 +- .../DecimatingTunerChannelSource.java} | 116 +- .../tuner/{ => channel}/TunerChannel.java | 2 +- .../tuner/channel/TunerChannelProvider.java | 45 + .../tuner/channel/TunerChannelSource.java | 105 ++ src/source/tuner/ettus/B100Tuner.java | 83 -- .../tuner/ettus/B100TunerEditorPanel.java | 44 - src/source/tuner/fcd/FCDTuner.java | 56 +- src/source/tuner/fcd/FCDTunerController.java | 53 +- .../tuner/fcd/proV1/FCD1TunerController.java | 1268 ++++++++--------- .../fcd/proplusV2/FCD2TunerController.java | 8 +- .../frequency/DCTrackingSourceControl.java | 2 +- .../tuner/frequency/FrequencyController.java | 15 + src/source/tuner/hackrf/HackRFTuner.java | 47 - .../tuner/hackrf/HackRFTunerController.java | 28 +- .../tuner/manager/AbstractSourceManager.java | 111 ++ .../tuner/manager/TunerSourceManager.java | 83 ++ src/source/tuner/rtl/RTL2832Tuner.java | 150 +- .../tuner/rtl/RTL2832TunerController.java | 38 +- src/source/tuner/test/TestTuner.java | 103 +- .../tuner/test/TestTunerController.java | 14 +- .../tuner/usb/USBTransferProcessor.java | 35 +- src/source/tuner/usb/USBTunerController.java | 72 + src/source/wave/ComplexWaveSource.java | 2 +- src/source/wave/RealWaveSource.java | 2 +- src/spectrum/OverlayPanel.java | 2 +- src/spectrum/SpectralDisplayPanel.java | 2 +- 88 files changed, 2112 insertions(+), 2547 deletions(-) create mode 100644 src/sample/complex/IComplexBufferProvider.java rename src/{channel => source}/heartbeat/Heartbeat.java (97%) create mode 100644 src/source/heartbeat/HeartbeatManager.java rename src/{channel => source}/heartbeat/IHeartbeatListener.java (97%) rename src/{channel => source}/heartbeat/IHeartbeatProvider.java (84%) delete mode 100644 src/source/tuner/MixerTuner.java delete mode 100644 src/source/tuner/TunerChannelProvider.java rename src/source/tuner/{TunerChannelSource.java => channel/DecimatingTunerChannelSource.java} (89%) rename src/source/tuner/{ => channel}/TunerChannel.java (98%) create mode 100644 src/source/tuner/channel/TunerChannelProvider.java create mode 100644 src/source/tuner/channel/TunerChannelSource.java delete mode 100644 src/source/tuner/ettus/B100Tuner.java delete mode 100644 src/source/tuner/ettus/B100TunerEditorPanel.java create mode 100644 src/source/tuner/manager/AbstractSourceManager.java create mode 100644 src/source/tuner/manager/TunerSourceManager.java create mode 100644 src/source/tuner/usb/USBTunerController.java diff --git a/src/audio/AudioModule.java b/src/audio/AudioModule.java index 58b0be0f..f1cdf38f 100644 --- a/src/audio/AudioModule.java +++ b/src/audio/AudioModule.java @@ -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 */ } diff --git a/src/channel/state/AlwaysUnsquelchedDecoderState.java b/src/channel/state/AlwaysUnsquelchedDecoderState.java index ef6a0db0..b2a67cc9 100644 --- a/src/channel/state/AlwaysUnsquelchedDecoderState.java +++ b/src/channel/state/AlwaysUnsquelchedDecoderState.java @@ -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(Attribute.PRIMARY_ADDRESS_TO, mChannelName)); broadcast(new DecoderStateEvent(this, Event.ALWAYS_UNSQUELCH, State.IDLE)); } - - @Override - public void stop() - { - } } diff --git a/src/channel/state/ChannelState.java b/src/channel/state/ChannelState.java index 01fc6175..3bb4d00b 100644 --- a/src/channel/state/ChannelState.java +++ b/src/channel/state/ChannelState.java @@ -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(Attribute.CHANNEL_STATE, mState)); diff --git a/src/channel/state/DecoderState.java b/src/channel/state/DecoderState.java index 4e68bb8f..707f0241 100644 --- a/src/channel/state/DecoderState.java +++ b/src/channel/state/DecoderState.java @@ -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 diff --git a/src/channel/traffic/TrafficChannelManager.java b/src/channel/traffic/TrafficChannelManager.java index d7fac5a2..f01bc492 100644 --- a/src/channel/traffic/TrafficChannelManager.java +++ b/src/channel/traffic/TrafficChannelManager.java @@ -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() { } diff --git a/src/controller/channel/Channel.java b/src/controller/channel/Channel.java index b3e49224..c099b517 100644 --- a/src/controller/channel/Channel.java +++ b/src/controller/channel/Channel.java @@ -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; diff --git a/src/dsp/filter/channelizer/ChannelCalculator.java b/src/dsp/filter/channelizer/ChannelCalculator.java index bf98e1ea..0f80483b 100644 --- a/src/dsp/filter/channelizer/ChannelCalculator.java +++ b/src/dsp/filter/channelizer/ChannelCalculator.java @@ -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; diff --git a/src/dsp/filter/channelizer/ChannelizerViewer.java b/src/dsp/filter/channelizer/ChannelizerViewer.java index 8afc76b6..91db8580 100644 --- a/src/dsp/filter/channelizer/ChannelizerViewer.java +++ b/src/dsp/filter/channelizer/ChannelizerViewer.java @@ -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, 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; } diff --git a/src/dsp/filter/channelizer/PolyphaseChannelManager.java b/src/dsp/filter/channelizer/PolyphaseChannelManager.java index b82b212d..aa95a3c3 100644 --- a/src/dsp/filter/channelizer/PolyphaseChannelManager.java +++ b/src/dsp/filter/channelizer/PolyphaseChannelManager.java @@ -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 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 mChannelSources = new CopyOnWriteArrayList<>(); private ChannelCalculator mChannelCalculator; private ComplexPolyphaseChannelizerM2 mPolyphaseChannelizer; @@ -57,27 +57,29 @@ public class PolyphaseChannelManager implements Listener * 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 mBufferProcessor = new ScheduledBufferProcessor(500, 100, 50, 50); updateChannelizer(sampleRate, null); - - mTuner.addSourceEventListener(this); - } /** @@ -159,7 +158,7 @@ public class PolyphaseChannelManager implements Listener if(mPolyphaseChannelizer.getRegisteredChannelCount() == 1) { - mTuner.addListener(mBufferProcessor); + mComplexBufferProvider.addComplexBufferListener(mBufferProcessor); mBufferProcessor.start(); } } @@ -179,7 +178,7 @@ public class PolyphaseChannelManager implements Listener if(mPolyphaseChannelizer.getRegisteredChannelCount() == 0) { - mTuner.removeListener(mBufferProcessor); + mComplexBufferProvider.removeComplexBufferListener(mBufferProcessor); mBufferProcessor.stop(); } } diff --git a/src/dsp/filter/channelizer/PolyphaseChannelSource.java b/src/dsp/filter/channelizer/PolyphaseChannelSource.java index 89a7e0df..20e789fb 100644 --- a/src/dsp/filter/channelizer/PolyphaseChannelSource.java +++ b/src/dsp/filter/channelizer/PolyphaseChannelSource.java @@ -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; diff --git a/src/module/Module.java b/src/module/Module.java index a46b3e9c..e7d1fcc4 100644 --- a/src/module/Module.java +++ b/src/module/Module.java @@ -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 diff --git a/src/module/ProcessingChain.java b/src/module/ProcessingChain.java index 6736aece..516d71c8 100644 --- a/src/module/ProcessingChain.java +++ b/src/module/ProcessingChain.java @@ -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) { diff --git a/src/module/decode/Decoder.java b/src/module/decode/Decoder.java index cc6880a7..3a029035 100644 --- a/src/module/decode/Decoder.java +++ b/src/module/decode/Decoder.java @@ -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 + * 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 + * ******************************************************************************/ 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 +public abstract class Decoder extends Module implements IMessageProvider, Listener { /* This has to be a broadcaster in order for references to persist */ protected Listener 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() + { + } } diff --git a/src/module/decode/fleetsync2/Fleetsync2Decoder.java b/src/module/decode/fleetsync2/Fleetsync2Decoder.java index 5b18628b..c27d4a97 100644 --- a/src/module/decode/fleetsync2/Fleetsync2Decoder.java +++ b/src/module/decode/fleetsync2/Fleetsync2Decoder.java @@ -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() - { - } } diff --git a/src/module/decode/fleetsync2/Fleetsync2DecoderState.java b/src/module/decode/fleetsync2/Fleetsync2DecoderState.java index e3aa69ec..d72346e1 100644 --- a/src/module/decode/fleetsync2/Fleetsync2DecoderState.java +++ b/src/module/decode/fleetsync2/Fleetsync2DecoderState.java @@ -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 */ diff --git a/src/module/decode/lj1200/LJ1200Decoder.java b/src/module/decode/lj1200/LJ1200Decoder.java index 3e12b544..2e936984 100644 --- a/src/module/decode/lj1200/LJ1200Decoder.java +++ b/src/module/decode/lj1200/LJ1200Decoder.java @@ -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() - { - } } diff --git a/src/module/decode/lj1200/LJ1200DecoderState.java b/src/module/decode/lj1200/LJ1200DecoderState.java index 5382f034..a5cac1b8 100644 --- a/src/module/decode/lj1200/LJ1200DecoderState.java +++ b/src/module/decode/lj1200/LJ1200DecoderState.java @@ -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) { diff --git a/src/module/decode/ltrnet/LTRNetDecoder.java b/src/module/decode/ltrnet/LTRNetDecoder.java index 5e1eecff..bf1fbcd4 100644 --- a/src/module/decode/ltrnet/LTRNetDecoder.java +++ b/src/module/decode/ltrnet/LTRNetDecoder.java @@ -120,14 +120,4 @@ public class LTRNetDecoder extends Decoder { mLTRMessageFramer.reset(); } - - @Override - public void start( ScheduledExecutorService executor ) - { - } - - @Override - public void stop() - { - } } diff --git a/src/module/decode/ltrnet/LTRNetDecoderState.java b/src/module/decode/ltrnet/LTRNetDecoderState.java index e643b558..544302bc 100644 --- a/src/module/decode/ltrnet/LTRNetDecoderState.java +++ b/src/module/decode/ltrnet/LTRNetDecoderState.java @@ -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() - { - } } diff --git a/src/module/decode/ltrstandard/LTRStandardDecoder.java b/src/module/decode/ltrstandard/LTRStandardDecoder.java index 06f0a927..40b355c5 100644 --- a/src/module/decode/ltrstandard/LTRStandardDecoder.java +++ b/src/module/decode/ltrstandard/LTRStandardDecoder.java @@ -84,14 +84,4 @@ public class LTRStandardDecoder extends Decoder implements IUnFilteredRealBuffer { mLTRMessageFramer.reset(); } - - @Override - public void start( ScheduledExecutorService executor ) - { - } - - @Override - public void stop() - { - } } diff --git a/src/module/decode/ltrstandard/LTRStandardDecoderState.java b/src/module/decode/ltrstandard/LTRStandardDecoderState.java index a826ceeb..1693b2b6 100644 --- a/src/module/decode/ltrstandard/LTRStandardDecoderState.java +++ b/src/module/decode/ltrstandard/LTRStandardDecoderState.java @@ -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) { diff --git a/src/module/decode/mdc1200/MDCDecoder.java b/src/module/decode/mdc1200/MDCDecoder.java index 89f04afc..2bd89fba 100644 --- a/src/module/decode/mdc1200/MDCDecoder.java +++ b/src/module/decode/mdc1200/MDCDecoder.java @@ -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 - - } } diff --git a/src/module/decode/mdc1200/MDCDecoderState.java b/src/module/decode/mdc1200/MDCDecoderState.java index eecd0de9..a70c8f0f 100644 --- a/src/module/decode/mdc1200/MDCDecoderState.java +++ b/src/module/decode/mdc1200/MDCDecoderState.java @@ -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 */ diff --git a/src/module/decode/mpt1327/MPT1327Decoder.java b/src/module/decode/mpt1327/MPT1327Decoder.java index 15163f11..228f8dc9 100644 --- a/src/module/decode/mpt1327/MPT1327Decoder.java +++ b/src/module/decode/mpt1327/MPT1327Decoder.java @@ -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 - } } diff --git a/src/module/decode/mpt1327/MPT1327DecoderState.java b/src/module/decode/mpt1327/MPT1327DecoderState.java index a70b96fa..940e1496 100644 --- a/src/module/decode/mpt1327/MPT1327DecoderState.java +++ b/src/module/decode/mpt1327/MPT1327DecoderState.java @@ -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) { diff --git a/src/module/decode/p25/P25DecoderState.java b/src/module/decode/p25/P25DecoderState.java index 5072bc31..2a84255b 100644 --- a/src/module/decode/p25/P25DecoderState.java +++ b/src/module/decode/p25/P25DecoderState.java @@ -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) diff --git a/src/module/decode/p25/P25_C4FMDecoder.java b/src/module/decode/p25/P25_C4FMDecoder.java index cbae323d..3dc771fc 100644 --- a/src/module/decode/p25/P25_C4FMDecoder.java +++ b/src/module/decode/p25/P25_C4FMDecoder.java @@ -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 listener ) { diff --git a/src/module/decode/p25/P25_LSMDecoder.java b/src/module/decode/p25/P25_LSMDecoder.java index 3e30e9ae..d461cfd0 100644 --- a/src/module/decode/p25/P25_LSMDecoder.java +++ b/src/module/decode/p25/P25_LSMDecoder.java @@ -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 - - } } diff --git a/src/module/decode/p25/audio/P25AudioModule.java b/src/module/decode/p25/audio/P25AudioModule.java index d6aa4c9f..c49dd2f4 100644 --- a/src/module/decode/p25/audio/P25AudioModule.java +++ b/src/module/decode/p25/audio/P25AudioModule.java @@ -71,7 +71,7 @@ public class P25AudioModule extends Module implements Listener, IAudioP } @Override - public void start(ScheduledExecutorService executor) + public void start() { } diff --git a/src/module/decode/passport/PassportDecoder.java b/src/module/decode/passport/PassportDecoder.java index 859e7c9d..1c298888 100644 --- a/src/module/decode/passport/PassportDecoder.java +++ b/src/module/decode/passport/PassportDecoder.java @@ -109,14 +109,4 @@ public class PassportDecoder extends Decoder { mPassportMessageFramer.reset(); } - - @Override - public void start( ScheduledExecutorService executor ) - { - } - - @Override - public void stop() - { - } } diff --git a/src/module/decode/passport/PassportDecoderState.java b/src/module/decode/passport/PassportDecoderState.java index ee5370e7..c98ef2c6 100644 --- a/src/module/decode/passport/PassportDecoderState.java +++ b/src/module/decode/passport/PassportDecoderState.java @@ -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)) diff --git a/src/module/decode/tait/Tait1200Decoder.java b/src/module/decode/tait/Tait1200Decoder.java index 97b9b788..917a2192 100644 --- a/src/module/decode/tait/Tait1200Decoder.java +++ b/src/module/decode/tait/Tait1200Decoder.java @@ -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 - - } } diff --git a/src/module/decode/tait/Tait1200DecoderState.java b/src/module/decode/tait/Tait1200DecoderState.java index b18ccb24..d8750b52 100644 --- a/src/module/decode/tait/Tait1200DecoderState.java +++ b/src/module/decode/tait/Tait1200DecoderState.java @@ -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 */ diff --git a/src/module/demodulate/am/AMDemodulatorModule.java b/src/module/demodulate/am/AMDemodulatorModule.java index 374491aa..9c9f1ace 100644 --- a/src/module/demodulate/am/AMDemodulatorModule.java +++ b/src/module/demodulate/am/AMDemodulatorModule.java @@ -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 ******************************************************************************/ 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 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 getComplexBufferListener() + { + return mIQFilter; + } - @Override - public void setFilteredRealBufferListener( Listener 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 listener) + { + mAGC.setListener(listener); + } + + @Override + public void removeFilteredRealBufferListener() + { + mAGC.removeListener(); + } + + @Override + public void start() + { + } + + @Override + public void stop() + { + } } diff --git a/src/module/demodulate/audio/DemodulatedAudioFilterModule.java b/src/module/demodulate/audio/DemodulatedAudioFilterModule.java index a28ff3fd..fa73f27d 100644 --- a/src/module/demodulate/audio/DemodulatedAudioFilterModule.java +++ b/src/module/demodulate/audio/DemodulatedAudioFilterModule.java @@ -97,7 +97,7 @@ public class DemodulatedAudioFilterModule extends Module implements IUnFilteredR } @Override - public void start(ScheduledExecutorService executor) + public void start() { } diff --git a/src/module/demodulate/fm/FMDemodulatorModule.java b/src/module/demodulate/fm/FMDemodulatorModule.java index 8b7646de..d3278e68 100644 --- a/src/module/demodulate/fm/FMDemodulatorModule.java +++ b/src/module/demodulate/fm/FMDemodulatorModule.java @@ -104,7 +104,7 @@ public class FMDemodulatorModule extends Module implements IComplexBufferListene } @Override - public void start(ScheduledExecutorService executor) + public void start() { } diff --git a/src/module/log/EventLogger.java b/src/module/log/EventLogger.java index b6d800f5..dcd90caa 100644 --- a/src/module/log/EventLogger.java +++ b/src/module/log/EventLogger.java @@ -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) { diff --git a/src/record/AudioRecorder.java b/src/record/AudioRecorder.java index 15723eba..3c1b7f64 100644 --- a/src/record/AudioRecorder.java +++ b/src/record/AudioRecorder.java @@ -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 ******************************************************************************/ 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 +public class ComplexBufferWaveRecorder extends Module + implements IComplexBufferListener, Listener { - 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 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 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 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 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]); + } + } } diff --git a/src/record/wave/RealBufferWaveRecorder.java b/src/record/wave/RealBufferWaveRecorder.java index 763c9ded..d006fe4a 100644 --- a/src/record/wave/RealBufferWaveRecorder.java +++ b/src/record/wave/RealBufferWaveRecorder.java @@ -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) { diff --git a/src/sample/OverflowableTransferQueue.java b/src/sample/OverflowableTransferQueue.java index f37314a4..193ef811 100644 --- a/src/sample/OverflowableTransferQueue.java +++ b/src/sample/OverflowableTransferQueue.java @@ -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; diff --git a/src/sample/complex/IComplexBufferProvider.java b/src/sample/complex/IComplexBufferProvider.java new file mode 100644 index 00000000..480cede0 --- /dev/null +++ b/src/sample/complex/IComplexBufferProvider.java @@ -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 + * + ******************************************************************************/ +package sample.complex; + +import sample.Listener; + +public interface IComplexBufferProvider +{ + /** + * Adds the listener to receive complex buffer samples + */ + void addComplexBufferListener(Listener listener); + + /** + * Removes the listener from receiving complex buffer samples + */ + void removeComplexBufferListener(Listener listener); + + /** + * Indicates if there are any complex buffer listeners registered + */ + boolean hasComplexBufferListeners(); +} diff --git a/src/source/Source.java b/src/source/Source.java index 1518279d..9304ddbe 100644 --- a/src/source/Source.java +++ b/src/source/Source.java @@ -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; /** diff --git a/src/source/SourceEvent.java b/src/source/SourceEvent.java index 5a9dd3b7..074eee9a 100644 --- a/src/source/SourceEvent.java +++ b/src/source/SourceEvent.java @@ -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); + } } diff --git a/src/source/config/SourceConfigRecording.java b/src/source/config/SourceConfigRecording.java index f1b7c1c3..d92b4fe5 100644 --- a/src/source/config/SourceConfigRecording.java +++ b/src/source/config/SourceConfigRecording.java @@ -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; diff --git a/src/source/config/SourceConfigTuner.java b/src/source/config/SourceConfigTuner.java index 2f31457d..8fe54732 100644 --- a/src/source/config/SourceConfigTuner.java +++ b/src/source/config/SourceConfigTuner.java @@ -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; diff --git a/src/channel/heartbeat/Heartbeat.java b/src/source/heartbeat/Heartbeat.java similarity index 97% rename from src/channel/heartbeat/Heartbeat.java rename to src/source/heartbeat/Heartbeat.java index 4799754e..993d51ee 100644 --- a/src/channel/heartbeat/Heartbeat.java +++ b/src/source/heartbeat/Heartbeat.java @@ -16,7 +16,7 @@ * along with this program. If not, see * ******************************************************************************/ -package channel.heartbeat; +package source.heartbeat; public class Heartbeat { diff --git a/src/source/heartbeat/HeartbeatManager.java b/src/source/heartbeat/HeartbeatManager.java new file mode 100644 index 00000000..d35188e3 --- /dev/null +++ b/src/source/heartbeat/HeartbeatManager.java @@ -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 + * + ******************************************************************************/ +package source.heartbeat; + +import sample.Broadcaster; +import sample.Listener; + +public class HeartbeatManager +{ + private static final Heartbeat HEARTBEAT = new Heartbeat(); + private Broadcaster 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 listener) + { + mHeartbeatBroadcaster.addListener(listener); + } + + /** + * Removes the listener from receiving heartbeats + * @param listener + */ + public void removeHeartbeatListener(Listener listener) + { + mHeartbeatBroadcaster.removeListener(listener); + } + +} diff --git a/src/channel/heartbeat/IHeartbeatListener.java b/src/source/heartbeat/IHeartbeatListener.java similarity index 97% rename from src/channel/heartbeat/IHeartbeatListener.java rename to src/source/heartbeat/IHeartbeatListener.java index 904d0501..296860a7 100644 --- a/src/channel/heartbeat/IHeartbeatListener.java +++ b/src/source/heartbeat/IHeartbeatListener.java @@ -16,7 +16,7 @@ * along with this program. If not, see * ******************************************************************************/ -package channel.heartbeat; +package source.heartbeat; import sample.Listener; diff --git a/src/channel/heartbeat/IHeartbeatProvider.java b/src/source/heartbeat/IHeartbeatProvider.java similarity index 84% rename from src/channel/heartbeat/IHeartbeatProvider.java rename to src/source/heartbeat/IHeartbeatProvider.java index 3583cd42..9941b716 100644 --- a/src/channel/heartbeat/IHeartbeatProvider.java +++ b/src/source/heartbeat/IHeartbeatProvider.java @@ -16,13 +16,9 @@ * along with this program. If not, see * ******************************************************************************/ -package channel.heartbeat; - -import sample.Listener; +package source.heartbeat; public interface IHeartbeatProvider { - public void setHeartbeatListener(Listener listener); - - public void removeHeartbeatListener(); + public HeartbeatManager getHeartbeatManager(); } diff --git a/src/source/mixer/RealMixerSource.java b/src/source/mixer/RealMixerSource.java index 408f04ea..e8558429 100644 --- a/src/source/mixer/RealMixerSource.java +++ b/src/source/mixer/RealMixerSource.java @@ -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; diff --git a/src/source/recording/Recording.java b/src/source/recording/Recording.java index a89f77eb..938dc6a6 100644 --- a/src/source/recording/Recording.java +++ b/src/source/recording/Recording.java @@ -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; diff --git a/src/source/recording/RecordingSourceManager.java b/src/source/recording/RecordingSourceManager.java index 4a47c660..d5fb4a69 100644 --- a/src/source/recording/RecordingSourceManager.java +++ b/src/source/recording/RecordingSourceManager.java @@ -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 { diff --git a/src/source/tuner/ITunerChannelProvider.java b/src/source/tuner/ITunerChannelProvider.java index b3a69a63..17558dbc 100644 --- a/src/source/tuner/ITunerChannelProvider.java +++ b/src/source/tuner/ITunerChannelProvider.java @@ -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; /** diff --git a/src/source/tuner/MixerTuner.java b/src/source/tuner/MixerTuner.java deleted file mode 100644 index c873aa8c..00000000 --- a/src/source/tuner/MixerTuner.java +++ /dev/null @@ -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 - ******************************************************************************/ -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 -{ - private final static Logger mLog = - LoggerFactory.getLogger( MixerTuner.class ); - - protected MixerTunerType mMixerTunerType; - protected ComplexMixer mComplexMixer; - protected ArrayList mListeners = - new ArrayList(); - - /** - * 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)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 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 listener ) - { - super.removeListener( listener ); - - if( !hasListeners() ) - { - mComplexMixer.stop(); - } - } -} diff --git a/src/source/tuner/Tuner.java b/src/source/tuner/Tuner.java index aac3297e..13370fb3 100644 --- a/src/source/tuner/Tuner.java +++ b/src/source/tuner/Tuner.java @@ -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 mTunerEventBroadcaster = new Broadcaster<>(); private TunerController mTunerController; - private Broadcaster mSampleBroadcaster = new Broadcaster<>(); - protected List> mTunerChangeListeners = new ArrayList<>(); - protected Broadcaster 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 listener) - { - mSampleBroadcaster.addListener(listener); - } - - /** - * Removes the registered listener - */ - public void removeListener(Listener 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 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 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 listener) { - mTunerChangeListeners.add(listener); + mTunerEventBroadcaster.addListener(listener); } /** @@ -223,7 +156,7 @@ public abstract class Tuner implements ITunerChannelProvider */ public void removeTunerChangeListener(Listener listener) { - mTunerChangeListeners.remove(listener); + mTunerEventBroadcaster.removeListener(listener); } /** @@ -231,36 +164,6 @@ public abstract class Tuner implements ITunerChannelProvider */ public void broadcast(TunerEvent tunerEvent) { - for(Listener 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 listener) - { - mSourceEventBroadcaster.addListener(listener); - } - - /** - * Removes the listener from receiving source events - */ - public void removeSourceEventListener(Listener listener) - { - mSourceEventBroadcaster.removeListener(listener); + mTunerEventBroadcaster.broadcast(tunerEvent); } } diff --git a/src/source/tuner/TunerChannelProvider.java b/src/source/tuner/TunerChannelProvider.java deleted file mode 100644 index 8f53f144..00000000 --- a/src/source/tuner/TunerChannelProvider.java +++ /dev/null @@ -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 ); - - -} diff --git a/src/source/tuner/TunerController.java b/src/source/tuner/TunerController.java index 8fd2cc54..50c4a98c 100644 --- a/src/source/tuner/TunerController.java +++ b/src/source/tuner/TunerController.java @@ -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 { private final static Logger mLog = LoggerFactory.getLogger(TunerController.class); + protected Broadcaster mSampleBroadcaster = new Broadcaster<>(); /* List of currently tuned channels being served to demod channels */ private SortedSet 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 listener) + { + mSampleBroadcaster.addListener(listener); + } + + /** + * Removes the listener from receiving complex buffer samples + */ + @Override + public void removeComplexBufferListener(Listener 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 interface to receive and distribute complex buffers from subclass implementations + */ + @Override + public void receive(ComplexBuffer complexBuffer) + { + broadcast(complexBuffer); + } } diff --git a/src/source/tuner/TunerManager.java b/src/source/tuner/TunerManager.java index b5181c26..12d228aa 100644 --- a/src/source/tuner/TunerManager.java +++ b/src/source/tuner/TunerManager.java @@ -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 { diff --git a/src/source/tuner/TunerModel.java b/src/source/tuner/TunerModel.java index 09a74804..9680d732 100644 --- a/src/source/tuner/TunerModel.java +++ b/src/source/tuner/TunerModel.java @@ -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 { - 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 mTuners = new ArrayList<>(); - private List> 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 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 Tuners ) - { - for( Tuner tuner: Tuners ) - { - addTuner( tuner ); - } - } + private List mTuners = new ArrayList<>(); + private List> 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 listener ) - { - mTunerEventListeners.add( listener ); - } + public TunerModel(TunerConfigurationModel tunerConfigurationModel) + { + mTunerConfigurationModel = tunerConfigurationModel; + } - public void removeListener( Listener listener ) - { - mTunerEventListeners.remove( listener ); - } + public TunerConfigurationModel getTunerConfigurationModel() + { + return mTunerConfigurationModel; + } - public void broadcast( TunerEvent event ) - { - for( Listener listener: mTunerEventListeners ) - { - listener.receive( event ); - } - } + /** + * List of Tuners currently in the model + */ + public List 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 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 listener) + { + mTunerEventListeners.add(listener); + } + + public void removeListener(Listener listener) + { + mTunerEventListeners.remove(listener); + } + + public void broadcast(TunerEvent event) + { + for(Listener 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 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 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; } - + } diff --git a/src/source/tuner/airspy/AirspyTuner.java b/src/source/tuner/airspy/AirspyTuner.java index 48c06d3b..649a4eae 100644 --- a/src/source/tuner/airspy/AirspyTuner.java +++ b/src/source/tuner/airspy/AirspyTuner.java @@ -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)source ); - - /* Tell the controller to release the channel and cleanup */ - if( source != null ) - { - getController().releaseChannel( source ); - } - } - - @Override - public void addListener( Listener listener ) - { - getController().addListener( listener ); - } - - @Override - public void removeListener( Listener listener ) - { - getController().removeListener( listener ); - } } diff --git a/src/source/tuner/airspy/AirspyTunerController.java b/src/source/tuner/airspy/AirspyTunerController.java index f8362bf6..490cd231 100644 --- a/src/source/tuner/airspy/AirspyTunerController.java +++ b/src/source/tuner/airspy/AirspyTunerController.java @@ -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 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 listener) - { - if(mUSBTransferProcessor != null) - { - mUSBTransferProcessor.removeListener(listener); - } - else - { - mLog.error("Couldn't remove IQ buffer listener from Airspy tuner - processor is null"); - } - } } diff --git a/src/source/tuner/TunerChannelSource.java b/src/source/tuner/channel/DecimatingTunerChannelSource.java similarity index 89% rename from src/source/tuner/TunerChannelSource.java rename to src/source/tuner/channel/DecimatingTunerChannelSource.java index a41b22de..134601b1 100644 --- a/src/source/tuner/TunerChannelSource.java +++ b/src/source/tuner/channel/DecimatingTunerChannelSource.java @@ -16,12 +16,10 @@ * along with this program. If not, see * ******************************************************************************/ -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 +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 mBuffer; - private Tuner mTuner; private TunerChannel mTunerChannel; private Oscillator mMixer; private ComplexPrimeCICDecimate mDecimationFilter; private Listener mListener; private ISourceEventProcessor mFrequencyChangeProcessor; - private Listener 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 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) 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 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 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); diff --git a/src/source/tuner/TunerChannel.java b/src/source/tuner/channel/TunerChannel.java similarity index 98% rename from src/source/tuner/TunerChannel.java rename to src/source/tuner/channel/TunerChannel.java index f2b383b3..0fa851a0 100644 --- a/src/source/tuner/TunerChannel.java +++ b/src/source/tuner/channel/TunerChannel.java @@ -16,7 +16,7 @@ * along with this program. If not, see * ******************************************************************************/ -package source.tuner; +package source.tuner.channel; public class TunerChannel implements Comparable { diff --git a/src/source/tuner/channel/TunerChannelProvider.java b/src/source/tuner/channel/TunerChannelProvider.java new file mode 100644 index 00000000..56b85aa1 --- /dev/null +++ b/src/source/tuner/channel/TunerChannelProvider.java @@ -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 + * + ******************************************************************************/ +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 ); + + +} diff --git a/src/source/tuner/channel/TunerChannelSource.java b/src/source/tuner/channel/TunerChannelSource.java new file mode 100644 index 00000000..54a2bdc3 --- /dev/null +++ b/src/source/tuner/channel/TunerChannelSource.java @@ -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 + * + ******************************************************************************/ +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 +{ + private final static Logger mLog = LoggerFactory.getLogger(TunerChannelSource.class); + private TunerChannel mTunerChannel; + protected Listener 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 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 + } +} diff --git a/src/source/tuner/ettus/B100Tuner.java b/src/source/tuner/ettus/B100Tuner.java deleted file mode 100644 index be5271f8..00000000 --- a/src/source/tuner/ettus/B100Tuner.java +++ /dev/null @@ -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 - ******************************************************************************/ -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; - } -} diff --git a/src/source/tuner/ettus/B100TunerEditorPanel.java b/src/source/tuner/ettus/B100TunerEditorPanel.java deleted file mode 100644 index ed30ed68..00000000 --- a/src/source/tuner/ettus/B100TunerEditorPanel.java +++ /dev/null @@ -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 - ******************************************************************************/ -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") ); - } -} diff --git a/src/source/tuner/fcd/FCDTuner.java b/src/source/tuner/fcd/FCDTuner.java index 9c32abfd..18c89fc6 100644 --- a/src/source/tuner/fcd/FCDTuner.java +++ b/src/source/tuner/fcd/FCDTuner.java @@ -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)source ); - - /* Tell the controller to release the channel and cleanup */ - /* This will release the channel as a frequency change listener */ - getController().releaseChannel( source ); - } - } } diff --git a/src/source/tuner/fcd/FCDTunerController.java b/src/source/tuner/fcd/FCDTunerController.java index f8dae0e8..3b907683 100644 --- a/src/source/tuner/fcd/FCDTunerController.java +++ b/src/source/tuner/fcd/FCDTunerController.java @@ -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 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 listener) + { + super.removeComplexBufferListener(listener); + + if(!hasComplexBufferListeners()) + { + mComplexMixer.stop(); } } diff --git a/src/source/tuner/fcd/proV1/FCD1TunerController.java b/src/source/tuner/fcd/proV1/FCD1TunerController.java index 9df0590e..143f820f 100644 --- a/src/source/tuner/fcd/proV1/FCD1TunerController.java +++ b/src/source/tuner/fcd/proV1/FCD1TunerController.java @@ -1,34 +1,28 @@ /******************************************************************************* * 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 ******************************************************************************/ package source.tuner.fcd.proV1; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; - -import javax.usb.UsbClaimException; -import javax.usb.UsbException; - import org.slf4j.Logger; 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; @@ -36,377 +30,381 @@ import source.tuner.configuration.TunerConfiguration; import source.tuner.fcd.FCDCommand; import source.tuner.fcd.FCDTunerController; +import javax.usb.UsbClaimException; +import javax.usb.UsbException; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + public class FCD1TunerController extends FCDTunerController { - private final static Logger mLog = - LoggerFactory.getLogger( FCD1TunerController.class ); + private final static Logger mLog = LoggerFactory.getLogger(FCD1TunerController.class); - public static final int sMINIMUM_TUNABLE_FREQUENCY = 64000000; - public static final int sMAXIMUM_TUNABLE_FREQUENCY = 1700000000; - public static final int sSAMPLE_RATE = 96000; - - private double mDCCorrectionInPhase = 0.0; - private double mDCCorrectionQuadrature = 0.0; - private double mPhaseCorrection = 0.0; - private double mGainCorrection = 0.0; - private LNAGain mLNAGain; - private LNAEnhance mLNAEnhance; - private MixerGain mMixerGain; - private FCD1TunerConfiguration mTunerConfiguration; - private FCD1TunerEditor mEditor; + public static final int MINIMUM_TUNABLE_FREQUENCY = 64000000; + public static final int MAXIMUM_TUNABLE_FREQUENCY = 1700000000; + public static final int SAMPLE_RATE = 96000; - public FCD1TunerController( Device device, DeviceDescriptor descriptor ) - { - super( device, - descriptor, - (int)MixerTunerType.FUNCUBE_DONGLE_PRO.getAudioFormat().getSampleRate(), - sMINIMUM_TUNABLE_FREQUENCY, - sMAXIMUM_TUNABLE_FREQUENCY ); - } - - public void init() throws SourceException - { - super.init(); - - mFrequencyController.setSampleRate( sSAMPLE_RATE ); - - try - { - setFCDMode( Mode.APPLICATION ); - getPhaseAndGainCorrection(); - getDCIQCorrection(); - getLNAGainSetting(); - getLNAEnhanceSetting(); - getMixerGainSetting(); - - send( FCDCommand.APP_SET_MIXER_GAIN, 1l ); - } - catch( Exception e ) - { - e.printStackTrace(); - - throw new SourceException( "FCDTunerController error " + - "during construction", e ); - } - } - - public int getCurrentSampleRate() - { - return sSAMPLE_RATE; - } + private double mDCCorrectionInPhase = 0.0; + private double mDCCorrectionQuadrature = 0.0; + private double mPhaseCorrection = 0.0; + private double mGainCorrection = 0.0; + private LNAGain mLNAGain; + private LNAEnhance mLNAEnhance; + private MixerGain mMixerGain; + private FCD1TunerConfiguration mTunerConfiguration; + private FCD1TunerEditor mEditor; - @Override + public FCD1TunerController(MixerTunerDataLine mixerTDL, Device device, DeviceDescriptor descriptor) + { + super(mixerTDL, device, descriptor, MixerTunerType.FUNCUBE_DONGLE_PRO.getDisplayString(), MINIMUM_TUNABLE_FREQUENCY, + MAXIMUM_TUNABLE_FREQUENCY, MixerTunerType.FUNCUBE_DONGLE_PRO.getAudioFormat()); + } + + public void init() throws SourceException + { + super.init(); + + mFrequencyController.setSampleRate(SAMPLE_RATE); + + try + { + setFCDMode(Mode.APPLICATION); + getPhaseAndGainCorrection(); + getDCIQCorrection(); + getLNAGainSetting(); + getLNAEnhanceSetting(); + getMixerGainSetting(); + + send(FCDCommand.APP_SET_MIXER_GAIN, 1l); + } + catch(Exception e) + { + e.printStackTrace(); + + throw new SourceException("FCDTunerController error " + + "during construction", e); + } + } + + public int getCurrentSampleRate() + { + return SAMPLE_RATE; + } + + @Override public TunerClass getTunerClass() { - return TunerClass.FUNCUBE_DONGLE_PRO; + return TunerClass.FUNCUBE_DONGLE_PRO; } - @Override + @Override public TunerType getTunerType() { - return TunerType.FUNCUBE_DONGLE_PRO; + return TunerType.FUNCUBE_DONGLE_PRO; } - /** - * Get the tuner configuration - * - * @return current config or null if one hasn't been applied yet - */ - public FCD1TunerConfiguration getTunerConfiguration() - { - return mTunerConfiguration; - } - - @Override - public void apply( TunerConfiguration tunerConfig ) - throws SourceException + /** + * Get the tuner configuration + * + * @return current config or null if one hasn't been applied yet + */ + public FCD1TunerConfiguration getTunerConfiguration() { - FCD1TunerConfiguration config = (FCD1TunerConfiguration)tunerConfig; - - try - { - setFrequencyCorrection( config.getFrequencyCorrection() ); - setLNAGain( config.getLNAGain() ); - setLNAEnhance( config.getLNAEnhance() ); - setMixerGain( config.getMixerGain() ); - setDCCorrectionInPhase( config.getInphaseDCCorrection() ); - setDCCorrectionQuadrature( config.getQuadratureDCCorrection() ); - setGainCorrection( config.getGainCorrection() ); - setPhaseCorrection( config.getPhaseCorrection() ); - - //If we get to here without exception, store this config - mTunerConfiguration = config; - } - catch( Exception e) - { - //Re-cast any usb exceptions as source exceptions - throw new SourceException( "FCD 1 Controller encountered an " - + "exception while applying tuner config - " + - e.getLocalizedMessage() ); - } - - try - { - setFrequency( config.getFrequency() ); - } - catch( SourceException se ) - { - //Do nothing, we couldn't set the frequency - } - } + return mTunerConfiguration; + } - /** - * Gets the current LNA Gain value from the controller and stores it - */ - private void getLNAGainSetting() throws UsbClaimException, UsbException - { - ByteBuffer buffer = send( FCDCommand.APP_GET_LNA_GAIN ); - - buffer.order( ByteOrder.LITTLE_ENDIAN ); - - int gain = buffer.getInt( 2 ); + @Override + public void apply(TunerConfiguration tunerConfig) + throws SourceException + { + FCD1TunerConfiguration config = (FCD1TunerConfiguration)tunerConfig; - mLNAGain = LNAGain.valueOf( gain ); - } - - /** - * @return - current lna gain setting - */ - public LNAGain getLNAGain() - { - return mLNAGain; - } + try + { + setFrequencyCorrection(config.getFrequencyCorrection()); + setLNAGain(config.getLNAGain()); + setLNAEnhance(config.getLNAEnhance()); + setMixerGain(config.getMixerGain()); + setDCCorrectionInPhase(config.getInphaseDCCorrection()); + setDCCorrectionQuadrature(config.getQuadratureDCCorrection()); + setGainCorrection(config.getGainCorrection()); + setPhaseCorrection(config.getPhaseCorrection()); - /** - * Sets lna gain for the controller - * @param gain - * @throws UsbException - * @throws UsbClaimException - */ - public void setLNAGain( LNAGain gain ) - throws UsbException, UsbClaimException - { - send( FCDCommand.APP_SET_LNA_GAIN, gain.getSetting() ); - } - - /** - * @return - current phase correction setting - */ - public double getPhaseCorrection() - { - return mPhaseCorrection; - } + //If we get to here without exception, store this config + mTunerConfiguration = config; + } + catch(Exception e) + { + //Re-cast any usb exceptions as source exceptions + throw new SourceException("FCD 1 Controller encountered an " + + "exception while applying tuner config - " + + e.getLocalizedMessage()); + } - /** - * Sets the phase correction - * @param value - new phase correction - * @throws IllegalArgumentException for value outside limits (0.0 <> 1.0) - * @throws UsbClaimException - if cannot claim the FCD HID controller - * @throws UsbException - if there was a usb error - */ - public void setPhaseCorrection( double value ) - throws UsbClaimException, UsbException - { - if( value < -1.0 || value > 1.0 ) - { - throw new IllegalArgumentException( "Phase correction value [" + - value + "] outside limits ( -1.0 <> 1.0 )" ); - } - - mPhaseCorrection = value; - setPhaseAndGainCorrection(); - } + try + { + setFrequency(config.getFrequency()); + } + catch(SourceException se) + { + //Do nothing, we couldn't set the frequency + } + } - /** - * @return current gain correction setting - */ - public double getGainCorrection() - { - return mGainCorrection; - } + /** + * Gets the current LNA Gain value from the controller and stores it + */ + private void getLNAGainSetting() throws UsbClaimException, UsbException + { + ByteBuffer buffer = send(FCDCommand.APP_GET_LNA_GAIN); - /** - * - * @param value - gain correction value - * @throws IllegalArgumentException for value outside limits (0.0 <> 1.0) - * @throws UsbClaimException - if cannot claim the FCD HID controller - * @throws UsbException - if there was a usb error - */ - public void setGainCorrection( double value ) - throws UsbClaimException, UsbException - { - if( value < -1.0 || value > 1.0 ) - { - throw new IllegalArgumentException( "Gain correction value [" + - value + "] outside limits ( -1.0 <> 1.0 )" ); - } - - mGainCorrection = value; - setPhaseAndGainCorrection(); - } - - /** - * Sends the phase and gain correction values to FCD. - * - * The interface expects an argument with the SIGNED phase value in the first - * 16 bits and the UNSIGNED gain value in the second 16 bits, both in - * big-endian format. Since we're sending the argument in java little endian - * format, we place the phase in the high-order bits and the gain in the - * low-order bits, so that the send(command) function will reorder the bytes - * into big endian format, and place the arguments correctly. - * - * Gain correction values range -1.0 to 0.0 to 1.0 and applied to the dongle - * as values 0 to 65534 - * - * Phase correction values range -1.0 to 0.0 to 1.0 and applied to the - * dongle as signed values -32768 to 32767 - */ - private void setPhaseAndGainCorrection() throws UsbClaimException, - UsbException - { - //UNSIGNED short gain value - masked into a long to avoid sign-extension - long gain = (long)( ( 1.0 + mGainCorrection ) * Short.MAX_VALUE ); - - //Left shift gain to place value in upper 16 bits - long longGain = Long.rotateLeft( ( 0xFFFF & gain ), 16 ); + buffer.order(ByteOrder.LITTLE_ENDIAN); - //SIGNED short phase value - short phase = (short)( mPhaseCorrection * Short.MAX_VALUE ); + int gain = buffer.getInt(2); - //Merge the results - long correction = longGain | phase; + mLNAGain = LNAGain.valueOf(gain); + } - send( FCDCommand.APP_SET_IQ_CORRECTION, correction ); - } + /** + * @return - current lna gain setting + */ + public LNAGain getLNAGain() + { + return mLNAGain; + } - /** - * Retrieves the stored phase and gain correction values from the FCD. - * - * Note: testing shows that when the dongle is unplugged, any stored phase - * and gain correction values are reset. Reading the dongle after a fresh - * plugin shows the values reset. - * - * @throws UsbClaimException - * @throws UsbException - */ - private void getPhaseAndGainCorrection() throws UsbClaimException, - UsbException - { - ByteBuffer buffer = send( FCDCommand.APP_GET_IQ_CORRECTION ); - - buffer.order( ByteOrder.LITTLE_ENDIAN ); - - int correction = buffer.getInt( 2 ); - - /** - * Gain: mask the upper 16 phase bits and right shift to get the - * unsigned short value and then divide by 32768 to get the double - * value. We place the unsigned short value in an int to avoid sign - * issues. - */ - int gain = (int)( Long.rotateRight( correction & 0xFFFF0000, 16 ) ); + /** + * Sets lna gain for the controller + * + * @param gain + * @throws UsbException + * @throws UsbClaimException + */ + public void setLNAGain(LNAGain gain) + throws UsbException, UsbClaimException + { + send(FCDCommand.APP_SET_LNA_GAIN, gain.getSetting()); + } - mGainCorrection = ( (double)gain / (double)Short.MAX_VALUE ) - 1.0; + /** + * @return - current phase correction setting + */ + public double getPhaseCorrection() + { + return mPhaseCorrection; + } - /** - * Phase: mask the lower 16 bits and divide by 32768 to get the double - * value - */ - short phase = (short)( correction & 0x0000FFFF ); - - mPhaseCorrection = ( (double)phase / (double)Short.MAX_VALUE ); - } - - /** - * @return current DC correction for the I (inphase) component - */ - public double getDCCorrectionInPhase() - { - return mDCCorrectionInPhase; - } + /** + * Sets the phase correction + * + * @param value - new phase correction + * @throws IllegalArgumentException for value outside limits (0.0 <> 1.0) + * @throws UsbClaimException - if cannot claim the FCD HID controller + * @throws UsbException - if there was a usb error + */ + public void setPhaseCorrection(double value) + throws UsbClaimException, UsbException + { + if(value < -1.0 || value > 1.0) + { + throw new IllegalArgumentException("Phase correction value [" + + value + "] outside limits ( -1.0 <> 1.0 )"); + } - /** - * Sets DC correction for the I (inphase) component and sends the new value - * to the FCD controller - * @param value - new DC correction value for the inphase component - * @throws IllegalArgumentException for values outside limits (-1.0 <> 1.0) - * @throws UsbClaimException - if cannot claim the FCD HID controller - * @throws UsbException - if there was a usb error - */ - public void setDCCorrectionInPhase( double value ) - throws UsbClaimException, UsbException - { - if( value < -1.0 || value > 1.0 ) - { - throw new IllegalArgumentException( "DC inphase correction value " - + "[" + value + "] outside limits ( 0.0 <> 1.0 )" ); - } - - mDCCorrectionInPhase = value; - setDCIQCorrection(); - } + mPhaseCorrection = value; + setPhaseAndGainCorrection(); + } - /** - * @return current DC correction for the Q (quadrature) component - */ - public double getDCCorrectionQuadrature() - { - return mDCCorrectionQuadrature; - } + /** + * @return current gain correction setting + */ + public double getGainCorrection() + { + return mGainCorrection; + } - /** - * Sets the DC correction for the Q (quadrature) component and send the - * new value to the FCD controller - * @param value - new DC correction value for the quadrature component - * @throws IllegalArgumentException for values outside limits (-1.0 <> 1.0) - * @throws UsbClaimException - if cannot claim the FCD HID controller - * @throws UsbException - if there was a usb error - */ - public void setDCCorrectionQuadrature( double value ) - throws UsbClaimException, UsbException - { - if( value < -1.0 || value > 1.0 ) - { - throw new IllegalArgumentException( "DC quadrature correction " - + "value [" + value + "] outside limits ( 0.0 <> 1.0 )" ); - } - - mDCCorrectionQuadrature = value; - setDCIQCorrection(); - } + /** + * @param value - gain correction value + * @throws IllegalArgumentException for value outside limits (0.0 <> 1.0) + * @throws UsbClaimException - if cannot claim the FCD HID controller + * @throws UsbException - if there was a usb error + */ + public void setGainCorrection(double value) + throws UsbClaimException, UsbException + { + if(value < -1.0 || value > 1.0) + { + throw new IllegalArgumentException("Gain correction value [" + + value + "] outside limits ( -1.0 <> 1.0 )"); + } - /** - * Sends the I/Q DC offset correction values to FCD. - * - * The interface expects an argument with the signed inphase value in the - * first 16 bits and the signed quadrature value in the second 16 bits, - * both in big-endian format. - * - * Since we're sending the argument in java little endian format, we place - * the quadrature in the high-order bits and the inphase in the low-order - * bits, so that the send(command) function will reorder the bytes into big - * endian format, and place the arguments correctly. - * - * Both Inphase and Quadrature correction values range -1.0 to 0.0 to 1.0 - * and are applied to the dongle as signed short values -32768 to 0 to 32767 - */ - private void setDCIQCorrection() throws UsbClaimException, UsbException - { - //I & Q DC offset correction values are signed short values - short inphase = (short)( mDCCorrectionInPhase * Short.MAX_VALUE ); - short quadrature = (short)( mDCCorrectionQuadrature * Short.MAX_VALUE ); + mGainCorrection = value; + setPhaseAndGainCorrection(); + } + + /** + * Sends the phase and gain correction values to FCD. + * + * The interface expects an argument with the SIGNED phase value in the first + * 16 bits and the UNSIGNED gain value in the second 16 bits, both in + * big-endian format. Since we're sending the argument in java little endian + * format, we place the phase in the high-order bits and the gain in the + * low-order bits, so that the send(command) function will reorder the bytes + * into big endian format, and place the arguments correctly. + * + * Gain correction values range -1.0 to 0.0 to 1.0 and applied to the dongle + * as values 0 to 65534 + * + * Phase correction values range -1.0 to 0.0 to 1.0 and applied to the + * dongle as signed values -32768 to 32767 + */ + private void setPhaseAndGainCorrection() throws UsbClaimException, + UsbException + { + //UNSIGNED short gain value - masked into a long to avoid sign-extension + long gain = (long)((1.0 + mGainCorrection) * Short.MAX_VALUE); + + //Left shift gain to place value in upper 16 bits + long longGain = Long.rotateLeft((0xFFFF & gain), 16); + + //SIGNED short phase value + short phase = (short)(mPhaseCorrection * Short.MAX_VALUE); + + //Merge the results + long correction = longGain | phase; + + send(FCDCommand.APP_SET_IQ_CORRECTION, correction); + } + + /** + * Retrieves the stored phase and gain correction values from the FCD. + * + * Note: testing shows that when the dongle is unplugged, any stored phase + * and gain correction values are reset. Reading the dongle after a fresh + * plugin shows the values reset. + * + * @throws UsbClaimException + * @throws UsbException + */ + private void getPhaseAndGainCorrection() throws UsbClaimException, + UsbException + { + ByteBuffer buffer = send(FCDCommand.APP_GET_IQ_CORRECTION); + + buffer.order(ByteOrder.LITTLE_ENDIAN); + + int correction = buffer.getInt(2); + + /** + * Gain: mask the upper 16 phase bits and right shift to get the + * unsigned short value and then divide by 32768 to get the double + * value. We place the unsigned short value in an int to avoid sign + * issues. + */ + int gain = (int)(Long.rotateRight(correction & 0xFFFF0000, 16)); + + mGainCorrection = ((double)gain / (double)Short.MAX_VALUE) - 1.0; + + /** + * Phase: mask the lower 16 bits and divide by 32768 to get the double + * value + */ + short phase = (short)(correction & 0x0000FFFF); + + mPhaseCorrection = ((double)phase / (double)Short.MAX_VALUE); + } + + /** + * @return current DC correction for the I (inphase) component + */ + public double getDCCorrectionInPhase() + { + return mDCCorrectionInPhase; + } + + /** + * Sets DC correction for the I (inphase) component and sends the new value + * to the FCD controller + * + * @param value - new DC correction value for the inphase component + * @throws IllegalArgumentException for values outside limits (-1.0 <> 1.0) + * @throws UsbClaimException - if cannot claim the FCD HID controller + * @throws UsbException - if there was a usb error + */ + public void setDCCorrectionInPhase(double value) + throws UsbClaimException, UsbException + { + if(value < -1.0 || value > 1.0) + { + throw new IllegalArgumentException("DC inphase correction value " + + "[" + value + "] outside limits ( 0.0 <> 1.0 )"); + } + + mDCCorrectionInPhase = value; + setDCIQCorrection(); + } + + /** + * @return current DC correction for the Q (quadrature) component + */ + public double getDCCorrectionQuadrature() + { + return mDCCorrectionQuadrature; + } + + /** + * Sets the DC correction for the Q (quadrature) component and send the + * new value to the FCD controller + * + * @param value - new DC correction value for the quadrature component + * @throws IllegalArgumentException for values outside limits (-1.0 <> 1.0) + * @throws UsbClaimException - if cannot claim the FCD HID controller + * @throws UsbException - if there was a usb error + */ + public void setDCCorrectionQuadrature(double value) + throws UsbClaimException, UsbException + { + if(value < -1.0 || value > 1.0) + { + throw new IllegalArgumentException("DC quadrature correction " + + "value [" + value + "] outside limits ( 0.0 <> 1.0 )"); + } + + mDCCorrectionQuadrature = value; + setDCIQCorrection(); + } + + /** + * Sends the I/Q DC offset correction values to FCD. + * + * The interface expects an argument with the signed inphase value in the + * first 16 bits and the signed quadrature value in the second 16 bits, + * both in big-endian format. + * + * Since we're sending the argument in java little endian format, we place + * the quadrature in the high-order bits and the inphase in the low-order + * bits, so that the send(command) function will reorder the bytes into big + * endian format, and place the arguments correctly. + * + * Both Inphase and Quadrature correction values range -1.0 to 0.0 to 1.0 + * and are applied to the dongle as signed short values -32768 to 0 to 32767 + */ + private void setDCIQCorrection() throws UsbClaimException, UsbException + { + //I & Q DC offset correction values are signed short values + short inphase = (short)(mDCCorrectionInPhase * Short.MAX_VALUE); + short quadrature = (short)(mDCCorrectionQuadrature * Short.MAX_VALUE); + + //Mask the shorts into longs to preserve the sign bit and prepare + //for merging to a single 32-bit value + long maskedInphase = inphase & 0x0000FFFF; + long maskedQuadrature = quadrature & 0x0000FFFF; + + //Left shift quadrature to place value in upper 16 bits + long shiftedQuadrature = Long.rotateLeft(maskedQuadrature, 16); + + //Merge the results + long correction = shiftedQuadrature | maskedInphase; - //Mask the shorts into longs to preserve the sign bit and prepare - //for merging to a single 32-bit value - long maskedInphase = inphase & 0x0000FFFF; - long maskedQuadrature = quadrature & 0x0000FFFF; - - //Left shift quadrature to place value in upper 16 bits - long shiftedQuadrature = Long.rotateLeft( maskedQuadrature, 16 ); - - //Merge the results - long correction = shiftedQuadrature | maskedInphase; - // Log.info( "FCD1: setting iq correction," + // " inphase:" + inphase + // " masked inphase:" + maskedInphase + @@ -415,305 +413,305 @@ public class FCD1TunerController extends FCDTunerController // " shifted quadrature:" + shiftedQuadrature + // " correction:" + correction ); - send( FCDCommand.APP_SET_DC_CORRECTION, correction ); - } - - private void getDCIQCorrection() throws UsbClaimException, - UsbException - { - ByteBuffer buffer = send( FCDCommand.APP_GET_DC_CORRECTION ); - - buffer.order( ByteOrder.LITTLE_ENDIAN ); - - int correction = buffer.getInt( 2 ); + send(FCDCommand.APP_SET_DC_CORRECTION, correction); + } - /** - * Quadrature: mask the upper 16 bits and divide by 32768 to get the - * stored quadrature value. Cast the 16-bit value to a short to get - * the signed short value - */ - long shiftedQuadrature = correction & 0xFFFF0000; - long quadrature = Long.rotateRight( shiftedQuadrature, 16 ); + private void getDCIQCorrection() throws UsbClaimException, + UsbException + { + ByteBuffer buffer = send(FCDCommand.APP_GET_DC_CORRECTION); - mDCCorrectionQuadrature = ( (short)quadrature / (double)Short.MAX_VALUE ); + buffer.order(ByteOrder.LITTLE_ENDIAN); + + int correction = buffer.getInt(2); + + /** + * Quadrature: mask the upper 16 bits and divide by 32768 to get the + * stored quadrature value. Cast the 16-bit value to a short to get + * the signed short value + */ + long shiftedQuadrature = correction & 0xFFFF0000; + long quadrature = Long.rotateRight(shiftedQuadrature, 16); + + mDCCorrectionQuadrature = ((short)quadrature / (double)Short.MAX_VALUE); + + /** + * InPhase: mask the lower 16 bits to get the short + * value and then divide by 32768 to get the stored inphase value. + * Cast the 16-bit value to a short to get the signed short value + */ + long inphase = correction & 0x0000FFFF; + mDCCorrectionInPhase = ((short)inphase / (double)Short.MAX_VALUE); - /** - * InPhase: mask the lower 16 bits to get the short - * value and then divide by 32768 to get the stored inphase value. - * Cast the 16-bit value to a short to get the signed short value - */ - long inphase = correction & 0x0000FFFF; - mDCCorrectionInPhase = ( (short)inphase / (double)Short.MAX_VALUE ); - // Log.info( "FCD1:" + // " correction:" + correction + // " Q:" + quadrature + // " I:" + inphase + // " QCorr:" + mDCCorrectionQuadrature + // " ICorr:" + mDCCorrectionInPhase ); - } - - private void getLNAEnhanceSetting() throws UsbClaimException, UsbException - { - ByteBuffer buffer = send( FCDCommand.APP_GET_LNA_ENHANCE ); - - buffer.order( ByteOrder.LITTLE_ENDIAN ); - - int enhance = buffer.getInt( 2 ); - - mLNAEnhance = LNAEnhance.valueOf( enhance ); - } - - public LNAEnhance getLNAEnhance() - { - return mLNAEnhance; - } - - public void setLNAEnhance( LNAEnhance enhance ) - throws UsbClaimException, UsbException - { - send( FCDCommand.APP_SET_LNA_ENHANCE, enhance.getSetting() ); - } - - private void getMixerGainSetting() throws UsbClaimException, UsbException - { - ByteBuffer buffer = send( FCDCommand.APP_GET_MIXER_GAIN ); - - buffer.order( ByteOrder.LITTLE_ENDIAN ); - - int gain = buffer.getInt( 2 ); - - mMixerGain = MixerGain.valueOf( gain ); - } - - public MixerGain getMixerGain() - { - return mMixerGain; - } - - public void setMixerGain( MixerGain gain ) throws UsbClaimException, UsbException - { - send( FCDCommand.APP_SET_MIXER_GAIN, gain.getSetting() ); - } - - public enum Block - { - CELLULAR_BAND_BLOCKED( "Blocked" ), - NO_BAND_BLOCK( "Unblocked" ), - UNKNOWN( "Unknown" ); - - private String mLabel; - - private Block( String label ) - { - mLabel = label; - } - - public String getLabel() - { - return mLabel; - } - - public static Block getBlock( String block ) - { - Block retVal = UNKNOWN; - - if( block.equalsIgnoreCase( "No blk" ) ) - { - retVal = NO_BAND_BLOCK; - } - else if( block.equalsIgnoreCase( "Cell blk" ) ) - { - retVal = CELLULAR_BAND_BLOCKED; - } - - return retVal; - } - } - - /** - * LNA Gain values suppported by the FCD Pro 1.0 - */ - public enum LNAGain - { - LNA_GAIN_MINUS_5_0( 0, "-5.0 dB" ), - LNA_GAIN_MINUS_2_5( 1, "-2.5 dB" ), - LNA_GAIN_PLUS_0_0( 4, "0.0 dB" ), - LNA_GAIN_PLUS_2_5( 5, "2.5 dB" ), - LNA_GAIN_PLUS_5_0( 6, "5.0 dB" ), - LNA_GAIN_PLUS_7_5( 7, "7.5 dB" ), - LNA_GAIN_PLUS_10_0( 8, "10.0 dB" ), - LNA_GAIN_PLUS_12_5( 9, "12.5 dB" ), - LNA_GAIN_PLUS_15_0( 10, "15.0 dB" ), - LNA_GAIN_PLUS_17_5( 11, "17.5 dB" ), - LNA_GAIN_PLUS_20_0( 12, "20.0 dB" ), - LNA_GAIN_PLUS_25_0( 13, "25.0 dB" ), - LNA_GAIN_PLUS_30_0( 14, "30.0 dB" ); - - private int mSetting; - private String mLabel; - - private LNAGain( int setting, String label ) - { - mSetting = setting; - mLabel = label; - } - - public static LNAGain valueOf( int setting ) - { - switch( setting ) - { - case 0: - return LNA_GAIN_MINUS_5_0; - case 1: - return LNA_GAIN_MINUS_2_5; - case 4: - return LNA_GAIN_PLUS_0_0; - case 5: - return LNA_GAIN_PLUS_2_5; - case 6: - return LNA_GAIN_PLUS_5_0; - case 7: - return LNA_GAIN_PLUS_7_5; - case 8: - return LNA_GAIN_PLUS_10_0; - case 9: - return LNA_GAIN_PLUS_12_5; - case 10: - return LNA_GAIN_PLUS_15_0; - case 11: - return LNA_GAIN_PLUS_17_5; - case 12: - return LNA_GAIN_PLUS_20_0; - case 13: - return LNA_GAIN_PLUS_25_0; - case 14: - return LNA_GAIN_PLUS_30_0; - default: - throw new IllegalArgumentException( "FCD 1.0 Tuner " - + "Controller - unrecognized LNA gain setting " - + "value [" + setting + "]" ); - } - } - - public int getSetting() - { - return mSetting; - } - - public String getLabel() - { - return mLabel; - } - - public String toString() - { - return getLabel(); - } } - /** - * LNA Enhance values suppported by the FCD Pro 1.0 - */ - public enum LNAEnhance + private void getLNAEnhanceSetting() throws UsbClaimException, UsbException { - LNA_ENHANCE_OFF( 0, "Off" ), - LNA_ENHANCE_0( 1, "0" ), - LNA_ENHANCE_1( 3, "1" ), - LNA_ENHANCE_2( 5, "2" ), - LNA_ENHANCE_3( 7, "3" ); - - private int mSetting; - private String mLabel; - - private LNAEnhance( int setting, String label ) - { - mSetting = setting; - mLabel = label; - } - - public static LNAEnhance valueOf( int setting ) - { - switch( setting ) - { - case 0: - return LNA_ENHANCE_OFF; - case 1: - return LNA_ENHANCE_0; - case 3: - return LNA_ENHANCE_1; - case 5: - return LNA_ENHANCE_2; - case 7: - return LNA_ENHANCE_3; - default: - throw new IllegalArgumentException( "FCD 1.0 Tuner " - + "Controller - unrecognized LNA enhance setting " - + "value [" + setting + "]" ); - } - } - - public int getSetting() - { - return mSetting; - } - - public String getLabel() - { - return mLabel; - } - - public String toString() - { - return getLabel(); - } + ByteBuffer buffer = send(FCDCommand.APP_GET_LNA_ENHANCE); + + buffer.order(ByteOrder.LITTLE_ENDIAN); + + int enhance = buffer.getInt(2); + + mLNAEnhance = LNAEnhance.valueOf(enhance); } - /** - * Mixer Gain values suppported by the FCD Pro 1.0 - */ - public enum MixerGain + public LNAEnhance getLNAEnhance() { - MIXER_GAIN_PLUS_4_0( 0, "4.0 dB" ), - MIXER_GAIN_PLUS_12_0( 1, "12.0 dB" ); - - private int mSetting; - private String mLabel; - - private MixerGain( int setting, String label ) - { - mSetting = setting; - mLabel = label; - } - - public static MixerGain valueOf( int setting ) - { - switch( setting ) - { - case 0: - return MIXER_GAIN_PLUS_4_0; - case 1: - return MIXER_GAIN_PLUS_12_0; - default: - throw new IllegalArgumentException( "FCD 1.0 Tuner " - + "Controller - unrecognized mixer gain setting " - + "value [" + setting + "]" ); - } - } - - public int getSetting() - { - return mSetting; - } - - public String getLabel() - { - return mLabel; - } - - public String toString() - { - return getLabel(); - } + return mLNAEnhance; + } + + public void setLNAEnhance(LNAEnhance enhance) + throws UsbClaimException, UsbException + { + send(FCDCommand.APP_SET_LNA_ENHANCE, enhance.getSetting()); + } + + private void getMixerGainSetting() throws UsbClaimException, UsbException + { + ByteBuffer buffer = send(FCDCommand.APP_GET_MIXER_GAIN); + + buffer.order(ByteOrder.LITTLE_ENDIAN); + + int gain = buffer.getInt(2); + + mMixerGain = MixerGain.valueOf(gain); + } + + public MixerGain getMixerGain() + { + return mMixerGain; + } + + public void setMixerGain(MixerGain gain) throws UsbClaimException, UsbException + { + send(FCDCommand.APP_SET_MIXER_GAIN, gain.getSetting()); + } + + public enum Block + { + CELLULAR_BAND_BLOCKED("Blocked"), + NO_BAND_BLOCK("Unblocked"), + UNKNOWN("Unknown"); + + private String mLabel; + + private Block(String label) + { + mLabel = label; + } + + public String getLabel() + { + return mLabel; + } + + public static Block getBlock(String block) + { + Block retVal = UNKNOWN; + + if(block.equalsIgnoreCase("No blk")) + { + retVal = NO_BAND_BLOCK; + } + else if(block.equalsIgnoreCase("Cell blk")) + { + retVal = CELLULAR_BAND_BLOCKED; + } + + return retVal; + } + } + + /** + * LNA Gain values suppported by the FCD Pro 1.0 + */ + public enum LNAGain + { + LNA_GAIN_MINUS_5_0(0, "-5.0 dB"), + LNA_GAIN_MINUS_2_5(1, "-2.5 dB"), + LNA_GAIN_PLUS_0_0(4, "0.0 dB"), + LNA_GAIN_PLUS_2_5(5, "2.5 dB"), + LNA_GAIN_PLUS_5_0(6, "5.0 dB"), + LNA_GAIN_PLUS_7_5(7, "7.5 dB"), + LNA_GAIN_PLUS_10_0(8, "10.0 dB"), + LNA_GAIN_PLUS_12_5(9, "12.5 dB"), + LNA_GAIN_PLUS_15_0(10, "15.0 dB"), + LNA_GAIN_PLUS_17_5(11, "17.5 dB"), + LNA_GAIN_PLUS_20_0(12, "20.0 dB"), + LNA_GAIN_PLUS_25_0(13, "25.0 dB"), + LNA_GAIN_PLUS_30_0(14, "30.0 dB"); + + private int mSetting; + private String mLabel; + + private LNAGain(int setting, String label) + { + mSetting = setting; + mLabel = label; + } + + public static LNAGain valueOf(int setting) + { + switch(setting) + { + case 0: + return LNA_GAIN_MINUS_5_0; + case 1: + return LNA_GAIN_MINUS_2_5; + case 4: + return LNA_GAIN_PLUS_0_0; + case 5: + return LNA_GAIN_PLUS_2_5; + case 6: + return LNA_GAIN_PLUS_5_0; + case 7: + return LNA_GAIN_PLUS_7_5; + case 8: + return LNA_GAIN_PLUS_10_0; + case 9: + return LNA_GAIN_PLUS_12_5; + case 10: + return LNA_GAIN_PLUS_15_0; + case 11: + return LNA_GAIN_PLUS_17_5; + case 12: + return LNA_GAIN_PLUS_20_0; + case 13: + return LNA_GAIN_PLUS_25_0; + case 14: + return LNA_GAIN_PLUS_30_0; + default: + throw new IllegalArgumentException("FCD 1.0 Tuner " + + "Controller - unrecognized LNA gain setting " + + "value [" + setting + "]"); + } + } + + public int getSetting() + { + return mSetting; + } + + public String getLabel() + { + return mLabel; + } + + public String toString() + { + return getLabel(); + } + } + + /** + * LNA Enhance values suppported by the FCD Pro 1.0 + */ + public enum LNAEnhance + { + LNA_ENHANCE_OFF(0, "Off"), + LNA_ENHANCE_0(1, "0"), + LNA_ENHANCE_1(3, "1"), + LNA_ENHANCE_2(5, "2"), + LNA_ENHANCE_3(7, "3"); + + private int mSetting; + private String mLabel; + + private LNAEnhance(int setting, String label) + { + mSetting = setting; + mLabel = label; + } + + public static LNAEnhance valueOf(int setting) + { + switch(setting) + { + case 0: + return LNA_ENHANCE_OFF; + case 1: + return LNA_ENHANCE_0; + case 3: + return LNA_ENHANCE_1; + case 5: + return LNA_ENHANCE_2; + case 7: + return LNA_ENHANCE_3; + default: + throw new IllegalArgumentException("FCD 1.0 Tuner " + + "Controller - unrecognized LNA enhance setting " + + "value [" + setting + "]"); + } + } + + public int getSetting() + { + return mSetting; + } + + public String getLabel() + { + return mLabel; + } + + public String toString() + { + return getLabel(); + } + } + + /** + * Mixer Gain values suppported by the FCD Pro 1.0 + */ + public enum MixerGain + { + MIXER_GAIN_PLUS_4_0(0, "4.0 dB"), + MIXER_GAIN_PLUS_12_0(1, "12.0 dB"); + + private int mSetting; + private String mLabel; + + private MixerGain(int setting, String label) + { + mSetting = setting; + mLabel = label; + } + + public static MixerGain valueOf(int setting) + { + switch(setting) + { + case 0: + return MIXER_GAIN_PLUS_4_0; + case 1: + return MIXER_GAIN_PLUS_12_0; + default: + throw new IllegalArgumentException("FCD 1.0 Tuner " + + "Controller - unrecognized mixer gain setting " + + "value [" + setting + "]"); + } + } + + public int getSetting() + { + return mSetting; + } + + public String getLabel() + { + return mLabel; + } + + public String toString() + { + return getLabel(); + } } } diff --git a/src/source/tuner/fcd/proplusV2/FCD2TunerController.java b/src/source/tuner/fcd/proplusV2/FCD2TunerController.java index f2173ceb..f6c39834 100644 --- a/src/source/tuner/fcd/proplusV2/FCD2TunerController.java +++ b/src/source/tuner/fcd/proplusV2/FCD2TunerController.java @@ -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 diff --git a/src/source/tuner/frequency/DCTrackingSourceControl.java b/src/source/tuner/frequency/DCTrackingSourceControl.java index 09b48831..de042699 100644 --- a/src/source/tuner/frequency/DCTrackingSourceControl.java +++ b/src/source/tuner/frequency/DCTrackingSourceControl.java @@ -113,7 +113,7 @@ public class DCTrackingSourceControl extends Module } @Override - public void start(ScheduledExecutorService executor) + public void start() { } diff --git a/src/source/tuner/frequency/FrequencyController.java b/src/source/tuner/frequency/FrequencyController.java index 7403ad64..86723b10 100644 --- a/src/source/tuner/frequency/FrequencyController.java +++ b/src/source/tuner/frequency/FrequencyController.java @@ -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); } } diff --git a/src/source/tuner/hackrf/HackRFTuner.java b/src/source/tuner/hackrf/HackRFTuner.java index 2194a7de..fa0ff210 100644 --- a/src/source/tuner/hackrf/HackRFTuner.java +++ b/src/source/tuner/hackrf/HackRFTuner.java @@ -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)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 listener ) - { - getController().addListener( listener ); - } - - @Override - public void removeListener( Listener listener ) - { - getController().removeListener( listener ); - } } diff --git a/src/source/tuner/hackrf/HackRFTunerController.java b/src/source/tuner/hackrf/HackRFTunerController.java index 1741bfaf..812f37f5 100644 --- a/src/source/tuner/hackrf/HackRFTunerController.java +++ b/src/source/tuner/hackrf/HackRFTunerController.java @@ -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 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 listener) - { - mUSBTransferProcessor.removeListener(listener); - } - } diff --git a/src/source/tuner/manager/AbstractSourceManager.java b/src/source/tuner/manager/AbstractSourceManager.java new file mode 100644 index 00000000..9c373d2d --- /dev/null +++ b/src/source/tuner/manager/AbstractSourceManager.java @@ -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 + * + ******************************************************************************/ +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 +{ + protected Broadcaster 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 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 sourceEventListener); + + /** + * Removes the listener from receiving source events from this source manager + * @param sourceEventListener to remove + */ + public abstract void removeSourceEventListener(Listener sourceEventListener); + + /** + * Adds the listener to receive complex buffer samples + */ + @Override + public void addComplexBufferListener(Listener listener) + { + mComplexBufferBroadcaster.addListener(listener); + } + + /** + * Removes the listener from receiving complex buffer samples + */ + @Override + public void removeComplexBufferListener(Listener 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); + } +} diff --git a/src/source/tuner/manager/TunerSourceManager.java b/src/source/tuner/manager/TunerSourceManager.java new file mode 100644 index 00000000..fb6856bd --- /dev/null +++ b/src/source/tuner/manager/TunerSourceManager.java @@ -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 + * + ******************************************************************************/ +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 getChannels() + { + return null; + } + + @Override + public TunerChannelSource getSource(TunerChannel tunerChannel) + { + return null; + } + + @Override + public void releaseSource(TunerChannelSource source) + { + + } + + @Override + public void addSourceEventListener(Listener sourceEventListener) + { + + } + + @Override + public void removeSourceEventListener(Listener sourceEventListener) + { + + } + + @Override + public void receive(SourceEvent sourceEvent) + { + + } +} diff --git a/src/source/tuner/rtl/RTL2832Tuner.java b/src/source/tuner/rtl/RTL2832Tuner.java index 20b746ea..dceece1c 100644 --- a/src/source/tuner/rtl/RTL2832Tuner.java +++ b/src/source/tuner/rtl/RTL2832Tuner.java @@ -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 ******************************************************************************/ 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)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 listener ) - { - getController().addListener( listener ); - } - - @Override - public void removeListener( Listener listener ) - { - getController().removeListener( listener ); - } } diff --git a/src/source/tuner/rtl/RTL2832TunerController.java b/src/source/tuner/rtl/RTL2832TunerController.java index 7666890b..6102f951 100644 --- a/src/source/tuner/rtl/RTL2832TunerController.java +++ b/src/source/tuner/rtl/RTL2832TunerController.java @@ -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 listener) + @Override + public void addComplexBufferListener(Listener 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 listener) + @Override + public void removeComplexBufferListener(Listener listener) { - mUSBTransferProcessor.removeListener(listener); + super.removeComplexBufferListener(listener); + + if(!hasComplexBufferListeners()) + { + mUSBTransferProcessor.removeListener(); + } } /** diff --git a/src/source/tuner/test/TestTuner.java b/src/source/tuner/test/TestTuner.java index 476c6174..f43e646b 100644 --- a/src/source/tuner/test/TestTuner.java +++ b/src/source/tuner/test/TestTuner.java @@ -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 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 listener) - { - super.removeListener(listener); - - if(!hasListeners()) - { - getTestTunerController().stop(); - } - } } diff --git a/src/source/tuner/test/TestTunerController.java b/src/source/tuner/test/TestTunerController.java index b09f630b..5664d9d0 100644 --- a/src/source/tuner/test/TestTunerController.java +++ b/src/source/tuner/test/TestTunerController.java @@ -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 listener) + { + } + + @Override + public void addComplexBufferListener(Listener listener) { mSampleGenerator.setListener(listener); mSampleGenerator.start(); } + @Override + public void removeComplexBufferListener(Listener 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 diff --git a/src/source/tuner/usb/USBTransferProcessor.java b/src/source/tuner/usb/USBTransferProcessor.java index 7052db0c..60d6642a 100644 --- a/src/source/tuner/usb/USBTransferProcessor.java +++ b/src/source/tuner/usb/USBTransferProcessor.java @@ -70,10 +70,10 @@ public class USBTransferProcessor implements TransferCallback //Byte array transfer buffers size in bytes private int mBufferSize; - private Broadcaster mComplexBufferBroadcaster = new Broadcaster<>(); private OverflowableTransferQueue mFilledBuffers; private LinkedTransferQueue mAvailableTransfers = new LinkedTransferQueue<>(); private LinkedTransferQueue mTransfersInProgress = new LinkedTransferQueue<>(); + private Listener 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 listener) { - return mComplexBufferBroadcaster.hasListeners(); - } - - public void addListener(Listener listener) - { - mComplexBufferBroadcaster.addListener(listener); - + mComplexBufferListener = listener; start(); } - public void removeListener(Listener 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) diff --git a/src/source/tuner/usb/USBTunerController.java b/src/source/tuner/usb/USBTunerController.java new file mode 100644 index 00000000..f731783b --- /dev/null +++ b/src/source/tuner/usb/USBTunerController.java @@ -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 + * + ******************************************************************************/ +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 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 listener) + { + super.removeComplexBufferListener(listener); + + if(!hasComplexBufferListeners()) + { + getUSBTransferProcessor().removeListener(); + } + } +} diff --git a/src/source/wave/ComplexWaveSource.java b/src/source/wave/ComplexWaveSource.java index 9ec77f2a..f7d0c601 100644 --- a/src/source/wave/ComplexWaveSource.java +++ b/src/source/wave/ComplexWaveSource.java @@ -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; diff --git a/src/source/wave/RealWaveSource.java b/src/source/wave/RealWaveSource.java index 3545efc2..f64eef05 100644 --- a/src/source/wave/RealWaveSource.java +++ b/src/source/wave/RealWaveSource.java @@ -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; diff --git a/src/spectrum/OverlayPanel.java b/src/spectrum/OverlayPanel.java index d78101e2..dc211bae 100644 --- a/src/spectrum/OverlayPanel.java +++ b/src/spectrum/OverlayPanel.java @@ -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; diff --git a/src/spectrum/SpectralDisplayPanel.java b/src/spectrum/SpectralDisplayPanel.java index 9c871aea..c050e27c 100644 --- a/src/spectrum/SpectralDisplayPanel.java +++ b/src/spectrum/SpectralDisplayPanel.java @@ -424,7 +424,7 @@ public class SpectralDisplayPanel extends JPanel implements Listener)mDFTProcessor); + mTuner.getSourceManager().addComplexBufferListener(mDFTProcessor); mSpectrumPanel.setSampleSize(mTuner.getSampleSize());