diff --git a/src/decode/DecoderType.java b/src/decode/DecoderType.java
index 2a96eb57..8487051b 100644
--- a/src/decode/DecoderType.java
+++ b/src/decode/DecoderType.java
@@ -60,7 +60,7 @@ public enum DecoderType
DecoderType.LTR_STANDARD,
DecoderType.MPT1327,
DecoderType.NBFM,
-// DecoderType.P25_PHASE1,
+ DecoderType.P25_PHASE1,
DecoderType.PASSPORT );
}
diff --git a/src/decode/p25/P25CallEvent.java b/src/decode/p25/P25CallEvent.java
new file mode 100644
index 00000000..22dfdaad
--- /dev/null
+++ b/src/decode/p25/P25CallEvent.java
@@ -0,0 +1,154 @@
+/*******************************************************************************
+ * 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
+ ******************************************************************************/
+package decode.p25;
+
+import alias.Alias;
+import alias.AliasList;
+import controller.activity.CallEvent;
+import decode.DecoderType;
+
+public class P25CallEvent extends CallEvent
+{
+ private int mChannel;
+ private long mFrequency;
+
+ public P25CallEvent( CallEventType callEventType,
+ AliasList aliasList,
+ String fromID,
+ String toID,
+ int channel,
+ long frequency,
+ String details )
+ {
+ super( DecoderType.MPT1327, callEventType,
+ aliasList, fromID, toID, details );
+
+ mChannel = channel;
+ mFrequency = frequency;
+ }
+
+ private Alias getAlias( String mpt1327Talkgroup )
+ {
+ if( hasAliasList() )
+ {
+ return getAliasList().getMPT1327Alias( mpt1327Talkgroup );
+ }
+
+ return null;
+ }
+
+ @Override
+ public Alias getFromIDAlias()
+ {
+ return getAlias( getFromID() );
+ }
+
+ @Override
+ public Alias getToIDAlias()
+ {
+ return getAlias( getToID() );
+ }
+
+ @Override
+ public int getChannel()
+ {
+ return mChannel;
+ }
+
+ @Override
+ public long getFrequency()
+ {
+ return mFrequency;
+ }
+
+ public static class Builder
+ {
+ /* Required parameters */
+ private CallEventType mCallEventType;
+
+ /* Optional parameters */
+ private AliasList mAliasList;
+ private String mFromID;
+ private String mToID;
+ private String mDetails;
+ private int mChannel;
+ private long mFrequency;
+
+ public Builder( CallEventType callEventType )
+ {
+ mCallEventType = callEventType;
+ }
+
+ public Builder aliasList( AliasList aliasList )
+ {
+ mAliasList = aliasList;
+ return this;
+ }
+
+ public Builder from( String val )
+ {
+ mFromID = val;
+ return this;
+ }
+
+ public Builder channel( int channel )
+ {
+ mChannel = channel;
+ return this;
+ }
+
+ public Builder details( String details )
+ {
+ mDetails = details;
+ return this;
+ }
+
+ public Builder frequency( long frequency )
+ {
+ mFrequency = frequency;
+ return this;
+ }
+
+ public Builder to( String toID )
+ {
+ mToID = toID;
+ return this;
+ }
+
+ public P25CallEvent build()
+ {
+ return new P25CallEvent( this );
+ }
+
+ }
+
+ /**
+ * Private constructor for the builder
+ */
+ private P25CallEvent( Builder builder )
+ {
+ this( builder.mCallEventType,
+ builder.mAliasList,
+ builder.mFromID,
+ builder.mToID,
+ builder.mChannel,
+ builder.mFrequency,
+ builder.mDetails );
+ }
+
+}
diff --git a/src/decode/p25/P25ChannelState.java b/src/decode/p25/P25ChannelState.java
index 4603c32e..bc23528b 100644
--- a/src/decode/p25/P25ChannelState.java
+++ b/src/decode/p25/P25ChannelState.java
@@ -17,12 +17,14 @@
******************************************************************************/
package decode.p25;
+import message.Message;
import alias.AliasList;
import audio.SquelchListener;
import audio.SquelchListener.SquelchState;
import controller.channel.ProcessingChain;
import controller.state.AuxChannelState;
import controller.state.ChannelState;
+import decode.p25.message.P25Message;
public class P25ChannelState extends ChannelState
{
@@ -31,21 +33,30 @@ public class P25ChannelState extends ChannelState
super( chain, aliasList );
}
- /**
- * Make the ConventionalChannelState always unsquelched
- */
public void addListener( SquelchListener listener )
{
super.addListener( listener );
super.setSquelchState( SquelchState.UNSQUELCH );
}
+
+ public void receive( Message message )
+ {
+ super.receive( message );
+
+ if( message instanceof P25Message )
+ {
+
+ }
+ }
+
@Override
public void setSquelchState( SquelchState state )
{
//do nothing ... we want the squelch state always unsquelched (for now)
}
+
@Override
public String getActivitySummary()
{
diff --git a/src/decode/p25/P25Decoder.java b/src/decode/p25/P25Decoder.java
index edced6fb..9d7ec70c 100644
--- a/src/decode/p25/P25Decoder.java
+++ b/src/decode/p25/P25Decoder.java
@@ -85,6 +85,7 @@ public class P25Decoder extends Decoder implements Instrumentable
mSymbolFilter.setListener( mSlicer );
mMessageProcessor = new P25MessageProcessor( mAliasList );
+ mMessageProcessor.addMessageListener( this );
mNormalFramer = new P25MessageFramer(
FrameSync.P25_PHASE1.getSync(), 64, false, mAliasList );
diff --git a/src/decode/p25/P25MessageProcessor.java b/src/decode/p25/P25MessageProcessor.java
index 993fce9c..2afb8536 100644
--- a/src/decode/p25/P25MessageProcessor.java
+++ b/src/decode/p25/P25MessageProcessor.java
@@ -1,19 +1,35 @@
package decode.p25;
+import java.util.Arrays;
+import java.util.HashMap;
+
import message.Message;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import bits.BitSetBuffer;
+import sample.Broadcaster;
import sample.Listener;
import alias.AliasList;
+import decode.mpt1327.MPT1327Message;
import decode.p25.message.tsbk.TSBKMessage;
+import decode.p25.message.tsbk.osp.control.IdentifierUpdate;
+import decode.p25.message.tsbk.osp.control.IdentifierUpdateReceiver;
public class P25MessageProcessor implements Listener
{
private final static Logger mLog =
LoggerFactory.getLogger( P25MessageProcessor.class );
+ private Broadcaster mBroadcaster = new Broadcaster();
+
+ /* Map of up to 16 band identifiers per RFSS. These identifier update
+ * messages are inserted into any message that conveys channel information
+ * so that the uplink/downlink frequencies can be calculated */
+ private HashMap mIdentifierMap =
+ new HashMap();
+
private AliasList mAliasList;
public P25MessageProcessor( AliasList aliasList )
@@ -24,6 +40,29 @@ public class P25MessageProcessor implements Listener
@Override
public void receive( Message message )
{
+ /* Insert band identifier update messages into channel-type messages */
+ if( message instanceof IdentifierUpdateReceiver )
+ {
+ IdentifierUpdateReceiver receiver = (IdentifierUpdateReceiver)message;
+
+ int[] identifiers = receiver.getIdentifiers();
+
+ for( int identifier: identifiers )
+ {
+ receiver.setIdentifierMessage( identifier,
+ mIdentifierMap.get( identifier ) );
+ }
+ }
+
+ /* Store band identifiers so that they can be injected into channel
+ * type messages */
+ if( message instanceof IdentifierUpdate )
+ {
+ IdentifierUpdate identifierUpdate = (IdentifierUpdate)message;
+
+ mIdentifierMap.put( identifierUpdate.getIdentifier(), identifierUpdate );
+ }
+
if( message instanceof TSBKMessage )
{
mLog.debug( message.getMessage() );
@@ -32,5 +71,23 @@ public class P25MessageProcessor implements Listener
{
mLog.debug( message.getMessage() + "\t" + message.getBinaryMessage() );
}
+
+ mBroadcaster.broadcast( message );
+ }
+
+ public void dispose()
+ {
+ mIdentifierMap.clear();
+ mBroadcaster.dispose();
+ }
+
+ public void addMessageListener( Listener listener )
+ {
+ mBroadcaster.addListener( listener );
+ }
+
+ public void removeMessageListener( Listener listener )
+ {
+ mBroadcaster.removeListener( listener );
}
}
diff --git a/src/decode/p25/message/tsbk/ChannelGrant.java b/src/decode/p25/message/tsbk/ChannelGrant.java
index 2750725d..575f4c3a 100644
--- a/src/decode/p25/message/tsbk/ChannelGrant.java
+++ b/src/decode/p25/message/tsbk/ChannelGrant.java
@@ -4,49 +4,20 @@ import alias.AliasList;
import bits.BitSetBuffer;
import decode.p25.reference.DataUnitID;
-public abstract class ChannelGrant extends TSBKMessage
+public abstract class ChannelGrant extends ServiceMessage
{
- /* Service Options */
- public static final int EMERGENCY_FLAG = 80;
- public static final int ENCRYPTED_CHANNEL_FLAG = 81;
- public static final int DUPLEX_MODE = 82;
- public static final int SESSION_MODE = 83;
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 };
- public enum DuplexMode { HALF, FULL };
- public enum SessionMode { PACKET, CIRCUIT };
-
public ChannelGrant( BitSetBuffer message,
- DataUnitID duid,
- AliasList aliasList )
+ DataUnitID duid,
+ AliasList aliasList )
{
super( message, duid, aliasList );
}
- public boolean isEmergency()
- {
- return mMessage.get( EMERGENCY_FLAG );
- }
-
- public boolean isEncryptedChannel()
- {
- return mMessage.get( ENCRYPTED_CHANNEL_FLAG );
- }
-
- public DuplexMode getDuplexMode()
- {
- return mMessage.get( DUPLEX_MODE ) ? DuplexMode.FULL : DuplexMode.HALF;
- }
-
- public SessionMode getSessionMode()
- {
- return mMessage.get( SESSION_MODE ) ?
- SessionMode.CIRCUIT : SessionMode.PACKET;
- }
-
/**
* 1 = Lowest, 4 = Default, 7 = Highest
*/
diff --git a/src/decode/p25/message/tsbk/ServiceMessage.java b/src/decode/p25/message/tsbk/ServiceMessage.java
new file mode 100644
index 00000000..ec8aa8e4
--- /dev/null
+++ b/src/decode/p25/message/tsbk/ServiceMessage.java
@@ -0,0 +1,45 @@
+package decode.p25.message.tsbk;
+
+import alias.AliasList;
+import bits.BitSetBuffer;
+import decode.p25.reference.DataUnitID;
+
+public abstract class ServiceMessage extends TSBKMessage
+{
+ /* Service Options */
+ public static final int EMERGENCY_FLAG = 80;
+ public static final int ENCRYPTED_CHANNEL_FLAG = 81;
+ public static final int DUPLEX_MODE = 82;
+ public static final int SESSION_MODE = 83;
+
+ public enum DuplexMode { HALF, FULL };
+ public enum SessionMode { PACKET, CIRCUIT };
+
+ public ServiceMessage( BitSetBuffer message,
+ DataUnitID duid,
+ AliasList aliasList )
+ {
+ super( message, duid, aliasList );
+ }
+
+ public boolean isEmergency()
+ {
+ return mMessage.get( EMERGENCY_FLAG );
+ }
+
+ public boolean isEncryptedChannel()
+ {
+ return mMessage.get( ENCRYPTED_CHANNEL_FLAG );
+ }
+
+ public DuplexMode getDuplexMode()
+ {
+ return mMessage.get( DUPLEX_MODE ) ? DuplexMode.FULL : DuplexMode.HALF;
+ }
+
+ public SessionMode getSessionMode()
+ {
+ return mMessage.get( SESSION_MODE ) ?
+ SessionMode.CIRCUIT : SessionMode.PACKET;
+ }
+}
diff --git a/src/decode/p25/message/tsbk/TSBKMessage.java b/src/decode/p25/message/tsbk/TSBKMessage.java
index fec05dbc..42b0f025 100644
--- a/src/decode/p25/message/tsbk/TSBKMessage.java
+++ b/src/decode/p25/message/tsbk/TSBKMessage.java
@@ -3,6 +3,7 @@ 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.reference.DataUnitID;
import decode.p25.reference.Opcode;
import decode.p25.reference.Vendor;
@@ -128,4 +129,27 @@ public class TSBKMessage extends P25Message
return sb.toString();
}
+
+ protected static long calculateUplink( IdentifierUpdate iden, int channel )
+ {
+ long downlink = calculateDownlink( iden, channel );
+
+ if( downlink > 0 && iden != null )
+ {
+ return downlink + iden.getTransmitOffset();
+ }
+
+ return 0;
+ }
+
+ protected static long calculateDownlink( IdentifierUpdate iden, int channel )
+ {
+ if( iden != null )
+ {
+ return iden.getBaseFrequency() +
+ ( channel * iden.getChannelSpacing() );
+ }
+
+ return 0;
+ }
}
diff --git a/src/decode/p25/message/tsbk/TSBKMessageFactory.java b/src/decode/p25/message/tsbk/TSBKMessageFactory.java
index fee8444a..b18631ba 100644
--- a/src/decode/p25/message/tsbk/TSBKMessageFactory.java
+++ b/src/decode/p25/message/tsbk/TSBKMessageFactory.java
@@ -2,6 +2,10 @@ package decode.p25.message.tsbk;
import alias.AliasList;
import bits.BitSetBuffer;
+import decode.p25.message.tsbk.motorola.ChannelCWIDUpdate;
+import decode.p25.message.tsbk.motorola.MotorolaOpcode;
+import decode.p25.message.tsbk.motorola.MotorolaTSBKMessage;
+import decode.p25.message.tsbk.motorola.SystemLoading;
import decode.p25.message.tsbk.osp.control.AcknowledgeResponse;
import decode.p25.message.tsbk.osp.control.AdjacentStatusBroadcast;
import decode.p25.message.tsbk.osp.control.AuthenticationCommand;
@@ -10,7 +14,8 @@ import decode.p25.message.tsbk.osp.control.DenyResponse;
import decode.p25.message.tsbk.osp.control.ExtendedFunctionCommand;
import decode.p25.message.tsbk.osp.control.GroupAffiliationQuery;
import decode.p25.message.tsbk.osp.control.GroupAffiliationResponse;
-import decode.p25.message.tsbk.osp.control.IdentifierUpdate;
+import decode.p25.message.tsbk.osp.control.IdentifierUpdateNonVUHF;
+import decode.p25.message.tsbk.osp.control.IdentifierUpdateVUHF;
import decode.p25.message.tsbk.osp.control.LocationRegistrationResponse;
import decode.p25.message.tsbk.osp.control.MessageUpdate;
import decode.p25.message.tsbk.osp.control.NetworkStatusBroadcast;
@@ -19,9 +24,11 @@ import decode.p25.message.tsbk.osp.control.RFSSStatusBroadcast;
import decode.p25.message.tsbk.osp.control.RadioUnitMonitorCommand;
import decode.p25.message.tsbk.osp.control.RoamingAddressCommand;
import decode.p25.message.tsbk.osp.control.SecondaryControlChannelBroadcast;
+import decode.p25.message.tsbk.osp.control.SecondaryControlChannelBroadcastExplicit;
import decode.p25.message.tsbk.osp.control.StatusQuery;
import decode.p25.message.tsbk.osp.control.StatusUpdate;
import decode.p25.message.tsbk.osp.control.SystemServiceBroadcast;
+import decode.p25.message.tsbk.osp.control.TimeAndDateAnnouncement;
import decode.p25.message.tsbk.osp.control.UnitDeregistrationAcknowledge;
import decode.p25.message.tsbk.osp.control.UnitRegistrationCommand;
import decode.p25.message.tsbk.osp.control.UnitRegistrationResponse;
@@ -29,6 +36,9 @@ import decode.p25.message.tsbk.osp.data.GroupDataChannelAnnouncement;
import decode.p25.message.tsbk.osp.data.GroupDataChannelAnnouncementExplicit;
import decode.p25.message.tsbk.osp.data.GroupDataChannelGrant;
import decode.p25.message.tsbk.osp.data.IndividualDataChannelGrant;
+import decode.p25.message.tsbk.osp.data.SNDCPDataChannelAnnouncementExplicit;
+import decode.p25.message.tsbk.osp.data.SNDCPDataChannelGrant;
+import decode.p25.message.tsbk.osp.data.SNDCPDataPageRequest;
import decode.p25.message.tsbk.osp.voice.GroupVoiceChannelGrant;
import decode.p25.message.tsbk.osp.voice.GroupVoiceChannelGrantUpdate;
import decode.p25.message.tsbk.osp.voice.GroupVoiceChannelGrantUpdateExplicit;
@@ -47,14 +57,15 @@ public class TSBKMessageFactory
DataUnitID duid,
AliasList aliasList )
{
- Opcode opcode = Opcode.fromValue( message.getInt( TSBKMessage.OPCODE ) );
-
Vendor vendor = Vendor.fromValue(
message.getInt( TSBKMessage.VENDOR_ID ) );
switch( vendor )
{
case STANDARD:
+ Opcode opcode =
+ Opcode.fromValue( message.getInt( TSBKMessage.OPCODE ) );
+
switch( opcode )
{
case ACKNOWLEDGE_RESPONSE_FNE:
@@ -97,9 +108,12 @@ public class TSBKMessageFactory
case GROUP_VOICE_CHANNEL_GRANT_UPDATE_EXPLICIT:
return new GroupVoiceChannelGrantUpdateExplicit( message,
duid, aliasList );
- case IDENTIFIER_UPDATE:
+ case IDENTIFIER_UPDATE_NON_VUHF:
+ return new IdentifierUpdateNonVUHF( message, duid,
+ aliasList );
case IDENTIFIER_UPDATE_VHF_UHF_BANDS:
- return new IdentifierUpdate( message, duid, aliasList );
+ return new IdentifierUpdateVUHF( message, duid,
+ aliasList );
case INDIVIDUAL_DATA_CHANNEL_GRANT:
return new IndividualDataChannelGrant( message, duid,
aliasList );
@@ -126,6 +140,18 @@ public class TSBKMessageFactory
case SECONDARY_CONTROL_CHANNEL_BROADCAST:
return new SecondaryControlChannelBroadcast( message,
duid, aliasList );
+ case SECONDARY_CONTROL_CHANNEL_BROADCAST_EXPLICIT:
+ return new SecondaryControlChannelBroadcastExplicit(
+ message, duid, aliasList );
+ case SNDCP_DATA_CHANNEL_GRANT:
+ return new SNDCPDataChannelGrant( message, duid,
+ aliasList );
+ case SNDCP_DATA_CHANNEL_ANNOUNCEMENT_EXPLICIT:
+ return new SNDCPDataChannelAnnouncementExplicit(
+ message, duid, aliasList );
+ case SNDCP_DATA_PAGE_REQUEST:
+ return new SNDCPDataPageRequest( message, duid,
+ aliasList );
case STATUS_QUERY:
return new StatusQuery( message, duid, aliasList );
case STATUS_UPDATE:
@@ -139,6 +165,9 @@ public class TSBKMessageFactory
case TELEPHONE_INTERCONNECT_VOICE_CHANNEL_GRANT:
return new TelephoneInterconnectVoiceChannelGrant(
message, duid, aliasList );
+ case TIME_DATE_ANNOUNCEMENT:
+ return new TimeAndDateAnnouncement( message, duid,
+ aliasList );
case UNIT_DEREGISTRATION_ACKNOWLEDGE:
return new UnitDeregistrationAcknowledge( message,
duid, aliasList );
@@ -162,6 +191,19 @@ public class TSBKMessageFactory
}
case MOTOROLA:
+ MotorolaOpcode motorolaOpcode = MotorolaOpcode.
+ fromValue( message.getInt( TSBKMessage.OPCODE ) );
+
+ switch( motorolaOpcode )
+ {
+ case CHANNEL_CWID_UPDATE:
+ return new ChannelCWIDUpdate( message, duid, aliasList );
+ case SYSTEM_LOADING:
+ return new SystemLoading( message, duid, aliasList );
+ default:
+ }
+
+ return new MotorolaTSBKMessage( message, duid, aliasList );
default:
return new TSBKMessage( message, duid, aliasList );
}
diff --git a/src/decode/p25/message/tsbk/motorola/ChannelCWIDUpdate.java b/src/decode/p25/message/tsbk/motorola/ChannelCWIDUpdate.java
new file mode 100644
index 00000000..f1a91ff9
--- /dev/null
+++ b/src/decode/p25/message/tsbk/motorola/ChannelCWIDUpdate.java
@@ -0,0 +1,184 @@
+package decode.p25.message.tsbk.motorola;
+
+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 class ChannelCWIDUpdate extends MotorolaTSBKMessage
+ implements IdentifierUpdateReceiver
+{
+ public static final int[] CHARACTER_1 = { 80,81,82,83,84,85 };
+ public static final int[] CHARACTER_2 = { 86,87,88,89,90,91 };
+ public static final int[] CHARACTER_3 = { 92,93,94,95,96,97 };
+ public static final int[] CHARACTER_4 = { 98,99,100,101,102,103 };
+ public static final int[] CHARACTER_5 = { 104,105,106,107,108,109 };
+ public static final int[] CHARACTER_6 = { 110,111,112,113,114,115 };
+ public static final int[] CHARACTER_7 = { 116,117,118,119,120,121 };
+ public static final int[] CHARACTER_8 = { 122,123,124,125,126,127 };
+
+ public static final int[] IDENTIFIER = { 128,129,130,131 };
+ public static final int[] CHANNEL = { 132,133,134,135,136,137,138,139,140,
+ 141,142,143 };
+
+ private IdentifierUpdate mIdentifierUpdate;
+
+ public ChannelCWIDUpdate( BitSetBuffer message,
+ DataUnitID duid,
+ AliasList aliasList )
+ {
+ super( message, duid, aliasList );
+ }
+
+ @Override
+ public String getEventType()
+ {
+ return MotorolaOpcode.CHANNEL_CWID_UPDATE.getDescription();
+ }
+
+ public String getMessage()
+ {
+ StringBuilder sb = new StringBuilder();
+
+ sb.append( getMessageStub() );
+
+ sb.append( " CHAN:" + getIdentifier() + "-" + getChannel() );
+
+ sb.append( " CWID:" + getCWID() );
+
+ sb.append( " DN:" + getDownlinkFrequency() );
+
+ sb.append( " UP:" + getUplinkFrequency() );
+
+ return sb.toString();
+ }
+
+
+ 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 ) );
+ }
+
+ return sb.toString();
+ }
+
+ public int getIdentifier()
+ {
+ return mMessage.getInt( IDENTIFIER );
+ }
+
+ public int getChannel()
+ {
+ return mMessage.getInt( CHANNEL );
+ }
+
+ public long getUplinkFrequency()
+ {
+ return calculateUplink( mIdentifierUpdate, getChannel() );
+ }
+
+ public long getDownlinkFrequency()
+ {
+ return calculateDownlink( mIdentifierUpdate, getChannel() );
+ }
+
+ @Override
+ public void setIdentifierMessage( int identifier, IdentifierUpdate message )
+ {
+ mIdentifierUpdate = message;
+ }
+
+ @Override
+ public int[] getIdentifiers()
+ {
+ int[] identifiers = new int[ 1 ];
+
+ identifiers[ 0 ] = getIdentifier();
+
+ return identifiers;
+ }
+}
diff --git a/src/decode/p25/message/tsbk/motorola/MotorolaOpcode.java b/src/decode/p25/message/tsbk/motorola/MotorolaOpcode.java
new file mode 100644
index 00000000..b1e1b7ff
--- /dev/null
+++ b/src/decode/p25/message/tsbk/motorola/MotorolaOpcode.java
@@ -0,0 +1,50 @@
+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 ),
+ UNKNOWN( "UNKNOWN ", "Unknown", -1 );
+
+ private String mLabel;
+ private String mDescription;
+ private int mCode;
+
+ private MotorolaOpcode( String label, String description, int code )
+ {
+ mLabel = label;
+ mDescription = description;
+ mCode = code;
+ }
+
+ public String getLabel()
+ {
+ return mLabel;
+ }
+
+ public String getDescription()
+ {
+ return mDescription;
+ }
+
+ public int getCode()
+ {
+ return mCode;
+ }
+
+ public static MotorolaOpcode fromValue( int value )
+ {
+ switch( value )
+ {
+ case 0x05:
+ return UNKNOWN_OPCODE_5;
+ case 0x09:
+ return SYSTEM_LOADING;
+ case 0x0B:
+ return CHANNEL_CWID_UPDATE;
+ }
+
+ return UNKNOWN;
+ }
+}
diff --git a/src/decode/p25/message/tsbk/motorola/MotorolaTSBKMessage.java b/src/decode/p25/message/tsbk/motorola/MotorolaTSBKMessage.java
new file mode 100644
index 00000000..d73b6394
--- /dev/null
+++ b/src/decode/p25/message/tsbk/motorola/MotorolaTSBKMessage.java
@@ -0,0 +1,42 @@
+package decode.p25.message.tsbk.motorola;
+
+import alias.AliasList;
+import bits.BitSetBuffer;
+import decode.p25.message.tsbk.TSBKMessage;
+import decode.p25.reference.DataUnitID;
+
+public class MotorolaTSBKMessage extends TSBKMessage
+{
+ public MotorolaTSBKMessage( BitSetBuffer message, DataUnitID duid,
+ AliasList aliasList )
+ {
+ super( message, duid, aliasList );
+ }
+
+ protected String getMessageStub()
+ {
+ StringBuilder sb = new StringBuilder();
+
+ sb.append( "SYS:" );
+ sb.append( getNAC() ); /* NAC is the system id for TSBK messages */
+ sb.append( " " );
+ sb.append( getDUID().getLabel() );
+
+ sb.append( " MOTOROLA" );
+
+ if( isEncrypted() )
+ {
+ sb.append( " ENCRYPTED" );
+ }
+
+ sb.append( " OPCD:" );
+ sb.append( getMotorolaOpcode().getLabel() );
+
+ return sb.toString();
+ }
+
+ public MotorolaOpcode getMotorolaOpcode()
+ {
+ return MotorolaOpcode.fromValue( mMessage.getInt( OPCODE ) );
+ }
+}
diff --git a/src/decode/p25/message/tsbk/motorola/SystemLoading.java b/src/decode/p25/message/tsbk/motorola/SystemLoading.java
new file mode 100644
index 00000000..c5885872
--- /dev/null
+++ b/src/decode/p25/message/tsbk/motorola/SystemLoading.java
@@ -0,0 +1,34 @@
+package decode.p25.message.tsbk.motorola;
+
+import alias.AliasList;
+import bits.BitSetBuffer;
+import decode.p25.message.tsbk.TSBKMessage;
+import decode.p25.reference.DataUnitID;
+
+public class SystemLoading extends MotorolaTSBKMessage
+{
+ public SystemLoading( BitSetBuffer message, DataUnitID duid,
+ AliasList aliasList )
+ {
+ super( message, duid, aliasList );
+ }
+
+ @Override
+ public String getEventType()
+ {
+ return MotorolaOpcode.SYSTEM_LOADING.getLabel();
+ }
+
+ @Override
+ public String getMessage()
+ {
+ StringBuilder sb = new StringBuilder();
+
+ sb.append( getMessageStub() );
+
+ sb.append( " - " + getMessageHex() );
+
+ return sb.toString();
+ }
+
+}
diff --git a/src/decode/p25/message/tsbk/osp/control/AdjacentStatusBroadcast.java b/src/decode/p25/message/tsbk/osp/control/AdjacentStatusBroadcast.java
index 0071ea3e..b40b0b60 100644
--- a/src/decode/p25/message/tsbk/osp/control/AdjacentStatusBroadcast.java
+++ b/src/decode/p25/message/tsbk/osp/control/AdjacentStatusBroadcast.java
@@ -6,7 +6,7 @@ import decode.p25.message.tsbk.TSBKMessage;
import decode.p25.reference.DataUnitID;
import decode.p25.reference.Opcode;
-public class AdjacentStatusBroadcast extends TSBKMessage
+public class AdjacentStatusBroadcast extends TSBKMessage implements IdentifierUpdateReceiver
{
public static final int[] LOCATION_REGISTRATION_AREA = { 80,81,82,83,84,85,
86,87 };
@@ -18,14 +18,17 @@ public class AdjacentStatusBroadcast extends TSBKMessage
102,103 };
public static final int[] RFSS_ID = { 104,105,106,107,108,109,110,111 };
public static final int[] SITE_ID = { 112,113,114,115,116,117,118,119 };
- public static final int[] CHANNEL = { 120,121,122,123,124,125,126,127,
- 128,129,130,131,132,133,134,135 };
+ public static final int[] IDENTIFIER = { 120,121,122,123 };
+ public static final int[] CHANNEL = { 124,125,126,127,128,129,130,131,132,
+ 133,134,135 };
public static final int[] SYSTEM_SERVICE_CLASS = { 136,137,138,139,140,141,
142,143 };
+ private IdentifierUpdate mIdentifierUpdate;
+
public AdjacentStatusBroadcast( BitSetBuffer message,
- DataUnitID duid,
- AliasList aliasList )
+ DataUnitID duid,
+ AliasList aliasList )
{
super( message, duid, aliasList );
}
@@ -50,7 +53,11 @@ public class AdjacentStatusBroadcast extends TSBKMessage
sb.append( " SITE:" + getSiteID() );
- sb.append( " CHAN:" + getChannel() );
+ sb.append( " CHAN:" + getIdentifier() + "-" + getChannel() );
+
+ sb.append( " DN:" + getDownlinkFrequency() );
+
+ sb.append( " UP:" + getUplinkFrequency() );
if( isConventionalChannel() )
{
@@ -122,8 +129,39 @@ public class AdjacentStatusBroadcast extends TSBKMessage
return mMessage.getInt( CHANNEL );
}
+ public int getIdentifier()
+ {
+ return mMessage.getInt( IDENTIFIER );
+ }
+
public int getSystemServiceClass()
{
return mMessage.getInt( SYSTEM_SERVICE_CLASS );
}
+
+ public long getDownlinkFrequency()
+ {
+ return calculateDownlink( mIdentifierUpdate, getChannel() );
+ }
+
+ public long getUplinkFrequency()
+ {
+ return calculateUplink( mIdentifierUpdate, getChannel() );
+ }
+
+ @Override
+ public void setIdentifierMessage( int identifier, IdentifierUpdate message )
+ {
+ /* we're only expecting 1 identifier, so use whatever is received */
+ mIdentifierUpdate = message;
+ }
+
+ public int[] getIdentifiers()
+ {
+ int[] idens = new int[ 1 ];
+
+ idens[ 0 ] = getIdentifier();
+
+ return idens;
+ }
}
diff --git a/src/decode/p25/message/tsbk/osp/control/IdentifierUpdate.java b/src/decode/p25/message/tsbk/osp/control/IdentifierUpdate.java
index b5671515..5481954b 100644
--- a/src/decode/p25/message/tsbk/osp/control/IdentifierUpdate.java
+++ b/src/decode/p25/message/tsbk/osp/control/IdentifierUpdate.java
@@ -4,22 +4,13 @@ import alias.AliasList;
import bits.BitSetBuffer;
import decode.p25.message.tsbk.TSBKMessage;
import decode.p25.reference.DataUnitID;
-import decode.p25.reference.Opcode;
-public class IdentifierUpdate extends TSBKMessage
+public abstract class IdentifierUpdate extends TSBKMessage
{
- public static final int[] CHANNEL_IDENTIFIER = { 80,81,82,83 };
+ public static final int[] IDENTIFIER = { 80,81,82,83 };
public static final int[] CHANNEL_SPACING = { 102,103,104,105,106,107,108,
- 109,110,111 };
-
- public static final int[] BANDWIDTH = { 84,85,86,87,88,89,90,91,92 };
- public static final int[] BANDWIDTH_VHF_UHF = { 84,85,86,87 };
-
- public static final int[] TRANSMIT_OFFSET = { 93,94,95,96,97,98,99,100,101 };
- public static final int TRANSMIT_OFFSET_VHF_UHF_SIGN = 88;
- public static final int[] TRANSMIT_OFFSET_VHF_UHF = { 89,90,91,92,93,94,
- 95,96,97,98,99,100,101 };
+ 109,110,111 };
public static final int[] BASE_FREQUENCY = { 112,113,114,115,116,117,118,
119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,
@@ -32,42 +23,20 @@ public class IdentifierUpdate extends TSBKMessage
super( message, duid, aliasList );
}
- @Override
- public String getEventType()
- {
- return getOpcode().getDescription();
- }
-
- public String getMessage()
- {
- StringBuilder sb = new StringBuilder();
-
- sb.append( getMessageStub() );
-
- sb.append( " CHAN:" + getIdentifier() );
-
- sb.append( " DOWNLINK:" + getDownlinkFrequency() );
-
- sb.append( " UPLINK:" + ( getUplinkFrequency() ) );
-
- sb.append( " BW:" + getBandwidth() );
-
- sb.append( " SPACING:" + getChannelSpacing() );
-
- return sb.toString();
- }
-
+ /**
+ * (Band) Identifier
+ */
public int getIdentifier()
{
- return mMessage.getInt( CHANNEL_IDENTIFIER );
+ return mMessage.getInt( IDENTIFIER );
}
/**
* Channel spacing in hertz
*/
- public int getChannelSpacing()
+ public long getChannelSpacing()
{
- return mMessage.getInt( CHANNEL_SPACING ) * 125; //Hertz
+ return mMessage.getLong( CHANNEL_SPACING ) * 125l;
}
/**
@@ -75,79 +44,8 @@ public class IdentifierUpdate extends TSBKMessage
*/
public long getBaseFrequency()
{
- return mMessage.getLong( BASE_FREQUENCY ) * 5l; //Hertz
+ return mMessage.getLong( BASE_FREQUENCY ) * 5l;
}
- public boolean isVHF_UHF()
- {
- long base = getBaseFrequency();
-
- return ( ( 136000000 <= base && base <= 172000000 ) ||
- ( 380000000 <= base && base <= 512000000 ) );
- }
-
- /**
- * Channel bandwidth in hertz
- */
- public int getBandwidth()
- {
- int bandwidth = mMessage.getInt( BANDWIDTH );
-
- if( isVHF_UHF() )
- {
- switch( bandwidth )
- {
- case 4:
- return 6250;
- case 5:
- return 12500;
- default:
- return 0;
- }
- }
- else
- {
- return bandwidth * 125;
- }
- }
-
- /**
- * Transmit offset in hertz
- */
- public long getOffset()
- {
- if( isVHF_UHF() )
- {
- long offset = mMessage.getLong( TRANSMIT_OFFSET_VHF_UHF );
-
- if( mMessage.get( TRANSMIT_OFFSET_VHF_UHF_SIGN ) )
- {
- return offset;
- }
- else
- {
- return -offset;
- }
- }
- else
- {
- return mMessage.getLong( TRANSMIT_OFFSET ) * 250l;
- }
- }
-
- /**
- * Uplink ( radio to tower) frequency in hertz
- */
- public long getUplinkFrequency()
- {
- return getBaseFrequency() - getOffset();
- }
-
- /**
- * Downlink ( tower to radio ) frequency in hertz
- */
- public long getDownlinkFrequency()
- {
- return getBaseFrequency();
- }
+ public abstract long getTransmitOffset();
}
diff --git a/src/decode/p25/message/tsbk/osp/control/IdentifierUpdateNonVUHF.java b/src/decode/p25/message/tsbk/osp/control/IdentifierUpdateNonVUHF.java
new file mode 100644
index 00000000..f64d4d19
--- /dev/null
+++ b/src/decode/p25/message/tsbk/osp/control/IdentifierUpdateNonVUHF.java
@@ -0,0 +1,73 @@
+package decode.p25.message.tsbk.osp.control;
+
+import alias.AliasList;
+import bits.BitSetBuffer;
+import decode.p25.reference.DataUnitID;
+import decode.p25.reference.Opcode;
+
+public class IdentifierUpdateNonVUHF extends IdentifierUpdate
+{
+ public static final int[] BANDWIDTH = { 84,85,86,87,88,89,90,91,92 };
+
+ public static final int TRANSMIT_OFFSET_VHF_UHF_SIGN = 93;
+
+ public static final int[] TRANSMIT_OFFSET = { 94,95,96,97,98,99,100,101 };
+
+ public IdentifierUpdateNonVUHF( BitSetBuffer message,
+ DataUnitID duid,
+ AliasList aliasList )
+ {
+ super( message, duid, aliasList );
+ }
+
+ @Override
+ public String getEventType()
+ {
+ return Opcode.IDENTIFIER_UPDATE_NON_VUHF.getDescription();
+ }
+
+ public String getMessage()
+ {
+ StringBuilder sb = new StringBuilder();
+
+ sb.append( getMessageStub() );
+
+ sb.append( " IDEN:" + getIdentifier() );
+
+ sb.append( " BASE:" + getBaseFrequency() );
+
+ sb.append( " BW:" + getBandwidth() );
+
+ sb.append( " SPACING:" + getChannelSpacing() );
+
+ sb.append( " OFFSET:" + getTransmitOffset() );
+
+ return sb.toString();
+ }
+
+ /**
+ * Channel bandwidth in hertz
+ */
+ public int getBandwidth()
+ {
+ return mMessage.getInt( BANDWIDTH ) * 125;
+ }
+
+ /**
+ * Transmit offset in hertz
+ */
+ @Override
+ public long getTransmitOffset()
+ {
+ long offset = mMessage.getLong( TRANSMIT_OFFSET ) * 250000l;
+
+ if( mMessage.get( TRANSMIT_OFFSET_VHF_UHF_SIGN ) )
+ {
+ return offset;
+ }
+ else
+ {
+ return -offset;
+ }
+ }
+}
diff --git a/src/decode/p25/message/tsbk/osp/control/IdentifierUpdateReceiver.java b/src/decode/p25/message/tsbk/osp/control/IdentifierUpdateReceiver.java
new file mode 100644
index 00000000..6c5ca26e
--- /dev/null
+++ b/src/decode/p25/message/tsbk/osp/control/IdentifierUpdateReceiver.java
@@ -0,0 +1,13 @@
+package decode.p25.message.tsbk.osp.control;
+
+/**
+ * Interface to allow messages to be augmented with IdentiferUpdateXXX type
+ * messages that provide the channel information necessary to calculate the
+ * uplink and downlink frequency for the channel.
+ */
+public interface IdentifierUpdateReceiver
+{
+ public void setIdentifierMessage( int identifier, IdentifierUpdate message );
+
+ public int[] getIdentifiers();
+}
diff --git a/src/decode/p25/message/tsbk/osp/control/IdentifierUpdateVUHF.java b/src/decode/p25/message/tsbk/osp/control/IdentifierUpdateVUHF.java
new file mode 100644
index 00000000..70420048
--- /dev/null
+++ b/src/decode/p25/message/tsbk/osp/control/IdentifierUpdateVUHF.java
@@ -0,0 +1,85 @@
+package decode.p25.message.tsbk.osp.control;
+
+import alias.AliasList;
+import bits.BitSetBuffer;
+import decode.p25.reference.DataUnitID;
+import decode.p25.reference.Opcode;
+
+public class IdentifierUpdateVUHF extends IdentifierUpdate
+{
+ public static final int[] BANDWIDTH = { 84,85,86,87 };
+
+ public static final int TRANSMIT_OFFSET_VHF_UHF_SIGN = 88;
+
+ public static final int[] TRANSMIT_OFFSET = { 89,90,91,92,93,94,95,96,97,98,
+ 99,100,101 };
+
+ public IdentifierUpdateVUHF( BitSetBuffer message,
+ DataUnitID duid,
+ AliasList aliasList )
+ {
+ super( message, duid, aliasList );
+ }
+
+ @Override
+ public String getEventType()
+ {
+ return Opcode.IDENTIFIER_UPDATE_VHF_UHF_BANDS.getDescription();
+ }
+
+ public String getMessage()
+ {
+ StringBuilder sb = new StringBuilder();
+
+ sb.append( getMessageStub() );
+
+ sb.append( " IDEN:" + getIdentifier() );
+
+ sb.append( " BASE:" + getBaseFrequency() );
+
+ sb.append( " BW:" + getBandwidth() );
+
+ sb.append( " SPACING:" + getChannelSpacing() );
+
+ sb.append( " OFFSET:" + getTransmitOffset() );
+
+ return sb.toString();
+ }
+
+ /**
+ * Channel bandwidth in hertz
+ */
+ public int getBandwidth()
+ {
+ int bandwidth = mMessage.getInt( BANDWIDTH );
+
+ switch( bandwidth )
+ {
+ case 4:
+ return 6250;
+ case 5:
+ return 12500;
+ default:
+ return 0;
+ }
+ }
+
+ /**
+ * Transmit offset in hertz
+ */
+ @Override
+ public long getTransmitOffset()
+ {
+ long offset = mMessage.getLong( TRANSMIT_OFFSET ) *
+ getChannelSpacing();
+
+ if( mMessage.get( TRANSMIT_OFFSET_VHF_UHF_SIGN ) )
+ {
+ return offset;
+ }
+ else
+ {
+ return -offset;
+ }
+ }
+}
diff --git a/src/decode/p25/message/tsbk/osp/control/NetworkStatusBroadcast.java b/src/decode/p25/message/tsbk/osp/control/NetworkStatusBroadcast.java
index 922b22fd..ab2ac3c6 100644
--- a/src/decode/p25/message/tsbk/osp/control/NetworkStatusBroadcast.java
+++ b/src/decode/p25/message/tsbk/osp/control/NetworkStatusBroadcast.java
@@ -6,7 +6,7 @@ import decode.p25.message.tsbk.TSBKMessage;
import decode.p25.reference.DataUnitID;
import decode.p25.reference.Opcode;
-public class NetworkStatusBroadcast extends TSBKMessage
+public class NetworkStatusBroadcast extends TSBKMessage implements IdentifierUpdateReceiver
{
public static final int[] LOCATION_REGISTRATION_AREA = { 80,81,82,83,84,85,
86,87 };
@@ -14,11 +14,14 @@ public class NetworkStatusBroadcast extends TSBKMessage
101,102,103,104,105,106,107 };
public static final int[] SYSTEM_ID = { 108,109,110,111,112,113,114,115,116,
117,118,119 };
- public static final int[] CHANNEL = { 120,121,122,123,124,125,126,127,128,
- 129,130,131,132,133,134,135 };
+ public static final int[] IDENTIFIER = { 120,121,122,123 };
+ public static final int[] CHANNEL = { 124,125,126,127,128,129,130,131,132,
+ 133,134,135 };
public static final int[] SYSTEM_SERVICE_CLASS = { 136,137,138,139,140,141,
142,143 };
+ private IdentifierUpdate mIdentifierUpdate;
+
public NetworkStatusBroadcast( BitSetBuffer message,
DataUnitID duid,
AliasList aliasList )
@@ -44,7 +47,11 @@ public class NetworkStatusBroadcast extends TSBKMessage
sb.append( " SYS:" + getSystemID() );
- sb.append( " CNTRL CHAN:" + getChannel() );
+ sb.append( " CTRL CHAN:" + getIdentifier() + "-" + getChannel() );
+
+ sb.append( " DN:" + getDownlinkFrequency() );
+
+ sb.append( " UP:" + getUplinkFrequency() );
sb.append( " SYS SVC CLASS:" +
SystemService.toString( getSystemServiceClass() ) );
@@ -66,6 +73,11 @@ public class NetworkStatusBroadcast extends TSBKMessage
{
return mMessage.getHex( SYSTEM_ID, 3 );
}
+
+ public int getIdentifier()
+ {
+ return mMessage.getInt( IDENTIFIER );
+ }
public int getChannel()
{
@@ -76,4 +88,30 @@ public class NetworkStatusBroadcast extends TSBKMessage
{
return mMessage.getInt( SYSTEM_SERVICE_CLASS );
}
+
+ public long getDownlinkFrequency()
+ {
+ return calculateDownlink( mIdentifierUpdate, getChannel() );
+ }
+
+ public long getUplinkFrequency()
+ {
+ return calculateUplink( mIdentifierUpdate, getChannel() );
+ }
+
+ @Override
+ public void setIdentifierMessage( int identifier, IdentifierUpdate message )
+ {
+ /* we're only expecting 1 identifier, so use whatever is received */
+ mIdentifierUpdate = message;
+ }
+
+ public int[] getIdentifiers()
+ {
+ int[] idens = new int[ 1 ];
+
+ idens[ 0 ] = getIdentifier();
+
+ return idens;
+ }
}
diff --git a/src/decode/p25/message/tsbk/osp/control/RFSSStatusBroadcast.java b/src/decode/p25/message/tsbk/osp/control/RFSSStatusBroadcast.java
index 21042adf..728c376b 100644
--- a/src/decode/p25/message/tsbk/osp/control/RFSSStatusBroadcast.java
+++ b/src/decode/p25/message/tsbk/osp/control/RFSSStatusBroadcast.java
@@ -6,7 +6,7 @@ import decode.p25.message.tsbk.TSBKMessage;
import decode.p25.reference.DataUnitID;
import decode.p25.reference.Opcode;
-public class RFSSStatusBroadcast extends TSBKMessage
+public class RFSSStatusBroadcast extends TSBKMessage implements IdentifierUpdateReceiver
{
public static final int[] LOCATION_REGISTRATION_AREA = { 80,81,82,83,84,85,
86,87 };
@@ -15,11 +15,14 @@ public class RFSSStatusBroadcast extends TSBKMessage
102,103 };
public static final int[] RFSS_ID = { 104,105,106,107,108,109,110,111 };
public static final int[] SITE_ID = { 112,113,114,115,116,117,118,119 };
- public static final int[] CHANNEL = { 120,121,122,123,124,125,126,127,
- 128,129,130,131,132,133,134,135 };
+ public static final int[] IDENTIFIER = { 120,121,122,123 };
+ public static final int[] CHANNEL = { 124,125,126,127,128,129,130,131,132,
+ 133,134,135 };
public static final int[] SYSTEM_SERVICE_CLASS = { 136,137,138,139,140,141,
142,143 };
+ private IdentifierUpdate mIdentifierUpdate;
+
public RFSSStatusBroadcast( BitSetBuffer message,
DataUnitID duid,
AliasList aliasList )
@@ -47,8 +50,12 @@ public class RFSSStatusBroadcast extends TSBKMessage
sb.append( " SITE:" + getSiteID() );
- sb.append( " CHAN:" + getChannel() );
+ sb.append( " CHAN:" + getIdentifier() + "-" + getChannel() );
+ sb.append( " DN:" + getDownlinkFrequency() );
+
+ sb.append( " UP:" + getUplinkFrequency() );
+
if( hasActiveNetworkConnection() )
{
sb.append( " ACTIVE-NETWORK-CONN" );
@@ -83,6 +90,11 @@ public class RFSSStatusBroadcast extends TSBKMessage
{
return mMessage.getHex( SITE_ID, 2 );
}
+
+ public int getIdentifier()
+ {
+ return mMessage.getInt( IDENTIFIER );
+ }
public int getChannel()
{
@@ -93,4 +105,30 @@ public class RFSSStatusBroadcast extends TSBKMessage
{
return mMessage.getInt( SYSTEM_SERVICE_CLASS );
}
+
+ public long getDownlinkFrequency()
+ {
+ return calculateDownlink( mIdentifierUpdate, getChannel() );
+ }
+
+ public long getUplinkFrequency()
+ {
+ return calculateUplink( mIdentifierUpdate, getChannel() );
+ }
+
+ @Override
+ public void setIdentifierMessage( int identifier, IdentifierUpdate message )
+ {
+ /* we're only expecting 1 identifier, so use whatever is received */
+ mIdentifierUpdate = message;
+ }
+
+ public int[] getIdentifiers()
+ {
+ int[] idens = new int[ 1 ];
+
+ idens[ 0 ] = getIdentifier();
+
+ return idens;
+ }
}
diff --git a/src/decode/p25/message/tsbk/osp/control/SecondaryControlChannelBroadcast.java b/src/decode/p25/message/tsbk/osp/control/SecondaryControlChannelBroadcast.java
index 4f404d0a..75cc0e9a 100644
--- a/src/decode/p25/message/tsbk/osp/control/SecondaryControlChannelBroadcast.java
+++ b/src/decode/p25/message/tsbk/osp/control/SecondaryControlChannelBroadcast.java
@@ -1,24 +1,36 @@
package decode.p25.message.tsbk.osp.control;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
import alias.AliasList;
import bits.BitSetBuffer;
import decode.p25.message.tsbk.TSBKMessage;
import decode.p25.reference.DataUnitID;
import decode.p25.reference.Opcode;
-public class SecondaryControlChannelBroadcast extends TSBKMessage
+public class SecondaryControlChannelBroadcast extends TSBKMessage
+ implements IdentifierUpdateReceiver
{
- public static final int[] RFSS_ID = { 80,81,82,83,84,85,86,87 };
+ private final static Logger mLog =
+ LoggerFactory.getLogger( SecondaryControlChannelBroadcast.class );
+
+ public static final int[] RFSS_ID = { 80,81,82,83,84,85,86,87 };
public static final int[] SITE_ID = { 88,89,90,91,92,93,94,95 };
- public static final int[] CHANNEL_1 = { 96,97,98,99,100,101,102,103,104,105,
- 106,107,108,109,110,111 };
+ public static final int[] IDENTIFIER_1 = { 96,97,98,99 };
+ public static final int[] CHANNEL_1 = { 100,101,102,103,104,105,106,107,108,
+ 109,110,111 };
public static final int[] SYSTEM_SERVICE_CLASS_1 = { 112,113,114,115,116,
117,118,119 };
- public static final int[] CHANNEL_2 = { 120,121,122,123,124,125,126,127,
- 128,129,130,131,132,133,134,135 };
+ public static final int[] IDENTIFIER_2 = { 120,121,122,123 };
+ public static final int[] CHANNEL_2 = { 124,125,126,127,128,129,130,131,132,
+ 133,134,135 };
public static final int[] SYSTEM_SERVICE_CLASS_2 = { 136,137,138,139,140,
141,142,143 };
+ private IdentifierUpdate mIdentifierUpdate1;
+ private IdentifierUpdate mIdentifierUpdate2;
+
public SecondaryControlChannelBroadcast( BitSetBuffer message,
DataUnitID duid,
AliasList aliasList )
@@ -42,22 +54,27 @@ public class SecondaryControlChannelBroadcast extends TSBKMessage
sb.append( " SITE:" + getSiteID() );
- sb.append( " CHAN 1:" + getChannel1() );
+ sb.append( " CHAN1:" + getIdentifier1() + "-" + getChannel1() );
- sb.append( " SVC CLASS 1:" +
+ sb.append( " DN1:" + getDownlinkFrequency1() );
+
+ sb.append( " UP1:" + getUplinkFrequency1() );
+
+ sb.append( " SVC1:" +
SystemService.toString( getSystemServiceClass1() ) );
if( hasChannel2() )
{
- sb.append( " CHAN 2:" + getChannel2() );
+ sb.append( " CHAN2:" + getIdentifier2() + "-" + getChannel2() );
- sb.append( " SVC CLASS 2:" +
+ sb.append( " DN2:" + getDownlinkFrequency2() );
+
+ sb.append( " UP2:" + getUplinkFrequency2() );
+
+ sb.append( " SVC2:" +
SystemService.toString( getSystemServiceClass2() ) );
}
- else
- {
- sb.append( " CHAN 2: empty" );
- }
+
return sb.toString();
}
@@ -71,6 +88,11 @@ public class SecondaryControlChannelBroadcast extends TSBKMessage
return mMessage.getHex( SITE_ID, 2 );
}
+ public int getIdentifier1()
+ {
+ return mMessage.getInt( IDENTIFIER_1 );
+ }
+
public int getChannel1()
{
return mMessage.getInt( CHANNEL_1 );
@@ -80,6 +102,11 @@ public class SecondaryControlChannelBroadcast extends TSBKMessage
{
return mMessage.getInt( SYSTEM_SERVICE_CLASS_1 );
}
+
+ public int getIdentifier2()
+ {
+ return mMessage.getInt( IDENTIFIER_2 );
+ }
public int getChannel2()
{
@@ -95,4 +122,64 @@ public class SecondaryControlChannelBroadcast extends TSBKMessage
{
return getSystemServiceClass2() != 0;
}
+
+
+ public long getDownlinkFrequency1()
+ {
+ return calculateDownlink( mIdentifierUpdate1, getChannel1() );
+ }
+
+ public long getUplinkFrequency1()
+ {
+ return calculateUplink( mIdentifierUpdate1, getChannel1() );
+ }
+
+ public long getDownlinkFrequency2()
+ {
+ return calculateDownlink( mIdentifierUpdate2, getChannel2() );
+ }
+
+ public long getUplinkFrequency2()
+ {
+ return calculateUplink( mIdentifierUpdate2, getChannel2() );
+ }
+
+ @Override
+ public void setIdentifierMessage( int identifier, IdentifierUpdate message )
+ {
+ if( identifier == getIdentifier1() )
+ {
+ mIdentifierUpdate1 = message;
+ }
+ else if( identifier == getIdentifier2() )
+ {
+ mIdentifierUpdate2 = message;
+ }
+ else
+ {
+ throw new IllegalArgumentException( "Unexpected identifier update "
+ + "message for identifier [" + identifier + "]" );
+ }
+ }
+
+ public int[] getIdentifiers()
+ {
+ int[] idens;
+
+ if( hasChannel2() )
+ {
+ idens = new int[ 2 ];
+
+ idens[ 0 ] = getIdentifier1();
+ idens[ 1 ] = getIdentifier2();
+ }
+ else
+ {
+ idens = new int[ 1 ];
+
+ idens[ 0 ] = getIdentifier1();
+ }
+
+ return idens;
+ }
}
diff --git a/src/decode/p25/message/tsbk/osp/control/SecondaryControlChannelBroadcastExplicit.java b/src/decode/p25/message/tsbk/osp/control/SecondaryControlChannelBroadcastExplicit.java
new file mode 100644
index 00000000..6d7aa2f6
--- /dev/null
+++ b/src/decode/p25/message/tsbk/osp/control/SecondaryControlChannelBroadcastExplicit.java
@@ -0,0 +1,60 @@
+package decode.p25.message.tsbk.osp.control;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import alias.AliasList;
+import bits.BitSetBuffer;
+import decode.p25.reference.DataUnitID;
+import decode.p25.reference.Opcode;
+
+public class SecondaryControlChannelBroadcastExplicit
+ extends SecondaryControlChannelBroadcast
+{
+ private final static Logger mLog = LoggerFactory.getLogger(
+ SecondaryControlChannelBroadcastExplicit.class );
+
+ public SecondaryControlChannelBroadcastExplicit( BitSetBuffer message,
+ DataUnitID duid, AliasList aliasList )
+ {
+ super( message, duid, aliasList );
+ }
+
+ @Override
+ public String getEventType()
+ {
+ return Opcode.SECONDARY_CONTROL_CHANNEL_BROADCAST_EXPLICIT.getDescription();
+ }
+
+ public String getMessage()
+ {
+ StringBuilder sb = new StringBuilder();
+
+ sb.append( getMessageStub() );
+
+ sb.append( " RFSS:" + getRFSS() );
+
+ sb.append( " SITE:" + getSiteID() );
+
+ sb.append( " DOWNLINK:" + getIdentifier1() + "-" + getChannel1() );
+
+ sb.append( " " + getDownlinkFrequency1() );
+
+ sb.append( " SVC1:" +
+ SystemService.toString( getSystemServiceClass1() ) );
+
+ if( hasChannel2() )
+ {
+ sb.append( " UPLINK:" + getIdentifier2() + "-" + getChannel2() );
+
+ sb.append( " " + getUplinkFrequency1() );
+
+ sb.append( " SVC2:" +
+ SystemService.toString( getSystemServiceClass2() ) );
+ }
+
+ return sb.toString();
+ }
+
+
+}
diff --git a/src/decode/p25/message/tsbk/osp/control/TimeAndDateAnnouncement.java b/src/decode/p25/message/tsbk/osp/control/TimeAndDateAnnouncement.java
new file mode 100644
index 00000000..d55a6548
--- /dev/null
+++ b/src/decode/p25/message/tsbk/osp/control/TimeAndDateAnnouncement.java
@@ -0,0 +1,155 @@
+package decode.p25.message.tsbk.osp.control;
+
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.TimeZone;
+
+import alias.AliasList;
+import bits.BitSetBuffer;
+import decode.p25.message.tsbk.TSBKMessage;
+import decode.p25.reference.DataUnitID;
+import decode.p25.reference.Opcode;
+
+public class TimeAndDateAnnouncement extends TSBKMessage
+{
+ public static final int VALID_DATE_INDICATOR = 80;
+ public static final int VALID_TIME_INDICATOR = 81;
+ public static final int VALID_LOCAL_TIME_OFFSET_INDICATOR = 82;
+
+ public static final int LOCAL_TIME_OFFSET_SIGN = 84;
+ public static final int[] LOCAL_TIME_OFFSET = { 85,86,87,88,89,90,91,92,
+ 93,94,95 };
+ public static final int[] MONTH = { 96,97,98,99 };
+ public static final int[] DAY = { 100,101,102,103 };
+ public static final int[] YEAR = { 105,106,107,108,109,110,111,112,113,114,
+ 115,116,117 };
+ public static final int[] HOURS = { 120,121,122,123,124 };
+ public static final int[] MINUTES = { 125,126,127,128,129,130 };
+ public static final int[] SECONDS = { 131,132,133,134,135,136 };
+
+ private SimpleDateFormat mTimeFormatter =
+ new SimpleDateFormat( "EEE, d MMM yyyy HH:mm:ss Z" );
+
+ public TimeAndDateAnnouncement( BitSetBuffer message, DataUnitID duid,
+ AliasList aliasList )
+ {
+ super( message, duid, aliasList );
+ }
+
+ @Override
+ public String getEventType()
+ {
+ return Opcode.TIME_DATE_ANNOUNCEMENT.getDescription();
+ }
+
+ public String getMessage()
+ {
+ StringBuilder sb = new StringBuilder();
+
+ sb.append( getMessageStub() );
+
+ if( hasValidDate() )
+ {
+ sb.append( " DATE-TIME:" );
+ }
+
+ sb.append( mTimeFormatter.format( new Date( getDateTimestamp() ) ) );
+
+ return sb.toString();
+ }
+
+ public long getDateTimestamp()
+ {
+ Calendar cal = new GregorianCalendar( getTimeZone() );
+
+ if( hasValidDate() )
+ {
+ cal.set( Calendar.YEAR, getYear() );
+ cal.set( Calendar.MONTH, getMonth() );
+ cal.set( Calendar.DAY_OF_MONTH, getDay() );
+ }
+
+ if( hasValidTime() )
+ {
+ cal.set( Calendar.HOUR, getHour() );
+ cal.set( Calendar.MINUTE, getMinute() );
+ cal.set( Calendar.SECOND, getSecond() );
+ }
+
+ return cal.getTimeInMillis();
+ }
+
+ public boolean hasValidDate()
+ {
+ return mMessage.get( VALID_DATE_INDICATOR );
+ }
+
+ public boolean hasValidTime()
+ {
+ return mMessage.get( VALID_TIME_INDICATOR );
+ }
+
+ public TimeZone getTimeZone()
+ {
+ StringBuilder sb = new StringBuilder();
+
+ sb.append( "GMT " );
+
+ sb.append( mMessage.get( LOCAL_TIME_OFFSET_SIGN ) ? "-" : "+" );
+
+ int offsetMinutes = getLocalTimeOffset();
+
+ sb.append( (int)( offsetMinutes / 60 ) );
+
+ sb.append( " : " );
+
+ sb.append( offsetMinutes % 60 );
+
+ return TimeZone.getTimeZone( sb.toString() );
+ }
+
+ public boolean hasValidLocalTimeOffset()
+ {
+ return mMessage.get( VALID_LOCAL_TIME_OFFSET_INDICATOR );
+ }
+
+ /**
+ * Local Time Offset in minutes
+ */
+ public int getLocalTimeOffset()
+ {
+ return mMessage.getInt( LOCAL_TIME_OFFSET );
+ }
+
+ public int getMonth()
+ {
+ return mMessage.getInt( MONTH );
+ }
+
+ public int getDay()
+ {
+ return mMessage.getInt( DAY );
+ }
+
+ public int getYear()
+ {
+ return mMessage.getInt( YEAR );
+ }
+
+ public int getHour()
+ {
+ return mMessage.getInt( HOURS );
+ }
+
+ public int getMinute()
+ {
+ return mMessage.getInt( MINUTES );
+ }
+
+ public int getSecond()
+ {
+ return mMessage.getInt( SECONDS );
+ }
+}
diff --git a/src/decode/p25/message/tsbk/osp/data/SNDCPData.java b/src/decode/p25/message/tsbk/osp/data/SNDCPData.java
new file mode 100644
index 00000000..6a30607b
--- /dev/null
+++ b/src/decode/p25/message/tsbk/osp/data/SNDCPData.java
@@ -0,0 +1,24 @@
+package decode.p25.message.tsbk.osp.data;
+
+import alias.AliasList;
+import bits.BitSetBuffer;
+import decode.p25.message.tsbk.ServiceMessage;
+import decode.p25.reference.DataUnitID;
+
+public abstract class SNDCPData extends ServiceMessage
+{
+ public static final int[] NETWORK_SERVICE_ACCESS_POINT_ID = { 84,85,86,87 };
+
+ public static final int[] TARGET_ADDRESS = { 96,97,98,99,100,101,102,103,
+ 104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119 };
+
+ public static final int[] SOURCE_ADDRESS = { 120,121,122,123,124,125,126,
+ 127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143 };
+
+ public SNDCPData( BitSetBuffer message,
+ DataUnitID duid,
+ AliasList aliasList )
+ {
+ super( message, duid, aliasList );
+ }
+}
diff --git a/src/decode/p25/message/tsbk/osp/data/SNDCPDataChannelAnnouncementExplicit.java b/src/decode/p25/message/tsbk/osp/data/SNDCPDataChannelAnnouncementExplicit.java
new file mode 100644
index 00000000..9c4b0777
--- /dev/null
+++ b/src/decode/p25/message/tsbk/osp/data/SNDCPDataChannelAnnouncementExplicit.java
@@ -0,0 +1,148 @@
+package decode.p25.message.tsbk.osp.data;
+
+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 class SNDCPDataChannelAnnouncementExplicit extends SNDCPData
+ implements IdentifierUpdateReceiver
+{
+ public static final int AUTONOMOUS_ACCESS_AVAILABLE_INDICATOR = 88;
+ public static final int REQUESTED_ACCESS_AVAILABLE_INDICATOR = 89;
+
+ public static final int[] TRANSMIT_IDENTIFIER = { 96,97,98,99 };
+ public static final int[] TRANSMIT_CHANNEL = { 100,101,102,103,104,105,106,
+ 107,108,109,110,111 };
+ public static final int[] RECEIVE_IDENTIFIER = { 112,113,114,115 };
+ public static final int[] RECEIVE_CHANNEL = { 116,117,118,119,120,121,122,
+ 123,124,125,126,127 };
+
+ public static final int[] DATA_ACCESS_CONTROL = { 128,129,130,131,132,133,
+ 134,135,136,137,138,139,140,141,142,143 };
+
+ private IdentifierUpdate mIdentifierUpdateTransmit;
+ private IdentifierUpdate mIdentifierUpdateReceive;
+
+ public SNDCPDataChannelAnnouncementExplicit( BitSetBuffer message,
+ DataUnitID duid,
+ AliasList aliasList )
+ {
+ super( message, duid, aliasList );
+ }
+
+ public String getMessage()
+ {
+ StringBuilder sb = new StringBuilder();
+
+ sb.append( getMessageStub() );
+
+ if( isEmergency() )
+ {
+ sb.append( " EMERGENCY" );
+ }
+
+ if( isAutonomousAccessAvailable() )
+ {
+ sb.append( " AUTONOMOUS ACCESS" );
+ }
+
+ if( isRequestedAccessAvailable() )
+ {
+ sb.append( " DOWNLINK:" );
+ sb.append( getTransmitIdentifier() + "-" + getTransmitChannel() );
+
+ sb.append( " " + getUplinkFrequency() );
+
+ sb.append( " UPLINK:" );
+ sb.append( getReceiveIdentifier() + "-" + getReceiveChannel() );
+
+ sb.append( " " + getDownlinkFrequency() );
+
+ sb.append( " DATA ACCESS CONTROL:" );
+ }
+ else
+ {
+ sb.append( " ACCESS DENIED TO DATA ACCESS CONTROL:" );
+ }
+
+ sb.append( getDataAccessControl() );
+
+ return sb.toString();
+ }
+
+ public boolean isAutonomousAccessAvailable()
+ {
+ return mMessage.get( AUTONOMOUS_ACCESS_AVAILABLE_INDICATOR );
+ }
+
+ public boolean isRequestedAccessAvailable()
+ {
+ return mMessage.get( REQUESTED_ACCESS_AVAILABLE_INDICATOR );
+ }
+
+ public int getTransmitIdentifier()
+ {
+ return mMessage.getInt( TRANSMIT_IDENTIFIER );
+ }
+
+ public int getTransmitChannel()
+ {
+ return mMessage.getInt( TRANSMIT_CHANNEL );
+ }
+
+ public int getReceiveIdentifier()
+ {
+ return mMessage.getInt( RECEIVE_IDENTIFIER );
+ }
+
+ public int getReceiveChannel()
+ {
+ return mMessage.getInt( RECEIVE_CHANNEL );
+ }
+
+ public String getDataAccessControl()
+ {
+ return mMessage.getHex( DATA_ACCESS_CONTROL, 4 );
+ }
+
+ @Override
+ public void setIdentifierMessage( int identifier, IdentifierUpdate message )
+ {
+ if( identifier == getTransmitIdentifier() )
+ {
+ mIdentifierUpdateTransmit = message;
+ }
+ else if( identifier == getReceiveIdentifier() )
+ {
+ mIdentifierUpdateReceive = message;
+ }
+ else
+ {
+ throw new IllegalArgumentException( "unexpected channel band "
+ + "identifier [" + identifier + "]" );
+ }
+ }
+
+ @Override
+ public int[] getIdentifiers()
+ {
+ int[] identifiers = new int[ 2 ];
+
+ identifiers[ 0 ] = getTransmitIdentifier();
+ identifiers[ 1 ] = getReceiveIdentifier();
+
+ return identifiers;
+ }
+
+ public long getDownlinkFrequency()
+ {
+ return calculateDownlink( mIdentifierUpdateTransmit, getReceiveChannel() );
+ }
+
+ public long getUplinkFrequency()
+ {
+ return calculateUplink( mIdentifierUpdateReceive, getTransmitChannel() );
+ }
+}
diff --git a/src/decode/p25/message/tsbk/osp/data/SNDCPDataChannelGrant.java b/src/decode/p25/message/tsbk/osp/data/SNDCPDataChannelGrant.java
new file mode 100644
index 00000000..ae398a4f
--- /dev/null
+++ b/src/decode/p25/message/tsbk/osp/data/SNDCPDataChannelGrant.java
@@ -0,0 +1,123 @@
+package decode.p25.message.tsbk.osp.data;
+
+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 class SNDCPDataChannelGrant extends SNDCPData implements IdentifierUpdateReceiver
+{
+ public static final int[] TRANSMIT_IDENTIFIER = { 88,89,90,91 };
+ public static final int[] TRANSMIT_CHANNEL = { 92,93,94,95,96,97,98,99,100,
+ 101,102,103 };
+ public static final int[] RECEIVE_IDENTIFIER = { 104,105,106,107 };
+ public static final int[] RECEIVE_CHANNEL = { 108,109,110,111,112,113,114,
+ 115,116,117,118,119 };
+
+ public static final int[] TARGET_ADDRESS = { 120,121,122,123,124,125,126,
+ 127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143 };
+
+ private IdentifierUpdate mIdentifierUpdateTransmit;
+ private IdentifierUpdate mIdentifierUpdateReceive;
+
+ public SNDCPDataChannelGrant( BitSetBuffer message,
+ DataUnitID duid,
+ AliasList aliasList )
+ {
+ super( message, duid, aliasList );
+ }
+
+ public String getMessage()
+ {
+ StringBuilder sb = new StringBuilder();
+
+ sb.append( getMessageStub() );
+
+ if( isEmergency() )
+ {
+ sb.append( " EMERGENCY" );
+ }
+
+ sb.append( " UPLINK:" );
+ sb.append( getTransmitIdentifier() + "-" + getTransmitChannel() );
+
+ sb.append( " " + getUplinkFrequency() );
+
+ sb.append( " DOWNLINK:" );
+ sb.append( getReceiveIdentifier() + "-" + getReceiveChannel() );
+
+ sb.append( " " + getDownlinkFrequency() );
+
+ sb.append( " TGT:" );
+ sb.append( getTargetAddress() );
+
+ return sb.toString();
+ }
+
+ public int getTransmitIdentifier()
+ {
+ return mMessage.getInt( TRANSMIT_IDENTIFIER );
+ }
+
+ public int getTransmitChannel()
+ {
+ return mMessage.getInt( TRANSMIT_CHANNEL );
+ }
+
+ public int getReceiveIdentifier()
+ {
+ return mMessage.getInt( RECEIVE_IDENTIFIER );
+ }
+
+ public int getReceiveChannel()
+ {
+ return mMessage.getInt( RECEIVE_CHANNEL );
+ }
+
+ public String getTargetAddress()
+ {
+ return mMessage.getHex( TARGET_ADDRESS, 6 );
+ }
+
+ @Override
+ public void setIdentifierMessage( int identifier, IdentifierUpdate message )
+ {
+ if( identifier == getTransmitIdentifier() )
+ {
+ mIdentifierUpdateTransmit = message;
+ }
+ else if( identifier == getReceiveIdentifier() )
+ {
+ mIdentifierUpdateReceive = message;
+ }
+ else
+ {
+ throw new IllegalArgumentException( "unexpected channel band "
+ + "identifier [" + identifier + "]" );
+ }
+ }
+
+ @Override
+ public int[] getIdentifiers()
+ {
+ int[] identifiers = new int[ 2 ];
+
+ identifiers[ 0 ] = getTransmitIdentifier();
+ identifiers[ 1 ] = getReceiveIdentifier();
+
+ return identifiers;
+ }
+
+ public long getDownlinkFrequency()
+ {
+ return calculateDownlink( mIdentifierUpdateReceive, getReceiveChannel() );
+ }
+
+ public long getUplinkFrequency()
+ {
+ return calculateUplink( mIdentifierUpdateTransmit, getTransmitChannel() );
+ }
+
+
+}
diff --git a/src/decode/p25/message/tsbk/osp/data/SNDCPDataPageRequest.java b/src/decode/p25/message/tsbk/osp/data/SNDCPDataPageRequest.java
new file mode 100644
index 00000000..cd51844b
--- /dev/null
+++ b/src/decode/p25/message/tsbk/osp/data/SNDCPDataPageRequest.java
@@ -0,0 +1,51 @@
+package decode.p25.message.tsbk.osp.data;
+
+import alias.AliasList;
+import bits.BitSetBuffer;
+import decode.p25.reference.DataUnitID;
+
+public class SNDCPDataPageRequest extends SNDCPData
+{
+ public static final int[] DATA_ACCESS_CONTROL = { 104,105,106,107,108,109,
+ 110,111,112,113,114,115,116,117,118,119 };
+
+ public static final int[] TARGET_ADDRESS = { 120,121,122,123,124,125,126,
+ 127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143 };
+
+ public SNDCPDataPageRequest( BitSetBuffer message,
+ DataUnitID duid,
+ AliasList aliasList )
+ {
+ super( message, duid, aliasList );
+ }
+
+ public String getMessage()
+ {
+ StringBuilder sb = new StringBuilder();
+
+ sb.append( getMessageStub() );
+
+ if( isEmergency() )
+ {
+ sb.append( " EMERGENCY" );
+ }
+
+ sb.append( " TGT:" );
+ sb.append( getTargetAddress() );
+
+ sb.append( " DAC:" );
+ sb.append( getDataAccessControl() );
+
+ return sb.toString();
+ }
+
+ public String getDataAccessControl()
+ {
+ return mMessage.getHex( DATA_ACCESS_CONTROL, 4 );
+ }
+
+ public String getTargetAddress()
+ {
+ return mMessage.getHex( TARGET_ADDRESS, 6 );
+ }
+}
diff --git a/src/decode/p25/reference/Opcode.java b/src/decode/p25/reference/Opcode.java
index 6f963e20..fa828a45 100644
--- a/src/decode/p25/reference/Opcode.java
+++ b/src/decode/p25/reference/Opcode.java
@@ -63,7 +63,7 @@ public enum Opcode
RFSS_STATUS_BROADCAST( "RFSS_STATUS_BCST", "RFSS Status Broadcast", 58 ),
NETWORK_STATUS_BROADCAST( "NET_STATUS_BCAST", "Network Status Broadcast", 59 ),
ADJACENT_STATUS_BROADCAST( "NGHBR_STAT_BCAST", "Adjacent Status Broadcast", 60 ),
- IDENTIFIER_UPDATE( "IDEN_UPDATE ", "Identifier Update", 61 ),
+ IDENTIFIER_UPDATE_NON_VUHF( "IDEN_UPDATE ", "Identifier Update", 61 ),
PROTECTION_PARAMETER_BROADCAST( "ENCRYPT_PAR_BCST", "Protection Parameter Broadcast", 62 ),
PROTECTION_PARAMETER_UPDATE( "ENCRYPT_PAR_UPDT", "Protection Parameter Update", 63 ),
UNKNOWN( "UNKNOWN ", "Unknown", -1 );
diff --git a/src/decode/p25/reference/Vendor.java b/src/decode/p25/reference/Vendor.java
index 7014b8e9..07b504fa 100644
--- a/src/decode/p25/reference/Vendor.java
+++ b/src/decode/p25/reference/Vendor.java
@@ -3,7 +3,7 @@ package decode.p25.reference;
public enum Vendor
{
STANDARD( "STANDARD", "STANDARD", 0),
- V1( "VENDOR01", "V_01", 1),
+ STANDARD_V1( "STANDARD", "STANDARD_01", 1),
V2( "VENDOR02", "V_02", 2),
V3( "VENDOR03", "V_03", 3),
V4( "VENDOR04", "V_04", 4),