mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-12 08:37:25 -05:00
Note: Several of the decoders are currently non-functioning in this commit. If you want the latest functioning codebase, please checkout the commit prior to this one. All decoders will eventually be updated with this new architecture. Currently broken: - Logging and recording - Aux decoders (Fleetsync/MDC/LoJack/Tait) - LTR/Passport decoders Changes in this commit: - Module architecture for decoders and components with central provisioning by the DecoderFactory - Transition from Complex sample stream processing to float buffer processing for improved memory management - Redesigned filter structures - to support buffers and streams - Resolved several memory leak issues - Universal sharing of the thread pool manager - Added centralized audio management with call/user prioritization and new gui with mute/volume controls - Embedded audio metadata for eventual streaming support - Redesigned ProcessingChain to support independent re-use - Added new TrafficChannelManager to handle traffic channel allocations - Traffic Channel pool size and Call timeout available in P25 and MPT1327 configs - Redesigned channel and decoder gui components
659 lines
18 KiB
Java
659 lines
18 KiB
Java
package module.decode.p25;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.HashMap;
|
|
import java.util.Iterator;
|
|
import java.util.List;
|
|
|
|
import org.slf4j.Logger;
|
|
import org.slf4j.LoggerFactory;
|
|
|
|
import bits.BinaryMessage;
|
|
import dsp.symbol.Dibit;
|
|
|
|
public class Trellis_3_4_Rate
|
|
{
|
|
private final static Logger mLog =
|
|
LoggerFactory.getLogger( Trellis_3_4_Rate.class );
|
|
|
|
/* Hamming distance (bit match count) between constellation pairs */
|
|
private static final int[][] CONSTELLATION_METRICS =
|
|
{ { 4,3,3,2,3,2,2,1,3,2,2,1,2,1,1,0 },
|
|
{ 3,4,2,3,2,3,1,2,2,3,1,2,1,2,0,1 },
|
|
{ 3,2,4,3,2,1,3,2,2,1,3,2,1,0,2,1 },
|
|
{ 2,3,3,4,1,2,2,3,1,2,2,3,0,1,1,2 },
|
|
{ 3,2,2,1,4,3,3,2,2,1,1,0,3,2,2,1 },
|
|
{ 2,3,1,2,3,4,2,3,1,2,0,1,2,3,1,2 },
|
|
{ 2,1,3,2,3,2,4,3,1,0,2,1,2,1,3,2 },
|
|
{ 1,2,2,3,2,3,3,4,0,1,1,2,1,2,2,3 },
|
|
{ 3,2,2,1,2,1,1,0,4,3,3,2,3,2,2,1 },
|
|
{ 2,3,1,2,1,2,0,1,3,4,2,3,2,3,1,2 },
|
|
{ 2,1,3,2,1,0,2,1,3,2,4,3,2,1,3,2 },
|
|
{ 1,2,2,3,0,1,1,2,2,3,3,4,1,2,2,3 },
|
|
{ 2,1,1,0,3,2,2,1,3,2,2,1,4,3,3,2 },
|
|
{ 1,2,0,1,2,3,1,2,2,3,1,2,3,4,2,3 },
|
|
{ 1,0,2,1,2,1,3,2,2,1,3,2,3,2,4,3 },
|
|
{ 0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4 } };
|
|
|
|
/* Even valued state tribits can only produce even valued constellations
|
|
* and odd valued state tribits can only produce odd constellations. */
|
|
public static Con[] EVEN_CONSTELLATIONS = new Con[] { Con.C0,Con.C2,Con.C4,
|
|
Con.C6,Con.C8,Con.CA,Con.CC,Con.CE };
|
|
public static Con[] ODD_CONSTELLATIONS = new Con[] { Con.C1,Con.C3,Con.C5,
|
|
Con.C7,Con.C9,Con.CB,Con.CD,Con.CF };
|
|
|
|
/* Constellation and state tribit lookup map to find the input tribit */
|
|
public static HashMap<Con,Tribit[]> INPUT_FROM_CONSTELLATION_MAP;
|
|
|
|
private ArrayList<Con> mTransmittedConstellations = new ArrayList<Con>();
|
|
private ArrayList<Path> mSurvivorPaths = new ArrayList<Path>();
|
|
private ArrayList<Path> mNewPaths = new ArrayList<Path>();
|
|
private PathMetrics mPathMetrics = new PathMetrics();
|
|
|
|
/**
|
|
* Implements the Viterbi algorithm to decode 3/4 rate trellis encoded 196-bit
|
|
* packet data messages.
|
|
*/
|
|
public Trellis_3_4_Rate()
|
|
{
|
|
createConstellationToTribitMap();
|
|
}
|
|
|
|
/**
|
|
* Creates a lookup map for state to input tribit values for a given
|
|
* constellation. Input tribits are contained in the array using the state
|
|
* tribit's value as the lookup index. Null values indicate illegal state
|
|
* and input combinations for the specified constellation.
|
|
*/
|
|
private void createConstellationToTribitMap()
|
|
{
|
|
INPUT_FROM_CONSTELLATION_MAP = new HashMap<Con,Tribit[]>();
|
|
|
|
INPUT_FROM_CONSTELLATION_MAP.put( Con.CB, new Tribit[] { null, null, Tribit.T5, Tribit.T3, Tribit.T1, Tribit.T7, null, null } );
|
|
INPUT_FROM_CONSTELLATION_MAP.put( Con.CC, new Tribit[] { Tribit.T3, Tribit.T1, null, null, null, null, Tribit.T7, Tribit.T5 } );
|
|
INPUT_FROM_CONSTELLATION_MAP.put( Con.C0, new Tribit[] { Tribit.T0, Tribit.T6, null, null, null, null, Tribit.T4, Tribit.T2 } );
|
|
INPUT_FROM_CONSTELLATION_MAP.put( Con.C7, new Tribit[] { null, null, Tribit.T6, Tribit.T4, Tribit.T2, Tribit.T0, null, null } );
|
|
INPUT_FROM_CONSTELLATION_MAP.put( Con.CE, new Tribit[] { Tribit.T7, Tribit.T5, null, null, null, null, Tribit.T3, Tribit.T1 } );
|
|
INPUT_FROM_CONSTELLATION_MAP.put( Con.C9, new Tribit[] { null, null, Tribit.T1, Tribit.T7, Tribit.T5, Tribit.T3, null, null } );
|
|
INPUT_FROM_CONSTELLATION_MAP.put( Con.C5, new Tribit[] { null, null, Tribit.T2, Tribit.T0, Tribit.T6, Tribit.T4, null, null } );
|
|
INPUT_FROM_CONSTELLATION_MAP.put( Con.C2, new Tribit[] { Tribit.T4, Tribit.T2, null, null, null, null, Tribit.T0, Tribit.T6 } );
|
|
INPUT_FROM_CONSTELLATION_MAP.put( Con.CA, new Tribit[] { Tribit.T5, Tribit.T3, null, null, null, null, Tribit.T1, Tribit.T7 } );
|
|
INPUT_FROM_CONSTELLATION_MAP.put( Con.CD, new Tribit[] { null, null, Tribit.T3, Tribit.T1, Tribit.T7, Tribit.T5, null, null } );
|
|
INPUT_FROM_CONSTELLATION_MAP.put( Con.C1, new Tribit[] { null, null, Tribit.T0, Tribit.T6, Tribit.T4, Tribit.T2, null, null } );
|
|
INPUT_FROM_CONSTELLATION_MAP.put( Con.C6, new Tribit[] { Tribit.T6, Tribit.T4, null, null, null, null, Tribit.T2, Tribit.T0 } );
|
|
INPUT_FROM_CONSTELLATION_MAP.put( Con.CF, new Tribit[] { null, null, Tribit.T7, Tribit.T5, Tribit.T3, Tribit.T1, null, null } );
|
|
INPUT_FROM_CONSTELLATION_MAP.put( Con.C8, new Tribit[] { Tribit.T1, Tribit.T7, null, null, null, null, Tribit.T5, Tribit.T3 } );
|
|
INPUT_FROM_CONSTELLATION_MAP.put( Con.C4, new Tribit[] { Tribit.T2, Tribit.T0, null, null, null, null, Tribit.T6, Tribit.T4 } );
|
|
INPUT_FROM_CONSTELLATION_MAP.put( Con.C3, new Tribit[] { null, null, Tribit.T4, Tribit.T2, Tribit.T0, Tribit.T6, null, null } );;
|
|
}
|
|
|
|
/**
|
|
* Member object cleanup prior to deleting
|
|
*/
|
|
public void dispose()
|
|
{
|
|
reset();
|
|
}
|
|
|
|
/**
|
|
* Decodes a 196-bit 3/4 rate convolutional encoded message located between
|
|
* start and end indexes and returns the decoded 144-bit message overlayed
|
|
* upon the original message with the remaining 52 bits cleared to zero.
|
|
*
|
|
* @return - original message with decoded message bits..
|
|
*/
|
|
public boolean decode( BinaryMessage message, int start, int end )
|
|
{
|
|
reset();
|
|
|
|
/* Load and decode each of the transmitted constellations */
|
|
for( int index = 0; index < 49; index++ )
|
|
{
|
|
Con c = getConstellation( message, start + index * 4 );
|
|
|
|
add( c );
|
|
}
|
|
|
|
/* The final decoded survivor path should be at path metrics tribit 0 */
|
|
Path path = mPathMetrics.getPath( Tribit.T0 );
|
|
|
|
/* We should have a path with 50 nodes, counting the starting 000 node
|
|
* and the flushing 000 node, otherwise there was an error */
|
|
if( path != null && path.getNodes().size() == 50 )
|
|
{
|
|
/* Clear the original message bits */
|
|
message.clear( start, end );
|
|
|
|
List<Node> nodes = path.getNodes();
|
|
|
|
/* Load each of the nodes' state tribit values into the original message */
|
|
for( int x = 1; x < 50; x++ )
|
|
{
|
|
message.load( start + ( ( x - 1 ) * 3 ), 3,
|
|
nodes.get( x ).getState().getValue() );
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Resets the decoder before decoding a new data packet.
|
|
*/
|
|
private void reset()
|
|
{
|
|
mTransmittedConstellations.clear();
|
|
mNewPaths.clear();
|
|
|
|
mPathMetrics.reset();
|
|
|
|
mSurvivorPaths.clear();
|
|
|
|
/* Tribit 000 is the only legal start point */
|
|
mSurvivorPaths.add( new Path( new Node( 0, Tribit.T0, Con.C0 ) ) );
|
|
}
|
|
|
|
private void add( Con con )
|
|
{
|
|
mTransmittedConstellations.add( con );
|
|
|
|
/* Add in any newly created survivor paths */
|
|
mSurvivorPaths.addAll( mNewPaths );
|
|
mNewPaths.clear();
|
|
|
|
/* Set a culling threshold for any path metrics 3 below the best path */
|
|
int survivorThreshold = mPathMetrics.getSurvivorThreshold();
|
|
|
|
/* Reset path metrics */
|
|
mPathMetrics.reset();
|
|
|
|
/* Add constellation to each survivor path. If a survivor path metric
|
|
* falls below the culling threshold, remove it */
|
|
Iterator<Path> it = mSurvivorPaths.iterator();
|
|
|
|
Path path;
|
|
|
|
while( it.hasNext() )
|
|
{
|
|
path = it.next();
|
|
|
|
if( path.isDead() || path.getPathMetric() < survivorThreshold )
|
|
{
|
|
it.remove();
|
|
}
|
|
else
|
|
{
|
|
path.add( con );
|
|
}
|
|
}
|
|
}
|
|
|
|
private Con getConstellation( BinaryMessage message, int index )
|
|
{
|
|
int value = message.getInt( index, index + 3 );
|
|
return Con.fromTransmittedValue( value );
|
|
}
|
|
|
|
/**
|
|
* Path metrics maintains a map of the current best path metrics that end
|
|
* at each of the 8 valid tribit values. Each new path is evaluated against
|
|
* the current best path for retention (survivor) or disposal (dead). A
|
|
* best metric is maintained to evaluate the Walking Dead for disposal.
|
|
* Walking dead is a path lagging the best path metric by more than 3.
|
|
*/
|
|
public class PathMetrics
|
|
{
|
|
private HashMap<Tribit,Path> mMetrics = new HashMap<Tribit,Path>();
|
|
|
|
private int mBestMetric = 0;
|
|
|
|
public PathMetrics()
|
|
{
|
|
}
|
|
|
|
public int getSurvivorThreshold()
|
|
{
|
|
return mBestMetric - 3;
|
|
}
|
|
|
|
/**
|
|
* Removes all path metrics
|
|
*/
|
|
public void reset()
|
|
{
|
|
mMetrics.clear();
|
|
mBestMetric = 0;
|
|
}
|
|
|
|
public int getMetric( Tribit tribit )
|
|
{
|
|
if( mMetrics.containsKey( tribit ) )
|
|
{
|
|
return mMetrics.get( tribit ).getPathMetric();
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
public Path getPath( Tribit tribit )
|
|
{
|
|
return mMetrics.get( tribit );
|
|
}
|
|
|
|
/**
|
|
* Evaluates the path's metric against the current best path metric for
|
|
* the final state tribit of the path. If the path's metric is higher
|
|
* than the current best path metric, the current path is marked for
|
|
* termination and the new path is promoted to best path. If the new
|
|
* path's metric is less than the best path metric, then the new path
|
|
* metric is marked for termination. If the metrics are equal, the new
|
|
* path is allowed to survive.
|
|
*
|
|
* @param path/metric to evaluate
|
|
*/
|
|
public void evalute( Path path )
|
|
{
|
|
Node lastNode = path.getLastNode();
|
|
|
|
Path bestPath = mMetrics.get( lastNode.getState() );
|
|
|
|
if( bestPath == null )
|
|
{
|
|
mMetrics.put( lastNode.getState(), path );
|
|
}
|
|
else
|
|
{
|
|
if( path.getPathMetric() < bestPath.getPathMetric() )
|
|
{
|
|
path.setSurvivor( false );
|
|
}
|
|
else if( path.getPathMetric() > bestPath.getPathMetric() )
|
|
{
|
|
mMetrics.put( lastNode.getState(), path );
|
|
|
|
bestPath.setSurvivor( false );
|
|
}
|
|
|
|
/* Equal metric paths are allowed to survive */
|
|
}
|
|
|
|
/* Capture the best path metric */
|
|
if( path.getPathMetric() > mBestMetric )
|
|
{
|
|
mBestMetric = path.getPathMetric();
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Path is a sequence of nodes where the starting node always contains
|
|
* tribit 0 and constellation 0. Path maintains a running path metric
|
|
* derived from the sum of each of the node's branch metrics.
|
|
*/
|
|
public class Path
|
|
{
|
|
private boolean mSurvivor = true;
|
|
private int mPathMetric = 0;
|
|
private ArrayList<Node> mNodes = new ArrayList<Node>();
|
|
|
|
public Path( Node first )
|
|
{
|
|
mNodes.add( first );
|
|
|
|
mPathMetric = first.getBranchMetric();
|
|
|
|
mPathMetrics.evalute( this );
|
|
}
|
|
|
|
public Path( ArrayList<Node> nodes, int metric )
|
|
{
|
|
mNodes = nodes;
|
|
mPathMetric = metric;
|
|
}
|
|
|
|
public List<Node> getNodes()
|
|
{
|
|
return mNodes;
|
|
}
|
|
|
|
public int getBitErrorCount()
|
|
{
|
|
return ( getLastNode().getIndex() * 4 ) - mPathMetric;
|
|
}
|
|
|
|
public void setSurvivor( boolean survivor )
|
|
{
|
|
mSurvivor = survivor;
|
|
}
|
|
|
|
public boolean isSurvivor()
|
|
{
|
|
return mSurvivor;
|
|
}
|
|
|
|
public boolean isDead()
|
|
{
|
|
return !mSurvivor;
|
|
}
|
|
|
|
@SuppressWarnings( "unchecked" )
|
|
public Path copyOf()
|
|
{
|
|
return new Path( (ArrayList<Node>)mNodes.clone(), mPathMetric );
|
|
}
|
|
|
|
/**
|
|
* Adds the constellation and returns any new paths created as a result
|
|
* of the constellation being detected as an errant constellation.
|
|
*/
|
|
public void add( Con con )
|
|
{
|
|
Node current = getLastNode();
|
|
|
|
Tribit input = INPUT_FROM_CONSTELLATION_MAP
|
|
.get( con )[ current.getState().getValue() ];
|
|
|
|
/* A non-null input tribit from the lookup table is valid, otherwise
|
|
* create 8 alternate branches from the original path to explore
|
|
* where the error occurred. */
|
|
if( input != null )
|
|
{
|
|
add( new Node( mNodes.size(), input, con ) );
|
|
}
|
|
else
|
|
{
|
|
/* Create new paths for each of the valid constellations that
|
|
* correspond to the parity of the current state tribit */
|
|
Con[] constellations = current.getState().getType() ==
|
|
Type.EVEN ? EVEN_CONSTELLATIONS : ODD_CONSTELLATIONS;
|
|
|
|
for( int x = 0; x < 8; x++ )
|
|
{
|
|
/* Get the input tribit corresponding to the state tribit
|
|
* and constellation combination */
|
|
Tribit tribit = INPUT_FROM_CONSTELLATION_MAP
|
|
.get( constellations[ x ] )[ current.getState().getValue() ];
|
|
|
|
Node candidate = new Node( mNodes.size(), tribit, constellations[ x ] );
|
|
|
|
/* If the current path metric plus the candidate node's
|
|
* branch metric is equal or better than the current best
|
|
* metric for the given tribit, add it as a potential path,
|
|
* otherwise discard it */
|
|
if( this.getPathMetric() + candidate.getBranchMetric() >=
|
|
mPathMetrics.getMetric( tribit ) )
|
|
{
|
|
/* If this is the final constellation of the set, simply
|
|
* add it to the existing path ... */
|
|
if( x == 7 )
|
|
{
|
|
add( candidate );
|
|
}
|
|
/* ... otherwise create a copy of the current path */
|
|
else
|
|
{
|
|
Path path = this.copyOf();
|
|
|
|
path.add( candidate );
|
|
|
|
mNewPaths.add( path );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public void add( Node node )
|
|
{
|
|
mNodes.add( node );
|
|
|
|
mPathMetric += node.getBranchMetric();
|
|
|
|
mPathMetrics.evalute( this );
|
|
}
|
|
|
|
public int getPathMetric()
|
|
{
|
|
return mPathMetric;
|
|
}
|
|
|
|
public Node getLastNode()
|
|
{
|
|
return mNodes.get( mNodes.size() - 1 );
|
|
}
|
|
|
|
public String toString()
|
|
{
|
|
StringBuilder sb = new StringBuilder();
|
|
|
|
sb.append( "PATH [" );
|
|
sb.append( mNodes.size() );
|
|
sb.append( "-" );
|
|
sb.append( mPathMetric );
|
|
sb.append( "] " );
|
|
|
|
for( Node node: mNodes )
|
|
{
|
|
sb.append( node.getState().name() );
|
|
sb.append( "/" );
|
|
sb.append( node.getConstellation().name() );
|
|
sb.append( " " );
|
|
}
|
|
|
|
sb.append( mSurvivor ? "SURVIVOR" : "DEAD" );
|
|
|
|
return sb.toString();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Trellis node containing the constellation used to produce the node and
|
|
* the input tribit for the constellation.
|
|
*/
|
|
public class Node
|
|
{
|
|
private int mIndex;
|
|
private int mBranchMetric;
|
|
private Tribit mTribit;
|
|
private Con mConstellation;
|
|
|
|
public Node( int index, Tribit tribit, Con constellation )
|
|
{
|
|
mIndex = index;
|
|
mTribit = tribit;
|
|
mConstellation = constellation;
|
|
|
|
if( mIndex > 0 )
|
|
{
|
|
Con transmitted = mTransmittedConstellations.get( index - 1 );
|
|
|
|
if( transmitted == null )
|
|
{
|
|
throw new IllegalArgumentException( "Cannot calculate "
|
|
+ "metric - transmitted constellation symbol is null" );
|
|
}
|
|
|
|
mBranchMetric = transmitted.getMetricTo( constellation );
|
|
}
|
|
}
|
|
|
|
public int getIndex()
|
|
{
|
|
return mIndex;
|
|
}
|
|
|
|
public Tribit getState()
|
|
{
|
|
return mTribit;
|
|
}
|
|
|
|
public Con getConstellation()
|
|
{
|
|
return mConstellation;
|
|
}
|
|
|
|
public int getBranchMetric()
|
|
{
|
|
return mBranchMetric;
|
|
}
|
|
|
|
public String toString()
|
|
{
|
|
StringBuilder sb = new StringBuilder();
|
|
|
|
sb.append( "Node " );
|
|
sb.append( mIndex );
|
|
sb.append( " Metric:" );
|
|
sb.append( mBranchMetric );
|
|
sb.append( " Con:" );
|
|
sb.append( mConstellation.name() );
|
|
sb.append( " State:" );
|
|
if( mTribit == null )
|
|
{
|
|
sb.append( "null" );
|
|
}
|
|
else
|
|
{
|
|
sb.append( mTribit.name() );
|
|
}
|
|
|
|
return sb.toString();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Constellation Type. Constellations are classified as even or add based
|
|
* on the the composition/comparison of the transmitted (representative)
|
|
* dibits:
|
|
*
|
|
* EVEN: - first dibit differs from second dibit in exactly one bit position
|
|
*
|
|
* ODD: - first dibit differs from second dibit in zero or two bit positions
|
|
* following three patterns:
|
|
* 1) repeat (10 10), mirror (01 10), or invert(00 11)
|
|
*/
|
|
public enum Type { EVEN, ODD };
|
|
|
|
/**
|
|
* Constellations, ordered by transmitted value. Transmitted value is the
|
|
* value of the dibit pair that represents the constellation when transmitted.
|
|
*/
|
|
public enum Con
|
|
{
|
|
CB( 0 ),
|
|
CC( 1 ),
|
|
C0( 2 ),
|
|
C7( 3 ),
|
|
CE( 4 ),
|
|
C9( 5 ),
|
|
C5( 6 ),
|
|
C2( 7 ),
|
|
CA( 8 ),
|
|
CD( 9 ),
|
|
C1( 10 ),
|
|
C6( 11 ),
|
|
CF( 12 ),
|
|
C8( 13 ),
|
|
C4( 14 ),
|
|
C3( 15 );
|
|
|
|
private int mTransmittedValue;
|
|
|
|
private Con( int transmittedValue )
|
|
{
|
|
mTransmittedValue = transmittedValue;
|
|
}
|
|
|
|
public int getTransmittedValue()
|
|
{
|
|
return mTransmittedValue;
|
|
}
|
|
|
|
public static Con fromTransmittedValue( int value )
|
|
{
|
|
if( 0 <= value && value <= 15 )
|
|
{
|
|
return values()[ value ];
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public static Con fromTransmittedDibits( Dibit left, Dibit right )
|
|
{
|
|
return fromTransmittedValue( left.getHighValue() + right.getLowValue() );
|
|
}
|
|
|
|
/**
|
|
* Returns the metric or hamming distance to the other constellation
|
|
* using the values from the constellation costs table. A perfect match
|
|
* has a metric of 4 and a complete miss has a value of 0.
|
|
*/
|
|
public int getMetricTo( Con other )
|
|
{
|
|
return CONSTELLATION_METRICS[ getTransmittedValue() ][ other.getTransmittedValue() ];
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Tribit. Used to define both the state and the input bit sequences that
|
|
* produce the transmitted constellation bit sequences. Each tribit is
|
|
* either even or odd valued producing a direct correlation to the types
|
|
* of valid constellations containing the tribit as the state tribit.
|
|
*/
|
|
public enum Tribit
|
|
{
|
|
T0( 0, Type.EVEN ),
|
|
T1( 1, Type.EVEN ),
|
|
T2( 2, Type.ODD ),
|
|
T3( 3, Type.ODD ),
|
|
T4( 4, Type.ODD ),
|
|
T5( 5, Type.ODD ),
|
|
T6( 6, Type.EVEN ),
|
|
T7( 7, Type.EVEN );
|
|
|
|
private int mValue;
|
|
private Type mType;
|
|
|
|
private Tribit( int value, Type type )
|
|
{
|
|
mValue = value;
|
|
mType = type;
|
|
}
|
|
|
|
public Type getType()
|
|
{
|
|
return mType;
|
|
}
|
|
|
|
public int getValue()
|
|
{
|
|
return mValue;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Test harness
|
|
*/
|
|
public static void main( String[] args )
|
|
{
|
|
String raw = "12:58:51.635 DEBUG dsp.fsk.P25MessageFramer - AFTER DEINTERLEAVE: 00100110000011001010010101111101000101010110100111111100101011000111011011000110000000000000000000000111111101011000001000001111000110000000000010101011110110010000000001011011000000010100011110110001000010100100011111000000000100000000000000000000010000000000001100000000000000011010101010101010101010100010001010000110010111111101101000001010000010100000101000001010000010100000101000001010000010100000101000001010000010100000101000001010000010100000010111001001000011100101011010101010101001010011";
|
|
|
|
BinaryMessage message = BinaryMessage.load( raw );
|
|
|
|
mLog.debug( "MSG: " + message.toString() );
|
|
|
|
Trellis_3_4_Rate t = new Trellis_3_4_Rate();
|
|
t.decode( message, 0, 196 );
|
|
|
|
mLog.debug( "DEC: " + message.toString() );
|
|
|
|
mLog.debug( "Finished!" );
|
|
}
|
|
|
|
|
|
}
|