from ais_hub.core.stats import Stats from ais_hub.parser.gps import GpsParser def _withcs(body: str) -> str: cs = 0 for ch in body.encode("ascii"): cs ^= ch return f"${body}*{cs:02X}" def test_rmc_produces_lat_lon_sog_cog(): p = GpsParser(stats=Stats()) line = _withcs("GPRMC,123519,A,4807.038,N,01131.000,E,22.4,84.4,230394,3.1,W") fix = p.feed("gps_uart", line) assert fix is not None assert abs(fix.lat - (48 + 7.038 / 60)) < 1e-6 assert abs(fix.lon - (11 + 31.0 / 60)) < 1e-6 assert fix.sog == 22.4 assert fix.cog == 84.4 def test_gga_supplies_fix_quality_and_hdop(): p = GpsParser(stats=Stats()) line = _withcs("GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,") fix = p.feed("gps_uart", line) assert fix is not None assert fix.fix_quality == 1 assert fix.sats == 8 assert fix.hdop == 0.9 assert fix.alt == 545.4 def test_vtg_speed_from_kmh_when_knots_missing(): p = GpsParser(stats=Stats()) line = _withcs("GPVTG,054.7,T,034.4,M,,,010.0,K,A") fix = p.feed("gps_uart", line) assert fix is not None assert fix.cog == 54.7 # 10 km/h ~ 5.3996 knots assert fix.sog is not None and abs(fix.sog - 5.3996) < 1e-3 def test_bad_checksum_rejected(): p = GpsParser(stats=Stats()) fix = p.feed("gps_uart", "$GPRMC,,,,,,,,,,,,*FF") assert fix is None