P25 Work in progress. Adds CRC checking to NID/DUID. Adds limiter to trellis decoder to prevent it from decoding an excessive error message. Updates channel state with initial handling/generation of call events.

This commit is contained in:
dennis.sheirer
2014-12-14 02:34:19 +00:00
parent 0d8da161e1
commit 170688b95e
42 changed files with 1099 additions and 390 deletions

View File

@@ -24,16 +24,16 @@ import decode.DecoderType;
public class P25CallEvent extends CallEvent
{
private int mChannel;
private String mChannel;
private long mFrequency;
public P25CallEvent( CallEventType callEventType,
AliasList aliasList,
String fromID,
String toID,
int channel,
long frequency,
String details )
AliasList aliasList,
String fromID,
String toID,
String channel,
long frequency,
String details )
{
super( DecoderType.MPT1327, callEventType,
aliasList, fromID, toID, details );
@@ -42,11 +42,11 @@ public class P25CallEvent extends CallEvent
mFrequency = frequency;
}
private Alias getAlias( String mpt1327Talkgroup )
private Alias getAlias( String talkgroup )
{
if( hasAliasList() )
{
return getAliasList().getMPT1327Alias( mpt1327Talkgroup );
return getAliasList().getTalkgroupAlias( talkgroup );
}
return null;
@@ -65,7 +65,7 @@ public class P25CallEvent extends CallEvent
}
@Override
public int getChannel()
public String getChannel()
{
return mChannel;
}
@@ -86,7 +86,7 @@ public class P25CallEvent extends CallEvent
private String mFromID;
private String mToID;
private String mDetails;
private int mChannel;
private String mChannel;
private long mFrequency;
public Builder( CallEventType callEventType )
@@ -106,7 +106,7 @@ public class P25CallEvent extends CallEvent
return this;
}
public Builder channel( int channel )
public Builder channel( String channel )
{
mChannel = channel;
return this;

View File

@@ -22,12 +22,21 @@ import alias.Alias;
import alias.AliasList;
import audio.SquelchListener;
import audio.SquelchListener.SquelchState;
import controller.activity.CallEvent.CallEventType;
import controller.channel.ProcessingChain;
import controller.state.AuxChannelState;
import controller.state.ChannelState;
import decode.p25.message.P25Message;
import decode.p25.message.pdu.osp.control.RFSSStatusBroadcastExtended;
import decode.p25.message.tsbk.TSBKMessage;
import decode.p25.message.tsbk.osp.control.LocationRegistrationResponse;
import decode.p25.message.tsbk.osp.control.RFSSStatusBroadcast;
import decode.p25.message.tsbk.osp.control.UnitDeregistrationAcknowledge;
import decode.p25.message.tsbk.osp.voice.GroupVoiceChannelGrant;
import decode.p25.message.tsbk.osp.voice.GroupVoiceChannelGrantUpdate;
import decode.p25.message.tsbk.osp.voice.GroupVoiceChannelGrantUpdateExplicit;
import decode.p25.message.tsbk.osp.voice.UnitToUnitAnswerRequest;
import decode.p25.reference.Vendor;
public class P25ChannelState extends ChannelState
{
@@ -123,11 +132,118 @@ public class P25ChannelState extends ChannelState
{
mSiteAlias = alias.getName();
broadcastChange( ChangedAttribute.SITE_ALIAS );
}
else
{
mSiteAlias = null;
}
}
broadcastChange( ChangedAttribute.SITE );
broadcastChange( ChangedAttribute.SITE_ALIAS );
}
}
else if( message instanceof TSBKMessage )
{
TSBKMessage tsbk = (TSBKMessage)message;
setState( State.CONTROL );
if( tsbk.getVendor() == Vendor.STANDARD )
{
switch( tsbk.getOpcode() )
{
case GROUP_VOICE_CHANNEL_GRANT:
GroupVoiceChannelGrant gvcg = (GroupVoiceChannelGrant)tsbk;
mCallEventModel.add(
new P25CallEvent.Builder( CallEventType.CALL )
.aliasList( mAliasList )
.channel( gvcg.getChannelIdentifier() + "-" + gvcg.getChannel() )
.details( ( gvcg.isEncrypted() ? "ENCRYPTED" : "" ) +
( gvcg.isEmergency() ? " EMERGENCY" : "") )
.frequency( gvcg.getDownlinkFrequency() )
.from( gvcg.getSourceAddress() )
.to( gvcg.getGroupAddress() )
.build() );
break;
case GROUP_VOICE_CHANNEL_GRANT_UPDATE:
GroupVoiceChannelGrantUpdate gvcgu =
(GroupVoiceChannelGrantUpdate)tsbk;
mCallEventModel.add(
new P25CallEvent.Builder( CallEventType.CALL )
.aliasList( mAliasList )
.channel( gvcgu.getChannelID1() + "-" + gvcgu.getChannelNumber1() )
.details( ( gvcgu.isEncrypted() ? "ENCRYPTED" : "" ) +
( gvcgu.isEmergency() ? " EMERGENCY" : "") )
.frequency( gvcgu.getDownlinkFrequency() )
.to( gvcgu.getGroupAddress1() )
.build() );
if( gvcgu.getChannelNumber2() != 0 )
{
mCallEventModel.add(
new P25CallEvent.Builder( CallEventType.CALL )
.aliasList( mAliasList )
.channel( gvcgu.getChannelID2() + "-" + gvcgu.getChannelNumber2() )
.details( ( gvcgu.isEncrypted() ? "ENCRYPTED" : "" ) +
( gvcgu.isEmergency() ? " EMERGENCY" : "") )
.frequency( gvcgu.getDownlinkFrequency() )
.to( gvcgu.getGroupAddress2() )
.build() );
}
break;
case GROUP_VOICE_CHANNEL_GRANT_UPDATE_EXPLICIT:
GroupVoiceChannelGrantUpdateExplicit gvcgue =
(GroupVoiceChannelGrantUpdateExplicit)tsbk;
mCallEventModel.add(
new P25CallEvent.Builder( CallEventType.CALL )
.aliasList( mAliasList )
.channel( gvcgue.getTransmitChannelID() + "-" +
gvcgue.getTransmitChannelNumber() )
.details( ( gvcgue.isEncrypted() ? "ENCRYPTED" : "" ) +
( gvcgue.isEmergency() ? " EMERGENCY" : "") )
.frequency( gvcgue.getDownlinkFrequency() )
.to( gvcgue.getGroupAddress() )
.build() );
break;
case UNIT_TO_UNIT_ANSWER_REQUEST:
UnitToUnitAnswerRequest utuar =
(UnitToUnitAnswerRequest)tsbk;
mCallEventModel.add(
new P25CallEvent.Builder( CallEventType.PAGE )
.aliasList( mAliasList )
.details( ( utuar.isEmergency() ? "EMERGENCY" : "" ) )
.from( utuar.getSourceAddress() )
.to( utuar.getTargetAddress() )
.build() );
break;
case LOCATION_REGISTRATION_RESPONSE:
LocationRegistrationResponse lrr =
(LocationRegistrationResponse)tsbk;
mCallEventModel.add(
new P25CallEvent.Builder( CallEventType.REGISTER )
.aliasList( mAliasList )
.details( "REG:" + lrr.getResponse().name() +
" SITE: " + lrr.getRFSSID() + "-" +
lrr.getSiteID() )
.from( lrr.getTargetAddress() )
.to( lrr.getGroupAddress() )
.build() );
break;
case UNIT_DEREGISTRATION_ACKNOWLEDGE:
UnitDeregistrationAcknowledge udr =
(UnitDeregistrationAcknowledge)tsbk;
mCallEventModel.add(
new P25CallEvent.Builder( CallEventType.DEREGISTER )
.aliasList( mAliasList )
.from( udr.getSourceID() )
.build() );
break;
}
}
}
}

View File

@@ -45,7 +45,7 @@ public class P25Panel extends ChannelStatePanel
private JLabel mSystem = new JLabel( "SYS:" );
private JLabel mSite = new JLabel( "Site:" );
private JLabel mSiteAlias = new JLabel( "WACN:" );
private JLabel mSiteAlias = new JLabel( "" );
public P25Panel( Channel channel )

View File

@@ -1,17 +1,20 @@
package decode.p25;
import java.util.ArrayList;
import java.util.Random;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import bits.BitSetBuffer;
import dsp.fsk.Dibit;
import dsp.fsk.P25MessageFramer;
public class TrellisHalfRate
{
private final static Logger mLog = LoggerFactory.getLogger( TrellisHalfRate.class );
public final static int MAX_ERROR_THRESHOLD = 7;
private final static Logger mLog =
LoggerFactory.getLogger( TrellisHalfRate.class );
private ArrayList<ConstellationNode> mConstellationNodes =
new ArrayList<ConstellationNode>();
@@ -94,7 +97,7 @@ public class TrellisHalfRate
return sb.toString();
}
public void decode( BitSetBuffer message, int start, int end )
public boolean decode( BitSetBuffer message, int start, int end )
{
/* load each of the nodes with de-interleaved constellations */
for( int index = 0; index < 49; index++ )
@@ -104,20 +107,24 @@ public class TrellisHalfRate
mConstellationNodes.get( index ).setConstellation( c );
}
// mLog.debug( constellationsToString( " LOADED: ") );
//
/* test to see if constellations are correct - otherwise correct them */
ConstellationNode firstNode = mConstellationNodes.get( 0 );
if( !firstNode.hasStateDibit( Dibit.D00_PLUS_1 ) || !firstNode.isCorrect() )
{
firstNode.correctTo( Dibit.D00_PLUS_1 );
}
int errorCount = firstNode.getErrorCount();
// mLog.debug( constellationsToString( "CORRECTD: " ) );
//
// mLog.debug( inputsToString() );
//
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 );
@@ -137,6 +144,8 @@ public class TrellisHalfRate
message.set( start + ( index * 2 ) + 1 );
}
}
return true;
}
private Constellation getConstellation( BitSetBuffer message, int index )
@@ -301,24 +310,18 @@ public class TrellisHalfRate
* @return - true - all node connections to the right are correct
* false - one or more nodes to the right are incorrect
*/
public boolean isCorrect()
public int getErrorCount()
{
if( mCorrect )
{
return mCorrect;
}
if( mConnectedNode == null )
{
mCorrect = true;
return 0;
}
else
{
mCorrect = mConnectedNode.isCorrect() &&
mConstellation.getInput() == mConnectedNode.getStateDibit();
}
mCorrect = mConstellation.getInput() == mConnectedNode.getStateDibit();
return mCorrect;
return mConnectedNode.getErrorCount() + ( mCorrect ? 0 : 1 );
}
public Dibit getStateDibit()
@@ -495,15 +498,17 @@ public class TrellisHalfRate
*/
public static void main( String[] args )
{
String original = "00100110000001111000101111110011111000011110100111100001000100100010110000001101100100100010001000100010001000101100111000100010111110001000100010001000100010001000100010001000100010001000100010001000100010001000100010001000101100001110000101000110011100001110";
BitSetBuffer buffer = new BitSetBuffer( original.length() );
Random random = new Random();
BitSetBuffer buffer = new BitSetBuffer( 196 );
try
{
for( int x = 0; x < original.length(); x++ )
for( int x = 0; x < 196; x++ )
{
if( original.substring( x, x + 1 ).contentEquals( "0" ) )
int number = random.nextInt( 2 );
if( number == 1 )
{
buffer.add( false );
}
@@ -518,12 +523,11 @@ public class TrellisHalfRate
e.printStackTrace();
}
mLog.debug( "ORIGINAL: " + original );
mLog.debug( " BUFFER: " + buffer.toString() );
TrellisHalfRate t = new TrellisHalfRate();
t.decode( buffer, 64, original.length() );
t.decode( buffer, 0, 196 );
mLog.debug( " DECODED: " + buffer.toString() );
mLog.debug( "Finished!" );
}

View File

@@ -12,7 +12,7 @@ public class P25MessageFilterSet extends FilterSet<Message>
addFilter( new PDUMessageFilter() );
addFilter( new TDUMessageFilter() );
addFilter( new TSBKMessageFilter() );
addFilter( new TSBKMessageFilterSet() );
}
@Override

View File

@@ -1,54 +0,0 @@
package decode.p25.message.filter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import message.Message;
import decode.p25.message.tsbk.TSBKMessage;
import decode.p25.reference.Opcode;
import filter.Filter;
import filter.FilterElement;
public class TSBKMessageFilter extends Filter<Message>
{
private HashMap<Opcode,FilterElement<Opcode>> mFilterElements =
new HashMap<Opcode,FilterElement<Opcode>>();
public TSBKMessageFilter()
{
super( "TSBK Trunking Signalling Block" );
for( Opcode opcode: Opcode.values() )
{
mFilterElements.put( opcode, new FilterElement<Opcode>( opcode ) );
}
}
@Override
public boolean passes( Message message )
{
if( mEnabled && canProcess( message ) )
{
TSBKMessage tsbk = (TSBKMessage)message;
Opcode opcode = tsbk.getOpcode();
return mFilterElements.get( opcode ).isEnabled();
}
return false;
}
@Override
public boolean canProcess( Message message )
{
return message instanceof TSBKMessage;
}
@Override
public List<FilterElement<?>> getFilterElements()
{
return new ArrayList<FilterElement<?>>( mFilterElements.values() );
}
}

View File

@@ -0,0 +1,200 @@
package decode.p25.message.filter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import message.Message;
import decode.p25.message.tsbk.TSBKMessage;
import decode.p25.message.tsbk.motorola.MotorolaOpcode;
import decode.p25.message.tsbk.vendor.VendorOpcode;
import decode.p25.reference.Opcode;
import decode.p25.reference.Vendor;
import filter.Filter;
import filter.FilterElement;
import filter.FilterSet;
public class TSBKMessageFilterSet extends FilterSet<Message>
{
public TSBKMessageFilterSet()
{
super( "TSBK Trunking Signalling Block" );
addFilter( new StandardOpcodeFilter() );
addFilter( new MotorolaOpcodeFilter() );
addFilter( new VendorOpcodeFilter() );
}
// @Override
// public boolean passes( Message message )
// {
// if( mEnabled && canProcess( message ) )
// {
// TSBKMessage tsbk = (TSBKMessage)message;
//
// switch( tsbk.getVendor() )
// {
// case STANDARD:
// return
// }
// Opcode opcode = tsbk.getOpcode();
//
// return mFilterElements.get( opcode ).isEnabled();
// }
//
// return false;
// }
@Override
public boolean canProcess( Message message )
{
return message instanceof TSBKMessage;
}
public class StandardOpcodeFilter extends Filter<Message>
{
private HashMap<Opcode,FilterElement<Opcode>> mStandardElements =
new HashMap<Opcode,FilterElement<Opcode>>();
public StandardOpcodeFilter()
{
super( "Standard Opcode Filter" );
for( Opcode opcode: Opcode.values() )
{
if( opcode != Opcode.UNKNOWN )
{
mStandardElements.put( opcode,
new FilterElement<Opcode>( opcode ) );
}
}
}
@Override
public boolean passes( Message message )
{
if( mEnabled && canProcess( message ) )
{
Opcode opcode = ((TSBKMessage)message).getOpcode();
if( mStandardElements.containsKey( opcode ) )
{
return mStandardElements.get( opcode ).isEnabled();
}
}
return false;
}
@Override
public boolean canProcess( Message message )
{
return ((TSBKMessage)message).getVendor() == Vendor.STANDARD;
}
@Override
public List<FilterElement<?>> getFilterElements()
{
return new ArrayList<FilterElement<?>>( mStandardElements.values() );
}
}
public class MotorolaOpcodeFilter extends Filter<Message>
{
private HashMap<MotorolaOpcode,FilterElement<MotorolaOpcode>> mMotorolaElements =
new HashMap<MotorolaOpcode,FilterElement<MotorolaOpcode>>();
public MotorolaOpcodeFilter()
{
super( "Motorola Opcode Filter" );
for( MotorolaOpcode opcode: MotorolaOpcode.values() )
{
if( opcode != MotorolaOpcode.UNKNOWN )
{
mMotorolaElements.put( opcode,
new FilterElement<MotorolaOpcode>( opcode ) );
}
}
}
@Override
public boolean passes( Message message )
{
if( mEnabled && canProcess( message ) )
{
MotorolaOpcode opcode = ((TSBKMessage)message).getMotorolaOpcode();
if( mMotorolaElements.containsKey( opcode ) )
{
return mMotorolaElements.get( opcode ).isEnabled();
}
}
return false;
}
@Override
public boolean canProcess( Message message )
{
return ((TSBKMessage)message).getVendor() == Vendor.MOTOROLA;
}
@Override
public List<FilterElement<?>> getFilterElements()
{
return new ArrayList<FilterElement<?>>( mMotorolaElements.values() );
}
}
public class VendorOpcodeFilter extends Filter<Message>
{
private HashMap<VendorOpcode,FilterElement<VendorOpcode>> mVendorElements =
new HashMap<VendorOpcode,FilterElement<VendorOpcode>>();
public VendorOpcodeFilter()
{
super( "Vendor Opcode Filter" );
for( VendorOpcode opcode: VendorOpcode.values() )
{
if( opcode != VendorOpcode.UNKNOWN )
{
mVendorElements.put( opcode,
new FilterElement<VendorOpcode>( opcode ) );
}
}
}
@Override
public boolean passes( Message message )
{
if( mEnabled && canProcess( message ) )
{
MotorolaOpcode opcode = ((TSBKMessage)message).getMotorolaOpcode();
if( mVendorElements.containsKey( opcode ) )
{
return mVendorElements.get( opcode ).isEnabled();
}
}
return false;
}
@Override
public boolean canProcess( Message message )
{
Vendor vendor = ((TSBKMessage)message).getVendor();
return vendor != Vendor.STANDARD && vendor != Vendor.MOTOROLA;
}
@Override
public List<FilterElement<?>> getFilterElements()
{
return new ArrayList<FilterElement<?>>( mVendorElements.values() );
}
}
}

View File

@@ -2,15 +2,20 @@ package decode.p25.message.tsbk;
import alias.AliasList;
import bits.BitSetBuffer;
import decode.p25.message.tsbk.osp.control.IdentifierUpdate;
import decode.p25.message.tsbk.osp.control.IdentifierUpdateReceiver;
import decode.p25.reference.DataUnitID;
public abstract class ChannelGrant extends ServiceMessage
public abstract class ChannelGrant extends ServiceMessage
implements IdentifierUpdateReceiver
{
public static final int[] PRIORITY = { 85,86,87 };
public static final int[] CHANNEL_ID = { 88,89,90,91 };
public static final int[] CHANNEL_NUMBER = { 92,93,94,95,96,97,98,99,100,
101,102,103 };
private IdentifierUpdate mIdentifierUpdate;
public ChannelGrant( BitSetBuffer message,
DataUnitID duid,
AliasList aliasList )
@@ -26,13 +31,40 @@ public abstract class ChannelGrant extends ServiceMessage
return mMessage.getInt( PRIORITY );
}
public int getChannelID()
public int getChannelIdentifier()
{
return mMessage.getInt( CHANNEL_ID );
}
public int getChannelNumber()
public int getChannel()
{
return mMessage.getInt( CHANNEL_NUMBER );
}
@Override
public void setIdentifierMessage( int identifier, IdentifierUpdate message )
{
mIdentifierUpdate = message;
}
@Override
public int[] getIdentifiers()
{
int[] identifiers = new int[ 1 ];
identifiers[ 0 ] = getChannelIdentifier();
return identifiers;
}
public long getDownlinkFrequency()
{
return calculateDownlink( mIdentifierUpdate, getChannel() );
}
public long getUplinkFrequency()
{
return calculateUplink( mIdentifierUpdate, getChannel() );
}
}

View File

@@ -41,15 +41,19 @@ public abstract class GroupChannelGrant extends ChannelGrant
sb.append( " EMERGENCY" );
}
sb.append( " CHAN:" );
sb.append( getChannelID() + "/" + getChannelNumber() );
sb.append( " FROM:" );
sb.append( " SOURCE UNIT:" );
sb.append( getSourceAddress() );
sb.append( " GRP:" );
sb.append( " GROUP:" );
sb.append( getGroupAddress() );
sb.append( " CHAN:" );
sb.append( getChannelIdentifier() + "-" + getChannel() );
sb.append( " DN:" + getDownlinkFrequency() );
sb.append( " UP:" + getUplinkFrequency() );
return sb.toString();
}

View File

@@ -2,6 +2,7 @@ package decode.p25.message.tsbk;
import alias.AliasList;
import bits.BitSetBuffer;
import decode.p25.message.tsbk.osp.control.IdentifierUpdate;
import decode.p25.reference.DataUnitID;
public abstract class GroupChannelGrantExplicit extends ChannelGrant
@@ -15,6 +16,9 @@ public abstract class GroupChannelGrantExplicit extends ChannelGrant
public static final int[] GROUP_ADDRESS = { 128,129,130,131,132,133,134,135,
136,137,138,139,140,141,142,143 };
private IdentifierUpdate mTransmitIdentifierUpdate;
private IdentifierUpdate mReceiveIdentifierUpdate;
public GroupChannelGrantExplicit( BitSetBuffer message,
DataUnitID duid,
AliasList aliasList )
@@ -69,4 +73,38 @@ public abstract class GroupChannelGrantExplicit extends ChannelGrant
{
return mMessage.getHex( GROUP_ADDRESS, 4 );
}
@Override
public void setIdentifierMessage( int identifier, IdentifierUpdate message )
{
if( identifier == getTransmitChannelID() )
{
mTransmitIdentifierUpdate = message;
}
if( identifier == getReceiveChannelID() )
{
mReceiveIdentifierUpdate = message;
}
}
@Override
public int[] getIdentifiers()
{
int[] identifiers = new int[ 2 ];
identifiers[ 0 ] = getTransmitChannelID();
identifiers[ 1 ] = getReceiveChannelID();
return identifiers;
}
public long getDownlinkFrequency()
{
return calculateDownlink( mTransmitIdentifierUpdate, getChannel() );
}
public long getUplinkFrequency()
{
return calculateUplink( mReceiveIdentifierUpdate, getChannel() );
}
}

View File

@@ -3,7 +3,8 @@ package decode.p25.message.tsbk;
import alias.AliasList;
import bits.BitSetBuffer;
import decode.p25.message.P25Message;
import decode.p25.message.tsbk.osp.control.IdentifierUpdate;
import decode.p25.message.tsbk.motorola.MotorolaOpcode;
import decode.p25.message.tsbk.vendor.VendorOpcode;
import decode.p25.reference.DataUnitID;
import decode.p25.reference.Opcode;
import decode.p25.reference.Vendor;
@@ -56,6 +57,16 @@ public class TSBKMessage extends P25Message
return Opcode.fromValue( mMessage.getInt( OPCODE ) );
}
public MotorolaOpcode getMotorolaOpcode()
{
return MotorolaOpcode.fromValue( mMessage.getInt( OPCODE ) );
}
public VendorOpcode getVendorOpcode()
{
return VendorOpcode.fromValue( mMessage.getInt( OPCODE ) );
}
@Override
public String getMessage()
{

View File

@@ -196,9 +196,9 @@ public class TSBKMessageFactory
switch( motorolaOpcode )
{
case CHANNEL_CWID_UPDATE:
case OP0B:
return new ChannelCWIDUpdate( message, duid, aliasList );
case SYSTEM_LOADING:
case OP09:
return new SystemLoading( message, duid, aliasList );
default:
}

View File

@@ -34,7 +34,7 @@ public class ChannelCWIDUpdate extends MotorolaTSBKMessage
@Override
public String getEventType()
{
return MotorolaOpcode.CHANNEL_CWID_UPDATE.getDescription();
return MotorolaOpcode.OP0B.getDescription();
}
public String getMessage()
@@ -58,94 +58,31 @@ public class ChannelCWIDUpdate extends MotorolaTSBKMessage
public String getCWID()
{
StringBuilder sb = new StringBuilder();
int char1 = mMessage.getInt( CHARACTER_1 );
if( char1 != 0 )
{
sb.append( (char)( char1 + 46 ) );
}
else
{
return null;
}
int char2 = mMessage.getInt( CHARACTER_2 );
if( char2 != 0 )
{
sb.append( (char)( char2 + 46 ) );
}
else
{
return sb.toString();
}
int char3 = mMessage.getInt( CHARACTER_3 );
if( char3 != 0 )
{
sb.append( (char)( char3 + 46 ) );
}
else
{
return sb.toString();
}
int char4 = mMessage.getInt( CHARACTER_4 );
if( char4 != 0 )
{
sb.append( (char)( char4 + 46 ) );
}
else
{
return sb.toString();
}
int char5 = mMessage.getInt( CHARACTER_5 );
if( char5 != 0 )
{
sb.append( (char)( char5 + 46 ) );
}
else
{
return sb.toString();
}
int char6 = mMessage.getInt( CHARACTER_6 );
if( char6 != 0 )
{
sb.append( (char)( char6 + 46 ) );
}
else
{
return sb.toString();
}
int char7 = mMessage.getInt( CHARACTER_7 );
if( char7 != 0 )
{
sb.append( (char)( char7 + 46 ) );
}
else
{
return sb.toString();
}
int char8 = mMessage.getInt( CHARACTER_8 );
if( char8 != 0 )
{
sb.append( (char)( char8 + 46 ) );
}
sb.append( getCharacter( CHARACTER_1 ) );
sb.append( getCharacter( CHARACTER_2 ) );
sb.append( getCharacter( CHARACTER_3 ) );
sb.append( getCharacter( CHARACTER_4 ) );
sb.append( getCharacter( CHARACTER_5 ) );
sb.append( getCharacter( CHARACTER_6 ) );
sb.append( getCharacter( CHARACTER_7 ) );
sb.append( getCharacter( CHARACTER_8 ) );
return sb.toString();
}
private String getCharacter( int[] field )
{
int value = mMessage.getInt( field );
if( value != 0 )
{
return String.valueOf( (char)( value + 43 ) );
}
return null;
}
public int getIdentifier()
{
return mMessage.getInt( IDENTIFIER );

View File

@@ -2,9 +2,70 @@ package decode.p25.message.tsbk.motorola;
public enum MotorolaOpcode
{
UNKNOWN_OPCODE_5( "MOTO OPCODE 05 ", "Unknown Opcode 0x05", 0x05 ),
SYSTEM_LOADING( "SYSTEM_LOADING ", "System Loading", 0x09 ),
CHANNEL_CWID_UPDATE( "CHAN_CWID_UPDATE", "Channel FCC Callsign Update", 0x0B ),
OP00( "OP00:UNKNOWN ", "Opcode 0x00 Unknown", 0x00 ),
OP01( "OP01:UNKNOWN ", "Opcode 0x01 Unknown", 0x01 ),
OP02( "OP02:UNKNOWN ", "Opcode 0x02 Unknown", 0x02 ),
OP03( "OP03:UNKNOWN ", "Opcode 0x03 Unknown", 0x03 ),
OP04( "OP04:UNKNOWN ", "Opcode 0x04 Unknown", 0x04 ),
OP05( "OP05:UNKNOWN ", "Opcode 0x05 Unknown", 0x05 ),
OP06( "OP06:UNKNOWN ", "Opcode 0x06 Unknown", 0x06 ),
OP07( "OP07:UNKNOWN ", "Opcode 0x07 Unknown", 0x07 ),
OP08( "OP08:UNKNOWN ", "Opcode 0x08 Unknown", 0x08 ),
OP09( "OP09:SYSTEM_LOAD", "Opcode 0x09 System Loading", 0x09 ),
OP0A( "OP0A:UNKNOWN ", "Opcode 0x0A Unknown", 0x0A ),
OP0B( "OP0B:CWID_UPDATE", "Opcode 0x0B FCC Callsign", 0x0B ),
OP0C( "OP0C:UNKNOWN ", "Opcode 0x0C Unknown", 0x0C ),
OP0D( "OP0D:UNKNOWN ", "Opcode 0x0D Unknown", 0x0D ),
OP0E( "OP0E:UNKNOWN ", "Opcode 0x0E Unknown", 0x0E ),
OP0F( "OP0F:UNKNOWN ", "Opcode 0x0F Unknown", 0x0F ),
OP10( "OP10:UNKNOWN ", "Opcode 0x10 Unknown", 0x10 ),
OP11( "OP11:UNKNOWN ", "Opcode 0x11 Unknown", 0x11 ),
OP12( "OP12:UNKNOWN ", "Opcode 0x12 Unknown", 0x12 ),
OP13( "OP13:UNKNOWN ", "Opcode 0x13 Unknown", 0x13 ),
OP14( "OP14:UNKNOWN ", "Opcode 0x14 Unknown", 0x14 ),
OP15( "OP15:UNKNOWN ", "Opcode 0x15 Unknown", 0x15 ),
OP16( "OP16:UNKNOWN ", "Opcode 0x16 Unknown", 0x16 ),
OP17( "OP17:UNKNOWN ", "Opcode 0x17 Unknown", 0x17 ),
OP18( "OP18:UNKNOWN ", "Opcode 0x18 Unknown", 0x18 ),
OP19( "OP19:UNKNOWN ", "Opcode 0x19 Unknown", 0x19 ),
OP1A( "OP1A:UNKNOWN ", "Opcode 0x1A Unknown", 0x1A ),
OP1B( "OP1B:UNKNOWN ", "Opcode 0x1B Unknown", 0x1B ),
OP1C( "OP1C:UNKNOWN ", "Opcode 0x1C Unknown", 0x1C ),
OP1D( "OP1D:UNKNOWN ", "Opcode 0x1D Unknown", 0x1D ),
OP1E( "OP1E:UNKNOWN ", "Opcode 0x1E Unknown", 0x1E ),
OP1F( "OP1F:UNKNOWN ", "Opcode 0x1F Unknown", 0x1F ),
OP20( "OP20:UNKNOWN ", "Opcode 0x20 Unknown", 0x20 ),
OP21( "OP21:UNKNOWN ", "Opcode 0x21 Unknown", 0x21 ),
OP22( "OP22:UNKNOWN ", "Opcode 0x22 Unknown", 0x22 ),
OP23( "OP23:UNKNOWN ", "Opcode 0x23 Unknown", 0x23 ),
OP24( "OP24:UNKNOWN ", "Opcode 0x24 Unknown", 0x24 ),
OP25( "OP25:UNKNOWN ", "Opcode 0x25 Unknown", 0x25 ),
OP26( "OP26:UNKNOWN ", "Opcode 0x26 Unknown", 0x26 ),
OP27( "OP27:UNKNOWN ", "Opcode 0x27 Unknown", 0x27 ),
OP28( "OP28:UNKNOWN ", "Opcode 0x28 Unknown", 0x28 ),
OP29( "OP29:UNKNOWN ", "Opcode 0x29 Unknown", 0x29 ),
OP2A( "OP2A:UNKNOWN ", "Opcode 0x2A Unknown", 0x2A ),
OP2B( "OP2B:UNKNOWN ", "Opcode 0x2B Unknown", 0x2B ),
OP2C( "OP2C:UNKNOWN ", "Opcode 0x2C Unknown", 0x2C ),
OP2D( "OP2D:UNKNOWN ", "Opcode 0x2D Unknown", 0x2D ),
OP2E( "OP2E:UNKNOWN ", "Opcode 0x2E Unknown", 0x2E ),
OP2F( "OP2F:UNKNOWN ", "Opcode 0x2F Unknown", 0x2F ),
OP30( "OP30:UNKNOWN ", "Opcode 0x30 Unknown", 0x30 ),
OP31( "OP31:UNKNOWN ", "Opcode 0x31 Unknown", 0x31 ),
OP32( "OP32:UNKNOWN ", "Opcode 0x32 Unknown", 0x32 ),
OP33( "OP33:UNKNOWN ", "Opcode 0x33 Unknown", 0x33 ),
OP34( "OP34:UNKNOWN ", "Opcode 0x34 Unknown", 0x34 ),
OP35( "OP35:UNKNOWN ", "Opcode 0x35 Unknown", 0x35 ),
OP36( "OP36:UNKNOWN ", "Opcode 0x36 Unknown", 0x36 ),
OP37( "OP37:UNKNOWN ", "Opcode 0x37 Unknown", 0x37 ),
OP38( "OP38:UNKNOWN ", "Opcode 0x38 Unknown", 0x38 ),
OP39( "OP39:UNKNOWN ", "Opcode 0x39 Unknown", 0x39 ),
OP3A( "OP3A:UNKNOWN ", "Opcode 0x3A Unknown", 0x3A ),
OP3B( "OP3B:UNKNOWN ", "Opcode 0x3B Unknown", 0x3B ),
OP3C( "OP3C:UNKNOWN ", "Opcode 0x3C Unknown", 0x3C ),
OP3D( "OP3D:UNKNOWN ", "Opcode 0x3D Unknown", 0x3D ),
OP3E( "OP3E:UNKNOWN ", "Opcode 0x3E Unknown", 0x3E ),
OP3F( "OP3F:UNKNOWN ", "Opcode 0x3F Unknown", 0x3F ),
UNKNOWN( "UNKNOWN ", "Unknown", -1 );
private String mLabel;
@@ -23,6 +84,11 @@ public enum MotorolaOpcode
return mLabel;
}
public String toString()
{
return getLabel();
}
public String getDescription()
{
return mDescription;
@@ -35,14 +101,9 @@ public enum MotorolaOpcode
public static MotorolaOpcode fromValue( int value )
{
switch( value )
if( 0 <= value && value <= 0x3F )
{
case 0x05:
return UNKNOWN_OPCODE_5;
case 0x09:
return SYSTEM_LOADING;
case 0x0B:
return CHANNEL_CWID_UPDATE;
return MotorolaOpcode.values()[ value ];
}
return UNKNOWN;

View File

@@ -16,7 +16,7 @@ public class SystemLoading extends MotorolaTSBKMessage
@Override
public String getEventType()
{
return MotorolaOpcode.SYSTEM_LOADING.getLabel();
return MotorolaOpcode.OP09.getLabel();
}
@Override

View File

@@ -52,7 +52,7 @@ public class LocationRegistrationResponse extends TSBKMessage
public String getGroupAddress()
{
return mMessage.getHex( GROUP_ADDRESS, 6 );
return mMessage.getHex( GROUP_ADDRESS, 4 );
}
public String getRFSSID()

View File

@@ -54,7 +54,8 @@ public class TimeAndDateAnnouncement extends TSBKMessage
{
sb.append( " DATE-TIME:" );
}
sb.append( " " );
sb.append( mTimeFormatter.format( new Date( getDateTimestamp() ) ) );
return sb.toString();

View File

@@ -20,4 +20,5 @@ public class GroupVoiceChannelGrant extends GroupChannelGrant
{
return Opcode.GROUP_VOICE_CHANNEL_GRANT.getDescription();
}
}

View File

@@ -30,7 +30,7 @@ public class TelephoneInterconnectVoiceChannelGrant extends UnitChannelGrant
}
sb.append( " CHAN:" );
sb.append( getChannelID() + "/" + getChannelNumber() );
sb.append( getChannelIdentifier() + "/" + getChannel() );
sb.append( " CALL TIMER:" );
sb.append( getCallTimer() );

View File

@@ -3,6 +3,7 @@ package decode.p25.message.tsbk.osp.voice;
import alias.AliasList;
import bits.BitSetBuffer;
import decode.p25.message.tsbk.UnitChannelGrant;
import decode.p25.message.tsbk.osp.control.IdentifierUpdate;
import decode.p25.reference.DataUnitID;
import decode.p25.reference.Opcode;
@@ -20,4 +21,28 @@ public class UnitToUnitVoiceChannelGrant extends UnitChannelGrant
{
return Opcode.UNIT_TO_UNIT_VOICE_CHANNEL_GRANT.getDescription();
}
public String getMessage()
{
StringBuilder sb = new StringBuilder();
sb.append( getMessageStub() );
if( isEmergency() )
{
sb.append( " EMERGENCY" );
}
sb.append( " SOURCE UNIT:" );
sb.append( getSourceAddress() );
sb.append( " TARGET UNIT:" );
sb.append( getTargetAddress() );
sb.append( " CHAN:" + getChannelIdentifier() + "-" + getChannel() );
sb.append( " DN:" + getDownlinkFrequency() );
sb.append( " UP:" + getUplinkFrequency() );
return sb.toString();
}
}

View File

@@ -0,0 +1,111 @@
package decode.p25.message.tsbk.vendor;
public enum VendorOpcode
{
OP00( "OPCODE 00 ", "VENDOR OPCODE 0x00", 0x00 ),
OP01( "OPCODE 01 ", "VENDOR OPCODE 0x01", 0x01 ),
OP02( "OPCODE 02 ", "VENDOR OPCODE 0x02", 0x02 ),
OP03( "OPCODE 03 ", "VENDOR OPCODE 0x03", 0x03 ),
OP04( "OPCODE 04 ", "VENDOR OPCODE 0x04", 0x04 ),
OP05( "OPCODE 05 ", "VENDOR OPCODE 0x05", 0x05 ),
OP06( "OPCODE 06 ", "VENDOR OPCODE 0x06", 0x06 ),
OP07( "OPCODE 07 ", "VENDOR OPCODE 0x07", 0x07 ),
OP08( "OPCODE 08 ", "VENDOR OPCODE 0x08", 0x08 ),
OP09( "OPCODE 09 ", "VENDOR OPCODE 0x09", 0x09 ),
OP0A( "OPCODE 0A ", "VENDOR OPCODE 0x0A", 0x0A ),
OP0B( "OPCODE 0B ", "VENDOR OPCODE 0x0B", 0x0B ),
OP0C( "OPCODE 0C ", "VENDOR OPCODE 0x0C", 0x0C ),
OP0D( "OPCODE 0D ", "VENDOR OPCODE 0x0D", 0x0D ),
OP0E( "OPCODE 0E ", "VENDOR OPCODE 0x0E", 0x0E ),
OP0F( "OPCODE 0F ", "VENDOR OPCODE 0x0F", 0x0F ),
OP10( "OPCODE 10 ", "VENDOR OPCODE 0x10", 0x10 ),
OP11( "OPCODE 11 ", "VENDOR OPCODE 0x11", 0x11 ),
OP12( "OPCODE 12 ", "VENDOR OPCODE 0x12", 0x12 ),
OP13( "OPCODE 13 ", "VENDOR OPCODE 0x13", 0x13 ),
OP14( "OPCODE 14 ", "VENDOR OPCODE 0x14", 0x14 ),
OP15( "OPCODE 15 ", "VENDOR OPCODE 0x15", 0x15 ),
OP16( "OPCODE 16 ", "VENDOR OPCODE 0x16", 0x16 ),
OP17( "OPCODE 17 ", "VENDOR OPCODE 0x17", 0x17 ),
OP18( "OPCODE 18 ", "VENDOR OPCODE 0x18", 0x18 ),
OP19( "OPCODE 19 ", "VENDOR OPCODE 0x19", 0x19 ),
OP1A( "OPCODE 1A ", "VENDOR OPCODE 0x1A", 0x1A ),
OP1B( "OPCODE 1B ", "VENDOR OPCODE 0x1B", 0x1B ),
OP1C( "OPCODE 1C ", "VENDOR OPCODE 0x1C", 0x1C ),
OP1D( "OPCODE 1D ", "VENDOR OPCODE 0x1D", 0x1D ),
OP1E( "OPCODE 1E ", "VENDOR OPCODE 0x1E", 0x1E ),
OP1F( "OPCODE 1F ", "VENDOR OPCODE 0x1F", 0x1F ),
OP20( "OPCODE 20 ", "VENDOR OPCODE 0x20", 0x20 ),
OP21( "OPCODE 21 ", "VENDOR OPCODE 0x21", 0x21 ),
OP22( "OPCODE 22 ", "VENDOR OPCODE 0x22", 0x22 ),
OP23( "OPCODE 23 ", "VENDOR OPCODE 0x23", 0x23 ),
OP24( "OPCODE 24 ", "VENDOR OPCODE 0x24", 0x24 ),
OP25( "OPCODE 25 ", "VENDOR OPCODE 0x25", 0x25 ),
OP26( "OPCODE 26 ", "VENDOR OPCODE 0x26", 0x26 ),
OP27( "OPCODE 27 ", "VENDOR OPCODE 0x27", 0x27 ),
OP28( "OPCODE 28 ", "VENDOR OPCODE 0x28", 0x28 ),
OP29( "OPCODE 29 ", "VENDOR OPCODE 0x29", 0x29 ),
OP2A( "OPCODE 2A ", "VENDOR OPCODE 0x2A", 0x2A ),
OP2B( "OPCODE 2B ", "VENDOR OPCODE 0x2B", 0x2B ),
OP2C( "OPCODE 2C ", "VENDOR OPCODE 0x2C", 0x2C ),
OP2D( "OPCODE 2D ", "VENDOR OPCODE 0x2D", 0x2D ),
OP2E( "OPCODE 2E ", "VENDOR OPCODE 0x2E", 0x2E ),
OP2F( "OPCODE 2F ", "VENDOR OPCODE 0x2F", 0x2F ),
OP30( "OPCODE 30 ", "VENDOR OPCODE 0x30", 0x30 ),
OP31( "OPCODE 31 ", "VENDOR OPCODE 0x31", 0x31 ),
OP32( "OPCODE 32 ", "VENDOR OPCODE 0x32", 0x32 ),
OP33( "OPCODE 33 ", "VENDOR OPCODE 0x33", 0x33 ),
OP34( "OPCODE 34 ", "VENDOR OPCODE 0x34", 0x34 ),
OP35( "OPCODE 35 ", "VENDOR OPCODE 0x35", 0x35 ),
OP36( "OPCODE 36 ", "VENDOR OPCODE 0x36", 0x36 ),
OP37( "OPCODE 37 ", "VENDOR OPCODE 0x37", 0x37 ),
OP38( "OPCODE 38 ", "VENDOR OPCODE 0x38", 0x38 ),
OP39( "OPCODE 39 ", "VENDOR OPCODE 0x39", 0x39 ),
OP3A( "OPCODE 3A ", "VENDOR OPCODE 0x3A", 0x3A ),
OP3B( "OPCODE 3B ", "VENDOR OPCODE 0x3B", 0x3B ),
OP3C( "OPCODE 3C ", "VENDOR OPCODE 0x3C", 0x3C ),
OP3D( "OPCODE 3D ", "VENDOR OPCODE 0x3D", 0x3D ),
OP3E( "OPCODE 3E ", "VENDOR OPCODE 0x3E", 0x3E ),
OP3F( "OPCODE 3F ", "VENDOR OPCODE 0x3F", 0x3F ),
UNKNOWN( "UNKNOWN ", "Unknown", -1 );
private String mLabel;
private String mDescription;
private int mCode;
private VendorOpcode( String label, String description, int code )
{
mLabel = label;
mDescription = description;
mCode = code;
}
public String getLabel()
{
return mLabel;
}
public String toString()
{
return getLabel();
}
public String getDescription()
{
return mDescription;
}
public int getCode()
{
return mCode;
}
public static VendorOpcode fromValue( int value )
{
if( 0 <= value && value <= 0x3F )
{
return VendorOpcode.values()[ value ];
}
return UNKNOWN;
}
}

View File

@@ -2,13 +2,13 @@ package decode.p25.reference;
public enum ServiceAccessPoint
{
SAP_0( "0", 0 ),
SAP_1( "1", 1 ),
SAP_2( "2", 2 ),
SAP_3( "3", 3 ),
SAP_4( "4", 4 ),
SAP_5( "5", 5 ),
SAP_6( "6", 6 ),
UNENCRYPTED_USER_DATA( "0", 0 ),
ENCRYPTED_USER_DATA( "1", 1 ),
CIRCUIT_DATA( "2", 2 ),
CIRCUIT_DATA_CONTROL( "3", 3 ),
PACKET_DATA( "4", 4 ),
ADDRESS_RESOLUTION_PROTOCOL( "5", 5 ),
SNDCP_PACKET_DATA_CONTROL( "6", 6 ),
SAP_7( "7", 7 ),
SAP_8( "8", 8 ),
SAP_9( "9", 9 ),
@@ -33,17 +33,17 @@ public enum ServiceAccessPoint
SAP_28( "28", 28 ),
SAP_29( "29", 29 ),
SAP_30( "30", 30 ),
SAP_31( "31", 31 ),
SAP_32( "32", 32 ),
SAP_33( "33", 33 ),
SAP_34( "34", 34 ),
SAP_35( "35", 35 ),
SAP_36( "36", 36 ),
SAP_37( "37", 37 ),
SAP_38( "38", 38 ),
SAP_39( "39", 39 ),
SAP_40( "40", 40 ),
SAP_41( "41", 41 ),
EXTENDED_ADDRESS( "31", 31 ),
REGISTRATION_AND_AUTHORIZATION( "32", 32 ),
CHANNEL_REASSIGNMENT( "33", 33 ),
SYSTEM_CONFIGURATION( "34", 34 ),
MR_LOOPBACK( "35", 35 ),
MR_STATISTICS( "36", 36 ),
MR_OUT_OF_SERVICE( "37", 37 ),
MR_PAGING( "38", 38 ),
MR_CONFIGURATION( "39", 39 ),
UNENCRYPTED_KEY_MANAGEMENT_MESSAGE( "40", 40 ),
ENCRYPTED_KEY_MANAGEMENT_MESSAGE( "41", 41 ),
SAP_42( "42", 42 ),
SAP_43( "43", 43 ),
SAP_44( "44", 44 ),