Open Model Railroad Network (OpenMRN)
Loading...
Searching...
No Matches
DccDebug.cxx
Go to the documentation of this file.
1
35#include "dcc/DccDebug.hxx"
36#include "dcc/Defs.hxx"
38#include "utils/logging.h"
39
40namespace dcc
41{
42
43string packet_to_string(const DCCPacket &pkt, bool bin_payload)
44{
45 if (pkt.packet_header.is_pkt)
46 {
47 return StringPrintf("[cmd:%u]", pkt.command_header.cmd);
48 }
49 // First render the option bits
50 string options;
51 if (pkt.packet_header.is_marklin)
52 {
53 options += "[marklin]";
54 }
55 else
56 {
57 options += "[dcc]";
58 }
59 if (pkt.packet_header.send_long_preamble)
60 {
61 options += "[long_preamble]";
62 }
63 if (pkt.packet_header.sense_ack)
64 {
65 options += "[sense_ack]";
66 }
67 if (pkt.packet_header.rept_count)
68 {
69 options +=
70 StringPrintf("[repeat %u times]", pkt.packet_header.rept_count + 1);
71 }
72 if (!pkt.dlc)
73 {
74 return options + " no payload";
75 }
76 if (bin_payload || pkt.packet_header.is_marklin)
77 {
78 options += "[";
79 for (unsigned i = 0; i < pkt.dlc; ++i)
80 {
81 options += StringPrintf("%02x ", pkt.payload[i]);
82 }
83 options.pop_back();
84 options += "]";
85 }
86 if (pkt.packet_header.is_marklin)
87 {
88 return options;
89 }
90 unsigned ofs = 0;
91 bool is_idle_packet = false;
92 bool is_basic_accy_packet = false;
93 bool is_unknown_packet = false;
94 bool is_svc_packet = false;
95 unsigned accy_address = 0;
96 if (pkt.packet_header.send_long_preamble)
97 {
98 // Checks for service mode packets.
99
100 // WARNING: This code is only guaranteed to correctly decide between
101 // service mode packets and basic decoder packets if the packets were
102 // generated by OpenMRN. Otherwise it is not guaranteed that
103 // long_preamble is equivalent to service mode.
104 using namespace dcc::Defs;
105 is_svc_packet = true;
106 uint8_t cmd = pkt.payload[ofs];
107 int cv = (((pkt.payload[ofs] & 0x3) << 8) | pkt.payload[ofs + 1]) + 1;
108 int reg = (pkt.payload[ofs] & 0x7) + 1;
109 // How many bytes does the command have (cmd and arguments), usually 2
110 // for paged mode and 3 for direct mode commands.
111 int num_bytes =
112 pkt.packet_header.skip_ec ? pkt.dlc - ofs - 1 : pkt.dlc - ofs;
113 if (num_bytes == 3 && (cmd & DCC_SVC_MASK) == DCC_SVC_WRITE)
114 {
115 options += StringPrintf(
116 "[svc] Direct Write Byte CV %d = %d", cv, pkt.payload[ofs + 2]);
117 ofs += 2;
118 }
119 else if (num_bytes == 3 && (cmd & DCC_SVC_MASK) == DCC_SVC_VERIFY)
120 {
121 options += StringPrintf("[svc] Direct Verify Byte CV %d =?= %d", cv,
122 pkt.payload[ofs + 2]);
123 ofs += 2;
124 }
125 else if (num_bytes == 3 &&
126 ((cmd & DCC_SVC_MASK) == DCC_SVC_BIT_MANIPULATE) &&
127 ((pkt.payload[ofs + 2] & DCC_SVC_BITVAL_MASK) ==
128 DCC_SVC_BITVAL_WRITE))
129 {
130 int bitnum = pkt.payload[ofs + 2] & 7;
131 int bitval = pkt.payload[ofs + 2] & DCC_SVC_BITVAL_VALUE ? 1 : 0;
132 options += StringPrintf(
133 "[svc] Direct Write Bit CV %d bit %d = %d", cv, bitnum, bitval);
134 ofs += 2;
135 }
136 else if (num_bytes == 3 &&
137 ((cmd & DCC_SVC_MASK) == DCC_SVC_BIT_MANIPULATE) &&
138 ((pkt.payload[ofs + 2] & DCC_SVC_BITVAL_MASK) ==
139 DCC_SVC_BITVAL_VERIFY))
140 {
141 int bitnum = pkt.payload[ofs + 2] & 7;
142 int bitval = pkt.payload[ofs + 2] & DCC_SVC_BITVAL_VALUE ? 1 : 0;
143 options +=
144 StringPrintf("[svc] Direct Verify Bit CV %d bit %d =?= %d", cv,
145 bitnum, bitval);
146 ofs += 2;
147 }
148 else if (num_bytes == 2 &&
149 (cmd & DCC_SVC_PAGED_MASK) == DCC_SVC_PAGED_WRITE)
150 {
151 options += StringPrintf("[svc] Paged Write Byte Reg %d = %d", reg,
152 pkt.payload[ofs + 1]);
153 ofs++;
154 }
155 else if (num_bytes == 2 &&
156 (cmd & DCC_SVC_PAGED_MASK) == DCC_SVC_PAGED_VERIFY)
157 {
158 options += StringPrintf("[svc] Paged Verify Byte Reg %d =?= %d",
159 reg, pkt.payload[ofs + 1]);
160 ofs++;
161 }
162 else
163 {
164 // Not recognized.
165 is_svc_packet = false;
166 }
167 }
168
169 if (is_svc_packet)
170 {
171 // Do not use the regular address partition logic here.
172 }
173 else if (pkt.payload[ofs] == 0xff)
174 {
175 options += " Idle packet";
176 ofs++;
177 if (pkt.payload[ofs] != 0)
178 {
179 options += StringPrintf(" unexpected[0x%02x]", pkt.payload[ofs]);
180 }
181 is_idle_packet = true;
182 }
183 else if (pkt.payload[ofs] == 0)
184 {
185 options += " Broadcast";
186 ofs++;
187 if (pkt.payload[ofs] == 0)
188 {
189 options += " reset";
190 }
191 }
192 else if ((pkt.payload[ofs] & 0x80) == 0)
193 {
194 options += StringPrintf(" Short Address %u", pkt.payload[ofs]);
195 ofs++;
196 }
197 else if ((pkt.payload[ofs] & 0xC0) == 0x80)
198 {
199 // accessory decoder
200 is_basic_accy_packet = true;
201 accy_address = (pkt.payload[ofs] & 0b111111) << 3;
202 ofs++;
203 }
204 else if (pkt.payload[ofs] >= 192 && pkt.payload[ofs] <= 231)
205 {
206 // long address
207 unsigned addr = pkt.payload[ofs] & 0x3F;
208 addr <<= 8;
209 ofs++;
210 addr |= pkt.payload[ofs];
211 ofs++;
212 options += StringPrintf(" Long Address %u", addr);
213 } else if (pkt.payload[ofs] == 254) {
214 options += " Logon packet";
215 is_unknown_packet = true;
216 while (ofs < pkt.dlc)
217 {
218 options += StringPrintf(" 0x%02x", pkt.payload[ofs++]);
219 }
220 } else if (pkt.payload[ofs] == 253) {
221 options += " Advanced extended packet";
222 is_unknown_packet = true;
223 while (ofs < pkt.dlc)
224 {
225 options += StringPrintf(" 0x%02x", pkt.payload[ofs++]);
226 }
227 }
228 uint8_t cmd = pkt.payload[ofs];
229 if (!is_unknown_packet)
230 {
231 ofs++;
232 }
233 if (is_basic_accy_packet && ((cmd & 0x80) == 0x80))
234 {
235 accy_address |= cmd & 0b111;
236 cmd >>= 3;
237 bool is_activate = cmd & 1;
238 cmd >>= 1;
239 accy_address |= ((~cmd) & 0b111) << 9;
240 unsigned user_address =
241 Defs::accy_address_binary_to_user(accy_address >> 1);
242 const char *n_r =
243 accy_address & 1 ? "closed/normal/on" : "thrown/reverse/off";
244 const char* a_d = is_activate ? "activate" : "deactivate";
245 options += StringPrintf(
246 " Accy %u (user %u %s) %s", accy_address, user_address, n_r, a_d);
247 }
248 else if (is_unknown_packet || is_svc_packet)
249 {
250 }
251 else if ((cmd & 0xC0) == 0x40)
252 {
253 // Speed and direction
254 bool is_forward = (cmd & 0x20) != 0;
255 options += " SPD ";
256 options += is_forward ? 'F' : 'R';
257 uint8_t speed = ((cmd & 0xF) << 1) | ((cmd & 0x10) >> 4);
258 switch (speed)
259 {
260 case 0:
261 options += " 0";
262 break;
263 case 1:
264 options += " 0'";
265 break;
266 case 2:
267 options += " E-STOP";
268 break;
269 case 3:
270 options += " E-STOP'";
271 break;
272 default:
273 options += StringPrintf(" %u", speed - 3);
274 }
275 }
276 else if (cmd == 0x3F) {
277 // 128-speed step
278 uint8_t val = pkt.payload[ofs];
279 ofs++;
280 bool is_forward = (val & 0x80) != 0;
281 uint8_t speed = val & 0x7F;
282 options += " SPD128 ";
283 options += is_forward ? 'F' : 'R';
284 switch (speed)
285 {
286 case 0:
287 options += " 0";
288 break;
289 case 1:
290 options += " E-STOP";
291 break;
292 default:
293 options += StringPrintf(" %u", speed - 1);
294 }
295 }
296 else if ((cmd >> 5) == 0b100)
297 {
298 // function group 0
299 options += StringPrintf(" F[0-4]=%d%d%d%d%d", (cmd >> 4) & 1,
300 (cmd >> 0) & 1, (cmd >> 1) & 1, (cmd >> 2) & 1, (cmd >> 3) & 1);
301 }
302 else if ((cmd >> 5) == 0b101)
303 {
304 // function group 1 or 2
305 if (cmd & 0x10)
306 {
307 options += " F[5-8]=";
308 }
309 else
310 {
311 options += " F[9-12]=";
312 }
313 options += StringPrintf("%d%d%d%d", (cmd >> 0) & 1, (cmd >> 1) & 1,
314 (cmd >> 2) & 1, (cmd >> 3) & 1);
315 }
316 else if ((cmd >> 5) == 0b110)
317 {
318 // expansion
319 uint8_t c = cmd & 0x1F;
320 if ((c & ~1) == 0b11110)
321 {
322 if (c & 1)
323 {
324 options += " F[21-28]=";
325 }
326 else
327 {
328 options += " F[13-20]=";
329 }
330 c = pkt.payload[ofs];
331 ofs++;
332 for (int i = 0; i < 8; ++i, c >>= 1)
333 options += '0' + (c & 1);
334 }
335 else
336 {
338 }
339 }
340 else if (cmd == 0 && is_idle_packet)
341 {
342 }
343 else if ((cmd >> 4) == 0b1110)
344 {
345 // POM command
346 options += " POM CV";
347 unsigned kk = (cmd >> 2) & 3;
348 unsigned cv = (cmd & 3) << 8;
349 cv |= pkt.payload[ofs];
350 ofs++;
351 options += StringPrintf("%d", cv + 1);
352 uint8_t d = pkt.payload[ofs++];
353
354 switch (kk)
355 {
356 case 0b00:
357 {
358 options += StringPrintf(" resvd %02x", d);
359 break;
360 }
361 case 0b01:
362 {
363 options += StringPrintf(" read/verify %d", d);
364 break;
365 }
366 case 0b11:
367 {
368 options += StringPrintf(" write = %d", d);
369 break;
370 }
371 case 0b10:
372 {
373 unsigned bit = d & 7;
374 unsigned value = (d >> 3) & 1;
375 if ((d & 0xE0) != 0xE0)
376 {
377 options += StringPrintf(" bit manipulate unknown (%02x)", d);
378 break;
379 }
380 if ((d & 0x10) == 0x10)
381 {
382 options += StringPrintf(" bit %d write = %d", bit, value);
383 }
384 else
385 {
386 options += StringPrintf(" bit %d verify ?= %d", bit, value);
387 }
388 break;
389 }
390 }
391 }
392
393 // checksum of packet
394 if (ofs == pkt.dlc && (pkt.packet_header.skip_ec == 0 || is_unknown_packet))
395 {
396 // EC skipped.
397 }
398 else if (((ofs + 1) == pkt.dlc) && pkt.packet_header.skip_ec == 1)
399 {
400 uint8_t x = 0;
401 for (unsigned i = 0; i + 1 < pkt.dlc; ++i)
402 {
403 x ^= pkt.payload[i];
404 }
405 if (x != pkt.payload[pkt.dlc - 1])
406 {
407 options += StringPrintf(" [bad EC expected 0x%02x actual 0x%02x]",
408 x, pkt.payload[pkt.dlc - 1]);
409 }
410 }
411 else
412 {
413 options += StringPrintf(" [bad dlc, exp %u, actual %u]", ofs+1, pkt.dlc);
414 while (ofs < pkt.dlc)
415 {
416 options += StringPrintf(" 0x%02x", pkt.payload[ofs++]);
417 }
418 }
419 if (pkt.packet_header.csum_error)
420 {
421 options += " [csum err]";
422 }
423 return options;
424}
425
426} // namespace dcc
string packet_to_string(const DCCPacket &pkt, bool bin_payload)
Renders a DCC packet as a debug string.
Definition DccDebug.cxx:43
uint8_t cmd
Command identifier.
Definition packet.h:85
uint8_t skip_ec
typically for DCC packets: 1: do NOT append an EC byte to the end of the packet.
Definition packet.h:66
uint8_t send_long_preamble
1: send long preamble instead of packet.
Definition packet.h:69
uint8_t is_marklin
0: DCC packet, 1: motorola packet.
Definition packet.h:62
uint8_t csum_error
1 if there was a checksum error in this packet.
Definition packet.h:76
uint8_t sense_ack
1: wait for service mode ack and report it back to the host.
Definition packet.h:71
uint8_t is_pkt
Always 0.
Definition packet.h:60
uint8_t rept_count
The packet will be sent 1 + rept_count times to the wire.
Definition packet.h:74
Stores a DCC packet in memory.
Definition packet.h:55
uint8_t payload[DCC_PACKET_MAX_PAYLOAD]
Packet payload bytes.
Definition packet.h:97
uint8_t dlc
Specifies the number of used payload bytes.
Definition packet.h:95