Add more data into GPS packets, and upload this to sondehub-amateur

rfm98w_packettx
Mark Jessop 2024-09-27 16:05:39 +09:30
rodzic b94ffcbcf5
commit 95ee72cd20
5 zmienionych plików z 114 dodań i 21 usunięć

Wyświetl plik

@ -36,9 +36,9 @@ class WENET_PACKET_TYPES:
class WENET_PACKET_LENGTHS:
GPS_TELEMETRY = 35
ORIENTATION_TELEMETRY = 43
IMAGE_TELEMETRY = 80
GPS_TELEMETRY = 61
ORIENTATION_TELEMETRY = 43
IMAGE_TELEMETRY = 80
def decode_packet_type(packet):
@ -178,7 +178,8 @@ def gps_telemetry_decoder(packet):
""" Extract GPS telemetry data from a packet, and return it as a dictionary.
Keyword Arguments:
packet: A GPS telemetry packet, as per https://docs.google.com/document/d/12230J1X3r2-IcLVLkeaVmIXqFeo3uheurFakElIaPVo/edit?usp=sharing
packet: A GPS telemetry packet, as per
https://github.com/projecthorus/wenet/wiki/Modem-&-Packet-Format-Details#0x01---gps-telemetry
This can be provided as either a string, or a list of integers, which will be converted
to a string prior to decoding.
@ -207,7 +208,7 @@ def gps_telemetry_decoder(packet):
# Wrap the next bit in exception handling.
try:
# Unpack the packet into a list.
data = struct.unpack('>BHIBffffffBBB', packet)
data = struct.unpack(">BHIBffffffBBBffHffff", packet)
gps_data['week'] = data[1]
gps_data['iTOW'] = data[2]/1000.0 # iTOW provided as milliseconds, convert to seconds.
@ -221,6 +222,27 @@ def gps_telemetry_decoder(packet):
gps_data['numSV'] = data[10]
gps_data['gpsFix'] = data[11]
gps_data['dynamic_model'] = data[12]
# New fields 2024-09
gps_data['radio_temp'] = round(data[13],1)
gps_data['cpu_temp'] = round(data[14],1)
gps_data['cpu_speed'] = data[15]
gps_data['load_avg_1'] = round(data[16],3)
gps_data['load_avg_5'] = round(data[17],3)
gps_data['load_avg_15'] = round(data[18],3)
gps_data['disk_percent'] = round(data[19],3)
# Check to see if we actually have real data in these new fields.
# If its an old transmitter, it will have 0x55 in these spots, which we can detect
if gps_data['cpu_speed'] == 21845:
# 0x5555 -> 21825, which we use as an indication that padding is in use.
# Set all the new fields to invalid values
gps_data['radio_temp'] = -999.0
gps_data['cpu_temp'] = -999.0
gps_data['cpu_speed'] = 0
gps_data['load_avg_1'] = 0
gps_data['load_avg_5'] = 0
gps_data['load_avg_15'] = 0
gps_data['disk_percent'] = -1.0
# Perform some post-processing on the data, to make some of the fields easier to read.
@ -281,7 +303,7 @@ def gps_telemetry_string(packet):
if gps_data['error'] != 'None':
return "GPS: ERROR Could not decode."
else:
gps_data_string = "GPS: %s Lat/Lon: %.5f,%.5f Alt: %dm, Speed: H %dkph V %.1fm/s, Heading: %d deg, Fix: %s, SVs: %d, Model: %s " % (
gps_data_string = "GPS: %s Lat/Lon: %.5f,%.5f Alt: %dm, Speed: H %dkph V %.1fm/s, Heading: %d deg, Fix: %s, SVs: %d, DynModel: %s, Radio Temp: %.1f, CPU Temp: %.1f, CPU Speed: %d, Load Avg: %.2f, %.2f, %.2f, Disk Usage: %.1f%%" % (
gps_data['timestamp'],
gps_data['latitude'],
gps_data['longitude'],
@ -291,7 +313,14 @@ def gps_telemetry_string(packet):
int(gps_data['heading']),
gps_data['gpsFix_str'],
gps_data['numSV'],
gps_data['dynamic_model_str']
gps_data['dynamic_model_str'],
gps_data['radio_temp'],
gps_data['cpu_temp'],
gps_data['cpu_speed'],
gps_data['load_avg_1'],
gps_data['load_avg_5'],
gps_data['load_avg_15'],
gps_data['disk_percent']
)
return gps_data_string

Wyświetl plik

@ -212,7 +212,7 @@ while True:
# Only proceed if there are no decode errors.
if packet_info['error'] != 'None':
logging.error(message['error'])
logging.error(packet_info['error'])
continue
if (packet_info['image_id'] != current_image) or (packet_info['callsign'] != current_callsign) :
@ -264,4 +264,5 @@ while True:
logging.debug("Unknown Packet Format.")
except Exception as e:
logging.exception(e)
logging.error("Error handling packet - " + str(e))

Wyświetl plik

@ -133,6 +133,25 @@ def handle_gps_telemetry(gps_data):
if sondehub:
# Add to the SondeHub-Amateur uploader!
_extra_fields = {
'ascent_rate': round(gps_data['ascent_rate'],1),
'speed': round(gps_data['ground_speed'],1)
}
# Add in new fields from 2024-09 if they exist and are valid
if 'radio_temp' in gps_data:
if gps_data['radio_temp'] > -999.0:
_extra_fields['radio_temp'] = gps_data['radio_temp']
if gps_data['cpu_temp'] > -999.0:
_extra_fields['cpu_temp'] = gps_data['cpu_temp']
_extra_fields['cpu_speed'] = gps_data['cpu_speed']
_extra_fields['load_avg_1'] = gps_data['load_avg_1']
_extra_fields['load_avg_5'] = gps_data['load_avg_5']
_extra_fields['load_avg_15'] = gps_data['load_avg_15']
_extra_fields['disk_percent'] = gps_data['disk_percent']
sondehub.add_telemetry(
current_callsign + "-Wenet",
gps_data['timestamp'] + "Z",
@ -141,10 +160,7 @@ def handle_gps_telemetry(gps_data):
round(gps_data['altitude'],1),
sats = gps_data['numSV'],
heading = round(gps_data['heading'],1),
extra_fields = {
'ascent_rate': round(gps_data['ascent_rate'],1),
'speed': round(gps_data['ground_speed'],1)
},
extra_fields = _extra_fields,
modulation = "Wenet",
frequency = round(current_modem_stats['fcentre']/1e6, 5),
snr = round(current_modem_stats['snr'],1)
@ -317,7 +333,7 @@ if __name__ == "__main__":
parser.add_argument("callsign", help="SondeHub-Amateur Uploader Callsign")
parser.add_argument("-l", "--listen_port", default=5003, help="Port to run Web Server on. (Default: 5003)")
parser.add_argument("-v", "--verbose", action='store_true', help="Enable debug output.")
parser.add_argument("--no_sondehub", action='store_true', help="Disable SondeHub-Amateur position upload.")
parser.add_argument("--no_sondehub", default=False, action='store_true', help="Disable SondeHub-Amateur position upload.")
parser.add_argument("-u", "--udp_port", default=None, type=int, help="Port to emit Horus UDP packets on. (Default: 0 (disabled), Typical: 55673)")
args = parser.parse_args()

Wyświetl plik

@ -21,8 +21,10 @@ import os
import datetime
import crcmod
import json
import shutil
import socket
import struct
import subprocess
import traceback
from time import sleep
from threading import Thread
@ -256,21 +258,39 @@ class PacketTX(object):
def transmit_gps_telemetry(self, gps_data):
""" Generate and Transmit a GPS Telemetry Packet.
Host platform CPU speed, temperature and load averages are collected and included in this packet too.
Keyword Arguments:
gps_data: A dictionary, as produced by the UBloxGPS class. It must have the following fields:
latitude, longitude, altitude, ground_speed, ascent_rate, heading, gpsFix, numSV,
week, iTOW, leapS, dynamic_model.
The generated packet format is in accordance with the specification in:
https://docs.google.com/document/d/12230J1X3r2-IcLVLkeaVmIXqFeo3uheurFakElIaPVo/edit?usp=sharing
https://github.com/projecthorus/wenet/wiki/Modem-&-Packet-Format-Details#0x01---gps-telemetry
The corresponding decoder for this packet format is within rx/WenetPackets.py, in the function
gps_telemetry_decoder
"""
# Collect non-GPS information to add to the packet.
_radio_temp = self.radio.temperature
_cpu_speed = self.get_cpu_speed()
_cpu_temp = self.get_cpu_temperature()
_load_avg_1, _load_avg_5, _load_avg_15 = os.getloadavg()
# Collect disk usage information
# Unsure of the likelyhood of this failing, but wrapping it in a try/except anyway
try:
gps_packet = struct.pack(">BHIBffffffBBB",
_disk_usage = shutil.disk_usage(".")
_disk_percent = 100.0 * (_disk_usage.used / _disk_usage.total)
except:
_disk_percent = -1.0
# Construct the packet
try:
gps_packet = struct.pack(">BHIBffffffBBBffHffff",
1, # Packet ID for the GPS Telemetry Packet.
gps_data['week'],
int(gps_data['iTOW']*1000), # Convert the GPS week value to milliseconds, and cast to an int.
@ -283,7 +303,15 @@ class PacketTX(object):
gps_data['ascent_rate'],
gps_data['numSV'],
gps_data['gpsFix'],
gps_data['dynamic_model']
gps_data['dynamic_model'],
# New fields 2024-09
_radio_temp,
_cpu_temp,
int(_cpu_speed),
_load_avg_1,
_load_avg_5,
_load_avg_15,
_disk_percent
)
self.queue_telemetry_packet(gps_packet)
@ -423,6 +451,25 @@ class PacketTX(object):
self.queue_telemetry_packet(_packet, repeats=repeats)
def get_cpu_temperature(self):
""" Grab the temperature of the RPi CPU """
try:
data = subprocess.check_output("/usr/bin/vcgencmd measure_temp", shell=True)
temp = data.decode().split('=')[1].split('\'')[0]
return float(temp)
except Exception as e:
print("Error reading temperature - %s" % str(e))
return -999.0
def get_cpu_speed(self):
""" Get the current CPU Frequency """
try:
data = subprocess.check_output("cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq", shell=True)
freq = int(data.decode().strip())/1000
return freq
except Exception as e:
print("Error reading CPU Freq - %s" % str(e))
return 9999
#
# UDP messaging functions.

Wyświetl plik

@ -90,7 +90,7 @@ class RFM98W_Serial(object):
self.lora.set_register(0x31,0x00) # Set Continuous Transmit Mode
# Get the IC temperature
self.temperature = self.get_temperature()
self.get_temperature()
self.lora.set_freq(self.frequency)
logging.info(f"RFM98W - Frequency set to: {self.frequency} MHz.")
@ -213,12 +213,12 @@ class RFM98W_Serial(object):
Get radio module temperature (uncalibrated)
"""
# Make temperature measurement
temperature = self.lora.get_register(0x3c) * (-1)
if temperature < -63:
temperature += 255
self.temperature = self.lora.get_register(0x3c) * (-1)
if self.temperature < -63:
self.temperature += 255
logging.info(f"RFM98W - Temperature: {self.temperature} C")
return temperature
return self.temperature
class SerialOnly(object):
"""