diff --git a/src/main/java/io/github/dsheirer/dsp/filter/channelizer/PolyphaseChannelSource.java b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/PolyphaseChannelSource.java
index 8aee60f4..1645a77a 100644
--- a/src/main/java/io/github/dsheirer/dsp/filter/channelizer/PolyphaseChannelSource.java
+++ b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/PolyphaseChannelSource.java
@@ -1,7 +1,6 @@
/*
- * ******************************************************************************
- * sdrtrunk
- * Copyright (C) 2014-2018 Dennis Sheirer
+ * *****************************************************************************
+ * Copyright (C) 2014-2022 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
@@ -15,7 +14,7 @@
*
* 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.channelizer;
@@ -40,6 +39,7 @@ public class PolyphaseChannelSource extends TunerChannelSource
{
// private final static Logger mLog = LoggerFactory.getLogger(PolyphaseChannelSource.class);
+ private static final int PROCESSED_BUFFER_SAMPLE_SIZE = 2048;
private ReusableComplexBufferAssembler mReusableComplexBufferAssembler;
private IPolyphaseChannelOutputProcessor mPolyphaseChannelOutputProcessor;
private IPolyphaseChannelOutputProcessor mReplacementPolyphaseChannelOutputProcessor;
@@ -71,7 +71,7 @@ public class PolyphaseChannelSource extends TunerChannelSource
super(producerSourceEventListener, tunerChannel);
mPolyphaseChannelOutputProcessor = outputProcessor;
mChannelSampleRate = channelSampleRate;
- mReusableComplexBufferAssembler = new ReusableComplexBufferAssembler(2500, mChannelSampleRate);
+ mReusableComplexBufferAssembler = new ReusableComplexBufferAssembler(PROCESSED_BUFFER_SAMPLE_SIZE, mChannelSampleRate);
float[] filterCoefficients = getLowPassFilter(channelSampleRate, channelSpecification.getPassFrequency(),
channelSpecification.getStopFrequency());
diff --git a/src/main/java/io/github/dsheirer/dsp/filter/decimate/ComplexDecimateX0Filter.java b/src/main/java/io/github/dsheirer/dsp/filter/decimate/ComplexDecimateX0Filter.java
new file mode 100644
index 00000000..bd686ddd
--- /dev/null
+++ b/src/main/java/io/github/dsheirer/dsp/filter/decimate/ComplexDecimateX0Filter.java
@@ -0,0 +1,40 @@
+/*
+ * *****************************************************************************
+ * Copyright (C) 2014-2022 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.sample.buffer.ReusableComplexBuffer;
+
+/**
+ * No decimation filter.
+ */
+public class ComplexDecimateX0Filter implements IComplexDecimationFilter
+{
+ @Override
+ public float[] decimateComplex(float[] samples)
+ {
+ return samples;
+ }
+
+ @Override
+ public ReusableComplexBuffer decimate(ReusableComplexBuffer buffer)
+ {
+ return buffer;
+ }
+}
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
index 85e491ef..d8d5a750 100644
--- a/src/main/java/io/github/dsheirer/dsp/filter/decimate/DecimationFilterFactory.java
+++ b/src/main/java/io/github/dsheirer/dsp/filter/decimate/DecimationFilterFactory.java
@@ -1,6 +1,6 @@
/*
* *****************************************************************************
- * Copyright (C) 2014-2021 Dennis Sheirer
+ * Copyright (C) 2014-2022 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
@@ -24,7 +24,7 @@ package io.github.dsheirer.dsp.filter.decimate;
*/
public class DecimationFilterFactory
{
- private static final int[] SUPPORTED_RATES = new int[]{2,4,8,16,32,64,128,256,512,1024};
+ private static final int[] SUPPORTED_RATES = new int[]{0,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
@@ -37,6 +37,8 @@ public class DecimationFilterFactory
{
switch(decimationRate)
{
+ case 0:
+ return new RealDecimateX0Filter();
case 2:
return new RealDecimateX2Filter();
case 4:
@@ -74,6 +76,8 @@ public class DecimationFilterFactory
{
switch(decimationRate)
{
+ case 0:
+ return new ComplexDecimateX0Filter();
case 2:
return new ComplexDecimateX2Filter();
case 4:
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
index 0c6c544e..1779ece9 100644
--- a/src/main/java/io/github/dsheirer/dsp/filter/decimate/IComplexDecimationFilter.java
+++ b/src/main/java/io/github/dsheirer/dsp/filter/decimate/IComplexDecimationFilter.java
@@ -1,6 +1,6 @@
/*
* *****************************************************************************
- * Copyright (C) 2014-2021 Dennis Sheirer
+ * Copyright (C) 2014-2022 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
@@ -19,10 +19,24 @@
package io.github.dsheirer.dsp.filter.decimate;
+import io.github.dsheirer.sample.buffer.ReusableComplexBuffer;
+
/**
* Interface for a decimation filter for float array sample buffers
*/
public interface IComplexDecimationFilter
{
+ /**
+ * Decimates an array of float complex samples
+ * @param samples to decimate
+ * @return decimated samples
+ */
float[] decimateComplex(float[] samples);
+
+ /**
+ * Decimates an buffer of float complex samples
+ * @param buffer to decimate
+ * @return decimated samples
+ */
+ ReusableComplexBuffer decimate(ReusableComplexBuffer buffer);
}
diff --git a/src/main/java/io/github/dsheirer/dsp/filter/decimate/RealDecimateX0Filter.java b/src/main/java/io/github/dsheirer/dsp/filter/decimate/RealDecimateX0Filter.java
new file mode 100644
index 00000000..84b3620d
--- /dev/null
+++ b/src/main/java/io/github/dsheirer/dsp/filter/decimate/RealDecimateX0Filter.java
@@ -0,0 +1,32 @@
+/*
+ * *****************************************************************************
+ * Copyright (C) 2014-2022 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;
+
+/**
+ * No decimation filter.
+ */
+public class RealDecimateX0Filter implements IRealDecimationFilter
+{
+ @Override
+ public float[] decimateReal(float[] samples)
+ {
+ return samples;
+ }
+}
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
index 8b308a95..b13ae60c 100644
--- 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
@@ -1,6 +1,6 @@
/*
* *****************************************************************************
- * Copyright (C) 2014-2021 Dennis Sheirer
+ * Copyright (C) 2014-2022 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
@@ -20,6 +20,8 @@
package io.github.dsheirer.dsp.filter.halfband.complex;
import io.github.dsheirer.dsp.filter.decimate.IComplexDecimationFilter;
+import io.github.dsheirer.sample.buffer.ReusableComplexBuffer;
+import io.github.dsheirer.sample.buffer.ReusableComplexBufferQueue;
/**
* Complex half-band filter that processes samples on a per-array basis, versus a per-sample basis.
@@ -35,6 +37,7 @@ public class ComplexHalfBandDecimationFilter implements IComplexDecimationFilter
private int mCoefficientsLengthMinus2;
private int mBufferPointer;
private int mHalf;
+ private ReusableComplexBufferQueue mReusableComplexBufferQueue = new ReusableComplexBufferQueue("half-band decimation filter");
/**
* Constructs a complex sample half-band decimate x2 filter using the specified filter coefficients.
@@ -118,4 +121,17 @@ public class ComplexHalfBandDecimationFilter implements IComplexDecimationFilter
return filtered;
}
+
+ /**
+ * Decimates the complex samples and returns a buffer of decimated samples.
+ * @param buffer to decimate
+ * @return decimated buffer.
+ */
+ @Override
+ public ReusableComplexBuffer decimate(ReusableComplexBuffer buffer)
+ {
+ float[] decimated = decimateComplex(buffer.getSamples());
+ buffer.decrementUserCount();
+ return mReusableComplexBufferQueue.getBuffer(decimated, buffer.getTimestamp());
+ }
}
diff --git a/src/main/java/io/github/dsheirer/module/decode/nbfm/NBFMDecoder.java b/src/main/java/io/github/dsheirer/module/decode/nbfm/NBFMDecoder.java
index 4676744c..d2f82592 100644
--- a/src/main/java/io/github/dsheirer/module/decode/nbfm/NBFMDecoder.java
+++ b/src/main/java/io/github/dsheirer/module/decode/nbfm/NBFMDecoder.java
@@ -1,20 +1,21 @@
-/*******************************************************************************
- * SDR Trunk
- * Copyright (C) 2014,2015 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
- ******************************************************************************/
+/*
+ * *****************************************************************************
+ * Copyright (C) 2014-2022 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.module.decode.nbfm;
import io.github.dsheirer.channel.state.DecoderStateEvent;
@@ -22,6 +23,8 @@ import io.github.dsheirer.channel.state.IDecoderStateEventProvider;
import io.github.dsheirer.channel.state.State;
import io.github.dsheirer.dsp.filter.FilterFactory;
import io.github.dsheirer.dsp.filter.Window;
+import io.github.dsheirer.dsp.filter.decimate.DecimationFilterFactory;
+import io.github.dsheirer.dsp.filter.decimate.IComplexDecimationFilter;
import io.github.dsheirer.dsp.filter.design.FilterDesignException;
import io.github.dsheirer.dsp.filter.fir.FIRFilterSpecification;
import io.github.dsheirer.dsp.filter.fir.complex.ComplexFIRFilter2;
@@ -49,11 +52,12 @@ public class NBFMDecoder extends PrimaryDecoder implements ISourceEventListener,
{
private final static Logger mLog = LoggerFactory.getLogger(NBFMDecoder.class);
private static final double DEMODULATED_AUDIO_SAMPLE_RATE = 8000.0;
- private static final double POWER_SQUELCH_ALPHA_DECAY = 0.0002;
+ private static final double POWER_SQUELCH_ALPHA_DECAY = 0.0004;
private static final double POWER_SQUELCH_THRESHOLD_DB = -78.0;
private static final int POWER_SQUELCH_RAMP = 4;
private ComplexFIRFilter2 mIQFilter;
+ private IComplexDecimationFilter mDecimationFilter;
private SquelchingFMDemodulator mDemodulator = new SquelchingFMDemodulator(POWER_SQUELCH_ALPHA_DECAY,
POWER_SQUELCH_THRESHOLD_DB, POWER_SQUELCH_RAMP);
private RealResampler mResampler;
@@ -131,7 +135,9 @@ public class NBFMDecoder extends PrimaryDecoder implements ISourceEventListener,
"event before it can process complex sample buffers");
}
- ReusableComplexBuffer basebandFilteredBuffer = mIQFilter.filter(reusableComplexBuffer);
+ ReusableComplexBuffer decimatedBuffer = mDecimationFilter.decimate(reusableComplexBuffer);
+ ReusableComplexBuffer basebandFilteredBuffer = mIQFilter.filter(decimatedBuffer);
+
ReusableFloatBuffer demodulatedBuffer = mDemodulator.demodulate(basebandFilteredBuffer);
if(mResampler != null)
@@ -268,11 +274,31 @@ public class NBFMDecoder extends PrimaryDecoder implements ISourceEventListener,
double sampleRate = sourceEvent.getValue().doubleValue();
- if((sampleRate < (2.0 * mChannelBandwidth)))
+ int decimationRate = 0;
+ double decimatedSampleRate = sampleRate;
+
+ if(sampleRate / 2 > mChannelBandwidth)
+ {
+ decimationRate = 2;
+
+ while(sampleRate / decimationRate / 2 > mChannelBandwidth)
+ {
+ decimationRate *= 2;
+ }
+ }
+
+ if(decimationRate > 0)
+ {
+ decimatedSampleRate /= decimationRate;
+ }
+
+ mDecimationFilter = DecimationFilterFactory.getComplexDecimationFilter(decimationRate);
+
+ if((decimatedSampleRate < (2.0 * mChannelBandwidth)))
{
throw new IllegalStateException("FM Demodulator with channel bandwidth [" + mChannelBandwidth +
"] requires a channel sample rate of [" + (2.0 * mChannelBandwidth + "] - sample rate of [" +
- sampleRate + "] is not supported"));
+ decimatedSampleRate + "] is not supported"));
}
int passBandStop = (int)(mChannelBandwidth * .8);
@@ -281,7 +307,7 @@ public class NBFMDecoder extends PrimaryDecoder implements ISourceEventListener,
float[] filterTaps = null;
FIRFilterSpecification specification = FIRFilterSpecification.lowPassBuilder()
- .sampleRate(sampleRate)
+ .sampleRate(decimatedSampleRate * 2)
.gridDensity(16)
.oddLength(true)
.passBandCutoff(passBandStop)
@@ -300,18 +326,21 @@ public class NBFMDecoder extends PrimaryDecoder implements ISourceEventListener,
{
mLog.error("Couldn't design FM demodulator remez filter for sample rate [" + sampleRate +
"] pass frequency [" + passBandStop + "] and stop frequency [" + stopBandStart +
- "] - using sinc filter");
+ "] - will proceed using sinc (low-pass) filter");
}
if(filterTaps == null)
{
- filterTaps = FilterFactory.getLowPass(sampleRate, passBandStop, stopBandStart, 60,
+ mLog.info("Unable to use remez filter designer for sample rate [" + decimatedSampleRate +
+ "] pass band stop [" + passBandStop +
+ "] and stop band start [" + stopBandStart + "] - will proceed using simple low pass filter design");
+ filterTaps = FilterFactory.getLowPass(decimatedSampleRate, passBandStop, stopBandStart, 60,
Window.WindowType.HAMMING, true);
}
mIQFilter = new ComplexFIRFilter2(filterTaps);
- mResampler = new RealResampler(sampleRate, mOutputSampleRate, 2000, 1000);
+ mResampler = new RealResampler(decimatedSampleRate, mOutputSampleRate, 2000, 1000);
mResampler.setListener(reusableFloatBuffer ->
{
diff --git a/src/main/java/io/github/dsheirer/sample/buffer/AbstractReusableBufferQueue.java b/src/main/java/io/github/dsheirer/sample/buffer/AbstractReusableBufferQueue.java
index 2f5d38b3..dd07dbab 100644
--- a/src/main/java/io/github/dsheirer/sample/buffer/AbstractReusableBufferQueue.java
+++ b/src/main/java/io/github/dsheirer/sample/buffer/AbstractReusableBufferQueue.java
@@ -1,18 +1,21 @@
-/*******************************************************************************
- * sdr-trunk
- * Copyright (C) 2014-2018 Dennis Sheirer
+/*
+ * *****************************************************************************
+ * Copyright (C) 2014-2022 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.sample.buffer;
import org.slf4j.Logger;
@@ -50,6 +53,14 @@ public abstract class AbstractReusableBufferQueue
- *
- ******************************************************************************/
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see
+ * ****************************************************************************
+ */
package io.github.dsheirer.sample.buffer;
import org.slf4j.Logger;
@@ -61,4 +64,31 @@ public class ReusableComplexBufferQueue extends AbstractReusableBufferQueue