Skip to content

suunto_nautic: reconnect trigger, dive time and max depth from hardware testing - #9

Merged
latishab merged 2 commits into
mainfrom
fix/nautic-reconnect-msgid-summary-maxdepth
Oct 4, 2026
Merged

latishab merged 2 commits into
mainfrom
fix/nautic-reconnect-msgid-summary-maxdepth

Conversation

@latishab

@latishab latishab commented Oct 4, 2026

Copy link
Copy Markdown
Contributor

Findings from trustthegoose's 1.17.1 hardware run on a Suunto Ocean (23 dives, fw 2.51.28) and Nautic (10 dives), deepsealabs/libdc-swift#29.

Sync trigger lost after an automatic reconnect

After a link drop mid-download, the reconnect's /Logbook/UnsynchronisedLogs subscription timed out on 2 of 3 reconnects; a fresh connection got it back.

The trigger resources aren't slot-bound: in the Suunto app captures they answer with static handles (F0 24 0D, F0 4C 00), so the /Data slot theory doesn't apply to them. What did repeat on reconnect was the message ids. Each connection restarted at 1, so the reconnect's first requests were byte-identical to the dropped connection's first requests; the Suunto app's ids carry on across connections (its captures start at 0x00BA, 0x00C8, 0x180C, 0x18D9). If the watch keeps a dropped link's session and drops a repeated request as a retransmit, the reconnect's first GET goes unanswered. That fits the pattern: both failures followed short sessions (the repeated requests were still recent), the one success followed a long one. Not confirmed on a watch; the fix is harmless if the model is wrong.

  • Message ids carry on from the previous connection (clock-seeded on the first open in a process).
  • suunto_nautic_device_unsubscribe releases a per-id slot handle (static handles are still skipped, as the app does).
  • A GET answered with the 10 slot releases the stale 00 slot: the driver releases every handle before its next GET, so a bound 00 slot is left over from a dropped link.

Dive time and max depth

  • Dive time is truncated, not rounded (4680.5 s is 78:00 in the app; we showed 78:01).
  • The /Summary dive record carries the app's DiveTime and Depth.Max (float32 at payload offsets 321 and 330, the same on 2040/2056/2342 B summaries). Used when its dive time is within 10% (min 30 s) of the profile's and the depth within -0.5/+3 m of the sampled maximum. All 13 corpus dives with a summary and their own app JSON now match the app exactly (e.g. 33.12 m vs the sampled 33.11 m; 10.59 vs 10.43 m).

Tests live in libdc-swift (fake watch + capture corpus), in the PR that bumps this submodule.

🤖 Generated with Claude Code

latishab and others added 2 commits October 5, 2026 02:44
…nd stale slots

After a link drop in the middle of a download, the reconnect's
/Logbook/UnsynchronisedLogs subscription timed out on two of three
hardware reconnects (deepsealabs/libdc-swift#29), while a fresh
connection got it back.

The trigger resources answer with static F0 handles in the Suunto app
captures (F0 24 0D, F0 4C 00), so they don't use the per-id slots. What
the reconnect repeated was the message ids: every connection restarted
at 1, so its first requests were byte-identical to the dropped
connection's first requests. The Suunto app's ids carry on across
connections. If the watch keeps the dropped link's session and treats a
repeat as a retransmit, the reconnect's first GET goes unanswered, which
matches the two failures (a short previous session) and the success
(a long one).

- Message ids carry on from the previous connection (seeded from the
  clock on the first open in a process).
- suunto_nautic_device_unsubscribe releases the handle when it is a
  per-id slot; static handles are still skipped.
- A GET answered with the 10 slot releases the 00 slot: every handle is
  released before the next GET, so a bound 00 slot was left by a link
  that dropped mid-transfer.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Hardware comparison against the Suunto app (deepsealabs/libdc-swift#29):
dive time came out 1 s long (4680.5 s rounded to 78:01, the app shows
78:00) and max depth 0.2-0.3 m shallow (the deepest logged sample; the
watch's own maximum falls between samples).

- Dive time is truncated instead of rounded.
- The /Summary's dive record carries the app's DiveTime and Depth.Max
  as float32 at payload offsets 321 and 330, on 2040, 2056 and 2342 B
  summaries (13 corpus dives match the app JSON exactly). Taken from
  the last record long enough to hold them, and only when its dive time
  is within 10% (30 s minimum) of the profile's and the depth within
  -0.5/+3 m of the sampled maximum; otherwise the profile values stay.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@latishab
latishab merged commit 3cd4cfd into main Oct 4, 2026
18 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant