mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-14 17:47:32 -05:00
#1626 Updates DMR call events to include timeslot and adds DMR encryption information when available.
This commit is contained in:
@@ -72,7 +72,10 @@ import io.github.dsheirer.module.decode.dmr.message.data.packet.DMRPacketMessage
|
|||||||
import io.github.dsheirer.module.decode.dmr.message.data.packet.UDTShortMessageService;
|
import io.github.dsheirer.module.decode.dmr.message.data.packet.UDTShortMessageService;
|
||||||
import io.github.dsheirer.module.decode.dmr.message.data.terminator.Terminator;
|
import io.github.dsheirer.module.decode.dmr.message.data.terminator.Terminator;
|
||||||
import io.github.dsheirer.module.decode.dmr.message.type.ServiceOptions;
|
import io.github.dsheirer.module.decode.dmr.message.type.ServiceOptions;
|
||||||
|
import io.github.dsheirer.module.decode.dmr.message.voice.VoiceEMBMessage;
|
||||||
import io.github.dsheirer.module.decode.dmr.message.voice.VoiceMessage;
|
import io.github.dsheirer.module.decode.dmr.message.voice.VoiceMessage;
|
||||||
|
import io.github.dsheirer.module.decode.dmr.message.voice.embedded.Arc4EncryptionParameters;
|
||||||
|
import io.github.dsheirer.module.decode.dmr.message.voice.embedded.EmbeddedParameters;
|
||||||
import io.github.dsheirer.module.decode.event.DecodeEvent;
|
import io.github.dsheirer.module.decode.event.DecodeEvent;
|
||||||
import io.github.dsheirer.module.decode.event.DecodeEventType;
|
import io.github.dsheirer.module.decode.event.DecodeEventType;
|
||||||
import io.github.dsheirer.module.decode.event.PlottableDecodeEvent;
|
import io.github.dsheirer.module.decode.event.PlottableDecodeEvent;
|
||||||
@@ -289,7 +292,7 @@ public class DMRDecoderState extends TimeslotDecoderState
|
|||||||
DecodeEvent smsEvent = DMRDecodeEvent.builder(DecodeEventType.SMS, sms.getTimestamp())
|
DecodeEvent smsEvent = DMRDecodeEvent.builder(DecodeEventType.SMS, sms.getTimestamp())
|
||||||
.details("MESSAGE: " + sms.getSMS())
|
.details("MESSAGE: " + sms.getSMS())
|
||||||
.identifiers(new IdentifierCollection(sms.getIdentifiers()))
|
.identifiers(new IdentifierCollection(sms.getIdentifiers()))
|
||||||
.timeslot(sms.getTimeslot())
|
.timeslot(getTimeslot())
|
||||||
.build();
|
.build();
|
||||||
broadcast(smsEvent);
|
broadcast(smsEvent);
|
||||||
}
|
}
|
||||||
@@ -312,7 +315,7 @@ public class DMRDecoderState extends TimeslotDecoderState
|
|||||||
|
|
||||||
DecodeEvent smsEvent = DMRDecodeEvent.builder(DecodeEventType.SMS, packet.getTimestamp())
|
DecodeEvent smsEvent = DMRDecodeEvent.builder(DecodeEventType.SMS, packet.getTimestamp())
|
||||||
.identifiers(mic)
|
.identifiers(mic)
|
||||||
.timeslot(packet.getTimeslot())
|
.timeslot(getTimeslot())
|
||||||
.details("SMS:" + hyteraSmsPacket.getSMS())
|
.details("SMS:" + hyteraSmsPacket.getSMS())
|
||||||
.build();
|
.build();
|
||||||
broadcast(smsEvent);
|
broadcast(smsEvent);
|
||||||
@@ -324,7 +327,7 @@ public class DMRDecoderState extends TimeslotDecoderState
|
|||||||
|
|
||||||
DecodeEvent unknownTokenEvent = DMRDecodeEvent.builder(DecodeEventType.UNKNOWN_PACKET, packet.getTimestamp())
|
DecodeEvent unknownTokenEvent = DMRDecodeEvent.builder(DecodeEventType.UNKNOWN_PACKET, packet.getTimestamp())
|
||||||
.identifiers(mic)
|
.identifiers(mic)
|
||||||
.timeslot(packet.getTimeslot())
|
.timeslot(getTimeslot())
|
||||||
.details("HYTERA UNK TOKEN MSG:" + hyteraUnknownPacket.getHeader().toString())
|
.details("HYTERA UNK TOKEN MSG:" + hyteraUnknownPacket.getHeader().toString())
|
||||||
.build();
|
.build();
|
||||||
broadcast(unknownTokenEvent);
|
broadcast(unknownTokenEvent);
|
||||||
@@ -333,7 +336,7 @@ public class DMRDecoderState extends TimeslotDecoderState
|
|||||||
{
|
{
|
||||||
DecodeEvent packetEvent = DMRDecodeEvent.builder(DecodeEventType.DATA_PACKET, packet.getTimestamp())
|
DecodeEvent packetEvent = DMRDecodeEvent.builder(DecodeEventType.DATA_PACKET, packet.getTimestamp())
|
||||||
.identifiers(getMergedIdentifierCollection(packet.getIdentifiers()))
|
.identifiers(getMergedIdentifierCollection(packet.getIdentifiers()))
|
||||||
.timeslot(packet.getTimeslot())
|
.timeslot(getTimeslot())
|
||||||
.details(packet.toString())
|
.details(packet.toString())
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
@@ -370,6 +373,19 @@ public class DMRDecoderState extends TimeslotDecoderState
|
|||||||
{
|
{
|
||||||
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.CALL, getTimeslot()));
|
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.CALL, getTimeslot()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(message.getSyncPattern() == DMRSyncPattern.BS_VOICE_FRAME_F && message instanceof VoiceEMBMessage voiceEmb)
|
||||||
|
{
|
||||||
|
if(voiceEmb.hasEmbeddedParameters())
|
||||||
|
{
|
||||||
|
EmbeddedParameters embedded = voiceEmb.getEmbeddedParameters();
|
||||||
|
|
||||||
|
if(embedded.getShortBurst() instanceof Arc4EncryptionParameters arc4)
|
||||||
|
{
|
||||||
|
updateEncryptedCall(arc4, true, voiceEmb.getTimestamp());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -778,7 +794,7 @@ public class DMRDecoderState extends TimeslotDecoderState
|
|||||||
DecodeEvent event = DMRDecodeEvent.builder(DecodeEventType.REQUEST, csbk.getTimestamp())
|
DecodeEvent event = DMRDecodeEvent.builder(DecodeEventType.REQUEST, csbk.getTimestamp())
|
||||||
.details("Registration Request")
|
.details("Registration Request")
|
||||||
.identifiers(getMergedIdentifierCollection(csbk.getIdentifiers()))
|
.identifiers(getMergedIdentifierCollection(csbk.getIdentifiers()))
|
||||||
.timeslot(csbk.getTimeslot())
|
.timeslot(getTimeslot())
|
||||||
.build();
|
.build();
|
||||||
broadcast(event);
|
broadcast(event);
|
||||||
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.CONTROL, getTimeslot()));
|
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.CONTROL, getTimeslot()));
|
||||||
@@ -787,7 +803,7 @@ public class DMRDecoderState extends TimeslotDecoderState
|
|||||||
DecodeEvent regRespEvent = DMRDecodeEvent.builder(DecodeEventType.RESPONSE, csbk.getTimestamp())
|
DecodeEvent regRespEvent = DMRDecodeEvent.builder(DecodeEventType.RESPONSE, csbk.getTimestamp())
|
||||||
.details("Registration Response")
|
.details("Registration Response")
|
||||||
.identifiers(getMergedIdentifierCollection(csbk.getIdentifiers()))
|
.identifiers(getMergedIdentifierCollection(csbk.getIdentifiers()))
|
||||||
.timeslot(csbk.getTimeslot())
|
.timeslot(getTimeslot())
|
||||||
.build();
|
.build();
|
||||||
broadcast(regRespEvent);
|
broadcast(regRespEvent);
|
||||||
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.CONTROL, getTimeslot()));
|
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.CONTROL, getTimeslot()));
|
||||||
@@ -829,7 +845,7 @@ public class DMRDecoderState extends TimeslotDecoderState
|
|||||||
DecodeEvent affiliateEvent = DMRDecodeEvent.builder(DecodeEventType.AFFILIATE, csbk.getTimestamp())
|
DecodeEvent affiliateEvent = DMRDecodeEvent.builder(DecodeEventType.AFFILIATE, csbk.getTimestamp())
|
||||||
.details("TALKGROUP AFFILIATION")
|
.details("TALKGROUP AFFILIATION")
|
||||||
.identifiers(getMergedIdentifierCollection(csbk.getIdentifiers()))
|
.identifiers(getMergedIdentifierCollection(csbk.getIdentifiers()))
|
||||||
.timeslot(csbk.getTimeslot())
|
.timeslot(getTimeslot())
|
||||||
.build();
|
.build();
|
||||||
broadcast(affiliateEvent);
|
broadcast(affiliateEvent);
|
||||||
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.CONTROL, getTimeslot()));
|
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.CONTROL, getTimeslot()));
|
||||||
@@ -840,19 +856,20 @@ public class DMRDecoderState extends TimeslotDecoderState
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private DecodeEvent getDecodeEvent(CSBKMessage csbk, DecodeEventType decodeEventType, DMRChannel channel, IdentifierCollection mergedIdentifiers) {
|
private DecodeEvent getDecodeEvent(CSBKMessage csbk, DecodeEventType decodeEventType, DMRChannel channel,
|
||||||
|
IdentifierCollection mergedIdentifiers) {
|
||||||
return DMRDecodeEvent.builder(decodeEventType, csbk.getTimestamp())
|
return DMRDecodeEvent.builder(decodeEventType, csbk.getTimestamp())
|
||||||
.channel(channel)
|
.channel(channel)
|
||||||
.details(csbk.getOpcode().getLabel())
|
.details(csbk.getOpcode().getLabel())
|
||||||
.identifiers(mergedIdentifiers)
|
.identifiers(mergedIdentifiers)
|
||||||
.timeslot(channel.getTimeslot())
|
.timeslot(getTimeslot())
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
private DecodeEvent getDecodeEvent(CSBKMessage csbk, DecodeEventType decodeEventType, String details) {
|
private DecodeEvent getDecodeEvent(CSBKMessage csbk, DecodeEventType decodeEventType, String details) {
|
||||||
return DMRDecodeEvent.builder(decodeEventType, csbk.getTimestamp())
|
return DMRDecodeEvent.builder(decodeEventType, csbk.getTimestamp())
|
||||||
.identifiers(getMergedIdentifierCollection(csbk.getIdentifiers()))
|
.identifiers(getMergedIdentifierCollection(csbk.getIdentifiers()))
|
||||||
.timeslot(csbk.getTimeslot())
|
.timeslot(getTimeslot())
|
||||||
.details(details)
|
.details(details)
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
@@ -978,7 +995,7 @@ public class DMRDecoderState extends TimeslotDecoderState
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case FULL_CAPACITY_PLUS_GROUP_VOICE_CHANNEL_USER:
|
case FULL_MOTOROLA_GROUP_VOICE_CHANNEL_USER:
|
||||||
if(message instanceof MotorolaGroupVoiceChannelUser cpgvcu)
|
if(message instanceof MotorolaGroupVoiceChannelUser cpgvcu)
|
||||||
{
|
{
|
||||||
if(isTerminator)
|
if(isTerminator)
|
||||||
@@ -1103,7 +1120,7 @@ public class DMRDecoderState extends TimeslotDecoderState
|
|||||||
|
|
||||||
DecodeEvent gpsEvent = DMRDecodeEvent.builder(DecodeEventType.GPS, message.getTimestamp())
|
DecodeEvent gpsEvent = DMRDecodeEvent.builder(DecodeEventType.GPS, message.getTimestamp())
|
||||||
.identifiers(ic)
|
.identifiers(ic)
|
||||||
.timeslot(message.getTimeslot())
|
.timeslot(getTimeslot())
|
||||||
.details("LOCATION:" + gpsInformation.getGPSLocation())
|
.details("LOCATION:" + gpsInformation.getGPSLocation())
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
@@ -1119,6 +1136,41 @@ public class DMRDecoderState extends TimeslotDecoderState
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the current call with encryption information.
|
||||||
|
* @param encryptionParameters decoded from the Voice Frame F
|
||||||
|
* @param isGroup true for group or false for individual call.
|
||||||
|
*/
|
||||||
|
private void updateEncryptedCall(Arc4EncryptionParameters encryptionParameters, boolean isGroup, long timestamp)
|
||||||
|
{
|
||||||
|
if(mCurrentCallEvent != null)
|
||||||
|
{
|
||||||
|
String details = mCurrentCallEvent.getDetails();;
|
||||||
|
|
||||||
|
if(details == null)
|
||||||
|
{
|
||||||
|
details = encryptionParameters.toString();
|
||||||
|
}
|
||||||
|
else if(!details.contains(encryptionParameters.toString()))
|
||||||
|
{
|
||||||
|
details += " " + encryptionParameters;
|
||||||
|
}
|
||||||
|
|
||||||
|
mCurrentCallEvent.setDetails(details);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mCurrentCallEvent = DMRDecodeEvent.builder(isGroup ? DecodeEventType.CALL_GROUP_ENCRYPTED :
|
||||||
|
DecodeEventType.CALL_ENCRYPTED, timestamp)
|
||||||
|
.channel(getCurrentChannel())
|
||||||
|
.details(encryptionParameters.toString())
|
||||||
|
.identifiers(getIdentifierCollection().copyOf())
|
||||||
|
.timeslot(getTimeslot())
|
||||||
|
.build();
|
||||||
|
broadcast(mCurrentCallEvent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates or creates a current call event.
|
* Updates or creates a current call event.
|
||||||
*
|
*
|
||||||
@@ -1136,13 +1188,14 @@ public class DMRDecoderState extends TimeslotDecoderState
|
|||||||
.channel(getCurrentChannel())
|
.channel(getCurrentChannel())
|
||||||
.details(details)
|
.details(details)
|
||||||
.identifiers(getIdentifierCollection().copyOf())
|
.identifiers(getIdentifierCollection().copyOf())
|
||||||
|
.timeslot(getTimeslot())
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
broadcast(mCurrentCallEvent);
|
broadcast(mCurrentCallEvent);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if(type != DecodeEventType.CALL)
|
if(mCurrentCallEvent.getDetails() == null)
|
||||||
{
|
{
|
||||||
mCurrentCallEvent.setDetails(details);
|
mCurrentCallEvent.setDetails(details);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -205,7 +205,7 @@ public class DMRCallSequenceRecorder extends MBECallSequenceRecorder
|
|||||||
{
|
{
|
||||||
switch(message.getOpcode())
|
switch(message.getOpcode())
|
||||||
{
|
{
|
||||||
case FULL_CAPACITY_PLUS_GROUP_VOICE_CHANNEL_USER:
|
case FULL_MOTOROLA_GROUP_VOICE_CHANNEL_USER:
|
||||||
if(message instanceof MotorolaGroupVoiceChannelUser cpvcu)
|
if(message instanceof MotorolaGroupVoiceChannelUser cpvcu)
|
||||||
{
|
{
|
||||||
if(mCallSequence == null)
|
if(mCallSequence == null)
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ public class LCMessageFactory
|
|||||||
case FULL_STANDARD_TERMINATOR_DATA:
|
case FULL_STANDARD_TERMINATOR_DATA:
|
||||||
flc = new TerminatorData(message, timestamp, timeslot);
|
flc = new TerminatorData(message, timestamp, timeslot);
|
||||||
break;
|
break;
|
||||||
case FULL_CAPACITY_PLUS_GROUP_VOICE_CHANNEL_USER:
|
case FULL_MOTOROLA_GROUP_VOICE_CHANNEL_USER:
|
||||||
flc = new MotorolaGroupVoiceChannelUser(message, timestamp, timeslot);
|
flc = new MotorolaGroupVoiceChannelUser(message, timestamp, timeslot);
|
||||||
break;
|
break;
|
||||||
case FULL_CAPACITY_PLUS_ENCRYPTED_VOICE_CHANNEL_USER:
|
case FULL_CAPACITY_PLUS_ENCRYPTED_VOICE_CHANNEL_USER:
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ public enum LCOpcode
|
|||||||
FULL_STANDARD_TERMINATOR_DATA(Vendor.STANDARD, true, 48, "TERMINATOR DATA"),
|
FULL_STANDARD_TERMINATOR_DATA(Vendor.STANDARD, true, 48, "TERMINATOR DATA"),
|
||||||
FULL_STANDARD_UNKNOWN(Vendor.STANDARD,true, -1, "FULL UNKNOWN"),
|
FULL_STANDARD_UNKNOWN(Vendor.STANDARD,true, -1, "FULL UNKNOWN"),
|
||||||
|
|
||||||
FULL_CAPACITY_PLUS_GROUP_VOICE_CHANNEL_USER(Vendor.MOTOROLA_CAPACITY_PLUS, true, 0, "GROUP VOICE CHANNEL USER"),
|
FULL_MOTOROLA_GROUP_VOICE_CHANNEL_USER(Vendor.MOTOROLA_CAPACITY_PLUS, true, 0, "GROUP VOICE CHANNEL USER"),
|
||||||
FULL_CAPACITY_PLUS_WIDE_AREA_VOICE_CHANNEL_USER(Vendor.MOTOROLA_CAPACITY_PLUS, true, 4, "WAN GROUP VOICE CHANNEL USER"),
|
FULL_CAPACITY_PLUS_WIDE_AREA_VOICE_CHANNEL_USER(Vendor.MOTOROLA_CAPACITY_PLUS, true, 4, "WAN GROUP VOICE CHANNEL USER"),
|
||||||
//Observed on Cap+ Multi-Site System during an encrypted voice call
|
//Observed on Cap+ Multi-Site System during an encrypted voice call
|
||||||
FULL_CAPACITY_PLUS_ENCRYPTED_VOICE_CHANNEL_USER(Vendor.MOTOROLA_CAPACITY_PLUS, true, 32, "ENCRYPTED VOICE CHANNEL USER"),
|
FULL_CAPACITY_PLUS_ENCRYPTED_VOICE_CHANNEL_USER(Vendor.MOTOROLA_CAPACITY_PLUS, true, 32, "ENCRYPTED VOICE CHANNEL USER"),
|
||||||
|
|||||||
Reference in New Issue
Block a user