mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-15 01:57:39 -05:00
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:
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 );
|
||||||
|
|||||||
@@ -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 );
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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" );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user