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