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;