Compare commits

..

88 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
144 changed files with 3500 additions and 2350 deletions

View File

@ -19,7 +19,7 @@ jobs:
CC: ${{ matrix.compiler }}
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Install dependencies
run: sudo apt-get install libbluetooth-dev libusb-1.0-0-dev
- run: autoreconf --install --force
@ -30,7 +30,7 @@ jobs:
run: |
make install DESTDIR=$PWD/artifacts
tar -czf ${{ github.job }}-${{ matrix.compiler }}.tar.gz -C artifacts usr
- uses: actions/upload-artifact@v3
- uses: actions/upload-artifact@v4
with:
name: ${{ github.job }}-${{ matrix.compiler }}
path: ${{ github.job }}-${{ matrix.compiler }}.tar.gz
@ -50,7 +50,7 @@ jobs:
CC: ${{ matrix.compiler }}
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Install dependencies
run: brew install autoconf automake libtool hidapi libusb
- run: autoreconf --install --force
@ -61,7 +61,7 @@ jobs:
run: |
make install DESTDIR=$PWD/artifacts
tar -czf ${{ github.job }}-${{ matrix.compiler }}.tar.gz -C artifacts usr
- uses: actions/upload-artifact@v3
- uses: actions/upload-artifact@v4
with:
name: ${{ github.job }}-${{ matrix.compiler }}
path: ${{ github.job }}-${{ matrix.compiler }}.tar.gz
@ -78,7 +78,7 @@ jobs:
arch: [i686, x86_64]
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Install dependencies
run: sudo apt-get install gcc-mingw-w64 binutils-mingw-w64 mingw-w64-tools
- name: Install libusb
@ -118,7 +118,7 @@ jobs:
run: |
make install DESTDIR=$PWD/artifacts
tar -czf ${{ github.job }}-${{ matrix.arch }}.tar.gz -C artifacts usr
- uses: actions/upload-artifact@v3
- uses: actions/upload-artifact@v4
with:
name: ${{ github.job }}-${{ matrix.arch }}
path: ${{ github.job }}-${{ matrix.arch }}.tar.gz
@ -138,7 +138,7 @@ jobs:
CONFIGURATION: Release
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: msys2/setup-msys2@v2
with:
install: autoconf automake libtool pkg-config make gcc
@ -147,9 +147,9 @@ jobs:
./configure --prefix=/usr
make -C src revision.h
shell: msys2 {0}
- uses: microsoft/setup-msbuild@v1
- uses: microsoft/setup-msbuild@v2
- run: msbuild -m -p:Platform=${{ matrix.platform }} -p:Configuration=${{ env.CONFIGURATION }} contrib/msvc/libdivecomputer.vcxproj
- uses: actions/upload-artifact@v3
- uses: actions/upload-artifact@v4
with:
name: ${{ github.job }}-${{ matrix.platform }}
path: contrib/msvc/${{ matrix.platform }}/${{ env.CONFIGURATION }}/bin
@ -161,13 +161,13 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- 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@v3
- uses: actions/upload-artifact@v4
with:
name: ${{ github.job }}
path: contrib/android/libs

View File

@ -9,7 +9,7 @@ jobs:
name: Release
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Version number
id: version

View File

@ -1,7 +1,7 @@
# Versioning.
m4_define([dc_version_major],[0])
m4_define([dc_version_minor],[8])
m4_define([dc_version_micro],[1])
m4_define([dc_version_minor],[9])
m4_define([dc_version_micro],[0])
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]))

View File

@ -71,6 +71,7 @@ LOCAL_SRC_FILES := \
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 \

View File

@ -239,6 +239,7 @@
<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" />
@ -331,7 +332,6 @@
<ClInclude Include="..\..\src\cressi_leonardo.h" />
<ClInclude Include="..\..\src\deepblu_cosmiq.h" />
<ClInclude Include="..\..\src\deepsix_excursion.h" />
<ClInclude Include="..\..\src\descriptor-private.h" />
<ClInclude Include="..\..\src\device-private.h" />
<ClInclude Include="..\..\src\diverite_nitekq.h" />
<ClInclude Include="..\..\src\divesoft_freedom.h" />
@ -358,6 +358,7 @@
<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" />

View File

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

View File

@ -53,7 +53,7 @@ with
Each dive invokes
.Fa callback
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.
The fingerprint can be used to record the newest dive and stop
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
.Dv DC_STATUS_SUCCESS ) .
.Sh SEE ALSO
.Xr dc_parser_set_data 3
.Xr dc_parser_new 3
.Sh AUTHORS
The
.Lb libdivecomputer

View File

@ -37,7 +37,7 @@
Extract the date and time of a dive,
.Fa parser ,
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
the dive.
.Pp
@ -57,7 +57,7 @@ messages on further failure.
.Sh SEE ALSO
.Xr dc_datetime_gmtime 3 ,
.Xr dc_datetime_localtime 3 ,
.Xr dc_parser_set_data 3
.Xr dc_parser_new 3
.Sh AUTHORS
The
.Lb libdivecomputer

View File

@ -39,7 +39,7 @@
Extract a field from a dive,
.Fa parser ,
previously initialised with
.Xr dc_parser_set_data 3 .
.Xr dc_parser_new 3 .
The
.Fa value
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
further failure.
.Sh SEE ALSO
.Xr dc_parser_set_data 3
.Xr dc_parser_new 3
.Sh AUTHORS
The
.Lb libdivecomputer

View File

@ -39,8 +39,6 @@
.Fa "dc_parser_t **parser"
.Fa "dc_context_t *context"
.Fa "dc_descriptor_t *descriptor"
.Fa "unsigned int devtime"
.Fa "dc_ticks_t systime"
.Fc
.Sh DESCRIPTION
Creates a parser for a single dive extracted from the dive computer with
@ -55,10 +53,6 @@ parameter; and
.Nm dc_parser_new2 ,
which is given device values (model, etc.) directly.
.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
.Xr dc_parser_destroy 3 .
.Sh RETURN VALUES

View File

@ -31,7 +31,7 @@
.Ft "typedef void"
.Fo "(*dc_sample_callback_t)"
.Fa "dc_sample_type_t type"
.Fa "dc_sample_value_t value"
.Fa "const dc_sample_value_t *value"
.Fa "void *userdata"
.Fc
.Ft dc_status_t
@ -42,7 +42,7 @@
.Fc
.Sh DESCRIPTION
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
.Fa callback
with the
@ -63,7 +63,7 @@ closed.
The following sample types may be raised:
.Bl -tag -width Ds
.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
.Fa time
field.
@ -184,7 +184,7 @@ Returns
.Dv DC_STATUS_OK
on success and another code on failure.
.Sh SEE ALSO
.Xr dc_parser_set_data 3
.Xr dc_parser_new 3
.Sh AUTHORS
The
.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 .
.It
For each iterated dive, create a new parser with
.Xr dc_parser_new 3
and set the parsed data with
.Xr dc_parser_set_data 3 .
.Xr dc_parser_new 3 .
.It
Get attributes of the parsed dive with
.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},
{"veo250", DC_FAMILY_OCEANIC_VEO250, 0x424C},
{"atom2", DC_FAMILY_OCEANIC_ATOM2, 0x4342},
{"i330r", DC_FAMILY_PELAGIC_I330R, 0x4744},
{"nemo", DC_FAMILY_MARES_NEMO, 0},
{"puck", DC_FAMILY_MARES_PUCK, 7},
{"darwin", DC_FAMILY_MARES_DARWIN, 0},

View File

@ -80,20 +80,12 @@ dive_cb (const unsigned char *data, unsigned int size, const unsigned char *fing
// Create the parser.
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) {
ERROR ("Error creating the parser.");
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.
message ("Parsing the dive data.\n");
rc = dctool_output_write (divedata->output, parser, data, size, fingerprint, fsize);

View File

@ -54,17 +54,17 @@ parse (dc_buffer_t *buffer, dc_context_t *context, dc_descriptor_t *descriptor,
// Create the parser.
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) {
ERROR ("Error creating the parser.");
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.");
// Set the clock.
message ("Setting the clock.\n");
rc = dc_parser_set_clock (parser, devtime, systime);
if (rc != DC_STATUS_SUCCESS && rc != DC_STATUS_UNSUPPORTED) {
ERROR ("Error setting the clock.");
goto cleanup;
}

View File

@ -90,7 +90,7 @@ convert_volume (double value, dctool_units_t units)
}
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[] = {
"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;
unsigned int seconds = 0, milliseconds = 0;
switch (type) {
case DC_SAMPLE_TIME:
seconds = value->time / 1000;
milliseconds = value->time % 1000;
if (sampledata->nsamples++)
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;
case DC_SAMPLE_DEPTH:
fprintf (sampledata->ostream, " <depth>%.2f</depth>\n",
convert_depth(value.depth, sampledata->units));
convert_depth(value->depth, sampledata->units));
break;
case DC_SAMPLE_PRESSURE:
fprintf (sampledata->ostream, " <pressure tank=\"%u\">%.2f</pressure>\n",
value.pressure.tank,
convert_pressure(value.pressure.value, sampledata->units));
value->pressure.tank,
convert_pressure(value->pressure.value, sampledata->units));
break;
case DC_SAMPLE_TEMPERATURE:
fprintf (sampledata->ostream, " <temperature>%.2f</temperature>\n",
convert_temperature(value.temperature, sampledata->units));
convert_temperature(value->temperature, sampledata->units));
break;
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",
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;
case DC_SAMPLE_RBT:
fprintf (sampledata->ostream, " <rbt>%u</rbt>\n", value.rbt);
fprintf (sampledata->ostream, " <rbt>%u</rbt>\n", value->rbt);
break;
case DC_SAMPLE_HEARTBEAT:
fprintf (sampledata->ostream, " <heartbeat>%u</heartbeat>\n", value.heartbeat);
fprintf (sampledata->ostream, " <heartbeat>%u</heartbeat>\n", value->heartbeat);
break;
case DC_SAMPLE_BEARING:
fprintf (sampledata->ostream, " <bearing>%u</bearing>\n", value.bearing);
fprintf (sampledata->ostream, " <bearing>%u</bearing>\n", value->bearing);
break;
case DC_SAMPLE_VENDOR:
fprintf (sampledata->ostream, " <vendor type=\"%u\" size=\"%u\">", value.vendor.type, value.vendor.size);
for (unsigned int i = 0; i < value.vendor.size; ++i)
fprintf (sampledata->ostream, "%02X", ((const unsigned char *) value.vendor.data)[i]);
fprintf (sampledata->ostream, " <vendor type=\"%u\" size=\"%u\">", value->vendor.type, value->vendor.size);
for (unsigned int i = 0; i < value->vendor.size; ++i)
fprintf (sampledata->ostream, "%02X", ((const unsigned char *) value->vendor.data)[i]);
fprintf (sampledata->ostream, "</vendor>\n");
break;
case DC_SAMPLE_SETPOINT:
fprintf (sampledata->ostream, " <setpoint>%.2f</setpoint>\n", value.setpoint);
fprintf (sampledata->ostream, " <setpoint>%.2f</setpoint>\n", value->setpoint);
break;
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;
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;
case DC_SAMPLE_DECO:
fprintf (sampledata->ostream, " <deco time=\"%u\" depth=\"%.2f\">%s</deco>\n",
value.deco.time,
convert_depth(value.deco.depth, sampledata->units),
decostop[value.deco.type]);
value->deco.time,
convert_depth(value->deco.depth, sampledata->units),
decostop[value->deco.type]);
if (value->deco.tts) {
fprintf (sampledata->ostream, " <tts>%u</tts>\n",
value->deco.tts);
}
break;
case DC_SAMPLE_GASMIX:
fprintf (sampledata->ostream, " <gasmix>%u</gasmix>\n", value.gasmix);
fprintf (sampledata->ostream, " <gasmix>%u</gasmix>\n", value->gasmix);
break;
default:
break;
@ -322,11 +338,19 @@ dctool_xml_output_write (dctool_output_t *abstract, dc_parser_t *parser, const u
"<gasmix>\n"
" <he>%.1f</he>\n"
" <o2>%.1f</o2>\n"
" <n2>%.1f</n2>\n"
"</gasmix>\n",
" <n2>%.1f</n2>\n",
gasmix.helium * 100.0,
gasmix.oxygen * 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.
@ -354,6 +378,12 @@ dctool_xml_output_write (dctool_output_t *abstract, dc_parser_t *parser, const u
" <gasmix>%u</gasmix>\n",
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) {
fprintf (output->ostream,
" <type>%s</type>\n"
@ -420,8 +450,14 @@ dctool_xml_output_write (dctool_output_t *abstract, dc_parser_t *parser, const u
}
if (status != DC_STATUS_UNSUPPORTED) {
fprintf (output->ostream, "<salinity type=\"%u\">%.1f</salinity>\n",
salinity.type, salinity.density);
const char *names[] = {"fresh", "salt"};
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.

View File

@ -36,9 +36,6 @@ atomics_cobalt_device_version (dc_device_t *device, unsigned char data[], unsign
dc_status_t
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
}
#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)
/**
* 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
}
#endif /* __cplusplus */

View File

@ -74,6 +74,7 @@ typedef enum dc_family_t {
DC_FAMILY_OCEANIC_VTPRO = (4 << 16),
DC_FAMILY_OCEANIC_VEO250,
DC_FAMILY_OCEANIC_ATOM2,
DC_FAMILY_PELAGIC_I330R,
/* Mares */
DC_FAMILY_MARES_NEMO = (5 << 16),
DC_FAMILY_MARES_PUCK,

View File

@ -29,29 +29,96 @@
extern "C" {
#endif /* __cplusplus */
/**
* Opaque object representing a supported dive computer.
*/
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_descriptor_iterator (dc_iterator_t **iterator);
/**
* Free the device descriptor.
*
* @param[in] descriptor A valid device descriptor.
*/
void
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 *
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 *
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_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
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
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
}
#endif /* __cplusplus */

View File

@ -141,12 +141,21 @@ typedef struct dc_salinity_t {
double density;
} 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 {
double helium;
double oxygen;
double nitrogen;
dc_usage_t usage;
} dc_gasmix_t;
#define DC_SENSOR_NONE 0xFFFFFFFF
#define DC_GASMIX_UNKNOWN 0xFFFFFFFF
typedef enum dc_tankvolume_t {
@ -185,6 +194,7 @@ typedef struct dc_tank_t {
double workpressure; /* Work pressure (bar) */
double beginpressure; /* Begin pressure (bar) */
double endpressure; /* End pressure (bar) */
dc_usage_t usage;
} dc_tank_t;
typedef enum dc_decomodel_type_t {
@ -225,7 +235,7 @@ typedef struct dc_decomodel_t {
} dc_decomodel_t;
typedef union dc_sample_value_t {
unsigned int time;
unsigned int time; /* Milliseconds */
double depth;
struct {
unsigned int tank;
@ -247,25 +257,29 @@ typedef union dc_sample_value_t {
const void *data;
} vendor;
double setpoint;
double ppo2;
struct {
unsigned int sensor;
double value;
} ppo2;
double cns;
struct {
unsigned int type;
unsigned int time;
double depth;
unsigned int tts;
} deco;
unsigned int gasmix; /* Gas mix index */
} dc_sample_value_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_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_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_parser_get_type (dc_parser_t *parser);
@ -279,9 +293,6 @@ 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_parser_set_data (dc_parser_t *parser, const unsigned char *data, unsigned int size);
dc_status_t
dc_parser_get_datetime (dc_parser_t *parser, dc_datetime_t *datetime);

View File

@ -35,9 +35,6 @@ extern "C" {
dc_status_t
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
}
#endif /* __cplusplus */

View File

@ -38,9 +38,6 @@ reefnet_sensuspro_device_get_handshake (dc_device_t *device, unsigned char data[
dc_status_t
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
}
#endif /* __cplusplus */

View File

@ -56,9 +56,6 @@ reefnet_sensusultra_device_write_parameter (dc_device_t *device, reefnet_sensusu
dc_status_t
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
}
#endif /* __cplusplus */

View File

@ -86,6 +86,14 @@ typedef enum dc_usb_recipient_t {
DC_USB_RECIPIENT_OTHER = 0x03,
} 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.
*/

View File

@ -32,6 +32,14 @@
extern "C" {
#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.
*/

View File

@ -16,7 +16,7 @@ endif
libdivecomputer_la_SOURCES = \
version.c \
descriptor-private.h descriptor.c \
descriptor.c \
iostream-private.h iostream.c \
iterator-private.h iterator.c \
common-private.h common.c \
@ -43,6 +43,7 @@ libdivecomputer_la_SOURCES = \
oceanic_atom2.h oceanic_atom2.c oceanic_atom2_parser.c \
oceanic_veo250.h oceanic_veo250.c oceanic_veo250_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_nemo.h mares_nemo.c mares_nemo_parser.c \
mares_puck.h mares_puck.c \

View File

@ -404,3 +404,14 @@ signextend (unsigned int value, unsigned int nbits)
else
return value & mask;
}
unsigned int
popcount (unsigned int value)
{
unsigned int count = 0;
while (value) {
value &= value - 1;
count++;
}
return count;
}

View File

@ -126,6 +126,9 @@ dec2bcd (unsigned char value);
unsigned int
signextend (unsigned int value, unsigned int nbits);
unsigned int
popcount (unsigned int value);
#ifdef __cplusplus
}
#endif /* __cplusplus */

View File

@ -41,6 +41,8 @@
#define FP_OFFSET 20
#define SZ_HEADER 228
#define SZ_MEMORY1 (29 * 64 * 1024) // Cobalt 1
#define SZ_MEMORY2 (41 * 64 * 1024) // Cobalt 2
#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;
}
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) {
dc_buffer_free (buffer);
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);
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
}

View File

@ -43,7 +43,6 @@ struct atomics_cobalt_parser_t {
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_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value);
@ -52,7 +51,6 @@ static dc_status_t atomics_cobalt_parser_samples_foreach (dc_parser_t *abstract,
static const dc_parser_vtable_t atomics_cobalt_parser_vtable = {
sizeof(atomics_cobalt_parser_t),
DC_FAMILY_ATOMICS_COBALT,
atomics_cobalt_parser_set_data, /* set_data */
NULL, /* set_clock */
NULL, /* set_atmospheric */
atomics_cobalt_parser_set_density, /* set_density */
@ -64,7 +62,7 @@ static const dc_parser_vtable_t atomics_cobalt_parser_vtable = {
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;
@ -72,7 +70,7 @@ atomics_cobalt_parser_create (dc_parser_t **out, dc_context_t *context)
return DC_STATUS_INVALIDARGS;
// 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) {
ERROR (context, "Failed to allocate memory.");
return DC_STATUS_NOMEMORY;
@ -87,27 +85,6 @@ atomics_cobalt_parser_create (dc_parser_t **out, dc_context_t *context)
}
static dc_status_t
atomics_cobalt_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size)
{
return DC_STATUS_SUCCESS;
}
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->hydrostatic = hydrostatic;
return DC_STATUS_SUCCESS;
}
static dc_status_t
atomics_cobalt_parser_set_density (dc_parser_t *abstract, double density)
{
@ -171,6 +148,7 @@ atomics_cobalt_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, un
*((unsigned int *) value) = p[0x2a];
break;
case DC_FIELD_GASMIX:
gasmix->usage = DC_USAGE_NONE;
gasmix->helium = p[SZ_HEADER + SZ_GASMIX * flags + 5] / 100.0;
gasmix->oxygen = p[SZ_HEADER + SZ_GASMIX * flags + 4] / 100.0;
gasmix->nitrogen = 1.0 - gasmix->oxygen - gasmix->helium;
@ -202,6 +180,7 @@ atomics_cobalt_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, un
tank->gasmix = flags;
tank->beginpressure = array_uint16_le(p + 6) * PSI / BAR;
tank->endpressure = array_uint16_le(p + 14) * PSI / BAR;
tank->usage = DC_USAGE_NONE;
break;
case DC_FIELD_DIVEMODE:
switch(p[0x24]) {
@ -276,19 +255,19 @@ atomics_cobalt_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback
// Time (seconds).
time += interval;
sample.time = time;
if (callback) callback (DC_SAMPLE_TIME, sample, userdata);
sample.time = time * 1000;
if (callback) callback (DC_SAMPLE_TIME, &sample, userdata);
// Depth (1/1000 bar).
unsigned int depth = array_uint16_le (data + offset + 0);
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).
unsigned int pressure = array_uint16_le (data + offset + 2);
sample.pressure.tank = tank;
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
unsigned int gasmix = data[offset + 4];
@ -304,14 +283,14 @@ atomics_cobalt_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback
return DC_STATUS_DATAFORMAT;
}
sample.gasmix = idx;
if (callback) callback (DC_SAMPLE_GASMIX, sample, userdata);
if (callback) callback (DC_SAMPLE_GASMIX, &sample, userdata);
gasmix_previous = gasmix;
}
// Temperature (1 °F).
unsigned int temperature = data[offset + 8];
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
sample.event.type = 0;
@ -321,15 +300,15 @@ atomics_cobalt_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback
unsigned int violation = data[offset + 11];
if (violation & 0x01) {
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) {
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) {
sample.event.type = SAMPLE_EVENT_PO2;
if (callback) callback (DC_SAMPLE_EVENT, sample, userdata);
if (callback) callback (DC_SAMPLE_EVENT, &sample, userdata);
}
// NDL & deco
@ -344,7 +323,8 @@ atomics_cobalt_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback
sample.deco.type = DC_DECO_NDL;
sample.deco.time = ndl;
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;
}

View File

@ -51,7 +51,6 @@
#include "context-private.h"
#include "iostream-private.h"
#include "iterator-private.h"
#include "descriptor-private.h"
#include "platform.h"
#ifdef _WIN32
@ -455,7 +454,7 @@ dc_bluetooth_iterator_next (dc_iterator_t *abstract, void *out)
INFO (abstract->context, "Discover: address=" DC_ADDRESS_FORMAT ", name=%s",
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;
}

View File

@ -31,6 +31,8 @@
#define ISINSTANCE(device) dc_device_isinstance((device), &citizen_aqualand_device_vtable)
#define SZ_HEADER 32
typedef struct citizen_aqualand_device_t {
dc_device_t base;
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 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) {
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);
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
}

View File

@ -36,7 +36,6 @@ typedef struct citizen_aqualand_parser_t {
dc_parser_t base;
} 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_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);
@ -44,7 +43,6 @@ static dc_status_t citizen_aqualand_parser_samples_foreach (dc_parser_t *abstrac
static const dc_parser_vtable_t citizen_aqualand_parser_vtable = {
sizeof(citizen_aqualand_parser_t),
DC_FAMILY_CITIZEN_AQUALAND,
citizen_aqualand_parser_set_data, /* set_data */
NULL, /* set_clock */
NULL, /* set_atmospheric */
NULL, /* set_density */
@ -56,7 +54,7 @@ static const dc_parser_vtable_t citizen_aqualand_parser_vtable = {
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;
@ -64,7 +62,7 @@ citizen_aqualand_parser_create (dc_parser_t **out, dc_context_t *context)
return DC_STATUS_INVALIDARGS;
// 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) {
ERROR (context, "Failed to allocate memory.");
return DC_STATUS_NOMEMORY;
@ -76,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
citizen_aqualand_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime)
{
@ -241,15 +232,15 @@ citizen_aqualand_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callba
// Time
time += interval;
sample.time = time;
if (callback) callback (DC_SAMPLE_TIME, sample, userdata);
sample.time = time * 1000;
if (callback) callback (DC_SAMPLE_TIME, &sample, userdata);
// Depth
if (metric)
sample.depth = depth / 10.0;
else
sample.depth = depth * FEET;
if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata);
if (callback) callback (DC_SAMPLE_DEPTH, &sample, userdata);
// Temperature
if (time % 300 == 0) {
@ -260,7 +251,7 @@ citizen_aqualand_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callba
sample.temperature = temperature / 10.0;
else
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

@ -603,7 +603,7 @@ cochran_commander_profile_size(cochran_commander_device_t *device, cochran_data_
// Corrupt dive, guess the end address
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);
}
@ -965,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 );
// 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) {
ERROR (abstract->context, "Failed to create the ringbuffer stream.");
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);
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
}

View File

@ -99,7 +99,6 @@ typedef struct cochran_commander_parser_t {
unsigned int nevents;
} 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_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);
@ -107,7 +106,6 @@ static dc_status_t cochran_commander_parser_samples_foreach (dc_parser_t *parser
static const dc_parser_vtable_t cochran_commander_parser_vtable = {
sizeof(cochran_commander_parser_t),
DC_FAMILY_COCHRAN_COMMANDER,
cochran_commander_parser_set_data, /* set_data */
NULL, /* set_clock */
NULL, /* set_atmospheric */
NULL, /* set_density */
@ -315,7 +313,7 @@ cochran_commander_handle_event (cochran_commander_parser_t *parser, unsigned cha
sample.event.time = 0;
sample.event.value = 0;
sample.event.flags = event->flag;
if (callback) callback (DC_SAMPLE_EVENT, sample, userdata);
if (callback) callback (DC_SAMPLE_EVENT, &sample, userdata);
}
}
@ -352,7 +350,7 @@ cochran_commander_backparse(cochran_commander_parser_t *parser, const unsigned c
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;
dc_status_t status = DC_STATUS_SUCCESS;
@ -361,7 +359,7 @@ cochran_commander_parser_create (dc_parser_t **out, dc_context_t *context, unsig
return DC_STATUS_INVALIDARGS;
// 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) {
ERROR (context, "Failed to allocate memory.");
return DC_STATUS_NOMEMORY;
@ -407,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
cochran_commander_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime)
{
@ -514,6 +505,7 @@ cochran_commander_parser_get_field (dc_parser_t *abstract, dc_field_type_t type,
// Gas percentages are decimal and encoded as
// highbyte = integer portion
// 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;
if (layout->helium == UNSUPPORTED) {
gasmix->helium = 0;
@ -578,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 depth = samples[1]; // Half feet
last_sample_time = sample.time = time;
if (callback) callback (DC_SAMPLE_TIME, sample, userdata);
last_sample_time = sample.time = time * 1000;
if (callback) callback (DC_SAMPLE_TIME, &sample, userdata);
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;
if (callback) callback (DC_SAMPLE_TEMPERATURE, sample, userdata);
if (callback) callback (DC_SAMPLE_TEMPERATURE, &sample, userdata);
sample.gasmix = 0;
if (callback) callback(DC_SAMPLE_GASMIX, sample, userdata);
if (callback) callback(DC_SAMPLE_GASMIX, &sample, userdata);
while (offset < size) {
const unsigned char *s = samples + offset;
sample.time = time;
sample.time = time * 1000;
if (last_sample_time != sample.time) {
// We haven't issued this time yet.
last_sample_time = sample.time;
if (callback) callback (DC_SAMPLE_TIME, sample, userdata);
if (callback) callback (DC_SAMPLE_TIME, &sample, userdata);
}
if (*s & 0x80) {
@ -615,7 +607,8 @@ cochran_commander_parser_samples_foreach_tm (dc_parser_t *abstract, dc_sample_ca
sample.deco.type = DC_DECO_DECOSTOP;
sample.deco.time = 60; // We don't know the duration
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;
case 0xAD: // Increment ceiling (shallower)
deco_ceiling -= 10; // feet
@ -623,7 +616,8 @@ cochran_commander_parser_samples_foreach_tm (dc_parser_t *abstract, dc_sample_ca
sample.deco.type = DC_DECO_DECOSTOP;
sample.deco.depth = deco_ceiling * FEET;
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;
default:
cochran_commander_handle_event(parser, s[0], callback, userdata);
@ -636,7 +630,7 @@ cochran_commander_parser_samples_foreach_tm (dc_parser_t *abstract, dc_sample_ca
else
temp += (*s & 0x0f);
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++;
@ -650,7 +644,7 @@ cochran_commander_parser_samples_foreach_tm (dc_parser_t *abstract, dc_sample_ca
depth += s[0] & 0x3f;
sample.depth = (depth / 2.0) * FEET;
if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata);
if (callback) callback (DC_SAMPLE_DEPTH, &sample, userdata);
offset++;
time += sample_interval;
@ -714,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;
}
last_sample_time = sample.time = time;
if (callback) callback (DC_SAMPLE_TIME, sample, userdata);
last_sample_time = sample.time = time * 1000;
if (callback) callback (DC_SAMPLE_TIME, &sample, userdata);
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;
if (callback) callback (DC_SAMPLE_TEMPERATURE, sample, userdata);
if (callback) callback (DC_SAMPLE_TEMPERATURE, &sample, userdata);
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;
while (offset < size) {
const unsigned char *s = samples + offset;
sample.time = time;
sample.time = time * 1000;
if (last_sample_time != sample.time) {
// We haven't issued this time yet.
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
@ -774,7 +768,8 @@ cochran_commander_parser_samples_foreach_emc (dc_parser_t *abstract, dc_sample_c
sample.deco.type = DC_DECO_DECOSTOP;
sample.deco.time = (array_uint16_le(s + 3) + 1) * 60;
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;
case 0xAD: // Increment ceiling (shallower)
deco_ceiling -= 10; // feet
@ -782,7 +777,8 @@ cochran_commander_parser_samples_foreach_emc (dc_parser_t *abstract, dc_sample_c
sample.deco.type = DC_DECO_DECOSTOP;
sample.deco.depth = deco_ceiling * FEET;
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;
case 0xC0: // Switched to FO2 21% mode (surface)
// Event seen upon surfacing
@ -791,14 +787,14 @@ cochran_commander_parser_samples_foreach_emc (dc_parser_t *abstract, dc_sample_c
case 0xEF: // Switched to gas blend 2
if (last_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;
}
break;
case 0xF3: // Switched to gas blend 1
if (last_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;
}
break;
@ -818,7 +814,7 @@ cochran_commander_parser_samples_foreach_emc (dc_parser_t *abstract, dc_sample_c
depth += (s[0] & 0x3f);
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.
if (time % 2 == 0) {
@ -834,7 +830,7 @@ cochran_commander_parser_samples_foreach_emc (dc_parser_t *abstract, dc_sample_c
double temperature = s[1] / 2.0 + 20.0;
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
@ -855,7 +851,8 @@ cochran_commander_parser_samples_foreach_emc (dc_parser_t *abstract, dc_sample_c
sample.deco.type = DC_DECO_NDL;
sample.deco.time = deco_time * 60;
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;
case 23:
@ -865,7 +862,8 @@ cochran_commander_parser_samples_foreach_emc (dc_parser_t *abstract, dc_sample_c
sample.deco.type = DC_DECO_DECOSTOP;
sample.deco.depth = deco_ceiling * FEET;
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;
}

View File

@ -38,6 +38,8 @@
#define SZ_PACKET 0x80
#define SZ_PAGE (SZ_PACKET / 4)
#define SZ_HEADER 32
#define IQ700 0x05
#define EDY 0x08
@ -430,7 +432,7 @@ cressi_edy_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, v
}
// 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.
unsigned int eop = array_uint_le (logbook + layout->config + 2, layout->rb_logbook_size) * SZ_PAGE + layout->rb_profile_begin;
@ -455,7 +457,7 @@ cressi_edy_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, v
}
// 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.
if (total + length > layout->rb_profile_end - layout->rb_profile_begin) {
@ -479,7 +481,7 @@ cressi_edy_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, v
// Create the ringbuffer stream.
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) {
ERROR (abstract->context, "Failed to create the ringbuffer stream.");
return rc;
@ -508,7 +510,7 @@ cressi_edy_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, v
}
// 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.
offset -= length;
@ -522,6 +524,13 @@ cressi_edy_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, v
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;
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);
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
}

View File

@ -31,6 +31,8 @@
#define IQ700 0x05
#define EDY 0x08
#define SZ_HEADER 32
typedef struct cressi_edy_parser_t cressi_edy_parser_t;
struct cressi_edy_parser_t {
@ -38,7 +40,6 @@ struct cressi_edy_parser_t {
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_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);
@ -46,7 +47,6 @@ static dc_status_t cressi_edy_parser_samples_foreach (dc_parser_t *abstract, dc_
static const dc_parser_vtable_t cressi_edy_parser_vtable = {
sizeof(cressi_edy_parser_t),
DC_FAMILY_CRESSI_EDY,
cressi_edy_parser_set_data, /* set_data */
NULL, /* set_clock */
NULL, /* set_atmospheric */
NULL, /* set_density */
@ -73,7 +73,7 @@ cressi_edy_parser_count_gasmixes (const unsigned char *data)
}
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;
@ -81,7 +81,7 @@ cressi_edy_parser_create (dc_parser_t **out, dc_context_t *context, unsigned int
return DC_STATUS_INVALIDARGS;
// 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) {
ERROR (context, "Failed to allocate memory.");
return DC_STATUS_NOMEMORY;
@ -96,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
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;
const unsigned char *p = abstract->data;
@ -130,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;
if (abstract->size < 32)
if (abstract->size < SZ_HEADER)
return DC_STATUS_DATAFORMAT;
const unsigned char *p = abstract->data;
@ -152,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);
break;
case DC_FIELD_GASMIX:
gasmix->usage = DC_USAGE_NONE;
gasmix->helium = 0.0;
gasmix->oxygen = bcd2dec (p[0x17 - flags]) / 100.0;
gasmix->nitrogen = 1.0 - gasmix->oxygen - gasmix->helium;
@ -188,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 gasmix = 0xFFFFFFFF;
unsigned int offset = 32;
unsigned int offset = SZ_HEADER;
while (offset + 2 <= size) {
dc_sample_value_t sample = {0};
@ -201,13 +195,13 @@ cressi_edy_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t c
// Time (seconds).
time += interval;
sample.time = time;
if (callback) callback (DC_SAMPLE_TIME, sample, userdata);
sample.time = time * 1000;
if (callback) callback (DC_SAMPLE_TIME, &sample, userdata);
// Depth (1/10 m).
unsigned int depth = bcd2dec (data[offset + 0] & 0x0F) * 100 + bcd2dec (data[offset + 1]);
sample.depth = depth / 10.0;
if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata);
if (callback) callback (DC_SAMPLE_DEPTH, &sample, userdata);
// Current gasmix
if (ngasmixes) {
@ -220,7 +214,7 @@ cressi_edy_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t c
}
if (idx != gasmix) {
sample.gasmix = idx;
if (callback) callback (DC_SAMPLE_GASMIX, sample, userdata);
if (callback) callback (DC_SAMPLE_GASMIX, &sample, userdata);
gasmix = idx;
}
}

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);
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
}

View File

@ -62,7 +62,6 @@ typedef struct cressi_goa_layout_t {
unsigned int temperature;
} cressi_goa_layout_t;
static dc_status_t cressi_goa_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size);
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_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata);
@ -70,7 +69,6 @@ static dc_status_t cressi_goa_parser_samples_foreach (dc_parser_t *abstract, dc_
static const dc_parser_vtable_t cressi_goa_parser_vtable = {
sizeof(cressi_goa_parser_t),
DC_FAMILY_CRESSI_GOA,
cressi_goa_parser_set_data, /* set_data */
NULL, /* set_clock */
NULL, /* set_atmospheric */
NULL, /* set_density */
@ -128,7 +126,7 @@ static const cressi_goa_layout_t layouts[] = {
};
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;
@ -136,7 +134,7 @@ cressi_goa_parser_create (dc_parser_t **out, dc_context_t *context, unsigned int
return DC_STATUS_INVALIDARGS;
// 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) {
ERROR (context, "Failed to allocate memory.");
return DC_STATUS_NOMEMORY;
@ -149,12 +147,6 @@ cressi_goa_parser_create (dc_parser_t **out, dc_context_t *context, unsigned int
return DC_STATUS_SUCCESS;
}
static dc_status_t
cressi_goa_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size)
{
return DC_STATUS_SUCCESS;
}
static dc_status_t
cressi_goa_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime)
{
@ -250,6 +242,7 @@ cressi_goa_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsign
*((unsigned int *) value) = ngasmixes;
break;
case DC_FIELD_GASMIX:
gasmix->usage = DC_USAGE_NONE;
gasmix->helium = 0.0;
gasmix->oxygen = data[layout->gasmix + 2 * flags + 1] / 100.0;
gasmix->nitrogen = 1.0 - gasmix->oxygen - gasmix->helium;
@ -329,25 +322,25 @@ cressi_goa_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t c
if (complete) {
// Time (seconds).
sample.time = time;
if (callback) callback (DC_SAMPLE_TIME, sample, userdata);
sample.time = time * 1000;
if (callback) callback (DC_SAMPLE_TIME, &sample, userdata);
// Temperature (1/10 °C).
if (have_temperature) {
sample.temperature = temperature / 10.0;
if (callback) callback (DC_SAMPLE_TEMPERATURE, sample, userdata);
if (callback) callback (DC_SAMPLE_TEMPERATURE, &sample, userdata);
have_temperature = 0;
}
// Depth (1/10 m).
sample.depth = depth / 10.0;
if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata);
if (callback) callback (DC_SAMPLE_DEPTH, &sample, userdata);
// Gas change
if (divemode == SCUBA || divemode == NITROX) {
if (gasmix != gasmix_previous) {
sample.gasmix = gasmix;
if (callback) callback (DC_SAMPLE_GASMIX, sample, userdata);
if (callback) callback (DC_SAMPLE_GASMIX, &sample, userdata);
gasmix_previous = gasmix;
}
}

View File

@ -41,7 +41,7 @@
#define RB_PROFILE_BEGIN 0x1438
#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 PACKETSIZE 32

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);
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
}

View File

@ -39,7 +39,6 @@ struct cressi_leonardo_parser_t {
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_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);
@ -47,7 +46,6 @@ static dc_status_t cressi_leonardo_parser_samples_foreach (dc_parser_t *abstract
static const dc_parser_vtable_t cressi_leonardo_parser_vtable = {
sizeof(cressi_leonardo_parser_t),
DC_FAMILY_CRESSI_LEONARDO,
cressi_leonardo_parser_set_data, /* set_data */
NULL, /* set_clock */
NULL, /* set_atmospheric */
NULL, /* set_density */
@ -59,7 +57,7 @@ static const dc_parser_vtable_t cressi_leonardo_parser_vtable = {
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;
@ -67,7 +65,7 @@ cressi_leonardo_parser_create (dc_parser_t **out, dc_context_t *context, unsigne
return DC_STATUS_INVALIDARGS;
// 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) {
ERROR (context, "Failed to allocate memory.");
return DC_STATUS_NOMEMORY;
@ -81,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
cressi_leonardo_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime)
{
@ -146,6 +137,7 @@ cressi_leonardo_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, u
}
break;
case DC_FIELD_GASMIX:
gasmix->usage = DC_USAGE_NONE;
gasmix->helium = 0.0;
gasmix->oxygen = data[0x19] / 100.0;
gasmix->nitrogen = 1.0 - gasmix->oxygen - gasmix->helium;
@ -196,12 +188,12 @@ cressi_leonardo_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callbac
// Time (seconds).
time += surftime;
sample.time = time;
if (callback) callback (DC_SAMPLE_TIME, sample, userdata);
sample.time = time * 1000;
if (callback) callback (DC_SAMPLE_TIME, &sample, userdata);
// Depth (1/10 m).
sample.depth = 0.0;
if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata);
if (callback) callback (DC_SAMPLE_DEPTH, &sample, userdata);
offset += 4;
} else {
@ -211,17 +203,17 @@ cressi_leonardo_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callbac
// Time (seconds).
time += interval;
sample.time = time;
if (callback) callback (DC_SAMPLE_TIME, sample, userdata);
sample.time = time * 1000;
if (callback) callback (DC_SAMPLE_TIME, &sample, userdata);
// Depth (1/10 m).
sample.depth = depth / 10.0;
if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata);
if (callback) callback (DC_SAMPLE_DEPTH, &sample, userdata);
// Gas change.
if (gasmix != gasmix_previous) {
sample.gasmix = gasmix;
if (callback) callback (DC_SAMPLE_GASMIX, sample, userdata);
if (callback) callback (DC_SAMPLE_GASMIX, &sample, userdata);
gasmix_previous = gasmix;
}
@ -231,7 +223,7 @@ cressi_leonardo_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callbac
sample.event.time = 0;
sample.event.flags = 0;
sample.event.value = ascent;
if (callback) callback (DC_SAMPLE_EVENT, sample, userdata);
if (callback) callback (DC_SAMPLE_EVENT, &sample, userdata);
}
offset += 2;

View File

@ -36,7 +36,7 @@ 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);
deepblu_cosmiq_parser_create (dc_parser_t **parser, dc_context_t *context, const unsigned char data[], size_t size);
#ifdef __cplusplus
}

View File

@ -41,7 +41,6 @@ typedef struct deepblu_cosmiq_parser_t {
double hydrostatic;
} deepblu_cosmiq_parser_t;
static dc_status_t deepblu_cosmiq_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size);
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);
@ -50,7 +49,6 @@ static dc_status_t deepblu_cosmiq_parser_samples_foreach (dc_parser_t *abstract,
static const dc_parser_vtable_t deepblu_cosmiq_parser_vtable = {
sizeof(deepblu_cosmiq_parser_t),
DC_FAMILY_DEEPBLU_COSMIQ,
deepblu_cosmiq_parser_set_data, /* set_data */
NULL, /* set_clock */
NULL, /* set_atmospheric */
deepblu_cosmiq_parser_set_density, /* set_density */
@ -61,7 +59,7 @@ static const dc_parser_vtable_t deepblu_cosmiq_parser_vtable = {
};
dc_status_t
deepblu_cosmiq_parser_create (dc_parser_t **out, dc_context_t *context)
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;
@ -69,7 +67,7 @@ deepblu_cosmiq_parser_create (dc_parser_t **out, dc_context_t *context)
return DC_STATUS_INVALIDARGS;
// Allocate memory.
parser = (deepblu_cosmiq_parser_t *) dc_parser_allocate (context, &deepblu_cosmiq_parser_vtable);
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;
@ -83,12 +81,6 @@ deepblu_cosmiq_parser_create (dc_parser_t **out, dc_context_t *context)
return DC_STATUS_SUCCESS;
}
static dc_status_t
deepblu_cosmiq_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size)
{
return DC_STATUS_SUCCESS;
}
static dc_status_t
deepblu_cosmiq_parser_set_density (dc_parser_t *abstract, double density)
{
@ -151,6 +143,7 @@ deepblu_cosmiq_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, un
*((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;
@ -204,14 +197,14 @@ deepblu_cosmiq_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback
offset += SZ_SAMPLE;
time += interval;
sample.time = time;
if (callback) callback (DC_SAMPLE_TIME, sample, userdata);
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);
if (callback) callback (DC_SAMPLE_DEPTH, &sample, userdata);
sample.temperature = temperature / 10.0;
if (callback) callback (DC_SAMPLE_TEMPERATURE, sample, userdata);
if (callback) callback (DC_SAMPLE_TEMPERATURE, &sample, userdata);
}
return DC_STATUS_SUCCESS;

View File

@ -35,7 +35,7 @@ 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);
deepsix_excursion_parser_create (dc_parser_t **parser, dc_context_t *context, const unsigned char data[], size_t size);
#ifdef __cplusplus
}

View File

@ -112,7 +112,6 @@ typedef struct deepsix_excursion_parser_t {
deepsix_excursion_gasmix_t gasmix[MAX_GASMIXES];
} deepsix_excursion_parser_t;
static dc_status_t deepsix_excursion_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size);
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);
@ -122,7 +121,6 @@ static dc_status_t deepsix_excursion_parser_samples_foreach_v1 (dc_parser_t *abs
static const dc_parser_vtable_t deepsix_parser_vtable = {
sizeof(deepsix_excursion_parser_t),
DC_FAMILY_DEEPSIX_EXCURSION,
deepsix_excursion_parser_set_data, /* set_data */
NULL, /* set_clock */
NULL, /* set_atmospheric */
NULL, /* set_density */
@ -185,7 +183,7 @@ deepsix_excursion_find_gasmix(deepsix_excursion_parser_t *parser, unsigned int o
}
dc_status_t
deepsix_excursion_parser_create (dc_parser_t **out, dc_context_t *context)
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;
@ -193,7 +191,7 @@ deepsix_excursion_parser_create (dc_parser_t **out, dc_context_t *context)
return DC_STATUS_INVALIDARGS;
// Allocate memory.
parser = (deepsix_excursion_parser_t *) dc_parser_allocate (context, &deepsix_parser_vtable);
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;
@ -213,23 +211,6 @@ deepsix_excursion_parser_create (dc_parser_t **out, dc_context_t *context)
return DC_STATUS_SUCCESS;
}
static dc_status_t
deepsix_excursion_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size)
{
deepsix_excursion_parser_t *parser = (deepsix_excursion_parser_t *) abstract;
// Reset the cache.
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;
}
return DC_STATUS_SUCCESS;
}
static dc_status_t
deepsix_excursion_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime)
{
@ -322,6 +303,7 @@ deepsix_excursion_parser_get_field (dc_parser_t *abstract, dc_field_type_t type,
*((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;
@ -423,11 +405,11 @@ deepsix_excursion_parser_samples_foreach_v0 (dc_parser_t *abstract, dc_sample_ca
if (type == TEMPERATURE) {
time += interval;
sample.time = time;
if (callback) callback(DC_SAMPLE_TIME, sample, userdata);
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 (callback) callback(DC_SAMPLE_DEPTH, &sample, userdata);
}
if (type == ALARM) {
@ -440,11 +422,11 @@ deepsix_excursion_parser_samples_foreach_v0 (dc_parser_t *abstract, dc_sample_ca
length = 8;
} else if (temperature >= 10) {
sample.temperature = temperature / 10.0;
if (callback) callback(DC_SAMPLE_TEMPERATURE, sample, userdata);
if (callback) callback(DC_SAMPLE_TEMPERATURE, &sample, userdata);
}
} else {
sample.temperature = temperature / 10.0;
if (callback) callback(DC_SAMPLE_TEMPERATURE, sample, userdata);
if (callback) callback(DC_SAMPLE_TEMPERATURE, &sample, userdata);
}
} else if (type == DECO) {
unsigned int deco = array_uint16_le(data + offset + 4);
@ -454,7 +436,7 @@ deepsix_excursion_parser_samples_foreach_v0 (dc_parser_t *abstract, dc_sample_ca
} 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);
if (callback) callback(DC_SAMPLE_CNS, &sample, userdata);
}
offset += length;
@ -591,12 +573,12 @@ deepsix_excursion_parser_samples_foreach_v1 (dc_parser_t *abstract, dc_sample_ca
// Time (seconds).
time += samplerate;
sample.time = time;
if (callback) callback (DC_SAMPLE_TIME, sample, userdata);
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);
if (callback) callback (DC_SAMPLE_DEPTH, &sample, userdata);
offset += 2;
// event info
@ -664,7 +646,7 @@ deepsix_excursion_parser_samples_foreach_v1 (dc_parser_t *abstract, dc_sample_ca
break;
}
if (sample.event.type != SAMPLE_EVENT_NONE) {
if (callback) callback (DC_SAMPLE_EVENT, sample, userdata);
if (callback) callback (DC_SAMPLE_EVENT, &sample, userdata);
}
}
break;
@ -686,7 +668,7 @@ deepsix_excursion_parser_samples_foreach_v1 (dc_parser_t *abstract, dc_sample_ca
}
sample.gasmix = mix_idx;
if (callback) callback(DC_SAMPLE_GASMIX, sample, userdata);
if (callback) callback(DC_SAMPLE_GASMIX, &sample, userdata);
break;
case EVENT_SAMPLES_MISSED:
count = array_uint16_le(data + offset + event_offset);
@ -727,12 +709,12 @@ deepsix_excursion_parser_samples_foreach_v1 (dc_parser_t *abstract, dc_sample_ca
case SAMPLE_TEMPERATURE:
value = array_uint16_le(data + offset);
sample.temperature = value / 10.0;
if (callback) callback(DC_SAMPLE_TEMPERATURE, sample, userdata);
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);
if (callback) callback (DC_SAMPLE_CNS, &sample, userdata);
break;
case SAMPLE_DECO_NDL:
deco_flags = data[offset];
@ -752,7 +734,7 @@ deepsix_excursion_parser_samples_foreach_v1 (dc_parser_t *abstract, dc_sample_ca
sample.deco.depth = 0;
sample.deco.time = deco_ndl_tts;
}
if (callback) callback (DC_SAMPLE_DECO, sample, userdata);
if (callback) callback (DC_SAMPLE_DECO, &sample, userdata);
break;
default:
break;

View File

@ -23,7 +23,10 @@
#include <stdlib.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 "platform.h"
@ -36,36 +39,26 @@
values, \
C_ARRAY_SIZE(values) - isnullterminated, \
C_ARRAY_ITEMSIZE(values), \
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))
match)
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_suunto (dc_transport_t transport, const void *userdata, void *params);
static int dc_filter_shearwater (dc_transport_t transport, const void *userdata, void *params);
static int dc_filter_hw (dc_transport_t transport, const void *userdata, void *params);
static int dc_filter_tecdiving (dc_transport_t transport, const void *userdata, void *params);
static int dc_filter_mares (dc_transport_t transport, const void *userdata, void *params);
static int dc_filter_divesystem (dc_transport_t transport, const void *userdata, void *params);
static int dc_filter_oceanic (dc_transport_t transport, const void *userdata, void *params);
static int dc_filter_mclean (dc_transport_t transport, const void *userdata, void *params);
static int dc_filter_atomic (dc_transport_t transport, const void *userdata, void *params);
static int dc_filter_deepsix (dc_transport_t transport, const void *userdata, void *params);
static int dc_filter_deepblu (dc_transport_t transport, const void *userdata, void *params);
static int dc_filter_oceans (dc_transport_t transport, const void *userdata, void *params);
static int dc_filter_divesoft (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_descriptor_t *descriptor, dc_transport_t transport, const void *userdata);
static int dc_filter_shearwater (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata);
static int dc_filter_hw (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata);
static int dc_filter_tecdiving (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata);
static int dc_filter_mares (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata);
static int dc_filter_divesystem (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata);
static int dc_filter_oceanic (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata);
static int dc_filter_mclean (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata);
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);
@ -177,7 +170,10 @@ static const dc_descriptor_t g_descriptors[] = {
{"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 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", "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", "Sensus", DC_FAMILY_REEFNET_SENSUS, 1, DC_TRANSPORT_SERIAL, NULL},
{"Reefnet", "Sensus Pro", DC_FAMILY_REEFNET_SENSUSPRO, 2, DC_TRANSPORT_SERIAL, NULL},
@ -276,6 +272,9 @@ static const dc_descriptor_t g_descriptors[] = {
{"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", DC_FAMILY_MARES_NEMO, 0, DC_TRANSPORT_SERIAL, NULL},
{"Mares", "Nemo Steel", DC_FAMILY_MARES_NEMO, 0, DC_TRANSPORT_SERIAL, NULL},
@ -308,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", "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", "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", "OSTC", DC_FAMILY_HW_OSTC, 0, DC_TRANSPORT_SERIAL, NULL},
{"Heinrichs Weikamp", "OSTC Mk2", DC_FAMILY_HW_OSTC, 1, DC_TRANSPORT_SERIAL, NULL},
@ -361,6 +364,7 @@ static const dc_descriptor_t g_descriptors[] = {
{"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", DC_FAMILY_DIVERITE_NITEKQ, 0, DC_TRANSPORT_SERIAL, NULL},
/* Citizen Hyper Aqualand */
@ -414,6 +418,20 @@ 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 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", "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},
@ -443,7 +461,6 @@ static const dc_descriptor_t g_descriptors[] = {
{"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},
{"Tusa", "TC1", 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},
@ -493,6 +510,15 @@ dc_match_usb (const void *key, const void *value)
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
dc_match_number_with_prefix (const void *key, const void *value)
{
@ -533,16 +559,13 @@ dc_match_oceanic (const void *key, const void *value)
}
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)
return 1;
for (size_t i = 0; i < count; ++i) {
if (match (key, (const unsigned char *) values + i * size)) {
if (params_src && params_dst) {
memcpy (params_dst, params_src, params_size);
}
return 1;
}
}
@ -557,7 +580,8 @@ static const char * const rfcomm[] = {
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[] = {
"Aladin Smart Com",
@ -568,7 +592,7 @@ static int dc_filter_uwatec (dc_transport_t transport, const void *userdata, voi
"UWATEC Galileo",
"UWATEC Galileo Sol",
};
static const dc_usb_desc_t usbhid[] = {
static const dc_usbhid_desc_t usbhid[] = {
{0x2e6c, 0x3201}, // G2, G2 TEK
{0x2e6c, 0x3211}, // G2 Console
{0x2e6c, 0x4201}, // G2 HUD
@ -581,12 +605,15 @@ static int dc_filter_uwatec (dc_transport_t transport, const void *userdata, voi
"A1",
"A2",
"G2 TEK",
"Galileo 3",
"Luna 2.0 AI",
"Luna 2.0",
};
if (transport == DC_TRANSPORT_IRDA) {
return DC_FILTER_INTERNAL (userdata, irda, 0, dc_match_name);
} 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) {
return DC_FILTER_INTERNAL (userdata, bluetooth, 0, dc_match_name);
}
@ -594,9 +621,10 @@ static int dc_filter_uwatec (dc_transport_t transport, const void *userdata, voi
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, 0x0033}, // Eon Core
{0x1493, 0x0035}, // D5
@ -610,7 +638,7 @@ static int dc_filter_suunto (dc_transport_t transport, const void *userdata, voi
};
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) {
return DC_FILTER_INTERNAL (userdata, bluetooth, 0, dc_match_prefix);
}
@ -618,7 +646,8 @@ static int dc_filter_suunto (dc_transport_t transport, const void *userdata, voi
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[] = {
"OSTC",
@ -634,7 +663,8 @@ static int dc_filter_hw (dc_transport_t transport, const void *userdata, void *p
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[] = {
"Predator",
@ -646,6 +676,7 @@ static int dc_filter_shearwater (dc_transport_t transport, const void *userdata,
"Perdix 2",
"Teric",
"Peregrine",
"Tern"
};
if (transport == DC_TRANSPORT_BLUETOOTH || transport == DC_TRANSPORT_BLE) {
@ -657,7 +688,8 @@ static int dc_filter_shearwater (dc_transport_t transport, const void *userdata,
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[] = {
"DiveComputer",
@ -672,11 +704,15 @@ static int dc_filter_tecdiving (dc_transport_t transport, const void *userdata,
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[] = {
"Mares bluelink pro",
"Mares Genius",
"Sirius",
"Quad Ci",
"Puck4",
};
if (transport == DC_TRANSPORT_BLE) {
@ -686,11 +722,13 @@ static int dc_filter_mares (dc_transport_t transport, const void *userdata, void
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[] = {
"DS",
"IX5M",
"RATIO-",
};
if (transport == DC_TRANSPORT_BLUETOOTH || transport == DC_TRANSPORT_BLE) {
@ -700,7 +738,8 @@ static int dc_filter_divesystem (dc_transport_t transport, const void *userdata,
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[] = {
0x4552, // Oceanic Pro Plus X
@ -714,8 +753,10 @@ static int dc_filter_oceanic (dc_transport_t transport, const void *userdata, vo
0x4654, // Oceanic Veo 4.0
0x4655, // Sherwood Wisdom 4
0x4656, // Oceanic Pro Plus 4
0x4741, // Apeks DSX
0x4742, // Sherwood Beacon
0x4743, // Aqualung i470TC
0x4744, // Aqualung i330R
0x4749, // Aqualung i200C
0x474B, // Oceanic Geo Air
};
@ -727,7 +768,8 @@ static int dc_filter_oceanic (dc_transport_t transport, const void *userdata, vo
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[] = {
"McLean Extreme",
@ -742,30 +784,27 @@ static int dc_filter_mclean(dc_transport_t transport, const void *userdata, void
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[] = {
{0x0471, 0x0888}, // Atomic Aquatics Cobalt
};
static const dc_usb_params_t usb_params = {
0, 0x82, 0x02
};
if (transport == DC_TRANSPORT_USB) {
return DC_FILTER_INTERNAL_WITH_PARAMS (userdata, usb, 0, dc_match_usb, params, &usb_params);
return DC_FILTER_INTERNAL (userdata, usb, 0, dc_match_usb);
}
return 1;
}
static int dc_filter_deepsix (dc_transport_t transport, const void *userdata, void *params)
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",
"TC1",
"ALPHA",
};
@ -776,7 +815,8 @@ static int dc_filter_deepsix (dc_transport_t transport, const void *userdata, vo
return 1;
}
static int dc_filter_deepblu (dc_transport_t transport, const void *userdata, void *params)
static int
dc_filter_deepblu (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata)
{
static const char * const bluetooth[] = {
"COSMIQ",
@ -789,7 +829,8 @@ static int dc_filter_deepblu (dc_transport_t transport, const void *userdata, vo
return 1;
}
static int dc_filter_oceans (dc_transport_t transport, const void *userdata, void *params)
static int
dc_filter_oceans (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata)
{
static const char * const bluetooth[] = {
"S1",
@ -802,7 +843,8 @@ static int dc_filter_oceans (dc_transport_t transport, const void *userdata, voi
return 1;
}
static int dc_filter_divesoft (dc_transport_t transport, const void *userdata, void *params)
static int
dc_filter_divesoft (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata)
{
static const char * const bluetooth[] = {
"Freedom",
@ -908,10 +950,10 @@ dc_descriptor_get_transports (dc_descriptor_t *descriptor)
}
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)
return 1;
return descriptor->filter (transport, userdata, params);
return descriptor->filter (descriptor, transport, userdata);
}

View File

@ -38,6 +38,7 @@
#include "oceanic_atom2.h"
#include "oceanic_veo250.h"
#include "oceanic_vtpro.h"
#include "pelagic_i330r.h"
#include "mares_darwin.h"
#include "mares_iconhd.h"
#include "mares_nemo.h"
@ -162,6 +163,9 @@ dc_device_open (dc_device_t **out, dc_context_t *context, dc_descriptor_t *descr
case DC_FAMILY_OCEANIC_ATOM2:
rc = oceanic_atom2_device_open (&device, context, iostream, dc_descriptor_get_model (descriptor));
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:
rc = mares_nemo_device_open (&device, context, iostream);
break;
@ -172,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));
break;
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;
case DC_FAMILY_HW_OSTC:
rc = hw_ostc_device_open (&device, context, iostream);

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);
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
}

View File

@ -49,7 +49,6 @@ struct diverite_nitekq_parser_t {
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_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);
@ -57,7 +56,6 @@ static dc_status_t diverite_nitekq_parser_samples_foreach (dc_parser_t *abstract
static const dc_parser_vtable_t diverite_nitekq_parser_vtable = {
sizeof(diverite_nitekq_parser_t),
DC_FAMILY_DIVERITE_NITEKQ,
diverite_nitekq_parser_set_data, /* set_data */
NULL, /* set_clock */
NULL, /* set_atmospheric */
NULL, /* set_density */
@ -69,7 +67,7 @@ static const dc_parser_vtable_t diverite_nitekq_parser_vtable = {
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;
@ -77,7 +75,7 @@ diverite_nitekq_parser_create (dc_parser_t **out, dc_context_t *context)
return DC_STATUS_INVALIDARGS;
// 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) {
ERROR (context, "Failed to allocate memory.");
return DC_STATUS_NOMEMORY;
@ -101,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
diverite_nitekq_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime)
{
@ -161,6 +152,7 @@ diverite_nitekq_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, u
*((unsigned int *) value) = parser->ngasmixes;
break;
case DC_FIELD_GASMIX:
gasmix->usage = DC_USAGE_NONE;
gasmix->helium = parser->he[flags] / 100.0;
gasmix->oxygen = parser->o2[flags] / 100.0;
gasmix->nitrogen = 1.0 - gasmix->oxygen - gasmix->helium;
@ -264,13 +256,13 @@ diverite_nitekq_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callbac
// Time (seconds).
time += interval;
sample.time = time;
if (callback) callback (DC_SAMPLE_TIME, sample, userdata);
sample.time = time * 1000;
if (callback) callback (DC_SAMPLE_TIME, &sample, userdata);
// Gas change
if (gasmix != gasmix_previous) {
sample.gasmix = gasmix;
if (callback) callback (DC_SAMPLE_GASMIX, sample, userdata);
if (callback) callback (DC_SAMPLE_GASMIX, &sample, userdata);
gasmix_previous = gasmix;
}
@ -282,7 +274,7 @@ diverite_nitekq_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callbac
sample.depth = depth / 10.0;
else
sample.depth = depth * FEET / 10.0;
if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata);
if (callback) callback (DC_SAMPLE_DEPTH, &sample, userdata);
offset += 2;
if (type == 3) {
@ -293,8 +285,9 @@ diverite_nitekq_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callbac
if (offset + 1 > size)
return DC_STATUS_DATAFORMAT;
unsigned int ppo2 = data[offset];
sample.ppo2 = ppo2 / 100.0;
if (callback) callback (DC_SAMPLE_PPO2, sample, userdata);
sample.ppo2.sensor = DC_SENSOR_NONE;
sample.ppo2.value = ppo2 / 100.0;
if (callback) callback (DC_SAMPLE_PPO2, &sample, userdata);
offset++;
}
} else {

View File

@ -35,7 +35,7 @@ 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);
divesoft_freedom_parser_create (dc_parser_t **parser, dc_context_t *context, const unsigned char data[], size_t size);
#ifdef __cplusplus
}

View File

@ -254,7 +254,6 @@ typedef struct divesoft_freedom_parser_t {
unsigned int calibrated;
} divesoft_freedom_parser_t;
static dc_status_t divesoft_freedom_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size);
static dc_status_t divesoft_freedom_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime);
static dc_status_t divesoft_freedom_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value);
static dc_status_t divesoft_freedom_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata);
@ -262,7 +261,6 @@ static dc_status_t divesoft_freedom_parser_samples_foreach (dc_parser_t *abstrac
static const dc_parser_vtable_t divesoft_freedom_parser_vtable = {
sizeof(divesoft_freedom_parser_t),
DC_FAMILY_DIVESOFT_FREEDOM,
divesoft_freedom_parser_set_data, /* set_data */
NULL, /* set_clock */
NULL, /* set_atmospheric */
NULL, /* set_density */
@ -643,7 +641,7 @@ divesoft_freedom_cache (divesoft_freedom_parser_t *parser)
}
dc_status_t
divesoft_freedom_parser_create (dc_parser_t **out, dc_context_t *context)
divesoft_freedom_parser_create (dc_parser_t **out, dc_context_t *context, const unsigned char data[], size_t size)
{
divesoft_freedom_parser_t *parser = NULL;
@ -651,7 +649,7 @@ divesoft_freedom_parser_create (dc_parser_t **out, dc_context_t *context)
return DC_STATUS_INVALIDARGS;
// Allocate memory.
parser = (divesoft_freedom_parser_t *) dc_parser_allocate (context, &divesoft_freedom_parser_vtable);
parser = (divesoft_freedom_parser_t *) dc_parser_allocate (context, &divesoft_freedom_parser_vtable, data, size);
if (parser == NULL) {
ERROR (context, "Failed to allocate memory.");
return DC_STATUS_NOMEMORY;
@ -698,50 +696,6 @@ divesoft_freedom_parser_create (dc_parser_t **out, dc_context_t *context)
return DC_STATUS_SUCCESS;
}
static dc_status_t
divesoft_freedom_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size)
{
divesoft_freedom_parser_t *parser = (divesoft_freedom_parser_t *) abstract;
// Reset the cache.
parser->cached = 0;
parser->version = 0;
parser->headersize = 0;
parser->divetime = 0;
parser->divemode = 0;
parser->temperature_min = 0;
parser->maxdepth = 0;
parser->atmospheric = 0;
parser->avgdepth = 0;
parser->ngasmixes = 0;
for (unsigned int i = 0; i < NGASMIXES; ++i) {
parser->gasmix[i].oxygen = 0;
parser->gasmix[i].helium = 0;
parser->gasmix[i].type = 0;
parser->gasmix[i].id = 0;
}
parser->diluent = UNDEFINED;
parser->ntanks = 0;
for (unsigned int i = 0; i < NTANKS; ++i) {
parser->tank[i].volume = 0;
parser->tank[i].workpressure = 0;
parser->tank[i].beginpressure = 0;
parser->tank[i].endpressure = 0;
parser->tank[i].transmitter = 0;
parser->tank[i].active = 0;
}
parser->vpm = 0;
parser->gf_lo = 0;
parser->gf_hi = 0;
parser->seawater = 0;
for (unsigned int i = 0; i < NSENSORS; ++i) {
parser->calibration[i] = 0;
}
parser->calibrated = 0;
return DC_STATUS_SUCCESS;
}
static dc_status_t
divesoft_freedom_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime)
{
@ -755,13 +709,21 @@ divesoft_freedom_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *date
return status;
unsigned int timestamp = array_uint32_le (data + 8);
dc_ticks_t ticks = (dc_ticks_t) timestamp + EPOCH;
int timezone = 0;
if (parser->version == HEADER_SIGNATURE_V2) {
timezone = ((signed short) array_uint16_le (data + 40)) * 60;
} else {
timezone = 0;
}
dc_ticks_t ticks = (dc_ticks_t) timestamp + EPOCH + timezone;
if (!dc_datetime_gmtime (datetime, ticks))
return DC_STATUS_DATAFORMAT;
if (parser->version == HEADER_SIGNATURE_V2) {
datetime->timezone = ((signed short) array_uint16_le (data + 40)) * 60;
datetime->timezone = timezone;
} else {
datetime->timezone = DC_TIMEZONE_NONE;
}
@ -838,6 +800,13 @@ divesoft_freedom_parser_get_field (dc_parser_t *abstract, dc_field_type_t type,
*((unsigned int *) value) = parser->ngasmixes;
break;
case DC_FIELD_GASMIX:
if (parser->gasmix[flags].type == OXYGEN) {
gasmix->usage = DC_USAGE_OXYGEN;
} else if (parser->gasmix[flags].type == DILUENT) {
gasmix->usage = DC_USAGE_DILUENT;
} else {
gasmix->usage = DC_USAGE_NONE;
}
gasmix->helium = parser->gasmix[flags].helium / 100.0;
gasmix->oxygen = parser->gasmix[flags].oxygen / 100.0;
gasmix->nitrogen = 1.0 - gasmix->oxygen - gasmix->helium;
@ -859,6 +828,7 @@ divesoft_freedom_parser_get_field (dc_parser_t *abstract, dc_field_type_t type,
tank->beginpressure = parser->tank[flags].beginpressure * 2.0;
tank->endpressure = parser->tank[flags].endpressure * 2.0;
tank->gasmix = flags;
tank->usage = DC_USAGE_NONE;
break;
case DC_FIELD_DECOMODEL:
if (parser->vpm) {
@ -924,15 +894,15 @@ divesoft_freedom_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callba
continue;
}
time = timestamp;
sample.time = time;
if (callback) callback(DC_SAMPLE_TIME, sample, userdata);
sample.time = time * 1000;
if (callback) callback(DC_SAMPLE_TIME, &sample, userdata);
}
// Initial diluent.
if (!initial) {
if (parser->diluent != UNDEFINED) {
sample.gasmix = parser->diluent;
if (callback) callback(DC_SAMPLE_GASMIX, sample, userdata);
if (callback) callback(DC_SAMPLE_GASMIX, &sample, userdata);
}
initial = 1;
}
@ -943,18 +913,19 @@ divesoft_freedom_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callba
unsigned int ppo2 = array_uint16_le (data + offset + 6);
sample.depth = depth / 100.0;
if (callback) callback(DC_SAMPLE_DEPTH, sample, userdata);
if (callback) callback(DC_SAMPLE_DEPTH, &sample, userdata);
if (ppo2) {
sample.ppo2 = ppo2 * 10.0 / BAR;
if (callback) callback(DC_SAMPLE_PPO2, sample, userdata);
sample.ppo2.sensor = DC_SENSOR_NONE;
sample.ppo2.value = ppo2 * 10.0 / BAR;
if (callback) callback(DC_SAMPLE_PPO2, &sample, userdata);
}
if (id == POINT_2) {
unsigned int orientation = array_uint32_le (data + offset + 8);
unsigned int heading = orientation & 0x1FF;
sample.bearing = heading;
if (callback) callback (DC_SAMPLE_BEARING, sample, userdata);
if (callback) callback (DC_SAMPLE_BEARING, &sample, userdata);
} else if (id == POINT_1 || id == POINT_1_OLD) {
unsigned int misc = array_uint32_le (data + offset + 8);
unsigned int ceiling = array_uint16_le (data + offset + 12);
@ -965,7 +936,7 @@ divesoft_freedom_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callba
// Temperature
sample.temperature = (signed int) signextend (temp, 10) / 10.0;
if (callback) callback(DC_SAMPLE_TEMPERATURE, sample, userdata);
if (callback) callback(DC_SAMPLE_TEMPERATURE, &sample, userdata);
// Deco / NDL
if (ceiling) {
@ -977,12 +948,13 @@ divesoft_freedom_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callba
sample.deco.time = ndl * 60;
sample.deco.depth = 0.0;
}
if (callback) callback(DC_SAMPLE_DECO, sample, userdata);
sample.deco.tts = tts * 60;
if (callback) callback(DC_SAMPLE_DECO, &sample, userdata);
// Setpoint
if (setpoint) {
sample.setpoint = setpoint / 100.0;
if (callback) callback(DC_SAMPLE_SETPOINT, sample, userdata);
if (callback) callback(DC_SAMPLE_SETPOINT, &sample, userdata);
}
}
} else if ((type >= LREC_MANIPULATION && type <= LREC_ACTIVITY) || type == LREC_INFO) {
@ -994,7 +966,7 @@ divesoft_freedom_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callba
sample.event.time = 0;
sample.event.flags = 0;
sample.event.value = 0;
if (callback) callback(DC_SAMPLE_EVENT, sample, userdata);
if (callback) callback(DC_SAMPLE_EVENT, &sample, userdata);
} else if (event == EVENT_MIX_CHANGED || event == EVENT_DILUENT || event == EVENT_CHANGE_MODE) {
unsigned int o2 = data[offset + 6];
unsigned int he = data[offset + 7];
@ -1014,13 +986,13 @@ divesoft_freedom_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callba
return DC_STATUS_DATAFORMAT;
}
sample.gasmix = idx;
if (callback) callback(DC_SAMPLE_GASMIX, sample, userdata);
if (callback) callback(DC_SAMPLE_GASMIX, &sample, userdata);
} else if (event == EVENT_CNS) {
sample.cns = array_uint16_le (data + offset + 6) / 100.0;
if (callback) callback(DC_SAMPLE_CNS, sample, userdata);
if (callback) callback(DC_SAMPLE_CNS, &sample, userdata);
} else if (event == EVENT_SETPOINT_MANUAL || event == EVENT_SETPOINT_AUTO) {
sample.setpoint = data[6] / 100.0;
if (callback) callback(DC_SAMPLE_SETPOINT, sample, userdata);
if (callback) callback(DC_SAMPLE_SETPOINT, &sample, userdata);
}
} else if (type == LREC_MEASURE) {
// Measurement record.
@ -1038,25 +1010,27 @@ divesoft_freedom_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callba
sample.pressure.tank = idx;
sample.pressure.value = pressure * 2.0;
if (callback) callback(DC_SAMPLE_PRESSURE, sample, userdata);
if (callback) callback(DC_SAMPLE_PRESSURE, &sample, userdata);
}
} else if (id == MEASURE_ID_OXYGEN) {
for (unsigned int i = 0; i < NSENSORS; ++i) {
unsigned int ppo2 = array_uint16_le (data + offset + 4 + i * 2);
if (ppo2 == 0 || ppo2 == 0xFFFF)
continue;
sample.ppo2 = ppo2 * 10.0 / BAR;
if (callback) callback(DC_SAMPLE_PPO2, sample, userdata);
sample.ppo2.sensor = i;
sample.ppo2.value = ppo2 * 10.0 / BAR;
if (callback) callback(DC_SAMPLE_PPO2, &sample, userdata);
}
} else if (id == MEASURE_ID_OXYGEN_MV) {
for (unsigned int i = 0; i < NSENSORS; ++i) {
unsigned int value = array_uint16_le (data + offset + 4 + i * 2);
unsigned int state = data[offset + 12 + i];
if (!parser->calibrated || state == SENSTAT_UNCALIBRATED ||
state == SENSTAT_NOT_EXIST)
if (!parser->calibrated || parser->calibration[i] == 0 ||
state == SENSTAT_UNCALIBRATED || state == SENSTAT_NOT_EXIST)
continue;
sample.ppo2 = value / 100.0 * parser->calibration[i] / BAR;
if (callback) callback(DC_SAMPLE_PPO2, sample, userdata);
sample.ppo2.sensor = i;
sample.ppo2.value = value / 100.0 * parser->calibration[i] / BAR;
if (callback) callback(DC_SAMPLE_PPO2, &sample, userdata);
}
}
} else if (type == LREC_STATE) {

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);
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
}

View File

@ -58,6 +58,9 @@
#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_gasmix_t {
@ -89,7 +92,6 @@ struct divesystem_idive_parser_t {
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_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);
@ -97,7 +99,6 @@ static dc_status_t divesystem_idive_parser_samples_foreach (dc_parser_t *abstrac
static const dc_parser_vtable_t divesystem_idive_parser_vtable = {
sizeof(divesystem_idive_parser_t),
DC_FAMILY_DIVESYSTEM_IDIVE,
divesystem_idive_parser_set_data, /* set_data */
NULL, /* set_clock */
NULL, /* set_atmospheric */
NULL, /* set_density */
@ -109,7 +110,7 @@ static const dc_parser_vtable_t divesystem_idive_parser_vtable = {
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;
@ -117,7 +118,7 @@ divesystem_idive_parser_create (dc_parser_t **out, dc_context_t *context, unsign
return DC_STATUS_INVALIDARGS;
// 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) {
ERROR (context, "Failed to allocate memory.");
return DC_STATUS_NOMEMORY;
@ -156,35 +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;
}
parser->algorithm = INVALID;
parser->gf_low = INVALID;
parser->gf_high = INVALID;
return DC_STATUS_SUCCESS;
}
static dc_status_t
divesystem_idive_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime)
{
@ -317,6 +289,7 @@ divesystem_idive_parser_get_field (dc_parser_t *abstract, dc_field_type_t type,
*((unsigned int *) value) = parser->ngasmixes;
break;
case DC_FIELD_GASMIX:
gasmix->usage = DC_USAGE_NONE;
gasmix->helium = parser->gasmix[flags].helium / 100.0;
gasmix->oxygen = parser->gasmix[flags].oxygen / 100.0;
gasmix->nitrogen = 1.0 - gasmix->oxygen - gasmix->helium;
@ -331,6 +304,7 @@ divesystem_idive_parser_get_field (dc_parser_t *abstract, dc_field_type_t type,
tank->beginpressure = parser->tank[flags].beginpressure;
tank->endpressure = parser->tank[flags].endpressure;
tank->gasmix = DC_GASMIX_UNKNOWN;
tank->usage = DC_USAGE_NONE;
break;
case DC_FIELD_ATMOSPHERIC:
if (ISIX3M(parser->model)) {
@ -439,6 +413,7 @@ divesystem_idive_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callba
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 apos4 = 0;
@ -467,27 +442,37 @@ divesystem_idive_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callba
while (offset + samplesize <= size) {
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).
unsigned int timestamp = array_uint32_le (data + offset + 2);
if (timestamp <= time) {
ERROR (abstract->context, "Timestamp moved backwards.");
if (timestamp <= time && time != 0) {
ERROR (abstract->context, "Timestamp moved backwards (%u %u).", timestamp, time);
return DC_STATUS_DATAFORMAT;
}
time = timestamp;
sample.time = timestamp;
if (callback) callback (DC_SAMPLE_TIME, sample, userdata);
sample.time = timestamp * 1000;
if (callback) callback (DC_SAMPLE_TIME, &sample, userdata);
// Depth (1/10 m).
unsigned int depth = array_uint16_le (data + offset + 6);
if (maxdepth < depth)
maxdepth = depth;
sample.depth = depth / 10.0;
if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata);
if (callback) callback (DC_SAMPLE_DEPTH, &sample, userdata);
// Temperature (Celsius).
signed int temperature = (signed short) array_uint16_le (data + offset + 8);
sample.temperature = temperature / 10.0;
if (callback) callback (DC_SAMPLE_TEMPERATURE, sample, userdata);
if (callback) callback (DC_SAMPLE_TEMPERATURE, &sample, userdata);
// Dive mode
unsigned int mode = data[offset + 18];
@ -521,7 +506,7 @@ divesystem_idive_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callba
if (mode == SCR || mode == CCR) {
unsigned int setpoint = array_uint16_le (data + offset + 19);
sample.setpoint = setpoint / 1000.0;
if (callback) callback (DC_SAMPLE_SETPOINT, sample, userdata);
if (callback) callback (DC_SAMPLE_SETPOINT, &sample, userdata);
}
// Gaschange.
@ -548,7 +533,7 @@ divesystem_idive_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callba
}
sample.gasmix = i;
if (callback) callback (DC_SAMPLE_GASMIX, sample, userdata);
if (callback) callback (DC_SAMPLE_GASMIX, &sample, userdata);
o2_previous = o2;
he_previous = he;
}
@ -566,18 +551,20 @@ divesystem_idive_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callba
if (decostop) {
sample.deco.type = DC_DECO_DECOSTOP;
sample.deco.depth = decostop / 10.0;
sample.deco.time = apos4 ? decotime : tts;
sample.deco.time = decotime;
sample.deco.tts = tts;
} else {
sample.deco.type = DC_DECO_NDL;
sample.deco.depth = 0.0;
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
unsigned int cns = array_uint16_le (data + offset + 29);
sample.cns = cns / 100.0;
if (callback) callback (DC_SAMPLE_CNS, sample, userdata);
if (callback) callback (DC_SAMPLE_CNS, &sample, userdata);
// Tank Pressure
if (samplesize == SZ_SAMPLE_IX3M_APOS4) {
@ -598,7 +585,7 @@ divesystem_idive_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callba
sample.event.time = 0;
sample.event.flags = 0;
sample.event.value = 0;
if (callback) callback (DC_SAMPLE_EVENT, sample, userdata);
if (callback) callback (DC_SAMPLE_EVENT, &sample, userdata);
} else {
// Get the index of the tank.
if (id != tank_previous) {
@ -628,10 +615,20 @@ divesystem_idive_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callba
if (tank_idx < ntanks) {
sample.pressure.tank = tank_idx;
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;
}
}
// 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;

View File

@ -40,7 +40,7 @@
#define RB_PROFILE_BEGIN 0x000000
#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 HEADER 0x61

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);
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
}

View File

@ -1715,6 +1715,10 @@ hw_ostc3_device_dump (dc_device_t *abstract, dc_buffer_t *buffer)
return rc;
}
if (device->hardware == OSTC4) {
return DC_STATUS_UNSUPPORTED;
}
// Emit a device info event.
dc_event_devinfo_t devinfo;
devinfo.firmware = device->firmware;

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);
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
}

View File

@ -37,10 +37,6 @@
#define UNDEFINED 0xFFFFFFFF
#define ALL 0
#define FIXED 1
#define MANUAL 2
#define HEADER 1
#define PROFILE 2
@ -108,10 +104,12 @@ typedef struct hw_ostc_layout_t {
} hw_ostc_layout_t;
typedef struct hw_ostc_gasmix_t {
unsigned int id;
unsigned int oxygen;
unsigned int helium;
unsigned int type;
unsigned int enabled;
unsigned int active;
unsigned int diluent;
} hw_ostc_gasmix_t;
@ -126,21 +124,22 @@ typedef struct hw_ostc_parser_t {
const hw_ostc_layout_t *layout;
unsigned int ngasmixes;
unsigned int nfixed;
unsigned int ndisabled;
unsigned int initial;
unsigned int initial_setpoint;
unsigned int initial_cns;
hw_ostc_gasmix_t gasmix[NGASMIXES];
} 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_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_internal_foreach (hw_ostc_parser_t *parser, dc_sample_callback_t callback, void *userdata);
static const dc_parser_vtable_t hw_ostc_parser_vtable = {
sizeof(hw_ostc_parser_t),
DC_FAMILY_HW_OSTC,
hw_ostc_parser_set_data, /* set_data */
NULL, /* set_clock */
NULL, /* set_atmospheric */
NULL, /* set_density */
@ -196,15 +195,10 @@ static const hw_ostc_layout_t hw_ostc_layout_ostc3 = {
};
static unsigned int
hw_ostc_find_gasmix (hw_ostc_parser_t *parser, unsigned int o2, unsigned int he, unsigned int dil, 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;
if (type == FIXED) {
count = parser->nfixed;
} else if (type == MANUAL) {
offset = parser->nfixed;
}
unsigned int i = offset;
while (i < count) {
@ -216,6 +210,22 @@ hw_ostc_find_gasmix (hw_ostc_parser_t *parser, unsigned int o2, unsigned int he,
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)
{
@ -297,19 +307,23 @@ hw_ostc_parser_cache (hw_ostc_parser_t *parser)
initial = data[31];
}
for (unsigned int i = 0; i < ngasmixes; ++i) {
gasmix[i].id = i + 1;
gasmix[i].oxygen = data[25 + 2 * i];
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) {
ngasmixes = 5;
for (unsigned int i = 0; i < ngasmixes; ++i) {
gasmix[i].id = i + 1;
gasmix[i].oxygen = data[28 + 4 * i + 0];
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.
if (initial == UNDEFINED && data[28 + 4 * i + 3] == 1) {
@ -328,6 +342,7 @@ hw_ostc_parser_cache (hw_ostc_parser_t *parser)
initial = data[31];
}
for (unsigned int i = 0; i < ngasmixes; ++i) {
gasmix[i].id = i + 1;
gasmix[i].oxygen = data[19 + 2 * i + 0];
gasmix[i].helium = data[19 + 2 * i + 1];
gasmix[i].type = 0;
@ -336,6 +351,7 @@ hw_ostc_parser_cache (hw_ostc_parser_t *parser)
} else {
gasmix[i].enabled = 1;
}
gasmix[i].active = 0;
gasmix[i].diluent = ccr;
}
}
@ -344,7 +360,6 @@ hw_ostc_parser_cache (hw_ostc_parser_t *parser)
ERROR(abstract->context, "Invalid initial gas mix.");
return DC_STATUS_DATAFORMAT;
}
initial--; /* Convert to a zero based index. */
} else {
WARNING(abstract->context, "No initial gas mix available.");
}
@ -355,6 +370,7 @@ hw_ostc_parser_cache (hw_ostc_parser_t *parser)
parser->layout = layout;
parser->ngasmixes = ngasmixes;
parser->nfixed = ngasmixes;
parser->ndisabled = 0;
parser->initial = initial;
parser->initial_setpoint = initial_setpoint;
parser->initial_cns = initial_cns;
@ -367,7 +383,7 @@ hw_ostc_parser_cache (hw_ostc_parser_t *parser)
}
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;
@ -375,7 +391,7 @@ hw_ostc_parser_create_internal (dc_parser_t **out, dc_context_t *context, unsign
return DC_STATUS_INVALIDARGS;
// 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) {
ERROR (context, "Failed to allocate memory.");
return DC_STATUS_NOMEMORY;
@ -390,14 +406,17 @@ hw_ostc_parser_create_internal (dc_parser_t **out, dc_context_t *context, unsign
parser->layout = NULL;
parser->ngasmixes = 0;
parser->nfixed = 0;
parser->ndisabled = 0;
parser->initial = 0;
parser->initial_setpoint = 0;
parser->initial_cns = 0;
for (unsigned int i = 0; i < NGASMIXES; ++i) {
parser->gasmix[i].id = 0;
parser->gasmix[i].oxygen = 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;
}
@ -408,44 +427,17 @@ hw_ostc_parser_create_internal (dc_parser_t **out, dc_context_t *context, unsign
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
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;
parser->gasmix[i].type = 0;
parser->gasmix[i].enabled = 0;
parser->gasmix[i].diluent = 0;
}
return DC_STATUS_SUCCESS;
}
static dc_status_t
hw_ostc_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime)
{
@ -519,7 +511,7 @@ hw_ostc_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned
// Cache the profile data.
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)
return rc;
}
@ -552,6 +544,8 @@ hw_ostc_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned
*((unsigned int *) value) = parser->ngasmixes;
break;
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->helium = parser->gasmix[flags].helium / 100.0;
gasmix->nitrogen = 1.0 - gasmix->oxygen - gasmix->helium;
@ -705,17 +699,12 @@ hw_ostc_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned
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;
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 header = parser->header;
const hw_ostc_layout_t *layout = parser->layout;
@ -821,7 +810,7 @@ hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t call
unsigned int time = 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;
if (version == 0x23 || version == 0x24)
@ -833,31 +822,33 @@ hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t call
// Time (seconds).
time += samplerate;
sample.time = time;
if (callback) callback (DC_SAMPLE_TIME, sample, userdata);
sample.time = time * 1000;
if (callback) callback (DC_SAMPLE_TIME, &sample, userdata);
// Initial gas mix.
if (time == samplerate && parser->initial != UNDEFINED) {
sample.gasmix = parser->initial;
if (callback) callback (DC_SAMPLE_GASMIX, sample, userdata);
unsigned int idx = hw_ostc_find_gasmix_fixed (parser, parser->initial);
parser->gasmix[idx].active = 1;
sample.gasmix = idx;
if (callback) callback (DC_SAMPLE_GASMIX, &sample, userdata);
}
// Initial setpoint (mbar).
if (time == samplerate && parser->initial_setpoint != UNDEFINED) {
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 (%).
if (time == samplerate && parser->initial_cns != UNDEFINED) {
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).
unsigned int depth = array_uint16_le (data + offset);
sample.depth = depth / 100.0;
if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata);
if (callback) callback (DC_SAMPLE_DEPTH, &sample, userdata);
offset += 2;
// Extended sample info.
@ -916,7 +907,7 @@ hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t call
break;
}
if (sample.event.type && callback)
callback (DC_SAMPLE_EVENT, sample, userdata);
callback (DC_SAMPLE_EVENT, &sample, userdata);
// Manual Gas Set & Change
if (events & 0x10) {
@ -926,22 +917,24 @@ hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t call
}
unsigned int o2 = data[offset];
unsigned int he = data[offset + 1];
unsigned int idx = hw_ostc_find_gasmix (parser, o2, he, ccr, MANUAL);
unsigned int idx = hw_ostc_find_gasmix_manual (parser, o2, he, ccr);
if (idx >= parser->ngasmixes) {
if (idx >= NGASMIXES) {
ERROR (abstract->context, "Maximum number of gas mixes reached.");
return DC_STATUS_NOMEMORY;
}
parser->gasmix[idx].id = 0;
parser->gasmix[idx].oxygen = o2;
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;
}
sample.gasmix = idx;
if (callback) callback (DC_SAMPLE_GASMIX, sample, userdata);
if (callback) callback (DC_SAMPLE_GASMIX, &sample, userdata);
offset += 2;
length -= 2;
}
@ -952,19 +945,20 @@ hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t call
ERROR (abstract->context, "Buffer overflow detected!");
return DC_STATUS_DATAFORMAT;
}
unsigned int idx = data[offset];
if (parser->model == OSTC4 && ccr && idx > parser->nfixed) {
unsigned int id = data[offset];
if (parser->model == OSTC4 && ccr && id > parser->nfixed) {
// Fix the OSTC4 diluent index.
idx -= parser->nfixed;
id -= parser->nfixed;
}
if (idx < 1 || idx > parser->nfixed) {
ERROR(abstract->context, "Invalid gas mix (%u).", idx);
if (id < 1 || id > parser->nfixed) {
ERROR(abstract->context, "Invalid gas mix (%u).", id);
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;
if (callback) callback (DC_SAMPLE_GASMIX, sample, userdata);
tank = idx;
if (callback) callback (DC_SAMPLE_GASMIX, &sample, userdata);
tank = id - 1;
offset++;
length--;
}
@ -977,7 +971,7 @@ hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t call
return DC_STATUS_DATAFORMAT;
}
sample.setpoint = data[offset] / 100.0;
if (callback) callback (DC_SAMPLE_SETPOINT, sample, userdata);
if (callback) callback (DC_SAMPLE_SETPOINT, &sample, userdata);
offset++;
length--;
}
@ -991,22 +985,24 @@ hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t call
unsigned int o2 = data[offset];
unsigned int he = data[offset + 1];
unsigned int idx = hw_ostc_find_gasmix (parser, o2, he, 0, MANUAL);
unsigned int idx = hw_ostc_find_gasmix_manual (parser, o2, he, 0);
if (idx >= parser->ngasmixes) {
if (idx >= NGASMIXES) {
ERROR (abstract->context, "Maximum number of gas mixes reached.");
return DC_STATUS_NOMEMORY;
}
parser->gasmix[idx].id = 0;
parser->gasmix[idx].oxygen = o2;
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;
}
sample.gasmix = idx;
if (callback) callback (DC_SAMPLE_GASMIX, sample, userdata);
if (callback) callback (DC_SAMPLE_GASMIX, &sample, userdata);
offset += 2;
length -= 2;
}
@ -1038,7 +1034,7 @@ hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t call
case TEMPERATURE:
value = array_uint16_le (data + offset);
sample.temperature = value / 10.0;
if (callback) callback (DC_SAMPLE_TEMPERATURE, sample, userdata);
if (callback) callback (DC_SAMPLE_TEMPERATURE, &sample, userdata);
break;
case DECO:
// Due to a firmware bug, the deco/ndl info is incorrect for
@ -1053,7 +1049,8 @@ hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t call
sample.deco.depth = 0.0;
}
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;
case PPO2:
for (unsigned int j = 0; j < 3; ++j) {
@ -1067,8 +1064,9 @@ hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t call
}
if (count) {
for (unsigned int j = 0; j < 3; ++j) {
sample.ppo2 = ppo2[j] / 100.0;
if (callback) callback (DC_SAMPLE_PPO2, sample, userdata);
sample.ppo2.sensor = i;
sample.ppo2.value = ppo2[j] / 100.0;
if (callback) callback (DC_SAMPLE_PPO2, &sample, userdata);
}
}
break;
@ -1077,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;
else
sample.cns = data[offset] / 100.0;
if (callback) callback (DC_SAMPLE_CNS, sample, userdata);
if (callback) callback (DC_SAMPLE_CNS, &sample, userdata);
break;
case TANK:
value = array_uint16_le (data + offset);
@ -1090,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))) {
sample.pressure.value /= 10.0;
}
if (callback) callback (DC_SAMPLE_PRESSURE, sample, userdata);
if (callback) callback (DC_SAMPLE_PRESSURE, &sample, userdata);
}
break;
default: // Not yet used.
@ -1110,7 +1108,7 @@ hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t call
return DC_STATUS_DATAFORMAT;
}
sample.setpoint = data[offset] / 100.0;
if (callback) callback (DC_SAMPLE_SETPOINT, sample, userdata);
if (callback) callback (DC_SAMPLE_SETPOINT, &sample, userdata);
offset++;
length--;
}
@ -1124,22 +1122,24 @@ hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t call
unsigned int o2 = data[offset];
unsigned int he = data[offset + 1];
unsigned int idx = hw_ostc_find_gasmix (parser, o2, he, 0, MANUAL);
unsigned int idx = hw_ostc_find_gasmix_manual (parser, o2, he, 0);
if (idx >= parser->ngasmixes) {
if (idx >= NGASMIXES) {
ERROR (abstract->context, "Maximum number of gas mixes reached.");
return DC_STATUS_NOMEMORY;
}
parser->gasmix[idx].id = 0;
parser->gasmix[idx].oxygen = o2;
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;
}
sample.gasmix = idx;
if (callback) callback (DC_SAMPLE_GASMIX, sample, userdata);
if (callback) callback (DC_SAMPLE_GASMIX, &sample, userdata);
offset += 2;
length -= 2;
}
@ -1157,7 +1157,50 @@ hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t call
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;
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

@ -47,7 +47,6 @@
#include "context-private.h"
#include "iostream-private.h"
#include "iterator-private.h"
#include "descriptor-private.h"
#include "array.h"
#include "platform.h"
@ -226,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",
address, name, charset, hints);
if (!dc_descriptor_filter (descriptor, DC_TRANSPORT_IRDA, name, NULL)) {
if (!dc_descriptor_filter (descriptor, DC_TRANSPORT_IRDA, name)) {
continue;
}

View File

@ -34,6 +34,7 @@ dc_descriptor_get_product
dc_descriptor_get_type
dc_descriptor_get_model
dc_descriptor_get_transports
dc_descriptor_filter
dc_iostream_get_transport
dc_iostream_set_timeout
@ -91,17 +92,11 @@ dc_parser_set_clock
dc_parser_set_atmospheric
dc_parser_set_density
dc_parser_get_type
dc_parser_set_data
dc_parser_get_datetime
dc_parser_get_field
dc_parser_samples_foreach
dc_parser_destroy
reefnet_sensus_parser_set_calibration
reefnet_sensuspro_parser_set_calibration
reefnet_sensusultra_parser_set_calibration
atomics_cobalt_parser_set_calibration
dc_device_open
dc_device_close
dc_device_dump

View File

@ -67,12 +67,12 @@
#define RB_LOGBOOK_BEGIN (1 * PAGESIZE)
#define RB_LOGBOOK_END (25 * PAGESIZE)
#define RB_LOGBOOK_SIZE (RB_LOGBOOK_END - RB_LOGBOOK_BEGIN)
#define RB_LOGBOOK_DISTANCE(a,b) ringbuffer_distance (a, b, 1, RB_LOGBOOK_BEGIN, RB_LOGBOOK_END)
#define RB_LOGBOOK_DISTANCE(a,b) ringbuffer_distance (a, b, DC_RINGBUFFER_FULL, RB_LOGBOOK_BEGIN, RB_LOGBOOK_END)
#define RB_PROFILE_BEGIN (25 * PAGESIZE)
#define RB_PROFILE_END (500 * PAGESIZE)
#define RB_PROFILE_SIZE (RB_PROFILE_END - RB_PROFILE_BEGIN)
#define RB_PROFILE_DISTANCE(a,b) ringbuffer_distance (a, b, 1, RB_PROFILE_BEGIN, RB_PROFILE_END)
#define RB_PROFILE_DISTANCE(a,b) ringbuffer_distance (a, b, DC_RINGBUFFER_FULL, RB_PROFILE_BEGIN, RB_PROFILE_END)
#define SZ_HEADER_XEN 80
#define SZ_HEADER_OTHER 96
@ -469,7 +469,7 @@ liquivision_lynx_device_foreach (dc_device_t *abstract, dc_dive_callback_t callb
// Create the ringbuffer stream.
dc_rbstream_t *rblogbook = NULL;
status = dc_rbstream_new (&rblogbook, abstract, SEGMENTSIZE, SEGMENTSIZE, RB_LOGBOOK_BEGIN, RB_LOGBOOK_END, rb_logbook_end);
status = dc_rbstream_new (&rblogbook, abstract, SEGMENTSIZE, SEGMENTSIZE, RB_LOGBOOK_BEGIN, RB_LOGBOOK_END, rb_logbook_end, DC_RBSTREAM_BACKWARD);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to create the ringbuffer stream.");
goto error_free_logbook;
@ -600,7 +600,7 @@ liquivision_lynx_device_foreach (dc_device_t *abstract, dc_dive_callback_t callb
// Create the ringbuffer stream.
dc_rbstream_t *rbprofile = NULL;
status = dc_rbstream_new (&rbprofile, abstract, SEGMENTSIZE, SEGMENTSIZE, RB_PROFILE_BEGIN, RB_PROFILE_END, rb_profile_end);
status = dc_rbstream_new (&rbprofile, abstract, SEGMENTSIZE, SEGMENTSIZE, RB_PROFILE_BEGIN, RB_PROFILE_END, rb_profile_end, DC_RBSTREAM_BACKWARD);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to create the ringbuffer stream.");
goto error_free_profile;

View File

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

View File

@ -117,7 +117,6 @@ struct liquivision_lynx_parser_t {
liquivision_lynx_tank_t tank[NTANKS];
};
static dc_status_t liquivision_lynx_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size);
static dc_status_t liquivision_lynx_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime);
static dc_status_t liquivision_lynx_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value);
static dc_status_t liquivision_lynx_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata);
@ -125,7 +124,6 @@ static dc_status_t liquivision_lynx_parser_samples_foreach (dc_parser_t *abstrac
static const dc_parser_vtable_t liquivision_lynx_parser_vtable = {
sizeof(liquivision_lynx_parser_t),
DC_FAMILY_LIQUIVISION_LYNX,
liquivision_lynx_parser_set_data, /* set_data */
NULL, /* set_clock */
NULL, /* set_atmospheric */
NULL, /* set_density */
@ -137,7 +135,7 @@ static const dc_parser_vtable_t liquivision_lynx_parser_vtable = {
dc_status_t
liquivision_lynx_parser_create (dc_parser_t **out, dc_context_t *context, unsigned int model)
liquivision_lynx_parser_create (dc_parser_t **out, dc_context_t *context, const unsigned char data[], size_t size, unsigned int model)
{
liquivision_lynx_parser_t *parser = NULL;
@ -145,7 +143,7 @@ liquivision_lynx_parser_create (dc_parser_t **out, dc_context_t *context, unsign
return DC_STATUS_INVALIDARGS;
// Allocate memory.
parser = (liquivision_lynx_parser_t *) dc_parser_allocate (context, &liquivision_lynx_parser_vtable);
parser = (liquivision_lynx_parser_t *) dc_parser_allocate (context, &liquivision_lynx_parser_vtable, data, size);
if (parser == NULL) {
ERROR (context, "Failed to allocate memory.");
return DC_STATUS_NOMEMORY;
@ -173,29 +171,6 @@ liquivision_lynx_parser_create (dc_parser_t **out, dc_context_t *context, unsign
}
static dc_status_t
liquivision_lynx_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size)
{
liquivision_lynx_parser_t *parser = (liquivision_lynx_parser_t *) abstract;
// Reset the cache.
parser->cached = 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
liquivision_lynx_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime)
{
@ -315,6 +290,7 @@ liquivision_lynx_parser_get_field (dc_parser_t *abstract, dc_field_type_t type,
*((unsigned int *) value) = parser->ngasmixes;
break;
case DC_FIELD_GASMIX:
gasmix->usage = DC_USAGE_NONE;
gasmix->helium = parser->gasmix[flags].helium / 100.0;
gasmix->oxygen = parser->gasmix[flags].oxygen / 100.0;
gasmix->nitrogen = 1.0 - gasmix->oxygen - gasmix->helium;
@ -329,6 +305,7 @@ liquivision_lynx_parser_get_field (dc_parser_t *abstract, dc_field_type_t type,
tank->beginpressure = parser->tank[flags].beginpressure / 100.0;
tank->endpressure = parser->tank[flags].endpressure / 100.0;
tank->gasmix = DC_GASMIX_UNKNOWN;
tank->usage = DC_USAGE_NONE;
break;
default:
return DC_STATUS_UNSUPPORTED;
@ -545,29 +522,29 @@ liquivision_lynx_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callba
// Time (seconds).
time += interval;
sample.time = time;
if (callback) callback (DC_SAMPLE_TIME, sample, userdata);
sample.time = time * 1000;
if (callback) callback (DC_SAMPLE_TIME, &sample, userdata);
// Depth (1/100 m).
sample.depth = value / 100.0;
if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata);
if (callback) callback (DC_SAMPLE_DEPTH, &sample, userdata);
// Temperature (1/10 °C).
int temperature = (signed short) array_uint16_le (data + offset);
sample.temperature = temperature / 10.0;
if (callback) callback (DC_SAMPLE_TEMPERATURE, sample, userdata);
if (callback) callback (DC_SAMPLE_TEMPERATURE, &sample, userdata);
// Gas mix
if (have_gasmix) {
sample.gasmix = gasmix_idx;
if (callback) callback (DC_SAMPLE_GASMIX, sample, userdata);
if (callback) callback (DC_SAMPLE_GASMIX, &sample, userdata);
have_gasmix = 0;
}
// Setpoint (1/10 bar).
if (have_setpoint) {
sample.setpoint = setpoint / 10.0;
if (callback) callback (DC_SAMPLE_SETPOINT, sample, userdata);
if (callback) callback (DC_SAMPLE_SETPOINT, &sample, userdata);
have_setpoint = 0;
}
@ -577,7 +554,7 @@ liquivision_lynx_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callba
if (have_pressure & (1 << i)) {
sample.pressure.tank = i;
sample.pressure.value = pressure[i] / 100.0;
if (callback) callback (DC_SAMPLE_PRESSURE, sample, userdata);
if (callback) callback (DC_SAMPLE_PRESSURE, &sample, userdata);
}
}
have_pressure = 0;
@ -593,7 +570,8 @@ liquivision_lynx_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callba
sample.deco.depth = 0.0;
}
sample.deco.time = 0;
if (callback) callback (DC_SAMPLE_DECO, sample, userdata);
sample.deco.tts = 0;
if (callback) callback (DC_SAMPLE_DECO, &sample, userdata);
have_deco = 0;
}

View File

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

View File

@ -47,7 +47,6 @@ struct mares_darwin_parser_t {
unsigned int samplesize;
};
static dc_status_t mares_darwin_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size);
static dc_status_t mares_darwin_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime);
static dc_status_t mares_darwin_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value);
static dc_status_t mares_darwin_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata);
@ -55,7 +54,6 @@ static dc_status_t mares_darwin_parser_samples_foreach (dc_parser_t *abstract, d
static const dc_parser_vtable_t mares_darwin_parser_vtable = {
sizeof(mares_darwin_parser_t),
DC_FAMILY_MARES_DARWIN,
mares_darwin_parser_set_data, /* set_data */
NULL, /* set_clock */
NULL, /* set_atmospheric */
NULL, /* set_density */
@ -67,7 +65,7 @@ static const dc_parser_vtable_t mares_darwin_parser_vtable = {
dc_status_t
mares_darwin_parser_create (dc_parser_t **out, dc_context_t *context, unsigned int model)
mares_darwin_parser_create (dc_parser_t **out, dc_context_t *context, const unsigned char data[], size_t size, unsigned int model)
{
mares_darwin_parser_t *parser = NULL;
@ -75,7 +73,7 @@ mares_darwin_parser_create (dc_parser_t **out, dc_context_t *context, unsigned i
return DC_STATUS_INVALIDARGS;
// Allocate memory.
parser = (mares_darwin_parser_t *) dc_parser_allocate (context, &mares_darwin_parser_vtable);
parser = (mares_darwin_parser_t *) dc_parser_allocate (context, &mares_darwin_parser_vtable, data, size);
if (parser == NULL) {
ERROR (context, "Failed to allocate memory.");
return DC_STATUS_NOMEMORY;
@ -97,13 +95,6 @@ mares_darwin_parser_create (dc_parser_t **out, dc_context_t *context, unsigned i
}
static dc_status_t
mares_darwin_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size)
{
return DC_STATUS_SUCCESS;
}
static dc_status_t
mares_darwin_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime)
{
@ -159,6 +150,7 @@ mares_darwin_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsi
}
break;
case DC_FIELD_GASMIX:
gasmix->usage = DC_USAGE_NONE;
gasmix->helium = 0.0;
if (mode == NITROX) {
gasmix->oxygen = p[0x0E] / 100.0;
@ -185,6 +177,7 @@ mares_darwin_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsi
tank->gasmix = 0;
tank->beginpressure = array_uint16_be (p + 0x17);
tank->endpressure = array_uint16_be (p + 0x19);
tank->usage = DC_USAGE_NONE;
} else {
return DC_STATUS_UNSUPPORTED;
}
@ -242,17 +235,17 @@ mares_darwin_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t
// Surface Time (seconds).
time += 20;
sample.time = time;
if (callback) callback (DC_SAMPLE_TIME, sample, userdata);
sample.time = time * 1000;
if (callback) callback (DC_SAMPLE_TIME, &sample, userdata);
// Depth (1/10 m).
sample.depth = depth / 10.0;
if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata);
if (callback) callback (DC_SAMPLE_DEPTH, &sample, userdata);
// Gas change.
if (gasmix != gasmix_previous) {
sample.gasmix = gasmix;
if (callback) callback (DC_SAMPLE_GASMIX, sample, userdata);
if (callback) callback (DC_SAMPLE_GASMIX, &sample, userdata);
gasmix_previous = gasmix;
}
@ -262,7 +255,7 @@ mares_darwin_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t
sample.event.time = 0;
sample.event.flags = 0;
sample.event.value = ascent;
if (callback) callback (DC_SAMPLE_EVENT, sample, userdata);
if (callback) callback (DC_SAMPLE_EVENT, &sample, userdata);
}
// Deco violation
@ -271,7 +264,7 @@ mares_darwin_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t
sample.event.time = 0;
sample.event.flags = 0;
sample.event.value = 0;
if (callback) callback (DC_SAMPLE_EVENT, sample, userdata);
if (callback) callback (DC_SAMPLE_EVENT, &sample, userdata);
}
// Deco stop
@ -282,7 +275,8 @@ mares_darwin_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t
}
sample.deco.time = 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);
if (parser->samplesize == 3) {
unsigned int type = (time / 20 + 2) % 3;
@ -291,7 +285,7 @@ mares_darwin_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t
pressure -= abstract->data[offset + 2];
sample.pressure.tank = 0;
sample.pressure.value = pressure;
if (callback) callback (DC_SAMPLE_PRESSURE, sample, userdata);
if (callback) callback (DC_SAMPLE_PRESSURE, &sample, userdata);
}
}

View File

@ -21,7 +21,6 @@
#include <string.h> // memcpy, memcmp
#include <stdlib.h> // malloc, free
#include <assert.h> // assert
#include "mares_iconhd.h"
#include "context-private.h"
@ -49,11 +48,39 @@
#define SMARTAIR 0x24
#define QUAD 0x29
#define HORIZON 0x2C
#define PUCKAIR2 0x2D
#define SIRIUS 0x2F
#define QUADCI 0x31
#define PUCK4 0x35
#define ISSMART(model) ( \
(model) == SMART || \
(model) == SMARTAPNEA || \
(model) == SMARTAIR)
#define ISGENIUS(model) ( \
(model) == GENIUS || \
(model) == HORIZON || \
(model) == PUCKAIR2 || \
(model) == SIRIUS || \
(model) == QUADCI || \
(model) == PUCK4)
#define ISSIRIUS(model) ( \
(model) == PUCKAIR2 || \
(model) == SIRIUS || \
(model) == QUADCI || \
(model) == PUCK4)
#define MAXRETRIES 4
#define MAXPACKET 244
#define FIXED 0
#define VARIABLE 1
#define ACK 0xAA
#define EOF 0xEA
#define END 0xEA
#define XOR 0xA5
#define CMD_VERSION 0xC2
@ -63,6 +90,8 @@
#define CMD_OBJ_EVEN 0xAC
#define CMD_OBJ_ODD 0xFE
#define OBJ_DEVICE 0x2000
#define OBJ_DEVICE_SERIAL 0x04
#define OBJ_LOGBOOK 0x2008
#define OBJ_LOGBOOK_COUNT 0x01
#define OBJ_DIVE 0x3000
@ -94,6 +123,7 @@ typedef struct mares_iconhd_device_t {
unsigned char version[140];
unsigned int model;
unsigned int packetsize;
unsigned int ble;
} mares_iconhd_device_t;
static dc_status_t mares_iconhd_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size);
@ -161,6 +191,10 @@ mares_iconhd_get_model (mares_iconhd_device_t *device)
{"Smart Air", SMARTAIR},
{"Quad", QUAD},
{"Horizon", HORIZON},
{"Puck Air 2", PUCKAIR2},
{"Sirius", SIRIUS},
{"Quad Ci", QUADCI},
{"Puck4", PUCK4},
};
// Check the product name in the version packet against the list
@ -177,44 +211,57 @@ mares_iconhd_get_model (mares_iconhd_device_t *device)
}
static dc_status_t
mares_iconhd_packet (mares_iconhd_device_t *device,
const unsigned char command[], unsigned int csize,
unsigned char answer[], unsigned int asize)
mares_iconhd_packet_fixed (mares_iconhd_device_t *device,
unsigned char cmd,
const unsigned char data[], unsigned int size,
unsigned char answer[], unsigned int asize,
unsigned int *actual)
{
dc_status_t status = DC_STATUS_SUCCESS;
dc_device_t *abstract = (dc_device_t *) device;
assert (csize >= 2);
dc_transport_t transport = dc_iostream_get_transport (device->iostream);
if (device_is_cancelled (abstract))
return DC_STATUS_CANCELLED;
// Send the command header to the dive computer.
status = dc_iostream_write (device->iostream, command, 2, NULL);
const unsigned char command[2] = {
cmd, cmd ^ XOR,
};
status = dc_iostream_write (device->iostream, command, sizeof(command), NULL);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to send the command.");
ERROR (abstract->context, "Failed to send the command header.");
return status;
}
// Send the command payload to the dive computer.
if (size && transport == DC_TRANSPORT_BLE) {
status = dc_iostream_write (device->iostream, data, size, NULL);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to send the command data.");
return status;
}
}
// Receive the header byte.
unsigned char header[1] = {0};
status = dc_iostream_read (device->iostream, header, sizeof (header), NULL);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to receive the answer.");
ERROR (abstract->context, "Failed to receive the packet header.");
return status;
}
// Verify the header byte.
if (header[0] != ACK) {
ERROR (abstract->context, "Unexpected answer byte.");
ERROR (abstract->context, "Unexpected packet header byte (%02x).", header[0]);
return DC_STATUS_PROTOCOL;
}
// Send the command payload to the dive computer.
if (csize > 2) {
status = dc_iostream_write (device->iostream, command + 2, csize - 2, NULL);
if (size && transport != DC_TRANSPORT_BLE) {
status = dc_iostream_write (device->iostream, data, size, NULL);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to send the command.");
ERROR (abstract->context, "Failed to send the command data.");
return status;
}
}
@ -222,7 +269,7 @@ mares_iconhd_packet (mares_iconhd_device_t *device,
// Read the packet.
status = dc_iostream_read (device->iostream, answer, asize, NULL);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to receive the answer.");
ERROR (abstract->context, "Failed to receive the packet data.");
return status;
}
@ -230,25 +277,130 @@ mares_iconhd_packet (mares_iconhd_device_t *device,
unsigned char trailer[1] = {0};
status = dc_iostream_read (device->iostream, trailer, sizeof (trailer), NULL);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to receive the answer.");
ERROR (abstract->context, "Failed to receive the packet trailer.");
return status;
}
// Verify the trailer byte.
if (trailer[0] != EOF) {
ERROR (abstract->context, "Unexpected answer byte.");
if (trailer[0] != END) {
ERROR (abstract->context, "Unexpected packet trailer byte (%02x).", trailer[0]);
return DC_STATUS_PROTOCOL;
}
if (actual) {
*actual = asize;
}
return DC_STATUS_SUCCESS;
}
static dc_status_t
mares_iconhd_transfer (mares_iconhd_device_t *device, const unsigned char command[], unsigned int csize, unsigned char answer[], unsigned int asize)
mares_iconhd_packet_variable (mares_iconhd_device_t *device,
unsigned char cmd,
const unsigned char data[], unsigned int size,
unsigned char answer[], unsigned int asize,
unsigned int *actual)
{
dc_status_t status = DC_STATUS_SUCCESS;
dc_device_t *abstract = (dc_device_t *) device;
unsigned char packet[MAXPACKET] = {0};
size_t length = 0;
if (device_is_cancelled (abstract))
return DC_STATUS_CANCELLED;
// Send the command header to the dive computer.
const unsigned char command[2] = {
cmd, cmd ^ XOR,
};
status = dc_iostream_write (device->iostream, command, sizeof(command), NULL);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to send the command header.");
return status;
}
// Read either the entire data packet (if there is no command data to send),
// or only the header byte (if there is also command data to send).
status = dc_iostream_read (device->iostream, packet, sizeof (packet), &length);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to receive the packet header.");
return status;
}
if (size) {
// Send the command payload to the dive computer.
status = dc_iostream_write (device->iostream, data, size, NULL);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to send the command data.");
return status;
}
// Read the data packet.
size_t len = 0;
status = dc_iostream_read (device->iostream, packet + length, sizeof (packet) - length, &len);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to receive the packet data.");
return status;
}
length += len;
}
if (length < 2 || length - 2 > asize) {
ERROR (abstract->context, "Unexpected packet length (" DC_PRINTF_SIZE ").", length);
return DC_STATUS_PROTOCOL;
}
// Verify the header byte.
if (packet[0] != ACK) {
ERROR (abstract->context, "Unexpected packet header byte (%02x).", packet[0]);
return DC_STATUS_PROTOCOL;
}
// Verify the trailer byte.
if (packet[length - 1] != END) {
ERROR (abstract->context, "Unexpected packet trailer byte (%02x).", packet[length - 1]);
return DC_STATUS_PROTOCOL;
}
if (actual == NULL) {
// Verify the actual length.
if (length - 2 != asize) {
ERROR (abstract->context, "Unexpected packet length (" DC_PRINTF_SIZE ").", length - 2);
return DC_STATUS_PROTOCOL;
}
} else {
// Return the actual length.
*actual = length - 2;
}
memcpy (answer, packet + 1, length - 2);
return DC_STATUS_SUCCESS;
}
static dc_status_t
mares_iconhd_packet (mares_iconhd_device_t *device,
unsigned char cmd,
const unsigned char data[], unsigned int size,
unsigned char answer[], unsigned int asize,
unsigned int *actual)
{
dc_transport_t transport = dc_iostream_get_transport (device->iostream);
if (transport == DC_TRANSPORT_BLE && device->ble == VARIABLE) {
return mares_iconhd_packet_variable (device, cmd, data, size, answer, asize, actual);
} else {
return mares_iconhd_packet_fixed (device, cmd, data, size, answer, asize, actual);
}
}
static dc_status_t
mares_iconhd_transfer (mares_iconhd_device_t *device, unsigned char cmd, const unsigned char data[], unsigned int size, unsigned char answer[], unsigned int asize, unsigned int *actual)
{
unsigned int nretries = 0;
dc_status_t rc = DC_STATUS_SUCCESS;
while ((rc = mares_iconhd_packet (device, command, csize, answer, asize)) != DC_STATUS_SUCCESS) {
while ((rc = mares_iconhd_packet (device, cmd, data, size, answer, asize, actual)) != DC_STATUS_SUCCESS) {
// Automatically discard a corrupted packet,
// and request a new one.
if (rc != DC_STATUS_PROTOCOL && rc != DC_STATUS_TIMEOUT)
@ -272,7 +424,9 @@ mares_iconhd_read_object(mares_iconhd_device_t *device, dc_event_progress_t *pro
dc_status_t status = DC_STATUS_SUCCESS;
dc_device_t *abstract = (dc_device_t *) device;
dc_transport_t transport = dc_iostream_get_transport (device->iostream);
const unsigned int maxpacket = (transport == DC_TRANSPORT_BLE) ? 124 : 504;
const unsigned int maxpacket = (transport == DC_TRANSPORT_BLE) ?
(device->ble == VARIABLE ? MAXPACKET - 3 : 124) :
504;
// Update and emit a progress event.
unsigned int initial = 0;
@ -283,23 +437,21 @@ mares_iconhd_read_object(mares_iconhd_device_t *device, dc_event_progress_t *pro
// Transfer the init packet.
unsigned char rsp_init[16];
unsigned char cmd_init[18] = {
CMD_OBJ_INIT,
CMD_OBJ_INIT ^ XOR,
unsigned char cmd_init[16] = {
0x40,
(index >> 0) & 0xFF,
(index >> 8) & 0xFF,
subindex & 0xFF
};
memset (cmd_init + 6, 0x00, sizeof(cmd_init) - 6);
status = mares_iconhd_transfer (device, cmd_init, sizeof (cmd_init), rsp_init, sizeof (rsp_init));
status = mares_iconhd_transfer (device, CMD_OBJ_INIT, cmd_init, sizeof (cmd_init), rsp_init, sizeof (rsp_init), NULL);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to transfer the init packet.");
return status;
}
// Verify the packet header.
if (memcmp (cmd_init + 3, rsp_init + 1, 3) != 0) {
if (memcmp (cmd_init + 1, rsp_init + 1, 3) != 0) {
ERROR (abstract->context, "Unexpected packet header.");
return DC_STATUS_PROTOCOL;
}
@ -346,14 +498,21 @@ mares_iconhd_read_object(mares_iconhd_device_t *device, dc_event_progress_t *pro
}
// Transfer the segment packet.
unsigned int length = 0;
unsigned char rsp_segment[1 + 504];
unsigned char cmd_segment[] = {cmd, cmd ^ XOR};
status = mares_iconhd_transfer (device, cmd_segment, sizeof (cmd_segment), rsp_segment, len + 1);
status = mares_iconhd_transfer (device, cmd, NULL, 0, rsp_segment, len + 1, &length);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to transfer the segment packet.");
return status;
}
if (length < 1) {
ERROR (abstract->context, "Unexpected packet length (%u).", length);
return DC_STATUS_PROTOCOL;
}
length--;
// Verify the packet header.
if ((rsp_segment[0] & 0xF0) >> 4 != toggle) {
ERROR (abstract->context, "Unexpected packet header (%02x).", rsp_segment[0]);
@ -361,12 +520,12 @@ mares_iconhd_read_object(mares_iconhd_device_t *device, dc_event_progress_t *pro
}
// Append the payload to the output buffer.
if (!dc_buffer_append (buffer, rsp_segment + 1, len)) {
if (!dc_buffer_append (buffer, rsp_segment + 1, length)) {
ERROR (abstract->context, "Insufficient buffer space available.");
return DC_STATUS_NOMEMORY;
}
nbytes += len;
nbytes += length;
npackets++;
// Update and emit the progress events.
@ -380,7 +539,7 @@ mares_iconhd_read_object(mares_iconhd_device_t *device, dc_event_progress_t *pro
}
dc_status_t
mares_iconhd_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t *iostream)
mares_iconhd_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t *iostream, unsigned int model)
{
dc_status_t status = DC_STATUS_SUCCESS;
mares_iconhd_device_t *device = NULL;
@ -403,10 +562,11 @@ mares_iconhd_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_
memset (device->version, 0, sizeof (device->version));
device->model = 0;
device->packetsize = 0;
device->ble = ISSIRIUS(model) ? VARIABLE : FIXED;
// Create the packet stream.
if (transport == DC_TRANSPORT_BLE) {
status = dc_packet_open (&device->iostream, context, iostream, 20, 20);
if (transport == DC_TRANSPORT_BLE && device->ble == FIXED) {
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;
@ -447,9 +607,8 @@ mares_iconhd_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_
dc_iostream_purge (device->iostream, DC_DIRECTION_ALL);
// Send the version command.
unsigned char command[] = {CMD_VERSION, CMD_VERSION ^ XOR};
status = mares_iconhd_transfer (device, command, sizeof (command),
device->version, sizeof (device->version));
status = mares_iconhd_transfer (device, CMD_VERSION, NULL, 0,
device->version, sizeof (device->version), NULL);
if (status != DC_STATUS_SUCCESS) {
goto error_free_iostream;
}
@ -460,9 +619,8 @@ mares_iconhd_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_
// Read the size of the flash memory.
unsigned int memsize = 0;
if (device->model == QUAD) {
unsigned char cmd_flash[] = {CMD_FLASHSIZE, CMD_FLASHSIZE ^ XOR};
unsigned char rsp_flash[4] = {0};
status = mares_iconhd_transfer (device, cmd_flash, sizeof (cmd_flash), rsp_flash, sizeof (rsp_flash));
status = mares_iconhd_transfer (device, CMD_FLASHSIZE, NULL, 0, rsp_flash, sizeof (rsp_flash), NULL);
if (status != DC_STATUS_SUCCESS) {
WARNING (context, "Failed to read the flash memory size.");
} else {
@ -521,7 +679,7 @@ mares_iconhd_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_
error_free_iostream:
if (transport == DC_TRANSPORT_BLE) {
if (transport == DC_TRANSPORT_BLE && device->ble == FIXED) {
dc_iostream_close (device->iostream);
}
error_free:
@ -534,9 +692,10 @@ static dc_status_t
mares_iconhd_device_close (dc_device_t *abstract)
{
mares_iconhd_device_t *device = (mares_iconhd_device_t *) abstract;
dc_transport_t transport = dc_iostream_get_transport (device->iostream);
// Close the packet stream.
if (dc_iostream_get_transport (device->iostream) == DC_TRANSPORT_BLE) {
if (transport == DC_TRANSPORT_BLE && device->ble == FIXED) {
return dc_iostream_close (device->iostream);
}
@ -575,7 +734,7 @@ mares_iconhd_device_read (dc_device_t *abstract, unsigned int address, unsigned
len = device->packetsize;
// Read the packet.
unsigned char command[] = {CMD_READ, CMD_READ ^ XOR,
unsigned char command[] = {
(address ) & 0xFF,
(address >> 8) & 0xFF,
(address >> 16) & 0xFF,
@ -584,7 +743,7 @@ mares_iconhd_device_read (dc_device_t *abstract, unsigned int address, unsigned
(len >> 8) & 0xFF,
(len >> 16) & 0xFF,
(len >> 24) & 0xFF};
rc = mares_iconhd_transfer (device, command, sizeof (command), data, len);
rc = mares_iconhd_transfer (device, CMD_READ, command, sizeof (command), data, len, NULL);
if (rc != DC_STATUS_SUCCESS)
return rc;
@ -641,6 +800,21 @@ mares_iconhd_device_foreach_raw (dc_device_t *abstract, dc_dive_callback_t callb
const mares_iconhd_layout_t *layout = device->layout;
// Read the serial number.
unsigned char serial[4] = {0};
rc = mares_iconhd_device_read (abstract, 0x0C, serial, sizeof (serial));
if (rc != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to read the memory.");
return rc;
}
// Emit a device info event.
dc_event_devinfo_t devinfo;
devinfo.model = device->model;
devinfo.firmware = 0;
devinfo.serial = array_uint32_le (serial);
device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo);
// Enable progress notifications.
dc_event_progress_t progress = EVENT_PROGRESS_INITIALIZER;
progress.maximum = layout->rb_profile_end - layout->rb_profile_begin;
@ -690,7 +864,7 @@ mares_iconhd_device_foreach_raw (dc_device_t *abstract, dc_dive_callback_t callb
// Create the ringbuffer stream.
dc_rbstream_t *rbstream = NULL;
rc = dc_rbstream_new (&rbstream, abstract, 1, device->packetsize, layout->rb_profile_begin, layout->rb_profile_end, eop);
rc = dc_rbstream_new (&rbstream, abstract, 1, device->packetsize, layout->rb_profile_begin, layout->rb_profile_end, eop, DC_RBSTREAM_BACKWARD);
if (rc != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to create the ringbuffer stream.");
return rc;
@ -717,7 +891,7 @@ mares_iconhd_device_foreach_raw (dc_device_t *abstract, dc_dive_callback_t callb
// Get the number of samples in the profile data.
unsigned int type = 0, nsamples = 0;
if (model == SMART || model == SMARTAPNEA || model == SMARTAIR) {
if (ISSMART(model)) {
type = array_uint16_le (buffer + offset - header + 2);
nsamples = array_uint16_le (buffer + offset - header + 0);
} else {
@ -846,6 +1020,33 @@ mares_iconhd_device_foreach_object (dc_device_t *abstract, dc_dive_callback_t ca
return DC_STATUS_NOMEMORY;
}
// Read the serial number.
rc = mares_iconhd_read_object (device, NULL, buffer, OBJ_DEVICE, OBJ_DEVICE_SERIAL);
if (rc != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to read the serial number.");
dc_buffer_free (buffer);
return rc;
}
if (dc_buffer_get_size (buffer) < 16) {
ERROR (abstract->context, "Unexpected number of bytes received (" DC_PRINTF_SIZE ").",
dc_buffer_get_size (buffer));
dc_buffer_free (buffer);
return DC_STATUS_PROTOCOL;
}
unsigned int serial = array_convert_str2num (dc_buffer_get_data (buffer) + 10, 6);
// Emit a device info event.
dc_event_devinfo_t devinfo;
devinfo.model = device->model;
devinfo.firmware = 0;
devinfo.serial = serial;
device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo);
// Erase the buffer.
dc_buffer_clear (buffer);
// Read the number of dives.
rc = mares_iconhd_read_object (device, NULL, buffer, OBJ_LOGBOOK, OBJ_LOGBOOK_COUNT);
if (rc != DC_STATUS_SUCCESS) {
@ -908,7 +1109,6 @@ mares_iconhd_device_foreach_object (dc_device_t *abstract, dc_dive_callback_t ca
static dc_status_t
mares_iconhd_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata)
{
dc_status_t rc = DC_STATUS_SUCCESS;
mares_iconhd_device_t *device = (mares_iconhd_device_t *) abstract;
// Emit a vendor event.
@ -917,22 +1117,7 @@ mares_iconhd_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback,
vendor.size = sizeof (device->version);
device_event_emit (abstract, DC_EVENT_VENDOR, &vendor);
// Read the serial number.
unsigned char serial[4] = {0};
rc = mares_iconhd_device_read (abstract, 0x0C, serial, sizeof (serial));
if (rc != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to read the memory.");
return rc;
}
// Emit a device info event.
dc_event_devinfo_t devinfo;
devinfo.model = device->model;
devinfo.firmware = 0;
devinfo.serial = array_uint32_le (serial);
device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo);
if (device->model == GENIUS || device->model == HORIZON) {
if (ISGENIUS(device->model)) {
return mares_iconhd_device_foreach_object (abstract, callback, userdata);
} else {
return mares_iconhd_device_foreach_raw (abstract, callback, userdata);

View File

@ -32,10 +32,10 @@ extern "C" {
#endif /* __cplusplus */
dc_status_t
mares_iconhd_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream);
mares_iconhd_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream, unsigned int model);
dc_status_t
mares_iconhd_parser_create (dc_parser_t **parser, dc_context_t *context, unsigned int model);
mares_iconhd_parser_create (dc_parser_t **parser, dc_context_t *context, const unsigned char data[], size_t size, unsigned int model);
#ifdef __cplusplus
}

View File

@ -45,6 +45,23 @@
#define QUADAIR 0x23
#define SMARTAIR 0x24
#define HORIZON 0x2C
#define PUCKAIR2 0x2D
#define SIRIUS 0x2F
#define QUADCI 0x31
#define PUCK4 0x35
#define ISSMART(model) ( \
(model) == SMART || \
(model) == SMARTAPNEA || \
(model) == SMARTAIR)
#define ISGENIUS(model) ( \
(model) == GENIUS || \
(model) == HORIZON || \
(model) == PUCKAIR2 || \
(model) == SIRIUS || \
(model) == QUADCI || \
(model) == PUCK4)
#define NGASMIXES_ICONHD 3
#define NGASMIXES_GENIUS 5
@ -264,7 +281,6 @@ static const mares_iconhd_layout_t horizon = {
0x54 + 8, /* tanks */
};
static dc_status_t mares_iconhd_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size);
static dc_status_t mares_iconhd_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime);
static dc_status_t mares_iconhd_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value);
static dc_status_t mares_iconhd_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata);
@ -272,7 +288,6 @@ static dc_status_t mares_iconhd_parser_samples_foreach (dc_parser_t *abstract, d
static const dc_parser_vtable_t mares_iconhd_parser_vtable = {
sizeof(mares_iconhd_parser_t),
DC_FAMILY_MARES_ICONHD,
mares_iconhd_parser_set_data, /* set_data */
NULL, /* set_clock */
NULL, /* set_atmospheric */
NULL, /* set_density */
@ -282,28 +297,6 @@ static const dc_parser_vtable_t mares_iconhd_parser_vtable = {
NULL /* destroy */
};
static unsigned int
mares_genius_isvalid (const unsigned char data[], size_t size, unsigned int type)
{
if (size < 10) {
return 0;
}
unsigned int head = array_uint32_be(data);
unsigned int tail = array_uint32_be(data + size - 4);
if (head != type || tail != type) {
return 0;
}
unsigned short crc = array_uint16_le(data + size - 6);
unsigned short ccrc = checksum_crc16_ccitt(data + 4, size - 10, 0x0000, 0x0000);
if (crc != ccrc) {
return 0;
}
return 1;
}
static dc_status_t
mares_iconhd_cache (mares_iconhd_parser_t *parser)
{
@ -334,7 +327,7 @@ mares_iconhd_cache (mares_iconhd_parser_t *parser)
// Get the number of samples in the profile data.
unsigned int type = 0, nsamples = 0;
if (parser->model == SMART || parser->model == SMARTAPNEA || parser->model == SMARTAIR) {
if (ISSMART (parser->model)) {
type = array_uint16_le (data + length - header + 2);
nsamples = array_uint16_le (data + length - header + 0);
} else {
@ -389,7 +382,7 @@ mares_iconhd_cache (mares_iconhd_parser_t *parser)
}
const unsigned char *p = data + length - headersize;
if (parser->model != SMART && parser->model != SMARTAPNEA && parser->model != SMARTAIR) {
if (!ISSMART(parser->model)) {
p += 4;
}
@ -401,12 +394,12 @@ mares_iconhd_cache (mares_iconhd_parser_t *parser)
unsigned int samplerate = 0;
if (parser->model == SMARTAPNEA) {
unsigned int idx = (settings & 0x0600) >> 9;
interval = 1;
samplerate = 1 << idx;
interval = 1000 / samplerate;
} else {
const unsigned int intervals[] = {1, 5, 10, 20};
unsigned int idx = (settings & 0x0C00) >> 10;
interval = intervals[idx];
interval = intervals[idx] * 1000;
samplerate = 1;
}
@ -547,30 +540,22 @@ mares_genius_cache (mares_iconhd_parser_t *parser)
// Get the dive mode.
unsigned int mode = settings & 0xF;
// Get the sample size.
unsigned int samplesize = logformat == 1 ? SDPT_SIZE: DPRS_SIZE;
// Calculate the total number of bytes for this dive.
unsigned int nbytes = headersize + 4 + DSTR_SIZE + TISS_SIZE + nsamples * samplesize + (nsamples / 4) * AIRS_SIZE + DEND_SIZE;
if (nbytes > size) {
ERROR (abstract->context, "Buffer overflow detected!");
return DC_STATUS_DATAFORMAT;
}
// Get the profile type and version.
unsigned int profile_type = array_uint16_le (data + headersize);
unsigned int profile_minor = data[headersize + 2];
unsigned int profile_major = data[headersize + 3];
// Get the surface timeout setting (in minutes).
// 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.
unsigned int surftime = 3;
if (profile_type == 0 &&
OBJVERSION(profile_major,profile_minor) >= OBJVERSION(1,0)) {
surftime = (settings >> 13) & 0x3F;
if (headersize + 4 <= size) {
// Get the profile type and version.
unsigned int profile_type = array_uint16_le (data + headersize);
unsigned int profile_minor = data[headersize + 2];
unsigned int profile_major = data[headersize + 3];
if (profile_type == 0 &&
OBJVERSION(profile_major,profile_minor) >= OBJVERSION(1,0)) {
surftime = (settings >> 13) & 0x3F;
}
}
// Gas mixes and tanks.
@ -621,11 +606,11 @@ mares_genius_cache (mares_iconhd_parser_t *parser)
parser->logformat = logformat;
parser->mode = mode;
parser->nsamples = nsamples;
parser->samplesize = samplesize;
parser->samplesize = 0;
parser->headersize = headersize;
parser->settings = settings;
parser->surftime = surftime * 60;
parser->interval = 5;
parser->interval = 5000;
parser->samplerate = 1;
parser->ntanks = ntanks;
parser->ngasmixes = ngasmixes;
@ -648,7 +633,7 @@ mares_iconhd_parser_cache (mares_iconhd_parser_t *parser)
return DC_STATUS_SUCCESS;
}
if (parser->model == GENIUS || parser->model == HORIZON) {
if (ISGENIUS(parser->model)) {
return mares_genius_cache (parser);
} else {
return mares_iconhd_cache (parser);
@ -656,7 +641,7 @@ mares_iconhd_parser_cache (mares_iconhd_parser_t *parser)
}
dc_status_t
mares_iconhd_parser_create (dc_parser_t **out, dc_context_t *context, unsigned int model)
mares_iconhd_parser_create (dc_parser_t **out, dc_context_t *context, const unsigned char data[], size_t size, unsigned int model)
{
mares_iconhd_parser_t *parser = NULL;
@ -664,7 +649,7 @@ mares_iconhd_parser_create (dc_parser_t **out, dc_context_t *context, unsigned i
return DC_STATUS_INVALIDARGS;
// Allocate memory.
parser = (mares_iconhd_parser_t *) dc_parser_allocate (context, &mares_iconhd_parser_vtable);
parser = (mares_iconhd_parser_t *) dc_parser_allocate (context, &mares_iconhd_parser_vtable, data, size);
if (parser == NULL) {
ERROR (context, "Failed to allocate memory.");
return DC_STATUS_NOMEMORY;
@ -674,7 +659,7 @@ mares_iconhd_parser_create (dc_parser_t **out, dc_context_t *context, unsigned i
parser->model = model;
parser->cached = 0;
parser->logformat = 0;
parser->mode = (model == GENIUS || model == HORIZON) ? GENIUS_AIR : ICONHD_AIR;
parser->mode = ISGENIUS(model) ? GENIUS_AIR : ICONHD_AIR;
parser->nsamples = 0;
parser->samplesize = 0;
parser->headersize = 0;
@ -701,41 +686,6 @@ mares_iconhd_parser_create (dc_parser_t **out, dc_context_t *context, unsigned i
return DC_STATUS_SUCCESS;
}
static dc_status_t
mares_iconhd_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size)
{
mares_iconhd_parser_t *parser = (mares_iconhd_parser_t *) abstract;
// Reset the cache.
parser->cached = 0;
parser->logformat = 0;
parser->mode = (parser->model == GENIUS || parser->model == HORIZON) ? GENIUS_AIR : ICONHD_AIR;
parser->nsamples = 0;
parser->samplesize = 0;
parser->headersize = 0;
parser->settings = 0;
parser->surftime = 0;
parser->interval = 0;
parser->samplerate = 0;
parser->ntanks = 0;
parser->ngasmixes = 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].volume = 0;
parser->tank[i].workpressure = 0;
parser->tank[i].beginpressure = 0;
parser->tank[i].endpressure = 0;
}
parser->layout = NULL;
return DC_STATUS_SUCCESS;
}
static dc_status_t
mares_iconhd_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime)
{
@ -748,9 +698,9 @@ mares_iconhd_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime
// Pointer to the header data.
const unsigned char *p = abstract->data;
if (parser->model != GENIUS && parser->model != HORIZON) {
if (!ISGENIUS(parser->model)) {
p += abstract->size - parser->headersize;
if (parser->model != SMART && parser->model != SMARTAPNEA && parser->model != SMARTAIR) {
if (!ISSMART(parser->model)) {
p += 4;
}
}
@ -759,7 +709,7 @@ mares_iconhd_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime
p += parser->layout->datetime;
if (datetime) {
if (parser->model == GENIUS || parser->model == HORIZON) {
if (ISGENIUS(parser->model)) {
unsigned int timestamp = array_uint32_le (p);
datetime->hour = (timestamp ) & 0x1F;
datetime->minute = (timestamp >> 5) & 0x3F;
@ -794,9 +744,9 @@ mares_iconhd_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsi
// Pointer to the header data.
const unsigned char *p = abstract->data;
if (parser->model != GENIUS && parser->model != HORIZON) {
if (!ISGENIUS(parser->model)) {
p += abstract->size - parser->headersize;
if (parser->model != SMART && parser->model != SMARTAPNEA && parser->model != SMARTAIR) {
if (!ISSMART(parser->model)) {
p += 4;
}
}
@ -807,7 +757,7 @@ mares_iconhd_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsi
extra = 8;
}
unsigned int metric = (parser->model == GENIUS || parser->model == HORIZON) ?
unsigned int metric = ISGENIUS(parser->model) ?
p[0x34 + extra] : parser->settings & 0x0100;
dc_gasmix_t *gasmix = (dc_gasmix_t *) value;
@ -820,7 +770,7 @@ mares_iconhd_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsi
if (parser->layout->divetime != UNSUPPORTED) {
*((unsigned int *) value) = array_uint16_le (p + parser->layout->divetime);
} else {
*((unsigned int *) value) = parser->nsamples * parser->interval - parser->surftime;
*((unsigned int *) value) = parser->nsamples * parser->interval / 1000 - parser->surftime;
}
break;
case DC_FIELD_MAXDEPTH:
@ -830,6 +780,7 @@ mares_iconhd_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsi
*((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;
@ -857,12 +808,13 @@ mares_iconhd_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsi
} else {
tank->gasmix = DC_GASMIX_UNKNOWN;
}
tank->usage = DC_USAGE_NONE;
break;
case DC_FIELD_ATMOSPHERIC:
*((double *) value) = array_uint16_le (p + parser->layout->atmospheric) / (1000.0 * parser->layout->atmospheric_divisor);
break;
case DC_FIELD_SALINITY:
if (parser->model == GENIUS || parser->model == HORIZON) {
if (ISGENIUS(parser->model)) {
unsigned int salinity = (parser->settings >> 5) & 0x03;
switch (salinity) {
case WATER_FRESH:
@ -904,7 +856,7 @@ mares_iconhd_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsi
*((double *) value) = (signed short) array_uint16_le (p + parser->layout->temperature_max) / 10.0;
break;
case DC_FIELD_DIVEMODE:
if (parser->model == GENIUS || parser->model == HORIZON) {
if (ISGENIUS(parser->model)) {
switch (parser->mode) {
case GENIUS_AIR:
case GENIUS_NITROX_SINGLE:
@ -952,64 +904,16 @@ mares_iconhd_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsi
static dc_status_t
mares_iconhd_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata)
mares_iconhd_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata)
{
mares_iconhd_parser_t *parser = (mares_iconhd_parser_t *) abstract;
// Cache the parser data.
dc_status_t rc = mares_iconhd_parser_cache (parser);
if (rc != DC_STATUS_SUCCESS)
return rc;
const unsigned char *data = abstract->data;
if (parser->samplerate > 1) {
// The Smart Apnea supports multiple samples per second
// (e.g. 2, 4 or 8). Since our smallest unit of time is one
// second, we can't represent this, and the extra samples
// will get dropped.
WARNING(abstract->context, "Multiple samples per second are not supported!");
}
// Previous gas mix - initialize with impossible value
unsigned int gasmix_previous = 0xFFFFFFFF;
unsigned int offset = 4;
unsigned int marker = 0;
if (parser->model == GENIUS || parser->model == HORIZON) {
// Skip the dive header.
data += parser->headersize;
// Check the profile type and version.
unsigned int type = array_uint16_le (data);
unsigned int minor = data[2];
unsigned int major = data[3];
if (type > 1 ||
(type == 0 && OBJVERSION(major,minor) > OBJVERSION(1,0)) ||
(type == 1 && OBJVERSION(major,minor) > OBJVERSION(0,2))) {
ERROR (abstract->context, "Unsupported object type (%u) or version (%u.%u).",
type, major, minor);
return DC_STATUS_DATAFORMAT;
}
// Skip the DSTR record.
if (!mares_genius_isvalid (data + offset, DSTR_SIZE, DSTR_TYPE)) {
ERROR (abstract->context, "Invalid DSTR record.");
return DC_STATUS_DATAFORMAT;
}
offset += DSTR_SIZE;
// Skip the TISS record.
if (!mares_genius_isvalid (data + offset, TISS_SIZE, TISS_TYPE)) {
ERROR (abstract->context, "Invalid TISS record.");
return DC_STATUS_DATAFORMAT;
}
offset += TISS_SIZE;
// Size of the record type marker.
marker = 4;
}
unsigned int time = 0;
unsigned int nsamples = 0;
while (nsamples < parser->nsamples) {
@ -1021,118 +925,73 @@ mares_iconhd_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t
unsigned int surftime = array_uint16_le (data + offset + 4);
// Surface Time (seconds).
time += surftime;
time += surftime * 1000;
sample.time = time;
if (callback) callback (DC_SAMPLE_TIME, sample, userdata);
if (callback) callback (DC_SAMPLE_TIME, &sample, userdata);
// Surface Depth (0 m).
sample.depth = 0.0;
if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata);
if (callback) callback (DC_SAMPLE_DEPTH, &sample, userdata);
offset += parser->samplesize;
nsamples++;
for (unsigned int i = 0; i < divetime; ++i) {
unsigned int count = divetime * parser->samplerate;
for (unsigned int i = 0; i < count; ++i) {
// Time (seconds).
time += parser->interval;
sample.time = time;
if (callback) callback (DC_SAMPLE_TIME, sample, userdata);
if (callback) callback (DC_SAMPLE_TIME, &sample, userdata);
// Depth (1/10 m).
unsigned int depth = array_uint16_le (data + offset);
sample.depth = depth / 10.0;
if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata);
if (callback) callback (DC_SAMPLE_DEPTH, &sample, userdata);
offset += 2 * parser->samplerate;
offset += 2;
}
} else if (parser->model != GENIUS && parser->model != HORIZON && parser->mode == ICONHD_FREEDIVE) {
} else if (parser->mode == ICONHD_FREEDIVE) {
unsigned int maxdepth = array_uint16_le (data + offset + 0);
unsigned int divetime = array_uint16_le (data + offset + 2);
unsigned int surftime = array_uint16_le (data + offset + 4);
// Surface Time (seconds).
time += surftime;
time += surftime * 1000;
sample.time = time;
if (callback) callback (DC_SAMPLE_TIME, sample, userdata);
if (callback) callback (DC_SAMPLE_TIME, &sample, userdata);
// Surface Depth (0 m).
sample.depth = 0.0;
if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata);
if (callback) callback (DC_SAMPLE_DEPTH, &sample, userdata);
// Dive Time (seconds).
time += divetime;
time += divetime * 1000;
sample.time = time;
if (callback) callback (DC_SAMPLE_TIME, sample, userdata);
if (callback) callback (DC_SAMPLE_TIME, &sample, userdata);
// Maximum Depth (1/10 m).
sample.depth = maxdepth / 10.0;
if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata);
if (callback) callback (DC_SAMPLE_DEPTH, &sample, userdata);
offset += parser->samplesize;
nsamples++;
} else {
unsigned int depth = 0, temperature = 0;
unsigned int gasmix = 0, alarms = 0;
unsigned int decostop = 0, decodepth = 0, decotime = 0, tts = 0;
unsigned int bookmark = 0;
if (parser->model == GENIUS || parser->model == HORIZON) {
if (parser->logformat == 1) {
if (!mares_genius_isvalid (data + offset, SDPT_SIZE, SDPT_TYPE)) {
ERROR (abstract->context, "Invalid SDPT record.");
return DC_STATUS_DATAFORMAT;
}
unsigned int misc = 0, deco = 0;
depth = array_uint16_le (data + offset + marker + 2);
temperature = array_uint16_le (data + offset + marker + 6);
alarms = array_uint32_le (data + offset + marker + 0x14);
misc = array_uint32_le (data + offset + marker + 0x18);
deco = array_uint32_le (data + offset + marker + 0x1C);
bookmark = (misc >> 2) & 0x0F;
gasmix = (misc >> 6) & 0x0F;
decostop = (misc >> 10) & 0x01;
if (decostop) {
decodepth = (deco >> 3) & 0x7F;
decotime = (deco >> 10) & 0xFF;
tts = (deco >> 18) & 0x3FFF;
} else {
decotime = deco & 0xFF;
}
} else {
if (!mares_genius_isvalid (data + offset, DPRS_SIZE, DPRS_TYPE)) {
ERROR (abstract->context, "Invalid DPRS record.");
return DC_STATUS_DATAFORMAT;
}
unsigned int misc = 0;
depth = array_uint16_le (data + offset + marker + 0);
temperature = array_uint16_le (data + offset + marker + 4);
decotime = array_uint16_le (data + offset + marker + 0x0A);
alarms = array_uint32_le (data + offset + marker + 0x0C);
misc = array_uint32_le (data + offset + marker + 0x14);
bookmark = (misc >> 2) & 0x0F;
gasmix = (misc >> 6) & 0x0F;
decostop = (misc >> 18) & 0x01;
decodepth = (misc >> 19) & 0x7F;
}
} else {
depth = array_uint16_le (data + offset + 0);
temperature = array_uint16_le (data + offset + 2) & 0x0FFF;
gasmix = (data[offset + 3] & 0xF0) >> 4;
}
unsigned int depth = array_uint16_le (data + offset + 0);
unsigned int temperature = array_uint16_le (data + offset + 2) & 0x0FFF;
unsigned int gasmix = (data[offset + 3] & 0xF0) >> 4;
// Time (seconds).
time += parser->interval;
sample.time = time;
if (callback) callback (DC_SAMPLE_TIME, sample, userdata);
if (callback) callback (DC_SAMPLE_TIME, &sample, userdata);
// Depth (1/10 m).
sample.depth = depth / 10.0;
if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata);
if (callback) callback (DC_SAMPLE_DEPTH, &sample, userdata);
// Temperature (1/10 °C).
sample.temperature = temperature / 10.0;
if (callback) callback (DC_SAMPLE_TEMPERATURE, sample, userdata);
if (callback) callback (DC_SAMPLE_TEMPERATURE, &sample, userdata);
// Current gas mix
if (parser->ngasmixes > 0) {
@ -1142,95 +1001,264 @@ mares_iconhd_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t
}
if (gasmix != gasmix_previous) {
sample.gasmix = gasmix;
if (callback) callback (DC_SAMPLE_GASMIX, sample, userdata);
if (callback) callback (DC_SAMPLE_GASMIX, &sample, userdata);
gasmix_previous = gasmix;
}
}
// Bookmark
if (bookmark) {
sample.event.type = SAMPLE_EVENT_BOOKMARK;
sample.event.time = 0;
sample.event.flags = 0;
sample.event.value = bookmark;
if (callback) callback (DC_SAMPLE_EVENT, sample, userdata);
}
if (parser->model == GENIUS || parser->model == HORIZON) {
// Deco stop / NDL.
if (decostop) {
sample.deco.type = DC_DECO_DECOSTOP;
sample.deco.depth = decodepth;
} else {
sample.deco.type = DC_DECO_NDL;
sample.deco.depth = 0.0;
}
sample.deco.time = decotime * 60;
if (callback) callback (DC_SAMPLE_DECO, sample, userdata);
// Alarms
for (unsigned int v = alarms, i = 0; v; v >>= 1, ++i) {
if ((v & 1) == 0) {
continue;
}
switch (i) {
case ALARM_FAST_ASCENT:
case ALARM_UNCONTROLLED_ASCENT:
sample.event.type = SAMPLE_EVENT_ASCENT;
break;
case ALARM_MISSED_DECO:
case ALARM_DIVE_VIOLATION_DECO:
sample.event.type = SAMPLE_EVENT_CEILING;
break;
default:
sample.event.type = SAMPLE_EVENT_NONE;
break;
}
if (sample.event.type != SAMPLE_EVENT_NONE) {
sample.event.time = 0;
sample.event.flags = 0;
sample.event.value = 0;
if (callback) callback (DC_SAMPLE_EVENT, sample, userdata);
}
}
}
offset += parser->samplesize;
nsamples++;
// Some extra data.
if (parser->layout->tanks != UNSUPPORTED && (nsamples % 4) == 0) {
if ((parser->model == GENIUS || parser->model == HORIZON) &&
!mares_genius_isvalid (data + offset, AIRS_SIZE, AIRS_TYPE)) {
ERROR (abstract->context, "Invalid AIRS record.");
return DC_STATUS_DATAFORMAT;
}
// Pressure (1/100 bar).
unsigned int pressure = array_uint16_le(data + offset + marker + 0);
if (gasmix < parser->ntanks) {
sample.pressure.tank = gasmix;
sample.pressure.value = pressure / 100.0;
if (callback) callback (DC_SAMPLE_PRESSURE, sample, userdata);
if (callback) callback (DC_SAMPLE_PRESSURE, &sample, userdata);
} else if (pressure != 0) {
WARNING (abstract->context, "Invalid tank with non-zero pressure.");
}
offset += (parser->model == GENIUS || parser->model == HORIZON) ? AIRS_SIZE : 8;
offset += 8;
}
}
}
if (parser->model == GENIUS || parser->model == HORIZON) {
// Skip the DEND record.
if (!mares_genius_isvalid (data + offset, DEND_SIZE, DEND_TYPE)) {
ERROR (abstract->context, "Invalid DEND record.");
return DC_STATUS_SUCCESS;
}
static dc_status_t
mares_genius_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata)
{
mares_iconhd_parser_t *parser = (mares_iconhd_parser_t *) abstract;
const unsigned char *data = abstract->data;
const unsigned int size = abstract->size;
// Previous gas mix - initialize with impossible value
unsigned int gasmix_previous = 0xFFFFFFFF;
unsigned int tank = 0xFFFFFFFF;
// Skip the dive header.
unsigned int offset = parser->headersize;
if (offset + 4 > size) {
ERROR (abstract->context, "Buffer overflow detected!");
return DC_STATUS_DATAFORMAT;
}
// Check the profile type and version.
unsigned int profile_type = array_uint16_le (data + offset);
unsigned int profile_minor = data[offset + 2];
unsigned int profile_major = data[offset + 3];
if (profile_type > 1 ||
(profile_type == 0 && OBJVERSION(profile_major,profile_minor) > OBJVERSION(1,0)) ||
(profile_type == 1 && OBJVERSION(profile_major,profile_minor) > OBJVERSION(0,2))) {
ERROR (abstract->context, "Unsupported object type (%u) or version (%u.%u).",
profile_type, profile_major, profile_minor);
return DC_STATUS_DATAFORMAT;
}
offset += 4;
unsigned int time = 0;
unsigned int marker = 4;
while (offset < size) {
dc_sample_value_t sample = {0};
if (offset + 10 > size) {
ERROR (abstract->context, "Buffer overflow detected!");
return DC_STATUS_DATAFORMAT;
}
offset += DEND_SIZE;
// Get the record type and length.
unsigned int type = array_uint32_be(data + offset);
unsigned int length = 0;
switch (type) {
case DSTR_TYPE:
length = DSTR_SIZE;
break;
case TISS_TYPE:
length = TISS_SIZE;
break;
case DPRS_TYPE:
length = DPRS_SIZE;
break;
case SDPT_TYPE:
length = SDPT_SIZE;
break;
case AIRS_TYPE:
length = AIRS_SIZE;
break;
case DEND_TYPE:
length = DEND_SIZE;
break;
default:
ERROR (abstract->context, "Unknown record type (%08x).", type);
return DC_STATUS_DATAFORMAT;
}
if (offset + length > size) {
ERROR (abstract->context, "Buffer overflow detected!");
return DC_STATUS_DATAFORMAT;
}
unsigned int etype = array_uint32_be(data + offset + length - 4);
if (etype != type) {
ERROR (abstract->context, "Invalid record end type (%08x).", etype);
return DC_STATUS_DATAFORMAT;
}
unsigned short crc = array_uint16_le(data + offset + length - 6);
unsigned short ccrc = checksum_crc16_ccitt(data + offset + 4, length - 10, 0x0000, 0x0000);
if (crc != ccrc) {
ERROR (abstract->context, "Invalid record checksum (%04x %04x).", crc, ccrc);
return DC_STATUS_DATAFORMAT;
}
if (type == DPRS_TYPE || type == SDPT_TYPE) {
unsigned int depth = 0, temperature = 0;
unsigned int gasmix = 0, alarms = 0;
unsigned int decostop = 0, decodepth = 0, decotime = 0, tts = 0;
unsigned int bookmark = 0;
if (type == SDPT_TYPE) {
unsigned int misc = 0, deco = 0;
depth = array_uint16_le (data + offset + marker + 2);
temperature = array_uint16_le (data + offset + marker + 6);
alarms = array_uint32_le (data + offset + marker + 0x14);
misc = array_uint32_le (data + offset + marker + 0x18);
deco = array_uint32_le (data + offset + marker + 0x1C);
bookmark = (misc >> 2) & 0x0F;
gasmix = (misc >> 6) & 0x0F;
decostop = (misc >> 10) & 0x01;
if (decostop) {
decodepth = (deco >> 3) & 0x7F;
decotime = (deco >> 10) & 0xFF;
tts = (deco >> 18) & 0x3FFF;
} else {
decotime = deco & 0xFF;
}
} else {
unsigned int misc = 0;
depth = array_uint16_le (data + offset + marker + 0);
temperature = array_uint16_le (data + offset + marker + 4);
decotime = array_uint16_le (data + offset + marker + 0x0A);
alarms = array_uint32_le (data + offset + marker + 0x0C);
misc = array_uint32_le (data + offset + marker + 0x14);
bookmark = (misc >> 2) & 0x0F;
gasmix = (misc >> 6) & 0x0F;
decostop = (misc >> 18) & 0x01;
decodepth = (misc >> 19) & 0x7F;
}
// Time (seconds).
time += parser->interval;
sample.time = time;
if (callback) callback (DC_SAMPLE_TIME, &sample, userdata);
// Depth (1/10 m).
sample.depth = depth / 10.0;
if (callback) callback (DC_SAMPLE_DEPTH, &sample, userdata);
// Temperature (1/10 °C).
sample.temperature = temperature / 10.0;
if (callback) callback (DC_SAMPLE_TEMPERATURE, &sample, userdata);
// Current gas mix
if (parser->ngasmixes > 0) {
if (gasmix >= parser->ngasmixes) {
ERROR (abstract->context, "Invalid gas mix index.");
return DC_STATUS_DATAFORMAT;
}
if (gasmix != gasmix_previous) {
sample.gasmix = gasmix;
if (callback) callback (DC_SAMPLE_GASMIX, &sample, userdata);
gasmix_previous = gasmix;
}
}
// Current tank
tank = gasmix;
// Bookmark
if (bookmark) {
sample.event.type = SAMPLE_EVENT_BOOKMARK;
sample.event.time = 0;
sample.event.flags = 0;
sample.event.value = bookmark;
if (callback) callback (DC_SAMPLE_EVENT, &sample, userdata);
}
// Deco stop / NDL.
if (decostop) {
sample.deco.type = DC_DECO_DECOSTOP;
sample.deco.depth = decodepth;
} else {
sample.deco.type = DC_DECO_NDL;
sample.deco.depth = 0.0;
}
sample.deco.time = decotime * 60;
sample.deco.tts = tts;
if (callback) callback (DC_SAMPLE_DECO, &sample, userdata);
// Alarms
for (unsigned int v = alarms, i = 0; v; v >>= 1, ++i) {
if ((v & 1) == 0) {
continue;
}
switch (i) {
case ALARM_FAST_ASCENT:
case ALARM_UNCONTROLLED_ASCENT:
sample.event.type = SAMPLE_EVENT_ASCENT;
break;
case ALARM_MISSED_DECO:
case ALARM_DIVE_VIOLATION_DECO:
sample.event.type = SAMPLE_EVENT_CEILING;
break;
default:
sample.event.type = SAMPLE_EVENT_NONE;
break;
}
if (sample.event.type != SAMPLE_EVENT_NONE) {
sample.event.time = 0;
sample.event.flags = 0;
sample.event.value = 0;
if (callback) callback (DC_SAMPLE_EVENT, &sample, userdata);
}
}
} else if (type == AIRS_TYPE) {
// Pressure (1/100 bar).
unsigned int pressure = array_uint16_le(data + offset + marker + 0);
if (tank < parser->ntanks) {
sample.pressure.tank = tank;
sample.pressure.value = pressure / 100.0;
if (callback) callback (DC_SAMPLE_PRESSURE, &sample, userdata);
} else if (pressure != 0) {
WARNING (abstract->context, "Invalid tank with non-zero pressure.");
}
}
offset += length;
}
return DC_STATUS_SUCCESS;
}
static dc_status_t
mares_iconhd_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata)
{
mares_iconhd_parser_t *parser = (mares_iconhd_parser_t *) abstract;
// Cache the parser data.
dc_status_t rc = mares_iconhd_parser_cache (parser);
if (rc != DC_STATUS_SUCCESS)
return rc;
if (ISGENIUS(parser->model)) {
return mares_genius_foreach (abstract, callback, userdata);
} else {
return mares_iconhd_foreach (abstract, callback, userdata);
}
}

View File

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

View File

@ -59,7 +59,6 @@ struct mares_nemo_parser_t {
unsigned int extra;
};
static dc_status_t mares_nemo_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size);
static dc_status_t mares_nemo_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime);
static dc_status_t mares_nemo_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value);
static dc_status_t mares_nemo_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata);
@ -67,7 +66,6 @@ static dc_status_t mares_nemo_parser_samples_foreach (dc_parser_t *abstract, dc_
static const dc_parser_vtable_t mares_nemo_parser_vtable = {
sizeof(mares_nemo_parser_t),
DC_FAMILY_MARES_NEMO,
mares_nemo_parser_set_data, /* set_data */
NULL, /* set_clock */
NULL, /* set_atmospheric */
NULL, /* set_density */
@ -79,15 +77,16 @@ static const dc_parser_vtable_t mares_nemo_parser_vtable = {
dc_status_t
mares_nemo_parser_create (dc_parser_t **out, dc_context_t *context, unsigned int model)
mares_nemo_parser_create (dc_parser_t **out, dc_context_t *context, const unsigned char data[], size_t size, unsigned int model)
{
dc_status_t status = DC_STATUS_SUCCESS;
mares_nemo_parser_t *parser = NULL;
if (out == NULL)
return DC_STATUS_INVALIDARGS;
// Allocate memory.
parser = (mares_nemo_parser_t *) dc_parser_allocate (context, &mares_nemo_parser_vtable);
parser = (mares_nemo_parser_t *) dc_parser_allocate (context, &mares_nemo_parser_vtable, data, size);
if (parser == NULL) {
ERROR (context, "Failed to allocate memory.");
return DC_STATUS_NOMEMORY;
@ -98,70 +97,42 @@ mares_nemo_parser_create (dc_parser_t **out, dc_context_t *context, unsigned int
if (model == NEMOWIDE || model == NEMOAIR || model == PUCK || model == PUCKAIR)
freedive = GAUGE;
// Set the default values.
parser->model = model;
parser->freedive = freedive;
parser->mode = AIR;
parser->length = 0;
parser->sample_count = 0;
parser->sample_size = 0;
parser->header = 0;
parser->extra = 0;
*out = (dc_parser_t*) parser;
return DC_STATUS_SUCCESS;
}
static dc_status_t
mares_nemo_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size)
{
mares_nemo_parser_t *parser = (mares_nemo_parser_t *) abstract;
// Clear the previous state.
parser->base.data = NULL;
parser->base.size = 0;
parser->mode = AIR;
parser->length = 0;
parser->sample_count = 0;
parser->sample_size = 0;
parser->header = 0;
parser->extra = 0;
if (size == 0)
return DC_STATUS_SUCCESS;
if (size < 2 + 3)
return DC_STATUS_DATAFORMAT;
if (size < 2 + 3) {
status = DC_STATUS_DATAFORMAT;
goto error_free;
}
unsigned int length = array_uint16_le (data);
if (length > size)
return DC_STATUS_DATAFORMAT;
if (length > size) {
status = DC_STATUS_DATAFORMAT;
goto error_free;
}
unsigned int extra = 0;
const unsigned char marker[3] = {0xAA, 0xBB, 0xCC};
if (memcmp (data + length - 3, marker, sizeof (marker)) == 0) {
if (parser->model == PUCKAIR)
if (model == PUCKAIR)
extra = 7;
else
extra = 12;
}
if (length < 2 + extra + 3)
return DC_STATUS_DATAFORMAT;
if (length < 2 + extra + 3) {
status = DC_STATUS_DATAFORMAT;
goto error_free;
}
unsigned int mode = data[length - extra - 1];
unsigned int header_size = 53;
unsigned int sample_size = 2;
if (extra) {
if (parser->model == PUCKAIR)
if (model == PUCKAIR)
sample_size = 3;
else
sample_size = 5;
}
if (mode == parser->freedive) {
if (mode == freedive) {
header_size = 28;
sample_size = 6;
}
@ -169,12 +140,14 @@ mares_nemo_parser_set_data (dc_parser_t *abstract, const unsigned char *data, un
unsigned int nsamples = array_uint16_le (data + length - extra - 3);
unsigned int nbytes = 2 + nsamples * sample_size + header_size + extra;
if (length != nbytes)
return DC_STATUS_DATAFORMAT;
if (length != nbytes) {
status = DC_STATUS_DATAFORMAT;
goto error_free;
}
// Store the new state.
parser->base.data = data;
parser->base.size = size;
// Set the default values.
parser->model = model;
parser->freedive = freedive;
parser->mode = mode;
parser->length = length;
parser->sample_count = nsamples;
@ -182,7 +155,13 @@ mares_nemo_parser_set_data (dc_parser_t *abstract, const unsigned char *data, un
parser->header = header_size;
parser->extra = extra;
*out = (dc_parser_t*) parser;
return DC_STATUS_SUCCESS;
error_free:
dc_parser_deallocate ((dc_parser_t *) parser);
return status;
}
@ -251,6 +230,7 @@ mares_nemo_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsign
}
gasmix->helium = 0.0;
gasmix->nitrogen = 1.0 - gasmix->oxygen - gasmix->helium;
gasmix->usage = DC_USAGE_NONE;
break;
case DC_FIELD_TANK_COUNT:
if (parser->extra)
@ -290,6 +270,7 @@ mares_nemo_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsign
} else {
tank->gasmix = DC_GASMIX_UNKNOWN;
}
tank->usage = DC_USAGE_NONE;
break;
case DC_FIELD_TEMPERATURE_MINIMUM:
*((double *) value) = (signed char) p[53 - 11];
@ -382,17 +363,17 @@ mares_nemo_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t c
// Time (seconds).
time += 20;
sample.time = time;
if (callback) callback (DC_SAMPLE_TIME, sample, userdata);
sample.time = time * 1000;
if (callback) callback (DC_SAMPLE_TIME, &sample, userdata);
// Depth (1/10 m).
sample.depth = depth / 10.0;
if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata);
if (callback) callback (DC_SAMPLE_DEPTH, &sample, userdata);
// Gas change.
if (gasmix != gasmix_previous) {
sample.gasmix = gasmix;
if (callback) callback (DC_SAMPLE_GASMIX, sample, userdata);
if (callback) callback (DC_SAMPLE_GASMIX, &sample, userdata);
gasmix_previous = gasmix;
}
@ -402,7 +383,7 @@ mares_nemo_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t c
sample.event.time = 0;
sample.event.flags = 0;
sample.event.value = ascent;
if (callback) callback (DC_SAMPLE_EVENT, sample, userdata);
if (callback) callback (DC_SAMPLE_EVENT, &sample, userdata);
}
// Deco violation
@ -411,7 +392,7 @@ mares_nemo_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t c
sample.event.time = 0;
sample.event.flags = 0;
sample.event.value = 0;
if (callback) callback (DC_SAMPLE_EVENT, sample, userdata);
if (callback) callback (DC_SAMPLE_EVENT, &sample, userdata);
}
// Deco stop
@ -422,20 +403,21 @@ mares_nemo_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t c
}
sample.deco.time = 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);
// Pressure (1 bar).
if (parser->sample_size == 3) {
sample.pressure.tank = 0;
sample.pressure.value = data[idx + 2];
if (callback) callback (DC_SAMPLE_PRESSURE, sample, userdata);
if (callback) callback (DC_SAMPLE_PRESSURE, &sample, userdata);
} else if (parser->sample_size == 5) {
unsigned int type = (time / 20) % 3;
if (type == 0) {
pressure -= data[idx + 2] * 100;
sample.pressure.tank = 0;
sample.pressure.value = pressure / 100.0;
if (callback) callback (DC_SAMPLE_PRESSURE, sample, userdata);
if (callback) callback (DC_SAMPLE_PRESSURE, &sample, userdata);
}
}
}
@ -459,12 +441,12 @@ mares_nemo_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t c
// Surface Time (seconds).
time += surftime;
sample.time = time;
if (callback) callback (DC_SAMPLE_TIME, sample, userdata);
sample.time = time * 1000;
if (callback) callback (DC_SAMPLE_TIME, &sample, userdata);
// Surface Depth (0 m).
sample.depth = 0.0;
if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata);
if (callback) callback (DC_SAMPLE_DEPTH, &sample, userdata);
if (profiles) {
// Get the freedive sample interval for this model.
@ -500,12 +482,12 @@ mares_nemo_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t c
time += interval;
if (time > maxtime)
time = maxtime; // Adjust the last sample.
sample.time = time;
if (callback) callback (DC_SAMPLE_TIME, sample, userdata);
sample.time = time * 1000;
if (callback) callback (DC_SAMPLE_TIME, &sample, userdata);
// Depth (1/10 m).
sample.depth = depth / 10.0;
if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata);
if (callback) callback (DC_SAMPLE_DEPTH, &sample, userdata);
}
// Verify that the number of samples in the profile data
@ -519,12 +501,12 @@ mares_nemo_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t c
} else {
// Dive Time (seconds).
time += divetime;
sample.time = time;
if (callback) callback (DC_SAMPLE_TIME, sample, userdata);
sample.time = time * 1000;
if (callback) callback (DC_SAMPLE_TIME, &sample, userdata);
// Maximum Depth (1/10 m).
sample.depth = maxdepth / 10.0;
if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata);
if (callback) callback (DC_SAMPLE_DEPTH, &sample, userdata);
}
}
}

View File

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

View File

@ -58,7 +58,6 @@ struct mclean_extreme_parser_t {
unsigned int gasmix[NGASMIXES];
};
static dc_status_t mclean_extreme_parser_set_data(dc_parser_t *abstract, const unsigned char *data, unsigned int size);
static dc_status_t mclean_extreme_parser_get_datetime(dc_parser_t *abstract, dc_datetime_t *datetime);
static dc_status_t mclean_extreme_parser_get_field(dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value);
static dc_status_t mclean_extreme_parser_samples_foreach(dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata);
@ -66,7 +65,6 @@ static dc_status_t mclean_extreme_parser_samples_foreach(dc_parser_t *abstract,
static const dc_parser_vtable_t mclean_extreme_parser_vtable = {
sizeof(mclean_extreme_parser_t),
DC_FAMILY_MCLEAN_EXTREME,
mclean_extreme_parser_set_data, /* set_data */
NULL, /* set_clock */
NULL, /* set_atmospheric */
NULL, /* set_density */
@ -77,7 +75,7 @@ static const dc_parser_vtable_t mclean_extreme_parser_vtable = {
};
dc_status_t
mclean_extreme_parser_create(dc_parser_t **out, dc_context_t *context)
mclean_extreme_parser_create(dc_parser_t **out, dc_context_t *context, const unsigned char data[], size_t size)
{
mclean_extreme_parser_t *parser = NULL;
@ -86,7 +84,7 @@ mclean_extreme_parser_create(dc_parser_t **out, dc_context_t *context)
}
// Allocate memory.
parser = (mclean_extreme_parser_t *)dc_parser_allocate(context, &mclean_extreme_parser_vtable);
parser = (mclean_extreme_parser_t *)dc_parser_allocate(context, &mclean_extreme_parser_vtable, data, size);
if (parser == NULL) {
ERROR(context, "Failed to allocate memory.");
return DC_STATUS_NOMEMORY;
@ -104,21 +102,6 @@ mclean_extreme_parser_create(dc_parser_t **out, dc_context_t *context)
return DC_STATUS_SUCCESS;
}
static dc_status_t
mclean_extreme_parser_set_data(dc_parser_t *abstract, const unsigned char *data, unsigned int size)
{
mclean_extreme_parser_t *parser = (mclean_extreme_parser_t *)abstract;
// Reset the cache.
parser->cached = 0;
parser->ngasmixes = 0;
for (unsigned int i = 0; i < NGASMIXES; ++i) {
parser->gasmix[i] = INVALID;
}
return DC_STATUS_SUCCESS;
}
static dc_status_t
mclean_extreme_parser_get_datetime(dc_parser_t *abstract, dc_datetime_t *datetime)
{
@ -221,6 +204,7 @@ mclean_extreme_parser_get_field(dc_parser_t *abstract, dc_field_type_t type, uns
*((unsigned int *)value) = parser->ngasmixes;
break;
case DC_FIELD_GASMIX:
gasmix->usage = DC_USAGE_NONE;
gasmix->helium = 0.01 * abstract->data[0x0001 + 1 + 2 * parser->gasmix[flags]];
gasmix->oxygen = 0.01 * abstract->data[0x0001 + 0 + 2 * parser->gasmix[flags]];
gasmix->nitrogen = 1.0 - gasmix->oxygen - gasmix->helium;
@ -269,14 +253,14 @@ mclean_extreme_parser_samples_foreach(dc_parser_t *abstract, dc_sample_callback_
const unsigned int setpoint = abstract->data[0x0013 + sp_index];
time += interval;
sample.time = time;
if (callback) callback(DC_SAMPLE_TIME, sample, userdata);
sample.time = time * 1000;
if (callback) callback(DC_SAMPLE_TIME, &sample, userdata);
sample.depth = 0.1 * depth;
if (callback) callback(DC_SAMPLE_DEPTH, sample, userdata);
if (callback) callback(DC_SAMPLE_DEPTH, &sample, userdata);
sample.temperature = temperature;
if (callback) callback(DC_SAMPLE_TEMPERATURE, sample, userdata);
if (callback) callback(DC_SAMPLE_TEMPERATURE, &sample, userdata);
if (gasmix_id != gasmix_previous) {
// Find the gasmix in the list.
@ -298,13 +282,13 @@ mclean_extreme_parser_samples_foreach(dc_parser_t *abstract, dc_sample_callback_
}
sample.gasmix = idx;
if (callback) callback(DC_SAMPLE_GASMIX, sample, userdata);
if (callback) callback(DC_SAMPLE_GASMIX, &sample, userdata);
gasmix_previous = gasmix_id;
}
if (ccr) {
sample.setpoint = 0.01 * setpoint;
if (callback) callback(DC_SAMPLE_SETPOINT, sample, userdata);
if (callback) callback(DC_SAMPLE_SETPOINT, &sample, userdata);
}
offset += SZ_SAMPLE;

View File

@ -86,6 +86,8 @@ static const oceanic_common_device_vtable_t oceanic_atom2_device_vtable = {
NULL, /* timesync */
oceanic_atom2_device_close /* close */
},
oceanic_common_device_devinfo,
oceanic_common_device_pointers,
oceanic_common_device_logbook,
oceanic_common_device_profile,
};
@ -98,10 +100,11 @@ static const oceanic_common_layout_t aeris_f10_layout = {
0x0100, /* rb_logbook_begin */
0x0D80, /* rb_logbook_end */
32, /* rb_logbook_entry_size */
1, /* rb_logbook_direction */
0x0D80, /* rb_profile_begin */
0x10000, /* rb_profile_end */
0, /* pt_mode_global */
2, /* pt_mode_logbook */
3, /* pt_mode_logbook */
0, /* pt_mode_serial */
};
@ -113,10 +116,11 @@ static const oceanic_common_layout_t aeris_f11_layout = {
0x0100, /* rb_logbook_begin */
0x0D80, /* rb_logbook_end */
32, /* rb_logbook_entry_size */
1, /* rb_logbook_direction */
0x0D80, /* rb_profile_begin */
0x20000, /* rb_profile_end */
0, /* pt_mode_global */
3, /* pt_mode_logbook */
2, /* pt_mode_logbook */
0, /* pt_mode_serial */
};
@ -128,6 +132,7 @@ static const oceanic_common_layout_t oceanic_default_layout = {
0x0240, /* rb_logbook_begin */
0x0A40, /* rb_logbook_end */
8, /* rb_logbook_entry_size */
1, /* rb_logbook_direction */
0x0A40, /* rb_profile_begin */
0x10000, /* rb_profile_end */
0, /* pt_mode_global */
@ -143,6 +148,7 @@ static const oceanic_common_layout_t oceanic_atom1_layout = {
0x0240, /* rb_logbook_begin */
0x0440, /* rb_logbook_end */
8, /* rb_logbook_entry_size */
1, /* rb_logbook_direction */
0x0440, /* rb_profile_begin */
0x8000, /* rb_profile_end */
0, /* pt_mode_global */
@ -158,6 +164,7 @@ static const oceanic_common_layout_t oceanic_atom2a_layout = {
0x0240, /* rb_logbook_begin */
0x0A40, /* rb_logbook_end */
8, /* rb_logbook_entry_size */
1, /* rb_logbook_direction */
0x0A40, /* rb_profile_begin */
0xFE00, /* rb_profile_end */
0, /* pt_mode_global */
@ -173,6 +180,7 @@ static const oceanic_common_layout_t oceanic_atom2b_layout = {
0x0240, /* rb_logbook_begin */
0x0A40, /* rb_logbook_end */
8, /* rb_logbook_entry_size */
1, /* rb_logbook_direction */
0x0A40, /* rb_profile_begin */
0xFE00, /* rb_profile_end */
0, /* pt_mode_global */
@ -188,6 +196,7 @@ static const oceanic_common_layout_t oceanic_atom2c_layout = {
0x0240, /* rb_logbook_begin */
0x0A40, /* rb_logbook_end */
8, /* rb_logbook_entry_size */
1, /* rb_logbook_direction */
0x0A40, /* rb_profile_begin */
0xFFF0, /* rb_profile_end */
0, /* pt_mode_global */
@ -203,6 +212,7 @@ static const oceanic_common_layout_t sherwood_wisdom_layout = {
0x03D0, /* rb_logbook_begin */
0x0A40, /* rb_logbook_end */
8, /* rb_logbook_entry_size */
1, /* rb_logbook_direction */
0x0A40, /* rb_profile_begin */
0xFE00, /* rb_profile_end */
0, /* pt_mode_global */
@ -218,6 +228,7 @@ static const oceanic_common_layout_t oceanic_proplus3_layout = {
0x03E0, /* rb_logbook_begin */
0x0A40, /* rb_logbook_end */
8, /* rb_logbook_entry_size */
1, /* rb_logbook_direction */
0x0A40, /* rb_profile_begin */
0xFE00, /* rb_profile_end */
0, /* pt_mode_global */
@ -233,6 +244,7 @@ static const oceanic_common_layout_t tusa_zenair_layout = {
0x0240, /* rb_logbook_begin */
0x0A40, /* rb_logbook_end */
8, /* rb_logbook_entry_size */
1, /* rb_logbook_direction */
0x0A40, /* rb_profile_begin */
0xFE00, /* rb_profile_end */
0, /* pt_mode_global */
@ -248,6 +260,7 @@ static const oceanic_common_layout_t oceanic_oc1_layout = {
0x0240, /* rb_logbook_begin */
0x0A40, /* rb_logbook_end */
8, /* rb_logbook_entry_size */
1, /* rb_logbook_direction */
0x0A40, /* rb_profile_begin */
0x1FE00, /* rb_profile_end */
0, /* pt_mode_global */
@ -263,6 +276,7 @@ static const oceanic_common_layout_t oceanic_oci_layout = {
0x10C0, /* rb_logbook_begin */
0x1400, /* rb_logbook_end */
8, /* rb_logbook_entry_size */
1, /* rb_logbook_direction */
0x1400, /* rb_profile_begin */
0x1FE00, /* rb_profile_end */
0, /* pt_mode_global */
@ -278,6 +292,7 @@ static const oceanic_common_layout_t oceanic_atom3_layout = {
0x0400, /* rb_logbook_begin */
0x0A40, /* rb_logbook_end */
8, /* rb_logbook_entry_size */
1, /* rb_logbook_direction */
0x0A40, /* rb_profile_begin */
0x1FE00, /* rb_profile_end */
0, /* pt_mode_global */
@ -293,6 +308,7 @@ static const oceanic_common_layout_t oceanic_vt4_layout = {
0x0420, /* rb_logbook_begin */
0x0A40, /* rb_logbook_end */
8, /* rb_logbook_entry_size */
1, /* rb_logbook_direction */
0x0A40, /* rb_profile_begin */
0x1FE00, /* rb_profile_end */
0, /* pt_mode_global */
@ -308,6 +324,7 @@ static const oceanic_common_layout_t hollis_tx1_layout = {
0x0780, /* rb_logbook_begin */
0x1000, /* rb_logbook_end */
8, /* rb_logbook_entry_size */
1, /* rb_logbook_direction */
0x1000, /* rb_profile_begin */
0x40000, /* rb_profile_end */
0, /* pt_mode_global */
@ -323,6 +340,7 @@ static const oceanic_common_layout_t oceanic_veo1_layout = {
0x0400, /* rb_logbook_begin */
0x0400, /* rb_logbook_end */
8, /* rb_logbook_entry_size */
1, /* rb_logbook_direction */
0x0400, /* rb_profile_begin */
0x0400, /* rb_profile_end */
0, /* pt_mode_global */
@ -338,8 +356,9 @@ static const oceanic_common_layout_t oceanic_reactpro_layout = {
0x0400, /* rb_logbook_begin */
0x0600, /* rb_logbook_end */
8, /* rb_logbook_entry_size */
1, /* rb_logbook_direction */
0x0600, /* rb_profile_begin */
0xFFF0, /* rb_profile_end */
0xFE00, /* rb_profile_end */
1, /* pt_mode_global */
1, /* pt_mode_logbook */
0, /* pt_mode_serial */
@ -353,6 +372,7 @@ static const oceanic_common_layout_t oceanic_proplusx_layout = {
0x1000, /* rb_logbook_begin */
0x10000, /* rb_logbook_end */
16, /* rb_logbook_entry_size */
1, /* rb_logbook_direction */
0x40000, /* rb_profile_begin */
0x440000, /* rb_profile_end */
0, /* pt_mode_global */
@ -368,6 +388,7 @@ static const oceanic_common_layout_t aqualung_i770r_layout = {
0x2000, /* rb_logbook_begin */
0x10000, /* rb_logbook_end */
16, /* rb_logbook_entry_size */
1, /* rb_logbook_direction */
0x40000, /* rb_profile_begin */
0x640000, /* rb_profile_end */
0, /* pt_mode_global */
@ -383,6 +404,7 @@ static const oceanic_common_layout_t aeris_a300cs_layout = {
0x0900, /* rb_logbook_begin */
0x1000, /* rb_logbook_end */
16, /* rb_logbook_entry_size */
1, /* rb_logbook_direction */
0x1000, /* rb_profile_begin */
0x3FE00, /* rb_profile_end */
0, /* pt_mode_global */
@ -398,6 +420,7 @@ static const oceanic_common_layout_t aqualung_i450t_layout = {
0x10C0, /* rb_logbook_begin */
0x1400, /* rb_logbook_end */
16, /* rb_logbook_entry_size */
1, /* rb_logbook_direction */
0x1400, /* rb_profile_begin */
0x3FE00, /* rb_profile_end */
0, /* pt_mode_global */
@ -415,13 +438,14 @@ static const oceanic_common_version_t versions[] = {
{"MANTA R\0\0 512K", 0, MANTA, &oceanic_atom2c_layout},
{"2M ATOM r\0\0 512K", 0x3349, ATOM2, &oceanic_atom2a_layout},
{"2M ATOM r\0\0 512K", 0, ATOM2, &oceanic_atom2c_layout},
{"OCE GEO R\0\0 512K", 0x3242, GEO, &oceanic_atom2a_layout},
{"OCE GEO R\0\0 512K", 0, GEO, &oceanic_atom2c_layout},
{"INSIGHT2 \0\0 512K", 0, INSIGHT2, &oceanic_atom2a_layout},
{"OCEVEO30 \0\0 512K", 0, VEO30, &oceanic_atom2a_layout},
{"ATMOSAI R\0\0 512K", 0, ATMOSAI2, &oceanic_atom2a_layout},
{"PROPLUS2 \0\0 512K", 0, PROPLUS21, &oceanic_atom2a_layout},
{"OCEGEO20 \0\0 512K", 0, GEO20, &oceanic_atom2a_layout},
{"OCE GEO R\0\0 512K", 0, GEO, &oceanic_atom2a_layout},
{"AQUAI200 \0\0 512K", 0, I200, &oceanic_atom2a_layout},
{"AQUA200C \0\0 512K", 0, I200C, &oceanic_atom2a_layout},

View File

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

View File

@ -35,6 +35,12 @@
#define GAUGE 1
#define FREEDIVE 2
#define DSX_CC 0
#define DSX_OC 1
#define DSX_SIDEMOUNT 2
#define DSX_SIDEGAUGE 3
#define DSX_GAUGE 4
#define NGASMIXES 6
#define HEADER 1
@ -45,6 +51,7 @@ typedef struct oceanic_atom2_parser_t oceanic_atom2_parser_t;
struct oceanic_atom2_parser_t {
dc_parser_t base;
unsigned int model;
unsigned int logbooksize;
unsigned int headersize;
unsigned int footersize;
// Cached fields.
@ -59,7 +66,6 @@ struct oceanic_atom2_parser_t {
double maxdepth;
};
static dc_status_t oceanic_atom2_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size);
static dc_status_t oceanic_atom2_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime);
static dc_status_t oceanic_atom2_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value);
static dc_status_t oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata);
@ -67,7 +73,6 @@ static dc_status_t oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract,
static const dc_parser_vtable_t oceanic_atom2_parser_vtable = {
sizeof(oceanic_atom2_parser_t),
DC_FAMILY_OCEANIC_ATOM2,
oceanic_atom2_parser_set_data, /* set_data */
NULL, /* set_clock */
NULL, /* set_atmospheric */
NULL, /* set_density */
@ -79,7 +84,7 @@ static const dc_parser_vtable_t oceanic_atom2_parser_vtable = {
dc_status_t
oceanic_atom2_parser_create (dc_parser_t **out, dc_context_t *context, unsigned int model)
oceanic_atom2_parser_create (dc_parser_t **out, dc_context_t *context, const unsigned char data[], size_t size, unsigned int model)
{
oceanic_atom2_parser_t *parser = NULL;
@ -87,7 +92,7 @@ oceanic_atom2_parser_create (dc_parser_t **out, dc_context_t *context, unsigned
return DC_STATUS_INVALIDARGS;
// Allocate memory.
parser = (oceanic_atom2_parser_t *) dc_parser_allocate (context, &oceanic_atom2_parser_vtable);
parser = (oceanic_atom2_parser_t *) dc_parser_allocate (context, &oceanic_atom2_parser_vtable, data, size);
if (parser == NULL) {
ERROR (context, "Failed to allocate memory.");
return DC_STATUS_NOMEMORY;
@ -95,6 +100,7 @@ oceanic_atom2_parser_create (dc_parser_t **out, dc_context_t *context, unsigned
// Set the default values.
parser->model = model;
parser->logbooksize = 0;
parser->headersize = 9 * PAGESIZE / 2;
parser->footersize = 2 * PAGESIZE / 2;
if (model == DATAMASK || model == COMPUMASK ||
@ -133,6 +139,14 @@ oceanic_atom2_parser_create (dc_parser_t **out, dc_context_t *context, unsigned
} else if (model == I550C || model == WISDOM4 ||
model == I200CV2) {
parser->headersize = 5 * PAGESIZE / 2;
} else if (model == I330R) {
parser->logbooksize = 64;
parser->headersize = parser->logbooksize + 80;
parser->footersize = 48;
} else if (model == DSX) {
parser->logbooksize = 512;
parser->headersize = parser->logbooksize + 256;
parser->footersize = 64;
}
parser->cached = 0;
@ -153,28 +167,6 @@ oceanic_atom2_parser_create (dc_parser_t **out, dc_context_t *context, unsigned
}
static dc_status_t
oceanic_atom2_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size)
{
oceanic_atom2_parser_t *parser = (oceanic_atom2_parser_t *) abstract;
// Reset the cache.
parser->cached = 0;
parser->header = 0;
parser->footer = 0;
parser->mode = NORMAL;
parser->ngasmixes = 0;
for (unsigned int i = 0; i < NGASMIXES; ++i) {
parser->oxygen[i] = 0;
parser->helium[i] = 0;
}
parser->divetime = 0;
parser->maxdepth = 0.0;
return DC_STATUS_SUCCESS;
}
static dc_status_t
oceanic_atom2_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime)
{
@ -194,8 +186,18 @@ oceanic_atom2_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetim
if (datetime) {
// AM/PM bit of the 12-hour clock.
unsigned int pm = p[1] & 0x80;
unsigned int have_ampm = 1;
switch (parser->model) {
case I330R:
case DSX:
datetime->year = p[7] + 2000;
datetime->month = p[6];
datetime->day = p[5];
datetime->hour = p[3];
datetime->minute = p[4];
have_ampm = 0;
break;
case OC1A:
case OC1B:
case OC1C:
@ -304,9 +306,11 @@ oceanic_atom2_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetim
datetime->timezone = DC_TIMEZONE_NONE;
// Convert to a 24-hour clock.
datetime->hour %= 12;
if (pm)
datetime->hour += 12;
if (have_ampm) {
datetime->hour %= 12;
if (pm)
datetime->hour += 12;
}
/*
* Workaround for the year 2010 problem.
@ -381,6 +385,10 @@ oceanic_atom2_parser_cache (oceanic_atom2_parser_t *parser)
} else if (parser->model == VEO20 || parser->model == VEO30 ||
parser->model == OCS) {
mode = (data[1] & 0x60) >> 5;
} else if (parser->model == I330R) {
mode = data[2];
} else if (parser->model == DSX) {
mode = data[45];
}
// Get the gas mixes.
@ -438,6 +446,17 @@ oceanic_atom2_parser_cache (oceanic_atom2_parser_t *parser)
} else if (parser->model == WISDOM4) {
o2_offset = header + 4;
ngasmixes = 1;
} else if (parser->model == I330R) {
ngasmixes = 3;
o2_offset = parser->logbooksize + 16;
} else if (parser->model == DSX) {
if (mode < DSX_SIDEGAUGE) {
o2_offset = parser->logbooksize + 0x89 + mode * 16;
he_offset = parser->logbooksize + 0xB9 + mode * 16;
ngasmixes = 6;
} else {
ngasmixes = 0;
}
} else {
o2_offset = header + 4;
ngasmixes = 3;
@ -451,6 +470,10 @@ oceanic_atom2_parser_cache (oceanic_atom2_parser_t *parser)
for (unsigned int i = 0; i < ngasmixes; ++i) {
if (data[o2_offset + i * o2_step]) {
parser->oxygen[i] = data[o2_offset + i * o2_step];
// The i330R uses 20 as "Air" and 21 as 21% Nitrox
if (parser->model == I330R && parser->oxygen[i] == 20) {
parser->oxygen[i] = 21;
}
} else {
parser->oxygen[i] = 21;
}
@ -509,13 +532,23 @@ oceanic_atom2_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, uns
parser->model == F11A || parser->model == F11B ||
parser->model == MUNDIAL2 || parser->model == MUNDIAL3)
*((double *) value) = array_uint16_le (data + 4) / 16.0 * FEET;
else if (parser->model == I330R || parser->model == DSX)
*((double *) value) = array_uint16_le (data + parser->footer + 10) / 10.0 * FEET;
else
*((double *) value) = (array_uint16_le (data + parser->footer + 4) & 0x0FFF) / 16.0 * FEET;
break;
case DC_FIELD_AVGDEPTH:
if (parser->model == I330R || parser->model == DSX) {
*((double *) value) = array_uint16_le (data + parser->footer + 12) / 10.0 * FEET;
} else {
return DC_STATUS_UNSUPPORTED;
}
break;
case DC_FIELD_GASMIX_COUNT:
*((unsigned int *) value) = parser->ngasmixes;
break;
case DC_FIELD_GASMIX:
gasmix->usage = DC_USAGE_NONE;
gasmix->oxygen = parser->oxygen[flags] / 100.0;
gasmix->helium = parser->helium[flags] / 100.0;
gasmix->nitrogen = 1.0 - gasmix->oxygen - gasmix->helium;
@ -529,23 +562,49 @@ oceanic_atom2_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, uns
water->type = DC_WATER_SALT;
}
water->density = 0.0;
} else if (parser->model == I330R || parser->model == DSX) {
unsigned int settings = array_uint32_le (data + parser->logbooksize + 12);
if (settings & 0x10000) {
water->type = DC_WATER_FRESH;
} else {
water->type = DC_WATER_SALT;
}
water->density = 0.0;
} else {
return DC_STATUS_UNSUPPORTED;
}
break;
case DC_FIELD_DIVEMODE:
switch (parser->mode) {
case NORMAL:
*((unsigned int *) value) = DC_DIVEMODE_OC;
break;
case GAUGE:
*((unsigned int *) value) = DC_DIVEMODE_GAUGE;
break;
case FREEDIVE:
*((unsigned int *) value) = DC_DIVEMODE_FREEDIVE;
break;
default:
return DC_STATUS_DATAFORMAT;
if (parser->model == DSX) {
switch (parser->mode) {
case DSX_OC:
case DSX_SIDEMOUNT:
*((unsigned int *) value) = DC_DIVEMODE_OC;
break;
case DSX_SIDEGAUGE:
case DSX_GAUGE:
*((unsigned int *) value) = DC_DIVEMODE_GAUGE;
break;
case DSX_CC:
*((unsigned int *) value) = DC_DIVEMODE_CCR;
break;
default:
return DC_STATUS_DATAFORMAT;
}
} else {
switch (parser->mode) {
case NORMAL:
*((unsigned int *) value) = DC_DIVEMODE_OC;
break;
case GAUGE:
*((unsigned int *) value) = DC_DIVEMODE_GAUGE;
break;
case FREEDIVE:
*((unsigned int *) value) = DC_DIVEMODE_FREEDIVE;
break;
default:
return DC_STATUS_DATAFORMAT;
}
}
break;
default:
@ -586,7 +645,7 @@ oceanic_atom2_parser_vendor (oceanic_atom2_parser_t *parser, const unsigned char
sample.vendor.type = SAMPLE_VENDOR_OCEANIC_ATOM2;
sample.vendor.size = length;
sample.vendor.data = data + offset;
if (callback) callback (DC_SAMPLE_VENDOR, sample, userdata);
if (callback) callback (DC_SAMPLE_VENDOR, &sample, userdata);
offset += length;
}
@ -608,30 +667,25 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_
unsigned int extratime = 0;
unsigned int time = 0;
unsigned int interval = 1;
unsigned int samplerate = 1;
unsigned int interval = 1000;
if (parser->mode != FREEDIVE) {
unsigned int offset = 0x17;
if (parser->model == A300CS || parser->model == VTX ||
parser->model == I450T || parser->model == I750TC ||
parser->model == PROPLUSX || parser->model == I770R ||
parser->model == SAGE || parser->model == BEACON)
offset = 0x1f;
const unsigned int intervals[] = {2, 15, 30, 60};
unsigned int idx = data[offset] & 0x03;
interval = intervals[idx];
if (parser->model == I330R || parser->model == DSX) {
interval = data[parser->logbooksize + 36] * 1000;
} else {
unsigned int offset = 0x17;
if (parser->model == A300CS || parser->model == VTX ||
parser->model == I450T || parser->model == I750TC ||
parser->model == PROPLUSX || parser->model == I770R ||
parser->model == SAGE || parser->model == BEACON)
offset = 0x1f;
const unsigned int intervals[] = {2000, 15000, 30000, 60000};
unsigned int idx = data[offset] & 0x03;
interval = intervals[idx];
}
} else if (parser->model == F11A || parser->model == F11B) {
const unsigned int intervals[] = {1, 1, 1, 2};
const unsigned int samplerates[] = {4, 2, 1, 1};
const unsigned int intervals[] = {250, 500, 1000, 2000};
unsigned int idx = data[0x29] & 0x03;
interval = intervals[idx];
samplerate = samplerates[idx];
if (samplerate > 1) {
// Some models supports multiple samples per second.
// Since our smallest unit of time is one second, we can't
// represent this, and the extra samples will get dropped.
WARNING(abstract->context, "Multiple samples per second are not supported!");
}
}
unsigned int samplesize = PAGESIZE / 2;
@ -650,8 +704,10 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_
parser->model == I750TC || parser->model == PROPLUSX ||
parser->model == I770R || parser->model == I470TC ||
parser->model == SAGE || parser->model == BEACON ||
parser->model == GEOAIR) {
parser->model == GEOAIR || parser->model == I330R) {
samplesize = PAGESIZE;
} else if (parser->model == DSX) {
samplesize = 32;
}
unsigned int have_temperature = 1, have_pressure = 1;
@ -666,7 +722,8 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_
parser->model == I200 || parser->model == I100 ||
parser->model == I300C || parser->model == TALIS ||
parser->model == I200C || parser->model == I200CV2 ||
parser->model == GEO40 || parser->model == VEO40) {
parser->model == GEO40 || parser->model == VEO40 ||
parser->model == I330R) {
have_pressure = 0;
}
@ -677,7 +734,7 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_
}
// Initial tank pressure.
unsigned int tank = 0;
unsigned int tank = 1;
unsigned int pressure = 0;
if (have_pressure) {
unsigned int idx = 2;
@ -732,17 +789,17 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_
if (sampletype == 0xAA) {
if (parser->model == DATAMASK || parser->model == COMPUMASK) {
// Tank pressure (1 psi) and number
tank = 0;
tank = 1;
pressure = (((data[offset + 7] << 8) + data[offset + 6]) & 0x0FFF);
} else if (parser->model == A300CS || parser->model == VTX ||
parser->model == I750TC || parser->model == SAGE ||
parser->model == BEACON) {
// Tank pressure (1 psi) and number (one based index)
tank = (data[offset + 1] & 0x03) - 1;
tank = data[offset + 1] & 0x03;
pressure = ((data[offset + 7] << 8) + data[offset + 6]) & 0x0FFF;
} else {
// Tank pressure (2 psi) and number (one based index)
tank = (data[offset + 1] & 0x03) - 1;
tank = data[offset + 1] & 0x03;
if (parser->model == ATOM2 || parser->model == EPICA || parser->model == EPICB)
pressure = (((data[offset + 3] << 8) + data[offset + 4]) & 0x0FFF) * 2;
else
@ -752,14 +809,14 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_
// The surface time is not always a nice multiple of the samplerate.
// The number of inserted surface samples is therefore rounded down
// to keep the timestamps aligned at multiples of the samplerate.
unsigned int surftime = 60 * bcd2dec (data[offset + 1]) + bcd2dec (data[offset + 2]);
unsigned int surftime = (60 * bcd2dec (data[offset + 1]) + bcd2dec (data[offset + 2])) * 1000;
unsigned int nsamples = surftime / interval;
for (unsigned int i = 0; i < nsamples; ++i) {
// Time
time += interval;
sample.time = time;
if (callback) callback (DC_SAMPLE_TIME, sample, userdata);
if (callback) callback (DC_SAMPLE_TIME, &sample, userdata);
// Vendor specific data
if (i == 0) {
@ -771,25 +828,18 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_
// Depth
sample.depth = 0.0;
if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata);
if (callback) callback (DC_SAMPLE_DEPTH, &sample, userdata);
complete = 1;
}
extratime += surftime;
} else {
// Skip the extra samples.
if ((count % samplerate) != 0) {
offset += samplesize;
count++;
continue;
}
// Time.
if (parser->model == I450T || parser->model == I470TC) {
unsigned int minute = bcd2dec(data[offset + 0]);
unsigned int hour = bcd2dec(data[offset + 1] & 0x0F);
unsigned int second = bcd2dec(data[offset + 2]);
unsigned int timestamp = (hour * 3600) + (minute * 60 ) + second + extratime;
unsigned int timestamp = ((hour * 3600) + (minute * 60 ) + second) * 1000 + extratime;
if (timestamp < time) {
ERROR (abstract->context, "Timestamp moved backwards.");
return DC_STATUS_DATAFORMAT;
@ -803,7 +853,7 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_
time += interval;
}
sample.time = time;
if (callback) callback (DC_SAMPLE_TIME, sample, userdata);
if (callback) callback (DC_SAMPLE_TIME, &sample, userdata);
// Vendor specific data
oceanic_atom2_parser_vendor (parser,
@ -842,6 +892,8 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_
parser->model == I770R|| parser->model == SAGE ||
parser->model == BEACON) {
temperature = data[offset + 11];
} else if (parser->model == I330R || parser->model == DSX) {
temperature = array_uint16_le(data + offset + 10);
} else {
unsigned int sign;
if (parser->model == DG03 || parser->model == PROPLUS3 ||
@ -864,8 +916,12 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_
else
temperature += (data[offset + 7] & 0x0C) >> 2;
}
sample.temperature = (temperature - 32.0) * (5.0 / 9.0);
if (callback) callback (DC_SAMPLE_TEMPERATURE, sample, userdata);
if (parser->model == I330R || parser->model == DSX) {
sample.temperature = ((temperature / 10.0) - 32.0) * (5.0 / 9.0);
} else {
sample.temperature = (temperature - 32.0) * (5.0 / 9.0);
}
if (callback) callback (DC_SAMPLE_TEMPERATURE, &sample, userdata);
}
// Tank Pressure (psi)
@ -889,11 +945,17 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_
parser->model == PROPLUSX || parser->model == I770R ||
parser->model == SAGE || parser->model == BEACON)
pressure = array_uint16_le (data + offset + 4);
else
else if (parser->model == DSX) {
pressure = array_uint16_le (data + offset + 14);
tank = (data[offset] & 0xF0) >> 4;
} else {
pressure -= data[offset + 1];
sample.pressure.tank = tank;
sample.pressure.value = pressure * PSI / BAR;
if (callback) callback (DC_SAMPLE_PRESSURE, sample, userdata);
}
if (tank) {
sample.pressure.tank = tank - 1;
sample.pressure.value = pressure * PSI / BAR;
if (callback) callback (DC_SAMPLE_PRESSURE, &sample, userdata);
}
}
// Depth (1/16 ft)
@ -911,12 +973,18 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_
parser->model == I470TC || parser->model == I200CV2 ||
parser->model == GEOAIR)
depth = (data[offset + 4] + (data[offset + 5] << 8)) & 0x0FFF;
else if (parser->model == I330R || parser->model == DSX)
depth = array_uint16_le (data + offset + 2);
else if (parser->model == ATOM1)
depth = data[offset + 3] * 16;
else
depth = (data[offset + 2] + (data[offset + 3] << 8)) & 0x0FFF;
sample.depth = depth / 16.0 * FEET;
if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata);
if (parser->model == I330R || parser->model == DSX) {
sample.depth = depth / 10.0 * FEET;
} else {
sample.depth = depth / 16.0 * FEET;
}
if (callback) callback (DC_SAMPLE_DEPTH, &sample, userdata);
// Gas mix
unsigned int have_gasmix = 0;
@ -924,14 +992,17 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_
if (parser->model == TX1) {
gasmix = data[offset] & 0x07;
have_gasmix = 1;
} else if (parser->model == DSX) {
gasmix = (data[offset] & 0xF0) >> 4;
have_gasmix = 1;
}
if (have_gasmix && gasmix != gasmix_previous) {
if (have_gasmix && gasmix != gasmix_previous && parser->ngasmixes > 0) {
if (gasmix < 1 || gasmix > parser->ngasmixes) {
ERROR (abstract->context, "Invalid gas mix index (%u).", gasmix);
return DC_STATUS_DATAFORMAT;
}
sample.gasmix = gasmix - 1;
if (callback) callback (DC_SAMPLE_GASMIX, sample, userdata);
if (callback) callback (DC_SAMPLE_GASMIX, &sample, userdata);
gasmix_previous = gasmix;
}
@ -955,7 +1026,8 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_
have_deco = 1;
} else if (parser->model == ATOM31 || parser->model == VISION ||
parser->model == XPAIR || parser->model == I550 ||
parser->model == I550C || parser->model == WISDOM4) {
parser->model == I550C || parser->model == WISDOM4 ||
parser->model == PROPLUS4 || parser->model == ATMOSAI2) {
decostop = (data[offset + 5] & 0xF0) >> 4;
decotime = array_uint16_le(data + offset + 4) & 0x03FF;
have_deco = 1;
@ -970,17 +1042,32 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_
decostop = (data[offset + 7] & 0xF0) >> 4;
decotime = array_uint16_le(data + offset + 6) & 0x0FFF;
have_deco = 1;
} else if (parser->model == I330R || parser->model == DSX) {
decostop = data[offset + 8];
if (decostop) {
// Deco time
decotime = array_uint16_le(data + offset + 6);
} else {
// NDL
decotime = array_uint16_le(data + offset + 4);
}
have_deco = 1;
}
if (have_deco) {
if (decostop) {
sample.deco.type = DC_DECO_DECOSTOP;
sample.deco.depth = decostop * 10 * FEET;
if (parser->model == I330R || parser->model == DSX) {
sample.deco.depth = decostop * FEET;
} else {
sample.deco.depth = decostop * 10 * FEET;
}
} else {
sample.deco.type = DC_DECO_NDL;
sample.deco.depth = 0.0;
}
sample.deco.time = decotime * 60;
if (callback) callback (DC_SAMPLE_DECO, sample, userdata);
sample.deco.tts = 0;
if (callback) callback (DC_SAMPLE_DECO, &sample, userdata);
}
unsigned int have_rbt = 0;
@ -997,13 +1084,21 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_
have_rbt = 1;
} else if (parser->model == VISION || parser->model == XPAIR ||
parser->model == I550 || parser->model == I550C ||
parser->model == WISDOM4) {
parser->model == WISDOM4 || parser->model == PROPLUS4 ||
parser->model == ATMOSAI2) {
rbt = array_uint16_le(data + offset + 6) & 0x03FF;
have_rbt = 1;
}
if (have_rbt) {
sample.rbt = rbt;
if (callback) callback (DC_SAMPLE_RBT, sample, userdata);
if (callback) callback (DC_SAMPLE_RBT, &sample, userdata);
}
// PPO2
if (parser->model == I330R) {
sample.ppo2.sensor = DC_SENSOR_NONE;
sample.ppo2.value = data[offset + 9] / 100.0;
if (callback) callback (DC_SAMPLE_PPO2, &sample, userdata);
}
// Bookmarks
@ -1018,7 +1113,7 @@ oceanic_atom2_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_
sample.event.time = 0;
sample.event.flags = 0;
sample.event.value = 0;
if (callback) callback (DC_SAMPLE_EVENT, sample, userdata);
if (callback) callback (DC_SAMPLE_EVENT, &sample, userdata);
}
count++;

View File

@ -32,72 +32,62 @@
#define VTABLE(abstract) ((const oceanic_common_device_vtable_t *) abstract->vtable)
#define RB_LOGBOOK_DISTANCE(a,b,l) ringbuffer_distance (a, b, 1, l->rb_logbook_begin, l->rb_logbook_end)
#define RB_LOGBOOK_INCR(a,b,l) ringbuffer_increment (a, b, l->rb_logbook_begin, l->rb_logbook_end)
#define RB_LOGBOOK_DISTANCE(a,b,l,m) ringbuffer_distance (a, b, m, l->rb_logbook_begin, l->rb_logbook_end)
#define RB_PROFILE_DISTANCE(a,b,l) ringbuffer_distance (a, b, 0, l->rb_profile_begin, l->rb_profile_end)
#define RB_PROFILE_INCR(a,b,l) ringbuffer_increment (a, b, l->rb_profile_begin, l->rb_profile_end)
#define RB_PROFILE_DISTANCE(a,b,l,m) ringbuffer_distance (a, b, m, l->rb_profile_begin, l->rb_profile_end)
#define INVALID 0
static unsigned int
get_profile_first (const unsigned char data[], const oceanic_common_layout_t *layout, unsigned int pagesize)
static dc_status_t
oceanic_common_device_get_profile (const unsigned char data[], const oceanic_common_layout_t *layout, unsigned int *begin, unsigned int *end)
{
unsigned int value;
assert (layout != NULL);
assert (begin != NULL && end != NULL);
// Get the pagesize
unsigned int pagesize = layout->highmem ? 16 * PAGESIZE : PAGESIZE;
// Get the profile pointers.
unsigned int first = 0, last = 0;
if (layout->pt_mode_logbook == 0) {
value = array_uint16_le (data + 5);
first = array_uint16_le (data + 5);
last = array_uint16_le (data + 6) >> 4;
} else if (layout->pt_mode_logbook == 1) {
value = array_uint16_le (data + 4);
} else if (layout->pt_mode_logbook == 3) {
value = array_uint16_le (data + 16);
} else {
return array_uint16_le (data + 16);
first = array_uint16_le (data + 4);
last = array_uint16_le (data + 6);
} else if (layout->pt_mode_logbook == 2 || layout->pt_mode_logbook == 3) {
first = array_uint16_le (data + 16);
last = array_uint16_le (data + 18);
} else if (layout->pt_mode_logbook == 4) {
first = array_uint32_le (data + 8);
last = array_uint32_le (data + 12);
}
unsigned int npages = (layout->memsize - layout->highmem) / pagesize;
if (npages > 0x4000) {
value &= 0x7FFF;
} else if (npages > 0x2000) {
value &= 0x3FFF;
} else if (npages > 0x1000) {
value &= 0x1FFF;
} else {
value &= 0x0FFF;
// Convert pages to bytes.
if (layout->pt_mode_logbook < 3) {
unsigned int npages = (layout->memsize - layout->highmem) / pagesize;
if (npages > 0x4000) {
first &= 0x7FFF;
last &= 0x7FFF;
} else if (npages > 0x2000) {
first &= 0x3FFF;
last &= 0x3FFF;
} else if (npages > 0x1000) {
first &= 0x1FFF;
last &= 0x1FFF;
} else {
first &= 0x0FFF;
last &= 0x0FFF;
}
first *= pagesize;
last *= pagesize;
}
return layout->highmem + value * pagesize;
}
*begin = layout->highmem + first;
*end = layout->highmem + last + (layout->pt_mode_logbook < 4 ? pagesize : 0);
static unsigned int
get_profile_last (const unsigned char data[], const oceanic_common_layout_t *layout, unsigned int pagesize)
{
unsigned int value;
if (layout->pt_mode_logbook == 0) {
value = array_uint16_le (data + 6) >> 4;
} else if (layout->pt_mode_logbook == 1) {
value = array_uint16_le (data + 6);
} else if (layout->pt_mode_logbook == 3) {
value = array_uint16_le (data + 18);
} else {
return array_uint16_le(data + 18);
}
unsigned int npages = (layout->memsize - layout->highmem) / pagesize;
if (npages > 0x4000) {
value &= 0x7FFF;
} else if (npages > 0x2000) {
value &= 0x3FFF;
} else if (npages > 0x1000) {
value &= 0x1FFF;
} else {
value &= 0x0FFF;
}
return layout->highmem + value * pagesize;
return DC_STATUS_SUCCESS;
}
@ -206,11 +196,11 @@ oceanic_common_device_dump (dc_device_t *abstract, dc_buffer_t *buffer)
return DC_STATUS_NOMEMORY;
}
// Emit a vendor event.
dc_event_vendor_t vendor;
vendor.data = device->version;
vendor.size = sizeof (device->version);
device_event_emit (abstract, DC_EVENT_VENDOR, &vendor);
// Read the device info.
status = VTABLE(abstract)->devinfo (abstract, NULL);
if (status != DC_STATUS_SUCCESS) {
return status;
}
// Download the memory dump.
status = device_dump_read (abstract, 0, dc_buffer_get_data (buffer),
@ -219,8 +209,43 @@ oceanic_common_device_dump (dc_device_t *abstract, dc_buffer_t *buffer)
return status;
}
return status;
}
dc_status_t
oceanic_common_device_devinfo (dc_device_t *abstract, dc_event_progress_t *progress)
{
dc_status_t status = DC_STATUS_SUCCESS;
oceanic_common_device_t *device = (oceanic_common_device_t *) abstract;
assert (device != NULL);
assert (device->layout != NULL);
const oceanic_common_layout_t *layout = device->layout;
// Read the device id.
unsigned char id[PAGESIZE] = {0};
status = dc_device_read (abstract, layout->cf_devinfo, id, sizeof (id));
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to read the memory page.");
return status;
}
// Update and emit a progress event.
if (progress) {
progress->current += PAGESIZE;
progress->maximum += PAGESIZE;
device_event_emit (abstract, DC_EVENT_PROGRESS, progress);
}
// Emit a vendor event.
dc_event_vendor_t vendor;
vendor.data = device->version;
vendor.size = sizeof (device->version);
device_event_emit (abstract, DC_EVENT_VENDOR, &vendor);
// Emit a device info event.
unsigned char *id = dc_buffer_get_data (buffer) + layout->cf_devinfo;
dc_event_devinfo_t devinfo;
devinfo.model = array_uint16_be (id + 8);
devinfo.firmware = device->firmware;
@ -240,7 +265,51 @@ oceanic_common_device_dump (dc_device_t *abstract, dc_buffer_t *buffer)
dc_status_t
oceanic_common_device_logbook (dc_device_t *abstract, dc_event_progress_t *progress, dc_buffer_t *logbook)
oceanic_common_device_pointers (dc_device_t *abstract, dc_event_progress_t *progress,
unsigned int *rb_logbook_begin, unsigned int *rb_logbook_end,
unsigned int *rb_profile_begin, unsigned int *rb_profile_end)
{
dc_status_t status = DC_STATUS_SUCCESS;
oceanic_common_device_t *device = (oceanic_common_device_t *) abstract;
assert (device != NULL);
assert (device->layout != NULL);
assert (rb_logbook_begin != NULL && rb_logbook_end != NULL);
assert (rb_profile_begin != NULL && rb_profile_end != NULL);
const oceanic_common_layout_t *layout = device->layout;
// Read the pointer data.
unsigned char pointers[PAGESIZE] = {0};
status = dc_device_read (abstract, layout->cf_pointers, pointers, sizeof (pointers));
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to read the memory page.");
return status;
}
// Update and emit a progress event.
if (progress) {
progress->current += PAGESIZE;
progress->maximum += PAGESIZE;
device_event_emit (abstract, DC_EVENT_PROGRESS, progress);
}
// Get the pointers.
unsigned int rb_logbook_first = array_uint16_le (pointers + 4);
unsigned int rb_logbook_last = array_uint16_le (pointers + 6);
unsigned int rb_profile_first = array_uint16_le (pointers + 8);
unsigned int rb_profile_last = array_uint16_le (pointers + 10);
*rb_logbook_begin = rb_logbook_first;
*rb_logbook_end = rb_logbook_last + (layout->pt_mode_global == 0 ? layout->rb_logbook_entry_size : 0);
*rb_profile_begin = rb_profile_first;
*rb_profile_end = rb_profile_last;
return status;
}
dc_status_t
oceanic_common_device_logbook (dc_device_t *abstract, dc_event_progress_t *progress, dc_buffer_t *logbook, unsigned int begin, unsigned int end)
{
oceanic_common_device_t *device = (oceanic_common_device_t *) abstract;
dc_status_t rc = DC_STATUS_SUCCESS;
@ -256,37 +325,30 @@ oceanic_common_device_logbook (dc_device_t *abstract, dc_event_progress_t *progr
if (!dc_buffer_clear (logbook))
return DC_STATUS_NOMEMORY;
// For devices without a logbook ringbuffer, downloading dives isn't
// possible. This is not considered a fatal error, but handled as if there
// are no dives present.
if (layout->rb_logbook_begin == layout->rb_logbook_end) {
return DC_STATUS_SUCCESS;
}
// Read the pointer data.
unsigned char pointers[PAGESIZE] = {0};
rc = dc_device_read (abstract, layout->cf_pointers, pointers, sizeof (pointers));
if (rc != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to read the memory page.");
return rc;
}
// Get the logbook pointers.
unsigned int rb_logbook_first = array_uint16_le (pointers + 4);
unsigned int rb_logbook_last = array_uint16_le (pointers + 6);
if (rb_logbook_last < layout->rb_logbook_begin ||
rb_logbook_last >= layout->rb_logbook_end)
// Validate the logbook pointers.
unsigned int rb_logbook_begin = begin;
unsigned int rb_logbook_end = end;
if (rb_logbook_begin < layout->rb_logbook_begin ||
rb_logbook_begin > layout->rb_logbook_end)
{
ERROR (abstract->context, "Invalid logbook end pointer detected (0x%04x).", rb_logbook_last);
return DC_STATUS_DATAFORMAT;
ERROR (abstract->context, "Invalid logbook begin pointer detected (0x%04x).", rb_logbook_begin);
if (layout->rb_logbook_direction == 0) {
return DC_STATUS_DATAFORMAT;
}
// Fall back to downloading the entire logbook ringbuffer as
// workaround for an invalid logbook begin pointer!
rb_logbook_begin = rb_logbook_end;
}
// Calculate the end pointer.
unsigned int rb_logbook_end = 0;
if (layout->pt_mode_global == 0) {
rb_logbook_end = RB_LOGBOOK_INCR (rb_logbook_last, layout->rb_logbook_entry_size, layout);
} else {
rb_logbook_end = rb_logbook_last;
if (rb_logbook_end < layout->rb_logbook_begin ||
rb_logbook_end > layout->rb_logbook_end)
{
ERROR (abstract->context, "Invalid logbook end pointer detected (0x%04x).", rb_logbook_end);
if (layout->rb_logbook_direction != 0) {
return DC_STATUS_DATAFORMAT;
}
// Fall back to downloading the entire logbook ringbuffer as
// workaround for an invalid logbook end pointer!
rb_logbook_end = rb_logbook_begin;
}
// Calculate the number of bytes.
@ -295,21 +357,9 @@ oceanic_common_device_logbook (dc_device_t *abstract, dc_event_progress_t *progr
// full ringbuffer. We always consider the ringbuffer full in that
// case, because an empty ringbuffer can be detected by inspecting
// the logbook entries once they are downloaded.
unsigned int rb_logbook_size = 0;
if (rb_logbook_first < layout->rb_logbook_begin ||
rb_logbook_first >= layout->rb_logbook_end)
{
// Fall back to downloading the entire logbook ringbuffer as
// workaround for an invalid logbook begin pointer!
ERROR (abstract->context, "Invalid logbook begin pointer detected (0x%04x).", rb_logbook_first);
rb_logbook_size = layout->rb_logbook_end - layout->rb_logbook_begin;
} else {
rb_logbook_size = RB_LOGBOOK_DISTANCE (rb_logbook_first, rb_logbook_end, layout);
}
unsigned int rb_logbook_size = RB_LOGBOOK_DISTANCE (rb_logbook_begin, rb_logbook_end, layout, DC_RINGBUFFER_FULL);
// Update and emit a progress event.
progress->current += PAGESIZE;
progress->maximum += PAGESIZE;
progress->maximum -= (layout->rb_logbook_end - layout->rb_logbook_begin) - rb_logbook_size;
device_event_emit (abstract, DC_EVENT_PROGRESS, progress);
@ -327,7 +377,11 @@ oceanic_common_device_logbook (dc_device_t *abstract, dc_event_progress_t *progr
// Create the ringbuffer stream.
dc_rbstream_t *rbstream = NULL;
rc = dc_rbstream_new (&rbstream, abstract, PAGESIZE, PAGESIZE * device->multipage, layout->rb_logbook_begin, layout->rb_logbook_end, rb_logbook_end);
rc = dc_rbstream_new (&rbstream, abstract,
PAGESIZE, PAGESIZE * device->multipage,
layout->rb_logbook_begin, layout->rb_logbook_end,
layout->rb_logbook_direction ? rb_logbook_end : rb_logbook_begin,
layout->rb_logbook_direction ? DC_RBSTREAM_BACKWARD : DC_RBSTREAM_FORWARD);
if (rc != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to create the ringbuffer stream.");
return rc;
@ -403,9 +457,6 @@ oceanic_common_device_profile (dc_device_t *abstract, dc_event_progress_t *progr
const oceanic_common_layout_t *layout = device->layout;
// Get the pagesize
unsigned int pagesize = layout->highmem ? 16 * PAGESIZE : PAGESIZE;
// Cache the logbook pointer and size.
const unsigned char *logbooks = dc_buffer_get_data (logbook);
unsigned int rb_logbook_size = dc_buffer_get_size (logbook);
@ -413,6 +464,7 @@ oceanic_common_device_profile (dc_device_t *abstract, dc_event_progress_t *progr
// Go through the logbook entries a first time, to get the end of
// profile pointer and calculate the total amount of bytes in the
// profile ringbuffer.
unsigned int rb_profile_begin = INVALID;
unsigned int rb_profile_end = INVALID;
unsigned int rb_profile_size = 0;
@ -434,22 +486,20 @@ oceanic_common_device_profile (dc_device_t *abstract, dc_event_progress_t *progr
}
// Get the profile pointers.
unsigned int rb_entry_first = get_profile_first (logbooks + entry, layout, pagesize);
unsigned int rb_entry_last = get_profile_last (logbooks + entry, layout, pagesize);
if (rb_entry_first < layout->rb_profile_begin ||
rb_entry_first >= layout->rb_profile_end ||
rb_entry_last < layout->rb_profile_begin ||
rb_entry_last >= layout->rb_profile_end)
unsigned int rb_entry_begin = 0, rb_entry_end = 0;
oceanic_common_device_get_profile (logbooks + entry, layout, &rb_entry_begin, &rb_entry_end);
if (rb_entry_begin < layout->rb_profile_begin ||
rb_entry_begin > layout->rb_profile_end ||
rb_entry_end < layout->rb_profile_begin ||
rb_entry_end > layout->rb_profile_end)
{
ERROR (abstract->context, "Invalid ringbuffer pointer detected (0x%06x 0x%06x).",
rb_entry_first, rb_entry_last);
rb_entry_begin, rb_entry_end);
status = DC_STATUS_DATAFORMAT;
continue;
}
// Calculate the end pointer and the number of bytes.
unsigned int rb_entry_end = RB_PROFILE_INCR (rb_entry_last, pagesize, layout);
unsigned int rb_entry_size = RB_PROFILE_DISTANCE (rb_entry_first, rb_entry_last, layout) + pagesize;
DEBUG (abstract->context, "Entry: %08x %08x", rb_entry_begin, rb_entry_end);
// Take the end pointer of the most recent logbook entry as the
// end of profile pointer.
@ -457,11 +507,13 @@ oceanic_common_device_profile (dc_device_t *abstract, dc_event_progress_t *progr
rb_profile_end = previous = rb_entry_end;
}
// Calculate the number of bytes.
unsigned int rb_entry_size = RB_PROFILE_DISTANCE (rb_entry_begin, rb_entry_end, layout, DC_RINGBUFFER_FULL);
// Skip gaps between the profiles.
unsigned int gap = 0;
if (rb_entry_end != previous) {
WARNING (abstract->context, "Profiles are not continuous.");
gap = RB_PROFILE_DISTANCE (rb_entry_end, previous, layout);
unsigned int gap = RB_PROFILE_DISTANCE (rb_entry_end, previous, layout, DC_RINGBUFFER_EMPTY);
if (gap) {
WARNING (abstract->context, "Profiles are not continuous (%u bytes).", gap);
}
// Make sure the profile size is valid.
@ -470,13 +522,18 @@ oceanic_common_device_profile (dc_device_t *abstract, dc_event_progress_t *progr
break;
}
// Update the profile begin pointer.
rb_profile_begin = rb_entry_begin;
// Update the total profile size.
rb_profile_size += rb_entry_size + gap;
remaining -= rb_entry_size + gap;
previous = rb_entry_first;
previous = rb_entry_begin;
}
DEBUG (abstract->context, "Profile: %08x %08x", rb_profile_begin, rb_profile_end);
// At this point, we know the exact amount of data
// that needs to be transfered for the profiles.
progress->maximum -= (layout->rb_profile_end - layout->rb_profile_begin) - rb_profile_size;
@ -489,7 +546,7 @@ oceanic_common_device_profile (dc_device_t *abstract, dc_event_progress_t *progr
// Create the ringbuffer stream.
dc_rbstream_t *rbstream = NULL;
rc = dc_rbstream_new (&rbstream, abstract, PAGESIZE, PAGESIZE * device->multipage, layout->rb_profile_begin, layout->rb_profile_end, rb_profile_end);
rc = dc_rbstream_new (&rbstream, abstract, PAGESIZE, PAGESIZE * device->multipage, layout->rb_profile_begin, layout->rb_profile_end, rb_profile_end, DC_RBSTREAM_BACKWARD);
if (rc != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to create the ringbuffer stream.");
return rc;
@ -524,28 +581,28 @@ oceanic_common_device_profile (dc_device_t *abstract, dc_event_progress_t *progr
}
// Get the profile pointers.
unsigned int rb_entry_first = get_profile_first (logbooks + entry, layout, pagesize);
unsigned int rb_entry_last = get_profile_last (logbooks + entry, layout, pagesize);
if (rb_entry_first < layout->rb_profile_begin ||
rb_entry_first >= layout->rb_profile_end ||
rb_entry_last < layout->rb_profile_begin ||
rb_entry_last >= layout->rb_profile_end)
unsigned int rb_entry_begin = 0, rb_entry_end = 0;
oceanic_common_device_get_profile (logbooks + entry, layout, &rb_entry_begin, &rb_entry_end);
if (rb_entry_begin < layout->rb_profile_begin ||
rb_entry_begin > layout->rb_profile_end ||
rb_entry_end < layout->rb_profile_begin ||
rb_entry_end > layout->rb_profile_end)
{
ERROR (abstract->context, "Invalid ringbuffer pointer detected (0x%06x 0x%06x).",
rb_entry_first, rb_entry_last);
rb_entry_begin, rb_entry_end);
status = DC_STATUS_DATAFORMAT;
continue;
}
// Calculate the end pointer and the number of bytes.
unsigned int rb_entry_end = RB_PROFILE_INCR (rb_entry_last, pagesize, layout);
unsigned int rb_entry_size = RB_PROFILE_DISTANCE (rb_entry_first, rb_entry_last, layout) + pagesize;
DEBUG (abstract->context, "Entry: %08x %08x", rb_entry_begin, rb_entry_end);
// Calculate the number of bytes.
unsigned int rb_entry_size = RB_PROFILE_DISTANCE (rb_entry_begin, rb_entry_end, layout, DC_RINGBUFFER_FULL);
// Skip gaps between the profiles.
unsigned int gap = 0;
if (rb_entry_end != previous) {
WARNING (abstract->context, "Profiles are not continuous.");
gap = RB_PROFILE_DISTANCE (rb_entry_end, previous, layout);
unsigned int gap = RB_PROFILE_DISTANCE (rb_entry_end, previous, layout, DC_RINGBUFFER_EMPTY);
if (gap) {
WARNING (abstract->context, "Profiles are not continuous (%u bytes).", gap);
}
// Make sure the profile size is valid.
@ -566,7 +623,7 @@ oceanic_common_device_profile (dc_device_t *abstract, dc_event_progress_t *progr
}
remaining -= rb_entry_size + gap;
previous = rb_entry_first;
previous = rb_entry_begin;
// Prepend the logbook entry to the profile data. The memory buffer is
// large enough to store this entry.
@ -604,6 +661,7 @@ oceanic_common_device_profile (dc_device_t *abstract, dc_event_progress_t *progr
dc_status_t
oceanic_common_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata)
{
dc_status_t rc = DC_STATUS_SUCCESS;
oceanic_common_device_t *device = (oceanic_common_device_t *) abstract;
assert (device != NULL);
@ -611,45 +669,37 @@ oceanic_common_device_foreach (dc_device_t *abstract, dc_dive_callback_t callbac
const oceanic_common_layout_t *layout = device->layout;
// For devices without a logbook and profile ringbuffer, downloading dives
// isn't possible. This is not considered a fatal error, but handled as if
// there are no dives present.
if (layout->rb_logbook_begin == layout->rb_logbook_end &&
layout->rb_profile_begin == layout->rb_profile_end) {
return DC_STATUS_SUCCESS;
}
// Enable progress notifications.
dc_event_progress_t progress = EVENT_PROGRESS_INITIALIZER;
progress.maximum = PAGESIZE +
progress.maximum =
(layout->rb_logbook_end - layout->rb_logbook_begin) +
(layout->rb_profile_end - layout->rb_profile_begin);
device_event_emit (abstract, DC_EVENT_PROGRESS, &progress);
// Emit a vendor event.
dc_event_vendor_t vendor;
vendor.data = device->version;
vendor.size = sizeof (device->version);
device_event_emit (abstract, DC_EVENT_VENDOR, &vendor);
// Read the device id.
unsigned char id[PAGESIZE] = {0};
dc_status_t rc = dc_device_read (abstract, layout->cf_devinfo, id, sizeof (id));
// Read the device info.
rc = VTABLE(abstract)->devinfo (abstract, &progress);
if (rc != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to read the memory page.");
return rc;
}
// Update and emit a progress event.
progress.current += PAGESIZE;
device_event_emit (abstract, DC_EVENT_PROGRESS, &progress);
// Read the ringbuffer pointers.
unsigned int rb_logbook_begin = 0, rb_logbook_end = 0;
unsigned int rb_profile_begin = 0, rb_profile_end = 0;
rc = VTABLE(abstract)->pointers (abstract, &progress, &rb_logbook_begin, &rb_logbook_end, &rb_profile_begin, &rb_profile_end);
if (rc != DC_STATUS_SUCCESS) {
return rc;
}
// Emit a device info event.
dc_event_devinfo_t devinfo;
devinfo.model = array_uint16_be (id + 8);
devinfo.firmware = device->firmware;
if (layout->pt_mode_serial == 0)
devinfo.serial = array_convert_bcd2dec (id + 10, 3);
else if (layout->pt_mode_serial == 1)
devinfo.serial = array_convert_bin2dec (id + 11, 3);
else
devinfo.serial =
(id[11] & 0x0F) * 100000 + ((id[11] & 0xF0) >> 4) * 10000 +
(id[12] & 0x0F) * 1000 + ((id[12] & 0xF0) >> 4) * 100 +
(id[13] & 0x0F) * 10 + ((id[13] & 0xF0) >> 4) * 1;
device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo);
DEBUG (abstract->context, "Logbook: %08x %08x", rb_logbook_begin, rb_logbook_end);
DEBUG (abstract->context, "Profile: %08x %08x", rb_profile_begin, rb_profile_end);
// Memory buffer for the logbook data.
dc_buffer_t *logbook = dc_buffer_new (0);
@ -658,7 +708,7 @@ oceanic_common_device_foreach (dc_device_t *abstract, dc_dive_callback_t callbac
}
// Download the logbook ringbuffer.
rc = VTABLE(abstract)->logbook (abstract, &progress, logbook);
rc = VTABLE(abstract)->logbook (abstract, &progress, logbook, rb_logbook_begin, rb_logbook_end);
if (rc != DC_STATUS_SUCCESS) {
dc_buffer_free (logbook);
return rc;

View File

@ -125,8 +125,12 @@ extern "C" {
#define I200CV2 0x4749
#define GEOAIR 0x474B
// i330r
#define DSX 0x4741
#define I330R 0x4744
#define PAGESIZE 0x10
#define FPMAXSIZE 0x20
#define FPMAXSIZE 0x200
#define OCEANIC_COMMON_MATCH(version,patterns,firmware) \
oceanic_common_match ((version), (patterns), \
@ -144,6 +148,7 @@ typedef struct oceanic_common_layout_t {
unsigned int rb_logbook_begin;
unsigned int rb_logbook_end;
unsigned int rb_logbook_entry_size;
unsigned int rb_logbook_direction;
// Profile ringbuffer
unsigned int rb_profile_begin;
unsigned int rb_profile_end;
@ -168,7 +173,9 @@ typedef struct oceanic_common_device_t {
typedef struct oceanic_common_device_vtable_t {
dc_device_vtable_t base;
dc_status_t (*logbook) (dc_device_t *device, dc_event_progress_t *progress, dc_buffer_t *logbook);
dc_status_t (*devinfo) (dc_device_t *device, dc_event_progress_t *progress);
dc_status_t (*pointers) (dc_device_t *device, dc_event_progress_t *progress, unsigned int *rb_logbook_begin, unsigned int *rb_logbook_end, unsigned int *rb_profile_begin, unsigned int *rb_profile_end);
dc_status_t (*logbook) (dc_device_t *device, dc_event_progress_t *progress, dc_buffer_t *logbook, unsigned int begin, unsigned int end);
dc_status_t (*profile) (dc_device_t *device, dc_event_progress_t *progress, dc_buffer_t *logbook, dc_dive_callback_t callback, void *userdata);
} oceanic_common_device_vtable_t;
@ -186,7 +193,15 @@ void
oceanic_common_device_init (oceanic_common_device_t *device);
dc_status_t
oceanic_common_device_logbook (dc_device_t *device, dc_event_progress_t *progress, dc_buffer_t *logbook);
oceanic_common_device_devinfo (dc_device_t *device, dc_event_progress_t *progress);
dc_status_t
oceanic_common_device_pointers (dc_device_t *device, dc_event_progress_t *progress,
unsigned int *rb_logbook_begin, unsigned int *rb_logbook_end,
unsigned int *rb_profile_begin, unsigned int *rb_profile_end);
dc_status_t
oceanic_common_device_logbook (dc_device_t *device, dc_event_progress_t *progress, dc_buffer_t *logbook, unsigned int begin, unsigned int end);
dc_status_t
oceanic_common_device_profile (dc_device_t *device, dc_event_progress_t *progress, dc_buffer_t *logbook, dc_dive_callback_t callback, void *userdata);

View File

@ -58,6 +58,8 @@ static const oceanic_common_device_vtable_t oceanic_veo250_device_vtable = {
NULL, /* timesync */
oceanic_veo250_device_close /* close */
},
oceanic_common_device_devinfo,
oceanic_common_device_pointers,
oceanic_common_device_logbook,
oceanic_common_device_profile,
};
@ -70,6 +72,7 @@ static const oceanic_common_layout_t oceanic_veo250_layout = {
0x0400, /* rb_logbook_begin */
0x0600, /* rb_logbook_end */
8, /* rb_logbook_entry_size */
1, /* rb_logbook_direction */
0x0600, /* rb_profile_begin */
0x8000, /* rb_profile_end */
1, /* pt_mode_global */

View File

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

View File

@ -42,7 +42,6 @@ struct oceanic_veo250_parser_t {
double maxdepth;
};
static dc_status_t oceanic_veo250_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size);
static dc_status_t oceanic_veo250_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime);
static dc_status_t oceanic_veo250_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value);
static dc_status_t oceanic_veo250_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata);
@ -50,7 +49,6 @@ static dc_status_t oceanic_veo250_parser_samples_foreach (dc_parser_t *abstract,
static const dc_parser_vtable_t oceanic_veo250_parser_vtable = {
sizeof(oceanic_veo250_parser_t),
DC_FAMILY_OCEANIC_VEO250,
oceanic_veo250_parser_set_data, /* set_data */
NULL, /* set_clock */
NULL, /* set_atmospheric */
NULL, /* set_density */
@ -62,7 +60,7 @@ static const dc_parser_vtable_t oceanic_veo250_parser_vtable = {
dc_status_t
oceanic_veo250_parser_create (dc_parser_t **out, dc_context_t *context, unsigned int model)
oceanic_veo250_parser_create (dc_parser_t **out, dc_context_t *context, const unsigned char data[], size_t size, unsigned int model)
{
oceanic_veo250_parser_t *parser = NULL;
@ -70,7 +68,7 @@ oceanic_veo250_parser_create (dc_parser_t **out, dc_context_t *context, unsigned
return DC_STATUS_INVALIDARGS;
// Allocate memory.
parser = (oceanic_veo250_parser_t *) dc_parser_allocate (context, &oceanic_veo250_parser_vtable);
parser = (oceanic_veo250_parser_t *) dc_parser_allocate (context, &oceanic_veo250_parser_vtable, data, size);
if (parser == NULL) {
ERROR (context, "Failed to allocate memory.");
return DC_STATUS_NOMEMORY;
@ -88,20 +86,6 @@ oceanic_veo250_parser_create (dc_parser_t **out, dc_context_t *context, unsigned
}
static dc_status_t
oceanic_veo250_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size)
{
oceanic_veo250_parser_t *parser = (oceanic_veo250_parser_t *) abstract;
// Reset the cache.
parser->cached = 0;
parser->divetime = 0;
parser->maxdepth = 0.0;
return DC_STATUS_SUCCESS;
}
static dc_status_t
oceanic_veo250_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime)
{
@ -170,6 +154,7 @@ oceanic_veo250_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, un
*((unsigned int *) value) = 1;
break;
case DC_FIELD_GASMIX:
gasmix->usage = DC_USAGE_NONE;
gasmix->helium = 0.0;
if (data[footer + 6])
gasmix->oxygen = data[footer + 6] / 100.0;
@ -230,19 +215,19 @@ oceanic_veo250_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback
// Time.
time += interval;
sample.time = time;
if (callback) callback (DC_SAMPLE_TIME, sample, userdata);
sample.time = time * 1000;
if (callback) callback (DC_SAMPLE_TIME, &sample, userdata);
// Vendor specific data
sample.vendor.type = SAMPLE_VENDOR_OCEANIC_VEO250;
sample.vendor.size = PAGESIZE / 2;
sample.vendor.data = data + offset;
if (callback) callback (DC_SAMPLE_VENDOR, sample, userdata);
if (callback) callback (DC_SAMPLE_VENDOR, &sample, userdata);
// Depth (ft)
unsigned int depth = data[offset + 2];
sample.depth = depth * FEET;
if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata);
if (callback) callback (DC_SAMPLE_DEPTH, &sample, userdata);
// Temperature (°F)
unsigned int temperature;
@ -253,7 +238,7 @@ oceanic_veo250_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback
temperature = data[offset + 7];
}
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);
// NDL / Deco
unsigned int have_deco = 0;
@ -277,7 +262,8 @@ oceanic_veo250_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback
sample.deco.depth = 0.0;
}
sample.deco.time = decotime * 60;
if (callback) callback (DC_SAMPLE_DECO, sample, userdata);
sample.deco.tts = 0;
if (callback) callback (DC_SAMPLE_DECO, &sample, userdata);
}
offset += PAGESIZE / 2;

View File

@ -51,7 +51,8 @@ typedef struct oceanic_vtpro_device_t {
oceanic_vtpro_protocol_t protocol;
} oceanic_vtpro_device_t;
static dc_status_t oceanic_vtpro_device_logbook (dc_device_t *abstract, dc_event_progress_t *progress, dc_buffer_t *logbook);
static dc_status_t oceanic_vtpro_device_pointers (dc_device_t *abstract, dc_event_progress_t *progress, unsigned int *rb_logbook_begin, unsigned int *rb_logbook_end, unsigned int *rb_profile_begin, unsigned int *rb_profile_end);
static dc_status_t oceanic_vtpro_device_logbook (dc_device_t *abstract, dc_event_progress_t *progress, dc_buffer_t *logbook, unsigned int begin, unsigned int end);
static dc_status_t oceanic_vtpro_device_read (dc_device_t *abstract, unsigned int address, unsigned char data[], unsigned int size);
static dc_status_t oceanic_vtpro_device_close (dc_device_t *abstract);
@ -67,6 +68,8 @@ static const oceanic_common_device_vtable_t oceanic_vtpro_device_vtable = {
NULL, /* timesync */
oceanic_vtpro_device_close /* close */
},
oceanic_common_device_devinfo,
oceanic_vtpro_device_pointers,
oceanic_vtpro_device_logbook,
oceanic_common_device_profile,
};
@ -79,6 +82,7 @@ static const oceanic_common_layout_t oceanic_vtpro_layout = {
0x0240, /* rb_logbook_begin */
0x0440, /* rb_logbook_end */
8, /* rb_logbook_entry_size */
1, /* rb_logbook_direction */
0x0440, /* rb_profile_begin */
0x8000, /* rb_profile_end */
0, /* pt_mode_global */
@ -94,6 +98,7 @@ static const oceanic_common_layout_t oceanic_wisdom_layout = {
0x03D0, /* rb_logbook_begin */
0x05D0, /* rb_logbook_end */
8, /* rb_logbook_entry_size */
1, /* rb_logbook_direction */
0x05D0, /* rb_profile_begin */
0x8000, /* rb_profile_end */
0, /* pt_mode_global */
@ -109,6 +114,7 @@ static const oceanic_common_layout_t aeris_500ai_layout = {
0x0200, /* rb_logbook_begin */
0x0200, /* rb_logbook_end */
8, /* rb_logbook_entry_size */
1, /* rb_logbook_direction */
0x00200, /* rb_profile_begin */
0x20000, /* rb_profile_end */
0, /* pt_mode_global */
@ -286,7 +292,49 @@ oceanic_vtpro_calibrate (oceanic_vtpro_device_t *device)
}
static dc_status_t
oceanic_aeris500ai_device_logbook (dc_device_t *abstract, dc_event_progress_t *progress, dc_buffer_t *logbook)
oceanic_aeris500ai_device_pointers (dc_device_t *abstract, dc_event_progress_t *progress, unsigned int *rb_logbook_begin, unsigned int *rb_logbook_end, unsigned int *rb_profile_begin, unsigned int *rb_profile_end)
{
dc_status_t status = DC_STATUS_SUCCESS;
oceanic_vtpro_device_t *device = (oceanic_vtpro_device_t *) abstract;
assert (device != NULL);
assert (device->base.layout != NULL);
assert (rb_logbook_begin != NULL && rb_logbook_end != NULL);
assert (rb_profile_begin != NULL && rb_profile_end != NULL);
const oceanic_common_layout_t *layout = device->base.layout;
// Read the pointer data.
unsigned char pointers[PAGESIZE] = {0};
status = oceanic_vtpro_device_read (abstract, layout->cf_pointers, pointers, sizeof (pointers));
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to read the memory page.");
return status;
}
// Update and emit a progress event.
if (progress) {
progress->current += PAGESIZE;
progress->maximum += PAGESIZE;
device_event_emit (abstract, DC_EVENT_PROGRESS, progress);
}
// Get the pointers.
unsigned int rb_logbook_first = pointers[0x02];
unsigned int rb_logbook_last = pointers[0x03];
unsigned int rb_profile_first = array_uint16_le (pointers + 4) * PAGESIZE;
unsigned int rb_profile_last = array_uint16_le (pointers + 6) * PAGESIZE;
*rb_logbook_begin = rb_logbook_first;
*rb_logbook_end = rb_logbook_last;
*rb_profile_begin = rb_profile_first;
*rb_profile_end = rb_profile_last;
return status;
}
static dc_status_t
oceanic_aeris500ai_device_logbook (dc_device_t *abstract, dc_event_progress_t *progress, dc_buffer_t *logbook, unsigned int begin, unsigned int end)
{
dc_status_t rc = DC_STATUS_SUCCESS;
oceanic_vtpro_device_t *device = (oceanic_vtpro_device_t *) abstract;
@ -297,36 +345,25 @@ oceanic_aeris500ai_device_logbook (dc_device_t *abstract, dc_event_progress_t *p
assert (device->base.layout->rb_logbook_begin == device->base.layout->rb_logbook_end);
assert (progress != NULL);
const oceanic_common_layout_t *layout = device->base.layout;
// Erase the buffer.
if (!dc_buffer_clear (logbook))
return DC_STATUS_NOMEMORY;
// Read the pointer data.
unsigned char pointers[PAGESIZE] = {0};
rc = oceanic_vtpro_device_read (abstract, layout->cf_pointers, pointers, sizeof (pointers));
if (rc != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to read the memory page.");
return rc;
}
// Get the logbook pointers.
unsigned int last = pointers[0x03];
// Get the number of dives.
unsigned int ndives = end - begin + 1;
// Update and emit a progress event.
progress->current += PAGESIZE;
progress->maximum += PAGESIZE + (last + 1) * PAGESIZE / 2;
progress->maximum += ndives * PAGESIZE / 2;
device_event_emit (abstract, DC_EVENT_PROGRESS, progress);
// Allocate memory for the logbook entries.
if (!dc_buffer_reserve (logbook, (last + 1) * PAGESIZE / 2))
if (!dc_buffer_reserve (logbook, ndives * PAGESIZE / 2))
return DC_STATUS_NOMEMORY;
// Send the logbook index command.
unsigned char command[] = {0x52,
(last >> 8) & 0xFF, // high
(last ) & 0xFF, // low
begin & 0xFF,
end & 0xFF,
0x00};
rc = oceanic_vtpro_transfer (device, command, sizeof (command), NULL, 0);
if (rc != DC_STATUS_SUCCESS) {
@ -335,7 +372,7 @@ oceanic_aeris500ai_device_logbook (dc_device_t *abstract, dc_event_progress_t *p
}
// Read the logbook index.
for (unsigned int i = 0; i < last + 1; ++i) {
for (unsigned int i = 0; i < ndives; ++i) {
// Receive the answer of the dive computer.
unsigned char answer[PAGESIZE / 2 + 1] = {0};
rc = dc_iostream_read (device->iostream, answer, sizeof(answer), NULL);
@ -374,14 +411,26 @@ oceanic_aeris500ai_device_logbook (dc_device_t *abstract, dc_event_progress_t *p
}
static dc_status_t
oceanic_vtpro_device_logbook (dc_device_t *abstract, dc_event_progress_t *progress, dc_buffer_t *logbook)
oceanic_vtpro_device_pointers (dc_device_t *abstract, dc_event_progress_t *progress, unsigned int *rb_logbook_begin, unsigned int *rb_logbook_end, unsigned int *rb_profile_begin, unsigned int *rb_profile_end)
{
oceanic_vtpro_device_t *device = (oceanic_vtpro_device_t *) abstract;
if (device->base.model == AERIS500AI) {
return oceanic_aeris500ai_device_logbook (abstract, progress, logbook);
return oceanic_aeris500ai_device_pointers (abstract, progress, rb_logbook_begin, rb_logbook_end, rb_profile_begin, rb_profile_end);
} else {
return oceanic_common_device_logbook (abstract, progress, logbook);
return oceanic_common_device_pointers (abstract, progress, rb_logbook_begin, rb_logbook_end, rb_profile_begin, rb_profile_end);
}
}
static dc_status_t
oceanic_vtpro_device_logbook (dc_device_t *abstract, dc_event_progress_t *progress, dc_buffer_t *logbook, unsigned int begin, unsigned int end)
{
oceanic_vtpro_device_t *device = (oceanic_vtpro_device_t *) abstract;
if (device->base.model == AERIS500AI) {
return oceanic_aeris500ai_device_logbook (abstract, progress, logbook, begin, end);
} else {
return oceanic_common_device_logbook (abstract, progress, logbook, begin, end);
}
}

View File

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

View File

@ -42,7 +42,6 @@ struct oceanic_vtpro_parser_t {
double maxdepth;
};
static dc_status_t oceanic_vtpro_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size);
static dc_status_t oceanic_vtpro_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime);
static dc_status_t oceanic_vtpro_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value);
static dc_status_t oceanic_vtpro_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata);
@ -50,7 +49,6 @@ static dc_status_t oceanic_vtpro_parser_samples_foreach (dc_parser_t *abstract,
static const dc_parser_vtable_t oceanic_vtpro_parser_vtable = {
sizeof(oceanic_vtpro_parser_t),
DC_FAMILY_OCEANIC_VTPRO,
oceanic_vtpro_parser_set_data, /* set_data */
NULL, /* set_clock */
NULL, /* set_atmospheric */
NULL, /* set_density */
@ -62,7 +60,7 @@ static const dc_parser_vtable_t oceanic_vtpro_parser_vtable = {
dc_status_t
oceanic_vtpro_parser_create (dc_parser_t **out, dc_context_t *context, unsigned int model)
oceanic_vtpro_parser_create (dc_parser_t **out, dc_context_t *context, const unsigned char data[], size_t size, unsigned int model)
{
oceanic_vtpro_parser_t *parser = NULL;
@ -70,7 +68,7 @@ oceanic_vtpro_parser_create (dc_parser_t **out, dc_context_t *context, unsigned
return DC_STATUS_INVALIDARGS;
// Allocate memory.
parser = (oceanic_vtpro_parser_t *) dc_parser_allocate (context, &oceanic_vtpro_parser_vtable);
parser = (oceanic_vtpro_parser_t *) dc_parser_allocate (context, &oceanic_vtpro_parser_vtable, data, size);
if (parser == NULL) {
ERROR (context, "Failed to allocate memory.");
return DC_STATUS_NOMEMORY;
@ -88,20 +86,6 @@ oceanic_vtpro_parser_create (dc_parser_t **out, dc_context_t *context, unsigned
}
static dc_status_t
oceanic_vtpro_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size)
{
oceanic_vtpro_parser_t *parser = (oceanic_vtpro_parser_t *) abstract;
// Reset the cache.
parser->cached = 0;
parser->divetime = 0;
parser->maxdepth = 0.0;
return DC_STATUS_SUCCESS;
}
static dc_status_t
oceanic_vtpro_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime)
{
@ -200,6 +184,7 @@ oceanic_vtpro_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, uns
*((unsigned int *) value) = 1;
break;
case DC_FIELD_GASMIX:
gasmix->usage = DC_USAGE_NONE;
gasmix->helium = 0.0;
if (oxygen)
gasmix->oxygen = oxygen / 100.0;
@ -220,6 +205,7 @@ oceanic_vtpro_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, uns
tank->gasmix = flags;
tank->beginpressure = beginpressure * 2 * PSI / BAR;
tank->endpressure = endpressure * 2 * PSI / BAR;
tank->usage = DC_USAGE_NONE;
break;
default:
return DC_STATUS_UNSUPPORTED;
@ -331,14 +317,14 @@ oceanic_vtpro_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_
time = timestamp * 60 + (i + 1) * interval;
else
time = timestamp * 60 + (i + 1) * 60.0 / count + 0.5;
sample.time = time;
if (callback) callback (DC_SAMPLE_TIME, sample, userdata);
sample.time = time * 1000;
if (callback) callback (DC_SAMPLE_TIME, &sample, userdata);
// Vendor specific data
sample.vendor.type = SAMPLE_VENDOR_OCEANIC_VTPRO;
sample.vendor.size = PAGESIZE / 2;
sample.vendor.data = data + offset;
if (callback) callback (DC_SAMPLE_VENDOR, sample, userdata);
if (callback) callback (DC_SAMPLE_VENDOR, &sample, userdata);
// Depth (ft)
unsigned int depth = 0;
@ -348,7 +334,7 @@ oceanic_vtpro_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_
depth = data[offset + 3];
}
sample.depth = depth * FEET;
if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata);
if (callback) callback (DC_SAMPLE_DEPTH, &sample, userdata);
// Temperature (°F)
unsigned int temperature = 0;
@ -358,7 +344,7 @@ oceanic_vtpro_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_
temperature = data[offset + 6];
}
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);
// NDL / Deco
if (parser->model != AERIS500AI) {
@ -372,7 +358,8 @@ oceanic_vtpro_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_
sample.deco.depth = 0.0;
}
sample.deco.time = decotime * 60;
if (callback) callback (DC_SAMPLE_DECO, sample, userdata);
sample.deco.tts = 0;
if (callback) callback (DC_SAMPLE_DECO, &sample, userdata);
}
offset += PAGESIZE / 2;

View File

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

View File

@ -58,7 +58,6 @@ struct oceans_s1_parser_t {
unsigned int divetime;
};
static dc_status_t oceans_s1_parser_set_data(dc_parser_t *abstract, const unsigned char *data, unsigned int size);
static dc_status_t oceans_s1_parser_get_datetime(dc_parser_t *abstract, dc_datetime_t *datetime);
static dc_status_t oceans_s1_parser_get_field(dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value);
static dc_status_t oceans_s1_parser_samples_foreach(dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata);
@ -66,7 +65,6 @@ static dc_status_t oceans_s1_parser_samples_foreach(dc_parser_t *abstract, dc_sa
static const dc_parser_vtable_t oceans_s1_parser_vtable = {
sizeof(oceans_s1_parser_t),
DC_FAMILY_OCEANS_S1,
oceans_s1_parser_set_data, /* set_data */
NULL, /* set_clock */
NULL, /* set_atmospheric */
NULL, /* set_density */
@ -77,7 +75,7 @@ static const dc_parser_vtable_t oceans_s1_parser_vtable = {
};
dc_status_t
oceans_s1_parser_create (dc_parser_t **out, dc_context_t *context)
oceans_s1_parser_create (dc_parser_t **out, dc_context_t *context, const unsigned char data[], size_t size)
{
oceans_s1_parser_t *parser = NULL;
@ -85,7 +83,7 @@ oceans_s1_parser_create (dc_parser_t **out, dc_context_t *context)
return DC_STATUS_INVALIDARGS;
// Allocate memory.
parser = (oceans_s1_parser_t *) dc_parser_allocate (context, &oceans_s1_parser_vtable);
parser = (oceans_s1_parser_t *) dc_parser_allocate (context, &oceans_s1_parser_vtable, data, size);
if (parser == NULL) {
ERROR (context, "Failed to allocate memory.");
return DC_STATUS_NOMEMORY;
@ -105,23 +103,6 @@ oceans_s1_parser_create (dc_parser_t **out, dc_context_t *context)
return DC_STATUS_SUCCESS;
}
static dc_status_t
oceans_s1_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size)
{
oceans_s1_parser_t *parser = (oceans_s1_parser_t *) abstract;
// Reset the cache.
parser->cached = 0;
parser->timestamp = 0;
parser->number = 0;
parser->divemode = 0;
parser->oxygen = 0;
parser->maxdepth = 0;
parser->divetime = 0;
return DC_STATUS_SUCCESS;
}
static dc_status_t
oceans_s1_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime)
{
@ -166,6 +147,7 @@ oceans_s1_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigne
*((unsigned int *) value) = parser->divemode == SCUBA;
break;
case DC_FIELD_GASMIX:
gasmix->usage = DC_USAGE_NONE;
gasmix->helium = 0.0;
gasmix->oxygen = parser->oxygen / 100.0;
gasmix->nitrogen = 1.0 - gasmix->oxygen - gasmix->helium;
@ -249,19 +231,19 @@ oceans_s1_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t ca
unsigned int nsamples = seconds / interval;
for (unsigned int i = 0; i < nsamples; ++i) {
time += interval;
sample.time = time;
if (callback) callback (DC_SAMPLE_TIME, sample, userdata);
sample.time = time * 1000;
if (callback) callback (DC_SAMPLE_TIME, &sample, userdata);
sample.depth = 0;
if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata);
if (callback) callback (DC_SAMPLE_DEPTH, &sample, userdata);
}
time += interval;
sample.time = time;
if (callback) callback (DC_SAMPLE_TIME, sample, userdata);
sample.time = time * 1000;
if (callback) callback (DC_SAMPLE_TIME, &sample, userdata);
sample.depth = depth / 100.0;
if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata);
if (callback) callback (DC_SAMPLE_DEPTH, &sample, userdata);
} else if (strncmp(line, "enddive", 7) == 0) {
if (sscanf(line, "enddive %u,%u", &maxdepth, &divetime) != 2) {
ERROR (parser->base.context, "Failed to parse the line '%s'.", line);
@ -280,14 +262,14 @@ oceans_s1_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t ca
}
time += interval;
sample.time = time;
if (callback) callback (DC_SAMPLE_TIME, sample, userdata);
sample.time = time * 1000;
if (callback) callback (DC_SAMPLE_TIME, &sample, userdata);
sample.depth = depth / 100.0;
if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata);
if (callback) callback (DC_SAMPLE_DEPTH, &sample, userdata);
sample.temperature = temperature;
if (callback) callback (DC_SAMPLE_TEMPERATURE, sample, userdata);
if (callback) callback (DC_SAMPLE_TEMPERATURE, &sample, userdata);
if (events & EVENT_DECO_STOP) {
sample.deco.type = DC_DECO_DECOSTOP;
@ -298,7 +280,8 @@ oceans_s1_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t ca
}
sample.deco.depth = 0.0;
sample.deco.time = 0;
if (callback) callback (DC_SAMPLE_DECO, sample, userdata);
sample.deco.tts = 0;
if (callback) callback (DC_SAMPLE_DECO, &sample, userdata);
}
}

View File

@ -41,7 +41,7 @@ typedef struct dc_parser_vtable_t dc_parser_vtable_t;
struct dc_parser_t {
const dc_parser_vtable_t *vtable;
dc_context_t *context;
const unsigned char *data;
unsigned char *data;
unsigned int size;
};
@ -50,8 +50,6 @@ struct dc_parser_vtable_t {
dc_family_t type;
dc_status_t (*set_data) (dc_parser_t *parser, const unsigned char *data, unsigned int size);
dc_status_t (*set_clock) (dc_parser_t *parser, unsigned int devtime, dc_ticks_t systime);
dc_status_t (*set_atmospheric) (dc_parser_t *parser, double atmospheric);
@ -68,7 +66,7 @@ struct dc_parser_vtable_t {
};
dc_parser_t *
dc_parser_allocate (dc_context_t *context, const dc_parser_vtable_t *vtable);
dc_parser_allocate (dc_context_t *context, const dc_parser_vtable_t *vtable, const unsigned char data[], size_t size);
void
dc_parser_deallocate (dc_parser_t *parser);
@ -84,7 +82,7 @@ typedef struct sample_statistics_t {
#define SAMPLE_STATISTICS_INITIALIZER {0, 0.0}
void
sample_statistics_cb (dc_sample_type_t type, dc_sample_value_t value, void *userdata);
sample_statistics_cb (dc_sample_type_t type, const dc_sample_value_t *value, void *userdata);
#ifdef __cplusplus
}

View File

@ -20,6 +20,7 @@
*/
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "suunto_d9.h"
@ -72,7 +73,7 @@
#define REACTPROWHITE 0x4354
static dc_status_t
dc_parser_new_internal (dc_parser_t **out, dc_context_t *context, dc_family_t family, unsigned int model, unsigned int devtime, dc_ticks_t systime)
dc_parser_new_internal (dc_parser_t **out, dc_context_t *context, const unsigned char data[], size_t size, dc_family_t family, unsigned int model)
{
dc_status_t rc = DC_STATUS_SUCCESS;
dc_parser_t *parser = NULL;
@ -82,126 +83,127 @@ dc_parser_new_internal (dc_parser_t **out, dc_context_t *context, dc_family_t fa
switch (family) {
case DC_FAMILY_SUUNTO_SOLUTION:
rc = suunto_solution_parser_create (&parser, context);
rc = suunto_solution_parser_create (&parser, context, data, size);
break;
case DC_FAMILY_SUUNTO_EON:
rc = suunto_eon_parser_create (&parser, context, 0);
rc = suunto_eon_parser_create (&parser, context, data, size, 0);
break;
case DC_FAMILY_SUUNTO_VYPER:
if (model == 0x01)
rc = suunto_eon_parser_create (&parser, context, 1);
rc = suunto_eon_parser_create (&parser, context, data, size, 1);
else
rc = suunto_vyper_parser_create (&parser, context);
rc = suunto_vyper_parser_create (&parser, context, data, size);
break;
case DC_FAMILY_SUUNTO_VYPER2:
case DC_FAMILY_SUUNTO_D9:
rc = suunto_d9_parser_create (&parser, context, model);
rc = suunto_d9_parser_create (&parser, context, data, size, model);
break;
case DC_FAMILY_SUUNTO_EONSTEEL:
rc = suunto_eonsteel_parser_create(&parser, context, model);
rc = suunto_eonsteel_parser_create(&parser, context, data, size, model);
break;
case DC_FAMILY_UWATEC_ALADIN:
case DC_FAMILY_UWATEC_MEMOMOUSE:
rc = uwatec_memomouse_parser_create (&parser, context, devtime, systime);
rc = uwatec_memomouse_parser_create (&parser, context, data, size);
break;
case DC_FAMILY_UWATEC_SMART:
rc = uwatec_smart_parser_create (&parser, context, model);
rc = uwatec_smart_parser_create (&parser, context, data, size, model);
break;
case DC_FAMILY_REEFNET_SENSUS:
rc = reefnet_sensus_parser_create (&parser, context, devtime, systime);
rc = reefnet_sensus_parser_create (&parser, context, data, size);
break;
case DC_FAMILY_REEFNET_SENSUSPRO:
rc = reefnet_sensuspro_parser_create (&parser, context, devtime, systime);
rc = reefnet_sensuspro_parser_create (&parser, context, data, size);
break;
case DC_FAMILY_REEFNET_SENSUSULTRA:
rc = reefnet_sensusultra_parser_create (&parser, context, devtime, systime);
rc = reefnet_sensusultra_parser_create (&parser, context, data, size);
break;
case DC_FAMILY_OCEANIC_VTPRO:
rc = oceanic_vtpro_parser_create (&parser, context, model);
rc = oceanic_vtpro_parser_create (&parser, context, data, size, model);
break;
case DC_FAMILY_OCEANIC_VEO250:
rc = oceanic_veo250_parser_create (&parser, context, model);
rc = oceanic_veo250_parser_create (&parser, context, data, size, model);
break;
case DC_FAMILY_OCEANIC_ATOM2:
case DC_FAMILY_PELAGIC_I330R:
if (model == REACTPROWHITE)
rc = oceanic_veo250_parser_create (&parser, context, model);
rc = oceanic_veo250_parser_create (&parser, context, data, size, model);
else
rc = oceanic_atom2_parser_create (&parser, context, model);
rc = oceanic_atom2_parser_create (&parser, context, data, size, model);
break;
case DC_FAMILY_MARES_NEMO:
case DC_FAMILY_MARES_PUCK:
rc = mares_nemo_parser_create (&parser, context, model);
rc = mares_nemo_parser_create (&parser, context, data, size, model);
break;
case DC_FAMILY_MARES_DARWIN:
rc = mares_darwin_parser_create (&parser, context, model);
rc = mares_darwin_parser_create (&parser, context, data, size, model);
break;
case DC_FAMILY_MARES_ICONHD:
rc = mares_iconhd_parser_create (&parser, context, model);
rc = mares_iconhd_parser_create (&parser, context, data, size, model);
break;
case DC_FAMILY_HW_OSTC:
rc = hw_ostc_parser_create (&parser, context);
rc = hw_ostc_parser_create (&parser, context, data, size);
break;
case DC_FAMILY_HW_FROG:
case DC_FAMILY_HW_OSTC3:
rc = hw_ostc3_parser_create (&parser, context, model);
rc = hw_ostc3_parser_create (&parser, context, data, size, model);
break;
case DC_FAMILY_CRESSI_EDY:
case DC_FAMILY_ZEAGLE_N2ITION3:
rc = cressi_edy_parser_create (&parser, context, model);
rc = cressi_edy_parser_create (&parser, context, data, size, model);
break;
case DC_FAMILY_CRESSI_LEONARDO:
rc = cressi_leonardo_parser_create (&parser, context, model);
rc = cressi_leonardo_parser_create (&parser, context, data, size, model);
break;
case DC_FAMILY_CRESSI_GOA:
rc = cressi_goa_parser_create (&parser, context, model);
rc = cressi_goa_parser_create (&parser, context, data, size, model);
break;
case DC_FAMILY_ATOMICS_COBALT:
rc = atomics_cobalt_parser_create (&parser, context);
rc = atomics_cobalt_parser_create (&parser, context, data, size);
break;
case DC_FAMILY_SHEARWATER_PREDATOR:
rc = shearwater_predator_parser_create (&parser, context, model);
rc = shearwater_predator_parser_create (&parser, context, data, size, model);
break;
case DC_FAMILY_SHEARWATER_PETREL:
rc = shearwater_petrel_parser_create (&parser, context, model);
rc = shearwater_petrel_parser_create (&parser, context, data, size, model);
break;
case DC_FAMILY_DIVERITE_NITEKQ:
rc = diverite_nitekq_parser_create (&parser, context);
rc = diverite_nitekq_parser_create (&parser, context, data, size);
break;
case DC_FAMILY_CITIZEN_AQUALAND:
rc = citizen_aqualand_parser_create (&parser, context);
rc = citizen_aqualand_parser_create (&parser, context, data, size);
break;
case DC_FAMILY_DIVESYSTEM_IDIVE:
rc = divesystem_idive_parser_create (&parser, context, model);
rc = divesystem_idive_parser_create (&parser, context, data, size, model);
break;
case DC_FAMILY_COCHRAN_COMMANDER:
rc = cochran_commander_parser_create (&parser, context, model);
rc = cochran_commander_parser_create (&parser, context, data, size, model);
break;
case DC_FAMILY_TECDIVING_DIVECOMPUTEREU:
rc = tecdiving_divecomputereu_parser_create (&parser, context);
rc = tecdiving_divecomputereu_parser_create (&parser, context, data, size);
break;
case DC_FAMILY_MCLEAN_EXTREME:
rc = mclean_extreme_parser_create (&parser, context);
rc = mclean_extreme_parser_create (&parser, context, data, size);
break;
case DC_FAMILY_LIQUIVISION_LYNX:
rc = liquivision_lynx_parser_create (&parser, context, model);
rc = liquivision_lynx_parser_create (&parser, context, data, size, model);
break;
case DC_FAMILY_SPORASUB_SP2:
rc = sporasub_sp2_parser_create (&parser, context);
rc = sporasub_sp2_parser_create (&parser, context, data, size);
break;
case DC_FAMILY_DEEPSIX_EXCURSION:
rc = deepsix_excursion_parser_create (&parser, context);
rc = deepsix_excursion_parser_create (&parser, context, data, size);
break;
case DC_FAMILY_SEAC_SCREEN:
rc = seac_screen_parser_create (&parser, context);
rc = seac_screen_parser_create (&parser, context, data, size);
break;
case DC_FAMILY_DEEPBLU_COSMIQ:
rc = deepblu_cosmiq_parser_create (&parser, context);
rc = deepblu_cosmiq_parser_create (&parser, context, data, size);
break;
case DC_FAMILY_OCEANS_S1:
rc = oceans_s1_parser_create (&parser, context);
rc = oceans_s1_parser_create (&parser, context, data, size);
break;
case DC_FAMILY_DIVESOFT_FREEDOM:
rc = divesoft_freedom_parser_create (&parser, context);
rc = divesoft_freedom_parser_create (&parser, context, data, size);
break;
default:
return DC_STATUS_INVALIDARGS;
@ -213,26 +215,42 @@ dc_parser_new_internal (dc_parser_t **out, dc_context_t *context, dc_family_t fa
}
dc_status_t
dc_parser_new (dc_parser_t **out, dc_device_t *device)
dc_parser_new (dc_parser_t **out, dc_device_t *device, const unsigned char data[], size_t size)
{
dc_status_t status = DC_STATUS_SUCCESS;
dc_parser_t *parser = NULL;
if (device == NULL)
return DC_STATUS_INVALIDARGS;
return dc_parser_new_internal (out, device->context,
dc_device_get_type (device), device->devinfo.model,
device->clock.devtime, device->clock.systime);
status = dc_parser_new_internal (&parser, device->context, data, size,
dc_device_get_type (device), device->devinfo.model);
if (status != DC_STATUS_SUCCESS)
goto error_exit;
status = dc_parser_set_clock (parser, device->clock.devtime, device->clock.systime);
if (status != DC_STATUS_SUCCESS && status != DC_STATUS_UNSUPPORTED)
goto error_free;
*out = parser;
return DC_STATUS_SUCCESS;
error_free:
dc_parser_deallocate (parser);
error_exit:
return status;
}
dc_status_t
dc_parser_new2 (dc_parser_t **out, dc_context_t *context, dc_descriptor_t *descriptor, unsigned int devtime, dc_ticks_t systime)
dc_parser_new2 (dc_parser_t **out, dc_context_t *context, dc_descriptor_t *descriptor, const unsigned char data[], size_t size)
{
return dc_parser_new_internal (out, context,
dc_descriptor_get_type (descriptor), dc_descriptor_get_model (descriptor),
devtime, systime);
return dc_parser_new_internal (out, context, data, size,
dc_descriptor_get_type (descriptor), dc_descriptor_get_model (descriptor));
}
dc_parser_t *
dc_parser_allocate (dc_context_t *context, const dc_parser_vtable_t *vtable)
dc_parser_allocate (dc_context_t *context, const dc_parser_vtable_t *vtable, const unsigned char data[], size_t size)
{
dc_parser_t *parser = NULL;
@ -249,15 +267,34 @@ dc_parser_allocate (dc_context_t *context, const dc_parser_vtable_t *vtable)
// Initialize the base class.
parser->vtable = vtable;
parser->context = context;
parser->data = NULL;
parser->size = 0;
if (size) {
// Allocate memory for the data.
parser->data = malloc (size);
if (parser->data == NULL) {
ERROR (context, "Failed to allocate memory.");
free (parser);
return NULL;
}
// Copy the data.
memcpy (parser->data, data, size);
parser->size = size;
} else {
parser->data = NULL;
parser->size = 0;
}
return parser;
}
void
dc_parser_deallocate (dc_parser_t *parser)
{
if (parser == NULL)
return;
free (parser->data);
free (parser);
}
@ -320,22 +357,6 @@ dc_parser_set_density (dc_parser_t *parser, double density)
}
dc_status_t
dc_parser_set_data (dc_parser_t *parser, const unsigned char *data, unsigned int size)
{
if (parser == NULL)
return DC_STATUS_UNSUPPORTED;
if (parser->vtable->set_data == NULL)
return DC_STATUS_UNSUPPORTED;
parser->data = data;
parser->size = size;
return parser->vtable->set_data (parser, data, size);
}
dc_status_t
dc_parser_get_datetime (dc_parser_t *parser, dc_datetime_t *datetime)
{
@ -393,17 +414,17 @@ dc_parser_destroy (dc_parser_t *parser)
void
sample_statistics_cb (dc_sample_type_t type, dc_sample_value_t value, void *userdata)
sample_statistics_cb (dc_sample_type_t type, const dc_sample_value_t *value, void *userdata)
{
sample_statistics_t *statistics = (sample_statistics_t *) userdata;
switch (type) {
case DC_SAMPLE_TIME:
statistics->divetime = value.time;
statistics->divetime = value->time / 1000;
break;
case DC_SAMPLE_DEPTH:
if (statistics->maxdepth < value.depth)
statistics->maxdepth = value.depth;
if (statistics->maxdepth < value->depth)
statistics->maxdepth = value->depth;
break;
default:
break;

646
src/pelagic_i330r.c Normal file
View File

@ -0,0 +1,646 @@
/*
* libdivecomputer
*
* Copyright (C) 2023 Janice McLaughlin
*
* 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> // memcpy
#include <stdlib.h> // malloc, free
#include <assert.h>
#include <libdivecomputer/ble.h>
#include "pelagic_i330r.h"
#include "oceanic_common.h"
#include "context-private.h"
#include "device-private.h"
#include "ringbuffer.h"
#include "rbstream.h"
#include "checksum.h"
#include "array.h"
#define UNDEFINED 0
#define STARTBYTE 0xCD
#define FLAG_NONE 0x00
#define FLAG_REQUEST 0x40
#define FLAG_DATA 0x80
#define FLAG_LAST 0xC0
#define CMD_ACCESS_REQUEST 0xFA
#define CMD_ACCESS_CODE 0xFB
#define CMD_AUTHENTICATION 0x97
#define CMD_WAKEUP_RDONLY 0x21
#define CMD_WAKEUP_RDWR 0x22
#define CMD_READ_HW_CAL 0x27
#define CMD_READ_A2D 0x25
#define CMD_READ_DEVICE_REC 0x31
#define CMD_READ_GEN_SET 0x29
#define CMD_READ_EXFLASHMAP 0x2F
#define CMD_READ_FLASH 0x0D
#define RSP_READY 1
#define RSP_DONE 2
#define MAXPACKET 255
#define MAXPASSCODE 6
typedef struct pelagic_i330r_device_t {
oceanic_common_device_t base;
dc_iostream_t *iostream;
unsigned char accesscode[16];
unsigned char id[16];
unsigned char hwcal[256];
unsigned char flashmap[256];
unsigned int model;
} pelagic_i330r_device_t;
static dc_status_t pelagic_i330r_device_read (dc_device_t *abstract, unsigned int address, unsigned char data[], unsigned int size);
static dc_status_t pelagic_i330r_device_devinfo (dc_device_t *abstract, dc_event_progress_t *progress);
static dc_status_t pelagic_i330r_device_pointers (dc_device_t *abstract, dc_event_progress_t *progress, unsigned int *rb_logbook_begin, unsigned int *rb_logbook_end, unsigned int *rb_profile_begin, unsigned int *rb_profile_end);
static const oceanic_common_device_vtable_t pelagic_i330r_device_vtable = {
{
sizeof(pelagic_i330r_device_t),
DC_FAMILY_PELAGIC_I330R,
oceanic_common_device_set_fingerprint, /* set_fingerprint */
pelagic_i330r_device_read, /* read */
NULL, /* write */
oceanic_common_device_dump, /* dump */
oceanic_common_device_foreach, /* foreach */
NULL, /* timesync */
NULL /* close */
},
pelagic_i330r_device_devinfo,
pelagic_i330r_device_pointers,
oceanic_common_device_logbook,
oceanic_common_device_profile,
};
static const oceanic_common_layout_t pelagic_i330r = {
0x00400000, /* memsize */
0, /* highmem */
UNDEFINED, /* cf_devinfo */
UNDEFINED, /* cf_pointers */
0x00102000, /* rb_logbook_begin */
0x00106000, /* rb_logbook_end */
64, /* rb_logbook_entry_size */
0, /* rb_logbook_direction */
0x0010A000, /* rb_profile_begin */
0x00400000, /* rb_profile_end */
1, /* pt_mode_global */
4, /* pt_mode_logbook */
UNDEFINED, /* pt_mode_serial */
};
static const oceanic_common_layout_t pelagic_dsx = {
0x02000000, /* memsize */
0, /* highmem */
UNDEFINED, /* cf_devinfo */
UNDEFINED, /* cf_pointers */
0x00800000, /* rb_logbook_begin */
0x00880000, /* rb_logbook_end */
512, /* rb_logbook_entry_size */
1, /* rb_logbook_direction */
0x01000000, /* rb_profile_begin */
0x02000000, /* rb_profile_end */
1, /* pt_mode_global */
4, /* pt_mode_logbook */
UNDEFINED /* pt_mode_serial */
};
static unsigned char
checksum (const unsigned char data[], unsigned int size)
{
unsigned int csum = 0;
for (unsigned int i = 0; i < size; i++) {
unsigned int a = csum ^ data[i];
unsigned int b = (a >> 7) ^ ((a >> 4) ^ a);
csum = ((b << 4) & 0xFF) ^ ((b << 1) & 0xFF);
}
return csum & 0xFF;
}
static dc_status_t
pelagic_i330r_send (pelagic_i330r_device_t *device, unsigned char cmd, unsigned char flag, const unsigned char data[], unsigned int size)
{
dc_status_t status = DC_STATUS_SUCCESS;
dc_device_t *abstract = (dc_device_t *) device;
if (size > MAXPACKET) {
ERROR (abstract->context, "Packet payload is too large (%u).", size);
return DC_STATUS_INVALIDARGS;
}
unsigned char packet[MAXPACKET + 5] = {
STARTBYTE,
flag,
cmd,
0,
size
};
if (size) {
memcpy(packet + 5, data, size);
}
packet[3] = checksum (packet, size + 5);
// Send the data packet.
status = dc_iostream_write (device->iostream, packet, size + 5, NULL);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to send the command.");
return status;
}
return DC_STATUS_SUCCESS;
}
static dc_status_t
pelagic_i330r_recv (pelagic_i330r_device_t *device, unsigned char cmd, unsigned char data[], unsigned int size, unsigned int *errorcode)
{
dc_status_t status = DC_STATUS_SUCCESS;
dc_device_t *abstract = (dc_device_t *) device;
unsigned char packet[MAXPACKET + 5] = {0};
unsigned int errcode = 0;
unsigned int nbytes = 0;
while (1) {
// Read the data packet.
size_t transferred = 0;
status = dc_iostream_read (device->iostream, packet, sizeof(packet), &transferred);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to receive the data packet.");
return status;
}
// Verify the minimum packet size.
if (transferred < 5) {
ERROR (abstract->context, "Invalid packet length (" DC_PRINTF_SIZE ").", transferred);
return DC_STATUS_PROTOCOL;
}
// Verify the start byte.
if (packet[0] != STARTBYTE) {
ERROR (abstract->context, "Unexpected packet start byte (%02x).", packet[0]);
return DC_STATUS_PROTOCOL;
}
// Verify the command byte.
if (packet[2] != cmd) {
ERROR (abstract->context, "Unexpected packet command byte (%02x).", packet[2]);
return DC_STATUS_PROTOCOL;
}
// Verify the length byte.
unsigned int length = packet[4];
if (length + 5 > transferred) {
ERROR (abstract->context, "Invalid packet length (%u).", length);
return DC_STATUS_PROTOCOL;
}
// Verify the checksum.
unsigned char crc = packet[3]; packet[3] = 0;
unsigned char ccrc = checksum (packet, length + 5);
if (crc != ccrc) {
ERROR (abstract->context, "Unexpected packet checksum (%02x %02x).", crc, ccrc);
return DC_STATUS_PROTOCOL;
}
// Check the flag byte for the last packet.
unsigned char flag = packet[1];
if ((flag & FLAG_LAST) == FLAG_LAST) {
// The last packet (typically 2 bytes) does not get appended!
if (length) {
errcode = packet[5];
}
break;
}
// 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) {
unsigned int n = length;
if (nbytes + n > size) {
n = size - nbytes;
}
memcpy (data + nbytes, packet + 5, n);
}
nbytes += length;
}
// Verify the expected number of bytes.
if (nbytes != size) {
ERROR (abstract->context, "Unexpected number of bytes received (%u %u).", nbytes, size);
return DC_STATUS_PROTOCOL;
}
if (errorcode) {
*errorcode = errcode;
}
return DC_STATUS_SUCCESS;
}
static dc_status_t
pelagic_i330r_transfer (pelagic_i330r_device_t *device, unsigned char cmd, unsigned char flag, const unsigned char data[], unsigned int size, unsigned char answer[], unsigned int asize, unsigned int response)
{
dc_status_t status = DC_STATUS_SUCCESS;
dc_device_t *abstract = (dc_device_t *) device;
unsigned int errorcode = 0;
status = pelagic_i330r_send (device, cmd, flag, data, size);
if (status != DC_STATUS_SUCCESS)
return status;
status = pelagic_i330r_recv (device, cmd, answer, asize, &errorcode);
if (status != DC_STATUS_SUCCESS)
return status;
if (errorcode != response) {
ERROR (abstract->context, "Unexpected response code (%u)", errorcode);
return DC_STATUS_PROTOCOL;
}
return status;
}
static dc_status_t
pelagic_i330r_init_accesscode (pelagic_i330r_device_t *device)
{
dc_status_t status = DC_STATUS_SUCCESS;
const unsigned char zero[9] = {0};
status = pelagic_i330r_transfer (device, CMD_ACCESS_REQUEST, FLAG_REQUEST, zero, sizeof(zero), NULL, 0, RSP_READY);
if (status != DC_STATUS_SUCCESS)
return status;
status = pelagic_i330r_transfer (device, CMD_ACCESS_REQUEST, FLAG_DATA, device->accesscode, sizeof(device->accesscode), NULL, 0, RSP_DONE);
if (status != DC_STATUS_SUCCESS)
return status;
return status;
}
static dc_status_t
pelagic_i330r_init_passcode (pelagic_i330r_device_t *device, const char *pincode)
{
dc_status_t status = DC_STATUS_SUCCESS;
dc_device_t *abstract = (dc_device_t *) device;
unsigned char passcode[MAXPASSCODE] = {0};
// Check the maximum length.
size_t len = pincode ? strlen (pincode) : 0;
if (len > sizeof(passcode)) {
ERROR (abstract->context, "Invalid pincode length (" DC_PRINTF_SIZE ").", len);
return DC_STATUS_INVALIDARGS;
}
// Convert to binary number.
unsigned int offset = sizeof(passcode) - len;
for (unsigned int i = 0; i < len; i++) {
unsigned char c = pincode[i];
if (c < '0' || c > '9') {
ERROR (abstract->context, "Invalid pincode character (%c).", c);
return DC_STATUS_INVALIDARGS;
}
passcode[offset + i] = c - '0';
}
const unsigned char zero[9] = {0};
status = pelagic_i330r_transfer (device, CMD_ACCESS_CODE, FLAG_REQUEST, zero, sizeof(zero), NULL, 0, RSP_READY);
if (status != DC_STATUS_SUCCESS)
return status;
status = pelagic_i330r_transfer (device, CMD_ACCESS_CODE, FLAG_DATA, passcode, sizeof(passcode), device->accesscode, sizeof(device->accesscode), RSP_DONE);
if (status != DC_STATUS_SUCCESS)
return status;
HEXDUMP (abstract->context, DC_LOGLEVEL_DEBUG, "Access code", device->accesscode, sizeof(device->accesscode));
return status;
}
static dc_status_t
pelagic_i330r_init_handshake (pelagic_i330r_device_t *device, unsigned int readwrite)
{
dc_status_t status = DC_STATUS_SUCCESS;
dc_device_t *abstract = (dc_device_t *) device;
const unsigned char cmd = readwrite ? CMD_WAKEUP_RDWR : CMD_WAKEUP_RDONLY;
const unsigned char args[9] = {0, 0, 0, 0, 0x0C, 0, 0, 0, 0};
status = pelagic_i330r_transfer (device, cmd, FLAG_REQUEST, args, sizeof(args), device->id, sizeof(device->id), RSP_DONE);
if (status != DC_STATUS_SUCCESS)
return status;
HEXDUMP (abstract->context, DC_LOGLEVEL_DEBUG, "ID", device->id, sizeof(device->id));
device->model = array_uint16_be (device->id + 12);
return status;
}
static dc_status_t
pelagic_i330r_init_auth (pelagic_i330r_device_t *device)
{
dc_status_t status = DC_STATUS_SUCCESS;
const unsigned char args[2][9] = {
{0xFF, 0xFF, 0xFF, 0xFF}, // DSX
{0x37, 0x30, 0x31, 0x55}, // I330R
};
unsigned int args_idx = device->model == DSX ? 0 : 1;
status = pelagic_i330r_transfer (device, CMD_AUTHENTICATION, FLAG_REQUEST, args[args_idx], sizeof(args[args_idx]), NULL, 0, RSP_READY);
if (status != DC_STATUS_SUCCESS)
return status;
return status;
}
static dc_status_t
pelagic_i330r_init (pelagic_i330r_device_t *device)
{
dc_status_t status = DC_STATUS_SUCCESS;
dc_device_t *abstract = (dc_device_t *) device;
// Get the bluetooth access code.
status = dc_iostream_ioctl (device->iostream, DC_IOCTL_BLE_GET_ACCESSCODE, device->accesscode, sizeof(device->accesscode));
if (status != DC_STATUS_SUCCESS && status != DC_STATUS_UNSUPPORTED) {
ERROR (abstract->context, "Failed to get the access code.");
return status;
}
if (array_isequal (device->accesscode, sizeof(device->accesscode), 0)) {
// Request to display the PIN code.
status = pelagic_i330r_init_accesscode (device);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to display the PIN code.");
return status;
}
// Get the bluetooth PIN code.
char pincode[6 + 1] = {0};
status = dc_iostream_ioctl (device->iostream, DC_IOCTL_BLE_GET_PINCODE, pincode, sizeof(pincode));
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to get the PIN code.");
return status;
}
// Force a null terminated string.
pincode[sizeof(pincode) - 1] = 0;
// Request the access code.
status = pelagic_i330r_init_passcode (device, pincode);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to request the access code.");
return status;
}
// Store the bluetooth access code.
status = dc_iostream_ioctl (device->iostream, DC_IOCTL_BLE_SET_ACCESSCODE, device->accesscode, sizeof(device->accesscode));
if (status != DC_STATUS_SUCCESS && status != DC_STATUS_UNSUPPORTED) {
ERROR (abstract->context, "Failed to store the access code.");
return status;
}
}
// Request access.
status = pelagic_i330r_init_accesscode (device);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to request access.");
return status;
}
// Send the wakeup command.
status = pelagic_i330r_init_handshake (device, 1);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to send the wakeup command.");
return status;
}
// Send the authentication code.
status = pelagic_i330r_init_auth (device);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to send the authentication code.");
return status;
}
return status;
}
static dc_status_t
pelagic_i330r_download (pelagic_i330r_device_t *device, unsigned char cmd, const unsigned char data[], unsigned int size, unsigned char answer[], unsigned int asize)
{
dc_status_t status = DC_STATUS_SUCCESS;
dc_device_t *abstract = (dc_device_t *) device;
status = pelagic_i330r_transfer (device, cmd, FLAG_REQUEST, data, size, answer, asize, RSP_DONE);
if (status != DC_STATUS_SUCCESS)
return status;
// Verify the checksum
unsigned short crc = array_uint16_be (answer + asize - 2);
unsigned short ccrc = checksum_crc16_ccitt (answer, asize - 2, 0xffff, 0x0000);
if (crc != ccrc) {
ERROR (abstract->context, "Unexpected data checksum (%04x %04x).", crc, ccrc);
return DC_STATUS_PROTOCOL;
}
return status;
}
dc_status_t
pelagic_i330r_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t *iostream, unsigned int model)
{
dc_status_t status = DC_STATUS_SUCCESS;
pelagic_i330r_device_t *device = NULL;
if (out == NULL)
return DC_STATUS_INVALIDARGS;
// Allocate memory.
device = (pelagic_i330r_device_t *) dc_device_allocate (context, &pelagic_i330r_device_vtable.base);
if (device == NULL) {
ERROR (context, "Failed to allocate memory.");
return DC_STATUS_NOMEMORY;
}
// Initialize the base class.
oceanic_common_device_init (&device->base);
// Override the base class values.
device->base.multipage = 256;
// Set the default values.
device->iostream = iostream;
memset (device->accesscode, 0, sizeof(device->accesscode));
memset (device->id, 0, sizeof(device->id));
memset (device->hwcal, 0, sizeof(device->hwcal));
memset (device->flashmap, 0, sizeof(device->flashmap));
device->model = 0;
// Set the timeout for receiving data (3000 ms).
status = dc_iostream_set_timeout (device->iostream, 3000);
if (status != DC_STATUS_SUCCESS) {
ERROR (context, "Failed to set the timeout.");
goto error_free;
}
// Perform the bluetooth authentication.
status = pelagic_i330r_init (device);
if (status != DC_STATUS_SUCCESS) {
ERROR (context, "Failed to perform the bluetooth authentication.");
goto error_free;
}
// Download the calibration data.
const unsigned char args[9] = {0, 0, 0, 0, 0, 0x01, 0, 0, 0};
status = pelagic_i330r_download (device, CMD_READ_HW_CAL, args, sizeof(args), device->hwcal, sizeof(device->hwcal));
if (status != DC_STATUS_SUCCESS) {
ERROR (context, "Failed to download the calibration data.");
goto error_free;
}
HEXDUMP (context, DC_LOGLEVEL_DEBUG, "Hwcal", device->hwcal, sizeof(device->hwcal));
// Download the flash map.
const unsigned char zero[9] = {0};
status = pelagic_i330r_download (device, CMD_READ_EXFLASHMAP, zero, sizeof(zero), device->flashmap, sizeof(device->flashmap));
if (status != DC_STATUS_SUCCESS) {
ERROR (context, "Failed to download the flash map.");
goto error_free;
}
HEXDUMP (context, DC_LOGLEVEL_DEBUG, "Flashmap", device->flashmap, sizeof(device->flashmap));
// Detect the memory layout.
if (device->model == DSX) {
device->base.layout = &pelagic_dsx;
} else {
device->base.layout = &pelagic_i330r;
}
*out = (dc_device_t *) device;
return DC_STATUS_SUCCESS;
error_free:
dc_device_deallocate ((dc_device_t *) device);
return status;
}
static dc_status_t
pelagic_i330r_device_read (dc_device_t *abstract, unsigned int address, unsigned char data[], unsigned int size)
{
dc_status_t status = DC_STATUS_SUCCESS;
pelagic_i330r_device_t *device = (pelagic_i330r_device_t*) abstract;
unsigned char command[9] = {0};
array_uint32_le_set(command + 0, address);
array_uint32_le_set(command + 4, size);
status = pelagic_i330r_transfer (device, CMD_READ_FLASH, FLAG_NONE, command, sizeof(command), data, size, RSP_DONE);
if (status != DC_STATUS_SUCCESS) {
return status;
}
return status;
}
static dc_status_t
pelagic_i330r_device_devinfo (dc_device_t *abstract, dc_event_progress_t *progress)
{
pelagic_i330r_device_t *device = (pelagic_i330r_device_t *) abstract;
assert (device != NULL);
// Emit a device info event.
dc_event_devinfo_t devinfo;
devinfo.model = device->model;
devinfo.firmware = 0;
devinfo.serial =
bcd2dec (device->hwcal[12]) +
bcd2dec (device->hwcal[13]) * 100 +
bcd2dec (device->hwcal[14]) * 10000;
device_event_emit (abstract, DC_EVENT_DEVINFO, &devinfo);
return DC_STATUS_SUCCESS;
}
static dc_status_t
pelagic_i330r_device_pointers (dc_device_t *abstract, dc_event_progress_t *progress, unsigned int *rb_logbook_begin, unsigned int *rb_logbook_end, unsigned int *rb_profile_begin, unsigned int *rb_profile_end)
{
pelagic_i330r_device_t *device = (pelagic_i330r_device_t *) abstract;
assert (device != NULL);
assert (device->base.layout != NULL);
assert (rb_logbook_begin != NULL && rb_logbook_end != NULL);
assert (rb_profile_begin != NULL && rb_profile_end != NULL);
const oceanic_common_layout_t *layout = device->base.layout;
// Get the logbook pointers.
unsigned int rb_logbook_min = array_uint32_le (device->flashmap + 0x50);
unsigned int rb_logbook_max = array_uint32_le (device->flashmap + 0x54);
unsigned int rb_logbook_first = array_uint32_le (device->flashmap + 0x58);
unsigned int rb_logbook_last = array_uint32_le (device->flashmap + 0x5C);
if (rb_logbook_min != 0 && rb_logbook_max != 0) {
rb_logbook_max += 1;
}
// Get the profile pointers.
unsigned int rb_profile_min = array_uint32_le (device->flashmap + 0x70);
unsigned int rb_profile_max = array_uint32_le (device->flashmap + 0x74);
unsigned int rb_profile_first = array_uint32_le (device->flashmap + 0x78);
unsigned int rb_profile_last = array_uint32_le (device->flashmap + 0x7C);
if (rb_profile_min != 0 && rb_profile_max != 0) {
rb_profile_max += 1;
}
// Check the logbook ringbuffer area.
if (rb_logbook_min != layout->rb_logbook_begin ||
rb_logbook_max != layout->rb_logbook_end) {
ERROR (abstract->context, "Unexpected logbook ringbuffer area (%08x %08x)",
rb_logbook_min, rb_logbook_max);
return DC_STATUS_DATAFORMAT;
}
// Check the profile ringbuffer area.
if (rb_profile_min != layout->rb_profile_begin ||
rb_profile_max != layout->rb_profile_end) {
ERROR (abstract->context, "Unexpected profile ringbuffer area (%08x %08x)",
rb_profile_min, rb_profile_max);
return DC_STATUS_DATAFORMAT;
}
// Get the begin/end pointers.
if (device->model == DSX) {
*rb_logbook_begin = rb_logbook_first;
*rb_logbook_end = rb_logbook_last;
} else {
*rb_logbook_begin = rb_logbook_min;
*rb_logbook_end = rb_logbook_last + 1;
}
*rb_profile_begin = rb_profile_first;
*rb_profile_end = rb_profile_last;
return DC_STATUS_SUCCESS;
}

View File

@ -1,7 +1,7 @@
/*
* libdivecomputer
*
* Copyright (C) 2017 Jef Driesen
* Copyright (C) 2023 Janice McLaughlin
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -19,30 +19,22 @@
* MA 02110-1301 USA
*/
#ifndef DC_DESCRIPTOR_PRIVATE_H
#define DC_DESCRIPTOR_PRIVATE_H
#ifndef PELAGIC_I330R_H
#define PELAGIC_I330R_H
#include <libdivecomputer/descriptor.h>
#include <libdivecomputer/context.h>
#include <libdivecomputer/iostream.h>
#include <libdivecomputer/device.h>
#include <libdivecomputer/parser.h>
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
typedef struct dc_usb_desc_t {
unsigned short vid;
unsigned short pid;
} dc_usb_desc_t;
typedef struct dc_usb_params_t {
unsigned int interface;
unsigned char endpoint_in;
unsigned char endpoint_out;
} dc_usb_params_t;
int
dc_descriptor_filter (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata, void *params);
dc_status_t
pelagic_i330r_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream, unsigned int model);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* DC_DESCRIPTOR_PRIVATE_H */
#endif /* PELAGIC_I330R_H */

View File

@ -28,11 +28,13 @@
struct dc_rbstream_t {
dc_device_t *device;
dc_rbstream_direction_t direction;
unsigned int pagesize;
unsigned int packetsize;
unsigned int begin;
unsigned int end;
unsigned int address;
unsigned int offset;
unsigned int available;
unsigned int skip;
unsigned char cache[];
@ -53,7 +55,7 @@ iceil (unsigned int x, unsigned int n)
}
dc_status_t
dc_rbstream_new (dc_rbstream_t **out, dc_device_t *device, unsigned int pagesize, unsigned int packetsize, unsigned int begin, unsigned int end, unsigned int address)
dc_rbstream_new (dc_rbstream_t **out, dc_device_t *device, unsigned int pagesize, unsigned int packetsize, unsigned int begin, unsigned int end, unsigned int address, dc_rbstream_direction_t direction)
{
dc_rbstream_t *rbstream = NULL;
@ -78,6 +80,18 @@ dc_rbstream_new (dc_rbstream_t **out, dc_device_t *device, unsigned int pagesize
return DC_STATUS_INVALIDARGS;
}
// Ringbuffer boundaries should not be reversed.
if (begin > end) {
ERROR (device->context, "Ringbuffer boundaries reversed!");
return DC_STATUS_INVALIDARGS;
}
// Packet size should be smaller than the ringbuffer size.
if (packetsize > (end - begin)) {
ERROR (device->context, "Packet size larger than the ringbuffer size!");
return DC_STATUS_INVALIDARGS;
}
// Address should be inside the ringbuffer.
if (address < begin || address > end) {
ERROR (device->context, "Address outside the ringbuffer!");
@ -92,64 +106,64 @@ dc_rbstream_new (dc_rbstream_t **out, dc_device_t *device, unsigned int pagesize
}
rbstream->device = device;
rbstream->direction = direction;
rbstream->pagesize = pagesize;
rbstream->packetsize = packetsize;
rbstream->begin = begin;
rbstream->end = end;
rbstream->address = iceil(address, pagesize);
if (direction == DC_RBSTREAM_FORWARD) {
rbstream->address = ifloor(address, pagesize);
rbstream->skip = address - rbstream->address;
} else {
rbstream->address = iceil(address, pagesize);
rbstream->skip = rbstream->address - address;
}
rbstream->offset = 0;
rbstream->available = 0;
rbstream->skip = rbstream->address - address;
*out = rbstream;
return DC_STATUS_SUCCESS;
}
dc_status_t
dc_rbstream_read (dc_rbstream_t *rbstream, dc_event_progress_t *progress, unsigned char data[], unsigned int size)
static dc_status_t
dc_rbstream_read_backward (dc_rbstream_t *rbstream, dc_event_progress_t *progress, unsigned char data[], unsigned int size)
{
dc_status_t rc = DC_STATUS_SUCCESS;
if (rbstream == NULL)
return DC_STATUS_INVALIDARGS;
unsigned int address = rbstream->address;
unsigned int available = rbstream->available;
unsigned int skip = rbstream->skip;
unsigned int nbytes = 0;
unsigned int offset = size;
while (nbytes < size) {
if (available == 0) {
if (rbstream->available == 0) {
// Handle the ringbuffer wrap point.
if (address == rbstream->begin)
address = rbstream->end;
if (rbstream->address == rbstream->begin)
rbstream->address = rbstream->end;
// Calculate the packet size.
unsigned int len = rbstream->packetsize;
if (rbstream->begin + len > address)
len = address - rbstream->begin;
// Move to the begin of the current packet.
address -= len;
if (rbstream->begin + len > rbstream->address)
len = rbstream->address - rbstream->begin;
// Read the packet into the cache.
rc = dc_device_read (rbstream->device, address, rbstream->cache, rbstream->packetsize);
rc = dc_device_read (rbstream->device, rbstream->address - len, rbstream->cache, rbstream->packetsize);
if (rc != DC_STATUS_SUCCESS)
return rc;
available = len - skip;
skip = 0;
// Move to the end of the next packet.
rbstream->address -= len;
rbstream->available = len - rbstream->skip;
rbstream->skip = 0;
}
unsigned int length = available;
unsigned int length = rbstream->available;
if (nbytes + length > size)
length = size - nbytes;
offset -= length;
available -= length;
rbstream->available -= length;
memcpy (data + offset, rbstream->cache + available, length);
memcpy (data + offset, rbstream->cache + rbstream->available, length);
// Update and emit a progress event.
if (progress) {
@ -160,13 +174,76 @@ dc_rbstream_read (dc_rbstream_t *rbstream, dc_event_progress_t *progress, unsign
nbytes += length;
}
rbstream->address = address;
rbstream->available = available;
rbstream->skip = skip;
return rc;
}
static dc_status_t
dc_rbstream_read_forward (dc_rbstream_t *rbstream, dc_event_progress_t *progress, unsigned char data[], unsigned int size)
{
dc_status_t rc = DC_STATUS_SUCCESS;
unsigned int nbytes = 0;
while (nbytes < size) {
if (rbstream->available == 0) {
// Handle the ringbuffer wrap point.
if (rbstream->address == rbstream->end)
rbstream->address = rbstream->begin;
// Calculate the packet size.
unsigned int len = rbstream->packetsize;
if (rbstream->address + len > rbstream->end)
len = rbstream->end - rbstream->address;
// Calculate the excess number of bytes.
unsigned int extra = rbstream->packetsize - len;
// Read the packet into the cache.
rc = dc_device_read (rbstream->device, rbstream->address - extra, rbstream->cache, rbstream->packetsize);
if (rc != DC_STATUS_SUCCESS)
return rc;
// Move to the begin of the next packet.
rbstream->address += len;
rbstream->offset = extra + rbstream->skip;
rbstream->available = len - rbstream->skip;
rbstream->skip = 0;
}
unsigned int length = rbstream->available;
if (nbytes + length > size)
length = size - nbytes;
memcpy (data + nbytes, rbstream->cache + rbstream->offset, length);
rbstream->offset += length;
rbstream->available -= length;
// Update and emit a progress event.
if (progress) {
progress->current += length;
device_event_emit (rbstream->device, DC_EVENT_PROGRESS, progress);
}
nbytes += length;
}
return rc;
}
dc_status_t
dc_rbstream_read (dc_rbstream_t *rbstream, dc_event_progress_t *progress, unsigned char data[], unsigned int size)
{
if (rbstream == NULL)
return DC_STATUS_INVALIDARGS;
if (rbstream->direction == DC_RBSTREAM_FORWARD) {
return dc_rbstream_read_forward (rbstream, progress, data, size);
} else {
return dc_rbstream_read_backward (rbstream, progress, data, size);
}
}
dc_status_t
dc_rbstream_free (dc_rbstream_t *rbstream)
{

View File

@ -33,6 +33,14 @@ extern "C" {
*/
typedef struct dc_rbstream_t dc_rbstream_t;
/**
* The ringbuffer read direction.
*/
typedef enum dc_rbstream_direction_t {
DC_RBSTREAM_FORWARD,
DC_RBSTREAM_BACKWARD
} dc_rbstream_direction_t;
/**
* Create a new ringbuffer stream.
*
@ -43,11 +51,12 @@ typedef struct dc_rbstream_t dc_rbstream_t;
* @param[in] begin The ringbuffer begin address.
* @param[in] end The ringbuffer end address.
* @param[in] address The stream start address.
* @param[in] direction The ringbuffer read direction.
* @returns #DC_STATUS_SUCCESS on success, or another #dc_status_t code
* on failure.
*/
dc_status_t
dc_rbstream_new (dc_rbstream_t **rbstream, dc_device_t *device, unsigned int pagesize, unsigned int packetsize, unsigned int begin, unsigned int end, unsigned int address);
dc_rbstream_new (dc_rbstream_t **rbstream, dc_device_t *device, unsigned int pagesize, unsigned int packetsize, unsigned int begin, unsigned int end, unsigned int address, dc_rbstream_direction_t direction);
/**
* Read data from the ringbuffer stream.

View File

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

View File

@ -48,7 +48,6 @@ struct reefnet_sensus_parser_t {
unsigned int maxdepth;
};
static dc_status_t reefnet_sensus_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size);
static dc_status_t reefnet_sensus_parser_set_clock (dc_parser_t *abstract, unsigned int devtime, dc_ticks_t systime);
static dc_status_t reefnet_sensus_parser_set_atmospheric (dc_parser_t *abstract, double atmospheric);
static dc_status_t reefnet_sensus_parser_set_density (dc_parser_t *abstract, double density);
@ -59,7 +58,6 @@ static dc_status_t reefnet_sensus_parser_samples_foreach (dc_parser_t *abstract,
static const dc_parser_vtable_t reefnet_sensus_parser_vtable = {
sizeof(reefnet_sensus_parser_t),
DC_FAMILY_REEFNET_SENSUS,
reefnet_sensus_parser_set_data, /* set_data */
reefnet_sensus_parser_set_clock, /* set_clock */
reefnet_sensus_parser_set_atmospheric, /* set_atmospheric */
reefnet_sensus_parser_set_density, /* set_density */
@ -71,7 +69,7 @@ static const dc_parser_vtable_t reefnet_sensus_parser_vtable = {
dc_status_t
reefnet_sensus_parser_create (dc_parser_t **out, dc_context_t *context, unsigned int devtime, dc_ticks_t systime)
reefnet_sensus_parser_create (dc_parser_t **out, dc_context_t *context, const unsigned char data[], size_t size)
{
reefnet_sensus_parser_t *parser = NULL;
@ -79,7 +77,7 @@ reefnet_sensus_parser_create (dc_parser_t **out, dc_context_t *context, unsigned
return DC_STATUS_INVALIDARGS;
// Allocate memory.
parser = (reefnet_sensus_parser_t *) dc_parser_allocate (context, &reefnet_sensus_parser_vtable);
parser = (reefnet_sensus_parser_t *) dc_parser_allocate (context, &reefnet_sensus_parser_vtable, data, size);
if (parser == NULL) {
ERROR (context, "Failed to allocate memory.");
return DC_STATUS_NOMEMORY;
@ -88,8 +86,8 @@ reefnet_sensus_parser_create (dc_parser_t **out, dc_context_t *context, unsigned
// Set the default values.
parser->atmospheric = DEF_ATMOSPHERIC;
parser->hydrostatic = DEF_DENSITY_SALT * GRAVITY;
parser->devtime = devtime;
parser->systime = systime;
parser->devtime = 0;
parser->systime = 0;
parser->cached = 0;
parser->divetime = 0;
parser->maxdepth = 0;
@ -100,35 +98,6 @@ reefnet_sensus_parser_create (dc_parser_t **out, dc_context_t *context, unsigned
}
static dc_status_t
reefnet_sensus_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size)
{
reefnet_sensus_parser_t *parser = (reefnet_sensus_parser_t*) abstract;
// Reset the cache.
parser->cached = 0;
parser->divetime = 0;
parser->maxdepth = 0;
return DC_STATUS_SUCCESS;
}
dc_status_t
reefnet_sensus_parser_set_calibration (dc_parser_t *abstract, double atmospheric, double hydrostatic)
{
reefnet_sensus_parser_t *parser = (reefnet_sensus_parser_t*) abstract;
if (!ISINSTANCE (abstract))
return DC_STATUS_INVALIDARGS;
parser->atmospheric = atmospheric;
parser->hydrostatic = hydrostatic;
return DC_STATUS_SUCCESS;
}
static dc_status_t
reefnet_sensus_parser_set_clock (dc_parser_t *abstract, unsigned int devtime, dc_ticks_t systime)
{
@ -279,13 +248,13 @@ reefnet_sensus_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback
// Time (seconds)
time += interval;
sample.time = time;
if (callback) callback (DC_SAMPLE_TIME, sample, userdata);
sample.time = time * 1000;
if (callback) callback (DC_SAMPLE_TIME, &sample, userdata);
// Depth (adjusted feet of seawater).
unsigned int depth = data[offset++];
sample.depth = ((depth + 33.0 - (double) SAMPLE_DEPTH_ADJUST) * FSW - parser->atmospheric) / parser->hydrostatic;
if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata);
if (callback) callback (DC_SAMPLE_DEPTH, &sample, userdata);
// Temperature (degrees Fahrenheit)
if ((nsamples % 6) == 0) {
@ -293,7 +262,7 @@ reefnet_sensus_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback
return DC_STATUS_DATAFORMAT;
unsigned int temperature = data[offset++];
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);
}
// Current sample is complete.

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