mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-12 08:37:25 -05:00
P25 Work-in-progress check-in.
This commit is contained in:
@@ -87,6 +87,13 @@ public class BitSetBuffer extends BitSet
|
||||
this.or( toCopyFrom );
|
||||
this.mPointer = toCopyFrom.pointer();
|
||||
}
|
||||
|
||||
public BitSetBuffer( BitSet bitset, int size )
|
||||
{
|
||||
this( size );
|
||||
this.or( bitset );
|
||||
this.mPointer = size - 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Current pointer index
|
||||
@@ -95,6 +102,11 @@ public class BitSetBuffer extends BitSet
|
||||
{
|
||||
return mPointer;
|
||||
}
|
||||
|
||||
public void setPointer( int index )
|
||||
{
|
||||
mPointer = index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Static method to construct a new BitSetBuffer, preloaded with the bits
|
||||
@@ -153,6 +165,11 @@ public class BitSetBuffer extends BitSet
|
||||
{
|
||||
return mSize;
|
||||
}
|
||||
|
||||
public void setSize( int size )
|
||||
{
|
||||
mSize = size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears (sets to false or 0) the bits in this bitset and resets the
|
||||
|
||||
@@ -48,6 +48,28 @@ public class SyncPatternMatcher
|
||||
}
|
||||
}
|
||||
|
||||
public SyncPatternMatcher( long sync )
|
||||
{
|
||||
mSync = sync;
|
||||
mMask = getMask( sync );
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an all ones mask corresponding to the sync pattern
|
||||
*/
|
||||
private static long getMask( long sync )
|
||||
{
|
||||
long mask = sync;
|
||||
|
||||
for( int x = 1; x < 64; x *= 2 )
|
||||
{
|
||||
mask |= ( mask >> x );
|
||||
}
|
||||
|
||||
return mask;
|
||||
}
|
||||
|
||||
|
||||
public void receive( boolean bit )
|
||||
{
|
||||
//Left shift the previous value
|
||||
|
||||
13
src/crc/BCH.java
Normal file
13
src/crc/BCH.java
Normal file
@@ -0,0 +1,13 @@
|
||||
package crc;
|
||||
|
||||
import java.util.BitSet;
|
||||
|
||||
public class BCH
|
||||
{
|
||||
public BCH( int n, int k, int t, boolean[] polynomial, boolean sys )
|
||||
{
|
||||
int[] exponents = new int[ n - k + 1 ];
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
19
src/decode/p25/FrameSync.java
Normal file
19
src/decode/p25/FrameSync.java
Normal file
@@ -0,0 +1,19 @@
|
||||
package decode.p25;
|
||||
|
||||
public enum FrameSync
|
||||
{
|
||||
P25_PHASE1( 0x5575F5FF77FFl ),
|
||||
P25_PHASE1_INVERTED( 0xFFDF5F55DD55l );
|
||||
|
||||
private long mSync;
|
||||
|
||||
private FrameSync( long sync )
|
||||
{
|
||||
mSync = sync;
|
||||
}
|
||||
|
||||
public long getSync()
|
||||
{
|
||||
return mSync;
|
||||
}
|
||||
}
|
||||
@@ -20,8 +20,12 @@ package decode.p25;
|
||||
import instrument.Instrumentable;
|
||||
import instrument.tap.Tap;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import sample.real.RealSampleListener;
|
||||
import source.Source.SampleType;
|
||||
import alias.AliasList;
|
||||
@@ -29,19 +33,26 @@ import decode.Decoder;
|
||||
import decode.DecoderType;
|
||||
import dsp.filter.ComplexFIRFilter;
|
||||
import dsp.filter.FilterFactory;
|
||||
import dsp.filter.FloatFIRFilter;
|
||||
import dsp.filter.Window.WindowType;
|
||||
import dsp.fsk.C4FMDecoder;
|
||||
import dsp.fsk.P25MessageFramer;
|
||||
import dsp.fsk.C4FMSlicer;
|
||||
import dsp.fsk.C4FMSymbolFilter;
|
||||
import dsp.nbfm.FMDiscriminator;
|
||||
|
||||
public class P25Decoder extends Decoder implements Instrumentable
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger( P25Decoder.class );
|
||||
|
||||
private ComplexFIRFilter mBasebandFilter = new ComplexFIRFilter(
|
||||
FilterFactory.getLowPass( 48000, 6250, 73, WindowType.HAMMING ), 1.0 );
|
||||
private FMDiscriminator mFMDiscriminator = new FMDiscriminator( 1 );
|
||||
// private FloatFIRFilter mAudioFilter = new FloatFIRFilter( FilterFactory
|
||||
// .getLowPass( 48000, 6000, 73, WindowType.HAMMING ), 1.0 );
|
||||
private C4FMDecoder mC4FMDecoder = new C4FMDecoder();
|
||||
FilterFactory.getLowPass( 48000, 5000, 31, WindowType.HAMMING ), 1.0 );
|
||||
|
||||
private FMDiscriminator mDemodulator = new FMDiscriminator( 1 );
|
||||
private C4FMSymbolFilter mSymbolFilter = new C4FMSymbolFilter();
|
||||
private C4FMSlicer mSlicer = new C4FMSlicer();
|
||||
private P25MessageFramer mNormalFramer;
|
||||
private P25MessageFramer mInvertedFramer;
|
||||
private P25MessageProcessor mMessageProcessor;
|
||||
|
||||
private AliasList mAliasList;
|
||||
|
||||
public P25Decoder( SampleType sampleType, AliasList aliasList )
|
||||
@@ -65,13 +76,25 @@ public class P25Decoder extends Decoder implements Instrumentable
|
||||
* to process
|
||||
*/
|
||||
this.addComplexListener( mBasebandFilter );
|
||||
mBasebandFilter.setListener( mFMDiscriminator );
|
||||
mFMDiscriminator.setListener( getRealReceiver() );
|
||||
// mFMDiscriminator.setListener( mAudioFilter );
|
||||
// mAudioFilter.setListener( getRealReceiver() );
|
||||
mBasebandFilter.setListener( mDemodulator );
|
||||
mDemodulator.setListener( getRealReceiver() );
|
||||
}
|
||||
|
||||
addRealSampleListener( mC4FMDecoder );
|
||||
addRealSampleListener( mSymbolFilter );
|
||||
|
||||
mSymbolFilter.setListener( mSlicer );
|
||||
|
||||
mMessageProcessor = new P25MessageProcessor( mAliasList );
|
||||
|
||||
mNormalFramer = new P25MessageFramer(
|
||||
FrameSync.P25_PHASE1.getSync(), 64, false );
|
||||
mSlicer.addListener( mNormalFramer );
|
||||
mNormalFramer.setListener( mMessageProcessor );
|
||||
|
||||
mInvertedFramer = new P25MessageFramer(
|
||||
FrameSync.P25_PHASE1_INVERTED.getSync(), 64, true );
|
||||
mSlicer.addListener( mInvertedFramer );
|
||||
mInvertedFramer.setListener( mMessageProcessor );
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -83,24 +106,23 @@ public class P25Decoder extends Decoder implements Instrumentable
|
||||
@Override
|
||||
public List<Tap> getTaps()
|
||||
{
|
||||
return mC4FMDecoder.getTaps();
|
||||
return new ArrayList<Tap>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addTap( Tap tap )
|
||||
{
|
||||
mC4FMDecoder.addTap( tap );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeTap( Tap tap )
|
||||
{
|
||||
mC4FMDecoder.removeTap( tap );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addUnfilteredRealSampleListener( RealSampleListener listener )
|
||||
{
|
||||
throw new IllegalArgumentException( "unfiltered real sample provider not implemented in P25 Decoder" );
|
||||
throw new IllegalArgumentException( "unfiltered real sample provider "
|
||||
+ "not implemented in P25 Decoder" );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,4 +94,30 @@ public class P25Interleave
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
public static void main( String[] args )
|
||||
{
|
||||
int start = 0;
|
||||
int end = 196;
|
||||
|
||||
BitSetBuffer b = new BitSetBuffer( end );
|
||||
|
||||
for( int x = 0; x < end; x += 3 )
|
||||
{
|
||||
if( x > 20 )
|
||||
{
|
||||
b.set( x );
|
||||
}
|
||||
}
|
||||
|
||||
mLog.debug( "Origi:" + b.toString() );
|
||||
|
||||
P25Interleave.interleave( b, start, end );
|
||||
|
||||
mLog.debug( "Inter:" + b.toString() );
|
||||
|
||||
P25Interleave.deinterleave( b, start, end );
|
||||
|
||||
mLog.debug( "Donee:" + b.toString() );
|
||||
}
|
||||
}
|
||||
|
||||
28
src/decode/p25/P25MessageProcessor.java
Normal file
28
src/decode/p25/P25MessageProcessor.java
Normal file
@@ -0,0 +1,28 @@
|
||||
package decode.p25;
|
||||
|
||||
import message.Message;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import sample.Listener;
|
||||
import alias.AliasList;
|
||||
|
||||
public class P25MessageProcessor implements Listener<Message>
|
||||
{
|
||||
private final static Logger mLog =
|
||||
LoggerFactory.getLogger( P25MessageProcessor.class );
|
||||
|
||||
private AliasList mAliasList;
|
||||
|
||||
public P25MessageProcessor( AliasList aliasList )
|
||||
{
|
||||
mAliasList = aliasList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive( Message message )
|
||||
{
|
||||
mLog.debug( "P25: " + message.toString() + "\t" + message.getBinaryMessage() );
|
||||
}
|
||||
}
|
||||
502
src/decode/p25/TrellisHalfRate.java
Normal file
502
src/decode/p25/TrellisHalfRate.java
Normal file
@@ -0,0 +1,502 @@
|
||||
package decode.p25;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import bits.BitSetBuffer;
|
||||
|
||||
public class TrellisHalfRate
|
||||
{
|
||||
private ArrayList<ConstellationNode> mConstellationNodes =
|
||||
new ArrayList<ConstellationNode>();
|
||||
|
||||
private static final int[][] CONSTELLATION_COSTS =
|
||||
{ { 0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4 },
|
||||
{ 1,0,2,1,2,1,3,2,2,1,3,2,3,2,4,3 },
|
||||
{ 1,2,0,1,2,3,1,2,2,3,1,2,3,4,2,3 },
|
||||
{ 2,1,1,0,3,2,2,1,3,2,2,1,4,3,3,2 },
|
||||
{ 1,2,2,3,0,1,1,2,2,3,3,4,1,2,2,3 },
|
||||
{ 2,1,3,2,1,0,2,1,3,2,4,3,2,1,3,2 },
|
||||
{ 2,3,1,2,1,2,0,1,3,4,2,3,2,3,1,2 },
|
||||
{ 3,2,2,1,2,1,1,0,4,3,3,2,3,2,2,1 },
|
||||
{ 1,2,2,3,2,3,3,4,0,1,1,2,1,2,2,3 },
|
||||
{ 2,1,3,2,3,2,4,3,1,0,2,1,2,1,3,2 },
|
||||
{ 2,3,1,2,3,4,2,3,1,2,0,1,2,3,1,2 },
|
||||
{ 3,2,2,1,4,3,3,2,2,1,1,0,3,2,2,1 },
|
||||
{ 2,3,3,4,1,2,2,3,1,2,2,3,0,1,1,2 },
|
||||
{ 3,2,4,3,2,1,3,2,2,1,3,2,1,0,2,1 },
|
||||
{ 3,4,2,3,2,3,1,2,2,3,1,2,1,2,0,1 },
|
||||
{ 4,3,3,2,3,2,2,1,3,2,2,1,2,1,1,0 } };
|
||||
|
||||
public TrellisHalfRate()
|
||||
{
|
||||
/* Create 49 x 4-bit constellation nodes to handle 196-bit blocks */
|
||||
ConstellationNode previous = null;
|
||||
|
||||
for( int x = 0; x < 49; x++ )
|
||||
{
|
||||
ConstellationNode node = new ConstellationNode();
|
||||
|
||||
if( previous != null )
|
||||
{
|
||||
previous.connect( node );
|
||||
}
|
||||
|
||||
previous = node;
|
||||
|
||||
mConstellationNodes.add( node );
|
||||
}
|
||||
}
|
||||
|
||||
public void dispose()
|
||||
{
|
||||
for( ConstellationNode node: mConstellationNodes )
|
||||
{
|
||||
node.dispose();
|
||||
}
|
||||
|
||||
mConstellationNodes.clear();
|
||||
}
|
||||
|
||||
public void decode( BitSetBuffer message, int start, int end )
|
||||
{
|
||||
/* load each of the nodes with deinterleaved constellations */
|
||||
for( int index = 0; index < 49; index++ )
|
||||
{
|
||||
Constellation c = getConstellation( message, index * 4 );
|
||||
|
||||
mConstellationNodes.get( index ).setConstellation( c );
|
||||
}
|
||||
|
||||
/* test to see if constellations are correct - otherwise correct them */
|
||||
ConstellationNode firstNode = mConstellationNodes.get( 0 );
|
||||
|
||||
if( !firstNode.startsWith( Dibit.D0 ) || !firstNode.isCorrect() )
|
||||
{
|
||||
firstNode.correctTo( Dibit.D0 );
|
||||
}
|
||||
|
||||
/* clear constellations from original message */
|
||||
message.clear( start, end );
|
||||
|
||||
/* replace with decoded values from the nodes */
|
||||
for( int index = 0; index < 49; index++ )
|
||||
{
|
||||
ConstellationNode node = mConstellationNodes.get( index );
|
||||
|
||||
if( node.firstBit() )
|
||||
{
|
||||
message.set( start + ( index * 2 ) );
|
||||
}
|
||||
if( node.secondBit() )
|
||||
{
|
||||
message.set( start + ( index * 2 ) + 1 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Constellation getConstellation( BitSetBuffer message, int index )
|
||||
{
|
||||
int constellation = 0;
|
||||
|
||||
for( int x = 0; x < 4; x++ )
|
||||
{
|
||||
if( message.get( index + x ) )
|
||||
{
|
||||
constellation += ( 1 << ( 3 - x ) );
|
||||
}
|
||||
}
|
||||
|
||||
return Constellation.fromValue( constellation );
|
||||
}
|
||||
|
||||
public class ConstellationNode
|
||||
{
|
||||
private ConstellationNode mConnectedNode;
|
||||
private Constellation mConstellation;
|
||||
private boolean mCorrect;
|
||||
|
||||
public ConstellationNode()
|
||||
{
|
||||
}
|
||||
|
||||
public void dispose()
|
||||
{
|
||||
mConnectedNode = null;
|
||||
}
|
||||
|
||||
public boolean startsWith( Dibit dibit )
|
||||
{
|
||||
return mConstellation.getLeft() == dibit;
|
||||
}
|
||||
|
||||
public boolean firstBit()
|
||||
{
|
||||
return mConstellation.getRight().firstBit();
|
||||
}
|
||||
|
||||
public boolean secondBit()
|
||||
{
|
||||
return mConstellation.getRight().secondBit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a correction down the line of connected nodes. Only nodes
|
||||
* with the mCorrect flag set to false will be corrected.
|
||||
*
|
||||
* Note: Assumes that the starting node's value is 0. Initiate the
|
||||
* corrective sequence by invoking this method with Dibit.D0 on the
|
||||
* first node.
|
||||
*
|
||||
* @param dibit to use for the left side.
|
||||
*/
|
||||
public void correctTo( Dibit dibit )
|
||||
{
|
||||
if( mCorrect && mConstellation.getLeft() == dibit )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if( isCurrentConnectionCorrect() )
|
||||
{
|
||||
mConstellation = Constellation.
|
||||
fromDibits( dibit, mConstellation.getRight() );
|
||||
|
||||
mCorrect = true;
|
||||
|
||||
if( mConnectedNode != null )
|
||||
{
|
||||
mConnectedNode.correctTo( mConstellation.getRight() );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Constellation cheapest = mConstellation;
|
||||
|
||||
int cost = 100; //arbitrary
|
||||
|
||||
for( Dibit d: Dibit.values() )
|
||||
{
|
||||
Constellation test = Constellation.fromDibits( dibit, d );
|
||||
|
||||
int testCost = mConstellation.costTo( test ) +
|
||||
mConnectedNode.costTo( d );
|
||||
|
||||
if( testCost < cost )
|
||||
{
|
||||
cost = testCost;
|
||||
cheapest = test;
|
||||
}
|
||||
}
|
||||
|
||||
mConstellation = cheapest;
|
||||
|
||||
mConnectedNode.correctTo( mConstellation.getRight() );
|
||||
|
||||
mCorrect = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the cost (hamming distance) of using the argument as the
|
||||
* left side dibit for the current node, and recursively finding the
|
||||
* cheapest corresponding right dibit.
|
||||
*
|
||||
* @param leftTest
|
||||
* @return
|
||||
*/
|
||||
public int costTo( Dibit leftTest )
|
||||
{
|
||||
if( isCurrentConnectionCorrect() )
|
||||
{
|
||||
Constellation c = Constellation.
|
||||
fromDibits( leftTest, mConstellation.getRight() );
|
||||
|
||||
return mConstellation.costTo( c );
|
||||
}
|
||||
else
|
||||
{
|
||||
int cheapestCost = 100; //arbitrary
|
||||
|
||||
for( Dibit d: Dibit.values() )
|
||||
{
|
||||
Constellation c = Constellation.fromDibits( leftTest, d );
|
||||
|
||||
int cost = mConnectedNode.costTo( d ) +
|
||||
mConstellation.costTo( c );
|
||||
|
||||
if( cost < cheapestCost )
|
||||
{
|
||||
cheapestCost = cost;
|
||||
}
|
||||
}
|
||||
|
||||
return cheapestCost;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if the immediate connection is correct
|
||||
*/
|
||||
public boolean isCurrentConnectionCorrect()
|
||||
{
|
||||
return ( mConnectedNode == null ||
|
||||
mConstellation.getRight() == mConnectedNode.getLeft() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a recursive call to all nodes to the right, setting the
|
||||
* mCorrect flag on all nodes to true, if the node's connection to the
|
||||
* right node is correct and all nodes to the right are correct. Or,
|
||||
* false if this node's connection, or any node's connection to the
|
||||
* right is incorrect.
|
||||
*
|
||||
* @return - true - all node connections to the right are correct
|
||||
* false - one or more nodes to the right are incorrect
|
||||
*/
|
||||
public boolean isCorrect()
|
||||
{
|
||||
if( mCorrect )
|
||||
{
|
||||
return mCorrect;
|
||||
}
|
||||
|
||||
if( mConnectedNode == null )
|
||||
{
|
||||
mCorrect = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
mCorrect = mConnectedNode.isCorrect() &&
|
||||
mConstellation.getRight() == mConnectedNode.getLeft();
|
||||
}
|
||||
|
||||
return mCorrect;
|
||||
}
|
||||
|
||||
public Dibit getLeft()
|
||||
{
|
||||
return mConstellation.getLeft();
|
||||
}
|
||||
|
||||
public void setConstellation( Constellation constellation )
|
||||
{
|
||||
mConstellation = constellation;
|
||||
mCorrect = false;
|
||||
}
|
||||
|
||||
public void connect( ConstellationNode node )
|
||||
{
|
||||
mConnectedNode = node;
|
||||
}
|
||||
}
|
||||
|
||||
public enum Constellation
|
||||
{
|
||||
C0( Dibit.D1, Dibit.D1, 0 ),
|
||||
C1( Dibit.D0, Dibit.D2, 1 ),
|
||||
C2( Dibit.D0, Dibit.D0, 2 ),
|
||||
C3( Dibit.D1, Dibit.D3, 3 ),
|
||||
C4( Dibit.D2, Dibit.D3, 4 ),
|
||||
C5( Dibit.D3, Dibit.D0, 5 ),
|
||||
C6( Dibit.D3, Dibit.D2, 6 ),
|
||||
C7( Dibit.D2, Dibit.D1, 7 ),
|
||||
C8( Dibit.D3, Dibit.D3, 8 ),
|
||||
C9( Dibit.D2, Dibit.D0, 9 ),
|
||||
CA( Dibit.D2, Dibit.D2, 10 ),
|
||||
CB( Dibit.D3, Dibit.D1, 11 ),
|
||||
CC( Dibit.D0, Dibit.D1, 12 ),
|
||||
CD( Dibit.D1, Dibit.D2, 13 ),
|
||||
CE( Dibit.D1, Dibit.D0, 14 ),
|
||||
CF( Dibit.D0, Dibit.D3, 15 );
|
||||
|
||||
private Dibit mLeftDibit;
|
||||
private Dibit mRightDibit;
|
||||
private int mValue;
|
||||
|
||||
private Constellation( Dibit leftDibit, Dibit rightDibit, int value )
|
||||
{
|
||||
mLeftDibit = leftDibit;
|
||||
mRightDibit = rightDibit;
|
||||
mValue = value;
|
||||
}
|
||||
|
||||
public Dibit getLeft()
|
||||
{
|
||||
return mLeftDibit;
|
||||
}
|
||||
|
||||
public Dibit getRight()
|
||||
{
|
||||
return mRightDibit;
|
||||
}
|
||||
|
||||
public int getValue()
|
||||
{
|
||||
return mValue;
|
||||
}
|
||||
|
||||
public static Constellation fromValue( int value )
|
||||
{
|
||||
if( 0 <= value && value <= 15 )
|
||||
{
|
||||
return Constellation.values()[ value ];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the cost or hamming distance to the other constellation using
|
||||
* the values from the constellation costs table
|
||||
*/
|
||||
public int costTo( Constellation other )
|
||||
{
|
||||
return CONSTELLATION_COSTS[ getValue() ][ other.getValue() ];
|
||||
}
|
||||
|
||||
public static Constellation fromDibits( Dibit left, Dibit right )
|
||||
{
|
||||
switch( left )
|
||||
{
|
||||
case D0:
|
||||
switch( right )
|
||||
{
|
||||
case D0:
|
||||
return C2;
|
||||
case D1:
|
||||
return CC;
|
||||
case D2:
|
||||
return C1;
|
||||
case D3:
|
||||
return CF;
|
||||
default:
|
||||
}
|
||||
case D1:
|
||||
switch( right )
|
||||
{
|
||||
case D0:
|
||||
return CE;
|
||||
case D1:
|
||||
return C0;
|
||||
case D2:
|
||||
return CD;
|
||||
case D3:
|
||||
return C3;
|
||||
default:
|
||||
}
|
||||
case D2:
|
||||
switch( right )
|
||||
{
|
||||
case D0:
|
||||
return C9;
|
||||
case D1:
|
||||
return C7;
|
||||
case D2:
|
||||
return CA;
|
||||
case D3:
|
||||
return C4;
|
||||
default:
|
||||
}
|
||||
case D3:
|
||||
switch( right )
|
||||
{
|
||||
case D0:
|
||||
return C5;
|
||||
case D1:
|
||||
return CB;
|
||||
case D2:
|
||||
return C6;
|
||||
case D3:
|
||||
return C8;
|
||||
default:
|
||||
}
|
||||
default:
|
||||
}
|
||||
|
||||
/* we should never get to here */
|
||||
return C0;
|
||||
}
|
||||
}
|
||||
|
||||
public enum Dibit
|
||||
{
|
||||
D0( false, false ),
|
||||
D1( false, true ),
|
||||
D2( true, false ),
|
||||
D3( true, true );
|
||||
|
||||
private boolean mFirstBit;
|
||||
private boolean mSecondBit;
|
||||
|
||||
private Dibit( boolean firstBit, boolean secondBit )
|
||||
{
|
||||
mFirstBit = firstBit;
|
||||
mSecondBit = secondBit;
|
||||
}
|
||||
|
||||
public boolean firstBit()
|
||||
{
|
||||
return mFirstBit;
|
||||
}
|
||||
|
||||
public boolean secondBit()
|
||||
{
|
||||
return mSecondBit;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test harness
|
||||
*/
|
||||
public static void main( String[] args )
|
||||
{
|
||||
String original = "0010101110101010001011001100111011001000100000101101001000100000110010111010100100100111100100100010000000001110001110010010110000010110001000101101101001110010001001011100001101011110110110011011";
|
||||
// String original = "0010111110111110001000100010001011000000000000001101101010101001110011011001001000101100000011100010001000101100001101010010001000011010011100001101100111001110000101111101010001101010101001111110";
|
||||
// String original = "0010001011000000110101111110001000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100010111110111110110000110101001011110101";
|
||||
String witherrors = "0010001011000000110101100110001000100010001100100011001000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100010001000100010111110111110110000110101001011110101";
|
||||
//Injected errors: xx x x
|
||||
|
||||
BitSetBuffer bufferOriginal = new BitSetBuffer( 196 );
|
||||
BitSetBuffer bufferWithErrors = new BitSetBuffer( 196 );
|
||||
|
||||
try
|
||||
{
|
||||
for( int x = 0; x < 196; x++ )
|
||||
{
|
||||
if( witherrors.substring( x, x + 1 ).contentEquals( "0" ) )
|
||||
{
|
||||
bufferWithErrors.add( false );
|
||||
}
|
||||
else
|
||||
{
|
||||
bufferWithErrors.add( true );
|
||||
}
|
||||
|
||||
if( original.substring( x, x + 1 ).contentEquals( "0" ) )
|
||||
{
|
||||
bufferOriginal.add( false );
|
||||
}
|
||||
else
|
||||
{
|
||||
bufferOriginal.add( true );
|
||||
}
|
||||
}
|
||||
}
|
||||
catch( Exception e )
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
System.out.println( " ORIGINAL: " + original );
|
||||
System.out.println( " WITH ERRORS: " + witherrors );
|
||||
|
||||
TrellisHalfRate t = new TrellisHalfRate();
|
||||
|
||||
t.decode( bufferOriginal, 0, 196 );
|
||||
System.out.println( "ORIG DECODED: " + bufferOriginal.toString() );
|
||||
|
||||
t.decode( bufferWithErrors, 0, 196 );
|
||||
System.out.println( "ERRS DECODED: " + bufferWithErrors.toString() );
|
||||
|
||||
}
|
||||
}
|
||||
116
src/decode/p25/message/P25Message.java
Normal file
116
src/decode/p25/message/P25Message.java
Normal file
@@ -0,0 +1,116 @@
|
||||
package decode.p25.message;
|
||||
|
||||
import decode.p25.reference.DataUnitID;
|
||||
import map.Plottable;
|
||||
import message.Message;
|
||||
import alias.Alias;
|
||||
import bits.BitSetBuffer;
|
||||
|
||||
public class P25Message extends Message
|
||||
{
|
||||
public static final int[] NAC = { 0,1,2,3,4,5,6,7,8,9,10,11 };
|
||||
public static final int[] DUID = { 12,13,14,15 };
|
||||
public static final int[] BCH = { 16,17,18,19,20,21,22,23,24,25,26,27,28,29,
|
||||
30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,
|
||||
54,55,56,57,58,59,60,61,62,63 };
|
||||
|
||||
protected BitSetBuffer mMessage;
|
||||
protected DataUnitID mDUID;
|
||||
|
||||
public P25Message( BitSetBuffer message, DataUnitID duid )
|
||||
{
|
||||
super();
|
||||
|
||||
mMessage = message;
|
||||
mDUID = duid;
|
||||
}
|
||||
|
||||
public String getNAC()
|
||||
{
|
||||
return mMessage.getHex( NAC, 3 );
|
||||
}
|
||||
|
||||
public DataUnitID getDUID()
|
||||
{
|
||||
return mDUID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getErrorStatus()
|
||||
{
|
||||
return "UNK";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.append( "NAC:" );
|
||||
sb.append( getNAC() );
|
||||
sb.append( " " );
|
||||
sb.append( getDUID().name() );
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBinaryMessage()
|
||||
{
|
||||
return mMessage.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getProtocol()
|
||||
{
|
||||
return "P25 Phase 1";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getEventType()
|
||||
{
|
||||
return mDUID.name();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFromID()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Alias getFromIDAlias()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getToID()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Alias getToIDAlias()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Plottable getPlottable()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return getMessage();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
157
src/decode/p25/message/PDUMessage.java
Normal file
157
src/decode/p25/message/PDUMessage.java
Normal file
@@ -0,0 +1,157 @@
|
||||
package decode.p25.message;
|
||||
|
||||
import decode.p25.reference.DataUnitID;
|
||||
import decode.p25.reference.PDUFormat;
|
||||
import decode.p25.reference.ServiceAccessPoint;
|
||||
import decode.p25.reference.Vendor;
|
||||
import bits.BitSetBuffer;
|
||||
|
||||
public class PDUMessage extends P25Message
|
||||
{
|
||||
public static final int PDU_HEADER_DUMMY1 = 66; //Always 0
|
||||
public static final int PDU_HEADER_CONFIRMATION_REQUIRED = 67;
|
||||
public static final int PDU_HEADER_DIRECTION_FLAG = 68;
|
||||
public static final int[] PDU_HEADER_FORMAT = { 69,70,71,72,73 };
|
||||
public static final int PDU_HEADER_DUMMY2 = 74; //Always 1
|
||||
public static final int PDU_HEADER_DUMMY3 = 75; //Always 1
|
||||
public static final int[] PDU_HEADER_SAP_ID = { 76,77,78,79,80 };
|
||||
public static final int[] PDU_HEADER_VENDOR_ID = { 81,82,83,84,85,86,87,88 };
|
||||
public static final int[] PDU_HEADER_LOGICAL_LINK_ID = { 89,90,91,92,93,94,
|
||||
97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114 };
|
||||
public static final int PDU_HEADER_FULL_MESSAGE_FLAG = 115;
|
||||
public static final int[] PDU_HEADER_BLOCKS_TO_FOLLOW = { 116,117,118,119,
|
||||
120,121,122 };
|
||||
public static final int PDU_HEADER_DUMMY4 = 123; //Always 0
|
||||
public static final int PDU_HEADER_DUMMY5 = 124; //Always 0
|
||||
public static final int PDU_HEADER_DUMMY6 = 125; //Always 0
|
||||
public static final int[] PDU_HEADER_PAD_BLOCKS = { 126,127,128,129,130 };
|
||||
public static final int PDU_HEADER_RESYNCHRONIZE_FLAG = 131;
|
||||
public static final int[] PDU_HEADER_SEQUENCE_NUMBER = { 132,133,134 };
|
||||
public static final int[] PDU_HEADER_FRAGMENT_SEQUENCE_NUMBER = { 135,136,
|
||||
137,138 };
|
||||
public static final int PDU_HEADER_DUMMY7 = 139; //Always 0
|
||||
public static final int PDU_HEADER_DUMMY8 = 140; //Always 0
|
||||
public static final int[] PDU_HEADER_DATA_HEADER_OFFSET = { 141,142,143,
|
||||
144,145,146 };
|
||||
public static final int[] PDU_HEADER_CRC = { 147,148,149,150,151,152,153,154,
|
||||
155,156,157,158,159,160,161,162 };
|
||||
|
||||
//status 166,167
|
||||
public PDUMessage( BitSetBuffer message, DataUnitID duid )
|
||||
{
|
||||
super( message, duid );
|
||||
}
|
||||
|
||||
public String getConfirmation()
|
||||
{
|
||||
return mMessage.get( PDU_HEADER_CONFIRMATION_REQUIRED ) ? "CON" : "UNC";
|
||||
}
|
||||
|
||||
public String getDirection()
|
||||
{
|
||||
return mMessage.get( PDU_HEADER_DIRECTION_FLAG ) ? "OSP" : "ISP";
|
||||
}
|
||||
|
||||
public PDUFormat getFormat()
|
||||
{
|
||||
return PDUFormat.fromValue( mMessage.getInt( PDU_HEADER_FORMAT ) );
|
||||
}
|
||||
|
||||
public ServiceAccessPoint getServiceAccessPoint()
|
||||
{
|
||||
return ServiceAccessPoint.fromValue( mMessage.getInt( PDU_HEADER_SAP_ID ) );
|
||||
}
|
||||
|
||||
public Vendor getVendor()
|
||||
{
|
||||
return Vendor.fromValue( mMessage.getInt( PDU_HEADER_VENDOR_ID ) );
|
||||
}
|
||||
|
||||
public String getLogicalLinkID()
|
||||
{
|
||||
return mMessage.getHex( PDU_HEADER_LOGICAL_LINK_ID, 6 );
|
||||
}
|
||||
|
||||
public boolean isFullMessage()
|
||||
{
|
||||
return mMessage.get( PDU_HEADER_FULL_MESSAGE_FLAG );
|
||||
}
|
||||
|
||||
public int getBlockCount()
|
||||
{
|
||||
return mMessage.getInt( PDU_HEADER_BLOCKS_TO_FOLLOW );
|
||||
}
|
||||
|
||||
public int getPadBlockCount()
|
||||
{
|
||||
return mMessage.getInt( PDU_HEADER_PAD_BLOCKS );
|
||||
}
|
||||
|
||||
public boolean isResync()
|
||||
{
|
||||
return mMessage.get( PDU_HEADER_RESYNCHRONIZE_FLAG );
|
||||
}
|
||||
|
||||
public int getSequenceNumber()
|
||||
{
|
||||
return mMessage.getInt( PDU_HEADER_SEQUENCE_NUMBER );
|
||||
}
|
||||
|
||||
public int getFragmentSequenceNumber()
|
||||
{
|
||||
return mMessage.getInt( PDU_HEADER_FRAGMENT_SEQUENCE_NUMBER );
|
||||
}
|
||||
|
||||
public int getDataHeaderOffset()
|
||||
{
|
||||
return mMessage.getInt( PDU_HEADER_DATA_HEADER_OFFSET );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.append( "NAC:" );
|
||||
sb.append( getNAC() );
|
||||
sb.append( " " );
|
||||
sb.append( getDUID().name() );
|
||||
sb.append( " " );
|
||||
sb.append( getConfirmation() );
|
||||
sb.append( " " );
|
||||
sb.append( getDirection() );
|
||||
sb.append( " FMT:" );
|
||||
sb.append( getFormat().getLabel() );
|
||||
|
||||
switch( getFormat() )
|
||||
{
|
||||
case ALTERNATE_MULTI_BLOCK_TRUNKING_CONTROL:
|
||||
case UNCONFIRMED_MULTI_BLOCK_TRUNKING_CONTROL:
|
||||
sb.append( " SAP:" );
|
||||
sb.append( getServiceAccessPoint().getLabel() );
|
||||
sb.append( " VEND:" );
|
||||
sb.append( getVendor().getLabel() );
|
||||
sb.append( " LLID:" );
|
||||
sb.append( getLogicalLinkID() );
|
||||
sb.append( " FMF:" );
|
||||
sb.append( isFullMessage() ? "1" : "0" );
|
||||
sb.append( " BLKS:" );
|
||||
sb.append( getBlockCount() );
|
||||
sb.append( " PAD:" );
|
||||
sb.append( getPadBlockCount() );
|
||||
sb.append( " RESYNC:" );
|
||||
sb.append( isResync() ? "1" : "0" );
|
||||
sb.append( " SEQ:" );
|
||||
sb.append( getSequenceNumber() );
|
||||
sb.append( " FRAG:" );
|
||||
sb.append( getFragmentSequenceNumber() );
|
||||
sb.append( " DHO:" );
|
||||
sb.append( getDataHeaderOffset() );
|
||||
break;
|
||||
default:
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
}
|
||||
67
src/decode/p25/message/TSBKMessage.java
Normal file
67
src/decode/p25/message/TSBKMessage.java
Normal file
@@ -0,0 +1,67 @@
|
||||
package decode.p25.message;
|
||||
|
||||
import decode.p25.reference.DataUnitID;
|
||||
import decode.p25.reference.Opcode;
|
||||
import decode.p25.reference.Vendor;
|
||||
import bits.BitSetBuffer;
|
||||
|
||||
public class TSBKMessage extends P25Message
|
||||
{
|
||||
public static final int LAST_BLOCK_FLAG = 0;
|
||||
public static final int ENCRYPTED_FLAG = 1;
|
||||
public static final int[] OPCODE = { 2,3,4,5,6,7 };
|
||||
public static final int[] VENDOR_ID = { 8,9,10,11,12,13,14,15 };
|
||||
|
||||
public TSBKMessage( int system, BitSetBuffer message )
|
||||
{
|
||||
super( message, DataUnitID.TSBK1 );
|
||||
}
|
||||
|
||||
public boolean isLastBlock()
|
||||
{
|
||||
return mMessage.get( LAST_BLOCK_FLAG );
|
||||
}
|
||||
|
||||
public boolean isEncrypted()
|
||||
{
|
||||
return mMessage.get( ENCRYPTED_FLAG );
|
||||
}
|
||||
|
||||
public Vendor getVendor()
|
||||
{
|
||||
return Vendor.fromValue( mMessage.getInt( VENDOR_ID ) );
|
||||
}
|
||||
|
||||
public Opcode getOpcode()
|
||||
{
|
||||
return Opcode.fromValue( mMessage.getInt( OPCODE ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.append( "SYSTEM:" );
|
||||
sb.append( getNAC() ); /* NAC is the system id for TSBK messages */
|
||||
sb.append( " VEND:" );
|
||||
sb.append( getVendor().getLabel() );
|
||||
|
||||
if( isEncrypted() )
|
||||
{
|
||||
sb.append( " ENCRYPTED" );
|
||||
}
|
||||
else
|
||||
{
|
||||
Opcode opcode = getOpcode();
|
||||
|
||||
sb.append( " OPCODE:" );
|
||||
sb.append( opcode.name() );
|
||||
sb.append( " [" );
|
||||
sb.append( mMessage.getHex( OPCODE, 2 ) );
|
||||
sb.append( "]" );
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
31
src/decode/p25/message/TSBKMessageFactory.java
Normal file
31
src/decode/p25/message/TSBKMessageFactory.java
Normal file
@@ -0,0 +1,31 @@
|
||||
package decode.p25.message;
|
||||
|
||||
import bits.BitSetBuffer;
|
||||
import decode.p25.reference.Opcode;
|
||||
import decode.p25.reference.Vendor;
|
||||
|
||||
public class TSBKMessageFactory
|
||||
{
|
||||
public static TSBKMessage getMessage( int system, BitSetBuffer buffer )
|
||||
{
|
||||
int opcode = buffer.getInt( TSBKMessage.OPCODE );
|
||||
|
||||
Vendor vendor = Vendor.fromValue(
|
||||
buffer.getInt( TSBKMessage.VENDOR_ID ) );
|
||||
|
||||
switch( vendor )
|
||||
{
|
||||
case STANDARD:
|
||||
switch( Opcode.fromValue( opcode ) )
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
|
||||
return new TSBKMessage( system, buffer );
|
||||
case MOTOROLA:
|
||||
default:
|
||||
return new TSBKMessage( system, buffer );
|
||||
}
|
||||
}
|
||||
}
|
||||
78
src/decode/p25/reference/DataUnitID.java
Normal file
78
src/decode/p25/reference/DataUnitID.java
Normal file
@@ -0,0 +1,78 @@
|
||||
package decode.p25.reference;
|
||||
|
||||
/**
|
||||
* The Data Unit ID (DUID) is part of the Network ID (NID) field and indicates
|
||||
* the type of message.
|
||||
*/
|
||||
public enum DataUnitID
|
||||
{
|
||||
NID ( -1, 64, "Network and Data Unit ID", false ),
|
||||
HDU ( 0, 722, "Header Data Unit", false ),
|
||||
TDU ( 3, 64, "Simple Terminator Data Unit", false ),
|
||||
LDU1 ( 5, 1630, "Logical Link Data Unit 1", true ),
|
||||
TSBK1( 7, 260, "Trunking Signaling Block", false ), //Single Block
|
||||
TSBK2( 7, 260, "Trunking Signaling Block", false ), //Single Block
|
||||
TSBK3( 7, 260, "Trunking Signaling Block", false ), //Single Block
|
||||
LDU2 ( 10, 1632, "Logical Link Data Unit 2", true ),
|
||||
PDU1 ( 12, 456, "Packet Data Unit", false ),
|
||||
PDU2 ( 12, 652, "Packet Data Unit", false ),
|
||||
PDU3 ( 12, 848, "Packet Data Unit", false ),
|
||||
TDULC( 15, 372, "Terminator Data Unit With Link Control", false ),
|
||||
UNKN ( -1, 0, "Unknown", false );
|
||||
|
||||
private int mValue;
|
||||
private int mMessageLength;
|
||||
private String mLabel;
|
||||
private boolean mParity;
|
||||
|
||||
private DataUnitID( int value, int length, String label, boolean parity )
|
||||
{
|
||||
mValue = value;
|
||||
mMessageLength = length;
|
||||
mLabel = label;
|
||||
mParity = parity;
|
||||
}
|
||||
|
||||
public int getValue()
|
||||
{
|
||||
return mValue;
|
||||
}
|
||||
|
||||
public int getMessageLength()
|
||||
{
|
||||
return mMessageLength;
|
||||
}
|
||||
|
||||
public String getLabel()
|
||||
{
|
||||
return mLabel;
|
||||
}
|
||||
|
||||
public boolean getParity()
|
||||
{
|
||||
return mParity;
|
||||
}
|
||||
|
||||
public static DataUnitID fromValue( int value )
|
||||
{
|
||||
switch( value )
|
||||
{
|
||||
case 0:
|
||||
return HDU;
|
||||
case 3:
|
||||
return TDU;
|
||||
case 5:
|
||||
return LDU1;
|
||||
case 7:
|
||||
return TSBK1;
|
||||
case 10:
|
||||
return LDU2;
|
||||
case 12:
|
||||
return PDU1;
|
||||
case 15:
|
||||
return TDULC;
|
||||
default:
|
||||
return UNKN;
|
||||
}
|
||||
}
|
||||
}
|
||||
23
src/decode/p25/reference/Encryption.java
Normal file
23
src/decode/p25/reference/Encryption.java
Normal file
@@ -0,0 +1,23 @@
|
||||
package decode.p25.reference;
|
||||
|
||||
public enum Encryption
|
||||
{
|
||||
ACCORDION_1_3( 0x00 ),
|
||||
BATON_AUTO_EVEN( 0x01 ),
|
||||
FIREFLY_TYPE1( 0x02 ),
|
||||
MAYFLY_TYPE1( 0x03 ),
|
||||
SAVILLE( 0x04 ),
|
||||
BATON_AUTO_ODD( 0x41 ),
|
||||
UNENCRYPTED( 0x80 ),
|
||||
DES_OFB( 0x81 ),
|
||||
TRIPLE_DES_2_KEY( 0x82 ),
|
||||
TRIPLE_DES_3_KEY( 0x83 ),
|
||||
AES( 0x84 );
|
||||
|
||||
private int mValue;
|
||||
|
||||
private Encryption( int value )
|
||||
{
|
||||
mValue = value;
|
||||
}
|
||||
}
|
||||
99
src/decode/p25/reference/Opcode.java
Normal file
99
src/decode/p25/reference/Opcode.java
Normal file
@@ -0,0 +1,99 @@
|
||||
package decode.p25.reference;
|
||||
|
||||
public enum Opcode
|
||||
{
|
||||
GROUP_VOICE_CHANNEL_GRANT( "Group Voice Channel Grant", 0 ),
|
||||
RESERVED_01( "Reserved", 1 ),
|
||||
GROUP_VOICE_CHANNLE_GRANT_UPDATE( "Group Voice Channel Grant Update", 2 ),
|
||||
GROUP_VOICE_CHANNEL_GRANT_UPDATE_EXPLICIT( "Group Voice Channel Grant Update - Explicit", 3 ),
|
||||
UNIT_TO_UNIT_VOICE_CHANNEL_GRANT( "Unit-to-Unit Voice Channel Grant", 4 ),
|
||||
UNIT_TO_UNIT_ANSWER_REQUEST( "Unit-to-Unit Answer Request", 5 ),
|
||||
UNIT_TO_UNIT_VOICE_CHANNEL_GRANT_UPDATE( "Unit-to-Unit Voice Channel Grant Update", 6 ),
|
||||
RESERVED_07( "Reserved", 7 ),
|
||||
TELEPHONE_INTERCONNECT_VOICE_CHANNEL_GRANT( "Telephone Interconnect Voice Channel Grant", 8 ),
|
||||
RESERVED_09( "Reserved", 9 ),
|
||||
TELEPHONE_INTERCONNECT_ANSWER_REQUEST( "Telephone Interconnect Answer Request", 10 ),
|
||||
RESERVED_0B( "Reserved", 11 ),
|
||||
RESERVED_0C( "Reserved", 12 ),
|
||||
RESERVED_0D( "Reserved", 13 ),
|
||||
RESERVED_0E( "Reserved", 14 ),
|
||||
RESERVED_0F( "Reserved", 15 ),
|
||||
INDIVIDUAL_DATA_CHANNEL_GRANT( "Individual Data Channel Grant", 16 ),
|
||||
GROUP_DATA_CHANNEL_GRANT( "Group Data Channel Grant", 17 ),
|
||||
GROUP_DATA_CHANNEL_ANNOUNCEMENT( "Group Data Channel Announcement", 18 ),
|
||||
GROUP_DATA_CHANNEL_ANNOUNCEMENT_EXPLICIT( "Group Data Channel Announcement-Explicit", 19 ),
|
||||
SNDCP_DATA_CHANNEL_GRANT( "SNDCP Data Channel Grant", 20 ),
|
||||
SNDCP_DATA_PAGE_REQUEST( "SNDCP Data Page Request", 21 ),
|
||||
SNDCP_DATA_CHANNEL_ANNOUNCEMENT_EXPLICIT( "SNDCP Data Channel Announcement Explicit", 22 ),
|
||||
RESERVED_17( "Reserved", 23 ),
|
||||
STATUS_UPDATE( "Status Update", 24 ),
|
||||
RESERVED_19( "Reserved", 25 ),
|
||||
STATUS_QUERY( "Status Query", 26 ),
|
||||
RESERVED_1B( "Reserved", 27 ),
|
||||
MESSAGE_UPDATE( "Message Update", 28 ),
|
||||
RADIO_UNIT_MONITOR_COMMAND( "Radio Unit Monitor Command", 29 ),
|
||||
RESERVED_1E( "Reserved", 30 ),
|
||||
CALL_ALERT( "Call Alert", 31 ),
|
||||
ACKNOWLEDGE_RESPONSE_FNE( "Acknowledge Response - FNE", 32 ),
|
||||
QUEUED_RESPONSE( "Queued Response", 33 ),
|
||||
RESERVED_22( "Reserved", 34 ),
|
||||
RESERVED_23( "Reserved", 35 ),
|
||||
EXTENDED_FUNCTION_COMMAND( "Extended Function Command", 36 ),
|
||||
RESERVED_25( "Reserved", 37 ),
|
||||
RESERVED_26( "Reserved", 38 ),
|
||||
DENY_RESPONSE( "Deny Response", 39 ),
|
||||
GROUP_AFFILIATION_RESPONSE( "Group Affiliation Response", 40 ),
|
||||
SECONDARY_CONTROL_CHANNEL_BROADCAST_EXPLICIT( "Secondary Control Channel Broadcast-Explicit", 41 ),
|
||||
GROUP_AFFILIATION_QUERY( "Group Affiliation Query", 42 ),
|
||||
LOCATION_REGISTRATION_RESPONSE( "Location Registration Response", 43 ),
|
||||
UNIT_REGISTRATION_RESPONSE( "Unit Registration Response", 44 ),
|
||||
UNIT_REGISTRATION_COMMAND( "Unit Registration Command", 45 ),
|
||||
AUTHENTICATION_COMMAND( "Authentication Command", 46 ),
|
||||
UNIT_DEREGISTRATION_ACKNOWLEDGE( "De-Registration Acknowledge", 47 ),
|
||||
RESERVED_30( "Reserved", 48 ),
|
||||
RESERVED_31( "Reserved", 49 ),
|
||||
RESERVED_32( "Reserved", 50 ),
|
||||
RESERVED_33( "Reserved", 51 ),
|
||||
IDENTIFIER_UPDATE_VHF_UHF_BANDS( "Identifier Update for VHF/UHF Bands", 52 ),
|
||||
TIME_DATE_ANNOUNCEMENT( "Time and Date Announcement", 53 ),
|
||||
ROAMING_ADDRESS_COMMAND( "Roaming Address Command", 54 ),
|
||||
ROAMING_ADDRESS_UPDATE( "Roaming Address Update", 55 ),
|
||||
SYSTEM_SERVICE_BROADCAST( "System Service Broadcast", 56 ),
|
||||
SECONDARY_CONTROL_CHANNEL_BROADCAST( "Secondary Control Channel Broadcast", 57 ),
|
||||
RFSS_STATUS_BROADCAST( "RFSS Status Broadcast", 58 ),
|
||||
NETWORK_STATUS_BROADCAST( "Network Status Broadcast", 59 ),
|
||||
ADJACENT_STATUS_BROADCAST( "Adjacent Status Broadcast", 60 ),
|
||||
IDENTIFIER_UPDATE_NON_VHF_UHF( "Identifier Update for non-VHF/UHF Bands", 61 ),
|
||||
PROTECTION_PARAMETER_BROADCAST( "Protection Parameter Broadcast", 62 ),
|
||||
PROTECTION_PARAMETER_UPDATE( "Protection Parameter Update", 63 ),
|
||||
UNKNOWN( "Unknown", -1 );
|
||||
|
||||
private String mDescription;
|
||||
private int mCode;
|
||||
|
||||
private Opcode( String description, int code )
|
||||
{
|
||||
mDescription = description;
|
||||
mCode = code;
|
||||
}
|
||||
|
||||
public String getDescription()
|
||||
{
|
||||
return mDescription;
|
||||
}
|
||||
|
||||
public int getCode()
|
||||
{
|
||||
return mCode;
|
||||
}
|
||||
|
||||
public static Opcode fromValue( int value )
|
||||
{
|
||||
if( 0 <= value && value <= 63 )
|
||||
{
|
||||
return values()[ value ];
|
||||
}
|
||||
|
||||
return UNKNOWN;
|
||||
}
|
||||
}
|
||||
67
src/decode/p25/reference/PDUFormat.java
Normal file
67
src/decode/p25/reference/PDUFormat.java
Normal file
@@ -0,0 +1,67 @@
|
||||
package decode.p25.reference;
|
||||
|
||||
public enum PDUFormat
|
||||
{
|
||||
F00( "F00", 0 ),
|
||||
F01( "F01", 1 ),
|
||||
F02( "F02", 2 ),
|
||||
F03( "F03", 3 ),
|
||||
F04( "F04", 4 ),
|
||||
F05( "F05", 5 ),
|
||||
F06( "F06", 6 ),
|
||||
F07( "F07", 7 ),
|
||||
F08( "F08", 8 ),
|
||||
F09( "F09", 9 ),
|
||||
F10( "F10", 10 ),
|
||||
F11( "F11", 11 ),
|
||||
F12( "F12", 12 ),
|
||||
F13( "F13", 13 ),
|
||||
F14( "F14", 14 ),
|
||||
F15( "F15", 15 ),
|
||||
F16( "F16", 16 ),
|
||||
F17( "F17", 17 ),
|
||||
F18( "F18", 18 ),
|
||||
F19( "F19", 19 ),
|
||||
F20( "F20", 20 ),
|
||||
UNCONFIRMED_MULTI_BLOCK_TRUNKING_CONTROL( "UMBTC", 21 ),
|
||||
ALTERNATE_MULTI_BLOCK_TRUNKING_CONTROL( "AMBTC", 22 ),
|
||||
F23( "F23", 23 ),
|
||||
F24( "F24", 24 ),
|
||||
F25( "F25", 25 ),
|
||||
F26( "F26", 26 ),
|
||||
F27( "F27", 27 ),
|
||||
F28( "F28", 28 ),
|
||||
F29( "F29", 29 ),
|
||||
F30( "F30", 30 ),
|
||||
F31( "F31", 31 ),
|
||||
UNKNOWN( "UNKN", -1 );
|
||||
|
||||
private String mLabel;
|
||||
private int mValue;
|
||||
|
||||
private PDUFormat( String label, int value )
|
||||
{
|
||||
mLabel = label;
|
||||
mValue = value;
|
||||
}
|
||||
|
||||
public String getLabel()
|
||||
{
|
||||
return mLabel;
|
||||
}
|
||||
|
||||
public int getValue()
|
||||
{
|
||||
return mValue;
|
||||
}
|
||||
|
||||
public static PDUFormat fromValue( int value )
|
||||
{
|
||||
if( 0 <= value && value <= 31 )
|
||||
{
|
||||
return values()[ value ];
|
||||
}
|
||||
|
||||
return UNKNOWN;
|
||||
}
|
||||
}
|
||||
99
src/decode/p25/reference/ServiceAccessPoint.java
Normal file
99
src/decode/p25/reference/ServiceAccessPoint.java
Normal file
@@ -0,0 +1,99 @@
|
||||
package decode.p25.reference;
|
||||
|
||||
public enum ServiceAccessPoint
|
||||
{
|
||||
SAP_0( "0", 0 ),
|
||||
SAP_1( "1", 1 ),
|
||||
SAP_2( "2", 2 ),
|
||||
SAP_3( "3", 3 ),
|
||||
SAP_4( "4", 4 ),
|
||||
SAP_5( "5", 5 ),
|
||||
SAP_6( "6", 6 ),
|
||||
SAP_7( "7", 7 ),
|
||||
SAP_8( "8", 8 ),
|
||||
SAP_9( "9", 9 ),
|
||||
SAP_10( "10", 10 ),
|
||||
SAP_11( "11", 11 ),
|
||||
SAP_12( "12", 12 ),
|
||||
SAP_13( "13", 13 ),
|
||||
SAP_14( "14", 14 ),
|
||||
SAP_15( "15", 15 ),
|
||||
SAP_16( "16", 16 ),
|
||||
SAP_17( "17", 17 ),
|
||||
SAP_18( "18", 18 ),
|
||||
SAP_19( "19", 19 ),
|
||||
SAP_20( "20", 20 ),
|
||||
SAP_21( "21", 21 ),
|
||||
SAP_22( "22", 22 ),
|
||||
SAP_23( "23", 23 ),
|
||||
SAP_24( "24", 24 ),
|
||||
SAP_25( "25", 25 ),
|
||||
SAP_26( "26", 26 ),
|
||||
SAP_27( "27", 27 ),
|
||||
SAP_28( "28", 28 ),
|
||||
SAP_29( "29", 29 ),
|
||||
SAP_30( "30", 30 ),
|
||||
SAP_31( "31", 31 ),
|
||||
SAP_32( "32", 32 ),
|
||||
SAP_33( "33", 33 ),
|
||||
SAP_34( "34", 34 ),
|
||||
SAP_35( "35", 35 ),
|
||||
SAP_36( "36", 36 ),
|
||||
SAP_37( "37", 37 ),
|
||||
SAP_38( "38", 38 ),
|
||||
SAP_39( "39", 39 ),
|
||||
SAP_40( "40", 40 ),
|
||||
SAP_41( "41", 41 ),
|
||||
SAP_42( "42", 42 ),
|
||||
SAP_43( "43", 43 ),
|
||||
SAP_44( "44", 44 ),
|
||||
SAP_45( "45", 45 ),
|
||||
SAP_46( "46", 46 ),
|
||||
SAP_47( "47", 47 ),
|
||||
SAP_48( "48", 48 ),
|
||||
SAP_49( "49", 49 ),
|
||||
SAP_50( "50", 50 ),
|
||||
SAP_51( "51", 51 ),
|
||||
SAP_52( "52", 52 ),
|
||||
SAP_53( "53", 53 ),
|
||||
SAP_54( "54", 54 ),
|
||||
SAP_55( "55", 55 ),
|
||||
SAP_56( "56", 56 ),
|
||||
SAP_57( "57", 57 ),
|
||||
SAP_58( "58", 58 ),
|
||||
SAP_59( "59", 59 ),
|
||||
SAP_60( "60", 60 ),
|
||||
UNENCRYPTED_TRUNKING_CONTROL( "OPEN TRUNK", 61 ),
|
||||
SAP_62( "62", 62 ),
|
||||
ENCRYPTED_TRUNKING_CONTROL( "ENC TRUNK", 63 ),
|
||||
UKNOWN( "UNKN", -1 );
|
||||
|
||||
private String mLabel;
|
||||
private int mValue;
|
||||
|
||||
private ServiceAccessPoint( String label, int value )
|
||||
{
|
||||
mLabel = label;
|
||||
mValue = value;
|
||||
}
|
||||
|
||||
public String getLabel()
|
||||
{
|
||||
return mLabel;
|
||||
}
|
||||
|
||||
public int getValue()
|
||||
{
|
||||
return mValue;
|
||||
}
|
||||
|
||||
public static ServiceAccessPoint fromValue( int value )
|
||||
{
|
||||
if( 0 <= value && value <= 63 )
|
||||
{
|
||||
return values()[ value ];
|
||||
}
|
||||
|
||||
return UKNOWN;
|
||||
}
|
||||
}
|
||||
21
src/decode/p25/reference/Status.java
Normal file
21
src/decode/p25/reference/Status.java
Normal file
@@ -0,0 +1,21 @@
|
||||
package decode.p25.reference;
|
||||
|
||||
public enum Status
|
||||
{
|
||||
TALKAROUND,
|
||||
BUSY,
|
||||
REPEATER,
|
||||
IDLE,
|
||||
UNKNOWN;
|
||||
|
||||
|
||||
public Status fromValue( int value )
|
||||
{
|
||||
if( 0 <= value && value <= 3 )
|
||||
{
|
||||
return values()[ value ];
|
||||
}
|
||||
|
||||
return UNKNOWN;
|
||||
}
|
||||
}
|
||||
292
src/decode/p25/reference/Vendor.java
Normal file
292
src/decode/p25/reference/Vendor.java
Normal file
@@ -0,0 +1,292 @@
|
||||
package decode.p25.reference;
|
||||
|
||||
public enum Vendor
|
||||
{
|
||||
STANDARD( "STANDARD", 0),
|
||||
V1( "V_01", 1),
|
||||
V2( "V_02", 2),
|
||||
V3( "V_03", 3),
|
||||
V4( "V_04", 4),
|
||||
V5( "V_05", 5),
|
||||
V6( "V_06", 6),
|
||||
V7( "V_07", 7),
|
||||
V8( "V_08", 8),
|
||||
V9( "V_09", 9),
|
||||
V10( "V_0A", 10),
|
||||
V11( "V_0B", 11),
|
||||
V12( "V_0C", 12),
|
||||
V13( "V_0D", 13),
|
||||
V14( "V_0E", 14),
|
||||
V15( "V_0F", 15),
|
||||
RELM( "RELM/BK RADIO", 16),
|
||||
V17( "V_11", 17),
|
||||
V18( "V_12", 18),
|
||||
V19( "V_13", 19),
|
||||
V20( "V_14", 20),
|
||||
V21( "V_15", 21),
|
||||
V22( "V_16", 22),
|
||||
V23( "V_17", 23),
|
||||
V24( "V_18", 24),
|
||||
V25( "V_19", 25),
|
||||
V26( "V_1A", 26),
|
||||
V27( "V_1B", 27),
|
||||
V28( "V_1C", 28),
|
||||
V29( "V_1D", 29),
|
||||
V30( "V_1E", 30),
|
||||
V31( "V_1F", 31),
|
||||
CYCOMM( "CYCOMM", 32),
|
||||
V33( "V_21", 33),
|
||||
V34( "V_22", 34),
|
||||
V35( "V_23", 35),
|
||||
V36( "V_24", 36),
|
||||
V37( "V_25", 37),
|
||||
V38( "V_26", 38),
|
||||
V39( "V_27", 39),
|
||||
EFRATOM( "EFRATOM", 40),
|
||||
V41( "V_29", 41),
|
||||
V42( "V_2A", 42),
|
||||
V43( "V_2B", 43),
|
||||
V44( "V_2C", 44),
|
||||
V45( "V_2D", 45),
|
||||
V46( "V_2E", 46),
|
||||
V47( "V_2F", 47),
|
||||
ERICSSON( "ERICSSON", 48),
|
||||
V49( "V_31", 49),
|
||||
V50( "V_32", 50),
|
||||
V51( "V_33", 51),
|
||||
V52( "V_34", 52),
|
||||
V53( "V_35", 53),
|
||||
V54( "V_36", 54),
|
||||
V55( "V_37", 55),
|
||||
DATRON( "DATRON", 56),
|
||||
V57( "V_39", 57),
|
||||
V58( "V_3A", 58),
|
||||
V59( "V_3B", 59),
|
||||
V60( "V_3C", 60),
|
||||
V61( "V_3D", 61),
|
||||
V62( "V_3E", 62),
|
||||
V63( "V_3F", 63),
|
||||
ICOM( "ICOM", 64),
|
||||
V65( "V_41", 65),
|
||||
V66( "V_42", 66),
|
||||
V67( "V_43", 67),
|
||||
V68( "V_44", 68),
|
||||
V69( "V_45", 69),
|
||||
V70( "V_46", 70),
|
||||
V71( "V_47", 71),
|
||||
GARMIN( "GARMIN", 72),
|
||||
V73( "V_49", 73),
|
||||
V74( "V_4A", 74),
|
||||
V75( "V_4B", 75),
|
||||
V76( "V_4C", 76),
|
||||
V77( "V_4D", 77),
|
||||
V78( "V_4E", 78),
|
||||
V79( "V_4F", 79),
|
||||
GTE( "GTE", 80),
|
||||
V81( "V_51", 81),
|
||||
V82( "V_52", 82),
|
||||
V83( "V_53", 83),
|
||||
V84( "V_54", 84),
|
||||
IFR_SYSTEMS( "IFR SYSTEMS", 85),
|
||||
V86( "V_56", 86),
|
||||
V87( "V_57", 87),
|
||||
V88( "V_58", 88),
|
||||
V89( "V_59", 89),
|
||||
V90( "V_5A", 90),
|
||||
V91( "V_5B", 91),
|
||||
V92( "V_5C", 92),
|
||||
V93( "V_5D", 93),
|
||||
V94( "V_5E", 94),
|
||||
V95( "V_5F", 95),
|
||||
GEC_MARCONI( "GEC-MARCONI", 96),
|
||||
V97( "V_61", 97),
|
||||
V98( "V_62", 98),
|
||||
V99( "V_63", 99),
|
||||
V100( "V_64", 100),
|
||||
V101( "V_65", 101),
|
||||
V102( "V_66", 102),
|
||||
V103( "V_67", 103),
|
||||
KENWOOD( "KENWOOD", 104),
|
||||
V105( "V_69", 105),
|
||||
V106( "V_6A", 106),
|
||||
V107( "V_6B", 107),
|
||||
V108( "V_6C", 108),
|
||||
V109( "V_6D", 109),
|
||||
V110( "V_6E", 110),
|
||||
V111( "V_6F", 111),
|
||||
GLENAYRE( "GLENAYRE ELECTRONICS", 112),
|
||||
V113( "V_71", 113),
|
||||
V114( "V_72", 114),
|
||||
V115( "V_73", 115),
|
||||
JAPAN_RADIO( "JAPAN RADIO CO", 116),
|
||||
V117( "V_75", 117),
|
||||
V118( "V_76", 118),
|
||||
V119( "V_77", 119),
|
||||
KOKUSAI( "KOKUSAI", 120),
|
||||
V121( "V_79", 121),
|
||||
V122( "V_7A", 122),
|
||||
V123( "V_7B", 123),
|
||||
MAXON( "MAXON", 124),
|
||||
V125( "V_7D", 125),
|
||||
V126( "V_7E", 126),
|
||||
V127( "V_7F", 127),
|
||||
MIDLAND( "MIDLAND", 128),
|
||||
V129( "V_81", 129),
|
||||
V130( "V_82", 130),
|
||||
V131( "V_83", 131),
|
||||
V132( "V_84", 132),
|
||||
V133( "V_85", 133),
|
||||
DANIELS( "DANIELS ELECTRONICS", 134),
|
||||
V135( "V_87", 135),
|
||||
V136( "V_88", 136),
|
||||
V137( "V_89", 137),
|
||||
V138( "V_8A", 138),
|
||||
V139( "V_8B", 139),
|
||||
V140( "V_8C", 140),
|
||||
V141( "V_8D", 141),
|
||||
V142( "V_8E", 142),
|
||||
V143( "V_8F", 143),
|
||||
MOTOROLA( "MOTOROLA", 144),
|
||||
V145( "V_91", 145),
|
||||
V146( "V_92", 146),
|
||||
V147( "V_93", 147),
|
||||
V148( "V_94", 148),
|
||||
V149( "V_95", 149),
|
||||
V150( "V_96", 150),
|
||||
V151( "V_97", 151),
|
||||
V152( "V_98", 152),
|
||||
V153( "V_99", 153),
|
||||
V154( "V_9A", 154),
|
||||
V155( "V_9B", 155),
|
||||
V156( "V_9C", 156),
|
||||
V157( "V_9D", 157),
|
||||
V158( "V_9E", 158),
|
||||
V159( "V_9F", 159),
|
||||
THALES( "THALES", 160),
|
||||
V161( "V_A1", 161),
|
||||
V162( "V_A2", 162),
|
||||
V163( "V_A3", 163),
|
||||
MACOM( "M/A-COM", 164),
|
||||
V165( "V_A5", 165),
|
||||
V166( "V_A6", 166),
|
||||
V167( "V_A7", 167),
|
||||
V168( "V_A8", 168),
|
||||
V169( "V_A9", 169),
|
||||
V170( "V_AA", 170),
|
||||
V171( "V_AB", 171),
|
||||
V172( "V_AC", 172),
|
||||
V173( "V_AD", 173),
|
||||
V174( "V_AE", 174),
|
||||
V175( "V_AF", 175),
|
||||
RATHEON( "RATHEON", 176),
|
||||
V177( "V_B1", 177),
|
||||
V178( "V_B2", 178),
|
||||
V179( "V_B3", 179),
|
||||
V180( "V_B4", 180),
|
||||
V181( "V_B5", 181),
|
||||
V182( "V_B6", 182),
|
||||
V183( "V_B7", 183),
|
||||
V184( "V_B8", 184),
|
||||
V185( "V_B9", 185),
|
||||
V186( "V_BA", 186),
|
||||
V187( "V_BB", 187),
|
||||
V188( "V_BC", 188),
|
||||
V189( "V_BD", 189),
|
||||
V190( "V_BE", 190),
|
||||
V191( "V_BF", 191),
|
||||
SEA( "SEA", 192),
|
||||
V193( "V_C1", 193),
|
||||
V194( "V_C2", 194),
|
||||
V195( "V_C3", 195),
|
||||
V196( "V_C4", 196),
|
||||
V197( "V_C5", 197),
|
||||
V198( "V_C6", 198),
|
||||
V199( "V_C7", 199),
|
||||
SECURICOR( "SECURICOR", 200),
|
||||
V201( "V_C9", 201),
|
||||
V202( "V_CA", 202),
|
||||
V203( "V_CB", 203),
|
||||
V204( "V_CC", 204),
|
||||
V205( "V_CD", 205),
|
||||
V206( "V_CE", 206),
|
||||
V207( "V_CF", 207),
|
||||
ADI( "ADI", 208),
|
||||
V209( "V_D1", 209),
|
||||
V210( "V_D2", 210),
|
||||
V211( "V_D3", 211),
|
||||
V212( "V_D4", 212),
|
||||
V213( "V_D5", 213),
|
||||
V214( "V_D6", 214),
|
||||
V215( "V_D7", 215),
|
||||
TAIT( "TAIT", 216),
|
||||
V217( "V_D9", 217),
|
||||
V218( "V_DA", 218),
|
||||
V219( "V_DB", 219),
|
||||
V220( "V_DC", 220),
|
||||
V221( "V_DD", 221),
|
||||
V222( "V_DE", 222),
|
||||
V223( "V_DF", 223),
|
||||
TELETEC( "TELETEC", 224),
|
||||
V225( "V_E1", 225),
|
||||
V226( "V_E2", 226),
|
||||
V227( "V_E3", 227),
|
||||
V228( "V_E4", 228),
|
||||
V229( "V_E5", 229),
|
||||
V230( "V_E6", 230),
|
||||
V231( "V_E7", 231),
|
||||
V232( "V_E8", 232),
|
||||
V233( "V_E9", 233),
|
||||
V234( "V_EA", 234),
|
||||
V235( "V_EB", 235),
|
||||
V236( "V_EC", 236),
|
||||
V237( "V_ED", 237),
|
||||
V238( "V_EE", 238),
|
||||
V239( "V_EF", 239),
|
||||
TRANSCRYPT( "TRANSCRYPT", 240),
|
||||
V241( "V_F1", 241),
|
||||
V242( "V_F2", 242),
|
||||
V243( "V_F3", 243),
|
||||
V244( "V_F4", 244),
|
||||
V245( "V_F5", 245),
|
||||
V246( "V_F6", 246),
|
||||
V247( "V_F7", 247),
|
||||
V248( "V_F8", 248),
|
||||
V249( "V_F9", 249),
|
||||
V250( "V_FA", 250),
|
||||
V251( "V_FB", 251),
|
||||
V252( "V_FC", 252),
|
||||
V253( "V_FD", 253),
|
||||
V254( "V_FE", 254),
|
||||
V255( "V_FF", 255),
|
||||
VUNK( "UNKN", -1 );
|
||||
|
||||
private String mLabel;
|
||||
private int mValue;
|
||||
|
||||
private Vendor( String label, int value )
|
||||
{
|
||||
mLabel = label;
|
||||
mValue = value;
|
||||
}
|
||||
|
||||
public String getLabel()
|
||||
{
|
||||
return mLabel;
|
||||
}
|
||||
|
||||
public int getValue()
|
||||
{
|
||||
return mValue;
|
||||
}
|
||||
|
||||
public static Vendor fromValue( int value )
|
||||
{
|
||||
if( 0 <= value && value <= 255 )
|
||||
{
|
||||
return values()[ value ];
|
||||
}
|
||||
|
||||
return VUNK;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
/**
|
||||
@@ -111,10 +111,7 @@ public class FloatTapViewPanel extends TapViewPanel
|
||||
{
|
||||
int middle = (int)( getHeight() / 2 );
|
||||
|
||||
float scaledValue = value / (float)Short.MAX_VALUE;
|
||||
|
||||
// mLog.info( "Float value is:" + scaledValue );
|
||||
float y = middle - ( middle * scaledValue * mVerticalZoom );
|
||||
float y = middle - ( middle * value * mVerticalZoom );
|
||||
|
||||
/* Clip y to zero as necessary */
|
||||
y = ( y < 0 ) ? 0 : y;
|
||||
|
||||
26
src/sample/adapter/Test.java
Normal file
26
src/sample/adapter/Test.java
Normal file
@@ -0,0 +1,26 @@
|
||||
package sample.adapter;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import source.tuner.hackrf.HackRFTunerController;
|
||||
|
||||
public class Test
|
||||
{
|
||||
private final static Logger mLog =
|
||||
LoggerFactory.getLogger( HackRFTunerController.class );
|
||||
|
||||
public static void main( String[] args )
|
||||
{
|
||||
mLog.debug( "Starting" );
|
||||
|
||||
ShortToFloatMap map1 = new ShortToFloatMap();
|
||||
|
||||
for( short x = -32768; x < -32758; x++ )
|
||||
{
|
||||
mLog.debug( "X:" + x + " value:" + map1.get( x ) );
|
||||
}
|
||||
mLog.debug( "Finished" );
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user