pico-tracker/firmware/tools/aprs/extract_backlog.py

142 wiersze
3.9 KiB
Python

"""
This script parses backlog data from the pico tracker.
Data is expected in the form of a series of lines containing
'/A={6 digits} {6 digits}z.{18 chars}{11 chars}\n'. Anything
else will be ignored.
"""
import re
import arrow
from datetime import datetime
from datetime import timedelta
from telemetry_format import *
from math import log, exp
"""
Decodes a 'base 91' encoded string
"""
def base91_decode(enc_str):
enc_ints = [ord(x) - 33 for x in enc_str]
powers = range(len(enc_ints))[::-1]
return sum(x * pow(91, i) for x, i in zip(enc_ints, powers))
"""
Takes a parsed telemetry line and returns a datetime
Assumes data is from the last month, as per the current machine's time
"""
def extract_time(line, length):
# Capture a 6 digit string
p = re.compile(r'(\d{6})z\S{'+str(length)+'}')
match = p.match(line)
if match == None:
return None
else:
# Get a arrow
arw = arrow.get(match.group(1), 'DDHHmm')
utcnow = arrow.utcnow()
# Set dt year/month from current utc
# Need to calculate year/month manually because months have varying number of days
#
if arw.day > utcnow.day: # from last month
last_month = utcnow.month-1
if last_month == 0: # Last December
arw = arw.replace(year=utcnow.year-1, month=12)
else:
arw = arw.replace(year=utcnow.year, month=last_month)
else: # current month
arw = arw.replace(year=utcnow.year, month=utcnow.month)
return arw
"""
Takes a parsed telemetry line and returns latitude, longitude and
altitude. It decodes from base 91 along the way
"""
def extract_lat_long_alt(line, telem_length):
# Capture a 4 char encoded latitude
p = re.compile(r'\d{6}z(\S{4})(\S{4})(\S{2})\S{'+str(telem_length)+'}')
match = p.match(line)
if match == None:
return None
else:
enc_lat, enc_long, enc_alt = match.groups()
# Lat/long in fractional degrees, alt in metres
latitude = 90.0 - (base91_decode(enc_lat) / 380926.0)
longitude = -180.0 + (base91_decode(enc_long) / 190463.0)
altitude = exp(log(1.002) * base91_decode(enc_alt)) / 3.2808
return (latitude, longitude, altitude)
"""
Takes a telemetry line and returns telemetry readings
It decodes from base91 along the way
"""
def extract_telemetry(line, tf, telem_length):
# Capture an encoded telemetry segment
p = re.compile(r'\d{6}z\S{10}(\S{'+str(telem_length)+'})')
match = p.match(line)
if match == None:
return None
else:
tel = match.group(1)
# Split into 2 char chunks
parts = [tel[i:i+2] for i in range(0, telem_length, 2)]
# Extract values from base 91
values = [base91_decode(enc) for enc in tuple(parts)]
return tf.decode_values(values)
"""
Exracts the 'raw data' segment from a line of data; this is the
section after \d{6}z
"""
def extract_raw_data(line, length):
# Capture the raw data segment
p = re.compile(r'(\d{6}z\S{'+str(length)+'})\|')
match = p.search(line)
if match == None:
return None
else:
return match.group(1)
"""
Returns a datum for the backlog in an APRS frame
"""
def extract_backlog_datum(frame, tf):
# telemetry length
telem_len = tf.base91_encoded_len()
# Extract raw data string
raw = extract_raw_data(frame, 10+telem_len)
if raw == None:
return None
else:
telem = extract_telemetry(raw, tf, telem_len)
data = {
'time': extract_time(raw, 10+telem_len),
'coords': extract_lat_long_alt(raw, telem_len),
}
data.update(telem)
return data
"""
Prints a datum
"""
def print_datum(datum, tf):
print "{}: {:.6f} {:.6f}, {}m {}".format(
str(datum['time']),
datum['coords'][0], datum['coords'][1], int(round(datum['coords'][2])),
tf.stringify(datum))