mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-12 08:37:25 -05:00
#39 WIP polyphase channelizers.
This commit is contained in:
@@ -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));
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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())
|
||||
{
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 " +
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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).
|
||||
*/
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user