2685 lines
129 KiB
C#
2685 lines
129 KiB
C#
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<RegEntry> regEntries = new List<RegEntry>();
|
||
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<byte> authSign = new List<byte>();
|
||
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();
|
||
}
|
||
|
||
/// <summary>
|
||
/// Returns the byte array which needs to be send, already containing the wireline authentication signature
|
||
/// </summary>
|
||
/// <param name="wirelineMessage">the byte array which will be used used for genereting the Wireline Signature Key</param>
|
||
/// <returns>A byte array containing the original byte array followed by the Authentication ID [Motorola Provided] and
|
||
/// the Authentication Signature Key [Generated using HMACSha1 algoritm]</returns>
|
||
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<byte> resp = new List<byte>(wirelineMessage);
|
||
resp.AddRange(AuthenticationID);
|
||
resp.AddRange(truncatedAuthenticationSignature);
|
||
|
||
// return the complete wireline message
|
||
return resp.ToArray();
|
||
}
|
||
|
||
|
||
#region WIRELINE MESSAGES
|
||
|
||
/// <summary>
|
||
/// Wireline registration procedure. This procedure creats the wireline message which will be sent to the
|
||
/// repeater in order to create the wireline registration
|
||
/// </summary>
|
||
/// <param name="slot">The slot on which the wireline registration is required</param>
|
||
/// <param name="regEntriesList">The list of entries for which the call messages will be received and parsed</param>
|
||
public void Register(SlotNumber slot, List<RegEntry> 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);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Deregister a prior registration on a desired slot
|
||
/// </summary>
|
||
/// <param name="slot">The slot on which the wireline registration was done</param>
|
||
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
|
||
|
||
/// <summary>
|
||
/// Enable a radio on a specific slot
|
||
/// </summary>
|
||
/// <param name="radioID">The radio ID of the unit which needs to be enabled</param>
|
||
/// <param name="slot">Slot number on which the request will be sent</param>
|
||
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);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Enable a radio on the current slot
|
||
/// </summary>
|
||
/// <param name="radioID">The radio ID of the unit which needs to be enabled</param>
|
||
public void EnableRadio(Int64 radioID)
|
||
{
|
||
EnableRadioOnSlot(radioID, GetCurrentSlot());
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// Disable a radio on a specific slot
|
||
/// </summary>
|
||
/// <param name="radioID">The radio ID of the unit which needs to be disabled</param>
|
||
/// <param name="slot">Slot number on which the request will be sent</param>
|
||
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) });
|
||
}
|
||
|
||
/// <summary>
|
||
/// Disable a radio on the current slot
|
||
/// </summary>
|
||
/// <param name="radioID">The radio ID of the unit which needs to be disabled</param>
|
||
public void DisableRadio(Int64 radioID)
|
||
{
|
||
DisableRadioOnSlot(radioID, GetCurrentSlot());
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// 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
|
||
/// </summary>
|
||
/// <param name="radioID">The radio which changed its status</param>
|
||
/// <param name="slot">The slot on which the radio had reported</param>
|
||
/// <param name="isEnabled">The radio state. Will be true if the radio is Enabled or false otherwise</param>
|
||
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));
|
||
}
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// Acknowledge an emergency received from a field radio on the current selected slot
|
||
/// </summary>
|
||
/// <param name="radioID">The field radio which had sent the emergency</param>
|
||
public void EmergencyAck(Int64 radioID)
|
||
{
|
||
EmergencyACKOnSlot(radioID, GetCurrentSlot());
|
||
}
|
||
|
||
/// <summary>
|
||
/// Acknowledge an emergency received from a field radio
|
||
/// </summary>
|
||
/// <param name="radioID">The field radio which had sent the emergency</param>
|
||
/// <param name="slot">The slot on which the request will be sent</param>
|
||
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
|
||
/// <summary>
|
||
/// Initiate a private call on the specified slot
|
||
/// </summary>
|
||
/// <param name="radioID">The radio ID of the field station which needs to be called</param>
|
||
/// <param name="slot">Slot number on which the request will be sent</param>
|
||
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);
|
||
}
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// Terminate a private call on the specified slot
|
||
/// </summary>
|
||
/// <param name="radioID">The radio ID of the field station which needs to end the call</param>
|
||
/// <param name="slot">Slot number on which the request will be sent</param>
|
||
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);
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// Initiate a group call on the specified slot
|
||
/// </summary>
|
||
/// <param name="groupID">The group ID of the field stations which needs to be called</param>
|
||
/// <param name="slot">Slot number on which the request will be sent</param>
|
||
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);
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// Terminate a group call on the specified slot
|
||
/// </summary>
|
||
/// <param name="groupID">The group ID of the field stations which needs to end the call</param>
|
||
/// <param name="slot">Slot number on which the request will be sent</param>
|
||
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);
|
||
}
|
||
|
||
|
||
|
||
/// <summary>
|
||
/// Initiate an all call on the specified slot
|
||
/// </summary>
|
||
/// <param name="slot">Slot number on which the request will be sent</param>
|
||
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);
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// Terminate an all call on the specified slot
|
||
/// </summary>
|
||
/// <param name="slot">Slot number on which the request will be sent</param>
|
||
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);
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// 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
|
||
/// </summary>
|
||
/// <param name="sourceID">The radio ID of the field radio which had initiated the call</param>
|
||
/// <param name="targetID">The radio ID of the field radio which had been called</param>
|
||
/// <param name="callType">The type of call which is in progress/was made</param>
|
||
/// <param name="chnCtrlStatus">The status of the channel </param>
|
||
/// <param name="slot">The slot on which the call was made</param>
|
||
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();
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Event Callback for a call session status received. The event is triggered each time a call is transmited,
|
||
/// declined etc.
|
||
/// </summary>
|
||
/// <param name="sourceID">The radio ID of the field radio which had initiated the call</param>
|
||
/// <param name="targetID">The radio ID of the field radio which had been called</param>
|
||
/// <param name="callType">The type of call which is in progress/was made</param>
|
||
/// <param name="callSessionStatus">The status of the call on Transportation Layer</param>
|
||
/// <param name="slot">The slot on which the call was made</param>
|
||
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;
|
||
}
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
///
|
||
/// </summary>
|
||
/// <param name="callID"></param>
|
||
/// <param name="isAccepted"></param>
|
||
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<byte> responseList = new List<byte>();
|
||
// 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<byte[]> 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<RegEntry> regEntriesList = new List<RegEntry>();
|
||
|
||
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
|
||
|
||
|
||
|
||
|
||
}
|
||
}
|