Added habitat upload support for imagery packets. Telemetry GUI loads by default with start_rx.sh

pull/1/head
Mark Jessop 2017-01-07 20:00:35 +10:30
rodzic 43f6f57455
commit 11fc3d73d0
4 zmienionych plików z 200 dodań i 152 usunięć

Wyświetl plik

@ -10,6 +10,7 @@ from threading import Thread
import traceback import traceback
import socket import socket
import json import json
import sys
import Queue import Queue
import datetime import datetime
from WenetPackets import * from WenetPackets import *
@ -20,6 +21,11 @@ import numpy as np
imu_plot_history_size = 60 # Seconds. imu_plot_history_size = 60 # Seconds.
user_callsign = "N0CALL"
if len(sys.argv) > 1:
user_callsign = sys.argv[1]
app = QtGui.QApplication([]) app = QtGui.QApplication([])
# Configure PyQtGraph to use black plots on a transparent background. # Configure PyQtGraph to use black plots on a transparent background.
@ -73,19 +79,19 @@ gpsFrameLayout.addWidget(gpsFixValidValue,8,1)
gpsFrame.setLayout(gpsFrameLayout) gpsFrame.setLayout(gpsFrameLayout)
def updateGpsFrame(gps_data): def updateGpsFrame(gps_data):
""" Update GPS Data Frame with a WenetPackets GPS Data dictionary """ """ Update GPS Data Frame with a WenetPackets GPS Data dictionary """
try: try:
gpsTimeValue.setText(gps_data['timestamp']) gpsTimeValue.setText(gps_data['timestamp'])
gpsLatitudeValue.setText("%.5f" % gps_data['latitude']) gpsLatitudeValue.setText("%.5f" % gps_data['latitude'])
gpsLongitudeValue.setText("%.5f" % gps_data['longitude']) gpsLongitudeValue.setText("%.5f" % gps_data['longitude'])
gpsAltitudeValue.setText("%d m" % int(gps_data['altitude'])) gpsAltitudeValue.setText("%d m" % int(gps_data['altitude']))
gpsSpeedValue.setText("H %d kph V %.1f m/s" % ( int(gps_data['ground_speed']), gps_data['ascent_rate'])) gpsSpeedValue.setText("H %d kph V %.1f m/s" % ( int(gps_data['ground_speed']), gps_data['ascent_rate']))
gpsHeadingValue.setText("%.1f deg" % gps_data['heading']) gpsHeadingValue.setText("%.1f deg" % gps_data['heading'])
gpsSatsValue.setText("%d" % gps_data['numSV']) gpsSatsValue.setText("%d" % gps_data['numSV'])
gpsFixValidValue.setText(gps_data['gpsFix_str']) gpsFixValidValue.setText(gps_data['gpsFix_str'])
except: except:
traceback.print_exc() traceback.print_exc()
pass pass
# #
# IMU Telemetry Data Frame # IMU Telemetry Data Frame
@ -174,57 +180,57 @@ roll_curve = imuPlot_Roll.plot(x=imu_times, y=roll_values, pen=pg.mkPen('k',widt
pitch_curve = imuPlot_Pitch.plot(x=imu_times, y=pitch_values, pen=pg.mkPen('k',width=2)) pitch_curve = imuPlot_Pitch.plot(x=imu_times, y=pitch_values, pen=pg.mkPen('k',width=2))
def updateIMUFrame(imu_data): def updateIMUFrame(imu_data):
""" Update IMU Frame and Plots with new IMU data """ """ Update IMU Frame and Plots with new IMU data """
global imu_times, heading_values, roll_values, pitch_values, heading_curve, roll_curve, pitch_curve global imu_times, heading_values, roll_values, pitch_values, heading_curve, roll_curve, pitch_curve
try: try:
imuTimeValue.setText(imu_data['timestamp']) imuTimeValue.setText(imu_data['timestamp'])
imuTempValue.setText("%d" % imu_data['temp']) imuTempValue.setText("%d" % imu_data['temp'])
imuCalStatus_System_OK.setText("%d" % imu_data['sys_cal']) imuCalStatus_System_OK.setText("%d" % imu_data['sys_cal'])
imuCalStatus_Gyro_OK.setText("%d" % imu_data['gyro_cal']) imuCalStatus_Gyro_OK.setText("%d" % imu_data['gyro_cal'])
imuCalStatus_Accel_OK.setText("%d" % imu_data['accel_cal']) imuCalStatus_Accel_OK.setText("%d" % imu_data['accel_cal'])
imuCalStatus_Magnet_OK.setText("%d" % imu_data['magnet_cal']) imuCalStatus_Magnet_OK.setText("%d" % imu_data['magnet_cal'])
imuEulerHeadingValue.setText("%.1f" % imu_data['euler_heading']) imuEulerHeadingValue.setText("%.1f" % imu_data['euler_heading'])
imuEulerRollValue.setText("%.1f" % imu_data['euler_roll']) imuEulerRollValue.setText("%.1f" % imu_data['euler_roll'])
imuEulerPitchValue.setText("%.1f" % imu_data['euler_pitch']) imuEulerPitchValue.setText("%.1f" % imu_data['euler_pitch'])
latest_imu_time = imu_data['iTOW'] latest_imu_time = imu_data['iTOW']
latest_euler_heading = imu_data['euler_heading'] latest_euler_heading = imu_data['euler_heading']
latest_euler_roll = imu_data['euler_roll'] latest_euler_roll = imu_data['euler_roll']
latest_euler_pitch = imu_data['euler_pitch'] latest_euler_pitch = imu_data['euler_pitch']
if imu_times[0] == 0: if imu_times[0] == 0:
# We need to initialise the arrays. # We need to initialise the arrays.
imu_times[0] = latest_imu_time imu_times[0] = latest_imu_time
heading_values[0] = latest_euler_heading heading_values[0] = latest_euler_heading
roll_values[0] = latest_euler_roll roll_values[0] = latest_euler_roll
pitch_values[0] = latest_euler_pitch pitch_values[0] = latest_euler_pitch
else: else:
# Append, and crop arrays if they are too big. # Append, and crop arrays if they are too big.
imu_times.append(latest_imu_time) imu_times.append(latest_imu_time)
heading_values.append(latest_euler_heading) heading_values.append(latest_euler_heading)
roll_values.append(latest_euler_roll) roll_values.append(latest_euler_roll)
pitch_values.append(latest_euler_pitch) pitch_values.append(latest_euler_pitch)
if len(imu_times) > buffer_size: if len(imu_times) > buffer_size:
imu_times = imu_times[-1*buffer_size:] imu_times = imu_times[-1*buffer_size:]
heading_values = heading_values[-1*buffer_size:] heading_values = heading_values[-1*buffer_size:]
roll_values = roll_values[-1*buffer_size:] roll_values = roll_values[-1*buffer_size:]
pitch_values = pitch_values[-1*buffer_size:] pitch_values = pitch_values[-1*buffer_size:]
# Update plots # Update plots
heading_curve.setData(x=(np.array(imu_times)-latest_imu_time), y=heading_values) heading_curve.setData(x=(np.array(imu_times)-latest_imu_time), y=heading_values)
roll_curve.setData(x=(np.array(imu_times)-latest_imu_time), y = roll_values) roll_curve.setData(x=(np.array(imu_times)-latest_imu_time), y = roll_values)
pitch_curve.setData(x=(np.array(imu_times)-latest_imu_time), y=pitch_values) pitch_curve.setData(x=(np.array(imu_times)-latest_imu_time), y=pitch_values)
except: except:
traceback.print_exc() traceback.print_exc()
pass pass
# Telemetry Log # Telemetry Log
packetSnifferFrame = QtGui.QFrame() packetSnifferFrame = QtGui.QFrame()
packetSnifferFrame.setFixedSize(1300,150) packetSnifferFrame.setFixedSize(1200,150)
packetSnifferFrame.setFrameStyle(QtGui.QFrame.Box) packetSnifferFrame.setFrameStyle(QtGui.QFrame.Box)
packetSnifferTitle = QtGui.QLabel("<b><u>Telemetry Log</u></b>") packetSnifferTitle = QtGui.QLabel("<b><u>Telemetry Log</u></b>")
console = QtGui.QPlainTextEdit() console = QtGui.QPlainTextEdit()
@ -234,6 +240,40 @@ packetSnifferLayout.addWidget(packetSnifferTitle)
packetSnifferLayout.addWidget(console) packetSnifferLayout.addWidget(console)
packetSnifferFrame.setLayout(packetSnifferLayout) packetSnifferFrame.setLayout(packetSnifferLayout)
# Habitat Upload Frame
uploadFrame = QtGui.QFrame()
uploadFrame.setFixedSize(200,150)
uploadFrame.setFrameStyle(QtGui.QFrame.Box)
uploadFrame.setLineWidth(1)
uploadFrameTitle = QtGui.QLabel("<b><u>Habitat Upload</u></b>")
uploadFrameHabitat = QtGui.QCheckBox("Habitat Upload")
uploadFrameHabitat.setChecked(True)
uploadFrameCallsignLabel = QtGui.QLabel("<b>Your Callsign:</b>")
uploadFrameCallsign = QtGui.QLineEdit(user_callsign)
uploadFrameCallsign.setMaxLength(10)
uploadFrameHabitatStatus = QtGui.QLabel("Last Upload: ")
uploadFrameLayout = QtGui.QGridLayout()
uploadFrameLayout.addWidget(uploadFrameTitle,0,0,1,1)
uploadFrameLayout.addWidget(uploadFrameCallsignLabel,1,0,1,1)
uploadFrameLayout.addWidget(uploadFrameCallsign,1,1,1,1)
uploadFrameLayout.addWidget(uploadFrameHabitat,2,0,1,2)
uploadFrameLayout.addWidget(uploadFrameHabitatStatus,3,0,1,2)
uploadFrame.setLayout(uploadFrameLayout)
def imageTelemetryHandler(packet):
(upload_ok, error) = image_telemetry_upload(packet, user_callsign = str(uploadFrameCallsign.text()))
timestamp = datetime.datetime.utcnow().strftime("%Y%m%d-%H%M%SZ")
if upload_ok:
uploadFrameHabitatStatus.setText("Last Upload: %s" % datetime.utcnow().strftime("%H:%M:%S"))
console.appendPlainText("%s \tHabitat Upload: OK")
else:
uploadFrameHabitatStatus.setText("Last Upload: Failed!")
console.appendPlainText("%s \tHabitat Upload: FAIL: %s" % (timestamp, error))
# Main Window # Main Window
main_widget = QtGui.QWidget() main_widget = QtGui.QWidget()
@ -242,8 +282,10 @@ main_widget.setLayout(layout)
layout.addWidget(gpsFrame,0,0) layout.addWidget(gpsFrame,0,0)
layout.addWidget(imuFrame,0,1) layout.addWidget(imuFrame,0,1)
layout.addWidget(imuPlot,0,2) layout.addWidget(imuPlot,0,2,1,2)
layout.addWidget(packetSnifferFrame,1,0,1,4) layout.addWidget(packetSnifferFrame,1,0,1,3)
layout.addWidget(uploadFrame,1,3,1,1)
mainwin = QtGui.QMainWindow() mainwin = QtGui.QMainWindow()
mainwin.setWindowTitle("Wenet GPS/IMU Telemetry Console") mainwin.setWindowTitle("Wenet GPS/IMU Telemetry Console")
@ -258,69 +300,73 @@ mainwin.show()
rxqueue = Queue.Queue(32) rxqueue = Queue.Queue(32)
def process_udp(udp_packet): def process_udp(udp_packet):
""" Process received UDP packets. """ """ Process received UDP packets. """
# Grab timestamp. # Grab timestamp.
timestamp = datetime.datetime.utcnow().strftime("%Y%m%d-%H%M%SZ") timestamp = datetime.datetime.utcnow().strftime("%Y%m%d-%H%M%SZ")
# Parse JSON, and extract packet. # Parse JSON, and extract packet.
packet_dict = json.loads(udp_packet) packet_dict = json.loads(udp_packet)
packet = packet_dict['packet'] packet = packet_dict['packet']
# Decode GPS and IMU packets, and pass onto their respective GUI update functions. # Decode GPS and IMU packets, and pass onto their respective GUI update functions.
packet_type = decode_packet_type(packet) packet_type = decode_packet_type(packet)
if packet_type == WENET_PACKET_TYPES.GPS_TELEMETRY: if packet_type == WENET_PACKET_TYPES.GPS_TELEMETRY:
gps_data = gps_telemetry_decoder(packet) gps_data = gps_telemetry_decoder(packet)
updateGpsFrame(gps_data) updateGpsFrame(gps_data)
elif packet_type == WENET_PACKET_TYPES.ORIENTATION_TELEMETRY: elif packet_type == WENET_PACKET_TYPES.ORIENTATION_TELEMETRY:
orientation_data = orientation_telemetry_decoder(packet) orientation_data = orientation_telemetry_decoder(packet)
updateIMUFrame(orientation_data) updateIMUFrame(orientation_data)
else: elif packet_type == WENET_PACKET_TYPES.IMAGE_TELEMETRY:
# Convert to string, and print to terminal with timestamp. # Print to console, then attempt to upload packet.
console.appendPlainText("%s \t%s" % (timestamp,packet_to_string(packet))) console.appendPlainText("%s \t%s" % (timestamp,packet_to_string(packet)))
imageTelemetryHandler(packet)
else:
# Convert to string, and print to terminal with timestamp.
console.appendPlainText("%s \t%s" % (timestamp,packet_to_string(packet)))
udp_listener_running = False udp_listener_running = False
def udp_rx_thread(): def udp_rx_thread():
""" Listen on a port for UDP broadcast packets, and pass them onto process_udp()""" """ Listen on a port for UDP broadcast packets, and pass them onto process_udp()"""
global udp_listener_running global udp_listener_running
s = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) s = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
s.settimeout(1) s.settimeout(1)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
try: try:
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1) s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
except: except:
pass pass
s.bind(('',WENET_TELEMETRY_UDP_PORT)) s.bind(('',WENET_TELEMETRY_UDP_PORT))
print("Started UDP Listener Thread.") print("Started UDP Listener Thread.")
udp_listener_running = True udp_listener_running = True
while udp_listener_running: while udp_listener_running:
try: try:
m = s.recvfrom(2048) m = s.recvfrom(2048)
except socket.timeout: except socket.timeout:
m = None m = None
if m != None: if m != None:
try: try:
rxqueue.put_nowait(m[0]) rxqueue.put_nowait(m[0])
except: except:
traceback.print_exc() traceback.print_exc()
pass pass
print("Closing UDP Listener") print("Closing UDP Listener")
s.close() s.close()
t = Thread(target=udp_rx_thread) t = Thread(target=udp_rx_thread)
t.start() t.start()
def read_queue(): def read_queue():
try: try:
if rxqueue.qsize()>0: if rxqueue.qsize()>0:
packet = rxqueue.get_nowait() packet = rxqueue.get_nowait()
process_udp(packet) process_udp(packet)
except: except:
traceback.print_exc() traceback.print_exc()
pass pass
# Start a timer to attempt to read a packet from the queue every 100ms. # Start a timer to attempt to read a packet from the queue every 100ms.
timer = QtCore.QTimer() timer = QtCore.QTimer()
@ -330,7 +376,7 @@ timer.start(100)
## Start Qt event loop unless running in interactive mode. ## Start Qt event loop unless running in interactive mode.
if __name__ == '__main__': if __name__ == '__main__':
import sys import sys
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_() QtGui.QApplication.instance().exec_()
udp_listener_running = False udp_listener_running = False

Wyświetl plik

@ -65,7 +65,7 @@ def ssdv_decode_callsign(code):
while code: while code:
callsign += _ssdv_callsign_alphabet[code % 40] callsign += _ssdv_callsign_alphabet[code % 40]
code /= 40 code /= 40
return callsign return callsign
def ssdv_packet_info(packet): def ssdv_packet_info(packet):
@ -138,11 +138,11 @@ def text_message_string(packet):
# The above # The above
def gps_weeksecondstoutc(gpsweek, gpsseconds, leapseconds): def gps_weeksecondstoutc(gpsweek, gpsseconds, leapseconds):
""" Convert time in GPS time (GPS Week, seconds-of-week) to a UTC timestamp """ """ Convert time in GPS time (GPS Week, seconds-of-week) to a UTC timestamp """
epoch = datetime.datetime.strptime("1980-01-06 00:00:00","%Y-%m-%d %H:%M:%S") epoch = datetime.datetime.strptime("1980-01-06 00:00:00","%Y-%m-%d %H:%M:%S")
elapsed = datetime.timedelta(days=(gpsweek*7),seconds=(gpsseconds+leapseconds)) elapsed = datetime.timedelta(days=(gpsweek*7),seconds=(gpsseconds+leapseconds))
timestamp = epoch + elapsed timestamp = epoch + elapsed
return timestamp.isoformat() return timestamp.isoformat()
def gps_telemetry_decoder(packet): def gps_telemetry_decoder(packet):
""" Extract GPS telemetry data from a packet, and return it as a dictionary. """ Extract GPS telemetry data from a packet, and return it as a dictionary.
@ -524,13 +524,13 @@ def image_telemetry_string(packet):
# CRC16 function for the above. # CRC16 function for the above.
def crc16_ccitt(data): def crc16_ccitt(data):
""" """
Calculate the CRC16 CCITT checksum of *data*. Calculate the CRC16 CCITT checksum of *data*.
(CRC16 CCITT: start 0xFFFF, poly 0x1021) (CRC16 CCITT: start 0xFFFF, poly 0x1021)
""" """
crc16 = crcmod.predefined.mkCrcFun('crc-ccitt-false') crc16 = crcmod.predefined.mkCrcFun('crc-ccitt-false')
return hex(crc16(data))[2:].upper().zfill(4) return hex(crc16(data))[2:].upper().zfill(4)
def image_telemetry_habitat_string(packet): def image_telemetry_habitat_string(packet):
@ -575,37 +575,37 @@ def image_telemetry_habitat_string(packet):
def image_telemetry_upload(packet, user_callsign="N0CALL"): def image_telemetry_upload(packet, user_callsign="N0CALL"):
""" Upload an image telemetry packet to habitat. """ """ Upload an image telemetry packet to habitat. """
sentence = image_telemetry_habitat_string(packet) sentence = image_telemetry_habitat_string(packet)
sentence_b64 = b64encode(sentence) sentence_b64 = b64encode(sentence)
date = datetime.datetime.utcnow().isoformat("T") + "Z" date = datetime.datetime.utcnow().isoformat("T") + "Z"
data = { data = {
"type": "payload_telemetry", "type": "payload_telemetry",
"data": { "data": {
"_raw": sentence_b64 "_raw": sentence_b64
}, },
"receivers": { "receivers": {
user_callsign: { user_callsign: {
"time_created": date, "time_created": date,
"time_uploaded": date, "time_uploaded": date,
}, },
}, },
} }
try: try:
c = httplib.HTTPConnection("habitat.habhub.org",timeout=5) c = httplib.HTTPConnection("habitat.habhub.org",timeout=5)
c.request( c.request(
"PUT", "PUT",
"/habitat/_design/payload_telemetry/_update/add_listener/%s" % sha256(sentence_b64).hexdigest(), "/habitat/_design/payload_telemetry/_update/add_listener/%s" % sha256(sentence_b64).hexdigest(),
json.dumps(data), # BODY json.dumps(data), # BODY
{"Content-Type": "application/json"} # HEADERS {"Content-Type": "application/json"} # HEADERS
) )
response = c.getresponse() response = c.getresponse()
return "Image Telemetry: Uploaded to Habitat Successfuly." return (True, "Image Telemetry: Uploaded to Habitat Successfuly.")
except Exception as e: except Exception as e:
return "Failed to upload to Habitat: %s" % (str(e)) return (False, "Failed to upload to Habitat: %s" % (str(e)))

Wyświetl plik

@ -19,6 +19,8 @@ python ssdv_upload.py $MYCALL &
# Start the SSDV RX GUI. # Start the SSDV RX GUI.
python rx_gui.py & python rx_gui.py &
# Start the Telemetry GUI.
python TelemetryGUI.py $MYCALL &
# Uncomment the following line if using a V3 RTLSDR and need the Bias-Tee enabled. # Uncomment the following line if using a V3 RTLSDR and need the Bias-Tee enabled.
# rtl_biast -b 1 # rtl_biast -b 1

Wyświetl plik

@ -1131,7 +1131,7 @@ class UBloxGPS(object):
self.gps.send_message(CLASS_CFG, MSG_CFG_NAV5,'\x00') self.gps.send_message(CLASS_CFG, MSG_CFG_NAV5,'\x00')
# Additional checks to be sure we're in the right dynamic model. # Additional checks to be sure we're in the right dynamic model.
if self.rx_counder % 40 == 0: if self.rx_counter % 40 == 0:
self.gps.set_preferred_dynamic_model(self.dynamic_model) self.gps.set_preferred_dynamic_model(self.dynamic_model)
# Send data to the callback function. # Send data to the callback function.