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@github.com>
This commit is contained in:
Denny Sheirer
2021-12-31 10:20:15 -05:00
committed by GitHub
parent 5d9d232585
commit 2a0427c559
33 changed files with 1795 additions and 182 deletions

View File

@@ -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 <http://www.gnu.org/licenses/>
******************************************************************************/
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package 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;
}
}

View File

@@ -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++)
{

View File

@@ -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<WindowType> 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);

View File

@@ -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 <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
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));
}
}

View File

@@ -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 <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
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));
}
}

View File

@@ -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 <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
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));
}
}

View File

@@ -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 <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
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));
}
}

View File

@@ -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 <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
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);
}
}
}

View File

@@ -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 <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
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));
}
}

View File

@@ -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 <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
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));
}
}

View File

@@ -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 <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
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));
}
}

View File

@@ -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 <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
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));
}
}

View File

@@ -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 <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
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));
}
}

View File

@@ -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 <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
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);
}
}
}

View File

@@ -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 <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.dsp.filter.decimate;
/**
* Interface for a decimation filter for float array sample buffers
*/
public interface IComplexDecimationFilter
{
float[] decimateComplex(float[] samples);
}

View File

@@ -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 <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.dsp.filter.decimate;
/**
* Interface for a decimation filter for float array sample buffers
*/
public interface IRealDecimationFilter
{
float[] decimateReal(float[] samples);
}

View File

@@ -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 <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
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));
}
}

View File

@@ -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 <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
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));
}
}

View File

@@ -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 <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
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));
}
}

View File

@@ -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 <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
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));
}
}

View File

@@ -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 <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
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);
}
}
}

View File

@@ -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 <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
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));
}
}

View File

@@ -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 <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
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));
}
}

View File

@@ -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 <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
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));
}
}

View File

@@ -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 <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
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));
}
}

View File

@@ -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 <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
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));
}
}

View File

@@ -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 <http://www.gnu.org/licenses/>
* * *****************************************************************************
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package 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)
{

View File

@@ -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 <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
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();
}
}

View File

@@ -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 <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
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;
}
}

View File

@@ -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 <http://www.gnu.org/licenses/>
*
******************************************************************************/
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package 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");

View File

@@ -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 <http://www.gnu.org/licenses/>
*
******************************************************************************/
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package 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");
}
}

View File

@@ -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 <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
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;
}
}

View File

@@ -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 <http://www.gnu.org/licenses/>
*
******************************************************************************/
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package 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 */