Initial commit of redesigned internal processing and audio management.

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
This commit is contained in:
denny
2015-09-01 07:10:26 -04:00
parent 538a299246
commit f4d8ea281d
570 changed files with 21578 additions and 17012 deletions

View File

@@ -0,0 +1,534 @@
package module.decode.p25;
import java.util.ArrayList;
import java.util.Random;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import bits.BinaryMessage;
import dsp.symbol.Dibit;
public class Trellis_1_2_Rate
{
public final static int MAX_ERROR_THRESHOLD = 7;
private final static Logger mLog =
LoggerFactory.getLogger( Trellis_1_2_Rate.class );
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 Trellis_1_2_Rate()
{
/* 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();
}
private String constellationsToString( String label )
{
StringBuilder sb = new StringBuilder();
sb.append( label );
for( ConstellationNode node: mConstellationNodes )
{
sb.append( " " + node.getConstellation().name() );
}
return sb.toString();
}
private String inputsToString()
{
StringBuilder sb = new StringBuilder();
sb.append( " INPUTS: " );
for( ConstellationNode node: mConstellationNodes )
{
sb.append( " " );
sb.append( ( node.getConstellation().getInput().getBit1() ? "1" : "0" ) );
sb.append( ( node.getConstellation().getInput().getBit2() ? "1" : "0" ) );
}
return sb.toString();
}
public boolean decode( BinaryMessage message, int start, int end )
{
/* load each of the nodes with de-interleaved constellations */
for( int index = 0; index < 49; index++ )
{
Constellation c = getConstellation( message, start + index * 4 );
mConstellationNodes.get( index ).setConstellation( c );
}
/* test to see if constellations are correct - otherwise correct them */
ConstellationNode firstNode = mConstellationNodes.get( 0 );
int errorCount = firstNode.getErrorCount();
if( errorCount > 0 )
{
if( errorCount < MAX_ERROR_THRESHOLD )
{
/* repair the errors */
firstNode.correctTo( Dibit.D00_PLUS_1 );
}
else
{
return false;
}
}
/* clear constellations from original message */
message.clear( start, end );
/* replace with decoded values from the constellation nodes */
for( int index = 0; index < 49; index++ )
{
ConstellationNode node = mConstellationNodes.get( index );
Dibit input = node.getInputDibit();
if( input.getBit1() )
{
message.set( start + ( index * 2 ) );
}
if( input.getBit2() )
{
message.set( start + ( index * 2 ) + 1 );
}
}
return true;
}
private Constellation getConstellation( BinaryMessage message, int index )
{
int transmittedValue = 0;
for( int x = 0; x < 4; x++ )
{
if( message.get( index + x ) )
{
transmittedValue += ( 1 << ( 3 - x ) );
}
}
return Constellation.fromTransmittedValue( transmittedValue );
}
public class ConstellationNode
{
private ConstellationNode mConnectedNode;
private Constellation mConstellation;
private boolean mCorrect;
public ConstellationNode()
{
}
public void dispose()
{
mConnectedNode = null;
}
public Constellation getConstellation()
{
return mConstellation;
}
public Dibit getInputDibit()
{
return mConstellation.getInput();
}
public boolean hasStateDibit( Dibit dibit )
{
return mConstellation.getState() == dibit;
}
/**
* 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 stateDibit to use for the left side.
*/
public void correctTo( Dibit stateDibit )
{
if( mCorrect && mConstellation.getState() == stateDibit )
{
return;
}
if( isCurrentConnectionCorrect() )
{
mConstellation = Constellation.fromStateAndInputDibits( stateDibit,
mConstellation.getInput() );
mCorrect = true;
if( mConnectedNode != null )
{
/* the next node's state was this node's input */
mConnectedNode.correctTo( mConstellation.getInput() );
}
}
else
{
Constellation cheapestConstellation = mConstellation;
int cheapestCost = 100; //arbitrary
for( Dibit testInput: Dibit.values() )
{
Constellation testConstellation = Constellation
.fromStateAndInputDibits( stateDibit, testInput );
int testCost = mConstellation.costTo( testConstellation ) +
mConnectedNode.costTo( testInput );
if( testCost < cheapestCost )
{
cheapestCost = testCost;
cheapestConstellation = testConstellation;
}
}
mConstellation = cheapestConstellation;
mConnectedNode.correctTo( mConstellation.getInput() );
mCorrect = true;
}
}
/**
* Calculates the cost (hamming distance) of using the argument as the
* state dibit for the current node, and recursively finding the
* cheapest corresponding input dibit.
*
* @param stateTest
* @return
*/
public int costTo( Dibit stateTest )
{
if( isCurrentConnectionCorrect() )
{
Constellation c = Constellation.fromStateAndInputDibits(
stateTest, mConstellation.getInput() );
return mConstellation.costTo( c );
}
else
{
int cheapestCost = 100; //arbitrary
for( Dibit inputTest: Dibit.values() )
{
Constellation constellationTest =
Constellation.fromStateAndInputDibits( stateTest, inputTest );
int cost = mConnectedNode.costTo( inputTest ) +
mConstellation.costTo( constellationTest );
if( cost < cheapestCost )
{
cheapestCost = cost;
}
}
return cheapestCost;
}
}
/**
* Indicates if the immediate connection to the right is correct
*/
public boolean isCurrentConnectionCorrect()
{
return ( mConnectedNode == null ||
mConstellation.getInput() == mConnectedNode.getStateDibit() );
}
/**
* 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 int getErrorCount()
{
if( mConnectedNode == null )
{
mCorrect = true;
return 0;
}
mCorrect = mConstellation.getInput() == mConnectedNode.getStateDibit();
return mConnectedNode.getErrorCount() + ( mCorrect ? 0 : 1 );
}
public Dibit getStateDibit()
{
return mConstellation.getState();
}
public void setConstellation( Constellation constellation )
{
mConstellation = constellation;
mCorrect = false;
}
public void connect( ConstellationNode node )
{
mConnectedNode = node;
}
}
public enum Constellation
{
/* State (previous), Input (next), Transmit 1, Transmit 2 */
CB( Dibit.D01_PLUS_3, Dibit.D01_PLUS_3, Dibit.D00_PLUS_1, Dibit.D00_PLUS_1, 0 ),
CC( Dibit.D00_PLUS_1, Dibit.D10_MINUS_1, Dibit.D00_PLUS_1, Dibit.D01_PLUS_3, 1 ),
C0( Dibit.D00_PLUS_1, Dibit.D00_PLUS_1, Dibit.D00_PLUS_1, Dibit.D10_MINUS_1, 2 ),
C7( Dibit.D01_PLUS_3, Dibit.D11_MINUS_3, Dibit.D00_PLUS_1, Dibit.D11_MINUS_3, 3 ),
CE( Dibit.D10_MINUS_1, Dibit.D11_MINUS_3, Dibit.D01_PLUS_3, Dibit.D00_PLUS_1, 4 ),
C9( Dibit.D11_MINUS_3, Dibit.D00_PLUS_1, Dibit.D01_PLUS_3, Dibit.D01_PLUS_3, 5 ),
C5( Dibit.D11_MINUS_3, Dibit.D10_MINUS_1, Dibit.D01_PLUS_3, Dibit.D10_MINUS_1, 6 ),
C2( Dibit.D10_MINUS_1, Dibit.D01_PLUS_3, Dibit.D01_PLUS_3, Dibit.D11_MINUS_3, 7 ),
CA( Dibit.D11_MINUS_3, Dibit.D11_MINUS_3, Dibit.D10_MINUS_1, Dibit.D00_PLUS_1, 8 ),
CD( Dibit.D10_MINUS_1, Dibit.D00_PLUS_1, Dibit.D10_MINUS_1, Dibit.D01_PLUS_3, 9 ),
C1( Dibit.D10_MINUS_1, Dibit.D10_MINUS_1, Dibit.D10_MINUS_1, Dibit.D10_MINUS_1, 10 ),
C6( Dibit.D11_MINUS_3, Dibit.D01_PLUS_3, Dibit.D10_MINUS_1, Dibit.D11_MINUS_3, 11 ),
CF( Dibit.D00_PLUS_1, Dibit.D01_PLUS_3, Dibit.D11_MINUS_3, Dibit.D00_PLUS_1, 12 ),
C8( Dibit.D01_PLUS_3, Dibit.D10_MINUS_1, Dibit.D11_MINUS_3, Dibit.D01_PLUS_3, 13 ),
C4( Dibit.D01_PLUS_3, Dibit.D00_PLUS_1, Dibit.D11_MINUS_3, Dibit.D10_MINUS_1, 14 ),
C3( Dibit.D00_PLUS_1, Dibit.D11_MINUS_3, Dibit.D11_MINUS_3, Dibit.D11_MINUS_3, 15 );
private Dibit mStateDibit;
private Dibit mInputDibit;
private Dibit mTransmitDibit1;
private Dibit mTransmitDibit2;
private int mTransmittedValue;
private Constellation( Dibit stateDibit,
Dibit inputDibit,
Dibit transmitDibit1,
Dibit transmitDibit2,
int transmittedValue )
{
mStateDibit = stateDibit;
mInputDibit = inputDibit;
mTransmitDibit1 = transmitDibit1;
mTransmitDibit2 = transmitDibit2;
mTransmittedValue = transmittedValue;
}
public Dibit getState()
{
return mStateDibit;
}
public Dibit getInput()
{
return mInputDibit;
}
public Dibit getTransmitDibit1()
{
return mTransmitDibit1;
}
public Dibit getTransmitDibit2()
{
return mTransmitDibit2;
}
public int getTransmittedValue()
{
return mTransmittedValue;
}
public static Constellation fromTransmittedValue( int value )
{
if( 0 <= value && value <= 15 )
{
return values()[ value ];
}
return null;
}
public static Constellation fromTransmittedDibits( Dibit left, Dibit right )
{
return fromTransmittedValue( left.getHighValue() + right.getLowValue() );
}
public static Constellation fromStateAndInputDibits( Dibit state, Dibit input )
{
switch( state )
{
case D00_PLUS_1:
switch( input )
{
case D00_PLUS_1:
return C0;
case D01_PLUS_3:
return CF;
case D10_MINUS_1:
return CC;
case D11_MINUS_3:
return C3;
}
break;
case D01_PLUS_3:
switch( input )
{
case D00_PLUS_1:
return C4;
case D01_PLUS_3:
return CB;
case D10_MINUS_1:
return C8;
case D11_MINUS_3:
return C7;
}
break;
case D10_MINUS_1:
switch( input )
{
case D00_PLUS_1:
return CD;
case D01_PLUS_3:
return C2;
case D10_MINUS_1:
return C1;
case D11_MINUS_3:
return CE;
}
break;
case D11_MINUS_3:
switch( input )
{
case D00_PLUS_1:
return C9;
case D01_PLUS_3:
return C6;
case D10_MINUS_1:
return C5;
case D11_MINUS_3:
return CA;
}
break;
}
/* Should never get to here */
return C0;
}
/**
* 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[ getTransmittedValue() ][ other.getTransmittedValue() ];
}
}
/**
* Test harness
*/
public static void main( String[] args )
{
Random random = new Random();
BinaryMessage buffer = new BinaryMessage( 196 );
try
{
for( int x = 0; x < 196; x++ )
{
int number = random.nextInt( 2 );
if( number == 1 )
{
buffer.add( false );
}
else
{
buffer.add( true );
}
}
}
catch( Exception e )
{
e.printStackTrace();
}
mLog.debug( " BUFFER: " + buffer.toString() );
Trellis_1_2_Rate t = new Trellis_1_2_Rate();
t.decode( buffer, 0, 196 );
mLog.debug( " DECODED: " + buffer.toString() );
mLog.debug( "Finished!" );
}
}