kopia lustrzana https://github.com/bristol-seds/pico-tracker
142 wiersze
3.9 KiB
Python
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))
|