From b6f282a85ad1bf1342ff3a4533a6b27cd004c307 Mon Sep 17 00:00:00 2001 From: Denny Date: Fri, 19 Jan 2018 12:44:31 -0500 Subject: [PATCH] Polyphase Channelizer Viewer is fully functional again, now using the TestTuner and TestTunerController implementations. This implementation only has support for channels that are completely centered within a channelizer output. Two output slice stitching to form a composite channel will be implemented next. Initial profiling indicates that the FFT function within the polyphase channelizer is now the dominant processing workload - 28% cpu loading when channelizing a 10 MHz sample rate input into 800 channels running at 25.0 kHz each. This is ideal for offloading to an OpenCL kernel and running on a GPU. --- .../filter/channelizer/ChannelizerViewer.java | 141 ++++++++++++++---- .../ComplexPolyphaseChannelizerM2.java | 18 ++- .../channelizer/PolyphaseChannelManager.java | 9 -- .../PolyphaseChannelResultsBuffer.java | 13 +- .../channelizer/ScheduledBufferProcessor.java | 9 -- .../output/OneChannelOutputProcessor.java | 1 - .../source/tuner/TunerController.java | 4 +- .../source/tuner/test/SampleGenerator.java | 67 ++++++--- .../tuner/test/TestTunerController.java | 48 +++--- 9 files changed, 208 insertions(+), 102 deletions(-) diff --git a/src/main/java/io/github/dsheirer/dsp/filter/channelizer/ChannelizerViewer.java b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/ChannelizerViewer.java index 33832fe3..b37e82ff 100644 --- a/src/main/java/io/github/dsheirer/dsp/filter/channelizer/ChannelizerViewer.java +++ b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/ChannelizerViewer.java @@ -23,6 +23,7 @@ import io.github.dsheirer.settings.SettingsManager; import io.github.dsheirer.source.ISourceEventProcessor; import io.github.dsheirer.source.SourceEvent; import io.github.dsheirer.source.SourceException; +import io.github.dsheirer.source.tuner.Tuner; import io.github.dsheirer.source.tuner.channel.TunerChannel; import io.github.dsheirer.source.tuner.channel.TunerChannelSource; import io.github.dsheirer.source.tuner.configuration.TunerConfigurationModel; @@ -44,6 +45,8 @@ import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import java.awt.Dimension; import java.awt.EventQueue; +import java.util.ArrayList; +import java.util.List; public class ChannelizerViewer extends JFrame { @@ -55,13 +58,14 @@ public class ChannelizerViewer extends JFrame private SettingsManager mSettingsManager = new SettingsManager(new TunerConfigurationModel()); private JPanel mPrimaryPanel; private JPanel mControlPanel; + private JLabel mToneFrequencyLabel; private PrimarySpectrumPanel mPrimarySpectrumPanel; private ChannelArrayPanel mChannelPanel; private int mChannelCount; private int mChannelsPerRow; - private long mToneFrequency; + private long mBaseFrequency = 100000000; //100 MHz private DFTSize mMainPanelDFTSize = DFTSize.FFT32768; - private DFTSize mChannelPanelDFTSize = DFTSize.FFT01024; + private DFTSize mChannelPanelDFTSize = DFTSize.FFT04096; private TestTuner mTestTuner; /** @@ -76,16 +80,12 @@ public class ChannelizerViewer extends JFrame mChannelCount = (int)(mTestTuner.getTunerController().getUsableBandwidth() / CHANNEL_BANDWIDTH); mChannelsPerRow = channelsPerRow; - mToneFrequency = mTestTuner.getTunerController().getFrequency() + 19200; - - mTestTuner.getTunerController().setToneFrequency(mToneFrequency); - init(); } private void init() { - setTitle("TunerChannelizer Viewer"); + setTitle("Polyphase Channelizer Viewer"); setSize(1000, 800); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLayout(new MigLayout("insets 0 0 0 0", "[grow,fill]", "[grow,fill]")); @@ -129,11 +129,11 @@ public class ChannelizerViewer extends JFrame mControlPanel.setLayout(new MigLayout("insets 0 0 0 0", "", "")); mControlPanel.add(new JLabel("Tone:"), "align left"); - int maximumFrequencyOffset = (int)(mTestTuner.getTunerController().getSampleRate() / 2); - long minimumFrequency = mTestTuner.getTunerController().getFrequency() - maximumFrequencyOffset; - long maximumFrequency = mTestTuner.getTunerController().getFrequency() + maximumFrequencyOffset; + long minimumFrequency = -(long)mTestTuner.getTunerController().getSampleRate() / 2; + long maximumFrequency = (long)mTestTuner.getTunerController().getSampleRate() / 2; + long toneFrequency = 0; - SpinnerNumberModel model = new SpinnerNumberModel(mToneFrequency, minimumFrequency, maximumFrequency, + SpinnerNumberModel model = new SpinnerNumberModel(toneFrequency, minimumFrequency, maximumFrequency, 100 ); model.addChangeListener(new ChangeListener() @@ -141,8 +141,9 @@ public class ChannelizerViewer extends JFrame @Override public void stateChanged(ChangeEvent e) { - int toneFrequency = model.getNumber().intValue(); + long toneFrequency = model.getNumber().longValue(); mTestTuner.getTunerController().setToneFrequency(toneFrequency); + mToneFrequencyLabel.setText(String.valueOf(getToneFrequency())); } }); @@ -151,15 +152,21 @@ public class ChannelizerViewer extends JFrame mControlPanel.add(spinner); mControlPanel.add(new JLabel("Hz")); - mControlPanel.add(new JLabel("Sample Rate: " + mTestTuner.getTunerController().getSampleRate() + " Hz"), "push,align right"); + mControlPanel.add(new JLabel("Frequency:"), "push,align right"); + mToneFrequencyLabel = new JLabel(String.valueOf(getToneFrequency())); + mControlPanel.add(mToneFrequencyLabel, "push,align left"); - mControlPanel.add(new JLabel("Max: " + (mTestTuner.getTunerController().getSampleRate() / 2) + " Hz"), "push,align right"); mControlPanel.add(new JLabel("Channels: " + mChannelCount), "push,align right"); } return mControlPanel; } + private long getToneFrequency() + { + return mTestTuner.getTunerController().getFrequency() + mTestTuner.getTunerController().getToneFrequency(); + } + private ChannelArrayPanel getChannelArrayPanel() { if(mChannelPanel == null) @@ -192,7 +199,7 @@ public class ChannelizerViewer extends JFrame int channelToLog = -1; - long baseFrequency = 100006250; //100.006250 MHz + long baseFrequency = mBaseFrequency + (CHANNEL_BANDWIDTH / 2); for(int x = 0; x < mChannelCount - 1; x++) { @@ -215,6 +222,33 @@ public class ChannelizerViewer extends JFrame } } + /** + * Returns a list of tuner channels that will fit within the tuner's bandwidth, minus a half channel each at the + * lower and upper ends of the spectrum. + * + * @param tuner to create channels for + * @return list of contiguous channels filling the tuner bandwidth + */ + public static List getTunerChannels(Tuner tuner) + { + List tunerChannels = new ArrayList<>(); + + long baseFrequency = tuner.getTunerController().getFrequency(); + baseFrequency -= tuner.getTunerController().getSampleRate() / 2; + baseFrequency += (CHANNEL_BANDWIDTH / 2); + + int channelCount = (int)(tuner.getTunerController().getSampleRate() / CHANNEL_BANDWIDTH) - 1; + + for(int x = 0; x < channelCount; x++) + { + long frequency = baseFrequency + (x * CHANNEL_BANDWIDTH); + TunerChannel tunerChannel = new TunerChannel(frequency, CHANNEL_BANDWIDTH); + tunerChannels.add(tunerChannel); + } + + return tunerChannels; + } + public class PrimarySpectrumPanel extends JPanel implements Listener, ISourceEventProcessor { private DFTProcessor mDFTProcessor = new DFTProcessor(SampleType.COMPLEX); @@ -225,6 +259,7 @@ public class ChannelizerViewer extends JFrame { setLayout(new MigLayout("insets 0 0 0 0", "[grow,fill]", "[grow,fill]")); mSpectrumPanel = new SpectrumPanel(settingsManager); + mSpectrumPanel.setSampleSize(26); add(mSpectrumPanel); mDFTProcessor.addConverter(mComplexDecibelConverter); @@ -256,21 +291,20 @@ public class ChannelizerViewer extends JFrame private DFTProcessor mDFTProcessor = new DFTProcessor(SampleType.COMPLEX); private ComplexDecibelConverter mComplexDecibelConverter = new ComplexDecibelConverter(); private SpectrumPanel mSpectrumPanel; - private long mFrequency; public ChannelPanel(SettingsManager settingsManager, double sampleRate, long frequency, int bandwidth, boolean enableLogging) { - setLayout(new MigLayout("insets 0 0 0 0", "[grow,fill]", "[grow,fill]")); + setLayout(new MigLayout("insets 0 0 0 0", "[center,grow,fill]", "[grow,fill][]")); mSpectrumPanel = new SpectrumPanel(settingsManager); - add(mSpectrumPanel); + mSpectrumPanel.setSampleSize(28); + add(mSpectrumPanel, "wrap"); + add(new JLabel("Center:" + frequency)); mDFTProcessor.addConverter(mComplexDecibelConverter); mDFTProcessor.process(SourceEvent.sampleRateChange(sampleRate)); mComplexDecibelConverter.addListener(mSpectrumPanel); - mFrequency = frequency; - TunerChannel tunerChannel = new TunerChannel(frequency, bandwidth); mSource = mTestTuner.getChannelSourceManager().getSource(tunerChannel); @@ -326,18 +360,69 @@ public class ChannelizerViewer extends JFrame public static void main(String[] args) { - int channelsPerRow = 4; + boolean useGUI = true; - final ChannelizerViewer frame = new ChannelizerViewer(channelsPerRow); - - EventQueue.invokeLater(new Runnable() + if(useGUI) { - @Override - public void run() + int channelsPerRow = 4; + + final ChannelizerViewer frame = new ChannelizerViewer(channelsPerRow); + + EventQueue.invokeLater(new Runnable() { - frame.setVisible(true); + @Override + public void run() + { + frame.setVisible(true); + } + }); + } + else + { + TestTuner tuner = new TestTuner(); + + List tunerChannels = getTunerChannels(tuner); + + List sources = new ArrayList<>(); + + int maxSourceCount = 30; + int sourceCount = 0; + for(TunerChannel tunerChannel: tunerChannels) + { + if(sourceCount < maxSourceCount) + { + TunerChannelSource source = tuner.getChannelSourceManager().getSource(tunerChannel); + + if(source != null) + { + sources.add(source); + sourceCount++; + } + else + { + mLog.debug("Couldn't get source for: " + tunerChannel); + } + } } - }); + + mLog.debug("Starting [" + sources.size() + "] tuner channel sources"); + + for(TunerChannelSource tunerChannelSource: sources) + { + tunerChannelSource.setListener(new Listener() + { + @Override + public void receive(ComplexBuffer complexBuffer) + { + //Do nothing + } + }); + + tunerChannelSource.start(); + } + + while(true); + } } } \ No newline at end of file diff --git a/src/main/java/io/github/dsheirer/dsp/filter/channelizer/ComplexPolyphaseChannelizerM2.java b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/ComplexPolyphaseChannelizerM2.java index 8b60fc86..63ec72db 100644 --- a/src/main/java/io/github/dsheirer/dsp/filter/channelizer/ComplexPolyphaseChannelizerM2.java +++ b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/ComplexPolyphaseChannelizerM2.java @@ -197,11 +197,16 @@ public class ComplexPolyphaseChannelizerM2 extends AbstractComplexPolyphaseChann int half = getChannelCount() / 2; + int outputOffset = getChannelCount() - half - 1; + int outputIndex; + int sampleIndex; + int filterIndex; + for(int channel = 0; channel < half; channel++) { - int outputIndex = 2 * (getChannelCount() - channel - 1); - int sampleIndex = 2 * channel; - int filterIndex = channel + half; + outputIndex = 2 * (outputOffset - channel); + sampleIndex = 2 * channel; + filterIndex = channel + half; processed[outputIndex] = filter(sampleIndex, mCoefficients[filterIndex], sampleIndexMap); processed[outputIndex + 1] = filter(sampleIndex + 1, mCoefficients[filterIndex], sampleIndexMap); @@ -209,12 +214,13 @@ public class ComplexPolyphaseChannelizerM2 extends AbstractComplexPolyphaseChann //The second half of the sample buffer was loaded using the previous column pointer - adjust for that now sampleIndexMap = mSampleIndexMap[(mColumnPointer == (mSamples.length - 1) ? 0 : mColumnPointer + 1)]; + outputOffset = getChannelCount() + half - 1; for(int channel = half; channel < getChannelCount(); channel++) { - int outputIndex = 2 * (getChannelCount() - channel - 1); - int sampleIndex = 2 * channel; - int filterIndex = channel - half; + outputIndex = 2 * (outputOffset - channel); + sampleIndex = 2 * channel; + filterIndex = channel - half; processed[outputIndex] = filter(sampleIndex, mCoefficients[filterIndex], sampleIndexMap); processed[outputIndex + 1] = filter(sampleIndex + 1, mCoefficients[filterIndex], sampleIndexMap); diff --git a/src/main/java/io/github/dsheirer/dsp/filter/channelizer/PolyphaseChannelManager.java b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/PolyphaseChannelManager.java index bf2d03ae..e0fc7eed 100644 --- a/src/main/java/io/github/dsheirer/dsp/filter/channelizer/PolyphaseChannelManager.java +++ b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/PolyphaseChannelManager.java @@ -132,7 +132,6 @@ public class PolyphaseChannelManager implements ISourceEventProcessor try { List polyphaseIndexes = mChannelCalculator.getChannelIndexes(tunerChannel); - mLog.debug("Getting polyphase channel for: " + tunerChannel + " indexes:" + polyphaseIndexes); IPolyphaseChannelOutputProcessor outputProcessor = getOutputProcessor(polyphaseIndexes); @@ -240,7 +239,6 @@ public class PolyphaseChannelManager implements ISourceEventProcessor { case NOTIFICATION_FREQUENCY_CHANGE: //Update channel calculator immediately so that channels can be allocated - mLog.debug("Updating channel calculator to frequency: " + sourceEvent.getValue()); mChannelCalculator.setCenterFrequency(sourceEvent.getValue().longValue()); //Defer channelizer configuration changes to be handled on the buffer processor thread @@ -248,7 +246,6 @@ public class PolyphaseChannelManager implements ISourceEventProcessor break; case NOTIFICATION_SAMPLE_RATE_CHANGE: //Update channel calculator immediately so that channels can be allocated - mLog.debug("Updating channel calculator to sample rate: " + sourceEvent.getValue()); mChannelCalculator.setSampleRate(sourceEvent.getValue().doubleValue()); //Defer channelizer configuration changes to be handled on the buffer processor thread @@ -355,13 +352,11 @@ public class PolyphaseChannelManager implements ISourceEventProcessor //If the indexes size is the same then update the current processor, otherwise create a new one if(channelSource.getPolyphaseChannelOutputProcessor().getInputChannelCount() == indexes.size()) { - mLog.debug("Updating indexes to " + indexes + " for: " + channelSource.getTunerChannel()); channelSource.getPolyphaseChannelOutputProcessor().setPolyphaseChannelIndices(indexes); channelSource.setFrequency(centerFrequency); } else { - mLog.debug("Creating new output processor for indexes " + indexes); channelSource.setPolyphaseChannelOutputProcessor(getOutputProcessor(indexes), centerFrequency); } } @@ -487,17 +482,13 @@ public class PolyphaseChannelManager implements ISourceEventProcessor switch(queuedSourceEvent.getEvent()) { case NOTIFICATION_FREQUENCY_CHANGE: - mLog.debug("******Frequency change event"); updateOutputProcessors(queuedSourceEvent); break; case NOTIFICATION_SAMPLE_RATE_CHANGE: - mLog.debug("******Sample Rate change event"); updateChannelizerSampleRate(queuedSourceEvent.getValue().intValue()); break; } - mLog.debug("****** Source event processed ... continuing"); - queuedSourceEvent = mQueuedSourceEvents.poll(); } diff --git a/src/main/java/io/github/dsheirer/dsp/filter/channelizer/PolyphaseChannelResultsBuffer.java b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/PolyphaseChannelResultsBuffer.java index f0ccc96c..ce46030c 100644 --- a/src/main/java/io/github/dsheirer/dsp/filter/channelizer/PolyphaseChannelResultsBuffer.java +++ b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/PolyphaseChannelResultsBuffer.java @@ -72,10 +72,10 @@ public class PolyphaseChannelResultsBuffer { throw new IllegalArgumentException("Buffer is full - cannot add new channel results"); } + if(mPointer == 0) { mChannelResults = new float[mMaxSize][channelResults.length]; - mChannelResults[mPointer++] = channelResults; } else { @@ -87,9 +87,9 @@ public class PolyphaseChannelResultsBuffer "a different length than the channel results that are already contained in this buffer [" + currentLength + "]"); } - - mChannelResults[mPointer++] = channelResults; } + + mChannelResults[mPointer++] = channelResults; } /** @@ -97,10 +97,12 @@ public class PolyphaseChannelResultsBuffer */ public float[][] getChannelResults() { + //If this buffer is full, return all of the channel results if(mPointer == mMaxSize) { return mChannelResults; } + //Otherwise, truncate the results to return else { return Arrays.copyOf(mChannelResults, mPointer); @@ -120,6 +122,7 @@ public class PolyphaseChannelResultsBuffer { throw new IllegalArgumentException("Buffer is empty - cannot extract channel [" + iChannelIndex + "]"); } + if(!isValidChannelIndex(iChannelIndex)) { throw new IllegalArgumentException("Channel [" + iChannelIndex + "] is not valid for the contained channel " + @@ -131,8 +134,10 @@ public class PolyphaseChannelResultsBuffer int qChannelIndex = iChannelIndex + 1; - for(float[] channelResults: mChannelResults) + for(int x = 0; x < mPointer; x++) { + float[] channelResults = mChannelResults[x]; + samples[pointer++] = channelResults[iChannelIndex]; samples[pointer++] = channelResults[qChannelIndex]; } diff --git a/src/main/java/io/github/dsheirer/dsp/filter/channelizer/ScheduledBufferProcessor.java b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/ScheduledBufferProcessor.java index f9b0ab0c..57c3b9fc 100644 --- a/src/main/java/io/github/dsheirer/dsp/filter/channelizer/ScheduledBufferProcessor.java +++ b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/ScheduledBufferProcessor.java @@ -60,15 +60,6 @@ public class ScheduledBufferProcessor implements Listener public ScheduledBufferProcessor(int maximumSize, int resetThreshold, long distributionInterval, int maxBuffersPerInterval) { mQueue = new OverflowableTransferQueue<>(maximumSize, resetThreshold); - - mQueue.setOverflowListener(new IOverflowListener() - { - @Override - public void sourceOverflow(boolean overflow) - { - mLog.error("Overflow state changed - overflow:" + overflow); - } - }); mDistributionInterval = distributionInterval; mMaxBuffersPerInterval = maxBuffersPerInterval; } diff --git a/src/main/java/io/github/dsheirer/dsp/filter/channelizer/output/OneChannelOutputProcessor.java b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/output/OneChannelOutputProcessor.java index d1c1f687..eaff4feb 100644 --- a/src/main/java/io/github/dsheirer/dsp/filter/channelizer/output/OneChannelOutputProcessor.java +++ b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/output/OneChannelOutputProcessor.java @@ -42,7 +42,6 @@ public class OneChannelOutputProcessor extends ChannelOutputProcessor { super(1, sampleRate); setPolyphaseChannelIndices(channelIndexes); - mLog.debug("Sample Rate:" + sampleRate + " PolyPhase Channel Indexes:" + channelIndexes); } /** diff --git a/src/main/java/io/github/dsheirer/source/tuner/TunerController.java b/src/main/java/io/github/dsheirer/source/tuner/TunerController.java index 36b177e9..5a7b4abf 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/TunerController.java +++ b/src/main/java/io/github/dsheirer/source/tuner/TunerController.java @@ -161,12 +161,12 @@ public abstract class TunerController implements Tunable, ISourceEventProcessor, return mFrequencyController.getMaximumFrequency(); } - private long getMinTunedFrequency() throws SourceException + public long getMinTunedFrequency() throws SourceException { return mFrequencyController.getFrequency() - (getUsableBandwidth() / 2); } - private long getMaxTunedFrequency() throws SourceException + public long getMaxTunedFrequency() throws SourceException { return mFrequencyController.getFrequency() + (getUsableBandwidth() / 2); } diff --git a/src/main/java/io/github/dsheirer/source/tuner/test/SampleGenerator.java b/src/main/java/io/github/dsheirer/source/tuner/test/SampleGenerator.java index fd6d322a..c7a4147a 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/test/SampleGenerator.java +++ b/src/main/java/io/github/dsheirer/source/tuner/test/SampleGenerator.java @@ -36,6 +36,7 @@ public class SampleGenerator private int mSweepUpdateInterval; private long mInterval; private int mSamplesPerInterval; + private boolean mReuseBuffers; private ScheduledFuture mScheduledFuture; /** @@ -49,17 +50,17 @@ public class SampleGenerator * @param sweepUpdateRate to turn on sweeping of the tone frequency where the frequency is incremented by the * rate specified at each interval, resetting to 1 Hz after reaching/exceeding the highest frequency. */ - public SampleGenerator(int sampleRate, long frequency, long interval, int sweepUpdateRate) + public SampleGenerator(int sampleRate, long frequency, long interval, int sweepUpdateRate, boolean reuseBuffers) { if(Math.abs(sweepUpdateRate) >= sampleRate) { throw new IllegalArgumentException("Sweep update rate cannot be greater than sample rate"); } - mLog.debug("Sample Generator - Sample Rate:" + sampleRate + " Frequency:" + frequency); mOscillator = new LowPhaseNoiseOscillator(sampleRate, frequency); mInterval = interval; mSweepUpdateInterval = sweepUpdateRate; + mReuseBuffers = reuseBuffers; updateSamplesPerInterval(); } @@ -73,9 +74,9 @@ public class SampleGenerator * @param frequency of the tone produced by the generator * @param interval in milliseconds for generating samples */ - public SampleGenerator(int sampleRate, long frequency, long interval) + public SampleGenerator(int sampleRate, long frequency, long interval, boolean reuseBuffers) { - this(sampleRate, frequency, interval, 0); + this(sampleRate, frequency, interval, 0, reuseBuffers); } /** @@ -84,8 +85,6 @@ public class SampleGenerator private void updateSamplesPerInterval() { mSamplesPerInterval = (int)((double)mOscillator.getSampleRate() * ((double)mInterval / 1000.0)); - - mLog.debug("Sample Generator - " + mSamplesPerInterval + " samples every " + mInterval + "ms"); } /** @@ -175,35 +174,65 @@ public class SampleGenerator updateSamplesPerInterval(); } + public double getSampleRate() + { + return mOscillator.getSampleRate(); + } + /** * Generates a complex sample buffer and distributes the buffer to a registered listener */ public class Generator implements Runnable { + private int mTriggerInterval = 0; + private long mOscillatorFrequency = 1; + private ComplexBuffer mReusableBuffer; + @Override public void run() { if(mComplexBufferBroadcaster.hasListeners()) { - float[] samples = mOscillator.generate(mSamplesPerInterval); + if(mReuseBuffers) + { + if(mReusableBuffer == null || + mReusableBuffer.getSamples().length != mSamplesPerInterval || + mOscillatorFrequency != mOscillator.getFrequency()) + { + mOscillatorFrequency = mOscillator.getFrequency(); + mReusableBuffer = new TimestampedComplexBuffer(mOscillator.generate(mSamplesPerInterval)); + } - mComplexBufferBroadcaster.receive(new TimestampedComplexBuffer(samples)); + mComplexBufferBroadcaster.receive(mReusableBuffer); + } + else + { + float[] samples = mOscillator.generate(mSamplesPerInterval); + mComplexBufferBroadcaster.receive(new TimestampedComplexBuffer(samples)); + } if(mSweepUpdateInterval != 0) { - long updatedFrequency = mOscillator.getFrequency() + mSweepUpdateInterval; + mTriggerInterval++; - if(updatedFrequency > mOscillator.getSampleRate() / 2) + if(mTriggerInterval >= 10) { - mOscillator.setFrequency(mOscillator.getSampleRate() / -2); - } - else if(updatedFrequency < mOscillator.getSampleRate() / -2) - { - mOscillator.setFrequency(mOscillator.getSampleRate() / 2); - } - else - { - mOscillator.setFrequency(updatedFrequency); + mTriggerInterval = 0; + + long updatedFrequency = mOscillator.getFrequency() + mSweepUpdateInterval; + + if(updatedFrequency > mOscillator.getSampleRate() / 2) + { + mOscillator.setFrequency(mOscillator.getSampleRate() / -2); + } + else if(updatedFrequency < mOscillator.getSampleRate() / -2) + { + mOscillator.setFrequency(mOscillator.getSampleRate() / 2); + } + else + { + mOscillator.setFrequency(updatedFrequency); + } } } } diff --git a/src/main/java/io/github/dsheirer/source/tuner/test/TestTunerController.java b/src/main/java/io/github/dsheirer/source/tuner/test/TestTunerController.java index 9867f2c2..b9e6f3c8 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/test/TestTunerController.java +++ b/src/main/java/io/github/dsheirer/source/tuner/test/TestTunerController.java @@ -30,17 +30,18 @@ public class TestTunerController extends TunerController { private final static Logger mLog = LoggerFactory.getLogger(TestTunerController.class); - public static final long MINIMUM_FREQUENCY = 1000000l; //1 MHz - public static final long MAXIMUM_FREQUENCY = 10000000000l; //10 GHz - public static final long DEFAULT_CENTER_FREQUENCY = 100000000; //100 MHz - public static final int DEFAULT_SAMPLE_RATE = 100000; //25 kHz - public static final int DC_NOISE_BANDWIDTH = 0; // +/-5 kHz - public static final double USABLE_BANDWIDTH = 1.00; //100% usable bandwidth + public static final long MINIMUM_FREQUENCY = 1l; + public static final long MAXIMUM_FREQUENCY = 1000000000l; + public static final int SAMPLE_RATE = 225000; + public static final int DC_NOISE_BANDWIDTH = 0; + public static final double USABLE_BANDWIDTH = 1.00; + public static final int SPECTRAL_FRAME_RATE = 20; public static final long SAMPLE_GENERATION_INTERVAL = 1000 / SPECTRAL_FRAME_RATE; private SampleGenerator mSampleGenerator; - private long mReferenceFrequency = DEFAULT_CENTER_FREQUENCY; + private boolean mReuseBuffers = true; + private long mFrequency = 100000000l; /** * Tuner controller testing implementation. @@ -50,15 +51,14 @@ public class TestTunerController extends TunerController super(MINIMUM_FREQUENCY, MAXIMUM_FREQUENCY, DC_NOISE_BANDWIDTH, USABLE_BANDWIDTH); int sweepRate = 0; //Hz per interval - - mSampleGenerator = new SampleGenerator(DEFAULT_SAMPLE_RATE, 12500, SAMPLE_GENERATION_INTERVAL, sweepRate); + long initialToneFrequency = SAMPLE_RATE / 2 + 100; + mSampleGenerator = new SampleGenerator(SAMPLE_RATE, initialToneFrequency, SAMPLE_GENERATION_INTERVAL, + sweepRate, mReuseBuffers); try { - mLog.debug("Setting frequency to:" + DEFAULT_CENTER_FREQUENCY); - mFrequencyController.setFrequency(DEFAULT_CENTER_FREQUENCY); - mLog.debug("Setting sample rate of freq controller to: " + DEFAULT_SAMPLE_RATE); - mFrequencyController.setSampleRate(DEFAULT_SAMPLE_RATE); + mFrequencyController.setFrequency(mFrequency); + mFrequencyController.setSampleRate(SAMPLE_RATE); } catch(Exception e) { @@ -91,7 +91,7 @@ public class TestTunerController extends TunerController @Override public long getTunedFrequency() throws SourceException { - return mReferenceFrequency; + return mFrequency; } /** @@ -102,26 +102,26 @@ public class TestTunerController extends TunerController @Override public void setTunedFrequency(long frequency) throws SourceException { - mReferenceFrequency = frequency; + mFrequency = frequency; } /** - * Sets the tone frequency for the sample generator. The tone frequency is offset from the center tuned frequency. - * @param toneFrequency + * Sets the tone output of the frequency generator + * @param frequency in the range: 0 <> Sample Rate */ - public void setToneFrequency(long toneFrequency) + public void setToneFrequency(long frequency) { - long frequency = toneFrequency - mReferenceFrequency; mSampleGenerator.setFrequency(frequency); } /** - * Current tone frequency for this test controller. - * @return + * Frequency of the tone being generated + * + * @return tone frequency in range: 0 <> Sample Rate */ public long getToneFrequency() { - return mReferenceFrequency + mSampleGenerator.getFrequency(); + return mSampleGenerator.getFrequency(); } /** @@ -130,7 +130,7 @@ public class TestTunerController extends TunerController @Override public double getCurrentSampleRate() { - return mFrequencyController.getSampleRate(); + return mSampleGenerator.getSampleRate(); } /** @@ -138,6 +138,6 @@ public class TestTunerController extends TunerController */ public void setSampleRate(int sampleRate) throws SourceException { - mFrequencyController.setSampleRate(sampleRate); + mSampleGenerator.setSampleRate(sampleRate); } }