wip-channelizer

This commit is contained in:
Denny
2017-04-21 12:17:55 -04:00
parent ab31b40124
commit 7c6b7494b8
50 changed files with 2360 additions and 1670 deletions

View File

@@ -567,7 +567,7 @@ public class FilterFactory
* For 40db attenuation, calculate the number of symbols based on the following formula:
* Symbols = -44 * alpha + 33
*
* Polyphase Channelizer notes:
* Polyphase TunerChannelizer notes:
* -Set samples Per Symbol at 2 (or more) * number of channels
* -Set symbolCount to sufficient size to produce required attenuation
* -Set symbol rate in hertz
@@ -725,7 +725,7 @@ public class FilterFactory
}
/**
* Creates a windowed-sync (Nyquist) M/2 prototype FIR filter for use with a Polyphase Channelizer/Synthesizer.
* Creates a windowed-sync (Nyquist) M/2 prototype FIR filter for use with a Polyphase TunerChannelizer/Synthesizer.
* The filter is designed for x2 oversampling of each channel and the filter cutoff frequency is incrementally
* adjusted to achieve a -6.02 dB attenuation at the channel band edge to enable perfect reconstruction of adjacent
* channels.
@@ -833,7 +833,7 @@ public class FilterFactory
if(logResults)
{
mLog.debug("Polyphase Channelizer Filter Design Summary");
mLog.debug("Polyphase TunerChannelizer Filter Design Summary");
mLog.debug("-------------------------------------------");
mLog.debug("Channels: " + channels);
mLog.debug("Taps Per Channel: " + tapsPerChannel);

View File

@@ -0,0 +1,128 @@
/*******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2017 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
******************************************************************************/
package dsp.filter.channelizer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sample.Broadcaster;
import sample.Listener;
import sample.complex.ComplexBuffer;
import source.ISourceEventListener;
import source.SourceEvent;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
public abstract class AbstractComplexPolyphaseChannelizer implements Listener<ComplexBuffer>, ISourceEventListener
{
private final static Logger mLog = LoggerFactory.getLogger(AbstractComplexPolyphaseChannelizer.class);
private Broadcaster<SourceEvent> mSourceChangeBroadcaster = new Broadcaster();
private List<PolyphaseChannelSource> mChannels = new CopyOnWriteArrayList<>();
private int mChannelCount;
private int mChannelSampleRate;
/**
* Complex sample polyphase channelizer
*
* @param channelCount
*/
public AbstractComplexPolyphaseChannelizer(int channelCount, int channelSampleRate)
{
mChannelCount = channelCount;
mChannelSampleRate = channelSampleRate;
}
/**
* Number of channels being processed by this channelizer.
*/
public int getChannelCount()
{
return mChannelCount;
}
/**
* Output sample rate for each channel
* @return sample rate in hertz
*/
public int getChannelSampleRate()
{
return mChannelSampleRate;
}
/**
* Filters the complex sample pair
*
* @param inphase sample
* @param quadrature sample
*/
protected abstract void filter(float inphase, float quadrature);
@Override
public void receive(ComplexBuffer complexBuffer)
{
float[] samples = complexBuffer.getSamples();
for(int x = 0; x < samples.length; x += 2)
{
filter(samples[x], samples[x + 1]);
}
}
/**
* Dispatches the processed channel samples to any registered polyphase channel outputs.
*
* @param channels containing an array of I/Q samples per channel
*/
protected void dispatch(float[] channels)
{
for(PolyphaseChannelSource channel : mChannels)
{
channel.processChannels(channels);
}
}
/**
* Adds the polyphase channel source to receive processed output channel samples
*
* @param polyphaseChannelSource
*/
public void addChannel(PolyphaseChannelSource polyphaseChannelSource)
{
mChannels.add(polyphaseChannelSource);
mSourceChangeBroadcaster.addListener(polyphaseChannelSource.getSourceEventListener());
}
/**
* Removes the polyphase channel source from receiving output channel samples.
*
* @param polyphaseChannelSource
*/
public void removeChannel(PolyphaseChannelSource polyphaseChannelSource)
{
mChannels.remove(polyphaseChannelSource);
mSourceChangeBroadcaster.removeListener(polyphaseChannelSource.getSourceEventListener());
}
@Override
public Listener<SourceEvent> getSourceEventListener()
{
return mSourceChangeBroadcaster;
}
}

View File

@@ -0,0 +1,135 @@
/*******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2017 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
******************************************************************************/
package dsp.filter.channelizer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sample.Listener;
import source.ISourceEventProcessor;
import source.Source;
import source.SourceEvent;
import source.tuner.Tuner;
import source.tuner.TunerChannel;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
public class Channelizer implements ISourceEventProcessor, Listener<SourceEvent>
{
private final static Logger mLog = LoggerFactory.getLogger(Channelizer.class);
private Tuner mTuner;
private List<PolyphaseChannelSource> mChannelSources = new CopyOnWriteArrayList<>();
/**
* Provides access to Digital Drop Channel (DDC) sources from the tuner. Incorporates a polyphase channelizer
* with downstream channel and upstream tuner management.
*
* @param tuner providing broadband sample buffers
*/
public Channelizer(Tuner tuner)
{
mTuner = tuner;
mTuner.addSourceEventListener(this);
}
public Source getChannel(TunerChannel tunerChannel)
{
//TODO: return a polyphase channel here
return null;
}
/**
* Starts/adds the channel source to receive channelized sample buffers, registering with the tuner to receive
* sample buffers when this is the first channel.
*
* @param channelSource to start
*/
private void startChannelSource(PolyphaseChannelSource channelSource)
{
if(mChannelSources.isEmpty())
{
//TODO: start the tuner
}
mChannelSources.add(channelSource);
}
/**
* Stops/removes the channel source from receiving channelized sample buffers and deregisters from the tuner
* if this is the last channel being sourced.
*
* @param channelSource to stop
*/
private void stopChannelSource(PolyphaseChannelSource channelSource)
{
mChannelSources.remove(channelSource);
if(mChannelSources.isEmpty())
{
//TODO: stop the tuner
}
if(mTuner != null)
{
//TODO: change the tuner to accept a remove(TunerChannel) method
// mTuner.remove(channel.getTunerChannel());
}
}
@Override
public void process(SourceEvent sourceEvent)
{
switch(sourceEvent.getEvent())
{
case REQUEST_START_SAMPLE_STREAM:
if(sourceEvent.hasSource() && sourceEvent.getSource() instanceof PolyphaseChannelSource)
{
startChannelSource((PolyphaseChannelSource)sourceEvent.getSource());
}
else
{
mLog.error("Request to stop sample stream for unrecognized source: " +
(sourceEvent.hasSource() ? sourceEvent.getSource().getClass() : "null source"));
}
//TODO: add the channel and register for samples from the tuner
break;
case REQUEST_STOP_SAMPLE_STREAM:
if(sourceEvent.hasSource() && sourceEvent.getSource() instanceof PolyphaseChannelSource)
{
stopChannelSource((PolyphaseChannelSource)sourceEvent.getSource());
}
else
{
mLog.error("Request to stop sample stream for unrecognized source: " +
(sourceEvent.hasSource() ? sourceEvent.getSource().getClass() : "null source"));
}
break;
}
}
@Override
public void receive(SourceEvent sourceEvent)
{
}
}

View File

@@ -20,7 +20,6 @@ package dsp.filter.channelizer;
import dsp.filter.FilterFactory;
import dsp.filter.Window;
import dsp.filter.correction.IQCorrectionFilter;
import dsp.filter.design.FilterDesignException;
import dsp.mixer.LowPhaseNoiseOscillator;
import net.miginfocom.swing.MigLayout;
@@ -31,7 +30,7 @@ import sample.SampleType;
import sample.complex.ComplexBuffer;
import settings.SettingsManager;
import source.tuner.configuration.TunerConfigurationModel;
import source.tuner.frequency.FrequencyChangeEvent;
import source.SourceEvent;
import spectrum.DFTProcessor;
import spectrum.DFTSize;
import spectrum.SpectrumPanel;
@@ -86,7 +85,7 @@ public class ChannelizerViewer extends JFrame
private void init()
{
setTitle("Channelizer Viewer");
setTitle("TunerChannelizer Viewer");
setSize(1000, 800);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLayout(new MigLayout("insets 0 0 0 0", "[grow,fill]", "[grow,fill]"));
@@ -212,7 +211,7 @@ public class ChannelizerViewer extends JFrame
public class ChannelArrayPanel extends JPanel implements Listener<ComplexBuffer>
{
private ComplexPolyphaseChannelizerM2 mPolyphaseChannelizer;
private ComplexPolyphaseChannelizer mPolyphaseChannelizer;
private ChannelDistributor mChannelDistributor;
public ChannelArrayPanel(float[] taps)
@@ -223,7 +222,7 @@ public class ChannelizerViewer extends JFrame
bufferSize++;
}
mPolyphaseChannelizer = new ComplexPolyphaseChannelizerM2(taps, mChannelCount);
mPolyphaseChannelizer = new ComplexPolyphaseChannelizer(taps, mChannelCount, CHANNEL_BANDWIDTH);
mChannelDistributor = new ChannelDistributor(bufferSize, mChannelCount);
mPolyphaseChannelizer.setChannelDistributor(mChannelDistributor);
@@ -272,8 +271,7 @@ public class ChannelizerViewer extends JFrame
add(mSpectrumPanel);
mDFTProcessor.addConverter(mComplexDecibelConverter);
mDFTProcessor.frequencyChanged(new FrequencyChangeEvent(
FrequencyChangeEvent.Event.NOTIFICATION_SAMPLE_RATE_CHANGE, sampleRate));
mDFTProcessor.process(SourceEvent.sampleRateChange(sampleRate));
mComplexDecibelConverter.addListener(mSpectrumPanel);
}

View File

@@ -25,44 +25,40 @@ import dsp.mixer.Oscillator;
import org.jtransforms.fft.FloatFFT_1D;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sample.Listener;
import sample.complex.ComplexBuffer;
import java.text.DecimalFormat;
public class ComplexPolyphaseChannelizer implements Listener<ComplexBuffer>
public class ComplexPolyphaseChannelizer extends AbstractComplexPolyphaseChannelizer
{
private final static Logger mLog = LoggerFactory.getLogger(ComplexPolyphaseChannelizer.class);
private RealFIRFilter[] mInphaseFilters;
private RealFIRFilter[] mQuadratureFilters;
private int mFilterPointer = 0;
private int mChannelCount;
private float[] mFilteredSamples;
private FloatFFT_1D mFFT;
private ChannelDistributor mChannelDistributor;
/**
* Maximally Decimated Polyphase Filter Bank (MDPFB) channelizer that divides the input frequency band into
* equal bandwidth channels.
*
* @param taps of a low-pass filter designed for the inbound sample rate with a cutoff frequency
* @param coefficients of a prototype low-pass filter designed for the inbound sample rate with a cutoff frequency
* equal to the channel bandwidth (sample rate / filters). If you need to synthesize (combine two or more
* filte outputs) a new bandwidth signal from the outputs of this filter, then the filter should be designed
* filter outputs) a new bandwidth signal from the outputs of this filter, then the filter should be designed
* as a nyquist filter with -6 dB attenuation at the channel bandwidth cutoff frequency
*
* @param channels - number of channels to output.
* @param channelCount - number of channels to output.
* @param channelSampleRate - channel sample rate (after decimation)
*/
public ComplexPolyphaseChannelizer(float[] taps, int channels)
public ComplexPolyphaseChannelizer(float[] coefficients, int channelCount, int channelSampleRate)
{
mChannelCount = channels;
super(channelCount, channelSampleRate);
initFilters(taps);
initFilters(coefficients);
}
public void filter(float inphase, float quadrature)
@Override
protected void filter(float inphase, float quadrature)
{
int index = (mChannelCount - mFilterPointer - 1) * 2;
int index = (getChannelCount() - mFilterPointer - 1) * 2;
mFilteredSamples[index] = mInphaseFilters[mFilterPointer].filter(inphase);
mFilteredSamples[index + 1] = mQuadratureFilters[mFilterPointer].filter(quadrature);
@@ -70,7 +66,7 @@ public class ComplexPolyphaseChannelizer implements Listener<ComplexBuffer>
if(mFilterPointer == 0)
{
calculate();
mFilterPointer = mChannelCount - 1;
mFilterPointer = getChannelCount() - 1;
}
else
{
@@ -78,20 +74,9 @@ public class ComplexPolyphaseChannelizer implements Listener<ComplexBuffer>
}
}
@Override
public void receive(ComplexBuffer complexBuffer)
{
float[] samples = complexBuffer.getSamples();
for(int x = 0; x < samples.length; x += 2)
{
filter(samples[x], samples[x + 1]);
}
}
private void calculate()
{
float[] samples = new float[mChannelCount * 2];
float[] samples = new float[getChannelCount() * 2];
System.arraycopy(mFilteredSamples, 0, samples, 0, mFilteredSamples.length);
//FFT is executed in-place where the output overwrites the input
@@ -100,24 +85,6 @@ public class ComplexPolyphaseChannelizer implements Listener<ComplexBuffer>
dispatch(samples);
}
private void dispatch(float[] channels)
{
if(mChannelDistributor != null)
{
mChannelDistributor.receive(channels);
}
}
public void setChannelDistributor(ChannelDistributor channelDistributor)
{
mChannelDistributor = channelDistributor;
}
private double magnitude(float real, float imaginary, int fftSize)
{
return Math.sqrt(Math.pow(real, 2.0) + Math.pow(imaginary, 2.0));
}
/**
* Distributes the filter taps to each of the polyphase filters
*
@@ -125,15 +92,15 @@ public class ComplexPolyphaseChannelizer implements Listener<ComplexBuffer>
*/
private void initFilters(float[] taps)
{
int tapCount = (int)Math.ceil((double)taps.length / (double)mChannelCount);
int tapCount = (int)Math.ceil((double)taps.length / (double)getChannelCount());
float[][] coefficients = new float[mChannelCount][tapCount];
float[][] coefficients = new float[getChannelCount()][tapCount];
for(int tap = 0; tap < tapCount; tap++)
{
for(int filter = 0; filter < mChannelCount; filter++)
for(int filter = 0; filter < getChannelCount(); filter++)
{
int index = tap * mChannelCount + filter;
int index = tap * getChannelCount() + filter;
if(index < taps.length)
{
@@ -142,18 +109,18 @@ public class ComplexPolyphaseChannelizer implements Listener<ComplexBuffer>
}
}
mInphaseFilters = new RealFIRFilter[mChannelCount];
mQuadratureFilters = new RealFIRFilter[mChannelCount];
mInphaseFilters = new RealFIRFilter[getChannelCount()];
mQuadratureFilters = new RealFIRFilter[getChannelCount()];
for(int filter = 0; filter < mChannelCount; filter++)
for(int filter = 0; filter < getChannelCount(); filter++)
{
mInphaseFilters[filter] = new RealFIRFilter(coefficients[filter], 1.0f);
mQuadratureFilters[filter] = new RealFIRFilter(coefficients[filter], 1.0f);
}
mFilteredSamples = new float[mChannelCount * 2];
mFFT = new FloatFFT_1D(mChannelCount);
mFilterPointer = mChannelCount -1;
mFilteredSamples = new float[getChannelCount() * 2];
mFFT = new FloatFFT_1D(getChannelCount());
mFilterPointer = getChannelCount() -1;
}
public static void main(String[] args)
@@ -171,14 +138,14 @@ public class ComplexPolyphaseChannelizer implements Listener<ComplexBuffer>
int channelCenter = (int)(12500 * 1.5);
StringBuilder sb = new StringBuilder();
sb.append("\nPolyphase Channelizer\n");
sb.append("\nPolyphase TunerChannelizer\n");
sb.append("Sample Rate:" + sampleRate + " Channels:" + channels + " Channel Rate:" + channelBandwidth + "\n");
sb.append("Tap Count:" + taps.length + "\n");
sb.append("Channel:" + channelCenter);
mLog.debug(sb.toString());
ComplexPolyphaseChannelizer channelizer = new ComplexPolyphaseChannelizer(taps, 8);
ComplexPolyphaseChannelizer channelizer = new ComplexPolyphaseChannelizer(taps, 8, channelBandwidth);
Oscillator oscillator = new Oscillator(channelCenter, 2 * sampleRate);

View File

@@ -25,21 +25,18 @@ import dsp.mixer.Oscillator;
import org.jtransforms.fft.FloatFFT_1D;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sample.Listener;
import sample.complex.ComplexBuffer;
import java.text.DecimalFormat;
public class ComplexPolyphaseChannelizerM2 implements Listener<ComplexBuffer>
public class ComplexPolyphaseChannelizerM2 extends AbstractComplexPolyphaseChannelizer
{
private final static Logger mLog = LoggerFactory.getLogger(ComplexPolyphaseChannelizerM2.class);
private ReverseFloatCircularBuffer[] mDataBuffers;
private float[][] mFilterCoefficients;
private int mFilterPointer = 0;
private int mChannelCount;
private FloatFFT_1D mFFT;
private ChannelDistributor mChannelDistributor;
private DecimalFormat DECIMAL_FORMAT = new DecimalFormat("0.0000");
@@ -51,23 +48,23 @@ public class ComplexPolyphaseChannelizerM2 implements Listener<ComplexBuffer>
* equal to the channel bandwidth (sample rate / filters). If you need to synthesize (combine two or more
* filte outputs) a new bandwidth signal from the outputs of this filter, then the filter should be designed
* as a nyquist filter with -6 dB attenuation at the channel bandwidth cutoff frequency
*
* @param channels - number of filters/channels to output. Since this filter bank oversamples each filter
* output, this number must be even (divisible by the oversample rate).
* @param channelSampleRate for each decimated, oversampled channel
*/
public ComplexPolyphaseChannelizerM2(float[] taps, int channels)
public ComplexPolyphaseChannelizerM2(float[] taps, int channels, int channelSampleRate)
{
super(channels, channelSampleRate);
if(channels % 2 != 0)
{
throw new IllegalArgumentException("Channel count must be a multiple of 2");
}
mChannelCount = channels;
initFilters(taps);
}
public void filter(float inphase, float quadrature)
protected void filter(float inphase, float quadrature)
{
try
{
@@ -77,9 +74,9 @@ public class ComplexPolyphaseChannelizerM2 implements Listener<ComplexBuffer>
if(mFilterPointer == 0)
{
calculate();
mFilterPointer = mChannelCount * 2;
mFilterPointer = getChannelCount() * 2;
}
else if(mFilterPointer == mChannelCount)
else if(mFilterPointer == getChannelCount())
{
calculate();
}
@@ -92,20 +89,9 @@ public class ComplexPolyphaseChannelizerM2 implements Listener<ComplexBuffer>
}
}
@Override
public void receive(ComplexBuffer complexBuffer)
{
float[] samples = complexBuffer.getSamples();
for(int x = 0; x < samples.length; x += 2)
{
filter(samples[x], samples[x + 1]);
}
}
private void calculate()
{
float[] samples = new float[mChannelCount * 2];
float[] samples = new float[getChannelCount() * 2];
if(mFilterPointer == 0)
{
@@ -158,6 +144,7 @@ public class ComplexPolyphaseChannelizerM2 implements Listener<ComplexBuffer>
/**
* Filters the samples contained in the buffer against the filter coefficients
*
* @param coefficients of the sub filter
* @param buffer containing data samples
* @return
@@ -166,32 +153,14 @@ public class ComplexPolyphaseChannelizerM2 implements Listener<ComplexBuffer>
{
float accumulator = 0.0f;
for( int x = 0; x < coefficients.length; x++ )
for(int x = 0; x < coefficients.length; x++)
{
accumulator += coefficients[ x ] * buffer.get(x);
accumulator += coefficients[x] * buffer.get(x);
}
return accumulator;
}
private void dispatch(float[] channels)
{
if(mChannelDistributor != null)
{
mChannelDistributor.receive(channels);
}
}
public void setChannelDistributor(ChannelDistributor channelDistributor)
{
mChannelDistributor = channelDistributor;
}
private double magnitude(float real, float imaginary, int fftSize)
{
return Math.sqrt(Math.pow(real, 2.0) + Math.pow(imaginary, 2.0));
}
/**
* Distributes the filter taps to each of the polyphase filters
*
@@ -199,22 +168,22 @@ public class ComplexPolyphaseChannelizerM2 implements Listener<ComplexBuffer>
*/
private void initFilters(float[] taps)
{
int tapCount = (int)Math.ceil((double)taps.length / (double)mChannelCount);
int tapCount = (int)Math.ceil((double)taps.length / (double)getChannelCount());
mDataBuffers = new ReverseFloatCircularBuffer[mChannelCount * 2];
mDataBuffers = new ReverseFloatCircularBuffer[getChannelCount() * 2];
for(int x = 0; x < mChannelCount * 2; x++)
for(int x = 0; x < getChannelCount() * 2; x++)
{
mDataBuffers[x] = new ReverseFloatCircularBuffer(tapCount);
}
mFilterCoefficients = new float[mChannelCount][tapCount];
mFilterCoefficients = new float[getChannelCount()][tapCount];
for(int tap = 0; tap < tapCount; tap++)
{
for(int filter = 0; filter < mChannelCount; filter++)
for(int filter = 0; filter < getChannelCount(); filter++)
{
int index = tap * mChannelCount + filter;
int index = tap * getChannelCount() + filter;
if(index < taps.length)
{
@@ -223,8 +192,8 @@ public class ComplexPolyphaseChannelizerM2 implements Listener<ComplexBuffer>
}
}
mFFT = new FloatFFT_1D(mChannelCount);
mFilterPointer = mChannelCount - 1; //Start at M/2
mFFT = new FloatFFT_1D(getChannelCount());
mFilterPointer = getChannelCount() - 1; //Start at M/2
}
public static void main(String[] args)
@@ -237,19 +206,19 @@ public class ComplexPolyphaseChannelizerM2 implements Listener<ComplexBuffer>
int channelBandwidth = 12500;
int sampleRate = channels * channelBandwidth;
float[] taps = FilterFactory.getLowPass( 10000000, 12500, 5600, Window.WindowType.BLACKMAN_HARRIS_7 );
float[] taps = FilterFactory.getLowPass(10000000, 12500, 5600, Window.WindowType.BLACKMAN_HARRIS_7);
int channelCenter = (int)(12500 * 1.5);
StringBuilder sb = new StringBuilder();
sb.append("\nPolyphase Channelizer\n");
sb.append("\nPolyphase TunerChannelizer\n");
sb.append("Sample Rate:" + sampleRate + " Channels:" + channels + " Channel Rate:" + channelBandwidth + "\n");
sb.append("Tap Count:" + taps.length + "\n");
sb.append("Channel:" + channelCenter);
mLog.debug(sb.toString());
ComplexPolyphaseChannelizerM2 channelizer = new ComplexPolyphaseChannelizerM2(taps, 8);
ComplexPolyphaseChannelizerM2 channelizer = new ComplexPolyphaseChannelizerM2(taps, 8, channelBandwidth * 2);
Oscillator oscillator = new Oscillator(channelCenter, 2 * sampleRate);

View File

@@ -0,0 +1,201 @@
/*******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2017 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
******************************************************************************/
package dsp.filter.channelizer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sample.Listener;
import sample.complex.ComplexBuffer;
import source.ISourceEventProcessor;
import source.SourceEvent;
import source.SourceException;
import source.tuner.ComplexChannelSource;
import source.tuner.TunerChannel;
import java.util.concurrent.ScheduledExecutorService;
public class PolyphaseChannelSource extends ComplexChannelSource
{
private final static Logger mLog = LoggerFactory.getLogger(PolyphaseChannelSource.class);
private TunerChannel mTunerChannel;
private long mTunerCenterFrequency;
private int mUpstreamSampleRate;
private int mChannelSampleRate;
private int mChannelCount;
private int mOutputFrequencyCorrection;
/**
* Polyphase channelizer channel implementation. Adapts the channel array output samples from the polyphase
* channelizer into a single channel, or a channel synthesized from two adjacent channels that is frequency
* translated and decimated to a single channel.
*
* @param tunerChannel - requested output tuner channel frequency and bandwidth.
*/
public PolyphaseChannelSource(ISourceEventProcessor upstreamSourceEventProcessor, TunerChannel tunerChannel)
{
super(upstreamSourceEventProcessor);
mTunerChannel = tunerChannel;
}
/**
* Process source event notifications received from upstream source provider and source event requests
* received from downstream consumers.
*
* @param sourceEvent containing a request or notification
* @throws SourceException if there are any errors while processing the source event
*/
@Override
public void process(SourceEvent sourceEvent) throws SourceException
{
switch(sourceEvent.getEvent())
{
case NOTIFICATION_CHANNEL_SAMPLE_RATE_CHANGE:
setChannelSampleRate(sourceEvent.getValue().intValue());
break;
case NOTIFICATION_FREQUENCY_CHANGE:
mTunerCenterFrequency = sourceEvent.getValue().longValue();
updateInputConfiguration();
break;
case NOTIFICATION_SAMPLE_RATE_CHANGE:
setUpstreamSampleRate(sourceEvent.getValue().intValue());
updateInputConfiguration();
break;
case REQUEST_CHANNEL_FREQUENCY_CORRECTION_CHANGE:
setFrequencyCorrection(sourceEvent.getValue().intValue());
break;
}
}
/**
* Process the array samples for each of the output channels from the polyphase channelizer.
* @param channels
*/
public void processChannels(float[] channels)
{
//TODO we have to dispatch full buffers on a separate thread so that we don't bog down the channelizer
//TODO thread that is delivering the channel samples to us.
}
/**
* Updates current processing configuration any time there is a change in tuner center frequency
* or tuner sample rate.
*/
private void updateInputConfiguration()
{
}
/**
* Downstream sample rate
*
* @return sample rate in Hertz
* @throws SourceException if this method is accessed before the channel count is established
*/
@Override
public int getSampleRate() throws SourceException
{
if(mChannelSampleRate > 0)
{
return mChannelSampleRate;
}
throw new SourceException("Channel sample rate has not been set.");
}
/**
* Sets the downstream channel sample rate. Since oversampling may occur in the upstream channelizer, we can't
* rely on simply dividing the upstream sample rate by the number of channels. Thus, we required the upstream
* channelizer to tell us the exact channel sample rate.
*
* @param channelSampleRate in hertz
*/
private void setChannelSampleRate(int channelSampleRate)
{
mChannelSampleRate = channelSampleRate;
//Translate the upstream channel sample rate event into a downstream sample rate change event
broadcast(SourceEvent.sampleRateChange(mChannelSampleRate));
}
/**
* Sets the input sample rate and broadcasts a source change event to the downstream listener
* @param sampleRate from the upstream source
*/
private void setUpstreamSampleRate(int sampleRate)
{
mUpstreamSampleRate = sampleRate;
try
{
broadcast(SourceEvent.sampleRateChange(getSampleRate()));
}
catch(SourceException se)
{
mLog.error("Upstream sample rate changed - couldn't broadcast channel sample rate change event " +
"to downstream channel", se);
}
}
/**
* Sets the polyphase channel count.
* @param channelCount number of channels being processed by the polyphase channelizer
*/
private void setChannelCount(int channelCount)
{
mChannelCount = channelCount;
try
{
broadcast(SourceEvent.sampleRateChange(getSampleRate()));
}
catch(SourceException se)
{
mLog.error("Polyphase channel count changed - couldn't broadcast channel sample rate change event " +
"to downstream channel", se);
}
}
@Override
public long getFrequency() throws SourceException
{
return mTunerChannel.getFrequency();
}
private void setFrequencyCorrection(int correction)
{
//TODO:
mLog.debug("Request for frequency correction: " + correction + " -- not yet implemented");
}
@Override
public void reset()
{
}
@Override
public void dispose()
{
}
}

View File

@@ -151,7 +151,7 @@ public class FIRFilterSpecification
}
/**
* Creates a Polyphase Channelizer filter specification builder that allows you to define the filter parameters and
* Creates a Polyphase TunerChannelizer filter specification builder that allows you to define the filter parameters and
* create a filter specification
*/
public static ChannelizerBuilder channelizerBuilder()

View File

@@ -23,7 +23,7 @@ import dsp.filter.fir.FIRFilterSpecification;
public class PolyphaseChannelizerDesigner extends FIRFilterSpecification
{
/**
* Constructs a Polyphase Channelizer Remez filter specification.
* Constructs a Polyphase TunerChannelizer Remez filter specification.
*
* Constructor is package private -- use the PolyphaseChannelizerFilterFactory to create a filter
*

View File

@@ -43,7 +43,7 @@ import java.util.List;
import sample.Listener;
import sample.complex.Complex;
import sample.complex.ComplexSampleListener;
import source.tuner.frequency.IFrequencyChangeListener;
import source.ISourceEventListener;
import buffer.FloatAveragingBuffer;
import dsp.filter.interpolator.RealInterpolator;
import dsp.symbol.Dibit;
@@ -73,7 +73,7 @@ public class LSMDemodulator implements Instrumentable, ComplexSampleListener
private CostasLoop mCostasLoop = new CostasLoop();
private IFrequencyChangeListener mFrequencyChangeListener;
private ISourceEventListener mFrequencyChangeListener;
private EyeDiagramDataTap mEyeDiagramDataTap;
@@ -147,12 +147,12 @@ public class LSMDemodulator implements Instrumentable, ComplexSampleListener
}
}
public void addListener( IFrequencyChangeListener listener )
public void addListener( ISourceEventListener listener )
{
mFrequencyChangeListener = listener;
}
public void removeListener( IFrequencyChangeListener listener )
public void removeListener( ISourceEventListener listener )
{
mFrequencyChangeListener = null;
}
@@ -202,8 +202,8 @@ public class LSMDemodulator implements Instrumentable, ComplexSampleListener
// if( correctionNeeded && mFrequencyChangeListener != null )
// {
// mLog.debug( "Issuing Frequency Correction: " + mFrequencyError );
// mFrequencyChangeListener.frequencyChanged(
// new FrequencyChangeEvent( Attribute.FREQUENCY_ERROR,
// mFrequencyChangeListener.process(
// new SourceEvent( Attribute.FREQUENCY_ERROR,
// mFrequencyError ) );
// }
//