When using the dc_datetime_gmtime() function for a timestamp with a
timezone offset, the timezone offset needs to be added to the timestamp
prior to the call.
The BLE (and USBHID) protocol stores the size of the payload as a single
byte in the packet header. Hence the size of the payload is limited to a
maximum of 255 bytes. For sending packets, there is an additional
command byte present, which reduces the maximum payload size to 254
bytes. For receiving packets, there is no command byte present and thus
the maximum payload size is 255 bytes.
The Scubapro G3 sends BLE packets of 256 bytes, and that caused the
download to fail because the receive buffer was one byte too small.
The Mares Sirius uses the same communication protocol as the Genius,
except for the fact that it uses a newer BLE version which supports
larger data packets. The actual MTU is likely negotiated because we see
different sizes like 244 and 182 bytes. We don't have access to this MTU
size because not all BLE implementations can expose this information.
Unfortunately not only the BLE packet size is variable, but also the
size of the higher level data frames (used for downloading the content
of the objects) is no longer fixed. The frame size appears to adapt to
the BLE MTU size. This is most likely done to reduce the overhead and
maximize the throughput.
Although each frame ends with an OxEA byte, we can't rely on this
knowledge to detect the end of the frame. The END byte is not escaped in
the payload, and thus can also appear anywhere in the frame. As a
workaround, we rely on the fact that each frame appears to be send as a
single BLE packet. The only exception is the ACK byte, which gets send
as a separate BLE packet if the command requires parameter data.
Compatible models: Quad Ci, Puck 4 and Puck Air 2
The current implementation assumes a fixed order for the different
record types to share some code with the older models. See commits [1]
and [2] for more details. The main disadvantages of this approach are
the extra complexity and the limited flexibility to adapt to future
changes to the data format.
To make the parsing code more future proof, split the code into separate
functions for the Genius/Horizon and the older models.
[1] Commit 8b06f2c31d437d6e067c21e7263b5ccc33539537 (Horizon)
[2] Commit feec939a2924095f07e030aa98d839b40d4cb6cc (Genius)
The Mares Genius (and compatible models) uses a new command to download
different types of objects, instead of manually reading and parsing the
flash memory. This command also supports reading device properties like
the serial number.
This change is necessary because newer models like the Sirius no longer
support reading directly from the flash memory.
Devices supporting BLE 4.2 (or higher) can use data packets with a
payload size of up to 244 bytes. None of the current Mares models
support this at the moment, but increasing the size on the receiving
side already prepares the code for future models and remains fully
backwards compatible.
For devices with a newer firmware version (2B) the profile ringbuffer
ends already at 0xFE00, while for devices with an older firmware version
(1D) it runs to the end of the memory.
The BLE changes in commit e83732e200620882b13804f1ca54c1ab90a38188 are
causing major problems for some of the usb-serial enabled models, like
the Puck Pro and Quad Air.
These models appear to require a small delay of a few milliseconds
between sending the two command bytes and the remainder of the command
payload. I suspect the device is still busy processing those first two
bytes, and thus not ready in time to receive the remaining data. Instead
of manually adding a fixed delay, restore the previous behaviour and
wait for the ack byte again. This has the advantage that the delay is
automatically proportional to the response time of the dive computer.
For the BLE communication nothing changes.
The Aqualung i330R and Apeks DSX use a completely new communication
protocol. The main (and most problematic) difference is the use of a
proprietary bluetooth pairing mechanism instead of the standard
bluetooth pairing. The data format remains more or less compatible with
the previous models, with only the usual changes to the parser.
The initial code and reverse engineering work was contributed by Janice,
with further improvements and modifications for integration in
libdivecomputer by Jef.
Co-authored-by: Jef Driesen <jef@libdivecomputer.org>
For dive computers which are using an application specific proprietary
pairing mechanism instead of the standard bluetooth pairing, we need to
be able to exchange some additional information with the application.
Therefore, 3 new BLE specific ioctl's are added:
When a device has not been paired yet, libdivecomputer will request the
PIN code from the application with DC_IOCTL_BLE_GET_PINCODE. Once the
device has been paired successfully, the access code is passed back to
the application with DC_IOCTL_BLE_SET_ACCESSCODE. On the next download,
libdivecomputer will request this access code again from the application
with DC_IOCTL_BLE_GET_ACCESSCODE. If no access code is available, the
pairing procedure will start again by requesting the PIN.
To prepare the code to support reading the logbook ringbuffer in the
forward direction, a new field is added to the layout data structure to
indicate the direction of the ringbuffer, the workaround for handling an
invalid pointer is extended to support both directions, and finally the
correct parameters are passed to the rbstream reader.
Move the code to read the ringbuffer pointers into a separate function
that deals with all the quirks internally and simply returns two
begin/end pairs.
To obtain the logbook end pointer, the page size is now added to the
value of the last pointer without taking into account the ringbuffer
boundaries. Therefore the boundary checks in the caller need to be
relaxed to accept the end pointer as a valid value.
The profile begin/end pointers are not used anywhere and are only
retrieved for diagnostics purposes.
Move the code to read and emit the device info into a separate function
to reduce some code duplication.
As a side effect, the check for devices without a logbook and profile
ringbuffer needs to be performed earlier now, in order to prevent a
division by zero in the progress events.
Refactor the code to retrieve the profile pointers from the logbook
entry into a single function that deals with all the quirks internally
and simply returns a begin/end pair.
To obtain the end pointer, the page size is now added to the value of
the last pointer without taking into account the ringbuffer boundaries.
The consequence are:
- The boundary checks in the caller need to be relaxed to accept the
end pointer as a valid value.
- The check for the gap between the profiles can no longer compare the
pointer values directly because the begin/end values are equivalent,
but not equal.
Swapping values 2 and 3 of the pointer mode has the advantage that only
the mode with the largest value uses bytes as its unit, while all others
use the page size as their unit.
The two bytes in the command to read the logbook index are most likely
not a single 16 bit number, but two 8 bit numbers specifiying a range.
The same pattern can be found in the logbook pointers.
The ringbuffer calculations contained several flaws:
The ringbuffer_normalize() function doesn't shift the result from the
internal normalize call back to the range [begin,end-1]. The only reason
why this bug didn't cause any issues yet, is because this function isn't
used anywhere.
The ringbuffer_distance() function can return a wrong result whenever
the difference between the two values is an exact multiple of the
ringbuffer size. For example:
distance(x,x,n) == 0 or n (depending on the empty/full mode)
distance(x,x+n,n) == 0
distance(x+n,x,n) == n
So far this bug didn't cause any problems yet, because in practice this
function is always used with values inside the safe range [begin,end-1].
For input values outside the safe range [begin,end-1], only larger
values are accepted, while smaller values will trigger an assert.
A zero-length ringbuffer (e.g. begin == end) results in a division by
zero.
A dive computer typically writes its ringbuffer in the forward
direction. Thus, when downloading the dives in reverse order (newest
first), the ringbuffer needs to be read in the backward direction.
However, some dive computers already re-arrange the data in the correct
order, which means the data needs to be read in the opposite direction.
To support this case without drastic changes in the logic, the rbstream
implementation is extended to also support reading in the forward
direction.
Some of the internal state is cached in local variables at the start of
the function, and is updated only at the end of the function. But the
contents of the packet buffer is never cached. As a result, the two can
go out of sync when an error occurs and the function returns early.
Trying to restore the original state is pointless if the corresponding
data in the packet buffer is no longer available.
Fixed by removing the local variables and always updating the internal
state in-place to reflect the current state.
The ringbuffer boundary addresses (begin/end) should be ordered
correctly, and the packet size should be smaller than the ringbuffer
size, otherwise the code won't work as expected.
Keeping the one based tank number interally allows to easily discard the
pressure samples from invalid tanks. This avoid returning a huge tank
number (e.g. 0xFFFFFFFF) when the internal tank number happens to be
zero.
The new models appear to be compatible with the previous G2, but with
new model numbers and bluetooth names. The USB VID/PID for the G3 is
still unknown.
Reported-by: Greg McLaughlin <support@moremobilesoftware.com>
When no compass bearing is set on the dive computer, the stored value is
initialized to zero. Since this can also be a valid value, those zero
values are only ignored untill another non-zero value is present.
In later firmware versions, the value will get initialized to 0xFFFF
instead.
Returning disabled gas mixes to the application mainly results in lots
of unnecessary information. Therefore, remove all disabled gas mixes,
unless they are actively used. Many other dive computers do not even
include disabled gas mixes in the data.
Unlike previously assumed, the on/off state of each gas mix is actually
available in the PNF data format (opening record 4). For the older
Predator data format, this info isn't available and all gas mixes are
manually marked as enabled.
The IX3M 2 with the APOS5 firmware supports a new info record containing
the GPS coordinates. To be able to identify this new record type, the
previously reserved field at byte offset 52 is now used to store the
record type: zero for the existing sample record and one for the new
info record.
This also fixes the underlying problem of the zero timestamp in commit
3c50e91a1096332df66b2d33d64e5a8dc9136ab9, because the zero timestamp was
the result of incorrectly interpreting the first info record as a sample
record.
Previously the timestamp of the first sample was always a non-zero
value, but the IX3M 2 with the APOS5 firmware now appears to record a
timestamp of zero. This was incorrectly detected as a backwards time
jump because the time is also initialized to zero.
Sending the two byte command header and then waiting for the ack byte
before sending the remainder of the command payload causes problems for
some (but not all) users. Most likely the extra roundtrip time due to
waiting for the ack byte results in a delay that sometimes exceeds a
timeout in the dive computer while it's waiting for the payload data.
On the other hand, sending both the command header and the payload data
in one single packet is also not an option, because it causes problems
for some models, like the Mares Matrix. See commit
59bfb0f3189b14ae858650b851539d59e3fefe86 for details.
As an alternative solution, send the packet payload immediately after
the header, without waiting for the ack byte. This eliminates the extra
roundtrip time, while still sending out two separate bluetooth packets.
The functionality provided by the filter function is not only useful for
the built-in transports, but also for the applications. For example in
combination with a custom transport.
The USB I/O backend needs some additional information (e.g. interface
number and in/out endpoints) to setup the USB connection. This info is
currently maintained inside the descriptor filter function and gets
passed to the USB backend by means of the filter parameters.
This approach is not only unnecessary complex, but also makes it very
difficult to expose the filter function in the public api because the
data structures for those parameters are private.
Therefore, this data exchange is replaced with a direct mapping between
the USB VID/PID and the configuration info in the USB backend itself.
Passing the descriptor for which the filter function is being called is
a good practice and will also allow to implement some more specific
filtering in the future.
Returning disabled gas mixes to the application mainly results in lots
of unnecessary information. Therefore, remove all disabled gas mixes,
unless they are actively used. Many other dive computers do not even
include disabled gas mixes in the data.
The removal of the disabled gas mixes requires a two pass approach for
parsing the profile data. The first pass is only used to discover which
gas mixes are actively used during the dive. Next, all disabled and not
actively used gas mixes are removed from the list. Since removing one or
more gas mixes also invalidates the index of the remaining gas mixes,
the profile needs to be parsed again to report the new index in the gas
switch samples.
The original one based index is used as the stable gas mix id, used for
looking up the new gas mix index.
The hwOS models support switching to a disabled gas mix. Therefore, the
disabled state is not always a good indication whether a gas mix is used
or not. Look for gas switches during the parsing step instead to keep
track of the actively used gas mixes.
Looking up the gasmix by oxygen and helium content is only needed for
the manual gas mixes. For gas switches to a fixed gas mix, the index is
stored directly in the data.
The hexdump only includes the command parameters, but not the main
command byte. Since there are many commands without parameters, that's
not very useful.
For dives in HP CCR mode, the oxygen and diluent tanks are stored at a
fixed index. This information is more reliable than using the tank name,
and also prevents the incorrect labeling of one of the other tanks as an
oxygen or diluent tank.
Firmware v84 introduced support for sidemount diving. Users can now
configure the two sidemount tanks as the source for the GTR (Gas Time
Remaining) estimations. We can take advantage of this feature to detect
the sidemount tanks. This is more reliable than using the tank name.
For open-circuit dives, the oxygen and diluent usage doesn't make any
sense at all. But when an open-circuit diver uses the letter 'D' to
indicate a tank for decompression use, it will get incorrectly labeled
as a diluent tank.
Fixed by restricting the oxygen/diluent usage to CCR dives only.
In commit 1c8cd096b57a876c4fb0afc5113aac05d75d924e the block size was
changed from 64 to 1024 bytes. For bluetooth classic communication, this
shouldn't matter, but for some reason it does cause the OSTC4 firmware
upgrade to fail. Maybe some buffering problem in the OSTC4 firmware or
bluetooth stack?
Change the block size back to 64 bytes.
For open-circuit dives it makes no sense to also include the configured
diluents. Usually those diluents are only present because the diver uses
the same dive computer for both open and closed circuit dives.
Add time synchronisation for the Shearwater dive computers. All models
support setting the local time. Only the Teric has basic support for
time zones, and can set UTC time with a timezone offset.
Co-authored-by: Michael Keller <github@ike.ch>
When the dive computer receives an RDBI or WDBI command it doesn't
support, it sends a 3 byte NAK packet containing an error code. Detect
these NAK packets and use it to return a more appropriate error.
The RDBI (Read Data by Identifier) responses always have a fixed length.
Using a resizable buffer for the API only makes the memory management
more complex than necessary.
Also add some symbolic constants to improve readability.
Replace the numeric type with a name (fresh or salt) and change the
density value into an xml attribute. The type is the primary
information here, while the density value is optional.
The dc_parser_set_data() function allows to re-use a parser object for
multiple dives. The advantages of this feature are actually very limited
in practice. The reduction in memory consumption is almost negligible,
because the amount of internal state in the parser is typically very
small. But the implementation requires some additional complexity
because each backend needs code to reset its internal state. Therefore,
the function is removed and the data and size needs to be passed
directly to the dc_parser_new() and dc_parser_new2() functions instead.
Because keeping a reference to the data has also caused issues in the
past, especially for applications implemented in a garbage collected
language, the data will now also get copied internally.
Only a few dive computer backends (reefnet, aladin and memomouse)
require the clock parameters for parsing the date/time. Therefore,
those parameters are removed from the constructor function and
applications should set the clock parameters with the
dc_parser_set_clock() function instead.
The backend specific calibration function are deprecated. Applications
should use the new replacement functions introduced in commit
6ab140461a3a85fba3803283070427f3be413c79.
Because the sample struct is passed by value, the size of the structure
can't be changed without also changing the function signature and
breaking backwards compatibility. This prevents adding new fields in the
future, to support some new features.
When passing the sample struct by reference using a pointer, the size of
the pointer does always remains the same.
For gas consumption calculations it's very convenient to know whether a
tank is used for example in a sidemount configuration, or as
oxygen/diluent tank on a rebreather.
For rebreather dives, it's convenient to know whether a gas mix is used
as a closed-circuit mix (oxygen/diluent) or as an open circuit mix
(bailout).
Some dive computers report the time of the next decompression stop,
while others report the Time To Surface (TTS). Some models can even
report both.
Add a TTS field to the deco sample to support both values.
Rebreathers typically support multiple ppO2 sensors as a safety measure
in case a sensor fails during the dive. The current api can already
report multiple ppO2 values per sample, but it does not provide any
information about which sensor the measurement is from.
The new sensor index provides this info, and can also be used to
distinguish between the average/voted ppO2 value using the special value
DC_SENSOR_NONE.
After the previous commit changed the resolution of the sample time to
milliseconds, the dive computers which actually support a higher
resoltion can now enable this feature and report all samples.
Some dive computers, especially freediving computers, supports multiple
samples per second. Since our smallest unit of time is one second, we
can't represent this, and the extra samples are dropped. Therefore, the
units are changed to milliseconds to prepare supporting this extra
resolution.
The newer u-Blox Nina B2 bluetooth module supports larger packets up to
244 bytes, but the older Telit/Stollman bluetooth module does not.
Trying to send a packet larger than 20 bytes fails. For maximum
compatibility, limit the output packet size to 20 bytes.
Replace the custom packet handling code in the iconhd and ostc3 backends
with the new layered packet I/O, and also integrate it into the idive
and extreme backends.
The latest versions of the Divesoft Freedom (HW 4.x) and Liberty (HW
2.x) dive computers support BLE communication. Previous generations did
support only a mass storage mode, where the dives are available as DLF
files. The BLE communication protocol uses HDLC framing for the data
packets. The dives downloaded over BLE have the same data format as the
DLF files.
Co-authored-by: Jan Matoušek <jan.matousek@rekomando.cz>
Tested-by: Jakub Hečko <jakub.hecko@divesoft.com>
During troubleshooting it's very convenient to know the exact version
used in a bug report. With the git commit SHA1 added to the version
string in all builds, that becomes very easy.
The Windows version resource is compiled and can include the (generated)
version.h header file for the definition of the version macros. There is
no need to have it generated by autotools. Less generated files makes it
easier to use other build systems, like Visual Studio.
Add a basic Android.mk for building with the Android NDK. This can serve
as a good starting point for developers integrating libdivecomputer into
an Android application.
Co-authored-by: Sven Knoch <info@divinglog.de>
On Android operating systems, the getopt() function is posix compliant
and thus the option processing stops when the first non-option is found.
But the getopt_long() function permutes the argument vector, just like
the GNU implementation.
Using a leading '+' character in the option string disables the
permutation again.
The hwos devices support 5 gas mixes for open-circuit and 5 diluents for
CCR dives. Internally, both sets are stored separately, but depending on
the dive mode only one of both sets gets stored in the dive header. The
gas change event contains the index of the corresponding gas mix or
diluent, and should always be in the range 1 to 5.
The OSTC4 behaves a bit different from the other hwOS models and uses
index 6 to 10 for the diluents. That means the index needs to be
adjusted to refer to the correct mix in the dive header.
Reported-by: Michael Keller <github@ike.ch>
The OSTC stores either the OC gas mixes or the CCR diluents depending on
the dive mode. For CCR dives, there is also bailout to an OC gas
possible, and those gas mixes are added dynamically to the manual gas
mixes.
The Shearwater dive computers store both the configured OC gas mixes and
CCR diluents in the header.
In both cases, the gas change events should reference the correct type
of gas mix. This patch takes care of that.
Keep track of the gas mix type, and whether the gas mix is enabled or
not. Right now this extra information isn't really used for anything
yet, but it's available for future use.
The index in the gas change event should refer to the one of the fixed
gas mixes. All gas mixes with a higher index are either manual or
bailout gas mixes, and are reported with different events containing an
O2 and He percentages instead.
Dives without a valid gas mix in the sample data (e.g. both the O2 and
He set to zero) are currently ignored by accident. Because the
o2_previous and he_previous variables were initialized to zero, those
invalid gas mixes were not processed.
Add an explicit check for such gas mixes to make this more obvious.
For freedives it makes no sense to report any gas mixes. The freedives
also use a different sample format, which doesn't generate any gas
change events.
Especially among technical divers, it's not uncommon to carry spare
tanks that will only be used in emergency situations (for example a
rebreather with one or more bailout tanks). Since those gas mixes are
not used throughout the dive, they were also not reported to the
application.
Fixed by reporting all configured gas mixes. Applications can still
obtain the previous result after manually inspecting the gas switch
events in the samples and filtering out the unused gas mixes.
This partially reverts commit c8b166dadbf961e17a9bd1cc28db3d92832ddf72.
The new Excursion v6 firmware supports some new commands for accessing
the dive index, and also uses a completely new data format. To preserve
backwards compatibility in the download logic to some extent, some
critical fields such as the profile length, remain stored at identical
offsets.
The new data format now contains a version field to allow for future
modifications. This version field is located at byte offset 3, which
corresponds to the highest byte of the 32 bit dive number in the old
format. Thus, unless someone manages to reach 16M dives, this field will
always be zero for the old format.
Co-authored-by: Ryan Gardner <ryebrye@gmail.com>
For the HP CCR mode, the O2 and diluent tank pressure was stored in the
T1 and T2 tank pressure fields. Starting from log version 14 they moved
to dedicated fields in the EXT sample, next to the T3 and T4 tank
pressure. Thus the maximum number of tanks increased from 4 to 6.
The Oceans S1 uses a plaintext and line based communication protocol
over BLE. The larger payloads, which also contain plaintext data, are
transferred using the XMODEM-CRC protocol.
Based-on-code-by: Linus Torvalds <torvalds@linux-foundation.org>
The Deepblu Cosmiq+ uses a plaintext and line based communication
protocol over BLE, where the binary payload data is encoded as
hexadecimal characters.
Based-on-code-by: Linus Torvalds <torvalds@linux-foundation.org>
There are functions for reading 8, 16, 24 and 32-bit big and little
endian values, but the corresponding functions for writing such values
are not always available. The 64-bit variants are also missing.
Moving the implementation of the snprintf wrapper functions to the
platform module allows to re-use the same implementation throughout the
entire codebase.
The Oceanic Pro Plus 4 appears to "disconnect" somehow after about 30
seconds. The BLE connection remains up, but the dive computer simply
stops responding to commands. The download fails with a timeout error,
and the end-user can only download a few dives at most.
The Android DiverLog+ application appears to keep the connection alive
by re-sending the version and handshake commands once in a while. Copy
this behaviour by repeating those two commands every 50 read requests.
During testing, that's approximately every 25 seconds.
Note that both commands are required, sending only one of them does not
fix the problem.
The Oceanic Pro Plus 4 appears to support the big page B4 and B8 read
commands, but with some strange twists:
* When sending the B8 read command, a 256 byte packet is received. The
checksums of the packet are valid, but the upper half of the payload
data is always filled with zero bytes. That means we can't use this
command.
* The B4 read command appears to use a 2 byte checksum instead of the
normal 1 byte checksum. That means we can use this command with a
small model specific tweak.
The model number is now also available in the common struct. There is no
need to store it twice. The auto-detected model number from the version
table is also more reliable than the one passed by the caller.
With the model number in the version table, the version string can be
mapped to the corresponding model number. This allows to implement some
model specific behaviour already before being able to read the model
number.
In most cases, there is a simple one to one relationship between the
version string and the model number, but there are also a few
exceptions:
* For the Sherwood Wisdom 2 and 3, and the Beuchat Mundial 2 and 3,
each variant has a different model number, but the first part of the
version string is identical. The difference is in the firmware
version part. Handling this correctly requires two entries in the
table.
* For the Oceanic OC1 there are 3 different model numbers, and only 2
different version strings. That means there is no correct mapping
possible.
Some iX3M models support a dual mode Buhlmann and VPM decompression
algorithm. Currently libdivecomputer is only capable of reporting one of
those two algorithms, but that's still better than returning an error.
The getopt function is defined in the unistd.h header file. This header
file is only available on posix compatible systems. For example, on
Windows it's not available when building without mingw.
When no dives are present, the maximum value for the progress events is
set to zero, which triggers an assert. Fixed by letting the progress
events reach 100% instead.
In the latest G2 firmware v2.0, the size of the BLE packets increased to
101 bytes (with a one byte header and 100 bytes of actual payload). This
caused the download to fail, because the internal buffer was suddenly
too small for those larger packets.
These larger packets are most likely due to an update in the BLE stack
of the dive computer. Originally, the maximum BLE packet size was just
20 bytes (excluding the 4 byte L2CAP header and 3 bytes GATT header),
but BLE 4.2 increased the maximum packet size to 244 bytes (or 251 bytes
with the headers).
The USB HID code path keeps using the same fixed size packets as before.
The first byte of the BLE packets does no longer contain the size of the
payload. Since BLE supports variable sized packets, we can simply ignore
this byte and obtain the payload size from the BLE packet size.
When compiling a 32bit dll with the mingw-w64 compiler, some 64bit
integer arithmetic operations are implemented using functions from
libgcc (e.g. __udivdi3 and __umoddi3 from libgcc_s_dw2-1.dll). This
unexpected dependency is inconvenient for applications.
The run-time dependency can be avoid by linking statically.
The current hidapi version (v0.10.1) fails to build with newer autoconf
versions (v2.70) due to a duplicated AC_CONFIG_MACRO_DIR macro in the
configure.ac file. This is fixed in newer versions.
The hidapi-hidraw variant of the hidapi library needs access to the
specific /dev/hidraw* device nodes. The existing udev rules for the USB
devices don't apply to the hidraw device nodes.
On Linux, the hidapi library is usually available in two variants:
hidapi-libusb and hidapi-hidraw. By default, the autotools build system
won't be able to detect those variants (due to the difference in the
name) and will automatically fallback to the libusb implementation
instead.
If for some reason the hidapi library should be used, the preferred
hidapi variant can now be specified during configuation with a
parameter:
./configure --with-hidapi=hidapi-libusb|hidapi-hidraw
The default value for the parameter remains 'hidapi'.
The bookmark value is a bitfield indicating the type of bookmark:
1 - Pressed the bookmark button during a dive
2 - Reset the stopwatch
4 - Unknown
8 - Unknown
The hidapi based implementation returns as soon as the first packet is
received, while the libusb based implementation tries to read the
requested number of bytes. That fails with a timeout if the requested
number of bytes is larger than the size of a single packet and no
further packets are received.
Avoid this problem by limiting the size to the maximum packet size.
At the moment, trying to download an old dive for which the profile data
has already been overwritten with newer data fails. This used to work
fine, but around hwOS firmware v3.10, the behaviour described in commit
76187c550a806fe422920eb8795fa687244513f1 changed.
When downloading the compact/full headers, the firmware always sends the
headers without inspecting their content. Next, libdivecomputer uses the
length field in these headers to determine how many bytes to expect when
downloading the dive. However, when downloading the entire dive, the
hwOS firmware now checks whether the profile data of the dive is still
available. If that's no longer the case, the firmware sends a modified
dive header (with the begin/end pointer fields reset to zero, and the
length field reduced to 8 bytes), along with an empty dive profile.
Since libdivecomputer expects to receive the full profile as indicated
in the original header, the download fails with a timeout.
To workaround this problem, download the dive data in two steps. First,
download the 256 byte header and check whether it has been modified. If
that's the case, reduce the length to that of the 5 byte empty profile.
The header check is also updated to exclude the modified fields. For the
progress events, just pretend the full profile has been downloaded.
Not only the two byte end-of-profile marker 0xFDFD is a valid empty dive
profile, but also a profile with the length field present and set to 8
bytes. In that case the actual length will be just 5 bytes.
When downloading the compact headers (which is the default for recent
hwOS firmware), it's not possible to compare the entire dive header, but
we can at least the check the fields that are available.
Also return an error if the verification fails.
The Aqualung i770R appears to have 6M instead of 4M (high) memory.
Confirmed by trying to read past the 6M limit, which fails with a NAK
response. This amount also matches with the capacity stated in the
manual (6553 hours of profile data at a 60 second sample rate).
The memcpy and related functions expects a valid pointer, even if the
size is zero. Most libc implementations will handle a NULL pointer just
fine, but that's not guaranteed.
Simply skip the call when there is nothing to copy.
The maximum depth value is stored in the dive header. There is no need
to parse the profile data to obtain it. This also avoids returning a
zero depth when the profile data is no longer available.
A few other fields (e.g. average depth, atmospheric pressure and
temperature) are also present in the dive header.
For dive computers where the reference time (epoch) of the device is
unknown, libdivecomputer uses the current time of the device (devtime)
and the host system (systime) to synchronize both clocks.
Currently, both timestamps are passed directly to the constructor of the
parser. With the new public function, the application can adjust the
timestamps afterwards.
Some dive computers store the depth as an absolute pressure value (in
bar). To convert to a depth value (in meters), the atmospheric pressure
and water density are required. For dive computers that do not have
those values available, libdivecomputer uses a default value. With the
new public api functions, applications can adjust those default values.
Some dive computers already provided a backend specific calibration
function. Those functions are now deprecated. They are kept around to
maintain backwards compatibility for now, but they will be removed in
the next version.
Report the decompression algorithm (Buhlmann, VPM, RGBM or DCIEM), and
if available also the parameters. For now only the conservatism setting
is supported, and for the Buhlmann algorithm also the Gradient Factors
(GF).
For dives with a timestamp that is larger than the current device time
(e.g. in the future), the clock synchronization produces incorrect
values. In that case, the time difference suddenly becomes negative,
which corresponds to a large positive value as an unsigned integer.
Under normal circumstances, this scenario can't happen. But sometimes
actions such as battery replacements or firmware upgrades can cause the
internal clock to reset.
The Reefnet devices shouldn't need this fix, because their internal
clock can't be changed, but it doesn't hurt either.
Since the clock synchronization has been removed (see commit
a1962558412b8c89a79656992c8e7f4d001065c2 for the details), those
parameters serve no purpose anymore.
The command to change the OSTC settings has a variable length payload (1
to 4 bytes). Therefore, the expected number of bytes is only know after
evaluating the first option index byte. Due to the limited UART buffer
in the OSTC, sending the remainder of the packet immediately after the
first byte, can cause the OSTC to get out of sync.
Introduce a small delay between sending the option index and the
remaining parameters to avoid this problem.
The latest firmware does store some additional information for each
tank. Right now it's not really used for anything yet, but it's
available for future use.
In the older Predator-like data format, the 4th opening/closing record
is the last one. To avoid accidental use of the higher ones, leave them
undefined.
During the download, the model number is obtained from the hardware type
because the model number isn't available before downloading the first
dive. Since the list with available hardware types is incomplete, the
correct model number is not always available. However, during parsing
the correct model number is available in the final block.
Starting with log version 8, the dive mode is stored in one of the
opening records. For backwards compatibility with older firmware
versions, the autodetection based on the status field in the sample data
is kept as a fallback mechanism.
In the latest OSTC hardware, the Telit/Stollman bluetooth module has
been replaced with a u-Blox Nina B2 bluetooth module. This new module
supports BLE data packets of up to 244 bytes (corresponding to an ATT
MTU of 247 bytes and a LL PDU payload size of 251 bytes).
For some BLE enabled models, like the Oceanic Pro Plus X, Aqualung
i750TC, Sherwood Sage and Sherwood Beacon, the BLE handshake command
is not supported and therefore disabled.
However, based on a bug report by a user with two Aqualung i770R dive
computers, this detection mechanism based on the dive computer model
number isn't sufficient. Allthough the two devices have the exact same
firmware version (2A), the handshake command only works on the newest
unit, and fails with a NAK response on the oldest unit.
Remove the model based detection and always try to send the handshake
command and rely on the NAK response to ignore the failure instead. An
additional advantage is that we no longer have to (manually) maintain a
list with the model numbers where the handshaking is known to fail.
When the dive computer receives a command it doesn't support, it sends
back a single byte NAK (0xA5) packet instead of the expected ACK byte
(0x5A) at the start of the packet. Retrying is pointless in this case,
because the next attempt will also fail. Instead, return immediately
with an appropriate error code, and let the upper layers handle the
unsupported command.
Note that the detection is currently only enabled for BLE communication.
The Uwatec/Scubapro dive computers are confirmed to internally use the
approximated standard gravity (10.0 m/s²) for the depth conversion.
Allthough this is technically wrong, users expect to get the same depth
values as their dive computer shows.
This partially reverts commit cfc9ddc380bdc5616893fc2af4e05204b5500ea2.
The Mares Puck Pro + is compatible with the previous Puck Pro. Both
models can't even be distinguished because they share the same model
number and use the same product name in the version packet.
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 parser->mode field is only initialized at the end of the function.
The result is that the current code always used the default value
(zero). Inside the function itself, the local variable should be used
instead.
Like most dive commputers, the Mares Genius uses a surface timeout to
detect the end of the dive. But since there is no explicit dive time
field stored, the total dive time is based on the number of samples,
which includes this surface time at the end of the dive. However, the
dive time displayed by the dive computer (and also the Mares
DiveOrganizer application) does not include this surface time.
For older firmware versions the value is hardcoded to 3 minutes, but
starting with the newer v01.02.00 firmware the value is configurable and
stored in the settings. To detect whether the setting is available, we
need to check the profile version instead of the header version. That's
because the header appears to be generated on-the-fly during the
download, and thus the header version also changes for dives recorded
prior to the firmware update.
For all other models, also take into account a hardcoded timeout of 3
minutes.
The changes in commit 5cb527d53ca88ac692beb55288172fc1003975fa to
support the new Mares Genius firmware v01.02.00 were incomplete.
For dives recorded prior to the firmware upgrade, only the header
version changed from 0.0 to 1.1, while the profile version remained the
same. But for dives recorded after the firmware upgrade, the profile
version also changed from 0.2 to 1.0.
The known header and profile object versions (indicated as
type.major.minor) are now:
Model | Firmware | Header | Profile
========|==========|========|=========
Genius | 1.0.x | 1.0.0 | 0.0.2
Genius | 1.2.0 | 1.1.1 | 0.1.0
Horizon | 1.7.28 | 1.0.1 | 1.0.2
To simplify the object version check, all versions below a certain upper
limit are now considered supported.
The byte at offset 0x1A appears to be some settings byte, containing not
only the sample rate index, but also some other values in the higher
bits. Except for the 4th bit, which contains the salinity setting, the
meaning of those higher bits remains unknown. Since the sample rate is
limited to only 4 possible values, 2 bits are sufficient for encoding
the sample rate index. The 3th bit might contain some other setting, or
be reserved for future sample rates.
The new Mares Genius firmware v01.02.00 has a header which is 10 bytes
larger. This difference is indicated with a change in the major version
from 0 to 1.
For manual depth calculations by divers, the standard gravity is
often approximated as 10.0 (e.g. 1 bar corresponds to 10 meter), but
libdivecomputer prefers to use the exact value of 9.80665 m/s².
For the McLean Extreme, it has been confirmed that the device also uses
the correct standard gravity internally for the conversion of the sample
depth.
Prefer SI units for internal constants (e.g. density) and calculations.
This results in more consistent conversion formulas across the different
backends.
For the Uwatec backend, this changes makes it also much more visible
that the unit for the depth is either 1 millibar (maximum depth) or 2
millibar (sample depth).
The difference between two unsigned integers can be negative. To avoid
ending up with some very large positive values, an explicit cast to a
signed integer is required.
Depths are normally expected to be always positive, but near the surface
the pressure will be very close to the atmospheric pressure. Therefore
small negative values are not unusual.
Processing the data in device units, and performing the unit conversion
only at the very last moment, avoids the need for intermediate floating
point math and thus possible rounding errors. In practice this is not
really an important issue, except for some case where a negative zero
value was returned.
The migration to Visual Studio 2013 allows to build the Visual Studio
project in a Github Action.
Because some of the source files (e.g. the resource script and version
header) are normally auto-generated by the autotools build system, an
msys environment is setup to run the configure script. When building
from a distribution tarball, this extra step isn't necessary.
Starting with msvc 2013, the C compiler has much better C99 support. The
previous workaround to compile everything as C++ code is no longer
needed.
Some additional changes:
- Add a 64bit build
- Enable _CRT_SECURE_NO_WARNINGS to silence warnings
With just 12 bits, the depth values are limited to at most 40.95m.
However for some deeper dives, this appears to be wrong. The next two
higher bits, which were previously unknown, are also part of the depth.
This increases the maximum depth to 163.83m.
Support for the S_BLOCK_WRITE2 command is only available since the hwOS
Tech firmware v3.09 and the hwOS Sport firmware v10.64. In commit
9e92381be48866f3f13a11e98d59962575bb5ad3 it was enabled for all firmware
versions newer than v3.09, causing the firmware upgrade to fail for
older hwOS Sport firmware versions.
Reported-by: Anton Lundin <glance@acc.umu.se>
The Sporasub SP2 uses a very simple communication protocol and memory
layout, but with some unusual aspects:
Dives are artifically limited to a maximum of 6000 samples.
Unlike all other dive computers, the dives are not stored in some kind
of ringbuffer structure. Once the memory is full, no new dives can be
recorded. The existing dives need to be erased first, and the dive
computer will start recording again at te start of the memory area. The
Sporasub application has an "Auto-clear watch memory after data
transfer" feature for this purpose.
I didn't implement a more efficient download algorithm because
downloading a full memory dumps takes less than 10 seconds.
The maximum value for the progress events is based on the amount of the
flash memory available for storing the dives. But the 8 byte serial
number is not stored inside the dive data, and is added dynamically
during the data transfer. This extra data needs to be taken into account
to avoid overflowing the progress events and trigger an assert in the
code.
The clang compiler accepts *any* compiler flags, and just reports
an annoying warning instead:
warning: unknown warning option '-Wsomething' [-Wunknown-warning-option]
Use -Werror=unknown-warning-option to change this warning into an error,
and detect the unsupported flag. Since this is a clang specific option,
it can't be used unconditionally with other compilers. The option is
also only used for checking the compiler flags, and not for compiling
the code.
The new Github Actions offers similar functionality as the Travis CI
integration, but with some interesting extra features:
The build action is almost equivalent to the existing Travis build
configuration. But as an extra feature, the build artifacts are now
available for download.
The release action does automatically build a distribution tarball and
create a Github release, whenever a new version is tagged and pushed.
The upper bits appear to contain some other (currently unknown)
information. The Oceanic VT Pro specifications list a maximum depth of
399 ft (120 m), which requires only 9 bits. The Oceanic application also
ignores the higher bits.
The McLean Extreme uses a dual stack Bluetooth module from Microchip
which supports both Bluetooth Classic and Low Energy.
Reported-by: David Carron <david_de_carron@hotmail.com>
The CBC initialization vector is passed as a const pointer, and then
cast to a non-const pointer to store it in the aes state struct. This
cast can easily be avoided by changing the struct field into a const
pointer.
The new iX3M 2021 models with bluetooth do support BLE communication.
Bluetooth Classic (rfcomm), which was the only supported bluetooth
variant in the previous models, is not available.
When an Uwatec Aladin is connected, but the transfer hasn't been started
yet, we receive a continuous stream of zero bytes. Approximately every
7-8ms a new zero byte is received. But when the dive computer is
(temporary) disconnected, the stream of zero bytes also ends.
The consequence is that due to the use of blocking read call with an
infinite timeout, the application becomes unresponsive, without any
chance to abort the communication. This can eaily be avoided by using a
timeout instead. Receiving the main 2048 byte packet takes about 1050ms.
Thus a 3000ms timeout should be long enough to not cause the main data
transfer to timeout, but still short enough to cancel reasonable fast.
The for loop construct without an increment statement is a bit unusual
and thus easy to miss. With an equivalent while loop, the intent becomes
a bit more obvious.
Currently the fingerprint feature uses the first 7 bytes of the computer
configuration data. Since this information does not uniquely identify a
dive, and is actually often identical for several dives, no new dives
are detected anymore. Fixed by using the date/time timestamp at the
start of the dive configuration data instead.
Reported-by: David Carron <david_de_carron@hotmail.com>
The previous commit added a check for a NULL key inside the filter
functions, but it's more efficient to handle it early on, before even
calling the filter function.
An example where the filter functions can be called with a NULL key is
when a bluetooth discovery fails to retrieve the name of the remote
device. In such case, we have no information to detect whether the
bluetooth device matches a known dive computer or not, and thus it
shouldn't be filtered out.
The matching functions expect a pointer to the value as argument, and
not the value itself. Since a C string is already a pointer (to a NULL
terminated character array), an extra pointer indirection is required.
Without the small delay, sending the first frame often fails. Trying to
read the ACK response byte just fails with a timeout, and no data is
received at all. The bootloader is probably not ready to receive data
yet.
The new iX3M 2021 models (refered to as 'iX3M with Sequared Buttons' in
the Ratio support section) identify as iX5M in the Bluetooth name.
Reported-by: Damian Zaremba <damian@damianzaremba.co.uk>
Handling the SIGPIPE signal in a library is tricky, because installing a
signal handler does affects the entire application. But we can at least
try to avoid generating the SIGPIPE signal ourselves. On Linux, that
requires the use of the MSG_NOSIGNAL flag, and on Mac and BSD setting
the SO_NOSIGPIPE socket option.
Because the dive_count variable is decremented, it doesn't contain the
total number of dives, but the index of the last dive. A negative number
indicates no dives. The same result can be obtained by using the total
number of dives directly. That's not only more intuitive, but also fixes
a -Wsign-compare compiler warning.
Windows and unix use a diferent data type for representing socket file
descriptors (SOCKET vs int). This mismatch results in a compiler warning
when comparing to S_INVALID.
Allthough the input buffer size has type 'size_t', the return value of
the function has only type 'int'. Hence the function can't support input
buffers larger than INT_MAX.
This allows to fix a -Wsign-compare compiler warning: operand of ?:
changes signedness from ‘int’ to ‘size_t’ due to unsignedness of other
operand.
The hexadecimal value 0x80000000 is too large to be represented as a
signed 32bit integer. Therefore the default type for the constant is an
unsigned 32bit integer. This is a bit annoying because the timezone
field is actually defined as a signed integer, and thus comparisions
produce -Wsign-compare compiler warnings.
Fixed by switching to INT_MIN, which is the same underlying value but
interpreted as a signed integer.
Comparing signed and unsigned integer expressions can have unexpected
results because the signed integer will get promoted to an unsigned
integer. To avoid the warning, add an explicit cast to the unsigned
type, along with a check to catch negative values.
String literals have the type 'char[N]' by default. Allthough they are
not really 'const', modifying a string literal is undefined behaviour.
Therefore, to avoid mistakes, libdivecomputer uses the -Wwrite-strings
compiler option to change the default type to 'const char[N]'.
The cast that triggers the -Wcast-qual warning can be avoided by using a
character array instead of string literal.
The name of the RBT (Remaining Bottom Time) sample was taken from the
Uwatec dive computers. The actual definition depends on the dive
computer, but it usually corresponds to the air time remaining (with or
without some additional factors taken into account).
The Mares Horizon is a variant of the Mares Genius, with a few changes
to support SCR dives:
The dive header is slightly modified. There is an extra 8 byte field at
offset 0x18, which causes all later fields to have moved up with the
same amount. This difference is indicated in both the object minor
version (with a change from v0.0 to v0.1), and the logformat.
For the profile data, there is a new SDPT sample type which contains a
bit more information compared to the existing DPRS sample type. This
difference is indicated with a change in the object type (from 0 to 1).
The current implementation assumes a fixed order for the record types (a
DSTR record, a TISS record, zero or more DPRS/SDPT records with an AIRS
record every 4 sample, and finally a DEND record), and either only DPRS
or SDPT records but never a mixture of the two. If these assumptions
turns out to be incorrect, the implementation will need to be changed
significantly. Note that the assumption of the fixed order was already
present for the Genius.
Bluetooth support is currently disabled in the Horizon firmware, but
might be re-enabled in the future.
Since logbook entries with invalid ringbuffer pointers are considered a
fatal error, the download is aborted. The result is that any remaining
entries, located after the invalid entry, can't be downloaded at all.
This can be avoided by skipping the problematic entry instead of
aborting the download.
The Sherwood Sage appears to be very similar to the Aeris A300CS. For
the BLE communication the handshake fails and is disabled.
Reported-By: Nick Shore <support@mac-dive.com>
With just a single table, the version string matching not only becomes
simpler, but adding new layouts also requires no code changes anymore.
For devices that need different layouts depending on the firmware
version (e.g. Oceanic Atom 2.0 and Aeris Manta), the table may contain
multiple entries with the different firmware versions. The only
requirement is that the entry with the highest firmware version must be
listed first.
The patterns used for the version string have the firmware version
masked out because it varies. We can re-use those masks to extract the
firmware version.
The gauge and freedives have a slightly different data format compared
to scuba dives. The main difference is the size of the header and two
new sample types.
The logbook header has 5 bytes extra, which are not present in the dive
header. Those 5 extra bytes contain the dive mode, which is required for
parsing the dive data. Therefore, insert all 5 bytes again and update
the parser.
The remaining 4 bytes appear to be some 32 bit address. They are not
used for anything right now, but are preserved as well in case they are
needed in the future.
The memory buffer already supported appending and prepending data, but
not inserting data anywhere in the buffer. That's exactly what the new
function does. The free space is still maintained at either the start or
the end of the buffer.
The existing check accepts the number of array elements as a valid
index. That's clearly wrong for zero based indexing, and would result in
a buffer overflow.
At some point (possibly around v71 of their firmware), Shearwater implemented
PNF for the Petrel and Petrel 2. Those are of course not air integrated, and
apparently don't support adjustable sample rate, so the log data doesn't include
opening and closing record 5. Instead of failing when those aren't found, we
should simply only access those when they actually exist.
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
The original Mares Genius support was based on Mares Dive Organizer
2.25, which has a bug in the salinity parsing. The latest version 2.26
has this bug fixed.
Reported-by: Greg McLaughlin <gregm@somemore.com>
The McLean Extreme records a sample every 10 seconds instead of every 20
seconds. This resulted in dive durations that were twice as long as
expected.
Reported-by: David Carron <david_de_carron@hotmail.com>
In one of the Windows system header files, an "interface" macro is
defined as:
#define interface struct
This results in some very strange build errors when also including the
descriptor-private.h header file. That's because the dc_usb_params_t
struct has a member field named "interface":
typedef struct dc_usb_params_t {
unsigned int interface;
unsigned char endpoint_in;
unsigned char endpoint_out;
} dc_usb_params_t;
As a workaround, define the WIN32_LEAN_AND_MEAN macro before including
the windows.h header file. This excludes some less common Windows API
declarations, including the above one.
In commit 57f0ce6d7902117cfc4d0d6b401b516fc93ca488, an extra parameter
was added to the dc_descriptor_filter() function. But I only updated the
libusb code path and forgot to do the same for the hidapi variant.
Commit d1b865d192afc9efde337b5cff8a239366f15565 breaks the OSTC4
firmware upgrade because the OSTC4 expects to receive the service init
command and the service key all at once, before sending any response.
The hwOS firmware still reads the service init command one byte at a
time, and sends the echo immediately after each byte. But in the
meantime, the hwos firmware has also been optimized. The processing time
for an incoming byte is now always faster then the time it takes for the
next byte to physically arrive via the serial line between the USB/BT
chip and the processor. Thus, even without any buffering, sending all
bytes at once should no longer be a problem.
This partially reverts commit d1b865d192afc9efde337b5cff8a239366f15565.
Reported-by: Anton Lundin <glance@acc.umu.se>
Suggested-by: Ralph Lembcke <mail@ralph-lembcke.de>
There is no reason for libusb to ever return a negative number of bytes,
but due to the use of a signed integer, it's technically possible. And
because libdivecomputer returns an unsigned integer, such a negative
number would be reported as a very large size.
Replace the hardcoded libusb based code with the new USB I/O transport.
This enables the use of a custom I/O on platforms where libusb is not
available.
The USB communication is now also implemented as an I/O stream
transport. Unlike most I/O devices, USB communication supports multiple
interfaces and endpoints. This requires some some special care:
In the general case, autodetection isn't really possible without
additional knowledge. Hence the need for the filter parameters to pass
this kind of information.
The implementation assumes two bulk endpoints for the standard
read/write interface. Communication with the control endpoint is
supported through the new DC_IOCTL_USB_CONTROL_{READ,WRITE} ioctl's.
Replace the small helper function to retrieve the function pointer and
then call the function, with another helper function to call the filter
function directly. This way the function pointer doesn't need to be
exposed at all.
When the fingerprint feature isn't used (or with a timestamp of zero),
the response to the SIZE (0xC6) and DATA (0xC4) commands is received
almost instantly:
[0.302704] W: C60000000010270000
[0.366727] R: DCF90F00
[0.367829] W: C40000000010270000
[0.394812] R: E0F90F00
But when the fingerprint feature is used (with a non-zero timestamp),
there is a noticable delay:
[0.341218] W: C64CEB204D10270000
[1.927905] R: FE0B0000
[1.931610] W: C44CEB204D10270000
[5.092081] R: 020C0000
In this particular case, the total amount of dive data was close to 1M
bytes, which pushed the delay over the 3 second timeout, and caused the
download to fail. Increasing the timeout to 5 seconds fixed the problem.
The most likely explanation is that the dive computer needs to scan its
internal logbook to determine which dives and how many bytes to send.
Because that involves reading relative slow flash memory, this can take
up to a few seconds, especially if there are many dives present.
The duration of the delay also depends on the value of the fingerprint
timestamp: the less dives we try to download, the longer the delay! I
suspect that's because the most recent dives are located near the end of
the logbook. Hence, the less dives we request, the more dives the dive
computer needs to skip.
Below are some timings for downloading espectively 792410, 531856 and
270850 bytes from the same dive computer, but with a different
fingerprint value:
[0.216305] W: C6F8D5C84510270000
[0.373511] R: 56170C00
[0.378929] W: C4F8D5C84510270000
[0.661388] R: 5A170C00
[0.236246] W: C620D80F4810270000
[0.559608] R: 8C1D0800
[0.563755] W: C420D80F4810270000
[1.171631] R: 901D0800
[0.246193] W: C654E6434A10270000
[0.826365] R: FE210400
[0.831760] W: C454E6434A10270000
[1.974351] R: 02220400
- Added missing man pages for the new functions.
- Updated the main libdivecomputer man page to reflect the new flow.
- Fixed minor typos in the dc_parser_get_field and
dc_parser_samples_foreach functions.
Instead of assuming that the dive list is presented in a sorted circular
list, sort it properly alphabetically (which also ends up being a
numerical sort for the HEX ascii dive names).
The "search for most recent dive, then splice the list around" case
doesn't work in the general case. It happens to work if you don't
delete any dives, and dives only disappear as they are being overwritten
by new dives when the storage overflows.
But if you delete dives and then create new ones, the dive list will not
be sorted at all, and we should sort it properly when downloading.
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
In recent hwos firmware versions, the depth is no longer stored as
pressure (in millibar), but directly as depth (in meters) with the
salinity and gravity factor already applied.
The S_BLOCK_WRITE (0x30) command sends a stream of bytes to the dive
computer. Because the payload has no fixed length and there is no length
field included, the hwOS firmware detects the end of the stream by means
of a 400ms timeout. The main disadvantage of this approach is that a
short hiccup in the communication will be incorrectly detected as the
end of the stream. Hence only a part of the data will get written to
the flash memory, and the remainder of the data will get interpreted as
the next commands.
To avoid this problem, the hwOS firmware v3.09 and later supports a new
S_BLOCK_WRITE2 (0x31) command, which uses a fixed size payload of 256
bytes.
Reported-by: Ralph Lembcke <mail@ralph-lembcke.de>
By reading the firmware version information immediately after entering
download or service mode, we can identify the specific firmware version
and adapt to minor differences in the communication protocol.
The S_BLOCK_WRITE (0x30) command sends a stream of bytes to the dive
computer. Because the payload has no fixed length and there is no length
field included, the hwOS firmware detects the end of the stream by means
of a 400ms timeout. Therefore the ready byte is always delayed by this
400ms timeout.
The same remark applies to the DISPLAY (0x6E) and CUSTOMTEXT (0x63)
commands. But because libdivecomputer always pad the text with zeros and
sends the maximum payload size, we won't hit the timeout.
Reported-by: Ralph Lembcke <mail@ralph-lembcke.de>
Erasing a flash memory page is a relative slow operation and takes a
significant amount of time. Therefore, the ready byte is delayed, and
the standard timeout is no longer sufficient. Estimate the required
delay and wait.
Reported-by: Ralph Lembcke <mail@ralph-lembcke.de>
The hwOS firmware reads the service init command one byte at a time, and
sends the echo immediately after each byte.
Reported-by: Ralph Lembcke <mail@ralph-lembcke.de>
For the Oceanic Pro Plus X and the Aqualung i770R, downloading over BLE
often fails because the version packet contains one or more unexpected
bytes.
For a successful download, the correct structure for the version packet
is as follows:
5A 4F4345414E4F43582031432030303032 C6
That's the start byte, the payload "OCEANOCX 1C 0002" and the checksum.
For all the failed packets, there are one or more bytes extra present
between the payload and the checksum:
5A 4F4345414E4F43582031432030303032 9F02 67
5A 4F4345414E4F43582031432030303032 3603 FF
5A 4F4345414E4F43582031432030303032 64 2A
5A 4F4345414E4F43582031432030303032 9202 5A
5A 4F4345414E4F43582031432030303032 08 CE
5A 4F4345414E4F43582031432030303032 2C01 F3
The amount of extra bytes, and their content appears to be pretty
random. The strangest part is that the checksum of the packet is
actually correct and includes those extra bytes!
As workaround, accept extra bytes in the BLE packet, verify the checksum
as usual, and finally strip the excess bytes and only pass the actual
content to the next layer. To avoid false positives, the workaround is
limited to packets with a payload and checksum, and only enabled for the
two affected models.
When the last deco stop is cleared, the dive computer switches to NDL
mode with an infinite time (0x7FFF for APOS4 and 0xFFFF for APOS3). But
because libdivecomputer does not report those infinite values to the
application, detecting the end of the deco phase is not very intuitive.
This issue is fixed by passing those infinite NDL values as-is to the
application, despite the relative large values (respectively 9.1 and
18.2 hours). For reference, the finite NDL values reported by the ratio
dive computers can be large as well, with values up to 0x4000 (4.55
hours).
Due to how the Oceanic ringbuffer is implemented, the ringbuffer always
contains at least one entry. If there are no dives recorded yet, the
content of that entry will be empty. Such entries are already ignored
during processing, but instead of returning this empty entry to the
caller, simply clear the logbook buffer, and return no entries at all.
Previously, invalid ringbuffer pointers were always handled during the
second pass. But that changed after the previous commit. If the invalid
pointer is located in the first logbook entry, this is now handled as
"no dives present". Fixed by returning the correct error code instead.
If all the entries in the logbook ringbuffer happen to be empty, the
ringbuffer end pointer will not have a valid value. Creating the
ringbuffer stream will fail, and an error will be returned to the
caller. Fixed by adding an extra check, and exit if there are no dives.
The depth value is encoded with only 11 bits instead of 12 bits. The
extra bit contains the gas mix index. This resulted in wrong depths,
with values larger than 204.8m.
For the BCD encoded day field (range 1-31), two bits are sufficient to
represent the upper digit (range 0-3). The purpose of the highest bit is
unknown, but it's certainly not part of the day field, and needs to be
masked off.
The bluetooth device filtering is based on the fact that the format of
the bluetooth device name is something like 'FQ001124', where the two
first letters are the ASCII representation of the model number (e.g.
'FQ' or 0x4651 for the i770R), and the six digits are the serial number.
It seems that the BLE communication protocol is somewhat different from
the serial one in the version string: while the serial version tends to
show the memory size, the BLE version string has some other numeric
pattern.
Linus Torvalds reports the BLE pattern for the i770R is normally just
"0001", allthough he once also observed "0090" with the same dive
computer. A communication trace from a Pro Plus X also showed "0001".
We don't have enough information to guess the meaning of the number.
Regardless, for those two dive computers supporting BLE, make the
pattern simply ignore the last four digits, since they clearly vary.
Based-on-code-by: Linus Torvalds <torvalds@linux-foundation.org>
The BLE communication sends a handshake packet containing a passphrase
based on the serial number of the device. Sadly, we can't actually read
the serial number from the device until after this handshake has
successfully completed, which makes it a bit of a chicken-and-egg
problem from a communication standpoint. However, the serial number is
also exposed in the bluetooth device name the device advertizes, which
is the reason for the newly added DC_IOCTL_BLE_GET_NAME ioctl.
Thanks to Janice McLaughlin for pointing out the logic of this magic
handshake.
Based-on-code-by: Linus Torvalds <torvalds@linux-foundation.org>
Refactor the packet receiving code to read the ack byte, the payload
data and the checksum all at once, with just a single read operation.
This is not only a bit more efficient, but will also simplify the BLE
support.
The trailing zero byte is present for historic reasons only. At the time
the Oceanic protocol was implemented, the Oceanic application send this
extra zero byte too, and we simply copied this behaviour. But more
recent versions no longer send it. Probably a small (harmless) bug that
was fixed.
The write command is send as two separate packets. The first packet
contains the B2 command and the page number, and the second packet
contains the payload and checksum. Because the payload can contain
arbitrary data, the first byte of a packet is not necessary a command
byte. But the code to select the correct ack byte is based on this
assumption. Fixed by passing the expected ack byte.
The set_latency function is the perfect example of a feature that should
be implemented as an ioctl: it's only implemented by a single driver,
and the functionality is also highly platform specific.
This new ioctl function allows to perform I/O stream specific requests
through a generic interface. This provides an easy way to extend the I/O
interface with some driver specific features, without having to modify
the public api.
Replace the manual polling, implemented using a combination of the
dc_iostream_get_available and dc_iostream_sleep functions, with the new
and more efficient poll function.
The Linux implementation is very straighforward and just a lightweight
wrapper around the select function. But the Windows implementation is
much more complex, because the Windows event notification mechanism
behaves very different:
The WaitCommEvent function does not support a timeout and is always a
blocking call. The only way to implement a timeout is to use
asynchronous I/O (or overlapped I/O as it's called in the Windows API),
to run the operation in the background. This requires some additional
book keeping to keep track of the pending background operation.
The event mechanism is also edge triggered instead of level triggered,
and reading the event with the WaitCommEvent function clears the pending
event. Therefore, the state of the input buffer needs to be checked with
the ClearCommError function before and after the WaitCommEvent call.
The check before is necessary in case the event is already cleared by a
previous WaitCommEvent call, while there is still data present in the
input buffer. In this case, WaitCommEvent should not be called at all,
because it would wait until more data arrives.
The check afterwards is necessary in case WaitCommEvent reports a
pending event, while the data in the input buffer has already been
consumed. In this case, the current event must be ignored and
WaitCommEvent needs to be called again, to wait for the next event.
The BLE communication is significant slower than usb-serial. The first
BLE data packet of each response often takes longer than one second to
arrive. This causes the first attempt to fail with a timeout. The second
attempt will appear to succeed, because it actually receives the
response of the first attempt. But now the next command will fail,
because it will receive the response of the second attempt of the
previous command.
Increasing the timeout and adding an extra delay before retrying, avoids
this problem.
Reported-by: Linus Torvalds <torvalds@linux-foundation.org>
For the Aeris Manta, the end of the profile ringbuffer appears to depend
on the firmware version. For older firmware versions (1x), the end of
the ringbuffer is at address 0xFFF0, while for the newer versions (2x),
it's 0xFE00.
The code checks for firmware version 2B, because that's the lowest known
version in the 2x range.
Reported-by: Nick Shore <support@mac-dive.com>
The logbook ringbuffer is always updated sequentially. Therefore, emtpy
entries can only be present after the oldest dive. However it appears
that under some special conditions (for example an empty battery during
the dive), the logbook entry is not always stored correctly, which can
result in an empty entry after all.
I suspect that at the start of each dive, the OSTC erases the next
available entry in the logbook ringbuffer and updates the internal write
pointer. Once the dive is finished, the actual content of the erased
logbook is written. Thus, when the OSTC runs out of battery power during
the dive, that last step never happens, and the erased entry remains in
place.
As a workaround, ignore all empty logbook entries instead of assuming we
reached the last dive.
Due to a bug in the hwOS Tech firmware v3.03 to v3.07, and the hwOS
Sport firmware v10.57 to v10.63, the ppO2 divisor is sometimes not
correctly reset to zero when no ppO2 samples are being recorded.
Usually this condition can be detected by the fact that the length of
the extended sample will not have enough space left for the ppO2 sample
(9 bytes). As a workaround, reset the divisor back to zero to manually
disable the ppO2 samples.
In theory this detection method is not 100% reliable. There can still be
other sample types present in the extended sample. If their total size
is larger than 9 bytes, the bug will not be detected at all. Instead,
those bytes will get interpreted as the ppO2 sample, resulting in bogus
ppO2 values. Additionally, one of the other sample types will now run
out of space and cause the parsing to fail with an error. However, in
practice this risk is relative low. Most of the other samples are
relative small (1 or 2 bytes), so you would need many of them. That's
rather unlikely in most configurations. The only exception is the large
deco plan sample (15 bytes).
In commit 2829f7ebf9902170bf653d67dbe412a0a4f140cf, the hwos parameter
of the hw_ostc_parser_create() function was kept to preserve backwards
compatibility. Since the function has been removed from the public api,
the parameter can be removed now.
The workaround for the tank pressure in the previous commit is only
relevant for the newer hwOS based devices, and not for the original OSTC
devices. In practice this doesn't cause any problems because the
original OSTC doesn't support a tank pressure sensor, but nevertheless
it's better to use the correct condition.
The tank pressure is stored with a resolution of 1 bar instead of 0.1
bar. There is however one exception. The hwOS Sport firmware used a
resolution of 0.1 bar between versions 10.40 and 10.50.
Unfortunately the only way to distinguish the Sport from the Tech
variant is the different range of the version number (10.x vs 3.x). The
consequence is that this workaround will start to produce wrong results
once the firmware version number of the hwOS tech variant reaches the
10.x range. If that ever happens, this workaround should be removed
again!
Some of the newer Shearwater dive computers support up to 2 tank
pressure sensors. The tank pressure samples were already reported, but
the tank field with the corresponding begin/end pressure was still
missing.
To be able to collect the tank begin/end pressure, the log version needs
to be available earlier, because it's needed for parsing the tank
pressure data in the samples. Therefore, extract the log version
immediately after locating the opening record.
Add the Shearwater Nerd 2 bluetooth device name.
The change to uppercase is purely cosmetic. The string comparisions are
not case-sensitive. But for documentation purposes it's good practice to
list the exact name as reported by the device.
When using a BLE connection, it's not sufficient to purge the buffers of
the underlying I/O stream. The locally cached BLE packet needs to be
discarded also.
It's exactly the same as the regular i200, but has a new version number
and string.
Tested-by: Tiago Thedim Dias <tiagotsoc@gmail.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Suppress the tank pressure sample when there is no active transmitter
available, or the connection with the transmitter is (temporary) lost.
In the latter case, the pressure is recorded as zero.
The Ratio dive computers support up to 10 tank pressure sensors. The ID
of the active tank sensor is stored in the sample data, and gets mapped
to the corresponding tank index.
A small typo introduced with the Tusa Talis support in commit
b188c414206daaa5b6de464ced98d78f6da7cde1 accidentally disabled the tank
pressure reporting for all models.
The Mares Genius supports a new command to download different types of
objects (e.g. dive header, dive profiles, etc) directly, without needing
to manually read and parse the contents of the flash memory.
The data structure also changed significantly. The profile data is now
organized into different records. Each record starts and ends with a 4
byte ascii marker:
DSTR: Dive start record
TISS: Tissue record
DPRS: Sample record
AIRS: Air integration record
DEND: Dive end record
and contains a CRC checksum. The contents of the records remains very
similar to the existing iconhd data format.
Based-on-code-by: Janice McLaughlin <janice@moremobilesoftware.com>
Split the offset calculation in two steps: first the offset to the
header data, and then the date/time field. The main advantage is that
the resulting code now follows the same logic as in the other functions.
The Mares Genius dive header is no longer located at the end of the data
(after the dive profile), but at the start. Therefore we don't need the
offset to the dive header anymore. Replace with the size of the header
instead.
The two byte commands are in fact a single byte command. The second byte
is some kind of checksum, containing the command byte xor'ed with the
value 0xA5.
The latest variant of the Mares Quad has 4 times more flash memory
compared to the original variant (1M vs 256K). Therefore this variant
supports a new command to read the size of the flash memory.
At the moment, it's unknown whether the previous variant also supports
this new command or not. Probably not, because none of the other
compatible models seems to support it either. Hence we only attempt to
read the flash memory size for the Quad, and a failure is not considered
a fatal error. The disadvantage of this approach is that a temporary
communication problem can result in a misdetection of the flash memory
size.
Reported-by: Janice McLaughlin <janice@moremobilesoftware.com>
In the EON Steel descriptor for the temperature field, the "nillable"
value is -3000:
int16,precision=2,nillable=-3000
So the missing equals sign is just a small oversight.
The length field in the data is checked for the maximum size (e.g. the
size of the buffer), but there is no such check on the minimum size
(e.g. the size of the header). If the length is smaller, the code
accessed data before the start of the buffer.
The main difference with the serial communication is that the BLE
communication uses data packets (with a maximum size of 20 bytes)
instead of a continuous data stream.
Occasionally, the device responds with an empty packet (with just the
ACK and EOF byte) or with a short packet where one or more payload bytes
are missing. The empty packet is most likely caused because the device
didn't receive the second part of the command (with the parameters) in
time. The missing bytes might be caused by a buffer overflow on the
Bluelink Pro, when the data from the dive computer arrives faster over
the serial link than the device can forward it over the slower bluetooth
link to the host system.
One way to address this problem is to lower the packet size from 256 bytes
to only 128 bytes. The main disadvantage is that this also impacts the
download speed considerably. In one particular example, the total
download time increased from about 135 to 210 seconds (which is already
slow compared to the 62 seconds the same download takes over usb)!
An alternative solution is to simply retry the failed command. That way
there is only a performance penalty for the few bad packets, and not for
every single packet. In the above example, only two packets needed a
retransmission.
Note that the iconhd communication protocol uses no checksums. Hence
it's not possible to detect corrupt data packets. Only short packets can
be detected, because they result in a timeout.
The Suunto DX supports 3 CCR diluents and 8 OC gas mixes. Since the gas
mix index in the data is relative to either the set of CCR diluents or
OC gas mixes (depending on the dive mode) and libdivecomputer reports
both sets, the index needs to be adjusted.
There is no need to lookup the gas mix index, because the number is
stored directly in the event data, right next to the oxygen and helium
values. This actually removes an ambiguity in cases where the user has
configured two or more identical gas mixes. In that case, the lookup
would always find the first gas mix.
On certain devices, for example the Aeris Elite T3, the logbook
ringbuffer can sometimes contain an empty logbook entry in between the
valid entries. Because the presence of such an empty entry is currently
being interpreted as having reached the last valid entry, the download
is aborted. The result is that all remaining valid entries, located
after the empty entry, can't be downloaded.
This can be avoided by skipping the empty entry instead of aborting the
download.
The Ratio dive computers support synchronizing the internal clock. One
complication is that recent firmware versions (4.0.56 or 4.1.10) support
two timezones (home and abroad), while the libdivecomputer api only
supports one timezone. To deal with this, the most recent firmware
versions (4.0.58 or 4.1.12) will interprete an invalid timezone index
(0xFF) as leaving the timezone unchanged.
If the firmware doesn't support the dual timezone command, or if the
firmware doesn't have the invalid timezone index modification yet, then
a fallback to the single timezone command is provided. Note that in the
latter case the side effect is that both timezones will be changed!
The memcpy and related functions expects a valid pointer, even if the
size is zero. Most libc implementations will handle a NULL pointer just
fine, but that's not guaranteed.
Simply skip the call when there is nothing to copy.
There were quite a few models missing in the list. And because the
lowest iX3M model number has changed, the iX3M detection needed to be
updated as well.
The Ratio dive computers with the latest APOS4 firmware support a
timezone setting. Take this timezone into account instead of using the
timezone of the host system.
This will allow parsing dives from the Shearwater Teric, but depending on the
firmware could also be used on older models.
Based on ideas and code from Dirk Hohndel
The Log Upload RDBI (Read Data by Identifier) response tells us which
format the dive computer supports.
Shearwater recommends to use the 'Petrel Native Format' for all dive
computers which support it, even those pre-Teric models which (depending
on firmware) might support both PNF and the older 'Predator-Like
Format'.
They also recommend to ignore the 0x90000000 format which is very
similar to PNF but without the final record and to use the older
Predator Like Format in that case.
The 0xDD000000 format is never an option by the time you got here, but
in the old code (prior to the PNF addition) we would have fallen back to
0xC0000000, so let's do the same here.
Any other value is actually an unknown format and should be treated as
such.
Which format we use is determined by the base address used to download
the logbook entries.
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
The Cobalt 2 has a bit more flash memory available for storing dives
compared to the original Cobalt 1. This larger amount of memory can
cause the progress events to exceed past 100% if there are many dives
present. This will trigger the assert in the event code and crash the
application.
The Atomic Aquatics Cobalt backend uses 8K data packets. Since a hexdump
of such a data packet needs at least twice the size of the binary data,
the internal log buffer should be increased to 16K bytes.
Due to the integer promotion rules of the C language, the unsigned char
values are promoted to a signed integer (and not an unsigned integer)
before being shifted. But the result of a left shift on a signed type is
undefined if the resulting value can't be represented in the signed
type.
GCC's Undefined Behavior Sanitizer (ubsan), enabled with the option
-fsanitize=undefined, detects this type of problem at runtime with the
following warning: "left shift of X by Y places cannot be represented in
type 'int'".
Fixed with an explicit cast to unsigned integer.
It appears that the Aqualung i770R looks almost the same as the Pro Plus
X, but has an additional pO2 field for each gas by the O2 field, which
impacts the offset calculations.
This fixes the dive truncation that happened with long dives due to the
removal of extra padding (commit a2100843b9cf: "Remove extra padding
from the end of the profile"). It turns out the rest of the profile was
in bits 13-15 (with bit 12 being something else).
The packetization format for the BLE communication used to be that the
first byte of the BLE GATT payload was the size of the payload (1-19
bytes).
That seems to no longer be true as of fw version 1.4. It is still the
size of the payload for the simple small reply packets, but for the long
data stream it ends up being an odd sequence of 13 different values that
are almost - but not quite - 19 apart.
Whatever. Modify our strict "length byte must make sense" rule to be
more of a guidline than a hard rule. This makes the download succeed
again.
Very weird.
Reported-by: Adric Norris <landstander668@gmail.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Unfortunately there are several devices with an invalid ringbuffer begin
pointer. In such cases, the strict validation of the pointer causes the
download to fail, without being able to download any dives at all.
Since the begin pointer is only needed to detect the start of the oldest
dive, we can fall back to downloading the entire profile ringbuffer.
Usually we can still detect the start of the oldest dive in some other
(indirect) way. For example when reaching past the end of the
ringbuffer, or the presence of invalid pointers in the linked list
structure. The result is that, we'll be able to download at least some
dives before hitting some other error.
The RTS signal needs to be low before it is raised, and not just set.
This ensures that the PIC inside the Pelagic PC interface is reset and
the initialization sequence always starts cleanly, regardless of the
previous state of the signal.
Reported-By: Bill Perry <bperrybap@opensource.billsworld.billandterrie.com>
Even after removing the pages padded with 0xFF bytes, there are still
some invalid sample pages present at the end of the profile. But it
turns out the number of valid profile pages is stored in the logbook
entry.
The only caveat is that the number of pages appears to be stored as a 12
bit number, which limits the total profile size to only 64Kb. We don't
known what happens for larger dives.
The libusb and hidapi pkg-config files already add the subdirectory to
the header search path with:
-I/usr/include/libusb-1.0
-I/usr/include/hidapi
Therefore, using the subdirectory in the include statement is wrong. In
practice, this usualy works fine by accident, because the base directory
(/usr/include) is typically listed in the default search path of the
compiler. But that's not always true. For example when cross-compiling
or when using the PKG_CONFIG_LIBDIR environment variable.
The Smart Air uses almost the same data format as the Quad Air. Only the
4 bytes containing the dive mode and number of samples moved from the
beginning of the header to the end. This is a change adopted from the
regular Smart.
For older OSTC dives, using logbook format version 0x20, the average
depth is not available in the dive header. It's only available since
version 0x21, which increased the header size from 47 to 57 bytes.
The Oceanic Pro Plus X is quite different from the previous models. The
profile data is now stored in a dedicated memory area, and hence there
are a few important differences:
Reading data from the new profile memory area is done with a new F6
command. This new command is very similar to the existing B8 command,
but accesses a completely different memory area. In order to integrate
those two memory areas as transparantly as possible into the existing
infrastructure, a virtual memory space is introduced. The lower part of
the virtual memory is mapped onto the main memory area, while the upper
part is mapped onto the new profile memory area.
The page size of the new profile memory area also increased from 16 to
256 bytes. If the profile size is not an exact multiple of 256 bytes,
the dive computer pads the profile data with 0xFF bytes.
The other changes are the usual Oceanic device specific changes.
The memcpy and related functions expects a valid pointer, even if the
size is zero. Most libc implementations will handle a NULL pointer just
fine, but that's not guaranteed.
Simply skip the call when there is nothing to copy.
In the error handling code, the dc_buffer_free() function can be called
with an unitialized "buffer" variable as parameter. Fixed by adding an
extra label.
Reported-By: Linus Torvalds <torvalds@linux-foundation.org>
On Windows, the WSAStartup() function needs to be called, to initialize
the socket library, before using any of the other WSA socket functions.
This includes the functions used for the bluetooth device discovery.
The Mac OS X timer implementation returned timestamps with nanoseconds
as unit instead of microseconds.
Reported-by: Rick Balsano <rick.balsano@gmail.com>
The Scubapro LogTrak application doesn't send the handshake commands for
BLE communication. Also the Aladin Sport Matrix, which supports only
BLE, responds with a 0x01 byte instead of the expected 0x01 byte and
that causes the handshaking to fail. Thus simply omit the handshaking
for BLE communication.
Reported-by: Berthold Stöger <berthold.stoeger@tuwien.ac.at>
The Uwatec Smart, Meridian and G2 backends are almost identical, except
for the low-level packet sending and receiving code. With the new I/O
layer, those three backends can easily be unified in a single backend.
The Meridian and G2 are completely removed, only the family types are
kept for backwards compatibility.
The Aladin Tec (and Tec 2G) sample descriptor table supports up to two
event bytes, but there is only a single event descriptor. This missing
descriptor causes a fatal error during parsing. Add a dummy descriptor
to avoid the error.
Instead of reading data packets, the code is actually sending some
random data to the dive computer! A small typo with bad consequences!
This is a critical bug because it not only causes the download to fail,
but also appears to change random settings on the dive computer. I
suspect that the garbage data that gets send to the dive computer
happens to contain some valid write settings commands.
For most I/O stream implementations the serial communication specific
functions are meaningless. Implementing them as no-ops allows the dive
computer backends the call the I/O stream functions unconditionally.
However, implementing the no-op with a dummy function returning
DC_STATUS_SUCCESS, does not only add some (small) overhead at runtime,
but also requires many such functions. This is inconvenient and the same
result can easily be obtained by using a NULL pointer instead.
The consequence is that the logic is reversed now. To obtain the
previous behaviour of returning the DC_STATUS_UNSUPPORTED error code
again, you'll need to implement a dummy function. But that's fine
because it's the less common case.
I/O functions with output parameters, should always initialize those
output parameters, even when an error is returned. This prevents the
(accidental) use of uninitialized variables, whenever the caller forgets
to check the return code.
As a nice side effect, the use of a local variable guarantees that the
underlying I/O implementation will always receive a valid pointer.
When two or more identical (or very similar) dive computers are
connected, the USB VID/PID can be ambiguous. That's because the VID/PID
identifies the type of the USB device, and not the individual device.
But each USB HID device descriptor returned by the device discovery
represents a single connected device, and thus guarantees to open the
correct USB device.
To obtain the same behaviour as before, an application can simply open
the first discovered device.
Replace the global USB library context with a reference counted session
to manage the lifetime of the USB library context. For the libusb based
implementation, this is actually a much better match for the underlying
libusb api, and allows to eliminate the global state. For the hidapi
based implementation, the global state is unavoidable because the hidapi
doesn't support multiple sessions. Therefore we use a singleton session.
The main difference with the serial communication is that the BLE
communication transmits each SLIP encoded data packet as one or more BLE
data packets. The BLE packets have an extra two byte header with the
total number of packets and the current packet number.
The main difference with the serial communication is that the BLE
communication uses data packets (with a maximum size of 20 bytes)
instead of a continuous data stream.
The main difference with the USB HID communication is that the BLE data
packets have a variable size and are no longer padded to the full 32
(Tx) or 64 (Rx) bytes.
The main difference with the USB HID communication is that the BLE data
stream is encoded using HDLC framing with a 32 bit CRC checksum. Due to
this encoding, the data packets can no longer be processed one by one
(as is done for the USB HID packets). The entire HDLC encoded stream
needs to be received before it can be processed. This requires some
additional buffering.
Setting a default transport type avoids the need to explicitely set a
transport using the the new --transport command-line option. This also
preserves backwards compatibility with previous versions where the
option didn't exist yet.
The dctool example application is updated to the latest changes:
- The I/O stream is opened and closed by the application.
- A new (mandatory) option is added to select the desired transport
type. This is nessecary because several dive computers support
multiple transport types now.
Currently the dive computer backends are responsible for opening (and
closing) the underlying I/O stream internally. The consequence is that
each backend is hardwired to a specific transport type (e.g. serial,
irda or usbhid). In order to remove this dependency and support more
than one transport type in the same backend, the opening (and closing)
of the I/O stream is moved to the application.
The dc_device_open() function is modified to accept a pointer to the I/O
stream, instead of a string with the device node (which only makes sense
for serial communication). The dive computer backends only depend on the
common I/O interface.
With the support for multiple transports per device, the
dc_descriptor_get_transport() function became obsolete because it does
support only a single transport type. Applications should use the new
dc_descriptor_get_transports() function instead.
With the support for multiple transports per device and the possibility
to use custom I/O implementations, libdivecomputer no longer knows which
devices are actually supported. Hence libdivecomputer needs to always
report all the devices it knows about, and it's up to the application to
filter out entries for which there is no suitable transport available
(either built-in or custom).
Because the list of supported built-in transports depends on the
availability of external libraries (libusb, hidapi) and the operating
system, the application needs some mechanism to retrieve this
information at runtime. Therefore, a new dc_context_get_transports()
function is added, which returns a bitmask with all the available
built-in transports.
Several dive computers support multiple transports. For example the
Suunto Eon Steel supports both USB HID and BLE. All devices using
bluetooth classic communication support both the native bluetooth
transport and the legacy serial port emulation.
To support this feature, the values of the dc_transport_t type are
changed into bitmasks, and the dc_descriptor_t struct is extended with a
bitfield with all the supported transports.
Add a function to query the underlying transport type. This allows the
dive computer backends to implement transport specific behaviour where
necessary.
For the built-in I/O implementations, the transport type is obviously
always hardcoded, but for a custom I/O implementation the application
needs to provide the correct type. Hence the transport type can't be
hardcoded in the vtable and needs to be passed as a parameter.
Trying to purge the input buffer by reading and discarding data packets,
results in an annoying and confusing error message if no data packet is
received. To avoid this error, the functionality should be integrated in
the USB HID code, either automatically during initialization or by
implementing the purge function.
But since there seems to be no evidence that this is actually necessary,
let's remove this code.
Check the fingerprint before downloading the dive. If a match is found,
this avoids some unnecessary communication and thus makes the download a
little bit faster.
The file list isn't freed when an error occurs, and the strings returned
from the lookup_enum function are dynamically allocated and thus should
be freed as well.
The descriptor strings are dynamically allocated and owned by the
struct. The const qualifiers are a bit misleading here, and result in
warnings when trying to free the pointers again.
The multiplication is evaluated using 32bit arithmetic, and then stored
in a 64bit integer. The 32bit integer overflow can be avoided by casting
to a 64bit type first.
The device descriptor is either mandatory for a certain command (with
DCTOOL_CONFIG_DESCRIPTOR) or always NULL. But for some commands it will
be useful to support an optional descriptor as well. To support this, we
always try to lookup the device descriptor whenever the corresponding
command-line options are set.
The write function is modified to always fill the buffer completely
before sending out the data. For escaped characters, the previous
implementation needed to append two bytes at once. Thus, if there was
only space left for a single byte, the buffer got flushed early with one
byte still unused. This can be avoid by appending one byte at a time.
The read function is modified to use a simple state machine with only a
single read call. This is mainly preparation to support reading and
processing larger data packets instead of just single bytes.
I received a bug report complaining that the most recent dives did not
get downloaded. It turns out that the internal dive number in the
logbook entries got reset back to zero somehow:
Logbook 0: empty
...
Logbook 209: empty
Logbook 210: 1
Logbook 211: 2
...
Logbook 235: 26
Logbook 236: 27
Logbook 237: 0
Logbook 238: 1
...
Logbook 254: 17
Logbook 255: 18
This confuses the logic to locate the most recent dive. Because that
logic assumes that the entry with the highest internal dive number is
always the most recent dive, it finds logbook entry #236 instead of the
correct entry #255. Now, when processing the logbook entries backwards,
it stops at those empty entries, and thus never reaches then newest
entries #237 to #255.
The workaround is based on the fact that the OSTC4, unlike the other
hwos based models, already re-orders the logbook entries and always
sends the most recent logbook entry last. So we can ignore the dive
number and simply use the last non-empty entry.
I'd never noticed this before, since my date had always been already set
correctly, but the timesync with the EON Steel only set the time, not
the date.
The fix is trivial, since the code already filled in the datetime data,
it just didn't do the SET_DATE command.
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
The Dive Rite NiTek Q supports OC and CC samples. For reporting the dive
mode, any dive containing at least one CC sample is considered to be a
CCR dive.
The half-duplex emulation is basically a workaround for a timing problem
in the vyper2 backend. Since no other dive computer backend uses or
needs this feature, it makes no sense to support this at the I/O layer.
It can be implemented directly in the vyper2 backend instead.
Replace the gettimeofday() based implementation with the new monotonic
timers. This makes the implementation more robust against unexpected
adjustments of the clock.
The new timer module provides an cross-platform interface for a high
resolution monotonic clock. The timestamps are always relative to the
creation of the timer and their unit is one microseconds.
The timers can be used for logging, measuring elapsed time and
implementing timeouts.
Unfortunately there are several devices where an invalid logbook begin
pointer occurs relative frequently. Typical examples are the Oceanic VT
4.1 and the Sherwood Wisdom 2. In such cases, the strict validation of
the pointer causes the download to fail, without being able to download
any dives at all.
Since the begin pointer is only needed to detect the oldest logbook
entry, we can fall back to downloading the entire logbook ringbuffer. If
we're lucky (and we usually are), we can detect the oldest entry by
inspecting the logbook entries once they are downloaded (e.g. presence
of uninitialized entries) and then the download will finish succesfully.
In the worst case scenario, we'll be able to download at least some
dives before hitting another error.
The ringbuffer_distance() function has a parameter to specify whether a
ringbuffer with identical begin/end pointers should be considered an
empty or a full ringbuffer. Hence there is no need to handle the case of
a full ringbuffer manually.
The warning about disabling the O2 sensors due to factory default
calibration values, applies only if there is at least one calibrated O2
sensor present.
This has no effect on the calibration bits, because those are already
zero if there are no calibrated O2 sensors present.
There is no need to expose the two step connection setup of the
underlying socket interface in the public api. Doing so may complicate
the implementation on platforms where the native api is not based on the
socket interface (e.g. Mac OS X).
Note that the function to connect based on the IrDA service name is
removed. It's not used anywhere in libdivecomputer and since IrDA is an
outdated technology nowadays, it's unlikely we'll need it in the future.
The device descriptors are extended with a filter function. During the
device discovery, this filter function is used to return only devices
that match a known dive computer.
The filtering is optional, and can be disabled by passing a NULL pointer
for the device descriptor when creating the iterator with one of the
dc_xxx_iterator_new() functions.
Replacing the callback based interface with an iterator based interface,
results in a more extensible abstraction with a common interface for
each of the built-in I/O implementations (serial, usbhid, irda and
bluetooth).
The -Wno-pedantic-ms-format option is only needed for the MinGW target.
But for some reason, the AX_APPEND_COMPILE_FLAGS macro enables the
option for all other GCC targets too. But during compilation GCC outputs
the warning "unrecognized command line option".
When the port number is set to zero (which is an invalid value), detect
the port number automatically. On Windows, we can simply supply the UUID
of the serial port service, and the Windows api will take care of the
discovery. On Linux (bluez), the SDP discovery needs to be performed
manually to retrieve the port number.
If all (calibrated) sensors still have their factory default calibration
values (2100), they are probably not calibrated properly. To avoid
returning incorrect ppO2 values to the application, they are manually
disabled (e.g. marked as uncalibrated).
Appending data to the buffer may fail if a memory allocation is
necessary to enlarge the buffer. Hence the return value of the
dc_buffer_append() call should always be checked, unless the memory was
already pre-allocated or the check is deferred after the last operation.
Unlike the Shearwater Petrel, the Shearwater Nerd 2 appears to have a
distinct model number from the Nerd.
Reported-by: Janice McLaughlin <janice@moremobilesoftware.com>
Shifting a 32bit value by 32 is undefined.
Instead of using shifts to create the mask, explicitly create it by
subtracting 1 from the signbit value (and using bitwise NOT to fill all
the higher bits).
This commit looks confusing because Jef wanted me to not have two places
where I use the bitwise not. So instead of creating an equivalent mask
variable and not having to change the return statements we end up with a
mask that is the bitwise invert of what was there before this commit and
therefore the return statements need to change as well.
Coverity CID 207769
Suggested-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
The Linux kernel uses the sir_name as a standard C string (in one
instance copying it into a 60 char buffer using kstrncpy with a length
limit of 60), we therefore need to ensure that it is 0 terminated.
Since the existing code didn't notify the caller if we were truncating
the string at 25 characters, I didn't add such a warning/error for
truncating at 24 characters.
I was not able to find documentation on how Windows uses irdaServiceName
but since this is implementing the same standard, the same change was
made to the Windows code.
In both cases I replaced the hardcoded length of 25 with a sizeof()
argument (but both Linux and Windows hard code that length in their
headers, so it seems unlikely this would ever change).
Coverity CID 207790
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
In the case of a submodule, the .git file is a text file pointing to the
correct module in the parent's .git folder. The git rev-parse works
correctly in both cases.
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
Add a new type to distinguish between closed circuit (CCR) and
semi-closed circuit (SCR) diving. Some dive computers from HW and
DiveSystem/Ratio support this.
Because the CCR/SCR abbreviations are more commonly used, let's take the
opportunity to also rename the existing DC_DIVEMODE_CC. To preserve
backwards compatibility, a macro is added to map the old name to the new
one.
Reported-by: Jan Mulder <jlmulder@xs4all.nl>
The OSTC3 stores the dive headers and profile data in two separate
memory areas. There is a header area with fixed positions and a profile
area which is used as a ring buffer. Each dive header stores the
position of the profile data in the ring buffer.
Now, once there are more dive headers then room for the profiles, the
oldest profiles (but not the headers) are overwritten with new data.
Because the dive headers are not updated when their profile data gets
overwritten, they will now point to data that is no longer available.
The internal logbook detects this situation and does not display the
profile. But during the download, there is no such check, and the OSTC
will send invalid profile data.
This invalid profile data should be dropped on the receiver side.
Unfortunately implementing the exact same check as is done by the OSTC
itself isn't possible, because the OSTC doesn't send the 6 byte internal
header on which the check is based. As a workaround, the two byte
end-of-profile marker and the length field in the profile header is used
to detect overwritten profiles.
For the socket based I/O stream implementations (IrDA and bluetooth) the
serial communication specific functions are meaningless. Implementing
them as no-ops allows the dive computer backends the call the I/O stream
functions unconditionally.
This is important for the bluetooth implementation, because bluetooth
enabled dive computers will be able to use both the native bluetooth
communication and the legacy bluetooth serial port emulation.
Wih the custom I/O implementation, an application can use its own
low-level I/O layer instead of using one of the built-in ones. The
application only needs to provide a set of callback functions, and
libdivecomputer will wrap them into a I/O stream.
The purpose of the new I/O interface is to provide a common interface
for all existing I/O implementations (serial, IrDA, bluetooth and USB
HID). With a common interface the dive computer backends can more easily
use different I/O implementations at runtime, without needing
significant code changes. For example bluetooth enabled devices can
easily switch between native bluetooth communication and serial port
emulation mode.
The new interface is modelled after the existing serial communication
api. Implementations where some of those functions are meaningless (e.g.
IrDA, bluetooth and USB), can just leave those functions unimplemented
(causing the call to fail with DC_STATUS_UNSUPPORTED), or implement it
as a no-op (always return DC_STATUS_SUCCESS).
Because some of those compiler warnings are GCC specific, they should
only be enabled if the compiler actually supports them. This is take
care of with some macros from the autoconf-archive.
To avoid breaking the build on systems that don't have those macros
installed (e.g. Mac OS X), they are included in the project.
With the new APOS4 firmware, both the tts and the duration of the first
deco stop are recorded while in deco. But compared with the older
firmware, the tts field has moved to a slightly different offset. And
contrary to the new documentation, it seems that the value for invalid
or infinite has also changed from 0xFFFF to 0x7FFF,
Note that for dives recorded with an older firmware version, the
duration of the first deco stop isn't available, and libdivecomputer
reports the tts instead. This is the same behaviour as before.
Reported-by: Janice McLaughlin <janice@moremobilesoftware.com>
The Suunto Eon Core uses a different USB PID, but otherwise it's
compatible with the Eon Steel. It's probably an Eon Steel internally,
but with a smaller form factor.
To be able to distinguish between the two models and use the correct USB
PID, each model is assigned a different (artificial) model number.
Reported-by: Nick Shore <support@mac-dive.com>
The Suunto Eon Steel seems to have a limit of maximum 400 dives. Once
that limit is reached, the oldest dives get overwritten with newer
dives. But the order in which the dive entries are downloaded isn't
changed, and thus the most recent dive is no longer the last entry.
For the first 400 dives, the order is always straightforward:
D001 D002 ... D399 D400
The most recent dive is always the last entry, and no special processing
is necessary. But once the limit is reached, the next few dives will
start to overwrite the oldest dives, but the order remains unchanged:
D401 D402 ... D399 D400
Thus in order to return the dives in the correct order (newest first),
we can no longer assume the most recent dive is the last entry, and thus
we need to locate it manually.
The new algorithm is based on the assumption that the most recent dive
will have the hightest timestamp. And to be able to walk backwards
through all the entries, the list is assumed to be a circular list.
The EON Steel can use the new 'timesync' interface to set the time
automatically from the computer it is connected to.
This also regularizes the EON Steel command names a bit, and adds a few
new commands (you can also read the time etc, which this doesn't
actually use).
[Jef Driesen: Modified to follow the existing naming conventions, return
the correct error code and avoid arithmetic operations with signed
integers.]
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
At the moment the progress events are reported for each download
operation separately. Combined with the fact that the size of the dives
isn't known in advance, and thus the progress events are based on a
worst case value, the user experience is far from optimal. In practice,
the progress goes from 0 to 100% for every manifest, and it stays close
to zero while downloading the dives.
This is improved by combining the individual progress events into a
single progress for the entire download. This global progress simply
counts the number of individual download operations. Since each
operation is now subdivided into a fixed number of steps, regardless of
the size of the transfer, the perceived speed is no longer constant.
The model number is stored in the final block of each dive. But for an
efficient implementation of the fingerprint feature, the devinfo event
should be emitted before downloading the manifests or the dives. Thus
reporting the correct model number is problematic.
Currently the model number is simply hardcoded to the value of the
Petrel. This is sufficient for the parser, because there the model
number is only used to distinguish the Predator from all the other
models. Now, because the petrel backend doesn't support the Predator,
and the predator backend (which supports both the Predator and Petrel)
can obtain the correct model number from the final block, the hardcoded
value works fine. Except of course for identifying the actual model!
Allthough there doesn't seems to be a command to retrieve the model
number directly, we can retrieve the hardware type and map that to the
model number.
The first dive computer to support this is the Perdix AI. Interestingly,
this keeps track of two sensors at all times. I haven't seen data with
two sensors active, yet.
[Jef Driesen: Update to the latest documentation.]
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
The earliest document I have references log version 6. There are
apparently older versions, but I don't know what the differences
are. Before version 7, the log version wasn't always reliably
stored, so we assume 6 is the minimum and use 7 (or later) if we
find it.
[Jef Driesen: Initialize and reset the cache correctly.]
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
The communication protocol is identical to the G2 protocol, but with a
different USB VID/PID (c251:2006).
Note that unlike the G2, the Aladin Square seems to support only 33 byte
USB HID packets (1 byte report id and 32 bytes payload), even when the
actual command is much smaller. Without padding the commands, the dive
computer doesn't reply at all. Because the padding is already there, to
support the Windows api, no further changes are necessary.
As the OSTC does not report a CNS value on the first sample, we need to
initialize it differently. This can be solved by using the initial CNS
value form the dive header, and storing that value in the first sample.
The resulting patch is very similar to 44f629f03a91a3b3.
Signed-off-by: Jan Mulder <jlmulder@xs4all.nl>
The main purpose of the magic value UNDEFINED, is to indicate that a
value isn't present in the data. But since the value 0xFF can actually
be stored in the data, we can't distinguish between those two cases.
This ambiguity can be avoided by using a magic value that lies outside
the valid range for 8 and 16 bit fields.
Note that an initial gas mix value of 0xFF remains interpreted as
UNDEFINED, but this is now made explicit.
Currently the date/time parsing assumes the reference time (epoch) of
the device clock is unknown. Hence we retrieve the current time of the
device (devtime) and the host system (systime) during the download.
Since both timestamps represent the same instant in time, but with a
different reference time, we can calibrate the device clock as follows:
ticks = parser->systime - (parser->devtime - timestamp) / 2
But this produces wrong results once the device clock has been adjusted
manually. Adjusting the device clock will suddenly increase (or
decrease) the devtime, while the systime continues ticking forwards
normally. Hence all dives recorded before the time adjustment will get
an incorrect date/time value.
Fortunately all devices appear to use a fixed epoch (2000-01-01 00:00:00
UTC) and we can simply replace the calibration with a hardcoded value.
Reported-By: Linus Torvalds <torvalds@linux-foundation.org>
Perform the initialization inside a critical section.
Unfortunately Windows critical sections, which are the simplest
synchronization mechanism available on Windows, do not support static
initialization. A call to InitializeCriticalSection is required.
Therefore a simple spinlock, with an implementation based on atomic
operations, is used as a workaround.
Initializing the hidapi library more than once is tricky. The hid_init()
function can be called multiple times, but the the hid_exit() function
will free the resources unconditionally, regardless of how many times
the hid_init() function has been called. The consequence is a premature
termination of the library.
To avoid this problem, the calls are reference counted. Note that this
workaround can't protect against calls made outside the libdivecomputer
code!
The libusb library doesn't suffer from this problem, because each
initialization returns a new context pointer. But for consistency, we
now also use a single reference counted libusb context.
When libusb uses the Windows HID api internally, it does automatically
prepend a zero report ID to the data for devices which support only a
single report. But apparently it also returns a size of one byte extra!
As a workaround, the number of bytes is limited to the actual size.
See commit c9ed92d3f55c259931527a27d018eb5791a176dd for a similar issue
in the hidapi library.
The Windows HID api always expects to receive a fixed size buffer
(corresponding to the largest report supported by the device). But
unlike the hidapi library, the libusb library doesn't automatically pad
the buffer to the expected size when trying to send less bytes. Thus
when trying to submit the transfer, the underlying Windows api call
immediately fails with ERROR_INVALID_PARAMETER.
To workaround this problem, pad the data packet with zeros manually and
always send the entire packet (1 byte report ID and 32 bytes payload).
At the time support for the Orca and iDive series was implemented, they
were being sold under the "DiveSystem" brand. But nowadays, the newer
iDive and iX3M series are being sold under the "Ratio" brand. Since this
frequently confuses end-users, let's rename the libdivecomputer vendor
name as well.
The Orca and the original iDive series remain under the DiveSystem
brand.
The parameters used with the FTDI USB serial port drivers didn't work
well with directly with libftdi1. The new baud rate results in the same
effective baud rates for both.
The rbstream block size was reduced to help with the unreliability of
the libftdi communications.
Using FTDI for custom I/O resulted in very unrealible reads. This patch
allows more reliable use of FTDI custom I/O, like what might be needed
when used on a mobile device like Android.
[Jef Driesen: Modified to retry only for non-fatal errors. This simply
restores the code from commit b3d2c603ddec9758fb36706bbde46ce23ca9f0ed,
which was removed in commit 55e8f83eb5d934e65fbf587d427de267f174c651.]
With the new APOS4 firmware, the latest ix3m and idive models support a
wireless tank sensor. For dives without a tank pressure sensor, the
pressure field in the sample is zero. Thus the first non-zero value
indicates the presence of a tank sensor.
Those two samples are no longer unknown. The first one contains some
freedive related data, and the second one contains some additional data
with several sub types. At the moment only the tank and gas mix info is
used.
In the trimix data format, the tank and gas mix information is no longer
stored in the header, but in a special sample. Because this sample is
usually located at the end of the profile, the info isn't available yet
during the first pass. Hence the need for a second pass.
Without this change, the tank and gas mix samples will be missing unless
the caller calls the dc_parser_get_field() function before calling the
dc_parser_samples_foreach() function.
The trimix dive header is only 84 bytes large, instead of 0xB1 bytes.
The difference is quite hard to notice, because compared to the normal
Galileo data format, the majority of the fields are located at exactly
the same offset. But there are also some subtle differences, like the
settings field containing the freedive and gauge bits.
To fix this bug, a new header table is added. The rest of the code is
updated to use this new table instead of the old trimix flag. The only
place where the old flag is still used is for the decoding of the tank
and pressure sample.
Allthough most dive computers always use local time and don't support
timezones at all, there are a few exceptions. There are two different
sources of timezone information:
- Some of the newer Uwatec/Scubapro devices use UTC internally and also
support a timezone setting. This UTC offset is currently taken into
account to obtain the dive date/time, but the UTC offset itself is
lost.
- Uwatec/Scubapro and Reefnet devices rely on the clock of the host
system to synchronize the internal device clock and calculate the
dive date/time. The consequence is that the resulting date/time is
always in the timezone of the host system.
In order to preserve this timezone information, the dc_datetime_t
structure is extended with a new "timezone" field, containing the UTC
offset in seconds. Devices without timezone support will set the field
to the special value DC_TIMEZONE_NONE.
The dc_datetime_localtime() and dc_datetime_gmtime() functions will
automatically populate the new field with respectively the local
timezone offset and zero. The dc_datetime_mktime() function will take
into account the new timezone field for the conversion to UTC. The
special value DC_TIMEZONE_NONE is interpreted as zero.
Being able to synchronize the dive computer clock with the host system
is a very useful feature. Add the infrastructure to support this feature
through the public api.
On Windows, the hidapi library uses the standard Microsoft USB HID
driver, while libusb requires the installation of a different driver
(WinUSB or libusbK). But installing one of the libusb drivers breaks
compatibility with other applications using hidapi (Scubapro LogTRAK and
Suunto DM5) because only one driver can be active. Switching
libdivecomputer to hidapi avoids this problem.
On Linux, the hidapi library doesn't seem to offer any advantages over
libusb. Most distributions don't even have the hidapi library installed
by default. Because there are usually two variants of the hidapi library
available on Linux (hidapi-libusb and hidapi-hidraw), the autotools
build system won't be able to detect it out-of-the-box, and will
automatically fallback to the libusb implementation.
On Mac OS X, hidapi is already the default (and also the only option).
The Windows HID api always expects to receive a fixed size buffer
(corresponding to the largest report supported by the device). Therefore
the hidapi library internally pads the buffer with zeros to the expected
size, but apparently it also returns the size of the padded buffer! As a
workaround the number of bytes is limited to the actual size.
The hidapi read and write functions return a negative value if an error
occurs. Those negative values should not be returned to the caller as
the actual number of bytes (or used in the logging). The value is reset
to zero instead.
The zero report ID byte is required when using the hidapi library. We
just never noticed this problem before, because we use libusb by
default, and libusb doesn't need the extra zero byte.
The hidapi library requires that the first byte contains the report ID.
For devices which support only a single report, the report ID byte
should be zero. The remaining bytes contain the actual report data.
Now, when hidapi uses libusb internally, it strips the zero report ID
byte again before passing the data to libusb. Thus in order to remain
compatible with the hidapi based implementation, our libusb based
implementation should do the same.
The correct name for the OSTC 3+ is OSTC Plus nowadays. Allthough the
exact name doesn't really matter because all OSTC3 based models are
compatible, using the correct name should reduce confusing for
end-users.
I received a bug report from a device which failed to download new dives
after a reset (probably caused by an empty battery). This reset appears
to reset the internal dive counter back to zero, and also resets the
write pointer back to the begin of the logbook ringbuffer, but leaves
the existing logbook entries in place. The result is that the logic to
find the most recent dive based on the highest internal dive counter,
will be wrong because it finds those old entries.
The discovery of the logbook (and profile) write pointers eliminates the
need to search for the most recent logbook entry.
I dived the model enough to wrap the profile buffer and I was wrong
about where the end was. Also, the buffer starts 3 bytes after where it
could. We were treating profile pointers as 4 bytes when they are two
bytes. This worked most of the time when short tissues were clear
(tissue load follows the pointer).
The Cochran Commander TM appears to be a first generation Commander with
limited storage and function compared to later models.
The main differences are:
- The TM doesn't support high-speed transfer so use the 0x05 read
command and don't change to a higher baud rate. Still reset to 9600
to wait for the heartbeat.
- The TM has a different config command (one byte).
- The TM has only one config page.
For previously supported Cochran computers high-speed read of log and
profile data started at byte 0. Older models that lack the high-speed
transfer function use the standard speed read commands and so the log
and profile data are read at higher addresses.
Since moving to per-dive download of profile data (and now rbstream
download) the data->sample_data_offset and data->sample_size variables
aren't used so calculating them doesn't make sense.
The progress bar was taking 18 seconds between updates on a Commander II
when using a 128K pagesize. Since devices differ in their baud rates, it
makes sense to use smaller pages on slower devices. This change reduces
it to 32K on a Commander II and to 64K on EMC devices.
Newer cochran DCs record a gas change event at the begining of a
dive. The code creates a gas change before processing samples so
with newer DCs this resulted in duplicate events.
The method used to calculate the data used by dives (to determine when
we run out of ringbuffer) incorrectly didn't include surface sample
data. Ten to twenty minute of sample data is recorded at the surface in
case the diver re-descends, continuing the dive. The code then thought
that older dive profiles were not yet overwritten. The improper data was
returned to the user.
This function is much more useful if it works like a
ringbuffer_distance() function. It assumed the wrong values when
calculating profile size and it didn't have easy access to values it
needed to properly calculate profile sizes.
It makes sense to keep since it validates pointers.
Commander II pointers to profile ringbuffer data was wrong. After seeing
the Commander I encoding I realized the Commander II encoding of RB
pointers was in a flipped word big endian format. It only appeared to be
in normal big endian format because of an adjacent pointer that usually
shared the same first two bytes.
This adds support for older Cochran Commander dive computers,
specifically Commanders with serial numbers prior to 21000.
This also renames "Commander" model to "Commander II" and
adds "Commander I" to refer to pre-21000 models.
Since commit 344bfab229a17c7227b9bec02f616505a8d9e998 only a subset of
the id string is used to detect the model. But because the offset was
never updated, the model detection always fails now.
At the moment, the progress reporting will jump straight from about 0%
at the start of the download to 100% at the end of the download, without
any updates in between. This is improved by updating after every packet.
The error code returned by the dc_usbhid_read() function should be
returned as-is, instead of being replaced with some generic error, which
gets translated again to DC_STATUS_IO in the caller.
I did the packet logging for the received data side, but forgot to do
the same thing on the command transfer side, which makes the debug logs
a bit less useful.
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
When doing the G2 downloader, I dropped the initial handshake as I tried
to keep the code minimal, and the handshake didn't seem to make any
difference what-so-ever to me.
And it probably doesn't matter for anybody else either. But the code
isn't working for some people, and maybe it does actually matter.
More importantly, Scubapro's own LogTRAK application does send those two
initial commands, and it's probably a good idea to minimize the
differences between the different downloaders anyway, so add the
handshake sequence back in.
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Jef Driesen correctly points out that the 'address' field is just
leftover from the IrDA code, and is meaningless for the USB HID
transport version of the Scubapro G2.
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
The back-end parser seems to be the same as for the Uwatec Smart (aka
Galileo Sol). At least that's the assumption right now.
The downloader just uses USB HID (very similar to EON Steel) rather than
the horrible IrDA thing.
There's also eventually a BLE thing, but that's for the future.
This is an unholy mixture of the Uwatec Smart downloader logic and the
EON Steel usbhid transfer code. The back-end is pure Uwatec Smart
(model 0x11, same as Galileo Sol).
I'm not at all sure this gets everything right, but it downloads
*something*.
[Jef Driesen: Renamed the backend to uwatec, and made some smaller
cosmetic changes to match the existing coding style.]
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Dives that are reported by the dive computer as unreadable (for example
due to a power loss during the dive) are now skipped instead of being
reported as a fatal error. Those dives can't be retrieved, so there is
no good reason to abort the download.
Originally packets are only retried when a valid NAK packet with the
busy error code is received. The retrying is now enabled for other types
of errors also, such as data packets with checksum errors.
For the time being, the bluetooth communication code is of very limited
use. It's not used anywhere in the library, and as an internal api it's
also not available to applications. It serves mainly as a reference
implementation for future use.
The implementation supports Windows and Linux.
The dummy IrDA implementation is integrated in the main file. The
appropriate implementation is selected using conditional compilation
based on the features detect by the autotools build system.
The new APOS4 firmware changed the data format and communication
protocol. The size of the samples changed from 54 to 64 bytes, and in
order to speedup the download, a single data packet contains 3 samples
at once. If the number of samples is not an exact multiple of three, the
last packet appears to contain garbage data.
For parsing, the firmware version is available in the dive header.
Unfortunately it can't be used for dives that are downloaded from a
device with the new firmware, but which have been recorded with an older
firmware. Such dives store the old firmware version in the dive header,
but they also use the new sample format. As a workaround, we inspect the
size of the dive.
The O2 sensor millivolt values are only valid if external O2 sensor
monitoring is enabled.
Note that the interpretation of the PPO2 status bit appears to be
reversed (0=external and 1=internal).
Reviewed-by: Anton Lundin <glance@acc.umu.se>
Correcting the Predator calibration value with a scaling factor produces
even more reasonable ppO2 values compared to using a constant offset.
The scaling factor of 2.2 is based on a linear regression between the
average ppO2 reported by the dive computer, and the average ppO2
calculated over all (calibrated) sensors using the raw calibration
value.
Reviewed-by: Anton Lundin <glance@acc.umu.se>
The calibration values for the Petrel are typically in the range 1600 to
2400, while for Predator they are much smaller, with values in the range
800 to 1400. The consequence is that the calculated ppO2 values are too
low for the Predator. Adding a constant offset of about 1000 changes the
calibration value to be in approximately the same range as the Petrel,
and hence more reasonable ppO2 values. But this correction should only
be applied for the Predator, and not the Petrel.
Reviewed-by: Anton Lundin <glance@acc.umu.se>
This reads the reported mV values from the sensors, and based on the
calibration values converts it into a ppo2 value to report.
Signed-off-by: Anton Lundin <glance@acc.umu.se>
Sending this in OC mode is redundant and might confuse applications that
assume they only get PPO2 data in CC mode.
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
In CCR fixed setpoint mode of the OSTC3, the initial setpoint at the start
of the dive was not set. This fix adds the initial setpoint based on
the data in the fixed setpoint table (ie, the first fixed setpoint is
the initial one).
Signed-off-by: Jan Mulder <jlmulder@xs4all.nl>
After being removed from the public api, these functions can be changed
into local, static functions. And in a few cases, they are no longer
necessary and can be removed completely.
The second variant of the open or create functions were introduced to
maintain backwards compatibility. But after being removed from the
public api, these functions serve no purpose anymore, and can be removed
completely.
The vendor_product_parser_create() and vendor_product_device_open()
functions should be called indirectly, through the generic
dc_device_open() and dc_parser_new() functions. And the
vendor_product_extract_dives() functions are internal functions that
should never have been part of the public api in the first place.
When downloading a Shearwater Petrel using the predator backend, the
firmware version and serial number are different compared to those
reported by the petrel backend.
This is caused by a difference in the encoding of the data. In the
predator data format, the firmware version appears to be BCD encoded,
and the serial number is stored as a big endian integer.
The number of tank pressure sensors is not necessary equal to the number
of gas mixes. Take for example a dive with two gas mixes, but only a
single tank pressure sensor attached to one of the two tanks. Because
the tank index is shared with the gas mix index, it will refer to a
non-existing sensor when switching to a tank without a pressure sensor
attached.
The invalid tank index should not be considered a fatal error. The tank
pressure values should be ignored instead. The device appears to record
zero values anyway, except for the first value immediately after the gas
switch. I suspect that's caused by the fact that the pressure is only
recorded every 4 samples, and therefore the last pressure value is
reported with a small delay.
The Uwatec Aladin Tec 2G doesn't support freedive mode. This appears to
be a bug in SmartTrak and LogTrak. They both report gauge and air/nitrox
dives as apnea dives.
The current algorithm always downloads a full memory dump, and extracts
the dives afterwards. For the typical scenario where only a few dives
are being downloaded, this is inefficient because most of the data isn't
needed. This can easily be avoided by downloading the data on the fly.
Reading a ringbuffer backwards in order to process the most recent data
first, is a very common operation. Nearly every dive computer backend
has its own implementation. Thus with a common implementation, the
amount of code duplication and complexity in the dive computer backends
can be greatly reduced.
The common algorithm is implemented as a simple ringbuffer stream, which
takes care of all the technical details like the ringbuffer boundaries,
alignment to the page size, using the optimal packet size and caching
the remaining data.
The select system call modifies the file descriptor set, and depending
on the underlying implementation also the timeout. Therefore these
parameters should be re-initialized before every call.
The existing code also didn't handle EINTR and EAGAIN correct.
With a time based sample interval, the number of samples for a single
timestamp should be constant. However in practice some devices
occasionally store fewer samples. Since our sample time is based purely
on the sample interval, it goes completely out of sync with the sample
timestamp. To avoid this problem, the sample timestamp is used as the
base value.
For the Oceanic Pro Plus 2, this problem is very noticable. After about
115 minutes into a dive, the sample interval appears to increase to 60
seconds. Thus, without this fix, the resulting dive time for long dives
is suddenly much shorter than it should be.
The Aqualung i450T appears to ignore the fixed sample rate and instead
store a timestamp in each sample.
The presence of the surface samples in combination with this timestamp
based format is odd. Even the official Diverlog software is confused:
the Windows versions seems to ignore them, but the Mac version takes
them into account.
After the previous commit, the raw data is now reported with one large
vendor sample. Because that makes the data more difficult to interpret
(for example during debugging), a small helper function is added to
split the data again in multiple vendor samples.
Originally, the time and vendor sample values are emitted immediately
after the previous sample is complete. This is now postponed until all
raw samples are available.
This will be required for the Aqualung i450t. That model appears to
ignore the fixed sample rate and instead store a timestamp in each
sample. That means the timestamp is only available once the last raw
sample data has been reached.
Skipping the extra samples by increasing the length is not always
reliable. If there are empty samples present, they will get skipped
instead of the real samples. And if the number of samples isn't an exact
multiple of the samplerate, we're accessing data beyond the end of the
dive profile.
The Cressi Drake is a mainly a freedive computer. The data format is
almost identical to the Leonardo. The main difference is that a single
dive now contains an entire freedive session. Each freedive in the
session is delimited with a 4 byte header containing the surface
interval and a special marker.
The logbook entries are stored separately from the profile data. If the
profile ringbuffer is filled faster than the logbook ringbuffer, then
the oldest logbook entries can still point to profile data that has
already been overwritten with newer data.
To detect such overwritten profile data, we keep track of the remaining
space in the profile ringbuffer.
The sample interval is stored in the settings, and thus there is no need
to use a hardcoded value. In practice all dives appear to be using the
default value (5 seconds), so this is more about being future proof.
On linux, several users are reporting download problems, while on
windows everything works fine. Simply toggling the DTR line appears to
fix the problem.
A possible explanation is that on windows, the SetCommState() function
not only configures the serial protocol parameters, but also initializes
the DTR and RTS lines. In the libdivecomputer implementation the default
state is enabled (DTR_CONTROL_ENABLE and RTS_CONTROL_ENABLE). The result
is that the DTR line gets automatically initialized to enabled, and then
manually disabled again.
On linux, the DTR and RTS lines are not automatically initialized during
configuration, and need to be controlled explicitely. The result is that
the DTR line ends up disabled without being toggled.
The read command appears to be limited to the range 0x1000-0x1100. That
range seems to correspond with the first 256 bytes of the full memory
dump. The packet size of 32 bytes is an arbitrary choice.
When building the Windows version resource, the -DHAVE_CONFIG_H option
isn't passed to resource compiler automatically. The result is that
development builds don't have their git revision embedded in the DLL.
The dive mode is stored in each sample, and can change during the dive.
In order to report a single value for the entire dive, we assume the
value of the first sample is representive for the entire dive. For
example a dive started as a CC dive but with a bailout to OC during the
dive, is still considered to be a CC dive.
A warning is generated if the dive mode changes.
For dives with multiple gas mixes, an application doesn't have enough
info to figure out which one is the initial gas mix. Usually it's the
first gas mix, but that's not guaranteed. Reporting the intial gas mix
on the first sample avoids this problem.
In the public header files, all symbols are marked extern C. When using
a C compiler, there is usually no problem if the header isn't included
in the C file. But the msvc build system uses the C++ compiler (due to
the use of some C99 features not supported by the msvc C compiler).
On Mac OS X, libusb doesn't work for USB HID devices. We can use the
hidapi library instead. Although the hidapi library supports Linux and
Windows too, we keep using libusb there to avoid the extra dependency.
In commit 864b46603963ea2f70f5166bb7a738a12fc280fc, the sample events
have been removed because we need to parse the enum string descriptor
instead of the numeric value.
The gasmix query interface considers cylinders and gas mixes independent
things, so the tank data structure has a pointer to the gasmix index.
But the EON Steel treats cylinders as just having a gasmix (and so does
subsurface, for that matter), so the gasmix index for the tank is just
the same as the tank index.
But we never filled it in, so you'd always see a "gas index" of zero,
and subsurface would end up warning each time about how the gasmix index
doesn't match the cylinder index (but because subsurface actually agreed
with EON Steel, it worked despite the warning).
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
Instead of hardcoding the enum values for setpoint type and gas type,
use "lookup_enum()" to actually parse the enum data and use that.
I don't think this matters right now, since the numeric translations
haven't changed, but it is the RigthThing(tm) to do.
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
This fixes a bug where the begin/end marker was mistakenly added as the
value instead of as flag.
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
The dive gas parsing cases can be split out into a helper function to
keep things more manageable. Especially since there will be a couple
more cases coming up.
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
It turns out you can't hardcode the enum numbers either, since they
change from dive to dive (or possibly firmware version to firmware
version).
So do it right, and actually parse the string descriptor for the enum.
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
The samples that take 'enum' types need the type descriptor to parse
what the enum type means.
This doesn't actually use the data yet, I need to add parsing of the
enum descriptor string.
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
The previous commit fixed the cache initialization testing, and
uncovered the fact that the tank size cache initialization didn't set
the initialized bit correctly. That oversight had been hidden by the
fact that we then tested the bit wrongly, so not setting it right didn't
use to matter as long as there were other higher cache bits that were
set.
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
The check for whether we had initialized a field in the EON Steel cache
data structure was wrong, causing some entries to be returned
successfully even if their field had never been initialized.
In most cases, it didn't matter, since the cache data was initialized to
zero (which is a fine default for uninitialized data), so most of the
time it didn't matter.
But for the recently added dive mode field, this caused OC dives to be
returned as freedives, for example.
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
This does the basic divemode and setpoint parsing for the EON Steel, and
gets the CCR download right in the big picture.
The cylinder information is still confusing and incorrect, but this is a
big step in the right direction.
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
.. every time I look for a new feature I add debug code to print out all
the descriptors. So let's just do it once and for all.
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
Sadly the data we get from the EON Steel is a bit of a mess.
It doesn't really tell us if the data is metric or imperial (it always sends
both wet volume and working pressure). And in imperial mode the Suunto
engineers seem a bit confused. The pressure given (entered on the dive computer
in psi) is sent to us not in bar, not in atm... it's "something". As far as I
can tell it's a constant factor of 1.00069182389937 different from bar.
And the wet sizes are a bit to small to get the cuft size the user entered.
But instead of trying to guess and fix the mess, we just pass it through...
So this is somewhat useful, but not really what most users will want.
Linus started this commit with a few lines that parsed the right values out of
the data stream from the Suunto EON Steel. I then added the implementation of
the infrastructure to convert the raw data and report it back to the caller.
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
Unlike the other models, the Aladin Tec 2G uses only a single byte to
store the oxygen percentage, and there is no need to manually re-map the
deco mix.
Normally, the oxygen percentage is stored using two bytes (little endian
byte order). Thus for a device supporting two gas mixes, four bytes will
be used, and the corresponding gas mix id for each byte is as follows:
ID: 0 0 1 1
After re-mapping the id of the deco mix, this becomes:
ID: 0 0 2 2
Since oxygen percentages are limited to the range 0-100%, the highest
byte (marked with an X) should always be zero and can thus be ignored:
ID: 0 X 2 X
Now, because an oxygen percentage of zero indicates a disabled gas mix,
this is equivalent to a device supporting three (or even four) gas
mixes, each stored using only a single byte:
ID: 0 1 2 3
We can take advantage of this knowledge to avoid having to re-map the
deco mix id.
Several devices (e.g. Oceanic Geo, Oceanic Veo 2.0 and Tusa Zen) report
a maximum depth that is clearly too large. The problem is easily fixed
by ignoring the higher bits. The exact bit mask is an educated guess. A
12 bit value is plausible because it's also used for the sample depths.
Select the default memory layout for unsupported devices based on the
amount of memory indicated in the version string. This allows to
download a full memory dump.
In freedive mode the sample rate is only 1 second instead of 4 seconds.
The tank pressure fields appear to be re-used for some other (unknown)
purposes, and should be ignored.
The gauge and freedive mode bits are stored in the settings field. The
location of the gauge bit appears to be the same for all models. For the
freedive bit it depends on the model, and also not all models support
it.
The dive time is stored in two separate bytes: one for the number of
minutes, and another one for the hours. Since we only took into account
the first byte containing the minutes, the divetime for dives longer
than one hour was always too short.
The Aeris 500AI serial number appears to be located at a slightly
different offset, and with an unusual encoding. It's a BCD encoded
number, but with the digits of each byte swapped.
At the moment, the encoding of the serial number is tied to the global
pointer mode. To support devices where this is no longer the case, a new
entry for the serial number encoding is added.
The latest v2.09 (hwOS) and v1.1.0 (hwOS4) firmware introduced a new
variant of the hardware descriptor command. This extended command
returns:
- a 16 bit hardware descriptor
- a 16 bit feature descriptor
- an 8 bit model descriptor
The hardware descriptor is backwards compatible with the existing one
(upper bits are all zero for now). For the OSTC4, the feature descriptor
is currently used to indicated the Bonex scooter version (0x01), and the
model descriptor is 0x43. For all other models, the feature and model
descriptors are always zero for now.
For older firmware versions, which do not support the new extended
descriptor yet, there is an automatic fallback to the previous
descriptor.
Due to a firmware bug, the deco/ndl info is incorrect for all OSTC4
dives with a firmware older than version 1.0.8. Since it's not possible
to correct the info again, it's ignored and not returned to the
application.
The OSTC4 firmware version uses four digits for the firmware version
(X.Y.Z.Beta), while all other hwOS models use two digits (X.Y). To
preserve backwards compatibility with the existing two byte data format,
the OSTC4 firmware version is packed into a 16 bit integer as follows:
XXXX XYYY YYZZ ZZZB
and stored with little endian byte order.
To be able to pass the OSTC 3 model number to the parser, and preserve
backwards compatibility, we need a new function. The new function should
also be used for the Frog, by passing zero as the model number.
Using the hw_ostc_parser_create() function with the hwos parameter set
to one, is now deprecated but will remain supported for backwards
compatibility.
The frog parameter and field are not only used for the Frog, but also
for all hwOS based models. Therefore, using the more generic hwos as the
name is more meaningful.
For applications supporting offline parsing (like libdivecomputer's own
dctool application), some device specific knowledge is still required in
order to map a particular model to the corresponding backend. The new
convenience function will take care of that internally.
The already existing dc_parser_new() function does the same, but
requires an open device handle, which makes it unsuitable for offline
parsing.
When the device family is provided without an explicit model number, we
simply choose the first available model. But since new models are being
added all the time, this default model is not guaranteed to remain the
same. That's not desirable because it can alter the behaviour of the
application.
The introduction of the Aeris 500AI is an example of this problem. The
default model in the vtpro family used to be the Oceanic Versa Pro. But
because the Aeris 500AI has a lower model number, it automatically
became the new default model. Since both use a different protocol
variant (MOD vs INTR) they are not interchangable.
The default model is now hardcoded. The best option is of course to
provide the model number explicitly!
The large and complex oceanic_common_device_foreach() function is
refactored into two independent helper functions. One for reading the
entries in the logbook ringbuffer, and another for reading the profile
data.
The Aeris 500AI is quite different from the other vtpro compatible
models. First, it uses the INTR protocol variant. Next, it doesn't
appear to have a logbook ringbuffer. Instead it supports a new read
logbook index command (0x52) that returns the logbook entries. This
requires a custom implementation of the logbook function.
There are two variants of the vtpro communication protocol: the existing
MOD variant and the new INTR variant. The main difference is in the
initialization sequence, with two completely different response packets:
MOD--OK_V2.00
INTR-OK_V1.11
The INTR variant does not appear to return useful information in the
version packet.
By adding the logbook and profile functions to the vtable, a dive
computer backend can now easily replace the default implementation with
a custom one, without having to duplicate the common code.
To guard applications from divisions by zero, the progress event code
asserts the maximum value should always be non-zero. The eonsteel does
trigger this assert when there are no dives present.
Mares Darwin compatible devices support a nitrox mode. The nitrogen
percentage should only be taken into account when the dive mode is set
to nitrox, because the last used value remains in place for air dives.
Errors reported by system calls are now converted to the corresponding
libdivecomputer status code. This results in a more descriptive and
meaningfull return value.
The low level serial and IrDA functions are modified to:
- Use the libdivecomputer namespace prefix.
- Return a more detailed status code instead of the zero on success and
negative on error return value. This will allow to return more
fine-grained error codes.
- The read and write functions have an additional output parameter to
return the actual number of bytes transferred. Since these functions
are not atomic, some data might still be transferred successfully if
an error occurs.
The dive computer backends are updated to use the new api.
The devinfo event with the device serial number is required for the
fingerprint feature. Without this event, applications won't be able to
load (or save) the correct fingerprint. All necessary information is
already available in the initial handshake packet.
A new buffer is allocated for each dive, but only the last one is freed.
Since the code is already prepared to simply re-use the same buffer,
there is no need to allocate those extra buffers.
Just like the D4i and D6i, the new header is a few bytes larger. The
correct variant can again be detected by means of the logbook id tag at
the start of the header.
Libdivecomputer always uses metric units internally. But when reverse
engineering a device that stores everything using imperial units, it's
very convenient to be able to switch the output to imperial units too.
The Shearwater devices support adding, removing or editing gas mixes
during the dive. The pre-defined gas mixes available in the opening and
closing block are only a snapshot of the configuration at the start and
at the end of the dive. Thus by editing the gas mixes during the dive
it's possible to switch to a gas mix that is not present in the opening
(or even the closing block). The parser doesn't support that.
To avoid this problem, we now collect the available gas mixes from the
sample data. As a side effect we only return those gas mixes that are
effectively used during the dive.
One of the newer D4i and D6i firmware versions (for example v1.5.9),
introduces a new variant of the data format. The new dive header is 8
bytes larger. The correct variant can be detected by means of the
logbook id tag at the start of the header.
By reading the hardware descriptor immediately after entering download
or service mode, we can identify the specific model and adapt to minor
differences in the communication protocol.
For the OSTC3 compatible devices, a missing initial gas mix (e.g. no gas
marked as the first gas) leaves the initial gas mix index at its default
value of zero. This is different from the OSTC2 compatible devices,
where a missing initial gas is stored as the value 0xFF.
By initializing the index with the value 0xFF, the existing detection
works for both variants.
The existing output code is removed and replaced with the new XML and
RAW output formats. The desired output format can be selected with a new
command-line option. The XML format remains the default output format.
The RAW output format exports each dive to a raw (binary) file. To
output multiple files, the filename is interpreted as a template and
should contain one or more placeholders.
The new output interface provides the necessary infrastructure to add
support for multiple output formats. Due to the abstract interface, each
new format will require only minimal changes in the application itself.
On BSD based operating systems (which includes Mac OS X), the getopt()
function is posix compliant and thus the option processing stops when
the first non-option is found. But the getopt_long() function permutes
the argument vector, just like the GNU implementation.
Using a leading '+' character in the option string disables the
permutation again.
Unlike the other socket functions, the WSAStartup() function returns the
extended error code directly. A call to the WSAGetLastError() function
is not needed and should not be used.
In freedive mode, samples are only 2 or 4 bytes large, thus a sample
containing all 0x00 bytes represents a zero depth value and not some
invalid data that should be ignored.
I originally assumed the second byte contains the hour, but that turns
out to be wrong. At least some of the bits have a different meaning.
With only seconds and minutes, the maximum divetime is limited to at
most 99 minutes and 59 seconds. That shouldn't be a problem for
freedives.
The Oceanic and Aeris F11 have a configurable sample rate. The possible
sample intervals are 2, 1, 0.5 and 0.25 seconds. Since our smallest unit
of time is one second, we can't represent the last two, and the extra
samples will get dropped for now.
Both the allocation and initialization of the object data structure is
now moved to a single function. The corresponding deallocation function
is intended to free objects that have been allocated, but are not fully
initialized yet. The public cleanup function shouldn't be used in such
case, because it may try to release resources that haven't been
initialized yet.
Instead of freeing the object data structure in the backend specific
cleanup function, the memory is now freed automatically in the base
class function. This reduces the amount of boilerplate code in the
backends. Backends that don't allocate any additional resources, do no
longer require a cleanup function at all.
When the close function returns, all resources should be freed,
regardless of whether an error has occured or not. The error code is
purely informative.
However, in order to return the first error code, which is usually the
most interesting one, the current implementation is unnecessary
complicated. If an error occurs, there is no need to exit immediately.
Simply store the error code unless there is already a previous one, and
then continue.
The universal application works well, but is quite difficult to extend
with more functionality. Therefore a new and more modular application is
needed. The new dctool application will support multiple sub-commands,
to carry out specific actions. Extending the application will be as easy
as adding new commands.
To store the day (range 1 to 31) as a binary encoded value, only 5 bits
are required. The extra 6th bit is part of the year. The year is also
not BCD encoded. This happened to work by accident, because for a single
nibble, the current implementation of the bcd2dec() function returns the
binary value.
The new gasmix sample contains the index of the active gas mix.
This new sample is intended as a replacement for the existing gas change
events (SAMPLE_EVENT_GASCHANGE and SAMPLE_EVENT_GASCHANGE2). To maintain
backwards compatibility, the legacy events are marked as deprecated but
not removed yet.
In CC mode, only the diluents are stored in the header. The list with
the OC gas mixes, which are used for bailout, are not stored in the
header. In order to retrieve the bailout mixes too, we need to parse the
profile and add them to the manual gas mixes.
The protocol of the iX3M series is almost identical to the protocol of
the iDive series. The main difference is that the command bytes and the
size of the response packets have been changed. In order to be able to
communicate with the correct set of commands, the user needs to supply
the correct number now. To maintain backwards compatibility, a new
variant of the open function is added.
Apparantly Fedora applies a custom patch to glibc's tcsetattr()
function, which adds an extra check to verify the PARENB/CREAD/CSIZE
bits in the termios c_cflag field.
However, in commit 197b9f09421111e03588c94d55a72aa6ec624c63 we already
discovered that for pty's, some of the termios settings make no sense at
all, and therefore the Linux kernel always does:
tty->termios.c_cflag &= ~(CSIZE | PARENB);
tty->termios.c_cflag |= (CS8 | CREAD);
Thus, instead of ignoring such nonsense termios settings, the kernel
changes the termios structure to reflect what pty's actually do. The
consequence is that these settings will not stick, and cause the extra
check in the Fedora specific patch to fail.
To workaround this problem, we ignore the error when building
libdivecomputer with pty support enabled.
If the dive computer has not recorded any dives yet, the profile pointer
isn't valid and contains the default value 0xFFFFFFFF. There is no need
to return an error in this case.
In some rare cases, the initial gas mix contains the value 0xFF. This
value is obviously outside the expected range (1-5), and therefore
causes the parsing to fail. It's not really clear how this can happen.
As a workaround for the fatal error, we now ignore the invalid value and
simply proceed without a gas mix.
The dive computer records the raw absolute pressure. To convert this
pressure value into a depth, we need to divide by the salinity factor,
not multiply!
The GCC 5 compiler with -Wpedantic enabled generates warnings for the
non-standard predefined identifier __FUNCTION___. These warnings can be
avoided by using the C99 identifier __func__ instead.
The internal Uwatec tank id should be converted to the libdivecomputer
tank index. If there is no corresponding tank, the tank pressure samples
are dropped for the following reasons:
Some models appear to record an absolute tank pressure sample, even if
there is no pressure sensor attached to the corresponding tank. In this
case only the tank index changes. The sample value simply retains the
last pressure of the previous tank. Since we don't have any real
pressure data, dropping those samples is fine.
Dives are downloaded using bulk transfers with an 8K buffer. Normally
the 2 second timeout is more than sufficient, and the timeout should
never expire, unless there is some serious communcation problem. But
nevertheless, users are reporting timeouts for dives having a length
that is an exact multiple of the USB packet size (64 bytes). In that
case, libusb reports a timeout with an non-zero amount of bytes
received. Despite the timeout, the received data contains a complete
dive.
I suspect libusb is somehow unable to determine whether the transfer is
complete and therefore waits until the timeout expires. For transfers
that are not a multiple of the USB packet size, the end of the transfer
is indicated by the last incomplete packet. This is not the case if the
length is an exact multiple of the USB packet size. This problem is
usually solved by sending a zero-length packet. Maybe the USB stack of
the Cobalt is not sending such a zero-length packet?
Atomics will address the problem with a Coblat 2 firmware upgrade, that
will simply append two zero bytes if the length is a multiple of 64
bytes. As a workaround for older firmware versions, we ignore the
timeout and process all received data. This shouldn't have any
disadvantages. An incomplete dive, for example due to a real timeout,
will now be detected by means of the minimum length and/or the checksum.
The Mares Smart Apnea uses a different data format than the regular
Smart, because it records not only a summary of each freedive in the
session, but also a full depth profile.
Because both the regular Smart and the Smart Apnea have the same model
number (0x10), another way to distinguish the two variants is needed.
Therefore, the Smart Apnea gets a modified model number, with one of the
higher bit set. The actual type is detected from the contents of the
version packet.
The new Smart Apnea is also capable of recording multiple samples per
second (e.g. 2, 4 or 8). But since our smallest unit of time is one
second, we can't represent this, and the extra samples will get dropped
for now.
The Galileo Trimix supports up to 10 tanks and gas mixes. However, the
existing alarm based gas switch events have only 2 bits available, and
can support at most 4 gas mixes. Therefore, the trimix variant stores
another 4 bit value in the second alarm byte.
For the first three gas mixes (and possibly also the fourth), both alarm
bytes appear to be always set to the same value. For the higher mixes,
the value in the first alarm byte is always zero. This doesn't cause any
problems, because in the data stream the second alarm byte is stored
after the first one, and our final value is always the last one.
The non-trimix variant also has the second alarm byte, but the gas mix
bits appear to be always zero. In order to avoid taking this zero as the
final value, a separate table is used for the trimix variant.
The air integrated uwatec aladin models do record the total consumption,
and not the begin/end pressure. Returning the total consumption as the
begin pressure, combined with a zero end pressure is strictly speaking
not correct, but it still provides useful information.
Even if there are no O2 sensors connected (for example in auto or fixed
setpoint mode), the device records a ppO2 sample with all three values
set to zero. Such samples are now ignored, as if there was no ppO2
sample present.
Reported-by: Anton Lundin <glance@acc.umu.se>
The Atomics Cobalt backend uses libusb directly, without going through
an internal I/O layer that support logging. Therefore the logging needs
to be done in the backend itself.
The logbook ringbuffer starts at address 0x03E0 instead of 0x240. Since
none of the other models uses the same address, a completely new layout
structure is necessary.
The begin/end pressure for unused tanks is normally zero. But I noticed
that in some cases both pressure values are stored as 0xFFFF. Since that
corresponds to a pressure of 511.99 bar, this is most likely some
special magic value, and not a valid pressure.
Tanks where either the begin or end pressure is 0xFFFF are now ignored
too.
When the PPO2 sample was introduced, we didn't take into account the
fact that rebreathers usually support multiple O2 sensors. The HW OSTC
supports for example three sensors. In order to support multiple values,
without having to introduce a new data structure with an additional
sensor id field, we simply relax the assumption of allowing only one
DC_SAMPLE_PPO2 value per sample. Applications that are not prepared for
multiple values, will automatically use only one of them (probably the
last one).
With firmware version 1.84 (beta), a more compact logbook header is
introduced. Due to their smaller size (16 vs 256 bytes), the total
amount of data that needs to be transferred is much smaller (4K vs 64K).
Therefore, downloading these compact headers is roughly 16 times faster
then downloading the full headers.
For backwards compatibility with older firmware versions, there is an
automatic fallback to the full logbook headers.
The Shearwaters store the current dive mode for each sample, and not one
single value for each dive. Therfore we need to process all samples. If
there is at least one sample marked as closed circuit, we classify the
dive as a closed circuit dive. Otherwise we default to open circuit.
For devices without a logbook ringbuffer, such as the Oceanic Veo 1.0
and the Aeris XR-1 NX, the ringbuffer begin and end are identical. In
this case, the changes in the previous commit will always result in a
fatal error due to an invalid ringbuffer pointer. To avoid the error, we
exit before trying to use the pointers.
Until now, an invalid logbook pointer was silently ignored and handled
as an empty ringbuffer. But this hides real errors, which is worse than
failing if no dives are present. Trying to download dives from an empty
device should be a rather uncommon scenario anyway.
Now that we are able to parse the event bytes properly, there is no need
to pass the raw event bytes to the application.
In the current implementation, the vendor event was broken for devices
with multiple event bytes anyway. Because we deliver all event bytes at
once, the application doesn't know which bytes contain a valid value
(originating from the profile data) or just a dummy zero value.
Right now only gas switch and bookmark events are supported, because
none of the other uwatec events can easily be mapped onto the existing
libdivecomputer events. But the basic infrastructure for supporting more
events is in place now.
For obvious reasons, the -static-libgcc linker option is not supported
by non gcc compilers, like clang. Since we only need this option when
building Windows DLL's, we can easily avoid the problem with a
conditional on the mingw compiler.
The Shearwater Petrel 2 uses exactly the same communication protocol as
the original Petrel. Therefore the new model has always been supported
out, and we only need to add the new model to the list of supported
devices.
The id of the uwatec deco mix is always 2, even for devices which
support maximum two gas mixes. Strictly speaking, this change is more
correct than commit 4fd825cdac341a2d4a1366c6deb7d4a71bbdf94d, allthough
in practice they are equivalent.
The bitmask for the gas mix bits was wrong. With the new mask we also no
longer have to disable the alarm based gas switches for the Meridian and
Chromis.
Originally I assumed it's not possible to enable a gas mix if the
previous gas mix has already been disabled. However, this assumption
turns out to be wrong. For devices with support for 3 gas mixes, it's
possible to enable only the first and the third gas mix, and leave the
second gas mix disabled.
This is fixed by checking all gas mixes, instead of aborting once the
first disabled gas mix has been found. Due to this change the uwatec gas
mix id's are no longer sequential and need to remapped to the
corresponding internal array index.
It looks like the SAMPLE_EVENT_AIRTIME is actually meant for just
airtime warnings (as in the dive computer warning about low air).
If we ever care, I think we'd need to add a new interface for this.
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
Now that I actually have dives with the new format and with air
integration, I could test it all out and noticed some missing pieces.
This adds parsing for gastime and ventilation events (although we don't
*do* anything with the ventilation data, I have no idea what the number
means).
Also, this fixes an annoying warning message problem, which caused these
missing events to cause a SIGSEGV rather than just a bening warning.
Stupid bug, and only went unnoticed because libdivecomputer isn't built
with format string warnings.
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
This gets rid of the fixed sample indexes and the hardcoded grouping
code, and replaces them with proper parsing of the type descriptions.
This should mean that the new Suunto firmware v1.1.15 is now fully
supported by libdivecomputer.
There are still parts of the event description that we should really
parse better, notably the 'enum' descriptions of what the different
enumerated types mean, because it looks like those will change too. But
that is not nearly as important as getting the basic infrastructure done
for the core sample types.
Almost accidentally, this also ends up now parsing the compass heading
event.
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
I was initially fooled into thinking that the field type numbers have
some meaning: the types didn't change if the upper byte of the type
number was zero. So I assumed that meant "fixed".
But the most recent firmware update made clear that no, they aren't
fixed, and the upper byte of the type must be some other thing.
This moves some more of the parsing over to comparing the strings,
rather than looking at the type index. It still leaves the sample data
alone, and I really want to do something more efficient than comparing
the type descriptor string for that, but at least the dive header fields
are now just comparing strings.
The actual marshalling that Suunto uses also describes the encoding, and
it's all ignoring that for now.
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Suunto's new v1.1.15 firmware ends up terminating some final descriptor
lines with a newline, rather than just using newlines as separators. So
the last newline may not be followed by further data, but simple be the
end of the string. Accept that case.
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This is some small cleanup after the whole reply size rewrite. It
further improves on the error log reporting a bit, and it undoes the
"read exact size" thing introduced in "suunto eon steel: fix file
reading special case", because it is no longer necessary.
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
When reading data from the EON Steel, we'd generally continue reading
until we saw that a response was done by seeing a packet that wasn't
full.
That broke for the case of the data boundary matching the packet
boundary, fixed by the commit "suunto eon steel: fix file reading
special case".
However, that commit only fixed it for the case of reading a file, where
the result has a size that is known up-front. And most other situations
really don't matter, because the result size is fixed and fits in a
single packet, so it all works.
However, there are still a few cases that could trigger the problem,
notably reading the directory contents.
So change the send_receive() logic to actually read the expected size
from the receive header in the first packet of the reply. This means
that we need to re-organize the packet reception code a bit, but the end
result is that we are much more careful about data sizes,
This also changes the packet logging to be much more readable, by
logging just the actual data, and not the (uninteresting) per-packet
header, or the stale data at the end of the packet.
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
The "receive_data()" function would continue to try to read packets as
long as the previous packet was full-sized, but with just the right size
of file and the right chunking, the file might end at a packet boundary.
Then receive_data() would try to read more data, which fails - there are
no more packets, despite the last packet being full.
This never triggered for me, but Robert Helling forwarded a data dump of
a filure to read a dive due to this.
Since I don't trigger this case, I can't really test it, but I did check
that the new "stop early" logic works for me (ie never triggers ;).
Reported-by: Robert C. Helling <helling@atdotde.de>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
When compiling a 32bit dll with the mingw-w64 compiler, some 64bit
integer arithmetic operations are implemented using functions from
libgcc (e.g. __divdi3 and __moddi3 from libgcc_s_sjlj-1.dll). This
unexpected dependency is inconvenient for applications.
The run-time dependency can be avoid by linking statically.
reefnet_sensusultra_device_set_maxretries was killed off back in v0.3,
but was still lying around in the public header.
Signed-off-by: Anton Lundin <glance@acc.umu.se>
At least one Oceanic Geo is unable to download the last memory page.
However, another device with an older firmware version (version 1D
instead of 2B), does not have this problem. So in theory the correct
layout depends on the firmware version.
But since the profile ringbuffer also appears to end at the address
0xFE00 instead of 0x10000, we'll never need to download that last page
in practice. Thus we can be lazy and simply use a single layout for all
firmware versions. The only drawback is that the last page might be
missing when downloading a full memory dump.
Applications are supposed to lookup the fingerprint when the devinfo
event is emitted. For the Uwatec Smart devices, the fingerprint is the
raw device timestamp. But because this timestamp was already used to
initialize the command before emitting the event, an outdated value
(typically the default value of zero) was used instead of the correct
one set by the application.
The Uwatec dive computers report the depth based on fresh water. Salt
water dives need a correction to take into account the salinity. There
is only a fresh/salt water flag present in the data, but Uwatec appears
to assume a salinity factor of 1.025.
The size of the dive header is one byte smaller: 0x32 instead of 0x33
bytes. Because we were expecting a larger packet, this bug caused the
downloading of the dive headers to fail. I'm surprised no one noticed
this mistake before.
With the new temperature and pressure field, artificially injecting the
temperature and pressure info from the header into the samples is no
longer necessary. Applications will typically expect a real temperature
or pressure profile, and not a few discrete points.
The two surface samples (with zero depth) at the begin and end of the
dive are kept for now, to support events that occur at the surface.
I forgot to include the Suunto Solution part in the previous commit.
In theory the Suunto Solution (and Eon) can't be affected by the ghost
event bug. All possible events bytes are already exhausted, and
therefore unknown events are impossible. But applying the fix anyway
doesn't hurt and keeps the code consistent with the other backends.
The newest Suunto models (e.g. D4i, D6i, D9tx and DX) support a few
additional events (type 0x15 and higher), which are not supported yet
because their interpretation isn't known.
Due to a nasty bug, these unkown events result in "ghost" events. When
such an unknown event is encountered, the sample type field isn't set
explicitely. Therefore it simply retains the value from the previous
sample, whatever that might be. If the previous sample happens to be an
event as well, then the unknown event will show up as a duplicate event.
But if the previous sample is not an event, then the resulting event
type is undefined.
This is fixed by always resetting the event type explicitely. Those
unknown events are also suppressed now and no longer delivered to the
application.
Allthough I haven't observed this bug with the Suunto Eon and Vyper,
they could be affected too.
The DC_FIELD_GASMIX api doesn't support the 6th manual gas very well.
Manual gas mixes are either not taken into account at all (OSTC3), or
only the last value is returned (OSTC2).
We now parse the profile data in order to retrieve all the manual gas
mixes too. Note that manual gas mixes are only included when they are
actively used during the dive. This is a small change in behaviour for
the OSTC2.
To simplify the implementation, there is an upper limit of 10 manual gas
mixes (or 15 gas mixes in total). This is an arbitrary choice, which
should be more than sufficient in practice. If it turns out to be too
small, we can always increase the number, or even make it truely
unlimited.
This adds a dump function for the ostc3 series computers. This function
dumps the whole external eprom, that contains the dive-headers and the
dive data.
Signed-off-by: Anton Lundin <glance@acc.umu.se>
For the IQ-700, the existing code for the active gas mix always results
in an out-of-range gas mix index. The index of the active gas mix is
probably stored in another bit. As a temporary workaround, we simply
assume the active gas mix is always the first gas mix. This should
already produce correct results for dives with only single gas mix.
The Tusa IQ-700 is very similar to the other Seiko based models. The
most important change is that due the smaller amount of memory (8K vs
32K), the logbook entries are only 1 byte large instead of two bytes.
Currently the profile ringbuffer starts at the base address 0x4000, but
I believe the real start is one 0x20 byte page earlier, at 0x3FE0. I
have two reasons for this:
1. To locate the start of a dive, we always have to substract one page
from the pointers in the logbook ringbuffer. With the new base address,
they would point directly to the start of the dive, which makes a lot
more sense.
2. When comparing the divetime as stored in the header with the one
obtained by counting the number of samples, they always match except for
dives that span the ringbuffer wrap point. If those extra 0x20 bytes are
included, the counts do match again.
Unfortunately, this change breaks the assumption that the ringbuffer is
aligned to packet boundaries. As a workaround, we define a virtual
ringbuffer that is slightly larger than the actual ringbuffer, but
properly aligned. Data outside the real ringbuffer is downloaded and
then immediately dropped.
Packets have a fixed size of 0x80 bytes, while a single page is only
0x20 bytes long. Thus each read operation always returns 4 pages at
once. Now, if the end-of-profile pointer is not nicely aligned on a
packet boundary, then the download algorithm won't arrive exactly at the
start address of the ringbuffer, because the ringbuffer is properly
aligned. The consequence is that we won't even notice we reached the
ringbuffer boundary and happily continue reading outside the ringbuffer.
Oops!
This is fixed by aligning the end-of-profile pointer, which guarantees
that all read operations are now nicely aligned to packet boundaries.
When trying to send a command, the first attempt always fails. We
receive the echo, but no data packet. A second attempt usually works,
but we always get back the same data packet. That's cleary wrong.
Now, when comparing the data packets with those of the Tusa application,
I noticed something very interesting. When we request the first packet
(page 0x0000), we get:
W: 520000
R: 520000
R: 00880124056202000250002890470824...19202720002000200020002000204145
The Tusa application also request this page, but the response is
completely different:
W: 520000
R: 520000
R: 22182224222322092203220522112210...0000000000f021fc0000000000000045
The response we get is identical to the response that the Tusa
application gets for page 0x0052:
W: 520052
R: 520052
R: 00880124056202000250002890470824...19202720002000200020002000204145
The only difference here is the echo of the command. But the echo should
be ignored, because it's generated by the pc interface, and not send by
the dive computer. This is easily verified by the fact that we always
receive an echo, even without a dive computer connected (e.g. only the
pc interface).
Notice how the command type (first byte) and page number (last byte) are
identical (0x52) for this request! I suspect that somehow the command
type ends up being interpreted as the page number. That would explain
why we're always getting the same response: as far as the device is
concerned we're always requesting page 0x52. This is probably also
related to the fact that the device doesn't respond after the first
request. It's not impossible that if the first command wasn't received
correctly and we resend the command, the device receives something that
contains parts of both attempts.
By sending the command and reading the echo byte by byte instead of all
at once, the above problem disappears.
Without the delay, the communication immediately fails. We receive the
command echo, but not the actual data packet. I suspect the device is
still be busy with the initialization and needs a bit more time before
it's ready to accept a request.
We received data from a Mares Smart where the existing heuristics to
detect the last dive are not sufficient. The very last check, where the
calculated and stored are compared results in a fatal error.
The Mares Smart supports not only scuba diving, but also freediving.
Because the freedive data format is fundamentally different from the
regular dives, being able to detect the type of dive in advance is very
important. For that reason, Mares moved the 4 bytes containing the dive
mode and number of samples from the beginning of the header to the end.
Except for this 4 byte shift, there are no changes for regular dives.
For the freedives, there is no real profile available, only a summary of
the entire freedive session. In an attempt to workaround this
limitation, we generate a pseudo profile in exactly the same (but ugly)
way as is done in the Mares Nemo backend.
The new OSTC3 (model 0x1A) is identical to the existing OSTC3, except
that the usb interface has been replaced with a bluetooth interface.
The new OSTC2 (model 0x1A) will retain the look and feel of the OSTC 2C
(e.g. plastic housing, piezo buttons and rechargable battery), but under
the hood it will use the same firmware as the OSTC3. It will also have a
bluetooth interface.
The gas mix index stored in the alarm bytes is occasionally out of range
(larger than the number of available gas mixes). The index refers to one
of the disabled gas mixes. Since those are excluded now, this results in
a fatal error instead of a switch to a nonsense gas mix.
To workaround this problem, the alarm based gas switches are disabled
until we figure out how to parse them correctly.
The Chromis is almost identical to the Meridian, and will probably need
the same workaround. Although I don't have any data to confirm this,
let's just keep the two in sync.
Gas mixes are disabled by setting their oxygen percentage byte to 0x00.
This is clearly an invalid gas mix, and it makes no sense to return it
back to the application.
It seems the device doesn't allow you to enable a gas mix if the
previous gas mix has already been disabled. Therefore we can simply stop
parsing the gas mixes once the first disabled gas mix has been found.
According to the technical specifications, the Uwatec Aladin Tec 2G
supports maximum two gas mixes. The data appears to confirm this,
because the extra third gas mix always contains unrealistic oxygen
percentages.
However, I came across some data containing gas switches to the third
gas mix. The interesting part is that according to the Uwatec
application, this is actually a switch to the second gas mix in the
header. One possible explanation is that for models with up to 3 gas
mixes, they are labelled respectively "bottom", "travel" and "deco" mix.
But the documentation for the Aladin Tec 2G only refers to the bottom
and deco mix. So it might be that internally the index of the deco mix
is always the 3th mix, regardless of whether a travel mix is supported
or not.
If the only allowed values for the gas mix index are 0 (for the bottom
mix) or 2 (for the deco mix), then manually remapping the deco mix is
equivalent with ignoring the lowest bit. This has the advantage that the
required bitmasks and shifts are no longer different from those for the
other models.
For the Galileo Trimix we don't know yet where and how the gas mixes are
stored. Right now, we just pretend there are no gas mixes available,
which is misleading.
Originally, I assumed that the trimix firmware update changed the model
number from 0x11 (Galileo) to 0x19 (Galileo Trimix). But that assumption
appears to be wrong because I received data from a Galileo with model
number 0x11, but with the trimix data format. Another explanation might
be that the trimix data format is not specific to the trimix firmware.
Anyway, this is easily fixed by treating both models identically.
The latest firmware v1.75 introduced a new hardware descriptor byte to
identify the different models based on their hardware features. This new
hardware descriptor is now used as the libdivecomputer model number. For
older firmware versions, which do not support the descriptor yet, there
is an automatic fallback to the previous method based on the serial
number.
When trying to send an unsupported command, the ostc will simply ignore
the command. Instead of echoing the command byte back, the ostc will
immediately send the ready byte, to indicate it's ready to receive the
next command. We can use this to detect unsupported commands, because
the ready byte is a reserved value and guaranteed to never be a valid
command byte.
Normally we don't send invalid commands. But newer firmware version can
always introduce new commands to support new features. To maintain
backwards compatibility with older firmware versions, it's important to
be able to detect unsupported commands and provide a fallback.
In freedive mode, the Oceanic Veo 2.0 and 3.0 have samples that are only
4 bytes long, instead of the normal 8 bytes.
The Oceanic VT3 and Hollis DG03 use the same logbook layout, and
although I haven't been able to confirm this with real data, it's very
likely they need the same fix.
The Oceanic VTX is very similar to the Aeris A300 CS. The main
difference is the higher baudrate (115200 vs 38400). This is annoying
because without the correct baudrate, no communication is possible and
thus the existing autodetection based on the version packet doesn't
work. As a workaround, we now rely on the model number from the device
descriptor. The consequence is that the user must select the correct
model now (or at least a model with the same baudrate).
This change will be necessary for the Oceanic VTX, because it uses a
higher baudrate.
To maintain backwards compatibility, the existing function remains
unchanged and a new function is introduced instead.
At the moment, there is only retry logic for the ACK/NAK byte. Corrupt
data packets (e.g. wrong checksum, unexpected length, etc) are treated
as a fatal error. By reading the entire packet inside the retry loop,
all non-fatal errors are automatically taken care off.
This change is necessary for the Oceanic VTX. For some unknown reason,
the device always responds with an invalid data packet when sending the
version command for the first time:
W: 84 00
R: 5A 4F ?? 45 41 4E 56 54 58 20 31 42 20 32 30 34 38 E9
There is always one byte missing (marked with question marks), resulting
in a timeout. However, when re-sending the command again, we receive a
valid data packet:
W: 84 00
R: 5A 4F 43 45 41 4E 56 54 58 20 31 42 20 32 30 34 38 E9
This might be a firmware bug, because we're observing the exact same
behaviour with the official Oceanlog application.
In freedive mode, the Aeris Elite T3 has samples that are only 2 bytes
long, instead of the normal 8 bytes.
Since there are many other models which support a freedive mode, the new
implemention provides the necessary infrastructure to easily integrate
those other models too.
The OSTC cR is new model for closed circuit (CCR) diving. Under the hood
it's identical to the OSTC 3, but with an S8 connector to interface with
an external oxygen sensor. Since the data format already supports oxygen
sensors, the OSTC cR can be added to the list of supported devices
without any further changes.
The EON Steel notifications and states match the libdivecomputer ones
very badly, but this tries to make sense of the ones that match. And
puts the infrastructure in to do others in the future.
Signed-off-by: Linus Torvalds <torvalds@linux-fouindation.org>
The really sad part is that the EON Steel handles gas change events
correctly, by actually saying which cylinder it switches to. But the
libdivecomputer interfaces are broken, and only contain the gas *mix*
you switch to, which is ambiguous since you could have the same mix in
multiple cylinders.
Maybe we could put the one-based cylinder index into the "flags" field?
With zero meaning "unknown". That would be a straightforward extension.
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
The OSTC3 can display a status message of up to 16 characters large.
Since this does not include the terminating null character, the buffer
needs to be one byte larger.
The new implementation is similar to the already existing code for
reading Intel HEX files. It can handle arbitrary line endings, and not
just CRLF or LF.
This simplifies the code to check and handle state switching for the
OSTC3.
Suggested-by: Jef Driesen <jef@libdivecomputer.org>
Signed-off-by: Anton Lundin <glance@acc.umu.se>
This connects the bits and implements firmware upgrade for the OSTC3.
This code is inspired by JeanDo ostc-companion.
Reviewed-by: Jef Driesen <jef@libdivecomputer.org>
Signed-off-by: Anton Lundin <glance@acc.umu.se>
This function triggers a reboot into the bootloader which flashes the
new firmware to Prom.
This code is inspired by JeanDo ostc-companion.
Reviewed-by: Jef Driesen <jef@libdivecomputer.org>
Signed-off-by: Anton Lundin <glance@acc.umu.se>
This is how you transfer a new firmware to the OSTC3.
This code is inspired by JeanDo ostc-companion.
Reviewed-by: Jef Driesen <jef@libdivecomputer.org>
Signed-off-by: Anton Lundin <glance@acc.umu.se>
This is necessary to verify that the memory written got transfered
correctly.
This code is inspired by JeanDo ostc-companion.
Reviewed-by: Jef Driesen <jef@libdivecomputer.org>
Signed-off-by: Anton Lundin <glance@acc.umu.se>
This is the fist step in the firmware upgrade process.
This code is inspired by JeanDo ostc-companion.
Reviewed-by: Jef Driesen <jef@libdivecomputer.org>
Signed-off-by: Anton Lundin <glance@acc.umu.se>
This lifts the OSTC3 INIT command out from the open function and does
that separately. This is refactoring to be able to enter service mode so
we can access service mode commands.
Reviewed-by: Jef Driesen <jef@libdivecomputer.org>
Signed-off-by: Anton Lundin <glance@acc.umu.se>
This imports Tiny AES128 from https://github.com/kokke/tiny-AES128-C for
use in the decoding of OSTC3 firmwares.
This aes-code is released into the public domain.
Signed-off-by: Anton Lundin <glance@acc.umu.se>
At least one dive computer in the Suunto Vyper family, the Vytec DS, is
capable of recording gas mix switches, and therefore supports more than
one mix. When going through the dive profile caching process, also look
for gas changes and record how many mixes were used in the dive. Mix 1
is always the main tank and it’s O2 value can be found in the dive
header, but the other tank O2 values are not available ahead of time. By
reviewing the dive profile ahead of time, the entire list of mixes can
be returned to the application at the beginning of the dive as per
usual.
The code to calculate parameters like maximum depth and divetime is
duplicated in multiple places. Move this code to a single place, and
cache the data in the parser instead.
This is only the silence the "enumeration value not handled in switch"
compiler warning. There is already a check earlier on to take care of
unsupported fields, but the default case is a good practice anyway.
The array with type descriptors is populated with dynamically allocated
strings, but they are never freed anywhere. To be able to free those
strings easily, they are now initialized with NULL pointers instead of
zero length strings.
This avoids some more compiler warnings regarding incompatible pointer
types (e.g. signed vs unsigned char pointers). Arrays have the
additional advantage of the sizeof operator. This eliminates an
unnecessary strlen() call, and the strcpy() call can be replaced with
memcpy().
This avoids some compiler warnings. The type of the string literal is a
const char pointer, while the send_cmd() function expects an unsigned
char pointer.
Since we're dealing with byte arrays, there's no need to use void
pointers. Using unsigned char pointers also eliminates some compiler
warnings for pointer arithmetic on void pointers.
There is no need for custom logging functions, because libdivecomputer
already has an extensive logging infrastructure, featuring conditional
compilation, multiple loglevels, customization by the application, etc.
All entry point functions (e.g. public functions or functions called
through the vtable) use the backend name as the prefix. The same applies
to the main device and parser structures.
Basic Suunto EON Steel downloading copied from my test application.
This parses all the core dive data, including sample data (time, depth,
cylinder pressure, deco information etc).
The deco information returns ceiling and TTS rather than ceiling and
"time at ceiling", because that's what the dive computer has, and I
don't see any other way to return the information.
We don't report any events yet, though.
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
There are two different ways to specify the volume of a tank. In the
metric system, the tank volume is specified as the water capacity, while
in the imperial system the tank volume is specified as the air capacity
at the surface (1 ATM) when the tank is filled at its working pressure.
To avoid mistakes, the tank volume is now always returned as the metric
volume. For imperial tanks, the tank volume is converted to the metric
representation.
The difference between CUFT@PSI and CUFT@BAR is only for the Cobalt user
interface, to allow the user to enter the tank volume independent of the
units configured in the main preferences. Internally the working
pressure is always stored in psi.
Devices that only reports a single temperature in the header
will now be able to report it as well when they implement this.
Signed-off-by: Calle Gunnarsson <calle.gunnarsson@gmail.com>
When closing the slave side of a pseudo terminal, the exclusive access
mode will persists on the master side. The result is that re-opening the
slave side will fail with EBUSY, unless the process has root priviliges.
To workaround this problem, we already introduced an option that enables
better compatibility with pseudo terminals. See commmit
fab606b00a44ea2114a4029ad09b70c66c3049f7 for details.
In my development environment, I always have this option enabled. But
occasionally I also need to test release builds. And then I usually end
up with inaccessible pty's again, because the pty support is disabled by
default for release build.
This problem can easily be avoided by disabling the exclusive access
mode, just before closing the file descriptor.
Adds the new model and treats it like a Wisdom 2 (and many other, similar
versions of the Oceanic Atom2 style dive computers).
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
This change was already present in the oceanic_atom2_parser_get_field()
function, but not in the oceanic_atom2_parser_samples_foreach()
function. These two should always be kept in sync.
This fixes a bug in the tank pressure detection.
Most of the changes needed for the F11 are the standard model specific
tweaks. But the F11 also has another interesting "feature". If you try
to download a full memory dump using the standard B1 read command, then
the data starts to repeat after the first 64K. It seems that somehow,
the B1 command can only address the first 64K of the memory. To avoid
this problem, the newer B4 read command needs to be used instead.
This might be a firmware bug, or maybe internally they store the address
in a 12bit variable, which causes the upper bits to get lost? That would
explain the repeating data.
In bigpage mode, we always read large pages, regardless of the amount of
bytes requested by the caller. Excess bytes are simply discarded. This
is inefficient because the same large page will be downloaded multiple
times, when requesting more than one 16 byte page from the same large
page. By caching the pages internally, we can greatly reduce the amount
of I/O operations.
In practice, applications and also libdivecomputer's internal algorithm
for downloading the dives will typically request contiguous pages, so we
only need to cache the last page.
This implementation is based on ideas and code contributed by Dirk
Hohndel.
Encoded in every sample. The depth is in multiples of 10 feet which gives
somewhat odd metric stop depth - but rounding to full meters would take
care of that.
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
This is ignoring a ton of data that the dive computer provides. But it
gives profile, tank pressure and temperatures - so it's a start.
This patch adds a set_dtr and set_rts call to the serial interface prior
to interacting with the device. This change is required for the A300CS to
talk to the computer.
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
Some new Oceanic dive computers like the Aeris A300CS support a new read
command that always reads 256 byte pages instead of 16 byte pages, other
versions support reading 128 byte pages.
This patch adds a field to the oceanic_atom2_device_t structure to
indicate which type of device this is. If bigpage mode is enabled, the
algorithm will always request larger, aligned reads and fall back to
standard 16 byte reads otherwise.
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
The primary tank, which is the tank connected to the hose and thus the
tank pressure sensor, it not necessary the first tank. The correct tank
index can be found by searching the array for a gas mix with a sensor id
equal to one.
The gasmix value in the sample data is the id of the gasmix. Depending
how the user manipulated the gasmix settings, this id is not necessary
identical to the index into the gasmix array. The array index can be
found by searching the array for a mix with the correct id.
For the Tusa Zen, the header is 16 bytes smaller. With this change, the
gasmixes and the intial temperature are now parsed correctly.
The tank pressure samples have also been disabled manually, because this
is a non-air integrated model that doesn't seem to set the initial tank
pressure to the magic value 10000.
The 6 byte logbook entry gets prepended to the profile data. We need to
take this into account when allocating the memory buffer. Under normal
circumstances this shouldn't be a problem, because a single dive is
usually much smaller than the profile ringbuffer. But it's better to be
on the safe side.
The fingerprint is used unconditionally, regardless of whether it's
explicitly set by the application or not. Therefore it needs to be
initialized properly.
Although the OSTC Sport uses bluetooth communication, the new model
remains fully compatible with the OSTC3, because it provides a virtual
serial port interface. As usual, the new model can be detected based on
the serial number.
For a dive in gauge mode, the gas mixes defined in the header should be
ignored, and no gas change event should be emitted. This is done by
hardcoding the number of gas mixes to zero.
The code to find the offset to the footer and parse the gas mix data is
duplicated in multiple places. Move this code to a single place, and
cache the data in the parser instead.
Gas mixes that have been disabled are stored as a mix with 0% oxygen and
0% helium. This is clearly an invalid gas mix, and it makes no sense to
return it back to the application.
For the Nemo Air, a dive mode with the value 2 indicates gauge mode
instead of freedive mode. With this change, all models from the puck
family now use the same values.
With a time based sample interval, the maximum number of samples for a
single timestamp should be constant. However in practice some devices
occasionally store one or more additional samples with the same
timestamp.
Ignoring those excess samples, allows the parser to proceed, while still
preserving the strict time based interval for the application.
The example applications may not always be needed. An option to disable
them might be useful.
Signed-off-by: Venkatesh Shukla <venkatesh.shukla.eee11@iitbhu.ac.in>
Commit d414da70a42afca0096e0402a426c4ac8d2a2881 added support for a
second Aeris Elite T3 variant. But the parser wasn't updated to take
into account this second model number.
For the Aeris Manta and Sherwood Insight, the header size should be 16
bytes smaller. Due to this mistake not only two samples were missing,
but also the byte offset for the gas mixes were wrong. This resulted in
invalid gas mixes, with oxygen percentages exceeding 100%.
For the older models, gas mixes are disabled by setting their oxygen
percentage byte to 0x00 or 0xFF. Trying to parse such a byte as a valid
percentage results in an invalid gas mix.
Because the device doesn't allow you to enable a gas mix if the previous
gas mix has already been disabled, we can simply stop parsing the gas
mixes once the first disabled gas mix has been found.
The gas mode should not only be taken into account for parsing the gas
mix definitions, but also for the initial gas mix. Because the logic
needs to be kept in sync, it's convenient to have all related code in a
single place, and cache the value.
Because the gas mode takes precedence over the individual gas mix
definitions, we can simplify the code by taking the gas mode into
account immediately when parsing the gas mixes.
In commit 886884ab606def1183d99eca7dec8affefee635e, the custom log
function in the example applications has already been modified to use
high resolution timestamps. But I forgot about the built-in default log
function. So this is basically the same change, but now applied to the
built-in log function.
The extra memcmp check after the tcsetattr call is intended to verify
whether all the changes to the termios structure have been applied
correctly.
But for pty's, some of the termios settings make no sense at all, and
therefore the Linux kernel always does:
tty->termios.c_cflag &= ~(CSIZE | PARENB);
tty->termios.c_cflag |= (CS8 | CREAD);
Thus, instead of ignoring such nonsense termios settings, the kernel
changes the termios structure to reflect what pty's actually do. The
consequence is that these settings will not stick, and cause the memcmp
check to fail.
An example where this affects libdivecomputer, are the two backends that
require odd or even parity (e.g. vyper and iconhd). Here, the kernel
will clear the PARENB flag, and thus cause the memcmp check to fail.
Since this check appears to causes more trouble than it solves, let's
just remove it completely!
The previous commit exposed another issue. The termios structure may
contain padding bytes. Because the content of those padding bytes is
unspecified, they may contain some random data, which causes the memcmp
to fail.
Explicitly initializing the termios structure with memset, will also set
the padding bytes to zero.
Due to a minor mistake, only the first byte was being checked for
equality. Fixed it by changing position of parenthesis.
Signed-off-by: Venkatesh Shukla <venkatesh.shukla.eee11@iitbhu.ac.in>
The Cressi Leonardo does store an indicator for the ascent rate (e.g.
the number of arrows) in the upper two bits. This results in 4 possible
values, ranging from 0 to 3, with the following interpretation:
0.0 - 3.9 m/min No signal
4.0 - 7.9 m/min 1 Arrow
8.0 - 11.9 m/min 2 Arrows
12.0+ m/min 3 Arrows (with STOP icon)
Reported-By: Nick Shore <support@mac-dive.com>
With the exception of the different event mask (single byte 0x80 vs two
byte 0x0100), the OSTC3 bailout event is identical to the OSTC2 variant.
Just as before, the new bailout event is reported to the application as
a normal gas change event.
Currently, the buffer overflow checks take into account the size of the
entire dive. But since the length of the extended sample is stored in
the data, we can actually check for overflows in each sample. The main
benefit is that errors will be caught much earlier now.
An additional advantage is that we can now easily skip any remaining
sample bytes. Normally such bytes are not present, unless a firmware
update introduces a new feature which our parser doesn't support yet.
The Mares Nemo Wide 2 appears to have a slightly larger profile
ringbuffer, which extends to the end of the internal memory. It remains
to be seen whether this is also true for some of the other models. The
Mares Matrix is currently the only model where this is definitely not
the case.
The Hollis TX1 has several new features compared to the other models. It
supports trimix and up to 6 different gas mixes can be configured. It
also has twice the amount of memory, which requires an extra bit for the
ringbuffer pointers.
The initial gas mix index has been confirmed for the D6i only. For the
other two models, it's an educated guess that the byte offset will be
identical.
Normally, the last valid dive is followed by an empty entry with all
0xFF bytes. However, we received data from a device where a few of those
bytes are not equal to 0xFF. This causes the entry to be processed
incorrectly as a valid dive.
As a workaround, we now check the internal dive number for 0xFFFF.
This makes libdivecomputer build via Android NDK. Its currently unusable
due to the fact that Android usually doesn't provide any kernel serial
drivers.
Signed-off-by: Anton Lundin <glance@acc.umu.se>
On some systems a small delay is required between receiving the response
of a packet and sending the request for the next packet. Without this
delay, almost every request fails with either a NAK or a timeout.
Retrying usually works, but also causes the download to slow down
considerable.
The root of the problem appears to be related to the FTDI low latency
setting. On Windows and Mac OS X systems, the default setting for the
latency is 16ms, while Linux defaults to a low latency setting of only
1ms. This higher latency on Windows and Mac automatically introduces a
small delay while reading the packet, and that probably happens to be
just enough to make the transfer succeed without any problems. But on
Linux, due to the low latency setting, the next request is send almost
immediately, and that causes the transfer to fail. I suspect the dive
computer may still be busy with the previous request and needs a bit
more time before it's ready to accept a new request.
To mitigate this problem, we introduce a adaptive inter packet delay.
Initially this adaptive delay is set to zero. This is to make sure we
don't affect those systems that don't need a delay at all. Now, every
time a packet fails, that's an indication the inter packet delay is too
small, and we slightly increase the value. After a few attempts we'll
automatically reach the minimal required value. To avoid that the delay
grows out of control in the case of serious trouble, it's limited at
16ms.
Note that the fixed 100ms delay before retrying a failed packet remains
in place. That's on purpose, to make sure the next attempt will always
be successful, regardless of whether the adaptive delay has already
reached its minimal value or not.
Currently this isn't used or needed anywhere, but the research has been
done, and it would be silly to drop the knowledge. We may need it in the
future.
The Suunto dive computers record gas change events in the profile data.
But because there is no gas change event stored on the first sample, the
application doesn't know which gas mix is in use, until the very first
gas change event occurs.
For the Suunto HelO2, the index of the initial gas mix is stored in the
dive header. This is most likely also the case for the other models, but
I haven't found yet where exactly it is stored. As a temporary solution,
we simply assume the initial gas mix is the first gas in the list with
available gas mixes. This should be a reasonable assumption for most
dives.
Fixes ticket #2
The gas mix data is used from multiple functions, and the code to parse
that data is duplicated in each function. Because this is error prone,
the code is moved to a single place, and the data cached in the parser.
The resolution of the GetTickCount function is limited to the resolution
of the system timer, which is typically in the range of 10 to 16
milliseconds. This is unsufficient for measuring very short time
intervals in the milliseconds range. The QueryPerformanceCounter
function provides much higher resolution timestamps. We already use it
for the half-duplex workaround.
Parsing the gas switch events is somewhat complicated, because depending
on the actual model, they can be stored in different ways.
The table below lists the number of gas mixes (G), tanks (T) and alarm
bytes (A) supported by each model. Note that only the representative
model in each group is listed.
Model | G | T | A
==============|===|===|===
Pro | 1 | 0 | 1
Com | 1 | 1 | 1
Tec | 3 | 3 | 1
Aladin Tec | 1 | 0 | 2
Aladin Tec 2G | 3 | 0 | 2
Galileo | 3 | 3 | 3
Models without support for multiple gasmixes only need to set the
initial gas mix correctly. Models with support for one or more tank
pressure sensors can link the gas switches to the tank switches. Models
with at least two alarm bytes can store a gas switch event in the second
alarm byte.
As can be observed from the table, some models can use several different
methods to store the gas switches. The current implementation supports
all of them. In the case of a conflict, the last used method always
takes precedence. That's simply due to the fact that each method just
sets the gasmix index, and only the final value is used. If this
approach turns out to produce wrong results, it can always be fixed
later.
The initial gasmix is always assumed to be the first configured gasmix.
One byte is already more than sufficient to store an oxygen percentage.
Using two bytes doesn't cause any problems for some models, because the
upper byte is always zero, but for other models that's not the case.
Most likely this upper byte is used to store something else.
Internal, these two models use exactly the same data format. For those
features which are only available on one of the models, the
corresponding data fields are set to their unused values. For example,
the Smart Tec supports up to three gasmixes, while the Smart Z supports
only one gasmix. But although the Smart Z has storage reserved for up to
three gas mixes, only the first one is ever used.
For the Galileo Trimix, the header length isn't fixed. The actual length
depends on whether the dive has the trimix setting enabled or not.
Because at this point we're still trying to determine the length of the
header, it hasn't been checked yet and an extra check is necessary.
Because for selecting the correct header and sample tables (and
associated data) only the model number is necessary, they can easily be
stored inside the device handle. The main benefit is less duplicated
code.
The serial number is stored as a plain 32bit integer, and the device
shows the hexadecimal representation. Because a BCD encoding can only
represent the lower digits (0-9) of a hexadecimal number, the upper
digits (A-F) got mangled.
Reported-By: Nick Shore <support@mac-dive.com>
Although the settings sample will most likely always be located at the
start, it's better to be on the safe side and use an offset relative to
the start of the settings sample.
Reported-By: Nick Shore <support@mac-dive.com>
Starting with firmware v1.23, the OSTC3 settings can be configured
through the USB interface. There are new commands for reading, writing
and restoring the settings to their default values.
The workaround in the previous commit has a bug. If after a reset, the
first entry happens to be located near the end of the TOC, then after a
few dives, the TOC will overflow and new entries will be written at the
start of the TOC. But the current algorithm starts scanning the TOC from
the start and abort the scan as soon as an empty entry is found. Thus if
there are less than 256 dives present, those entries near the end will
never be reached.
We now ignore all uninitialized entries, when searching for the most
recent dive. An explicit safety check is added in case dives are
unexpectedly interleaved with empty entries.
Normally, the OSTC3 will always start writing dives at the first entry
in the TOC (Table of Contents). Therefore, uninitialized entries can
only be present after the last dive. However due to a minor firmware
bug, resetting the logbook erases the TOC but leaves the internal dive
counter unchanged. The consequence is that the next dive will still be
stored at the corresponding next TOC entry, while all previous TOC
entries have been erased.
As a workaround, we simply ignore uninitialized entries, unless we have
already found at least one dive.
With the switch to 4K data packets in the Icon HD backend, the internal
log buffer is no longer large enough for hexdumps of these data packets.
A hexdump needs twice the size of the binary data, plus some extra bytes
for the header and the terminating null byte.
The switch from downloading the entire data with a single large packet
to multiple smaller 256 byte packets, resulted in a considerable
performance regression. In one particular case, the difference was a
factor 6.7 slower!
I performed a small tests (using an Icon HD Net Ready) with 256, 1024
and 4096 byte packets, and the total time was respectively 21.0, 11.3
and 6.5 seconds. For a single large packet, the total time is only 5.9
seconds. Thus the difference with a 4096 byte packet is negligible.
The model number stored in the main memory isn't always the most
reliable source of information, because there are devices where the
model number has not been filled in properly. Instead, we check the
product name in the version packet against the list with valid names,
and return the corresponding model number.
As an additional advantage, we no longer depend on the model number
supplied by the application for selecting the correct memory layout.
Nevertheless, the model parameter is kept for backwards compatibility.
The recommended packet size is 256 bytes, which matches the maximal
amount of data that can be read back from the internal memory chip.
Larger requests are split by the firmware into multiple blocks of
maximum 256 bytes.
Note that initially, we already used 256 byte packets for the newer
models, but it seems this was accidentally changed to only 64 bytes in
commit ad0e187c0c932121efa50763afc02771633cbd61.
The main benefit of this change is a simplification of the code, because
now there is no longer a difference between the Icon HD and Matrix
variant of the protocol.
The Mares Icon HD use a processor with integrated USB circuit, which
presents itself to the host system as a CDC-ACM device. Since there is
no external usb-serial chip involved, the baudrate and other serial
line parameters are irrelevant. By choosing the same settings as the
later models, which do use an usb-serial chip, we can completely avoid
the problem of unresponsive devices due to using the wrong baudrate.
The serial number appears to be either a 24 or a 16 bit number, but
certainly not a 32bit number. Whether it's 24 or 16 bit is hard to
tell because the data we have available always contains a zero value
in the third byte.
The communication protocol and memory layout of the Cressi Giotto
appears to be identical to that of the Leonardo. The first byte seems
to be the model number, with 0x01 for the Leonardo and 0x04 for the
Giotto.
Gas mixes that have been marked as disabled are stored as the value 0xF0.
When interpreted as an oxygen percentage, this results in an out of range
value. Therefore, these gas mixes should simply be ignored.
Reading the data packet in multiple smaller chunks greatly improves the
progress events. Instead of just two events, before and after the
download, there are now many intermediate events. This change also
allows to significantly reduce the timeout, from 30 seconds to just one
second, which avoids blocking for too long in case the device doesn't
respond at all.
An infinite timeout causes major problems, because if the device doesn't
respond at all, the read call will block forever. With the eon serial
line settings (1200 8N2), the total time to read the 2305 byte packet is
about 21.129 seconds. A timeout of 30 seconds should be plenty of time.
The VT4.x memory layout appears to be slightly different from the Atom
3.x memory layout. The logbook ringbuffer does start at offset 0x420
instead of 0x400.
When using up to 14 bits for the depth, the resulting values are too
large in some cases. Most likely the upper bits are used to store
something else. Even with only 11 bits, the resulting depth range
(0-204.7m) should still be more than sufficient.
The Uwatec Meridian protocol is identical to the Uwatec Smart/Galileo
protocol, except for some additional framing around each data packet,
and the switch from IrDA to usb-serial communication. For parsing, the
data format appears to be identical to the Galileo data format.
The XTender 5 is identical to the other models in the Zeagle N2iTiON3
family, and can be added to the list of supported devices without any
further changes.
On Mac OS X, sending the slip packet byte by byte results in an abysmal
performance. The first byte takes up to 160ms to send, and each next
byte approximately 250ms. The packet to request a data block is
typically 7 bytes large, and therefore takes about 1660ms to send.
Because a dive is transmitted as multiple smaller packets (typically
144 bytes without protocol overhead), downloading a single dive can
easily take several seconds.
However, when sending the entire slip packet at once, the time remains
roughly identical to sending just the first byte. The result is that
the time for sending a packet reduces significantly, proportional to
the length of the packet.
Under the hood, the slip packet is now internally buffered, and the
buffer is send only when the entire packet is complete, or whenever the
buffer gets full. But in practice, the buffer is large enough to always
store an entire packet.
In the original bug report, downloading 57 dives took about 40 minutes.
After applying the patch, that time reduced to only 5 minutes!
The new Oceanic OCi appears to be almost identical to the already
supported Oceanic OC1. The most important change is the different
location for the logbook ringbuffer.
The profile ringbuffer appears to be slightly smaller than expected for
some models. For the Mares Matrix (and all compatible devices) it ends
earlier, while for the Icon HD Net Ready it starts later.
This bug resulted in missing dives, because all remaining dives were
getting dropped once a dive that crossed the ringbuffer boundary was
reached.
The logbook ringbuffer appears to start at offset 0x400 instead of
0x240. Since these ringbuffer boundaries have to be taken taken into
account only once the ringbuffer has been filled completely, this bug
affects users with more than 200 dives only. Thus, no surprise it didn't
get noticed earlier.
Several backends require a shutdown command to be send before closing
the connection. If such a command gets cancelled, that might result in
an unclean shutdown. Backends where this is problematic can always
ignore cancellation requests internally, but it's less error-prone (and
much easier) to simply disable the cancellation callback for all
backends, before closing the connection.
When updating the firmware while the OSTC is running in normal mode, the
V2 protocol should be used. However, the OSTC can also be rebooted from
the menu or reset using the magnetic switch, and then the V1 protocol
should be used. The only difference with the V2 protocol is the slower
baudrate (19200 vs 115200).
True autodetection of the protocol variant is difficult, because the
bootloader will abort the firmware update when not receiving a 0xC1
byte. But we can still take advantage of the fact that the normal
firmware does ignore any 0xC1 bytes sent at the wrong baudrate, and
probe with the V1 protocol first. If the bootloader is already running,
it will respond immediately and we're done. If the normal firmware is
still running, it won't respond and the probing with the V1 protocol
will fail. If we then switch to the V2 protocol, the firmware update
will continue as before.
The result is a firmware updater which supports both bootloader
protocols without requiring any manual configuration.
This dive computer has the same enclosure as the Mares Puck, but the
wire protocol is that of the Puck Pro. The original software identifies
it as a "Mares Puck 2", and the handshake string also contains the
sequence "PUCK 2", so this string is used to identify the device.
This event is on when accumulating deco time. Once you reach the floor
deco time will start decreasing and the event will stop. Going below the
floor again will re-activate the event.
Signed-off-by: Michael Andreen <harv@ruin.nu>
This is only a cosmetic change, to make the IrDA device address on
Windows consistent with the address Linux. There is no functional
difference because the address is always serialized and deserialized
internally, and applications shouldn't care about the actual number.
Anyway, the difference in endianness is easy to notice because the
Uwatec Smart family of devices use the serial number as the IrDA device
address. On Linux, the address was identical to the serial number, while
on Windows the byte order was reversed.
For the time being, the serial port enumeration code is of very limited
use. It's not used anywhere in the library, and as an internal api it's
also not available to applications. It serves mainly as a reference
implementation for future use.
The Aeris XR-1 NX has very limited memory and doesn't have a
logbook and profile ringbuffer. Hence downloading dives isn't
really supported, but even this limited amount of data might be
useful for someone.
When using all 16 bits for the temperature, the resulting values are
clearly wrong in some cases. Most likely the upper 4 bits are used to
store something else. Even with only 12 bits, the resulting temperature
range (0-409.5°C) should still be more than sufficient.
In the OSTC3 data format, the 7th bit of the event byte is used to
indicate whether another event byte is present or not. For the OSTC2,
this 7th bit remained unused, and I assumed it would eventually get used
in the same way as the OSTC3 does. But that assumption turns out to be
wrong. Starting with firmware v2.66 the 7th bit is used for a new
bailout event.
This patch leaves the existing logic intact, but except for the OSTC3
format (version 0x23), the maximum number of events bytes is now limited
to just one byte.
The OSTC 3 dataformat does contain the profile length twice: once in the
main 256 byte header, and again in the small profile header. However due
to a firmware bug, both values are not identical. The value in the main
header is wrong and 3 bytes larger than the value in the small profile
header. This bug was fixed in firmware version 0.93.
Unfortunately we rely on the length in the main header to calculate the
number of bytes to read when downloading the dive. The consequence is
that for all dives recorded with firmware 0.93 or later, the length is
calculated incorrectly, and the download fails. Luckily the firmware
version is stored in the main header too, and we can adjust the length
calculation accordingly.
Unlike the corresponding mktime C library function, the libdivecomputer
function will never modify its argument. By marking the parameter as
const this is made more explicit.
The firmware version and serial number are stored in the final block
of each dive. That makes it very tricky to support the devinfo event
correctly. For an efficient implementation of the fingerprint feature,
the devinfo event should be emitted before downloading the manifests
or the dives. Fortunately it turns out it is actually possible to
retrieve the firmware version and serial number independently, using
the special identifier command.
A shutdown command should be send to the device, before the connection
is actually closed. In the absence of this command, the device will
display an error, even if the data transfer itself was successful!
The only difference with the two other Oceanic OC1 variants is the new
model number. I have absolutely no idea what's the purpose of such a
silly change.
The Mares Matrix, Puck Pro and Nemo Wide 2 have only 256K of memory,
which is 4 times less compared to the Icon HD. However for some unknown
reason, trying to download 1024K succeeds, and these devices just
repeat the same data 4 times. That's why we never noticed the
difference in memory capacity before.
The Suunto DX has support for 8 gas mixes (OC) and 3 diluents (CC).
Because it's still unknown how rebreather dives are stored, we simply
return all 11 gas mixes. For the rest, the DX data format is very
similar to that of the existing Suunto models, with only a few
different offsets here and there.
When the number of parameters is zero, there are no sample values, and
the offset variable is never increased. The result is an infinite loop.
In practice this shouldn't happen because there should always be at
least one sample value (e.g. depth). But if a new data format is
available, which is not yet supported by the parser, we might be trying
to interpret the wrong byte.
A side effect is that the mares_iconhd_extract_dives function now
requires a valid device handle. This shouldn't cause any real problems
because this function will likely become private some day.
The new xml element makes the the gaschange events stand out more
against the other less important events. At the same time it also
demonstrates the decoding of the packed oxygen and helium percentages.
Without the root element the output isn't valid xml. Although the
output is supposed to provide a human readable representation for
internal use only, and thus never really intended for processing by
third-party applications, it doesn't hurt to turn it into valid xml
either.
In the public header files, all symbols are marked extern C. When using
a C compiler, there is usually no problem if the header isn't included
in the C file. But the msvc build system uses the C++ compiler (due to
the use of some C99 features not supported by the msvc C compiler).
Currently all device descriptors are included, regardless of whether
the device is actually supported by the library. If IrDA or USB support
is unavailable, a dummy backend is build which will always fail with
DC_STATUS_UNSUPPORTED. Thus there is no point in listing those devices
as being supported. Doing so will only confuse end-users.
Although the communication protocol of the OSTC3 is nearly identical to
that of the Frog, the different size parameters make it hard to share
the code easily. On top of that, if we ever implement native bluetooth
communication support, we'll need a completely separate backend anyway.
Therefore the Frog backend is simply duplicated, with a few OSTC3
specific changes applied here and there.
The existing ostc parser is upgraded to support the new OSTC3 data
format.
The Frog stores the index of the initial gas mix at the same location
as the OSTC, but the gas mix percentages are at a different offset, and
the number of gas mixes is different too. Parsing all the gas mixes in
advance makes the code easier to read and more future proof.
With the layout descriptors, most hardcoded constants are now in a
central place, which will make it easier to add support for new data
format variants.
If the gas model flag is set to air, the individual gas mix definitions
are ignored, and a single mix with air is returned instead. For non-air
dives, only the gas mixes marked as active are returned.
Sometimes there are garbage bytes present after opening the serial
port, which causes the communication to fail. Flushing the buffers
after a small delay is all it takes to get rid of those bytes.
The React Pro White appears to be a newer variant of the React Pro. For
the communication it uses the newer atom2 protocol, but the data format
remains (almost) the same as the older React Pro.
This model doesn't support a 2 second sample rate. It appears the
possible sample rate values have been shifted by one to map the value
zero to a 15 second sample rate.
To avoid any trouble with possible out of range values, the index is
shifted in a circular way.
There are two good reasons for this change. First of all, it makes the
Predator data format more consistent with the Petrel data format, which
also has the final block appended to each dive. But even more important
is that we might actually need the information stored in the final block
someday.
The final block contains important information about the device, such as
the firmware and logbook version. Right now this information is simply
lost after the download. But if the data format ever changes to support
some new feature, we'll likely need that information to autodetect the
correct format.
Unfortunately this also changes the dive format in a non-backwards
compatible way. However, to minimize the inconvenience, the legacy
format (without the extra final block) remains supported in the parser.
Because the size of a dive isn't known in advance, we use the worst case
value of 0xFFFFFF (or nearly 16MB). However in practice, the real size
is many orders of magnitude smaller, even after the decompression.
Instead of pre-allocating a huge memory buffer, we now start with a much
smaller one, and increase when necessary.
For the predator and petrel manifests, where the size is known in
advance, we continue to pre-allocate the exact amount of memory as
before.
The Petrel (with updated firmware) supports an enhanced communication
protocol, which is more efficient and powerfull than the legacy Predator
compatibility mode. The new protocol uses data compression for faster
transfers and supports the ability to selectively download individual
dives. Last but not least, the new protocol isn't limited to the last
128kB of logbook data, but can access the full logbook capacity (16MB).
The new Petrel protocol uses a simple data compression scheme to reduce
the transfer times. The data is broken up into blocks of 32 bytes each.
Each block except the first is XOR'ed with the previous block, creating
large runs of zeros due to the similarity of the data. The zeros are
then run-length encoded (RLE) to save space.
This is done in preparation for the implementation of the new Petrel
protocol, which shares the low level communication with the existing
Predator protocol.
With the new interface, an application can easily retrieve the
underlying transport type from the device descriptors and present a
custom user interface element to the end-user for supplying transport
specific parameters. For example the serial port for devices using
serial communcication.
For devices using a usb-serial chipset or the bluetooth Serial Port
Profile (SPP/rfcomm), the transport type is DC_TRANSPORT_SERIAL, because
internally the serial emulation layer is used for the communication.
Currently, each backend has it's own function to verify whether the
object vtable pointer is the expected one. All these functions can be
removed in favor of a single isintance function in the base class,
which takes the expected vtable pointer as a parameter.
Functions which are called through the vtable, don't need to verify the
vtable pointer, and those checks are removed.
The term "backend" can be confusing because it can refer to both the
virtual function table and the device/parser backends. The use of the
term "vtable" avoids this.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.