diff --git a/src/dsp/filter/FilterFactory.java b/src/dsp/filter/FilterFactory.java index eac69592..0dff4b73 100644 --- a/src/dsp/filter/FilterFactory.java +++ b/src/dsp/filter/FilterFactory.java @@ -834,9 +834,10 @@ public class FilterFactory if(logResults) { mLog.debug("Polyphase TunerChannelizer Filter Design Summary"); - mLog.debug("-------------------------------------------"); + mLog.debug("-----------------------------------------------------"); + mLog.debug("Window Type: " + windowType.name()); mLog.debug("Channels: " + channels); - mLog.debug("Taps Per Channel: " + tapsPerChannel); + mLog.debug("Taps Per Channel: " + tapsPerChannel + " (Requested:" + tapsPerChannel + ")"); mLog.debug("Filter Length: " + (taps.length + 1)); mLog.debug("Channel Bandwidth: " + channelBandwidth); mLog.debug("Cutoff Frequency: " + (sampleRate * cutoffFrequency)); diff --git a/src/dsp/filter/channelizer/AbstractComplexPolyphaseChannelizer.java b/src/dsp/filter/channelizer/AbstractComplexPolyphaseChannelizer.java index 814a1b92..41cce4d0 100644 --- a/src/dsp/filter/channelizer/AbstractComplexPolyphaseChannelizer.java +++ b/src/dsp/filter/channelizer/AbstractComplexPolyphaseChannelizer.java @@ -26,6 +26,7 @@ import sample.complex.ComplexBuffer; import source.ISourceEventListener; import source.SourceEvent; +import java.util.Arrays; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; diff --git a/src/dsp/filter/channelizer/ChannelIndexCalculator.java b/src/dsp/filter/channelizer/ChannelIndexCalculator.java index 9970bcdf..d7b6f28b 100644 --- a/src/dsp/filter/channelizer/ChannelIndexCalculator.java +++ b/src/dsp/filter/channelizer/ChannelIndexCalculator.java @@ -187,6 +187,9 @@ public class ChannelIndexCalculator */ public List getPolyphaseChannelIndexes(TunerChannel tunerChannel) throws IllegalArgumentException { + long min = getMinimumFrequency(); + long max = getMaximumFrequency(); + if(tunerChannel.getMinFrequency() < getMinimumFrequency() || tunerChannel.getMaxFrequency() > getMaximumFrequency()) { diff --git a/src/dsp/filter/channelizer/ChannelizerViewer.java b/src/dsp/filter/channelizer/ChannelizerViewer.java index e1f721f0..950a5913 100644 --- a/src/dsp/filter/channelizer/ChannelizerViewer.java +++ b/src/dsp/filter/channelizer/ChannelizerViewer.java @@ -32,6 +32,7 @@ import source.tuner.TunerChannel; import source.tuner.configuration.TunerConfigurationModel; import source.tuner.test.TestTuner; import spectrum.DFTProcessor; +import spectrum.DFTResultsListener; import spectrum.DFTSize; import spectrum.SpectrumPanel; import spectrum.converter.ComplexDecibelConverter; @@ -56,8 +57,6 @@ public class ChannelizerViewer extends JFrame private ChannelArrayPanel mChannelPanel; private int mChannelCount; private int mChannelsPerRow; - private int mFilterTapsPerChannel; - private int mSampleRate; private long mToneFrequency; private DFTSize mMainPanelDFTSize = DFTSize.FFT32768; private DFTSize mChannelPanelDFTSize = DFTSize.FFT00512; @@ -66,19 +65,15 @@ public class ChannelizerViewer extends JFrame /** * GUI Test utility for researching polyphase channelizers. * - * @param channelCount * @param channelsPerRow */ - public ChannelizerViewer(int channelCount, int channelsPerRow, int tapsPerChannel) + public ChannelizerViewer(int channelsPerRow) { - mChannelCount = channelCount; - mChannelsPerRow = channelsPerRow; - mFilterTapsPerChannel = tapsPerChannel; - - mSampleRate = mChannelCount * CHANNEL_BANDWIDTH; - mTestTuner = new TestTuner(); + mChannelCount = mTestTuner.getSampleRate() / CHANNEL_BANDWIDTH; + mChannelsPerRow = channelsPerRow; + mToneFrequency = mTestTuner.getTunerController().getFrequency() + (int)(CHANNEL_BANDWIDTH * 1); //Set to channel 1 as default @@ -115,7 +110,7 @@ public class ChannelizerViewer extends JFrame { if(mPrimarySpectrumPanel == null) { - mPrimarySpectrumPanel = new PrimarySpectrumPanel(mSettingsManager, mSampleRate); + mPrimarySpectrumPanel = new PrimarySpectrumPanel(mSettingsManager, mTestTuner.getSampleRate()); mPrimarySpectrumPanel.setPreferredSize(new Dimension(1000, 200)); mPrimarySpectrumPanel.setDFTSize(mMainPanelDFTSize); mTestTuner.addListener(mPrimarySpectrumPanel); @@ -132,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 = mSampleRate / 2; + int maximumFrequencyOffset = mTestTuner.getSampleRate() / 2; long minimumFrequency = mTestTuner.getTunerController().getFrequency() - maximumFrequencyOffset; long maximumFrequency = mTestTuner.getTunerController().getFrequency() + maximumFrequencyOffset; @@ -154,9 +149,9 @@ public class ChannelizerViewer extends JFrame mControlPanel.add(spinner); mControlPanel.add(new JLabel("Hz")); - mControlPanel.add(new JLabel("Sample Rate: " + mSampleRate + " Hz"), "push,align right"); + mControlPanel.add(new JLabel("Sample Rate: " + mTestTuner.getSampleRate() + " Hz"), "push,align right"); - mControlPanel.add(new JLabel("Max: " + (mSampleRate / 2) + " Hz"), "push,align right"); + mControlPanel.add(new JLabel("Max: " + (mTestTuner.getSampleRate() / 2) + " Hz"), "push,align right"); mControlPanel.add(new JLabel("Channels: " + mChannelCount), "push,align right"); } @@ -196,15 +191,20 @@ public class ChannelizerViewer extends JFrame { setLayout(new MigLayout("insets 0 0 0 0", "fill", "fill")); - long spectralBandwidth = mChannelCount * CHANNEL_BANDWIDTH; + long spectralBandwidth = mTestTuner.getSampleRate(); long halfSpectralBandwidth = spectralBandwidth / 2; + int channelToLog = -1; + for(int x = 0; x < mChannelCount; x++) { //place the channels left and right of 10.0 MHz long frequency = mTestTuner.getTunerController().getFrequency() + - ((x * CHANNEL_BANDWIDTH) - halfSpectralBandwidth); - ChannelPanel channelPanel = new ChannelPanel(mSettingsManager, CHANNEL_BANDWIDTH * 2, frequency, CHANNEL_BANDWIDTH); + ((x * CHANNEL_BANDWIDTH) - halfSpectralBandwidth) + (CHANNEL_BANDWIDTH / 2); + + mLog.debug("Channel " + x + " Frequency: " + frequency); + + ChannelPanel channelPanel = new ChannelPanel(mSettingsManager, CHANNEL_BANDWIDTH * 2, frequency, CHANNEL_BANDWIDTH, (x == channelToLog)); channelPanel.setDFTSize(mChannelPanelDFTSize); if(x % mChannelsPerRow == mChannelsPerRow - 1) @@ -260,8 +260,9 @@ 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, int sampleRate, long frequency, int bandwidth) + public ChannelPanel(SettingsManager settingsManager, int sampleRate, long frequency, int bandwidth, boolean enableLogging) { setLayout(new MigLayout("insets 0 0 0 0", "[grow,fill]", "[grow,fill]")); mSpectrumPanel = new SpectrumPanel(settingsManager); @@ -269,14 +270,32 @@ public class ChannelizerViewer extends JFrame mDFTProcessor.addConverter(mComplexDecibelConverter); mDFTProcessor.process(SourceEvent.sampleRateChange(sampleRate)); + mComplexDecibelConverter.addListener(mSpectrumPanel); + mFrequency = frequency; + TunerChannel tunerChannel = new TunerChannel(frequency, bandwidth); mSource = (PolyphaseChannelSource)mTestTuner.getChannelSource(tunerChannel); if(mSource != null) { - mSource.setListener(mDFTProcessor); + if(enableLogging) + { + mSource.setListener(new Listener() + { + @Override + public void receive(ComplexBuffer complexBuffer) + { + mLog.debug("Sending samples to DFT processor: " + complexBuffer.getSamples().length); + mDFTProcessor.receive(complexBuffer); + } + }); + } + else + { + mSource.setListener(mDFTProcessor); + } mSource.start(ThreadPool.SCHEDULED); } else @@ -298,6 +317,7 @@ public class ChannelizerViewer extends JFrame @Override public void receive(ComplexBuffer complexBuffer) { + mLog.debug("Got a buffer for the channel dft processor"); mDFTProcessor.receive(complexBuffer); } @@ -310,11 +330,9 @@ public class ChannelizerViewer extends JFrame public static void main(String[] args) { - int channelCount = 160; - int channelsPerRow = 20; - int tapsPerChannel = 17; + int channelsPerRow = 4; - final ChannelizerViewer frame = new ChannelizerViewer(channelCount, channelsPerRow, tapsPerChannel); + final ChannelizerViewer frame = new ChannelizerViewer(channelsPerRow); EventQueue.invokeLater(new Runnable() { diff --git a/src/dsp/filter/channelizer/ComplexPolyphaseChannelizerM2.java b/src/dsp/filter/channelizer/ComplexPolyphaseChannelizerM2.java index dd9f32a1..114b3417 100644 --- a/src/dsp/filter/channelizer/ComplexPolyphaseChannelizerM2.java +++ b/src/dsp/filter/channelizer/ComplexPolyphaseChannelizerM2.java @@ -77,14 +77,14 @@ public class ComplexPolyphaseChannelizerM2 extends AbstractComplexPolyphaseChann * * @param sampleRate to be channelized. */ - private ComplexPolyphaseChannelizerM2(int sampleRate) throws FilterDesignException + private ComplexPolyphaseChannelizerM2(int sampleRate, int tapsPerFilter) throws FilterDesignException { super(sampleRate / DEFAULT_CHANNEL_BANDWIDTH, DEFAULT_CHANNEL_BANDWIDTH); mLog.debug("Sample Rate: " + sampleRate); float[] filterTaps = FilterFactory.getSincChannelizer(getChannelSampleRate(), getChannelCount(), - 15, Window.WindowType.BLACKMAN_HARRIS_7, true); + tapsPerFilter, Window.WindowType.BLACKMAN_HARRIS_7, true); initFilters(filterTaps); } @@ -97,7 +97,7 @@ public class ComplexPolyphaseChannelizerM2 extends AbstractComplexPolyphaseChann * @return channelizer * @throws IllegalArgumentException if the sample rate argument is not an even multiple of 25.0 kHz */ - public static ComplexPolyphaseChannelizerM2 create(int sampleRate) throws IllegalArgumentException, + public static ComplexPolyphaseChannelizerM2 create(int sampleRate, int tapsPerFilter) throws IllegalArgumentException, FilterDesignException { if(sampleRate % (2 * DEFAULT_CHANNEL_BANDWIDTH) != 0) @@ -105,7 +105,7 @@ public class ComplexPolyphaseChannelizerM2 extends AbstractComplexPolyphaseChann throw new IllegalArgumentException("Sample rate must be an even multiple of 25 kHz"); } - return new ComplexPolyphaseChannelizerM2(sampleRate); + return new ComplexPolyphaseChannelizerM2(sampleRate, tapsPerFilter); } /** diff --git a/src/dsp/filter/channelizer/PolyphaseChannelManager.java b/src/dsp/filter/channelizer/PolyphaseChannelManager.java index b640ddec..6bd797af 100644 --- a/src/dsp/filter/channelizer/PolyphaseChannelManager.java +++ b/src/dsp/filter/channelizer/PolyphaseChannelManager.java @@ -41,6 +41,7 @@ public class PolyphaseChannelManager implements Listener private static final int CHANNEL_BANDWIDTH = 12500; private static final double CHANNEL_OVERSAMPLING = 2.0; 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 List mChannelSources = new CopyOnWriteArrayList<>(); @@ -232,7 +233,9 @@ public class PolyphaseChannelManager implements Listener { if(mPolyphaseChannelizer == null) { - mPolyphaseChannelizer = ComplexPolyphaseChannelizerM2.create(sampleRate); + mPolyphaseChannelizer = ComplexPolyphaseChannelizerM2.create(sampleRate, + POLYPHASE_FILTER_TAPS_PER_CHANNEL); + mBufferProcessor.setListener(mPolyphaseChannelizer); } else diff --git a/src/dsp/filter/channelizer/PolyphaseChannelSource.java b/src/dsp/filter/channelizer/PolyphaseChannelSource.java index 993b2b87..89a7e0df 100644 --- a/src/dsp/filter/channelizer/PolyphaseChannelSource.java +++ b/src/dsp/filter/channelizer/PolyphaseChannelSource.java @@ -32,6 +32,7 @@ import source.SourceEvent; import source.SourceException; import source.tuner.TunerChannel; +import java.util.Arrays; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; @@ -67,15 +68,29 @@ public class PolyphaseChannelSource extends ComplexSource implements ISourceEven mPolyphaseChannelOutputProcessor = outputProcessor; mUpstreamSourceEventHandler = new UpstreamSourceEventHandler(externalEventListener);; mChannelSampleRate = channelSampleRate; + } /** * Registers the listener to receive complex sample buffers from this channel source */ @Override - public void setListener(Listener listener) + public void setListener(final Listener listener) { - mComplexBufferAssembler.setListener(listener); +// mComplexBufferAssembler.setListener(listener); + + mComplexBufferAssembler.setListener(new Listener() + { + @Override + public void receive(ComplexBuffer complexBuffer) + { + if(mTunerChannel.getFrequency() == 99981250) + { +// mLog.debug(Arrays.toString(complexBuffer.getSamples())); + } + listener.receive(complexBuffer); + } + }); } /** diff --git a/src/dsp/filter/channelizer/ScheduledBufferProcessor.java b/src/dsp/filter/channelizer/ScheduledBufferProcessor.java index d1513219..17635504 100644 --- a/src/dsp/filter/channelizer/ScheduledBufferProcessor.java +++ b/src/dsp/filter/channelizer/ScheduledBufferProcessor.java @@ -18,6 +18,8 @@ ******************************************************************************/ package dsp.filter.channelizer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import sample.Listener; import sample.OverflowableTransferQueue; import sample.real.IOverflowListener; @@ -31,6 +33,8 @@ import java.util.concurrent.atomic.AtomicBoolean; public class ScheduledBufferProcessor implements Listener { + private final static Logger mLog = LoggerFactory.getLogger(ScheduledBufferProcessor.class); + private OverflowableTransferQueue mQueue; private Listener mListener; private ScheduledFuture mScheduledFuture; @@ -59,6 +63,15 @@ 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/dsp/filter/channelizer/output/ChannelOutputProcessor.java b/src/dsp/filter/channelizer/output/ChannelOutputProcessor.java index cf04d434..653be07a 100644 --- a/src/dsp/filter/channelizer/output/ChannelOutputProcessor.java +++ b/src/dsp/filter/channelizer/output/ChannelOutputProcessor.java @@ -33,14 +33,14 @@ public abstract class ChannelOutputProcessor implements IPolyphaseChannelOutputP { private final static Logger mLog = LoggerFactory.getLogger(ChannelOutputProcessor.class); - private static final int OVERFLOW_THRESHOLD = 25000 * 3; //25 kHz * 3 seconds - private static final int RESET_THRESHOLD = (int)(OVERFLOW_THRESHOLD * .25); //reset at 25% - private OverflowableTransferQueue mChannelResultsQueue; private List mChannelResultsToProcess = new ArrayList<>(); + private int mMaxResultsToProcess; private int mInputChannelCount; +//TODO: swap this out and use the LowPhaseNoiseOscillator private Oscillator mFrequencyCorrectionMixer; + private boolean mFrequencyCorrectionEnabled; /** * Base class for polyphase channelizer output channel processing. Provides built-in frequency translation @@ -54,7 +54,9 @@ public abstract class ChannelOutputProcessor implements IPolyphaseChannelOutputP { mInputChannelCount = inputChannelCount; mFrequencyCorrectionMixer = new Oscillator(0, sampleRate); - mChannelResultsQueue = new OverflowableTransferQueue<>(OVERFLOW_THRESHOLD, RESET_THRESHOLD); + mMaxResultsToProcess = sampleRate / 10 * 2; //process at 100 millis interval, twice the expected inflow rate + + mChannelResultsQueue = new OverflowableTransferQueue<>(sampleRate * 3, (int)(sampleRate * 0.5)); } @Override @@ -72,33 +74,27 @@ public abstract class ChannelOutputProcessor implements IPolyphaseChannelOutputP { try { - mChannelResultsQueue.drainTo(mChannelResultsToProcess, 1000); + int count = mChannelResultsQueue.drainTo(mChannelResultsToProcess, mMaxResultsToProcess); - while(!mChannelResultsToProcess.isEmpty()) + if(count > 0) { - for(float[] channelResults: mChannelResultsToProcess) - { - process(channelResults, listener); - } - - mChannelResultsToProcess.clear(); - mChannelResultsQueue.drainTo(mChannelResultsToProcess, 1000); + process(mChannelResultsToProcess, listener); } } catch(Throwable throwable) { mLog.error("Error while processing polyphase channel samples", throwable); - mChannelResultsToProcess.clear(); } + mChannelResultsToProcess.clear(); } /** * Sub-class implementation to process one polyphase channelizer result array. - * @param channelResult to process + * @param channelResults to process * @param listener to receive the channel complex sample */ - public abstract void process(float[] channelResult, ComplexSampleListener listener); + public abstract void process(List channelResults, ComplexSampleListener listener); /** @@ -133,16 +129,8 @@ public abstract class ChannelOutputProcessor implements IPolyphaseChannelOutputP */ protected float getFrequencyCorrectedInphase(float inphase, float quadrature) { - //Only apply frequency correction if the oscillator is set to a non-zero frequency offset - if(mFrequencyCorrectionMixer.enabled()) - { - return Complex.multiplyInphase(inphase, quadrature, mFrequencyCorrectionMixer.inphase(), - mFrequencyCorrectionMixer.quadrature()); - } - else - { - return inphase; - } + return Complex.multiplyInphase(inphase, quadrature, mFrequencyCorrectionMixer.inphase(), + mFrequencyCorrectionMixer.quadrature()); } /** @@ -155,16 +143,8 @@ public abstract class ChannelOutputProcessor implements IPolyphaseChannelOutputP */ protected float getFrequencyCorrectedQuadrature(float inphase, float quadrature) { - //Only apply frequency correction if the oscillator is set to a non-zero frequency offset - if(mFrequencyCorrectionMixer.enabled()) - { - return Complex.multiplyQuadrature(inphase, quadrature, mFrequencyCorrectionMixer.inphase(), - mFrequencyCorrectionMixer.quadrature()); - } - else - { - return quadrature; - } + return Complex.multiplyQuadrature(inphase, quadrature, mFrequencyCorrectionMixer.inphase(), + mFrequencyCorrectionMixer.quadrature()); } /** @@ -177,6 +157,16 @@ public abstract class ChannelOutputProcessor implements IPolyphaseChannelOutputP public void setFrequencyCorrection(long frequencyCorrection) { mFrequencyCorrectionMixer.setFrequency(frequencyCorrection); + + mFrequencyCorrectionEnabled = (frequencyCorrection != 0); + } + + /** + * Indicates if this channel has a frequency correction offset + */ + public boolean hasFrequencyCorrection() + { + return mFrequencyCorrectionEnabled; } } diff --git a/src/dsp/filter/channelizer/output/OneChannelOutputProcessor.java b/src/dsp/filter/channelizer/output/OneChannelOutputProcessor.java index a03ee59a..44f5f396 100644 --- a/src/dsp/filter/channelizer/output/OneChannelOutputProcessor.java +++ b/src/dsp/filter/channelizer/output/OneChannelOutputProcessor.java @@ -18,12 +18,17 @@ ******************************************************************************/ package dsp.filter.channelizer.output; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import sample.complex.ComplexSampleListener; +import java.util.Arrays; import java.util.List; public class OneChannelOutputProcessor extends ChannelOutputProcessor { + private final static Logger mLog = LoggerFactory.getLogger(OneChannelOutputProcessor.class); + private int mChannelOffset; /** @@ -37,6 +42,7 @@ public class OneChannelOutputProcessor extends ChannelOutputProcessor { super(1, sampleRate); setPolyphaseChannelIndices(channelIndexes); + mLog.debug("Sample Rate:" + sampleRate + " PolyPhase Channel Indexes:" + channelIndexes); } /** @@ -61,21 +67,31 @@ public class OneChannelOutputProcessor extends ChannelOutputProcessor * Extract the channel from the channel results array, apply frequency translation, and deliver the * extracted frequency-corrected channel I/Q sample set to the complex sample listener. * - * @param channels to process containing an array of channel I/Q sample pairs (I0,Q0,I1,Q1...In,Qn) + * @param channelResults to process containing a list of channel array of I/Q sample pairs (I0,Q0,I1,Q1...In,Qn) * @param listener to receive the extracted, frequency-translated channel results */ @Override - public void process(float[] channels, ComplexSampleListener listener) + public void process(List channelResults, ComplexSampleListener listener) { - if(channels.length < mChannelOffset + 1) + for(float[] channels: channelResults) { - throw new IllegalArgumentException("Polyphase channelizer output channels array is not large enough to " + - "cover this single channel output processor with channel offset [" + mChannelOffset + "]"); + if(channels.length < mChannelOffset + 1) + { + throw new IllegalArgumentException("Polyphase channelizer output channels array is not large enough to " + + "cover this single channel output processor with channel offset [" + mChannelOffset + "]"); + } + + float i = channels[mChannelOffset]; + float q = channels[mChannelOffset + 1]; + + if(hasFrequencyCorrection()) + { + listener.receive(getFrequencyCorrectedInphase(i, q), getFrequencyCorrectedQuadrature(i, q)); + } + else + { + listener.receive(i, q); + } } - - float i = channels[mChannelOffset]; - float q = channels[mChannelOffset + 1]; - - listener.receive(getFrequencyCorrectedInphase(i, q), getFrequencyCorrectedQuadrature(i, q)); } } diff --git a/src/dsp/filter/channelizer/output/TwoChannelOutputProcessor.java b/src/dsp/filter/channelizer/output/TwoChannelOutputProcessor.java index a9ffd404..ba0c3e15 100644 --- a/src/dsp/filter/channelizer/output/TwoChannelOutputProcessor.java +++ b/src/dsp/filter/channelizer/output/TwoChannelOutputProcessor.java @@ -18,12 +18,16 @@ ******************************************************************************/ package dsp.filter.channelizer.output; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import sample.complex.ComplexSampleListener; import java.util.List; public class TwoChannelOutputProcessor extends ChannelOutputProcessor { + private final static Logger mLog = LoggerFactory.getLogger(TwoChannelOutputProcessor.class); + private int mChannelOffset1; private int mChannelOffset2; @@ -66,12 +70,13 @@ public class TwoChannelOutputProcessor extends ChannelOutputProcessor * Extract the channel from the channel results array, apply frequency translation, and deliver the * extracted frequency-corrected channel I/Q sample set to the complex sample listener. * - * @param channels to process containing an array of channel I/Q sample pairs (I0,Q0,I1,Q1...In,Qn) + * @param channelResults to process containing an array of channel I/Q sample pairs (I0,Q0,I1,Q1...In,Qn) * @param listener to receive the extracted, frequency-translated channel results */ @Override - public void process(float[] channels, ComplexSampleListener listener) + public void process(List channelResults, ComplexSampleListener listener) { + mLog.debug("I got channel results - doing nothing !!"); // if(channels.length < mChannelOffset + 1) // { // throw new IllegalArgumentException("Polyphase channelizer output channels array is not large enough to " + diff --git a/src/sample/OverflowableTransferQueue.java b/src/sample/OverflowableTransferQueue.java index 2deeca07..f37314a4 100644 --- a/src/sample/OverflowableTransferQueue.java +++ b/src/sample/OverflowableTransferQueue.java @@ -74,6 +74,14 @@ public class OverflowableTransferQueue } } + /** + * Removes and returns a single element from the head of the queue or null if the queue is empty + */ + public E poll() + { + return mQueue.poll(); + } + /** * Retrieves elements from the queue into the collection up to the maximum number of elements specified */ diff --git a/src/source/tuner/Tuner.java b/src/source/tuner/Tuner.java index 8657ac27..aac3297e 100644 --- a/src/source/tuner/Tuner.java +++ b/src/source/tuner/Tuner.java @@ -168,6 +168,14 @@ public abstract class Tuner implements ITunerChannelProvider 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). */ diff --git a/src/source/tuner/test/SampleGenerator.java b/src/source/tuner/test/SampleGenerator.java index 723482c3..0c52a338 100644 --- a/src/source/tuner/test/SampleGenerator.java +++ b/src/source/tuner/test/SampleGenerator.java @@ -19,6 +19,8 @@ package source.tuner.test; import dsp.mixer.LowPhaseNoiseOscillator; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import sample.Listener; import sample.complex.ComplexBuffer; import util.ThreadPool; @@ -28,12 +30,40 @@ import java.util.concurrent.TimeUnit; public class SampleGenerator { + private final static Logger mLog = LoggerFactory.getLogger(SampleGenerator.class); + private LowPhaseNoiseOscillator mOscillator; + private int mSweepUpdateInterval; private Listener mListener; private long mInterval; private int mSamplesPerInterval; private ScheduledFuture mScheduledFuture; + /** + * Generates complex sample buffers at the specified sample rate with a unity gain tone at the specified + * frequency. This generator runs via a scheduled thread pool and generates samples at the specified time + * interval. + * + * @param sampleRate of the complex samples + * @param frequency of the tone produced by the generator + * @param interval in milliseconds for generating samples + * @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) + { + if(Math.abs(sweepUpdateRate) >= sampleRate) + { + throw new IllegalArgumentException("Sweep update rate cannot be greater than sample rate"); + } + + mOscillator = new LowPhaseNoiseOscillator(sampleRate, frequency); + mInterval = interval; + mSweepUpdateInterval = sweepUpdateRate; + + updateSamplesPerInterval(); + } + /** * Generates complex sample buffers at the specified sample rate with a unity gain tone at the specified * frequency. This generator runs via a scheduled thread pool and generates samples at the specified time @@ -45,10 +75,7 @@ public class SampleGenerator */ public SampleGenerator(int sampleRate, long frequency, long interval) { - mOscillator = new LowPhaseNoiseOscillator(sampleRate, frequency); - mInterval = interval; - - updateSamplesPerInterval(); + this(sampleRate, frequency, interval, 0); } /** @@ -56,7 +83,9 @@ public class SampleGenerator */ private void updateSamplesPerInterval() { - mSamplesPerInterval = (int)((double)mOscillator.getSampleRate() * ((double)mInterval / 1000.0) * 2.0); + mSamplesPerInterval = (int)((double)mOscillator.getSampleRate() * ((double)mInterval / 1000.0)); + + mLog.debug("Sample Generator: " + mSamplesPerInterval + " samples every " + mInterval + " ms."); } /** @@ -141,6 +170,24 @@ public class SampleGenerator { float[] samples = mOscillator.generate(mSamplesPerInterval); mListener.receive(new ComplexBuffer(samples)); + + if(mSweepUpdateInterval != 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/source/tuner/test/TestTuner.java b/src/source/tuner/test/TestTuner.java index e80657f2..476c6174 100644 --- a/src/source/tuner/test/TestTuner.java +++ b/src/source/tuner/test/TestTuner.java @@ -49,9 +49,6 @@ public class TestTuner extends Tuner { super("Test Tuner", new TestTunerController()); - mLog.debug("Initializing test tuner. Tuner Controller's sample rate is:" + getTunerController().getSampleRate()); - mLog.debug("Initializing test tuner. Test Tuner Controller's sample rate is:" + getTestTunerController().getSampleRate()); - mPolyphaseChannelManager = new PolyphaseChannelManager(this); } diff --git a/src/source/tuner/test/TestTunerController.java b/src/source/tuner/test/TestTunerController.java index 9878202e..3f1c6093 100644 --- a/src/source/tuner/test/TestTunerController.java +++ b/src/source/tuner/test/TestTunerController.java @@ -33,7 +33,7 @@ public class TestTunerController extends TunerController public static final long MINIMUM_FREQUENCY = 1000000l; //1 MHz public static final long MAXIMUM_FREQUENCY = 10000000000l; //10 GHz public static final long DEFAULT_FREQUENCY = 100000000; //100 MHz - public static final int DEFAULT_SAMPLE_RATE = 1000000; //1 MHz + public static final int DEFAULT_SAMPLE_RATE = 50000; //25 kHz public static final int DC_NOISE_BANDWIDTH = 5000; // +/-5 kHz public static final double USABLE_BANDWIDTH = 0.90; //90% usable bandwidth - 5% unusable on either end @@ -46,7 +46,9 @@ public class TestTunerController extends TunerController { super(MINIMUM_FREQUENCY, MAXIMUM_FREQUENCY, DC_NOISE_BANDWIDTH, USABLE_BANDWIDTH); - mSampleGenerator = new SampleGenerator(DEFAULT_SAMPLE_RATE, DEFAULT_FREQUENCY, 100); + int sweepRate = 0; //Hz per interval + + mSampleGenerator = new SampleGenerator(DEFAULT_SAMPLE_RATE, DEFAULT_FREQUENCY, 100, sweepRate); try {