From 664d6c7d49abfe4cd74f6b850530d9cad51eddbe Mon Sep 17 00:00:00 2001 From: Thomas Hooge Date: Sun, 20 Jul 2025 21:29:22 +0200 Subject: [PATCH] Ankerseite und mehr NMEA0183 code --- README | 5 +++- nmea0183.py | 69 ++++++++++++++++++++++++++++++++++++------------- pages/anchor.py | 58 +++++++++++++++++++++++++++++++++-------- 3 files changed, 102 insertions(+), 30 deletions(-) diff --git a/README b/README index c6f60f0..4afb9d8 100644 --- a/README +++ b/README @@ -31,9 +31,12 @@ Abhängigkeiten - python3-heapdict - python3-setproctitle -Für GPS +Für GPS und/oder NMEA0183 - python3-serial - python3-nmea2 + Achtung: in Debian ist Version 1.15, die aktuelle Version 1.19 hat + weitere Satzarten implementiert. Allerdings ist dort auch kein AIS + vorhanden. Für BME280 - python3-smbus2 diff --git a/nmea0183.py b/nmea0183.py index 3e78744..0ca64e9 100644 --- a/nmea0183.py +++ b/nmea0183.py @@ -13,6 +13,13 @@ def DBT(boatdata, msg): pass #boatdata.setValue("DBT", msg.depth) +def DPT(boatdata, msg): + # Depth + print("-> DPT") + print(msg.fields) + print(msg.data) + #boatdata.setValue("DBT", msg.depth) + def GBS(boatdata, msg): # GNSS satellite fault detection """ @@ -42,9 +49,20 @@ def GGA(boatdata, msg): boatdata.setValue("HDOP", msg.horizontal_dil) def GLL(boatdata, msg): - print("-> GLL") - boatdata.setValue("LAT", msg.latitude) - boatdata.setValue("LON", msg.longitude) + # Position data: position fix, time of position fix, and status + # UTC of position ignored + print(msg.data) + if not msg.status == 'A': + return + lat_fac = 1 if msg.lat_dir == 'N' else -1 + lat_deg = int(msg.lat[0:2]) + lat_min = float(msg.lat[2:]) + print(lat_deg, lat_min) + boatdata.setValue("LAT", lat_fac * lat_deg + lat_min / 60) + lon_fac = 1 if msg.lon_dir == 'E' else -1 + lon_deg = int(msg.lon[0:3]) + lon_min = float(msg.lon[3:]) + boatdata.setValue("LON", lon_fac * lon_deg + lon_min / 60) def GSA(boatdata, msg): # Satellites @@ -113,8 +131,10 @@ def HDM(boatdata, msg): def HDT(boatdata, msg): # Heading True - print("-> HDT") - print(msg.fields) + if msg.hdg_true == 'T': + boatdata.setValue("HDT", msg.heading) + else: + print("HDT: T not set!") def HTD(boatdata, msg): # Heading/Track control data @@ -141,20 +161,25 @@ def RMB(boatdata, msg): def RMC(boatdata, msg): # Recommended Minimum Navigation Information - # print("-> RMC") + #print("-> RMC") # print(msg.timestamp, msg.datestamp) - # print(msg.status) # V=Warning, P=Precise, A=? + # print(msg.status) # V=Warning, P=Precise, A=OK # 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) + # TODO nav_status welche Bedeutung? + if not msg.status == 'A': + return + lat_fac = 1 if msg.lat_dir == 'N' else -1 + lat_deg = int(msg.lat[0:2]) + lat_min = float(msg.lat[2:]) + boatdata.setValue("LAT", lat_fac * lat_deg + lat_min / 60) + lon_fac = 1 if msg.lon_dir == 'E' else -1 + lon_deg = int(msg.lon[0:3]) + lon_min = float(msg.lon[3:]) + boatdata.setValue("LON", lon_fac * lon_deg + lon_min / 60) + if msg.spd_over_grnd: + boatdata.setValue("SOG", float(msg.spd_over_grnd)) + if msg.true_course: + boatdata.setValue("COG", float(msg.true_course)) def ROT(boatdata, msg): # Rate Of Turn @@ -242,7 +267,13 @@ def XDR(boatdata, msg): # units: D # id: Yaw if msg.id.lower() == 'yaw': - boatdata.setValue("YAW", msg.value) + boatdata.setValue("YAW", float(msg.value)) + elif msg.id.lower() == 'ptch': + boatdata.setValue("PTCH", float(msg.value)) + elif msg.id.lower() == 'roll': + boatdata.setValue("ROLL", float(msg.value)) + elif msg.id.lower() == 'barometer': + boatdata.setValue("xdrPress", float(msg.value)) else: print(f"-> XDR: {msg.type}, {msg.value}, {msg.units}, {msg.id}") @@ -275,6 +306,7 @@ def VDO(boatdata, msg): decoder = { "DBS": DBS, "DBT": DBT, + "DPT": DPT, "GBS": GBS, "GGA": GGA, "GLL": GLL, @@ -282,6 +314,7 @@ decoder = { "GSV": GSV, "HDG": HDG, "HDM": HDM, + "HDT": HDT, "HTD": HTD, "MWV": MWV, "MTW": MTW, diff --git a/pages/anchor.py b/pages/anchor.py index d3d04e8..c85919c 100644 --- a/pages/anchor.py +++ b/pages/anchor.py @@ -14,6 +14,7 @@ Daten - Alarm aktiv J/N - Alarmradius - GPS Abweichung/ Fehler in der Horizontalen + - Zeitpunkt des Ankeraus Darstellung: - Modi: @@ -29,6 +30,7 @@ Es gibt verschiedene Unterseiten import os import cairo import math +import time from .page import Page class Anchor(Page): @@ -41,6 +43,7 @@ class Anchor(Page): self.buttonlabel[5] = '' # ALARM erst möglich wenn der Anker unten ist self.mode = 'N' # (N)ormal, (C)onfiguration + self.scale = 50 # Radius of display circle in meter self._bd = boatdata @@ -54,6 +57,7 @@ class Anchor(Page): self.anchor_lat = 0 self.anchor_lon = 0 self.anchor_depth = -1 + self.anchor_ts = None # Timestamp of dropped anchor self.lat = 0 self.lon = 0 self.heading = -1 @@ -75,13 +79,15 @@ class Anchor(Page): self.anchor_lat = self._bd.lat self.anchor_lon = self._bd.lon self.anchor_set = True + self.anchor_ts = time.time() self.buttonlabel[2] = 'RISE' self.buttonlabel[5] = 'ALARM' else: self.anchor_set = False self.alarm = False self.alarm_enabled = False - self.buttonlabel[2] = 'SET' + self.anchor_ts = None + self.buttonlabel[2] = 'DROP' self.buttonlabel[5] = '' if buttonid == 5: # Bei aktivem Alarm kann mit dieser Taste der Alarm zurückgesetzt @@ -142,27 +148,57 @@ class Anchor(Page): cy = 150 r = 125 - ctx.set_line_width(1.5) + # Skala + ctx.set_line_width(1) ctx.arc(cx, cy, r, 0, 2*math.pi) + ctx.move_to(cx + 10, cy + 0.5) + ctx.line_to(cx + r - 4, cy + 0.5) + ctx.move_to(cx + r / 2, cy + 20) + # Pfeil links + ctx.move_to(cx + 10, cy + 0.5) + ctx.line_to(cx + 16, cy - 4 + 0.5) + ctx.move_to(cx + 10, cy + 0.5) + ctx.line_to(cx + 16, cy + 4 + 0.5) + # Pfeil rechts + ctx.move_to(cx + r - 4, cy + 0.5) + ctx.line_to(cx + r - 10, cy - 4 + 0.5) + ctx.move_to(cx + r - 4, cy + 0.5) + ctx.line_to(cx + r - 10, cy + 4 + 0.5) + + ctx.set_font_size(16) + self.draw_text_center(ctx, cx + r / 2, cy + 8, str(self.scale) + " m") ctx.stroke() - # Ankersymbol falls Anker fallen gelassen wurde, ansonsten nur Kreis- - # mittelpunkt kennzeichnen + ctx.set_line_width(1.5) + # Ankersymbol falls Anker fallen gelassen wurde + # Ansonsten ist das Boot-Symbol im Mittelpunkt if self.anchor_set: ctx.save() ctx.set_source_surface(self.sym_anchor, cx-8, cy-8) ctx.paint() ctx.restore() - else: - ctx.move_to(cx-6, cy-6) - ctx.line_to(cx+6, cy+6) - ctx.move_to(cx+6, cy-6) - ctx.line_to(cx-6, cy+6) - ctx.stroke() # Boot zeichnen # Heading beachten - + # Wir arbeiten mit einer Kopie, weil bx/by im Laufe der Zeit von + # cx/cy abweichen werden + if self.anchor_set: + bx = cx + by = cy + 30 + else: + bx = cx + by = cy + 8 + p = ((bx - 5, by), (bx - 5, by - 10), (bx, by - 16), (bx + 5, by - 10), (bx + 5, by), (bx - 6, by)) + if self._bd.hdt.value: + # Rotiere Boot-Symbol um eigenes Zentrum + boat = self.rotate((bx, by - 8), p, self._bd.hdt.value) + else: + # Kein Heading, Boot immer zeichnen + boat = p + ctx.move_to(*boat[0]) + for point in boat[1:]: + ctx.line_to(*point) + ctx.stroke() # Windpfeil zeichnen if self._bd.awa.value: