kopia lustrzana https://github.com/projecthorus/radiosonde_auto_rx
206 wiersze
6.1 KiB
Python
206 wiersze
6.1 KiB
Python
#!/usr/bin/env python
|
|
#
|
|
# Auto-RX - Log File Replay
|
|
#
|
|
# Usage:
|
|
# python3 -m autorx.emulation [-h] [-s SPEED] [-p PORT] log
|
|
#
|
|
# positional arguments:
|
|
# log auto_rx log file to replay.
|
|
#
|
|
# optional arguments:
|
|
# -h, --help show this help message and exit
|
|
# -s SPEED, --speed SPEED
|
|
# Replay speedup multiple (Default: 1.0)
|
|
# -p PORT, --port PORT Payload Summary UDP Output Port (Default: 55673)
|
|
#
|
|
# Copyright (C) 2021 Mark Jessop <vk5qi@rfhead.net>
|
|
# Released under GNU GPL v3 or later
|
|
#
|
|
|
|
import argparse
|
|
import json
|
|
import logging
|
|
import socket
|
|
import sys
|
|
import time
|
|
import datetime
|
|
import traceback
|
|
from dateutil.parser import parse
|
|
|
|
|
|
def send_payload_summary(telemetry, port=55673):
|
|
""" Send a payload summary message into the network via UDP broadcast.
|
|
|
|
Args:
|
|
telemetry (dict): Telemetry dictionary to send.
|
|
|
|
"""
|
|
|
|
try:
|
|
# Prepare heading & speed fields, if they are provided in the incoming telemetry blob.
|
|
if "heading" in telemetry.keys():
|
|
_heading = telemetry["heading"]
|
|
else:
|
|
_heading = -1
|
|
|
|
if "vel_h" in telemetry.keys():
|
|
_speed = telemetry["vel_h"] * 3.6
|
|
else:
|
|
_speed = -1
|
|
|
|
# Generate 'short' time field.
|
|
_short_time = telemetry["datetime_dt"].strftime("%H:%M:%S")
|
|
|
|
packet = {
|
|
"type": "PAYLOAD_SUMMARY",
|
|
"station": "Replay",
|
|
"callsign": telemetry["id"],
|
|
"latitude": telemetry["lat"],
|
|
"longitude": telemetry["lon"],
|
|
"altitude": telemetry["alt"],
|
|
"speed": _speed,
|
|
"heading": _heading,
|
|
"time": _short_time,
|
|
"comment": "Replay",
|
|
# Additional fields specifically for radiosondes
|
|
"model": telemetry["type"],
|
|
"freq": telemetry["freq"],
|
|
"temp": telemetry["temp"],
|
|
"frame": telemetry["frame"],
|
|
}
|
|
|
|
# Set up our UDP socket
|
|
_s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
|
_s.settimeout(1)
|
|
# Set up socket for broadcast, and allow re-use of the address
|
|
_s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
|
|
_s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
|
# Under OSX we also need to set SO_REUSEPORT to 1
|
|
try:
|
|
_s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
|
|
except:
|
|
pass
|
|
|
|
try:
|
|
_s.sendto(
|
|
json.dumps(packet).encode("ascii"), ("<broadcast>", port),
|
|
)
|
|
# Catch any socket errors, that may occur when attempting to send to a broadcast address
|
|
# when there is no network connected. In this case, re-try and send to localhost instead.
|
|
except socket.error as e:
|
|
logging.debug(
|
|
"Send to broadcast address failed, sending to localhost instead."
|
|
)
|
|
_s.sendto(
|
|
json.dumps(packet).encode("ascii"), ("127.0.0.1", port),
|
|
)
|
|
|
|
_s.close()
|
|
|
|
except Exception as e:
|
|
logging.debug("Error sending Payload Summary: %s" % str(e))
|
|
|
|
|
|
def emulate_telemetry(filename, port=55673, speed=1.0):
|
|
"""
|
|
Read in a auto_rx log file and emit payload summary messages.
|
|
"""
|
|
|
|
_f = open(filename, "r")
|
|
# Skip header line
|
|
_f.readline()
|
|
|
|
# Use the current time as the start time for our telemetry sentences.
|
|
_first_time = True
|
|
|
|
# Log file format...
|
|
# timestamp,serial,frame,lat,lon,alt,vel_v,vel_h,heading,temp,humidity,pressure,type,freq_mhz,snr,f_error_hz,sats,batt_v,burst_timer,aux_data
|
|
# 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
|
|
# 2020-12-29T23:29:40.000Z,S1050212,2138,-34.96582,138.53122,4496.8,8.4,6.4,157.1,-273.0,-1.0,-1.0,RS41,401.500,23.6,562,8,3.0,-1,-1
|
|
|
|
# Read in first datetime line
|
|
_line = _f.readline()
|
|
_fields = _line.split(",")
|
|
_telemetry_datetime = parse(_fields[0])
|
|
|
|
_current_datetime = datetime.datetime.utcnow()
|
|
|
|
for _line in _f:
|
|
_fields = _line.split(",")
|
|
|
|
_time = parse(_fields[0])
|
|
_id = _fields[1]
|
|
_frame = int(_fields[2])
|
|
_lat = float(_fields[3])
|
|
_lon = float(_fields[4])
|
|
_alt = float(_fields[5])
|
|
_type = _fields[12]
|
|
_freq = _fields[13]
|
|
_temp = float(_fields[9])
|
|
_vel_h = float(_fields[7])
|
|
_heading = float(_fields[8])
|
|
|
|
# Get Delta in time between telemetry lines.
|
|
_time_delta = _time - _telemetry_datetime
|
|
_telemetry_datetime = _time
|
|
|
|
# Calculate our delay before emitting this packet.
|
|
_delay_time = _time_delta.seconds * (1.0 / speed)
|
|
|
|
# Increment the emitted datetime field.
|
|
_current_datetime = _current_datetime + _time_delta
|
|
|
|
# Delay
|
|
print("Sleeping for %.1f seconds." % _delay_time)
|
|
time.sleep(_delay_time)
|
|
|
|
# Format telemetry data into a dictionary.
|
|
_telemetry = {
|
|
"datetime_dt": _current_datetime,
|
|
"id": _id,
|
|
"lat": _lat,
|
|
"lon": _lon,
|
|
"alt": _alt,
|
|
"vel_h": _vel_h,
|
|
"heading": _heading,
|
|
"type": _type,
|
|
"temp": _temp,
|
|
"freq": _freq,
|
|
"frame": _frame,
|
|
}
|
|
|
|
send_payload_summary(_telemetry, port=port)
|
|
|
|
print(_telemetry)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
logging.basicConfig(
|
|
format="%(asctime)s %(levelname)s:%(message)s", level=logging.DEBUG
|
|
)
|
|
|
|
# Command line arguments.
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument(
|
|
"-s",
|
|
"--speed",
|
|
type=float,
|
|
default=1.0,
|
|
help="Replay speedup multiple (Default: 1.0)",
|
|
)
|
|
parser.add_argument(
|
|
"-p",
|
|
"--port",
|
|
type=int,
|
|
default=55673,
|
|
help="Payload Summary UDP Output Port (Default: 55673)",
|
|
)
|
|
parser.add_argument(
|
|
"log", help="auto_rx log file to replay.",
|
|
)
|
|
args = parser.parse_args()
|
|
|
|
emulate_telemetry(args.log, port=args.port, speed=args.speed)
|