WIP - polyphase channelizer

This commit is contained in:
Denny
2017-10-30 19:12:14 -04:00
parent 2c3be02939
commit 0181926fe3
8 changed files with 159 additions and 53 deletions

10
.idea/vcs.xml generated
View File

@@ -1,5 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="IssueNavigationConfiguration">
<option name="links">
<list>
<IssueNavigationLink>
<option name="issueRegexp" value="#(\d+)" />
<option name="linkRegexp" value="https://github.com/DSheirer/sdrtrunk/issues/$1" />
</IssueNavigationLink>
</list>
</option>
</component>
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>

View File

@@ -25,9 +25,9 @@ import source.tuner.TunerChannel;
import java.util.ArrayList;
import java.util.List;
public class ChannelizerConfiguration
public class ChannelIndexCalculator
{
private final static Logger mLog = LoggerFactory.getLogger(ChannelizerConfiguration.class);
private final static Logger mLog = LoggerFactory.getLogger(ChannelIndexCalculator.class);
private long mCenterFrequency;
private int mChannelCount;
@@ -35,16 +35,15 @@ public class ChannelizerConfiguration
private double mOversampling = 1.0;
/**
* Configuration details for a polyphase channelizer that can be used to identify which polyphase
* channelizer output channel(s) should be processed to produce an output channel of a specified
* center frequency and channel sample rate/bandwidth.
* Calculates the channel index(es) from a polyphase channelizer output to source a DDC polyphase channel source
* with a specified channel center frequency and bandwidth.
*
* @param centerFrequency
* @param channelCount
* @param channelBandwidth
* @param oversampling set to 1.0 for no oversample, or greater than 1.0 for oversampling
*/
public ChannelizerConfiguration(long centerFrequency, int channelCount, int channelBandwidth, double oversampling)
public ChannelIndexCalculator(long centerFrequency, int channelCount, int channelBandwidth, double oversampling)
{
mCenterFrequency = centerFrequency;
mChannelCount = channelCount;
@@ -289,19 +288,19 @@ public class ChannelizerConfiguration
int channelCount = 10;
ChannelizerConfiguration channelizerConfiguration =
new ChannelizerConfiguration(100062500, channelCount, 12500, 2.0);
ChannelIndexCalculator channelIndexCalculator =
new ChannelIndexCalculator(100062500, channelCount, 12500, 2.0);
for(int x = 0; x < channelCount; x++)
{
mLog.debug("Channel " + x + ": " + channelizerConfiguration.getMinimumFrequencyForChannelIndex(x) + " - " +
channelizerConfiguration.getMaximumFrequencyForChannelIndex(x) +
" Center: " + channelizerConfiguration.getCenterFrequencyForChannelIndex(x));
mLog.debug("Channel " + x + ": " + channelIndexCalculator.getMinimumFrequencyForChannelIndex(x) + " - " +
channelIndexCalculator.getMaximumFrequencyForChannelIndex(x) +
" Center: " + channelIndexCalculator.getCenterFrequencyForChannelIndex(x));
}
TunerChannel tunerChannel = new TunerChannel(100006250, 12500);
List<Integer> indices = channelizerConfiguration.getPolyphaseChannelIndexes(tunerChannel);
List<Integer> indices = channelIndexCalculator.getPolyphaseChannelIndexes(tunerChannel);
mLog.debug("Indices: " + indices);

View File

@@ -32,7 +32,6 @@ import source.tuner.TunerChannel;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicInteger;
public class PolyphaseChannelManager implements ISourceEventProcessor, Listener<SourceEvent>
{
@@ -43,12 +42,16 @@ public class PolyphaseChannelManager implements ISourceEventProcessor, Listener<
private Tuner mTuner;
private List<PolyphaseChannelSource> mChannelSources = new CopyOnWriteArrayList<>();
private ChannelizerConfiguration mChannelizerConfiguration;
private ChannelIndexCalculator mChannelIndexCalculator;
private ComplexPolyphaseChannelizerM2 mPolyphaseChannelizer;
/**
* Provides access to Digital Drop Channel (DDC) sources from a tuner. Incorporates a polyphase channelizer
* with downstream channel and upstream tuner management.
* Polyphase Channel Manager is a DDC channel manager for a tuner. This class provides DDC polyphase channel
* sources and wraps a polyphase channelizer processing sample buffers produced by the tuner and distributing
* channelized sample buffers to each allocated DDC polyphase channel source. This class is responsible for
* monitoring the tuner for changes in center frequency and/or sample rate and updating active DDC polyphase channel
* sources accordingly. This class also monitors source event requests and notifications received from active DDC
* polyphase channel sources to adjust sample streams as required.
*
* @param tuner providing broadband sample buffers
*/
@@ -74,8 +77,8 @@ public class PolyphaseChannelManager implements ISourceEventProcessor, Listener<
long frequency = mTuner.getTunerController().getFrequency();
mChannelizerConfiguration = new ChannelizerConfiguration(frequency, channelCount,
CHANNEL_BANDWIDTH, CHANNEL_OVERSAMPLING);
mChannelIndexCalculator = new ChannelIndexCalculator(frequency, channelCount, CHANNEL_BANDWIDTH,
CHANNEL_OVERSAMPLING);
mTuner.addSourceEventListener(this);
}
@@ -90,41 +93,42 @@ public class PolyphaseChannelManager implements ISourceEventProcessor, Listener<
{
PolyphaseChannelSource channelSource = null;
IPolyphaseChannelOutputProcessor outputProcessor = null;
try
{
List<Integer> polyphaseIndexes = mChannelizerConfiguration.getPolyphaseChannelIndexes(tunerChannel);
List<Integer> polyphaseIndexes = mChannelIndexCalculator.getPolyphaseChannelIndexes(tunerChannel);
switch(polyphaseIndexes.size())
IPolyphaseChannelOutputProcessor outputProcessor = getOutputProcessor(polyphaseIndexes);
if(outputProcessor != null)
{
case 1:
outputProcessor = new OneChannelOutputProcessor(CHANNEL_SAMPLE_RATE, polyphaseIndexes);
break;
case 2:
outputProcessor = new TwoChannelOutputProcessor(CHANNEL_SAMPLE_RATE, polyphaseIndexes);
break;
default:
//TODO: create output processor for greater than 2 input channels
break;
channelSource = new PolyphaseChannelSource(tunerChannel, outputProcessor, this);
mChannelSources.add(channelSource);
}
}
catch(IllegalArgumentException iae)
{
mLog.info("Can't provide DDC for " + tunerChannel.toString() + " due to channelizer frequency [" +
mChannelizerConfiguration.getCenterFrequency() + "] and sample rate [" +
(mChannelizerConfiguration.getChannelCount() * mChannelizerConfiguration.getChannelBandwidth()) + "]");
}
if(outputProcessor != null)
{
channelSource = new PolyphaseChannelSource(tunerChannel, outputProcessor, this);
mChannelSources.add(channelSource);
mChannelIndexCalculator.getCenterFrequency() + "] and sample rate [" +
(mChannelIndexCalculator.getChannelCount() * mChannelIndexCalculator.getChannelBandwidth()) + "]");
}
return channelSource;
}
private IPolyphaseChannelOutputProcessor getOutputProcessor(List<Integer> indexes) throws IllegalArgumentException
{
switch(indexes.size())
{
case 1:
return new OneChannelOutputProcessor(CHANNEL_SAMPLE_RATE, indexes);
case 2:
return new TwoChannelOutputProcessor(CHANNEL_SAMPLE_RATE, indexes);
default:
//TODO: create output processor for greater than 2 input channels
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.
@@ -157,6 +161,9 @@ public class PolyphaseChannelManager implements ISourceEventProcessor, Listener<
}
}
/**
* Processes source events from the tuner or from any of the child polyphase channel sources.
*/
@Override
public void process(SourceEvent sourceEvent)
{
@@ -169,7 +176,7 @@ public class PolyphaseChannelManager implements ISourceEventProcessor, Listener<
}
else
{
mLog.error("Request to stop sample stream for unrecognized source: " +
mLog.error("Request to start sample stream for unrecognized source: " +
(sourceEvent.hasSource() ? sourceEvent.getSource().getClass() : "null source"));
}
//TODO: add the channel and register for samples from the tuner
@@ -186,29 +193,69 @@ public class PolyphaseChannelManager implements ISourceEventProcessor, Listener<
}
break;
case NOTIFICATION_FREQUENCY_CHANGE:
mChannelizerConfiguration.setCenterFrequency(sourceEvent.getValue().longValue());
updateChannelResultsProcessors();
mChannelIndexCalculator.setCenterFrequency(sourceEvent.getValue().longValue());
updateOutputProcessors();
break;
case NOTIFICATION_SAMPLE_RATE_CHANGE:
int sampleRate = sourceEvent.getValue().intValue();
int channelCount = sampleRate / mChannelizerConfiguration.getChannelBandwidth();
mChannelizerConfiguration.setChannelCount(channelCount);
updateChannelResultsProcessors();
int channelCount = sampleRate / mChannelIndexCalculator.getChannelBandwidth();
mChannelIndexCalculator.setChannelCount(channelCount);
updateOutputProcessors();
break;
}
}
/**
* Implements the Listener<SourceEvent> interface by delegating to the process(sourceEvent) method.
*/
@Override
public void receive(SourceEvent sourceEvent)
{
process(sourceEvent);
}
private void updateChannelResultsProcessors()
/**
* Updates each of the output processors for any changes in the tuner's center frequency or sample rate, which
* would cause the output processors to change the polyphase channelizer results channel(s) that the processor is
* consuming
*/
private void updateOutputProcessors()
{
//Update the processors anytime that the center frequency or sample rate changes
for(PolyphaseChannelSource channelSource: mChannelSources)
{
updateOutputProcessor(channelSource);
}
}
/**
* Updates the polyphase channel source's output processor due to a change in the center frequency or sample
* rate for the source providing sample buffers to the polyphase channelizer, or whenever the DDC channel's
* center tuned frequency changes.
*
* @param channelSource that requires an update to its output processor
*/
private void updateOutputProcessor(PolyphaseChannelSource channelSource)
{
try
{
List<Integer> indexes = mChannelIndexCalculator.getPolyphaseChannelIndexes(channelSource.getTunerChannel());
//If the indexes size is the same then update the current processor, otherwise create a new one
if(channelSource.getPolyphaseChannelOutputProcessor().getInputChannelCount() == indexes.size())
{
channelSource.getPolyphaseChannelOutputProcessor().setPolyphaseChannelIndices(indexes);
}
else
{
channelSource.setPolyphaseChannelOutputProcessor(getOutputProcessor(indexes));
}
}
catch(IllegalArgumentException iae)
{
mLog.error("Error updating polyphase channel source - can't determine output channel indexes for " +
"updated tuner center frequency and sample rate. Stopping channel source", iae);
stopChannelSource(channelSource);
}
}
}

View File

@@ -63,6 +63,33 @@ public class PolyphaseChannelSource extends ComplexChannelSource
mPolyphaseChannelOutputProcessor = outputProcessor;
}
/**
* Channel output processor used by this channel source.
*/
public IPolyphaseChannelOutputProcessor getPolyphaseChannelOutputProcessor()
{
return mPolyphaseChannelOutputProcessor;
}
/**
* Updates the output processor for this channel source
*/
public void setPolyphaseChannelOutputProcessor(IPolyphaseChannelOutputProcessor outputProcessor)
{
synchronized(mPolyphaseChannelOutputProcessor)
{
mPolyphaseChannelOutputProcessor = outputProcessor;
}
}
/**
* Tuner channel sourced by this channel source.
*/
public TunerChannel getTunerChannel()
{
return mTunerChannel;
}
/**
* Process source event notifications received from upstream source provider and source event requests
* received from downstream consumers.

View File

@@ -23,20 +23,29 @@ import sample.complex.Complex;
public abstract class ChannelOutputProcessor implements IPolyphaseChannelOutputProcessor
{
private int mInputChannelCount;
private Oscillator mFrequencyCorrectionMixer;
/**
* Base class for polyphase channelizer output channel processing. Provides built-in frequency translation
* oscillator support to apply frequency correction to the channel sample stream as requested by sample consumer.
*
* @param inputChannelCount is the number of input channels for this output processor
* @param sampleRate of the output channel. This is used to match the oscillator's sample rate to the output
* channel sample rate for frequency translation/correction.
*/
public ChannelOutputProcessor(int sampleRate)
public ChannelOutputProcessor(int inputChannelCount, int sampleRate)
{
mInputChannelCount = inputChannelCount;
mFrequencyCorrectionMixer = new Oscillator(0, sampleRate);
}
@Override
public int getInputChannelCount()
{
return mInputChannelCount;
}
/**
* Provides frequency correction of the inphase component or simply returns the inphase argument value when no
* frequency correction value has been specified.

View File

@@ -20,6 +20,8 @@ package dsp.filter.channelizer.output;
import sample.complex.IComplexSampleListener;
import java.util.List;
public interface IPolyphaseChannelOutputProcessor
{
/**
@@ -37,4 +39,16 @@ public interface IPolyphaseChannelOutputProcessor
* @param frequencyCorrection correction value
*/
void setFrequencyCorrection(long frequencyCorrection);
/**
* Indicates the number of input channels processed by this output processor
* @return
*/
int getInputChannelCount();
/**
* Updates the input polyphase channel index(es) used by this output processor
* @param indexes
*/
void setPolyphaseChannelIndices(List<Integer> indexes);
}

View File

@@ -35,7 +35,7 @@ public class OneChannelOutputProcessor extends ChannelOutputProcessor
*/
public OneChannelOutputProcessor(int sampleRate, List<Integer> channelIndexes)
{
super(sampleRate);
super(1, sampleRate);
setPolyphaseChannelIndices(channelIndexes);
}

View File

@@ -38,7 +38,7 @@ public class TwoChannelOutputProcessor extends ChannelOutputProcessor
{
//Set the frequency correction oscillator to 2 x output sample rate since we'll be correcting the frequency
//after synthesizing both input channels
super(sampleRate * 2);
super(2, sampleRate * 2);
setPolyphaseChannelIndices(channelIndexes);
}