diff --git a/.idea/vcs.xml b/.idea/vcs.xml index 35eb1ddf..0dc6f92c 100644 --- a/.idea/vcs.xml +++ b/.idea/vcs.xml @@ -1,5 +1,15 @@ + + + diff --git a/src/dsp/filter/channelizer/ChannelizerConfiguration.java b/src/dsp/filter/channelizer/ChannelIndexCalculator.java similarity index 90% rename from src/dsp/filter/channelizer/ChannelizerConfiguration.java rename to src/dsp/filter/channelizer/ChannelIndexCalculator.java index b206da9a..6714786c 100644 --- a/src/dsp/filter/channelizer/ChannelizerConfiguration.java +++ b/src/dsp/filter/channelizer/ChannelIndexCalculator.java @@ -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 indices = channelizerConfiguration.getPolyphaseChannelIndexes(tunerChannel); + List indices = channelIndexCalculator.getPolyphaseChannelIndexes(tunerChannel); mLog.debug("Indices: " + indices); diff --git a/src/dsp/filter/channelizer/PolyphaseChannelManager.java b/src/dsp/filter/channelizer/PolyphaseChannelManager.java index 2b1416c0..8765c7fd 100644 --- a/src/dsp/filter/channelizer/PolyphaseChannelManager.java +++ b/src/dsp/filter/channelizer/PolyphaseChannelManager.java @@ -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 { @@ -43,12 +42,16 @@ public class PolyphaseChannelManager implements ISourceEventProcessor, Listener< private Tuner mTuner; private List 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 polyphaseIndexes = mChannelizerConfiguration.getPolyphaseChannelIndexes(tunerChannel); + List 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 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 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 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); + } } } diff --git a/src/dsp/filter/channelizer/PolyphaseChannelSource.java b/src/dsp/filter/channelizer/PolyphaseChannelSource.java index aa2a4767..f5e905a7 100644 --- a/src/dsp/filter/channelizer/PolyphaseChannelSource.java +++ b/src/dsp/filter/channelizer/PolyphaseChannelSource.java @@ -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. diff --git a/src/dsp/filter/channelizer/output/ChannelOutputProcessor.java b/src/dsp/filter/channelizer/output/ChannelOutputProcessor.java index a4c84694..e65ca444 100644 --- a/src/dsp/filter/channelizer/output/ChannelOutputProcessor.java +++ b/src/dsp/filter/channelizer/output/ChannelOutputProcessor.java @@ -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. diff --git a/src/dsp/filter/channelizer/output/IPolyphaseChannelOutputProcessor.java b/src/dsp/filter/channelizer/output/IPolyphaseChannelOutputProcessor.java index d5473ff1..cc8644d1 100644 --- a/src/dsp/filter/channelizer/output/IPolyphaseChannelOutputProcessor.java +++ b/src/dsp/filter/channelizer/output/IPolyphaseChannelOutputProcessor.java @@ -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 indexes); } diff --git a/src/dsp/filter/channelizer/output/OneChannelOutputProcessor.java b/src/dsp/filter/channelizer/output/OneChannelOutputProcessor.java index f162bcc4..863b6454 100644 --- a/src/dsp/filter/channelizer/output/OneChannelOutputProcessor.java +++ b/src/dsp/filter/channelizer/output/OneChannelOutputProcessor.java @@ -35,7 +35,7 @@ public class OneChannelOutputProcessor extends ChannelOutputProcessor */ public OneChannelOutputProcessor(int sampleRate, List channelIndexes) { - super(sampleRate); + super(1, sampleRate); setPolyphaseChannelIndices(channelIndexes); } diff --git a/src/dsp/filter/channelizer/output/TwoChannelOutputProcessor.java b/src/dsp/filter/channelizer/output/TwoChannelOutputProcessor.java index b242ce29..e2ff98aa 100644 --- a/src/dsp/filter/channelizer/output/TwoChannelOutputProcessor.java +++ b/src/dsp/filter/channelizer/output/TwoChannelOutputProcessor.java @@ -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); }