You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
On a Shearwater CCR dive with two AI transmitters (diluent + O2), the Bluetooth import assigns wrong gas mixes, wrong roles and a wrong diluent end pressure. All of it traces back to one assumption in parsed_tank_resolver.dart: that a profile sample carries at most one transmitter reading. In practice both transmitters report in the same sample, and ProfileSample.pressureBar / tankIndex only hold the last one reported (always the O2 transmitter on the Petrel 3), even though every transmitter's reading is already available in ProfileSample.tankPressuresBar (#1316).
Setup
Shearwater Petrel 3, CCR mode, log version 17, AI mode 5
Transmitter T1 named D1 (diluent), T2 named O2 (oxygen)
Transmitter registry entries assign role Diluent / O2 Supply
Enabled gases, e.g. OC1 99/0, OC5 15/55 (bailout), DIL2 15/55; the whole dive is on the loop
What happens
Decoded from the raw download of eight real dives and cross-checked against the stored cylinders and Shearwater Cloud:
Expected
Imported
O2 cylinder
100/0
the diluent mix, on every dive
Diluent cylinder
the breathed diluent
the breathed diluent only if the O2 transmitter missed at least one sample; otherwise the first gas in the list (OC1 99/0)
Bailout gas (no transmitter)
Bailout
Back Gas
Diluent end pressure
reading at surfacing
last reading of the post-surfacing tail (e.g. 13.4 bar instead of 77.6 bar)
Per-sample "last reporter" counts show how fragile the diluent result is: across the analyzed dives the diluent transmitter was the last reporter in 0 to 23 of 289 to 805 samples. One dive got the right diluent from a single sample; the dive with zero such samples imported the diluent cylinder as 99/0.
Root causes
Gas per transmitter (_resolveTankGasIndex, rule 1): the cylinder gets the gas that was active while its transmitter was the sample's tankIndex. On CCR the active gas is always the diluent, and the O2 transmitter owns almost every sample, so the O2 cylinder gets the diluent. The oxygen default added in fix(dive-log): auto-assign 100% O2 when a cylinder's role is set to oxygen supply #1972 only runs after rule 1 found nothing, so it never applies here (its test builds the dive without samples). The diluent transmitter rarely owns a sample and falls through to rule 3 (return 0), the first gas mix, which on Shearwater is OC1.
Shearwater Cloud's own TankProfileData also labels every transmitter with the breathed gas (including O2), so the tank role has to come from usage / the transmitter name, not from the gas timeline.
Suggested fix
On a CCR dive, resolve a transmitter tank tagged DC_USAGE_OXYGEN to 100/0 (or an oxygen-tagged gas mix) before rule 1.
Resolve a transmitter tank tagged DC_USAGE_DILUENT to the breathed gas mix with usage == 2, never to gas index 0.
Attribute gas per transmitter and build surfacing points from tankPressuresBar (all transmitters per sample) instead of pressureBar / tankIndex.
Apply the sensorless role logic (usage, CCR bailout ranking) to gases without a transmitter as well.
Regression test with realistic samples: both transmitters in each sample, O2 reported last, OC1 99/0 as the first gas mix.
Summary
On a Shearwater CCR dive with two AI transmitters (diluent + O2), the Bluetooth import assigns wrong gas mixes, wrong roles and a wrong diluent end pressure. All of it traces back to one assumption in
parsed_tank_resolver.dart: that a profile sample carries at most one transmitter reading. In practice both transmitters report in the same sample, andProfileSample.pressureBar/tankIndexonly hold the last one reported (always the O2 transmitter on the Petrel 3), even though every transmitter's reading is already available inProfileSample.tankPressuresBar(#1316).Setup
D1(diluent), T2 namedO2(oxygen)What happens
Decoded from the raw download of eight real dives and cross-checked against the stored cylinders and Shearwater Cloud:
Per-sample "last reporter" counts show how fragile the diluent result is: across the analyzed dives the diluent transmitter was the last reporter in 0 to 23 of 289 to 805 samples. One dive got the right diluent from a single sample; the dive with zero such samples imported the diluent cylinder as 99/0.
Root causes
_resolveTankGasIndex, rule 1): the cylinder gets the gas that was active while its transmitter was the sample'stankIndex. On CCR the active gas is always the diluent, and the O2 transmitter owns almost every sample, so the O2 cylinder gets the diluent. The oxygen default added in fix(dive-log): auto-assign 100% O2 when a cylinder's role is set to oxygen supply #1972 only runs after rule 1 found nothing, so it never applies here (its test builds the dive without samples). The diluent transmitter rarely owns a sample and falls through to rule 3 (return 0), the first gas mix, which on Shearwater is OC1._inferRole(null, ...), which ignoresGasMix.usageand the CCR bailout ranking from feat(dive-computer): rank sensorless CCR bailout gases against each other #1752 (scoped to sensorless dives only)._surfacingPoints()builds one entry per sample frompressureBar/tankIndex, so only the O2 transmitter gets a reading at surfacing. The diluent keeps its untrimmed tail value. The trim itself is correct and needed; it just never sees the diluent.Shearwater Cloud's own
TankProfileDataalso labels every transmitter with the breathed gas (including O2), so the tank role has to come fromusage/ the transmitter name, not from the gas timeline.Suggested fix
DC_USAGE_OXYGENto 100/0 (or an oxygen-tagged gas mix) before rule 1.DC_USAGE_DILUENTto the breathed gas mix withusage == 2, never to gas index 0.tankPressuresBar(all transmitters per sample) instead ofpressureBar/tankIndex.usage, CCR bailout ranking) to gases without a transmitter as well.Related
#1092, #1290, #1316, #1752, #1879, #1972, #2034, #2224