Added Habitat Telemetry Upload
authordarksidelemm <darksidelemm@01035d8c-6547-0410-b346-abe4f91aad63>
Fri, 1 Jan 2016 04:48:00 +0000 (04:48 +0000)
committerdarksidelemm <darksidelemm@01035d8c-6547-0410-b346-abe4f91aad63>
Fri, 1 Jan 2016 04:48:00 +0000 (04:48 +0000)
git-svn-id: https://svn.code.sf.net/p/freetel/code@2600 01035d8c-6547-0410-b346-abe4f91aad63

codec2-dev/octave/fsk_horus_stream.m
codec2-dev/octave/telem_upload.py [new file with mode: 0644]

index 968331acf8236283801930fe8020bab834cdc7de..9f63b5a939e2f0a2f3467dcc7a615e5368ba39a6 100755 (executable)
@@ -9,6 +9,10 @@
 % usage:
 %  $ chmod 777 fsk_horus_stream.m
 %  $ rec -t raw -r 8000 -s -2 -c 1 - -q | ./fsk_horus_stream.m
+%  or (for those of us that avoid alsa like the plague)
+%  $ arecord -D pulse -r 8000 -c 1 -f S16_LE - | ./fsk_horus_stream.m
+%  and use the 'pavucontrol' utility to select a sound device for arecord.
+%
 %
 % OR to test with a stored file (8kHz 16-bit shorts):
 %  $ cat ~/Desktop/vk5arg-3.wav | ./fsk_horus_stream.m
@@ -22,6 +26,11 @@ fsk_horus;
 gps_log = "~/Desktop/gps_log.txt"
 system_command = "echo -n \"/home/david/Desktop/gps_log.txt\" | nc -u -q1 127.0.0.1 21234";
 
+% Upload Telemetry to Habitat (http://tracker.habhub.org/)
+telem_upload_enabled = false;
+% Update this command with your own callsign.
+telem_upload_command = "python telem_upload.py -c N0CALL_Octave";
+
 more off;
 states = fsk_horus_init(8000, 100);
 %states = fsk_horus_init_rtty_uw(states);
@@ -83,6 +92,13 @@ while c
       end
       
       if crc_ok
+        if telem_upload_enabled
+          % Upload to Habitat.
+          ascii_upload_cmd = sprintf("%s %s",telem_upload_command,str);
+          printf("Uploading ASCII to Habitat...\n");
+          system(ascii_upload_cmd,false,"async");
+        end
+
         strok = sprintf("%s CRC OK", str);
       else
         strok = sprintf("%s CRC BAD", str);
@@ -142,7 +158,14 @@ while c
       % compile with:
       %   codec2-dev/src$ gcc horus_l2.c -o horus_l2 -Wall -DDEC_RX_BITS -DHORUS_L2_RX
 
-      system("../src/horus_l2"); 
+      system("../src/horus_l2");
+      if telem_upload_enabled
+        % Upload binary payload data to Habitat.
+        binary_upload_addition = "`cat horus_rx_bits_hex.txt`";
+        binary_upload_cmd = sprintf("%s %s",telem_upload_command,binary_upload_addition);
+        printf("Uploading Binary to Habitat...\n");
+        system(binary_upload_cmd,type="async");
+      end
 
       % throw out used bits in buffer.  We're not sure where the next packet starts
       % so lets remove everything up to just after the UW we just used to force
diff --git a/codec2-dev/octave/telem_upload.py b/codec2-dev/octave/telem_upload.py
new file mode 100644 (file)
index 0000000..e368b42
--- /dev/null
@@ -0,0 +1,157 @@
+#!/usr/bin/env python
+#
+#   Horus Binary (and oldschool) Telemetry Uploader
+#
+#   Mark Jessop 2015-12-31
+#   <vk5qi@rfhead.net>
+#
+#   This script takes either a hex representation of the binary payload, or 
+#   a 'classic' ASCII sentence, and uploads it to Habitat.
+#
+#   Currently this script tells the two apart by looking for 'HORUS' at the start
+#   of the argument to determine if it's an ASCII sentence.
+#   
+#   It's designed to be called from fsk_horus_stream.m, and is tailored for it's output.
+#
+#   Dependencies:
+#   - Python 2.7 (Will probably break in Python 3)
+#   - crcmod (pip install crcmod)
+#
+
+import time, struct, json, socket, httplib, crcmod, argparse, sys
+from base64 import b64encode
+from hashlib import sha256
+from datetime import datetime
+
+def crc16_ccitt(data):
+    """
+    Calculate the CRC16 CCITT checksum of *data*.
+    
+    (CRC16 CCITT: start 0xFFFF, poly 0x1021)
+    """
+    crc16 = crcmod.predefined.mkCrcFun('crc-ccitt-false')
+    return crc16(data)
+
+# Binary packet format, from https://github.com/darksidelemm/PicoHorusBinary/tree/master/PicoPayloadGPS
+# struct TBinaryPacket
+# {
+# uint8_t       PayloadID;
+# uint16_t  Counter;
+# uint8_t       Hours;
+# uint8_t       Minutes;
+# uint8_t       Seconds;
+# float     Latitude;
+# float     Longitude;
+# uint16_t      Altitude;
+# uint8_t   Speed; // Speed in Knots (1-255 knots)
+# uint8_t   Sats;
+# int8_t   Temp; // Twos Complement Temp value.
+# uint8_t   BattVoltage; // 0 = 0.5v, 255 = 2.0V, linear steps in-between.
+# uint16_t Checksum; // CRC16-CCITT Checksum.
+# };  //  __attribute__ ((packed));
+
+def decode_horus_binary_telemetry(payload):
+
+    horus_format_struct = "<BHBBBffHBBbBH"
+    try:
+        unpacked = struct.unpack(horus_format_struct, payload)
+    except:
+        print "Wrong string length. Packet contents:"
+        print ":".join("{:02x}".format(ord(c)) for c in payload)
+        sys.exit(1)
+
+    telemetry = {}
+    telemetry['payload_id'] = unpacked[0]
+    telemetry['counter'] = unpacked[1]
+    telemetry['time'] = "%02d:%02d:%02d" % (unpacked[2],unpacked[3],unpacked[4])
+    telemetry['latitude'] = unpacked[5]
+    telemetry['longitude'] = unpacked[6]
+    telemetry['altitude'] = unpacked[7]
+    telemetry['speed'] = unpacked[8]
+    telemetry['sats'] = unpacked[9]
+    telemetry['temp'] = unpacked[10]
+    telemetry['batt_voltage_raw'] = unpacked[11]
+    telemetry['checksum'] = unpacked[12]
+
+    # Convert some of the fields into more useful units.
+    telemetry['batt_voltage'] = 0.5 + 1.5*telemetry['batt_voltage_raw']/255.0
+
+    return telemetry
+
+# Compatible with Habitat Payload ID 55f39c02e518bdd2885c8aac7d1cdd7c
+def telemetry_to_sentence(telemetry):
+    sentence = "$$PICOHORUSBINARY,%d,%s,%.5f,%.5f,%d,%d,%d,%d,%.2f" % (telemetry['counter'],telemetry['time'],telemetry['latitude'],
+        telemetry['longitude'],telemetry['altitude'],telemetry['speed'],telemetry['sats'],telemetry['temp'],telemetry['batt_voltage'])
+
+    checksum = hex(crc16_ccitt(sentence[2:]))[2:].upper().zfill(4)
+    output = sentence + "*" + checksum + "\n"
+    return output
+
+# Habitat Upload Functions
+def habitat_upload_sentence(sentence, callsign="N0CALL"):
+
+    sentence_b64 = b64encode(sentence)
+
+    date = datetime.utcnow().isoformat("T") + "Z"
+
+    data = {
+        "type": "payload_telemetry",
+        "data": {
+            "_raw": sentence_b64
+            },
+        "receivers": {
+            callsign: {
+                "time_created": date,
+                "time_uploaded": date,
+                },
+            },
+    }
+    try:
+        c = httplib.HTTPConnection("habitat.habhub.org",timeout=4)
+        c.request(
+            "PUT",
+            "/habitat/_design/payload_telemetry/_update/add_listener/%s" % sha256(sentence_b64).hexdigest(),
+            json.dumps(data),  # BODY
+            {"Content-Type": "application/json"}  # HEADERS
+            )
+
+        response = c.getresponse()
+        sys.exit(0)
+    except Exception as e:
+        print("Failed to upload to Habitat.")
+        sys.exit(1)
+
+parser = argparse.ArgumentParser()
+parser.add_argument("raw_data", help="Raw Data, either hex binary data, or ASCII payload string.")
+parser.add_argument("-c","--callsign",default="N0CALL",help="Habitat Upload Callsign")
+args = parser.parse_args()
+
+
+uploader_callsign = args.callsign
+raw_data = args.raw_data
+print(raw_data)
+
+if raw_data.startswith("HORUS"):
+    # Assume the data is a standard telemetry string and just upload it.
+    # Append a Newline and checksum (if not alread there) to the end, and "$$"'s to the start.
+    if not '*' in raw_data:
+        # Assume there is no checksum on the end of the string, and add one.
+        checksum = hex(crc16_ccitt(raw_data))[2:].upper().zfill(4)
+        raw_data = raw_data + "*" + checksum
+    if raw_data[:-1] != '\n':
+        raw_data = "$$" + raw_data + '\n'
+
+    habitat_upload_sentence(raw_data, callsign = uploader_callsign)
+else:
+    # Attempt to decode some hex data.
+    data = raw_data.decode("hex")
+    telem = decode_horus_binary_telemetry(data)
+    # Only convert and upload if checksum passes.
+    if(crc16_ccitt(data[:-2]) != telem['checksum']):
+        print("Checksum Failed!")
+        sys.exit(1)
+
+    sentence = telemetry_to_sentence(telem)
+    print("Uploading: %s"%(sentence))
+    habitat_upload_sentence(sentence, callsign = uploader_callsign)
+