Skip to content

garmin: report per-tank gas-consumption usage via DC_FIELD_TANK - #145

Merged
mikeller merged 2 commits into
subsurface:Subsurface-DS9from
mikeller:feat/garmin-tank-gas-rate-usage-204
Oct 15, 2026
Merged

mikeller merged 2 commits into
subsurface:Subsurface-DS9from
mikeller:feat/garmin-tank-gas-rate-usage-204

Conversation

@mikeller

@mikeller mikeller commented Oct 13, 2026 •

Copy link
Copy Markdown
Member

Garmin FIT logs can mark each tank-pod sensor's SENSOR_PROFILE field
78 ("used for SAC calculations?") to say whether the dive computer
counts that tank toward the diver's own gas consumption. The parser
decoded the field into garmin_sensor.sensor_used_for_gas_rate but
never exposed it, and DC_FIELD_TANK was not populated for Garmin at
all.

The field's meaning is confirmed by Garmin's own Descent T1/T2
manuals (the transceiver "SAC/RMV/ATR" setting) and independently by
the public FIT file attached to subsurface#4465: field 78 is 1 on the
reporter's own tank and 0 on a second, watched sensor that carries a
different user-assigned transmitter name, consistent with both
pressure traces. This is a single dive on Mk2i/Mk3i-era firmware and
has not been verified across other Garmin dive computer models.

Add DC_TANK_USAGE_INCLUDED/DC_TANK_USAGE_EXCLUDED to dc_tank_usage_t,
documented in terms of gas consumption rather than ownership, with
DC_TANK_USAGE_NONE kept as the 0/default value.

Storage has to be tri-state: the field-decoding machinery drops
absent/invalid (0xff) values before the handler runs, and the sensor
struct is memset to zero per dive, so a raw 0 is ambiguous between
"device said not counted" and "device said nothing". Track a
separate sensor_used_for_gas_rate_valid flag so a log that omits
field 78 reports DC_TANK_USAGE_NONE, never EXCLUDED. The per-tank
index matches the one already used for DC_SAMPLE_PRESSURE (both walk
garmin->dive.sensor[]). Volume/type/workpressure are left at their
defaults; populating them from TANK_SUMMARY is a separate concern.

Also make field-cache.c's generic DC_FIELD_TANK case set usage from
the cache and explicitly default beginpressure/endpressure to 0, so a
cache-backed backend can never return stale caller memory for those
members. No other backend currently reaches this generic path for
DC_FIELD_TANK without first overriding usage/pressures itself, so
this does not change their behaviour.

Adds DC_TANK_USAGE_INCLUDED and DC_TANK_USAGE_EXCLUDED for Garmin tank usage reporting and cache-backed tank fields. XML output now maps tank usage values to none, sidemount, included, and excluded.

Garmin FIT logs can mark each tank-pod sensor's SENSOR_PROFILE field
78 ("used for SAC calculations?") to say whether the dive computer
counts that tank toward the diver's own gas consumption. The parser
decoded the field into garmin_sensor.sensor_used_for_gas_rate but
never exposed it, and DC_FIELD_TANK was not populated for Garmin at
all.

The field's meaning is confirmed by Garmin's own Descent T1/T2
manuals (the transceiver "SAC/RMV/ATR" setting) and independently by
the public FIT file attached to subsurface#4465: field 78 is 1 on the
reporter's own tank and 0 on a second, watched sensor that carries a
different user-assigned transmitter name, consistent with both
pressure traces. This is a single dive on Mk2i/Mk3i-era firmware and
has not been verified across other Garmin dive computer models.

Add DC_TANK_USAGE_INCLUDED/DC_TANK_USAGE_EXCLUDED to dc_tank_usage_t,
documented in terms of gas consumption rather than ownership, with
DC_TANK_USAGE_NONE kept as the 0/default value.

Storage has to be tri-state: the field-decoding machinery drops
absent/invalid (0xff) values before the handler runs, and the sensor
struct is memset to zero per dive, so a raw 0 is ambiguous between
"device said not counted" and "device said nothing". Track a
separate sensor_used_for_gas_rate_valid flag so a log that omits
field 78 reports DC_TANK_USAGE_NONE, never EXCLUDED. The per-tank
index matches the one already used for DC_SAMPLE_PRESSURE (both walk
garmin->dive.sensor[]). Volume/type/workpressure are left at their
defaults; populating them from TANK_SUMMARY is a separate concern.

Also make field-cache.c's generic DC_FIELD_TANK case set usage from
the cache and explicitly default beginpressure/endpressure to 0, so a
cache-backed backend can never return stale caller memory for those
members. No other backend currently reaches this generic path for
DC_FIELD_TANK without first overriding usage/pressures itself, so
this does not change their behaviour.

Signed-off-by: Michael Keller <github@ike.ch>
Copilot AI lite review requested due to automatic review settings October 13, 2026 12:12

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Fix incorrect gasmix reporting, phantom tank exposure, and serialization of the new enum values.

1 open finding

🧠 Review effort: Lite

Comment on lines +235 to +239
DC_TANK_USAGE_INCLUDED, /* The dive computer counts this tank towards
the diver's own gas consumption. */
DC_TANK_USAGE_EXCLUDED, /* The dive computer does not count this tank
towards the diver's own gas consumption
(for example a buddy's or a stage/deco

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, thank you. examples/output_xml.c's tank-reporting block prints tank.usage (a dc_tank_usage_t) through the array built for the unrelated dc_usage_t gas-mix mapping, {"none", "oxygen", "diluent", "sidemount"}, so DC_TANK_USAGE_INCLUDED would print as "diluent" and DC_TANK_USAGE_EXCLUDED as "sidemount".

This will be fixed by giving the tank-reporting block its own mapping, {"none", "sidemount", "included", "excluded"}, matching dc_tank_usage_t's actual enumerator order, without touching the separate gas-mix block.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This has been fixed in commit 590d585 ("examples: fix tank usage XML labels to match dc_tank_usage_t"), which is already part of this PR. The tank-reporting block now has its own usage[] array, {"none", "sidemount", "included", "excluded"}, matching dc_tank_usage_t's actual enumerator order, and no longer reuses the unrelated gas-mix dc_usage_t mapping.

The tank-reporting block in output_xml.c reused the gas-mix usage[]
array ({"none", "oxygen", "diluent", "sidemount"}), which maps
dc_usage_t (NONE/OXYGEN/DILUENT/OPEN_CIRCUIT). dc_tank_usage_t has a
different enumerator order (NONE/SIDEMOUNT/INCLUDED/EXCLUDED), so a
tank with usage DC_TANK_USAGE_SIDEMOUNT was printed as <usage>oxygen</usage>,
and DC_TANK_USAGE_INCLUDED/EXCLUDED had no matching label at all.

Add a tank-specific usage[] array distinct from the gas-mix one, so
the XML output names tank usage correctly. The gas-mix block's own
dc_usage_t mapping is unchanged.

Signed-off-by: Michael Keller <github@ike.ch>
Copilot AI lite review requested due to automatic review settings October 13, 2026 17:43

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Garmin may expose non-existent tank indices when gas-mix counts exceed sensor counts; the tank bounds need correction.

1 open finding

🧠 Review effort: Lite

@mikeller
mikeller marked this pull request as ready for review October 14, 2026 00:53
@coderabbitai

coderabbitai Bot commented Oct 14, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: 347ed848-9e2d-4712-a4ae-63d7df169f84
📥 Commits

Reviewing files that changed from the base of the PR and between 8d3f629 and 590d585.

📒 Files selected for processing: 4
  • examples/output_xml.c
  • include/libdivecomputer/parser.h
  • src/field-cache.c
  • src/garmin_parser.c

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 9 remain after this review.

📝 Walkthrough

The change adds included and excluded tank usage values, maps Garmin sensor data to tank usage, and returns and serializes the resulting values.

Changes: Tank usage

Layer / File(s) Summary
Define and map tank usage
include/libdivecomputer/parser.h, src/garmin_parser.c
The enum adds included and excluded values. Garmin parsing distinguishes an absent used_for_gas_rate field from a decoded zero and maps sensor values to tank usage.
Return and serialize tank usage
src/field-cache.c, examples/output_xml.c
Tank field lookup copies cached usage and sets begin and end pressures to zero. XML output uses the labels none, sidemount, included, and excluded.

Priority: ⬇️ Low

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Feature

Merge Risk: ⚪ Minimal · up to 590d5

Tank-use classifications flow through the cache and XML output with matching indices and labels. No concrete failure affecting the changed behavior is established, so the change appears ready to merge.

Security architecture risk: 🔵 Low · up to 590d5

The change preserves existing enum values and does not introduce a privileged operation. However, sensor-record state can carry into a later tank and produce a classification that the tank itself did not specify. The demonstrated impact is incorrect metadata; occurrence in real Garmin logs remains unverified.

Retained concerns

  • Low · architecture · inferred: A non-tank SENSOR_PROFILE carrying field 78 writes into the same slot subsequently used by a tank pod. If that tank omits field 78 or supplies its invalid value, the inherited validity and value cause INCLUDED or EXCLUDED to be published instead of NONE. Per-dive reset does not provide per-record isolation. This newly exposes another sensor's state through the public tank contract; occurrence in device-produced logs and downstream security impact remain unverified.
Security review details

Security Blast Radius

  • inferred — A crafted mixed-sensor log can affect the reported usage of a subsequent tank within the parsed dive. The demonstrated outcome is metadata misattribution through the field API and XML output, not cross-tenant access, credential exposure, or increased privilege.

Trust Boundaries and Controls

  • observed — The sensor counter reserves a scratch slot and caps counted tanks below MAX_SENSORS. The field getter checks cache initialization and rejects indices at or above MAXGASES. These bounds limit memory access but do not establish that cached usage originated in the corresponding tank record.

Resilience and Maintainability Implications

  • inferred — Per-dive reset prevents initialization-time carryover between dives, and a tank's own valid field 78 overwrites inherited state. Neither control prevents within-dive misattribution when a later tank's field is absent or invalid.
Pre-merge checks: Passed 4 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 3 functions across 4 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 4 passed
Check name Status Explanation
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check Passed The title clearly and concisely describes the main change: reporting per-tank gas-consumption usage for Garmin data through DC_FIELD_TANK.
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
  • Fix all pre-merge checks with AI
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Comment @coderabbitai help to get the list of available commands.

@mikeller
mikeller merged commit 7800671 into subsurface:Subsurface-DS9 Oct 15, 2026
12 checks passed
@mikeller
mikeller deleted the feat/garmin-tank-gas-rate-usage-204 branch October 15, 2026 10:14
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.

2 participants