radiosonde_auto_rx/auto_rx/autorx/emulation.py

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.now(datetime.timezone.utc)
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)