Skip to content

Commit dbba06d

Browse files
Merge branch 'NMEA0183-Data-Input' into master
2 parents b1996f7 + 2d7fd3a commit dbba06d

3 files changed

Lines changed: 301 additions & 3 deletions

File tree

lib/aircraft.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,7 @@ def __init__(self):
311311

312312
self.WPDist = 0
313313
self.WPTrack = 0
314+
self.WPName = None
314315

315316
self.ILSDev = 0
316317
self.GSDev = 0

lib/inputs/serial_g3x.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ def mean(nums):
149149
else:
150150
if (self.isPlaybackMode ): # if no bytes read and in playback mode. then reset the file pointer to the start of the file.
151151
self.ser.seek(0)
152-
return aircraft
152+
return aircraft
153153

154154
SentID = self.ser.read(1) # get message id
155155
if(not isinstance(SentID,str)): SentID = SentID.decode('utf-8')
@@ -269,8 +269,7 @@ def mean(nums):
269269
self.ser.flushInput() # flush the serial after every message else we see delays
270270
return aircraft
271271

272-
except serial.serialutil.SerialException as e:
273-
print(e)
272+
except:
274273
print("G3X serial exception")
275274
aircraft.errorFoundNeedToExit = True
276275

lib/inputs/serial_nmea.py

Lines changed: 298 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,298 @@
1+
#!/usr/bin/env python
2+
3+
# Serial input source
4+
# NMEA 0183 V3.01
5+
6+
from ._input import Input
7+
from . import _utils
8+
import serial
9+
import struct
10+
import math, sys
11+
import time
12+
import datetime
13+
import pytz
14+
import airportsdata
15+
from timezonefinder import TimezoneFinder
16+
17+
tf = TimezoneFinder()
18+
19+
#######################################
20+
### FAA Mode indicator decoder ###
21+
## A = Autonomous Mode ##
22+
## C = Caution ##
23+
## D = Differential (WAAS) ##
24+
## E = Estimated/Dead-Reckoning Mode ##
25+
## F = RTK Float Mode ##
26+
## M = Manual Input Mode ##
27+
## N = Invalid ##
28+
## R = RTK Integer Mode ##
29+
## S = Simulated Mode ##
30+
## U = Unsafe ##
31+
#######################################
32+
33+
#######################################
34+
### GPS Quality Indicator Decoder ###
35+
## 0 = No Fix ##
36+
## 1 = GPS Fix ##
37+
## 2 = Differential (WAAS) ##
38+
## 3 = PPS Fix ##
39+
## 4 = Realtime Kinematic ##
40+
## 5 = Float RTK ##
41+
## 6 = Estimated (Dead-Reckoning) ##
42+
## 7 = Manual Input Mode ##
43+
## 8 = Simulated Mode ##
44+
#######################################
45+
46+
#############################################
47+
### NMEA Sentences Not Used Intentionally ###
48+
## GPGSA = GPS Degree of Precision ##
49+
## GPGSV = GPS Satellites In View ##
50+
## GPAP A/B = Autopilot Type A/B ##
51+
## Most proprietary sentences ##
52+
#############################################
53+
54+
class serial_nmea(Input):
55+
def __init__(self):
56+
self.name = "nmea"
57+
self.version = 3.01
58+
self.inputtype = "serial"
59+
60+
# Setup moving averages to smooth a bit
61+
self.readings = []
62+
self.max_samples = 10
63+
self.readings1 = []
64+
self.max_samples1 = 20
65+
self.EOL = 10
66+
67+
def initInput(self,num, aircraft):
68+
Input.initInput(self,num, aircraft) # call parent init Input.
69+
self.efis_data_port = "/dev/cu.PL2303G-USBtoUART110"
70+
self.efis_data_baudrate = 9600
71+
# open serial connection.
72+
self.ser = serial.Serial(
73+
port=self.efis_data_port,
74+
baudrate=self.efis_data_baudrate,
75+
parity=serial.PARITY_NONE,
76+
stopbits=serial.STOPBITS_ONE,
77+
bytesize=serial.EIGHTBITS,
78+
timeout=None,
79+
)
80+
81+
# check for system platform??
82+
#if sys.platform.startswith('win'):
83+
# self.EOL = 10
84+
#else:
85+
# self.EOL = 13
86+
self.EOL = 13
87+
88+
# close this input source
89+
def closeInput(self, aircraft):
90+
self.ser.close()
91+
92+
#############################################
93+
## Function: readMessage
94+
def readMessage(self, aircraft):
95+
airports = airportsdata.load()
96+
def mean(nums):
97+
return float(sum(nums)) / max(len(nums), 1)
98+
99+
if aircraft.errorFoundNeedToExit:
100+
return aircraft
101+
try:
102+
x = 0
103+
t = self.ser.read(1)
104+
if len(t) != 0:
105+
x = ord(t)
106+
if x == 36:
107+
msg = self.ser.readline()
108+
checksum = None
109+
aircraft.msg_last = msg
110+
if self.output_logFile != None:
111+
Input.addToLog(self,self.output_logFile,bytes([64]))
112+
Input.addToLog(self,self.output_logFile,msg)
113+
msg = msg.rstrip(b'\r\n')
114+
try:
115+
msg = msg.decode('utf-8')
116+
except:
117+
aircraft.nav.msg_bad += 1
118+
return aircraft
119+
try:
120+
msg = msg.split('*')
121+
checksum = msg[1:]
122+
# print(checksum)
123+
msg = msg[0]
124+
# print(msg)
125+
if(self.calcChecksum(msg,checksum)):
126+
aircraft.msg_count += 1
127+
else:
128+
aircraft.gps.msg_bad += 1
129+
return aircraft
130+
msg = msg.split(",")
131+
# print(msg)
132+
except:
133+
msg = msg.split(",")
134+
aircraft.msg_count += 1
135+
sentence = msg[0] # break out sentence ID from the list so we can perform a switch action on it
136+
match sentence:
137+
case "GPRMC": # Recommended Minimum Navigation Info Sentence C
138+
utctime = msg[1] # UTC Time in format hhmmss
139+
status = msg[2] # GPS Status, A = Acive, V = Warning
140+
lat = msg[3] # Latitude in format ddmm.mmmm
141+
lathemi = msg[4] # Latitude hemisphere in N/S
142+
lon = msg[5] # Longitude in format dddmm.mmmm
143+
lonhemi = msg[6] # Longitude hemisphere in E/W
144+
gs = msg[7] # GPS Groundspeed
145+
truetrack = msg[8] # GPS Track in true heading
146+
date = msg[9] # UTC date in ddmmyy
147+
magvar = msg[10] # Magnetic variation x.x degrees
148+
magvardir = msg[11] # magnetic variation E or W
149+
mode = msg[12] # FAA Mode, explained at top of file
150+
aircraft.sys_time_string = "%d:%d:%d"%(int(utctime[0:1]),int(utctime[2:3]),int(utctime[4:5]))
151+
self.time_stamp_string = aircraft.sys_time_string
152+
self.time_stamp_min = int(utctime[2:3])
153+
self.time_stamp_sec = int(utctime[4:5])
154+
aircraft.gps.LatHemi = lathemi
155+
aircraft.gps.LatDeg = int(lat[0:1])
156+
aircraft.gps.LatMin = float(lat[2:8])
157+
aircraft.gps.LonHemi = lonhemi
158+
aircraft.gps.LonDeg = int(lon[0:2])
159+
aircraft.gps.LonMin = int(lon[3:9])
160+
aircraft.gps.GndSpeed = float(gs)
161+
aircraft.groundspeed = float(gs)
162+
if(magvardir == "E"):
163+
magvar = (magvar - (magvar * 2))
164+
aircraft.gndtrack = (truetrack + magvar)
165+
aircraft.gps.GPSTrack = aircraft.gndtrack
166+
aircraft.gps.Source = "NMEA"
167+
case "GPRMB": # Recommended Minimum Navigation Info Sentence B - Note: Destination waypoint is NOT related to the actual flight plan - it is the waypoint currently being navigated to
168+
status = msg[1] # A = Active, V = Invalid
169+
xtkerror = msg[2] # Crosstrack error NM x.x
170+
steerdir = msg[3] # Steer left or right for course? L/R
171+
originwpt = msg[4] # Origin Waypoint ID xxxxx
172+
destwpt = msg[5] # Destination Waypoint ID xxxxx
173+
destlat = msg[6] # Destination waypoint latitude ddmm.mmmm
174+
destlathemi = msg[7] # Destination waypoint latitude hemisphere N/S
175+
destlon = msg[8] # Destination waypoint longitude dddmm.mmmm
176+
destlonhemi = msg[9] # Destination longitude hemisphere E/W
177+
distwpt = msg[10] # Distance to destination waypoint xx.x
178+
destbrg = msg[11] # Bearing in degrees TRUE to destination waypoint xxx.x
179+
wptclosure = msg[12] # Closure rate to destination waypoint xxx.x
180+
wptarrival = msg[13] # Arrival alarm for destination waypoint A = arriving, V = not arriving
181+
mode = msg[14] # FAA Mode, explained at top of file
182+
if(not aircraft.mag_decl == None):
183+
aircraft.nav.WPTrack = float(destbrg) + aircraft.mag_decl
184+
if(not aircraft.nav.Source == "NMEA GPBWC"):
185+
aircraft.nav.WPName = destwpt
186+
aircraft.nav.WPDist = float(distwpt)
187+
case "GPGGA": # GPS Pos and Altitude
188+
utctime = msg[1] # hhmmss
189+
lat = msg[2] # Latitude in format ddmm.mmmm
190+
lathemi = msg[3] # Latitude hemisphere in N/S
191+
lon = msg[4] # Longitude in format dddmm.mmmm
192+
lonhemi = msg[5] # Longitude hemisphere in E/W
193+
quality = msg[6] # GPS Quality explained at top of file x
194+
satnum = msg[7] # Number of satellites used in solution xx
195+
hprecision = msg[8] # Horizontal precision in meters x.x
196+
gpsalt = msg[9] # GPS Altitude in MSL (geoid) xx.x
197+
altunit = msg[10] # GPS Altitude unit of measure X
198+
geosep = msg[11] # Geoidal separation between WGS-84 and MSL xx.x
199+
geosepunit = msg[12] # unit of measure for geoidal separation X
200+
diffage = msg[13] # Age of differential (WAAS) GPS data in seconds x.x
201+
diffstation = msg[14] # Station ID of differential reference station (WAAS Station)
202+
aircraft.sys_time_string = "%d:%d:%d"%(int(utctime[0:1]),int(utctime[2:3]),int(utctime[4:5]))
203+
self.time_stamp_string = aircraft.sys_time_string
204+
self.time_stamp_min = int(utctime[2:3])
205+
self.time_stamp_sec = int(utctime[4:5])
206+
aircraft.gps.LatHemi = lathemi
207+
aircraft.gps.LatDeg = int(lat[0:1])
208+
aircraft.gps.LatMin = float(lat[2:8])
209+
aircraft.gps.LonHemi = lonhemi
210+
aircraft.gps.LonDeg = int(lon[0:2])
211+
aircraft.gps.LonMin = int(lon[3:9])
212+
aircraft.gps.SatsTracked = int(satnum)
213+
aircraft.gps.GPSAlt = int(round(float(gpsalt)))
214+
if(quality == "1"):
215+
aircraft.gps.GPSStatus = 2
216+
aircraft.gps.GPSWAAS = 0
217+
if(quality == "2"):
218+
aircraft.gps.GPSStatus = 3
219+
aircraft.gps.GPSWAAS = 1
220+
case "GPGLL": # GPS Lat/Long
221+
lat = msg[1] # Latitude in format ddmm.mmmm
222+
lathemi = msg[2] # Latitude hemisphere in N/S
223+
lon = msg[3] # Longitude in format dddmm.mmmm
224+
lonhemi = msg[4] # Longitude hemisphere in E/W
225+
utctime = msg[5] # hhmmss
226+
status = msg[6] # A = Active, V = Invalid
227+
mode = msg[7] # FAA Mode, explained at top of file
228+
aircraft.sys_time_string = "%d:%d:%d"%(int(utctime[0:1]),int(utctime[2:3]),int(utctime[4:5]))
229+
self.time_stamp_string = aircraft.sys_time_string
230+
self.time_stamp_min = int(utctime[2:3])
231+
self.time_stamp_sec = int(utctime[4:5])
232+
aircraft.gps.LatHemi = lathemi
233+
aircraft.gps.LatDeg = int(lat[0:1])
234+
aircraft.gps.LatMin = float(lat[2:8])
235+
aircraft.gps.LonHemi = lonhemi
236+
aircraft.gps.LonDeg = int(lon[0:2])
237+
aircraft.gps.LonMin = int(lon[3:9])
238+
if(mode == "D"):
239+
aircraft.gps.GPSWAAS = 1
240+
else:
241+
aircraft.gps.GPSWAAS = 0
242+
case "GPBOD": # Bearing waypoint to waypoint
243+
brgtrue = msg[1] # Bearing to waypoint in degrees True xx.x
244+
brgmag = msg[3] # bearing to waypoint in degrees Magnetic xx.x
245+
nextwpt = msg[5] # Destination (next) waypoint XXXX
246+
originwpt = msg[6] # Origin (from) waypoint XXXX
247+
case "GPBWC": # bearing and distance to waypoint - Great Circle
248+
utctime = msg[1] # hhmmss
249+
lat = msg[2] # Latitude in format ddmm.mmmm
250+
lathemi = msg[3] # Latitude hemisphere in N/S
251+
lon = msg[4] # Longitude in format dddmm.mmmm
252+
lonhemi = msg[5] # Longitude hemisphere in E/W
253+
brgtrue = msg[6] # bearing to next waypoint degrees true xx.x
254+
brgmag = msg[8] # bearing to next waypoint degrees magnetic xx.x
255+
distwpt = msg[10] # Distance to next waypoint in nautical miles xx.x
256+
nextwpt = msg[12] # Next waypoint ID XXXX
257+
mode = msg[13] # FAA Mode, explained at top of file
258+
aircraft.sys_time_string = "%d:%d:%d"%(int(utctime[0:1]),int(utctime[2:3]),int(utctime[4:5]))
259+
self.time_stamp_string = aircraft.sys_time_string
260+
self.time_stamp_min = int(utctime[2:3])
261+
self.time_stamp_sec = int(utctime[4:5])
262+
aircraft.gps.LatHemi = lathemi
263+
aircraft.gps.LatDeg = int(lat[0:1])
264+
aircraft.gps.LatMin = float(lat[2:8])
265+
aircraft.gps.LonHemi = lonhemi
266+
aircraft.gps.LonDeg = int(lon[0:2])
267+
aircraft.gps.LonMin = int(lon[3:9])
268+
aircraft.nav.WPName = nextwpt
269+
aircraft.nav.WPDist = float(distwpt)
270+
aircraft.nav.WPTrack = float(brgmag)
271+
case "GPVTG": # GPS Track made good and ground speed, Intentionally omitted KM/H Ground speed
272+
coursetrue = msg[1] # GPS Course over ground degrees true xxx.x
273+
coursemag = msg[3] # GPS Course over ground degrees magnetic xxx.x
274+
gs = msg[5] # GPS Groundspeed, knots xxx.x
275+
mode = msg[9] # FAA Mode, explained at top of file
276+
aircraft.groundspeed = float(gs)
277+
aircraft.gps.GndSpeed = float(gs)
278+
aircraft.gps.GndTrack = float(coursemag)
279+
case "GPXTE": # GPS Cross-track error, measured
280+
status1 = msg[1] # GPS Status, A = Valid, V = Invalid
281+
xtkerror = msg[3] # Cross track distance NM xx.x
282+
steerdir = msg[4] # Cross track direction to steer, L/R
283+
mode = msg[6] # FAA Mode, explained at top of file
284+
case "PGRMZ": # Garmin-proprietary altitude in feet. Pressure altitude, or GPS if unavailable
285+
alt = msg[1] # Altitude in feet
286+
alttype = msg[3] # 2 if Pressure altitude, 3 if GPS altitude
287+
return aircraft
288+
289+
except:
290+
print("NMEA serial exception")
291+
aircraft.errorFoundNeedToExit = True
292+
293+
return aircraft
294+
295+
296+
297+
298+
# vi: modeline tabstop=8 expandtab shiftwidth=4 softtabstop=4 syntax=python

0 commit comments

Comments
 (0)