-
Notifications
You must be signed in to change notification settings - Fork 240
/
LCMessageFactory.java
238 lines (218 loc) · 11.1 KB
/
LCMessageFactory.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
/*
* *****************************************************************************
* Copyright (C) 2014-2023 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.module.decode.dmr.message.data.lc;
import io.github.dsheirer.bits.CorrectedBinaryMessage;
import io.github.dsheirer.edac.CRCDMR;
import io.github.dsheirer.edac.Checksum_5_DMR;
import io.github.dsheirer.edac.ReedSolomon_12_9_4_DMR;
import io.github.dsheirer.module.decode.dmr.message.data.lc.full.FullLCMessage;
import io.github.dsheirer.module.decode.dmr.message.data.lc.full.GPSInformation;
import io.github.dsheirer.module.decode.dmr.message.data.lc.full.GroupVoiceChannelUser;
import io.github.dsheirer.module.decode.dmr.message.data.lc.full.TalkerAliasBlock1;
import io.github.dsheirer.module.decode.dmr.message.data.lc.full.TalkerAliasBlock2;
import io.github.dsheirer.module.decode.dmr.message.data.lc.full.TalkerAliasBlock3;
import io.github.dsheirer.module.decode.dmr.message.data.lc.full.TalkerAliasHeader;
import io.github.dsheirer.module.decode.dmr.message.data.lc.full.TerminatorData;
import io.github.dsheirer.module.decode.dmr.message.data.lc.full.UnitToUnitVoiceChannelUser;
import io.github.dsheirer.module.decode.dmr.message.data.lc.full.UnknownFullLCMessage;
import io.github.dsheirer.module.decode.dmr.message.data.lc.full.hytera.HyteraArc4EncryptionParameters;
import io.github.dsheirer.module.decode.dmr.message.data.lc.full.hytera.HyteraGroupVoiceChannelUser;
import io.github.dsheirer.module.decode.dmr.message.data.lc.full.hytera.HyteraTerminator;
import io.github.dsheirer.module.decode.dmr.message.data.lc.full.hytera.HyteraUnitToUnitVoiceChannelUser;
import io.github.dsheirer.module.decode.dmr.message.data.lc.full.motorola.CapacityPlusEncryptedVoiceChannelUser;
import io.github.dsheirer.module.decode.dmr.message.data.lc.full.motorola.CapacityPlusWideAreaVoiceChannelUser;
import io.github.dsheirer.module.decode.dmr.message.data.lc.full.motorola.MotorolaArc4EncryptionParameters;
import io.github.dsheirer.module.decode.dmr.message.data.lc.full.motorola.MotorolaGroupVoiceChannelUser;
import io.github.dsheirer.module.decode.dmr.message.data.lc.shorty.ActivityUpdateMessage;
import io.github.dsheirer.module.decode.dmr.message.data.lc.shorty.CapacityPlusRestChannel;
import io.github.dsheirer.module.decode.dmr.message.data.lc.shorty.ConnectPlusControlChannel;
import io.github.dsheirer.module.decode.dmr.message.data.lc.shorty.ConnectPlusTrafficChannel;
import io.github.dsheirer.module.decode.dmr.message.data.lc.shorty.ControlChannelSystemParameters;
import io.github.dsheirer.module.decode.dmr.message.data.lc.shorty.HyteraXPTChannel;
import io.github.dsheirer.module.decode.dmr.message.data.lc.shorty.NullMessage;
import io.github.dsheirer.module.decode.dmr.message.data.lc.shorty.ShortLCMessage;
import io.github.dsheirer.module.decode.dmr.message.data.lc.shorty.TrafficChannelSystemParameters;
import io.github.dsheirer.module.decode.dmr.message.data.lc.shorty.UnknownShortLCMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Link control factory class for creating both short and full link control messages
*/
public class LCMessageFactory
{
private final static Logger mLog = LoggerFactory.getLogger(LCMessageFactory.class);
private static final ReedSolomon_12_9_4_DMR REED_SOLOMON_12_9_4_DMR = new ReedSolomon_12_9_4_DMR();
private static int TERMINATOR_LINK_CONTROL_CRC_MASK = 0x99;
private static int VOICE_LINK_CONTROL_CRC_MASK = 0x96;
/**
* Creates a full link control message
* @param message bits
* @param timestamp of the original message
* @param timeslot of the original message
* @param isTerminator set to true if this is LC for a terminator data burst.
* @return message class
*/
public static FullLCMessage createFull(CorrectedBinaryMessage message, long timestamp, int timeslot, boolean isTerminator)
{
if(message == null)
{
throw new IllegalArgumentException("Message cannot be null");
}
boolean valid = true;
if(message.size() == 77)
{
valid = Checksum_5_DMR.isValid(message);
}
else if(message.size() == 96)
{
//RS(12,9,4) can correct up to floor(4/2) = 1 bit error.
valid = REED_SOLOMON_12_9_4_DMR.correctFullLinkControl(message,
isTerminator ? TERMINATOR_LINK_CONTROL_CRC_MASK : VOICE_LINK_CONTROL_CRC_MASK);
}
else
{
mLog.warn("Unrecognized Link Control Message Size: " + message.size());
}
LCOpcode opcode = FullLCMessage.getOpcode(message);
FullLCMessage flc = null;
switch(opcode)
{
case FULL_STANDARD_GROUP_VOICE_CHANNEL_USER:
flc = new GroupVoiceChannelUser(message, timestamp, timeslot);
break;
case FULL_STANDARD_UNIT_TO_UNIT_VOICE_CHANNEL_USER:
flc = new UnitToUnitVoiceChannelUser(message, timestamp, timeslot);
break;
case FULL_STANDARD_GPS_INFO:
flc = new GPSInformation(message, timestamp, timeslot);
break;
case FULL_STANDARD_TERMINATOR_DATA:
flc = new TerminatorData(message, timestamp, timeslot);
break;
case FULL_MOTOROLA_GROUP_VOICE_CHANNEL_USER:
flc = new MotorolaGroupVoiceChannelUser(message, timestamp, timeslot);
break;
case FULL_CAPACITY_PLUS_ENCRYPTED_VOICE_CHANNEL_USER:
flc = new CapacityPlusEncryptedVoiceChannelUser(message, timestamp, timeslot);
break;
case FULL_CAPACITY_PLUS_WIDE_AREA_VOICE_CHANNEL_USER:
flc = new CapacityPlusWideAreaVoiceChannelUser(message, timestamp, timeslot);
break;
case FULL_ARC4_ENCRYPTION_PARAMETERS:
boolean isHytera = false;
//This is apparently now a DMR standard FLC opcode, even though it's using Motorola's vendor ID (0x10).
//As observed on a known Hytera system, it's not using the standard FLC RS-12/9/4 check, which fails.
//It's apparently using the CRC-CCITT with 0x9696 initial fill. Not sure if both Hytera and Motorola
// are using this FEC. But, by using only 16 bits for checksum versus the standad 24, they're able to
// include a full 24-bit group/radio ID in the message. For now we'll identify as either Motorola
//(using reed solomon) or Hytera (using crc-ccitt).
if(!valid)
{
int bitErrors = CRCDMR.correctCCITT80(message, 0, 80, 0x9696);
if(bitErrors < 2)
{
valid = true;
isHytera = true;
}
}
if(isHytera)
{
flc = new HyteraArc4EncryptionParameters(message, timestamp, timeslot);
}
else
{
flc = new MotorolaArc4EncryptionParameters(message, timestamp, timeslot);
}
break;
case FULL_HYTERA_GROUP_VOICE_CHANNEL_USER:
flc = new HyteraGroupVoiceChannelUser(message, timestamp, timeslot);
break;
case FULL_HYTERA_UNIT_TO_UNIT_VOICE_CHANNEL_USER:
flc = new HyteraUnitToUnitVoiceChannelUser(message, timestamp, timeslot);
break;
case FULL_HYTERA_TERMINATOR:
flc = new HyteraTerminator(message, timestamp, timeslot);
break;
case FULL_STANDARD_TALKER_ALIAS_HEADER:
flc = new TalkerAliasHeader(message, timestamp, timeslot);
break;
case FULL_STANDARD_TALKER_ALIAS_BLOCK_1:
flc = new TalkerAliasBlock1(message, timestamp, timeslot);
break;
case FULL_STANDARD_TALKER_ALIAS_BLOCK_2:
flc = new TalkerAliasBlock2(message, timestamp, timeslot);
break;
case FULL_STANDARD_TALKER_ALIAS_BLOCK_3:
flc = new TalkerAliasBlock3(message, timestamp, timeslot);
break;
default:
flc = new UnknownFullLCMessage(message, timestamp, timeslot);
break;
}
flc.setValid(valid);
return flc;
}
/**
* Creates a short link control message
* @param message bits
* @return message class
*/
public static ShortLCMessage createShort(CorrectedBinaryMessage message, long timestamp, int timeslot)
{
LCOpcode opcode = ShortLCMessage.getOpcode(message);
ShortLCMessage slc = null;
switch(opcode)
{
case SHORT_STANDARD_NULL_MESSAGE:
slc = new NullMessage(message, timestamp, timeslot);
break;
case SHORT_STANDARD_ACTIVITY_UPDATE:;
slc = new ActivityUpdateMessage(message, timestamp, timeslot);
break;
case SHORT_CAPACITY_PLUS_REST_CHANNEL_NOTIFICATION:
slc = new CapacityPlusRestChannel(message, timestamp, timeslot);
break;
case SHORT_CONNECT_PLUS_CONTROL_CHANNEL:
slc = new ConnectPlusControlChannel(message, timestamp, timeslot);
break;
case SHORT_CONNECT_PLUS_TRAFFIC_CHANNEL:
slc = new ConnectPlusTrafficChannel(message, timestamp, timeslot);
break;
case SHORT_STANDARD_CONTROL_CHANNEL_SYSTEM_PARAMETERS:
slc = new ControlChannelSystemParameters(message, timestamp, timeslot);
break;
case SHORT_STANDARD_TRAFFIC_CHANNEL_SYSTEM_PARAMETERS:
slc = new TrafficChannelSystemParameters(message, timestamp, timeslot);
break;
case SHORT_HYTERA_XPT_CHANNEL:
case SHORT_STANDARD_XPT_CHANNEL:
slc = new HyteraXPTChannel(message, timestamp, timeslot);
break;
case SHORT_STANDARD_UNKNOWN:
default:
slc = new UnknownShortLCMessage(message, timestamp, timeslot);
break;
}
boolean valid = CRCDMR.crc8(message, 36) == 0;
// System.out.println("SLC MSG:" + message.toHexString() + " CRC-8 RESIDUAL: " + CRCDMR.crc8(message, 36));
//
slc.setValid(valid);
return slc;
}
}