Compare commits

...

246 Commits

Author SHA1 Message Date
Jef Driesen
72a88b18d9 Fix the date/time with timezone offset
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.
2024-05-20 20:59:03 +02:00
Jef Driesen
eb4b082b1b Use a different buffer size for Rx and Tx
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.
2024-05-16 16:41:57 +02:00
Jef Driesen
8fe598e1b0 Merge branch 'sirius' 2024-05-03 17:27:39 +02:00
Jef Driesen
1663997111 Add support for the Mares Sirius (and compatible models)
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
2024-05-03 17:27:25 +02:00
Jef Driesen
f20d9cb972 Refactor the Mares Genius and Horizon parser
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)
2024-05-03 17:27:25 +02:00
Jef Driesen
4516842a5a Use a new command to read the serial number
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.
2024-05-03 17:27:25 +02:00
Jef Driesen
9a603fa8cf Use macros to detect the device type
The macros make the code a bit more compact, and adding support for new
models becomes easier too.
2024-05-03 17:27:25 +02:00
Jef Driesen
89a28427d6 Increase the BLE packet size
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.
2024-05-03 17:27:22 +02:00
Jef Driesen
4914f6bff3 Fix the memory layout of the Oceanic Geo
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.
2024-04-21 22:50:57 +02:00
Jef Driesen
dcf842cd9d Fix the Apeks DSX tank number
Since commit f5f855d4284a84d5249462c46a9c4f6107a12b09, the tank number
should remain one based instead of zero based.
2024-04-01 23:37:37 +02:00
Jef Driesen
8745a3b95a Fix the Mares usb-serial communication
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.
2024-03-26 22:32:48 +01:00
Jef Driesen
a86cb92ed8 Add some more details to the error messages 2024-03-26 22:19:02 +01:00
Jef Driesen
b8c3a09c6f Fix a macro redefinition warning
The C library stdio.h header file already defines the EOF macro. Rename
our macro to avoid the conflict.
2024-03-21 19:18:04 +01:00
Jef Driesen
6903a66cc5 Update the Github actions
All Github actions using Node.js 16 are deprecated [1]. Update the
following actions to a newer version using Node.js 20:

 - actions/checkout
 - actions/upload-artifact
 - microsoft/setup-msbuild

[1] https://github.blog/changelog/2023-09-22-github-actions-transitioning-from-node-16-to-node-20/
2024-03-20 17:31:15 +01:00
Janice McLaughlin
9070da3570 Add support for the Aqualung i330R and Apeks DSX
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>
2024-03-20 17:24:59 +01:00
Jef Driesen
8e349d4046 Add ioctl's for the bluetooth authentication
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.
2024-03-20 17:24:59 +01:00
Jef Driesen
47f6949db1 Merge branch 'pelagic-refactor' 2024-03-20 17:24:25 +01:00
Jef Driesen
9070b7d035 Log the ringbuffer pointers
Logging the ringbuffer pointers is very useful while investigating
problems and adding support for new models.
2024-03-20 17:23:46 +01:00
Jef Driesen
4cc0bc25ae Support reading the logbook in forward direction
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.
2024-03-20 17:23:46 +01:00
Jef Driesen
f49af5208c Separate the error handling from the size calculation 2024-03-20 17:23:46 +01:00
Jef Driesen
b9d7684552 Refactor the code to read the ringbuffer pointers
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.
2024-03-20 17:23:46 +01:00
Jef Driesen
ae292253ba Refactor the code to read the device info
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.
2024-03-20 17:23:46 +01:00
Jef Driesen
a91a7dbc38 Refactor the function to get the profile pointers
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.
2024-03-20 17:23:46 +01:00
Jef Driesen
121c7c12fb Swap values 2 and 3 of the pointer mode
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.
2024-03-20 17:23:43 +01:00
Jef Driesen
f42449b101 Fix the Aeris 500AI logbook read command
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.
2024-03-15 14:26:25 +01:00
Jef Driesen
aa2dbac509 Log the number of skipped bytes 2024-03-15 14:26:25 +01:00
Jef Driesen
60a9b889de Merge branch 'ringbuffer' 2024-03-14 12:26:52 +01:00
Jef Driesen
4139509238 Use symbolic constants for empty/full ringbuffer 2024-03-14 12:26:24 +01:00
Jef Driesen
1ba7e5cad0 Fix errors in the ringbuffer operations
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.
2024-03-14 12:26:24 +01:00
Jef Driesen
04fe252625 Merge branch 'rbstream' 2024-03-14 12:26:07 +01:00
Jef Driesen
9090f713b4 Add support for reading a ringbuffer forwards
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.
2024-03-14 12:25:37 +01:00
Jef Driesen
00b0169578 Update the internal state in-place
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.
2024-03-14 12:24:58 +01:00
Jef Driesen
e0cf41a14e Add some extra parameter validation
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.
2024-03-14 12:24:45 +01:00
Jef Driesen
f5f855d428 Discard pressure samples from invalid tanks
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.
2024-03-13 16:52:05 +01:00
Janice McLaughlin
811ae7de82 Add the deco and rbt samples
Add the deco and rbt samples for the Oceanic Pro Plus 4 and Aeris Atmos
AI 2.
2024-03-13 16:51:55 +01:00
Jef Driesen
1d0aeecf65 Fix the React Pro White memory layout 2024-01-18 10:53:08 +01:00
Jef Driesen
de6696bc7f Add support for the Shearwater Tern 2024-01-17 07:48:54 +01:00
Jef Driesen
cfe345aa8e Exclude O2 sensors without calibration data
O2 sensor for which no calibration data is available will always result
in a ppO2 value of zero for all samples, which isn't very useful.
2024-01-12 09:15:12 +01:00
Jef Driesen
d47e1ce02b Add support for the Scubapro G3 and Luna 2.0
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>
2024-01-10 09:01:59 +01:00
Jef Driesen
08d8c3e132 Add support for parsing the compass bearings
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.
2023-11-23 11:42:23 +01:00
Jef Driesen
2d9008aff7 Remove disabled gas mixes
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.
2023-11-15 11:08:21 +01:00
Jef Driesen
e1762fc8bd Skip all non-sample records
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.
2023-11-02 15:45:24 +01:00
Jef Driesen
3c50e91a10 Allow a zero timestamp for the first sample
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.
2023-10-29 21:31:40 +01:00
Jef Driesen
cb0164150e Merge branch 'bluelinkpro' 2023-10-19 10:34:26 +02:00
Jef Driesen
e83732e200 Fix the Mares Bluelink Pro communication
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.
2023-09-27 10:10:40 +02:00
Jef Driesen
a7e7439cab Setup the 2 byte command header internally
Instead of setting up the two byte command header in every function,
move this logic to a central place.
2023-09-15 09:23:58 +02:00
Jef Driesen
072f0d4242 Fix a potential buffer overflow 2023-09-08 16:10:45 +02:00
Jef Driesen
baa1c494c1 Use a symbolic constant for the header size 2023-09-08 16:10:45 +02:00
Jef Driesen
330cb88952 Use plain ascii text in the code comments 2023-09-08 16:10:38 +02:00
Jef Driesen
37421a1b9a Merge branch 'filter-function' 2023-08-24 17:18:46 +02:00
Jef Driesen
fe9b47f4bd Document the device descriptor functions 2023-08-24 17:18:24 +02:00
Jef Driesen
ed871137b1 Export the filter function in the public api
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.
2023-08-24 17:18:24 +02:00
Jef Driesen
4a9be44afd Use separate data structures for USB and USB HID
Using separate data structures for the filtering allows to keep the USB
and USB HID backends completely independent from each other.
2023-08-24 17:18:24 +02:00
Jef Driesen
a985b11859 Replace the filter parameters with an alternative
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.
2023-08-24 17:18:24 +02:00
Jef Driesen
b2310e62d6 Pass the descriptor to the filter function
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.
2023-08-24 17:18:22 +02:00
Jef Driesen
301fdbe364 Remove support for the Tusa TC1
Apparantly the Tusa TC1 does not support downloading dives at all. The
bluetooth communication is probably disabled in the firmware.
2023-08-16 13:17:55 +02:00
Jef Driesen
beb348dbf6 Merge branch 'ostc-remove-disabled-gasmixes' 2023-08-14 10:23:55 +02:00
Jef Driesen
5d36cc0798 Remove disabled gas mixes
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.
2023-08-11 12:39:39 +02:00
Jef Driesen
993283d1c8 Store the original one based gas mix index 2023-08-10 14:04:52 +02:00
Jef Driesen
323804d5e6 Keep track of the actively used gas mixes
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.
2023-08-10 14:04:20 +02:00
Jef Driesen
ecc9e0b09b Limit the lookup function to the manual 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.
2023-08-02 15:43:19 +02:00
Jef Driesen
8bfbb94087 Include the command byte in the hexdump
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.
2023-07-20 23:40:02 +02:00
Jef Driesen
9cde393e5f Merge branch 'shearwater-tank-usage' 2023-07-18 00:30:22 +02:00
Jef Driesen
9bc742d3ac Use the HP CCR data for the oxygen/diluent usage
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.
2023-07-18 00:30:06 +02:00
Jef Driesen
a4cd21b811 Use the GTR mode to detect sidemount tanks
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.
2023-07-18 00:30:06 +02:00
Jef Driesen
f77e9c03fc Restrict the oxygen/diluent usage to CCR dives
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.
2023-07-18 00:30:06 +02:00
Jef Driesen
f818a5a92a Add a function for detecting CCR dives 2023-07-18 00:30:06 +02:00
Jef Driesen
ceaaba3e77 Add support for the new Ratio iX3M 2 models
Except for the new prefix in the bluetooth name, the new models are
backwards compatible with the previous models.
2023-07-12 13:11:19 +02:00
Michael Keller
0afd62d7af Return an error for the OSTC4 memory dump
The OSTC4 does not support downloading memory dumps.
2023-07-07 12:30:46 +02:00
Michael Keller
3a68af418e Fix the OSTC4 firmware upgrade
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.
2023-07-07 12:30:46 +02:00
Jef Driesen
ff0328537e Ignore the diluents for open-circuit dives
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.
2023-07-06 14:12:41 +02:00
Jef Driesen
3d82d6796f Merge branch 'shearwater-timesync' 2023-07-05 23:49:38 +02:00
Jef Driesen
25bd1f9853 Add support for time synchronization
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>
2023-07-05 15:43:12 +02:00
Jef Driesen
c16530b8ab Detect negative response packets
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.
2023-07-05 15:43:12 +02:00
Jef Driesen
d4402aa296 Add support for the WDBI function
The RDBI (Read Data by Identifier) has a WDBI (Write Data by Identifier)
counterpart, which supports changing settings on the dive computer.
2023-07-05 15:43:12 +02:00
Jef Driesen
13705f2b2d Use a static buffer for the RDBI function
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.
2023-07-05 15:43:12 +02:00
Jef Driesen
ee147afceb Move the model number mapping to a function 2023-07-05 15:43:12 +02:00
Jef Driesen
9b7aa813e0 Change the salinity format in the xml output
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.
2023-05-16 13:48:49 +02:00
Jef Driesen
63f5a4d652 Remove the dc_parser_set_data function
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.
2023-05-15 22:19:37 +02:00
Jef Driesen
679db0bae6 Remove the clock parameters from the constructor
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.
2023-05-15 22:19:36 +02:00
Jef Driesen
0a4f37770f Remove the backend specific calibration functions
The backend specific calibration function are deprecated. Applications
should use the new replacement functions introduced in commit
6ab140461a3a85fba3803283070427f3be413c79.
2023-05-15 22:19:36 +02:00
Jef Driesen
4e24b3a277 Pass the sample struct by reference
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.
2023-05-15 22:19:36 +02:00
Jef Driesen
becb8bd36e Add a usage field to the tank and gas mix
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).
2023-05-15 22:19:36 +02:00
Jef Driesen
4b383a778e Add a TTS field to the deco sample
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.
2023-05-15 22:19:36 +02:00
Jef Driesen
b1ff2c6a8e Add the sensor index to the ppO2 sample
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.
2023-05-15 22:19:36 +02:00
Jef Driesen
bca8f9e2d2 Enable the millisecond resolution sample time
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.
2023-05-15 22:19:36 +02:00
Jef Driesen
a34e909a84 Change the units for the sample time to milliseconds
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.
2023-05-15 22:19:34 +02:00
Jef Driesen
070de23b83 Post release version bump to 0.9.0 2023-05-11 18:56:37 +02:00
Jef Driesen
db9371cf9f Release version 0.8.0 2023-05-11 18:56:37 +02:00
Jef Driesen
49aa12b172 Fix the date of the v0.7.0 release 2023-05-11 16:27:55 +02:00
Jef Driesen
2f1b99f2f9 Add a missing filter for the Aqualung i750TC
Commit 58d410b1a217bbc1f03d5c7a5a203c139a92616c accidentally omitted the
descriptor filter function.
2023-05-11 16:26:40 +02:00
Jef Driesen
6c3bbb2cc7 Reduce the BLE output packet size to 20 bytes again
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.
2023-05-11 16:25:59 +02:00
Jef Driesen
1c8cd096b5 Integrate the new packet I/O in the backends
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.
2023-05-11 16:25:59 +02:00
Jef Driesen
40c95ca02a Add a generic packet I/O implementation
The new packet I/O provides a layered I/O for reading and writing a byte
stream from the underlying packet oriented transport.
2023-05-11 16:25:59 +02:00
Jef Driesen
72ddd6a439 Fix a typo in the documentation 2023-04-24 00:02:17 +02:00
Jef Driesen
d4472b758f Add support for the Divesoft Freedom and Liberty
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>
2023-04-24 00:02:17 +02:00
Jef Driesen
b0e77fd05f Integrate the HDLC stream in the eonsteel backend 2023-04-24 00:02:17 +02:00
Jef Driesen
cee9a2e926 Add a generic HDLC I/O implementation
The new HDLC I/O provides a generic I/O stream for reading and writing
HDLC encoded frames on top of another base I/O stream.
2023-04-24 00:02:17 +02:00
Jef Driesen
9c38ae3e01 Remove the local endianess functions
Replace the local functions for writing 16 and 32 bit little endian
values with the corresponding common functions.
2023-04-17 16:28:19 +02:00
Jef Driesen
629d567381 Use the correct function to free resources
Currently this doesn't make any difference because the
dc_device_allocate() function simply calls free(), but this may change
in the future.
2023-04-12 11:47:25 +02:00
Jef Driesen
bec4a747ff Add the udev rules to the distribution tarball 2023-04-07 20:37:19 +02:00
Jef Driesen
43f48af418 Add a README file to the contrib directory 2023-04-07 20:37:19 +02:00
Jef Driesen
e45c62b028 Include the revision in the Visual Studio and Android builds
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.
2023-04-07 20:37:17 +02:00
Jef Driesen
767a2fad91 Don't generate the Windows version resource
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.
2023-04-07 20:35:05 +02:00
Jef Driesen
083b1eb8de Move the Visual Studio project to the contrib directory
With this change all the alternative build systems are now located in
the contrib directory.
2023-04-07 20:35:05 +02:00
Jef Driesen
118f6d79ba Add a basic Android build system
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>
2023-04-07 20:35:02 +02:00
Jef Driesen
cf221de9b7 Disable the getopt argument permutation on Android
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.
2023-04-07 14:04:00 +02:00
Jef Driesen
86fd58c8c6 Move the sign extension function to a common place 2023-03-23 19:43:16 +01:00
Jef Driesen
1930b9eb59 Merge branch 'checksums' 2023-03-23 19:41:11 +01:00
Jef Driesen
554855cc7d Add the 16-bit CRC-ANSI functions
Yet another family of 16-bit CRC function. The only difference with the
already available CRC-CCITT algorithm is the choice of the polynomial.
2023-03-23 19:31:01 +01:00
Jef Driesen
27b471e76b Document the properties of the CRC functions 2023-03-23 19:30:23 +01:00
Jef Driesen
00033e4af0 Rename the 32-bit CRC functions
The new names make it easier to identify the normal and reflected
variant of the CRC function.
2023-03-23 19:30:23 +01:00
Jef Driesen
d327aea6ff Add a reflected variant of the 16-bit CRC-CCITT
The reflected variant of a CRC does reverse the bits of each input byte
and requires a different lookup table for an efficient implementation.
2023-03-23 19:28:37 +01:00
Jef Driesen
12f44f3410 Add an extra parameter for the xorout value
This change allows to calculate some more variants of the CRC-CCITT
algorithm with a single function.
2023-03-23 19:28:24 +01:00
Jef Driesen
2ba9904757 Fix the OSTC4 diluent changes
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>
2023-02-23 23:46:13 +01:00
Jef Driesen
255a2dbb9a Keep open-circuit and diluent gas mixes separately
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.
2023-02-17 15:00:49 +01:00
Jef Driesen
ee78d6f65b Parse some extra gas mix information
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.
2023-02-17 14:45:05 +01:00
Jef Driesen
328812e95b Limit the index to the fixed gas mixes
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.
2023-02-17 14:45:05 +01:00
Jef Driesen
98c7887e9c Handle dives without a valid gas mix more explicit
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.
2023-02-17 14:43:32 +01:00
Jef Driesen
5fd9317533 Ignore all gas mixes for freedives
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.
2023-02-17 14:41:01 +01:00
Jef Driesen
9787bb7ac9 Always include all gas mixes defined in the header
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.
2023-02-16 14:05:53 +01:00
Jef Driesen
201be561d4 Add support for the new Excursion v6+ firmware
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>
2023-02-09 08:17:32 +01:00
Jef Driesen
78373d827b Add support for the HP CCR tank pressure
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.
2023-02-03 07:56:47 +01:00
Jef Driesen
4e83b1642c Use the correct field for the setpoint sample
This only happened to work correctly because both the setpoint and ppO2
field have the same data type and are located at the same offset in the
union.
2023-01-27 09:25:58 +01:00
Jef Driesen
9eef8c50c0 Add support for the Oceans S1
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>
2023-01-25 14:17:29 +01:00
Jef Driesen
86e9cc3443 Add support for the Deepblu Cosmiq+
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>
2023-01-25 13:32:14 +01:00
Jef Driesen
3ce34a0b6d Add missing functions for accessing big/little endian values
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.
2023-01-25 13:32:14 +01:00
Jef Driesen
187f8d625b Move the snprintf functions to the platform module
Moving the implementation of the snprintf wrapper functions to the
platform module allows to re-use the same implementation throughout the
entire codebase.
2023-01-25 13:32:14 +01:00
Jef Driesen
fca64faa3c Merge branch 'proplus4' 2023-01-25 12:15:50 +01:00
Jef Driesen
e0e3bc8994 Repeat the handshake every few packets
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.
2023-01-25 12:13:08 +01:00
Jef Driesen
ceae89e149 Enable big page support
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.
2023-01-23 21:13:52 +01:00
Jef Driesen
3414f72f60 Remove the model number from the vtpro struct
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.
2023-01-23 21:10:10 +01:00
Jef Driesen
d0857c49ec Add the model number to the version table
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.
2023-01-23 21:10:10 +01:00
Jef Driesen
f59cbf0fe5 Move all model numbers to the common header 2023-01-23 21:10:10 +01:00
Jef Driesen
45b9ee8376 Remove a duplicated include statement 2023-01-23 21:10:10 +01:00
Jef Driesen
cf81ac79b3 Add support for the 300bar pressure sensor
The new 300bar pressure transmitter records the pressure in units of
2bar, because otherwise the value doesn't fit into an 8-bit integer.
2023-01-19 16:18:42 +01:00
Jef Driesen
90bb40e5ea Fix the iX3M 2 decompression algorithms
The iX3M 2021 and iX3M 2 models use different values for the
decompression algorithm.
2023-01-19 14:50:00 +01:00
Jef Driesen
bf268d79b4 Fix parsing dives using dual Buhlmann and VPM algorithm
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.
2023-01-17 08:19:58 +01:00
Nikolay Zhekov
989c992154 Add Shearwater Perdix AI hardware ID 2023-01-07 19:50:34 +01:00
Jef Driesen
b1f4ad94eb Fix the decoding of the CNS value
The CNS value is reported as a fraction instead of a percentage.
2023-01-06 17:16:29 +01:00
Jef Driesen
547b1cfd15 Parse the timezone setting
Since firmware version 5B and later, a timezone offset is available.
2023-01-06 17:16:29 +01:00
Jef Driesen
9019805f52 Validate the parameter before calling the vtable function
This removes the need to validate the date/time pointer in every single
backend.
2022-12-21 14:55:50 +01:00
Jef Driesen
f4fae1b9f6 Add some workarounds for the msvc compiler 2022-12-21 14:55:50 +01:00
Jef Driesen
094a225363 Detect the posix unistd.h header file
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.
2022-12-21 14:55:12 +01:00
Jef Driesen
79c9c5b7f9 Add support for the Oceanic Geo Air
The Oceanic Geo Air appears to be compatible with the OC1.
2022-12-08 23:16:31 +01:00
Jef Driesen
59a0844ee6 Fix the progress events when no dives are present
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.
2022-12-03 13:29:10 +01:00
Jef Driesen
ed0b21beae Increase the BLE packet size
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.
2022-12-02 13:45:50 +01:00
Jef Driesen
755f23fdfa Ignore the first byte of the BLE packets
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.
2022-12-01 14:12:41 +01:00
Jef Driesen
db2540485e Link hidapi statically against libgcc
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.
2022-11-25 21:54:34 +01:00
Jef Driesen
2577afed55 Update libusb and hidapi in the CI builds
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.
2022-11-25 21:52:23 +01:00
Jef Driesen
2c5ebef594 Add udev rules for hidraw devices
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.
2022-11-25 13:25:23 +01:00
Jef Driesen
913a65fde6 Allow to specify the hidapi library variant
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'.
2022-11-25 13:25:23 +01:00
Jef Driesen
5218d3921a Read the software and hardware version 2022-11-23 08:37:25 +01:00
Jef Driesen
6874130743 Add the return type to the function definition
In commit 12c77a228e84f1ceed520b6afb53b4b64ea9def6, the return type of
the function was accidentally omitted.
2022-11-19 17:43:46 +01:00
Charlotte Koch
2f3a057969 Look for select(2) in a more reliable place 2022-11-14 13:29:28 +01:00
Greg McLaughlin
c2102f62d6 Add support for parsing bookmark events
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
2022-11-10 14:09:05 +01:00
Jef Driesen
bf93040ab1 Receive only a single USB packet at a time
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.
2022-11-10 14:03:46 +01:00
Jef Driesen
8a6abab1da Update the Github actions
The Github actions need an update due to upcoming deprecations:

 * The 'set-output' command is deprecated [1]. Update to write to the
   GITHUB_OUTPUT environment file instead.

 * All Github actions using Node.js 12 are deprecated [2]. Update the
   following actions to a newer version using Node.js 16:

    - actions/checkout
    - actions/upload-artifact
    - microsoft/setup-msbuild

 * The Github create-release and upload-release-asset actions are no
   longer maintained. Replace with an alternative solution using the
   Github CLI.

[1] https://github.blog/changelog/2022-10-11-github-actions-deprecating-save-state-and-set-output-commands/
[2] https://github.blog/changelog/2022-09-22-github-actions-all-actions-will-begin-running-on-node16-instead-of-node12/
2022-11-10 14:02:20 +01:00
Jef Driesen
005a2501b9 Merge branch 'hwos-empty-profile' 2022-11-10 12:38:00 +01:00
Jef Driesen
9508401971 Fix the download of dives without a profile
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.
2022-11-10 11:28:38 +01:00
Jef Driesen
89ae8b94cf Fix the detection of empty dive profiles
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.
2022-11-09 14:27:37 +01:00
Jef Driesen
a99d990117 Verify the fields of the compact header
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.
2022-11-09 14:27:34 +01:00
Jef Driesen
c578e0a158 Use symbolic constants for the header offsets 2022-11-09 14:24:09 +01:00
Jef Driesen
34bc6b1613 Use the macro for encoding firmware versions
This makes it a bit easer to quickly locate the workarounds for specific
firmware versions.
2022-11-09 14:23:58 +01:00
Greg McLaughlin
59dd6a2a56 Increase the memory size for the Aqualung i770R
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).
2022-11-02 22:56:53 +01:00
Jef Driesen
3d388a0a96 Don't pass a NULL pointer to memcpy
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.
2022-11-02 13:23:14 +01:00
Jef Driesen
c5813d624a Add support for the Scubapro G2 TEK
The Scubapro G2 TEK is compatible with the G2, but with a new model
number.

Reported-by: Greg McLaughlin <support@moremobilesoftware.com>
2022-10-16 23:15:54 +02:00
Jef Driesen
3eedf4d24d Use the value stored in the dive header
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.
2022-10-13 20:56:59 +02:00
Jef Driesen
3e5282bf74 Add support for the Scorpena Alpha
The Scorpena Alpha uses the same communication protocol and data format
as the Deep Six Excursion.

Reported-by: Sven Knoch <info@divinglog.de>
2022-10-11 11:41:12 +02:00
Jef Driesen
ce578cafb9 Report the dive mode for Air and Nitrox dives
Currently the dive mode is only reported for rebreather dives.
2022-08-11 18:26:52 +02:00
Jef Driesen
12c77a228e Add a public api to configure the clock synchronization
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.
2022-08-11 17:36:26 +02:00
Jef Driesen
6ab140461a Add a public api to configure the depth calibration
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.
2022-08-11 17:36:15 +02:00
Jef Driesen
18f06ea585 Add a macro for the default density and atmospheric pressure
Replace the hardcoded default values with a macro defined in a central
location. This makes it much easier to adjust the values if necessary.
2022-08-11 17:36:15 +02:00
Jef Driesen
7fb943ae7f Add support for parsing the decompression model
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).
2022-08-11 17:35:03 +02:00
Jef Driesen
95f309a1c9 Add support for the Cressi Donatello
The Cressi Donatello appears to be compatible with the Goa, with just a
different model number.
2022-08-02 09:03:33 +02:00
Jef Driesen
391d4db419 Add support for the Ratio iX3M 2 models
The protocol and data structures have not changed, so just adding the
new names and model numbers.
2022-07-22 00:27:30 +02:00
Jef Driesen
972beb52be Add support for the Sherwood Amphos Air 2.0
The Amphos Air 2.0 appears to be identical to the previous Amphos Air,
except for the new model number and version string.
2022-07-15 22:14:33 +02:00
Jef Driesen
80f22dce0b Replace switch statements with an array lookup
No functional change, just some more compact code.
2022-07-15 22:11:24 +02:00
Dirk Hohndel
26c43d6d8b Add Shearwater Perdix 2 and Petrel 3
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2022-06-27 15:08:35 +02:00
Dirk Hohndel
8451286c17 Update the Shearwater hardware IDs
Update and fix the hardware IDs based on the latest information from
Shearwater.

Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2022-06-27 15:02:51 +02:00
Jef Driesen
be5bb9e690 Fix the clock synchronization
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.
2022-06-23 15:57:08 +02:00
Jef Driesen
e6f091909b Remove unused time parameters
Since the clock synchronization has been removed (see commit
a1962558412b8c89a79656992c8e7f4d001065c2 for the details), those
parameters serve no purpose anymore.
2022-06-23 15:56:10 +02:00
Jef Driesen
4616e2ed21 Add support for a new Aqualung i200C variant
There is a new variant of the i200C with a different model number
(0x4749) and double the amount of memory (128K).
2022-06-19 20:56:14 +02:00
Jef Driesen
4b4efb2c07 Add support for the Seac Screen and Action 2022-06-02 11:29:32 +02:00
Jef Driesen
2443d3ea47 Add an address parameter to the memory dump helper function
To support devices where not all memory is readable, the memory dump
helper function needs an extra parameter to specify the start address.
2022-06-01 13:27:13 +02:00
Jef Driesen
d0c7562c41 Ignore invalid gas mixes
The Cressi Michelangelo appears to store inactive gas mixes as a zero
oxygen percentage. Such invalid values shouldn't be reported.
2022-06-01 09:16:23 +02:00
Jef Driesen
0064097c03 Add support for the Cressi Michelangelo
The Cressi Michelangelo appears to be compatible with the Goa, with just
a different model number.
2022-05-23 12:54:44 +02:00
Jef Driesen
8bfb965589 Move the C_ARRAY_SIZE macro to a common place 2022-05-23 12:14:00 +02:00
Jef Driesen
82c0134811 Fix the timezone offset in the xml output
For negative timezone offsets, only the hour part should have a sign.
The minute part must always be formatted as a positive number.
2022-05-23 12:13:49 +02:00
Jef Driesen
4512a0a5d7 Emit a devinfo event when downloading a memory dump
For diagnostics purposes it's often very useful to have the device
information available when downloading a memory dump.
2022-05-23 12:01:42 +02:00
Jef Driesen
52d2684479 Read the info and more info data during startup 2022-05-23 12:01:38 +02:00
Jef Driesen
7e3bf7eeb8 Use helper functions to decode multibyte values
Add and use some helper functions for decoding multibyte BCD and binary
encoded values, to remove some code duplication.
2022-05-23 12:00:35 +02:00
Jef Driesen
0753f10661 Fix changing the OSTC settings
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.
2022-03-31 13:20:13 +02:00
Nick Shore
0448ce686a Fix the Genesis React Pro serial number 2022-03-27 10:51:08 +02:00
Jef Driesen
bedd6180f1 Merge branch 'shearwater' 2022-02-27 11:55:10 +01:00
Jef Driesen
c6640aa7d3 Read the extra tank information
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.
2022-02-27 11:49:53 +01:00
Jef Driesen
7a650f940c Add support for transmitter T3 and T4
In recent firmware versions (log version 13), Shearwater added support
for two more tank transmittors: T3 and T4.
2022-02-27 11:48:41 +01:00
Jef Driesen
86e1d59a6a Limit the number of records for the Predator
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.
2022-02-27 11:47:13 +01:00
Jef Driesen
54fa676e75 Report the timezone offset for the Teric
The Shearwater Teric supports a UTC offset and DST setting.
2022-02-27 11:46:51 +01:00
Jef Driesen
d1242a28cf Use the correct model number from the final block
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.
2022-02-27 11:45:26 +01:00
Jef Driesen
ccd37d4fa3 Use the dive mode stored in the header
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.
2022-02-27 11:44:58 +01:00
Jef Driesen
811a9b4f89 Report the correct dive mode for SCR dives
The status field contains an extra bit for SCR dives, so there is no
reason to report them as CCR dives.
2022-02-18 13:54:22 +01:00
Jef Driesen
d960a37e12 Increase the size of the BLE packet cache
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).
2022-01-31 14:06:17 +01:00
Jef Driesen
f6df075d51 Add support for the Genesis Centauri and Tusa TC1
Both models are compatible with the Deep Six Excursion.
2022-01-03 16:09:37 +01:00
Jef Driesen
c5dced237a Read the hardware and software version 2022-01-03 15:58:33 +01:00
Jef Driesen
c9c441b8bb Merge branch 'ostc' 2022-01-03 15:55:40 +01:00
Jef Driesen
5e2d376627 Report the initial setpoint in SCR mode 2022-01-03 15:54:49 +01:00
Jef Driesen
16e49eee6d Add the divemode to the layout descriptor 2022-01-03 15:54:49 +01:00
Jef Driesen
70411048e5 Re-order the fields in the layout descriptor
The new order of the fields matches the physical memory layout more
closely.
2022-01-03 15:54:39 +01:00
Jef Driesen
331bcbdaf7 Show the correct help message for the scan command 2021-12-13 08:46:09 +01:00
Jef Driesen
af03e39383 Add support for the Crest CR-4
The Crest CR-4 is compatible with the Deep Six Excursion.
2021-11-19 08:26:19 +01:00
Jef Driesen
b2040d9adb Add udev rule for the Suunto EON Steel Black 2021-11-16 14:04:45 +01:00
Jef Driesen
cadbffe416 Merge branch 'oceanic-ble-handshake' 2021-10-20 15:04:26 +02:00
Jef Driesen
cd0f42804a Ignore unsupported BLE handshake
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.
2021-10-20 15:02:57 +02:00
Jef Driesen
ba4a119a4f Detect NAK response packets
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.
2021-10-20 15:02:34 +02:00
Jef Driesen
9ff6e5caad Remove the initial gas switch
The Cressi Drake is a freedive computer and does not support gas
switches.
2021-10-13 11:06:26 +02:00
Jef Driesen
a8bcfb998b Restore the original standard gravity factor
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.
2021-10-03 11:37:59 +02:00
Jef Driesen
1c39c68203 Add support for the Mares Puck Pro +
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.
2021-09-28 12:14:10 +02:00
Jef Driesen
0a9b8b1318 Merge branch 'smartair-freedives' 2021-09-14 21:34:01 +02:00
Jef Driesen
7e075eb959 Add support for Mares Smart Air freedives
The Mares Smart Air supports a freedive mode, which uses a different
data format compared to the scuba dives.
2021-09-14 21:33:47 +02:00
Jef Driesen
e52468e0c3 Use a layout descriptor
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.
2021-09-14 21:32:48 +02:00
Jef Driesen
1a4798792e Use the divetime stored in the header
There is no need to calculate the total time from the pseudo profile,
because the dive time is also stored in the header.
2021-09-14 21:32:48 +02:00
Jef Driesen
c4b694fdb1 Fix the salinity parsing
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.
2021-09-14 21:32:31 +02:00
Ryan Gardner
75f260a941 Add support for the Deep Six Excursion
Based on original work and code by Ryan Gardner, with some additional
improvements and integration into libdivecomputer by Jef Driesen.
2021-08-17 22:38:53 +02:00
Jef Driesen
e0409a9496 Add support for the Cressi Neon
The Cressi Neon appears to be compatible with the Goa and Cartesio, with
just a different model number.
2021-08-17 17:53:16 +02:00
Jef Driesen
70d3cdcc08 Use symbolic constant for invalid value 2021-08-16 15:26:45 +02:00
Jef Driesen
03974481b0 Remove duplicate macro definition 2021-08-09 10:04:17 +02:00
Jef Driesen
58d410b1a2 Add BLE support for the Aqualung i750TC
The Aqualung i750TC supports BLE, but the BLE handshaking fails and
needs to be disabled.
2021-08-08 20:34:49 +02:00
Jef Driesen
38bd51e63a Exclude the surface time from the dive time
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.
2021-07-26 17:20:16 +02:00
Jef Driesen
927362354d Some more fixes for the new Mares Genius firmware
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.
2021-07-13 22:45:40 +02:00
Jef Driesen
5bb6257acb Implement the salinity field
The 4th bit of the settings byte contains the salinity setting (salt or
fresh water).
2021-07-13 00:00:26 +02:00
Jef Driesen
060c0b7215 Fix the sample rate parsing
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.
2021-07-12 23:52:56 +02:00
Jef Driesen
5cb527d53c Add support for the new Mares Genius firmware
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.
2021-07-06 17:43:41 +02:00
Jef Driesen
cfc9ddc380 Use the correct standard gravity factor
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.
2021-07-06 16:16:18 +02:00
Jef Driesen
cffda88ae8 Use SI units internally
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).
2021-07-06 16:16:18 +02:00
Jef Driesen
b186846a9e Fix negative depth values
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.
2021-07-06 16:16:18 +02:00
Jef Driesen
da2446283a Move the unit conversion to the last moment
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.
2021-07-06 16:16:18 +02:00
Jef Driesen
6645b3f5e4 Implement the atmospheric pressure field 2021-07-06 16:16:18 +02:00
Jef Driesen
c8bd477c84 Always use the stored atmospheric pressure
The correct atmospheric pressure is measured and recorded by the dive
computer. There is no need to replace the correct value with some other
value.
2021-07-06 16:16:18 +02:00
Jef Driesen
c747dc7184 Add a CI job to build with Visual Studio
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.
2021-07-02 17:30:58 +02:00
Jef Driesen
9307acbe4a Migrate to Visual Studio 2013 (or newer)
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
2021-07-02 17:29:27 +02:00
Jef Driesen
9106250a53 Emit events when downloading a memory dump 2021-06-25 17:17:20 +02:00
Jef Driesen
449b65cf1b Fix the depth decoding
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.
2021-06-25 17:16:33 +02:00
Vincent Hagen
0bc7b195e5 Add library dependencies in windows build
Include libusb and hidapi when building for windows
these are also added to the artifact
2021-06-07 09:25:29 +02:00
Jef Driesen
fba5676b78 Fix the hwOS Sport firmware upgrade
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>
2021-05-26 12:04:18 +02:00
Jef Driesen
14fd0296d4 Post release version bump to 0.8.0 2021-05-07 21:22:36 +02:00
198 changed files with 13916 additions and 4246 deletions

View File

@ -19,7 +19,7 @@ jobs:
CC: ${{ matrix.compiler }} CC: ${{ matrix.compiler }}
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v4
- name: Install dependencies - name: Install dependencies
run: sudo apt-get install libbluetooth-dev libusb-1.0-0-dev run: sudo apt-get install libbluetooth-dev libusb-1.0-0-dev
- run: autoreconf --install --force - run: autoreconf --install --force
@ -30,7 +30,7 @@ jobs:
run: | run: |
make install DESTDIR=$PWD/artifacts make install DESTDIR=$PWD/artifacts
tar -czf ${{ github.job }}-${{ matrix.compiler }}.tar.gz -C artifacts usr tar -czf ${{ github.job }}-${{ matrix.compiler }}.tar.gz -C artifacts usr
- uses: actions/upload-artifact@v2 - uses: actions/upload-artifact@v4
with: with:
name: ${{ github.job }}-${{ matrix.compiler }} name: ${{ github.job }}-${{ matrix.compiler }}
path: ${{ github.job }}-${{ matrix.compiler }}.tar.gz path: ${{ github.job }}-${{ matrix.compiler }}.tar.gz
@ -50,7 +50,7 @@ jobs:
CC: ${{ matrix.compiler }} CC: ${{ matrix.compiler }}
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v4
- name: Install dependencies - name: Install dependencies
run: brew install autoconf automake libtool hidapi libusb run: brew install autoconf automake libtool hidapi libusb
- run: autoreconf --install --force - run: autoreconf --install --force
@ -61,7 +61,7 @@ jobs:
run: | run: |
make install DESTDIR=$PWD/artifacts make install DESTDIR=$PWD/artifacts
tar -czf ${{ github.job }}-${{ matrix.compiler }}.tar.gz -C artifacts usr tar -czf ${{ github.job }}-${{ matrix.compiler }}.tar.gz -C artifacts usr
- uses: actions/upload-artifact@v2 - uses: actions/upload-artifact@v4
with: with:
name: ${{ github.job }}-${{ matrix.compiler }} name: ${{ github.job }}-${{ matrix.compiler }}
path: ${{ github.job }}-${{ matrix.compiler }}.tar.gz path: ${{ github.job }}-${{ matrix.compiler }}.tar.gz
@ -78,18 +78,96 @@ jobs:
arch: [i686, x86_64] arch: [i686, x86_64]
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v4
- name: Install dependencies - name: Install dependencies
run: sudo apt-get install gcc-mingw-w64 binutils-mingw-w64 mingw-w64-tools run: sudo apt-get install gcc-mingw-w64 binutils-mingw-w64 mingw-w64-tools
- name: Install libusb
env:
LIBUSB_VERSION: 1.0.26
run: |
wget -c https://github.com/libusb/libusb/archive/refs/tags/v${LIBUSB_VERSION}.tar.gz
tar xzf v${LIBUSB_VERSION}.tar.gz
pushd libusb-${LIBUSB_VERSION}
autoreconf --install --force
./configure --host=${{ matrix.arch }}-w64-mingw32 --prefix=/usr
make
make install DESTDIR=$PWD/../artifacts
popd
- name: Install hidapi
env:
HIDAPI_VERSION: 0.12.0
run: |
wget -c https://github.com/libusb/hidapi/archive/refs/tags/hidapi-${HIDAPI_VERSION}.tar.gz
tar xzf hidapi-${HIDAPI_VERSION}.tar.gz
pushd hidapi-hidapi-${HIDAPI_VERSION}
autoreconf --install --force
./configure --host=${{ matrix.arch }}-w64-mingw32 --prefix=/usr LDFLAGS='-static-libgcc'
make
make install DESTDIR=$PWD/../artifacts
popd
- run: autoreconf --install --force - run: autoreconf --install --force
- run: ./configure --host=${{ matrix.arch }}-w64-mingw32 --prefix=/usr - run: ./configure --host=${{ matrix.arch }}-w64-mingw32 --prefix=/usr
env:
PKG_CONFIG_LIBDIR: ${{ github.workspace }}/artifacts/usr/lib/pkgconfig
PKG_CONFIG_SYSROOT_DIR: ${{ github.workspace }}/artifacts
PKG_CONFIG_ALLOW_SYSTEM_CFLAGS: 1
PKG_CONFIG_ALLOW_SYSTEM_LIBS: 1
- run: make - run: make
- run: make distcheck - run: make distcheck
- name: Package artifacts - name: Package artifacts
run: | run: |
make install DESTDIR=$PWD/artifacts make install DESTDIR=$PWD/artifacts
tar -czf ${{ github.job }}-${{ matrix.arch }}.tar.gz -C artifacts usr tar -czf ${{ github.job }}-${{ matrix.arch }}.tar.gz -C artifacts usr
- uses: actions/upload-artifact@v2 - uses: actions/upload-artifact@v4
with: with:
name: ${{ github.job }}-${{ matrix.arch }} name: ${{ github.job }}-${{ matrix.arch }}
path: ${{ github.job }}-${{ matrix.arch }}.tar.gz path: ${{ github.job }}-${{ matrix.arch }}.tar.gz
msvc:
name: Visual Studio
runs-on: windows-latest
strategy:
fail-fast: false
matrix:
platform: [x86, x64]
env:
CONFIGURATION: Release
steps:
- uses: actions/checkout@v4
- uses: msys2/setup-msys2@v2
with:
install: autoconf automake libtool pkg-config make gcc
- run: |
autoreconf --install --force
./configure --prefix=/usr
make -C src revision.h
shell: msys2 {0}
- uses: microsoft/setup-msbuild@v2
- run: msbuild -m -p:Platform=${{ matrix.platform }} -p:Configuration=${{ env.CONFIGURATION }} contrib/msvc/libdivecomputer.vcxproj
- uses: actions/upload-artifact@v4
with:
name: ${{ github.job }}-${{ matrix.platform }}
path: contrib/msvc/${{ matrix.platform }}/${{ env.CONFIGURATION }}/bin
android:
name: Android
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: |
autoreconf --install --force
./configure --prefix=/usr
make -C src revision.h
- run: $ANDROID_NDK/ndk-build -C contrib/android NDK_PROJECT_PATH=. APP_BUILD_SCRIPT=Android.mk
- uses: actions/upload-artifact@v4
with:
name: ${{ github.job }}
path: contrib/android/libs

View File

@ -9,19 +9,13 @@ jobs:
name: Release name: Release
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v4
- name: Version number - name: Version number
id: version id: version
run: | run: |
VERSION="${GITHUB_REF/refs\/tags\/v/}" VERSION="${GITHUB_REF/refs\/tags\/v/}"
if [ "${VERSION}" = "${VERSION%%-*}" ]; then echo "version=${VERSION}" >> $GITHUB_OUTPUT
PRERELEASE=false
else
PRERELEASE=true
fi
echo ::set-output name=version::${VERSION}
echo ::set-output name=prerelease::${PRERELEASE}
- name: Build distribution tarball - name: Build distribution tarball
id: build id: build
@ -41,21 +35,13 @@ jobs:
exit 1 exit 1
fi fi
- uses: actions/create-release@v1 - name: Create Github release
id: release id: release
run: |
VERSION="${{ steps.version.outputs.version }}"
if [ "${VERSION}" != "${VERSION%%-*}" ]; then
PRERELEASE="-p"
fi
gh release create ${PRERELEASE} "${{ github.ref }}" "libdivecomputer-${VERSION}.tar.gz"
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ github.ref }}
release_name: ${{ github.ref }}
prerelease: ${{ steps.version.outputs.prerelease }}
- uses: actions/upload-release-asset@v1
id: upload
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.release.outputs.upload_url }}
asset_path: libdivecomputer-${{ steps.version.outputs.version }}.tar.gz
asset_name: libdivecomputer-${{ steps.version.outputs.version }}.tar.gz
asset_content_type: application/gzip

5
.gitignore vendored
View File

@ -47,11 +47,10 @@ Makefile.in
/m4/ltsugar.m4 /m4/ltsugar.m4
/m4/ltversion.m4 /m4/ltversion.m4
/msvc/Debug/ /msvc/x64/
/msvc/Release/ /msvc/x86/
/msvc/*.ncb /msvc/*.ncb
/msvc/*.suo /msvc/*.suo
/msvc/*.vcproj.*.user
/src/libdivecomputer.exp /src/libdivecomputer.exp
/src/libdivecomputer.la /src/libdivecomputer.la

View File

@ -16,4 +16,8 @@ pkgconfig_DATA = libdivecomputer.pc
EXTRA_DIST = \ EXTRA_DIST = \
libdivecomputer.pc.in \ libdivecomputer.pc.in \
msvc/libdivecomputer.vcproj contrib/README \
contrib/android/Android.mk \
contrib/msvc/libdivecomputer.vcxproj \
contrib/msvc/libdivecomputer.vcxproj.filters \
contrib/udev/libdivecomputer.rules

36
NEWS
View File

@ -1,4 +1,38 @@
Version 0.7.0 (2020-05-07) Version 0.8.0 (2023-05-11)
==========================
The v0.8.0 release is mainly a bugfix release, and brings in support for a
number of new devices. This release is fully backwards compatible with the
previous one.
New features:
* Add support for new backends:
- excursion: Deep Six Excursion, Crest CR-4, Genesis Centauri, Tusa TC1, Scorpena Alpha
- screen: Seac Screen and Action
- cosmiq: Deepblu Cosmiq+
- s1: Oceans S1
- freedom: Divesoft Freedom and Liberty
* Add support for some new devices:
- Aqualung: i200C
- Cressi: Donatello, Michelangelo, Neon
- Mares: Puck Pro +
- Oceanic: Geo Air
- Ratio: iX3M 2
- Scubapro: G2 TEK
- Shearwater: Petrel 3, Perdix 2
- Sherwood: Amphos Air 2.0
* Add support for parsing the decompression model
* Add a public api to configure the depth calibration
* Add a public api to configure the clock synchronization
* Add a basic Android build system
Removed/changed features:
* Migrate to Visual Studio 2013 (or newer)
* Move the Visual Studio project to the contrib directory
Version 0.7.0 (2021-05-07)
========================== ==========================
The main highlight of the v0.7.0 release is the introduction of the new The main highlight of the v0.7.0 release is the introduction of the new

View File

@ -1,8 +1,8 @@
# Versioning. # Versioning.
m4_define([dc_version_major],[0]) m4_define([dc_version_major],[0])
m4_define([dc_version_minor],[7]) m4_define([dc_version_minor],[9])
m4_define([dc_version_micro],[0]) m4_define([dc_version_micro],[0])
m4_define([dc_version_suffix],[]) m4_define([dc_version_suffix],[devel])
m4_define([dc_version],dc_version_major.dc_version_minor.dc_version_micro[]m4_ifset([dc_version_suffix],-[dc_version_suffix])) m4_define([dc_version],dc_version_major.dc_version_minor.dc_version_micro[]m4_ifset([dc_version_suffix],-[dc_version_suffix]))
# Libtool versioning. # Libtool versioning.
@ -107,12 +107,12 @@ AS_IF([test "x$with_libusb" != "xno"], [
AC_ARG_WITH([hidapi], AC_ARG_WITH([hidapi],
[AS_HELP_STRING([--without-hidapi], [AS_HELP_STRING([--without-hidapi],
[Build without the hidapi library])], [Build without the hidapi library])],
[], [with_hidapi=auto]) [], [with_hidapi=hidapi])
AS_IF([test "x$with_hidapi" != "xno"], [ AS_IF([test "x$with_hidapi" != "xno"], [
PKG_CHECK_MODULES([HIDAPI], [hidapi], [have_hidapi=yes], [have_hidapi=no]) PKG_CHECK_MODULES([HIDAPI], [$with_hidapi], [have_hidapi=yes], [have_hidapi=no])
AS_IF([test "x$have_hidapi" = "xyes"], [ AS_IF([test "x$have_hidapi" = "xyes"], [
AC_DEFINE([HAVE_HIDAPI], [1], [hidapi library]) AC_DEFINE([HAVE_HIDAPI], [1], [hidapi library])
DEPENDENCIES="$DEPENDENCIES hidapi" DEPENDENCIES="$DEPENDENCIES $with_hidapi"
]) ])
]) ])
@ -157,7 +157,7 @@ AC_CHECK_HEADERS([sys/socket.h linux/types.h linux/irda.h], , , [
# Checks for header files. # Checks for header files.
AC_CHECK_HEADERS([linux/serial.h]) AC_CHECK_HEADERS([linux/serial.h])
AC_CHECK_HEADERS([IOKit/serial/ioss.h]) AC_CHECK_HEADERS([IOKit/serial/ioss.h])
AC_CHECK_HEADERS([getopt.h]) AC_CHECK_HEADERS([unistd.h getopt.h])
AC_CHECK_HEADERS([sys/param.h]) AC_CHECK_HEADERS([sys/param.h])
AC_CHECK_HEADERS([pthread.h]) AC_CHECK_HEADERS([pthread.h])
AC_CHECK_HEADERS([mach/mach_time.h]) AC_CHECK_HEADERS([mach/mach_time.h])
@ -238,7 +238,6 @@ AC_CONFIG_FILES([
include/libdivecomputer/Makefile include/libdivecomputer/Makefile
include/libdivecomputer/version.h include/libdivecomputer/version.h
src/Makefile src/Makefile
src/libdivecomputer.rc
doc/Makefile doc/Makefile
doc/doxygen.cfg doc/doxygen.cfg
doc/man/Makefile doc/man/Makefile

56
contrib/README Normal file
View File

@ -0,0 +1,56 @@
Alternative build systems
=========================
The autotools based build system is the official build system for the
libdivecomputer project. But for convenience, a few alternative build systems
are available as well. Unfortunately, these builds systems require a few extra
steps to generate some header files.
If you have access to a UNIX build system (for example a Linux virtual machine,
MinGW, Cygwin or the Windows Subsystem for Linux), you can use the autotools
build system to generate those files:
$ autoreconf --install --force
$ ./configure
$ make -C src revision.h
Alternative, you can generate those files manually. First, create the version.h
file from the version.h.in template:
$ cp include/libdivecomputer/version.h.in include/libdivecomputer/version.h
and replace all the @DC_VERSION@ placeholders with the values defined in the
configure.ac file.
Next, generate the revision.h file:
$ echo "#define DC_VERSION_REVISION \"$(git rev-parse --verify HEAD)\"" > src/revision.h
The alternative build systems are ready to use now.
Visual Studio
-------------
The Visual Studio project file can be opened in the IDE, or build directly from
the command-line:
msbuild -m -p:Platform=x86|x64 -p:Configuration=Debug|Release contrib/msvc/libdivecomputer.vcxproj
Android NDK
-----------
$ANDROID_NDK/ndk-build -C contrib/android NDK_PROJECT_PATH=. APP_BUILD_SCRIPT=Android.mk
Linux udev rules
================
For dive computers using USB or USB HID communication, regular users typically
don't have the necessary permissions to access the device nodes. This can be
fixed with some udev rules.
Install the udev rules, and reload them:
$ sudo cp contrib/udev/libdivecomputer.rules /etc/udev/rules.d/
$ sudo udevadm control --reload
Note: the provided udev rules assume the user is in the plugdev group.

144
contrib/android/Android.mk Normal file
View File

@ -0,0 +1,144 @@
LOCAL_PATH := $(call my-dir)/../..
include $(CLEAR_VARS)
LOCAL_MODULE := libdivecomputer
LOCAL_CFLAGS := -DENABLE_LOGGING -DHAVE_VERSION_SUFFIX -DHAVE_PTHREAD_H -DHAVE_STRERROR_R -DHAVE_CLOCK_GETTIME -DHAVE_LOCALTIME_R -DHAVE_GMTIME_R -DHAVE_TIMEGM -DHAVE_STRUCT_TM_TM_GMTOFF
LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
LOCAL_SRC_FILES := \
src/aes.c \
src/array.c \
src/atomics_cobalt.c \
src/atomics_cobalt_parser.c \
src/bluetooth.c \
src/buffer.c \
src/checksum.c \
src/citizen_aqualand.c \
src/citizen_aqualand_parser.c \
src/cochran_commander.c \
src/cochran_commander_parser.c \
src/common.c \
src/context.c \
src/cressi_edy.c \
src/cressi_edy_parser.c \
src/cressi_goa.c \
src/cressi_goa_parser.c \
src/cressi_leonardo.c \
src/cressi_leonardo_parser.c \
src/custom.c \
src/datetime.c \
src/deepblu_cosmiq.c \
src/deepblu_cosmiq_parser.c \
src/deepsix_excursion.c \
src/deepsix_excursion_parser.c \
src/descriptor.c \
src/device.c \
src/diverite_nitekq.c \
src/diverite_nitekq_parser.c \
src/divesoft_freedom.c \
src/divesoft_freedom_parser.c \
src/divesystem_idive.c \
src/divesystem_idive_parser.c \
src/hdlc.c \
src/hw_frog.c \
src/hw_ostc3.c \
src/hw_ostc.c \
src/hw_ostc_parser.c \
src/ihex.c \
src/iostream.c \
src/irda.c \
src/iterator.c \
src/liquivision_lynx.c \
src/liquivision_lynx_parser.c \
src/mares_common.c \
src/mares_darwin.c \
src/mares_darwin_parser.c \
src/mares_iconhd.c \
src/mares_iconhd_parser.c \
src/mares_nemo.c \
src/mares_nemo_parser.c \
src/mares_puck.c \
src/mclean_extreme.c \
src/mclean_extreme_parser.c \
src/oceanic_atom2.c \
src/oceanic_atom2_parser.c \
src/oceanic_common.c \
src/oceanic_veo250.c \
src/oceanic_veo250_parser.c \
src/oceanic_vtpro.c \
src/oceanic_vtpro_parser.c \
src/oceans_s1.c \
src/oceans_s1_common.c \
src/oceans_s1_parser.c \
src/packet.c \
src/parser.c \
src/pelagic_i330r.c \
src/platform.c \
src/rbstream.c \
src/reefnet_sensus.c \
src/reefnet_sensus_parser.c \
src/reefnet_sensuspro.c \
src/reefnet_sensuspro_parser.c \
src/reefnet_sensusultra.c \
src/reefnet_sensusultra_parser.c \
src/ringbuffer.c \
src/seac_screen.c \
src/seac_screen_parser.c \
src/serial_posix.c \
src/shearwater_common.c \
src/shearwater_petrel.c \
src/shearwater_predator.c \
src/shearwater_predator_parser.c \
src/socket.c \
src/sporasub_sp2.c \
src/sporasub_sp2_parser.c \
src/suunto_common2.c \
src/suunto_common.c \
src/suunto_d9.c \
src/suunto_d9_parser.c \
src/suunto_eon.c \
src/suunto_eon_parser.c \
src/suunto_eonsteel.c \
src/suunto_eonsteel_parser.c \
src/suunto_solution.c \
src/suunto_solution_parser.c \
src/suunto_vyper2.c \
src/suunto_vyper.c \
src/suunto_vyper_parser.c \
src/tecdiving_divecomputereu.c \
src/tecdiving_divecomputereu_parser.c \
src/timer.c \
src/usb.c \
src/usbhid.c \
src/uwatec_aladin.c \
src/uwatec_memomouse.c \
src/uwatec_memomouse_parser.c \
src/uwatec_smart.c \
src/uwatec_smart_parser.c \
src/version.c \
src/zeagle_n2ition3.c
include $(BUILD_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := dctool
LOCAL_SHARED_LIBRARIES := libdivecomputer
LOCAL_CFLAGS := -DHAVE_UNISTD_H -DHAVE_GETOPT_H -DHAVE_GETOPT_LONG -DHAVE_DECL_OPTRESET=1
LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
LOCAL_SRC_FILES := \
examples/common.c \
examples/dctool.c \
examples/dctool_download.c \
examples/dctool_dump.c \
examples/dctool_fwupdate.c \
examples/dctool_help.c \
examples/dctool_list.c \
examples/dctool_parse.c \
examples/dctool_read.c \
examples/dctool_scan.c \
examples/dctool_timesync.c \
examples/dctool_version.c \
examples/dctool_write.c \
examples/output.c \
examples/output_raw.c \
examples/output_xml.c \
examples/utils.c
include $(BUILD_EXECUTABLE)

View File

@ -0,0 +1,408 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{CEA7215A-D6B5-4840-8086-3C854F371997}</ProjectGuid>
<RootNamespace>libdivecomputer</RootNamespace>
<Keyword>Win32Proj</Keyword>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>$(DefaultPlatformToolset)</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>$(DefaultPlatformToolset)</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>$(DefaultPlatformToolset)</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
<WholeProgramOptimization>true</WholeProgramOptimization>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>$(DefaultPlatformToolset)</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
<WholeProgramOptimization>true</WholeProgramOptimization>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
<OutDir>$(SolutionDir)$(PlatformTarget)\$(Configuration)\bin\</OutDir>
<IntDir>$(PlatformTarget)\$(Configuration)\obj\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
<OutDir>$(SolutionDir)$(PlatformTarget)\$(Configuration)\bin\</OutDir>
<IntDir>$(PlatformTarget)\$(Configuration)\obj\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
<OutDir>$(SolutionDir)$(PlatformTarget)\$(Configuration)\bin\</OutDir>
<IntDir>$(PlatformTarget)\$(Configuration)\obj\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
<OutDir>$(SolutionDir)$(PlatformTarget)\$(Configuration)\bin\</OutDir>
<IntDir>$(PlatformTarget)\$(Configuration)\obj\</IntDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<Optimization>Disabled</Optimization>
<AdditionalIncludeDirectories>..\..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBDIVECOMPUTER_EXPORTS;ENABLE_LOGGING;HAVE_VERSION_SUFFIX;HAVE_AF_IRDA_H;HAVE_WS2BTH_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PrecompiledHeader />
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
</ClCompile>
<Link>
<AdditionalDependencies>ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<ModuleDefinitionFile>$(OutDir)libdivecomputer.def</ModuleDefinitionFile>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Windows</SubSystem>
</Link>
<ResourceCompile>
<AdditionalIncludeDirectories>..\..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ResourceCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<Optimization>Disabled</Optimization>
<AdditionalIncludeDirectories>..\..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBDIVECOMPUTER_EXPORTS;ENABLE_LOGGING;HAVE_VERSION_SUFFIX;HAVE_AF_IRDA_H;HAVE_WS2BTH_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
</ClCompile>
<Link>
<AdditionalDependencies>ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<ModuleDefinitionFile>$(OutDir)libdivecomputer.def</ModuleDefinitionFile>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Windows</SubSystem>
</Link>
<ResourceCompile>
<AdditionalIncludeDirectories>..\..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ResourceCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<Optimization>MaxSpeed</Optimization>
<IntrinsicFunctions>true</IntrinsicFunctions>
<AdditionalIncludeDirectories>..\..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_WINDOWS;_USRDLL;LIBDIVECOMPUTER_EXPORTS;ENABLE_LOGGING;HAVE_VERSION_SUFFIX;HAVE_AF_IRDA_H;HAVE_WS2BTH_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<FunctionLevelLinking>true</FunctionLevelLinking>
<PrecompiledHeader />
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
</ClCompile>
<Link>
<AdditionalDependencies>ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<ModuleDefinitionFile>$(OutDir)libdivecomputer.def</ModuleDefinitionFile>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Windows</SubSystem>
<OptimizeReferences>true</OptimizeReferences>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
</Link>
<ResourceCompile>
<AdditionalIncludeDirectories>..\..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ResourceCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<Optimization>MaxSpeed</Optimization>
<IntrinsicFunctions>true</IntrinsicFunctions>
<AdditionalIncludeDirectories>..\..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_WINDOWS;_USRDLL;LIBDIVECOMPUTER_EXPORTS;ENABLE_LOGGING;HAVE_VERSION_SUFFIX;HAVE_AF_IRDA_H;HAVE_WS2BTH_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<FunctionLevelLinking>true</FunctionLevelLinking>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
</ClCompile>
<Link>
<AdditionalDependencies>ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<ModuleDefinitionFile>$(OutDir)libdivecomputer.def</ModuleDefinitionFile>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Windows</SubSystem>
<OptimizeReferences>true</OptimizeReferences>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
</Link>
<ResourceCompile>
<AdditionalIncludeDirectories>..\..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ResourceCompile>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\..\src\aes.c" />
<ClCompile Include="..\..\src\array.c" />
<ClCompile Include="..\..\src\atomics_cobalt.c" />
<ClCompile Include="..\..\src\atomics_cobalt_parser.c" />
<ClCompile Include="..\..\src\bluetooth.c" />
<ClCompile Include="..\..\src\buffer.c" />
<ClCompile Include="..\..\src\checksum.c" />
<ClCompile Include="..\..\src\citizen_aqualand.c" />
<ClCompile Include="..\..\src\citizen_aqualand_parser.c" />
<ClCompile Include="..\..\src\cochran_commander.c" />
<ClCompile Include="..\..\src\cochran_commander_parser.c" />
<ClCompile Include="..\..\src\common.c" />
<ClCompile Include="..\..\src\context.c" />
<ClCompile Include="..\..\src\cressi_edy.c" />
<ClCompile Include="..\..\src\cressi_edy_parser.c" />
<ClCompile Include="..\..\src\cressi_goa.c" />
<ClCompile Include="..\..\src\cressi_goa_parser.c" />
<ClCompile Include="..\..\src\cressi_leonardo.c" />
<ClCompile Include="..\..\src\cressi_leonardo_parser.c" />
<ClCompile Include="..\..\src\custom.c" />
<ClCompile Include="..\..\src\datetime.c" />
<ClCompile Include="..\..\src\deepblu_cosmiq.c" />
<ClCompile Include="..\..\src\deepblu_cosmiq_parser.c" />
<ClCompile Include="..\..\src\deepsix_excursion.c" />
<ClCompile Include="..\..\src\deepsix_excursion_parser.c" />
<ClCompile Include="..\..\src\descriptor.c" />
<ClCompile Include="..\..\src\device.c" />
<ClCompile Include="..\..\src\diverite_nitekq.c" />
<ClCompile Include="..\..\src\diverite_nitekq_parser.c" />
<ClCompile Include="..\..\src\divesoft_freedom.c" />
<ClCompile Include="..\..\src\divesoft_freedom_parser.c" />
<ClCompile Include="..\..\src\divesystem_idive.c" />
<ClCompile Include="..\..\src\divesystem_idive_parser.c" />
<ClCompile Include="..\..\src\hdlc.c" />
<ClCompile Include="..\..\src\hw_frog.c" />
<ClCompile Include="..\..\src\hw_ostc.c" />
<ClCompile Include="..\..\src\hw_ostc3.c" />
<ClCompile Include="..\..\src\hw_ostc_parser.c" />
<ClCompile Include="..\..\src\ihex.c" />
<ClCompile Include="..\..\src\iostream.c" />
<ClCompile Include="..\..\src\irda.c" />
<ClCompile Include="..\..\src\iterator.c" />
<ClCompile Include="..\..\src\liquivision_lynx.c" />
<ClCompile Include="..\..\src\liquivision_lynx_parser.c" />
<ClCompile Include="..\..\src\mares_common.c" />
<ClCompile Include="..\..\src\mares_darwin.c" />
<ClCompile Include="..\..\src\mares_darwin_parser.c" />
<ClCompile Include="..\..\src\mares_iconhd.c" />
<ClCompile Include="..\..\src\mares_iconhd_parser.c" />
<ClCompile Include="..\..\src\mares_nemo.c" />
<ClCompile Include="..\..\src\mares_nemo_parser.c" />
<ClCompile Include="..\..\src\mares_puck.c" />
<ClCompile Include="..\..\src\mclean_extreme.c" />
<ClCompile Include="..\..\src\mclean_extreme_parser.c" />
<ClCompile Include="..\..\src\oceanic_atom2.c" />
<ClCompile Include="..\..\src\oceanic_atom2_parser.c" />
<ClCompile Include="..\..\src\oceanic_common.c" />
<ClCompile Include="..\..\src\oceanic_veo250.c" />
<ClCompile Include="..\..\src\oceanic_veo250_parser.c" />
<ClCompile Include="..\..\src\oceanic_vtpro.c" />
<ClCompile Include="..\..\src\oceanic_vtpro_parser.c" />
<ClCompile Include="..\..\src\oceans_s1.c" />
<ClCompile Include="..\..\src\oceans_s1_common.c" />
<ClCompile Include="..\..\src\oceans_s1_parser.c" />
<ClCompile Include="..\..\src\packet.c" />
<ClCompile Include="..\..\src\parser.c" />
<ClCompile Include="..\..\src\pelagic_i330r.c" />
<ClCompile Include="..\..\src\platform.c" />
<ClCompile Include="..\..\src\rbstream.c" />
<ClCompile Include="..\..\src\reefnet_sensus.c" />
<ClCompile Include="..\..\src\reefnet_sensuspro.c" />
<ClCompile Include="..\..\src\reefnet_sensuspro_parser.c" />
<ClCompile Include="..\..\src\reefnet_sensusultra.c" />
<ClCompile Include="..\..\src\reefnet_sensusultra_parser.c" />
<ClCompile Include="..\..\src\reefnet_sensus_parser.c" />
<ClCompile Include="..\..\src\ringbuffer.c" />
<ClCompile Include="..\..\src\seac_screen.c" />
<ClCompile Include="..\..\src\seac_screen_parser.c" />
<ClCompile Include="..\..\src\serial_win32.c" />
<ClCompile Include="..\..\src\shearwater_common.c" />
<ClCompile Include="..\..\src\shearwater_petrel.c" />
<ClCompile Include="..\..\src\shearwater_predator.c" />
<ClCompile Include="..\..\src\shearwater_predator_parser.c" />
<ClCompile Include="..\..\src\socket.c" />
<ClCompile Include="..\..\src\sporasub_sp2.c" />
<ClCompile Include="..\..\src\sporasub_sp2_parser.c" />
<ClCompile Include="..\..\src\suunto_common.c" />
<ClCompile Include="..\..\src\suunto_common2.c" />
<ClCompile Include="..\..\src\suunto_d9.c" />
<ClCompile Include="..\..\src\suunto_d9_parser.c" />
<ClCompile Include="..\..\src\suunto_eon.c" />
<ClCompile Include="..\..\src\suunto_eonsteel.c" />
<ClCompile Include="..\..\src\suunto_eonsteel_parser.c" />
<ClCompile Include="..\..\src\suunto_eon_parser.c" />
<ClCompile Include="..\..\src\suunto_solution.c" />
<ClCompile Include="..\..\src\suunto_solution_parser.c" />
<ClCompile Include="..\..\src\suunto_vyper.c" />
<ClCompile Include="..\..\src\suunto_vyper2.c" />
<ClCompile Include="..\..\src\suunto_vyper_parser.c" />
<ClCompile Include="..\..\src\tecdiving_divecomputereu.c" />
<ClCompile Include="..\..\src\tecdiving_divecomputereu_parser.c" />
<ClCompile Include="..\..\src\timer.c" />
<ClCompile Include="..\..\src\usb.c" />
<ClCompile Include="..\..\src\usbhid.c" />
<ClCompile Include="..\..\src\uwatec_aladin.c" />
<ClCompile Include="..\..\src\uwatec_memomouse.c" />
<ClCompile Include="..\..\src\uwatec_memomouse_parser.c" />
<ClCompile Include="..\..\src\uwatec_smart.c" />
<ClCompile Include="..\..\src\uwatec_smart_parser.c" />
<ClCompile Include="..\..\src\version.c" />
<ClCompile Include="..\..\src\zeagle_n2ition3.c" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\include\libdivecomputer\atomics_cobalt.h" />
<ClInclude Include="..\..\include\libdivecomputer\ble.h" />
<ClInclude Include="..\..\include\libdivecomputer\bluetooth.h" />
<ClInclude Include="..\..\include\libdivecomputer\buffer.h" />
<ClInclude Include="..\..\include\libdivecomputer\common.h" />
<ClInclude Include="..\..\include\libdivecomputer\context.h" />
<ClInclude Include="..\..\include\libdivecomputer\custom.h" />
<ClInclude Include="..\..\include\libdivecomputer\datetime.h" />
<ClInclude Include="..\..\include\libdivecomputer\descriptor.h" />
<ClInclude Include="..\..\include\libdivecomputer\device.h" />
<ClInclude Include="..\..\include\libdivecomputer\divesystem_idive.h" />
<ClInclude Include="..\..\include\libdivecomputer\hw_frog.h" />
<ClInclude Include="..\..\include\libdivecomputer\hw_ostc.h" />
<ClInclude Include="..\..\include\libdivecomputer\hw_ostc3.h" />
<ClInclude Include="..\..\include\libdivecomputer\ioctl.h" />
<ClInclude Include="..\..\include\libdivecomputer\iostream.h" />
<ClInclude Include="..\..\include\libdivecomputer\irda.h" />
<ClInclude Include="..\..\include\libdivecomputer\iterator.h" />
<ClInclude Include="..\..\include\libdivecomputer\oceanic_atom2.h" />
<ClInclude Include="..\..\include\libdivecomputer\oceanic_veo250.h" />
<ClInclude Include="..\..\include\libdivecomputer\oceanic_vtpro.h" />
<ClInclude Include="..\..\include\libdivecomputer\parser.h" />
<ClInclude Include="..\..\include\libdivecomputer\reefnet_sensus.h" />
<ClInclude Include="..\..\include\libdivecomputer\reefnet_sensuspro.h" />
<ClInclude Include="..\..\include\libdivecomputer\reefnet_sensusultra.h" />
<ClInclude Include="..\..\include\libdivecomputer\serial.h" />
<ClInclude Include="..\..\include\libdivecomputer\suunto_d9.h" />
<ClInclude Include="..\..\include\libdivecomputer\suunto_eon.h" />
<ClInclude Include="..\..\include\libdivecomputer\suunto_vyper2.h" />
<ClInclude Include="..\..\include\libdivecomputer\units.h" />
<ClInclude Include="..\..\include\libdivecomputer\usb.h" />
<ClInclude Include="..\..\include\libdivecomputer\usbhid.h" />
<ClInclude Include="..\..\include\libdivecomputer\version.h" />
<ClInclude Include="..\..\src\aes.h" />
<ClInclude Include="..\..\src\array.h" />
<ClInclude Include="..\..\src\atomics_cobalt.h" />
<ClInclude Include="..\..\src\checksum.h" />
<ClInclude Include="..\..\src\citizen_aqualand.h" />
<ClInclude Include="..\..\src\cochran_commander.h" />
<ClInclude Include="..\..\src\common-private.h" />
<ClInclude Include="..\..\src\context-private.h" />
<ClInclude Include="..\..\src\cressi_edy.h" />
<ClInclude Include="..\..\src\cressi_goa.h" />
<ClInclude Include="..\..\src\cressi_leonardo.h" />
<ClInclude Include="..\..\src\deepblu_cosmiq.h" />
<ClInclude Include="..\..\src\deepsix_excursion.h" />
<ClInclude Include="..\..\src\device-private.h" />
<ClInclude Include="..\..\src\diverite_nitekq.h" />
<ClInclude Include="..\..\src\divesoft_freedom.h" />
<ClInclude Include="..\..\src\divesystem_idive.h" />
<ClInclude Include="..\..\src\hdlc.h" />
<ClInclude Include="..\..\src\hw_frog.h" />
<ClInclude Include="..\..\src\hw_ostc.h" />
<ClInclude Include="..\..\src\hw_ostc3.h" />
<ClInclude Include="..\..\src\ihex.h" />
<ClInclude Include="..\..\src\iostream-private.h" />
<ClInclude Include="..\..\src\iterator-private.h" />
<ClInclude Include="..\..\src\liquivision_lynx.h" />
<ClInclude Include="..\..\src\mares_common.h" />
<ClInclude Include="..\..\src\mares_darwin.h" />
<ClInclude Include="..\..\src\mares_iconhd.h" />
<ClInclude Include="..\..\src\mares_nemo.h" />
<ClInclude Include="..\..\src\mares_puck.h" />
<ClInclude Include="..\..\src\mclean_extreme.h" />
<ClInclude Include="..\..\src\oceanic_atom2.h" />
<ClInclude Include="..\..\src\oceanic_common.h" />
<ClInclude Include="..\..\src\oceanic_veo250.h" />
<ClInclude Include="..\..\src\oceanic_vtpro.h" />
<ClInclude Include="..\..\src\oceans_s1.h" />
<ClInclude Include="..\..\src\oceans_s1_common.h" />
<ClInclude Include="..\..\src\packet.h" />
<ClInclude Include="..\..\src\parser-private.h" />
<ClInclude Include="..\..\src\pelagic_i330r.h" />
<ClInclude Include="..\..\src\platform.h" />
<ClInclude Include="..\..\src\rbstream.h" />
<ClInclude Include="..\..\src\reefnet_sensus.h" />
<ClInclude Include="..\..\src\reefnet_sensuspro.h" />
<ClInclude Include="..\..\src\reefnet_sensusultra.h" />
<ClInclude Include="..\..\src\revision.h" />
<ClInclude Include="..\..\src\ringbuffer.h" />
<ClInclude Include="..\..\src\seac_screen.h" />
<ClInclude Include="..\..\src\shearwater_common.h" />
<ClInclude Include="..\..\src\shearwater_petrel.h" />
<ClInclude Include="..\..\src\shearwater_predator.h" />
<ClInclude Include="..\..\src\socket.h" />
<ClInclude Include="..\..\src\sporasub_sp2.h" />
<ClInclude Include="..\..\src\suunto_common.h" />
<ClInclude Include="..\..\src\suunto_common2.h" />
<ClInclude Include="..\..\src\suunto_d9.h" />
<ClInclude Include="..\..\src\suunto_eon.h" />
<ClInclude Include="..\..\src\suunto_eonsteel.h" />
<ClInclude Include="..\..\src\suunto_solution.h" />
<ClInclude Include="..\..\src\suunto_vyper.h" />
<ClInclude Include="..\..\src\suunto_vyper2.h" />
<ClInclude Include="..\..\src\tecdiving_divecomputereu.h" />
<ClInclude Include="..\..\src\timer.h" />
<ClInclude Include="..\..\src\uwatec_aladin.h" />
<ClInclude Include="..\..\src\uwatec_memomouse.h" />
<ClInclude Include="..\..\src\uwatec_smart.h" />
<ClInclude Include="..\..\src\zeagle_n2ition3.h" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="..\..\src\libdivecomputer.rc" />
</ItemGroup>
<ItemGroup>
<CustomBuild Include="..\..\src\libdivecomputer.symbols">
<Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">echo EXPORTS &gt; "$(OutDir)libdivecomputer.def" &amp;&amp; type "%(FullPath)" &gt;&gt; "$(OutDir)libdivecomputer.def"</Command>
<Command Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">echo EXPORTS &gt; "$(OutDir)libdivecomputer.def" &amp;&amp; type "%(FullPath)" &gt;&gt; "$(OutDir)libdivecomputer.def"</Command>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(OutDir)libdivecomputer.def;%(Outputs)</Outputs>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(OutDir)libdivecomputer.def;%(Outputs)</Outputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">echo EXPORTS &gt; "$(OutDir)libdivecomputer.def" &amp;&amp; type "%(FullPath)" &gt;&gt; "$(OutDir)libdivecomputer.def"</Command>
<Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">echo EXPORTS &gt; "$(OutDir)libdivecomputer.def" &amp;&amp; type "%(FullPath)" &gt;&gt; "$(OutDir)libdivecomputer.def"</Command>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(OutDir)libdivecomputer.def;%(Outputs)</Outputs>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(OutDir)libdivecomputer.def;%(Outputs)</Outputs>
</CustomBuild>
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
</Project>

View File

@ -1,23 +1,35 @@
# Atomic Aquatics Cobalt # Atomic Aquatics Cobalt
SUBSYSTEM=="usb", ATTR{idVendor}=="0471", ATTR{idProduct}=="0888", GROUP="plugdev" SUBSYSTEM=="usb", ATTR{idVendor}=="0471", ATTR{idProduct}=="0888", GROUP="plugdev"
SUBSYSTEM=="hidraw", ATTRS{idVendor}=="0471", ATTRS{idProduct}=="0888", GROUP="plugdev"
# Suunto EON Steel # Suunto EON Steel
SUBSYSTEM=="usb", ATTR{idVendor}=="1493", ATTR{idProduct}=="0030", GROUP="plugdev" SUBSYSTEM=="usb", ATTR{idVendor}=="1493", ATTR{idProduct}=="0030", GROUP="plugdev"
SUBSYSTEM=="hidraw", ATTRS{idVendor}=="1493", ATTRS{idProduct}=="0030", GROUP="plugdev"
# Suunto EON Core # Suunto EON Core
SUBSYSTEM=="usb", ATTR{idVendor}=="1493", ATTR{idProduct}=="0033", GROUP="plugdev" SUBSYSTEM=="usb", ATTR{idVendor}=="1493", ATTR{idProduct}=="0033", GROUP="plugdev"
SUBSYSTEM=="hidraw", ATTRS{idVendor}=="1493", ATTRS{idProduct}=="0033", GROUP="plugdev"
# Suunto D5 # Suunto D5
SUBSYSTEM=="usb", ATTR{idVendor}=="1493", ATTR{idProduct}=="0035", GROUP="plugdev" SUBSYSTEM=="usb", ATTR{idVendor}=="1493", ATTR{idProduct}=="0035", GROUP="plugdev"
SUBSYSTEM=="hidraw", ATTRS{idVendor}=="1493", ATTRS{idProduct}=="0035", GROUP="plugdev"
# Suunto EON Steel Black
SUBSYSTEM=="usb", ATTR{idVendor}=="1493", ATTR{idProduct}=="0036", GROUP="plugdev"
SUBSYSTEM=="hidraw", ATTRS{idVendor}=="1493", ATTRS{idProduct}=="0036", GROUP="plugdev"
# Scubapro G2 # Scubapro G2
SUBSYSTEM=="usb", ATTR{idVendor}=="2e6c", ATTR{idProduct}=="3201", GROUP="plugdev" SUBSYSTEM=="usb", ATTR{idVendor}=="2e6c", ATTR{idProduct}=="3201", GROUP="plugdev"
SUBSYSTEM=="hidraw", ATTRS{idVendor}=="2e6c", ATTRS{idProduct}=="3201", GROUP="plugdev"
# Scubapro G2 Console # Scubapro G2 Console
SUBSYSTEM=="usb", ATTR{idVendor}=="2e6c", ATTR{idProduct}=="3211", GROUP="plugdev" SUBSYSTEM=="usb", ATTR{idVendor}=="2e6c", ATTR{idProduct}=="3211", GROUP="plugdev"
SUBSYSTEM=="hidraw", ATTRS{idVendor}=="2e6c", ATTRS{idProduct}=="3211", GROUP="plugdev"
# Scubapro G2 HUD # Scubapro G2 HUD
SUBSYSTEM=="usb", ATTR{idVendor}=="2e6c", ATTR{idProduct}=="4201", GROUP="plugdev" SUBSYSTEM=="usb", ATTR{idVendor}=="2e6c", ATTR{idProduct}=="4201", GROUP="plugdev"
SUBSYSTEM=="hidraw", ATTRS{idVendor}=="2e6c", ATTRS{idProduct}=="4201", GROUP="plugdev"
# Scubapro Aladin Square # Scubapro Aladin Square
SUBSYSTEM=="usb", ATTR{idVendor}=="c251", ATTR{idProduct}=="2006", GROUP="plugdev" SUBSYSTEM=="usb", ATTR{idVendor}=="c251", ATTR{idProduct}=="2006", GROUP="plugdev"
SUBSYSTEM=="hidraw", ATTRS{idVendor}=="c251", ATTRS{idProduct}=="2006", GROUP="plugdev"

View File

@ -32,7 +32,6 @@ MANPAGES = \
dc_parser_get_field.3 \ dc_parser_get_field.3 \
dc_parser_new.3 \ dc_parser_new.3 \
dc_parser_samples_foreach.3 \ dc_parser_samples_foreach.3 \
dc_parser_set_data.3 \
dc_bluetooth_open.3 \ dc_bluetooth_open.3 \
dc_bluetooth_iterator_new.3 \ dc_bluetooth_iterator_new.3 \
dc_bluetooth_device_get_address.3 \ dc_bluetooth_device_get_address.3 \

View File

@ -82,7 +82,7 @@ is
.Sh SEE ALSO .Sh SEE ALSO
.Xr dc_datetime_localtime 3 , .Xr dc_datetime_localtime 3 ,
.Xr dc_datetime_mktime 3 , .Xr dc_datetime_mktime 3 ,
.Xr dc_datetime_new 3 .Xr dc_datetime_now 3
.Sh AUTHORS .Sh AUTHORS
The The
.Lb libdivecomputer .Lb libdivecomputer

View File

@ -82,7 +82,7 @@ is
.Sh SEE ALSO .Sh SEE ALSO
.Xr dc_datetime_gmtime 3 , .Xr dc_datetime_gmtime 3 ,
.Xr dc_datetime_mktime 3 , .Xr dc_datetime_mktime 3 ,
.Xr dc_datetime_new 3 .Xr dc_datetime_now 3
.Sh AUTHORS .Sh AUTHORS
The The
.Lb libdivecomputer .Lb libdivecomputer

View File

@ -47,7 +47,7 @@ may not sanely be converted.
.Sh SEE ALSO .Sh SEE ALSO
.Xr dc_datetime_gmtime 3 , .Xr dc_datetime_gmtime 3 ,
.Xr dc_datetime_localtime 3 , .Xr dc_datetime_localtime 3 ,
.Xr dc_datetime_new 3 .Xr dc_datetime_now 3
.Sh AUTHORS .Sh AUTHORS
The The
.Lb libdivecomputer .Lb libdivecomputer

View File

@ -53,7 +53,7 @@ with
Each dive invokes Each dive invokes
.Fa callback .Fa callback
with the dive data, which should be parsed with with the dive data, which should be parsed with
.Xr dc_parser_set_data 3 , .Xr dc_parser_new 3 ,
and the binary fingerprint of the dive. and the binary fingerprint of the dive.
The fingerprint can be used to record the newest dive and stop The fingerprint can be used to record the newest dive and stop
processing (on subsequent invocations) when the same dive fingerprint is processing (on subsequent invocations) when the same dive fingerprint is
@ -72,7 +72,7 @@ If
returns zero, this will not be reflected in the return value (usually returns zero, this will not be reflected in the return value (usually
.Dv DC_STATUS_SUCCESS ) . .Dv DC_STATUS_SUCCESS ) .
.Sh SEE ALSO .Sh SEE ALSO
.Xr dc_parser_set_data 3 .Xr dc_parser_new 3
.Sh AUTHORS .Sh AUTHORS
The The
.Lb libdivecomputer .Lb libdivecomputer

View File

@ -37,7 +37,7 @@
Extract the date and time of a dive, Extract the date and time of a dive,
.Fa parser , .Fa parser ,
previously initialised with previously initialised with
.Xr dc_parser_set_data 3 . .Xr dc_parser_new 3 .
This returns the broken-down time-stamp of the dive in the local time of This returns the broken-down time-stamp of the dive in the local time of
the dive. the dive.
.Pp .Pp
@ -57,7 +57,7 @@ messages on further failure.
.Sh SEE ALSO .Sh SEE ALSO
.Xr dc_datetime_gmtime 3 , .Xr dc_datetime_gmtime 3 ,
.Xr dc_datetime_localtime 3 , .Xr dc_datetime_localtime 3 ,
.Xr dc_parser_set_data 3 .Xr dc_parser_new 3
.Sh AUTHORS .Sh AUTHORS
The The
.Lb libdivecomputer .Lb libdivecomputer

View File

@ -39,7 +39,7 @@
Extract a field from a dive, Extract a field from a dive,
.Fa parser , .Fa parser ,
previously initialised with previously initialised with
.Xr dc_parser_set_data 3 . .Xr dc_parser_new 3 .
The The
.Fa value .Fa value
field type depends upon the field type depends upon the
@ -187,7 +187,7 @@ if the field was retrieved,
if the field is not supported by the device, or other error messages on if the field is not supported by the device, or other error messages on
further failure. further failure.
.Sh SEE ALSO .Sh SEE ALSO
.Xr dc_parser_set_data 3 .Xr dc_parser_new 3
.Sh AUTHORS .Sh AUTHORS
The The
.Lb libdivecomputer .Lb libdivecomputer

View File

@ -39,8 +39,6 @@
.Fa "dc_parser_t **parser" .Fa "dc_parser_t **parser"
.Fa "dc_context_t *context" .Fa "dc_context_t *context"
.Fa "dc_descriptor_t *descriptor" .Fa "dc_descriptor_t *descriptor"
.Fa "unsigned int devtime"
.Fa "dc_ticks_t systime"
.Fc .Fc
.Sh DESCRIPTION .Sh DESCRIPTION
Creates a parser for a single dive extracted from the dive computer with Creates a parser for a single dive extracted from the dive computer with
@ -55,10 +53,6 @@ parameter; and
.Nm dc_parser_new2 , .Nm dc_parser_new2 ,
which is given device values (model, etc.) directly. which is given device values (model, etc.) directly.
.Pp .Pp
After filling in the
.Fa parser
parameter, one usually sets parser data with
.Xr dc_parser_set_data 3 .
The pointer must later be freed with The pointer must later be freed with
.Xr dc_parser_destroy 3 . .Xr dc_parser_destroy 3 .
.Sh RETURN VALUES .Sh RETURN VALUES

View File

@ -31,7 +31,7 @@
.Ft "typedef void" .Ft "typedef void"
.Fo "(*dc_sample_callback_t)" .Fo "(*dc_sample_callback_t)"
.Fa "dc_sample_type_t type" .Fa "dc_sample_type_t type"
.Fa "dc_sample_value_t value" .Fa "const dc_sample_value_t *value"
.Fa "void *userdata" .Fa "void *userdata"
.Fc .Fc
.Ft dc_status_t .Ft dc_status_t
@ -42,7 +42,7 @@
.Fc .Fc
.Sh DESCRIPTION .Sh DESCRIPTION
Extract the samples taken during a dive as previously initialised with Extract the samples taken during a dive as previously initialised with
.Xr dc_parser_set_data 3 . .Xr dc_parser_new 3 .
Each sample is passed to Each sample is passed to
.Fa callback .Fa callback
with the with the
@ -63,7 +63,7 @@ closed.
The following sample types may be raised: The following sample types may be raised:
.Bl -tag -width Ds .Bl -tag -width Ds
.It Dv DC_SAMPLE_TIME .It Dv DC_SAMPLE_TIME
The time of the sample taken in seconds after the dive began. The time of the sample taken in milliseconds after the dive began.
Set in the Set in the
.Fa time .Fa time
field. field.
@ -184,7 +184,7 @@ Returns
.Dv DC_STATUS_OK .Dv DC_STATUS_OK
on success and another code on failure. on success and another code on failure.
.Sh SEE ALSO .Sh SEE ALSO
.Xr dc_parser_set_data 3 .Xr dc_parser_new 3
.Sh AUTHORS .Sh AUTHORS
The The
.Lb libdivecomputer .Lb libdivecomputer

View File

@ -1,65 +0,0 @@
.\"
.\" libdivecomputer
.\"
.\" Copyright (C) 2017 Kristaps Dzonsons <kristaps@bsd.lv>
.\"
.\" This library is free software; you can redistribute it and/or
.\" modify it under the terms of the GNU Lesser General Public
.\" License as published by the Free Software Foundation; either
.\" version 2.1 of the License, or (at your option) any later version.
.\"
.\" This library is distributed in the hope that it will be useful,
.\" but WITHOUT ANY WARRANTY; without even the implied warranty of
.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
.\" Lesser General Public License for more details.
.\"
.\" You should have received a copy of the GNU Lesser General Public
.\" License along with this library; if not, write to the Free Software
.\" Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
.\" MA 02110-1301 USA
.\"
.Dd January 5, 2017
.Dt DC_PARSER_SET_DATA 3
.Os
.Sh NAME
.Nm dc_parser_set_data
.Nd assigns parse data to a dive parser
.Sh LIBRARY
.Lb libdivecomputer
.Sh SYNOPSIS
.In libdivecomputer/parser.h
.Ft dc_status_t
.Fo dc_parser_set_data
.Fa "dc_parser_t *parser"
.Fa "const unsigned char *data"
.Fa "unsigned int size"
.Fc
.Sh DESCRIPTION
Assigns the binary sequence
.Fa data
of length
.Fa size
bytes to
.Fa parser ,
which was created with
.Xr dc_parser_new 3 .
How the data is parsed depends upon the values provided to
.Xr dc_parser_new 3 .
The data usually comes from the callback assigned to
.Xr dc_device_foreach 3 .
.Sh RETURN VALUES
Returns
.Dv DC_STATUS_OK
on success and another code on failure.
.Sh SEE ALSO
.Xr dc_device_foreach 3 ,
.Xr dc_parser_new 3
.Sh AUTHORS
The
.Lb libdivecomputer
library was written by
.An Jef Driesen ,
.Mt jef@libdivecomputer.org .
The manpages were written by
.An Kristaps Dzonsons ,
.Mt kristaps@bsd.lv .

View File

@ -82,9 +82,7 @@ Iterate over all dives with
.Xr dc_device_foreach 3 . .Xr dc_device_foreach 3 .
.It .It
For each iterated dive, create a new parser with For each iterated dive, create a new parser with
.Xr dc_parser_new 3 .Xr dc_parser_new 3 .
and set the parsed data with
.Xr dc_parser_set_data 3 .
.It .It
Get attributes of the parsed dive with Get attributes of the parsed dive with
.Xr dc_parser_get_field 3 . .Xr dc_parser_get_field 3 .

View File

@ -72,6 +72,7 @@ static const backend_table_t g_backends[] = {
{"vtpro", DC_FAMILY_OCEANIC_VTPRO, 0x4245}, {"vtpro", DC_FAMILY_OCEANIC_VTPRO, 0x4245},
{"veo250", DC_FAMILY_OCEANIC_VEO250, 0x424C}, {"veo250", DC_FAMILY_OCEANIC_VEO250, 0x424C},
{"atom2", DC_FAMILY_OCEANIC_ATOM2, 0x4342}, {"atom2", DC_FAMILY_OCEANIC_ATOM2, 0x4342},
{"i330r", DC_FAMILY_PELAGIC_I330R, 0x4744},
{"nemo", DC_FAMILY_MARES_NEMO, 0}, {"nemo", DC_FAMILY_MARES_NEMO, 0},
{"puck", DC_FAMILY_MARES_PUCK, 7}, {"puck", DC_FAMILY_MARES_PUCK, 7},
{"darwin", DC_FAMILY_MARES_DARWIN, 0}, {"darwin", DC_FAMILY_MARES_DARWIN, 0},
@ -94,6 +95,11 @@ static const backend_table_t g_backends[] = {
{"extreme", DC_FAMILY_MCLEAN_EXTREME, 0}, {"extreme", DC_FAMILY_MCLEAN_EXTREME, 0},
{"lynx", DC_FAMILY_LIQUIVISION_LYNX, 0}, {"lynx", DC_FAMILY_LIQUIVISION_LYNX, 0},
{"sp2", DC_FAMILY_SPORASUB_SP2, 0}, {"sp2", DC_FAMILY_SPORASUB_SP2, 0},
{"excursion", DC_FAMILY_DEEPSIX_EXCURSION, 0},
{"screen", DC_FAMILY_SEAC_SCREEN, 0},
{"cosmiq", DC_FAMILY_DEEPBLU_COSMIQ, 0},
{"s1", DC_FAMILY_OCEANS_S1, 0},
{"freedom", DC_FAMILY_DIVESOFT_FREEDOM, 19},
}; };
static const transport_table_t g_transports[] = { static const transport_table_t g_transports[] = {

View File

@ -24,10 +24,12 @@
#endif #endif
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h>
#include <string.h> #include <string.h>
#include <stdio.h> #include <stdio.h>
#include <signal.h> #include <signal.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_GETOPT_H #ifdef HAVE_GETOPT_H
#include <getopt.h> #include <getopt.h>
#endif #endif
@ -48,7 +50,7 @@
#define RESET 1 #define RESET 1
#endif #endif
#if defined(__GLIBC__) || defined(__MINGW32__) || defined(BSD) #if defined(__GLIBC__) || defined(__MINGW32__) || defined(BSD) || defined(__ANDROID__)
#define NOPERMUTATION "+" #define NOPERMUTATION "+"
#else #else
#define NOPERMUTATION "" #define NOPERMUTATION ""

View File

@ -24,9 +24,11 @@
#endif #endif
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_GETOPT_H #ifdef HAVE_GETOPT_H
#include <getopt.h> #include <getopt.h>
#endif #endif
@ -78,20 +80,12 @@ dive_cb (const unsigned char *data, unsigned int size, const unsigned char *fing
// Create the parser. // Create the parser.
message ("Creating the parser.\n"); message ("Creating the parser.\n");
rc = dc_parser_new (&parser, divedata->device); rc = dc_parser_new (&parser, divedata->device, data, size);
if (rc != DC_STATUS_SUCCESS) { if (rc != DC_STATUS_SUCCESS) {
ERROR ("Error creating the parser."); ERROR ("Error creating the parser.");
goto cleanup; goto cleanup;
} }
// Register the data.
message ("Registering the data.\n");
rc = dc_parser_set_data (parser, data, size);
if (rc != DC_STATUS_SUCCESS) {
ERROR ("Error registering the data.");
goto cleanup;
}
// Parse the dive data. // Parse the dive data.
message ("Parsing the dive data.\n"); message ("Parsing the dive data.\n");
rc = dctool_output_write (divedata->output, parser, data, size, fingerprint, fsize); rc = dctool_output_write (divedata->output, parser, data, size, fingerprint, fsize);

View File

@ -24,9 +24,11 @@
#endif #endif
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_GETOPT_H #ifdef HAVE_GETOPT_H
#include <getopt.h> #include <getopt.h>
#endif #endif

View File

@ -24,8 +24,10 @@
#endif #endif
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h>
#include <stdio.h> #include <stdio.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_GETOPT_H #ifdef HAVE_GETOPT_H
#include <getopt.h> #include <getopt.h>
#endif #endif

View File

@ -24,8 +24,10 @@
#endif #endif
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h>
#include <stdio.h> #include <stdio.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_GETOPT_H #ifdef HAVE_GETOPT_H
#include <getopt.h> #include <getopt.h>
#endif #endif

View File

@ -24,8 +24,10 @@
#endif #endif
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h>
#include <stdio.h> #include <stdio.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_GETOPT_H #ifdef HAVE_GETOPT_H
#include <getopt.h> #include <getopt.h>
#endif #endif

View File

@ -24,9 +24,11 @@
#endif #endif
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_GETOPT_H #ifdef HAVE_GETOPT_H
#include <getopt.h> #include <getopt.h>
#endif #endif
@ -52,17 +54,17 @@ parse (dc_buffer_t *buffer, dc_context_t *context, dc_descriptor_t *descriptor,
// Create the parser. // Create the parser.
message ("Creating the parser.\n"); message ("Creating the parser.\n");
rc = dc_parser_new2 (&parser, context, descriptor, devtime, systime); rc = dc_parser_new2 (&parser, context, descriptor, data, size);
if (rc != DC_STATUS_SUCCESS) { if (rc != DC_STATUS_SUCCESS) {
ERROR ("Error creating the parser."); ERROR ("Error creating the parser.");
goto cleanup; goto cleanup;
} }
// Register the data. // Set the clock.
message ("Registering the data.\n"); message ("Setting the clock.\n");
rc = dc_parser_set_data (parser, data, size); rc = dc_parser_set_clock (parser, devtime, systime);
if (rc != DC_STATUS_SUCCESS) { if (rc != DC_STATUS_SUCCESS && rc != DC_STATUS_UNSUPPORTED) {
ERROR ("Error registering the data."); ERROR ("Error setting the clock.");
goto cleanup; goto cleanup;
} }

View File

@ -24,8 +24,10 @@
#endif #endif
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h>
#include <stdio.h> #include <stdio.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_GETOPT_H #ifdef HAVE_GETOPT_H
#include <getopt.h> #include <getopt.h>
#endif #endif

View File

@ -24,8 +24,10 @@
#endif #endif
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h>
#include <stdio.h> #include <stdio.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_GETOPT_H #ifdef HAVE_GETOPT_H
#include <getopt.h> #include <getopt.h>
#endif #endif
@ -158,7 +160,7 @@ dctool_scan_run (int argc, char *argv[], dc_context_t *context, dc_descriptor_t
// Show help message. // Show help message.
if (help) { if (help) {
dctool_command_showhelp (&dctool_list); dctool_command_showhelp (&dctool_scan);
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }

View File

@ -24,8 +24,10 @@
#endif #endif
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h>
#include <stdio.h> #include <stdio.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_GETOPT_H #ifdef HAVE_GETOPT_H
#include <getopt.h> #include <getopt.h>
#endif #endif

View File

@ -24,8 +24,10 @@
#endif #endif
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h>
#include <stdio.h> #include <stdio.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_GETOPT_H #ifdef HAVE_GETOPT_H
#include <getopt.h> #include <getopt.h>
#endif #endif

View File

@ -24,8 +24,10 @@
#endif #endif
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h>
#include <stdio.h> #include <stdio.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_GETOPT_H #ifdef HAVE_GETOPT_H
#include <getopt.h> #include <getopt.h>
#endif #endif

View File

@ -90,7 +90,7 @@ convert_volume (double value, dctool_units_t units)
} }
static void static void
sample_cb (dc_sample_type_t type, dc_sample_value_t value, void *userdata) sample_cb (dc_sample_type_t type, const dc_sample_value_t *value, void *userdata)
{ {
static const char *events[] = { static const char *events[] = {
"none", "deco", "rbt", "ascent", "ceiling", "workload", "transmitter", "none", "deco", "rbt", "ascent", "ceiling", "workload", "transmitter",
@ -104,64 +104,80 @@ sample_cb (dc_sample_type_t type, dc_sample_value_t value, void *userdata)
sample_data_t *sampledata = (sample_data_t *) userdata; sample_data_t *sampledata = (sample_data_t *) userdata;
unsigned int seconds = 0, milliseconds = 0;
switch (type) { switch (type) {
case DC_SAMPLE_TIME: case DC_SAMPLE_TIME:
seconds = value->time / 1000;
milliseconds = value->time % 1000;
if (sampledata->nsamples++) if (sampledata->nsamples++)
fprintf (sampledata->ostream, "</sample>\n"); fprintf (sampledata->ostream, "</sample>\n");
fprintf (sampledata->ostream, "<sample>\n"); fprintf (sampledata->ostream, "<sample>\n");
fprintf (sampledata->ostream, " <time>%02u:%02u</time>\n", value.time / 60, value.time % 60); if (milliseconds) {
fprintf (sampledata->ostream, " <time>%02u:%02u.%03u</time>\n", seconds / 60, seconds % 60, milliseconds);
} else {
fprintf (sampledata->ostream, " <time>%02u:%02u</time>\n", seconds / 60, seconds % 60);
}
break; break;
case DC_SAMPLE_DEPTH: case DC_SAMPLE_DEPTH:
fprintf (sampledata->ostream, " <depth>%.2f</depth>\n", fprintf (sampledata->ostream, " <depth>%.2f</depth>\n",
convert_depth(value.depth, sampledata->units)); convert_depth(value->depth, sampledata->units));
break; break;
case DC_SAMPLE_PRESSURE: case DC_SAMPLE_PRESSURE:
fprintf (sampledata->ostream, " <pressure tank=\"%u\">%.2f</pressure>\n", fprintf (sampledata->ostream, " <pressure tank=\"%u\">%.2f</pressure>\n",
value.pressure.tank, value->pressure.tank,
convert_pressure(value.pressure.value, sampledata->units)); convert_pressure(value->pressure.value, sampledata->units));
break; break;
case DC_SAMPLE_TEMPERATURE: case DC_SAMPLE_TEMPERATURE:
fprintf (sampledata->ostream, " <temperature>%.2f</temperature>\n", fprintf (sampledata->ostream, " <temperature>%.2f</temperature>\n",
convert_temperature(value.temperature, sampledata->units)); convert_temperature(value->temperature, sampledata->units));
break; break;
case DC_SAMPLE_EVENT: case DC_SAMPLE_EVENT:
if (value.event.type != SAMPLE_EVENT_GASCHANGE && value.event.type != SAMPLE_EVENT_GASCHANGE2) { if (value->event.type != SAMPLE_EVENT_GASCHANGE && value->event.type != SAMPLE_EVENT_GASCHANGE2) {
fprintf (sampledata->ostream, " <event type=\"%u\" time=\"%u\" flags=\"%u\" value=\"%u\">%s</event>\n", fprintf (sampledata->ostream, " <event type=\"%u\" time=\"%u\" flags=\"%u\" value=\"%u\">%s</event>\n",
value.event.type, value.event.time, value.event.flags, value.event.value, events[value.event.type]); value->event.type, value->event.time, value->event.flags, value->event.value, events[value->event.type]);
} }
break; break;
case DC_SAMPLE_RBT: case DC_SAMPLE_RBT:
fprintf (sampledata->ostream, " <rbt>%u</rbt>\n", value.rbt); fprintf (sampledata->ostream, " <rbt>%u</rbt>\n", value->rbt);
break; break;
case DC_SAMPLE_HEARTBEAT: case DC_SAMPLE_HEARTBEAT:
fprintf (sampledata->ostream, " <heartbeat>%u</heartbeat>\n", value.heartbeat); fprintf (sampledata->ostream, " <heartbeat>%u</heartbeat>\n", value->heartbeat);
break; break;
case DC_SAMPLE_BEARING: case DC_SAMPLE_BEARING:
fprintf (sampledata->ostream, " <bearing>%u</bearing>\n", value.bearing); fprintf (sampledata->ostream, " <bearing>%u</bearing>\n", value->bearing);
break; break;
case DC_SAMPLE_VENDOR: case DC_SAMPLE_VENDOR:
fprintf (sampledata->ostream, " <vendor type=\"%u\" size=\"%u\">", value.vendor.type, value.vendor.size); fprintf (sampledata->ostream, " <vendor type=\"%u\" size=\"%u\">", value->vendor.type, value->vendor.size);
for (unsigned int i = 0; i < value.vendor.size; ++i) for (unsigned int i = 0; i < value->vendor.size; ++i)
fprintf (sampledata->ostream, "%02X", ((const unsigned char *) value.vendor.data)[i]); fprintf (sampledata->ostream, "%02X", ((const unsigned char *) value->vendor.data)[i]);
fprintf (sampledata->ostream, "</vendor>\n"); fprintf (sampledata->ostream, "</vendor>\n");
break; break;
case DC_SAMPLE_SETPOINT: case DC_SAMPLE_SETPOINT:
fprintf (sampledata->ostream, " <setpoint>%.2f</setpoint>\n", value.setpoint); fprintf (sampledata->ostream, " <setpoint>%.2f</setpoint>\n", value->setpoint);
break; break;
case DC_SAMPLE_PPO2: case DC_SAMPLE_PPO2:
fprintf (sampledata->ostream, " <ppo2>%.2f</ppo2>\n", value.ppo2); if (value->ppo2.sensor != DC_SENSOR_NONE) {
fprintf (sampledata->ostream, " <ppo2 sensor=\"%u\">%.2f</ppo2>\n", value->ppo2.sensor, value->ppo2.value);
} else {
fprintf (sampledata->ostream, " <ppo2>%.2f</ppo2>\n", value->ppo2.value);
}
break; break;
case DC_SAMPLE_CNS: case DC_SAMPLE_CNS:
fprintf (sampledata->ostream, " <cns>%.1f</cns>\n", value.cns * 100.0); fprintf (sampledata->ostream, " <cns>%.1f</cns>\n", value->cns * 100.0);
break; break;
case DC_SAMPLE_DECO: case DC_SAMPLE_DECO:
fprintf (sampledata->ostream, " <deco time=\"%u\" depth=\"%.2f\">%s</deco>\n", fprintf (sampledata->ostream, " <deco time=\"%u\" depth=\"%.2f\">%s</deco>\n",
value.deco.time, value->deco.time,
convert_depth(value.deco.depth, sampledata->units), convert_depth(value->deco.depth, sampledata->units),
decostop[value.deco.type]); decostop[value->deco.type]);
if (value->deco.tts) {
fprintf (sampledata->ostream, " <tts>%u</tts>\n",
value->deco.tts);
}
break; break;
case DC_SAMPLE_GASMIX: case DC_SAMPLE_GASMIX:
fprintf (sampledata->ostream, " <gasmix>%u</gasmix>\n", value.gasmix); fprintf (sampledata->ostream, " <gasmix>%u</gasmix>\n", value->gasmix);
break; break;
default: default:
break; break;
@ -238,7 +254,7 @@ dctool_xml_output_write (dctool_output_t *abstract, dc_parser_t *parser, const u
fprintf (output->ostream, "<datetime>%04i-%02i-%02i %02i:%02i:%02i %+03i:%02i</datetime>\n", fprintf (output->ostream, "<datetime>%04i-%02i-%02i %02i:%02i:%02i %+03i:%02i</datetime>\n",
dt.year, dt.month, dt.day, dt.year, dt.month, dt.day,
dt.hour, dt.minute, dt.second, dt.hour, dt.minute, dt.second,
dt.timezone / 3600, (dt.timezone % 3600) / 60); dt.timezone / 3600, (abs(dt.timezone) % 3600) / 60);
} }
// Parse the divetime. // Parse the divetime.
@ -322,11 +338,19 @@ dctool_xml_output_write (dctool_output_t *abstract, dc_parser_t *parser, const u
"<gasmix>\n" "<gasmix>\n"
" <he>%.1f</he>\n" " <he>%.1f</he>\n"
" <o2>%.1f</o2>\n" " <o2>%.1f</o2>\n"
" <n2>%.1f</n2>\n" " <n2>%.1f</n2>\n",
"</gasmix>\n",
gasmix.helium * 100.0, gasmix.helium * 100.0,
gasmix.oxygen * 100.0, gasmix.oxygen * 100.0,
gasmix.nitrogen * 100.0); gasmix.nitrogen * 100.0);
if (gasmix.usage) {
const char *usage[] = {"none", "oxygen", "diluent", "sidemount"};
fprintf (output->ostream,
" <usage>%s</usage>\n",
usage[gasmix.usage]);
}
fprintf (output->ostream,
"</gasmix>\n");
} }
// Parse the tanks. // Parse the tanks.
@ -354,6 +378,12 @@ dctool_xml_output_write (dctool_output_t *abstract, dc_parser_t *parser, const u
" <gasmix>%u</gasmix>\n", " <gasmix>%u</gasmix>\n",
tank.gasmix); tank.gasmix);
} }
if (tank.usage) {
const char *usage[] = {"none", "oxygen", "diluent", "sidemount"};
fprintf (output->ostream,
" <usage>%s</usage>\n",
usage[tank.usage]);
}
if (tank.type != DC_TANKVOLUME_NONE) { if (tank.type != DC_TANKVOLUME_NONE) {
fprintf (output->ostream, fprintf (output->ostream,
" <type>%s</type>\n" " <type>%s</type>\n"
@ -386,6 +416,30 @@ dctool_xml_output_write (dctool_output_t *abstract, dc_parser_t *parser, const u
names[divemode]); names[divemode]);
} }
// Parse the deco model.
message ("Parsing the deco model.\n");
dc_decomodel_t decomodel = {DC_DECOMODEL_NONE};
status = dc_parser_get_field (parser, DC_FIELD_DECOMODEL, 0, &decomodel);
if (status != DC_STATUS_SUCCESS && status != DC_STATUS_UNSUPPORTED) {
ERROR ("Error parsing the deco model.");
goto cleanup;
}
if (status != DC_STATUS_UNSUPPORTED) {
const char *names[] = {"none", "buhlmann", "vpm", "rgbm", "dciem"};
fprintf (output->ostream, "<decomodel>%s</decomodel>\n",
names[decomodel.type]);
if (decomodel.type == DC_DECOMODEL_BUHLMANN &&
(decomodel.params.gf.low != 0 || decomodel.params.gf.high != 0)) {
fprintf (output->ostream, "<gf>%u/%u</gf>\n",
decomodel.params.gf.low, decomodel.params.gf.high);
}
if (decomodel.conservatism) {
fprintf (output->ostream, "<conservatism>%d</conservatism>\n",
decomodel.conservatism);
}
}
// Parse the salinity. // Parse the salinity.
message ("Parsing the salinity.\n"); message ("Parsing the salinity.\n");
dc_salinity_t salinity = {DC_WATER_FRESH, 0.0}; dc_salinity_t salinity = {DC_WATER_FRESH, 0.0};
@ -396,8 +450,14 @@ dctool_xml_output_write (dctool_output_t *abstract, dc_parser_t *parser, const u
} }
if (status != DC_STATUS_UNSUPPORTED) { if (status != DC_STATUS_UNSUPPORTED) {
fprintf (output->ostream, "<salinity type=\"%u\">%.1f</salinity>\n", const char *names[] = {"fresh", "salt"};
salinity.type, salinity.density); if (salinity.density) {
fprintf (output->ostream, "<salinity density=\"%.1f\">%s</salinity>\n",
salinity.density, names[salinity.type]);
} else {
fprintf (output->ostream, "<salinity>%s</salinity>\n",
names[salinity.type]);
}
} }
// Parse the atmospheric pressure. // Parse the atmospheric pressure.

View File

@ -26,6 +26,12 @@
extern "C" { extern "C" {
#endif /* __cplusplus */ #endif /* __cplusplus */
#ifdef _MSC_VER
#define snprintf _snprintf
#define strcasecmp _stricmp
#define strncasecmp _strnicmp
#endif
#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) #if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
#define FUNCTION __func__ #define FUNCTION __func__
#else #else

View File

@ -36,9 +36,6 @@ atomics_cobalt_device_version (dc_device_t *device, unsigned char data[], unsign
dc_status_t dc_status_t
atomics_cobalt_device_set_simulation (dc_device_t *device, unsigned int simulation); atomics_cobalt_device_set_simulation (dc_device_t *device, unsigned int simulation);
dc_status_t
atomics_cobalt_parser_set_calibration (dc_parser_t *parser, double atmospheric, double hydrostatic);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif /* __cplusplus */ #endif /* __cplusplus */

View File

@ -33,6 +33,21 @@ extern "C" {
*/ */
#define DC_IOCTL_BLE_GET_NAME DC_IOCTL_IOR('b', 0, DC_IOCTL_SIZE_VARIABLE) #define DC_IOCTL_BLE_GET_NAME DC_IOCTL_IOR('b', 0, DC_IOCTL_SIZE_VARIABLE)
/**
* Get the bluetooth authentication PIN code.
*
* The data format is a NULL terminated string.
*/
#define DC_IOCTL_BLE_GET_PINCODE DC_IOCTL_IOR('b', 1, DC_IOCTL_SIZE_VARIABLE)
/**
* Get/set the bluetooth authentication access code.
*
* The data format is a variable sized byte array.
*/
#define DC_IOCTL_BLE_GET_ACCESSCODE DC_IOCTL_IOR('b', 2, DC_IOCTL_SIZE_VARIABLE)
#define DC_IOCTL_BLE_SET_ACCESSCODE DC_IOCTL_IOW('b', 2, DC_IOCTL_SIZE_VARIABLE)
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif /* __cplusplus */ #endif /* __cplusplus */

View File

@ -74,6 +74,7 @@ typedef enum dc_family_t {
DC_FAMILY_OCEANIC_VTPRO = (4 << 16), DC_FAMILY_OCEANIC_VTPRO = (4 << 16),
DC_FAMILY_OCEANIC_VEO250, DC_FAMILY_OCEANIC_VEO250,
DC_FAMILY_OCEANIC_ATOM2, DC_FAMILY_OCEANIC_ATOM2,
DC_FAMILY_PELAGIC_I330R,
/* Mares */ /* Mares */
DC_FAMILY_MARES_NEMO = (5 << 16), DC_FAMILY_MARES_NEMO = (5 << 16),
DC_FAMILY_MARES_PUCK, DC_FAMILY_MARES_PUCK,
@ -110,6 +111,16 @@ typedef enum dc_family_t {
DC_FAMILY_LIQUIVISION_LYNX = (17 << 16), DC_FAMILY_LIQUIVISION_LYNX = (17 << 16),
/* Sporasub */ /* Sporasub */
DC_FAMILY_SPORASUB_SP2 = (18 << 16), DC_FAMILY_SPORASUB_SP2 = (18 << 16),
/* Deep Six */
DC_FAMILY_DEEPSIX_EXCURSION = (19 << 16),
/* Seac Screen */
DC_FAMILY_SEAC_SCREEN = (20 << 16),
/* Deepblu Cosmiq */
DC_FAMILY_DEEPBLU_COSMIQ = (21 << 16),
/* Oceans S1 */
DC_FAMILY_OCEANS_S1 = (22 << 16),
/* Divesoft Freedom */
DC_FAMILY_DIVESOFT_FREEDOM = (23 << 16),
} dc_family_t; } dc_family_t;
#ifdef __cplusplus #ifdef __cplusplus

View File

@ -29,29 +29,96 @@
extern "C" { extern "C" {
#endif /* __cplusplus */ #endif /* __cplusplus */
/**
* Opaque object representing a supported dive computer.
*/
typedef struct dc_descriptor_t dc_descriptor_t; typedef struct dc_descriptor_t dc_descriptor_t;
/**
* Create an iterator to enumerate the supported dive computers.
*
* @param[out] iterator A location to store the iterator.
* @returns #DC_STATUS_SUCCESS on success, or another #dc_status_t code
* on failure.
*/
dc_status_t dc_status_t
dc_descriptor_iterator (dc_iterator_t **iterator); dc_descriptor_iterator (dc_iterator_t **iterator);
/**
* Free the device descriptor.
*
* @param[in] descriptor A valid device descriptor.
*/
void void
dc_descriptor_free (dc_descriptor_t *descriptor); dc_descriptor_free (dc_descriptor_t *descriptor);
/**
* Get the vendor name of the dive computer.
*
* @param[in] descriptor A valid device descriptor.
* @returns The vendor name of the dive computer on success, or NULL on failure.
*/
const char * const char *
dc_descriptor_get_vendor (dc_descriptor_t *descriptor); dc_descriptor_get_vendor (dc_descriptor_t *descriptor);
/**
* Get the product name of the dive computer.
*
* @param[in] descriptor A valid device descriptor.
* @returns The product name of the dive computer on success, or NULL on
* failure.
*/
const char * const char *
dc_descriptor_get_product (dc_descriptor_t *descriptor); dc_descriptor_get_product (dc_descriptor_t *descriptor);
/**
* Get the family type of the dive computer.
*
* @param[in] descriptor A valid device descriptor.
* @returns The family type of the dive computer on success, or DC_FAMILY_NULL
* on failure.
*/
dc_family_t dc_family_t
dc_descriptor_get_type (dc_descriptor_t *descriptor); dc_descriptor_get_type (dc_descriptor_t *descriptor);
/**
* Get the model number of the dive computer.
*
* @param[in] descriptor A valid device descriptor.
* @returns The model number of the dive computer on success, or zero on
* failure.
*/
unsigned int unsigned int
dc_descriptor_get_model (dc_descriptor_t *descriptor); dc_descriptor_get_model (dc_descriptor_t *descriptor);
/**
* Get all transports supported by the dive computer.
*
* @param[in] descriptor A valid device descriptor.
* @returns A bitmask with all the transports supported by the dive computer on
* success, or DC_TRANSPORT_NONE on failure.
*/
unsigned int unsigned int
dc_descriptor_get_transports (dc_descriptor_t *descriptor); dc_descriptor_get_transports (dc_descriptor_t *descriptor);
/**
* Check if a low-level I/O device matches a supported dive computer.
*
* @param[in] descriptor A valid device descriptor.
* @param[in] transport The transport type of the I/O device.
* @param[in] userdata A pointer to a transport specific data structure:
* - DC_TRANSPORT_SERIAL: Name of the device node (string)
* - DC_TRANSPORT_USB: USB VID/PID (#dc_usb_desc_t)
* - DC_TRANSPORT_USBHID: USB VID/PID (#dc_usbhid_desc_t)
* - DC_TRANSPORT_IRDA: IrDA device name (string)
* - DC_TRANSPORT_BLUETOOTH: Bluetooth device name (string)
* - DC_TRANSPORT_BLE: Bluetooth device name (string)
* @returns Non-zero if the device matches a supported dive computer, or zero if
* there is no match.
*/
int
dc_descriptor_filter (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif /* __cplusplus */ #endif /* __cplusplus */

View File

@ -62,7 +62,8 @@ typedef enum dc_field_type_t {
DC_FIELD_TEMPERATURE_MAXIMUM, DC_FIELD_TEMPERATURE_MAXIMUM,
DC_FIELD_TANK_COUNT, DC_FIELD_TANK_COUNT,
DC_FIELD_TANK, DC_FIELD_TANK,
DC_FIELD_DIVEMODE DC_FIELD_DIVEMODE,
DC_FIELD_DECOMODEL,
} dc_field_type_t; } dc_field_type_t;
typedef enum parser_sample_event_t { typedef enum parser_sample_event_t {
@ -140,12 +141,21 @@ typedef struct dc_salinity_t {
double density; double density;
} dc_salinity_t; } dc_salinity_t;
typedef enum dc_usage_t {
DC_USAGE_NONE,
DC_USAGE_OXYGEN,
DC_USAGE_DILUENT,
DC_USAGE_SIDEMOUNT,
} dc_usage_t;
typedef struct dc_gasmix_t { typedef struct dc_gasmix_t {
double helium; double helium;
double oxygen; double oxygen;
double nitrogen; double nitrogen;
dc_usage_t usage;
} dc_gasmix_t; } dc_gasmix_t;
#define DC_SENSOR_NONE 0xFFFFFFFF
#define DC_GASMIX_UNKNOWN 0xFFFFFFFF #define DC_GASMIX_UNKNOWN 0xFFFFFFFF
typedef enum dc_tankvolume_t { typedef enum dc_tankvolume_t {
@ -184,10 +194,48 @@ typedef struct dc_tank_t {
double workpressure; /* Work pressure (bar) */ double workpressure; /* Work pressure (bar) */
double beginpressure; /* Begin pressure (bar) */ double beginpressure; /* Begin pressure (bar) */
double endpressure; /* End pressure (bar) */ double endpressure; /* End pressure (bar) */
dc_usage_t usage;
} dc_tank_t; } dc_tank_t;
typedef enum dc_decomodel_type_t {
DC_DECOMODEL_NONE,
DC_DECOMODEL_BUHLMANN,
DC_DECOMODEL_VPM,
DC_DECOMODEL_RGBM,
DC_DECOMODEL_DCIEM,
} dc_decomodel_type_t;
/*
* Decompression model
*
* The type field contains the decompression algorithm.
*
* The (optional) conservatism field contains the personal adjustment
* setting of the algorithm. The exact interpretation depends on the
* dive computer, but the default value (zero) will typically correspond
* to the neutral setting, while a positive value is more conservative
* and a negative value more aggressive.
*
* The (optional) params field contains the parameters of the algorithm:
*
* DC_DECOMODEL_BUHLMANN: The Gradient Factor (GF) parameters low and
* high. For a pure Buhlmann algorithm (e.g. without GF enabled), both
* values are 100. If GF are enabled, but the actual parameter values
* are not available from the dive computer, both values are zero.
*/
typedef struct dc_decomodel_t {
dc_decomodel_type_t type;
int conservatism;
union {
struct {
unsigned int high;
unsigned int low;
} gf;
} params;
} dc_decomodel_t;
typedef union dc_sample_value_t { typedef union dc_sample_value_t {
unsigned int time; unsigned int time; /* Milliseconds */
double depth; double depth;
struct { struct {
unsigned int tank; unsigned int tank;
@ -209,31 +257,41 @@ typedef union dc_sample_value_t {
const void *data; const void *data;
} vendor; } vendor;
double setpoint; double setpoint;
double ppo2; struct {
unsigned int sensor;
double value;
} ppo2;
double cns; double cns;
struct { struct {
unsigned int type; unsigned int type;
unsigned int time; unsigned int time;
double depth; double depth;
unsigned int tts;
} deco; } deco;
unsigned int gasmix; /* Gas mix index */ unsigned int gasmix; /* Gas mix index */
} dc_sample_value_t; } dc_sample_value_t;
typedef struct dc_parser_t dc_parser_t; typedef struct dc_parser_t dc_parser_t;
typedef void (*dc_sample_callback_t) (dc_sample_type_t type, dc_sample_value_t value, void *userdata); typedef void (*dc_sample_callback_t) (dc_sample_type_t type, const dc_sample_value_t *value, void *userdata);
dc_status_t dc_status_t
dc_parser_new (dc_parser_t **parser, dc_device_t *device); dc_parser_new (dc_parser_t **parser, dc_device_t *device, const unsigned char data[], size_t size);
dc_status_t dc_status_t
dc_parser_new2 (dc_parser_t **parser, dc_context_t *context, dc_descriptor_t *descriptor, unsigned int devtime, dc_ticks_t systime); dc_parser_new2 (dc_parser_t **parser, dc_context_t *context, dc_descriptor_t *descriptor, const unsigned char data[], size_t size);
dc_family_t dc_family_t
dc_parser_get_type (dc_parser_t *parser); dc_parser_get_type (dc_parser_t *parser);
dc_status_t dc_status_t
dc_parser_set_data (dc_parser_t *parser, const unsigned char *data, unsigned int size); dc_parser_set_clock (dc_parser_t *parser, unsigned int devtime, dc_ticks_t systime);
dc_status_t
dc_parser_set_atmospheric (dc_parser_t *parser, double atmospheric);
dc_status_t
dc_parser_set_density (dc_parser_t *parser, double density);
dc_status_t dc_status_t
dc_parser_get_datetime (dc_parser_t *parser, dc_datetime_t *datetime); dc_parser_get_datetime (dc_parser_t *parser, dc_datetime_t *datetime);

View File

@ -35,9 +35,6 @@ extern "C" {
dc_status_t dc_status_t
reefnet_sensus_device_get_handshake (dc_device_t *device, unsigned char data[], unsigned int size); reefnet_sensus_device_get_handshake (dc_device_t *device, unsigned char data[], unsigned int size);
dc_status_t
reefnet_sensus_parser_set_calibration (dc_parser_t *parser, double atmospheric, double hydrostatic);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif /* __cplusplus */ #endif /* __cplusplus */

View File

@ -38,9 +38,6 @@ reefnet_sensuspro_device_get_handshake (dc_device_t *device, unsigned char data[
dc_status_t dc_status_t
reefnet_sensuspro_device_write_interval (dc_device_t *device, unsigned char interval); reefnet_sensuspro_device_write_interval (dc_device_t *device, unsigned char interval);
dc_status_t
reefnet_sensuspro_parser_set_calibration (dc_parser_t *parser, double atmospheric, double hydrostatic);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif /* __cplusplus */ #endif /* __cplusplus */

View File

@ -56,9 +56,6 @@ reefnet_sensusultra_device_write_parameter (dc_device_t *device, reefnet_sensusu
dc_status_t dc_status_t
reefnet_sensusultra_device_sense (dc_device_t *device, unsigned char data[], unsigned int size); reefnet_sensusultra_device_sense (dc_device_t *device, unsigned char data[], unsigned int size);
dc_status_t
reefnet_sensusultra_parser_set_calibration (dc_parser_t *parser, double atmospheric, double hydrostatic);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif /* __cplusplus */ #endif /* __cplusplus */

View File

@ -86,6 +86,14 @@ typedef enum dc_usb_recipient_t {
DC_USB_RECIPIENT_OTHER = 0x03, DC_USB_RECIPIENT_OTHER = 0x03,
} dc_usb_recipient_t; } dc_usb_recipient_t;
/**
* USB device descriptor.
*/
typedef struct dc_usb_desc_t {
unsigned short vid;
unsigned short pid;
} dc_usb_desc_t;
/** /**
* Opaque object representing a USB device. * Opaque object representing a USB device.
*/ */

View File

@ -32,6 +32,14 @@
extern "C" { extern "C" {
#endif /* __cplusplus */ #endif /* __cplusplus */
/**
* USB HID device descriptor.
*/
typedef struct dc_usbhid_desc_t {
unsigned short vid;
unsigned short pid;
} dc_usbhid_desc_t;
/** /**
* Opaque object representing a USB HID device. * Opaque object representing a USB HID device.
*/ */

View File

@ -1,959 +0,0 @@
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="9.00"
Name="libdivecomputer"
ProjectGUID="{CEA7215A-D6B5-4840-8086-3C854F371997}"
RootNamespace="libdivecomputer"
Keyword="Win32Proj"
TargetFrameworkVersion="196613"
>
<Platforms>
<Platform
Name="Win32"
/>
</Platforms>
<ToolFiles>
</ToolFiles>
<Configurations>
<Configuration
Name="Debug|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="2"
CharacterSet="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories="..\include"
PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBDIVECOMPUTER_EXPORTS;ENABLE_LOGGING;HAVE_AF_IRDA_H;HAVE_WS2BTH_H"
MinimalRebuild="true"
BasicRuntimeChecks="3"
RuntimeLibrary="3"
UsePrecompiledHeader="0"
WarningLevel="3"
DebugInformationFormat="4"
CompileAs="2"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="ws2_32.lib"
LinkIncremental="2"
ModuleDefinitionFile="$(OutDir)/libdivecomputer.def"
GenerateDebugInformation="true"
SubSystem="2"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="2"
CharacterSet="1"
WholeProgramOptimization="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="2"
EnableIntrinsicFunctions="true"
AdditionalIncludeDirectories="..\include"
PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;LIBDIVECOMPUTER_EXPORTS;ENABLE_LOGGING;HAVE_AF_IRDA_H;HAVE_WS2BTH_H"
RuntimeLibrary="2"
EnableFunctionLevelLinking="true"
UsePrecompiledHeader="0"
WarningLevel="3"
DebugInformationFormat="3"
CompileAs="2"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="ws2_32.lib"
LinkIncremental="1"
ModuleDefinitionFile="$(OutDir)/libdivecomputer.def"
GenerateDebugInformation="true"
SubSystem="2"
OptimizeReferences="2"
EnableCOMDATFolding="2"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<Filter
Name="Source Files"
Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
>
<File
RelativePath="..\src\aes.c"
>
</File>
<File
RelativePath="..\src\array.c"
>
</File>
<File
RelativePath="..\src\atomics_cobalt.c"
>
</File>
<File
RelativePath="..\src\atomics_cobalt_parser.c"
>
</File>
<File
RelativePath="..\src\bluetooth.c"
>
</File>
<File
RelativePath="..\src\buffer.c"
>
</File>
<File
RelativePath="..\src\checksum.c"
>
</File>
<File
RelativePath="..\src\citizen_aqualand.c"
>
</File>
<File
RelativePath="..\src\citizen_aqualand_parser.c"
>
</File>
<File
RelativePath="..\src\cochran_commander.c"
>
</File>
<File
RelativePath="..\src\cochran_commander_parser.c"
>
</File>
<File
RelativePath="..\src\common.c"
>
</File>
<File
RelativePath="..\src\context.c"
>
</File>
<File
RelativePath="..\src\cressi_edy.c"
>
</File>
<File
RelativePath="..\src\cressi_edy_parser.c"
>
</File>
<File
RelativePath="..\src\cressi_goa.c"
>
</File>
<File
RelativePath="..\src\cressi_goa_parser.c"
>
</File>
<File
RelativePath="..\src\cressi_leonardo.c"
>
</File>
<File
RelativePath="..\src\cressi_leonardo_parser.c"
>
</File>
<File
RelativePath="..\src\custom.c"
>
</File>
<File
RelativePath="..\src\datetime.c"
>
</File>
<File
RelativePath="..\src\descriptor.c"
>
</File>
<File
RelativePath="..\src\device.c"
>
</File>
<File
RelativePath="..\src\diverite_nitekq.c"
>
</File>
<File
RelativePath="..\src\diverite_nitekq_parser.c"
>
</File>
<File
RelativePath="..\src\divesystem_idive.c"
>
</File>
<File
RelativePath="..\src\divesystem_idive_parser.c"
>
</File>
<File
RelativePath="..\src\hw_frog.c"
>
</File>
<File
RelativePath="..\src\hw_ostc.c"
>
</File>
<File
RelativePath="..\src\hw_ostc3.c"
>
</File>
<File
RelativePath="..\src\hw_ostc_parser.c"
>
</File>
<File
RelativePath="..\src\ihex.c"
>
</File>
<File
RelativePath="..\src\iostream.c"
>
</File>
<File
RelativePath="..\src\irda.c"
>
</File>
<File
RelativePath="..\src\iterator.c"
>
</File>
<File
RelativePath="..\src\liquivision_lynx.c"
>
</File>
<File
RelativePath="..\src\liquivision_lynx_parser.c"
>
</File>
<File
RelativePath="..\src\mares_common.c"
>
</File>
<File
RelativePath="..\src\mares_darwin.c"
>
</File>
<File
RelativePath="..\src\mares_darwin_parser.c"
>
</File>
<File
RelativePath="..\src\mares_iconhd.c"
>
</File>
<File
RelativePath="..\src\mares_iconhd_parser.c"
>
</File>
<File
RelativePath="..\src\mares_nemo.c"
>
</File>
<File
RelativePath="..\src\mares_nemo_parser.c"
>
</File>
<File
RelativePath="..\src\mares_puck.c"
>
</File>
<File
RelativePath="..\src\mclean_extreme.c"
>
</File>
<File
RelativePath="..\src\mclean_extreme_parser.c"
>
</File>
<File
RelativePath="..\src\oceanic_atom2.c"
>
</File>
<File
RelativePath="..\src\oceanic_atom2_parser.c"
>
</File>
<File
RelativePath="..\src\oceanic_common.c"
>
</File>
<File
RelativePath="..\src\oceanic_veo250.c"
>
</File>
<File
RelativePath="..\src\oceanic_veo250_parser.c"
>
</File>
<File
RelativePath="..\src\oceanic_vtpro.c"
>
</File>
<File
RelativePath="..\src\oceanic_vtpro_parser.c"
>
</File>
<File
RelativePath="..\src\parser.c"
>
</File>
<File
RelativePath="..\src\platform.c"
>
</File>
<File
RelativePath="..\src\rbstream.c"
>
</File>
<File
RelativePath="..\src\reefnet_sensus.c"
>
</File>
<File
RelativePath="..\src\reefnet_sensus_parser.c"
>
</File>
<File
RelativePath="..\src\reefnet_sensuspro.c"
>
</File>
<File
RelativePath="..\src\reefnet_sensuspro_parser.c"
>
</File>
<File
RelativePath="..\src\reefnet_sensusultra.c"
>
</File>
<File
RelativePath="..\src\reefnet_sensusultra_parser.c"
>
</File>
<File
RelativePath="..\src\ringbuffer.c"
>
</File>
<File
RelativePath="..\src\serial_win32.c"
>
</File>
<File
RelativePath="..\src\shearwater_common.c"
>
</File>
<File
RelativePath="..\src\shearwater_petrel.c"
>
</File>
<File
RelativePath="..\src\shearwater_predator.c"
>
</File>
<File
RelativePath="..\src\shearwater_predator_parser.c"
>
</File>
<File
RelativePath="..\src\socket.c"
>
</File>
<File
RelativePath="..\src\sporasub_sp2.c"
>
</File>
<File
RelativePath="..\src\sporasub_sp2_parser.c"
>
</File>
<File
RelativePath="..\src\suunto_common.c"
>
</File>
<File
RelativePath="..\src\suunto_common2.c"
>
</File>
<File
RelativePath="..\src\suunto_d9.c"
>
</File>
<File
RelativePath="..\src\suunto_d9_parser.c"
>
</File>
<File
RelativePath="..\src\suunto_eon.c"
>
</File>
<File
RelativePath="..\src\suunto_eon_parser.c"
>
</File>
<File
RelativePath="..\src\suunto_eonsteel.c"
>
</File>
<File
RelativePath="..\src\suunto_eonsteel_parser.c"
>
</File>
<File
RelativePath="..\src\suunto_solution.c"
>
</File>
<File
RelativePath="..\src\suunto_solution_parser.c"
>
</File>
<File
RelativePath="..\src\suunto_vyper.c"
>
</File>
<File
RelativePath="..\src\suunto_vyper2.c"
>
</File>
<File
RelativePath="..\src\suunto_vyper_parser.c"
>
</File>
<File
RelativePath="..\src\tecdiving_divecomputereu.c"
>
</File>
<File
RelativePath="..\src\tecdiving_divecomputereu_parser.c"
>
</File>
<File
RelativePath="..\src\timer.c"
>
</File>
<File
RelativePath="..\src\usb.c"
>
</File>
<File
RelativePath="..\src\usbhid.c"
>
</File>
<File
RelativePath="..\src\uwatec_aladin.c"
>
</File>
<File
RelativePath="..\src\uwatec_memomouse.c"
>
</File>
<File
RelativePath="..\src\uwatec_memomouse_parser.c"
>
</File>
<File
RelativePath="..\src\uwatec_smart.c"
>
</File>
<File
RelativePath="..\src\uwatec_smart_parser.c"
>
</File>
<File
RelativePath="..\src\version.c"
>
</File>
<File
RelativePath="..\src\zeagle_n2ition3.c"
>
</File>
</Filter>
<Filter
Name="Header Files"
Filter="h;hpp;hxx;hm;inl;inc;xsd"
UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
>
<File
RelativePath="..\src\aes.h"
>
</File>
<File
RelativePath="..\src\array.h"
>
</File>
<File
RelativePath="..\src\atomics_cobalt.h"
>
</File>
<File
RelativePath="..\include\libdivecomputer\atomics_cobalt.h"
>
</File>
<File
RelativePath="..\include\libdivecomputer\bluetooth.h"
>
</File>
<File
RelativePath="..\include\libdivecomputer\buffer.h"
>
</File>
<File
RelativePath="..\src\checksum.h"
>
</File>
<File
RelativePath="..\src\citizen_aqualand.h"
>
</File>
<File
RelativePath="..\src\cochran_commander.h"
>
</File>
<File
RelativePath="..\src\common-private.h"
>
</File>
<File
RelativePath="..\src\context-private.h"
>
</File>
<File
RelativePath="..\include\libdivecomputer\context.h"
>
</File>
<File
RelativePath="..\src\cressi_edy.h"
>
</File>
<File
RelativePath="..\src\cressi_goa.h"
>
</File>
<File
RelativePath="..\src\cressi_leonardo.h"
>
</File>
<File
RelativePath="..\include\libdivecomputer\custom.h"
>
</File>
<File
RelativePath="..\include\libdivecomputer\datetime.h"
>
</File>
<File
RelativePath="..\include\libdivecomputer\descriptor.h"
>
</File>
<File
RelativePath="..\src\device-private.h"
>
</File>
<File
RelativePath="..\include\libdivecomputer\device.h"
>
</File>
<File
RelativePath="..\src\diverite_nitekq.h"
>
</File>
<File
RelativePath="..\src\divesystem_idive.h"
>
</File>
<File
RelativePath="..\include\libdivecomputer\hw_frog.h"
>
</File>
<File
RelativePath="..\src\hw_frog.h"
>
</File>
<File
RelativePath="..\include\libdivecomputer\hw_ostc.h"
>
</File>
<File
RelativePath="..\src\hw_ostc.h"
>
</File>
<File
RelativePath="..\src\hw_ostc3.h"
>
</File>
<File
RelativePath="..\include\libdivecomputer\hw_ostc3.h"
>
</File>
<File
RelativePath="..\src\ihex.h"
>
</File>
<File
RelativePath="..\src\iostream-private.h"
>
</File>
<File
RelativePath="..\src\iostream.h"
>
</File>
<File
RelativePath="..\include\libdivecomputer\irda.h"
>
</File>
<File
RelativePath="..\src\iterator-private.h"
>
</File>
<File
RelativePath="..\include\libdivecomputer\iterator.h"
>
</File>
<File
RelativePath="..\src\liquivision_lynx.h"
>
</File>
<File
RelativePath="..\src\mares_common.h"
>
</File>
<File
RelativePath="..\src\mares_darwin.h"
>
</File>
<File
RelativePath="..\src\mares_iconhd.h"
>
</File>
<File
RelativePath="..\src\mares_nemo.h"
>
</File>
<File
RelativePath="..\src\mares_puck.h"
>
</File>
<File
RelativePath="..\src\mclean_extreme.h"
>
</File>
<File
RelativePath="..\src\oceanic_atom2.h"
>
</File>
<File
RelativePath="..\include\libdivecomputer\oceanic_atom2.h"
>
</File>
<File
RelativePath="..\src\oceanic_common.h"
>
</File>
<File
RelativePath="..\src\oceanic_veo250.h"
>
</File>
<File
RelativePath="..\include\libdivecomputer\oceanic_veo250.h"
>
</File>
<File
RelativePath="..\src\oceanic_vtpro.h"
>
</File>
<File
RelativePath="..\include\libdivecomputer\oceanic_vtpro.h"
>
</File>
<File
RelativePath="..\src\parser-private.h"
>
</File>
<File
RelativePath="..\include\libdivecomputer\parser.h"
>
</File>
<File
RelativePath="..\src\platform.h"
>
</File>
<File
RelativePath="..\src\rbstream.h"
>
</File>
<File
RelativePath="..\src\reefnet_sensus.h"
>
</File>
<File
RelativePath="..\include\libdivecomputer\reefnet_sensus.h"
>
</File>
<File
RelativePath="..\src\reefnet_sensuspro.h"
>
</File>
<File
RelativePath="..\include\libdivecomputer\reefnet_sensuspro.h"
>
</File>
<File
RelativePath="..\include\libdivecomputer\reefnet_sensusultra.h"
>
</File>
<File
RelativePath="..\src\reefnet_sensusultra.h"
>
</File>
<File
RelativePath="..\src\revision.h"
>
</File>
<File
RelativePath="..\src\ringbuffer.h"
>
</File>
<File
RelativePath="..\include\libdivecomputer\serial.h"
>
</File>
<File
RelativePath="..\src\shearwater_common.h"
>
</File>
<File
RelativePath="..\src\shearwater_petrel.h"
>
</File>
<File
RelativePath="..\src\shearwater_predator.h"
>
</File>
<File
RelativePath="..\src\socket.h"
>
</File>
<File
RelativePath="..\src\sporasub_sp2.h"
>
</File>
<File
RelativePath="..\src\suunto_common.h"
>
</File>
<File
RelativePath="..\src\suunto_common2.h"
>
</File>
<File
RelativePath="..\include\libdivecomputer\suunto_d9.h"
>
</File>
<File
RelativePath="..\src\suunto_d9.h"
>
</File>
<File
RelativePath="..\include\libdivecomputer\suunto_eon.h"
>
</File>
<File
RelativePath="..\src\suunto_eon.h"
>
</File>
<File
RelativePath="..\src\suunto_eonsteel.h"
>
</File>
<File
RelativePath="..\src\suunto_solution.h"
>
</File>
<File
RelativePath="..\src\suunto_vyper.h"
>
</File>
<File
RelativePath="..\include\libdivecomputer\suunto_vyper2.h"
>
</File>
<File
RelativePath="..\src\suunto_vyper2.h"
>
</File>
<File
RelativePath="..\src\tecdiving_divecomputereu.h"
>
</File>
<File
RelativePath="..\src\timer.h"
>
</File>
<File
RelativePath="..\include\libdivecomputer\units.h"
>
</File>
<File
RelativePath="..\include\libdivecomputer\usb.h"
>
</File>
<File
RelativePath="..\include\libdivecomputer\usbhid.h"
>
</File>
<File
RelativePath="..\src\uwatec_aladin.h"
>
</File>
<File
RelativePath="..\src\uwatec_memomouse.h"
>
</File>
<File
RelativePath="..\src\uwatec_smart.h"
>
</File>
<File
RelativePath="..\include\libdivecomputer\version.h"
>
</File>
<File
RelativePath="..\src\zeagle_n2ition3.h"
>
</File>
</Filter>
<Filter
Name="Resource Files"
Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
>
<File
RelativePath="..\src\libdivecomputer.rc"
>
</File>
<File
RelativePath="..\src\libdivecomputer.symbols"
>
<FileConfiguration
Name="Debug|Win32"
>
<Tool
Name="VCCustomBuildTool"
CommandLine="echo EXPORTS &gt; &quot;$(OutDir)/libdivecomputer.def&quot; &amp;&amp; type &quot;$(InputPath)&quot; &gt;&gt; &quot;$(OutDir)/libdivecomputer.def&quot;"
Outputs="$(OutDir)/libdivecomputer.def"
/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32"
>
<Tool
Name="VCCustomBuildTool"
CommandLine="echo EXPORTS &gt; &quot;$(OutDir)/libdivecomputer.def&quot; &amp;&amp; type &quot;$(InputPath)&quot; &gt;&gt; &quot;$(OutDir)/libdivecomputer.def&quot;"
Outputs="$(OutDir)/libdivecomputer.def"
/>
</FileConfiguration>
</File>
</Filter>
</Files>
<Globals>
</Globals>
</VisualStudioProject>

View File

@ -16,7 +16,7 @@ endif
libdivecomputer_la_SOURCES = \ libdivecomputer_la_SOURCES = \
version.c \ version.c \
descriptor-private.h descriptor.c \ descriptor.c \
iostream-private.h iostream.c \ iostream-private.h iostream.c \
iterator-private.h iterator.c \ iterator-private.h iterator.c \
common-private.h common.c \ common-private.h common.c \
@ -43,6 +43,7 @@ libdivecomputer_la_SOURCES = \
oceanic_atom2.h oceanic_atom2.c oceanic_atom2_parser.c \ oceanic_atom2.h oceanic_atom2.c oceanic_atom2_parser.c \
oceanic_veo250.h oceanic_veo250.c oceanic_veo250_parser.c \ oceanic_veo250.h oceanic_veo250.c oceanic_veo250_parser.c \
oceanic_vtpro.h oceanic_vtpro.c oceanic_vtpro_parser.c \ oceanic_vtpro.h oceanic_vtpro.c oceanic_vtpro_parser.c \
pelagic_i330r.h pelagic_i330r.c \
mares_common.h mares_common.c \ mares_common.h mares_common.c \
mares_nemo.h mares_nemo.c mares_nemo_parser.c \ mares_nemo.h mares_nemo.c mares_nemo_parser.c \
mares_puck.h mares_puck.c \ mares_puck.h mares_puck.c \
@ -75,6 +76,14 @@ libdivecomputer_la_SOURCES = \
mclean_extreme.h mclean_extreme.c mclean_extreme_parser.c \ mclean_extreme.h mclean_extreme.c mclean_extreme_parser.c \
liquivision_lynx.h liquivision_lynx.c liquivision_lynx_parser.c \ liquivision_lynx.h liquivision_lynx.c liquivision_lynx_parser.c \
sporasub_sp2.h sporasub_sp2.c sporasub_sp2_parser.c \ sporasub_sp2.h sporasub_sp2.c sporasub_sp2_parser.c \
deepsix_excursion.h deepsix_excursion.c deepsix_excursion_parser.c \
seac_screen.h seac_screen.c seac_screen_parser.c \
deepblu_cosmiq.h deepblu_cosmiq.c deepblu_cosmiq_parser.c \
oceans_s1_common.h oceans_s1_common.c \
oceans_s1.h oceans_s1.c oceans_s1_parser.c \
divesoft_freedom.h divesoft_freedom.c divesoft_freedom_parser.c \
hdlc.h hdlc.c \
packet.h packet.c \
socket.h socket.c \ socket.h socket.c \
irda.c \ irda.c \
usb.c \ usb.c \
@ -98,7 +107,7 @@ libdivecomputer.exp: libdivecomputer.symbols
$(AM_V_GEN) sed -e '/^$$/d' $< > $@ $(AM_V_GEN) sed -e '/^$$/d' $< > $@
.rc.lo: .rc.lo:
$(AM_V_GEN) $(LIBTOOL) --silent --tag=CC --mode=compile $(RC) $(DEFS) $(DEFAULT_INCLUDES) $< -o $@ $(AM_V_GEN) $(LIBTOOL) --silent --tag=CC --mode=compile $(RC) $(DEFS) $(DEFAULT_INCLUDES) $(AM_CPPFLAGS) $< -o $@
libdivecomputer.lo: revision.h libdivecomputer.lo: revision.h

View File

@ -160,6 +160,30 @@ array_convert_str2num (const unsigned char data[], unsigned int size)
return value; return value;
} }
unsigned int
array_convert_bin2dec (const unsigned char data[], unsigned int size)
{
unsigned int value = 0;
for (unsigned int i = 0; i < size; ++i) {
value *= 100;
value += data[i];
}
return value;
}
unsigned int
array_convert_bcd2dec (const unsigned char data[], unsigned int size)
{
unsigned int value = 0;
for (unsigned int i = 0; i < size; ++i) {
value *= 100;
value += bcd2dec(data[i]);
}
return value;
}
unsigned int unsigned int
array_uint_be (const unsigned char data[], unsigned int n) array_uint_be (const unsigned char data[], unsigned int n)
{ {
@ -184,6 +208,32 @@ array_uint_le (const unsigned char data[], unsigned int n)
return value; return value;
} }
unsigned long long
array_uint64_be (const unsigned char data[])
{
return ((unsigned long long) data[0] << 56) |
((unsigned long long) data[1] << 48) |
((unsigned long long) data[2] << 40) |
((unsigned long long) data[3] << 32) |
((unsigned long long) data[4] << 24) |
((unsigned long long) data[5] << 16) |
((unsigned long long) data[6] << 8) |
((unsigned long long) data[7] << 0);
}
unsigned long long
array_uint64_le (const unsigned char data[])
{
return ((unsigned long long) data[0] << 0) |
((unsigned long long) data[1] << 8) |
((unsigned long long) data[2] << 16) |
((unsigned long long) data[3] << 24) |
((unsigned long long) data[4] << 32) |
((unsigned long long) data[5] << 40) |
((unsigned long long) data[6] << 48) |
((unsigned long long) data[7] << 56);
}
unsigned int unsigned int
array_uint32_be (const unsigned char data[]) array_uint32_be (const unsigned char data[])
{ {
@ -213,17 +263,6 @@ array_uint32_word_be (const unsigned char data[])
((unsigned int) data[3] << 16); ((unsigned int) data[3] << 16);
} }
void
array_uint32_le_set (unsigned char data[], const unsigned int input)
{
data[0] = input & 0xFF;
data[1] = (input >> 8) & 0xFF;
data[2] = (input >> 16) & 0xFF;
data[3] = (input >> 24) & 0xFF;
}
unsigned int unsigned int
array_uint24_be (const unsigned char data[]) array_uint24_be (const unsigned char data[])
{ {
@ -232,16 +271,6 @@ array_uint24_be (const unsigned char data[])
((unsigned int) data[2] << 0); ((unsigned int) data[2] << 0);
} }
void
array_uint24_be_set (unsigned char data[], const unsigned int input)
{
data[0] = (input >> 16) & 0xFF;
data[1] = (input >> 8) & 0xFF;
data[2] = input & 0xFF;
}
unsigned int unsigned int
array_uint24_le (const unsigned char data[]) array_uint24_le (const unsigned char data[])
{ {
@ -265,8 +294,124 @@ array_uint16_le (const unsigned char data[])
((unsigned int) data[1] << 8); ((unsigned int) data[1] << 8);
} }
void
array_uint64_be_set (unsigned char data[], const unsigned long long input)
{
data[0] = (input >> 56) & 0xFF;
data[1] = (input >> 48) & 0xFF;
data[2] = (input >> 40) & 0xFF;
data[3] = (input >> 32) & 0xFF;
data[4] = (input >> 24) & 0xFF;
data[5] = (input >> 16) & 0xFF;
data[6] = (input >> 8) & 0xFF;
data[7] = (input ) & 0xFF;
}
void
array_uint64_le_set (unsigned char data[], const unsigned long long input)
{
data[0] = (input ) & 0xFF;
data[1] = (input >> 8) & 0xFF;
data[2] = (input >> 16) & 0xFF;
data[3] = (input >> 24) & 0xFF;
data[4] = (input >> 32) & 0xFF;
data[5] = (input >> 40) & 0xFF;
data[6] = (input >> 48) & 0xFF;
data[7] = (input >> 56) & 0xFF;
}
void
array_uint32_be_set (unsigned char data[], const unsigned int input)
{
data[0] = (input >> 24) & 0xFF;
data[1] = (input >> 16) & 0xFF;
data[2] = (input >> 8) & 0xFF;
data[3] = (input ) & 0xFF;
}
void
array_uint32_le_set (unsigned char data[], const unsigned int input)
{
data[0] = (input ) & 0xFF;
data[1] = (input >> 8) & 0xFF;
data[2] = (input >> 16) & 0xFF;
data[3] = (input >> 24) & 0xFF;
}
void
array_uint24_be_set (unsigned char data[], const unsigned int input)
{
data[0] = (input >> 16) & 0xFF;
data[1] = (input >> 8) & 0xFF;
data[2] = (input ) & 0xFF;
}
void
array_uint24_le_set (unsigned char data[], const unsigned int input)
{
data[0] = (input ) & 0xFF;
data[1] = (input >> 8) & 0xFF;
data[2] = (input >> 16) & 0xFF;
}
void
array_uint16_be_set (unsigned char data[], const unsigned short input)
{
data[0] = (input >> 8) & 0xFF;
data[1] = (input ) & 0xFF;
}
void
array_uint16_le_set (unsigned char data[], const unsigned short input)
{
data[0] = (input ) & 0xFF;
data[1] = (input >> 8) & 0xFF;
}
unsigned char unsigned char
bcd2dec (unsigned char value) bcd2dec (unsigned char value)
{ {
return ((value >> 4) & 0x0f) * 10 + (value & 0x0f); return ((value >> 4) & 0x0f) * 10 + (value & 0x0f);
} }
unsigned char
dec2bcd (unsigned char value)
{
if (value >= 100)
return 0;
unsigned char hi = value / 10;
unsigned char lo = value % 10;
return (hi << 4) | lo;
}
/*
* When turning a two's-complement number with a certain number
* of bits into one with more bits, the sign bit must be repeated
* in all the extra bits.
*/
unsigned int
signextend (unsigned int value, unsigned int nbits)
{
if (nbits <= 0 || nbits > 32)
return 0;
unsigned int signbit = 1U << (nbits - 1);
unsigned int mask = signbit - 1;
if ((value & signbit) == signbit)
return value | ~mask;
else
return value & mask;
}
unsigned int
popcount (unsigned int value)
{
unsigned int count = 0;
while (value) {
value &= value - 1;
count++;
}
return count;
}

View File

@ -22,6 +22,8 @@
#ifndef ARRAY_H #ifndef ARRAY_H
#define ARRAY_H #define ARRAY_H
#define C_ARRAY_SIZE(a) (sizeof (a) / sizeof *(a))
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif /* __cplusplus */ #endif /* __cplusplus */
@ -52,12 +54,24 @@ array_convert_hex2bin (const unsigned char input[], unsigned int isize, unsigned
unsigned int unsigned int
array_convert_str2num (const unsigned char data[], unsigned int size); array_convert_str2num (const unsigned char data[], unsigned int size);
unsigned int
array_convert_bin2dec (const unsigned char data[], unsigned int size);
unsigned int
array_convert_bcd2dec (const unsigned char data[], unsigned int size);
unsigned int unsigned int
array_uint_be (const unsigned char data[], unsigned int n); array_uint_be (const unsigned char data[], unsigned int n);
unsigned int unsigned int
array_uint_le (const unsigned char data[], unsigned int n); array_uint_le (const unsigned char data[], unsigned int n);
unsigned long long
array_uint64_be (const unsigned char data[]);
unsigned long long
array_uint64_le (const unsigned char data[]);
unsigned int unsigned int
array_uint32_be (const unsigned char data[]); array_uint32_be (const unsigned char data[]);
@ -67,15 +81,9 @@ array_uint32_le (const unsigned char data[]);
unsigned int unsigned int
array_uint32_word_be (const unsigned char data[]); array_uint32_word_be (const unsigned char data[]);
void
array_uint32_le_set (unsigned char data[], const unsigned int input);
unsigned int unsigned int
array_uint24_be (const unsigned char data[]); array_uint24_be (const unsigned char data[]);
void
array_uint24_be_set (unsigned char data[], const unsigned int input);
unsigned int unsigned int
array_uint24_le (const unsigned char data[]); array_uint24_le (const unsigned char data[]);
@ -85,9 +93,42 @@ array_uint16_be (const unsigned char data[]);
unsigned short unsigned short
array_uint16_le (const unsigned char data[]); array_uint16_le (const unsigned char data[]);
void
array_uint64_be_set (unsigned char data[], const unsigned long long input);
void
array_uint64_le_set (unsigned char data[], const unsigned long long input);
void
array_uint32_be_set (unsigned char data[], const unsigned int input);
void
array_uint32_le_set (unsigned char data[], const unsigned int input);
void
array_uint24_be_set (unsigned char data[], const unsigned int input);
void
array_uint24_le_set (unsigned char data[], const unsigned int input);
void
array_uint16_be_set (unsigned char data[], const unsigned short input);
void
array_uint16_le_set (unsigned char data[], const unsigned short input);
unsigned char unsigned char
bcd2dec (unsigned char value); bcd2dec (unsigned char value);
unsigned char
dec2bcd (unsigned char value);
unsigned int
signextend (unsigned int value, unsigned int nbits);
unsigned int
popcount (unsigned int value);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif /* __cplusplus */ #endif /* __cplusplus */

View File

@ -41,6 +41,8 @@
#define FP_OFFSET 20 #define FP_OFFSET 20
#define SZ_HEADER 228
#define SZ_MEMORY1 (29 * 64 * 1024) // Cobalt 1 #define SZ_MEMORY1 (29 * 64 * 1024) // Cobalt 1
#define SZ_MEMORY2 (41 * 64 * 1024) // Cobalt 2 #define SZ_MEMORY2 (41 * 64 * 1024) // Cobalt 2
#define SZ_VERSION 14 #define SZ_VERSION 14
@ -347,6 +349,12 @@ atomics_cobalt_device_foreach (dc_device_t *abstract, dc_dive_callback_t callbac
return DC_STATUS_SUCCESS; return DC_STATUS_SUCCESS;
} }
if (size < SZ_HEADER) {
ERROR (abstract->context, "Dive header is too small (%u).", size);
dc_buffer_free (buffer);
return DC_STATUS_DATAFORMAT;
}
if (memcmp (data + FP_OFFSET, device->fingerprint, sizeof (device->fingerprint)) == 0) { if (memcmp (data + FP_OFFSET, device->fingerprint, sizeof (device->fingerprint)) == 0) {
dc_buffer_free (buffer); dc_buffer_free (buffer);
return DC_STATUS_SUCCESS; return DC_STATUS_SUCCESS;

View File

@ -36,7 +36,7 @@ dc_status_t
atomics_cobalt_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream); atomics_cobalt_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream);
dc_status_t dc_status_t
atomics_cobalt_parser_create (dc_parser_t **parser, dc_context_t *context); atomics_cobalt_parser_create (dc_parser_t **parser, dc_context_t *context, const unsigned char data[], size_t size);
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@ -40,11 +40,10 @@ typedef struct atomics_cobalt_parser_t atomics_cobalt_parser_t;
struct atomics_cobalt_parser_t { struct atomics_cobalt_parser_t {
dc_parser_t base; dc_parser_t base;
// Depth calibration. // Depth calibration.
double atmospheric;
double hydrostatic; double hydrostatic;
}; };
static dc_status_t atomics_cobalt_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size); static dc_status_t atomics_cobalt_parser_set_density (dc_parser_t *abstract, double density);
static dc_status_t atomics_cobalt_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime); static dc_status_t atomics_cobalt_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime);
static dc_status_t atomics_cobalt_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value); static dc_status_t atomics_cobalt_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value);
static dc_status_t atomics_cobalt_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata); static dc_status_t atomics_cobalt_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata);
@ -52,7 +51,9 @@ static dc_status_t atomics_cobalt_parser_samples_foreach (dc_parser_t *abstract,
static const dc_parser_vtable_t atomics_cobalt_parser_vtable = { static const dc_parser_vtable_t atomics_cobalt_parser_vtable = {
sizeof(atomics_cobalt_parser_t), sizeof(atomics_cobalt_parser_t),
DC_FAMILY_ATOMICS_COBALT, DC_FAMILY_ATOMICS_COBALT,
atomics_cobalt_parser_set_data, /* set_data */ NULL, /* set_clock */
NULL, /* set_atmospheric */
atomics_cobalt_parser_set_density, /* set_density */
atomics_cobalt_parser_get_datetime, /* datetime */ atomics_cobalt_parser_get_datetime, /* datetime */
atomics_cobalt_parser_get_field, /* fields */ atomics_cobalt_parser_get_field, /* fields */
atomics_cobalt_parser_samples_foreach, /* samples_foreach */ atomics_cobalt_parser_samples_foreach, /* samples_foreach */
@ -61,7 +62,7 @@ static const dc_parser_vtable_t atomics_cobalt_parser_vtable = {
dc_status_t dc_status_t
atomics_cobalt_parser_create (dc_parser_t **out, dc_context_t *context) atomics_cobalt_parser_create (dc_parser_t **out, dc_context_t *context, const unsigned char data[], size_t size)
{ {
atomics_cobalt_parser_t *parser = NULL; atomics_cobalt_parser_t *parser = NULL;
@ -69,15 +70,14 @@ atomics_cobalt_parser_create (dc_parser_t **out, dc_context_t *context)
return DC_STATUS_INVALIDARGS; return DC_STATUS_INVALIDARGS;
// Allocate memory. // Allocate memory.
parser = (atomics_cobalt_parser_t *) dc_parser_allocate (context, &atomics_cobalt_parser_vtable); parser = (atomics_cobalt_parser_t *) dc_parser_allocate (context, &atomics_cobalt_parser_vtable, data, size);
if (parser == NULL) { if (parser == NULL) {
ERROR (context, "Failed to allocate memory."); ERROR (context, "Failed to allocate memory.");
return DC_STATUS_NOMEMORY; return DC_STATUS_NOMEMORY;
} }
// Set the default values. // Set the default values.
parser->atmospheric = 0.0; parser->hydrostatic = DEF_DENSITY_SALT * GRAVITY;
parser->hydrostatic = 1025.0 * GRAVITY;
*out = (dc_parser_t*) parser; *out = (dc_parser_t*) parser;
@ -86,22 +86,11 @@ atomics_cobalt_parser_create (dc_parser_t **out, dc_context_t *context)
static dc_status_t static dc_status_t
atomics_cobalt_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size) atomics_cobalt_parser_set_density (dc_parser_t *abstract, double density)
{ {
return DC_STATUS_SUCCESS; atomics_cobalt_parser_t *parser = (atomics_cobalt_parser_t *) abstract;
}
parser->hydrostatic = density * GRAVITY;
dc_status_t
atomics_cobalt_parser_set_calibration (dc_parser_t *abstract, double atmospheric, double hydrostatic)
{
atomics_cobalt_parser_t *parser = (atomics_cobalt_parser_t*) abstract;
if (!ISINSTANCE (abstract))
return DC_STATUS_INVALIDARGS;
parser->atmospheric = atmospheric;
parser->hydrostatic = hydrostatic;
return DC_STATUS_SUCCESS; return DC_STATUS_SUCCESS;
} }
@ -142,11 +131,7 @@ atomics_cobalt_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, un
dc_gasmix_t *gasmix = (dc_gasmix_t *) value; dc_gasmix_t *gasmix = (dc_gasmix_t *) value;
dc_tank_t *tank = (dc_tank_t *) value; dc_tank_t *tank = (dc_tank_t *) value;
double atmospheric = 0.0; unsigned int atmospheric = array_uint16_le (p + 0x26);
if (parser->atmospheric)
atmospheric = parser->atmospheric;
else
atmospheric = array_uint16_le (p + 0x26) * BAR / 1000.0;
unsigned int workpressure = 0; unsigned int workpressure = 0;
@ -156,13 +141,14 @@ atomics_cobalt_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, un
*((unsigned int *) value) = array_uint16_le (p + 0x58) * 60; *((unsigned int *) value) = array_uint16_le (p + 0x58) * 60;
break; break;
case DC_FIELD_MAXDEPTH: case DC_FIELD_MAXDEPTH:
*((double *) value) = (array_uint16_le (p + 0x56) * BAR / 1000.0 - atmospheric) / parser->hydrostatic; *((double *) value) = (signed int)(array_uint16_le (p + 0x56) - atmospheric) * (BAR / 1000.0) / parser->hydrostatic;
break; break;
case DC_FIELD_GASMIX_COUNT: case DC_FIELD_GASMIX_COUNT:
case DC_FIELD_TANK_COUNT: case DC_FIELD_TANK_COUNT:
*((unsigned int *) value) = p[0x2a]; *((unsigned int *) value) = p[0x2a];
break; break;
case DC_FIELD_GASMIX: case DC_FIELD_GASMIX:
gasmix->usage = DC_USAGE_NONE;
gasmix->helium = p[SZ_HEADER + SZ_GASMIX * flags + 5] / 100.0; gasmix->helium = p[SZ_HEADER + SZ_GASMIX * flags + 5] / 100.0;
gasmix->oxygen = p[SZ_HEADER + SZ_GASMIX * flags + 4] / 100.0; gasmix->oxygen = p[SZ_HEADER + SZ_GASMIX * flags + 4] / 100.0;
gasmix->nitrogen = 1.0 - gasmix->oxygen - gasmix->helium; gasmix->nitrogen = 1.0 - gasmix->oxygen - gasmix->helium;
@ -194,6 +180,7 @@ atomics_cobalt_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, un
tank->gasmix = flags; tank->gasmix = flags;
tank->beginpressure = array_uint16_le(p + 6) * PSI / BAR; tank->beginpressure = array_uint16_le(p + 6) * PSI / BAR;
tank->endpressure = array_uint16_le(p + 14) * PSI / BAR; tank->endpressure = array_uint16_le(p + 14) * PSI / BAR;
tank->usage = DC_USAGE_NONE;
break; break;
case DC_FIELD_DIVEMODE: case DC_FIELD_DIVEMODE:
switch(p[0x24]) { switch(p[0x24]) {
@ -208,6 +195,9 @@ atomics_cobalt_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, un
return DC_STATUS_DATAFORMAT; return DC_STATUS_DATAFORMAT;
} }
break; break;
case DC_FIELD_ATMOSPHERIC:
*((double *) value) = atmospheric / 1000.0;
break;
default: default:
return DC_STATUS_UNSUPPORTED; return DC_STATUS_UNSUPPORTED;
} }
@ -239,11 +229,7 @@ atomics_cobalt_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback
if (size < header + SZ_SEGMENT * nsegments) if (size < header + SZ_SEGMENT * nsegments)
return DC_STATUS_DATAFORMAT; return DC_STATUS_DATAFORMAT;
double atmospheric = 0.0; unsigned int atmospheric = array_uint16_le (data + 0x26);
if (parser->atmospheric)
atmospheric = parser->atmospheric;
else
atmospheric = array_uint16_le (data + 0x26) * BAR / 1000.0;
// Previous gas mix - initialize with impossible value // Previous gas mix - initialize with impossible value
unsigned int gasmix_previous = 0xFFFFFFFF; unsigned int gasmix_previous = 0xFFFFFFFF;
@ -269,19 +255,19 @@ atomics_cobalt_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback
// Time (seconds). // Time (seconds).
time += interval; time += interval;
sample.time = time; sample.time = time * 1000;
if (callback) callback (DC_SAMPLE_TIME, sample, userdata); if (callback) callback (DC_SAMPLE_TIME, &sample, userdata);
// Depth (1/1000 bar). // Depth (1/1000 bar).
unsigned int depth = array_uint16_le (data + offset + 0); unsigned int depth = array_uint16_le (data + offset + 0);
sample.depth = (depth * BAR / 1000.0 - atmospheric) / parser->hydrostatic; sample.depth = (signed int)(depth - atmospheric) * (BAR / 1000.0) / parser->hydrostatic;
if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata); if (callback) callback (DC_SAMPLE_DEPTH, &sample, userdata);
// Pressure (1 psi). // Pressure (1 psi).
unsigned int pressure = array_uint16_le (data + offset + 2); unsigned int pressure = array_uint16_le (data + offset + 2);
sample.pressure.tank = tank; sample.pressure.tank = tank;
sample.pressure.value = pressure * PSI / BAR; sample.pressure.value = pressure * PSI / BAR;
if (callback) callback (DC_SAMPLE_PRESSURE, sample, userdata); if (callback) callback (DC_SAMPLE_PRESSURE, &sample, userdata);
// Current gas mix // Current gas mix
unsigned int gasmix = data[offset + 4]; unsigned int gasmix = data[offset + 4];
@ -297,14 +283,14 @@ atomics_cobalt_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback
return DC_STATUS_DATAFORMAT; return DC_STATUS_DATAFORMAT;
} }
sample.gasmix = idx; sample.gasmix = idx;
if (callback) callback (DC_SAMPLE_GASMIX, sample, userdata); if (callback) callback (DC_SAMPLE_GASMIX, &sample, userdata);
gasmix_previous = gasmix; gasmix_previous = gasmix;
} }
// Temperature (1 °F). // Temperature (1 °F).
unsigned int temperature = data[offset + 8]; unsigned int temperature = data[offset + 8];
sample.temperature = (temperature - 32.0) * (5.0 / 9.0); sample.temperature = (temperature - 32.0) * (5.0 / 9.0);
if (callback) callback (DC_SAMPLE_TEMPERATURE, sample, userdata); if (callback) callback (DC_SAMPLE_TEMPERATURE, &sample, userdata);
// violation status // violation status
sample.event.type = 0; sample.event.type = 0;
@ -314,15 +300,15 @@ atomics_cobalt_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback
unsigned int violation = data[offset + 11]; unsigned int violation = data[offset + 11];
if (violation & 0x01) { if (violation & 0x01) {
sample.event.type = SAMPLE_EVENT_ASCENT; sample.event.type = SAMPLE_EVENT_ASCENT;
if (callback) callback (DC_SAMPLE_EVENT, sample, userdata); if (callback) callback (DC_SAMPLE_EVENT, &sample, userdata);
} }
if (violation & 0x04) { if (violation & 0x04) {
sample.event.type = SAMPLE_EVENT_CEILING; sample.event.type = SAMPLE_EVENT_CEILING;
if (callback) callback (DC_SAMPLE_EVENT, sample, userdata); if (callback) callback (DC_SAMPLE_EVENT, &sample, userdata);
} }
if (violation & 0x08) { if (violation & 0x08) {
sample.event.type = SAMPLE_EVENT_PO2; sample.event.type = SAMPLE_EVENT_PO2;
if (callback) callback (DC_SAMPLE_EVENT, sample, userdata); if (callback) callback (DC_SAMPLE_EVENT, &sample, userdata);
} }
// NDL & deco // NDL & deco
@ -337,7 +323,8 @@ atomics_cobalt_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback
sample.deco.type = DC_DECO_NDL; sample.deco.type = DC_DECO_NDL;
sample.deco.time = ndl; sample.deco.time = ndl;
sample.deco.depth = 0.0; sample.deco.depth = 0.0;
if (callback) callback (DC_SAMPLE_DECO, sample, userdata); sample.deco.tts = 0;
if (callback) callback (DC_SAMPLE_DECO, &sample, userdata);
offset += SZ_SEGMENT; offset += SZ_SEGMENT;
} }

View File

@ -24,7 +24,6 @@
#endif #endif
#include <stdlib.h> // malloc, free #include <stdlib.h> // malloc, free
#include <stdio.h>
#include "socket.h" #include "socket.h"
@ -52,7 +51,6 @@
#include "context-private.h" #include "context-private.h"
#include "iostream-private.h" #include "iostream-private.h"
#include "iterator-private.h" #include "iterator-private.h"
#include "descriptor-private.h"
#include "platform.h" #include "platform.h"
#ifdef _WIN32 #ifdef _WIN32
@ -231,7 +229,7 @@ dc_bluetooth_addr2str(dc_bluetooth_address_t address, char *str, size_t size)
if (str == NULL || size < DC_BLUETOOTH_SIZE) if (str == NULL || size < DC_BLUETOOTH_SIZE)
return NULL; return NULL;
int n = snprintf(str, size, "%02X:%02X:%02X:%02X:%02X:%02X", int n = dc_platform_snprintf(str, size, "%02X:%02X:%02X:%02X:%02X:%02X",
(unsigned char)((address >> 40) & 0xFF), (unsigned char)((address >> 40) & 0xFF),
(unsigned char)((address >> 32) & 0xFF), (unsigned char)((address >> 32) & 0xFF),
(unsigned char)((address >> 24) & 0xFF), (unsigned char)((address >> 24) & 0xFF),
@ -456,7 +454,7 @@ dc_bluetooth_iterator_next (dc_iterator_t *abstract, void *out)
INFO (abstract->context, "Discover: address=" DC_ADDRESS_FORMAT ", name=%s", INFO (abstract->context, "Discover: address=" DC_ADDRESS_FORMAT ", name=%s",
address, name ? name : ""); address, name ? name : "");
if (!dc_descriptor_filter (iterator->descriptor, DC_TRANSPORT_BLUETOOTH, name, NULL)) { if (!dc_descriptor_filter (iterator->descriptor, DC_TRANSPORT_BLUETOOTH, name)) {
continue; continue;
} }

View File

@ -68,8 +68,13 @@ checksum_xor_uint8 (const unsigned char data[], unsigned int size, unsigned char
} }
/*
* Polynomial: 0x1021
* RefIn: False
* RefOut: False
*/
unsigned short unsigned short
checksum_crc16_ccitt (const unsigned char data[], unsigned int size, unsigned short init) checksum_crc16_ccitt (const unsigned char data[], unsigned int size, unsigned short init, unsigned short xorout)
{ {
static const unsigned short crc_ccitt_table[] = { static const unsigned short crc_ccitt_table[] = {
0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7,
@ -110,11 +115,170 @@ checksum_crc16_ccitt (const unsigned char data[], unsigned int size, unsigned sh
for (unsigned int i = 0; i < size; ++i) for (unsigned int i = 0; i < size; ++i)
crc = (crc << 8) ^ crc_ccitt_table[(crc >> 8) ^ data[i]]; crc = (crc << 8) ^ crc_ccitt_table[(crc >> 8) ^ data[i]];
return crc; return crc ^ xorout;
} }
/*
* Polynomial: 0x1021
* RefIn: True
* RefOut: True
*/
unsigned short
checksum_crc16r_ccitt (const unsigned char data[], unsigned int size, unsigned short init, unsigned short xorout)
{
static const unsigned short crc_ccitt_table[] = {
0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
};
unsigned short crc = init;
for (unsigned int i = 0; i < size; ++i)
crc = (crc >> 8) ^ crc_ccitt_table[(crc ^ data[i]) & 0xff];
return crc ^ xorout;
}
/*
* Polynomial: 0x8005
* RefIn: False
* RefOut: False
*/
unsigned short
checksum_crc16_ansi (const unsigned char data[], unsigned int size, unsigned short init, unsigned short xorout)
{
static const unsigned short crc_ccitt_table[] = {
0x0000, 0x8005, 0x800f, 0x000a, 0x801b, 0x001e, 0x0014, 0x8011,
0x8033, 0x0036, 0x003c, 0x8039, 0x0028, 0x802d, 0x8027, 0x0022,
0x8063, 0x0066, 0x006c, 0x8069, 0x0078, 0x807d, 0x8077, 0x0072,
0x0050, 0x8055, 0x805f, 0x005a, 0x804b, 0x004e, 0x0044, 0x8041,
0x80c3, 0x00c6, 0x00cc, 0x80c9, 0x00d8, 0x80dd, 0x80d7, 0x00d2,
0x00f0, 0x80f5, 0x80ff, 0x00fa, 0x80eb, 0x00ee, 0x00e4, 0x80e1,
0x00a0, 0x80a5, 0x80af, 0x00aa, 0x80bb, 0x00be, 0x00b4, 0x80b1,
0x8093, 0x0096, 0x009c, 0x8099, 0x0088, 0x808d, 0x8087, 0x0082,
0x8183, 0x0186, 0x018c, 0x8189, 0x0198, 0x819d, 0x8197, 0x0192,
0x01b0, 0x81b5, 0x81bf, 0x01ba, 0x81ab, 0x01ae, 0x01a4, 0x81a1,
0x01e0, 0x81e5, 0x81ef, 0x01ea, 0x81fb, 0x01fe, 0x01f4, 0x81f1,
0x81d3, 0x01d6, 0x01dc, 0x81d9, 0x01c8, 0x81cd, 0x81c7, 0x01c2,
0x0140, 0x8145, 0x814f, 0x014a, 0x815b, 0x015e, 0x0154, 0x8151,
0x8173, 0x0176, 0x017c, 0x8179, 0x0168, 0x816d, 0x8167, 0x0162,
0x8123, 0x0126, 0x012c, 0x8129, 0x0138, 0x813d, 0x8137, 0x0132,
0x0110, 0x8115, 0x811f, 0x011a, 0x810b, 0x010e, 0x0104, 0x8101,
0x8303, 0x0306, 0x030c, 0x8309, 0x0318, 0x831d, 0x8317, 0x0312,
0x0330, 0x8335, 0x833f, 0x033a, 0x832b, 0x032e, 0x0324, 0x8321,
0x0360, 0x8365, 0x836f, 0x036a, 0x837b, 0x037e, 0x0374, 0x8371,
0x8353, 0x0356, 0x035c, 0x8359, 0x0348, 0x834d, 0x8347, 0x0342,
0x03c0, 0x83c5, 0x83cf, 0x03ca, 0x83db, 0x03de, 0x03d4, 0x83d1,
0x83f3, 0x03f6, 0x03fc, 0x83f9, 0x03e8, 0x83ed, 0x83e7, 0x03e2,
0x83a3, 0x03a6, 0x03ac, 0x83a9, 0x03b8, 0x83bd, 0x83b7, 0x03b2,
0x0390, 0x8395, 0x839f, 0x039a, 0x838b, 0x038e, 0x0384, 0x8381,
0x0280, 0x8285, 0x828f, 0x028a, 0x829b, 0x029e, 0x0294, 0x8291,
0x82b3, 0x02b6, 0x02bc, 0x82b9, 0x02a8, 0x82ad, 0x82a7, 0x02a2,
0x82e3, 0x02e6, 0x02ec, 0x82e9, 0x02f8, 0x82fd, 0x82f7, 0x02f2,
0x02d0, 0x82d5, 0x82df, 0x02da, 0x82cb, 0x02ce, 0x02c4, 0x82c1,
0x8243, 0x0246, 0x024c, 0x8249, 0x0258, 0x825d, 0x8257, 0x0252,
0x0270, 0x8275, 0x827f, 0x027a, 0x826b, 0x026e, 0x0264, 0x8261,
0x0220, 0x8225, 0x822f, 0x022a, 0x823b, 0x023e, 0x0234, 0x8231,
0x8213, 0x0216, 0x021c, 0x8219, 0x0208, 0x820d, 0x8207, 0x0202
};
unsigned short crc = init;
for (unsigned int i = 0; i < size; ++i)
crc = (crc << 8) ^ crc_ccitt_table[(crc >> 8) ^ data[i]];
return crc ^ xorout;
}
/*
* Polynomial: 0x8005
* RefIn: True
* RefOut: True
*/
unsigned short
checksum_crc16r_ansi (const unsigned char data[], unsigned int size, unsigned short init, unsigned short xorout)
{
static const unsigned short crc_ccitt_table[] = {
0x0000, 0xc0c1, 0xc181, 0x0140, 0xc301, 0x03c0, 0x0280, 0xc241,
0xc601, 0x06c0, 0x0780, 0xc741, 0x0500, 0xc5c1, 0xc481, 0x0440,
0xcc01, 0x0cc0, 0x0d80, 0xcd41, 0x0f00, 0xcfc1, 0xce81, 0x0e40,
0x0a00, 0xcac1, 0xcb81, 0x0b40, 0xc901, 0x09c0, 0x0880, 0xc841,
0xd801, 0x18c0, 0x1980, 0xd941, 0x1b00, 0xdbc1, 0xda81, 0x1a40,
0x1e00, 0xdec1, 0xdf81, 0x1f40, 0xdd01, 0x1dc0, 0x1c80, 0xdc41,
0x1400, 0xd4c1, 0xd581, 0x1540, 0xd701, 0x17c0, 0x1680, 0xd641,
0xd201, 0x12c0, 0x1380, 0xd341, 0x1100, 0xd1c1, 0xd081, 0x1040,
0xf001, 0x30c0, 0x3180, 0xf141, 0x3300, 0xf3c1, 0xf281, 0x3240,
0x3600, 0xf6c1, 0xf781, 0x3740, 0xf501, 0x35c0, 0x3480, 0xf441,
0x3c00, 0xfcc1, 0xfd81, 0x3d40, 0xff01, 0x3fc0, 0x3e80, 0xfe41,
0xfa01, 0x3ac0, 0x3b80, 0xfb41, 0x3900, 0xf9c1, 0xf881, 0x3840,
0x2800, 0xe8c1, 0xe981, 0x2940, 0xeb01, 0x2bc0, 0x2a80, 0xea41,
0xee01, 0x2ec0, 0x2f80, 0xef41, 0x2d00, 0xedc1, 0xec81, 0x2c40,
0xe401, 0x24c0, 0x2580, 0xe541, 0x2700, 0xe7c1, 0xe681, 0x2640,
0x2200, 0xe2c1, 0xe381, 0x2340, 0xe101, 0x21c0, 0x2080, 0xe041,
0xa001, 0x60c0, 0x6180, 0xa141, 0x6300, 0xa3c1, 0xa281, 0x6240,
0x6600, 0xa6c1, 0xa781, 0x6740, 0xa501, 0x65c0, 0x6480, 0xa441,
0x6c00, 0xacc1, 0xad81, 0x6d40, 0xaf01, 0x6fc0, 0x6e80, 0xae41,
0xaa01, 0x6ac0, 0x6b80, 0xab41, 0x6900, 0xa9c1, 0xa881, 0x6840,
0x7800, 0xb8c1, 0xb981, 0x7940, 0xbb01, 0x7bc0, 0x7a80, 0xba41,
0xbe01, 0x7ec0, 0x7f80, 0xbf41, 0x7d00, 0xbdc1, 0xbc81, 0x7c40,
0xb401, 0x74c0, 0x7580, 0xb541, 0x7700, 0xb7c1, 0xb681, 0x7640,
0x7200, 0xb2c1, 0xb381, 0x7340, 0xb101, 0x71c0, 0x7080, 0xb041,
0x5000, 0x90c1, 0x9181, 0x5140, 0x9301, 0x53c0, 0x5280, 0x9241,
0x9601, 0x56c0, 0x5780, 0x9741, 0x5500, 0x95c1, 0x9481, 0x5440,
0x9c01, 0x5cc0, 0x5d80, 0x9d41, 0x5f00, 0x9fc1, 0x9e81, 0x5e40,
0x5a00, 0x9ac1, 0x9b81, 0x5b40, 0x9901, 0x59c0, 0x5880, 0x9841,
0x8801, 0x48c0, 0x4980, 0x8941, 0x4b00, 0x8bc1, 0x8a81, 0x4a40,
0x4e00, 0x8ec1, 0x8f81, 0x4f40, 0x8d01, 0x4dc0, 0x4c80, 0x8c41,
0x4400, 0x84c1, 0x8581, 0x4540, 0x8701, 0x47c0, 0x4680, 0x8641,
0x8201, 0x42c0, 0x4380, 0x8341, 0x4100, 0x81c1, 0x8081, 0x4040
};
unsigned short crc = init;
for (unsigned int i = 0; i < size; ++i)
crc = (crc >> 8) ^ crc_ccitt_table[(crc ^ data[i]) & 0xff];
return crc ^ xorout;
}
/*
* Polynomial: 0x04C11DB7
* Init: 0xffffffff
* XorOut: 0xffffffff
* RefIn: True
* RefOut: True
*/
unsigned int unsigned int
checksum_crc32 (const unsigned char data[], unsigned int size) checksum_crc32r (const unsigned char data[], unsigned int size)
{ {
static const unsigned int crc_table[] = { static const unsigned int crc_table[] = {
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
@ -158,8 +322,16 @@ checksum_crc32 (const unsigned char data[], unsigned int size)
return crc ^ 0xffffffff; return crc ^ 0xffffffff;
} }
/*
* Polynomial: 0x04C11DB7
* Init: 0xffffffff
* XorOut: 0xffffffff
* RefIn: False
* RefOut: False
*/
unsigned int unsigned int
checksum_crc32b (const unsigned char data[], unsigned int size) checksum_crc32 (const unsigned char data[], unsigned int size)
{ {
static const unsigned int crc_table[] = { static const unsigned int crc_table[] = {
0x00000000, 0x04C11DB7, 0x09823B6E, 0x0D4326D9, 0x130476DC, 0x17C56B6B, 0x1A864DB2, 0x1E475005, 0x00000000, 0x04C11DB7, 0x09823B6E, 0x0D4326D9, 0x130476DC, 0x17C56B6B, 0x1A864DB2, 0x1E475005,

View File

@ -39,14 +39,23 @@ unsigned char
checksum_xor_uint8 (const unsigned char data[], unsigned int size, unsigned char init); checksum_xor_uint8 (const unsigned char data[], unsigned int size, unsigned char init);
unsigned short unsigned short
checksum_crc16_ccitt (const unsigned char data[], unsigned int size, unsigned short init); checksum_crc16_ccitt (const unsigned char data[], unsigned int size, unsigned short init, unsigned short xorout);
unsigned short
checksum_crc16r_ccitt (const unsigned char data[], unsigned int size, unsigned short init, unsigned short xorout);
unsigned short
checksum_crc16_ansi (const unsigned char data[], unsigned int size, unsigned short init, unsigned short xorout);
unsigned short
checksum_crc16r_ansi (const unsigned char data[], unsigned int size, unsigned short init, unsigned short xorout);
unsigned int
checksum_crc32r (const unsigned char data[], unsigned int size);
unsigned int unsigned int
checksum_crc32 (const unsigned char data[], unsigned int size); checksum_crc32 (const unsigned char data[], unsigned int size);
unsigned int
checksum_crc32b (const unsigned char data[], unsigned int size);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif /* __cplusplus */ #endif /* __cplusplus */

View File

@ -31,6 +31,8 @@
#define ISINSTANCE(device) dc_device_isinstance((device), &citizen_aqualand_device_vtable) #define ISINSTANCE(device) dc_device_isinstance((device), &citizen_aqualand_device_vtable)
#define SZ_HEADER 32
typedef struct citizen_aqualand_device_t { typedef struct citizen_aqualand_device_t {
dc_device_t base; dc_device_t base;
dc_iostream_t *iostream; dc_iostream_t *iostream;
@ -200,6 +202,12 @@ citizen_aqualand_device_foreach (dc_device_t *abstract, dc_dive_callback_t callb
unsigned char *data = dc_buffer_get_data (buffer); unsigned char *data = dc_buffer_get_data (buffer);
unsigned int size = dc_buffer_get_size (buffer); unsigned int size = dc_buffer_get_size (buffer);
if (size < SZ_HEADER) {
ERROR (abstract->context, "Dive header is too small (%u).", size);
dc_buffer_free (buffer);
return DC_STATUS_DATAFORMAT;
}
if (callback && memcmp (data + 0x05, device->fingerprint, sizeof (device->fingerprint)) != 0) { if (callback && memcmp (data + 0x05, device->fingerprint, sizeof (device->fingerprint)) != 0) {
callback (data, size, data + 0x05, sizeof (device->fingerprint), userdata); callback (data, size, data + 0x05, sizeof (device->fingerprint), userdata);
} }

View File

@ -35,7 +35,7 @@ dc_status_t
citizen_aqualand_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream); citizen_aqualand_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream);
dc_status_t dc_status_t
citizen_aqualand_parser_create (dc_parser_t **parser, dc_context_t *context); citizen_aqualand_parser_create (dc_parser_t **parser, dc_context_t *context, const unsigned char data[], size_t size);
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@ -36,7 +36,6 @@ typedef struct citizen_aqualand_parser_t {
dc_parser_t base; dc_parser_t base;
} citizen_aqualand_parser_t; } citizen_aqualand_parser_t;
static dc_status_t citizen_aqualand_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size);
static dc_status_t citizen_aqualand_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime); static dc_status_t citizen_aqualand_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime);
static dc_status_t citizen_aqualand_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value); static dc_status_t citizen_aqualand_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value);
static dc_status_t citizen_aqualand_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata); static dc_status_t citizen_aqualand_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata);
@ -44,7 +43,9 @@ static dc_status_t citizen_aqualand_parser_samples_foreach (dc_parser_t *abstrac
static const dc_parser_vtable_t citizen_aqualand_parser_vtable = { static const dc_parser_vtable_t citizen_aqualand_parser_vtable = {
sizeof(citizen_aqualand_parser_t), sizeof(citizen_aqualand_parser_t),
DC_FAMILY_CITIZEN_AQUALAND, DC_FAMILY_CITIZEN_AQUALAND,
citizen_aqualand_parser_set_data, /* set_data */ NULL, /* set_clock */
NULL, /* set_atmospheric */
NULL, /* set_density */
citizen_aqualand_parser_get_datetime, /* datetime */ citizen_aqualand_parser_get_datetime, /* datetime */
citizen_aqualand_parser_get_field, /* fields */ citizen_aqualand_parser_get_field, /* fields */
citizen_aqualand_parser_samples_foreach, /* samples_foreach */ citizen_aqualand_parser_samples_foreach, /* samples_foreach */
@ -53,7 +54,7 @@ static const dc_parser_vtable_t citizen_aqualand_parser_vtable = {
dc_status_t dc_status_t
citizen_aqualand_parser_create (dc_parser_t **out, dc_context_t *context) citizen_aqualand_parser_create (dc_parser_t **out, dc_context_t *context, const unsigned char data[], size_t size)
{ {
citizen_aqualand_parser_t *parser = NULL; citizen_aqualand_parser_t *parser = NULL;
@ -61,7 +62,7 @@ citizen_aqualand_parser_create (dc_parser_t **out, dc_context_t *context)
return DC_STATUS_INVALIDARGS; return DC_STATUS_INVALIDARGS;
// Allocate memory. // Allocate memory.
parser = (citizen_aqualand_parser_t *) dc_parser_allocate (context, &citizen_aqualand_parser_vtable); parser = (citizen_aqualand_parser_t *) dc_parser_allocate (context, &citizen_aqualand_parser_vtable, data, size);
if (parser == NULL) { if (parser == NULL) {
ERROR (context, "Failed to allocate memory."); ERROR (context, "Failed to allocate memory.");
return DC_STATUS_NOMEMORY; return DC_STATUS_NOMEMORY;
@ -73,13 +74,6 @@ citizen_aqualand_parser_create (dc_parser_t **out, dc_context_t *context)
} }
static dc_status_t
citizen_aqualand_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size)
{
return DC_STATUS_SUCCESS;
}
static dc_status_t static dc_status_t
citizen_aqualand_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime) citizen_aqualand_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime)
{ {
@ -238,15 +232,15 @@ citizen_aqualand_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callba
// Time // Time
time += interval; time += interval;
sample.time = time; sample.time = time * 1000;
if (callback) callback (DC_SAMPLE_TIME, sample, userdata); if (callback) callback (DC_SAMPLE_TIME, &sample, userdata);
// Depth // Depth
if (metric) if (metric)
sample.depth = depth / 10.0; sample.depth = depth / 10.0;
else else
sample.depth = depth * FEET; sample.depth = depth * FEET;
if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata); if (callback) callback (DC_SAMPLE_DEPTH, &sample, userdata);
// Temperature // Temperature
if (time % 300 == 0) { if (time % 300 == 0) {
@ -257,7 +251,7 @@ citizen_aqualand_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callba
sample.temperature = temperature / 10.0; sample.temperature = temperature / 10.0;
else else
sample.temperature = (temperature - 32.0) * (5.0 / 9.0); sample.temperature = (temperature - 32.0) * (5.0 / 9.0);
if (callback) callback (DC_SAMPLE_TEMPERATURE, sample, userdata); if (callback) callback (DC_SAMPLE_TEMPERATURE, &sample, userdata);
} }
} }
} }

View File

@ -30,8 +30,6 @@
#include "ringbuffer.h" #include "ringbuffer.h"
#include "rbstream.h" #include "rbstream.h"
#define C_ARRAY_SIZE(array) (sizeof (array) / sizeof *(array))
#define MAXRETRIES 2 #define MAXRETRIES 2
#define COCHRAN_MODEL_COMMANDER_TM 0 #define COCHRAN_MODEL_COMMANDER_TM 0
@ -605,7 +603,7 @@ cochran_commander_profile_size(cochran_commander_device_t *device, cochran_data_
// Corrupt dive, guess the end address // Corrupt dive, guess the end address
sample_end_address = cochran_commander_guess_sample_end_address(device, data, dive_num); sample_end_address = cochran_commander_guess_sample_end_address(device, data, dive_num);
return ringbuffer_distance(sample_start_address, sample_end_address, 0, device->layout->rb_profile_begin, device->layout->rb_profile_end); return ringbuffer_distance(sample_start_address, sample_end_address, DC_RINGBUFFER_EMPTY, device->layout->rb_profile_begin, device->layout->rb_profile_end);
} }
@ -967,7 +965,7 @@ cochran_commander_device_foreach (dc_device_t *abstract, dc_dive_callback_t call
last_start_address = base + array_uint32_le(data.config + layout->cf_last_log ); last_start_address = base + array_uint32_le(data.config + layout->cf_last_log );
// Create the ringbuffer stream. // Create the ringbuffer stream.
status = dc_rbstream_new (&rbstream, abstract, 1, layout->rbstream_size, layout->rb_profile_begin, layout->rb_profile_end, last_start_address); status = dc_rbstream_new (&rbstream, abstract, 1, layout->rbstream_size, layout->rb_profile_begin, layout->rb_profile_end, last_start_address, DC_RBSTREAM_BACKWARD);
if (status != DC_STATUS_SUCCESS) { if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to create the ringbuffer stream."); ERROR (abstract->context, "Failed to create the ringbuffer stream.");
goto error; goto error;

View File

@ -35,7 +35,7 @@ dc_status_t
cochran_commander_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream); cochran_commander_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream);
dc_status_t dc_status_t
cochran_commander_parser_create (dc_parser_t **parser, dc_context_t *context, unsigned int model); cochran_commander_parser_create (dc_parser_t **parser, dc_context_t *context, const unsigned char data[], size_t size, unsigned int model);
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@ -29,8 +29,6 @@
#include "parser-private.h" #include "parser-private.h"
#include "array.h" #include "array.h"
#define C_ARRAY_SIZE(array) (sizeof (array) / sizeof *(array))
#define COCHRAN_MODEL_COMMANDER_TM 0 #define COCHRAN_MODEL_COMMANDER_TM 0
#define COCHRAN_MODEL_COMMANDER_PRE21000 1 #define COCHRAN_MODEL_COMMANDER_PRE21000 1
#define COCHRAN_MODEL_COMMANDER_AIR_NITROX 2 #define COCHRAN_MODEL_COMMANDER_AIR_NITROX 2
@ -101,7 +99,6 @@ typedef struct cochran_commander_parser_t {
unsigned int nevents; unsigned int nevents;
} cochran_commander_parser_t ; } cochran_commander_parser_t ;
static dc_status_t cochran_commander_parser_set_data (dc_parser_t *parser, const unsigned char *data, unsigned int size);
static dc_status_t cochran_commander_parser_get_datetime (dc_parser_t *parser, dc_datetime_t *datetime); static dc_status_t cochran_commander_parser_get_datetime (dc_parser_t *parser, dc_datetime_t *datetime);
static dc_status_t cochran_commander_parser_get_field (dc_parser_t *parser, dc_field_type_t type, unsigned int flags, void *value); static dc_status_t cochran_commander_parser_get_field (dc_parser_t *parser, dc_field_type_t type, unsigned int flags, void *value);
static dc_status_t cochran_commander_parser_samples_foreach (dc_parser_t *parser, dc_sample_callback_t callback, void *userdata); static dc_status_t cochran_commander_parser_samples_foreach (dc_parser_t *parser, dc_sample_callback_t callback, void *userdata);
@ -109,7 +106,9 @@ static dc_status_t cochran_commander_parser_samples_foreach (dc_parser_t *parser
static const dc_parser_vtable_t cochran_commander_parser_vtable = { static const dc_parser_vtable_t cochran_commander_parser_vtable = {
sizeof(cochran_commander_parser_t), sizeof(cochran_commander_parser_t),
DC_FAMILY_COCHRAN_COMMANDER, DC_FAMILY_COCHRAN_COMMANDER,
cochran_commander_parser_set_data, /* set_data */ NULL, /* set_clock */
NULL, /* set_atmospheric */
NULL, /* set_density */
cochran_commander_parser_get_datetime, /* datetime */ cochran_commander_parser_get_datetime, /* datetime */
cochran_commander_parser_get_field, /* fields */ cochran_commander_parser_get_field, /* fields */
cochran_commander_parser_samples_foreach, /* samples_foreach */ cochran_commander_parser_samples_foreach, /* samples_foreach */
@ -314,7 +313,7 @@ cochran_commander_handle_event (cochran_commander_parser_t *parser, unsigned cha
sample.event.time = 0; sample.event.time = 0;
sample.event.value = 0; sample.event.value = 0;
sample.event.flags = event->flag; sample.event.flags = event->flag;
if (callback) callback (DC_SAMPLE_EVENT, sample, userdata); if (callback) callback (DC_SAMPLE_EVENT, &sample, userdata);
} }
} }
@ -351,7 +350,7 @@ cochran_commander_backparse(cochran_commander_parser_t *parser, const unsigned c
dc_status_t dc_status_t
cochran_commander_parser_create (dc_parser_t **out, dc_context_t *context, unsigned int model) cochran_commander_parser_create (dc_parser_t **out, dc_context_t *context, const unsigned char data[], size_t size, unsigned int model)
{ {
cochran_commander_parser_t *parser = NULL; cochran_commander_parser_t *parser = NULL;
dc_status_t status = DC_STATUS_SUCCESS; dc_status_t status = DC_STATUS_SUCCESS;
@ -360,7 +359,7 @@ cochran_commander_parser_create (dc_parser_t **out, dc_context_t *context, unsig
return DC_STATUS_INVALIDARGS; return DC_STATUS_INVALIDARGS;
// Allocate memory. // Allocate memory.
parser = (cochran_commander_parser_t *) dc_parser_allocate (context, &cochran_commander_parser_vtable); parser = (cochran_commander_parser_t *) dc_parser_allocate (context, &cochran_commander_parser_vtable, data, size);
if (parser == NULL) { if (parser == NULL) {
ERROR (context, "Failed to allocate memory."); ERROR (context, "Failed to allocate memory.");
return DC_STATUS_NOMEMORY; return DC_STATUS_NOMEMORY;
@ -406,13 +405,6 @@ error_free:
} }
static dc_status_t
cochran_commander_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size)
{
return DC_STATUS_SUCCESS;
}
static dc_status_t static dc_status_t
cochran_commander_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime) cochran_commander_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime)
{ {
@ -513,6 +505,7 @@ cochran_commander_parser_get_field (dc_parser_t *abstract, dc_field_type_t type,
// Gas percentages are decimal and encoded as // Gas percentages are decimal and encoded as
// highbyte = integer portion // highbyte = integer portion
// lowbyte = decimal portion, divide by 256 to get decimal value // lowbyte = decimal portion, divide by 256 to get decimal value
gasmix->usage = DC_USAGE_NONE;
gasmix->oxygen = array_uint16_le (data + layout->oxygen + 2 * flags) / 256.0 / 100; gasmix->oxygen = array_uint16_le (data + layout->oxygen + 2 * flags) / 256.0 / 100;
if (layout->helium == UNSUPPORTED) { if (layout->helium == UNSUPPORTED) {
gasmix->helium = 0; gasmix->helium = 0;
@ -577,26 +570,26 @@ cochran_commander_parser_samples_foreach_tm (dc_parser_t *abstract, dc_sample_ca
unsigned int temp = samples[0]; // Half degrees F unsigned int temp = samples[0]; // Half degrees F
unsigned int depth = samples[1]; // Half feet unsigned int depth = samples[1]; // Half feet
last_sample_time = sample.time = time; last_sample_time = sample.time = time * 1000;
if (callback) callback (DC_SAMPLE_TIME, sample, userdata); if (callback) callback (DC_SAMPLE_TIME, &sample, userdata);
sample.depth = (depth / 2.0) * FEET; sample.depth = (depth / 2.0) * FEET;
if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata); if (callback) callback (DC_SAMPLE_DEPTH, &sample, userdata);
sample.temperature = (temp / 2.0 - 32.0) / 1.8; sample.temperature = (temp / 2.0 - 32.0) / 1.8;
if (callback) callback (DC_SAMPLE_TEMPERATURE, sample, userdata); if (callback) callback (DC_SAMPLE_TEMPERATURE, &sample, userdata);
sample.gasmix = 0; sample.gasmix = 0;
if (callback) callback(DC_SAMPLE_GASMIX, sample, userdata); if (callback) callback(DC_SAMPLE_GASMIX, &sample, userdata);
while (offset < size) { while (offset < size) {
const unsigned char *s = samples + offset; const unsigned char *s = samples + offset;
sample.time = time; sample.time = time * 1000;
if (last_sample_time != sample.time) { if (last_sample_time != sample.time) {
// We haven't issued this time yet. // We haven't issued this time yet.
last_sample_time = sample.time; last_sample_time = sample.time;
if (callback) callback (DC_SAMPLE_TIME, sample, userdata); if (callback) callback (DC_SAMPLE_TIME, &sample, userdata);
} }
if (*s & 0x80) { if (*s & 0x80) {
@ -614,7 +607,8 @@ cochran_commander_parser_samples_foreach_tm (dc_parser_t *abstract, dc_sample_ca
sample.deco.type = DC_DECO_DECOSTOP; sample.deco.type = DC_DECO_DECOSTOP;
sample.deco.time = 60; // We don't know the duration sample.deco.time = 60; // We don't know the duration
sample.deco.depth = deco_ceiling * FEET; sample.deco.depth = deco_ceiling * FEET;
if (callback) callback(DC_SAMPLE_DECO, sample, userdata); sample.deco.tts = 0;
if (callback) callback(DC_SAMPLE_DECO, &sample, userdata);
break; break;
case 0xAD: // Increment ceiling (shallower) case 0xAD: // Increment ceiling (shallower)
deco_ceiling -= 10; // feet deco_ceiling -= 10; // feet
@ -622,7 +616,8 @@ cochran_commander_parser_samples_foreach_tm (dc_parser_t *abstract, dc_sample_ca
sample.deco.type = DC_DECO_DECOSTOP; sample.deco.type = DC_DECO_DECOSTOP;
sample.deco.depth = deco_ceiling * FEET; sample.deco.depth = deco_ceiling * FEET;
sample.deco.time = 60; // We don't know the duration sample.deco.time = 60; // We don't know the duration
if (callback) callback(DC_SAMPLE_DECO, sample, userdata); sample.deco.tts = 0;
if (callback) callback(DC_SAMPLE_DECO, &sample, userdata);
break; break;
default: default:
cochran_commander_handle_event(parser, s[0], callback, userdata); cochran_commander_handle_event(parser, s[0], callback, userdata);
@ -635,7 +630,7 @@ cochran_commander_parser_samples_foreach_tm (dc_parser_t *abstract, dc_sample_ca
else else
temp += (*s & 0x0f); temp += (*s & 0x0f);
sample.temperature = (temp / 2.0 - 32.0) / 1.8; sample.temperature = (temp / 2.0 - 32.0) / 1.8;
if (callback) callback (DC_SAMPLE_TEMPERATURE, sample, userdata); if (callback) callback (DC_SAMPLE_TEMPERATURE, &sample, userdata);
} }
offset++; offset++;
@ -649,7 +644,7 @@ cochran_commander_parser_samples_foreach_tm (dc_parser_t *abstract, dc_sample_ca
depth += s[0] & 0x3f; depth += s[0] & 0x3f;
sample.depth = (depth / 2.0) * FEET; sample.depth = (depth / 2.0) * FEET;
if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata); if (callback) callback (DC_SAMPLE_DEPTH, &sample, userdata);
offset++; offset++;
time += sample_interval; time += sample_interval;
@ -713,27 +708,27 @@ cochran_commander_parser_samples_foreach_emc (dc_parser_t *abstract, dc_sample_c
start_depth = array_uint16_le (data + layout->start_depth) / 256.0; start_depth = array_uint16_le (data + layout->start_depth) / 256.0;
} }
last_sample_time = sample.time = time; last_sample_time = sample.time = time * 1000;
if (callback) callback (DC_SAMPLE_TIME, sample, userdata); if (callback) callback (DC_SAMPLE_TIME, &sample, userdata);
sample.depth = start_depth * FEET; sample.depth = start_depth * FEET;
if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata); if (callback) callback (DC_SAMPLE_DEPTH, &sample, userdata);
sample.temperature = (data[layout->start_temp] - 32.0) / 1.8; sample.temperature = (data[layout->start_temp] - 32.0) / 1.8;
if (callback) callback (DC_SAMPLE_TEMPERATURE, sample, userdata); if (callback) callback (DC_SAMPLE_TEMPERATURE, &sample, userdata);
sample.gasmix = 0; sample.gasmix = 0;
if (callback) callback(DC_SAMPLE_GASMIX, sample, userdata); if (callback) callback(DC_SAMPLE_GASMIX, &sample, userdata);
unsigned int last_gasmix = sample.gasmix; unsigned int last_gasmix = sample.gasmix;
while (offset < size) { while (offset < size) {
const unsigned char *s = samples + offset; const unsigned char *s = samples + offset;
sample.time = time; sample.time = time * 1000;
if (last_sample_time != sample.time) { if (last_sample_time != sample.time) {
// We haven't issued this time yet. // We haven't issued this time yet.
last_sample_time = sample.time; last_sample_time = sample.time;
if (callback) callback (DC_SAMPLE_TIME, sample, userdata); if (callback) callback (DC_SAMPLE_TIME, &sample, userdata);
} }
// If corrupt_dive end before offset // If corrupt_dive end before offset
@ -773,7 +768,8 @@ cochran_commander_parser_samples_foreach_emc (dc_parser_t *abstract, dc_sample_c
sample.deco.type = DC_DECO_DECOSTOP; sample.deco.type = DC_DECO_DECOSTOP;
sample.deco.time = (array_uint16_le(s + 3) + 1) * 60; sample.deco.time = (array_uint16_le(s + 3) + 1) * 60;
sample.deco.depth = deco_ceiling * FEET; sample.deco.depth = deco_ceiling * FEET;
if (callback) callback(DC_SAMPLE_DECO, sample, userdata); sample.deco.tts = 0;
if (callback) callback(DC_SAMPLE_DECO, &sample, userdata);
break; break;
case 0xAD: // Increment ceiling (shallower) case 0xAD: // Increment ceiling (shallower)
deco_ceiling -= 10; // feet deco_ceiling -= 10; // feet
@ -781,7 +777,8 @@ cochran_commander_parser_samples_foreach_emc (dc_parser_t *abstract, dc_sample_c
sample.deco.type = DC_DECO_DECOSTOP; sample.deco.type = DC_DECO_DECOSTOP;
sample.deco.depth = deco_ceiling * FEET; sample.deco.depth = deco_ceiling * FEET;
sample.deco.time = (array_uint16_le(s + 3) + 1) * 60; sample.deco.time = (array_uint16_le(s + 3) + 1) * 60;
if (callback) callback(DC_SAMPLE_DECO, sample, userdata); sample.deco.tts = 0;
if (callback) callback(DC_SAMPLE_DECO, &sample, userdata);
break; break;
case 0xC0: // Switched to FO2 21% mode (surface) case 0xC0: // Switched to FO2 21% mode (surface)
// Event seen upon surfacing // Event seen upon surfacing
@ -790,14 +787,14 @@ cochran_commander_parser_samples_foreach_emc (dc_parser_t *abstract, dc_sample_c
case 0xEF: // Switched to gas blend 2 case 0xEF: // Switched to gas blend 2
if (last_gasmix != 1) { if (last_gasmix != 1) {
sample.gasmix = 1; sample.gasmix = 1;
if (callback) callback(DC_SAMPLE_GASMIX, sample, userdata); if (callback) callback(DC_SAMPLE_GASMIX, &sample, userdata);
last_gasmix = sample.gasmix; last_gasmix = sample.gasmix;
} }
break; break;
case 0xF3: // Switched to gas blend 1 case 0xF3: // Switched to gas blend 1
if (last_gasmix != 0) { if (last_gasmix != 0) {
sample.gasmix = 0; sample.gasmix = 0;
if (callback) callback(DC_SAMPLE_GASMIX, sample, userdata); if (callback) callback(DC_SAMPLE_GASMIX, &sample, userdata);
last_gasmix = sample.gasmix; last_gasmix = sample.gasmix;
} }
break; break;
@ -817,7 +814,7 @@ cochran_commander_parser_samples_foreach_emc (dc_parser_t *abstract, dc_sample_c
depth += (s[0] & 0x3f); depth += (s[0] & 0x3f);
sample.depth = (start_depth + depth / 4.0) * FEET; sample.depth = (start_depth + depth / 4.0) * FEET;
if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata); if (callback) callback (DC_SAMPLE_DEPTH, &sample, userdata);
// Ascent rate is logged in the 0th sample, temp in the 1st, repeat. // Ascent rate is logged in the 0th sample, temp in the 1st, repeat.
if (time % 2 == 0) { if (time % 2 == 0) {
@ -833,7 +830,7 @@ cochran_commander_parser_samples_foreach_emc (dc_parser_t *abstract, dc_sample_c
double temperature = s[1] / 2.0 + 20.0; double temperature = s[1] / 2.0 + 20.0;
sample.temperature = (temperature - 32.0) / 1.8; sample.temperature = (temperature - 32.0) / 1.8;
if (callback) callback (DC_SAMPLE_TEMPERATURE, sample, userdata); if (callback) callback (DC_SAMPLE_TEMPERATURE, &sample, userdata);
} }
// Cochran EMC models store NDL and deco stop time // Cochran EMC models store NDL and deco stop time
@ -854,7 +851,8 @@ cochran_commander_parser_samples_foreach_emc (dc_parser_t *abstract, dc_sample_c
sample.deco.type = DC_DECO_NDL; sample.deco.type = DC_DECO_NDL;
sample.deco.time = deco_time * 60; sample.deco.time = deco_time * 60;
sample.deco.depth = 0; sample.deco.depth = 0;
if (callback) callback (DC_SAMPLE_DECO, sample, userdata); sample.deco.tts = 0;
if (callback) callback (DC_SAMPLE_DECO, &sample, userdata);
} }
break; break;
case 23: case 23:
@ -864,7 +862,8 @@ cochran_commander_parser_samples_foreach_emc (dc_parser_t *abstract, dc_sample_c
sample.deco.type = DC_DECO_DECOSTOP; sample.deco.type = DC_DECO_DECOSTOP;
sample.deco.depth = deco_ceiling * FEET; sample.deco.depth = deco_ceiling * FEET;
sample.deco.time = deco_time * 60; sample.deco.time = deco_time * 60;
if (callback) callback (DC_SAMPLE_DECO, sample, userdata); sample.deco.tts = 0;
if (callback) callback (DC_SAMPLE_DECO, &sample, userdata);
} }
break; break;
} }

View File

@ -28,6 +28,8 @@
#include <libdivecomputer/context.h> #include <libdivecomputer/context.h>
#include "platform.h"
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif /* __cplusplus */ #endif /* __cplusplus */
@ -40,12 +42,6 @@ extern "C" {
#define FUNCTION __FUNCTION__ #define FUNCTION __FUNCTION__
#endif #endif
#if defined(__GNUC__)
#define ATTR_FORMAT_PRINTF(a,b) __attribute__((format(printf, a, b)))
#else
#define ATTR_FORMAT_PRINTF(a,b)
#endif
#ifdef ENABLE_LOGGING #ifdef ENABLE_LOGGING
#define HEXDUMP(context, loglevel, prefix, data, size) dc_context_hexdump (context, loglevel, __FILE__, __LINE__, FUNCTION, prefix, data, size) #define HEXDUMP(context, loglevel, prefix, data, size) dc_context_hexdump (context, loglevel, __FILE__, __LINE__, FUNCTION, prefix, data, size)
#define SYSERROR(context, errcode) dc_context_syserror (context, DC_LOGLEVEL_ERROR, __FILE__, __LINE__, FUNCTION, errcode) #define SYSERROR(context, errcode) dc_context_syserror (context, DC_LOGLEVEL_ERROR, __FILE__, __LINE__, FUNCTION, errcode)
@ -63,7 +59,7 @@ extern "C" {
#endif #endif
dc_status_t dc_status_t
dc_context_log (dc_context_t *context, dc_loglevel_t loglevel, const char *file, unsigned int line, const char *function, const char *format, ...) ATTR_FORMAT_PRINTF(6, 7); dc_context_log (dc_context_t *context, dc_loglevel_t loglevel, const char *file, unsigned int line, const char *function, const char *format, ...) DC_ATTR_FORMAT_PRINTF(6, 7);
dc_status_t dc_status_t
dc_context_syserror (dc_context_t *context, dc_loglevel_t loglevel, const char *file, unsigned int line, const char *function, int errcode); dc_context_syserror (dc_context_t *context, dc_loglevel_t loglevel, const char *file, unsigned int line, const char *function, int errcode);

View File

@ -25,7 +25,6 @@
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
#include <stdarg.h>
#include <string.h> #include <string.h>
#include <limits.h> #include <limits.h>
@ -36,6 +35,7 @@
#endif #endif
#include "context-private.h" #include "context-private.h"
#include "platform.h"
#include "timer.h" #include "timer.h"
struct dc_context_t { struct dc_context_t {
@ -49,55 +49,6 @@ struct dc_context_t {
}; };
#ifdef ENABLE_LOGGING #ifdef ENABLE_LOGGING
/*
* A wrapper for the vsnprintf function, which will always null terminate the
* string and returns a negative value if the destination buffer is too small.
*/
static int
l_vsnprintf (char *str, size_t size, const char *format, va_list ap)
{
int n;
if (size == 0)
return -1;
#ifdef _MSC_VER
/*
* The non-standard vsnprintf implementation provided by MSVC doesn't null
* terminate the string and returns a negative value if the destination
* buffer is too small.
*/
n = _vsnprintf (str, size - 1, format, ap);
if (n == size - 1 || n < 0)
str[size - 1] = 0;
#else
/*
* The C99 vsnprintf function will always null terminate the string. If the
* destination buffer is too small, the return value is the number of
* characters that would have been written if the buffer had been large
* enough.
*/
n = vsnprintf (str, size, format, ap);
if (n >= 0 && (size_t) n >= size)
n = -1;
#endif
return n;
}
static int
l_snprintf (char *str, size_t size, const char *format, ...)
{
va_list ap;
int n;
va_start (ap, format);
n = l_vsnprintf (str, size, format, ap);
va_end (ap);
return n;
}
static int static int
l_hexdump (char *str, size_t size, const unsigned char data[], size_t n) l_hexdump (char *str, size_t size, const unsigned char data[], size_t n)
{ {
@ -244,7 +195,7 @@ dc_context_log (dc_context_t *context, dc_loglevel_t loglevel, const char *file,
return DC_STATUS_SUCCESS; return DC_STATUS_SUCCESS;
va_start (ap, format); va_start (ap, format);
l_vsnprintf (context->msg, sizeof (context->msg), format, ap); dc_platform_vsnprintf (context->msg, sizeof (context->msg), format, ap);
va_end (ap); va_end (ap);
context->logfunc (context, loglevel, file, line, function, context->msg, context->userdata); context->logfunc (context, loglevel, file, line, function, context->msg, context->userdata);
@ -310,7 +261,7 @@ dc_context_hexdump (dc_context_t *context, dc_loglevel_t loglevel, const char *f
if (context->logfunc == NULL) if (context->logfunc == NULL)
return DC_STATUS_SUCCESS; return DC_STATUS_SUCCESS;
n = l_snprintf (context->msg, sizeof (context->msg), "%s: size=%u, data=", prefix, size); n = dc_platform_snprintf (context->msg, sizeof (context->msg), "%s: size=%u, data=", prefix, size);
if (n >= 0) { if (n >= 0) {
n = l_hexdump (context->msg + n, sizeof (context->msg) - n, data, size); n = l_hexdump (context->msg + n, sizeof (context->msg) - n, data, size);

View File

@ -38,6 +38,8 @@
#define SZ_PACKET 0x80 #define SZ_PACKET 0x80
#define SZ_PAGE (SZ_PACKET / 4) #define SZ_PAGE (SZ_PACKET / 4)
#define SZ_HEADER 32
#define IQ700 0x05 #define IQ700 0x05
#define EDY 0x08 #define EDY 0x08
@ -379,7 +381,14 @@ cressi_edy_device_dump (dc_device_t *abstract, dc_buffer_t *buffer)
return DC_STATUS_NOMEMORY; return DC_STATUS_NOMEMORY;
} }
return device_dump_read (abstract, dc_buffer_get_data (buffer), // Emit a device info event.
dc_event_devinfo_t devinfo;
devinfo.model = device->model;
devinfo.firmware = 0;
devinfo.serial = 0;
device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo);
return device_dump_read (abstract, 0, dc_buffer_get_data (buffer),
dc_buffer_get_size (buffer), SZ_PACKET); dc_buffer_get_size (buffer), SZ_PACKET);
} }
@ -423,7 +432,7 @@ cressi_edy_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, v
} }
// Get the number of logbook items. // Get the number of logbook items.
unsigned int count = ringbuffer_distance (first, last, 0, layout->rb_logbook_begin, layout->rb_logbook_end) + 1; unsigned int count = ringbuffer_distance (first, last, DC_RINGBUFFER_EMPTY, layout->rb_logbook_begin, layout->rb_logbook_end) + 1;
// Get the profile pointer. // Get the profile pointer.
unsigned int eop = array_uint_le (logbook + layout->config + 2, layout->rb_logbook_size) * SZ_PAGE + layout->rb_profile_begin; unsigned int eop = array_uint_le (logbook + layout->config + 2, layout->rb_logbook_size) * SZ_PAGE + layout->rb_profile_begin;
@ -448,7 +457,7 @@ cressi_edy_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, v
} }
// Get the profile length. // Get the profile length.
unsigned int length = ringbuffer_distance (current, previous, 1, layout->rb_profile_begin, layout->rb_profile_end); unsigned int length = ringbuffer_distance (current, previous, DC_RINGBUFFER_FULL, layout->rb_profile_begin, layout->rb_profile_end);
// Check for a ringbuffer overflow. // Check for a ringbuffer overflow.
if (total + length > layout->rb_profile_end - layout->rb_profile_begin) { if (total + length > layout->rb_profile_end - layout->rb_profile_begin) {
@ -472,7 +481,7 @@ cressi_edy_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, v
// Create the ringbuffer stream. // Create the ringbuffer stream.
dc_rbstream_t *rbstream = NULL; dc_rbstream_t *rbstream = NULL;
rc = dc_rbstream_new (&rbstream, abstract, SZ_PAGE, SZ_PACKET, layout->rb_profile_begin, layout->rb_profile_end, eop); rc = dc_rbstream_new (&rbstream, abstract, SZ_PAGE, SZ_PACKET, layout->rb_profile_begin, layout->rb_profile_end, eop, DC_RBSTREAM_BACKWARD);
if (rc != DC_STATUS_SUCCESS) { if (rc != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to create the ringbuffer stream."); ERROR (abstract->context, "Failed to create the ringbuffer stream.");
return rc; return rc;
@ -501,7 +510,7 @@ cressi_edy_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, v
} }
// Get the profile length. // Get the profile length.
unsigned int length = ringbuffer_distance (current, previous, 1, layout->rb_profile_begin, layout->rb_profile_end); unsigned int length = ringbuffer_distance (current, previous, DC_RINGBUFFER_FULL, layout->rb_profile_begin, layout->rb_profile_end);
// Move to the begin of the current dive. // Move to the begin of the current dive.
offset -= length; offset -= length;
@ -515,6 +524,13 @@ cressi_edy_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, v
return rc; return rc;
} }
if (length < SZ_HEADER) {
ERROR (abstract->context, "Dive header is too small (%u).", length);
dc_rbstream_free (rbstream);
free (buffer);
return DC_STATUS_DATAFORMAT;
}
unsigned char *p = buffer + offset; unsigned char *p = buffer + offset;
if (memcmp (p, device->fingerprint, sizeof (device->fingerprint)) == 0) if (memcmp (p, device->fingerprint, sizeof (device->fingerprint)) == 0)

View File

@ -35,7 +35,7 @@ dc_status_t
cressi_edy_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream); cressi_edy_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream);
dc_status_t dc_status_t
cressi_edy_parser_create (dc_parser_t **parser, dc_context_t *context, unsigned int model); cressi_edy_parser_create (dc_parser_t **parser, dc_context_t *context, const unsigned char data[], size_t size, unsigned int model);
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@ -31,6 +31,8 @@
#define IQ700 0x05 #define IQ700 0x05
#define EDY 0x08 #define EDY 0x08
#define SZ_HEADER 32
typedef struct cressi_edy_parser_t cressi_edy_parser_t; typedef struct cressi_edy_parser_t cressi_edy_parser_t;
struct cressi_edy_parser_t { struct cressi_edy_parser_t {
@ -38,7 +40,6 @@ struct cressi_edy_parser_t {
unsigned int model; unsigned int model;
}; };
static dc_status_t cressi_edy_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size);
static dc_status_t cressi_edy_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime); static dc_status_t cressi_edy_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime);
static dc_status_t cressi_edy_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value); static dc_status_t cressi_edy_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value);
static dc_status_t cressi_edy_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata); static dc_status_t cressi_edy_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata);
@ -46,7 +47,9 @@ static dc_status_t cressi_edy_parser_samples_foreach (dc_parser_t *abstract, dc_
static const dc_parser_vtable_t cressi_edy_parser_vtable = { static const dc_parser_vtable_t cressi_edy_parser_vtable = {
sizeof(cressi_edy_parser_t), sizeof(cressi_edy_parser_t),
DC_FAMILY_CRESSI_EDY, DC_FAMILY_CRESSI_EDY,
cressi_edy_parser_set_data, /* set_data */ NULL, /* set_clock */
NULL, /* set_atmospheric */
NULL, /* set_density */
cressi_edy_parser_get_datetime, /* datetime */ cressi_edy_parser_get_datetime, /* datetime */
cressi_edy_parser_get_field, /* fields */ cressi_edy_parser_get_field, /* fields */
cressi_edy_parser_samples_foreach, /* samples_foreach */ cressi_edy_parser_samples_foreach, /* samples_foreach */
@ -70,7 +73,7 @@ cressi_edy_parser_count_gasmixes (const unsigned char *data)
} }
dc_status_t dc_status_t
cressi_edy_parser_create (dc_parser_t **out, dc_context_t *context, unsigned int model) cressi_edy_parser_create (dc_parser_t **out, dc_context_t *context, const unsigned char data[], size_t size, unsigned int model)
{ {
cressi_edy_parser_t *parser = NULL; cressi_edy_parser_t *parser = NULL;
@ -78,7 +81,7 @@ cressi_edy_parser_create (dc_parser_t **out, dc_context_t *context, unsigned int
return DC_STATUS_INVALIDARGS; return DC_STATUS_INVALIDARGS;
// Allocate memory. // Allocate memory.
parser = (cressi_edy_parser_t *) dc_parser_allocate (context, &cressi_edy_parser_vtable); parser = (cressi_edy_parser_t *) dc_parser_allocate (context, &cressi_edy_parser_vtable, data, size);
if (parser == NULL) { if (parser == NULL) {
ERROR (context, "Failed to allocate memory."); ERROR (context, "Failed to allocate memory.");
return DC_STATUS_NOMEMORY; return DC_STATUS_NOMEMORY;
@ -93,17 +96,10 @@ cressi_edy_parser_create (dc_parser_t **out, dc_context_t *context, unsigned int
} }
static dc_status_t
cressi_edy_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size)
{
return DC_STATUS_SUCCESS;
}
static dc_status_t static dc_status_t
cressi_edy_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime) cressi_edy_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime)
{ {
if (abstract->size < 32) if (abstract->size < SZ_HEADER)
return DC_STATUS_DATAFORMAT; return DC_STATUS_DATAFORMAT;
const unsigned char *p = abstract->data; const unsigned char *p = abstract->data;
@ -127,7 +123,7 @@ cressi_edy_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsign
{ {
cressi_edy_parser_t *parser = (cressi_edy_parser_t *) abstract; cressi_edy_parser_t *parser = (cressi_edy_parser_t *) abstract;
if (abstract->size < 32) if (abstract->size < SZ_HEADER)
return DC_STATUS_DATAFORMAT; return DC_STATUS_DATAFORMAT;
const unsigned char *p = abstract->data; const unsigned char *p = abstract->data;
@ -149,6 +145,7 @@ cressi_edy_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsign
*((unsigned int *) value) = cressi_edy_parser_count_gasmixes(p); *((unsigned int *) value) = cressi_edy_parser_count_gasmixes(p);
break; break;
case DC_FIELD_GASMIX: case DC_FIELD_GASMIX:
gasmix->usage = DC_USAGE_NONE;
gasmix->helium = 0.0; gasmix->helium = 0.0;
gasmix->oxygen = bcd2dec (p[0x17 - flags]) / 100.0; gasmix->oxygen = bcd2dec (p[0x17 - flags]) / 100.0;
gasmix->nitrogen = 1.0 - gasmix->oxygen - gasmix->helium; gasmix->nitrogen = 1.0 - gasmix->oxygen - gasmix->helium;
@ -185,7 +182,7 @@ cressi_edy_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t c
unsigned int ngasmixes = cressi_edy_parser_count_gasmixes(data); unsigned int ngasmixes = cressi_edy_parser_count_gasmixes(data);
unsigned int gasmix = 0xFFFFFFFF; unsigned int gasmix = 0xFFFFFFFF;
unsigned int offset = 32; unsigned int offset = SZ_HEADER;
while (offset + 2 <= size) { while (offset + 2 <= size) {
dc_sample_value_t sample = {0}; dc_sample_value_t sample = {0};
@ -198,13 +195,13 @@ cressi_edy_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t c
// Time (seconds). // Time (seconds).
time += interval; time += interval;
sample.time = time; sample.time = time * 1000;
if (callback) callback (DC_SAMPLE_TIME, sample, userdata); if (callback) callback (DC_SAMPLE_TIME, &sample, userdata);
// Depth (1/10 m). // Depth (1/10 m).
unsigned int depth = bcd2dec (data[offset + 0] & 0x0F) * 100 + bcd2dec (data[offset + 1]); unsigned int depth = bcd2dec (data[offset + 0] & 0x0F) * 100 + bcd2dec (data[offset + 1]);
sample.depth = depth / 10.0; sample.depth = depth / 10.0;
if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata); if (callback) callback (DC_SAMPLE_DEPTH, &sample, userdata);
// Current gasmix // Current gasmix
if (ngasmixes) { if (ngasmixes) {
@ -217,7 +214,7 @@ cressi_edy_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t c
} }
if (idx != gasmix) { if (idx != gasmix) {
sample.gasmix = idx; sample.gasmix = idx;
if (callback) callback (DC_SAMPLE_GASMIX, sample, userdata); if (callback) callback (DC_SAMPLE_GASMIX, &sample, userdata);
gasmix = idx; gasmix = idx;
} }
} }

View File

@ -93,7 +93,7 @@ cressi_goa_device_send (cressi_goa_device_t *device, unsigned char cmd, const un
if (size) { if (size) {
memcpy (packet + 5, data, size); memcpy (packet + 5, data, size);
} }
crc = checksum_crc16_ccitt (packet + 3, size + 2, 0x000); crc = checksum_crc16_ccitt (packet + 3, size + 2, 0x000, 0x0000);
packet[5 + size + 0] = (crc ) & 0xFF; // Low packet[5 + size + 0] = (crc ) & 0xFF; // Low
packet[5 + size + 1] = (crc >> 8) & 0xFF; // High packet[5 + size + 1] = (crc >> 8) & 0xFF; // High
packet[5 + size + 2] = TRAILER; packet[5 + size + 2] = TRAILER;
@ -155,7 +155,7 @@ cressi_goa_device_receive (cressi_goa_device_t *device, unsigned char data[], un
// Verify the checksum of the packet. // Verify the checksum of the packet.
unsigned short crc = array_uint16_le (packet + length + 5); unsigned short crc = array_uint16_le (packet + length + 5);
unsigned short ccrc = checksum_crc16_ccitt (packet + 3, length + 2, 0x0000); unsigned short ccrc = checksum_crc16_ccitt (packet + 3, length + 2, 0x0000, 0x0000);
if (crc != ccrc) { if (crc != ccrc) {
ERROR (abstract->context, "Unexpected answer checksum."); ERROR (abstract->context, "Unexpected answer checksum.");
return DC_STATUS_PROTOCOL; return DC_STATUS_PROTOCOL;
@ -203,7 +203,7 @@ cressi_goa_device_download (cressi_goa_device_t *device, dc_buffer_t *buffer, dc
// Verify the checksum of the packet. // Verify the checksum of the packet.
unsigned short crc = array_uint16_le (packet + sizeof(packet) - 2); unsigned short crc = array_uint16_le (packet + sizeof(packet) - 2);
unsigned short ccrc = checksum_crc16_ccitt (packet + 3, sizeof(packet) - 5, 0x0000); unsigned short ccrc = checksum_crc16_ccitt (packet + 3, sizeof(packet) - 5, 0x0000, 0x0000);
if (crc != ccrc) { if (crc != ccrc) {
ERROR (abstract->context, "Unexpected answer checksum."); ERROR (abstract->context, "Unexpected answer checksum.");
return DC_STATUS_PROTOCOL; return DC_STATUS_PROTOCOL;

View File

@ -35,7 +35,7 @@ dc_status_t
cressi_goa_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream); cressi_goa_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream);
dc_status_t dc_status_t
cressi_goa_parser_create (dc_parser_t **parser, dc_context_t *context, unsigned int model); cressi_goa_parser_create (dc_parser_t **parser, dc_context_t *context, const unsigned char data[], size_t size, unsigned int model);
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@ -28,12 +28,7 @@
#define ISINSTANCE(parser) dc_device_isinstance((parser), &cressi_goa_parser_vtable) #define ISINSTANCE(parser) dc_device_isinstance((parser), &cressi_goa_parser_vtable)
#define C_ARRAY_SIZE(array) (sizeof (array) / sizeof *(array))
#define SZ_HEADER 23 #define SZ_HEADER 23
#define SZ_HEADER_SCUBA 0x61
#define SZ_HEADER_FREEDIVE 0x2B
#define SZ_HEADER_GAUGE 0x2D
#define DEPTH 0 #define DEPTH 0
#define DEPTH2 1 #define DEPTH2 1
@ -45,17 +40,28 @@
#define FREEDIVE 2 #define FREEDIVE 2
#define GAUGE 3 #define GAUGE 3
#define NGASMIXES 2
#define UNDEFINED 0xFFFFFFFF
typedef struct cressi_goa_parser_t cressi_goa_parser_t; typedef struct cressi_goa_parser_t cressi_goa_parser_t;
struct cressi_goa_parser_t { struct cressi_goa_parser_t {
dc_parser_t base; dc_parser_t base;
unsigned int model; unsigned int model;
// Cached fields.
unsigned int cached;
double maxdepth;
}; };
static dc_status_t cressi_goa_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size); typedef struct cressi_goa_layout_t {
unsigned int headersize;
unsigned int datetime;
unsigned int divetime;
unsigned int gasmix;
unsigned int atmospheric;
unsigned int maxdepth;
unsigned int avgdepth;
unsigned int temperature;
} cressi_goa_layout_t;
static dc_status_t cressi_goa_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime); static dc_status_t cressi_goa_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime);
static dc_status_t cressi_goa_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value); static dc_status_t cressi_goa_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value);
static dc_status_t cressi_goa_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata); static dc_status_t cressi_goa_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata);
@ -63,22 +69,64 @@ static dc_status_t cressi_goa_parser_samples_foreach (dc_parser_t *abstract, dc_
static const dc_parser_vtable_t cressi_goa_parser_vtable = { static const dc_parser_vtable_t cressi_goa_parser_vtable = {
sizeof(cressi_goa_parser_t), sizeof(cressi_goa_parser_t),
DC_FAMILY_CRESSI_GOA, DC_FAMILY_CRESSI_GOA,
cressi_goa_parser_set_data, /* set_data */ NULL, /* set_clock */
NULL, /* set_atmospheric */
NULL, /* set_density */
cressi_goa_parser_get_datetime, /* datetime */ cressi_goa_parser_get_datetime, /* datetime */
cressi_goa_parser_get_field, /* fields */ cressi_goa_parser_get_field, /* fields */
cressi_goa_parser_samples_foreach, /* samples_foreach */ cressi_goa_parser_samples_foreach, /* samples_foreach */
NULL /* destroy */ NULL /* destroy */
}; };
static const unsigned int headersizes[] = { static const cressi_goa_layout_t layouts[] = {
SZ_HEADER_SCUBA, /* SCUBA */
SZ_HEADER_SCUBA, {
SZ_HEADER_FREEDIVE, 0x61, /* headersize */
SZ_HEADER_GAUGE, 0x11, /* datetime */
0x19, /* divetime */
0x1F, /* gasmix */
0x23, /* atmospheric */
0x4E, /* maxdepth */
0x50, /* avgdepth */
0x52, /* temperature */
},
/* NITROX */
{
0x61, /* headersize */
0x11, /* datetime */
0x19, /* divetime */
0x1F, /* gasmix */
0x23, /* atmospheric */
0x4E, /* maxdepth */
0x50, /* avgdepth */
0x52, /* temperature */
},
/* FREEDIVE */
{
0x2B, /* headersize */
0x11, /* datetime */
0x19, /* divetime */
UNDEFINED, /* gasmix */
UNDEFINED, /* atmospheric */
0x1C, /* maxdepth */
UNDEFINED, /* avgdepth */
0x1E, /* temperature */
},
/* GAUGE */
{
0x2D, /* headersize */
0x11, /* datetime */
0x19, /* divetime */
UNDEFINED, /* gasmix */
0x1B, /* atmospheric */
0x1D, /* maxdepth */
0x1F, /* avgdepth */
0x21, /* temperature */
},
}; };
dc_status_t dc_status_t
cressi_goa_parser_create (dc_parser_t **out, dc_context_t *context, unsigned int model) cressi_goa_parser_create (dc_parser_t **out, dc_context_t *context, const unsigned char data[], size_t size, unsigned int model)
{ {
cressi_goa_parser_t *parser = NULL; cressi_goa_parser_t *parser = NULL;
@ -86,33 +134,19 @@ cressi_goa_parser_create (dc_parser_t **out, dc_context_t *context, unsigned int
return DC_STATUS_INVALIDARGS; return DC_STATUS_INVALIDARGS;
// Allocate memory. // Allocate memory.
parser = (cressi_goa_parser_t *) dc_parser_allocate (context, &cressi_goa_parser_vtable); parser = (cressi_goa_parser_t *) dc_parser_allocate (context, &cressi_goa_parser_vtable, data, size);
if (parser == NULL) { if (parser == NULL) {
ERROR (context, "Failed to allocate memory."); ERROR (context, "Failed to allocate memory.");
return DC_STATUS_NOMEMORY; return DC_STATUS_NOMEMORY;
} }
parser->model = model; parser->model = model;
parser->cached = 0;
parser->maxdepth = 0.0;
*out = (dc_parser_t*) parser; *out = (dc_parser_t*) parser;
return DC_STATUS_SUCCESS; return DC_STATUS_SUCCESS;
} }
static dc_status_t
cressi_goa_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size)
{
cressi_goa_parser_t *parser = (cressi_goa_parser_t *) abstract;
// Reset the cache.
parser->cached = 0;
parser->maxdepth = 0.0;
return DC_STATUS_SUCCESS;
}
static dc_status_t static dc_status_t
cressi_goa_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime) cressi_goa_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime)
{ {
@ -123,15 +157,16 @@ cressi_goa_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime)
return DC_STATUS_DATAFORMAT; return DC_STATUS_DATAFORMAT;
unsigned int divemode = data[2]; unsigned int divemode = data[2];
if (divemode >= C_ARRAY_SIZE(headersizes)) { if (divemode >= C_ARRAY_SIZE(layouts)) {
return DC_STATUS_DATAFORMAT; return DC_STATUS_DATAFORMAT;
} }
unsigned int headersize = headersizes[divemode]; const cressi_goa_layout_t *layout = &layouts[divemode];
if (size < headersize)
if (size < layout->headersize)
return DC_STATUS_DATAFORMAT; return DC_STATUS_DATAFORMAT;
const unsigned char *p = abstract->data + 0x11; const unsigned char *p = abstract->data + layout->datetime;
if (datetime) { if (datetime) {
datetime->year = array_uint16_le(p); datetime->year = array_uint16_le(p);
@ -149,7 +184,6 @@ cressi_goa_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime)
static dc_status_t static dc_status_t
cressi_goa_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value) cressi_goa_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value)
{ {
cressi_goa_parser_t *parser = (cressi_goa_parser_t *) abstract;
const unsigned char *data = abstract->data; const unsigned char *data = abstract->data;
unsigned int size = abstract->size; unsigned int size = abstract->size;
@ -157,23 +191,22 @@ cressi_goa_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsign
return DC_STATUS_DATAFORMAT; return DC_STATUS_DATAFORMAT;
unsigned int divemode = data[2]; unsigned int divemode = data[2];
if (divemode >= C_ARRAY_SIZE(headersizes)) { if (divemode >= C_ARRAY_SIZE(layouts)) {
return DC_STATUS_DATAFORMAT; return DC_STATUS_DATAFORMAT;
} }
unsigned int headersize = headersizes[divemode]; const cressi_goa_layout_t *layout = &layouts[divemode];
if (size < headersize)
if (size < layout->headersize)
return DC_STATUS_DATAFORMAT; return DC_STATUS_DATAFORMAT;
if (!parser->cached) { unsigned int ngasmixes = 0;
sample_statistics_t statistics = SAMPLE_STATISTICS_INITIALIZER; if (layout->gasmix != UNDEFINED) {
dc_status_t rc = cressi_goa_parser_samples_foreach ( for (unsigned int i = 0; i < NGASMIXES; ++i) {
abstract, sample_statistics_cb, &statistics); if (data[layout->gasmix + 2 * i + 1] == 0)
if (rc != DC_STATUS_SUCCESS) break;
return rc; ngasmixes++;
}
parser->cached = 1;
parser->maxdepth = statistics.maxdepth;
} }
dc_gasmix_t *gasmix = (dc_gasmix_t *) value; dc_gasmix_t *gasmix = (dc_gasmix_t *) value;
@ -181,17 +214,37 @@ cressi_goa_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsign
if (value) { if (value) {
switch (type) { switch (type) {
case DC_FIELD_DIVETIME: case DC_FIELD_DIVETIME:
*((unsigned int *) value) = array_uint16_le (data + 0x19); if (layout->divetime == UNDEFINED)
return DC_STATUS_UNSUPPORTED;
*((unsigned int *) value) = array_uint16_le (data + layout->divetime);
break; break;
case DC_FIELD_MAXDEPTH: case DC_FIELD_MAXDEPTH:
*((double *) value) = parser->maxdepth; if (layout->maxdepth == UNDEFINED)
return DC_STATUS_UNSUPPORTED;
*((double *) value) = array_uint16_le (data + layout->maxdepth) / 10.0;
break;
case DC_FIELD_AVGDEPTH:
if (layout->avgdepth == UNDEFINED)
return DC_STATUS_UNSUPPORTED;
*((double *) value) = array_uint16_le (data + layout->avgdepth) / 10.0;
break;
case DC_FIELD_TEMPERATURE_MINIMUM:
if (layout->temperature == UNDEFINED)
return DC_STATUS_UNSUPPORTED;
*((double *) value) = array_uint16_le (data + layout->temperature) / 10.0;
break;
case DC_FIELD_ATMOSPHERIC:
if (layout->atmospheric == UNDEFINED)
return DC_STATUS_UNSUPPORTED;
*((double *) value) = array_uint16_le (data + layout->atmospheric) / 1000.0;
break; break;
case DC_FIELD_GASMIX_COUNT: case DC_FIELD_GASMIX_COUNT:
*((unsigned int *) value) = divemode == SCUBA || divemode == NITROX ? 2 : 0; *((unsigned int *) value) = ngasmixes;
break; break;
case DC_FIELD_GASMIX: case DC_FIELD_GASMIX:
gasmix->usage = DC_USAGE_NONE;
gasmix->helium = 0.0; gasmix->helium = 0.0;
gasmix->oxygen = data[0x20 + 2 * flags] / 100.0; gasmix->oxygen = data[layout->gasmix + 2 * flags + 1] / 100.0;
gasmix->nitrogen = 1.0 - gasmix->oxygen - gasmix->helium; gasmix->nitrogen = 1.0 - gasmix->oxygen - gasmix->helium;
break; break;
case DC_FIELD_DIVEMODE: case DC_FIELD_DIVEMODE:
@ -228,12 +281,13 @@ cressi_goa_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t c
return DC_STATUS_DATAFORMAT; return DC_STATUS_DATAFORMAT;
unsigned int divemode = data[2]; unsigned int divemode = data[2];
if (divemode >= C_ARRAY_SIZE(headersizes)) { if (divemode >= C_ARRAY_SIZE(layouts)) {
return DC_STATUS_DATAFORMAT; return DC_STATUS_DATAFORMAT;
} }
unsigned int headersize = headersizes[divemode]; const cressi_goa_layout_t *layout = &layouts[divemode];
if (size < headersize)
if (size < layout->headersize)
return DC_STATUS_DATAFORMAT; return DC_STATUS_DATAFORMAT;
unsigned int interval = divemode == FREEDIVE ? 2 : 5; unsigned int interval = divemode == FREEDIVE ? 2 : 5;
@ -245,7 +299,7 @@ cressi_goa_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t c
unsigned int have_temperature = 0; unsigned int have_temperature = 0;
unsigned int complete = 0; unsigned int complete = 0;
unsigned int offset = headersize; unsigned int offset = layout->headersize;
while (offset + 2 <= size) { while (offset + 2 <= size) {
dc_sample_value_t sample = {0}; dc_sample_value_t sample = {0};
@ -268,25 +322,25 @@ cressi_goa_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t c
if (complete) { if (complete) {
// Time (seconds). // Time (seconds).
sample.time = time; sample.time = time * 1000;
if (callback) callback (DC_SAMPLE_TIME, sample, userdata); if (callback) callback (DC_SAMPLE_TIME, &sample, userdata);
// Temperature (1/10 °C). // Temperature (1/10 °C).
if (have_temperature) { if (have_temperature) {
sample.temperature = temperature / 10.0; sample.temperature = temperature / 10.0;
if (callback) callback (DC_SAMPLE_TEMPERATURE, sample, userdata); if (callback) callback (DC_SAMPLE_TEMPERATURE, &sample, userdata);
have_temperature = 0; have_temperature = 0;
} }
// Depth (1/10 m). // Depth (1/10 m).
sample.depth = depth / 10.0; sample.depth = depth / 10.0;
if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata); if (callback) callback (DC_SAMPLE_DEPTH, &sample, userdata);
// Gas change // Gas change
if (divemode == SCUBA || divemode == NITROX) { if (divemode == SCUBA || divemode == NITROX) {
if (gasmix != gasmix_previous) { if (gasmix != gasmix_previous) {
sample.gasmix = gasmix; sample.gasmix = gasmix;
if (callback) callback (DC_SAMPLE_GASMIX, sample, userdata); if (callback) callback (DC_SAMPLE_GASMIX, &sample, userdata);
gasmix_previous = gasmix; gasmix_previous = gasmix;
} }
} }

View File

@ -41,7 +41,7 @@
#define RB_PROFILE_BEGIN 0x1438 #define RB_PROFILE_BEGIN 0x1438
#define RB_PROFILE_END SZ_MEMORY #define RB_PROFILE_END SZ_MEMORY
#define RB_PROFILE_DISTANCE(a,b) ringbuffer_distance (a, b, 0, RB_PROFILE_BEGIN, RB_PROFILE_END) #define RB_PROFILE_DISTANCE(a,b) ringbuffer_distance (a, b, DC_RINGBUFFER_EMPTY, RB_PROFILE_BEGIN, RB_PROFILE_END)
#define MAXRETRIES 4 #define MAXRETRIES 4
#define PACKETSIZE 32 #define PACKETSIZE 32
@ -84,7 +84,7 @@ cressi_leonardo_make_ascii (const unsigned char raw[], unsigned int rsize, unsig
array_convert_bin2hex (raw, rsize, ascii + 1, 2 * rsize); array_convert_bin2hex (raw, rsize, ascii + 1, 2 * rsize);
// Checksum // Checksum
unsigned short crc = checksum_crc16_ccitt (ascii + 1, 2 * rsize, 0xffff); unsigned short crc = checksum_crc16_ccitt (ascii + 1, 2 * rsize, 0xffff, 0x0000);
unsigned char checksum[] = { unsigned char checksum[] = {
(crc >> 8) & 0xFF, // High (crc >> 8) & 0xFF, // High
(crc ) & 0xFF}; // Low (crc ) & 0xFF}; // Low
@ -129,7 +129,7 @@ cressi_leonardo_packet (cressi_leonardo_device_t *device, const unsigned char co
// Verify the checksum of the packet. // Verify the checksum of the packet.
unsigned short crc = array_uint16_be (checksum); unsigned short crc = array_uint16_be (checksum);
unsigned short ccrc = checksum_crc16_ccitt (answer + 1, asize - 6, 0xffff); unsigned short ccrc = checksum_crc16_ccitt (answer + 1, asize - 6, 0xffff, 0x0000);
if (crc != ccrc) { if (crc != ccrc) {
ERROR (abstract->context, "Unexpected answer checksum."); ERROR (abstract->context, "Unexpected answer checksum.");
return DC_STATUS_PROTOCOL; return DC_STATUS_PROTOCOL;
@ -372,12 +372,19 @@ cressi_leonardo_device_dump (dc_device_t *abstract, dc_buffer_t *buffer)
// Verify the checksum. // Verify the checksum.
unsigned int csum1 = array_uint16_be (checksum); unsigned int csum1 = array_uint16_be (checksum);
unsigned int csum2 = checksum_crc16_ccitt (data, SZ_MEMORY, 0xffff); unsigned int csum2 = checksum_crc16_ccitt (data, SZ_MEMORY, 0xffff, 0x0000);
if (csum1 != csum2) { if (csum1 != csum2) {
ERROR (abstract->context, "Unexpected answer bytes."); ERROR (abstract->context, "Unexpected answer bytes.");
return DC_STATUS_PROTOCOL; return DC_STATUS_PROTOCOL;
} }
// Emit a device info event.
dc_event_devinfo_t devinfo;
devinfo.model = data[0];
devinfo.firmware = 0;
devinfo.serial = array_uint24_le (data + 1);
device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo);
return DC_STATUS_SUCCESS; return DC_STATUS_SUCCESS;
} }
@ -394,13 +401,6 @@ cressi_leonardo_device_foreach (dc_device_t *abstract, dc_dive_callback_t callba
return rc; return rc;
} }
unsigned char *data = dc_buffer_get_data (buffer);
dc_event_devinfo_t devinfo;
devinfo.model = data[0];
devinfo.firmware = 0;
devinfo.serial = array_uint24_le (data + 1);
device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo);
rc = cressi_leonardo_extract_dives (abstract, dc_buffer_get_data (buffer), rc = cressi_leonardo_extract_dives (abstract, dc_buffer_get_data (buffer),
dc_buffer_get_size (buffer), callback, userdata); dc_buffer_get_size (buffer), callback, userdata);

View File

@ -35,7 +35,7 @@ dc_status_t
cressi_leonardo_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream); cressi_leonardo_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream);
dc_status_t dc_status_t
cressi_leonardo_parser_create (dc_parser_t **parser, dc_context_t *context, unsigned int model); cressi_leonardo_parser_create (dc_parser_t **parser, dc_context_t *context, const unsigned char data[], size_t size, unsigned int model);
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@ -39,7 +39,6 @@ struct cressi_leonardo_parser_t {
unsigned int model; unsigned int model;
}; };
static dc_status_t cressi_leonardo_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size);
static dc_status_t cressi_leonardo_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime); static dc_status_t cressi_leonardo_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime);
static dc_status_t cressi_leonardo_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value); static dc_status_t cressi_leonardo_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value);
static dc_status_t cressi_leonardo_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata); static dc_status_t cressi_leonardo_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata);
@ -47,7 +46,9 @@ static dc_status_t cressi_leonardo_parser_samples_foreach (dc_parser_t *abstract
static const dc_parser_vtable_t cressi_leonardo_parser_vtable = { static const dc_parser_vtable_t cressi_leonardo_parser_vtable = {
sizeof(cressi_leonardo_parser_t), sizeof(cressi_leonardo_parser_t),
DC_FAMILY_CRESSI_LEONARDO, DC_FAMILY_CRESSI_LEONARDO,
cressi_leonardo_parser_set_data, /* set_data */ NULL, /* set_clock */
NULL, /* set_atmospheric */
NULL, /* set_density */
cressi_leonardo_parser_get_datetime, /* datetime */ cressi_leonardo_parser_get_datetime, /* datetime */
cressi_leonardo_parser_get_field, /* fields */ cressi_leonardo_parser_get_field, /* fields */
cressi_leonardo_parser_samples_foreach, /* samples_foreach */ cressi_leonardo_parser_samples_foreach, /* samples_foreach */
@ -56,7 +57,7 @@ static const dc_parser_vtable_t cressi_leonardo_parser_vtable = {
dc_status_t dc_status_t
cressi_leonardo_parser_create (dc_parser_t **out, dc_context_t *context, unsigned int model) cressi_leonardo_parser_create (dc_parser_t **out, dc_context_t *context, const unsigned char data[], size_t size, unsigned int model)
{ {
cressi_leonardo_parser_t *parser = NULL; cressi_leonardo_parser_t *parser = NULL;
@ -64,7 +65,7 @@ cressi_leonardo_parser_create (dc_parser_t **out, dc_context_t *context, unsigne
return DC_STATUS_INVALIDARGS; return DC_STATUS_INVALIDARGS;
// Allocate memory. // Allocate memory.
parser = (cressi_leonardo_parser_t *) dc_parser_allocate (context, &cressi_leonardo_parser_vtable); parser = (cressi_leonardo_parser_t *) dc_parser_allocate (context, &cressi_leonardo_parser_vtable, data, size);
if (parser == NULL) { if (parser == NULL) {
ERROR (context, "Failed to allocate memory."); ERROR (context, "Failed to allocate memory.");
return DC_STATUS_NOMEMORY; return DC_STATUS_NOMEMORY;
@ -78,13 +79,6 @@ cressi_leonardo_parser_create (dc_parser_t **out, dc_context_t *context, unsigne
} }
static dc_status_t
cressi_leonardo_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size)
{
return DC_STATUS_SUCCESS;
}
static dc_status_t static dc_status_t
cressi_leonardo_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime) cressi_leonardo_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime)
{ {
@ -143,6 +137,7 @@ cressi_leonardo_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, u
} }
break; break;
case DC_FIELD_GASMIX: case DC_FIELD_GASMIX:
gasmix->usage = DC_USAGE_NONE;
gasmix->helium = 0.0; gasmix->helium = 0.0;
gasmix->oxygen = data[0x19] / 100.0; gasmix->oxygen = data[0x19] / 100.0;
gasmix->nitrogen = 1.0 - gasmix->oxygen - gasmix->helium; gasmix->nitrogen = 1.0 - gasmix->oxygen - gasmix->helium;
@ -178,6 +173,9 @@ cressi_leonardo_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callbac
unsigned int gasmix_previous = 0xFFFFFFFF; unsigned int gasmix_previous = 0xFFFFFFFF;
unsigned int gasmix = 0; unsigned int gasmix = 0;
if (parser->model == DRAKE) {
gasmix = gasmix_previous;
}
unsigned int offset = SZ_HEADER; unsigned int offset = SZ_HEADER;
while (offset + 2 <= size) { while (offset + 2 <= size) {
@ -190,12 +188,12 @@ cressi_leonardo_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callbac
// Time (seconds). // Time (seconds).
time += surftime; time += surftime;
sample.time = time; sample.time = time * 1000;
if (callback) callback (DC_SAMPLE_TIME, sample, userdata); if (callback) callback (DC_SAMPLE_TIME, &sample, userdata);
// Depth (1/10 m). // Depth (1/10 m).
sample.depth = 0.0; sample.depth = 0.0;
if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata); if (callback) callback (DC_SAMPLE_DEPTH, &sample, userdata);
offset += 4; offset += 4;
} else { } else {
@ -205,17 +203,17 @@ cressi_leonardo_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callbac
// Time (seconds). // Time (seconds).
time += interval; time += interval;
sample.time = time; sample.time = time * 1000;
if (callback) callback (DC_SAMPLE_TIME, sample, userdata); if (callback) callback (DC_SAMPLE_TIME, &sample, userdata);
// Depth (1/10 m). // Depth (1/10 m).
sample.depth = depth / 10.0; sample.depth = depth / 10.0;
if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata); if (callback) callback (DC_SAMPLE_DEPTH, &sample, userdata);
// Gas change. // Gas change.
if (gasmix != gasmix_previous) { if (gasmix != gasmix_previous) {
sample.gasmix = gasmix; sample.gasmix = gasmix;
if (callback) callback (DC_SAMPLE_GASMIX, sample, userdata); if (callback) callback (DC_SAMPLE_GASMIX, &sample, userdata);
gasmix_previous = gasmix; gasmix_previous = gasmix;
} }
@ -225,7 +223,7 @@ cressi_leonardo_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callbac
sample.event.time = 0; sample.event.time = 0;
sample.event.flags = 0; sample.event.flags = 0;
sample.event.value = ascent; sample.event.value = ascent;
if (callback) callback (DC_SAMPLE_EVENT, sample, userdata); if (callback) callback (DC_SAMPLE_EVENT, &sample, userdata);
} }
offset += 2; offset += 2;

548
src/deepblu_cosmiq.c Normal file
View File

@ -0,0 +1,548 @@
/*
* libdivecomputer
*
* Copyright (C) 2019 Linus Torvalds
* Copyright (C) 2022 Jef Driesen
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
#include <string.h>
#include <stdlib.h>
#include "deepblu_cosmiq.h"
#include "context-private.h"
#include "device-private.h"
#include "platform.h"
#include "checksum.h"
#include "array.h"
// Maximum data in a packet. It's actually much
// less than this, since BLE packets are small and
// with the 7 bytes of headers and final newline
// and the HEX encoding, the actual maximum is
// just something like 6 bytes.
//
// But in theory the data could be done over
// multiple packets. That doesn't seem to be
// the case in anything I've seen so far.
//
// Pick something small and easy to use for
// stack buffers.
#define MAX_DATA 20
#define SZ_HEADER 36
#define FP_OFFSET 6
#define FP_SIZE 6
#define CMD_SET_DATETIME 0x20
#define CMD_DIVE_COUNT 0x40
#define CMD_DIVE_HEADER 0x41
#define CMD_DIVE_HEADER_DATA 0x42
#define CMD_DIVE_PROFILE 0x43
#define CMD_DIVE_PROFILE_DATA 0x44
#define CMD_SYSTEM_FW 0x58
#define CMD_SYSTEM_MAC 0x5A
#define NSTEPS 1000
#define STEP(i,n) (NSTEPS * (i) / (n))
typedef struct deepblu_cosmiq_device_t {
dc_device_t base;
dc_iostream_t *iostream;
unsigned char fingerprint[FP_SIZE];
} deepblu_cosmiq_device_t;
static dc_status_t deepblu_cosmiq_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size);
static dc_status_t deepblu_cosmiq_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata);
static dc_status_t deepblu_cosmiq_device_timesync(dc_device_t *abstract, const dc_datetime_t *datetime);
static const dc_device_vtable_t deepblu_cosmiq_device_vtable = {
sizeof(deepblu_cosmiq_device_t),
DC_FAMILY_DEEPBLU_COSMIQ,
deepblu_cosmiq_device_set_fingerprint, /* set_fingerprint */
NULL, /* read */
NULL, /* write */
NULL, /* dump */
deepblu_cosmiq_device_foreach, /* foreach */
deepblu_cosmiq_device_timesync, /* timesync */
NULL, /* close */
};
//
// Send a cmd packet.
//
// The format of the cmd on the "wire" is:
// - byte '#'
// - HEX char of cmd
// - HEX char two's complement modular sum of packet data (including cmd/size)
// - HEX char size of data as encoded in HEX
// - n * HEX char data
// - byte '\n'
// so you end up having 8 bytes of header/trailer overhead, and two bytes
// for every byte of data sent due to the HEX encoding.
//
static dc_status_t
deepblu_cosmiq_send (deepblu_cosmiq_device_t *device, const unsigned char cmd, const unsigned char data[], size_t size)
{
dc_status_t status = DC_STATUS_SUCCESS;
dc_device_t *abstract = (dc_device_t *) device;
if (size > MAX_DATA)
return DC_STATUS_INVALIDARGS;
if (device_is_cancelled (abstract))
return DC_STATUS_CANCELLED;
// Build the packet.
unsigned char csum = ~checksum_add_uint8 (data, size, cmd + 2 * size) + 1;
unsigned char raw[3 + MAX_DATA] = {
cmd,
csum,
2 * size
};
if (size) {
memcpy (raw + 3, data, size);
}
unsigned char packet[1 + 2 * (3 + MAX_DATA) + 1] = {0};
packet[0] = '#';
array_convert_bin2hex (raw, 3 + size, packet + 1, 2 * (3 + size));
packet[1 + 2 * (3 + size)] = '\n';
HEXDUMP (device->base.context, DC_LOGLEVEL_DEBUG, "cmd", raw, 3 + size);
// Send the command.
status = dc_iostream_write (device->iostream, packet, 2 + 2 * (3 + size), NULL);
if (status != DC_STATUS_SUCCESS) {
ERROR (device->base.context, "Failed to send the command.");
return status;
}
return status;
}
//
// Receive one 'line' of data
//
// The deepblu BLE protocol is ASCII line based and packetized.
// Normally one packet is one line, but it looks like the Nordic
// Semi BLE chip will sometimes send packets early (some internal
// serial buffer timeout?) with incompete data.
//
// So read packets until you get newline.
static dc_status_t
deepblu_cosmiq_recv_line (deepblu_cosmiq_device_t *device, unsigned char data[], size_t size, size_t *actual)
{
dc_status_t status = DC_STATUS_SUCCESS;
size_t nbytes = 0;
while (1) {
unsigned char buffer[20] = {0};
size_t transferred = 0;
status = dc_iostream_read (device->iostream, buffer, sizeof(buffer), &transferred);
if (status != DC_STATUS_SUCCESS) {
ERROR (device->base.context, "Failed to receive the reply packet.");
return status;
}
if (transferred < 1) {
ERROR (device->base.context, "Empty reply packet received.");
return DC_STATUS_PROTOCOL;
}
// Append the payload data to the output buffer. If the output
// buffer is too small, the error is not reported immediately
// but delayed until all packets have been received.
if (nbytes < size) {
size_t n = transferred;
if (nbytes + n > size) {
n = size - nbytes;
}
memcpy(data + nbytes, buffer, n);
}
nbytes += transferred;
// Last packet?
if (buffer[transferred - 1] == '\n')
break;
}
// Verify the expected number of bytes.
if (nbytes > size) {
ERROR (device->base.context, "Unexpected number of bytes received (" DC_PRINTF_SIZE " " DC_PRINTF_SIZE ").", nbytes, size);
return DC_STATUS_PROTOCOL;
}
if (actual)
*actual = nbytes;
return DC_STATUS_SUCCESS;
}
//
// Receive a reply packet
//
// The reply packet has the same format as the cmd packet we
// send, except the first byte is '$' instead of '#'.
static dc_status_t
deepblu_cosmiq_recv (deepblu_cosmiq_device_t *device, const unsigned char cmd, unsigned char data[], size_t size, size_t *actual)
{
dc_status_t status = DC_STATUS_SUCCESS;
unsigned char packet[1 + 2 * (3 + MAX_DATA) + 1] = {0};
size_t transferred = 0;
status = deepblu_cosmiq_recv_line (device, packet, sizeof(packet), &transferred);
if (status != DC_STATUS_SUCCESS)
return status;
if (transferred < 8 || (transferred % 2) != 0) {
ERROR (device->base.context, "Unexpected packet length (" DC_PRINTF_SIZE ").", transferred);
return DC_STATUS_PROTOCOL;
}
if (packet[0] != '$' || packet[transferred - 1] != '\n') {
ERROR (device->base.context, "Unexpected packet start/end byte.");
return DC_STATUS_PROTOCOL;
}
size_t length = transferred - 2;
unsigned char raw[3 + MAX_DATA] = {0};
if (array_convert_hex2bin (packet + 1, length, raw, length / 2) != 0) {
ERROR (device->base.context, "Unexpected packet data.");
return DC_STATUS_PROTOCOL;
}
length /= 2;
HEXDUMP (device->base.context, DC_LOGLEVEL_DEBUG, "rcv", raw, length);
unsigned char rsp = raw[0];
if (rsp != cmd) {
ERROR (device->base.context, "Unexpected packet command byte (%02x)", rsp);
return DC_STATUS_PROTOCOL;
}
unsigned int n = raw[2];
if ((n % 2) != 0 || n != transferred - 8) {
ERROR (device->base.context, "Unexpected packet length (%u)", n);
return DC_STATUS_PROTOCOL;
}
unsigned char csum = checksum_add_uint8 (raw, length, 0);
if (csum != 0) {
ERROR (device->base.context, "Unexpected packet checksum (%02x).", csum);
return DC_STATUS_PROTOCOL;
}
length -= 3;
if (length > size) {
ERROR (device->base.context, "Unexpected number of bytes received (" DC_PRINTF_SIZE " " DC_PRINTF_SIZE ").", length, size);
return DC_STATUS_PROTOCOL;
}
memcpy (data, raw + 3, length);
if (actual)
*actual = length;
return DC_STATUS_SUCCESS;
}
static dc_status_t
deepblu_cosmiq_transfer (deepblu_cosmiq_device_t *device, const unsigned char cmd,
const unsigned char input[], size_t isize,
unsigned char output[], size_t osize)
{
dc_status_t status = DC_STATUS_SUCCESS;
status = deepblu_cosmiq_send (device, cmd, input, isize);
if (status != DC_STATUS_SUCCESS)
return status;
size_t transferred = 0;
status = deepblu_cosmiq_recv (device, cmd, output, osize, &transferred);
if (status != DC_STATUS_SUCCESS)
return status;
if (transferred != osize) {
ERROR (device->base.context, "Unexpected number of bytes received (" DC_PRINTF_SIZE " " DC_PRINTF_SIZE ").", osize, transferred);
return DC_STATUS_PROTOCOL;
}
return DC_STATUS_SUCCESS;
}
static dc_status_t
deepblu_cosmiq_recv_bulk (deepblu_cosmiq_device_t *device, dc_event_progress_t *progress, const unsigned char cmd, unsigned char data[], size_t size)
{
dc_status_t status = DC_STATUS_SUCCESS;
dc_device_t *abstract = (dc_device_t *) device;
const unsigned int initial = progress ? progress->current : 0;
size_t nbytes = 0;
while (nbytes < size) {
size_t transferred = 0;
status = deepblu_cosmiq_recv (device, cmd, data + nbytes, size - nbytes, &transferred);
if (status != DC_STATUS_SUCCESS)
return status;
nbytes += transferred;
// Update and emit a progress event.
if (progress) {
progress->current = initial + STEP(nbytes, size);
device_event_emit (abstract, DC_EVENT_PROGRESS, progress);
}
}
return DC_STATUS_SUCCESS;
}
dc_status_t
deepblu_cosmiq_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t *iostream)
{
dc_status_t status = DC_STATUS_SUCCESS;
deepblu_cosmiq_device_t *device = NULL;
if (out == NULL)
return DC_STATUS_INVALIDARGS;
// Allocate memory.
device = (deepblu_cosmiq_device_t *) dc_device_allocate (context, &deepblu_cosmiq_device_vtable);
if (device == NULL) {
ERROR (context, "Failed to allocate memory.");
return DC_STATUS_NOMEMORY;
}
// Set the default values.
device->iostream = iostream;
memset (device->fingerprint, 0, sizeof(device->fingerprint));
// Set the timeout for receiving data (1000ms).
status = dc_iostream_set_timeout (device->iostream, 1000);
if (status != DC_STATUS_SUCCESS) {
ERROR (context, "Failed to set the timeout.");
goto error_free;
}
dc_iostream_purge (device->iostream, DC_DIRECTION_ALL);
*out = (dc_device_t *) device;
return DC_STATUS_SUCCESS;
error_free:
dc_device_deallocate ((dc_device_t *) device);
return status;
}
static dc_status_t
deepblu_cosmiq_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size)
{
deepblu_cosmiq_device_t *device = (deepblu_cosmiq_device_t *)abstract;
if (size && size != sizeof (device->fingerprint))
return DC_STATUS_INVALIDARGS;
if (size)
memcpy (device->fingerprint, data, sizeof (device->fingerprint));
else
memset (device->fingerprint, 0, sizeof (device->fingerprint));
return DC_STATUS_SUCCESS;
}
static dc_status_t
deepblu_cosmiq_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata)
{
dc_status_t status = DC_STATUS_SUCCESS;
deepblu_cosmiq_device_t *device = (deepblu_cosmiq_device_t *) abstract;
const unsigned char zero = 0;
// Enable progress notifications.
dc_event_progress_t progress = EVENT_PROGRESS_INITIALIZER;
device_event_emit (abstract, DC_EVENT_PROGRESS, &progress);
// Read the firmware version.
unsigned char fw[1] = {0};
status = deepblu_cosmiq_transfer (device, CMD_SYSTEM_FW, &zero, 1, fw, sizeof(fw));
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to read the firmware version.");
goto error_exit;
}
// Read the MAC address.
unsigned char mac[6] = {0};
status = deepblu_cosmiq_transfer (device, CMD_SYSTEM_MAC, &zero, 1, mac, sizeof(mac));
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to read the MAC address.");
goto error_exit;
}
// Emit a device info event.
dc_event_devinfo_t devinfo;
devinfo.model = 0;
devinfo.firmware = fw[0] & 0x3F;
devinfo.serial = array_uint32_le (mac);
device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo);
unsigned char ndives = 0;
status = deepblu_cosmiq_transfer (device, CMD_DIVE_COUNT, &zero, 1, &ndives, 1);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to read the number of dives.");
goto error_exit;
}
// Update and emit a progress event.
progress.current = (ndives == 0 ? 1 : 0) * NSTEPS;
progress.maximum = (ndives + 1) * NSTEPS;
device_event_emit (abstract, DC_EVENT_PROGRESS, &progress);
if (ndives == 0) {
status = DC_STATUS_SUCCESS;
goto error_exit;
}
unsigned char *headers = malloc (ndives * SZ_HEADER);
if (headers == NULL) {
ERROR (abstract->context, "Failed to allocate memory.");
status = DC_STATUS_NOMEMORY;
goto error_exit;
}
unsigned int count = 0;
for (unsigned int i = 0; i < ndives; ++i) {
unsigned char number = i + 1;
unsigned char len = 0;
status = deepblu_cosmiq_transfer (device, CMD_DIVE_HEADER, &number, 1, &len, 1);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to read the dive header.");
goto error_free_headers;
}
if (len != SZ_HEADER) {
status = DC_STATUS_PROTOCOL;
goto error_free_headers;
}
status = deepblu_cosmiq_recv_bulk (device, NULL, CMD_DIVE_HEADER_DATA, headers + i * SZ_HEADER, SZ_HEADER);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to read the dive header.");
goto error_free_headers;
}
// Update and emit a progress event.
progress.current = STEP(i + 1, ndives);
device_event_emit (abstract, DC_EVENT_PROGRESS, &progress);
if (memcmp (headers + i * SZ_HEADER + FP_OFFSET, device->fingerprint, sizeof (device->fingerprint)) == 0)
break;
count++;
}
// Update and emit a progress event.
progress.current = 1 * NSTEPS;
progress.maximum = (count + 1) * NSTEPS;
device_event_emit (abstract, DC_EVENT_PROGRESS, &progress);
// Allocate memory for the dive.
dc_buffer_t *dive = dc_buffer_new (4096);
if (dive == NULL) {
ERROR (abstract->context, "Failed to allocate memory.");
status = DC_STATUS_NOMEMORY;
goto error_free_headers;
}
for (unsigned int i = 0; i < count; ++i) {
unsigned char number = i + 1;
unsigned char rsp_len[2] = {0};
status = deepblu_cosmiq_transfer (device, CMD_DIVE_PROFILE, &number, 1, rsp_len, sizeof(rsp_len));
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to read the dive profile.");
goto error_free_dive;
}
unsigned int len = array_uint16_be (rsp_len);
// Erase the buffer.
dc_buffer_clear (dive);
// Allocate memory for the dive.
if (!dc_buffer_resize (dive, len + SZ_HEADER)) {
ERROR (abstract->context, "Failed to allocate memory.");
status = DC_STATUS_NOMEMORY;
goto error_free_dive;
}
// Cache the pointer.
unsigned char *data = dc_buffer_get_data (dive);
memcpy (data, headers + i * SZ_HEADER, SZ_HEADER);
status = deepblu_cosmiq_recv_bulk (device, &progress, CMD_DIVE_PROFILE_DATA, data + SZ_HEADER, len);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to read the dive profile.");
goto error_free_dive;
}
if (callback && !callback (data, len + SZ_HEADER, data + FP_OFFSET, FP_SIZE, userdata))
break;
}
error_free_dive:
dc_buffer_free (dive);
error_free_headers:
free (headers);
error_exit:
return status;
}
static dc_status_t
deepblu_cosmiq_device_timesync (dc_device_t *abstract, const dc_datetime_t *datetime)
{
dc_status_t status = DC_STATUS_SUCCESS;
deepblu_cosmiq_device_t *device = (deepblu_cosmiq_device_t *) abstract;
if (datetime->year < 2000) {
ERROR (abstract->context, "Invalid date/time value specified.");
return DC_STATUS_INVALIDARGS;
}
const unsigned char cmd[6] = {
dec2bcd(datetime->year - 2000),
dec2bcd(datetime->month),
dec2bcd(datetime->day),
dec2bcd(datetime->hour),
dec2bcd(datetime->minute),
dec2bcd(datetime->second)};
unsigned char rsp[1] = {0};
status = deepblu_cosmiq_transfer (device, CMD_SET_DATETIME,
cmd, sizeof(cmd), rsp, sizeof(rsp));
if (status != DC_STATUS_SUCCESS)
return status;
return DC_STATUS_SUCCESS;
}

44
src/deepblu_cosmiq.h Normal file
View File

@ -0,0 +1,44 @@
/*
* libdivecomputer
*
* Copyright (C) 2018 Linus Torvalds
* Copyright (C) 2022 Jef Driesen
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
#ifndef DEEPBLU_COSMIQ_H
#define DEEPBLU_COSMIQ_H
#include <libdivecomputer/context.h>
#include <libdivecomputer/iostream.h>
#include <libdivecomputer/device.h>
#include <libdivecomputer/parser.h>
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
dc_status_t
deepblu_cosmiq_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream);
dc_status_t
deepblu_cosmiq_parser_create (dc_parser_t **parser, dc_context_t *context, const unsigned char data[], size_t size);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* DEEPBLU_COSMIQ_H */

211
src/deepblu_cosmiq_parser.c Normal file
View File

@ -0,0 +1,211 @@
/*
* libdivecomputer
*
* Copyright (C) 2019 Linus Torvalds
* Copyright (C) 2022 Jef Driesen
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
#include <stdlib.h>
#include <libdivecomputer/units.h>
#include "deepblu_cosmiq.h"
#include "context-private.h"
#include "parser-private.h"
#include "array.h"
#define SCUBA 2
#define GAUGE 3
#define FREEDIVE 4
#define SZ_HEADER 36
#define SZ_SAMPLE 4
typedef struct deepblu_cosmiq_parser_t {
dc_parser_t base;
double hydrostatic;
} deepblu_cosmiq_parser_t;
static dc_status_t deepblu_cosmiq_parser_set_density (dc_parser_t *abstract, double density);
static dc_status_t deepblu_cosmiq_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime);
static dc_status_t deepblu_cosmiq_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value);
static dc_status_t deepblu_cosmiq_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata);
static const dc_parser_vtable_t deepblu_cosmiq_parser_vtable = {
sizeof(deepblu_cosmiq_parser_t),
DC_FAMILY_DEEPBLU_COSMIQ,
NULL, /* set_clock */
NULL, /* set_atmospheric */
deepblu_cosmiq_parser_set_density, /* set_density */
deepblu_cosmiq_parser_get_datetime, /* datetime */
deepblu_cosmiq_parser_get_field, /* fields */
deepblu_cosmiq_parser_samples_foreach, /* samples_foreach */
NULL /* destroy */
};
dc_status_t
deepblu_cosmiq_parser_create (dc_parser_t **out, dc_context_t *context, const unsigned char data[], size_t size)
{
deepblu_cosmiq_parser_t *parser = NULL;
if (out == NULL)
return DC_STATUS_INVALIDARGS;
// Allocate memory.
parser = (deepblu_cosmiq_parser_t *) dc_parser_allocate (context, &deepblu_cosmiq_parser_vtable, data, size);
if (parser == NULL) {
ERROR (context, "Failed to allocate memory.");
return DC_STATUS_NOMEMORY;
}
// Set the default values.
parser->hydrostatic = DEF_DENSITY_SALT * GRAVITY;
*out = (dc_parser_t *) parser;
return DC_STATUS_SUCCESS;
}
static dc_status_t
deepblu_cosmiq_parser_set_density (dc_parser_t *abstract, double density)
{
deepblu_cosmiq_parser_t *parser = (deepblu_cosmiq_parser_t *) abstract;
parser->hydrostatic = density * GRAVITY;
return DC_STATUS_SUCCESS;
}
static dc_status_t
deepblu_cosmiq_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime)
{
const unsigned char *data = abstract->data;
unsigned int size = abstract->size;
if (size < SZ_HEADER)
return DC_STATUS_DATAFORMAT;
if (datetime) {
datetime->year = array_uint16_le(data + 6);
datetime->day = data[8];
datetime->month = data[9];
datetime->minute = data[10];
datetime->hour = data[11];
datetime->second = 0;
datetime->timezone = DC_TIMEZONE_NONE;
}
return DC_STATUS_SUCCESS;
}
static dc_status_t
deepblu_cosmiq_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value)
{
deepblu_cosmiq_parser_t *parser = (deepblu_cosmiq_parser_t *) abstract;
const unsigned char *data = abstract->data;
unsigned int size = abstract->size;
dc_gasmix_t *gasmix = (dc_gasmix_t *) value;
if (size < SZ_HEADER)
return DC_STATUS_DATAFORMAT;
unsigned int mode = data[2];
unsigned int atmospheric = array_uint16_le (data + 4) & 0x1FFF;
if (value) {
switch (type) {
case DC_FIELD_DIVETIME:
if (mode == SCUBA || mode == GAUGE)
*((unsigned int *) value) = array_uint16_le(data + 12) * 60;
else
*((unsigned int *) value) = array_uint16_le(data + 12);
break;
case DC_FIELD_MAXDEPTH:
*((double *) value) = (signed int) (array_uint16_le (data + 22) - atmospheric) * (BAR / 1000.0) / parser->hydrostatic;
break;
case DC_FIELD_GASMIX_COUNT:
*((unsigned int *) value) = mode == SCUBA;
break;
case DC_FIELD_GASMIX:
gasmix->usage = DC_USAGE_NONE;
gasmix->oxygen = data[3] / 100.0;
gasmix->helium = 0.0;
gasmix->nitrogen = 1.0 - gasmix->oxygen - gasmix->helium;
break;
case DC_FIELD_DIVEMODE:
switch (mode) {
case SCUBA:
*((dc_divemode_t *) value) = DC_DIVEMODE_OC;
break;
case GAUGE:
*((dc_divemode_t *) value) = DC_DIVEMODE_GAUGE;
break;
case FREEDIVE:
*((dc_divemode_t *) value) = DC_DIVEMODE_FREEDIVE;
break;
default:
ERROR (abstract->context, "Unknown activity type '%02x'", mode);
return DC_STATUS_DATAFORMAT;
}
break;
case DC_FIELD_ATMOSPHERIC:
*((double *) value) = atmospheric / 1000.0;
break;
default:
return DC_STATUS_UNSUPPORTED;
}
}
return DC_STATUS_SUCCESS;
}
static dc_status_t
deepblu_cosmiq_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata)
{
deepblu_cosmiq_parser_t *parser = (deepblu_cosmiq_parser_t *) abstract;
const unsigned char *data = abstract->data;
unsigned int size = abstract->size;
if (size < SZ_HEADER)
return DC_STATUS_DATAFORMAT;
unsigned int interval = data[26];
unsigned int atmospheric = array_uint16_le (data + 4) & 0x1FFF;
unsigned int time = 0;
unsigned int offset = SZ_HEADER;
while (offset + SZ_SAMPLE <= size) {
dc_sample_value_t sample = {0};
unsigned int temperature = array_uint16_le(data + offset + 0);
unsigned int depth = array_uint16_le(data + offset + 2);
offset += SZ_SAMPLE;
time += interval;
sample.time = time * 1000;
if (callback) callback (DC_SAMPLE_TIME, &sample, userdata);
sample.depth = (signed int) (depth - atmospheric) * (BAR / 1000.0) / parser->hydrostatic;
if (callback) callback (DC_SAMPLE_DEPTH, &sample, userdata);
sample.temperature = temperature / 10.0;
if (callback) callback (DC_SAMPLE_TEMPERATURE, &sample, userdata);
}
return DC_STATUS_SUCCESS;
}

516
src/deepsix_excursion.c Normal file
View File

@ -0,0 +1,516 @@
/*
* libdivecomputer
*
* Copyright (C) 2021 Ryan Gardner, Jef Driesen
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include "deepsix_excursion.h"
#include "context-private.h"
#include "device-private.h"
#include "platform.h"
#include "checksum.h"
#include "array.h"
#define MAXPACKET 255
#define HEADERSIZE_MIN 128
#define HEADERSIZE_V0 156
#define NSTEPS 1000
#define STEP(i,n) (NSTEPS * (i) / (n))
#define FP_SIZE 6
#define FP_OFFSET 12
#define DIR_WRITE 0x00
#define DIR_READ 0x01
#define GRP_INFO 0xA0
#define CMD_INFO_HARDWARE 0x01
#define CMD_INFO_SOFTWARE 0x02
#define CMD_INFO_SERIAL 0x03
#define CMD_INFO_LASTDIVE 0x04
#define GRP_SETTINGS 0xB0
#define CMD_SETTINGS_DATE 0x01
#define CMD_SETTINGS_TIME 0x03
#define CMD_SETTINGS_STORE 0x27
#define CMD_SETTINGS_LOAD 0x28
#define GRP_DIVE 0xC0
#define CMD_DIVE_HEADER 0x02
#define CMD_DIVE_PROFILE 0x03
#define CMD_DIVE_COUNT 0x05
#define CMD_DIVE_INDEX_LAST 0x06
#define CMD_DIVE_INDEX_FIRST 0x07
#define CMD_DIVE_INDEX_PREVIOUS 0x08
#define CMD_DIVE_INDEX_NEXT 0x09
typedef struct deepsix_excursion_device_t {
dc_device_t base;
dc_iostream_t *iostream;
unsigned char fingerprint[FP_SIZE];
} deepsix_excursion_device_t;
static dc_status_t deepsix_excursion_device_set_fingerprint (dc_device_t *abstract, const unsigned char *data, unsigned int size);
static dc_status_t deepsix_excursion_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata);
static dc_status_t deepsix_excursion_device_timesync(dc_device_t *abstract, const dc_datetime_t *datetime);
static const dc_device_vtable_t deepsix_excursion_device_vtable = {
sizeof(deepsix_excursion_device_t),
DC_FAMILY_DEEPSIX_EXCURSION,
deepsix_excursion_device_set_fingerprint, /* set_fingerprint */
NULL, /* read */
NULL, /* write */
NULL, /* dump */
deepsix_excursion_device_foreach, /* foreach */
deepsix_excursion_device_timesync, /* timesync */
NULL, /* close */
};
static dc_status_t
deepsix_excursion_send (deepsix_excursion_device_t *device, unsigned char grp, unsigned char cmd, unsigned char dir, const unsigned char data[], unsigned int size)
{
dc_status_t status = DC_STATUS_SUCCESS;
dc_device_t *abstract = (dc_device_t *) device;
unsigned char packet[4 + MAXPACKET + 1];
if (device_is_cancelled (abstract))
return DC_STATUS_CANCELLED;
if (size > MAXPACKET)
return DC_STATUS_INVALIDARGS;
// Setup the data packet
packet[0] = grp;
packet[1] = cmd;
packet[2] = dir;
packet[3] = size;
if (size) {
memcpy(packet + 4, data, size);
}
packet[size + 4] = checksum_add_uint8 (packet, size + 4, 0) ^ 0xFF;
// Send the data packet.
status = dc_iostream_write (device->iostream, packet, 4 + size + 1, NULL);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to send the command.");
return status;
}
return status;
}
static dc_status_t
deepsix_excursion_recv (deepsix_excursion_device_t *device, unsigned char grp, unsigned char cmd, unsigned char dir, unsigned char data[], unsigned int size, unsigned int *actual)
{
dc_status_t status = DC_STATUS_SUCCESS;
dc_device_t *abstract = (dc_device_t *) device;
unsigned char packet[4 + MAXPACKET + 1];
size_t transferred = 0;
// Read the packet header, payload and checksum.
status = dc_iostream_read (device->iostream, packet, sizeof(packet), &transferred);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to receive the packet.");
return status;
}
if (transferred < 4) {
ERROR (abstract->context, "Packet header too short ("DC_PRINTF_SIZE").", transferred);
return DC_STATUS_PROTOCOL;
}
// Verify the packet header.
if (packet[0] != grp || packet[1] != cmd || packet[2] != dir) {
ERROR (device->base.context, "Unexpected packet header.");
return DC_STATUS_PROTOCOL;
}
unsigned int len = packet[3];
if (len > MAXPACKET) {
ERROR (abstract->context, "Packet header length too large (%u).", len);
return DC_STATUS_PROTOCOL;
}
if (transferred < 4 + len + 1) {
ERROR (abstract->context, "Packet data too short ("DC_PRINTF_SIZE").", transferred);
return DC_STATUS_PROTOCOL;
}
// Verify the checksum.
unsigned char csum = checksum_add_uint8 (packet, len + 4, 0) ^ 0xFF;
if (packet[len + 4] != csum) {
ERROR (abstract->context, "Unexpected packet checksum (%02x)", csum);
return DC_STATUS_PROTOCOL;
}
if (len > size) {
ERROR (abstract->context, "Unexpected packet length (%u).", len);
return DC_STATUS_PROTOCOL;
}
if (len) {
memcpy(data, packet + 4, len);
}
if (actual)
*actual = len;
return status;
}
static dc_status_t
deepsix_excursion_transfer (deepsix_excursion_device_t *device, unsigned char grp, unsigned char cmd, unsigned char dir, const unsigned char command[], unsigned int csize, unsigned char answer[], unsigned int asize, unsigned int *actual)
{
dc_status_t status = DC_STATUS_SUCCESS;
status = deepsix_excursion_send (device, grp, cmd, dir, command, csize);
if (status != DC_STATUS_SUCCESS)
return status;
status = deepsix_excursion_recv (device, grp + 1, cmd, dir, answer, asize, actual);
if (status != DC_STATUS_SUCCESS)
return status;
return status;
}
dc_status_t
deepsix_excursion_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t *iostream)
{
dc_status_t status = DC_STATUS_SUCCESS;
deepsix_excursion_device_t *device = NULL;
if (out == NULL)
return DC_STATUS_INVALIDARGS;
// Allocate memory.
device = (deepsix_excursion_device_t *) dc_device_allocate (context, &deepsix_excursion_device_vtable);
if (device == NULL) {
ERROR (context, "Failed to allocate memory.");
return DC_STATUS_NOMEMORY;
}
// Set the default values.
device->iostream = iostream;
memset(device->fingerprint, 0, sizeof(device->fingerprint));
// Set the serial communication protocol (115200 8N1).
status = dc_iostream_configure (device->iostream, 115200, 8, DC_PARITY_NONE, DC_STOPBITS_ONE, DC_FLOWCONTROL_NONE);
if (status != DC_STATUS_SUCCESS) {
ERROR (context, "Failed to set the terminal attributes.");
goto error_free;
}
// Set the timeout for receiving data (3000ms).
status = dc_iostream_set_timeout (device->iostream, 3000);
if (status != DC_STATUS_SUCCESS) {
ERROR (context, "Failed to set the timeout.");
goto error_free;
}
// Make sure everything is in a sane state.
dc_iostream_sleep (device->iostream, 300);
dc_iostream_purge (device->iostream, DC_DIRECTION_ALL);
*out = (dc_device_t *) device;
return DC_STATUS_SUCCESS;
error_free:
dc_device_deallocate ((dc_device_t *) device);
return status;
}
static dc_status_t
deepsix_excursion_device_set_fingerprint (dc_device_t *abstract, const unsigned char *data, unsigned int size)
{
deepsix_excursion_device_t *device = (deepsix_excursion_device_t *)abstract;
if (size && size != sizeof (device->fingerprint))
return DC_STATUS_INVALIDARGS;
if (size)
memcpy (device->fingerprint, data, sizeof (device->fingerprint));
else
memset (device->fingerprint, 0, sizeof (device->fingerprint));
return DC_STATUS_SUCCESS;
}
static dc_status_t
deepsix_excursion_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata)
{
dc_status_t status = DC_STATUS_SUCCESS;
deepsix_excursion_device_t *device = (deepsix_excursion_device_t *) abstract;
// Enable progress notifications.
dc_event_progress_t progress = EVENT_PROGRESS_INITIALIZER;
device_event_emit (abstract, DC_EVENT_PROGRESS, &progress);
// Load the settings into memory.
status = deepsix_excursion_transfer (device, GRP_SETTINGS, CMD_SETTINGS_LOAD, DIR_WRITE, NULL, 0, NULL, 0, NULL);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to load the settings.");
return status;
}
// Read the hardware version.
unsigned char rsp_hardware[6] = {0};
status = deepsix_excursion_transfer (device, GRP_INFO, CMD_INFO_HARDWARE, DIR_READ, NULL, 0, rsp_hardware, sizeof(rsp_hardware), NULL);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to read the hardware version.");
return status;
}
// Read the software version.
unsigned char rsp_software[6] = {0};
status = deepsix_excursion_transfer (device, GRP_INFO, CMD_INFO_SOFTWARE, DIR_READ, NULL, 0, rsp_software, sizeof(rsp_software), NULL);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to read the software version.");
return status;
}
// Read the serial number
unsigned char rsp_serial[12] = {0};
status = deepsix_excursion_transfer (device, GRP_INFO, CMD_INFO_SERIAL, DIR_READ, NULL, 0, rsp_serial, sizeof(rsp_serial), NULL);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to read the serial number.");
return status;
}
// Emit a device info event.
dc_event_devinfo_t devinfo;
devinfo.model = 0;
devinfo.firmware = array_uint16_be (rsp_software + 4);
devinfo.serial = array_convert_str2num (rsp_serial + 3, sizeof(rsp_serial) - 3);
device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo);
// Firmware version 6+ uses the new commands.
unsigned int fw6 = memcmp(rsp_software, "D01", 3) == 0 && rsp_software[4] >= '6';
unsigned int ndives = 0, last = 0;
if (fw6) {
// Read the number of dives.
unsigned char rsp_ndives[2] = {0};
status = deepsix_excursion_transfer (device, GRP_DIVE, CMD_DIVE_COUNT, DIR_READ, NULL, 0, rsp_ndives, sizeof(rsp_ndives), NULL);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to read the number of dives");
return status;
}
// Read the index of the last dive.
unsigned char rsp_last[4] = {0};
status = deepsix_excursion_transfer (device, GRP_DIVE, CMD_DIVE_INDEX_LAST, DIR_READ, NULL, 0, rsp_last, sizeof(rsp_last), NULL);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to read the last dive index.");
return status;
}
ndives = array_uint16_le (rsp_ndives);
last = array_uint16_le (rsp_last);
} else {
// Read the index of the last dive.
const unsigned char cmd_last[2] = {0};
unsigned char rsp_last[2] = {0};
status = deepsix_excursion_transfer (device, GRP_INFO, CMD_INFO_LASTDIVE, DIR_READ, cmd_last, sizeof(cmd_last), rsp_last, sizeof(rsp_last), NULL);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to read the last dive index.");
return status;
}
ndives = last = array_uint16_le (rsp_last);
}
// Update and emit a progress event.
progress.current = 1 * NSTEPS;
progress.maximum = (ndives + 1) * NSTEPS;
device_event_emit (abstract, DC_EVENT_PROGRESS, &progress);
dc_buffer_t *buffer = dc_buffer_new(0);
if (buffer == NULL) {
ERROR (abstract->context, "Insufficient buffer space available.");
return DC_STATUS_NOMEMORY;
}
unsigned int number = last;
for (unsigned int i = 0; i < ndives; ++i) {
if (fw6) {
if (i > 0) {
const unsigned char cmd_previous[] = {
(number ) & 0xFF,
(number >> 8) & 0xFF,
(number >> 16) & 0xFF,
(number >> 24) & 0xFF};
unsigned char rsp_previous[4] = {0};
status = deepsix_excursion_transfer (device, GRP_DIVE, CMD_DIVE_INDEX_PREVIOUS, DIR_READ,
cmd_previous, sizeof(cmd_previous), rsp_previous, sizeof(rsp_previous), NULL);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to read the previous dive index");
return status;
}
number = array_uint32_le (rsp_previous);
}
} else {
number = ndives - i;
}
unsigned int headersize = 0;
const unsigned char cmd_header[] = {
(number ) & 0xFF,
(number >> 8) & 0xFF};
unsigned char rsp_header[MAXPACKET] = {0};
status = deepsix_excursion_transfer (device, GRP_DIVE, CMD_DIVE_HEADER, DIR_READ,
cmd_header, sizeof(cmd_header), rsp_header, sizeof(rsp_header), &headersize);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to read the dive header.");
goto error_free;
}
if (headersize < HEADERSIZE_MIN || headersize != (fw6 ? rsp_header[2] : HEADERSIZE_V0)) {
ERROR (abstract->context, "Unexpected size of the dive header (%u).", headersize);
goto error_free;
}
if (memcmp(rsp_header + FP_OFFSET, device->fingerprint, sizeof(device->fingerprint)) == 0)
break;
unsigned int length = array_uint32_le (rsp_header + 8);
// Update and emit a progress event.
progress.current = (i + 1) * NSTEPS + STEP(headersize, headersize + length);
device_event_emit(abstract, DC_EVENT_PROGRESS, &progress);
dc_buffer_clear(buffer);
dc_buffer_reserve(buffer, headersize + length);
if (!dc_buffer_append(buffer, rsp_header, headersize)) {
ERROR (abstract->context, "Insufficient buffer space available.");
status = DC_STATUS_NOMEMORY;
goto error_free;
}
unsigned offset = 0;
while (offset < length) {
unsigned int len = 0;
const unsigned char cmd_profile[] = {
(number ) & 0xFF,
(number >> 8) & 0xFF,
(offset ) & 0xFF,
(offset >> 8) & 0xFF,
(offset >> 16) & 0xFF,
(offset >> 24) & 0xFF};
unsigned char rsp_profile[MAXPACKET] = {0};
status = deepsix_excursion_transfer (device, GRP_DIVE, CMD_DIVE_PROFILE, DIR_READ,
cmd_profile, sizeof(cmd_profile), rsp_profile, sizeof(rsp_profile), &len);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to read the dive profile.");
goto error_free;
}
// Remove padding from the last packet.
unsigned int n = len;
if (offset + n > length) {
n = length - offset;
}
// Update and emit a progress event.
progress.current = (i + 1) * NSTEPS + STEP(headersize + offset + n, headersize + length);
device_event_emit(abstract, DC_EVENT_PROGRESS, &progress);
if (!dc_buffer_append(buffer, rsp_profile, n)) {
ERROR (abstract->context, "Insufficient buffer space available.");
status = DC_STATUS_NOMEMORY;
goto error_free;
}
offset += n;
}
unsigned char *data = dc_buffer_get_data(buffer);
unsigned int size = dc_buffer_get_size(buffer);
if (callback && !callback (data, size, data + FP_OFFSET, sizeof(device->fingerprint), userdata)) {
break;
}
}
error_free:
dc_buffer_free(buffer);
return status;
}
static dc_status_t
deepsix_excursion_device_timesync (dc_device_t *abstract, const dc_datetime_t *datetime)
{
dc_status_t status = DC_STATUS_SUCCESS;
deepsix_excursion_device_t *device = (deepsix_excursion_device_t *) abstract;
if (datetime->year < 2000) {
ERROR (abstract->context, "Invalid date/time value specified.");
return DC_STATUS_INVALIDARGS;
}
const unsigned char cmd_date[] = {
datetime->year - 2000,
datetime->month,
datetime->day};
const unsigned char cmd_time[] = {
datetime->hour,
datetime->minute,
datetime->second};
const unsigned char cmd_store[] = {0x00};
unsigned char rsp_date[sizeof(cmd_date)] = {0};
status = deepsix_excursion_transfer (device, GRP_SETTINGS, CMD_SETTINGS_DATE, DIR_WRITE, cmd_date, sizeof(cmd_date), rsp_date, sizeof(rsp_date), NULL);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to set the date.");
return status;
}
if (memcmp(rsp_date, cmd_date, sizeof(cmd_date)) != 0) {
ERROR (abstract->context, "Failed to verify the date.");
return DC_STATUS_PROTOCOL;
}
unsigned char rsp_time[sizeof(cmd_time)] = {0};
status = deepsix_excursion_transfer (device, GRP_SETTINGS, CMD_SETTINGS_TIME, DIR_WRITE, cmd_time, sizeof(cmd_time), rsp_time, sizeof(rsp_time), NULL);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to set the time.");
return status;
}
if (memcmp(rsp_time, cmd_time, sizeof(cmd_time)) != 0) {
ERROR (abstract->context, "Failed to verify the time.");
return DC_STATUS_PROTOCOL;
}
status = deepsix_excursion_transfer (device, GRP_SETTINGS, CMD_SETTINGS_STORE, DIR_WRITE, cmd_store, sizeof(cmd_store), NULL, 0, NULL);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to store the settings.");
return status;
}
return status;
}

43
src/deepsix_excursion.h Normal file
View File

@ -0,0 +1,43 @@
/*
* libdivecomputer
*
* Copyright (C) 2021 Ryan Gardner, Jef Driesen
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*
*/
#ifndef DEEPSIX_EXCURSION_H
#define DEEPSIX_EXCURSION_H
#include <libdivecomputer/context.h>
#include <libdivecomputer/iostream.h>
#include <libdivecomputer/device.h>
#include <libdivecomputer/parser.h>
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
dc_status_t
deepsix_excursion_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t *iostream);
dc_status_t
deepsix_excursion_parser_create (dc_parser_t **parser, dc_context_t *context, const unsigned char data[], size_t size);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* DEEPSIX_EXCURSION_H */

View File

@ -0,0 +1,768 @@
/*
* libdivecomputer
*
* Copyright (C) 2021 Ryan Gardner, Jef Driesen
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <libdivecomputer/units.h>
#include "deepsix_excursion.h"
#include "context-private.h"
#include "parser-private.h"
#include "array.h"
#define HEADERSIZE_MIN 128
#define MAX_SAMPLES 7
#define MAX_EVENTS 7
#define MAX_GASMIXES 20
#define ALARM 0x0001
#define TEMPERATURE 0x0002
#define DECO 0x0003
#define CEILING 0x0004
#define CNS 0x0005
#define SAMPLE_TEMPERATURE 0
#define SAMPLE_DECO_NDL 1
#define SAMPLE_CNS 2
#define EVENT_CHANGE_GAS 7
#define EVENT_ALARMS 8
#define EVENT_CHANGE_SETPOINT 9
#define EVENT_SAMPLES_MISSED 10
#define EVENT_RESERVED 15
#define ALARM_ASCENTRATE 0
#define ALARM_CEILING 1
#define ALARM_PO2 2
#define ALARM_MAXDEPTH 3
#define ALARM_DIVETIME 4
#define ALARM_CNS 5
#define DECOSTOP 0x02
#define SAFETYSTOP 0x04
#define DENSITY 1024
#define UNDEFINED 0xFFFFFFFF
#define FWVERSION(major,minor) ( \
((((major) + '0') & 0xFF) << 8) | \
((minor) & 0xFF))
typedef struct deepsix_excursion_layout_t {
unsigned int headersize;
unsigned int version;
unsigned int divemode;
unsigned int samplerate;
unsigned int salinity;
unsigned int datetime;
unsigned int divetime;
unsigned int maxdepth;
unsigned int temperature_min;
unsigned int avgdepth;
unsigned int firmware;
unsigned int temperature_surf;
unsigned int atmospheric;
unsigned int gf;
} deepsix_excursion_layout_t;
typedef struct deepsix_excursion_gasmix_t {
unsigned int id;
unsigned int oxygen;
unsigned int helium;
} deepsix_excursion_gasmix_t;
typedef struct deepsix_excursion_sample_info_t {
unsigned int type;
unsigned int divisor;
unsigned int size;
} deepsix_excursion_sample_info_t;
typedef struct deepsix_excursion_event_info_t {
unsigned int type;
unsigned int size;
} deepsix_excursion_event_info_t;
typedef struct deepsix_excursion_parser_t {
dc_parser_t base;
unsigned int cached;
unsigned int ngasmixes;
deepsix_excursion_gasmix_t gasmix[MAX_GASMIXES];
} deepsix_excursion_parser_t;
static dc_status_t deepsix_excursion_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime);
static dc_status_t deepsix_excursion_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value);
static dc_status_t deepsix_excursion_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata);
static dc_status_t deepsix_excursion_parser_samples_foreach_v0 (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata);
static dc_status_t deepsix_excursion_parser_samples_foreach_v1 (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata);
static const dc_parser_vtable_t deepsix_parser_vtable = {
sizeof(deepsix_excursion_parser_t),
DC_FAMILY_DEEPSIX_EXCURSION,
NULL, /* set_clock */
NULL, /* set_atmospheric */
NULL, /* set_density */
deepsix_excursion_parser_get_datetime, /* datetime */
deepsix_excursion_parser_get_field, /* fields */
deepsix_excursion_parser_samples_foreach, /* samples_foreach */
NULL /* destroy */
};
static const deepsix_excursion_layout_t deepsix_excursion_layout_v0 = {
156,/* headersize */
UNDEFINED, /* version */
4, /* divemode */
24, /* samplerate */
UNDEFINED, /* salinity */
12, /* datetime */
20, /* divetime */
28, /* maxdepth */
32, /* temperature_min */
UNDEFINED, /* avgdepth */
48, /* firmware */
UNDEFINED, /* temperature_surf */
56, /* atmospheric */
UNDEFINED, /* gf */
};
static const deepsix_excursion_layout_t deepsix_excursion_layout_v1 = {
129,/* headersize */
3, /* version */
4, /* divemode */
5, /* samplerate */
7, /* salinity */
12, /* datetime */
19, /* divetime */
29, /* maxdepth */
31, /* temperature_min */
33, /* avgdepth */
35, /* firmware */
43, /* temperature_surf */
45, /* atmospheric */
127, /* gf */
};
static double
pressure_to_depth(unsigned int depth, unsigned int atmospheric, unsigned int density)
{
return ((signed int)(depth - atmospheric)) * (BAR / 1000.0) / (density * GRAVITY);
}
static unsigned int
deepsix_excursion_find_gasmix(deepsix_excursion_parser_t *parser, unsigned int o2, unsigned int he, unsigned int id)
{
unsigned int i = 0;
while (i < parser->ngasmixes) {
if (o2 == parser->gasmix[i].oxygen && he == parser->gasmix[i].helium && id == parser->gasmix[i].id)
break;
i++;
}
return i;
}
dc_status_t
deepsix_excursion_parser_create (dc_parser_t **out, dc_context_t *context, const unsigned char data[], size_t size)
{
deepsix_excursion_parser_t *parser = NULL;
if (out == NULL)
return DC_STATUS_INVALIDARGS;
// Allocate memory.
parser = (deepsix_excursion_parser_t *) dc_parser_allocate (context, &deepsix_parser_vtable, data, size);
if (parser == NULL) {
ERROR (context, "Failed to allocate memory.");
return DC_STATUS_NOMEMORY;
}
// Set the default values.
parser->cached = 0;
parser->ngasmixes = 0;
for (unsigned int i = 0; i < MAX_GASMIXES; ++i) {
parser->gasmix[i].id = 0;
parser->gasmix[i].oxygen = 0;
parser->gasmix[i].helium = 0;
}
*out = (dc_parser_t *) parser;
return DC_STATUS_SUCCESS;
}
static dc_status_t
deepsix_excursion_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime)
{
const unsigned char *data = abstract->data;
unsigned int size = abstract->size;
if (size < HEADERSIZE_MIN)
return DC_STATUS_DATAFORMAT;
unsigned int version = data[3];
const deepsix_excursion_layout_t *layout = version == 0 ?
&deepsix_excursion_layout_v0 : &deepsix_excursion_layout_v1;
if (size < layout->headersize)
return DC_STATUS_DATAFORMAT;
unsigned int firmware = array_uint16_be (data + layout->firmware + 4);
const unsigned char *p = data + layout->datetime;
if (datetime) {
datetime->year = p[0] + 2000;
datetime->month = p[1];
datetime->day = p[2];
datetime->hour = p[3];
datetime->minute = p[4];
datetime->second = p[5];
if (version == 0) {
if (firmware >= FWVERSION(5, 'B')) {
datetime->timezone = (p[6] - 12) * 3600;
} else {
datetime->timezone = DC_TIMEZONE_NONE;
}
} else {
datetime->timezone = ((signed char) p[6]) * 900;
}
}
return DC_STATUS_SUCCESS;
}
static dc_status_t
deepsix_excursion_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value)
{
deepsix_excursion_parser_t *parser = (deepsix_excursion_parser_t *) abstract;
const unsigned char *data = abstract->data;
unsigned int size = abstract->size;
dc_gasmix_t *gasmix = (dc_gasmix_t *) value;
dc_salinity_t *water = (dc_salinity_t *) value;
dc_decomodel_t *decomodel = (dc_decomodel_t *) value;
if (size < HEADERSIZE_MIN)
return DC_STATUS_DATAFORMAT;
unsigned int version = data[3];
const deepsix_excursion_layout_t *layout = version == 0 ?
&deepsix_excursion_layout_v0 : &deepsix_excursion_layout_v1;
if (size < layout->headersize)
return DC_STATUS_DATAFORMAT;
if (version != 0 && !parser->cached) {
dc_status_t rc = deepsix_excursion_parser_samples_foreach_v1(abstract, NULL, NULL);
if (rc != DC_STATUS_SUCCESS)
return rc;
}
unsigned int atmospheric = array_uint16_le(data + layout->atmospheric);
unsigned int density = DENSITY;
if (layout->salinity != UNDEFINED) {
density = 1000 + data[layout->salinity] * 10;
}
if (value) {
switch (type) {
case DC_FIELD_DIVETIME:
*((unsigned int *) value) = array_uint32_le(data + layout->divetime);
break;
case DC_FIELD_MAXDEPTH:
*((double *) value) = pressure_to_depth(array_uint16_le(data + layout->maxdepth), atmospheric, density);
break;
case DC_FIELD_AVGDEPTH:
if (layout->avgdepth == UNDEFINED)
return DC_STATUS_UNSUPPORTED;
*((double *) value) = pressure_to_depth(array_uint16_le(data + layout->avgdepth), atmospheric, density);
break;
case DC_FIELD_GASMIX_COUNT:
*((unsigned int *) value) = parser->ngasmixes;
break;
case DC_FIELD_GASMIX:
gasmix->usage = DC_USAGE_NONE;
gasmix->oxygen = parser->gasmix[flags].oxygen / 100.0;
gasmix->helium = parser->gasmix[flags].helium / 100.0;
gasmix->nitrogen = 1.0 - gasmix->oxygen - gasmix->helium;
break;
case DC_FIELD_TEMPERATURE_MINIMUM:
*((double *) value) = (signed int) array_uint16_le(data + layout->temperature_min) / 10.0;
break;
case DC_FIELD_TEMPERATURE_SURFACE:
if (layout->temperature_surf == UNDEFINED)
return DC_STATUS_UNSUPPORTED;
*((double *) value) = (signed int) array_uint16_le(data + layout->temperature_surf) / 10.0;
break;
case DC_FIELD_ATMOSPHERIC:
*((double *) value) = atmospheric / 1000.0;
break;
case DC_FIELD_SALINITY:
water->type = (density == 1000) ? DC_WATER_FRESH : DC_WATER_SALT;
water->density = density;
break;
case DC_FIELD_DIVEMODE:
switch (data[layout->divemode]) {
case 0:
*((dc_divemode_t *) value) = DC_DIVEMODE_OC;
break;
case 1:
*((dc_divemode_t *) value) = DC_DIVEMODE_GAUGE;
break;
case 2:
*((dc_divemode_t *) value) = DC_DIVEMODE_FREEDIVE;
break;
default:
return DC_STATUS_DATAFORMAT;
}
break;
case DC_FIELD_DECOMODEL:
decomodel->type = DC_DECOMODEL_BUHLMANN;
decomodel->conservatism = 0;
if (layout->gf != UNDEFINED) {
decomodel->params.gf.low = data[layout->gf + 0];
decomodel->params.gf.high = data[layout->gf + 1];
} else {
decomodel->params.gf.low = 0;
decomodel->params.gf.high = 0;
}
break;
default:
return DC_STATUS_UNSUPPORTED;
}
}
return DC_STATUS_SUCCESS;
}
static dc_status_t
deepsix_excursion_parser_samples_foreach_v0 (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata)
{
const unsigned char *data = abstract->data;
unsigned int size = abstract->size;
const deepsix_excursion_layout_t *layout = &deepsix_excursion_layout_v0;
if (size < layout->headersize)
return DC_STATUS_DATAFORMAT;
int firmware4c = memcmp(data + layout->firmware, "D01-4C", 6) == 0;
unsigned int maxtype = firmware4c ? TEMPERATURE : CNS;
unsigned int interval = array_uint32_le(data + layout->samplerate);
unsigned int atmospheric = array_uint32_le(data + layout->atmospheric);
unsigned int time = 0;
unsigned int offset = layout->headersize;
while (offset + 1 < size) {
dc_sample_value_t sample = {0};
// Get the sample type.
unsigned int type = data[offset];
if (type < 1 || type > maxtype) {
ERROR (abstract->context, "Unknown sample type (%u).", type);
return DC_STATUS_DATAFORMAT;
}
// Get the sample length.
unsigned int length = 1;
if (type == ALARM || type == CEILING) {
length = 8;
} else if (type == TEMPERATURE || type == DECO || type == CNS) {
length = 6;
}
// Verify the length.
if (offset + length > size) {
WARNING (abstract->context, "Unexpected end of data.");
break;
}
unsigned int misc = data[offset + 1];
unsigned int depth = array_uint16_le(data + offset + 2);
if (type == TEMPERATURE) {
time += interval;
sample.time = time * 1000;
if (callback) callback(DC_SAMPLE_TIME, &sample, userdata);
sample.depth = pressure_to_depth(depth, atmospheric, DENSITY);
if (callback) callback(DC_SAMPLE_DEPTH, &sample, userdata);
}
if (type == ALARM) {
unsigned int alarm_time = array_uint16_le(data + offset + 4);
unsigned int alarm_value = array_uint16_le(data + offset + 6);
} else if (type == TEMPERATURE) {
unsigned int temperature = array_uint16_le(data + offset + 4);
if (firmware4c) {
if (temperature > 1300) {
length = 8;
} else if (temperature >= 10) {
sample.temperature = temperature / 10.0;
if (callback) callback(DC_SAMPLE_TEMPERATURE, &sample, userdata);
}
} else {
sample.temperature = temperature / 10.0;
if (callback) callback(DC_SAMPLE_TEMPERATURE, &sample, userdata);
}
} else if (type == DECO) {
unsigned int deco = array_uint16_le(data + offset + 4);
} else if (type == CEILING) {
unsigned int ceiling_depth = array_uint16_le(data + offset + 4);
unsigned int ceiling_time = array_uint16_le(data + offset + 6);
} else if (type == CNS) {
unsigned int cns = array_uint16_le(data + offset + 4);
sample.cns = cns / 100.0;
if (callback) callback(DC_SAMPLE_CNS, &sample, userdata);
}
offset += length;
}
return DC_STATUS_SUCCESS;
}
static dc_status_t
deepsix_excursion_parser_samples_foreach_v1 (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata)
{
deepsix_excursion_parser_t *parser = (deepsix_excursion_parser_t *) abstract;
const unsigned char *data = abstract->data;
unsigned int size = abstract->size;
const deepsix_excursion_layout_t *layout = &deepsix_excursion_layout_v1;
if (size < layout->headersize)
return DC_STATUS_DATAFORMAT;
unsigned int headersize = data[2];
if (headersize < layout->headersize)
return DC_STATUS_DATAFORMAT;
unsigned int samplerate = data[layout->samplerate];
unsigned int atmospheric = array_uint16_le(data + layout->atmospheric);
unsigned int density = 1000 + data[layout->salinity] * 10;
unsigned int offset = headersize;
if (offset + 1 > size) {
ERROR (abstract->context, "Buffer overflow detected!");
return DC_STATUS_DATAFORMAT;
}
unsigned int nconfig = data[offset];
if (nconfig > MAX_SAMPLES) {
ERROR(abstract->context, "Too many sample descriptors (%u).", nconfig);
return DC_STATUS_DATAFORMAT;
}
offset += 1;
if (offset + 3 * nconfig > size) {
ERROR (abstract->context, "Buffer overflow detected!");
return DC_STATUS_DATAFORMAT;
}
deepsix_excursion_sample_info_t sample_info[MAX_SAMPLES] = {{0}};
for (unsigned int i = 0; i < nconfig; i++) {
sample_info[i].type = data[offset + 3 * i + 0];
sample_info[i].size = data[offset + 3 * i + 1];
sample_info[i].divisor = data[offset + 3 * i + 2];
if (sample_info[i].divisor) {
switch (sample_info[i].type) {
case SAMPLE_CNS:
case SAMPLE_TEMPERATURE:
if (sample_info[i].size != 2) {
ERROR(abstract->context, "Unexpected sample size (%u).", sample_info[i].size);
return DC_STATUS_DATAFORMAT;
}
break;
case SAMPLE_DECO_NDL:
if (sample_info[i].size != 7) {
ERROR(abstract->context, "Unexpected sample size (%u).", sample_info[i].size);
return DC_STATUS_DATAFORMAT;
}
break;
default:
WARNING (abstract->context, "Unknown sample descriptor (%u %u %u).",
sample_info[i].type, sample_info[i].size, sample_info[i].divisor);
break;
}
}
}
offset += 3 * nconfig;
if (offset + 1 > size) {
ERROR (abstract->context, "Buffer overflow detected!");
return DC_STATUS_DATAFORMAT;
}
unsigned int nevents = data[offset];
if (nevents > MAX_EVENTS) {
ERROR(abstract->context, "Too many event descriptors (%u).", nevents);
return DC_STATUS_DATAFORMAT;
}
offset += 1;
if (offset + 2 * nevents > size) {
ERROR (abstract->context, "Buffer overflow detected!");
return DC_STATUS_DATAFORMAT;
}
deepsix_excursion_event_info_t event_info[MAX_EVENTS] = {{0}};
for (unsigned int i = 0; i < nevents; i++) {
event_info[i].type = data[offset + 2 * i];
event_info[i].size = data[offset + 2 * i + 1];
switch (event_info[i].type) {
case EVENT_ALARMS:
if (event_info[i].size != 1) {
ERROR(abstract->context, "Unexpected event size (%u).", event_info[i].size);
return DC_STATUS_DATAFORMAT;
}
break;
case EVENT_CHANGE_GAS:
if (event_info[i].size != 3) {
ERROR(abstract->context, "Unexpected event size (%u).", event_info[i].size);
return DC_STATUS_DATAFORMAT;
}
break;
case EVENT_SAMPLES_MISSED:
if (event_info[i].size != 6) {
ERROR(abstract->context, "Unexpected event size (%u).", event_info[i].size);
return DC_STATUS_DATAFORMAT;
}
break;
default:
WARNING (abstract->context, "Unknown event descriptor (%u %u).",
event_info[i].type, event_info[i].size);
break;
}
}
offset += 2 * nevents;
unsigned int time = 0;
unsigned int nsamples = 0;
while (offset + 3 <= size) {
dc_sample_value_t sample = {0};
nsamples++;
// Time (seconds).
time += samplerate;
sample.time = time * 1000;
if (callback) callback (DC_SAMPLE_TIME, &sample, userdata);
unsigned int depth = array_uint16_le (data + offset);
sample.depth = pressure_to_depth(depth, atmospheric, density);
if (callback) callback (DC_SAMPLE_DEPTH, &sample, userdata);
offset += 2;
// event info
unsigned int length = data[offset];
offset += 1;
if (offset + length > size) {
ERROR (abstract->context, "Buffer overflow detected!");
return DC_STATUS_DATAFORMAT;
}
if (length) {
if (length < 2) {
ERROR (abstract->context, "Buffer overflow detected!");
return DC_STATUS_DATAFORMAT;
}
unsigned int events = array_uint16_le (data + offset);
unsigned int event_offset = 2;
for (unsigned int i = 0; i < nevents; i++) {
if ((events & (1 << event_info[i].type)) == 0)
continue;
if (event_offset + event_info[i].size > length) {
ERROR (abstract->context, "Buffer overflow detected!");
return DC_STATUS_DATAFORMAT;
}
unsigned int alarms = 0;
unsigned int id = 0, o2 = 0, he = 0;
unsigned int mix_idx = 0;
unsigned int count = 0, timestamp = 0;
switch (event_info[i].type) {
case EVENT_ALARMS:
alarms = data[offset + event_offset];
for (unsigned int v = alarms, j = 0; v; v >>= 1, ++j) {
if ((v & 1) == 0)
continue;
sample.event.type = SAMPLE_EVENT_NONE;
sample.event.time = 0;
sample.event.flags = 0;
sample.event.value = 0;
switch (j) {
case ALARM_ASCENTRATE:
sample.event.type = SAMPLE_EVENT_ASCENT;
break;
case ALARM_CEILING:
sample.event.type = SAMPLE_EVENT_CEILING;
break;
case ALARM_PO2:
sample.event.type = SAMPLE_EVENT_PO2;
break;
case ALARM_MAXDEPTH:
sample.event.type = SAMPLE_EVENT_MAXDEPTH;
break;
case ALARM_DIVETIME:
sample.event.type = SAMPLE_EVENT_DIVETIME;
break;
case ALARM_CNS:
break;
default:
WARNING (abstract->context, "Unknown event (%u).", j);
break;
}
if (sample.event.type != SAMPLE_EVENT_NONE) {
if (callback) callback (DC_SAMPLE_EVENT, &sample, userdata);
}
}
break;
case EVENT_CHANGE_GAS:
id = data[offset + event_offset];
o2 = data[offset + event_offset + 1];
he = data[offset + event_offset + 2];
mix_idx = deepsix_excursion_find_gasmix(parser, o2, he, id);
if (mix_idx >= parser->ngasmixes) {
if (mix_idx >= MAX_GASMIXES) {
ERROR (abstract->context, "Maximum number of gas mixes reached.");
return DC_STATUS_NOMEMORY;
}
parser->gasmix[mix_idx].oxygen = o2;
parser->gasmix[mix_idx].helium = he;
parser->gasmix[mix_idx].id = id;
parser->ngasmixes = mix_idx + 1;
}
sample.gasmix = mix_idx;
if (callback) callback(DC_SAMPLE_GASMIX, &sample, userdata);
break;
case EVENT_SAMPLES_MISSED:
count = array_uint16_le(data + offset + event_offset);
timestamp = array_uint32_le(data + offset + event_offset + 2);
if (timestamp < time) {
ERROR (abstract->context, "Timestamp moved backwards (%u %u).", timestamp, time);
return DC_STATUS_DATAFORMAT;
}
nsamples += count;
time = timestamp;
break;
default:
WARNING (abstract->context, "Unknown event (%u %u).",
event_info[i].type, event_info[i].size);
break;
}
event_offset += event_info[i].size;
}
// Skip remaining sample bytes (if any).
if (event_offset < length) {
WARNING (abstract->context, "Remaining %u bytes skipped.", length - event_offset);
}
offset += length;
}
for (unsigned int i = 0; i < nconfig; ++i) {
if (sample_info[i].divisor && (nsamples % sample_info[i].divisor) == 0) {
if (offset + sample_info[i].size > size) {
ERROR (abstract->context, "Buffer overflow detected!");
return DC_STATUS_DATAFORMAT;
}
unsigned int value = 0;
unsigned int deco_flags = 0, deco_ndl_tts = 0;
unsigned int deco_depth = 0, deco_time = 0;
switch (sample_info[i].type) {
case SAMPLE_TEMPERATURE:
value = array_uint16_le(data + offset);
sample.temperature = value / 10.0;
if (callback) callback(DC_SAMPLE_TEMPERATURE, &sample, userdata);
break;
case SAMPLE_CNS:
value = array_uint16_le(data + offset);
sample.cns = value / 10000.0;
if (callback) callback (DC_SAMPLE_CNS, &sample, userdata);
break;
case SAMPLE_DECO_NDL:
deco_flags = data[offset];
deco_ndl_tts = array_uint16_le(data + offset + 1);
deco_depth = array_uint16_le(data + offset + 3);
deco_time = array_uint16_le(data + offset + 5);
if (deco_flags & DECOSTOP) {
sample.deco.type = DC_DECO_DECOSTOP;
sample.deco.depth = pressure_to_depth(deco_depth, atmospheric, density);
sample.deco.time = deco_time;
} else if (deco_flags & SAFETYSTOP) {
sample.deco.type = DC_DECO_SAFETYSTOP;
sample.deco.depth = pressure_to_depth(deco_depth, atmospheric, density);
sample.deco.time = deco_time;
} else {
sample.deco.type = DC_DECO_NDL;
sample.deco.depth = 0;
sample.deco.time = deco_ndl_tts;
}
if (callback) callback (DC_SAMPLE_DECO, &sample, userdata);
break;
default:
break;
}
offset += sample_info[i].size;
}
}
}
parser->cached = 1;
return DC_STATUS_SUCCESS;
}
static dc_status_t
deepsix_excursion_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata)
{
const unsigned char *data = abstract->data;
unsigned int size = abstract->size;
if (size < HEADERSIZE_MIN)
return DC_STATUS_DATAFORMAT;
unsigned int version = data[3];
if (version == 0) {
return deepsix_excursion_parser_samples_foreach_v0(abstract, callback, userdata);
} else {
return deepsix_excursion_parser_samples_foreach_v1(abstract, callback, userdata);
}
}

View File

@ -23,7 +23,10 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include "descriptor-private.h" #include <libdivecomputer/descriptor.h>
#include <libdivecomputer/usbhid.h>
#include <libdivecomputer/usb.h>
#include "iterator-private.h" #include "iterator-private.h"
#include "platform.h" #include "platform.h"
@ -36,32 +39,26 @@
values, \ values, \
C_ARRAY_SIZE(values) - isnullterminated, \ C_ARRAY_SIZE(values) - isnullterminated, \
C_ARRAY_ITEMSIZE(values), \ C_ARRAY_ITEMSIZE(values), \
match, \ match)
NULL, NULL, 0)
#define DC_FILTER_INTERNAL_WITH_PARAMS(key, values, isnullterminated, match, params_dst, params_src) \
dc_filter_internal( \
key, \
values, \
C_ARRAY_SIZE(values) - isnullterminated, \
C_ARRAY_ITEMSIZE(values), \
match, \
params_dst, params_src, sizeof *(params_src))
typedef int (*dc_match_t)(const void *, const void *); typedef int (*dc_match_t)(const void *, const void *);
typedef int (*dc_filter_t) (dc_transport_t transport, const void *userdata, void *params); typedef int (*dc_filter_t) (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata);
static int dc_filter_uwatec (dc_transport_t transport, const void *userdata, void *params); static int dc_filter_uwatec (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata);
static int dc_filter_suunto (dc_transport_t transport, const void *userdata, void *params); static int dc_filter_suunto (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata);
static int dc_filter_shearwater (dc_transport_t transport, const void *userdata, void *params); static int dc_filter_shearwater (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata);
static int dc_filter_hw (dc_transport_t transport, const void *userdata, void *params); static int dc_filter_hw (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata);
static int dc_filter_tecdiving (dc_transport_t transport, const void *userdata, void *params); static int dc_filter_tecdiving (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata);
static int dc_filter_mares (dc_transport_t transport, const void *userdata, void *params); static int dc_filter_mares (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata);
static int dc_filter_divesystem (dc_transport_t transport, const void *userdata, void *params); static int dc_filter_divesystem (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata);
static int dc_filter_oceanic (dc_transport_t transport, const void *userdata, void *params); static int dc_filter_oceanic (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata);
static int dc_filter_mclean (dc_transport_t transport, const void *userdata, void *params); static int dc_filter_mclean (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata);
static int dc_filter_atomic (dc_transport_t transport, const void *userdata, void *params); static int dc_filter_atomic (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata);
static int dc_filter_deepsix (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata);
static int dc_filter_deepblu (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata);
static int dc_filter_oceans (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata);
static int dc_filter_divesoft (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata);
static dc_status_t dc_descriptor_iterator_next (dc_iterator_t *iterator, void *item); static dc_status_t dc_descriptor_iterator_next (dc_iterator_t *iterator, void *item);
@ -170,9 +167,13 @@ static const dc_descriptor_t g_descriptors[] = {
{"Scubapro", "Aladin A1", DC_FAMILY_UWATEC_SMART, 0x25, DC_TRANSPORT_BLE, dc_filter_uwatec}, {"Scubapro", "Aladin A1", DC_FAMILY_UWATEC_SMART, 0x25, DC_TRANSPORT_BLE, dc_filter_uwatec},
{"Scubapro", "Mantis 2", DC_FAMILY_UWATEC_SMART, 0x26, DC_TRANSPORT_SERIAL, NULL}, {"Scubapro", "Mantis 2", DC_FAMILY_UWATEC_SMART, 0x26, DC_TRANSPORT_SERIAL, NULL},
{"Scubapro", "Aladin A2", DC_FAMILY_UWATEC_SMART, 0x28, DC_TRANSPORT_BLE, dc_filter_uwatec}, {"Scubapro", "Aladin A2", DC_FAMILY_UWATEC_SMART, 0x28, DC_TRANSPORT_BLE, dc_filter_uwatec},
{"Scubapro", "G2 TEK", DC_FAMILY_UWATEC_SMART, 0x31, DC_TRANSPORT_USBHID | DC_TRANSPORT_BLE, dc_filter_uwatec},
{"Scubapro", "G2", DC_FAMILY_UWATEC_SMART, 0x32, DC_TRANSPORT_USBHID | DC_TRANSPORT_BLE, dc_filter_uwatec}, {"Scubapro", "G2", DC_FAMILY_UWATEC_SMART, 0x32, DC_TRANSPORT_USBHID | DC_TRANSPORT_BLE, dc_filter_uwatec},
{"Scubapro", "G2 Console", DC_FAMILY_UWATEC_SMART, 0x32, DC_TRANSPORT_USBHID | DC_TRANSPORT_BLE, dc_filter_uwatec}, {"Scubapro", "G2 Console", DC_FAMILY_UWATEC_SMART, 0x32, DC_TRANSPORT_USBHID | DC_TRANSPORT_BLE, dc_filter_uwatec},
{"Scubapro", "G3", DC_FAMILY_UWATEC_SMART, 0x34, DC_TRANSPORT_USBHID | DC_TRANSPORT_BLE, dc_filter_uwatec},
{"Scubapro", "G2 HUD", DC_FAMILY_UWATEC_SMART, 0x42, DC_TRANSPORT_USBHID | DC_TRANSPORT_BLE, dc_filter_uwatec}, {"Scubapro", "G2 HUD", DC_FAMILY_UWATEC_SMART, 0x42, DC_TRANSPORT_USBHID | DC_TRANSPORT_BLE, dc_filter_uwatec},
{"Scubapro", "Luna 2.0 AI", DC_FAMILY_UWATEC_SMART, 0x50, DC_TRANSPORT_BLE, dc_filter_uwatec},
{"Scubapro", "Luna 2.0", DC_FAMILY_UWATEC_SMART, 0x51, DC_TRANSPORT_BLE, dc_filter_uwatec},
/* Reefnet */ /* Reefnet */
{"Reefnet", "Sensus", DC_FAMILY_REEFNET_SENSUS, 1, DC_TRANSPORT_SERIAL, NULL}, {"Reefnet", "Sensus", DC_FAMILY_REEFNET_SENSUS, 1, DC_TRANSPORT_SERIAL, NULL},
{"Reefnet", "Sensus Pro", DC_FAMILY_REEFNET_SENSUSPRO, 2, DC_TRANSPORT_SERIAL, NULL}, {"Reefnet", "Sensus Pro", DC_FAMILY_REEFNET_SENSUSPRO, 2, DC_TRANSPORT_SERIAL, NULL},
@ -251,7 +252,7 @@ static const dc_descriptor_t g_descriptors[] = {
{"Sherwood", "Vision", DC_FAMILY_OCEANIC_ATOM2, 0x4556, DC_TRANSPORT_SERIAL, NULL}, {"Sherwood", "Vision", DC_FAMILY_OCEANIC_ATOM2, 0x4556, DC_TRANSPORT_SERIAL, NULL},
{"Oceanic", "VTX", DC_FAMILY_OCEANIC_ATOM2, 0x4557, DC_TRANSPORT_SERIAL, NULL}, {"Oceanic", "VTX", DC_FAMILY_OCEANIC_ATOM2, 0x4557, DC_TRANSPORT_SERIAL, NULL},
{"Aqualung", "i300", DC_FAMILY_OCEANIC_ATOM2, 0x4559, DC_TRANSPORT_SERIAL, NULL}, {"Aqualung", "i300", DC_FAMILY_OCEANIC_ATOM2, 0x4559, DC_TRANSPORT_SERIAL, NULL},
{"Aqualung", "i750TC", DC_FAMILY_OCEANIC_ATOM2, 0x455A, DC_TRANSPORT_SERIAL, NULL}, {"Aqualung", "i750TC", DC_FAMILY_OCEANIC_ATOM2, 0x455A, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_oceanic},
{"Aqualung", "i450T", DC_FAMILY_OCEANIC_ATOM2, 0x4641, DC_TRANSPORT_SERIAL, NULL}, {"Aqualung", "i450T", DC_FAMILY_OCEANIC_ATOM2, 0x4641, DC_TRANSPORT_SERIAL, NULL},
{"Aqualung", "i550", DC_FAMILY_OCEANIC_ATOM2, 0x4642, DC_TRANSPORT_SERIAL, NULL}, {"Aqualung", "i550", DC_FAMILY_OCEANIC_ATOM2, 0x4642, DC_TRANSPORT_SERIAL, NULL},
{"Aqualung", "i200", DC_FAMILY_OCEANIC_ATOM2, 0x4646, DC_TRANSPORT_SERIAL, NULL}, {"Aqualung", "i200", DC_FAMILY_OCEANIC_ATOM2, 0x4646, DC_TRANSPORT_SERIAL, NULL},
@ -266,8 +267,14 @@ static const dc_descriptor_t g_descriptors[] = {
{"Sherwood", "Wisdom 4", DC_FAMILY_OCEANIC_ATOM2, 0x4655, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_oceanic}, {"Sherwood", "Wisdom 4", DC_FAMILY_OCEANIC_ATOM2, 0x4655, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_oceanic},
{"Oceanic", "Pro Plus 4", DC_FAMILY_OCEANIC_ATOM2, 0x4656, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_oceanic}, {"Oceanic", "Pro Plus 4", DC_FAMILY_OCEANIC_ATOM2, 0x4656, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_oceanic},
{"Sherwood", "Amphos 2.0", DC_FAMILY_OCEANIC_ATOM2, 0x4657, DC_TRANSPORT_SERIAL, NULL}, {"Sherwood", "Amphos 2.0", DC_FAMILY_OCEANIC_ATOM2, 0x4657, DC_TRANSPORT_SERIAL, NULL},
{"Sherwood", "Amphos Air 2.0", DC_FAMILY_OCEANIC_ATOM2, 0x4658, DC_TRANSPORT_SERIAL, NULL},
{"Sherwood", "Beacon", DC_FAMILY_OCEANIC_ATOM2, 0x4742, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_oceanic}, {"Sherwood", "Beacon", DC_FAMILY_OCEANIC_ATOM2, 0x4742, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_oceanic},
{"Aqualung", "i470TC", DC_FAMILY_OCEANIC_ATOM2, 0x4743, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_oceanic}, {"Aqualung", "i470TC", DC_FAMILY_OCEANIC_ATOM2, 0x4743, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_oceanic},
{"Aqualung", "i200C", DC_FAMILY_OCEANIC_ATOM2, 0x4749, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_oceanic},
{"Oceanic", "Geo Air", DC_FAMILY_OCEANIC_ATOM2, 0x474B, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_oceanic},
/* Pelagic I330R */
{"Apeks", "DSX", DC_FAMILY_PELAGIC_I330R, 0x4741, DC_TRANSPORT_BLE, dc_filter_oceanic},
{"Aqualung", "i330R", DC_FAMILY_PELAGIC_I330R, 0x4744, DC_TRANSPORT_BLE, dc_filter_oceanic},
/* Mares Nemo */ /* Mares Nemo */
{"Mares", "Nemo", DC_FAMILY_MARES_NEMO, 0, DC_TRANSPORT_SERIAL, NULL}, {"Mares", "Nemo", DC_FAMILY_MARES_NEMO, 0, DC_TRANSPORT_SERIAL, NULL},
{"Mares", "Nemo Steel", DC_FAMILY_MARES_NEMO, 0, DC_TRANSPORT_SERIAL, NULL}, {"Mares", "Nemo Steel", DC_FAMILY_MARES_NEMO, 0, DC_TRANSPORT_SERIAL, NULL},
@ -292,6 +299,7 @@ static const dc_descriptor_t g_descriptors[] = {
{"Mares", "Icon HD", DC_FAMILY_MARES_ICONHD , 0x14, DC_TRANSPORT_SERIAL, NULL}, {"Mares", "Icon HD", DC_FAMILY_MARES_ICONHD , 0x14, DC_TRANSPORT_SERIAL, NULL},
{"Mares", "Icon HD Net Ready", DC_FAMILY_MARES_ICONHD , 0x15, DC_TRANSPORT_SERIAL, NULL}, {"Mares", "Icon HD Net Ready", DC_FAMILY_MARES_ICONHD , 0x15, DC_TRANSPORT_SERIAL, NULL},
{"Mares", "Puck Pro", DC_FAMILY_MARES_ICONHD , 0x18, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_mares}, {"Mares", "Puck Pro", DC_FAMILY_MARES_ICONHD , 0x18, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_mares},
{"Mares", "Puck Pro +", DC_FAMILY_MARES_ICONHD , 0x18, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_mares},
{"Mares", "Nemo Wide 2", DC_FAMILY_MARES_ICONHD , 0x19, DC_TRANSPORT_SERIAL, NULL}, {"Mares", "Nemo Wide 2", DC_FAMILY_MARES_ICONHD , 0x19, DC_TRANSPORT_SERIAL, NULL},
{"Mares", "Genius", DC_FAMILY_MARES_ICONHD , 0x1C, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_mares}, {"Mares", "Genius", DC_FAMILY_MARES_ICONHD , 0x1C, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_mares},
{"Mares", "Puck 2", DC_FAMILY_MARES_ICONHD , 0x1F, DC_TRANSPORT_SERIAL, NULL}, {"Mares", "Puck 2", DC_FAMILY_MARES_ICONHD , 0x1F, DC_TRANSPORT_SERIAL, NULL},
@ -299,6 +307,10 @@ static const dc_descriptor_t g_descriptors[] = {
{"Mares", "Smart Air", DC_FAMILY_MARES_ICONHD , 0x24, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_mares}, {"Mares", "Smart Air", DC_FAMILY_MARES_ICONHD , 0x24, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_mares},
{"Mares", "Quad", DC_FAMILY_MARES_ICONHD , 0x29, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_mares}, {"Mares", "Quad", DC_FAMILY_MARES_ICONHD , 0x29, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_mares},
{"Mares", "Horizon", DC_FAMILY_MARES_ICONHD , 0x2C, DC_TRANSPORT_SERIAL, NULL}, {"Mares", "Horizon", DC_FAMILY_MARES_ICONHD , 0x2C, DC_TRANSPORT_SERIAL, NULL},
{"Mares", "Puck Air 2", DC_FAMILY_MARES_ICONHD , 0x2D, DC_TRANSPORT_BLE, dc_filter_mares},
{"Mares", "Sirius", DC_FAMILY_MARES_ICONHD , 0x2F, DC_TRANSPORT_BLE, dc_filter_mares},
{"Mares", "Quad Ci", DC_FAMILY_MARES_ICONHD , 0x31, DC_TRANSPORT_BLE, dc_filter_mares},
{"Mares", "Puck 4", DC_FAMILY_MARES_ICONHD , 0x35, DC_TRANSPORT_BLE, dc_filter_mares},
/* Heinrichs Weikamp */ /* Heinrichs Weikamp */
{"Heinrichs Weikamp", "OSTC", DC_FAMILY_HW_OSTC, 0, DC_TRANSPORT_SERIAL, NULL}, {"Heinrichs Weikamp", "OSTC", DC_FAMILY_HW_OSTC, 0, DC_TRANSPORT_SERIAL, NULL},
{"Heinrichs Weikamp", "OSTC Mk2", DC_FAMILY_HW_OSTC, 1, DC_TRANSPORT_SERIAL, NULL}, {"Heinrichs Weikamp", "OSTC Mk2", DC_FAMILY_HW_OSTC, 1, DC_TRANSPORT_SERIAL, NULL},
@ -328,6 +340,9 @@ static const dc_descriptor_t g_descriptors[] = {
/* Cressi Goa */ /* Cressi Goa */
{"Cressi", "Cartesio", DC_FAMILY_CRESSI_GOA, 1, DC_TRANSPORT_SERIAL, NULL}, {"Cressi", "Cartesio", DC_FAMILY_CRESSI_GOA, 1, DC_TRANSPORT_SERIAL, NULL},
{"Cressi", "Goa", DC_FAMILY_CRESSI_GOA, 2, DC_TRANSPORT_SERIAL, NULL}, {"Cressi", "Goa", DC_FAMILY_CRESSI_GOA, 2, DC_TRANSPORT_SERIAL, NULL},
{"Cressi", "Donatello", DC_FAMILY_CRESSI_GOA, 4, DC_TRANSPORT_SERIAL, NULL},
{"Cressi", "Michelangelo", DC_FAMILY_CRESSI_GOA, 5, DC_TRANSPORT_SERIAL, NULL},
{"Cressi", "Neon", DC_FAMILY_CRESSI_GOA, 9, DC_TRANSPORT_SERIAL, NULL},
/* Zeagle N2iTiON3 */ /* Zeagle N2iTiON3 */
{"Zeagle", "N2iTiON3", DC_FAMILY_ZEAGLE_N2ITION3, 0, DC_TRANSPORT_SERIAL, NULL}, {"Zeagle", "N2iTiON3", DC_FAMILY_ZEAGLE_N2ITION3, 0, DC_TRANSPORT_SERIAL, NULL},
{"Apeks", "Quantum X", DC_FAMILY_ZEAGLE_N2ITION3, 0, DC_TRANSPORT_SERIAL, NULL}, {"Apeks", "Quantum X", DC_FAMILY_ZEAGLE_N2ITION3, 0, DC_TRANSPORT_SERIAL, NULL},
@ -347,6 +362,9 @@ static const dc_descriptor_t g_descriptors[] = {
{"Shearwater", "Nerd 2", DC_FAMILY_SHEARWATER_PETREL, 7, DC_TRANSPORT_BLE, dc_filter_shearwater}, {"Shearwater", "Nerd 2", DC_FAMILY_SHEARWATER_PETREL, 7, DC_TRANSPORT_BLE, dc_filter_shearwater},
{"Shearwater", "Teric", DC_FAMILY_SHEARWATER_PETREL, 8, DC_TRANSPORT_BLE, dc_filter_shearwater}, {"Shearwater", "Teric", DC_FAMILY_SHEARWATER_PETREL, 8, DC_TRANSPORT_BLE, dc_filter_shearwater},
{"Shearwater", "Peregrine", DC_FAMILY_SHEARWATER_PETREL, 9, DC_TRANSPORT_BLE, dc_filter_shearwater}, {"Shearwater", "Peregrine", DC_FAMILY_SHEARWATER_PETREL, 9, DC_TRANSPORT_BLE, dc_filter_shearwater},
{"Shearwater", "Petrel 3", DC_FAMILY_SHEARWATER_PETREL, 10, DC_TRANSPORT_BLE, dc_filter_shearwater},
{"Shearwater", "Perdix 2", DC_FAMILY_SHEARWATER_PETREL, 11, DC_TRANSPORT_BLE, dc_filter_shearwater},
{"Shearwater", "Tern", DC_FAMILY_SHEARWATER_PETREL, 12, DC_TRANSPORT_BLE, dc_filter_shearwater},
/* Dive Rite NiTek Q */ /* Dive Rite NiTek Q */
{"Dive Rite", "NiTek Q", DC_FAMILY_DIVERITE_NITEKQ, 0, DC_TRANSPORT_SERIAL, NULL}, {"Dive Rite", "NiTek Q", DC_FAMILY_DIVERITE_NITEKQ, 0, DC_TRANSPORT_SERIAL, NULL},
/* Citizen Hyper Aqualand */ /* Citizen Hyper Aqualand */
@ -400,6 +418,25 @@ static const dc_descriptor_t g_descriptors[] = {
{"Ratio", "iX3M 2021 Pro Deep", DC_FAMILY_DIVESYSTEM_IDIVE, 0x73, DC_TRANSPORT_SERIAL, NULL}, {"Ratio", "iX3M 2021 Pro Deep", DC_FAMILY_DIVESYSTEM_IDIVE, 0x73, DC_TRANSPORT_SERIAL, NULL},
{"Ratio", "iX3M 2021 Pro Tech+", DC_FAMILY_DIVESYSTEM_IDIVE, 0x74, DC_TRANSPORT_SERIAL, NULL}, {"Ratio", "iX3M 2021 Pro Tech+", DC_FAMILY_DIVESYSTEM_IDIVE, 0x74, DC_TRANSPORT_SERIAL, NULL},
{"Ratio", "iX3M 2021 Pro Reb", DC_FAMILY_DIVESYSTEM_IDIVE, 0x75, DC_TRANSPORT_SERIAL, NULL}, {"Ratio", "iX3M 2021 Pro Reb", DC_FAMILY_DIVESYSTEM_IDIVE, 0x75, DC_TRANSPORT_SERIAL, NULL},
{"Ratio", "iDive 2 Free", DC_FAMILY_DIVESYSTEM_IDIVE, 0x80, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_divesystem},
{"Ratio", "iDive 2 Fancy", DC_FAMILY_DIVESYSTEM_IDIVE, 0x81, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_divesystem},
{"Ratio", "iDive 2 Easy", DC_FAMILY_DIVESYSTEM_IDIVE, 0x82, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_divesystem},
{"Ratio", "iDive 2 Pro", DC_FAMILY_DIVESYSTEM_IDIVE, 0x83, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_divesystem},
{"Ratio", "iDive 2 Deep", DC_FAMILY_DIVESYSTEM_IDIVE, 0x84, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_divesystem},
{"Ratio", "iDive 2 Tech", DC_FAMILY_DIVESYSTEM_IDIVE, 0x85, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_divesystem},
{"Ratio", "iDive 2 Reb", DC_FAMILY_DIVESYSTEM_IDIVE, 0x86, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_divesystem},
{"Ratio", "iX3M 2 GPS Gauge", DC_FAMILY_DIVESYSTEM_IDIVE, 0x90, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_divesystem},
{"Ratio", "iX3M 2 GPS Easy", DC_FAMILY_DIVESYSTEM_IDIVE, 0x91, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_divesystem},
{"Ratio", "iX3M 2 GPS Pro", DC_FAMILY_DIVESYSTEM_IDIVE, 0x92, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_divesystem},
{"Ratio", "iX3M 2 GPS Deep", DC_FAMILY_DIVESYSTEM_IDIVE, 0x93, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_divesystem},
{"Ratio", "iX3M 2 GPS Tech", DC_FAMILY_DIVESYSTEM_IDIVE, 0x94, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_divesystem},
{"Ratio", "iX3M 2 GPS Reb", DC_FAMILY_DIVESYSTEM_IDIVE, 0x95, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_divesystem},
{"Ratio", "ATOM", DC_FAMILY_DIVESYSTEM_IDIVE, 0x96, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_divesystem},
{"Ratio", "iX3M 2 Gauge", DC_FAMILY_DIVESYSTEM_IDIVE, 0x100, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_divesystem},
{"Ratio", "iX3M 2 Easy", DC_FAMILY_DIVESYSTEM_IDIVE, 0x101, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_divesystem},
{"Ratio", "iX3M 2 Pro", DC_FAMILY_DIVESYSTEM_IDIVE, 0x102, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_divesystem},
{"Ratio", "iX3M 2 Deep", DC_FAMILY_DIVESYSTEM_IDIVE, 0x103, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_divesystem},
{"Ratio", "iX3M 2 Tech+", DC_FAMILY_DIVESYSTEM_IDIVE, 0x104, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_divesystem},
{"Seac", "Jack", DC_FAMILY_DIVESYSTEM_IDIVE, 0x1000, DC_TRANSPORT_SERIAL, NULL}, {"Seac", "Jack", DC_FAMILY_DIVESYSTEM_IDIVE, 0x1000, DC_TRANSPORT_SERIAL, NULL},
{"Seac", "Guru", DC_FAMILY_DIVESYSTEM_IDIVE, 0x1002, DC_TRANSPORT_SERIAL, NULL}, {"Seac", "Guru", DC_FAMILY_DIVESYSTEM_IDIVE, 0x1002, DC_TRANSPORT_SERIAL, NULL},
/* Cochran Commander */ /* Cochran Commander */
@ -420,6 +457,21 @@ static const dc_descriptor_t g_descriptors[] = {
{"Liquivision", "Kaon", DC_FAMILY_LIQUIVISION_LYNX, 3, DC_TRANSPORT_SERIAL, NULL}, {"Liquivision", "Kaon", DC_FAMILY_LIQUIVISION_LYNX, 3, DC_TRANSPORT_SERIAL, NULL},
/* Sporasub */ /* Sporasub */
{"Sporasub", "SP2", DC_FAMILY_SPORASUB_SP2, 0, DC_TRANSPORT_SERIAL, NULL}, {"Sporasub", "SP2", DC_FAMILY_SPORASUB_SP2, 0, DC_TRANSPORT_SERIAL, NULL},
/* Deep Six Excursion */
{"Deep Six", "Excursion", DC_FAMILY_DEEPSIX_EXCURSION, 0, DC_TRANSPORT_BLE, dc_filter_deepsix},
{"Crest", "CR-4", DC_FAMILY_DEEPSIX_EXCURSION, 0, DC_TRANSPORT_BLE, dc_filter_deepsix},
{"Genesis", "Centauri", DC_FAMILY_DEEPSIX_EXCURSION, 0, DC_TRANSPORT_BLE, dc_filter_deepsix},
{"Scorpena", "Alpha", DC_FAMILY_DEEPSIX_EXCURSION, 0, DC_TRANSPORT_BLE, dc_filter_deepsix},
/* Seac Screen */
{"Seac", "Screen", DC_FAMILY_SEAC_SCREEN, 0, DC_TRANSPORT_SERIAL, NULL},
{"Seac", "Action", DC_FAMILY_SEAC_SCREEN, 0, DC_TRANSPORT_SERIAL, NULL},
/* Deepblu Cosmiq */
{"Deepblu", "Cosmiq+", DC_FAMILY_DEEPBLU_COSMIQ, 0, DC_TRANSPORT_BLE, dc_filter_deepblu},
/* Oceans S1 */
{"Oceans", "S1", DC_FAMILY_OCEANS_S1, 0, DC_TRANSPORT_BLE, dc_filter_oceans},
/* Divesoft Freedom */
{"Divesoft", "Freedom", DC_FAMILY_DIVESOFT_FREEDOM, 19, DC_TRANSPORT_BLE, dc_filter_divesoft},
{"Divesoft", "Liberty", DC_FAMILY_DIVESOFT_FREEDOM, 10, DC_TRANSPORT_BLE, dc_filter_divesoft},
}; };
static int static int
@ -458,6 +510,15 @@ dc_match_usb (const void *key, const void *value)
return k->vid == v->vid && k->pid == v->pid; return k->vid == v->vid && k->pid == v->pid;
} }
static int
dc_match_usbhid (const void *key, const void *value)
{
const dc_usbhid_desc_t *k = (const dc_usbhid_desc_t *) key;
const dc_usbhid_desc_t *v = (const dc_usbhid_desc_t *) value;
return k->vid == v->vid && k->pid == v->pid;
}
static int static int
dc_match_number_with_prefix (const void *key, const void *value) dc_match_number_with_prefix (const void *key, const void *value)
{ {
@ -498,16 +559,13 @@ dc_match_oceanic (const void *key, const void *value)
} }
static int static int
dc_filter_internal (const void *key, const void *values, size_t count, size_t size, dc_match_t match, void *params_dst, const void *params_src, size_t params_size) dc_filter_internal (const void *key, const void *values, size_t count, size_t size, dc_match_t match)
{ {
if (key == NULL) if (key == NULL)
return 1; return 1;
for (size_t i = 0; i < count; ++i) { for (size_t i = 0; i < count; ++i) {
if (match (key, (const unsigned char *) values + i * size)) { if (match (key, (const unsigned char *) values + i * size)) {
if (params_src && params_dst) {
memcpy (params_dst, params_src, params_size);
}
return 1; return 1;
} }
} }
@ -522,7 +580,8 @@ static const char * const rfcomm[] = {
NULL NULL
}; };
static int dc_filter_uwatec (dc_transport_t transport, const void *userdata, void *params) static int
dc_filter_uwatec (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata)
{ {
static const char * const irda[] = { static const char * const irda[] = {
"Aladin Smart Com", "Aladin Smart Com",
@ -533,8 +592,8 @@ static int dc_filter_uwatec (dc_transport_t transport, const void *userdata, voi
"UWATEC Galileo", "UWATEC Galileo",
"UWATEC Galileo Sol", "UWATEC Galileo Sol",
}; };
static const dc_usb_desc_t usbhid[] = { static const dc_usbhid_desc_t usbhid[] = {
{0x2e6c, 0x3201}, // G2 {0x2e6c, 0x3201}, // G2, G2 TEK
{0x2e6c, 0x3211}, // G2 Console {0x2e6c, 0x3211}, // G2 Console
{0x2e6c, 0x4201}, // G2 HUD {0x2e6c, 0x4201}, // G2 HUD
{0xc251, 0x2006}, // Aladin Square {0xc251, 0x2006}, // Aladin Square
@ -545,12 +604,16 @@ static int dc_filter_uwatec (dc_transport_t transport, const void *userdata, voi
"HUD", "HUD",
"A1", "A1",
"A2", "A2",
"G2 TEK",
"Galileo 3",
"Luna 2.0 AI",
"Luna 2.0",
}; };
if (transport == DC_TRANSPORT_IRDA) { if (transport == DC_TRANSPORT_IRDA) {
return DC_FILTER_INTERNAL (userdata, irda, 0, dc_match_name); return DC_FILTER_INTERNAL (userdata, irda, 0, dc_match_name);
} else if (transport == DC_TRANSPORT_USBHID) { } else if (transport == DC_TRANSPORT_USBHID) {
return DC_FILTER_INTERNAL (userdata, usbhid, 0, dc_match_usb); return DC_FILTER_INTERNAL (userdata, usbhid, 0, dc_match_usbhid);
} else if (transport == DC_TRANSPORT_BLE) { } else if (transport == DC_TRANSPORT_BLE) {
return DC_FILTER_INTERNAL (userdata, bluetooth, 0, dc_match_name); return DC_FILTER_INTERNAL (userdata, bluetooth, 0, dc_match_name);
} }
@ -558,9 +621,10 @@ static int dc_filter_uwatec (dc_transport_t transport, const void *userdata, voi
return 1; return 1;
} }
static int dc_filter_suunto (dc_transport_t transport, const void *userdata, void *params) static int
dc_filter_suunto (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata)
{ {
static const dc_usb_desc_t usbhid[] = { static const dc_usbhid_desc_t usbhid[] = {
{0x1493, 0x0030}, // Eon Steel {0x1493, 0x0030}, // Eon Steel
{0x1493, 0x0033}, // Eon Core {0x1493, 0x0033}, // Eon Core
{0x1493, 0x0035}, // D5 {0x1493, 0x0035}, // D5
@ -574,7 +638,7 @@ static int dc_filter_suunto (dc_transport_t transport, const void *userdata, voi
}; };
if (transport == DC_TRANSPORT_USBHID) { if (transport == DC_TRANSPORT_USBHID) {
return DC_FILTER_INTERNAL (userdata, usbhid, 0, dc_match_usb); return DC_FILTER_INTERNAL (userdata, usbhid, 0, dc_match_usbhid);
} else if (transport == DC_TRANSPORT_BLE) { } else if (transport == DC_TRANSPORT_BLE) {
return DC_FILTER_INTERNAL (userdata, bluetooth, 0, dc_match_prefix); return DC_FILTER_INTERNAL (userdata, bluetooth, 0, dc_match_prefix);
} }
@ -582,7 +646,8 @@ static int dc_filter_suunto (dc_transport_t transport, const void *userdata, voi
return 1; return 1;
} }
static int dc_filter_hw (dc_transport_t transport, const void *userdata, void *params) static int
dc_filter_hw (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata)
{ {
static const char * const bluetooth[] = { static const char * const bluetooth[] = {
"OSTC", "OSTC",
@ -598,16 +663,20 @@ static int dc_filter_hw (dc_transport_t transport, const void *userdata, void *p
return 1; return 1;
} }
static int dc_filter_shearwater (dc_transport_t transport, const void *userdata, void *params) static int
dc_filter_shearwater (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata)
{ {
static const char * const bluetooth[] = { static const char * const bluetooth[] = {
"Predator", "Predator",
"Petrel", "Petrel",
"Petrel 3",
"NERD", "NERD",
"NERD 2", "NERD 2",
"Perdix", "Perdix",
"Perdix 2",
"Teric", "Teric",
"Peregrine", "Peregrine",
"Tern"
}; };
if (transport == DC_TRANSPORT_BLUETOOTH || transport == DC_TRANSPORT_BLE) { if (transport == DC_TRANSPORT_BLUETOOTH || transport == DC_TRANSPORT_BLE) {
@ -619,7 +688,8 @@ static int dc_filter_shearwater (dc_transport_t transport, const void *userdata,
return 1; return 1;
} }
static int dc_filter_tecdiving (dc_transport_t transport, const void *userdata, void *params) static int
dc_filter_tecdiving (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata)
{ {
static const char * const bluetooth[] = { static const char * const bluetooth[] = {
"DiveComputer", "DiveComputer",
@ -634,11 +704,15 @@ static int dc_filter_tecdiving (dc_transport_t transport, const void *userdata,
return 1; return 1;
} }
static int dc_filter_mares (dc_transport_t transport, const void *userdata, void *params) static int
dc_filter_mares (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata)
{ {
static const char * const bluetooth[] = { static const char * const bluetooth[] = {
"Mares bluelink pro", "Mares bluelink pro",
"Mares Genius", "Mares Genius",
"Sirius",
"Quad Ci",
"Puck4",
}; };
if (transport == DC_TRANSPORT_BLE) { if (transport == DC_TRANSPORT_BLE) {
@ -648,11 +722,13 @@ static int dc_filter_mares (dc_transport_t transport, const void *userdata, void
return 1; return 1;
} }
static int dc_filter_divesystem (dc_transport_t transport, const void *userdata, void *params) static int
dc_filter_divesystem (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata)
{ {
static const char * const bluetooth[] = { static const char * const bluetooth[] = {
"DS", "DS",
"IX5M", "IX5M",
"RATIO-",
}; };
if (transport == DC_TRANSPORT_BLUETOOTH || transport == DC_TRANSPORT_BLE) { if (transport == DC_TRANSPORT_BLUETOOTH || transport == DC_TRANSPORT_BLE) {
@ -662,10 +738,12 @@ static int dc_filter_divesystem (dc_transport_t transport, const void *userdata,
return 1; return 1;
} }
static int dc_filter_oceanic (dc_transport_t transport, const void *userdata, void *params) static int
dc_filter_oceanic (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata)
{ {
static const unsigned int model[] = { static const unsigned int model[] = {
0x4552, // Oceanic Pro Plus X 0x4552, // Oceanic Pro Plus X
0x455A, // Aqualung i750TC
0x4647, // Sherwood Sage 0x4647, // Sherwood Sage
0x4648, // Aqualung i300C 0x4648, // Aqualung i300C
0x4649, // Aqualung i200C 0x4649, // Aqualung i200C
@ -675,8 +753,12 @@ static int dc_filter_oceanic (dc_transport_t transport, const void *userdata, vo
0x4654, // Oceanic Veo 4.0 0x4654, // Oceanic Veo 4.0
0x4655, // Sherwood Wisdom 4 0x4655, // Sherwood Wisdom 4
0x4656, // Oceanic Pro Plus 4 0x4656, // Oceanic Pro Plus 4
0x4741, // Apeks DSX
0x4742, // Sherwood Beacon 0x4742, // Sherwood Beacon
0x4743, // Aqualung i470TC 0x4743, // Aqualung i470TC
0x4744, // Aqualung i330R
0x4749, // Aqualung i200C
0x474B, // Oceanic Geo Air
}; };
if (transport == DC_TRANSPORT_BLE) { if (transport == DC_TRANSPORT_BLE) {
@ -686,7 +768,8 @@ static int dc_filter_oceanic (dc_transport_t transport, const void *userdata, vo
return 1; return 1;
} }
static int dc_filter_mclean(dc_transport_t transport, const void *userdata, void *params) static int
dc_filter_mclean(dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata)
{ {
static const char * const bluetooth[] = { static const char * const bluetooth[] = {
"McLean Extreme", "McLean Extreme",
@ -701,18 +784,75 @@ static int dc_filter_mclean(dc_transport_t transport, const void *userdata, void
return 1; return 1;
} }
static int dc_filter_atomic (dc_transport_t transport, const void *userdata, void *params) static int
dc_filter_atomic (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata)
{ {
static const dc_usb_desc_t usb[] = { static const dc_usb_desc_t usb[] = {
{0x0471, 0x0888}, // Atomic Aquatics Cobalt {0x0471, 0x0888}, // Atomic Aquatics Cobalt
}; };
static const dc_usb_params_t usb_params = { if (transport == DC_TRANSPORT_USB) {
0, 0x82, 0x02 return DC_FILTER_INTERNAL (userdata, usb, 0, dc_match_usb);
}
return 1;
}
static int
dc_filter_deepsix (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata)
{
static const char * const bluetooth[] = {
"EXCURSION",
"Crest-CR4",
"CENTAURI",
"ALPHA",
}; };
if (transport == DC_TRANSPORT_USB) { if (transport == DC_TRANSPORT_BLE) {
return DC_FILTER_INTERNAL_WITH_PARAMS (userdata, usb, 0, dc_match_usb, params, &usb_params); return DC_FILTER_INTERNAL (userdata, bluetooth, 0, dc_match_name);
}
return 1;
}
static int
dc_filter_deepblu (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata)
{
static const char * const bluetooth[] = {
"COSMIQ",
};
if (transport == DC_TRANSPORT_BLE) {
return DC_FILTER_INTERNAL (userdata, bluetooth, 0, dc_match_name);
}
return 1;
}
static int
dc_filter_oceans (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata)
{
static const char * const bluetooth[] = {
"S1",
};
if (transport == DC_TRANSPORT_BLE) {
return DC_FILTER_INTERNAL (userdata, bluetooth, 0, dc_match_prefix);
}
return 1;
}
static int
dc_filter_divesoft (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata)
{
static const char * const bluetooth[] = {
"Freedom",
"Liberty",
};
if (transport == DC_TRANSPORT_BLE) {
return DC_FILTER_INTERNAL (userdata, bluetooth, 0, dc_match_prefix);
} }
return 1; return 1;
@ -810,10 +950,10 @@ dc_descriptor_get_transports (dc_descriptor_t *descriptor)
} }
int int
dc_descriptor_filter (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata, void *params) dc_descriptor_filter (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata)
{ {
if (descriptor == NULL || descriptor->filter == NULL || userdata == NULL) if (descriptor == NULL || descriptor->filter == NULL || userdata == NULL)
return 1; return 1;
return descriptor->filter (transport, userdata, params); return descriptor->filter (descriptor, transport, userdata);
} }

View File

@ -92,7 +92,7 @@ int
device_is_cancelled (dc_device_t *device); device_is_cancelled (dc_device_t *device);
dc_status_t dc_status_t
device_dump_read (dc_device_t *device, unsigned char data[], unsigned int size, unsigned int blocksize); device_dump_read (dc_device_t *device, unsigned int address, unsigned char data[], unsigned int size, unsigned int blocksize);
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@ -38,6 +38,7 @@
#include "oceanic_atom2.h" #include "oceanic_atom2.h"
#include "oceanic_veo250.h" #include "oceanic_veo250.h"
#include "oceanic_vtpro.h" #include "oceanic_vtpro.h"
#include "pelagic_i330r.h"
#include "mares_darwin.h" #include "mares_darwin.h"
#include "mares_iconhd.h" #include "mares_iconhd.h"
#include "mares_nemo.h" #include "mares_nemo.h"
@ -60,6 +61,11 @@
#include "mclean_extreme.h" #include "mclean_extreme.h"
#include "liquivision_lynx.h" #include "liquivision_lynx.h"
#include "sporasub_sp2.h" #include "sporasub_sp2.h"
#include "deepsix_excursion.h"
#include "seac_screen.h"
#include "deepblu_cosmiq.h"
#include "oceans_s1.h"
#include "divesoft_freedom.h"
#include "device-private.h" #include "device-private.h"
#include "context-private.h" #include "context-private.h"
@ -157,6 +163,9 @@ dc_device_open (dc_device_t **out, dc_context_t *context, dc_descriptor_t *descr
case DC_FAMILY_OCEANIC_ATOM2: case DC_FAMILY_OCEANIC_ATOM2:
rc = oceanic_atom2_device_open (&device, context, iostream, dc_descriptor_get_model (descriptor)); rc = oceanic_atom2_device_open (&device, context, iostream, dc_descriptor_get_model (descriptor));
break; break;
case DC_FAMILY_PELAGIC_I330R:
rc = pelagic_i330r_device_open (&device, context, iostream, dc_descriptor_get_model (descriptor));
break;
case DC_FAMILY_MARES_NEMO: case DC_FAMILY_MARES_NEMO:
rc = mares_nemo_device_open (&device, context, iostream); rc = mares_nemo_device_open (&device, context, iostream);
break; break;
@ -167,7 +176,7 @@ dc_device_open (dc_device_t **out, dc_context_t *context, dc_descriptor_t *descr
rc = mares_darwin_device_open (&device, context, iostream, dc_descriptor_get_model (descriptor)); rc = mares_darwin_device_open (&device, context, iostream, dc_descriptor_get_model (descriptor));
break; break;
case DC_FAMILY_MARES_ICONHD: case DC_FAMILY_MARES_ICONHD:
rc = mares_iconhd_device_open (&device, context, iostream); rc = mares_iconhd_device_open (&device, context, iostream, dc_descriptor_get_model (descriptor));
break; break;
case DC_FAMILY_HW_OSTC: case DC_FAMILY_HW_OSTC:
rc = hw_ostc_device_open (&device, context, iostream); rc = hw_ostc_device_open (&device, context, iostream);
@ -223,6 +232,21 @@ dc_device_open (dc_device_t **out, dc_context_t *context, dc_descriptor_t *descr
case DC_FAMILY_SPORASUB_SP2: case DC_FAMILY_SPORASUB_SP2:
rc = sporasub_sp2_device_open (&device, context, iostream); rc = sporasub_sp2_device_open (&device, context, iostream);
break; break;
case DC_FAMILY_DEEPSIX_EXCURSION:
rc = deepsix_excursion_device_open (&device, context, iostream);
break;
case DC_FAMILY_SEAC_SCREEN:
rc = seac_screen_device_open (&device, context, iostream);
break;
case DC_FAMILY_DEEPBLU_COSMIQ:
rc = deepblu_cosmiq_device_open (&device, context, iostream);
break;
case DC_FAMILY_OCEANS_S1:
rc = oceans_s1_device_open (&device, context, iostream);
break;
case DC_FAMILY_DIVESOFT_FREEDOM:
rc = divesoft_freedom_device_open (&device, context, iostream);
break;
default: default:
return DC_STATUS_INVALIDARGS; return DC_STATUS_INVALIDARGS;
} }
@ -338,7 +362,7 @@ dc_device_dump (dc_device_t *device, dc_buffer_t *buffer)
dc_status_t dc_status_t
device_dump_read (dc_device_t *device, unsigned char data[], unsigned int size, unsigned int blocksize) device_dump_read (dc_device_t *device, unsigned int address, unsigned char data[], unsigned int size, unsigned int blocksize)
{ {
if (device == NULL) if (device == NULL)
return DC_STATUS_UNSUPPORTED; return DC_STATUS_UNSUPPORTED;
@ -359,7 +383,7 @@ device_dump_read (dc_device_t *device, unsigned char data[], unsigned int size,
len = blocksize; len = blocksize;
// Read the packet. // Read the packet.
dc_status_t rc = device->vtable->read (device, nbytes, data + nbytes, len); dc_status_t rc = device->vtable->read (device, address + nbytes, data + nbytes, len);
if (rc != DC_STATUS_SUCCESS) if (rc != DC_STATUS_SUCCESS)
return rc; return rc;
@ -396,6 +420,9 @@ dc_device_timesync (dc_device_t *device, const dc_datetime_t *datetime)
if (device->vtable->timesync == NULL) if (device->vtable->timesync == NULL)
return DC_STATUS_UNSUPPORTED; return DC_STATUS_UNSUPPORTED;
if (datetime == NULL)
return DC_STATUS_INVALIDARGS;
return device->vtable->timesync (device, datetime); return device->vtable->timesync (device, datetime);
} }

View File

@ -35,7 +35,7 @@ dc_status_t
diverite_nitekq_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream); diverite_nitekq_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream);
dc_status_t dc_status_t
diverite_nitekq_parser_create (dc_parser_t **parser, dc_context_t *context); diverite_nitekq_parser_create (dc_parser_t **parser, dc_context_t *context, const unsigned char data[], size_t size);
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@ -49,7 +49,6 @@ struct diverite_nitekq_parser_t {
double maxdepth; double maxdepth;
}; };
static dc_status_t diverite_nitekq_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size);
static dc_status_t diverite_nitekq_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime); static dc_status_t diverite_nitekq_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime);
static dc_status_t diverite_nitekq_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value); static dc_status_t diverite_nitekq_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value);
static dc_status_t diverite_nitekq_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata); static dc_status_t diverite_nitekq_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata);
@ -57,7 +56,9 @@ static dc_status_t diverite_nitekq_parser_samples_foreach (dc_parser_t *abstract
static const dc_parser_vtable_t diverite_nitekq_parser_vtable = { static const dc_parser_vtable_t diverite_nitekq_parser_vtable = {
sizeof(diverite_nitekq_parser_t), sizeof(diverite_nitekq_parser_t),
DC_FAMILY_DIVERITE_NITEKQ, DC_FAMILY_DIVERITE_NITEKQ,
diverite_nitekq_parser_set_data, /* set_data */ NULL, /* set_clock */
NULL, /* set_atmospheric */
NULL, /* set_density */
diverite_nitekq_parser_get_datetime, /* datetime */ diverite_nitekq_parser_get_datetime, /* datetime */
diverite_nitekq_parser_get_field, /* fields */ diverite_nitekq_parser_get_field, /* fields */
diverite_nitekq_parser_samples_foreach, /* samples_foreach */ diverite_nitekq_parser_samples_foreach, /* samples_foreach */
@ -66,7 +67,7 @@ static const dc_parser_vtable_t diverite_nitekq_parser_vtable = {
dc_status_t dc_status_t
diverite_nitekq_parser_create (dc_parser_t **out, dc_context_t *context) diverite_nitekq_parser_create (dc_parser_t **out, dc_context_t *context, const unsigned char data[], size_t size)
{ {
diverite_nitekq_parser_t *parser = NULL; diverite_nitekq_parser_t *parser = NULL;
@ -74,7 +75,7 @@ diverite_nitekq_parser_create (dc_parser_t **out, dc_context_t *context)
return DC_STATUS_INVALIDARGS; return DC_STATUS_INVALIDARGS;
// Allocate memory. // Allocate memory.
parser = (diverite_nitekq_parser_t *) dc_parser_allocate (context, &diverite_nitekq_parser_vtable); parser = (diverite_nitekq_parser_t *) dc_parser_allocate (context, &diverite_nitekq_parser_vtable, data, size);
if (parser == NULL) { if (parser == NULL) {
ERROR (context, "Failed to allocate memory."); ERROR (context, "Failed to allocate memory.");
return DC_STATUS_NOMEMORY; return DC_STATUS_NOMEMORY;
@ -98,13 +99,6 @@ diverite_nitekq_parser_create (dc_parser_t **out, dc_context_t *context)
} }
static dc_status_t
diverite_nitekq_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size)
{
return DC_STATUS_SUCCESS;
}
static dc_status_t static dc_status_t
diverite_nitekq_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime) diverite_nitekq_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime)
{ {
@ -158,6 +152,7 @@ diverite_nitekq_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, u
*((unsigned int *) value) = parser->ngasmixes; *((unsigned int *) value) = parser->ngasmixes;
break; break;
case DC_FIELD_GASMIX: case DC_FIELD_GASMIX:
gasmix->usage = DC_USAGE_NONE;
gasmix->helium = parser->he[flags] / 100.0; gasmix->helium = parser->he[flags] / 100.0;
gasmix->oxygen = parser->o2[flags] / 100.0; gasmix->oxygen = parser->o2[flags] / 100.0;
gasmix->nitrogen = 1.0 - gasmix->oxygen - gasmix->helium; gasmix->nitrogen = 1.0 - gasmix->oxygen - gasmix->helium;
@ -261,13 +256,13 @@ diverite_nitekq_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callbac
// Time (seconds). // Time (seconds).
time += interval; time += interval;
sample.time = time; sample.time = time * 1000;
if (callback) callback (DC_SAMPLE_TIME, sample, userdata); if (callback) callback (DC_SAMPLE_TIME, &sample, userdata);
// Gas change // Gas change
if (gasmix != gasmix_previous) { if (gasmix != gasmix_previous) {
sample.gasmix = gasmix; sample.gasmix = gasmix;
if (callback) callback (DC_SAMPLE_GASMIX, sample, userdata); if (callback) callback (DC_SAMPLE_GASMIX, &sample, userdata);
gasmix_previous = gasmix; gasmix_previous = gasmix;
} }
@ -279,7 +274,7 @@ diverite_nitekq_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callbac
sample.depth = depth / 10.0; sample.depth = depth / 10.0;
else else
sample.depth = depth * FEET / 10.0; sample.depth = depth * FEET / 10.0;
if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata); if (callback) callback (DC_SAMPLE_DEPTH, &sample, userdata);
offset += 2; offset += 2;
if (type == 3) { if (type == 3) {
@ -290,8 +285,9 @@ diverite_nitekq_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callbac
if (offset + 1 > size) if (offset + 1 > size)
return DC_STATUS_DATAFORMAT; return DC_STATUS_DATAFORMAT;
unsigned int ppo2 = data[offset]; unsigned int ppo2 = data[offset];
sample.ppo2 = ppo2 / 100.0; sample.ppo2.sensor = DC_SENSOR_NONE;
if (callback) callback (DC_SAMPLE_PPO2, sample, userdata); sample.ppo2.value = ppo2 / 100.0;
if (callback) callback (DC_SAMPLE_PPO2, &sample, userdata);
offset++; offset++;
} }
} else { } else {

595
src/divesoft_freedom.c Normal file
View File

@ -0,0 +1,595 @@
/*
* libdivecomputer
*
* Copyright (C) 2023 Jan Matoušek, Jef Driesen
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include "divesoft_freedom.h"
#include "context-private.h"
#include "device-private.h"
#include "platform.h"
#include "checksum.h"
#include "array.h"
#include "hdlc.h"
#define MAXDATA 256
#define HEADER_SIGNATURE_V1 0x45766944 // "DivE"
#define HEADER_SIGNATURE_V2 0x45566944 // "DiVE"
#define HEADER_SIZE_V1 32
#define HEADER_SIZE_V2 64
#define RECORD_SIZE 16
#define FINGERPRINT_SIZE 20
#define INVALID 0xFFFFFFFF
#define COMPRESSION 1
#define DIRECTION 1
#define NRECORDS 100
#define DEVICE_CCR_CU 1 // Liberty HW rev. 1.X
#define DEVICE_FREEDOM 2 // Freedom HW rev. 2.X
#define DEVICE_FREEDOM3 5 // Freedom HW rev. 3.X
#define DEVICE_CCR_CU15 10 // Liberty HW rev. 2.X, Bluetooth enabled
#define DEVICE_FREEDOM4 19 // Freedom HW rev. 4.X, Bluetooth enabled
typedef enum message_t {
MSG_ECHO = 0,
MSG_RESULT = 1,
MSG_CONNECT = 2,
MSG_CONNECTED = 3,
MSG_VERSION = 4,
MSG_VERSION_RSP = 5,
MSG_DIVE_DATA = 64,
MSG_DIVE_DATA_RSP = 65,
MSG_DIVE_LIST = 66,
MSG_DIVE_LIST_V1 = 67,
MSG_DIVE_LIST_V2 = 71,
} message_t;
typedef struct divesoft_freedom_device_t {
dc_device_t base;
dc_iostream_t *iostream;
unsigned char fingerprint[FINGERPRINT_SIZE];
unsigned int seqnum;
} divesoft_freedom_device_t;
static dc_status_t divesoft_freedom_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size);
static dc_status_t divesoft_freedom_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata);
static dc_status_t divesoft_freedom_device_close (dc_device_t *device);
static const dc_device_vtable_t divesoft_freedom_device_vtable = {
sizeof(divesoft_freedom_device_t),
DC_FAMILY_DIVESOFT_FREEDOM,
divesoft_freedom_device_set_fingerprint, /* set_fingerprint */
NULL, /* read */
NULL, /* write */
NULL, /* dump */
divesoft_freedom_device_foreach, /* foreach */
NULL, /* timesync */
divesoft_freedom_device_close, /* close */
};
static dc_status_t
divesoft_freedom_send (divesoft_freedom_device_t *device, message_t message, const unsigned char data[], size_t size)
{
dc_status_t status = DC_STATUS_SUCCESS;
dc_device_t *abstract = (dc_device_t *) device;
size_t nbytes = 0, count = 0;
while (1) {
size_t len = size - nbytes;
if (len > MAXDATA)
len = MAXDATA;
unsigned int islast = nbytes + len == size;
unsigned char packet[6 + MAXDATA + 2] = {0};
packet[0] = ((count & 0x0F) << 4) | (device->seqnum & 0x0F);
packet[1] = 0x80 | (islast << 6);
array_uint16_le_set (packet + 2, message);
array_uint16_le_set (packet + 4, len);
if (len) {
memcpy (packet + 6, data + nbytes, len);
}
unsigned short crc = checksum_crc16r_ccitt (packet, len + 6, 0xFFFF, 0xFFFF);
array_uint16_le_set (packet + 6 + len, crc);
HEXDUMP (abstract->context, DC_LOGLEVEL_DEBUG, "cmd", packet, 6 + len + 2);
status = dc_iostream_write (device->iostream, packet, 6 + len + 2, NULL);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to send the packet.");
return status;
}
nbytes += len;
count++;
if (islast)
break;
}
return status;
}
static dc_status_t
divesoft_freedom_recv (divesoft_freedom_device_t *device, dc_event_progress_t *progress, message_t *message, dc_buffer_t *buffer)
{
dc_status_t status = DC_STATUS_SUCCESS;
dc_device_t *abstract = (dc_device_t *) device;
unsigned int msg = INVALID;
unsigned int count = 0;
while (1) {
size_t len = 0;
unsigned char packet[6 + MAXDATA + 2] = {0};
status = dc_iostream_read (device->iostream, packet, sizeof(packet), &len);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to receive the packet.");
return status;
}
HEXDUMP (abstract->context, DC_LOGLEVEL_DEBUG, "rcv", packet, len);
if (len < 8) {
ERROR (abstract->context, "Unexpected packet length (" DC_PRINTF_SIZE ").", len);
return DC_STATUS_PROTOCOL;
}
unsigned int seqnum = packet[0];
unsigned int flags = packet[1];
unsigned int type = array_uint16_le (packet + 2);
unsigned int length = array_uint16_le (packet + 4);
unsigned int expected = ((count & 0x0F) << 4) | (device->seqnum & 0x0F);
if (seqnum != expected) {
ERROR (abstract->context, "Unexpected packet sequence number (%u %u).", seqnum, expected);
return DC_STATUS_PROTOCOL;
}
if ((flags & ~0x40) != 0) {
ERROR (abstract->context, "Unexpected packet flags (%u).", flags);
return DC_STATUS_PROTOCOL;
}
if (length != len - 8) {
ERROR (abstract->context, "Unexpected packet length (%u " DC_PRINTF_SIZE ").", length, len - 8);
return DC_STATUS_PROTOCOL;
}
if (msg == INVALID) {
msg = type;
} else if (msg != type) {
ERROR (abstract->context, "Unexpected packet type (%u).", msg);
return DC_STATUS_PROTOCOL;
}
unsigned short crc = array_uint16_le (packet + len - 2);
unsigned short ccrc = checksum_crc16r_ccitt (packet, len - 2, 0xFFFF, 0xFFFF);
if (crc != ccrc) {
ERROR (abstract->context, "Unexpected packet checksum (%04x %04x).", crc, ccrc);
return DC_STATUS_PROTOCOL;
}
// Update and emit a progress event.
if (progress) {
progress->current += len - 8;
// Limit the progress to the maximum size. This could happen if the
// dive computer sends more data than requested for some reason.
if (progress->current > progress->maximum) {
WARNING (abstract->context, "Progress exceeds the maximum size.");
progress->current = progress->maximum;
}
device_event_emit (abstract, DC_EVENT_PROGRESS, progress);
}
if (!dc_buffer_append (buffer, packet + 6, len - 8)) {
ERROR (abstract->context, "Insufficient buffer space available.");
return DC_STATUS_NOMEMORY;
}
count++;
if (flags & 0x40)
break;
}
if (message)
*message = msg;
return status;
}
static dc_status_t
divesoft_freedom_transfer (divesoft_freedom_device_t *device, dc_event_progress_t *progress, message_t cmd, const unsigned char data[], size_t size, message_t *msg, dc_buffer_t *buffer)
{
dc_status_t status = DC_STATUS_SUCCESS;
dc_device_t *abstract = (dc_device_t *) device;
if (device_is_cancelled (abstract))
return DC_STATUS_CANCELLED;
device->seqnum++;
status = divesoft_freedom_send (device, cmd, data, size);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to send the command.");
return status;
}
status = divesoft_freedom_recv (device, progress, msg, buffer);
if(status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to receive response.");
return status;
}
return status;
}
static dc_status_t
divesoft_freedom_download (divesoft_freedom_device_t *device, message_t cmd, const unsigned char cdata[], size_t csize, unsigned char rdata[], size_t rsize)
{
dc_status_t status = DC_STATUS_SUCCESS;
dc_device_t *abstract = (dc_device_t *) device;
dc_buffer_t *buffer = dc_buffer_new (rsize);
if (buffer == NULL) {
ERROR (abstract->context, "Failed to allocate memory.");
status = DC_STATUS_NOMEMORY;
goto error_exit;
}
message_t msg = MSG_ECHO;
status = divesoft_freedom_transfer (device, NULL, cmd, cdata, csize, &msg, buffer);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to transfer the packet.");
goto error_free;
}
if (msg != cmd + 1) {
ERROR (abstract->context, "Unexpected response message (%u).", msg);
status = DC_STATUS_PROTOCOL;
goto error_free;
}
size_t length = dc_buffer_get_size (buffer);
if (length != rsize) {
ERROR (abstract->context, "Unexpected response length (" DC_PRINTF_SIZE " " DC_PRINTF_SIZE ").", length, rsize);
status = DC_STATUS_PROTOCOL;
goto error_free;
}
if (rsize) {
memcpy (rdata, dc_buffer_get_data (buffer), rsize);
}
error_free:
dc_buffer_free (buffer);
error_exit:
return status;
}
dc_status_t
divesoft_freedom_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t *iostream)
{
dc_status_t status = DC_STATUS_SUCCESS;
divesoft_freedom_device_t *device = NULL;
if (out == NULL)
return DC_STATUS_INVALIDARGS;
// Allocate memory.
device = (divesoft_freedom_device_t *) dc_device_allocate (context, &divesoft_freedom_device_vtable);
if (device == NULL) {
ERROR (context, "Failed to allocate memory.");
return DC_STATUS_NOMEMORY;
}
// Set the default values.
device->iostream = NULL;
memset(device->fingerprint, 0, sizeof(device->fingerprint));
device->seqnum = 0;
// Setup the HDLC communication.
status = dc_hdlc_open (&device->iostream, context, iostream, 244, 244);
if (status != DC_STATUS_SUCCESS) {
ERROR (context, "Failed to create the HDLC stream.");
goto error_free;
}
// Set the serial communication protocol (115200 8N1).
status = dc_iostream_configure (device->iostream, 115200, 8, DC_PARITY_NONE, DC_STOPBITS_ONE, DC_FLOWCONTROL_NONE);
if (status != DC_STATUS_SUCCESS) {
ERROR (context, "Failed to set the terminal attributes.");
goto error_free_hdlc;
}
// Set the timeout for receiving data (3000ms).
status = dc_iostream_set_timeout (device->iostream, 3000);
if (status != DC_STATUS_SUCCESS) {
ERROR (context, "Failed to set the timeout.");
goto error_free_hdlc;
}
// Initiate the connection with the dive computer.
const char client[] = "libdivecomputer";
unsigned char cmd_connect[2 + sizeof(client) - 1] = {0};
array_uint16_le_set (cmd_connect, COMPRESSION);
memcpy (cmd_connect + 2, client, sizeof(client) - 1);
unsigned char rsp_connect[36] = {0};
status = divesoft_freedom_download (device, MSG_CONNECT, cmd_connect, sizeof(cmd_connect), rsp_connect, sizeof(rsp_connect));
if (status != DC_STATUS_SUCCESS) {
ERROR (context, "Failed to connect to the device.");
goto error_free_hdlc;
}
DEBUG (context, "Connection: compression=%u, protocol=%u.%u, serial=%.16s",
array_uint16_le (rsp_connect),
rsp_connect[2], rsp_connect[3],
rsp_connect + 4);
*out = (dc_device_t *) device;
return DC_STATUS_SUCCESS;
error_free_hdlc:
dc_iostream_close (device->iostream);
error_free:
dc_device_deallocate ((dc_device_t *) device);
return status;
}
static dc_status_t
divesoft_freedom_device_close (dc_device_t *abstract)
{
divesoft_freedom_device_t *device = (divesoft_freedom_device_t *) abstract;
return dc_iostream_close (device->iostream);
}
static dc_status_t
divesoft_freedom_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size)
{
divesoft_freedom_device_t *device = (divesoft_freedom_device_t *) abstract;
if (size && size != sizeof (device->fingerprint))
return DC_STATUS_INVALIDARGS;
if (size)
memcpy (device->fingerprint, data, sizeof (device->fingerprint));
else
memset (device->fingerprint, 0, sizeof (device->fingerprint));
return DC_STATUS_SUCCESS;
}
static dc_status_t
divesoft_freedom_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata)
{
dc_status_t status = DC_STATUS_SUCCESS;
divesoft_freedom_device_t *device = (divesoft_freedom_device_t *) abstract;
// Enable progress notifications.
dc_event_progress_t progress = EVENT_PROGRESS_INITIALIZER;
device_event_emit (abstract, DC_EVENT_PROGRESS, &progress);
// Read the device information.
unsigned char rsp_version[26] = {0};
status = divesoft_freedom_download (device, MSG_VERSION, NULL, 0, rsp_version, sizeof(rsp_version));
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to read the device information.");
goto error_exit;
}
DEBUG (abstract->context, "Device: model=%u, hw=%u.%u, sw=%u.%u.%u.%u serial=%.16s",
rsp_version[0],
rsp_version[1], rsp_version[2],
rsp_version[3], rsp_version[4], rsp_version[5],
array_uint32_le (rsp_version + 6),
rsp_version + 10);
// Emit a device info event.
dc_event_devinfo_t devinfo;
devinfo.model = rsp_version[0];
devinfo.firmware = array_uint24_be (rsp_version + 3);
devinfo.serial = array_convert_str2num (rsp_version + 10 + 5, 11);
device_event_emit(abstract, DC_EVENT_DEVINFO, &devinfo);
// Allocate memory for the dive list.
dc_buffer_t *divelist = dc_buffer_new (0);
if (divelist == NULL) {
status = DC_STATUS_NOMEMORY;
goto error_exit;
}
// Allocate memory for the download buffer.
dc_buffer_t *buffer = dc_buffer_new (NRECORDS * (4 + FINGERPRINT_SIZE + HEADER_SIZE_V2));
if (buffer == NULL) {
status = DC_STATUS_NOMEMORY;
goto error_free_divelist;
}
// Record version and size.
unsigned int version = 0;
unsigned int headersize = 0;
unsigned int recordsize = 0;
// Download the dive list.
unsigned int ndives = 0;
unsigned int total = 0;
unsigned int maxsize = 0;
unsigned int current = INVALID;
while (1) {
// Clear the buffer.
dc_buffer_clear (buffer);
// Prepare the command.
unsigned char cmd_list[6] = {0};
array_uint32_le_set (cmd_list, current);
cmd_list[4] = DIRECTION;
cmd_list[5] = NRECORDS;
// Download the dive list records.
message_t msg_list = MSG_ECHO;
status = divesoft_freedom_transfer (device, &progress, MSG_DIVE_LIST, cmd_list, sizeof(cmd_list), &msg_list, buffer);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to download the dive list.");
goto error_free_buffer;
}
// Check the response message type.
if (msg_list != MSG_DIVE_LIST_V1 && msg_list != MSG_DIVE_LIST_V2) {
ERROR (abstract->context, "Unexpected response message (%u).", msg_list);
status = DC_STATUS_PROTOCOL;
goto error_free_buffer;
}
// Store/check the version.
if (version == 0) {
version = msg_list;
headersize = version == MSG_DIVE_LIST_V1 ?
HEADER_SIZE_V1 : HEADER_SIZE_V2;
recordsize = 4 + FINGERPRINT_SIZE + headersize;
} else if (version != msg_list) {
ERROR (abstract->context, "Unexpected response message (%u).", msg_list);
status = DC_STATUS_PROTOCOL;
goto error_free_buffer;
}
const unsigned char *data = dc_buffer_get_data (buffer);
size_t size = dc_buffer_get_size (buffer);
// Process the records.
size_t offset = 0, count = 0;
while (offset + recordsize <= size) {
// Get the record data.
unsigned int handle = array_uint32_le (data + offset);
const unsigned char *fingerprint = data + offset + 4;
const unsigned char *header = data + offset + 4 + FINGERPRINT_SIZE;
// Check the fingerprint data.
if (memcmp (device->fingerprint, fingerprint, sizeof(device->fingerprint)) == 0) {
break;
}
// Get the length of the dive.
unsigned int nrecords = version == MSG_DIVE_LIST_V1 ?
array_uint32_le (header + 16) & 0x3FFFF :
array_uint32_le (header + 20);
unsigned int length = headersize + nrecords * RECORD_SIZE;
// Calculate the total and maximum size.
if (length > maxsize)
maxsize = length;
total += length;
// Set the handle for the next request.
current = handle;
offset += recordsize;
count++;
ndives++;
}
// Append the records to the dive list buffer.
if (!dc_buffer_append (divelist, data, count * recordsize)) {
ERROR (abstract->context, "Insufficient buffer space available.");
status = DC_STATUS_NOMEMORY;
goto error_free_buffer;
}
// Stop downloading if there are no more records.
if (count < NRECORDS)
break;
}
// Update and emit a progress event.
progress.maximum = progress.current + total;
device_event_emit(abstract, DC_EVENT_PROGRESS, &progress);
// Reserve memory for the largest dive.
dc_buffer_reserve (buffer, maxsize);
const unsigned char *data = dc_buffer_get_data (divelist);
size_t size = dc_buffer_get_size (divelist);
size_t offset = 0;
while (offset + recordsize <= size) {
// Get the record data.
unsigned int handle = array_uint32_le (data + offset);
const unsigned char *fingerprint = data + offset + 4;
const unsigned char *header = data + offset + 4 + FINGERPRINT_SIZE;
// Get the length of the dive.
unsigned int nrecords = version == MSG_DIVE_LIST_V1 ?
array_uint32_le (header + 16) & 0x3FFFF :
array_uint32_le (header + 20);
unsigned int length = headersize + nrecords * RECORD_SIZE;
// Clear the buffer.
dc_buffer_clear (buffer);
// Prepare the command.
unsigned char cmd_dive[12] = {0};
array_uint32_le_set (cmd_dive + 0, handle);
array_uint32_le_set (cmd_dive + 4, 0);
array_uint32_le_set (cmd_dive + 8, length);
// Download the dive.
message_t msg_dive = MSG_ECHO;
status = divesoft_freedom_transfer (device, &progress, MSG_DIVE_DATA, cmd_dive, sizeof(cmd_dive), &msg_dive, buffer);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to download the dive.");
goto error_free_buffer;
}
// Check the response message type.
if (msg_dive != MSG_DIVE_DATA_RSP) {
ERROR (abstract->context, "Unexpected response message (%u).", msg_dive);
status = DC_STATUS_PROTOCOL;
goto error_free_buffer;
}
// Verify both dive headers are identical.
if (dc_buffer_get_size (buffer) < headersize ||
memcmp (header, dc_buffer_get_data (buffer), headersize) != 0) {
ERROR (abstract->context, "Unexpected profile header.");
status = DC_STATUS_PROTOCOL;
goto error_free_buffer;
}
if (callback && !callback (dc_buffer_get_data(buffer), dc_buffer_get_size(buffer), fingerprint, sizeof (device->fingerprint), userdata)) {
break;
}
offset += recordsize;
}
error_free_buffer:
dc_buffer_free (buffer);
error_free_divelist:
dc_buffer_free (divelist);
error_exit:
return status;
}

43
src/divesoft_freedom.h Normal file
View File

@ -0,0 +1,43 @@
/*
* libdivecomputer
*
* Copyright (C) 2023 Jan Matoušek, Jef Driesen
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
#ifndef DIVESOFT_FREEDOM_H
#define DIVESOFT_FREEDOM_H
#include <libdivecomputer/context.h>
#include <libdivecomputer/iostream.h>
#include <libdivecomputer/device.h>
#include <libdivecomputer/parser.h>
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
dc_status_t
divesoft_freedom_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream);
dc_status_t
divesoft_freedom_parser_create (dc_parser_t **parser, dc_context_t *context, const unsigned char data[], size_t size);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* DIVESOFT_FREEDOM_H */

File diff suppressed because it is too large Load Diff

View File

@ -29,8 +29,7 @@
#include "platform.h" #include "platform.h"
#include "checksum.h" #include "checksum.h"
#include "array.h" #include "array.h"
#include "packet.h"
#define C_ARRAY_SIZE(array) (sizeof (array) / sizeof *(array))
#define ISINSTANCE(device) dc_device_isinstance((device), &divesystem_idive_device_vtable) #define ISINSTANCE(device) dc_device_isinstance((device), &divesystem_idive_device_vtable)
@ -104,6 +103,7 @@ typedef struct divesystem_idive_device_t {
static dc_status_t divesystem_idive_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size); static dc_status_t divesystem_idive_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size);
static dc_status_t divesystem_idive_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata); static dc_status_t divesystem_idive_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata);
static dc_status_t divesystem_idive_device_timesync (dc_device_t *abstract, const dc_datetime_t *datetime); static dc_status_t divesystem_idive_device_timesync (dc_device_t *abstract, const dc_datetime_t *datetime);
static dc_status_t divesystem_idive_device_close (dc_device_t *abstract);
static const dc_device_vtable_t divesystem_idive_device_vtable = { static const dc_device_vtable_t divesystem_idive_device_vtable = {
sizeof(divesystem_idive_device_t), sizeof(divesystem_idive_device_t),
@ -114,7 +114,7 @@ static const dc_device_vtable_t divesystem_idive_device_vtable = {
NULL, /* dump */ NULL, /* dump */
divesystem_idive_device_foreach, /* foreach */ divesystem_idive_device_foreach, /* foreach */
divesystem_idive_device_timesync, /* timesync */ divesystem_idive_device_timesync, /* timesync */
NULL /* close */ divesystem_idive_device_close /* close */
}; };
static const divesystem_idive_commands_t idive = { static const divesystem_idive_commands_t idive = {
@ -154,6 +154,7 @@ divesystem_idive_device_open (dc_device_t **out, dc_context_t *context, dc_iostr
{ {
dc_status_t status = DC_STATUS_SUCCESS; dc_status_t status = DC_STATUS_SUCCESS;
divesystem_idive_device_t *device = NULL; divesystem_idive_device_t *device = NULL;
dc_transport_t transport = dc_iostream_get_transport (iostream);
if (out == NULL) if (out == NULL)
return DC_STATUS_INVALIDARGS; return DC_STATUS_INVALIDARGS;
@ -166,22 +167,32 @@ divesystem_idive_device_open (dc_device_t **out, dc_context_t *context, dc_iostr
} }
// Set the default values. // Set the default values.
device->iostream = iostream;
memset (device->fingerprint, 0, sizeof (device->fingerprint)); memset (device->fingerprint, 0, sizeof (device->fingerprint));
device->model = model; device->model = model;
// Create the packet stream.
if (transport == DC_TRANSPORT_BLE) {
status = dc_packet_open (&device->iostream, context, iostream, 244, 244);
if (status != DC_STATUS_SUCCESS) {
ERROR (context, "Failed to create the packet stream.");
goto error_free;
}
} else {
device->iostream = iostream;
}
// Set the serial communication protocol (115200 8N1). // Set the serial communication protocol (115200 8N1).
status = dc_iostream_configure (device->iostream, 115200, 8, DC_PARITY_NONE, DC_STOPBITS_ONE, DC_FLOWCONTROL_NONE); status = dc_iostream_configure (device->iostream, 115200, 8, DC_PARITY_NONE, DC_STOPBITS_ONE, DC_FLOWCONTROL_NONE);
if (status != DC_STATUS_SUCCESS) { if (status != DC_STATUS_SUCCESS) {
ERROR (context, "Failed to set the terminal attributes."); ERROR (context, "Failed to set the terminal attributes.");
goto error_free; goto error_free_iostream;
} }
// Set the timeout for receiving data (1000ms). // Set the timeout for receiving data (1000ms).
status = dc_iostream_set_timeout (device->iostream, 1000); status = dc_iostream_set_timeout (device->iostream, 1000);
if (status != DC_STATUS_SUCCESS) { if (status != DC_STATUS_SUCCESS) {
ERROR (context, "Failed to set the timeout."); ERROR (context, "Failed to set the timeout.");
goto error_free; goto error_free_iostream;
} }
// Make sure everything is in a sane state. // Make sure everything is in a sane state.
@ -192,11 +203,27 @@ divesystem_idive_device_open (dc_device_t **out, dc_context_t *context, dc_iostr
return DC_STATUS_SUCCESS; return DC_STATUS_SUCCESS;
error_free_iostream:
if (transport == DC_TRANSPORT_BLE) {
dc_iostream_close (device->iostream);
}
error_free: error_free:
dc_device_deallocate ((dc_device_t *) device); dc_device_deallocate ((dc_device_t *) device);
return status; return status;
} }
static dc_status_t
divesystem_idive_device_close (dc_device_t *abstract)
{
divesystem_idive_device_t *device = (divesystem_idive_device_t *) abstract;
// Close the packet stream.
if (dc_iostream_get_transport (device->iostream) == DC_TRANSPORT_BLE) {
return dc_iostream_close (device->iostream);
}
return DC_STATUS_SUCCESS;
}
static dc_status_t static dc_status_t
divesystem_idive_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size) divesystem_idive_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size)
@ -233,7 +260,7 @@ divesystem_idive_send (divesystem_idive_device_t *device, const unsigned char co
packet[0] = START; packet[0] = START;
packet[1] = csize; packet[1] = csize;
memcpy(packet + 2, command, csize); memcpy(packet + 2, command, csize);
crc = checksum_crc16_ccitt (packet, csize + 2, 0xffff); crc = checksum_crc16_ccitt (packet, csize + 2, 0xffff, 0x0000);
packet[csize + 2] = (crc >> 8) & 0xFF; packet[csize + 2] = (crc >> 8) & 0xFF;
packet[csize + 3] = (crc ) & 0xFF; packet[csize + 3] = (crc ) & 0xFF;
@ -294,7 +321,7 @@ divesystem_idive_receive (divesystem_idive_device_t *device, unsigned char answe
// Verify the checksum. // Verify the checksum.
unsigned short crc = array_uint16_be (packet + len + 2); unsigned short crc = array_uint16_be (packet + len + 2);
unsigned short ccrc = checksum_crc16_ccitt (packet, len + 2, 0xffff); unsigned short ccrc = checksum_crc16_ccitt (packet, len + 2, 0xffff, 0x0000);
if (crc != ccrc) { if (crc != ccrc) {
ERROR (abstract->context, "Unexpected packet checksum."); ERROR (abstract->context, "Unexpected packet checksum.");
return DC_STATUS_PROTOCOL; return DC_STATUS_PROTOCOL;
@ -610,11 +637,6 @@ divesystem_idive_device_timesync (dc_device_t *abstract, const dc_datetime_t *da
return DC_STATUS_UNSUPPORTED; return DC_STATUS_UNSUPPORTED;
} }
if (datetime == NULL) {
ERROR (abstract->context, "Invalid parameter specified.");
return DC_STATUS_INVALIDARGS;
}
// Get the UTC timestamp. // Get the UTC timestamp.
dc_ticks_t timestamp = dc_datetime_mktime(datetime); dc_ticks_t timestamp = dc_datetime_mktime(datetime);
if (timestamp == -1) { if (timestamp == -1) {

View File

@ -36,7 +36,7 @@ dc_status_t
divesystem_idive_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream, unsigned int model); divesystem_idive_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream, unsigned int model);
dc_status_t dc_status_t
divesystem_idive_parser_create (dc_parser_t **parser, dc_context_t *context, unsigned int model); divesystem_idive_parser_create (dc_parser_t **parser, dc_context_t *context, const unsigned char data[], size_t size, unsigned int model);
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@ -26,11 +26,10 @@
#include "parser-private.h" #include "parser-private.h"
#include "array.h" #include "array.h"
#define C_ARRAY_SIZE(array) (sizeof (array) / sizeof *(array))
#define ISINSTANCE(parser) dc_device_isinstance((parser), &divesystem_idive_parser_vtable) #define ISINSTANCE(parser) dc_device_isinstance((parser), &divesystem_idive_parser_vtable)
#define ISIX3M(model) ((model) >= 0x21) #define ISIX3M(model) ((model) >= 0x21)
#define ISIX3M2(model) ((model) >= 0x60 && (model) < 0x1000)
#define SZ_HEADER_IDIVE 0x32 #define SZ_HEADER_IDIVE 0x32
#define SZ_SAMPLE_IDIVE 0x2A #define SZ_SAMPLE_IDIVE 0x2A
@ -50,6 +49,18 @@
#define FREEDIVE 4 #define FREEDIVE 4
#define INVALID 0xFFFFFFFF #define INVALID 0xFFFFFFFF
#define BUHLMANN 0
#define VPM 1
#define DUAL 2
#define IX3M2_BUHLMANN 0
#define IX3M2_ZHL16B 1
#define IX3M2_ZHL16C 2
#define IX3M2_VPM 3
#define REC_SAMPLE 0
#define REC_INFO 1
typedef struct divesystem_idive_parser_t divesystem_idive_parser_t; typedef struct divesystem_idive_parser_t divesystem_idive_parser_t;
typedef struct divesystem_idive_gasmix_t { typedef struct divesystem_idive_gasmix_t {
@ -76,9 +87,11 @@ struct divesystem_idive_parser_t {
unsigned int ntanks; unsigned int ntanks;
divesystem_idive_gasmix_t gasmix[NGASMIXES]; divesystem_idive_gasmix_t gasmix[NGASMIXES];
divesystem_idive_tank_t tank[NTANKS]; divesystem_idive_tank_t tank[NTANKS];
unsigned int algorithm;
unsigned int gf_low;
unsigned int gf_high;
}; };
static dc_status_t divesystem_idive_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size);
static dc_status_t divesystem_idive_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime); static dc_status_t divesystem_idive_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime);
static dc_status_t divesystem_idive_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value); static dc_status_t divesystem_idive_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value);
static dc_status_t divesystem_idive_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata); static dc_status_t divesystem_idive_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata);
@ -86,7 +99,9 @@ static dc_status_t divesystem_idive_parser_samples_foreach (dc_parser_t *abstrac
static const dc_parser_vtable_t divesystem_idive_parser_vtable = { static const dc_parser_vtable_t divesystem_idive_parser_vtable = {
sizeof(divesystem_idive_parser_t), sizeof(divesystem_idive_parser_t),
DC_FAMILY_DIVESYSTEM_IDIVE, DC_FAMILY_DIVESYSTEM_IDIVE,
divesystem_idive_parser_set_data, /* set_data */ NULL, /* set_clock */
NULL, /* set_atmospheric */
NULL, /* set_density */
divesystem_idive_parser_get_datetime, /* datetime */ divesystem_idive_parser_get_datetime, /* datetime */
divesystem_idive_parser_get_field, /* fields */ divesystem_idive_parser_get_field, /* fields */
divesystem_idive_parser_samples_foreach, /* samples_foreach */ divesystem_idive_parser_samples_foreach, /* samples_foreach */
@ -95,7 +110,7 @@ static const dc_parser_vtable_t divesystem_idive_parser_vtable = {
dc_status_t dc_status_t
divesystem_idive_parser_create (dc_parser_t **out, dc_context_t *context, unsigned int model) divesystem_idive_parser_create (dc_parser_t **out, dc_context_t *context, const unsigned char data[], size_t size, unsigned int model)
{ {
divesystem_idive_parser_t *parser = NULL; divesystem_idive_parser_t *parser = NULL;
@ -103,7 +118,7 @@ divesystem_idive_parser_create (dc_parser_t **out, dc_context_t *context, unsign
return DC_STATUS_INVALIDARGS; return DC_STATUS_INVALIDARGS;
// Allocate memory. // Allocate memory.
parser = (divesystem_idive_parser_t *) dc_parser_allocate (context, &divesystem_idive_parser_vtable); parser = (divesystem_idive_parser_t *) dc_parser_allocate (context, &divesystem_idive_parser_vtable, data, size);
if (parser == NULL) { if (parser == NULL) {
ERROR (context, "Failed to allocate memory."); ERROR (context, "Failed to allocate memory.");
return DC_STATUS_NOMEMORY; return DC_STATUS_NOMEMORY;
@ -131,6 +146,10 @@ divesystem_idive_parser_create (dc_parser_t **out, dc_context_t *context, unsign
parser->tank[i].beginpressure = 0; parser->tank[i].beginpressure = 0;
parser->tank[i].endpressure = 0; parser->tank[i].endpressure = 0;
} }
parser->algorithm = INVALID;
parser->gf_low = INVALID;
parser->gf_high = INVALID;
*out = (dc_parser_t*) parser; *out = (dc_parser_t*) parser;
@ -138,32 +157,6 @@ divesystem_idive_parser_create (dc_parser_t **out, dc_context_t *context, unsign
} }
static dc_status_t
divesystem_idive_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size)
{
divesystem_idive_parser_t *parser = (divesystem_idive_parser_t *) abstract;
// Reset the cache.
parser->cached = 0;
parser->divemode = INVALID;
parser->divetime = 0;
parser->maxdepth = 0;
parser->ngasmixes = 0;
parser->ntanks = 0;
for (unsigned int i = 0; i < NGASMIXES; ++i) {
parser->gasmix[i].oxygen = 0;
parser->gasmix[i].helium = 0;
}
for (unsigned int i = 0; i < NTANKS; ++i) {
parser->tank[i].id = 0;
parser->tank[i].beginpressure = 0;
parser->tank[i].endpressure = 0;
}
return DC_STATUS_SUCCESS;
}
static dc_status_t static dc_status_t
divesystem_idive_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime) divesystem_idive_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime)
{ {
@ -282,6 +275,7 @@ divesystem_idive_parser_get_field (dc_parser_t *abstract, dc_field_type_t type,
dc_gasmix_t *gasmix = (dc_gasmix_t *) value; dc_gasmix_t *gasmix = (dc_gasmix_t *) value;
dc_tank_t *tank = (dc_tank_t *) value; dc_tank_t *tank = (dc_tank_t *) value;
dc_salinity_t *water = (dc_salinity_t *) value; dc_salinity_t *water = (dc_salinity_t *) value;
dc_decomodel_t *decomodel = (dc_decomodel_t *) value;
if (value) { if (value) {
switch (type) { switch (type) {
@ -295,6 +289,7 @@ divesystem_idive_parser_get_field (dc_parser_t *abstract, dc_field_type_t type,
*((unsigned int *) value) = parser->ngasmixes; *((unsigned int *) value) = parser->ngasmixes;
break; break;
case DC_FIELD_GASMIX: case DC_FIELD_GASMIX:
gasmix->usage = DC_USAGE_NONE;
gasmix->helium = parser->gasmix[flags].helium / 100.0; gasmix->helium = parser->gasmix[flags].helium / 100.0;
gasmix->oxygen = parser->gasmix[flags].oxygen / 100.0; gasmix->oxygen = parser->gasmix[flags].oxygen / 100.0;
gasmix->nitrogen = 1.0 - gasmix->oxygen - gasmix->helium; gasmix->nitrogen = 1.0 - gasmix->oxygen - gasmix->helium;
@ -309,6 +304,7 @@ divesystem_idive_parser_get_field (dc_parser_t *abstract, dc_field_type_t type,
tank->beginpressure = parser->tank[flags].beginpressure; tank->beginpressure = parser->tank[flags].beginpressure;
tank->endpressure = parser->tank[flags].endpressure; tank->endpressure = parser->tank[flags].endpressure;
tank->gasmix = DC_GASMIX_UNKNOWN; tank->gasmix = DC_GASMIX_UNKNOWN;
tank->usage = DC_USAGE_NONE;
break; break;
case DC_FIELD_ATMOSPHERIC: case DC_FIELD_ATMOSPHERIC:
if (ISIX3M(parser->model)) { if (ISIX3M(parser->model)) {
@ -322,7 +318,7 @@ divesystem_idive_parser_get_field (dc_parser_t *abstract, dc_field_type_t type,
water->density = 0.0; water->density = 0.0;
break; break;
case DC_FIELD_DIVEMODE: case DC_FIELD_DIVEMODE:
if (parser->divemode == 0xFFFFFFFF) if (parser->divemode == INVALID)
return DC_STATUS_UNSUPPORTED; return DC_STATUS_UNSUPPORTED;
switch (parser->divemode) { switch (parser->divemode) {
case OC: case OC:
@ -345,6 +341,46 @@ divesystem_idive_parser_get_field (dc_parser_t *abstract, dc_field_type_t type,
return DC_STATUS_DATAFORMAT; return DC_STATUS_DATAFORMAT;
} }
break; break;
case DC_FIELD_DECOMODEL:
if (parser->algorithm == INVALID)
return DC_STATUS_UNSUPPORTED;
if (ISIX3M2(parser->model)) {
switch (parser->algorithm) {
case IX3M2_BUHLMANN:
case IX3M2_ZHL16B:
case IX3M2_ZHL16C:
decomodel->type = DC_DECOMODEL_BUHLMANN;
decomodel->conservatism = 0;
decomodel->params.gf.low = parser->gf_low;
decomodel->params.gf.high = parser->gf_high;
break;
case IX3M2_VPM:
decomodel->type = DC_DECOMODEL_VPM;
decomodel->conservatism = 0;
break;
default:
ERROR (abstract->context, "Unknown deco algorithm %02x.", parser->algorithm);
return DC_STATUS_DATAFORMAT;
}
} else {
switch (parser->algorithm) {
case BUHLMANN:
case DUAL:
decomodel->type = DC_DECOMODEL_BUHLMANN;
decomodel->conservatism = 0;
decomodel->params.gf.low = parser->gf_low;
decomodel->params.gf.high = parser->gf_high;
break;
case VPM:
decomodel->type = DC_DECOMODEL_VPM;
decomodel->conservatism = 0;
break;
default:
ERROR (abstract->context, "Unknown deco algorithm %02x.", parser->algorithm);
return DC_STATUS_DATAFORMAT;
}
}
break;
default: default:
return DC_STATUS_UNSUPPORTED; return DC_STATUS_UNSUPPORTED;
} }
@ -367,12 +403,17 @@ divesystem_idive_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callba
unsigned int ntanks = 0; unsigned int ntanks = 0;
divesystem_idive_gasmix_t gasmix[NGASMIXES] = {0}; divesystem_idive_gasmix_t gasmix[NGASMIXES] = {0};
divesystem_idive_tank_t tank[NTANKS] = {0}; divesystem_idive_tank_t tank[NTANKS] = {0};
unsigned int o2_previous = 0xFFFFFFFF; unsigned int o2_previous = INVALID;
unsigned int he_previous = 0xFFFFFFFF; unsigned int he_previous = INVALID;
unsigned int mode_previous = INVALID; unsigned int mode_previous = INVALID;
unsigned int divemode = INVALID; unsigned int divemode = INVALID;
unsigned int tank_previous = INVALID; unsigned int tank_previous = INVALID;
unsigned int tank_idx = INVALID; unsigned int tank_idx = INVALID;
unsigned int algorithm = INVALID;
unsigned int algorithm_previous = INVALID;
unsigned int gf_low = INVALID;
unsigned int gf_high = INVALID;
unsigned int have_bearing = 0;
unsigned int firmware = 0; unsigned int firmware = 0;
unsigned int apos4 = 0; unsigned int apos4 = 0;
@ -401,27 +442,37 @@ divesystem_idive_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callba
while (offset + samplesize <= size) { while (offset + samplesize <= size) {
dc_sample_value_t sample = {0}; dc_sample_value_t sample = {0};
// Get the record type.
unsigned int type = ISIX3M(parser->model) ?
array_uint16_le (data + offset + 52) :
REC_SAMPLE;
if (type != REC_SAMPLE) {
// Skip non-sample records.
offset += samplesize;
continue;
}
// Time (seconds). // Time (seconds).
unsigned int timestamp = array_uint32_le (data + offset + 2); unsigned int timestamp = array_uint32_le (data + offset + 2);
if (timestamp <= time) { if (timestamp <= time && time != 0) {
ERROR (abstract->context, "Timestamp moved backwards."); ERROR (abstract->context, "Timestamp moved backwards (%u %u).", timestamp, time);
return DC_STATUS_DATAFORMAT; return DC_STATUS_DATAFORMAT;
} }
time = timestamp; time = timestamp;
sample.time = timestamp; sample.time = timestamp * 1000;
if (callback) callback (DC_SAMPLE_TIME, sample, userdata); if (callback) callback (DC_SAMPLE_TIME, &sample, userdata);
// Depth (1/10 m). // Depth (1/10 m).
unsigned int depth = array_uint16_le (data + offset + 6); unsigned int depth = array_uint16_le (data + offset + 6);
if (maxdepth < depth) if (maxdepth < depth)
maxdepth = depth; maxdepth = depth;
sample.depth = depth / 10.0; sample.depth = depth / 10.0;
if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata); if (callback) callback (DC_SAMPLE_DEPTH, &sample, userdata);
// Temperature (Celsius). // Temperature (Celsius).
signed int temperature = (signed short) array_uint16_le (data + offset + 8); signed int temperature = (signed short) array_uint16_le (data + offset + 8);
sample.temperature = temperature / 10.0; sample.temperature = temperature / 10.0;
if (callback) callback (DC_SAMPLE_TEMPERATURE, sample, userdata); if (callback) callback (DC_SAMPLE_TEMPERATURE, &sample, userdata);
// Dive mode // Dive mode
unsigned int mode = data[offset + 18]; unsigned int mode = data[offset + 18];
@ -435,11 +486,27 @@ divesystem_idive_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callba
divemode = mode; divemode = mode;
} }
// Deco model
unsigned int s_algorithm = data[offset + 14];
unsigned int s_gf_high = data[offset + 15];
unsigned int s_gf_low = data[offset + 16];
if (s_algorithm != algorithm_previous) {
if (algorithm_previous != INVALID) {
WARNING (abstract->context, "Deco algorithm changed from %02x to %02x.", algorithm_previous, s_algorithm);
}
algorithm_previous = s_algorithm;
}
if (algorithm == INVALID) {
algorithm = s_algorithm;
gf_low = s_gf_low;
gf_high = s_gf_high;
}
// Setpoint // Setpoint
if (mode == SCR || mode == CCR) { if (mode == SCR || mode == CCR) {
unsigned int setpoint = array_uint16_le (data + offset + 19); unsigned int setpoint = array_uint16_le (data + offset + 19);
sample.setpoint = setpoint / 1000.0; sample.setpoint = setpoint / 1000.0;
if (callback) callback (DC_SAMPLE_SETPOINT, sample, userdata); if (callback) callback (DC_SAMPLE_SETPOINT, &sample, userdata);
} }
// Gaschange. // Gaschange.
@ -466,7 +533,7 @@ divesystem_idive_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callba
} }
sample.gasmix = i; sample.gasmix = i;
if (callback) callback (DC_SAMPLE_GASMIX, sample, userdata); if (callback) callback (DC_SAMPLE_GASMIX, &sample, userdata);
o2_previous = o2; o2_previous = o2;
he_previous = he; he_previous = he;
} }
@ -484,18 +551,20 @@ divesystem_idive_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callba
if (decostop) { if (decostop) {
sample.deco.type = DC_DECO_DECOSTOP; sample.deco.type = DC_DECO_DECOSTOP;
sample.deco.depth = decostop / 10.0; sample.deco.depth = decostop / 10.0;
sample.deco.time = apos4 ? decotime : tts; sample.deco.time = decotime;
sample.deco.tts = tts;
} else { } else {
sample.deco.type = DC_DECO_NDL; sample.deco.type = DC_DECO_NDL;
sample.deco.depth = 0.0; sample.deco.depth = 0.0;
sample.deco.time = tts; sample.deco.time = tts;
sample.deco.tts = 0;
} }
if (callback) callback (DC_SAMPLE_DECO, sample, userdata); if (callback) callback (DC_SAMPLE_DECO, &sample, userdata);
// CNS // CNS
unsigned int cns = array_uint16_le (data + offset + 29); unsigned int cns = array_uint16_le (data + offset + 29);
sample.cns = cns / 100.0; sample.cns = cns / 100.0;
if (callback) callback (DC_SAMPLE_CNS, sample, userdata); if (callback) callback (DC_SAMPLE_CNS, &sample, userdata);
// Tank Pressure // Tank Pressure
if (samplesize == SZ_SAMPLE_IX3M_APOS4) { if (samplesize == SZ_SAMPLE_IX3M_APOS4) {
@ -503,6 +572,11 @@ divesystem_idive_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callba
unsigned int flags = data[offset + 47] & 0xF0; unsigned int flags = data[offset + 47] & 0xF0;
unsigned int pressure = data[offset + 49]; unsigned int pressure = data[offset + 49];
if (flags & 0x20) {
// 300 bar transmitter.
pressure *= 2;
}
if (flags & 0x80) { if (flags & 0x80) {
// No active transmitter available // No active transmitter available
} else if (flags & 0x40) { } else if (flags & 0x40) {
@ -511,7 +585,7 @@ divesystem_idive_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callba
sample.event.time = 0; sample.event.time = 0;
sample.event.flags = 0; sample.event.flags = 0;
sample.event.value = 0; sample.event.value = 0;
if (callback) callback (DC_SAMPLE_EVENT, sample, userdata); if (callback) callback (DC_SAMPLE_EVENT, &sample, userdata);
} else { } else {
// Get the index of the tank. // Get the index of the tank.
if (id != tank_previous) { if (id != tank_previous) {
@ -541,10 +615,20 @@ divesystem_idive_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callba
if (tank_idx < ntanks) { if (tank_idx < ntanks) {
sample.pressure.tank = tank_idx; sample.pressure.tank = tank_idx;
sample.pressure.value = pressure; sample.pressure.value = pressure;
if (callback) callback (DC_SAMPLE_PRESSURE, sample, userdata); if (callback) callback (DC_SAMPLE_PRESSURE, &sample, userdata);
tank[tank_idx].endpressure = pressure; tank[tank_idx].endpressure = pressure;
} }
} }
// Compass bearing
unsigned int bearing = array_uint16_le (data + offset + 50);
if (bearing != 0) {
have_bearing = 1; // Stop ignoring zero values.
}
if (have_bearing && bearing != 0xFFFF) {
sample.bearing = bearing;
if (callback) callback (DC_SAMPLE_BEARING, &sample, userdata);
}
} }
offset += samplesize; offset += samplesize;
@ -562,6 +646,9 @@ divesystem_idive_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callba
parser->maxdepth = maxdepth; parser->maxdepth = maxdepth;
parser->divetime = time; parser->divetime = time;
parser->divemode = divemode; parser->divemode = divemode;
parser->algorithm = algorithm;
parser->gf_low = gf_low;
parser->gf_high = gf_high;
parser->cached = 1; parser->cached = 1;
return DC_STATUS_SUCCESS; return DC_STATUS_SUCCESS;

406
src/hdlc.c Normal file
View File

@ -0,0 +1,406 @@
/*
* libdivecomputer
*
* Copyright (C) 2023 Jef Driesen
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
#include <stdlib.h> // malloc, free
#include "hdlc.h"
#include "iostream-private.h"
#include "common-private.h"
#include "context-private.h"
#define END 0x7E
#define ESC 0x7D
#define ESC_BIT 0x20
static dc_status_t dc_hdlc_set_timeout (dc_iostream_t *abstract, int timeout);
static dc_status_t dc_hdlc_set_break (dc_iostream_t *abstract, unsigned int value);
static dc_status_t dc_hdlc_set_dtr (dc_iostream_t *abstract, unsigned int value);
static dc_status_t dc_hdlc_set_rts (dc_iostream_t *abstract, unsigned int value);
static dc_status_t dc_hdlc_get_lines (dc_iostream_t *abstract, unsigned int *value);
static dc_status_t dc_hdlc_configure (dc_iostream_t *abstract, unsigned int baudrate, unsigned int databits, dc_parity_t parity, dc_stopbits_t stopbits, dc_flowcontrol_t flowcontrol);
static dc_status_t dc_hdlc_poll (dc_iostream_t *abstract, int timeout);
static dc_status_t dc_hdlc_read (dc_iostream_t *abstract, void *data, size_t size, size_t *actual);
static dc_status_t dc_hdlc_write (dc_iostream_t *abstract, const void *data, size_t size, size_t *actual);
static dc_status_t dc_hdlc_ioctl (dc_iostream_t *abstract, unsigned int request, void *data, size_t size);
static dc_status_t dc_hdlc_flush (dc_iostream_t *abstract);
static dc_status_t dc_hdlc_purge (dc_iostream_t *abstract, dc_direction_t direction);
static dc_status_t dc_hdlc_sleep (dc_iostream_t *abstract, unsigned int milliseconds);
static dc_status_t dc_hdlc_close (dc_iostream_t *abstract);
typedef struct dc_hdlc_t {
/* Base class. */
dc_iostream_t base;
/* Internal state. */
dc_context_t *context;
dc_iostream_t *iostream;
unsigned char *rbuf;
unsigned char *wbuf;
size_t rbuf_size;
size_t rbuf_offset;
size_t rbuf_available;
size_t wbuf_size;
size_t wbuf_offset;
} dc_hdlc_t;
static const dc_iostream_vtable_t dc_hdlc_vtable = {
sizeof(dc_hdlc_t),
dc_hdlc_set_timeout, /* set_timeout */
dc_hdlc_set_break, /* set_break */
dc_hdlc_set_dtr, /* set_dtr */
dc_hdlc_set_rts, /* set_rts */
dc_hdlc_get_lines, /* get_lines */
NULL, /* get_available */
dc_hdlc_configure, /* configure */
dc_hdlc_poll, /* poll */
dc_hdlc_read, /* read */
dc_hdlc_write, /* write */
dc_hdlc_ioctl, /* ioctl */
dc_hdlc_flush, /* flush */
dc_hdlc_purge, /* purge */
dc_hdlc_sleep, /* sleep */
dc_hdlc_close, /* close */
};
dc_status_t
dc_hdlc_open (dc_iostream_t **out, dc_context_t *context, dc_iostream_t *base, size_t isize, size_t osize)
{
dc_status_t status = DC_STATUS_SUCCESS;
dc_hdlc_t *hdlc = NULL;
if (out == NULL)
return DC_STATUS_INVALIDARGS;
if (base == NULL || isize == 0 || osize == 0)
return DC_STATUS_INVALIDARGS;
dc_transport_t transport = dc_iostream_get_transport (base);
// Allocate memory.
hdlc = (dc_hdlc_t *) dc_iostream_allocate (NULL, &dc_hdlc_vtable, transport);
if (hdlc == NULL) {
ERROR (context, "Failed to allocate memory.");
status = DC_STATUS_NOMEMORY;
goto error_exit;
}
// Allocate the read buffer.
hdlc->rbuf = malloc (isize);
if (hdlc->rbuf == NULL) {
ERROR (context, "Failed to allocate memory.");
status = DC_STATUS_NOMEMORY;
goto error_free;
}
// Allocate the write buffer.
hdlc->wbuf = malloc (osize);
if (hdlc->wbuf == NULL) {
ERROR (context, "Failed to allocate memory.");
status = DC_STATUS_NOMEMORY;
goto error_free_rbuf;
}
hdlc->context = context;
hdlc->iostream = base;
hdlc->rbuf_size = isize;
hdlc->rbuf_offset = 0;
hdlc->rbuf_available = 0;
hdlc->wbuf_size = osize;
hdlc->wbuf_offset = 0;
*out = (dc_iostream_t *) hdlc;
return DC_STATUS_SUCCESS;
error_free_rbuf:
free (hdlc->rbuf);
error_free:
dc_iostream_deallocate ((dc_iostream_t *) hdlc);
error_exit:
return status;
}
static dc_status_t
dc_hdlc_set_timeout (dc_iostream_t *abstract, int timeout)
{
dc_hdlc_t *hdlc = (dc_hdlc_t *) abstract;
return dc_iostream_set_timeout (hdlc->iostream, timeout);
}
static dc_status_t
dc_hdlc_set_break (dc_iostream_t *abstract, unsigned int value)
{
dc_hdlc_t *hdlc = (dc_hdlc_t *) abstract;
return dc_iostream_set_break (hdlc->iostream, value);
}
static dc_status_t
dc_hdlc_set_dtr (dc_iostream_t *abstract, unsigned int value)
{
dc_hdlc_t *hdlc = (dc_hdlc_t *) abstract;
return dc_iostream_set_dtr (hdlc->iostream, value);
}
static dc_status_t
dc_hdlc_set_rts (dc_iostream_t *abstract, unsigned int value)
{
dc_hdlc_t *hdlc = (dc_hdlc_t *) abstract;
return dc_iostream_set_rts (hdlc->iostream, value);
}
static dc_status_t
dc_hdlc_get_lines (dc_iostream_t *abstract, unsigned int *value)
{
dc_hdlc_t *hdlc = (dc_hdlc_t *) abstract;
return dc_iostream_get_lines (hdlc->iostream, value);
}
static dc_status_t
dc_hdlc_configure (dc_iostream_t *abstract, unsigned int baudrate, unsigned int databits, dc_parity_t parity, dc_stopbits_t stopbits, dc_flowcontrol_t flowcontrol)
{
dc_hdlc_t *hdlc = (dc_hdlc_t *) abstract;
return dc_iostream_configure (hdlc->iostream, baudrate, databits, parity, stopbits, flowcontrol);
}
static dc_status_t
dc_hdlc_poll (dc_iostream_t *abstract, int timeout)
{
dc_hdlc_t *hdlc = (dc_hdlc_t *) abstract;
if (hdlc->rbuf_available) {
return DC_STATUS_SUCCESS;
}
return dc_iostream_poll (hdlc->iostream, timeout);
}
static dc_status_t
dc_hdlc_read (dc_iostream_t *abstract, void *data, size_t size, size_t *actual)
{
dc_status_t status = DC_STATUS_SUCCESS;
dc_hdlc_t *hdlc = (dc_hdlc_t *) abstract;
size_t nbytes = 0;
unsigned int initialized = 0;
unsigned int escaped = 0;
while (1) {
if (hdlc->rbuf_available == 0) {
// Read a packet into the cache.
size_t len = 0;
status = dc_iostream_read (hdlc->iostream, hdlc->rbuf, hdlc->rbuf_size, &len);
if (status != DC_STATUS_SUCCESS) {
goto out;
}
hdlc->rbuf_available = len;
hdlc->rbuf_offset = 0;
}
while (hdlc->rbuf_available) {
unsigned char c = hdlc->rbuf[hdlc->rbuf_offset];
hdlc->rbuf_offset++;
hdlc->rbuf_available--;
if (c == END) {
if (escaped) {
ERROR (hdlc->context, "HDLC frame escaped the special character %02x.", c);
status = DC_STATUS_IO;
goto out;
}
if (initialized) {
goto out;
}
initialized = 1;
continue;
}
if (!initialized) {
continue;
}
if (c == ESC) {
if (escaped) {
ERROR (hdlc->context, "HDLC frame escaped the special character %02x.", c);
status = DC_STATUS_IO;
goto out;
}
escaped = 1;
continue;
}
if (escaped) {
c ^= ESC_BIT;
escaped = 0;
}
if (nbytes < size)
((unsigned char *)data)[nbytes] = c;
nbytes++;
}
}
out:
if (nbytes > size) {
ERROR (hdlc->context, "HDLC frame is too large (" DC_PRINTF_SIZE " " DC_PRINTF_SIZE ").", nbytes, size);
dc_status_set_error (&status, DC_STATUS_IO);
nbytes = size;
}
if (actual)
*actual = nbytes;
return status;
}
static dc_status_t
dc_hdlc_write (dc_iostream_t *abstract, const void *data, size_t size, size_t *actual)
{
dc_status_t status = DC_STATUS_SUCCESS;
dc_hdlc_t *hdlc = (dc_hdlc_t *) abstract;
size_t nbytes = 0;
// Clear the buffer.
hdlc->wbuf_offset = 0;
// Start of the packet.
hdlc->wbuf[hdlc->wbuf_offset++] = END;
// Flush the buffer if necessary.
if (hdlc->wbuf_offset >= hdlc->wbuf_size) {
status = dc_iostream_write (hdlc->iostream, hdlc->wbuf, hdlc->wbuf_offset, NULL);
if (status != DC_STATUS_SUCCESS) {
goto out;
}
hdlc->wbuf_offset = 0;
}
while (nbytes < size) {
unsigned char c = ((const unsigned char *) data)[nbytes];
if (c == END || c == ESC) {
// Append the escape character.
hdlc->wbuf[hdlc->wbuf_offset++] = ESC;
// Flush the buffer if necessary.
if (hdlc->wbuf_offset >= hdlc->wbuf_size) {
status = dc_iostream_write (hdlc->iostream, hdlc->wbuf, hdlc->wbuf_offset, NULL);
if (status != DC_STATUS_SUCCESS) {
goto out;
}
hdlc->wbuf_offset = 0;
}
// Escape the character.
c ^= ESC_BIT;
}
// Append the character.
hdlc->wbuf[hdlc->wbuf_offset++] = c;
// Flush the buffer if necessary.
if (hdlc->wbuf_offset >= hdlc->wbuf_size) {
status = dc_iostream_write (hdlc->iostream, hdlc->wbuf, hdlc->wbuf_offset, NULL);
if (status != DC_STATUS_SUCCESS) {
goto out;
}
hdlc->wbuf_offset = 0;
}
nbytes++;
}
// End of the packet.
hdlc->wbuf[hdlc->wbuf_offset++] = END;
// Flush the buffer.
status = dc_iostream_write (hdlc->iostream, hdlc->wbuf, hdlc->wbuf_offset, NULL);
if (status != DC_STATUS_SUCCESS) {
goto out;
}
hdlc->wbuf_offset = 0;
out:
if (actual)
*actual = nbytes;
return status;
}
static dc_status_t
dc_hdlc_ioctl (dc_iostream_t *abstract, unsigned int request, void *data, size_t size)
{
dc_hdlc_t *hdlc = (dc_hdlc_t *) abstract;
return dc_iostream_ioctl (hdlc->iostream, request, data, size);
}
static dc_status_t
dc_hdlc_flush (dc_iostream_t *abstract)
{
dc_hdlc_t *hdlc = (dc_hdlc_t *) abstract;
return dc_iostream_flush (hdlc->iostream);
}
static dc_status_t
dc_hdlc_purge (dc_iostream_t *abstract, dc_direction_t direction)
{
dc_hdlc_t *hdlc = (dc_hdlc_t *) abstract;
if (direction & DC_DIRECTION_INPUT) {
hdlc->rbuf_available = 0;
hdlc->rbuf_offset = 0;
}
return dc_iostream_purge (hdlc->iostream, direction);
}
static dc_status_t
dc_hdlc_sleep (dc_iostream_t *abstract, unsigned int milliseconds)
{
dc_hdlc_t *hdlc = (dc_hdlc_t *) abstract;
return dc_iostream_sleep (hdlc->iostream, milliseconds);
}
static dc_status_t
dc_hdlc_close (dc_iostream_t *abstract)
{
dc_hdlc_t *hdlc = (dc_hdlc_t *) abstract;
free (hdlc->wbuf);
free (hdlc->rbuf);
return DC_STATUS_SUCCESS;
}

50
src/hdlc.h Normal file
View File

@ -0,0 +1,50 @@
/*
* libdivecomputer
*
* Copyright (C) 2023 Jef Driesen
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
#ifndef DC_HDLC_H
#define DC_HDLC_H
#include <libdivecomputer/common.h>
#include <libdivecomputer/context.h>
#include <libdivecomputer/iostream.h>
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
/**
* Create a HDLC I/O stream layered on top of another base I/O stream.
*
* @param[out] iostream A location to store the HDLC I/O stream.
* @param[in] context A valid context.
* @param[in] base A valid I/O stream.
* @param[in] isize The input packet size in bytes.
* @param[in] osize The output packet size in bytes.
* @returns #DC_STATUS_SUCCESS on success, or another #dc_status_t code
* on failure.
*/
dc_status_t
dc_hdlc_open (dc_iostream_t **iostream, dc_context_t *context, dc_iostream_t *base, size_t isize, size_t osize);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* DC_HDLC_H */

View File

@ -40,7 +40,7 @@
#define RB_PROFILE_BEGIN 0x000000 #define RB_PROFILE_BEGIN 0x000000
#define RB_PROFILE_END 0x200000 #define RB_PROFILE_END 0x200000
#define RB_PROFILE_DISTANCE(a,b) ringbuffer_distance (a, b, 0, RB_PROFILE_BEGIN, RB_PROFILE_END) #define RB_PROFILE_DISTANCE(a,b) ringbuffer_distance (a, b, DC_RINGBUFFER_EMPTY, RB_PROFILE_BEGIN, RB_PROFILE_END)
#define READY 0x4D #define READY 0x4D
#define HEADER 0x61 #define HEADER 0x61
@ -473,11 +473,6 @@ hw_frog_device_timesync (dc_device_t *abstract, const dc_datetime_t *datetime)
{ {
hw_frog_device_t *device = (hw_frog_device_t *) abstract; hw_frog_device_t *device = (hw_frog_device_t *) abstract;
if (datetime == NULL) {
ERROR (abstract->context, "Invalid parameter specified.");
return DC_STATUS_INVALIDARGS;
}
// Send the command. // Send the command.
unsigned char packet[6] = { unsigned char packet[6] = {
datetime->hour, datetime->minute, datetime->second, datetime->hour, datetime->minute, datetime->second,

View File

@ -31,8 +31,6 @@
#define ISINSTANCE(device) dc_device_isinstance((device), &hw_ostc_device_vtable) #define ISINSTANCE(device) dc_device_isinstance((device), &hw_ostc_device_vtable)
#define C_ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a)))
#define MAXRETRIES 9 #define MAXRETRIES 9
#define FW_190 0x015A #define FW_190 0x015A
@ -274,6 +272,20 @@ hw_ostc_device_dump (dc_device_t *abstract, dc_buffer_t *buffer)
nbytes += len; nbytes += len;
} }
// Emit a device info event.
dc_event_devinfo_t devinfo;
devinfo.firmware = array_uint16_be (data + 264);
devinfo.serial = array_uint16_le (data + 6);
if (devinfo.serial > 7000)
devinfo.model = 3; // OSTC 2C
else if (devinfo.serial > 2048)
devinfo.model = 2; // OSTC 2N
else if (devinfo.serial > 300)
devinfo.model = 1; // OSTC Mk2
else
devinfo.model = 0; // OSTC
device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo);
return DC_STATUS_SUCCESS; return DC_STATUS_SUCCESS;
} }
@ -291,21 +303,6 @@ hw_ostc_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void
return rc; return rc;
} }
// Emit a device info event.
unsigned char *data = dc_buffer_get_data (buffer);
dc_event_devinfo_t devinfo;
devinfo.firmware = array_uint16_be (data + 264);
devinfo.serial = array_uint16_le (data + 6);
if (devinfo.serial > 7000)
devinfo.model = 3; // OSTC 2C
else if (devinfo.serial > 2048)
devinfo.model = 2; // OSTC 2N
else if (devinfo.serial > 300)
devinfo.model = 1; // OSTC Mk2
else
devinfo.model = 0; // OSTC
device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo);
rc = hw_ostc_extract_dives (abstract, dc_buffer_get_data (buffer), rc = hw_ostc_extract_dives (abstract, dc_buffer_get_data (buffer),
dc_buffer_get_size (buffer), callback, userdata); dc_buffer_get_size (buffer), callback, userdata);
@ -351,11 +348,6 @@ hw_ostc_device_timesync (dc_device_t *abstract, const dc_datetime_t *datetime)
dc_status_t status = DC_STATUS_SUCCESS; dc_status_t status = DC_STATUS_SUCCESS;
hw_ostc_device_t *device = (hw_ostc_device_t *) abstract; hw_ostc_device_t *device = (hw_ostc_device_t *) abstract;
if (datetime == NULL) {
ERROR (abstract->context, "Invalid parameter specified.");
return DC_STATUS_INVALIDARGS;
}
// Send the command. // Send the command.
dc_status_t rc = hw_ostc_send (device, 'b', 1); dc_status_t rc = hw_ostc_send (device, 'b', 1);
if (rc != DC_STATUS_SUCCESS) if (rc != DC_STATUS_SUCCESS)

View File

@ -36,7 +36,7 @@ dc_status_t
hw_ostc_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream); hw_ostc_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream);
dc_status_t dc_status_t
hw_ostc_parser_create (dc_parser_t **parser, dc_context_t *context); hw_ostc_parser_create (dc_parser_t **parser, dc_context_t *context, const unsigned char data[], size_t size);
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@ -30,9 +30,14 @@
#include "array.h" #include "array.h"
#include "aes.h" #include "aes.h"
#include "platform.h" #include "platform.h"
#include "packet.h"
#define ISINSTANCE(device) dc_device_isinstance((device), &hw_ostc3_device_vtable) #define ISINSTANCE(device) dc_device_isinstance((device), &hw_ostc3_device_vtable)
#define OSTC3FW(major,minor) ( \
(((major) & 0xFF) << 8) | \
((minor) & 0xFF))
#define SZ_DISPLAY 16 #define SZ_DISPLAY 16
#define SZ_CUSTOMTEXT 60 #define SZ_CUSTOMTEXT 60
#define SZ_VERSION (SZ_CUSTOMTEXT + 4) #define SZ_VERSION (SZ_CUSTOMTEXT + 4)
@ -85,6 +90,19 @@
#define NODELAY 0 #define NODELAY 0
#define TIMEOUT 400 #define TIMEOUT 400
#define HDR_COMPACT_LENGTH 0 // 3 bytes
#define HDR_COMPACT_SUMMARY 3 // 10 bytes
#define HDR_COMPACT_NUMBER 13 // 2 bytes
#define HDR_COMPACT_VERSION 15 // 1 byte
#define HDR_FULL_LENGTH 9 // 3 bytes
#define HDR_FULL_SUMMARY 12 // 10 bytes
#define HDR_FULL_NUMBER 80 // 2 bytes
#define HDR_FULL_VERSION 8 // 1 byte
#define HDR_FULL_POINTERS 2 // 6 bytes
#define HDR_FULL_FIRMWARE 48 // 2 bytes
typedef enum hw_ostc3_state_t { typedef enum hw_ostc3_state_t {
OPEN, OPEN,
DOWNLOAD, DOWNLOAD,
@ -102,9 +120,6 @@ typedef struct hw_ostc3_device_t {
unsigned int firmware; unsigned int firmware;
unsigned char fingerprint[5]; unsigned char fingerprint[5];
hw_ostc3_state_t state; hw_ostc3_state_t state;
unsigned char cache[20];
unsigned int available;
unsigned int offset;
} hw_ostc3_device_t; } hw_ostc3_device_t;
typedef struct hw_ostc3_logbook_t { typedef struct hw_ostc3_logbook_t {
@ -112,6 +127,7 @@ typedef struct hw_ostc3_logbook_t {
unsigned int profile; unsigned int profile;
unsigned int fingerprint; unsigned int fingerprint;
unsigned int number; unsigned int number;
unsigned int version;
} hw_ostc3_logbook_t; } hw_ostc3_logbook_t;
typedef struct hw_ostc3_firmware_t { typedef struct hw_ostc3_firmware_t {
@ -151,16 +167,18 @@ static const dc_device_vtable_t hw_ostc3_device_vtable = {
static const hw_ostc3_logbook_t hw_ostc3_logbook_compact = { static const hw_ostc3_logbook_t hw_ostc3_logbook_compact = {
RB_LOGBOOK_SIZE_COMPACT, /* size */ RB_LOGBOOK_SIZE_COMPACT, /* size */
0, /* profile */ HDR_COMPACT_LENGTH, /* profile */
3, /* fingerprint */ HDR_COMPACT_SUMMARY, /* fingerprint */
13, /* number */ HDR_COMPACT_NUMBER, /* number */
HDR_COMPACT_VERSION, /* version */
}; };
static const hw_ostc3_logbook_t hw_ostc3_logbook_full = { static const hw_ostc3_logbook_t hw_ostc3_logbook_full = {
RB_LOGBOOK_SIZE_FULL, /* size */ RB_LOGBOOK_SIZE_FULL, /* size */
9, /* profile */ HDR_FULL_LENGTH, /* profile */
12, /* fingerprint */ HDR_FULL_SUMMARY, /* fingerprint */
80, /* number */ HDR_FULL_NUMBER, /* number */
HDR_FULL_VERSION, /* version */
}; };
@ -187,41 +205,20 @@ static dc_status_t
hw_ostc3_read (hw_ostc3_device_t *device, dc_event_progress_t *progress, unsigned char data[], size_t size) hw_ostc3_read (hw_ostc3_device_t *device, dc_event_progress_t *progress, unsigned char data[], size_t size)
{ {
dc_status_t rc = DC_STATUS_SUCCESS; dc_status_t rc = DC_STATUS_SUCCESS;
dc_transport_t transport = dc_iostream_get_transport(device->iostream);
size_t nbytes = 0; size_t nbytes = 0;
while (nbytes < size) { while (nbytes < size) {
if (transport == DC_TRANSPORT_BLE) {
if (device->available == 0) {
// Read a packet into the cache.
size_t len = 0;
rc = dc_iostream_read (device->iostream, device->cache, sizeof(device->cache), &len);
if (rc != DC_STATUS_SUCCESS)
return rc;
device->available = len;
device->offset = 0;
}
}
// Set the minimum packet size. // Set the minimum packet size.
size_t length = (transport == DC_TRANSPORT_BLE) ? device->available : 1024; size_t length = 1024;
// Limit the packet size to the total size. // Limit the packet size to the total size.
if (nbytes + length > size) if (nbytes + length > size)
length = size - nbytes; length = size - nbytes;
if (transport == DC_TRANSPORT_BLE) { // Read the packet.
// Copy the data from the cached packet. rc = dc_iostream_read (device->iostream, data + nbytes, length, NULL);
memcpy (data + nbytes, device->cache + device->offset, length); if (rc != DC_STATUS_SUCCESS)
device->available -= length; return rc;
device->offset += length;
} else {
// Read the packet.
rc = dc_iostream_read (device->iostream, data + nbytes, length, NULL);
if (rc != DC_STATUS_SUCCESS)
return rc;
}
// Update and emit a progress event. // Update and emit a progress event.
if (progress) { if (progress) {
@ -239,12 +236,11 @@ static dc_status_t
hw_ostc3_write (hw_ostc3_device_t *device, dc_event_progress_t *progress, const unsigned char data[], size_t size) hw_ostc3_write (hw_ostc3_device_t *device, dc_event_progress_t *progress, const unsigned char data[], size_t size)
{ {
dc_status_t rc = DC_STATUS_SUCCESS; dc_status_t rc = DC_STATUS_SUCCESS;
dc_transport_t transport = dc_iostream_get_transport(device->iostream);
size_t nbytes = 0; size_t nbytes = 0;
while (nbytes < size) { while (nbytes < size) {
// Set the maximum packet size. // Set the maximum packet size.
size_t length = (transport == DC_TRANSPORT_BLE) ? sizeof(device->cache) : 64; size_t length = (device->hardware == OSTC4) ? 64 : 1024;
// Limit the packet size to the total size. // Limit the packet size to the total size.
if (nbytes + length > size) if (nbytes + length > size)
@ -275,10 +271,15 @@ hw_ostc3_transfer (hw_ostc3_device_t *device,
unsigned int isize, unsigned int isize,
unsigned char output[], unsigned char output[],
unsigned int osize, unsigned int osize,
unsigned int *actual,
unsigned int delay) unsigned int delay)
{ {
dc_device_t *abstract = (dc_device_t *) device; dc_device_t *abstract = (dc_device_t *) device;
dc_status_t status = DC_STATUS_SUCCESS; dc_status_t status = DC_STATUS_SUCCESS;
unsigned int length = osize;
if (cmd == DIVE && length < RB_LOGBOOK_SIZE_FULL)
return DC_STATUS_INVALIDARGS;
if (device_is_cancelled (abstract)) if (device_is_cancelled (abstract))
return DC_STATUS_CANCELLED; return DC_STATUS_CANCELLED;
@ -288,7 +289,7 @@ hw_ostc3_transfer (hw_ostc3_device_t *device,
// Send the command. // Send the command.
unsigned char command[1] = {cmd}; unsigned char command[1] = {cmd};
status = hw_ostc3_write (device, NULL, command, sizeof (command)); status = dc_iostream_write (device->iostream, command, sizeof (command), NULL);
if (status != DC_STATUS_SUCCESS) { if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to send the command."); ERROR (abstract->context, "Failed to send the command.");
return status; return status;
@ -296,7 +297,7 @@ hw_ostc3_transfer (hw_ostc3_device_t *device,
// Read the echo. // Read the echo.
unsigned char echo[1] = {0}; unsigned char echo[1] = {0};
status = hw_ostc3_read (device, NULL, echo, sizeof (echo)); status = dc_iostream_read (device->iostream, echo, sizeof (echo), NULL);
if (status != DC_STATUS_SUCCESS) { if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to receive the echo."); ERROR (abstract->context, "Failed to receive the echo.");
return status; return status;
@ -314,31 +315,82 @@ hw_ostc3_transfer (hw_ostc3_device_t *device,
} }
if (input) { if (input) {
// Send the input data packet. if (cmd == WRITE) {
status = hw_ostc3_write (device, progress, input, isize); // Send the first byte of the input data packet.
if (status != DC_STATUS_SUCCESS) { status = hw_ostc3_write (device, progress, input, 1);
ERROR (abstract->context, "Failed to send the data packet."); if (status != DC_STATUS_SUCCESS) {
return status; ERROR (abstract->context, "Failed to send the data packet.");
return status;
}
dc_iostream_sleep (device->iostream, 10);
// Send the reamainder of the input data packet.
status = hw_ostc3_write (device, progress, input + 1, isize - 1);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to send the data packet.");
return status;
}
} else {
// Send the input data packet.
status = hw_ostc3_write (device, progress, input, isize);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to send the data packet.");
return status;
}
} }
} }
if (output) { if (output) {
// Read the output data packet. if (cmd == DIVE) {
status = hw_ostc3_read (device, progress, output, osize); // Read the dive header.
if (status != DC_STATUS_SUCCESS) { status = hw_ostc3_read (device, progress, output, RB_LOGBOOK_SIZE_FULL);
ERROR (abstract->context, "Failed to receive the answer."); if (status != DC_STATUS_SUCCESS) {
return status; ERROR (abstract->context, "Failed to receive the dive header.");
return status;
}
// When the hwOS firmware detects the dive profile is no longer
// valid, it 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. Detect this condition
// and adjust the expected length.
if (array_isequal (output + HDR_FULL_POINTERS, 6, 0x00) &&
array_uint24_le (output + HDR_FULL_LENGTH) == 8 &&
length > RB_LOGBOOK_SIZE_FULL + 5) {
length = RB_LOGBOOK_SIZE_FULL + 5;
}
// Read the dive profile.
status = hw_ostc3_read (device, progress, output + RB_LOGBOOK_SIZE_FULL, length - RB_LOGBOOK_SIZE_FULL);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to receive the dive profile.");
return status;
}
// Update and emit a progress event.
if (progress && osize > length) {
progress->current += osize - length;
device_event_emit ((dc_device_t *) device, DC_EVENT_PROGRESS, progress);
}
} else {
// Read the output data packet.
status = hw_ostc3_read (device, progress, output, length);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to receive the answer.");
return status;
}
} }
} }
if (delay && device->available == 0) { if (delay) {
dc_iostream_poll (device->iostream, delay); dc_iostream_poll (device->iostream, delay);
} }
if (cmd != EXIT) { if (cmd != EXIT) {
// Read the ready byte. // Read the ready byte.
unsigned char answer[1] = {0}; unsigned char answer[1] = {0};
status = hw_ostc3_read (device, NULL, answer, sizeof (answer)); status = dc_iostream_read (device->iostream, answer, sizeof (answer), NULL);
if (status != DC_STATUS_SUCCESS) { if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to receive the ready byte."); ERROR (abstract->context, "Failed to receive the ready byte.");
return status; return status;
@ -351,6 +403,9 @@ hw_ostc3_transfer (hw_ostc3_device_t *device,
} }
} }
if (actual)
*actual = length;
return DC_STATUS_SUCCESS; return DC_STATUS_SUCCESS;
} }
@ -360,6 +415,7 @@ hw_ostc3_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t *i
{ {
dc_status_t status = DC_STATUS_SUCCESS; dc_status_t status = DC_STATUS_SUCCESS;
hw_ostc3_device_t *device = NULL; hw_ostc3_device_t *device = NULL;
dc_transport_t transport = dc_iostream_get_transport (iostream);
if (out == NULL) if (out == NULL)
return DC_STATUS_INVALIDARGS; return DC_STATUS_INVALIDARGS;
@ -372,29 +428,36 @@ hw_ostc3_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t *i
} }
// Set the default values. // Set the default values.
device->iostream = iostream;
device->hardware = INVALID; device->hardware = INVALID;
device->feature = 0; device->feature = 0;
device->model = 0; device->model = 0;
device->serial = 0; device->serial = 0;
device->firmware = 0; device->firmware = 0;
memset (device->fingerprint, 0, sizeof (device->fingerprint)); memset (device->fingerprint, 0, sizeof (device->fingerprint));
memset (device->cache, 0, sizeof (device->cache));
device->available = 0; // Create the packet stream.
device->offset = 0; if (transport == DC_TRANSPORT_BLE) {
status = dc_packet_open (&device->iostream, context, iostream, 244, 20);
if (status != DC_STATUS_SUCCESS) {
ERROR (context, "Failed to create the packet stream.");
goto error_free;
}
} else {
device->iostream = iostream;
}
// Set the serial communication protocol (115200 8N1). // Set the serial communication protocol (115200 8N1).
status = dc_iostream_configure (device->iostream, 115200, 8, DC_PARITY_NONE, DC_STOPBITS_ONE, DC_FLOWCONTROL_NONE); status = dc_iostream_configure (device->iostream, 115200, 8, DC_PARITY_NONE, DC_STOPBITS_ONE, DC_FLOWCONTROL_NONE);
if (status != DC_STATUS_SUCCESS) { if (status != DC_STATUS_SUCCESS) {
ERROR (context, "Failed to set the terminal attributes."); ERROR (context, "Failed to set the terminal attributes.");
goto error_free; goto error_free_iostream;
} }
// Set the timeout for receiving data (3000ms). // Set the timeout for receiving data (3000ms).
status = dc_iostream_set_timeout (device->iostream, 3000); status = dc_iostream_set_timeout (device->iostream, 3000);
if (status != DC_STATUS_SUCCESS) { if (status != DC_STATUS_SUCCESS) {
ERROR (context, "Failed to set the timeout."); ERROR (context, "Failed to set the timeout.");
goto error_free; goto error_free_iostream;
} }
// Make sure everything is in a sane state. // Make sure everything is in a sane state.
@ -407,6 +470,10 @@ hw_ostc3_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t *i
return DC_STATUS_SUCCESS; return DC_STATUS_SUCCESS;
error_free_iostream:
if (transport == DC_TRANSPORT_BLE) {
dc_iostream_close (device->iostream);
}
error_free: error_free:
dc_device_deallocate ((dc_device_t *) device); dc_device_deallocate ((dc_device_t *) device);
return status; return status;
@ -423,9 +490,9 @@ hw_ostc3_device_id (hw_ostc3_device_t *device, unsigned char data[], unsigned in
// Send the command. // Send the command.
unsigned char hardware[SZ_HARDWARE2] = {0}; unsigned char hardware[SZ_HARDWARE2] = {0};
status = hw_ostc3_transfer (device, NULL, HARDWARE2, NULL, 0, hardware, SZ_HARDWARE2, NODELAY); status = hw_ostc3_transfer (device, NULL, HARDWARE2, NULL, 0, hardware, SZ_HARDWARE2, NULL, NODELAY);
if (status == DC_STATUS_UNSUPPORTED) { if (status == DC_STATUS_UNSUPPORTED) {
status = hw_ostc3_transfer (device, NULL, HARDWARE, NULL, 0, hardware + 1, SZ_HARDWARE, NODELAY); status = hw_ostc3_transfer (device, NULL, HARDWARE, NULL, 0, hardware + 1, SZ_HARDWARE, NULL, NODELAY);
} }
if (status != DC_STATUS_SUCCESS) if (status != DC_STATUS_SUCCESS)
return status; return status;
@ -447,7 +514,7 @@ hw_ostc3_device_init_download (hw_ostc3_device_t *device)
dc_context_t *context = (abstract ? abstract->context : NULL); dc_context_t *context = (abstract ? abstract->context : NULL);
// Send the init command. // Send the init command.
dc_status_t status = hw_ostc3_transfer (device, NULL, INIT, NULL, 0, NULL, 0, NODELAY); dc_status_t status = hw_ostc3_transfer (device, NULL, INIT, NULL, 0, NULL, 0, NULL, NODELAY);
if (status != DC_STATUS_SUCCESS) { if (status != DC_STATUS_SUCCESS) {
ERROR (context, "Failed to send the command."); ERROR (context, "Failed to send the command.");
return status; return status;
@ -469,14 +536,14 @@ hw_ostc3_device_init_service (hw_ostc3_device_t *device)
unsigned char answer[5] = {0}; unsigned char answer[5] = {0};
// Send the command and service key. // Send the command and service key.
status = hw_ostc3_write (device, NULL, command, sizeof (command)); status = dc_iostream_write (device->iostream, command, sizeof (command), NULL);
if (status != DC_STATUS_SUCCESS) { if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to send the command."); ERROR (abstract->context, "Failed to send the command.");
return status; return status;
} }
// Read the response. // Read the response.
status = hw_ostc3_read (device, NULL, answer, sizeof (answer)); status = dc_iostream_read (device->iostream, answer, sizeof (answer), NULL);
if (status != DC_STATUS_SUCCESS) { if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to receive the answer."); ERROR (abstract->context, "Failed to receive the answer.");
return status; return status;
@ -540,7 +607,7 @@ hw_ostc3_device_init (hw_ostc3_device_t *device, hw_ostc3_state_t state)
// Read the version information. // Read the version information.
unsigned char version[SZ_VERSION] = {0}; unsigned char version[SZ_VERSION] = {0};
rc = hw_ostc3_transfer (device, NULL, IDENTITY, NULL, 0, version, sizeof(version), NODELAY); rc = hw_ostc3_transfer (device, NULL, IDENTITY, NULL, 0, version, sizeof(version), NULL, NODELAY);
if (rc != DC_STATUS_SUCCESS) { if (rc != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to read the version information."); ERROR (abstract->context, "Failed to read the version information.");
return rc; return rc;
@ -570,13 +637,22 @@ hw_ostc3_device_close (dc_device_t *abstract)
// Send the exit command // Send the exit command
if (device->state == DOWNLOAD || device->state == SERVICE) { if (device->state == DOWNLOAD || device->state == SERVICE) {
rc = hw_ostc3_transfer (device, NULL, EXIT, NULL, 0, NULL, 0, NODELAY); rc = hw_ostc3_transfer (device, NULL, EXIT, NULL, 0, NULL, 0, NULL, NODELAY);
if (rc != DC_STATUS_SUCCESS) { if (rc != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to send the command."); ERROR (abstract->context, "Failed to send the command.");
dc_status_set_error(&status, rc); dc_status_set_error(&status, rc);
} }
} }
// Close the packet stream.
if (dc_iostream_get_transport (device->iostream) == DC_TRANSPORT_BLE) {
rc = dc_iostream_close (device->iostream);
if (rc != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to close the packet stream.");
dc_status_set_error(&status, rc);
}
}
return status; return status;
} }
@ -614,7 +690,7 @@ hw_ostc3_device_version (dc_device_t *abstract, unsigned char data[], unsigned i
return rc; return rc;
// Send the command. // Send the command.
rc = hw_ostc3_transfer (device, NULL, IDENTITY, NULL, 0, data, size, NODELAY); rc = hw_ostc3_transfer (device, NULL, IDENTITY, NULL, 0, data, size, NULL, NODELAY);
if (rc != DC_STATUS_SUCCESS) if (rc != DC_STATUS_SUCCESS)
return rc; return rc;
@ -687,11 +763,11 @@ hw_ostc3_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, voi
// This is slower, but also works for older firmware versions. // This is slower, but also works for older firmware versions.
unsigned int compact = 1; unsigned int compact = 1;
rc = hw_ostc3_transfer (device, &progress, COMPACT, rc = hw_ostc3_transfer (device, &progress, COMPACT,
NULL, 0, header, RB_LOGBOOK_SIZE_COMPACT * RB_LOGBOOK_COUNT, NODELAY); NULL, 0, header, RB_LOGBOOK_SIZE_COMPACT * RB_LOGBOOK_COUNT, NULL, NODELAY);
if (rc == DC_STATUS_UNSUPPORTED) { if (rc == DC_STATUS_UNSUPPORTED) {
compact = 0; compact = 0;
rc = hw_ostc3_transfer (device, &progress, HEADER, rc = hw_ostc3_transfer (device, &progress, HEADER,
NULL, 0, header, RB_LOGBOOK_SIZE_FULL * RB_LOGBOOK_COUNT, NODELAY); NULL, 0, header, RB_LOGBOOK_SIZE_FULL * RB_LOGBOOK_COUNT, NULL, NODELAY);
} }
if (rc != DC_STATUS_SUCCESS) { if (rc != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to read the header."); ERROR (abstract->context, "Failed to read the header.");
@ -747,8 +823,8 @@ hw_ostc3_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, voi
unsigned int length = RB_LOGBOOK_SIZE_FULL + array_uint24_le (header + offset + logbook->profile) - 3; unsigned int length = RB_LOGBOOK_SIZE_FULL + array_uint24_le (header + offset + logbook->profile) - 3;
if (!compact) { if (!compact) {
// Workaround for a bug in older firmware versions. // Workaround for a bug in older firmware versions.
unsigned int firmware = array_uint16_be (header + offset + 0x30); unsigned int firmware = array_uint16_be (header + offset + HDR_FULL_FIRMWARE);
if (firmware < 93) if (firmware < OSTC3FW(0,93))
length -= 3; length -= 3;
} }
if (length < RB_LOGBOOK_SIZE_FULL) { if (length < RB_LOGBOOK_SIZE_FULL) {
@ -795,15 +871,15 @@ hw_ostc3_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, voi
unsigned int length = RB_LOGBOOK_SIZE_FULL + array_uint24_le (header + offset + logbook->profile) - 3; unsigned int length = RB_LOGBOOK_SIZE_FULL + array_uint24_le (header + offset + logbook->profile) - 3;
if (!compact) { if (!compact) {
// Workaround for a bug in older firmware versions. // Workaround for a bug in older firmware versions.
unsigned int firmware = array_uint16_be (header + offset + 0x30); unsigned int firmware = array_uint16_be (header + offset + HDR_FULL_FIRMWARE);
if (firmware < 93) if (firmware < OSTC3FW(0,93))
length -= 3; length -= 3;
} }
// Download the dive. // Download the dive.
unsigned char number[1] = {idx}; unsigned char number[1] = {idx};
rc = hw_ostc3_transfer (device, &progress, DIVE, rc = hw_ostc3_transfer (device, &progress, DIVE,
number, sizeof (number), profile, length, NODELAY); number, sizeof (number), profile, length, &length, NODELAY);
if (rc != DC_STATUS_SUCCESS) { if (rc != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to read the dive."); ERROR (abstract->context, "Failed to read the dive.");
free (profile); free (profile);
@ -812,11 +888,15 @@ hw_ostc3_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, voi
} }
// Verify the header in the logbook and profile are identical. // Verify the header in the logbook and profile are identical.
if (!compact && memcmp (profile, header + offset, logbook->size) != 0) { if (memcmp (profile + HDR_FULL_VERSION, header + offset + logbook->version, 1) != 0 ||
compact ?
memcmp (profile + HDR_FULL_SUMMARY, header + offset + HDR_COMPACT_SUMMARY, 10) != 0 ||
memcmp (profile + HDR_FULL_NUMBER, header + offset + HDR_COMPACT_NUMBER, 2) != 0 :
memcmp (profile + HDR_FULL_SUMMARY, header + offset + HDR_FULL_SUMMARY, RB_LOGBOOK_SIZE_FULL - HDR_FULL_SUMMARY) != 0) {
ERROR (abstract->context, "Unexpected profile header."); ERROR (abstract->context, "Unexpected profile header.");
free (profile); free (profile);
free (header); free (header);
return rc; return DC_STATUS_DATAFORMAT;
} }
// Detect invalid profile data. // Detect invalid profile data.
@ -830,8 +910,8 @@ hw_ostc3_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, voi
} else if (length == RB_LOGBOOK_SIZE_FULL + 2) { } else if (length == RB_LOGBOOK_SIZE_FULL + 2) {
// A profile containing only the 2 byte end-of-profile // A profile containing only the 2 byte end-of-profile
// marker is considered a valid empty profile. // marker is considered a valid empty profile.
} else if (length < RB_LOGBOOK_SIZE_FULL + 5 + 2 || } else if (length < RB_LOGBOOK_SIZE_FULL + 5 ||
array_uint24_le (profile + RB_LOGBOOK_SIZE_FULL) + delta != array_uint24_le (profile + 9)) { array_uint24_le (profile + RB_LOGBOOK_SIZE_FULL) + delta != array_uint24_le (profile + HDR_FULL_LENGTH)) {
// If there is more data available, then there should be a // If there is more data available, then there should be a
// valid profile header containing a length matching the // valid profile header containing a length matching the
// length in the dive header. // length in the dive header.
@ -839,7 +919,7 @@ hw_ostc3_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, voi
length = RB_LOGBOOK_SIZE_FULL; length = RB_LOGBOOK_SIZE_FULL;
} }
if (callback && !callback (profile, length, profile + 12, sizeof (device->fingerprint), userdata)) if (callback && !callback (profile, length, profile + HDR_FULL_SUMMARY, sizeof (device->fingerprint), userdata))
break; break;
} }
@ -855,11 +935,6 @@ hw_ostc3_device_timesync (dc_device_t *abstract, const dc_datetime_t *datetime)
{ {
hw_ostc3_device_t *device = (hw_ostc3_device_t *) abstract; hw_ostc3_device_t *device = (hw_ostc3_device_t *) abstract;
if (datetime == NULL) {
ERROR (abstract->context, "Invalid parameter specified.");
return DC_STATUS_INVALIDARGS;
}
dc_status_t rc = hw_ostc3_device_init (device, DOWNLOAD); dc_status_t rc = hw_ostc3_device_init (device, DOWNLOAD);
if (rc != DC_STATUS_SUCCESS) if (rc != DC_STATUS_SUCCESS)
return rc; return rc;
@ -868,7 +943,7 @@ hw_ostc3_device_timesync (dc_device_t *abstract, const dc_datetime_t *datetime)
unsigned char packet[6] = { unsigned char packet[6] = {
datetime->hour, datetime->minute, datetime->second, datetime->hour, datetime->minute, datetime->second,
datetime->month, datetime->day, datetime->year - 2000}; datetime->month, datetime->day, datetime->year - 2000};
rc = hw_ostc3_transfer (device, NULL, CLOCK, packet, sizeof (packet), NULL, 0, NODELAY); rc = hw_ostc3_transfer (device, NULL, CLOCK, packet, sizeof (packet), NULL, 0, NULL, NODELAY);
if (rc != DC_STATUS_SUCCESS) if (rc != DC_STATUS_SUCCESS)
return rc; return rc;
@ -896,7 +971,7 @@ hw_ostc3_device_display (dc_device_t *abstract, const char *text)
return rc; return rc;
// Send the command. // Send the command.
rc = hw_ostc3_transfer (device, NULL, DISPLAY, packet, sizeof (packet), NULL, 0, NODELAY); rc = hw_ostc3_transfer (device, NULL, DISPLAY, packet, sizeof (packet), NULL, 0, NULL, NODELAY);
if (rc != DC_STATUS_SUCCESS) if (rc != DC_STATUS_SUCCESS)
return rc; return rc;
@ -924,7 +999,7 @@ hw_ostc3_device_customtext (dc_device_t *abstract, const char *text)
return rc; return rc;
// Send the command. // Send the command.
rc = hw_ostc3_transfer (device, NULL, CUSTOMTEXT, packet, sizeof (packet), NULL, 0, NODELAY); rc = hw_ostc3_transfer (device, NULL, CUSTOMTEXT, packet, sizeof (packet), NULL, 0, NULL, NODELAY);
if (rc != DC_STATUS_SUCCESS) if (rc != DC_STATUS_SUCCESS)
return rc; return rc;
@ -950,7 +1025,7 @@ hw_ostc3_device_config_read (dc_device_t *abstract, unsigned int config, unsigne
// Send the command. // Send the command.
unsigned char command[1] = {config}; unsigned char command[1] = {config};
rc = hw_ostc3_transfer (device, NULL, READ, command, sizeof (command), data, size, NODELAY); rc = hw_ostc3_transfer (device, NULL, READ, command, sizeof (command), data, size, NULL, NODELAY);
if (rc != DC_STATUS_SUCCESS) if (rc != DC_STATUS_SUCCESS)
return rc; return rc;
@ -977,7 +1052,7 @@ hw_ostc3_device_config_write (dc_device_t *abstract, unsigned int config, const
// Send the command. // Send the command.
unsigned char command[SZ_CONFIG + 1] = {config}; unsigned char command[SZ_CONFIG + 1] = {config};
memcpy(command + 1, data, size); memcpy(command + 1, data, size);
rc = hw_ostc3_transfer (device, NULL, WRITE, command, size + 1, NULL, 0, NODELAY); rc = hw_ostc3_transfer (device, NULL, WRITE, command, size + 1, NULL, 0, NULL, NODELAY);
if (rc != DC_STATUS_SUCCESS) if (rc != DC_STATUS_SUCCESS)
return rc; return rc;
@ -997,7 +1072,7 @@ hw_ostc3_device_config_reset (dc_device_t *abstract)
return rc; return rc;
// Send the command. // Send the command.
rc = hw_ostc3_transfer (device, NULL, RESET, NULL, 0, NULL, 0, NODELAY); rc = hw_ostc3_transfer (device, NULL, RESET, NULL, 0, NULL, 0, NULL, NODELAY);
if (rc != DC_STATUS_SUCCESS) if (rc != DC_STATUS_SUCCESS)
return rc; return rc;
@ -1223,7 +1298,7 @@ hw_ostc3_firmware_erase (hw_ostc3_device_t *device, unsigned int addr, unsigned
array_uint24_be_set (buffer, addr); array_uint24_be_set (buffer, addr);
buffer[3] = blocks; buffer[3] = blocks;
return hw_ostc3_transfer (device, NULL, S_ERASE, buffer, sizeof (buffer), NULL, 0, delay); return hw_ostc3_transfer (device, NULL, S_ERASE, buffer, sizeof (buffer), NULL, 0, NULL, delay);
} }
static dc_status_t static dc_status_t
@ -1233,7 +1308,7 @@ hw_ostc3_firmware_block_read (hw_ostc3_device_t *device, unsigned int addr, unsi
array_uint24_be_set (buffer, addr); array_uint24_be_set (buffer, addr);
array_uint24_be_set (buffer + 3, block_size); array_uint24_be_set (buffer + 3, block_size);
return hw_ostc3_transfer (device, NULL, S_BLOCK_READ, buffer, sizeof (buffer), block, block_size, NODELAY); return hw_ostc3_transfer (device, NULL, S_BLOCK_READ, buffer, sizeof (buffer), block, block_size, NULL, NODELAY);
} }
static dc_status_t static dc_status_t
@ -1248,7 +1323,7 @@ hw_ostc3_firmware_block_write1 (hw_ostc3_device_t *device, unsigned int addr, co
array_uint24_be_set (buffer, addr); array_uint24_be_set (buffer, addr);
memcpy (buffer + 3, block, block_size); memcpy (buffer + 3, block, block_size);
return hw_ostc3_transfer (device, NULL, S_BLOCK_WRITE, buffer, 3 + block_size, NULL, 0, TIMEOUT); return hw_ostc3_transfer (device, NULL, S_BLOCK_WRITE, buffer, 3 + block_size, NULL, 0, NULL, TIMEOUT);
} }
static dc_status_t static dc_status_t
@ -1267,7 +1342,7 @@ hw_ostc3_firmware_block_write2 (hw_ostc3_device_t *device, unsigned int address,
array_uint24_be_set (buffer, address); array_uint24_be_set (buffer, address);
memcpy (buffer + 3, data + nbytes, SZ_FIRMWARE_BLOCK2); memcpy (buffer + 3, data + nbytes, SZ_FIRMWARE_BLOCK2);
status = hw_ostc3_transfer (device, NULL, S_BLOCK_WRITE2, buffer, sizeof(buffer), NULL, 0, NODELAY); status = hw_ostc3_transfer (device, NULL, S_BLOCK_WRITE2, buffer, sizeof(buffer), NULL, 0, NULL, NODELAY);
if (status != DC_STATUS_SUCCESS) { if (status != DC_STATUS_SUCCESS) {
return status; return status;
} }
@ -1282,10 +1357,13 @@ hw_ostc3_firmware_block_write2 (hw_ostc3_device_t *device, unsigned int address,
static dc_status_t static dc_status_t
hw_ostc3_firmware_block_write (hw_ostc3_device_t *device, unsigned int address, const unsigned char data[], unsigned int size) hw_ostc3_firmware_block_write (hw_ostc3_device_t *device, unsigned int address, const unsigned char data[], unsigned int size)
{ {
if (device->firmware >= 0x0309) { // Support for the S_BLOCK_WRITE2 command is only available since the
return hw_ostc3_firmware_block_write2 (device, address, data, size); // hwOS Tech firmware v3.09 and the hwOS Sport firmware v10.64.
} else { if ((device->firmware < OSTC3FW(3,9)) ||
(device->firmware >= OSTC3FW(10,0) && device->firmware < OSTC3FW(10,64))) {
return hw_ostc3_firmware_block_write1 (device, address, data, size); return hw_ostc3_firmware_block_write1 (device, address, data, size);
} else {
return hw_ostc3_firmware_block_write2 (device, address, data, size);
} }
} }
@ -1305,7 +1383,7 @@ hw_ostc3_firmware_upgrade (dc_device_t *abstract, unsigned int checksum)
buffer[4] = (buffer[4]<<1 | buffer[4]>>7); buffer[4] = (buffer[4]<<1 | buffer[4]>>7);
} }
rc = hw_ostc3_transfer (device, NULL, S_UPGRADE, buffer, sizeof (buffer), NULL, 0, NODELAY); rc = hw_ostc3_transfer (device, NULL, S_UPGRADE, buffer, sizeof (buffer), NULL, 0, NULL, NODELAY);
if (rc != DC_STATUS_SUCCESS) { if (rc != DC_STATUS_SUCCESS) {
ERROR (context, "Failed to send flash firmware command"); ERROR (context, "Failed to send flash firmware command");
return rc; return rc;
@ -1366,7 +1444,7 @@ hw_ostc3_device_fwupdate3 (dc_device_t *abstract, const char *filename)
for (unsigned int len = 0; len < SZ_FIRMWARE; len += SZ_FIRMWARE_BLOCK) { for (unsigned int len = 0; len < SZ_FIRMWARE; len += SZ_FIRMWARE_BLOCK) {
char status[SZ_DISPLAY + 1]; // Status message on the display char status[SZ_DISPLAY + 1]; // Status message on the display
snprintf (status, sizeof(status), " Uploading %2d%%", (100 * len) / SZ_FIRMWARE); dc_platform_snprintf (status, sizeof(status), " Uploading %2d%%", (100 * len) / SZ_FIRMWARE);
hw_ostc3_device_display (abstract, status); hw_ostc3_device_display (abstract, status);
rc = hw_ostc3_firmware_block_write (device, FIRMWARE_AREA + len, firmware->data + len, SZ_FIRMWARE_BLOCK); rc = hw_ostc3_firmware_block_write (device, FIRMWARE_AREA + len, firmware->data + len, SZ_FIRMWARE_BLOCK);
@ -1385,7 +1463,7 @@ hw_ostc3_device_fwupdate3 (dc_device_t *abstract, const char *filename)
for (unsigned int len = 0; len < SZ_FIRMWARE; len += SZ_FIRMWARE_BLOCK) { for (unsigned int len = 0; len < SZ_FIRMWARE; len += SZ_FIRMWARE_BLOCK) {
unsigned char block[SZ_FIRMWARE_BLOCK]; unsigned char block[SZ_FIRMWARE_BLOCK];
char status[SZ_DISPLAY + 1]; // Status message on the display char status[SZ_DISPLAY + 1]; // Status message on the display
snprintf (status, sizeof(status), " Verifying %2d%%", (100 * len) / SZ_FIRMWARE); dc_platform_snprintf (status, sizeof(status), " Verifying %2d%%", (100 * len) / SZ_FIRMWARE);
hw_ostc3_device_display (abstract, status); hw_ostc3_device_display (abstract, status);
rc = hw_ostc3_firmware_block_read (device, FIRMWARE_AREA + len, block, sizeof (block)); rc = hw_ostc3_firmware_block_read (device, FIRMWARE_AREA + len, block, sizeof (block));
@ -1487,7 +1565,7 @@ hw_ostc3_device_fwupdate4 (dc_device_t *abstract, const char *filename)
// Read the firmware version info. // Read the firmware version info.
unsigned char fwinfo[SZ_FWINFO] = {0}; unsigned char fwinfo[SZ_FWINFO] = {0};
status = hw_ostc3_transfer (device, NULL, S_FWINFO, status = hw_ostc3_transfer (device, NULL, S_FWINFO,
data + offset + 4, 1, fwinfo, sizeof(fwinfo), NODELAY); data + offset + 4, 1, fwinfo, sizeof(fwinfo), NULL, NODELAY);
if (status != DC_STATUS_SUCCESS) { if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to read the firmware info."); ERROR (abstract->context, "Failed to read the firmware info.");
goto error; goto error;
@ -1500,7 +1578,7 @@ hw_ostc3_device_fwupdate4 (dc_device_t *abstract, const char *filename)
!array_isequal(fwinfo, sizeof(fwinfo), 0xFF)) !array_isequal(fwinfo, sizeof(fwinfo), 0xFF))
{ {
status = hw_ostc3_transfer (device, &progress, S_UPLOAD, status = hw_ostc3_transfer (device, &progress, S_UPLOAD,
data + offset, length, NULL, 0, usecs / 1000); data + offset, length, NULL, 0, NULL, usecs / 1000);
if (status != DC_STATUS_SUCCESS) { if (status != DC_STATUS_SUCCESS) {
goto error; goto error;
} }
@ -1637,6 +1715,25 @@ hw_ostc3_device_dump (dc_device_t *abstract, dc_buffer_t *buffer)
return rc; return rc;
} }
if (device->hardware == OSTC4) {
return DC_STATUS_UNSUPPORTED;
}
// Emit a device info event.
dc_event_devinfo_t devinfo;
devinfo.firmware = device->firmware;
devinfo.serial = device->serial;
if (device->hardware != UNKNOWN) {
devinfo.model = device->hardware;
} else {
// Fallback to the serial number.
if (devinfo.serial > 10000)
devinfo.model = SPORT;
else
devinfo.model = OSTC3;
}
device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo);
// Allocate the required amount of memory. // Allocate the required amount of memory.
if (!dc_buffer_resize (buffer, SZ_MEMORY)) { if (!dc_buffer_resize (buffer, SZ_MEMORY)) {
ERROR (abstract->context, "Insufficient buffer space available."); ERROR (abstract->context, "Insufficient buffer space available.");

View File

@ -36,7 +36,7 @@ dc_status_t
hw_ostc3_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream); hw_ostc3_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream);
dc_status_t dc_status_t
hw_ostc3_parser_create (dc_parser_t **out, dc_context_t *context, unsigned int model); hw_ostc3_parser_create (dc_parser_t **out, dc_context_t *context, const unsigned char data[], size_t size, unsigned int model);
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@ -20,6 +20,7 @@
*/ */
#include <stdlib.h> #include <stdlib.h>
#include <string.h>
#include "libdivecomputer/units.h" #include "libdivecomputer/units.h"
@ -36,10 +37,6 @@
#define UNDEFINED 0xFFFFFFFF #define UNDEFINED 0xFFFFFFFF
#define ALL 0
#define FIXED 1
#define MANUAL 2
#define HEADER 1 #define HEADER 1
#define PROFILE 2 #define PROFILE 2
@ -69,6 +66,10 @@
#define OSTC3_APNEA 3 #define OSTC3_APNEA 3
#define OSTC3_PSCR 4 #define OSTC3_PSCR 4
#define OSTC3_ZHL16 0
#define OSTC3_ZHL16_GF 1
#define OSTC4_VPM 2
#define OSTC4 0x3B #define OSTC4 0x3B
#define OSTC3FW(major,minor) ( \ #define OSTC3FW(major,minor) ( \
@ -90,18 +91,26 @@ typedef struct hw_ostc_sample_info_t {
typedef struct hw_ostc_layout_t { typedef struct hw_ostc_layout_t {
unsigned int datetime; unsigned int datetime;
unsigned int maxdepth; unsigned int maxdepth;
unsigned int avgdepth;
unsigned int divetime; unsigned int divetime;
unsigned int atmospheric;
unsigned int salinity;
unsigned int duration;
unsigned int temperature; unsigned int temperature;
unsigned int atmospheric;
unsigned int firmware; unsigned int firmware;
unsigned int salinity;
unsigned int avgdepth;
unsigned int duration;
unsigned int gf;
unsigned int decomodel;
unsigned int divemode;
} hw_ostc_layout_t; } hw_ostc_layout_t;
typedef struct hw_ostc_gasmix_t { typedef struct hw_ostc_gasmix_t {
unsigned int id;
unsigned int oxygen; unsigned int oxygen;
unsigned int helium; unsigned int helium;
unsigned int type;
unsigned int enabled;
unsigned int active;
unsigned int diluent;
} hw_ostc_gasmix_t; } hw_ostc_gasmix_t;
typedef struct hw_ostc_parser_t { typedef struct hw_ostc_parser_t {
@ -115,21 +124,25 @@ typedef struct hw_ostc_parser_t {
const hw_ostc_layout_t *layout; const hw_ostc_layout_t *layout;
unsigned int ngasmixes; unsigned int ngasmixes;
unsigned int nfixed; unsigned int nfixed;
unsigned int ndisabled;
unsigned int initial; unsigned int initial;
unsigned int initial_setpoint; unsigned int initial_setpoint;
unsigned int initial_cns; unsigned int initial_cns;
hw_ostc_gasmix_t gasmix[NGASMIXES]; hw_ostc_gasmix_t gasmix[NGASMIXES];
} hw_ostc_parser_t; } hw_ostc_parser_t;
static dc_status_t hw_ostc_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size);
static dc_status_t hw_ostc_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime); static dc_status_t hw_ostc_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime);
static dc_status_t hw_ostc_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value); static dc_status_t hw_ostc_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value);
static dc_status_t hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata); static dc_status_t hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata);
static dc_status_t hw_ostc_parser_internal_foreach (hw_ostc_parser_t *parser, dc_sample_callback_t callback, void *userdata);
static const dc_parser_vtable_t hw_ostc_parser_vtable = { static const dc_parser_vtable_t hw_ostc_parser_vtable = {
sizeof(hw_ostc_parser_t), sizeof(hw_ostc_parser_t),
DC_FAMILY_HW_OSTC, DC_FAMILY_HW_OSTC,
hw_ostc_parser_set_data, /* set_data */ NULL, /* set_clock */
NULL, /* set_atmospheric */
NULL, /* set_density */
hw_ostc_parser_get_datetime, /* datetime */ hw_ostc_parser_get_datetime, /* datetime */
hw_ostc_parser_get_field, /* fields */ hw_ostc_parser_get_field, /* fields */
hw_ostc_parser_samples_foreach, /* samples_foreach */ hw_ostc_parser_samples_foreach, /* samples_foreach */
@ -139,53 +152,57 @@ static const dc_parser_vtable_t hw_ostc_parser_vtable = {
static const hw_ostc_layout_t hw_ostc_layout_ostc = { static const hw_ostc_layout_t hw_ostc_layout_ostc = {
3, /* datetime */ 3, /* datetime */
8, /* maxdepth */ 8, /* maxdepth */
45, /* avgdepth */
10, /* divetime */ 10, /* divetime */
15, /* atmospheric */
43, /* salinity */
47, /* duration */
13, /* temperature */ 13, /* temperature */
15, /* atmospheric */
32, /* firmware */ 32, /* firmware */
43, /* salinity */
45, /* avgdepth */
47, /* duration */
49, /* gf */
UNDEFINED, /* decomodel */
51, /* divemode */
}; };
static const hw_ostc_layout_t hw_ostc_layout_frog = { static const hw_ostc_layout_t hw_ostc_layout_frog = {
9, /* datetime */ 9, /* datetime */
14, /* maxdepth */ 14, /* maxdepth */
45, /* avgdepth */
16, /* divetime */ 16, /* divetime */
21, /* atmospheric */
43, /* salinity */
47, /* duration */
19, /* temperature */ 19, /* temperature */
21, /* atmospheric */
32, /* firmware */ 32, /* firmware */
43, /* salinity */
45, /* avgdepth */
47, /* duration */
49, /* gf */
UNDEFINED, /* decomodel */
51, /* divemode */
}; };
static const hw_ostc_layout_t hw_ostc_layout_ostc3 = { static const hw_ostc_layout_t hw_ostc_layout_ostc3 = {
12, /* datetime */ 12, /* datetime */
17, /* maxdepth */ 17, /* maxdepth */
73, /* avgdepth */
19, /* divetime */ 19, /* divetime */
24, /* atmospheric */
70, /* salinity */
75, /* duration */
22, /* temperature */ 22, /* temperature */
24, /* atmospheric */
48, /* firmware */ 48, /* firmware */
70, /* salinity */
73, /* avgdepth */
75, /* duration */
77, /* gf */
79, /* decomodel */
82, /* divemode */
}; };
static unsigned int static unsigned int
hw_ostc_find_gasmix (hw_ostc_parser_t *parser, unsigned int o2, unsigned int he, unsigned int type) hw_ostc_find_gasmix_manual (hw_ostc_parser_t *parser, unsigned int o2, unsigned int he, unsigned int dil)
{ {
unsigned int offset = 0; unsigned int offset = parser->nfixed - parser->ndisabled;
unsigned int count = parser->ngasmixes; unsigned int count = parser->ngasmixes;
if (type == FIXED) {
count = parser->nfixed;
} else if (type == MANUAL) {
offset = parser->nfixed;
}
unsigned int i = offset; unsigned int i = offset;
while (i < count) { while (i < count) {
if (o2 == parser->gasmix[i].oxygen && he == parser->gasmix[i].helium) if (o2 == parser->gasmix[i].oxygen && he == parser->gasmix[i].helium && dil == parser->gasmix[i].diluent)
break; break;
i++; i++;
} }
@ -193,6 +210,34 @@ hw_ostc_find_gasmix (hw_ostc_parser_t *parser, unsigned int o2, unsigned int he,
return i; return i;
} }
static unsigned int
hw_ostc_find_gasmix_fixed (hw_ostc_parser_t *parser, unsigned int id)
{
unsigned int offset = 0;
unsigned int count = parser->nfixed - parser->ndisabled;
unsigned int i = offset;
while (i < count) {
if (id == parser->gasmix[i].id)
break;
i++;
}
return i;
}
static unsigned int
hw_ostc_is_ccr (unsigned int divemode, unsigned int version)
{
if (version == 0x21) {
return divemode == OSTC_ZHL16_CC || divemode == OSTC_ZHL16_CC_GF || divemode == OSTC_PSCR_GF;
} else if (version == 0x23 || version == 0x24) {
return divemode == OSTC3_CC || divemode == OSTC3_PSCR;
} else {
return 0;
}
}
static dc_status_t static dc_status_t
hw_ostc_parser_cache (hw_ostc_parser_t *parser) hw_ostc_parser_cache (hw_ostc_parser_t *parser)
{ {
@ -241,6 +286,13 @@ hw_ostc_parser_cache (hw_ostc_parser_t *parser)
return DC_STATUS_DATAFORMAT; return DC_STATUS_DATAFORMAT;
} }
// Get the dive mode.
unsigned int divemode = layout->divemode < header ?
data[layout->divemode] : UNDEFINED;
// Get the CCR mode.
unsigned int ccr = hw_ostc_is_ccr (divemode, version);
// Get all the gas mixes, the index of the inital mix, // Get all the gas mixes, the index of the inital mix,
// the initial setpoint (used in the fixed setpoint CCR mode), // the initial setpoint (used in the fixed setpoint CCR mode),
// and the initial CNS from the header // and the initial CNS from the header
@ -255,21 +307,31 @@ hw_ostc_parser_cache (hw_ostc_parser_t *parser)
initial = data[31]; initial = data[31];
} }
for (unsigned int i = 0; i < ngasmixes; ++i) { for (unsigned int i = 0; i < ngasmixes; ++i) {
gasmix[i].id = i + 1;
gasmix[i].oxygen = data[25 + 2 * i]; gasmix[i].oxygen = data[25 + 2 * i];
gasmix[i].helium = 0; gasmix[i].helium = 0;
gasmix[i].type = 0;
gasmix[i].enabled = 1;
gasmix[i].active = 0;
gasmix[i].diluent = 0;
} }
} else if (version == 0x23 || version == 0x24) { } else if (version == 0x23 || version == 0x24) {
ngasmixes = 5; ngasmixes = 5;
for (unsigned int i = 0; i < ngasmixes; ++i) { for (unsigned int i = 0; i < ngasmixes; ++i) {
gasmix[i].id = i + 1;
gasmix[i].oxygen = data[28 + 4 * i + 0]; gasmix[i].oxygen = data[28 + 4 * i + 0];
gasmix[i].helium = data[28 + 4 * i + 1]; gasmix[i].helium = data[28 + 4 * i + 1];
gasmix[i].type = data[28 + 4 * i + 3];
gasmix[i].enabled = gasmix[i].type != 0;
gasmix[i].active = 0;
gasmix[i].diluent = ccr;
// Find the first gas marked as the initial gas. // Find the first gas marked as the initial gas.
if (initial == UNDEFINED && data[28 + 4 * i + 3] == 1) { if (initial == UNDEFINED && data[28 + 4 * i + 3] == 1) {
initial = i + 1; /* One based index! */ initial = i + 1; /* One based index! */
} }
} }
// The first fixed setpoint is the initial setpoint in CCR mode. // The first fixed setpoint is the initial setpoint in CCR mode.
if (data[82] == OSTC3_CC) { if (ccr) {
initial_setpoint = data[60]; initial_setpoint = data[60];
} }
// Initial CNS // Initial CNS
@ -280,8 +342,17 @@ hw_ostc_parser_cache (hw_ostc_parser_t *parser)
initial = data[31]; initial = data[31];
} }
for (unsigned int i = 0; i < ngasmixes; ++i) { for (unsigned int i = 0; i < ngasmixes; ++i) {
gasmix[i].id = i + 1;
gasmix[i].oxygen = data[19 + 2 * i + 0]; gasmix[i].oxygen = data[19 + 2 * i + 0];
gasmix[i].helium = data[19 + 2 * i + 1]; gasmix[i].helium = data[19 + 2 * i + 1];
gasmix[i].type = 0;
if (version == 0x21) {
gasmix[i].enabled = data[53] & (1 << i);
} else {
gasmix[i].enabled = 1;
}
gasmix[i].active = 0;
gasmix[i].diluent = ccr;
} }
} }
if (initial != UNDEFINED) { if (initial != UNDEFINED) {
@ -289,7 +360,6 @@ hw_ostc_parser_cache (hw_ostc_parser_t *parser)
ERROR(abstract->context, "Invalid initial gas mix."); ERROR(abstract->context, "Invalid initial gas mix.");
return DC_STATUS_DATAFORMAT; return DC_STATUS_DATAFORMAT;
} }
initial--; /* Convert to a zero based index. */
} else { } else {
WARNING(abstract->context, "No initial gas mix available."); WARNING(abstract->context, "No initial gas mix available.");
} }
@ -300,6 +370,7 @@ hw_ostc_parser_cache (hw_ostc_parser_t *parser)
parser->layout = layout; parser->layout = layout;
parser->ngasmixes = ngasmixes; parser->ngasmixes = ngasmixes;
parser->nfixed = ngasmixes; parser->nfixed = ngasmixes;
parser->ndisabled = 0;
parser->initial = initial; parser->initial = initial;
parser->initial_setpoint = initial_setpoint; parser->initial_setpoint = initial_setpoint;
parser->initial_cns = initial_cns; parser->initial_cns = initial_cns;
@ -312,7 +383,7 @@ hw_ostc_parser_cache (hw_ostc_parser_t *parser)
} }
static dc_status_t static dc_status_t
hw_ostc_parser_create_internal (dc_parser_t **out, dc_context_t *context, unsigned int hwos, unsigned int model) hw_ostc_parser_create_internal (dc_parser_t **out, dc_context_t *context, const unsigned char data[], size_t size, unsigned int hwos, unsigned int model)
{ {
hw_ostc_parser_t *parser = NULL; hw_ostc_parser_t *parser = NULL;
@ -320,7 +391,7 @@ hw_ostc_parser_create_internal (dc_parser_t **out, dc_context_t *context, unsign
return DC_STATUS_INVALIDARGS; return DC_STATUS_INVALIDARGS;
// Allocate memory. // Allocate memory.
parser = (hw_ostc_parser_t *) dc_parser_allocate (context, &hw_ostc_parser_vtable); parser = (hw_ostc_parser_t *) dc_parser_allocate (context, &hw_ostc_parser_vtable, data, size);
if (parser == NULL) { if (parser == NULL) {
ERROR (context, "Failed to allocate memory."); ERROR (context, "Failed to allocate memory.");
return DC_STATUS_NOMEMORY; return DC_STATUS_NOMEMORY;
@ -335,12 +406,18 @@ hw_ostc_parser_create_internal (dc_parser_t **out, dc_context_t *context, unsign
parser->layout = NULL; parser->layout = NULL;
parser->ngasmixes = 0; parser->ngasmixes = 0;
parser->nfixed = 0; parser->nfixed = 0;
parser->ndisabled = 0;
parser->initial = 0; parser->initial = 0;
parser->initial_setpoint = 0; parser->initial_setpoint = 0;
parser->initial_cns = 0; parser->initial_cns = 0;
for (unsigned int i = 0; i < NGASMIXES; ++i) { for (unsigned int i = 0; i < NGASMIXES; ++i) {
parser->gasmix[i].id = 0;
parser->gasmix[i].oxygen = 0; parser->gasmix[i].oxygen = 0;
parser->gasmix[i].helium = 0; parser->gasmix[i].helium = 0;
parser->gasmix[i].type = 0;
parser->gasmix[i].enabled = 0;
parser->gasmix[i].active = 0;
parser->gasmix[i].diluent = 0;
} }
*out = (dc_parser_t *) parser; *out = (dc_parser_t *) parser;
@ -350,41 +427,17 @@ hw_ostc_parser_create_internal (dc_parser_t **out, dc_context_t *context, unsign
dc_status_t dc_status_t
hw_ostc_parser_create (dc_parser_t **out, dc_context_t *context) hw_ostc_parser_create (dc_parser_t **out, dc_context_t *context, const unsigned char data[], size_t size)
{ {
return hw_ostc_parser_create_internal (out, context, 0, 0); return hw_ostc_parser_create_internal (out, context, data, size, 0, 0);
} }
dc_status_t dc_status_t
hw_ostc3_parser_create (dc_parser_t **out, dc_context_t *context, unsigned int model) hw_ostc3_parser_create (dc_parser_t **out, dc_context_t *context, const unsigned char data[], size_t size, unsigned int model)
{ {
return hw_ostc_parser_create_internal (out, context, 1, model); return hw_ostc_parser_create_internal (out, context, data, size, 1, model);
} }
static dc_status_t
hw_ostc_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size)
{
hw_ostc_parser_t *parser = (hw_ostc_parser_t *) abstract;
// Reset the cache.
parser->cached = 0;
parser->version = 0;
parser->header = 0;
parser->layout = NULL;
parser->ngasmixes = 0;
parser->nfixed = 0;
parser->initial = 0;
parser->initial_setpoint = 0;
parser->initial_cns = 0;
for (unsigned int i = 0; i < NGASMIXES; ++i) {
parser->gasmix[i].oxygen = 0;
parser->gasmix[i].helium = 0;
}
return DC_STATUS_SUCCESS;
}
static dc_status_t static dc_status_t
hw_ostc_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime) hw_ostc_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime)
{ {
@ -458,7 +511,7 @@ hw_ostc_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned
// Cache the profile data. // Cache the profile data.
if (parser->cached < PROFILE) { if (parser->cached < PROFILE) {
rc = hw_ostc_parser_samples_foreach (abstract, NULL, NULL); rc = hw_ostc_parser_internal_foreach (parser, NULL, NULL);
if (rc != DC_STATUS_SUCCESS) if (rc != DC_STATUS_SUCCESS)
return rc; return rc;
} }
@ -468,6 +521,8 @@ hw_ostc_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned
dc_gasmix_t *gasmix = (dc_gasmix_t *) value; dc_gasmix_t *gasmix = (dc_gasmix_t *) value;
dc_salinity_t *water = (dc_salinity_t *) value; dc_salinity_t *water = (dc_salinity_t *) value;
dc_decomodel_t *decomodel = (dc_decomodel_t *) value;
unsigned int salinity = data[layout->salinity]; unsigned int salinity = data[layout->salinity];
if (version == 0x23 || version == 0x24) if (version == 0x23 || version == 0x24)
salinity += 100; salinity += 100;
@ -489,6 +544,8 @@ hw_ostc_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned
*((unsigned int *) value) = parser->ngasmixes; *((unsigned int *) value) = parser->ngasmixes;
break; break;
case DC_FIELD_GASMIX: case DC_FIELD_GASMIX:
gasmix->usage = parser->gasmix[flags].diluent ?
DC_USAGE_DILUENT : DC_USAGE_NONE;
gasmix->oxygen = parser->gasmix[flags].oxygen / 100.0; gasmix->oxygen = parser->gasmix[flags].oxygen / 100.0;
gasmix->helium = parser->gasmix[flags].helium / 100.0; gasmix->helium = parser->gasmix[flags].helium / 100.0;
gasmix->nitrogen = 1.0 - gasmix->oxygen - gasmix->helium; gasmix->nitrogen = 1.0 - gasmix->oxygen - gasmix->helium;
@ -511,7 +568,7 @@ hw_ostc_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned
break; break;
case DC_FIELD_DIVEMODE: case DC_FIELD_DIVEMODE:
if (version == 0x21) { if (version == 0x21) {
switch (data[51]) { switch (data[layout->divemode]) {
case OSTC_APNEA: case OSTC_APNEA:
*((dc_divemode_t *) value) = DC_DIVEMODE_FREEDIVE; *((dc_divemode_t *) value) = DC_DIVEMODE_FREEDIVE;
break; break;
@ -533,7 +590,7 @@ hw_ostc_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned
return DC_STATUS_DATAFORMAT; return DC_STATUS_DATAFORMAT;
} }
} else if (version == 0x22) { } else if (version == 0x22) {
switch (data[51]) { switch (data[layout->divemode]) {
case FROG_ZHL16: case FROG_ZHL16:
case FROG_ZHL16_GF: case FROG_ZHL16_GF:
*((dc_divemode_t *) value) = DC_DIVEMODE_OC; *((dc_divemode_t *) value) = DC_DIVEMODE_OC;
@ -545,7 +602,7 @@ hw_ostc_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned
return DC_STATUS_DATAFORMAT; return DC_STATUS_DATAFORMAT;
} }
} else if (version == 0x23 || version == 0x24) { } else if (version == 0x23 || version == 0x24) {
switch (data[82]) { switch (data[layout->divemode]) {
case OSTC3_OC: case OSTC3_OC:
*((dc_divemode_t *) value) = DC_DIVEMODE_OC; *((dc_divemode_t *) value) = DC_DIVEMODE_OC;
break; break;
@ -568,6 +625,70 @@ hw_ostc_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned
return DC_STATUS_UNSUPPORTED; return DC_STATUS_UNSUPPORTED;
} }
break; break;
case DC_FIELD_DECOMODEL:
if (version == 0x21) {
switch (data[layout->divemode]) {
case OSTC_APNEA:
case OSTC_GAUGE:
decomodel->type = DC_DECOMODEL_NONE;
break;
case OSTC_ZHL16_OC:
case OSTC_ZHL16_CC:
decomodel->type = DC_DECOMODEL_BUHLMANN;
decomodel->params.gf.low = 100;
decomodel->params.gf.high = 100;
break;
case OSTC_ZHL16_OC_GF:
case OSTC_ZHL16_CC_GF:
case OSTC_PSCR_GF:
decomodel->type = DC_DECOMODEL_BUHLMANN;
decomodel->params.gf.low = data[layout->gf + 0];
decomodel->params.gf.high = data[layout->gf + 1];
break;
default:
return DC_STATUS_DATAFORMAT;
}
} else if (version == 0x22) {
switch (data[layout->divemode]) {
case FROG_ZHL16:
decomodel->type = DC_DECOMODEL_BUHLMANN;
decomodel->params.gf.low = 100;
decomodel->params.gf.high = 100;
break;
case FROG_ZHL16_GF:
decomodel->type = DC_DECOMODEL_BUHLMANN;
decomodel->params.gf.low = data[layout->gf + 0];
decomodel->params.gf.high = data[layout->gf + 1];
break;
case FROG_APNEA:
decomodel->type = DC_DECOMODEL_NONE;
break;
default:
return DC_STATUS_DATAFORMAT;
}
} else if (version == 0x23 || version == 0x24) {
switch (data[layout->decomodel]) {
case OSTC3_ZHL16:
decomodel->type = DC_DECOMODEL_BUHLMANN;
decomodel->params.gf.low = 100;
decomodel->params.gf.high = 100;
break;
case OSTC3_ZHL16_GF:
decomodel->type = DC_DECOMODEL_BUHLMANN;
decomodel->params.gf.low = data[layout->gf + 0];
decomodel->params.gf.high = data[layout->gf + 1];
break;
case OSTC4_VPM:
decomodel->type = DC_DECOMODEL_VPM;
break;
default:
return DC_STATUS_DATAFORMAT;
}
} else {
return DC_STATUS_UNSUPPORTED;
}
decomodel->conservatism = 0;
break;
default: default:
return DC_STATUS_UNSUPPORTED; return DC_STATUS_UNSUPPORTED;
} }
@ -578,24 +699,21 @@ hw_ostc_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned
static dc_status_t static dc_status_t
hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata) hw_ostc_parser_internal_foreach (hw_ostc_parser_t *parser, dc_sample_callback_t callback, void *userdata)
{ {
hw_ostc_parser_t *parser = (hw_ostc_parser_t *) abstract; dc_parser_t *abstract = (dc_parser_t *) parser;
const unsigned char *data = abstract->data; const unsigned char *data = abstract->data;
unsigned int size = abstract->size; unsigned int size = abstract->size;
// Cache the parser data.
dc_status_t rc = hw_ostc_parser_cache (parser);
if (rc != DC_STATUS_SUCCESS)
return rc;
unsigned int version = parser->version; unsigned int version = parser->version;
unsigned int header = parser->header; unsigned int header = parser->header;
const hw_ostc_layout_t *layout = parser->layout; const hw_ostc_layout_t *layout = parser->layout;
// Exit if no profile data available. // Exit if no profile data available.
if (size == header || (size == header + 2 && const unsigned char empty[] = {0x08, 0x00, 0x00, 0xFD, 0xFD};
data[header] == 0xFD && data[header + 1] == 0xFD)) { if (size == header ||
(size == header + 2 && memcmp(data + header, empty + 3, 2) == 0) ||
(size == header + 5 && memcmp(data + header, empty, 5) == 0)) {
parser->cached = PROFILE; parser->cached = PROFILE;
return DC_STATUS_SUCCESS; return DC_STATUS_SUCCESS;
} }
@ -683,9 +801,16 @@ hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t call
firmware = array_uint16_be (data + layout->firmware); firmware = array_uint16_be (data + layout->firmware);
} }
// Get the dive mode.
unsigned int divemode = layout->divemode < header ?
data[layout->divemode] : UNDEFINED;
// Get the CCR mode.
unsigned int ccr = hw_ostc_is_ccr (divemode, version);
unsigned int time = 0; unsigned int time = 0;
unsigned int nsamples = 0; unsigned int nsamples = 0;
unsigned int tank = parser->initial != UNDEFINED ? parser->initial : 0; unsigned int tank = parser->initial != UNDEFINED ? parser->initial - 1 : 0;
unsigned int offset = header; unsigned int offset = header;
if (version == 0x23 || version == 0x24) if (version == 0x23 || version == 0x24)
@ -697,31 +822,33 @@ hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t call
// Time (seconds). // Time (seconds).
time += samplerate; time += samplerate;
sample.time = time; sample.time = time * 1000;
if (callback) callback (DC_SAMPLE_TIME, sample, userdata); if (callback) callback (DC_SAMPLE_TIME, &sample, userdata);
// Initial gas mix. // Initial gas mix.
if (time == samplerate && parser->initial != UNDEFINED) { if (time == samplerate && parser->initial != UNDEFINED) {
sample.gasmix = parser->initial; unsigned int idx = hw_ostc_find_gasmix_fixed (parser, parser->initial);
if (callback) callback (DC_SAMPLE_GASMIX, sample, userdata); parser->gasmix[idx].active = 1;
sample.gasmix = idx;
if (callback) callback (DC_SAMPLE_GASMIX, &sample, userdata);
} }
// Initial setpoint (mbar). // Initial setpoint (mbar).
if (time == samplerate && parser->initial_setpoint != UNDEFINED) { if (time == samplerate && parser->initial_setpoint != UNDEFINED) {
sample.setpoint = parser->initial_setpoint / 100.0; sample.setpoint = parser->initial_setpoint / 100.0;
if (callback) callback (DC_SAMPLE_SETPOINT, sample, userdata); if (callback) callback (DC_SAMPLE_SETPOINT, &sample, userdata);
} }
// Initial CNS (%). // Initial CNS (%).
if (time == samplerate && parser->initial_cns != UNDEFINED) { if (time == samplerate && parser->initial_cns != UNDEFINED) {
sample.cns = parser->initial_cns / 100.0; sample.cns = parser->initial_cns / 100.0;
if (callback) callback (DC_SAMPLE_CNS, sample, userdata); if (callback) callback (DC_SAMPLE_CNS, &sample, userdata);
} }
// Depth (1/100 m). // Depth (1/100 m).
unsigned int depth = array_uint16_le (data + offset); unsigned int depth = array_uint16_le (data + offset);
sample.depth = depth / 100.0; sample.depth = depth / 100.0;
if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata); if (callback) callback (DC_SAMPLE_DEPTH, &sample, userdata);
offset += 2; offset += 2;
// Extended sample info. // Extended sample info.
@ -780,7 +907,7 @@ hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t call
break; break;
} }
if (sample.event.type && callback) if (sample.event.type && callback)
callback (DC_SAMPLE_EVENT, sample, userdata); callback (DC_SAMPLE_EVENT, &sample, userdata);
// Manual Gas Set & Change // Manual Gas Set & Change
if (events & 0x10) { if (events & 0x10) {
@ -790,19 +917,24 @@ hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t call
} }
unsigned int o2 = data[offset]; unsigned int o2 = data[offset];
unsigned int he = data[offset + 1]; unsigned int he = data[offset + 1];
unsigned int idx = hw_ostc_find_gasmix (parser, o2, he, MANUAL); unsigned int idx = hw_ostc_find_gasmix_manual (parser, o2, he, ccr);
if (idx >= parser->ngasmixes) { if (idx >= parser->ngasmixes) {
if (idx >= NGASMIXES) { if (idx >= NGASMIXES) {
ERROR (abstract->context, "Maximum number of gas mixes reached."); ERROR (abstract->context, "Maximum number of gas mixes reached.");
return DC_STATUS_NOMEMORY; return DC_STATUS_NOMEMORY;
} }
parser->gasmix[idx].id = 0;
parser->gasmix[idx].oxygen = o2; parser->gasmix[idx].oxygen = o2;
parser->gasmix[idx].helium = he; parser->gasmix[idx].helium = he;
parser->gasmix[idx].type = 0;
parser->gasmix[idx].enabled = 1;
parser->gasmix[idx].active = 1;
parser->gasmix[idx].diluent = ccr;
parser->ngasmixes = idx + 1; parser->ngasmixes = idx + 1;
} }
sample.gasmix = idx; sample.gasmix = idx;
if (callback) callback (DC_SAMPLE_GASMIX, sample, userdata); if (callback) callback (DC_SAMPLE_GASMIX, &sample, userdata);
offset += 2; offset += 2;
length -= 2; length -= 2;
} }
@ -813,15 +945,20 @@ hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t call
ERROR (abstract->context, "Buffer overflow detected!"); ERROR (abstract->context, "Buffer overflow detected!");
return DC_STATUS_DATAFORMAT; return DC_STATUS_DATAFORMAT;
} }
unsigned int idx = data[offset]; unsigned int id = data[offset];
if (idx < 1 || idx > parser->ngasmixes) { if (parser->model == OSTC4 && ccr && id > parser->nfixed) {
ERROR(abstract->context, "Invalid gas mix."); // Fix the OSTC4 diluent index.
id -= parser->nfixed;
}
if (id < 1 || id > parser->nfixed) {
ERROR(abstract->context, "Invalid gas mix (%u).", id);
return DC_STATUS_DATAFORMAT; return DC_STATUS_DATAFORMAT;
} }
idx--; /* Convert to a zero based index. */ unsigned int idx = hw_ostc_find_gasmix_fixed (parser, id);
parser->gasmix[idx].active = 1;
sample.gasmix = idx; sample.gasmix = idx;
if (callback) callback (DC_SAMPLE_GASMIX, sample, userdata); if (callback) callback (DC_SAMPLE_GASMIX, &sample, userdata);
tank = idx; tank = id - 1;
offset++; offset++;
length--; length--;
} }
@ -834,7 +971,7 @@ hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t call
return DC_STATUS_DATAFORMAT; return DC_STATUS_DATAFORMAT;
} }
sample.setpoint = data[offset] / 100.0; sample.setpoint = data[offset] / 100.0;
if (callback) callback (DC_SAMPLE_SETPOINT, sample, userdata); if (callback) callback (DC_SAMPLE_SETPOINT, &sample, userdata);
offset++; offset++;
length--; length--;
} }
@ -848,19 +985,24 @@ hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t call
unsigned int o2 = data[offset]; unsigned int o2 = data[offset];
unsigned int he = data[offset + 1]; unsigned int he = data[offset + 1];
unsigned int idx = hw_ostc_find_gasmix (parser, o2, he, MANUAL); unsigned int idx = hw_ostc_find_gasmix_manual (parser, o2, he, 0);
if (idx >= parser->ngasmixes) { if (idx >= parser->ngasmixes) {
if (idx >= NGASMIXES) { if (idx >= NGASMIXES) {
ERROR (abstract->context, "Maximum number of gas mixes reached."); ERROR (abstract->context, "Maximum number of gas mixes reached.");
return DC_STATUS_NOMEMORY; return DC_STATUS_NOMEMORY;
} }
parser->gasmix[idx].id = 0;
parser->gasmix[idx].oxygen = o2; parser->gasmix[idx].oxygen = o2;
parser->gasmix[idx].helium = he; parser->gasmix[idx].helium = he;
parser->gasmix[idx].type = 0;
parser->gasmix[idx].enabled = 1;
parser->gasmix[idx].active = 1;
parser->gasmix[idx].diluent = 0;
parser->ngasmixes = idx + 1; parser->ngasmixes = idx + 1;
} }
sample.gasmix = idx; sample.gasmix = idx;
if (callback) callback (DC_SAMPLE_GASMIX, sample, userdata); if (callback) callback (DC_SAMPLE_GASMIX, &sample, userdata);
offset += 2; offset += 2;
length -= 2; length -= 2;
} }
@ -892,7 +1034,7 @@ hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t call
case TEMPERATURE: case TEMPERATURE:
value = array_uint16_le (data + offset); value = array_uint16_le (data + offset);
sample.temperature = value / 10.0; sample.temperature = value / 10.0;
if (callback) callback (DC_SAMPLE_TEMPERATURE, sample, userdata); if (callback) callback (DC_SAMPLE_TEMPERATURE, &sample, userdata);
break; break;
case DECO: case DECO:
// Due to a firmware bug, the deco/ndl info is incorrect for // Due to a firmware bug, the deco/ndl info is incorrect for
@ -907,7 +1049,8 @@ hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t call
sample.deco.depth = 0.0; sample.deco.depth = 0.0;
} }
sample.deco.time = data[offset + 1] * 60; sample.deco.time = data[offset + 1] * 60;
if (callback) callback (DC_SAMPLE_DECO, sample, userdata); sample.deco.tts = 0;
if (callback) callback (DC_SAMPLE_DECO, &sample, userdata);
break; break;
case PPO2: case PPO2:
for (unsigned int j = 0; j < 3; ++j) { for (unsigned int j = 0; j < 3; ++j) {
@ -921,8 +1064,9 @@ hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t call
} }
if (count) { if (count) {
for (unsigned int j = 0; j < 3; ++j) { for (unsigned int j = 0; j < 3; ++j) {
sample.ppo2 = ppo2[j] / 100.0; sample.ppo2.sensor = i;
if (callback) callback (DC_SAMPLE_PPO2, sample, userdata); sample.ppo2.value = ppo2[j] / 100.0;
if (callback) callback (DC_SAMPLE_PPO2, &sample, userdata);
} }
} }
break; break;
@ -931,7 +1075,7 @@ hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t call
sample.cns = array_uint16_le (data + offset) / 100.0; sample.cns = array_uint16_le (data + offset) / 100.0;
else else
sample.cns = data[offset] / 100.0; sample.cns = data[offset] / 100.0;
if (callback) callback (DC_SAMPLE_CNS, sample, userdata); if (callback) callback (DC_SAMPLE_CNS, &sample, userdata);
break; break;
case TANK: case TANK:
value = array_uint16_le (data + offset); value = array_uint16_le (data + offset);
@ -944,7 +1088,7 @@ hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t call
(firmware >= OSTC3FW(10,40) && firmware <= OSTC3FW(10,50))) { (firmware >= OSTC3FW(10,40) && firmware <= OSTC3FW(10,50))) {
sample.pressure.value /= 10.0; sample.pressure.value /= 10.0;
} }
if (callback) callback (DC_SAMPLE_PRESSURE, sample, userdata); if (callback) callback (DC_SAMPLE_PRESSURE, &sample, userdata);
} }
break; break;
default: // Not yet used. default: // Not yet used.
@ -964,7 +1108,7 @@ hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t call
return DC_STATUS_DATAFORMAT; return DC_STATUS_DATAFORMAT;
} }
sample.setpoint = data[offset] / 100.0; sample.setpoint = data[offset] / 100.0;
if (callback) callback (DC_SAMPLE_SETPOINT, sample, userdata); if (callback) callback (DC_SAMPLE_SETPOINT, &sample, userdata);
offset++; offset++;
length--; length--;
} }
@ -978,19 +1122,24 @@ hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t call
unsigned int o2 = data[offset]; unsigned int o2 = data[offset];
unsigned int he = data[offset + 1]; unsigned int he = data[offset + 1];
unsigned int idx = hw_ostc_find_gasmix (parser, o2, he, MANUAL); unsigned int idx = hw_ostc_find_gasmix_manual (parser, o2, he, 0);
if (idx >= parser->ngasmixes) { if (idx >= parser->ngasmixes) {
if (idx >= NGASMIXES) { if (idx >= NGASMIXES) {
ERROR (abstract->context, "Maximum number of gas mixes reached."); ERROR (abstract->context, "Maximum number of gas mixes reached.");
return DC_STATUS_NOMEMORY; return DC_STATUS_NOMEMORY;
} }
parser->gasmix[idx].id = 0;
parser->gasmix[idx].oxygen = o2; parser->gasmix[idx].oxygen = o2;
parser->gasmix[idx].helium = he; parser->gasmix[idx].helium = he;
parser->gasmix[idx].type = 0;
parser->gasmix[idx].enabled = 1;
parser->gasmix[idx].active = 1;
parser->gasmix[idx].diluent = 0;
parser->ngasmixes = idx + 1; parser->ngasmixes = idx + 1;
} }
sample.gasmix = idx; sample.gasmix = idx;
if (callback) callback (DC_SAMPLE_GASMIX, sample, userdata); if (callback) callback (DC_SAMPLE_GASMIX, &sample, userdata);
offset += 2; offset += 2;
length -= 2; length -= 2;
} }
@ -1008,7 +1157,50 @@ hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t call
return DC_STATUS_DATAFORMAT; return DC_STATUS_DATAFORMAT;
} }
// Remove the disabled gas mixes from the fixed gas mixes.
unsigned int ndisabled = 0, nenabled = 0;
unsigned int count = parser->nfixed - parser->ndisabled;
for (unsigned int i = 0; i < count; ++i) {
if (parser->gasmix[i].enabled || parser->gasmix[i].active) {
// Move the fixed gas mix.
parser->gasmix[nenabled] = parser->gasmix[i];
nenabled++;
} else {
ndisabled++;
}
}
// Move all the manual gas mixes.
memmove (parser->gasmix + nenabled, parser->gasmix + count,
(parser->ngasmixes - count) * sizeof (hw_ostc_gasmix_t));
memset (parser->gasmix + parser->ngasmixes - ndisabled, 0,
ndisabled * sizeof (hw_ostc_gasmix_t));
// Adjust the counts.
parser->ngasmixes -= ndisabled;
parser->ndisabled += ndisabled;
parser->cached = PROFILE; parser->cached = PROFILE;
return DC_STATUS_SUCCESS; return DC_STATUS_SUCCESS;
} }
static dc_status_t
hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata)
{
hw_ostc_parser_t *parser = (hw_ostc_parser_t *) abstract;
// Cache the header data.
dc_status_t rc = hw_ostc_parser_cache (parser);
if (rc != DC_STATUS_SUCCESS)
return rc;
// Cache the profile data.
if (parser->cached < PROFILE) {
rc = hw_ostc_parser_internal_foreach (parser, NULL, NULL);
if (rc != DC_STATUS_SUCCESS)
return rc;
}
return hw_ostc_parser_internal_foreach (parser, callback, userdata);
}

View File

@ -24,7 +24,6 @@
#endif #endif
#include <stdlib.h> // malloc, free #include <stdlib.h> // malloc, free
#include <stdio.h> // snprintf
#include <string.h> #include <string.h>
#include "socket.h" #include "socket.h"
@ -48,7 +47,6 @@
#include "context-private.h" #include "context-private.h"
#include "iostream-private.h" #include "iostream-private.h"
#include "iterator-private.h" #include "iterator-private.h"
#include "descriptor-private.h"
#include "array.h" #include "array.h"
#include "platform.h" #include "platform.h"
@ -227,7 +225,7 @@ dc_irda_iterator_new (dc_iterator_t **out, dc_context_t *context, dc_descriptor_
INFO (context, "Discover: address=%08x, name=%s, charset=%02x, hints=%04x", INFO (context, "Discover: address=%08x, name=%s, charset=%02x, hints=%04x",
address, name, charset, hints); address, name, charset, hints);
if (!dc_descriptor_filter (descriptor, DC_TRANSPORT_IRDA, name, NULL)) { if (!dc_descriptor_filter (descriptor, DC_TRANSPORT_IRDA, name)) {
continue; continue;
} }
@ -314,7 +312,7 @@ dc_irda_open (dc_iostream_t **out, dc_context_t *context, unsigned int address,
peer.irdaDeviceID[1] = (address >> 8) & 0xFF; peer.irdaDeviceID[1] = (address >> 8) & 0xFF;
peer.irdaDeviceID[2] = (address >> 16) & 0xFF; peer.irdaDeviceID[2] = (address >> 16) & 0xFF;
peer.irdaDeviceID[3] = (address >> 24) & 0xFF; peer.irdaDeviceID[3] = (address >> 24) & 0xFF;
snprintf (peer.irdaServiceName, sizeof(peer.irdaServiceName), "LSAP-SEL%u", lsap); dc_platform_snprintf (peer.irdaServiceName, sizeof(peer.irdaServiceName), "LSAP-SEL%u", lsap);
#else #else
struct sockaddr_irda peer; struct sockaddr_irda peer;
peer.sir_family = AF_IRDA; peer.sir_family = AF_IRDA;

Some files were not shown because too many files have changed in this diff Show More