#39 WIP polyphase channelizers.

This commit is contained in:
Denny
2017-11-18 05:50:00 -05:00
parent 5281679b7f
commit b3e4bd6a9c
16 changed files with 217 additions and 90 deletions

View File

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

View File

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

View File

@@ -187,6 +187,9 @@ public class ChannelIndexCalculator
*/
public List<Integer> getPolyphaseChannelIndexes(TunerChannel tunerChannel) throws IllegalArgumentException
{
long min = getMinimumFrequency();
long max = getMaximumFrequency();
if(tunerChannel.getMinFrequency() < getMinimumFrequency() ||
tunerChannel.getMaxFrequency() > getMaximumFrequency())
{

View File

@@ -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<ComplexBuffer>()
{
@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()
{

View File

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

View File

@@ -41,6 +41,7 @@ public class PolyphaseChannelManager implements Listener<SourceEvent>
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<PolyphaseChannelSource> mChannelSources = new CopyOnWriteArrayList<>();
@@ -232,7 +233,9 @@ public class PolyphaseChannelManager implements Listener<SourceEvent>
{
if(mPolyphaseChannelizer == null)
{
mPolyphaseChannelizer = ComplexPolyphaseChannelizerM2.create(sampleRate);
mPolyphaseChannelizer = ComplexPolyphaseChannelizerM2.create(sampleRate,
POLYPHASE_FILTER_TAPS_PER_CHANNEL);
mBufferProcessor.setListener(mPolyphaseChannelizer);
}
else

View File

@@ -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<ComplexBuffer> listener)
public void setListener(final Listener<ComplexBuffer> listener)
{
mComplexBufferAssembler.setListener(listener);
// mComplexBufferAssembler.setListener(listener);
mComplexBufferAssembler.setListener(new Listener<ComplexBuffer>()
{
@Override
public void receive(ComplexBuffer complexBuffer)
{
if(mTunerChannel.getFrequency() == 99981250)
{
// mLog.debug(Arrays.toString(complexBuffer.getSamples()));
}
listener.receive(complexBuffer);
}
});
}
/**

View File

@@ -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<E> implements Listener<E>
{
private final static Logger mLog = LoggerFactory.getLogger(ScheduledBufferProcessor.class);
private OverflowableTransferQueue<E> mQueue;
private Listener<E> mListener;
private ScheduledFuture<?> mScheduledFuture;
@@ -59,6 +63,15 @@ public class ScheduledBufferProcessor<E> implements Listener<E>
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;
}

View File

@@ -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<float[]> mChannelResultsQueue;
private List<float[]> 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<float[]> 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;
}
}

View File

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

View File

@@ -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<float[]> 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 " +

View File

@@ -74,6 +74,14 @@ public class OverflowableTransferQueue<E>
}
}
/**
* 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
*/

View File

@@ -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).
*/

View File

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

View File

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

View File

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