From 8868c1f954b592a613a6e4d8321321cacb67865e Mon Sep 17 00:00:00 2001 From: Denny Date: Sat, 17 Mar 2018 09:45:23 -0400 Subject: [PATCH] 330 - updates P25 C4FM decoder to use the new DQPSK demodulator and removes the previous FM demodulator based decoder. --- .../dsp/psk/DQPSKSymbolEvaluator.java | 2 +- .../psk/DQPSKSymbolPhaseErrorCalculator.java | 37 --- .../dsp/psk/DQPSKTimingErrorDetector.java | 89 ------ .../gui/viewer/decoder/P25Phase1Pane.java | 6 +- .../module/decode/DecoderFactory.java | 4 +- .../module/decode/p25/P25MessageFramer.java | 3 +- .../module/decode/p25/P25_C4FMDecoder.java | 297 ++++++++++-------- .../module/decode/p25/P25_C4FMDecoder2.java | 200 ------------ ....java => P25_C4FMDecoderInstrumented.java} | 4 +- .../source/tuner/TunerChannelSource.java | 1 + 10 files changed, 181 insertions(+), 462 deletions(-) delete mode 100644 src/main/java/io/github/dsheirer/dsp/psk/DQPSKSymbolPhaseErrorCalculator.java delete mode 100644 src/main/java/io/github/dsheirer/dsp/psk/DQPSKTimingErrorDetector.java delete mode 100644 src/main/java/io/github/dsheirer/module/decode/p25/P25_C4FMDecoder2.java rename src/main/java/io/github/dsheirer/module/decode/p25/{P25_C4FMDecoder2Instrumented.java => P25_C4FMDecoderInstrumented.java} (97%) diff --git a/src/main/java/io/github/dsheirer/dsp/psk/DQPSKSymbolEvaluator.java b/src/main/java/io/github/dsheirer/dsp/psk/DQPSKSymbolEvaluator.java index 16b9018d..cab0fd22 100644 --- a/src/main/java/io/github/dsheirer/dsp/psk/DQPSKSymbolEvaluator.java +++ b/src/main/java/io/github/dsheirer/dsp/psk/DQPSKSymbolEvaluator.java @@ -32,7 +32,7 @@ public class DQPSKSymbolEvaluator implements IDQPSKSymbolEvaluator private Dibit mSymbolDecision = Dibit.D00_PLUS_1; /** - * Data-directed symbol phase and timing error detector and symbol decision slicer. + * Decision-directed symbol phase and timing error detector and symbol decision slicer. * * Symbol decision is based on the closest reference quadrant for the sampled symbol. * diff --git a/src/main/java/io/github/dsheirer/dsp/psk/DQPSKSymbolPhaseErrorCalculator.java b/src/main/java/io/github/dsheirer/dsp/psk/DQPSKSymbolPhaseErrorCalculator.java deleted file mode 100644 index 7eafb69a..00000000 --- a/src/main/java/io/github/dsheirer/dsp/psk/DQPSKSymbolPhaseErrorCalculator.java +++ /dev/null @@ -1,37 +0,0 @@ -/******************************************************************************* - * sdr-trunk - * Copyright (C) 2014-2018 Dennis Sheirer - * - * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public - * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any - * later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with this program. - * If not, see - * - ******************************************************************************/ -package io.github.dsheirer.dsp.psk; - -import io.github.dsheirer.sample.complex.Complex; - -public class DQPSKSymbolPhaseErrorCalculator extends QPSKSymbolPhaseErrorCalculator -{ - //45 degrees rotation to orient the symbol to a polar axis to make error calculation easy/efficient - public static final Complex DIFFERENTIAL_OFFSET = Complex.fromAngle(Math.PI / 4.0d); - private Complex mSymbol = new Complex(0, 0); - - public DQPSKSymbolPhaseErrorCalculator() - { - } - - @Override - public float getPhaseError(Complex symbol) - { - mSymbol.setValues(symbol); - mSymbol.multiply(DIFFERENTIAL_OFFSET); - return super.getPhaseError(mSymbol); - } -} diff --git a/src/main/java/io/github/dsheirer/dsp/psk/DQPSKTimingErrorDetector.java b/src/main/java/io/github/dsheirer/dsp/psk/DQPSKTimingErrorDetector.java deleted file mode 100644 index 929e2b06..00000000 --- a/src/main/java/io/github/dsheirer/dsp/psk/DQPSKTimingErrorDetector.java +++ /dev/null @@ -1,89 +0,0 @@ -/******************************************************************************* - * sdr-trunk - * Copyright (C) 2014-2018 Dennis Sheirer - * - * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public - * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any - * later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with this program. - * If not, see - * - ******************************************************************************/ -package io.github.dsheirer.dsp.psk; - -import io.github.dsheirer.sample.complex.Complex; - -public class DQPSKTimingErrorDetector -{ - private static final float PI_4_ROTATION = (float)(Math.PI / 4.0); - - /** - * Custom early/late symbol timing error detector designed to optimize the sample timing point such that the - * current symbol's inphase and quadrature approach the optimal 45 degree (.707) sampling instant. - */ - public DQPSKTimingErrorDetector() - { - } - - /** - * Calculates a symbol sampling time error by comparing the current complex sample against a normal QPSK or - * Pi/4 rotated four symbol constellation. The error signal (advance/retard) is determined by the slope - * of each of the inphase and quadrature legs as detected by comparing an immediately preceding sample against - * the current sample and then calculating the error distance of the sampled symbol from the ideal Pi/4 sampling - * location. - * - * Note: both the preceding and the current symbols MUST be normalized (ie symbol.normalize()) to the unit circle - * prior to invoking this method. - * - * @param precedingSymbol - * @param currentSymbol - * @return timing error signal used to advance (positive) or retard (negative) the sampling point - */ - public float getError(Complex precedingSymbol, Complex currentSymbol) - { - return getError(precedingSymbol.inphase(), currentSymbol.inphase()) + - getError(precedingSymbol.quadrature(), currentSymbol.quadrature()); - } - - /** - * Calculates timing error of current value relative to an ideal 45 degree or Pi/4 radians (.707) value. - * - * @param early sample that closely precedes the current sampling instant - used to determine slope - * @param current sampling value - * @return error as the distance of the current value from an ideal value and the sign (advance/retard) - * appropriate for the detected slope - */ - private float getError(float early, float current) - { - if(early < current) //Ascending slope - { - if(current > 0.0) - { - //Positive quadrature hemisphere, ascending slope - return PI_4_ROTATION - current; - } - else - { - //Negative quadrature hemisphere, ascending slope - return -PI_4_ROTATION - current; - } - } - else //Descending slope - { - if(current > 0.0) - { - //Positive quadrature hemisphere, descending slope - return current - PI_4_ROTATION; - } - else - { - //Negative quadrature hemisphere, descending slope - return current + PI_4_ROTATION; - } - } - } -} diff --git a/src/main/java/io/github/dsheirer/instrument/gui/viewer/decoder/P25Phase1Pane.java b/src/main/java/io/github/dsheirer/instrument/gui/viewer/decoder/P25Phase1Pane.java index 1b3f31d9..526d3e7f 100644 --- a/src/main/java/io/github/dsheirer/instrument/gui/viewer/decoder/P25Phase1Pane.java +++ b/src/main/java/io/github/dsheirer/instrument/gui/viewer/decoder/P25Phase1Pane.java @@ -22,7 +22,7 @@ import io.github.dsheirer.instrument.gui.viewer.chart.PhaseLineChart; import io.github.dsheirer.instrument.gui.viewer.chart.SymbolChart; import io.github.dsheirer.message.Message; import io.github.dsheirer.module.decode.DecoderType; -import io.github.dsheirer.module.decode.p25.P25_C4FMDecoder2Instrumented; +import io.github.dsheirer.module.decode.p25.P25_C4FMDecoderInstrumented; import io.github.dsheirer.sample.Broadcaster; import io.github.dsheirer.sample.Listener; import io.github.dsheirer.sample.complex.ComplexBuffer; @@ -44,7 +44,7 @@ public class P25Phase1Pane extends DecoderPane private DoubleLineChart mPLLFrequencyLineChart; private DoubleLineChart mSamplesPerSymbolLineChart; private Broadcaster mFilteredBufferBroadcaster = new Broadcaster<>(); - private P25_C4FMDecoder2Instrumented mDecoder = new P25_C4FMDecoder2Instrumented(null); + private P25_C4FMDecoderInstrumented mDecoder = new P25_C4FMDecoderInstrumented(null); public P25Phase1Pane() { @@ -90,7 +90,7 @@ public class P25Phase1Pane extends DecoderPane getSampleLineChart().setSamplesPerSymbol((int)samplesPerSymbol); } - private P25_C4FMDecoder2Instrumented getDecoder() + private P25_C4FMDecoderInstrumented getDecoder() { return mDecoder; } 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 29dbcf5b..94ec4ae7 100644 --- a/src/main/java/io/github/dsheirer/module/decode/DecoderFactory.java +++ b/src/main/java/io/github/dsheirer/module/decode/DecoderFactory.java @@ -246,9 +246,7 @@ public class DecoderFactory switch(modulation) { case C4FM: - modules.add(new FMDemodulatorModule(P25_C4FM_IQ_FILTER)); - modules.add(new DemodulatedAudioFilterModule(P25_C4FM_DEMOD_FILTER, 1.0f)); - modules.add(new P25_C4FMDecoder(aliasList, decodeConfig.getAFCMaximumCorrection())); + modules.add(new P25_C4FMDecoder(aliasList)); modules.add(new P25DecoderState(aliasList, channelType, P25Decoder.Modulation.C4FM, p25Config.getIgnoreDataCalls())); break; diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/P25MessageFramer.java b/src/main/java/io/github/dsheirer/module/decode/p25/P25MessageFramer.java index 4d38ed79..54047262 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/P25MessageFramer.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/P25MessageFramer.java @@ -698,8 +698,7 @@ public class P25MessageFramer implements Listener @Override public void syncDetected() { - mLog.warn("Phase Inversion SYNC detected - applying correction: " + mCorrection); -// mPhaseLockedLoop.correctInversion(mCorrection); + mPhaseLockedLoop.correctInversion(mCorrection); /* Since we detected a sync pattern, start a message assembler */ for(P25MessageAssembler assembler : mAssemblers) diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/P25_C4FMDecoder.java b/src/main/java/io/github/dsheirer/module/decode/p25/P25_C4FMDecoder.java index c51a7adf..6923bc37 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/P25_C4FMDecoder.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/P25_C4FMDecoder.java @@ -1,153 +1,200 @@ /******************************************************************************* - * sdrtrunk - * Copyright (C) 2014-2017 Dennis Sheirer + * sdr-trunk + * Copyright (C) 2014-2018 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.module.decode.p25; import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.instrument.tap.TapGroup; +import io.github.dsheirer.dsp.filter.FilterFactory; +import io.github.dsheirer.dsp.filter.design.FilterDesignException; +import io.github.dsheirer.dsp.filter.fir.FIRFilterSpecification; +import io.github.dsheirer.dsp.filter.fir.complex.ComplexFIRFilter; +import io.github.dsheirer.dsp.psk.DQPSKDemodulator; +import io.github.dsheirer.dsp.psk.InterpolatingSampleBuffer; +import io.github.dsheirer.dsp.psk.InterpolatingSampleBufferInstrumented; +import io.github.dsheirer.dsp.psk.pll.CostasLoop; import io.github.dsheirer.sample.Listener; -import io.github.dsheirer.sample.real.IFilteredRealBufferListener; -import io.github.dsheirer.sample.real.RealBuffer; +import io.github.dsheirer.sample.complex.ComplexBuffer; +import io.github.dsheirer.sample.complex.IComplexBufferListener; import io.github.dsheirer.source.tuner.frequency.FrequencyChangeEvent; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.List; +import java.util.HashMap; +import java.util.Map; import java.util.concurrent.ScheduledExecutorService; -public class P25_C4FMDecoder extends P25Decoder implements IFilteredRealBufferListener +public class P25_C4FMDecoder extends P25Decoder implements IComplexBufferListener, Listener { - private final static Logger mLog = LoggerFactory.getLogger( P25_C4FMDecoder.class ); - - /* Instrumentation Taps */ - private static final String INSTRUMENT_C4FM_SYMBOL_FILTER_OUTPUT = "Tap Point: Symbol Filter Output"; - private static final String INSTRUMENT_C4FM_SLICER_OUTPUT = "Tap Point: C4FM Slicer Output"; + private final static Logger mLog = LoggerFactory.getLogger(P25_C4FMDecoder.class); + protected static final double SYMBOL_RATE = 4800; + protected InterpolatingSampleBuffer mInterpolatingSampleBuffer; + protected DQPSKDemodulator mQPSKDemodulator; + protected CostasLoop mCostasLoop; + protected P25MessageFramer mMessageFramer; + protected double mSampleRate; + private Map mBasebandFilters = new HashMap<>(); + private ComplexFIRFilter mBasebandFilter; - private List mAvailableTaps; - private C4FMSymbolFilter mSymbolFilter; - private C4FMSlicer mC4FMSlicer; - private P25MessageFramer mMessageFramer; - - /** - * P25 Phase 1 C4FM Decoder processes real buffers of un-filtered, - * demodulated audio and produces decoded P25 Phase 1 Messages. - * - * Provides Frequency Control to steer external tuner source and incorporates - * internal gain control. - * - * Note: use the P25AudioModule to convert the decoded messages into audio. - * - * @param aliasList - optional (can be null) list of alias values for network - * infrastructure and subscriber identities that will be included in each - * decoded message - */ - public P25_C4FMDecoder(AliasList aliasList, int frequencyCorrectionMaximum ) - { - super( aliasList ); - - /* Shape gain and frequency offsets to optimize sample stream */ - mSymbolFilter = new C4FMSymbolFilter( frequencyCorrectionMaximum ); + public P25_C4FMDecoder(AliasList aliasList) + { + super(aliasList); + setSampleRate(48000.0); + } - /* Convert samples to symbols */ - mC4FMSlicer = new C4FMSlicer(); - mSymbolFilter.setListener( mC4FMSlicer ); + public void setSampleRate(double sampleRate) + { + if(sampleRate <= SYMBOL_RATE * 2) + { + throw new IllegalArgumentException("Sample rate must be at least twice the symbol rate [4800]"); + } - /* Sync pattern detection and message construction */ - mMessageFramer = new P25MessageFramer( aliasList ); - mC4FMSlicer.addListener( mMessageFramer ); - - /* Process and broadcast messages */ - mMessageFramer.setListener( getMessageProcessor() ); - } + mSampleRate = sampleRate; + mBasebandFilter = new ComplexFIRFilter(getBasebandFilter(), 1.0f); - @Override - public void dispose() - { - super.dispose(); - - mSymbolFilter.dispose(); - mSymbolFilter = null; - - mMessageFramer.dispose(); - mMessageFramer = null; + mCostasLoop = new CostasLoop(mSampleRate, SYMBOL_RATE); - mC4FMSlicer.dispose(); - mC4FMSlicer = null; - } - - public Modulation getModulation() - { - return Modulation.C4FM; - } - - @Override - public Listener getFilteredRealBufferListener() - { - return mSymbolFilter; - } - - @Override - public void reset() - { - // TODO Auto-generated method stub - - } + mInterpolatingSampleBuffer = new InterpolatingSampleBufferInstrumented((float)(sampleRate / SYMBOL_RATE)); + mQPSKDemodulator = new DQPSKDemodulator(mCostasLoop, mInterpolatingSampleBuffer); - @Override - public void start( ScheduledExecutorService executor ) - { - // TODO Auto-generated method stub - - } + //Message framer can trigger a symbol-inversion correction to the PLL when detected + mMessageFramer = new P25MessageFramer(getAliasList(), mCostasLoop); + mMessageFramer.setListener(getMessageProcessor()); + mQPSKDemodulator.setDibitListener(mMessageFramer); + } - @Override - public void stop() - { - // TODO Auto-generated method stub - - } + @Override + public void receive(ComplexBuffer complexBuffer) + { + ComplexBuffer filtered = filter(complexBuffer); + mQPSKDemodulator.receive(filtered); + } - @Override - public void setFrequencyChangeListener( Listener listener ) - { - if( mSymbolFilter != null ) - { - mSymbolFilter.setFrequencyChangeListener( listener ); - } - } + protected ComplexBuffer filter(ComplexBuffer complexBuffer) + { + return mBasebandFilter.filter(complexBuffer); + } - @Override - public void removeFrequencyChangeListener() - { - if( mSymbolFilter != null ) - { - mSymbolFilter.setFrequencyChangeListener( null ); - } - } + private double getSampleRate() + { + return mSampleRate; + } - @Override - public Listener getFrequencyChangeListener() - { - if( mSymbolFilter != null ) - { - return mSymbolFilter.getFrequencyChangeListener(); - } - - return null; - } + private float[] getBasebandFilter() + { + float[] filter = mBasebandFilters.get(getSampleRate()); + + if(filter == null) + { + FIRFilterSpecification specification = FIRFilterSpecification.lowPassBuilder() + .sampleRate((int)getSampleRate()) + .passBandCutoff(5100) + .passBandAmplitude(1.0) + .passBandRipple(0.01) + .stopBandAmplitude(0.0) + .stopBandStart(6500) + .stopBandRipple(0.01) + .build(); + + try + { + filter = FilterFactory.getTaps(specification); + + } + catch(FilterDesignException fde) + { + mLog.error("Couldn't design low pass baseband filter for sample rate: " + getSampleRate()); + } + if(filter != null) + { + mBasebandFilters.put(getSampleRate(), filter); + } + else + { + throw new IllegalStateException("Couldn't design a C4FM symbol filter for sample rate: " + mSampleRate); + } + } + + return filter; + } + + public void dispose() + { + super.dispose(); + + mBasebandFilter.dispose(); + mBasebandFilter = null; + + mMessageFramer.dispose(); + mMessageFramer = null; + } + + @Override + public void setFrequencyChangeListener(Listener listener) + { + } + + @Override + public void removeFrequencyChangeListener() + { + } + + @Override + public Listener getFrequencyChangeListener() + { + return new Listener() + { + @Override + public void receive(FrequencyChangeEvent frequencyChangeEvent) + { + switch(frequencyChangeEvent.getEvent()) + { + case NOTIFICATION_FREQUENCY_CHANGE: + case NOTIFICATION_FREQUENCY_CORRECTION_CHANGE: + case NOTIFICATION_CHANNEL_FREQUENCY_CORRECTION_CHANGE: + case NOTIFICATION_SAMPLE_RATE_CHANGE: + mCostasLoop.reset(); + break; + } + } + }; + } + + @Override + public Listener getComplexBufferListener() + { + return P25_C4FMDecoder.this; + } + + public Modulation getModulation() + { + return Modulation.C4FM; + } + + @Override + public void reset() + { + mCostasLoop.reset(); + } + + @Override + public void start(ScheduledExecutorService executor) + { + } + + @Override + public void stop() + { + } } diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/P25_C4FMDecoder2.java b/src/main/java/io/github/dsheirer/module/decode/p25/P25_C4FMDecoder2.java deleted file mode 100644 index dbd56609..00000000 --- a/src/main/java/io/github/dsheirer/module/decode/p25/P25_C4FMDecoder2.java +++ /dev/null @@ -1,200 +0,0 @@ -/******************************************************************************* - * sdr-trunk - * Copyright (C) 2014-2018 Dennis Sheirer - * - * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public - * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any - * later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with this program. - * If not, see - * - ******************************************************************************/ -package io.github.dsheirer.module.decode.p25; - -import io.github.dsheirer.alias.AliasList; -import io.github.dsheirer.dsp.filter.FilterFactory; -import io.github.dsheirer.dsp.filter.design.FilterDesignException; -import io.github.dsheirer.dsp.filter.fir.FIRFilterSpecification; -import io.github.dsheirer.dsp.filter.fir.complex.ComplexFIRFilter; -import io.github.dsheirer.dsp.psk.DQPSKDemodulator; -import io.github.dsheirer.dsp.psk.InterpolatingSampleBuffer; -import io.github.dsheirer.dsp.psk.InterpolatingSampleBufferInstrumented; -import io.github.dsheirer.dsp.psk.pll.CostasLoop; -import io.github.dsheirer.sample.Listener; -import io.github.dsheirer.sample.complex.ComplexBuffer; -import io.github.dsheirer.sample.complex.IComplexBufferListener; -import io.github.dsheirer.source.tuner.frequency.FrequencyChangeEvent; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.ScheduledExecutorService; - -public class P25_C4FMDecoder2 extends P25Decoder implements IComplexBufferListener, Listener -{ - private final static Logger mLog = LoggerFactory.getLogger(P25_C4FMDecoder2.class); - protected static final double SYMBOL_RATE = 4800; - protected InterpolatingSampleBuffer mInterpolatingSampleBuffer; - protected DQPSKDemodulator mQPSKDemodulator; - protected CostasLoop mCostasLoop; - protected P25MessageFramer mMessageFramer; - protected double mSampleRate; - private Map mBasebandFilters = new HashMap<>(); - private ComplexFIRFilter mBasebandFilter; - - public P25_C4FMDecoder2(AliasList aliasList) - { - super(aliasList); - setSampleRate(48000.0); - } - - public void setSampleRate(double sampleRate) - { - if(sampleRate <= SYMBOL_RATE * 2) - { - throw new IllegalArgumentException("Sample rate must be at least twice the symbol rate [4800]"); - } - - mSampleRate = sampleRate; - mBasebandFilter = new ComplexFIRFilter(getBasebandFilter(), 1.0f); - - mCostasLoop = new CostasLoop(mSampleRate, SYMBOL_RATE); - - mInterpolatingSampleBuffer = new InterpolatingSampleBufferInstrumented((float)(sampleRate / SYMBOL_RATE)); - mQPSKDemodulator = new DQPSKDemodulator(mCostasLoop, mInterpolatingSampleBuffer); - - //Message framer can trigger a symbol-inversion correction to the PLL when detected - mMessageFramer = new P25MessageFramer(getAliasList(), mCostasLoop); - mMessageFramer.setListener(getMessageProcessor()); - mQPSKDemodulator.setDibitListener(mMessageFramer); - } - - @Override - public void receive(ComplexBuffer complexBuffer) - { - ComplexBuffer filtered = filter(complexBuffer); - mQPSKDemodulator.receive(filtered); - } - - protected ComplexBuffer filter(ComplexBuffer complexBuffer) - { - return mBasebandFilter.filter(complexBuffer); - } - - private double getSampleRate() - { - return mSampleRate; - } - - private float[] getBasebandFilter() - { - float[] filter = mBasebandFilters.get(getSampleRate()); - - if(filter == null) - { - FIRFilterSpecification specification = FIRFilterSpecification.lowPassBuilder() - .sampleRate((int)getSampleRate()) - .passBandCutoff(5100) - .passBandAmplitude(1.0) - .passBandRipple(0.01) - .stopBandAmplitude(0.0) - .stopBandStart(6500) - .stopBandRipple(0.01) - .build(); - - try - { - filter = FilterFactory.getTaps(specification); - - } - catch(FilterDesignException fde) - { - mLog.error("Couldn't design low pass baseband filter for sample rate: " + getSampleRate()); - } - if(filter != null) - { - mBasebandFilters.put(getSampleRate(), filter); - } - else - { - throw new IllegalStateException("Couldn't design a C4FM symbol filter for sample rate: " + mSampleRate); - } - } - - return filter; - } - - public void dispose() - { - super.dispose(); - - mBasebandFilter.dispose(); - mBasebandFilter = null; - - mMessageFramer.dispose(); - mMessageFramer = null; - } - - @Override - public void setFrequencyChangeListener(Listener listener) - { - } - - @Override - public void removeFrequencyChangeListener() - { - } - - @Override - public Listener getFrequencyChangeListener() - { - return new Listener() - { - @Override - public void receive(FrequencyChangeEvent frequencyChangeEvent) - { - switch(frequencyChangeEvent.getEvent()) - { - case NOTIFICATION_FREQUENCY_CHANGE: - case NOTIFICATION_FREQUENCY_CORRECTION_CHANGE: - case NOTIFICATION_CHANNEL_FREQUENCY_CORRECTION_CHANGE: - case NOTIFICATION_SAMPLE_RATE_CHANGE: - mCostasLoop.reset(); - break; - } - } - }; - } - - @Override - public Listener getComplexBufferListener() - { - return P25_C4FMDecoder2.this; - } - - public Modulation getModulation() - { - return Modulation.C4FM; - } - - @Override - public void reset() - { - mCostasLoop.reset(); - } - - @Override - public void start(ScheduledExecutorService executor) - { - } - - @Override - public void stop() - { - } -} diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/P25_C4FMDecoder2Instrumented.java b/src/main/java/io/github/dsheirer/module/decode/p25/P25_C4FMDecoderInstrumented.java similarity index 97% rename from src/main/java/io/github/dsheirer/module/decode/p25/P25_C4FMDecoder2Instrumented.java rename to src/main/java/io/github/dsheirer/module/decode/p25/P25_C4FMDecoderInstrumented.java index 4877261f..903396a7 100644 --- a/src/main/java/io/github/dsheirer/module/decode/p25/P25_C4FMDecoder2Instrumented.java +++ b/src/main/java/io/github/dsheirer/module/decode/p25/P25_C4FMDecoderInstrumented.java @@ -23,7 +23,7 @@ import io.github.dsheirer.sample.Listener; import io.github.dsheirer.sample.complex.Complex; import io.github.dsheirer.sample.complex.ComplexBuffer; -public class P25_C4FMDecoder2Instrumented extends P25_C4FMDecoder2 +public class P25_C4FMDecoderInstrumented extends P25_C4FMDecoder { private Listener mPLLPhaseErrorListener; private Listener mPLLFrequencyListener; @@ -32,7 +32,7 @@ public class P25_C4FMDecoder2Instrumented extends P25_C4FMDecoder2 private Listener mFilteredSymbolListener; private Listener mSymbolDecisionDataListener; - public P25_C4FMDecoder2Instrumented(AliasList aliasList) + public P25_C4FMDecoderInstrumented(AliasList aliasList) { super(aliasList); } diff --git a/src/main/java/io/github/dsheirer/source/tuner/TunerChannelSource.java b/src/main/java/io/github/dsheirer/source/tuner/TunerChannelSource.java index c577789e..6804d8d7 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/TunerChannelSource.java +++ b/src/main/java/io/github/dsheirer/source/tuner/TunerChannelSource.java @@ -469,6 +469,7 @@ public class TunerChannelSource extends ComplexSource implements IFrequencyChang { //Frequency correction requests are the only change requests supported from downstream components case REQUEST_CHANNEL_FREQUENCY_CORRECTION_CHANGE: + mLog.debug("Got a freq corr change request: " + event.getValue().intValue()); setFrequencyCorrection(event.getValue().intValue()); break; }