#!/usr/bin/env python2.7 # # Wenet IMU Data Viewer # from pyqtgraph.Qt import QtCore, QtGui import pyqtgraph as pg import pyqtgraph.opengl as gl from threading import Thread import traceback import socket import json import sys import Queue import datetime from WenetPackets import * import numpy as np # Various GUI Settings imu_plot_history_size = 60 # Seconds. user_callsign = "N0CALL" if len(sys.argv) > 1: user_callsign = sys.argv[1] app = QtGui.QApplication([]) # Configure PyQtGraph to use black plots on a transparent background. pg.setConfigOption('background',(0,0,0,0)) pg.setConfigOption('foreground', 'k') # # GPS Telemetry Data Frame # gpsFrame = QtGui.QFrame() gpsFrame.setFixedSize(250,250) gpsFrame.setFrameStyle(QtGui.QFrame.Box) gpsFrame.setLineWidth(2) gpsFrameTitle = QtGui.QLabel("GPS Data") gpsTime = QtGui.QLabel("Time:") gpsTimeValue = QtGui.QLabel("00:00:00") gpsLatitude = QtGui.QLabel("Latitude:") gpsLatitudeValue = QtGui.QLabel("-00.00000") gpsLongitude = QtGui.QLabel("Longitude:") gpsLongitudeValue = QtGui.QLabel("000.00000") gpsAltitude = QtGui.QLabel("Altitude:") gpsAltitudeValue = QtGui.QLabel("00000m") gpsSpeed = QtGui.QLabel("Speed") gpsSpeedValue = QtGui.QLabel("H 000kph V 0.0m/s") gpsHeading = QtGui.QLabel("Heading:") gpsHeadingValue = QtGui.QLabel("-") gpsSats = QtGui.QLabel("Satellites:") gpsSatsValue = QtGui.QLabel("0") gpsFixValid = QtGui.QLabel("Fix Status:") gpsFixValidValue = QtGui.QLabel("-") gpsFrameLayout = QtGui.QGridLayout() gpsFrameLayout.addWidget(gpsFrameTitle,0,0,1,2) gpsFrameLayout.addWidget(gpsTime,1,0) gpsFrameLayout.addWidget(gpsTimeValue,1,1) gpsFrameLayout.addWidget(gpsLatitude,2,0) gpsFrameLayout.addWidget(gpsLatitudeValue,2,1) gpsFrameLayout.addWidget(gpsLongitude,3,0) gpsFrameLayout.addWidget(gpsLongitudeValue,3,1) gpsFrameLayout.addWidget(gpsAltitude,4,0) gpsFrameLayout.addWidget(gpsAltitudeValue,4,1) gpsFrameLayout.addWidget(gpsSpeed,5,0) gpsFrameLayout.addWidget(gpsSpeedValue,5,1) gpsFrameLayout.addWidget(gpsHeading,6,0) gpsFrameLayout.addWidget(gpsHeadingValue,6,1) gpsFrameLayout.addWidget(gpsSats,7,0) gpsFrameLayout.addWidget(gpsSatsValue,7,1) gpsFrameLayout.addWidget(gpsFixValid,8,0) gpsFrameLayout.addWidget(gpsFixValidValue,8,1) gpsFrame.setLayout(gpsFrameLayout) def updateGpsFrame(gps_data): """ Update GPS Data Frame with a WenetPackets GPS Data dictionary """ try: gpsTimeValue.setText(gps_data['timestamp']) gpsLatitudeValue.setText("%.5f" % gps_data['latitude']) gpsLongitudeValue.setText("%.5f" % gps_data['longitude']) 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'])) gpsHeadingValue.setText("%.1f deg" % gps_data['heading']) gpsSatsValue.setText("%d" % gps_data['numSV']) gpsFixValidValue.setText(gps_data['gpsFix_str']) except: traceback.print_exc() pass # # IMU Telemetry Data Frame # imuFrame = QtGui.QFrame() imuFrame.setFixedSize(250,250) imuFrame.setFrameStyle(QtGui.QFrame.Box) imuFrame.setLineWidth(2) imuFrameTitle = QtGui.QLabel("IMU Data") imuTime = QtGui.QLabel("Time:") imuTimeValue = QtGui.QLabel("00:00:00") imuTemp = QtGui.QLabel("Temp:") imuTempValue = QtGui.QLabel("0 deg C") imuCalStatus = QtGui.QLabel("Calibration Status") imuCalStatus_System = QtGui.QLabel("System") imuCalStatus_System_OK = QtGui.QLabel("?") imuCalStatus_Gyro = QtGui.QLabel("Gyro") imuCalStatus_Gyro_OK = QtGui.QLabel("?") imuCalStatus_Accel = QtGui.QLabel("Accel") imuCalStatus_Accel_OK =QtGui.QLabel("?") imuCalStatus_Magnet = QtGui.QLabel("Mag") imuCalStatus_Magnet_OK = QtGui.QLabel("?") imuEulerLabel = QtGui.QLabel("Euler Angles") imuEulerHeadingLabel = QtGui.QLabel("Heading") imuEulerHeadingValue = QtGui.QLabel("0 deg") imuEulerRollLabel = QtGui.QLabel("Roll") imuEulerRollValue = QtGui.QLabel("0 deg") imuEulerPitchLabel = QtGui.QLabel("Pitch") imuEulerPitchValue = QtGui.QLabel("0 deg") imuFrameLayout = QtGui.QGridLayout() imuFrameLayout.addWidget(imuFrameTitle,0,0,1,4) imuFrameLayout.addWidget(imuTime,1,0,1,1) imuFrameLayout.addWidget(imuTimeValue,1,2,1,3) imuFrameLayout.addWidget(imuTemp,2,0,1,1) imuFrameLayout.addWidget(imuTempValue,2,2,1,3) imuFrameLayout.addWidget(imuCalStatus,3,0,1,4) imuFrameLayout.addWidget(imuCalStatus_System,4,0,1,1) imuFrameLayout.addWidget(imuCalStatus_Gyro,4,1,1,1) imuFrameLayout.addWidget(imuCalStatus_Accel,4,2,1,1) imuFrameLayout.addWidget(imuCalStatus_Magnet,4,3,1,1) imuFrameLayout.addWidget(imuCalStatus_System_OK,5,0,1,1) imuFrameLayout.addWidget(imuCalStatus_Gyro_OK,5,1,1,1) imuFrameLayout.addWidget(imuCalStatus_Accel_OK,5,2,1,1) imuFrameLayout.addWidget(imuCalStatus_Magnet_OK,5,3,1,1) imuFrameLayout.addWidget(imuEulerLabel,6,0,1,4) imuFrameLayout.addWidget(imuEulerHeadingLabel,7,0,1,2) imuFrameLayout.addWidget(imuEulerHeadingValue,7,2,1,2) imuFrameLayout.addWidget(imuEulerRollLabel,8,0,1,2) imuFrameLayout.addWidget(imuEulerRollValue,8,2,1,2) imuFrameLayout.addWidget(imuEulerPitchLabel,9,0,1,2) imuFrameLayout.addWidget(imuEulerPitchValue,9,2,1,2) imuFrame.setLayout(imuFrameLayout) # # IMU Plots. # buffer_size = 60 heading_values =[0] roll_values = [0] pitch_values = [0] imu_times = [0] imuPlot = pg.GraphicsLayoutWidget() imuPlot_Heading = imuPlot.addPlot(title='Heading') imuPlot_Roll = imuPlot.addPlot(title='Roll') imuPlot_Pitch = imuPlot.addPlot(title='Pitch') imuPlotLayout = imuPlot.ci.layout imuPlotLayout.rowMaximumHeight(220) # Configure Plots imuPlot_Heading.setXRange(-1*imu_plot_history_size,0) imuPlot_Heading.setYRange(0,360) imuPlot_Roll.setXRange(-1*imu_plot_history_size,0) imuPlot_Roll.setYRange(-180,180) imuPlot_Pitch.setXRange(-1*imu_plot_history_size,0) imuPlot_Pitch.setYRange(-180,180) # Get curve objects so we can update them with new data. heading_curve = imuPlot_Heading.plot(x=imu_times, y=heading_values, pen=pg.mkPen('k',width=2)) roll_curve = imuPlot_Roll.plot(x=imu_times, y=roll_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): """ Update IMU Frame and Plots with new IMU data """ global imu_times, heading_values, roll_values, pitch_values, heading_curve, roll_curve, pitch_curve try: imuTimeValue.setText(imu_data['timestamp']) imuTempValue.setText("%d" % imu_data['temp']) imuCalStatus_System_OK.setText("%d" % imu_data['sys_cal']) imuCalStatus_Gyro_OK.setText("%d" % imu_data['gyro_cal']) imuCalStatus_Accel_OK.setText("%d" % imu_data['accel_cal']) imuCalStatus_Magnet_OK.setText("%d" % imu_data['magnet_cal']) imuEulerHeadingValue.setText("%.1f" % imu_data['euler_heading']) imuEulerRollValue.setText("%.1f" % imu_data['euler_roll']) imuEulerPitchValue.setText("%.1f" % imu_data['euler_pitch']) latest_imu_time = imu_data['iTOW'] latest_euler_heading = imu_data['euler_heading'] latest_euler_roll = imu_data['euler_roll'] latest_euler_pitch = imu_data['euler_pitch'] if imu_times[0] == 0: # We need to initialise the arrays. imu_times[0] = latest_imu_time heading_values[0] = latest_euler_heading roll_values[0] = latest_euler_roll pitch_values[0] = latest_euler_pitch else: # Append, and crop arrays if they are too big. imu_times.append(latest_imu_time) heading_values.append(latest_euler_heading) roll_values.append(latest_euler_roll) pitch_values.append(latest_euler_pitch) if len(imu_times) > buffer_size: imu_times = imu_times[-1*buffer_size:] heading_values = heading_values[-1*buffer_size:] roll_values = roll_values[-1*buffer_size:] pitch_values = pitch_values[-1*buffer_size:] # Update plots 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) pitch_curve.setData(x=(np.array(imu_times)-latest_imu_time), y=pitch_values) except: traceback.print_exc() pass # Telemetry Log packetSnifferFrame = QtGui.QFrame() packetSnifferFrame.setFixedSize(1200,150) packetSnifferFrame.setFrameStyle(QtGui.QFrame.Box) packetSnifferTitle = QtGui.QLabel("Telemetry Log") console = QtGui.QPlainTextEdit() console.setReadOnly(True) packetSnifferLayout = QtGui.QGridLayout() packetSnifferLayout.addWidget(packetSnifferTitle) packetSnifferLayout.addWidget(console) packetSnifferFrame.setLayout(packetSnifferLayout) # Habitat Upload Frame uploadFrame = QtGui.QFrame() uploadFrame.setFixedSize(200,150) uploadFrame.setFrameStyle(QtGui.QFrame.Box) uploadFrame.setLineWidth(1) uploadFrameTitle = QtGui.QLabel("Habitat Upload") uploadFrameHabitat = QtGui.QCheckBox("Habitat Upload") uploadFrameHabitat.setChecked(True) uploadFrameCallsignLabel = QtGui.QLabel("Your Callsign:") 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.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_widget = QtGui.QWidget() layout = QtGui.QGridLayout() main_widget.setLayout(layout) layout.addWidget(gpsFrame,0,0) layout.addWidget(imuFrame,0,1) layout.addWidget(imuPlot,0,2,1,2) layout.addWidget(packetSnifferFrame,1,0,1,3) layout.addWidget(uploadFrame,1,3,1,1) mainwin = QtGui.QMainWindow() mainwin.setWindowTitle("Wenet GPS/IMU Telemetry Console") mainwin.setCentralWidget(main_widget) mainwin.resize(1300,400) mainwin.show() # # UDP Packet Handling Functions. # rxqueue = Queue.Queue(32) def process_udp(udp_packet): """ Process received UDP packets. """ # Grab timestamp. timestamp = datetime.datetime.utcnow().strftime("%Y%m%d-%H%M%SZ") # Parse JSON, and extract packet. packet_dict = json.loads(udp_packet) packet = packet_dict['packet'] # Decode GPS and IMU packets, and pass onto their respective GUI update functions. packet_type = decode_packet_type(packet) if packet_type == WENET_PACKET_TYPES.GPS_TELEMETRY: gps_data = gps_telemetry_decoder(packet) updateGpsFrame(gps_data) elif packet_type == WENET_PACKET_TYPES.ORIENTATION_TELEMETRY: orientation_data = orientation_telemetry_decoder(packet) updateIMUFrame(orientation_data) elif packet_type == WENET_PACKET_TYPES.IMAGE_TELEMETRY: # Print to console, then attempt to upload 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 def udp_rx_thread(): """ Listen on a port for UDP broadcast packets, and pass them onto process_udp()""" global udp_listener_running s = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) s.settimeout(1) s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) try: s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1) except: pass s.bind(('',WENET_TELEMETRY_UDP_PORT)) print("Started UDP Listener Thread.") udp_listener_running = True while udp_listener_running: try: m = s.recvfrom(2048) except socket.timeout: m = None if m != None: try: rxqueue.put_nowait(m[0]) except: traceback.print_exc() pass print("Closing UDP Listener") s.close() t = Thread(target=udp_rx_thread) t.start() def read_queue(): try: if rxqueue.qsize()>0: packet = rxqueue.get_nowait() process_udp(packet) except: traceback.print_exc() pass # Start a timer to attempt to read a packet from the queue every 100ms. timer = QtCore.QTimer() timer.timeout.connect(read_queue) timer.start(100) ## Start Qt event loop unless running in interactive mode. if __name__ == '__main__': import sys if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): QtGui.QApplication.instance().exec_() udp_listener_running = False