mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-12 08:37:25 -05:00
Updates complex decimation filters to use either scalar or vector optimal implementation. (#1148)
Updates HackRF native byte buffers with automatic DC removal. Creates new half-band channel source to replace the cic-based channel source in the heterodyne channelizer. Resolves issue in RTL native buffer processing. Creates a CACHED thread pool and reduces the number of threads for the SCHEDULED thread pool to 2. Changes each of the channel source implementations to use a dedicated thread (Dispatcher class) instead of the scheduled thread pool, to reduce the amount of buffered/retained samples objects in memory. Lots of other small changes and tweaks. Removes last of Reusable buffer code. Updates DFTProcessor to use a single thread scheduled executor. Updates Polyphase channelizer to use threads instead of an executor. Removes a number of unused classes. Commenting out window calibration. Scalar is faster in both implementations. Adds vector and scalar implementations for windows. Repackages window and window type classes. WIP adds vector implementations for Airspy non-interleaved buffer iterator and calibration plugins for both interleaved and non-interleaved buffer iterators. WIP adds vector implementations for Airspy interleaved buffer iterator. Work on developing scalar and vector implementations of airspy sample conversion and airspy native buffer processing. Introduces new INativeBuffer framework where tuner raw samples are retained in their native (compressed) format until when they're needed and they are converted on-demand into smaller complex sample buffers to aid garbage collection. Removes all usage of ReusableComplexBuffers and ReusableChannelREsultsBuffers. Updates polyphase channelizer to be more efficient in output channel processing. Updates ViterbiDecoder to change Map usage to array for memory efficiency. Updates channel processors to used scalar implementation for gain and mixer, until the polyphase channel results can be fixed to be a multiple of the SIMD lane width. Resolves final changes for the conversion from interleaved sample arrays to complexsamples buffers for distributing baseband samples to channels and processing chains. Updates tuner channel sources and all decoders to receive new complex samples buffers, removing the previous reusable complex buffers as the default baseband samples buffer object. Still to resolve is the ComplexMixer implementations. Needs validation and a calibration implementation with an update to the complex mixer factory. Updates the DMR decoder to use optimal (scalar vs vector) implementations of filter and AGC controls. Updates the AM demodulator for separated I&Q float sample processing. Code review and testing changes. updates build script for runtime configuration. Integrates calibration manager with main sdrtrunk application. Updates NBFMDecoder to use new optimized FIR and Decimation filter options. Updates real half-band decimation filters to select the optimal (scalar vs vector) implementation. Adds calibration manager plugins. Adds calibration manager and plugins to determine the optimal (scalar vs vector) implementation type to use for various classes of dsp components. Adds vector squelching FM demodulator and cleans up the IIR filter and power squelch to use all floats. Adds vector implementation of FM demodulator. This introduces a new interface that accepts i/q samples as two separate float arrays, versus an interleaved array, which enables more efficient processing. This new complex sample interface will be used across the codebase eventually. Adds ComplexOscillator and a SIMD version VectorComplexOscillator. Deprecates the LowPhaseNoiseOscillator because the ComplexOscillator is about 35% faster. The vectorized version is about 43% faster when SIMD lane width is 8x. Adds new vector complex filters and new real oscillators. Note: the RealOscillator using straight Java math sin() function is 3x slower than the existing legacy implementation. Will replace this with a complex oscillator that only uses the real part. Updates ComplexFIRFilter to be 50% more efficient. Adds 23 tap filter implementations. Will not add 63-tap implementations because they are slower than the default vector implementation. Adds 15-tap customized vector half band filters. Adds initial custom complex vector half-band decimation filters for 11-tap and 15-tap decimation filters.. Adds vector complex half band filter implementations. Note: performance of the generic 64-bit 2-lane filter is horrible and is not included. Optimizes complex half-band filter to be 27% more efficient. Implements vectorized real half-band filter implementations. Implements a new FIR filter class that processes sample buffers instead of single sample at a time. Refactored all of the calibratons to be time-duration based. Testing on windows i7 gen 3 CPU showed that it took 1000x longer for one of the calibrations to run, versus my AMD 5800x. Changed gradle jvm args so that the windows plaf export is always applied. Updates gradle build script to ensure incubator module is included and to cleanup javafx library management for building runtime images. Testing tweaks and changes. Adds logging limiter to dispatcher for buffer overflows Adds User Preferences editor for performing Vector/SIMD calibrations and new startup dialog prompting the user to perform calibrations. Updates calibration manager and all calibration implementations to add warm-up and test phases and to reduce the overall time it takes to calibrate a system. Co-authored-by: Dennis Sheirer <dsheirer@github.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2020 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
|
||||
@@ -54,18 +54,15 @@ import io.github.dsheirer.module.decode.event.IDecodeEventProvider;
|
||||
import io.github.dsheirer.module.decode.traffic.TrafficChannelManager;
|
||||
import io.github.dsheirer.module.log.EventLogger;
|
||||
import io.github.dsheirer.record.binary.BinaryRecorder;
|
||||
import io.github.dsheirer.record.wave.ComplexBufferWaveRecorder;
|
||||
import io.github.dsheirer.record.wave.ComplexSamplesWaveRecorder;
|
||||
import io.github.dsheirer.sample.Broadcaster;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.sample.buffer.IReusableBufferListener;
|
||||
import io.github.dsheirer.sample.buffer.IReusableBufferProvider;
|
||||
import io.github.dsheirer.sample.buffer.IReusableByteBufferListener;
|
||||
import io.github.dsheirer.sample.buffer.IReusableByteBufferProvider;
|
||||
import io.github.dsheirer.sample.buffer.IReusableComplexBufferListener;
|
||||
import io.github.dsheirer.sample.buffer.ReusableBufferBroadcaster;
|
||||
import io.github.dsheirer.sample.buffer.ReusableByteBuffer;
|
||||
import io.github.dsheirer.sample.buffer.ReusableComplexBuffer;
|
||||
import io.github.dsheirer.sample.buffer.ReusableFloatBuffer;
|
||||
import io.github.dsheirer.sample.buffer.IByteBufferListener;
|
||||
import io.github.dsheirer.sample.buffer.IByteBufferProvider;
|
||||
import io.github.dsheirer.sample.complex.ComplexSamples;
|
||||
import io.github.dsheirer.sample.complex.IComplexSamplesListener;
|
||||
import io.github.dsheirer.sample.real.IRealBufferListener;
|
||||
import io.github.dsheirer.sample.real.IRealBufferProvider;
|
||||
import io.github.dsheirer.source.ComplexSource;
|
||||
import io.github.dsheirer.source.ISourceEventListener;
|
||||
import io.github.dsheirer.source.ISourceEventProvider;
|
||||
@@ -78,6 +75,7 @@ import io.github.dsheirer.source.heartbeat.IHeartbeatProvider;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
@@ -105,9 +103,9 @@ public class ProcessingChain implements Listener<ChannelEvent>
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger(ProcessingChain.class);
|
||||
|
||||
private ReusableBufferBroadcaster<ReusableFloatBuffer> mDemodulatedAudioBufferBroadcaster = new ReusableBufferBroadcaster();
|
||||
private ReusableBufferBroadcaster<ReusableComplexBuffer> mBasebandComplexBufferBroadcaster = new ReusableBufferBroadcaster();
|
||||
private ReusableBufferBroadcaster<ReusableByteBuffer> mDemodulatedBitstreamBufferBroadcaster = new ReusableBufferBroadcaster();
|
||||
private Broadcaster<float[]> mDemodulatedAudioBufferBroadcaster = new Broadcaster();
|
||||
private Broadcaster<ComplexSamples> mBasebandComplexSamplesBroadcaster = new Broadcaster<>();
|
||||
private Broadcaster<ByteBuffer> mDemodulatedBitstreamBufferBroadcaster = new Broadcaster();
|
||||
private Broadcaster<AudioSegment> mAudioSegmentBroadcaster = new AudioSegmentBroadcaster<>();
|
||||
private Broadcaster<IDecodeEvent> mDecodeEventBroadcaster = new Broadcaster<>();
|
||||
private Broadcaster<ChannelEvent> mChannelEventBroadcaster = new Broadcaster<>();
|
||||
@@ -226,7 +224,7 @@ public class ProcessingChain implements Listener<ChannelEvent>
|
||||
mAudioSegmentBroadcaster.dispose();
|
||||
mDecodeEventBroadcaster.dispose();
|
||||
mChannelEventBroadcaster.dispose();
|
||||
mBasebandComplexBufferBroadcaster.dispose();
|
||||
mBasebandComplexSamplesBroadcaster.dispose();
|
||||
mDemodulatedBitstreamBufferBroadcaster.dispose();
|
||||
mMessageBroadcaster.dispose();
|
||||
mSquelchStateEventBroadcaster.dispose();
|
||||
@@ -391,19 +389,19 @@ public class ProcessingChain implements Listener<ChannelEvent>
|
||||
mMessageBroadcaster.addListener(((IMessageListener)module).getMessageListener());
|
||||
}
|
||||
|
||||
if(module instanceof IReusableBufferListener)
|
||||
if(module instanceof IRealBufferListener)
|
||||
{
|
||||
mDemodulatedAudioBufferBroadcaster.addListener(((IReusableBufferListener)module).getReusableBufferListener());
|
||||
mDemodulatedAudioBufferBroadcaster.addListener(((IRealBufferListener)module).getBufferListener());
|
||||
}
|
||||
|
||||
if(module instanceof IReusableByteBufferListener)
|
||||
if(module instanceof IByteBufferListener)
|
||||
{
|
||||
mDemodulatedBitstreamBufferBroadcaster.addListener(((IReusableByteBufferListener)module).getReusableByteBufferListener());
|
||||
mDemodulatedBitstreamBufferBroadcaster.addListener(((IByteBufferListener)module).getByteBufferListener());
|
||||
}
|
||||
|
||||
if(module instanceof IReusableComplexBufferListener)
|
||||
if(module instanceof IComplexSamplesListener)
|
||||
{
|
||||
mBasebandComplexBufferBroadcaster.addListener(((IReusableComplexBufferListener)module).getReusableComplexBufferListener());
|
||||
mBasebandComplexSamplesBroadcaster.addListener(((IComplexSamplesListener)module).getComplexSamplesListener());
|
||||
}
|
||||
|
||||
if(module instanceof ISourceEventListener)
|
||||
@@ -458,19 +456,19 @@ public class ProcessingChain implements Listener<ChannelEvent>
|
||||
mHeartbeatBroadcaster.removeListener(((IHeartbeatListener)module).getHeartbeatListener());
|
||||
}
|
||||
|
||||
if(module instanceof IReusableBufferListener)
|
||||
if(module instanceof IRealBufferListener)
|
||||
{
|
||||
mDemodulatedAudioBufferBroadcaster.removeListener(((IReusableBufferListener)module).getReusableBufferListener());
|
||||
mDemodulatedAudioBufferBroadcaster.removeListener(((IRealBufferListener)module).getBufferListener());
|
||||
}
|
||||
|
||||
if(module instanceof IReusableByteBufferListener)
|
||||
if(module instanceof IByteBufferListener)
|
||||
{
|
||||
mDemodulatedBitstreamBufferBroadcaster.removeListener(((IReusableByteBufferListener)module).getReusableByteBufferListener());
|
||||
mDemodulatedBitstreamBufferBroadcaster.removeListener(((IByteBufferListener)module).getByteBufferListener());
|
||||
}
|
||||
|
||||
if(module instanceof IReusableComplexBufferListener)
|
||||
if(module instanceof IComplexSamplesListener)
|
||||
{
|
||||
mBasebandComplexBufferBroadcaster.removeListener(((IReusableComplexBufferListener)module).getReusableComplexBufferListener());
|
||||
mBasebandComplexSamplesBroadcaster.removeListener(((IComplexSamplesListener)module).getComplexSamplesListener());
|
||||
}
|
||||
|
||||
if(module instanceof ISourceEventListener)
|
||||
@@ -535,14 +533,14 @@ public class ProcessingChain implements Listener<ChannelEvent>
|
||||
((IMessageProvider)module).setMessageListener(mMessageBroadcaster);
|
||||
}
|
||||
|
||||
if(module instanceof IReusableByteBufferProvider)
|
||||
if(module instanceof IByteBufferProvider)
|
||||
{
|
||||
((IReusableByteBufferProvider)module).setBufferListener(mDemodulatedBitstreamBufferBroadcaster);
|
||||
((IByteBufferProvider)module).setBufferListener(mDemodulatedBitstreamBufferBroadcaster);
|
||||
}
|
||||
|
||||
if(module instanceof IReusableBufferProvider)
|
||||
if(module instanceof IRealBufferProvider)
|
||||
{
|
||||
((IReusableBufferProvider)module).setBufferListener(mDemodulatedAudioBufferBroadcaster);
|
||||
((IRealBufferProvider)module).setBufferListener(mDemodulatedAudioBufferBroadcaster);
|
||||
}
|
||||
|
||||
if(module instanceof ISourceEventProvider)
|
||||
@@ -572,9 +570,9 @@ public class ProcessingChain implements Listener<ChannelEvent>
|
||||
((IAudioSegmentProvider)module).setAudioSegmentListener(null);
|
||||
}
|
||||
|
||||
if(module instanceof IReusableByteBufferProvider)
|
||||
if(module instanceof IByteBufferProvider)
|
||||
{
|
||||
((IReusableByteBufferProvider)module).removeBufferListener(mDemodulatedBitstreamBufferBroadcaster);
|
||||
((IByteBufferProvider)module).removeBufferListener(mDemodulatedBitstreamBufferBroadcaster);
|
||||
}
|
||||
|
||||
if(module instanceof IDecodeEventProvider)
|
||||
@@ -607,9 +605,9 @@ public class ProcessingChain implements Listener<ChannelEvent>
|
||||
((IMessageProvider)module).setMessageListener(null);
|
||||
}
|
||||
|
||||
if(module instanceof IReusableBufferProvider)
|
||||
if(module instanceof IRealBufferProvider)
|
||||
{
|
||||
((IReusableBufferProvider)module).setBufferListener(null);
|
||||
((IRealBufferProvider)module).setBufferListener(null);
|
||||
}
|
||||
|
||||
if(module instanceof ISourceEventProvider)
|
||||
@@ -648,7 +646,7 @@ public class ProcessingChain implements Listener<ChannelEvent>
|
||||
switch(mSource.getSampleType())
|
||||
{
|
||||
case COMPLEX:
|
||||
((ComplexSource)mSource).setListener(mBasebandComplexBufferBroadcaster);
|
||||
((ComplexSource)mSource).setListener(mBasebandComplexSamplesBroadcaster);
|
||||
break;
|
||||
case REAL:
|
||||
((RealSource)mSource).setListener(mDemodulatedAudioBufferBroadcaster);
|
||||
@@ -696,10 +694,10 @@ public class ProcessingChain implements Listener<ChannelEvent>
|
||||
switch(mSource.getSampleType())
|
||||
{
|
||||
case COMPLEX:
|
||||
((ComplexSource)mSource).removeListener(mBasebandComplexBufferBroadcaster);
|
||||
((ComplexSource)mSource).setListener(null);
|
||||
break;
|
||||
case REAL:
|
||||
((RealSource)mSource).removeListener(mDemodulatedAudioBufferBroadcaster);
|
||||
((RealSource)mSource).setListener(null);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unrecognized source sample type - cannot start processing " +
|
||||
@@ -753,7 +751,7 @@ public class ProcessingChain implements Listener<ChannelEvent>
|
||||
|
||||
for(Module module : mModules)
|
||||
{
|
||||
if(module instanceof ComplexBufferWaveRecorder)
|
||||
if(module instanceof ComplexSamplesWaveRecorder)
|
||||
{
|
||||
recordingModules.add(module);
|
||||
}
|
||||
@@ -882,7 +880,7 @@ public class ProcessingChain implements Listener<ChannelEvent>
|
||||
/**
|
||||
* Adds listener to receive demodulated audio buffers from an modules that produce demodulated audio.
|
||||
*/
|
||||
public void addDemodulatedAudioListener(Listener<ReusableFloatBuffer> listener)
|
||||
public void addDemodulatedAudioListener(Listener<float[]> listener)
|
||||
{
|
||||
mDemodulatedAudioBufferBroadcaster.addListener(listener);
|
||||
}
|
||||
@@ -890,7 +888,7 @@ public class ProcessingChain implements Listener<ChannelEvent>
|
||||
/**
|
||||
* Removes the listener from receiving demodulated audio buffers.
|
||||
*/
|
||||
public void removeDemodulatedAudioListener(Listener<ReusableFloatBuffer> listener)
|
||||
public void removeDemodulatedAudioListener(Listener<float[]> listener)
|
||||
{
|
||||
mDemodulatedAudioBufferBroadcaster.removeListener(listener);
|
||||
}
|
||||
|
||||
@@ -1,25 +1,27 @@
|
||||
/*******************************************************************************
|
||||
* 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 <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.module.decode.afsk;
|
||||
|
||||
import io.github.dsheirer.dsp.afsk.AFSK1200Decoder;
|
||||
import io.github.dsheirer.module.decode.Decoder;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.sample.buffer.IReusableBufferListener;
|
||||
import io.github.dsheirer.sample.buffer.ReusableFloatBuffer;
|
||||
import io.github.dsheirer.sample.real.IRealBufferListener;
|
||||
|
||||
/**
|
||||
* Abstract class for Audio Frequency Shift Keying (AFSK) 1200-baud decoder based decoder modules.
|
||||
@@ -28,7 +30,7 @@ import io.github.dsheirer.sample.buffer.ReusableFloatBuffer;
|
||||
*
|
||||
* Sub-class implementations should invoke getDecoder().setListener() to receive decoded symbol stream.
|
||||
*/
|
||||
public abstract class AbstractAFSKDecoder extends Decoder implements IReusableBufferListener, Listener<ReusableFloatBuffer>
|
||||
public abstract class AbstractAFSKDecoder extends Decoder implements IRealBufferListener, Listener<float[]>
|
||||
{
|
||||
private AFSK1200Decoder mAFSK1200Decoder;
|
||||
|
||||
@@ -48,13 +50,13 @@ public abstract class AbstractAFSKDecoder extends Decoder implements IReusableBu
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive(ReusableFloatBuffer reusableFloatBuffer)
|
||||
public void receive(float[] realBuffer)
|
||||
{
|
||||
mAFSK1200Decoder.receive(reusableFloatBuffer);
|
||||
mAFSK1200Decoder.receive(realBuffer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Listener<ReusableFloatBuffer> getReusableBufferListener()
|
||||
public Listener<float[]> getBufferListener()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -21,8 +21,9 @@ package io.github.dsheirer.module.decode.dmr;
|
||||
|
||||
import io.github.dsheirer.dsp.filter.FilterFactory;
|
||||
import io.github.dsheirer.dsp.filter.fir.FIRFilterSpecification;
|
||||
import io.github.dsheirer.dsp.filter.fir.complex.ComplexFIRFilter2;
|
||||
import io.github.dsheirer.dsp.gain.ComplexFeedForwardGainControl;
|
||||
import io.github.dsheirer.dsp.filter.fir.real.IRealFilter;
|
||||
import io.github.dsheirer.dsp.gain.complex.ComplexGainFactory;
|
||||
import io.github.dsheirer.dsp.gain.complex.IComplexGainControl;
|
||||
import io.github.dsheirer.dsp.psk.DQPSKDecisionDirectedDemodulator;
|
||||
import io.github.dsheirer.dsp.psk.InterpolatingSampleBuffer;
|
||||
import io.github.dsheirer.dsp.psk.pll.CostasLoop;
|
||||
@@ -35,16 +36,16 @@ import io.github.dsheirer.module.decode.DecoderType;
|
||||
import io.github.dsheirer.module.decode.FeedbackDecoder;
|
||||
import io.github.dsheirer.sample.Broadcaster;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.sample.buffer.IReusableByteBufferProvider;
|
||||
import io.github.dsheirer.sample.buffer.IReusableComplexBufferListener;
|
||||
import io.github.dsheirer.sample.buffer.ReusableByteBuffer;
|
||||
import io.github.dsheirer.sample.buffer.ReusableComplexBuffer;
|
||||
import io.github.dsheirer.sample.buffer.IByteBufferProvider;
|
||||
import io.github.dsheirer.sample.complex.ComplexSamples;
|
||||
import io.github.dsheirer.sample.complex.IComplexSamplesListener;
|
||||
import io.github.dsheirer.source.ISourceEventListener;
|
||||
import io.github.dsheirer.source.ISourceEventProvider;
|
||||
import io.github.dsheirer.source.SourceEvent;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -52,7 +53,7 @@ import java.util.Map;
|
||||
* DMR decoder module.
|
||||
*/
|
||||
public class DMRDecoder extends FeedbackDecoder implements ISourceEventListener, ISourceEventProvider,
|
||||
IReusableComplexBufferListener, Listener<ReusableComplexBuffer>, IReusableByteBufferProvider
|
||||
IComplexSamplesListener, Listener<ComplexSamples>, IByteBufferProvider
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger(DMRDecoder.class);
|
||||
protected static final float SAMPLE_COUNTER_GAIN = 0.4f;
|
||||
@@ -61,15 +62,17 @@ public class DMRDecoder extends FeedbackDecoder implements ISourceEventListener,
|
||||
private Broadcaster<Dibit> mDibitBroadcaster = new Broadcaster<>();
|
||||
private DibitToByteBufferAssembler mByteBufferAssembler = new DibitToByteBufferAssembler(300);
|
||||
private DMRMessageProcessor mMessageProcessor;
|
||||
private ComplexFeedForwardGainControl mAGC = new ComplexFeedForwardGainControl(32);
|
||||
protected IComplexGainControl mAGC = ComplexGainFactory.getComplexGainControl();
|
||||
private Map<Double,float[]> mBasebandFilters = new HashMap<>();
|
||||
private ComplexFIRFilter2 mBasebandFilter;
|
||||
protected IRealFilter mIBasebandFilter;
|
||||
protected IRealFilter mQBasebandFilter;
|
||||
|
||||
protected InterpolatingSampleBuffer mInterpolatingSampleBuffer;
|
||||
protected DQPSKDecisionDirectedDemodulator mQPSKDemodulator;
|
||||
protected CostasLoop mCostasLoop;
|
||||
protected FrequencyCorrectionSyncMonitor mFrequencyCorrectionSyncMonitor;
|
||||
protected DMRMessageFramer mMessageFramer;
|
||||
private PowerMonitor mPowerMonitor = new PowerMonitor();
|
||||
protected PowerMonitor mPowerMonitor = new PowerMonitor();
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
@@ -101,7 +104,7 @@ public class DMRDecoder extends FeedbackDecoder implements ISourceEventListener,
|
||||
|
||||
/**
|
||||
* Sets the sample rate and configures internal decoder components.
|
||||
* @param sampleRate
|
||||
* @param sampleRate of the channel to decode
|
||||
*/
|
||||
public void setSampleRate(double sampleRate)
|
||||
{
|
||||
@@ -113,7 +116,8 @@ public class DMRDecoder extends FeedbackDecoder implements ISourceEventListener,
|
||||
|
||||
mPowerMonitor.setSampleRate((int)sampleRate);
|
||||
mSampleRate = sampleRate;
|
||||
mBasebandFilter = new ComplexFIRFilter2(getBasebandFilter());
|
||||
mIBasebandFilter = FilterFactory.getRealFilter(getBasebandFilter());
|
||||
mQBasebandFilter = FilterFactory.getRealFilter(getBasebandFilter());
|
||||
mCostasLoop = new CostasLoop(getSampleRate(), getSymbolRate());
|
||||
mCostasLoop.setPLLBandwidth(PLLBandwidth.BW_300);
|
||||
mFrequencyCorrectionSyncMonitor = new FrequencyCorrectionSyncMonitor(mCostasLoop, this);
|
||||
@@ -138,35 +142,21 @@ public class DMRDecoder extends FeedbackDecoder implements ISourceEventListener,
|
||||
|
||||
/**
|
||||
* Primary method for processing incoming complex sample buffers
|
||||
* @param reusableComplexBuffer containing channelized complex samples
|
||||
* @param samples containing channelized complex samples
|
||||
*/
|
||||
@Override
|
||||
public void receive(ReusableComplexBuffer reusableComplexBuffer)
|
||||
public void receive(ComplexSamples samples)
|
||||
{
|
||||
//User accounting of the incoming buffer is handled by the filter
|
||||
ReusableComplexBuffer basebandFiltered = filter(reusableComplexBuffer);
|
||||
mMessageFramer.setCurrentTime(System.currentTimeMillis());
|
||||
|
||||
float[] i = mIBasebandFilter.filter(samples.i());
|
||||
float[] q = mQBasebandFilter.filter(samples.q());
|
||||
|
||||
//Process buffer for power measurements
|
||||
mPowerMonitor.process(basebandFiltered);
|
||||
mPowerMonitor.process(i, q);
|
||||
|
||||
//User accounting of the incoming buffer is handled by the gain filter
|
||||
ReusableComplexBuffer gainApplied = mAGC.filter(basebandFiltered);
|
||||
|
||||
mMessageFramer.setCurrentTime(reusableComplexBuffer.getTimestamp());
|
||||
|
||||
//User accounting of the filtered buffer is handled by the demodulator
|
||||
mQPSKDemodulator.receive(gainApplied);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the complex buffer and returns a new reusable complex buffer with the filtered contents.
|
||||
* @param reusableComplexBuffer to filter
|
||||
* @return filtered complex buffer
|
||||
*/
|
||||
protected ReusableComplexBuffer filter(ReusableComplexBuffer reusableComplexBuffer)
|
||||
{
|
||||
//User accounting of the incoming buffer is handled by the filter
|
||||
return mBasebandFilter.filter(reusableComplexBuffer);
|
||||
ComplexSamples amplified = mAGC.process(i, q);
|
||||
mQPSKDemodulator.receive(amplified);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -251,7 +241,7 @@ public class DMRDecoder extends FeedbackDecoder implements ISourceEventListener,
|
||||
* Implements the IByteBufferProvider interface - delegates to the byte buffer assembler
|
||||
*/
|
||||
@Override
|
||||
public void setBufferListener(Listener<ReusableByteBuffer> listener)
|
||||
public void setBufferListener(Listener<ByteBuffer> listener)
|
||||
{
|
||||
mByteBufferAssembler.setBufferListener(listener);
|
||||
}
|
||||
@@ -260,7 +250,7 @@ public class DMRDecoder extends FeedbackDecoder implements ISourceEventListener,
|
||||
* Implements the IByteBufferProvider interface - delegates to the byte buffer assembler
|
||||
*/
|
||||
@Override
|
||||
public void removeBufferListener(Listener<ReusableByteBuffer> listener)
|
||||
public void removeBufferListener(Listener<ByteBuffer> listener)
|
||||
{
|
||||
mByteBufferAssembler.removeBufferListener(listener);
|
||||
}
|
||||
@@ -318,21 +308,14 @@ public class DMRDecoder extends FeedbackDecoder implements ISourceEventListener,
|
||||
@Override
|
||||
public Listener<SourceEvent> getSourceEventListener()
|
||||
{
|
||||
return new Listener<SourceEvent>()
|
||||
{
|
||||
@Override
|
||||
public void receive(SourceEvent sourceEvent)
|
||||
{
|
||||
process(sourceEvent);
|
||||
}
|
||||
};
|
||||
return sourceEvent -> process(sourceEvent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Listener interface to receive reusable complex buffers
|
||||
*/
|
||||
@Override
|
||||
public Listener<ReusableComplexBuffer> getReusableComplexBufferListener()
|
||||
public Listener<ComplexSamples> getComplexSamplesListener()
|
||||
{
|
||||
return DMRDecoder.this;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2019 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 <http://www.gnu.org/licenses/>
|
||||
* *****************************************************************************
|
||||
* ****************************************************************************
|
||||
*/
|
||||
package io.github.dsheirer.module.decode.dmr;
|
||||
|
||||
@@ -23,8 +22,8 @@ import io.github.dsheirer.dsp.psk.DQPSKDecisionDirectedDemodulatorInstrumented;
|
||||
import io.github.dsheirer.dsp.psk.InterpolatingSampleBufferInstrumented;
|
||||
import io.github.dsheirer.dsp.psk.SymbolDecisionData;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.sample.buffer.ReusableComplexBuffer;
|
||||
import io.github.dsheirer.sample.complex.Complex;
|
||||
import io.github.dsheirer.sample.complex.ComplexSamples;
|
||||
|
||||
/**
|
||||
* Instrumented version of DMR decoder
|
||||
@@ -35,7 +34,7 @@ public class DMRDecoderInstrumented extends DMRDecoder
|
||||
private Listener<Double> mPLLFrequencyListener;
|
||||
private Listener<Double> mSamplesPerSymbolListener;
|
||||
private Listener<Complex> mComplexSymbolListener;
|
||||
private Listener<ReusableComplexBuffer> mFilteredSymbolListener;
|
||||
private Listener<ComplexSamples> mFilteredSymbolListener;
|
||||
private Listener<SymbolDecisionData> mSymbolDecisionDataListener;
|
||||
|
||||
/**
|
||||
@@ -48,19 +47,27 @@ public class DMRDecoderInstrumented extends DMRDecoder
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides the filter method so that we can capture the filtered samples for instrumentation
|
||||
* Primary method for processing incoming complex sample buffers
|
||||
* @param complexSamples containing channelized complex samples
|
||||
*/
|
||||
protected ReusableComplexBuffer filter(ReusableComplexBuffer reusableComplexBuffer)
|
||||
@Override
|
||||
public void receive(ComplexSamples samples)
|
||||
{
|
||||
ReusableComplexBuffer filtered = super.filter(reusableComplexBuffer);
|
||||
mMessageFramer.setCurrentTime(System.currentTimeMillis());
|
||||
|
||||
float[] i = mIBasebandFilter.filter(samples.i());
|
||||
float[] q = mQBasebandFilter.filter(samples.q());
|
||||
|
||||
if(mFilteredSymbolListener != null)
|
||||
{
|
||||
filtered.incrementUserCount();
|
||||
mFilteredSymbolListener.receive(filtered);
|
||||
mFilteredSymbolListener.receive(new ComplexSamples(i, q));
|
||||
}
|
||||
|
||||
return filtered;
|
||||
//Process buffer for power measurements
|
||||
mPowerMonitor.process(i, q);
|
||||
|
||||
ComplexSamples amplified = mAGC.process(i, q);
|
||||
mQPSKDemodulator.receive(amplified);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -104,7 +111,7 @@ public class DMRDecoderInstrumented extends DMRDecoder
|
||||
((DQPSKDecisionDirectedDemodulatorInstrumented)mQPSKDemodulator).setPLLFrequencyListener(listener);
|
||||
}
|
||||
|
||||
public void setFilteredBufferListener(Listener<ReusableComplexBuffer> listener)
|
||||
public void setFilteredBufferListener(Listener<ComplexSamples> listener)
|
||||
{
|
||||
mFilteredSymbolListener = listener;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -38,11 +38,11 @@ import io.github.dsheirer.protocol.Protocol;
|
||||
import io.github.dsheirer.record.binary.BinaryReader;
|
||||
import io.github.dsheirer.sample.Broadcaster;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.sample.buffer.ReusableByteBuffer;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.file.DirectoryStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
@@ -173,20 +173,15 @@ public class DMRMessageFramer implements Listener<Dibit>, IDMRBurstDetectListene
|
||||
*
|
||||
* @param buffer to process into a stream of dibits for processing.
|
||||
*/
|
||||
public void receive(ReusableByteBuffer buffer)
|
||||
public void receive(ByteBuffer buffer)
|
||||
{
|
||||
//Updates current timestamp to the timestamp from the incoming buffer
|
||||
setCurrentTime(buffer.getTimestamp());
|
||||
|
||||
for(byte value : buffer.getBytes())
|
||||
for(byte value : buffer.array())
|
||||
{
|
||||
for(int x = 0; x <= 3; x++)
|
||||
{
|
||||
receive(Dibit.parse(value, x));
|
||||
}
|
||||
}
|
||||
|
||||
buffer.decrementUserCount();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -469,7 +464,7 @@ public class DMRMessageFramer implements Listener<Dibit>, IDMRBurstDetectListene
|
||||
{
|
||||
while(reader.hasNext())
|
||||
{
|
||||
ReusableByteBuffer buffer = reader.next();
|
||||
ByteBuffer buffer = reader.next();
|
||||
messageFramer.receive(buffer);
|
||||
}
|
||||
}
|
||||
@@ -517,7 +512,7 @@ public class DMRMessageFramer implements Listener<Dibit>, IDMRBurstDetectListene
|
||||
{
|
||||
while(reader.hasNext())
|
||||
{
|
||||
ReusableByteBuffer buffer = reader.next();
|
||||
ByteBuffer buffer = reader.next();
|
||||
messageFramer.receive(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +1,25 @@
|
||||
/*******************************************************************************
|
||||
* 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 <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.module.decode.fleetsync2;
|
||||
|
||||
import io.github.dsheirer.dsp.afsk.AFSK1200DecoderInstrumented;
|
||||
import io.github.dsheirer.gui.instrument.chart.IInstrumentedAFSK1200Decoder;
|
||||
import io.github.dsheirer.sample.buffer.ReusableFloatBuffer;
|
||||
import javafx.beans.property.SimpleIntegerProperty;
|
||||
|
||||
/**
|
||||
@@ -44,9 +46,9 @@ public class Fleetsync2DecoderInstrumented extends Fleetsync2Decoder implements
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive(ReusableFloatBuffer reusableFloatBuffer)
|
||||
public void receive(float[] realBuffer)
|
||||
{
|
||||
super.receive(reusableFloatBuffer);
|
||||
super.receive(realBuffer);
|
||||
|
||||
bufferCount.setValue(bufferCount.intValue() + 1);
|
||||
}
|
||||
|
||||
@@ -1,23 +1,25 @@
|
||||
/*******************************************************************************
|
||||
* 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 <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.module.decode.lj1200;
|
||||
|
||||
import io.github.dsheirer.dsp.afsk.AFSK1200DecoderInstrumented;
|
||||
import io.github.dsheirer.gui.instrument.chart.IInstrumentedAFSK1200Decoder;
|
||||
import io.github.dsheirer.sample.buffer.ReusableFloatBuffer;
|
||||
import javafx.beans.property.SimpleIntegerProperty;
|
||||
|
||||
/**
|
||||
@@ -44,9 +46,9 @@ public class LJ1200DecoderInstrumented extends LJ1200Decoder implements IInstrum
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive(ReusableFloatBuffer reusableFloatBuffer)
|
||||
public void receive(float[] realBuffer)
|
||||
{
|
||||
super.receive(reusableFloatBuffer);
|
||||
super.receive(realBuffer);
|
||||
|
||||
bufferCount.setValue(bufferCount.intValue() + 1);
|
||||
}
|
||||
|
||||
@@ -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 <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.module.decode.ltrnet;
|
||||
|
||||
import io.github.dsheirer.bits.MessageFramer;
|
||||
@@ -22,10 +25,9 @@ import io.github.dsheirer.message.MessageDirection;
|
||||
import io.github.dsheirer.module.decode.Decoder;
|
||||
import io.github.dsheirer.module.decode.DecoderType;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.sample.buffer.IReusableBufferListener;
|
||||
import io.github.dsheirer.sample.buffer.ReusableFloatBuffer;
|
||||
import io.github.dsheirer.sample.real.IRealBufferListener;
|
||||
|
||||
public class LTRNetDecoder extends Decoder implements IReusableBufferListener, Listener<ReusableFloatBuffer>
|
||||
public class LTRNetDecoder extends Decoder implements IRealBufferListener, Listener<float[]>
|
||||
{
|
||||
public static final int LTR_NET_MESSAGE_LENGTH = 40;
|
||||
protected LTRDecoder mLTRDecoder;
|
||||
@@ -72,13 +74,13 @@ public class LTRNetDecoder extends Decoder implements IReusableBufferListener, L
|
||||
}
|
||||
|
||||
@Override
|
||||
public Listener<ReusableFloatBuffer> getReusableBufferListener()
|
||||
public Listener<float[]> getBufferListener()
|
||||
{
|
||||
return mLTRDecoder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive(ReusableFloatBuffer reusableFloatBuffer)
|
||||
public void receive(float[] reusableFloatBuffer)
|
||||
{
|
||||
mLTRDecoder.receive(reusableFloatBuffer);
|
||||
}
|
||||
|
||||
@@ -1,22 +1,24 @@
|
||||
/*******************************************************************************
|
||||
* 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 <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.module.decode.ltrnet;
|
||||
|
||||
import io.github.dsheirer.dsp.fsk.LTRDecoderInstrumented;
|
||||
import io.github.dsheirer.sample.buffer.ReusableFloatBuffer;
|
||||
import javafx.beans.property.SimpleIntegerProperty;
|
||||
|
||||
public class LTRNetDecoderInstrumented extends LTRNetDecoder
|
||||
@@ -34,9 +36,9 @@ public class LTRNetDecoderInstrumented extends LTRNetDecoder
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive(ReusableFloatBuffer reusableFloatBuffer)
|
||||
public void receive(float[] realBuffer)
|
||||
{
|
||||
super.receive(reusableFloatBuffer);
|
||||
super.receive(realBuffer);
|
||||
|
||||
bufferCount.setValue(bufferCount.intValue() + 1);
|
||||
}
|
||||
|
||||
@@ -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 <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.module.decode.ltrstandard;
|
||||
|
||||
import io.github.dsheirer.alias.AliasList;
|
||||
@@ -23,10 +26,9 @@ import io.github.dsheirer.message.MessageDirection;
|
||||
import io.github.dsheirer.module.decode.Decoder;
|
||||
import io.github.dsheirer.module.decode.DecoderType;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.sample.buffer.IReusableBufferListener;
|
||||
import io.github.dsheirer.sample.buffer.ReusableFloatBuffer;
|
||||
import io.github.dsheirer.sample.real.IRealBufferListener;
|
||||
|
||||
public class LTRStandardDecoder extends Decoder implements IReusableBufferListener, Listener<ReusableFloatBuffer>
|
||||
public class LTRStandardDecoder extends Decoder implements IRealBufferListener, Listener<float[]>
|
||||
{
|
||||
public static final int LTR_STANDARD_MESSAGE_LENGTH = 40;
|
||||
|
||||
@@ -65,15 +67,15 @@ public class LTRStandardDecoder extends Decoder implements IReusableBufferListen
|
||||
}
|
||||
|
||||
@Override
|
||||
public Listener<ReusableFloatBuffer> getReusableBufferListener()
|
||||
public Listener<float[]> getBufferListener()
|
||||
{
|
||||
return mLTRDecoder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive(ReusableFloatBuffer reusableFloatBuffer)
|
||||
public void receive(float[] realBuffer)
|
||||
{
|
||||
mLTRDecoder.receive(reusableFloatBuffer);
|
||||
mLTRDecoder.receive(realBuffer);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -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,13 +14,12 @@
|
||||
*
|
||||
* 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.module.decode.mdc1200;
|
||||
|
||||
import io.github.dsheirer.dsp.afsk.AFSK1200DecoderInstrumented;
|
||||
import io.github.dsheirer.gui.instrument.chart.IInstrumentedAFSK1200Decoder;
|
||||
import io.github.dsheirer.sample.buffer.ReusableFloatBuffer;
|
||||
import javafx.beans.property.SimpleIntegerProperty;
|
||||
|
||||
/**
|
||||
@@ -48,9 +46,9 @@ public class MDCDecoderInstrumented extends MDCDecoder implements IInstrumentedA
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive(ReusableFloatBuffer reusableFloatBuffer)
|
||||
public void receive(float[] realBuffer)
|
||||
{
|
||||
super.receive(reusableFloatBuffer);
|
||||
super.receive(realBuffer);
|
||||
|
||||
bufferCount.setValue(bufferCount.intValue() + 1);
|
||||
}
|
||||
|
||||
@@ -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 <http://www.gnu.org/licenses/>
|
||||
* *****************************************************************************
|
||||
* ****************************************************************************
|
||||
*/
|
||||
package io.github.dsheirer.module.decode.mpt1327;
|
||||
|
||||
@@ -26,15 +25,16 @@ import io.github.dsheirer.dsp.symbol.BinaryToByteBufferAssembler;
|
||||
import io.github.dsheirer.module.decode.DecoderType;
|
||||
import io.github.dsheirer.module.decode.afsk.AbstractAFSKDecoder;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.sample.buffer.IReusableByteBufferProvider;
|
||||
import io.github.dsheirer.sample.buffer.ReusableByteBuffer;
|
||||
import io.github.dsheirer.sample.buffer.IByteBufferProvider;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* Instrumented version of the MPT1327 decoder. Exposes properties for instrumented manual decoding of a signal.
|
||||
*/
|
||||
public class MPT1327Decoder extends AbstractAFSKDecoder implements IBinarySymbolProcessor, IReusableByteBufferProvider
|
||||
public class MPT1327Decoder extends AbstractAFSKDecoder implements IBinarySymbolProcessor, IByteBufferProvider
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger(MPT1327Decoder.class);
|
||||
|
||||
@@ -114,13 +114,13 @@ public class MPT1327Decoder extends AbstractAFSKDecoder implements IBinarySymbol
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBufferListener(Listener<ReusableByteBuffer> listener)
|
||||
public void setBufferListener(Listener<ByteBuffer> listener)
|
||||
{
|
||||
mBinaryToByteBufferAssembler.setBufferListener(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeBufferListener(Listener<ReusableByteBuffer> listener)
|
||||
public void removeBufferListener(Listener<ByteBuffer> listener)
|
||||
{
|
||||
mBinaryToByteBufferAssembler.removeBufferListener(listener);
|
||||
}
|
||||
|
||||
@@ -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,13 +14,12 @@
|
||||
*
|
||||
* 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.module.decode.mpt1327;
|
||||
|
||||
import io.github.dsheirer.dsp.afsk.AFSK1200DecoderInstrumented;
|
||||
import io.github.dsheirer.gui.instrument.chart.IInstrumentedAFSK1200Decoder;
|
||||
import io.github.dsheirer.sample.buffer.ReusableFloatBuffer;
|
||||
import javafx.beans.property.SimpleIntegerProperty;
|
||||
|
||||
/**
|
||||
@@ -48,9 +46,9 @@ public class MPT1327DecoderInstrumented extends MPT1327Decoder implements IInstr
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive(ReusableFloatBuffer reusableFloatBuffer)
|
||||
public void receive(float[] realBuffer)
|
||||
{
|
||||
super.receive(reusableFloatBuffer);
|
||||
super.receive(realBuffer);
|
||||
|
||||
bufferCount.setValue(bufferCount.intValue() + 1);
|
||||
}
|
||||
|
||||
@@ -22,21 +22,21 @@ import io.github.dsheirer.channel.state.DecoderStateEvent;
|
||||
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.decimate.IRealDecimationFilter;
|
||||
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;
|
||||
import io.github.dsheirer.dsp.filter.fir.real.IRealFilter;
|
||||
import io.github.dsheirer.dsp.filter.resample.RealResampler;
|
||||
import io.github.dsheirer.dsp.fm.SquelchingFMDemodulator;
|
||||
import io.github.dsheirer.dsp.fm.FmDemodulatorFactory;
|
||||
import io.github.dsheirer.dsp.fm.ISquelchingFmDemodulator;
|
||||
import io.github.dsheirer.dsp.window.WindowType;
|
||||
import io.github.dsheirer.module.decode.DecoderType;
|
||||
import io.github.dsheirer.module.decode.PrimaryDecoder;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.sample.buffer.IReusableBufferProvider;
|
||||
import io.github.dsheirer.sample.buffer.IReusableComplexBufferListener;
|
||||
import io.github.dsheirer.sample.buffer.ReusableComplexBuffer;
|
||||
import io.github.dsheirer.sample.buffer.ReusableFloatBuffer;
|
||||
import io.github.dsheirer.sample.complex.ComplexSamples;
|
||||
import io.github.dsheirer.sample.complex.IComplexSamplesListener;
|
||||
import io.github.dsheirer.sample.real.IRealBufferProvider;
|
||||
import io.github.dsheirer.source.ISourceEventListener;
|
||||
import io.github.dsheirer.source.ISourceEventProvider;
|
||||
import io.github.dsheirer.source.SourceEvent;
|
||||
@@ -47,22 +47,23 @@ import org.slf4j.LoggerFactory;
|
||||
* Decoder module with integrated narrowband FM (12.5 or 25.0 kHz channel) demodulator
|
||||
*/
|
||||
public class NBFMDecoder extends PrimaryDecoder implements ISourceEventListener, ISourceEventProvider,
|
||||
IReusableComplexBufferListener, Listener<ReusableComplexBuffer>, IReusableBufferProvider,
|
||||
IComplexSamplesListener, Listener<ComplexSamples>, IRealBufferProvider,
|
||||
IDecoderStateEventProvider
|
||||
{
|
||||
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.0004;
|
||||
private static final double POWER_SQUELCH_THRESHOLD_DB = -78.0;
|
||||
private static final float POWER_SQUELCH_ALPHA_DECAY = 0.0004f;
|
||||
private static final float POWER_SQUELCH_THRESHOLD_DB = -78.0f;
|
||||
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 IRealFilter mIBasebandFilter;
|
||||
private IRealFilter mQBasebandFilter;
|
||||
private IRealDecimationFilter mIDecimationFilter;
|
||||
private IRealDecimationFilter mQDecimationFilter;
|
||||
private ISquelchingFmDemodulator mDemodulator;
|
||||
private RealResampler mResampler;
|
||||
private SourceEventProcessor mSourceEventProcessor = new SourceEventProcessor();
|
||||
private Listener<ReusableFloatBuffer> mResampledReusableBufferListener;
|
||||
private Listener<float[]> mResampledBufferListener;
|
||||
private Listener<DecoderStateEvent> mDecoderStateEventListener;
|
||||
private double mChannelBandwidth;
|
||||
private double mOutputSampleRate = DEMODULATED_AUDIO_SAMPLE_RATE;
|
||||
@@ -76,6 +77,8 @@ public class NBFMDecoder extends PrimaryDecoder implements ISourceEventListener,
|
||||
{
|
||||
super( config );
|
||||
mChannelBandwidth = config.getBandwidth().getValue();
|
||||
mDemodulator = FmDemodulatorFactory.getSquelchingFmDemodulator(POWER_SQUELCH_ALPHA_DECAY,
|
||||
POWER_SQUELCH_THRESHOLD_DB, POWER_SQUELCH_RAMP);
|
||||
mDemodulator.setSquelchThreshold(config.getSquelchThreshold());
|
||||
}
|
||||
|
||||
@@ -86,7 +89,7 @@ public class NBFMDecoder extends PrimaryDecoder implements ISourceEventListener,
|
||||
}
|
||||
|
||||
@Override
|
||||
public Listener<ReusableComplexBuffer> getReusableComplexBufferListener()
|
||||
public Listener<ComplexSamples> getComplexSamplesListener()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
@@ -114,31 +117,33 @@ public class NBFMDecoder extends PrimaryDecoder implements ISourceEventListener,
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBufferListener(Listener<ReusableFloatBuffer> listener)
|
||||
public void setBufferListener(Listener<float[]> listener)
|
||||
{
|
||||
mResampledReusableBufferListener = listener;
|
||||
mResampledBufferListener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeBufferListener()
|
||||
{
|
||||
mResampledReusableBufferListener = null;
|
||||
mResampledBufferListener = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive(ReusableComplexBuffer reusableComplexBuffer)
|
||||
public void receive(ComplexSamples samples)
|
||||
{
|
||||
if(mIQFilter == null)
|
||||
if(mIDecimationFilter == null || mQDecimationFilter == null)
|
||||
{
|
||||
reusableComplexBuffer.decrementUserCount();
|
||||
throw new IllegalStateException("NBFM demodulator module must receive a sample rate change source " +
|
||||
"event before it can process complex sample buffers");
|
||||
}
|
||||
|
||||
ReusableComplexBuffer decimatedBuffer = mDecimationFilter.decimate(reusableComplexBuffer);
|
||||
ReusableComplexBuffer basebandFilteredBuffer = mIQFilter.filter(decimatedBuffer);
|
||||
float[] decimatedI = mIDecimationFilter.decimateReal(samples.i());
|
||||
float[] decimatedQ = mQDecimationFilter.decimateReal(samples.q());
|
||||
|
||||
ReusableFloatBuffer demodulatedBuffer = mDemodulator.demodulate(basebandFilteredBuffer);
|
||||
float[] filteredI = mIBasebandFilter.filter(decimatedI);
|
||||
float[] filteredQ = mQBasebandFilter.filter(decimatedQ);
|
||||
|
||||
float[] demodulated = mDemodulator.demodulate(filteredI, filteredQ);
|
||||
|
||||
if(mResampler != null)
|
||||
{
|
||||
@@ -153,12 +158,11 @@ public class NBFMDecoder extends PrimaryDecoder implements ISourceEventListener,
|
||||
//Either send the demodulated buffer to the resampler for distro, or decrement the user count
|
||||
if(mSquelch)
|
||||
{
|
||||
demodulatedBuffer.incrementUserCount();
|
||||
notifyIdle();
|
||||
}
|
||||
else
|
||||
{
|
||||
mResampler.resample(demodulatedBuffer);
|
||||
mResampler.resample(demodulated);
|
||||
notifyCallContinuation();
|
||||
}
|
||||
|
||||
@@ -171,7 +175,6 @@ public class NBFMDecoder extends PrimaryDecoder implements ISourceEventListener,
|
||||
}
|
||||
else
|
||||
{
|
||||
demodulatedBuffer.decrementUserCount();
|
||||
notifyIdle();
|
||||
}
|
||||
}
|
||||
@@ -266,10 +269,10 @@ public class NBFMDecoder extends PrimaryDecoder implements ISourceEventListener,
|
||||
{
|
||||
if(sourceEvent.getEvent() == SourceEvent.Event.NOTIFICATION_SAMPLE_RATE_CHANGE)
|
||||
{
|
||||
if(mIQFilter != null)
|
||||
if(mIBasebandFilter != null)
|
||||
{
|
||||
mIQFilter.dispose();
|
||||
mIQFilter = null;
|
||||
mIBasebandFilter = null;
|
||||
mQBasebandFilter = null;
|
||||
}
|
||||
|
||||
double sampleRate = sourceEvent.getValue().doubleValue();
|
||||
@@ -292,7 +295,8 @@ public class NBFMDecoder extends PrimaryDecoder implements ISourceEventListener,
|
||||
decimatedSampleRate /= decimationRate;
|
||||
}
|
||||
|
||||
mDecimationFilter = DecimationFilterFactory.getComplexDecimationFilter(decimationRate);
|
||||
mIDecimationFilter = DecimationFilterFactory.getRealDecimationFilter(decimationRate);
|
||||
mQDecimationFilter = DecimationFilterFactory.getRealDecimationFilter(decimationRate);
|
||||
|
||||
if((decimatedSampleRate < (2.0 * mChannelBandwidth)))
|
||||
{
|
||||
@@ -304,7 +308,7 @@ public class NBFMDecoder extends PrimaryDecoder implements ISourceEventListener,
|
||||
int passBandStop = (int)(mChannelBandwidth * .8);
|
||||
int stopBandStart = (int)mChannelBandwidth;
|
||||
|
||||
float[] filterTaps = null;
|
||||
float[] coefficients = null;
|
||||
|
||||
FIRFilterSpecification specification = FIRFilterSpecification.lowPassBuilder()
|
||||
.sampleRate(decimatedSampleRate * 2)
|
||||
@@ -320,7 +324,7 @@ public class NBFMDecoder extends PrimaryDecoder implements ISourceEventListener,
|
||||
|
||||
try
|
||||
{
|
||||
filterTaps = FilterFactory.getTaps(specification);
|
||||
coefficients = FilterFactory.getTaps(specification);
|
||||
}
|
||||
catch(FilterDesignException fde)
|
||||
{
|
||||
@@ -329,28 +333,25 @@ public class NBFMDecoder extends PrimaryDecoder implements ISourceEventListener,
|
||||
"] - will proceed using sinc (low-pass) filter");
|
||||
}
|
||||
|
||||
if(filterTaps == null)
|
||||
if(coefficients == null)
|
||||
{
|
||||
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);
|
||||
coefficients = FilterFactory.getLowPass(decimatedSampleRate, passBandStop, stopBandStart, 60,
|
||||
WindowType.HAMMING, true);
|
||||
}
|
||||
|
||||
mIQFilter = new ComplexFIRFilter2(filterTaps);
|
||||
mIBasebandFilter = FilterFactory.getRealFilter(coefficients);
|
||||
mQBasebandFilter = FilterFactory.getRealFilter(coefficients);
|
||||
|
||||
mResampler = new RealResampler(decimatedSampleRate, mOutputSampleRate, 2000, 1000);
|
||||
mResampler = new RealResampler(decimatedSampleRate, mOutputSampleRate, 4192, 512);
|
||||
|
||||
mResampler.setListener(reusableFloatBuffer ->
|
||||
mResampler.setListener(resampled ->
|
||||
{
|
||||
if(mResampledReusableBufferListener != null)
|
||||
if(mResampledBufferListener != null)
|
||||
{
|
||||
mResampledReusableBufferListener.receive(reusableFloatBuffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
reusableFloatBuffer.decrementUserCount();
|
||||
mResampledBufferListener.receive(resampled);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,471 +0,0 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* 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/>
|
||||
* *****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.module.decode.p25.phase1;
|
||||
|
||||
import io.github.dsheirer.dsp.gain.DirectGainControl;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.sample.buffer.ReusableFloatBuffer;
|
||||
import io.github.dsheirer.sample.real.RealSampleListener;
|
||||
import io.github.dsheirer.sample.real.RealSampleProvider;
|
||||
import io.github.dsheirer.source.ISourceEventListener;
|
||||
import io.github.dsheirer.source.SourceEvent;
|
||||
import org.apache.commons.math3.util.FastMath;
|
||||
|
||||
public class C4FMSymbolFilter implements Listener<ReusableFloatBuffer>, ISourceEventListener, RealSampleProvider
|
||||
{
|
||||
private static final float TAPS[][] =
|
||||
{
|
||||
{0.00000e+00f, 0.00000e+00f, 0.00000e+00f, 0.00000e+00f, 1.00000e+00f, 0.00000e+00f, 0.00000e+00f, 0.00000e+00f}, // 0/128
|
||||
{-1.54700e-04f, 8.53777e-04f, -2.76968e-03f, 7.89295e-03f, 9.98534e-01f, -5.41054e-03f, 1.24642e-03f, -1.98993e-04f}, // 1/128
|
||||
{-3.09412e-04f, 1.70888e-03f, -5.55134e-03f, 1.58840e-02f, 9.96891e-01f, -1.07209e-02f, 2.47942e-03f, -3.96391e-04f}, // 2/128
|
||||
{-4.64053e-04f, 2.56486e-03f, -8.34364e-03f, 2.39714e-02f, 9.95074e-01f, -1.59305e-02f, 3.69852e-03f, -5.92100e-04f}, // 3/128
|
||||
{-6.18544e-04f, 3.42130e-03f, -1.11453e-02f, 3.21531e-02f, 9.93082e-01f, -2.10389e-02f, 4.90322e-03f, -7.86031e-04f}, // 4/128
|
||||
{-7.72802e-04f, 4.27773e-03f, -1.39548e-02f, 4.04274e-02f, 9.90917e-01f, -2.60456e-02f, 6.09305e-03f, -9.78093e-04f}, // 5/128
|
||||
{-9.26747e-04f, 5.13372e-03f, -1.67710e-02f, 4.87921e-02f, 9.88580e-01f, -3.09503e-02f, 7.26755e-03f, -1.16820e-03f}, // 6/128
|
||||
{-1.08030e-03f, 5.98883e-03f, -1.95925e-02f, 5.72454e-02f, 9.86071e-01f, -3.57525e-02f, 8.42626e-03f, -1.35627e-03f}, // 7/128
|
||||
{-1.23337e-03f, 6.84261e-03f, -2.24178e-02f, 6.57852e-02f, 9.83392e-01f, -4.04519e-02f, 9.56876e-03f, -1.54221e-03f}, // 8/128
|
||||
{-1.38589e-03f, 7.69462e-03f, -2.52457e-02f, 7.44095e-02f, 9.80543e-01f, -4.50483e-02f, 1.06946e-02f, -1.72594e-03f}, // 9/128
|
||||
{-1.53777e-03f, 8.54441e-03f, -2.80746e-02f, 8.31162e-02f, 9.77526e-01f, -4.95412e-02f, 1.18034e-02f, -1.90738e-03f}, // 10/128
|
||||
{-1.68894e-03f, 9.39154e-03f, -3.09033e-02f, 9.19033e-02f, 9.74342e-01f, -5.39305e-02f, 1.28947e-02f, -2.08645e-03f}, // 11/128
|
||||
{-1.83931e-03f, 1.02356e-02f, -3.37303e-02f, 1.00769e-01f, 9.70992e-01f, -5.82159e-02f, 1.39681e-02f, -2.26307e-03f}, // 12/128
|
||||
{-1.98880e-03f, 1.10760e-02f, -3.65541e-02f, 1.09710e-01f, 9.67477e-01f, -6.23972e-02f, 1.50233e-02f, -2.43718e-03f}, // 13/128
|
||||
{-2.13733e-03f, 1.19125e-02f, -3.93735e-02f, 1.18725e-01f, 9.63798e-01f, -6.64743e-02f, 1.60599e-02f, -2.60868e-03f}, // 14/128
|
||||
{-2.28483e-03f, 1.27445e-02f, -4.21869e-02f, 1.27812e-01f, 9.59958e-01f, -7.04471e-02f, 1.70776e-02f, -2.77751e-03f}, // 15/128
|
||||
{-2.43121e-03f, 1.35716e-02f, -4.49929e-02f, 1.36968e-01f, 9.55956e-01f, -7.43154e-02f, 1.80759e-02f, -2.94361e-03f}, // 16/128
|
||||
{-2.57640e-03f, 1.43934e-02f, -4.77900e-02f, 1.46192e-01f, 9.51795e-01f, -7.80792e-02f, 1.90545e-02f, -3.10689e-03f}, // 17/128
|
||||
{-2.72032e-03f, 1.52095e-02f, -5.05770e-02f, 1.55480e-01f, 9.47477e-01f, -8.17385e-02f, 2.00132e-02f, -3.26730e-03f}, // 18/128
|
||||
{-2.86289e-03f, 1.60193e-02f, -5.33522e-02f, 1.64831e-01f, 9.43001e-01f, -8.52933e-02f, 2.09516e-02f, -3.42477e-03f}, // 19/128
|
||||
{-3.00403e-03f, 1.68225e-02f, -5.61142e-02f, 1.74242e-01f, 9.38371e-01f, -8.87435e-02f, 2.18695e-02f, -3.57923e-03f}, // 20/128
|
||||
{-3.14367e-03f, 1.76185e-02f, -5.88617e-02f, 1.83711e-01f, 9.33586e-01f, -9.20893e-02f, 2.27664e-02f, -3.73062e-03f}, // 21/128
|
||||
{-3.28174e-03f, 1.84071e-02f, -6.15931e-02f, 1.93236e-01f, 9.28650e-01f, -9.53307e-02f, 2.36423e-02f, -3.87888e-03f}, // 22/128
|
||||
{-3.41815e-03f, 1.91877e-02f, -6.43069e-02f, 2.02814e-01f, 9.23564e-01f, -9.84679e-02f, 2.44967e-02f, -4.02397e-03f}, // 23/128
|
||||
{-3.55283e-03f, 1.99599e-02f, -6.70018e-02f, 2.12443e-01f, 9.18329e-01f, -1.01501e-01f, 2.53295e-02f, -4.16581e-03f}, // 24/128
|
||||
{-3.68570e-03f, 2.07233e-02f, -6.96762e-02f, 2.22120e-01f, 9.12947e-01f, -1.04430e-01f, 2.61404e-02f, -4.30435e-03f}, // 25/128
|
||||
{-3.81671e-03f, 2.14774e-02f, -7.23286e-02f, 2.31843e-01f, 9.07420e-01f, -1.07256e-01f, 2.69293e-02f, -4.43955e-03f}, // 26/128
|
||||
{-3.94576e-03f, 2.22218e-02f, -7.49577e-02f, 2.41609e-01f, 9.01749e-01f, -1.09978e-01f, 2.76957e-02f, -4.57135e-03f}, // 27/128
|
||||
{-4.07279e-03f, 2.29562e-02f, -7.75620e-02f, 2.51417e-01f, 8.95936e-01f, -1.12597e-01f, 2.84397e-02f, -4.69970e-03f}, // 28/128
|
||||
{-4.19774e-03f, 2.36801e-02f, -8.01399e-02f, 2.61263e-01f, 8.89984e-01f, -1.15113e-01f, 2.91609e-02f, -4.82456e-03f}, // 29/128
|
||||
{-4.32052e-03f, 2.43930e-02f, -8.26900e-02f, 2.71144e-01f, 8.83893e-01f, -1.17526e-01f, 2.98593e-02f, -4.94589e-03f}, // 30/128
|
||||
{-4.44107e-03f, 2.50946e-02f, -8.52109e-02f, 2.81060e-01f, 8.77666e-01f, -1.19837e-01f, 3.05345e-02f, -5.06363e-03f}, // 31/128
|
||||
{-4.55932e-03f, 2.57844e-02f, -8.77011e-02f, 2.91006e-01f, 8.71305e-01f, -1.22047e-01f, 3.11866e-02f, -5.17776e-03f}, // 32/128
|
||||
{-4.67520e-03f, 2.64621e-02f, -9.01591e-02f, 3.00980e-01f, 8.64812e-01f, -1.24154e-01f, 3.18153e-02f, -5.28823e-03f}, // 33/128
|
||||
{-4.78866e-03f, 2.71272e-02f, -9.25834e-02f, 3.10980e-01f, 8.58189e-01f, -1.26161e-01f, 3.24205e-02f, -5.39500e-03f}, // 34/128
|
||||
{-4.89961e-03f, 2.77794e-02f, -9.49727e-02f, 3.21004e-01f, 8.51437e-01f, -1.28068e-01f, 3.30021e-02f, -5.49804e-03f}, // 35/128
|
||||
{-5.00800e-03f, 2.84182e-02f, -9.73254e-02f, 3.31048e-01f, 8.44559e-01f, -1.29874e-01f, 3.35600e-02f, -5.59731e-03f}, // 36/128
|
||||
{-5.11376e-03f, 2.90433e-02f, -9.96402e-02f, 3.41109e-01f, 8.37557e-01f, -1.31581e-01f, 3.40940e-02f, -5.69280e-03f}, // 37/128
|
||||
{-5.21683e-03f, 2.96543e-02f, -1.01915e-01f, 3.51186e-01f, 8.30432e-01f, -1.33189e-01f, 3.46042e-02f, -5.78446e-03f}, // 38/128
|
||||
{-5.31716e-03f, 3.02507e-02f, -1.04150e-01f, 3.61276e-01f, 8.23188e-01f, -1.34699e-01f, 3.50903e-02f, -5.87227e-03f}, // 39/128
|
||||
{-5.41467e-03f, 3.08323e-02f, -1.06342e-01f, 3.71376e-01f, 8.15826e-01f, -1.36111e-01f, 3.55525e-02f, -5.95620e-03f}, // 40/128
|
||||
{-5.50931e-03f, 3.13987e-02f, -1.08490e-01f, 3.81484e-01f, 8.08348e-01f, -1.37426e-01f, 3.59905e-02f, -6.03624e-03f}, // 41/128
|
||||
{-5.60103e-03f, 3.19495e-02f, -1.10593e-01f, 3.91596e-01f, 8.00757e-01f, -1.38644e-01f, 3.64044e-02f, -6.11236e-03f}, // 42/128
|
||||
{-5.68976e-03f, 3.24843e-02f, -1.12650e-01f, 4.01710e-01f, 7.93055e-01f, -1.39767e-01f, 3.67941e-02f, -6.18454e-03f}, // 43/128
|
||||
{-5.77544e-03f, 3.30027e-02f, -1.14659e-01f, 4.11823e-01f, 7.85244e-01f, -1.40794e-01f, 3.71596e-02f, -6.25277e-03f}, // 44/128
|
||||
{-5.85804e-03f, 3.35046e-02f, -1.16618e-01f, 4.21934e-01f, 7.77327e-01f, -1.41727e-01f, 3.75010e-02f, -6.31703e-03f}, // 45/128
|
||||
{-5.93749e-03f, 3.39894e-02f, -1.18526e-01f, 4.32038e-01f, 7.69305e-01f, -1.42566e-01f, 3.78182e-02f, -6.37730e-03f}, // 46/128
|
||||
{-6.01374e-03f, 3.44568e-02f, -1.20382e-01f, 4.42134e-01f, 7.61181e-01f, -1.43313e-01f, 3.81111e-02f, -6.43358e-03f}, // 47/128
|
||||
{-6.08674e-03f, 3.49066e-02f, -1.22185e-01f, 4.52218e-01f, 7.52958e-01f, -1.43968e-01f, 3.83800e-02f, -6.48585e-03f}, // 48/128
|
||||
{-6.15644e-03f, 3.53384e-02f, -1.23933e-01f, 4.62289e-01f, 7.44637e-01f, -1.44531e-01f, 3.86247e-02f, -6.53412e-03f}, // 49/128
|
||||
{-6.22280e-03f, 3.57519e-02f, -1.25624e-01f, 4.72342e-01f, 7.36222e-01f, -1.45004e-01f, 3.88454e-02f, -6.57836e-03f}, // 50/128
|
||||
{-6.28577e-03f, 3.61468e-02f, -1.27258e-01f, 4.82377e-01f, 7.27714e-01f, -1.45387e-01f, 3.90420e-02f, -6.61859e-03f}, // 51/128
|
||||
{-6.34530e-03f, 3.65227e-02f, -1.28832e-01f, 4.92389e-01f, 7.19116e-01f, -1.45682e-01f, 3.92147e-02f, -6.65479e-03f}, // 52/128
|
||||
{-6.40135e-03f, 3.68795e-02f, -1.30347e-01f, 5.02377e-01f, 7.10431e-01f, -1.45889e-01f, 3.93636e-02f, -6.68698e-03f}, // 53/128
|
||||
{-6.45388e-03f, 3.72167e-02f, -1.31800e-01f, 5.12337e-01f, 7.01661e-01f, -1.46009e-01f, 3.94886e-02f, -6.71514e-03f}, // 54/128
|
||||
{-6.50285e-03f, 3.75341e-02f, -1.33190e-01f, 5.22267e-01f, 6.92808e-01f, -1.46043e-01f, 3.95900e-02f, -6.73929e-03f}, // 55/128
|
||||
{-6.54823e-03f, 3.78315e-02f, -1.34515e-01f, 5.32164e-01f, 6.83875e-01f, -1.45993e-01f, 3.96678e-02f, -6.75943e-03f}, // 56/128
|
||||
{-6.58996e-03f, 3.81085e-02f, -1.35775e-01f, 5.42025e-01f, 6.74865e-01f, -1.45859e-01f, 3.97222e-02f, -6.77557e-03f}, // 57/128
|
||||
{-6.62802e-03f, 3.83650e-02f, -1.36969e-01f, 5.51849e-01f, 6.65779e-01f, -1.45641e-01f, 3.97532e-02f, -6.78771e-03f}, // 58/128
|
||||
{-6.66238e-03f, 3.86006e-02f, -1.38094e-01f, 5.61631e-01f, 6.56621e-01f, -1.45343e-01f, 3.97610e-02f, -6.79588e-03f}, // 59/128
|
||||
{-6.69300e-03f, 3.88151e-02f, -1.39150e-01f, 5.71370e-01f, 6.47394e-01f, -1.44963e-01f, 3.97458e-02f, -6.80007e-03f}, // 60/128
|
||||
{-6.71985e-03f, 3.90083e-02f, -1.40136e-01f, 5.81063e-01f, 6.38099e-01f, -1.44503e-01f, 3.97077e-02f, -6.80032e-03f}, // 61/128
|
||||
{-6.74291e-03f, 3.91800e-02f, -1.41050e-01f, 5.90706e-01f, 6.28739e-01f, -1.43965e-01f, 3.96469e-02f, -6.79662e-03f}, // 62/128
|
||||
{-6.76214e-03f, 3.93299e-02f, -1.41891e-01f, 6.00298e-01f, 6.19318e-01f, -1.43350e-01f, 3.95635e-02f, -6.78902e-03f}, // 63/128
|
||||
{-6.77751e-03f, 3.94578e-02f, -1.42658e-01f, 6.09836e-01f, 6.09836e-01f, -1.42658e-01f, 3.94578e-02f, -6.77751e-03f}, // 64/128
|
||||
{-6.78902e-03f, 3.95635e-02f, -1.43350e-01f, 6.19318e-01f, 6.00298e-01f, -1.41891e-01f, 3.93299e-02f, -6.76214e-03f}, // 65/128
|
||||
{-6.79662e-03f, 3.96469e-02f, -1.43965e-01f, 6.28739e-01f, 5.90706e-01f, -1.41050e-01f, 3.91800e-02f, -6.74291e-03f}, // 66/128
|
||||
{-6.80032e-03f, 3.97077e-02f, -1.44503e-01f, 6.38099e-01f, 5.81063e-01f, -1.40136e-01f, 3.90083e-02f, -6.71985e-03f}, // 67/128
|
||||
{-6.80007e-03f, 3.97458e-02f, -1.44963e-01f, 6.47394e-01f, 5.71370e-01f, -1.39150e-01f, 3.88151e-02f, -6.69300e-03f}, // 68/128
|
||||
{-6.79588e-03f, 3.97610e-02f, -1.45343e-01f, 6.56621e-01f, 5.61631e-01f, -1.38094e-01f, 3.86006e-02f, -6.66238e-03f}, // 69/128
|
||||
{-6.78771e-03f, 3.97532e-02f, -1.45641e-01f, 6.65779e-01f, 5.51849e-01f, -1.36969e-01f, 3.83650e-02f, -6.62802e-03f}, // 70/128
|
||||
{-6.77557e-03f, 3.97222e-02f, -1.45859e-01f, 6.74865e-01f, 5.42025e-01f, -1.35775e-01f, 3.81085e-02f, -6.58996e-03f}, // 71/128
|
||||
{-6.75943e-03f, 3.96678e-02f, -1.45993e-01f, 6.83875e-01f, 5.32164e-01f, -1.34515e-01f, 3.78315e-02f, -6.54823e-03f}, // 72/128
|
||||
{-6.73929e-03f, 3.95900e-02f, -1.46043e-01f, 6.92808e-01f, 5.22267e-01f, -1.33190e-01f, 3.75341e-02f, -6.50285e-03f}, // 73/128
|
||||
{-6.71514e-03f, 3.94886e-02f, -1.46009e-01f, 7.01661e-01f, 5.12337e-01f, -1.31800e-01f, 3.72167e-02f, -6.45388e-03f}, // 74/128
|
||||
{-6.68698e-03f, 3.93636e-02f, -1.45889e-01f, 7.10431e-01f, 5.02377e-01f, -1.30347e-01f, 3.68795e-02f, -6.40135e-03f}, // 75/128
|
||||
{-6.65479e-03f, 3.92147e-02f, -1.45682e-01f, 7.19116e-01f, 4.92389e-01f, -1.28832e-01f, 3.65227e-02f, -6.34530e-03f}, // 76/128
|
||||
{-6.61859e-03f, 3.90420e-02f, -1.45387e-01f, 7.27714e-01f, 4.82377e-01f, -1.27258e-01f, 3.61468e-02f, -6.28577e-03f}, // 77/128
|
||||
{-6.57836e-03f, 3.88454e-02f, -1.45004e-01f, 7.36222e-01f, 4.72342e-01f, -1.25624e-01f, 3.57519e-02f, -6.22280e-03f}, // 78/128
|
||||
{-6.53412e-03f, 3.86247e-02f, -1.44531e-01f, 7.44637e-01f, 4.62289e-01f, -1.23933e-01f, 3.53384e-02f, -6.15644e-03f}, // 79/128
|
||||
{-6.48585e-03f, 3.83800e-02f, -1.43968e-01f, 7.52958e-01f, 4.52218e-01f, -1.22185e-01f, 3.49066e-02f, -6.08674e-03f}, // 80/128
|
||||
{-6.43358e-03f, 3.81111e-02f, -1.43313e-01f, 7.61181e-01f, 4.42134e-01f, -1.20382e-01f, 3.44568e-02f, -6.01374e-03f}, // 81/128
|
||||
{-6.37730e-03f, 3.78182e-02f, -1.42566e-01f, 7.69305e-01f, 4.32038e-01f, -1.18526e-01f, 3.39894e-02f, -5.93749e-03f}, // 82/128
|
||||
{-6.31703e-03f, 3.75010e-02f, -1.41727e-01f, 7.77327e-01f, 4.21934e-01f, -1.16618e-01f, 3.35046e-02f, -5.85804e-03f}, // 83/128
|
||||
{-6.25277e-03f, 3.71596e-02f, -1.40794e-01f, 7.85244e-01f, 4.11823e-01f, -1.14659e-01f, 3.30027e-02f, -5.77544e-03f}, // 84/128
|
||||
{-6.18454e-03f, 3.67941e-02f, -1.39767e-01f, 7.93055e-01f, 4.01710e-01f, -1.12650e-01f, 3.24843e-02f, -5.68976e-03f}, // 85/128
|
||||
{-6.11236e-03f, 3.64044e-02f, -1.38644e-01f, 8.00757e-01f, 3.91596e-01f, -1.10593e-01f, 3.19495e-02f, -5.60103e-03f}, // 86/128
|
||||
{-6.03624e-03f, 3.59905e-02f, -1.37426e-01f, 8.08348e-01f, 3.81484e-01f, -1.08490e-01f, 3.13987e-02f, -5.50931e-03f}, // 87/128
|
||||
{-5.95620e-03f, 3.55525e-02f, -1.36111e-01f, 8.15826e-01f, 3.71376e-01f, -1.06342e-01f, 3.08323e-02f, -5.41467e-03f}, // 88/128
|
||||
{-5.87227e-03f, 3.50903e-02f, -1.34699e-01f, 8.23188e-01f, 3.61276e-01f, -1.04150e-01f, 3.02507e-02f, -5.31716e-03f}, // 89/128
|
||||
{-5.78446e-03f, 3.46042e-02f, -1.33189e-01f, 8.30432e-01f, 3.51186e-01f, -1.01915e-01f, 2.96543e-02f, -5.21683e-03f}, // 90/128
|
||||
{-5.69280e-03f, 3.40940e-02f, -1.31581e-01f, 8.37557e-01f, 3.41109e-01f, -9.96402e-02f, 2.90433e-02f, -5.11376e-03f}, // 91/128
|
||||
{-5.59731e-03f, 3.35600e-02f, -1.29874e-01f, 8.44559e-01f, 3.31048e-01f, -9.73254e-02f, 2.84182e-02f, -5.00800e-03f}, // 92/128
|
||||
{-5.49804e-03f, 3.30021e-02f, -1.28068e-01f, 8.51437e-01f, 3.21004e-01f, -9.49727e-02f, 2.77794e-02f, -4.89961e-03f}, // 93/128
|
||||
{-5.39500e-03f, 3.24205e-02f, -1.26161e-01f, 8.58189e-01f, 3.10980e-01f, -9.25834e-02f, 2.71272e-02f, -4.78866e-03f}, // 94/128
|
||||
{-5.28823e-03f, 3.18153e-02f, -1.24154e-01f, 8.64812e-01f, 3.00980e-01f, -9.01591e-02f, 2.64621e-02f, -4.67520e-03f}, // 95/128
|
||||
{-5.17776e-03f, 3.11866e-02f, -1.22047e-01f, 8.71305e-01f, 2.91006e-01f, -8.77011e-02f, 2.57844e-02f, -4.55932e-03f}, // 96/128
|
||||
{-5.06363e-03f, 3.05345e-02f, -1.19837e-01f, 8.77666e-01f, 2.81060e-01f, -8.52109e-02f, 2.50946e-02f, -4.44107e-03f}, // 97/128
|
||||
{-4.94589e-03f, 2.98593e-02f, -1.17526e-01f, 8.83893e-01f, 2.71144e-01f, -8.26900e-02f, 2.43930e-02f, -4.32052e-03f}, // 98/128
|
||||
{-4.82456e-03f, 2.91609e-02f, -1.15113e-01f, 8.89984e-01f, 2.61263e-01f, -8.01399e-02f, 2.36801e-02f, -4.19774e-03f}, // 99/128
|
||||
{-4.69970e-03f, 2.84397e-02f, -1.12597e-01f, 8.95936e-01f, 2.51417e-01f, -7.75620e-02f, 2.29562e-02f, -4.07279e-03f}, // 100/128
|
||||
{-4.57135e-03f, 2.76957e-02f, -1.09978e-01f, 9.01749e-01f, 2.41609e-01f, -7.49577e-02f, 2.22218e-02f, -3.94576e-03f}, // 101/128
|
||||
{-4.43955e-03f, 2.69293e-02f, -1.07256e-01f, 9.07420e-01f, 2.31843e-01f, -7.23286e-02f, 2.14774e-02f, -3.81671e-03f}, // 102/128
|
||||
{-4.30435e-03f, 2.61404e-02f, -1.04430e-01f, 9.12947e-01f, 2.22120e-01f, -6.96762e-02f, 2.07233e-02f, -3.68570e-03f}, // 103/128
|
||||
{-4.16581e-03f, 2.53295e-02f, -1.01501e-01f, 9.18329e-01f, 2.12443e-01f, -6.70018e-02f, 1.99599e-02f, -3.55283e-03f}, // 104/128
|
||||
{-4.02397e-03f, 2.44967e-02f, -9.84679e-02f, 9.23564e-01f, 2.02814e-01f, -6.43069e-02f, 1.91877e-02f, -3.41815e-03f}, // 105/128
|
||||
{-3.87888e-03f, 2.36423e-02f, -9.53307e-02f, 9.28650e-01f, 1.93236e-01f, -6.15931e-02f, 1.84071e-02f, -3.28174e-03f}, // 106/128
|
||||
{-3.73062e-03f, 2.27664e-02f, -9.20893e-02f, 9.33586e-01f, 1.83711e-01f, -5.88617e-02f, 1.76185e-02f, -3.14367e-03f}, // 107/128
|
||||
{-3.57923e-03f, 2.18695e-02f, -8.87435e-02f, 9.38371e-01f, 1.74242e-01f, -5.61142e-02f, 1.68225e-02f, -3.00403e-03f}, // 108/128
|
||||
{-3.42477e-03f, 2.09516e-02f, -8.52933e-02f, 9.43001e-01f, 1.64831e-01f, -5.33522e-02f, 1.60193e-02f, -2.86289e-03f}, // 109/128
|
||||
{-3.26730e-03f, 2.00132e-02f, -8.17385e-02f, 9.47477e-01f, 1.55480e-01f, -5.05770e-02f, 1.52095e-02f, -2.72032e-03f}, // 110/128
|
||||
{-3.10689e-03f, 1.90545e-02f, -7.80792e-02f, 9.51795e-01f, 1.46192e-01f, -4.77900e-02f, 1.43934e-02f, -2.57640e-03f}, // 111/128
|
||||
{-2.94361e-03f, 1.80759e-02f, -7.43154e-02f, 9.55956e-01f, 1.36968e-01f, -4.49929e-02f, 1.35716e-02f, -2.43121e-03f}, // 112/128
|
||||
{-2.77751e-03f, 1.70776e-02f, -7.04471e-02f, 9.59958e-01f, 1.27812e-01f, -4.21869e-02f, 1.27445e-02f, -2.28483e-03f}, // 113/128
|
||||
{-2.60868e-03f, 1.60599e-02f, -6.64743e-02f, 9.63798e-01f, 1.18725e-01f, -3.93735e-02f, 1.19125e-02f, -2.13733e-03f}, // 114/128
|
||||
{-2.43718e-03f, 1.50233e-02f, -6.23972e-02f, 9.67477e-01f, 1.09710e-01f, -3.65541e-02f, 1.10760e-02f, -1.98880e-03f}, // 115/128
|
||||
{-2.26307e-03f, 1.39681e-02f, -5.82159e-02f, 9.70992e-01f, 1.00769e-01f, -3.37303e-02f, 1.02356e-02f, -1.83931e-03f}, // 116/128
|
||||
{-2.08645e-03f, 1.28947e-02f, -5.39305e-02f, 9.74342e-01f, 9.19033e-02f, -3.09033e-02f, 9.39154e-03f, -1.68894e-03f}, // 117/128
|
||||
{-1.90738e-03f, 1.18034e-02f, -4.95412e-02f, 9.77526e-01f, 8.31162e-02f, -2.80746e-02f, 8.54441e-03f, -1.53777e-03f}, // 118/128
|
||||
{-1.72594e-03f, 1.06946e-02f, -4.50483e-02f, 9.80543e-01f, 7.44095e-02f, -2.52457e-02f, 7.69462e-03f, -1.38589e-03f}, // 119/128
|
||||
{-1.54221e-03f, 9.56876e-03f, -4.04519e-02f, 9.83392e-01f, 6.57852e-02f, -2.24178e-02f, 6.84261e-03f, -1.23337e-03f}, // 120/128
|
||||
{-1.35627e-03f, 8.42626e-03f, -3.57525e-02f, 9.86071e-01f, 5.72454e-02f, -1.95925e-02f, 5.98883e-03f, -1.08030e-03f}, // 121/128
|
||||
{-1.16820e-03f, 7.26755e-03f, -3.09503e-02f, 9.88580e-01f, 4.87921e-02f, -1.67710e-02f, 5.13372e-03f, -9.26747e-04f}, // 122/128
|
||||
{-9.78093e-04f, 6.09305e-03f, -2.60456e-02f, 9.90917e-01f, 4.04274e-02f, -1.39548e-02f, 4.27773e-03f, -7.72802e-04f}, // 123/128
|
||||
{-7.86031e-04f, 4.90322e-03f, -2.10389e-02f, 9.93082e-01f, 3.21531e-02f, -1.11453e-02f, 3.42130e-03f, -6.18544e-04f}, // 124/128
|
||||
{-5.92100e-04f, 3.69852e-03f, -1.59305e-02f, 9.95074e-01f, 2.39714e-02f, -8.34364e-03f, 2.56486e-03f, -4.64053e-04f}, // 125/128
|
||||
{-3.96391e-04f, 2.47942e-03f, -1.07209e-02f, 9.96891e-01f, 1.58840e-02f, -5.55134e-03f, 1.70888e-03f, -3.09412e-04f}, // 126/128
|
||||
{-1.98993e-04f, 1.24642e-03f, -5.41054e-03f, 9.98534e-01f, 7.89295e-03f, -2.76968e-03f, 8.53777e-04f, -1.54700e-04f}, // 127/128
|
||||
{0.00000e+00f, 0.00000e+00f, 0.00000e+00f, 1.00000e+00f, 0.00000e+00f, 0.00000e+00f, 0.00000e+00f, 0.00000e+00f}, // 128/128
|
||||
};
|
||||
|
||||
private static final int NUMBER_FILTER_TAPS = 8;
|
||||
private static final int NUMBER_FILTER_STEPS = 128;
|
||||
|
||||
private static final int SAMPLE_RATE = 48000;
|
||||
private static final int SYMBOL_RATE = 4800;
|
||||
|
||||
/* PLLGain loop gain constant */
|
||||
private static final double K_SYMBOL_SPREAD = 0.0100;
|
||||
|
||||
/* Constraints on symbol spreading */
|
||||
private static final float SYMBOL_SPREAD_MAX = 2.4f; // upper range limit: +20%
|
||||
private static final float SYMBOL_SPREAD_MIN = 1.6f; // lower range limit: -20%
|
||||
|
||||
/* Symbol clock tracking loop gain */
|
||||
private static final double K_SYMBOL_TIMING = 0.025;
|
||||
|
||||
/* Coarse and fine frequency tracking constants */
|
||||
private static final double K_COARSE_FREQUENCY = 0.00125;
|
||||
private static final double K_FINE_FREQUENCY = 0.125;
|
||||
|
||||
/* Frequency correction broadcast threshold */
|
||||
// private static final double COARSE_FREQUENCY_DEADBAND = 1.66;
|
||||
private static final double COARSE_FREQUENCY_THRESHOLD = 1.20;
|
||||
|
||||
/* 2.0 symbol spread gives -3, -1, 1, 3 */
|
||||
private float mSymbolSpread = 2.0f;
|
||||
private float mSymbolClock = 0.0f;
|
||||
private float mSymbolTime = (float)SYMBOL_RATE / (float)SAMPLE_RATE;
|
||||
|
||||
private float mFineFrequencyCorrection = 0.0f;
|
||||
private float mCoarseFrequencyCorrection = 0.0f;
|
||||
|
||||
private float mHistory[] = new float[NUMBER_FILTER_TAPS];
|
||||
private int mHistoryLast = 0;
|
||||
|
||||
private RealSampleListener mListener;
|
||||
|
||||
private DirectGainControl mGainController = new DirectGainControl(15.0f, 0.1f, 35.0f, 0.3f);
|
||||
|
||||
private int mFrequencyAdjustmentRequested = 0;
|
||||
private int mFrequencyCorrection = 0;
|
||||
private int mFrequencyCorrectionMaximum = 3000;
|
||||
private boolean mResetFrequencyTracker = false;
|
||||
private FrequencyCorrectionProcessor mFrequencyCorrectionProcessor;
|
||||
private Listener<SourceEvent> mFrequencyChangeListener;
|
||||
|
||||
/**
|
||||
* C4FM Symbol Filter
|
||||
*
|
||||
* Sample Gain values - the gain of incoming sample values will critically
|
||||
* impact the performance of this filter. Gain should be adjusted to
|
||||
* optimize the value of mSymbolSpread toward a value of 2.0. If the value
|
||||
* is less than 2.0, then increase gain. If the value is over 2.0, then
|
||||
* decrease gain.
|
||||
*
|
||||
* When preceded by the RealAutomaticGainFilter class, an optimal gain
|
||||
* setting for this filter is 15.4 and that yields a symbol spread that
|
||||
* is centered on 2.0, ranging 1.92 to 2.08.
|
||||
*/
|
||||
public C4FMSymbolFilter(int frequencyCorrectionMaximum)
|
||||
{
|
||||
mFrequencyCorrectionMaximum = frequencyCorrectionMaximum;
|
||||
}
|
||||
|
||||
public void dispose()
|
||||
{
|
||||
mGainController = null;
|
||||
// mFrequencyCorrectionControl = null;
|
||||
mListener = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive(ReusableFloatBuffer buffer)
|
||||
{
|
||||
for(float sample : buffer.getSamples())
|
||||
{
|
||||
receive(sample);
|
||||
}
|
||||
|
||||
/* If a frequency correction was requested during the processing of this
|
||||
* buffer, we'll apply the change and it will be reflected in the next
|
||||
* arriving buffer. Reset the lock on frequency correction and reset
|
||||
* the internal frequency correction tracker */
|
||||
if(mFrequencyAdjustmentRequested != 0)
|
||||
{
|
||||
int correction = mFrequencyCorrection + mFrequencyAdjustmentRequested;
|
||||
|
||||
if(correction > mFrequencyCorrectionMaximum)
|
||||
{
|
||||
correction = mFrequencyCorrectionMaximum;
|
||||
}
|
||||
else if(correction < -mFrequencyCorrectionMaximum)
|
||||
{
|
||||
correction = -mFrequencyCorrectionMaximum;
|
||||
}
|
||||
|
||||
broadcast(SourceEvent.channelFrequencyCorrectionRequest(correction));
|
||||
|
||||
mFrequencyAdjustmentRequested = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public void receive(float sample)
|
||||
{
|
||||
sample = mGainController.correct(sample);
|
||||
|
||||
if(mResetFrequencyTracker)
|
||||
{
|
||||
mCoarseFrequencyCorrection = 0.0f;
|
||||
mFineFrequencyCorrection = 0.0f;
|
||||
|
||||
broadcast(SourceEvent.channelFrequencyCorrectionRequest(0l));
|
||||
|
||||
mResetFrequencyTracker = false;
|
||||
}
|
||||
|
||||
mSymbolClock += mSymbolTime;
|
||||
|
||||
mHistory[mHistoryLast++] = sample;
|
||||
|
||||
mHistoryLast %= NUMBER_FILTER_TAPS;
|
||||
|
||||
if(mSymbolClock > 1.0f)
|
||||
{
|
||||
mSymbolClock -= 1.0f;
|
||||
|
||||
int imu = (int) FastMath.floor(0.5 +
|
||||
((float)NUMBER_FILTER_STEPS * (mSymbolClock / mSymbolTime)));
|
||||
|
||||
if(imu >= NUMBER_FILTER_STEPS)
|
||||
{
|
||||
imu = NUMBER_FILTER_STEPS - 1;
|
||||
}
|
||||
|
||||
int imu_p1 = imu + 1;
|
||||
|
||||
int j = mHistoryLast;
|
||||
|
||||
double interp = 0.0;
|
||||
double interp_p1 = 0.0;
|
||||
|
||||
for(int i = 0; i < NUMBER_FILTER_TAPS; i++)
|
||||
{
|
||||
interp += TAPS[imu][i] * mHistory[j];
|
||||
interp_p1 += TAPS[imu_p1][i] * mHistory[j];
|
||||
|
||||
j = (j + 1) % NUMBER_FILTER_TAPS;
|
||||
}
|
||||
|
||||
/* Output symbol will be interpolated value corrected for symbol
|
||||
* spread and frequency offset */
|
||||
interp -= mFineFrequencyCorrection;
|
||||
interp_p1 -= mFineFrequencyCorrection;
|
||||
|
||||
/* Correct output for symbol deviation (spread) */
|
||||
float output = (float)(2.0 * interp / mSymbolSpread);
|
||||
|
||||
/* Detect received symbol error: basically use a hard decision and
|
||||
* subtract off expected position nominal symbol level which will be
|
||||
* +/- 0.5 * symbol spread and +/- 1.5 symbol spread. Remember that
|
||||
* nominal symbol spread will be 2.0 */
|
||||
double symbolError;
|
||||
|
||||
if(interp < -mSymbolSpread)
|
||||
{
|
||||
/* symbol is -3: Expected at -1.5 * symbol spread */
|
||||
symbolError = interp + (1.5 * mSymbolSpread);
|
||||
mSymbolSpread -= (symbolError * 0.5 * K_SYMBOL_SPREAD);
|
||||
}
|
||||
else if(interp < 0.0)
|
||||
{
|
||||
/* symbol is -1: Expected at -0.5 * symbol_spread */
|
||||
symbolError = interp + (0.5 * mSymbolSpread);
|
||||
mSymbolSpread -= (symbolError * K_SYMBOL_SPREAD);
|
||||
}
|
||||
else if(interp < mSymbolSpread)
|
||||
{
|
||||
/* symbol is +1: Expected at +0.5 * symbol_spread */
|
||||
symbolError = interp - (0.5 * mSymbolSpread);
|
||||
mSymbolSpread += (symbolError * K_SYMBOL_SPREAD);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* symbol is +3: Expected at +1.5 * symbol_spread */
|
||||
symbolError = interp - (1.5 * mSymbolSpread);
|
||||
mSymbolSpread += (symbolError * 0.5 * K_SYMBOL_SPREAD);
|
||||
}
|
||||
|
||||
/* Symbol clock tracking loop adjustment */
|
||||
if(interp_p1 < interp)
|
||||
{
|
||||
mSymbolClock += symbolError * K_SYMBOL_TIMING;
|
||||
}
|
||||
else
|
||||
{
|
||||
mSymbolClock -= symbolError * K_SYMBOL_TIMING;
|
||||
}
|
||||
|
||||
if(mSymbolSpread < SYMBOL_SPREAD_MIN)
|
||||
{
|
||||
mGainController.increase();
|
||||
|
||||
mSymbolSpread = SYMBOL_SPREAD_MIN;
|
||||
}
|
||||
else if(mSymbolSpread > SYMBOL_SPREAD_MAX)
|
||||
{
|
||||
mGainController.decrease();
|
||||
|
||||
mSymbolSpread = SYMBOL_SPREAD_MAX;
|
||||
}
|
||||
|
||||
mCoarseFrequencyCorrection += ((mFineFrequencyCorrection -
|
||||
mCoarseFrequencyCorrection) * K_COARSE_FREQUENCY);
|
||||
|
||||
mFineFrequencyCorrection += (symbolError * K_FINE_FREQUENCY);
|
||||
|
||||
/* Queue a frequency adjustment (once per buffer) as needed */
|
||||
if(FastMath.abs(mCoarseFrequencyCorrection) > COARSE_FREQUENCY_THRESHOLD)
|
||||
{
|
||||
mFrequencyAdjustmentRequested =
|
||||
500 * (mCoarseFrequencyCorrection > 0 ? 1 : -1);
|
||||
}
|
||||
|
||||
/* dispatch the interpolated value to the listener */
|
||||
if(mListener != null)
|
||||
{
|
||||
mListener.receive(output);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setListener(RealSampleListener listener)
|
||||
{
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeListener(RealSampleListener listener)
|
||||
{
|
||||
mListener = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Broadcasts a frequency change event to the registered listener
|
||||
*
|
||||
* @param event
|
||||
*/
|
||||
public void broadcast(SourceEvent event)
|
||||
{
|
||||
if(mFrequencyChangeListener != null)
|
||||
{
|
||||
mFrequencyChangeListener.receive(event);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the listener for frequency change events.
|
||||
*
|
||||
* @param listener
|
||||
*/
|
||||
public void setFrequencyChangeListener(Listener<SourceEvent> listener)
|
||||
{
|
||||
mFrequencyChangeListener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Listener<SourceEvent> getSourceEventListener()
|
||||
{
|
||||
if(mFrequencyCorrectionProcessor == null)
|
||||
{
|
||||
mFrequencyCorrectionProcessor = new FrequencyCorrectionProcessor();
|
||||
}
|
||||
|
||||
return mFrequencyCorrectionProcessor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Receives notifications that the channel frequency correction has been
|
||||
* applied and updates internal tracking value.
|
||||
*/
|
||||
public class FrequencyCorrectionProcessor implements Listener<SourceEvent>
|
||||
{
|
||||
@Override
|
||||
public void receive(SourceEvent event)
|
||||
{
|
||||
switch(event.getEvent())
|
||||
{
|
||||
case NOTIFICATION_CHANNEL_FREQUENCY_CORRECTION_CHANGE:
|
||||
mFrequencyCorrection = event.getValue().intValue();
|
||||
|
||||
//Reset internal frequency tracking
|
||||
mCoarseFrequencyCorrection = 0.0f;
|
||||
mFineFrequencyCorrection = 0.0f;
|
||||
break;
|
||||
case NOTIFICATION_FREQUENCY_CORRECTION_CHANGE:
|
||||
case NOTIFICATION_SAMPLE_RATE_CHANGE:
|
||||
mResetFrequencyTracker = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,16 +25,17 @@ import io.github.dsheirer.module.decode.DecoderType;
|
||||
import io.github.dsheirer.module.decode.FeedbackDecoder;
|
||||
import io.github.dsheirer.sample.Broadcaster;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.sample.buffer.IReusableByteBufferProvider;
|
||||
import io.github.dsheirer.sample.buffer.IReusableComplexBufferListener;
|
||||
import io.github.dsheirer.sample.buffer.ReusableByteBuffer;
|
||||
import io.github.dsheirer.sample.buffer.ReusableComplexBuffer;
|
||||
import io.github.dsheirer.sample.buffer.IByteBufferProvider;
|
||||
import io.github.dsheirer.sample.complex.ComplexSamples;
|
||||
import io.github.dsheirer.sample.complex.IComplexSamplesListener;
|
||||
import io.github.dsheirer.source.ISourceEventListener;
|
||||
import io.github.dsheirer.source.ISourceEventProvider;
|
||||
import io.github.dsheirer.source.SourceEvent;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
public abstract class P25P1Decoder extends FeedbackDecoder implements ISourceEventListener, ISourceEventProvider,
|
||||
IReusableComplexBufferListener, Listener<ReusableComplexBuffer>, IReusableByteBufferProvider
|
||||
IComplexSamplesListener, Listener<ComplexSamples>, IByteBufferProvider
|
||||
{
|
||||
private double mSampleRate;
|
||||
private Broadcaster<Dibit> mDibitBroadcaster = new Broadcaster<>();
|
||||
@@ -78,7 +79,7 @@ public abstract class P25P1Decoder extends FeedbackDecoder implements ISourceEve
|
||||
* Implements the IByteBufferProvider interface - delegates to the byte buffer assembler
|
||||
*/
|
||||
@Override
|
||||
public void setBufferListener(Listener<ReusableByteBuffer> listener)
|
||||
public void setBufferListener(Listener<ByteBuffer> listener)
|
||||
{
|
||||
mByteBufferAssembler.setBufferListener(listener);
|
||||
}
|
||||
@@ -87,7 +88,7 @@ public abstract class P25P1Decoder extends FeedbackDecoder implements ISourceEve
|
||||
* Implements the IByteBufferProvider interface - delegates to the byte buffer assembler
|
||||
*/
|
||||
@Override
|
||||
public void removeBufferListener(Listener<ReusableByteBuffer> listener)
|
||||
public void removeBufferListener(Listener<ByteBuffer> listener)
|
||||
{
|
||||
mByteBufferAssembler.removeBufferListener(listener);
|
||||
}
|
||||
@@ -160,7 +161,7 @@ public abstract class P25P1Decoder extends FeedbackDecoder implements ISourceEve
|
||||
* Listener interface to receive reusable complex buffers
|
||||
*/
|
||||
@Override
|
||||
public Listener<ReusableComplexBuffer> getReusableComplexBufferListener()
|
||||
public Listener<ComplexSamples> getComplexSamplesListener()
|
||||
{
|
||||
return P25P1Decoder.this;
|
||||
}
|
||||
|
||||
@@ -24,16 +24,16 @@ package io.github.dsheirer.module.decode.p25.phase1;
|
||||
import io.github.dsheirer.dsp.filter.FilterFactory;
|
||||
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;
|
||||
import io.github.dsheirer.dsp.gain.ComplexFeedForwardGainControl;
|
||||
import io.github.dsheirer.dsp.filter.fir.real.IRealFilter;
|
||||
import io.github.dsheirer.dsp.gain.complex.ComplexGainFactory;
|
||||
import io.github.dsheirer.dsp.gain.complex.IComplexGainControl;
|
||||
import io.github.dsheirer.dsp.psk.DQPSKDecisionDirectedDemodulator;
|
||||
import io.github.dsheirer.dsp.psk.InterpolatingSampleBuffer;
|
||||
import io.github.dsheirer.dsp.psk.pll.CostasLoop;
|
||||
import io.github.dsheirer.dsp.psk.pll.FrequencyCorrectionSyncMonitor;
|
||||
import io.github.dsheirer.dsp.psk.pll.PLLBandwidth;
|
||||
import io.github.dsheirer.dsp.squelch.PowerMonitor;
|
||||
import io.github.dsheirer.module.decode.DecoderType;
|
||||
import io.github.dsheirer.sample.buffer.ReusableComplexBuffer;
|
||||
import io.github.dsheirer.sample.complex.ComplexSamples;
|
||||
import io.github.dsheirer.source.SourceEvent;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -51,9 +51,10 @@ public class P25P1DecoderC4FM extends P25P1Decoder
|
||||
protected CostasLoop mCostasLoop;
|
||||
protected FrequencyCorrectionSyncMonitor mFrequencyCorrectionSyncMonitor;
|
||||
protected P25P1MessageFramer mMessageFramer;
|
||||
private ComplexFeedForwardGainControl mAGC = new ComplexFeedForwardGainControl(32);
|
||||
protected IComplexGainControl mAGC = ComplexGainFactory.getComplexGainControl();
|
||||
private Map<Double,float[]> mBasebandFilters = new HashMap<>();
|
||||
private ComplexFIRFilter2 mBasebandFilter;
|
||||
protected IRealFilter mIBasebandFilter;
|
||||
protected IRealFilter mQBasebandFilter;
|
||||
|
||||
/**
|
||||
* P25 Phase 1 - standard C4FM modulation decoder. Uses Differential QPSK decoding with a Costas PLL and a
|
||||
@@ -68,7 +69,8 @@ public class P25P1DecoderC4FM extends P25P1Decoder
|
||||
public void setSampleRate(double sampleRate)
|
||||
{
|
||||
super.setSampleRate(sampleRate);
|
||||
mBasebandFilter = new ComplexFIRFilter2(getBasebandFilter());
|
||||
mIBasebandFilter = FilterFactory.getRealFilter(getBasebandFilter());
|
||||
mQBasebandFilter = FilterFactory.getRealFilter(getBasebandFilter());
|
||||
|
||||
mCostasLoop = new CostasLoop(getSampleRate(), getSymbolRate());
|
||||
mCostasLoop.setPLLBandwidth(PLLBandwidth.BW_300);
|
||||
@@ -95,35 +97,21 @@ public class P25P1DecoderC4FM extends P25P1Decoder
|
||||
|
||||
/**
|
||||
* Primary method for processing incoming complex sample buffers
|
||||
* @param reusableComplexBuffer containing channelized complex samples
|
||||
* @param samples containing channelized complex samples
|
||||
*/
|
||||
@Override
|
||||
public void receive(ReusableComplexBuffer reusableComplexBuffer)
|
||||
public void receive(ComplexSamples samples)
|
||||
{
|
||||
//User accounting of the incoming buffer is handled by the filter
|
||||
ReusableComplexBuffer basebandFiltered = filter(reusableComplexBuffer);
|
||||
mMessageFramer.setCurrentTime(System.currentTimeMillis());
|
||||
|
||||
float[] i = mIBasebandFilter.filter(samples.i());
|
||||
float[] q = mQBasebandFilter.filter(samples.q());
|
||||
|
||||
//Process the buffer for power meter measurements (before gain is applied)
|
||||
mPowerMonitor.process(basebandFiltered);
|
||||
mPowerMonitor.process(i, q);
|
||||
|
||||
//User accounting of the incoming buffer is handled by the gain filter
|
||||
ReusableComplexBuffer gainApplied = mAGC.filter(basebandFiltered);
|
||||
|
||||
mMessageFramer.setCurrentTime(reusableComplexBuffer.getTimestamp());
|
||||
|
||||
//User accounting of the filtered buffer is handled by the demodulator
|
||||
mQPSKDemodulator.receive(gainApplied);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the complex buffer and returns a new reusable complex buffer with the filtered contents.
|
||||
* @param reusableComplexBuffer to filter
|
||||
* @return filtered complex buffer
|
||||
*/
|
||||
protected ReusableComplexBuffer filter(ReusableComplexBuffer reusableComplexBuffer)
|
||||
{
|
||||
//User accounting of the incoming buffer is handled by the filter
|
||||
return mBasebandFilter.filter(reusableComplexBuffer);
|
||||
ComplexSamples amplified = mAGC.process(i, q);
|
||||
mQPSKDemodulator.receive(amplified);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2019 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 <http://www.gnu.org/licenses/>
|
||||
* *****************************************************************************
|
||||
* ****************************************************************************
|
||||
*/
|
||||
package io.github.dsheirer.module.decode.p25.phase1;
|
||||
|
||||
@@ -23,8 +22,8 @@ import io.github.dsheirer.dsp.psk.DQPSKDecisionDirectedDemodulatorInstrumented;
|
||||
import io.github.dsheirer.dsp.psk.InterpolatingSampleBufferInstrumented;
|
||||
import io.github.dsheirer.dsp.psk.SymbolDecisionData;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.sample.buffer.ReusableComplexBuffer;
|
||||
import io.github.dsheirer.sample.complex.Complex;
|
||||
import io.github.dsheirer.sample.complex.ComplexSamples;
|
||||
|
||||
public class P25P1DecoderC4FMInstrumented extends P25P1DecoderC4FM
|
||||
{
|
||||
@@ -32,7 +31,7 @@ public class P25P1DecoderC4FMInstrumented extends P25P1DecoderC4FM
|
||||
private Listener<Double> mPLLFrequencyListener;
|
||||
private Listener<Double> mSamplesPerSymbolListener;
|
||||
private Listener<Complex> mComplexSymbolListener;
|
||||
private Listener<ReusableComplexBuffer> mFilteredSymbolListener;
|
||||
private Listener<ComplexSamples> mFilteredSymbolListener;
|
||||
private Listener<SymbolDecisionData> mSymbolDecisionDataListener;
|
||||
|
||||
/**
|
||||
@@ -44,19 +43,27 @@ public class P25P1DecoderC4FMInstrumented extends P25P1DecoderC4FM
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides the filter method so that we can capture the filtered samples for instrumentation
|
||||
* Primary method for processing incoming complex sample buffers
|
||||
* @param samples containing channelized complex samples
|
||||
*/
|
||||
protected ReusableComplexBuffer filter(ReusableComplexBuffer reusableComplexBuffer)
|
||||
@Override
|
||||
public void receive(ComplexSamples samples)
|
||||
{
|
||||
ReusableComplexBuffer filtered = super.filter(reusableComplexBuffer);
|
||||
mMessageFramer.setCurrentTime(System.currentTimeMillis());
|
||||
|
||||
float[] i = mIBasebandFilter.filter(samples.i());
|
||||
float[] q = mQBasebandFilter.filter(samples.q());
|
||||
|
||||
if(mFilteredSymbolListener != null)
|
||||
{
|
||||
filtered.incrementUserCount();
|
||||
mFilteredSymbolListener.receive(filtered);
|
||||
mFilteredSymbolListener.receive(new ComplexSamples(i, q));
|
||||
}
|
||||
|
||||
return filtered;
|
||||
//Process the buffer for power meter measurements (before gain is applied)
|
||||
mPowerMonitor.process(i, q);
|
||||
|
||||
ComplexSamples amplified = mAGC.process(i, q);
|
||||
mQPSKDemodulator.receive(amplified);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -100,7 +107,7 @@ public class P25P1DecoderC4FMInstrumented extends P25P1DecoderC4FM
|
||||
((DQPSKDecisionDirectedDemodulatorInstrumented)mQPSKDemodulator).setPLLFrequencyListener(listener);
|
||||
}
|
||||
|
||||
public void setFilteredBufferListener(Listener<ReusableComplexBuffer> listener)
|
||||
public void setFilteredBufferListener(Listener<ComplexSamples> listener)
|
||||
{
|
||||
mFilteredSymbolListener = listener;
|
||||
}
|
||||
|
||||
@@ -22,26 +22,21 @@
|
||||
package io.github.dsheirer.module.decode.p25.phase1;
|
||||
|
||||
import io.github.dsheirer.dsp.filter.FilterFactory;
|
||||
import io.github.dsheirer.dsp.filter.Window.WindowType;
|
||||
import io.github.dsheirer.dsp.filter.fir.complex.ComplexFIRFilter2;
|
||||
import io.github.dsheirer.dsp.gain.ComplexFeedForwardGainControl;
|
||||
import io.github.dsheirer.dsp.filter.fir.real.IRealFilter;
|
||||
import io.github.dsheirer.dsp.gain.complex.ComplexGainFactory;
|
||||
import io.github.dsheirer.dsp.gain.complex.IComplexGainControl;
|
||||
import io.github.dsheirer.dsp.psk.DQPSKGardnerDemodulator;
|
||||
import io.github.dsheirer.dsp.psk.InterpolatingSampleBuffer;
|
||||
import io.github.dsheirer.dsp.psk.pll.CostasLoop;
|
||||
import io.github.dsheirer.dsp.psk.pll.FrequencyCorrectionSyncMonitor;
|
||||
import io.github.dsheirer.dsp.psk.pll.PLLBandwidth;
|
||||
import io.github.dsheirer.dsp.window.WindowType;
|
||||
import io.github.dsheirer.module.decode.DecoderType;
|
||||
import io.github.dsheirer.protocol.Protocol;
|
||||
import io.github.dsheirer.record.binary.BinaryRecorder;
|
||||
import io.github.dsheirer.sample.buffer.ReusableComplexBuffer;
|
||||
import io.github.dsheirer.sample.complex.ComplexSamples;
|
||||
import io.github.dsheirer.source.SourceEvent;
|
||||
import io.github.dsheirer.source.wave.ComplexWaveSource;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -52,8 +47,9 @@ public class P25P1DecoderLSM extends P25P1Decoder
|
||||
protected static final float SAMPLE_COUNTER_GAIN = 0.3f;
|
||||
|
||||
private Map<Double,float[]> mBasebandFilters = new HashMap<>();
|
||||
private ComplexFIRFilter2 mBasebandFilter;
|
||||
private ComplexFeedForwardGainControl mAGC = new ComplexFeedForwardGainControl(32);
|
||||
protected IRealFilter mIBasebandFilter;
|
||||
protected IRealFilter mQBasebandFilter;
|
||||
protected IComplexGainControl mAGC = ComplexGainFactory.getComplexGainControl();
|
||||
protected DQPSKGardnerDemodulator mQPSKDemodulator;
|
||||
protected P25P1MessageFramer mMessageFramer;
|
||||
protected CostasLoop mCostasLoop;
|
||||
@@ -79,7 +75,8 @@ public class P25P1DecoderLSM extends P25P1Decoder
|
||||
{
|
||||
super.setSampleRate(sampleRate);
|
||||
|
||||
mBasebandFilter = new ComplexFIRFilter2(getBasebandFilter());
|
||||
mIBasebandFilter = FilterFactory.getRealFilter(getBasebandFilter());
|
||||
mQBasebandFilter = FilterFactory.getRealFilter(getBasebandFilter());
|
||||
|
||||
mCostasLoop = new CostasLoop(getSampleRate(), getSymbolRate());
|
||||
mCostasLoop.setPLLBandwidth(PLLBandwidth.BW_200);
|
||||
@@ -108,37 +105,19 @@ public class P25P1DecoderLSM extends P25P1Decoder
|
||||
* Primary method for receiving incoming channel samples
|
||||
*/
|
||||
@Override
|
||||
public void receive(ReusableComplexBuffer reusableComplexBuffer)
|
||||
public void receive(ComplexSamples samples)
|
||||
{
|
||||
mMessageFramer.setCurrentTime(System.currentTimeMillis());
|
||||
|
||||
//The filter will decrement the user count when finished
|
||||
ReusableComplexBuffer basebandFiltered = filter(reusableComplexBuffer);
|
||||
float[] i = mIBasebandFilter.filter(samples.i());
|
||||
float[] q = mQBasebandFilter.filter(samples.q());
|
||||
|
||||
//Process the buffer for power measurements
|
||||
mPowerMonitor.process(basebandFiltered);
|
||||
mPowerMonitor.process(i, q);
|
||||
|
||||
//AGC will decrement the user count when finished
|
||||
ReusableComplexBuffer gainApplied = mAGC.filter(basebandFiltered);
|
||||
|
||||
mMessageFramer.setCurrentTime(reusableComplexBuffer.getTimestamp());
|
||||
|
||||
//Decoder will decrement the user count when finished
|
||||
mQPSKDemodulator.receive(gainApplied);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the complex buffer and returns a new reusable complex buffer with the filtered contents.
|
||||
*
|
||||
* @param reusableComplexBuffer to filter
|
||||
* @return filtered complex buffer
|
||||
*/
|
||||
protected ReusableComplexBuffer filter(ReusableComplexBuffer reusableComplexBuffer)
|
||||
{
|
||||
//No additional filtering of the channel is currently needed, since the polyphase channelizer
|
||||
//provides the filtering.
|
||||
return reusableComplexBuffer;
|
||||
|
||||
//User accounting of the incoming buffer is handled by the filter
|
||||
// return mBasebandFilter.filter(reusableComplexBuffer);
|
||||
ComplexSamples amplified = mAGC.process(i, q);
|
||||
mQPSKDemodulator.receive(amplified);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -194,44 +173,4 @@ public class P25P1DecoderLSM extends P25P1Decoder
|
||||
|
||||
return filter;
|
||||
}
|
||||
|
||||
public static void main(String[] args)
|
||||
{
|
||||
String path = "/media/denny/500G1EXT4/RadioRecordings/";
|
||||
// String input = "DFWAirport_Site_857_3875_baseband_20181213_223236.wav";
|
||||
// String output = "DFWAirport_Site_857_3875_baseband_20181213_223236";
|
||||
|
||||
String input = "P25P2_HCPM_Metrocrest_Dallas_857_7625_phase_2_not_motorola_baseband_20181213_224616_control_channel.wav";
|
||||
String output = "P25P2_HCPM_Metrocrest_Dallas_857_7625_phase_2_not_motorola_baseband_20181213_224616_control_channel";
|
||||
|
||||
|
||||
P25P1DecoderLSM decoder = new P25P1DecoderLSM();
|
||||
BinaryRecorder recorder = new BinaryRecorder(Path.of(path), output, Protocol.APCO25);
|
||||
decoder.setBufferListener(recorder.getReusableByteBufferListener());
|
||||
recorder.start();
|
||||
|
||||
File file = new File(path + input);
|
||||
|
||||
boolean running = true;
|
||||
|
||||
try(ComplexWaveSource source = new ComplexWaveSource(file))
|
||||
{
|
||||
decoder.setSampleRate(50000.0);
|
||||
source.setListener(decoder);
|
||||
source.start();
|
||||
|
||||
while(running)
|
||||
{
|
||||
source.next(200, true);
|
||||
}
|
||||
}
|
||||
catch(IOException e)
|
||||
{
|
||||
mLog.error("Error", e);
|
||||
running = false;
|
||||
}
|
||||
|
||||
recorder.stop();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2019 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 <http://www.gnu.org/licenses/>
|
||||
* *****************************************************************************
|
||||
* ****************************************************************************
|
||||
*/
|
||||
package io.github.dsheirer.module.decode.p25.phase1;
|
||||
|
||||
@@ -23,8 +22,8 @@ import io.github.dsheirer.dsp.psk.DQPSKGardnerDemodulatorInstrumented;
|
||||
import io.github.dsheirer.dsp.psk.InterpolatingSampleBufferInstrumented;
|
||||
import io.github.dsheirer.dsp.psk.SymbolDecisionData;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.sample.buffer.ReusableComplexBuffer;
|
||||
import io.github.dsheirer.sample.complex.Complex;
|
||||
import io.github.dsheirer.sample.complex.ComplexSamples;
|
||||
|
||||
public class P25P1DecoderLSMInstrumented extends P25P1DecoderLSM
|
||||
{
|
||||
@@ -32,7 +31,7 @@ public class P25P1DecoderLSMInstrumented extends P25P1DecoderLSM
|
||||
private Listener<Double> mPLLFrequencyListener;
|
||||
private Listener<Double> mSamplesPerSymbolListener;
|
||||
private Listener<Complex> mComplexSymbolListener;
|
||||
private Listener<ReusableComplexBuffer> mFilteredSymbolListener;
|
||||
private Listener<ComplexSamples> mFilteredSymbolListener;
|
||||
private Listener<SymbolDecisionData> mSymbolDecisionDataListener;
|
||||
|
||||
/**
|
||||
@@ -46,19 +45,27 @@ public class P25P1DecoderLSMInstrumented extends P25P1DecoderLSM
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides the filter method so that we can capture the filtered samples for instrumentation
|
||||
* Primary method for receiving incoming channel samples
|
||||
*/
|
||||
protected ReusableComplexBuffer filter(ReusableComplexBuffer reusableComplexBuffer)
|
||||
@Override
|
||||
public void receive(ComplexSamples samples)
|
||||
{
|
||||
ReusableComplexBuffer filtered = super.filter(reusableComplexBuffer);
|
||||
mMessageFramer.setCurrentTime(System.currentTimeMillis());
|
||||
|
||||
//The filter will decrement the user count when finished
|
||||
float[] i = mIBasebandFilter.filter(samples.i());
|
||||
float[] q = mQBasebandFilter.filter(samples.q());
|
||||
|
||||
if(mFilteredSymbolListener != null)
|
||||
{
|
||||
filtered.incrementUserCount();
|
||||
mFilteredSymbolListener.receive(filtered);
|
||||
mFilteredSymbolListener.receive(new ComplexSamples(i, q));
|
||||
}
|
||||
|
||||
return filtered;
|
||||
//Process the buffer for power measurements
|
||||
mPowerMonitor.process(i, q);
|
||||
|
||||
ComplexSamples amplified = mAGC.process(i, q);
|
||||
mQPSKDemodulator.receive(amplified);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -101,7 +108,7 @@ public class P25P1DecoderLSMInstrumented extends P25P1DecoderLSM
|
||||
((DQPSKGardnerDemodulatorInstrumented)mQPSKDemodulator).setPLLFrequencyListener(listener);
|
||||
}
|
||||
|
||||
public void setFilteredBufferListener(Listener<ReusableComplexBuffer> listener)
|
||||
public void setFilteredBufferListener(Listener<ComplexSamples> listener)
|
||||
{
|
||||
mFilteredSymbolListener = listener;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -36,7 +36,6 @@ import io.github.dsheirer.message.StuffBitsMessage;
|
||||
import io.github.dsheirer.message.SyncLossMessage;
|
||||
import io.github.dsheirer.module.ProcessingChain;
|
||||
import io.github.dsheirer.module.decode.DecoderType;
|
||||
import io.github.dsheirer.module.decode.ip.IPacket;
|
||||
import io.github.dsheirer.module.decode.ip.ipv4.IPV4Packet;
|
||||
import io.github.dsheirer.module.decode.ip.lrrp.LRRPPacket;
|
||||
import io.github.dsheirer.module.decode.ip.udp.UDPPacket;
|
||||
@@ -53,17 +52,16 @@ import io.github.dsheirer.protocol.Protocol;
|
||||
import io.github.dsheirer.record.AudioRecordingManager;
|
||||
import io.github.dsheirer.record.binary.BinaryReader;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.sample.buffer.ReusableByteBuffer;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.file.DirectoryStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
@@ -376,20 +374,15 @@ public class P25P1MessageFramer implements Listener<Dibit>, IP25P1DataUnitDetect
|
||||
*
|
||||
* @param buffer to process into a stream of dibits for processing.
|
||||
*/
|
||||
public void receive(ReusableByteBuffer buffer)
|
||||
public void receive(ByteBuffer buffer)
|
||||
{
|
||||
//Updates current timestamp to the timestamp from the incoming buffer
|
||||
setCurrentTime(buffer.getTimestamp());
|
||||
|
||||
for(byte value : buffer.getBytes())
|
||||
for(byte value : buffer.array())
|
||||
{
|
||||
for(int x = 0; x <= 3; x++)
|
||||
{
|
||||
receive(Dibit.parse(value, x));
|
||||
}
|
||||
}
|
||||
|
||||
buffer.decrementUserCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -545,7 +538,7 @@ public class P25P1MessageFramer implements Listener<Dibit>, IP25P1DataUnitDetect
|
||||
{
|
||||
while(reader.hasNext())
|
||||
{
|
||||
ReusableByteBuffer buffer = reader.next();
|
||||
ByteBuffer buffer = reader.next();
|
||||
messageFramer.receive(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,19 +25,20 @@ import io.github.dsheirer.module.decode.DecoderType;
|
||||
import io.github.dsheirer.module.decode.FeedbackDecoder;
|
||||
import io.github.dsheirer.sample.Broadcaster;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.sample.buffer.IReusableByteBufferProvider;
|
||||
import io.github.dsheirer.sample.buffer.IReusableComplexBufferListener;
|
||||
import io.github.dsheirer.sample.buffer.ReusableByteBuffer;
|
||||
import io.github.dsheirer.sample.buffer.ReusableComplexBuffer;
|
||||
import io.github.dsheirer.sample.buffer.IByteBufferProvider;
|
||||
import io.github.dsheirer.sample.complex.ComplexSamples;
|
||||
import io.github.dsheirer.sample.complex.IComplexSamplesListener;
|
||||
import io.github.dsheirer.source.ISourceEventListener;
|
||||
import io.github.dsheirer.source.ISourceEventProvider;
|
||||
import io.github.dsheirer.source.SourceEvent;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* Base P25 Phase 2 Decoder
|
||||
*/
|
||||
public abstract class P25P2Decoder extends FeedbackDecoder implements ISourceEventListener, ISourceEventProvider,
|
||||
IReusableComplexBufferListener, Listener<ReusableComplexBuffer>, IReusableByteBufferProvider
|
||||
IComplexSamplesListener, Listener<ComplexSamples>, IByteBufferProvider
|
||||
{
|
||||
private double mSampleRate;
|
||||
private Broadcaster<Dibit> mDibitBroadcaster = new Broadcaster<>();
|
||||
@@ -80,7 +81,7 @@ public abstract class P25P2Decoder extends FeedbackDecoder implements ISourceEve
|
||||
* Implements the IByteBufferProvider interface - delegates to the byte buffer assembler
|
||||
*/
|
||||
@Override
|
||||
public void setBufferListener(Listener<ReusableByteBuffer> listener)
|
||||
public void setBufferListener(Listener<ByteBuffer> listener)
|
||||
{
|
||||
mByteBufferAssembler.setBufferListener(listener);
|
||||
}
|
||||
@@ -89,7 +90,7 @@ public abstract class P25P2Decoder extends FeedbackDecoder implements ISourceEve
|
||||
* Implements the IByteBufferProvider interface - delegates to the byte buffer assembler
|
||||
*/
|
||||
@Override
|
||||
public void removeBufferListener(Listener<ReusableByteBuffer> listener)
|
||||
public void removeBufferListener(Listener<ByteBuffer> listener)
|
||||
{
|
||||
mByteBufferAssembler.removeBufferListener(listener);
|
||||
}
|
||||
@@ -163,7 +164,7 @@ public abstract class P25P2Decoder extends FeedbackDecoder implements ISourceEve
|
||||
* Listener interface to receive reusable complex buffers
|
||||
*/
|
||||
@Override
|
||||
public Listener<ReusableComplexBuffer> getReusableComplexBufferListener()
|
||||
public Listener<ComplexSamples> getComplexSamplesListener()
|
||||
{
|
||||
return P25P2Decoder.this;
|
||||
}
|
||||
|
||||
@@ -24,32 +24,25 @@ package io.github.dsheirer.module.decode.p25.phase2;
|
||||
import io.github.dsheirer.dsp.filter.FilterFactory;
|
||||
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;
|
||||
import io.github.dsheirer.dsp.gain.ComplexFeedForwardGainControl;
|
||||
import io.github.dsheirer.dsp.filter.fir.real.IRealFilter;
|
||||
import io.github.dsheirer.dsp.gain.complex.ComplexGainFactory;
|
||||
import io.github.dsheirer.dsp.gain.complex.IComplexGainControl;
|
||||
import io.github.dsheirer.dsp.psk.DQPSKGardnerDemodulator;
|
||||
import io.github.dsheirer.dsp.psk.InterpolatingSampleBuffer;
|
||||
import io.github.dsheirer.dsp.psk.pll.CostasLoop;
|
||||
import io.github.dsheirer.dsp.psk.pll.FrequencyCorrectionSyncMonitor;
|
||||
import io.github.dsheirer.dsp.psk.pll.PLLBandwidth;
|
||||
import io.github.dsheirer.dsp.squelch.PowerMonitor;
|
||||
import io.github.dsheirer.identifier.Form;
|
||||
import io.github.dsheirer.identifier.IdentifierUpdateListener;
|
||||
import io.github.dsheirer.identifier.IdentifierUpdateNotification;
|
||||
import io.github.dsheirer.message.IMessage;
|
||||
import io.github.dsheirer.message.SyncLossMessage;
|
||||
import io.github.dsheirer.module.decode.DecoderType;
|
||||
import io.github.dsheirer.module.decode.p25.phase2.enumeration.ScrambleParameters;
|
||||
import io.github.dsheirer.module.decode.p25.phase2.message.EncryptionSynchronizationSequence;
|
||||
import io.github.dsheirer.module.decode.p25.phase2.message.mac.MacMessage;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.sample.buffer.ReusableComplexBuffer;
|
||||
import io.github.dsheirer.sample.complex.ComplexSamples;
|
||||
import io.github.dsheirer.source.SourceEvent;
|
||||
import io.github.dsheirer.source.wave.ComplexWaveSource;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -64,9 +57,10 @@ public class P25P2DecoderHDQPSK extends P25P2Decoder implements IdentifierUpdate
|
||||
protected DQPSKGardnerDemodulator mQPSKDemodulator;
|
||||
protected CostasLoop mCostasLoop;
|
||||
protected P25P2MessageFramer mMessageFramer;
|
||||
private ComplexFeedForwardGainControl mAGC = new ComplexFeedForwardGainControl(32);
|
||||
protected IComplexGainControl mAGC = ComplexGainFactory.getComplexGainControl();
|
||||
private Map<Double,float[]> mBasebandFilters = new HashMap<>();
|
||||
private ComplexFIRFilter2 mBasebandFilter;
|
||||
protected IRealFilter mIBasebandFilter;
|
||||
protected IRealFilter mQBasebandFilter;
|
||||
private DecodeConfigP25Phase2 mDecodeConfigP25Phase2;
|
||||
private FrequencyCorrectionSyncMonitor mFrequencyCorrectionSyncMonitor;
|
||||
|
||||
@@ -81,7 +75,8 @@ public class P25P2DecoderHDQPSK extends P25P2Decoder implements IdentifierUpdate
|
||||
{
|
||||
super.setSampleRate(sampleRate);
|
||||
|
||||
mBasebandFilter = new ComplexFIRFilter2(getBasebandFilter());
|
||||
mIBasebandFilter = FilterFactory.getRealFilter(getBasebandFilter());
|
||||
mQBasebandFilter = FilterFactory.getRealFilter(getBasebandFilter());
|
||||
mCostasLoop = new CostasLoop(getSampleRate(), getSymbolRate());
|
||||
mCostasLoop.setPLLBandwidth(PLLBandwidth.BW_300);
|
||||
|
||||
@@ -113,35 +108,21 @@ public class P25P2DecoderHDQPSK extends P25P2Decoder implements IdentifierUpdate
|
||||
|
||||
/**
|
||||
* Primary method for processing incoming complex sample buffers
|
||||
* @param reusableComplexBuffer containing channelized complex samples
|
||||
* @param samples containing channelized complex samples
|
||||
*/
|
||||
@Override
|
||||
public void receive(ReusableComplexBuffer reusableComplexBuffer)
|
||||
public void receive(ComplexSamples samples)
|
||||
{
|
||||
//User accounting of the incoming buffer is handled by the filter
|
||||
ReusableComplexBuffer basebandFiltered = filter(reusableComplexBuffer);
|
||||
mMessageFramer.setCurrentTime(System.currentTimeMillis());
|
||||
|
||||
float[] i = mIBasebandFilter.filter(samples.i());
|
||||
float[] q = mQBasebandFilter.filter(samples.q());
|
||||
|
||||
//Process the buffer for power measurements
|
||||
mPowerMonitor.process(basebandFiltered);
|
||||
mPowerMonitor.process(i, q);
|
||||
|
||||
//User accounting of the incoming buffer is handled by the gain filter
|
||||
ReusableComplexBuffer gainApplied = mAGC.filter(basebandFiltered);
|
||||
|
||||
mMessageFramer.setCurrentTime(reusableComplexBuffer.getTimestamp());
|
||||
|
||||
//User accounting of the filtered buffer is handled by the demodulator
|
||||
mQPSKDemodulator.receive(gainApplied);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the complex buffer and returns a new reusable complex buffer with the filtered contents.
|
||||
* @param reusableComplexBuffer to filter
|
||||
* @return filtered complex buffer
|
||||
*/
|
||||
protected ReusableComplexBuffer filter(ReusableComplexBuffer reusableComplexBuffer)
|
||||
{
|
||||
//User accounting of the incoming buffer is handled by the filter
|
||||
return mBasebandFilter.filter(reusableComplexBuffer);
|
||||
ComplexSamples amplified = mAGC.process(i, q);
|
||||
mQPSKDemodulator.receive(amplified);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -224,83 +205,6 @@ public class P25P2DecoderHDQPSK extends P25P2Decoder implements IdentifierUpdate
|
||||
}
|
||||
}
|
||||
|
||||
public static class MessageErrorListener implements Listener<IMessage>
|
||||
{
|
||||
private int mMessageCount = 0;
|
||||
private int mBitErrorCount = 0;
|
||||
|
||||
@Override
|
||||
public void receive(IMessage iMessage)
|
||||
{
|
||||
if(!(iMessage instanceof SyncLossMessage) && !(iMessage instanceof EncryptionSynchronizationSequence))
|
||||
{
|
||||
mMessageCount++;
|
||||
}
|
||||
|
||||
if(iMessage instanceof MacMessage)
|
||||
{
|
||||
mBitErrorCount += ((MacMessage)iMessage).getBitErrorCount();
|
||||
}
|
||||
|
||||
mLog.debug("[" + mMessageCount + " | " + mBitErrorCount + "] " + iMessage.toString());
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args)
|
||||
{
|
||||
// String path = "/media/denny/500G1EXT4/RadioRecordings/APCO25P2/CNYICC/";
|
||||
// String input = "CNYICC_Rome_CNYICC_154_250_baseband_20190322_180331_good.wav";
|
||||
// String output = "CNYICC_ROME_154_250_8";
|
||||
|
||||
String path = "/home/denny/SDRTrunk/recordings/";
|
||||
String input = "CNYICC_Site_P25P2_151.37_baseband_20200221_110306.wav";
|
||||
String output = "P25P2_test";
|
||||
|
||||
ScrambleParameters scrambleParameters = new ScrambleParameters(781824, 686, 677); //CNYICC
|
||||
DecodeConfigP25Phase2 decodeConfigP25Phase2 = new DecodeConfigP25Phase2();
|
||||
decodeConfigP25Phase2.setScrambleParameters(scrambleParameters);
|
||||
|
||||
P25P2DecoderHDQPSK decoder = new P25P2DecoderHDQPSK(decodeConfigP25Phase2);
|
||||
decoder.setMessageListener(new MessageErrorListener());
|
||||
|
||||
// decoder.setMessageListener(new Listener<IMessage>()
|
||||
// {
|
||||
// @Override
|
||||
// public void receive(IMessage iMessage)
|
||||
// {
|
||||
// mLog.debug(iMessage.toString());
|
||||
// }
|
||||
// });
|
||||
decoder.start();
|
||||
|
||||
// BinaryRecorder recorder = new BinaryRecorder(Path.of(path), output, Protocol.APCO25_PHASE2);
|
||||
// decoder.setBufferListener(recorder.getReusableByteBufferListener());
|
||||
// recorder.start();
|
||||
|
||||
File file = new File(path + input);
|
||||
|
||||
boolean running = true;
|
||||
|
||||
try(ComplexWaveSource source = new ComplexWaveSource(file))
|
||||
{
|
||||
decoder.setSampleRate(50000.0);
|
||||
source.setListener(decoder);
|
||||
source.start();
|
||||
|
||||
while(running)
|
||||
{
|
||||
source.next(200, true);
|
||||
}
|
||||
}
|
||||
catch(IOException e)
|
||||
{
|
||||
mLog.error("Error", e);
|
||||
running = false;
|
||||
}
|
||||
|
||||
// recorder.stop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Listener<IdentifierUpdateNotification> getIdentifierUpdateListener()
|
||||
{
|
||||
|
||||
@@ -1,23 +1,20 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2022 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.module.decode.p25.phase2;
|
||||
|
||||
@@ -25,8 +22,8 @@ import io.github.dsheirer.dsp.psk.DQPSKGardnerDemodulatorInstrumented;
|
||||
import io.github.dsheirer.dsp.psk.InterpolatingSampleBufferInstrumented;
|
||||
import io.github.dsheirer.dsp.psk.SymbolDecisionData;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.sample.buffer.ReusableComplexBuffer;
|
||||
import io.github.dsheirer.sample.complex.Complex;
|
||||
import io.github.dsheirer.sample.complex.ComplexSamples;
|
||||
|
||||
public class P25P2DecoderHDQPSKInstrumented extends P25P2DecoderHDQPSK
|
||||
{
|
||||
@@ -34,7 +31,7 @@ public class P25P2DecoderHDQPSKInstrumented extends P25P2DecoderHDQPSK
|
||||
private Listener<Double> mPLLFrequencyListener;
|
||||
private Listener<Double> mSamplesPerSymbolListener;
|
||||
private Listener<Complex> mComplexSymbolListener;
|
||||
private Listener<ReusableComplexBuffer> mFilteredSymbolListener;
|
||||
private Listener<ComplexSamples> mFilteredSymbolListener;
|
||||
private Listener<SymbolDecisionData> mSymbolDecisionDataListener;
|
||||
|
||||
/**
|
||||
@@ -55,19 +52,27 @@ public class P25P2DecoderHDQPSKInstrumented extends P25P2DecoderHDQPSK
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides the filter method so that we can capture the filtered samples for instrumentation
|
||||
* Primary method for processing incoming complex sample buffers
|
||||
* @param samples containing channelized complex samples
|
||||
*/
|
||||
protected ReusableComplexBuffer filter(ReusableComplexBuffer reusableComplexBuffer)
|
||||
@Override
|
||||
public void receive(ComplexSamples samples)
|
||||
{
|
||||
ReusableComplexBuffer filtered = super.filter(reusableComplexBuffer);
|
||||
mMessageFramer.setCurrentTime(System.currentTimeMillis());
|
||||
|
||||
float[] i = mIBasebandFilter.filter(samples.i());
|
||||
float[] q = mQBasebandFilter.filter(samples.q());
|
||||
|
||||
if(mFilteredSymbolListener != null)
|
||||
{
|
||||
filtered.incrementUserCount();
|
||||
mFilteredSymbolListener.receive(filtered);
|
||||
mFilteredSymbolListener.receive(samples);
|
||||
}
|
||||
|
||||
return filtered;
|
||||
//Process the buffer for power measurements
|
||||
mPowerMonitor.process(i, q);
|
||||
|
||||
ComplexSamples amplified = mAGC.process(i, q);
|
||||
mQPSKDemodulator.receive(amplified);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -119,7 +124,7 @@ public class P25P2DecoderHDQPSKInstrumented extends P25P2DecoderHDQPSK
|
||||
getDemodulator().setPLLFrequencyListener(listener);
|
||||
}
|
||||
|
||||
public void setFilteredBufferListener(Listener<ReusableComplexBuffer> listener)
|
||||
public void setFilteredBufferListener(Listener<ComplexSamples> listener)
|
||||
{
|
||||
mFilteredSymbolListener = listener;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2020 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
|
||||
@@ -38,12 +38,12 @@ import io.github.dsheirer.preference.UserPreferences;
|
||||
import io.github.dsheirer.record.AudioRecordingManager;
|
||||
import io.github.dsheirer.record.binary.BinaryReader;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.sample.buffer.ReusableByteBuffer;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.file.DirectoryStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
@@ -190,20 +190,15 @@ public class P25P2MessageFramer implements Listener<Dibit>
|
||||
*
|
||||
* @param buffer to process into a stream of dibits for processing.
|
||||
*/
|
||||
public void receive(ReusableByteBuffer buffer)
|
||||
public void receive(ByteBuffer buffer)
|
||||
{
|
||||
//TODO: set timestamp in super frame detector
|
||||
setCurrentTime(buffer.getTimestamp());
|
||||
|
||||
for(byte value : buffer.getBytes())
|
||||
for(byte value : buffer.array())
|
||||
{
|
||||
for(int x = 0; x <= 3; x++)
|
||||
{
|
||||
receive(Dibit.parse(value, x));
|
||||
}
|
||||
}
|
||||
|
||||
buffer.decrementUserCount();
|
||||
}
|
||||
|
||||
public static void main(String[] args)
|
||||
@@ -298,7 +293,7 @@ public class P25P2MessageFramer implements Listener<Dibit>
|
||||
{
|
||||
while(reader.hasNext())
|
||||
{
|
||||
ReusableByteBuffer buffer = reader.next();
|
||||
ByteBuffer buffer = reader.next();
|
||||
messageFramer.receive(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 <http://www.gnu.org/licenses/>
|
||||
* *****************************************************************************
|
||||
* ****************************************************************************
|
||||
*/
|
||||
package io.github.dsheirer.module.decode.passport;
|
||||
|
||||
@@ -26,10 +25,9 @@ import io.github.dsheirer.module.decode.Decoder;
|
||||
import io.github.dsheirer.module.decode.DecoderType;
|
||||
import io.github.dsheirer.module.decode.config.DecodeConfiguration;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.sample.buffer.IReusableBufferListener;
|
||||
import io.github.dsheirer.sample.buffer.ReusableFloatBuffer;
|
||||
import io.github.dsheirer.sample.real.IRealBufferListener;
|
||||
|
||||
public class PassportDecoder extends Decoder implements IReusableBufferListener, Listener<ReusableFloatBuffer>
|
||||
public class PassportDecoder extends Decoder implements IRealBufferListener, Listener<float[]>
|
||||
{
|
||||
public static final int PASSPORT_MESSAGE_LENGTH = 68;
|
||||
|
||||
@@ -55,15 +53,15 @@ public class PassportDecoder extends Decoder implements IReusableBufferListener,
|
||||
}
|
||||
|
||||
@Override
|
||||
public Listener<ReusableFloatBuffer> getReusableBufferListener()
|
||||
public Listener<float[]> getBufferListener()
|
||||
{
|
||||
return mLTRDecoder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive(ReusableFloatBuffer reusableFloatBuffer)
|
||||
public void receive(float[] realBuffer)
|
||||
{
|
||||
mLTRDecoder.receive(reusableFloatBuffer);
|
||||
mLTRDecoder.receive(realBuffer);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -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,13 +14,12 @@
|
||||
*
|
||||
* 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.module.decode.tait;
|
||||
|
||||
import io.github.dsheirer.dsp.afsk.AFSK1200DecoderInstrumented;
|
||||
import io.github.dsheirer.gui.instrument.chart.IInstrumentedAFSK1200Decoder;
|
||||
import io.github.dsheirer.sample.buffer.ReusableFloatBuffer;
|
||||
import javafx.beans.property.SimpleIntegerProperty;
|
||||
|
||||
/**
|
||||
@@ -48,9 +46,9 @@ public class Tait1200DecoderInstrumented extends Tait1200Decoder implements IIns
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive(ReusableFloatBuffer reusableFloatBuffer)
|
||||
public void receive(float[] realBuffer)
|
||||
{
|
||||
super.receive(reusableFloatBuffer);
|
||||
super.receive(realBuffer);
|
||||
|
||||
bufferCount.setValue(bufferCount.intValue() + 1);
|
||||
}
|
||||
|
||||
@@ -1,36 +1,36 @@
|
||||
/*******************************************************************************
|
||||
* 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 <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.module.demodulate.am;
|
||||
|
||||
import io.github.dsheirer.dsp.am.AMDemodulator;
|
||||
import io.github.dsheirer.dsp.filter.FilterFactory;
|
||||
import io.github.dsheirer.dsp.filter.Window;
|
||||
import io.github.dsheirer.dsp.filter.Window.WindowType;
|
||||
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;
|
||||
import io.github.dsheirer.dsp.filter.fir.real.RealFIRFilter2;
|
||||
import io.github.dsheirer.dsp.filter.fir.real.IRealFilter;
|
||||
import io.github.dsheirer.dsp.filter.resample.RealResampler;
|
||||
import io.github.dsheirer.dsp.gain.AutomaticGainControl;
|
||||
import io.github.dsheirer.dsp.window.WindowType;
|
||||
import io.github.dsheirer.module.Module;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.sample.buffer.IReusableBufferProvider;
|
||||
import io.github.dsheirer.sample.buffer.IReusableComplexBufferListener;
|
||||
import io.github.dsheirer.sample.buffer.ReusableComplexBuffer;
|
||||
import io.github.dsheirer.sample.buffer.ReusableFloatBuffer;
|
||||
import io.github.dsheirer.sample.complex.ComplexSamples;
|
||||
import io.github.dsheirer.sample.complex.IComplexSamplesListener;
|
||||
import io.github.dsheirer.sample.real.IRealBufferProvider;
|
||||
import io.github.dsheirer.source.ISourceEventListener;
|
||||
import io.github.dsheirer.source.SourceEvent;
|
||||
import org.slf4j.Logger;
|
||||
@@ -39,23 +39,24 @@ import org.slf4j.LoggerFactory;
|
||||
/**
|
||||
* AM Demodulator Module for demodulating complex baseband channel sample buffers and producing demodulated audio
|
||||
* at a specified output sample rate, normally 8 kHz.
|
||||
*
|
||||
* <p>
|
||||
* This module requires a sample rate SourceEvent prior to processing baseband sample buffers in order to configure
|
||||
* internal filters and the resampler.
|
||||
*/
|
||||
public class AMDemodulatorModule extends Module implements ISourceEventListener, IReusableComplexBufferListener,
|
||||
IReusableBufferProvider, Listener<ReusableComplexBuffer>
|
||||
public class AMDemodulatorModule extends Module
|
||||
implements ISourceEventListener, IComplexSamplesListener, IRealBufferProvider, Listener<ComplexSamples>
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger(AMDemodulatorModule.class);
|
||||
private static float[] mLowPassFilterTaps;
|
||||
private ComplexFIRFilter2 mIQFilter;
|
||||
private IRealFilter mIBasebandFilter;
|
||||
private IRealFilter mQBasebandFilter;
|
||||
|
||||
private AMDemodulator mDemodulator;
|
||||
private RealFIRFilter2 mLowPassFilter;
|
||||
private IRealFilter mLowPassFilter;
|
||||
private AutomaticGainControl mAGC = new AutomaticGainControl();
|
||||
private double mChannelBandwidth;
|
||||
private double mOutputSampleRate;
|
||||
private RealResampler mResampler;
|
||||
private Listener<ReusableFloatBuffer> mResampledReusableBufferListener;
|
||||
private Listener<float[]> mResampledReusableBufferListener;
|
||||
private SourceEventProcessor mSourceEventProcessor = new SourceEventProcessor();
|
||||
|
||||
/**
|
||||
@@ -71,101 +72,81 @@ public class AMDemodulatorModule extends Module implements ISourceEventListener,
|
||||
|
||||
mDemodulator = new AMDemodulator(500.0f);
|
||||
|
||||
if(mLowPassFilterTaps == null)
|
||||
float[] lowPassCoefficients = null;
|
||||
|
||||
FIRFilterSpecification specification = FIRFilterSpecification.lowPassBuilder().sampleRate(mOutputSampleRate)
|
||||
.gridDensity(16).oddLength(true).passBandCutoff(3000).passBandAmplitude(1.0).passBandRipple(0.01)
|
||||
.stopBandStart(3500).stopBandAmplitude(0.0).stopBandRipple(0.028).build();
|
||||
|
||||
try
|
||||
{
|
||||
FIRFilterSpecification specification = FIRFilterSpecification.lowPassBuilder()
|
||||
.sampleRate(mOutputSampleRate)
|
||||
.gridDensity(16)
|
||||
.oddLength(true)
|
||||
.passBandCutoff(3000)
|
||||
.passBandAmplitude(1.0)
|
||||
.passBandRipple(0.01)
|
||||
.stopBandStart(3500)
|
||||
.stopBandAmplitude(0.0)
|
||||
.stopBandRipple(0.028)
|
||||
.build();
|
||||
|
||||
try
|
||||
{
|
||||
mLowPassFilterTaps = FilterFactory.getTaps(specification);
|
||||
}
|
||||
catch(FilterDesignException fde)
|
||||
{
|
||||
mLog.error("Couldn't design AM demodulator remez filter for sample rate [" + mOutputSampleRate +
|
||||
"] pass frequency [3000] and stop frequency [3500] - using sinc filter");
|
||||
}
|
||||
|
||||
if(mLowPassFilterTaps == null)
|
||||
{
|
||||
mLowPassFilterTaps = FilterFactory.getLowPass(mOutputSampleRate, 3000, 3500,
|
||||
60, WindowType.HAMMING, true);
|
||||
}
|
||||
lowPassCoefficients = FilterFactory.getTaps(specification);
|
||||
}
|
||||
catch(FilterDesignException fde)
|
||||
{
|
||||
mLog.error("Couldn't design AM demodulator remez filter for sample rate [" + mOutputSampleRate
|
||||
+ "] pass frequency [3000] and stop frequency [3500] - using sinc filter");
|
||||
}
|
||||
|
||||
mLowPassFilter = new RealFIRFilter2(mLowPassFilterTaps);
|
||||
if(lowPassCoefficients == null)
|
||||
{
|
||||
lowPassCoefficients = FilterFactory.getLowPass(mOutputSampleRate, 3000, 3500, 60, WindowType.HAMMING, true);
|
||||
}
|
||||
|
||||
mLowPassFilter = FilterFactory.getRealFilter(lowPassCoefficients);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive(ReusableComplexBuffer basebandBuffer)
|
||||
@Override public void receive(ComplexSamples samples)
|
||||
{
|
||||
ReusableComplexBuffer filteredBuffer = mIQFilter.filter(basebandBuffer);
|
||||
ReusableFloatBuffer demodulated = mDemodulator.demodulate(filteredBuffer);
|
||||
float[] i = mIBasebandFilter.filter(samples.i());
|
||||
float[] q = mQBasebandFilter.filter(samples.q());
|
||||
float[] demodulated = mDemodulator.demodulate(i, q);
|
||||
mResampler.resample(demodulated);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes resampled audio buffers produced by the resampler.
|
||||
*
|
||||
* @param resampledAudio buffer to process.
|
||||
*/
|
||||
private void processResampledAudio(ReusableFloatBuffer resampledAudio)
|
||||
private void processResampledAudio(float[] resampledAudio)
|
||||
{
|
||||
if(mResampledReusableBufferListener != null)
|
||||
{
|
||||
ReusableFloatBuffer filteredAudio = mLowPassFilter.filter(resampledAudio);
|
||||
ReusableFloatBuffer gainApplied = mAGC.process(filteredAudio);
|
||||
mResampledReusableBufferListener.receive(gainApplied);
|
||||
}
|
||||
else
|
||||
{
|
||||
resampledAudio.decrementUserCount();
|
||||
float[] filtered = mLowPassFilter.filter(resampledAudio);
|
||||
float[] amplified = mAGC.process(filtered);
|
||||
mResampledReusableBufferListener.receive(amplified);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Listener<ReusableComplexBuffer> getReusableComplexBufferListener()
|
||||
@Override public Listener<ComplexSamples> getComplexSamplesListener()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset()
|
||||
@Override public void reset()
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start()
|
||||
@Override public void start()
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop()
|
||||
@Override public void stop()
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBufferListener(Listener<ReusableFloatBuffer> listener)
|
||||
@Override public void setBufferListener(Listener<float[]> listener)
|
||||
{
|
||||
mResampledReusableBufferListener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeBufferListener()
|
||||
@Override public void removeBufferListener()
|
||||
{
|
||||
mResampledReusableBufferListener = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Listener<SourceEvent> getSourceEventListener()
|
||||
@Override public Listener<SourceEvent> getSourceEventListener()
|
||||
{
|
||||
return mSourceEventProcessor;
|
||||
}
|
||||
@@ -175,73 +156,58 @@ public class AMDemodulatorModule extends Module implements ISourceEventListener,
|
||||
*/
|
||||
public class SourceEventProcessor implements Listener<SourceEvent>
|
||||
{
|
||||
@Override
|
||||
public void receive(SourceEvent sourceEvent)
|
||||
@Override public void receive(SourceEvent sourceEvent)
|
||||
{
|
||||
if(sourceEvent.getEvent() == SourceEvent.Event.NOTIFICATION_SAMPLE_RATE_CHANGE)
|
||||
{
|
||||
if(mIQFilter != null)
|
||||
if(mIBasebandFilter != null || mQBasebandFilter != null)
|
||||
{
|
||||
mIQFilter.dispose();
|
||||
mIQFilter = null;
|
||||
mIBasebandFilter = null;
|
||||
mQBasebandFilter = null;
|
||||
}
|
||||
|
||||
double sampleRate = sourceEvent.getValue().doubleValue();
|
||||
|
||||
if((sampleRate < (2.0 * mChannelBandwidth)))
|
||||
{
|
||||
throw new IllegalStateException("AM Demodulator with channel bandwidth [" + mChannelBandwidth +
|
||||
"] requires a channel sample rate of [" + (2.0 * mChannelBandwidth + "] - sample rate of [" +
|
||||
sampleRate + "] is not supported"));
|
||||
throw new IllegalStateException("AM Demodulator with channel bandwidth [" + mChannelBandwidth
|
||||
+ "] requires a channel sample rate of [" + (2.0 * mChannelBandwidth
|
||||
+ "] - sample rate of [" + sampleRate + "] is not supported"));
|
||||
}
|
||||
|
||||
double cutoff = sampleRate / 4.0;
|
||||
int passBandStop = (int)cutoff - 500;
|
||||
int stopBandStart = (int)cutoff + 500;
|
||||
|
||||
float[] filterTaps = null;
|
||||
float[] coefficients = null;
|
||||
|
||||
FIRFilterSpecification specification = FIRFilterSpecification.lowPassBuilder()
|
||||
.sampleRate(sampleRate)
|
||||
.gridDensity(16)
|
||||
.oddLength(true)
|
||||
.passBandCutoff(passBandStop)
|
||||
.passBandAmplitude(1.0)
|
||||
.passBandRipple(0.01)
|
||||
.stopBandStart(stopBandStart)
|
||||
.stopBandAmplitude(0.0)
|
||||
.stopBandRipple(0.028) //Approximately 60 dB attenuation
|
||||
.build();
|
||||
FIRFilterSpecification specification = FIRFilterSpecification.lowPassBuilder().sampleRate(sampleRate)
|
||||
.gridDensity(16).oddLength(true).passBandCutoff(passBandStop).passBandAmplitude(1.0)
|
||||
.passBandRipple(0.01).stopBandStart(stopBandStart).stopBandAmplitude(0.0)
|
||||
.stopBandRipple(0.028) //Approximately 60 dB attenuation
|
||||
.build();
|
||||
|
||||
try
|
||||
{
|
||||
filterTaps = FilterFactory.getTaps(specification);
|
||||
coefficients = FilterFactory.getTaps(specification);
|
||||
}
|
||||
catch(FilterDesignException fde)
|
||||
{
|
||||
mLog.error("Couldn't design FM demodulator remez filter for sample rate [" + sampleRate +
|
||||
"] pass frequency [" + passBandStop + "] and stop frequency [" + stopBandStart +
|
||||
"] - using sinc filter");
|
||||
mLog.error("Couldn't design AM demodulator remez filter for sample rate [" + sampleRate
|
||||
+ "] pass frequency [" + passBandStop + "] and stop frequency [" + stopBandStart
|
||||
+ "] - using sinc filter");
|
||||
}
|
||||
|
||||
if(filterTaps == null)
|
||||
if(coefficients == null)
|
||||
{
|
||||
filterTaps = FilterFactory.getLowPass(sampleRate, passBandStop, stopBandStart, 60,
|
||||
Window.WindowType.HAMMING, true);
|
||||
coefficients = FilterFactory.getLowPass(sampleRate, passBandStop, stopBandStart, 60,
|
||||
WindowType.HAMMING, true);
|
||||
}
|
||||
|
||||
mIQFilter = new ComplexFIRFilter2(filterTaps);
|
||||
|
||||
mResampler = new RealResampler(sampleRate, mOutputSampleRate, 2000, 1000);
|
||||
|
||||
mResampler.setListener(new Listener<ReusableFloatBuffer>()
|
||||
{
|
||||
@Override
|
||||
public void receive(ReusableFloatBuffer resampledAudioBuffer)
|
||||
{
|
||||
processResampledAudio(resampledAudioBuffer);
|
||||
}
|
||||
});
|
||||
mIBasebandFilter = FilterFactory.getRealFilter(coefficients);
|
||||
mQBasebandFilter = FilterFactory.getRealFilter(coefficients);
|
||||
mResampler = new RealResampler(sampleRate, mOutputSampleRate, 8192, 512);
|
||||
mResampler.setListener(resampledBuffer -> processResampledAudio(resampledBuffer));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,34 +1,36 @@
|
||||
/*******************************************************************************
|
||||
* 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 <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.module.demodulate.fm;
|
||||
|
||||
import io.github.dsheirer.dsp.filter.FilterFactory;
|
||||
import io.github.dsheirer.dsp.filter.Window;
|
||||
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;
|
||||
import io.github.dsheirer.dsp.filter.fir.real.IRealFilter;
|
||||
import io.github.dsheirer.dsp.filter.resample.RealResampler;
|
||||
import io.github.dsheirer.dsp.fm.FMDemodulator;
|
||||
import io.github.dsheirer.dsp.fm.ScalarFMDemodulator;
|
||||
import io.github.dsheirer.dsp.squelch.PowerMonitor;
|
||||
import io.github.dsheirer.dsp.window.WindowType;
|
||||
import io.github.dsheirer.module.Module;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.sample.buffer.IReusableBufferProvider;
|
||||
import io.github.dsheirer.sample.buffer.IReusableComplexBufferListener;
|
||||
import io.github.dsheirer.sample.buffer.ReusableComplexBuffer;
|
||||
import io.github.dsheirer.sample.buffer.ReusableFloatBuffer;
|
||||
import io.github.dsheirer.sample.complex.ComplexSamples;
|
||||
import io.github.dsheirer.sample.complex.IComplexSamplesListener;
|
||||
import io.github.dsheirer.sample.real.IRealBufferProvider;
|
||||
import io.github.dsheirer.source.ISourceEventListener;
|
||||
import io.github.dsheirer.source.ISourceEventProvider;
|
||||
import io.github.dsheirer.source.SourceEvent;
|
||||
@@ -41,16 +43,17 @@ import org.slf4j.LoggerFactory;
|
||||
* Note: no filtering is applied to the demodulated audio.
|
||||
*/
|
||||
public class FMDemodulatorModule extends Module implements ISourceEventListener, ISourceEventProvider,
|
||||
IReusableComplexBufferListener, Listener<ReusableComplexBuffer>, IReusableBufferProvider
|
||||
IComplexSamplesListener, Listener<ComplexSamples>, IRealBufferProvider
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger(FMDemodulatorModule.class);
|
||||
|
||||
private ComplexFIRFilter2 mIQFilter;
|
||||
private FMDemodulator mDemodulator = new FMDemodulator();
|
||||
private IRealFilter mIBasebandFilter;
|
||||
private IRealFilter mQBasebandFilter;
|
||||
private ScalarFMDemodulator mDemodulator = new ScalarFMDemodulator();
|
||||
private PowerMonitor mPowerMonitor = new PowerMonitor();
|
||||
private RealResampler mResampler;
|
||||
private SourceEventProcessor mSourceEventProcessor = new SourceEventProcessor();
|
||||
private Listener<ReusableFloatBuffer> mResampledReusableBufferListener;
|
||||
private Listener<float[]> mResampledBufferListener;
|
||||
private double mChannelBandwidth;
|
||||
private double mOutputSampleRate;
|
||||
|
||||
@@ -68,7 +71,7 @@ public class FMDemodulatorModule extends Module implements ISourceEventListener,
|
||||
}
|
||||
|
||||
@Override
|
||||
public Listener<ReusableComplexBuffer> getReusableComplexBufferListener()
|
||||
public Listener<ComplexSamples> getComplexSamplesListener()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
@@ -82,12 +85,6 @@ public class FMDemodulatorModule extends Module implements ISourceEventListener,
|
||||
@Override
|
||||
public void dispose()
|
||||
{
|
||||
if(mIQFilter != null)
|
||||
{
|
||||
mIQFilter.dispose();
|
||||
mIQFilter = null;
|
||||
}
|
||||
|
||||
mDemodulator.dispose();
|
||||
mDemodulator = null;
|
||||
}
|
||||
@@ -109,38 +106,35 @@ public class FMDemodulatorModule extends Module implements ISourceEventListener,
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBufferListener(Listener<ReusableFloatBuffer> listener)
|
||||
public void setBufferListener(Listener<float[]> listener)
|
||||
{
|
||||
mResampledReusableBufferListener = listener;
|
||||
mResampledBufferListener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeBufferListener()
|
||||
{
|
||||
mResampledReusableBufferListener = null;
|
||||
mResampledBufferListener = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive(ReusableComplexBuffer reusableComplexBuffer)
|
||||
public void receive(ComplexSamples samples)
|
||||
{
|
||||
if(mIQFilter == null)
|
||||
if(mIBasebandFilter == null || mQBasebandFilter == null)
|
||||
{
|
||||
reusableComplexBuffer.decrementUserCount();
|
||||
throw new IllegalStateException("FM demodulator module must receive a sample rate change source " +
|
||||
"event before it can process complex sample buffers");
|
||||
}
|
||||
|
||||
ReusableComplexBuffer basebandFilteredBuffer = mIQFilter.filter(reusableComplexBuffer);
|
||||
mPowerMonitor.process(basebandFilteredBuffer);
|
||||
ReusableFloatBuffer demodulatedBuffer = mDemodulator.demodulate(basebandFilteredBuffer);
|
||||
float[] i = mIBasebandFilter.filter(samples.i());
|
||||
float[] q = mQBasebandFilter.filter(samples.q());
|
||||
|
||||
mPowerMonitor.process(i, q);
|
||||
float[] demodulated = mDemodulator.demodulate(i, q);
|
||||
|
||||
if(mResampler != null)
|
||||
{
|
||||
mResampler.resample(demodulatedBuffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
demodulatedBuffer.decrementUserCount();
|
||||
mResampler.resample(demodulated);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,12 +160,6 @@ public class FMDemodulatorModule extends Module implements ISourceEventListener,
|
||||
{
|
||||
if(sourceEvent.getEvent() == SourceEvent.Event.NOTIFICATION_SAMPLE_RATE_CHANGE)
|
||||
{
|
||||
if(mIQFilter != null)
|
||||
{
|
||||
mIQFilter.dispose();
|
||||
mIQFilter = null;
|
||||
}
|
||||
|
||||
double sampleRate = sourceEvent.getValue().doubleValue();
|
||||
|
||||
if((sampleRate < (2.0 * mChannelBandwidth)))
|
||||
@@ -187,7 +175,7 @@ public class FMDemodulatorModule extends Module implements ISourceEventListener,
|
||||
int passBandStop = (int)cutoff - 500;
|
||||
int stopBandStart = (int)cutoff + 500;
|
||||
|
||||
float[] filterTaps = null;
|
||||
float[] coefficients = null;
|
||||
|
||||
FIRFilterSpecification specification = FIRFilterSpecification.lowPassBuilder()
|
||||
.sampleRate(sampleRate)
|
||||
@@ -203,7 +191,7 @@ public class FMDemodulatorModule extends Module implements ISourceEventListener,
|
||||
|
||||
try
|
||||
{
|
||||
filterTaps = FilterFactory.getTaps(specification);
|
||||
coefficients = FilterFactory.getTaps(specification);
|
||||
}
|
||||
catch(FilterDesignException fde)
|
||||
{
|
||||
@@ -212,29 +200,20 @@ public class FMDemodulatorModule extends Module implements ISourceEventListener,
|
||||
"] - using sinc filter");
|
||||
}
|
||||
|
||||
if(filterTaps == null)
|
||||
if(coefficients == null)
|
||||
{
|
||||
filterTaps = FilterFactory.getLowPass(sampleRate, passBandStop, stopBandStart, 60,
|
||||
Window.WindowType.HAMMING, true);
|
||||
coefficients = FilterFactory.getLowPass(sampleRate, passBandStop, stopBandStart, 60,
|
||||
WindowType.HAMMING, true);
|
||||
}
|
||||
|
||||
mIQFilter = new ComplexFIRFilter2(filterTaps);
|
||||
mIBasebandFilter = FilterFactory.getRealFilter(coefficients);
|
||||
mQBasebandFilter = FilterFactory.getRealFilter(coefficients);
|
||||
mResampler = new RealResampler(sampleRate, mOutputSampleRate, 8192, 512);
|
||||
|
||||
mResampler = new RealResampler(sampleRate, mOutputSampleRate, 2000, 1000);
|
||||
|
||||
mResampler.setListener(new Listener<ReusableFloatBuffer>()
|
||||
{
|
||||
@Override
|
||||
public void receive(ReusableFloatBuffer reusableFloatBuffer)
|
||||
mResampler.setListener(resampledBuffer -> {
|
||||
if(mResampledBufferListener != null)
|
||||
{
|
||||
if(mResampledReusableBufferListener != null)
|
||||
{
|
||||
mResampledReusableBufferListener.receive(reusableFloatBuffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
reusableFloatBuffer.decrementUserCount();
|
||||
}
|
||||
mResampledBufferListener.receive(resampledBuffer);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user