package de.tu.darmstadt.seemoo.ansian.control.threads; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.TimeUnit; import android.util.Log; import de.tu.darmstadt.seemoo.ansian.model.SamplePacket; import de.tu.darmstadt.seemoo.ansian.model.filter.FirFilter; import de.tu.darmstadt.seemoo.ansian.model.filter.HalfBandLowPassFilter; /** * <h1>AnSiAn - Decimator</h1> * * Module: Decimator.java Description: This class implements a decimation block * used to downsample the incoming signal to the sample rate used by the * demodulation routines. It will run in a separate thread. * * @author Dennis Mantz * @author Markus Grau * @author Steffen Kreis * * Copyright (C) 2014 Dennis Mantz License: * http://www.gnu.org/licenses/gpl.html GPL version 2 or higher * * This library 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 2 of the License, or (at * your option) any later version. * * This library 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 library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA */ public class Decimator extends Thread { private int outputSampleRate; // sample rate at the output of the decimator // block private boolean stopRequested = true; private static final String LOGTAG = "Decimator"; private static final int OUTPUT_QUEUE_SIZE = 2; // Double Buffer private ArrayBlockingQueue<SamplePacket> inputQueue; // queue that holds // the // incoming sample // packets private ArrayBlockingQueue<SamplePacket> outputQueue; // queue that will // hold the // decimated // sample // packets // DOWNSAMPLING: private static final int INPUT_RATE = 1000000; // For now, this decimator // only works with a fixed // input rate of 1Msps private HalfBandLowPassFilter inputFilter1 = null; private HalfBandLowPassFilter inputFilter2 = null; private HalfBandLowPassFilter inputFilter3 = null; private FirFilter inputFilter4 = null; private SamplePacket tmpDownsampledSamples; /** * Constructor. Will create a new Decimator block. * * @param outputSampleRate * // sample rate to which the incoming samples should be * decimated * @param packetSize * // packet size of the incoming sample packets * @param inputQueue2 * // queue that delivers incoming sample packets * @param inputReturnQueue * // queue to return used input sample packets */ public Decimator(int outputSampleRate, int packetSize, ArrayBlockingQueue<SamplePacket> inputQueue2) { this.outputSampleRate = outputSampleRate; this.inputQueue = inputQueue2; // Create output queues: this.outputQueue = new ArrayBlockingQueue<SamplePacket>(OUTPUT_QUEUE_SIZE); // Create half band filters for downsampling: this.inputFilter1 = new HalfBandLowPassFilter(8); this.inputFilter2 = new HalfBandLowPassFilter(8); this.inputFilter3 = new HalfBandLowPassFilter(8); // Create local buffers: this.tmpDownsampledSamples = new SamplePacket(packetSize); } public int getOutputSampleRate() { return outputSampleRate; } public void setOutputSampleRate(int outputSampleRate) { this.outputSampleRate = outputSampleRate; } public SamplePacket getDecimatedPacket(int timeout) { try { return outputQueue.poll(timeout, TimeUnit.MILLISECONDS); } catch (InterruptedException e) { Log.e(LOGTAG, "getPacket: Interrupted while waiting on queue"); return null; } } @Override public synchronized void start() { this.stopRequested = false; super.start(); } public void stopDecimator() { this.stopRequested = true; } @Override public void run() { SamplePacket inputSamples; Log.i(LOGTAG, "Decimator started. (Thread: " + this.getName() + ")"); while (!stopRequested) { // Get a packet from the input queue: try { inputSamples = inputQueue.poll(1000, TimeUnit.MILLISECONDS); } catch (InterruptedException e) { Log.e(LOGTAG, "run: Interrupted while waiting on input queue! stop."); this.stopRequested = true; break; } // Verify the input sample packet is not null: if (inputSamples == null) { // Log.d(LOGTAG, // "run: Input sample is null. skip this round..."); continue; } // Verify the input sample rate: (For now, this decimator only works // with a fixed input rate of 1Msps) if (inputSamples.getSampleRate() != INPUT_RATE) { Log.d(LOGTAG, "run: Input sample rate is " + inputSamples.getSampleRate() + " but should be" + INPUT_RATE + ". skip."); continue; } // downsampling // deliver the outputSamples to the output queue outputQueue.offer(downsampling(inputSamples)); } this.stopRequested = true; Log.i(LOGTAG, "Decimator stopped. (Thread: " + this.getName() + ")"); } /** * Will decimate the input samples to the outputSampleRate and store them in * output * * @param input * incoming samples at the incoming rate (input rate) * @param output * outgoing (decimated) samples at output rate (quadrature rate) * @return */ private SamplePacket downsampling(SamplePacket input) { SamplePacket output = new SamplePacket(input.size()); // Verify that the input filter 4 is still correct configured (gain): if (inputFilter4 == null || inputFilter4.getGain() != 2 * (outputSampleRate / (double) input.getSampleRate())) { // We have to (re-)create the filter: this.inputFilter4 = FirFilter.createLowPass(2, 2 * (outputSampleRate / (float) input.getSampleRate()), 1, 0.15f, 0.2f, 20); Log.d(LOGTAG, "downsampling: created new inputFilter4 with " + inputFilter4.getNumberOfTaps() + " taps. Decimation=" + inputFilter4.getDecimation() + " Cut-Off=" + inputFilter4.getCutOffFrequency() + " transition=" + inputFilter4.getTransitionWidth()); } // apply first filter (decimate to INPUT_RATE/2) tmpDownsampledSamples.setSize(0); // mark buffer as empty if (inputFilter1.filterN8(input, tmpDownsampledSamples, 0, input.size()) < input.size()) { Log.e(LOGTAG, "downsampling: [inputFilter1] could not filter all samples from input packet."); } // if we need a decimation of 16: apply second and third filter // (decimate to INPUT_RATE/8) if (input.getSampleRate() / outputSampleRate == 16) { output.setSize(0); // mark buffer as empty if (inputFilter2.filterN8(tmpDownsampledSamples, output, 0, tmpDownsampledSamples.size()) < tmpDownsampledSamples.size()) { Log.e(LOGTAG, "downsampling: [inputFilter2] could not filter all samples from input packet."); } tmpDownsampledSamples.setSize(0); // mark tmp buffer as again if (inputFilter3.filterN8(output, tmpDownsampledSamples, 0, output.size()) < output.size()) { Log.e(LOGTAG, "downsampling: [inputFilter3] could not filter all samples from input packet."); } } // apply fourth filter (decimate either to INPUT_RATE/4 or // INPUT_RATE/16) output.setSize(0); // mark buffer as empty if (inputFilter4.filter(tmpDownsampledSamples, output, 0, tmpDownsampledSamples.size()) < tmpDownsampledSamples .size()) { Log.e(LOGTAG, "downsampling: [inputFilter4] could not filter all samples from input packet."); } return output; } }