Moved QtGPS here
This commit is contained in:
303
gps_tool/gps_tool.py
Executable file
303
gps_tool/gps_tool.py
Executable file
@ -0,0 +1,303 @@
|
||||
#! /usr/bin/python3
|
||||
|
||||
import os
|
||||
import sys
|
||||
import yaml
|
||||
|
||||
from math import radians
|
||||
from datetime import datetime
|
||||
from time import strftime, time
|
||||
|
||||
from gps import *
|
||||
import gpxpy
|
||||
|
||||
|
||||
from PySide2.QtGui import *
|
||||
from PySide2.QtWidgets import QApplication, QMainWindow, QGraphicsEllipseItem, QFileDialog, QGraphicsScene, QGraphicsProxyWidget
|
||||
from PySide2.QtCore import QObject, Slot, Signal, QThread, QDir, QRect
|
||||
|
||||
import matplotlib as mp
|
||||
import matplotlib.pyplot as plot
|
||||
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
|
||||
from matplotlib.figure import Figure
|
||||
from matplotlib.backends.qt_compat import QtCore, QtWidgets
|
||||
from matplotlib import cm
|
||||
|
||||
from MainWindow import Ui_MainWindow
|
||||
|
||||
import maidenhead as mh
|
||||
|
||||
##############################################################################
|
||||
# log
|
||||
##############################################################################
|
||||
|
||||
#if not os.path.isdir("logs"):
|
||||
# os.mkdir("logs")
|
||||
|
||||
fix_status = [
|
||||
'Normal', 'DGPS', 'RTK FixPt', 'RTK Float', 'DR Fix', 'GNSSDR', 'Surveyed', 'Simulated', 'P(Y) Fix'
|
||||
];
|
||||
|
||||
fix_mode = [
|
||||
'0=None', '1=No', '2=2D', '3=3D'
|
||||
];
|
||||
|
||||
gpsd_host="127.0.0.1"
|
||||
gpsd_report = ()
|
||||
|
||||
##############################################################################
|
||||
# thread
|
||||
##############################################################################
|
||||
|
||||
class DataSignal(QObject):
|
||||
sig = Signal(int)
|
||||
|
||||
class DataThread(QThread):
|
||||
def __init__(self, parent=None):
|
||||
super(DataThread, self).__init__(parent)
|
||||
self.new_data = DataSignal()
|
||||
|
||||
def run(self):
|
||||
global gpsd_host, gpsd_report
|
||||
gpsd = gps(host=gpsd_host, mode=WATCH_ENABLE|WATCH_NEWSTYLE)
|
||||
|
||||
while not self.isInterruptionRequested():
|
||||
gpsd_report = gpsd.next() # wait for next packet
|
||||
self.new_data.sig.emit(1)
|
||||
|
||||
def stop(self):
|
||||
self.requestInterruption()
|
||||
self.wait()
|
||||
|
||||
##############################################################################
|
||||
# plot
|
||||
##############################################################################
|
||||
|
||||
##############################################################################
|
||||
# main
|
||||
##############################################################################
|
||||
|
||||
class MainWindow(QMainWindow, Ui_MainWindow):
|
||||
def __init__(self, *args, obj=None, **kwargs):
|
||||
super(MainWindow, self).__init__(*args, **kwargs)
|
||||
self.setupUi(self)
|
||||
|
||||
class QtGPSWindow(MainWindow):
|
||||
def __init__(self, *args, obj=None, **kwargs):
|
||||
super(QtGPSWindow, self).__init__(*args, **kwargs)
|
||||
|
||||
self.exitButton.clicked.connect(self.exit_button)
|
||||
self.toggleLogging.clicked.connect(self.toggle_logging)
|
||||
|
||||
# GPS log
|
||||
self.gpx = gpxpy.gpx.GPX()
|
||||
# Create first track in our GPX:
|
||||
self.gpx_track = gpxpy.gpx.GPXTrack()
|
||||
self.gpx.tracks.append(self.gpx_track)
|
||||
# Create first segment in our GPX track:
|
||||
self.gpx_segment = gpxpy.gpx.GPXTrackSegment()
|
||||
self.gpx_track.segments.append(self.gpx_segment)
|
||||
|
||||
self.log_enabled = 0
|
||||
self.dirname = ""
|
||||
self.num_points = 0
|
||||
|
||||
self.my_thread = DataThread()
|
||||
|
||||
self.txtSatellites.setReadOnly(True)
|
||||
self.black = QColor(0,0,0)
|
||||
self.gray = QColor(128,128,128)
|
||||
self.red = QColor(255,0,0)
|
||||
self.orange = QColor(255,80,0)
|
||||
self.yellow = QColor(255,211,0)
|
||||
self.green = QColor(0,255,0)
|
||||
self.green = QColor(0,0,255)
|
||||
|
||||
self.scene = QtWidgets.QGraphicsScene()
|
||||
self.viewSatellites.setScene(self.scene)
|
||||
self.viewSatellites.scale(0.65, 0.65)
|
||||
|
||||
@Slot(list)
|
||||
def update_data(self, x):
|
||||
global gpsd_report
|
||||
report = gpsd_report
|
||||
|
||||
if report['class'] == 'TPV':
|
||||
status = getattr(report, 'status', 0)
|
||||
mode = getattr(report, 'mode', 0)
|
||||
time = getattr(report, 'time', 0)
|
||||
lat = getattr(report, 'lat', 0.0)
|
||||
lon = getattr(report, 'lon', 0.0)
|
||||
elev = getattr(report, 'altHAE', 0.0) # meters
|
||||
heading = getattr(report, 'track', 0.0)
|
||||
speed = getattr(report, 'speed', 0.0) # m/sec
|
||||
|
||||
self.txtStatus.setText(fix_status[status])
|
||||
self.txtMode.setText(fix_mode[mode])
|
||||
self.txtTime.setText(time)
|
||||
self.txtLatitude.setText("%10.7f" %(lat))
|
||||
self.txtLongitude.setText("%10.7f" % (lon))
|
||||
self.txtAltitude.setText("%7.2f" % (elev))
|
||||
self.txtHeading.setText("%7.2f" % (heading))
|
||||
self.txtSpeed.setText("%7.2f" % (speed))
|
||||
|
||||
self.txtGrid.setText(mh.to_maiden(lat, lon))
|
||||
|
||||
if self.log_enabled:
|
||||
point = gpxpy.gpx.GPXTrackPoint(lat, lon, elevation=elev)
|
||||
point.time = datetime.now()
|
||||
self.gpx_segment.points.append(point)
|
||||
self.num_points += 1
|
||||
|
||||
elif report['class'] == 'SKY':
|
||||
self.txtHDOP.setText(str(getattr(report, 'hdop', 0.0)))
|
||||
self.txtVDOP.setText(str(getattr(report, 'vdop', 0.0)))
|
||||
satellites = getattr(report, 'satellites')
|
||||
|
||||
txt = "%4s %4s %4s %3s %1s" % ('PRN', 'Azim', 'Elev', 'SNR', 'Used')
|
||||
self.txtSatellites.setTextColor(self.black)
|
||||
self.txtSatellites.setFontWeight(QFont.Bold)
|
||||
self.txtSatellites.setText(txt)
|
||||
|
||||
prns = []
|
||||
radius = []
|
||||
theta = []
|
||||
colors = []
|
||||
markers = []
|
||||
|
||||
for (i, satellite) in enumerate(satellites):
|
||||
prn = satellite['PRN']
|
||||
azim = satellite['az']
|
||||
elev = satellite['el']
|
||||
snr = satellite['ss']
|
||||
used = satellite['used']
|
||||
# health = satellite['health']
|
||||
|
||||
# don't show if not visible
|
||||
if azim >= 360 or elev < -10 or elev > 90:
|
||||
continue;
|
||||
|
||||
cname = 'black'
|
||||
color = self.black
|
||||
if snr < 12:
|
||||
cname = 'gray'
|
||||
color = self.gray
|
||||
elif snr < 30:
|
||||
cname = 'red'
|
||||
color = self.red
|
||||
elif snr < 36:
|
||||
cname = 'yellow'
|
||||
color = self.yellow
|
||||
elif snr < 42:
|
||||
color = 'green'
|
||||
color = self.green
|
||||
else:
|
||||
color = 'blue'
|
||||
color = self.blue
|
||||
|
||||
txt = "%4u %4u %4u %3u %1u" % (prn, azim, elev, snr, used)
|
||||
self.txtSatellites.setTextColor(color)
|
||||
if used:
|
||||
self.txtSatellites.setFontWeight(QFont.Bold)
|
||||
else:
|
||||
self.txtSatellites.setFontWeight(QFont.Normal)
|
||||
self.txtSatellites.append(txt)
|
||||
|
||||
prns.append(prn)
|
||||
theta.append(radians(float(azim)))
|
||||
radius.append(float(elev))
|
||||
colors.append(cname)
|
||||
if used:
|
||||
markers.append(ord('P'))
|
||||
else:
|
||||
markers.append(ord('+'))
|
||||
|
||||
area = 100
|
||||
figure = Figure()
|
||||
polar = figure.add_subplot(111, projection='polar')
|
||||
polar.scatter(theta, radius, c=colors, s=area, cmap=None, alpha=0.75)
|
||||
polar.set_rorigin(90.0)
|
||||
polar.set_yticklabels([])
|
||||
for i, prn in enumerate(prns):
|
||||
polar.annotate(str(prn), (theta[i], radius[i]))
|
||||
|
||||
polar.set_theta_direction(-1)
|
||||
polar.set_theta_zero_location('N', offset=0)
|
||||
|
||||
canvas = FigureCanvas(figure)
|
||||
self.scene.addWidget(canvas)
|
||||
h = float(self.scene.height()) / 2.0
|
||||
w = float(self.scene.width()) / 2.0
|
||||
self.viewSatellites.centerOn(w,h)
|
||||
self.viewSatellites.show()
|
||||
|
||||
self.show()
|
||||
|
||||
|
||||
def toggle_logging(self):
|
||||
if self.log_enabled:
|
||||
self.log_enabled = 0
|
||||
self.dir_dialog()
|
||||
self.save_log()
|
||||
self.toggleLogging.setText("Start")
|
||||
else:
|
||||
self.log_enabled = 1
|
||||
self.toggleLogging.setText("Stop")
|
||||
|
||||
def dir_dialog(self):
|
||||
dirdialog = QFileDialog(self)
|
||||
dirdialog.setDirectory(QDir.currentPath())
|
||||
dirdialog.setDefaultSuffix("gpx")
|
||||
dirdialog.setFileMode(QFileDialog.Directory)
|
||||
dirdialog.setOption(QFileDialog.ShowDirsOnly, True)
|
||||
selected = dirdialog.exec()
|
||||
if selected:
|
||||
dirname = dirdialog.selectedFiles()[0]
|
||||
# self.outputDir.setText(dirname)
|
||||
self.dirname = dirname
|
||||
|
||||
def save_log(self):
|
||||
if self.num_points:
|
||||
self.num_points = 0
|
||||
log = open(strftime(self.dirname + "/track-%Y%m%d-%H%M.gpx"), 'w')
|
||||
log.write(self.gpx.to_xml())
|
||||
log.close()
|
||||
self.gpx_segment = gpxpy.gpx.GPXTrackSegment()
|
||||
|
||||
def start_thread(self):
|
||||
if not self.my_thread.isRunning():
|
||||
self.my_thread.new_data.sig.connect(self.update_data)
|
||||
self.my_thread.start()
|
||||
self.my_thread.setTerminationEnabled(True)
|
||||
|
||||
def stop_thread(self):
|
||||
self.my_thread.stop()
|
||||
self.my_thread.wait(3)
|
||||
self.save_log()
|
||||
|
||||
def closeEvent(self, event):
|
||||
self.stop_thread()
|
||||
event.accept()
|
||||
sys.exit(0)
|
||||
|
||||
def exit_button(self):
|
||||
self.stop_thread()
|
||||
if self.log_enabled:
|
||||
self.toggle_logging()
|
||||
self.log_enabled = 0
|
||||
sys.exit(0)
|
||||
|
||||
if os.path.isfile('config.yaml'):
|
||||
with open('config.yaml') as f:
|
||||
config = yaml.load(f, Loader=yaml.FullLoader)
|
||||
if 'host' in config:
|
||||
gpsd_host = config['host']
|
||||
|
||||
if __name__ == '__main__':
|
||||
app = QApplication(sys.argv)
|
||||
|
||||
window = QtGPSWindow()
|
||||
window.show()
|
||||
window.start_thread()
|
||||
ret = app.exec_()
|
||||
sys.exit(ret)
|
||||
Reference in New Issue
Block a user