diff --git a/src/rivian/parallax/dynamics.py b/src/rivian/parallax/dynamics.py index 0420d32..5f374f4 100644 --- a/src/rivian/parallax/dynamics.py +++ b/src/rivian/parallax/dynamics.py @@ -93,16 +93,20 @@ def decode_gear(m: dynamics_pb2.Gear) -> dict[str, Any]: return {"gearStatus": _enum(_GEAR_MAP, m.gear, what="gear")} -# Some vehicles send the fix time as GPS time (milliseconds since the GPS -# epoch, 1980-01-06, without leap seconds) instead of Unix time. Any time -# before 2020 is taken as GPS time. +# Vehicles send the fix time in Unix milliseconds, Unix seconds, or GPS time +# (milliseconds since the GPS epoch, 1980-01-06, without leap seconds). Any +# time below 10^11 (1973 in ms, year 5138 in seconds) is taken as seconds, and +# then any time before 2020 as GPS time. _GPS_EPOCH_OFFSET_MS: Final = 315_964_800_000 _GPS_LEAP_SECONDS_MS: Final = 18_000 _GPS_TIME_CUTOFF_MS: Final = 1_577_836_800_000 # 2020-01-01 in Unix ms +_SECONDS_CUTOFF: Final = 100_000_000_000 def _gnss_unix_ms(time: int) -> int: """A GNSS fix time as Unix epoch milliseconds.""" + if 0 < time < _SECONDS_CUTOFF: + time *= 1000 if 0 < time < _GPS_TIME_CUTOFF_MS: return time + _GPS_EPOCH_OFFSET_MS - _GPS_LEAP_SECONDS_MS return time diff --git a/src/rivian/parallax/proto/dynamics.proto b/src/rivian/parallax/proto/dynamics.proto index 707d5e4..1a9731c 100644 --- a/src/rivian/parallax/proto/dynamics.proto +++ b/src/rivian/parallax/proto/dynamics.proto @@ -55,7 +55,7 @@ message Gnss { float position_vertical_error = 7; // meters float speed_error = 8; // m/s float bearing_error = 9; // degrees - int64 time = 10; // epoch ms (Unix, or GPS on some vehicles); the whole fix + int64 time = 10; // Unix ms, Unix seconds or GPS ms, by vehicle; the whole fix } // dynamics.vehicle.location diff --git a/tests/parallax/test_dynamics.py b/tests/parallax/test_dynamics.py index a88e7fa..e49034a 100644 --- a/tests/parallax/test_dynamics.py +++ b/tests/parallax/test_dynamics.py @@ -52,6 +52,15 @@ def test_gnss_gps_time() -> None: assert result["gnssTimeStamp"] == epoch(1790553428211) +def test_gnss_unix_seconds() -> None: + """A fix time in Unix seconds is converted to milliseconds.""" + result = decode( + "dynamics.vehicle.gnss", dynamics.Gnss(latitude=1.0, time=1794604023) + ) + assert result["gnssTimeStamp"] == epoch(1794604023000) + assert result["gnssLocation"]["timeStamp"] == epoch(1794604023000) + + def test_gnss_empty() -> None: """An empty payload has no fix, so nothing is reported.""" assert decode("dynamics.vehicle.gnss") == {}