From 2a0427c5597c816507b2fdd755a2f789d2b23987 Mon Sep 17 00:00:00 2001 From: Denny Sheirer Date: Fri, 31 Dec 2021 10:20:15 -0500 Subject: [PATCH] Adds support for power-of-2 decimation for both real and complex sample buffers. Creates new class to analyze the filter attenuation rates using a variety of filter lengths and window options to enable selecting the optimal combination of cascaded power-of-2 half band decimation filters. (#1127) Co-authored-by: Dennis Sheirer --- .../dsheirer/buffer/FloatCircularBuffer.java | 71 ++++---- .../dsheirer/dsp/filter/FilterFactory.java | 6 +- .../io/github/dsheirer/dsp/filter/Window.java | 6 + .../decimate/ComplexDecimateX1024Filter.java | 53 ++++++ .../decimate/ComplexDecimateX128Filter.java | 53 ++++++ .../decimate/ComplexDecimateX16Filter.java | 53 ++++++ .../decimate/ComplexDecimateX256Filter.java | 53 ++++++ .../decimate/ComplexDecimateX2Filter.java | 55 ++++++ .../decimate/ComplexDecimateX32Filter.java | 53 ++++++ .../decimate/ComplexDecimateX4Filter.java | 54 ++++++ .../decimate/ComplexDecimateX512Filter.java | 53 ++++++ .../decimate/ComplexDecimateX64Filter.java | 53 ++++++ .../decimate/ComplexDecimateX8Filter.java | 53 ++++++ .../decimate/DecimationFilterFactory.java | 102 +++++++++++ .../decimate/IComplexDecimationFilter.java | 28 +++ .../decimate/IRealDecimationFilter.java | 28 +++ .../decimate/RealDecimateX1024Filter.java | 53 ++++++ .../decimate/RealDecimateX128Filter.java | 54 ++++++ .../decimate/RealDecimateX16Filter.java | 53 ++++++ .../decimate/RealDecimateX256Filter.java | 53 ++++++ .../filter/decimate/RealDecimateX2Filter.java | 55 ++++++ .../decimate/RealDecimateX32Filter.java | 53 ++++++ .../filter/decimate/RealDecimateX4Filter.java | 53 ++++++ .../decimate/RealDecimateX512Filter.java | 53 ++++++ .../decimate/RealDecimateX64Filter.java | 53 ++++++ .../filter/decimate/RealDecimateX8Filter.java | 53 ++++++ .../dsp/filter/design/FilterViewer.java | 100 ++--------- .../HalfBandDecimationFilterAnalysis.java | 170 ++++++++++++++++++ .../ComplexHalfBandDecimationFilter.java | 121 +++++++++++++ .../complex/ComplexHalfBandFilter.java | 37 ++-- .../filter/halfband/real/HalfBandFilter2.java | 101 ++++++++--- .../real/RealHalfBandDecimationFilter.java | 112 ++++++++++++ .../gain/ComplexFeedForwardGainControl.java | 29 +-- 33 files changed, 1795 insertions(+), 182 deletions(-) create mode 100644 src/main/java/io/github/dsheirer/dsp/filter/decimate/ComplexDecimateX1024Filter.java create mode 100644 src/main/java/io/github/dsheirer/dsp/filter/decimate/ComplexDecimateX128Filter.java create mode 100644 src/main/java/io/github/dsheirer/dsp/filter/decimate/ComplexDecimateX16Filter.java create mode 100644 src/main/java/io/github/dsheirer/dsp/filter/decimate/ComplexDecimateX256Filter.java create mode 100644 src/main/java/io/github/dsheirer/dsp/filter/decimate/ComplexDecimateX2Filter.java create mode 100644 src/main/java/io/github/dsheirer/dsp/filter/decimate/ComplexDecimateX32Filter.java create mode 100644 src/main/java/io/github/dsheirer/dsp/filter/decimate/ComplexDecimateX4Filter.java create mode 100644 src/main/java/io/github/dsheirer/dsp/filter/decimate/ComplexDecimateX512Filter.java create mode 100644 src/main/java/io/github/dsheirer/dsp/filter/decimate/ComplexDecimateX64Filter.java create mode 100644 src/main/java/io/github/dsheirer/dsp/filter/decimate/ComplexDecimateX8Filter.java create mode 100644 src/main/java/io/github/dsheirer/dsp/filter/decimate/DecimationFilterFactory.java create mode 100644 src/main/java/io/github/dsheirer/dsp/filter/decimate/IComplexDecimationFilter.java create mode 100644 src/main/java/io/github/dsheirer/dsp/filter/decimate/IRealDecimationFilter.java create mode 100644 src/main/java/io/github/dsheirer/dsp/filter/decimate/RealDecimateX1024Filter.java create mode 100644 src/main/java/io/github/dsheirer/dsp/filter/decimate/RealDecimateX128Filter.java create mode 100644 src/main/java/io/github/dsheirer/dsp/filter/decimate/RealDecimateX16Filter.java create mode 100644 src/main/java/io/github/dsheirer/dsp/filter/decimate/RealDecimateX256Filter.java create mode 100644 src/main/java/io/github/dsheirer/dsp/filter/decimate/RealDecimateX2Filter.java create mode 100644 src/main/java/io/github/dsheirer/dsp/filter/decimate/RealDecimateX32Filter.java create mode 100644 src/main/java/io/github/dsheirer/dsp/filter/decimate/RealDecimateX4Filter.java create mode 100644 src/main/java/io/github/dsheirer/dsp/filter/decimate/RealDecimateX512Filter.java create mode 100644 src/main/java/io/github/dsheirer/dsp/filter/decimate/RealDecimateX64Filter.java create mode 100644 src/main/java/io/github/dsheirer/dsp/filter/decimate/RealDecimateX8Filter.java create mode 100644 src/main/java/io/github/dsheirer/dsp/filter/design/HalfBandDecimationFilterAnalysis.java create mode 100644 src/main/java/io/github/dsheirer/dsp/filter/halfband/complex/ComplexHalfBandDecimationFilter.java create mode 100644 src/main/java/io/github/dsheirer/dsp/filter/halfband/real/RealHalfBandDecimationFilter.java diff --git a/src/main/java/io/github/dsheirer/buffer/FloatCircularBuffer.java b/src/main/java/io/github/dsheirer/buffer/FloatCircularBuffer.java index 640e6506..03efc955 100644 --- a/src/main/java/io/github/dsheirer/buffer/FloatCircularBuffer.java +++ b/src/main/java/io/github/dsheirer/buffer/FloatCircularBuffer.java @@ -1,20 +1,21 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer +/* + * ***************************************************************************** + * Copyright (C) 2014-2021 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.buffer; import java.util.Arrays; @@ -25,13 +26,12 @@ import java.util.Arrays; * fills buffer with 0-valued samples * * Can be used as a delay-type buffer, to delay samples by the 'size' amount - * - * @author denny */ public class FloatCircularBuffer { - float[] mBuffer; - int mBufferPointer = 0; + private float[] mBuffer; + private int mBufferPointer = 0; + private float mOldestSample; /** * Creates a circular double buffer of the specified size and all entries filled with the specified initial value. @@ -61,25 +61,27 @@ public class FloatCircularBuffer } /** - * Puts the new value into the buffer and returns the oldest buffer value that it replaced + * Gets the current buffer value and overwrites that value position in the buffer with the new value. * * @param newValue to add to the buffer * @return oldest value that was overwritten by the new value */ - public float get(float newValue) + public float getAndPut(float newValue) { - float oldestSample = mBuffer[mBufferPointer]; + mOldestSample = mBuffer[mBufferPointer]; + put(newValue); + return mOldestSample; + } - mBuffer[mBufferPointer] = newValue; - - mBufferPointer++; - - if(mBufferPointer >= mBuffer.length) - { - mBufferPointer = 0; - } - - return oldestSample; + /** + * Puts the value into the buffer, updates the pointer and returns the buffer value at the pointer position. + * @param sample to add + * @return next pointed sample. + */ + public float putAndGet(float sample) + { + put(sample); + return mBuffer[mBufferPointer]; } public float[] getAll() @@ -109,12 +111,7 @@ public class FloatCircularBuffer public void put(float newValue) { mBuffer[mBufferPointer] = newValue; - mBufferPointer++; - - if(mBufferPointer >= mBuffer.length) - { - mBufferPointer = 0; - } + mBufferPointer %= mBuffer.length; } } diff --git a/src/main/java/io/github/dsheirer/dsp/filter/FilterFactory.java b/src/main/java/io/github/dsheirer/dsp/filter/FilterFactory.java index 6c83b7d4..94b29a63 100644 --- a/src/main/java/io/github/dsheirer/dsp/filter/FilterFactory.java +++ b/src/main/java/io/github/dsheirer/dsp/filter/FilterFactory.java @@ -1004,7 +1004,7 @@ public class FilterFactory * @param windowType to apply to the filter. * @return coefficients */ - public static double[] getHalfBand(int length, Window.WindowType windowType) + public static float[] getHalfBand(int length, Window.WindowType windowType) { if((length - 3) % 4 != 0) { @@ -1013,7 +1013,7 @@ public class FilterFactory } double[] window = Window.getWindow(windowType, length); - double[] taps = new double[length]; + float[] taps = new float[length]; int halfLength = length / 2; @@ -1053,7 +1053,7 @@ public class FilterFactory int length = 15; - double [] taps = FilterFactory.getHalfBand(length, Window.WindowType.HAMMING); + float [] taps = FilterFactory.getHalfBand(length, Window.WindowType.HAMMING); for(int x = 0; x < length; x++) { diff --git a/src/main/java/io/github/dsheirer/dsp/filter/Window.java b/src/main/java/io/github/dsheirer/dsp/filter/Window.java index 7ccea3f0..c0a7e4d5 100644 --- a/src/main/java/io/github/dsheirer/dsp/filter/Window.java +++ b/src/main/java/io/github/dsheirer/dsp/filter/Window.java @@ -24,6 +24,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Arrays; +import java.util.EnumSet; /** * Window factory and corresponding utility methods. @@ -484,12 +485,17 @@ public class Window mLabel = label; } + public String toString() { return mLabel; } } + public static final EnumSet NO_PARAMETER_WINDOWS = EnumSet.of(WindowType.BLACKMAN, WindowType.BLACKMAN_HARRIS_4, + WindowType.BLACKMAN_HARRIS_7, WindowType.BLACKMAN_NUTALL, WindowType.COSINE, WindowType.FLAT_TOP, + WindowType.HAMMING, WindowType.HANN, WindowType.NUTALL); + public static void main(String[] args) { double[] kaiser = getKaiser(16, 80.0); diff --git a/src/main/java/io/github/dsheirer/dsp/filter/decimate/ComplexDecimateX1024Filter.java b/src/main/java/io/github/dsheirer/dsp/filter/decimate/ComplexDecimateX1024Filter.java new file mode 100644 index 00000000..01c067e4 --- /dev/null +++ b/src/main/java/io/github/dsheirer/dsp/filter/decimate/ComplexDecimateX1024Filter.java @@ -0,0 +1,53 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2021 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.decimate; + +import io.github.dsheirer.dsp.filter.FilterFactory; +import io.github.dsheirer.dsp.filter.Window; +import io.github.dsheirer.dsp.filter.halfband.complex.ComplexHalfBandDecimationFilter; + +/** + * Decimate by 1024 filter for complex valued sample buffers. + */ +public class ComplexDecimateX1024Filter extends ComplexDecimateX512Filter +{ + private static final int VALIDATION_LENGTH = 1024 * 2; + private static final int DECIMATE_BY_1024_FILTER_LENGTH = 11; + private static final Window.WindowType DECIMATE_BY_1024_WINDOW_TYPE = Window.WindowType.BLACKMAN; + private ComplexHalfBandDecimationFilter mFilter; + + /** + * Constructs the decimation filter. + */ + public ComplexDecimateX1024Filter() + { + mFilter = new ComplexHalfBandDecimationFilter(FilterFactory.getHalfBand(DECIMATE_BY_1024_FILTER_LENGTH, + DECIMATE_BY_1024_WINDOW_TYPE)); + } + + @Override + public float[] decimateComplex(float[] samples) + { + validate(samples, VALIDATION_LENGTH); + + //Decimate by this filter, then by the parent decimation filter + return super.decimateComplex(mFilter.decimateComplex(samples)); + } +} diff --git a/src/main/java/io/github/dsheirer/dsp/filter/decimate/ComplexDecimateX128Filter.java b/src/main/java/io/github/dsheirer/dsp/filter/decimate/ComplexDecimateX128Filter.java new file mode 100644 index 00000000..c6aac08e --- /dev/null +++ b/src/main/java/io/github/dsheirer/dsp/filter/decimate/ComplexDecimateX128Filter.java @@ -0,0 +1,53 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2021 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.decimate; + +import io.github.dsheirer.dsp.filter.FilterFactory; +import io.github.dsheirer.dsp.filter.Window; +import io.github.dsheirer.dsp.filter.halfband.complex.ComplexHalfBandDecimationFilter; + +/** + * Decimate by 128 filter for complex valued sample buffers. + */ +public class ComplexDecimateX128Filter extends ComplexDecimateX64Filter +{ + private static final int VALIDATION_LENGTH = 128 * 2; + private static final int DECIMATE_BY_128_FILTER_LENGTH = 11; + private static final Window.WindowType DECIMATE_BY_128_WINDOW_TYPE = Window.WindowType.BLACKMAN; + private ComplexHalfBandDecimationFilter mFilter; + + /** + * Constructs the decimation filter. + */ + public ComplexDecimateX128Filter() + { + mFilter = new ComplexHalfBandDecimationFilter(FilterFactory.getHalfBand(DECIMATE_BY_128_FILTER_LENGTH, + DECIMATE_BY_128_WINDOW_TYPE)); + } + + @Override + public float[] decimateComplex(float[] samples) + { + validate(samples, VALIDATION_LENGTH); + + //Decimate by this filter, then by the parent decimation filter + return super.decimateComplex(mFilter.decimateComplex(samples)); + } +} diff --git a/src/main/java/io/github/dsheirer/dsp/filter/decimate/ComplexDecimateX16Filter.java b/src/main/java/io/github/dsheirer/dsp/filter/decimate/ComplexDecimateX16Filter.java new file mode 100644 index 00000000..a6204a0f --- /dev/null +++ b/src/main/java/io/github/dsheirer/dsp/filter/decimate/ComplexDecimateX16Filter.java @@ -0,0 +1,53 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2021 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.decimate; + +import io.github.dsheirer.dsp.filter.FilterFactory; +import io.github.dsheirer.dsp.filter.Window; +import io.github.dsheirer.dsp.filter.halfband.complex.ComplexHalfBandDecimationFilter; + +/** + * Decimate by 16 filter for complex valued sample buffers. + */ +public class ComplexDecimateX16Filter extends ComplexDecimateX8Filter +{ + private static final int VALIDATION_LENGTH = 16 * 2; + private static final int DECIMATE_BY_16_FILTER_LENGTH = 15; + private static final Window.WindowType DECIMATE_BY_16_WINDOW_TYPE = Window.WindowType.BLACKMAN; + private ComplexHalfBandDecimationFilter mFilter; + + /** + * Constructs the decimation filter. + */ + public ComplexDecimateX16Filter() + { + mFilter = new ComplexHalfBandDecimationFilter(FilterFactory.getHalfBand(DECIMATE_BY_16_FILTER_LENGTH, + DECIMATE_BY_16_WINDOW_TYPE)); + } + + @Override + public float[] decimateComplex(float[] samples) + { + validate(samples, VALIDATION_LENGTH); + + //Decimate by this filter, then by the parent decimation filter + return super.decimateComplex(mFilter.decimateComplex(samples)); + } +} diff --git a/src/main/java/io/github/dsheirer/dsp/filter/decimate/ComplexDecimateX256Filter.java b/src/main/java/io/github/dsheirer/dsp/filter/decimate/ComplexDecimateX256Filter.java new file mode 100644 index 00000000..edf62cda --- /dev/null +++ b/src/main/java/io/github/dsheirer/dsp/filter/decimate/ComplexDecimateX256Filter.java @@ -0,0 +1,53 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2021 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.decimate; + +import io.github.dsheirer.dsp.filter.FilterFactory; +import io.github.dsheirer.dsp.filter.Window; +import io.github.dsheirer.dsp.filter.halfband.complex.ComplexHalfBandDecimationFilter; + +/** + * Decimate by 256 filter for complex valued sample buffers. + */ +public class ComplexDecimateX256Filter extends ComplexDecimateX128Filter +{ + private static final int VALIDATION_LENGTH = 256 * 2; + private static final int DECIMATE_BY_256_FILTER_LENGTH = 11; + private static final Window.WindowType DECIMATE_BY_256_WINDOW_TYPE = Window.WindowType.BLACKMAN; + private ComplexHalfBandDecimationFilter mFilter; + + /** + * Constructs the decimation filter. + */ + public ComplexDecimateX256Filter() + { + mFilter = new ComplexHalfBandDecimationFilter(FilterFactory.getHalfBand(DECIMATE_BY_256_FILTER_LENGTH, + DECIMATE_BY_256_WINDOW_TYPE)); + } + + @Override + public float[] decimateComplex(float[] samples) + { + validate(samples, VALIDATION_LENGTH); + + //Decimate by this filter, then by the parent decimation filter + return super.decimateComplex(mFilter.decimateComplex(samples)); + } +} diff --git a/src/main/java/io/github/dsheirer/dsp/filter/decimate/ComplexDecimateX2Filter.java b/src/main/java/io/github/dsheirer/dsp/filter/decimate/ComplexDecimateX2Filter.java new file mode 100644 index 00000000..6adf9721 --- /dev/null +++ b/src/main/java/io/github/dsheirer/dsp/filter/decimate/ComplexDecimateX2Filter.java @@ -0,0 +1,55 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2021 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.decimate; + +import io.github.dsheirer.dsp.filter.FilterFactory; +import io.github.dsheirer.dsp.filter.Window; +import io.github.dsheirer.dsp.filter.halfband.complex.ComplexHalfBandDecimationFilter; + +/** + * Constructs the decimation filter. + */ +public class ComplexDecimateX2Filter extends ComplexHalfBandDecimationFilter +{ + private static final int DECIMATE_BY_2_FILTER_LENGTH = 63; + private static final Window.WindowType DECIMATE_BY_2_WINDOW_TYPE = Window.WindowType.HAMMING; + + /** + * Creates a half band filter with inherent decimation by two. + */ + public ComplexDecimateX2Filter() + { + super(FilterFactory.getHalfBand(DECIMATE_BY_2_FILTER_LENGTH, DECIMATE_BY_2_WINDOW_TYPE)); + } + + /** + * Validates that the samples length is an integer multiple of the specified validation multiple argument. + * @param samples to validate + * @param validationMultiple to validate against + */ + protected static void validate(float[] samples, int validationMultiple) + { + if(samples.length % validationMultiple != 0) + { + throw new IllegalArgumentException("Sample buffer length [" + samples.length + + "] must be an integer multiple of " + validationMultiple); + } + } +} diff --git a/src/main/java/io/github/dsheirer/dsp/filter/decimate/ComplexDecimateX32Filter.java b/src/main/java/io/github/dsheirer/dsp/filter/decimate/ComplexDecimateX32Filter.java new file mode 100644 index 00000000..b3edae7a --- /dev/null +++ b/src/main/java/io/github/dsheirer/dsp/filter/decimate/ComplexDecimateX32Filter.java @@ -0,0 +1,53 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2021 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.decimate; + +import io.github.dsheirer.dsp.filter.FilterFactory; +import io.github.dsheirer.dsp.filter.Window; +import io.github.dsheirer.dsp.filter.halfband.complex.ComplexHalfBandDecimationFilter; + +/** + * Decimate by 32 filter for complex valued sample buffers. + */ +public class ComplexDecimateX32Filter extends ComplexDecimateX16Filter +{ + private static final int VALIDATION_LENGTH = 32 * 2; + private static final int DECIMATE_BY_32_FILTER_LENGTH = 11; + private static final Window.WindowType DECIMATE_BY_32_WINDOW_TYPE = Window.WindowType.BLACKMAN; + private ComplexHalfBandDecimationFilter mFilter; + + /** + * Constructs the decimation filter. + */ + public ComplexDecimateX32Filter() + { + mFilter = new ComplexHalfBandDecimationFilter(FilterFactory.getHalfBand(DECIMATE_BY_32_FILTER_LENGTH, + DECIMATE_BY_32_WINDOW_TYPE)); + } + + @Override + public float[] decimateComplex(float[] samples) + { + validate(samples, VALIDATION_LENGTH); + + //Decimate by this filter, then by the parent decimation filter + return super.decimateComplex(mFilter.decimateComplex(samples)); + } +} diff --git a/src/main/java/io/github/dsheirer/dsp/filter/decimate/ComplexDecimateX4Filter.java b/src/main/java/io/github/dsheirer/dsp/filter/decimate/ComplexDecimateX4Filter.java new file mode 100644 index 00000000..738bd74a --- /dev/null +++ b/src/main/java/io/github/dsheirer/dsp/filter/decimate/ComplexDecimateX4Filter.java @@ -0,0 +1,54 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2021 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.decimate; + +import io.github.dsheirer.dsp.filter.FilterFactory; +import io.github.dsheirer.dsp.filter.Window; +import io.github.dsheirer.dsp.filter.halfband.complex.ComplexHalfBandDecimationFilter; +import io.github.dsheirer.dsp.filter.halfband.real.RealHalfBandDecimationFilter; + +/** + * Decimate by 4 filter for complex valued sample buffers. + */ +public class ComplexDecimateX4Filter extends ComplexDecimateX2Filter +{ + private static final int VALIDATION_LENGTH = 4 * 2; + private static final int DECIMATE_BY_4_FILTER_LENGTH = 23; + private static final Window.WindowType DECIMATE_BY_4_WINDOW_TYPE = Window.WindowType.BLACKMAN; + private ComplexHalfBandDecimationFilter mFilter; + + /** + * Constructs the decimation filter. + */ + public ComplexDecimateX4Filter() + { + mFilter = new ComplexHalfBandDecimationFilter(FilterFactory.getHalfBand(DECIMATE_BY_4_FILTER_LENGTH, + DECIMATE_BY_4_WINDOW_TYPE)); + } + + @Override + public float[] decimateComplex(float[] samples) + { + validate(samples, VALIDATION_LENGTH); + + //Decimate by this filter, then by the parent decimation filter + return super.decimateComplex(mFilter.decimateComplex(samples)); + } +} diff --git a/src/main/java/io/github/dsheirer/dsp/filter/decimate/ComplexDecimateX512Filter.java b/src/main/java/io/github/dsheirer/dsp/filter/decimate/ComplexDecimateX512Filter.java new file mode 100644 index 00000000..ef9d5472 --- /dev/null +++ b/src/main/java/io/github/dsheirer/dsp/filter/decimate/ComplexDecimateX512Filter.java @@ -0,0 +1,53 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2021 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.decimate; + +import io.github.dsheirer.dsp.filter.FilterFactory; +import io.github.dsheirer.dsp.filter.Window; +import io.github.dsheirer.dsp.filter.halfband.complex.ComplexHalfBandDecimationFilter; + +/** + * Decimate by 512 filter for complex valued sample buffers. + */ +public class ComplexDecimateX512Filter extends ComplexDecimateX256Filter +{ + private static final int VALIDATION_LENGTH = 512 * 2; + private static final int DECIMATE_BY_512_FILTER_LENGTH = 11; + private static final Window.WindowType DECIMATE_BY_512_WINDOW_TYPE = Window.WindowType.BLACKMAN; + private ComplexHalfBandDecimationFilter mFilter; + + /** + * Constructs the decimation filter. + */ + public ComplexDecimateX512Filter() + { + mFilter = new ComplexHalfBandDecimationFilter(FilterFactory.getHalfBand(DECIMATE_BY_512_FILTER_LENGTH, + DECIMATE_BY_512_WINDOW_TYPE)); + } + + @Override + public float[] decimateComplex(float[] samples) + { + validate(samples, VALIDATION_LENGTH); + + //Decimate by this filter, then by the parent decimation filter + return super.decimateComplex(mFilter.decimateComplex(samples)); + } +} diff --git a/src/main/java/io/github/dsheirer/dsp/filter/decimate/ComplexDecimateX64Filter.java b/src/main/java/io/github/dsheirer/dsp/filter/decimate/ComplexDecimateX64Filter.java new file mode 100644 index 00000000..26152ec2 --- /dev/null +++ b/src/main/java/io/github/dsheirer/dsp/filter/decimate/ComplexDecimateX64Filter.java @@ -0,0 +1,53 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2021 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.decimate; + +import io.github.dsheirer.dsp.filter.FilterFactory; +import io.github.dsheirer.dsp.filter.Window; +import io.github.dsheirer.dsp.filter.halfband.complex.ComplexHalfBandDecimationFilter; + +/** + * Decimate by 64 filter for complex valued sample buffers. + */ +public class ComplexDecimateX64Filter extends ComplexDecimateX32Filter +{ + private static final int VALIDATION_LENGTH = 64 * 2; + private static final int DECIMATE_BY_64_FILTER_LENGTH = 11; + private static final Window.WindowType DECIMATE_BY_64_WINDOW_TYPE = Window.WindowType.BLACKMAN; + private ComplexHalfBandDecimationFilter mFilter; + + /** + * Constructs the decimation filter. + */ + public ComplexDecimateX64Filter() + { + mFilter = new ComplexHalfBandDecimationFilter(FilterFactory.getHalfBand(DECIMATE_BY_64_FILTER_LENGTH, + DECIMATE_BY_64_WINDOW_TYPE)); + } + + @Override + public float[] decimateComplex(float[] samples) + { + validate(samples, VALIDATION_LENGTH); + + //Decimate by this filter, then by the parent decimation filter + return super.decimateComplex(mFilter.decimateComplex(samples)); + } +} diff --git a/src/main/java/io/github/dsheirer/dsp/filter/decimate/ComplexDecimateX8Filter.java b/src/main/java/io/github/dsheirer/dsp/filter/decimate/ComplexDecimateX8Filter.java new file mode 100644 index 00000000..42477c9c --- /dev/null +++ b/src/main/java/io/github/dsheirer/dsp/filter/decimate/ComplexDecimateX8Filter.java @@ -0,0 +1,53 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2021 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.decimate; + +import io.github.dsheirer.dsp.filter.FilterFactory; +import io.github.dsheirer.dsp.filter.Window; +import io.github.dsheirer.dsp.filter.halfband.complex.ComplexHalfBandDecimationFilter; + +/** + * Decimate by 8 filter for complex valued sample buffers. + */ +public class ComplexDecimateX8Filter extends ComplexDecimateX4Filter +{ + private static final int VALIDATION_LENGTH = 8 * 2; + private static final int DECIMATE_BY_8_FILTER_LENGTH = 15; + private static final Window.WindowType DECIMATE_BY_8_WINDOW_TYPE = Window.WindowType.BLACKMAN; + private ComplexHalfBandDecimationFilter mFilter; + + /** + * Constructs the decimation filter. + */ + public ComplexDecimateX8Filter() + { + mFilter = new ComplexHalfBandDecimationFilter(FilterFactory.getHalfBand(DECIMATE_BY_8_FILTER_LENGTH, + DECIMATE_BY_8_WINDOW_TYPE)); + } + + @Override + public float[] decimateComplex(float[] samples) + { + validate(samples, VALIDATION_LENGTH); + + //Decimate by this filter, then by the parent decimation filter + return super.decimateComplex(mFilter.decimateComplex(samples)); + } +} diff --git a/src/main/java/io/github/dsheirer/dsp/filter/decimate/DecimationFilterFactory.java b/src/main/java/io/github/dsheirer/dsp/filter/decimate/DecimationFilterFactory.java new file mode 100644 index 00000000..85e491ef --- /dev/null +++ b/src/main/java/io/github/dsheirer/dsp/filter/decimate/DecimationFilterFactory.java @@ -0,0 +1,102 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2021 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.decimate; + +/** + * Factory for creating real and complex decimation filters. + */ +public class DecimationFilterFactory +{ + private static final int[] SUPPORTED_RATES = new int[]{2,4,8,16,32,64,128,256,512,1024}; + + /** + * Creates a real-valued decimation filter for float array sample buffers providing greater than 100 dB of + * attenuation for out of band signal aliases. Supports power of 2 decimation rates up to 1024. + * + * @param decimationRate requested @see SUPPORTED_RATES + * @return constructed decimation filter + */ + public static IRealDecimationFilter getRealDecimationFilter(int decimationRate) + { + switch(decimationRate) + { + case 2: + return new RealDecimateX2Filter(); + case 4: + return new RealDecimateX4Filter(); + case 8: + return new RealDecimateX8Filter(); + case 16: + return new RealDecimateX16Filter(); + case 32: + return new RealDecimateX32Filter(); + case 64: + return new RealDecimateX64Filter(); + case 128: + return new RealDecimateX128Filter(); + case 256: + return new RealDecimateX256Filter(); + case 512: + return new RealDecimateX512Filter(); + case 1024: + return new RealDecimateX1024Filter(); + default: + throw new IllegalArgumentException("Unsupported decimation rate: " + decimationRate + + ". Supported decimation rates are:" + SUPPORTED_RATES); + } + } + + /** + * Creates a complex-valued decimation filter for float array sample buffers providing greater than 100 dB of + * attenuation for out of band signal aliases. Supports power of 2 decimation rates up to 1024. + * + * @param decimationRate requested @see SUPPORTED_RATES + * @return constructed decimation filter + */ + public static IComplexDecimationFilter getComplexDecimationFilter(int decimationRate) + { + switch(decimationRate) + { + case 2: + return new ComplexDecimateX2Filter(); + case 4: + return new ComplexDecimateX4Filter(); + case 8: + return new ComplexDecimateX8Filter(); + case 16: + return new ComplexDecimateX16Filter(); + case 32: + return new ComplexDecimateX32Filter(); + case 64: + return new ComplexDecimateX64Filter(); + case 128: + return new ComplexDecimateX128Filter(); + case 256: + return new ComplexDecimateX256Filter(); + case 512: + return new ComplexDecimateX512Filter(); + case 1024: + return new ComplexDecimateX1024Filter(); + default: + throw new IllegalArgumentException("Unsupported decimation rate: " + decimationRate + + ". Supported decimation rates are:" + SUPPORTED_RATES); + } + } +} diff --git a/src/main/java/io/github/dsheirer/dsp/filter/decimate/IComplexDecimationFilter.java b/src/main/java/io/github/dsheirer/dsp/filter/decimate/IComplexDecimationFilter.java new file mode 100644 index 00000000..0c6c544e --- /dev/null +++ b/src/main/java/io/github/dsheirer/dsp/filter/decimate/IComplexDecimationFilter.java @@ -0,0 +1,28 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2021 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.decimate; + +/** + * Interface for a decimation filter for float array sample buffers + */ +public interface IComplexDecimationFilter +{ + float[] decimateComplex(float[] samples); +} diff --git a/src/main/java/io/github/dsheirer/dsp/filter/decimate/IRealDecimationFilter.java b/src/main/java/io/github/dsheirer/dsp/filter/decimate/IRealDecimationFilter.java new file mode 100644 index 00000000..a595afb5 --- /dev/null +++ b/src/main/java/io/github/dsheirer/dsp/filter/decimate/IRealDecimationFilter.java @@ -0,0 +1,28 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2021 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.decimate; + +/** + * Interface for a decimation filter for float array sample buffers + */ +public interface IRealDecimationFilter +{ + float[] decimateReal(float[] samples); +} diff --git a/src/main/java/io/github/dsheirer/dsp/filter/decimate/RealDecimateX1024Filter.java b/src/main/java/io/github/dsheirer/dsp/filter/decimate/RealDecimateX1024Filter.java new file mode 100644 index 00000000..5a4fba7c --- /dev/null +++ b/src/main/java/io/github/dsheirer/dsp/filter/decimate/RealDecimateX1024Filter.java @@ -0,0 +1,53 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2021 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.decimate; + +import io.github.dsheirer.dsp.filter.FilterFactory; +import io.github.dsheirer.dsp.filter.Window; +import io.github.dsheirer.dsp.filter.halfband.real.RealHalfBandDecimationFilter; + +/** + * Decimate by 1024 filter for real valued sample buffers. + */ +public class RealDecimateX1024Filter extends RealDecimateX512Filter +{ + private static final int VALIDATION_LENGTH = 1024; + private static final int DECIMATE_BY_1024_FILTER_LENGTH = 11; + private static final Window.WindowType DECIMATE_BY_1024_WINDOW_TYPE = Window.WindowType.BLACKMAN; + private RealHalfBandDecimationFilter mFilter; + + /** + * Constructs the decimation filter. + */ + public RealDecimateX1024Filter() + { + mFilter = new RealHalfBandDecimationFilter(FilterFactory.getHalfBand(DECIMATE_BY_1024_FILTER_LENGTH, + DECIMATE_BY_1024_WINDOW_TYPE)); + } + + @Override + public float[] decimateReal(float[] samples) + { + validate(samples, VALIDATION_LENGTH); + + //Decimate by this filter, then by the parent decimation filter + return super.decimateReal(mFilter.decimateReal(samples)); + } +} diff --git a/src/main/java/io/github/dsheirer/dsp/filter/decimate/RealDecimateX128Filter.java b/src/main/java/io/github/dsheirer/dsp/filter/decimate/RealDecimateX128Filter.java new file mode 100644 index 00000000..faf2da51 --- /dev/null +++ b/src/main/java/io/github/dsheirer/dsp/filter/decimate/RealDecimateX128Filter.java @@ -0,0 +1,54 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2021 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.decimate; + +import io.github.dsheirer.dsp.filter.FilterFactory; +import io.github.dsheirer.dsp.filter.Window; +import io.github.dsheirer.dsp.filter.halfband.real.RealHalfBandDecimationFilter; +import io.github.dsheirer.gui.playlist.alias.ViewAliasIdentifierRequest; + +/** + * Decimate by 128 filter for real valued sample buffers. + */ +public class RealDecimateX128Filter extends RealDecimateX64Filter +{ + private static final int VALIDATION_LENGTH = 128; + private static final int DECIMATE_BY_128_FILTER_LENGTH = 11; + private static final Window.WindowType DECIMATE_BY_128_WINDOW_TYPE = Window.WindowType.BLACKMAN; + private RealHalfBandDecimationFilter mFilter; + + /** + * Constructs the decimation filter. + */ + public RealDecimateX128Filter() + { + mFilter = new RealHalfBandDecimationFilter(FilterFactory.getHalfBand(DECIMATE_BY_128_FILTER_LENGTH, + DECIMATE_BY_128_WINDOW_TYPE)); + } + + @Override + public float[] decimateReal(float[] samples) + { + validate(samples, VALIDATION_LENGTH); + + //Decimate by this filter, then by the parent decimation filter + return super.decimateReal(mFilter.decimateReal(samples)); + } +} diff --git a/src/main/java/io/github/dsheirer/dsp/filter/decimate/RealDecimateX16Filter.java b/src/main/java/io/github/dsheirer/dsp/filter/decimate/RealDecimateX16Filter.java new file mode 100644 index 00000000..ca732a0b --- /dev/null +++ b/src/main/java/io/github/dsheirer/dsp/filter/decimate/RealDecimateX16Filter.java @@ -0,0 +1,53 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2021 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.decimate; + +import io.github.dsheirer.dsp.filter.FilterFactory; +import io.github.dsheirer.dsp.filter.Window; +import io.github.dsheirer.dsp.filter.halfband.real.RealHalfBandDecimationFilter; + +/** + * Decimate by 16 filter for real valued sample buffers. + */ +public class RealDecimateX16Filter extends RealDecimateX8Filter +{ + private static final int VALIDATION_LENGTH = 16; + private static final int DECIMATE_BY_16_FILTER_LENGTH = 15; + private static final Window.WindowType DECIMATE_BY_16_WINDOW_TYPE = Window.WindowType.BLACKMAN; + private RealHalfBandDecimationFilter mFilter; + + /** + * Constructs the decimation filter. + */ + public RealDecimateX16Filter() + { + mFilter = new RealHalfBandDecimationFilter(FilterFactory.getHalfBand(DECIMATE_BY_16_FILTER_LENGTH, + DECIMATE_BY_16_WINDOW_TYPE)); + } + + @Override + public float[] decimateReal(float[] samples) + { + validate(samples, VALIDATION_LENGTH); + + //Decimate by this filter, then by the parent decimation filter + return super.decimateReal(mFilter.decimateReal(samples)); + } +} diff --git a/src/main/java/io/github/dsheirer/dsp/filter/decimate/RealDecimateX256Filter.java b/src/main/java/io/github/dsheirer/dsp/filter/decimate/RealDecimateX256Filter.java new file mode 100644 index 00000000..e72e3d36 --- /dev/null +++ b/src/main/java/io/github/dsheirer/dsp/filter/decimate/RealDecimateX256Filter.java @@ -0,0 +1,53 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2021 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.decimate; + +import io.github.dsheirer.dsp.filter.FilterFactory; +import io.github.dsheirer.dsp.filter.Window; +import io.github.dsheirer.dsp.filter.halfband.real.RealHalfBandDecimationFilter; + +/** + * Decimate by 256 filter for real valued sample buffers. + */ +public class RealDecimateX256Filter extends RealDecimateX128Filter +{ + private static final int VALIDATION_LENGTH = 256; + private static final int DECIMATE_BY_256_FILTER_LENGTH = 11; + private static final Window.WindowType DECIMATE_BY_256_WINDOW_TYPE = Window.WindowType.BLACKMAN; + private RealHalfBandDecimationFilter mFilter; + + /** + * Constructs the decimation filter. + */ + public RealDecimateX256Filter() + { + mFilter = new RealHalfBandDecimationFilter(FilterFactory.getHalfBand(DECIMATE_BY_256_FILTER_LENGTH, + DECIMATE_BY_256_WINDOW_TYPE)); + } + + @Override + public float[] decimateReal(float[] samples) + { + validate(samples, VALIDATION_LENGTH); + + //Decimate by this filter, then by the parent decimation filter + return super.decimateReal(mFilter.decimateReal(samples)); + } +} diff --git a/src/main/java/io/github/dsheirer/dsp/filter/decimate/RealDecimateX2Filter.java b/src/main/java/io/github/dsheirer/dsp/filter/decimate/RealDecimateX2Filter.java new file mode 100644 index 00000000..c62daef8 --- /dev/null +++ b/src/main/java/io/github/dsheirer/dsp/filter/decimate/RealDecimateX2Filter.java @@ -0,0 +1,55 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2021 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.decimate; + +import io.github.dsheirer.dsp.filter.FilterFactory; +import io.github.dsheirer.dsp.filter.Window; +import io.github.dsheirer.dsp.filter.halfband.real.RealHalfBandDecimationFilter; + +/** + * Constructs the decimation filter. + */ +public class RealDecimateX2Filter extends RealHalfBandDecimationFilter +{ + private static final int DECIMATE_BY_2_FILTER_LENGTH = 63; + private static final Window.WindowType DECIMATE_BY_2_WINDOW_TYPE = Window.WindowType.HAMMING; + + /** + * Creates a half band filter with inherent decimation by two. + */ + public RealDecimateX2Filter() + { + super(FilterFactory.getHalfBand(DECIMATE_BY_2_FILTER_LENGTH, DECIMATE_BY_2_WINDOW_TYPE)); + } + + /** + * Validates that the samples length is an integer multiple of the specified validation multiple argument. + * @param samples to validate + * @param validationMultiple to validate against + */ + protected static void validate(float[] samples, int validationMultiple) + { + if(samples.length % validationMultiple != 0) + { + throw new IllegalArgumentException("Sample buffer length [" + samples.length + + "] must be an integer multiple of " + validationMultiple); + } + } +} diff --git a/src/main/java/io/github/dsheirer/dsp/filter/decimate/RealDecimateX32Filter.java b/src/main/java/io/github/dsheirer/dsp/filter/decimate/RealDecimateX32Filter.java new file mode 100644 index 00000000..86227039 --- /dev/null +++ b/src/main/java/io/github/dsheirer/dsp/filter/decimate/RealDecimateX32Filter.java @@ -0,0 +1,53 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2021 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.decimate; + +import io.github.dsheirer.dsp.filter.FilterFactory; +import io.github.dsheirer.dsp.filter.Window; +import io.github.dsheirer.dsp.filter.halfband.real.RealHalfBandDecimationFilter; + +/** + * Decimate by 32 filter for real valued sample buffers. + */ +public class RealDecimateX32Filter extends RealDecimateX16Filter +{ + private static final int VALIDATION_LENGTH = 32; + private static final int DECIMATE_BY_32_FILTER_LENGTH = 11; + private static final Window.WindowType DECIMATE_BY_32_WINDOW_TYPE = Window.WindowType.BLACKMAN; + private RealHalfBandDecimationFilter mFilter; + + /** + * Constructs the decimation filter. + */ + public RealDecimateX32Filter() + { + mFilter = new RealHalfBandDecimationFilter(FilterFactory.getHalfBand(DECIMATE_BY_32_FILTER_LENGTH, + DECIMATE_BY_32_WINDOW_TYPE)); + } + + @Override + public float[] decimateReal(float[] samples) + { + validate(samples, VALIDATION_LENGTH); + + //Decimate by this filter, then by the parent decimation filter + return super.decimateReal(mFilter.decimateReal(samples)); + } +} diff --git a/src/main/java/io/github/dsheirer/dsp/filter/decimate/RealDecimateX4Filter.java b/src/main/java/io/github/dsheirer/dsp/filter/decimate/RealDecimateX4Filter.java new file mode 100644 index 00000000..5461cf91 --- /dev/null +++ b/src/main/java/io/github/dsheirer/dsp/filter/decimate/RealDecimateX4Filter.java @@ -0,0 +1,53 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2021 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.decimate; + +import io.github.dsheirer.dsp.filter.FilterFactory; +import io.github.dsheirer.dsp.filter.Window; +import io.github.dsheirer.dsp.filter.halfband.real.RealHalfBandDecimationFilter; + +/** + * Decimate by 4 filter for real valued sample buffers. + */ +public class RealDecimateX4Filter extends RealDecimateX2Filter +{ + private static final int VALIDATION_LENGTH = 4; + private static final int DECIMATE_BY_4_FILTER_LENGTH = 23; + private static final Window.WindowType DECIMATE_BY_4_WINDOW_TYPE = Window.WindowType.BLACKMAN; + private RealHalfBandDecimationFilter mFilter; + + /** + * Constructs the decimation filter. + */ + public RealDecimateX4Filter() + { + mFilter = new RealHalfBandDecimationFilter(FilterFactory.getHalfBand(DECIMATE_BY_4_FILTER_LENGTH, + DECIMATE_BY_4_WINDOW_TYPE)); + } + + @Override + public float[] decimateReal(float[] samples) + { + validate(samples, VALIDATION_LENGTH); + + //Decimate by this filter, then by the parent decimation filter + return super.decimateReal(mFilter.decimateReal(samples)); + } +} diff --git a/src/main/java/io/github/dsheirer/dsp/filter/decimate/RealDecimateX512Filter.java b/src/main/java/io/github/dsheirer/dsp/filter/decimate/RealDecimateX512Filter.java new file mode 100644 index 00000000..7d2882ef --- /dev/null +++ b/src/main/java/io/github/dsheirer/dsp/filter/decimate/RealDecimateX512Filter.java @@ -0,0 +1,53 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2021 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.decimate; + +import io.github.dsheirer.dsp.filter.FilterFactory; +import io.github.dsheirer.dsp.filter.Window; +import io.github.dsheirer.dsp.filter.halfband.real.RealHalfBandDecimationFilter; + +/** + * Decimate by 512 filter for real valued sample buffers. + */ +public class RealDecimateX512Filter extends RealDecimateX256Filter +{ + private static final int VALIDATION_LENGTH = 512; + private static final int DECIMATE_BY_512_FILTER_LENGTH = 11; + private static final Window.WindowType DECIMATE_BY_512_WINDOW_TYPE = Window.WindowType.BLACKMAN; + private RealHalfBandDecimationFilter mFilter; + + /** + * Constructs the decimation filter. + */ + public RealDecimateX512Filter() + { + mFilter = new RealHalfBandDecimationFilter(FilterFactory.getHalfBand(DECIMATE_BY_512_FILTER_LENGTH, + DECIMATE_BY_512_WINDOW_TYPE)); + } + + @Override + public float[] decimateReal(float[] samples) + { + validate(samples, VALIDATION_LENGTH); + + //Decimate by this filter, then by the parent decimation filter + return super.decimateReal(mFilter.decimateReal(samples)); + } +} diff --git a/src/main/java/io/github/dsheirer/dsp/filter/decimate/RealDecimateX64Filter.java b/src/main/java/io/github/dsheirer/dsp/filter/decimate/RealDecimateX64Filter.java new file mode 100644 index 00000000..a2ba8c87 --- /dev/null +++ b/src/main/java/io/github/dsheirer/dsp/filter/decimate/RealDecimateX64Filter.java @@ -0,0 +1,53 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2021 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.decimate; + +import io.github.dsheirer.dsp.filter.FilterFactory; +import io.github.dsheirer.dsp.filter.Window; +import io.github.dsheirer.dsp.filter.halfband.real.RealHalfBandDecimationFilter; + +/** + * Decimate by 64 filter for real valued sample buffers. + */ +public class RealDecimateX64Filter extends RealDecimateX32Filter +{ + private static final int VALIDATION_LENGTH = 64; + private static final int DECIMATE_BY_64_FILTER_LENGTH = 11; + private static final Window.WindowType DECIMATE_BY_64_WINDOW_TYPE = Window.WindowType.BLACKMAN; + private RealHalfBandDecimationFilter mFilter; + + /** + * Constructs the decimation filter. + */ + public RealDecimateX64Filter() + { + mFilter = new RealHalfBandDecimationFilter(FilterFactory.getHalfBand(DECIMATE_BY_64_FILTER_LENGTH, + DECIMATE_BY_64_WINDOW_TYPE)); + } + + @Override + public float[] decimateReal(float[] samples) + { + validate(samples, VALIDATION_LENGTH); + + //Decimate by this filter, then by the parent decimation filter + return super.decimateReal(mFilter.decimateReal(samples)); + } +} diff --git a/src/main/java/io/github/dsheirer/dsp/filter/decimate/RealDecimateX8Filter.java b/src/main/java/io/github/dsheirer/dsp/filter/decimate/RealDecimateX8Filter.java new file mode 100644 index 00000000..8feb9318 --- /dev/null +++ b/src/main/java/io/github/dsheirer/dsp/filter/decimate/RealDecimateX8Filter.java @@ -0,0 +1,53 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2021 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.decimate; + +import io.github.dsheirer.dsp.filter.FilterFactory; +import io.github.dsheirer.dsp.filter.Window; +import io.github.dsheirer.dsp.filter.halfband.real.RealHalfBandDecimationFilter; + +/** + * Decimate by 8 filter for real valued sample buffers. + */ +public class RealDecimateX8Filter extends RealDecimateX4Filter +{ + private static final int VALIDATION_LENGTH = 8; + private static final int DECIMATE_BY_8_FILTER_LENGTH = 15; + private static final Window.WindowType DECIMATE_BY_8_WINDOW_TYPE = Window.WindowType.BLACKMAN; + private RealHalfBandDecimationFilter mFilter; + + /** + * Constructs the decimation filter. + */ + public RealDecimateX8Filter() + { + mFilter = new RealHalfBandDecimationFilter(FilterFactory.getHalfBand(DECIMATE_BY_8_FILTER_LENGTH, + DECIMATE_BY_8_WINDOW_TYPE)); + } + + @Override + public float[] decimateReal(float[] samples) + { + validate(samples, VALIDATION_LENGTH); + + //Decimate by this filter, then by the parent decimation filter + return super.decimateReal(mFilter.decimateReal(samples)); + } +} diff --git a/src/main/java/io/github/dsheirer/dsp/filter/design/FilterViewer.java b/src/main/java/io/github/dsheirer/dsp/filter/design/FilterViewer.java index 6af2eea3..c4062976 100644 --- a/src/main/java/io/github/dsheirer/dsp/filter/design/FilterViewer.java +++ b/src/main/java/io/github/dsheirer/dsp/filter/design/FilterViewer.java @@ -1,28 +1,26 @@ /* + * ***************************************************************************** + * Copyright (C) 2014-2021 Dennis Sheirer * - * * ****************************************************************************** - * * Copyright (C) 2014-2019 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 - * * ***************************************************************************** + * 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.design; import io.github.dsheirer.dsp.filter.FilterFactory; +import io.github.dsheirer.dsp.filter.Window; import io.github.dsheirer.dsp.filter.fir.FIRFilterSpecification; import javafx.application.Application; import javafx.scene.Scene; @@ -61,74 +59,10 @@ public class FilterViewer extends Application */ private float[] getFilter() { -// FIRFilterSpecification specification = FIRFilterSpecification.lowPassBuilder() -// .sampleRate(8000) -// .gridDensity(16) -// .oddLength(true) -// .passBandCutoff(300) -// .passBandAmplitude(1.0) -// .passBandRipple(0.01) -// .stopBandStart(500) -// .stopBandAmplitude(0.0) -// .stopBandRipple(0.03) //Approximately 60 dB attenuation -// .build(); -// -// FIRFilterSpecification specification = FIRFilterSpecification.highPassBuilder() -// .sampleRate(8000) -// .stopBandCutoff(200) -// .stopBandAmplitude(0.0) -// .stopBandRipple(0.025) -// .passBandStart(300) -// .passBandAmplitude(1.0) -// .passBandRipple(0.01) -// .build(); - -// FIRFilterSpecification specification = FIRFilterSpecification.lowPassBuilder() -// .sampleRate(50000.0) -// .passBandCutoff(6500) -// .passBandAmplitude(1.0) -// .passBandRipple(0.005) -// .stopBandAmplitude(0.0) -// .stopBandStart(7200) -// .stopBandRipple(0.01) -// .build(); - - float[] taps = null; - - boolean rrc = false; - - if(rrc) - { - double sampleRate = 50000.0; - int symbolRate = 4800; - double samplesPerSymbol = sampleRate / symbolRate / 2; - float alpha = 0.25f; - int symbolCount = 20; - - taps = FilterFactory.getRootRaisedCosine(samplesPerSymbol, symbolCount, alpha); - } - else - { - FIRFilterSpecification specification = FIRFilterSpecification.lowPassBuilder() - .sampleRate(50000) - .passBandCutoff(4500) - .passBandAmplitude(1.0) - .passBandRipple(0.1) - .stopBandAmplitude(0.0) - .stopBandStart(7000) - .stopBandRipple(0.01) - .build(); - - try - { - taps = FilterFactory.getTaps(specification);// - } - catch(Exception fde) //FilterDesignException - { - mLog.error("Couldn't design low pass baseband filter for sample rate"); - } - } + int filterLength = 63; + Window.WindowType windowType = Window.WindowType.HAMMING; + float[] taps = FilterFactory.getHalfBand(filterLength, windowType); if(taps == null) { diff --git a/src/main/java/io/github/dsheirer/dsp/filter/design/HalfBandDecimationFilterAnalysis.java b/src/main/java/io/github/dsheirer/dsp/filter/design/HalfBandDecimationFilterAnalysis.java new file mode 100644 index 00000000..14c4dee3 --- /dev/null +++ b/src/main/java/io/github/dsheirer/dsp/filter/design/HalfBandDecimationFilterAnalysis.java @@ -0,0 +1,170 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2021 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.design; + +import io.github.dsheirer.dsp.filter.FilterFactory; +import io.github.dsheirer.dsp.filter.Window; +import org.apache.commons.math3.util.FastMath; +import org.jtransforms.fft.FloatFFT_1D; + +import java.text.DecimalFormat; + +/** + * Develop tables that illustrate Half-Band filter attenutation at various filter lenths and decimation by two scenarios. + */ +public class HalfBandDecimationFilterAnalysis +{ + private static final int FFT_SIZE = 4096; + private static FloatFFT_1D FFT = new FloatFFT_1D(FFT_SIZE); + private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("0.0"); + + private static final Window.WindowType WINDOW_TYPE = Window.WindowType.BLACKMAN; + private static final int[] TAP_LENGTHS = new int[]{7,11,15,19,23,27,31,35,39,43,47,51,55,59,63,67,71,75,79,83,87,91,95,99,103,107}; + private static final int BY_2 = (int)(FFT_SIZE / 2 * (1.0 / 2.0)); + private static final int BY_2_25 = (int)(FFT_SIZE / 2 * (1.0 / 2.25)); + private static final int BY_2_50 = (int)(FFT_SIZE / 2 * (1.0 / 2.5)); + private static final int BY_2_75 = (int)(FFT_SIZE / 2 * (1.0 / 2.75)); + private static final int BY_3 = (int)(FFT_SIZE / 2 * (1.0 / 3.0)); + private static final int BY_4 = (int)(FFT_SIZE / 2 * (1.0 / 4.0)); + private static final int BY_8 = (int)(FFT_SIZE / 2 * (1.0 / 8.0)); + private static final int BY_16 = (int)(FFT_SIZE / 2 * (1.0 / 16.0)); + private static final int BY_32 = (int)(FFT_SIZE / 2 * (1.0 / 32.0)); + private static final int BY_64 = (int)(FFT_SIZE / 2 * (1.0 / 64.0)); + private static final int BY_128 = (int)(FFT_SIZE / 2 * (1.0 / 128.0)); + private static final int BY_256 = (int)(FFT_SIZE / 2 * (1.0 / 256.0)); + private static final int BY_512 = (int)(FFT_SIZE / 2 * (1.0 / 512.0)); + private static final int BY_1024 = (int)(FFT_SIZE / 2 * (1.0 / 1024.0)); + + public HalfBandDecimationFilterAnalysis() + { + } + + public static void analyze() + { + for(Window.WindowType windowType: Window.NO_PARAMETER_WINDOWS) + { + StringBuilder sb = new StringBuilder(); + sb.append("Window: " + windowType).append("\n"); + sb.append("Length\t\t2x\t\t2.25x\t2.50x\t2.75x\t3x\t\t4x\t\t8x\t\t16x\t\t32x\t\t64x\t\t128x\t256x\t512x\t1024x\n"); + + for(int length: TAP_LENGTHS) + { + float[] taps = getTaps(length, windowType); + float[] dft = getDFT(taps); + float[] decibels = convertDFTToDecibel(dft, FFT_SIZE / 2); + float x2Cutoff = getCutoff(BY_2, decibels); + float x225Cutoff = getCutoff(BY_2_25, decibels); + float x250Cutoff = getCutoff(BY_2_50, decibels); + float x275Cutoff = getCutoff(BY_2_75, decibels); + float x3Cutoff = getCutoff(BY_3, decibels); + float x4Cutoff = getCutoff(BY_4, decibels); + float x8Cutoff = getCutoff(BY_8, decibels); + float x16Cutoff = getCutoff(BY_16, decibels); + float x32Cutoff = getCutoff(BY_32, decibels); + float x64Cutoff = getCutoff(BY_64, decibels); + float x128Cutoff = getCutoff(BY_128, decibels); + float x256Cutoff = getCutoff(BY_256, decibels); + float x512Cutoff = getCutoff(BY_512, decibels); + float x1024Cutoff = getCutoff(BY_1024, decibels); + + sb.append(length).append("\t\t\t"); + sb.append(DECIMAL_FORMAT.format(x2Cutoff)).append("\t"); + sb.append(DECIMAL_FORMAT.format(x225Cutoff)).append("\t"); + sb.append(DECIMAL_FORMAT.format(x250Cutoff)).append("\t"); + sb.append(DECIMAL_FORMAT.format(x275Cutoff)).append("\t"); + sb.append(DECIMAL_FORMAT.format(x3Cutoff)).append("\t"); + sb.append(DECIMAL_FORMAT.format(x4Cutoff)).append("\t"); + sb.append(DECIMAL_FORMAT.format(x8Cutoff)).append("\t"); + sb.append(DECIMAL_FORMAT.format(x16Cutoff)).append("\t"); + sb.append(DECIMAL_FORMAT.format(x32Cutoff)).append("\t"); + sb.append(DECIMAL_FORMAT.format(x64Cutoff)).append("\t"); + sb.append(DECIMAL_FORMAT.format(x128Cutoff)).append("\t"); + sb.append(DECIMAL_FORMAT.format(x256Cutoff)).append("\t"); + sb.append(DECIMAL_FORMAT.format(x512Cutoff)).append("\t"); + sb.append(DECIMAL_FORMAT.format(x1024Cutoff)).append("\n"); + } + + System.out.println(sb.toString()); + } + } + + /** + * Calculates the maximum cutoff starting at the specified index through the end index. + * @param index + * @return + */ + private static float getCutoff(int index, float[] decibels) + { + float cutoff = -200.0f; + + int startIndex = decibels.length - index; + + for(int x = startIndex; x < decibels.length; x++) + { + if(decibels[x] > cutoff) + { + cutoff = decibels[x]; + } + } + + return cutoff; + } + + /** + * Creates a half-band filter for the specified filter lenth and window type. + * @param filterLength for the filter + * @param windowType to apply to the taps during filter creation + * @return filter + */ + private static float[] getTaps(int filterLength, Window.WindowType windowType) + { + return FilterFactory.getHalfBand(filterLength, windowType); + } + + private static float[] getDFT(float[] taps) + { + float[] dft = new float[FFT_SIZE]; + System.arraycopy(taps, 0, dft, 0, taps.length); + FFT.realForward(dft); + return dft; + } + + public static float[] convertDFTToDecibel(float[] dft, int tapLength) + { + float[] decibels = new float[dft.length / 2]; + + int index = 0; + + for(int x = 0; x < decibels.length; x++) + { + index = x * 2; + + decibels[x] = 20.0f * (float) FastMath.log10(((dft[index] * dft[index]) + + (dft[index + 1] * dft[index + 1]))); + } + + return decibels; + } + + public static void main(String[] args) + { + HalfBandDecimationFilterAnalysis.analyze(); + } +} diff --git a/src/main/java/io/github/dsheirer/dsp/filter/halfband/complex/ComplexHalfBandDecimationFilter.java b/src/main/java/io/github/dsheirer/dsp/filter/halfband/complex/ComplexHalfBandDecimationFilter.java new file mode 100644 index 00000000..8b308a95 --- /dev/null +++ b/src/main/java/io/github/dsheirer/dsp/filter/halfband/complex/ComplexHalfBandDecimationFilter.java @@ -0,0 +1,121 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2021 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.decimate.IComplexDecimationFilter; + +/** + * Complex half-band filter that processes samples on a per-array basis, versus a per-sample basis. + */ +public class ComplexHalfBandDecimationFilter implements IComplexDecimationFilter +{ + private static final float CENTER_COEFFICIENT = 0.5f; + private float[] mCoefficients; + private float[] mBuffer; + private float mIAccumulator; + private float mQAccumulator; + private int mCoefficientPointer; + private int mCoefficientsLengthMinus2; + private int mBufferPointer; + private int mHalf; + + /** + * Constructs a complex sample half-band decimate x2 filter using the specified filter coefficients. + * @param coefficients for the half-band filter where the middle coefficient is 0.5, and the even-numbered + * coefficients are non-zero and symmetrical, and the odd-numbered coefficients are all zero valued. + */ + public ComplexHalfBandDecimationFilter(float[] coefficients) + { + if((coefficients.length + 1) % 4 != 0) + { + throw new IllegalArgumentException("Half-band filter coefficients must be odd-length and " + + "symmetrical where L = [x * 4 - 1]"); + } + + mCoefficients = new float[coefficients.length * 2]; + for(int x = 0; x < coefficients.length; x++) + { + mCoefficients[2 * x] = coefficients[x]; + mCoefficients[2 * x + 1] = coefficients[x]; + } + + mCoefficientsLengthMinus2 = mCoefficients.length - 2; + mHalf = mCoefficients.length / 2 - 1; + } + + public float[] decimateComplex(float[] samples) + { + if(samples.length % 4 != 0) + { + throw new IllegalArgumentException("Samples array length must be an integer multiple of 4"); + } + + int bufferLength = samples.length + mCoefficientsLengthMinus2; + + if(mBuffer == null) + { + mBuffer = new float[bufferLength]; + } + else if(mBuffer.length != bufferLength) + { + float[] temp = new float[bufferLength]; + //Move residual samples from end of old buffer to the beginning of the new temp buffer + System.arraycopy(mBuffer, mBuffer.length - mCoefficientsLengthMinus2, temp, 0, mCoefficientsLengthMinus2); + mBuffer = temp; + } + else + { + //Move residual samples from end of buffer to the beginning of the buffer + System.arraycopy(mBuffer, samples.length, mBuffer, 0, mCoefficientsLengthMinus2); + } + + //Copy new sample array into end of buffer + System.arraycopy(samples, 0, mBuffer, mCoefficientsLengthMinus2, samples.length); + + float[] filtered = new float[samples.length / 2]; + + for(mBufferPointer = 0; mBufferPointer < samples.length; mBufferPointer += 4) + { + mIAccumulator = 0.0f; + mQAccumulator = 0.0f; + + for(mCoefficientPointer = 0; mCoefficientPointer < mHalf; mCoefficientPointer += 4) + { + //Half band filter coefficients are mirrored, so we add the mirrored samples and then multiply by + //one of the coefficients to achieve the same effect. + mIAccumulator += mCoefficients[mCoefficientPointer] * + (mBuffer[mBufferPointer + mCoefficientPointer] + + mBuffer[mBufferPointer + (mCoefficientsLengthMinus2 - mCoefficientPointer)]); + + mQAccumulator += mCoefficients[mCoefficientPointer] * + (mBuffer[mBufferPointer + mCoefficientPointer + 1] + + mBuffer[mBufferPointer + (mCoefficientsLengthMinus2 - mCoefficientPointer) + 1]); + } + + mIAccumulator += mBuffer[mBufferPointer + mHalf] * CENTER_COEFFICIENT; + mQAccumulator += mBuffer[mBufferPointer + mHalf + 1] * CENTER_COEFFICIENT; + + filtered[mBufferPointer / 2] = mIAccumulator; + filtered[mBufferPointer / 2 + 1] = mQAccumulator; + } + + return filtered; + } +} 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 index e79774f8..b86f190f 100644 --- 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 @@ -1,18 +1,21 @@ -/******************************************************************************* - * sdr-trunk - * Copyright (C) 2014-2018 Dennis Sheirer +/* + * ***************************************************************************** + * Copyright (C) 2014-2021 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.halfband.complex; import io.github.dsheirer.dsp.filter.Filters; @@ -36,10 +39,10 @@ public class ComplexHalfBandFilter private float[] mFilteredSamples; private int mFilteredSamplesPointer; - public ComplexHalfBandFilter(float[] coefficients, float gain) + public ComplexHalfBandFilter(float[] coefficients) { - mIFilter = new HalfBandFilter2(coefficients, gain); - mQFilter = new HalfBandFilter2(coefficients, gain); + mIFilter = new HalfBandFilter2(coefficients); + mQFilter = new HalfBandFilter2(coefficients); } public ReusableComplexBuffer filter(ReusableComplexBuffer originalBuffer) @@ -63,7 +66,7 @@ public class ComplexHalfBandFilter while(mSamplesPointer + 3 < mSamples.length) { mFilteredSamples[mFilteredSamplesPointer++] = mIFilter.filter(mSamples[mSamplesPointer], mSamples[mSamplesPointer + 2]); - mFilteredSamples[mFilteredSamplesPointer++] = mIFilter.filter(mSamples[mSamplesPointer + 1], mSamples[mSamplesPointer + 3]); + mFilteredSamples[mFilteredSamplesPointer++] = mQFilter.filter(mSamples[mSamplesPointer + 1], mSamples[mSamplesPointer + 3]); mSamplesPointer += 4; } @@ -81,7 +84,7 @@ public class ComplexHalfBandFilter public static void main(String[] args) { Oscillator oscillator = new Oscillator(1, 16); - ComplexHalfBandFilter filter = new ComplexHalfBandFilter(Filters.HALF_BAND_FILTER_27T.getCoefficients(), 1.0f); + ComplexHalfBandFilter filter = new ComplexHalfBandFilter(Filters.HALF_BAND_FILTER_27T.getCoefficients()); ReusableComplexBufferQueue bufferQueue = new ReusableComplexBufferQueue("Test"); diff --git a/src/main/java/io/github/dsheirer/dsp/filter/halfband/real/HalfBandFilter2.java b/src/main/java/io/github/dsheirer/dsp/filter/halfband/real/HalfBandFilter2.java index 4b4ccbde..ddfd5724 100644 --- a/src/main/java/io/github/dsheirer/dsp/filter/halfband/real/HalfBandFilter2.java +++ b/src/main/java/io/github/dsheirer/dsp/filter/halfband/real/HalfBandFilter2.java @@ -1,20 +1,29 @@ -/******************************************************************************* - * sdr-trunk - * Copyright (C) 2014-2018 Dennis Sheirer +/* + * ***************************************************************************** + * Copyright (C) 2014-2021 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.halfband.real; +import io.github.dsheirer.buffer.FloatCircularBuffer; +import io.github.dsheirer.dsp.filter.FilterFactory; +import io.github.dsheirer.dsp.filter.Window; + +import java.util.Random; + /** * Implements a half-band filter that produces one filtered output for every two input samples. * @@ -26,30 +35,26 @@ public class HalfBandFilter2 private static final float CENTER_COEFFICIENT = 0.5f; private float[] mCoefficients; private float[] mEvenSamples; - private float[] mOddSamples; - private int mCenterSampleIndex; private float mAccumulator; private int mPointer; - private float mGain; + private FloatCircularBuffer mCircularBuffer; /** * Creates a half band filter with inherent decimation by two. * * @param coefficients of the half-band filter that is odd-length where all odd index coefficients are * zero valued except for the middle odd index coefficient which should be valued 0.5 - * @param gain value to apply to the output */ - public HalfBandFilter2(float[] coefficients, float gain) + public HalfBandFilter2(float[] coefficients) { - if(coefficients.length % 2 == 0) + if((coefficients.length + 1) % 4 != 0) { - throw new IllegalArgumentException("Half-band filter coefficients must be odd-length"); + throw new IllegalArgumentException("Half-band filter coefficients must be odd-length and symmetrical (length = [x * 4 + 1]"); } - mGain = gain; - int half = coefficients.length / 2 + 1; + mCircularBuffer = new FloatCircularBuffer(half / 2); mCoefficients = new float[half]; mEvenSamples = new float[half]; @@ -58,20 +63,14 @@ public class HalfBandFilter2 { mCoefficients[x / 2] = coefficients[x]; } - - //Setup the odd samples array as a simple delay line, half the size of the even samples array - mOddSamples = new float[half / 2]; - mCenterSampleIndex = mOddSamples.length - 1; } public float filter(float sample1, float sample2) { //Structured for SIMD array copy optimization when supported by host CPU System.arraycopy(mEvenSamples, 0, mEvenSamples, 1, mEvenSamples.length - 1); - System.arraycopy(mOddSamples, 0, mOddSamples, 1, mOddSamples.length - 1); mEvenSamples[0] = sample1; - mOddSamples[0] = sample2; mAccumulator = 0.0f; @@ -81,9 +80,53 @@ public class HalfBandFilter2 mAccumulator += (mEvenSamples[mPointer] * mCoefficients[mPointer]); } - mAccumulator += (mOddSamples[mCenterSampleIndex] * CENTER_COEFFICIENT); - mAccumulator *= mGain; + mAccumulator += (mCircularBuffer.getAndPut(sample2) * CENTER_COEFFICIENT); return mAccumulator; } + + public float[] filter(float[] samples) + { + float[] filtered = new float[samples.length / 2]; + for(int x = 0; x < samples.length; x += 2) + { + filtered[x / 2] = filter(samples[x], samples[x + 1]); + } + + return filtered; + } + + public static void main(String[] args) + { + System.out.println("Starting ..."); + float[] taps = FilterFactory.getHalfBand(99, Window.WindowType.HAMMING); + + HalfBandFilter2 halfband = new HalfBandFilter2(taps); + + Random random = new Random(); + float[] samples = new float[1024]; + float[] filtered = new float[512]; + + for(int x = 0; x < samples.length; x++) + { + samples[x] = (2.0f * random.nextFloat()) - 1.0f; + } + + System.out.println("Testing ..."); + long current = System.currentTimeMillis(); + + int iterations = 10_000_000; + + for(int x = 0; x < iterations; x++) + { + for(int y = 0; y < samples.length; y += 2) + { + filtered[y / 2] = halfband.filter(samples[y], samples[y + 1]); + } + } + + long elapsed = System.currentTimeMillis() - current; + + System.out.println("Elapsed: " + (elapsed / 1000d) + " seconds"); + } } diff --git a/src/main/java/io/github/dsheirer/dsp/filter/halfband/real/RealHalfBandDecimationFilter.java b/src/main/java/io/github/dsheirer/dsp/filter/halfband/real/RealHalfBandDecimationFilter.java new file mode 100644 index 00000000..53d99569 --- /dev/null +++ b/src/main/java/io/github/dsheirer/dsp/filter/halfband/real/RealHalfBandDecimationFilter.java @@ -0,0 +1,112 @@ +/* + * ***************************************************************************** + * Copyright (C) 2014-2021 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.dsp.filter.decimate.IRealDecimationFilter; + +/** + * Implements a half-band filter that produces one filtered output for every two input samples. + * + * This filter is structured for Java 8+ compiler JIT optimization for SIMD instructions when + * supported by the host CPU. + * + * Note: this class is structured to process an entire float array, versus processing one sample at a time from the + * array. + */ +public class RealHalfBandDecimationFilter implements IRealDecimationFilter +{ + private static final float CENTER_COEFFICIENT = 0.5f; + private float[] mCoefficients; + private float[] mBuffer; + private float mAccumulator; + private int mCoefficientPointer; + private int mCoefficientsLengthMinus1; + private int mBufferPointer; + private int mHalf; + + /** + * Creates a half band filter with inherent decimation by two. + * + * @param coefficients of the half-band filter that is odd-length where all odd index coefficients are + * zero valued except for the middle odd index coefficient which should be valued 0.5 + */ + public RealHalfBandDecimationFilter(float[] coefficients) + { + if((coefficients.length + 1) % 4 != 0) + { + throw new IllegalArgumentException("Half-band filter coefficients must be odd-length and symmetrical (length = [x * 4 + 1]"); + } + + mCoefficients = coefficients; + mCoefficientsLengthMinus1 = mCoefficients.length - 1; + mHalf = mCoefficientsLengthMinus1 / 2; + } + + public float[] decimateReal(float[] samples) + { + if(samples.length % 2 != 0) + { + throw new IllegalArgumentException("Samples array length must be an integer multiple of 2"); + } + + int bufferLength = samples.length + mCoefficientsLengthMinus1; + + if(mBuffer == null) + { + mBuffer = new float[bufferLength]; + } + else if(mBuffer.length != bufferLength) + { + float[] temp = new float[bufferLength]; + //Move residual samples from end of old buffer to the beginning of the new temp buffer + System.arraycopy(mBuffer, mBuffer.length - mCoefficientsLengthMinus1, temp, 0, mCoefficientsLengthMinus1); + mBuffer = temp; + } + else + { + //Move residual samples from end of buffer to the beginning of the buffer + System.arraycopy(mBuffer, samples.length, mBuffer, 0, mCoefficientsLengthMinus1); + } + + //Copy new sample array into end of buffer + System.arraycopy(samples, 0, mBuffer, mCoefficientsLengthMinus1, samples.length); + + float[] filtered = new float[samples.length / 2]; + + for(mBufferPointer = 0; mBufferPointer < samples.length; mBufferPointer += 2) + { + mAccumulator = 0.0f; + + for(mCoefficientPointer = 0; mCoefficientPointer < mHalf; mCoefficientPointer += 2) + { + //Half band filter coefficients are mirrored, so we add the mirrored samples and then multiply by + //one of the coefficients to achieve the same effect. + mAccumulator += mCoefficients[mCoefficientPointer] * + (mBuffer[mBufferPointer + mCoefficientPointer] + + mBuffer[mBufferPointer + (mCoefficientsLengthMinus1 - mCoefficientPointer)]); + } + + mAccumulator += mBuffer[mBufferPointer + mHalf] * CENTER_COEFFICIENT; + + filtered[mBufferPointer / 2] = mAccumulator; + } + + return filtered; + } +} 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 3ae83bdd..0eef1a31 100644 --- a/src/main/java/io/github/dsheirer/dsp/gain/ComplexFeedForwardGainControl.java +++ b/src/main/java/io/github/dsheirer/dsp/gain/ComplexFeedForwardGainControl.java @@ -1,18 +1,21 @@ -/******************************************************************************* - * sdr-trunk - * Copyright (C) 2014-2018 Dennis Sheirer +/* + * ***************************************************************************** + * Copyright (C) 2014-2021 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.gain; import io.github.dsheirer.buffer.FloatCircularBuffer; @@ -78,7 +81,7 @@ public class ComplexFeedForwardGainControl implements ComplexSampleListener } /* Replace oldest envelope value with current envelope value */ - float oldestEnvelope = mEnvelopeHistory.get(envelope); + float oldestEnvelope = mEnvelopeHistory.getAndPut(envelope); /* If the oldest envelope value was the max envelope value, then we * have to rediscover the max value from the envelope history */