Skip to content

Update libdivecomputer from Upstream. #83

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged

Conversation

mikeller
Copy link
Member

@mikeller mikeller commented Jun 8, 2025

Included in this pull request:

  • Wait longer before retrying and ignore invalid bytes
  • Add support for the Halcyon Symbios
  • Remove some unused variables
  • Use the GCC unused attribute to silence warnings
  • Remove the duplicated C_ARRAY_SIZE macros
  • Replace the dc_descriptor_iterator function
  • Mark the descriptor parameter as const
  • Check the descriptor transport in the filter
  • Remove the rfcomm filter function
  • Select a bluetooth enabled model as the default
  • Verify the checksum in the dive header
  • Read the model number for diagnostics purposes
  • Add support for the Puck Lite
  • Limit the size of the fingerprint to 4 bytes
  • Include the fingerprint data in the logs
  • Ignore zero pressure values
  • Add support for the average depth
  • Fix the decoding of the temperature
  • Fix the active gasmix detection
  • Use a layout descriptor with a generic decode function
  • Read the payload and trailer byte separately
  • Add support for the Cressi Archimede
  • Add support for time synchronization
  • Add extra debug information to the log
  • Add support for a new Sirius header version
  • Fix the name of the include guard
  • Fix the ppO2 scaling factor
  • Link the tank to the corresponding gas mix
  • Add support for new record types
  • Update the filter function
  • Change the order of the functions
  • Rename the prefix functions
  • Use a case-insensitive comparision
  • Add support for a new Cressi Nepto firmware
  • Fix the Cressi Nepto filter function
  • Always initialize all fields to zero
  • Fix the deco stop depth scaling factor
  • Report the device model number
  • Detect NAK response packets
  • Ignore invalid start bytes
  • Add support for the Seac Tablet

Tested with:

  • Heinrichs Weikamp OSTC 4 & 5
  • Shearwater Petrel 2
  • Garmin Descent Mk1

jefdriesen and others added 30 commits June 8, 2025 15:25
When an error occurs, simply re-sending the failed command doesn't
always work. Sometimes we appear to receive some stale data from the
previous attempt, despite the fact that the input queue is purged before
retrying.

To improve the chances of re-synchronizing on the start of the next
packet, wait a bit longer before re-trying and also ignore all received
bytes until an ACK byte is encountered. This isn't entirely reliable,
because the payload data isn't escaped and can also contain ACK bytes,
but it's better than nothing.

Below is a real-life example of such a case.

First, we hit a timeout:

  [8.122494] INFO: Write: size=2, data=E742
  [8.122663] INFO: Write: size=8, data=708F020080000000
  [11.130287] INFO: Read: size=0, data=
  [11.130308] ERROR: Failed to receive the answer. [in src/mares_iconhd.c:213 (mares_iconhd_packet)]

Next, we retry a few times:

  [11.130321] INFO: Sleep: value=100
  [11.230536] INFO: Purge: direction=1
  [11.231117] INFO: Write: size=2, data=E742
  [11.240494] INFO: Write: size=8, data=708F020080000000
  [11.415317] INFO: Read: size=20, data=0000000035004F000C00000037004F0000000000
  [11.415331] ERROR: Unexpected answer byte. [in src/mares_iconhd.c:219 (mares_iconhd_packet)]
  [11.415339] INFO: Sleep: value=100
  [11.515889] INFO: Purge: direction=1
  [11.517474] INFO: Write: size=2, data=E742
  [11.517628] INFO: Write: size=8, data=708F020080000000
  [11.517798] INFO: Read: size=13, data=1300000036004F000A00000037
  [11.517803] ERROR: Unexpected answer byte. [in src/mares_iconhd.c:219 (mares_iconhd_packet)]

That didn't really work, but the interesting part is that we did receive
some data now. We also see the same data appear in the next retry:

  [11.517811] INFO: Sleep: value=100
  [11.618398] INFO: Purge: direction=1
  [11.618760] INFO: Write: size=2, data=E742
  [11.619100] INFO: Write: size=8, data=708F020080000000
  [11.619341] INFO: Read: size=1, data=AA
  [11.619349] INFO: Read: size=20, data=0000000035004F000C00000037004F0000000000
  [11.619355] INFO: Read: size=20, data=37004F001E00000036004F001000000037004F00
  [11.619360] INFO: Read: size=20, data=2C00000037004F001200000034004F0033000000
  [11.619366] INFO: Read: size=20, data=37004F000000000037004F001E00000036004F00
  [11.630104] INFO: Read: size=12, data=1300000036004F000A000000
  [11.630122] INFO: Read: size=20, data=37004F002000000038004F001D00000038004F00
  [11.630131] INFO: Read: size=17, data=2300000039004F000600000037004F00EA

This time, the retry seems to have worked and we move on to the next
request:

  [11.635160] INFO: Write: size=2, data=E742
  [11.635359] INFO: Write: size=8, data=F08E020080000000
  [11.687620] INFO: Read: size=1, data=AA
  [11.708476] INFO: Read: size=20, data=0000000035004F000C00000037004F0000000000
  [11.719214] INFO: Read: size=20, data=37004F001E00000036004F001000000037004F00
  [11.719231] INFO: Read: size=20, data=2C00000037004F001200000034004F0033000000
  [11.719240] INFO: Read: size=20, data=37004F000000000037004F001E00000036004F00
  [11.740569] INFO: Read: size=13, data=1300000036004F000A00000037
  [11.740585] INFO: Read: size=20, data=004F002000000038004F001D00000038004F0023
  [11.740592] INFO: Read: size=20, data=00000039004F000600000037004F00EAAA090010

This appears to have worked, but upon closer inspection you can see that
the very last packet already contains the start of the response to the
next request (AA090010). But we haven't send the next request yet!

That means the response we received here is actually the response to one
of the earlier (failed) request and this start of the next packet is our
real response. So we're getting completely out of sync! Unfortunately
the Mares protocol doesn't use checksums or sequence numbers, so we
can't really detect this problem.
The Halcyon Symbios HUD and Handset support both BLE communication and
USB mass storage mode. In both cases the dives have the same data
format.

Tested-by: Benjamin Kuch <[email protected]>
Mark some variable with the GCC 'unused' attribute to silence (harmless)
compiler warnings (-Wunused-variable and -Wunused-but-set-variable). The
variables are kept because they are valuable as documentation or for
debugging purposes.
Use the common macro defined in the array.h header instead of
duplicating the same macro. The C_ARRAY_ITEMSIZE macro is also moved to
the common header.
The existing dc_descriptor_iterator function is missing a context
parameter, and also doesn't follow the standard naming convention. Fixed
by adding a new dc_descriptor_iterator_new function.

For backwards compatibility, the old function remains present in the
library and a macro is used to automatically redirect to the new
function.
The descriptor functions do not modify the passed descriptor. This is
now made explicit by marking the parameter as const.
The filter function should take into account the transports supported by
the device descriptor. If a transport isn't supported, the filter
function shouldn't indicate a match.
The rfcomm filter function is not only platform dependent (Linux only),
but also difficult to use correctly. It should only be enabled for
devices which support DC_TRANSPORT_SERIAL due to the serial port
emulation layer of the bluetooth Serial Port Profile (SPP/rfcomm), and
not for usb-serial communication. However, some dive computers models
like the OSTC3 have both rfcomm and usb-serial enabled variants. Since
the filter functions are shared by all models, the result isn't always
reliable.

Fixed by simply removing the rfcomm filter. For serial communication,
every string will now be considered a match, which is the same result as
with no filter function present.
Using a bluetooth enabled model (e.g the OSTC 2) as the default choice
is very convenient when using only the --family command-line option to
scan for bluetooth devices or establish a connection. That's because
after commit ce2c3c6, the filter will
no longer result in a match for the non-bluetooth enabled models.
The checksum can help to detect corrupt dives and/or bugs in the
download code.
The model number isn't strictly needed anymore, because libdivecomputer
uses the information in the version packet to the detect the dive
computer model. However, since the internal lookup table is still based
on the model number, having easy access to the model number in the debug
logs is very convenient when adding support for new devices.
The Puck Lite re-uses the same model number as the Puck 4. Only the
bluetooth device name appears to be different.
For the Sirius (and compatible models), the data/time information is
only 4 bytes large instead of the default 10 bytes.
The fingerprint data is valuable information when trying to reproduce
some issues reported by users.
Occasionally there are a few samples with a zero pressure value at the
begin and/or the end of a dive. Those values result in an incorrect
begin/end pressure. Fixed by ignoring the zero pressure values.
The lower nibble at offset 0x0C contains part of the dive time and only
the higher nibble contains part of the temperature. Since this extra
nibble ended up as the second decimal figure, the error is actually very
small and nobody really noticed the mistake.
The Cressi Edy appears to set the disabled gas mixes to 0xFF. The check
for the magic value 0xF0 doesn't catch this case and therefore the value
0xFF results in a gas mix with 165% O2, which is obviously invalid.

Fixed by checking the high nibble only.
Replace hardcoded constants with a layout descriptor. This reduces the
amount of model specific conditions, and makes it easier to add support
for new models.

The Cressi data format uses nibbles (4 bits) as the smallest unit of
storage. Larger values are stored as one or more nibble, typically with
a BCD (binary coded decimal) encoding applied. To decode such variable
sized numbers, a generic function is added. The offsets in the layout
descriptor are also specified in nibbles instead of bytes.
Reading the payload and the trailer byte separately simplifies the code
in the caller because the destination buffer doesn't need space to store
the extra trailer byte.
The Cressi Archimede protocol is very similar to the existing Edy
protocol, with a few smaller differences:

 - No init3 command and baudrate change
 - Smaller packet size (32 instead of 128 bytes)
 - Modified read command with a 1 byte address

The most curious change is the fact that the nibbles of all bytes are
reversed (little endian). To simplify the processing of the data, the
nibbles are swapped again immediately after downloading.
The Suunto d9/vyper2 protocol supports a command to synchronize the
clock.
The Mares Sirius firmare upgrade to v1.6.16 also changed the header
version to v2.0. Again, there appear to be no functional changes in the
data format.

See also commit 82712ce.
The ppO2 values are stored with a unit of 0.01 bar and not 0.1 bar.
The ID_GAS_TRANSMITTER record reports a tank pressure from a transmitter
linked to a gas mix. Use this information to link the tank to the
corresponding gas mix.
Log format version 1.6 introduces some new record types. The
ID_TANK_TRANSMITTER record reports a tank pressure from a transmitter
not linked to a gas mix. Unlike the previous ID_GAS_TRANSMITTER record,
the tank id is no longer the transmitter serial number, but a
transmitter index (0-3). To avoid conflicts and being able to
distinguish the two types, one of the higher bit is set internally.
Halcyon changed the bluetooth device name to the full serial number
without a prefix. The serial number uses the format YYMMPCNNNN (e.g.
2412070123) with:

  - YY = Production year
  - MM = Production month
  - PC = Product code (01 = HUD, 07 = handset)
  - NNNN = Continuous number

The existing filter with the prefix is kept for backwards compatibility.
jefdriesen added 11 commits June 8, 2025 15:29
The USB matching functions have been moved to the top so that all the
string based matching functions are grouped together.
The prefix based matching functions have been renamed so that their
function names share the same prefix.
The other prefix matching function already use a case-insensitive
comparision. There is no reason for a different behaviour.
The Cressi Nepto with firmware v209 uses format version 4. Since the
exact range isn't known, assume all firmware versions between 200 and
299 use the same format.
The model number in the bluetooth device name appears to be encoded as a
hexadecimal number instead of a decimal number (e.g. a_xxxx vs 10_xxxx
for the Nepto).
The decompression stop depth values are stored with a unit of
centimeters and not 1 meter.
The hardware info contains a 32 bit model number that can be used to
distinguish the different models. There is also a serial number prefix
(at offset 8) which contains a single letter that serves the same
purpose:

  A = Action
  B = Screen
  E = Tablet
Errors are reported by the dive computer with a NAK packet containing an
error code. Detect and handle such packets and log the error code.
Ignoring all received bytes until an START bytes is encountered makes
the re-synchronization on the start of the packet a bit more robust.
The Seac Tablet uses almost the same protocol as the Seac Screen and
Action, but with a few small differences:

 - A slightly different set of commands, but with exactly the same
   functionality.
 - The new variant of the READ command does no longer pad the response
   packet to 2048 bytes.
 - The profile area starts at address 0x0A0000 instead of 0x010000.

The Seac Tablet also support tank pressure transmitters.
@mikeller mikeller requested a review from Copilot June 8, 2025 06:17
Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR updates libdivecomputer with multiple upstream changes including new support for devices, improvements to function signatures, and documentation updates. Key changes include:

  • Replacing the old dc_descriptor_iterator function with dc_descriptor_iterator_new and marking related parameters as const.
  • Adding support for the Halcyon Symbios and updating hardware constants.
  • Updating documentation and build files to reference the new function name and added files.

Reviewed Changes

Copilot reviewed 74 out of 74 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
include/libdivecomputer/descriptor.h Updated iterator function signature and maintained backward compatibility via a macro.
include/libdivecomputer/common.h Added new device family constant for the Halcyon Symbios.
examples/dctool_list.c Adjusted the iterator function call to use the new API.
examples/common.c Updated hardware constant for "ostc3".
doc/man/* Updated man pages to reference dc_descriptor_iterator_new consistently.
contrib/msvc & contrib/android Added new source and header files for Halcyon Symbios support.
Comments suppressed due to low confidence (1)

examples/common.c:82

  • The change of the hex constant for 'ostc3' from 0x0A to 0x11 should be verified against hardware specifications to ensure it is intentional.
{"ostc3",       DC_FAMILY_HW_OSTC3,            0x11},

dc_descriptor_iterator (dc_iterator_t **iterator);
dc_descriptor_iterator_new (dc_iterator_t **iterator, dc_context_t *context);

/* For backwards compatibility */
Copy link
Preview

Copilot AI Jun 8, 2025

Choose a reason for hiding this comment

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

The backward compatibility macro may obscure the behavior of the updated function. Consider adding a comment to clarify its intended use or reevaluate its necessity.

Suggested change
/* For backwards compatibility */
/**
* Backward compatibility macro for creating an iterator to enumerate
* supported dive computers. This macro is a shorthand for calling
* dc_descriptor_iterator_new with the second parameter set to NULL.
*
* @param[out] iterator A location to store the iterator.
* @note This macro is intended for legacy code and assumes that the
* context parameter is not required. For new code, it is
* recommended to use dc_descriptor_iterator_new directly to
* explicitly specify the context parameter.
*/

Copilot uses AI. Check for mistakes.

@mikeller mikeller merged commit 2622135 into subsurface:Subsurface-DS9 Jun 10, 2025
7 of 8 checks passed
@mikeller mikeller deleted the update_from_upstream_202506 branch June 10, 2025 11:33
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