From 137d35697e2f0fdf6003a3ae58bbac0acc586e0d Mon Sep 17 00:00:00 2001 From: denny Date: Sun, 20 Dec 2015 18:34:58 -0500 Subject: [PATCH] Adds P25 Phase II/TDMA Sync Broadcast message (Opcode 30) used to synchronize timing across an FDMA control channel and a TDMA traffic channel. --- .../p25/message/tsbk/TSBKMessageFactory.java | 3 + .../tsbk/osp/control/SyncBroadcast.java | 188 ++++++++++++++++++ src/module/decode/p25/reference/Opcode.java | 4 +- 3 files changed, 193 insertions(+), 2 deletions(-) create mode 100644 src/module/decode/p25/message/tsbk/osp/control/SyncBroadcast.java diff --git a/src/module/decode/p25/message/tsbk/TSBKMessageFactory.java b/src/module/decode/p25/message/tsbk/TSBKMessageFactory.java index 872e11ef..e06ac784 100644 --- a/src/module/decode/p25/message/tsbk/TSBKMessageFactory.java +++ b/src/module/decode/p25/message/tsbk/TSBKMessageFactory.java @@ -32,6 +32,7 @@ import module.decode.p25.message.tsbk.osp.control.SecondaryControlChannelBroadca import module.decode.p25.message.tsbk.osp.control.SecondaryControlChannelBroadcastExplicit; import module.decode.p25.message.tsbk.osp.control.StatusQuery; import module.decode.p25.message.tsbk.osp.control.StatusUpdate; +import module.decode.p25.message.tsbk.osp.control.SyncBroadcast; import module.decode.p25.message.tsbk.osp.control.SystemServiceBroadcast; import module.decode.p25.message.tsbk.osp.control.TimeAndDateAnnouncement; import module.decode.p25.message.tsbk.osp.control.UnitDeregistrationAcknowledge; @@ -165,6 +166,8 @@ public class TSBKMessageFactory return new StatusQuery( message, duid, aliasList ); case STATUS_UPDATE: return new StatusUpdate( message, duid, aliasList ); + case TDMA_SYNC_BROADCAST: + return new SyncBroadcast( message, duid, aliasList ); case SYSTEM_SERVICE_BROADCAST: return new SystemServiceBroadcast( message, duid, aliasList ); diff --git a/src/module/decode/p25/message/tsbk/osp/control/SyncBroadcast.java b/src/module/decode/p25/message/tsbk/osp/control/SyncBroadcast.java new file mode 100644 index 00000000..50c09e73 --- /dev/null +++ b/src/module/decode/p25/message/tsbk/osp/control/SyncBroadcast.java @@ -0,0 +1,188 @@ +package module.decode.p25.message.tsbk.osp.control; + +import java.util.Calendar; +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.TimeZone; + +import module.decode.p25.message.tsbk.TSBKMessage; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; +import alias.AliasList; +import bits.BinaryMessage; + +/** + * Sync Broadcast - used to broadcast FDMA-TDMA synchronization timestamp so + * that a subscriber unit that is time synchronized on an FDMA control channel + * is also synchronized with a TDMA traffic channel. + */ +public class SyncBroadcast extends TSBKMessage +{ + public static final int US = 92; + public static final int IST = 93; + public static final int MMU = 94; + public static final int[] MC = { 95,96 }; + public static final int VL = 97; + public static final int LOCAL_TIME_OFFSET_SIGN = 98; + public static final int[] LOCAL_TIME_OFFSET = { 99,100,101,102 }; + public static final int LOCAL_TIME_OFFSET_HALF = 103; + + public static final int[] YEAR = { 104,105,106,107,108,109,110 }; + public static final int[] MONTH = { 111,112,113,114 }; + public static final int[] DAY = { 115,116,117,118,119 }; + 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[] MICRO_SLOTS = { 131,132,133,134,135,136,137,138, + 139,140,141,142,143 }; + public static final int[] TSBK_CRC = { 144,145,146,147,148,149,150,151,152, + 153,154,155,156,157,158,159 }; + + public SyncBroadcast( BinaryMessage message, + DataUnitID duid, + AliasList aliasList ) + { + super( message, duid, aliasList ); + } + + @Override + public String getEventType() + { + return Opcode.TDMA_SYNC_BROADCAST.getDescription(); + } + + public String getMessage() + { + StringBuilder sb = new StringBuilder(); + + sb.append( getMessageStub() ); + + sb.append( " " + new Date( getTime() ).toString() ); + sb.append( " TIME ZONE OFFSET:" + getTimeZone().toUpperCase() ); + sb.append( " US:" + isUS() ); + sb.append( " IST:" + isIST() ); + sb.append( " MMU:" + isMMU() ); + sb.append( " VL:" + isVL() ); + + return sb.toString(); + } + + /** + * Returns the time sync in java time (ie milliseconds since epoch ) + */ + public long getTime() + { + Calendar cal = new GregorianCalendar(); + cal.clear(); + cal.setTimeZone( TimeZone.getTimeZone( "GMT" ) ); + cal.set( Calendar.YEAR, getYear() ); + cal.set( Calendar.MONTH, getMonth() - 1 ); + cal.set( Calendar.DAY_OF_MONTH, getDay() ); + cal.set( Calendar.HOUR_OF_DAY, getHours() ); + cal.set( Calendar.MINUTE, getMinutes() ); + cal.set( Calendar.MILLISECOND, getMilliSeconds() ); + + return cal.getTimeInMillis(); + } + + public boolean isUS() + { + return mMessage.get( US ); + } + + /** + * Daylight Savings Time + */ + public boolean isIST() + { + return mMessage.get( IST ); + } + + /** + * No idea ... + */ + public boolean isMMU() + { + return mMessage.get( MMU ); + } + + /** + * Millenium ...? + */ + public int getMC() + { + return mMessage.getInt( MC ); + } + + /** + * No idea .... + */ + public boolean isVL() + { + return mMessage.get( VL ); + } + + /** + * Calculates the time zone from the local time offset field. + */ + public String getTimeZone() + { + StringBuilder sb = new StringBuilder(); + + sb.append( mMessage.get( LOCAL_TIME_OFFSET_SIGN ) ? "-" : "+" ); + + int hours = mMessage.getInt( LOCAL_TIME_OFFSET ); + sb.append( ( hours < 10 ? "0" : "" ) + String.valueOf( hours ) ); + + sb.append( ":" ); + + sb.append( mMessage.get( LOCAL_TIME_OFFSET_HALF ) ? "30" : "00" ); + + if( mMessage.get( IST ) ) + { + sb.append( " DST" ); + } + + return sb.toString(); + } + + public int getYear() + { + return 2000 + mMessage.getInt( YEAR ); + } + + public int getMonth() + { + return mMessage.getInt( MONTH ); + } + + public int getDay() + { + return mMessage.getInt( DAY ); + } + + public int getHours() + { + return mMessage.getInt( HOURS ); + } + + public int getMinutes() + { + return mMessage.getInt( MINUTES ); + } + + public int getMilliSeconds() + { + return (int)( (double)getMicroSlots() * 7.5 ); + } + + /** + * Micro Slots - each uslot = 7.5 milliseconds. The value ranges 0 - 7999 + * and represents 60,000 milliseconds ( 8000 x 7.5 ms). Superframes occur + * every 360 ms (48 micro-slots) on FDMA and Ultraframes occur every 4 + * super frames on TDMA. + */ + public int getMicroSlots() + { + return mMessage.getInt( MICRO_SLOTS ); + } +} diff --git a/src/module/decode/p25/reference/Opcode.java b/src/module/decode/p25/reference/Opcode.java index cd3bc40c..4dbf8870 100644 --- a/src/module/decode/p25/reference/Opcode.java +++ b/src/module/decode/p25/reference/Opcode.java @@ -32,7 +32,7 @@ public enum Opcode RESERVED_1B( "RESERVED_1B ", "Reserved", 27 ), MESSAGE_UPDATE( "MESSAGE_UPDATE ", "Message Update", 28 ), RADIO_UNIT_MONITOR_COMMAND( "RADIO_MONITR_CMD", "Radio Unit Monitor Command", 29 ), - RESERVED_1E( "RESERVED_1E ", "Reserved", 30 ), + RESERVED_1E( "RESERVED_1E ", "Reserved", 30 ), CALL_ALERT( "CALL_ALERT ", "Call Alert", 31 ), ACKNOWLEDGE_RESPONSE( "ACK_RESPONSE_FNE", "Acknowledge Response - FNE", 32 ), QUEUED_RESPONSE( "QUEUED_RESPONSE ", "Queued Response", 33 ), @@ -50,7 +50,7 @@ public enum Opcode UNIT_REGISTRATION_COMMAND( "UNIT_REG_COMMAND", "Unit Registration Command", 45 ), AUTHENTICATION_COMMAND( "AUTH_COMMAND ", "Authentication Command", 46 ), UNIT_DEREGISTRATION_ACKNOWLEDGE( "DE_REGIST_ACK ", "De-Registration Acknowledge", 47 ), - RESERVED_30( "RESERVED_30 ", "Reserved", 48 ), + TDMA_SYNC_BROADCAST( "TDMA_SYNC_BCST ", "FDMA-TDMA Sync Broadcast", 48 ), RESERVED_31( "RESERVED_31 ", "Reserved", 49 ), RESERVED_32( "RESERVED_32 ", "Reserved", 50 ), RESERVED_33( "RESERVED_33 ", "Reserved", 51 ),