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 */