mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-13 00:57:31 -05:00
P25 Work-in-progress check-in.
This commit is contained in:
@@ -1,117 +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 <http://www.gnu.org/licenses/>
|
||||
******************************************************************************/
|
||||
package dsp;
|
||||
|
||||
import sample.Listener;
|
||||
|
||||
public class BooleanZeroCrossingDetector implements Listener<Boolean>
|
||||
{
|
||||
private int mBaudLength;
|
||||
private int mCounter = 0;
|
||||
private int mThreshold;
|
||||
private Listener<Boolean> mListener;
|
||||
private Cycle mCycle = Cycle.POSITIVE;
|
||||
|
||||
/**
|
||||
* Implements a zero crossing detector for use with squared, filtered
|
||||
* binary values. Makes a baud decision at 100% of the baud
|
||||
* length, or at 80% of the baud length during a zero crossing. Counter
|
||||
* is reset at each zero crossing.
|
||||
*
|
||||
* @param baudLength - estimated number of boolean samples per baud
|
||||
*/
|
||||
public BooleanZeroCrossingDetector( int baudLength )
|
||||
{
|
||||
mBaudLength = baudLength;
|
||||
mThreshold = (int)( baudLength * .80 );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive( Boolean sample )
|
||||
{
|
||||
mCounter++;
|
||||
|
||||
switch( mCycle )
|
||||
{
|
||||
case POSITIVE:
|
||||
if( sample )
|
||||
{
|
||||
if( mCounter >= mBaudLength )
|
||||
{
|
||||
send( true );
|
||||
mCounter -= mBaudLength;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if( mCounter >= mThreshold )
|
||||
{
|
||||
send( true );
|
||||
mCounter -= mBaudLength;
|
||||
}
|
||||
|
||||
mCycle = Cycle.NEGATIVE;
|
||||
}
|
||||
break;
|
||||
|
||||
case NEGATIVE:
|
||||
if( sample )
|
||||
{
|
||||
if( mCounter >= mThreshold )
|
||||
{
|
||||
send( false );
|
||||
|
||||
mCounter -= mBaudLength;
|
||||
}
|
||||
|
||||
mCycle = Cycle.POSITIVE;
|
||||
}
|
||||
else
|
||||
{
|
||||
if( mCounter >= mBaudLength )
|
||||
{
|
||||
send( false );
|
||||
|
||||
mCounter -= mBaudLength;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void send( boolean bit )
|
||||
{
|
||||
if( mListener != null )
|
||||
{
|
||||
mListener.receive( bit );
|
||||
}
|
||||
}
|
||||
|
||||
public void setListener( Listener<Boolean> listener )
|
||||
{
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
public void removeListener( Listener<Boolean> listener )
|
||||
{
|
||||
mListener = null;
|
||||
}
|
||||
|
||||
public enum Cycle { POSITIVE, NEGATIVE };
|
||||
}
|
||||
@@ -1,69 +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 <http://www.gnu.org/licenses/>
|
||||
******************************************************************************/
|
||||
package dsp;
|
||||
|
||||
import sample.Listener;
|
||||
import buffer.FloatCircularAveragingBuffer;
|
||||
|
||||
public class FloatToBinarySlicer implements Listener<Float>
|
||||
|
||||
{
|
||||
private FloatCircularAveragingBuffer mAveragingBuffer;
|
||||
private Listener<Boolean> mListener;
|
||||
|
||||
/**
|
||||
* Provides binary slicing of the stream of floats, using a circular buffer
|
||||
* to calculate a running average, then basing the slice decision based
|
||||
* on the oldest sample leaving the circular buffer, compared to the running
|
||||
* average.
|
||||
*
|
||||
* Designed to operate with a DC bias inherent in the sample stream.
|
||||
*
|
||||
* Tracks a long average and a short average to compensate for drifting
|
||||
* transmitters.
|
||||
*
|
||||
* The long buffer compensates for long strings of ones or
|
||||
* zeros. Set the long buffer size to a few baud periods longer than the
|
||||
* longest anticipated string of like symbols.
|
||||
*
|
||||
*/
|
||||
public FloatToBinarySlicer( int bufferSize, int sampleCountForAveraging )
|
||||
{
|
||||
mAveragingBuffer = new FloatCircularAveragingBuffer( bufferSize,
|
||||
sampleCountForAveraging );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive( Float newestSample )
|
||||
{
|
||||
float oldestSample = mAveragingBuffer.get( newestSample );
|
||||
|
||||
//Dispatch the decision
|
||||
mListener.receive( oldestSample > mAveragingBuffer.getAverage() );
|
||||
}
|
||||
|
||||
public void setListener( Listener<Boolean> listener )
|
||||
{
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
public void clearListener()
|
||||
{
|
||||
mListener = null;
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,6 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import sample.real.RealSampleListener;
|
||||
import dsp.filter.C4FMSymbolFilter;
|
||||
|
||||
public class C4FMDecoder implements RealSampleListener, Instrumentable
|
||||
{
|
||||
|
||||
15
src/dsp/fsk/C4FMFrameListener.java
Normal file
15
src/dsp/fsk/C4FMFrameListener.java
Normal file
@@ -0,0 +1,15 @@
|
||||
package dsp.fsk;
|
||||
|
||||
import bits.BitSetBuffer;
|
||||
|
||||
public interface C4FMFrameListener
|
||||
{
|
||||
/**
|
||||
* Listener interface to receive the output of the C4FMMessageFramer.
|
||||
*
|
||||
* @param buffer - framed message without the sync pattern
|
||||
* @param inverted - flag indicating if the message was received inverted
|
||||
*/
|
||||
public void receive( BitSetBuffer buffer, boolean inverted );
|
||||
|
||||
}
|
||||
69
src/dsp/fsk/C4FMSlicer.java
Normal file
69
src/dsp/fsk/C4FMSlicer.java
Normal file
@@ -0,0 +1,69 @@
|
||||
package dsp.fsk;
|
||||
|
||||
import sample.Broadcaster;
|
||||
import sample.Listener;
|
||||
import sample.real.RealSampleListener;
|
||||
|
||||
/**
|
||||
* C4FM slicer to convert the output stream of the C4FMSymbolFilter into a
|
||||
* stream of C4FM symbols.
|
||||
*
|
||||
* Supports registering listener(s) to receive normal and/or inverted symbol
|
||||
* output streams.
|
||||
*/
|
||||
public class C4FMSlicer implements RealSampleListener
|
||||
{
|
||||
private static final float THRESHOLD = 2.0f;
|
||||
|
||||
private Broadcaster<C4FMSymbol> mBroadcaster = new Broadcaster<C4FMSymbol>();
|
||||
|
||||
/**
|
||||
* Primary method for receiving output from the C4FMSymbolFilter. Slices
|
||||
* (converts) the filtered sample value into a C4FMSymbol decision.
|
||||
*/
|
||||
@Override
|
||||
public void receive( float sample )
|
||||
{
|
||||
if( sample > THRESHOLD )
|
||||
{
|
||||
dispatch( C4FMSymbol.SYMBOL_PLUS_3 );
|
||||
}
|
||||
else if( sample > 0 )
|
||||
{
|
||||
dispatch( C4FMSymbol.SYMBOL_PLUS_1 );
|
||||
}
|
||||
else if( sample > -THRESHOLD )
|
||||
{
|
||||
dispatch( C4FMSymbol.SYMBOL_MINUS_1 );
|
||||
}
|
||||
else
|
||||
{
|
||||
dispatch( C4FMSymbol.SYMBOL_MINUS_3 );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatches the symbol decision to any registered listeners
|
||||
*/
|
||||
private void dispatch( C4FMSymbol symbol )
|
||||
{
|
||||
mBroadcaster.receive( symbol );
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the listener to receive the normal (non-inverted) C4FM symbol
|
||||
* stream.
|
||||
*/
|
||||
public void addListener( Listener<C4FMSymbol> listener )
|
||||
{
|
||||
mBroadcaster.addListener( listener );
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the listener
|
||||
*/
|
||||
public void removeListener( Listener<C4FMSymbol> listener )
|
||||
{
|
||||
mBroadcaster.removeListener( listener );
|
||||
}
|
||||
}
|
||||
44
src/dsp/fsk/C4FMSymbol.java
Normal file
44
src/dsp/fsk/C4FMSymbol.java
Normal file
@@ -0,0 +1,44 @@
|
||||
package dsp.fsk;
|
||||
|
||||
public enum C4FMSymbol
|
||||
{
|
||||
SYMBOL_PLUS_3( false, true ),
|
||||
SYMBOL_PLUS_1( false, false ),
|
||||
SYMBOL_MINUS_1( true, false ),
|
||||
SYMBOL_MINUS_3( true, true );
|
||||
|
||||
private boolean mBit1;
|
||||
private boolean mBit2;
|
||||
|
||||
private C4FMSymbol( boolean bit1, boolean bit2 )
|
||||
{
|
||||
mBit1 = bit1;
|
||||
mBit2 = bit2;
|
||||
}
|
||||
|
||||
public boolean getBit1()
|
||||
{
|
||||
return mBit1;
|
||||
}
|
||||
|
||||
public boolean getBit2()
|
||||
{
|
||||
return mBit2;
|
||||
}
|
||||
|
||||
public static C4FMSymbol inverted( C4FMSymbol symbol )
|
||||
{
|
||||
switch( symbol )
|
||||
{
|
||||
case SYMBOL_MINUS_1:
|
||||
return SYMBOL_MINUS_3;
|
||||
case SYMBOL_MINUS_3:
|
||||
return SYMBOL_PLUS_3;
|
||||
case SYMBOL_PLUS_1:
|
||||
return SYMBOL_MINUS_1;
|
||||
case SYMBOL_PLUS_3:
|
||||
default:
|
||||
return SYMBOL_PLUS_1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package dsp.filter;
|
||||
package dsp.fsk;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -147,6 +147,7 @@ public class C4FMSymbolFilter implements RealSampleListener,
|
||||
|
||||
private static final int NUMBER_FILTER_TAPS = 8;
|
||||
private static final int NUMBER_FILTER_STEPS = 128;
|
||||
|
||||
private static final int SAMPLE_RATE = 48000;
|
||||
private static final int SYMBOL_RATE = 4800;
|
||||
|
||||
@@ -171,6 +172,7 @@ public class C4FMSymbolFilter implements RealSampleListener,
|
||||
private float mSymbolSpread = 2.0f;
|
||||
private float mSymbolClock = 0.0f;
|
||||
private float mSymbolTime = (float)SYMBOL_RATE / (float)SAMPLE_RATE;
|
||||
|
||||
private float mFineFrequencyCorrection = 0.0f;
|
||||
private float mCoarseFrequencyCorrection = 0.0f;
|
||||
|
||||
@@ -178,8 +180,20 @@ public class C4FMSymbolFilter implements RealSampleListener,
|
||||
private int mHistoryLast = 0;
|
||||
|
||||
private RealSampleListener mListener;
|
||||
|
||||
|
||||
/**
|
||||
* C4FM Symbol Filter
|
||||
*
|
||||
* Sample Gain values - the gain of incoming sample values will critically
|
||||
* impact the performance of this filter. Gain should be adjusted to
|
||||
* optimize the value of mSymbolSpread toward a value of 2.0. If the value
|
||||
* is less than 2.0, then increase gain. If the value is over 2.0, then
|
||||
* decrease gain.
|
||||
*
|
||||
* During testing, the incoming samples were within the range of -0.5 <> 0.5
|
||||
* and applying a gain of 11.0 to those samples achieved the best symbol
|
||||
* spread, centered about 2.0 with +/- 0.5% deviations on a strong signal.
|
||||
*/
|
||||
public C4FMSymbolFilter()
|
||||
{
|
||||
}
|
||||
@@ -189,7 +203,8 @@ public class C4FMSymbolFilter implements RealSampleListener,
|
||||
{
|
||||
mSymbolClock += mSymbolTime;
|
||||
|
||||
mHistory[ mHistoryLast++ ] = sample;
|
||||
// mHistory[ mHistoryLast++ ] = sample;
|
||||
mHistory[ mHistoryLast++ ] = sample * 10.5f;
|
||||
mHistoryLast %= NUMBER_FILTER_TAPS;
|
||||
|
||||
if( mSymbolClock > 1.0f )
|
||||
@@ -199,14 +214,13 @@ public class C4FMSymbolFilter implements RealSampleListener,
|
||||
int imu = (int)Math.floor( 0.5 +
|
||||
( (float)NUMBER_FILTER_STEPS * ( mSymbolClock / mSymbolTime ) ) );
|
||||
|
||||
int imu_p1 = imu + 1;
|
||||
|
||||
if( imu >= NUMBER_FILTER_STEPS )
|
||||
{
|
||||
imu = NUMBER_FILTER_STEPS - 1;
|
||||
imu_p1 = NUMBER_FILTER_STEPS;
|
||||
}
|
||||
|
||||
int imu_p1 = imu + 1;
|
||||
|
||||
int j = mHistoryLast;
|
||||
|
||||
double interp = 0.0;
|
||||
@@ -220,7 +234,7 @@ public class C4FMSymbolFilter implements RealSampleListener,
|
||||
j = ( j + 1 ) % NUMBER_FILTER_TAPS;
|
||||
}
|
||||
|
||||
/* Ouput symbol will be interpolated value corrected for symbol
|
||||
/* Output symbol will be interpolated value corrected for symbol
|
||||
* spread and frequency offset */
|
||||
interp -= mFineFrequencyCorrection;
|
||||
interp_p1 -= mFineFrequencyCorrection;
|
||||
@@ -230,7 +244,7 @@ public class C4FMSymbolFilter implements RealSampleListener,
|
||||
|
||||
/* Detect received symbol error: basically use a hard decision and
|
||||
* subtract off expected position nominal symbol level which will be
|
||||
* +/= 0.5 * symbol spread and +/- 1.5 symbol spread. Rembmer that
|
||||
* +/- 0.5 * symbol spread and +/- 1.5 symbol spread. Remember that
|
||||
* nominal symbol spread will be 2.0 */
|
||||
double symbolError;
|
||||
|
||||
@@ -269,8 +283,6 @@ public class C4FMSymbolFilter implements RealSampleListener,
|
||||
mSymbolClock -= symbolError * K_SYMBOL_TIMING;
|
||||
}
|
||||
|
||||
mLog.debug( "Symbol Clock Adjusted: " + mSymbolClock );
|
||||
|
||||
mSymbolSpread = Math.max( mSymbolSpread, SYMBOL_SPREAD_MIN );
|
||||
mSymbolSpread = Math.min( mSymbolSpread, SYMBOL_SPREAD_MAX );
|
||||
|
||||
@@ -282,11 +294,11 @@ public class C4FMSymbolFilter implements RealSampleListener,
|
||||
/* send frequency correction */
|
||||
if( Math.abs( mCoarseFrequencyCorrection ) > COARSE_FREQUENCY_DEADBAND )
|
||||
{
|
||||
mLog.debug( "Frequency Correction [" +
|
||||
( mCoarseFrequencyCorrection > 0 ? "-" : "+" ) +
|
||||
mCoarseFrequencyCorrection + "]" );
|
||||
}
|
||||
|
||||
mLog.error( "C4FM Filter - output [ " + output + " ] frequency correction: " +
|
||||
mCoarseFrequencyCorrection );
|
||||
|
||||
/* dispatch the interpolated value to the listener */
|
||||
if( mListener != null )
|
||||
{
|
||||
@@ -28,14 +28,13 @@ import java.util.List;
|
||||
|
||||
import sample.Listener;
|
||||
import sample.real.RealSampleListener;
|
||||
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;
|
||||
import dsp.fsk.Slicer.Output;
|
||||
|
||||
public class LTRFSKDecoder implements RealSampleListener, Instrumentable
|
||||
{
|
||||
|
||||
374
src/dsp/fsk/P25MessageFramer.java
Normal file
374
src/dsp/fsk/P25MessageFramer.java
Normal file
@@ -0,0 +1,374 @@
|
||||
package dsp.fsk;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.BitSet;
|
||||
|
||||
import message.Message;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import sample.Listener;
|
||||
import bits.BitSetBuffer;
|
||||
import bits.BitSetFullException;
|
||||
import bits.SyncPatternMatcher;
|
||||
import decode.p25.P25Interleave;
|
||||
import decode.p25.TrellisHalfRate;
|
||||
import decode.p25.message.P25Message;
|
||||
import decode.p25.message.PDUMessage;
|
||||
import decode.p25.message.TSBKMessage;
|
||||
import decode.p25.message.TSBKMessageFactory;
|
||||
import decode.p25.reference.DataUnitID;
|
||||
|
||||
public class P25MessageFramer implements Listener<C4FMSymbol>
|
||||
{
|
||||
private final static Logger mLog =
|
||||
LoggerFactory.getLogger( P25MessageFramer.class );
|
||||
|
||||
private static final int[] STATUS_BITS =
|
||||
new int[] { 22,92,162,232,302,372,442,512,582,652,722,792,862,932 };
|
||||
|
||||
private ArrayList<P25MessageAssembler> mAssemblers =
|
||||
new ArrayList<P25MessageAssembler>();
|
||||
|
||||
public static final int TSBK_BEGIN = 64;
|
||||
public static final int TSBK_END = 260;
|
||||
public static final int TSBK_DECODED_END = 162;
|
||||
|
||||
private Listener<Message> mListener;
|
||||
private SyncPatternMatcher mMatcher;
|
||||
private boolean mInverted = false;
|
||||
private TrellisHalfRate mHalfRate = new TrellisHalfRate();
|
||||
|
||||
/**
|
||||
* Constructs a C4FM message framer to receive a stream of C4FM symbols and
|
||||
* detect the sync pattern then capture the following stream of symbols up
|
||||
* to the message length, and then broadcast that bit buffer to the registered
|
||||
* listener.
|
||||
*
|
||||
* @param sync - sync pattern (maximum of 63 bits)
|
||||
* @param messageLength - in bits
|
||||
* @param inverted - optional flag to indicate the symbol stream should be
|
||||
* uninverted prior to processing
|
||||
*/
|
||||
public P25MessageFramer( long sync, int messageLength, boolean inverted )
|
||||
{
|
||||
mMatcher = new SyncPatternMatcher( sync );
|
||||
mInverted = inverted;
|
||||
|
||||
/**
|
||||
* We use two message assemblers to catch any sync detections, so that
|
||||
* if we have a false trigger then we still have a chance to catch the
|
||||
* valid message. If we have two false triggers before the arrival of
|
||||
* the actual message sync, then the third or subsequent real or false
|
||||
* sync pattern will produce a debug message indicating no assemblers
|
||||
* are available..
|
||||
*/
|
||||
mAssemblers.add( new P25MessageAssembler() );
|
||||
mAssemblers.add( new P25MessageAssembler() );
|
||||
}
|
||||
|
||||
private void dispatch( Message message )
|
||||
{
|
||||
if( mListener != null )
|
||||
{
|
||||
mListener.receive( message );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive( C4FMSymbol symbol )
|
||||
{
|
||||
C4FMSymbol correctedSymbol = symbol;
|
||||
|
||||
mMatcher.receive( correctedSymbol.getBit1() );
|
||||
mMatcher.receive( correctedSymbol.getBit2() );
|
||||
|
||||
for( P25MessageAssembler assembler: mAssemblers )
|
||||
{
|
||||
if( assembler.isActive() )
|
||||
{
|
||||
assembler.receive( correctedSymbol );
|
||||
|
||||
if( assembler.complete() )
|
||||
{
|
||||
assembler.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Check for sync match and activate a message assembler, if we can
|
||||
* find an inactive assembler. Otherwise, ignore and log the issue */
|
||||
if( mMatcher.matches() )
|
||||
{
|
||||
boolean found = false;
|
||||
|
||||
for( P25MessageAssembler assembler: mAssemblers )
|
||||
{
|
||||
if( !assembler.isActive() )
|
||||
{
|
||||
assembler.setActive( true );
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( !found )
|
||||
{
|
||||
mLog.debug( "no inactive C4FM message assemblers available" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setListener( Listener<Message> listener )
|
||||
{
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
public void removeListener( Listener<Message> listener )
|
||||
{
|
||||
mListener = null;
|
||||
}
|
||||
|
||||
private class P25MessageAssembler
|
||||
{
|
||||
private int mStatusIndicatorPointer = 0;
|
||||
private BitSetBuffer mMessage;
|
||||
private int mMessageLength;
|
||||
private boolean mComplete = false;
|
||||
private boolean mActive = false;
|
||||
private DataUnitID mDUID = DataUnitID.NID;
|
||||
|
||||
public P25MessageAssembler()
|
||||
{
|
||||
mMessageLength = mDUID.getMessageLength();
|
||||
mMessage = new BitSetBuffer( mMessageLength );
|
||||
reset();
|
||||
}
|
||||
|
||||
public void receive( C4FMSymbol symbol )
|
||||
{
|
||||
if( mActive )
|
||||
{
|
||||
/* Throw away status bits that are injected every 70 bits */
|
||||
if( mMessage.pointer() == STATUS_BITS[ mStatusIndicatorPointer ] )
|
||||
{
|
||||
mStatusIndicatorPointer++;
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
mMessage.add( symbol.getBit1() );
|
||||
mMessage.add( symbol.getBit2() );
|
||||
}
|
||||
catch( BitSetFullException e )
|
||||
{
|
||||
mComplete = true;
|
||||
}
|
||||
|
||||
/* Check the message for complete */
|
||||
if( mMessage.isFull() )
|
||||
{
|
||||
checkComplete();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void reset()
|
||||
{
|
||||
mDUID = DataUnitID.NID;
|
||||
mMessage.setSize( mDUID.getMessageLength() );
|
||||
mMessage.clear();
|
||||
mStatusIndicatorPointer = 0;
|
||||
mComplete = false;
|
||||
mActive = false;
|
||||
}
|
||||
|
||||
public void setActive( boolean active )
|
||||
{
|
||||
mActive = active;
|
||||
}
|
||||
|
||||
private void setDUID( DataUnitID id )
|
||||
{
|
||||
mDUID = id;
|
||||
mMessageLength = id.getMessageLength();
|
||||
mMessage.setSize( mMessageLength );
|
||||
}
|
||||
|
||||
private void checkComplete()
|
||||
{
|
||||
switch( mDUID )
|
||||
{
|
||||
case NID:
|
||||
int value = mMessage.getInt( P25Message.DUID );
|
||||
|
||||
DataUnitID duid = DataUnitID.fromValue( value );
|
||||
|
||||
if( duid != DataUnitID.UNKN )
|
||||
{
|
||||
setDUID( duid );
|
||||
}
|
||||
else
|
||||
{
|
||||
mComplete = true;
|
||||
}
|
||||
break;
|
||||
case HDU:
|
||||
mComplete = true;
|
||||
dispatch( new P25Message( mMessage.copy(), mDUID ) );
|
||||
break;
|
||||
case LDU1:
|
||||
mComplete = true;
|
||||
dispatch( new P25Message( mMessage.copy(), mDUID ) );
|
||||
break;
|
||||
case LDU2:
|
||||
mComplete = true;
|
||||
dispatch( new P25Message( mMessage.copy(), mDUID ) );
|
||||
break;
|
||||
case PDU1:
|
||||
int blocks = mMessage.getInt(
|
||||
PDUMessage.PDU_HEADER_BLOCKS_TO_FOLLOW );
|
||||
int padBlocks = mMessage.getInt( PDUMessage.PDU_HEADER_PAD_BLOCKS );
|
||||
|
||||
int blockCount = blocks + padBlocks;
|
||||
|
||||
if( blockCount == 24 || blockCount == 32 )
|
||||
{
|
||||
setDUID( DataUnitID.PDU2 );
|
||||
}
|
||||
else if( blockCount == 36 || blockCount == 48 )
|
||||
{
|
||||
setDUID( DataUnitID.PDU3 );
|
||||
}
|
||||
else
|
||||
{
|
||||
dispatch( new PDUMessage( mMessage.copy(), mDUID ) );
|
||||
mComplete = true;
|
||||
}
|
||||
break;
|
||||
case PDU2:
|
||||
case PDU3:
|
||||
mComplete = true;
|
||||
dispatch( new P25Message( mMessage.copy(), mDUID ) );
|
||||
break;
|
||||
case TDU:
|
||||
mComplete = true;
|
||||
dispatch( new P25Message( mMessage.copy(), mDUID ) );
|
||||
break;
|
||||
case TDULC:
|
||||
mComplete = true;
|
||||
dispatch( new P25Message( mMessage.copy(), mDUID ) );
|
||||
break;
|
||||
case TSBK1:
|
||||
/* Remove interleaving */
|
||||
P25Interleave.deinterleave( mMessage, TSBK_BEGIN, TSBK_END );
|
||||
|
||||
/* Remove trellis encoding */
|
||||
mHalfRate.decode( mMessage, TSBK_BEGIN, TSBK_END );
|
||||
|
||||
/* Construct the message */
|
||||
int tsbkSystem1 = mMessage.getInt( P25Message.NAC );
|
||||
|
||||
BitSetBuffer tsbkBuffer1 = new BitSetBuffer( mMessage.get(
|
||||
TSBK_BEGIN, TSBK_DECODED_END ), 96 );
|
||||
|
||||
TSBKMessage tsbkMessage1 =
|
||||
TSBKMessageFactory.getMessage( tsbkSystem1, tsbkBuffer1 );
|
||||
|
||||
if( tsbkMessage1.isLastBlock() )
|
||||
{
|
||||
mComplete = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
setDUID( DataUnitID.TSBK2 );
|
||||
mMessage.setPointer( TSBK_BEGIN );
|
||||
}
|
||||
|
||||
dispatch( tsbkMessage1 );
|
||||
break;
|
||||
case TSBK2:
|
||||
/* Remove interleaving */
|
||||
P25Interleave.deinterleave( mMessage, TSBK_BEGIN, TSBK_END );
|
||||
|
||||
/* Remove trellis encoding */
|
||||
mHalfRate.decode( mMessage, TSBK_BEGIN, TSBK_END );
|
||||
|
||||
/* Construct the message */
|
||||
int tsbkSystem2 = mMessage.getInt( P25Message.NAC );
|
||||
|
||||
BitSetBuffer tsbkBuffer2 = new BitSetBuffer( mMessage.get(
|
||||
TSBK_BEGIN, TSBK_DECODED_END ), 98 );
|
||||
|
||||
TSBKMessage tsbkMessage2 =
|
||||
TSBKMessageFactory.getMessage( tsbkSystem2, tsbkBuffer2 );
|
||||
|
||||
if( tsbkMessage2.isLastBlock() )
|
||||
{
|
||||
mComplete = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
setDUID( DataUnitID.TSBK3 );
|
||||
mMessage.setPointer( TSBK_BEGIN );
|
||||
}
|
||||
|
||||
dispatch( tsbkMessage2 );
|
||||
|
||||
break;
|
||||
case TSBK3:
|
||||
/* Remove interleaving */
|
||||
P25Interleave.deinterleave( mMessage, TSBK_BEGIN, TSBK_END );
|
||||
|
||||
/* Remove trellis encoding */
|
||||
mHalfRate.decode( mMessage, TSBK_BEGIN, TSBK_END );
|
||||
|
||||
/* Construct the message */
|
||||
int tsbkSystem3 = mMessage.getInt( P25Message.NAC );
|
||||
|
||||
BitSetBuffer tsbkBuffer3 = new BitSetBuffer( mMessage.get(
|
||||
TSBK_BEGIN, TSBK_DECODED_END ), 96 );
|
||||
|
||||
TSBKMessage tsbkMessage3 =
|
||||
TSBKMessageFactory.getMessage( tsbkSystem3, tsbkBuffer3 );
|
||||
|
||||
mComplete = true;
|
||||
|
||||
dispatch( tsbkMessage3 );
|
||||
|
||||
break;
|
||||
case UNKN:
|
||||
mComplete = true;
|
||||
dispatch( new P25Message( mMessage.copy(), mDUID ) );
|
||||
break;
|
||||
default:
|
||||
mComplete = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void dispose()
|
||||
{
|
||||
mMessage = null;
|
||||
mHalfRate.dispose();
|
||||
}
|
||||
|
||||
/**
|
||||
* Flag to indicate when this assembler has received all of the bits it
|
||||
* is looking for (ie message length), and should then be removed from
|
||||
* receiving any more bits
|
||||
*/
|
||||
public boolean complete()
|
||||
{
|
||||
return mComplete;
|
||||
}
|
||||
|
||||
public boolean isActive()
|
||||
{
|
||||
return mActive;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,10 @@
|
||||
package dsp;
|
||||
package dsp.fsk;
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
Reference in New Issue
Block a user