Skip to content

feat(import): keep the tissue loading the dive computer recorded (#1977) - #1978

Open
dotanalon wants to merge 4 commits into
submersion-app:mainfrom
dotanalon:feat/import-computer-tissue-data
Open

dotanalon wants to merge 4 commits into
submersion-app:mainfrom
dotanalon:feat/import-computer-tissue-data

Conversation

@dotanalon

@dotanalon dotanalon commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

Related Issue

Closes #1977

Summary

Dive computers export their own tissue state and the app dropped all of it, showing only a Buhlmann recompute that never matched the numbers the diver saw underwater. This imports what each supported format actually records and shows the computer's value wherever one exists, falling back to the app's calculation only where the computer reported nothing.

What each format carries, verified against real files:

Source Per sample Dive level
Garmin FIT n2_load percent start and end N2 load, CNS
Shearwater Cloud DB gf99 start surface GF, end GF99, deco model, CNS
UDDF (Shearwater Cloud, Subsurface) gradientfactor (GF99) none
Suunto SML via Suunto Cloud none per-compartment N2 and He tensions at start and end (9 or 15 RGBM compartments), RGBM factors, CNS, OTU, algorithm
Ratio iX3M XML 16 compartment percentages (first and last sample stored) start and end compartment loading, CNS

No format carries per-sample per-compartment tensions except Ratio, so the 16-compartment recompute remains the source of the tissue curves and the 3D scene.

Changes

  • Profile series codec v2 adds gf99 and n2_load per-sample fields. A v1 blob still decodes with them null; an older peer stores a v2 blob and skips it until updated (existing forward-version handling), so the sync compatibility floor is unchanged.
  • New dives.computer_tissue_json column (schema 224) holding a ComputerTissueSnapshot with start and end ComputerTissueState (compartment tensions in bar or loading percent, aggregate N2 load, GF99, surface GF, CNS, OTU, RGBM factors, algorithm). Tolerant JSON decode so a corrupt column cannot break dive loading.
  • Garmin FIT: FitSample.n2Load, FitSummary.startN2/endN2, snapshot built in fitTissueSnapshot; wired through both the file import parser and the ImportedDive path.
  • Shearwater Cloud DB: reads dive_log_records.gf99 and dive_logs header values when those optional tables exist, infers the currentTime unit per dive, aligns GF99 onto the libdivecomputer samples by nearest time (ShearwaterGf99Aligner), spells out the integer decoModel code, and prefers calculated_values_from_samples.EndGF99 over a zero placeholder in dive_details.
  • UDDF: per-waypoint <gradientfactor> parsed in both readers via parseUddfGradientFactorPercent (integers as percent, fractions scaled); both UDDF writers emit it back.
  • Suunto: normalizer hoists StartTissue, EndTissue and Algorithm from the cloud DiveHeader; parseSuuntoTissue accepts all three array encodings and converts Pascal to bar; the snapshot travels on DownloadedDive through the import service into the repository, where a re-import without a snapshot never clears a stored one.
  • Ratio iX3M: tissueGroupNPercent read from the first and last sample into start and end states.
  • Analysis: overlayComputerDecoData gains gf99Source (computer-first) and replaces the GF99 curve with the computer's samples when present; ProfileAnalysis.n2LoadCurve exposed; MetricSourceInfo.gf99Actual reports what was used.
  • UI: ComputerTissueSection on the tissue card, shown only when a snapshot exists: algorithm, compartment bars or tensions in the diver's pressure unit, start versus end table, calculated value as a secondary line where both exist. New diveLog_computerTissue_* strings, l10n regenerated.
  • Docs: import gap tracker row added. All 11 locales carry the new strings; non-English ones are machine-style translations that a native speaker may want to review.

Not in this PR: storing Ratio's per-sample compartment series (needs 16 new codec channels), a chart for the N2 load curve, a GF99 source badge in the legend, and a standalone Suunto SML file parser (only the cloud path is live).

Test Plan

  • flutter test passes (full suite run locally; the only remaining failures are the known local-timezone tests, which pass under TZ=UTC, and the Linux ffmpeg smoke test's output-size assertion, both unrelated to this change)
  • flutter analyze passes
  • Manual testing on: not manually tested

Fixture-backed tests cover the real Garmin Descent trimix file (start 0, end 86, peak 191 N2 load), the Shearwater Cloud CCR export (end GF99 62), the Perdix 2 and Petrel 3 UDDF exports (GF99 on 195 of 385 samples), and the Ratio iX3M export (16 compartments). The Shearwater fixture's sample table is empty, so per-sample GF99 alignment is covered by synthetic SQLite databases only. Suunto tissue parsing is covered by unit tests built from real EON Core and HelO2 header values; no fixture in the repo carries the tissue block.

Screenshots

Not captured; the new tissue card section renders only for dives that carry an imported snapshot, and no such dive exists in the test fixtures that run in a widget harness.

@codecov

codecov Bot commented Sep 14, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@github-actions

github-actions Bot commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

📦 Build artifacts for this PR · commit f0c659e

Platform Download
Android (APK) android-apk
macOS macos-build
Windows windows-build
Linux linux-build

Artifacts expire in 7 days. Downloading requires being signed in to GitHub. macOS needs two extractions: unzip the downloaded artifact, then unzip the submersion-macos.zip inside it to get a runnable submersion.app. The build is ad-hoc signed — right-click → Open on first launch.

Updated automatically on each push.

dotanalon added a commit to dotanalon/submersion that referenced this pull request Sep 15, 2026
Patch coverage on submersion-app#1978 left 28 lines untested: the tissue card's computer section, Shearwater GF99 sample equality and deco-model name parsing, copyWith fallbacks, RGBM helium, the Drift column getter, and a successful FFI parse that aligns GF99. Also drop the unused private aligner constructor.

Signed-off-by: Alon Dotan <alon1.dotan@gmail.com>
dotanalon added a commit to dotanalon/submersion that referenced this pull request Sep 15, 2026
Drift replaces Table DSL getters with generated fields, so calling Dives().computerTissueJson throws. Exclude the line from coverage so the last codecov miss on submersion-app#1978 is gone.

Signed-off-by: Alon Dotan <alon1.dotan@gmail.com>
dotanalon added a commit to dotanalon/submersion that referenced this pull request Sep 16, 2026
Patch coverage on submersion-app#1978 left 28 lines untested: the tissue card's computer section, Shearwater GF99 sample equality and deco-model name parsing, copyWith fallbacks, RGBM helium, the Drift column getter, and a successful FFI parse that aligns GF99. Also drop the unused private aligner constructor.

Signed-off-by: Alon Dotan <alon1.dotan@gmail.com>
dotanalon added a commit to dotanalon/submersion that referenced this pull request Sep 16, 2026
Drift replaces Table DSL getters with generated fields, so calling Dives().computerTissueJson throws. Exclude the line from coverage so the last codecov miss on submersion-app#1978 is gone.

Signed-off-by: Alon Dotan <alon1.dotan@gmail.com>
@dotanalon
dotanalon force-pushed the feat/import-computer-tissue-data branch from 1e0ecae to 032c9c8 Compare September 16, 2026 05:50
dotanalon added a commit to dotanalon/submersion that referenced this pull request Sep 17, 2026
Patch coverage on submersion-app#1978 left 28 lines untested: the tissue card's computer section, Shearwater GF99 sample equality and deco-model name parsing, copyWith fallbacks, RGBM helium, the Drift column getter, and a successful FFI parse that aligns GF99. Also drop the unused private aligner constructor.

Signed-off-by: Alon Dotan <alon1.dotan@gmail.com>
dotanalon added a commit to dotanalon/submersion that referenced this pull request Sep 17, 2026
Drift replaces Table DSL getters with generated fields, so calling Dives().computerTissueJson throws. Exclude the line from coverage so the last codecov miss on submersion-app#1978 is gone.

Signed-off-by: Alon Dotan <alon1.dotan@gmail.com>
@dotanalon
dotanalon force-pushed the feat/import-computer-tissue-data branch from 032c9c8 to d97c865 Compare September 17, 2026 07:37
dotanalon added a commit to dotanalon/submersion that referenced this pull request Sep 19, 2026
Patch coverage on submersion-app#1978 left 28 lines untested: the tissue card's computer section, Shearwater GF99 sample equality and deco-model name parsing, copyWith fallbacks, RGBM helium, the Drift column getter, and a successful FFI parse that aligns GF99. Also drop the unused private aligner constructor.

Signed-off-by: Alon Dotan <alon1.dotan@gmail.com>
dotanalon added a commit to dotanalon/submersion that referenced this pull request Sep 19, 2026
Drift replaces Table DSL getters with generated fields, so calling Dives().computerTissueJson throws. Exclude the line from coverage so the last codecov miss on submersion-app#1978 is gone.

Signed-off-by: Alon Dotan <alon1.dotan@gmail.com>
@dotanalon
dotanalon force-pushed the feat/import-computer-tissue-data branch from 63a22d8 to 88ad3d4 Compare September 19, 2026 07:49
@dotanalon

Copy link
Copy Markdown
Contributor Author

@ericgriffin can we merge this?

dotanalon added a commit to dotanalon/submersion that referenced this pull request Sep 21, 2026
Patch coverage on submersion-app#1978 left 28 lines untested: the tissue card's computer section, Shearwater GF99 sample equality and deco-model name parsing, copyWith fallbacks, RGBM helium, the Drift column getter, and a successful FFI parse that aligns GF99. Also drop the unused private aligner constructor.

Signed-off-by: Alon Dotan <alon1.dotan@gmail.com>
dotanalon added a commit to dotanalon/submersion that referenced this pull request Sep 21, 2026
Drift replaces Table DSL getters with generated fields, so calling Dives().computerTissueJson throws. Exclude the line from coverage so the last codecov miss on submersion-app#1978 is gone.

Signed-off-by: Alon Dotan <alon1.dotan@gmail.com>
@dotanalon
dotanalon force-pushed the feat/import-computer-tissue-data branch from da11b6e to 9a1e0e0 Compare September 21, 2026 06:03
…mersion-app#1977)

Dive computers export their own tissue state and the app threw all of
it away, then showed a Buhlmann recompute that could never match the
numbers the diver saw underwater. Garmin FIT records an aggregate N2
load percent on every sample and start/end N2 in the dive summary,
Shearwater Cloud databases record GF99 per sample plus start surface
GF and end GF99, Shearwater and Subsurface UDDF carry a per-waypoint
gradientfactor, Suunto SML headers (also delivered by Suunto Cloud)
carry per-compartment N2 and He tensions at dive start and end with
the RGBM factors, and Ratio iX3M XML carries 16 compartment loading
percentages on every sample.

Two per-sample fields, gf99 and n2_load, are added under a new codec
v2 field table. A v1 blob still decodes with them null, and a peer on
the old codec stores a v2 blob and skips it until updated, which the
existing forward-version handling already covered, so the sync floor
is unchanged. Dive-level start and end state lands in a new JSON
column, computer_tissue_json (schema 219), as a ComputerTissueSnapshot
holding whatever the source had: compartment tensions in bar or
loading percent, aggregate N2 load, GF99, surface GF, CNS, OTU, RGBM
factors and the algorithm name. Suunto tensions are converted from
Pascal; Shearwater's decoModel integer code is spelled out; a zero
EndGF99 in dive_details is treated as the placeholder it is and the
calculated_values JSON wins.

Where the computer reported a value it is now the one shown. The
profile GF99 curve is taken from the computer's samples when any
exist, falling back to the calculated curve otherwise; this is
hardcoded computer-first because every per-metric source toggle is
its own settings column and migration. The tissue card gains a dive
computer section that renders only when a snapshot exists, with the
calculated figure as a secondary line where both are known. Nothing
imported carries per-sample per-compartment tensions except Ratio,
whose series is not stored yet, so the 16-compartment recompute stays
the source of the tissue curves and the 3D scene.

Both UDDF writers now emit gradientfactor so a backup round trip keeps
the recorded value.

Signed-off-by: Alon Dotan <alon1.dotan@gmail.com>
Patch coverage on submersion-app#1978 left 28 lines untested: the tissue card's computer section, Shearwater GF99 sample equality and deco-model name parsing, copyWith fallbacks, RGBM helium, the Drift column getter, and a successful FFI parse that aligns GF99. Also drop the unused private aligner constructor.

Signed-off-by: Alon Dotan <alon1.dotan@gmail.com>
Drift replaces Table DSL getters with generated fields, so calling Dives().computerTissueJson throws. Exclude the line from coverage so the last codecov miss on submersion-app#1978 is gone.

Signed-off-by: Alon Dotan <alon1.dotan@gmail.com>
@dotanalon
dotanalon force-pushed the feat/import-computer-tissue-data branch from 9a1e0e0 to 2ab5966 Compare September 22, 2026 05:10
Main shipped a census over every Dive constructor parameter (submersion-app#2040).
computerTissue is what the computer recorded for this diver, so a planned
dive fill takes it and a sibling mirror does not copy it.

Signed-off-by: Alon Dotan <alon1.dotan@gmail.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Backlog

Development

Successfully merging this pull request may close these issues.

Imported dives drop the tissue loading the dive computer recorded

1 participant