using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Net.Sockets; using System.Net; using System.Runtime.Caching; using SafeMobileLib; namespace MotoRepeater { public class Wireline { // all available operations public enum Operations { WL_REGISTRATION_REQUEST = 0x01, WL_REGISTRATION_STATUS = 0x02, WL_REGISTRATION_GENERAL_OPS = 0x03, WL_PROTOCOL_VERSION_QUERY = 0x04, WL_PROTOCOL_VERSION_QUERY_RESPONSE = 0x05, WL_CHNL_STATUS = 0x11, WL_CHNL_STATUS_QUERY = 0x12, WL_VC_CHNL_CTRL_REQUEST = 0x13, WL_VC_CHNL_CTRL_STATUS = 0x16, WL_VC_CSBK_CALL = 0x17, WL_VC_VOICE_START = 0x18, WL_VC_VOICE_END_BURST = 0X19, WL_VC_CALL_SESSION_STATUS = 0x20, WL_VC_VOICE_BURST = 0x21, WL_VC_PRIVACY_BURST = 0X22 }; private static byte[] AuthenticationID = new byte[] { 0x01, 0x02, 0x00, 0x6E }; private static byte[] VendorKey = new byte[] { 0x85, 0x8a, 0x5e, 0xa6, 0x8c, 0xf9, 0xa9, 0x7f, 0x49, 0xc7, 0x10, 0x87, 0xe7, 0xf2, 0xb4, 0x64, 0xd5, 0x9c, 0x52, 0x5d }; private static byte[] AuthenticationKey = new byte[] { 0x00 }; // set by the user in the repeater private static byte[] AuthenticationSignatureKey = new byte[40]; /* System variables */ private Thread wirelineHandlerThread; public int majorWirelineProtocolVersion = 3; public byte wirelineProtocolVersion = 0x00; private bool isWirelineRegistered = false; private SlotNumber currentSlot = SlotNumber.BothSlots; private LinkEstablishment le; /* CRT CALL VARIABLES */ private CSBKCall crtCSBKCallStausSlot1 = new CSBKCall(); private CSBKCall crtCSBKCallStausSlot2 = new CSBKCall(); // store the previous burst type in order to add the missing frames private int previousBrustTypeSlot1 = 0x06; private int previousBrustTypeSlot2 = 0x06; private byte currentWirelineProtocolVersion = 0x00; private byte oldestWirelineProtocolVersion = 0x00; private MemoryCache mem = new MemoryCache("MotoTRBOCache"); public Wireline(LinkEstablishment linkEstablishment) { this.le = linkEstablishment; le.OnRegistrationResponseReceived += new LinkEstablishment.RegistrationResponseDEl(le_OnRegistrationResponseReceived); le.OnWirelineMessageReceived += new LinkEstablishment.WirelineMessageReceivedDEl(le_OnWirelineMessageReceived); this.OnRadioStatusChanged += new RadioStatusChangedDEl(Wireline_OnRadioStatusChanged); this.OnCSBKCallReceived += new CSBKCallReceivedDEl(Wireline_OnCSBKCallReceived); this.OnChannelControlStatusReceived += new ChannelControlStatusReceivedDel(Wireline_OnChannelControlStatusReceived); this.OnCallSesionStatusReceived += new CallSessionStatusReceivedDEl(Wireline_OnCallSesionStatusReceived); SetCurrentAndOldestWirelineProtocolVersion(); GenerateAuthenticationSignatureKey(); /* TEST WITH THE VALUES FROM MOTOROLA AuthenticationID = new byte[] { 0x01, 0x01, 0x00, 0x2A }; VendorKey = new byte[] { 0xcf, 0xd0, 0x25, 0xcb, 0x75, 0x60, 0xdb, 0x13, 0x5d, 0x70, 0x2a, 0x20, 0x1e, 0x65, 0xd0, 0x30, 0x9f, 0xd2, 0xb3, 0xb2 }; //AuthenticationKey = new byte[] { 0xAB, 0xCD }; // set by the user //AuthenticationSignatureKey = new byte[40]; GenerateAuthenticationSignatureKey(); byte[] pdu; byte[] restp = AddWirelineSignatureKey( pdu = new byte[] { 0xb2, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x94, 0xb8, 0xcf, 0x00, 0x00, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x80, 0x04, 0x04} ); //*/ } /* When a new Wireline Message is received by the Link Establishment */ void le_OnWirelineMessageReceived(byte[] received, string peerIP, Int32 peerPort) { try { decode(received, peerIP, peerPort); } catch (Exception ex) { Console.Write("\n\n " + ex.ToString()); } } void le_OnRegistrationResponseReceived(bool isValid) { if (isValid) { Utils.WriteLine("LE Registered. Starting Wireline registration..."); List regEntries = new List(); regEntries.Add(new RegEntry(AddressType.AllIndividualCall, 1, 16777215)); regEntries.Add(new RegEntry(AddressType.AllTalkgroupCall, 1, 16777215)); regEntries.Add(new RegEntry(AddressType.AllLocalTalkgroupsCall, 1, 16777215)); regEntries.Add(new RegEntry(AddressType.AllWideTakgroupsCall, 1, 16777215)); //reg.regEntriesList.Add(new RegEntry(AddressType.GroupCall, 1, 1024)); Register(le.GetRegisteredSlots(), regEntries); //le.SendLEMessage(createWirelineMessage(Operations.WL_REGISTRATION_REQUEST, reg)); } } // set the current and oldest wireline Protocol Version private void SetCurrentAndOldestWirelineProtocolVersion() { // clear previous values currentWirelineProtocolVersion = 0x00; oldestWirelineProtocolVersion = 0x00; // create two 'bit' arrays for System ID and Protocol Version bool[] systemID = new bool[6]; bool[] protocolVers = new bool[10]; bool[] oldestProtocolVers = new bool[10]; switch (le.systemType) { case MotoRepeater.LinkEstablishment.SystemType.IP_SITE_CONNECT: { systemID = new bool[] { false, false, false, false, false, true }; break; } case MotoRepeater.LinkEstablishment.SystemType.CAPACITY_PLUS: { systemID = new bool[] { false, false, false, false, true, false }; break; } case MotoRepeater.LinkEstablishment.SystemType.LINKED_CAPACITY_PLUS: { systemID = new bool[] { false, false, false, true, false, false }; break; } } switch (le.releaseNumber) { case MotoRepeater.LinkEstablishment.SystemReleaseNumber.R23A: { majorWirelineProtocolVersion = 3; wirelineProtocolVersion = 0x0C; // 0b00000100 break; } case MotoRepeater.LinkEstablishment.SystemReleaseNumber.R23: case MotoRepeater.LinkEstablishment.SystemReleaseNumber.R22: { majorWirelineProtocolVersion = 1; wirelineProtocolVersion = 0x04; // 0b00001100 break; } } currentWirelineProtocolVersion = wirelineProtocolVersion; oldestWirelineProtocolVersion = 0x04; } // function which will generate the authentication Signature KEY which is used as HMAC-SHA1 key for // generating the Authentication Signature bytes which are added to each Call Control Wireline Messages private void GenerateAuthenticationSignatureKey() { List authSign = new List(); authSign.AddRange(VendorKey); // padd with zeros for (int i = 0; i < 20 - AuthenticationKey.Length; i++) { authSign.Add(0x00); } foreach (byte by in AuthenticationKey) authSign.Add(by); // save the authentication signature key which is compute from the Vendor Key and Authentication Key AuthenticationSignatureKey = authSign.ToArray(); } /// /// Returns the byte array which needs to be send, already containing the wireline authentication signature /// /// the byte array which will be used used for genereting the Wireline Signature Key /// A byte array containing the original byte array followed by the Authentication ID [Motorola Provided] and /// the Authentication Signature Key [Generated using HMACSha1 algoritm] private byte[] AddWirelineSignatureKey(byte[] wirelineMessage) { // encrypts the wireline messages using Authentication Signature as the key System.Security.Cryptography.HMACSHA1 hmacSha1 = new System.Security.Cryptography.HMACSHA1(AuthenticationSignatureKey); hmacSha1.ComputeHash(wirelineMessage); // trim the authentication signature to only 10 bytes byte[] truncatedAuthenticationSignature = new byte[10]; for (int i = 0; i < 10; i++) truncatedAuthenticationSignature[i] = hmacSha1.Hash[i]; // add Authentication Key and Authentication Signature to the end of the wireline message List resp = new List(wirelineMessage); resp.AddRange(AuthenticationID); resp.AddRange(truncatedAuthenticationSignature); // return the complete wireline message return resp.ToArray(); } #region WIRELINE MESSAGES /// /// Wireline registration procedure. This procedure creats the wireline message which will be sent to the /// repeater in order to create the wireline registration /// /// The slot on which the wireline registration is required /// The list of entries for which the call messages will be received and parsed public void Register(SlotNumber slot, List regEntriesList) { // create a wireline registration object and populate it with the required values WirelineRegistration reg = new WirelineRegistration(); reg.slotNumber = slot; reg.wirelineChannelStatus = Wireline.WirelineChannelStatus.NotRegisteredWLStatus; // copy all registry foreach (RegEntry regEntry in regEntriesList) reg.regEntriesList.Add(regEntry); // add Authentication Signature to the wireline message byte[] toSend = AddWirelineSignatureKey(createWirelineMessage(Operations.WL_REGISTRATION_REQUEST, reg)); // send wireline message to the repeater SendWirelineMessage(toSend); } /// /// Deregister a prior registration on a desired slot /// /// The slot on which the wireline registration was done public void DeRegister(SlotNumber slot) { WirelineQueryRegistration queryReg = new WirelineQueryRegistration(); queryReg.regOpCode = RegistrationOperationCode.DeRegister; queryReg.slotNumber = slot; // add Authentication Signature to the wireline message byte[] toSend = AddWirelineSignatureKey(createWirelineMessage(Operations.WL_REGISTRATION_GENERAL_OPS, queryReg)); // send wireline message to the repeater SendWirelineMessage(toSend); } // query a prior registration to find out the Identifier of the registration public void QueryRegistration(SlotNumber slot) { WirelineQueryRegistration queryReg = new WirelineQueryRegistration(); queryReg.regOpCode = RegistrationOperationCode.QueryRegStatus; queryReg.slotNumber = slot; // add Authentication Signature to the wireline message byte[] toSend = AddWirelineSignatureKey(createWirelineMessage(Operations.WL_REGISTRATION_GENERAL_OPS, queryReg)); SendWirelineMessage(toSend); } // check what protocol version is supported by the repeater public void ProtocolVersionQuery() { // add Authentication Signature to the wireline message byte[] toSend = AddWirelineSignatureKey(createWirelineMessage(Operations.WL_PROTOCOL_VERSION_QUERY, null)); SendWirelineMessage(toSend); } // request status from the repeater about the rest channel or conventional channel // depending on the system type public void ChannelStatusReq(SlotNumber slot) { CSBKCall call = new CSBKCall(); call.slotNumber = slot; // add Authentication Signature to the wireline message byte[] toSend = AddWirelineSignatureKey(createWirelineMessage(Operations.WL_CHNL_STATUS_QUERY, call)); SendWirelineMessage(toSend); } #region ENABLE/DISABLE /// /// Enable a radio on a specific slot /// /// The radio ID of the unit which needs to be enabled /// Slot number on which the request will be sent public void EnableRadioOnSlot(Int64 radioID, SlotNumber slot) { CSBKCall call = new CSBKCall(); call.slotNumber = slot; call.callType = CallType.RadioUninhibitRequest; call.sourceID = le.myselfPeer.peerID; call.targetID = radioID; // add Authentication Signature to the wireline message byte[] toSend = AddWirelineSignatureKey(createWirelineMessage(Operations.WL_VC_CHNL_CTRL_REQUEST, call)); SendWirelineMessage(toSend); } /// /// Enable a radio on the current slot /// /// The radio ID of the unit which needs to be enabled public void EnableRadio(Int64 radioID) { EnableRadioOnSlot(radioID, GetCurrentSlot()); } /// /// Disable a radio on a specific slot /// /// The radio ID of the unit which needs to be disabled /// Slot number on which the request will be sent public void DisableRadioOnSlot(Int64 radioID, SlotNumber slot) { CSBKCall call = new CSBKCall(); call.slotNumber = slot; call.callType = CallType.RadioInhibitRequest; call.sourceID = le.myselfPeer.peerID; call.targetID = radioID; // add Authentication Signature to the wireline message byte[] toSend = AddWirelineSignatureKey(createWirelineMessage(Operations.WL_VC_CHNL_CTRL_REQUEST, call)); // if thread allready exists I should send the commnad only once if (mem.Contains(String.Format("{0}.{1}", radioID, slot))) { // get the thread and reset the counter to 0 //Thread running = (Thread)mem.Get(String.Format("{0}.{1}", radioID, slot)); return; } // create a thread which will resend the message after 3 seconds Thread p = new Thread(() => { // save the message which needs to be sent byte[] toSendd = (byte[])toSend.Clone(); Int64 radioIDD = radioID; SlotNumber slott = slot; int count = 0; // run this procedure for three times or while the answer is received from the repeater while (mem.Contains(String.Format("{0}.{1}", radioID, slot))) { Thread.CurrentThread.IsBackground = true; // send wireline message each 3 seconds if (count++ % 30 == 0) { //Utils.WriteLine(String.Format("Thread {0}.{1} is alive", radioID, slot)); SendWirelineMessage(toSendd); } Thread.Sleep(100); // sleep 100 ms } //Utils.WriteLine(String.Format("Thread {0}.{1} ended", radioID, slot)); }); p.Name = String.Format("{0}.{1}", radioID, slot); p.Start(); // add thread to the memory cache and set live duration to 10 seconds mem.Add(p.Name, p, new CacheItemPolicy() { AbsoluteExpiration = DateTime.UtcNow.AddSeconds(10) }); } /// /// Disable a radio on the current slot /// /// The radio ID of the unit which needs to be disabled public void DisableRadio(Int64 radioID) { DisableRadioOnSlot(radioID, GetCurrentSlot()); } /// /// Event Callback for when a radio changes its status to Enable or Disable. /// This callback will have to remove the thread that sends the Inhibit [disable] request from memCache /// /// The radio which changed its status /// The slot on which the radio had reported /// The radio state. Will be true if the radio is Enabled or false otherwise void Wireline_OnRadioStatusChanged(long radioID, Wireline.SlotNumber slot, bool isEnabled) { // close the thread which sends the Inhibit [disble] command, if exists if (!isEnabled && mem.Contains(String.Format("{0}.{1}", radioID, slot))) { mem.Remove(String.Format("{0}.{1}", radioID, slot)); } } /// /// Acknowledge an emergency received from a field radio on the current selected slot /// /// The field radio which had sent the emergency public void EmergencyAck(Int64 radioID) { EmergencyACKOnSlot(radioID, GetCurrentSlot()); } /// /// Acknowledge an emergency received from a field radio /// /// The field radio which had sent the emergency /// The slot on which the request will be sent public void EmergencyACKOnSlot(Int64 radioID, SlotNumber slot) { // response to Emergency with Emerg ACK CSBKCall csbkCall = new CSBKCall(); csbkCall.slotNumber = slot; csbkCall.sourceID = le.myselfPeer.peerID; csbkCall.targetID = radioID; csbkCall.callType = CallType.EmergencyCSBKAlertResponse; this.SendWirelineMessage(AddWirelineSignatureKey(createWirelineMessage(Operations.WL_VC_CHNL_CTRL_REQUEST, csbkCall))); } #endregion #region CALL /// /// Initiate a private call on the specified slot /// /// The radio ID of the field station which needs to be called /// Slot number on which the request will be sent public void InitiatePrivateCallOnSlot(Int64 radioID, SlotNumber slot) { CSBKCall call = new CSBKCall(); call.callType = CallType.PrivateCallRequest; call.preambleDuration = PreambleDuration.BurstDurationMS; call.preambleValue = 0x03; call.slotNumber = slot; call.sourceID = (slot == SlotNumber.Slot1 ? le.myselfPeer.slot1ID : le.myselfPeer.slot2ID); call.targetID = radioID; if ((slot == SlotNumber.Slot1 && crtCSBKCallStausSlot1 != null) || (slot == SlotNumber.Slot2 && crtCSBKCallStausSlot2 != null)) { // save current call parameters crtCSBKCallStausSlot1 = call; // add Authentication Signature to the wireline message byte[] toSend = AddWirelineSignatureKey(createWirelineMessage(Operations.WL_VC_CHNL_CTRL_REQUEST, call)); SendWirelineMessage(toSend); } } public void InitiatePrivateCallOnSlotAfterPreamble(Int64 radioID, SlotNumber slot) { CSBKCall call = new CSBKCall(); call.callType = CallType.PrivateCallResponse; call.preambleDuration = PreambleDuration.BurstDurationMS; call.preambleValue = 0x03; call.slotNumber = slot; call.sourceID = (slot == SlotNumber.Slot1 ? le.myselfPeer.slot1ID : le.myselfPeer.slot2ID); call.targetID = radioID; call.callAttributes = CallAttributes.SmartAfterPreamble; if ((slot == SlotNumber.Slot1 && crtCSBKCallStausSlot1 != null) || (slot == SlotNumber.Slot2 && crtCSBKCallStausSlot2 != null)) { // save current call parameters crtCSBKCallStausSlot1 = call; // add Authentication Signature to the wireline message byte[] toSend = AddWirelineSignatureKey(createWirelineMessage(Operations.WL_VC_CHNL_CTRL_REQUEST, call)); SendWirelineMessage(toSend); } } /// /// Terminate a private call on the specified slot /// /// The radio ID of the field station which needs to end the call /// Slot number on which the request will be sent public void EndPrivateCallOnSlot(Int64 radioID, SlotNumber slot) { CSBKCall call = new CSBKCall(); call.callType = CallType.PrivateCallRequest; call.preambleDuration = PreambleDuration.BurstDurationMS; call.preambleValue = 0x03; call.slotNumber = slot; call.sourceID = slot == SlotNumber.Slot1 ? le.myselfPeer.slot1ID : le.myselfPeer.slot2ID; call.targetID = radioID; // add Authentication Signature to the wireline message byte[] toSend = AddWirelineSignatureKey(createWirelineMessage(Operations.WL_VC_CHNL_CTRL_REQUEST, call)); SendWirelineMessage(toSend); } /// /// Initiate a group call on the specified slot /// /// The group ID of the field stations which needs to be called /// Slot number on which the request will be sent public void InitiateGroupCallOnSlot(Int64 groupID, SlotNumber slot) { CSBKCall call = new CSBKCall(); call.callType = CallType.GroupVoiceCall; call.preambleDuration = PreambleDuration.BurstDurationMS; call.preambleValue = 0x03; call.slotNumber = slot; call.sourceID = slot == SlotNumber.Slot1 ? le.myselfPeer.slot1ID : le.myselfPeer.slot2ID; call.targetID = groupID; // save current call parameters crtCSBKCallStausSlot1 = call; // add Authentication Signature to the wireline message byte[] toSend = AddWirelineSignatureKey(createWirelineMessage(Operations.WL_VC_CHNL_CTRL_REQUEST, call)); SendWirelineMessage(toSend); } /// /// Terminate a group call on the specified slot /// /// The group ID of the field stations which needs to end the call /// Slot number on which the request will be sent public void EndGroupCallOnSlot(Int64 groupID, SlotNumber slot) { CSBKCall call = new CSBKCall(); call.callType = CallType.GroupVoiceCall; call.preambleDuration = PreambleDuration.BurstDurationMS; call.preambleValue = 0x03; call.slotNumber = slot; call.sourceID = slot == SlotNumber.Slot1 ? le.myselfPeer.slot1ID : le.myselfPeer.slot2ID; call.targetID = groupID; // add Authentication Signature to the wireline message byte[] toSend = AddWirelineSignatureKey(createWirelineMessage(Operations.WL_VC_CHNL_CTRL_REQUEST, call)); SendWirelineMessage(toSend); } /// /// Initiate an all call on the specified slot /// /// Slot number on which the request will be sent public void InitiateAllCallOnSlot(SlotNumber slot) { CSBKCall call = new CSBKCall(); call.callType = CallType.AllCall; call.preambleDuration = PreambleDuration.BurstDurationMS; call.preambleValue = 0x03; call.slotNumber = slot; call.sourceID = slot == SlotNumber.Slot1 ? le.myselfPeer.slot1ID : le.myselfPeer.slot2ID; call.targetID = 16777215; // save current call parameters crtCSBKCallStausSlot1 = call; // add Authentication Signature to the wireline message byte[] toSend = AddWirelineSignatureKey(createWirelineMessage(Operations.WL_VC_CHNL_CTRL_REQUEST, call)); SendWirelineMessage(toSend); } /// /// Terminate an all call on the specified slot /// /// Slot number on which the request will be sent public void EndAllCallOnSlot(SlotNumber slot) { CSBKCall call = new CSBKCall(); call.callType = CallType.AllCall; call.preambleDuration = PreambleDuration.BurstDurationMS; call.preambleValue = 0x03; call.slotNumber = slot; call.sourceID = slot == SlotNumber.Slot1 ? le.myselfPeer.slot1ID : le.myselfPeer.slot2ID; call.targetID = 16777215; // add Authentication Signature to the wireline message byte[] toSend = AddWirelineSignatureKey(createWirelineMessage(Operations.WL_VC_CHNL_CTRL_REQUEST, call)); SendWirelineMessage(toSend); } /// /// Event Callback for a channel control status received. The event is triggered each time a call changes /// it's status, this is equal to channel status had changed /// /// The radio ID of the field radio which had initiated the call /// The radio ID of the field radio which had been called /// The type of call which is in progress/was made /// The status of the channel /// The slot on which the call was made void Wireline_OnChannelControlStatusReceived(Int64 callID, CallType callType, ChannelControlStatus chnCtrlStatus, SlotNumber slot) { // call has been accepted and I can send voice as from the docs/Smart Wireshark if (chnCtrlStatus == ChannelControlStatus.SmartInProgress) { Thread p = new Thread(new ThreadStart(CSBKVoiceCall)); p.Start(); } } /// /// Event Callback for a call session status received. The event is triggered each time a call is transmited, /// declined etc. /// /// The radio ID of the field radio which had initiated the call /// The radio ID of the field radio which had been called /// The type of call which is in progress/was made /// The status of the call on Transportation Layer /// The slot on which the call was made void Wireline_OnCallSesionStatusReceived(Int64 sourceID, Int64 targertID, CallType callType, CallSessionStatus callSessionStatus, SlotNumber slot) { // get csbkCall for current slot number CSBKCall crtCSBKCall = (slot == SlotNumber.Slot1 ? crtCSBKCallStausSlot1 : crtCSBKCallStausSlot2); // I should resend the call request because the call is hanged as a result of a CSBK Preamble recieved if (callSessionStatus == CallSessionStatus.CallHang && crtCSBKCall.isCSBKPreambleReceived) { switch (crtCSBKCall.callType) { // initiate the all call on selected slot case CallType.AllCall: InitiateAllCallOnSlot(slot); break; // initiate the group call on selected slot case CallType.GroupVoiceCall: InitiateGroupCallOnSlot(crtCSBKCall.targetID, slot); break; // initiate the private call on selected slot case CallType.PrivateCallRequest: InitiatePrivateCallOnSlotAfterPreamble(crtCSBKCall.targetID, slot); break; } crtCSBKCall.isCSBKPreambleReceived = false; } } /// /// /// /// /// private void Wireline_OnCSBKCallReceived(Int64 sourceID, Int64 targetID, CallType callType, SlotNumber slot) { // get csbkCall for current slot number CSBKCall crtCSBKCall = (slot == SlotNumber.Slot1 ? crtCSBKCallStausSlot1 : crtCSBKCallStausSlot2); // flag that the Preamble CSBK was received and I can now resend the cmd when the hang status is received crtCSBKCall.isCSBKPreambleReceived = true; } private void CSBKVoiceCall() { for(int i=0; i<30; i++) { CSBKCall csbkCall = new CSBKCall(); csbkCall.slotNumber = SlotNumber.Slot1; // add Authentication Signature to the wireline message byte[] toSend = AddWirelineSignatureKey(createWirelineMessage(Operations.WL_VC_VOICE_BURST, csbkCall)); Thread.Sleep(100); SendWirelineMessage(toSend); } } #endregion // return the current slot number public SlotNumber GetCurrentSlot() { return currentSlot; } public SlotNumber GetSlotNumber(int slotNumber) { switch (slotNumber) { case 1: return SlotNumber.Slot1; case 2: return SlotNumber.Slot2; case 3: return SlotNumber.BothSlots; case 0: { if (le.systemType == LinkEstablishment.SystemType.IP_SITE_CONNECT) return SlotNumber.SiteIPAddress; else if (le.systemType == LinkEstablishment.SystemType.CAPACITY_PLUS) return SlotNumber.RestChannel; else return SlotNumber.BothSlots; } default: return SlotNumber.BothSlots; } } #endregion private Int32 csbkCallParam1 = 0x7bee; private Int32 csbkCallParam2 = 0x3178; private Int32 csbkCallParam3 = 0x94; private Int32 csbkCallParam4 = 0x2A; private static int val = 0x01; private byte[] createWirelineMessage(Operations operation, Object parameters) { List responseList = new List(); // opCode responseList.Add(0xB2); // ID of the sending peer = me responseList.Add(BitConverter.GetBytes(le.myselfPeer.peerID)[3]); responseList.Add(BitConverter.GetBytes(le.myselfPeer.peerID)[2]); responseList.Add(BitConverter.GetBytes(le.myselfPeer.peerID)[1]); responseList.Add(BitConverter.GetBytes(le.myselfPeer.peerID)[0]); // increment registration PDU number if (operation == Operations.WL_REGISTRATION_REQUEST || operation == Operations.WL_REGISTRATION_GENERAL_OPS) WirelineRegistration.registrationPduID++; byte[] response = { }; switch (operation) { #region WL_REGISTRATION_REQUEST case Operations.WL_REGISTRATION_REQUEST: { WirelineRegistration wireReg = null; try { // get the paramerets in the correct form wireReg = (WirelineRegistration)parameters; } catch (Exception) { // THROW AN EXCEPTION return new byte[0]; } /* Wireline Operation Code*/ responseList.Add((byte)Operations.WL_REGISTRATION_REQUEST); /* registrationSlotNumber - Only for de-register operation in SS/IPSC mode */ if (le.systemType == LinkEstablishment.SystemType.LINKED_CAPACITY_PLUS) responseList.Add((byte)0x00); else responseList.Add((byte)wireReg.slotNumber); /* registrationPduID - The identifier of the registration PDU [Uint32] */ responseList.Add(BitConverter.GetBytes(WirelineRegistration.registrationPduID)[3]); responseList.Add(BitConverter.GetBytes(WirelineRegistration.registrationPduID)[2]); responseList.Add(BitConverter.GetBytes(WirelineRegistration.registrationPduID)[1]); responseList.Add(BitConverter.GetBytes(WirelineRegistration.registrationPduID)[0]); /* registrationID - The identifier associated with the registration profile in this PDU. [Uint16] */ responseList.Add(BitConverter.GetBytes(wireReg.registrationID)[1]); responseList.Add(BitConverter.GetBytes(wireReg.registrationID)[0]); /* wirelineStatusRegistration - Registration flag for Wireline Channel Status [Uint8] */ responseList.Add((byte)wireReg.wirelineChannelStatus); /* numberOfRegistrationEntries - Number of registration entries in this registration message [Uint8] */ responseList.Add((byte)wireReg.regEntriesList.Count); foreach (RegEntry entity in wireReg.regEntriesList) { /* AddressType - Address type of the call being registered in this entry */ responseList.Add((byte)entity.addressType); /* addressRangeStart - Start of radio/group addresses (CAI ID) range in this entry [Uint32] */ responseList.Add(BitConverter.GetBytes(entity.addressRangeStart)[3]); responseList.Add(BitConverter.GetBytes(entity.addressRangeStart)[2]); responseList.Add(BitConverter.GetBytes(entity.addressRangeStart)[1]); responseList.Add(BitConverter.GetBytes(entity.addressRangeStart)[0]); /* addressRangeEnd - Start of radio/group addresses (CAI ID) range in this entry [Uint32] */ responseList.Add(BitConverter.GetBytes(entity.addressRangeEnd)[3]); responseList.Add(BitConverter.GetBytes(entity.addressRangeEnd)[2]); responseList.Add(BitConverter.GetBytes(entity.addressRangeEnd)[1]); responseList.Add(BitConverter.GetBytes(entity.addressRangeEnd)[0]); /* Reserved field */ responseList.Add((byte)0x80); /* CSBK Attributes - Attributes of CSBK Registration in this entry */ responseList.Add((byte)entity.csbkAttributes); /* Reserved field */ responseList.Add((byte)0x00); } /* Current or Accepted Wireline Protocol Version */ responseList.Add(currentWirelineProtocolVersion); responseList.Add(oldestWirelineProtocolVersion); // convert the list of bytes to a byte array response = new byte[responseList.Count]; for (int i = 0; i < responseList.Count; i++) response[i] = responseList[i]; break; } #endregion #region WL_REGISTRATION_GENERAL_OPS case Operations.WL_REGISTRATION_GENERAL_OPS: { WirelineQueryRegistration queryReg = null; try { // get the paramerets in the correct form queryReg = (WirelineQueryRegistration)parameters; } catch (Exception) { // THROW AN EXCEPTION return new byte[0]; } /* Wireline Operation Code*/ responseList.Add((byte)Operations.WL_REGISTRATION_GENERAL_OPS); /* registrationSlotNumber - Only for de-register operation in SS/IPSC mode */ if(le.systemType == LinkEstablishment.SystemType.IP_SITE_CONNECT && queryReg.regOpCode == RegistrationOperationCode.DeRegister) responseList.Add((byte)queryReg.slotNumber); else responseList.Add((byte)0x00); /* registrationPduID - The identifier of the registration PDU [Uint32] */ responseList.Add(BitConverter.GetBytes(queryReg.registrationPduID)[3]); responseList.Add(BitConverter.GetBytes(queryReg.registrationPduID)[2]); responseList.Add(BitConverter.GetBytes(queryReg.registrationPduID)[1]); responseList.Add(BitConverter.GetBytes(queryReg.registrationPduID)[0]); /* registrationOperationOpcode - The registration Operation Code [Uint8] */ responseList.Add((byte)queryReg.regOpCode); /* Current or Accepted Wireline Protocol Version */ responseList.Add(currentWirelineProtocolVersion); responseList.Add(oldestWirelineProtocolVersion); // convert the list of bytes to a byte array response = new byte[responseList.Count]; for (int i = 0; i < responseList.Count; i++) response[i] = responseList[i]; break; } #endregion #region WL_PROTOCOL_VERSION_QUERY case Operations.WL_PROTOCOL_VERSION_QUERY: { /* Wireline Operation Code*/ responseList.Add((byte)Operations.WL_PROTOCOL_VERSION_QUERY); /* Reserved field */ responseList.Add((byte)0x00); /* query ID - ID of the protocol version query PDU. Response from the repeater * pertaining to this message has the same Query ID value */ responseList.Add(BitConverter.GetBytes(queryID)[3]); responseList.Add(BitConverter.GetBytes(queryID)[2]); responseList.Add(BitConverter.GetBytes(queryID)[1]); responseList.Add(BitConverter.GetBytes(queryID)[0]); //increment query id queryID++; /* Current or Accepted Wireline Protocol Version */ responseList.Add(currentWirelineProtocolVersion); responseList.Add(oldestWirelineProtocolVersion); // convert the list of bytes to a byte array response = new byte[responseList.Count]; for (int i = 0; i < responseList.Count; i++) response[i] = responseList[i]; break; } #endregion #region WL_CHNL_STATUS_QUERY case Operations.WL_CHNL_STATUS_QUERY: { CSBKCall call = null; try { // get the paramerets in the correct form call = (CSBKCall)parameters; } catch (Exception) { // THROW AN EXCEPTION return new byte[0]; } /* Wireline Operation Code*/ responseList.Add((byte)Operations.WL_CHNL_STATUS_QUERY); /* slot number - Specifies the channel/slot reporting the channel status.*/ responseList.Add((byte)call.slotNumber); /* Current or Accepted Wireline Protocol Version */ responseList.Add(currentWirelineProtocolVersion); responseList.Add(oldestWirelineProtocolVersion); // convert the list of bytes to a byte array response = new byte[responseList.Count]; for (int i = 0; i < responseList.Count; i++) response[i] = responseList[i]; break; } #endregion #region WL_VC_CHNL_CTRL_REQUEST /* * This message is sent from the third party applications to the repeater peer to initial the * CSBK call or send CSBK call response */ case Operations.WL_VC_CHNL_CTRL_REQUEST: { CSBKCall call = null; try { // get the paramerets in the correct form call = (CSBKCall)parameters; } catch (Exception ) { // THROW AN EXCEPTION return new byte[0]; } /* Wireline Operation Code*/ responseList.Add((byte)Operations.WL_VC_CHNL_CTRL_REQUEST); /* slot number - Specifies the channel/slot reporting the channel status */ responseList.Add((byte)call.slotNumber); /* call ID */ responseList.Add(BitConverter.GetBytes(call.callID)[3]); responseList.Add(BitConverter.GetBytes(call.callID)[2]); responseList.Add(BitConverter.GetBytes(call.callID)[1]); responseList.Add(BitConverter.GetBytes(call.callID)[0]); /* callType - The type of call */ responseList.Add((byte)call.callType); /* source ID - The ID of the subscriber that initiated the call */ responseList.Add(BitConverter.GetBytes(call.sourceID)[3]); responseList.Add(BitConverter.GetBytes(call.sourceID)[2]); responseList.Add(BitConverter.GetBytes(call.sourceID)[1]); responseList.Add(BitConverter.GetBytes(call.sourceID)[0]); /* target ID - The ID of the subscriber that initiated the call */ responseList.Add(BitConverter.GetBytes(call.targetID)[3]); responseList.Add(BitConverter.GetBytes(call.targetID)[2]); responseList.Add(BitConverter.GetBytes(call.targetID)[1]); responseList.Add(BitConverter.GetBytes(call.targetID)[0]); /* accessCriteria - The priority type to access the channel */ responseList.Add((byte)call.accessCriteria); /* callAttributes - General Call Attributes */ responseList.Add((byte)call.callAttributes); /* Reserved field */ responseList.Add((byte)0x00); /* preambleDuration - Only available for CSBK Call. * The numbers of Preamble bursts send before transmission. */ if (call.preambleDuration.ToString().Contains("BurstDurationMS")) responseList.Add((byte)call.preambleValue); else responseList.Add((byte)0x00); /* Reserved field */ responseList.Add((byte)0x00); responseList.Add((byte)0x00); /* CSBK Arguments - Only available for CSBK call * The arguments encoded in the DMR CSBK burst [Uint64] */ responseList.Add((byte)0x00); responseList.Add((byte)0x00); // reserved responseList.Add((byte)0x00); responseList.Add((byte)0x00); responseList.Add((byte)0x00); responseList.Add((byte)0x00); responseList.Add((byte)0x00); responseList.Add((byte)0x00); /* responseList.Add(BitConverter.GetBytes(call.targetID)[2]); responseList.Add(BitConverter.GetBytes(call.targetID)[1]); responseList.Add(BitConverter.GetBytes(call.targetID)[0]); responseList.Add(BitConverter.GetBytes(call.sourceID)[2]); responseList.Add(BitConverter.GetBytes(call.sourceID)[1]); responseList.Add(BitConverter.GetBytes(call.sourceID)[0]); */ /* Current or Accepted Wireline Protocol Version */ responseList.Add(currentWirelineProtocolVersion); responseList.Add(oldestWirelineProtocolVersion); // convert the list of bytes to a byte array response = new byte[responseList.Count]; for (int i = 0; i < responseList.Count; i++) response[i] = responseList[i]; break; } #endregion #region WL_VC_VOICE_BURST /* * This message is sent from the third party applications to the repeater peer containing * all the voice data [I hope: pray] */ case Operations.WL_VC_VOICE_BURST: { CSBKCall crtCSBKCall = null; try { // get the paramerets in the correct form crtCSBKCall = (CSBKCall)parameters; // set current csbk call values depending on the slot crtCSBKCall = (crtCSBKCall.slotNumber == SlotNumber.Slot1 ? crtCSBKCallStausSlot1 : crtCSBKCallStausSlot2); } catch (Exception) { // THROW AN EXCEPTION return new byte[0]; } /* Wireline Operation Code*/ responseList.Add((byte)Operations.WL_VC_VOICE_BURST); /* slot number - Specifies the channel/slot reporting the channel status */ responseList.Add((byte)crtCSBKCall.slotNumber); /* call ID */ responseList.Add(BitConverter.GetBytes(crtCSBKCall.crtCallID)[3]); responseList.Add(BitConverter.GetBytes(crtCSBKCall.crtCallID)[2]); responseList.Add(BitConverter.GetBytes(crtCSBKCall.crtCallID)[1]); responseList.Add(BitConverter.GetBytes(crtCSBKCall.crtCallID)[0]); /* callType - The type of call */ responseList.Add((byte)crtCSBKCall.callType); /* source ID - The ID of the subscriber that initiated the call */ responseList.Add(BitConverter.GetBytes(crtCSBKCall.sourceID)[3]); responseList.Add(BitConverter.GetBytes(crtCSBKCall.sourceID)[2]); responseList.Add(BitConverter.GetBytes(crtCSBKCall.sourceID)[1]); responseList.Add(BitConverter.GetBytes(crtCSBKCall.sourceID)[0]); /* target ID - The ID of the subscriber that initiated the call */ responseList.Add(BitConverter.GetBytes(crtCSBKCall.targetID)[3]); responseList.Add(BitConverter.GetBytes(crtCSBKCall.targetID)[2]); responseList.Add(BitConverter.GetBytes(crtCSBKCall.targetID)[1]); responseList.Add(BitConverter.GetBytes(crtCSBKCall.targetID)[0]); /* RESERVED ? ? ? */ responseList.Add(0xA0); /* RESERVED */ responseList.Add(0x00); /* callAttributes or Preamble Duration */ responseList.Add((byte)crtCSBKCall.callAttributes); /* Reserved field */ responseList.Add((byte)0x5D); /* CSBK Field Param 1 */ responseList.Add(BitConverter.GetBytes(csbkCallParam1)[1]); responseList.Add(BitConverter.GetBytes(csbkCallParam1)[0]); csbkCallParam1++; /* CSBK Field Param 2 */ responseList.Add(BitConverter.GetBytes(csbkCallParam2)[1]); responseList.Add(BitConverter.GetBytes(csbkCallParam2)[0]); /* CSBK Field Param 3 */ responseList.Add(BitConverter.GetBytes(csbkCallParam3)[0]); csbkCallParam3 = csbkCallParam3 + 0x02; /* CSBK Field Param 4 */ responseList.Add(BitConverter.GetBytes(csbkCallParam4)[0]); csbkCallParam4 = csbkCallParam4 - 0x20; /* CSBK Arguments - Only available for CSBK call * The arguments encoded in the DMR CSBK burst [Uint64] */ responseList.Add((byte)0x00); responseList.Add((byte)0x00); // reserved responseList.Add((byte)0x00); responseList.Add((byte)0x00); /* Seqeuence number */ responseList.Add((byte)(val++ % 6)); /* 10 RESERVED Fields */ responseList.Add((byte)0x00); responseList.Add((byte)0x00); responseList.Add((byte)0x00); responseList.Add((byte)0x00); responseList.Add((byte)0x00); responseList.Add((byte)0x00); responseList.Add((byte)0x00); responseList.Add((byte)0x00); responseList.Add((byte)0x00); responseList.Add((byte)0x00); /* add 19 bytes for voice ??? */ responseList.AddRange(new byte[] { 0xb8, 0x34, 0xb3, 0x87, 0xa2, 0xa2, 0xb6, 0x0e, 0x29, 0x8b, 0x99, 0x80, 0xcd, 0x83, 0xb0, 0x39, 0x94, 0x59, 0xc0 }); /* RESERVED */ responseList.Add(0x00); responseList.Add(0x00); /* Current or Accepted Wireline Protocol Version */ responseList.Add(currentWirelineProtocolVersion); responseList.Add(oldestWirelineProtocolVersion); // convert the list of bytes to a byte array response = new byte[responseList.Count]; for (int i = 0; i < responseList.Count; i++) response[i] = responseList[i]; break; } #endregion } return response; } public void decode(byte[] toDecode, string peerIP, Int32 peerPort) { int crtPosition = 0; byte opCode = toDecode[crtPosition++]; // first opCode is for a CSBK Call message //if (opCode != 0xB2) // return; /* get peerID */ byte[] remotePeerID = new byte[4]; remotePeerID[3] = toDecode[crtPosition++]; remotePeerID[2] = toDecode[crtPosition++]; remotePeerID[1] = toDecode[crtPosition++]; remotePeerID[0] = toDecode[crtPosition++]; Int64 peerID = BitConverter.ToUInt32(remotePeerID, 0); byte wirelineOpCode = toDecode[crtPosition++]; Utils.WriteLine(""); Utils.WriteLine(String.Format("»»» {0}", GetMessageType(wirelineOpCode)), ConsoleColor.Yellow); //Utils.WriteLine(Utils.printBytesArray(toDecode)); switch (wirelineOpCode) { #region WL_REGISTRATION_STATUS case (byte)Operations.WL_REGISTRATION_STATUS: { if (wirelineProtocolVersion >= 1) { // identifier of the registration PDU [Uint32] byte[] registrationPduID = new byte[4]; registrationPduID[3] = toDecode[crtPosition++]; registrationPduID[2] = toDecode[crtPosition++]; registrationPduID[1] = toDecode[crtPosition++]; registrationPduID[0] = toDecode[crtPosition++]; Int64 registrPduID = BitConverter.ToUInt32(registrationPduID, 0); // identifier associated with the registration profile in this PDU [Uint16] byte[] registrationIDSlot1 = new byte[2]; registrationIDSlot1[1] = toDecode[crtPosition++]; registrationIDSlot1[0] = toDecode[crtPosition++]; // identifier associated with the registration profile in this PDU [Uint16] byte[] registrationIDSlot2 = new byte[2]; registrationIDSlot2[1] = toDecode[crtPosition++]; registrationIDSlot2[0] = toDecode[crtPosition++]; // registration status [Uint8] byte registrationStatus = toDecode[crtPosition++]; // registration status code [Uint8] byte registrationStatusCode = toDecode[crtPosition++]; // Current or Accepted Wireline Protocol Version [Uint8] byte currentWirelineProtocolVersion = toDecode[crtPosition++]; // Oldest Wireline Protocol Version [Uint8] byte oldestWirelineProtocolVersion = toDecode[crtPosition++]; Utils.WriteLine(String.Format("\tRegistration Status: {0}", GetRegistrationStatus(registrationStatus)), ConsoleType.WIRELINE); Utils.WriteLine(String.Format("\tRegistration Status Code: {0}", GetRegistrationStatusCode(registrationStatusCode)), ConsoleType.WIRELINE); // do not display and trigger nothing if the registered PDU is empty if (!(registrPduID > 0)) break; if (BitConverter.ToInt16(registrationIDSlot1, 0) != 0) { Utils.WriteLine(String.Format("\tSlot 1 Registered"), ConsoleType.WIRELINE); OnRegistrationReceived(true, SlotNumber.Slot1); } if (BitConverter.ToInt16(registrationIDSlot2, 0) != 0) { Utils.WriteLine(String.Format("\tSlot 2 Registered"), ConsoleType.WIRELINE); OnRegistrationReceived(true, SlotNumber.Slot2); } } break; } #endregion #region WL_REGISTRATION_GENERAL_OPS case (byte)Operations.WL_REGISTRATION_GENERAL_OPS: { if (wirelineProtocolVersion >= 1) { // identifier associated with the registration profile in this PDU [Uint16] int registrationSlot = toDecode[crtPosition++]; // identifier of the registration PDU [Uint32] byte[] registrationPduID = new byte[4]; registrationPduID[3] = toDecode[crtPosition++]; registrationPduID[2] = toDecode[crtPosition++]; registrationPduID[1] = toDecode[crtPosition++]; registrationPduID[0] = toDecode[crtPosition++]; Int64 registrPduID = BitConverter.ToUInt32(registrationPduID, 0); // registration operation code [Uint8] byte registrationOperationCode = toDecode[crtPosition++]; // Current or Accepted Wireline Protocol Version [Uint8] byte currentWirelineProtocolVersion = toDecode[crtPosition++]; // Oldest Wireline Protocol Version [Uint8] byte oldestWirelineProtocolVersion = toDecode[crtPosition++]; Utils.WriteLine("RegSlot " + registrationSlot + " | OpCode " + registrationOperationCode + " [" + registrPduID + "]", ConsoleType.WIRELINE); } break; } #endregion #region WL_PROTOCOL_VERSION_QUERY_RESPONSE /* * This message is sent from the repeater peer to the third party application as a response * to the WL_PROTOCOL_VERSION_QUERY. The repeater always responds with its * current and oldest version irrespective of the version numbers in the Wireline Protocol * Version Query. */ case (byte)Operations.WL_PROTOCOL_VERSION_QUERY_RESPONSE: { if (wirelineProtocolVersion >= 3) { // reserved field Int64 reserved = toDecode[crtPosition++]; // queryID - ID of the protocol version query PDU. // Response from the repeater pertaining to this message // has the same Query ID value [Uint32] byte[] queryID = new byte[4]; queryID[3] = toDecode[crtPosition++]; queryID[2] = toDecode[crtPosition++]; queryID[1] = toDecode[crtPosition++]; queryID[0] = toDecode[crtPosition++]; Int64 querryID = BitConverter.ToUInt32(queryID, 0); // Current Wireline Protocol Version [Uint8] byte currentWirelineProtocolVersion = toDecode[crtPosition++]; // Oldest Wireline Protocol Version [Uint8] byte oldestWirelineProtocolVersion = toDecode[crtPosition++]; } break; } #endregion #region WL_CHNL_STATUS /* * This message is sent from the repeater peer to the third party applications to report the * conventional channel status of SS/IPSC system or rest channel status of CPP/LCP * system. This message is only sent out by the first repeater peer, which receives the call * over the air from the radio or receives the call over the network from the third party * application. * Only the third party application who has subscribed the wireline channel status during * wireline registration can receive this message. * A repeater peer sends out a Wireline Channel Status in one of the following situations: * 1) Upon receiving a Wireline Channel Status Query from the third party application. * 2) The register channel status of SS/IPSC system or rest channel status of * CPC/LCP system is updated. * In MOTOTRBO 2.2, the data revert repeaters in CPP/LCP system didn’t send Wireline * Channel Status PDU. */ case (byte)Operations.WL_CHNL_STATUS: { if (wirelineProtocolVersion >= 1) { // Slot number - Specifies the channel/slot reporting the channel status.[Uint8] byte slotNumber = toDecode[crtPosition++]; // statusPduID - The ID of the status PDU // uniquely identifies the // channel status PDU. [Uint32] byte[] statusPduID = new byte[4]; statusPduID[3] = toDecode[crtPosition++]; statusPduID[2] = toDecode[crtPosition++]; statusPduID[1] = toDecode[crtPosition++]; statusPduID[0] = toDecode[crtPosition++]; Int64 statusPDUID = BitConverter.ToUInt32(statusPduID, 0); // Only available in IPSC modes. The channel status information. [Uint8] byte conventionalChannelStatus = toDecode[crtPosition++]; // Only available in CPP/LCP modes. The rest channel status information. [Uint8] byte restChannelStatus = toDecode[crtPosition++]; // Call type [Uint8] byte typeOfCall = toDecode[crtPosition++]; // Current Wireline Protocol Version [Uint8] byte currentWirelineProtocolVersion = toDecode[crtPosition++]; // Oldest Wireline Protocol Version [Uint8] byte oldestWirelineProtocolVersion = toDecode[crtPosition++]; Utils.WriteLine(String.Format("\t{0}", GetSlotNumberName(slotNumber, le.systemType)), ConsoleType.WIRELINE); Utils.WriteLine(String.Format("\tConventional Channel Status: {0}", GetChannelStatus(conventionalChannelStatus)), ConsoleType.WIRELINE); Utils.WriteLine(String.Format("\tRest Channel Status: {0}", GetChannelStatus(restChannelStatus)), ConsoleType.WIRELINE); Utils.WriteLine(String.Format("\tCall Type: {0}", GetCallType(typeOfCall)), ConsoleType.WIRELINE); } break; } #endregion #region WL_VC_CHNL_CTRL_STATUS /* * This message is sent from the repeater peer to the third party applications to response * the Wireline Call Channel Control Request. */ case (byte)Operations.WL_VC_CHNL_CTRL_STATUS: { if (wirelineProtocolVersion >= 1) { // Slot number - Specifies the channel/slot reporting the channel status.[Uint8] byte slotNumber = toDecode[crtPosition++]; // call ID - The ID of the call byte[] callID = new byte[4]; callID[3] = toDecode[crtPosition++]; callID[2] = toDecode[crtPosition++]; callID[1] = toDecode[crtPosition++]; callID[0] = toDecode[crtPosition++]; Int64 calllID = BitConverter.ToUInt32(callID, 0); // The type of the call [Uint8] byte callType = toDecode[crtPosition++]; // chnCtrlstatus - Channel control Status [Uint8] byte chnCtrlstatus = toDecode[crtPosition++]; // DeclineReasonCode - The reason for decline the call control request [Uint8] byte declineReasonCode = toDecode[crtPosition++]; // Current Wireline Protocol Version [Uint8] byte currentWirelineProtocolVersion = toDecode[crtPosition++]; // Oldest Wireline Protocol Version [Uint8] byte oldestWirelineProtocolVersion = toDecode[crtPosition++]; Utils.WriteLine(String.Format("\tSlot {0}",GetSlotNumber(slotNumber)), ConsoleType.WIRELINE); Utils.WriteLine(String.Format("\tCall Type: {0}", GetCallType(callType)), ConsoleType.WIRELINE); Utils.WriteLine(String.Format("\tChannel Ctrl Status: {0}", GetChannelControlStatus(chnCtrlstatus)), ConsoleType.WIRELINE); if ((SlotNumber)slotNumber == SlotNumber.Slot1) crtCSBKCallStausSlot1.chnCtrlstatus = (ChannelControlStatus)chnCtrlstatus; else crtCSBKCallStausSlot2.chnCtrlstatus = (ChannelControlStatus)chnCtrlstatus; OnChannelControlStatusReceived(calllID, (CallType)callType, (ChannelControlStatus)chnCtrlstatus, (SlotNumber) slotNumber); } break; } #endregion #region WL_VC_CSBK_CALL /* * This message is sent from the repeater peer to the third party application to send the * CSBK request or response message */ case (byte)Operations.WL_VC_CSBK_CALL: { if (wirelineProtocolVersion >= 1) { // Slot number - Specifies the channel/slot reporting the channel status.[Uint8] byte slotNumber = toDecode[crtPosition++]; // call ID - The ID of the call byte[] callID = new byte[4]; callID[3] = toDecode[crtPosition++]; callID[2] = toDecode[crtPosition++]; callID[1] = toDecode[crtPosition++]; callID[0] = toDecode[crtPosition++]; Int64 calllID = BitConverter.ToUInt32(callID, 0); // The type of the call [Uint8] byte callType = toDecode[crtPosition++]; // source ID - The ID of the subscriber that initiated the call byte[] sourceID = new byte[4]; sourceID[3] = toDecode[crtPosition++]; sourceID[2] = toDecode[crtPosition++]; sourceID[1] = toDecode[crtPosition++]; sourceID[0] = toDecode[crtPosition++]; Int64 sourceeID = BitConverter.ToUInt32(sourceID, 0); // target ID - The ID of the subscriber ot talk-group to which the call is targeted byte[] targetID = new byte[4]; targetID[3] = toDecode[crtPosition++]; targetID[2] = toDecode[crtPosition++]; targetID[1] = toDecode[crtPosition++]; targetID[0] = toDecode[crtPosition++]; Int64 targettID = BitConverter.ToUInt32(targetID, 0); // reserved [Uint8] byte reserved = toDecode[crtPosition++]; // MFID - The DMR Feature Set ID / Manufacturer’s ID [Uint8] byte mfid = toDecode[crtPosition++]; // CSBK Arguments - The reason for decline the call control request [Uint8] byte[] csbkArguments = new byte[8]; csbkArguments[7] = toDecode[crtPosition++]; csbkArguments[6] = toDecode[crtPosition++]; csbkArguments[5] = toDecode[crtPosition++]; csbkArguments[4] = toDecode[crtPosition++]; csbkArguments[3] = toDecode[crtPosition++]; csbkArguments[2] = toDecode[crtPosition++]; csbkArguments[1] = toDecode[crtPosition++]; csbkArguments[0] = toDecode[crtPosition++]; // rawRssiValue - The raw RSSI value for each burst byte[] rawRssiValue = new byte[2]; rawRssiValue[1] = toDecode[crtPosition++]; rawRssiValue[0] = toDecode[crtPosition++]; // Current Wireline Protocol Version [Uint8] byte currentWirelineProtocolVersion = toDecode[crtPosition++]; // Oldest Wireline Protocol Version [Uint8] byte oldestWirelineProtocolVersion = toDecode[crtPosition++]; Utils.WriteLine(String.Format("\t{0}", GetSlotNumberName(slotNumber, le.systemType)), ConsoleType.WIRELINE); Utils.WriteLine(String.Format("\tCall Type: {0}", GetCallType(callType)), ConsoleType.WIRELINE); Utils.WriteLine(String.Format("\tSource ID: {0}", sourceeID), ConsoleType.WIRELINE); Utils.WriteLine(String.Format("\tTarget ID: {0}", targettID), ConsoleType.WIRELINE); switch (callType) { case (byte)CallType.RadioInhibitResponse: { // eg. // 0xB2 0x0 0x0 0x0 0x2 0x17 0x1 0x0 0x0 0x0 0x65 0x4A 0x0 0x0 0x0 0x6F 0x0 0x0 // 0x0 0x1 0x0 0x0 0x10 0x0 0xFF 0x0 0x0 0x1 0x0 0x0 0x6F 0x3B 0x60 0xC 0x4 OnRadioStatusChanged(sourceeID, GetSlotNumber(slotNumber), false); break; } case (byte)CallType.RadioUninhibitResponse: { // eg. // 0xB2 0x0 0x0 0x0 0x2 0x17 0x1 0x0 0x0 0x0 0x4C 0x4C 0x0 0x0 0x0 0x65 0x0 0x0 // 0x0 0x1 0x0 0x0 0x10 0x0 0xFE 0x0 0x0 0x1 0x0 0x0 0x65 0x3B 0x69 0xC 0x4 OnRadioStatusChanged(sourceeID, GetSlotNumber(slotNumber), true); break; } case (byte)CallType.EmergencyCSBKAlertRequest: { OnEmergencyChanged(sourceeID, GetSlotNumber(slotNumber)); break; } case (byte)CallType.AllCall: case (byte)CallType.GroupVoiceCall: case (byte)CallType.PrivateCallRequest: { OnCSBKCallReceived(sourceeID, targettID, (CallType)callType, (SlotNumber)slotNumber); break; } } } break; } #endregion #region WL_VC_VOICE_START /* * This message is sent by the repeater peer to indicate the beginning of a voice stream. */ case (byte)Operations.WL_VC_VOICE_START: { if (wirelineProtocolVersion >= 1) { // Slot number - Specifies the channel/slot reporting the channel status.[Uint8] byte slotNumber = toDecode[crtPosition++]; // call ID - The ID of the call byte[] callID = new byte[4]; callID[3] = toDecode[crtPosition++]; callID[2] = toDecode[crtPosition++]; callID[1] = toDecode[crtPosition++]; callID[0] = toDecode[crtPosition++]; Int64 calllID = BitConverter.ToUInt32(callID, 0); // The type of the call [Uint8] byte callType = toDecode[crtPosition++]; CallType callTypee = GetCallType(callType); // source ID - The ID of the subscriber that initiated the call byte[] sourceID = new byte[4]; sourceID[3] = toDecode[crtPosition++]; sourceID[2] = toDecode[crtPosition++]; sourceID[1] = toDecode[crtPosition++]; sourceID[0] = toDecode[crtPosition++]; Int64 sourceeID = BitConverter.ToUInt32(sourceID, 0); // target ID - The ID of the subscriber ot talk-group to which the call is targeted byte[] targetID = new byte[4]; targetID[3] = toDecode[crtPosition++]; targetID[2] = toDecode[crtPosition++]; targetID[1] = toDecode[crtPosition++]; targetID[0] = toDecode[crtPosition++]; Int64 targettID = BitConverter.ToUInt32(targetID, 0); // callAttributes [Uint8] - Call General attributes byte callAttributes = toDecode[crtPosition++]; // reserved [Uint8] byte reserved = toDecode[crtPosition++]; // MFID [Uint8] - The DMR Feature Set ID / Manufacturer’s ID byte mfid = toDecode[crtPosition++]; // service option [Uint8] - DMR LC Service Option byte serviceOption = toDecode[crtPosition++]; // Current Wireline Protocol Version [Uint8] byte currentWirelineProtocolVersion = toDecode[crtPosition++]; // Oldest Wireline Protocol Version [Uint8] byte oldestWirelineProtocolVersion = toDecode[crtPosition++]; Utils.WriteLine(String.Format("\t{0}", GetSlotNumberName(slotNumber, le.systemType)), ConsoleType.WIRELINE); Utils.WriteLine(String.Format("\tCall Type: {0}", GetCallType(callType)), ConsoleType.WIRELINE); Utils.WriteLine(String.Format("\tSource ID: {0}", sourceeID), ConsoleType.WIRELINE); Utils.WriteLine(String.Format("\tTarget ID: {0}", targettID), ConsoleType.WIRELINE); if (callTypee == CallType.AllCall || callTypee == CallType.GroupVoiceCall || callTypee == CallType.PrivateVoiceCall || callTypee == CallType.EmergencyVoiceCall) OnCallStatusReceived(sourceeID, targettID, ConvertCallType4MessageBus(callTypee), ConvertCallSessionStatus4MessageBus(CallSessionStatus.CallInProgress), GetSlotNumber(slotNumber)); } break; } #endregion #region WL_VC_VOICE_END_BURST /* * This message is sent by the repeater peer to indicate the beginning of a voice stream. */ case (byte)Operations.WL_VC_VOICE_END_BURST: { if (wirelineProtocolVersion >= 1) { // Slot number - Specifies the channel/slot reporting the channel status.[Uint8] byte slotNumber = toDecode[crtPosition++]; // call ID - The ID of the call byte[] callID = new byte[4]; callID[3] = toDecode[crtPosition++]; callID[2] = toDecode[crtPosition++]; callID[1] = toDecode[crtPosition++]; callID[0] = toDecode[crtPosition++]; Int64 calllID = BitConverter.ToUInt32(callID, 0); // The type of the call [Uint8] byte callType = toDecode[crtPosition++]; CallType callTypee = GetCallType(callType); // source ID - The ID of the subscriber that initiated the call byte[] sourceID = new byte[4]; sourceID[3] = toDecode[crtPosition++]; sourceID[2] = toDecode[crtPosition++]; sourceID[1] = toDecode[crtPosition++]; sourceID[0] = toDecode[crtPosition++]; Int64 sourceeID = BitConverter.ToUInt32(sourceID, 0); // target ID - The ID of the subscriber ot talk-group to which the call is targeted byte[] targetID = new byte[4]; targetID[3] = toDecode[crtPosition++]; targetID[2] = toDecode[crtPosition++]; targetID[1] = toDecode[crtPosition++]; targetID[0] = toDecode[crtPosition++]; Int64 targettID = BitConverter.ToUInt32(targetID, 0); // callAttributes [Uint8] - Call General attributes byte[] rtpInformationField = new byte[12]; rtpInformationField[11] = toDecode[crtPosition++]; rtpInformationField[10] = toDecode[crtPosition++]; rtpInformationField[9] = toDecode[crtPosition++]; rtpInformationField[8] = toDecode[crtPosition++]; rtpInformationField[7] = toDecode[crtPosition++]; rtpInformationField[6] = toDecode[crtPosition++]; rtpInformationField[5] = toDecode[crtPosition++]; rtpInformationField[4] = toDecode[crtPosition++]; rtpInformationField[3] = toDecode[crtPosition++]; rtpInformationField[2] = toDecode[crtPosition++]; rtpInformationField[1] = toDecode[crtPosition++]; rtpInformationField[0] = toDecode[crtPosition++]; // burst type [Uint8] - DMR voice burst type Must be Voice Terminator byte burstType = toDecode[crtPosition++]; // reserved [Uint8] byte reserved = toDecode[crtPosition++]; // MFID [Uint8] - The DMR Feature Set ID / Manufacturer’s ID byte mfid = toDecode[crtPosition++]; // service option [Uint8] - DMR LC Service Option byte serviceOption = toDecode[crtPosition++]; // Current Wireline Protocol Version [Uint8] byte currentWirelineProtocolVersion = toDecode[crtPosition++]; // Oldest Wireline Protocol Version [Uint8] byte oldestWirelineProtocolVersion = toDecode[crtPosition++]; //Utils.WriteLine(ConsoleType.WIRELINE, String.Format("\t{0}", GetSlotNumberName(slotNumber, le.systemType))); //Utils.WriteLine(ConsoleType.WIRELINE, String.Format("\tCall Type: {0}", GetCallType(callType))); //Utils.WriteLine(ConsoleType.WIRELINE, String.Format("\tSource ID: {0}", sourceeID)); //Utils.WriteLine(ConsoleType.WIRELINE, String.Format("\tTarget ID: {0}", targettID)); Utils.WriteLine(String.Format("\tBurst Type: {0}", GetBurstType(burstType)), ConsoleType.WIRELINE); /* if (callTypee == CallType.AllCall || callTypee == CallType.GroupVoiceCall || callTypee == CallType.PrivateCallRequest) OnCallStatusReceived(sourceeID, targettID, ConvertCallType4MessageBus(callTypee), ConvertCallSessionStatus4MessageBus(CallSessionStatus.CallInProgress), GetSlotNumber(slotNumber)); */ } break; } #endregion #region WL_VC_CALL_SESSION_STATUS /* * This message is sent from repeater peer to the third party application to inform the call * session status. Only the repeater peer, which receives the call over the air from the * radio or receives the call from the third party application over the network interface, * sends out the WL_VC_CALL_SESSION_STATUS message */ case (byte)Operations.WL_VC_CALL_SESSION_STATUS: { if (wirelineProtocolVersion >= 1) { // Slot number - Specifies the channel/slot reporting the channel status.[Uint8] byte slotNumber = toDecode[crtPosition++]; // call ID - The ID of the call byte[] callID = new byte[4]; callID[3] = toDecode[crtPosition++]; callID[2] = toDecode[crtPosition++]; callID[1] = toDecode[crtPosition++]; callID[0] = toDecode[crtPosition++]; Int64 calllID = BitConverter.ToUInt32(callID, 0); // The type of the call [Uint8] byte callType = toDecode[crtPosition++]; // source ID - The ID of the subscriber that initiated the call byte[] sourceID = new byte[4]; sourceID[3] = toDecode[crtPosition++]; sourceID[2] = toDecode[crtPosition++]; sourceID[1] = toDecode[crtPosition++]; sourceID[0] = toDecode[crtPosition++]; Int64 sourceeID = BitConverter.ToUInt32(sourceID, 0); // target ID - The ID of the subscriber ot talk-group to which the call is targeted byte[] targetID = new byte[4]; targetID[3] = toDecode[crtPosition++]; targetID[2] = toDecode[crtPosition++]; targetID[1] = toDecode[crtPosition++]; targetID[0] = toDecode[crtPosition++]; Int64 targettID = BitConverter.ToUInt32(targetID, 0); // reserved [Uint32] byte reserved = toDecode[crtPosition++]; reserved = toDecode[crtPosition++]; reserved = toDecode[crtPosition++]; reserved = toDecode[crtPosition++]; // MFIcallSessionStatusD - Call Session Stats [Uint8] byte callSessionStatus = toDecode[crtPosition++]; // Current Wireline Protocol Version [Uint8] byte currentWirelineProtocolVersion = toDecode[crtPosition++]; // Oldest Wireline Protocol Version [Uint8] byte oldestWirelineProtocolVersion = toDecode[crtPosition++]; Utils.WriteLine(String.Format("\t{0}", GetSlotNumberName(slotNumber, le.systemType))); Utils.WriteLine(String.Format("\tCall Type: {0}", GetCallType(callType))); Utils.WriteLine(String.Format("\tSource ID: {0}", sourceeID)); Utils.WriteLine(String.Format("\tTarget ID: {0}", targettID)); Utils.WriteLine(String.Format("\tCall Session Status: {0}", GetCallSessionStatus(callSessionStatus))); CallSessionStatus callStatus = (callSessionStatus == (byte)CallSessionStatus.CallEnd ? CallSessionStatus.CallEnd : CallSessionStatus.CallHang); // broadcast the call status only if in hangtime or ended if(callStatus == CallSessionStatus.CallEnd || callStatus == CallSessionStatus.CallHang) { if (callType == (byte)CallType.AllCall || callType == (byte)CallType.GroupVoiceCall || callType == (byte)CallType.PrivateVoiceCall || callType == (byte)CallType.EmergencyVoiceCall) OnCallStatusReceived(sourceeID, targettID, ConvertCallType4MessageBus((CallType)callType), ConvertCallSessionStatus4MessageBus(callStatus), GetSlotNumber(slotNumber, le.systemType)); /* if (callType == (byte)CallType.GroupVoiceCall) OnCallStatusReceived(sourceeID, targettID, ConvertCallType4MessageBus(CallType.GroupVoiceCall), ConvertCallSessionStatus4MessageBus(callStatus), GetSlotNumber(slotNumber, le.systemType)); else if (callType == (byte)CallType.PrivateVoiceCall) OnCallStatusReceived(sourceeID, targettID, ConvertCallType4MessageBus(CallType.PrivateVoiceCall), ConvertCallSessionStatus4MessageBus(callStatus), GetSlotNumber(slotNumber, le.systemType)); else if (callType == (byte)CallType.AllCall) OnCallStatusReceived(sourceeID, targettID, ConvertCallType4MessageBus(CallType.AllCall), ConvertCallSessionStatus4MessageBus(callStatus), GetSlotNumber(slotNumber, le.systemType)); else if (callType == (byte)CallType.EmergencyVoiceCall) OnCallStatusReceived(sourceeID, targettID, ConvertCallType4MessageBus(CallType.EmergencyVoiceCall), ConvertCallSessionStatus4MessageBus(callStatus), GetSlotNumber(slotNumber, le.systemType)); */ } // fire event for call session status received OnCallSesionStatusReceived(sourceeID, targettID, (CallType)callType, (CallSessionStatus)callSessionStatus, (SlotNumber) slotNumber); } break; } #endregion #region WL_VC_VOICE_BURST /* * This message is used by the Repeater Peer to send Voice Burst to the third party * application. */ case (byte)Operations.WL_VC_VOICE_BURST: { if (wirelineProtocolVersion >= 1) { // Slot number - Specifies the channel/slot reporting the channel status.[Uint8] byte slotNumber = toDecode[crtPosition++]; // call ID - The ID of the call byte[] callID = new byte[4]; callID[3] = toDecode[crtPosition++]; callID[2] = toDecode[crtPosition++]; callID[1] = toDecode[crtPosition++]; callID[0] = toDecode[crtPosition++]; Int64 calllID = BitConverter.ToUInt32(callID, 0); // The type of the call [Uint8] byte callType = toDecode[crtPosition++]; CallType callTypee = GetCallType(callType); // source ID - The ID of the subscriber that initiated the call byte[] sourceID = new byte[4]; sourceID[3] = toDecode[crtPosition++]; sourceID[2] = toDecode[crtPosition++]; sourceID[1] = toDecode[crtPosition++]; sourceID[0] = toDecode[crtPosition++]; Int64 sourceeID = BitConverter.ToUInt32(sourceID, 0); // target ID - The ID of the subscriber ot talk-group to which the call is targeted byte[] targetID = new byte[4]; targetID[3] = toDecode[crtPosition++]; targetID[2] = toDecode[crtPosition++]; targetID[1] = toDecode[crtPosition++]; targetID[0] = toDecode[crtPosition++]; Int64 targettID = BitConverter.ToUInt32(targetID, 0); // callAttributes [Uint8] - Call General attributes byte callAttributes = toDecode[crtPosition++]; // reserved [Uint8] byte reserved = toDecode[crtPosition++]; // rptInformationField [Uint8 * 12] - RPT Header Information byte[] rtpInformationField = new byte[12]; rtpInformationField[11] = toDecode[crtPosition++]; rtpInformationField[10] = toDecode[crtPosition++]; rtpInformationField[9] = toDecode[crtPosition++]; rtpInformationField[8] = toDecode[crtPosition++]; rtpInformationField[7] = toDecode[crtPosition++]; rtpInformationField[6] = toDecode[crtPosition++]; rtpInformationField[5] = toDecode[crtPosition++]; rtpInformationField[4] = toDecode[crtPosition++]; rtpInformationField[3] = toDecode[crtPosition++]; rtpInformationField[2] = toDecode[crtPosition++]; rtpInformationField[1] = toDecode[crtPosition++]; rtpInformationField[0] = toDecode[crtPosition++]; // burst type [Uint8] - DMR voice burst type Must be Voice Terminator byte burstType = toDecode[crtPosition++]; // reserved [Uint8] byte reserved3 = toDecode[crtPosition++]; // MFID [Uint8] - The DMR Feature Set ID / Manufacturer’s ID byte mfid = toDecode[crtPosition++]; // service option [Uint8] - DMR LC Service Option byte serviceOption = toDecode[crtPosition++]; // algorithmID [Uint8] - Algorithm ID byte algorithmID = toDecode[crtPosition++]; // keyID [Uint8] - ID of the Privacy Key byte keyID = toDecode[crtPosition++]; // iv [Uint8] - Initialization Vector byte[] iv = new byte[4]; iv[3] = toDecode[crtPosition++]; iv[2] = toDecode[crtPosition++]; iv[1] = toDecode[crtPosition++]; iv[0] = toDecode[crtPosition++]; // ambe [Uint8 * 20] - The AMBE voice encoded frames byte[] ambe = new byte[20]; ambe[0] = toDecode[crtPosition++]; ambe[1] = toDecode[crtPosition++]; ambe[2] = toDecode[crtPosition++]; ambe[3] = toDecode[crtPosition++]; ambe[4] = toDecode[crtPosition++]; ambe[5] = toDecode[crtPosition++]; ambe[6] = toDecode[crtPosition++]; ambe[7] = toDecode[crtPosition++]; ambe[8] = toDecode[crtPosition++]; ambe[9] = toDecode[crtPosition++]; ambe[10] = toDecode[crtPosition++]; ambe[11] = toDecode[crtPosition++]; ambe[12] = toDecode[crtPosition++]; ambe[13] = toDecode[crtPosition++]; ambe[14] = toDecode[crtPosition++]; ambe[15] = toDecode[crtPosition++]; ambe[16] = toDecode[crtPosition++]; ambe[17] = toDecode[crtPosition++]; ambe[18] = toDecode[crtPosition++]; ambe[19] = toDecode[crtPosition++]; // add ambe burst to file //DecodeAMBE2(ambe, GetBurstType(burstType), (SlotNumber)slotNumber); // rawRssiValue [Uint16] - Only for WL outbound: The raw RSSI value for each voice burst byte[] rawRssiValue = new byte[2]; rawRssiValue[1] = toDecode[crtPosition++]; rawRssiValue[0] = toDecode[crtPosition++]; // Current Wireline Protocol Version [Uint8] byte currentWirelineProtocolVersion = toDecode[crtPosition++]; // Oldest Wireline Protocol Version [Uint8] byte oldestWirelineProtocolVersion = toDecode[crtPosition++]; Utils.WriteLine(String.Format("\t{0}", GetSlotNumberName(slotNumber, le.systemType)), ConsoleType.WIRELINE); //Utils.WriteLine(ConsoleType.WIRELINE, String.Format("\tCall Type: {0}", GetCallType(callType))); //Utils.WriteLine(ConsoleType.WIRELINE, String.Format("\tSource ID: {0}", sourceeID)); //Utils.WriteLine(ConsoleType.WIRELINE, String.Format("\tTarget ID: {0}", targettID)); //Utils.WriteLine(ConsoleType.WIRELINE, String.Format("\tCurrent Version: {0}", currentWirelineProtocolVersion)); //Utils.WriteLine(ConsoleType.WIRELINE, String.Format("\tOldest Version: {0}", oldestWirelineProtocolVersion)); Utils.WriteLine(String.Format("\tBurst Type: {0}", GetBurstType(burstType)), ConsoleType.WIRELINE); // verify if a voice burst was skyped and send an empty one if yes int skipped = 0; // set the previous bursty type for this slot number int prevWorkingBurstType = (slotNumber == (byte)SlotNumber.Slot1 ? previousBrustTypeSlot1 : previousBrustTypeSlot2); if (burstType < prevWorkingBurstType) skipped = Math.Abs(burstType + 6 - prevWorkingBurstType) % 6 - 1; else skipped = Math.Abs(burstType - prevWorkingBurstType % 6) - 1; if (skipped > 0) { for (int i = 0; i < skipped; i++) { // frame 1 OnAudioFrameReceived(new byte[7], (Wireline.SlotNumber)slotNumber); // frame 2 OnAudioFrameReceived(new byte[7], (Wireline.SlotNumber)slotNumber); // frame 3 OnAudioFrameReceived(new byte[7], (Wireline.SlotNumber)slotNumber); } } // trigger event for voice frame received List voiceFrames = AMBE.Rate34PrepareForEncoding(ambe); foreach (byte[] by in voiceFrames) OnAudioFrameReceived(by, (Wireline.SlotNumber)slotNumber); // save previous burst type for specified slot if (slotNumber == (byte)SlotNumber.Slot1) previousBrustTypeSlot1 = burstType; else previousBrustTypeSlot2 = burstType; } break; } #endregion #region WL_VC_PRIVACY_BURST /* * This message is use by the Repeater Peer to send Privacy Voice Burst to the third party * application. */ case (byte)Operations.WL_VC_PRIVACY_BURST: { if (wirelineProtocolVersion >= 1) { // Slot number - Specifies the channel/slot reporting the channel status.[Uint8] byte slotNumber = toDecode[crtPosition++]; // call ID - The ID of the call byte[] callID = new byte[4]; callID[3] = toDecode[crtPosition++]; callID[2] = toDecode[crtPosition++]; callID[1] = toDecode[crtPosition++]; callID[0] = toDecode[crtPosition++]; Int64 calllID = BitConverter.ToUInt32(callID, 0); // The type of the call [Uint8] byte callType = toDecode[crtPosition++]; CallType callTypee = GetCallType(callType); // source ID - The ID of the subscriber that initiated the call byte[] sourceID = new byte[4]; sourceID[3] = toDecode[crtPosition++]; sourceID[2] = toDecode[crtPosition++]; sourceID[1] = toDecode[crtPosition++]; sourceID[0] = toDecode[crtPosition++]; Int64 sourceeID = BitConverter.ToUInt32(sourceID, 0); // target ID - The ID of the subscriber ot talk-group to which the call is targeted byte[] targetID = new byte[4]; targetID[3] = toDecode[crtPosition++]; targetID[2] = toDecode[crtPosition++]; targetID[1] = toDecode[crtPosition++]; targetID[0] = toDecode[crtPosition++]; Int64 targettID = BitConverter.ToUInt32(targetID, 0); // rptInformationField [Uint8 * 12] - RPT Header Information byte[] rtpInformationField = new byte[12]; rtpInformationField[11] = toDecode[crtPosition++]; rtpInformationField[10] = toDecode[crtPosition++]; rtpInformationField[9] = toDecode[crtPosition++]; rtpInformationField[8] = toDecode[crtPosition++]; rtpInformationField[7] = toDecode[crtPosition++]; rtpInformationField[6] = toDecode[crtPosition++]; rtpInformationField[5] = toDecode[crtPosition++]; rtpInformationField[4] = toDecode[crtPosition++]; rtpInformationField[3] = toDecode[crtPosition++]; rtpInformationField[2] = toDecode[crtPosition++]; rtpInformationField[1] = toDecode[crtPosition++]; rtpInformationField[0] = toDecode[crtPosition++]; // burst type [Uint8] - DMR Voice burst Type Must be Privacy Header byte burstType = toDecode[crtPosition++]; // reserved [Uint8] byte reserved = toDecode[crtPosition++]; // MFID [Uint8] - The DMR Feature Set ID / Manufacturer’s ID byte mfid = toDecode[crtPosition++]; // service option [Uint8] - DMR LC Service Option byte serviceOption = toDecode[crtPosition++]; // algorithmID [Uint8] - Algorithm ID byte algorithmID = toDecode[crtPosition++]; // keyID [Uint8] - ID of the Privacy Key byte keyID = toDecode[crtPosition++]; // iv [Uint8] - Initialization Vector byte iv = toDecode[crtPosition++]; // Current Wireline Protocol Version [Uint8] byte currentWirelineProtocolVersion = toDecode[crtPosition++]; // Oldest Wireline Protocol Version [Uint8] byte oldestWirelineProtocolVersion = toDecode[crtPosition++]; Utils.WriteLine(String.Format("\t{0}", GetSlotNumberName(slotNumber, le.systemType)), ConsoleType.WIRELINE); Utils.WriteLine(String.Format("\tCall Type: {0}", GetCallType(callType)), ConsoleType.WIRELINE); Utils.WriteLine(String.Format("\tSource ID: {0}", sourceeID), ConsoleType.WIRELINE); Utils.WriteLine(String.Format("\tTarget ID: {0}", targettID), ConsoleType.WIRELINE); Utils.WriteLine(String.Format("\tBurst Type: {0}", GetBurstType(burstType)), ConsoleType.WIRELINE); if (callTypee == CallType.AllCall || callTypee == CallType.GroupVoiceCall || callTypee == CallType.PrivateVoiceCall || callTypee == CallType.EmergencyVoiceCall) OnCallStatusReceived(sourceeID, targettID, ConvertCallType4MessageBus(callTypee), ConvertCallSessionStatus4MessageBus(CallSessionStatus.CallInProgress), GetSlotNumber(slotNumber)); } break; } #endregion default: { Utils.WriteLine(String.Format("Unknown Wireline Message: 0x{0:X} [{1}:{2}]", wirelineOpCode, peerIP, peerPort), ConsoleType.WIRELINE); Utils.WriteLine(Utils.PrintBytesArray(toDecode), ConsoleType.WIRELINE); break; } } } #region WIRELINE SENDING /* Send a Wireline message to the desired peer */ public void SendWirelineMessage(byte[] message, String peerIP, Int32 peerUDPPort) { if (message.Length < 1) return; try { le.udpClient.Client.SendTo(message, new IPEndPoint(IPAddress.Parse(peerIP), peerUDPPort)); Utils.WriteLine(String.Format("««« {0,-34} [{1}]", GetMessageType(message[5]), peerIP), ConsoleType.WIRELINE); // + ":" + peerUDPPort); //Utils.WriteLine(ConsoleType.WIRELINE, Utils.printBytesArray(message)); } catch (Exception ex) { Utils.WriteLine("Wireline Thread Send exception: " + ex.ToString(), ConsoleType.WIRELINE); } } /* Send a Wireline message to the current Master Peer */ public void SendWirelineMessage(byte[] message) { SendWirelineMessage(message, le.masterPeer.ipAddress, le.masterPeer.port); } /* Get a string that represents the Message Type based on the opCode */ private string GetMessageType(byte wirelineOpCode) { switch (wirelineOpCode) { case (byte)Operations.WL_REGISTRATION_REQUEST: return Operations.WL_REGISTRATION_REQUEST.ToString(); case (byte)Operations.WL_REGISTRATION_STATUS: return Operations.WL_REGISTRATION_STATUS.ToString(); case (byte)Operations.WL_REGISTRATION_GENERAL_OPS: return Operations.WL_REGISTRATION_GENERAL_OPS.ToString(); case (byte)Operations.WL_PROTOCOL_VERSION_QUERY: return Operations.WL_PROTOCOL_VERSION_QUERY.ToString(); case (byte)Operations.WL_PROTOCOL_VERSION_QUERY_RESPONSE: return Operations.WL_PROTOCOL_VERSION_QUERY_RESPONSE.ToString(); case (byte)Operations.WL_CHNL_STATUS: return Operations.WL_CHNL_STATUS.ToString(); case (byte)Operations.WL_CHNL_STATUS_QUERY: return Operations.WL_CHNL_STATUS_QUERY.ToString(); case (byte)Operations.WL_VC_CHNL_CTRL_REQUEST: return Operations.WL_VC_CHNL_CTRL_REQUEST.ToString(); case (byte)Operations.WL_VC_CHNL_CTRL_STATUS: return Operations.WL_VC_CHNL_CTRL_STATUS.ToString(); case (byte)Operations.WL_VC_CSBK_CALL: return Operations.WL_VC_CSBK_CALL.ToString(); case (byte)Operations.WL_VC_VOICE_START: return Operations.WL_VC_VOICE_START.ToString(); case (byte)Operations.WL_VC_VOICE_END_BURST: return Operations.WL_VC_VOICE_END_BURST.ToString(); case (byte)Operations.WL_VC_CALL_SESSION_STATUS: return Operations.WL_VC_CALL_SESSION_STATUS.ToString(); case (byte)Operations.WL_VC_VOICE_BURST: return Operations.WL_VC_VOICE_BURST.ToString(); case (byte)Operations.WL_VC_PRIVACY_BURST: return Operations.WL_VC_PRIVACY_BURST.ToString(); default: return "Unknown Wireline Message"; } } #endregion #region WIRELINE CALL ENUMS public enum AccessCriteria { PoliteAccess = 0x01 }; public enum CallAttributes { ForwardCSBK = 0x20, DoNotForwardCSBK = 0x00, SmartPTT = 0x80, SmartAfterPreamble = 0xA0 }; public enum PreambleDuration { MinDuration = 0x00, MaxDuration = 0x80, BurstDurationMS = 0x3C }; public enum CallSessionStatus { CallHang = 0x0A, CallEnd = 0x0B, CallInProgress = 0x09, RESERVED = 0x00 }; public enum CallType { PreamblePrivateCSBKCall = 0x32, PreambleGroupCSBKCall = 0x33, PreambleEmergencyCall = 0x34, EmergencyCSBKAlertRequest = 0x40, EmergencyCSBKAlertResponse = 0x41, EmergencyVoiceCall = 0x42, PrivateCallRequest = 0x43, PrivateCallResponse = 0x44, CallAlertRequest = 0x45, CallAlertResponse = 0x46, RadioCheckRequest = 0x47, RadioCheckResponse = 0x48, RadioInhibitRequest = 0x49, RadioInhibitResponse = 0x4A, RadioUninhibitRequest = 0x4B, RadioUninhibitResponse = 0x4C, RadioRemoteMonitorRequest = 0x4D, RadioRemoteMonitorResponse = 0x4E, GroupVoiceCall = 0x4F, PrivateVoiceCall = 0x50, AllCall = 0x53, Reserved = 0x54, Other = 0x55, IPConsoleRadioUninhibitRequest = 0x56, IPConsoleRadioInhibitRequest = 0x57, IPConsoleRadioUninhibitResponse = 0x58, IPConsoleRadioInhibitResponse = 0x59, GroupPhoneCall = 0x5A, PrivatePhoneCall = 0x5B, PhoneAllCall = 0x5C, CallAlertNackResponse = 0x83, RadioMonitorNackResponse = 0x84, RadioInhibitUninhibitNackResponse = 0x85 }; public enum ChannelStatus { Active = 0x01, Idle = 0x02, SlotBlocked = 0x0A, SlotUnblocked = 0x0B, BusyRestChannel = 0x10, RestChannelIdleAvailable = 0x11, // works for idle and available LocalGroupCallsNotAllowed = 0x12, LocalGroupCallsAllowed = 0x13, RestChannelBlocked = 0x14 }; public enum ChannelControlStatus { Received = 0x01, Transmiting = 0x02, TransmissionSuccessful = 0x03, SmartInProgress = 0x04, Declined = 0x05 }; public enum SlotNumber { Slot1 = 0x01, Slot2 = 0x02, BothSlots = 0x03, RestChannel = 0x00, // only in CPC SiteIPAddress = 0x00, // only in IPSC Reserved = 0xFF }; public enum RegistrationStatusCode { CFSNotEnabled = 0x01, RegistrationEntriesExceded = 0x02, IncompatibleVersion = 0x03 }; public enum RegistrationStatus { Successful = 0x00, Unsuccessful = 0x01 }; public enum RegistrationOperationCode { QueryRegStatus = 0x01, DeRegister = 0x02 }; public enum WirelineChannelStatus { RegisteredWLStatus = 0x80, NotRegisteredWLStatus = 0x00 }; public enum AddressType { IndividualCall = 0x01, GroupCall = 0x02, AllIndividualCall = 0x03, AllTalkgroupCall = 0x04, AllWideTakgroupsCall = 0x05, AllLocalTalkgroupsCall = 0x06 }; public enum CSBKAttributes { // bit 0 VoiceCallNotInterruptible = 0x00, VoiceCallInterruptible = 0x01, // bit 1 ClearCall = 0x00, PrivacyCall = 0x02, // bit 2-3 ClearCall23 = 0x00, BasicPrivacy = 0x01, EnhancedPrivacy = 0x02, // bit 6 CSBKService = 0x00, CSBKMonitorOnly = 0x40, // bit 7 CSBKNotRegisteredService = 0x00, CSBKRegisteredService = 0x80 } public enum VoiceAttributes { NormalService = 0x00, VoiceMonitorOnly = 0x01, NotRegisteredVoiceService = 0x00, RegisterVoiceService = 0x01 }; public enum BurstType { VoiceBurstA = 0x01, VoiceBurstB = 0x02, VoiceBurstC = 0x03, VoiceBurstD = 0x04, VoiceBurstE = 0x05, VoiceBurstF = 0x06, VoiceTerminator = 0x07, PrivacyHeader = 0x08, Unknown = 0x00 } public enum ServiceOption { // bit 3 - Only for group call NonBroadcastService = 0x00, BroadcastService = 0x04, // bit 7 NonEmergencyService = 0x00, BroadcastService7 = 0x80 } public enum PrivacyType { Clear = 0x00, Basic = 0x01, Enhanced = 0x02 }; public enum ManufacturerID { StandardFeature = 0x00, MotorolaSolutionProprietaryFeature = 0x10, } private string GetRegistrationStatus(int registrationStatus) { switch(registrationStatus) { case (int)RegistrationStatus.Successful: return "Successful"; case (int)RegistrationStatus.Unsuccessful: return "Unsuccessful"; default: return "RESERVED"; } } private string GetRegistrationStatusCode(int registrationStatusCode) { switch(registrationStatusCode) { case (int)RegistrationStatusCode.CFSNotEnabled: return "CFS Not Enabled"; case (int)RegistrationStatusCode.IncompatibleVersion: return "Incompatible Version"; case (int)RegistrationStatusCode.RegistrationEntriesExceded: return "Registration Entries Exceded"; case (int)0x00: return "Registered"; default: return "RESERVED"; } } private int ConvertCallType4MessageBus(CallType type) { switch (type) { case CallType.GroupVoiceCall: return (int)SafeMobileLib.CallType.GROUPCALL; case CallType.PrivateVoiceCall: return (int)SafeMobileLib.CallType.PRIVATECALL; case CallType.AllCall: return (int)SafeMobileLib.CallType.ALLCALL; case CallType.EmergencyVoiceCall: return (int)SafeMobileLib.CallType.EMERGENCYCALL; default: return 0; } } private string GetCallType(int type) { switch (type) { case (int)CallType.CallAlertNackResponse: return "CallAlertNackResponse"; case (int)CallType.CallAlertRequest: return "CallAlertRequest"; case (int)CallType.CallAlertResponse: return "CallAlertResponse"; case (int)CallType.EmergencyCSBKAlertRequest: return "EmergencyCSBKAlertRequest"; case (int)CallType.EmergencyCSBKAlertResponse: return "EmergencyCSBKAlertResponse"; case (int)CallType.EmergencyVoiceCall: return "EmergencyVoiceCall"; case (int)CallType.GroupVoiceCall: return "GroupVoiceCall"; case (int)CallType.PrivateVoiceCall: return "PrivateVoiceCall"; case (int)CallType.IPConsoleRadioInhibitRequest: return "IPConsoleRadioInhibitRequest"; case (int)CallType.IPConsoleRadioInhibitResponse: return "IPConsoleRadioInhibitResponse"; case (int)CallType.IPConsoleRadioUninhibitRequest: return "IPConsoleRadioUninhibitRequest"; case (int)CallType.IPConsoleRadioUninhibitResponse: return "IPConsoleRadioUninhibitResponse"; case (int)CallType.AllCall: return "AllCall"; case (int)CallType.Other: return "Other"; case (int)CallType.GroupPhoneCall: return "GroupPhoneCall"; case (int)CallType.PrivatePhoneCall: return "PrivatePhoneCall"; case (int)CallType.PreambleEmergencyCall: return "PreambleEmergencyCall"; case (int)CallType.PreambleGroupCSBKCall: return "PreambleGroupCSBKCall"; case (int)CallType.PreamblePrivateCSBKCall: return "PreamblePrivateCSBKCall"; case (int)CallType.RadioCheckRequest: return "RadioCheckRequest"; case (int)CallType.RadioCheckResponse: return "RadioCheckResponse"; case (int)CallType.RadioInhibitRequest: return "RadioInhibitRequest"; case (int)CallType.RadioInhibitResponse: return "RadioInhibitResponse"; case (int)CallType.RadioInhibitUninhibitNackResponse: return "RadioInhibitUninhibitNackResponse"; case (int)CallType.RadioMonitorNackResponse: return "RadioMonitorNackResponse"; case (int)CallType.RadioRemoteMonitorRequest: return "RadioRemoteMonitorRequest"; case (int)CallType.RadioRemoteMonitorResponse: return "RadioRemoteMonitorResponse"; case (int)CallType.RadioUninhibitRequest: return "RadioUninhibitRequest"; case (int)CallType.RadioUninhibitResponse: return "RadioUninhibitResponse"; default: { if ((type >= 0x00 && type <= 0x31) || (type >= 0x35 && type <= 0x3F) || (type >= 0x4F && type <= 0x54) || (type >= 0x5A && type <= 0x5C) || (type >= 0x86 && type <= 0xFF)) return "RESERVED"; else return "Unknown call type"; } } } private CallType GetCallType(byte type) { switch (type) { case (byte)CallType.AllCall: return CallType.AllCall; case (byte)CallType.GroupVoiceCall: return CallType.GroupVoiceCall; case (byte)CallType.PrivateVoiceCall: return CallType.PrivateVoiceCall; case (byte)CallType.PrivateCallRequest: return CallType.PrivateCallRequest; case (byte)CallType.PrivateCallResponse: return CallType.PrivateCallResponse; case (byte)CallType.GroupPhoneCall: return CallType.GroupPhoneCall; case (byte)CallType.PrivatePhoneCall: return CallType.PrivatePhoneCall; case (byte)CallType.PhoneAllCall: return CallType.PhoneAllCall; case (byte)CallType.CallAlertNackResponse: return CallType.CallAlertNackResponse; case (byte)CallType.CallAlertRequest: return CallType.CallAlertRequest; case (byte)CallType.CallAlertResponse: return CallType.CallAlertResponse; case (byte)CallType.EmergencyCSBKAlertRequest: return CallType.EmergencyCSBKAlertRequest; case (byte)CallType.EmergencyCSBKAlertResponse: return CallType.EmergencyCSBKAlertResponse; case (byte)CallType.IPConsoleRadioInhibitRequest: return CallType.IPConsoleRadioInhibitRequest; case (byte)CallType.IPConsoleRadioInhibitResponse: return CallType.IPConsoleRadioInhibitResponse; case (byte)CallType.IPConsoleRadioUninhibitRequest: return CallType.IPConsoleRadioUninhibitRequest; case (byte)CallType.IPConsoleRadioUninhibitResponse: return CallType.IPConsoleRadioUninhibitResponse; case (byte)CallType.Other: return CallType.Other; case (byte)CallType.PreambleEmergencyCall: return CallType.PreambleEmergencyCall; case (byte)CallType.PreambleGroupCSBKCall: return CallType.PreambleGroupCSBKCall; case (byte)CallType.PreamblePrivateCSBKCall: return CallType.PreamblePrivateCSBKCall; case (byte)CallType.RadioCheckRequest: return CallType.RadioCheckRequest; case (byte)CallType.RadioCheckResponse: return CallType.RadioCheckResponse; case (byte)CallType.RadioInhibitRequest: return CallType.RadioInhibitRequest; case (byte)CallType.RadioInhibitResponse: return CallType.RadioInhibitResponse; case (byte)CallType.RadioInhibitUninhibitNackResponse: return CallType.RadioInhibitUninhibitNackResponse; case (byte)CallType.RadioMonitorNackResponse: return CallType.RadioMonitorNackResponse; case (byte)CallType.RadioRemoteMonitorRequest: return CallType.RadioRemoteMonitorRequest; case (byte)CallType.RadioRemoteMonitorResponse: return CallType.RadioRemoteMonitorResponse; case (byte)CallType.RadioUninhibitRequest: return CallType.RadioUninhibitRequest; case (byte)CallType.RadioUninhibitResponse: return CallType.RadioUninhibitResponse; case (byte)CallType.EmergencyVoiceCall: return CallType.EmergencyVoiceCall; default: { if ((type >= 0x00 && type <= 0x31) || (type >= 0x35 && type <= 0x3F) || (type >= 0x4F && type <= 0x54) || (type >= 0x5A && type <= 0x5C) || (type >= 0x86 && type <= 0xFF)) return CallType.Reserved; else return CallType.Reserved; } } } private BurstType GetBurstType(byte type) { switch (type) { case (byte)BurstType.PrivacyHeader: return BurstType.PrivacyHeader; case (byte)BurstType.VoiceBurstA: return BurstType.VoiceBurstA; case (byte)BurstType.VoiceBurstB: return BurstType.VoiceBurstB; case (byte)BurstType.VoiceBurstC: return BurstType.VoiceBurstC; case (byte)BurstType.VoiceBurstD: return BurstType.VoiceBurstD; case (byte)BurstType.VoiceBurstE: return BurstType.VoiceBurstE; case (byte)BurstType.VoiceBurstF: return BurstType.VoiceBurstF; case (byte)BurstType.VoiceTerminator: return BurstType.VoiceTerminator; default: return BurstType.Unknown; } } private string GetChannelStatus(int channelStatus) { switch (channelStatus) { case (int)ChannelStatus.Active: return "Active"; case (int)ChannelStatus.Idle: return "Idle"; case (int)ChannelStatus.SlotBlocked: return "SlotBlocked"; case (int)ChannelStatus.SlotUnblocked: return "SlotUnblocked"; case (int)ChannelStatus.BusyRestChannel: return "BusyRestChannel"; case (int)ChannelStatus.RestChannelIdleAvailable: return "RestChannelIdleAvailable"; case (int)ChannelStatus.LocalGroupCallsNotAllowed: return "LocalGroupCallsBotAllowed"; case (int)ChannelStatus.LocalGroupCallsAllowed: return "LocalGroupCallsAllowed"; case (int)ChannelStatus.RestChannelBlocked: return "RestChannelBlocked"; default: { if (channelStatus == 0x00 || (channelStatus >= 0x03 && channelStatus <= 0x09) || (channelStatus >= 0x0C && channelStatus <= 0x0F) || (channelStatus >= 0x15 && channelStatus <= 0xFF)) return "RESERVED"; else return "Unknown Channel Control Status"; } } } private string GetSlotNumberName(int slotNumber, MotoRepeater.LinkEstablishment.SystemType systemType) { switch (slotNumber) { case (int)SlotNumber.Slot1: return "Slot1"; case (int)SlotNumber.Slot2: return "Slot2"; case 0x00: { switch (systemType) { case MotoRepeater.LinkEstablishment.SystemType.IP_SITE_CONNECT: return "RESERVED"; case MotoRepeater.LinkEstablishment.SystemType.CAPACITY_PLUS: return "RestChannel"; case MotoRepeater.LinkEstablishment.SystemType.LINKED_CAPACITY_PLUS: return "SiteIPAddress"; default: return "RESERVED"; } } default: return "RESERVED"; } } private SlotNumber GetSlotNumber(int slotNumber, MotoRepeater.LinkEstablishment.SystemType systemType) { switch (slotNumber) { case (int)SlotNumber.Slot1: return SlotNumber.Slot1; case (int)SlotNumber.Slot2: return SlotNumber.Slot2; case 0x00: { switch (systemType) { case MotoRepeater.LinkEstablishment.SystemType.IP_SITE_CONNECT: return SlotNumber.Reserved; case MotoRepeater.LinkEstablishment.SystemType.CAPACITY_PLUS: return SlotNumber.RestChannel; case MotoRepeater.LinkEstablishment.SystemType.LINKED_CAPACITY_PLUS: return SlotNumber.SiteIPAddress; default: return SlotNumber.Reserved; } } default: return SlotNumber.Reserved; } } private int ConvertCallSessionStatus4MessageBus(CallSessionStatus status) { switch (status) { case CallSessionStatus.CallInProgress: return (int)CallStatus.INPROGRESS; case CallSessionStatus.CallHang: return (int)CallStatus.HANGTIME; case CallSessionStatus.CallEnd: return (int)CallStatus.ENDED; default: return 0; } } private CallSessionStatus GetCallSessionStatus(int callSessionStatus) { switch (callSessionStatus) { case (int)CallSessionStatus.CallInProgress: return CallSessionStatus.CallInProgress; case (int)CallSessionStatus.CallHang: return CallSessionStatus.CallHang; case (int)CallSessionStatus.CallEnd: return CallSessionStatus.CallEnd; default: return CallSessionStatus.RESERVED; } } private string GetChannelControlStatus(int chCtrlStatus) { switch (chCtrlStatus) { case (int)ChannelControlStatus.Received: return "Received"; case (int)ChannelControlStatus.Transmiting: return "Transmiting"; case (int)ChannelControlStatus.TransmissionSuccessful: return "TransmissionSuccessful"; case (int)ChannelControlStatus.Declined: return "Declined"; default: return "RESERVED"; } } #endregion #region WIRELINE CLASSES private static Int64 queryID = 0x01; public class CSBKCall { public SlotNumber slotNumber = SlotNumber.Slot1; public Int64 callID { get { return _callID; } } private static Int64 _callID = 0x131313; public CallType callType = CallType.PreamblePrivateCSBKCall; public Int64 sourceID = 0; public Int64 targetID = 0; public AccessCriteria accessCriteria = AccessCriteria.PoliteAccess; public CallAttributes callAttributes = CallAttributes.SmartPTT; public PreambleDuration preambleDuration = PreambleDuration.MinDuration; public Int16 preambleValue = 0x03; // added for current CSBK Call Status public ChannelControlStatus chnCtrlstatus = ChannelControlStatus.Declined; public Int64 crtCallID = 0; public Boolean isCSBKPreambleReceived = false; public CSBKCall() { _callID++; } } public class WirelineRegistration { public SlotNumber slotNumber = SlotNumber.Slot1; public static Int64 registrationPduID = BitConverter.ToInt32(new byte[] { 0x61, 0x01, 0x15, 0x43 }, 0); public readonly Int64 registrationID = 0x1; public RegistrationOperationCode regOpCode = RegistrationOperationCode.QueryRegStatus; public WirelineChannelStatus wirelineChannelStatus = WirelineChannelStatus.NotRegisteredWLStatus; public int numberOfRegistrationEntries = 0; public List regEntriesList = new List(); public WirelineRegistration() { } // add a new entry for which the status will be broadcasted public void AddRegEntry(RegEntry entry) { this.numberOfRegistrationEntries++; regEntriesList.Add(entry); } } public class RegEntry { public AddressType addressType = AddressType.AllIndividualCall; public Int64 addressRangeStart = 0; public Int64 addressRangeEnd = 0; public CSBKAttributes csbkAttributes = CSBKAttributes.CSBKRegisteredService; public RegEntry(AddressType addressType, Int64 startID, Int64 endID) { this.addressType = addressType; this.addressRangeStart = startID; this.addressRangeEnd = endID; } } public class WirelineQueryRegistration { public SlotNumber slotNumber = SlotNumber.Slot1; public Int64 registrationPduID = 0; public RegistrationOperationCode regOpCode = RegistrationOperationCode.QueryRegStatus; public WirelineQueryRegistration() { } } #endregion #region EVENT DELEGATES public delegate void RadioStatusChangedDEl(Int64 radioID, SlotNumber slot, Boolean isEnabled); public event RadioStatusChangedDEl OnRadioStatusChanged; public delegate void EmergencyReceivedDEl(Int64 radioID, SlotNumber slot); public event EmergencyReceivedDEl OnEmergencyChanged; public delegate void RegistrationReceivedDEl(Boolean isValid, SlotNumber slot); public event RegistrationReceivedDEl OnRegistrationReceived; public delegate void CallStatusReceivedDEl(Int64 sourceID, Int64 targertID, int callType, int callStatus, SlotNumber slot); public event CallStatusReceivedDEl OnCallStatusReceived; private delegate void ChannelControlStatusReceivedDel(Int64 callID, CallType callType, ChannelControlStatus chnCtrlStatus, SlotNumber slot); private event ChannelControlStatusReceivedDel OnChannelControlStatusReceived; public delegate void CallSessionStatusReceivedDEl(Int64 sourceID, Int64 targertID, CallType callType, CallSessionStatus callSessionStatus, SlotNumber slot); public event CallSessionStatusReceivedDEl OnCallSesionStatusReceived; public delegate void CSBKCallReceivedDEl(Int64 sourceID, Int64 targetID, CallType callType, SlotNumber slot); public event CSBKCallReceivedDEl OnCSBKCallReceived; public delegate void AudioFrameReceivedDEl(byte[] audioFrame, SlotNumber slot); public event AudioFrameReceivedDEl OnAudioFrameReceived; #endregion } }