kopia lustrzana https://github.com/projecthorus/wenet
Add more data into GPS packets, and upload this to sondehub-amateur
rodzic
b94ffcbcf5
commit
95ee72cd20
|
@ -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
|
||||
|
|
|
@ -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))
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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):
|
||||
"""
|
||||
|
|
Ładowanie…
Reference in New Issue