/******************************************************************************* * sdrtrunk * Copyright (C) 2014-2016 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/> ******************************************************************************/ package dsp.filter.polyphase; import dsp.filter.design.FilterDesignException; import dsp.filter.fir.FIRFilter; import dsp.filter.fir.FIRFilterSpecification; import dsp.filter.fir.remez.RemezFIRFilterDesigner; import dsp.mixer.Oscillator; import gui.SDRTrunk; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import sample.Listener; import sample.real.RealBuffer; import sample.real.RealToRealBufferAssembler; import java.util.ArrayList; import java.util.Arrays; import java.util.List; public class PolyphaseFIRDecimatingFilter_RB extends FIRFilter implements Listener<RealBuffer> { private final static Logger mLog = LoggerFactory.getLogger( PolyphaseFIRDecimatingFilter_RB.class ); private List<FilterStage> mFilterStages = new ArrayList<>(); private int mFilterStagePointer; private Listener<RealBuffer> mListener; private RealToRealBufferAssembler mAssembler; private int mDecimationRatio; private float mAccumulator = 0.0f; private float mGain = 1.0f; /** * Polyphase filter stage for decimating real-valued sample buffers by the decimation ratio. Creates a number of * polyphase filter arms/stages equal to the decimation ratio. * * Note: output real-valued buffer sizes are determined by the first input buffer that is presented to the filter * where the output buffer size is equal to the floor of the input buffer size divided by the decimation ratio. * * @param coefficients of a low pass filter designed for the input sample rate with a cutoff frequency that conforms * to the nyquist frequency for an output sample rate equal to the input sample rate divided by the decimation ratio. * * @param decimationRatio to decimate the input sample rate by. * @param gain to apply to the filtered output. Use a value of 1.0 to apply no gain. */ public PolyphaseFIRDecimatingFilter_RB(float[] coefficients, int decimationRatio, float gain) { mDecimationRatio = decimationRatio; mGain = gain; createFilterStages(coefficients, mDecimationRatio); mFilterStagePointer = mFilterStages.size() - 1; } /** * Creates polyphase filter stages (arms) from the set of coefficients. * * @param coefficients for the low-pass filter * @param decimationRatio to determine the number of polyphase filter stages. */ private void createFilterStages(float[] coefficients, int decimationRatio) { int tapSize = (int)Math.ceil((double)coefficients.length / (double)decimationRatio); float[][] coefficientSets = new float[decimationRatio][tapSize]; int stagePointer = 0; int coefficientPointer = 0; //Split the coefficients up into polyphase stage coefficient sets for(int x = 0; x < coefficients.length; x++) { coefficientSets[stagePointer++][coefficientPointer] = coefficients[x]; if(stagePointer >= decimationRatio) { stagePointer = 0; coefficientPointer++; } } //Create the stages for(int x = 0; x < decimationRatio; x++) { mFilterStages.add(new FilterStage(coefficientSets[x])); } } public void dispose() { mListener = null; } @Override public void receive(RealBuffer buffer) { if(mAssembler == null) { mAssembler = new RealToRealBufferAssembler(buffer.getSamples().length / mDecimationRatio); mAssembler.setListener(mListener); } for(float sample: buffer.getSamples()) { filter(sample); } int a = 0; } private void filter(float sample) { FilterStage stage = mFilterStages.get(mFilterStagePointer--); mAccumulator += stage.filter(sample); if(mFilterStagePointer < 0) { mAssembler.receive(mAccumulator * mGain); mAccumulator = 0.0f; mFilterStagePointer = mFilterStages.size() - 1; } } public void setListener(Listener<RealBuffer> listener) { mListener = listener; if(mAssembler != null) { mAssembler.setListener(mListener); } } public void removeListener() { mListener = null; if(mAssembler != null) { mAssembler.setListener(null); } } public static void main(String[] args) { FIRFilterSpecification specification = FIRFilterSpecification.lowPassBuilder() .sampleRate( 48000 ) .gridDensity( 16 ) .passBandCutoff( 3000 ) .passBandAmplitude( 1.0 ) .passBandRipple( 0.01 ) .stopBandStart( 4000 ) .stopBandAmplitude( 0.0 ) .stopBandRipple( 0.027 ) .build(); RemezFIRFilterDesigner designer = new RemezFIRFilterDesigner(specification); try { if(!designer.isValid()) { throw new FilterDesignException("Couldn't design the filter"); } float[] coefficients = designer.getImpulseResponse(); PolyphaseFIRDecimatingFilter_RB filter = new PolyphaseFIRDecimatingFilter_RB(coefficients, 6, 1.0f); filter.setListener(new Listener<RealBuffer>() { @Override public void receive(RealBuffer realBuffer) { mLog.info("Filtered: " + Arrays.toString(realBuffer.getSamples())); } }); Oscillator oscillator = new Oscillator(3400, 48000); float[] samples = new float[500]; for(int x = 0; x < 500; x++) { samples[x] = oscillator.getFloat(); oscillator.rotate(); } RealBuffer buffer = new RealBuffer(samples); filter.receive(buffer); } catch(FilterDesignException e) { mLog.debug("Error designing filter", e); } } }