summaryrefslogtreecommitdiffstats
path: root/listen/opentracdump.c
diff options
context:
space:
mode:
Diffstat (limited to 'listen/opentracdump.c')
-rw-r--r--listen/opentracdump.c577
1 files changed, 577 insertions, 0 deletions
diff --git a/listen/opentracdump.c b/listen/opentracdump.c
new file mode 100644
index 0000000..953618b
--- /dev/null
+++ b/listen/opentracdump.c
@@ -0,0 +1,577 @@
+/* OpenTRAC packet decoding by Scott Miller, N1VG
+ Copyright (c) 2003 Scott Miller
+ Protocol v1.0 draft, Modified 19 Jun 2003
+*/
+
+#include <stdio.h>
+#include <time.h>
+#include <string.h>
+#include "listen.h"
+
+#define MAX_UNIT_INDEX 28
+
+const char *units[]={"Volts","Amperes","Watts","Kelvins","Meters","Seconds",
+ "Meters/Second","Liters","Kilograms","Bits/Second",
+ "Bytes","Radians","Radians/Second","Square Meters",
+ "Joules","Newtons","Pascals","Hertz","Meters/Sec^2",
+ "Grays","Lumens","Cubic Meters/Second","Pascal Seconds",
+ "Kilograms/Meter^3","Radians/Second^2","Coulombs",
+ "Farads","Siemens","Count"};
+
+// Return values: 0 = OK, -1 = Couldn't Decode, -2 = Invalid Data
+
+int decode_sequence(unsigned char *element, int element_len);
+int decode_origination(unsigned char *element, int element_len);
+int decode_entityid(unsigned char *element, int element_len);
+int decode_position(unsigned char *element, int element_len);
+int decode_timestamp(unsigned char *element, int element_len);
+int decode_comment(unsigned char *element, int element_len);
+int decode_courseandspeed(unsigned char *element, int element_len);
+int decode_ambiguity(unsigned char *element, int element_len);
+int decode_country(unsigned char *element, int element_len);
+int decode_displayname(unsigned char *element, int element_len);
+int decode_waypoint(unsigned char *element, int element_len);
+int decode_symbol(unsigned char *element, int element_len);
+int decode_pathtrace(unsigned char *element, int element_len);
+int decode_heardby(unsigned char *element, int element_len);
+int decode_availablenets(unsigned char *element, int element_len);
+int decode_maidenhead(unsigned char *element, int element_len);
+int decode_gpsquality(unsigned char *element, int element_len);
+int decode_acreg(unsigned char *element, int element_len);
+int decode_rivergauge(unsigned char *element, int element_len);
+int decode_hazmat(unsigned char *element, int element_len);
+int flag_emergency(void);
+int flag_attention(void);
+
+int extract_ssid(unsigned char *call);
+
+int decode_units(unsigned int unitnum, unsigned char *element, int element_len);
+
+unsigned char origin_call[7]; // Who's talking
+unsigned char origin_ssid;
+unsigned char entity_call[7]; // What they're talking about
+unsigned char entity_ssid;
+unsigned int entity_serial;
+unsigned int entity_sequence;
+
+/* Dump an OpenTRAC packet */
+void opentrac_dump(unsigned char *data, int length, int hexdump)
+{
+ int elen;
+ int etype;
+ int decoded = 0;
+
+ lprintf(T_PROTOCOL, "OpenTRAC decode (%d bytes):\r\n", length);
+ strcpy(origin_call, "SENDER"); // Listen doesn't tell us the sender
+ origin_ssid = 0;
+ entity_serial = 0;
+ entity_sequence = 0;
+ while (decoded < length) {
+ elen = (int)*data;
+ decoded += (elen & 0x7f)+1;
+ if (elen & 0x80) { // See if it's got a 16-bit ID
+ elen = (elen & 0x7f) - 2; // Strip the extid flag
+ etype = get16(++data);
+ }
+ else {
+ elen--; // Don't count the type byte
+ etype = (int)*(data+1);
+ }
+ data+=2; // Skip to the body
+ lprintf(T_OPENTRAC, "EID 0x%0x len %d: ", etype, elen);
+ switch (etype) {
+ case (0x00): // Sequence
+ decode_sequence(data, elen);
+ break;
+ case (0x01): // Originating Station
+ decode_origination(data, elen);
+ break;
+ case (0x02): // Entity ID
+ decode_entityid(data, elen);
+ break;
+ case (0x10): // Position report
+ decode_position(data, elen);
+ break;
+ case (0x11): // Timestamp
+ decode_timestamp(data, elen);
+ break;
+ case (0x12): // Comment
+ decode_comment(data, elen);
+ break;
+ case (0x13): // Course and Speed
+ decode_courseandspeed(data, elen);
+ break;
+ case (0x14): // Positional Ambiguity
+ decode_ambiguity(data, elen);
+ break;
+ case (0x15): // Country Code
+ decode_country(data, elen);
+ break;
+ case (0x16): // Display Name
+ decode_displayname(data, elen);
+ break;
+ case (0x17): // Waypoint Name
+ decode_waypoint(data, elen);
+ break;
+ case (0x18): // Map Symbol
+ decode_symbol(data, elen);
+ break;
+ case (0x20): // Path Trace
+ decode_pathtrace(data, elen);
+ break;
+ case (0x21): // Heard-By List
+ decode_heardby(data, elen);
+ break;
+ case (0x22): // Available Networks
+ decode_availablenets(data, elen);
+ break;
+ case (0x32): // Maidenhead Locator
+ decode_maidenhead(data, elen);
+ break;
+ case (0x34): // GPS Data Quality
+ decode_gpsquality(data, elen);
+ break;
+ case (0x35): // Aircraft Registration
+ decode_acreg(data, elen);
+ break;
+ case (0x42): // River Flow Gauge
+ decode_rivergauge(data, elen);
+ break;
+ case (0x100): // Emergency/distress flag
+ flag_emergency();
+ break;
+ case (0x101): // Attention/ident flag
+ flag_attention();
+ break;
+ case (0x300): // Hazmat
+ decode_hazmat(data, elen);
+ break;
+ default: // Everything else
+ if ((etype & 0xff00) == 0x500) {
+ decode_units(etype & 0x00ff, data, elen);
+ }
+ else {
+ lprintf(T_OPENTRAC, "Unknown Element Type\r\n");
+ }
+ }
+ data+=elen;
+ }
+}
+
+int decode_sequence(unsigned char *element, int element_len) {
+ // 0x00 Sequence number - 16 bit integer
+ if (element_len != 2 && element_len != 0) return -1;
+
+ if (!element_len) {
+ entity_sequence++;
+ }
+ else {
+ entity_sequence = get16(element);
+ }
+
+ lprintf(T_OPENTRAC,"Sequence: %d\r\n",entity_sequence);
+
+ return 0;
+}
+
+int decode_origination(unsigned char *element, int element_len) {
+ // 0x01 Originating Station - Callsign, SSID, Sequence, and Network
+ unsigned char network;
+
+ if (element_len < 8) return -1;
+ if (element_len > 9) return -1;
+
+ memcpy(origin_call, element, 6);
+ origin_call[6]=0;
+ origin_ssid = extract_ssid(origin_call);
+ entity_sequence = get16(element+6);
+ strcpy(entity_call, origin_call);
+ entity_ssid = origin_ssid;
+ entity_serial = 0;
+ if (element_len == 9) {
+ network = *(element+9);
+ lprintf(T_OPENTRAC, "Origin: %s-%d seq %d net %d\r\n",origin_call,origin_ssid,entity_sequence,network);
+ }
+ else lprintf(T_OPENTRAC, "Origin: %s-%d seq %d direct\r\n",origin_call,origin_ssid,entity_sequence);
+
+ return 0;
+}
+
+int decode_entityid(unsigned char *element, int element_len) {
+ // 0x02 Entity ID
+
+ if (element_len > 10) return -1;
+
+ if (element_len > 5) {
+ memcpy(entity_call, element, 6);
+ entity_call[6]=0;
+ entity_ssid = extract_ssid(entity_call);
+ }
+ else {
+ strcpy(entity_call, origin_call);
+ }
+
+ switch (element_len) {
+ case 0:
+ entity_serial++;
+ entity_sequence = 0;
+ break;
+ case 2:
+ entity_serial = get16(element);
+ entity_sequence = 0;
+ break;
+ case 4:
+ entity_serial = get16(element);
+ entity_sequence = get16(element+2);
+ break;
+ case 6:
+ entity_serial = 0;
+ break;
+ case 8:
+ entity_serial = get16(element+6);
+ entity_sequence = 0;
+ break;
+ case 10:
+ entity_serial = get16(element+6);
+ entity_sequence = get16(element+8);
+ break;
+ default:
+ return -1;
+ }
+
+ lprintf(T_OPENTRAC, "Entity %s-%d:%04x #%d\r\n", entity_call, entity_ssid, entity_serial, entity_sequence);
+
+ return 0;
+}
+
+int decode_position(unsigned char *element, int element_len) {
+ // 0x10 Position Report - Lat/Lon/<Alt>
+ // Lat/Lon is WGS84, 180/2^31 degrees, Alt is 1/100 meter
+ const double semicircles = 11930464.71111111111;
+ double lat, lon;
+ float alt = 0;
+
+ if (element_len < 8) return -1; // Too short!
+ if (element_len > 11) return -1; // Too long!
+
+ lat = get32(element) / semicircles;
+ lon = get32(element+4) / semicircles;
+ if (element_len == 11) {
+ alt = ((((*(element+8))<<16)+get16(element+9))/100)-10000;
+ }
+ if (lat >= 90 || lat <= -90 || lon >= 180 || lon <= -180) return -2;
+ lprintf(T_OPENTRAC, "Position: Lat %f Lon %f Alt %f\r\n",lat,lon,alt);
+
+ return 0;
+}
+
+int decode_timestamp(unsigned char *element, int element_len) {
+ // 0x11 Timestamp - Unix format time (unsigned)
+ long rawtime = 0;
+
+ if (element_len != 4) return -1;
+
+ rawtime = get32(element);
+ lprintf(T_OPENTRAC, "Time: %s", ctime(&rawtime));
+
+ return 0;
+}
+
+int decode_comment(unsigned char *element, int element_len) {
+ // 0x12 Freeform Comment - ASCII text
+ char comment[127];
+
+ if (element_len > 126) return -1; // shouldn't be possible
+
+ strncpy(comment, element, element_len);
+ comment[element_len] = 0;
+ lprintf(T_OPENTRAC, "Text: %s\r\n", comment);
+
+ return 0;
+}
+
+int decode_courseandspeed(unsigned char *element, int element_len) {
+ // 0x13 Course and Speed - Course in degrees, speed in 1/50 m/s
+ unsigned int course;
+ unsigned int rawspeed;
+ float speed;
+
+ if (element_len != 3) return -1;
+
+ course = (*element<<1) + ((*(element+1)&0x8000) >> 15);
+ rawspeed = (get16(element+1) & 0x7ffff);
+ speed = (float)rawspeed*0.072; // kph
+ if (course >= 360) return -2;
+ lprintf(T_OPENTRAC, "Course: %d Speed: %f kph\r\n", course, speed);
+
+ return 0;
+}
+
+int decode_ambiguity(unsigned char *element, int element_len) {
+ // 0x14 Positional Ambiguity - 16 bits, in meters
+ int ambiguity;
+
+ if (element_len != 2) return -1;
+
+ ambiguity = get16(element);
+ lprintf(T_OPENTRAC, "Position +/- %d meters\r\n", ambiguity);
+ return 0;
+}
+
+int decode_country(unsigned char *element, int element_len) {
+ // 0x15 Country Code - ISO 3166-1 and optionally -2
+ char country[3];
+ char subdivision[4];
+
+ if (element_len < 2) return -1;
+ if (element_len > 5) return -1;
+
+ strncpy(country, element, 2);
+ country[2] = 0;
+ if (element_len > 2) {
+ strncpy(subdivision, element+2, element_len-2);
+ subdivision[element_len-2] = 0;
+ lprintf(T_OPENTRAC, "Country Code %s-%s\r\n", country, subdivision);
+ }
+ else {
+ lprintf(T_OPENTRAC, "Country Code %s\r\n", country);
+ }
+ return 0;
+}
+
+int decode_displayname(unsigned char *element, int element_len) {
+// 0x16 - Display Name (UTF-8 text)
+ char displayname[31];
+
+ if (element_len > 30 || !element_len) return -1;
+
+ strncpy(displayname, element, element_len);
+ displayname[element_len] = 0;
+
+ lprintf(T_OPENTRAC, "Display Name: %s\r\n", displayname);
+ return 0;
+}
+
+int decode_waypoint(unsigned char *element, int element_len) {
+// 0x17 - Waypoint Name (up to 6 chars, uppercase)
+ char waypoint[7];
+
+ if (element_len > 6 || !element_len) return -1;
+
+ strncpy(waypoint, element, element_len);
+ waypoint[element_len] = 0;
+
+ lprintf(T_OPENTRAC, "Waypoint Name: %s\r\n", waypoint);
+ return 0;
+}
+
+int decode_symbol(unsigned char *element, int element_len) {
+ // 0x18 Map Symbol - Packed 4-bit integers
+ int c;
+
+ if (!element_len) return -1;
+
+ lprintf(T_OPENTRAC, "Symbol: ");
+ for (c=0;c<element_len;c++) {
+ if (c>0) lprintf(T_OPENTRAC, ".");
+ lprintf(T_OPENTRAC, "%d", element[c] >> 4);
+ if (element[c] & 0x0f) lprintf(T_OPENTRAC, ".%d", element[c] & 0x0f);
+ }
+ lprintf(T_OPENTRAC, "\r\n");
+
+ return 0;
+}
+
+int decode_pathtrace(unsigned char *element, int element_len) {
+ // 0x20 Path Trace - Call/SSID, Network
+ char callsign[7];
+ int ssid, c, network;
+
+ if (element_len % 7) return -1; // Must be multiple of 7 octets
+ if (!element_len) {
+ lprintf(T_OPENTRAC, "Empty Path\r\n");
+ return 0;
+ }
+
+ lprintf(T_OPENTRAC, "Path: ");
+ for (c=0; c<element_len; c+=7) {
+ memcpy(callsign, element+c, 6);
+ ssid = extract_ssid(callsign);
+ network = (int)*(element+c+6);
+ lprintf(T_OPENTRAC, " %s-%d (%d)", callsign, ssid, network);
+ }
+ lprintf(T_OPENTRAC, "\r\n");
+
+ return 0;
+}
+
+int decode_heardby(unsigned char *element, int element_len) {
+ // 0x21 Heard-By List
+ int c;
+
+ lprintf(T_OPENTRAC, "Heard By:");
+ for (c=0; c<element_len; c++) {
+ lprintf(T_OPENTRAC, " %d", (int)*(element+c));
+ }
+ lprintf(T_OPENTRAC, "\r\n");
+
+ return 0;
+}
+
+int decode_availablenets(unsigned char *element, int element_len) {
+ // 0x22 Available Networks
+ int c;
+
+ lprintf(T_OPENTRAC, "Available Networks:");
+ for (c=0; c<element_len; c++) {
+ lprintf(T_OPENTRAC, " %d", (int)*(element+c));
+ }
+ lprintf(T_OPENTRAC, "\r\n");
+
+ return 0;
+}
+
+int decode_gpsquality(unsigned char *element, int element_len) {
+ // 0x34 GPS Data Quality - Fix, Validity, Sats, PDOP, HDOP, VDOP
+ int fixtype, validity, sats;
+ const char *fixstr[] = {"Unknown Fix", "No Fix", "2D Fix", "3D Fix"};
+ const char *validstr[] = {"Invalid", "Valid SPS", "Valid DGPS",
+ "Valid PPS"};
+
+ if (element_len > 4 || !element_len) return -1;
+
+ fixtype = (element[0] & 0xc0) >> 6;
+ validity = (element[0] & 0x30) >> 4;
+ sats = (element[0] & 0x0f);
+ lprintf(T_OPENTRAC, "GPS: %s %s, %d sats", fixstr[fixtype],
+ validstr[validity], sats);
+ if (element_len > 1)
+ lprintf(T_OPENTRAC, " PDOP=%.1f", (float)element[1]/10);
+ if (element_len > 2)
+ lprintf(T_OPENTRAC, " HDOP=%.1f", (float)element[2]/10);
+ if (element_len > 3)
+ lprintf(T_OPENTRAC, " VDOP=%.1f", (float)element[3]/10);
+ lprintf(T_OPENTRAC, "\r\n");
+
+ return 0;
+}
+
+
+int decode_acreg(unsigned char *element, int element_len) {
+ // 0x35 Aircraft Registration - ASCII text
+ char nnumber[9];
+
+ if (element_len > 8) return -1;
+
+ strncpy(nnumber, element, element_len);
+ nnumber[element_len]=0;
+ lprintf(T_OPENTRAC, "Aircraft ID: %s\r\n", nnumber);
+
+ return 0;
+}
+
+int decode_rivergauge(unsigned char *element, int element_len) {
+ // 0x42 River Flow Gauge - 1/64 m^3/sec, centimeters
+ unsigned int flow;
+ unsigned int height;
+ float flowm;
+ float heightm;
+
+ if (element_len != 4) return -1;
+
+ flow = get16(element);
+ height = get16(element+2);
+ flowm = (float)flow / 64;
+ heightm = (float)height / 100;
+ lprintf(T_OPENTRAC, "River flow rate: %f Cu M/Sec Height: %f M\r\n",
+ flowm, heightm);
+
+ return 0;
+}
+
+
+int decode_units(unsigned int unitnum, unsigned char *element, int element_len) {
+ // 0x0500 to 0x05ff Generic Measurement Elements
+ // Values may be 8-bit int, 16-bit int, single float, or double float
+ union measurement {
+ char c;
+ float f;
+ double d;
+ } *mval;
+ int ival; // too much variation in byte order and size for union
+
+ if (unitnum > MAX_UNIT_INDEX) return -2; // Invalid unit name
+ mval = (void *)element;
+ switch (element_len) {
+ case 1:
+ lprintf(T_OPENTRAC, "%d %s\r\n", mval->c, units[unitnum]);
+ break;
+ case 2:
+ ival = get16(element);
+ lprintf(T_OPENTRAC, "%d %s\r\n", ival, units[unitnum]);
+ break;
+ case 4:
+ lprintf(T_OPENTRAC, "%f %s\r\n", mval->f, units[unitnum]);
+ break;
+ case 8:
+ lprintf(T_OPENTRAC, "%f %s\r\n", mval->d, units[unitnum]);
+ break;
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+int flag_emergency(void) {
+ // 0x0100 - Emergency / Distress Call
+ lprintf(T_ERROR, "* * * EMERGENCY * * *\r\n");
+ return 0;
+}
+
+int flag_attention(void) {
+ // 0x0101 - Attention / Ident
+ lprintf(T_PROTOCOL, " - ATTENTION - \r\n");
+ return 0;
+}
+
+int decode_hazmat(unsigned char *element, int element_len) {
+// 0x0300 - HAZMAT (UN ID in lower 14 bits)
+ int un_id;
+
+ if (element_len < 2) {
+ lprintf(T_OPENTRAC, "HAZMAT: Unknown Material\r\n");
+ }
+ else
+ {
+ un_id = get16(element) & 0x3fff;
+ lprintf(T_OPENTRAC, "HAZMAT: UN%04d\r\n", un_id);
+ }
+ return 0;
+}
+
+int decode_maidenhead(unsigned char *element, int element_len) {
+// 0x32 - Maidenhead Locator (4 or 6 chars)
+ char maidenhead[7];
+
+ if (element_len > 6 || !element_len) return -1;
+
+ strncpy(maidenhead, element, element_len);
+ maidenhead[element_len] = 0;
+
+ lprintf(T_OPENTRAC, "Grid ID: %s\r\n", maidenhead);
+ return 0;
+}
+
+int extract_ssid(unsigned char *call) {
+ // Strip the SSID from the callsign and return it
+ int c, ssid;
+
+ for (c=ssid=0;c<6;c++) {
+ ssid |= (call[c] & 0x80) >> (c+2);
+ call[c] &= 0x7f;
+ }
+
+ return ssid;
+}
+