From 19f7a5171bf9cf9d965153455cb3497faebe6e97 Mon Sep 17 00:00:00 2001 From: Denny Sheirer Date: Mon, 5 Jun 2023 06:13:12 -0400 Subject: [PATCH] #1565 updates LTR, LTRNet and Passport decoders with new slope decoder. Resolves issue with LTR Standard decoder state that was combining individual calls to the same talkgroup and causing excessively long calls. (#1567) Co-authored-by: Dennis Sheirer --- .../io/github/dsheirer/audio/AudioModule.java | 2 +- .../github/dsheirer/bits/BinaryMessage.java | 3 +- .../io/github/dsheirer/bits/SyncPattern.java | 6 +- .../dsheirer/buffer/FloatNativeBuffer.java | 28 +- .../github/dsheirer/dsp/fsk/LTRDecoder.java | 329 ++++++++++-------- .../dsp/fsk/LTRDecoderInstrumented.java | 44 +-- .../java/io/github/dsheirer/edac/CRCLTR.java | 44 ++- .../chart/LTRNetSampleBufferChart.java | 91 ++--- .../chart/ZeroCrossingErrorDetectorChart.java | 101 +++--- .../gui/instrument/decoder/LTRNetPane.java | 31 +- .../module/decode/DecoderFactory.java | 77 +++- .../module/decode/ltrnet/LTRNetDecoder.java | 24 +- .../ltrnet/LTRNetDecoderInstrumented.java | 8 +- .../decode/ltrnet/message/LtrNetMessage.java | 18 +- .../ltrnet/message/osw/ChannelMapHigh.java | 14 +- .../ltrnet/message/osw/ChannelMapLow.java | 14 +- .../decode/ltrnet/message/osw/NeighborId.java | 14 +- .../decode/ltrnet/message/osw/OswCallEnd.java | 14 +- .../ltrnet/message/osw/OswCallStart.java | 14 +- .../decode/ltrnet/message/osw/OswUnknown.java | 14 +- .../message/osw/ReceiveFrequencyHigh.java | 14 +- .../message/osw/ReceiveFrequencyLow.java | 14 +- .../message/osw/RegistrationAccept.java | 14 +- .../decode/ltrnet/message/osw/SiteId.java | 14 +- .../decode/ltrnet/message/osw/SystemIdle.java | 14 +- .../message/osw/TransmitFrequencyHigh.java | 14 +- .../message/osw/TransmitFrequencyLow.java | 14 +- .../ltrstandard/LTRStandardDecoder.java | 26 +- .../ltrstandard/LTRStandardDecoderState.java | 5 +- .../LTRStandardMessageProcessor.java | 12 +- .../decode/passport/PassportDecoder.java | 18 +- .../decode/passport/PassportMessage.java | 2 + .../demodulate/fm/FMDemodulatorModule.java | 114 +++--- .../source/wave/ComplexWaveSource.java | 5 +- 34 files changed, 707 insertions(+), 463 deletions(-) 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); } }