1 // ---------------------------------------------------------------------
2 // ax25_decode.cxx -- AX25 Packet disassembler.
3 //
4 // This file is a proposed part of fldigi. Adapted very liberally from
5 // rtty.cxx, with many thanks to John Hansen, W2FS, who wrote
6 // 'dcc.doc' and 'dcc2.doc', GNU Octave, GNU Radio Companion, and finally
7 // Bartek Kania (bk.gnarf.org) whose 'aprs.c' expository coding style helped
8 // shape this implementation.
9 //
10 // Copyright (C) 2010, 2014
11 // Dave Freese, W1HKJ
12 // Chris Sylvain, KB3CS
13 // Robert Stiles, KK5VD
14 //
15 // fldigi is free software; you can redistribute it and/or modify
16 // it under the terms of the GNU General Public License as published by
17 // the Free Software Foundation; either version 3 of the License, or
18 // (at your option) any later version.
19 //
20 // fldigi is distributed in the hope that it will be useful,
21 // but WITHOUT ANY WARRANTY; without even the implied warranty of
22 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 // GNU General Public License for more details.
24 //
25 // You should have received a copy of the GNU General Public License
26 // along with fldigi; if not, write to the
27 //
28 // Free Software Foundation, Inc.
29 // 51 Franklin Street, Fifth Floor
30 // Boston, MA 02110-1301 USA.
31 //
32 // ---------------------------------------------------------------------
33
34 #include "fl_digi.h"
35 #include "modem.h"
36 #include "misc.h"
37 #include "confdialog.h"
38 #include "configuration.h"
39 #include "status.h"
40 #include "timeops.h"
41 #include "debug.h"
42 #include "qrunner.h"
43 #include "threads.h"
44 #include "ax25_decode.h"
45
46 static PKT_MicE_field MicE_table[][12][5] = {
47 {
48 { Zero, Zero, South, P0, East },
49 { One, Zero, South, P0, East },
50 { Two, Zero, South, P0, East },
51 { Three, Zero, South, P0, East },
52 { Four, Zero, South, P0, East },
53 { Five, Zero, South, P0, East },
54 { Six, Zero, South, P0, East },
55 { Seven, Zero, South, P0, East },
56 { Eight, Zero, South, P0, East },
57 { Nine, Zero, South, P0, East },
58 { Invalid, Null, Null, Null, Null },
59 { Invalid, Null, Null, Null, Null }
60 },
61 { // ['A'..'K'] + 'L'
62 { Zero, One, Null, Null, Null }, // custom A/B/C msg codes
63 { One, One, Null, Null, Null },
64 { Two, One, Null, Null, Null },
65 { Three, One, Null, Null, Null },
66 { Four, One, Null, Null, Null },
67 { Five, One, Null, Null, Null },
68 { Six, One, Null, Null, Null },
69 { Seven, One, Null, Null, Null },
70 { Eight, One, Null, Null, Null },
71 { Nine, One, Null, Null, Null },
72 { Space, One, Null, Null, Null },
73 { Space, Zero, South, P0, East }
74 },
75 { // ['P'..'Z']
76 { Zero, One, North, P100, West }, // standard A/B/C msg codes
77 { One, One, North, P100, West },
78 { Two, One, North, P100, West },
79 { Three, One, North, P100, West },
80 { Four, One, North, P100, West },
81 { Five, One, North, P100, West },
82 { Six, One, North, P100, West },
83 { Seven, One, North, P100, West },
84 { Eight, One, North, P100, West },
85 { Nine, One, North, P100, West },
86 { Space, One, North, P100, West },
87 { Invalid, Null, Null, Null, Null }
88 } };
89
90 static PKT_PHG_table PHG_table[] = {
91 { "Omni", 4 },
92 { "NE", 2 },
93 { "E", 1 },
94 { "SE", 2 },
95 { "S", 1 },
96 { "SW", 2 },
97 { "W", 1 },
98 { "NW", 2 },
99 { "N", 1 }
100 };
101
102 static unsigned char rxbuf[MAXOCTETS+4];
103
104 int mode = FTextBase::RECV;
105
106 #define put_rx_char put_rx_local_char
107
put_rx_local_char(char value)108 inline void put_rx_local_char(char value)
109 {
110 put_rx_processed_char(value, mode);
111 }
112
put_rx_hex(unsigned char c)113 inline void put_rx_hex(unsigned char c)
114 {
115 char v[3];
116
117 snprintf(&v[0], 3, "%02x", c);
118
119 put_rx_char(v[0]);
120 put_rx_char(v[1]);
121 }
122
123
put_rx_const(const char s[])124 inline void put_rx_const(const char s[])
125 {
126 unsigned char *p = (unsigned char *) &s[0];
127 for( ; *p; p++) put_rx_char(*p);
128 }
129
expand_Cmp(unsigned char * cpI)130 static void expand_Cmp(unsigned char *cpI)
131 {
132 // APRS Spec 1.0.1 Chapter 9 - Compressed Position Report format
133
134 unsigned char *cp, tc, cc;
135 unsigned char Cmpbuf[96], *bp = &Cmpbuf[0];
136 unsigned char *tbp = bp;
137
138 double Lat, Lon, td;
139 bool sign;
140
141 cp = cpI+1; // skip past Symbol Table ID char
142
143 // Latitude as base91 number
144 tc = *cp++ - 33;
145 Lat = tc * 91 * 91 * 91; // fourth digit ==> x * 91^3
146 tc = *cp++ - 33;
147 Lat += tc * 91 * 91; // third digit ==> x * 91^2
148 tc = *cp++ - 33;
149 Lat += tc * 91; // second digit ==> x * 91^1
150 tc = *cp++ - 33;
151 Lat += tc; // units digit ==> x * 91^0
152
153 Lat = 90.0 - Lat / 380926.0; // - ==> S, + ==> N
154
155 // Longitude as base91 number
156 tc = *cp++ - 33;
157 Lon = tc * 91 * 91 * 91; // 4th digit
158 tc = *cp++ - 33;
159 Lon += tc * 91 * 91; // 3rd digit
160 tc = *cp++ - 33;
161 Lon += tc * 91; // 2nd digit
162 tc = *cp++ - 33;
163 Lon += tc; // units digit
164
165 Lon = -180.0 + Lon / 190463.0; // - ==> W, + ==> E
166
167 if (Lat < 0) {
168 sign = 1; // has sign (is negative)
169 Lat *= -1;
170 }
171 else sign = 0;
172
173 td = Lat - floor(Lat);
174 cc = snprintf((char *)bp, 3, "%2.f", (Lat - td)); // DD
175 bp += cc;
176 cc = snprintf((char *)bp, 6, "%05.2f", td*60); // MM.MM
177 bp += cc;
178
179 if (sign) *bp++ = 'S';
180 else *bp++ = 'N';
181
182 *bp++ = ' ';
183
184 if (Lon < 0) {
185 sign = 1;
186 Lon *= -1;
187 }
188 else sign = 0;
189
190 td = Lon - floor(Lon);
191 cc = snprintf((char *)bp, 4, "%03.f", (Lon - td)); // DDD
192 bp += cc;
193 cc = snprintf((char *)bp, 6, "%5.2f", td*60); // MM.MM
194 bp += cc;
195
196 if (sign) *bp++ = 'W';
197 else *bp++ = 'E';
198
199 cp += 1; // skip past Symbol Code char
200
201 if (*cp != ' ') { // still more
202 if ((*(cp + 2) & 0x18) == 0x10) { // NMEA source = GGA sentence
203 // compressed Altitude uses chars in the same range
204 // as CSE/SPD but the Compression Type ID takes precedence
205 // when it indicates the NMEA source is a GGA sentence.
206 // so check on this one first and CSE/SPD last.
207 double Altitude;
208
209 tc = *cp++ - 33; // 2nd digit
210 Altitude = tc * 91;
211 tc = *cp++ - 33;
212 Altitude += tc;
213
214 // this compressed posit field is not very useful as spec'ed,
215 // since it cannot produce a possible negative altitude!
216 // the NMEA GGA sentence is perfectly capable of providing
217 // a negative altitude value. Mic-E gets this right.
218
219 // Since the example given in the APRS 1.0.1 Spec uses a value
220 // in excess of 10000, this field should be re-spec'ed as a
221 // value in meters relative to 10km below mean sea level (just
222 // as done in Mic-E).
223 Altitude = pow(1.002, Altitude);
224
225 if (progdefaults.PKT_unitsSI)
226 cc = snprintf((char *)bp, 11, " %-.1fm", Altitude*0.3048);
227 else // units per Spec
228 cc = snprintf((char *)bp, 12, " %-.1fft", Altitude);
229 bp += cc;
230 }
231 else if (*cp == '{') { // pre-calculated radio range
232 double Range;
233
234 cp += 1; // skip past ID char
235
236 tc = *cp++ - 33; // range
237 Range = pow(1.08, (double)tc) * 2;
238
239 if (progdefaults.PKT_unitsSI)
240 cc = snprintf((char *)bp, 24, " Est. Range = %-.1fkm", Range*1.609);
241 else // units per Spec
242 cc = snprintf((char *)bp, 24, " Est. Range = %-.1fmi", Range);
243 bp += cc;
244 }
245 else if (*cp >= '!' && *cp <= 'z') { // compressed CSE/SPD
246 int Speed;
247
248 tc = *cp++ - 33; // course
249 cc = snprintf((char *)bp, 8, " %03ddeg", tc*4);
250 bp += cc;
251
252 tc = *cp++ - 33; // speed
253 Speed = (int)floor(pow(1.08, (double)tc) - 1); // 1.08^tc - 1 kts
254
255 if (progdefaults.PKT_unitsSI)
256 cc = snprintf((char *)bp, 8, " %03dkph", (int)floor(Speed*1.852+0.5));
257 else if (progdefaults.PKT_unitsEnglish)
258 cc = snprintf((char *)bp, 8, " %03dmph", (int)floor(Speed*1.151+0.5));
259 else // units per Spec
260 cc = snprintf((char *)bp, 8, " %03dkts", Speed);
261 bp += cc;
262 }
263 }
264 if (progdefaults.PKT_RXTimestamp)
265 put_rx_const(" ");
266
267 put_rx_const(" [Cmp] ");
268
269 for(; tbp < bp; tbp++) put_rx_char(*tbp);
270
271 put_rx_char('\r');
272
273 if (debug::level >= debug::VERBOSE_LEVEL) {
274 cp = cpI+12; // skip to Compression Type ID char
275
276 if (*(cp - 2) != ' ') { // Cmp Type ID is valid
277 tbp = bp = &Cmpbuf[0];
278
279 tc = *cp - 33; // T
280
281 cc = snprintf((char *)bp, 4, "%02x:", tc);
282 bp += cc;
283
284 strcpy((char *)bp, " GPS Fix = ");
285 bp += 11;
286
287 if ((tc & 0x20) == 0x20) {
288 strcpy((char *)bp, "old");
289 bp += 3;
290 }
291 else {
292 strcpy((char *)bp, "current");
293 bp += 7;
294 }
295
296 strcpy((char *)bp, ", NMEA Source = ");
297 bp += 16;
298
299 switch (tc & 0x18) {
300 case 0x00:
301 strcpy((char *)bp, "other");
302 bp += 5;
303 break;
304 case 0x08:
305 strcpy((char *)bp, "GLL");
306 bp += 3;
307 break;
308 case 0x10:
309 strcpy((char *)bp, "GGA");
310 bp += 3;
311 break;
312 case 0x18:
313 strcpy((char *)bp, "RMC");
314 bp += 3;
315 break;
316 default:
317 strcpy((char *)bp, "\?\?");
318 bp += 2;
319 break;
320 }
321
322 strcpy((char *)bp, ", Cmp Origin = ");
323 bp += 15;
324
325 switch (tc & 0x07) {
326 case 0x00:
327 strcpy((char *)bp, "Compressed");
328 bp += 10;
329 break;
330 case 0x01:
331 strcpy((char *)bp, "TNC BText");
332 bp += 9;
333 break;
334 case 0x02:
335 strcpy((char *)bp, "Software (DOS/Mac/Win/+SA)");
336 bp += 26;
337 break;
338 case 0x03:
339 strcpy((char *)bp, "[tbd]");
340 bp += 5;
341 break;
342 case 0x04:
343 strcpy((char *)bp, "KPC3");
344 bp += 4;
345 break;
346 case 0x05:
347 strcpy((char *)bp, "Pico");
348 bp += 4;
349 break;
350 case 0x06:
351 strcpy((char *)bp, "Other tracker [tbd]");
352 bp += 19;
353 break;
354 case 0x07:
355 strcpy((char *)bp, "Digipeater conversion");
356 bp += 21;
357 break;
358 default:
359 strcpy((char *)bp, "\?\?");
360 bp += 2;
361 break;
362 }
363
364 if (progdefaults.PKT_RXTimestamp)
365 put_rx_const(" ");
366
367 put_rx_const(" [CmpType] ");
368
369 for(; tbp < bp; tbp++) put_rx_char(*tbp);
370
371 put_rx_char('\r');
372 }
373 }
374 }
375
expand_PHG(unsigned char * cpI)376 static void expand_PHG(unsigned char *cpI)
377 {
378 // APRS Spec 1.0.1 Chapter 6 - Time and Position format
379 // APRS Spec 1.0.1 Chapter 7 - PHG Extension format
380
381 bool hasPHG = false;
382 unsigned char *cp, tc, cc;
383 unsigned char PHGbuf[64], *bp = &PHGbuf[0];
384 unsigned char *tbp = bp;
385
386 switch (*cpI) {
387 case '!':
388 case '=': // simplest posits
389
390 cp = cpI+1; // skip past posit ID char
391
392 if (*cp != '/') { // posit not compressed
393 cp += 19; // skip past posit data
394 }
395 else { // posit is compressed
396 cp += 1; // skip past compressed posit ID char
397 cp += 12; // skip past compressed posit data
398 }
399
400 if (strncmp((const char *)cp, "PHG", 3) == 0) { // strings match
401 unsigned char ndigits;
402 int power, height;
403 double gain, range;
404
405 cp += 3; // skip past Data Extension ID chars
406
407 // get span of chars in cp which are only digits
408 ndigits = strspn((const char *)cp, "0123456789");
409
410 switch (ndigits) {
411 //case 1: H might be larger than '9'. code below will work.
412 // must also check that P.GD are all '0'-'9'
413 //break;
414 case 4: // APRS Spec 1.0.1 Chapter 7 page 28
415 case 5: // PHGR proposed for APRS Spec 1.2
416 hasPHG = true;
417
418 tc = *cp++ - '0'; // P
419 power = tc * tc; // tc^2
420 cc = snprintf((char *)bp, 5, "%dW,", power);
421 bp += cc;
422
423 tc = *cp++ - '0'; // H
424 *bp++ = ' ';
425 if (tc < 30) { // constrain Height to signed 32bit value
426 height = 10 * (1 << tc); // 10 * 2^tc
427
428 if (progdefaults.PKT_unitsSI)
429 cc = snprintf((char *)bp, 11, "%dm", (int)floor(height*0.3048+0.5));
430 else // units per Spec
431 cc = snprintf((char *)bp, 12, "%dft", height);
432 bp += cc;
433 }
434 else {
435 height = 0;
436 strcpy((char *)bp, "-\?\?-");
437 bp += 4;
438 }
439 strcpy((char *)bp, " HAAT,");
440 bp += 6;
441
442 tc = *cp++; // G
443 gain = pow(10, ((double)(tc - '0') / 10));
444 cc = snprintf((char *)bp, 6, " %cdB,", tc);
445 bp += cc;
446
447 tc = *cp++ - '0'; // D
448 *bp++ = ' ';
449 if (tc < 9) {
450 strcpy((char *)bp, PHG_table[tc].s);
451 bp += PHG_table[tc].l;
452 }
453 else {
454 strcpy((char *)bp, "-\?\?-");
455 bp += 4;
456 }
457 *bp++ = ',';
458
459 range = sqrt(2 * height * sqrt(((double)power / 10) * (gain / 2)));
460 if (progdefaults.PKT_unitsSI)
461 cc = snprintf((char *)bp, 24, " Est. Range = %-.1fkm", range*1.609);
462 else // units per Spec
463 cc = snprintf((char *)bp, 24, " Est. Range = %-.1fmi", range);
464 bp += cc;
465
466 if (ndigits == 5 && *(cp + 1) == '/') {
467 // PHGR: http://www.aprs.org/aprs12/probes.txt
468 // '1'-'9' and 'A'-'Z' are actually permissible.
469 // does anyone send 10 ('A') or more beacons per hour?
470 strcpy((char *)bp, ", ");
471 bp += 2;
472
473 tc = *cp++; // R
474 cc = snprintf((char *)bp, 14, "%c beacons/hr", tc);
475 bp += cc;
476 }
477 break;
478 default: // switch(ndigits)
479 break;
480 }
481 }
482 break;
483 default: // switch(*cpI)
484 break;
485 }
486
487 if (hasPHG) {
488 if (progdefaults.PKT_RXTimestamp)
489 put_rx_const(" ");
490
491 put_rx_const(" [PHG] ");
492
493 for(; tbp < bp; tbp++) put_rx_char(*tbp);
494
495 put_rx_char('\r');
496 }
497 }
498
expand_MicE(unsigned char * cpI,unsigned char * cpE)499 static void expand_MicE(unsigned char *cpI, unsigned char *cpE)
500 {
501 // APRS Spec 1.0.1 Chapter 10 - Mic-E Data format
502
503 bool isMicE = true;
504 bool msgstd = false, msgcustom = false;
505
506 // decoding starts at first AX.25 dest addr
507 unsigned char *cp = &rxbuf[1], tc, cc;
508 unsigned char MicEbuf[64], *bp = &MicEbuf[0];
509 unsigned char *tbp = bp;
510 unsigned int msgABC = 0;
511 PKT_MicE_field Lat = North, LonOffset = Zero, Lon = West;
512
513 for (int i = 0; i < 3; i++) {
514 // remember: AX.25 dest addr chars are shifted left by one
515 tc = *cp++ >> 1;
516
517 switch (tc & 0xF0) {
518 case 0x30: // MicE_table[0]
519 cc = tc - '0';
520 if (cc < 10) {
521 *bp++ = MicE_table[0][cc][0];
522 }
523 else isMicE = false;
524 break;
525 case 0x40: // MicE_table[1]
526 cc = tc - 'A';
527 if (cc < 12) {
528 bool t = MicE_table[1][cc][1]-'0';
529 if (t) {
530 msgABC |= t << (2-i);
531 msgcustom = true;
532 }
533 else msgABC &= ~(1 << (2-i));
534 *bp++ = MicE_table[1][cc][0];
535 }
536 else isMicE = false;
537 break;
538 case 0x50: // MicE_table[2]
539 cc = tc - 'P';
540 if (cc < 11) {
541 msgABC |= (MicE_table[2][cc][1]-'0') << (2-i);
542 msgstd = true;
543 *bp++ = MicE_table[2][cc][0];
544 }
545 else isMicE = false;
546 break;
547 default: // Invalid
548 isMicE = false;
549 break;
550 }
551 }
552
553 for (int i = 3; i < 6; i++) {
554 // remember: AX.25 dest addr chars are shifted left by one
555 tc = *cp++ >> 1;
556
557 switch (i) {
558 case 3:
559 switch (tc & 0xF0) {
560 case 0x30: // MicE_table[0]
561 cc = tc - '0';
562 if (cc < 10) {
563 Lat = MicE_table[0][cc][2];
564 *bp++ = MicE_table[0][cc][0];
565 }
566 else isMicE = false;
567 break;
568 case 0x40: // MicE_table[1]
569 cc = tc - 'A';
570 if (cc == 11) {
571 Lat = MicE_table[1][cc][2];
572 *bp++ = MicE_table[1][cc][0];
573 }
574 else isMicE = false;
575 break;
576 case 0x50: // MicE_table[2]
577 cc = tc - 'P';
578 if (cc < 11) {
579 Lat = MicE_table[2][cc][2];
580 *bp++ = MicE_table[2][cc][0];
581 }
582 else isMicE = false;
583 break;
584 default: // Invalid
585 isMicE = false;
586 break;
587 }
588 break;
589 case 4:
590 switch (tc & 0xF0) {
591 case 0x30: // MicE_table[0]
592 cc = tc - '0';
593 if (cc < 10) {
594 LonOffset = MicE_table[0][cc][3];
595 *bp++ = MicE_table[0][cc][0];
596 }
597 else isMicE = false;
598 break;
599 case 0x40: // MicE_table[1]
600 cc = tc - 'A';
601 if (cc == 11) {
602 LonOffset = MicE_table[1][cc][3];
603 *bp++ = MicE_table[1][cc][0];
604 }
605 else isMicE = false;
606 break;
607 case 0x50: // MicE_table[2]
608 cc = tc - 'P';
609 if (cc < 11) {
610 LonOffset = MicE_table[2][cc][3];
611 *bp++ = MicE_table[2][cc][0];
612 }
613 else isMicE = false;
614 break;
615 default: // Invalid
616 isMicE = false;
617 break;
618 }
619 break;
620 case 5:
621 switch (tc & 0xF0) {
622 case 0x30: // MicE_table[0]
623 cc = tc - '0';
624 if (cc < 10) {
625 Lon = MicE_table[0][cc][4];
626 *bp++ = MicE_table[0][cc][0];
627 }
628 else isMicE = false;
629 break;
630 case 0x40: // MicE_table[1]
631 cc = tc - 'A';
632 if (cc == 11) {
633 Lon = MicE_table[1][cc][4];
634 *bp++ = MicE_table[1][cc][0];
635 }
636 else isMicE = false;
637 break;
638 case 0x50: // MicE_table[2]
639 cc = tc - 'P';
640 if (cc < 11) {
641 Lon = MicE_table[2][cc][4];
642 *bp++ = MicE_table[2][cc][0];
643 }
644 else isMicE = false;
645 break;
646 default: // Invalid
647 isMicE = false;
648 break;
649 }
650 break;
651 default: // Invalid
652 isMicE = false;
653 break;
654 }
655 }
656
657 if (isMicE) {
658 int Speed = 0, Course = 0;
659
660 if (progdefaults.PKT_RXTimestamp)
661 put_rx_const(" ");
662
663 put_rx_const(" [Mic-E] ");
664
665 if (msgstd && msgcustom)
666 put_rx_const("Unknown? ");
667 else if (msgcustom) {
668 put_rx_const("Custom-");
669 put_rx_char((7 - msgABC)+'0');
670 put_rx_const(". ");
671 }
672 else {
673 switch (msgABC) { // APRS Spec 1.0.1 Chapter 10 page 45
674 case 0:
675 put_rx_const("Emergency");
676 break;
677 case 1:
678 put_rx_const("Priority");
679 break;
680 case 2:
681 put_rx_const("Special");
682 break;
683 case 3:
684 put_rx_const("Committed");
685 break;
686 case 4:
687 put_rx_const("Returning");
688 break;
689 case 5:
690 put_rx_const("In Service");
691 break;
692 case 6:
693 put_rx_const("En Route");
694 break;
695 case 7:
696 put_rx_const("Off Duty");
697 break;
698 default:
699 put_rx_const("-\?\?-");
700 break;
701 }
702 if (msgABC) put_rx_char('.');
703 else put_rx_char('!'); // Emergency!
704
705 put_rx_char(' ');
706 }
707
708 for (; tbp < bp; tbp++) {
709 put_rx_char(*tbp);
710 if (tbp == (bp - 3)) put_rx_char('.');
711 }
712
713 if (Lat == North) put_rx_char('N');
714 else if (Lat == South) put_rx_char('S');
715 else put_rx_char('\?');
716
717 put_rx_char(' ');
718
719 cp = cpI+1; // one past the Data Type ID char
720
721 // decode Lon degrees - APRS Spec 1.0.1 Chapter 10 page 48
722 tc = *cp++ - 28;
723 if (LonOffset == P100) tc += 100;
724 if (tc > 179 && tc < 190) tc -= 80;
725 else if (tc > 189 && tc < 200) tc -= 190;
726
727 cc = snprintf((char *)bp, 4, "%03d", tc);
728 bp += cc;
729
730 // decode Lon minutes
731 tc = *cp++ - 28;
732 if (tc > 59) tc -= 60;
733
734 cc = snprintf((char *)bp, 3, "%02d", tc);
735 bp += cc;
736
737 // decode Lon hundredths of a minute
738 tc = *cp++ - 28;
739
740 cc = snprintf((char *)bp, 3, "%02d", tc);
741 bp += cc;
742
743 for (; tbp < bp; tbp++) {
744 put_rx_char(*tbp);
745 if (tbp == (bp - 3)) put_rx_char('.');
746 }
747
748 if (Lon == East) put_rx_char('E');
749 else if (Lon == West) put_rx_char('W');
750 else put_rx_char('\?');
751
752 // decode Speed and Course - APRS Spec 1.0.1 Chapter 10 page 52
753 tc = *cp++ - 28; // speed: hundreds and tens
754
755 if (tc > 79) tc -= 80;
756 Speed = tc * 10;
757
758 tc = *cp++ - 28; // speed: units and course: hundreds
759
760 Course = (tc % 10); // remainder from dividing by 10
761 tc -= Course; tc /= 10; // tc is now quotient from dividing by 10
762 Speed += tc;
763
764 if (Course > 3) Course -= 4;
765 Course *= 100;
766
767 tc = *cp++ - 28; // course: tens and units
768
769 Course += tc;
770
771 if (progdefaults.PKT_unitsSI)
772 cc = snprintf((char *)bp, 8, " %03dkph", (int)floor(Speed*1.852+0.5));
773 else if (progdefaults.PKT_unitsEnglish)
774 cc = snprintf((char *)bp, 8, " %03dmph", (int)floor(Speed*1.151+0.5));
775 else // units per Spec
776 cc = snprintf((char *)bp, 8, " %03dkts", Speed);
777 bp += cc;
778
779 cc = snprintf((char *)bp, 8, " %03ddeg", Course);
780 bp += cc;
781
782 for (; tbp < bp; tbp++) {
783 put_rx_char(*tbp);
784 }
785
786 cp += 2; // skip past Symbol and Symbol Table ID chars
787
788 if (cp <= cpE) { // still more
789
790 if (*cp == '>') {
791 cp += 1;
792 put_rx_const(" TH-D7");
793 }
794 else if (*cp == ']' && *cpE == '=') {
795 cp += 1;
796 cpE -= 1;
797 put_rx_const(" TM-D710");
798 }
799 else if (*cp == ']') {
800 cp += 1;
801 put_rx_const(" TM-D700");
802 }
803 else if (*cp == '\'' && *(cpE - 1) == '|' && *cpE == '3') {
804 cp += 1;
805 cpE -= 2;
806 put_rx_const(" TT3");
807 }
808 else if (*cp == '\'' && *(cpE - 1) == '|' && *cpE == '4') {
809 cp += 1;
810 cpE -= 2;
811 put_rx_const(" TT4");
812 }
813 else if (*cp == '`' && *(cpE - 1) == '_' && *cpE == ' ') {
814 cp += 1;
815 cpE -= 2;
816 put_rx_const(" VX-8");
817 }
818 else if (*cp == '`' && *(cpE - 1) == '_' && *cpE == '#') {
819 cp += 1;
820 cpE -= 2;
821 put_rx_const(" VX-8D/G"); // VX-8G for certain. guessing.
822 }
823 else if (*cp == '`' && *(cpE - 1) == '_' && *cpE == '\"') {
824 cp += 1;
825 cpE -= 2;
826 put_rx_const(" FTM-350");
827 }
828 else if ((*cp == '\'' || *cp == '`') && *(cp + 4) == '}') {
829 cp += 1;
830 // tracker? rig? ID codes are somewhat ad hoc.
831 put_rx_const(" MFR\?");
832 }
833
834 if (cp < cpE) {
835 if (*(cp + 3) == '}') { // station altitude as base91 number
836 int Altitude = 0;
837
838 tc = *cp++ - 33; // third digit ==> x * 91^2
839 Altitude = tc * 91 * 91;
840 tc = *cp++ - 33; // second digit ==> x * 91^1 ==> x * 91
841 Altitude += tc * 91;
842 tc = *cp++ - 33; // unit digit ==> x * 91^0 ==> x * 1
843 Altitude += tc;
844
845 Altitude -= 10000; // remove offset from datum
846
847 *bp++ = ' ';
848 if (Altitude >= 0) *bp++ = '+';
849
850 if (progdefaults.PKT_unitsEnglish)
851 cc = snprintf((char *)bp, 12, "%dft", (int)floor(Altitude*3.281+0.5));
852 else // units per Spec
853 cc = snprintf((char *)bp, 11, "%dm", Altitude);
854 bp += cc;
855
856 for (; tbp < bp; tbp++) {
857 put_rx_char(*tbp);
858 }
859
860 cp += 1; // skip past '}'
861 }
862 }
863
864 if (cp < cpE) put_rx_char(' ');
865
866 for (; cp <= cpE; cp++) put_rx_char(*cp);
867 }
868
869 put_rx_char('\r');
870 }
871 }
872
do_put_rx_char(unsigned char * cp,size_t count)873 static void do_put_rx_char(unsigned char *cp, size_t count)
874 {
875 int i, j;
876 unsigned char c;
877 bool isMicE = false;
878 unsigned char *cpInfo;
879
880 for (i = 8; i < 14; i++) { // src callsign is second in AX.25 frame
881 c = rxbuf[i] >> 1;
882 if (c != ' ') put_rx_char(c); // skip past padding (if any)
883 }
884
885 // bit 7 = command/response bit
886 // bits 6,5 = 1
887 // bits 4-1 = src SSID
888 // bit 0 = last callsign flag
889 c = (rxbuf[14] & 0x7f) >> 1;
890
891 if (c > 0x30) {
892 put_rx_char('-');
893 if (c < 0x3a)
894 put_rx_char(c);
895 else {
896 put_rx_char('1');
897 put_rx_char(c-0x0a);
898 }
899 }
900
901 put_rx_char('>');
902
903 for (i = 1; i < 7; i++) { // dest callsign is first in AX.25 frame
904 c = rxbuf[i] >> 1;
905 if (c != ' ') put_rx_char(c);
906 }
907
908 c = (rxbuf[7] & 0x7f) >> 1;
909 if (c > 0x30) {
910 put_rx_char('-');
911 if (c < 0x3a)
912 put_rx_char(c);
913 else {
914 put_rx_char('1');
915 put_rx_char(c-0x0a);
916 }
917 }
918
919 j=8;
920 if ((rxbuf[14] & 0x01) != 1) { // check last callsign flag
921 do {
922 put_rx_char(',');
923
924 j += 7;
925 for (i = j; i < (j+6); i++) {
926 c = rxbuf[i] >> 1;
927 if (c != ' ') put_rx_char(c);
928 }
929
930 c = (rxbuf[j+6] & 0x7f) >> 1;
931 if (c > 0x30) {
932 put_rx_char('-');
933 if (c < 0x3a)
934 put_rx_char(c);
935 else {
936 put_rx_char('1');
937 put_rx_char(c-0x0a);
938 }
939 }
940
941 } while ((rxbuf[j+6] & 0x01) != 1);
942
943 if (rxbuf[j+6] & 0x80) // packet gets no more hops
944 put_rx_char('*');
945 }
946
947 if (debug::level < debug::VERBOSE_LEVEL) {
948 // skip past CTRL and PID to INFO bytes when I_FRAME
949 // puts buffer pointer in FCS when U_FRAME and S_FRAME
950 // (save CTRL byte for possible MicE decoding)
951 j += 7;
952 c = rxbuf[j];
953 j += 2;
954 }
955 else { // show more frame info when .ge. VERBOSE debug level
956 j += 7;
957 put_rx_char(';');
958
959 c = rxbuf[j]; // CTRL present in all frames
960
961 if ((c & 0x01) == 0) { // I_FRAME
962 unsigned char p = rxbuf[j+1]; // PID present only in I_FRAME
963
964 if (debug::level == debug::DEBUG_LEVEL) {
965 put_rx_hex(c);
966 put_rx_char(' ');
967 put_rx_hex(p);
968 put_rx_char(';');
969 }
970
971 put_rx_const("I/");
972
973 put_rx_hex( (c & 0xE0) >> 5 ); // AX.25 v2.2 para 2.3.2.1
974 if (c & 0x10) put_rx_char('*'); // P/F bit
975 else put_rx_char('.');
976 put_rx_hex( (c & 0x0E) >> 1 );
977
978 put_rx_char('/');
979
980 switch (p) { // AX.25 v2.2 para 2.2.4
981 case 0x01:
982 put_rx_const("X.25PLP");
983 break;
984 case 0x06:
985 put_rx_const("C-TCPIP");
986 break;
987 case 0x07:
988 put_rx_const("U-TCPIP");
989 break;
990 case 0x08:
991 put_rx_const("FRAG");
992 break;
993 case 0xC3:
994 put_rx_const("TEXNET");
995 break;
996 case 0xC4:
997 put_rx_const("LQP");
998 break;
999 case 0xCA:
1000 put_rx_const("ATALK");
1001 break;
1002 case 0xCB:
1003 put_rx_const("ATALK-ARP");
1004 break;
1005 case 0xCC:
1006 put_rx_const("ARPA-IP");
1007 break;
1008 case 0xCD:
1009 put_rx_const("ARPA-AR");
1010 break;
1011 case 0xCE:
1012 put_rx_const("FLEXNET");
1013 break;
1014 case 0xCF:
1015 put_rx_const("NET/ROM");
1016 break;
1017 case 0xF0:
1018 put_rx_const("NO-L3");
1019 break;
1020
1021 case 0xFF:
1022 put_rx_const("L3ESC=");
1023 put_rx_hex(rxbuf[++j]);
1024 break;
1025
1026 default:
1027 if ((p & 0x30) == 0x10) put_rx_const("L3V1");
1028 else if ((p & 0x30) == 0x20) put_rx_const("L3V2");
1029 else put_rx_const("L3-RSVD");
1030
1031 put_rx_char('=');
1032 put_rx_hex(p);
1033 break;
1034 }
1035 }
1036 else if ((c & 0x03) == 0x01) { // S_FRAME
1037
1038 if (debug::level == debug::DEBUG_LEVEL) {
1039 put_rx_hex(c);
1040 put_rx_char(';');
1041 }
1042
1043 put_rx_const("S/");
1044
1045 put_rx_hex( (c & 0xE0) >> 5 );
1046 if (c & 0x10) put_rx_char('*');
1047 else put_rx_char('.');
1048 put_rx_char('/');
1049
1050 switch (c & 0x0C) { // AX.25 v2.2 para 2.3.4.2
1051 case 0x00:
1052 put_rx_const("RR");
1053 break;
1054 case 0x04:
1055 put_rx_const("RNR");
1056 break;
1057 case 0x08:
1058 put_rx_const("REJ");
1059 break;
1060 case 0x0C:
1061 default:
1062 put_rx_const("UNK");
1063 break;
1064 }
1065 }
1066 else if ((c & 0x03) == 0x03) { // U_FRAME
1067
1068 if (debug::level == debug::DEBUG_LEVEL) {
1069 put_rx_hex(c);
1070 put_rx_char(';');
1071 }
1072
1073 put_rx_char('U');
1074
1075 if (c & 0x10) put_rx_char('*');
1076 else put_rx_char('.');
1077
1078 switch (c & 0xEC) { // AX.25 v2.2 para 2.3.4.3
1079 case 0x00:
1080 put_rx_const("UI");
1081 break;
1082 case 0x0E:
1083 put_rx_const("DM");
1084 break;
1085 case 0x1E:
1086 put_rx_const("SABM");
1087 break;
1088 case 0x20:
1089 put_rx_const("DISC");
1090 break;
1091 case 0x30:
1092 put_rx_const("UA");
1093 break;
1094 case 0x81:
1095 put_rx_const("FRMR");
1096 break;
1097 default:
1098 put_rx_const("UNK");
1099 break;
1100 }
1101 }
1102 j+=2;
1103 }
1104 put_rx_char(':');
1105
1106 // ptr to first info field char
1107 cpInfo = &rxbuf[j];
1108
1109 if ((c & 0x03) == 0x03 && (c & 0xEC) == 0x00
1110 && (*cpInfo == '\'' || *cpInfo == '`'
1111 || *cpInfo == 0x1C || *cpInfo == 0x1D)
1112 && (cp - cpInfo) > 7) {
1113 /*
1114 Mic-E must have at least 8 info chars + Data Type ID char
1115 cp - (cpInfo - 1) > 8 ==> cp - cpInfo > 7
1116 */
1117 // this is very probably a Mic-E encoded packet
1118 isMicE = true;
1119 }
1120
1121 // offset between last info char (not FCS) and bufhead
1122 //i = (cp - &rxbuf[0]); // (cp - &rxbuf[1]) + 1 ==> (cp - &rxbuf[0])
1123 i = count; // (cp - &rxbuf[1]) + 1 ==> (cp - &rxbuf[0])
1124
1125 while (j < i) put_rx_char(rxbuf[j++]);
1126
1127 if (*(cp-1) != '\r')
1128 put_rx_char('\r'); // <cr> only for packets not ending with <cr>
1129
1130 // cp points to FCS, so (cp-X) is last info field char
1131 if ((progdefaults.PKT_expandMicE || debug::level >= debug::VERBOSE_LEVEL)
1132 && isMicE)
1133 expand_MicE(cpInfo, (*(cp-1) == '\r' ? cp-2 : cp-1));
1134
1135 // need to deal with posits having timestamps ('/' and '@' leading char)
1136 if (*cpInfo == '!' || *cpInfo == '=') {
1137 if ((progdefaults.PKT_expandCmp || debug::level >= debug::VERBOSE_LEVEL)
1138 && (*(cpInfo + 1) == '/' || *(cpInfo + 1) == '\\'))
1139 // compressed posit
1140 expand_Cmp(cpInfo+1);
1141
1142 if (progdefaults.PKT_expandPHG
1143 || debug::level >= debug::VERBOSE_LEVEL) // look for PHG data
1144 expand_PHG(cpInfo);
1145 }
1146
1147 if (*(cp-1) == '\r')
1148 put_rx_char('\r'); // for packets ending with <cr>: show it on-screen
1149 }
1150
1151 extern bool PERFORM_CPS_TEST;
1152 static pthread_mutex_t decode_lock_mutex = PTHREAD_MUTEX_INITIALIZER;
1153
ax25_decode(unsigned char * buffer,size_t count,bool pad,bool tx_flag)1154 void ax25_decode(unsigned char *buffer, size_t count, bool pad, bool tx_flag)
1155 {
1156 guard_lock decode_lock(&decode_lock_mutex);
1157
1158 if(!buffer && !count) return;
1159
1160 if(count > MAXOCTETS)
1161 count = MAXOCTETS;
1162
1163 memset(rxbuf, 0, sizeof(rxbuf));
1164
1165 if(pad) {
1166 rxbuf[0] = 0xfe;
1167 memcpy(&rxbuf[1], buffer, count);
1168 count++;
1169 } else {
1170 memcpy(rxbuf, buffer, count);
1171 }
1172
1173 if(tx_flag) {
1174 mode = FTextBase::XMIT;
1175 } else {
1176 mode = FTextBase::RECV;
1177 }
1178
1179 do_put_rx_char(&rxbuf[count], count);
1180 }
1181