Browse Source

Groundstation

subDesTagesMitExtraKaese 5 years ago
parent
commit
be4e5d037a
39 changed files with 6870 additions and 0 deletions
  1. 9 0
      .gitignore
  2. 274 0
      groundstation/AppWindow.py
  3. 0 0
      groundstation/Neues Textdokument.txt
  4. 79 0
      groundstation/RAW UDP Recording/raw_listener.py
  5. 1 0
      groundstation/RAW UDP Recording/run.bat
  6. 199 0
      groundstation/auswertung/auswertung.py
  7. 191 0
      groundstation/auswertung/binToCsv.py
  8. BIN
      groundstation/auswertung/python_lzo-1.12-cp36-cp36m-win_amd64.whl
  9. 18 0
      groundstation/bexus.py
  10. 159 0
      groundstation/functions.py
  11. 153 0
      groundstation/listenUDP.py
  12. 0 0
      groundstation/logs/default_1026-213049.txt
  13. 889 0
      groundstation/main.ui
  14. 531 0
      groundstation/main_ui.py
  15. 832 0
      groundstation/mainold.ui
  16. 223 0
      groundstation/ping.py
  17. 34 0
      groundstation/pingThread.py
  18. 3 0
      groundstation/run.bat
  19. 146 0
      groundstation/settings.ui
  20. 82 0
      groundstation/settings_ui.py
  21. 3 0
      groundstation/udp-test.bat
  22. 190 0
      groundstation/udpToFile/auswertung.py
  23. 128 0
      groundstation/udpToFile/listenUDP.py
  24. 3 0
      groundstation/udpToFile/run.bat
  25. 200 0
      groundstation/ui läuft 15.10.18/AppWindow.py
  26. 18 0
      groundstation/ui läuft 15.10.18/bexus.py
  27. 159 0
      groundstation/ui läuft 15.10.18/functions.py
  28. 140 0
      groundstation/ui läuft 15.10.18/listenUDP.py
  29. 672 0
      groundstation/ui läuft 15.10.18/main.ui
  30. 410 0
      groundstation/ui läuft 15.10.18/main_ui.py
  31. 223 0
      groundstation/ui läuft 15.10.18/ping.py
  32. 34 0
      groundstation/ui läuft 15.10.18/pingThread.py
  33. 3 0
      groundstation/ui läuft 15.10.18/run.bat
  34. 146 0
      groundstation/ui läuft 15.10.18/settings.ui
  35. 82 0
      groundstation/ui läuft 15.10.18/settings_ui.py
  36. 3 0
      groundstation/ui läuft 15.10.18/udp-test.bat
  37. 215 0
      groundstation/ui läuft 15.10.18/vispyHelper.py
  38. 193 0
      groundstation/vispyHelper - Kopie.py
  39. 225 0
      groundstation/vispyHelper.py

+ 9 - 0
.gitignore

@@ -0,0 +1,9 @@
+__pycache__
+*.log
+*.obj
+pp.png
+*.csv
+*.ini
+*.url
+*.7z
+*.bin

+ 274 - 0
groundstation/AppWindow.py

@@ -0,0 +1,274 @@
+#!/usr/bin/python3
+# -*- coding: utf-8 -*-
+
+from PyQt5.QtWidgets import *
+from PyQt5.QtCore import (QSettings, QThread, pyqtSignal, pyqtSlot)
+
+from vispy import app, visuals, scene
+
+from main_ui import Ui_MainWindow
+from settings_ui import Ui_Dialog as Ui_Settings
+
+import numpy as np
+import random
+from colorsys import hsv_to_rgb
+import pprint
+import datetime, time
+from collections import deque
+
+from functions import *
+from listenUDP import listenUDP
+from pingThread import pingThread
+import vispyHelper
+
+import csv
+import socket
+
+
+random.seed()
+
+class AppWindow(QMainWindow):
+	
+	csv = None
+	rate = 10
+	
+	vel = [0] * 3
+	vel2 = [0] * 3
+	pos = [0] * 3
+	pos2 = [0] * 3
+	
+	oldSlaveCount = 0
+	
+	def __init__(self):
+		super().__init__()
+		self.ui = Ui_MainWindow()
+		self.ui.setupUi(self)
+		
+		self.settings = QSettings("bexus", "imufusion")
+		
+		self.master_rate = [0] * self.rate
+		self.slave_rate  = [0] * self.rate
+		
+		self.ip = getSettings(self.settings, 'ip', "172.16.18.171")
+		self.port = getSettings(self.settings, 'port', 1234)
+		logFile = getSettings(self.settings, 'logFile', "default")
+		self.logFile = open("logs/{}_{}.txt".format(logFile, datetime.datetime.now().strftime('%m%d-%H%M%S')), "w", newline='')
+		
+		
+		self.ui.labelAddress.setText("{}:{}".format(self.ip, self.port))
+		self.ui.pushButton_reset.clicked.connect(self.reset_pressed)
+		
+		self.udpServer = listenUDP(self.port)
+		self.udpServer.newMsg.connect(self.onNewMsg)
+		
+		self.ui.tableWidget.setColumnCount(3)
+		self.ui.tableWidget.setHorizontalHeaderLabels(("Master", "Slave", "Fusion"))
+		
+		self.fields = self.udpServer.master.keys()
+		self.ui.tableWidget.setRowCount(len(self.fields))
+		self.ui.tableWidget.setVerticalHeaderLabels(self.fields)
+		
+		self.pingThread = pingThread(self.ip)
+		self.pingThread.pong.connect(self.onPong)
+		
+		self.data3d = np.array([[0,0,0]], np.float32)
+		self.data3d2 = np.array([[0,0,0]], np.float32)
+		
+		self.view3D = vispyHelper.view3D(self.ui.openGLWidget)
+		self.viewQuad = vispyHelper.viewQuad(self.ui.openGLWidgetQuad)
+
+		
+		self.udpServer.start()
+		self.pingThread.start()
+		
+		self.timer = app.Timer()
+		self.timer.connect(self.update)
+		self.timer.start(0.1)
+		
+		self.show()
+		
+	def onNewMsg(self, data):
+	
+		### MAIN STUFF ###
+		
+		#self.ui.labelTestData.setText(pprint.pformat(data))
+		
+		if self.csv == None:
+			self.csv = csv.DictWriter(self.logFile, data.keys())
+			self.csv.writeheader()
+			
+		self.csv.writerow(data)
+		
+		self.master_rate.append(time.time() * 1000)
+		self.master_rate.pop(0)
+		
+		if data["slave_count"] != self.oldSlaveCount:
+			self.slave_rate.append(time.time() * 1000)
+			self.slave_rate.pop(0)
+			
+		self.oldSlaveCount = data["slave_count"];
+		
+		delayMaster = (max(self.master_rate) - min(self.master_rate)) / self.rate
+		delaySlave = (max(self.slave_rate) - min(self.slave_rate)) / self.rate 
+		
+		self.ui.labelLink_Master.setText("{:.1f}/s".format(1000 / delayMaster if delayMaster!=0 else 0))
+		self.ui.labelLink_Slave.setText("{:.1f}/s".format(1000 / delaySlave if delaySlave!=0 else 0))
+		
+		
+		rx, ry, rz =data["rot"]
+		heading = data["mag_head"]
+		
+		if rz != 0:
+			rx = np.arctan(ry / rz)
+			ry = np.arctan(rx / rz)
+		else:
+			rx = np.arctan(0)
+			ry = np.arctan(0)
+		rz = 180 + heading
+		
+		rx *= 45
+		ry *= -45
+		
+		rx += 90
+		rz -= 90
+		
+		#self.data3d = np.append(self.data3d, [data["pos"]], axis=0)
+		#self.data3d = np.append(self.data3d, [[data["latitude"], data["longitude"], data["altitude"]]], axis=0)
+		
+		self.vel[0] += data["global_accel"][0]
+		self.vel[1] += data["global_accel"][1]
+		self.vel[2] += data["global_accel"][2]
+		
+		self.pos[0] += self.vel[0]
+		self.pos[1] += self.vel[1]
+		self.pos[2] += self.vel[2] - 2
+		
+		self.vel2[0] += data["slave_global_accel"][0]
+		self.vel2[1] += data["slave_global_accel"][1]
+		self.vel2[2] += data["slave_global_accel"][2]
+		
+		self.pos2[0] += self.vel2[0]
+		self.pos2[1] += self.vel2[1]
+		self.pos2[2] += self.vel2[2] - 2
+		
+		self.data3d = np.append(self.data3d, [self.pos], axis=0)
+		self.data3d2 = np.append(self.data3d2, [self.pos], axis=0)
+		
+		if self.ui.tabWidget.currentIndex() == 0:
+			self.view3D.update((self.data3d, self.data3d), (rx, ry, rz))
+		elif self.ui.tabWidget.currentIndex() == 1:
+			self.viewQuad.update((self.data3d, self.data3d2), (rx, ry, rz))
+		else:
+			i = 0
+			for k in self.fields:
+				self.ui.tableWidget.setItem(i,0, QTableWidgetItem(str(data[k])))
+				self.ui.tableWidget.setItem(i,1, QTableWidgetItem(str(data["slave_"+k])))
+				self.ui.tableWidget.setItem(i,2, QTableWidgetItem(str(data["fusion_"+k])))
+				
+				i+=1
+				
+				
+		self.ui.label_temp_adc_1.setText("{:,.2g} °C".format(data["adc_temperature"]))
+		self.ui.label_temp_cis_1.setText("{:.2g} °C".format(data["temperature_cis"]))
+		self.ui.label_temp_mag_1.setText("{:.2g} °C".format(data["temperature_mag"]))
+		self.ui.label_temp_mpu_1.setText("{:.2g} °C".format(data["temperature_mpu"]))
+		
+		self.updateVector("label_Ac", data["accel"])
+		self.updateVector("label_Gy", data["gyro"])
+		self.updateVector("label_Ma", data["mag"])
+		self.updateVector("label_Ga", data["global_accel"])
+		self.updateVector("label_Rot", data["rot"])
+		self.updateVector("label_Pos", self.pos)
+		
+		self.ui.label_pres_cis_1.setText("{:f} Pa".format(data["pressure_cis"]))
+		self.ui.label_pres_i2c_1.setText("{:f} Pa".format(data["pressure_spi"]))	
+		
+		self.ui.label_lat_1.setText("{:.5g}".format(data["latitude"]))
+		self.ui.label_lon_1.setText("{:.5g}".format(data["longitude"]))
+		self.ui.label_alt_1.setText("{:.2f} m".format(data["altitude"]))
+		
+		##--------------------------------------------------------------------------------##	
+		self.ui.label_temp_mp.setText("{:g} °C".format(data["adc_temperature_internal"]))
+		self.ui.label_temp_i2c.setText("{:g} °C".format(data["temperature_i2c"]))
+		
+		self.ui.label_voltage.setText("{:.3f} V".format(data["adc_voltage"] * 1.0603))		
+		self.ui.label_current.setText("{:.2f} mA".format(data["adc_current"] * 1000))	
+		
+		##--------------------------------------------------------------------------------##	
+	
+		try:
+			dt = datetime.datetime.fromtimestamp(data["time"] / 1000)
+			#self.ui.label_date_1.setText(dt.strftime('%d.%m.%Y'))
+			self.ui.label_time_1.setText(dt.strftime('%H:%M:%S'))
+		except(OSError):
+			pass
+		self.ui.label_hdop_1.setText("{:g}".format(data["hdop"]))
+	
+	def updateVector(self, elem, data, f = "{:g}"):
+		getattr(self.ui, elem+"X").setText(f.format(data[0]))
+		getattr(self.ui, elem+"Y").setText(f.format(data[1]))
+		getattr(self.ui, elem+"Z").setText(f.format(data[2]))
+	
+	def update(self, ev):
+		#reset
+		value = self.ui.progressBar_reset.value()
+		if value > 0:
+			value -= 1
+			self.ui.progressBar_reset.setValue(value)
+			
+		return
+	
+	def reset_pressed(self):
+		value = self.ui.progressBar_reset.value()
+		value += 10
+		if value > 100:
+			value = 0
+			sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+			sock.sendto(bytes("RESET!!", "utf-8"), (self.ip, 65100))
+			
+		self.ui.progressBar_reset.setValue(value)
+	
+	def onPong(self, t):
+		if t != -1:
+			self.ui.labelLatency.setText("{:.1f}ms".format(t*1000))
+			self.ui.labelLink.setText("Online")
+			self.ui.labelLink.setStyleSheet("color:white;background-color:green")
+		else:
+			self.ui.labelLatency.setText("")
+			self.ui.labelLink.setText("Offline")
+			self.ui.labelLink.setStyleSheet("color:white;background-color:red;")
+
+	@pyqtSlot(bool)
+	def on_actionSettings_triggered(self, triggered):
+		self.settings
+
+		d = QDialog()
+		d.ui = Ui_Settings()
+		d.ui.setupUi(d)
+		d.ui.lineEditIP.setText(self.ip)
+		d.ui.lineEditPort.setText(str(self.port))
+		
+		if d.exec_():
+			self.ip = d.ui.lineEditIP.text()
+			self.port = safe_cast(d.ui.lineEditPort.text(), int, 1234)
+			logFile = d.ui.lineEditLogFile.text()
+			self.settings.setValue('ip', self.ip)
+			self.settings.setValue('port', self.port)
+			self.settings.setValue('logFile', logFile)
+			
+			self.logFile = open("logs/{}_{}.txt".format(logFile, datetime.datetime.now().strftime('%m%d-%H%M%S')), "w")
+			self.csv = None
+			
+			self.pingThread.changeIP(self.ip)
+			self.udpServer.stop()
+			self.udpServer = listenUDP(self.port)
+			self.udpServer.newMsg.connect(self.onNewMsg)
+			self.udpServer.start()
+			
+			self.ui.labelAddress.setText("{}:{}".format(self.ip, self.port))
+
+			# this writes the settings to storage
+			self.settings.sync()
+	
+	def on_actionFollow_triggered(self):
+		pass

+ 0 - 0
groundstation/Neues Textdokument.txt


+ 79 - 0
groundstation/RAW UDP Recording/raw_listener.py

@@ -0,0 +1,79 @@
+import socket
+import datetime, time
+import pprint
+
+import csv
+import sys
+sys.path.insert(0,'..')
+
+from functions import *
+
+pp = pprint.PrettyPrinter(indent=4)
+port = 1111
+count = 0
+
+cs = None
+
+dumpFile = open("logs/{}_{}.txt".format("binary", datetime.datetime.now().strftime('%m_%d-%H_%M_%S')), "bw")
+logFile = open("logs/{}_{}.txt".format("flight", datetime.datetime.now().strftime('%m_%d-%H_%M_%S')), "w", newline='')
+
+dataset = """
+	struct vector_Uint accel_raw;
+	struct vector_Uint gyro_raw; 
+	struct vector_Uint mag_raw;
+	uint32_t time;
+	uint32_t count;
+"""
+
+master = PktParser(dataset)
+slave = PktParser(dataset, master.end)
+
+udpServer = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+udpServer.bind(("", port))
+
+print("UDP Server is listening on port " + str(port))
+
+while True:
+	try:
+		data, addr = udpServer.recvfrom(2048)
+		
+	except WindowsError as e:
+		print(e)
+		time.sleep(1)
+		continue
+	if data == None:
+			continue
+
+	dumpFile.write(data + bytes("\r\n", 'ascii'))
+	
+	if len(data) != slave.end:
+		print("incorrect data length {} != {}".format(len(data), slave.end))
+		continue
+	
+	data_master = master.parse(data)
+	data_slave  = slave.parse(data)
+	
+	data = data_master
+
+	if not data_master:
+		continue
+		
+	for prop in data_slave.keys():
+		if data_slave:
+			data["slave_" + prop] = data_slave[prop]
+	
+	data["datetime"] = str(datetime.datetime.now())
+	data["count"] = count
+	
+	if cs == None:
+		cs = csv.DictWriter(logFile, data.keys())
+		cs.writeheader()
+	
+	cs.writerow(data)
+	
+	count+=1
+	
+	#pp.pprint(data)
+	
+	print("{:=14} Accel Z: Master: {:05} Slave: {:05}".format(count, data["accel_raw_z"], data["slave_accel_raw_z"]), end='\r', flush=True)
+	

+ 1 - 0
groundstation/RAW UDP Recording/run.bat

@@ -0,0 +1 @@
+python raw_listener.py || pause

+ 199 - 0
groundstation/auswertung/auswertung.py

@@ -0,0 +1,199 @@
+import socket
+import time
+import pprint
+import argparse
+import sys, os
+
+sys.path.insert(0,'..')
+
+from functions import *
+
+import numpy as np
+import matplotlib.pyplot as plt
+
+pp = pprint.PrettyPrinter(indent=4)
+
+parser = argparse.ArgumentParser()
+parser.add_argument("input_file")
+args = parser.parse_args()
+
+size = os.stat(args.input_file).st_size
+print("file name: {} size: {} bytes".format(args.input_file, size))
+f = open(args.input_file, "rb")
+
+#fw = open("data.csv", "w")
+
+buf = b''
+length = 1024
+
+t = []
+	
+result = {
+	"mag_x": [],
+	"temperature_mpu": [],
+	#"adc_temperature": [],
+}
+axis = [0, 1, 1]
+count = 0
+colors = ["tab:red", "tab:blue", "tab:green"]
+
+
+
+dataset = """
+/*-------RAW-----------*/
+	uint32_t pressure_cis_raw;		// pack 1 x 4
+	uint32_t temperature_cis_raw; //		+ 1 x 4
+	
+	uint32_t pressure_i2c_raw;
+	uint32_t temperature_i2c_raw;
+	
+	uint32_t temperature_spi_raw;
+	uint32_t adc_temperature_raw;
+	
+	int32_t	temperature_mpu_raw;
+	uint32_t adc_temperature_internal_raw;
+	
+	uint32_t	adc_voltage_raw;
+	uint32_t	adc_current_raw;
+	
+	struct vector_Uint accel_raw; // pack 3 x 4
+	struct vector_Uint gyro_raw;	//		+ 3 x 4
+	
+	/*-----Computed--------*/
+	
+	double pressure_cis; // no packing needed 1 x 8
+	
+	double temperature_cis;	
+	
+	double pressure_spi;	
+	
+	double temperature_i2c;	
+	
+	double temperature_spi;	
+	
+	double adc_temperature;	
+	
+	double adc_temperature_internal;			
+	
+	double adc_voltage;	
+
+	double adc_current;		
+
+	double temperature_mpu;	
+	
+	struct vector accel; // no packing needed 3 x 8
+	struct vector gyro;
+	struct vector rot;
+
+	/*-----------gps------------*/
+	int32_t longitude;
+	int32_t longitude_mod2;
+	
+	int32_t latitude;
+	int32_t latitude_mod2;
+	
+	int32_t altitude;
+	int32_t altitude_mod2;
+	
+	uint32_t time;
+	uint32_t time_mod2;
+	
+	uint32_t hdop;
+	uint32_t hdop_mod2;
+	/*--------------------------*/	
+
+	/*---------magnet-----------*/
+	struct vector_Uint mag_raw;	// pack 3 x 4
+	int32_t temperature_mag_raw; //		+ 1 x 4
+	
+	struct vector mag;
+
+	double temperature_mag;
+	
+	uint32_t count;
+	uint32_t crc;
+"""
+
+master = PktParser(dataset)
+slave = PktParser(dataset, master.end)
+fusion = PktParser(dataset, slave.end)
+	
+def check():
+	global count
+	#if buf[length-2] != ord("\r") or buf[length-1] != ord("\n"):
+	if buf.find(b"\r\n") == -1:
+		print("[{:8}] error missing EOL! bufLen={}".format(count, len(buf)))
+		return
+	
+	obj = master.parse(buf)
+	obj2 = slave.parse(buf)
+	
+	#if count == 0:
+		#fw.write(",".join(obj.keys()) + "\n")
+	
+	if obj["count"] != 0: #obj2["count"]:
+		print("[{:8}] count error! {} != {}".format(count, obj["count"], obj2["count"]))
+		return
+	if obj["crc"] != 1094795585: #obj2["crc"]:
+		print("[{:8}] crc error! crc={}".format(count, obj["crc"]))
+		return
+	
+	#if count != obj["count"] - 1:
+	#	print("[{0:8}] missing data between row {0} and {1}!".format(count, obj["count"]))
+	#count = obj["count"]
+	count += 1
+	
+	t.append(count/10)
+	for key in result:
+		result[key].append(obj[key])
+	
+	if count % 10000 == 0:
+		print("[{:8}] Reading...".format(count))
+	
+	return obj
+try:
+	byte = b'A'
+	while byte != b"":
+		# Do stuff with byte.
+		if len(buf) >= length:
+			obj = check()
+			
+			#if obj:
+				#fw.write(",".join(str(x) for x in obj.values()) + "\n")
+			
+			
+			pos = buf.find(b"\r\n")
+			if pos == -1:
+				print("[{:8}] can't find next line! bufLen={}".format(count, len(buf)))
+			
+			buf = buf[pos+2:]
+		else:
+			byte = f.read(length)
+			buf += byte
+			
+	#if obj != None:
+		
+	#	pp.pprint(obj)
+finally:
+	f.close()
+	#fw.close()
+
+	fig, ax1 = plt.subplots()
+	ax1.set_xlabel('time (s)')
+	
+	n = 0
+	
+	ax2 = ax1.twinx()
+	axes = [ax1, ax2]
+		
+	for key in result:
+		
+		axes[axis[n]].plot(t, result[key], label=key, color=colors[n])
+		axes[axis[n]].set_ylabel(key, color=colors[n])
+		axes[axis[n]].tick_params(axis='y', labelcolor=colors[n])
+		n += 1
+		
+	#plt.legend()
+	fig.tight_layout()
+	plt.show()
+	

+ 191 - 0
groundstation/auswertung/binToCsv.py

@@ -0,0 +1,191 @@
+#!/usr/bin/python3
+# -*- coding: utf-8 -*-
+
+
+import numpy as np
+import pprint
+import sys, os, time, datetime
+
+sys.path.insert(0,'..')
+from functions import *
+
+import csv
+import argparse
+
+pp = pprint.PrettyPrinter(indent=4)
+
+parser = argparse.ArgumentParser()
+parser.add_argument("input_file")
+parser.add_argument("output_file")
+args = parser.parse_args()
+
+#os.system('cls')
+
+size = os.stat(args.input_file).st_size
+print("file name: {} size: {} bytes".format(args.input_file, size))
+f = open(args.input_file, "rb")
+logFile = open(args.output_file, "w", newline='')
+
+dataset = """
+/*-------RAW-----------*/
+	uint32_t pressure_cis_raw;    // pack 1 x 4
+	uint32_t temperature_cis_raw; //    + 1 x 4
+	
+	uint32_t pressure_spi_raw;
+	uint32_t temperature_i2c_raw;
+	
+	uint32_t temperature_spi_raw;
+	uint32_t adc_temperature_raw;
+	
+	int32_t  temperature_mpu_raw;
+	uint32_t adc_temperature_internal_raw;
+	
+	uint32_t	adc_voltage_raw;
+	uint32_t	adc_current_raw;
+	
+	struct vector_Uint accel_raw; // pack 3 x 4
+	struct vector_Uint gyro_raw;  //    + 3 x 4
+	
+	struct vector_Uint mag_raw; // pack 3 x 4
+	                            //    + 1 x 4
+  int32_t temperature_mag_raw;
+	
+	/*-----Computed--------*/
+	
+	double pressure_cis; // no packing needed 1 x 8
+	
+	double temperature_cis;	
+	
+	double pressure_spi;	
+	
+	double temperature_i2c;	
+	
+	double temperature_spi;	
+	
+	double adc_temperature;	
+	
+	double adc_temperature_internal;			
+	
+	double adc_voltage;	
+
+	double adc_current;		
+
+	double temperature_mpu;	
+	
+	struct vector accel; // no packing needed 3 x 8
+	struct vector gyro;
+	struct vector rot;
+	
+	struct vector mag;
+	double temperature_mag;
+	
+	struct vector global_accel;
+	struct vector pos;
+
+	/*-----------gps------------*/
+	int32_t longitude;
+	int32_t latitude;
+	
+	int32_t altitude;
+	uint32_t time;
+	
+	uint32_t hdop;
+	int32_t mag_head; //heading
+	
+	uint32_t count;
+	uint32_t crc;
+"""
+master = PktParser(dataset)
+slave  = PktParser(dataset, master.end)
+fusion = PktParser(dataset, slave.end)
+
+length = fusion.end + 2
+
+print("expected length of row: {}".format(fusion.end))
+total = size/fusion.end
+print("expected row count: {}".format(total))
+print()
+print(master.keys())
+time.sleep(1)
+
+count = 0
+buf = b''
+obj = None
+
+cs = None
+		
+
+def check():
+	#pp.pprint(x)
+	
+	if buf[fusion.end:fusion.end+2] != b"\r\n":
+		print("[{:8}/{:8}] error missing EOL!".format(count, total))
+		return
+	
+	m = master.parse(buf)
+	s = slave.parse(buf)
+	f = fusion.parse(buf)
+	
+	crc_m = sum(buf[:master.end-4])
+	crc_s = sum(buf[master.end:slave.end-4])
+	crc_f = sum(buf[slave.end:fusion.end-4])
+	
+	
+	if crc_m != m["crc"]:
+		print("[{:8}/{:8}] crc error in master! diff = {:12}".format(count, total, crc_m - m["crc"]))
+		return
+	#if crc_s != s["crc"]:
+		#print("[{:8}/{:8}] crc error in slave!".format(count, total))
+	#	return
+	#if crc_f != f["crc"]:
+		#print("[{:8}/{:8}] crc error in fusion!".format(count, total))
+	#	return
+	
+	data = m
+	# for prop in slave.keys():
+		# if s:
+			# data["slave_" + prop] = s[prop]
+		# if f:
+			# data["fusion_" + prop] = f[prop]
+	
+	data["slave_count"] = s["count"]
+	data["slave_time"] = s["time"]
+	
+	return data
+
+try:
+	byte = b'A'
+	while byte != b"":
+		# Do stuff with byte.
+		if len(buf) >= length:
+			obj = check()
+			
+			if obj:
+
+				if cs == None:
+					cs = csv.DictWriter(logFile, obj.keys())
+					cs.writeheader()
+				
+				cs.writerow(obj)
+			
+				if count % 10000 == 1:
+					print("[{:8}/{:8}] ...{:.2%}".format(count, total, count/total))
+			
+				count += 1
+			pos = buf.find(b"\r\n")
+			if pos == -1:
+				print("[{:8}/{:8}] can't find next line!".format(count, total))
+			
+			#if not obj and pos >= 0:
+				#print("[{:8}] skipping {:4} chars".format(count, pos+2))
+			
+			if pos != fusion.end and not obj:
+				print("[{:8}/{:8}] expected {:4} bytes but got {:4}".format(count, total, fusion.end+2, pos+2))
+			
+			buf = buf[pos+2:]
+		else:
+			byte = f.read(length)
+			buf += byte
+finally:
+	f.close()
+	#fw.close()

BIN
groundstation/auswertung/python_lzo-1.12-cp36-cp36m-win_amd64.whl


+ 18 - 0
groundstation/bexus.py

@@ -0,0 +1,18 @@
+#!/usr/bin/python3
+# -*- coding: utf-8 -*-
+
+import sys
+
+from PyQt5.QtWidgets import *
+from PyQt5.QtCore import (QThread, pyqtSignal, pyqtSlot)
+
+from functions import *
+from AppWindow import AppWindow
+
+
+
+qApp = QApplication(sys.argv)
+w = AppWindow()
+ret = qApp.exec_()
+
+sys.exit(ret)

+ 159 - 0
groundstation/functions.py

@@ -0,0 +1,159 @@
+#!/usr/bin/python3
+# -*- coding: utf-8 -*-
+
+import struct
+import copy
+from collections import OrderedDict
+
+def safe_cast(val, to_type, default=None):
+	try:
+		return to_type(val)
+	except (ValueError, TypeError):
+		return default
+
+def listToInt(a, signed=False):
+	res = 0
+	for x in reversed(a):
+		res = res * 256 + x
+	if signed and res >= 256 ** len(a) / 2:
+		res -= 256 ** len(a)
+	return res
+	
+def listToDouble(a):
+	return struct.unpack('d', a)[0]
+
+def listToFloat(a):
+	return struct.unpack('f', a)[0]
+	
+def listToVector(a, double=True):
+	if double:
+		return struct.unpack('ddd', a)
+	else:
+		return struct.unpack('iii', a)
+
+def listToVector_Uint(a):
+	return struct.unpack('iii', a)
+		
+def getSettings(set, name, default=None):
+	val = set.value(name)
+	if val == None:
+		return default
+	
+	return val
+	
+	
+class PktParser():
+	def __init__(self, struct, offset=0):
+		self.props = OrderedDict()
+		
+		for line in struct.splitlines():
+			prop = self._getInfo(line)
+			if prop:
+				length, cb, name = prop
+				
+				#print("{:4d} {:2d} {}".format(offset, length, name))
+				
+				self.props[name] = lambda data, c=cb, o=offset, l=length: c(data[o:o+l])
+				offset += length
+				
+		self.end = offset
+		self.offset = offset
+		
+		
+	def _getInfo(self, line):
+		i = 0
+		type = None
+		name = None
+		
+		for word in line.split():
+			if word == "":
+				#print("(empty)")
+				continue
+				
+			if word[:2] in ["//", "/*"]:
+				#print(word)
+				break
+				
+			if word == "struct":
+				continue
+				
+			if i == 0: #type
+				type = self._getType(word)
+				if type:
+					i += 1
+					continue
+			
+			if i == 1: #name
+				p = word.find(";")
+				name = word[:p]
+			
+		if type and name:
+			return type + (name,)
+			
+	def _getType(self, name):
+		
+		if name == "uint32_t":
+			return (4, listToInt)
+			
+		if name == "int32_t":
+			return (4, lambda v: listToInt(v, True))
+		
+		if name == "double":
+			return (8, listToDouble)
+		
+		if name == "float":
+			return (4, listToFloat)
+		
+		if name == "vector_Uint":
+			return (12, listToVector_Uint)
+			
+		if name == "vector":
+			return (24, listToVector)
+			
+		print("Type {} invalid".format(name))	
+		
+		return None
+	
+	def parse(self, data):
+		
+		if len(data) < self.end:
+			print("data too short {} < {}".format(len(data), self.end))
+			return
+		
+		ret = OrderedDict()
+		
+		for prop in self.props.keys():
+			ret[prop] = self.props[prop](data)
+		
+		d = {}
+		for key in ret:
+			if type(ret[key]) is tuple:
+				d.update(dict(zip((key+"_x",key+"_y",key+"_z"), ret[key])))
+		ret.update(d)
+		
+		return ret
+		
+	def keys(self):
+		tmp = b'\x00' * self.end
+		return self.parse(tmp).keys()
+		
+def confirm(prompt=None, resp=False):
+	if prompt is None:
+		prompt = 'Confirm'
+
+	if resp:
+		prompt = '%s [%s]|%s: ' % (prompt, 'y', 'n')
+	else:
+		prompt = '%s [%s]|%s: ' % (prompt, 'n', 'y')
+		
+	while True:
+		ans = raw_input(prompt)
+		if not ans:
+			return resp
+		if ans not in ['y', 'Y', 'n', 'N']:
+			print ('please enter y or n.')
+			continue
+		if ans == 'y' or ans == 'Y':
+			return True
+		if ans == 'n' or ans == 'N':
+			return False

+ 153 - 0
groundstation/listenUDP.py

@@ -0,0 +1,153 @@
+#!/usr/bin/python3
+# -*- coding: utf-8 -*-
+
+import socket
+import time
+import pprint
+import sys
+
+import datetime
+
+from PyQt5.QtWidgets import *
+from PyQt5.QtCore import (QThread, pyqtSignal, pyqtSlot)
+
+from functions import *
+
+pp = pprint.PrettyPrinter(indent=4)
+dataset = """
+/*-------RAW-----------*/
+	uint32_t pressure_cis_raw;    // pack 1 x 4
+	uint32_t temperature_cis_raw; //    + 1 x 4
+	
+	uint32_t pressure_spi_raw;
+	uint32_t temperature_i2c_raw;
+	
+	uint32_t temperature_spi_raw;
+	uint32_t adc_temperature_raw;
+	
+	int32_t  temperature_mpu_raw;
+	uint32_t adc_temperature_internal_raw;
+	
+	uint32_t	adc_voltage_raw;
+	uint32_t	adc_current_raw;
+	
+	struct vector_Uint accel_raw; // pack 3 x 4
+	struct vector_Uint gyro_raw;  //    + 3 x 4
+	
+	struct vector_Uint mag_raw; // pack 3 x 4
+	                            //    + 1 x 4
+  int32_t temperature_mag_raw;
+	
+	/*-----Computed--------*/
+	
+	double pressure_cis; // no packing needed 1 x 8
+	
+	double temperature_cis;	
+	
+	double pressure_spi;	
+	
+	double temperature_i2c;	
+	
+	double temperature_spi;	
+	
+	double adc_temperature;	
+	
+	double adc_temperature_internal;			
+	
+	double adc_voltage;	
+
+	double adc_current;		
+
+	double temperature_mpu;	
+	
+	struct vector accel; // no packing needed 3 x 8
+	struct vector gyro;
+	struct vector rot;
+	
+	struct vector mag;
+	double temperature_mag;
+	
+	struct vector global_accel;
+	struct vector pos;
+
+	/*-----------gps------------*/
+	float longitude;
+	float latitude;
+	
+	int32_t altitude;
+	uint32_t time;
+	
+	uint32_t hdop;
+	int32_t mag_head; //heading
+	
+	uint32_t count;
+	uint32_t crc;
+"""
+
+class listenUDP(QThread):
+	newMsg = pyqtSignal(dict)
+	
+	master = PktParser(dataset)
+	slave = PktParser(dataset, master.end)
+	fusion = PktParser(dataset, slave.end)
+	
+	dumpFile = open("logs/dump_{}.bin".format(datetime.datetime.now().strftime('%m%d-%H%M%S')), "bw")
+	
+	def __init__(self, port = 1234):
+		QThread.__init__(self)
+		self._udpServer = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+		self._udpServer.bind(("", port))
+		self.port = port
+		
+	def run(self):
+		while self._udpServer:
+			try:
+				data, addr = self._udpServer.recvfrom(2048)
+				
+			except WindowsError as e:
+				print(e)
+				time.sleep(1)
+				continue
+			if data == None:
+					continue
+		
+			self.dumpFile.write(data + bytes("\r\n", 'ascii'))
+			
+			data_master = self.master.parse(data)
+			data_slave  = self.slave.parse(data)
+			data_fusion = self.fusion.parse(data)
+			
+			data = data_master
+
+			if not data_master:
+				continue
+				
+			for prop in self.master.keys():
+				if data_slave:
+					data["slave_" + prop] = data_slave[prop]
+				if data_fusion:
+					data["fusion_" + prop] = data_fusion[prop]
+			
+			data["datetime"] = str(datetime.datetime.now())
+			
+			if __name__ == "__main__":
+				pp.pprint(data)
+				
+			self.newMsg.emit(data)
+			time.sleep(0.01)
+	
+	def stop(self):
+		self._udpServer.close()
+		self._udpServer = None
+		self.quit()
+		
+		if __name__ == "__main__":
+			pp.pprint(obj)
+			
+		return
+
+if __name__ == "__main__":
+	udp = listenUDP(port = 1234)
+	print("UDP Server is listening on port " + str(udp.port))
+	udp.run()
+	

+ 0 - 0
groundstation/logs/default_1026-213049.txt


+ 889 - 0
groundstation/main.ui

@@ -0,0 +1,889 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>MainWindow</class>
+ <widget class="QMainWindow" name="MainWindow">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>1083</width>
+    <height>832</height>
+   </rect>
+  </property>
+  <property name="sizePolicy">
+   <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+    <horstretch>0</horstretch>
+    <verstretch>0</verstretch>
+   </sizepolicy>
+  </property>
+  <property name="minimumSize">
+   <size>
+    <width>1056</width>
+    <height>670</height>
+   </size>
+  </property>
+  <property name="windowTitle">
+   <string>BEXUS</string>
+  </property>
+  <property name="windowIcon">
+   <iconset>
+    <normaloff>../../../../.designer/backup/pp.png</normaloff>../../../../.designer/backup/pp.png</iconset>
+  </property>
+  <widget class="QWidget" name="centralwidget">
+   <layout class="QGridLayout" name="gridLayout_2">
+    <item row="0" column="1">
+     <layout class="QGridLayout" name="gridLayout">
+      <item row="0" column="0">
+       <layout class="QVBoxLayout" name="verticalLayout_2">
+        <item>
+         <widget class="QGroupBox" name="groupBox">
+          <property name="minimumSize">
+           <size>
+            <width>306</width>
+            <height>0</height>
+           </size>
+          </property>
+          <property name="title">
+           <string>Stats</string>
+          </property>
+          <layout class="QGridLayout" name="gridLayout_5">
+           <item row="2" column="0">
+            <widget class="QLabel" name="label_6">
+             <property name="text">
+              <string>Latency:</string>
+             </property>
+            </widget>
+           </item>
+           <item row="1" column="1">
+            <widget class="QLabel" name="labelLink">
+             <property name="font">
+              <font>
+               <weight>75</weight>
+               <bold>true</bold>
+              </font>
+             </property>
+             <property name="styleSheet">
+              <string notr="true">color:white;background-color:red;</string>
+             </property>
+             <property name="text">
+              <string>Offline</string>
+             </property>
+            </widget>
+           </item>
+           <item row="1" column="0">
+            <widget class="QLabel" name="label_2">
+             <property name="text">
+              <string>Link:</string>
+             </property>
+            </widget>
+           </item>
+           <item row="4" column="1">
+            <widget class="QLabel" name="labelLink_Slave">
+             <property name="text">
+              <string>0/s</string>
+             </property>
+            </widget>
+           </item>
+           <item row="4" column="0">
+            <widget class="QLabel" name="label_25">
+             <property name="text">
+              <string>MCU Slave:</string>
+             </property>
+            </widget>
+           </item>
+           <item row="0" column="0">
+            <widget class="QLabel" name="label">
+             <property name="text">
+              <string>Address:</string>
+             </property>
+            </widget>
+           </item>
+           <item row="3" column="1">
+            <widget class="QLabel" name="labelLink_Master">
+             <property name="text">
+              <string>0/s</string>
+             </property>
+            </widget>
+           </item>
+           <item row="2" column="1">
+            <widget class="QLabel" name="labelLatency">
+             <property name="text">
+              <string>20ms</string>
+             </property>
+            </widget>
+           </item>
+           <item row="3" column="0">
+            <widget class="QLabel" name="label_3">
+             <property name="text">
+              <string>MCU Master:</string>
+             </property>
+            </widget>
+           </item>
+           <item row="0" column="1">
+            <widget class="QLabel" name="labelAddress">
+             <property name="text">
+              <string>192.168.0.8:1234</string>
+             </property>
+            </widget>
+           </item>
+          </layout>
+         </widget>
+        </item>
+        <item>
+         <widget class="QScrollArea" name="scrollArea_2">
+          <property name="sizePolicy">
+           <sizepolicy hsizetype="Minimum" vsizetype="Expanding">
+            <horstretch>0</horstretch>
+            <verstretch>0</verstretch>
+           </sizepolicy>
+          </property>
+          <property name="minimumSize">
+           <size>
+            <width>400</width>
+            <height>0</height>
+           </size>
+          </property>
+          <property name="frameShape">
+           <enum>QFrame::NoFrame</enum>
+          </property>
+          <property name="horizontalScrollBarPolicy">
+           <enum>Qt::ScrollBarAlwaysOff</enum>
+          </property>
+          <property name="widgetResizable">
+           <bool>true</bool>
+          </property>
+          <widget class="QWidget" name="scrollAreaWidgetContents_2">
+           <property name="geometry">
+            <rect>
+             <x>0</x>
+             <y>0</y>
+             <width>379</width>
+             <height>763</height>
+            </rect>
+           </property>
+           <layout class="QGridLayout" name="gridLayout_13">
+            <property name="leftMargin">
+             <number>0</number>
+            </property>
+            <property name="topMargin">
+             <number>0</number>
+            </property>
+            <property name="rightMargin">
+             <number>0</number>
+            </property>
+            <property name="bottomMargin">
+             <number>0</number>
+            </property>
+            <item row="0" column="0">
+             <widget class="QGroupBox" name="groupBox_6">
+              <property name="title">
+               <string>Position</string>
+              </property>
+              <layout class="QGridLayout" name="gridLayout_11">
+               <item row="2" column="1">
+                <widget class="QLabel" name="label_alt_1">
+                 <property name="text">
+                  <string>-</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="2" column="0">
+                <widget class="QLabel" name="label_21">
+                 <property name="text">
+                  <string>Altitude:</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="1" column="0">
+                <widget class="QLabel" name="label_29">
+                 <property name="text">
+                  <string>Longitude</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="1" column="1">
+                <widget class="QLabel" name="label_lon_1">
+                 <property name="text">
+                  <string>-</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="0" column="1">
+                <widget class="QLabel" name="label_lat_1">
+                 <property name="text">
+                  <string>-</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="0" column="0">
+                <widget class="QLabel" name="label_27">
+                 <property name="text">
+                  <string>Latitude</string>
+                 </property>
+                </widget>
+               </item>
+              </layout>
+             </widget>
+            </item>
+            <item row="3" column="0">
+             <widget class="QGroupBox" name="groupBox_5">
+              <property name="title">
+               <string>Pressure</string>
+              </property>
+              <layout class="QGridLayout" name="gridLayout_9">
+               <item row="0" column="0">
+                <widget class="QLabel" name="label_9">
+                 <property name="text">
+                  <string>Cis</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="1" column="0">
+                <widget class="QLabel" name="label_20">
+                 <property name="text">
+                  <string>SPI</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="0" column="1">
+                <widget class="QLabel" name="label_pres_cis_1">
+                 <property name="text">
+                  <string>-</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="1" column="1">
+                <widget class="QLabel" name="label_pres_i2c_1">
+                 <property name="text">
+                  <string>-</string>
+                 </property>
+                </widget>
+               </item>
+              </layout>
+             </widget>
+            </item>
+            <item row="1" column="0">
+             <widget class="QGroupBox" name="groupBox_4">
+              <property name="title">
+               <string>IMU-Raw</string>
+              </property>
+              <layout class="QGridLayout" name="gridLayout_7">
+               <item row="1" column="1">
+                <widget class="QLabel" name="label_GyX">
+                 <property name="text">
+                  <string>0</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="0" column="1">
+                <widget class="QLabel" name="label_AcX">
+                 <property name="text">
+                  <string>0</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="1" column="3">
+                <widget class="QLabel" name="label_16">
+                 <property name="text">
+                  <string>GyY:</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="1" column="4">
+                <widget class="QLabel" name="label_GyY">
+                 <property name="text">
+                  <string>0</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="1" column="0">
+                <widget class="QLabel" name="label_15">
+                 <property name="text">
+                  <string>GyX:</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="0" column="0">
+                <widget class="QLabel" name="label_5">
+                 <property name="text">
+                  <string>AcX:</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="0" column="4">
+                <widget class="QLabel" name="label_AcY">
+                 <property name="text">
+                  <string>0</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="0" column="3">
+                <widget class="QLabel" name="label_11">
+                 <property name="text">
+                  <string>AcY:</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="1" column="6">
+                <widget class="QLabel" name="label_GyZ">
+                 <property name="text">
+                  <string>0</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="0" column="6">
+                <widget class="QLabel" name="label_AcZ">
+                 <property name="text">
+                  <string>0</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="1" column="5">
+                <widget class="QLabel" name="label_17">
+                 <property name="text">
+                  <string>GyZ:</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="0" column="5">
+                <widget class="QLabel" name="label_13">
+                 <property name="text">
+                  <string>AcZ:</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="2" column="0">
+                <widget class="QLabel" name="label_8">
+                 <property name="text">
+                  <string>MaX:</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="2" column="1">
+                <widget class="QLabel" name="label_MaX">
+                 <property name="text">
+                  <string>0</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="2" column="3">
+                <widget class="QLabel" name="label_12">
+                 <property name="text">
+                  <string>MaY:</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="2" column="4">
+                <widget class="QLabel" name="label_MaY">
+                 <property name="text">
+                  <string>0</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="2" column="5">
+                <widget class="QLabel" name="label_18">
+                 <property name="text">
+                  <string>MaZ:</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="2" column="6">
+                <widget class="QLabel" name="label_MaZ">
+                 <property name="text">
+                  <string>0</string>
+                 </property>
+                </widget>
+               </item>
+              </layout>
+             </widget>
+            </item>
+            <item row="2" column="0">
+             <widget class="QGroupBox" name="groupBox_8">
+              <property name="title">
+               <string>IMU-Fusion</string>
+              </property>
+              <layout class="QGridLayout" name="gridLayout_8">
+               <item row="2" column="0">
+                <widget class="QLabel" name="label_30">
+                 <property name="text">
+                  <string>PosX:</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="2" column="1">
+                <widget class="QLabel" name="label_PosX">
+                 <property name="text">
+                  <string>0</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="2" column="2">
+                <widget class="QLabel" name="label_34">
+                 <property name="text">
+                  <string>PosY:</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="0" column="5">
+                <widget class="QLabel" name="label_GaZ">
+                 <property name="text">
+                  <string>0</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="1" column="2">
+                <widget class="QLabel" name="label_19">
+                 <property name="text">
+                  <string>RotY:</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="1" column="0">
+                <widget class="QLabel" name="label_23">
+                 <property name="text">
+                  <string>RotX:</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="1" column="5">
+                <widget class="QLabel" name="label_RotZ">
+                 <property name="text">
+                  <string>0</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="0" column="4">
+                <widget class="QLabel" name="label_28">
+                 <property name="text">
+                  <string>AcZ:</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="0" column="1">
+                <widget class="QLabel" name="label_GaX">
+                 <property name="text">
+                  <string>0</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="1" column="4">
+                <widget class="QLabel" name="label_26">
+                 <property name="text">
+                  <string>RotZ:</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="0" column="0">
+                <widget class="QLabel" name="label_7">
+                 <property name="text">
+                  <string>AcX:</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="1" column="3">
+                <widget class="QLabel" name="label_RotY">
+                 <property name="text">
+                  <string>0</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="0" column="3">
+                <widget class="QLabel" name="label_GaY">
+                 <property name="text">
+                  <string>0</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="1" column="1">
+                <widget class="QLabel" name="label_RotX">
+                 <property name="text">
+                  <string>0</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="0" column="2">
+                <widget class="QLabel" name="label_14">
+                 <property name="text">
+                  <string>AcY:</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="2" column="4">
+                <widget class="QLabel" name="label_36">
+                 <property name="text">
+                  <string>PosZ:</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="2" column="3">
+                <widget class="QLabel" name="label_PosY">
+                 <property name="text">
+                  <string>0</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="2" column="5">
+                <widget class="QLabel" name="label_PosZ">
+                 <property name="text">
+                  <string>0</string>
+                 </property>
+                </widget>
+               </item>
+              </layout>
+             </widget>
+            </item>
+            <item row="4" column="0">
+             <widget class="QGroupBox" name="groupBox_2">
+              <property name="title">
+               <string>Temperature</string>
+              </property>
+              <layout class="QGridLayout" name="gridLayout_6">
+               <item row="0" column="1">
+                <widget class="QLabel" name="label_temp_adc_1">
+                 <property name="text">
+                  <string>-</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="0" column="0">
+                <widget class="QLabel" name="label_4">
+                 <property name="text">
+                  <string>Viehmann</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="2" column="1">
+                <widget class="QLabel" name="label_temp_mpu_1">
+                 <property name="text">
+                  <string>-</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="1" column="1">
+                <widget class="QLabel" name="label_temp_cis_1">
+                 <property name="text">
+                  <string>-</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="2" column="0">
+                <widget class="QLabel" name="label_24">
+                 <property name="text">
+                  <string>MPU</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="1" column="0">
+                <widget class="QLabel" name="label_22">
+                 <property name="text">
+                  <string>Cis</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="3" column="0">
+                <widget class="QLabel" name="label_37">
+                 <property name="text">
+                  <string>Mag</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="3" column="1">
+                <widget class="QLabel" name="label_temp_mag_1">
+                 <property name="text">
+                  <string>-</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="4" column="1">
+                <widget class="QLabel" name="label_temp_i2c">
+                 <property name="text">
+                  <string>20.00 °C</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="4" column="0">
+                <widget class="QLabel" name="label_33">
+                 <property name="text">
+                  <string>I2C</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="5" column="0">
+                <widget class="QLabel" name="label_38">
+                 <property name="text">
+                  <string>µC</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="5" column="1">
+                <widget class="QLabel" name="label_temp_mp">
+                 <property name="text">
+                  <string>20.00 °C</string>
+                 </property>
+                </widget>
+               </item>
+              </layout>
+             </widget>
+            </item>
+            <item row="5" column="0">
+             <widget class="QGroupBox" name="groupBox_7">
+              <property name="title">
+               <string>GPS</string>
+              </property>
+              <layout class="QGridLayout" name="gridLayout_12">
+               <item row="1" column="0">
+                <widget class="QLabel" name="label_35">
+                 <property name="text">
+                  <string>Hdop</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="1" column="1">
+                <widget class="QLabel" name="label_hdop_1">
+                 <property name="text">
+                  <string>0</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="0" column="0">
+                <widget class="QLabel" name="label_31">
+                 <property name="text">
+                  <string>Time</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="0" column="1">
+                <widget class="QLabel" name="label_time_1">
+                 <property name="text">
+                  <string>12:00</string>
+                 </property>
+                </widget>
+               </item>
+              </layout>
+             </widget>
+            </item>
+            <item row="6" column="0">
+             <widget class="QGroupBox" name="groupBox_9">
+              <property name="title">
+               <string>Power</string>
+              </property>
+              <layout class="QGridLayout" name="gridLayout_15">
+               <item row="0" column="0">
+                <widget class="QLabel" name="label_10">
+                 <property name="text">
+                  <string>Voltage</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="1" column="1">
+                <widget class="QLabel" name="label_current">
+                 <property name="text">
+                  <string>0.00 A</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="0" column="1">
+                <widget class="QLabel" name="label_voltage">
+                 <property name="text">
+                  <string>0.00 V</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="1" column="0">
+                <widget class="QLabel" name="label_32">
+                 <property name="text">
+                  <string>Current</string>
+                 </property>
+                </widget>
+               </item>
+              </layout>
+             </widget>
+            </item>
+            <item row="9" column="0">
+             <spacer name="verticalSpacer">
+              <property name="orientation">
+               <enum>Qt::Vertical</enum>
+              </property>
+              <property name="sizeType">
+               <enum>QSizePolicy::Expanding</enum>
+              </property>
+              <property name="sizeHint" stdset="0">
+               <size>
+                <width>20</width>
+                <height>40</height>
+               </size>
+              </property>
+             </spacer>
+            </item>
+           </layout>
+          </widget>
+         </widget>
+        </item>
+        <item>
+         <widget class="QProgressBar" name="progressBar_reset">
+          <property name="sizePolicy">
+           <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
+            <horstretch>0</horstretch>
+            <verstretch>0</verstretch>
+           </sizepolicy>
+          </property>
+          <property name="value">
+           <number>0</number>
+          </property>
+          <property name="textVisible">
+           <bool>false</bool>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QPushButton" name="pushButton_reset">
+          <property name="sizePolicy">
+           <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
+            <horstretch>0</horstretch>
+            <verstretch>0</verstretch>
+           </sizepolicy>
+          </property>
+          <property name="text">
+           <string>Restart</string>
+          </property>
+         </widget>
+        </item>
+       </layout>
+      </item>
+     </layout>
+    </item>
+    <item row="0" column="0">
+     <widget class="QTabWidget" name="tabWidget">
+      <property name="currentIndex">
+       <number>2</number>
+      </property>
+      <widget class="QWidget" name="tab">
+       <attribute name="title">
+        <string>3D View</string>
+       </attribute>
+       <layout class="QGridLayout" name="gridLayout_4">
+        <item row="0" column="0">
+         <widget class="QOpenGLWidget" name="openGLWidget">
+          <property name="sizePolicy">
+           <sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
+            <horstretch>0</horstretch>
+            <verstretch>0</verstretch>
+           </sizepolicy>
+          </property>
+          <property name="mouseTracking">
+           <bool>false</bool>
+          </property>
+         </widget>
+        </item>
+       </layout>
+      </widget>
+      <widget class="QWidget" name="tab_3">
+       <attribute name="title">
+        <string>Orthogonal View</string>
+       </attribute>
+       <layout class="QGridLayout" name="gridLayout_3">
+        <item row="0" column="0">
+         <widget class="QOpenGLWidget" name="openGLWidgetQuad"/>
+        </item>
+       </layout>
+      </widget>
+      <widget class="QWidget" name="tab_2">
+       <attribute name="title">
+        <string>Raw data</string>
+       </attribute>
+       <layout class="QGridLayout" name="gridLayout_10">
+        <item row="0" column="0">
+         <widget class="QTableWidget" name="tableWidget"/>
+        </item>
+       </layout>
+      </widget>
+     </widget>
+    </item>
+   </layout>
+  </widget>
+  <widget class="QMenuBar" name="menubar">
+   <property name="geometry">
+    <rect>
+     <x>0</x>
+     <y>0</y>
+     <width>1083</width>
+     <height>26</height>
+    </rect>
+   </property>
+   <widget class="QMenu" name="menuFile">
+    <property name="title">
+     <string>File</string>
+    </property>
+    <addaction name="actionSave"/>
+    <addaction name="actionSettings"/>
+    <addaction name="actionClose"/>
+   </widget>
+   <widget class="QMenu" name="menuCamera_Settings">
+    <property name="title">
+     <string>Camera Settings</string>
+    </property>
+    <addaction name="actionFollow"/>
+   </widget>
+   <widget class="QMenu" name="menuBoard_select">
+    <property name="title">
+     <string>Board select</string>
+    </property>
+    <addaction name="actionMaster"/>
+    <addaction name="actionSlave"/>
+    <addaction name="actionFusion"/>
+   </widget>
+   <addaction name="menuFile"/>
+   <addaction name="menuCamera_Settings"/>
+   <addaction name="menuBoard_select"/>
+  </widget>
+  <widget class="QStatusBar" name="statusbar"/>
+  <action name="actionClose">
+   <property name="text">
+    <string>Close</string>
+   </property>
+  </action>
+  <action name="actionFollow">
+   <property name="checkable">
+    <bool>true</bool>
+   </property>
+   <property name="checked">
+    <bool>true</bool>
+   </property>
+   <property name="text">
+    <string>Follow</string>
+   </property>
+  </action>
+  <action name="actionReset">
+   <property name="text">
+    <string>Reset</string>
+   </property>
+  </action>
+  <action name="actionSave">
+   <property name="text">
+    <string>Save</string>
+   </property>
+  </action>
+  <action name="actionSettings">
+   <property name="text">
+    <string>Settings</string>
+   </property>
+  </action>
+  <action name="actionMaster">
+   <property name="text">
+    <string>Master</string>
+   </property>
+  </action>
+  <action name="actionSlave">
+   <property name="text">
+    <string>Slave</string>
+   </property>
+  </action>
+  <action name="actionFusion">
+   <property name="text">
+    <string>Fusion</string>
+   </property>
+  </action>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>

+ 531 - 0
groundstation/main_ui.py

@@ -0,0 +1,531 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'main.ui'
+#
+# Created by: PyQt5 UI code generator 5.10.1
+#
+# WARNING! All changes made in this file will be lost!
+
+from PyQt5 import QtCore, QtGui, QtWidgets
+
+class Ui_MainWindow(object):
+    def setupUi(self, MainWindow):
+        MainWindow.setObjectName("MainWindow")
+        MainWindow.resize(1083, 832)
+        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred)
+        sizePolicy.setHorizontalStretch(0)
+        sizePolicy.setVerticalStretch(0)
+        sizePolicy.setHeightForWidth(MainWindow.sizePolicy().hasHeightForWidth())
+        MainWindow.setSizePolicy(sizePolicy)
+        MainWindow.setMinimumSize(QtCore.QSize(1056, 670))
+        icon = QtGui.QIcon()
+        icon.addPixmap(QtGui.QPixmap("../../../../.designer/backup/pp.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
+        MainWindow.setWindowIcon(icon)
+        self.centralwidget = QtWidgets.QWidget(MainWindow)
+        self.centralwidget.setObjectName("centralwidget")
+        self.gridLayout_2 = QtWidgets.QGridLayout(self.centralwidget)
+        self.gridLayout_2.setObjectName("gridLayout_2")
+        self.gridLayout = QtWidgets.QGridLayout()
+        self.gridLayout.setObjectName("gridLayout")
+        self.verticalLayout_2 = QtWidgets.QVBoxLayout()
+        self.verticalLayout_2.setObjectName("verticalLayout_2")
+        self.groupBox = QtWidgets.QGroupBox(self.centralwidget)
+        self.groupBox.setMinimumSize(QtCore.QSize(306, 0))
+        self.groupBox.setObjectName("groupBox")
+        self.gridLayout_5 = QtWidgets.QGridLayout(self.groupBox)
+        self.gridLayout_5.setObjectName("gridLayout_5")
+        self.label_6 = QtWidgets.QLabel(self.groupBox)
+        self.label_6.setObjectName("label_6")
+        self.gridLayout_5.addWidget(self.label_6, 2, 0, 1, 1)
+        self.labelLink = QtWidgets.QLabel(self.groupBox)
+        font = QtGui.QFont()
+        font.setBold(True)
+        font.setWeight(75)
+        self.labelLink.setFont(font)
+        self.labelLink.setStyleSheet("color:white;background-color:red;")
+        self.labelLink.setObjectName("labelLink")
+        self.gridLayout_5.addWidget(self.labelLink, 1, 1, 1, 1)
+        self.label_2 = QtWidgets.QLabel(self.groupBox)
+        self.label_2.setObjectName("label_2")
+        self.gridLayout_5.addWidget(self.label_2, 1, 0, 1, 1)
+        self.labelLink_Slave = QtWidgets.QLabel(self.groupBox)
+        self.labelLink_Slave.setObjectName("labelLink_Slave")
+        self.gridLayout_5.addWidget(self.labelLink_Slave, 4, 1, 1, 1)
+        self.label_25 = QtWidgets.QLabel(self.groupBox)
+        self.label_25.setObjectName("label_25")
+        self.gridLayout_5.addWidget(self.label_25, 4, 0, 1, 1)
+        self.label = QtWidgets.QLabel(self.groupBox)
+        self.label.setObjectName("label")
+        self.gridLayout_5.addWidget(self.label, 0, 0, 1, 1)
+        self.labelLink_Master = QtWidgets.QLabel(self.groupBox)
+        self.labelLink_Master.setObjectName("labelLink_Master")
+        self.gridLayout_5.addWidget(self.labelLink_Master, 3, 1, 1, 1)
+        self.labelLatency = QtWidgets.QLabel(self.groupBox)
+        self.labelLatency.setObjectName("labelLatency")
+        self.gridLayout_5.addWidget(self.labelLatency, 2, 1, 1, 1)
+        self.label_3 = QtWidgets.QLabel(self.groupBox)
+        self.label_3.setObjectName("label_3")
+        self.gridLayout_5.addWidget(self.label_3, 3, 0, 1, 1)
+        self.labelAddress = QtWidgets.QLabel(self.groupBox)
+        self.labelAddress.setObjectName("labelAddress")
+        self.gridLayout_5.addWidget(self.labelAddress, 0, 1, 1, 1)
+        self.verticalLayout_2.addWidget(self.groupBox)
+        self.scrollArea_2 = QtWidgets.QScrollArea(self.centralwidget)
+        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
+        sizePolicy.setHorizontalStretch(0)
+        sizePolicy.setVerticalStretch(0)
+        sizePolicy.setHeightForWidth(self.scrollArea_2.sizePolicy().hasHeightForWidth())
+        self.scrollArea_2.setSizePolicy(sizePolicy)
+        self.scrollArea_2.setMinimumSize(QtCore.QSize(400, 0))
+        self.scrollArea_2.setFrameShape(QtWidgets.QFrame.NoFrame)
+        self.scrollArea_2.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
+        self.scrollArea_2.setWidgetResizable(True)
+        self.scrollArea_2.setObjectName("scrollArea_2")
+        self.scrollAreaWidgetContents_2 = QtWidgets.QWidget()
+        self.scrollAreaWidgetContents_2.setGeometry(QtCore.QRect(0, 0, 379, 763))
+        self.scrollAreaWidgetContents_2.setObjectName("scrollAreaWidgetContents_2")
+        self.gridLayout_13 = QtWidgets.QGridLayout(self.scrollAreaWidgetContents_2)
+        self.gridLayout_13.setContentsMargins(0, 0, 0, 0)
+        self.gridLayout_13.setObjectName("gridLayout_13")
+        self.groupBox_6 = QtWidgets.QGroupBox(self.scrollAreaWidgetContents_2)
+        self.groupBox_6.setObjectName("groupBox_6")
+        self.gridLayout_11 = QtWidgets.QGridLayout(self.groupBox_6)
+        self.gridLayout_11.setObjectName("gridLayout_11")
+        self.label_alt_1 = QtWidgets.QLabel(self.groupBox_6)
+        self.label_alt_1.setObjectName("label_alt_1")
+        self.gridLayout_11.addWidget(self.label_alt_1, 2, 1, 1, 1)
+        self.label_21 = QtWidgets.QLabel(self.groupBox_6)
+        self.label_21.setObjectName("label_21")
+        self.gridLayout_11.addWidget(self.label_21, 2, 0, 1, 1)
+        self.label_29 = QtWidgets.QLabel(self.groupBox_6)
+        self.label_29.setObjectName("label_29")
+        self.gridLayout_11.addWidget(self.label_29, 1, 0, 1, 1)
+        self.label_lon_1 = QtWidgets.QLabel(self.groupBox_6)
+        self.label_lon_1.setObjectName("label_lon_1")
+        self.gridLayout_11.addWidget(self.label_lon_1, 1, 1, 1, 1)
+        self.label_lat_1 = QtWidgets.QLabel(self.groupBox_6)
+        self.label_lat_1.setObjectName("label_lat_1")
+        self.gridLayout_11.addWidget(self.label_lat_1, 0, 1, 1, 1)
+        self.label_27 = QtWidgets.QLabel(self.groupBox_6)
+        self.label_27.setObjectName("label_27")
+        self.gridLayout_11.addWidget(self.label_27, 0, 0, 1, 1)
+        self.gridLayout_13.addWidget(self.groupBox_6, 0, 0, 1, 1)
+        self.groupBox_5 = QtWidgets.QGroupBox(self.scrollAreaWidgetContents_2)
+        self.groupBox_5.setObjectName("groupBox_5")
+        self.gridLayout_9 = QtWidgets.QGridLayout(self.groupBox_5)
+        self.gridLayout_9.setObjectName("gridLayout_9")
+        self.label_9 = QtWidgets.QLabel(self.groupBox_5)
+        self.label_9.setObjectName("label_9")
+        self.gridLayout_9.addWidget(self.label_9, 0, 0, 1, 1)
+        self.label_20 = QtWidgets.QLabel(self.groupBox_5)
+        self.label_20.setObjectName("label_20")
+        self.gridLayout_9.addWidget(self.label_20, 1, 0, 1, 1)
+        self.label_pres_cis_1 = QtWidgets.QLabel(self.groupBox_5)
+        self.label_pres_cis_1.setObjectName("label_pres_cis_1")
+        self.gridLayout_9.addWidget(self.label_pres_cis_1, 0, 1, 1, 1)
+        self.label_pres_i2c_1 = QtWidgets.QLabel(self.groupBox_5)
+        self.label_pres_i2c_1.setObjectName("label_pres_i2c_1")
+        self.gridLayout_9.addWidget(self.label_pres_i2c_1, 1, 1, 1, 1)
+        self.gridLayout_13.addWidget(self.groupBox_5, 3, 0, 1, 1)
+        self.groupBox_4 = QtWidgets.QGroupBox(self.scrollAreaWidgetContents_2)
+        self.groupBox_4.setObjectName("groupBox_4")
+        self.gridLayout_7 = QtWidgets.QGridLayout(self.groupBox_4)
+        self.gridLayout_7.setObjectName("gridLayout_7")
+        self.label_GyX = QtWidgets.QLabel(self.groupBox_4)
+        self.label_GyX.setObjectName("label_GyX")
+        self.gridLayout_7.addWidget(self.label_GyX, 1, 1, 1, 1)
+        self.label_AcX = QtWidgets.QLabel(self.groupBox_4)
+        self.label_AcX.setObjectName("label_AcX")
+        self.gridLayout_7.addWidget(self.label_AcX, 0, 1, 1, 1)
+        self.label_16 = QtWidgets.QLabel(self.groupBox_4)
+        self.label_16.setObjectName("label_16")
+        self.gridLayout_7.addWidget(self.label_16, 1, 3, 1, 1)
+        self.label_GyY = QtWidgets.QLabel(self.groupBox_4)
+        self.label_GyY.setObjectName("label_GyY")
+        self.gridLayout_7.addWidget(self.label_GyY, 1, 4, 1, 1)
+        self.label_15 = QtWidgets.QLabel(self.groupBox_4)
+        self.label_15.setObjectName("label_15")
+        self.gridLayout_7.addWidget(self.label_15, 1, 0, 1, 1)
+        self.label_5 = QtWidgets.QLabel(self.groupBox_4)
+        self.label_5.setObjectName("label_5")
+        self.gridLayout_7.addWidget(self.label_5, 0, 0, 1, 1)
+        self.label_AcY = QtWidgets.QLabel(self.groupBox_4)
+        self.label_AcY.setObjectName("label_AcY")
+        self.gridLayout_7.addWidget(self.label_AcY, 0, 4, 1, 1)
+        self.label_11 = QtWidgets.QLabel(self.groupBox_4)
+        self.label_11.setObjectName("label_11")
+        self.gridLayout_7.addWidget(self.label_11, 0, 3, 1, 1)
+        self.label_GyZ = QtWidgets.QLabel(self.groupBox_4)
+        self.label_GyZ.setObjectName("label_GyZ")
+        self.gridLayout_7.addWidget(self.label_GyZ, 1, 6, 1, 1)
+        self.label_AcZ = QtWidgets.QLabel(self.groupBox_4)
+        self.label_AcZ.setObjectName("label_AcZ")
+        self.gridLayout_7.addWidget(self.label_AcZ, 0, 6, 1, 1)
+        self.label_17 = QtWidgets.QLabel(self.groupBox_4)
+        self.label_17.setObjectName("label_17")
+        self.gridLayout_7.addWidget(self.label_17, 1, 5, 1, 1)
+        self.label_13 = QtWidgets.QLabel(self.groupBox_4)
+        self.label_13.setObjectName("label_13")
+        self.gridLayout_7.addWidget(self.label_13, 0, 5, 1, 1)
+        self.label_8 = QtWidgets.QLabel(self.groupBox_4)
+        self.label_8.setObjectName("label_8")
+        self.gridLayout_7.addWidget(self.label_8, 2, 0, 1, 1)
+        self.label_MaX = QtWidgets.QLabel(self.groupBox_4)
+        self.label_MaX.setObjectName("label_MaX")
+        self.gridLayout_7.addWidget(self.label_MaX, 2, 1, 1, 1)
+        self.label_12 = QtWidgets.QLabel(self.groupBox_4)
+        self.label_12.setObjectName("label_12")
+        self.gridLayout_7.addWidget(self.label_12, 2, 3, 1, 1)
+        self.label_MaY = QtWidgets.QLabel(self.groupBox_4)
+        self.label_MaY.setObjectName("label_MaY")
+        self.gridLayout_7.addWidget(self.label_MaY, 2, 4, 1, 1)
+        self.label_18 = QtWidgets.QLabel(self.groupBox_4)
+        self.label_18.setObjectName("label_18")
+        self.gridLayout_7.addWidget(self.label_18, 2, 5, 1, 1)
+        self.label_MaZ = QtWidgets.QLabel(self.groupBox_4)
+        self.label_MaZ.setObjectName("label_MaZ")
+        self.gridLayout_7.addWidget(self.label_MaZ, 2, 6, 1, 1)
+        self.gridLayout_13.addWidget(self.groupBox_4, 1, 0, 1, 1)
+        self.groupBox_8 = QtWidgets.QGroupBox(self.scrollAreaWidgetContents_2)
+        self.groupBox_8.setObjectName("groupBox_8")
+        self.gridLayout_8 = QtWidgets.QGridLayout(self.groupBox_8)
+        self.gridLayout_8.setObjectName("gridLayout_8")
+        self.label_30 = QtWidgets.QLabel(self.groupBox_8)
+        self.label_30.setObjectName("label_30")
+        self.gridLayout_8.addWidget(self.label_30, 2, 0, 1, 1)
+        self.label_PosX = QtWidgets.QLabel(self.groupBox_8)
+        self.label_PosX.setObjectName("label_PosX")
+        self.gridLayout_8.addWidget(self.label_PosX, 2, 1, 1, 1)
+        self.label_34 = QtWidgets.QLabel(self.groupBox_8)
+        self.label_34.setObjectName("label_34")
+        self.gridLayout_8.addWidget(self.label_34, 2, 2, 1, 1)
+        self.label_GaZ = QtWidgets.QLabel(self.groupBox_8)
+        self.label_GaZ.setObjectName("label_GaZ")
+        self.gridLayout_8.addWidget(self.label_GaZ, 0, 5, 1, 1)
+        self.label_19 = QtWidgets.QLabel(self.groupBox_8)
+        self.label_19.setObjectName("label_19")
+        self.gridLayout_8.addWidget(self.label_19, 1, 2, 1, 1)
+        self.label_23 = QtWidgets.QLabel(self.groupBox_8)
+        self.label_23.setObjectName("label_23")
+        self.gridLayout_8.addWidget(self.label_23, 1, 0, 1, 1)
+        self.label_RotZ = QtWidgets.QLabel(self.groupBox_8)
+        self.label_RotZ.setObjectName("label_RotZ")
+        self.gridLayout_8.addWidget(self.label_RotZ, 1, 5, 1, 1)
+        self.label_28 = QtWidgets.QLabel(self.groupBox_8)
+        self.label_28.setObjectName("label_28")
+        self.gridLayout_8.addWidget(self.label_28, 0, 4, 1, 1)
+        self.label_GaX = QtWidgets.QLabel(self.groupBox_8)
+        self.label_GaX.setObjectName("label_GaX")
+        self.gridLayout_8.addWidget(self.label_GaX, 0, 1, 1, 1)
+        self.label_26 = QtWidgets.QLabel(self.groupBox_8)
+        self.label_26.setObjectName("label_26")
+        self.gridLayout_8.addWidget(self.label_26, 1, 4, 1, 1)
+        self.label_7 = QtWidgets.QLabel(self.groupBox_8)
+        self.label_7.setObjectName("label_7")
+        self.gridLayout_8.addWidget(self.label_7, 0, 0, 1, 1)
+        self.label_RotY = QtWidgets.QLabel(self.groupBox_8)
+        self.label_RotY.setObjectName("label_RotY")
+        self.gridLayout_8.addWidget(self.label_RotY, 1, 3, 1, 1)
+        self.label_GaY = QtWidgets.QLabel(self.groupBox_8)
+        self.label_GaY.setObjectName("label_GaY")
+        self.gridLayout_8.addWidget(self.label_GaY, 0, 3, 1, 1)
+        self.label_RotX = QtWidgets.QLabel(self.groupBox_8)
+        self.label_RotX.setObjectName("label_RotX")
+        self.gridLayout_8.addWidget(self.label_RotX, 1, 1, 1, 1)
+        self.label_14 = QtWidgets.QLabel(self.groupBox_8)
+        self.label_14.setObjectName("label_14")
+        self.gridLayout_8.addWidget(self.label_14, 0, 2, 1, 1)
+        self.label_36 = QtWidgets.QLabel(self.groupBox_8)
+        self.label_36.setObjectName("label_36")
+        self.gridLayout_8.addWidget(self.label_36, 2, 4, 1, 1)
+        self.label_PosY = QtWidgets.QLabel(self.groupBox_8)
+        self.label_PosY.setObjectName("label_PosY")
+        self.gridLayout_8.addWidget(self.label_PosY, 2, 3, 1, 1)
+        self.label_PosZ = QtWidgets.QLabel(self.groupBox_8)
+        self.label_PosZ.setObjectName("label_PosZ")
+        self.gridLayout_8.addWidget(self.label_PosZ, 2, 5, 1, 1)
+        self.gridLayout_13.addWidget(self.groupBox_8, 2, 0, 1, 1)
+        self.groupBox_2 = QtWidgets.QGroupBox(self.scrollAreaWidgetContents_2)
+        self.groupBox_2.setObjectName("groupBox_2")
+        self.gridLayout_6 = QtWidgets.QGridLayout(self.groupBox_2)
+        self.gridLayout_6.setObjectName("gridLayout_6")
+        self.label_temp_adc_1 = QtWidgets.QLabel(self.groupBox_2)
+        self.label_temp_adc_1.setObjectName("label_temp_adc_1")
+        self.gridLayout_6.addWidget(self.label_temp_adc_1, 0, 1, 1, 1)
+        self.label_4 = QtWidgets.QLabel(self.groupBox_2)
+        self.label_4.setObjectName("label_4")
+        self.gridLayout_6.addWidget(self.label_4, 0, 0, 1, 1)
+        self.label_temp_mpu_1 = QtWidgets.QLabel(self.groupBox_2)
+        self.label_temp_mpu_1.setObjectName("label_temp_mpu_1")
+        self.gridLayout_6.addWidget(self.label_temp_mpu_1, 2, 1, 1, 1)
+        self.label_temp_cis_1 = QtWidgets.QLabel(self.groupBox_2)
+        self.label_temp_cis_1.setObjectName("label_temp_cis_1")
+        self.gridLayout_6.addWidget(self.label_temp_cis_1, 1, 1, 1, 1)
+        self.label_24 = QtWidgets.QLabel(self.groupBox_2)
+        self.label_24.setObjectName("label_24")
+        self.gridLayout_6.addWidget(self.label_24, 2, 0, 1, 1)
+        self.label_22 = QtWidgets.QLabel(self.groupBox_2)
+        self.label_22.setObjectName("label_22")
+        self.gridLayout_6.addWidget(self.label_22, 1, 0, 1, 1)
+        self.label_37 = QtWidgets.QLabel(self.groupBox_2)
+        self.label_37.setObjectName("label_37")
+        self.gridLayout_6.addWidget(self.label_37, 3, 0, 1, 1)
+        self.label_temp_mag_1 = QtWidgets.QLabel(self.groupBox_2)
+        self.label_temp_mag_1.setObjectName("label_temp_mag_1")
+        self.gridLayout_6.addWidget(self.label_temp_mag_1, 3, 1, 1, 1)
+        self.label_temp_i2c = QtWidgets.QLabel(self.groupBox_2)
+        self.label_temp_i2c.setObjectName("label_temp_i2c")
+        self.gridLayout_6.addWidget(self.label_temp_i2c, 4, 1, 1, 1)
+        self.label_33 = QtWidgets.QLabel(self.groupBox_2)
+        self.label_33.setObjectName("label_33")
+        self.gridLayout_6.addWidget(self.label_33, 4, 0, 1, 1)
+        self.label_38 = QtWidgets.QLabel(self.groupBox_2)
+        self.label_38.setObjectName("label_38")
+        self.gridLayout_6.addWidget(self.label_38, 5, 0, 1, 1)
+        self.label_temp_mp = QtWidgets.QLabel(self.groupBox_2)
+        self.label_temp_mp.setObjectName("label_temp_mp")
+        self.gridLayout_6.addWidget(self.label_temp_mp, 5, 1, 1, 1)
+        self.gridLayout_13.addWidget(self.groupBox_2, 4, 0, 1, 1)
+        self.groupBox_7 = QtWidgets.QGroupBox(self.scrollAreaWidgetContents_2)
+        self.groupBox_7.setObjectName("groupBox_7")
+        self.gridLayout_12 = QtWidgets.QGridLayout(self.groupBox_7)
+        self.gridLayout_12.setObjectName("gridLayout_12")
+        self.label_35 = QtWidgets.QLabel(self.groupBox_7)
+        self.label_35.setObjectName("label_35")
+        self.gridLayout_12.addWidget(self.label_35, 1, 0, 1, 1)
+        self.label_hdop_1 = QtWidgets.QLabel(self.groupBox_7)
+        self.label_hdop_1.setObjectName("label_hdop_1")
+        self.gridLayout_12.addWidget(self.label_hdop_1, 1, 1, 1, 1)
+        self.label_31 = QtWidgets.QLabel(self.groupBox_7)
+        self.label_31.setObjectName("label_31")
+        self.gridLayout_12.addWidget(self.label_31, 0, 0, 1, 1)
+        self.label_time_1 = QtWidgets.QLabel(self.groupBox_7)
+        self.label_time_1.setObjectName("label_time_1")
+        self.gridLayout_12.addWidget(self.label_time_1, 0, 1, 1, 1)
+        self.gridLayout_13.addWidget(self.groupBox_7, 5, 0, 1, 1)
+        self.groupBox_9 = QtWidgets.QGroupBox(self.scrollAreaWidgetContents_2)
+        self.groupBox_9.setObjectName("groupBox_9")
+        self.gridLayout_15 = QtWidgets.QGridLayout(self.groupBox_9)
+        self.gridLayout_15.setObjectName("gridLayout_15")
+        self.label_10 = QtWidgets.QLabel(self.groupBox_9)
+        self.label_10.setObjectName("label_10")
+        self.gridLayout_15.addWidget(self.label_10, 0, 0, 1, 1)
+        self.label_current = QtWidgets.QLabel(self.groupBox_9)
+        self.label_current.setObjectName("label_current")
+        self.gridLayout_15.addWidget(self.label_current, 1, 1, 1, 1)
+        self.label_voltage = QtWidgets.QLabel(self.groupBox_9)
+        self.label_voltage.setObjectName("label_voltage")
+        self.gridLayout_15.addWidget(self.label_voltage, 0, 1, 1, 1)
+        self.label_32 = QtWidgets.QLabel(self.groupBox_9)
+        self.label_32.setObjectName("label_32")
+        self.gridLayout_15.addWidget(self.label_32, 1, 0, 1, 1)
+        self.gridLayout_13.addWidget(self.groupBox_9, 6, 0, 1, 1)
+        spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
+        self.gridLayout_13.addItem(spacerItem, 9, 0, 1, 1)
+        self.scrollArea_2.setWidget(self.scrollAreaWidgetContents_2)
+        self.verticalLayout_2.addWidget(self.scrollArea_2)
+        self.progressBar_reset = QtWidgets.QProgressBar(self.centralwidget)
+        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed)
+        sizePolicy.setHorizontalStretch(0)
+        sizePolicy.setVerticalStretch(0)
+        sizePolicy.setHeightForWidth(self.progressBar_reset.sizePolicy().hasHeightForWidth())
+        self.progressBar_reset.setSizePolicy(sizePolicy)
+        self.progressBar_reset.setProperty("value", 0)
+        self.progressBar_reset.setTextVisible(False)
+        self.progressBar_reset.setObjectName("progressBar_reset")
+        self.verticalLayout_2.addWidget(self.progressBar_reset)
+        self.pushButton_reset = QtWidgets.QPushButton(self.centralwidget)
+        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed)
+        sizePolicy.setHorizontalStretch(0)
+        sizePolicy.setVerticalStretch(0)
+        sizePolicy.setHeightForWidth(self.pushButton_reset.sizePolicy().hasHeightForWidth())
+        self.pushButton_reset.setSizePolicy(sizePolicy)
+        self.pushButton_reset.setObjectName("pushButton_reset")
+        self.verticalLayout_2.addWidget(self.pushButton_reset)
+        self.gridLayout.addLayout(self.verticalLayout_2, 0, 0, 1, 1)
+        self.gridLayout_2.addLayout(self.gridLayout, 0, 1, 1, 1)
+        self.tabWidget = QtWidgets.QTabWidget(self.centralwidget)
+        self.tabWidget.setObjectName("tabWidget")
+        self.tab = QtWidgets.QWidget()
+        self.tab.setObjectName("tab")
+        self.gridLayout_4 = QtWidgets.QGridLayout(self.tab)
+        self.gridLayout_4.setObjectName("gridLayout_4")
+        self.openGLWidget = QtWidgets.QOpenGLWidget(self.tab)
+        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding)
+        sizePolicy.setHorizontalStretch(0)
+        sizePolicy.setVerticalStretch(0)
+        sizePolicy.setHeightForWidth(self.openGLWidget.sizePolicy().hasHeightForWidth())
+        self.openGLWidget.setSizePolicy(sizePolicy)
+        self.openGLWidget.setMouseTracking(False)
+        self.openGLWidget.setObjectName("openGLWidget")
+        self.gridLayout_4.addWidget(self.openGLWidget, 0, 0, 1, 1)
+        self.tabWidget.addTab(self.tab, "")
+        self.tab_3 = QtWidgets.QWidget()
+        self.tab_3.setObjectName("tab_3")
+        self.gridLayout_3 = QtWidgets.QGridLayout(self.tab_3)
+        self.gridLayout_3.setObjectName("gridLayout_3")
+        self.openGLWidgetQuad = QtWidgets.QOpenGLWidget(self.tab_3)
+        self.openGLWidgetQuad.setObjectName("openGLWidgetQuad")
+        self.gridLayout_3.addWidget(self.openGLWidgetQuad, 0, 0, 1, 1)
+        self.tabWidget.addTab(self.tab_3, "")
+        self.tab_2 = QtWidgets.QWidget()
+        self.tab_2.setObjectName("tab_2")
+        self.gridLayout_10 = QtWidgets.QGridLayout(self.tab_2)
+        self.gridLayout_10.setObjectName("gridLayout_10")
+        self.tableWidget = QtWidgets.QTableWidget(self.tab_2)
+        self.tableWidget.setObjectName("tableWidget")
+        self.tableWidget.setColumnCount(0)
+        self.tableWidget.setRowCount(0)
+        self.gridLayout_10.addWidget(self.tableWidget, 0, 0, 1, 1)
+        self.tabWidget.addTab(self.tab_2, "")
+        self.gridLayout_2.addWidget(self.tabWidget, 0, 0, 1, 1)
+        MainWindow.setCentralWidget(self.centralwidget)
+        self.menubar = QtWidgets.QMenuBar(MainWindow)
+        self.menubar.setGeometry(QtCore.QRect(0, 0, 1083, 26))
+        self.menubar.setObjectName("menubar")
+        self.menuFile = QtWidgets.QMenu(self.menubar)
+        self.menuFile.setObjectName("menuFile")
+        self.menuCamera_Settings = QtWidgets.QMenu(self.menubar)
+        self.menuCamera_Settings.setObjectName("menuCamera_Settings")
+        self.menuBoard_select = QtWidgets.QMenu(self.menubar)
+        self.menuBoard_select.setObjectName("menuBoard_select")
+        MainWindow.setMenuBar(self.menubar)
+        self.statusbar = QtWidgets.QStatusBar(MainWindow)
+        self.statusbar.setObjectName("statusbar")
+        MainWindow.setStatusBar(self.statusbar)
+        self.actionClose = QtWidgets.QAction(MainWindow)
+        self.actionClose.setObjectName("actionClose")
+        self.actionFollow = QtWidgets.QAction(MainWindow)
+        self.actionFollow.setCheckable(True)
+        self.actionFollow.setChecked(True)
+        self.actionFollow.setObjectName("actionFollow")
+        self.actionReset = QtWidgets.QAction(MainWindow)
+        self.actionReset.setObjectName("actionReset")
+        self.actionSave = QtWidgets.QAction(MainWindow)
+        self.actionSave.setObjectName("actionSave")
+        self.actionSettings = QtWidgets.QAction(MainWindow)
+        self.actionSettings.setObjectName("actionSettings")
+        self.actionMaster = QtWidgets.QAction(MainWindow)
+        self.actionMaster.setObjectName("actionMaster")
+        self.actionSlave = QtWidgets.QAction(MainWindow)
+        self.actionSlave.setObjectName("actionSlave")
+        self.actionFusion = QtWidgets.QAction(MainWindow)
+        self.actionFusion.setObjectName("actionFusion")
+        self.menuFile.addAction(self.actionSave)
+        self.menuFile.addAction(self.actionSettings)
+        self.menuFile.addAction(self.actionClose)
+        self.menuCamera_Settings.addAction(self.actionFollow)
+        self.menuBoard_select.addAction(self.actionMaster)
+        self.menuBoard_select.addAction(self.actionSlave)
+        self.menuBoard_select.addAction(self.actionFusion)
+        self.menubar.addAction(self.menuFile.menuAction())
+        self.menubar.addAction(self.menuCamera_Settings.menuAction())
+        self.menubar.addAction(self.menuBoard_select.menuAction())
+
+        self.retranslateUi(MainWindow)
+        self.tabWidget.setCurrentIndex(2)
+        QtCore.QMetaObject.connectSlotsByName(MainWindow)
+
+    def retranslateUi(self, MainWindow):
+        _translate = QtCore.QCoreApplication.translate
+        MainWindow.setWindowTitle(_translate("MainWindow", "BEXUS"))
+        self.groupBox.setTitle(_translate("MainWindow", "Stats"))
+        self.label_6.setText(_translate("MainWindow", "Latency:"))
+        self.labelLink.setText(_translate("MainWindow", "Offline"))
+        self.label_2.setText(_translate("MainWindow", "Link:"))
+        self.labelLink_Slave.setText(_translate("MainWindow", "0/s"))
+        self.label_25.setText(_translate("MainWindow", "MCU Slave:"))
+        self.label.setText(_translate("MainWindow", "Address:"))
+        self.labelLink_Master.setText(_translate("MainWindow", "0/s"))
+        self.labelLatency.setText(_translate("MainWindow", "20ms"))
+        self.label_3.setText(_translate("MainWindow", "MCU Master:"))
+        self.labelAddress.setText(_translate("MainWindow", "192.168.0.8:1234"))
+        self.groupBox_6.setTitle(_translate("MainWindow", "Position"))
+        self.label_alt_1.setText(_translate("MainWindow", "-"))
+        self.label_21.setText(_translate("MainWindow", "Altitude:"))
+        self.label_29.setText(_translate("MainWindow", "Longitude"))
+        self.label_lon_1.setText(_translate("MainWindow", "-"))
+        self.label_lat_1.setText(_translate("MainWindow", "-"))
+        self.label_27.setText(_translate("MainWindow", "Latitude"))
+        self.groupBox_5.setTitle(_translate("MainWindow", "Pressure"))
+        self.label_9.setText(_translate("MainWindow", "Cis"))
+        self.label_20.setText(_translate("MainWindow", "SPI"))
+        self.label_pres_cis_1.setText(_translate("MainWindow", "-"))
+        self.label_pres_i2c_1.setText(_translate("MainWindow", "-"))
+        self.groupBox_4.setTitle(_translate("MainWindow", "IMU-Raw"))
+        self.label_GyX.setText(_translate("MainWindow", "0"))
+        self.label_AcX.setText(_translate("MainWindow", "0"))
+        self.label_16.setText(_translate("MainWindow", "GyY:"))
+        self.label_GyY.setText(_translate("MainWindow", "0"))
+        self.label_15.setText(_translate("MainWindow", "GyX:"))
+        self.label_5.setText(_translate("MainWindow", "AcX:"))
+        self.label_AcY.setText(_translate("MainWindow", "0"))
+        self.label_11.setText(_translate("MainWindow", "AcY:"))
+        self.label_GyZ.setText(_translate("MainWindow", "0"))
+        self.label_AcZ.setText(_translate("MainWindow", "0"))
+        self.label_17.setText(_translate("MainWindow", "GyZ:"))
+        self.label_13.setText(_translate("MainWindow", "AcZ:"))
+        self.label_8.setText(_translate("MainWindow", "MaX:"))
+        self.label_MaX.setText(_translate("MainWindow", "0"))
+        self.label_12.setText(_translate("MainWindow", "MaY:"))
+        self.label_MaY.setText(_translate("MainWindow", "0"))
+        self.label_18.setText(_translate("MainWindow", "MaZ:"))
+        self.label_MaZ.setText(_translate("MainWindow", "0"))
+        self.groupBox_8.setTitle(_translate("MainWindow", "IMU-Fusion"))
+        self.label_30.setText(_translate("MainWindow", "PosX:"))
+        self.label_PosX.setText(_translate("MainWindow", "0"))
+        self.label_34.setText(_translate("MainWindow", "PosY:"))
+        self.label_GaZ.setText(_translate("MainWindow", "0"))
+        self.label_19.setText(_translate("MainWindow", "RotY:"))
+        self.label_23.setText(_translate("MainWindow", "RotX:"))
+        self.label_RotZ.setText(_translate("MainWindow", "0"))
+        self.label_28.setText(_translate("MainWindow", "AcZ:"))
+        self.label_GaX.setText(_translate("MainWindow", "0"))
+        self.label_26.setText(_translate("MainWindow", "RotZ:"))
+        self.label_7.setText(_translate("MainWindow", "AcX:"))
+        self.label_RotY.setText(_translate("MainWindow", "0"))
+        self.label_GaY.setText(_translate("MainWindow", "0"))
+        self.label_RotX.setText(_translate("MainWindow", "0"))
+        self.label_14.setText(_translate("MainWindow", "AcY:"))
+        self.label_36.setText(_translate("MainWindow", "PosZ:"))
+        self.label_PosY.setText(_translate("MainWindow", "0"))
+        self.label_PosZ.setText(_translate("MainWindow", "0"))
+        self.groupBox_2.setTitle(_translate("MainWindow", "Temperature"))
+        self.label_temp_adc_1.setText(_translate("MainWindow", "-"))
+        self.label_4.setText(_translate("MainWindow", "Viehmann"))
+        self.label_temp_mpu_1.setText(_translate("MainWindow", "-"))
+        self.label_temp_cis_1.setText(_translate("MainWindow", "-"))
+        self.label_24.setText(_translate("MainWindow", "MPU"))
+        self.label_22.setText(_translate("MainWindow", "Cis"))
+        self.label_37.setText(_translate("MainWindow", "Mag"))
+        self.label_temp_mag_1.setText(_translate("MainWindow", "-"))
+        self.label_temp_i2c.setText(_translate("MainWindow", "20.00 °C"))
+        self.label_33.setText(_translate("MainWindow", "I2C"))
+        self.label_38.setText(_translate("MainWindow", "µC"))
+        self.label_temp_mp.setText(_translate("MainWindow", "20.00 °C"))
+        self.groupBox_7.setTitle(_translate("MainWindow", "GPS"))
+        self.label_35.setText(_translate("MainWindow", "Hdop"))
+        self.label_hdop_1.setText(_translate("MainWindow", "0"))
+        self.label_31.setText(_translate("MainWindow", "Time"))
+        self.label_time_1.setText(_translate("MainWindow", "12:00"))
+        self.groupBox_9.setTitle(_translate("MainWindow", "Power"))
+        self.label_10.setText(_translate("MainWindow", "Voltage"))
+        self.label_current.setText(_translate("MainWindow", "0.00 A"))
+        self.label_voltage.setText(_translate("MainWindow", "0.00 V"))
+        self.label_32.setText(_translate("MainWindow", "Current"))
+        self.pushButton_reset.setText(_translate("MainWindow", "Restart"))
+        self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab), _translate("MainWindow", "3D View"))
+        self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_3), _translate("MainWindow", "Orthogonal View"))
+        self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_2), _translate("MainWindow", "Raw data"))
+        self.menuFile.setTitle(_translate("MainWindow", "File"))
+        self.menuCamera_Settings.setTitle(_translate("MainWindow", "Camera Settings"))
+        self.menuBoard_select.setTitle(_translate("MainWindow", "Board select"))
+        self.actionClose.setText(_translate("MainWindow", "Close"))
+        self.actionFollow.setText(_translate("MainWindow", "Follow"))
+        self.actionReset.setText(_translate("MainWindow", "Reset"))
+        self.actionSave.setText(_translate("MainWindow", "Save"))
+        self.actionSettings.setText(_translate("MainWindow", "Settings"))
+        self.actionMaster.setText(_translate("MainWindow", "Master"))
+        self.actionSlave.setText(_translate("MainWindow", "Slave"))
+        self.actionFusion.setText(_translate("MainWindow", "Fusion"))
+

+ 832 - 0
groundstation/mainold.ui

@@ -0,0 +1,832 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>MainWindow</class>
+ <widget class="QMainWindow" name="MainWindow">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>1337</width>
+    <height>1073</height>
+   </rect>
+  </property>
+  <property name="sizePolicy">
+   <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+    <horstretch>0</horstretch>
+    <verstretch>0</verstretch>
+   </sizepolicy>
+  </property>
+  <property name="windowTitle">
+   <string>BEXUS</string>
+  </property>
+  <property name="windowIcon">
+   <iconset>
+    <normaloff>../../../../.designer/backup/pp.png</normaloff>../../../../.designer/backup/pp.png</iconset>
+  </property>
+  <widget class="QWidget" name="centralwidget">
+   <layout class="QGridLayout" name="gridLayout_2">
+    <item row="0" column="1">
+     <layout class="QGridLayout" name="gridLayout">
+      <item row="0" column="0">
+       <layout class="QVBoxLayout" name="verticalLayout_2">
+        <item>
+         <widget class="QGroupBox" name="groupBox">
+          <property name="minimumSize">
+           <size>
+            <width>306</width>
+            <height>0</height>
+           </size>
+          </property>
+          <property name="title">
+           <string>Stats</string>
+          </property>
+          <layout class="QGridLayout" name="gridLayout_5">
+           <item row="2" column="0">
+            <widget class="QLabel" name="label_6">
+             <property name="text">
+              <string>Latency:</string>
+             </property>
+            </widget>
+           </item>
+           <item row="1" column="1">
+            <widget class="QLabel" name="labelLink">
+             <property name="font">
+              <font>
+               <weight>75</weight>
+               <bold>true</bold>
+              </font>
+             </property>
+             <property name="styleSheet">
+              <string notr="true">color:red</string>
+             </property>
+             <property name="text">
+              <string>Offline</string>
+             </property>
+            </widget>
+           </item>
+           <item row="1" column="0">
+            <widget class="QLabel" name="label_2">
+             <property name="text">
+              <string>Link:</string>
+             </property>
+            </widget>
+           </item>
+           <item row="4" column="1">
+            <widget class="QLabel" name="labelLink_MCU2">
+             <property name="font">
+              <font>
+               <weight>75</weight>
+               <bold>true</bold>
+              </font>
+             </property>
+             <property name="styleSheet">
+              <string notr="true">color:#ad6d00</string>
+             </property>
+             <property name="text">
+              <string>Unknown</string>
+             </property>
+            </widget>
+           </item>
+           <item row="4" column="0">
+            <widget class="QLabel" name="label_25">
+             <property name="text">
+              <string>MCU 2:</string>
+             </property>
+            </widget>
+           </item>
+           <item row="0" column="0">
+            <widget class="QLabel" name="label">
+             <property name="text">
+              <string>Address:</string>
+             </property>
+            </widget>
+           </item>
+           <item row="3" column="1">
+            <widget class="QLabel" name="labelLink_MCU1">
+             <property name="font">
+              <font>
+               <weight>75</weight>
+               <bold>true</bold>
+              </font>
+             </property>
+             <property name="styleSheet">
+              <string notr="true">color:#ad6d00</string>
+             </property>
+             <property name="text">
+              <string>Unknown</string>
+             </property>
+            </widget>
+           </item>
+           <item row="2" column="1">
+            <widget class="QLabel" name="labelLatency">
+             <property name="text">
+              <string>20ms</string>
+             </property>
+            </widget>
+           </item>
+           <item row="3" column="0">
+            <widget class="QLabel" name="label_3">
+             <property name="text">
+              <string>MCU 1:</string>
+             </property>
+            </widget>
+           </item>
+           <item row="0" column="1">
+            <widget class="QLabel" name="labelAddress">
+             <property name="text">
+              <string>192.168.0.8:1234</string>
+             </property>
+            </widget>
+           </item>
+          </layout>
+         </widget>
+        </item>
+        <item>
+         <widget class="QScrollArea" name="scrollArea_2">
+          <property name="sizePolicy">
+           <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+            <horstretch>0</horstretch>
+            <verstretch>0</verstretch>
+           </sizepolicy>
+          </property>
+          <property name="minimumSize">
+           <size>
+            <width>0</width>
+            <height>500</height>
+           </size>
+          </property>
+          <property name="lineWidth">
+           <number>0</number>
+          </property>
+          <property name="widgetResizable">
+           <bool>true</bool>
+          </property>
+          <widget class="QWidget" name="scrollAreaWidgetContents_2">
+           <property name="geometry">
+            <rect>
+             <x>0</x>
+             <y>0</y>
+             <width>627</width>
+             <height>644</height>
+            </rect>
+           </property>
+           <layout class="QGridLayout" name="gridLayout_13">
+            <property name="leftMargin">
+             <number>0</number>
+            </property>
+            <property name="topMargin">
+             <number>0</number>
+            </property>
+            <property name="rightMargin">
+             <number>0</number>
+            </property>
+            <property name="bottomMargin">
+             <number>20</number>
+            </property>
+            <item row="0" column="0">
+             <widget class="QGroupBox" name="groupBox_6">
+              <property name="title">
+               <string>Position</string>
+              </property>
+              <layout class="QGridLayout" name="gridLayout_11">
+               <item row="2" column="1">
+                <widget class="QLabel" name="label_alt_1">
+                 <property name="text">
+                  <string>165.07 m</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="2" column="0">
+                <widget class="QLabel" name="label_21">
+                 <property name="text">
+                  <string>Altitude:</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="1" column="0">
+                <widget class="QLabel" name="label_29">
+                 <property name="text">
+                  <string>Longitude</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="1" column="1">
+                <widget class="QLabel" name="label_lon_1">
+                 <property name="text">
+                  <string>10.2452</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="0" column="1">
+                <widget class="QLabel" name="label_lat_1">
+                 <property name="text">
+                  <string>51.4562</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="0" column="0">
+                <widget class="QLabel" name="label_27">
+                 <property name="text">
+                  <string>Latitude</string>
+                 </property>
+                </widget>
+               </item>
+              </layout>
+             </widget>
+            </item>
+            <item row="3" column="0">
+             <widget class="QGroupBox" name="groupBox_5">
+              <property name="title">
+               <string>Pressure</string>
+              </property>
+              <layout class="QGridLayout" name="gridLayout_9">
+               <item row="0" column="0">
+                <widget class="QLabel" name="label_9">
+                 <property name="text">
+                  <string>Cis</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="1" column="0">
+                <widget class="QLabel" name="label_20">
+                 <property name="text">
+                  <string>SPI</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="0" column="1">
+                <widget class="QLabel" name="label_pres_cis_1">
+                 <property name="text">
+                  <string>99999.4 Pa</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="1" column="1">
+                <widget class="QLabel" name="label_pres_i2c_1">
+                 <property name="text">
+                  <string>99355.0 Pa</string>
+                 </property>
+                </widget>
+               </item>
+              </layout>
+             </widget>
+            </item>
+            <item row="1" column="0">
+             <widget class="QGroupBox" name="groupBox_4">
+              <property name="title">
+               <string>IMU-Raw</string>
+              </property>
+              <layout class="QGridLayout" name="gridLayout_7">
+               <item row="1" column="1">
+                <widget class="QLabel" name="label_GyX">
+                 <property name="text">
+                  <string>0</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="0" column="1">
+                <widget class="QLabel" name="label_AcX">
+                 <property name="text">
+                  <string>0</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="1" column="3">
+                <widget class="QLabel" name="label_16">
+                 <property name="text">
+                  <string>GyY:</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="1" column="4">
+                <widget class="QLabel" name="label_GyY">
+                 <property name="text">
+                  <string>0</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="1" column="0">
+                <widget class="QLabel" name="label_15">
+                 <property name="text">
+                  <string>GyX:</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="0" column="0">
+                <widget class="QLabel" name="label_5">
+                 <property name="text">
+                  <string>AcX:</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="0" column="4">
+                <widget class="QLabel" name="label_AcY">
+                 <property name="text">
+                  <string>0</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="0" column="3">
+                <widget class="QLabel" name="label_11">
+                 <property name="text">
+                  <string>AcY:</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="1" column="6">
+                <widget class="QLabel" name="label_GyZ">
+                 <property name="text">
+                  <string>0</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="0" column="6">
+                <widget class="QLabel" name="label_AcZ">
+                 <property name="text">
+                  <string>0</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="1" column="5">
+                <widget class="QLabel" name="label_17">
+                 <property name="text">
+                  <string>GyZ:</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="0" column="5">
+                <widget class="QLabel" name="label_13">
+                 <property name="text">
+                  <string>AcZ:</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="2" column="0">
+                <widget class="QLabel" name="label_8">
+                 <property name="text">
+                  <string>MaX:</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="2" column="1">
+                <widget class="QLabel" name="label_MaX">
+                 <property name="text">
+                  <string>0</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="2" column="3">
+                <widget class="QLabel" name="label_12">
+                 <property name="text">
+                  <string>MaY:</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="2" column="4">
+                <widget class="QLabel" name="label_MaY">
+                 <property name="text">
+                  <string>0</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="2" column="5">
+                <widget class="QLabel" name="label_18">
+                 <property name="text">
+                  <string>MaZ:</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="2" column="6">
+                <widget class="QLabel" name="label_MaZ">
+                 <property name="text">
+                  <string>0</string>
+                 </property>
+                </widget>
+               </item>
+              </layout>
+             </widget>
+            </item>
+            <item row="2" column="0">
+             <widget class="QGroupBox" name="groupBox_8">
+              <property name="title">
+               <string>IMU-Fusion</string>
+              </property>
+              <layout class="QGridLayout" name="gridLayout_8">
+               <item row="2" column="0">
+                <widget class="QLabel" name="label_30">
+                 <property name="text">
+                  <string>PosX:</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="2" column="1">
+                <widget class="QLabel" name="label_PosX">
+                 <property name="text">
+                  <string>0</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="2" column="2">
+                <widget class="QLabel" name="label_34">
+                 <property name="text">
+                  <string>PosY:</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="0" column="5">
+                <widget class="QLabel" name="label_GaZ">
+                 <property name="text">
+                  <string>0</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="1" column="2">
+                <widget class="QLabel" name="label_19">
+                 <property name="text">
+                  <string>RotY:</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="1" column="0">
+                <widget class="QLabel" name="label_23">
+                 <property name="text">
+                  <string>RotX:</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="1" column="5">
+                <widget class="QLabel" name="label_RotZ">
+                 <property name="text">
+                  <string>0</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="0" column="4">
+                <widget class="QLabel" name="label_28">
+                 <property name="text">
+                  <string>AcZ:</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="0" column="1">
+                <widget class="QLabel" name="label_GaX">
+                 <property name="text">
+                  <string>0</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="1" column="4">
+                <widget class="QLabel" name="label_26">
+                 <property name="text">
+                  <string>RotZ:</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="0" column="0">
+                <widget class="QLabel" name="label_7">
+                 <property name="text">
+                  <string>AcX:</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="1" column="3">
+                <widget class="QLabel" name="label_RotY">
+                 <property name="text">
+                  <string>0</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="0" column="3">
+                <widget class="QLabel" name="label_GaY">
+                 <property name="text">
+                  <string>0</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="1" column="1">
+                <widget class="QLabel" name="label_RotX">
+                 <property name="text">
+                  <string>0</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="0" column="2">
+                <widget class="QLabel" name="label_14">
+                 <property name="text">
+                  <string>AcY:</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="2" column="4">
+                <widget class="QLabel" name="label_36">
+                 <property name="text">
+                  <string>PosZ:</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="2" column="3">
+                <widget class="QLabel" name="label_PosY">
+                 <property name="text">
+                  <string>0</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="2" column="5">
+                <widget class="QLabel" name="label_PosZ">
+                 <property name="text">
+                  <string>0</string>
+                 </property>
+                </widget>
+               </item>
+              </layout>
+             </widget>
+            </item>
+            <item row="4" column="0">
+             <widget class="QGroupBox" name="groupBox_2">
+              <property name="title">
+               <string>Temperature</string>
+              </property>
+              <layout class="QGridLayout" name="gridLayout_6">
+               <item row="0" column="0">
+                <widget class="QLabel" name="label_4">
+                 <property name="text">
+                  <string>ADC</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="2" column="1">
+                <widget class="QLabel" name="label_temp_mpu_1">
+                 <property name="text">
+                  <string>39.20 °C</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="1" column="1">
+                <widget class="QLabel" name="label_temp_cis_1">
+                 <property name="text">
+                  <string>36.43 °C</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="2" column="0">
+                <widget class="QLabel" name="label_24">
+                 <property name="text">
+                  <string>MPU</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="0" column="1">
+                <widget class="QLabel" name="label_temp_adc_1">
+                 <property name="text">
+                  <string>20.21 °C</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="1" column="0">
+                <widget class="QLabel" name="label_22">
+                 <property name="text">
+                  <string>Cis</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="3" column="0">
+                <widget class="QLabel" name="label_37">
+                 <property name="text">
+                  <string>Mag</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="3" column="1">
+                <widget class="QLabel" name="label_temp_mag_1">
+                 <property name="text">
+                  <string>36.01 °C</string>
+                 </property>
+                </widget>
+               </item>
+              </layout>
+             </widget>
+            </item>
+            <item row="5" column="0">
+             <widget class="QGroupBox" name="groupBox_7">
+              <property name="title">
+               <string>GPS</string>
+              </property>
+              <layout class="QGridLayout" name="gridLayout_12">
+               <item row="1" column="0">
+                <widget class="QLabel" name="label_35">
+                 <property name="text">
+                  <string>Hdop</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="1" column="1">
+                <widget class="QLabel" name="label_hdop_1">
+                 <property name="text">
+                  <string>0</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="0" column="0">
+                <widget class="QLabel" name="label_31">
+                 <property name="text">
+                  <string>Time</string>
+                 </property>
+                </widget>
+               </item>
+               <item row="0" column="1">
+                <widget class="QLabel" name="label_time_1">
+                 <property name="text">
+                  <string>12:00</string>
+                 </property>
+                </widget>
+               </item>
+              </layout>
+             </widget>
+            </item>
+           </layout>
+          </widget>
+         </widget>
+        </item>
+        <item>
+         <spacer name="verticalSpacer">
+          <property name="orientation">
+           <enum>Qt::Vertical</enum>
+          </property>
+          <property name="sizeType">
+           <enum>QSizePolicy::Expanding</enum>
+          </property>
+          <property name="sizeHint" stdset="0">
+           <size>
+            <width>20</width>
+            <height>40</height>
+           </size>
+          </property>
+         </spacer>
+        </item>
+        <item>
+         <widget class="QProgressBar" name="progressBar_reset">
+          <property name="sizePolicy">
+           <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
+            <horstretch>0</horstretch>
+            <verstretch>0</verstretch>
+           </sizepolicy>
+          </property>
+          <property name="value">
+           <number>0</number>
+          </property>
+          <property name="textVisible">
+           <bool>false</bool>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QPushButton" name="pushButton_reset">
+          <property name="sizePolicy">
+           <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
+            <horstretch>0</horstretch>
+            <verstretch>0</verstretch>
+           </sizepolicy>
+          </property>
+          <property name="text">
+           <string>Restart</string>
+          </property>
+         </widget>
+        </item>
+       </layout>
+      </item>
+     </layout>
+    </item>
+    <item row="0" column="0">
+     <widget class="QTabWidget" name="tabWidget">
+      <property name="currentIndex">
+       <number>2</number>
+      </property>
+      <widget class="QWidget" name="tab">
+       <attribute name="title">
+        <string>3D View</string>
+       </attribute>
+       <layout class="QGridLayout" name="gridLayout_4">
+        <item row="0" column="0">
+         <widget class="QOpenGLWidget" name="openGLWidget">
+          <property name="sizePolicy">
+           <sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
+            <horstretch>0</horstretch>
+            <verstretch>0</verstretch>
+           </sizepolicy>
+          </property>
+          <property name="mouseTracking">
+           <bool>false</bool>
+          </property>
+         </widget>
+        </item>
+       </layout>
+      </widget>
+      <widget class="QWidget" name="tab_3">
+       <attribute name="title">
+        <string>Orthogonal View</string>
+       </attribute>
+       <layout class="QGridLayout" name="gridLayout_3">
+        <item row="0" column="0">
+         <widget class="QOpenGLWidget" name="openGLWidgetQuad"/>
+        </item>
+       </layout>
+      </widget>
+      <widget class="QWidget" name="tab_2">
+       <attribute name="title">
+        <string>Raw data</string>
+       </attribute>
+       <layout class="QGridLayout" name="gridLayout_10">
+        <item row="2" column="0">
+         <widget class="QScrollArea" name="scrollArea">
+          <property name="widgetResizable">
+           <bool>true</bool>
+          </property>
+          <widget class="QWidget" name="scrollAreaWidgetContents">
+           <property name="geometry">
+            <rect>
+             <x>0</x>
+             <y>0</y>
+             <width>624</width>
+             <height>947</height>
+            </rect>
+           </property>
+           <layout class="QGridLayout" name="gridLayout_14">
+            <item row="0" column="0">
+             <widget class="QLabel" name="labelTestData">
+              <property name="text">
+               <string>Test Data</string>
+              </property>
+             </widget>
+            </item>
+           </layout>
+          </widget>
+         </widget>
+        </item>
+       </layout>
+      </widget>
+     </widget>
+    </item>
+   </layout>
+  </widget>
+  <widget class="QMenuBar" name="menubar">
+   <property name="geometry">
+    <rect>
+     <x>0</x>
+     <y>0</y>
+     <width>1337</width>
+     <height>26</height>
+    </rect>
+   </property>
+   <widget class="QMenu" name="menuFile">
+    <property name="title">
+     <string>File</string>
+    </property>
+    <addaction name="actionSave"/>
+    <addaction name="actionSettings"/>
+    <addaction name="actionClose"/>
+   </widget>
+   <widget class="QMenu" name="menuCamera_Settings">
+    <property name="title">
+     <string>Camera Settings</string>
+    </property>
+    <addaction name="actionFollow"/>
+   </widget>
+   <addaction name="menuFile"/>
+   <addaction name="menuCamera_Settings"/>
+  </widget>
+  <widget class="QStatusBar" name="statusbar"/>
+  <action name="actionClose">
+   <property name="text">
+    <string>Close</string>
+   </property>
+  </action>
+  <action name="actionFollow">
+   <property name="checkable">
+    <bool>true</bool>
+   </property>
+   <property name="checked">
+    <bool>true</bool>
+   </property>
+   <property name="text">
+    <string>Follow</string>
+   </property>
+  </action>
+  <action name="actionReset">
+   <property name="text">
+    <string>Reset</string>
+   </property>
+  </action>
+  <action name="actionSave">
+   <property name="text">
+    <string>Save</string>
+   </property>
+  </action>
+  <action name="actionSettings">
+   <property name="text">
+    <string>Settings</string>
+   </property>
+  </action>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>

+ 223 - 0
groundstation/ping.py

@@ -0,0 +1,223 @@
+#!/usr/bin/env python
+
+"""
+    A pure python ping implementation using raw socket.
+
+
+    Note that ICMP messages can only be sent from processes running as root.
+
+
+    Derived from ping.c distributed in Linux's netkit. That code is
+    copyright (c) 1989 by The Regents of the University of California.
+    That code is in turn derived from code written by Mike Muuss of the
+    US Army Ballistic Research Laboratory in December, 1983 and
+    placed in the public domain. They have my thanks.
+
+    Bugs are naturally mine. I'd be glad to hear about them. There are
+    certainly word - size dependenceies here.
+
+    Copyright (c) Matthew Dixon Cowles, <http://www.visi.com/~mdc/>.
+    Distributable under the terms of the GNU General Public License
+    version 2. Provided with no warranties of any sort.
+
+    Original Version from Matthew Dixon Cowles:
+      -> ftp://ftp.visi.com/users/mdc/ping.py
+
+    Rewrite by Jens Diemer:
+      -> http://www.python-forum.de/post-69122.html#69122
+
+
+    Revision history
+    ~~~~~~~~~~~~~~~~
+    March 17, 2016
+    Changes by Corentin Debains:
+    - converted to support python 3 and added packaging info
+
+    Januari 27, 2015
+    Changed receive response to not accept ICMP request messages.
+    It was possible to receive the very request that was sent.
+
+    March 11, 2010
+    changes by Samuel Stauffer:
+    - replaced time.clock with default_timer which is set to
+      time.clock on windows and time.time on other systems.
+
+    May 30, 2007
+    little rewrite by Jens Diemer:
+     -  change socket asterisk import to a normal import
+     -  replace time.time() with time.clock()
+     -  delete "return None" (or change to "return" only)
+     -  in checksum() rename "str" to "source_string"
+
+    November 22, 1997
+    Initial hack. Doesn't do much, but rather than try to guess
+    what features I (or others) will want in the future, I've only
+    put in what I need now.
+
+    December 16, 1997
+    For some reason, the checksum bytes are in the wrong order when
+    this is run under Solaris 2.X for SPARC but it works right under
+    Linux x86. Since I don't know just what's wrong, I'll swap the
+    bytes always and then do an htons().
+
+    December 4, 2000
+    Changed the struct.pack() calls to pack the checksum and ID as
+    unsigned. My thanks to Jerome Poincheval for the fix.
+
+    Last commit info:
+    ~~~~~~~~~~~~~~~~~
+    $LastChangedDate: $
+    $Rev: $
+    $Author: $
+"""
+
+
+import os, sys, socket, struct, select, time
+
+if sys.platform == "win32":
+    # On Windows, the best timer is time.clock()
+    default_timer = time.clock
+else:
+    # On most other platforms the best timer is time.time()
+    default_timer = time.time
+
+# From /usr/include/linux/icmp.h; your milage may vary.
+ICMP_ECHO_REQUEST = 8 # Seems to be the same on Solaris.
+
+
+def checksum(source_string):
+    """
+    I'm not too confident that this is right but testing seems
+    to suggest that it gives the same answers as in_cksum in ping.c
+    """
+    sum = 0
+    countTo = (len(source_string)/2)*2
+    count = 0
+    while count<countTo:
+        thisVal = source_string[count + 1]*256 + source_string[count]
+        sum = sum + thisVal
+        sum = sum & 0xffffffff # Necessary?
+        count = count + 2
+
+    if countTo<len(source_string):
+        sum = sum + source_string[len(source_string) - 1]
+        sum = sum & 0xffffffff # Necessary?
+
+    sum = (sum >> 16)  +  (sum & 0xffff)
+    sum = sum + (sum >> 16)
+    answer = ~sum
+    answer = answer & 0xffff
+
+    # Swap bytes. Bugger me if I know why.
+    answer = answer >> 8 | (answer << 8 & 0xff00)
+
+    return answer
+
+
+def receive_one_ping(my_socket, ID, timeout):
+    """
+    receive the ping from the socket.
+    """
+    timeLeft = timeout
+    while True:
+        startedSelect = default_timer()
+        whatReady = select.select([my_socket], [], [], timeLeft)
+        howLongInSelect = (default_timer() - startedSelect)
+        if whatReady[0] == []: # Timeout
+            return
+
+        timeReceived = default_timer()
+        recPacket, addr = my_socket.recvfrom(1024)
+        icmpHeader = recPacket[20:28]
+        type, code, checksum, packetID, sequence = struct.unpack(
+            "bbHHh", icmpHeader
+        )
+        # Filters out the echo request itself.
+        # This can be tested by pinging 127.0.0.1
+        # You'll see your own request
+        if type != 8 and packetID == ID:
+            bytesInDouble = struct.calcsize("d")
+            timeSent = struct.unpack("d", recPacket[28:28 + bytesInDouble])[0]
+            return timeReceived - timeSent
+
+        timeLeft = timeLeft - howLongInSelect
+        if timeLeft <= 0:
+            return
+
+
+def send_one_ping(my_socket, dest_addr, ID):
+    """
+    Send one ping to the given >dest_addr<.
+    """
+    dest_addr  =  socket.gethostbyname(dest_addr)
+
+    # Header is type (8), code (8), checksum (16), id (16), sequence (16)
+    my_checksum = 0
+
+    # Make a dummy heder with a 0 checksum.
+    header = struct.pack("bbHHh", ICMP_ECHO_REQUEST, 0, my_checksum, ID, 1)
+    bytesInDouble = struct.calcsize("d")
+    data = bytes((192 - bytesInDouble) * "Q", 'utf-8')
+    data = struct.pack("d", default_timer()) + data
+
+    # Calculate the checksum on the data and the dummy header.
+    my_checksum = checksum(header + data)
+
+    # Now that we have the right checksum, we put that in. It's just easier
+    # to make up a new header than to stuff it into the dummy.
+    header = struct.pack(
+        "bbHHh", ICMP_ECHO_REQUEST, 0, socket.htons(my_checksum), ID, 1
+    )
+    packet = header + data
+    my_socket.sendto(packet, (dest_addr, 1)) # Don't know about the 1
+
+
+def do_one(dest_addr, timeout):
+    """
+    Returns either the delay (in seconds) or none on timeout.
+    """
+    icmp = socket.getprotobyname("icmp")
+    try:
+        my_socket = socket.socket(socket.AF_INET, socket.SOCK_RAW, icmp)
+    except PermissionError as e:
+        e.args = (e.args if e.args else tuple()) + ((
+            " - Note that ICMP messages can only be sent from processes"
+            " running as root."
+        ),)
+        raise
+
+    my_ID = os.getpid() & 0xFFFF
+
+    send_one_ping(my_socket, dest_addr, my_ID)
+    delay = receive_one_ping(my_socket, my_ID, timeout)
+
+    my_socket.close()
+    return delay
+
+
+def verbose_ping(dest_addr, timeout = 2, count = 4):
+    """
+    Send >count< ping to >dest_addr< with the given >timeout< and display
+    the result.
+    """
+    for i in range(count):
+        print("ping %s..." % dest_addr, end=' ')
+        try:
+            delay  =  do_one(dest_addr, timeout)
+        except socket.gaierror as e:
+            print("failed. (socket error: '%s')" % e)
+            break
+
+        if delay is None:
+            print("failed. (timeout within %ssec.)" % timeout)
+        else:
+            delay = delay * 1000
+            print("get ping in %0.4fms" % delay)
+    print()
+
+
+if __name__ == '__main__':
+    verbose_ping("heise.de")
+    verbose_ping("google.com")
+    verbose_ping("a-test-url-taht-is-not-available.com")
+    verbose_ping("192.168.1.1")

+ 34 - 0
groundstation/pingThread.py

@@ -0,0 +1,34 @@
+#!/usr/bin/python3
+# -*- coding: utf-8 -*-
+
+import time
+
+from PyQt5.QtCore import (QThread, pyqtSignal, pyqtSlot)
+
+import ping
+from functions import *
+import socket
+
+class pingThread(QThread):
+	pong = pyqtSignal(float)
+	
+	def __init__(self, ip, timeout = 4):
+		QThread.__init__(self)
+		self.ip = ip
+		self.timeout = timeout
+		
+	def run(self):
+		while True:
+			try:
+				t = ping.do_one(self.ip, self.timeout)
+			except (socket.error, UnicodeError) as e:
+				print(e)
+				t = None
+				
+			if t == None:
+				t = -1
+			self.pong.emit(t)
+			time.sleep(1)
+
+	def changeIP(self, ip):
+		self.ip = ip

+ 3 - 0
groundstation/run.bat

@@ -0,0 +1,3 @@
+pyuic5 main.ui -o main_ui.py
+pyuic5 settings.ui -o settings_ui.py
+python bexus.py || pause

+ 146 - 0
groundstation/settings.ui

@@ -0,0 +1,146 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>Dialog</class>
+ <widget class="QDialog" name="Dialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>215</width>
+    <height>195</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Dialog</string>
+  </property>
+  <property name="windowIcon">
+   <iconset>
+    <normaloff>pp.png</normaloff>pp.png</iconset>
+  </property>
+  <layout class="QGridLayout" name="gridLayout">
+   <item row="0" column="0">
+    <widget class="QGroupBox" name="groupBox_2">
+     <property name="title">
+      <string>Settings</string>
+     </property>
+     <layout class="QFormLayout" name="formLayout">
+      <item row="0" column="0">
+       <widget class="QLabel" name="label">
+        <property name="text">
+         <string>IP</string>
+        </property>
+       </widget>
+      </item>
+      <item row="0" column="1">
+       <widget class="QLineEdit" name="lineEditIP">
+        <property name="sizePolicy">
+         <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+          <horstretch>0</horstretch>
+          <verstretch>0</verstretch>
+         </sizepolicy>
+        </property>
+        <property name="inputMask">
+         <string>009.009.009.009;_</string>
+        </property>
+        <property name="text">
+         <string>192.168.0.8</string>
+        </property>
+        <property name="cursorPosition">
+         <number>0</number>
+        </property>
+       </widget>
+      </item>
+      <item row="1" column="0">
+       <widget class="QLabel" name="label_4">
+        <property name="text">
+         <string>Port</string>
+        </property>
+       </widget>
+      </item>
+      <item row="1" column="1">
+       <widget class="QLineEdit" name="lineEditPort">
+        <property name="sizePolicy">
+         <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+          <horstretch>0</horstretch>
+          <verstretch>0</verstretch>
+         </sizepolicy>
+        </property>
+        <property name="inputMask">
+         <string>00009</string>
+        </property>
+        <property name="text">
+         <string>1234</string>
+        </property>
+       </widget>
+      </item>
+      <item row="2" column="0">
+       <widget class="QLabel" name="label_5">
+        <property name="text">
+         <string>Log file</string>
+        </property>
+       </widget>
+      </item>
+      <item row="2" column="1">
+       <widget class="QLineEdit" name="lineEditLogFile">
+        <property name="sizePolicy">
+         <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+          <horstretch>0</horstretch>
+          <verstretch>0</verstretch>
+         </sizepolicy>
+        </property>
+        <property name="text">
+         <string>default</string>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item row="1" column="0">
+    <widget class="QDialogButtonBox" name="buttonBox">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="standardButtons">
+      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>Dialog</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>248</x>
+     <y>254</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>157</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>Dialog</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>316</x>
+     <y>260</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>286</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>

+ 82 - 0
groundstation/settings_ui.py

@@ -0,0 +1,82 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'settings.ui'
+#
+# Created by: PyQt5 UI code generator 5.10.1
+#
+# WARNING! All changes made in this file will be lost!
+
+from PyQt5 import QtCore, QtGui, QtWidgets
+
+class Ui_Dialog(object):
+    def setupUi(self, Dialog):
+        Dialog.setObjectName("Dialog")
+        Dialog.resize(215, 195)
+        icon = QtGui.QIcon()
+        icon.addPixmap(QtGui.QPixmap("pp.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
+        Dialog.setWindowIcon(icon)
+        self.gridLayout = QtWidgets.QGridLayout(Dialog)
+        self.gridLayout.setObjectName("gridLayout")
+        self.groupBox_2 = QtWidgets.QGroupBox(Dialog)
+        self.groupBox_2.setObjectName("groupBox_2")
+        self.formLayout = QtWidgets.QFormLayout(self.groupBox_2)
+        self.formLayout.setObjectName("formLayout")
+        self.label = QtWidgets.QLabel(self.groupBox_2)
+        self.label.setObjectName("label")
+        self.formLayout.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.label)
+        self.lineEditIP = QtWidgets.QLineEdit(self.groupBox_2)
+        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
+        sizePolicy.setHorizontalStretch(0)
+        sizePolicy.setVerticalStretch(0)
+        sizePolicy.setHeightForWidth(self.lineEditIP.sizePolicy().hasHeightForWidth())
+        self.lineEditIP.setSizePolicy(sizePolicy)
+        self.lineEditIP.setCursorPosition(0)
+        self.lineEditIP.setObjectName("lineEditIP")
+        self.formLayout.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.lineEditIP)
+        self.label_4 = QtWidgets.QLabel(self.groupBox_2)
+        self.label_4.setObjectName("label_4")
+        self.formLayout.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.label_4)
+        self.lineEditPort = QtWidgets.QLineEdit(self.groupBox_2)
+        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
+        sizePolicy.setHorizontalStretch(0)
+        sizePolicy.setVerticalStretch(0)
+        sizePolicy.setHeightForWidth(self.lineEditPort.sizePolicy().hasHeightForWidth())
+        self.lineEditPort.setSizePolicy(sizePolicy)
+        self.lineEditPort.setObjectName("lineEditPort")
+        self.formLayout.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.lineEditPort)
+        self.label_5 = QtWidgets.QLabel(self.groupBox_2)
+        self.label_5.setObjectName("label_5")
+        self.formLayout.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.label_5)
+        self.lineEditLogFile = QtWidgets.QLineEdit(self.groupBox_2)
+        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
+        sizePolicy.setHorizontalStretch(0)
+        sizePolicy.setVerticalStretch(0)
+        sizePolicy.setHeightForWidth(self.lineEditLogFile.sizePolicy().hasHeightForWidth())
+        self.lineEditLogFile.setSizePolicy(sizePolicy)
+        self.lineEditLogFile.setObjectName("lineEditLogFile")
+        self.formLayout.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.lineEditLogFile)
+        self.gridLayout.addWidget(self.groupBox_2, 0, 0, 1, 1)
+        self.buttonBox = QtWidgets.QDialogButtonBox(Dialog)
+        self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
+        self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
+        self.buttonBox.setObjectName("buttonBox")
+        self.gridLayout.addWidget(self.buttonBox, 1, 0, 1, 1)
+
+        self.retranslateUi(Dialog)
+        self.buttonBox.accepted.connect(Dialog.accept)
+        self.buttonBox.rejected.connect(Dialog.reject)
+        QtCore.QMetaObject.connectSlotsByName(Dialog)
+
+    def retranslateUi(self, Dialog):
+        _translate = QtCore.QCoreApplication.translate
+        Dialog.setWindowTitle(_translate("Dialog", "Dialog"))
+        self.groupBox_2.setTitle(_translate("Dialog", "Settings"))
+        self.label.setText(_translate("Dialog", "IP"))
+        self.lineEditIP.setInputMask(_translate("Dialog", "009.009.009.009;_"))
+        self.lineEditIP.setText(_translate("Dialog", "192.168.0.8"))
+        self.label_4.setText(_translate("Dialog", "Port"))
+        self.lineEditPort.setInputMask(_translate("Dialog", "00009"))
+        self.lineEditPort.setText(_translate("Dialog", "1234"))
+        self.label_5.setText(_translate("Dialog", "Log file"))
+        self.lineEditLogFile.setText(_translate("Dialog", "default"))
+

+ 3 - 0
groundstation/udp-test.bat

@@ -0,0 +1,3 @@
+
+python listenUDP.py 
+pause

+ 190 - 0
groundstation/udpToFile/auswertung.py

@@ -0,0 +1,190 @@
+import socket
+import time
+import pprint
+import sys
+
+sys.path.insert(0,'..')
+
+from functions import *
+
+import numpy as np
+import matplotlib.pyplot as plt
+
+pp = pprint.PrettyPrinter(indent=4)
+
+filepath = 'data.log'	
+
+f = open(filepath, "rb")
+
+#fw = open("data.csv", "w")
+
+buf = b''
+length = 514
+
+t = []
+	
+result = {
+	"temperature_i2c": [],
+	"temperature_mpu": []
+	#"rawTempAdc": [],
+}
+axis = [0, 1, 1]
+count = 0
+colors = ["tab:red", "tab:blue", "tab:green"]
+
+dataset = """
+/*-------RAW-----------*/
+	uint32_t pressure_cis_raw;    // pack 1 x 4
+	uint32_t temperature_cis_raw; //    + 1 x 4
+	
+	uint32_t pressure_i2c_raw;
+	uint32_t temperature_i2c_raw;
+	
+	uint32_t temperature_spi_raw;
+	uint32_t adc_temperature_raw;
+	
+	int32_t  temperature_mpu_raw;
+	uint32_t adc_temperature_internal_raw;
+	
+	uint32_t	adc_voltage_raw;
+	uint32_t	adc_current_raw;
+	
+	struct vector_Uint accel_raw; // pack 3 x 4
+	struct vector_Uint gyro_raw;  //    + 3 x 4
+	
+	/*-----Computed--------*/
+	
+	double pressure_cis; // no packing needed 1 x 8
+	
+	double temperature_cis;	
+	
+	double pressure_spi;	
+	
+	double temperature_i2c;	
+	
+	double temperature_spi;	
+	
+	double adc_temperature;	
+	
+	double adc_temperature_internal;			
+	
+	double adc_voltage;	
+
+	double adc_current;		
+
+	double temperature_mpu;	
+	
+	struct vector accel; // no packing needed 3 x 8
+	struct vector gyro;
+	struct vector rot;
+
+	/*-----------gps------------*/
+	int32_t longitude;
+	int32_t longitude_mod2;
+	
+	int32_t latitude;
+	int32_t latitude_mod2;
+	
+	int32_t altitude;
+	int32_t altitude_mod2;
+	
+	uint32_t time;
+	uint32_t time_mod2;
+	
+	uint32_t hdop;
+	uint32_t hdop_mod2;
+	/*--------------------------*/	
+
+	/*---------magnet-----------*/
+	struct vector_Uint mag_raw;  // pack 3 x 4
+  int32_t temperature_mag_raw; //    + 1 x 4
+	
+	struct vector mag;
+
+	double temperature_mag;
+	
+	uint32_t count;
+	uint32_t crc;
+"""
+
+master = PktParser(dataset)
+slave = PktParser(dataset, master.end)
+	
+def check():
+	global count
+	if buf[length-2] != ord("\r") or buf[length-1] != ord("\n"):
+		print("[{:8}] error missing EOL!".format(count))
+		return
+	
+	obj = master.parse(buf)
+	#obj2 = slave.parse(buf)
+	
+	#if count == 0:
+		#fw.write(",".join(obj.keys()) + "\n")
+	
+	#if obj["count"] != obj2["count"]:
+	#	print("[{:8}] count error! {} != {}".format(count, obj["count"], obj2["count"]))
+	#	return
+	#if obj["crc"] != obj2["crc"]:
+	#	print("[{:8}] crc error!".format(count))
+	#	return
+	
+	#if count != obj["count"] - 1:
+	#	print("[{0:8}] missing data between row {0} and {1}!".format(count, obj["count"]))
+	#count = obj["count"]
+	count += 1
+	
+	t.append(count/10)
+	for key in result:
+		result[key].append(obj[key])
+	
+	if obj["count"] % 10000 == 0:
+		print("[{:8}] Reading...".format(count))
+	
+	return obj
+try:
+	byte = b'A'
+	while byte != b"":
+		# Do stuff with byte.
+		if len(buf) >= length:
+			obj = check()
+			
+			#if obj:
+				#fw.write(",".join(str(x) for x in obj.values()) + "\n")
+			
+			
+			pos = buf.find(b"\r\n")
+			if pos == -1:
+				print("[{:8}] can't find next line!".format(count))
+			
+			buf = buf[pos+2:]
+		else:
+			byte = f.read(length)
+			buf += byte
+			
+	if obj != None:
+		
+		pp.pprint(obj)
+finally:
+	f.close()
+	#fw.close()
+
+	fig, ax1 = plt.subplots()
+	ax1.set_xlabel('time (s)')
+	
+	n = 0
+	
+	ax2 = ax1.twinx()
+	axes = [ax1, ax2]
+		
+	for key in result:
+		
+		axes[axis[n]].plot(t, result[key], label=key, color=colors[n])
+		axes[axis[n]].set_ylabel(key, color=colors[n])
+		axes[axis[n]].tick_params(axis='y', labelcolor=colors[n])
+		n += 1
+		
+	#plt.legend()
+	fig.tight_layout()
+	plt.show()
+	

+ 128 - 0
groundstation/udpToFile/listenUDP.py

@@ -0,0 +1,128 @@
+#!/usr/bin/python3
+# -*- coding: utf-8 -*-
+
+import socket
+import time
+import pprint
+
+import sys
+sys.path.insert(0,'..')
+
+from functions import *
+
+pp = pprint.PrettyPrinter(indent=4)
+port = 1234
+
+f = open('new struct.log','bw')
+
+	
+dataset = """
+/*-------RAW-----------*/
+	uint32_t pressure_cis_raw;    // pack 1 x 4
+	uint32_t temperature_cis_raw; //    + 1 x 4
+	
+	uint32_t pressure_i2c_raw;
+	uint32_t temperature_i2c_raw;
+	
+	uint32_t temperature_spi_raw;
+	uint32_t adc_temperature_raw;
+	
+	int32_t  temperature_mpu_raw;
+	uint32_t adc_temperature_internal_raw;
+	
+	uint32_t	adc_voltage_raw;
+	uint32_t	adc_current_raw;
+	
+	struct vector_Uint accel_raw; // pack 3 x 4
+	struct vector_Uint gyro_raw;  //    + 3 x 4
+	
+	/*-----Computed--------*/
+	
+	double pressure_cis; // no packing needed 1 x 8
+	
+	double temperature_cis;	
+	
+	double pressure_i2c;	
+	
+	double temperature_i2c;	
+	
+	double temperature_spi;	
+	
+	double adc_temperature;	
+	
+	double adc_temperature_internal;			
+	
+	double adc_voltage;	
+
+	double adc_current;		
+
+	double temperature_mpu;	
+	
+	struct vector accel; // no packing needed 3 x 8
+	struct vector gyro;
+	struct vector rot;
+
+	/*-----------gps------------*/
+	int32_t longitude;
+	int32_t longitude_mod2;
+	
+	int32_t latitude;
+	int32_t latitude_mod2;
+	
+	int32_t altitude;
+	int32_t altitude_mod2;
+	
+	uint32_t time;
+	uint32_t time_mod2;
+	
+	uint32_t hdop;
+	uint32_t hdop_mod2;
+	/*--------------------------*/	
+
+	/*---------magnet-----------*/
+	struct vector_Uint mag_raw;  // pack 3 x 4
+	int32_t temperature_mag_raw; //    + 1 x 4
+	
+	struct vector mag;
+
+	double temperature_mag;
+	/*--------------------------*/
+	uint32_t count; // pack 1 x 4
+	uint32_t crc;   //    + 1 x 4
+"""
+print("master:")
+master = PktParser(dataset)
+print("slave:")
+slave = PktParser(dataset, master.end)
+
+#x = master.parse(bytes("\00\00\00\01"*slave.end, 'ascii'))
+
+#pp.pprint(x)
+
+
+
+udpServer = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+udpServer.bind(("", port))
+
+print("UDP Server is listening on port " + str(port))
+	
+while True:
+	try:
+		data, addr = udpServer.recvfrom(2048)
+
+	except WindowsError as e:
+		print(e)
+		time.sleep(1)
+		continue
+	if data == None:
+			continue
+	f.write(data + bytes("\r\n", 'ascii'))
+	
+	
+	pp.pprint(master.parse(data))
+	pp.pprint(slave.parse(data))
+	
+	
+	
+	
+	

+ 3 - 0
groundstation/udpToFile/run.bat

@@ -0,0 +1,3 @@
+
+python listenUDP.py 
+pause

+ 200 - 0
groundstation/ui läuft 15.10.18/AppWindow.py

@@ -0,0 +1,200 @@
+#!/usr/bin/python3
+# -*- coding: utf-8 -*-
+
+from PyQt5.QtWidgets import *
+from PyQt5.QtCore import (QSettings, QThread, pyqtSignal, pyqtSlot)
+
+from vispy import app, visuals, scene
+
+from main_ui import Ui_MainWindow
+from settings_ui import Ui_Dialog as Ui_Settings
+
+import numpy as np
+import random
+from colorsys import hsv_to_rgb
+import pprint
+import datetime
+
+from functions import *
+from listenUDP import listenUDP
+from pingThread import pingThread
+import vispyHelper
+
+import csv
+import socket
+
+
+random.seed()
+
+class AppWindow(QMainWindow):
+	
+	csv = None
+	
+	def __init__(self):
+		super().__init__()
+		self.ui = Ui_MainWindow()
+		self.ui.setupUi(self)
+		
+		self.settings = QSettings("bexus", "imufusion")
+		
+		self.ip = getSettings(self.settings, 'ip', "172.16.18.171")
+		self.port = getSettings(self.settings, 'port', 1234)
+		logFile = getSettings(self.settings, 'logFile', "default")
+		self.logFile = open("logs/{}_{}.txt".format(logFile, datetime.datetime.now().strftime('%m%d-%H%M%S')), "w")
+		
+		
+		self.ui.labelAddress.setText("{}:{}".format(self.ip, self.port))
+		self.ui.pushButton_reset.clicked.connect(self.reset_pressed)
+		
+		#self.ui.tableWidget.setColumnCount(4)
+		#self.ui.tableWidget.setRowCount(4)
+		#self.ui.tableWidget.setItem(0,1, QTableWidgetItem("TEXT"))
+		
+		self.udpServer = listenUDP(self.port)
+		self.udpServer.newMsg.connect(self.onNewMsg)
+		
+		self.pingThread = pingThread(self.ip)
+		self.pingThread.pong.connect(self.onPong)
+		
+		self.data3d = np.array([[0,0,0]], np.float32)
+		
+		self.view3D = vispyHelper.view3D(self.ui.openGLWidget)
+		self.viewQuad = vispyHelper.viewQuad(self.ui.openGLWidgetQuad)
+
+		
+		self.udpServer.start()
+		self.pingThread.start()
+		
+		self.timer = app.Timer()
+		self.timer.connect(self.update)
+		self.timer.start(0.1)
+		
+		self.show()
+		
+	def onNewMsg(self, data):
+		self.ui.labelTestData.setText(pprint.pformat(data))
+		
+		if self.csv == None:
+			self.csv = csv.DictWriter(self.logFile, data.keys())
+			self.csv.writeheader()
+			
+
+		self.csv.writerow(data)
+		
+		rx, ry, rz =data["rot"]
+		heading = data["mag_head"]
+		
+		if rz != 0:
+			rx = np.arctan(ry / rz)
+			ry = np.arctan(rx / rz)
+		else:
+			rx = np.arctan(0)
+			ry = np.arctan(0)
+		rz = 180 + heading
+		
+		rx *= 45
+		ry *= -45
+		
+		rx += 90
+		rz -= 90
+		
+		np.append(self.data3d, data["pos"])
+		
+		if self.ui.tabWidget.currentIndex() == 0:
+			self.view3D.update(self.data3d, (rx, ry, rz))
+		elif self.ui.tabWidget.currentIndex() == 1:
+			self.viewQuad.update(self.data3d, (rx, ry, rz))
+			
+		self.ui.label_temp_adc_1.setText("{:.2g} °C".format(data["adc_temperature"]))
+		self.ui.label_temp_cis_1.setText("{:.2g} °C".format(data["temperature_cis"]))
+		self.ui.label_temp_mag_1.setText("{:.2g} °C".format(data["temperature_mag"]))
+		self.ui.label_temp_mpu_1.setText("{:.2g} °C".format(data["temperature_mpu"]))
+		
+		self.ui.label_AcX.setText("{:g}".format(data["accel_raw"][0]))
+		self.ui.label_AcY.setText("{:g}".format(data["accel_raw"][1]))
+		self.ui.label_AcZ.setText("{:g}".format(data["accel_raw"][2]))
+		self.ui.label_GyX.setText("{:g}".format(data["gyro_raw"][0]))
+		self.ui.label_GyY.setText("{:g}".format(data["gyro_raw"][1]))
+		self.ui.label_GyZ.setText("{:g}".format(data["gyro_raw"][2]))
+		
+		self.ui.label_MaX.setText("{:g}".format(data["mag_raw"][0]))
+		self.ui.label_MaY.setText("{:g}".format(data["mag_raw"][1]))
+		self.ui.label_MaZ.setText("{:g}".format(data["mag_raw"][2]))
+		
+		self.ui.label_pres_cis_1.setText("{:.1g} Pa".format(data["pressure_cis"]))
+		#self.ui.label_pres_i2c_1.setText("{:.1g} Pa".format(data["pressure_spi"]))	
+		
+		self.ui.label_lat_1.setText("{:.5g}".format(data["latitude"]))
+		self.ui.label_lon_1.setText("{:.5g}".format(data["longitude"]))
+		self.ui.label_alt_1.setText("{:.2g} m".format(data["altitude"]))
+		try:
+			dt = datetime.datetime.fromtimestamp(data["time"] / 1000)
+			self.ui.label_date_1.setText(dt.strftime('%d.%m.%Y'))
+			self.ui.label_time_1.setText(dt.strftime('%H:%M:%S'))
+		except(OSError):
+			pass
+		self.ui.label_hdop_1.setText("{:g}".format(data["hdop"]))
+		
+	def update(self, ev):
+		#reset
+		value = self.ui.progressBar_reset.value()
+		if value > 0:
+			value -= 1
+			self.ui.progressBar_reset.setValue(value)
+			
+		return
+	
+	def reset_pressed(self):
+		value = self.ui.progressBar_reset.value()
+		value += 10
+		if value > 100:
+			value = 0
+			sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+			sock.sendto(bytes("RESET!!", "utf-8"), (self.ip, 65100))
+			
+		self.ui.progressBar_reset.setValue(value)
+	
+	def onPong(self, t):
+		if t != -1:
+			self.ui.labelLatency.setText("{:.1f}ms".format(t*1000))
+			self.ui.labelLink.setText("Online")
+			self.ui.labelLink.setStyleSheet("color:green")
+		else:
+			self.ui.labelLatency.setText("")
+			self.ui.labelLink.setText("Offline")
+			self.ui.labelLink.setStyleSheet("color:red")
+
+	@pyqtSlot(bool)
+	def on_actionSettings_triggered(self, triggered):
+		self.settings
+
+		d = QDialog()
+		d.ui = Ui_Settings()
+		d.ui.setupUi(d)
+		d.ui.lineEditIP.setText(self.ip)
+		d.ui.lineEditPort.setText(str(self.port))
+		
+		if d.exec_():
+			self.ip = d.ui.lineEditIP.text()
+			self.port = safe_cast(d.ui.lineEditPort.text(), int, 1234)
+			logFile = d.ui.lineEditLogFile.text()
+			self.settings.setValue('ip', self.ip)
+			self.settings.setValue('port', self.port)
+			self.settings.setValue('logFile', logFile)
+			
+			self.logFile = open("logs/{}_{}.txt".format(logFile, datetime.datetime.now().strftime('%m%d-%H%M%S')), "w")
+			self.csv = None
+			
+			self.pingThread.changeIP(self.ip)
+			self.udpServer.stop()
+			self.udpServer = listenUDP(self.port)
+			self.udpServer.newMsg.connect(self.onNewMsg)
+			self.udpServer.start()
+			
+			self.ui.labelAddress.setText("{}:{}".format(self.ip, self.port))
+
+			# this writes the settings to storage
+			self.settings.sync()
+	
+	def on_actionFollow_triggered(self, triggered):
+		print(triggered)

+ 18 - 0
groundstation/ui läuft 15.10.18/bexus.py

@@ -0,0 +1,18 @@
+#!/usr/bin/python3
+# -*- coding: utf-8 -*-
+
+import sys
+
+from PyQt5.QtWidgets import *
+from PyQt5.QtCore import (QThread, pyqtSignal, pyqtSlot)
+
+from functions import *
+from AppWindow import AppWindow
+
+
+
+qApp = QApplication(sys.argv)
+w = AppWindow()
+ret = qApp.exec_()
+
+sys.exit(ret)

+ 159 - 0
groundstation/ui läuft 15.10.18/functions.py

@@ -0,0 +1,159 @@
+#!/usr/bin/python3
+# -*- coding: utf-8 -*-
+
+import struct
+import copy
+from collections import OrderedDict
+
+def safe_cast(val, to_type, default=None):
+	try:
+		return to_type(val)
+	except (ValueError, TypeError):
+		return default
+
+def listToInt(a, signed=False):
+	res = 0
+	for x in reversed(a):
+		res = res * 256 + x
+	if signed and res >= 256 ** len(a) / 2:
+		res -= 256 ** len(a)
+	return res
+	
+def listToDouble(a):
+	return struct.unpack('d', a)[0]
+	
+def listToFloat(a):
+	return struct.unpack('f', a)[0]
+	
+def listToVector(a, double=True):
+	if double:
+		return struct.unpack('ddd', a)
+	else:
+		return struct.unpack('iii', a)
+
+def listToVector_Uint(a):
+	return struct.unpack('iii', a)
+		
+def getSettings(set, name, default=None):
+	val = set.value(name)
+	if val == None:
+		return default
+	
+	return val
+	
+	
+class PktParser():
+	def __init__(self, struct, offset=0):
+		self.props = OrderedDict()
+		
+		for line in struct.splitlines():
+			prop = self._getInfo(line)
+			if prop:
+				length, cb, name = prop
+				
+				#print("{:4d} {:2d} {}".format(offset, length, name))
+				
+				self.props[name] = lambda data, c=cb, o=offset, l=length: c(data[o:o+l])
+				offset += length
+				
+		self.end = offset
+		self.offset = offset
+		
+		
+	def _getInfo(self, line):
+		i = 0
+		type = None
+		name = None
+		
+		for word in line.split():
+			if word == "":
+				#print("(empty)")
+				continue
+				
+			if word[:2] in ["//", "/*"]:
+				#print(word)
+				break
+				
+			if word == "struct":
+				continue
+				
+			if i == 0: #type
+				type = self._getType(word)
+				if type:
+					i += 1
+					continue
+			
+			if i == 1: #name
+				p = word.find(";")
+				name = word[:p]
+			
+		if type and name:
+			return type + (name,)
+			
+	def _getType(self, name):
+		
+		if name == "uint32_t":
+			return (4, listToInt)
+			
+		if name == "int32_t":
+			return (4, lambda v: listToInt(v, True))
+
+		if name == "float":
+			return (4, listToFloat)
+			
+		if name == "double":
+			return (8, listToDouble)
+		
+		if name == "vector_Uint":
+			return (12, listToVector_Uint)
+			
+		if name == "vector":
+			return (24, listToVector)
+			
+		print("Type {} invalid".format(name))	
+		
+		return None
+	
+	def parse(self, data):
+		
+		if len(data) < self.end:
+			print("data too short {} < {}".format(len(data), self.end))
+			return
+		
+		ret = OrderedDict()
+		
+		for prop in self.props.keys():
+			ret[prop] = self.props[prop](data)
+		
+		d = {}
+		for key in ret:
+			if type(ret[key]) is tuple:
+				d.update(dict(zip((key+"_x",key+"_y",key+"_z"), ret[key])))
+		ret.update(d)
+		
+		return ret
+		
+	def keys(self):
+		tmp = b'\x00' * self.end
+		return self.parse(tmp).keys()
+		
+def confirm(prompt=None, resp=False):
+	if prompt is None:
+		prompt = 'Confirm'
+
+	if resp:
+		prompt = '%s [%s]|%s: ' % (prompt, 'y', 'n')
+	else:
+		prompt = '%s [%s]|%s: ' % (prompt, 'n', 'y')
+		
+	while True:
+		ans = raw_input(prompt)
+		if not ans:
+			return resp
+		if ans not in ['y', 'Y', 'n', 'N']:
+			print ('please enter y or n.')
+			continue
+		if ans == 'y' or ans == 'Y':
+			return True
+		if ans == 'n' or ans == 'N':
+			return False

+ 140 - 0
groundstation/ui läuft 15.10.18/listenUDP.py

@@ -0,0 +1,140 @@
+#!/usr/bin/python3
+# -*- coding: utf-8 -*-
+
+import socket
+import time
+import pprint
+import sys
+
+import datetime
+
+from PyQt5.QtWidgets import *
+from PyQt5.QtCore import (QThread, pyqtSignal, pyqtSlot)
+
+from functions import *
+
+pp = pprint.PrettyPrinter(indent=4)
+dataset = """
+/*-------RAW-----------*/
+	uint32_t pressure_cis_raw;    // pack 1 x 4
+	uint32_t temperature_cis_raw; //    + 1 x 4
+	
+	uint32_t pressure_spi_raw;
+	uint32_t temperature_i2c_raw;
+	
+	uint32_t temperature_spi_raw;
+	uint32_t adc_temperature_raw;
+	
+	int32_t  temperature_mpu_raw;
+	uint32_t adc_temperature_internal_raw;
+	
+	uint32_t	adc_voltage_raw;
+	uint32_t	adc_current_raw;
+	
+	struct vector_Uint accel_raw; // pack 3 x 4
+	struct vector_Uint gyro_raw;  //    + 3 x 4
+	
+	struct vector_Uint mag_raw; // pack 3 x 4
+	                            //    + 1 x 4
+  int32_t temperature_mag_raw;
+	
+	/*-----Computed--------*/
+	
+	double pressure_cis; // no packing needed 1 x 8
+	
+	double temperature_cis;	
+	
+	double pressure_spi;	
+	
+	double temperature_i2c;	
+	
+	double temperature_spi;	
+	
+	double adc_temperature;	
+	
+	double adc_temperature_internal;			
+	
+	double adc_voltage;	
+
+	double adc_current;		
+
+	double temperature_mpu;	
+	
+	struct vector accel; // no packing needed 3 x 8
+	struct vector gyro;
+	struct vector rot;
+	
+	struct vector mag;
+	double temperature_mag;
+	
+	struct vector global_accel;
+	struct vector pos;
+
+	/*-----------gps------------*/
+	float longitude;
+	float latitude;
+	
+	int32_t altitude;
+	uint32_t time;
+	
+	uint32_t hdop;
+	int32_t mag_head; //heading
+	
+	uint32_t count;
+	uint32_t crc;
+"""
+
+class listenUDP(QThread):
+	newMsg = pyqtSignal(dict)
+	
+	master = PktParser(dataset)
+	#slave = PktParser(dataset, master.end)
+	
+	dumpFile = open("logs/dump_{}.bin".format(datetime.datetime.now().strftime('%m%d-%H%M%S')), "bw")
+	
+	def __init__(self, port = 1234):
+		QThread.__init__(self)
+		self._udpServer = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+		self._udpServer.bind(("", port))
+		self.port = port
+		
+	def run(self):
+		while self._udpServer:
+			try:
+				data, addr = self._udpServer.recvfrom(2048)
+				
+			except WindowsError as e:
+				print(e)
+				time.sleep(1)
+				continue
+			if data == None:
+					continue
+		
+			self.dumpFile.write(data + bytes("\r\n", 'ascii'))
+			
+			data = self.master.parse(data)
+			
+			if not data:
+				continue
+			
+			if __name__ == "__main__":
+				pp.pprint(data)
+				
+			self.newMsg.emit(data)
+			time.sleep(0.01)
+	
+	def stop(self):
+		self._udpServer.close()
+		self._udpServer = None
+		self.quit()
+		
+		if __name__ == "__main__":
+			pp.pprint(obj)
+			
+		return obj
+
+if __name__ == "__main__":
+	udp = listenUDP(port = 1234)
+	print("UDP Server is listening on port " + str(udp.port))
+	udp.run()
+	

+ 672 - 0
groundstation/ui läuft 15.10.18/main.ui

@@ -0,0 +1,672 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>MainWindow</class>
+ <widget class="QMainWindow" name="MainWindow">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>1337</width>
+    <height>879</height>
+   </rect>
+  </property>
+  <property name="sizePolicy">
+   <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+    <horstretch>0</horstretch>
+    <verstretch>0</verstretch>
+   </sizepolicy>
+  </property>
+  <property name="windowTitle">
+   <string>BEXUS</string>
+  </property>
+  <property name="windowIcon">
+   <iconset>
+    <normaloff>../../../.designer/backup/pp.png</normaloff>../../../.designer/backup/pp.png</iconset>
+  </property>
+  <widget class="QWidget" name="centralwidget">
+   <layout class="QGridLayout" name="gridLayout_2">
+    <item row="0" column="1">
+     <layout class="QGridLayout" name="gridLayout">
+      <item row="0" column="0">
+       <layout class="QVBoxLayout" name="verticalLayout_2">
+        <item>
+         <widget class="QGroupBox" name="groupBox">
+          <property name="minimumSize">
+           <size>
+            <width>306</width>
+            <height>0</height>
+           </size>
+          </property>
+          <property name="title">
+           <string>Stats</string>
+          </property>
+          <layout class="QGridLayout" name="gridLayout_5">
+           <item row="4" column="1">
+            <widget class="QLabel" name="labelLink_MCU2">
+             <property name="font">
+              <font>
+               <weight>75</weight>
+               <bold>true</bold>
+              </font>
+             </property>
+             <property name="styleSheet">
+              <string notr="true">color:#ad6d00</string>
+             </property>
+             <property name="text">
+              <string>Unknown</string>
+             </property>
+            </widget>
+           </item>
+           <item row="3" column="1">
+            <widget class="QLabel" name="labelLink_MCU1">
+             <property name="font">
+              <font>
+               <weight>75</weight>
+               <bold>true</bold>
+              </font>
+             </property>
+             <property name="styleSheet">
+              <string notr="true">color:#ad6d00</string>
+             </property>
+             <property name="text">
+              <string>Unknown</string>
+             </property>
+            </widget>
+           </item>
+           <item row="1" column="0">
+            <widget class="QLabel" name="label_2">
+             <property name="text">
+              <string>Link:</string>
+             </property>
+            </widget>
+           </item>
+           <item row="4" column="0">
+            <widget class="QLabel" name="label_25">
+             <property name="text">
+              <string>MCU 2:</string>
+             </property>
+            </widget>
+           </item>
+           <item row="2" column="1">
+            <widget class="QLabel" name="labelLatency">
+             <property name="text">
+              <string>20ms</string>
+             </property>
+            </widget>
+           </item>
+           <item row="1" column="1">
+            <widget class="QLabel" name="labelLink">
+             <property name="font">
+              <font>
+               <weight>75</weight>
+               <bold>true</bold>
+              </font>
+             </property>
+             <property name="styleSheet">
+              <string notr="true">color:red</string>
+             </property>
+             <property name="text">
+              <string>Offline</string>
+             </property>
+            </widget>
+           </item>
+           <item row="2" column="0">
+            <widget class="QLabel" name="label_6">
+             <property name="text">
+              <string>Latency:</string>
+             </property>
+            </widget>
+           </item>
+           <item row="3" column="0">
+            <widget class="QLabel" name="label_3">
+             <property name="text">
+              <string>MCU 1:</string>
+             </property>
+            </widget>
+           </item>
+           <item row="0" column="0">
+            <widget class="QLabel" name="label">
+             <property name="text">
+              <string>Address:</string>
+             </property>
+            </widget>
+           </item>
+           <item row="0" column="1">
+            <widget class="QLabel" name="labelAddress">
+             <property name="text">
+              <string>192.168.0.8:1234</string>
+             </property>
+            </widget>
+           </item>
+          </layout>
+         </widget>
+        </item>
+        <item>
+         <widget class="QGroupBox" name="groupBox_6">
+          <property name="title">
+           <string>Position</string>
+          </property>
+          <layout class="QGridLayout" name="gridLayout_11">
+           <item row="0" column="1">
+            <widget class="QLabel" name="label_lat_1">
+             <property name="text">
+              <string>51.4562</string>
+             </property>
+            </widget>
+           </item>
+           <item row="0" column="0">
+            <widget class="QLabel" name="label_27">
+             <property name="text">
+              <string>Latitude</string>
+             </property>
+            </widget>
+           </item>
+           <item row="2" column="1">
+            <widget class="QLabel" name="label_alt_1">
+             <property name="text">
+              <string>165.07 m</string>
+             </property>
+            </widget>
+           </item>
+           <item row="2" column="0">
+            <widget class="QLabel" name="label_21">
+             <property name="text">
+              <string>Altitude:</string>
+             </property>
+            </widget>
+           </item>
+           <item row="1" column="0">
+            <widget class="QLabel" name="label_29">
+             <property name="text">
+              <string>Longitude</string>
+             </property>
+            </widget>
+           </item>
+           <item row="1" column="1">
+            <widget class="QLabel" name="label_lon_1">
+             <property name="text">
+              <string>10.2452</string>
+             </property>
+            </widget>
+           </item>
+          </layout>
+         </widget>
+        </item>
+        <item>
+         <widget class="QGroupBox" name="groupBox_4">
+          <property name="title">
+           <string>Gyro</string>
+          </property>
+          <layout class="QGridLayout" name="gridLayout_7">
+           <item row="0" column="6">
+            <widget class="QLabel" name="label_AcZ">
+             <property name="text">
+              <string>0</string>
+             </property>
+            </widget>
+           </item>
+           <item row="0" column="0">
+            <widget class="QLabel" name="label_5">
+             <property name="text">
+              <string>AcX:</string>
+             </property>
+            </widget>
+           </item>
+           <item row="1" column="5">
+            <widget class="QLabel" name="label_17">
+             <property name="text">
+              <string>GyZ:</string>
+             </property>
+            </widget>
+           </item>
+           <item row="1" column="3">
+            <widget class="QLabel" name="label_16">
+             <property name="text">
+              <string>GyY:</string>
+             </property>
+            </widget>
+           </item>
+           <item row="0" column="4">
+            <widget class="QLabel" name="label_AcY">
+             <property name="text">
+              <string>0</string>
+             </property>
+            </widget>
+           </item>
+           <item row="1" column="0">
+            <widget class="QLabel" name="label_15">
+             <property name="text">
+              <string>GyX:</string>
+             </property>
+            </widget>
+           </item>
+           <item row="0" column="3">
+            <widget class="QLabel" name="label_11">
+             <property name="text">
+              <string>AcY:</string>
+             </property>
+            </widget>
+           </item>
+           <item row="0" column="1">
+            <widget class="QLabel" name="label_AcX">
+             <property name="text">
+              <string>0</string>
+             </property>
+            </widget>
+           </item>
+           <item row="0" column="5">
+            <widget class="QLabel" name="label_13">
+             <property name="text">
+              <string>AcZ:</string>
+             </property>
+            </widget>
+           </item>
+           <item row="1" column="1">
+            <widget class="QLabel" name="label_GyX">
+             <property name="text">
+              <string>0</string>
+             </property>
+            </widget>
+           </item>
+           <item row="1" column="4">
+            <widget class="QLabel" name="label_GyY">
+             <property name="text">
+              <string>0</string>
+             </property>
+            </widget>
+           </item>
+           <item row="1" column="6">
+            <widget class="QLabel" name="label_GyZ">
+             <property name="text">
+              <string>0</string>
+             </property>
+            </widget>
+           </item>
+          </layout>
+         </widget>
+        </item>
+        <item>
+         <widget class="QGroupBox" name="groupBox_3">
+          <property name="title">
+           <string>Magnetic</string>
+          </property>
+          <layout class="QGridLayout" name="gridLayout_8">
+           <item row="0" column="0">
+            <widget class="QLabel" name="label_8">
+             <property name="text">
+              <string>X:</string>
+             </property>
+            </widget>
+           </item>
+           <item row="0" column="4">
+            <widget class="QLabel" name="label_18">
+             <property name="text">
+              <string>Z:</string>
+             </property>
+            </widget>
+           </item>
+           <item row="0" column="1">
+            <widget class="QLabel" name="label_MaX">
+             <property name="text">
+              <string>0</string>
+             </property>
+            </widget>
+           </item>
+           <item row="0" column="2">
+            <widget class="QLabel" name="label_12">
+             <property name="text">
+              <string>Y:</string>
+             </property>
+            </widget>
+           </item>
+           <item row="0" column="3">
+            <widget class="QLabel" name="label_MaY">
+             <property name="text">
+              <string>0</string>
+             </property>
+            </widget>
+           </item>
+           <item row="0" column="5">
+            <widget class="QLabel" name="label_MaZ">
+             <property name="text">
+              <string>0</string>
+             </property>
+            </widget>
+           </item>
+          </layout>
+         </widget>
+        </item>
+        <item>
+         <widget class="QGroupBox" name="groupBox_5">
+          <property name="title">
+           <string>Pressure</string>
+          </property>
+          <layout class="QGridLayout" name="gridLayout_9">
+           <item row="0" column="0">
+            <widget class="QLabel" name="label_9">
+             <property name="text">
+              <string>Cis</string>
+             </property>
+            </widget>
+           </item>
+           <item row="1" column="0">
+            <widget class="QLabel" name="label_20">
+             <property name="text">
+              <string>SPI</string>
+             </property>
+            </widget>
+           </item>
+           <item row="0" column="1">
+            <widget class="QLabel" name="label_pres_cis_1">
+             <property name="text">
+              <string>99999.4 Pa</string>
+             </property>
+            </widget>
+           </item>
+           <item row="1" column="1">
+            <widget class="QLabel" name="label_pres_i2c_1">
+             <property name="text">
+              <string>99355.0 Pa</string>
+             </property>
+            </widget>
+           </item>
+          </layout>
+         </widget>
+        </item>
+        <item>
+         <widget class="QGroupBox" name="groupBox_2">
+          <property name="title">
+           <string>Temperature</string>
+          </property>
+          <layout class="QGridLayout" name="gridLayout_6">
+           <item row="0" column="0">
+            <widget class="QLabel" name="label_4">
+             <property name="text">
+              <string>ADC</string>
+             </property>
+            </widget>
+           </item>
+           <item row="2" column="1">
+            <widget class="QLabel" name="label_temp_mpu_1">
+             <property name="text">
+              <string>39.20 °C</string>
+             </property>
+            </widget>
+           </item>
+           <item row="1" column="1">
+            <widget class="QLabel" name="label_temp_cis_1">
+             <property name="text">
+              <string>36.43 °C</string>
+             </property>
+            </widget>
+           </item>
+           <item row="2" column="0">
+            <widget class="QLabel" name="label_24">
+             <property name="text">
+              <string>MPU</string>
+             </property>
+            </widget>
+           </item>
+           <item row="0" column="1">
+            <widget class="QLabel" name="label_temp_adc_1">
+             <property name="text">
+              <string>20.21 °C</string>
+             </property>
+            </widget>
+           </item>
+           <item row="1" column="0">
+            <widget class="QLabel" name="label_22">
+             <property name="text">
+              <string>Cis</string>
+             </property>
+            </widget>
+           </item>
+           <item row="3" column="0">
+            <widget class="QLabel" name="label_37">
+             <property name="text">
+              <string>Mag</string>
+             </property>
+            </widget>
+           </item>
+           <item row="3" column="1">
+            <widget class="QLabel" name="label_temp_mag_1">
+             <property name="text">
+              <string>36.01 °C</string>
+             </property>
+            </widget>
+           </item>
+          </layout>
+         </widget>
+        </item>
+        <item>
+         <widget class="QGroupBox" name="groupBox_7">
+          <property name="title">
+           <string>GPS</string>
+          </property>
+          <layout class="QGridLayout" name="gridLayout_12">
+           <item row="0" column="1">
+            <widget class="QLabel" name="label_time_1">
+             <property name="text">
+              <string>12:00</string>
+             </property>
+            </widget>
+           </item>
+           <item row="1" column="1">
+            <widget class="QLabel" name="label_date_1">
+             <property name="text">
+              <string>01.01.1970</string>
+             </property>
+            </widget>
+           </item>
+           <item row="1" column="0">
+            <widget class="QLabel" name="label_32">
+             <property name="text">
+              <string>Date</string>
+             </property>
+            </widget>
+           </item>
+           <item row="0" column="0">
+            <widget class="QLabel" name="label_31">
+             <property name="text">
+              <string>Time</string>
+             </property>
+            </widget>
+           </item>
+           <item row="2" column="0">
+            <widget class="QLabel" name="label_35">
+             <property name="text">
+              <string>Hdop</string>
+             </property>
+            </widget>
+           </item>
+           <item row="2" column="1">
+            <widget class="QLabel" name="label_hdop_1">
+             <property name="text">
+              <string>0</string>
+             </property>
+            </widget>
+           </item>
+          </layout>
+         </widget>
+        </item>
+        <item>
+         <spacer name="verticalSpacer">
+          <property name="orientation">
+           <enum>Qt::Vertical</enum>
+          </property>
+          <property name="sizeType">
+           <enum>QSizePolicy::Expanding</enum>
+          </property>
+          <property name="sizeHint" stdset="0">
+           <size>
+            <width>20</width>
+            <height>40</height>
+           </size>
+          </property>
+         </spacer>
+        </item>
+        <item>
+         <widget class="QProgressBar" name="progressBar_reset">
+          <property name="sizePolicy">
+           <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
+            <horstretch>0</horstretch>
+            <verstretch>0</verstretch>
+           </sizepolicy>
+          </property>
+          <property name="value">
+           <number>0</number>
+          </property>
+          <property name="textVisible">
+           <bool>true</bool>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QPushButton" name="pushButton_reset">
+          <property name="sizePolicy">
+           <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
+            <horstretch>0</horstretch>
+            <verstretch>0</verstretch>
+           </sizepolicy>
+          </property>
+          <property name="text">
+           <string>Restart</string>
+          </property>
+         </widget>
+        </item>
+       </layout>
+      </item>
+     </layout>
+    </item>
+    <item row="0" column="0">
+     <widget class="QTabWidget" name="tabWidget">
+      <property name="currentIndex">
+       <number>0</number>
+      </property>
+      <widget class="QWidget" name="tab">
+       <attribute name="title">
+        <string>3D View</string>
+       </attribute>
+       <layout class="QGridLayout" name="gridLayout_4">
+        <item row="0" column="0">
+         <widget class="QOpenGLWidget" name="openGLWidget">
+          <property name="sizePolicy">
+           <sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
+            <horstretch>0</horstretch>
+            <verstretch>0</verstretch>
+           </sizepolicy>
+          </property>
+          <property name="mouseTracking">
+           <bool>false</bool>
+          </property>
+         </widget>
+        </item>
+       </layout>
+      </widget>
+      <widget class="QWidget" name="tab_3">
+       <attribute name="title">
+        <string>Orthogonal View</string>
+       </attribute>
+       <layout class="QGridLayout" name="gridLayout_3">
+        <item row="0" column="0">
+         <widget class="QOpenGLWidget" name="openGLWidgetQuad"/>
+        </item>
+       </layout>
+      </widget>
+      <widget class="QWidget" name="tab_2">
+       <attribute name="title">
+        <string>Raw data</string>
+       </attribute>
+       <layout class="QGridLayout" name="gridLayout_10">
+        <item row="1" column="0">
+         <widget class="QScrollArea" name="scrollArea">
+          <property name="widgetResizable">
+           <bool>true</bool>
+          </property>
+          <widget class="QWidget" name="scrollAreaWidgetContents">
+           <property name="geometry">
+            <rect>
+             <x>0</x>
+             <y>0</y>
+             <width>98</width>
+             <height>28</height>
+            </rect>
+           </property>
+          </widget>
+         </widget>
+        </item>
+        <item row="0" column="0">
+         <widget class="QLabel" name="labelTestData">
+          <property name="text">
+           <string>Test Data</string>
+          </property>
+         </widget>
+        </item>
+       </layout>
+      </widget>
+     </widget>
+    </item>
+   </layout>
+  </widget>
+  <widget class="QMenuBar" name="menubar">
+   <property name="geometry">
+    <rect>
+     <x>0</x>
+     <y>0</y>
+     <width>1337</width>
+     <height>26</height>
+    </rect>
+   </property>
+   <widget class="QMenu" name="menuFile">
+    <property name="title">
+     <string>File</string>
+    </property>
+    <addaction name="actionSave"/>
+    <addaction name="actionSettings"/>
+    <addaction name="actionClose"/>
+   </widget>
+   <widget class="QMenu" name="menuCamera_Settings">
+    <property name="title">
+     <string>Camera Settings</string>
+    </property>
+    <addaction name="actionFollow"/>
+   </widget>
+   <addaction name="menuFile"/>
+   <addaction name="menuCamera_Settings"/>
+  </widget>
+  <widget class="QStatusBar" name="statusbar"/>
+  <action name="actionClose">
+   <property name="text">
+    <string>Close</string>
+   </property>
+  </action>
+  <action name="actionFollow">
+   <property name="checkable">
+    <bool>true</bool>
+   </property>
+   <property name="checked">
+    <bool>true</bool>
+   </property>
+   <property name="text">
+    <string>Follow</string>
+   </property>
+  </action>
+  <action name="actionReset">
+   <property name="text">
+    <string>Reset</string>
+   </property>
+  </action>
+  <action name="actionSave">
+   <property name="text">
+    <string>Save</string>
+   </property>
+  </action>
+  <action name="actionSettings">
+   <property name="text">
+    <string>Settings</string>
+   </property>
+  </action>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>

+ 410 - 0
groundstation/ui läuft 15.10.18/main_ui.py

@@ -0,0 +1,410 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'main.ui'
+#
+# Created by: PyQt5 UI code generator 5.10.1
+#
+# WARNING! All changes made in this file will be lost!
+
+from PyQt5 import QtCore, QtGui, QtWidgets
+
+class Ui_MainWindow(object):
+    def setupUi(self, MainWindow):
+        MainWindow.setObjectName("MainWindow")
+        MainWindow.resize(1337, 879)
+        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred)
+        sizePolicy.setHorizontalStretch(0)
+        sizePolicy.setVerticalStretch(0)
+        sizePolicy.setHeightForWidth(MainWindow.sizePolicy().hasHeightForWidth())
+        MainWindow.setSizePolicy(sizePolicy)
+        icon = QtGui.QIcon()
+        icon.addPixmap(QtGui.QPixmap("../../../.designer/backup/pp.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
+        MainWindow.setWindowIcon(icon)
+        self.centralwidget = QtWidgets.QWidget(MainWindow)
+        self.centralwidget.setObjectName("centralwidget")
+        self.gridLayout_2 = QtWidgets.QGridLayout(self.centralwidget)
+        self.gridLayout_2.setObjectName("gridLayout_2")
+        self.gridLayout = QtWidgets.QGridLayout()
+        self.gridLayout.setObjectName("gridLayout")
+        self.verticalLayout_2 = QtWidgets.QVBoxLayout()
+        self.verticalLayout_2.setObjectName("verticalLayout_2")
+        self.groupBox = QtWidgets.QGroupBox(self.centralwidget)
+        self.groupBox.setMinimumSize(QtCore.QSize(306, 0))
+        self.groupBox.setObjectName("groupBox")
+        self.gridLayout_5 = QtWidgets.QGridLayout(self.groupBox)
+        self.gridLayout_5.setObjectName("gridLayout_5")
+        self.labelLink_MCU2 = QtWidgets.QLabel(self.groupBox)
+        font = QtGui.QFont()
+        font.setBold(True)
+        font.setWeight(75)
+        self.labelLink_MCU2.setFont(font)
+        self.labelLink_MCU2.setStyleSheet("color:#ad6d00")
+        self.labelLink_MCU2.setObjectName("labelLink_MCU2")
+        self.gridLayout_5.addWidget(self.labelLink_MCU2, 4, 1, 1, 1)
+        self.labelLink_MCU1 = QtWidgets.QLabel(self.groupBox)
+        font = QtGui.QFont()
+        font.setBold(True)
+        font.setWeight(75)
+        self.labelLink_MCU1.setFont(font)
+        self.labelLink_MCU1.setStyleSheet("color:#ad6d00")
+        self.labelLink_MCU1.setObjectName("labelLink_MCU1")
+        self.gridLayout_5.addWidget(self.labelLink_MCU1, 3, 1, 1, 1)
+        self.label_2 = QtWidgets.QLabel(self.groupBox)
+        self.label_2.setObjectName("label_2")
+        self.gridLayout_5.addWidget(self.label_2, 1, 0, 1, 1)
+        self.label_25 = QtWidgets.QLabel(self.groupBox)
+        self.label_25.setObjectName("label_25")
+        self.gridLayout_5.addWidget(self.label_25, 4, 0, 1, 1)
+        self.labelLatency = QtWidgets.QLabel(self.groupBox)
+        self.labelLatency.setObjectName("labelLatency")
+        self.gridLayout_5.addWidget(self.labelLatency, 2, 1, 1, 1)
+        self.labelLink = QtWidgets.QLabel(self.groupBox)
+        font = QtGui.QFont()
+        font.setBold(True)
+        font.setWeight(75)
+        self.labelLink.setFont(font)
+        self.labelLink.setStyleSheet("color:red")
+        self.labelLink.setObjectName("labelLink")
+        self.gridLayout_5.addWidget(self.labelLink, 1, 1, 1, 1)
+        self.label_6 = QtWidgets.QLabel(self.groupBox)
+        self.label_6.setObjectName("label_6")
+        self.gridLayout_5.addWidget(self.label_6, 2, 0, 1, 1)
+        self.label_3 = QtWidgets.QLabel(self.groupBox)
+        self.label_3.setObjectName("label_3")
+        self.gridLayout_5.addWidget(self.label_3, 3, 0, 1, 1)
+        self.label = QtWidgets.QLabel(self.groupBox)
+        self.label.setObjectName("label")
+        self.gridLayout_5.addWidget(self.label, 0, 0, 1, 1)
+        self.labelAddress = QtWidgets.QLabel(self.groupBox)
+        self.labelAddress.setObjectName("labelAddress")
+        self.gridLayout_5.addWidget(self.labelAddress, 0, 1, 1, 1)
+        self.verticalLayout_2.addWidget(self.groupBox)
+        self.groupBox_6 = QtWidgets.QGroupBox(self.centralwidget)
+        self.groupBox_6.setObjectName("groupBox_6")
+        self.gridLayout_11 = QtWidgets.QGridLayout(self.groupBox_6)
+        self.gridLayout_11.setObjectName("gridLayout_11")
+        self.label_lat_1 = QtWidgets.QLabel(self.groupBox_6)
+        self.label_lat_1.setObjectName("label_lat_1")
+        self.gridLayout_11.addWidget(self.label_lat_1, 0, 1, 1, 1)
+        self.label_27 = QtWidgets.QLabel(self.groupBox_6)
+        self.label_27.setObjectName("label_27")
+        self.gridLayout_11.addWidget(self.label_27, 0, 0, 1, 1)
+        self.label_alt_1 = QtWidgets.QLabel(self.groupBox_6)
+        self.label_alt_1.setObjectName("label_alt_1")
+        self.gridLayout_11.addWidget(self.label_alt_1, 2, 1, 1, 1)
+        self.label_21 = QtWidgets.QLabel(self.groupBox_6)
+        self.label_21.setObjectName("label_21")
+        self.gridLayout_11.addWidget(self.label_21, 2, 0, 1, 1)
+        self.label_29 = QtWidgets.QLabel(self.groupBox_6)
+        self.label_29.setObjectName("label_29")
+        self.gridLayout_11.addWidget(self.label_29, 1, 0, 1, 1)
+        self.label_lon_1 = QtWidgets.QLabel(self.groupBox_6)
+        self.label_lon_1.setObjectName("label_lon_1")
+        self.gridLayout_11.addWidget(self.label_lon_1, 1, 1, 1, 1)
+        self.verticalLayout_2.addWidget(self.groupBox_6)
+        self.groupBox_4 = QtWidgets.QGroupBox(self.centralwidget)
+        self.groupBox_4.setObjectName("groupBox_4")
+        self.gridLayout_7 = QtWidgets.QGridLayout(self.groupBox_4)
+        self.gridLayout_7.setObjectName("gridLayout_7")
+        self.label_AcZ = QtWidgets.QLabel(self.groupBox_4)
+        self.label_AcZ.setObjectName("label_AcZ")
+        self.gridLayout_7.addWidget(self.label_AcZ, 0, 6, 1, 1)
+        self.label_5 = QtWidgets.QLabel(self.groupBox_4)
+        self.label_5.setObjectName("label_5")
+        self.gridLayout_7.addWidget(self.label_5, 0, 0, 1, 1)
+        self.label_17 = QtWidgets.QLabel(self.groupBox_4)
+        self.label_17.setObjectName("label_17")
+        self.gridLayout_7.addWidget(self.label_17, 1, 5, 1, 1)
+        self.label_16 = QtWidgets.QLabel(self.groupBox_4)
+        self.label_16.setObjectName("label_16")
+        self.gridLayout_7.addWidget(self.label_16, 1, 3, 1, 1)
+        self.label_AcY = QtWidgets.QLabel(self.groupBox_4)
+        self.label_AcY.setObjectName("label_AcY")
+        self.gridLayout_7.addWidget(self.label_AcY, 0, 4, 1, 1)
+        self.label_15 = QtWidgets.QLabel(self.groupBox_4)
+        self.label_15.setObjectName("label_15")
+        self.gridLayout_7.addWidget(self.label_15, 1, 0, 1, 1)
+        self.label_11 = QtWidgets.QLabel(self.groupBox_4)
+        self.label_11.setObjectName("label_11")
+        self.gridLayout_7.addWidget(self.label_11, 0, 3, 1, 1)
+        self.label_AcX = QtWidgets.QLabel(self.groupBox_4)
+        self.label_AcX.setObjectName("label_AcX")
+        self.gridLayout_7.addWidget(self.label_AcX, 0, 1, 1, 1)
+        self.label_13 = QtWidgets.QLabel(self.groupBox_4)
+        self.label_13.setObjectName("label_13")
+        self.gridLayout_7.addWidget(self.label_13, 0, 5, 1, 1)
+        self.label_GyX = QtWidgets.QLabel(self.groupBox_4)
+        self.label_GyX.setObjectName("label_GyX")
+        self.gridLayout_7.addWidget(self.label_GyX, 1, 1, 1, 1)
+        self.label_GyY = QtWidgets.QLabel(self.groupBox_4)
+        self.label_GyY.setObjectName("label_GyY")
+        self.gridLayout_7.addWidget(self.label_GyY, 1, 4, 1, 1)
+        self.label_GyZ = QtWidgets.QLabel(self.groupBox_4)
+        self.label_GyZ.setObjectName("label_GyZ")
+        self.gridLayout_7.addWidget(self.label_GyZ, 1, 6, 1, 1)
+        self.verticalLayout_2.addWidget(self.groupBox_4)
+        self.groupBox_3 = QtWidgets.QGroupBox(self.centralwidget)
+        self.groupBox_3.setObjectName("groupBox_3")
+        self.gridLayout_8 = QtWidgets.QGridLayout(self.groupBox_3)
+        self.gridLayout_8.setObjectName("gridLayout_8")
+        self.label_8 = QtWidgets.QLabel(self.groupBox_3)
+        self.label_8.setObjectName("label_8")
+        self.gridLayout_8.addWidget(self.label_8, 0, 0, 1, 1)
+        self.label_18 = QtWidgets.QLabel(self.groupBox_3)
+        self.label_18.setObjectName("label_18")
+        self.gridLayout_8.addWidget(self.label_18, 0, 4, 1, 1)
+        self.label_MaX = QtWidgets.QLabel(self.groupBox_3)
+        self.label_MaX.setObjectName("label_MaX")
+        self.gridLayout_8.addWidget(self.label_MaX, 0, 1, 1, 1)
+        self.label_12 = QtWidgets.QLabel(self.groupBox_3)
+        self.label_12.setObjectName("label_12")
+        self.gridLayout_8.addWidget(self.label_12, 0, 2, 1, 1)
+        self.label_MaY = QtWidgets.QLabel(self.groupBox_3)
+        self.label_MaY.setObjectName("label_MaY")
+        self.gridLayout_8.addWidget(self.label_MaY, 0, 3, 1, 1)
+        self.label_MaZ = QtWidgets.QLabel(self.groupBox_3)
+        self.label_MaZ.setObjectName("label_MaZ")
+        self.gridLayout_8.addWidget(self.label_MaZ, 0, 5, 1, 1)
+        self.verticalLayout_2.addWidget(self.groupBox_3)
+        self.groupBox_5 = QtWidgets.QGroupBox(self.centralwidget)
+        self.groupBox_5.setObjectName("groupBox_5")
+        self.gridLayout_9 = QtWidgets.QGridLayout(self.groupBox_5)
+        self.gridLayout_9.setObjectName("gridLayout_9")
+        self.label_9 = QtWidgets.QLabel(self.groupBox_5)
+        self.label_9.setObjectName("label_9")
+        self.gridLayout_9.addWidget(self.label_9, 0, 0, 1, 1)
+        self.label_20 = QtWidgets.QLabel(self.groupBox_5)
+        self.label_20.setObjectName("label_20")
+        self.gridLayout_9.addWidget(self.label_20, 1, 0, 1, 1)
+        self.label_pres_cis_1 = QtWidgets.QLabel(self.groupBox_5)
+        self.label_pres_cis_1.setObjectName("label_pres_cis_1")
+        self.gridLayout_9.addWidget(self.label_pres_cis_1, 0, 1, 1, 1)
+        self.label_pres_i2c_1 = QtWidgets.QLabel(self.groupBox_5)
+        self.label_pres_i2c_1.setObjectName("label_pres_i2c_1")
+        self.gridLayout_9.addWidget(self.label_pres_i2c_1, 1, 1, 1, 1)
+        self.verticalLayout_2.addWidget(self.groupBox_5)
+        self.groupBox_2 = QtWidgets.QGroupBox(self.centralwidget)
+        self.groupBox_2.setObjectName("groupBox_2")
+        self.gridLayout_6 = QtWidgets.QGridLayout(self.groupBox_2)
+        self.gridLayout_6.setObjectName("gridLayout_6")
+        self.label_4 = QtWidgets.QLabel(self.groupBox_2)
+        self.label_4.setObjectName("label_4")
+        self.gridLayout_6.addWidget(self.label_4, 0, 0, 1, 1)
+        self.label_temp_mpu_1 = QtWidgets.QLabel(self.groupBox_2)
+        self.label_temp_mpu_1.setObjectName("label_temp_mpu_1")
+        self.gridLayout_6.addWidget(self.label_temp_mpu_1, 2, 1, 1, 1)
+        self.label_temp_cis_1 = QtWidgets.QLabel(self.groupBox_2)
+        self.label_temp_cis_1.setObjectName("label_temp_cis_1")
+        self.gridLayout_6.addWidget(self.label_temp_cis_1, 1, 1, 1, 1)
+        self.label_24 = QtWidgets.QLabel(self.groupBox_2)
+        self.label_24.setObjectName("label_24")
+        self.gridLayout_6.addWidget(self.label_24, 2, 0, 1, 1)
+        self.label_temp_adc_1 = QtWidgets.QLabel(self.groupBox_2)
+        self.label_temp_adc_1.setObjectName("label_temp_adc_1")
+        self.gridLayout_6.addWidget(self.label_temp_adc_1, 0, 1, 1, 1)
+        self.label_22 = QtWidgets.QLabel(self.groupBox_2)
+        self.label_22.setObjectName("label_22")
+        self.gridLayout_6.addWidget(self.label_22, 1, 0, 1, 1)
+        self.label_37 = QtWidgets.QLabel(self.groupBox_2)
+        self.label_37.setObjectName("label_37")
+        self.gridLayout_6.addWidget(self.label_37, 3, 0, 1, 1)
+        self.label_temp_mag_1 = QtWidgets.QLabel(self.groupBox_2)
+        self.label_temp_mag_1.setObjectName("label_temp_mag_1")
+        self.gridLayout_6.addWidget(self.label_temp_mag_1, 3, 1, 1, 1)
+        self.verticalLayout_2.addWidget(self.groupBox_2)
+        self.groupBox_7 = QtWidgets.QGroupBox(self.centralwidget)
+        self.groupBox_7.setObjectName("groupBox_7")
+        self.gridLayout_12 = QtWidgets.QGridLayout(self.groupBox_7)
+        self.gridLayout_12.setObjectName("gridLayout_12")
+        self.label_time_1 = QtWidgets.QLabel(self.groupBox_7)
+        self.label_time_1.setObjectName("label_time_1")
+        self.gridLayout_12.addWidget(self.label_time_1, 0, 1, 1, 1)
+        self.label_date_1 = QtWidgets.QLabel(self.groupBox_7)
+        self.label_date_1.setObjectName("label_date_1")
+        self.gridLayout_12.addWidget(self.label_date_1, 1, 1, 1, 1)
+        self.label_32 = QtWidgets.QLabel(self.groupBox_7)
+        self.label_32.setObjectName("label_32")
+        self.gridLayout_12.addWidget(self.label_32, 1, 0, 1, 1)
+        self.label_31 = QtWidgets.QLabel(self.groupBox_7)
+        self.label_31.setObjectName("label_31")
+        self.gridLayout_12.addWidget(self.label_31, 0, 0, 1, 1)
+        self.label_35 = QtWidgets.QLabel(self.groupBox_7)
+        self.label_35.setObjectName("label_35")
+        self.gridLayout_12.addWidget(self.label_35, 2, 0, 1, 1)
+        self.label_hdop_1 = QtWidgets.QLabel(self.groupBox_7)
+        self.label_hdop_1.setObjectName("label_hdop_1")
+        self.gridLayout_12.addWidget(self.label_hdop_1, 2, 1, 1, 1)
+        self.verticalLayout_2.addWidget(self.groupBox_7)
+        spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
+        self.verticalLayout_2.addItem(spacerItem)
+        self.progressBar_reset = QtWidgets.QProgressBar(self.centralwidget)
+        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed)
+        sizePolicy.setHorizontalStretch(0)
+        sizePolicy.setVerticalStretch(0)
+        sizePolicy.setHeightForWidth(self.progressBar_reset.sizePolicy().hasHeightForWidth())
+        self.progressBar_reset.setSizePolicy(sizePolicy)
+        self.progressBar_reset.setProperty("value", 0)
+        self.progressBar_reset.setTextVisible(True)
+        self.progressBar_reset.setObjectName("progressBar_reset")
+        self.verticalLayout_2.addWidget(self.progressBar_reset)
+        self.pushButton_reset = QtWidgets.QPushButton(self.centralwidget)
+        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed)
+        sizePolicy.setHorizontalStretch(0)
+        sizePolicy.setVerticalStretch(0)
+        sizePolicy.setHeightForWidth(self.pushButton_reset.sizePolicy().hasHeightForWidth())
+        self.pushButton_reset.setSizePolicy(sizePolicy)
+        self.pushButton_reset.setObjectName("pushButton_reset")
+        self.verticalLayout_2.addWidget(self.pushButton_reset)
+        self.gridLayout.addLayout(self.verticalLayout_2, 0, 0, 1, 1)
+        self.gridLayout_2.addLayout(self.gridLayout, 0, 1, 1, 1)
+        self.tabWidget = QtWidgets.QTabWidget(self.centralwidget)
+        self.tabWidget.setObjectName("tabWidget")
+        self.tab = QtWidgets.QWidget()
+        self.tab.setObjectName("tab")
+        self.gridLayout_4 = QtWidgets.QGridLayout(self.tab)
+        self.gridLayout_4.setObjectName("gridLayout_4")
+        self.openGLWidget = QtWidgets.QOpenGLWidget(self.tab)
+        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding)
+        sizePolicy.setHorizontalStretch(0)
+        sizePolicy.setVerticalStretch(0)
+        sizePolicy.setHeightForWidth(self.openGLWidget.sizePolicy().hasHeightForWidth())
+        self.openGLWidget.setSizePolicy(sizePolicy)
+        self.openGLWidget.setMouseTracking(False)
+        self.openGLWidget.setObjectName("openGLWidget")
+        self.gridLayout_4.addWidget(self.openGLWidget, 0, 0, 1, 1)
+        self.tabWidget.addTab(self.tab, "")
+        self.tab_3 = QtWidgets.QWidget()
+        self.tab_3.setObjectName("tab_3")
+        self.gridLayout_3 = QtWidgets.QGridLayout(self.tab_3)
+        self.gridLayout_3.setObjectName("gridLayout_3")
+        self.openGLWidgetQuad = QtWidgets.QOpenGLWidget(self.tab_3)
+        self.openGLWidgetQuad.setObjectName("openGLWidgetQuad")
+        self.gridLayout_3.addWidget(self.openGLWidgetQuad, 0, 0, 1, 1)
+        self.tabWidget.addTab(self.tab_3, "")
+        self.tab_2 = QtWidgets.QWidget()
+        self.tab_2.setObjectName("tab_2")
+        self.gridLayout_10 = QtWidgets.QGridLayout(self.tab_2)
+        self.gridLayout_10.setObjectName("gridLayout_10")
+        self.scrollArea = QtWidgets.QScrollArea(self.tab_2)
+        self.scrollArea.setWidgetResizable(True)
+        self.scrollArea.setObjectName("scrollArea")
+        self.scrollAreaWidgetContents = QtWidgets.QWidget()
+        self.scrollAreaWidgetContents.setGeometry(QtCore.QRect(0, 0, 98, 28))
+        self.scrollAreaWidgetContents.setObjectName("scrollAreaWidgetContents")
+        self.scrollArea.setWidget(self.scrollAreaWidgetContents)
+        self.gridLayout_10.addWidget(self.scrollArea, 1, 0, 1, 1)
+        self.labelTestData = QtWidgets.QLabel(self.tab_2)
+        self.labelTestData.setObjectName("labelTestData")
+        self.gridLayout_10.addWidget(self.labelTestData, 0, 0, 1, 1)
+        self.tabWidget.addTab(self.tab_2, "")
+        self.gridLayout_2.addWidget(self.tabWidget, 0, 0, 1, 1)
+        MainWindow.setCentralWidget(self.centralwidget)
+        self.menubar = QtWidgets.QMenuBar(MainWindow)
+        self.menubar.setGeometry(QtCore.QRect(0, 0, 1337, 26))
+        self.menubar.setObjectName("menubar")
+        self.menuFile = QtWidgets.QMenu(self.menubar)
+        self.menuFile.setObjectName("menuFile")
+        self.menuCamera_Settings = QtWidgets.QMenu(self.menubar)
+        self.menuCamera_Settings.setObjectName("menuCamera_Settings")
+        MainWindow.setMenuBar(self.menubar)
+        self.statusbar = QtWidgets.QStatusBar(MainWindow)
+        self.statusbar.setObjectName("statusbar")
+        MainWindow.setStatusBar(self.statusbar)
+        self.actionClose = QtWidgets.QAction(MainWindow)
+        self.actionClose.setObjectName("actionClose")
+        self.actionFollow = QtWidgets.QAction(MainWindow)
+        self.actionFollow.setCheckable(True)
+        self.actionFollow.setChecked(True)
+        self.actionFollow.setObjectName("actionFollow")
+        self.actionReset = QtWidgets.QAction(MainWindow)
+        self.actionReset.setObjectName("actionReset")
+        self.actionSave = QtWidgets.QAction(MainWindow)
+        self.actionSave.setObjectName("actionSave")
+        self.actionSettings = QtWidgets.QAction(MainWindow)
+        self.actionSettings.setObjectName("actionSettings")
+        self.menuFile.addAction(self.actionSave)
+        self.menuFile.addAction(self.actionSettings)
+        self.menuFile.addAction(self.actionClose)
+        self.menuCamera_Settings.addAction(self.actionFollow)
+        self.menubar.addAction(self.menuFile.menuAction())
+        self.menubar.addAction(self.menuCamera_Settings.menuAction())
+
+        self.retranslateUi(MainWindow)
+        self.tabWidget.setCurrentIndex(0)
+        QtCore.QMetaObject.connectSlotsByName(MainWindow)
+
+    def retranslateUi(self, MainWindow):
+        _translate = QtCore.QCoreApplication.translate
+        MainWindow.setWindowTitle(_translate("MainWindow", "BEXUS"))
+        self.groupBox.setTitle(_translate("MainWindow", "Stats"))
+        self.labelLink_MCU2.setText(_translate("MainWindow", "Unknown"))
+        self.labelLink_MCU1.setText(_translate("MainWindow", "Unknown"))
+        self.label_2.setText(_translate("MainWindow", "Link:"))
+        self.label_25.setText(_translate("MainWindow", "MCU 2:"))
+        self.labelLatency.setText(_translate("MainWindow", "20ms"))
+        self.labelLink.setText(_translate("MainWindow", "Offline"))
+        self.label_6.setText(_translate("MainWindow", "Latency:"))
+        self.label_3.setText(_translate("MainWindow", "MCU 1:"))
+        self.label.setText(_translate("MainWindow", "Address:"))
+        self.labelAddress.setText(_translate("MainWindow", "192.168.0.8:1234"))
+        self.groupBox_6.setTitle(_translate("MainWindow", "Position"))
+        self.label_lat_1.setText(_translate("MainWindow", "51.4562"))
+        self.label_27.setText(_translate("MainWindow", "Latitude"))
+        self.label_alt_1.setText(_translate("MainWindow", "165.07 m"))
+        self.label_21.setText(_translate("MainWindow", "Altitude:"))
+        self.label_29.setText(_translate("MainWindow", "Longitude"))
+        self.label_lon_1.setText(_translate("MainWindow", "10.2452"))
+        self.groupBox_4.setTitle(_translate("MainWindow", "Gyro"))
+        self.label_AcZ.setText(_translate("MainWindow", "0"))
+        self.label_5.setText(_translate("MainWindow", "AcX:"))
+        self.label_17.setText(_translate("MainWindow", "GyZ:"))
+        self.label_16.setText(_translate("MainWindow", "GyY:"))
+        self.label_AcY.setText(_translate("MainWindow", "0"))
+        self.label_15.setText(_translate("MainWindow", "GyX:"))
+        self.label_11.setText(_translate("MainWindow", "AcY:"))
+        self.label_AcX.setText(_translate("MainWindow", "0"))
+        self.label_13.setText(_translate("MainWindow", "AcZ:"))
+        self.label_GyX.setText(_translate("MainWindow", "0"))
+        self.label_GyY.setText(_translate("MainWindow", "0"))
+        self.label_GyZ.setText(_translate("MainWindow", "0"))
+        self.groupBox_3.setTitle(_translate("MainWindow", "Magnetic"))
+        self.label_8.setText(_translate("MainWindow", "X:"))
+        self.label_18.setText(_translate("MainWindow", "Z:"))
+        self.label_MaX.setText(_translate("MainWindow", "0"))
+        self.label_12.setText(_translate("MainWindow", "Y:"))
+        self.label_MaY.setText(_translate("MainWindow", "0"))
+        self.label_MaZ.setText(_translate("MainWindow", "0"))
+        self.groupBox_5.setTitle(_translate("MainWindow", "Pressure"))
+        self.label_9.setText(_translate("MainWindow", "Cis"))
+        self.label_20.setText(_translate("MainWindow", "SPI"))
+        self.label_pres_cis_1.setText(_translate("MainWindow", "99999.4 Pa"))
+        self.label_pres_i2c_1.setText(_translate("MainWindow", "99355.0 Pa"))
+        self.groupBox_2.setTitle(_translate("MainWindow", "Temperature"))
+        self.label_4.setText(_translate("MainWindow", "ADC"))
+        self.label_temp_mpu_1.setText(_translate("MainWindow", "39.20 °C"))
+        self.label_temp_cis_1.setText(_translate("MainWindow", "36.43 °C"))
+        self.label_24.setText(_translate("MainWindow", "MPU"))
+        self.label_temp_adc_1.setText(_translate("MainWindow", "20.21 °C"))
+        self.label_22.setText(_translate("MainWindow", "Cis"))
+        self.label_37.setText(_translate("MainWindow", "Mag"))
+        self.label_temp_mag_1.setText(_translate("MainWindow", "36.01 °C"))
+        self.groupBox_7.setTitle(_translate("MainWindow", "GPS"))
+        self.label_time_1.setText(_translate("MainWindow", "12:00"))
+        self.label_date_1.setText(_translate("MainWindow", "01.01.1970"))
+        self.label_32.setText(_translate("MainWindow", "Date"))
+        self.label_31.setText(_translate("MainWindow", "Time"))
+        self.label_35.setText(_translate("MainWindow", "Hdop"))
+        self.label_hdop_1.setText(_translate("MainWindow", "0"))
+        self.pushButton_reset.setText(_translate("MainWindow", "Restart"))
+        self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab), _translate("MainWindow", "3D View"))
+        self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_3), _translate("MainWindow", "Orthogonal View"))
+        self.labelTestData.setText(_translate("MainWindow", "Test Data"))
+        self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_2), _translate("MainWindow", "Raw data"))
+        self.menuFile.setTitle(_translate("MainWindow", "File"))
+        self.menuCamera_Settings.setTitle(_translate("MainWindow", "Camera Settings"))
+        self.actionClose.setText(_translate("MainWindow", "Close"))
+        self.actionFollow.setText(_translate("MainWindow", "Follow"))
+        self.actionReset.setText(_translate("MainWindow", "Reset"))
+        self.actionSave.setText(_translate("MainWindow", "Save"))
+        self.actionSettings.setText(_translate("MainWindow", "Settings"))
+

+ 223 - 0
groundstation/ui läuft 15.10.18/ping.py

@@ -0,0 +1,223 @@
+#!/usr/bin/env python
+
+"""
+    A pure python ping implementation using raw socket.
+
+
+    Note that ICMP messages can only be sent from processes running as root.
+
+
+    Derived from ping.c distributed in Linux's netkit. That code is
+    copyright (c) 1989 by The Regents of the University of California.
+    That code is in turn derived from code written by Mike Muuss of the
+    US Army Ballistic Research Laboratory in December, 1983 and
+    placed in the public domain. They have my thanks.
+
+    Bugs are naturally mine. I'd be glad to hear about them. There are
+    certainly word - size dependenceies here.
+
+    Copyright (c) Matthew Dixon Cowles, <http://www.visi.com/~mdc/>.
+    Distributable under the terms of the GNU General Public License
+    version 2. Provided with no warranties of any sort.
+
+    Original Version from Matthew Dixon Cowles:
+      -> ftp://ftp.visi.com/users/mdc/ping.py
+
+    Rewrite by Jens Diemer:
+      -> http://www.python-forum.de/post-69122.html#69122
+
+
+    Revision history
+    ~~~~~~~~~~~~~~~~
+    March 17, 2016
+    Changes by Corentin Debains:
+    - converted to support python 3 and added packaging info
+
+    Januari 27, 2015
+    Changed receive response to not accept ICMP request messages.
+    It was possible to receive the very request that was sent.
+
+    March 11, 2010
+    changes by Samuel Stauffer:
+    - replaced time.clock with default_timer which is set to
+      time.clock on windows and time.time on other systems.
+
+    May 30, 2007
+    little rewrite by Jens Diemer:
+     -  change socket asterisk import to a normal import
+     -  replace time.time() with time.clock()
+     -  delete "return None" (or change to "return" only)
+     -  in checksum() rename "str" to "source_string"
+
+    November 22, 1997
+    Initial hack. Doesn't do much, but rather than try to guess
+    what features I (or others) will want in the future, I've only
+    put in what I need now.
+
+    December 16, 1997
+    For some reason, the checksum bytes are in the wrong order when
+    this is run under Solaris 2.X for SPARC but it works right under
+    Linux x86. Since I don't know just what's wrong, I'll swap the
+    bytes always and then do an htons().
+
+    December 4, 2000
+    Changed the struct.pack() calls to pack the checksum and ID as
+    unsigned. My thanks to Jerome Poincheval for the fix.
+
+    Last commit info:
+    ~~~~~~~~~~~~~~~~~
+    $LastChangedDate: $
+    $Rev: $
+    $Author: $
+"""
+
+
+import os, sys, socket, struct, select, time
+
+if sys.platform == "win32":
+    # On Windows, the best timer is time.clock()
+    default_timer = time.clock
+else:
+    # On most other platforms the best timer is time.time()
+    default_timer = time.time
+
+# From /usr/include/linux/icmp.h; your milage may vary.
+ICMP_ECHO_REQUEST = 8 # Seems to be the same on Solaris.
+
+
+def checksum(source_string):
+    """
+    I'm not too confident that this is right but testing seems
+    to suggest that it gives the same answers as in_cksum in ping.c
+    """
+    sum = 0
+    countTo = (len(source_string)/2)*2
+    count = 0
+    while count<countTo:
+        thisVal = source_string[count + 1]*256 + source_string[count]
+        sum = sum + thisVal
+        sum = sum & 0xffffffff # Necessary?
+        count = count + 2
+
+    if countTo<len(source_string):
+        sum = sum + source_string[len(source_string) - 1]
+        sum = sum & 0xffffffff # Necessary?
+
+    sum = (sum >> 16)  +  (sum & 0xffff)
+    sum = sum + (sum >> 16)
+    answer = ~sum
+    answer = answer & 0xffff
+
+    # Swap bytes. Bugger me if I know why.
+    answer = answer >> 8 | (answer << 8 & 0xff00)
+
+    return answer
+
+
+def receive_one_ping(my_socket, ID, timeout):
+    """
+    receive the ping from the socket.
+    """
+    timeLeft = timeout
+    while True:
+        startedSelect = default_timer()
+        whatReady = select.select([my_socket], [], [], timeLeft)
+        howLongInSelect = (default_timer() - startedSelect)
+        if whatReady[0] == []: # Timeout
+            return
+
+        timeReceived = default_timer()
+        recPacket, addr = my_socket.recvfrom(1024)
+        icmpHeader = recPacket[20:28]
+        type, code, checksum, packetID, sequence = struct.unpack(
+            "bbHHh", icmpHeader
+        )
+        # Filters out the echo request itself.
+        # This can be tested by pinging 127.0.0.1
+        # You'll see your own request
+        if type != 8 and packetID == ID:
+            bytesInDouble = struct.calcsize("d")
+            timeSent = struct.unpack("d", recPacket[28:28 + bytesInDouble])[0]
+            return timeReceived - timeSent
+
+        timeLeft = timeLeft - howLongInSelect
+        if timeLeft <= 0:
+            return
+
+
+def send_one_ping(my_socket, dest_addr, ID):
+    """
+    Send one ping to the given >dest_addr<.
+    """
+    dest_addr  =  socket.gethostbyname(dest_addr)
+
+    # Header is type (8), code (8), checksum (16), id (16), sequence (16)
+    my_checksum = 0
+
+    # Make a dummy heder with a 0 checksum.
+    header = struct.pack("bbHHh", ICMP_ECHO_REQUEST, 0, my_checksum, ID, 1)
+    bytesInDouble = struct.calcsize("d")
+    data = bytes((192 - bytesInDouble) * "Q", 'utf-8')
+    data = struct.pack("d", default_timer()) + data
+
+    # Calculate the checksum on the data and the dummy header.
+    my_checksum = checksum(header + data)
+
+    # Now that we have the right checksum, we put that in. It's just easier
+    # to make up a new header than to stuff it into the dummy.
+    header = struct.pack(
+        "bbHHh", ICMP_ECHO_REQUEST, 0, socket.htons(my_checksum), ID, 1
+    )
+    packet = header + data
+    my_socket.sendto(packet, (dest_addr, 1)) # Don't know about the 1
+
+
+def do_one(dest_addr, timeout):
+    """
+    Returns either the delay (in seconds) or none on timeout.
+    """
+    icmp = socket.getprotobyname("icmp")
+    try:
+        my_socket = socket.socket(socket.AF_INET, socket.SOCK_RAW, icmp)
+    except PermissionError as e:
+        e.args = (e.args if e.args else tuple()) + ((
+            " - Note that ICMP messages can only be sent from processes"
+            " running as root."
+        ),)
+        raise
+
+    my_ID = os.getpid() & 0xFFFF
+
+    send_one_ping(my_socket, dest_addr, my_ID)
+    delay = receive_one_ping(my_socket, my_ID, timeout)
+
+    my_socket.close()
+    return delay
+
+
+def verbose_ping(dest_addr, timeout = 2, count = 4):
+    """
+    Send >count< ping to >dest_addr< with the given >timeout< and display
+    the result.
+    """
+    for i in range(count):
+        print("ping %s..." % dest_addr, end=' ')
+        try:
+            delay  =  do_one(dest_addr, timeout)
+        except socket.gaierror as e:
+            print("failed. (socket error: '%s')" % e)
+            break
+
+        if delay is None:
+            print("failed. (timeout within %ssec.)" % timeout)
+        else:
+            delay = delay * 1000
+            print("get ping in %0.4fms" % delay)
+    print()
+
+
+if __name__ == '__main__':
+    verbose_ping("heise.de")
+    verbose_ping("google.com")
+    verbose_ping("a-test-url-taht-is-not-available.com")
+    verbose_ping("192.168.1.1")

+ 34 - 0
groundstation/ui läuft 15.10.18/pingThread.py

@@ -0,0 +1,34 @@
+#!/usr/bin/python3
+# -*- coding: utf-8 -*-
+
+import time
+
+from PyQt5.QtCore import (QThread, pyqtSignal, pyqtSlot)
+
+import ping
+from functions import *
+import socket
+
+class pingThread(QThread):
+	pong = pyqtSignal(float)
+	
+	def __init__(self, ip, timeout = 2):
+		QThread.__init__(self)
+		self.ip = ip
+		self.timeout = timeout
+		
+	def run(self):
+		while True:
+			try:
+				t = ping.do_one(self.ip, self.timeout)
+			except (socket.error, UnicodeError) as e:
+				print(e)
+				t = None
+				
+			if t == None:
+				t = -1
+			self.pong.emit(t)
+			time.sleep(0.3)
+
+	def changeIP(self, ip):
+		self.ip = ip

+ 3 - 0
groundstation/ui läuft 15.10.18/run.bat

@@ -0,0 +1,3 @@
+pyuic5 main.ui -o main_ui.py
+pyuic5 settings.ui -o settings_ui.py
+python bexus.py || pause

+ 146 - 0
groundstation/ui läuft 15.10.18/settings.ui

@@ -0,0 +1,146 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>Dialog</class>
+ <widget class="QDialog" name="Dialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>215</width>
+    <height>195</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Dialog</string>
+  </property>
+  <property name="windowIcon">
+   <iconset>
+    <normaloff>pp.png</normaloff>pp.png</iconset>
+  </property>
+  <layout class="QGridLayout" name="gridLayout">
+   <item row="0" column="0">
+    <widget class="QGroupBox" name="groupBox_2">
+     <property name="title">
+      <string>Settings</string>
+     </property>
+     <layout class="QFormLayout" name="formLayout">
+      <item row="0" column="0">
+       <widget class="QLabel" name="label">
+        <property name="text">
+         <string>IP</string>
+        </property>
+       </widget>
+      </item>
+      <item row="0" column="1">
+       <widget class="QLineEdit" name="lineEditIP">
+        <property name="sizePolicy">
+         <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+          <horstretch>0</horstretch>
+          <verstretch>0</verstretch>
+         </sizepolicy>
+        </property>
+        <property name="inputMask">
+         <string>009.009.009.009;_</string>
+        </property>
+        <property name="text">
+         <string>192.168.0.8</string>
+        </property>
+        <property name="cursorPosition">
+         <number>0</number>
+        </property>
+       </widget>
+      </item>
+      <item row="1" column="0">
+       <widget class="QLabel" name="label_4">
+        <property name="text">
+         <string>Port</string>
+        </property>
+       </widget>
+      </item>
+      <item row="1" column="1">
+       <widget class="QLineEdit" name="lineEditPort">
+        <property name="sizePolicy">
+         <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+          <horstretch>0</horstretch>
+          <verstretch>0</verstretch>
+         </sizepolicy>
+        </property>
+        <property name="inputMask">
+         <string>00009</string>
+        </property>
+        <property name="text">
+         <string>1234</string>
+        </property>
+       </widget>
+      </item>
+      <item row="2" column="0">
+       <widget class="QLabel" name="label_5">
+        <property name="text">
+         <string>Log file</string>
+        </property>
+       </widget>
+      </item>
+      <item row="2" column="1">
+       <widget class="QLineEdit" name="lineEditLogFile">
+        <property name="sizePolicy">
+         <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+          <horstretch>0</horstretch>
+          <verstretch>0</verstretch>
+         </sizepolicy>
+        </property>
+        <property name="text">
+         <string>default</string>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item row="1" column="0">
+    <widget class="QDialogButtonBox" name="buttonBox">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="standardButtons">
+      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>Dialog</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>248</x>
+     <y>254</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>157</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>Dialog</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>316</x>
+     <y>260</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>286</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>

+ 82 - 0
groundstation/ui läuft 15.10.18/settings_ui.py

@@ -0,0 +1,82 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'settings.ui'
+#
+# Created by: PyQt5 UI code generator 5.10.1
+#
+# WARNING! All changes made in this file will be lost!
+
+from PyQt5 import QtCore, QtGui, QtWidgets
+
+class Ui_Dialog(object):
+    def setupUi(self, Dialog):
+        Dialog.setObjectName("Dialog")
+        Dialog.resize(215, 195)
+        icon = QtGui.QIcon()
+        icon.addPixmap(QtGui.QPixmap("pp.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
+        Dialog.setWindowIcon(icon)
+        self.gridLayout = QtWidgets.QGridLayout(Dialog)
+        self.gridLayout.setObjectName("gridLayout")
+        self.groupBox_2 = QtWidgets.QGroupBox(Dialog)
+        self.groupBox_2.setObjectName("groupBox_2")
+        self.formLayout = QtWidgets.QFormLayout(self.groupBox_2)
+        self.formLayout.setObjectName("formLayout")
+        self.label = QtWidgets.QLabel(self.groupBox_2)
+        self.label.setObjectName("label")
+        self.formLayout.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.label)
+        self.lineEditIP = QtWidgets.QLineEdit(self.groupBox_2)
+        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
+        sizePolicy.setHorizontalStretch(0)
+        sizePolicy.setVerticalStretch(0)
+        sizePolicy.setHeightForWidth(self.lineEditIP.sizePolicy().hasHeightForWidth())
+        self.lineEditIP.setSizePolicy(sizePolicy)
+        self.lineEditIP.setCursorPosition(0)
+        self.lineEditIP.setObjectName("lineEditIP")
+        self.formLayout.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.lineEditIP)
+        self.label_4 = QtWidgets.QLabel(self.groupBox_2)
+        self.label_4.setObjectName("label_4")
+        self.formLayout.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.label_4)
+        self.lineEditPort = QtWidgets.QLineEdit(self.groupBox_2)
+        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
+        sizePolicy.setHorizontalStretch(0)
+        sizePolicy.setVerticalStretch(0)
+        sizePolicy.setHeightForWidth(self.lineEditPort.sizePolicy().hasHeightForWidth())
+        self.lineEditPort.setSizePolicy(sizePolicy)
+        self.lineEditPort.setObjectName("lineEditPort")
+        self.formLayout.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.lineEditPort)
+        self.label_5 = QtWidgets.QLabel(self.groupBox_2)
+        self.label_5.setObjectName("label_5")
+        self.formLayout.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.label_5)
+        self.lineEditLogFile = QtWidgets.QLineEdit(self.groupBox_2)
+        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
+        sizePolicy.setHorizontalStretch(0)
+        sizePolicy.setVerticalStretch(0)
+        sizePolicy.setHeightForWidth(self.lineEditLogFile.sizePolicy().hasHeightForWidth())
+        self.lineEditLogFile.setSizePolicy(sizePolicy)
+        self.lineEditLogFile.setObjectName("lineEditLogFile")
+        self.formLayout.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.lineEditLogFile)
+        self.gridLayout.addWidget(self.groupBox_2, 0, 0, 1, 1)
+        self.buttonBox = QtWidgets.QDialogButtonBox(Dialog)
+        self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
+        self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
+        self.buttonBox.setObjectName("buttonBox")
+        self.gridLayout.addWidget(self.buttonBox, 1, 0, 1, 1)
+
+        self.retranslateUi(Dialog)
+        self.buttonBox.accepted.connect(Dialog.accept)
+        self.buttonBox.rejected.connect(Dialog.reject)
+        QtCore.QMetaObject.connectSlotsByName(Dialog)
+
+    def retranslateUi(self, Dialog):
+        _translate = QtCore.QCoreApplication.translate
+        Dialog.setWindowTitle(_translate("Dialog", "Dialog"))
+        self.groupBox_2.setTitle(_translate("Dialog", "Settings"))
+        self.label.setText(_translate("Dialog", "IP"))
+        self.lineEditIP.setInputMask(_translate("Dialog", "009.009.009.009;_"))
+        self.lineEditIP.setText(_translate("Dialog", "192.168.0.8"))
+        self.label_4.setText(_translate("Dialog", "Port"))
+        self.lineEditPort.setInputMask(_translate("Dialog", "00009"))
+        self.lineEditPort.setText(_translate("Dialog", "1234"))
+        self.label_5.setText(_translate("Dialog", "Log file"))
+        self.lineEditLogFile.setText(_translate("Dialog", "default"))
+

+ 3 - 0
groundstation/ui läuft 15.10.18/udp-test.bat

@@ -0,0 +1,3 @@
+
+python listenUDP.py 
+pause

+ 215 - 0
groundstation/ui läuft 15.10.18/vispyHelper.py

@@ -0,0 +1,215 @@
+#!/usr/bin/python3
+# -*- coding: utf-8 -*-
+
+from PyQt5.QtWidgets import *
+from vispy import app, visuals, scene, io
+import numpy as np
+from colorsys import hsv_to_rgb
+
+PlotAxis = scene.visuals.create_visual_node(visuals.AxisVisual)
+Plot3DLine = scene.visuals.create_visual_node(visuals.LinePlotVisual)
+Plot3DSurface = scene.visuals.create_visual_node(visuals.SurfacePlotVisual)
+PlotCube = scene.visuals.create_visual_node(visuals.CubeVisual)
+PlotMesh = scene.visuals.create_visual_node(visuals.MeshVisual)
+
+
+vertices, faces, normals, texcoords = io.read_mesh("gondel2.obj")
+arrowVert = np.array([[0,0,0],[10,0,0],[0,10,0],[0,0,10]])
+arrowConn = np.array([[0,1],[0,2],[0,3]])
+arrows = np.array([[0,0,0,10,0,0],[0,0,0,0,10,0],[0,0,0,0,0,10]])
+
+#vertex_colors = np.random.random(12)
+vertex_colors = np.linspace(0, 1, len(vertices))
+vertex_colors = np.array([hsv_to_rgb(c, 1, 1) for c in vertex_colors])
+
+class viewQuad():
+	def __init__(self, object, follow = True):
+		self.follow = follow
+		self.canvas = scene.SceneCanvas(keys='interactive', show=False)
+		self.grid = self.canvas.central_widget.add_grid(margin=10)
+		self.views = []
+		
+		self.views.append(viewOrtho(self.grid.add_grid(row=0, col=0), 3, self.follow))
+		self.views.append(viewOrtho(self.grid.add_grid(row=1, col=0), 2, self.follow))
+		self.views.append(viewOrtho(self.grid.add_grid(row=1, col=1), 1, self.follow))
+		self.views.append(viewRot(self.grid.add_grid(row=0,col=1), self.follow))
+		
+		for view in self.views:
+			view.grid.border_color = (0.5, 0.5, 0.5, 1)
+		
+		object.setLayout(QVBoxLayout())
+		object.layout().addWidget(self.canvas.native)
+	
+	def update(self, data3d, rot):
+		for view in self.views:
+			view.update(data3d, rot)
+		
+class view3D():
+
+	def __init__(self, object, follow = False):
+		self.canvas = scene.SceneCanvas(keys='interactive', show=False)
+		self.view = self.canvas.central_widget.add_view()
+		self.view.camera = scene.cameras.FlyCamera(parent=self.view.scene, fov=60, name='Fly')
+		
+		size = 100000
+		s = np.linspace(-size, size, 50)
+		YY, XX = np.meshgrid(s, s)
+		z = -0.1 * np.sqrt(YY*YY + XX*XX)
+		c=np.zeros(shape=(50,50,4))
+		
+		for x in range(50):
+			for y in range(50):
+				if x==25 and y>=25:
+					c[x][y] = np.array([0,1,0,1])
+				elif x>=25 and y==25:
+					c[x][y] = np.array([1,0,0,1])
+				else:
+					c[x][y] = np.array([0,0.3,1,0.5])
+		
+		#print(c)
+		# colors=c,
+		self.groundPlane = Plot3DSurface(x=s, y=s, z=z, parent=self.view.scene)
+		
+		self.dataPlot = Plot3DLine(np.array([[0,0,0]], np.float32), parent=self.view.scene)
+
+		self.carPlot = PlotMesh(vertices, faces, shading='smooth',
+		vertex_colors=vertex_colors, parent=self.view.scene)
+		#self.carPlot = PlotCube((1,1.5,0.5), edge_color=(1,1,1,1), face_colors=vertex_colors, parent=self.view.scene)
+		self.carPlot.transform = visuals.transforms.MatrixTransform()
+	
+		
+		object.setLayout(QVBoxLayout())
+		object.layout().addWidget(self.canvas.native)
+		self.follow = follow
+		
+	def update(self, data3d, rot):
+		rotX, rotY, rotZ = rot
+		self.dataPlot.set_data(data3d, width=2.0, color='red',	marker_size=0,face_color=(0.2, 0.2, 1, 0.8))
+		self.carPlot.transform.reset()
+		self.carPlot.transform.rotate(rotX, (1,0,0))
+		self.carPlot.transform.rotate(rotY, (0,1,0))
+		self.carPlot.transform.rotate(rotZ, (0,0,1))
+		
+		self.carPlot.transform.translate(data3d[-1])
+		if self.follow:
+			self.view.camera.center = tuple(np.add(data3d[-1], [5,5,5]))
+		
+
+class viewRot():
+
+	def __init__(self, grid, follow = True):
+		self.grid = grid
+		self.view = self.grid.add_view(row=0,col=0)
+		self.view.camera = scene.cameras.TurntableCamera(parent=self.view.scene)
+		
+		self.dataPlot = Plot3DLine(np.array([[0,0,0]], np.float32), parent=self.view.scene)
+		
+		#self.gridlines = scene.visuals.GridLines(scale=(10,10), color=(0,1,0), parent=self.view.scene)
+
+		self.carPlot = PlotMesh(vertices, faces,
+		vertex_colors=vertex_colors, shading='smooth', parent=self.view.scene)
+		self.carPlot.transform = visuals.transforms.MatrixTransform()
+		
+		self.follow = follow
+		
+	def update(self, data3d, rot):
+		rotX, rotY, rotZ = rot
+		self.dataPlot.set_data(data3d, width=2.0, color='red', marker_size=0)
+		self.carPlot.transform.reset()
+		self.carPlot.transform.rotate(rotX, (1,0,0))
+		self.carPlot.transform.rotate(rotY, (0,1,0))
+		self.carPlot.transform.rotate(rotZ, (0,0,1))
+		self.carPlot.transform.translate(data3d[-1])
+
+		if self.follow:
+			self.view.camera.center = tuple(data3d[-1])
+			
+class viewOrtho():
+	def __init__(self, grid, up=3, follow = True):
+		self.follow = follow
+		self.up = up
+		self.grid = grid
+		
+		if self.up == 3: #top
+			la='Y'
+			lb='X'
+			ti="Top"
+		elif self.up == 2:
+			la='Y'
+			lb='Z'
+			ti="Front"
+			
+		else:
+			la='X'
+			lb='Z'
+			ti="Side"
+		
+		title = scene.Label(ti, color='white')
+		title.height_max = 40
+		self.grid.add_widget(title, row=0, col=0, col_span=2)
+		
+		yaxis = scene.AxisWidget(orientation='left',
+												 axis_label=lb+' Axis',
+												 axis_font_size=12,
+												 axis_label_margin=50,
+												 tick_label_margin=5)
+		yaxis.width_max = 80
+		self.grid.add_widget(yaxis, row=1,col=0)
+
+		xaxis = scene.AxisWidget(orientation='bottom',
+												 axis_label=la+' Axis',
+												 axis_font_size=12,
+												 axis_label_margin=50,
+												 tick_label_margin=5)
+
+		xaxis.height_max = 80
+		self.grid.add_widget(xaxis, row=2, col=1)
+		
+		right_padding = self.grid.add_widget(row=1, col=2, row_span=1)
+		right_padding.width_max = 50
+		
+		self.view = self.grid.add_view(row=1,col=1)
+		
+		self.view.camera = scene.cameras.PanZoomCamera(rect=(-5,-5,5,5), aspect=1, parent=self.view.scene)#flip=(0,1,0),
+
+		
+		yaxis.link_view(self.view)
+		xaxis.link_view(self.view)
+		
+		self.gridlines = scene.visuals.GridLines(scale=(1,1), color=(0,1,0), parent=self.view.scene)
+		
+		self.dataPlot = Plot3DLine(np.array([[0,0]], np.float32), parent=self.view.scene)
+
+		self.carPlot = PlotMesh(vertices, faces,
+		vertex_colors=vertex_colors, shading='smooth', parent=self.view.scene)
+		self.carPlot.transform = visuals.transforms.MatrixTransform()
+		
+	def update(self, data3d, rot):
+		rotX, rotY, rotZ = rot
+		if self.up == 3: #top
+			data2d = data3d[:,:2]    # X Y 
+		elif self.up == 2: #front
+			data2d = data3d[:,0:3:2] # X Z
+		else: #side
+			data2d = data3d[:,1:]    # Y Z
+		
+		self.dataPlot.set_data(data2d, width=2.0, color='red', marker_size=0)
+		self.carPlot.transform.reset()
+		self.carPlot.transform.set_ortho(-1, 1, -1, 1, -1, 1)
+		
+		rotX, rotY, rotZ = (-rotX), (-rotY), (rotZ)
+		
+		self.carPlot.transform.rotate(rotX, (1,0,0))
+		self.carPlot.transform.rotate(rotY, (0,1,0))
+		self.carPlot.transform.rotate(rotZ, (0,0,1))
+		
+		if self.up <= 2:#front
+			self.carPlot.transform.rotate(90, (1,0,0))
+		
+		if self.up == 1:#side
+			self.carPlot.transform.rotate(90, (0,1,0))
+
+		self.carPlot.transform.translate(data2d[-1])
+		if self.follow:
+			self.view.camera.center = tuple(data2d[-1])
+			

+ 193 - 0
groundstation/vispyHelper - Kopie.py

@@ -0,0 +1,193 @@
+#!/usr/bin/python3
+# -*- coding: utf-8 -*-
+
+from PyQt5.QtWidgets import *
+from vispy import app, visuals, scene, io
+import numpy as np
+from colorsys import hsv_to_rgb
+
+
+Plot3DLine = scene.visuals.create_visual_node(visuals.LinePlotVisual)
+Plot3DSurface = scene.visuals.create_visual_node(visuals.SurfacePlotVisual)
+PlotCube = scene.visuals.create_visual_node(visuals.CubeVisual)
+PlotMesh = scene.visuals.create_visual_node(visuals.MeshVisual)
+
+#vertex_colors = np.random.random(12)
+vertex_colors = np.linspace(0, 1, 2832)
+vertex_colors = np.array([hsv_to_rgb(c, 1, 1) for c in vertex_colors])
+
+vertices, faces, normals, texcoords = io.read_mesh("mainbox.obj")
+
+class viewQuad():
+	def __init__(self, object, follow = True):
+		self.follow = follow
+		self.canvas = scene.SceneCanvas(keys='interactive', show=False)
+		self.grid = self.canvas.central_widget.add_grid(margin=10)
+		self.views = []
+		
+		self.views.append(viewOrtho(self.grid.add_grid(row=0, col=0), 3, self.follow))
+		self.views.append(viewOrtho(self.grid.add_grid(row=1, col=0), 2, self.follow))
+		self.views.append(viewOrtho(self.grid.add_grid(row=1, col=1), 1, self.follow))
+		self.views.append(viewRot(self.grid.add_grid(row=0,col=1), self.follow))
+		
+		for view in self.views:
+			view.grid.border_color = (0.5, 0.5, 0.5, 1)
+		
+		object.setLayout(QVBoxLayout())
+		object.layout().addWidget(self.canvas.native)
+	
+	def update(self, data3d, rot):
+		for view in self.views:
+			view.update(data3d, rot)
+		
+class view3D():
+
+	def __init__(self, object, follow = False):
+		self.canvas = scene.SceneCanvas(keys='interactive', show=False)
+		self.view = self.canvas.central_widget.add_view()
+		self.view.camera = scene.cameras.FlyCamera(parent=self.view.scene, fov=60, name='Fly')
+		
+		s = np.linspace(-100000,100000)
+		YY, XX = np.meshgrid(s, s)
+		z = -0.1 * np.sqrt(YY*YY + XX*XX)
+		self.groundPlane = Plot3DSurface(x=s, y=s, z=z, color=(0, 0.3, 1, 0.5), parent=self.view.scene)
+		
+		self.dataPlot = Plot3DLine(np.array([[0,0,0]], np.float32), parent=self.view.scene)
+
+		self.carPlot = PlotMesh(vertices, faces, shading='smooth', parent=self.view.scene)
+		#self.carPlot = PlotCube((1,1.5,0.5), edge_color=(1,1,1,1), face_colors=vertex_colors, parent=self.view.scene)
+		self.carPlot.transform = visuals.transforms.MatrixTransform()
+		
+		object.setLayout(QVBoxLayout())
+		object.layout().addWidget(self.canvas.native)
+		self.follow = follow
+		
+	def update(self, data3d, rot):
+		rotX, rotY, rotZ = rot
+		self.dataPlot.set_data(data3d, width=2.0, color='red',	marker_size=0,face_color=(0.2, 0.2, 1, 0.8))
+		self.carPlot.transform.reset()
+		self.carPlot.transform.rotate(rotX, (1,0,0))
+		self.carPlot.transform.rotate(rotY, (0,1,0))
+		self.carPlot.transform.rotate(rotZ, (0,0,1))
+		
+		self.carPlot.transform.translate(data3d[-1])
+		if self.follow:
+			self.view.camera.center = tuple(np.add(data3d[-1], [5,5,5]))
+		
+
+class viewRot():
+
+	def __init__(self, grid, follow = True):
+		self.grid = grid
+		self.view = self.grid.add_view(row=0,col=0)
+		self.view.camera = scene.cameras.TurntableCamera(parent=self.view.scene)
+		
+		self.dataPlot = Plot3DLine(np.array([[0,0,0]], np.float32), parent=self.view.scene)
+		
+		self.gridlines = scene.visuals.GridLines(scale=(10,10), color=(0,1,0), parent=self.view.scene)
+
+		self.carPlot = PlotMesh(vertices, faces, shading='smooth', parent=self.view.scene)
+		self.carPlot.transform = visuals.transforms.MatrixTransform()
+		
+		self.follow = follow
+		
+	def update(self, data3d, rot):
+		rotX, rotY, rotZ = rot
+		self.dataPlot.set_data(data3d, width=2.0, color='red', marker_size=0)
+		self.carPlot.transform.reset()
+		self.carPlot.transform.rotate(rotX, (1,0,0))
+		self.carPlot.transform.rotate(rotY, (0,1,0))
+		self.carPlot.transform.rotate(rotZ, (0,0,1))
+		self.carPlot.transform.translate(data3d[-1])
+
+		if self.follow:
+			self.view.camera.center = tuple(data3d[-1])
+			
+class viewOrtho():
+	def __init__(self, grid, up=3, follow = True):
+		self.follow = follow
+		self.up = up
+		self.grid = grid
+		
+		if self.up == 3:
+			la='X'
+			lb='Y'
+			ti="Top"
+		elif self.up == 2:
+			la='X'
+			lb='Z'
+			ti="Front"
+			
+		else:
+			la='Y'
+			lb='Z'
+			ti="Side"
+		
+		title = scene.Label(ti, color='white')
+		title.height_max = 40
+		self.grid.add_widget(title, row=0, col=0, col_span=2)
+		
+		yaxis = scene.AxisWidget(orientation='left',
+												 axis_label=lb+' Axis',
+												 axis_font_size=12,
+												 axis_label_margin=50,
+												 tick_label_margin=5)
+		yaxis.width_max = 80
+		self.grid.add_widget(yaxis, row=1,col=0)
+
+		xaxis = scene.AxisWidget(orientation='bottom',
+												 axis_label=la+' Axis',
+												 axis_font_size=12,
+												 axis_label_margin=50,
+												 tick_label_margin=5)
+
+		xaxis.height_max = 80
+		self.grid.add_widget(xaxis, row=2, col=1)
+		
+		right_padding = self.grid.add_widget(row=1, col=2, row_span=1)
+		right_padding.width_max = 50
+		
+		self.view = self.grid.add_view(row=1,col=1)
+		
+		self.view.camera = scene.cameras.PanZoomCamera(rect=(-5,-5,5,5), aspect=1, parent=self.view.scene)#flip=(0,1,0),
+
+		
+		yaxis.link_view(self.view)
+		xaxis.link_view(self.view)
+		
+		self.gridlines = scene.visuals.GridLines(scale=(1,1), color=(0,1,0), parent=self.view.scene)
+		
+		self.dataPlot = Plot3DLine(np.array([[0,0]], np.float32), parent=self.view.scene)
+
+		self.carPlot = PlotMesh(vertices, faces, shading='smooth', parent=self.view.scene)
+		self.carPlot.transform = visuals.transforms.MatrixTransform()
+		
+	def update(self, data3d, rot):
+		rotX, rotY, rotZ = rot
+		if self.up == 3:
+			data2d = data3d[:,:2]
+		elif self.up == 2:
+			data2d = data3d[:,0:3:2]
+		else:
+			data2d = data3d[:,1:]
+		
+		self.dataPlot.set_data(data2d, width=2.0, color='red', marker_size=0)
+		self.carPlot.transform.reset()
+		self.carPlot.transform.set_ortho(-1, 1, -1, 1, -1, 1)
+		
+		if self.up == 3:#top
+			rotX, rotY, rotZ = -rotX, -rotY, rotZ
+		else:#front or side
+			rotX, rotY, rotZ = -rotX+90, -rotZ, -rotY
+		
+		self.carPlot.transform.rotate(rotX, (1,0,0))
+		self.carPlot.transform.rotate(rotY, (0,1,0))
+		self.carPlot.transform.rotate(rotZ, (0,0,1))
+		
+		if self.up == 1:#side
+			self.carPlot.transform.rotate(90, (0,1,0))
+
+		self.carPlot.transform.translate(data2d[-1])
+		if self.follow:
+			self.view.camera.center = tuple(data2d[-1])
+			

+ 225 - 0
groundstation/vispyHelper.py

@@ -0,0 +1,225 @@
+#!/usr/bin/python3
+# -*- coding: utf-8 -*-
+
+from PyQt5.QtWidgets import *
+from vispy import app, visuals, scene, io
+import numpy as np
+from colorsys import hsv_to_rgb
+
+PlotAxis = scene.visuals.create_visual_node(visuals.AxisVisual)
+Plot3DLine = scene.visuals.create_visual_node(visuals.LinePlotVisual)
+Plot3DSurface = scene.visuals.create_visual_node(visuals.SurfacePlotVisual)
+PlotCube = scene.visuals.create_visual_node(visuals.CubeVisual)
+PlotMesh = scene.visuals.create_visual_node(visuals.MeshVisual)
+
+
+vertices, faces, normals, texcoords = io.read_mesh("gondel2.obj")
+arrowVert = np.array([[0,0,0],[10,0,0],[0,10,0],[0,0,10]])
+arrowConn = np.array([[0,1],[0,2],[0,3]])
+arrows = np.array([[0,0,0,10,0,0],[0,0,0,0,10,0],[0,0,0,0,0,10]])
+
+#vertex_colors = np.random.random(12)
+vertex_colors = np.linspace(0, 1, len(vertices))
+vertex_colors = np.array([hsv_to_rgb(c, 1, 1) for c in vertex_colors])
+
+class viewQuad():
+	def __init__(self, object, follow = True):
+		self.follow = follow
+		self.canvas = scene.SceneCanvas(keys='interactive', show=False)
+		self.grid = self.canvas.central_widget.add_grid(margin=10)
+		self.views = []
+		
+		self.views.append(viewOrtho(self.grid.add_grid(row=0, col=0), 3, self.follow))
+		self.views.append(viewOrtho(self.grid.add_grid(row=1, col=0), 2, self.follow))
+		self.views.append(viewOrtho(self.grid.add_grid(row=1, col=1), 1, self.follow))
+		self.views.append(viewRot(self.grid.add_grid(row=0,col=1), self.follow))
+		
+		for view in self.views:
+			view.grid.border_color = (0.5, 0.5, 0.5, 1)
+		
+		object.setLayout(QVBoxLayout())
+		object.layout().addWidget(self.canvas.native)
+	
+	def update(self, data3d, rot):
+		for view in self.views:
+			view.update(data3d, rot)
+		
+class view3D():
+
+	def __init__(self, object, follow = False):
+		self.canvas = scene.SceneCanvas(keys='interactive', show=False)
+		self.view = self.canvas.central_widget.add_view()
+		self.view.camera = scene.cameras.FlyCamera(parent=self.view.scene, fov=60, name='Fly')
+		
+		size = 100000
+		s = np.linspace(-size, size, 50)
+		YY, XX = np.meshgrid(s, s)
+		z = -0.1 * np.sqrt(YY*YY + XX*XX)
+		c=np.zeros(shape=(50,50,4))
+		
+		for x in range(50):
+			for y in range(50):
+				if x==25 and y>=25:
+					c[x][y] = np.array([0,1,0,1])
+				elif x>=25 and y==25:
+					c[x][y] = np.array([1,0,0,1])
+				else:
+					c[x][y] = np.array([0,0.3,1,0.5])
+		
+		#print(c)
+		# colors=c,
+		self.groundPlane = Plot3DSurface(x=s, y=s, z=z, parent=self.view.scene)
+		
+		self.dataPlot = Plot3DLine(np.array([[0,0,0]], np.float32), parent=self.view.scene)
+		self.dataPlot2 = Plot3DLine(np.array([[0,0,0]], np.float32), parent=self.view.scene)
+
+		self.carPlot = PlotMesh(vertices, faces, shading='smooth',
+		vertex_colors=vertex_colors, parent=self.view.scene)
+		#self.carPlot = PlotCube((1,1.5,0.5), edge_color=(1,1,1,1), face_colors=vertex_colors, parent=self.view.scene)
+		self.carPlot.transform = visuals.transforms.MatrixTransform()
+	
+		
+		object.setLayout(QVBoxLayout())
+		object.layout().addWidget(self.canvas.native)
+		self.follow = follow
+		
+	def update(self, data3d, rot):
+		rotX, rotY, rotZ = rot
+		self.dataPlot.set_data(data3d[0], width=2.0, color='red',	marker_size=0,face_color=(0.2, 0.2, 1, 0.8))
+		self.dataPlot2.set_data(data3d[1], width=2.0, color='green',	marker_size=0,face_color=(0.2, 0.2, 1, 0.8))
+		self.carPlot.transform.reset()
+		self.carPlot.transform.rotate(rotX, (1,0,0))
+		self.carPlot.transform.rotate(rotY, (0,1,0))
+		self.carPlot.transform.rotate(rotZ, (0,0,1))
+		
+		self.carPlot.transform.translate(data3d[0][-1])
+		if self.follow:
+			self.view.camera.center = tuple(np.add(data3d[0][-1], [5,5,5]))
+		
+
+class viewRot():
+
+	def __init__(self, grid, follow = True):
+		self.grid = grid
+		self.view = self.grid.add_view(row=0,col=0)
+		self.view.camera = scene.cameras.TurntableCamera(parent=self.view.scene)
+		
+		self.dataPlot = Plot3DLine(np.array([[0,0,0]], np.float32), parent=self.view.scene)
+		self.dataPlot2 = Plot3DLine(np.array([[0,0,0]], np.float32), parent=self.view.scene)
+		
+		#self.gridlines = scene.visuals.GridLines(scale=(10,10), color=(0,1,0), parent=self.view.scene)
+
+		self.carPlot = PlotMesh(vertices, faces,
+		vertex_colors=vertex_colors, shading='smooth', parent=self.view.scene)
+		self.carPlot.transform = visuals.transforms.MatrixTransform()
+		
+		self.follow = follow
+		
+	def update(self, data3d, rot):
+		rotX, rotY, rotZ = rot
+		self.dataPlot.set_data(data3d[0], width=2.0, color='red', marker_size=0)
+		self.dataPlot2.set_data(data3d[1], width=2.0, color='green', marker_size=0)
+		print(data3d[1])
+		self.carPlot.transform.reset()
+		self.carPlot.transform.rotate(rotX, (1,0,0))
+		self.carPlot.transform.rotate(rotY, (0,1,0))
+		self.carPlot.transform.rotate(rotZ, (0,0,1))
+		self.carPlot.transform.translate(data3d[0][-1])
+
+		if self.follow:
+			self.view.camera.center = tuple(data3d[0][-1])
+			
+class viewOrtho():
+	def __init__(self, grid, up=3, follow = True):
+		self.follow = follow
+		self.up = up
+		self.grid = grid
+		
+		if self.up == 3: #top
+			la='Y'
+			lb='X'
+			ti="Top"
+		elif self.up == 2:
+			la='Y'
+			lb='Z'
+			ti="Front"
+			
+		else:
+			la='X'
+			lb='Z'
+			ti="Side"
+		
+		title = scene.Label(ti, color='white')
+		title.height_max = 40
+		self.grid.add_widget(title, row=0, col=0, col_span=2)
+		
+		yaxis = scene.AxisWidget(orientation='left',
+												 axis_label=lb+' Axis',
+												 axis_font_size=12,
+												 axis_label_margin=50,
+												 tick_label_margin=5)
+		yaxis.width_max = 80
+		self.grid.add_widget(yaxis, row=1,col=0)
+
+		xaxis = scene.AxisWidget(orientation='bottom',
+												 axis_label=la+' Axis',
+												 axis_font_size=12,
+												 axis_label_margin=50,
+												 tick_label_margin=5)
+
+		xaxis.height_max = 80
+		self.grid.add_widget(xaxis, row=2, col=1)
+		
+		right_padding = self.grid.add_widget(row=1, col=2, row_span=1)
+		right_padding.width_max = 50
+		
+		self.view = self.grid.add_view(row=1,col=1)
+		
+		self.view.camera = scene.cameras.PanZoomCamera(rect=(-5,-5,5,5), aspect=1, parent=self.view.scene)#flip=(0,1,0),
+
+		
+		yaxis.link_view(self.view)
+		xaxis.link_view(self.view)
+		
+		self.gridlines = scene.visuals.GridLines(scale=(1,1), color=(0,1,0), parent=self.view.scene)
+		
+		self.dataPlot = Plot3DLine(np.array([[0,0]], np.float32), parent=self.view.scene)
+		self.dataPlot2 = Plot3DLine(np.array([[0,0]], np.float32), parent=self.view.scene)
+
+		self.carPlot = PlotMesh(vertices, faces,
+		vertex_colors=vertex_colors, shading='smooth', parent=self.view.scene)
+		self.carPlot.transform = visuals.transforms.MatrixTransform()
+		
+	def update(self, data3d, rot):
+		rotX, rotY, rotZ = rot
+		if self.up == 3: #top
+			data2d = data3d[0][:,:2]    # X Y 
+			data2d2 = data3d[1][:,:2]    # X Y 
+		elif self.up == 2: #front
+			data2d = data3d[0][:,0:3:2] # X Z
+			data2d2 = data3d[1][:,0:3:2] # X Z
+		else: #side
+			data2d = data3d[0][:,1:]    # Y Z
+			data2d2 = data3d[1][:,1:]    # Y Z
+		
+		self.dataPlot.set_data(data2d, width=2.0, color='red', marker_size=0)
+		self.dataPlot2.set_data(data2d2, width=2.0, color='green', marker_size=0)
+		self.carPlot.transform.reset()
+		self.carPlot.transform.set_ortho(-1, 1, -1, 1, -1, 1)
+		
+		rotX, rotY, rotZ = (-rotX), (-rotY), (rotZ)
+		
+		self.carPlot.transform.rotate(rotX, (1,0,0))
+		self.carPlot.transform.rotate(rotY, (0,1,0))
+		self.carPlot.transform.rotate(rotZ, (0,0,1))
+		
+		if self.up <= 2:#front
+			self.carPlot.transform.rotate(90, (1,0,0))
+		
+		if self.up == 1:#side
+			self.carPlot.transform.rotate(90, (0,1,0))
+
+		self.carPlot.transform.translate(data2d[-1])
+		if self.follow:
+			self.view.camera.center = tuple(data2d[-1])
+