From f15dd098e3d6bbeefd24b0f0fce1993251068c02 Mon Sep 17 00:00:00 2001 From: Teemu Ikonen Date: Thu, 17 Nov 2022 21:04:47 +0200 Subject: [PATCH 01/50] More GLib.SOURCE_REMOVE usage in return values --- satellite/application.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/satellite/application.py b/satellite/application.py index 5888ff4..f863118 100755 --- a/satellite/application.py +++ b/satellite/application.py @@ -192,7 +192,7 @@ class SatelliteApp(Gtk.Application): if not source_init: self.log_msg('No NmeaSource initialized') - return False # Remove from idle_add + return GLib.SOURCE_REMOVE self.log_msg( f"Source is {self.source.manufacturer}, model {self.source.model}" From ffd3f29ae308faf370d325fa6f2d0699fd996cc8 Mon Sep 17 00:00:00 2001 From: Teemu Ikonen Date: Thu, 17 Nov 2022 21:05:31 +0200 Subject: [PATCH 02/50] Log messages to the app window before initializing Use GLib.timeout_add instead of idle_add to have the window drawn and messages logged before starting source initialization. --- satellite/application.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/satellite/application.py b/satellite/application.py index f863118..d76493a 100755 --- a/satellite/application.py +++ b/satellite/application.py @@ -161,8 +161,6 @@ class SatelliteApp(Gtk.Application): def on_startup(self, app): self.create_actions() - # Initialize modem after GUI startup - GLib.idle_add(self.init_source) def on_activate(self, app): self.setup_styles() @@ -171,6 +169,11 @@ class SatelliteApp(Gtk.Application): if have_touchscreen(): self.datascroll.connect('edge-overshot', self.on_edge_overshot) + self.log_msg(f"{appname} version {__version__} started") + self.log_msg(f'Trying to initialize source "{self.args.source}"') + # Initialize modem after GUI startup + GLib.timeout_add(1000, self.init_source, None) + def on_shutdown(self, app): """Called after main loop exits.""" print("Cleaning up...") @@ -179,12 +182,9 @@ class SatelliteApp(Gtk.Application): self.source.close() print("...done.") - def init_source(self): - self.log_msg(f"{appname} version {__version__} started") + def init_source(self, unused): source_init = False - self.log_msg(f'Trying to initialize source "{self.args.source}"') - if self.args.source == 'quectel': source_init = self.init_quectel_source() elif self.args.source == 'gnss-share': From bef48e40560405c00586fa67b4d8b42cc02fc11f Mon Sep 17 00:00:00 2001 From: Teemu Ikonen Date: Fri, 18 Nov 2022 14:07:41 +0200 Subject: [PATCH 03/50] Update display also when new NMEAs are not received --- satellite/application.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/satellite/application.py b/satellite/application.py index d76493a..99db4bd 100755 --- a/satellite/application.py +++ b/satellite/application.py @@ -573,16 +573,19 @@ class SatelliteApp(Gtk.Application): self.log_msg(dtext) return - if not nmeas: + if not nmeas and self.last_data is None: return - if self.source_lost: - self.log_msg("Modem appeared") - self.main_box.set_sensitive(True) + if nmeas: + if self.source_lost: + self.log_msg("Modem appeared") + self.main_box.set_sensitive(True) - self.source_lost = False + self.source_lost = False + data = nmea.parse(nmeas) + else: + data = self.last_data - data = nmea.parse(nmeas) data["updateage"] = ((time.time() - self.last_update) if self.last_update else None) From 93cb27fb27aee6d0491380c8b2637860ab95b2ac Mon Sep 17 00:00:00 2001 From: Teemu Ikonen Date: Fri, 18 Nov 2022 14:21:32 +0200 Subject: [PATCH 04/50] Simplify error handling in update() --- satellite/application.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/satellite/application.py b/satellite/application.py index 99db4bd..0b0933c 100755 --- a/satellite/application.py +++ b/satellite/application.py @@ -540,7 +540,6 @@ class SatelliteApp(Gtk.Application): try: nmeas = self.source.get() except Exception as e: - fatal = False show_dialog = False etext = str(e) dtext = None @@ -563,15 +562,12 @@ class SatelliteApp(Gtk.Application): parent=self.window, modal=True, message_type=Gtk.MessageType.ERROR, buttons=Gtk.ButtonsType.OK, text=dtext) - dialog.set_title("Unrecoverable error" if fatal else "Error") + dialog.set_title("Error") dialog.run() dialog.destroy() - if fatal: - self.quit() - return elif dtext is not None: self.log_msg(dtext) - return + nmeas = None if not nmeas and self.last_data is None: return From 0861c78c7656696ca8e2ebdfb66ee838fea2a53e Mon Sep 17 00:00:00 2001 From: Teemu Ikonen Date: Fri, 18 Nov 2022 15:43:01 +0200 Subject: [PATCH 05/50] Yet Another Error Handling Refactor --- satellite/application.py | 53 ++++++++++++++++++++-------------------- 1 file changed, 26 insertions(+), 27 deletions(-) diff --git a/satellite/application.py b/satellite/application.py index 0b0933c..8fc1b50 100755 --- a/satellite/application.py +++ b/satellite/application.py @@ -117,7 +117,7 @@ class SatelliteApp(Gtk.Application): self.last_data = None self.last_speed = None self.last_update = None - self.source_lost = False + self.had_error = False self.sigint_received = False self.refresh_rate = 1 # Really delay between updates in seconds @@ -539,7 +539,14 @@ class SatelliteApp(Gtk.Application): def update(self): try: nmeas = self.source.get() + if self.had_error: + self.log_msg("Getting updates") + self.main_box.set_sensitive(True) + + self.had_error = False + data = nmea.parse(nmeas) except Exception as e: + nmeas = None show_dialog = False etext = str(e) dtext = None @@ -551,36 +558,28 @@ class SatelliteApp(Gtk.Application): elif isinstance(e, ModemError): dtext = "Modem error: " + str(e) elif isinstance(e, NmeaSourceNotFoundError): - if not self.source_lost: + if not self.had_error: dtext = etext if etext else "Modem disappeared" - self.source_lost = True + self.had_error = True self.main_box.set_sensitive(False) else: dtext = etext if etext else "Unknown error" - if show_dialog: - dialog = Gtk.MessageDialog( - parent=self.window, modal=True, - message_type=Gtk.MessageType.ERROR, - buttons=Gtk.ButtonsType.OK, text=dtext) - dialog.set_title("Error") - dialog.run() - dialog.destroy() - elif dtext is not None: - self.log_msg(dtext) - nmeas = None - - if not nmeas and self.last_data is None: - return - - if nmeas: - if self.source_lost: - self.log_msg("Modem appeared") - self.main_box.set_sensitive(True) - - self.source_lost = False - data = nmea.parse(nmeas) - else: - data = self.last_data + if not self.had_error: + if show_dialog: + dialog = Gtk.MessageDialog( + parent=self.window, modal=True, + message_type=Gtk.MessageType.ERROR, + buttons=Gtk.ButtonsType.OK, text=dtext) + dialog.set_title("Error") + dialog.run() + dialog.destroy() + elif dtext is not None: + self.log_msg(dtext) + self.had_error = True + if self.last_data is None: + return + else: + data = self.last_data data["updateage"] = ((time.time() - self.last_update) if self.last_update else None) From 760f80fb49f103e6d940ae75f547b47e61c28dc0 Mon Sep 17 00:00:00 2001 From: Teemu Ikonen Date: Mon, 16 Jan 2023 16:12:11 +0200 Subject: [PATCH 06/50] Split PyDBus based ModemManager source to its own file --- satellite/application.py | 2 +- satellite/mm_pydbus_source.py | 128 ++++++++++++++++++++++++++++++++++ satellite/nmeasource.py | 116 ------------------------------ 3 files changed, 129 insertions(+), 117 deletions(-) create mode 100644 satellite/mm_pydbus_source.py diff --git a/satellite/application.py b/satellite/application.py index 8fc1b50..0404f54 100755 --- a/satellite/application.py +++ b/satellite/application.py @@ -16,12 +16,12 @@ import importlib.resources as resources import satellite.nmea as nmea import satellite.quectel as quectel +from .mm_pydbus_source import QuectelNmeaSource from .nmeasource import ( ModemNoNMEAError, ModemLockedError, ModemError, NmeaSourceNotFoundError, - QuectelNmeaSource, GnssShareNmeaSource, ) from .util import bearing_to_arrow, have_touchscreen, now, unique_filename diff --git a/satellite/mm_pydbus_source.py b/satellite/mm_pydbus_source.py new file mode 100644 index 0000000..aa2fea1 --- /dev/null +++ b/satellite/mm_pydbus_source.py @@ -0,0 +1,128 @@ +# Copyright 2021-2023 Teemu Ikonen +# SPDX-License-Identifier: GPL-3.0-only + +import re +import satellite.modem_manager_defs as mm +from satellite.nmeasource import ( + NmeaSource, + NmeaSourceNotFoundError, + ModemError, + ModemLockedError, + ModemNoNMEAError, +) + +from pydbus import SystemBus +from pynmea2.nmea import NMEASentence + + +class ModemManagerPyDBusNmeaSource(NmeaSource): + + def __init__(self, update_callback, **kwargs): + super().__init__(update_callback, **kwargs) + self.bus = SystemBus() + self.manager = self.bus.get('.ModemManager1') + self.modem = None + self.old_refresh_rate = None + self.old_sources_enabled = None + self.old_signals = None + self.location_updated = None + + def initialize(self): + objs = self.manager.GetManagedObjects() + mkeys = list(objs.keys()) + if mkeys: + mstr = mkeys[0] + else: + raise NmeaSourceNotFoundError("No Modems Found") + info = objs[mstr]['org.freedesktop.ModemManager1.Modem'] + self.manufacturer = info.get('Manufacturer') + self.model = info.get('Model') + self.revision = info.get('Revision') + self.modem = self.bus.get('.ModemManager1', mstr) + + try: + if self.modem.State > 0: + if self.old_refresh_rate is None: + self.old_refresh_rate = self.modem.GpsRefreshRate + if self.old_sources_enabled is None: + self.old_sources_enabled = self.modem.Enabled + if self.old_signals is None: + self.old_signals = self.modem.SignalsLocation + cap = self.modem.Capabilities + if (cap & mm.MM_MODEM_LOCATION_SOURCE_GPS_NMEA) == 0: + raise NmeaSourceNotFoundError( + "Modem does not support NMEA") + self.modem.Setup( + (mm.MM_MODEM_LOCATION_SOURCE_GPS_NMEA + | (cap & mm.MM_MODEM_LOCATION_SOURCE_AGPS_MSB)), + True) + else: + raise ModemError("Modem state is: %d" % self.modem.State) + except AttributeError as e: + if self.modem.State == mm.MM_MODEM_STATE_LOCKED: + raise ModemLockedError from e + else: + raise ModemError from e + except Exception as e: + raise e + + self.modem.SetGpsRefreshRate(self.refresh_rate) + self.location_updated = self.bus.subscribe( + sender='org.freedesktop.ModemManager1', + iface='org.freedesktop.DBus.Properties', + signal='PropertiesChanged', + arg0='org.freedesktop.ModemManager1.Modem.Location', + signal_fired=self.update_callback) + self.initialized = True + + def _really_get(self): + if not self.initialized: + self.initialize() + try: + loc = self.modem.GetLocation() + except Exception as e: + self.initialized = False + raise e + + retval = loc.get(mm.MM_MODEM_LOCATION_SOURCE_GPS_NMEA) + if retval is None: + self.initialized = False + raise ModemNoNMEAError + return retval + + def close(self): + if self.location_updated is not None: + self.location_updated.disconnect() + if self.old_sources_enabled is not None: + self.modem.Setup(self.old_sources_enabled, self.old_signals) + if self.old_refresh_rate is not None: + self.modem.SetGpsRefreshRate(self.old_refresh_rate) + + +class QuectelNmeaSource(ModemManagerPyDBusNmeaSource): + + def _really_get(self): + return self.fix_talker(super()._really_get()) + + def fix_talker(self, nmeas): + pq_re = re.compile(r''' + ^\s*\$? + (?PPQ) + (?P\w{3}) + (?P[^*]*) + (?:[*](?P[A-F0-9]{2}))$''', re.VERBOSE) + out = [] + for nmea in (n for n in nmeas.split('\r\n') if n): + mo = pq_re.match(nmea) + if mo: + # The last extra data field is Signal ID, these are + # 1 = GPS, 2 = Glonass, 3 = Galileo, 4 = BeiDou, 5 = QZSS + # Determine talker from Signal ID + talker = 'QZ' if mo.group('data').endswith('5') else 'BD' + # Fake talker and checksum + fake = talker + "".join(mo.group(2, 3)) + out.append('$' + fake + "*%02X" % NMEASentence.checksum(fake)) + else: + out.append(nmea) + + return "\r\n".join(out) diff --git a/satellite/nmeasource.py b/satellite/nmeasource.py index 835d682..e685e76 100644 --- a/satellite/nmeasource.py +++ b/satellite/nmeasource.py @@ -1,12 +1,8 @@ # Copyright 2021-2022 Teemu Ikonen # SPDX-License-Identifier: GPL-3.0-only -import re -import satellite.modem_manager_defs as mm import os.path import socket -from pydbus import SystemBus -from pynmea2.nmea import NMEASentence from gi.repository import GLib @@ -115,118 +111,6 @@ class GnssShareNmeaSource(UnixSocketNmeaSource): **kwargs) -class ModemManagerNmeaSource(NmeaSource): - def __init__(self, update_callback, **kwargs): - super().__init__(update_callback, **kwargs) - self.bus = SystemBus() - self.manager = self.bus.get('.ModemManager1') - self.modem = None - self.old_refresh_rate = None - self.old_sources_enabled = None - self.old_signals = None - self.location_updated = None - - def initialize(self): - objs = self.manager.GetManagedObjects() - mkeys = list(objs.keys()) - if mkeys: - mstr = mkeys[0] - else: - raise NmeaSourceNotFoundError("No Modems Found") - info = objs[mstr]['org.freedesktop.ModemManager1.Modem'] - self.manufacturer = info.get('Manufacturer') - self.model = info.get('Model') - self.revision = info.get('Revision') - self.modem = self.bus.get('.ModemManager1', mstr) - - try: - if self.modem.State > 0: - if self.old_refresh_rate is None: - self.old_refresh_rate = self.modem.GpsRefreshRate - if self.old_sources_enabled is None: - self.old_sources_enabled = self.modem.Enabled - if self.old_signals is None: - self.old_signals = self.modem.SignalsLocation - cap = self.modem.Capabilities - if (cap & mm.MM_MODEM_LOCATION_SOURCE_GPS_NMEA) == 0: - raise NmeaSourceNotFoundError( - "Modem does not support NMEA") - self.modem.Setup( - (mm.MM_MODEM_LOCATION_SOURCE_GPS_NMEA - | (cap & mm.MM_MODEM_LOCATION_SOURCE_AGPS_MSB)), - True) - else: - raise ModemError("Modem state is: %d" % self.modem.State) - except AttributeError as e: - if self.modem.State == mm.MM_MODEM_STATE_LOCKED: - raise ModemLockedError from e - else: - raise ModemError from e - except Exception as e: - raise e - - self.modem.SetGpsRefreshRate(self.refresh_rate) - self.location_updated = self.bus.subscribe( - sender='org.freedesktop.ModemManager1', - iface='org.freedesktop.DBus.Properties', - signal='PropertiesChanged', - arg0='org.freedesktop.ModemManager1.Modem.Location', - signal_fired=self.update_callback) - self.initialized = True - - def _really_get(self): - if not self.initialized: - self.initialize() - try: - loc = self.modem.GetLocation() - except Exception as e: - self.initialized = False - raise e - - retval = loc.get(mm.MM_MODEM_LOCATION_SOURCE_GPS_NMEA) - if retval is None: - self.initialized = False - raise ModemNoNMEAError - return retval - - def close(self): - if self.location_updated is not None: - self.location_updated.disconnect() - if self.old_sources_enabled is not None: - self.modem.Setup(self.old_sources_enabled, self.old_signals) - if self.old_refresh_rate is not None: - self.modem.SetGpsRefreshRate(self.old_refresh_rate) - - -class QuectelNmeaSource(ModemManagerNmeaSource): - - def _really_get(self): - return self.fix_talker(super()._really_get()) - - def fix_talker(self, nmeas): - pq_re = re.compile(r''' - ^\s*\$? - (?PPQ) - (?P\w{3}) - (?P[^*]*) - (?:[*](?P[A-F0-9]{2}))$''', re.VERBOSE) - out = [] - for nmea in (n for n in nmeas.split('\r\n') if n): - mo = pq_re.match(nmea) - if mo: - # The last extra data field is Signal ID, these are - # 1 = GPS, 2 = Glonass, 3 = Galileo, 4 = BeiDou, 5 = QZSS - # Determine talker from Signal ID - talker = 'QZ' if mo.group('data').endswith('5') else 'BD' - # Fake talker and checksum - fake = talker + "".join(mo.group(2, 3)) - out.append('$' + fake + "*%02X" % NMEASentence.checksum(fake)) - else: - out.append(nmea) - - return "\r\n".join(out) - - class ReplayNmeaSource(NmeaSource): def __init__(self, update_callback, replay_filename=None, refresh_rate=1, **kwargs): From 3ebf88d0f773681be640a33404fa994efa1b2ac9 Mon Sep 17 00:00:00 2001 From: Teemu Ikonen Date: Thu, 19 Jan 2023 14:03:11 +0200 Subject: [PATCH 07/50] Linter config improvements, code style fixes --- satellite/application.py | 45 ++++++++++++++++------------------- satellite/mm_pydbus_source.py | 21 ++++++++-------- satellite/nmea.py | 11 +++++---- satellite/quectel.py | 1 - satellite/util.py | 15 +++++------- satellite/widgets.py | 12 ++++------ setup.cfg | 5 ++++ 7 files changed, 54 insertions(+), 56 deletions(-) diff --git a/satellite/application.py b/satellite/application.py index 0404f54..908aadd 100755 --- a/satellite/application.py +++ b/satellite/application.py @@ -2,8 +2,7 @@ # SPDX-License-Identifier: GPL-3.0-only import argparse -import gi -import gpxpy +import importlib.resources as resources import os import re import signal @@ -12,26 +11,28 @@ import time import tokenize from datetime import datetime -import importlib.resources as resources +import gi +import gpxpy import satellite.nmea as nmea import satellite.quectel as quectel +from satellite import __version__ + from .mm_pydbus_source import QuectelNmeaSource from .nmeasource import ( - ModemNoNMEAError, - ModemLockedError, - ModemError, - NmeaSourceNotFoundError, GnssShareNmeaSource, + ModemError, + ModemLockedError, + ModemNoNMEAError, + NmeaSourceNotFoundError, ) from .util import bearing_to_arrow, have_touchscreen, now, unique_filename -from .widgets import text_barchart, DataFrame -from satellite import __version__ +from .widgets import DataFrame, text_barchart gi.require_version('Gtk', '3.0') gi.require_version('Gdk', '3.0') gi.require_version('Handy', '1') -from gi.repository import Gdk, Gio, GLib, Gtk, Handy # noqa: E402 +from gi.repository import GLib, Gdk, Gio, Gtk, Handy # noqa: E402, I100 appname = 'Satellite' app_id = 'page.codeberg.tpikonen.satellite' @@ -54,9 +55,9 @@ class SatelliteApp(Gtk.Application): parser.add_argument( '-s', '--source', dest='source', default='quectel', - help='Select NMEA source. Options are ' - '\'quectel\' (default) for Quectel Modems or ' - '\'gnss-share\' when using gnss-share') + help="Select NMEA source. Options are " + "\'quectel\' (default) for Quectel Modems or " + "\'gnss-share\' when using gnss-share") self.args = parser.parse_args() GLib.unix_signal_add(GLib.PRIORITY_DEFAULT, signal.SIGINT, @@ -90,7 +91,7 @@ class SatelliteApp(Gtk.Application): self.source = None - self.infolabel.set_markup("" + "\n"*10 + "") + self.infolabel.set_markup("" + "\n" * 10 + "") self.dataframe = DataFrame() # self.dataframe.header.set_text("Satellite info") @@ -175,7 +176,6 @@ class SatelliteApp(Gtk.Application): GLib.timeout_add(1000, self.init_source, None) def on_shutdown(self, app): - """Called after main loop exits.""" print("Cleaning up...") self.gpx_write() if self.source is not None: @@ -418,16 +418,14 @@ class SatelliteApp(Gtk.Application): ("mode_indicator", "Modes (GP,GL,GA)", lambda x: str(x)), ("actives", "Active / in use sats", get_actives), ("visibles", "Receiving sats", lambda x: str(len( - list(r for r in x if r['snr'] > 0.0)))), + [r for r in x if r['snr'] > 0.0]))), ("visibles", "Visible sats", lambda x: str(len(x))), # ("fixage", "Age of fix", lambda x: to_str(x, "%0.0f s")), ("fixage", "Age of update / fix", get_ages), ("systime", "Sys. Time", lambda x: x.strftime(utcfmt)), - ("latlon", "Latitude", - lambda x: "%0.6f" % x[0] if x else "-"), - ("latlon", "Longitude", - lambda x: "%0.6f" % x[1] if x else "-"), - ("altitude", "Altitude", lambda x: to_str(x, "%0.1f m")), + ("latlon", "Latitude", lambda x: "%0.6f" % x[0] if x else "-"), + ("latlon", "Longitude", lambda x: "%0.6f" % x[1] if x else "-"), + ("altitude", "Altitude", lambda x: to_str(x, "%0.1f m")), # ("fixtime", "Time of fix", # lambda x: x.strftime(utcfmt) if x else "-"), # ("date", "Date of fix", @@ -471,10 +469,9 @@ class SatelliteApp(Gtk.Application): self.last_mode = mode def set_speedlabel(self, speed, bearing=None): - spd = str(int(3.6*speed)) if speed else "-" + spd = str(int(3.6 * speed)) if speed else "-" arrow = bearing_to_arrow(bearing) if bearing is not None else "" - speedfmt = ('%s%s\n' + - '%s') + speedfmt = '%s%s\n%s' speedstr = speedfmt % (spd, arrow, "km/h") self.speedlabel.set_markup(speedstr) diff --git a/satellite/mm_pydbus_source.py b/satellite/mm_pydbus_source.py index aa2fea1..a946a92 100644 --- a/satellite/mm_pydbus_source.py +++ b/satellite/mm_pydbus_source.py @@ -2,18 +2,19 @@ # SPDX-License-Identifier: GPL-3.0-only import re -import satellite.modem_manager_defs as mm -from satellite.nmeasource import ( - NmeaSource, - NmeaSourceNotFoundError, - ModemError, - ModemLockedError, - ModemNoNMEAError, -) from pydbus import SystemBus from pynmea2.nmea import NMEASentence +import satellite.modem_manager_defs as mm +from satellite.nmeasource import ( + ModemError, + ModemLockedError, + ModemNoNMEAError, + NmeaSource, + NmeaSourceNotFoundError, +) + class ModemManagerPyDBusNmeaSource(NmeaSource): @@ -105,12 +106,12 @@ class QuectelNmeaSource(ModemManagerPyDBusNmeaSource): return self.fix_talker(super()._really_get()) def fix_talker(self, nmeas): - pq_re = re.compile(r''' + pq_re = re.compile(r""" ^\s*\$? (?PPQ) (?P\w{3}) (?P[^*]*) - (?:[*](?P[A-F0-9]{2}))$''', re.VERBOSE) + (?:[*](?P[A-F0-9]{2}))$""", re.VERBOSE) out = [] for nmea in (n for n in nmeas.split('\r\n') if n): mo = pq_re.match(nmea) diff --git a/satellite/nmea.py b/satellite/nmea.py index 650dce0..9d13847 100644 --- a/satellite/nmea.py +++ b/satellite/nmea.py @@ -2,9 +2,10 @@ # SPDX-License-Identifier: GPL-3.0-only import datetime -import pynmea2 import re +import pynmea2 + MS_PER_KNOT = 0.514444 lastfix_dt = None @@ -61,9 +62,9 @@ def get_latlon(mdict): lat_min = float(lat[2:]) lon_deg = float(lon[:3]) lon_min = float(lon[3:]) - flat = lat_deg + lat_min/60 + flat = lat_deg + lat_min / 60 flat = -1 * flat if lat_dir == 'S' else flat - flon = lon_deg + lon_min/60 + flon = lon_deg + lon_min / 60 flon = -1 * flon if lon_dir == 'W' else flon return (flat, flon) @@ -182,7 +183,7 @@ def parse(nmeas, always_add_prefix=False): return float(s) if s else empty_val def add_prn_prefix(prns, talker, always=always_add_prefix): - """Add constellation prefix to PRN string""" + """Add constellation prefix to PRN string.""" beidou_prefix = "C" galileo_prefix = "E" glonass_prefix = "R" @@ -227,7 +228,7 @@ def parse(nmeas, always_add_prefix=False): 'snr': fl(getattr(msg, f'snr_{n}', None), 0.0), }) elif isinstance(msg, pynmea2.types.GSA): - for n in range(1, 12+1): + for n in range(1, 12 + 1): prns = getattr(msg, f'sv_id{n:02d}') if prns and prns.isdigit(): actives.append(add_prn_prefix(prns, msg.talker)) diff --git a/satellite/quectel.py b/satellite/quectel.py index 79047f8..e009832 100644 --- a/satellite/quectel.py +++ b/satellite/quectel.py @@ -2,7 +2,6 @@ # SPDX-License-Identifier: GPL-3.0-only import re - from datetime import datetime, timezone from .util import ( diff --git a/satellite/util.py b/satellite/util.py index 5f8b7e8..02881fd 100644 --- a/satellite/util.py +++ b/satellite/util.py @@ -1,11 +1,10 @@ # Copyright 2021-2022 Teemu Ikonen # SPDX-License-Identifier: GPL-3.0-only -import gi import os - from datetime import datetime, timezone +import gi gi.require_version('Gdk', '3.0') from gi.repository import Gdk # noqa: E402 @@ -16,15 +15,13 @@ week_now = int((now.timestamp() - gps_epoch.timestamp()) / one_week) def have_touchscreen(): - """Return True if the default seat of default display has touch capability - """ + """Return True if the default seat of default display has touch capability.""" return bool(Gdk.Display.get_default_seat( Gdk.Display.get_default()).get_capabilities() & Gdk.SeatCapabilities.TOUCH) def datetime_from_gpstime(week, millisecs, fix_week=False): - """Return a datetime object formed from GPS week number and - milliseconds from week start. + """Return a datetime from GPS week number and milliseconds from week start. If fix_week is True, set the bits above 10 in week number from current date, see @@ -38,7 +35,7 @@ def datetime_from_gpstime(week, millisecs, fix_week=False): def gpstime_from_datetime(dt): - """Return a (gps_week, millisec) tuple from a datetime object""" + """Return a (gps_week, millisec) tuple from a datetime object.""" if dt < gps_epoch: raise ValueError("Time cannot be less than GPS epoch") ts = dt.timestamp() @@ -51,7 +48,7 @@ def gpstime_from_datetime(dt): def unique_filename(namestem, ext, timestamp=False): if timestamp: namestem += "-" + datetime.now().isoformat( - '_', 'seconds').replace(':', '.') + '_', 'seconds').replace(':', '.') name = None for count in ('~%d' % n if n > 0 else '' for n in range(100)): test = namestem + count + ext @@ -77,7 +74,7 @@ def bearing_to_arrow(bearing): '\u2196', '\u2191', ] - edges = list(22.5 + 45.0 * n for n in range(0, 8)) + [360.0] + edges = [22.5 + 45.0 * n for n in range(0, 8)] + [360.0] angle = bearing - (bearing // 360) * 360 index = next(ind for (ind, e) in enumerate(edges) if angle < e) diff --git a/satellite/widgets.py b/satellite/widgets.py index d421720..23f768c 100644 --- a/satellite/widgets.py +++ b/satellite/widgets.py @@ -1,10 +1,9 @@ # Copyright 2021-2022 Teemu Ikonen # SPDX-License-Identifier: GPL-3.0-only -import gi - import importlib.resources as resources +import gi gi.require_version('Gtk', '3.0') gi.require_version('Gdk', '3.0') from gi.repository import Gtk # noqa: E402 @@ -18,8 +17,7 @@ def text_barchart(data, highlights, height=None, width=30): height Number of lines in the generated bar chart width Width of the generated bar chart in chars """ - sdata = list((d[0] if d[0] else '', - int(d[1]) if d[1] else 0) for d in data) + sdata = [(d[0] if d[0] else '', int(d[1]) if d[1] else 0) for d in data] sdata.sort(key=lambda x: x[1], reverse=True) dstr = '' @@ -41,14 +39,14 @@ def text_barchart(data, highlights, height=None, width=30): cmax_xaxis = cmaxbar + 3 for d in sdata[:barlines]: block = '\u2585' if d[0] in highlights else '=' - dstr += "%3s\u2502%s %d\n" % (d[0], block*int(scale*d[1]), d[1]) + dstr += "%3s\u2502%s %d\n" % (d[0], block * int(scale * d[1]), d[1]) if barlines < len(sdata): dstr += " \u256a\n" elif (len(sdata) - axislines) < height: # Add empty lines to y-axis dstr += ' \u2502\n' * (height - len(sdata) - axislines) - dstr += " \u251c" + '\u2500'*(cmax_xaxis) + '\u2524\n' - dstr += " 0" + ' '*(cmax_xaxis - 1) + str(max_xaxis) + dstr += " \u251c" + '\u2500' * (cmax_xaxis) + '\u2524\n' + dstr += " 0" + ' ' * (cmax_xaxis - 1) + str(max_xaxis) return dstr diff --git a/setup.cfg b/setup.cfg index f4b00fd..a98fb10 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,3 +1,8 @@ [flake8] exclude=.git,__pycache__,build max-line-length=88 +ignore = B902, BLK100, CCR001, CNL100, D1, I201, Q000, W503 + +[pycodestyle] +count=1 +max-line-length = 88 From 58f4433cda669210db4e4e4d4c72689237f13c90 Mon Sep 17 00:00:00 2001 From: Teemu Ikonen Date: Sun, 29 Jan 2023 15:45:44 +0200 Subject: [PATCH 08/50] Add ModemManagerGLibNmeaSource, use it --- satellite/application.py | 4 +- satellite/mm_glib_source.py | 99 +++++++++++++++++++++++++++++++++++++ 2 files changed, 101 insertions(+), 2 deletions(-) create mode 100644 satellite/mm_glib_source.py diff --git a/satellite/application.py b/satellite/application.py index 908aadd..9c00da1 100755 --- a/satellite/application.py +++ b/satellite/application.py @@ -18,7 +18,7 @@ import satellite.nmea as nmea import satellite.quectel as quectel from satellite import __version__ -from .mm_pydbus_source import QuectelNmeaSource +from .mm_glib_source import ModemManagerGLibNmeaSource from .nmeasource import ( GnssShareNmeaSource, ModemError, @@ -242,7 +242,7 @@ class SatelliteApp(Gtk.Application): def init_quectel_source(self): try: - self.source = QuectelNmeaSource( + self.source = ModemManagerGLibNmeaSource( self.location_update_cb, refresh_rate=self.refresh_rate, # save_filename=unique_filename(self.gpx_save_dir + '/nmeas', diff --git a/satellite/mm_glib_source.py b/satellite/mm_glib_source.py new file mode 100644 index 0000000..3468ffe --- /dev/null +++ b/satellite/mm_glib_source.py @@ -0,0 +1,99 @@ +# Copyright 2023 Teemu Ikonen +# SPDX-License-Identifier: GPL-3.0-only + +import gi +gi.require_version('ModemManager', '1.0') +from gi.repository import Gio, ModemManager # noqa: E402 + +from satellite.nmeasource import ( # noqa: E402 + ModemError, + ModemLockedError, + ModemNoNMEAError, + NmeaSource, + NmeaSourceNotFoundError, +) + + +class ModemManagerGLibNmeaSource(NmeaSource): + + def __init__(self, update_callback, **kwargs): + super().__init__(update_callback, **kwargs) + self.bus = None + self.manager = None + self.modem = None + self.mlocation = None + self.old_refresh_rate = None + self.old_sources_enabled = None + self.old_signals_location = None + self.location_updated = None + + def initialize(self): + self.bus = Gio.bus_get_sync(Gio.BusType.SYSTEM, None) + self.manager = ModemManager.Manager.new_sync( + self.bus, Gio.DBusObjectManagerClientFlags.DO_NOT_AUTO_START, None) + if self.manager.get_name_owner() is None: + raise NmeaSourceNotFoundError("ModemManager is not running") + objs = self.manager.get_objects() + if objs: + self.modem = objs[0].get_modem() + self.mlocation = objs[0].get_modem_location() + else: + raise NmeaSourceNotFoundError("No Modems Found") + self.manufacturer = self.modem.get_manufacturer() + self.model = self.modem.get_model() + self.revision = self.modem.get_revision() + + try: + state = self.modem.get_state() + if int(state) > 0: + if self.old_refresh_rate is None: + self.old_refresh_rate = self.mlocation.props.gps_refresh_rate + if self.old_sources_enabled is None: + self.old_sources_enabled = self.mlocation.props.enabled + if self.old_signals_location is None: + self.old_signals_location = self.mlocation.props.signals_location + caps = self.mlocation.get_capabilities() + if not caps & ModemManager.ModemLocationSource.GPS_NMEA: + raise NmeaSourceNotFoundError( + "Modem does not support NMEA") + self.mlocation.setup_sync( + (ModemManager.ModemLocationSource.GPS_NMEA + | ModemManager.ModemLocationSource.AGPS_MSB), True, None) + else: + raise ModemError("Modem state is: %d" % state) + except AttributeError as e: + if state == ModemManager.ModemState.LOCKED: + raise ModemLockedError from e + else: + raise e + + self.mlocation.set_gps_refresh_rate_sync(self.refresh_rate, None) + self.mlocation.connect('notify::location', self.update_callback) + + self.initialized = True + + def _really_get(self): + if not self.initialized: + self.initialize() + try: + loc = self.mlocation.get_signaled_gps_nmea() + except Exception as e: + self.initialized = False + raise e + + retval = loc.get_traces() + if retval is None: + self.initialized = False + raise ModemNoNMEAError + return '\r\n'.join(retval) + + def close(self): + if self.mlocation is None: + return + self.mlocation.disconnect_by_func(self.update_callback) + if self.old_sources_enabled is not None: + self.mlocation.setup_sync( + ModemManager.ModemLocationSource(self.old_sources_enabled), + self.old_signals_location, None) + if self.old_refresh_rate is not None: + self.mlocation.set_gps_refresh_rate_sync(self.old_refresh_rate, None) From 7115b1697c9962e04a23c8dd450747209189b19d Mon Sep 17 00:00:00 2001 From: Teemu Ikonen Date: Sun, 29 Jan 2023 16:41:52 +0200 Subject: [PATCH 09/50] mm_glib_source: Support quirks, add QuectelTalker quirk --- satellite/mm_glib_source.py | 44 ++++++++++++++++++++++++++++++++----- 1 file changed, 38 insertions(+), 6 deletions(-) diff --git a/satellite/mm_glib_source.py b/satellite/mm_glib_source.py index 3468ffe..419474b 100644 --- a/satellite/mm_glib_source.py +++ b/satellite/mm_glib_source.py @@ -1,9 +1,10 @@ # Copyright 2023 Teemu Ikonen # SPDX-License-Identifier: GPL-3.0-only +import re + import gi -gi.require_version('ModemManager', '1.0') -from gi.repository import Gio, ModemManager # noqa: E402 +from pynmea2.nmea import NMEASentence from satellite.nmeasource import ( # noqa: E402 ModemError, @@ -13,10 +14,13 @@ from satellite.nmeasource import ( # noqa: E402 NmeaSourceNotFoundError, ) +gi.require_version('ModemManager', '1.0') +from gi.repository import Gio, ModemManager # noqa: E402, I100 + class ModemManagerGLibNmeaSource(NmeaSource): - def __init__(self, update_callback, **kwargs): + def __init__(self, update_callback, quirks=[], **kwargs): super().__init__(update_callback, **kwargs) self.bus = None self.manager = None @@ -26,6 +30,7 @@ class ModemManagerGLibNmeaSource(NmeaSource): self.old_sources_enabled = None self.old_signals_location = None self.location_updated = None + self.quirks = quirks def initialize(self): self.bus = Gio.bus_get_sync(Gio.BusType.SYSTEM, None) @@ -81,11 +86,15 @@ class ModemManagerGLibNmeaSource(NmeaSource): self.initialized = False raise e - retval = loc.get_traces() - if retval is None: + nmeas = loc.get_traces() + if nmeas is None: self.initialized = False raise ModemNoNMEAError - return '\r\n'.join(retval) + + if 'QuectelTalker' in self.quirks: + nmeas = self.quectel_talker_quirk(nmeas) + + return '\r\n'.join(nmeas) def close(self): if self.mlocation is None: @@ -97,3 +106,26 @@ class ModemManagerGLibNmeaSource(NmeaSource): self.old_signals_location, None) if self.old_refresh_rate is not None: self.mlocation.set_gps_refresh_rate_sync(self.old_refresh_rate, None) + + def quectel_talker_quirk(self, nmeas): + pq_re = re.compile(r""" + ^\s*\$? + (?PPQ) + (?P\w{3}) + (?P[^*]*) + (?:[*](?P[A-F0-9]{2}))$""", re.VERBOSE) + out = [] + for nmea in (n for n in nmeas if n): + mo = pq_re.match(nmea) + if mo: + # The last extra data field is Signal ID, these are + # 1 = GPS, 2 = Glonass, 3 = Galileo, 4 = BeiDou, 5 = QZSS + # Determine talker from Signal ID + talker = 'QZ' if mo.group('data').endswith('5') else 'BD' + # Fake talker and checksum + fake = talker + "".join(mo.group(2, 3)) + out.append('$' + fake + "*%02X" % NMEASentence.checksum(fake)) + else: + out.append(nmea) + + return out From 17baef902d4dc4cfefb9377326f21c905b03c8d4 Mon Sep 17 00:00:00 2001 From: Teemu Ikonen Date: Sun, 29 Jan 2023 16:43:10 +0200 Subject: [PATCH 10/50] Refactor '--source' CLI arg handling, add 'mm' source Use QuectelTalker quirk in 'quectel' source, add a plain ModemManager source 'mm' without quirks. Fix '--source' arg help string formatting. --- satellite/application.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/satellite/application.py b/satellite/application.py index 9c00da1..6ec9d21 100755 --- a/satellite/application.py +++ b/satellite/application.py @@ -47,7 +47,8 @@ class SatelliteApp(Gtk.Application): Handy.init() desc = "Displays navigation satellite data and saves GPX tracks" - parser = argparse.ArgumentParser(description=desc) + parser = argparse.ArgumentParser( + description=desc, formatter_class=argparse.RawTextHelpFormatter) parser.add_argument( '-c', '--console-output', dest='console_output', action='store_true', default=False, @@ -55,9 +56,10 @@ class SatelliteApp(Gtk.Application): parser.add_argument( '-s', '--source', dest='source', default='quectel', - help="Select NMEA source. Options are " - "\'quectel\' (default) for Quectel Modems or " - "\'gnss-share\' when using gnss-share") + help="Select NMEA source. Options are:\n" + "'quectel' (default) ModemManager with Quectel quirks\n" + "'mm' ModemManager without quirks\n" + "'gnss-share' when using gnss-share\n") self.args = parser.parse_args() GLib.unix_signal_add(GLib.PRIORITY_DEFAULT, signal.SIGINT, @@ -186,7 +188,9 @@ class SatelliteApp(Gtk.Application): source_init = False if self.args.source == 'quectel': - source_init = self.init_quectel_source() + source_init = self.init_mm_source(quirks=['QuectelTalker']) + elif self.args.source == 'mm': + source_init = self.init_mm_source() elif self.args.source == 'gnss-share': source_init = self.init_gnss_share_source() @@ -240,11 +244,12 @@ class SatelliteApp(Gtk.Application): return True - def init_quectel_source(self): + def init_mm_source(self, quirks=[]): try: self.source = ModemManagerGLibNmeaSource( self.location_update_cb, refresh_rate=self.refresh_rate, + quirks=quirks, # save_filename=unique_filename(self.gpx_save_dir + '/nmeas', # '.txt') ) From 0b2cc896357f4402eed26e41f7f73473ae7508c9 Mon Sep 17 00:00:00 2001 From: Teemu Ikonen Date: Sun, 29 Jan 2023 16:48:33 +0200 Subject: [PATCH 11/50] Remove mm_pydbus_source.py (ModemManagerPyDBusNmeaSource) --- satellite/mm_pydbus_source.py | 129 ---------------------------------- 1 file changed, 129 deletions(-) delete mode 100644 satellite/mm_pydbus_source.py diff --git a/satellite/mm_pydbus_source.py b/satellite/mm_pydbus_source.py deleted file mode 100644 index a946a92..0000000 --- a/satellite/mm_pydbus_source.py +++ /dev/null @@ -1,129 +0,0 @@ -# Copyright 2021-2023 Teemu Ikonen -# SPDX-License-Identifier: GPL-3.0-only - -import re - -from pydbus import SystemBus -from pynmea2.nmea import NMEASentence - -import satellite.modem_manager_defs as mm -from satellite.nmeasource import ( - ModemError, - ModemLockedError, - ModemNoNMEAError, - NmeaSource, - NmeaSourceNotFoundError, -) - - -class ModemManagerPyDBusNmeaSource(NmeaSource): - - def __init__(self, update_callback, **kwargs): - super().__init__(update_callback, **kwargs) - self.bus = SystemBus() - self.manager = self.bus.get('.ModemManager1') - self.modem = None - self.old_refresh_rate = None - self.old_sources_enabled = None - self.old_signals = None - self.location_updated = None - - def initialize(self): - objs = self.manager.GetManagedObjects() - mkeys = list(objs.keys()) - if mkeys: - mstr = mkeys[0] - else: - raise NmeaSourceNotFoundError("No Modems Found") - info = objs[mstr]['org.freedesktop.ModemManager1.Modem'] - self.manufacturer = info.get('Manufacturer') - self.model = info.get('Model') - self.revision = info.get('Revision') - self.modem = self.bus.get('.ModemManager1', mstr) - - try: - if self.modem.State > 0: - if self.old_refresh_rate is None: - self.old_refresh_rate = self.modem.GpsRefreshRate - if self.old_sources_enabled is None: - self.old_sources_enabled = self.modem.Enabled - if self.old_signals is None: - self.old_signals = self.modem.SignalsLocation - cap = self.modem.Capabilities - if (cap & mm.MM_MODEM_LOCATION_SOURCE_GPS_NMEA) == 0: - raise NmeaSourceNotFoundError( - "Modem does not support NMEA") - self.modem.Setup( - (mm.MM_MODEM_LOCATION_SOURCE_GPS_NMEA - | (cap & mm.MM_MODEM_LOCATION_SOURCE_AGPS_MSB)), - True) - else: - raise ModemError("Modem state is: %d" % self.modem.State) - except AttributeError as e: - if self.modem.State == mm.MM_MODEM_STATE_LOCKED: - raise ModemLockedError from e - else: - raise ModemError from e - except Exception as e: - raise e - - self.modem.SetGpsRefreshRate(self.refresh_rate) - self.location_updated = self.bus.subscribe( - sender='org.freedesktop.ModemManager1', - iface='org.freedesktop.DBus.Properties', - signal='PropertiesChanged', - arg0='org.freedesktop.ModemManager1.Modem.Location', - signal_fired=self.update_callback) - self.initialized = True - - def _really_get(self): - if not self.initialized: - self.initialize() - try: - loc = self.modem.GetLocation() - except Exception as e: - self.initialized = False - raise e - - retval = loc.get(mm.MM_MODEM_LOCATION_SOURCE_GPS_NMEA) - if retval is None: - self.initialized = False - raise ModemNoNMEAError - return retval - - def close(self): - if self.location_updated is not None: - self.location_updated.disconnect() - if self.old_sources_enabled is not None: - self.modem.Setup(self.old_sources_enabled, self.old_signals) - if self.old_refresh_rate is not None: - self.modem.SetGpsRefreshRate(self.old_refresh_rate) - - -class QuectelNmeaSource(ModemManagerPyDBusNmeaSource): - - def _really_get(self): - return self.fix_talker(super()._really_get()) - - def fix_talker(self, nmeas): - pq_re = re.compile(r""" - ^\s*\$? - (?PPQ) - (?P\w{3}) - (?P[^*]*) - (?:[*](?P[A-F0-9]{2}))$""", re.VERBOSE) - out = [] - for nmea in (n for n in nmeas.split('\r\n') if n): - mo = pq_re.match(nmea) - if mo: - # The last extra data field is Signal ID, these are - # 1 = GPS, 2 = Glonass, 3 = Galileo, 4 = BeiDou, 5 = QZSS - # Determine talker from Signal ID - talker = 'QZ' if mo.group('data').endswith('5') else 'BD' - # Fake talker and checksum - fake = talker + "".join(mo.group(2, 3)) - out.append('$' + fake + "*%02X" % NMEASentence.checksum(fake)) - else: - out.append(nmea) - - return "\r\n".join(out) From 7206198321d175a7707d8090fe76e459c19b3044 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ferenc=20G=C3=A9czi?= Date: Wed, 22 Mar 2023 00:00:00 +0000 Subject: [PATCH 12/50] Update to Gnome runtime 44 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Ferenc Géczi --- flatpak/page.codeberg.tpikonen.satellite.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flatpak/page.codeberg.tpikonen.satellite.json b/flatpak/page.codeberg.tpikonen.satellite.json index 9709a60..00fae45 100644 --- a/flatpak/page.codeberg.tpikonen.satellite.json +++ b/flatpak/page.codeberg.tpikonen.satellite.json @@ -1,7 +1,7 @@ { "app-id": "page.codeberg.tpikonen.satellite", "runtime": "org.gnome.Platform", - "runtime-version": "43", + "runtime-version": "44", "sdk": "org.gnome.Sdk", "command": "satellite", "rename-desktop-file": "satellite.desktop", From 9de70b54469b95aa173e92aee029ec39f51adba0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ferenc=20G=C3=A9czi?= Date: Wed, 22 Mar 2023 00:00:00 +0000 Subject: [PATCH 13/50] Add ModemManager to flatpak as module MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Ferenc Géczi --- flatpak/page.codeberg.tpikonen.satellite.json | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/flatpak/page.codeberg.tpikonen.satellite.json b/flatpak/page.codeberg.tpikonen.satellite.json index 00fae45..da906ec 100644 --- a/flatpak/page.codeberg.tpikonen.satellite.json +++ b/flatpak/page.codeberg.tpikonen.satellite.json @@ -18,6 +18,27 @@ ], "modules": [ "python3-requirements.json", + { + "name": "ModemManager", + "config-opts": [ + "--without-udev", + "--with-udev-base-dir=/app/lib/udev", + "--with-systemdsystemunitdir=/app/lib/systemd/system", + "--without-examples", + "--without-tests", + "--without-mbim", + "--without-qmi", + "--without-qrtr", + "--without-man" + ], + "sources": [ + { + "type": "git", + "url": "https://gitlab.freedesktop.org/mobile-broadband/ModemManager.git", + "tag": "1.20.6" + } + ] + }, { "name": "satellite", "sources": [ From 7fd0d42f044e7cafa44dcecdd5c48ade77010354 Mon Sep 17 00:00:00 2001 From: Teemu Ikonen Date: Wed, 1 Mar 2023 11:40:19 +0200 Subject: [PATCH 14/50] Add .editorconfig file --- .editorconfig | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..0f1deb2 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,26 @@ +# EditorConfig is awesome: http://EditorConfig.org + +# top-most EditorConfig file +root = true + +# Unix-style newlines with a newline ending every file +[*] +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true +charset = utf-8 + +# 4 space indentation +[*.{py,java,r,R}] +indent_style = space +indent_size = 4 + +# 2 space indentation +[*.{js,json,y{a,}ml,html,cwl}] +indent_style = space +indent_size = 2 + +[*.{md,Rmd,rst}] +trim_trailing_whitespace = false +indent_style = space +indent_size = 2 From 8c0f1587f46ed2f63c50eb3cd6f504e29501ba8d Mon Sep 17 00:00:00 2001 From: Teemu Ikonen Date: Thu, 16 Mar 2023 18:34:50 +0200 Subject: [PATCH 15/50] Remove modem_manager_defs.py --- satellite/modem_manager_defs.py | 31 ------------------------------- 1 file changed, 31 deletions(-) delete mode 100644 satellite/modem_manager_defs.py diff --git a/satellite/modem_manager_defs.py b/satellite/modem_manager_defs.py deleted file mode 100644 index 6500509..0000000 --- a/satellite/modem_manager_defs.py +++ /dev/null @@ -1,31 +0,0 @@ -# Copyright 2021-2022 Teemu Ikonen -# SPDX-License-Identifier: GPL-3.0-only - -# flake8: noqa - -# See /usr/include/ModemManager/ModemManager-enums.h in modemmanager-dev -MM_MODEM_LOCATION_SOURCE_NONE = 0 -MM_MODEM_LOCATION_SOURCE_3GPP_LAC_CI = 1 << 0 -MM_MODEM_LOCATION_SOURCE_GPS_RAW = 1 << 1 -MM_MODEM_LOCATION_SOURCE_GPS_NMEA = 1 << 2 -MM_MODEM_LOCATION_SOURCE_CDMA_BS = 1 << 3 -MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED = 1 << 4 -MM_MODEM_LOCATION_SOURCE_AGPS_MSA = 1 << 5 -MM_MODEM_LOCATION_SOURCE_AGPS_MSB = 1 << 6 - -MM_MODEM_LOCATION_ASSISTANCE_DATA_TYPE_NONE = 0 -MM_MODEM_LOCATION_ASSISTANCE_DATA_TYPE_XTRA = 1 << 0 - -MM_MODEM_STATE_FAILED = -1 -MM_MODEM_STATE_UNKNOWN = 0 -MM_MODEM_STATE_INITIALIZING = 1 -MM_MODEM_STATE_LOCKED = 2 -MM_MODEM_STATE_DISABLED = 3 -MM_MODEM_STATE_DISABLING = 4 -MM_MODEM_STATE_ENABLING = 5 -MM_MODEM_STATE_ENABLED = 6 -MM_MODEM_STATE_SEARCHING = 7 -MM_MODEM_STATE_REGISTERED = 8 -MM_MODEM_STATE_DISCONNECTING = 9 -MM_MODEM_STATE_CONNECTING = 10 -MM_MODEM_STATE_CONNECTED = 11 From 2bb1b7099420ae11b62707c08f5ef56ce76a3063 Mon Sep 17 00:00:00 2001 From: Teemu Ikonen Date: Thu, 16 Mar 2023 18:45:27 +0200 Subject: [PATCH 16/50] mm_glib_source: Disconnect update cb in initialization --- satellite/mm_glib_source.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/satellite/mm_glib_source.py b/satellite/mm_glib_source.py index 419474b..f5c7db5 100644 --- a/satellite/mm_glib_source.py +++ b/satellite/mm_glib_source.py @@ -33,6 +33,9 @@ class ModemManagerGLibNmeaSource(NmeaSource): self.quirks = quirks def initialize(self): + # If reinitializing, disconnect old update cb + if self.mlocation is not None: + self.mlocation.disconnect_by_func(self.update_callback) self.bus = Gio.bus_get_sync(Gio.BusType.SYSTEM, None) self.manager = ModemManager.Manager.new_sync( self.bus, Gio.DBusObjectManagerClientFlags.DO_NOT_AUTO_START, None) From a4a599a6c513b5dc17198141a71355d66e3f6888 Mon Sep 17 00:00:00 2001 From: Teemu Ikonen Date: Thu, 16 Mar 2023 18:46:21 +0200 Subject: [PATCH 17/50] mm_glib_source: Enable AGPS_MSB only if it's in modem capabilities --- satellite/mm_glib_source.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/satellite/mm_glib_source.py b/satellite/mm_glib_source.py index f5c7db5..bcdad97 100644 --- a/satellite/mm_glib_source.py +++ b/satellite/mm_glib_source.py @@ -64,9 +64,10 @@ class ModemManagerGLibNmeaSource(NmeaSource): if not caps & ModemManager.ModemLocationSource.GPS_NMEA: raise NmeaSourceNotFoundError( "Modem does not support NMEA") - self.mlocation.setup_sync( - (ModemManager.ModemLocationSource.GPS_NMEA - | ModemManager.ModemLocationSource.AGPS_MSB), True, None) + enable = ModemManager.ModemLocationSource.GPS_NMEA + if caps & ModemManager.ModemLocationSource.AGPS_MSB: + enable |= ModemManager.ModemLocationSource.AGPS_MSB + self.mlocation.setup_sync(enable, True, None) else: raise ModemError("Modem state is: %d" % state) except AttributeError as e: From d6e4a71380f4d2f2686633ef02d430254ed5e6ae Mon Sep 17 00:00:00 2001 From: Teemu Ikonen Date: Fri, 17 Mar 2023 22:01:45 +0200 Subject: [PATCH 18/50] mm_glib_source: Ignore error in disconnect when closing an uninitialized source --- satellite/mm_glib_source.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/satellite/mm_glib_source.py b/satellite/mm_glib_source.py index bcdad97..056c6bf 100644 --- a/satellite/mm_glib_source.py +++ b/satellite/mm_glib_source.py @@ -103,7 +103,10 @@ class ModemManagerGLibNmeaSource(NmeaSource): def close(self): if self.mlocation is None: return - self.mlocation.disconnect_by_func(self.update_callback) + try: + self.mlocation.disconnect_by_func(self.update_callback) + except TypeError: + pass # Ignore error when nothing is connected if self.old_sources_enabled is not None: self.mlocation.setup_sync( ModemManager.ModemLocationSource(self.old_sources_enabled), From e6205bd9e5cf438fb8d5e8d71b6217100bfa8681 Mon Sep 17 00:00:00 2001 From: Teemu Ikonen Date: Fri, 17 Mar 2023 22:19:04 +0200 Subject: [PATCH 19/50] mm_glib_source: Ignore error when enabling AGPS MSB AGPS enablement sometimes timeouts on Oneplus 6. Also improve logging of NMEA initialization errors. --- satellite/application.py | 2 +- satellite/mm_glib_source.py | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/satellite/application.py b/satellite/application.py index 6ec9d21..22f8776 100755 --- a/satellite/application.py +++ b/satellite/application.py @@ -261,8 +261,8 @@ class SatelliteApp(Gtk.Application): dtext = "Please unlock the Modem" else: fatal = isinstance(e, gi.repository.GLib.GError) - self.log_msg("Error initializing NMEA source") etext = str(e) + self.log_msg(f"Error initializing NMEA source: {etext}") dtext = etext if etext else ( "Could not find or initialize NMEA source") dialog = Gtk.MessageDialog( diff --git a/satellite/mm_glib_source.py b/satellite/mm_glib_source.py index 056c6bf..6f23131 100644 --- a/satellite/mm_glib_source.py +++ b/satellite/mm_glib_source.py @@ -75,6 +75,10 @@ class ModemManagerGLibNmeaSource(NmeaSource): raise ModemLockedError from e else: raise e + except gi.repository.GLib.GError as e: + # Ignore error on AGPS enablement by this hack + if 'agps-msb' not in str(e): + raise e self.mlocation.set_gps_refresh_rate_sync(self.refresh_rate, None) self.mlocation.connect('notify::location', self.update_callback) From 4d73be8c73499a71fcb1ba287249c4e7b24e822a Mon Sep 17 00:00:00 2001 From: Teemu Ikonen Date: Wed, 22 Mar 2023 15:08:59 +0200 Subject: [PATCH 20/50] requirements.txt: Remove pydbus --- requirements.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 643efb9..19855d4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,2 @@ gpxpy pynmea2 -pydbus From 83164bb80f86f7b4f40ba536f061be4ba9c3c800 Mon Sep 17 00:00:00 2001 From: Teemu Ikonen Date: Wed, 22 Mar 2023 15:12:03 +0200 Subject: [PATCH 21/50] flatpak: Update python3-requirements --- flatpak/python3-requirements.json | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/flatpak/python3-requirements.json b/flatpak/python3-requirements.json index ad534ab..30f3cac 100644 --- a/flatpak/python3-requirements.json +++ b/flatpak/python3-requirements.json @@ -26,22 +26,8 @@ "sources": [ { "type": "file", - "url": "https://files.pythonhosted.org/packages/c9/13/6117f735c3e8083bfce0ccd31a1d561fc2adb0e0e2d1ab3ace12256a3513/pynmea2-1.18.0-py3-none-any.whl", - "sha256": "098f9ffd89c4a6c5e137b8b59e5b38194888d4a557c50b003ebcf2c3c15ec22e" - } - ] - }, - { - "name": "python3-pydbus", - "buildsystem": "simple", - "build-commands": [ - "pip3 install --verbose --exists-action=i --no-index --find-links=\"file://${PWD}\" --prefix=${FLATPAK_DEST} \"pydbus\" --no-build-isolation" - ], - "sources": [ - { - "type": "file", - "url": "https://files.pythonhosted.org/packages/92/56/27148014c2f85ce70332f18612f921f682395c7d4e91ec103783be4fce00/pydbus-0.6.0-py2.py3-none-any.whl", - "sha256": "66b80106352a718d80d6c681dc2a82588048e30b75aab933e4020eb0660bf85e" + "url": "https://files.pythonhosted.org/packages/75/24/1f575eb17a8135e54b3c243ff87e2f4d6b2389942836021d0628ed837559/pynmea2-1.19.0-py3-none-any.whl", + "sha256": "5138558b4fb5daa587b2c17de99eb43df0297039de1c98010c996624abfb00eb" } ] } From 51eb4bb71438f689fb9a048ac9c78ad05ee054b6 Mon Sep 17 00:00:00 2001 From: Teemu Ikonen Date: Wed, 22 Mar 2023 15:58:33 +0200 Subject: [PATCH 22/50] flatpak: Use ModemManager release tarball --- flatpak/page.codeberg.tpikonen.satellite.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flatpak/page.codeberg.tpikonen.satellite.json b/flatpak/page.codeberg.tpikonen.satellite.json index da906ec..9b765e9 100644 --- a/flatpak/page.codeberg.tpikonen.satellite.json +++ b/flatpak/page.codeberg.tpikonen.satellite.json @@ -33,9 +33,9 @@ ], "sources": [ { - "type": "git", - "url": "https://gitlab.freedesktop.org/mobile-broadband/ModemManager.git", - "tag": "1.20.6" + "type": "archive", + "url": "https://gitlab.freedesktop.org/mobile-broadband/ModemManager/-/archive/1.20.6/ModemManager-1.20.6.tar.gz", + "sha256": "d3e8112810e48ba32e80757fced218cf65b135b5a2987dad6b431d8cfbba765f" } ] }, From fbfd588f78147e971b72af74b3aee70d4609a070 Mon Sep 17 00:00:00 2001 From: Teemu Ikonen Date: Wed, 22 Mar 2023 16:31:09 +0200 Subject: [PATCH 23/50] appdata: Improve description --- data/appdata.xml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/data/appdata.xml b/data/appdata.xml index 13bb5b2..82e31bc 100644 --- a/data/appdata.xml +++ b/data/appdata.xml @@ -9,10 +9,11 @@ Check your GPS reception and save your tracks

Satellite displays global navigation satellite system (GNSS: that's GPS, - Galileo, Glonass etc.) data obtained from the ModemManager API. You can use - it to check the navigation satellite signal strength in your location and - see your speed, coordinates and other parameters once a fix is obtained. - It can also save GPX-tracks of your travels.

+ Galileo, Glonass etc.) data obtained from an NMEA source in your device. + Currently the ModemManager and gnss-share APIs are supported. You can use + it to check the navigation satellite signal strength and see your speed, + coordinates and other parameters once a fix is obtained. It can also save + GPX-tracks of your travels.

page.codeberg.tpikonen.satellite.desktop https://codeberg.org/tpikonen/satellite From 782499e27bbe489a1fdaf8730cae44e2b30d9226 Mon Sep 17 00:00:00 2001 From: Teemu Ikonen Date: Wed, 22 Mar 2023 17:41:25 +0200 Subject: [PATCH 24/50] nmea: Get system time as non-naive datetime object --- satellite/nmea.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/satellite/nmea.py b/satellite/nmea.py index 9d13847..6b2f001 100644 --- a/satellite/nmea.py +++ b/satellite/nmea.py @@ -249,7 +249,7 @@ def parse(nmeas, always_add_prefix=False): } out.update({k: msg_get(msgs, k) for k in getters.keys()}) - datenow = datetime.datetime.utcnow() + datenow = datetime.datetime.now(datetime.timezone.utc) fixtime = out.get('fixtime') fixdate = out.get('date') if fixdate is None and fixtime is not None: From c3115dd751b041b097c868ce2f530c609e459d74 Mon Sep 17 00:00:00 2001 From: Teemu Ikonen Date: Wed, 22 Mar 2023 20:25:58 +0200 Subject: [PATCH 25/50] Output 'n/a' for missing mode_indicator --- satellite/application.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/satellite/application.py b/satellite/application.py index 22f8776..ec4e996 100755 --- a/satellite/application.py +++ b/satellite/application.py @@ -420,7 +420,8 @@ class SatelliteApp(Gtk.Application): # Mapping: Data key, description, converter func order = [ ("mode", "Fix type", lambda x: mode2fix.get(x, "No Fix")), - ("mode_indicator", "Modes (GP,GL,GA)", lambda x: str(x)), + ("mode_indicator", "Modes (GP,GL,GA)", + lambda x: str(x) if x is not None else "n/a"), ("actives", "Active / in use sats", get_actives), ("visibles", "Receiving sats", lambda x: str(len( [r for r in x if r['snr'] > 0.0]))), From 89f6ac85db92a57f2787c93f92e1fb57b28aa762 Mon Sep 17 00:00:00 2001 From: Teemu Ikonen Date: Wed, 22 Mar 2023 20:27:06 +0200 Subject: [PATCH 26/50] nmea: Move GGA timestamp from 'fixtime' to 'time' --- satellite/nmea.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/satellite/nmea.py b/satellite/nmea.py index 6b2f001..9be0736 100644 --- a/satellite/nmea.py +++ b/satellite/nmea.py @@ -101,10 +101,10 @@ getters = { }, "fixtime": { 'RMC': get_time, - 'GGA': get_time, }, "time": { # Reported also when no fix 'GNS': get_time, + 'GGA': get_time, }, "date": { 'RMC': get_date, From 88e53d34bba358e0c03f7a97ff4054c364406069 Mon Sep 17 00:00:00 2001 From: Teemu Ikonen Date: Wed, 22 Mar 2023 20:28:00 +0200 Subject: [PATCH 27/50] nmea: Parse 'num_sats' also from GGA sentences --- satellite/nmea.py | 1 + 1 file changed, 1 insertion(+) diff --git a/satellite/nmea.py b/satellite/nmea.py index 9be0736..2843a7c 100644 --- a/satellite/nmea.py +++ b/satellite/nmea.py @@ -132,6 +132,7 @@ getters = { }, "num_sats": { 'GNS': iget('num_sats', default=0), + 'GGA': iget('num_sats', default=0), }, "pdop": { 'GSA': fget('pdop'), From f77ce58ee878b319392bcd72c927185ad016308d Mon Sep 17 00:00:00 2001 From: Teemu Ikonen Date: Wed, 22 Mar 2023 16:32:34 +0200 Subject: [PATCH 28/50] Release 0.4.0 --- data/appdata.xml | 10 ++++++++++ satellite/__init__.py | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/data/appdata.xml b/data/appdata.xml index 82e31bc..5d83cd0 100644 --- a/data/appdata.xml +++ b/data/appdata.xml @@ -49,6 +49,16 @@ + + +

The managerial release

+
    +
  • Use mm-glib to talk to ModemManager, remove pydbus
  • +
  • Support 'quirks' in the ModemManager source, e.g. Quectel talker fixes
  • +
  • Various reliability fixes
  • +
+
+

The quickfix release

diff --git a/satellite/__init__.py b/satellite/__init__.py index efd3dc7..24cfe9a 100644 --- a/satellite/__init__.py +++ b/satellite/__init__.py @@ -1,4 +1,4 @@ # Copyright 2021-2022 Teemu Ikonen # SPDX-License-Identifier: GPL-3.0-only -__version__ = "0.3.1" +__version__ = "0.4.0" From 98da3e4ffac2fd7f86bbdd31011929313c682541 Mon Sep 17 00:00:00 2001 From: Teemu Ikonen Date: Thu, 23 Mar 2023 13:53:08 +0200 Subject: [PATCH 29/50] mm_glib_source: Handle None from get_signaled_gps_nmea() --- satellite/mm_glib_source.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/satellite/mm_glib_source.py b/satellite/mm_glib_source.py index 6f23131..ba51a4c 100644 --- a/satellite/mm_glib_source.py +++ b/satellite/mm_glib_source.py @@ -94,6 +94,9 @@ class ModemManagerGLibNmeaSource(NmeaSource): self.initialized = False raise e + if loc is None: + raise ModemNoNMEAError + nmeas = loc.get_traces() if nmeas is None: self.initialized = False From 6d1a10f96a077e88a0afed8dae642d32817c811e Mon Sep 17 00:00:00 2001 From: Teemu Ikonen Date: Wed, 19 Apr 2023 14:22:12 +0300 Subject: [PATCH 30/50] nmea: Create a non-naive fix datetime --- satellite/nmea.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/satellite/nmea.py b/satellite/nmea.py index 2843a7c..5201149 100644 --- a/satellite/nmea.py +++ b/satellite/nmea.py @@ -256,7 +256,7 @@ def parse(nmeas, always_add_prefix=False): if fixdate is None and fixtime is not None: # We have a fix but no RMC sentence fixdate = datenow.date() - fixdt = (datetime.datetime.combine(fixdate, fixtime) + fixdt = (datetime.datetime.combine(fixdate, fixtime, datetime.timezone.utc) if (fixtime and fixdate) else None) out["datetime"] = fixdt out["systime"] = datenow From aec07d5fd4573e91b53fe45a780fbc76931904fd Mon Sep 17 00:00:00 2001 From: Teemu Ikonen Date: Wed, 19 Apr 2023 14:22:35 +0300 Subject: [PATCH 31/50] setup.py: set Development Status to Beta --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 1861be4..2f90ed5 100644 --- a/setup.py +++ b/setup.py @@ -29,7 +29,7 @@ setuptools.setup( "Bug Tracker": "https://codeberg.org/tpikonen/satellite/issues", }, classifiers=[ - "Development Status :: 3 - Alpha", + "Development Status :: 4 - Beta", "Environment :: X11 Applications :: GTK", "Intended Audience :: End Users/Desktop", "License :: OSI Approved :: GNU General Public License v3 (GPLv3)", From 38bc1f7eb539a440d9256c684ecf02e9b86c7d76 Mon Sep 17 00:00:00 2001 From: Teemu Ikonen Date: Fri, 5 May 2023 11:50:32 +0300 Subject: [PATCH 32/50] flatpak: Add filesystem read permission to /run/gnss-share.sock --- flatpak/page.codeberg.tpikonen.satellite.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/flatpak/page.codeberg.tpikonen.satellite.json b/flatpak/page.codeberg.tpikonen.satellite.json index 9b765e9..dfb0d11 100644 --- a/flatpak/page.codeberg.tpikonen.satellite.json +++ b/flatpak/page.codeberg.tpikonen.satellite.json @@ -12,7 +12,8 @@ "--device=dri", "--talk-name=org.gtk.vfs.*", "--system-talk-name=org.freedesktop.ModemManager1.*", - "--filesystem=xdg-documents/satellite-tracks:create" + "--filesystem=xdg-documents/satellite-tracks:create", + "--filesystem=/run/gnss-share.sock:ro" ], "cleanup": [ ], From 0af37d1dc39fd50cec978faa40c146e3e35bd994 Mon Sep 17 00:00:00 2001 From: Teemu Ikonen Date: Fri, 5 May 2023 12:22:12 +0300 Subject: [PATCH 33/50] flatpak: Convert build manifest to YAML --- README.md | 2 +- flatpak/page.codeberg.tpikonen.satellite.json | 61 ------------------- flatpak/page.codeberg.tpikonen.satellite.yaml | 42 +++++++++++++ 3 files changed, 43 insertions(+), 62 deletions(-) delete mode 100644 flatpak/page.codeberg.tpikonen.satellite.json create mode 100644 flatpak/page.codeberg.tpikonen.satellite.yaml diff --git a/README.md b/README.md index 619eb04..60e529f 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,7 @@ This creates an executable Python script in `$HOME/.local/bin/satellite`. Run - flatpak-builder --install --user build-dir flatpak/page.codeberg.tpikonen.satellite.json + flatpak-builder --install --user build-dir flatpak/page.codeberg.tpikonen.satellite.yaml in the source tree root to install a local build to the user flatpak repo. diff --git a/flatpak/page.codeberg.tpikonen.satellite.json b/flatpak/page.codeberg.tpikonen.satellite.json deleted file mode 100644 index dfb0d11..0000000 --- a/flatpak/page.codeberg.tpikonen.satellite.json +++ /dev/null @@ -1,61 +0,0 @@ -{ - "app-id": "page.codeberg.tpikonen.satellite", - "runtime": "org.gnome.Platform", - "runtime-version": "44", - "sdk": "org.gnome.Sdk", - "command": "satellite", - "rename-desktop-file": "satellite.desktop", - "finish-args": [ - "--socket=fallback-x11", - "--socket=wayland", - "--share=ipc", - "--device=dri", - "--talk-name=org.gtk.vfs.*", - "--system-talk-name=org.freedesktop.ModemManager1.*", - "--filesystem=xdg-documents/satellite-tracks:create", - "--filesystem=/run/gnss-share.sock:ro" - ], - "cleanup": [ - ], - "modules": [ - "python3-requirements.json", - { - "name": "ModemManager", - "config-opts": [ - "--without-udev", - "--with-udev-base-dir=/app/lib/udev", - "--with-systemdsystemunitdir=/app/lib/systemd/system", - "--without-examples", - "--without-tests", - "--without-mbim", - "--without-qmi", - "--without-qrtr", - "--without-man" - ], - "sources": [ - { - "type": "archive", - "url": "https://gitlab.freedesktop.org/mobile-broadband/ModemManager/-/archive/1.20.6/ModemManager-1.20.6.tar.gz", - "sha256": "d3e8112810e48ba32e80757fced218cf65b135b5a2987dad6b431d8cfbba765f" - } - ] - }, - { - "name": "satellite", - "sources": [ - { - "type": "git", - "path": "../", - "branch": "main" - } - ], - "buildsystem": "simple", - "build-commands": [ - "pip3 install --verbose --no-index --no-deps --no-build-isolation --prefix=${FLATPAK_DEST} ./" - ], - "post-install": [ - "install -Dm644 data/appdata.xml $FLATPAK_DEST/share/metainfo/$FLATPAK_ID.appdata.xml" - ] - } - ] -} diff --git a/flatpak/page.codeberg.tpikonen.satellite.yaml b/flatpak/page.codeberg.tpikonen.satellite.yaml new file mode 100644 index 0000000..6007aa5 --- /dev/null +++ b/flatpak/page.codeberg.tpikonen.satellite.yaml @@ -0,0 +1,42 @@ +app-id: page.codeberg.tpikonen.satellite +runtime: org.gnome.Platform +runtime-version: "44" +sdk: org.gnome.Sdk +command: satellite +rename-desktop-file: satellite.desktop +finish-args: + - --socket=fallback-x11 + - --socket=wayland + - --share=ipc + - --device=dri + - --talk-name=org.gtk.vfs.* + - --system-talk-name=org.freedesktop.ModemManager1.* + - --filesystem=xdg-documents/satellite-tracks:create + - --filesystem=/run/gnss-share.sock:ro +cleanup: [] +modules: + - python3-requirements.json + - name: ModemManager + config-opts: + - --without-udev + - --with-udev-base-dir=/app/lib/udev + - --with-systemdsystemunitdir=/app/lib/systemd/system + - --without-examples + - --without-tests + - --without-mbim + - --without-qmi + - --without-qrtr + - --without-man + sources: + - type: archive + url: https://gitlab.freedesktop.org/mobile-broadband/ModemManager/-/archive/1.20.6/ModemManager-1.20.6.tar.gz + sha256: d3e8112810e48ba32e80757fced218cf65b135b5a2987dad6b431d8cfbba765f + - name: satellite + sources: + - type: git + path: ../ + branch: main + buildsystem: simple + build-commands: + - pip3 install --verbose --no-index --no-deps --no-build-isolation --prefix=${FLATPAK_DEST} ./ + - install -Dm644 data/appdata.xml $FLATPAK_DEST/share/metainfo/$FLATPAK_ID.appdata.xml From 0f49eb26702d4a9162f0b7f9c7031ffd717261b0 Mon Sep 17 00:00:00 2001 From: Teemu Ikonen Date: Sat, 13 May 2023 11:34:40 +0300 Subject: [PATCH 34/50] README.md: update --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 60e529f..4fa36f7 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ ![Expanded satellite SNR view](https://github.com/flathub/page.codeberg.tpikonen.satellite/raw/master/screenshot-snr.png) ![Speedometer and track recording](https://github.com/flathub/page.codeberg.tpikonen.satellite/raw/master/screenshot-track.png) -Satellite is an adaptive GTK / libhandy application which displays global navigation satellite system +Satellite is an adaptive GTK3 / libhandy application which displays global navigation satellite system (GNSS: GPS et al.) data obtained from [ModemManager](https://www.freedesktop.org/wiki/Software/ModemManager/) or [gnss-share](https://gitlab.com/postmarketOS/gnss-share). It can also save your position to a GPX-file. @@ -15,7 +15,7 @@ GPL-3.0 ## Dependencies: - python 3.6+, gi, Gtk, libhandy, pydbus, pynmea2, gpxpy + python 3.6+, gi, Gtk3, libhandy, libmm-glib, pynmea2, gpxpy ## Installing and running @@ -45,9 +45,9 @@ Run the script `bin/satellite`. Run - pip3 install --user ./ + pip install --user ./ -in the source tree root. +in the source tree root (use `pipx` instead of `pip` if necessary). This creates an executable Python script in `$HOME/.local/bin/satellite`. From da78c02a68010f6ca44eb053bd2334e6be69865d Mon Sep 17 00:00:00 2001 From: Teemu Ikonen Date: Sat, 13 May 2023 13:21:12 +0300 Subject: [PATCH 35/50] Add NMEA source autodetection --- satellite/application.py | 77 ++++++++++++++++++++++++---------------- satellite/nmeasource.py | 6 ++-- 2 files changed, 50 insertions(+), 33 deletions(-) diff --git a/satellite/application.py b/satellite/application.py index ec4e996..8289ed5 100755 --- a/satellite/application.py +++ b/satellite/application.py @@ -55,11 +55,13 @@ class SatelliteApp(Gtk.Application): help='Output satellite data to console') parser.add_argument( '-s', '--source', dest='source', - default='quectel', + choices=['auto', 'quectel', 'mm', 'gnss-share'], + default='auto', help="Select NMEA source. Options are:\n" - "'quectel' (default) ModemManager with Quectel quirks\n" + "'auto' (default) Automatic source detection\n" + "'quectel' ModemManager with Quectel quirks\n" "'mm' ModemManager without quirks\n" - "'gnss-share' when using gnss-share\n") + "'gnss-share' Read from gnss-share socket\n") self.args = parser.parse_args() GLib.unix_signal_add(GLib.PRIORITY_DEFAULT, signal.SIGINT, @@ -173,7 +175,6 @@ class SatelliteApp(Gtk.Application): self.datascroll.connect('edge-overshot', self.on_edge_overshot) self.log_msg(f"{appname} version {__version__} started") - self.log_msg(f'Trying to initialize source "{self.args.source}"') # Initialize modem after GUI startup GLib.timeout_add(1000, self.init_source, None) @@ -187,21 +188,41 @@ class SatelliteApp(Gtk.Application): def init_source(self, unused): source_init = False - if self.args.source == 'quectel': - source_init = self.init_mm_source(quirks=['QuectelTalker']) - elif self.args.source == 'mm': - source_init = self.init_mm_source() - elif self.args.source == 'gnss-share': - source_init = self.init_gnss_share_source() - - if not source_init: - self.log_msg('No NmeaSource initialized') - return GLib.SOURCE_REMOVE + if self.args.source == 'auto': + self.log_msg("Detecting NMEA sources...") + if not source_init: + source_init = self.init_gnss_share_source(autodetect=True) + if not source_init: + source_init = self.init_mm_source( + quirks=['QuectelTalker'], autodetect=True) + if not source_init: + self.log_msg('NMEA source not found') + dialog = Gtk.MessageDialog( + parent=self.window, modal=True, + message_type=Gtk.MessageType.ERROR, + buttons=Gtk.ButtonsType.OK, + text="Could not find an NMEA source") + dialog.set_title("Error initializing NMEA source") + dialog.run() + dialog.destroy() + return GLib.SOURCE_REMOVE + else: + self.log_msg(f'NMEA source "{self.args.source}" selected') + if self.args.source == 'quectel': + source_init = self.init_mm_source(quirks=['QuectelTalker']) + elif self.args.source == 'mm': + source_init = self.init_mm_source() + elif self.args.source == 'gnss-share': + source_init = self.init_gnss_share_source() + if not source_init: + self.log_msg('Could not initialize NMEA source') + return GLib.SOURCE_REMOVE self.log_msg( f"Source is {self.source.manufacturer}, model {self.source.model}" - + f", revision {self.source.revision}" - if self.source.revision else "") + + (f", revision {self.source.revision}" if self.source.revision else "") + + (f" using {', '.join(self.source.quirks)} quirks" + if hasattr(self.source, "quirks") and self.source.quirks else "")) if (self.source.model and self.source.model.startswith("QUECTEL")): constellations = quectel.get_constellations(self.source) @@ -222,14 +243,15 @@ class SatelliteApp(Gtk.Application): return GLib.SOURCE_REMOVE - def init_gnss_share_source(self): + def init_gnss_share_source(self, autodetect=False): try: self.source = GnssShareNmeaSource(self.location_update_cb) self.source.initialize() except Exception as e: - fatal = False + if autodetect: + return False self.log_msg(str(e)) - dtext = 'Can you access "/var/run/gnss-share.sock"?\n' + str(e) + dtext = str(e) dialog = Gtk.MessageDialog( parent=self.window, modal=True, message_type=Gtk.MessageType.ERROR, @@ -237,14 +259,12 @@ class SatelliteApp(Gtk.Application): dialog.set_title("Error initializing NMEA source") dialog.run() dialog.destroy() - if fatal: - self.quit() return False return True - def init_mm_source(self, quirks=[]): + def init_mm_source(self, quirks=[], autodetect=False): try: self.source = ModemManagerGLibNmeaSource( self.location_update_cb, @@ -255,27 +275,24 @@ class SatelliteApp(Gtk.Application): ) self.source.initialize() except Exception as e: - fatal = False + if autodetect: + return False if isinstance(e, ModemLockedError): self.log_msg("Modem is locked") dtext = "Please unlock the Modem" else: - fatal = isinstance(e, gi.repository.GLib.GError) etext = str(e) - self.log_msg(f"Error initializing NMEA source: {etext}") + self.log_msg(f"Error initializing ModemManager NMEA source: {etext}") dtext = etext if etext else ( - "Could not find or initialize NMEA source") + "Could not initialize ModemManager NMEA source") dialog = Gtk.MessageDialog( parent=self.window, modal=True, message_type=Gtk.MessageType.ERROR, - buttons=Gtk.ButtonsType.CLOSE if fatal else Gtk.ButtonsType.OK, + buttons=Gtk.ButtonsType.OK, text=dtext) dialog.set_title("Error initializing NMEA source") dialog.run() dialog.destroy() - if fatal: - self.quit() - return return False diff --git a/satellite/nmeasource.py b/satellite/nmeasource.py index e685e76..130e1c7 100644 --- a/satellite/nmeasource.py +++ b/satellite/nmeasource.py @@ -71,9 +71,9 @@ class UnixSocketNmeaSource(NmeaSource): self.update_callback() def initialize(self): - if self.socket_file_path is None or \ - not os.path.exists(self.socket_file_path): - return + if (self.socket_file_path is None + or not os.path.exists(self.socket_file_path)): + raise FileNotFoundError(f"Could not open socket {self.socket_file_path}") self.s = socket.socket(socket.AF_UNIX, socket.SOCK_NONBLOCK | socket.SOCK_STREAM) From a895bfc32ff9690ccda8b1d74ff103fcf2137866 Mon Sep 17 00:00:00 2001 From: Teemu Ikonen Date: Thu, 25 May 2023 16:31:59 +0300 Subject: [PATCH 36/50] Also detect quirks when autodetecting source --- satellite/application.py | 2 +- satellite/mm_glib_source.py | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/satellite/application.py b/satellite/application.py index 8289ed5..bc06c63 100755 --- a/satellite/application.py +++ b/satellite/application.py @@ -194,7 +194,7 @@ class SatelliteApp(Gtk.Application): source_init = self.init_gnss_share_source(autodetect=True) if not source_init: source_init = self.init_mm_source( - quirks=['QuectelTalker'], autodetect=True) + quirks=['detect'], autodetect=True) if not source_init: self.log_msg('NMEA source not found') dialog = Gtk.MessageDialog( diff --git a/satellite/mm_glib_source.py b/satellite/mm_glib_source.py index ba51a4c..f34914a 100644 --- a/satellite/mm_glib_source.py +++ b/satellite/mm_glib_source.py @@ -30,7 +30,7 @@ class ModemManagerGLibNmeaSource(NmeaSource): self.old_sources_enabled = None self.old_signals_location = None self.location_updated = None - self.quirks = quirks + self.quirks = set(quirks) def initialize(self): # If reinitializing, disconnect old update cb @@ -51,6 +51,12 @@ class ModemManagerGLibNmeaSource(NmeaSource): self.model = self.modem.get_model() self.revision = self.modem.get_revision() + if 'detect' in self.quirks: + self.quirks.remove('detect') + if (self.model.startswith('QUECTEL') + and self.manufacturer == 'QUALCOMM INCORPORATED'): + self.quirks.add('QuectelTalker') + try: state = self.modem.get_state() if int(state) > 0: From 69551fa7a5489d8fea57d3be8a4b674a7566bc8b Mon Sep 17 00:00:00 2001 From: Teemu Ikonen Date: Thu, 25 May 2023 17:21:29 +0300 Subject: [PATCH 37/50] mm_glib_source: Add a quirk to disable MSB in SDM845 / OP6 --- satellite/mm_glib_source.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/satellite/mm_glib_source.py b/satellite/mm_glib_source.py index f34914a..d2e4097 100644 --- a/satellite/mm_glib_source.py +++ b/satellite/mm_glib_source.py @@ -56,6 +56,12 @@ class ModemManagerGLibNmeaSource(NmeaSource): if (self.model.startswith('QUECTEL') and self.manufacturer == 'QUALCOMM INCORPORATED'): self.quirks.add('QuectelTalker') + # Detect SDM845 GNSS unit and disable MSB assistance, + # which causes stalling at startup due to some bug somewhere + if (self.manufacturer == 'QUALCOMM INCORPORATED' + and self.model == '0' + and self.revision.find('SDM845') >= 0): + self.quirks.add('NoMSB') try: state = self.modem.get_state() @@ -71,7 +77,8 @@ class ModemManagerGLibNmeaSource(NmeaSource): raise NmeaSourceNotFoundError( "Modem does not support NMEA") enable = ModemManager.ModemLocationSource.GPS_NMEA - if caps & ModemManager.ModemLocationSource.AGPS_MSB: + if (caps & ModemManager.ModemLocationSource.AGPS_MSB + and 'NoMSB' not in self.quirks): enable |= ModemManager.ModemLocationSource.AGPS_MSB self.mlocation.setup_sync(enable, True, None) else: From 84ea03f70439588387641fc770bd1e306f2bf1a3 Mon Sep 17 00:00:00 2001 From: Teemu Ikonen Date: Fri, 26 May 2023 20:20:51 +0300 Subject: [PATCH 38/50] Release 0.4.1 --- data/appdata.xml | 9 +++++++++ satellite/__init__.py | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/data/appdata.xml b/data/appdata.xml index 5d83cd0..430ec35 100644 --- a/data/appdata.xml +++ b/data/appdata.xml @@ -49,6 +49,15 @@ + + +

The automatic release

+
    +
  • Autodetect sources and source quirks when --source option is not given
  • +
  • Some small fixes to mm_glib_source, NMEA parsing, flatpak, etc.
  • +
+
+

The managerial release

diff --git a/satellite/__init__.py b/satellite/__init__.py index 24cfe9a..7602d7c 100644 --- a/satellite/__init__.py +++ b/satellite/__init__.py @@ -1,4 +1,4 @@ # Copyright 2021-2022 Teemu Ikonen # SPDX-License-Identifier: GPL-3.0-only -__version__ = "0.4.0" +__version__ = "0.4.1" From 8f863ae0425e228d8e66b3f578b423ea49210d9e Mon Sep 17 00:00:00 2001 From: Teemu Ikonen Date: Fri, 7 Jul 2023 15:43:29 +0300 Subject: [PATCH 39/50] Update copyright years --- bin/satellite | 2 +- data/appdata.xml | 2 +- satellite/__init__.py | 2 +- satellite/__main__.py | 2 +- satellite/application.py | 4 ++-- satellite/nmea.py | 2 +- satellite/nmeasource.py | 2 +- satellite/quectel.py | 2 +- satellite/util.py | 2 +- satellite/widgets.py | 2 +- 10 files changed, 11 insertions(+), 11 deletions(-) diff --git a/bin/satellite b/bin/satellite index 9c91f9b..6446213 100755 --- a/bin/satellite +++ b/bin/satellite @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright 2021-2022 Teemu Ikonen +# Copyright 2021-2023 Teemu Ikonen # SPDX-License-Identifier: GPL-3.0-only import os diff --git a/data/appdata.xml b/data/appdata.xml index 430ec35..04614b6 100644 --- a/data/appdata.xml +++ b/data/appdata.xml @@ -1,5 +1,5 @@ - + page.codeberg.tpikonen.satellite diff --git a/satellite/__init__.py b/satellite/__init__.py index 7602d7c..2d554b9 100644 --- a/satellite/__init__.py +++ b/satellite/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2021-2022 Teemu Ikonen +# Copyright 2021-2023 Teemu Ikonen # SPDX-License-Identifier: GPL-3.0-only __version__ = "0.4.1" diff --git a/satellite/__main__.py b/satellite/__main__.py index b29c675..f956868 100644 --- a/satellite/__main__.py +++ b/satellite/__main__.py @@ -1,4 +1,4 @@ -# Copyright 2021-2022 Teemu Ikonen +# Copyright 2021-2023 Teemu Ikonen # SPDX-License-Identifier: GPL-3.0-only import sys diff --git a/satellite/application.py b/satellite/application.py index bc06c63..f2eac92 100755 --- a/satellite/application.py +++ b/satellite/application.py @@ -1,4 +1,4 @@ -# Copyright 2021-2022 Teemu Ikonen +# Copyright 2021-2023 Teemu Ikonen # SPDX-License-Identifier: GPL-3.0-only import argparse @@ -323,7 +323,7 @@ class SatelliteApp(Gtk.Application): version=__version__, comments="A program for showing navigation satellite data", license_type=Gtk.License.GPL_3_0_ONLY, - copyright="Copyright 2021-2022 Teemu Ikonen", + copyright="Copyright 2021-2023 Teemu Ikonen", ) adlg.present() diff --git a/satellite/nmea.py b/satellite/nmea.py index 5201149..b1f9a83 100644 --- a/satellite/nmea.py +++ b/satellite/nmea.py @@ -1,4 +1,4 @@ -# Copyright 2021-2022 Teemu Ikonen +# Copyright 2021-2023 Teemu Ikonen # SPDX-License-Identifier: GPL-3.0-only import datetime diff --git a/satellite/nmeasource.py b/satellite/nmeasource.py index 130e1c7..6e248d9 100644 --- a/satellite/nmeasource.py +++ b/satellite/nmeasource.py @@ -1,4 +1,4 @@ -# Copyright 2021-2022 Teemu Ikonen +# Copyright 2021-2023 Teemu Ikonen # SPDX-License-Identifier: GPL-3.0-only import os.path diff --git a/satellite/quectel.py b/satellite/quectel.py index e009832..0fcc1e4 100644 --- a/satellite/quectel.py +++ b/satellite/quectel.py @@ -1,4 +1,4 @@ -# Copyright 2021-2022 Teemu Ikonen +# Copyright 2021-2023 Teemu Ikonen # SPDX-License-Identifier: GPL-3.0-only import re diff --git a/satellite/util.py b/satellite/util.py index 02881fd..a6bb387 100644 --- a/satellite/util.py +++ b/satellite/util.py @@ -1,4 +1,4 @@ -# Copyright 2021-2022 Teemu Ikonen +# Copyright 2021-2023 Teemu Ikonen # SPDX-License-Identifier: GPL-3.0-only import os diff --git a/satellite/widgets.py b/satellite/widgets.py index 23f768c..151d855 100644 --- a/satellite/widgets.py +++ b/satellite/widgets.py @@ -1,4 +1,4 @@ -# Copyright 2021-2022 Teemu Ikonen +# Copyright 2021-2023 Teemu Ikonen # SPDX-License-Identifier: GPL-3.0-only import importlib.resources as resources From 2c5d3a7e9394c65e072b11b77e0c763ce2440efa Mon Sep 17 00:00:00 2001 From: Teemu Ikonen Date: Fri, 7 Jul 2023 15:49:28 +0300 Subject: [PATCH 40/50] nmeasource: Add manufacturer string to gnss-share source --- satellite/nmeasource.py | 1 + 1 file changed, 1 insertion(+) diff --git a/satellite/nmeasource.py b/satellite/nmeasource.py index 6e248d9..842aa7a 100644 --- a/satellite/nmeasource.py +++ b/satellite/nmeasource.py @@ -109,6 +109,7 @@ class GnssShareNmeaSource(UnixSocketNmeaSource): super().__init__(update_callback, socket_file_path='/var/run/gnss-share.sock', **kwargs) + self.manufacturer = "gnss-share" class ReplayNmeaSource(NmeaSource): From 41f4b196cc3f6784ad7240316fb4b0cb1d7f4bda Mon Sep 17 00:00:00 2001 From: Teemu Ikonen Date: Fri, 7 Jul 2023 15:50:40 +0300 Subject: [PATCH 41/50] Don't print source model if it does not exist --- satellite/application.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/satellite/application.py b/satellite/application.py index f2eac92..87090df 100755 --- a/satellite/application.py +++ b/satellite/application.py @@ -219,7 +219,8 @@ class SatelliteApp(Gtk.Application): return GLib.SOURCE_REMOVE self.log_msg( - f"Source is {self.source.manufacturer}, model {self.source.model}" + f"Source is {self.source.manufacturer}" + + (f", model {self.source.model}" if self.source.model else "") + (f", revision {self.source.revision}" if self.source.revision else "") + (f" using {', '.join(self.source.quirks)} quirks" if hasattr(self.source, "quirks") and self.source.quirks else "")) From 2a5785292f44b6305b7985ec2c5b05fe53e59a85 Mon Sep 17 00:00:00 2001 From: Teemu Ikonen Date: Fri, 7 Jul 2023 16:26:07 +0300 Subject: [PATCH 42/50] nmeasource: Keep update_cb running on UnixSocketNmeaSource --- satellite/nmeasource.py | 1 + 1 file changed, 1 insertion(+) diff --git a/satellite/nmeasource.py b/satellite/nmeasource.py index 842aa7a..73891b5 100644 --- a/satellite/nmeasource.py +++ b/satellite/nmeasource.py @@ -69,6 +69,7 @@ class UnixSocketNmeaSource(NmeaSource): def on_read_data_available(self, io_channel, condition, **unused): self.update_callback() + return True def initialize(self): if (self.socket_file_path is None From f1d04c6868e79b847cd852b55feece29496c51b8 Mon Sep 17 00:00:00 2001 From: Teemu Ikonen Date: Fri, 7 Jul 2023 16:39:08 +0300 Subject: [PATCH 43/50] Use last_mode as default GSA mode to avoid 'Lock lost' log spam --- satellite/application.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/satellite/application.py b/satellite/application.py index 87090df..439b100 100755 --- a/satellite/application.py +++ b/satellite/application.py @@ -622,7 +622,7 @@ class SatelliteApp(Gtk.Application): # log mode = data["mode"] - mode = int(mode) if mode else 0 + mode = int(mode) if mode else self.last_mode if mode != self.last_mode: if mode > 1: self.log_msg(f"Got lock, mode: {mode}") From c615a432f4fb45e0edf28d1d4ee62f6db77229bb Mon Sep 17 00:00:00 2001 From: Teemu Ikonen Date: Sun, 6 Aug 2023 17:18:25 +0300 Subject: [PATCH 44/50] Parse 'fixtime' also from GNS and GGA, remove unused 'time' from data dict --- satellite/nmea.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/satellite/nmea.py b/satellite/nmea.py index b1f9a83..bc93e14 100644 --- a/satellite/nmea.py +++ b/satellite/nmea.py @@ -99,10 +99,8 @@ getters = { 'GGA': get_altitude_gga, 'GNS': fget('altitude'), }, - "fixtime": { + "fixtime": { # Time of position report 'RMC': get_time, - }, - "time": { # Reported also when no fix 'GNS': get_time, 'GGA': get_time, }, From 85c6bfefd7f59858ba5e74fc7e79e207e972e1a1 Mon Sep 17 00:00:00 2001 From: Teemu Ikonen Date: Sat, 23 Sep 2023 12:34:22 +0300 Subject: [PATCH 45/50] nmea: Fix iget() --- satellite/nmea.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/satellite/nmea.py b/satellite/nmea.py index bc93e14..4141791 100644 --- a/satellite/nmea.py +++ b/satellite/nmea.py @@ -29,7 +29,7 @@ def fget(key, scale=1.0): def iget(key, default=None): def fn(d): try: - return int(d.get(key)) + return int(d.get(key, default)) except ValueError: return default return fn From 5d6059416e3af5bcba24bdf1db1fff8888daa623 Mon Sep 17 00:00:00 2001 From: Teemu Ikonen Date: Sat, 23 Sep 2023 13:02:56 +0300 Subject: [PATCH 46/50] nmea: Rename 'geo_sep' field to 'geoid_sep', parse it also from GGA --- satellite/nmea.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/satellite/nmea.py b/satellite/nmea.py index 4141791..91660b6 100644 --- a/satellite/nmea.py +++ b/satellite/nmea.py @@ -143,7 +143,8 @@ getters = { "vdop": { 'GSA': fget('vdop'), }, - "geo_sep": { + "geoid_sep": { + 'GGA': fget('geo_sep'), 'GNS': fget('geo_sep'), }, "sel_mode": { From a5453cf85f8105ea81a0d5c1599332772f454b7e Mon Sep 17 00:00:00 2001 From: Teemu Ikonen Date: Sat, 23 Sep 2023 13:03:38 +0300 Subject: [PATCH 47/50] Add 'Geoidal separation' field to dataframe --- satellite/application.py | 1 + 1 file changed, 1 insertion(+) diff --git a/satellite/application.py b/satellite/application.py index 439b100..187bd86 100755 --- a/satellite/application.py +++ b/satellite/application.py @@ -450,6 +450,7 @@ class SatelliteApp(Gtk.Application): ("latlon", "Latitude", lambda x: "%0.6f" % x[0] if x else "-"), ("latlon", "Longitude", lambda x: "%0.6f" % x[1] if x else "-"), ("altitude", "Altitude", lambda x: to_str(x, "%0.1f m")), + ("geoid_sep", "Geoidal separation", lambda x: to_str(x, "%0.1f m")), # ("fixtime", "Time of fix", # lambda x: x.strftime(utcfmt) if x else "-"), # ("date", "Date of fix", From 90384a0e276a5a26af59a08228a9135f90739664 Mon Sep 17 00:00:00 2001 From: Teemu Ikonen Date: Sat, 23 Sep 2023 13:27:33 +0300 Subject: [PATCH 48/50] Print DOPs (PDOP, HDOP, VDOP) on a single line --- satellite/application.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/satellite/application.py b/satellite/application.py index 187bd86..6dde3ee 100755 --- a/satellite/application.py +++ b/satellite/application.py @@ -430,6 +430,12 @@ class SatelliteApp(Gtk.Application): fixage = to_str(data.get("fixage"), "%0.0f s") return "%s / %s" % (up_age, fixage) + def get_dops(xkey): + pdop = to_str(data.get("pdop"), "%1.1f") + hdop = to_str(data.get("hdop"), "%1.1f") + vdop = to_str(data.get("vdop"), "%1.1f") + return f"{pdop} / {hdop} / {vdop}" + mode2fix = { "2": "2 D", "3": "3 D", @@ -459,9 +465,7 @@ class SatelliteApp(Gtk.Application): ("true_course", "True Course", lambda x: to_str(x, "%0.1f deg ") + (bearing_to_arrow(x) if x is not None else "")), - ("pdop", "PDOP", lambda x: to_str(x)), - ("hdop", "HDOP", lambda x: to_str(x)), - ("vdop", "VDOP", lambda x: to_str(x)), + ("pdop", "PDOP/HDOP/VDOP", get_dops), ] descs = [] vals = [] From ccaa2db75d6bb75005482a8cfdecd14e3d6621d6 Mon Sep 17 00:00:00 2001 From: Teemu Ikonen Date: Sat, 23 Sep 2023 13:54:52 +0300 Subject: [PATCH 49/50] flatpak: Update Gnome runtime to version 45 --- flatpak/page.codeberg.tpikonen.satellite.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flatpak/page.codeberg.tpikonen.satellite.yaml b/flatpak/page.codeberg.tpikonen.satellite.yaml index 6007aa5..6139184 100644 --- a/flatpak/page.codeberg.tpikonen.satellite.yaml +++ b/flatpak/page.codeberg.tpikonen.satellite.yaml @@ -1,6 +1,6 @@ app-id: page.codeberg.tpikonen.satellite runtime: org.gnome.Platform -runtime-version: "44" +runtime-version: "45" sdk: org.gnome.Sdk command: satellite rename-desktop-file: satellite.desktop From 81569df8e28db1dcad589e91b297b9cde6b54d4b Mon Sep 17 00:00:00 2001 From: Teemu Ikonen Date: Sat, 23 Sep 2023 14:31:05 +0300 Subject: [PATCH 50/50] Release 0.4.2 --- data/appdata.xml | 10 ++++++++++ satellite/__init__.py | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/data/appdata.xml b/data/appdata.xml index 04614b6..ce75e04 100644 --- a/data/appdata.xml +++ b/data/appdata.xml @@ -49,6 +49,16 @@ + + +

The geoidal release

+
    +
  • Add 'Geoidal separation' field to dataframe
  • +
  • Display DOPs (PDOP, HDOP, VDOP) on a single dataframe line
  • +
  • Various small fixes to gnss-share source, logging, NMEA parsing etc.
  • +
+
+

The automatic release

diff --git a/satellite/__init__.py b/satellite/__init__.py index 2d554b9..4f97f0a 100644 --- a/satellite/__init__.py +++ b/satellite/__init__.py @@ -1,4 +1,4 @@ # Copyright 2021-2023 Teemu Ikonen # SPDX-License-Identifier: GPL-3.0-only -__version__ = "0.4.1" +__version__ = "0.4.2"