mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-12 16:47:31 -05:00
#1565 updates LTR, LTRNet and Passport decoders with new slope decoder. Resolves issue with LTR Standard decoder state that was combining individual calls to the same talkgroup and causing excessively long calls. (#1567)
Co-authored-by: Dennis Sheirer <dsheirer@github.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2022 Dennis Sheirer
|
||||
* Copyright (C) 2014-2023 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
|
||||
|
||||
@@ -276,8 +276,7 @@ public class BinaryMessage extends BitSet
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new BitSetFullException("bitset is full -- contains " +
|
||||
(mPointer + 1) + "bits");
|
||||
throw new BitSetFullException("bitset is full -- contains [" + mPointer + "/" + size() + "] bits");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -132,12 +132,10 @@ public enum SyncPattern
|
||||
true, true, false, false //1100 0xC
|
||||
}),
|
||||
|
||||
/* Sync (0x158) = 1101011000 */
|
||||
/* Sync (0x158) = 101011000 */
|
||||
PASSPORT( new boolean[]
|
||||
{
|
||||
true, //0001 0x1
|
||||
false, true, false, true, //0101 0x5
|
||||
true, false, false, false //1000 0x8
|
||||
true,false,true,false,true,true,false,false,false
|
||||
} ),
|
||||
|
||||
LTR_STANDARD_OSW( new boolean[]
|
||||
|
||||
@@ -22,6 +22,8 @@ package io.github.dsheirer.buffer;
|
||||
import io.github.dsheirer.sample.SampleUtils;
|
||||
import io.github.dsheirer.sample.complex.ComplexSamples;
|
||||
import io.github.dsheirer.sample.complex.InterleavedComplexSamples;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
@@ -30,6 +32,7 @@ import java.util.Iterator;
|
||||
public class FloatNativeBuffer extends AbstractNativeBuffer
|
||||
{
|
||||
private float[] mInterleavedComplexSamples;
|
||||
private static final int BUFFER_SIZE = 2048;
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
@@ -81,48 +84,51 @@ public class FloatNativeBuffer extends AbstractNativeBuffer
|
||||
|
||||
private class ComplexSamplesIterator implements Iterator<ComplexSamples>
|
||||
{
|
||||
private boolean mEmpty;
|
||||
private int mBufferPointer = 0;
|
||||
|
||||
@Override
|
||||
public boolean hasNext()
|
||||
{
|
||||
return !mEmpty;
|
||||
return mBufferPointer < mInterleavedComplexSamples.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ComplexSamples next()
|
||||
{
|
||||
if(mEmpty)
|
||||
if(!hasNext())
|
||||
{
|
||||
throw new IllegalStateException("No more samples");
|
||||
}
|
||||
|
||||
mEmpty = true;
|
||||
return SampleUtils.deinterleave(mInterleavedComplexSamples, getTimestamp());
|
||||
float[] chunk = Arrays.copyOfRange(mInterleavedComplexSamples, mBufferPointer, mBufferPointer + BUFFER_SIZE * 2);
|
||||
mBufferPointer += BUFFER_SIZE * 2;
|
||||
|
||||
return SampleUtils.deinterleave(chunk, getTimestamp());
|
||||
}
|
||||
}
|
||||
|
||||
private class InterleavedComplexSamplesIterator implements Iterator<InterleavedComplexSamples>
|
||||
{
|
||||
private boolean mEmpty;
|
||||
|
||||
private int mBufferPointer = 0;
|
||||
|
||||
@Override
|
||||
public boolean hasNext()
|
||||
{
|
||||
return !mEmpty;
|
||||
return mBufferPointer < mInterleavedComplexSamples.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InterleavedComplexSamples next()
|
||||
{
|
||||
if(mEmpty)
|
||||
if(!hasNext())
|
||||
{
|
||||
throw new IllegalStateException("No more samples");
|
||||
}
|
||||
|
||||
mEmpty = true;
|
||||
return new InterleavedComplexSamples(mInterleavedComplexSamples, getTimestamp());
|
||||
float[] chunk = Arrays.copyOfRange(mInterleavedComplexSamples, mBufferPointer, mBufferPointer + BUFFER_SIZE * 2);
|
||||
mBufferPointer += BUFFER_SIZE * 2;
|
||||
|
||||
return new InterleavedComplexSamples(chunk, getTimestamp());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2022 Dennis Sheirer
|
||||
* Copyright (C) 2014-2023 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
|
||||
@@ -16,61 +16,64 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.dsp.fsk;
|
||||
|
||||
import io.github.dsheirer.bits.MessageFramer;
|
||||
import io.github.dsheirer.dsp.filter.FilterFactory;
|
||||
import io.github.dsheirer.dsp.filter.dc.IIRSinglePoleDCRemovalFilter;
|
||||
import io.github.dsheirer.dsp.filter.design.FilterDesignException;
|
||||
import io.github.dsheirer.dsp.filter.fir.FIRFilterSpecification;
|
||||
import io.github.dsheirer.dsp.filter.fir.real.IRealFilter;
|
||||
import io.github.dsheirer.dsp.filter.fir.remez.RemezFIRFilterDesigner;
|
||||
import io.github.dsheirer.dsp.symbol.ISyncDetectListener;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import java.util.Arrays;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Logic Trunked Radio (LTR) 300-baud FSK decoder is designed to operate against an 8 kHz FM demodulated audio stream.
|
||||
* LTR decoder designed to work with 8 kHz input audio samples and decode 300-baud signalling.
|
||||
*
|
||||
* This decoder employs a zero-crossing timing error detector with adjustable symbol timing error gain. This decoder
|
||||
* also employs a synchronization monitor that automatically adjusts symbol timing error gain according to the quantity
|
||||
* of successful message sync pattern detections. This is optimized for LTR signals that periodically transmit idle
|
||||
* bursts and transmit continuous message streams during voice transmissions. The synchronization monitor adjusts to an
|
||||
* aggressive gain value when sync is lost and automatically reduces timing error gain as successive sync pattern
|
||||
* detections occur.
|
||||
* This decoder monitors the incoming sample stream looking for either a positive or negative sequence of samples where
|
||||
* the energy delta across the baud period exceeds a threshold value and causes the symbol state to flip. The symbol
|
||||
* state remains across successive baud periods until the next state change sequence is detected.
|
||||
*
|
||||
* FM demodulated sample buffers should not be filtered prior to this decoder. An internal DC-removal filter coupled
|
||||
* with a low-pass filter removes any tuning offset and/or audio components above 300 Hertz. The DC removal filter is
|
||||
* adjusted for a slower offset removal in order to prevent overly distorting the FSK signalling component. This mild
|
||||
* adjustment may not fully compensate for mis-tuned signals and may result in missed decodes of idle bursts. However,
|
||||
* the DC filter will track a mis-tuned signal to baseband quickly upon a continuous transmission.
|
||||
* Valid LTR messages start with a zero symbol and transition to a positive symbol for the sync bit, this decoder
|
||||
* monitors for excessively long sequences of a one symbol and forces the symbol state back to a zero.
|
||||
*/
|
||||
public class LTRDecoder implements Listener<float[]>, ISyncDetectListener, ISyncStateListener
|
||||
public class LTRDecoder implements Listener<float[]>
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger(LTRDecoder.class);
|
||||
|
||||
public static final float SAMPLES_PER_SYMBOL = 8000.0f / 300.0f;
|
||||
|
||||
protected static final float COARSE_TIMING_GAIN = 1.0f / 3.0f;
|
||||
protected static final float MEDIUM_TIMING_GAIN = 1.0f / 4.0f;
|
||||
protected static final float FINE_TIMING_GAIN = 1.0f / 5.0f;
|
||||
private static final Logger mLog = LoggerFactory.getLogger(LTRDecoder.class);
|
||||
//Slope calculation sumXX value is always the same for 27 evenly spaced samples
|
||||
private static final double SLOPE_CALCULATION_SUM_XX = 1637.999992057681;
|
||||
private static final float BAUD_LENGTH = 8000.0f / 300.0f;
|
||||
private static final float SLOPE_THRESHOLD = 0.0048f;
|
||||
private static final int MAX_ONES_SEQUENCE = 18;
|
||||
private static final int OVERLAP = 26;
|
||||
private static final int POST_TRANSITION_SAMPLES_TO_SKIP = 26;
|
||||
private static final int SLOPE_CALCULATION_LENGTH = 27;
|
||||
private static final int SYMBOL_TRANSITION_IDEAL_MIN = 11;
|
||||
private static final int SYMBOL_TRANSITION_IDEAL_MAX = 16;
|
||||
|
||||
private static float[] sLowPassFilterCoefficients;
|
||||
private boolean mSymbol = false;
|
||||
private float[] mResidual = new float[OVERLAP];
|
||||
private float mBaudCounter = 0f;
|
||||
private float mMaxSlope = 0;
|
||||
private int mExcessiveOneSequenceCounter = 0;
|
||||
private Listener<boolean[]> mSymbolListener;
|
||||
|
||||
static
|
||||
{
|
||||
FIRFilterSpecification specification = FIRFilterSpecification.lowPassBuilder()
|
||||
.sampleRate(8000)
|
||||
.gridDensity(16)
|
||||
.oddLength(true)
|
||||
.passBandCutoff(300)
|
||||
.passBandAmplitude(1.0)
|
||||
.passBandRipple(0.01)
|
||||
.stopBandStart(500)
|
||||
.stopBandAmplitude(0.0)
|
||||
.stopBandRipple(0.03) //Approximately 60 dB attenuation
|
||||
.build();
|
||||
.sampleRate(8000)
|
||||
.gridDensity(16)
|
||||
.oddLength(true)
|
||||
.passBandCutoff(300)
|
||||
.passBandAmplitude(1.0)
|
||||
.passBandRipple(0.01)
|
||||
.stopBandStart(500)
|
||||
.stopBandAmplitude(0.0)
|
||||
.stopBandRipple(0.03) //Approximately 60 dB attenuation
|
||||
.build();
|
||||
|
||||
try
|
||||
{
|
||||
@@ -87,143 +90,193 @@ public class LTRDecoder implements Listener<float[]>, ISyncDetectListener, ISync
|
||||
}
|
||||
}
|
||||
|
||||
protected float mSymbolTimingGain = COARSE_TIMING_GAIN;
|
||||
protected SampleBuffer mSampleBuffer;
|
||||
protected ZeroCrossingErrorDetector mTimingErrorDetector = new ZeroCrossingErrorDetector(SAMPLES_PER_SYMBOL);
|
||||
protected SynchronizationMonitor mSynchronizationMonitor;
|
||||
private IIRSinglePoleDCRemovalFilter mDCFilter = new IIRSinglePoleDCRemovalFilter(0.99999f);
|
||||
private IRealFilter mLowPassFilter = FilterFactory.getRealFilter(sLowPassFilterCoefficients);
|
||||
private MessageFramer mMessageFramer;
|
||||
|
||||
private boolean mSampleDecision;
|
||||
|
||||
/**
|
||||
* Implements a Logic Trunked Radio sub-audible 300 baud FSK signaling decoder
|
||||
*
|
||||
* @param messageLength in symbols
|
||||
* Constructs an instance
|
||||
*/
|
||||
public LTRDecoder(int messageLength)
|
||||
public LTRDecoder()
|
||||
{
|
||||
this(messageLength, new SampleBuffer(SAMPLES_PER_SYMBOL, COARSE_TIMING_GAIN),
|
||||
new ZeroCrossingErrorDetector(SAMPLES_PER_SYMBOL));
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements a Logic Trunked Radio sub-audible 300 baud FSK signaling decoder.
|
||||
*
|
||||
* @param messageLength in symbols
|
||||
* @param sampleBuffer to use for storing sample decisions
|
||||
* @param detector for symbol timing errors
|
||||
* Registers the listener to receive decoded symbols.
|
||||
* @param listener to register
|
||||
*/
|
||||
public LTRDecoder(int messageLength, SampleBuffer sampleBuffer, ZeroCrossingErrorDetector detector)
|
||||
public void setListener(Listener<boolean[]> listener)
|
||||
{
|
||||
mSynchronizationMonitor = new SynchronizationMonitor(messageLength);
|
||||
mSynchronizationMonitor.setListener(this);
|
||||
mTimingErrorDetector = detector;
|
||||
mSampleBuffer = sampleBuffer;
|
||||
mSampleBuffer.setTimingGain(mSymbolTimingGain);
|
||||
mSymbolListener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements the ISyncDetectedListener interface to be notified of message sync detection events.
|
||||
*
|
||||
* This allows the internal timing error detector to adjust symbol timing error gain levels according to the
|
||||
* message synchronization state to quickly adjust to initial signal streams or to reduce gain levels once
|
||||
* synchronization has been achieved.
|
||||
* Processes the demodulated 8 kHz audio samples to extract the 300-baud LTR signalling and delivers the decoded
|
||||
* symbol array to the registered listener.
|
||||
* @param samples to demodulate.
|
||||
*/
|
||||
@Override
|
||||
public void syncDetected(int bitErrors)
|
||||
public void receive(float[] samples)
|
||||
{
|
||||
mSynchronizationMonitor.syncDetected(bitErrors);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void syncLost(int bitsProcessed)
|
||||
{
|
||||
//no-op
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the buffer samples by converting all samples to boolean values reflecting if the sample value is
|
||||
* greater than zero (or not). Average symbol timing offset is calculated for the full buffer and the offset is
|
||||
* adjusted and then each of the symbols are decoded using a simple majority decision.
|
||||
*
|
||||
* @param buffer containing 8.0 kHz unfiltered FM demodulated audio samples with sub-audible LTR signalling.
|
||||
*/
|
||||
@Override
|
||||
public void receive(float[] buffer)
|
||||
{
|
||||
float[] dcFiltered = mDCFilter.filter(buffer);
|
||||
float[] lowPassFiltered = mLowPassFilter.filter(dcFiltered);
|
||||
|
||||
for(float sample : lowPassFiltered)
|
||||
if(mSymbolListener != null)
|
||||
{
|
||||
mSampleDecision = sample > 0.0;
|
||||
float[] filtered = mLowPassFilter.filter(samples);
|
||||
|
||||
mSampleBuffer.receive(mSampleDecision);
|
||||
mTimingErrorDetector.receive(mSampleDecision);
|
||||
int timingAdjust = 0;
|
||||
int samplesToSkip = 0;
|
||||
|
||||
if(mSampleBuffer.hasSymbol())
|
||||
float[] buffer = new float[filtered.length + OVERLAP];
|
||||
boolean[] symbols = new boolean[(int)((filtered.length + mBaudCounter) / BAUD_LENGTH)];
|
||||
int symbolPointer = 0;
|
||||
float slope = 0;
|
||||
|
||||
try
|
||||
{
|
||||
if(mMessageFramer != null)
|
||||
System.arraycopy(mResidual, 0, buffer, 0, mResidual.length);
|
||||
System.arraycopy(filtered, 0, buffer, mResidual.length, filtered.length);
|
||||
System.arraycopy(filtered, filtered.length - OVERLAP, mResidual, 0, OVERLAP);
|
||||
|
||||
for(int bufferPointer = 0; bufferPointer < samples.length; bufferPointer++)
|
||||
{
|
||||
mMessageFramer.process(mSampleBuffer.getSymbol());
|
||||
//Don't calculate slope if we're within the baud period following last symbol transition
|
||||
if(samplesToSkip > 0)
|
||||
{
|
||||
samplesToSkip--;
|
||||
}
|
||||
else
|
||||
{
|
||||
slope = calculateSlope(buffer, bufferPointer);
|
||||
|
||||
if(mSymbol)
|
||||
{
|
||||
//Loop for crest of slope at lowest point below the negative slope threshold for transition
|
||||
if(slope > mMaxSlope && mMaxSlope < -SLOPE_THRESHOLD)
|
||||
{
|
||||
mSymbol = false;
|
||||
|
||||
//Coarse symbol timing adjust - ideal toggle is middle of baud, between 11 & 16 samples
|
||||
if(mBaudCounter < SYMBOL_TRANSITION_IDEAL_MIN)
|
||||
{
|
||||
timingAdjust = 1;
|
||||
}
|
||||
else if(mBaudCounter > SYMBOL_TRANSITION_IDEAL_MAX)
|
||||
{
|
||||
timingAdjust = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
timingAdjust = 0;
|
||||
}
|
||||
|
||||
mBaudCounter += timingAdjust;
|
||||
samplesToSkip = POST_TRANSITION_SAMPLES_TO_SKIP + timingAdjust;
|
||||
}
|
||||
else if(slope < mMaxSlope)
|
||||
{
|
||||
mMaxSlope = slope;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//Loop for crest of slope at the highest point above the positive threshold
|
||||
if(slope < mMaxSlope && mMaxSlope > SLOPE_THRESHOLD)
|
||||
{
|
||||
mSymbol = true;
|
||||
|
||||
//Coarse symbol timing adjust - ideal toggle is middle of baud, between 11 & 16 samples
|
||||
if(mBaudCounter < SYMBOL_TRANSITION_IDEAL_MIN)
|
||||
{
|
||||
timingAdjust = 1;
|
||||
}
|
||||
else if(mBaudCounter > SYMBOL_TRANSITION_IDEAL_MAX)
|
||||
{
|
||||
timingAdjust = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
timingAdjust = 0;
|
||||
}
|
||||
mBaudCounter += timingAdjust;
|
||||
samplesToSkip = 26 + timingAdjust;
|
||||
}
|
||||
else if(slope > mMaxSlope)
|
||||
{
|
||||
mMaxSlope = slope;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mBaudCounter++;
|
||||
|
||||
if(mBaudCounter > BAUD_LENGTH)
|
||||
{
|
||||
//Expand symbols array length if needed to account for symbol timing adjustments
|
||||
if(symbolPointer >= symbols.length)
|
||||
{
|
||||
symbols = Arrays.copyOf(symbols, symbols.length + 1);
|
||||
}
|
||||
|
||||
symbols[symbolPointer++] = mSymbol;
|
||||
|
||||
mBaudCounter -= BAUD_LENGTH;
|
||||
|
||||
//Check for a continuous string of ones and flip the symbol state to zero.
|
||||
if(mSymbol)
|
||||
{
|
||||
mExcessiveOneSequenceCounter++;
|
||||
|
||||
if(mExcessiveOneSequenceCounter > MAX_ONES_SEQUENCE) //Verify that this is the right threshold for a string of ones
|
||||
{
|
||||
mSymbol = false;
|
||||
mMaxSlope = -1f;
|
||||
mExcessiveOneSequenceCounter = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mExcessiveOneSequenceCounter = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
mLog.warn("Unexpected error while processing LTR samples", e);
|
||||
}
|
||||
|
||||
mSampleBuffer.resetAndAdjust(-mTimingErrorDetector.getError());
|
||||
|
||||
mSynchronizationMonitor.increment();
|
||||
//Truncate the symbols array if we didn't fill it completely, due to symbol timing adjustments.
|
||||
if(symbolPointer != symbols.length)
|
||||
{
|
||||
mSymbolListener.receive(Arrays.copyOf(symbols, symbolPointer));
|
||||
}
|
||||
else
|
||||
{
|
||||
mSymbolListener.receive(symbols);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a listener to receive decoded LTR symbols.
|
||||
*
|
||||
* @param messageFramer to receive symbols.
|
||||
* Calculates the slope of the 27 samples in the values array starting at the specified offset.
|
||||
* @param samples to load from
|
||||
* @param offset to the sample start for the slope calculation
|
||||
*/
|
||||
public void setMessageFramer(MessageFramer messageFramer)
|
||||
public float calculateSlope(float[] samples, int offset)
|
||||
{
|
||||
mMessageFramer = messageFramer;
|
||||
}
|
||||
/** mean of accumulated x & y values, used in updating formulas */
|
||||
double xbar = 0;
|
||||
double ybar = samples[offset];
|
||||
|
||||
/**
|
||||
* Removes the symbol listener from receiving decoded LTR symbols.
|
||||
*/
|
||||
public void removeListener()
|
||||
{
|
||||
mMessageFramer = null;
|
||||
}
|
||||
double sumXY = 0;
|
||||
double fact1, dx, dy;
|
||||
|
||||
|
||||
/**
|
||||
* Implements the ISyncStateListener interface to receive synchronization state events and adjust the symbol timing
|
||||
* error gain levels on the internal timing error detector.
|
||||
*
|
||||
* Gain levels are defined relative to a unit gain of 1.0 over a number of symbol periods. LTR uses an initial
|
||||
* ramp-up symbol reversal pattern of 0101 which provides three zero crossing detection opportunities for the timing
|
||||
* error detector, therefore we react to a COARSE gain state to average timing error over 3 symbols.
|
||||
*
|
||||
* @param syncState from an external synchronization state monitor
|
||||
*/
|
||||
@Override
|
||||
public void setSyncState(SyncState syncState)
|
||||
{
|
||||
switch(syncState)
|
||||
for(int x = 1; x < SLOPE_CALCULATION_LENGTH; x++)
|
||||
{
|
||||
case FINE:
|
||||
mSymbolTimingGain = FINE_TIMING_GAIN;
|
||||
break;
|
||||
case MEDIUM:
|
||||
mSymbolTimingGain = MEDIUM_TIMING_GAIN;
|
||||
break;
|
||||
case COARSE:
|
||||
mSymbolTimingGain = COARSE_TIMING_GAIN;
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unrecognized sync state level: " + syncState.name());
|
||||
fact1 = 1.0f + x;
|
||||
dx = x - xbar;
|
||||
dy = samples[offset + x] - ybar;
|
||||
sumXY += dx * dy * (x / (1.0f + x));
|
||||
xbar += dx / fact1;
|
||||
ybar += dy / fact1;
|
||||
}
|
||||
|
||||
mSampleBuffer.setTimingGain(mSymbolTimingGain);
|
||||
return (float)(sumXY / SLOPE_CALCULATION_SUM_XX);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,40 +1,30 @@
|
||||
/*******************************************************************************
|
||||
* sdr-trunk
|
||||
* Copyright (C) 2014-2018 Dennis Sheirer
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
|
||||
* License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
|
||||
* later version.
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with this program.
|
||||
* If not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
******************************************************************************/
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
package io.github.dsheirer.dsp.fsk;
|
||||
|
||||
public class LTRDecoderInstrumented extends LTRDecoder
|
||||
{
|
||||
/**
|
||||
* Instrumented extension to LTRDecoder
|
||||
*
|
||||
* @param messageLength in symbols
|
||||
*/
|
||||
public LTRDecoderInstrumented(int messageLength)
|
||||
public LTRDecoderInstrumented()
|
||||
{
|
||||
super(messageLength, new SampleBufferInstrumented(SAMPLES_PER_SYMBOL, COARSE_TIMING_GAIN),
|
||||
new ZeroCrossingErrorDetectorInstrumented(SAMPLES_PER_SYMBOL));
|
||||
}
|
||||
|
||||
public SampleBufferInstrumented getSampleBuffer()
|
||||
{
|
||||
return (SampleBufferInstrumented)mSampleBuffer;
|
||||
}
|
||||
|
||||
public ZeroCrossingErrorDetectorInstrumented getErrorDetector()
|
||||
{
|
||||
return (ZeroCrossingErrorDetectorInstrumented)mTimingErrorDetector;
|
||||
super();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* Copyright (C) 2014 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/>
|
||||
******************************************************************************/
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 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.edac;
|
||||
|
||||
import io.github.dsheirer.message.MessageDirection;
|
||||
|
||||
import java.util.BitSet;
|
||||
|
||||
/**
|
||||
@@ -98,6 +98,14 @@ public class CRCLTR
|
||||
|
||||
return CRC.FAILED_CRC;
|
||||
}
|
||||
|
||||
public static String getCRCReason(BitSet msg, MessageDirection direction)
|
||||
{
|
||||
int calculated = getCalculatedChecksum( msg );
|
||||
int transmitted = getTransmittedChecksum( msg );
|
||||
return "CALC: " + Integer.toHexString(calculated).toUpperCase() +
|
||||
" TRANS: " + Integer.toHexString(transmitted).toUpperCase();
|
||||
}
|
||||
|
||||
public static byte[] getChecks()
|
||||
{
|
||||
|
||||
@@ -1,18 +1,21 @@
|
||||
/*******************************************************************************
|
||||
* sdr-trunk
|
||||
* Copyright (C) 2014-2018 Dennis Sheirer
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 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.gui.instrument.chart;
|
||||
|
||||
import io.github.dsheirer.module.decode.ltrnet.LTRNetDecoderInstrumented;
|
||||
@@ -58,7 +61,7 @@ public class LTRNetSampleBufferChart extends LineChart
|
||||
|
||||
mLTRNetDecoderInstrumented = decoder;
|
||||
decoder.bufferCount.addListener(new BufferChangeListener());
|
||||
decoder.getLTRDecoder().getErrorDetector().timingError.addListener(new ErrorChangeListener());
|
||||
// decoder.getLTRDecoder().getErrorDetector().timingError.addListener(new ErrorChangeListener());
|
||||
}
|
||||
|
||||
public void setBuffer(boolean[] buffer)
|
||||
@@ -89,7 +92,7 @@ public class LTRNetSampleBufferChart extends LineChart
|
||||
@Override
|
||||
public void changed(ObservableValue observable, Object oldValue, Object newValue)
|
||||
{
|
||||
setBuffer(mLTRNetDecoderInstrumented.getLTRDecoder().getSampleBuffer().getDelayLine());
|
||||
// setBuffer(mLTRNetDecoderInstrumented.getLTRDecoder().getSampleBuffer().getDelayLine());
|
||||
|
||||
while(mSymbol.size() < 2)
|
||||
{
|
||||
@@ -115,29 +118,29 @@ public class LTRNetSampleBufferChart extends LineChart
|
||||
mSymbolSamples.add(sample);
|
||||
}
|
||||
|
||||
int pointer1 = mLTRNetDecoderInstrumented.getLTRDecoder().getSampleBuffer().getDelayLinePointer();
|
||||
int pointer2 = mLTRNetDecoderInstrumented.getLTRDecoder().getSampleBuffer().getDelayLineSecondPointer();
|
||||
// int pointer1 = mLTRNetDecoderInstrumented.getLTRDecoder().getSampleBuffer().getDelayLinePointer();
|
||||
// int pointer2 = mLTRNetDecoderInstrumented.getLTRDecoder().getSampleBuffer().getDelayLineSecondPointer();
|
||||
|
||||
mSamplePoints.get(0).setXValue(pointer1);
|
||||
mSamplePoints.get(1).setXValue(pointer2);
|
||||
// mSamplePoints.get(0).setXValue(pointer1);
|
||||
// mSamplePoints.get(1).setXValue(pointer2);
|
||||
|
||||
float samplesRemaining = mLTRNetDecoderInstrumented.getLTRDecoder().getSampleBuffer().getMidSymbolSamplingPoint();
|
||||
float samplesPerSymbol = mLTRNetDecoderInstrumented.getLTRDecoder().getSampleBuffer().getSamplesPerSymbol();
|
||||
// float samplesRemaining = mLTRNetDecoderInstrumented.getLTRDecoder().getSampleBuffer().getMidSymbolSamplingPoint();
|
||||
// float samplesPerSymbol = mLTRNetDecoderInstrumented.getLTRDecoder().getSampleBuffer().getSamplesPerSymbol();
|
||||
|
||||
float start = pointer1 + samplesRemaining;
|
||||
float end = start + samplesPerSymbol;
|
||||
// float start = pointer1 + samplesRemaining;
|
||||
// float end = start + samplesPerSymbol;
|
||||
|
||||
mSymbolSamples.get(0).setXValue(start);
|
||||
mSymbolSamples.get(1).setXValue(end);
|
||||
// mSymbolSamples.get(0).setXValue(start);
|
||||
// mSymbolSamples.get(1).setXValue(end);
|
||||
|
||||
boolean symbol = mLTRNetDecoderInstrumented.getLTRDecoder().getSampleBuffer().getLastSymbol();
|
||||
int symbolStart = mLTRNetDecoderInstrumented.getLTRDecoder().getSampleBuffer().getSymbolStart();
|
||||
int symbolEnd = mLTRNetDecoderInstrumented.getLTRDecoder().getSampleBuffer().getSymbolEnd();
|
||||
// boolean symbol = mLTRNetDecoderInstrumented.getLTRDecoder().getSampleBuffer().getLastSymbol();
|
||||
// int symbolStart = mLTRNetDecoderInstrumented.getLTRDecoder().getSampleBuffer().getSymbolStart();
|
||||
// int symbolEnd = mLTRNetDecoderInstrumented.getLTRDecoder().getSampleBuffer().getSymbolEnd();
|
||||
|
||||
mSymbol.get(0).setXValue(symbolStart);
|
||||
mSymbol.get(0).setYValue(symbol ? 0.55f : -0.55f);
|
||||
mSymbol.get(1).setXValue(symbolEnd);
|
||||
mSymbol.get(1).setYValue(symbol ? 0.55f : -0.55f);
|
||||
// mSymbol.get(0).setXValue(symbolStart);
|
||||
// mSymbol.get(0).setYValue(symbol ? 0.55f : -0.55f);
|
||||
// mSymbol.get(1).setXValue(symbolEnd);
|
||||
// mSymbol.get(1).setYValue(symbol ? 0.55f : -0.55f);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,20 +155,20 @@ public class LTRNetSampleBufferChart extends LineChart
|
||||
mZeroCrossing.add(sample);
|
||||
}
|
||||
|
||||
int pointer1 = mLTRNetDecoderInstrumented.getLTRDecoder().getSampleBuffer().getDelayLinePointer();
|
||||
int pointer2 = mLTRNetDecoderInstrumented.getLTRDecoder().getSampleBuffer().getDelayLineSecondPointer();
|
||||
// int pointer1 = mLTRNetDecoderInstrumented.getLTRDecoder().getSampleBuffer().getDelayLinePointer();
|
||||
// int pointer2 = mLTRNetDecoderInstrumented.getLTRDecoder().getSampleBuffer().getDelayLineSecondPointer();
|
||||
|
||||
float zeroCrossingIdeal = mLTRNetDecoderInstrumented.getLTRDecoder().getErrorDetector().getZeroCrossingIdeal();
|
||||
float detectedZeroCrossing = zeroCrossingIdeal - ((Number)newValue).floatValue();
|
||||
|
||||
int reference = (zeroCrossingIdeal < pointer1 || detectedZeroCrossing < pointer1 ? pointer2 : pointer1);
|
||||
reference--;
|
||||
|
||||
float start = reference - zeroCrossingIdeal;
|
||||
float end = reference - detectedZeroCrossing;
|
||||
|
||||
mZeroCrossing.get(0).setXValue(start);
|
||||
mZeroCrossing.get(1).setXValue(end);
|
||||
// float zeroCrossingIdeal = mLTRNetDecoderInstrumented.getLTRDecoder().getErrorDetector().getZeroCrossingIdeal();
|
||||
// float detectedZeroCrossing = zeroCrossingIdeal - ((Number)newValue).floatValue();
|
||||
//
|
||||
// int reference = (zeroCrossingIdeal < pointer1 || detectedZeroCrossing < pointer1 ? pointer2 : pointer1);
|
||||
// reference--;
|
||||
//
|
||||
// float start = reference - zeroCrossingIdeal;
|
||||
// float end = reference - detectedZeroCrossing;
|
||||
//
|
||||
// mZeroCrossing.get(0).setXValue(start);
|
||||
// mZeroCrossing.get(1).setXValue(end);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,21 @@
|
||||
/*******************************************************************************
|
||||
* sdr-trunk
|
||||
* Copyright (C) 2014-2018 Dennis Sheirer
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 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.gui.instrument.chart;
|
||||
|
||||
import io.github.dsheirer.module.decode.ltrnet.LTRNetDecoderInstrumented;
|
||||
@@ -53,7 +56,7 @@ public class ZeroCrossingErrorDetectorChart extends LineChart
|
||||
setData(observableList);
|
||||
|
||||
mLTRNetDecoderInstrumented = decoder;
|
||||
decoder.getLTRDecoder().getErrorDetector().timingError.addListener(new ErrorChangeListener());
|
||||
// decoder.getLTRDecoder().getErrorDetector().timingError.addListener(new ErrorChangeListener());
|
||||
decoder.bufferCount.addListener(new ChangeListener()
|
||||
{
|
||||
@Override
|
||||
@@ -84,13 +87,13 @@ public class ZeroCrossingErrorDetectorChart extends LineChart
|
||||
|
||||
private void updateBuffer()
|
||||
{
|
||||
boolean[] samples = mLTRNetDecoderInstrumented.getLTRDecoder().getErrorDetector().getBuffer();
|
||||
|
||||
for(int x = 0; x < samples.length; x++)
|
||||
{
|
||||
Data<Number,Number> sample = mCurrentSamples.get(x);
|
||||
sample.setYValue(samples[x] ? 0.7f : 0.3f);
|
||||
}
|
||||
// boolean[] samples = mLTRNetDecoderInstrumented.getLTRDecoder().getErrorDetector().getBuffer();
|
||||
//
|
||||
// for(int x = 0; x < samples.length; x++)
|
||||
// {
|
||||
// Data<Number,Number> sample = mCurrentSamples.get(x);
|
||||
// sample.setYValue(samples[x] ? 0.7f : 0.3f);
|
||||
// }
|
||||
}
|
||||
|
||||
public class ErrorChangeListener implements ChangeListener
|
||||
@@ -98,35 +101,35 @@ public class ZeroCrossingErrorDetectorChart extends LineChart
|
||||
@Override
|
||||
public void changed(ObservableValue observable, Object oldValue, Object newValue)
|
||||
{
|
||||
float error = mLTRNetDecoderInstrumented.getLTRDecoder().getErrorDetector().getError();
|
||||
|
||||
boolean[] samples = mLTRNetDecoderInstrumented.getLTRDecoder().getErrorDetector().getBuffer();
|
||||
|
||||
for(int x = 0; x < samples.length; x++)
|
||||
{
|
||||
Data<Number,Number> sample = mPreviousSamples.get(x);
|
||||
sample.setYValue(samples[x] ? -0.4f : -0.6f);
|
||||
}
|
||||
|
||||
Data<Number,Number> ideal1 = mIdeal.get(0);
|
||||
ideal1.setXValue(mLTRNetDecoderInstrumented.getLTRDecoder().getErrorDetector().getZeroCrossingIdeal());
|
||||
Data<Number,Number> ideal2 = mIdeal.get(1);
|
||||
ideal2.setXValue(mLTRNetDecoderInstrumented.getLTRDecoder().getErrorDetector().getZeroCrossingIdeal());
|
||||
|
||||
if(error == 0.0f)
|
||||
{
|
||||
Data<Number,Number> detected1 = mDetected.get(0);
|
||||
detected1.setXValue(0.0f);
|
||||
Data<Number,Number> detected2 = mDetected.get(1);
|
||||
detected2.setXValue(0.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
Data<Number,Number> detected1 = mDetected.get(0);
|
||||
detected1.setXValue(mLTRNetDecoderInstrumented.getLTRDecoder().getErrorDetector().getDetectedZeroCrossing());
|
||||
Data<Number,Number> detected2 = mDetected.get(1);
|
||||
detected2.setXValue(mLTRNetDecoderInstrumented.getLTRDecoder().getErrorDetector().getDetectedZeroCrossing());
|
||||
}
|
||||
// float error = mLTRNetDecoderInstrumented.getLTRDecoder().getErrorDetector().getError();
|
||||
//
|
||||
// boolean[] samples = mLTRNetDecoderInstrumented.getLTRDecoder().getErrorDetector().getBuffer();
|
||||
//
|
||||
// for(int x = 0; x < samples.length; x++)
|
||||
// {
|
||||
// Data<Number,Number> sample = mPreviousSamples.get(x);
|
||||
// sample.setYValue(samples[x] ? -0.4f : -0.6f);
|
||||
// }
|
||||
//
|
||||
// Data<Number,Number> ideal1 = mIdeal.get(0);
|
||||
// ideal1.setXValue(mLTRNetDecoderInstrumented.getLTRDecoder().getErrorDetector().getZeroCrossingIdeal());
|
||||
// Data<Number,Number> ideal2 = mIdeal.get(1);
|
||||
// ideal2.setXValue(mLTRNetDecoderInstrumented.getLTRDecoder().getErrorDetector().getZeroCrossingIdeal());
|
||||
//
|
||||
// if(error == 0.0f)
|
||||
// {
|
||||
// Data<Number,Number> detected1 = mDetected.get(0);
|
||||
// detected1.setXValue(0.0f);
|
||||
// Data<Number,Number> detected2 = mDetected.get(1);
|
||||
// detected2.setXValue(0.0f);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// Data<Number,Number> detected1 = mDetected.get(0);
|
||||
// detected1.setXValue(mLTRNetDecoderInstrumented.getLTRDecoder().getErrorDetector().getDetectedZeroCrossing());
|
||||
// Data<Number,Number> detected2 = mDetected.get(1);
|
||||
// detected2.setXValue(mLTRNetDecoderInstrumented.getLTRDecoder().getErrorDetector().getDetectedZeroCrossing());
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,21 @@
|
||||
/*******************************************************************************
|
||||
* sdr-trunk
|
||||
* Copyright (C) 2014-2018 Dennis Sheirer
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 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.gui.instrument.decoder;
|
||||
|
||||
import io.github.dsheirer.gui.instrument.chart.LTRNetSampleBufferChart;
|
||||
@@ -142,8 +145,8 @@ public class LTRNetPane extends RealDecoderPane
|
||||
{
|
||||
if(mZeroCrossingErrorDetectorChart == null)
|
||||
{
|
||||
mZeroCrossingErrorDetectorChart = new ZeroCrossingErrorDetectorChart(getDecoder(),
|
||||
getDecoder().getLTRDecoder().getErrorDetector().getBuffer().length);
|
||||
// mZeroCrossingErrorDetectorChart = new ZeroCrossingErrorDetectorChart(getDecoder(),
|
||||
// getDecoder().getLTRDecoder().getErrorDetector().getBuffer().length);
|
||||
}
|
||||
|
||||
return mZeroCrossingErrorDetectorChart;
|
||||
|
||||
@@ -98,12 +98,13 @@ import java.util.List;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Factory for creating decoder modules to use in a processing chain for any supported protocols.
|
||||
*/
|
||||
public class DecoderFactory
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger(DecoderFactory.class);
|
||||
|
||||
private static final double FM_CHANNEL_BANDWIDTH = 12500.0;
|
||||
private static final double DEMODULATED_AUDIO_SAMPLE_RATE = 8000.0;
|
||||
|
||||
/**
|
||||
* Returns a list of one primary decoder and any auxiliary decoders, as
|
||||
@@ -179,6 +180,13 @@ public class DecoderFactory
|
||||
return modules;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates decoder modules for APCO-25 Phase 2 decoder
|
||||
* @param channel configuration
|
||||
* @param userPreferences reference
|
||||
* @param modules collection to add to
|
||||
* @param aliasList for the channel
|
||||
*/
|
||||
private static void processP25Phase2(Channel channel, UserPreferences userPreferences, List<Module> modules, AliasList aliasList) {
|
||||
modules.add(new P25P2DecoderHDQPSK((DecodeConfigP25Phase2)channel.getDecodeConfiguration()));
|
||||
|
||||
@@ -188,6 +196,13 @@ public class DecoderFactory
|
||||
modules.add(new P25P2AudioModule(userPreferences, 1, aliasList));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates decoder modules for APCO-25 Phase 1 decoder
|
||||
* @param channel configuration
|
||||
* @param userPreferences reference
|
||||
* @param modules collection to add to
|
||||
* @param aliasList for the channel
|
||||
*/
|
||||
private static void processP25Phase1(Channel channel, UserPreferences userPreferences, List<Module> modules, AliasList aliasList, ChannelType channelType, DecodeConfigP25Phase1 decodeConfig) {
|
||||
DecodeConfigP25Phase1 p25Config = decodeConfig;
|
||||
|
||||
@@ -228,16 +243,30 @@ public class DecoderFactory
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates decoder modules for Passport decoder
|
||||
* @param channel configuration
|
||||
* @param userPreferences reference
|
||||
* @param modules collection to add to
|
||||
* @param aliasList for the channel
|
||||
*/
|
||||
private static void processPassport(Channel channel, List<Module> modules, AliasList aliasList, DecodeConfiguration decodeConfig) {
|
||||
modules.add(new PassportDecoder(decodeConfig));
|
||||
modules.add(new PassportDecoderState());
|
||||
modules.add(new AudioModule(aliasList));
|
||||
if(channel.getSourceConfiguration().getSourceType() == SourceType.TUNER)
|
||||
{
|
||||
modules.add(new FMDemodulatorModule(FM_CHANNEL_BANDWIDTH, DEMODULATED_AUDIO_SAMPLE_RATE));
|
||||
modules.add(new FMDemodulatorModule(FM_CHANNEL_BANDWIDTH));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates decoder modules for MPT-1327 decoder
|
||||
* @param channel configuration
|
||||
* @param userPreferences reference
|
||||
* @param modules collection to add to
|
||||
* @param aliasList for the channel
|
||||
*/
|
||||
private static void processMPT1327(ChannelMapModel channelMapModel, Channel channel, List<Module> modules, AliasList aliasList, ChannelType channelType, DecodeConfigMPT1327 decodeConfig) {
|
||||
DecodeConfigMPT1327 mptConfig = decodeConfig;
|
||||
ChannelMap channelMap = channelMapModel.getChannelMap(mptConfig.getChannelMapName());
|
||||
@@ -255,7 +284,7 @@ public class DecoderFactory
|
||||
SourceType sourceType = channel.getSourceConfiguration().getSourceType();
|
||||
if(sourceType == SourceType.TUNER || sourceType == SourceType.TUNER_MULTIPLE_FREQUENCIES)
|
||||
{
|
||||
modules.add(new FMDemodulatorModule(FM_CHANNEL_BANDWIDTH, DEMODULATED_AUDIO_SAMPLE_RATE));
|
||||
modules.add(new FMDemodulatorModule(FM_CHANNEL_BANDWIDTH));
|
||||
}
|
||||
|
||||
if(channelType == ChannelType.STANDARD)
|
||||
@@ -280,27 +309,48 @@ public class DecoderFactory
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates decoder modules for LTR-Net decoder
|
||||
* @param channel configuration
|
||||
* @param userPreferences reference
|
||||
* @param modules collection to add to
|
||||
* @param aliasList for the channel
|
||||
*/
|
||||
private static void processLTRNet(Channel channel, List<Module> modules, AliasList aliasList, DecodeConfigLTRNet decodeConfig) {
|
||||
modules.add(new LTRNetDecoder(decodeConfig));
|
||||
modules.add(new LTRNetDecoderState());
|
||||
modules.add(new AudioModule(aliasList));
|
||||
if(channel.getSourceConfiguration().getSourceType() == SourceType.TUNER)
|
||||
{
|
||||
modules.add(new FMDemodulatorModule(FM_CHANNEL_BANDWIDTH, DEMODULATED_AUDIO_SAMPLE_RATE));
|
||||
modules.add(new FMDemodulatorModule(FM_CHANNEL_BANDWIDTH));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates decoder modules for LTR decoder
|
||||
* @param channel configuration
|
||||
* @param userPreferences reference
|
||||
* @param modules collection to add to
|
||||
* @param aliasList for the channel
|
||||
*/
|
||||
private static void processLTRStandard(Channel channel, List<Module> modules, AliasList aliasList, DecodeConfigLTRStandard decodeConfig) {
|
||||
MessageDirection direction = decodeConfig.getMessageDirection();
|
||||
modules.add(new LTRStandardDecoder(null, direction));
|
||||
modules.add(new LTRStandardDecoder(direction));
|
||||
modules.add(new LTRStandardDecoderState());
|
||||
modules.add(new AudioModule(aliasList));
|
||||
if(channel.getSourceConfiguration().getSourceType() == SourceType.TUNER)
|
||||
{
|
||||
modules.add(new FMDemodulatorModule(FM_CHANNEL_BANDWIDTH, DEMODULATED_AUDIO_SAMPLE_RATE));
|
||||
modules.add(new FMDemodulatorModule(FM_CHANNEL_BANDWIDTH));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates decoder modules for Narrow Band FM decoder
|
||||
* @param channel configuration
|
||||
* @param userPreferences reference
|
||||
* @param modules collection to add to
|
||||
* @param aliasList for the channel
|
||||
*/
|
||||
private static void processNBFM(Channel channel, List<Module> modules, AliasList aliasList, DecodeConfiguration decodeConfig)
|
||||
{
|
||||
if(!(decodeConfig instanceof DecodeConfigNBFM))
|
||||
@@ -312,9 +362,16 @@ public class DecoderFactory
|
||||
DecodeConfigNBFM decodeConfigNBFM = (DecodeConfigNBFM)decodeConfig;
|
||||
modules.add(new NBFMDecoder(decodeConfigNBFM));
|
||||
modules.add(new NBFMDecoderState(channel.getName(), decodeConfigNBFM));
|
||||
modules.add(new AudioModule(aliasList));
|
||||
modules.add(new AudioModule(aliasList, 0, 60000));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates decoder modules for AM decoder
|
||||
* @param channel configuration
|
||||
* @param userPreferences reference
|
||||
* @param modules collection to add to
|
||||
* @param aliasList for the channel
|
||||
*/
|
||||
private static void processAM(Channel channel, List<Module> modules, AliasList aliasList, DecodeConfiguration decodeConfig) {
|
||||
|
||||
if(!(decodeConfig instanceof DecodeConfigAM))
|
||||
@@ -326,7 +383,7 @@ public class DecoderFactory
|
||||
DecodeConfigAM decodeConfigAM = (DecodeConfigAM) decodeConfig;
|
||||
modules.add(new AMDecoder(decodeConfigAM));
|
||||
modules.add(new AMDecoderState(channel.getName(), decodeConfigAM));
|
||||
modules.add(new AudioModule(aliasList));
|
||||
modules.add(new AudioModule(aliasList, 0, 60000));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -381,7 +438,7 @@ public class DecoderFactory
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a list of auxiliary decoders, as specified in the configuration
|
||||
* Constructs a list of auxiliary decoders, as specified in the channel configuration
|
||||
*
|
||||
* @param config - auxiliary configuration
|
||||
* @return - list of auxiliary decoders
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2022 Dennis Sheirer
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -20,15 +20,21 @@ package io.github.dsheirer.module.decode.ltrnet;
|
||||
|
||||
import io.github.dsheirer.bits.MessageFramer;
|
||||
import io.github.dsheirer.bits.SyncPattern;
|
||||
import io.github.dsheirer.dsp.fsk.LTRDecoder;
|
||||
import io.github.dsheirer.message.MessageDirection;
|
||||
import io.github.dsheirer.module.decode.Decoder;
|
||||
import io.github.dsheirer.module.decode.DecoderType;
|
||||
import io.github.dsheirer.dsp.fsk.LTRDecoder;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.sample.real.IRealBufferListener;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* LTR-Net decoder
|
||||
*/
|
||||
public class LTRNetDecoder extends Decoder implements IRealBufferListener, Listener<float[]>
|
||||
{
|
||||
private static final Logger mLog = LoggerFactory.getLogger(LTRNetDecoder.class);
|
||||
public static final int LTR_NET_MESSAGE_LENGTH = 40;
|
||||
protected LTRDecoder mLTRDecoder;
|
||||
private MessageFramer mLTRMessageFramer;
|
||||
@@ -51,8 +57,12 @@ public class LTRNetDecoder extends Decoder implements IRealBufferListener, Liste
|
||||
mLTRMessageFramer = new MessageFramer(SyncPattern.LTR_STANDARD_ISW.getPattern(), LTR_NET_MESSAGE_LENGTH);
|
||||
}
|
||||
|
||||
mLTRDecoder.setMessageFramer(mLTRMessageFramer);
|
||||
mLTRMessageFramer.setSyncDetectListener(mLTRDecoder);
|
||||
mLTRDecoder.setListener(bits -> {
|
||||
for(boolean bit: bits)
|
||||
{
|
||||
mLTRMessageFramer.process(bit);
|
||||
}
|
||||
});
|
||||
mLTRMessageProcessor = new LTRNetMessageProcessor(config.getMessageDirection());
|
||||
mLTRMessageFramer.addMessageListener(mLTRMessageProcessor);
|
||||
mLTRMessageProcessor.setMessageListener(getMessageListener());
|
||||
@@ -64,7 +74,7 @@ public class LTRNetDecoder extends Decoder implements IRealBufferListener, Liste
|
||||
*/
|
||||
public LTRNetDecoder(DecodeConfigLTRNet config)
|
||||
{
|
||||
this(config, new LTRDecoder(LTR_NET_MESSAGE_LENGTH));
|
||||
this(config, new LTRDecoder());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -80,9 +90,9 @@ public class LTRNetDecoder extends Decoder implements IRealBufferListener, Liste
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive(float[] reusableFloatBuffer)
|
||||
public void receive(float[] demodulatedSamples)
|
||||
{
|
||||
mLTRDecoder.receive(reusableFloatBuffer);
|
||||
mLTRDecoder.receive(demodulatedSamples);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2022 Dennis Sheirer
|
||||
* Copyright (C) 2014-2023 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
|
||||
@@ -27,7 +27,7 @@ public class LTRNetDecoderInstrumented extends LTRNetDecoder
|
||||
|
||||
public LTRNetDecoderInstrumented(DecodeConfigLTRNet config)
|
||||
{
|
||||
super(config, new LTRDecoderInstrumented(LTR_NET_MESSAGE_LENGTH));
|
||||
super(config, new LTRDecoderInstrumented());
|
||||
}
|
||||
|
||||
public LTRDecoderInstrumented getLTRDecoder()
|
||||
@@ -36,9 +36,9 @@ public class LTRNetDecoderInstrumented extends LTRNetDecoder
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive(float[] realBuffer)
|
||||
public void receive(float[] demodulatedSamples)
|
||||
{
|
||||
super.receive(realBuffer);
|
||||
super.receive(demodulatedSamples);
|
||||
|
||||
bufferCount.setValue(bufferCount.intValue() + 1);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2018 Dennis Sheirer
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 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.ltrnet.message;
|
||||
|
||||
@@ -42,14 +41,25 @@ public abstract class LtrNetMessage extends Message
|
||||
protected CorrectedBinaryMessage mMessage;
|
||||
protected CRC mCRC;
|
||||
private LTRTalkgroup mTalkgroup;
|
||||
private MessageDirection mMessageDirection;
|
||||
|
||||
public LtrNetMessage(CorrectedBinaryMessage message, MessageDirection direction, long timestamp)
|
||||
{
|
||||
super(timestamp);
|
||||
mMessage = message;
|
||||
mMessageDirection = direction;
|
||||
mCRC = CRCLTR.check(message, direction);
|
||||
}
|
||||
|
||||
/**
|
||||
* Message direction: outbound (OSW) from the repeater or inbound (ISW) to the repeater
|
||||
* @return message direction
|
||||
*/
|
||||
public MessageDirection getMessageDirection()
|
||||
{
|
||||
return mMessageDirection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Underlying binary message
|
||||
*/
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2018 Dennis Sheirer
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 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,14 +14,14 @@
|
||||
*
|
||||
* 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.message.osw;
|
||||
|
||||
import io.github.dsheirer.bits.CorrectedBinaryMessage;
|
||||
import io.github.dsheirer.edac.CRCLTR;
|
||||
import io.github.dsheirer.identifier.Identifier;
|
||||
import io.github.dsheirer.module.decode.ltrnet.LtrNetMessageType;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
@@ -50,7 +49,12 @@ public class ChannelMapHigh extends LtrNetOswMessage
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if(!isValid())
|
||||
{
|
||||
sb.append("[CRC FAIL: ").append(CRCLTR.getCRCReason(mMessage, getMessageDirection())).append("] ");
|
||||
}
|
||||
sb.append("CHANNEL MAP HIGH ").append(getChannels());
|
||||
sb.append(" MSG:").append(getMessage().toString());
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2018 Dennis Sheirer
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 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,14 +14,14 @@
|
||||
*
|
||||
* 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.message.osw;
|
||||
|
||||
import io.github.dsheirer.bits.CorrectedBinaryMessage;
|
||||
import io.github.dsheirer.edac.CRCLTR;
|
||||
import io.github.dsheirer.identifier.Identifier;
|
||||
import io.github.dsheirer.module.decode.ltrnet.LtrNetMessageType;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
@@ -50,7 +49,12 @@ public class ChannelMapLow extends LtrNetOswMessage
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if(!isValid())
|
||||
{
|
||||
sb.append("[CRC FAIL: ").append(CRCLTR.getCRCReason(mMessage, getMessageDirection())).append("] ");
|
||||
}
|
||||
sb.append("CHANNEL MAP LOW ").append(getChannels());
|
||||
sb.append(" MSG:").append(getMessage().toString());
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2018 Dennis Sheirer
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 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,15 +14,15 @@
|
||||
*
|
||||
* 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.message.osw;
|
||||
|
||||
import io.github.dsheirer.bits.CorrectedBinaryMessage;
|
||||
import io.github.dsheirer.edac.CRCLTR;
|
||||
import io.github.dsheirer.identifier.Identifier;
|
||||
import io.github.dsheirer.module.decode.ltrnet.LtrNetMessageType;
|
||||
import io.github.dsheirer.module.decode.ltrnet.identifier.NeighborIdentifier;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@@ -53,8 +52,13 @@ public class NeighborId extends LtrNetOswMessage
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if(!isValid())
|
||||
{
|
||||
sb.append("[CRC FAIL: ").append(CRCLTR.getCRCReason(mMessage, getMessageDirection())).append("] ");
|
||||
}
|
||||
sb.append("NEIGHBOR:").append(getNeighborID());
|
||||
sb.append(" RANK:").append(getNeighborRank());
|
||||
sb.append(" MSG:").append(getMessage().toString());
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2018 Dennis Sheirer
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 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,14 +14,14 @@
|
||||
*
|
||||
* 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.message.osw;
|
||||
|
||||
import io.github.dsheirer.bits.CorrectedBinaryMessage;
|
||||
import io.github.dsheirer.edac.CRCLTR;
|
||||
import io.github.dsheirer.identifier.Identifier;
|
||||
import io.github.dsheirer.module.decode.ltrnet.LtrNetMessageType;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@@ -51,10 +50,15 @@ public class OswCallEnd extends LtrNetOswMessage
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if(!isValid())
|
||||
{
|
||||
sb.append("[CRC FAIL: ").append(CRCLTR.getCRCReason(mMessage, getMessageDirection())).append("] ");
|
||||
}
|
||||
sb.append("CALL END TALKGROUP:").append(getTalkgroup().formatted());
|
||||
sb.append(" AREA:").append(getArea(getMessage()));
|
||||
sb.append(" LCN:").append(getChannel(getMessage()));
|
||||
sb.append(" FREE:").append(getFree(getMessage()));
|
||||
sb.append(" MSG:").append(getMessage().toString());
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2018 Dennis Sheirer
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 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,14 +14,14 @@
|
||||
*
|
||||
* 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.message.osw;
|
||||
|
||||
import io.github.dsheirer.bits.CorrectedBinaryMessage;
|
||||
import io.github.dsheirer.edac.CRCLTR;
|
||||
import io.github.dsheirer.identifier.Identifier;
|
||||
import io.github.dsheirer.module.decode.ltrnet.LtrNetMessageType;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@@ -51,10 +50,15 @@ public class OswCallStart extends LtrNetOswMessage
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if(!isValid())
|
||||
{
|
||||
sb.append("[CRC FAIL: ").append(CRCLTR.getCRCReason(mMessage, getMessageDirection())).append("] ");
|
||||
}
|
||||
sb.append("CALL START TALKGROUP:").append(getTalkgroup().formatted());
|
||||
sb.append(" AREA:").append(getArea(getMessage()));
|
||||
sb.append(" LCN:").append(getChannel(getMessage()));
|
||||
sb.append(" FREE:").append(getFree(getMessage()));
|
||||
sb.append(" MSG:").append(getMessage().toString());
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2018 Dennis Sheirer
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 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,14 +14,14 @@
|
||||
*
|
||||
* 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.message.osw;
|
||||
|
||||
import io.github.dsheirer.bits.CorrectedBinaryMessage;
|
||||
import io.github.dsheirer.edac.CRCLTR;
|
||||
import io.github.dsheirer.identifier.Identifier;
|
||||
import io.github.dsheirer.module.decode.ltrnet.LtrNetMessageType;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
@@ -49,11 +48,16 @@ public class OswUnknown extends LtrNetOswMessage
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if(!isValid())
|
||||
{
|
||||
sb.append("[CRC FAIL: ").append(CRCLTR.getCRCReason(mMessage, getMessageDirection())).append("] ");
|
||||
}
|
||||
sb.append("OSW UNKNOWN MESSAGE - AREA:").append(getArea(getMessage()));
|
||||
sb.append(" LCN:").append(getChannel(getMessage()));
|
||||
sb.append(" HOME:").append(getHomeRepeater(getMessage()));
|
||||
sb.append(" GROUP").append(getGroup(getMessage()));
|
||||
sb.append(" FREE:").append(getFree(getMessage()));
|
||||
sb.append(" MSG:").append(getMessage().toString());
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2018 Dennis Sheirer
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 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,15 +14,15 @@
|
||||
*
|
||||
* 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.message.osw;
|
||||
|
||||
import io.github.dsheirer.bits.CorrectedBinaryMessage;
|
||||
import io.github.dsheirer.edac.CRCLTR;
|
||||
import io.github.dsheirer.identifier.Identifier;
|
||||
import io.github.dsheirer.module.decode.ltrnet.LtrNetMessageType;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
@@ -50,8 +49,13 @@ public class ReceiveFrequencyHigh extends FrequencyHigh
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if(!isValid())
|
||||
{
|
||||
sb.append("[CRC FAIL: ").append(CRCLTR.getCRCReason(mMessage, getMessageDirection())).append("] ");
|
||||
}
|
||||
sb.append("LCN:").append(getChannel());
|
||||
sb.append(" RECEIVE FREQUENCY:").append(getFrequency()).append(" - HIGH");
|
||||
sb.append(" MSG:").append(getMessage().toString());
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2018 Dennis Sheirer
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 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,15 +14,15 @@
|
||||
*
|
||||
* 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.message.osw;
|
||||
|
||||
import io.github.dsheirer.bits.CorrectedBinaryMessage;
|
||||
import io.github.dsheirer.edac.CRCLTR;
|
||||
import io.github.dsheirer.identifier.Identifier;
|
||||
import io.github.dsheirer.module.decode.ltrnet.LtrNetMessageType;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
@@ -47,8 +46,13 @@ public class ReceiveFrequencyLow extends FrequencyLow
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if(!isValid())
|
||||
{
|
||||
sb.append("[CRC FAIL: ").append(CRCLTR.getCRCReason(mMessage, getMessageDirection())).append("] ");
|
||||
}
|
||||
sb.append("LCN:").append(getChannel());
|
||||
sb.append(" RECEIVE FREQUENCY:").append(getFrequency()).append(" - LOW");
|
||||
sb.append(" MSG:").append(getMessage().toString());
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2018 Dennis Sheirer
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 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,15 +14,15 @@
|
||||
*
|
||||
* 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.message.osw;
|
||||
|
||||
import io.github.dsheirer.bits.CorrectedBinaryMessage;
|
||||
import io.github.dsheirer.edac.CRCLTR;
|
||||
import io.github.dsheirer.identifier.Identifier;
|
||||
import io.github.dsheirer.module.decode.ltrnet.LtrNetMessageType;
|
||||
import io.github.dsheirer.module.decode.ltrnet.identifier.LtrNetRadioIdentifier;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@@ -64,7 +63,12 @@ public class RegistrationAccept extends LtrNetOswMessage
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if(!isValid())
|
||||
{
|
||||
sb.append("[CRC FAIL: ").append(CRCLTR.getCRCReason(mMessage, getMessageDirection())).append("] ");
|
||||
}
|
||||
sb.append("REGISTRATION ACCEPT - RADIO UNIQUE ID: ").append(getUniqueID());
|
||||
sb.append(" MSG:").append(getMessage().toString());
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2018 Dennis Sheirer
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 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,15 +14,15 @@
|
||||
*
|
||||
* 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.message.osw;
|
||||
|
||||
import io.github.dsheirer.bits.CorrectedBinaryMessage;
|
||||
import io.github.dsheirer.edac.CRCLTR;
|
||||
import io.github.dsheirer.identifier.Identifier;
|
||||
import io.github.dsheirer.module.decode.ltrnet.LtrNetMessageType;
|
||||
import io.github.dsheirer.module.decode.ltrnet.identifier.LtrSiteIdentifier;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@@ -53,7 +52,12 @@ public class SiteId extends LtrNetOswMessage
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if(!isValid())
|
||||
{
|
||||
sb.append("[CRC FAIL: ").append(CRCLTR.getCRCReason(mMessage, getMessageDirection())).append("] ");
|
||||
}
|
||||
sb.append("SITE:").append(getSiteID());
|
||||
sb.append(" MSG:").append(getMessage().toString());
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2018 Dennis Sheirer
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 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,14 +14,14 @@
|
||||
*
|
||||
* 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.message.osw;
|
||||
|
||||
import io.github.dsheirer.bits.CorrectedBinaryMessage;
|
||||
import io.github.dsheirer.edac.CRCLTR;
|
||||
import io.github.dsheirer.identifier.Identifier;
|
||||
import io.github.dsheirer.module.decode.ltrnet.LtrNetMessageType;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
@@ -43,11 +42,16 @@ public class SystemIdle extends LtrNetOswMessage
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if(!isValid())
|
||||
{
|
||||
sb.append("[CRC FAIL: ").append(CRCLTR.getCRCReason(mMessage, getMessageDirection())).append("] ");
|
||||
}
|
||||
sb.append("IDLE - AREA:").append(getArea(getMessage()));
|
||||
sb.append(" LCN:").append(getChannel(getMessage()));
|
||||
sb.append(" HOME:").append(getHomeRepeater(getMessage()));
|
||||
sb.append(" GROUP:").append(getGroup(getMessage()));
|
||||
sb.append(" FREE:").append(getFree(getMessage()));
|
||||
sb.append(" MSG:").append(getMessage().toString());
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2018 Dennis Sheirer
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 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,15 +14,15 @@
|
||||
*
|
||||
* 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.message.osw;
|
||||
|
||||
import io.github.dsheirer.bits.CorrectedBinaryMessage;
|
||||
import io.github.dsheirer.edac.CRCLTR;
|
||||
import io.github.dsheirer.identifier.Identifier;
|
||||
import io.github.dsheirer.module.decode.ltrnet.LtrNetMessageType;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
@@ -47,8 +46,13 @@ public class TransmitFrequencyHigh extends FrequencyHigh
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if(!isValid())
|
||||
{
|
||||
sb.append("[CRC FAIL: ").append(CRCLTR.getCRCReason(mMessage, getMessageDirection())).append("] ");
|
||||
}
|
||||
sb.append("LCN:").append(getChannel());
|
||||
sb.append(" TRANSMIT FREQUENCY:").append(getFrequency()).append(" - HIGH");
|
||||
sb.append(" MSG:").append(getMessage().toString());
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2018 Dennis Sheirer
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 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,15 +14,15 @@
|
||||
*
|
||||
* 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.message.osw;
|
||||
|
||||
import io.github.dsheirer.bits.CorrectedBinaryMessage;
|
||||
import io.github.dsheirer.edac.CRCLTR;
|
||||
import io.github.dsheirer.identifier.Identifier;
|
||||
import io.github.dsheirer.module.decode.ltrnet.LtrNetMessageType;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
@@ -47,8 +46,13 @@ public class TransmitFrequencyLow extends FrequencyLow
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if(!isValid())
|
||||
{
|
||||
sb.append("[CRC FAIL: ").append(CRCLTR.getCRCReason(mMessage, getMessageDirection())).append("] ");
|
||||
}
|
||||
sb.append("LCN:").append(getChannel());
|
||||
sb.append(" TRANSMIT FREQUENCY:").append(getFrequency()).append(" - LOW");
|
||||
sb.append(" MSG:").append(getMessage().toString());
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2022 Dennis Sheirer
|
||||
* Copyright (C) 2014-2023 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
|
||||
@@ -18,7 +18,6 @@
|
||||
*/
|
||||
package io.github.dsheirer.module.decode.ltrstandard;
|
||||
|
||||
import io.github.dsheirer.alias.AliasList;
|
||||
import io.github.dsheirer.bits.MessageFramer;
|
||||
import io.github.dsheirer.bits.SyncPattern;
|
||||
import io.github.dsheirer.dsp.fsk.LTRDecoder;
|
||||
@@ -27,11 +26,16 @@ 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.real.IRealBufferListener;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* LTR Decoder
|
||||
*/
|
||||
public class LTRStandardDecoder extends Decoder implements IRealBufferListener, Listener<float[]>
|
||||
{
|
||||
public static final int LTR_STANDARD_MESSAGE_LENGTH = 40;
|
||||
|
||||
private static final Logger mLog = LoggerFactory.getLogger(LTRStandardDecoder.class);
|
||||
private LTRDecoder mLTRDecoder;
|
||||
private MessageFramer mLTRMessageFramer;
|
||||
private LTRStandardMessageProcessor mLTRMessageProcessor;
|
||||
@@ -40,9 +44,9 @@ public class LTRStandardDecoder extends Decoder implements IRealBufferListener,
|
||||
* LTR Decoder. Decodes unfiltered (e.g. demodulated but with no DC or
|
||||
* audio filtering) samples and produces LTR Standard messages.
|
||||
*/
|
||||
public LTRStandardDecoder(AliasList aliasList, MessageDirection direction)
|
||||
public LTRStandardDecoder(MessageDirection direction)
|
||||
{
|
||||
mLTRDecoder = new LTRDecoder(LTR_STANDARD_MESSAGE_LENGTH);
|
||||
mLTRDecoder = new LTRDecoder();
|
||||
|
||||
if(direction == MessageDirection.OSW)
|
||||
{
|
||||
@@ -53,11 +57,17 @@ public class LTRStandardDecoder extends Decoder implements IRealBufferListener,
|
||||
mLTRMessageFramer = new MessageFramer(SyncPattern.LTR_STANDARD_ISW.getPattern(), LTR_STANDARD_MESSAGE_LENGTH);
|
||||
}
|
||||
|
||||
mLTRDecoder.setMessageFramer(mLTRMessageFramer);
|
||||
mLTRMessageFramer.setSyncDetectListener(mLTRDecoder);
|
||||
mLTRDecoder.setListener(symbols -> {
|
||||
for(boolean symbol: symbols)
|
||||
{
|
||||
mLTRMessageFramer.process(symbol);
|
||||
}
|
||||
});
|
||||
mLTRMessageProcessor = new LTRStandardMessageProcessor(direction);
|
||||
mLTRMessageFramer.addMessageListener(mLTRMessageProcessor);
|
||||
mLTRMessageProcessor.setMessageListener(getMessageListener());
|
||||
mLTRMessageProcessor.setMessageListener(message -> {
|
||||
getMessageListener().receive(message);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -118,6 +118,8 @@ public class LTRStandardDecoderState extends DecoderState
|
||||
{
|
||||
CallEnd end = (CallEnd)message;
|
||||
|
||||
mCurrentTalkgroup = null;
|
||||
|
||||
//Home channel is 31 for call end -- use the free channel as the call end channel
|
||||
int repeater = end.getFree();
|
||||
setChannelNumber(repeater);
|
||||
@@ -128,13 +130,14 @@ public class LTRStandardDecoderState extends DecoderState
|
||||
mCurrentCallEvent.end(end.getTimestamp());
|
||||
}
|
||||
|
||||
broadcast(new DecoderStateEvent(this, Event.END, State.FADE));
|
||||
broadcast(new DecoderStateEvent(this, Event.END, State.IDLE));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case IDLE:
|
||||
if(message instanceof Idle)
|
||||
{
|
||||
mCurrentTalkgroup = null;
|
||||
mLCNTracker.processCallChannel(((Idle)message).getChannel());
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -69,7 +69,7 @@ public class LTRStandardMessageProcessor implements Listener<CorrectedBinaryMess
|
||||
int free = binaryMessage.getInt(LTRMessage.FREE);
|
||||
int group = binaryMessage.getInt(LTRMessage.GROUP);
|
||||
|
||||
if(isValidChannel(channel) && isValidChannel(home) && isValidChannel(free))
|
||||
if(isValidChannel(channel) && isValidChannel(home) && isValidFreeChannel(free))
|
||||
{
|
||||
if(channel == free && group == 255)
|
||||
{
|
||||
@@ -80,7 +80,7 @@ public class LTRStandardMessageProcessor implements Listener<CorrectedBinaryMess
|
||||
message = new Call(binaryMessage, mDirection, crc);
|
||||
}
|
||||
}
|
||||
else if(channel == 31 && isValidChannel(home) && isValidChannel(free))
|
||||
else if(channel == 31 && isValidChannel(home) && isValidFreeChannel(free))
|
||||
{
|
||||
message = new CallEnd(binaryMessage, mDirection, crc);
|
||||
}
|
||||
@@ -102,6 +102,14 @@ public class LTRStandardMessageProcessor implements Listener<CorrectedBinaryMess
|
||||
return (1 <= channel && channel <= 20);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the channel (LCN) number to ensure it is in the range 1 -20
|
||||
*/
|
||||
private boolean isValidFreeChannel(int channel)
|
||||
{
|
||||
return (0 <= channel && channel <= 20);
|
||||
}
|
||||
|
||||
public void setMessageListener(Listener<IMessage> listener)
|
||||
{
|
||||
mMessageListener = listener;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2022 Dennis Sheirer
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -20,17 +20,19 @@ package io.github.dsheirer.module.decode.passport;
|
||||
|
||||
import io.github.dsheirer.bits.MessageFramer;
|
||||
import io.github.dsheirer.bits.SyncPattern;
|
||||
import io.github.dsheirer.dsp.fsk.LTRDecoder;
|
||||
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.dsp.fsk.LTRDecoder;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.sample.real.IRealBufferListener;
|
||||
|
||||
/**
|
||||
* LTR Passport decoder
|
||||
*/
|
||||
public class PassportDecoder extends Decoder implements IRealBufferListener, Listener<float[]>
|
||||
{
|
||||
public static final int PASSPORT_MESSAGE_LENGTH = 68;
|
||||
|
||||
private LTRDecoder mLTRDecoder;
|
||||
private MessageFramer mPassportMessageFramer;
|
||||
private PassportMessageProcessor mPassportMessageProcessor;
|
||||
@@ -41,12 +43,16 @@ public class PassportDecoder extends Decoder implements IRealBufferListener, Lis
|
||||
*/
|
||||
public PassportDecoder(DecodeConfiguration config)
|
||||
{
|
||||
mLTRDecoder = new LTRDecoder(PASSPORT_MESSAGE_LENGTH);
|
||||
mLTRDecoder = new LTRDecoder();
|
||||
|
||||
mPassportMessageFramer = new MessageFramer(SyncPattern.PASSPORT.getPattern(), PASSPORT_MESSAGE_LENGTH);
|
||||
|
||||
mLTRDecoder.setMessageFramer(mPassportMessageFramer);
|
||||
mPassportMessageFramer.setSyncDetectListener(mLTRDecoder);
|
||||
mLTRDecoder.setListener(bits -> {
|
||||
for(boolean bit: bits)
|
||||
{
|
||||
mPassportMessageFramer.process(bit);
|
||||
}
|
||||
});
|
||||
mPassportMessageProcessor = new PassportMessageProcessor();
|
||||
mPassportMessageFramer.addMessageListener(mPassportMessageProcessor);
|
||||
mPassportMessageProcessor.setMessageListener(getMessageListener());
|
||||
|
||||
@@ -352,6 +352,8 @@ public class PassportMessage extends Message
|
||||
break;
|
||||
}
|
||||
|
||||
sb.append(" MSG:").append(getMessage().toString());
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2022 Dennis Sheirer
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -19,11 +19,14 @@
|
||||
package io.github.dsheirer.module.demodulate.fm;
|
||||
|
||||
import io.github.dsheirer.dsp.filter.FilterFactory;
|
||||
import io.github.dsheirer.dsp.filter.decimate.DecimationFilterFactory;
|
||||
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.real.IRealFilter;
|
||||
import io.github.dsheirer.dsp.filter.resample.RealResampler;
|
||||
import io.github.dsheirer.dsp.fm.ScalarFMDemodulator;
|
||||
import io.github.dsheirer.dsp.fm.FmDemodulatorFactory;
|
||||
import io.github.dsheirer.dsp.fm.IDemodulator;
|
||||
import io.github.dsheirer.dsp.squelch.PowerMonitor;
|
||||
import io.github.dsheirer.dsp.window.WindowType;
|
||||
import io.github.dsheirer.module.Module;
|
||||
@@ -46,16 +49,17 @@ public class FMDemodulatorModule extends Module implements ISourceEventListener,
|
||||
IComplexSamplesListener, Listener<ComplexSamples>, IRealBufferProvider
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger(FMDemodulatorModule.class);
|
||||
|
||||
private static final double DEMODULATED_AUDIO_SAMPLE_RATE = 8000.0;
|
||||
private IRealFilter mIBasebandFilter;
|
||||
private IRealFilter mQBasebandFilter;
|
||||
private ScalarFMDemodulator mDemodulator = new ScalarFMDemodulator();
|
||||
private IRealDecimationFilter mIDecimationFilter;
|
||||
private IRealDecimationFilter mQDecimationFilter;
|
||||
private IDemodulator mDemodulator = FmDemodulatorFactory.getFmDemodulator();
|
||||
private PowerMonitor mPowerMonitor = new PowerMonitor();
|
||||
private RealResampler mResampler;
|
||||
private SourceEventProcessor mSourceEventProcessor = new SourceEventProcessor();
|
||||
private Listener<float[]> mResampledBufferListener;
|
||||
private double mChannelBandwidth;
|
||||
private double mOutputSampleRate;
|
||||
|
||||
/**
|
||||
* Creates an FM demodulator for the specified channel bandwidth and output sample rate.
|
||||
@@ -64,10 +68,21 @@ public class FMDemodulatorModule extends Module implements ISourceEventListener,
|
||||
* channel bandwidth and the demodulated output will be resampled to the desired output sample rate. The input
|
||||
* low pass filter is constructed at runtime based on receiving a sample rate notification source event.
|
||||
*/
|
||||
public FMDemodulatorModule(double channelBandwidth, double outputSampleRate)
|
||||
public FMDemodulatorModule(double channelBandwidth)
|
||||
{
|
||||
mChannelBandwidth = channelBandwidth;
|
||||
mOutputSampleRate = outputSampleRate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Broadcasts the demodulated audio samples to the registered listener.
|
||||
* @param demodulatedSamples to broadcast
|
||||
*/
|
||||
protected void broadcast(float[] demodulatedSamples)
|
||||
{
|
||||
if(mResampledBufferListener != null)
|
||||
{
|
||||
mResampledBufferListener.receive(demodulatedSamples);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -85,25 +100,15 @@ public class FMDemodulatorModule extends Module implements ISourceEventListener,
|
||||
@Override
|
||||
public void dispose()
|
||||
{
|
||||
mDemodulator.dispose();
|
||||
mDemodulator = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset()
|
||||
{
|
||||
mDemodulator.reset();
|
||||
}
|
||||
|
||||
public void reset() {}
|
||||
@Override
|
||||
public void start()
|
||||
{
|
||||
}
|
||||
|
||||
public void start() {}
|
||||
@Override
|
||||
public void stop()
|
||||
{
|
||||
}
|
||||
public void stop() {}
|
||||
|
||||
@Override
|
||||
public void setBufferListener(Listener<float[]> listener)
|
||||
@@ -120,17 +125,18 @@ public class FMDemodulatorModule extends Module implements ISourceEventListener,
|
||||
@Override
|
||||
public void receive(ComplexSamples samples)
|
||||
{
|
||||
if(mIBasebandFilter == null || mQBasebandFilter == null)
|
||||
if(mIBasebandFilter == null || mIDecimationFilter == null)
|
||||
{
|
||||
throw new IllegalStateException("FM demodulator module must receive a sample rate change source " +
|
||||
"event before it can process complex sample buffers");
|
||||
}
|
||||
|
||||
float[] i = mIBasebandFilter.filter(samples.i());
|
||||
float[] q = mQBasebandFilter.filter(samples.q());
|
||||
|
||||
mPowerMonitor.process(i, q);
|
||||
float[] demodulated = mDemodulator.demodulate(i, q);
|
||||
float[] decimatedI = mIDecimationFilter.decimateReal(samples.i());
|
||||
float[] decimatedQ = mQDecimationFilter.decimateReal(samples.q());
|
||||
float[] filteredI = mIBasebandFilter.filter(decimatedI);
|
||||
float[] filteredQ = mQBasebandFilter.filter(decimatedQ);
|
||||
float[] demodulated = mDemodulator.demodulate(filteredI, filteredQ);
|
||||
mPowerMonitor.process(filteredI, filteredQ);
|
||||
|
||||
if(mResampler != null)
|
||||
{
|
||||
@@ -162,23 +168,41 @@ public class FMDemodulatorModule extends Module implements ISourceEventListener,
|
||||
{
|
||||
double sampleRate = sourceEvent.getValue().doubleValue();
|
||||
|
||||
if((sampleRate < (2.0 * mChannelBandwidth)))
|
||||
int decimationRate = 0;
|
||||
double decimatedSampleRate = sampleRate;
|
||||
|
||||
if(sampleRate / 2 >= (mChannelBandwidth * 2))
|
||||
{
|
||||
throw new IllegalStateException("FM Demodulator with channel bandwidth [" + mChannelBandwidth +
|
||||
"] requires a channel sample rate of [" + (2.0 * mChannelBandwidth + "] - sample rate of [" +
|
||||
sampleRate + "] is not supported"));
|
||||
decimationRate = 2;
|
||||
|
||||
while(sampleRate / decimationRate / 2 >= (mChannelBandwidth * 2))
|
||||
{
|
||||
decimationRate *= 2;
|
||||
}
|
||||
}
|
||||
|
||||
mPowerMonitor.setSampleRate((int)sampleRate);
|
||||
if(decimationRate > 0)
|
||||
{
|
||||
decimatedSampleRate /= decimationRate;
|
||||
}
|
||||
|
||||
double cutoff = sampleRate / 4.0;
|
||||
int passBandStop = (int)cutoff - 500;
|
||||
int stopBandStart = (int)cutoff + 500;
|
||||
mIDecimationFilter = DecimationFilterFactory.getRealDecimationFilter(decimationRate);
|
||||
mQDecimationFilter = DecimationFilterFactory.getRealDecimationFilter(decimationRate);
|
||||
|
||||
if((decimatedSampleRate < (2.0 * mChannelBandwidth)))
|
||||
{
|
||||
throw new IllegalStateException("FM demodulator with channel bandwidth [" +
|
||||
mChannelBandwidth + "] requires a channel sample rate of [" + (2.0 * mChannelBandwidth +
|
||||
"] - sample rate of [" + decimatedSampleRate + "] is not supported"));
|
||||
}
|
||||
|
||||
int passBandStop = (int) (mChannelBandwidth * .8);
|
||||
int stopBandStart = (int) mChannelBandwidth;
|
||||
|
||||
float[] coefficients = null;
|
||||
|
||||
FIRFilterSpecification specification = FIRFilterSpecification.lowPassBuilder()
|
||||
.sampleRate(sampleRate)
|
||||
.sampleRate(decimatedSampleRate * 2)
|
||||
.gridDensity(16)
|
||||
.oddLength(true)
|
||||
.passBandCutoff(passBandStop)
|
||||
@@ -186,7 +210,7 @@ public class FMDemodulatorModule extends Module implements ISourceEventListener,
|
||||
.passBandRipple(0.01)
|
||||
.stopBandStart(stopBandStart)
|
||||
.stopBandAmplitude(0.0)
|
||||
.stopBandRipple(0.028) //Approximately 60 dB attenuation
|
||||
.stopBandRipple(0.005) //Approximately 90 dB attenuation
|
||||
.build();
|
||||
|
||||
try
|
||||
@@ -195,27 +219,25 @@ public class FMDemodulatorModule extends Module implements ISourceEventListener,
|
||||
}
|
||||
catch(FilterDesignException fde)
|
||||
{
|
||||
mLog.error("Couldn't design FM demodulator remez filter for sample rate [" + sampleRate +
|
||||
mLog.error("Couldn't design demodulator remez filter for sample rate [" + sampleRate +
|
||||
"] pass frequency [" + passBandStop + "] and stop frequency [" + stopBandStart +
|
||||
"] - using sinc filter");
|
||||
"] - will proceed using sinc (low-pass) filter");
|
||||
}
|
||||
|
||||
if(coefficients == null)
|
||||
{
|
||||
coefficients = FilterFactory.getLowPass(sampleRate, passBandStop, stopBandStart, 60,
|
||||
mLog.info("Unable to use remez filter designer for sample rate [" + decimatedSampleRate +
|
||||
"] pass band stop [" + passBandStop +
|
||||
"] and stop band start [" + stopBandStart + "] - will proceed using simple low pass filter design");
|
||||
coefficients = FilterFactory.getLowPass(decimatedSampleRate, passBandStop, stopBandStart, 60,
|
||||
WindowType.HAMMING, true);
|
||||
}
|
||||
|
||||
mIBasebandFilter = FilterFactory.getRealFilter(coefficients);
|
||||
mQBasebandFilter = FilterFactory.getRealFilter(coefficients);
|
||||
mResampler = new RealResampler(sampleRate, mOutputSampleRate, 8192, 512);
|
||||
|
||||
mResampler.setListener(resampledBuffer -> {
|
||||
if(mResampledBufferListener != null)
|
||||
{
|
||||
mResampledBufferListener.receive(resampledBuffer);
|
||||
}
|
||||
});
|
||||
mResampler = new RealResampler(decimatedSampleRate, DEMODULATED_AUDIO_SAMPLE_RATE, 4192, 512);
|
||||
mResampler.setListener(resampled -> broadcast(resampled));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,10 +142,11 @@ public class ComplexWaveSource extends Source implements IControllableFileSource
|
||||
if(mAutoReplay)
|
||||
{
|
||||
double sampleRate = getSampleRate();
|
||||
|
||||
double buffersPerSecond = (sampleRate / mBufferSampleCount);
|
||||
long intervalMilliseconds = (long)(1000.0 / buffersPerSecond);
|
||||
mReplayController = ThreadPool.SCHEDULED.scheduleAtFixedRate(new ReplayController(mBufferSampleCount),
|
||||
0, intervalMilliseconds, TimeUnit.MILLISECONDS);
|
||||
Runnable r = new ReplayController(mBufferSampleCount);
|
||||
mReplayController = ThreadPool.SCHEDULED.scheduleAtFixedRate(r, 0, intervalMilliseconds, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user