From da1db8a8040945757b18c618dd290999354716ef Mon Sep 17 00:00:00 2001 From: "dennis.sheirer" Date: Thu, 1 May 2014 23:24:51 +0000 Subject: [PATCH] Improved LTR FSK decoder for LTR, LTR-Net and Passport decoders. Added IIR DC blocking filter and used improved slicer from the FSK decoder. Added instrumentation to Passport decoder and updated some of the instrumentation components. --- .classpath | 16 ++ ...uffer.java => BooleanAveragingBuffer.java} | 4 +- src/buffer/DelayBuffer.java | 67 ++++++ src/decode/DecoderType.java | 3 +- src/decode/ltrnet/LTRNetDecoder.java | 12 +- src/decode/ltrstandard/LTRFSKDemodulator.java | 132 ---------- .../ltrstandard/LTRFSKDemodulator2.java | 122 ---------- .../ltrstandard/LTRStandardDecoder.java | 11 +- src/decode/passport/PassportDecoder.java | 49 +++- src/decode/passport/PassportMessage.java | 11 +- src/dsp/FloatToBinarySlicerLTR.java | 137 ----------- src/dsp/Slicer.java | 155 ++++++++++++ src/dsp/filter/BooleanAveragingFilter.java | 74 ++---- src/dsp/filter/DCRemovalFilter3.java | 45 ++++ src/dsp/filter/LTRPulseShapingFilter.java | 103 ++++++++ src/dsp/filter/SquaringFilter.java | 2 +- src/dsp/fsk/FSK2Decoder.java | 10 +- src/dsp/fsk/LTRFSKDecoder.java | 227 ++++++++++++++++++ src/instrument/gui/BinaryTapViewPanel.java | 2 + src/instrument/gui/DecoderViewFrame.java | 53 +++- src/instrument/gui/FloatTapViewPanel.java | 16 +- src/instrument/gui/SampleModel.java | 29 ++- .../gui/SymbolEventTapViewPanel.java | 15 +- src/instrument/gui/WaveSourceFrame.java | 3 + src/instrument/tap/stream/SymbolEventTap.java | 1 + 25 files changed, 803 insertions(+), 496 deletions(-) rename src/buffer/{AveragingCircularBooleanBuffer.java => BooleanAveragingBuffer.java} (96%) create mode 100644 src/buffer/DelayBuffer.java delete mode 100644 src/decode/ltrstandard/LTRFSKDemodulator.java delete mode 100644 src/decode/ltrstandard/LTRFSKDemodulator2.java delete mode 100644 src/dsp/FloatToBinarySlicerLTR.java create mode 100644 src/dsp/Slicer.java create mode 100644 src/dsp/filter/DCRemovalFilter3.java create mode 100644 src/dsp/filter/LTRPulseShapingFilter.java create mode 100644 src/dsp/fsk/LTRFSKDecoder.java diff --git a/.classpath b/.classpath index fb565a58..f5d9ad92 100644 --- a/.classpath +++ b/.classpath @@ -2,5 +2,21 @@ + + + + + + + + + + + + + + + + diff --git a/src/buffer/AveragingCircularBooleanBuffer.java b/src/buffer/BooleanAveragingBuffer.java similarity index 96% rename from src/buffer/AveragingCircularBooleanBuffer.java rename to src/buffer/BooleanAveragingBuffer.java index 714d34dd..73e6201e 100644 --- a/src/buffer/AveragingCircularBooleanBuffer.java +++ b/src/buffer/BooleanAveragingBuffer.java @@ -27,12 +27,12 @@ import java.util.Arrays; * * Also provides averaging over the elements in the buffer */ -public class AveragingCircularBooleanBuffer { +public class BooleanAveragingBuffer { private boolean[] mBuffer; private int mBufferPointer; - public AveragingCircularBooleanBuffer( int length ) + public BooleanAveragingBuffer( int length ) { mBuffer = new boolean[ length ]; diff --git a/src/buffer/DelayBuffer.java b/src/buffer/DelayBuffer.java new file mode 100644 index 00000000..001058db --- /dev/null +++ b/src/buffer/DelayBuffer.java @@ -0,0 +1,67 @@ +/******************************************************************************* + * 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 + ******************************************************************************/ +package buffer; + +import sample.Listener; + +/** + * Delay Buffer - implements a circular delay buffer backed by an object array + */ +public class DelayBuffer implements Listener +{ + private Object[] mBuffer; + private int mBufferPointer = 0; + private Listener mListener; + + public DelayBuffer( int length ) + { + mBuffer = new Object[ length ]; + } + + @Override + @SuppressWarnings( "unchecked" ) + public void receive( T value ) + { + Object delayed = mBuffer[ mBufferPointer ]; + + mBuffer[ mBufferPointer ] = value; + + mBufferPointer++; + + /* Wrap the buffer pointer around to 0 when necessary */ + if( mBufferPointer >= mBuffer.length ) + { + mBufferPointer = 0; + } + + if( mListener != null && delayed != null ) + { + mListener.receive( (T)delayed ); + } + } + + public void setListener( Listener listener ) + { + mListener = listener; + } + + public void removeListener( Listener listener ) + { + mListener = null; + } +} diff --git a/src/decode/DecoderType.java b/src/decode/DecoderType.java index ef192fc2..e57e5efc 100644 --- a/src/decode/DecoderType.java +++ b/src/decode/DecoderType.java @@ -66,7 +66,8 @@ public enum DecoderType { return EnumSet.of( DecoderType.FLEETSYNC2, DecoderType.MDC1200, - DecoderType.MPT1327 ); + DecoderType.MPT1327, + DecoderType.PASSPORT ); } /** diff --git a/src/decode/ltrnet/LTRNetDecoder.java b/src/decode/ltrnet/LTRNetDecoder.java index 23c0b07b..186ca753 100644 --- a/src/decode/ltrnet/LTRNetDecoder.java +++ b/src/decode/ltrnet/LTRNetDecoder.java @@ -24,8 +24,8 @@ import bits.MessageFramer; import bits.SyncPattern; import decode.Decoder; import decode.DecoderType; -import decode.ltrstandard.LTRFSKDemodulator; import dsp.filter.DCRemovalFilter2; +import dsp.fsk.LTRFSKDecoder; import dsp.nbfm.FilteringNBFMDemodulator; public class LTRNetDecoder extends Decoder @@ -39,7 +39,7 @@ public class LTRNetDecoder extends Decoder public static final int sLTR_STANDARD_MESSAGE_LENGTH = 40; private FilteringNBFMDemodulator mDemodulator; private DCRemovalFilter2 mDCRemovalFilter; - private LTRFSKDemodulator mLTRFSKDemodulator; + private LTRFSKDecoder mLTRFSKDecoder; private MessageFramer mLTRMessageFramer; private LTRNetMessageProcessor mLTRMessageProcessor; @@ -75,7 +75,7 @@ public class LTRNetDecoder extends Decoder mDCRemovalFilter.setListener( this.getFloatReceiver() ); } - mLTRFSKDemodulator = new LTRFSKDemodulator(); + mLTRFSKDecoder = new LTRFSKDecoder(); /** * The DC removal filter will impact performance of the LTRFSKDemod, @@ -83,11 +83,11 @@ public class LTRNetDecoder extends Decoder */ if( mSourceSampleType == SampleType.COMPLEX ) { - mDemodulator.addListener( mLTRFSKDemodulator ); + mDemodulator.addListener( mLTRFSKDecoder ); } else { - mDCRemovalFilter.setListener( mLTRFSKDemodulator ); + mDCRemovalFilter.setListener( mLTRFSKDecoder ); } if( direction == MessageDirection.OSW ) @@ -103,7 +103,7 @@ public class LTRNetDecoder extends Decoder sLTR_STANDARD_MESSAGE_LENGTH ); } - mLTRFSKDemodulator.addListener( mLTRMessageFramer ); + mLTRFSKDecoder.addListener( mLTRMessageFramer ); mLTRMessageProcessor = new LTRNetMessageProcessor( direction, aliasList ); mLTRMessageFramer.addMessageListener( mLTRMessageProcessor ); diff --git a/src/decode/ltrstandard/LTRFSKDemodulator.java b/src/decode/ltrstandard/LTRFSKDemodulator.java deleted file mode 100644 index a0091f7c..00000000 --- a/src/decode/ltrstandard/LTRFSKDemodulator.java +++ /dev/null @@ -1,132 +0,0 @@ -/******************************************************************************* - * 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 - ******************************************************************************/ -package decode.ltrstandard; - -import sample.Broadcaster; -import sample.Listener; -import dsp.BooleanZeroCrossingDetector; -import dsp.FloatToBinarySlicer; -import dsp.filter.BooleanAveragingFilter; -import dsp.filter.Filters; -import dsp.filter.FloatFIRFilter; -import dsp.filter.FloatHalfBandFilter; -import dsp.filter.FloatHalfBandNoDecimateFilter; -import dsp.filter.SquaringFilter; - -public class LTRFSKDemodulator implements Listener -{ - FloatHalfBandFilter mHBFilter1; - FloatHalfBandFilter mHBFilter2; - FloatHalfBandFilter mHBFilter3; - FloatHalfBandFilter mHBFilter4; - FloatHalfBandFilter mHBFilter5; - FloatHalfBandNoDecimateFilter mHBFilter6; - FloatFIRFilter mDCBlocker; - - SquaringFilter mSquaringFilter; - BooleanAveragingFilter mAveragingFilter; - BooleanZeroCrossingDetector mZeroCrossingDetector; - FloatToBinarySlicer mSlicer; - Broadcaster mBroadcaster = new Broadcaster(); - - //debug -// FloatWaveRecorder mRecorder; - /** - * Implements a Logic Trunked Radio sub-audible 300 baud FSK signaling - * decoder. Expects a 48k short-valued sample rate, which is decimated - * to a final 1.5k sample rate via five cascaded decimating half-band - * filters, and applies a sixth, non-decimating half-band filter to bring - * the overall bandwidth down to .75k. Each half band filter requires - * 8 multiply/accumulate (MAC) cycles per input sample, totaling 2560 - * MAC filtering cycles per output bit, to get from 48k input to 300-baud - * bit stream output. - * - * Applies a balancer to the demodulated fsk waveform to normalize the - * values about the zero-axis and to offset fluctuations induced by the - * DC correction filter. - * - * Converts filtered, decimated samples to squared bit values and then - * applies a zero crossing detector to make a baud decision every 5 samples. - * - * Outputs the decoded bit stream. - */ - public LTRFSKDemodulator() - { - mHBFilter1 = new FloatHalfBandFilter( - Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO, 1.0002 ); - - mHBFilter2 = new FloatHalfBandFilter( - Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO, 1.0002 ); - mHBFilter1.setListener( mHBFilter2 ); - - mHBFilter3 = new FloatHalfBandFilter( - Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO, 1.0002 ); - mHBFilter2.setListener( mHBFilter3 ); - - mHBFilter4 = new FloatHalfBandFilter( - Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO, 1.0002 ); - mHBFilter3.setListener( mHBFilter4 ); - - mHBFilter5 = new FloatHalfBandFilter( - Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO, 1.0002 ); - mHBFilter4.setListener( mHBFilter5 ); - - mHBFilter6 = new FloatHalfBandNoDecimateFilter( - Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO, 1.0002 ); - mHBFilter5.setListener( mHBFilter6 ); - -// mDCBlocker = new FloatFIRFilter( -// Filters.FIRHP_LTR_DCBLOCK_750FS_50FC.getCoefficients(), 1.0f ); -// mHBFilter6.setListener( mDCBlocker ); - - /** - * Set the slicer's long buffer to a sample length of 14 baud periods - * ( 5 * 14 )since 12 bauds should be the longest string of ones we'll - * encounter - * - * Set the short buffer to two baud periods (5 * 2) - */ - //Set the buffer to 5 samples per baud times 12 bauds. Set the sync - //lock period to 5 samples per baud times 31 bits (155 samples) - mSlicer = new FloatToBinarySlicer( 70, 5 ); - mHBFilter6.setListener( mSlicer ); -// mDCBlocker.setListener( mSlicer ); - - mAveragingFilter = new BooleanAveragingFilter( 5 ); - mSlicer.setListener( mAveragingFilter ); - - mZeroCrossingDetector = new BooleanZeroCrossingDetector( 5 ); - mAveragingFilter.setListener( mZeroCrossingDetector ); - } - - @Override - public void receive( Float sample ) - { - mHBFilter1.receive( sample ); - } - - public void addListener( Listener listener ) - { - mZeroCrossingDetector.setListener( listener ); - } - - public void removeListener( Listener listener ) - { - mZeroCrossingDetector.removeListener( listener ); - } -} diff --git a/src/decode/ltrstandard/LTRFSKDemodulator2.java b/src/decode/ltrstandard/LTRFSKDemodulator2.java deleted file mode 100644 index 8eb37223..00000000 --- a/src/decode/ltrstandard/LTRFSKDemodulator2.java +++ /dev/null @@ -1,122 +0,0 @@ -/******************************************************************************* - * 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 - ******************************************************************************/ -package decode.ltrstandard; - -import sample.Listener; -import dsp.BooleanZeroCrossingDetector; -import dsp.FloatToBinarySlicerLTR; -import dsp.SyncDetectListener; -import dsp.filter.BooleanAveragingFilter; -import dsp.filter.Filters; -import dsp.filter.FloatHalfBandFilter; -import dsp.filter.FloatHalfBandNoDecimateFilter; -import dsp.filter.SquaringFilter; - -public class LTRFSKDemodulator2 implements Listener, SyncDetectListener -{ - FloatHalfBandFilter mHBFilter1; - FloatHalfBandFilter mHBFilter2; - FloatHalfBandFilter mHBFilter3; - FloatHalfBandFilter mHBFilter4; - FloatHalfBandFilter mHBFilter5; - FloatHalfBandNoDecimateFilter mHBFilter6; - - SquaringFilter mSquaringFilter; - BooleanAveragingFilter mAveragingFilter; - BooleanZeroCrossingDetector mZeroCrossingDetector; - FloatToBinarySlicerLTR mSlicer; - - /** - * Implements a Logic Trunked Radio sub-audible 300 baud FSK signaling - * decoder. Expects a 48k short-valued sample rate, which is decimated - * to a final 1.5k sample rate via five cascaded decimating half-band - * filters, and applies a sixth, non-decimating half-band filter to bring - * the overall bandwidth down to .75k. Each half band filter requires - * 8 multiply/accumulate (MAC) cycles per input sample, totaling 2560 - * MAC filtering cycles per output bit, to get from 48k input to 300-baud - * bit stream output. - * - * Applies a balancer to the demodulated fsk waveform to normalize the - * values about the zero-axis and to offset fluctuations induced by the - * DC correction filter. - * - * Converts filtered, decimated samples to squared bit values and then - * applies a zero crossing detector to make a baud decision every 5 samples. - * - * Outputs the decoded bit stream. - */ - public LTRFSKDemodulator2( int syncLength, int messageLength ) - { - mHBFilter1 = new FloatHalfBandFilter( - Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO, 1.0002 ); - - mHBFilter2 = new FloatHalfBandFilter( - Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO, 1.0002 ); - mHBFilter1.setListener( mHBFilter2 ); - - mHBFilter3 = new FloatHalfBandFilter( - Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO, 1.0002 ); - mHBFilter2.setListener( mHBFilter3 ); - - mHBFilter4 = new FloatHalfBandFilter( - Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO, 1.0002 ); - mHBFilter3.setListener( mHBFilter4 ); - - mHBFilter5 = new FloatHalfBandFilter( - Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO, 1.0002 ); - mHBFilter4.setListener( mHBFilter5 ); - - mHBFilter6 = new FloatHalfBandNoDecimateFilter( - Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO, 1.0002 ); - mHBFilter5.setListener( mHBFilter6 ); - - mSlicer = new FloatToBinarySlicerLTR( syncLength, messageLength, 5 ); - mHBFilter6.setListener( mSlicer ); - - mAveragingFilter = new BooleanAveragingFilter( 5 ); - mSlicer.setListener( mAveragingFilter ); - - mZeroCrossingDetector = new BooleanZeroCrossingDetector( 5 ); - mAveragingFilter.setListener( mZeroCrossingDetector ); - } - - @Override - public void receive( Float sample ) - { - mHBFilter1.receive( sample ); - } - - public void setListener( Listener listener ) - { - mZeroCrossingDetector.setListener( listener ); - } - - public void removeListener( Listener listener ) - { - mZeroCrossingDetector.removeListener( listener ); - } - - /** - * Pass the sync detection down to the slicer - */ - @Override - public void syncDetected() - { - mSlicer.syncDetected(); - } -} diff --git a/src/decode/ltrstandard/LTRStandardDecoder.java b/src/decode/ltrstandard/LTRStandardDecoder.java index 423e4c27..e89fc302 100644 --- a/src/decode/ltrstandard/LTRStandardDecoder.java +++ b/src/decode/ltrstandard/LTRStandardDecoder.java @@ -25,6 +25,7 @@ import bits.SyncPattern; import decode.Decoder; import decode.DecoderType; import dsp.filter.DCRemovalFilter2; +import dsp.fsk.LTRFSKDecoder; import dsp.nbfm.FilteringNBFMDemodulator; public class LTRStandardDecoder extends Decoder @@ -38,7 +39,7 @@ public class LTRStandardDecoder extends Decoder public static final int sLTR_STANDARD_MESSAGE_LENGTH = 40; private FilteringNBFMDemodulator mDemodulator; private DCRemovalFilter2 mDCRemovalFilter; - private LTRFSKDemodulator mLTRFSKDemodulator; + private LTRFSKDecoder mLTRFSKDecoder; private MessageFramer mLTRMessageFramer; private LTRStandardMessageProcessor mLTRMessageProcessor; @@ -74,7 +75,7 @@ public class LTRStandardDecoder extends Decoder mDCRemovalFilter.setListener( this.getFloatReceiver() ); } - mLTRFSKDemodulator = new LTRFSKDemodulator(); + mLTRFSKDecoder = new LTRFSKDecoder(); /** * The DC removal filter will impact performance of the LTRFSKDemod, @@ -82,11 +83,11 @@ public class LTRStandardDecoder extends Decoder */ if( mSourceSampleType == SampleType.COMPLEX ) { - mDemodulator.addListener( mLTRFSKDemodulator ); + mDemodulator.addListener( mLTRFSKDecoder ); } else { - mDCRemovalFilter.setListener( mLTRFSKDemodulator ); + mDCRemovalFilter.setListener( mLTRFSKDecoder ); } if( direction == MessageDirection.OSW ) @@ -102,7 +103,7 @@ public class LTRStandardDecoder extends Decoder sLTR_STANDARD_MESSAGE_LENGTH ); } - mLTRFSKDemodulator.addListener( mLTRMessageFramer ); + mLTRFSKDecoder.addListener( mLTRMessageFramer ); mLTRMessageProcessor = new LTRStandardMessageProcessor( direction, aliasList ); mLTRMessageFramer.addMessageListener( mLTRMessageProcessor ); diff --git a/src/decode/passport/PassportDecoder.java b/src/decode/passport/PassportDecoder.java index 27d38d14..b4ad78e1 100644 --- a/src/decode/passport/PassportDecoder.java +++ b/src/decode/passport/PassportDecoder.java @@ -17,17 +17,23 @@ ******************************************************************************/ package decode.passport; +import instrument.Instrumentable; +import instrument.tap.Tap; + +import java.util.ArrayList; +import java.util.List; + import source.Source.SampleType; import alias.AliasList; import bits.MessageFramer; import bits.SyncPattern; import decode.Decoder; import decode.DecoderType; -import decode.ltrstandard.LTRFSKDemodulator; import dsp.filter.DCRemovalFilter2; +import dsp.fsk.LTRFSKDecoder; import dsp.nbfm.FilteringNBFMDemodulator; -public class PassportDecoder extends Decoder +public class PassportDecoder extends Decoder implements Instrumentable { /** * This value determines how quickly the DC remove filter responds to @@ -39,9 +45,11 @@ public class PassportDecoder extends Decoder public static final int sPASSPORT_SYNC_LENGTH = 9; private FilteringNBFMDemodulator mNBFMDemodulator; private DCRemovalFilter2 mDCRemovalFilter; - private LTRFSKDemodulator mPassportFSKDemodulator; + private LTRFSKDecoder mPassportFSKDecoder; private MessageFramer mPassportMessageFramer; private PassportMessageProcessor mPassportMessageProcessor; + + private List mAvailableTaps; public PassportDecoder( SampleType sampleType, AliasList aliasList ) { @@ -72,8 +80,8 @@ public class PassportDecoder extends Decoder mDCRemovalFilter.setListener( this.getFloatReceiver() ); } - mPassportFSKDemodulator = - new LTRFSKDemodulator(); + mPassportFSKDecoder = + new LTRFSKDecoder(); /** * The DC removal filter will impact performance of the LTRFSKDemod, @@ -81,11 +89,11 @@ public class PassportDecoder extends Decoder */ if( mSourceSampleType == SampleType.COMPLEX ) { - mNBFMDemodulator.addListener( mPassportFSKDemodulator ); + mNBFMDemodulator.addListener( mPassportFSKDecoder ); } else { - addFloatListener( mPassportFSKDemodulator ); + addFloatListener( mPassportFSKDecoder ); } @@ -93,7 +101,7 @@ public class PassportDecoder extends Decoder new MessageFramer( SyncPattern.PASSPORT.getPattern(), sPASSPORT_MESSAGE_LENGTH ); - mPassportFSKDemodulator.addListener( mPassportMessageFramer ); + mPassportFSKDecoder.addListener( mPassportMessageFramer ); mPassportMessageProcessor = new PassportMessageProcessor( aliasList ); mPassportMessageFramer.addMessageListener( mPassportMessageProcessor ); @@ -105,4 +113,29 @@ public class PassportDecoder extends Decoder { return DecoderType.PASSPORT; } + + @Override + public List getTaps() + { + if( mAvailableTaps == null ) + { + mAvailableTaps = new ArrayList(); + + mAvailableTaps.addAll( mPassportFSKDecoder.getTaps() ); + } + + return mAvailableTaps; + } + + @Override + public void addTap( Tap tap ) + { + mPassportFSKDecoder.addTap( tap ); + } + + @Override + public void removeTap( Tap tap ) + { + mPassportFSKDecoder.removeTap( tap ); + } } diff --git a/src/decode/passport/PassportMessage.java b/src/decode/passport/PassportMessage.java index 283e7d57..8379405d 100644 --- a/src/decode/passport/PassportMessage.java +++ b/src/decode/passport/PassportMessage.java @@ -175,8 +175,13 @@ public class PassportMessage extends Message public Alias getTalkgroupIDAlias() { int tg = getTalkgroupID(); - - return mAliasList.getTalkgroupAlias( String.valueOf( tg ) ); + + if( mAliasList != null ) + { + return mAliasList.getTalkgroupAlias( String.valueOf( tg ) ); + } + + return null; } public int getLCN() @@ -262,7 +267,7 @@ public class PassportMessage extends Message { String min = getMobileID(); - if( min != null ) + if( mAliasList != null && min != null ) { return mAliasList.getMobileIDNumberAlias( min ); } diff --git a/src/dsp/FloatToBinarySlicerLTR.java b/src/dsp/FloatToBinarySlicerLTR.java deleted file mode 100644 index 9160980e..00000000 --- a/src/dsp/FloatToBinarySlicerLTR.java +++ /dev/null @@ -1,137 +0,0 @@ -/******************************************************************************* - * 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 - ******************************************************************************/ -package dsp; - -import sample.Listener; -import buffer.FloatCircularAveragingBuffer2; - -/** - * Binary slicer tuned to operate on LTR type short burst signals in a heavy - * DC bias environment coupled with transmitter automatic frequency correction - * being applied as the transmission progresses. - * - * Uses a pair of float buffers that calculate the average of the samples - * contained in the buffer. - * - * The leading and trailing buffers are the same length as the sync pattern. - * - * The slicer uses the Leading buffer's average as the slicing reference for - * normal operation. - * - * Once a sync pattern is detected, the trailing buffer should have the sync - * pattern bauds and the leading buffer should have the first of the message - * bits. At this point, ie sync detect, we switch to using the average of both - * buffers as the slicing reference point. - * - * Once the reference counter counts down to indicate that there are sync length - * bauds remaining of the message, and those remaining bauds should be in the - * leading buffer, we swich to using the trailing buffer as the slicing - * reference point for the remainder of the message. - * - * As the reference counter decrements to 0, meaning the message should be - * clear of both buffers, we switch back to normal operation of using the - * leading buffer as the slicing reference. - * - * An external sync detector indicates a sync pattern match by using the - * syncDetected() method, which immediately switches the slicing average - * reference to both, and sets the counter to the expected message length. - */ -public class FloatToBinarySlicerLTR implements Listener, - SyncDetectListener - -{ - private enum SlicingReference - { - LEADING, TRAILING, BOTH; - } - - private SlicingReference mSlicingReference = SlicingReference.LEADING; - private FloatCircularAveragingBuffer2 mLeadingBuffer; - private FloatCircularAveragingBuffer2 mTrailingBuffer; - private int mReferenceCounter = 0; - private int mSyncLength; - private int mMessageLength; - private Listener mListener; - - public FloatToBinarySlicerLTR( int syncLength, - int messageLength, - int samplesPerBaud ) - { - mSyncLength = syncLength * samplesPerBaud; - mMessageLength = messageLength * samplesPerBaud; - - mLeadingBuffer = new FloatCircularAveragingBuffer2( mSyncLength ); - mTrailingBuffer = new FloatCircularAveragingBuffer2( mSyncLength ); - } - - @Override - public void receive( Float newestSample ) - { - float middleSample = mLeadingBuffer.get( newestSample ); - mTrailingBuffer.get( middleSample ); - - //Dispatch the decision - if( mListener != null ) - { - switch( mSlicingReference ) - { - case LEADING: - mListener.receive( middleSample > mLeadingBuffer.average() ); - break; - case BOTH: - mListener.receive( middleSample > - ( ( mLeadingBuffer.average() + - mTrailingBuffer.average() ) / 2.0f ) ); - break; - case TRAILING: - mListener.receive( middleSample > mTrailingBuffer.average() ); - break; - } - - mReferenceCounter--; - - if( mReferenceCounter <= mSyncLength && mReferenceCounter > 0 ) - { - mSlicingReference = SlicingReference.TRAILING; - } - else if( mReferenceCounter <= 0 ) - { - mSlicingReference = SlicingReference.LEADING; - - mReferenceCounter = 0; - } - } - } - - @Override - public void syncDetected() - { - mSlicingReference = SlicingReference.BOTH; - mReferenceCounter = mMessageLength; - } - - public void setListener( Listener listener ) - { - mListener = listener; - } - - public void clearListener() - { - mListener = null; - } -} diff --git a/src/dsp/Slicer.java b/src/dsp/Slicer.java new file mode 100644 index 00000000..f8493363 --- /dev/null +++ b/src/dsp/Slicer.java @@ -0,0 +1,155 @@ +package dsp; + +import instrument.tap.Tap; +import instrument.tap.stream.SymbolEventTap; + +import java.util.ArrayList; +import java.util.BitSet; +import java.util.List; + +import sample.Listener; +import dsp.fsk.SymbolEvent; +import dsp.fsk.SymbolEvent.Shift; + +/** + * Symbol slicer with auto-aligning baud timing + */ +public class Slicer implements Listener +{ + public enum Output{ NORMAL, INVERTED }; + + private BitSet mBitSet = new BitSet(); + private int mSymbolLength; + private int mDecisionThreshold; + private int mSampleCounter; + private SymbolEventTap mSymbolEventTap; + + private boolean mNormalOutput = true; + private Listener mListener; + + public Slicer( Output output, int samplesPerSymbol ) + { + mNormalOutput = ( output == Output.NORMAL ); + + mSymbolLength = samplesPerSymbol; + + /* Round up */ + mDecisionThreshold = (int)( mSymbolLength / 2 ) + ( mSymbolLength % 2 ); + } + + public void receive( Boolean sample ) + { + if( mSampleCounter >= 0 ) + { + if( sample ) + { + mBitSet.set( mSampleCounter ); + } + else + { + mBitSet.clear( mSampleCounter ); + } + } + + mSampleCounter++; + + if( mSampleCounter >= mSymbolLength ) + { + boolean decision = mBitSet.cardinality() >= mDecisionThreshold; + + send( decision ); + + /* Shift timing left if the left bit in the bitset is opposite + * the decision and the right bit is the same */ + if( ( mBitSet.get( 0 ) ^ decision ) && + ( !( mBitSet.get( mSymbolLength - 1 ) ^ decision ) ) ) + { + sendTapEvent( mBitSet, Shift.LEFT, decision ); + + reset(); + + mSampleCounter--; + } + /* Shift timing right if the left bit is the same as the + * decision and the right bit is opposite */ + else if( ( !( mBitSet.get( 0 ) ^ decision ) ) && + ( mBitSet.get( mSymbolLength - 1 ) ^ decision ) ) + { + sendTapEvent( mBitSet, Shift.RIGHT, decision ); + + /* Last bit from previous symbol to pre-fill next symbol */ + boolean previousSoftBit = mBitSet.get( mSymbolLength - 1 ); + + reset(); + + if( previousSoftBit ) + { + mBitSet.set( 0 ); + } + + mSampleCounter++; + } + /* No shift */ + else + { + sendTapEvent( mBitSet, Shift.NONE, decision ); + + reset(); + } + } + } + + /** + * Sends the bit decision to the listener + */ + private void send( boolean decision ) + { + if( mListener != null ) + { + mListener.receive( mNormalOutput ? decision : !decision ); + } + } + + private void reset() + { + mBitSet.clear(); + mSampleCounter = 0; + } + + public void setListener( Listener listener) + { + mListener = listener; + } + + public void removeListener( Listener listener ) + { + mListener = null; + } + + /** + * Sends instrumentation tap event to all registered listeners + */ + private void sendTapEvent( BitSet bitset, Shift shift, boolean decision ) + { + if( mSymbolEventTap != null ) + { + SymbolEvent event = + new SymbolEvent( bitset.get( 0, mSymbolLength ), + mSymbolLength, + decision, + shift ); + + mSymbolEventTap.receive( event ); + } + } + + public void addTap( SymbolEventTap tap ) + { + mSymbolEventTap = tap; + } + + public void removeTap() + { + mSymbolEventTap = null; + } +} diff --git a/src/dsp/filter/BooleanAveragingFilter.java b/src/dsp/filter/BooleanAveragingFilter.java index bdfe7185..5c0fe09f 100644 --- a/src/dsp/filter/BooleanAveragingFilter.java +++ b/src/dsp/filter/BooleanAveragingFilter.java @@ -17,84 +17,50 @@ ******************************************************************************/ package dsp.filter; -import java.util.Arrays; +import java.util.BitSet; import sample.Listener; /** - * Averages (low pass filter) boolean values over the defined length + * Averages (low pass filter) boolean values over the defined size */ public class BooleanAveragingFilter implements Listener { - private boolean[] mBuffer; + private BitSet mBuffer = new BitSet(); private int mBufferPointer; + private int mBufferLength; private int mThreshold; private Listener mListener; public BooleanAveragingFilter( int length ) { - mBuffer = new boolean[ length ]; - - if( length % 2 == 0 ) + mBufferLength = length; + + /* Round up */ + mThreshold = (int)( length / 2) + ( length % 2 ); + } + + public void receive( Boolean value ) + { + if( value ) { - mThreshold = (int)( length /2 ); + mBuffer.set( mBufferPointer ); } else { - mThreshold = (int)( ( length + 1 ) / 2 ); + mBuffer.clear( mBufferPointer ); } - - //Preload the array with false values - Arrays.fill( mBuffer, false ); - } - - /** - * Loads the newValue into this buffer and adjusts the buffer pointer - * to prepare for the next get/put cycle - */ - public void put( boolean newValue ) - { - //Store the new value to the buffer - mBuffer[ mBufferPointer ] = newValue; - - //Increment the buffer pointer - mBufferPointer++; - //Wrap the buffer pointer around to 0 when necessary - if( mBufferPointer >= mBuffer.length ) + mBufferPointer++; + + if( mBufferPointer >= mBufferLength ) { mBufferPointer = 0; } - } - - /** - * Loads the newValue into the buffer, calculates the average - * and returns that average from this method - * - * This effectively performs low-pass filtering - */ - public void receive( Boolean newValue ) - { - //Load the new value into the buffer - put( newValue ); - int trueCount = 0; - - for( int x = 0; x < mBuffer.length; x++ ) - { - if( mBuffer[ x ] ) - { - trueCount++; - } - } - - /** - * If the number of true values in the buffer is - * more than half, send true, otherwise, false - */ if( mListener != null ) { - mListener.receive( trueCount >= mThreshold ); + mListener.receive( mBuffer.cardinality() >= mThreshold ); } } @@ -103,7 +69,7 @@ public class BooleanAveragingFilter implements Listener mListener = listener; } - public void clearListener() + public void removeListener( Listener listener ) { mListener = null; } diff --git a/src/dsp/filter/DCRemovalFilter3.java b/src/dsp/filter/DCRemovalFilter3.java new file mode 100644 index 00000000..b584abfb --- /dev/null +++ b/src/dsp/filter/DCRemovalFilter3.java @@ -0,0 +1,45 @@ +package dsp.filter; + +import sample.Listener; + +public class DCRemovalFilter3 implements Listener +{ + private float mAlpha; + private float mPreviousInput = 0.0f; + private float mPreviousOutput = 0.0f; + + private Listener mListener; + + /** + * IIR single-pole DC removal filter, as described by J M de Freitas in + * 29Jan2007 paper at: + * + * http://www.mathworks.com/matlabcentral/fileexchange/downloads/72134/download + * + * @param alpha 0.0 - 1.0 float - the closer alpha is to unity, the closer + * the cutoff frequency is to DC. + */ + public DCRemovalFilter3( float alpha ) + { + mAlpha = alpha; + } + @Override + public void receive( Float currentInput ) + { + float currentOutput = ( currentInput - mPreviousInput ) + + ( mAlpha * mPreviousOutput ); + + if( mListener != null ) + { + mListener.receive( currentOutput ); + } + + mPreviousInput = currentInput; + mPreviousOutput = currentOutput; + } + + public void setListener( Listener listener ) + { + mListener = listener; + } +} diff --git a/src/dsp/filter/LTRPulseShapingFilter.java b/src/dsp/filter/LTRPulseShapingFilter.java new file mode 100644 index 00000000..43af44c9 --- /dev/null +++ b/src/dsp/filter/LTRPulseShapingFilter.java @@ -0,0 +1,103 @@ +package dsp.filter; + +import java.util.BitSet; + +import sample.Listener; + +public class LTRPulseShapingFilter implements Listener +{ + private int[][] mIndexes = { { 0, 1, 2, 3, 4 }, + { 1, 2, 3, 4, 0 }, + { 2, 3, 4, 0, 1 }, + { 3, 4, 0, 1, 2 }, + { 4, 0, 1, 2, 3 } }; + + private BitSet mBuffer; + private int mBufferPointer = 0; + private Listener mListener; + + /** + * Specialized pulse shaping filter that detects a weak 0 or 1 pulse that + * is characterized by two consecutive values, true or false, within a + * sequence of five boolean values where the remainder are the opposite + * value, and elongates that weak pulse to be three consecutive values, + * which is enough to cause the slicer to render a correct value. + */ + public LTRPulseShapingFilter() + { + mBuffer = new BitSet(); + mBuffer.clear(); + } + + @Override + public void receive( Boolean current ) + { + boolean previous = mBuffer.get( mBufferPointer ); + + if( current ) + { + mBuffer.set( mBufferPointer ); + } + else + { + mBuffer.clear( mBufferPointer ); + } + + mBufferPointer++; + + if( mBufferPointer >= 5 ) + { + mBufferPointer = 0; + } + + /* Check for weak positive pulse*/ + if( mBuffer.cardinality() == 2 ) + { + if( mBuffer.get( mIndexes[ mBufferPointer ][ 1 ] ) && + mBuffer.get( mIndexes[ mBufferPointer ][ 2 ] ) ) + { + mBuffer.set( mIndexes[ mBufferPointer][ 3 ] ); + } + } + /* Or, a weak negative pulse */ + else if( mBuffer.cardinality() == 3 ) + { + if( !mBuffer.get( mIndexes[ mBufferPointer][ 1 ] ) && + !mBuffer.get( mIndexes[ mBufferPointer][ 2 ] ) ) + { + mBuffer.clear( mIndexes[ mBufferPointer][ 3 ] ); + } + } + + if( mListener != null ) + { + mListener.receive( previous ); + } + } + + public String getIndexSet( int pointer ) + { + StringBuilder sb = new StringBuilder(); + sb.append( "[" ); + + for( int x = 0; x < 5; x++ ) + { + sb.append( mIndexes[ pointer ][ x ] ); + sb.append( "," ); + } + + sb.append( "[" ); + + return sb.toString(); + } + + public void setListener( Listener listener ) + { + mListener = listener; + } + + public void removeListener( Listener listener ) + { + mListener = null; + } +} diff --git a/src/dsp/filter/SquaringFilter.java b/src/dsp/filter/SquaringFilter.java index b169c45d..7b380845 100644 --- a/src/dsp/filter/SquaringFilter.java +++ b/src/dsp/filter/SquaringFilter.java @@ -35,7 +35,7 @@ public class SquaringFilter implements Listener { if( mListener != null ) { - mListener.receive( ( sample >= 0 ? true : false ) ); + mListener.receive( sample >= 0 ); } } diff --git a/src/dsp/fsk/FSK2Decoder.java b/src/dsp/fsk/FSK2Decoder.java index 5e36dbf9..a4ad9f93 100644 --- a/src/dsp/fsk/FSK2Decoder.java +++ b/src/dsp/fsk/FSK2Decoder.java @@ -26,7 +26,7 @@ import java.util.BitSet; import java.util.List; import sample.Listener; -import buffer.AveragingCircularBooleanBuffer; +import buffer.BooleanAveragingBuffer; import dsp.fsk.SymbolEvent.Shift; /** @@ -60,8 +60,8 @@ public class FSK2Decoder implements Instrumentable, Listener private ArrayList mTaps = new ArrayList(); private Listener mListener; - private AveragingCircularBooleanBuffer mDelayBuffer; - private AveragingCircularBooleanBuffer mLowPassFilter; + private BooleanAveragingBuffer mDelayBuffer; + private BooleanAveragingBuffer mLowPassFilter; private Slicer mSlicer; private boolean mNormalOutput; private int mSamplesPerSymbol; @@ -76,8 +76,8 @@ public class FSK2Decoder implements Instrumentable, Listener mNormalOutput = ( output == Output.NORMAL ); mSymbolRate = symbolRate; - mDelayBuffer = new AveragingCircularBooleanBuffer( mSamplesPerSymbol ); - mLowPassFilter = new AveragingCircularBooleanBuffer( mSamplesPerSymbol ); + mDelayBuffer = new BooleanAveragingBuffer( mSamplesPerSymbol ); + mLowPassFilter = new BooleanAveragingBuffer( mSamplesPerSymbol ); mSlicer = new Slicer( mSamplesPerSymbol ); } diff --git a/src/dsp/fsk/LTRFSKDecoder.java b/src/dsp/fsk/LTRFSKDecoder.java new file mode 100644 index 00000000..04d7fce9 --- /dev/null +++ b/src/dsp/fsk/LTRFSKDecoder.java @@ -0,0 +1,227 @@ +/******************************************************************************* + * 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 + ******************************************************************************/ +package dsp.fsk; + +import instrument.Instrumentable; +import instrument.tap.Tap; +import instrument.tap.stream.BinaryTap; +import instrument.tap.stream.FloatTap; +import instrument.tap.stream.SymbolEventTap; + +import java.util.ArrayList; +import java.util.List; + +import sample.Listener; +import dsp.Slicer; +import dsp.Slicer.Output; +import dsp.filter.DCRemovalFilter3; +import dsp.filter.Filters; +import dsp.filter.FloatHalfBandFilter; +import dsp.filter.FloatHalfBandNoDecimateFilter; +import dsp.filter.LTRPulseShapingFilter; +import dsp.filter.SquaringFilter; + +public class LTRFSKDecoder implements Listener, Instrumentable +{ + private FloatHalfBandFilter mHBFilter1; + private FloatHalfBandFilter mHBFilter2; + private FloatHalfBandFilter mHBFilter3; + private FloatHalfBandFilter mHBFilter4; + private FloatHalfBandFilter mHBFilter5; + private FloatHalfBandNoDecimateFilter mHBFilter6; + private DCRemovalFilter3 mDCFilter; + private SquaringFilter mSquaringFilter; + private LTRPulseShapingFilter mPulseShaper; + private Slicer mSlicer; + + private List mAvailableTaps; + private final String TAP_F1_F2 = "LTR FSK Demod Filter1 >< Filter2"; + private final String TAP_F2_F3 = "LTR FSK Demod Filter2 >< Filter3"; + private final String TAP_F3_F4 = "LTR FSK Demod Filter3 >< Filter4"; + private final String TAP_F4_F5 = "LTR FSK Demod Filter4 >< Filter5"; + private final String TAP_F5_F6 = "LTR FSK Demod Filter5 >< Filter6"; + private final String TAP_F6_DT = "LTR FSK Demod Filter6 >< DC Filter"; + private final String TAP_DT_SF1 = "LTR FSK Demod DC Filter >< Squaring Filter"; + private final String TAP_SF1_PS = "LTR FSK Demod Squaring Filter >< Pulse Shaper"; + private final String TAP_PS_SLICER = "LTR FSK Demod Pulse Shaper >< Slicer"; + private final String TAP_SYMBOL_EVENT = "LTR FSK Demod Symbol Event"; + + /** + * Implements a Logic Trunked Radio sub-audible 300 baud FSK signaling + * decoder. Expects a 48000 sample rate input. + */ + public LTRFSKDecoder() + { + mHBFilter1 = new FloatHalfBandFilter( + Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO, 1.1002 ); + + mHBFilter2 = new FloatHalfBandFilter( + Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO, 1.1002 ); + mHBFilter1.setListener( mHBFilter2 ); + + mHBFilter3 = new FloatHalfBandFilter( + Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO, 1.1002 ); + mHBFilter2.setListener( mHBFilter3 ); + + mHBFilter4 = new FloatHalfBandFilter( + Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO, 1.1002 ); + mHBFilter3.setListener( mHBFilter4 ); + + mHBFilter5 = new FloatHalfBandFilter( + Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO, 1.1002 ); + mHBFilter4.setListener( mHBFilter5 ); + + mHBFilter6 = new FloatHalfBandNoDecimateFilter( + Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO, 1.1002 ); + mHBFilter5.setListener( mHBFilter6 ); + + mDCFilter = new DCRemovalFilter3( 0.9946f ); + mHBFilter6.setListener( mDCFilter ); + + mSquaringFilter = new SquaringFilter(); + mDCFilter.setListener( mSquaringFilter ); + + mPulseShaper = new LTRPulseShapingFilter(); + mSquaringFilter.setListener( mPulseShaper ); + + mSlicer = new Slicer( Output.NORMAL, 5 ); + mPulseShaper.setListener( mSlicer ); + } + + @Override + public void receive( Float sample ) + { + mHBFilter1.receive( sample ); + } + + public void addListener( Listener listener ) + { + mSlicer.setListener( listener ); + } + + public void removeListener( Listener listener ) + { + mSlicer.removeListener( listener ); + } + + @Override + public List getTaps() + { + if( mAvailableTaps == null ) + { + mAvailableTaps = new ArrayList(); + mAvailableTaps.add( new FloatTap( TAP_F1_F2, 0, 1.0f ) ); + mAvailableTaps.add( new FloatTap( TAP_F2_F3, 8, 0.5f ) ); + mAvailableTaps.add( new FloatTap( TAP_F3_F4, 12, 0.25f ) ); + mAvailableTaps.add( new FloatTap( TAP_F4_F5, 14, 0.125f ) ); + mAvailableTaps.add( new FloatTap( TAP_F5_F6, 15, 0.0625f ) ); + mAvailableTaps.add( new FloatTap( TAP_F6_DT, 31, 0.0625f ) ); + mAvailableTaps.add( new FloatTap( TAP_DT_SF1, 31, 0.0625f ) ); + mAvailableTaps.add( new BinaryTap( TAP_SF1_PS, 33, 0.0625f ) ); + mAvailableTaps.add( new BinaryTap( TAP_PS_SLICER, 33, 0.0625f ) ); + mAvailableTaps.add( new SymbolEventTap( TAP_SYMBOL_EVENT, 7, 0.0125f ) ); + } + + return mAvailableTaps; + } + + @Override + public void addTap( Tap tap ) + { + switch( tap.getName() ) + { + case TAP_F1_F2: + mHBFilter1.setListener( (FloatTap)tap ); + ((FloatTap)tap).setListener( mHBFilter2 ); + break; + case TAP_F2_F3: + mHBFilter2.setListener( (FloatTap)tap ); + ((FloatTap)tap).setListener( mHBFilter3 ); + break; + case TAP_F3_F4: + mHBFilter3.setListener( (FloatTap)tap ); + ((FloatTap)tap).setListener( mHBFilter4 ); + break; + case TAP_F4_F5: + mHBFilter4.setListener( (FloatTap)tap ); + ((FloatTap)tap).setListener( mHBFilter5 ); + break; + case TAP_F5_F6: + mHBFilter5.setListener( (FloatTap)tap ); + ((FloatTap)tap).setListener( mHBFilter6 ); + break; + case TAP_F6_DT: + mHBFilter6.setListener( (FloatTap)tap ); + ((FloatTap)tap).setListener( mDCFilter ); + break; + case TAP_DT_SF1: + mDCFilter.setListener( (FloatTap)tap ); + ((FloatTap)tap).setListener( mSquaringFilter ); + break; + case TAP_SF1_PS: + mSquaringFilter.setListener( (BinaryTap)tap ); + ((BinaryTap)tap).setListener( mPulseShaper ); + break; + case TAP_PS_SLICER: + mPulseShaper.setListener( (BinaryTap)tap ); + ((BinaryTap)tap).setListener( mSlicer ); + break; + case TAP_SYMBOL_EVENT: + mSlicer.addTap( (SymbolEventTap)tap ); + break; + } + } + + @Override + public void removeTap( Tap tap ) + { + switch( tap.getName() ) + { + case TAP_F1_F2: + mHBFilter1.setListener( mHBFilter2 ); + break; + case TAP_F2_F3: + mHBFilter2.setListener( mHBFilter3 ); + break; + case TAP_F3_F4: + mHBFilter3.setListener( mHBFilter4 ); + break; + case TAP_F4_F5: + mHBFilter4.setListener( mHBFilter5 ); + break; + case TAP_F5_F6: + mHBFilter5.setListener( mHBFilter6 ); + break; + case TAP_F6_DT: + mHBFilter6.setListener( mDCFilter ); + break; + case TAP_DT_SF1: + mDCFilter.setListener( mSquaringFilter ); + break; + case TAP_SF1_PS: + mSquaringFilter.setListener( mPulseShaper ); + break; + case TAP_PS_SLICER: + mPulseShaper.setListener( mSlicer ); + break; + case TAP_SYMBOL_EVENT: + mSlicer.removeTap(); + break; + } + } +} diff --git a/src/instrument/gui/BinaryTapViewPanel.java b/src/instrument/gui/BinaryTapViewPanel.java index 60cabaee..b5d8f9bf 100644 --- a/src/instrument/gui/BinaryTapViewPanel.java +++ b/src/instrument/gui/BinaryTapViewPanel.java @@ -45,6 +45,8 @@ public class BinaryTapViewPanel extends TapViewPanel getModel().setSampleCount( (int)( getModel().getSampleCount() * tap.getSampleRateRatio() ) ); + + getModel().setDelay( tap.getDelay() ); } @Override diff --git a/src/instrument/gui/DecoderViewFrame.java b/src/instrument/gui/DecoderViewFrame.java index 103fddf2..10609409 100644 --- a/src/instrument/gui/DecoderViewFrame.java +++ b/src/instrument/gui/DecoderViewFrame.java @@ -29,10 +29,12 @@ import java.awt.event.ActionListener; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.util.HashMap; +import java.util.List; import javax.swing.JCheckBoxMenuItem; import javax.swing.JInternalFrame; import javax.swing.JMenu; +import javax.swing.JMenuItem; import javax.swing.JPopupMenu; import javax.swing.JSeparator; @@ -101,8 +103,12 @@ public class DecoderViewFrame extends JInternalFrame { JMenu tapMenu = new JMenu( "Taps" ); popup.add( tapMenu ); - + Instrumentable i = (Instrumentable)mDecoder; + + tapMenu.add( new AddAllTapsItem( i.getTaps() ) ); + + tapMenu.add( new JSeparator() ); for( Tap tap: i.getTaps() ) { @@ -157,13 +163,21 @@ public class DecoderViewFrame extends JInternalFrame break; } - add( panel, "span" ); - - validate(); - - mPanelMap.put( tap, panel ); + if( panel != null ) + { + add( panel, "span" ); + + validate(); + + mPanelMap.put( tap, panel ); - ((Instrumentable)mDecoder).addTap( tap ); + ((Instrumentable)mDecoder).addTap( tap ); + } + else + { + Log.info( "Tap panel is null, couldn't add for tap " + + tap.getName() + "[" + tap.getType().toString() + "]" ); + } } public void remove( Tap tap ) @@ -178,9 +192,34 @@ public class DecoderViewFrame extends JInternalFrame mPanelMap.remove( tap ); } + + public class AddAllTapsItem extends JMenuItem + { + private final List mTaps; + + public AddAllTapsItem( final List taps ) + { + super( "All Taps" ); + + mTaps = taps; + + addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + for( Tap tap: mTaps ) + { + DecoderViewFrame.this.add( tap ); + } + } + } ); + } + } public class TapSelectionItem extends JCheckBoxMenuItem { + private static final long serialVersionUID = 1L; private Tap mTap; public TapSelectionItem( Tap tap ) diff --git a/src/instrument/gui/FloatTapViewPanel.java b/src/instrument/gui/FloatTapViewPanel.java index d6b6b1cb..d2f48b48 100644 --- a/src/instrument/gui/FloatTapViewPanel.java +++ b/src/instrument/gui/FloatTapViewPanel.java @@ -28,8 +28,11 @@ import java.awt.geom.Point2D; import java.util.List; import java.util.Observable; +import log.Log; + public class FloatTapViewPanel extends TapViewPanel { + private static final long serialVersionUID = 1L; private Tap mTap; private List mSamples; private int mSampleCount; @@ -40,16 +43,20 @@ public class FloatTapViewPanel extends TapViewPanel mTap = tap; mTap.addListener( getModel() ); + + mSampleCount = (int)( 2000 * tap.getSampleRateRatio() ); + + getModel().setSampleCount( mSampleCount ); + getModel().setDelay( tap.getDelay() ); - getModel().setSampleCount( - (int)( getModel().getSampleCount() * tap.getSampleRateRatio() ) ); + Log.info( "Float Tap Panel [" + tap.getName() + "] count: " + + getModel().getSampleCount() + " delay:" + getModel().getDelay() ); } @Override public void update( Observable arg0, Object arg1 ) { mSamples = (List)getModel().getSamples(); - mSampleCount = getModel().getSampleCount(); repaint(); } @@ -68,7 +75,8 @@ public class FloatTapViewPanel extends TapViewPanel g2.drawLine( 0, (int)middle, width, (int)middle ); - if( mSamples != null && mSamples.size() > 0 ) + if( mSamples != null && + mSamples.size() > 0 ) { g2.setColor( getForeground() ); diff --git a/src/instrument/gui/SampleModel.java b/src/instrument/gui/SampleModel.java index 32dc0702..14109f59 100644 --- a/src/instrument/gui/SampleModel.java +++ b/src/instrument/gui/SampleModel.java @@ -24,9 +24,13 @@ import java.util.Collections; import java.util.List; import java.util.Observable; +import log.Log; +import dsp.fsk.SymbolEvent; + public class SampleModel extends Observable implements TapListener { private int mSampleCount = 2000; + private int mDelay = 0; private ArrayList mSamples = new ArrayList(); public SampleModel() @@ -56,15 +60,32 @@ public class SampleModel extends Observable implements TapListener changed(); } + + public void setDelay( int delay ) + { + mDelay = delay; + } + + public int getDelay() + { + return mDelay; + } @Override public void receive( T t ) { - mSamples.add( t ); - - while( mSamples.size() > mSampleCount ) + if( mDelay > 0 ) { - mSamples.remove( 0 ); + mDelay--; + } + else + { + mSamples.add( t ); + + while( mSamples.size() > mSampleCount ) + { + mSamples.remove( 0 ); + } } changed(); diff --git a/src/instrument/gui/SymbolEventTapViewPanel.java b/src/instrument/gui/SymbolEventTapViewPanel.java index 1663002d..a980b4a0 100644 --- a/src/instrument/gui/SymbolEventTapViewPanel.java +++ b/src/instrument/gui/SymbolEventTapViewPanel.java @@ -28,14 +28,16 @@ import java.awt.geom.Rectangle2D; import java.util.List; import java.util.Observable; +import log.Log; import dsp.fsk.SymbolEvent; public class SymbolEventTapViewPanel extends TapViewPanel { - private SymbolEventTap mTap; + private static final long serialVersionUID = 1L; + + private SymbolEventTap mTap; private List mSamples; private int mSampleCount; - private int mOffset = 0; public SymbolEventTapViewPanel( SymbolEventTap tap ) { @@ -44,15 +46,18 @@ public class SymbolEventTapViewPanel extends TapViewPanel mTap = tap; mTap.addListener( getModel() ); - getModel().setSampleCount( - (int)( getModel().getSampleCount() * tap.getSampleRateRatio() ) ); + mSampleCount = (int)( getModel().getSampleCount() * tap.getSampleRateRatio() ); + + getModel().setSampleCount( mSampleCount ); + + getModel().setDelay( tap.getDelay() ); } @Override public void update( Observable arg0, Object arg1 ) { mSamples = (List)getModel().getSamples(); - mSampleCount = getModel().getSampleCount(); + repaint(); } diff --git a/src/instrument/gui/WaveSourceFrame.java b/src/instrument/gui/WaveSourceFrame.java index 706d7c69..2fc22f3e 100644 --- a/src/instrument/gui/WaveSourceFrame.java +++ b/src/instrument/gui/WaveSourceFrame.java @@ -44,6 +44,7 @@ import decode.DecoderType; import decode.fleetsync2.Fleetsync2Decoder; import decode.mdc1200.MDCDecoder; import decode.mpt1327.MPT1327Decoder; +import decode.passport.PassportDecoder; public class WaveSourceFrame extends JInternalFrame implements PositionListener { @@ -138,6 +139,8 @@ public class WaveSourceFrame extends JInternalFrame implements PositionListener case MPT1327: decoder = new MPT1327Decoder( SampleType.FLOAT, null ); break; + case PASSPORT: + decoder = new PassportDecoder( SampleType.FLOAT, null ); } if( decoder != null ) diff --git a/src/instrument/tap/stream/SymbolEventTap.java b/src/instrument/tap/stream/SymbolEventTap.java index a5b05185..540c0c30 100644 --- a/src/instrument/tap/stream/SymbolEventTap.java +++ b/src/instrument/tap/stream/SymbolEventTap.java @@ -17,6 +17,7 @@ ******************************************************************************/ package instrument.tap.stream; +import log.Log; import instrument.tap.TapListener; import instrument.tap.TapType; import sample.Listener;