From 9983d2119d6c2c7996a3420ed55f37fbd3119da1 Mon Sep 17 00:00:00 2001 From: Denny Date: Sun, 10 Sep 2017 04:42:35 -0400 Subject: [PATCH] WIP --- .../output/ChannelOutputProcessor.java | 96 +++++++++++++++++++ .../output/ChannelOutputProcessorFactory.java | 2 +- .../output/DoubleChannelOutputProcessor.java | 84 ++++++++++++++++ .../IPolyphaseChannelOutputProcessor.java | 9 +- .../output/SingleChannelOutputProcessor.java | 48 +++------- src/dsp/mixer/Oscillator.java | 2 +- 6 files changed, 199 insertions(+), 42 deletions(-) create mode 100644 src/dsp/filter/channelizer/output/ChannelOutputProcessor.java create mode 100644 src/dsp/filter/channelizer/output/DoubleChannelOutputProcessor.java diff --git a/src/dsp/filter/channelizer/output/ChannelOutputProcessor.java b/src/dsp/filter/channelizer/output/ChannelOutputProcessor.java new file mode 100644 index 00000000..a4c84694 --- /dev/null +++ b/src/dsp/filter/channelizer/output/ChannelOutputProcessor.java @@ -0,0 +1,96 @@ +/******************************************************************************* + * 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 + * + ******************************************************************************/ +package dsp.filter.channelizer.output; + +import dsp.mixer.Oscillator; +import sample.complex.Complex; + +public abstract class ChannelOutputProcessor implements IPolyphaseChannelOutputProcessor +{ + 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 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) + { + mFrequencyCorrectionMixer = new Oscillator(0, sampleRate); + } + + /** + * Provides frequency correction of the inphase component or simply returns the inphase argument value when no + * frequency correction value has been specified. + * + * @param inphase value of the sample to translate/correct + * @param quadrature value of the sample to translate/correct + * @return corrected inphase value + */ + 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; + } + } + + /** + * Provides frequency correction of the quadrature component or simply returns the quadrature argument value when no + * frequency correction value has been applied, + * + * @param inphase value of the sample to translate/correct + * @param quadrature value of the sample to translate/correct + * @return translated inphase value + */ + 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; + } + } + + /** + * Specifies a frequency correction value to be applied to channel samples output from this polyphase channelizer + * output processor. + * + * @param frequencyCorrection offset value + */ + @Override + public void setFrequencyCorrection(long frequencyCorrection) + { + mFrequencyCorrectionMixer.setFrequency(frequencyCorrection); + } + +} diff --git a/src/dsp/filter/channelizer/output/ChannelOutputProcessorFactory.java b/src/dsp/filter/channelizer/output/ChannelOutputProcessorFactory.java index 323fd8a2..4dc141c2 100644 --- a/src/dsp/filter/channelizer/output/ChannelOutputProcessorFactory.java +++ b/src/dsp/filter/channelizer/output/ChannelOutputProcessorFactory.java @@ -21,7 +21,7 @@ package dsp.filter.channelizer.output; public class ChannelOutputProcessorFactory { /** - * Factory for obtaining polyphase channelizer results processors to perform any post-processing + * Factory for obtaining polyphase channelizer output processors to perform any post-processing * that may be required to perform frequency translation and/or resampling of channel results. */ public static IPolyphaseChannelOutputProcessor getProcessor(int sampleRate, long frequency) diff --git a/src/dsp/filter/channelizer/output/DoubleChannelOutputProcessor.java b/src/dsp/filter/channelizer/output/DoubleChannelOutputProcessor.java new file mode 100644 index 00000000..40df765a --- /dev/null +++ b/src/dsp/filter/channelizer/output/DoubleChannelOutputProcessor.java @@ -0,0 +1,84 @@ +/******************************************************************************* + * 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 + * + ******************************************************************************/ +package dsp.filter.channelizer.output; + +import sample.complex.IComplexSampleListener; + +import java.util.List; + +public class DoubleChannelOutputProcessor extends ChannelOutputProcessor +{ + private int mChannelOffset1; + private int mChannelOffset2; + + /** + * Processor to extract two channels from a polyphase channelizer, synthesize/recombine the channels, apply + * frequency translation and frequency correction, down-sample by a factor of two and output an I/Q complex sample. + * + * @param sampleRate of the output sample stream. + * @param channelIndexes containing two channel indices. + */ + public DoubleChannelOutputProcessor(int sampleRate, List channelIndexes) + { + super(sampleRate); + setPolyphaseChannelIndices(channelIndexes); + } + + /** + * Updates this processor to extract the two specified channel indexes. + * + * @param indexes containing a single channel index value. + * @throws IllegalArgumentException if the list of indexes does not contain a single channel index. + */ + public void setPolyphaseChannelIndices(List indexes) + { + if(indexes.size() != 2) + { + throw new IllegalArgumentException("Double channel output processor requires two indexes to " + + "process - provided indexes " + indexes.toString()); + } + + //Set the channelized output results offsets to twice the channel index to account for each channel having + //an I/Q pair + mChannelOffset1 = indexes.get(0) * 2; + mChannelOffset2 = indexes.get(1) * 2; + } + + /** + * 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 listener to receive the extracted, frequency-translated channel results + */ + @Override + public void process(float[] channels, IComplexSampleListener listener) + { +// 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]; + + listener.receive(getFrequencyCorrectedInphase(i, q), getFrequencyCorrectedQuadrature(i, q)); + } +} diff --git a/src/dsp/filter/channelizer/output/IPolyphaseChannelOutputProcessor.java b/src/dsp/filter/channelizer/output/IPolyphaseChannelOutputProcessor.java index 64f911f8..d5473ff1 100644 --- a/src/dsp/filter/channelizer/output/IPolyphaseChannelOutputProcessor.java +++ b/src/dsp/filter/channelizer/output/IPolyphaseChannelOutputProcessor.java @@ -31,9 +31,10 @@ public interface IPolyphaseChannelOutputProcessor void process(float[] channels, IComplexSampleListener listener); /** - * Specifies the positive or negative frequency correction that should be applied to samples extracted from - * the polyphase channelizer output results. - * @param frequency + * Specifies the frequency correction (+/-) that should be applied to samples extracted from the polyphase + * channelizer output results. + * + * @param frequencyCorrection correction value */ - void setFrequencyCorrection(long frequency); + void setFrequencyCorrection(long frequencyCorrection); } diff --git a/src/dsp/filter/channelizer/output/SingleChannelOutputProcessor.java b/src/dsp/filter/channelizer/output/SingleChannelOutputProcessor.java index a3713158..289382a4 100644 --- a/src/dsp/filter/channelizer/output/SingleChannelOutputProcessor.java +++ b/src/dsp/filter/channelizer/output/SingleChannelOutputProcessor.java @@ -18,16 +18,13 @@ ******************************************************************************/ package dsp.filter.channelizer.output; -import dsp.mixer.Oscillator; -import sample.complex.Complex; import sample.complex.IComplexSampleListener; import java.util.List; -public class SingleChannelOutputProcessor implements IPolyphaseChannelOutputProcessor +public class SingleChannelOutputProcessor extends ChannelOutputProcessor { private int mChannelOffset; - private Oscillator mFrequencyCorrectionMixer; /** * Processor to extract a single channel from a polyphase channelizer and produce an output I/Q complex sample @@ -38,12 +35,12 @@ public class SingleChannelOutputProcessor implements IPolyphaseChannelOutputProc */ public SingleChannelOutputProcessor(int sampleRate, List channelIndexes) { - mFrequencyCorrectionMixer = new Oscillator(0, sampleRate); + super(sampleRate); setPolyphaseChannelIndices(channelIndexes); } /** - * Updates this processor to extract a specified channel index. + * Updates this processor to extract a single, specified channel index. * @param indexes containing a single channel index value. * @throws IllegalArgumentException if the list of indexes does not contain a single channel index. */ @@ -61,8 +58,8 @@ public class SingleChannelOutputProcessor implements IPolyphaseChannelOutputProc } /** - * Extracts the channel from the channel results array, applies frequency translation, and delivers the - * extracted frequency-translated channel sample to the complex sample listener. + * 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 listener to receive the extracted, frequency-translated channel results @@ -70,36 +67,15 @@ public class SingleChannelOutputProcessor implements IPolyphaseChannelOutputProc @Override public void process(float[] channels, IComplexSampleListener listener) { - if(channels.length >= (mChannelOffset + 1)) + if(channels.length < mChannelOffset + 1) { - //Only perform frequency translation if the frequency correction mixer has a non-zero frequency value - if(mFrequencyCorrectionMixer.isEnabled()) - { - mFrequencyCorrectionMixer.rotate(); - - float i = Complex.multiplyInphase(channels[mChannelOffset], channels[mChannelOffset + 1], - mFrequencyCorrectionMixer.inphase(), mFrequencyCorrectionMixer.quadrature()); - - float q = Complex.multiplyQuadrature(channels[mChannelOffset], channels[mChannelOffset + 1], - mFrequencyCorrectionMixer.inphase(), mFrequencyCorrectionMixer.quadrature()); - - listener.receive(i, q); - } - else - { - listener.receive(channels[mChannelOffset], channels[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 + "]"); } - } - /** - * Specifies a frequency correction/translation value to be applied to channel samples output from the - * polyphase channelizer. - * @param frequency - */ - @Override - public void setFrequencyCorrection(long frequency) - { - mFrequencyCorrectionMixer.setFrequency(frequency); + float i = channels[mChannelOffset]; + float q = channels[mChannelOffset + 1]; + + listener.receive(getFrequencyCorrectedInphase(i, q), getFrequencyCorrectedQuadrature(i, q)); } } diff --git a/src/dsp/mixer/Oscillator.java b/src/dsp/mixer/Oscillator.java index 70383bec..865f4bcf 100644 --- a/src/dsp/mixer/Oscillator.java +++ b/src/dsp/mixer/Oscillator.java @@ -58,7 +58,7 @@ public class Oscillator * * @return true if the current frequency value is non-zero. */ - public boolean isEnabled() + public boolean enabled() { return mEnabled; }