P25 Audio Decoder - changed sync pattern matcher to allow up to 2 bit mismatches on audio message frames when in the middle of a call.

Encrypted audio is still going to the audio output -- have to add error detection and correction to the ldu link control messages before I can use the encryption flag as a valid sentinel value.
This commit is contained in:
dennis.sheirer
2015-04-03 15:36:50 +00:00
parent 023553c3bf
commit 71a15707db
5 changed files with 78 additions and 12 deletions

View File

@@ -23,7 +23,7 @@ package bits;
* *
* Note: works for sync patterns up to 63 bits (integer size - 1 ) long. * Note: works for sync patterns up to 63 bits (integer size - 1 ) long.
* If a 64 bit or larger sync pattern is applied, then the wrapping feature * If a 64 bit or larger sync pattern is applied, then the wrapping feature
* of the Integer.rotate methods will wrap the MSB around to the LSB and * of the Long.rotate methods will wrap the MSB around to the LSB and
* corrupt the matching value. * corrupt the matching value.
* *
*/ */
@@ -32,6 +32,8 @@ public class SyncPatternMatcher
private long mBits = 0; private long mBits = 0;
private long mMask = 0; private long mMask = 0;
private long mSync = 0; private long mSync = 0;
private boolean mSoftMode = false;
private int mSoftModeErrorThreshold = 0;
public SyncPatternMatcher( boolean[] syncPattern ) public SyncPatternMatcher( boolean[] syncPattern )
{ {
@@ -54,6 +56,21 @@ public class SyncPatternMatcher
mMask = getMask( sync ); mMask = getMask( sync );
} }
/**
* Constructs the sync pattern matcher and defines the soft mode maximum
* number of bit errors to allow in the pattern match.
*
* @param sync pattern
* @param softModeErrorThreshold - maximum bit errors (ie pattern mismatch
* positions) when considering a sync match
*/
public SyncPatternMatcher( long sync, int softModeErrorThreshold )
{
this( sync );
mSoftModeErrorThreshold = softModeErrorThreshold;
}
/** /**
* Generates an all ones mask corresponding to the sync pattern * Generates an all ones mask corresponding to the sync pattern
*/ */
@@ -91,6 +108,25 @@ public class SyncPatternMatcher
*/ */
public boolean matches() public boolean matches()
{ {
return ( mBits == mSync ); if( mSoftMode )
{
long difference = mBits ^ mSync;
return difference == 0 ||
Long.bitCount( difference ) <= mSoftModeErrorThreshold;
}
else
{
return ( mBits == mSync );
}
}
/**
* Sets the sync pattern matcher in soft mode which allows up to the soft
* mode threshold number of bit errors when considering a sync pattern match
*/
public void setSoftMode( boolean softMode )
{
mSoftMode = softMode;
} }
} }

View File

@@ -88,7 +88,7 @@ public class P25Decoder extends Decoder
/* Message framers and handlers */ /* Message framers and handlers */
private P25MessageFramer mNormalFramer; private P25MessageFramer mNormalFramer;
private P25MessageFramer mInvertedFramer; // private P25MessageFramer mInvertedFramer;
private P25MessageProcessor mMessageProcessor; private P25MessageProcessor mMessageProcessor;
/* Audio */ /* Audio */
@@ -114,9 +114,9 @@ public class P25Decoder extends Decoder
FrameSync.P25_PHASE1.getSync(), 64, false, mAliasList ); FrameSync.P25_PHASE1.getSync(), 64, false, mAliasList );
mNormalFramer.setListener( mMessageProcessor ); mNormalFramer.setListener( mMessageProcessor );
mInvertedFramer = new P25MessageFramer( // mInvertedFramer = new P25MessageFramer(
FrameSync.P25_PHASE1_INVERTED.getSync(), 64, true, mAliasList ); // FrameSync.P25_PHASE1_INVERTED.getSync(), 64, true, mAliasList );
mInvertedFramer.setListener( mMessageProcessor ); // mInvertedFramer.setListener( mMessageProcessor );
/* Setup demodulation chains based on sample type (real or complex) and /* Setup demodulation chains based on sample type (real or complex) and
* modulation (C4FM or CQPSK) */ * modulation (C4FM or CQPSK) */
@@ -139,7 +139,7 @@ public class P25Decoder extends Decoder
mCQPSKDemodulator.setListener( mCQPSKSlicer ); mCQPSKDemodulator.setListener( mCQPSKSlicer );
mCQPSKSlicer.addListener( mNormalFramer ); mCQPSKSlicer.addListener( mNormalFramer );
mCQPSKSlicer.addListener( mInvertedFramer ); // mCQPSKSlicer.addListener( mInvertedFramer );
} }
else /* C4FM */ else /* C4FM */
{ {
@@ -172,7 +172,7 @@ public class P25Decoder extends Decoder
mSymbolFilter.setListener( mC4FMSlicer ); mSymbolFilter.setListener( mC4FMSlicer );
mC4FMSlicer.addListener( mNormalFramer ); mC4FMSlicer.addListener( mNormalFramer );
mC4FMSlicer.addListener( mInvertedFramer ); // mC4FMSlicer.addListener( mInvertedFramer );
} }
mAudioOutput = new P25AudioOutput( resourceManager ); mAudioOutput = new P25AudioOutput( resourceManager );

View File

@@ -34,6 +34,8 @@ import edac.CRCP25;
public class P25MessageFramer implements Listener<Dibit> public class P25MessageFramer implements Listener<Dibit>
{ {
private static final int SYNC_PATTERN_MISMATCH_THRESHOLD = 2;
private final static Logger mLog = private final static Logger mLog =
LoggerFactory.getLogger( P25MessageFramer.class ); LoggerFactory.getLogger( P25MessageFramer.class );
@@ -80,7 +82,7 @@ public class P25MessageFramer implements Listener<Dibit>
boolean inverted, boolean inverted,
AliasList aliasList ) AliasList aliasList )
{ {
mMatcher = new SyncPatternMatcher( sync ); mMatcher = new SyncPatternMatcher( sync, SYNC_PATTERN_MISMATCH_THRESHOLD );
mInverted = inverted; mInverted = inverted;
mAliasList = aliasList; mAliasList = aliasList;
@@ -256,10 +258,12 @@ public class P25MessageFramer implements Listener<Dibit>
break; break;
case HDU: case HDU:
mComplete = true; mComplete = true;
mMatcher.setSoftMode( true );
dispatch( new HDUMessage( mMessage.copy(), mDUID, mAliasList ) ); dispatch( new HDUMessage( mMessage.copy(), mDUID, mAliasList ) );
break; break;
case LDU1: case LDU1:
mComplete = true; mComplete = true;
mMatcher.setSoftMode( true );
LDU1Message ldu1 = new LDU1Message( mMessage.copy(), LDU1Message ldu1 = new LDU1Message( mMessage.copy(),
mDUID, mAliasList ); mDUID, mAliasList );
@@ -269,9 +273,12 @@ public class P25MessageFramer implements Listener<Dibit>
break; break;
case LDU2: case LDU2:
mComplete = true; mComplete = true;
mMatcher.setSoftMode( true );
dispatch( new LDU2Message( mMessage.copy(), mDUID, mAliasList ) ); dispatch( new LDU2Message( mMessage.copy(), mDUID, mAliasList ) );
break; break;
case PDU0: case PDU0:
mMatcher.setSoftMode( false );
/* Remove interleaving */ /* Remove interleaving */
P25Interleave.deinterleaveData( mMessage, PDU0_BEGIN, PDU0_END ); P25Interleave.deinterleaveData( mMessage, PDU0_BEGIN, PDU0_END );
@@ -390,6 +397,8 @@ public class P25MessageFramer implements Listener<Dibit>
mComplete = true; mComplete = true;
break; break;
case PDUC: case PDUC:
mMatcher.setSoftMode( true );
/* De-interleave the latest block*/ /* De-interleave the latest block*/
P25Interleave.deinterleaveData( mMessage, P25Interleave.deinterleaveData( mMessage,
mMessage.size() - 196, mMessage.size() ); mMessage.size() - 196, mMessage.size() );
@@ -438,10 +447,12 @@ public class P25MessageFramer implements Listener<Dibit>
} }
break; break;
case TDU: case TDU:
mMatcher.setSoftMode( true );
dispatch( new TDUMessage( mMessage.copy(), mDUID, mAliasList ) ); dispatch( new TDUMessage( mMessage.copy(), mDUID, mAliasList ) );
mComplete = true; mComplete = true;
break; break;
case TDULC: case TDULC:
mMatcher.setSoftMode( true );
TDULinkControlMessage tdulc = new TDULinkControlMessage( TDULinkControlMessage tdulc = new TDULinkControlMessage(
mMessage.copy(), mDUID, mAliasList ); mMessage.copy(), mDUID, mAliasList );
@@ -452,6 +463,7 @@ public class P25MessageFramer implements Listener<Dibit>
mComplete = true; mComplete = true;
break; break;
case TSBK1: case TSBK1:
mMatcher.setSoftMode( true );
/* Remove interleaving */ /* Remove interleaving */
P25Interleave.deinterleaveData( mMessage, TSBK_BEGIN, TSBK_END ); P25Interleave.deinterleaveData( mMessage, TSBK_BEGIN, TSBK_END );

View File

@@ -7,6 +7,9 @@ import java.util.concurrent.atomic.AtomicBoolean;
import javax.sound.sampled.AudioInputStream; import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem; import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Control;
import javax.sound.sampled.FloatControl;
import javax.sound.sampled.FloatControl.Type;
import javax.sound.sampled.LineUnavailableException; import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.SourceDataLine; import javax.sound.sampled.SourceDataLine;
@@ -20,7 +23,6 @@ import audio.AudioType;
import audio.IAudioOutput; import audio.IAudioOutput;
import audio.IAudioTypeListener; import audio.IAudioTypeListener;
import audio.SquelchListener; import audio.SquelchListener;
import audio.SquelchListener.SquelchState;
import controller.ResourceManager; import controller.ResourceManager;
import controller.ThreadPoolManager; import controller.ThreadPoolManager;
import controller.ThreadPoolManager.ThreadType; import controller.ThreadPoolManager.ThreadType;
@@ -145,6 +147,17 @@ public class P25AudioOutput implements IAudioOutput, IAudioTypeListener,
/* Open the audio line with room for two buffers */ /* Open the audio line with room for two buffers */
mOutput.open( IMBEAudioFormat.PCM_SIGNED_8KHZ_16BITS, mOutput.open( IMBEAudioFormat.PCM_SIGNED_8KHZ_16BITS,
PROCESSED_AUDIO_FRAME_SIZE * 2 ); PROCESSED_AUDIO_FRAME_SIZE * 2 );
Control[] controls = mOutput.getControls();
FloatControl gain = (FloatControl)mOutput
.getControl( FloatControl.Type.MASTER_GAIN );
if( gain != null )
{
// gain.setValue( 4.0f );
}
mCanProcessAudio = true; mCanProcessAudio = true;
} }
else else

View File

@@ -44,19 +44,24 @@ public class LDU2Message extends LDUMessage
{ {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
sb.append( super.getMessageStub() );
Encryption encryption = getEncryption(); Encryption encryption = getEncryption();
if( encryption != Encryption.UNENCRYPTED ) if( encryption != Encryption.UNENCRYPTED )
{ {
sb.append( "ENCRYPTION:" ); sb.append( "NAC:" );
sb.append( getNAC() );
sb.append( " " );
sb.append( getDUID().getLabel() );
sb.append( " ENCRYPTED VOICE:" );
sb.append( encryption.name() ); sb.append( encryption.name() );
sb.append( " KEY ID:" + getKeyID() ); sb.append( " KEY ID:" + getKeyID() );
sb.append( " MSG INDICATOR:" + getMessageIndicator() ); sb.append( " MSG INDICATOR:" + getMessageIndicator() );
} }
else else
{ {
sb.append( super.getMessageStub() );
sb.append( "UNENCRYPTED" ); sb.append( "UNENCRYPTED" );
} }