diff --git a/src/main/java/io/github/dsheirer/channel/state/ChannelState.java b/src/main/java/io/github/dsheirer/channel/state/ChannelState.java index 2ba741e4..f2d069d5 100644 --- a/src/main/java/io/github/dsheirer/channel/state/ChannelState.java +++ b/src/main/java/io/github/dsheirer/channel/state/ChannelState.java @@ -32,8 +32,8 @@ import io.github.dsheirer.module.decode.DecoderType; import io.github.dsheirer.module.decode.config.DecodeConfiguration; import io.github.dsheirer.module.decode.event.CallEvent; import io.github.dsheirer.module.decode.event.ICallEventProvider; +import io.github.dsheirer.sample.IOverflowListener; import io.github.dsheirer.sample.Listener; -import io.github.dsheirer.sample.real.IOverflowListener; import io.github.dsheirer.source.ISourceEventListener; import io.github.dsheirer.source.SourceEvent; import io.github.dsheirer.source.heartbeat.Heartbeat; diff --git a/src/main/java/io/github/dsheirer/dsp/am/AMDemodulator_CB.java b/src/main/java/io/github/dsheirer/dsp/am/AMDemodulator_CB.java deleted file mode 100644 index 4a22ba6b..00000000 --- a/src/main/java/io/github/dsheirer/dsp/am/AMDemodulator_CB.java +++ /dev/null @@ -1,69 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014,2015 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 io.github.dsheirer.dsp.am; - -import io.github.dsheirer.sample.Listener; -import io.github.dsheirer.sample.complex.ComplexBuffer; -import io.github.dsheirer.sample.real.RealBuffer; - -public class AMDemodulator_CB extends AMDemodulator implements Listener -{ - private Listener mListener; - - public AMDemodulator_CB( float gain ) - { - super( gain ); - } - - @Override - public void receive( ComplexBuffer buffer ) - { - if( mListener != null ) - { - float[] samples = buffer.getSamples(); - - int half = samples.length / 2; - - float[] demodulated = new float[ half ]; - - for( int x = 0; x < half; x++ ) - { - int index = x * 2; - - demodulated[ x ] = demodulate( samples[ index ], samples[ index + 1 ] ); - } - - mListener.receive( new RealBuffer( demodulated ) ); - } - } - - public void setListener( Listener listener ) - { - mListener = listener; - } - - public void removeListener() - { - mListener = null; - } - - public void dispose() - { - mListener = null; - } -} diff --git a/src/main/java/io/github/dsheirer/dsp/filter/channelizer/ComplexPolyphaseChannelizerM2.java b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/ComplexPolyphaseChannelizerM2.java index e2bf21b0..91ebda5c 100644 --- a/src/main/java/io/github/dsheirer/dsp/filter/channelizer/ComplexPolyphaseChannelizerM2.java +++ b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/ComplexPolyphaseChannelizerM2.java @@ -20,10 +20,10 @@ package io.github.dsheirer.dsp.filter.channelizer; import io.github.dsheirer.dsp.filter.FilterFactory; import io.github.dsheirer.dsp.filter.design.FilterDesignException; +import io.github.dsheirer.sample.IOverflowListener; import io.github.dsheirer.sample.Listener; import io.github.dsheirer.sample.buffer.ReusableChannelResultsBuffer; import io.github.dsheirer.sample.buffer.ReusableComplexBuffer; -import io.github.dsheirer.sample.real.IOverflowListener; import org.jtransforms.fft.FloatFFT_1D; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/src/main/java/io/github/dsheirer/dsp/filter/channelizer/ContinuousBufferProcessor.java b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/ContinuousBufferProcessor.java index 1df13430..0ee7e268 100644 --- a/src/main/java/io/github/dsheirer/dsp/filter/channelizer/ContinuousBufferProcessor.java +++ b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/ContinuousBufferProcessor.java @@ -15,9 +15,9 @@ ******************************************************************************/ package io.github.dsheirer.dsp.filter.channelizer; +import io.github.dsheirer.sample.IOverflowListener; import io.github.dsheirer.sample.Listener; import io.github.dsheirer.sample.OverflowableTransferQueue; -import io.github.dsheirer.sample.real.IOverflowListener; import io.github.dsheirer.util.ThreadPool; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/src/main/java/io/github/dsheirer/dsp/filter/channelizer/PolyphaseChannelSource.java b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/PolyphaseChannelSource.java index 4e41ffbe..07e065f1 100644 --- a/src/main/java/io/github/dsheirer/dsp/filter/channelizer/PolyphaseChannelSource.java +++ b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/PolyphaseChannelSource.java @@ -17,9 +17,9 @@ package io.github.dsheirer.dsp.filter.channelizer; import io.github.dsheirer.dsp.filter.channelizer.output.IPolyphaseChannelOutputProcessor; import io.github.dsheirer.sample.Listener; -import io.github.dsheirer.sample.buffer.ReusableBufferAssembler; import io.github.dsheirer.sample.buffer.ReusableChannelResultsBuffer; import io.github.dsheirer.sample.buffer.ReusableComplexBuffer; +import io.github.dsheirer.sample.buffer.ReusableComplexBufferAssembler; import io.github.dsheirer.source.SourceEvent; import io.github.dsheirer.source.tuner.channel.TunerChannel; import io.github.dsheirer.source.tuner.channel.TunerChannelSource; @@ -30,7 +30,7 @@ public class PolyphaseChannelSource extends TunerChannelSource { private final static Logger mLog = LoggerFactory.getLogger(PolyphaseChannelSource.class); - private ReusableBufferAssembler mReusableBufferAssembler; + private ReusableComplexBufferAssembler mReusableComplexBufferAssembler; private IPolyphaseChannelOutputProcessor mPolyphaseChannelOutputProcessor; private double mChannelSampleRate; private long mIndexCenterFrequency; @@ -54,7 +54,7 @@ public class PolyphaseChannelSource extends TunerChannelSource super(producerSourceEventListener, tunerChannel); mPolyphaseChannelOutputProcessor = outputProcessor; mChannelSampleRate = channelSampleRate; - mReusableBufferAssembler = new ReusableBufferAssembler(2500, mChannelSampleRate); + mReusableComplexBufferAssembler = new ReusableComplexBufferAssembler(2500, mChannelSampleRate); setFrequency(centerFrequency); } @@ -64,7 +64,7 @@ public class PolyphaseChannelSource extends TunerChannelSource @Override public void setListener(final Listener listener) { - mReusableBufferAssembler.setListener(listener); + mReusableComplexBufferAssembler.setListener(listener); } /** @@ -73,7 +73,7 @@ public class PolyphaseChannelSource extends TunerChannelSource @Override public void removeListener(Listener listener) { - mReusableBufferAssembler.setListener(null); + mReusableComplexBufferAssembler.setListener(null); } /** @@ -112,7 +112,7 @@ public class PolyphaseChannelSource extends TunerChannelSource //Fully process the residual channel results buffer of the previous channel output processor if(existingProcessor != null) { - existingProcessor.processChannelResults(mReusableBufferAssembler); + existingProcessor.processChannelResults(mReusableComplexBufferAssembler); } //Finally, setup the frequency offset for the output processor and reset the frequency correction value @@ -133,7 +133,7 @@ public class PolyphaseChannelSource extends TunerChannelSource */ public void receiveChannelResults(ReusableChannelResultsBuffer channelResultsBuffer) { - mReusableBufferAssembler.updateTimestamp(channelResultsBuffer.getTimestamp()); + mReusableComplexBufferAssembler.updateTimestamp(channelResultsBuffer.getTimestamp()); mPolyphaseChannelOutputProcessor.receiveChannelResults(channelResultsBuffer); } @@ -161,9 +161,9 @@ public class PolyphaseChannelSource extends TunerChannelSource @Override public void dispose() { - if(mReusableBufferAssembler != null) + if(mReusableComplexBufferAssembler != null) { - mReusableBufferAssembler.dispose(); + mReusableComplexBufferAssembler.dispose(); } if(mPolyphaseChannelOutputProcessor != null) @@ -212,7 +212,7 @@ public class PolyphaseChannelSource extends TunerChannelSource //Lock on the output processor so that it can't be changed in the middle of processing synchronized(mPolyphaseChannelOutputProcessor) { - mPolyphaseChannelOutputProcessor.processChannelResults(mReusableBufferAssembler); + mPolyphaseChannelOutputProcessor.processChannelResults(mReusableComplexBufferAssembler); } } diff --git a/src/main/java/io/github/dsheirer/dsp/filter/channelizer/output/ChannelOutputProcessor.java b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/output/ChannelOutputProcessor.java index 872700bb..43c1e858 100644 --- a/src/main/java/io/github/dsheirer/dsp/filter/channelizer/output/ChannelOutputProcessor.java +++ b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/output/ChannelOutputProcessor.java @@ -20,10 +20,10 @@ package io.github.dsheirer.dsp.filter.channelizer.output; import io.github.dsheirer.dsp.mixer.IOscillator; import io.github.dsheirer.dsp.mixer.Oscillator; +import io.github.dsheirer.sample.IOverflowListener; import io.github.dsheirer.sample.buffer.OverflowableReusableBufferTransferQueue; -import io.github.dsheirer.sample.buffer.ReusableBufferAssembler; import io.github.dsheirer.sample.buffer.ReusableChannelResultsBuffer; -import io.github.dsheirer.sample.real.IOverflowListener; +import io.github.dsheirer.sample.buffer.ReusableComplexBufferAssembler; import io.github.dsheirer.source.Source; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -99,10 +99,10 @@ public abstract class ChannelOutputProcessor implements IPolyphaseChannelOutputP /** * Processes all enqueued polyphase channelizer results until the internal queue is empty - * @param reusableBufferAssembler to receive the processed channel results + * @param reusableComplexBufferAssembler to receive the processed channel results */ @Override - public void processChannelResults(ReusableBufferAssembler reusableBufferAssembler) + public void processChannelResults(ReusableComplexBufferAssembler reusableComplexBufferAssembler) { try { @@ -110,7 +110,7 @@ public abstract class ChannelOutputProcessor implements IPolyphaseChannelOutputP if(toProcess > 0) { - process(mChannelResultsToProcess, reusableBufferAssembler); + process(mChannelResultsToProcess, reusableComplexBufferAssembler); } } catch(Throwable throwable) @@ -124,10 +124,10 @@ public abstract class ChannelOutputProcessor implements IPolyphaseChannelOutputP /** * Sub-class implementation to process one polyphase channelizer result array. * @param channelResults to process - * @param reusableBufferAssembler to receive the processed channelizer results + * @param reusableComplexBufferAssembler to receive the processed channelizer results */ public abstract void process(List channelResults, - ReusableBufferAssembler reusableBufferAssembler); + ReusableComplexBufferAssembler reusableComplexBufferAssembler); /** diff --git a/src/main/java/io/github/dsheirer/dsp/filter/channelizer/output/IPolyphaseChannelOutputProcessor.java b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/output/IPolyphaseChannelOutputProcessor.java index 95c45055..70eb7c4a 100644 --- a/src/main/java/io/github/dsheirer/dsp/filter/channelizer/output/IPolyphaseChannelOutputProcessor.java +++ b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/output/IPolyphaseChannelOutputProcessor.java @@ -18,8 +18,8 @@ ******************************************************************************/ package io.github.dsheirer.dsp.filter.channelizer.output; -import io.github.dsheirer.sample.buffer.ReusableBufferAssembler; import io.github.dsheirer.sample.buffer.ReusableChannelResultsBuffer; +import io.github.dsheirer.sample.buffer.ReusableComplexBufferAssembler; import io.github.dsheirer.source.Source; import java.util.List; @@ -35,9 +35,9 @@ public interface IPolyphaseChannelOutputProcessor /** * Process the channel output channel results queue and deliver the output to the listener * - * @param reusableBufferAssembler to receive the processed channel results + * @param reusableComplexBufferAssembler to receive the processed channel results */ - void processChannelResults(ReusableBufferAssembler reusableBufferAssembler); + void processChannelResults(ReusableComplexBufferAssembler reusableComplexBufferAssembler); /** * Sets the desired frequency offset from center. The samples will be mixed with an oscillator set to this offset diff --git a/src/main/java/io/github/dsheirer/dsp/filter/channelizer/output/OneChannelOutputProcessor.java b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/output/OneChannelOutputProcessor.java index d06f1e44..ece7eedd 100644 --- a/src/main/java/io/github/dsheirer/dsp/filter/channelizer/output/OneChannelOutputProcessor.java +++ b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/output/OneChannelOutputProcessor.java @@ -18,9 +18,9 @@ ******************************************************************************/ package io.github.dsheirer.dsp.filter.channelizer.output; -import io.github.dsheirer.sample.buffer.ReusableBufferAssembler; import io.github.dsheirer.sample.buffer.ReusableChannelResultsBuffer; import io.github.dsheirer.sample.buffer.ReusableComplexBuffer; +import io.github.dsheirer.sample.buffer.ReusableComplexBufferAssembler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -74,11 +74,11 @@ public class OneChannelOutputProcessor extends ChannelOutputProcessor * extracted frequency-corrected channel I/Q sample set to the complex sample listener. * * @param channelResults to process containing a list of channel array of I/Q sample pairs (I0,Q0,I1,Q1...In,Qn) - * @param reusableBufferAssembler to receive the extracted, frequency-translated channel results + * @param reusableComplexBufferAssembler to receive the extracted, frequency-translated channel results */ @Override public void process(List channelResults, - ReusableBufferAssembler reusableBufferAssembler) + ReusableComplexBufferAssembler reusableComplexBufferAssembler) { for(ReusableChannelResultsBuffer channelResultsBuffer: channelResults) { @@ -91,7 +91,7 @@ public class OneChannelOutputProcessor extends ChannelOutputProcessor getFrequencyCorrectionMixer().mixComplex(channelBuffer.getSamples()); } - reusableBufferAssembler.receive(channelBuffer); + reusableComplexBufferAssembler.receive(channelBuffer); } catch(IllegalArgumentException iae) { diff --git a/src/main/java/io/github/dsheirer/dsp/filter/channelizer/output/TwoChannelOutputProcessor.java b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/output/TwoChannelOutputProcessor.java index 7852bd6d..34cca3a1 100644 --- a/src/main/java/io/github/dsheirer/dsp/filter/channelizer/output/TwoChannelOutputProcessor.java +++ b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/output/TwoChannelOutputProcessor.java @@ -21,9 +21,9 @@ import io.github.dsheirer.dsp.filter.fir.FIRFilterSpecification; import io.github.dsheirer.dsp.filter.fir.complex.ComplexFIRFilter2; import io.github.dsheirer.dsp.filter.fir.remez.RemezFIRFilterDesigner; import io.github.dsheirer.dsp.mixer.FS4DownConverter; -import io.github.dsheirer.sample.buffer.ReusableBufferAssembler; import io.github.dsheirer.sample.buffer.ReusableChannelResultsBuffer; import io.github.dsheirer.sample.buffer.ReusableComplexBuffer; +import io.github.dsheirer.sample.buffer.ReusableComplexBufferAssembler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -119,11 +119,11 @@ public class TwoChannelOutputProcessor extends ChannelOutputProcessor * extracted frequency-corrected channel I/Q sample set to the complex sample listener. * * @param channelResultsBuffers to process containing an array of channel I/Q sample pairs (I0,Q0,I1,Q1...In,Qn) - * @param reusableBufferAssembler to receive the extracted, frequency-translated channel results + * @param reusableComplexBufferAssembler to receive the extracted, frequency-translated channel results */ @Override public void process(List channelResultsBuffers, - ReusableBufferAssembler reusableBufferAssembler) + ReusableComplexBufferAssembler reusableComplexBufferAssembler) { for(ReusableChannelResultsBuffer buffer: channelResultsBuffers) { @@ -141,7 +141,7 @@ public class TwoChannelOutputProcessor extends ChannelOutputProcessor ReusableComplexBuffer lowPassFiltered = mLowPassFilter.filter(synthesized); - reusableBufferAssembler.receive(lowPassFiltered); + reusableComplexBufferAssembler.receive(lowPassFiltered); buffer.decrementUserCount(); } diff --git a/src/main/java/io/github/dsheirer/dsp/filter/cic/ComplexPrimeCICDecimate.java b/src/main/java/io/github/dsheirer/dsp/filter/cic/ComplexPrimeCICDecimate.java index 2ad5d95e..54c2db59 100644 --- a/src/main/java/io/github/dsheirer/dsp/filter/cic/ComplexPrimeCICDecimate.java +++ b/src/main/java/io/github/dsheirer/dsp/filter/cic/ComplexPrimeCICDecimate.java @@ -1,19 +1,16 @@ /******************************************************************************* - * sdrtrunk - * Copyright (C) 2014-2017 Dennis Sheirer + * sdr-trunk + * Copyright (C) 2014-2018 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 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. + * 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 + * You should have received a copy of the GNU General Public License along with this program. + * If not, see * ******************************************************************************/ package io.github.dsheirer.dsp.filter.cic; @@ -21,19 +18,19 @@ package io.github.dsheirer.dsp.filter.cic; import io.github.dsheirer.dsp.filter.FilterFactory; import io.github.dsheirer.dsp.filter.Filters; import io.github.dsheirer.dsp.filter.Window; -import io.github.dsheirer.dsp.filter.fir.complex.ComplexFIRFilter_CB_CB; -import io.github.dsheirer.dsp.filter.halfband.complex.HalfBandFilter_CB_CB; +import io.github.dsheirer.dsp.filter.fir.complex.ComplexFIRFilter2; +import io.github.dsheirer.dsp.filter.halfband.complex.ComplexHalfBandFilter; import io.github.dsheirer.sample.Listener; -import io.github.dsheirer.sample.complex.ComplexBuffer; +import io.github.dsheirer.sample.buffer.ReusableComplexBuffer; +import io.github.dsheirer.sample.buffer.ReusableComplexBufferAssembler; import io.github.dsheirer.sample.complex.ComplexSampleListener; -import io.github.dsheirer.sample.complex.ComplexToComplexBufferAssembler; import io.github.dsheirer.sample.decimator.ComplexDecimator; import org.apache.commons.lang3.Validate; import java.util.ArrayList; import java.util.List; -public class ComplexPrimeCICDecimate implements Listener +public class ComplexPrimeCICDecimate implements Listener { public static int[] PRIMES = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, @@ -87,7 +84,7 @@ public class ComplexPrimeCICDecimate implements Listener } else { - /* Wire the current stage to the previous stage */ + /* Wire the current stage to the previous stage */ mDecimatingStages.get(x - 1).setListener(stage); } } @@ -116,7 +113,7 @@ public class ComplexPrimeCICDecimate implements Listener /** * Adds a listener to receive the output of this CIC decimation filter */ - public void setListener(Listener listener) + public void setListener(Listener listener) { mOutput.setListener(listener); } @@ -166,7 +163,7 @@ public class ComplexPrimeCICDecimate implements Listener * Primary input method for receiving sample arrays composed as I,Q,I,Q, etc. */ @Override - public void receive(ComplexBuffer buffer) + public void receive(ReusableComplexBuffer buffer) { if(mFirstDecimatingStage != null) { @@ -177,6 +174,8 @@ public class ComplexPrimeCICDecimate implements Listener mFirstDecimatingStage.receive(samples[x], samples[x + 1]); } } + + buffer.decrementUserCount(); } /** @@ -289,11 +288,11 @@ public class ComplexPrimeCICDecimate implements Listener public void receive(float i, float q) { - /* Subtract the oldest sample and add back in the newest */ + /* Subtract the oldest sample and add back in the newest */ mISum = mISum - mISamples[mSamplePointer] + i; mQSum = mQSum - mQSamples[mSamplePointer] + q; - /* Overwrite the oldest sample with the newest */ + /* Overwrite the oldest sample with the newest */ mISamples[mSamplePointer] = i; mQSamples[mSamplePointer] = q; @@ -352,27 +351,37 @@ public class ComplexPrimeCICDecimate implements Listener public class Output implements ComplexSampleListener { /* Decimated output buffers will contain 1024 samples */ - private ComplexToComplexBufferAssembler mAssembler = - new ComplexToComplexBufferAssembler(2048); + private ReusableComplexBufferAssembler mAssembler = new ReusableComplexBufferAssembler(2048, 48000.0); - private ComplexFIRFilter_CB_CB mCleanupFilter; - private HalfBandFilter_CB_CB mHalfBandFilter = new HalfBandFilter_CB_CB( + private ComplexFIRFilter2 mCleanupFilter; + private ComplexHalfBandFilter mHalfBandFilter = new ComplexHalfBandFilter( Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO.getCoefficients(), 0.4f, false); + private Listener mReusableComplexBufferListener; public Output(int outputSampleRate, int passFrequency, int attenuation, Window.WindowType windowType) { - mCleanupFilter = new ComplexFIRFilter_CB_CB(FilterFactory - .getCICCleanupFilter(outputSampleRate, - passFrequency, - attenuation, - windowType), 0.4f); + mCleanupFilter = new ComplexFIRFilter2(FilterFactory + .getCICCleanupFilter(outputSampleRate, passFrequency, attenuation, windowType), 0.4f); //Bypassing the CIC cleanup filter for now - mAssembler.setListener(mHalfBandFilter); - -// mAssembler.setListener( mCleanupFilter ); -// mCleanupFilter.setListener( mHalfBandFilter ); + mAssembler.setListener(new Listener() + { + @Override + public void receive(ReusableComplexBuffer reusableComplexBuffer) + { + if(mReusableComplexBufferListener != null) + { + ReusableComplexBuffer cleanupFiltered = mCleanupFilter.filter(reusableComplexBuffer); + ReusableComplexBuffer halfBandFiltered = mHalfBandFilter.filter(cleanupFiltered); + mReusableComplexBufferListener.receive(halfBandFiltered); + } + else + { + reusableComplexBuffer.decrementUserCount(); + } + } + }); } public void dispose() @@ -390,23 +399,17 @@ public class ComplexPrimeCICDecimate implements Listener @Override public void receive(float inphase, float quadrature) { - mAssembler.receive(inphase, quadrature); + mAssembler.receive(new float[]{inphase, quadrature}); } - /** - * Adds a listener to receive output samples - */ - public void setListener(Listener listener) + public void setListener(Listener listener) { - mHalfBandFilter.setListener(listener); + mReusableComplexBufferListener = listener; } - /** - * Removes the listener from receiving output samples - */ public void removeListener() { - mHalfBandFilter.removeListener(); + mReusableComplexBufferListener = null; } } } diff --git a/src/main/java/io/github/dsheirer/dsp/filter/cic/RealPrimeCICDecimate.java b/src/main/java/io/github/dsheirer/dsp/filter/cic/RealPrimeCICDecimate.java index 2b1b2b82..87b444b7 100644 --- a/src/main/java/io/github/dsheirer/dsp/filter/cic/RealPrimeCICDecimate.java +++ b/src/main/java/io/github/dsheirer/dsp/filter/cic/RealPrimeCICDecimate.java @@ -1,17 +1,17 @@ /******************************************************************************* * SDR Trunk * Copyright (C) 2015 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 ******************************************************************************/ @@ -20,13 +20,13 @@ package io.github.dsheirer.dsp.filter.cic; import io.github.dsheirer.dsp.filter.FilterFactory; import io.github.dsheirer.dsp.filter.Filters; import io.github.dsheirer.dsp.filter.Window.WindowType; -import io.github.dsheirer.dsp.filter.fir.real.RealFIRFilter_RB_RB; -import io.github.dsheirer.dsp.filter.halfband.real.HalfBandFilter_RB_RB; +import io.github.dsheirer.dsp.filter.fir.real.RealFIRFilter2; +import io.github.dsheirer.dsp.filter.halfband.real.HalfBandFilter; import io.github.dsheirer.sample.Listener; +import io.github.dsheirer.sample.buffer.ReusableBuffer; +import io.github.dsheirer.sample.buffer.ReusableBufferAssembler; import io.github.dsheirer.sample.decimator.RealDecimator; -import io.github.dsheirer.sample.real.RealBuffer; import io.github.dsheirer.sample.real.RealSampleListener; -import io.github.dsheirer.sample.real.RealToRealBufferAssembler; import org.apache.commons.lang3.Validate; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -34,381 +34,389 @@ import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.List; -public class RealPrimeCICDecimate +public class RealPrimeCICDecimate { - private final static Logger mLog = - LoggerFactory.getLogger( RealPrimeCICDecimate.class ); - - public static int[] PRIMES = { 2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53, - 59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151, - 157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251, - 257,263,269,271,277,281,283,293,307,311,313,317,331,337,347,349,353,359, - 367,373,379,383,389,397,401,409,419,421,431,433,439,443,449,457,461,463, - 467,479,487,491,499,503,509,521,523,541,547,557,563,569,571,577,587,593, - 599,601,607,613,617,619,631,641,643,647,653,659,661,673,677,683,691 }; - - private ArrayList mDecimatingStages = - new ArrayList(); - - private DecimatingStage mFirstDecimatingStage; + private final static Logger mLog = LoggerFactory.getLogger(RealPrimeCICDecimate.class); - private Output mOutput; - - private int mOutputBufferSize = 2048; + public static int[] PRIMES = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, + 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, + 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, + 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, + 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, + 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, + 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691}; - /** - * Non-Recursive Prime-Factor CIC Filter with float sample array inputs and - * decimated float sample output. - * - * Implements the CIC filter described in Understanding Digital Signal - * Processing, 3e, Lyons, on page 769. This filter is comprised of multiple - * decimating stages each with a prime factor decimation rate. Multiple - * stages are cascaded to achieve the overall decimation rate. - * - * This filter supports a maximum decimation rate of 700. This filter can - * be adapted to higher decimation rates by adding additional prime factors - * to the PRIMES array. - * - * @param decimation - overall decimation rate - * @param order - filter order - */ - public RealPrimeCICDecimate( int decimation, int order, int passFrequency, - int attenuation, WindowType windowType ) - { - this( decimation, order, passFrequency, attenuation, windowType, 2048 ); - } - - public RealPrimeCICDecimate( int decimation, int order, int passFrequency, - int attenuation, WindowType windowType, int outputBufferSize ) - { - Validate.isTrue(decimation <= 700); + private List mDecimatingStages = new ArrayList(); + private DecimatingStage mFirstDecimatingStage; + private Output mOutput; - mOutputBufferSize = outputBufferSize; + private int mOutputBufferSize = 2048; - List stageSizes = getPrimeFactors( decimation ); - - for( int x = 0; x < stageSizes.size(); x++ ) - { - DecimatingStage stage = new DecimatingStage( stageSizes.get( x ), order ); - - mDecimatingStages.add( stage ); - - if( x == 0 ) - { - /* Reference to first stage -- will receive all samples */ - mFirstDecimatingStage = stage; - } - else - { - /* Wire the current stage to the previous stage */ - mDecimatingStages.get( x - 1 ).setListener( stage ); - } - } - - mOutput = new Output( 48000, passFrequency, attenuation, windowType, - outputBufferSize ); - - mDecimatingStages.get( mDecimatingStages.size() - 1 ) - .setListener( mOutput ); - } - - public void dispose() - { - for( DecimatingStage stage: mDecimatingStages ) - { - stage.dispose(); - } - - mDecimatingStages.clear(); - mDecimatingStages = null; - mFirstDecimatingStage = null; - - mOutput.dispose(); - mOutput = null; - } - - /** - * Adds a listener to receive the output of this CIC decimation filter - */ - public void setListener( Listener listener ) - { - mOutput.setListener( listener ); - } - - /** - * Removes listener from output of this CIC decimation filter - */ - public void removeListener() - { - mOutput.removeListener(); - } - - /** - * Calculates the prime factors of the decimation rate up to a maximum - * decimation rate of 700. If you wish to have decimation rates higher - * than 700, then add additional prime factors to the PRIMES array. - * - * @param decimation - integral decimation rate - * @return - ordered list (smallest to largest) of prime factors - */ - public static List getPrimeFactors( int decimation ) - { - ArrayList stages = new ArrayList(); - - int pointer = 0; - - while( decimation > 0 && pointer < PRIMES.length ) - { - int prime = PRIMES[ pointer ]; - - if( decimation % prime == 0 ) - { - stages.add( prime ); - - decimation /= prime; - } - else - { - pointer++; - } - } - - return stages; - } - - /** - * Primary input method for receiving real buffers. - */ - public void receive( RealBuffer buffer ) + /** + * Non-Recursive Prime-Factor CIC Filter with float sample array inputs and + * decimated float sample output. + * + * Implements the CIC filter described in Understanding Digital Signal + * Processing, 3e, Lyons, on page 769. This filter is comprised of multiple + * decimating stages each with a prime factor decimation rate. Multiple + * stages are cascaded to achieve the overall decimation rate. + * + * This filter supports a maximum decimation rate of 700. This filter can + * be adapted to higher decimation rates by adding additional prime factors + * to the PRIMES array. + * + * @param decimation - overall decimation rate + * @param order - filter order + */ + public RealPrimeCICDecimate(int decimation, int order, int passFrequency, int attenuation, WindowType windowType) { - if( mFirstDecimatingStage != null ) - { - for( float sample: buffer.getSamples() ) - { - mFirstDecimatingStage.receive( sample ); - } - } + this(decimation, order, passFrequency, attenuation, windowType, 2048); } - - /** - * Decimating stage combines multiple CIC stages with a decimator. The - * number of stages is indicated by the order value and the size indicates - * the decimation rate of this stage. - */ - public class DecimatingStage implements RealSampleListener - { - private ArrayList mStages = new ArrayList(); - private Stage mFirstStage; - private RealDecimator mDecimator; - - public DecimatingStage( int size, int order ) - { - for( int x = 0; x < order; x++ ) - { - Stage stage; - - if( size == 2 ) - { - stage = new TwoStage(); - } - else - { - stage = new Stage( size ); - } - - mStages.add( stage ); - - if( x == 0 ) - { - mFirstStage = stage; - } - else - { - mStages.get( x - 1 ).setListener( stage ); - } - } - - mDecimator = new RealDecimator( size ); - - mStages.get( mStages.size() - 1 ).setListener( mDecimator ); - } - - public void dispose() - { - for( Stage stage: mStages ) - { - stage.dispose(); - } - - mStages.clear(); - mDecimator.dispose(); - mDecimator = null; - mFirstStage = null; - mStages = null; - } - @Override - public void receive( float sample ) + public RealPrimeCICDecimate(int decimation, int order, int passFrequency, int attenuation, WindowType windowType, int outputBufferSize) + { + Validate.isTrue(decimation <= 700); + + mOutputBufferSize = outputBufferSize; + + List stageSizes = getPrimeFactors(decimation); + + for(int x = 0; x < stageSizes.size(); x++) { - mFirstStage.receive( sample ); - } - - public void setListener( RealSampleListener listener ) - { - mDecimator.setListener( listener ); - } - } - - /** - * Single non-decimating CIC stage component. Uses a circular buffer and a - * running average internally to implement the stage so that stage size has - * essentially no impact on the computational requirements of the stage - */ - public class Stage implements RealSampleListener - { - protected RealSampleListener mListener; - - private float[] mSamples; - - protected float mSum; - - private int mSamplePointer = 0; - private int mSize; - - protected float mGain; - - public Stage() - { - } + DecimatingStage stage = new DecimatingStage(stageSizes.get(x), order); - public Stage( int size ) - { - mSize = size - 1; - - mSamples = new float[ mSize ]; - - mGain = 1.0f / (float)size; - } - - public void dispose() - { - mListener = null; - } - - public void receive( float sample ) - { - /* Subtract the oldest sample and add back in the newest */ - mSum = mSum - mSamples[ mSamplePointer ] + sample; + mDecimatingStages.add(stage); - /* Overwrite the oldest sample with the newest */ - mSamples[ mSamplePointer ] = sample; - - mSamplePointer++; - - if( mSamplePointer >= mSize ) - { - mSamplePointer = 0; - } - - if( mListener != null ) - { - mListener.receive( mSum * mGain ); - } - } - - public void setListener( RealSampleListener listener ) - { - mListener = listener; - } - } - - /** - * Size 2 stage that removes the unnecessary circular buffer management for - * a two-stage. - */ - public class TwoStage extends Stage - { - public TwoStage() - { - super(); - - mGain = 0.5f; - } - - public void receive( float sample ) - { - float sum = mSum + sample; - - mSum = sample; - - if( mListener != null ) - { - mListener.receive( sum * mGain ); - } - } - } - - /** - * Output adapter - applies gain correction, applies cleanup filter, casts - * double-valued samples into float-valued complex sample and sends the - * result to the registered listener. - */ - public class Output implements RealSampleListener - { - /* Decimated output buffers will contain 1024 samples */ - private RealToRealBufferAssembler mAssembler; - - private RealFIRFilter_RB_RB mCleanupFilter; - private HalfBandFilter_RB_RB mHalfBandFilter = new HalfBandFilter_RB_RB( - Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO.getCoefficients(), 0.4f, false ); - - public Output( int outputSampleRate, int passFrequency, int attenuation, - WindowType windowType, int outputBufferSize ) - { - mCleanupFilter = new RealFIRFilter_RB_RB( FilterFactory - .getCICCleanupFilter( outputSampleRate, - passFrequency, - attenuation, - windowType ), 0.4f ); - - mAssembler = new RealToRealBufferAssembler( outputBufferSize ); - mAssembler.setListener( mCleanupFilter ); - mCleanupFilter.setListener( mHalfBandFilter ); - - } - - public void dispose() - { - mAssembler.dispose(); - mCleanupFilter.dispose(); - mHalfBandFilter.dispose(); - } - - /** - * Receiver method for the output adapter to receive a filtered, - * decimated sample, apply gain correction, apply cleanup filtering - * and output the sample values as a complex sample. - */ - @Override - public void receive( float sample ) - { - mAssembler.receive( sample * 32.0f ); - } - - /** - * Adds a listener to receive output samples - */ - public void setListener( Listener listener ) - { - mHalfBandFilter.setListener( listener ); + if(x == 0) + { + /* Reference to first stage -- will receive all samples */ + mFirstDecimatingStage = stage; + } + else + { + /* Wire the current stage to the previous stage */ + mDecimatingStages.get(x - 1).setListener(stage); + } } - /** - * Removes the listener from receiving output samples - */ + mOutput = new Output(48000, passFrequency, attenuation, windowType, + outputBufferSize); + + mDecimatingStages.get(mDecimatingStages.size() - 1) + .setListener(mOutput); + } + + public void dispose() + { + for(DecimatingStage stage : mDecimatingStages) + { + stage.dispose(); + } + + mDecimatingStages.clear(); + mDecimatingStages = null; + mFirstDecimatingStage = null; + + mOutput.dispose(); + mOutput = null; + } + + /** + * Adds a listener to receive the output of this CIC decimation filter + */ + public void setListener(Listener listener) + { + mOutput.setListener(listener); + } + + /** + * Removes listener from output of this CIC decimation filter + */ + public void removeListener() + { + mOutput.removeListener(); + } + + /** + * Calculates the prime factors of the decimation rate up to a maximum + * decimation rate of 700. If you wish to have decimation rates higher + * than 700, then add additional prime factors to the PRIMES array. + * + * @param decimation - integral decimation rate + * @return - ordered list (smallest to largest) of prime factors + */ + public static List getPrimeFactors(int decimation) + { + ArrayList stages = new ArrayList(); + + int pointer = 0; + + while(decimation > 0 && pointer < PRIMES.length) + { + int prime = PRIMES[pointer]; + + if(decimation % prime == 0) + { + stages.add(prime); + + decimation /= prime; + } + else + { + pointer++; + } + } + + return stages; + } + + /** + * Primary input method for receiving real buffers. + */ + public void receive(ReusableBuffer buffer) + { + if(mFirstDecimatingStage != null) + { + for(float sample : buffer.getSamples()) + { + mFirstDecimatingStage.receive(sample); + } + } + + buffer.decrementUserCount(); + } + + /** + * Decimating stage combines multiple CIC stages with a decimator. The + * number of stages is indicated by the order value and the size indicates + * the decimation rate of this stage. + */ + public class DecimatingStage implements RealSampleListener + { + private ArrayList mStages = new ArrayList(); + private Stage mFirstStage; + private RealDecimator mDecimator; + + public DecimatingStage(int size, int order) + { + for(int x = 0; x < order; x++) + { + Stage stage; + + if(size == 2) + { + stage = new TwoStage(); + } + else + { + stage = new Stage(size); + } + + mStages.add(stage); + + if(x == 0) + { + mFirstStage = stage; + } + else + { + mStages.get(x - 1).setListener(stage); + } + } + + mDecimator = new RealDecimator(size); + + mStages.get(mStages.size() - 1).setListener(mDecimator); + } + + public void dispose() + { + for(Stage stage : mStages) + { + stage.dispose(); + } + + mStages.clear(); + mDecimator.dispose(); + mDecimator = null; + mFirstStage = null; + mStages = null; + } + + @Override + public void receive(float sample) + { + mFirstStage.receive(sample); + } + + public void setListener(RealSampleListener listener) + { + mDecimator.setListener(listener); + } + } + + /** + * Single non-decimating CIC stage component. Uses a circular buffer and a + * running average internally to implement the stage so that stage size has + * essentially no impact on the computational requirements of the stage + */ + public class Stage implements RealSampleListener + { + protected RealSampleListener mListener; + + private float[] mSamples; + + protected float mSum; + + private int mSamplePointer = 0; + private int mSize; + + protected float mGain; + + public Stage() + { + } + + public Stage(int size) + { + mSize = size - 1; + + mSamples = new float[mSize]; + + mGain = 1.0f / (float)size; + } + + public void dispose() + { + mListener = null; + } + + public void receive(float sample) + { + /* Subtract the oldest sample and add back in the newest */ + mSum = mSum - mSamples[mSamplePointer] + sample; + + /* Overwrite the oldest sample with the newest */ + mSamples[mSamplePointer] = sample; + + mSamplePointer++; + + if(mSamplePointer >= mSize) + { + mSamplePointer = 0; + } + + if(mListener != null) + { + mListener.receive(mSum * mGain); + } + } + + public void setListener(RealSampleListener listener) + { + mListener = listener; + } + } + + /** + * Size 2 stage that removes the unnecessary circular buffer management for + * a two-stage. + */ + public class TwoStage extends Stage + { + public TwoStage() + { + super(); + + mGain = 0.5f; + } + + public void receive(float sample) + { + float sum = mSum + sample; + + mSum = sample; + + if(mListener != null) + { + mListener.receive(sum * mGain); + } + } + } + + /** + * Output adapter - applies gain correction, applies cleanup filter, casts + * double-valued samples into float-valued complex sample and sends the + * result to the registered listener. + */ + public class Output implements RealSampleListener + { + /* Decimated output buffers will contain 1024 samples */ + private ReusableBufferAssembler mAssembler; + private Listener mReusableBufferListener; + + private RealFIRFilter2 mCleanupFilter; + private HalfBandFilter mHalfBandFilter = new HalfBandFilter( + Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO.getCoefficients(), 0.4f, false); + + public Output(int outputSampleRate, int passFrequency, int attenuation, WindowType windowType, int outputBufferSize) + { + mCleanupFilter = new RealFIRFilter2(FilterFactory.getCICCleanupFilter(outputSampleRate, passFrequency, + attenuation, windowType), 0.4f); + + mAssembler = new ReusableBufferAssembler(outputBufferSize); + mAssembler.setListener(new Listener() + { + @Override + public void receive(ReusableBuffer reusableBuffer) + { + + if(mReusableBufferListener != null) + { + ReusableBuffer cleanupFiltered = mCleanupFilter.filter(reusableBuffer); + ReusableBuffer halfBandFiltered = mHalfBandFilter.filter(cleanupFiltered); + mReusableBufferListener.receive(halfBandFiltered); + } + else + { + reusableBuffer.decrementUserCount(); + } + } + }); + } + + public void dispose() + { + mAssembler.dispose(); + mCleanupFilter.dispose(); + mHalfBandFilter.dispose(); + } + + /** + * Receiver method for the output adapter to receive a filtered, + * decimated sample, apply gain correction, apply cleanup filtering + * and output the sample values as a complex sample. + */ + @Override + public void receive(float sample) + { + mAssembler.receive(sample * 32.0f); + } + + /** + * Adds a listener to receive output samples + */ + public void setListener(Listener listener) + { + mReusableBufferListener = listener; + } + + /** + * Removes the listener from receiving output samples + */ public void removeListener() { - mHalfBandFilter.removeListener(); + mReusableBufferListener = null; } - } + } } diff --git a/src/main/java/io/github/dsheirer/dsp/filter/correction/IQCorrectionFilter.java b/src/main/java/io/github/dsheirer/dsp/filter/correction/IQCorrectionFilter.java index ca219ac5..1264201a 100644 --- a/src/main/java/io/github/dsheirer/dsp/filter/correction/IQCorrectionFilter.java +++ b/src/main/java/io/github/dsheirer/dsp/filter/correction/IQCorrectionFilter.java @@ -18,13 +18,15 @@ ******************************************************************************/ package io.github.dsheirer.dsp.filter.correction; -import io.github.dsheirer.sample.complex.ComplexBuffer; +import io.github.dsheirer.sample.buffer.ReusableComplexBuffer; +import io.github.dsheirer.sample.buffer.ReusableComplexBufferQueue; public class IQCorrectionFilter { private float mRatio = 0.00001f; private float mAverageInphase = 0.0f; private float mAverageQuadrature = 0.0f; + private ReusableComplexBufferQueue mReusableComplexBufferQueue = new ReusableComplexBufferQueue("IQCorrectionFilter"); public IQCorrectionFilter(float ratio) { @@ -35,17 +37,23 @@ public class IQCorrectionFilter { } - public void filter(ComplexBuffer buffer) + public ReusableComplexBuffer filter(ReusableComplexBuffer buffer) { float[] samples = buffer.getSamples(); + ReusableComplexBuffer filtered = mReusableComplexBufferQueue.getBuffer(samples.length); + filtered.incrementUserCount(); + float[] filteredSamples = filtered.getSamples(); + for(int x = 0; x < samples.length; x += 2) { mAverageInphase = mAverageInphase + (mRatio * (samples[x] - mAverageInphase)); - samples[x] -= mAverageInphase; + filteredSamples[x] = samples[x] - mAverageInphase; mAverageQuadrature = mAverageQuadrature + (mRatio * (samples[x + 1] - mAverageQuadrature)); - samples[x + 1] -= mAverageQuadrature; + filteredSamples[x + 1] = samples[x + 1] - mAverageQuadrature; } + + return filtered; } } diff --git a/src/main/java/io/github/dsheirer/dsp/filter/fir/complex/ComplexFIRFilter.java b/src/main/java/io/github/dsheirer/dsp/filter/fir/complex/ComplexFIRFilter.java deleted file mode 100644 index a61f04f2..00000000 --- a/src/main/java/io/github/dsheirer/dsp/filter/fir/complex/ComplexFIRFilter.java +++ /dev/null @@ -1,129 +0,0 @@ -/******************************************************************************* - * sdr-trunk - * Copyright (C) 2014-2018 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 io.github.dsheirer.dsp.filter.fir.complex; - -import io.github.dsheirer.dsp.filter.fir.FIRFilter; -import io.github.dsheirer.dsp.filter.fir.real.RealFIRFilter; -import io.github.dsheirer.sample.buffer.ReusableComplexBuffer; -import io.github.dsheirer.sample.buffer.ReusableComplexBufferQueue; -import io.github.dsheirer.sample.complex.Complex; - -@Deprecated //Use ComplexFIRFilter2 instead -public class ComplexFIRFilter extends FIRFilter -{ - private ReusableComplexBufferQueue mReusableComplexBufferQueue = new ReusableComplexBufferQueue("Complex FIR Filter"); - private RealFIRFilter mIFilter; - private RealFIRFilter mQFilter; - private float[] mTemporaryBuffer; - - /** - * Complex FIR Filter for processing complex sample pairs. Wraps two real - * FIR filters for processing each of the inphase and quadrature samples. - * - * @param coefficients - filter taps - * @param gain - gain to apply to filtered outputs - use 1.0f for no gain - */ - public ComplexFIRFilter(float[] coefficients, float gain) - { - mIFilter = new RealFIRFilter(coefficients, gain); - mQFilter = new RealFIRFilter(coefficients, gain); - } - - public float[] getCoefficients() - { - return mIFilter.getCoefficients(); - } - - public float filterInphase(float sample) - { - return mIFilter.filter(sample); - } - - public float currentInphaseValue() - { - return mIFilter.currentValue(); - } - - public float filterQuadrature(float sample) - { - return mQFilter.filter(sample); - } - - public float currentQuadratureValue() - { - return mQFilter.currentValue(); - } - - public Complex filter(Complex sample) - { - float i = filterInphase(sample.inphase()); - float q = filterQuadrature(sample.quadrature()); - - return new Complex(i, q); - } - - public float[] filter(float[] samples) - { - if(mTemporaryBuffer == null || mTemporaryBuffer.length != samples.length) - { - mTemporaryBuffer = new float[samples.length]; - } - - System.arraycopy(samples, 0, mTemporaryBuffer, 0, samples.length); - - for(int x = 0; x < samples.length; x += 2) - { - samples[x] = filterInphase(mTemporaryBuffer[x]); - samples[x + 1] = filterQuadrature(mTemporaryBuffer[x + 1]); - } - - return samples; - } - - /** - * Filters the complex samples from the buffer and returns a new complex buffer with the filtered output - * - * @param originalBuffer with complex samples to filter - * @return new buffer containing filtered complex samples - */ - public ReusableComplexBuffer filter(ReusableComplexBuffer originalBuffer) - { - ReusableComplexBuffer filteredBuffer = mReusableComplexBufferQueue.getBuffer(originalBuffer.getSamples().length); - filteredBuffer.setTimestamp(originalBuffer.getTimestamp()); - - float[] samples = originalBuffer.getSamples(); - float[] filteredSamples = filteredBuffer.getSamples(); - - for(int x = 0; x < originalBuffer.getSamples().length; x += 2) - { - filteredSamples[x] = filterInphase(samples[x]); - filteredSamples[x + 1] = filterQuadrature(samples[x + 1]); - } - - originalBuffer.decrementUserCount(); - filteredBuffer.incrementUserCount(); - - return filteredBuffer; - } - - @Override - public void dispose() - { - mIFilter.dispose(); - mQFilter.dispose(); - mReusableComplexBufferQueue.dispose(); - } -} diff --git a/src/main/java/io/github/dsheirer/dsp/filter/fir/complex/ComplexFIRFilter_CB_CB.java b/src/main/java/io/github/dsheirer/dsp/filter/fir/complex/ComplexFIRFilter_CB_CB.java deleted file mode 100644 index bb1e3c0a..00000000 --- a/src/main/java/io/github/dsheirer/dsp/filter/fir/complex/ComplexFIRFilter_CB_CB.java +++ /dev/null @@ -1,68 +0,0 @@ -package io.github.dsheirer.dsp.filter.fir.complex; - -import io.github.dsheirer.sample.Listener; -import io.github.dsheirer.sample.complex.ComplexBuffer; - -@Deprecated //Use ComplexFIRFilter2 instead -public class ComplexFIRFilter_CB_CB extends ComplexFIRFilter implements Listener -{ - private Listener mListener; - - /** - * Complex FIR Filter for processing complex buffers. Wraps two real FIR - * filters for processing each of the inphase and quadrature samples. - * - * @param coefficients - odd length symmetric filter taps - * @param gain - gain value to apply to each of the filtered samples - */ - public ComplexFIRFilter_CB_CB(float[] coefficients, float gain) - { - super(coefficients, gain); - } - - public void dispose() - { - mListener = null; - } - - @Override - public void receive(ComplexBuffer buffer) - { - if(mListener != null) - { - float[] samples = buffer.getSamples(); - - for(int x = 0; x < samples.length; x += 2) - { - samples[x] = filterInphase(samples[x]); - samples[x + 1] = filterQuadrature(samples[x + 1]); - } - - mListener.receive(buffer); - } - } - - /** - * Filters the samples and send the results to the registered listener - */ - public void receive(float[] samples) - { - if(mListener != null) - { - float[] filtered = filter(samples); - - //TODO: this should be a reusable buffer here. - mListener.receive(new ComplexBuffer(filtered)); - } - } - - public void setListener(Listener listener) - { - mListener = listener; - } - - public void removeListener() - { - mListener = null; - } -} diff --git a/src/main/java/io/github/dsheirer/dsp/filter/fir/real/RealFIRFilter.java b/src/main/java/io/github/dsheirer/dsp/filter/fir/real/RealFIRFilter.java deleted file mode 100644 index bc42fb6b..00000000 --- a/src/main/java/io/github/dsheirer/dsp/filter/fir/real/RealFIRFilter.java +++ /dev/null @@ -1,136 +0,0 @@ -/******************************************************************************* - * sdr-trunk - * Copyright (C) 2014-2018 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 io.github.dsheirer.dsp.filter.fir.real; - -import io.github.dsheirer.dsp.filter.fir.FIRFilter; - -@Deprecated //Use RealFIRFilter2 instead -public class RealFIRFilter extends FIRFilter -{ - private float[] mBuffer; - private int mBufferSize = 1; - private int mBufferPointer = 0; - private int[][] mIndexMap; - - private float[] mCoefficients; - private float mGain; - private float mAccumulator; - - /** - * Float sample FIR filter base class. - * - * @param coefficients - filter coefficients - * @param gain value to apply to the filtered output - use 1.0f for no gain - */ - public RealFIRFilter(float[] coefficients, float gain) - { - mCoefficients = coefficients; - mGain = gain; - - mBufferSize = mCoefficients.length; - - mBuffer = new float[mBufferSize]; - mBufferPointer = mBufferSize - 1; - - generateIndexMap(mBufferSize); - } - - public float[] getCoefficients() - { - return mCoefficients; - } - - @Override - public void dispose() - { - mCoefficients = null; - mIndexMap = null; - mBuffer = null; - } - - /** - * Loads the sample into the circular buffer without calculating a filtered value. - * @param sample to load - */ - public void load(float sample) - { - mBuffer[mBufferPointer] = sample; - - mBufferPointer--; - - if(mBufferPointer < 0) - { - mBufferPointer += mBufferSize; - } - } - - /** - * Loads the sample into the circular buffer and calculates a filtered sample output. - * @param sample to load - * @return filtered value - */ - public float filter(float sample) - { - mBuffer[mBufferPointer] = sample; - - mAccumulator = 0.0f; - - for(int x = 0; x < mBufferSize; x++) - { - mAccumulator += mCoefficients[x] * mBuffer[mIndexMap[mBufferPointer][x]]; - } - - mBufferPointer--; - - if(mBufferPointer < 0) - { - mBufferPointer += mBufferSize; - } - - /* Apply gain and return the filtered value */ - mAccumulator *= mGain; - - return mAccumulator; - } - - /** - * Current output value for the filter after the filter() method has been invoked. - * @return - */ - public float currentValue() - { - return mAccumulator; - } - - /** - * Generates a circular buffer index map to support lookup of the translated - * index based on the current buffer pointer and the desired sample index. - */ - private void generateIndexMap(int size) - { - mIndexMap = new int[size][size]; - - for(int x = 0; x < size; x++) - { - for(int y = 0; y < size; y++) - { - int z = x + y; - - mIndexMap[x][y] = z < size ? z : z - size; - } - } - } -} \ No newline at end of file diff --git a/src/main/java/io/github/dsheirer/dsp/filter/fir/real/RealFIRFilter_RB_RB.java b/src/main/java/io/github/dsheirer/dsp/filter/fir/real/RealFIRFilter_RB_RB.java deleted file mode 100644 index 5f9a7669..00000000 --- a/src/main/java/io/github/dsheirer/dsp/filter/fir/real/RealFIRFilter_RB_RB.java +++ /dev/null @@ -1,68 +0,0 @@ -package io.github.dsheirer.dsp.filter.fir.real; - -import io.github.dsheirer.sample.Listener; -import io.github.dsheirer.sample.real.RealBuffer; - -@Deprecated //Use RealFIRFilter2 version instead -public class RealFIRFilter_RB_RB extends RealFIRFilter - implements Listener -{ - private Listener mListener; - - /** - * Real Buffer Float FIR filter with streaming float buffer provider and - * listener interfaces. - * - * @param coefficients - * @param gain - */ - public RealFIRFilter_RB_RB( float[] coefficients, float gain ) - { - super( coefficients, gain ); - } - - @Override - public void dispose() - { - super.dispose(); - - mListener = null; - } - - @Override - public void receive( RealBuffer buffer ) - { - if( mListener != null ) - { - mListener.receive( filter( buffer ) ); - } - } - - /** - * Filters the buffer samples - * - * @param buffer - * @return - */ - public RealBuffer filter( RealBuffer buffer ) - { - float[] samples = buffer.getSamples(); - - for( int x = 0; x < samples.length; x++ ) - { - samples[ x ] = filter( samples[ x ] ); - } - - return buffer; - } - - public void setListener( Listener listener ) - { - mListener = listener; - } - - public void removeListener() - { - mListener = null; - } -} diff --git a/src/main/java/io/github/dsheirer/dsp/filter/halfband/complex/ComplexHalfBandFilter.java b/src/main/java/io/github/dsheirer/dsp/filter/halfband/complex/ComplexHalfBandFilter.java new file mode 100644 index 00000000..7684676e --- /dev/null +++ b/src/main/java/io/github/dsheirer/dsp/filter/halfband/complex/ComplexHalfBandFilter.java @@ -0,0 +1,106 @@ +/******************************************************************************* + * sdr-trunk + * Copyright (C) 2014-2018 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 io.github.dsheirer.dsp.filter.halfband.complex; + +import io.github.dsheirer.dsp.filter.halfband.real.HalfBandFilter; +import io.github.dsheirer.sample.buffer.ReusableComplexBuffer; + +public class ComplexHalfBandFilter +{ + private HalfBandFilter mIFilter; + private HalfBandFilter mQFilter; + private boolean mDecimate; + private boolean mDecimateFlag; + + /** + * @param coefficients + * @param gain + * @param decimate + */ + public ComplexHalfBandFilter(float[] coefficients, float gain, boolean decimate) + { + mIFilter = new HalfBandFilter(coefficients, gain); + mQFilter = new HalfBandFilter(coefficients, gain); + mDecimate = decimate; + } + + public void dispose() + { + mIFilter.dispose(); + mQFilter.dispose(); + } + + public ReusableComplexBuffer filter(ReusableComplexBuffer buffer) + { + throw new IllegalStateException("This filter has to be updated to work with reusable buffers"); + +// if(mDecimate) +// { +// float[] samples = buffer.getSamples(); +// +// int half = samples.length / 2; +// +// float[] decimated; +// +// if(half % 2 == 0 || mDecimateFlag) +// { +// decimated = new float[half]; +// } +// else +// { +// /* If inbound buffer size is odd-length, then we have to +// * adjust when the first operation is non-decimation, since +// * that will produce an outbound buffer 1 sample larger */ +// decimated = new float[half + 1]; +// } +// +// int decimatedPointer = 0; +// +// for(int x = 0; x < samples.length; x += 2) +// { +// /* Insert the sample but don't filter */ +// if(mDecimateFlag) +// { +// mIFilter.insert(samples[x]); +// mQFilter.insert(samples[x + 1]); +// } +// else +// { +// decimated[decimatedPointer++] = mIFilter.filter(samples[x]); +// decimated[decimatedPointer++] = mQFilter.filter(samples[x + 1]); +// } +// +// /* Toggle the decimation flag for every sample */ +// mDecimateFlag = !mDecimateFlag; +// } +// +// //return buffer +// } +// else +// { +// /* Non Decimate - filter each of the values and return the buffer */ +// float[] samples = buffer.getSamples(); +// +// for(int x = 0; x < samples.length; x += 2) +// { +// samples[x] = mIFilter.filter(samples[x]); +// samples[x + 1] = mQFilter.filter(samples[x + 1]); +// } +// +// //return buffer +// } + } +} diff --git a/src/main/java/io/github/dsheirer/dsp/filter/halfband/complex/HalfBandFilter_CB_CB.java b/src/main/java/io/github/dsheirer/dsp/filter/halfband/complex/HalfBandFilter_CB_CB.java deleted file mode 100644 index 337077be..00000000 --- a/src/main/java/io/github/dsheirer/dsp/filter/halfband/complex/HalfBandFilter_CB_CB.java +++ /dev/null @@ -1,117 +0,0 @@ -package io.github.dsheirer.dsp.filter.halfband.complex; - -import io.github.dsheirer.dsp.filter.halfband.real.HalfBandFilter; -import io.github.dsheirer.sample.Listener; -import io.github.dsheirer.sample.complex.ComplexBuffer; - -public class HalfBandFilter_CB_CB implements Listener -{ - private HalfBandFilter mIFilter; - private HalfBandFilter mQFilter; - - private boolean mDecimate; - private boolean mDecimateFlag; - - private Listener mListener; - - /** - * - * @param coefficients - * @param gain - * @param decimate - */ - public HalfBandFilter_CB_CB( float[] coefficients, float gain, boolean decimate ) - { - mIFilter = new HalfBandFilter( coefficients, gain ); - mQFilter = new HalfBandFilter( coefficients, gain ); - - mDecimate = decimate; - } - - public void dispose() - { - mIFilter.dispose(); - mQFilter.dispose(); - - mListener = null; - } - - @Override - public void receive( ComplexBuffer buffer ) - { - if( mListener != null ) - { - if( mDecimate ) - { - float[] samples = buffer.getSamples(); - - int half = samples.length / 2; - - float[] decimated; - - if( half % 2 == 0 || mDecimateFlag ) - { - decimated = new float[ half ]; - } - else - { - /* If inbound buffer size is odd-length, then we have to - * adjust when the first operation is non-decimation, since - * that will produce an outbound buffer 1 sample larger */ - decimated = new float[ half + 1 ]; - } - - int decimatedPointer = 0; - - for( int x = 0; x < samples.length; x += 2 ) - { - /* Insert the sample but don't filter */ - if( mDecimateFlag ) - { - mIFilter.insert( samples[ x ] ); - mQFilter.insert( samples[ x + 1 ] ); - } - else - { - decimated[ decimatedPointer++ ] = mIFilter.filter( samples[ x ] ); - decimated[ decimatedPointer++ ] = mQFilter.filter( samples[ x + 1 ] ); - } - - /* Toggle the decimation flag for every sample */ - mDecimateFlag = !mDecimateFlag; - } - - if( mListener != null ) - { - mListener.receive( new ComplexBuffer( decimated ) ); - } - } - else - { - /* Non Decimate - filter each of the values and return the buffer */ - float[] samples = buffer.getSamples(); - - for( int x = 0; x < samples.length; x += 2 ) - { - samples[ x ] = mIFilter.filter( samples[ x ] ); - samples[ x + 1 ] = mQFilter.filter( samples[ x + 1 ] ); - } - - if( mListener != null ) - { - mListener.receive( buffer ); - } - } - } - } - - public void setListener( Listener listener ) - { - mListener = listener; - } - - public void removeListener() - { - mListener = null; - } -} diff --git a/src/main/java/io/github/dsheirer/dsp/filter/halfband/real/HalfBandFilter.java b/src/main/java/io/github/dsheirer/dsp/filter/halfband/real/HalfBandFilter.java index 678cb8e5..21255f08 100644 --- a/src/main/java/io/github/dsheirer/dsp/filter/halfband/real/HalfBandFilter.java +++ b/src/main/java/io/github/dsheirer/dsp/filter/halfband/real/HalfBandFilter.java @@ -1,162 +1,189 @@ /******************************************************************************* * SDR Trunk * Copyright (C) 2014,2015 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 io.github.dsheirer.dsp.filter.halfband.real; +import io.github.dsheirer.sample.buffer.ReusableBuffer; +import io.github.dsheirer.sample.buffer.ReusableBufferQueue; import org.apache.commons.lang3.Validate; public class HalfBandFilter { - private float[] mBuffer; + private float[] mBuffer; private int mBufferSize = 1; //Temporary initial value - private int mBufferPointer = 0; - - private float mGain; - private float[] mCoefficients; - private int[][] mIndexMap; - - private int mCenterCoefficient; - private int mCenterCoefficientMapIndex; + private int mBufferPointer = 0; - /** - * Half-Band filter for processing real float samples. - * - * Takes advantage of the 0-valued FIR half-band coefficents inherent - * in the half-band filter and does not calculate those coefficients. - * - * This reduces the workload to (tap-size - 1) / 4 + 1 calculations per sample. - * - * @param filter - filter coefficients - * @param gain - gain multiplier. Use 1.0 for unity/no gain - */ - public HalfBandFilter( float[] coefficients, float gain ) - { - mCoefficients = coefficients; - mGain = gain; - - mBufferSize = mCoefficients.length; - mBuffer = new float[ mBufferSize ]; - - generateIndexMap( mCoefficients.length ); - } + private float mGain; + private float[] mCoefficients; + private int[][] mIndexMap; - public void dispose() - { - } + private int mCenterCoefficient; + private int mCenterCoefficientMapIndex; - /** - * Inserts the sample into the buffer without calculating a filtered value - */ - public void insert( float sample ) - { - mBuffer[ mBufferPointer ] = sample; + private ReusableBufferQueue mReusableBufferQueue = new ReusableBufferQueue("HalfBandFilter"); - mBufferPointer++; - - mBufferPointer = mBufferPointer % mBufferSize; - } + /** + * Half-Band filter for processing real float samples. + * + * Takes advantage of the 0-valued FIR half-band coefficents inherent + * in the half-band filter and does not calculate those coefficients. + * + * This reduces the workload to (tap-size - 1) / 4 + 1 calculations per sample. + * + * @param filter - filter coefficients + * @param gain - gain multiplier. Use 1.0 for unity/no gain + */ + public HalfBandFilter(float[] coefficients, float gain) + { + //TODO: update this to use SIMD structures with 2 float arrays and a toggle operation - /** - * Calculate the filtered value by applying the coefficients against - * the complex samples in mBuffer - */ - public float filter( float sample ) - { - insert( sample ); - - float accumulator = 0; - //Start with the center tap value - accumulator += mCoefficients[ mCenterCoefficient ] * - mBuffer[ mIndexMap[ mBufferPointer ][ mCenterCoefficientMapIndex ] ]; - - //For the remaining coefficients, add the symmetric samples, oldest and newest - //first, then multiply by the single coefficient - for( int x = 0; x < mCenterCoefficientMapIndex; x += 2 ) - { - accumulator += mCoefficients[ x ] * - ( mBuffer[ mIndexMap[ mBufferPointer ][ x ] ] + - mBuffer[ mIndexMap[ mBufferPointer ][ x + 1 ] ] ); - } + mCoefficients = coefficients; + mGain = gain; - return accumulator * mGain; - } - - /** - * Creates an n X (n + 1 / 2) index map enabling quick access to the - * circular buffer samples. - * - * As the buffer shifts right with each subsequent sample, we have to move - * the index pointers with it, for efficient access of the samples. - * - * The first array index value in the index map corresponds to the current - * buffer pointer location. - * - * The second array index value points to the samples that should be - * multiplied by the coefficients as follows: - * - * 0 = center tap sample, to be multiplied by center coefficient - * - * 0 = sample( 1 ) - * 1 = sample( size - 1 ) - * - * Indexes 0 and 1 will be multiplied by coefficient( 0 ). - * - * Subsequent indexes 3, 4, etc, point to the oldest and newest samples that - * correspond to the matching ( 3 ) coefficient index. - * - * @param odd-sized number of filter taps (ie coefficients) and buffer - */ - private void generateIndexMap( int size ) - { - //Ensure we have an odd size - Validate.isTrue(size % 2 == 1); + mBufferSize = mCoefficients.length; + mBuffer = new float[mBufferSize]; - int mapWidth = ( ( size + 1 ) / 2 ) + 1; - - //Set center tap index for coefficients array - mCenterCoefficient = ( size - 1 ) / 2; - mCenterCoefficientMapIndex = mCenterCoefficient + 1; + generateIndexMap(mCoefficients.length); + } - mIndexMap = new int[ size ][ mapWidth ]; + public HalfBandFilter(float[] coefficients, float gain, boolean decimate) + { + this(coefficients, gain); - //Setup the first row, buffer pointer index 0, as a starting point - for( int x = 0; x < mapWidth - 2; x += 2 ) - { - mIndexMap[ 0 ][ x ] = x; - mIndexMap[ 0 ][ x + 1 ] = size - 1 - x; - } + throw new IllegalStateException("This filter must be updated to support non-decimate option"); + } - //Place center tap index in last element - mIndexMap[ 0 ][ mCenterCoefficientMapIndex ] = mCenterCoefficient; - - //For each subsequent row, increment the previous row's value by 1, - //subtracting size as needed, to keep the values between 0 and size - 1 - for( int x = 1; x < size; x++ ) - { - for( int y = 0; y < mapWidth; y++ ) - { - mIndexMap[ x ][ y ] = mIndexMap[ x - 1 ][ y ] + 1; - - if( mIndexMap[ x ][ y ] >= size ) - { - mIndexMap[ x ][ y ] -= size; - } - } - } - } + public void dispose() + { + } + + /** + * Inserts the sample into the buffer without calculating a filtered value + */ + public void insert(float sample) + { + mBuffer[mBufferPointer] = sample; + + mBufferPointer++; + + mBufferPointer = mBufferPointer % mBufferSize; + } + + public ReusableBuffer filter(ReusableBuffer input) + { + float[] inputSamples = input.getSamples(); + ReusableBuffer output = mReusableBufferQueue.getBuffer(inputSamples.length); + float[] outputSamples = output.getSamples(); + + for(int x = 0; x < inputSamples.length; x++) + { + outputSamples[x] = filter(inputSamples[x]); + } + + output.incrementUserCount(); + input.decrementUserCount(); + + return output; + } + + /** + * Calculate the filtered value by applying the coefficients against the samples in mBuffer + */ + public float filter(float sample) + { + insert(sample); + + float accumulator = 0; + + //Start with the center tap value + accumulator += mCoefficients[mCenterCoefficient] * mBuffer[mIndexMap[mBufferPointer][mCenterCoefficientMapIndex]]; + + //For the remaining coefficients, add the symmetric samples, oldest and newest + //first, then multiply by the single coefficient + for(int x = 0; x < mCenterCoefficientMapIndex; x += 2) + { + accumulator += mCoefficients[x] * (mBuffer[mIndexMap[mBufferPointer][x]] + mBuffer[mIndexMap[mBufferPointer][x + 1]]); + } + + return accumulator * mGain; + } + + /** + * Creates an n X (n + 1 / 2) index map enabling quick access to the + * circular buffer samples. + * + * As the buffer shifts right with each subsequent sample, we have to move + * the index pointers with it, for efficient access of the samples. + * + * The first array index value in the index map corresponds to the current + * buffer pointer location. + * + * The second array index value points to the samples that should be + * multiplied by the coefficients as follows: + * + * 0 = center tap sample, to be multiplied by center coefficient + * + * 0 = sample( 1 ) + * 1 = sample( size - 1 ) + * + * Indexes 0 and 1 will be multiplied by coefficient( 0 ). + * + * Subsequent indexes 3, 4, etc, point to the oldest and newest samples that + * correspond to the matching ( 3 ) coefficient index. + * + * @param odd-sized number of filter taps (ie coefficients) and buffer + */ + private void generateIndexMap(int size) + { + //Ensure we have an odd size + Validate.isTrue(size % 2 == 1); + + int mapWidth = ((size + 1) / 2) + 1; + + //Set center tap index for coefficients array + mCenterCoefficient = (size - 1) / 2; + mCenterCoefficientMapIndex = mCenterCoefficient + 1; + + mIndexMap = new int[size][mapWidth]; + + //Setup the first row, buffer pointer index 0, as a starting point + for(int x = 0; x < mapWidth - 2; x += 2) + { + mIndexMap[0][x] = x; + mIndexMap[0][x + 1] = size - 1 - x; + } + + //Place center tap index in last element + mIndexMap[0][mCenterCoefficientMapIndex] = mCenterCoefficient; + + //For each subsequent row, increment the previous row's value by 1, + //subtracting size as needed, to keep the values between 0 and size - 1 + for(int x = 1; x < size; x++) + { + for(int y = 0; y < mapWidth; y++) + { + mIndexMap[x][y] = mIndexMap[x - 1][y] + 1; + + if(mIndexMap[x][y] >= size) + { + mIndexMap[x][y] -= size; + } + } + } + } } diff --git a/src/main/java/io/github/dsheirer/dsp/filter/halfband/real/HalfBandFilter_RB_RB.java b/src/main/java/io/github/dsheirer/dsp/filter/halfband/real/HalfBandFilter_RB_RB.java deleted file mode 100644 index 914d9f2b..00000000 --- a/src/main/java/io/github/dsheirer/dsp/filter/halfband/real/HalfBandFilter_RB_RB.java +++ /dev/null @@ -1,117 +0,0 @@ -package io.github.dsheirer.dsp.filter.halfband.real; - -import io.github.dsheirer.sample.Listener; -import io.github.dsheirer.sample.real.RealBuffer; - -public class HalfBandFilter_RB_RB extends HalfBandFilter implements Listener -{ - private boolean mDecimate; - private boolean mDecimateFlag; - private Listener mListener; - - /** - * Halfband filter for real buffers with optional decimation by two. - * - * If this filter is used in non-decimating mode, the filter will overwrite - * the inbound sample buffer with the filtered samples. This will cause - * issues if the buffers are being used across several processes are sharing - * the buffer, since this filter will modify the contents of the object, so - * consider making a copy of the buffer prior to feeding the buffer to this - * filter. - * - * If used in decimating mode, a new (half-length) buffer will be created - * for the filtered samples. - * - * @param coefficients - filter kernel - * @param gain - gain to apply to the outputs - * @param decimate - true for decimate by 2, or false for no decimation - */ - public HalfBandFilter_RB_RB( float[] coefficients, float gain, boolean decimate ) - { - super( coefficients, gain ); - - mDecimate = decimate; - } - - public void dispose() - { - super.dispose(); - - mListener = null; - } - - @Override - public void receive( RealBuffer buffer ) - { - if( mListener != null ) - { - mListener.receive( filter( buffer ) ); - } - } - - public RealBuffer filter( RealBuffer buffer ) - { - if( mDecimate ) - { - float[] samples = buffer.getSamples(); - - int half = samples.length / 2; - - float[] decimated; - - if( half % 2 == 0 || mDecimateFlag ) - { - decimated = new float[ half ]; - } - else - { - /* If inbound buffer size is odd-length, then we have to - * adjust when the first operation is non-decimation, since - * that will produce an outbound buffer 1 sample larger */ - decimated = new float[ half + 1 ]; - } - - int decimatedPointer = 0; - - for( float sample: samples ) - { - /* Insert the sample but don't filter */ - if( mDecimateFlag ) - { - insert( sample ); - } - else - { - decimated[ decimatedPointer++ ] = filter( sample ); - } - - /* Toggle the decimation flag for every sample */ - mDecimateFlag = !mDecimateFlag; - } - - return new RealBuffer( decimated ); - } - else - { - /* Non Decimate - filter each of the values and return the buffer */ - float[] samples = buffer.getSamples(); - - for( int x = 0; x < samples.length; x++ ) - { - samples[ x ] = filter( samples[ x ] ); - } - - return buffer; - } - } - - public void setListener( Listener listener ) - { - mListener = listener; - } - - public void removeListener() - { - mListener = null; - } -} diff --git a/src/main/java/io/github/dsheirer/dsp/filter/hilbert/HilbertTransform.java b/src/main/java/io/github/dsheirer/dsp/filter/hilbert/HilbertTransform.java index 973ee726..98894616 100644 --- a/src/main/java/io/github/dsheirer/dsp/filter/hilbert/HilbertTransform.java +++ b/src/main/java/io/github/dsheirer/dsp/filter/hilbert/HilbertTransform.java @@ -1,281 +1,245 @@ +/******************************************************************************* + * sdr-trunk + * Copyright (C) 2014-2018 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 io.github.dsheirer.dsp.filter.hilbert; -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2015 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 - ******************************************************************************/ - import io.github.dsheirer.dsp.filter.Filters; -import io.github.dsheirer.sample.Listener; -import io.github.dsheirer.sample.complex.ComplexBuffer; -import io.github.dsheirer.sample.real.RealBuffer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Arrays; -public class HilbertTransform implements Listener +public class HilbertTransform { - private static final Logger mLog = LoggerFactory.getLogger( HilbertTransform.class ); + private static final Logger mLog = LoggerFactory.getLogger(HilbertTransform.class); - private float[] mHilbertFilter; - - private Listener mListener; - - private float[] mBuffer; + private float[] mHilbertFilter; + private float[] mBuffer; private int mBufferSize; - private int mBufferPointer; - - private int[][] mIndexMap; - private int mMapHeight; - private int mCenterTapIndex; - private float mCenterCoefficent; - - private boolean mInvertFlag = false; - - /** - * Hilbert transform filter used for converting real-valued samples into - * complex valued samples using frequency translation (FS/4) and a half-band - * filter. - * - * This filter uses a circular sample buffer and a pre-calculated - * index map to correctly map the filter coefficients to the circular delay - * buffer contents as each new sample is added to the buffer and processed. - * - * Half-band filter coefficients used in this filter must be of length N - * where (N + 1) is a multiple of 4. This filter uses a pre-defined half - * band filter with 47 coefficients. - * - * This transform process is described in Understanding Digital Signal - * Processing, Lyons, 3e, 2011, sections 13.1.2 and 13.1.3 (p 674-678) and - * implemented as described in Section 13.37.1 and 13.37.2 (p 802-804) - */ - public HilbertTransform() - { - convertHalfBandToHilbert( Filters.HALF_BAND_FILTER_47T.getCoefficients() ); - - mBufferSize = mHilbertFilter.length + 1; - mBuffer = new float[ mBufferSize ]; - - mCenterCoefficent = mHilbertFilter[ mHilbertFilter.length / 2 ]; + private int mBufferPointer; + private int[][] mIndexMap; + private int mMapHeight; + private int mCenterTapIndex; + private float mCenterCoefficent; - generateIndexMap( mHilbertFilter.length ); - } + private boolean mInvertFlag = false; - @Override - public void receive( RealBuffer buffer ) - { - if( mListener != null ) - { - mListener.receive( filter( buffer ) ); - } - } - - /** - * Sets the listener to receive filtered complex buffers. - */ - public void setListener( Listener listener ) - { - mListener = listener; - } - - /** - * Applies Hilbert transform to the real sample buffer and returns a - * complex sample buffer - */ - public ComplexBuffer filter( RealBuffer buffer ) - { - return new ComplexBuffer( filter( buffer.getSamples() ) ); - } + /** + * Hilbert transform filter used for converting real-valued samples into + * complex valued samples using frequency translation (FS/4) and a half-band + * filter. + * + * This filter uses a circular sample buffer and a pre-calculated + * index map to correctly map the filter coefficients to the circular delay + * buffer contents as each new sample is added to the buffer and processed. + * + * Half-band filter coefficients used in this filter must be of length N + * where (N + 1) is a multiple of 4. This filter uses a pre-defined half + * band filter with 47 coefficients. + * + * This transform process is described in Understanding Digital Signal + * Processing, Lyons, 3e, 2011, sections 13.1.2 and 13.1.3 (p 674-678) and + * implemented as described in Section 13.37.1 and 13.37.2 (p 802-804) + */ + public HilbertTransform() + { + convertHalfBandToHilbert(Filters.HALF_BAND_FILTER_47T.getCoefficients()); - /** - * Filters the continuous real-sample stream by treating the stream as a - * set of interleaved values and applying two hilbert filters against each - * interleaved set (I & Q). Frequency translation has already been - * pre-applied to the hilbert filter coefficients. - * - * Filtering of the inphase component is a simple multiply by the center - * coefficient (0.5) since all of the other coefficients are zero-valued. - * - * Filtering of the quadrature component uses a folded FIR structure and - * expects the hilbert coefficients to be symmetric with all negative valued - * coefficients below the center coefficient and all positive-valued coefficients - * above the center coefficient. So, convolution only applies the filter - * coefficient against the difference of the two samples. - * - * ( coefficient * value1 ) + (-coefficient * value2 ) = coefficient * ( value1 - value2 ) - * - * Performs frequency translation by FS/2 on the final filtered values by - * applying a sequence of 1,-1 (sign change) to each I/Q output sample. - */ - public float[] filter( float[] samples ) - { - float accumulator; + mBufferSize = mHilbertFilter.length + 1; + mBuffer = new float[mBufferSize]; - for( int y = 0; y < samples.length; y +=2 ) - { - insert( samples[ y ] ); - insert( samples[ y + 1 ] ); - - accumulator = 0.0f; + mCenterCoefficent = mHilbertFilter[mHilbertFilter.length / 2]; - int index = mBufferPointer / 2; + generateIndexMap(mHilbertFilter.length); + } - for( int x = 0; x < mHilbertFilter.length / 2; x += 2 ) - { - accumulator += mHilbertFilter[ x ] * - ( mBuffer[ mIndexMap[ index ][ x + 1] ] - - mBuffer[ mIndexMap[ index ][ x ] ] ); - } + /** + * Filters the continuous real-sample stream by treating the stream as a + * set of interleaved values and applying two hilbert filters against each + * interleaved set (I & Q). Frequency translation has already been + * pre-applied to the hilbert filter coefficients. + * + * Filtering of the inphase component is a simple multiply by the center + * coefficient (0.5) since all of the other coefficients are zero-valued. + * + * Filtering of the quadrature component uses a folded FIR structure and + * expects the hilbert coefficients to be symmetric with all negative valued + * coefficients below the center coefficient and all positive-valued coefficients + * above the center coefficient. So, convolution only applies the filter + * coefficient against the difference of the two samples. + * + * ( coefficient * value1 ) + (-coefficient * value2 ) = coefficient * ( value1 - value2 ) + * + * Performs frequency translation by FS/2 on the final filtered values by + * applying a sequence of 1,-1 (sign change) to each I/Q output sample. + */ + public float[] filter(float[] samples) + { + float accumulator; - //Perform FS/2 frequency translation on the final filtered values - if( mInvertFlag ) - { - //inphase - samples[ y ] = -( mBuffer[ mIndexMap[ index ][ mCenterTapIndex ] ] ); + for(int y = 0; y < samples.length; y += 2) + { + insert(samples[y]); + insert(samples[y + 1]); - //quadrature - samples[ y + 1 ] = -accumulator; - } - else - { - //inphase - samples[ y ] = mBuffer[ mIndexMap[ index ][ mCenterTapIndex ] ]; + accumulator = 0.0f; - //quadrature - samples[ y + 1 ] = accumulator; - } - - mInvertFlag = !mInvertFlag; - } - - return samples; - } - - /** - * Inserts the sample into the circular buffer, overwriting the oldest value - */ - private void insert( float sample ) - { - mBuffer[ mBufferPointer++ ] = sample; + int index = mBufferPointer / 2; - mBufferPointer = mBufferPointer % mBufferSize; - } - - /** - * Creates an index map to support efficient lookup of sample indexes from - * the circular buffer. - * - * The first index of the map corresponds to the current buffer pointer - * setting. - * - * The second index is structured in sample pairs to align with - * the hilbert filter coefficients. For example, the first iteration of - * the filter would use filter coefficient 0 and the index map values 0 and - * 1 would be pointing to samples 46 and 0. - * - * The final element of each index set is the index of the center sample. - */ - private void generateIndexMap( int size ) - { - mMapHeight = size / 2 + 1; - int mapWidth = mMapHeight + 1; - - mIndexMap = new int[ mMapHeight ][ mapWidth ]; + for(int x = 0; x < mHilbertFilter.length / 2; x += 2) + { + accumulator += mHilbertFilter[x] * + (mBuffer[mIndexMap[index][x + 1]] - + mBuffer[mIndexMap[index][x]]); + } - //Setup the first row with buffer pointer index 0 as a starting point - for( int x = 0; x < mapWidth - 1; x += 2 ) - { - mIndexMap[ 0 ][ x ] = size - 1 - x; - mIndexMap[ 0 ][ x + 1 ] = x; - } - - //Place the center index at the end of the array - mCenterTapIndex = mapWidth - 1; - - mIndexMap[ 0 ][ mCenterTapIndex ] = size / 2; + //Perform FS/2 frequency translation on the final filtered values + if(mInvertFlag) + { + //inphase + samples[y] = -(mBuffer[mIndexMap[index][mCenterTapIndex]]); - //For each subsequent row, increment the previous row's value by 2, - //wrapping as needed, to keep the values between 0 and size - 1 - for( int x = 1; x < mMapHeight; x++ ) - { - for( int y = 0; y < mapWidth; y++ ) - { - mIndexMap[ x ][ y ] = mIndexMap[ x - 1 ][ y ] + 2; - - if( mIndexMap[ x ][ y ] >= size ) - { - mIndexMap[ x ][ y ] -= size + 1; + //quadrature + samples[y + 1] = -accumulator; + } + else + { + //inphase + samples[y] = mBuffer[mIndexMap[index][mCenterTapIndex]]; - //Special handling for center tap wrap around - if( y == mCenterTapIndex && mIndexMap[ x ][ y ] < 0 ) - { - mIndexMap[ x ][ y ] = size; - } - } - } - } - } + //quadrature + samples[y + 1] = accumulator; + } - /** - * Logs the index map contents - */ - public void logIndexMap() - { - for( int[] indexSet: mIndexMap ) - { - mLog.debug( "Row:" + Arrays.toString( indexSet ) ); - } - } + mInvertFlag = !mInvertFlag; + } - /** - * Converts the half-band filter coefficients for use as hilbert transform - * filter coefficients. Sets all even-numbered coefficients left of center - * to negative and all even-numbered coefficients right of center to positive - * which applies a FS/4 frequency translation to the coefficients. - * - * Note: even though the coefficients above the center coefficient are being - * defined, they are unused during the convolution process since we're using - * a folded FIR structure to exploit the symmetric nature of the half-band - * filter coefficients. The polarity difference between the upper and lower - * halves is accounted for in the filter() method. - * - * Note: we apply a 2.0 gain to the coefficients to compensate for the loss - * of splitting the signal via two filters. - */ - private void convertHalfBandToHilbert( float[] coefficients ) - { - mHilbertFilter = new float[ coefficients.length ]; - - int middle = coefficients.length / 2; - - for( int x = 0; x < coefficients.length; x++ ) - { - if( x < middle ) - { - mHilbertFilter[ x ] = 2.0f * -Math.abs( coefficients[ x ] ); - } - else if( x > middle ) - { - mHilbertFilter[ x ] = 2.0f * Math.abs( coefficients[ x ] ); - } - else - { - mHilbertFilter[ x ] = 2.0f * coefficients[ x ]; - } - } - } + return samples; + } + + /** + * Inserts the sample into the circular buffer, overwriting the oldest value + */ + private void insert(float sample) + { + mBuffer[mBufferPointer++] = sample; + + mBufferPointer = mBufferPointer % mBufferSize; + } + + /** + * Creates an index map to support efficient lookup of sample indexes from + * the circular buffer. + * + * The first index of the map corresponds to the current buffer pointer + * setting. + * + * The second index is structured in sample pairs to align with + * the hilbert filter coefficients. For example, the first iteration of + * the filter would use filter coefficient 0 and the index map values 0 and + * 1 would be pointing to samples 46 and 0. + * + * The final element of each index set is the index of the center sample. + */ + private void generateIndexMap(int size) + { + mMapHeight = size / 2 + 1; + int mapWidth = mMapHeight + 1; + + mIndexMap = new int[mMapHeight][mapWidth]; + + //Setup the first row with buffer pointer index 0 as a starting point + for(int x = 0; x < mapWidth - 1; x += 2) + { + mIndexMap[0][x] = size - 1 - x; + mIndexMap[0][x + 1] = x; + } + + //Place the center index at the end of the array + mCenterTapIndex = mapWidth - 1; + + mIndexMap[0][mCenterTapIndex] = size / 2; + + //For each subsequent row, increment the previous row's value by 2, + //wrapping as needed, to keep the values between 0 and size - 1 + for(int x = 1; x < mMapHeight; x++) + { + for(int y = 0; y < mapWidth; y++) + { + mIndexMap[x][y] = mIndexMap[x - 1][y] + 2; + + if(mIndexMap[x][y] >= size) + { + mIndexMap[x][y] -= size + 1; + + //Special handling for center tap wrap around + if(y == mCenterTapIndex && mIndexMap[x][y] < 0) + { + mIndexMap[x][y] = size; + } + } + } + } + } + + /** + * Logs the index map contents + */ + public void logIndexMap() + { + for(int[] indexSet : mIndexMap) + { + mLog.debug("Row:" + Arrays.toString(indexSet)); + } + } + + /** + * Converts the half-band filter coefficients for use as hilbert transform + * filter coefficients. Sets all even-numbered coefficients left of center + * to negative and all even-numbered coefficients right of center to positive + * which applies a FS/4 frequency translation to the coefficients. + * + * Note: even though the coefficients above the center coefficient are being + * defined, they are unused during the convolution process since we're using + * a folded FIR structure to exploit the symmetric nature of the half-band + * filter coefficients. The polarity difference between the upper and lower + * halves is accounted for in the filter() method. + * + * Note: we apply a 2.0 gain to the coefficients to compensate for the loss + * of splitting the signal via two filters. + */ + private void convertHalfBandToHilbert(float[] coefficients) + { + mHilbertFilter = new float[coefficients.length]; + + int middle = coefficients.length / 2; + + for(int x = 0; x < coefficients.length; x++) + { + if(x < middle) + { + mHilbertFilter[x] = 2.0f * -Math.abs(coefficients[x]); + } + else if(x > middle) + { + mHilbertFilter[x] = 2.0f * Math.abs(coefficients[x]); + } + else + { + mHilbertFilter[x] = 2.0f * coefficients[x]; + } + } + } } diff --git a/src/main/java/io/github/dsheirer/dsp/filter/hilbert/InterpolatingHilbertTransform.java b/src/main/java/io/github/dsheirer/dsp/filter/hilbert/InterpolatingHilbertTransform.java deleted file mode 100644 index 75f7b9f2..00000000 --- a/src/main/java/io/github/dsheirer/dsp/filter/hilbert/InterpolatingHilbertTransform.java +++ /dev/null @@ -1,280 +0,0 @@ -package io.github.dsheirer.dsp.filter.hilbert; - -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2015 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 - * - * ---------------------------------------------------------------------------- - * Half-band filter coefficients retrieved October 2015 from: - * https://github.com/airspy/host/libairspy/src/filters.h - * - * Copyright (C) 2014, Youssef Touil - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - ******************************************************************************/ - -import io.github.dsheirer.sample.Listener; -import io.github.dsheirer.sample.complex.ComplexBuffer; -import io.github.dsheirer.sample.real.RealBuffer; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.Arrays; - -public class InterpolatingHilbertTransform implements Listener -{ - private static final Logger mLog = LoggerFactory.getLogger( InterpolatingHilbertTransform.class ); - - private static final float[] HALF_BAND_FILTER = - { - -0.000998606272947510f, 0.0f, 0.001695637278417295f, 0.0f, - -0.003054430179754289f, 0.0f, 0.005055504379767936f, 0.0f, - -0.007901319195893647f, 0.0f, 0.011873357051047719f, 0.0f, - -0.017411159379930066f, 0.0f, 0.025304817427568772f, 0.0f, - -0.037225225204559217f, 0.0f, 0.057533286997004301f, 0.0f, - -0.102327462004259350f, 0.0f, 0.317034472508947400f, 0.5f, - 0.317034472508947400f, 0.0f, -0.102327462004259350f, 0.0f, - 0.057533286997004301f, 0.0f, -0.037225225204559217f, 0.0f, - 0.025304817427568772f, 0.0f, -0.017411159379930066f, 0.0f, - 0.011873357051047719f, 0.0f, -0.007901319195893647f, 0.0f, - 0.005055504379767936f, 0.0f, -0.003054430179754289f, 0.0f, - 0.001695637278417295f, 0.0f, -0.000998606272947510f - }; - - private static final float GAIN = 2.0f; - - private Listener mListener; - - private float[] mBuffer; - private int mBufferSize; - private int mBufferPointer; - private int mGroupDelayedInphaseTapPointIndex; - - private float[] mCoefficients; - private int[][] mIndexMap; - - - /** - * Hilbert transform filter used for converting real-valued samples into - * complex valued samples using frequency translation (FS/4) and a half-band - * filter. - * - * This filter uses a circular sample buffer and a pre-calculated - * index map to correctly map the filter coefficients to the circular delay - * buffer contents as each new sample is added to the buffer and processed. - * - * Half-band filter coefficients used in this filter must be of length N - * where (N + 1) is a multiple of 4. This filter uses a pre-defined half - * band filter with 47 coefficients. - * - * This transform process is described in Understanding Digital Signal - * Processing, Lyons, 3e, 2011, sections 13.1.2 and 13.1.3 (p 674-678) and - * implemented as described in Section 13.37.1 and 13.37.2 (p 802-804) - */ - public InterpolatingHilbertTransform() - { - mBufferSize = HALF_BAND_FILTER.length; - mBuffer = new float[ mBufferSize ]; - - createHilbertCoefficients(); - - generateIndexMap( mBufferSize ); - } - - @Override - public void receive( RealBuffer buffer ) - { - if( mListener != null ) - { - mListener.receive( filter( buffer ) ); - } - } - - /** - * Sets the listener to receive filtered complex buffers. - */ - public void setListener( Listener listener ) - { - mListener = listener; - } - - /** - * Applies Hilbert transform to the real sample buffer and returns a - * complex sample buffer - */ - public ComplexBuffer filter( RealBuffer buffer ) - { - return new ComplexBuffer( filter( buffer.getSamples() ) ); - } - - /** - * Applies a hilbert transform to an array of real samples and returns an - * array twice as long containing I,Q,I,Q ... complex samples - */ - public float[] filter( float[] samples ) - { - float[] filtered = new float[ samples.length * 2 ]; - - float accumulator; - - for( int y = 0; y < samples.length; y++ ) - { - insert( samples[ y ] ); - - accumulator = 0.0f; - - for( int x = 0; x < mCoefficients.length; x++ ) - { - accumulator += mCoefficients[ x ] * - mBuffer[ mIndexMap[ mBufferPointer ][ x ] ] ; - } - - int index = y * 2; - - /* We use the buffered sample at the half group delay point as the - * Inphase value and the accumulator as the quadrature value */ - filtered[ index ] = mBuffer[ mIndexMap[ mBufferPointer ] - [ mGroupDelayedInphaseTapPointIndex ] ]; - - filtered[ index + 1 ] = accumulator; - } - - return filtered; - } - - /** - * Inserts the sample into the circular buffer, overwriting the oldest value - */ - private void insert( float sample ) - { - mBuffer[ mBufferPointer ] = sample; - - mBufferPointer++; - - mBufferPointer = mBufferPointer % mBufferSize; - } - - - /** - * Creates an N x (N + 1 / 2) index map enabling quick access to the - * circular buffer samples to support convolution. - * - * As the buffer pointer shifts right, as each sample is added, we use the - * index map pointed to by the buffer pointer, so that the filter coefficients - * are pointing to the correct circular buffer samples. - * - * We add an extra index to each index map with a value that points to the - * current index of the middle sample in the buffer, so that we can retrieve - * that value as the half-group delayed sample that forms the inphase - * component. - */ - private void generateIndexMap( int size ) - { - int mapWidth = ( ( size + 1 ) / 2 ) + 1; - - int center = ( size - 1 ) / 2; - - //Set center tap index for coefficients array - mGroupDelayedInphaseTapPointIndex = center + 1; - - mIndexMap = new int[ size ][ mapWidth ]; - - //Setup the first row with buffer pointer index 0 as a starting point - for( int x = 0; x < mapWidth - 1; x++ ) - { - mIndexMap[ 0 ][ x ] = size - 1 - ( x * 2 ); - } - - //Place the group delayed in-phase sample tap point in last element of - //the first row - mIndexMap[ 0 ][ mGroupDelayedInphaseTapPointIndex ] = center; - - //For each subsequent row, increment the previous row's value by 1, - //subtracting size as needed, to keep the values between 0 and size - 1 - for( int x = 1; x < size; x++ ) - { - for( int y = 0; y < mapWidth; y++ ) - { - mIndexMap[ x ][ y ] = mIndexMap[ x - 1 ][ y ] + 1; - - if( mIndexMap[ x ][ y ] >= size ) - { - mIndexMap[ x ][ y ] -= size; - } - } - } - } - - public void logIndexMap() - { - for( int[] indexSet: mIndexMap ) - { - mLog.debug( "Row:" + Arrays.toString( indexSet ) ); - } - } - - /** - * Converts the half-band filter coefficients into a packed set of hilbert - * coefficients by removing all of the odd index, zero-valued coefficients - * and the center 0.5 coefficient. Then, all coefficients left of the - * original center are made negative and all coefficients right of the - * original center are made positive. These coefficient sign modifications - * effectively incorporate a frequency translation by FS/4 into the hilbert - * coefficients. A gain of 2.0 is applied to the coefficients to offset the - * gain of 0.5 induced by the filter. - */ - private void createHilbertCoefficients() - { - int half = ( HALF_BAND_FILTER.length + 1 ) / 2; - int middle = half / 2; - - mCoefficients = new float[ half ]; - - /* Pack all of the even numbered HB coefficients into our coefficient - * array and pre-apply a gain of two to offset the loss in the filter */ - for( int x = 0; x < half; x++ ) - { - mCoefficients[ x ] = HALF_BAND_FILTER[ x * 2 ] * GAIN; - - /* Make all of the left of center coefficients negative */ - if( x < middle && mCoefficients[ x ] > 0.0f ) - { - mCoefficients[ x ] = -mCoefficients[ x ]; - } - /* Make all of the right of center coefficients positive */ - else if( x >= middle && mCoefficients[ x ] < 0.0f ) - { - mCoefficients[ x ] = -mCoefficients[ x ]; - } - } - } -} diff --git a/src/main/java/io/github/dsheirer/dsp/filter/iir/DeemphasisFilter.java b/src/main/java/io/github/dsheirer/dsp/filter/iir/DeemphasisFilter.java index ea14ac02..601f4a38 100644 --- a/src/main/java/io/github/dsheirer/dsp/filter/iir/DeemphasisFilter.java +++ b/src/main/java/io/github/dsheirer/dsp/filter/iir/DeemphasisFilter.java @@ -1,7 +1,20 @@ +/******************************************************************************* + * sdr-trunk + * Copyright (C) 2014-2018 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 io.github.dsheirer.dsp.filter.iir; -import io.github.dsheirer.sample.real.RealBuffer; - public class DeemphasisFilter { private static final float MAX_SAMPLE_VALUE = 0.95f; @@ -45,16 +58,4 @@ public class DeemphasisFilter return samples; } - - public RealBuffer filter( RealBuffer buffer ) - { - float[] samples = buffer.getSamples(); - - for( int x = 0; x < samples.length; x++ ) - { - samples[ x ] = filter( samples[ x ] ); - } - - return buffer; - } } diff --git a/src/main/java/io/github/dsheirer/dsp/gain/ComplexFeedForwardGainControl.java b/src/main/java/io/github/dsheirer/dsp/gain/ComplexFeedForwardGainControl.java index 3cb99948..48f2bf4b 100644 --- a/src/main/java/io/github/dsheirer/dsp/gain/ComplexFeedForwardGainControl.java +++ b/src/main/java/io/github/dsheirer/dsp/gain/ComplexFeedForwardGainControl.java @@ -19,7 +19,6 @@ import io.github.dsheirer.buffer.FloatCircularBuffer; import io.github.dsheirer.sample.buffer.ReusableComplexBuffer; import io.github.dsheirer.sample.buffer.ReusableComplexBufferQueue; import io.github.dsheirer.sample.complex.Complex; -import io.github.dsheirer.sample.complex.ComplexBuffer; import io.github.dsheirer.sample.complex.ComplexSampleListener; public class ComplexFeedForwardGainControl implements ComplexSampleListener @@ -137,17 +136,6 @@ public class ComplexFeedForwardGainControl implements ComplexSampleListener return processed; } - /** - * Applies gain to the complex sample buffer - * @param buffer to apply gain - * @return buffer with gain applied samples - */ - public ComplexBuffer filter(ComplexBuffer buffer) - { - float[] filtered = filter(buffer.getSamples()); - return new ComplexBuffer(filtered); - } - /** * Applies gain to the complex sample buffer * @param buffer to apply gain diff --git a/src/main/java/io/github/dsheirer/dsp/gain/NonClippingGain.java b/src/main/java/io/github/dsheirer/dsp/gain/NonClippingGain.java index ebfab2df..612e284f 100644 --- a/src/main/java/io/github/dsheirer/dsp/gain/NonClippingGain.java +++ b/src/main/java/io/github/dsheirer/dsp/gain/NonClippingGain.java @@ -1,7 +1,5 @@ package io.github.dsheirer.dsp.gain; -import io.github.dsheirer.sample.real.RealBuffer; - public class NonClippingGain { private float mGain; @@ -50,16 +48,4 @@ public class NonClippingGain return samples; } - - /** - * Applies gain to the real buffer - */ - public RealBuffer apply( RealBuffer buffer ) - { - float[] samples = buffer.getSamples(); - - apply( samples ); - - return buffer; - } } diff --git a/src/main/java/io/github/dsheirer/gui/channelizer/SynthesizerViewer.java b/src/main/java/io/github/dsheirer/gui/channelizer/SynthesizerViewer.java index 14e9aedf..5875990c 100644 --- a/src/main/java/io/github/dsheirer/gui/channelizer/SynthesizerViewer.java +++ b/src/main/java/io/github/dsheirer/gui/channelizer/SynthesizerViewer.java @@ -21,11 +21,11 @@ import io.github.dsheirer.dsp.filter.design.FilterDesignException; import io.github.dsheirer.dsp.mixer.FS4DownConverter; import io.github.dsheirer.dsp.mixer.IOscillator; import io.github.dsheirer.dsp.mixer.LowPhaseNoiseOscillator; +import io.github.dsheirer.sample.IOverflowListener; import io.github.dsheirer.sample.Listener; import io.github.dsheirer.sample.SampleType; import io.github.dsheirer.sample.buffer.ReusableComplexBuffer; import io.github.dsheirer.sample.buffer.ReusableComplexBufferQueue; -import io.github.dsheirer.sample.real.IOverflowListener; import io.github.dsheirer.settings.SettingsManager; import io.github.dsheirer.source.SourceEvent; import io.github.dsheirer.source.tuner.configuration.TunerConfigurationModel; diff --git a/src/main/java/io/github/dsheirer/module/ProcessingChain.java b/src/main/java/io/github/dsheirer/module/ProcessingChain.java index 11de7e21..c517a9b7 100644 --- a/src/main/java/io/github/dsheirer/module/ProcessingChain.java +++ b/src/main/java/io/github/dsheirer/module/ProcessingChain.java @@ -54,11 +54,6 @@ import io.github.dsheirer.sample.buffer.ReusableAudioPacket; import io.github.dsheirer.sample.buffer.ReusableBuffer; import io.github.dsheirer.sample.buffer.ReusableBufferBroadcaster; import io.github.dsheirer.sample.buffer.ReusableComplexBuffer; -import io.github.dsheirer.sample.real.IFilteredRealBufferListener; -import io.github.dsheirer.sample.real.IFilteredRealBufferProvider; -import io.github.dsheirer.sample.real.IUnFilteredRealBufferListener; -import io.github.dsheirer.sample.real.IUnFilteredRealBufferProvider; -import io.github.dsheirer.sample.real.RealBuffer; import io.github.dsheirer.source.ComplexSource; import io.github.dsheirer.source.ISourceEventListener; import io.github.dsheirer.source.ISourceEventProvider; @@ -97,18 +92,16 @@ public class ProcessingChain implements IChannelEventListener { private final static Logger mLog = LoggerFactory.getLogger(ProcessingChain.class); - private Broadcaster mAttributeChangeRequestBroadcaster = new Broadcaster<>(); private ReusableBufferBroadcaster mAudioPacketBroadcaster = new ReusableBufferBroadcaster<>(); + private ReusableBufferBroadcaster mDemodulatedAudioBufferBroadcaster = new ReusableBufferBroadcaster(); + private ReusableBufferBroadcaster mBasebandComplexBufferBroadcaster = new ReusableBufferBroadcaster(); + private Broadcaster mAttributeChangeRequestBroadcaster = new Broadcaster<>(); private Broadcaster mCallEventBroadcaster = new Broadcaster<>(); private Broadcaster mChannelEventBroadcaster = new Broadcaster<>(); - private ReusableBufferBroadcaster mComplexBufferBroadcaster = new ReusableBufferBroadcaster(); - private ReusableBufferBroadcaster mDemodulatedAudioBufferBroadcaster = new ReusableBufferBroadcaster(); private Broadcaster mDecoderStateEventBroadcaster = new Broadcaster<>(); private Broadcaster mHeartbeatBroadcaster = new Broadcaster<>(); private Broadcaster mSourceEventBroadcaster = new Broadcaster<>(); private Broadcaster mMessageBroadcaster = new Broadcaster<>(); - private Broadcaster mFilteredRealBufferBroadcaster = new Broadcaster<>(); - private Broadcaster mUnFilteredRealBufferBroadcaster = new Broadcaster<>(); private Broadcaster mSquelchStateBroadcaster = new Broadcaster<>(); private AtomicBoolean mRunning = new AtomicBoolean(); @@ -169,9 +162,8 @@ public class ProcessingChain implements IChannelEventListener mAudioPacketBroadcaster.dispose(); mCallEventBroadcaster.dispose(); mChannelEventBroadcaster.dispose(); - mComplexBufferBroadcaster.dispose(); + mBasebandComplexBufferBroadcaster.dispose(); mMessageBroadcaster.dispose(); - mFilteredRealBufferBroadcaster.dispose(); mSquelchStateBroadcaster.dispose(); } @@ -316,11 +308,6 @@ public class ProcessingChain implements IChannelEventListener mDecoderStateEventBroadcaster.addListener(((IDecoderStateEventListener) module).getDecoderStateListener()); } - if(module instanceof IFilteredRealBufferListener) - { - mFilteredRealBufferBroadcaster.addListener(((IFilteredRealBufferListener) module).getFilteredRealBufferListener()); - } - if(module instanceof IHeartbeatListener) { mHeartbeatBroadcaster.addListener(((IHeartbeatListener)module).getHeartbeatListener()); @@ -338,7 +325,7 @@ public class ProcessingChain implements IChannelEventListener if(module instanceof IReusableComplexBufferListener) { - mComplexBufferBroadcaster.addListener(((IReusableComplexBufferListener)module).getReusableComplexBufferListener()); + mBasebandComplexBufferBroadcaster.addListener(((IReusableComplexBufferListener)module).getReusableComplexBufferListener()); } if(module instanceof ISourceEventListener) @@ -355,11 +342,6 @@ public class ProcessingChain implements IChannelEventListener { mSquelchStateBroadcaster.addListener(((ISquelchStateListener) module).getSquelchStateListener()); } - - if(module instanceof IUnFilteredRealBufferListener) - { - mUnFilteredRealBufferBroadcaster.addListener(((IUnFilteredRealBufferListener) module).getUnFilteredRealBufferListener()); - } } /** @@ -393,11 +375,6 @@ public class ProcessingChain implements IChannelEventListener mDecoderStateEventBroadcaster.removeListener(((IDecoderStateEventListener) module).getDecoderStateListener()); } - if(module instanceof IFilteredRealBufferListener) - { - mFilteredRealBufferBroadcaster.removeListener(((IFilteredRealBufferListener) module).getFilteredRealBufferListener()); - } - if(module instanceof IHeartbeatListener) { mHeartbeatBroadcaster.removeListener(((IHeartbeatListener)module).getHeartbeatListener()); @@ -410,7 +387,7 @@ public class ProcessingChain implements IChannelEventListener if(module instanceof IReusableComplexBufferListener) { - mComplexBufferBroadcaster.removeListener(((IReusableComplexBufferListener) module).getReusableComplexBufferListener()); + mBasebandComplexBufferBroadcaster.removeListener(((IReusableComplexBufferListener) module).getReusableComplexBufferListener()); } if(module instanceof ISourceEventListener) @@ -427,11 +404,6 @@ public class ProcessingChain implements IChannelEventListener { mSquelchStateBroadcaster.removeListener(((ISquelchStateListener) module).getSquelchStateListener()); } - - if(module instanceof IUnFilteredRealBufferListener) - { - mUnFilteredRealBufferBroadcaster.removeListener(((IUnFilteredRealBufferListener) module).getUnFilteredRealBufferListener()); - } } /** @@ -475,11 +447,6 @@ public class ProcessingChain implements IChannelEventListener ((IMessageProvider) module).setMessageListener(mMessageBroadcaster); } - if(module instanceof IFilteredRealBufferProvider) - { - ((IFilteredRealBufferProvider) module).setFilteredRealBufferListener(mFilteredRealBufferBroadcaster); - } - if(module instanceof IMessageProvider) { ((IMessageProvider) module).setMessageListener(mMessageBroadcaster); @@ -499,11 +466,6 @@ public class ProcessingChain implements IChannelEventListener { ((ISquelchStateProvider) module).setSquelchStateListener(mSquelchStateBroadcaster); } - - if(module instanceof IUnFilteredRealBufferProvider) - { - ((IUnFilteredRealBufferProvider) module).setUnFilteredRealBufferListener(mUnFilteredRealBufferBroadcaster); - } } /** @@ -547,11 +509,6 @@ public class ProcessingChain implements IChannelEventListener ((IMessageProvider) module).setMessageListener(null); } - if(module instanceof IFilteredRealBufferProvider) - { - ((IFilteredRealBufferProvider) module).setFilteredRealBufferListener(null); - } - if(module instanceof IMessageProvider) { ((IMessageProvider) module).setMessageListener(null); @@ -571,11 +528,6 @@ public class ProcessingChain implements IChannelEventListener { ((ISquelchStateProvider) module).setSquelchStateListener(null); } - - if(module instanceof IUnFilteredRealBufferProvider) - { - ((IUnFilteredRealBufferProvider) module).setUnFilteredRealBufferListener(null); - } } /** @@ -609,7 +561,7 @@ public class ProcessingChain implements IChannelEventListener switch(mSource.getSampleType()) { case COMPLEX: - ((ComplexSource) mSource).setListener(mComplexBufferBroadcaster); + ((ComplexSource) mSource).setListener(mBasebandComplexBufferBroadcaster); break; case REAL: ((RealSource) mSource).setListener(mDemodulatedAudioBufferBroadcaster); @@ -657,7 +609,7 @@ public class ProcessingChain implements IChannelEventListener switch(mSource.getSampleType()) { case COMPLEX: - ((ComplexSource) mSource).removeListener(mComplexBufferBroadcaster); + ((ComplexSource) mSource).removeListener(mBasebandComplexBufferBroadcaster); break; case REAL: ((RealSource) mSource).removeListener(mDemodulatedAudioBufferBroadcaster); @@ -796,6 +748,9 @@ public class ProcessingChain implements IChannelEventListener } } + /** + * Removes the listener from receiving decoded messages from all decoders. + */ public void removeMessageListener(Listener listener) { mMessageBroadcaster.removeListener(listener); @@ -832,14 +787,20 @@ public class ProcessingChain implements IChannelEventListener mSquelchStateBroadcaster.removeListener(listener); } - public void addRealBufferListener(Listener listener) + /** + * Adds listener to receive demodulated audio buffers from an modules that produce demodulated audio. + */ + public void addDemodulatedAudioListener(Listener listener) { - mFilteredRealBufferBroadcaster.addListener(listener); + mDemodulatedAudioBufferBroadcaster.addListener(listener); } - public void removeRealBufferListener(Listener listener) + /** + * Removes the listener from receiving demodulated audio buffers. + */ + public void removeDemodulatedAudioListener(Listener listener) { - mFilteredRealBufferBroadcaster.removeListener(listener); + mDemodulatedAudioBufferBroadcaster.removeListener(listener); } @Override diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/C4FMSymbolFilter.java b/src/main/java/io/github/dsheirer/module/decode/p25/C4FMSymbolFilter.java index 5f3d4215..3bf17098 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/C4FMSymbolFilter.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/C4FMSymbolFilter.java @@ -2,13 +2,13 @@ package io.github.dsheirer.module.decode.p25; import io.github.dsheirer.dsp.gain.DirectGainControl; import io.github.dsheirer.sample.Listener; -import io.github.dsheirer.sample.real.RealBuffer; +import io.github.dsheirer.sample.buffer.ReusableBuffer; import io.github.dsheirer.sample.real.RealSampleListener; import io.github.dsheirer.sample.real.RealSampleProvider; import io.github.dsheirer.source.ISourceEventListener; import io.github.dsheirer.source.SourceEvent; -public class C4FMSymbolFilter implements Listener, ISourceEventListener, RealSampleProvider +public class C4FMSymbolFilter implements Listener, ISourceEventListener, RealSampleProvider { private static final float TAPS[][] = { @@ -215,7 +215,7 @@ public class C4FMSymbolFilter implements Listener, ISourceEventListe } @Override - public void receive(RealBuffer buffer) + public void receive(ReusableBuffer buffer) { for(float sample : buffer.getSamples()) { diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/audio/P25AudioModule.java b/src/main/java/io/github/dsheirer/module/decode/p25/audio/P25AudioModule.java index de5766dd..8c3851ac 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/audio/P25AudioModule.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/audio/P25AudioModule.java @@ -1,3 +1,18 @@ +/******************************************************************************* + * sdr-trunk + * Copyright (C) 2014-2018 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 io.github.dsheirer.module.decode.p25.audio; import io.github.dsheirer.audio.AudioFormats; diff --git a/src/main/java/io/github/dsheirer/record/RecorderManager.java b/src/main/java/io/github/dsheirer/record/RecorderManager.java index c3be7605..d6351867 100644 --- a/src/main/java/io/github/dsheirer/record/RecorderManager.java +++ b/src/main/java/io/github/dsheirer/record/RecorderManager.java @@ -20,10 +20,10 @@ import io.github.dsheirer.channel.metadata.Metadata; import io.github.dsheirer.properties.SystemProperties; import io.github.dsheirer.record.wave.AudioPacketWaveRecorder; import io.github.dsheirer.record.wave.ComplexBufferWaveRecorder; +import io.github.dsheirer.sample.IOverflowListener; import io.github.dsheirer.sample.Listener; import io.github.dsheirer.sample.OverflowableTransferQueue; import io.github.dsheirer.sample.buffer.ReusableAudioPacket; -import io.github.dsheirer.sample.real.IOverflowListener; import io.github.dsheirer.util.ThreadPool; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/src/main/java/io/github/dsheirer/sample/ConversionUtils.java b/src/main/java/io/github/dsheirer/sample/ConversionUtils.java index 9cc988ff..57b08bd8 100644 --- a/src/main/java/io/github/dsheirer/sample/ConversionUtils.java +++ b/src/main/java/io/github/dsheirer/sample/ConversionUtils.java @@ -1,7 +1,6 @@ package io.github.dsheirer.sample; import io.github.dsheirer.sample.buffer.ReusableBuffer; -import io.github.dsheirer.sample.real.RealBuffer; import java.nio.ByteBuffer; import java.nio.ByteOrder; @@ -53,24 +52,6 @@ public class ConversionUtils return converted; } - /** - * Converts the float samples in a complex buffer to a little endian 16-bit - * buffer - */ - public static ByteBuffer convertToSigned16BitSamples(Buffer buffer) - { - return convertToSigned16BitSamples(buffer.getSamples()); - } - - /** - * Converts the float samples in a complex buffer to a little endian 16-bit - * buffer - */ - public static ByteBuffer convertToSigned16BitSamples(RealBuffer buffer) - { - return convertToSigned16BitSamples(buffer.getSamples()); - } - /** * Converts the float samples in a complex buffer to a little endian 16-bit * buffer diff --git a/src/main/java/io/github/dsheirer/sample/complex/ComplexBuffer.java b/src/main/java/io/github/dsheirer/sample/IOverflowListener.java similarity index 76% rename from src/main/java/io/github/dsheirer/sample/complex/ComplexBuffer.java rename to src/main/java/io/github/dsheirer/sample/IOverflowListener.java index edc3d4ce..209bf74f 100644 --- a/src/main/java/io/github/dsheirer/sample/complex/ComplexBuffer.java +++ b/src/main/java/io/github/dsheirer/sample/IOverflowListener.java @@ -13,17 +13,13 @@ * If not, see * ******************************************************************************/ -package io.github.dsheirer.sample.complex; +package io.github.dsheirer.sample; -import io.github.dsheirer.sample.Buffer; - -public class ComplexBuffer extends Buffer +public interface IOverflowListener { /** - * Wrapper around float array containing interleaved I/Q samples + * Indicates that the source is in overflow state (true) or out of overflow state (false) + * @param overflow true to indicate an overflow state */ - public ComplexBuffer(float[] samples) - { - super(samples); - } + void sourceOverflow(boolean overflow); } diff --git a/src/main/java/io/github/dsheirer/sample/OverflowableTransferQueue.java b/src/main/java/io/github/dsheirer/sample/OverflowableTransferQueue.java index c342ac3f..b125a442 100644 --- a/src/main/java/io/github/dsheirer/sample/OverflowableTransferQueue.java +++ b/src/main/java/io/github/dsheirer/sample/OverflowableTransferQueue.java @@ -18,7 +18,6 @@ ******************************************************************************/ package io.github.dsheirer.sample; -import io.github.dsheirer.sample.real.IOverflowListener; import io.github.dsheirer.source.Source; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/src/main/java/io/github/dsheirer/sample/buffer/ReusableBufferAssembler.java b/src/main/java/io/github/dsheirer/sample/buffer/ReusableBufferAssembler.java index 111c33cb..d6e17209 100644 --- a/src/main/java/io/github/dsheirer/sample/buffer/ReusableBufferAssembler.java +++ b/src/main/java/io/github/dsheirer/sample/buffer/ReusableBufferAssembler.java @@ -15,131 +15,66 @@ ******************************************************************************/ package io.github.dsheirer.sample.buffer; -import io.github.dsheirer.dsp.filter.channelizer.SampleTimestampManager; import io.github.dsheirer.sample.Listener; -import org.apache.commons.lang3.Validate; - -import java.nio.FloatBuffer; +import io.github.dsheirer.sample.real.RealSampleListener; /** - * Assembles complex float samples into a ComplexBuffer containing an array of floats. Monitors incoming sample count - * to accurately assign a relative timestamp to each assembled buffer. + * Assembles single float samples into a ReusableBuffer containing an array of floats */ -public class ReusableBufferAssembler +public class ReusableBufferAssembler implements RealSampleListener { - private ReusableComplexBufferQueue mReusableComplexBufferQueue = new ReusableComplexBufferQueue("ReusableBufferAssembler"); - private SampleTimestampManager mTimestampManager; - private FloatBuffer mBuffer; private int mBufferSize; - private long mCurrentBufferTimestamp; - private Listener mListener; + private int mBufferPointer; + private ReusableBuffer mCurrentBuffer; + private ReusableBufferQueue mReusableBufferQueue = new ReusableBufferQueue("Real"); - public ReusableBufferAssembler(int bufferSize, double sampleRate) + private Listener mListener; + + public ReusableBufferAssembler(int bufferSize) { - Validate.isTrue(bufferSize % 2 == 0); mBufferSize = bufferSize; - mBuffer = FloatBuffer.allocate(mBufferSize); - mTimestampManager = new SampleTimestampManager(sampleRate); - } - - /** - * Updates the reference timestamp to use for all subsequent samples that are assembled. This timestamp should - * identify the relative timestamp of the first/next sample that is received by this assembler. - * @param timestamp to use relative to the incoming sample arrays. - */ - public void updateTimestamp(long timestamp) - { - mTimestampManager.setReferenceTimestamp(timestamp); - - if(mBuffer.position() == 0) - { - mCurrentBufferTimestamp = timestamp; - } } public void dispose() { - mReusableComplexBufferQueue.dispose(); mListener = null; } - /** - * Adds the samples to this assembler. As each buffer is assembled, it will be dispatched to the registered - * listener. - * - * @param samples to assemble - */ - public void receive(float[] samples) + public void reset() { - if(mBuffer.remaining() >= samples.length) - { - mBuffer.put(samples); - mTimestampManager.increment(samples.length / 2); + mCurrentBuffer.decrementUserCount(); + mCurrentBuffer = null; + mBufferPointer = 0; + } - if(!mBuffer.hasRemaining()) + @Override + public void receive(float sample) + { + if(mCurrentBuffer == null) + { + mCurrentBuffer = mReusableBufferQueue.getBuffer(mBufferSize); + mCurrentBuffer.incrementUserCount(); + mBufferPointer = 0; + } + + mCurrentBuffer.getSamples()[mBufferPointer++] = sample; + + if (mBufferPointer >= mBufferSize) + { + if (mListener != null) { - flush(); + mListener.receive(mCurrentBuffer); } - } - else - { - int offset = 0; - - while(offset < samples.length) + else { - int toCopy = mBuffer.remaining(); - - if((samples.length - offset) < toCopy) - { - toCopy = samples.length - offset; - } - - mBuffer.put(samples, offset, toCopy); - - offset += toCopy; - mTimestampManager.increment(toCopy / 2); - - if(!mBuffer.hasRemaining()) - { - flush(); - } + mCurrentBuffer.decrementUserCount(); } + + mCurrentBuffer = null; } } - /** - * Adds the samples to this assembler. As each buffer is assembled, it will be dispatched to the registered - * listener. - */ - public void receive(ReusableComplexBuffer samplesBuffer) - { - receive(samplesBuffer.getSamples()); - samplesBuffer.decrementUserCount(); - } - - /** - * Flushes the current buffer contents to the registered listener - */ - public void flush() - { - if(mListener != null && mBuffer.position() > 0) - { - mBuffer.rewind(); - ReusableComplexBuffer reusableComplexBuffer = mReusableComplexBufferQueue.getBuffer(mBufferSize); - reusableComplexBuffer.reloadFrom(mBuffer, mCurrentBufferTimestamp); - reusableComplexBuffer.incrementUserCount(); - mListener.receive(reusableComplexBuffer); - } - - mBuffer.rewind(); - mCurrentBufferTimestamp = mTimestampManager.getCurrentTimestamp(); - } - - /** - * Sets the listener to receive the output from this buffer assembler. Note: the listener is responsible for - * managing the user count for any buffers received from this buffer assembler. - */ - public void setListener(Listener listener) + public void setListener(Listener listener) { mListener = listener; } diff --git a/src/main/java/io/github/dsheirer/sample/buffer/ReusableComplexBufferAssembler.java b/src/main/java/io/github/dsheirer/sample/buffer/ReusableComplexBufferAssembler.java new file mode 100644 index 00000000..7e1fbaee --- /dev/null +++ b/src/main/java/io/github/dsheirer/sample/buffer/ReusableComplexBufferAssembler.java @@ -0,0 +1,146 @@ +/******************************************************************************* + * sdr-trunk + * Copyright (C) 2014-2018 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 io.github.dsheirer.sample.buffer; + +import io.github.dsheirer.dsp.filter.channelizer.SampleTimestampManager; +import io.github.dsheirer.sample.Listener; +import org.apache.commons.lang3.Validate; + +import java.nio.FloatBuffer; + +/** + * Assembles complex float samples into a ComplexBuffer containing an array of floats. Monitors incoming sample count + * to accurately assign a relative timestamp to each assembled buffer. + */ +public class ReusableComplexBufferAssembler +{ + private ReusableComplexBufferQueue mReusableComplexBufferQueue = new ReusableComplexBufferQueue("ReusableComplexBufferAssembler"); + private SampleTimestampManager mTimestampManager; + private FloatBuffer mBuffer; + private int mBufferSize; + private long mCurrentBufferTimestamp; + private Listener mListener; + + public ReusableComplexBufferAssembler(int bufferSize, double sampleRate) + { + Validate.isTrue(bufferSize % 2 == 0); + mBufferSize = bufferSize; + mBuffer = FloatBuffer.allocate(mBufferSize); + mTimestampManager = new SampleTimestampManager(sampleRate); + } + + /** + * Updates the reference timestamp to use for all subsequent samples that are assembled. This timestamp should + * identify the relative timestamp of the first/next sample that is received by this assembler. + * @param timestamp to use relative to the incoming sample arrays. + */ + public void updateTimestamp(long timestamp) + { + mTimestampManager.setReferenceTimestamp(timestamp); + + if(mBuffer.position() == 0) + { + mCurrentBufferTimestamp = timestamp; + } + } + + public void dispose() + { + mReusableComplexBufferQueue.dispose(); + mListener = null; + } + + /** + * Adds the samples to this assembler. As each buffer is assembled, it will be dispatched to the registered + * listener. + * + * @param samples to assemble + */ + public void receive(float[] samples) + { + if(mBuffer.remaining() >= samples.length) + { + mBuffer.put(samples); + mTimestampManager.increment(samples.length / 2); + + if(!mBuffer.hasRemaining()) + { + flush(); + } + } + else + { + int offset = 0; + + while(offset < samples.length) + { + int toCopy = mBuffer.remaining(); + + if((samples.length - offset) < toCopy) + { + toCopy = samples.length - offset; + } + + mBuffer.put(samples, offset, toCopy); + + offset += toCopy; + mTimestampManager.increment(toCopy / 2); + + if(!mBuffer.hasRemaining()) + { + flush(); + } + } + } + } + + /** + * Adds the samples to this assembler. As each buffer is assembled, it will be dispatched to the registered + * listener. + */ + public void receive(ReusableComplexBuffer samplesBuffer) + { + receive(samplesBuffer.getSamples()); + samplesBuffer.decrementUserCount(); + } + + /** + * Flushes the current buffer contents to the registered listener + */ + public void flush() + { + if(mListener != null && mBuffer.position() > 0) + { + mBuffer.rewind(); + ReusableComplexBuffer reusableComplexBuffer = mReusableComplexBufferQueue.getBuffer(mBufferSize); + reusableComplexBuffer.reloadFrom(mBuffer, mCurrentBufferTimestamp); + reusableComplexBuffer.incrementUserCount(); + mListener.receive(reusableComplexBuffer); + } + + mBuffer.rewind(); + mCurrentBufferTimestamp = mTimestampManager.getCurrentTimestamp(); + } + + /** + * Sets the listener to receive the output from this buffer assembler. Note: the listener is responsible for + * managing the user count for any buffers received from this buffer assembler. + */ + public void setListener(Listener listener) + { + mListener = listener; + } +} diff --git a/src/main/java/io/github/dsheirer/sample/complex/ComplexBufferToStreamConverter.java b/src/main/java/io/github/dsheirer/sample/complex/ComplexBufferToStreamConverter.java deleted file mode 100644 index 9a13b4da..00000000 --- a/src/main/java/io/github/dsheirer/sample/complex/ComplexBufferToStreamConverter.java +++ /dev/null @@ -1,35 +0,0 @@ -package io.github.dsheirer.sample.complex; - -import io.github.dsheirer.sample.Listener; - -/** - * Real sample buffer to stream converter - */ -public class ComplexBufferToStreamConverter implements Listener -{ - private ComplexSampleListener mListener; - - public void dispose() - { - mListener = null; - } - - @Override - public void receive( ComplexBuffer buffer ) - { - if( mListener != null ) - { - float[] samples = buffer.getSamples(); - - for( int x = 0; x < samples.length; x += 2 ) - { - mListener.receive( samples[ x ], samples[ x + 1 ] ); - } - } - } - - public void setListener( ComplexSampleListener listener ) - { - mListener = listener; - } -} diff --git a/src/main/java/io/github/dsheirer/sample/complex/ComplexToComplexBufferAssembler.java b/src/main/java/io/github/dsheirer/sample/complex/ComplexToComplexBufferAssembler.java deleted file mode 100644 index 88cdb58b..00000000 --- a/src/main/java/io/github/dsheirer/sample/complex/ComplexToComplexBufferAssembler.java +++ /dev/null @@ -1,81 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2015 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 io.github.dsheirer.sample.complex; - -import io.github.dsheirer.sample.Listener; -import org.apache.commons.lang3.Validate; - -/** - * Assembles complex float samples into a ComplexBuffer containing an array of floats - */ -public class ComplexToComplexBufferAssembler implements ComplexSampleListener -{ - private int mBufferSize; - private float[] mBuffer; - private int mBufferPointer; - - private Listener mListener; - - public ComplexToComplexBufferAssembler( int bufferSize ) - { - Validate.isTrue(bufferSize % 2 == 0); - - mBufferSize = bufferSize; - mBuffer = new float[ mBufferSize ]; - } - - public void dispose() - { - mListener = null; - mBuffer = null; - } - - public void reset() - { - mBuffer = new float[ mBufferSize ]; - mBufferPointer = 0; - } - - @Override - public void receive( float i, float q ) - { - if( mBufferPointer >= mBufferSize ) - { - if( mListener != null ) - { - mListener.receive( new ComplexBuffer( mBuffer ) ); - } - - mBuffer = new float[ mBufferSize ]; - mBufferPointer = 0; - } - - mBuffer[ mBufferPointer ] = i; - - mBufferPointer++; - - mBuffer[ mBufferPointer ] = q; - - mBufferPointer++; - } - - public void setListener( Listener listener ) - { - mListener = listener; - } -} diff --git a/src/main/java/io/github/dsheirer/sample/complex/RateCounter_CB.java b/src/main/java/io/github/dsheirer/sample/complex/RateCounter_CB.java deleted file mode 100644 index 433b6653..00000000 --- a/src/main/java/io/github/dsheirer/sample/complex/RateCounter_CB.java +++ /dev/null @@ -1,55 +0,0 @@ -package io.github.dsheirer.sample.complex; - -import io.github.dsheirer.sample.Listener; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class RateCounter_CB implements Listener -{ - private final static Logger mLog = - LoggerFactory.getLogger( RateCounter_CB.class ); - - private Listener mListener; - private long mCount; - private long mStartTime = -1; - - private String mName; - - public RateCounter_CB() - { - mName = "UNNAMED"; - } - - public RateCounter_CB( String name ) - { - mName = name; - } - - @Override - public void receive( ComplexBuffer buffer ) - { - if( mStartTime == -1 ) - { - mStartTime = System.currentTimeMillis(); - } - - mCount += ( buffer.getSamples().length / 2 ); - - long elapsed = System.currentTimeMillis() - mStartTime; - - if( elapsed != 0 ) - { - mLog.debug( "[" + mName + "] Sample Rate: " + ( mCount / elapsed ) + " kHz" ); - } - - if( mListener != null ) - { - mListener.receive( buffer ); - } - } - - public void setListener( Listener listener ) - { - mListener = listener; - } -} diff --git a/src/main/java/io/github/dsheirer/sample/real/IFilteredRealBufferListener.java b/src/main/java/io/github/dsheirer/sample/real/IFilteredRealBufferListener.java deleted file mode 100644 index 11465aea..00000000 --- a/src/main/java/io/github/dsheirer/sample/real/IFilteredRealBufferListener.java +++ /dev/null @@ -1,8 +0,0 @@ -package io.github.dsheirer.sample.real; - -import io.github.dsheirer.sample.Listener; - -public interface IFilteredRealBufferListener -{ - public Listener getFilteredRealBufferListener(); -} diff --git a/src/main/java/io/github/dsheirer/sample/real/IFilteredRealBufferProvider.java b/src/main/java/io/github/dsheirer/sample/real/IFilteredRealBufferProvider.java deleted file mode 100644 index ead47fae..00000000 --- a/src/main/java/io/github/dsheirer/sample/real/IFilteredRealBufferProvider.java +++ /dev/null @@ -1,9 +0,0 @@ -package io.github.dsheirer.sample.real; - -import io.github.dsheirer.sample.Listener; - -public interface IFilteredRealBufferProvider -{ - public void setFilteredRealBufferListener( Listener listener ); - public void removeFilteredRealBufferListener(); -} diff --git a/src/main/java/io/github/dsheirer/sample/real/IOverflowListener.java b/src/main/java/io/github/dsheirer/sample/real/IOverflowListener.java deleted file mode 100644 index 79930e64..00000000 --- a/src/main/java/io/github/dsheirer/sample/real/IOverflowListener.java +++ /dev/null @@ -1,28 +0,0 @@ -/******************************************************************************* - * 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 io.github.dsheirer.sample.real; - -public interface IOverflowListener -{ - /** - * Indicates that the source is in overflow state (true) or out of overflow state (false) - * @param overflow true to indicate an overflow state - */ - void sourceOverflow(boolean overflow); -} diff --git a/src/main/java/io/github/dsheirer/sample/real/IUnFilteredRealBufferListener.java b/src/main/java/io/github/dsheirer/sample/real/IUnFilteredRealBufferListener.java deleted file mode 100644 index 52a1e35b..00000000 --- a/src/main/java/io/github/dsheirer/sample/real/IUnFilteredRealBufferListener.java +++ /dev/null @@ -1,8 +0,0 @@ -package io.github.dsheirer.sample.real; - -import io.github.dsheirer.sample.Listener; - -public interface IUnFilteredRealBufferListener -{ - public Listener getUnFilteredRealBufferListener(); -} diff --git a/src/main/java/io/github/dsheirer/sample/real/IUnFilteredRealBufferProvider.java b/src/main/java/io/github/dsheirer/sample/real/IUnFilteredRealBufferProvider.java deleted file mode 100644 index 90100395..00000000 --- a/src/main/java/io/github/dsheirer/sample/real/IUnFilteredRealBufferProvider.java +++ /dev/null @@ -1,9 +0,0 @@ -package io.github.dsheirer.sample.real; - -import io.github.dsheirer.sample.Listener; - -public interface IUnFilteredRealBufferProvider -{ - public void setUnFilteredRealBufferListener( Listener listener ); - public void removeUnFilteredRealBufferListener(); -} diff --git a/src/main/java/io/github/dsheirer/sample/real/RealBuffer.java b/src/main/java/io/github/dsheirer/sample/real/RealBuffer.java deleted file mode 100644 index 7050e1bf..00000000 --- a/src/main/java/io/github/dsheirer/sample/real/RealBuffer.java +++ /dev/null @@ -1,41 +0,0 @@ -/******************************************************************************* - * sdr-trunk - * Copyright (C) 2014-2018 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 io.github.dsheirer.sample.real; - -import io.github.dsheirer.sample.Buffer; - -import java.util.Arrays; - -public class RealBuffer extends Buffer -{ - /** - * Wrapper around float array containing real float samples - */ - public RealBuffer(float[] samples) - { - super(samples); - } - - /** - * Creates a deep copy of the buffer and a shallow (reference) copy of the source event - */ - public RealBuffer copyOf() - { - float[] copy = Arrays.copyOf(getSamples(), getSamples().length); - - return new RealBuffer(copy); - } -} diff --git a/src/main/java/io/github/dsheirer/sample/real/RealToRealBufferAssembler.java b/src/main/java/io/github/dsheirer/sample/real/RealToRealBufferAssembler.java deleted file mode 100644 index 08e07783..00000000 --- a/src/main/java/io/github/dsheirer/sample/real/RealToRealBufferAssembler.java +++ /dev/null @@ -1,72 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2015 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 io.github.dsheirer.sample.real; - -import io.github.dsheirer.sample.Listener; - -/** - * Assembles single float samples into a RealBuffer containing an array of floats - */ -public class RealToRealBufferAssembler implements RealSampleListener -{ - private int mBufferSize; - private float[] mBuffer; - private int mBufferPointer; - - private Listener mListener; - - public RealToRealBufferAssembler(int bufferSize) - { - mBufferSize = bufferSize; - mBuffer = new float[mBufferSize]; - } - - public void dispose() - { - mListener = null; - mBuffer = null; - } - - public void reset() - { - mBuffer = new float[mBufferSize]; - mBufferPointer = 0; - } - - @Override - public void receive(float sample) - { - mBuffer[mBufferPointer++] = sample; - - if (mBufferPointer >= mBufferSize) - { - if (mListener != null) - { - mListener.receive(new RealBuffer(mBuffer)); - } - - mBuffer = new float[mBufferSize]; - mBufferPointer = 0; - } - } - - public void setListener(Listener listener) - { - mListener = listener; - } -} diff --git a/src/main/java/io/github/dsheirer/source/Source.java b/src/main/java/io/github/dsheirer/source/Source.java index 404a2cec..5e378606 100644 --- a/src/main/java/io/github/dsheirer/source/Source.java +++ b/src/main/java/io/github/dsheirer/source/Source.java @@ -18,8 +18,8 @@ package io.github.dsheirer.source; import io.github.dsheirer.module.Module; +import io.github.dsheirer.sample.IOverflowListener; import io.github.dsheirer.sample.SampleType; -import io.github.dsheirer.sample.real.IOverflowListener; import io.github.dsheirer.source.heartbeat.HeartbeatManager; import io.github.dsheirer.source.heartbeat.IHeartbeatProvider; diff --git a/src/main/java/io/github/dsheirer/source/mixer/ComplexMixer.java b/src/main/java/io/github/dsheirer/source/mixer/ComplexMixer.java index 51c68e44..4e58e197 100644 --- a/src/main/java/io/github/dsheirer/source/mixer/ComplexMixer.java +++ b/src/main/java/io/github/dsheirer/source/mixer/ComplexMixer.java @@ -38,7 +38,7 @@ public class ComplexMixer * @param targetDataLine - mixer or sound card to be used * @param format - audio format * @param name - token name to use for this mixer - * @param complexSampleAdapter - adapter to convert byte array data read from the mixer into ComplexBuffer. + * @param complexSampleAdapter - adapter to convert byte array data read from the mixer into ReusableComplexBuffer. */ public ComplexMixer(TargetDataLine targetDataLine, AudioFormat format, String name, ComplexSampleAdapter complexSampleAdapter) diff --git a/src/main/java/io/github/dsheirer/source/tuner/channel/CICTunerChannelSource.java b/src/main/java/io/github/dsheirer/source/tuner/channel/CICTunerChannelSource.java index efddbc58..ac6e5b4f 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/channel/CICTunerChannelSource.java +++ b/src/main/java/io/github/dsheirer/source/tuner/channel/CICTunerChannelSource.java @@ -20,11 +20,11 @@ import io.github.dsheirer.dsp.filter.Window; import io.github.dsheirer.dsp.filter.cic.ComplexPrimeCICDecimate; import io.github.dsheirer.dsp.mixer.IOscillator; import io.github.dsheirer.dsp.mixer.Oscillator; -import io.github.dsheirer.sample.Buffer; import io.github.dsheirer.sample.Listener; -import io.github.dsheirer.sample.OverflowableTransferQueue; +import io.github.dsheirer.sample.buffer.OverflowableReusableBufferTransferQueue; +import io.github.dsheirer.sample.buffer.ReusableComplexBuffer; +import io.github.dsheirer.sample.buffer.ReusableComplexBufferQueue; import io.github.dsheirer.sample.complex.Complex; -import io.github.dsheirer.sample.complex.ComplexBuffer; import io.github.dsheirer.source.tuner.Tuner; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -45,12 +45,12 @@ public class CICTunerChannelSource private static double CHANNEL_RATE = 48000.0; private static int CHANNEL_PASS_FREQUENCY = 12000; - private OverflowableTransferQueue mBuffer; - + private OverflowableReusableBufferTransferQueue mBuffer; + private ReusableComplexBufferQueue mReusableComplexBufferQueue = new ReusableComplexBufferQueue("CICTunerChannelSource"); private IOscillator mFrequencyCorrectionMixer; private ComplexPrimeCICDecimate mDecimationFilter; - private Listener mListener; - private List mSampleBuffers = new ArrayList(); + private Listener mListener; + private List mSampleBuffers = new ArrayList<>(); private long mTunerFrequency = 0; private double mTunerSampleRate; private long mChannelFrequencyCorrection = 0; @@ -68,7 +68,7 @@ public class CICTunerChannelSource */ public CICTunerChannelSource(Tuner tuner, TunerChannel tunerChannel) { - mBuffer = new OverflowableTransferQueue<>(BUFFER_MAX_CAPACITY, BUFFER_OVERFLOW_RESET_THRESHOLD); + mBuffer = new OverflowableReusableBufferTransferQueue<>(BUFFER_MAX_CAPACITY, BUFFER_OVERFLOW_RESET_THRESHOLD); // mBuffer.setSourceOverflowListener(this); //Setup the frequency translator to the current source frequency @@ -120,12 +120,12 @@ public class CICTunerChannelSource * Primary input method for receiving complex sample buffers from the wideband source (ie tuner) * @param buffer to receive and eventually process */ - public void receive(ComplexBuffer buffer) + public void receive(ReusableComplexBuffer buffer) { mBuffer.offer(buffer); } - public void setListener(Listener listener) + public void setListener(Listener listener) { /* Save a pointer to the listener so that if we have to change the * decimation filter, we can re-add the listener */ @@ -134,7 +134,7 @@ public class CICTunerChannelSource mDecimationFilter.setListener(listener); } - public void removeListener(Listener listener) + public void removeListener(Listener listener) { mDecimationFilter.removeListener(); } @@ -181,14 +181,13 @@ public class CICTunerChannelSource { mBuffer.drainTo(mSampleBuffers, 20); - for(Buffer buffer : mSampleBuffers) + for(ReusableComplexBuffer buffer : mSampleBuffers) { float[] samples = buffer.getSamples(); - /* We make a copy of the buffer so that we don't affect - * anyone else that is using the same buffer, like other - * channels or the spectral display */ - float[] translated = new float[samples.length]; + ReusableComplexBuffer reusableComplexBuffer = mReusableComplexBufferQueue.getBuffer(samples.length); + reusableComplexBuffer.incrementUserCount(); + float[] translated = reusableComplexBuffer.getSamples(); /* Perform frequency translation */ for(int x = 0; x < samples.length; x += 2) @@ -206,8 +205,10 @@ public class CICTunerChannelSource if(filter != null) { - filter.receive(new ComplexBuffer(translated)); + filter.receive(reusableComplexBuffer); } + + buffer.decrementUserCount(); } mSampleBuffers.clear(); diff --git a/src/main/java/io/github/dsheirer/spectrum/ChannelSpectrumPanel.java b/src/main/java/io/github/dsheirer/spectrum/ChannelSpectrumPanel.java index fb565cf1..6d91aa7a 100644 --- a/src/main/java/io/github/dsheirer/spectrum/ChannelSpectrumPanel.java +++ b/src/main/java/io/github/dsheirer/spectrum/ChannelSpectrumPanel.java @@ -21,12 +21,13 @@ import io.github.dsheirer.controller.channel.ChannelEventListener; import io.github.dsheirer.controller.channel.ChannelProcessingManager; import io.github.dsheirer.dsp.filter.Filters; import io.github.dsheirer.dsp.filter.Window.WindowType; -import io.github.dsheirer.dsp.filter.halfband.real.HalfBandFilter_RB_RB; +import io.github.dsheirer.dsp.filter.halfband.real.HalfBandFilter; import io.github.dsheirer.dsp.filter.smoothing.SmoothingFilter.SmoothingType; import io.github.dsheirer.module.ProcessingChain; import io.github.dsheirer.sample.Listener; import io.github.dsheirer.sample.SampleType; import io.github.dsheirer.sample.buffer.ReusableBuffer; +import io.github.dsheirer.sample.buffer.ReusableComplexBuffer; import io.github.dsheirer.sample.buffer.ReusableComplexBufferQueue; import io.github.dsheirer.settings.ColorSetting.ColorSettingName; import io.github.dsheirer.settings.ColorSettingMenuItem; @@ -74,7 +75,7 @@ public class ChannelSpectrumPanel extends JPanel implements ChannelEventListener private int mSampleBufferSize = 2400; - private HalfBandFilter_RB_RB mDecimatingFilter = new HalfBandFilter_RB_RB( + private HalfBandFilter mDecimatingFilter = new HalfBandFilter( Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO.getCoefficients(), 1.0f, true); private AtomicBoolean mEnabled = new AtomicBoolean(); @@ -215,8 +216,7 @@ public class ChannelSpectrumPanel extends JPanel implements ChannelEventListener if(processingChain != null) { -// processingChain.addRealBufferListener(this); - + processingChain.addDemodulatedAudioListener(this); mDFTProcessor.start(); } } @@ -226,12 +226,11 @@ public class ChannelSpectrumPanel extends JPanel implements ChannelEventListener { if(mCurrentChannel != null && mCurrentChannel.isProcessing()) { - ProcessingChain processingChain = mChannelProcessingManager - .getProcessingChain(mCurrentChannel); + ProcessingChain processingChain = mChannelProcessingManager.getProcessingChain(mCurrentChannel); if(processingChain != null) { -// processingChain.removeRealBufferListener(this); + processingChain.removeDemodulatedAudioListener(this); } } @@ -270,15 +269,14 @@ public class ChannelSpectrumPanel extends JPanel implements ChannelEventListener @Override public void receive(ReusableBuffer buffer) { - throw new IllegalStateException("This must be updated to work with reusable buffers"); -// RealBuffer decimated = mDecimatingFilter.filter(buffer); -// -// //Hack: we're placing real samples in a complex buffer that the DFT -// //processor is expecting. -// ReusableComplexBuffer reusableComplexBuffer = mReusableComplexBufferQueue.getBuffer(decimated.getSamples().length); -// reusableComplexBuffer.reloadFrom(decimated.getSamples(), System.currentTimeMillis()); -// reusableComplexBuffer.incrementUserCount(); -// mDFTProcessor.receive(reusableComplexBuffer); + ReusableBuffer decimated = mDecimatingFilter.filter(buffer); + + //Hack: we're placing real samples in a complex buffer that the DFT + //processor is expecting. + ReusableComplexBuffer reusableComplexBuffer = mReusableComplexBufferQueue.getBuffer(decimated.getSamples().length); + reusableComplexBuffer.reloadFrom(decimated.getSamples(), System.currentTimeMillis()); + reusableComplexBuffer.incrementUserCount(); + mDFTProcessor.receive(reusableComplexBuffer); } /** diff --git a/src/main/java/io/github/dsheirer/spectrum/DFTProcessor.java b/src/main/java/io/github/dsheirer/spectrum/DFTProcessor.java index 9c0f11c6..83debd0c 100644 --- a/src/main/java/io/github/dsheirer/spectrum/DFTProcessor.java +++ b/src/main/java/io/github/dsheirer/spectrum/DFTProcessor.java @@ -18,10 +18,10 @@ package io.github.dsheirer.spectrum; import io.github.dsheirer.dsp.filter.Window; import io.github.dsheirer.dsp.filter.Window.WindowType; import io.github.dsheirer.properties.SystemProperties; +import io.github.dsheirer.sample.IOverflowListener; import io.github.dsheirer.sample.Listener; import io.github.dsheirer.sample.SampleType; import io.github.dsheirer.sample.buffer.ReusableComplexBuffer; -import io.github.dsheirer.sample.real.IOverflowListener; import io.github.dsheirer.source.ISourceEventProcessor; import io.github.dsheirer.source.SourceEvent; import io.github.dsheirer.spectrum.converter.DFTResultsConverter;