diff --git a/src/main/java/io/github/dsheirer/audio/AudioModule.java b/src/main/java/io/github/dsheirer/audio/AudioModule.java index 5819e010..a12e6659 100644 --- a/src/main/java/io/github/dsheirer/audio/AudioModule.java +++ b/src/main/java/io/github/dsheirer/audio/AudioModule.java @@ -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 diff --git a/src/main/java/io/github/dsheirer/bits/BinaryMessage.java b/src/main/java/io/github/dsheirer/bits/BinaryMessage.java index 2395ea0b..23d95084 100644 --- a/src/main/java/io/github/dsheirer/bits/BinaryMessage.java +++ b/src/main/java/io/github/dsheirer/bits/BinaryMessage.java @@ -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"); } } diff --git a/src/main/java/io/github/dsheirer/bits/SyncPattern.java b/src/main/java/io/github/dsheirer/bits/SyncPattern.java index 8f589bbb..c25bf518 100644 --- a/src/main/java/io/github/dsheirer/bits/SyncPattern.java +++ b/src/main/java/io/github/dsheirer/bits/SyncPattern.java @@ -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[] diff --git a/src/main/java/io/github/dsheirer/buffer/FloatNativeBuffer.java b/src/main/java/io/github/dsheirer/buffer/FloatNativeBuffer.java index daf4999b..11cedf25 100644 --- a/src/main/java/io/github/dsheirer/buffer/FloatNativeBuffer.java +++ b/src/main/java/io/github/dsheirer/buffer/FloatNativeBuffer.java @@ -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 { - 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 { - 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()); } } } diff --git a/src/main/java/io/github/dsheirer/dsp/fsk/LTRDecoder.java b/src/main/java/io/github/dsheirer/dsp/fsk/LTRDecoder.java index ef44f1a6..a7e871ee 100644 --- a/src/main/java/io/github/dsheirer/dsp/fsk/LTRDecoder.java +++ b/src/main/java/io/github/dsheirer/dsp/fsk/LTRDecoder.java @@ -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 * **************************************************************************** */ + 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, ISyncDetectListener, ISyncStateListener +public class LTRDecoder implements Listener { - 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 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, 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 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); } } diff --git a/src/main/java/io/github/dsheirer/dsp/fsk/LTRDecoderInstrumented.java b/src/main/java/io/github/dsheirer/dsp/fsk/LTRDecoderInstrumented.java index eeabaadf..e08903c2 100644 --- a/src/main/java/io/github/dsheirer/dsp/fsk/LTRDecoderInstrumented.java +++ b/src/main/java/io/github/dsheirer/dsp/fsk/LTRDecoderInstrumented.java @@ -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 - * - ******************************************************************************/ + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** + */ 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(); } } diff --git a/src/main/java/io/github/dsheirer/edac/CRCLTR.java b/src/main/java/io/github/dsheirer/edac/CRCLTR.java index 32428494..c475aa22 100644 --- a/src/main/java/io/github/dsheirer/edac/CRCLTR.java +++ b/src/main/java/io/github/dsheirer/edac/CRCLTR.java @@ -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 - ******************************************************************************/ +/* + * ***************************************************************************** + * 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 + * **************************************************************************** + */ 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() { diff --git a/src/main/java/io/github/dsheirer/gui/instrument/chart/LTRNetSampleBufferChart.java b/src/main/java/io/github/dsheirer/gui/instrument/chart/LTRNetSampleBufferChart.java index 008b1824..fe898009 100644 --- a/src/main/java/io/github/dsheirer/gui/instrument/chart/LTRNetSampleBufferChart.java +++ b/src/main/java/io/github/dsheirer/gui/instrument/chart/LTRNetSampleBufferChart.java @@ -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 - * - ******************************************************************************/ + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** + */ 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); } } } diff --git a/src/main/java/io/github/dsheirer/gui/instrument/chart/ZeroCrossingErrorDetectorChart.java b/src/main/java/io/github/dsheirer/gui/instrument/chart/ZeroCrossingErrorDetectorChart.java index e10147c3..1ed84977 100644 --- a/src/main/java/io/github/dsheirer/gui/instrument/chart/ZeroCrossingErrorDetectorChart.java +++ b/src/main/java/io/github/dsheirer/gui/instrument/chart/ZeroCrossingErrorDetectorChart.java @@ -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 - * - ******************************************************************************/ + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** + */ 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 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 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 sample = mPreviousSamples.get(x); - sample.setYValue(samples[x] ? -0.4f : -0.6f); - } - - Data ideal1 = mIdeal.get(0); - ideal1.setXValue(mLTRNetDecoderInstrumented.getLTRDecoder().getErrorDetector().getZeroCrossingIdeal()); - Data ideal2 = mIdeal.get(1); - ideal2.setXValue(mLTRNetDecoderInstrumented.getLTRDecoder().getErrorDetector().getZeroCrossingIdeal()); - - if(error == 0.0f) - { - Data detected1 = mDetected.get(0); - detected1.setXValue(0.0f); - Data detected2 = mDetected.get(1); - detected2.setXValue(0.0f); - } - else - { - Data detected1 = mDetected.get(0); - detected1.setXValue(mLTRNetDecoderInstrumented.getLTRDecoder().getErrorDetector().getDetectedZeroCrossing()); - Data 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 sample = mPreviousSamples.get(x); +// sample.setYValue(samples[x] ? -0.4f : -0.6f); +// } +// +// Data ideal1 = mIdeal.get(0); +// ideal1.setXValue(mLTRNetDecoderInstrumented.getLTRDecoder().getErrorDetector().getZeroCrossingIdeal()); +// Data ideal2 = mIdeal.get(1); +// ideal2.setXValue(mLTRNetDecoderInstrumented.getLTRDecoder().getErrorDetector().getZeroCrossingIdeal()); +// +// if(error == 0.0f) +// { +// Data detected1 = mDetected.get(0); +// detected1.setXValue(0.0f); +// Data detected2 = mDetected.get(1); +// detected2.setXValue(0.0f); +// } +// else +// { +// Data detected1 = mDetected.get(0); +// detected1.setXValue(mLTRNetDecoderInstrumented.getLTRDecoder().getErrorDetector().getDetectedZeroCrossing()); +// Data detected2 = mDetected.get(1); +// detected2.setXValue(mLTRNetDecoderInstrumented.getLTRDecoder().getErrorDetector().getDetectedZeroCrossing()); +// } } } } diff --git a/src/main/java/io/github/dsheirer/gui/instrument/decoder/LTRNetPane.java b/src/main/java/io/github/dsheirer/gui/instrument/decoder/LTRNetPane.java index ddefac8f..667cc7f0 100644 --- a/src/main/java/io/github/dsheirer/gui/instrument/decoder/LTRNetPane.java +++ b/src/main/java/io/github/dsheirer/gui/instrument/decoder/LTRNetPane.java @@ -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 - * - ******************************************************************************/ + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * **************************************************************************** + */ 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; diff --git a/src/main/java/io/github/dsheirer/module/decode/DecoderFactory.java b/src/main/java/io/github/dsheirer/module/decode/DecoderFactory.java index 9669a535..2cd445dd 100644 --- a/src/main/java/io/github/dsheirer/module/decode/DecoderFactory.java +++ b/src/main/java/io/github/dsheirer/module/decode/DecoderFactory.java @@ -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 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 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 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 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 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 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 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 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 diff --git a/src/main/java/io/github/dsheirer/module/decode/ltrnet/LTRNetDecoder.java b/src/main/java/io/github/dsheirer/module/decode/ltrnet/LTRNetDecoder.java index 182dcd8e..b23c8391 100644 --- a/src/main/java/io/github/dsheirer/module/decode/ltrnet/LTRNetDecoder.java +++ b/src/main/java/io/github/dsheirer/module/decode/ltrnet/LTRNetDecoder.java @@ -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 { + 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 diff --git a/src/main/java/io/github/dsheirer/module/decode/ltrnet/LTRNetDecoderInstrumented.java b/src/main/java/io/github/dsheirer/module/decode/ltrnet/LTRNetDecoderInstrumented.java index 10a78c65..01456856 100644 --- a/src/main/java/io/github/dsheirer/module/decode/ltrnet/LTRNetDecoderInstrumented.java +++ b/src/main/java/io/github/dsheirer/module/decode/ltrnet/LTRNetDecoderInstrumented.java @@ -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); } diff --git a/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/LtrNetMessage.java b/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/LtrNetMessage.java index 8b7902aa..30ccd045 100644 --- a/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/LtrNetMessage.java +++ b/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/LtrNetMessage.java @@ -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 - * ***************************************************************************** + * **************************************************************************** */ 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 */ diff --git a/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/ChannelMapHigh.java b/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/ChannelMapHigh.java index 6a7e95e2..6f80373b 100644 --- a/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/ChannelMapHigh.java +++ b/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/ChannelMapHigh.java @@ -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 - * ***************************************************************************** + * **************************************************************************** */ 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(); } diff --git a/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/ChannelMapLow.java b/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/ChannelMapLow.java index 3ffddd0e..129a5a59 100644 --- a/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/ChannelMapLow.java +++ b/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/ChannelMapLow.java @@ -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 - * ***************************************************************************** + * **************************************************************************** */ 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(); } diff --git a/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/NeighborId.java b/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/NeighborId.java index 46dab8d7..b8ec4edb 100644 --- a/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/NeighborId.java +++ b/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/NeighborId.java @@ -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 - * ***************************************************************************** + * **************************************************************************** */ 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(); } diff --git a/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/OswCallEnd.java b/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/OswCallEnd.java index 07669926..571c6f2f 100644 --- a/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/OswCallEnd.java +++ b/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/OswCallEnd.java @@ -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 - * ***************************************************************************** + * **************************************************************************** */ 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(); } diff --git a/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/OswCallStart.java b/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/OswCallStart.java index 6ddf3f43..5af50a13 100644 --- a/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/OswCallStart.java +++ b/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/OswCallStart.java @@ -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 - * ***************************************************************************** + * **************************************************************************** */ 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(); } diff --git a/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/OswUnknown.java b/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/OswUnknown.java index 66c662ae..4e5a3b14 100644 --- a/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/OswUnknown.java +++ b/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/OswUnknown.java @@ -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 - * ***************************************************************************** + * **************************************************************************** */ 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(); } diff --git a/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/ReceiveFrequencyHigh.java b/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/ReceiveFrequencyHigh.java index e79a8624..a7f95172 100644 --- a/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/ReceiveFrequencyHigh.java +++ b/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/ReceiveFrequencyHigh.java @@ -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 - * ***************************************************************************** + * **************************************************************************** */ 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(); } diff --git a/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/ReceiveFrequencyLow.java b/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/ReceiveFrequencyLow.java index 5ec5d75d..9f33c398 100644 --- a/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/ReceiveFrequencyLow.java +++ b/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/ReceiveFrequencyLow.java @@ -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 - * ***************************************************************************** + * **************************************************************************** */ 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(); } diff --git a/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/RegistrationAccept.java b/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/RegistrationAccept.java index b4ee1a81..4de282d8 100644 --- a/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/RegistrationAccept.java +++ b/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/RegistrationAccept.java @@ -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 - * ***************************************************************************** + * **************************************************************************** */ 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(); } diff --git a/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/SiteId.java b/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/SiteId.java index 4f6e47de..4347bb00 100644 --- a/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/SiteId.java +++ b/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/SiteId.java @@ -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 - * ***************************************************************************** + * **************************************************************************** */ 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(); } diff --git a/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/SystemIdle.java b/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/SystemIdle.java index 2e080c36..46d09d62 100644 --- a/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/SystemIdle.java +++ b/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/SystemIdle.java @@ -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 - * ***************************************************************************** + * **************************************************************************** */ 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(); } diff --git a/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/TransmitFrequencyHigh.java b/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/TransmitFrequencyHigh.java index f662f45e..0a189d92 100644 --- a/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/TransmitFrequencyHigh.java +++ b/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/TransmitFrequencyHigh.java @@ -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 - * ***************************************************************************** + * **************************************************************************** */ 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(); } diff --git a/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/TransmitFrequencyLow.java b/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/TransmitFrequencyLow.java index e5904963..3a33fdc0 100644 --- a/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/TransmitFrequencyLow.java +++ b/src/main/java/io/github/dsheirer/module/decode/ltrnet/message/osw/TransmitFrequencyLow.java @@ -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 - * ***************************************************************************** + * **************************************************************************** */ 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(); } diff --git a/src/main/java/io/github/dsheirer/module/decode/ltrstandard/LTRStandardDecoder.java b/src/main/java/io/github/dsheirer/module/decode/ltrstandard/LTRStandardDecoder.java index b54e2cf9..34bb5747 100644 --- a/src/main/java/io/github/dsheirer/module/decode/ltrstandard/LTRStandardDecoder.java +++ b/src/main/java/io/github/dsheirer/module/decode/ltrstandard/LTRStandardDecoder.java @@ -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 { 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 diff --git a/src/main/java/io/github/dsheirer/module/decode/ltrstandard/LTRStandardDecoderState.java b/src/main/java/io/github/dsheirer/module/decode/ltrstandard/LTRStandardDecoderState.java index 2be9d1fc..f5539d21 100644 --- a/src/main/java/io/github/dsheirer/module/decode/ltrstandard/LTRStandardDecoderState.java +++ b/src/main/java/io/github/dsheirer/module/decode/ltrstandard/LTRStandardDecoderState.java @@ -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; diff --git a/src/main/java/io/github/dsheirer/module/decode/ltrstandard/LTRStandardMessageProcessor.java b/src/main/java/io/github/dsheirer/module/decode/ltrstandard/LTRStandardMessageProcessor.java index 0b18e754..44a6aa1d 100644 --- a/src/main/java/io/github/dsheirer/module/decode/ltrstandard/LTRStandardMessageProcessor.java +++ b/src/main/java/io/github/dsheirer/module/decode/ltrstandard/LTRStandardMessageProcessor.java @@ -69,7 +69,7 @@ public class LTRStandardMessageProcessor implements Listener listener) { mMessageListener = listener; diff --git a/src/main/java/io/github/dsheirer/module/decode/passport/PassportDecoder.java b/src/main/java/io/github/dsheirer/module/decode/passport/PassportDecoder.java index 6fd0a2bf..16bdb746 100644 --- a/src/main/java/io/github/dsheirer/module/decode/passport/PassportDecoder.java +++ b/src/main/java/io/github/dsheirer/module/decode/passport/PassportDecoder.java @@ -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 { 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()); diff --git a/src/main/java/io/github/dsheirer/module/decode/passport/PassportMessage.java b/src/main/java/io/github/dsheirer/module/decode/passport/PassportMessage.java index 883909d0..b93f7673 100644 --- a/src/main/java/io/github/dsheirer/module/decode/passport/PassportMessage.java +++ b/src/main/java/io/github/dsheirer/module/decode/passport/PassportMessage.java @@ -352,6 +352,8 @@ public class PassportMessage extends Message break; } + sb.append(" MSG:").append(getMessage().toString()); + return sb.toString(); } diff --git a/src/main/java/io/github/dsheirer/module/demodulate/fm/FMDemodulatorModule.java b/src/main/java/io/github/dsheirer/module/demodulate/fm/FMDemodulatorModule.java index 0ba78a80..cc6f57e2 100644 --- a/src/main/java/io/github/dsheirer/module/demodulate/fm/FMDemodulatorModule.java +++ b/src/main/java/io/github/dsheirer/module/demodulate/fm/FMDemodulatorModule.java @@ -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, 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 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 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)); } } } diff --git a/src/main/java/io/github/dsheirer/source/wave/ComplexWaveSource.java b/src/main/java/io/github/dsheirer/source/wave/ComplexWaveSource.java index 82bedb03..8f171fd1 100644 --- a/src/main/java/io/github/dsheirer/source/wave/ComplexWaveSource.java +++ b/src/main/java/io/github/dsheirer/source/wave/ComplexWaveSource.java @@ -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); } }