From f7337a0c6c411f25520d0d2bd2c000ac9a235f73 Mon Sep 17 00:00:00 2001 From: Thomas Hooge Date: Fri, 18 Jul 2025 14:40:19 +0200 Subject: [PATCH] Weitere NMEA0183 Daten verarbeiten --- nmea0183.py | 115 +++++++++++++++++++++++++++++++++++++++++++++--- obp60v.py | 15 ++++--- pages/anchor.py | 26 ++++++----- 3 files changed, 135 insertions(+), 21 deletions(-) diff --git a/nmea0183.py b/nmea0183.py index 308fa3f..87bfcf2 100644 --- a/nmea0183.py +++ b/nmea0183.py @@ -13,6 +13,23 @@ def DBT(boatdata, msg): pass #boatdata.setValue("DBT", msg.depth) +def GGA(boatdata, msg): + # Time, position, and fix related data + # msg.num_sats + # msg.timestamp + if msg.gps_qual == 0: + # No fix + return + if msg.lat_dir == 'N': + boatdata.setValue("LAT", msg.latitude) + else: + boatdata.setValue("LAT", msg.latitude * -1) + if msg.lon_dir == 'E': + boatdata.setValue("LON", msg.longitude) + else: + boatdata.setValue("LON", msg.longitude * -1) + boatdata.setValue("HDOP", msg.horizontal_dil) + def GLL(boatdata, msg): print("-> GLL") boatdata.setValue("LAT", msg.latitude) @@ -22,11 +39,32 @@ def GSV(boatdata, msg): # Satellites in view print("-> GSV") +def HDG(boatdata, msg): + # UNUSED: Heading - Deviation & Variation + # Magnetic Sensor heading in degrees + # msg.heading + # .deviation, dev_dir E/W + # .variation, var_dir E/W + pass + +def HDM(boatdata, msg): + # Heading magnetic + if msg.magnetic == 'M': + boatdata.setValue("HDM", msg.heading) + else: + print("HDM: M not set!") + def HDT(boatdata, msg): # Heading True print("-> HDT") print(msg.fields) +def HTD(boatdata, msg): + # Heading/Track control data + # e.g. $YDHTD,V,1.5,,R,N,,,,,,,,,A,,,*48 + print("-> HTD") + print(msg.fields) + def MWV(boatdata, msg): # Windgeschwindigkeit und -winkel print(f"Wind: {msg.wind_angle}° {msg.wind_speed}kt") @@ -45,16 +83,37 @@ def RMB(boatdata, msg): print("-> RMB") def RMC(boatdata, msg): - print("-> RMC") - #boatdata.setValue("LAT", msg.lat) - #boatdata.setValue("LON", msg.lon) + # Recommended Minimum Navigation Information + # print("-> RMC") + # print(msg.timestamp, msg.datestamp) + # print(msg.status) # V=Warning, P=Precise, A=? + # mag_variation, mag_var_dir E/W + # true_course + if msg.lat_dir == 'N': + boatdata.setValue("LAT", msg.lat) + else: + boatdata.setValue("LAT", msg.lat) * -1 + if msg.lon_dir == 'E': + boatdata.setValue("LON", msg.lon) + else: + boatdata.setValue("LON", msg.lon) * -1 + boatdata.setValue("SOG", msg.spd_over_grnd) + +def ROT(boatdata, msg): + # Rate Of Turn + # print("-> ROT") + if msg.status == 'A': + boatdata.setValue("ROT", msg.rate_of_turn) def RSA(boatdata, msg): # Rudder Sensor Angle # negative Werte bedeuten Backbord + #print("-> RSA") # Boatdata: RPOS primär, PRPOS sekundär - print("-> RSA") - print(msg.fields) + if msg.rsa_starboard_status== 'A': + boatdata.setValue("RPOS", msg.rsa_starboard) + if msg.rsa_port_status == 'A': + boatdata.setValue("PRPOS", msg.rsa_port) def RTE(boatdata, msg): # Route @@ -65,9 +124,16 @@ def VBW(boatdata, msg): print("-> VBW") def VHW(boatdata, msg): + print("-> VHW") boatdata.setValue("STW", float(msg.water_speed_knots)) +def VPW(boatdata, msg): + # UNUSED: Speed - Measured Parallel to Wind + # print(f"-> VPW: {msg.speed_kn} kn") + pass + def VTG(boatdata, msg): + print("-> VTG") boatdata.setValue("COG", int(msg.true_track)) #TODO klären was für Typen hier ankommen können # bytearray, str, decimal.Decimal? @@ -75,10 +141,40 @@ def VTG(boatdata, msg): #str von OpenCPN: sog = float(msg.spd_over_grnd_kts[:-1]) boatdata.setValue("SOG", sog) +def VWR(boatdata, msg): + # Relative Wind Speed and Angle + #print("-> VWR") + if msg.l_r == "R": + angle = msg.deg_r + else: + angle = 360 - msg.deg_r + boatdata.setValue("AWA", angle) + boatdata.setValue("AWS", msg.wind_speed_ms) + def WPL(boatdata, msg): print("-> WPL") print(msg.fields) +def VWT(boatdata, msg): + # True Wind Speed and Angle + if msg.direction == "R": + angle = msg.wind_angle_vessel + else: + angle = 360 - msg.wind_angle_vessel + boatdata.setValue("TWA", angle) + boatdata.setValue("TWS", msg.wind_speed_meters) + +def XDR(boatdata, msg): + # Extra sensor data / Transducer Measurement + # type, value, units, id + # type: A + # units: D + # id: Yaw + if msg.id.lower() == 'yaw': + boatdata.setValue("YAW", msg.value) + else: + print(f"-> XDR: {msg.type}, {msg.value}, {msg.units}, {msg.id}") + def XTE(boatdata, msg): # Cross Track error measured print("-> XTE") @@ -98,18 +194,27 @@ def ZDA(boatdata, msg): decoder = { "DBS": DBS, "DBT": DBT, + "GGA": GGA, "GLL": GLL, "GSV": GSV, + "HDG": HDG, + "HDM": HDM, + "HTD": HTD, "MWV": MWV, "MTW": MTW, "RMB": RMB, + "ROT": ROT, "RMC": RMC, "RSA": RSA, "RTE": RTE, "VBW": VBW, "VHW": VHW, + "VPW": VPW, "VTG": VTG, + "VWR": VWR, + "VWT": VWT, "WPL": WPL, + "XDR": XDR, "XTE": XTE, "XTR": XTR, "ZDA": ZDA diff --git a/obp60v.py b/obp60v.py index a452c6a..dde49f0 100755 --- a/obp60v.py +++ b/obp60v.py @@ -210,7 +210,7 @@ def rxd_0183(devname): try: msg = pynmea2.parse(raw) except pynmea2.nmea.ParseError: - print(f"NMEA0183: Parse-Error: {raw}") + print(f"NMEA0183: Parse-Error: {raw}", end='') continue # sentence_type kann fehlen try: @@ -223,11 +223,16 @@ def rxd_0183(devname): # if stype in stypefilter: # continue if stype in nmea0183.decoder: - nmea0183.decoder[stype(boatdata, msg)] + nmea0183.decoder[stype](boatdata, msg) else: # Hier unbekannter Satztyp: protokollieren und ignorieren - print("Nicht implementiert") - print(msg) + """ + ['checksum', 'data', 'fields', 'identifier', 'name_to_idx', 'parse', + 'proprietary_re', 'query_re', 'render', 'sentence_re', + 'sentence_type', 'sentence_types', 'talker', 'talker_re'] + """ + print(f"Nicht implementiert: '{stype}' from {msg.talker}") + ser.close() def rxd_gps(devname, devspeed): @@ -608,7 +613,7 @@ def init_profile(config, cfg, boatdata): cls = getattr(pages, p['type']) except AttributeError: # Klasse nicht vorhanden, Seite wird nicht benutzt - print(f"Klasse '{type}' nicht gefunden") + print(f"Klasse '{p['type']}' nicht gefunden") continue c = cls(i, cfg, boatdata, *[v for v in p['values'].values()]) clist[i] = c diff --git a/pages/anchor.py b/pages/anchor.py index 838796f..5d9da59 100644 --- a/pages/anchor.py +++ b/pages/anchor.py @@ -77,9 +77,9 @@ class Anchor(Page): self.anchor_set = True else: self.anchor_set = False - if self.buttonid == 5: + if buttonid == 5: # Bei aktivem Alarm kann mit dieser Taste der Alarm zurückgesetzt - # werden + # werden. Die Tastenbeschriftung wechselt zwischen ALARM und OFF. if self.alarm: self.alarm = False self.buttonlabel[5] = 'ALARM' @@ -87,6 +87,7 @@ class Anchor(Page): self.alarm_enabled = False self.buttonlabel[5] = 'ALARM' else: + self.alarm_enabled = True self.buttonlabel[5] = 'OFF' return False @@ -114,7 +115,11 @@ class Anchor(Page): ctx.set_font_size(16) ctx.move_to(2, 70) - ctx.show_text("Alarm: off") + ctx.show_text("Alarm: ") + if self.alarm_enabled: + ctx.show_text("On") + else: + ctx.show_text("Off") ctx.move_to(320, 70) ctx.show_text(f"{self.chain} m") ctx.stroke() @@ -138,11 +143,10 @@ class Anchor(Page): # Windpfeil zeichnen - # TWD / TWS - # boatdata.value(TWD) - p = ((cx, cy - r + 25), (cx - 12, cy - r - 4), (cx, cy - r + 6), (cx + 12, cy - r - 4), (cx, cy - r + 25)) - wind = self.rotate((cx, cy), p, 62) - ctx.move_to(*wind[0]) - for point in wind[1:]: - ctx.line_to(*point) - ctx.fill() + if self._bd.awa.value: + p = ((cx, cy - r + 25), (cx - 12, cy - r - 4), (cx, cy - r + 6), (cx + 12, cy - r - 4), (cx, cy - r + 25)) + wind = self.rotate((cx, cy), p, self._bd.awa.value) + ctx.move_to(*wind[0]) + for point in wind[1:]: + ctx.line_to(*point) + ctx.fill()