diff --git a/src/main/java/io/github/dsheirer/bits/IBinarySymbolProcessor.java b/src/main/java/io/github/dsheirer/bits/IBinarySymbolProcessor.java new file mode 100644 index 00000000..a46185ad --- /dev/null +++ b/src/main/java/io/github/dsheirer/bits/IBinarySymbolProcessor.java @@ -0,0 +1,21 @@ +/******************************************************************************* + * 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.bits; + +public interface IBinarySymbolProcessor +{ + void receive(boolean symbol); +} diff --git a/src/main/java/io/github/dsheirer/bits/MessageFramer.java b/src/main/java/io/github/dsheirer/bits/MessageFramer.java index 3a3acc64..27bdcabe 100644 --- a/src/main/java/io/github/dsheirer/bits/MessageFramer.java +++ b/src/main/java/io/github/dsheirer/bits/MessageFramer.java @@ -32,7 +32,7 @@ import java.util.List; * Will extract multiple messages simultaneously, for each sync pattern that is * encountered within the bitset bit stream. */ -public class MessageFramer implements Listener, SyncDetectProvider +public class MessageFramer implements IBinarySymbolProcessor, Listener, SyncDetectProvider { private boolean[] mSyncPattern; private int mMessageLength; diff --git a/src/main/java/io/github/dsheirer/dsp/afsk/AFSK1200Decoder.java b/src/main/java/io/github/dsheirer/dsp/afsk/AFSK1200Decoder.java index fd022a49..ca72ac66 100644 --- a/src/main/java/io/github/dsheirer/dsp/afsk/AFSK1200Decoder.java +++ b/src/main/java/io/github/dsheirer/dsp/afsk/AFSK1200Decoder.java @@ -15,7 +15,7 @@ ******************************************************************************/ package io.github.dsheirer.dsp.afsk; -import io.github.dsheirer.bits.MessageFramer; +import io.github.dsheirer.bits.IBinarySymbolProcessor; import io.github.dsheirer.buffer.FloatAveragingBuffer; import io.github.dsheirer.dsp.filter.design.FilterDesignException; import io.github.dsheirer.dsp.filter.fir.FIRFilterSpecification; @@ -96,7 +96,7 @@ public class AFSK1200Decoder implements Listener protected AFSKSampleBuffer mSampleBuffer; protected AFSKTimingErrorDetector mTimingErrorDetector = new AFSKTimingErrorDetector(SAMPLES_PER_SYMBOL); private RealFIRFilter2 mBandPassFilter = new RealFIRFilter2(sBandPassFilterCoefficients); - protected MessageFramer mMessageFramer; + protected IBinarySymbolProcessor mBinarySymbolProcessor; private boolean mSampleDecision; //Resample to an integral of the baud rate 1200 baud * 6 samples per symbol = 7200.0 Hertz @@ -155,20 +155,20 @@ public class AFSK1200Decoder implements Listener protected void dispatch(boolean symbol) { - if(mMessageFramer != null) + if(mBinarySymbolProcessor != null) { - mMessageFramer.receive(mNormalOutput ? symbol : !symbol); + mBinarySymbolProcessor.receive(mNormalOutput ? symbol : !symbol); } } /** * Registers a listener to receive decoded LTR symbols. * - * @param messageFramer to receive symbols. + * @param binarySymbolProcessor to receive symbols. */ - public void setMessageFramer(MessageFramer messageFramer) + public void setSymbolProcessor(IBinarySymbolProcessor binarySymbolProcessor) { - mMessageFramer = messageFramer; + mBinarySymbolProcessor = binarySymbolProcessor; } /** @@ -176,7 +176,7 @@ public class AFSK1200Decoder implements Listener */ public void removeListener() { - mMessageFramer = null; + mBinarySymbolProcessor = null; } diff --git a/src/main/java/io/github/dsheirer/dsp/afsk/AFSK1200DecoderInstrumented.java b/src/main/java/io/github/dsheirer/dsp/afsk/AFSK1200DecoderInstrumented.java index 5aef1105..dd0e444f 100644 --- a/src/main/java/io/github/dsheirer/dsp/afsk/AFSK1200DecoderInstrumented.java +++ b/src/main/java/io/github/dsheirer/dsp/afsk/AFSK1200DecoderInstrumented.java @@ -40,11 +40,7 @@ public class AFSK1200DecoderInstrumented extends AFSK1200Decoder protected void dispatch(boolean symbol) { - if(mMessageFramer != null) - { - mMessageFramer.receive(mNormalOutput ? symbol : !symbol); - } - + super.dispatch(symbol); mSymbolBroadcaster.broadcast(symbol); } 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 5a3a3308..f0cfe14e 100644 --- a/src/main/java/io/github/dsheirer/module/decode/DecoderFactory.java +++ b/src/main/java/io/github/dsheirer/module/decode/DecoderFactory.java @@ -64,10 +64,10 @@ import io.github.dsheirer.module.decode.mdc1200.MDCDecoderState; import io.github.dsheirer.module.decode.mdc1200.MDCMessageFilter; import io.github.dsheirer.module.decode.mpt1327.DecodeConfigMPT1327; import io.github.dsheirer.module.decode.mpt1327.MPT1327Decoder; -import io.github.dsheirer.module.decode.mpt1327.MPT1327Decoder.Sync; import io.github.dsheirer.module.decode.mpt1327.MPT1327DecoderEditor; import io.github.dsheirer.module.decode.mpt1327.MPT1327DecoderState; import io.github.dsheirer.module.decode.mpt1327.MPT1327MessageFilter; +import io.github.dsheirer.module.decode.mpt1327.Sync; import io.github.dsheirer.module.decode.nbfm.DecodeConfigNBFM; import io.github.dsheirer.module.decode.nbfm.NBFMDecoder; import io.github.dsheirer.module.decode.nbfm.NBFMDecoderEditor; @@ -175,15 +175,12 @@ public class DecoderFactory break; case MPT1327: DecodeConfigMPT1327 mptConfig = (DecodeConfigMPT1327) decodeConfig; - ChannelMap channelMap = channelMapModel.getChannelMap(mptConfig.getChannelMapName()); - Sync sync = mptConfig.getSync(); - modules.add(new MPT1327Decoder(aliasList, sync)); - - modules.add(new MPT1327DecoderState(aliasList, channelMap, channelType, mptConfig.getCallTimeout() * 1000)); + modules.add(new FMDemodulatorModule(CHANNEL_BANDWIDTH, DEMODULATED_AUDIO_SAMPLE_RATE)); + modules.add(new AudioModule(metadata)); if(channelType == ChannelType.STANDARD) { @@ -191,9 +188,6 @@ public class DecoderFactory channel.getRecordConfiguration(), channel.getSystem(), channel.getSite(), (aliasList != null ? aliasList.getName() : null), mptConfig.getTrafficChannelPoolSize())); } - - modules.add(new FMDemodulatorModule(CHANNEL_BANDWIDTH, DEMODULATED_AUDIO_SAMPLE_RATE)); - modules.add(new AudioModule(metadata)); break; case PASSPORT: modules.add(new PassportDecoder(decodeConfig, aliasList)); diff --git a/src/main/java/io/github/dsheirer/module/decode/fleetsync2/Fleetsync2Decoder.java b/src/main/java/io/github/dsheirer/module/decode/fleetsync2/Fleetsync2Decoder.java index 5196376f..dc3f52af 100644 --- a/src/main/java/io/github/dsheirer/module/decode/fleetsync2/Fleetsync2Decoder.java +++ b/src/main/java/io/github/dsheirer/module/decode/fleetsync2/Fleetsync2Decoder.java @@ -34,21 +34,40 @@ public class Fleetsync2Decoder extends AbstractAFSKDecoder private MessageFramer mMessageFramer; private Fleetsync2MessageProcessor mMessageProcessor; + /** + * Constructs a decoder for Fleetsync II protocol + * @param afsk1200Decoder to use for decoding the symbols + * @param aliasList to assign aliases to decoded values + */ public Fleetsync2Decoder(AFSK1200Decoder afsk1200Decoder, AliasList aliasList) { super(afsk1200Decoder); + init(aliasList); + } + + /** + * Constructs a decoder for Fleetsync II protocol + * @param aliasList to assign aliases to decoded values + */ + public Fleetsync2Decoder(AliasList aliasList) + { + super(AFSK1200Decoder.Output.NORMAL); + init(aliasList); + } + + /** + * Initializes the decoding chain. + * @param aliasList to assign to the message processor + */ + private void init(AliasList aliasList) + { mMessageFramer = new MessageFramer(SyncPattern.FLEETSYNC2.getPattern(), MESSAGE_LENGTH); - getDecoder().setMessageFramer(mMessageFramer); + getDecoder().setSymbolProcessor(mMessageFramer); mMessageProcessor = new Fleetsync2MessageProcessor(aliasList); mMessageFramer.addMessageListener(mMessageProcessor); mMessageProcessor.setMessageListener(getMessageListener()); } - public Fleetsync2Decoder(AliasList aliasList) - { - this(new AFSK1200Decoder(AFSK1200Decoder.Output.NORMAL), aliasList); - } - @Override public DecoderType getDecoderType() { diff --git a/src/main/java/io/github/dsheirer/module/decode/mpt1327/DecodeConfigMPT1327.java b/src/main/java/io/github/dsheirer/module/decode/mpt1327/DecodeConfigMPT1327.java index 172ee2e7..40b5a3c6 100644 --- a/src/main/java/io/github/dsheirer/module/decode/mpt1327/DecodeConfigMPT1327.java +++ b/src/main/java/io/github/dsheirer/module/decode/mpt1327/DecodeConfigMPT1327.java @@ -20,7 +20,6 @@ package io.github.dsheirer.module.decode.mpt1327; import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; import io.github.dsheirer.module.decode.DecoderType; import io.github.dsheirer.module.decode.config.DecodeConfiguration; -import io.github.dsheirer.module.decode.mpt1327.MPT1327Decoder.Sync; public class DecodeConfigMPT1327 extends DecodeConfiguration { diff --git a/src/main/java/io/github/dsheirer/module/decode/mpt1327/MPT1327Decoder.java b/src/main/java/io/github/dsheirer/module/decode/mpt1327/MPT1327Decoder.java index 6dc1fc84..eef7ba35 100644 --- a/src/main/java/io/github/dsheirer/module/decode/mpt1327/MPT1327Decoder.java +++ b/src/main/java/io/github/dsheirer/module/decode/mpt1327/MPT1327Decoder.java @@ -1,161 +1,65 @@ /******************************************************************************* - * 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.mpt1327; import io.github.dsheirer.alias.AliasList; +import io.github.dsheirer.bits.IBinarySymbolProcessor; import io.github.dsheirer.bits.MessageFramer; -import io.github.dsheirer.bits.SyncPattern; -import io.github.dsheirer.dsp.filter.FilterFactory; -import io.github.dsheirer.dsp.filter.Filters; -import io.github.dsheirer.dsp.filter.fir.FIRFilterSpecification; -import io.github.dsheirer.dsp.filter.fir.real.RealFIRFilter_RB_RB; -import io.github.dsheirer.dsp.filter.halfband.real.HalfBandFilter_RB_RB; -import io.github.dsheirer.dsp.fsk.FSK2Decoder; -import io.github.dsheirer.instrument.Instrumentable; -import io.github.dsheirer.instrument.tap.Tap; -import io.github.dsheirer.instrument.tap.TapGroup; -import io.github.dsheirer.instrument.tap.stream.BinaryTap; -import io.github.dsheirer.instrument.tap.stream.FloatBufferTap; -import io.github.dsheirer.instrument.tap.stream.FloatTap; -import io.github.dsheirer.module.decode.Decoder; +import io.github.dsheirer.dsp.afsk.AFSK1200Decoder; import io.github.dsheirer.module.decode.DecoderType; -import io.github.dsheirer.sample.Broadcaster; -import io.github.dsheirer.sample.Listener; -import io.github.dsheirer.sample.real.IFilteredRealBufferListener; -import io.github.dsheirer.sample.real.RealBuffer; +import io.github.dsheirer.module.decode.afsk.AbstractAFSKDecoder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.ArrayList; -import java.util.List; - -/** - * MPT1327 Decoder - 1200 baud 2FSK decoder that can process 48k sample rate - * complex or floating point samples and output fully framed MPT1327 control and - * traffic messages. - */ -public class MPT1327Decoder extends Decoder implements IFilteredRealBufferListener, Instrumentable +public class MPT1327Decoder extends AbstractAFSKDecoder implements IBinarySymbolProcessor { private final static Logger mLog = LoggerFactory.getLogger(MPT1327Decoder.class); - private static final FIRFilterSpecification HIGH_PASS_SPECIFICATION = FIRFilterSpecification.highPassBuilder() - .sampleRate( 24000 ) - .stopBandCutoff( 800 ) - .stopBandAmplitude( 0.0 ) - .stopBandRipple( 0.03 ) - .passBandStart( 1000 ) - .passBandAmplitude( 1.0 ) - .passBandRipple( 0.08 ) - .build(); - - private static float[] HIGHPASS_FILTER; - - static - { - try - { - HIGHPASS_FILTER = FilterFactory.getTaps(HIGH_PASS_SPECIFICATION); - } - catch(Exception e) - { - mLog.error("Couldn't design MPT1327 highpass filter"); - } - } - - /* Decimated sample rate ( 48,000 / 2 = 24,000 ) feeding the decoder */ - private static final int sDECIMATED_SAMPLE_RATE = 24000; - - /* Baud or Symbol Rate */ - private static final int sSYMBOL_RATE = 1200; - /* Message length -- longest possible message is: * 4xREVS + 16xSYNC + 64xADD1 + 64xDCW1 + 64xDCW2 + 64xDCW3 + 64xDCW4 */ - private static final int sMESSAGE_LENGTH = 350; + private static final int MESSAGE_LENGTH = 350; - /* Instrumentation Taps */ - private static final String INSTRUMENT_HB1_FILTER_TO_HIGH_PASS = - "Tap Point: Half-band Decimation Filter > < Low Pass Filter"; - private static final String INSTRUMENT_HIGH_PASS_TO_DECODER = - "Tap Point: Low Pass Filter > < Decoder"; - private static final String INSTRUMENT_DECODER_TO_FRAMER = - "Tap Point: Decoder > < Sync Detect/Message Framer"; - private List mAvailableTaps; - - private HalfBandFilter_RB_RB mDecimationFilter; - private RealFIRFilter_RB_RB mHighPassFilter; - private FSK2Decoder mFSKDecoder; - private Broadcaster mSymbolBroadcaster; private MessageFramer mControlMessageFramer; private MessageFramer mTrafficMessageFramer; private MPT1327MessageProcessor mMessageProcessor; public MPT1327Decoder(AliasList aliasList, Sync sync) { - /** - * Normal: 2FSK Decoder with inverted output - * French: 2FSK Decoder with normal output - */ - if(sync == Sync.NORMAL) - { - mFSKDecoder = new FSK2Decoder(24000, 1200, FSK2Decoder.Output.INVERTED); - } - else if(sync == Sync.FRENCH) - { - mFSKDecoder = new FSK2Decoder(24000, 1200, FSK2Decoder.Output.NORMAL); - } - else - { - throw new IllegalArgumentException("MPT1327 Decoder - unrecognized Sync type"); - } + super((sync == Sync.NORMAL ? AFSK1200Decoder.Output.NORMAL : AFSK1200Decoder.Output.INVERTED)); - /* Decimation filter - 48000 / 2 = 24000 output */ - mDecimationFilter = new HalfBandFilter_RB_RB(Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO.getCoefficients(), 1.0002f, true); + getDecoder().setSymbolProcessor(this); - //High-pass filter the audio to remove any DC component or CTCSS tones. The audio has already been low-pass - //filtered by the AudioDemodulation filter cutoff of 3400 Hz. - mHighPassFilter = new RealFIRFilter_RB_RB(HIGHPASS_FILTER, 1.0f); - mDecimationFilter.setListener(mHighPassFilter); + //Message framer for control channel messages + mControlMessageFramer = new MessageFramer(sync.getControlSyncPattern().getPattern(), MESSAGE_LENGTH); - mHighPassFilter.setListener(mFSKDecoder); + //Message framer for traffic channel massages + mTrafficMessageFramer = new MessageFramer(sync.getTrafficSyncPattern().getPattern(), MESSAGE_LENGTH); - mSymbolBroadcaster = new Broadcaster(); - mFSKDecoder.setListener(mSymbolBroadcaster); - - /* Message framer for control channel messages */ - mControlMessageFramer = new MessageFramer(sync.getControlSyncPattern().getPattern(), sMESSAGE_LENGTH); - mSymbolBroadcaster.addListener(mControlMessageFramer); - - /* Message framer for traffic channel massages */ - mTrafficMessageFramer = new MessageFramer(sync.getTrafficSyncPattern().getPattern(), sMESSAGE_LENGTH); - mSymbolBroadcaster.addListener(mTrafficMessageFramer); - - /* Fully decoded and framed messages processor */ + //Fully decoded and framed messages processor mMessageProcessor = new MPT1327MessageProcessor(aliasList); mMessageProcessor.setMessageListener(getMessageListener()); + mControlMessageFramer.addMessageListener(mMessageProcessor); mTrafficMessageFramer.addMessageListener(mMessageProcessor); } - @Override - public Listener getFilteredRealBufferListener() + public void receive(boolean symbol) { - return mDecimationFilter; + mControlMessageFramer.receive(symbol); + mTrafficMessageFramer.receive(symbol); } @Override @@ -164,6 +68,13 @@ public class MPT1327Decoder extends Decoder implements IFilteredRealBufferListen return DecoderType.MPT1327; } + @Override + public void reset() + { + mControlMessageFramer.reset(); + mTrafficMessageFramer.reset(); + } + /** * Cleanup method */ @@ -171,128 +82,9 @@ public class MPT1327Decoder extends Decoder implements IFilteredRealBufferListen { super.dispose(); - mSymbolBroadcaster.dispose(); - - mControlMessageFramer.dispose(); - - mFSKDecoder.dispose(); - - mDecimationFilter.dispose(); - mMessageProcessor.dispose(); - + mControlMessageFramer.dispose(); mTrafficMessageFramer.dispose(); } - /* Instrumentation Taps */ - @Override - public List getTapGroups() - { - if(mAvailableTaps == null) - { - mAvailableTaps = new ArrayList(); - - TapGroup group = new TapGroup("MPT-1327 Decoder"); - - group.add(new FloatTap(INSTRUMENT_HB1_FILTER_TO_HIGH_PASS, 0, 0.5f)); - group.add(new FloatTap(INSTRUMENT_HIGH_PASS_TO_DECODER, 0, 0.5f)); - group.add(new BinaryTap(INSTRUMENT_DECODER_TO_FRAMER, 0, 0.025f)); - - mAvailableTaps.add(group); - - /* Add the taps from the FSK decoder */ - mAvailableTaps.addAll(mFSKDecoder.getTapGroups()); - } - - return mAvailableTaps; - } - - @Override - public void registerTap(Tap tap) - { - /* Send request to decoder */ - mFSKDecoder.registerTap(tap); - - switch(tap.getName()) - { - case INSTRUMENT_HB1_FILTER_TO_HIGH_PASS: - FloatBufferTap hb1Tap = (FloatBufferTap) tap; - mDecimationFilter.setListener(hb1Tap); - hb1Tap.setListener(mHighPassFilter); - break; - case INSTRUMENT_HIGH_PASS_TO_DECODER: - FloatBufferTap lowTap = (FloatBufferTap) tap; - mHighPassFilter.setListener(lowTap); - lowTap.setListener(mFSKDecoder); - break; - case INSTRUMENT_DECODER_TO_FRAMER: - BinaryTap decoderTap = (BinaryTap) tap; - mFSKDecoder.setListener(decoderTap); - decoderTap.setListener(mSymbolBroadcaster); - break; - } - } - - @Override - public void unregisterTap(Tap tap) - { - mFSKDecoder.unregisterTap(tap); - - switch(tap.getName()) - { - case INSTRUMENT_HB1_FILTER_TO_HIGH_PASS: - mDecimationFilter.setListener(mHighPassFilter); - break; - case INSTRUMENT_HIGH_PASS_TO_DECODER: - mHighPassFilter.setListener(mFSKDecoder); - break; - case INSTRUMENT_DECODER_TO_FRAMER: - mFSKDecoder.setListener(mSymbolBroadcaster); - break; - } - } - - public enum Sync - { - NORMAL("Normal", SyncPattern.MPT1327_CONTROL, SyncPattern.MPT1327_TRAFFIC), - FRENCH("French", SyncPattern.MPT1327_CONTROL_FRENCH, SyncPattern.MPT1327_TRAFFIC_FRENCH); - - private String mLabel; - private SyncPattern mControlSyncPattern; - private SyncPattern mTrafficSyncPattern; - - private Sync(String label, SyncPattern controlPattern, SyncPattern trafficPattern) - { - mLabel = label; - mControlSyncPattern = controlPattern; - mTrafficSyncPattern = trafficPattern; - } - - public String getLabel() - { - return mLabel; - } - - public SyncPattern getControlSyncPattern() - { - return mControlSyncPattern; - } - - public SyncPattern getTrafficSyncPattern() - { - return mTrafficSyncPattern; - } - - public String toString() - { - return getLabel(); - } - } - - @Override - public void reset() - { - mControlMessageFramer.reset(); - mTrafficMessageFramer.reset(); - } } diff --git a/src/main/java/io/github/dsheirer/module/decode/mpt1327/MPT1327DecoderLegacy.java b/src/main/java/io/github/dsheirer/module/decode/mpt1327/MPT1327DecoderLegacy.java new file mode 100644 index 00000000..be8fef25 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/mpt1327/MPT1327DecoderLegacy.java @@ -0,0 +1,253 @@ +/******************************************************************************* + * sdrtrunk + * Copyright (C) 2014-2017 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.mpt1327; + +import io.github.dsheirer.alias.AliasList; +import io.github.dsheirer.bits.MessageFramer; +import io.github.dsheirer.dsp.filter.FilterFactory; +import io.github.dsheirer.dsp.filter.Filters; +import io.github.dsheirer.dsp.filter.fir.FIRFilterSpecification; +import io.github.dsheirer.dsp.filter.fir.real.RealFIRFilter_RB_RB; +import io.github.dsheirer.dsp.filter.halfband.real.HalfBandFilter_RB_RB; +import io.github.dsheirer.dsp.fsk.FSK2Decoder; +import io.github.dsheirer.instrument.Instrumentable; +import io.github.dsheirer.instrument.tap.Tap; +import io.github.dsheirer.instrument.tap.TapGroup; +import io.github.dsheirer.instrument.tap.stream.BinaryTap; +import io.github.dsheirer.instrument.tap.stream.FloatBufferTap; +import io.github.dsheirer.instrument.tap.stream.FloatTap; +import io.github.dsheirer.module.decode.Decoder; +import io.github.dsheirer.module.decode.DecoderType; +import io.github.dsheirer.sample.Broadcaster; +import io.github.dsheirer.sample.Listener; +import io.github.dsheirer.sample.real.IFilteredRealBufferListener; +import io.github.dsheirer.sample.real.RealBuffer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.List; + +/** + * MPT1327 Decoder - 1200 baud 2FSK decoder that can process 48k sample rate + * complex or floating point samples and output fully framed MPT1327 control and + * traffic messages. + */ +public class MPT1327DecoderLegacy extends Decoder implements IFilteredRealBufferListener, Instrumentable +{ + private final static Logger mLog = LoggerFactory.getLogger(MPT1327DecoderLegacy.class); + + private static final FIRFilterSpecification HIGH_PASS_SPECIFICATION = FIRFilterSpecification.highPassBuilder() + .sampleRate( 24000 ) + .stopBandCutoff( 800 ) + .stopBandAmplitude( 0.0 ) + .stopBandRipple( 0.03 ) + .passBandStart( 1000 ) + .passBandAmplitude( 1.0 ) + .passBandRipple( 0.08 ) + .build(); + + private static float[] HIGHPASS_FILTER; + + static + { + try + { + HIGHPASS_FILTER = FilterFactory.getTaps(HIGH_PASS_SPECIFICATION); + } + catch(Exception e) + { + mLog.error("Couldn't design MPT1327 highpass filter"); + } + } + + /* Decimated sample rate ( 48,000 / 2 = 24,000 ) feeding the decoder */ + private static final int sDECIMATED_SAMPLE_RATE = 24000; + + /* Baud or Symbol Rate */ + private static final int sSYMBOL_RATE = 1200; + + /* Message length -- longest possible message is: + * 4xREVS + 16xSYNC + 64xADD1 + 64xDCW1 + 64xDCW2 + 64xDCW3 + 64xDCW4 */ + private static final int sMESSAGE_LENGTH = 350; + + /* Instrumentation Taps */ + private static final String INSTRUMENT_HB1_FILTER_TO_HIGH_PASS = + "Tap Point: Half-band Decimation Filter > < Low Pass Filter"; + private static final String INSTRUMENT_HIGH_PASS_TO_DECODER = + "Tap Point: Low Pass Filter > < Decoder"; + private static final String INSTRUMENT_DECODER_TO_FRAMER = + "Tap Point: Decoder > < Sync Detect/Message Framer"; + private List mAvailableTaps; + + private HalfBandFilter_RB_RB mDecimationFilter; + private RealFIRFilter_RB_RB mHighPassFilter; + private FSK2Decoder mFSKDecoder; + private Broadcaster mSymbolBroadcaster; + private MessageFramer mControlMessageFramer; + private MessageFramer mTrafficMessageFramer; + private MPT1327MessageProcessor mMessageProcessor; + + public MPT1327DecoderLegacy(AliasList aliasList, Sync sync) + { + /** + * Normal: 2FSK Decoder with inverted output + * French: 2FSK Decoder with normal output + */ + if(sync == Sync.NORMAL) + { + mFSKDecoder = new FSK2Decoder(24000, 1200, FSK2Decoder.Output.INVERTED); + } + else if(sync == Sync.FRENCH) + { + mFSKDecoder = new FSK2Decoder(24000, 1200, FSK2Decoder.Output.NORMAL); + } + else + { + throw new IllegalArgumentException("MPT1327 Decoder - unrecognized Sync type"); + } + + /* Decimation filter - 48000 / 2 = 24000 output */ + mDecimationFilter = new HalfBandFilter_RB_RB(Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO.getCoefficients(), 1.0002f, true); + + //High-pass filter the audio to remove any DC component or CTCSS tones. The audio has already been low-pass + //filtered by the AudioDemodulation filter cutoff of 3400 Hz. + mHighPassFilter = new RealFIRFilter_RB_RB(HIGHPASS_FILTER, 1.0f); + mDecimationFilter.setListener(mHighPassFilter); + + mHighPassFilter.setListener(mFSKDecoder); + + mSymbolBroadcaster = new Broadcaster(); + mFSKDecoder.setListener(mSymbolBroadcaster); + + /* Message framer for control channel messages */ + mControlMessageFramer = new MessageFramer(sync.getControlSyncPattern().getPattern(), sMESSAGE_LENGTH); + mSymbolBroadcaster.addListener(mControlMessageFramer); + + /* Message framer for traffic channel massages */ + mTrafficMessageFramer = new MessageFramer(sync.getTrafficSyncPattern().getPattern(), sMESSAGE_LENGTH); + mSymbolBroadcaster.addListener(mTrafficMessageFramer); + + /* Fully decoded and framed messages processor */ + mMessageProcessor = new MPT1327MessageProcessor(aliasList); + mMessageProcessor.setMessageListener(getMessageListener()); + mControlMessageFramer.addMessageListener(mMessageProcessor); + mTrafficMessageFramer.addMessageListener(mMessageProcessor); + } + + @Override + public Listener getFilteredRealBufferListener() + { + return mDecimationFilter; + } + + @Override + public DecoderType getDecoderType() + { + return DecoderType.MPT1327; + } + + /** + * Cleanup method + */ + public void dispose() + { + super.dispose(); + + mSymbolBroadcaster.dispose(); + + mControlMessageFramer.dispose(); + + mFSKDecoder.dispose(); + + mDecimationFilter.dispose(); + + mMessageProcessor.dispose(); + + mTrafficMessageFramer.dispose(); + } + + /* Instrumentation Taps */ + @Override + public List getTapGroups() + { + if(mAvailableTaps == null) + { + mAvailableTaps = new ArrayList(); + + TapGroup group = new TapGroup("MPT-1327 Decoder"); + + group.add(new FloatTap(INSTRUMENT_HB1_FILTER_TO_HIGH_PASS, 0, 0.5f)); + group.add(new FloatTap(INSTRUMENT_HIGH_PASS_TO_DECODER, 0, 0.5f)); + group.add(new BinaryTap(INSTRUMENT_DECODER_TO_FRAMER, 0, 0.025f)); + + mAvailableTaps.add(group); + + /* Add the taps from the FSK decoder */ + mAvailableTaps.addAll(mFSKDecoder.getTapGroups()); + } + + return mAvailableTaps; + } + + @Override + public void registerTap(Tap tap) + { + /* Send request to decoder */ + mFSKDecoder.registerTap(tap); + + switch(tap.getName()) + { + case INSTRUMENT_HB1_FILTER_TO_HIGH_PASS: + FloatBufferTap hb1Tap = (FloatBufferTap) tap; + mDecimationFilter.setListener(hb1Tap); + hb1Tap.setListener(mHighPassFilter); + break; + case INSTRUMENT_HIGH_PASS_TO_DECODER: + FloatBufferTap lowTap = (FloatBufferTap) tap; + mHighPassFilter.setListener(lowTap); + lowTap.setListener(mFSKDecoder); + break; + case INSTRUMENT_DECODER_TO_FRAMER: + BinaryTap decoderTap = (BinaryTap) tap; + mFSKDecoder.setListener(decoderTap); + decoderTap.setListener(mSymbolBroadcaster); + break; + } + } + + @Override + public void unregisterTap(Tap tap) + { + mFSKDecoder.unregisterTap(tap); + + switch(tap.getName()) + { + case INSTRUMENT_HB1_FILTER_TO_HIGH_PASS: + mDecimationFilter.setListener(mHighPassFilter); + break; + case INSTRUMENT_HIGH_PASS_TO_DECODER: + mHighPassFilter.setListener(mFSKDecoder); + break; + case INSTRUMENT_DECODER_TO_FRAMER: + mFSKDecoder.setListener(mSymbolBroadcaster); + break; + } + } +} diff --git a/src/main/java/io/github/dsheirer/module/decode/mpt1327/Sync.java b/src/main/java/io/github/dsheirer/module/decode/mpt1327/Sync.java new file mode 100644 index 00000000..33adc048 --- /dev/null +++ b/src/main/java/io/github/dsheirer/module/decode/mpt1327/Sync.java @@ -0,0 +1,55 @@ +/******************************************************************************* + * 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.mpt1327; + +import io.github.dsheirer.bits.SyncPattern; + +public enum Sync +{ + NORMAL("Normal", SyncPattern.MPT1327_CONTROL, SyncPattern.MPT1327_TRAFFIC), + FRENCH("French", SyncPattern.MPT1327_CONTROL_FRENCH, SyncPattern.MPT1327_TRAFFIC_FRENCH); + + private String mLabel; + private SyncPattern mControlSyncPattern; + private SyncPattern mTrafficSyncPattern; + + Sync(String label, SyncPattern controlPattern, SyncPattern trafficPattern) + { + mLabel = label; + mControlSyncPattern = controlPattern; + mTrafficSyncPattern = trafficPattern; + } + + public String getLabel() + { + return mLabel; + } + + public SyncPattern getControlSyncPattern() + { + return mControlSyncPattern; + } + + public SyncPattern getTrafficSyncPattern() + { + return mTrafficSyncPattern; + } + + public String toString() + { + return getLabel(); + } +}