P25 work in progress

This commit is contained in:
dennis.sheirer
2014-12-23 22:43:55 +00:00
parent d15ff407bd
commit 86cb5b2f4b
20 changed files with 865 additions and 266 deletions

View File

@@ -220,7 +220,9 @@ public class CRCP25
0x00080000l, 0x00100000l, 0x00200000l, 0x00400000l, 0x00800000l,
0x01000000l, 0x02000000l, 0x04000000l, 0x08000000l, 0x10000000l,
0x20000000l, 0x40000000l, 0x80000000l
};
};
/**
* Performs CRC check and corrects single-bit errors against the message.

View File

@@ -67,7 +67,7 @@ public enum DecoderType
DecoderType.LTR_STANDARD,
DecoderType.MPT1327,
DecoderType.NBFM,
// DecoderType.P25_PHASE1,
DecoderType.P25_PHASE1,
DecoderType.PASSPORT );
}

File diff suppressed because it is too large Load Diff

View File

@@ -19,6 +19,7 @@ package decode.p25;
import instrument.Instrumentable;
import instrument.tap.Tap;
import instrument.tap.stream.DibitTap;
import instrument.tap.stream.FloatTap;
import java.util.ArrayList;
@@ -36,9 +37,9 @@ import dsp.agc.RealAutomaticGainControl;
import dsp.filter.ComplexFIRFilter;
import dsp.filter.FilterFactory;
import dsp.filter.Window.WindowType;
import dsp.fsk.P25MessageFramer;
import dsp.fsk.C4FMSlicer;
import dsp.fsk.C4FMSymbolFilter;
import dsp.fsk.P25MessageFramer;
import dsp.nbfm.FMDiscriminator;
public class P25Decoder extends Decoder implements Instrumentable
@@ -49,6 +50,7 @@ public class P25Decoder extends Decoder implements Instrumentable
private static final String INSTRUMENT_INPUT_TO_AGC = "Tap Point: Input to AGC";
private static final String INSTRUMENT_AGC_TO_SYMBOL_FILTER = "Tap Point: AGC to Symbol Filter";
private static final String INSTRUMENT_SYMBOL_FILTER_TO_SLICER = "Tap Point: Symbol Filter to Slicer";
private static final String INSTRUMENT_SLICER_OUTPUT = "Tap Point: Slicer Output";
private List<Tap> mAvailableTaps;
private ComplexFIRFilter mBasebandFilter = new ComplexFIRFilter(
@@ -125,6 +127,7 @@ public class P25Decoder extends Decoder implements Instrumentable
mAvailableTaps.add( new FloatTap( INSTRUMENT_INPUT_TO_AGC, 0, 1.0f ) );
mAvailableTaps.add( new FloatTap( INSTRUMENT_AGC_TO_SYMBOL_FILTER, 0, 1.0f ) );
mAvailableTaps.add( new FloatTap( INSTRUMENT_SYMBOL_FILTER_TO_SLICER, 0, 0.1f ) );
mAvailableTaps.add( new DibitTap( INSTRUMENT_SLICER_OUTPUT, 0, 0.1f ) );
mAvailableTaps.addAll( mSymbolFilter.getTaps() );
}
@@ -155,6 +158,10 @@ public class P25Decoder extends Decoder implements Instrumentable
mSymbolFilter.setListener( symbolSlicer );
symbolSlicer.setListener( mSlicer );
break;
case INSTRUMENT_SLICER_OUTPUT:
DibitTap slicer = (DibitTap)tap;
mSlicer.addListener( slicer );
break;
}
}
@@ -176,6 +183,10 @@ public class P25Decoder extends Decoder implements Instrumentable
case INSTRUMENT_SYMBOL_FILTER_TO_SLICER:
mSymbolFilter.setListener( mSlicer );
break;
case INSTRUMENT_SLICER_OUTPUT:
DibitTap slicerTap = (DibitTap)tap;
mSlicer.removeListener( slicerTap );
break;
}
}

View File

@@ -0,0 +1,44 @@
package decode.p25.message.hdu;
import alias.AliasList;
import bits.BitSetBuffer;
import crc.CRC;
import decode.p25.message.P25Message;
import decode.p25.reference.DataUnitID;
public class HDUMessage extends P25Message
{
public HDUMessage( BitSetBuffer message, DataUnitID duid,
AliasList aliasList )
{
super( message, duid, aliasList );
/* NID CRC is checked in the message framer, thus a constructed message
* means it passed the CRC */
mCRC = new CRC[ 2 ];
mCRC[ 0 ] = CRC.PASSED;
}
@Override
public String getMessage()
{
StringBuilder sb = new StringBuilder();
sb.append( "NAC:" );
sb.append( getNAC() );
sb.append( " " );
sb.append( getDUID().getLabel() );
sb.append( " " );
sb.append( mMessage.toString() );
return sb.toString();
}
/* Temporary override */
public boolean isValid()
{
return true;
}
}

View File

@@ -0,0 +1,44 @@
package decode.p25.message.ldu;
import alias.AliasList;
import bits.BitSetBuffer;
import crc.CRC;
import decode.p25.message.P25Message;
import decode.p25.reference.DataUnitID;
public class LDU1Message extends P25Message
{
public LDU1Message( BitSetBuffer message, DataUnitID duid,
AliasList aliasList )
{
super( message, duid, aliasList );
/* NID CRC is checked in the message framer, thus a constructed message
* means it passed the CRC */
mCRC = new CRC[ 2 ];
mCRC[ 0 ] = CRC.PASSED;
}
@Override
public String getMessage()
{
StringBuilder sb = new StringBuilder();
sb.append( "NAC:" );
sb.append( getNAC() );
sb.append( " " );
sb.append( getDUID().getLabel() );
sb.append( " " );
sb.append( mMessage.toString() );
return sb.toString();
}
/* Temporary override */
public boolean isValid()
{
return true;
}
}

View File

@@ -0,0 +1,44 @@
package decode.p25.message.ldu;
import alias.AliasList;
import bits.BitSetBuffer;
import crc.CRC;
import decode.p25.message.P25Message;
import decode.p25.reference.DataUnitID;
public class LDU2Message extends P25Message
{
public LDU2Message( BitSetBuffer message, DataUnitID duid,
AliasList aliasList )
{
super( message, duid, aliasList );
/* NID CRC is checked in the message framer, thus a constructed message
* means it passed the CRC */
mCRC = new CRC[ 2 ];
mCRC[ 0 ] = CRC.PASSED;
}
@Override
public String getMessage()
{
StringBuilder sb = new StringBuilder();
sb.append( "NAC:" );
sb.append( getNAC() );
sb.append( " " );
sb.append( getDUID().getLabel() );
sb.append( " " );
sb.append( mMessage.toString() );
return sb.toString();
}
/* Temporary override */
public boolean isValid()
{
return true;
}
}

View File

@@ -92,7 +92,7 @@ public abstract class UnitToUnitChannelGrantExtended extends PDUMessage
return mMessage.get( EMERGENCY_FLAG );
}
public boolean isEncryptedChannel()
public boolean isEncrypted()
{
return mMessage.get( ENCRYPTED_CHANNEL_FLAG );
}

View File

@@ -86,7 +86,7 @@ public class GroupDataChannelGrantExtended extends PDUMessage
return mMessage.get( EMERGENCY_FLAG );
}
public boolean isEncryptedChannel()
public boolean isEncrypted()
{
return mMessage.get( ENCRYPTED_CHANNEL_FLAG );
}

View File

@@ -84,7 +84,7 @@ public class GroupVoiceChannelGrantExplicit extends PDUMessage
return mMessage.get( EMERGENCY_FLAG );
}
public boolean isEncryptedChannel()
public boolean isEncrypted()
{
return mMessage.get( ENCRYPTED_CHANNEL_FLAG );
}

View File

@@ -99,7 +99,7 @@ public class UnitToUnitVoiceChannelGrantUpdateExtended extends PDUMessage
return mMessage.get( EMERGENCY_FLAG );
}
public boolean isEncryptedChannel()
public boolean isEncrypted()
{
return mMessage.get( ENCRYPTED_CHANNEL_FLAG );
}

View File

@@ -0,0 +1,44 @@
package decode.p25.message.tdu;
import alias.AliasList;
import bits.BitSetBuffer;
import crc.CRC;
import decode.p25.message.P25Message;
import decode.p25.reference.DataUnitID;
public class TDULCMessage extends P25Message
{
public TDULCMessage( BitSetBuffer message, DataUnitID duid,
AliasList aliasList )
{
super( message, duid, aliasList );
/* NID CRC is checked in the message framer, thus a constructed message
* means it passed the CRC */
mCRC = new CRC[ 2 ];
mCRC[ 0 ] = CRC.PASSED;
}
@Override
public String getMessage()
{
StringBuilder sb = new StringBuilder();
sb.append( "NAC:" );
sb.append( getNAC() );
sb.append( " " );
sb.append( getDUID().getLabel() );
sb.append( " " );
sb.append( mMessage.toString() );
return sb.toString();
}
/* Temporary override */
public boolean isValid()
{
return true;
}
}

View File

@@ -20,6 +20,7 @@ import decode.p25.message.tsbk.osp.control.LocationRegistrationResponse;
import decode.p25.message.tsbk.osp.control.MessageUpdate;
import decode.p25.message.tsbk.osp.control.NetworkStatusBroadcast;
import decode.p25.message.tsbk.osp.control.ProtectionParameterUpdate;
import decode.p25.message.tsbk.osp.control.QueuedResponse;
import decode.p25.message.tsbk.osp.control.RFSSStatusBroadcast;
import decode.p25.message.tsbk.osp.control.RadioUnitMonitorCommand;
import decode.p25.message.tsbk.osp.control.RoamingAddressCommand;
@@ -125,6 +126,8 @@ public class TSBKMessageFactory
case NETWORK_STATUS_BROADCAST:
return new NetworkStatusBroadcast( message, duid,
aliasList );
case QUEUED_RESPONSE:
return new QueuedResponse( message, duid, aliasList );
case PROTECTION_PARAMETER_UPDATE:
return new ProtectionParameterUpdate( message, duid,
aliasList );

View File

@@ -55,6 +55,19 @@ public class AuthenticationCommand extends TSBKMessage
{
return mMessage.getHex( TARGET_ID, 6 );
}
public String getFullTargetID()
{
StringBuilder sb = new StringBuilder();
sb.append( getWACN() );
sb.append( "-" );
sb.append( getSystemID() );
sb.append( "-" );
sb.append( getTargetID() );
return sb.toString();
}
@Override
public String getToID()

View File

@@ -18,7 +18,7 @@ public enum DataUnitID
PDU1 ( 12, 356, false, "PDU1 ", "Packet Data Unit" ),
PDU2 ( 12, 452, false, "PDU2 ", "Packet Data Unit" ),
PDU3 ( 12, 548, false, "PDU3 ", "Packet Data Unit" ),
TDULC( 15, 372, false, "TDULC", "Terminator Data Unit With Link Control" ),
TDULC( 15, 352, false, "TDULC", "Terminator Data Unit With Link Control" ),
UNKN ( -1, 0, false, "UNKWN", "Unknown" );
private int mValue;

View File

@@ -17,8 +17,12 @@ import crc.CRCP25;
import decode.p25.P25Interleave;
import decode.p25.TrellisHalfRate;
import decode.p25.message.P25Message;
import decode.p25.message.hdu.HDUMessage;
import decode.p25.message.ldu.LDU1Message;
import decode.p25.message.ldu.LDU2Message;
import decode.p25.message.pdu.PDUMessage;
import decode.p25.message.pdu.PDUMessageFactory;
import decode.p25.message.tdu.TDULCMessage;
import decode.p25.message.tdu.TDUMessage;
import decode.p25.message.tsbk.TSBKMessage;
import decode.p25.message.tsbk.TSBKMessageFactory;
@@ -246,15 +250,15 @@ public class P25MessageFramer implements Listener<Dibit>
break;
case HDU:
mComplete = true;
dispatch( new P25Message( mMessage.copy(), mDUID, mAliasList ) );
dispatch( new HDUMessage( mMessage.copy(), mDUID, mAliasList ) );
break;
case LDU1:
mComplete = true;
dispatch( new P25Message( mMessage.copy(), mDUID, mAliasList ) );
dispatch( new LDU1Message( mMessage.copy(), mDUID, mAliasList ) );
break;
case LDU2:
mComplete = true;
dispatch( new P25Message( mMessage.copy(), mDUID, mAliasList ) );
dispatch( new LDU2Message( mMessage.copy(), mDUID, mAliasList ) );
break;
case PDU0:
/* Remove interleaving */
@@ -368,7 +372,7 @@ public class P25MessageFramer implements Listener<Dibit>
mComplete = true;
break;
case TDULC:
dispatch( new P25Message( mMessage.copy(), mDUID, mAliasList ) );
dispatch( new TDULCMessage( mMessage.copy(), mDUID, mAliasList ) );
mComplete = true;
break;
case TSBK1:

View File

@@ -22,6 +22,8 @@ import instrument.tap.Tap;
import instrument.tap.TapViewPanel;
import instrument.tap.stream.BinaryTap;
import instrument.tap.stream.BinaryTapViewPanel;
import instrument.tap.stream.DibitTap;
import instrument.tap.stream.DibitTapViewPanel;
import instrument.tap.stream.FloatTap;
import instrument.tap.stream.FloatTapViewPanel;
import instrument.tap.stream.SymbolEventTap;
@@ -166,6 +168,9 @@ public class DecoderViewFrame extends JInternalFrame
break;
case STREAM_COMPLEX:
break;
case STREAM_DIBIT:
panel = new DibitTapViewPanel( (DibitTap)tap );
break;
case STREAM_FLOAT:
panel = new FloatTapViewPanel( (FloatTap)tap );
break;

View File

@@ -22,6 +22,7 @@ public enum TapType
EVENT_SYNC_DETECT,
STREAM_BINARY,
STREAM_COMPLEX,
STREAM_DIBIT,
STREAM_FLOAT,
STREAM_SYMBOL;
}

View File

@@ -0,0 +1,60 @@
/*******************************************************************************
* 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 instrument.tap.stream;
import instrument.tap.TapListener;
import instrument.tap.TapType;
import sample.Listener;
import dsp.fsk.Dibit;
public class DibitTap extends StreamTap implements Listener<Dibit>
{
private Listener<Dibit> mListener;
public DibitTap( String name,
int delay,
float sampleRateRatio )
{
super( TapType.STREAM_DIBIT, name, delay, sampleRateRatio );
}
@Override
public void receive( Dibit dibit )
{
if( mListener != null )
{
mListener.receive( dibit );
}
for( TapListener listener: mListeners )
{
listener.receive( dibit );
}
}
public void setListener( Listener<Dibit> listener )
{
mListener = listener;
}
public void removeListener( Listener<Dibit> listener )
{
mListener = null;
}
}

View File

@@ -0,0 +1,190 @@
/*******************************************************************************
* 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 instrument.tap.stream;
import instrument.gui.SampleModel;
import instrument.tap.Tap;
import instrument.tap.TapViewPanel;
import java.awt.Color;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Path2D;
import java.awt.geom.Rectangle2D;
import java.util.List;
import java.util.Observable;
import dsp.fsk.Dibit;
public class DibitTapViewPanel extends TapViewPanel
{
private static final long serialVersionUID = 1L;
private Tap mTap;
private List<Dibit> mSamples;
private int mSampleCount;
private int mOffset = 0;
public DibitTapViewPanel( DibitTap tap )
{
super( new SampleModel<Dibit>(), tap.getName() );
mTap = tap;
mTap.addListener( getModel() );
getModel().setSampleCount(
(int)( getModel().getSampleCount() * tap.getSampleRateRatio() ) );
getModel().setDelay( tap.getDelay() );
}
@Override
public void update( Observable arg0, Object arg1 )
{
mSamples = (List<Dibit>)getModel().getSamples();
mSampleCount = getModel().getSampleCount();
repaint();
}
public void paintComponent( Graphics g )
{
super.paintComponent( g );
Graphics2D g2 = (Graphics2D)g;
int height = getHeight();
int width = getWidth();
g2.setColor( Color.DARK_GRAY );
float middle = (float)height / 2.0f;
g2.drawLine( 0, (int)middle, width, (int)middle );
if( mSamples != null && mSamples.size() > 0 )
{
g2.setColor( getForeground() );
g2.drawString( mLabel, 5 , 15 );
for( int x = 1; x < mSamples.size(); x++ )
{
paintDibit( g, x, mSamples.get( x ) );
}
}
g2.dispose();
}
/**
* Paints a polyline representing a boolean value with a 0 or 1 label in
* the middle of it
*
* @param graphics
* @param index - index of the sample int the sample buffer
* @param value - bit value, 0 or 1
*/
private void paintDibit( Graphics graphics, int index, Dibit value )
{
Graphics2D g2 = (Graphics2D)graphics;
Path2D.Float polyline =
new Path2D.Float( Path2D.Float.WIND_EVEN_ODD, mSamples.size() );
float middleY = ( (float)getHeight() / 2.0f );
float indexWidth = (float)( getWidth() ) / (float)mSampleCount;
float startX = (float)index * indexWidth;
float stopX = (float)(index + 1 ) * indexWidth;
float fivePercent = indexWidth * .05f;
float leftX = startX + fivePercent;
float rightX = stopX - fivePercent;
/* Start */
polyline.moveTo( startX, middleY );
/* Left horizonal segment */
polyline.lineTo( leftX, middleY );
float y = middleY;
switch( value )
{
case D01_PLUS_3:
y = middleY - ( middleY * 0.75f);
break;
case D00_PLUS_1:
y = middleY - ( middleY * 0.25f);
break;
case D10_MINUS_1:
y = middleY + ( middleY * 0.25f);
break;
case D11_MINUS_3:
y = middleY + ( middleY * 0.75f);
break;
}
/* Left vertical segment */
polyline.lineTo( leftX, y );
/* Middle horizontal segment */
polyline.lineTo( rightX, y );
/* Right vertical segment */
polyline.lineTo( rightX, middleY );
/* Right horizontal segment */
polyline.lineTo( stopX, middleY );
g2.draw( polyline );
/* Draw the bit value label */
String bit = null;
switch( value )
{
case D00_PLUS_1:
bit = "+1";
break;
case D01_PLUS_3:
bit = "+3";
break;
case D10_MINUS_1:
bit = "-1";
break;
case D11_MINUS_3:
bit = "-3";
break;
}
FontMetrics fontMetrics = graphics.getFontMetrics( this.getFont() );
Rectangle2D label = fontMetrics.getStringBounds( bit, graphics );
float offsetX = (float)label.getWidth() / 2.0f;
float offsetY = (float)label.getWidth() / 2.0f;
float middleX = startX + ( indexWidth / 2.0f );
graphics.drawString( bit,
(int)( middleX - offsetX ),
(int)( middleY - offsetY ) );
}
}