Compare commits

...

143 Commits

Author SHA1 Message Date
d5f9b352fc Bluetooth updates for DSX 2024-06-14 17:51:26 -04:00
311f8d05f4 Fix EOL to CRLF 2024-06-14 17:37:27 -04:00
deaaeee7a1 Fix undeclared variables 2024-06-14 17:31:24 -04:00
c281b04bfc Fix invalid files 2024-06-14 15:01:47 -04:00
132a3949fd Add support for the Aqualung i330R and Apeks DSX 2024-06-14 14:52:50 -04:00
27a37fc95a Revert "Add support for the Aqualung i330R and Apeks DSX"
This reverts commit d1106cb8babb993d97b816f8274e22ae4fa6a08d.
2024-06-14 14:04:43 -04:00
d1106cb8ba Add support for the Aqualung i330R and Apeks DSX 2024-06-14 13:50:39 -04:00
Michael Keller
9641883f2f
Merge pull request #64 from mikeller/fix_sensor_count 2024-06-02 16:30:57 +12:00
Michael Keller
c7e89eab11 Garmin: Fix Reporting of Sensor Count.
Report the number of tank sensors independently of the number of
gasmixes. This fixes an unintuitive error message popping up when
importing dives with more sensors than gasmixes.

Fixes https://github.com/subsurface/subsurface/issues/4221.

Reported-by: @bwong2132
Signed-off-by: Michael Keller <mikeller@042.ch>
2024-05-31 11:35:32 +12:00
Michael Keller
9b12e8e638
Merge pull request #63 from mikeller/cherry_pick_mares_fix 2024-05-25 08:50:41 +12:00
Jef Driesen
d9c230820f 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-05-19 18:49:20 +12:00
Michael Keller
5d321deb8a
Merge pull request #62 from mikeller/remove_garmin_filter 2024-05-08 12:29:29 +12:00
Michael Keller
9f2674ee7c
Merge pull request #61 from mikeller/add_shearwater_stack_status 2024-05-08 12:29:10 +12:00
Michael Keller
6bd1183c34
Merge pull request #59 from mikeller/fix_shearwater_sensor_calibration 2024-05-08 12:28:54 +12:00
Michael Keller
0c125489df
Merge pull request #57 from mikeller/remove_shearwater_predator_timesync 2024-05-08 12:27:55 +12:00
Michael Keller
fa224f5300 Shearwater: Add Stack Timer Information.
Add the configured stack run time and the remaining stack run time at
the start and end of the dive as extra information.

Signed-off-by: Michael Keller <github@ike.ch>
2024-05-07 19:40:48 +12:00
Michael Keller
8f686ce1d8 Shearwater: Remove timesync for Predator.
Remove the timesync function from the code for the Predator - testing
has shown that this function is not supported by this model.

Signed-off-by: Michael Keller <github@ike.ch>
2024-05-07 12:28:39 +12:00
Michael Keller
a7fb341fb0 Shearwater: Add Sensor Calibration Info.
Add the calibration values for ppO2 sonsors in CCR mode as extra info
fields.

Signed-off-by: Michael Keller <github@ike.ch>
2024-05-07 09:44:46 +12:00
=Michael Keller
ff0023b8b4 Garmin: Remove Unused Transport Filter.
Remove the unused transport filter for the file based Garmin importers,
as it is not used at all in the way that the USB storage transport is
implemented.

Signed-off-by: Michael Keller <github@ike.ch>
2024-05-05 20:36:22 +12:00
Michael Keller
6e65428fc8 Shearwater: Fix Sensor Calibration for DiveCAN Controllers.
Remove the code that forces hiding of O2 sensor readings despite the
sensors being reported as properly calibrated by the divecomputer.
This fixes the problem of no sensor readings showing on DiveCAN
controllers due to the fact that the sensor calibration values are
returned as defaults on these devices.
Also increase the timeout for data reads - this seems to improve the
reliability when reading logs of long dives.

Signed-off-by: Michael Keller <github@ike.ch>
2024-05-04 01:53:50 +12:00
Michael Keller
89221475cf
Merge pull request #60 from mikeller/fix_serial_setting
I verified that this bug is breaking the reporting of the serial number on my dive computers as well - they all report as `0`. Merging this as it is affecting a lot of users.
2024-05-04 01:40:03 +12:00
Michael Keller
bff6f7c140 Fix Setting of the Device Serial Number.
Fix setting of the serial number in `dc_parser_new()`.

Fixes https://github.com/subsurface/subsurface/issues/4155

Signed-off-by: Michael Keller <mikeller@042.ch>
2024-05-03 16:09:02 +12:00
Michael Keller
ca55a11ed5
Merge pull request #58 from mikeller/add_garmin_descent_mk3_support
Merging this - there is demand for this fix, so now is a good time to get it tested: https://groups.google.com/g/subsurface-divelog/c/7Uo7A9YTGhg/m/BlhlZ2NNBQAJ
2024-04-28 18:46:40 +12:00
Michael Keller
bb502c6d8b Garmin: Fixes to Add Support .fit format version 2.1.
Ignore timestamps on messages that we do not read any other fields from
- with the new (firmware >= 16.x) .fit file format these messages seem
to be sent out of order, causing other messages to be ignored if they
come after them in the file but are before them in the timeline.
Track the OC / CCR state at the beginning of the dive based on the
dive type - this avoids spurious 'switched to ...' messages caused by
new 'gas switch' messages at the beginning of dives.

See also https://forums.garmin.com/developer/fit-sdk/b/news-announcements/posts/important-fit-activity-file-message-change

Signed-off-by: Michael Keller <github@ike.ch>
2024-04-26 01:17:06 +12:00
Michael Keller
792090566e Garmin: Add Support for Descent Mk3(i).
Add support for the Garmin Descent Mk3(i) models.
This pretty much just updates the entry in the device list to cover Mk2
and Mk3 models.
It does nothing to add support for MTP when reading from an Mk3 model -
I have not been able to get MTP support on linux going reliably, and I
do not have an Mk2 / Mk3 to test with anyway.
I suspect that MTP support is incomplete anyway, as the
product IDs for models like the Mk2S and Mk2G are not detected.
This change also adds an extra info field for the Model (based on a
heuristic and incomplete list).
It also fixes the data type for the sensor_type field.

Signed-off-by: Michael Keller <mikeller@042.ch>
2024-04-22 00:56:44 +12:00
Michael Keller
62a29eea15
Merge pull request #56 from mikeller/update_libdivecomputer_202402 2024-02-26 13:00:50 +13:00
Michael Keller
4bbfe1659e Shearwater: Fix Up Timesynch Changes.
A few fixes for bugs introduced during merge conflict resolution.

Signed-off-by: Michael Keller <mikeller@042.ch>
2024-02-14 12:01:58 +13:00
Michael Keller
39bc2fe05c Import: Clean up gasmix / tank usage.
Separate the usage enums and remove the unneeded values from each.

Signed-off-by: Michael Keller <github@ike.ch>
2024-02-14 12:01:51 +13:00
Michael Keller
edfb1a9c67 Import: Update Garmin Descent Import for libdivecomputer Update.
Update the importer for Garmin Descent to work with the latest upstream
version of libdivecomputer.

Signed-off-by: Michael Keller <github@ike.ch>
2024-02-14 11:38:31 +13:00
Michael Keller
d37cb91734 Merge remote-tracking branch 'libdivecomputer/master' into update_libdivecomputer_202402 2024-02-02 17:40:41 +13:00
Michael Keller
78710ab2f1
Merge pull request #55 from subsurface/add_garmin_descent_mk3_parsing_support 2024-01-23 11:08:37 +13:00
Michael Keller
76170009b4 Garmin: Add Support for Parsing of Garmin Descent Mk3 FIT files.
Add support for FIT files generated by the Garmin Descent Mk3.
This just fixes a field number limitation, it has not yet been
verified that the Mk3 file format is properly interpreted by Subsurface.

Signed-off-by: Michael Keller <mikeller@042.ch>
2024-01-22 12:54:02 +13: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
Michael Keller
577b694087
Merge pull request #54 from mikeller/improve_cressi_leonardo_ascent_warnings 2023-08-01 21:43:41 +12:00
Michael Keller
58ea31e62e Cressi Leonardo: Turn ascent warnings into info events.
According to user feedback the ascent warnings on the Cressi Leonardo
are very sensitive. They come in 3 different levels.
This change turns levels 1 and 2 into info events, leaving only 3 as a
warning.
Fixes https://groups.google.com/g/subsurface-divelog/c/qBXF0wmyjKg/m/5qA0_KABCQAJ

Signed-off-by: Michael Keller <github@ike.ch>
2023-07-28 00:02:14 +12:00
Michael Keller
97b2c89aaa
Merge pull request #52 from mikeller/hw_ostc_mark_inactive_gases 2023-07-23 15:43:32 +12: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
Linus Torvalds
3e39cb427a garmin: fix up some leftovers
When importing FIT files, we may not have serial numbers or firmware
versions in the result, so don't report them when they don't exist.

Also, add the product name to the FILE message field list, which can
contain relevant information.  Not that we report it right now, but now
we *could* do so.

This concludes the Suunto FIT file export saga.  It's not great, but it
looks like it should be usable.

Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2023-07-16 11:28:33 -07:00
Linus Torvalds
e81eca685a garmin: add a _third_ time offset field
So FIT files seem to have many many different ways to describe time
offsets.  And I'm not talking about the overall Garmin time offset of
631065600, which is the conversion from the "Unix Epoch" (Jan 1, 1970)
to the "Garmin Epoch" (Dec 31, 1989).

No, I'm talking just about "device time" to "local time" to "UTC"
conversions.

The DEVICE_SETTINGS message has two different fields for time offsets:
there's a "UTC offset" (presumably this is the timezone the device is
set to), and a "time offset" which we actually use to transform the
recorded time of the dive into the local time that we report.

But the Suunto FIT export doesn't seem to use either of those, and
instead Nick Clark points outthe Suunto FAQ:

 "Timestamp fields are deliberately defined as UTC time so that they may
  be conveniently displayed in the local time if so desired.

  In some instances it is useful to know the UTC offset when the file
  was generated (possibly different from when it is decoded). This can
  be accomplished by logging a single message containing both a
  local_timestamp and a timestamp field. This will establish the UTC
  offset of the file.

  Presently these fields are predefined for activity and monitoring
  messages"

so to get the actual local time, instead of getting it from the
DEVICE_SETTINGS message, we now have to parse the ACTIVITY message, and
take the difference between the regular timestamp and the
"local_timestamp" field.

The great thing about standards is that there are so many to choose
from.

Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2023-07-16 11:17:32 -07:00
Linus Torvalds
dc8f32609e garmin: add 'FIELD_DESCRIPTION' message definitions
We don't use them, but they seem to be trying to describe what the
developer fields are used for.  We may now parse the developer fields
enough to skip over them gracefully, but it looks like we migth some day
want to _really_ parse them, and while I haven't figured it out (at
all!) yet, this may some day help.

For example, we get things like this:

  FIELD_DESCRIPTION_name (STRING): "Depth"
  FIELD_DESCRIPTION_unit (STRING): "feet"
  FIELD_DESCRIPTION_original_mesg (UINT16): 20
  FIELD_DESCRIPTION_data_index (UINT8): 0
  FIELD_DESCRIPTION_field_definition (UINT8): 0
  FIELD_DESCRIPTION_base_type (UINT8): 136

which doesn't tell me anything at all right now, but looks like maybe it
should some day.

It looks like this is defining a developer field for depth in feet
(duh), and the data format may be the same as a RECORD message (20),
which does indeed normally contain the depth (but in mm as an UINT32,
and it's field number 92, so..)

End result: not useful right now, because I'm much too confused about
it.  But the debug printout looks interesting.

Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2023-07-16 11:01:00 -07:00
Linus Torvalds
196cfdf4df garmin: add names for a few RECORD message fields
Not because we use them, but because it makes it clearer from the debug
output that we know what they are and that they aren't interesting..

Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2023-07-16 11:01:00 -07:00
Linus Torvalds
503afc1a5f garmin: improve on the debug formatting for field printouts
This also gets the passing of string values right, even if we don't
actually use this right now.

Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2023-07-16 11:00:52 -07:00
Linus Torvalds
e02df46a2b garmin: remove some left-over verbose hexdump debugging
This was part of me trying to figure out the compressed formats and the
developer fields, it's just noise now.

Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2023-07-16 09:56:12 -07:00
Linus Torvalds
2879bd69a5 Garmin: parse the ACTIVITY message record
The ACTIVITY message contains a "local timestamp" in addition to the
regular timestamp. That gives us the timezone information.

Or rather, it would give us the timezone info if we actually used it.
But now we at least parse it, so that we *could* use it.

Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2023-07-15 10:11:04 -07:00
Linus Torvalds
4a2dec531e garmin: relax file name length rules
We end up using the FIT file name as the "fingerprint" for the dive, and
include it at the beginning of the dive data as such.  And because of
how Garmin encoded the FIT files, we ended up having a fixed 24-byte
length for this, which is normally the date encoding:

    YYYY-MM-DD-HH-MM-SS.FIT

with the terminating NUL character.

Of course, then Garmin started using a short-form encoding too
(presumably due to FAT filesystem limits), and we have magic code to
sort the dates properly, using the name format

    YMDHMMSS.FIT

with the numbers encoded in a shorter format (eg "C4ND0302.fit" is
equivalent to "2022-04-23-13-03-02.fit").  See name_cmp() and
parse_short_name() for details.

Anyway, because we use the (zero-padded) 24 characters of the name as
the fingerprint, we used a fixed-size buffer for the filename that was
limited to that maximum size Garmin creates.

But then you download those things, and have multiple vendors, and
suddenly that 24-character limit on the filename is very annoying.

Instead of fixing this in some clean and generic way, let's just raise
the namelength limit to something bigger, and continue to use the first
24 characters of the name for the fingerprint.

Pretty it isn't, but it makes it slightly easier to import random FIT
files that don't conform exactly to the traditional Garmin format.

Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2023-07-14 12:31:33 -07:00
Linus Torvalds
67cd1cc0fd Expose FIT file parsing as its own "vendor"
Since we can now parse FIT files from other vendors than Garmin (ie the
Suunto FIT file export), let's expose this as a generic "FIT file
import" in the dive computer list.

It's all still very much using the Garmin parser, but uses a model ID
that is zero.  The only difference that makes is that it also tells the
parser to treat the result as a dive even if it cannot find the actual
dive markers that Garmin uses, since those won't exist in the limited
FIT file export.

This is still somewhat inconvenient, in that you cannot point to the
file itself, you can just point to the directory that contains FIT
files.  And right now we have an artifical length limitation on the
filenames, but I'll fix that next.

Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2023-07-14 12:07:30 -07:00
Linus Torvalds
929ce47155 garmin_parser: add support for developer fields
This was the _actual_ reason why the Suunto FIT file import fell flat on
its face: it adds records with developer fields in them, and I just had
no idea how to parse them.

It turns out that they aren't all *that* horrible to parse: they are
kind of like a special case of the regular FIT event fields.

And no, this does not really parse them: it only parses the layout, and
using that it can then skip the developer fields without causing the
decoder to go all wonky and lose stream synchronization.

At least it works for the specific case of the Suunto FIT files, and the
code makes some amount of sense.  The FIT format may be odd, but at the
same time it's most definitely designed for pretty simplistic devices,
so it's not some kind of crazy XML thing.

This gets us parsing those Suunto FIT files at least partially.

That said, it is all very rough indeed, since you have to lie and claim
you're downloading from a Garmin, and have to set up the whole magic
'Garmin/Activity/' directory structure and limit the file size to the 24
characters that Garmin uses.

So this is by no means the real solution.

Considering that Jef doesn't want the Garmin parser in libdivecomputer
anyway, the proper solution might be to move this all to subsurface, and
make it be a "FIT file import" thing instead.  Annoying, but on the
other hand it has also been a bit awkward to have it in libdivecomputer.

Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2023-07-13 14:01:58 -07:00
Linus Torvalds
147d3df635 garmin_parser: add support for compressed records and more debug output
This turns out to have been another false turn: mis-parsing the FIT file
caused us to think it had compressed records.

So I spent much too much time trying to figure out how those compressed
records actually work.  This is the result.

It looks like the only difference between a compressed record and a
regular one is that the compressed record has a single-byte "this is the
record type and the time offset" field at the start.  That basically
avoids the need of then having a full 4-byte absolute time for such a
record.

Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2023-07-13 13:58:44 -07:00
Linus Torvalds
4aa70c9e2a garmin_parser: add a HRM profile message stub
This is an empty stub for the HRM profile message, which isn't actually
used by anything I know of, but came up as a result of some mis-parsing
of odd FIT files generated by the Suunto mobile app.

Losing synchronization in the FIT file then caused the parser to think
it needed this message type, and not having it then caused an early
abort.

While it's not actually needed once parsing things correctly, since I
looked up the message number and name for this message type, let's just
keep it around.  It won't hurt, and maybe it avoids me having to look it
up in the future.

Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2023-07-13 13:53:15 -07:00
Linus Torvalds
571df62ce5 garmin_parser: be a little less abrupt about parsing errors
Instead of returning an error on unrecognized input when parsing, just
skip to the end of the buffer.  This makes at least partially parsed
data available, which can help figure out what ended up happening.

This was part of my "Suunto also does FIT files now, and does them very
differently from Garmin" series.  I think I parse the Suunto files right
now, but next time this happens, I'd rather get partial data than no
data at all.

Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2023-07-13 13:49:00 -07: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
1222041b46 HW OSTC3/4: Mark Inactive Gases as Such.
Return `DC_STATUS_UNSUPPORTED` for inactive gas mixes, while retaining
the configured gas information. This makes it possible to mark inactive
gases as such, or completely hide them, without affecting the gas
indices that are referenced in gas switches.

Signed-off-by: Michael Keller <github@ike.ch>
2023-07-09 14:46:44 +12:00
Linus Torvalds
3733b87ac9 Merge branches 'add_shearwater_teric_timesync' and 'add_ostc4_dump_error' of https://github.com/mikeller/libdc into Subsurface-DS9
Pull libdivecomputer updates from Michael Keller:

 - Add time synchronisation for the Shearwater Teric

 - Fix two OSTC4 buglets

* 'add_shearwater_teric_timesync' of https://github.com/mikeller/libdc:
  Add Time Synchronisation for the  Shearwater Teric.

* 'add_ostc4_dump_error' of https://github.com/mikeller/libdc:
  Fix bugs in OSTC4 support.
2023-07-08 16:50:45 -07: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
Michael Keller
b13ad617ac Fix bugs in OSTC4 support.
Fix two bugs in the support for OSTC4:
- change block size for writes back to 64 bytes to prevent buffer
  overflow on the dive computer side;
- added 'unsupported' response for the dump command on OSTC4.

Signed-off-by: Michael Keller <github@ike.ch>
2023-06-14 12:37:11 +12:00
Michael Keller
838717dbae Add Time Synchronisation for the Shearwater Teric.
Add time synchronisation support for the Shearwater Teric. Unlike the
other Shearwater dive computers this model has basic support for time
zones, so we need to set the time in UTC and configure the time zone
offset.

Signed-off-by: Michael Keller <github@ike.ch>
2023-06-08 22:25:26 +12:00
Linus Torvalds
436063c74d Merge tag 'v0.8.0' into Subsurface-DS9
Merge upstream version 0.8.0 into our libdc fork.

Since we tracked the development branch, we already had merged all the
main changes, but it's been a couple of months since the last upstream
merge, and there were a few new changes upstream:

 - Divesoft Freedom and Liberty support

 - A couple of iostream abstraction layers: a new 'packet layer' and a
   HDLC layer, moving code from low-level dive computer downloaders to
   generic iostream layers.

 - misc minor updates

* tag 'v0.8.0': (25 commits)
  Release version 0.8.0
  Fix the date of the v0.7.0 release
  Add a missing filter for the Aqualung i750TC
  Reduce the BLE output packet size to 20 bytes again
  Integrate the new packet I/O in the backends
  Add a generic packet I/O implementation
  Fix a typo in the documentation
  Add support for the Divesoft Freedom and Liberty
  Integrate the HDLC stream in the eonsteel backend
  Add a generic HDLC I/O implementation
  Remove the local endianess functions
  Use the correct function to free resources
  Add the udev rules to the distribution tarball
  Add a README file to the contrib directory
  Include the revision in the Visual Studio and Android builds
  Don't generate the Windows version resource
  Move the Visual Studio project to the contrib directory
  Add a basic Android build system
  Disable the getopt argument permutation on Android
  Move the sign extension function to a common place
  ...
2023-05-27 16:18:41 -07:00
Michael Keller
763fc68741 Shearwater: Add time synchronisation.
Add time synchronisation for Shearwater dive computers.
This synchronises the local time, which is all that is supported by all
Shearwater models except for the Teric.
Time synchronisation including the time zone for the Teric still has to
be added.

Signed-off-by: Michael Keller <github@ike.ch>
2023-05-20 19:25:20 +12: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
db9371cf9f Release version 0.8.0 2023-05-11 18:56:37 +02:00
Jef Driesen
070de23b83 Post release version bump to 0.9.0 2023-05-11 18:56:37 +02:00
Jef Driesen
49aa12b172 Fix the date of the v0.7.0 release 2023-05-11 16:27:55 +02:00
Jef Driesen
2f1b99f2f9 Add a missing filter for the Aqualung i750TC
Commit 58d410b1a217bbc1f03d5c7a5a203c139a92616c accidentally omitted the
descriptor filter function.
2023-05-11 16:26:40 +02:00
Jef Driesen
6c3bbb2cc7 Reduce the BLE output packet size to 20 bytes again
The newer u-Blox Nina B2 bluetooth module supports larger packets up to
244 bytes, but the older Telit/Stollman bluetooth module does not.
Trying to send a packet larger than 20 bytes fails. For maximum
compatibility, limit the output packet size to 20 bytes.
2023-05-11 16:25:59 +02:00
Jef Driesen
1c8cd096b5 Integrate the new packet I/O in the backends
Replace the custom packet handling code in the iconhd and ostc3 backends
with the new layered packet I/O, and also integrate it into the idive
and extreme backends.
2023-05-11 16:25:59 +02:00
Jef Driesen
40c95ca02a Add a generic packet I/O implementation
The new packet I/O provides a layered I/O for reading and writing a byte
stream from the underlying packet oriented transport.
2023-05-11 16:25:59 +02:00
Jef Driesen
72ddd6a439 Fix a typo in the documentation 2023-04-24 00:02:17 +02:00
Jef Driesen
d4472b758f Add support for the Divesoft Freedom and Liberty
The latest versions of the Divesoft Freedom (HW 4.x) and Liberty (HW
2.x) dive computers support BLE communication. Previous generations did
support only a mass storage mode, where the dives are available as DLF
files. The BLE communication protocol uses HDLC framing for the data
packets. The dives downloaded over BLE have the same data format as the
DLF files.

Co-authored-by: Jan Matoušek <jan.matousek@rekomando.cz>
Tested-by: Jakub Hečko <jakub.hecko@divesoft.com>
2023-04-24 00:02:17 +02:00
Jef Driesen
b0e77fd05f Integrate the HDLC stream in the eonsteel backend 2023-04-24 00:02:17 +02:00
Jef Driesen
cee9a2e926 Add a generic HDLC I/O implementation
The new HDLC I/O provides a generic I/O stream for reading and writing
HDLC encoded frames on top of another base I/O stream.
2023-04-24 00:02:17 +02:00
Jef Driesen
9c38ae3e01 Remove the local endianess functions
Replace the local functions for writing 16 and 32 bit little endian
values with the corresponding common functions.
2023-04-17 16:28:19 +02:00
Jef Driesen
629d567381 Use the correct function to free resources
Currently this doesn't make any difference because the
dc_device_allocate() function simply calls free(), but this may change
in the future.
2023-04-12 11:47:25 +02:00
Jef Driesen
bec4a747ff Add the udev rules to the distribution tarball 2023-04-07 20:37:19 +02:00
Jef Driesen
43f48af418 Add a README file to the contrib directory 2023-04-07 20:37:19 +02:00
Jef Driesen
e45c62b028 Include the revision in the Visual Studio and Android builds
During troubleshooting it's very convenient to know the exact version
used in a bug report. With the git commit SHA1 added to the version
string in all builds, that becomes very easy.
2023-04-07 20:37:17 +02:00
Jef Driesen
767a2fad91 Don't generate the Windows version resource
The Windows version resource is compiled and can include the (generated)
version.h header file for the definition of the version macros. There is
no need to have it generated by autotools. Less generated files makes it
easier to use other build systems, like Visual Studio.
2023-04-07 20:35:05 +02:00
Jef Driesen
083b1eb8de Move the Visual Studio project to the contrib directory
With this change all the alternative build systems are now located in
the contrib directory.
2023-04-07 20:35:05 +02:00
Jef Driesen
118f6d79ba Add a basic Android build system
Add a basic Android.mk for building with the Android NDK. This can serve
as a good starting point for developers integrating libdivecomputer into
an Android application.

Co-authored-by: Sven Knoch <info@divinglog.de>
2023-04-07 20:35:02 +02:00
Jef Driesen
cf221de9b7 Disable the getopt argument permutation on Android
On Android operating systems, the getopt() function is posix compliant
and thus the option processing stops when the first non-option is found.
But the getopt_long() function permutes the argument vector, just like
the GNU implementation.

Using a leading '+' character in the option string disables the
permutation again.
2023-04-07 14:04:00 +02:00
Dirk Hohndel
3a2dc6cce4 Adapt dctool to recent API change
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2023-03-29 11:24:43 -07:00
Jef Driesen
86fd58c8c6 Move the sign extension function to a common place 2023-03-23 19:43:16 +01:00
Jef Driesen
1930b9eb59 Merge branch 'checksums' 2023-03-23 19:41:11 +01:00
Jef Driesen
554855cc7d Add the 16-bit CRC-ANSI functions
Yet another family of 16-bit CRC function. The only difference with the
already available CRC-CCITT algorithm is the choice of the polynomial.
2023-03-23 19:31:01 +01:00
Jef Driesen
27b471e76b Document the properties of the CRC functions 2023-03-23 19:30:23 +01:00
Jef Driesen
00033e4af0 Rename the 32-bit CRC functions
The new names make it easier to identify the normal and reflected
variant of the CRC function.
2023-03-23 19:30:23 +01:00
Jef Driesen
d327aea6ff Add a reflected variant of the 16-bit CRC-CCITT
The reflected variant of a CRC does reverse the bits of each input byte
and requires a different lookup table for an efficient implementation.
2023-03-23 19:28:37 +01:00
Jef Driesen
12f44f3410 Add an extra parameter for the xorout value
This change allows to calculate some more variants of the CRC-CCITT
algorithm with a single function.
2023-03-23 19:28:24 +01:00
Michael Keller
46d4bee8ab Add option to force overwrite firmware for OSTC4.
In order to support development of the open source firmware of the
OSTC4.
This is needed in order to be able to install firmware with the same
version number multiple times during development and testing.

Signed-off-by: Michael Keller <github@ike.ch>
2023-03-17 19:41:05 +13:00
Michael Keller
c288effa01 HW: Add gas type reporting through tank information.
Use tank information to report the type of gas used (open circuit /
diluent) for different gas mixes.

Signed-off-by: Michael Keller <github@ike.ch>
2023-03-17 10:23:48 +13:00
Michael Keller
d9dd30f327 HW: Add bailout events.
Add explicit events for OC bailout / switch back to closed
circuit on Heinrichs Weikamp dive computers.

Signed-off-by: Michael Keller <github@ike.ch>
2023-03-17 10:23:29 +13:00
Michael Keller
8b5b86fb4e HW: Apply the OSTC4 gas index offset to all HW computers.
Apply the gasmix index offset used by the OSTC4 on CCR dives to all
Heinrichs Weikamp computers and all dive types.

Signed-off-by: Michael Keller <github@ike.ch>
2023-03-17 10:21:54 +13:00
Michael Keller
4ddef92b80 HW OSTC4: Fix and improve CCR gas handling.
Fix a bug for the OSTC4 using the wrong gas after (diluent) gas
changes because the diluent gas index is offset by 5 when in CCR mode.
Also fix a bug adding manually entered gases on CCR dives as diluent
on OSTC4 - only OC gases can be manually entered on this dive computer.

Signed-off-by: Michael Keller <github@ike.ch>
2023-03-17 10:21:47 +13:00
Linus Torvalds
543bd58ddd Merge https://github.com/libdivecomputer/libdivecomputer into Subsurface-DS9
Merge Jef's upstream fix for the OSTC4 CCR gas parsing issue reported by
Michael Keller.

This also effectively obviates (and undoes) the revert I did in commit
8d3271e586cd.

* https://github.com/libdivecomputer/libdivecomputer:
  Fix the OSTC4 diluent changes
2023-03-16 09:49:21 -07:00
Linus Torvalds
8d3271e586 Revert "Limit the index to the fixed gas mixes"
This reverts commit 328812e95bfe7c6c9d2a8d36c75144f05c7dc9dc.

This turns out to cause fatal parse failures for the cases where the gas
change refers to a manual or bailout gas.

As noted by Jef in that commit, we should likely report those
differently, but in the meantime, at least don't fail the download.

See the original report at [1], and a (at this point still contentious)
bigger change to gas switch reporting at [2].  This revert exists purely
as a "make it at least work for now".

Reported-by: Michael Keller <github@ike.ch>
Link: https://github.com/subsurface/libdc/pull/46#issuecomment-1438313959 [1]
Link: https://github.com/subsurface/libdc/pull/44 [2]
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2023-03-06 12:02:45 -08:00
Jef Driesen
2ba9904757 Fix the OSTC4 diluent changes
The hwos devices support 5 gas mixes for open-circuit and 5 diluents for
CCR dives. Internally, both sets are stored separately, but depending on
the dive mode only one of both sets gets stored in the dive header. The
gas change event contains the index of the corresponding gas mix or
diluent, and should always be in the range 1 to 5.

The OSTC4 behaves a bit different from the other hwOS models and uses
index 6 to 10 for the diluents. That means the index needs to be
adjusted to refer to the correct mix in the dive header.

Reported-by: Michael Keller <github@ike.ch>
2023-02-23 23:46:13 +01:00
Michael Keller
648651e2d7 Heinrichs Weikamp: Increase parameter value buffer size.
Get rid of truncation warnings and possible string truncation by
increasing the size of the buffer used for parameter values. There is
enough space to display up to 64 characters.

Signed-off-by: Michael Keller <github@ike.ch>
2023-02-22 18:26:03 +13:00
161 changed files with 6600 additions and 2775 deletions

View File

@ -122,3 +122,52 @@ jobs:
with: with:
name: ${{ github.job }}-${{ matrix.arch }} name: ${{ github.job }}-${{ matrix.arch }}
path: ${{ github.job }}-${{ matrix.arch }}.tar.gz path: ${{ github.job }}-${{ matrix.arch }}.tar.gz
# msvc:
#
# name: Visual Studio
#
# runs-on: windows-latest
#
# strategy:
# fail-fast: false
# matrix:
# platform: [x86, x64]
#
# env:
# CONFIGURATION: Release
#
# steps:
# - uses: actions/checkout@v3
# - uses: msys2/setup-msys2@v2
# with:
# install: autoconf automake libtool pkg-config make gcc
# - run: |
# autoreconf --install --force
# ./configure --prefix=/usr
# make -C src revision.h
# shell: msys2 {0}
# - uses: microsoft/setup-msbuild@v1
# - run: msbuild -m -p:Platform=${{ matrix.platform }} -p:Configuration=${{ env.CONFIGURATION }} contrib/msvc/libdivecomputer.vcxproj
# - uses: actions/upload-artifact@v3
# with:
# name: ${{ github.job }}-${{ matrix.platform }}
# path: contrib/msvc/${{ matrix.platform }}/${{ env.CONFIGURATION }}/bin
android:
name: Android
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- 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
with:
name: ${{ github.job }}
path: contrib/android/libs

View File

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

36
NEWS
View File

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

View File

@ -1,6 +1,6 @@
# Versioning. # Versioning.
m4_define([dc_version_major],[0]) m4_define([dc_version_major],[0])
m4_define([dc_version_minor],[8]) m4_define([dc_version_minor],[9])
m4_define([dc_version_micro],[0]) m4_define([dc_version_micro],[0])
m4_define([dc_version_suffix],[devel-Subsurface-NG]) m4_define([dc_version_suffix],[devel-Subsurface-NG])
m4_define([dc_version],dc_version_major.dc_version_minor.dc_version_micro[]m4_ifset([dc_version_suffix],-[dc_version_suffix])) m4_define([dc_version],dc_version_major.dc_version_minor.dc_version_micro[]m4_ifset([dc_version_suffix],-[dc_version_suffix]))
@ -207,6 +207,8 @@ AX_APPEND_COMPILE_FLAGS([ \
-Wno-unused-but-set-variable \ -Wno-unused-but-set-variable \
-Wno-pointer-sign \ -Wno-pointer-sign \
-Wno-shadow \ -Wno-shadow \
-Wenum-conversion \
-Werror=enum-conversion \
-fmacro-prefix-map='$(top_srcdir)/'= \ -fmacro-prefix-map='$(top_srcdir)/'= \
],,[$ERROR_CFLAGS]) ],,[$ERROR_CFLAGS])
@ -253,7 +255,6 @@ AC_CONFIG_FILES([
include/libdivecomputer/Makefile include/libdivecomputer/Makefile
include/libdivecomputer/version.h include/libdivecomputer/version.h
src/Makefile src/Makefile
src/libdivecomputer.rc
doc/Makefile doc/Makefile
doc/doxygen.cfg doc/doxygen.cfg
doc/man/Makefile doc/man/Makefile

56
contrib/README Normal file
View File

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

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -72,6 +72,7 @@ static const backend_table_t g_backends[] = {
{"vtpro", DC_FAMILY_OCEANIC_VTPRO, 0x4245}, {"vtpro", DC_FAMILY_OCEANIC_VTPRO, 0x4245},
{"veo250", DC_FAMILY_OCEANIC_VEO250, 0x424C}, {"veo250", DC_FAMILY_OCEANIC_VEO250, 0x424C},
{"atom2", DC_FAMILY_OCEANIC_ATOM2, 0x4342}, {"atom2", DC_FAMILY_OCEANIC_ATOM2, 0x4342},
{"i330r", DC_FAMILY_PELAGIC_I330R, 0x4744},
{"nemo", DC_FAMILY_MARES_NEMO, 0}, {"nemo", DC_FAMILY_MARES_NEMO, 0},
{"puck", DC_FAMILY_MARES_PUCK, 7}, {"puck", DC_FAMILY_MARES_PUCK, 7},
{"darwin", DC_FAMILY_MARES_DARWIN, 0}, {"darwin", DC_FAMILY_MARES_DARWIN, 0},
@ -98,6 +99,7 @@ static const backend_table_t g_backends[] = {
{"screen", DC_FAMILY_SEAC_SCREEN, 0}, {"screen", DC_FAMILY_SEAC_SCREEN, 0},
{"cosmiq", DC_FAMILY_DEEPBLU_COSMIQ, 0}, {"cosmiq", DC_FAMILY_DEEPBLU_COSMIQ, 0},
{"s1", DC_FAMILY_OCEANS_S1, 0}, {"s1", DC_FAMILY_OCEANS_S1, 0},
{"freedom", DC_FAMILY_DIVESOFT_FREEDOM, 19},
// Not merged upstream yet // Not merged upstream yet
{"descentmk1", DC_FAMILY_GARMIN, 0}, {"descentmk1", DC_FAMILY_GARMIN, 0},

View File

@ -50,7 +50,7 @@
#define RESET 1 #define RESET 1
#endif #endif
#if defined(__GLIBC__) || defined(__MINGW32__) || defined(BSD) #if defined(__GLIBC__) || defined(__MINGW32__) || defined(BSD) || defined(__ANDROID__)
#define NOPERMUTATION "+" #define NOPERMUTATION "+"
#else #else
#define NOPERMUTATION "" #define NOPERMUTATION ""

View File

@ -80,20 +80,12 @@ dive_cb (const unsigned char *data, unsigned int size, const unsigned char *fing
// Create the parser. // Create the parser.
message ("Creating the parser.\n"); message ("Creating the parser.\n");
rc = dc_parser_new (&parser, divedata->device); rc = dc_parser_new (&parser, divedata->device, data, size);
if (rc != DC_STATUS_SUCCESS) { if (rc != DC_STATUS_SUCCESS) {
ERROR ("Error creating the parser."); ERROR ("Error creating the parser.");
goto cleanup; goto cleanup;
} }
// Register the data.
message ("Registering the data.\n");
rc = dc_parser_set_data (parser, data, size);
if (rc != DC_STATUS_SUCCESS) {
ERROR ("Error registering the data.");
goto cleanup;
}
// Parse the dive data. // Parse the dive data.
message ("Parsing the dive data.\n"); message ("Parsing the dive data.\n");
rc = dctool_output_write (divedata->output, parser, data, size, fingerprint, fsize); rc = dctool_output_write (divedata->output, parser, data, size, fingerprint, fsize);

View File

@ -94,7 +94,7 @@ fwupdate (dc_context_t *context, dc_descriptor_t *descriptor, dc_transport_t tra
rc = hw_ostc_device_fwupdate (device, hexfile); rc = hw_ostc_device_fwupdate (device, hexfile);
break; break;
case DC_FAMILY_HW_OSTC3: case DC_FAMILY_HW_OSTC3:
rc = hw_ostc3_device_fwupdate (device, hexfile); rc = hw_ostc3_device_fwupdate (device, hexfile, false);
break; break;
case DC_FAMILY_DIVESYSTEM_IDIVE: case DC_FAMILY_DIVESYSTEM_IDIVE:
rc = divesystem_idive_device_fwupdate (device, hexfile); rc = divesystem_idive_device_fwupdate (device, hexfile);

View File

@ -54,17 +54,17 @@ parse (dc_buffer_t *buffer, dc_context_t *context, dc_descriptor_t *descriptor,
// Create the parser. // Create the parser.
message ("Creating the parser.\n"); message ("Creating the parser.\n");
rc = dc_parser_new2 (&parser, context, descriptor, devtime, systime); rc = dc_parser_new2 (&parser, context, descriptor, data, size);
if (rc != DC_STATUS_SUCCESS) { if (rc != DC_STATUS_SUCCESS) {
ERROR ("Error creating the parser."); ERROR ("Error creating the parser.");
goto cleanup; goto cleanup;
} }
// Register the data. // Set the clock.
message ("Registering the data.\n"); message ("Setting the clock.\n");
rc = dc_parser_set_data (parser, data, size); rc = dc_parser_set_clock (parser, devtime, systime);
if (rc != DC_STATUS_SUCCESS) { if (rc != DC_STATUS_SUCCESS && rc != DC_STATUS_UNSUPPORTED) {
ERROR ("Error registering the data."); ERROR ("Error setting the clock.");
goto cleanup; goto cleanup;
} }

View File

@ -90,7 +90,7 @@ convert_volume (double value, dctool_units_t units)
} }
static void static void
sample_cb (dc_sample_type_t type, dc_sample_value_t value, void *userdata) sample_cb (dc_sample_type_t type, const dc_sample_value_t *value, void *userdata)
{ {
static const char *events[] = { static const char *events[] = {
"none", "deco", "rbt", "ascent", "ceiling", "workload", "transmitter", "none", "deco", "rbt", "ascent", "ceiling", "workload", "transmitter",
@ -104,64 +104,80 @@ sample_cb (dc_sample_type_t type, dc_sample_value_t value, void *userdata)
sample_data_t *sampledata = (sample_data_t *) userdata; sample_data_t *sampledata = (sample_data_t *) userdata;
unsigned int seconds = 0, milliseconds = 0;
switch (type) { switch (type) {
case DC_SAMPLE_TIME: case DC_SAMPLE_TIME:
seconds = value->time / 1000;
milliseconds = value->time % 1000;
if (sampledata->nsamples++) if (sampledata->nsamples++)
fprintf (sampledata->ostream, "</sample>\n"); fprintf (sampledata->ostream, "</sample>\n");
fprintf (sampledata->ostream, "<sample>\n"); fprintf (sampledata->ostream, "<sample>\n");
fprintf (sampledata->ostream, " <time>%02u:%02u</time>\n", value.time / 60, value.time % 60); if (milliseconds) {
fprintf (sampledata->ostream, " <time>%02u:%02u.%03u</time>\n", seconds / 60, seconds % 60, milliseconds);
} else {
fprintf (sampledata->ostream, " <time>%02u:%02u</time>\n", seconds / 60, seconds % 60);
}
break; break;
case DC_SAMPLE_DEPTH: case DC_SAMPLE_DEPTH:
fprintf (sampledata->ostream, " <depth>%.2f</depth>\n", fprintf (sampledata->ostream, " <depth>%.2f</depth>\n",
convert_depth(value.depth, sampledata->units)); convert_depth(value->depth, sampledata->units));
break; break;
case DC_SAMPLE_PRESSURE: case DC_SAMPLE_PRESSURE:
fprintf (sampledata->ostream, " <pressure tank=\"%u\">%.2f</pressure>\n", fprintf (sampledata->ostream, " <pressure tank=\"%u\">%.2f</pressure>\n",
value.pressure.tank, value->pressure.tank,
convert_pressure(value.pressure.value, sampledata->units)); convert_pressure(value->pressure.value, sampledata->units));
break; break;
case DC_SAMPLE_TEMPERATURE: case DC_SAMPLE_TEMPERATURE:
fprintf (sampledata->ostream, " <temperature>%.2f</temperature>\n", fprintf (sampledata->ostream, " <temperature>%.2f</temperature>\n",
convert_temperature(value.temperature, sampledata->units)); convert_temperature(value->temperature, sampledata->units));
break; break;
case DC_SAMPLE_EVENT: case DC_SAMPLE_EVENT:
if (value.event.type != SAMPLE_EVENT_GASCHANGE && value.event.type != SAMPLE_EVENT_GASCHANGE2) { if (value->event.type != SAMPLE_EVENT_GASCHANGE && value->event.type != SAMPLE_EVENT_GASCHANGE2) {
fprintf (sampledata->ostream, " <event type=\"%u\" time=\"%u\" flags=\"%u\" value=\"%u\">%s</event>\n", fprintf (sampledata->ostream, " <event type=\"%u\" time=\"%u\" flags=\"%u\" value=\"%u\">%s</event>\n",
value.event.type, value.event.time, value.event.flags, value.event.value, events[value.event.type]); value->event.type, value->event.time, value->event.flags, value->event.value, events[value->event.type]);
} }
break; break;
case DC_SAMPLE_RBT: case DC_SAMPLE_RBT:
fprintf (sampledata->ostream, " <rbt>%u</rbt>\n", value.rbt); fprintf (sampledata->ostream, " <rbt>%u</rbt>\n", value->rbt);
break; break;
case DC_SAMPLE_HEARTBEAT: case DC_SAMPLE_HEARTBEAT:
fprintf (sampledata->ostream, " <heartbeat>%u</heartbeat>\n", value.heartbeat); fprintf (sampledata->ostream, " <heartbeat>%u</heartbeat>\n", value->heartbeat);
break; break;
case DC_SAMPLE_BEARING: case DC_SAMPLE_BEARING:
fprintf (sampledata->ostream, " <bearing>%u</bearing>\n", value.bearing); fprintf (sampledata->ostream, " <bearing>%u</bearing>\n", value->bearing);
break; break;
case DC_SAMPLE_VENDOR: case DC_SAMPLE_VENDOR:
fprintf (sampledata->ostream, " <vendor type=\"%u\" size=\"%u\">", value.vendor.type, value.vendor.size); fprintf (sampledata->ostream, " <vendor type=\"%u\" size=\"%u\">", value->vendor.type, value->vendor.size);
for (unsigned int i = 0; i < value.vendor.size; ++i) for (unsigned int i = 0; i < value->vendor.size; ++i)
fprintf (sampledata->ostream, "%02X", ((const unsigned char *) value.vendor.data)[i]); fprintf (sampledata->ostream, "%02X", ((const unsigned char *) value->vendor.data)[i]);
fprintf (sampledata->ostream, "</vendor>\n"); fprintf (sampledata->ostream, "</vendor>\n");
break; break;
case DC_SAMPLE_SETPOINT: case DC_SAMPLE_SETPOINT:
fprintf (sampledata->ostream, " <setpoint>%.2f</setpoint>\n", value.setpoint); fprintf (sampledata->ostream, " <setpoint>%.2f</setpoint>\n", value->setpoint);
break; break;
case DC_SAMPLE_PPO2: case DC_SAMPLE_PPO2:
fprintf (sampledata->ostream, " <ppo2>%.2f</ppo2>\n", value.ppo2); if (value->ppo2.sensor != DC_SENSOR_NONE) {
fprintf (sampledata->ostream, " <ppo2 sensor=\"%u\">%.2f</ppo2>\n", value->ppo2.sensor, value->ppo2.value);
} else {
fprintf (sampledata->ostream, " <ppo2>%.2f</ppo2>\n", value->ppo2.value);
}
break; break;
case DC_SAMPLE_CNS: case DC_SAMPLE_CNS:
fprintf (sampledata->ostream, " <cns>%.1f</cns>\n", value.cns * 100.0); fprintf (sampledata->ostream, " <cns>%.1f</cns>\n", value->cns * 100.0);
break; break;
case DC_SAMPLE_DECO: case DC_SAMPLE_DECO:
fprintf (sampledata->ostream, " <deco time=\"%u\" depth=\"%.2f\">%s</deco>\n", fprintf (sampledata->ostream, " <deco time=\"%u\" depth=\"%.2f\">%s</deco>\n",
value.deco.time, value->deco.time,
convert_depth(value.deco.depth, sampledata->units), convert_depth(value->deco.depth, sampledata->units),
decostop[value.deco.type]); decostop[value->deco.type]);
if (value->deco.tts) {
fprintf (sampledata->ostream, " <tts>%u</tts>\n",
value->deco.tts);
}
break; break;
case DC_SAMPLE_GASMIX: case DC_SAMPLE_GASMIX:
fprintf (sampledata->ostream, " <gasmix>%u</gasmix>\n", value.gasmix); fprintf (sampledata->ostream, " <gasmix>%u</gasmix>\n", value->gasmix);
break; break;
default: default:
break; break;
@ -322,11 +338,19 @@ dctool_xml_output_write (dctool_output_t *abstract, dc_parser_t *parser, const u
"<gasmix>\n" "<gasmix>\n"
" <he>%.1f</he>\n" " <he>%.1f</he>\n"
" <o2>%.1f</o2>\n" " <o2>%.1f</o2>\n"
" <n2>%.1f</n2>\n" " <n2>%.1f</n2>\n",
"</gasmix>\n",
gasmix.helium * 100.0, gasmix.helium * 100.0,
gasmix.oxygen * 100.0, gasmix.oxygen * 100.0,
gasmix.nitrogen * 100.0); gasmix.nitrogen * 100.0);
if (gasmix.usage) {
const char *usage[] = {"none", "oxygen", "diluent", "sidemount"};
fprintf (output->ostream,
" <usage>%s</usage>\n",
usage[gasmix.usage]);
}
fprintf (output->ostream,
"</gasmix>\n");
} }
// Parse the tanks. // Parse the tanks.
@ -354,6 +378,12 @@ dctool_xml_output_write (dctool_output_t *abstract, dc_parser_t *parser, const u
" <gasmix>%u</gasmix>\n", " <gasmix>%u</gasmix>\n",
tank.gasmix); tank.gasmix);
} }
if (tank.usage) {
const char *usage[] = {"none", "oxygen", "diluent", "sidemount"};
fprintf (output->ostream,
" <usage>%s</usage>\n",
usage[tank.usage]);
}
if (tank.type != DC_TANKVOLUME_NONE) { if (tank.type != DC_TANKVOLUME_NONE) {
fprintf (output->ostream, fprintf (output->ostream,
" <type>%s</type>\n" " <type>%s</type>\n"
@ -420,8 +450,14 @@ dctool_xml_output_write (dctool_output_t *abstract, dc_parser_t *parser, const u
} }
if (status != DC_STATUS_UNSUPPORTED) { if (status != DC_STATUS_UNSUPPORTED) {
fprintf (output->ostream, "<salinity type=\"%u\">%.1f</salinity>\n", const char *names[] = {"fresh", "salt"};
salinity.type, salinity.density); if (salinity.density) {
fprintf (output->ostream, "<salinity density=\"%.1f\">%s</salinity>\n",
salinity.density, names[salinity.type]);
} else {
fprintf (output->ostream, "<salinity>%s</salinity>\n",
names[salinity.type]);
}
} }
// Parse the atmospheric pressure. // Parse the atmospheric pressure.

View File

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

View File

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

View File

@ -78,6 +78,7 @@ typedef enum dc_family_t {
DC_FAMILY_OCEANIC_VTPRO = (4 << 16), DC_FAMILY_OCEANIC_VTPRO = (4 << 16),
DC_FAMILY_OCEANIC_VEO250, DC_FAMILY_OCEANIC_VEO250,
DC_FAMILY_OCEANIC_ATOM2, DC_FAMILY_OCEANIC_ATOM2,
DC_FAMILY_PELAGIC_I330R,
/* Mares */ /* Mares */
DC_FAMILY_MARES_NEMO = (5 << 16), DC_FAMILY_MARES_NEMO = (5 << 16),
DC_FAMILY_MARES_PUCK, DC_FAMILY_MARES_PUCK,
@ -122,6 +123,8 @@ typedef enum dc_family_t {
DC_FAMILY_DEEPBLU_COSMIQ = (21 << 16), DC_FAMILY_DEEPBLU_COSMIQ = (21 << 16),
/* Oceans S1 */ /* Oceans S1 */
DC_FAMILY_OCEANS_S1 = (22 << 16), DC_FAMILY_OCEANS_S1 = (22 << 16),
/* Divesoft Freedom */
DC_FAMILY_DIVESOFT_FREEDOM = (23 << 16),
// Not merged upstream yet // Not merged upstream yet
/* Garmin */ /* Garmin */

View File

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

View File

@ -22,6 +22,8 @@
#ifndef DC_HW_OSTC3_H #ifndef DC_HW_OSTC3_H
#define DC_HW_OSTC3_H #define DC_HW_OSTC3_H
#include <stdbool.h>
#include "common.h" #include "common.h"
#include "device.h" #include "device.h"
#include "datetime.h" #include "datetime.h"
@ -55,7 +57,7 @@ dc_status_t
hw_ostc3_device_config_reset (dc_device_t *abstract); hw_ostc3_device_config_reset (dc_device_t *abstract);
dc_status_t dc_status_t
hw_ostc3_device_fwupdate (dc_device_t *abstract, const char *filename); hw_ostc3_device_fwupdate (dc_device_t *abstract, const char *filename, bool forceUpdate);
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@ -173,19 +173,26 @@ typedef struct dc_salinity_t {
double density; double density;
} dc_salinity_t; } dc_salinity_t;
typedef enum dc_usage_t {
DC_USAGE_NONE, // Usage not specified
DC_USAGE_OXYGEN,
DC_USAGE_DILUENT,
DC_USAGE_OPEN_CIRCUIT,
} dc_usage_t;
typedef struct dc_gasmix_t { typedef struct dc_gasmix_t {
double helium; double helium;
double oxygen; double oxygen;
double nitrogen; double nitrogen;
dc_usage_t usage;
} dc_gasmix_t; } dc_gasmix_t;
#define DC_SENSOR_NONE 0xFFFFFFFF
#define DC_GASMIX_UNKNOWN 0xFFFFFFFF #define DC_GASMIX_UNKNOWN 0xFFFFFFFF
typedef unsigned int dc_tankinfo_t; typedef unsigned int dc_tankinfo_t;
#define DC_TANKINFO_METRIC 1 #define DC_TANKINFO_METRIC 1
#define DC_TANKINFO_IMPERIAL 2 #define DC_TANKINFO_IMPERIAL 2
#define DC_TANKINFO_CC_DILUENT 4
#define DC_TANKINFO_CC_O2 8
// For backwards compatibility // For backwards compatibility
#define DC_TANKVOLUME_NONE 0 #define DC_TANKVOLUME_NONE 0
@ -215,6 +222,11 @@ typedef unsigned int dc_tankinfo_t;
* divide by 1 ATM (Vair = Vwater * Pwork / Patm). * divide by 1 ATM (Vair = Vwater * Pwork / Patm).
*/ */
typedef enum dc_tank_usage_t {
DC_TANK_USAGE_NONE,
DC_TANK_USAGE_SIDEMOUNT,
} dc_tank_usage_t;
typedef struct dc_tank_t { typedef struct dc_tank_t {
unsigned int gasmix; /* Gas mix index, or DC_GASMIX_UNKNOWN */ unsigned int gasmix; /* Gas mix index, or DC_GASMIX_UNKNOWN */
dc_tankinfo_t type; /* Tank type - metric/imperial and oc/cc */ dc_tankinfo_t type; /* Tank type - metric/imperial and oc/cc */
@ -222,6 +234,7 @@ typedef struct dc_tank_t {
double workpressure; /* Work pressure (bar) */ double workpressure; /* Work pressure (bar) */
double beginpressure; /* Begin pressure (bar) */ double beginpressure; /* Begin pressure (bar) */
double endpressure; /* End pressure (bar) */ double endpressure; /* End pressure (bar) */
dc_tank_usage_t usage;
} dc_tank_t; } dc_tank_t;
typedef enum dc_decomodel_type_t { typedef enum dc_decomodel_type_t {
@ -267,7 +280,7 @@ typedef struct dc_field_string_t {
} dc_field_string_t; } dc_field_string_t;
typedef union dc_sample_value_t { typedef union dc_sample_value_t {
unsigned int time; unsigned int time; /* Milliseconds */
double depth; double depth;
struct { struct {
unsigned int tank; unsigned int tank;
@ -290,25 +303,29 @@ typedef union dc_sample_value_t {
const void *data; const void *data;
} vendor; } vendor;
double setpoint; double setpoint;
double ppo2; struct {
unsigned int sensor;
double value;
} ppo2;
double cns; double cns;
struct { struct {
unsigned int type; unsigned int type;
unsigned int time; unsigned int time;
double depth; double depth;
unsigned int tts;
} deco; } deco;
unsigned int gasmix; /* Gas mix index */ unsigned int gasmix; /* Gas mix index */
} dc_sample_value_t; } dc_sample_value_t;
typedef struct dc_parser_t dc_parser_t; typedef struct dc_parser_t dc_parser_t;
typedef void (*dc_sample_callback_t) (dc_sample_type_t type, dc_sample_value_t value, void *userdata); typedef void (*dc_sample_callback_t) (dc_sample_type_t type, const dc_sample_value_t *value, void *userdata);
dc_status_t dc_status_t
dc_parser_new (dc_parser_t **parser, dc_device_t *device); dc_parser_new (dc_parser_t **parser, dc_device_t *device, const unsigned char data[], size_t size);
dc_status_t dc_status_t
dc_parser_new2 (dc_parser_t **parser, dc_context_t *context, dc_descriptor_t *descriptor, unsigned int devtime, dc_ticks_t systime); dc_parser_new2 (dc_parser_t **parser, dc_context_t *context, dc_descriptor_t *descriptor, const unsigned char data[], size_t size);
dc_family_t dc_family_t
dc_parser_get_type (dc_parser_t *parser); dc_parser_get_type (dc_parser_t *parser);
@ -322,9 +339,6 @@ dc_parser_set_atmospheric (dc_parser_t *parser, double atmospheric);
dc_status_t dc_status_t
dc_parser_set_density (dc_parser_t *parser, double density); 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_status_t
dc_parser_get_datetime (dc_parser_t *parser, dc_datetime_t *datetime); dc_parser_get_datetime (dc_parser_t *parser, dc_datetime_t *datetime);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -16,7 +16,7 @@ endif
libdivecomputer_la_SOURCES = \ libdivecomputer_la_SOURCES = \
version.c \ version.c \
descriptor-private.h descriptor.c \ descriptor.c \
iostream-private.h iostream.c \ iostream-private.h iostream.c \
iterator-private.h iterator.c \ iterator-private.h iterator.c \
common-private.h common.c \ common-private.h common.c \
@ -43,6 +43,7 @@ libdivecomputer_la_SOURCES = \
oceanic_atom2.h oceanic_atom2.c oceanic_atom2_parser.c \ oceanic_atom2.h oceanic_atom2.c oceanic_atom2_parser.c \
oceanic_veo250.h oceanic_veo250.c oceanic_veo250_parser.c \ oceanic_veo250.h oceanic_veo250.c oceanic_veo250_parser.c \
oceanic_vtpro.h oceanic_vtpro.c oceanic_vtpro_parser.c \ oceanic_vtpro.h oceanic_vtpro.c oceanic_vtpro_parser.c \
pelagic_i330r.h pelagic_i330r.c \
mares_common.h mares_common.c \ mares_common.h mares_common.c \
mares_nemo.h mares_nemo.c mares_nemo_parser.c \ mares_nemo.h mares_nemo.c mares_nemo_parser.c \
mares_puck.h mares_puck.c \ mares_puck.h mares_puck.c \
@ -80,6 +81,9 @@ libdivecomputer_la_SOURCES = \
deepblu_cosmiq.h deepblu_cosmiq.c deepblu_cosmiq_parser.c \ deepblu_cosmiq.h deepblu_cosmiq.c deepblu_cosmiq_parser.c \
oceans_s1_common.h oceans_s1_common.c \ oceans_s1_common.h oceans_s1_common.c \
oceans_s1.h oceans_s1.c oceans_s1_parser.c \ oceans_s1.h oceans_s1.c oceans_s1_parser.c \
divesoft_freedom.h divesoft_freedom.c divesoft_freedom_parser.c \
hdlc.h hdlc.c \
packet.h packet.c \
socket.h socket.c \ socket.h socket.c \
irda.c \ irda.c \
usb.c \ usb.c \
@ -109,7 +113,7 @@ libdivecomputer.exp: libdivecomputer.symbols
$(AM_V_GEN) sed -e '/^$$/d' $< > $@ $(AM_V_GEN) sed -e '/^$$/d' $< > $@
.rc.lo: .rc.lo:
$(AM_V_GEN) $(LIBTOOL) --silent --tag=CC --mode=compile $(RC) $(DEFS) $(DEFAULT_INCLUDES) $< -o $@ $(AM_V_GEN) $(LIBTOOL) --silent --tag=CC --mode=compile $(RC) $(DEFS) $(DEFAULT_INCLUDES) $(AM_CPPFLAGS) $< -o $@
libdivecomputer.lo: revision.h libdivecomputer.lo: revision.h

View File

@ -384,3 +384,34 @@ dec2bcd (unsigned char value)
unsigned char lo = value % 10; unsigned char lo = value % 10;
return (hi << 4) | lo; return (hi << 4) | lo;
} }
/*
* When turning a two's-complement number with a certain number
* of bits into one with more bits, the sign bit must be repeated
* in all the extra bits.
*/
unsigned int
signextend (unsigned int value, unsigned int nbits)
{
if (nbits <= 0 || nbits > 32)
return 0;
unsigned int signbit = 1U << (nbits - 1);
unsigned int mask = signbit - 1;
if ((value & signbit) == signbit)
return value | ~mask;
else
return value & mask;
}
unsigned int
popcount (unsigned int value)
{
unsigned int count = 0;
while (value) {
value &= value - 1;
count++;
}
return count;
}

View File

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

View File

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

View File

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

View File

@ -49,7 +49,6 @@ struct atomics_cobalt_parser_t {
double hydrostatic; double hydrostatic;
}; };
static dc_status_t atomics_cobalt_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size);
static dc_status_t atomics_cobalt_parser_set_density (dc_parser_t *abstract, double density); static dc_status_t atomics_cobalt_parser_set_density (dc_parser_t *abstract, double density);
static dc_status_t atomics_cobalt_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime); static dc_status_t atomics_cobalt_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime);
static dc_status_t atomics_cobalt_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value); static dc_status_t atomics_cobalt_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value);
@ -58,7 +57,6 @@ static dc_status_t atomics_cobalt_parser_samples_foreach (dc_parser_t *abstract,
static const dc_parser_vtable_t atomics_cobalt_parser_vtable = { static const dc_parser_vtable_t atomics_cobalt_parser_vtable = {
sizeof(atomics_cobalt_parser_t), sizeof(atomics_cobalt_parser_t),
DC_FAMILY_ATOMICS_COBALT, DC_FAMILY_ATOMICS_COBALT,
atomics_cobalt_parser_set_data, /* set_data */
NULL, /* set_clock */ NULL, /* set_clock */
NULL, /* set_atmospheric */ NULL, /* set_atmospheric */
atomics_cobalt_parser_set_density, /* set_density */ atomics_cobalt_parser_set_density, /* set_density */
@ -70,7 +68,7 @@ static const dc_parser_vtable_t atomics_cobalt_parser_vtable = {
dc_status_t dc_status_t
atomics_cobalt_parser_create (dc_parser_t **out, dc_context_t *context) atomics_cobalt_parser_create (dc_parser_t **out, dc_context_t *context, const unsigned char data[], size_t size)
{ {
atomics_cobalt_parser_t *parser = NULL; atomics_cobalt_parser_t *parser = NULL;
@ -78,7 +76,7 @@ atomics_cobalt_parser_create (dc_parser_t **out, dc_context_t *context)
return DC_STATUS_INVALIDARGS; return DC_STATUS_INVALIDARGS;
// Allocate memory. // Allocate memory.
parser = (atomics_cobalt_parser_t *) dc_parser_allocate (context, &atomics_cobalt_parser_vtable); parser = (atomics_cobalt_parser_t *) dc_parser_allocate (context, &atomics_cobalt_parser_vtable, data, size);
if (parser == NULL) { if (parser == NULL) {
ERROR (context, "Failed to allocate memory."); ERROR (context, "Failed to allocate memory.");
return DC_STATUS_NOMEMORY; return DC_STATUS_NOMEMORY;
@ -93,27 +91,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 static dc_status_t
atomics_cobalt_parser_set_density (dc_parser_t *abstract, double density) atomics_cobalt_parser_set_density (dc_parser_t *abstract, double density)
{ {
@ -182,6 +159,7 @@ atomics_cobalt_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, un
*((unsigned int *) value) = p[0x2a]; *((unsigned int *) value) = p[0x2a];
break; break;
case DC_FIELD_GASMIX: case DC_FIELD_GASMIX:
gasmix->usage = DC_USAGE_NONE;
gasmix->helium = p[SZ_HEADER + SZ_GASMIX * flags + 5] / 100.0; gasmix->helium = p[SZ_HEADER + SZ_GASMIX * flags + 5] / 100.0;
gasmix->oxygen = p[SZ_HEADER + SZ_GASMIX * flags + 4] / 100.0; gasmix->oxygen = p[SZ_HEADER + SZ_GASMIX * flags + 4] / 100.0;
gasmix->nitrogen = 1.0 - gasmix->oxygen - gasmix->helium; gasmix->nitrogen = 1.0 - gasmix->oxygen - gasmix->helium;
@ -213,6 +191,7 @@ atomics_cobalt_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, un
tank->gasmix = flags; tank->gasmix = flags;
tank->beginpressure = array_uint16_le(p + 6) * PSI / BAR; tank->beginpressure = array_uint16_le(p + 6) * PSI / BAR;
tank->endpressure = array_uint16_le(p + 14) * PSI / BAR; tank->endpressure = array_uint16_le(p + 14) * PSI / BAR;
tank->usage = DC_TANK_USAGE_NONE;
break; break;
case DC_FIELD_DIVEMODE: case DC_FIELD_DIVEMODE:
switch(p[0x24]) { switch(p[0x24]) {
@ -310,19 +289,19 @@ atomics_cobalt_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback
// Time (seconds). // Time (seconds).
time += interval; time += interval;
sample.time = time; sample.time = time * 1000;
if (callback) callback (DC_SAMPLE_TIME, sample, userdata); if (callback) callback (DC_SAMPLE_TIME, &sample, userdata);
// Depth (1/1000 bar). // Depth (1/1000 bar).
unsigned int depth = array_uint16_le (data + offset + 0); unsigned int depth = array_uint16_le (data + offset + 0);
sample.depth = (signed int)(depth - atmospheric) * (BAR / 1000.0) / parser->hydrostatic; sample.depth = (signed int)(depth - atmospheric) * (BAR / 1000.0) / parser->hydrostatic;
if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata); if (callback) callback (DC_SAMPLE_DEPTH, &sample, userdata);
// Pressure (1 psi). // Pressure (1 psi).
unsigned int pressure = array_uint16_le (data + offset + 2); unsigned int pressure = array_uint16_le (data + offset + 2);
sample.pressure.tank = tank; sample.pressure.tank = tank;
sample.pressure.value = pressure * PSI / BAR; sample.pressure.value = pressure * PSI / BAR;
if (callback) callback (DC_SAMPLE_PRESSURE, sample, userdata); if (callback) callback (DC_SAMPLE_PRESSURE, &sample, userdata);
// Current gas mix // Current gas mix
unsigned int gasmix = data[offset + 4]; unsigned int gasmix = data[offset + 4];
@ -338,14 +317,14 @@ atomics_cobalt_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback
return DC_STATUS_DATAFORMAT; return DC_STATUS_DATAFORMAT;
} }
sample.gasmix = idx; sample.gasmix = idx;
if (callback) callback (DC_SAMPLE_GASMIX, sample, userdata); if (callback) callback (DC_SAMPLE_GASMIX, &sample, userdata);
gasmix_previous = gasmix; gasmix_previous = gasmix;
} }
// Temperature (1 °F). // Temperature (1 °F).
unsigned int temperature = data[offset + 8]; unsigned int temperature = data[offset + 8];
sample.temperature = (temperature - 32.0) * (5.0 / 9.0); sample.temperature = (temperature - 32.0) * (5.0 / 9.0);
if (callback) callback (DC_SAMPLE_TEMPERATURE, sample, userdata); if (callback) callback (DC_SAMPLE_TEMPERATURE, &sample, userdata);
// violation status // violation status
sample.event.type = 0; sample.event.type = 0;
@ -355,15 +334,15 @@ atomics_cobalt_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback
unsigned int violation = data[offset + 11]; unsigned int violation = data[offset + 11];
if (violation & 0x01) { if (violation & 0x01) {
sample.event.type = SAMPLE_EVENT_ASCENT; sample.event.type = SAMPLE_EVENT_ASCENT;
if (callback) callback (DC_SAMPLE_EVENT, sample, userdata); if (callback) callback (DC_SAMPLE_EVENT, &sample, userdata);
} }
if (violation & 0x04) { if (violation & 0x04) {
sample.event.type = SAMPLE_EVENT_CEILING; sample.event.type = SAMPLE_EVENT_CEILING;
if (callback) callback (DC_SAMPLE_EVENT, sample, userdata); if (callback) callback (DC_SAMPLE_EVENT, &sample, userdata);
} }
if (violation & 0x08) { if (violation & 0x08) {
sample.event.type = SAMPLE_EVENT_PO2; sample.event.type = SAMPLE_EVENT_PO2;
if (callback) callback (DC_SAMPLE_EVENT, sample, userdata); if (callback) callback (DC_SAMPLE_EVENT, &sample, userdata);
} }
// NDL & deco // NDL & deco
@ -378,7 +357,8 @@ atomics_cobalt_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback
sample.deco.type = DC_DECO_NDL; sample.deco.type = DC_DECO_NDL;
sample.deco.time = ndl; sample.deco.time = ndl;
sample.deco.depth = 0.0; sample.deco.depth = 0.0;
if (callback) callback (DC_SAMPLE_DECO, sample, userdata); sample.deco.tts = 0;
if (callback) callback (DC_SAMPLE_DECO, &sample, userdata);
offset += SZ_SEGMENT; offset += SZ_SEGMENT;
} }

View File

@ -51,7 +51,6 @@
#include "context-private.h" #include "context-private.h"
#include "iostream-private.h" #include "iostream-private.h"
#include "iterator-private.h" #include "iterator-private.h"
#include "descriptor-private.h"
#include "platform.h" #include "platform.h"
#ifdef _WIN32 #ifdef _WIN32
@ -455,7 +454,7 @@ dc_bluetooth_iterator_next (dc_iterator_t *abstract, void *out)
INFO (abstract->context, "Discover: address=" DC_ADDRESS_FORMAT ", name=%s", INFO (abstract->context, "Discover: address=" DC_ADDRESS_FORMAT ", name=%s",
address, name ? name : ""); address, name ? name : "");
if (!dc_descriptor_filter (iterator->descriptor, DC_TRANSPORT_BLUETOOTH, name, NULL)) { if (!dc_descriptor_filter (iterator->descriptor, DC_TRANSPORT_BLUETOOTH, name)) {
continue; continue;
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -99,7 +99,6 @@ typedef struct cochran_commander_parser_t {
unsigned int nevents; unsigned int nevents;
} cochran_commander_parser_t ; } cochran_commander_parser_t ;
static dc_status_t cochran_commander_parser_set_data (dc_parser_t *parser, const unsigned char *data, unsigned int size);
static dc_status_t cochran_commander_parser_get_datetime (dc_parser_t *parser, dc_datetime_t *datetime); static dc_status_t cochran_commander_parser_get_datetime (dc_parser_t *parser, dc_datetime_t *datetime);
static dc_status_t cochran_commander_parser_get_field (dc_parser_t *parser, dc_field_type_t type, unsigned int flags, void *value); static dc_status_t cochran_commander_parser_get_field (dc_parser_t *parser, dc_field_type_t type, unsigned int flags, void *value);
static dc_status_t cochran_commander_parser_samples_foreach (dc_parser_t *parser, dc_sample_callback_t callback, void *userdata); static dc_status_t cochran_commander_parser_samples_foreach (dc_parser_t *parser, dc_sample_callback_t callback, void *userdata);
@ -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 = { static const dc_parser_vtable_t cochran_commander_parser_vtable = {
sizeof(cochran_commander_parser_t), sizeof(cochran_commander_parser_t),
DC_FAMILY_COCHRAN_COMMANDER, DC_FAMILY_COCHRAN_COMMANDER,
cochran_commander_parser_set_data, /* set_data */
NULL, /* set_clock */ NULL, /* set_clock */
NULL, /* set_atmospheric */ NULL, /* set_atmospheric */
NULL, /* set_density */ NULL, /* set_density */
@ -315,7 +313,7 @@ cochran_commander_handle_event (cochran_commander_parser_t *parser, unsigned cha
sample.event.time = 0; sample.event.time = 0;
sample.event.value = 0; sample.event.value = 0;
sample.event.flags = event->flag; sample.event.flags = event->flag;
if (callback) callback (DC_SAMPLE_EVENT, sample, userdata); if (callback) callback (DC_SAMPLE_EVENT, &sample, userdata);
} }
} }
@ -352,7 +350,7 @@ cochran_commander_backparse(cochran_commander_parser_t *parser, const unsigned c
dc_status_t dc_status_t
cochran_commander_parser_create (dc_parser_t **out, dc_context_t *context, unsigned int model) cochran_commander_parser_create (dc_parser_t **out, dc_context_t *context, const unsigned char data[], size_t size, unsigned int model)
{ {
cochran_commander_parser_t *parser = NULL; cochran_commander_parser_t *parser = NULL;
dc_status_t status = DC_STATUS_SUCCESS; dc_status_t status = DC_STATUS_SUCCESS;
@ -361,7 +359,7 @@ cochran_commander_parser_create (dc_parser_t **out, dc_context_t *context, unsig
return DC_STATUS_INVALIDARGS; return DC_STATUS_INVALIDARGS;
// Allocate memory. // Allocate memory.
parser = (cochran_commander_parser_t *) dc_parser_allocate (context, &cochran_commander_parser_vtable); parser = (cochran_commander_parser_t *) dc_parser_allocate (context, &cochran_commander_parser_vtable, data, size);
if (parser == NULL) { if (parser == NULL) {
ERROR (context, "Failed to allocate memory."); ERROR (context, "Failed to allocate memory.");
return DC_STATUS_NOMEMORY; return DC_STATUS_NOMEMORY;
@ -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 static dc_status_t
cochran_commander_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime) cochran_commander_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime)
{ {
@ -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 // Gas percentages are decimal and encoded as
// highbyte = integer portion // highbyte = integer portion
// lowbyte = decimal portion, divide by 256 to get decimal value // lowbyte = decimal portion, divide by 256 to get decimal value
gasmix->usage = DC_USAGE_NONE;
gasmix->oxygen = array_uint16_le (data + layout->oxygen + 2 * flags) / 256.0 / 100; gasmix->oxygen = array_uint16_le (data + layout->oxygen + 2 * flags) / 256.0 / 100;
if (layout->helium == UNSUPPORTED) { if (layout->helium == UNSUPPORTED) {
gasmix->helium = 0; gasmix->helium = 0;
@ -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 temp = samples[0]; // Half degrees F
unsigned int depth = samples[1]; // Half feet unsigned int depth = samples[1]; // Half feet
last_sample_time = sample.time = time; last_sample_time = sample.time = time * 1000;
if (callback) callback (DC_SAMPLE_TIME, sample, userdata); if (callback) callback (DC_SAMPLE_TIME, &sample, userdata);
sample.depth = (depth / 2.0) * FEET; sample.depth = (depth / 2.0) * FEET;
if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata); if (callback) callback (DC_SAMPLE_DEPTH, &sample, userdata);
sample.temperature = (temp / 2.0 - 32.0) / 1.8; sample.temperature = (temp / 2.0 - 32.0) / 1.8;
if (callback) callback (DC_SAMPLE_TEMPERATURE, sample, userdata); if (callback) callback (DC_SAMPLE_TEMPERATURE, &sample, userdata);
sample.gasmix = 0; sample.gasmix = 0;
if (callback) callback(DC_SAMPLE_GASMIX, sample, userdata); if (callback) callback(DC_SAMPLE_GASMIX, &sample, userdata);
while (offset < size) { while (offset < size) {
const unsigned char *s = samples + offset; const unsigned char *s = samples + offset;
sample.time = time; sample.time = time * 1000;
if (last_sample_time != sample.time) { if (last_sample_time != sample.time) {
// We haven't issued this time yet. // We haven't issued this time yet.
last_sample_time = sample.time; last_sample_time = sample.time;
if (callback) callback (DC_SAMPLE_TIME, sample, userdata); if (callback) callback (DC_SAMPLE_TIME, &sample, userdata);
} }
if (*s & 0x80) { if (*s & 0x80) {
@ -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.type = DC_DECO_DECOSTOP;
sample.deco.time = 60; // We don't know the duration sample.deco.time = 60; // We don't know the duration
sample.deco.depth = deco_ceiling * FEET; sample.deco.depth = deco_ceiling * FEET;
if (callback) callback(DC_SAMPLE_DECO, sample, userdata); sample.deco.tts = 0;
if (callback) callback(DC_SAMPLE_DECO, &sample, userdata);
break; break;
case 0xAD: // Increment ceiling (shallower) case 0xAD: // Increment ceiling (shallower)
deco_ceiling -= 10; // feet deco_ceiling -= 10; // feet
@ -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.type = DC_DECO_DECOSTOP;
sample.deco.depth = deco_ceiling * FEET; sample.deco.depth = deco_ceiling * FEET;
sample.deco.time = 60; // We don't know the duration sample.deco.time = 60; // We don't know the duration
if (callback) callback(DC_SAMPLE_DECO, sample, userdata); sample.deco.tts = 0;
if (callback) callback(DC_SAMPLE_DECO, &sample, userdata);
break; break;
default: default:
cochran_commander_handle_event(parser, s[0], callback, userdata); cochran_commander_handle_event(parser, s[0], callback, userdata);
@ -636,7 +630,7 @@ cochran_commander_parser_samples_foreach_tm (dc_parser_t *abstract, dc_sample_ca
else else
temp += (*s & 0x0f); temp += (*s & 0x0f);
sample.temperature = (temp / 2.0 - 32.0) / 1.8; sample.temperature = (temp / 2.0 - 32.0) / 1.8;
if (callback) callback (DC_SAMPLE_TEMPERATURE, sample, userdata); if (callback) callback (DC_SAMPLE_TEMPERATURE, &sample, userdata);
} }
offset++; offset++;
@ -650,7 +644,7 @@ cochran_commander_parser_samples_foreach_tm (dc_parser_t *abstract, dc_sample_ca
depth += s[0] & 0x3f; depth += s[0] & 0x3f;
sample.depth = (depth / 2.0) * FEET; sample.depth = (depth / 2.0) * FEET;
if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata); if (callback) callback (DC_SAMPLE_DEPTH, &sample, userdata);
offset++; offset++;
time += sample_interval; time += sample_interval;
@ -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; start_depth = array_uint16_le (data + layout->start_depth) / 256.0;
} }
last_sample_time = sample.time = time; last_sample_time = sample.time = time * 1000;
if (callback) callback (DC_SAMPLE_TIME, sample, userdata); if (callback) callback (DC_SAMPLE_TIME, &sample, userdata);
sample.depth = start_depth * FEET; sample.depth = start_depth * FEET;
if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata); if (callback) callback (DC_SAMPLE_DEPTH, &sample, userdata);
sample.temperature = (data[layout->start_temp] - 32.0) / 1.8; sample.temperature = (data[layout->start_temp] - 32.0) / 1.8;
if (callback) callback (DC_SAMPLE_TEMPERATURE, sample, userdata); if (callback) callback (DC_SAMPLE_TEMPERATURE, &sample, userdata);
sample.gasmix = 0; sample.gasmix = 0;
if (callback) callback(DC_SAMPLE_GASMIX, sample, userdata); if (callback) callback(DC_SAMPLE_GASMIX, &sample, userdata);
unsigned int last_gasmix = sample.gasmix; unsigned int last_gasmix = sample.gasmix;
while (offset < size) { while (offset < size) {
const unsigned char *s = samples + offset; const unsigned char *s = samples + offset;
sample.time = time; sample.time = time * 1000;
if (last_sample_time != sample.time) { if (last_sample_time != sample.time) {
// We haven't issued this time yet. // We haven't issued this time yet.
last_sample_time = sample.time; last_sample_time = sample.time;
if (callback) callback (DC_SAMPLE_TIME, sample, userdata); if (callback) callback (DC_SAMPLE_TIME, &sample, userdata);
} }
// If corrupt_dive end before offset // If corrupt_dive end before offset
@ -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.type = DC_DECO_DECOSTOP;
sample.deco.time = (array_uint16_le(s + 3) + 1) * 60; sample.deco.time = (array_uint16_le(s + 3) + 1) * 60;
sample.deco.depth = deco_ceiling * FEET; sample.deco.depth = deco_ceiling * FEET;
if (callback) callback(DC_SAMPLE_DECO, sample, userdata); sample.deco.tts = 0;
if (callback) callback(DC_SAMPLE_DECO, &sample, userdata);
break; break;
case 0xAD: // Increment ceiling (shallower) case 0xAD: // Increment ceiling (shallower)
deco_ceiling -= 10; // feet deco_ceiling -= 10; // feet
@ -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.type = DC_DECO_DECOSTOP;
sample.deco.depth = deco_ceiling * FEET; sample.deco.depth = deco_ceiling * FEET;
sample.deco.time = (array_uint16_le(s + 3) + 1) * 60; sample.deco.time = (array_uint16_le(s + 3) + 1) * 60;
if (callback) callback(DC_SAMPLE_DECO, sample, userdata); sample.deco.tts = 0;
if (callback) callback(DC_SAMPLE_DECO, &sample, userdata);
break; break;
case 0xC0: // Switched to FO2 21% mode (surface) case 0xC0: // Switched to FO2 21% mode (surface)
// Event seen upon surfacing // Event seen upon surfacing
@ -791,14 +787,14 @@ cochran_commander_parser_samples_foreach_emc (dc_parser_t *abstract, dc_sample_c
case 0xEF: // Switched to gas blend 2 case 0xEF: // Switched to gas blend 2
if (last_gasmix != 1) { if (last_gasmix != 1) {
sample.gasmix = 1; sample.gasmix = 1;
if (callback) callback(DC_SAMPLE_GASMIX, sample, userdata); if (callback) callback(DC_SAMPLE_GASMIX, &sample, userdata);
last_gasmix = sample.gasmix; last_gasmix = sample.gasmix;
} }
break; break;
case 0xF3: // Switched to gas blend 1 case 0xF3: // Switched to gas blend 1
if (last_gasmix != 0) { if (last_gasmix != 0) {
sample.gasmix = 0; sample.gasmix = 0;
if (callback) callback(DC_SAMPLE_GASMIX, sample, userdata); if (callback) callback(DC_SAMPLE_GASMIX, &sample, userdata);
last_gasmix = sample.gasmix; last_gasmix = sample.gasmix;
} }
break; break;
@ -818,7 +814,7 @@ cochran_commander_parser_samples_foreach_emc (dc_parser_t *abstract, dc_sample_c
depth += (s[0] & 0x3f); depth += (s[0] & 0x3f);
sample.depth = (start_depth + depth / 4.0) * FEET; sample.depth = (start_depth + depth / 4.0) * FEET;
if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata); if (callback) callback (DC_SAMPLE_DEPTH, &sample, userdata);
// Ascent rate is logged in the 0th sample, temp in the 1st, repeat. // Ascent rate is logged in the 0th sample, temp in the 1st, repeat.
if (time % 2 == 0) { if (time % 2 == 0) {
@ -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; double temperature = s[1] / 2.0 + 20.0;
sample.temperature = (temperature - 32.0) / 1.8; sample.temperature = (temperature - 32.0) / 1.8;
if (callback) callback (DC_SAMPLE_TEMPERATURE, sample, userdata); if (callback) callback (DC_SAMPLE_TEMPERATURE, &sample, userdata);
} }
// Cochran EMC models store NDL and deco stop time // Cochran EMC models store NDL and deco stop time
@ -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.type = DC_DECO_NDL;
sample.deco.time = deco_time * 60; sample.deco.time = deco_time * 60;
sample.deco.depth = 0; sample.deco.depth = 0;
if (callback) callback (DC_SAMPLE_DECO, sample, userdata); sample.deco.tts = 0;
if (callback) callback (DC_SAMPLE_DECO, &sample, userdata);
} }
break; break;
case 23: case 23:
@ -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.type = DC_DECO_DECOSTOP;
sample.deco.depth = deco_ceiling * FEET; sample.deco.depth = deco_ceiling * FEET;
sample.deco.time = deco_time * 60; sample.deco.time = deco_time * 60;
if (callback) callback (DC_SAMPLE_DECO, sample, userdata); sample.deco.tts = 0;
if (callback) callback (DC_SAMPLE_DECO, &sample, userdata);
} }
break; break;
} }

View File

@ -38,6 +38,8 @@
#define SZ_PACKET 0x80 #define SZ_PACKET 0x80
#define SZ_PAGE (SZ_PACKET / 4) #define SZ_PAGE (SZ_PACKET / 4)
#define SZ_HEADER 32
#define IQ700 0x05 #define IQ700 0x05
#define EDY 0x08 #define EDY 0x08
@ -522,6 +524,13 @@ cressi_edy_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, v
return rc; return rc;
} }
if (length < SZ_HEADER) {
ERROR (abstract->context, "Dive header is too small (%u).", length);
dc_rbstream_free (rbstream);
free (buffer);
return DC_STATUS_DATAFORMAT;
}
unsigned char *p = buffer + offset; unsigned char *p = buffer + offset;
if (memcmp (p, device->fingerprint, sizeof (device->fingerprint)) == 0) if (memcmp (p, device->fingerprint, sizeof (device->fingerprint)) == 0)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -84,7 +84,7 @@ cressi_leonardo_make_ascii (const unsigned char raw[], unsigned int rsize, unsig
array_convert_bin2hex (raw, rsize, ascii + 1, 2 * rsize); array_convert_bin2hex (raw, rsize, ascii + 1, 2 * rsize);
// Checksum // Checksum
unsigned short crc = checksum_crc16_ccitt (ascii + 1, 2 * rsize, 0xffff); unsigned short crc = checksum_crc16_ccitt (ascii + 1, 2 * rsize, 0xffff, 0x0000);
unsigned char checksum[] = { unsigned char checksum[] = {
(crc >> 8) & 0xFF, // High (crc >> 8) & 0xFF, // High
(crc ) & 0xFF}; // Low (crc ) & 0xFF}; // Low
@ -129,7 +129,7 @@ cressi_leonardo_packet (cressi_leonardo_device_t *device, const unsigned char co
// Verify the checksum of the packet. // Verify the checksum of the packet.
unsigned short crc = array_uint16_be (checksum); unsigned short crc = array_uint16_be (checksum);
unsigned short ccrc = checksum_crc16_ccitt (answer + 1, asize - 6, 0xffff); unsigned short ccrc = checksum_crc16_ccitt (answer + 1, asize - 6, 0xffff, 0x0000);
if (crc != ccrc) { if (crc != ccrc) {
ERROR (abstract->context, "Unexpected answer checksum."); ERROR (abstract->context, "Unexpected answer checksum.");
return DC_STATUS_PROTOCOL; return DC_STATUS_PROTOCOL;
@ -372,7 +372,7 @@ cressi_leonardo_device_dump (dc_device_t *abstract, dc_buffer_t *buffer)
// Verify the checksum. // Verify the checksum.
unsigned int csum1 = array_uint16_be (checksum); unsigned int csum1 = array_uint16_be (checksum);
unsigned int csum2 = checksum_crc16_ccitt (data, SZ_MEMORY, 0xffff); unsigned int csum2 = checksum_crc16_ccitt (data, SZ_MEMORY, 0xffff, 0x0000);
if (csum1 != csum2) { if (csum1 != csum2) {
ERROR (abstract->context, "Unexpected answer bytes."); ERROR (abstract->context, "Unexpected answer bytes.");
return DC_STATUS_PROTOCOL; return DC_STATUS_PROTOCOL;

View File

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

View File

@ -39,7 +39,6 @@ struct cressi_leonardo_parser_t {
unsigned int model; unsigned int model;
}; };
static dc_status_t cressi_leonardo_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size);
static dc_status_t cressi_leonardo_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime); static dc_status_t cressi_leonardo_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime);
static dc_status_t cressi_leonardo_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value); static dc_status_t cressi_leonardo_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value);
static dc_status_t cressi_leonardo_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata); static dc_status_t cressi_leonardo_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata);
@ -47,7 +46,6 @@ static dc_status_t cressi_leonardo_parser_samples_foreach (dc_parser_t *abstract
static const dc_parser_vtable_t cressi_leonardo_parser_vtable = { static const dc_parser_vtable_t cressi_leonardo_parser_vtable = {
sizeof(cressi_leonardo_parser_t), sizeof(cressi_leonardo_parser_t),
DC_FAMILY_CRESSI_LEONARDO, DC_FAMILY_CRESSI_LEONARDO,
cressi_leonardo_parser_set_data, /* set_data */
NULL, /* set_clock */ NULL, /* set_clock */
NULL, /* set_atmospheric */ NULL, /* set_atmospheric */
NULL, /* set_density */ NULL, /* set_density */
@ -59,7 +57,7 @@ static const dc_parser_vtable_t cressi_leonardo_parser_vtable = {
dc_status_t dc_status_t
cressi_leonardo_parser_create (dc_parser_t **out, dc_context_t *context, unsigned int model) cressi_leonardo_parser_create (dc_parser_t **out, dc_context_t *context, const unsigned char data[], size_t size, unsigned int model)
{ {
cressi_leonardo_parser_t *parser = NULL; cressi_leonardo_parser_t *parser = NULL;
@ -67,7 +65,7 @@ cressi_leonardo_parser_create (dc_parser_t **out, dc_context_t *context, unsigne
return DC_STATUS_INVALIDARGS; return DC_STATUS_INVALIDARGS;
// Allocate memory. // Allocate memory.
parser = (cressi_leonardo_parser_t *) dc_parser_allocate (context, &cressi_leonardo_parser_vtable); parser = (cressi_leonardo_parser_t *) dc_parser_allocate (context, &cressi_leonardo_parser_vtable, data, size);
if (parser == NULL) { if (parser == NULL) {
ERROR (context, "Failed to allocate memory."); ERROR (context, "Failed to allocate memory.");
return DC_STATUS_NOMEMORY; return DC_STATUS_NOMEMORY;
@ -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 static dc_status_t
cressi_leonardo_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime) cressi_leonardo_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime)
{ {
@ -146,6 +137,7 @@ cressi_leonardo_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, u
} }
break; break;
case DC_FIELD_GASMIX: case DC_FIELD_GASMIX:
gasmix->usage = DC_USAGE_NONE;
gasmix->helium = 0.0; gasmix->helium = 0.0;
gasmix->oxygen = data[0x19] / 100.0; gasmix->oxygen = data[0x19] / 100.0;
gasmix->nitrogen = 1.0 - gasmix->oxygen - gasmix->helium; gasmix->nitrogen = 1.0 - gasmix->oxygen - gasmix->helium;
@ -196,12 +188,12 @@ cressi_leonardo_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callbac
// Time (seconds). // Time (seconds).
time += surftime; time += surftime;
sample.time = time; sample.time = time * 1000;
if (callback) callback (DC_SAMPLE_TIME, sample, userdata); if (callback) callback (DC_SAMPLE_TIME, &sample, userdata);
// Depth (1/10 m). // Depth (1/10 m).
sample.depth = 0.0; sample.depth = 0.0;
if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata); if (callback) callback (DC_SAMPLE_DEPTH, &sample, userdata);
offset += 4; offset += 4;
} else { } else {
@ -211,17 +203,17 @@ cressi_leonardo_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callbac
// Time (seconds). // Time (seconds).
time += interval; time += interval;
sample.time = time; sample.time = time * 1000;
if (callback) callback (DC_SAMPLE_TIME, sample, userdata); if (callback) callback (DC_SAMPLE_TIME, &sample, userdata);
// Depth (1/10 m). // Depth (1/10 m).
sample.depth = depth / 10.0; sample.depth = depth / 10.0;
if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata); if (callback) callback (DC_SAMPLE_DEPTH, &sample, userdata);
// Gas change. // Gas change.
if (gasmix != gasmix_previous) { if (gasmix != gasmix_previous) {
sample.gasmix = gasmix; sample.gasmix = gasmix;
if (callback) callback (DC_SAMPLE_GASMIX, sample, userdata); if (callback) callback (DC_SAMPLE_GASMIX, &sample, userdata);
gasmix_previous = gasmix; gasmix_previous = gasmix;
} }
@ -229,9 +221,13 @@ cressi_leonardo_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callbac
if (ascent) { if (ascent) {
sample.event.type = SAMPLE_EVENT_ASCENT; sample.event.type = SAMPLE_EVENT_ASCENT;
sample.event.time = 0; sample.event.time = 0;
sample.event.flags = 0; if (ascent < 3)
// Turn the overly sensitive ascent warnings of this dive computer into info events
sample.event.flags = SAMPLE_FLAGS_SEVERITY_INFO;
else
sample.event.flags = 0;
sample.event.value = ascent; sample.event.value = ascent;
if (callback) callback (DC_SAMPLE_EVENT, sample, userdata); if (callback) callback (DC_SAMPLE_EVENT, &sample, userdata);
} }
offset += 2; offset += 2;

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); deepblu_cosmiq_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream);
dc_status_t 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 #ifdef __cplusplus
} }

View File

@ -41,7 +41,6 @@ typedef struct deepblu_cosmiq_parser_t {
double hydrostatic; double hydrostatic;
} deepblu_cosmiq_parser_t; } 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_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_datetime (dc_parser_t *abstract, dc_datetime_t *datetime);
static dc_status_t deepblu_cosmiq_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value); static dc_status_t deepblu_cosmiq_parser_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 = { static const dc_parser_vtable_t deepblu_cosmiq_parser_vtable = {
sizeof(deepblu_cosmiq_parser_t), sizeof(deepblu_cosmiq_parser_t),
DC_FAMILY_DEEPBLU_COSMIQ, DC_FAMILY_DEEPBLU_COSMIQ,
deepblu_cosmiq_parser_set_data, /* set_data */
NULL, /* set_clock */ NULL, /* set_clock */
NULL, /* set_atmospheric */ NULL, /* set_atmospheric */
deepblu_cosmiq_parser_set_density, /* set_density */ deepblu_cosmiq_parser_set_density, /* set_density */
@ -61,7 +59,7 @@ static const dc_parser_vtable_t deepblu_cosmiq_parser_vtable = {
}; };
dc_status_t 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; 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; return DC_STATUS_INVALIDARGS;
// Allocate memory. // 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) { if (parser == NULL) {
ERROR (context, "Failed to allocate memory."); ERROR (context, "Failed to allocate memory.");
return DC_STATUS_NOMEMORY; return DC_STATUS_NOMEMORY;
@ -83,12 +81,6 @@ deepblu_cosmiq_parser_create (dc_parser_t **out, dc_context_t *context)
return DC_STATUS_SUCCESS; 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 static dc_status_t
deepblu_cosmiq_parser_set_density (dc_parser_t *abstract, double density) 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; *((unsigned int *) value) = mode == SCUBA;
break; break;
case DC_FIELD_GASMIX: case DC_FIELD_GASMIX:
gasmix->usage = DC_USAGE_NONE;
gasmix->oxygen = data[3] / 100.0; gasmix->oxygen = data[3] / 100.0;
gasmix->helium = 0.0; gasmix->helium = 0.0;
gasmix->nitrogen = 1.0 - gasmix->oxygen - gasmix->helium; 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; offset += SZ_SAMPLE;
time += interval; time += interval;
sample.time = time; sample.time = time * 1000;
if (callback) callback (DC_SAMPLE_TIME, sample, userdata); if (callback) callback (DC_SAMPLE_TIME, &sample, userdata);
sample.depth = (signed int) (depth - atmospheric) * (BAR / 1000.0) / parser->hydrostatic; sample.depth = (signed int) (depth - atmospheric) * (BAR / 1000.0) / parser->hydrostatic;
if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata); if (callback) callback (DC_SAMPLE_DEPTH, &sample, userdata);
sample.temperature = temperature / 10.0; sample.temperature = temperature / 10.0;
if (callback) callback (DC_SAMPLE_TEMPERATURE, sample, userdata); if (callback) callback (DC_SAMPLE_TEMPERATURE, &sample, userdata);
} }
return DC_STATUS_SUCCESS; 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); deepsix_excursion_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t *iostream);
dc_status_t 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 #ifdef __cplusplus
} }

View File

@ -112,7 +112,6 @@ typedef struct deepsix_excursion_parser_t {
deepsix_excursion_gasmix_t gasmix[MAX_GASMIXES]; deepsix_excursion_gasmix_t gasmix[MAX_GASMIXES];
} deepsix_excursion_parser_t; } 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_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_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value);
static dc_status_t deepsix_excursion_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata); static dc_status_t deepsix_excursion_parser_samples_foreach (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 = { static const dc_parser_vtable_t deepsix_parser_vtable = {
sizeof(deepsix_excursion_parser_t), sizeof(deepsix_excursion_parser_t),
DC_FAMILY_DEEPSIX_EXCURSION, DC_FAMILY_DEEPSIX_EXCURSION,
deepsix_excursion_parser_set_data, /* set_data */
NULL, /* set_clock */ NULL, /* set_clock */
NULL, /* set_atmospheric */ NULL, /* set_atmospheric */
NULL, /* set_density */ NULL, /* set_density */
@ -185,7 +183,7 @@ deepsix_excursion_find_gasmix(deepsix_excursion_parser_t *parser, unsigned int o
} }
dc_status_t 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; 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; return DC_STATUS_INVALIDARGS;
// Allocate memory. // 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) { if (parser == NULL) {
ERROR (context, "Failed to allocate memory."); ERROR (context, "Failed to allocate memory.");
return DC_STATUS_NOMEMORY; return DC_STATUS_NOMEMORY;
@ -213,23 +211,6 @@ deepsix_excursion_parser_create (dc_parser_t **out, dc_context_t *context)
return DC_STATUS_SUCCESS; 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 static dc_status_t
deepsix_excursion_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime) 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; *((unsigned int *) value) = parser->ngasmixes;
break; break;
case DC_FIELD_GASMIX: case DC_FIELD_GASMIX:
gasmix->usage = DC_USAGE_NONE;
gasmix->oxygen = parser->gasmix[flags].oxygen / 100.0; gasmix->oxygen = parser->gasmix[flags].oxygen / 100.0;
gasmix->helium = parser->gasmix[flags].helium / 100.0; gasmix->helium = parser->gasmix[flags].helium / 100.0;
gasmix->nitrogen = 1.0 - gasmix->oxygen - gasmix->helium; gasmix->nitrogen = 1.0 - gasmix->oxygen - gasmix->helium;
@ -423,11 +405,11 @@ deepsix_excursion_parser_samples_foreach_v0 (dc_parser_t *abstract, dc_sample_ca
if (type == TEMPERATURE) { if (type == TEMPERATURE) {
time += interval; time += interval;
sample.time = time; sample.time = time * 1000;
if (callback) callback(DC_SAMPLE_TIME, sample, userdata); if (callback) callback(DC_SAMPLE_TIME, &sample, userdata);
sample.depth = pressure_to_depth(depth, atmospheric, DENSITY); 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) { if (type == ALARM) {
@ -440,11 +422,11 @@ deepsix_excursion_parser_samples_foreach_v0 (dc_parser_t *abstract, dc_sample_ca
length = 8; length = 8;
} else if (temperature >= 10) { } else if (temperature >= 10) {
sample.temperature = temperature / 10.0; sample.temperature = temperature / 10.0;
if (callback) callback(DC_SAMPLE_TEMPERATURE, sample, userdata); if (callback) callback(DC_SAMPLE_TEMPERATURE, &sample, userdata);
} }
} else { } else {
sample.temperature = temperature / 10.0; sample.temperature = temperature / 10.0;
if (callback) callback(DC_SAMPLE_TEMPERATURE, sample, userdata); if (callback) callback(DC_SAMPLE_TEMPERATURE, &sample, userdata);
} }
} else if (type == DECO) { } else if (type == DECO) {
unsigned int deco = array_uint16_le(data + offset + 4); 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) { } else if (type == CNS) {
unsigned int cns = array_uint16_le(data + offset + 4); unsigned int cns = array_uint16_le(data + offset + 4);
sample.cns = cns / 100.0; sample.cns = cns / 100.0;
if (callback) callback(DC_SAMPLE_CNS, sample, userdata); if (callback) callback(DC_SAMPLE_CNS, &sample, userdata);
} }
offset += length; offset += length;
@ -591,12 +573,12 @@ deepsix_excursion_parser_samples_foreach_v1 (dc_parser_t *abstract, dc_sample_ca
// Time (seconds). // Time (seconds).
time += samplerate; time += samplerate;
sample.time = time; sample.time = time * 1000;
if (callback) callback (DC_SAMPLE_TIME, sample, userdata); if (callback) callback (DC_SAMPLE_TIME, &sample, userdata);
unsigned int depth = array_uint16_le (data + offset); unsigned int depth = array_uint16_le (data + offset);
sample.depth = pressure_to_depth(depth, atmospheric, density); 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; offset += 2;
// event info // event info
@ -664,7 +646,7 @@ deepsix_excursion_parser_samples_foreach_v1 (dc_parser_t *abstract, dc_sample_ca
break; break;
} }
if (sample.event.type != SAMPLE_EVENT_NONE) { if (sample.event.type != SAMPLE_EVENT_NONE) {
if (callback) callback (DC_SAMPLE_EVENT, sample, userdata); if (callback) callback (DC_SAMPLE_EVENT, &sample, userdata);
} }
} }
break; break;
@ -686,7 +668,7 @@ deepsix_excursion_parser_samples_foreach_v1 (dc_parser_t *abstract, dc_sample_ca
} }
sample.gasmix = mix_idx; sample.gasmix = mix_idx;
if (callback) callback(DC_SAMPLE_GASMIX, sample, userdata); if (callback) callback(DC_SAMPLE_GASMIX, &sample, userdata);
break; break;
case EVENT_SAMPLES_MISSED: case EVENT_SAMPLES_MISSED:
count = array_uint16_le(data + offset + event_offset); 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: case SAMPLE_TEMPERATURE:
value = array_uint16_le(data + offset); value = array_uint16_le(data + offset);
sample.temperature = value / 10.0; sample.temperature = value / 10.0;
if (callback) callback(DC_SAMPLE_TEMPERATURE, sample, userdata); if (callback) callback(DC_SAMPLE_TEMPERATURE, &sample, userdata);
break; break;
case SAMPLE_CNS: case SAMPLE_CNS:
value = array_uint16_le(data + offset); value = array_uint16_le(data + offset);
sample.cns = value / 10000.0; sample.cns = value / 10000.0;
if (callback) callback (DC_SAMPLE_CNS, sample, userdata); if (callback) callback (DC_SAMPLE_CNS, &sample, userdata);
break; break;
case SAMPLE_DECO_NDL: case SAMPLE_DECO_NDL:
deco_flags = data[offset]; 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.depth = 0;
sample.deco.time = deco_ndl_tts; sample.deco.time = deco_ndl_tts;
} }
if (callback) callback (DC_SAMPLE_DECO, sample, userdata); if (callback) callback (DC_SAMPLE_DECO, &sample, userdata);
break; break;
default: default:
break; break;

View File

@ -23,7 +23,10 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include "descriptor-private.h" #include <libdivecomputer/descriptor.h>
#include <libdivecomputer/usbhid.h>
#include <libdivecomputer/usb.h>
#include "iterator-private.h" #include "iterator-private.h"
#include "platform.h" #include "platform.h"
@ -36,38 +39,29 @@
values, \ values, \
C_ARRAY_SIZE(values) - isnullterminated, \ C_ARRAY_SIZE(values) - isnullterminated, \
C_ARRAY_ITEMSIZE(values), \ C_ARRAY_ITEMSIZE(values), \
match, \ match)
NULL, NULL, 0)
#define DC_FILTER_INTERNAL_WITH_PARAMS(key, values, isnullterminated, match, params_dst, params_src) \
dc_filter_internal( \
key, \
values, \
C_ARRAY_SIZE(values) - isnullterminated, \
C_ARRAY_ITEMSIZE(values), \
match, \
params_dst, params_src, sizeof *(params_src))
typedef int (*dc_match_t)(const void *, const void *); typedef int (*dc_match_t)(const void *, const void *);
typedef int (*dc_filter_t) (dc_transport_t transport, const void *userdata, void *params); typedef int (*dc_filter_t) (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata);
static int dc_filter_uwatec (dc_transport_t transport, const void *userdata, void *params); static int dc_filter_uwatec (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata);
static int dc_filter_suunto (dc_transport_t transport, const void *userdata, void *params); static int dc_filter_suunto (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata);
static int dc_filter_shearwater (dc_transport_t transport, const void *userdata, void *params); static int dc_filter_shearwater (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata);
static int dc_filter_hw (dc_transport_t transport, const void *userdata, void *params); static int dc_filter_hw (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata);
static int dc_filter_tecdiving (dc_transport_t transport, const void *userdata, void *params); static int dc_filter_tecdiving (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata);
static int dc_filter_mares (dc_transport_t transport, const void *userdata, void *params); static int dc_filter_mares (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata);
static int dc_filter_divesystem (dc_transport_t transport, const void *userdata, void *params); static int dc_filter_divesystem (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata);
static int dc_filter_oceanic (dc_transport_t transport, const void *userdata, void *params); static int dc_filter_oceanic (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata);
static int dc_filter_mclean (dc_transport_t transport, const void *userdata, void *params); static int dc_filter_mclean (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata);
static int dc_filter_atomic (dc_transport_t transport, const void *userdata, void *params); static int dc_filter_atomic (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata);
static int dc_filter_deepsix (dc_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 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 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 int dc_filter_divesoft (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata);
// Not merged upstream yet // Not merged upstream yet
static int dc_filter_garmin (dc_transport_t transport, const void *userdata, void *params); static int dc_filter_garmin (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata);
static dc_status_t dc_descriptor_iterator_next (dc_iterator_t *iterator, void *item); static dc_status_t dc_descriptor_iterator_next (dc_iterator_t *iterator, void *item);
@ -179,7 +173,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 TEK", DC_FAMILY_UWATEC_SMART, 0x31, DC_TRANSPORT_USBHID | DC_TRANSPORT_BLE, dc_filter_uwatec},
{"Scubapro", "G2", DC_FAMILY_UWATEC_SMART, 0x32, DC_TRANSPORT_USBHID | DC_TRANSPORT_BLE, dc_filter_uwatec}, {"Scubapro", "G2", DC_FAMILY_UWATEC_SMART, 0x32, DC_TRANSPORT_USBHID | DC_TRANSPORT_BLE, dc_filter_uwatec},
{"Scubapro", "G2 Console", DC_FAMILY_UWATEC_SMART, 0x32, DC_TRANSPORT_USBHID | DC_TRANSPORT_BLE, dc_filter_uwatec}, {"Scubapro", "G2 Console", DC_FAMILY_UWATEC_SMART, 0x32, DC_TRANSPORT_USBHID | DC_TRANSPORT_BLE, dc_filter_uwatec},
{"Scubapro", "G3", DC_FAMILY_UWATEC_SMART, 0x34, DC_TRANSPORT_USBHID | DC_TRANSPORT_BLE, dc_filter_uwatec},
{"Scubapro", "G2 HUD", DC_FAMILY_UWATEC_SMART, 0x42, DC_TRANSPORT_USBHID | DC_TRANSPORT_BLE, dc_filter_uwatec}, {"Scubapro", "G2 HUD", DC_FAMILY_UWATEC_SMART, 0x42, DC_TRANSPORT_USBHID | DC_TRANSPORT_BLE, dc_filter_uwatec},
{"Scubapro", "Luna 2.0 AI", DC_FAMILY_UWATEC_SMART, 0x50, DC_TRANSPORT_BLE, dc_filter_uwatec},
{"Scubapro", "Luna 2.0", DC_FAMILY_UWATEC_SMART, 0x51, DC_TRANSPORT_BLE, dc_filter_uwatec},
/* Reefnet */ /* Reefnet */
{"Reefnet", "Sensus", DC_FAMILY_REEFNET_SENSUS, 1, DC_TRANSPORT_SERIAL, NULL}, {"Reefnet", "Sensus", DC_FAMILY_REEFNET_SENSUS, 1, DC_TRANSPORT_SERIAL, NULL},
{"Reefnet", "Sensus Pro", DC_FAMILY_REEFNET_SENSUSPRO, 2, DC_TRANSPORT_SERIAL, NULL}, {"Reefnet", "Sensus Pro", DC_FAMILY_REEFNET_SENSUSPRO, 2, DC_TRANSPORT_SERIAL, NULL},
@ -278,6 +275,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", "i470TC", DC_FAMILY_OCEANIC_ATOM2, 0x4743, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_oceanic},
{"Aqualung", "i200Cv2", DC_FAMILY_OCEANIC_ATOM2, 0x4749, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_oceanic}, {"Aqualung", "i200Cv2", 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}, {"Oceanic", "Geo Air", DC_FAMILY_OCEANIC_ATOM2, 0x474B, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_oceanic},
/* Pelagic I330R */
{"Apeks", "DSX", DC_FAMILY_PELAGIC_I330R, 0x4741, DC_TRANSPORT_BLE, dc_filter_oceanic},
{"Aqualung", "i330R", DC_FAMILY_PELAGIC_I330R, 0x4744, DC_TRANSPORT_BLE, dc_filter_oceanic},
/* Mares Nemo */ /* Mares Nemo */
{"Mares", "Nemo", DC_FAMILY_MARES_NEMO, 0, DC_TRANSPORT_SERIAL, NULL}, {"Mares", "Nemo", DC_FAMILY_MARES_NEMO, 0, DC_TRANSPORT_SERIAL, NULL},
{"Mares", "Nemo Steel", DC_FAMILY_MARES_NEMO, 0, DC_TRANSPORT_SERIAL, NULL}, {"Mares", "Nemo Steel", DC_FAMILY_MARES_NEMO, 0, DC_TRANSPORT_SERIAL, NULL},
@ -363,6 +363,7 @@ static const dc_descriptor_t g_descriptors[] = {
{"Shearwater", "Peregrine", DC_FAMILY_SHEARWATER_PETREL, 9, DC_TRANSPORT_BLE, dc_filter_shearwater}, {"Shearwater", "Peregrine", DC_FAMILY_SHEARWATER_PETREL, 9, DC_TRANSPORT_BLE, dc_filter_shearwater},
{"Shearwater", "Petrel 3", DC_FAMILY_SHEARWATER_PETREL, 10, DC_TRANSPORT_BLE, dc_filter_shearwater}, {"Shearwater", "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", "Perdix 2", DC_FAMILY_SHEARWATER_PETREL, 11, DC_TRANSPORT_BLE, dc_filter_shearwater},
{"Shearwater", "Tern", DC_FAMILY_SHEARWATER_PETREL, 12, DC_TRANSPORT_BLE, dc_filter_shearwater},
/* Dive Rite NiTek Q */ /* Dive Rite NiTek Q */
{"Dive Rite", "NiTek Q", DC_FAMILY_DIVERITE_NITEKQ, 0, DC_TRANSPORT_SERIAL, NULL}, {"Dive Rite", "NiTek Q", DC_FAMILY_DIVERITE_NITEKQ, 0, DC_TRANSPORT_SERIAL, NULL},
/* Citizen Hyper Aqualand */ /* Citizen Hyper Aqualand */
@ -416,6 +417,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 Deep", DC_FAMILY_DIVESYSTEM_IDIVE, 0x73, DC_TRANSPORT_SERIAL, NULL},
{"Ratio", "iX3M 2021 Pro Tech+", DC_FAMILY_DIVESYSTEM_IDIVE, 0x74, DC_TRANSPORT_SERIAL, NULL}, {"Ratio", "iX3M 2021 Pro Tech+", DC_FAMILY_DIVESYSTEM_IDIVE, 0x74, DC_TRANSPORT_SERIAL, NULL},
{"Ratio", "iX3M 2021 Pro Reb", DC_FAMILY_DIVESYSTEM_IDIVE, 0x75, DC_TRANSPORT_SERIAL, NULL}, {"Ratio", "iX3M 2021 Pro Reb", DC_FAMILY_DIVESYSTEM_IDIVE, 0x75, DC_TRANSPORT_SERIAL, NULL},
{"Ratio", "iDive 2 Free", DC_FAMILY_DIVESYSTEM_IDIVE, 0x80, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_divesystem},
{"Ratio", "iDive 2 Fancy", DC_FAMILY_DIVESYSTEM_IDIVE, 0x81, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_divesystem},
{"Ratio", "iDive 2 Easy", DC_FAMILY_DIVESYSTEM_IDIVE, 0x82, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_divesystem},
{"Ratio", "iDive 2 Pro", DC_FAMILY_DIVESYSTEM_IDIVE, 0x83, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_divesystem},
{"Ratio", "iDive 2 Deep", DC_FAMILY_DIVESYSTEM_IDIVE, 0x84, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_divesystem},
{"Ratio", "iDive 2 Tech", DC_FAMILY_DIVESYSTEM_IDIVE, 0x85, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_divesystem},
{"Ratio", "iDive 2 Reb", DC_FAMILY_DIVESYSTEM_IDIVE, 0x86, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_divesystem},
{"Ratio", "iX3M 2 GPS Gauge", DC_FAMILY_DIVESYSTEM_IDIVE, 0x90, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_divesystem},
{"Ratio", "iX3M 2 GPS Easy", DC_FAMILY_DIVESYSTEM_IDIVE, 0x91, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_divesystem},
{"Ratio", "iX3M 2 GPS Pro", DC_FAMILY_DIVESYSTEM_IDIVE, 0x92, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_divesystem},
{"Ratio", "iX3M 2 GPS Deep", DC_FAMILY_DIVESYSTEM_IDIVE, 0x93, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_divesystem},
{"Ratio", "iX3M 2 GPS Tech", DC_FAMILY_DIVESYSTEM_IDIVE, 0x94, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_divesystem},
{"Ratio", "iX3M 2 GPS Reb", DC_FAMILY_DIVESYSTEM_IDIVE, 0x95, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_divesystem},
{"Ratio", "ATOM", DC_FAMILY_DIVESYSTEM_IDIVE, 0x96, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_divesystem},
{"Ratio", "iX3M 2 Gauge", DC_FAMILY_DIVESYSTEM_IDIVE, 0x100, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_divesystem}, {"Ratio", "iX3M 2 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 Easy", DC_FAMILY_DIVESYSTEM_IDIVE, 0x101, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_divesystem},
{"Ratio", "iX3M 2 Pro", DC_FAMILY_DIVESYSTEM_IDIVE, 0x102, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_divesystem}, {"Ratio", "iX3M 2 Pro", DC_FAMILY_DIVESYSTEM_IDIVE, 0x102, DC_TRANSPORT_SERIAL | DC_TRANSPORT_BLE, dc_filter_divesystem},
@ -445,7 +460,6 @@ static const dc_descriptor_t g_descriptors[] = {
{"Deep Six", "Excursion", DC_FAMILY_DEEPSIX_EXCURSION, 0, DC_TRANSPORT_BLE, dc_filter_deepsix}, {"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}, {"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}, {"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}, {"Scorpena", "Alpha", DC_FAMILY_DEEPSIX_EXCURSION, 0, DC_TRANSPORT_BLE, dc_filter_deepsix},
/* Seac Screen */ /* Seac Screen */
{"Seac", "Screen", DC_FAMILY_SEAC_SCREEN, 0, DC_TRANSPORT_SERIAL, NULL}, {"Seac", "Screen", DC_FAMILY_SEAC_SCREEN, 0, DC_TRANSPORT_SERIAL, NULL},
@ -454,13 +468,18 @@ static const dc_descriptor_t g_descriptors[] = {
{"Deepblu", "Cosmiq+", DC_FAMILY_DEEPBLU_COSMIQ, 0, DC_TRANSPORT_BLE, dc_filter_deepblu}, {"Deepblu", "Cosmiq+", DC_FAMILY_DEEPBLU_COSMIQ, 0, DC_TRANSPORT_BLE, dc_filter_deepblu},
/* Oceans S1 */ /* Oceans S1 */
{"Oceans", "S1", DC_FAMILY_OCEANS_S1, 0, DC_TRANSPORT_BLE, dc_filter_oceans}, {"Oceans", "S1", DC_FAMILY_OCEANS_S1, 0, DC_TRANSPORT_BLE, dc_filter_oceans},
/* Divesoft Freedom */
{"Divesoft", "Freedom", DC_FAMILY_DIVESOFT_FREEDOM, 19, DC_TRANSPORT_BLE, dc_filter_divesoft},
{"Divesoft", "Liberty", DC_FAMILY_DIVESOFT_FREEDOM, 10, DC_TRANSPORT_BLE, dc_filter_divesoft},
// Not merged upstream yet // Not merged upstream yet
/* Garmin -- model numbers as defined in FIT format; USB product id is (0x4000 | model) */ /* Garmin -- model numbers as defined in FIT format; USB product id is (0x4000 | model) */
/* for the Mk1 we are using the model of the global model - the APAC model is 2991 */ /* for the Mk1 we are using the model of the global model */
/* for the Mk2 we are using the model of the global model - the APAC model is 3702 */ /* for the Mk2/Mk3 we are using the model of the Mk2 global model */
{"Garmin", "Descent Mk1", DC_FAMILY_GARMIN, 2859, DC_TRANSPORT_USBSTORAGE, dc_filter_garmin}, /* see garmin_parser.c for a more comprehensive list of models */
{"Garmin", "Descent Mk2/Mk2i", DC_FAMILY_GARMIN, 3258, DC_TRANSPORT_USBSTORAGE, dc_filter_garmin}, {"Garmin", "Descent Mk1", DC_FAMILY_GARMIN, 2859, DC_TRANSPORT_USBSTORAGE, NULL},
{"Garmin", "Descent Mk2(i)/Mk3(i)", DC_FAMILY_GARMIN, 3258, DC_TRANSPORT_USBSTORAGE, NULL},
{"FIT", "File import", DC_FAMILY_GARMIN, 0, DC_TRANSPORT_USBSTORAGE, NULL },
}; };
static int static int
@ -499,6 +518,15 @@ dc_match_usb (const void *key, const void *value)
return k->vid == v->vid && k->pid == v->pid; return k->vid == v->vid && k->pid == v->pid;
} }
static int
dc_match_usbhid (const void *key, const void *value)
{
const dc_usbhid_desc_t *k = (const dc_usbhid_desc_t *) key;
const dc_usbhid_desc_t *v = (const dc_usbhid_desc_t *) value;
return k->vid == v->vid && k->pid == v->pid;
}
static int static int
dc_match_number_with_prefix (const void *key, const void *value) dc_match_number_with_prefix (const void *key, const void *value)
{ {
@ -539,16 +567,13 @@ dc_match_oceanic (const void *key, const void *value)
} }
static int static int
dc_filter_internal (const void *key, const void *values, size_t count, size_t size, dc_match_t match, void *params_dst, const void *params_src, size_t params_size) dc_filter_internal (const void *key, const void *values, size_t count, size_t size, dc_match_t match)
{ {
if (key == NULL) if (key == NULL)
return 1; return 1;
for (size_t i = 0; i < count; ++i) { for (size_t i = 0; i < count; ++i) {
if (match (key, (const unsigned char *) values + i * size)) { if (match (key, (const unsigned char *) values + i * size)) {
if (params_src && params_dst) {
memcpy (params_dst, params_src, params_size);
}
return 1; return 1;
} }
} }
@ -563,7 +588,8 @@ static const char * const rfcomm[] = {
NULL NULL
}; };
static int dc_filter_uwatec (dc_transport_t transport, const void *userdata, void *params) static int
dc_filter_uwatec (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata)
{ {
static const char * const irda[] = { static const char * const irda[] = {
"Aladin Smart Com", "Aladin Smart Com",
@ -574,7 +600,7 @@ static int dc_filter_uwatec (dc_transport_t transport, const void *userdata, voi
"UWATEC Galileo", "UWATEC Galileo",
"UWATEC Galileo Sol", "UWATEC Galileo Sol",
}; };
static const dc_usb_desc_t usbhid[] = { static const dc_usbhid_desc_t usbhid[] = {
{0x2e6c, 0x3201}, // G2, G2 TEK {0x2e6c, 0x3201}, // G2, G2 TEK
{0x2e6c, 0x3211}, // G2 Console {0x2e6c, 0x3211}, // G2 Console
{0x2e6c, 0x4201}, // G2 HUD {0x2e6c, 0x4201}, // G2 HUD
@ -587,12 +613,15 @@ static int dc_filter_uwatec (dc_transport_t transport, const void *userdata, voi
"A1", "A1",
"A2", "A2",
"G2 TEK", "G2 TEK",
"Galileo 3",
"Luna 2.0 AI",
"Luna 2.0",
}; };
if (transport == DC_TRANSPORT_IRDA) { if (transport == DC_TRANSPORT_IRDA) {
return DC_FILTER_INTERNAL (userdata, irda, 0, dc_match_name); return DC_FILTER_INTERNAL (userdata, irda, 0, dc_match_name);
} else if (transport == DC_TRANSPORT_USBHID) { } else if (transport == DC_TRANSPORT_USBHID) {
return DC_FILTER_INTERNAL (userdata, usbhid, 0, dc_match_usb); return DC_FILTER_INTERNAL (userdata, usbhid, 0, dc_match_usbhid);
} else if (transport == DC_TRANSPORT_BLE) { } else if (transport == DC_TRANSPORT_BLE) {
return DC_FILTER_INTERNAL (userdata, bluetooth, 0, dc_match_name); return DC_FILTER_INTERNAL (userdata, bluetooth, 0, dc_match_name);
} }
@ -600,9 +629,10 @@ static int dc_filter_uwatec (dc_transport_t transport, const void *userdata, voi
return 1; return 1;
} }
static int dc_filter_suunto (dc_transport_t transport, const void *userdata, void *params) static int
dc_filter_suunto (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata)
{ {
static const dc_usb_desc_t usbhid[] = { static const dc_usbhid_desc_t usbhid[] = {
{0x1493, 0x0030}, // Eon Steel {0x1493, 0x0030}, // Eon Steel
{0x1493, 0x0033}, // Eon Core {0x1493, 0x0033}, // Eon Core
{0x1493, 0x0035}, // D5 {0x1493, 0x0035}, // D5
@ -616,7 +646,7 @@ static int dc_filter_suunto (dc_transport_t transport, const void *userdata, voi
}; };
if (transport == DC_TRANSPORT_USBHID) { if (transport == DC_TRANSPORT_USBHID) {
return DC_FILTER_INTERNAL (userdata, usbhid, 0, dc_match_usb); return DC_FILTER_INTERNAL (userdata, usbhid, 0, dc_match_usbhid);
} else if (transport == DC_TRANSPORT_BLE) { } else if (transport == DC_TRANSPORT_BLE) {
return DC_FILTER_INTERNAL (userdata, bluetooth, 0, dc_match_prefix); return DC_FILTER_INTERNAL (userdata, bluetooth, 0, dc_match_prefix);
} }
@ -624,7 +654,8 @@ static int dc_filter_suunto (dc_transport_t transport, const void *userdata, voi
return 1; return 1;
} }
static int dc_filter_hw (dc_transport_t transport, const void *userdata, void *params) static int
dc_filter_hw (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata)
{ {
static const char * const bluetooth[] = { static const char * const bluetooth[] = {
"OSTC", "OSTC",
@ -640,7 +671,8 @@ static int dc_filter_hw (dc_transport_t transport, const void *userdata, void *p
return 1; return 1;
} }
static int dc_filter_shearwater (dc_transport_t transport, const void *userdata, void *params) static int
dc_filter_shearwater (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata)
{ {
static const char * const bluetooth[] = { static const char * const bluetooth[] = {
"Predator", "Predator",
@ -652,6 +684,7 @@ static int dc_filter_shearwater (dc_transport_t transport, const void *userdata,
"Perdix 2", "Perdix 2",
"Teric", "Teric",
"Peregrine", "Peregrine",
"Tern"
}; };
if (transport == DC_TRANSPORT_BLUETOOTH || transport == DC_TRANSPORT_BLE) { if (transport == DC_TRANSPORT_BLUETOOTH || transport == DC_TRANSPORT_BLE) {
@ -663,7 +696,8 @@ static int dc_filter_shearwater (dc_transport_t transport, const void *userdata,
return 1; return 1;
} }
static int dc_filter_tecdiving (dc_transport_t transport, const void *userdata, void *params) static int
dc_filter_tecdiving (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata)
{ {
static const char * const bluetooth[] = { static const char * const bluetooth[] = {
"DiveComputer", "DiveComputer",
@ -678,7 +712,8 @@ static int dc_filter_tecdiving (dc_transport_t transport, const void *userdata,
return 1; return 1;
} }
static int dc_filter_mares (dc_transport_t transport, const void *userdata, void *params) static int
dc_filter_mares (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata)
{ {
static const char * const bluetooth[] = { static const char * const bluetooth[] = {
"Mares bluelink pro", "Mares bluelink pro",
@ -692,11 +727,13 @@ static int dc_filter_mares (dc_transport_t transport, const void *userdata, void
return 1; return 1;
} }
static int dc_filter_divesystem (dc_transport_t transport, const void *userdata, void *params) static int
dc_filter_divesystem (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata)
{ {
static const char * const bluetooth[] = { static const char * const bluetooth[] = {
"DS", "DS",
"IX5M", "IX5M",
"RATIO-",
}; };
if (transport == DC_TRANSPORT_BLUETOOTH || transport == DC_TRANSPORT_BLE) { if (transport == DC_TRANSPORT_BLUETOOTH || transport == DC_TRANSPORT_BLE) {
@ -706,7 +743,8 @@ static int dc_filter_divesystem (dc_transport_t transport, const void *userdata,
return 1; return 1;
} }
static int dc_filter_oceanic (dc_transport_t transport, const void *userdata, void *params) static int
dc_filter_oceanic (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata)
{ {
static const unsigned int model[] = { static const unsigned int model[] = {
0x4552, // Oceanic Pro Plus X 0x4552, // Oceanic Pro Plus X
@ -720,8 +758,10 @@ static int dc_filter_oceanic (dc_transport_t transport, const void *userdata, vo
0x4654, // Oceanic Veo 4.0 0x4654, // Oceanic Veo 4.0
0x4655, // Sherwood Wisdom 4 0x4655, // Sherwood Wisdom 4
0x4656, // Oceanic Pro Plus 4 0x4656, // Oceanic Pro Plus 4
0x4741, // Apeks DSX
0x4742, // Sherwood Beacon 0x4742, // Sherwood Beacon
0x4743, // Aqualung i470TC 0x4743, // Aqualung i470TC
0x4744, // Aqualung i330R
0x4749, // Aqualung i200C (newer model) 0x4749, // Aqualung i200C (newer model)
0x474B, // Oceanic Geo Air 0x474B, // Oceanic Geo Air
}; };
@ -733,7 +773,8 @@ static int dc_filter_oceanic (dc_transport_t transport, const void *userdata, vo
return 1; return 1;
} }
static int dc_filter_mclean(dc_transport_t transport, const void *userdata, void *params) static int
dc_filter_mclean(dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata)
{ {
static const char * const bluetooth[] = { static const char * const bluetooth[] = {
"McLean Extreme", "McLean Extreme",
@ -748,30 +789,27 @@ static int dc_filter_mclean(dc_transport_t transport, const void *userdata, void
return 1; return 1;
} }
static int dc_filter_atomic (dc_transport_t transport, const void *userdata, void *params) static int
dc_filter_atomic (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata)
{ {
static const dc_usb_desc_t usb[] = { static const dc_usb_desc_t usb[] = {
{0x0471, 0x0888}, // Atomic Aquatics Cobalt {0x0471, 0x0888}, // Atomic Aquatics Cobalt
}; };
static const dc_usb_params_t usb_params = {
0, 0x82, 0x02
};
if (transport == DC_TRANSPORT_USB) { 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; 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[] = { static const char * const bluetooth[] = {
"EXCURSION", "EXCURSION",
"Crest-CR4", "Crest-CR4",
"CENTAURI", "CENTAURI",
"TC1",
"ALPHA", "ALPHA",
}; };
@ -782,7 +820,8 @@ static int dc_filter_deepsix (dc_transport_t transport, const void *userdata, vo
return 1; 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[] = { static const char * const bluetooth[] = {
"COSMIQ", "COSMIQ",
@ -795,7 +834,8 @@ static int dc_filter_deepblu (dc_transport_t transport, const void *userdata, vo
return 1; 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[] = { static const char * const bluetooth[] = {
"S1", "S1",
@ -808,15 +848,16 @@ static int dc_filter_oceans (dc_transport_t transport, const void *userdata, voi
return 1; return 1;
} }
// Not merged upstream yet static int
static int dc_filter_garmin (dc_transport_t transport, const void *userdata, void *params) dc_filter_divesoft (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata)
{ {
static const dc_usb_desc_t usbhid[] = { static const char * const bluetooth[] = {
{0x091e, 0x2b2b}, // Garmin Descent Mk1 "Freedom",
"Liberty",
}; };
if (transport == DC_TRANSPORT_USBSTORAGE) { if (transport == DC_TRANSPORT_BLE) {
return DC_FILTER_INTERNAL (userdata, usbhid, 0, dc_match_usb); return DC_FILTER_INTERNAL (userdata, bluetooth, 0, dc_match_prefix);
} }
return 1; return 1;
@ -914,10 +955,10 @@ dc_descriptor_get_transports (dc_descriptor_t *descriptor)
} }
int int
dc_descriptor_filter (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata, void *params) dc_descriptor_filter (dc_descriptor_t *descriptor, dc_transport_t transport, const void *userdata)
{ {
if (descriptor == NULL || descriptor->filter == NULL || userdata == NULL) if (descriptor == NULL || descriptor->filter == NULL || userdata == NULL)
return 1; return 1;
return descriptor->filter (transport, userdata, params); return descriptor->filter (descriptor, transport, userdata);
} }

View File

@ -38,6 +38,7 @@
#include "oceanic_atom2.h" #include "oceanic_atom2.h"
#include "oceanic_veo250.h" #include "oceanic_veo250.h"
#include "oceanic_vtpro.h" #include "oceanic_vtpro.h"
#include "pelagic_i330r.h"
#include "mares_darwin.h" #include "mares_darwin.h"
#include "mares_iconhd.h" #include "mares_iconhd.h"
#include "mares_nemo.h" #include "mares_nemo.h"
@ -64,6 +65,7 @@
#include "seac_screen.h" #include "seac_screen.h"
#include "deepblu_cosmiq.h" #include "deepblu_cosmiq.h"
#include "oceans_s1.h" #include "oceans_s1.h"
#include "divesoft_freedom.h"
// Not merged upstream yet // Not merged upstream yet
#include "garmin.h" #include "garmin.h"
@ -164,6 +166,9 @@ dc_device_open (dc_device_t **out, dc_context_t *context, dc_descriptor_t *descr
case DC_FAMILY_OCEANIC_ATOM2: case DC_FAMILY_OCEANIC_ATOM2:
rc = oceanic_atom2_device_open (&device, context, iostream, dc_descriptor_get_model (descriptor)); rc = oceanic_atom2_device_open (&device, context, iostream, dc_descriptor_get_model (descriptor));
break; break;
case DC_FAMILY_PELAGIC_I330R:
rc = pelagic_i330r_device_open (&device, context, iostream, dc_descriptor_get_model (descriptor));
break;
case DC_FAMILY_MARES_NEMO: case DC_FAMILY_MARES_NEMO:
rc = mares_nemo_device_open (&device, context, iostream); rc = mares_nemo_device_open (&device, context, iostream);
break; break;
@ -242,6 +247,9 @@ dc_device_open (dc_device_t **out, dc_context_t *context, dc_descriptor_t *descr
case DC_FAMILY_OCEANS_S1: case DC_FAMILY_OCEANS_S1:
rc = oceans_s1_device_open (&device, context, iostream); rc = oceans_s1_device_open (&device, context, iostream);
break; break;
case DC_FAMILY_DIVESOFT_FREEDOM:
rc = divesoft_freedom_device_open (&device, context, iostream);
break;
default: default:
return DC_STATUS_INVALIDARGS; return DC_STATUS_INVALIDARGS;

View File

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

View File

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

595
src/divesoft_freedom.c Normal file
View File

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

View File

@ -1,7 +1,7 @@
/* /*
* libdivecomputer * libdivecomputer
* *
* Copyright (C) 2017 Jef Driesen * Copyright (C) 2023 Jan Matoušek, Jef Driesen
* *
* This library is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public * modify it under the terms of the GNU Lesser General Public
@ -19,33 +19,25 @@
* MA 02110-1301 USA * MA 02110-1301 USA
*/ */
#ifndef DC_DESCRIPTOR_PRIVATE_H #ifndef DIVESOFT_FREEDOM_H
#define DC_DESCRIPTOR_PRIVATE_H #define DIVESOFT_FREEDOM_H
#include <libdivecomputer/descriptor.h> #include <libdivecomputer/context.h>
#include <libdivecomputer/iostream.h>
// Oh joy. Windows is some truly horrendously broken crap #include <libdivecomputer/device.h>
#undef interface #include <libdivecomputer/parser.h>
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif /* __cplusplus */ #endif /* __cplusplus */
typedef struct dc_usb_desc_t { dc_status_t
unsigned short vid; divesoft_freedom_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream);
unsigned short pid;
} dc_usb_desc_t;
typedef struct dc_usb_params_t { dc_status_t
unsigned int interface; divesoft_freedom_parser_create (dc_parser_t **parser, dc_context_t *context, const unsigned char data[], size_t size);
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);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif /* __cplusplus */ #endif /* __cplusplus */
#endif /* DC_DESCRIPTOR_PRIVATE_H */ #endif /* DIVESOFT_FREEDOM_H */

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

@ -58,6 +58,9 @@
#define IX3M2_ZHL16C 2 #define IX3M2_ZHL16C 2
#define IX3M2_VPM 3 #define IX3M2_VPM 3
#define REC_SAMPLE 0
#define REC_INFO 1
typedef struct divesystem_idive_parser_t divesystem_idive_parser_t; typedef struct divesystem_idive_parser_t divesystem_idive_parser_t;
typedef struct divesystem_idive_gasmix_t { typedef struct divesystem_idive_gasmix_t {
@ -89,7 +92,6 @@ struct divesystem_idive_parser_t {
unsigned int gf_high; unsigned int gf_high;
}; };
static dc_status_t divesystem_idive_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size);
static dc_status_t divesystem_idive_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime); static dc_status_t divesystem_idive_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime);
static dc_status_t divesystem_idive_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value); static dc_status_t divesystem_idive_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value);
static dc_status_t divesystem_idive_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata); static dc_status_t divesystem_idive_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata);
@ -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 = { static const dc_parser_vtable_t divesystem_idive_parser_vtable = {
sizeof(divesystem_idive_parser_t), sizeof(divesystem_idive_parser_t),
DC_FAMILY_DIVESYSTEM_IDIVE, DC_FAMILY_DIVESYSTEM_IDIVE,
divesystem_idive_parser_set_data, /* set_data */
NULL, /* set_clock */ NULL, /* set_clock */
NULL, /* set_atmospheric */ NULL, /* set_atmospheric */
NULL, /* set_density */ NULL, /* set_density */
@ -109,7 +110,7 @@ static const dc_parser_vtable_t divesystem_idive_parser_vtable = {
dc_status_t dc_status_t
divesystem_idive_parser_create (dc_parser_t **out, dc_context_t *context, unsigned int model) divesystem_idive_parser_create (dc_parser_t **out, dc_context_t *context, const unsigned char data[], size_t size, unsigned int model)
{ {
divesystem_idive_parser_t *parser = NULL; divesystem_idive_parser_t *parser = NULL;
@ -117,7 +118,7 @@ divesystem_idive_parser_create (dc_parser_t **out, dc_context_t *context, unsign
return DC_STATUS_INVALIDARGS; return DC_STATUS_INVALIDARGS;
// Allocate memory. // Allocate memory.
parser = (divesystem_idive_parser_t *) dc_parser_allocate (context, &divesystem_idive_parser_vtable); parser = (divesystem_idive_parser_t *) dc_parser_allocate (context, &divesystem_idive_parser_vtable, data, size);
if (parser == NULL) { if (parser == NULL) {
ERROR (context, "Failed to allocate memory."); ERROR (context, "Failed to allocate memory.");
return DC_STATUS_NOMEMORY; return DC_STATUS_NOMEMORY;
@ -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 static dc_status_t
divesystem_idive_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime) divesystem_idive_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime)
{ {
@ -317,6 +289,7 @@ divesystem_idive_parser_get_field (dc_parser_t *abstract, dc_field_type_t type,
*((unsigned int *) value) = parser->ngasmixes; *((unsigned int *) value) = parser->ngasmixes;
break; break;
case DC_FIELD_GASMIX: case DC_FIELD_GASMIX:
gasmix->usage = DC_USAGE_NONE;
gasmix->helium = parser->gasmix[flags].helium / 100.0; gasmix->helium = parser->gasmix[flags].helium / 100.0;
gasmix->oxygen = parser->gasmix[flags].oxygen / 100.0; gasmix->oxygen = parser->gasmix[flags].oxygen / 100.0;
gasmix->nitrogen = 1.0 - gasmix->oxygen - gasmix->helium; gasmix->nitrogen = 1.0 - gasmix->oxygen - gasmix->helium;
@ -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->beginpressure = parser->tank[flags].beginpressure;
tank->endpressure = parser->tank[flags].endpressure; tank->endpressure = parser->tank[flags].endpressure;
tank->gasmix = DC_GASMIX_UNKNOWN; tank->gasmix = DC_GASMIX_UNKNOWN;
tank->usage = DC_TANK_USAGE_NONE;
break; break;
case DC_FIELD_ATMOSPHERIC: case DC_FIELD_ATMOSPHERIC:
if (ISIX3M(parser->model)) { 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 algorithm_previous = INVALID;
unsigned int gf_low = INVALID; unsigned int gf_low = INVALID;
unsigned int gf_high = INVALID; unsigned int gf_high = INVALID;
unsigned int have_bearing = 0;
unsigned int firmware = 0; unsigned int firmware = 0;
unsigned int apos4 = 0; unsigned int apos4 = 0;
@ -467,27 +442,37 @@ divesystem_idive_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callba
while (offset + samplesize <= size) { while (offset + samplesize <= size) {
dc_sample_value_t sample = {0}; dc_sample_value_t sample = {0};
// Get the record type.
unsigned int type = ISIX3M(parser->model) ?
array_uint16_le (data + offset + 52) :
REC_SAMPLE;
if (type != REC_SAMPLE) {
// Skip non-sample records.
offset += samplesize;
continue;
}
// Time (seconds). // Time (seconds).
unsigned int timestamp = array_uint32_le (data + offset + 2); unsigned int timestamp = array_uint32_le (data + offset + 2);
if (timestamp <= time) { if (timestamp <= time && time != 0) {
ERROR (abstract->context, "Timestamp moved backwards."); ERROR (abstract->context, "Timestamp moved backwards (%u %u).", timestamp, time);
return DC_STATUS_DATAFORMAT; return DC_STATUS_DATAFORMAT;
} }
time = timestamp; time = timestamp;
sample.time = timestamp; sample.time = timestamp * 1000;
if (callback) callback (DC_SAMPLE_TIME, sample, userdata); if (callback) callback (DC_SAMPLE_TIME, &sample, userdata);
// Depth (1/10 m). // Depth (1/10 m).
unsigned int depth = array_uint16_le (data + offset + 6); unsigned int depth = array_uint16_le (data + offset + 6);
if (maxdepth < depth) if (maxdepth < depth)
maxdepth = depth; maxdepth = depth;
sample.depth = depth / 10.0; sample.depth = depth / 10.0;
if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata); if (callback) callback (DC_SAMPLE_DEPTH, &sample, userdata);
// Temperature (Celsius). // Temperature (Celsius).
signed int temperature = (signed short) array_uint16_le (data + offset + 8); signed int temperature = (signed short) array_uint16_le (data + offset + 8);
sample.temperature = temperature / 10.0; sample.temperature = temperature / 10.0;
if (callback) callback (DC_SAMPLE_TEMPERATURE, sample, userdata); if (callback) callback (DC_SAMPLE_TEMPERATURE, &sample, userdata);
// Dive mode // Dive mode
unsigned int mode = data[offset + 18]; unsigned int mode = data[offset + 18];
@ -521,7 +506,7 @@ divesystem_idive_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callba
if (mode == SCR || mode == CCR) { if (mode == SCR || mode == CCR) {
unsigned int setpoint = array_uint16_le (data + offset + 19); unsigned int setpoint = array_uint16_le (data + offset + 19);
sample.setpoint = setpoint / 1000.0; sample.setpoint = setpoint / 1000.0;
if (callback) callback (DC_SAMPLE_SETPOINT, sample, userdata); if (callback) callback (DC_SAMPLE_SETPOINT, &sample, userdata);
} }
// Gaschange. // Gaschange.
@ -548,7 +533,7 @@ divesystem_idive_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callba
} }
sample.gasmix = i; sample.gasmix = i;
if (callback) callback (DC_SAMPLE_GASMIX, sample, userdata); if (callback) callback (DC_SAMPLE_GASMIX, &sample, userdata);
o2_previous = o2; o2_previous = o2;
he_previous = he; he_previous = he;
} }
@ -566,18 +551,20 @@ divesystem_idive_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callba
if (decostop) { if (decostop) {
sample.deco.type = DC_DECO_DECOSTOP; sample.deco.type = DC_DECO_DECOSTOP;
sample.deco.depth = decostop / 10.0; sample.deco.depth = decostop / 10.0;
sample.deco.time = apos4 ? decotime : tts; sample.deco.time = decotime;
sample.deco.tts = tts;
} else { } else {
sample.deco.type = DC_DECO_NDL; sample.deco.type = DC_DECO_NDL;
sample.deco.depth = 0.0; sample.deco.depth = 0.0;
sample.deco.time = tts; sample.deco.time = tts;
sample.deco.tts = 0;
} }
if (callback) callback (DC_SAMPLE_DECO, sample, userdata); if (callback) callback (DC_SAMPLE_DECO, &sample, userdata);
// CNS // CNS
unsigned int cns = array_uint16_le (data + offset + 29); unsigned int cns = array_uint16_le (data + offset + 29);
sample.cns = cns / 100.0; sample.cns = cns / 100.0;
if (callback) callback (DC_SAMPLE_CNS, sample, userdata); if (callback) callback (DC_SAMPLE_CNS, &sample, userdata);
// Tank Pressure // Tank Pressure
if (samplesize == SZ_SAMPLE_IX3M_APOS4) { if (samplesize == SZ_SAMPLE_IX3M_APOS4) {
@ -598,7 +585,7 @@ divesystem_idive_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callba
sample.event.time = 0; sample.event.time = 0;
sample.event.flags = 0; sample.event.flags = 0;
sample.event.value = 0; sample.event.value = 0;
if (callback) callback (DC_SAMPLE_EVENT, sample, userdata); if (callback) callback (DC_SAMPLE_EVENT, &sample, userdata);
} else { } else {
// Get the index of the tank. // Get the index of the tank.
if (id != tank_previous) { if (id != tank_previous) {
@ -628,10 +615,20 @@ divesystem_idive_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callba
if (tank_idx < ntanks) { if (tank_idx < ntanks) {
sample.pressure.tank = tank_idx; sample.pressure.tank = tank_idx;
sample.pressure.value = pressure; sample.pressure.value = pressure;
if (callback) callback (DC_SAMPLE_PRESSURE, sample, userdata); if (callback) callback (DC_SAMPLE_PRESSURE, &sample, userdata);
tank[tank_idx].endpressure = pressure; tank[tank_idx].endpressure = pressure;
} }
} }
// Compass bearing
unsigned int bearing = array_uint16_le (data + offset + 50);
if (bearing != 0) {
have_bearing = 1; // Stop ignoring zero values.
}
if (have_bearing && bearing != 0xFFFF) {
sample.bearing = bearing;
if (callback) callback (DC_SAMPLE_BEARING, &sample, userdata);
}
} }
offset += samplesize; offset += samplesize;

View File

@ -85,8 +85,9 @@ dc_field_get(dc_field_cache_t *cache, dc_field_type_t type, unsigned int flags,
case DC_FIELD_AVGDEPTH: case DC_FIELD_AVGDEPTH:
return DC_FIELD_VALUE(*cache, value, AVGDEPTH); return DC_FIELD_VALUE(*cache, value, AVGDEPTH);
case DC_FIELD_GASMIX_COUNT: case DC_FIELD_GASMIX_COUNT:
case DC_FIELD_TANK_COUNT:
return DC_FIELD_VALUE(*cache, value, GASMIX_COUNT); return DC_FIELD_VALUE(*cache, value, GASMIX_COUNT);
case DC_FIELD_TANK_COUNT:
return DC_FIELD_VALUE(*cache, value, TANK_COUNT);
case DC_FIELD_GASMIX: case DC_FIELD_GASMIX:
if (flags >= MAXGASES) if (flags >= MAXGASES)
break; break;

View File

@ -14,6 +14,7 @@ typedef struct dc_field_cache {
double ATMOSPHERIC; double ATMOSPHERIC;
dc_divemode_t DIVEMODE; dc_divemode_t DIVEMODE;
unsigned int GASMIX_COUNT; unsigned int GASMIX_COUNT;
unsigned int TANK_COUNT;
dc_salinity_t SALINITY; dc_salinity_t SALINITY;
dc_gasmix_t GASMIX[MAXGASES]; dc_gasmix_t GASMIX[MAXGASES];
@ -26,6 +27,7 @@ typedef struct dc_field_cache {
// dc_tank_t TANK[MAXGASES] // dc_tank_t TANK[MAXGASES]
// but that's for later // but that's for later
dc_tankinfo_t tankinfo[MAXGASES]; dc_tankinfo_t tankinfo[MAXGASES];
dc_tank_usage_t tankusage[MAXGASES];
double tanksize[MAXGASES]; double tanksize[MAXGASES];
double tankworkingpressure[MAXGASES]; double tankworkingpressure[MAXGASES];

View File

@ -52,6 +52,7 @@ typedef struct garmin_device_t {
dc_device_t base; dc_device_t base;
dc_iostream_t *iostream; dc_iostream_t *iostream;
unsigned char fingerprint[FIT_NAME_SIZE]; unsigned char fingerprint[FIT_NAME_SIZE];
unsigned int model;
#ifdef HAVE_LIBMTP #ifdef HAVE_LIBMTP
unsigned char use_mtp; unsigned char use_mtp;
LIBMTP_mtpdevice_t *mtp_device; LIBMTP_mtpdevice_t *mtp_device;
@ -92,6 +93,7 @@ garmin_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t *ios
// Set the default values. // Set the default values.
device->iostream = iostream; device->iostream = iostream;
memset(device->fingerprint, 0, sizeof(device->fingerprint)); memset(device->fingerprint, 0, sizeof(device->fingerprint));
device->model = model;
#ifdef HAVE_LIBMTP #ifdef HAVE_LIBMTP
// for a Descent Mk2/Mk2i, we have to use MTP to access its storage; // for a Descent Mk2/Mk2i, we have to use MTP to access its storage;
@ -99,7 +101,6 @@ garmin_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t *ios
// in order to have only one entry for the Mk2, we don't use the Mk2/APAC model number in our code // in order to have only one entry for the Mk2, we don't use the Mk2/APAC model number in our code
device->use_mtp = (model == (0x0FFF & DESCENT_MK2)); device->use_mtp = (model == (0x0FFF & DESCENT_MK2));
device->mtp_device = NULL; device->mtp_device = NULL;
DEBUG(context, "Found Garmin with model 0x%x which is a %s\n", model, (device->use_mtp ? "Mk2/Mk2i" : "Mk1"));
#endif #endif
*out = (dc_device_t *) device; *out = (dc_device_t *) device;
@ -136,6 +137,17 @@ garmin_device_close (dc_device_t *abstract)
return DC_STATUS_SUCCESS; return DC_STATUS_SUCCESS;
} }
/*
* NOTE! The fingerprint is only the 24 first bytes of this,
* aka FIT_NAME_SIZE.
*/
#define FILE_NAME_SIZE 64
struct fit_file {
char name[FILE_NAME_SIZE + 1];
unsigned int mtp_id;
};
struct file_list { struct file_list {
int nr, allocated; int nr, allocated;
struct fit_file *array; struct fit_file *array;
@ -173,8 +185,8 @@ static int name_cmp(const void *_a, const void *_b)
const char *a_name = a->name; const char *a_name = a->name;
const char *b_name = b->name; const char *b_name = b->name;
char a_buffer[FIT_NAME_SIZE]; char a_buffer[FILE_NAME_SIZE];
char b_buffer[FIT_NAME_SIZE]; char b_buffer[FILE_NAME_SIZE];
if (strlen(a_name) == 12) { if (strlen(a_name) == 12) {
parse_short_name(a_name, a_buffer); parse_short_name(a_name, a_buffer);
@ -200,19 +212,16 @@ static int
check_filename(dc_device_t *abstract, const char *name) check_filename(dc_device_t *abstract, const char *name)
{ {
int len = strlen(name); int len = strlen(name);
const char *explain = NULL;
DEBUG(abstract->context, " %s", name);
if (len < 5) if (len < 5)
explain = "name too short"; return 0;
if (len >= FIT_NAME_SIZE) if (len >= FILE_NAME_SIZE)
explain = "name too long"; return 0;
if (strncasecmp(name + len - 4, ".FIT", 4)) if (strncasecmp(name + len - 4, ".FIT", 4))
explain = "name lacks FIT suffix"; return 0;
DEBUG(abstract->context, " %s - %s", name, explain ? explain : "adding to list"); DEBUG(abstract->context, " %s - adding to list", name);
return explain == NULL; return 1;
} }
static dc_status_t static dc_status_t
@ -243,8 +252,8 @@ add_name(struct file_list *files, const char *name, unsigned int mtp_id)
* will zero-pad the end of the result buffer. * will zero-pad the end of the result buffer.
*/ */
struct fit_file *entry = files->array + files->nr++; struct fit_file *entry = files->array + files->nr++;
strncpy(entry->name, name, FIT_NAME_SIZE); strncpy(entry->name, name, FILE_NAME_SIZE);
entry->name[FIT_NAME_SIZE] = 0; // ensure it's null-terminated entry->name[FILE_NAME_SIZE] = 0; // ensure it's null-terminated
entry->mtp_id = mtp_id; entry->mtp_id = mtp_id;
} }
@ -321,12 +330,16 @@ mtp_get_file_list(dc_device_t *abstract, struct file_list *files)
for (i = 0; i < numrawdevices; i++) { for (i = 0; i < numrawdevices; i++) {
LIBMTP_devicestorage_t *storage; LIBMTP_devicestorage_t *storage;
// we only want to read from a Garmin Descent Mk2 device at this point // we only want to read from a Garmin Descent Mk2 device at this point
if (rawdevices[i].device_entry.vendor_id != GARMIN_VENDOR || if (rawdevices[i].device_entry.vendor_id != GARMIN_VENDOR) {
(rawdevices[i].device_entry.product_id != DESCENT_MK2 && rawdevices[i].device_entry.product_id != DESCENT_MK2_APAC)) {
DEBUG(abstract->context, "Garmin/mtp: skipping raw device %04x/%04x", DEBUG(abstract->context, "Garmin/mtp: skipping raw device %04x/%04x",
rawdevices[i].device_entry.vendor_id, rawdevices[i].device_entry.product_id); rawdevices[i].device_entry.vendor_id, rawdevices[i].device_entry.product_id);
continue; continue;
} }
if (rawdevices[i].device_entry.product_id != DESCENT_MK2 && rawdevices[i].device_entry.product_id != DESCENT_MK2_APAC) {
DEBUG(abstract->context, "Garmin/mtp: skipping Garmin raw device %04x/%04x, as it is not a dive computer / does not support MTP",
rawdevices[i].device_entry.vendor_id, rawdevices[i].device_entry.product_id);
continue;
}
device->mtp_device = LIBMTP_Open_Raw_Device_Uncached(&rawdevices[i]); device->mtp_device = LIBMTP_Open_Raw_Device_Uncached(&rawdevices[i]);
if (device->mtp_device == NULL) { if (device->mtp_device == NULL) {
DEBUG(abstract->context, "Garmin/mtp: unable to open raw device %d", i); DEBUG(abstract->context, "Garmin/mtp: unable to open raw device %d", i);
@ -407,7 +420,7 @@ read_file(char *pathname, int pathlen, const char *name, dc_buffer_t *file)
int fd, rc; int fd, rc;
pathname[pathlen] = '/'; pathname[pathlen] = '/';
memcpy(pathname+pathlen+1, name, FIT_NAME_SIZE); memcpy(pathname+pathlen+1, name, FILE_NAME_SIZE);
fd = open(pathname, O_RDONLY | O_BINARY); fd = open(pathname, O_RDONLY | O_BINARY);
if (fd < 0) if (fd < 0)
@ -438,7 +451,6 @@ garmin_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void
{ {
dc_status_t status = DC_STATUS_SUCCESS; dc_status_t status = DC_STATUS_SUCCESS;
garmin_device_t *device = (garmin_device_t *) abstract; garmin_device_t *device = (garmin_device_t *) abstract;
dc_parser_t *parser;
char pathname[PATH_MAX]; char pathname[PATH_MAX];
char pathname_input[PATH_MAX]; char pathname_input[PATH_MAX];
size_t pathlen; size_t pathlen;
@ -466,7 +478,7 @@ garmin_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void
// The actual dives are under the "Garmin/Activity/" directory // The actual dives are under the "Garmin/Activity/" directory
// as FIT files, with names like "2018-08-20-10-23-30.fit". // as FIT files, with names like "2018-08-20-10-23-30.fit".
// Make sure our buffer is big enough. // Make sure our buffer is big enough.
if (pathlen + strlen("/Garmin/Activity/") + FIT_NAME_SIZE + 2 > PATH_MAX) { if (pathlen + strlen("/Garmin/Activity/") + FILE_NAME_SIZE + 2 > PATH_MAX) {
ERROR (abstract->context, "Invalid Garmin base directory '%s'", pathname_input); ERROR (abstract->context, "Invalid Garmin base directory '%s'", pathname_input);
return DC_STATUS_IO; return DC_STATUS_IO;
} }
@ -529,16 +541,12 @@ garmin_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void
free(files.array); free(files.array);
return DC_STATUS_NOMEMORY; return DC_STATUS_NOMEMORY;
} }
if ((rc = garmin_parser_create(&parser, abstract->context) != DC_STATUS_SUCCESS)) {
ERROR (abstract->context, "Failed to create parser for dive verification.");
free(files.array);
return rc;
}
dc_event_devinfo_t devinfo; dc_event_devinfo_t devinfo;
dc_event_devinfo_t *devinfo_p = &devinfo; dc_event_devinfo_t *devinfo_p = &devinfo;
for (int i = 0; i < files.nr; i++) { for (int i = 0; i < files.nr; i++) {
const char *name = files.array[i].name; const char *name = files.array[i].name;
dc_parser_t *parser;
const unsigned char *data; const unsigned char *data;
unsigned int size; unsigned int size;
short is_dive = 0; short is_dive = 0;
@ -564,7 +572,14 @@ garmin_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void
data = dc_buffer_get_data(file); data = dc_buffer_get_data(file);
size = dc_buffer_get_size(file); size = dc_buffer_get_size(file);
is_dive = garmin_parser_is_dive(parser, data, size, devinfo_p); status = garmin_parser_create(&parser, abstract->context, data, size);
if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to create parser for dive verification.");
free(files.array);
return rc;
}
is_dive = !device->model || garmin_parser_is_dive(parser, devinfo_p);
if (devinfo_p) { if (devinfo_p) {
// first time we came through here, let's emit the // first time we came through here, let's emit the
// devinfo and vendor events // devinfo and vendor events
@ -573,6 +588,7 @@ garmin_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void
} }
if (!is_dive) { if (!is_dive) {
DEBUG(abstract->context, "decided %s isn't a dive.", name); DEBUG(abstract->context, "decided %s isn't a dive.", name);
dc_parser_destroy(parser);
continue; continue;
} }
@ -581,10 +597,10 @@ garmin_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void
progress.current++; progress.current++;
device_event_emit(abstract, DC_EVENT_PROGRESS, &progress); device_event_emit(abstract, DC_EVENT_PROGRESS, &progress);
dc_parser_destroy(parser);
} }
free(files.array); free(files.array);
dc_parser_destroy(parser);
dc_buffer_free(file); dc_buffer_free(file);
return status; return status;
} }

View File

@ -35,12 +35,12 @@ dc_status_t
garmin_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream, unsigned int model); garmin_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream, unsigned int model);
dc_status_t dc_status_t
garmin_parser_create (dc_parser_t **parser, dc_context_t *context); garmin_parser_create (dc_parser_t **parser, dc_context_t *context, const unsigned char data[], size_t size);
// we need to be able to call into the parser to check if the // we need to be able to call into the parser to check if the
// files that we find are actual dives // files that we find are actual dives
int int
garmin_parser_is_dive (dc_parser_t *abstract, const unsigned char *data, unsigned int size, dc_event_devinfo_t *devinfo_p); garmin_parser_is_dive (dc_parser_t *abstract, dc_event_devinfo_t *devinfo_p);
// The dive names are of the form "2018-08-20-10-23-30.fit" // The dive names are of the form "2018-08-20-10-23-30.fit"
// With the terminating zero, that's 24 bytes. // With the terminating zero, that's 24 bytes.
@ -49,11 +49,6 @@ garmin_parser_is_dive (dc_parser_t *abstract, const unsigned char *data, unsigne
// special fixed header in the parser data too. // special fixed header in the parser data too.
#define FIT_NAME_SIZE 24 #define FIT_NAME_SIZE 24
struct fit_file {
char name[FIT_NAME_SIZE + 1];
unsigned int mtp_id;
};
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif /* __cplusplus */ #endif /* __cplusplus */

View File

@ -30,7 +30,7 @@
#include "array.h" #include "array.h"
#include "field-cache.h" #include "field-cache.h"
#define MAXFIELDS 128 #define MAXFIELDS 160
struct msg_desc; struct msg_desc;
@ -38,7 +38,7 @@ struct msg_desc;
struct type_desc { struct type_desc {
const char *msg_name; const char *msg_name;
const struct msg_desc *msg_desc; const struct msg_desc *msg_desc;
unsigned char nrfields; unsigned char nrfields, devfields;
unsigned char fields[MAXFIELDS][3]; unsigned char fields[MAXFIELDS][3];
}; };
@ -65,13 +65,14 @@ struct garmin_sensor {
struct record_data { struct record_data {
unsigned int pending; unsigned int pending;
unsigned int time; unsigned int time;
unsigned int timestamp;
// RECORD_DECO // RECORD_DECO
int stop_time; int stop_time;
double ceiling; double ceiling;
// RECORD_GASMIX // RECORD_GASMIX
int index, gas_status, gas_type; int index, gas_status;
dc_gasmix_t gasmix; dc_gasmix_t gasmix;
// RECORD_EVENT // RECORD_EVENT
@ -128,7 +129,7 @@ typedef struct garmin_parser_t {
unsigned int setpoint_low_cbar, setpoint_high_cbar; unsigned int setpoint_low_cbar, setpoint_high_cbar;
unsigned int setpoint_low_switch_depth_mm, setpoint_high_switch_depth_mm; unsigned int setpoint_low_switch_depth_mm, setpoint_high_switch_depth_mm;
unsigned int setpoint_low_switch_mode, setpoint_high_switch_mode; unsigned int setpoint_low_switch_mode, setpoint_high_switch_mode;
dc_tankinfo_t *current_tankinfo; dc_usage_t current_gasmix_usage;
} dive; } dive;
// I count nine (!) different GPS fields Hmm. // I count nine (!) different GPS fields Hmm.
@ -231,27 +232,27 @@ static void garmin_event(struct garmin_parser_t *garmin,
if (!sample.event.name) if (!sample.event.name)
return; return;
garmin->callback(DC_SAMPLE_EVENT, sample, garmin->userdata); garmin->callback(DC_SAMPLE_EVENT, &sample, garmin->userdata);
return; return;
case 57: case 57:
sample.gasmix = data; sample.gasmix = data;
garmin->callback(DC_SAMPLE_GASMIX, sample, garmin->userdata); garmin->callback(DC_SAMPLE_GASMIX, &sample, garmin->userdata);
dc_tankinfo_t *tankinfo = &garmin->cache.tankinfo[data]; dc_usage_t gasmix_usage = garmin->cache.GASMIX[data].usage;
if (!garmin->dive.current_tankinfo || (*tankinfo & DC_TANKINFO_CC_DILUENT) != (*garmin->dive.current_tankinfo & DC_TANKINFO_CC_DILUENT)) { if (gasmix_usage != garmin->dive.current_gasmix_usage) {
dc_sample_value_t sample2 = {0}; dc_sample_value_t sample2 = {0};
sample2.event.type = SAMPLE_EVENT_STRING; sample2.event.type = SAMPLE_EVENT_STRING;
if (*tankinfo & DC_TANKINFO_CC_DILUENT) { if (gasmix_usage == DC_USAGE_DILUENT) {
sample2.event.name = "Switched to closed circuit"; sample2.event.name = "Switched to closed circuit";
} else { } else {
sample2.event.name = "Switched to open circuit bailout"; sample2.event.name = "Switched to open circuit bailout";
} }
sample2.event.flags = 2 << SAMPLE_FLAGS_SEVERITY_SHIFT; sample2.event.flags = 2 << SAMPLE_FLAGS_SEVERITY_SHIFT;
garmin->callback(DC_SAMPLE_EVENT, sample2, garmin->userdata); garmin->callback(DC_SAMPLE_EVENT, &sample2, garmin->userdata);
garmin->dive.current_tankinfo = tankinfo; garmin->dive.current_gasmix_usage = gasmix_usage;
} }
return; return;
@ -278,17 +279,7 @@ static void flush_pending_record(struct garmin_parser_t *garmin)
int index = record->index; int index = record->index;
if (enabled && index < MAXGASES) { if (enabled && index < MAXGASES) {
DC_ASSIGN_IDX(garmin->cache, GASMIX, index, record->gasmix); DC_ASSIGN_IDX(garmin->cache, GASMIX, index, record->gasmix);
if (index + 1 > garmin->cache.GASMIX_COUNT) { DC_ASSIGN_FIELD(garmin->cache, GASMIX_COUNT, index + 1);
DC_ASSIGN_FIELD(garmin->cache, GASMIX_COUNT, index + 1);
garmin->cache.initialized |= 1 << DC_FIELD_TANK_COUNT;
}
dc_tankinfo_t tankinfo = DC_TANKINFO_METRIC;
if (record->gas_type == 1) {
tankinfo |= DC_TANKINFO_CC_DILUENT;
}
garmin->cache.tankinfo[index] = tankinfo;
garmin->cache.initialized |= 1 << DC_FIELD_TANK;
} }
} }
if (pending & RECORD_DEVICE_INFO && record->device_index == 0) { if (pending & RECORD_DEVICE_INFO && record->device_index == 0) {
@ -323,7 +314,7 @@ static void flush_pending_record(struct garmin_parser_t *garmin)
sample.deco.type = DC_DECO_DECOSTOP; sample.deco.type = DC_DECO_DECOSTOP;
sample.deco.time = record->stop_time; sample.deco.time = record->stop_time;
sample.deco.depth = record->ceiling; sample.deco.depth = record->ceiling;
garmin->callback(DC_SAMPLE_DECO, sample, garmin->userdata); garmin->callback(DC_SAMPLE_DECO, &sample, garmin->userdata);
} }
if (pending & RECORD_EVENT) { if (pending & RECORD_EVENT) {
@ -336,19 +327,19 @@ static void flush_pending_record(struct garmin_parser_t *garmin)
sample.pressure.tank = find_tank_index(garmin, record->sensor); sample.pressure.tank = find_tank_index(garmin, record->sensor);
sample.pressure.value = record->pressure / 100.0; sample.pressure.value = record->pressure / 100.0;
garmin->callback(DC_SAMPLE_PRESSURE, sample, garmin->userdata); garmin->callback(DC_SAMPLE_PRESSURE, &sample, garmin->userdata);
} }
if (pending & RECORD_SETPOINT_CHANGE) { if (pending & RECORD_SETPOINT_CHANGE) {
dc_sample_value_t sample = {0}; dc_sample_value_t sample = {0};
sample.setpoint = record->setpoint_actual_cbar / 100.0; sample.setpoint = record->setpoint_actual_cbar / 100.0;
garmin->callback(DC_SAMPLE_SETPOINT, sample, garmin->userdata); garmin->callback(DC_SAMPLE_SETPOINT, &sample, garmin->userdata);
} }
} }
static dc_status_t garmin_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size); static dc_status_t garmin_parser_set_data (garmin_parser_t *garmin, const unsigned char *data, unsigned int size);
static dc_status_t garmin_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime); static dc_status_t garmin_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime);
static dc_status_t garmin_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value); static dc_status_t garmin_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value);
static dc_status_t garmin_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata); static dc_status_t garmin_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata);
@ -356,7 +347,6 @@ static dc_status_t garmin_parser_samples_foreach (dc_parser_t *abstract, dc_samp
static const dc_parser_vtable_t garmin_parser_vtable = { static const dc_parser_vtable_t garmin_parser_vtable = {
sizeof(garmin_parser_t), sizeof(garmin_parser_t),
DC_FAMILY_GARMIN, DC_FAMILY_GARMIN,
garmin_parser_set_data, /* set_data */
NULL, /* set_clock */ NULL, /* set_clock */
NULL, /* set_atmospheric */ NULL, /* set_atmospheric */
NULL, /* set_density */ NULL, /* set_density */
@ -367,7 +357,7 @@ static const dc_parser_vtable_t garmin_parser_vtable = {
}; };
dc_status_t dc_status_t
garmin_parser_create (dc_parser_t **out, dc_context_t *context) garmin_parser_create (dc_parser_t **out, dc_context_t *context, const unsigned char data[], size_t size)
{ {
garmin_parser_t *parser = NULL; garmin_parser_t *parser = NULL;
@ -375,39 +365,64 @@ garmin_parser_create (dc_parser_t **out, dc_context_t *context)
return DC_STATUS_INVALIDARGS; return DC_STATUS_INVALIDARGS;
// Allocate memory. // Allocate memory.
parser = (garmin_parser_t *) dc_parser_allocate (context, &garmin_parser_vtable); parser = (garmin_parser_t *) dc_parser_allocate (context, &garmin_parser_vtable, data, size);
if (parser == NULL) { if (parser == NULL) {
ERROR (context, "Failed to allocate memory."); ERROR (context, "Failed to allocate memory.");
return DC_STATUS_NOMEMORY; return DC_STATUS_NOMEMORY;
} }
garmin_parser_set_data(parser, data, size);
*out = (dc_parser_t *) parser; *out = (dc_parser_t *) parser;
return DC_STATUS_SUCCESS; return DC_STATUS_SUCCESS;
} }
#define DECLARE_FIT_TYPE(name, ctype, inval) \ /*
* We really shouldn't use array_uint_be/le, since they
* can't deal with 64-bit types.
*
* But we've not actually seen any yet, so..
*/
static inline unsigned long long garmin_value(struct garmin_parser_t *g, const unsigned char *p, unsigned int type_size)
{
if (g->is_big_endian)
return array_uint_be(p, type_size);
else
return array_uint_le(p, type_size);
}
#define FMTSIZE 64
#define DECLARE_FIT_TYPE(name, ctype, inval, fmt) \
typedef ctype name; \ typedef ctype name; \
static const name name##_INVAL = inval static const name name##_INVAL = inval; \
static name name##_VALUE(garmin_parser_t *g, const void *p) \
{ return (name) garmin_value(g, p, sizeof(name)); } \
static void name##_FORMAT(name val, char *buf) \
{ snprintf(buf, FMTSIZE, fmt, val); }
DECLARE_FIT_TYPE(ENUM, unsigned char, 0xff); DECLARE_FIT_TYPE(ENUM, unsigned char, 0xff, "%u");
DECLARE_FIT_TYPE(UINT8, unsigned char, 0xff); DECLARE_FIT_TYPE(UINT8, unsigned char, 0xff, "%u");
DECLARE_FIT_TYPE(UINT16, unsigned short, 0xffff); DECLARE_FIT_TYPE(UINT16, unsigned short, 0xffff, "%u");
DECLARE_FIT_TYPE(UINT32, unsigned int, 0xffffffff); DECLARE_FIT_TYPE(UINT32, unsigned int, 0xffffffff, "%u");
DECLARE_FIT_TYPE(UINT64, unsigned long long, 0xffffffffffffffffull); DECLARE_FIT_TYPE(UINT64, unsigned long long, 0xffffffffffffffffull, "%llu");
DECLARE_FIT_TYPE(UINT8Z, unsigned char, 0); DECLARE_FIT_TYPE(UINT8Z, unsigned char, 0, "%u");
DECLARE_FIT_TYPE(UINT16Z, unsigned short, 0); DECLARE_FIT_TYPE(UINT16Z, unsigned short, 0, "%u");
DECLARE_FIT_TYPE(UINT32Z, unsigned int, 0); DECLARE_FIT_TYPE(UINT32Z, unsigned int, 0, "%u");
DECLARE_FIT_TYPE(SINT8, signed char, 0x7f); DECLARE_FIT_TYPE(SINT8, signed char, 0x7f, "%d");
DECLARE_FIT_TYPE(SINT16, signed short, 0x7fff); DECLARE_FIT_TYPE(SINT16, signed short, 0x7fff, "%d");
DECLARE_FIT_TYPE(SINT32, signed int, 0x7fffffff); DECLARE_FIT_TYPE(SINT32, signed int, 0x7fffffff, "%d");
DECLARE_FIT_TYPE(SINT64, signed long long, 0x7fffffffffffffffll); DECLARE_FIT_TYPE(SINT64, signed long long, 0x7fffffffffffffffll, "%lld");
DECLARE_FIT_TYPE(FLOAT, unsigned int, 0xffffffff); DECLARE_FIT_TYPE(FLOAT, unsigned int, 0xffffffff, "%u");
DECLARE_FIT_TYPE(DOUBLE, unsigned long long, 0xffffffffffffffffll); DECLARE_FIT_TYPE(DOUBLE, unsigned long long, 0xffffffffffffffffll, "%llu");
DECLARE_FIT_TYPE(STRING, char *, NULL); DECLARE_FIT_TYPE(STRING, char *, NULL, "\"%s\"");
// Override string value function - it's the pointer itself
#define STRING_VALUE(g, p) ((char *)(p))
static const struct { static const struct {
const char *type_name; const char *type_name;
@ -447,33 +462,18 @@ struct field_desc {
void (*parse)(struct garmin_parser_t *, unsigned char base_type, const unsigned char *data); void (*parse)(struct garmin_parser_t *, unsigned char base_type, const unsigned char *data);
}; };
static inline int base_type_is_integer(unsigned char base_type)
{
return !memcmp(base_type_info[base_type].type_name + 1, "INT", 3);
}
static inline unsigned long array_uint_endian(const unsigned char *p, unsigned int type_size, unsigned char bigendian)
{
if (bigendian)
return array_uint_be(p, type_size);
else
return array_uint_le(p, type_size);
}
#define DECLARE_FIELD(msg, name, type) __DECLARE_FIELD(msg##_##name, type) #define DECLARE_FIELD(msg, name, type) __DECLARE_FIELD(msg##_##name, type)
#define __DECLARE_FIELD(name, type) \ #define __DECLARE_FIELD(name, type) \
static void parse_##name(struct garmin_parser_t *, const type); \ static void parse_##name(struct garmin_parser_t *, const type); \
static void parse_##name##_##type(struct garmin_parser_t *g, unsigned char base_type, const unsigned char *p) \ static void parse_##name##_##type(struct garmin_parser_t *g, unsigned char base_type, const unsigned char *p) \
{ \ { \
char fmtbuf[FMTSIZE]; \
if (strcmp(#type, base_type_info[base_type].type_name)) \ if (strcmp(#type, base_type_info[base_type].type_name)) \
fprintf(stderr, "%s: %s should be %s\n", #name, #type, base_type_info[base_type].type_name); \ fprintf(stderr, "%s: %s should be %s\n", #name, #type, base_type_info[base_type].type_name); \
type val; \ type val = type##_VALUE(g, p); \
if (base_type_info[base_type].type_size > 1 && base_type_is_integer(base_type)) \
val = (type)array_uint_endian(p, base_type_info[base_type].type_size, g->is_big_endian); \
else \
val = *(type *)p; \
if (val == type##_INVAL) return; \ if (val == type##_INVAL) return; \
DEBUG(g->base.context, "%s (%s): %lld", #name, #type, (long long)val); \ type##_FORMAT(val, fmtbuf); \
DEBUG(g->base.context, "%s (%s): %s", #name, #type, fmtbuf); \
parse_##name(g, val); \ parse_##name(g, val); \
} \ } \
static const struct field_desc name##_field_##type = { #name, parse_##name##_##type }; \ static const struct field_desc name##_field_##type = { #name, parse_##name##_##type }; \
@ -484,22 +484,26 @@ static inline unsigned long array_uint_endian(const unsigned char *p, unsigned i
// Convert to "standard epoch time" by adding 631065600. // Convert to "standard epoch time" by adding 631065600.
DECLARE_FIELD(ANY, timestamp, UINT32) DECLARE_FIELD(ANY, timestamp, UINT32)
{ {
garmin->record_data.timestamp = data;
if (garmin->callback) { if (garmin->callback) {
dc_sample_value_t sample = {0};
// Turn the timestamp relative to the beginning of the dive // Turn the timestamp relative to the beginning of the dive
if (data < garmin->dive.time) if (data < garmin->dive.time) {
DEBUG(garmin->base.context, "Timestamp before dive start: %d (dive start: %d)", data, garmin->dive.time);
return; return;
data -= garmin->dive.time; }
data -= garmin->dive.time - 1;
// Did we already do this? // Did we already do this?
if (data < garmin->record_data.time) if (data == garmin->record_data.time)
return; return;
garmin->record_data.time = data;
// Now we're ready to actually update the sample times // Now we're ready to actually update the sample times
garmin->record_data.time = data+1; dc_sample_value_t sample = {0};
sample.time = data; sample.time = data * 1000;
garmin->callback(DC_SAMPLE_TIME, sample, garmin->userdata); garmin->callback(DC_SAMPLE_TIME, &sample, garmin->userdata);
} }
} }
DECLARE_FIELD(ANY, message_index, UINT16) { garmin->record_data.index = data; } DECLARE_FIELD(ANY, message_index, UINT16) { garmin->record_data.index = data; }
@ -513,6 +517,7 @@ DECLARE_FIELD(FILE, serial, UINT32Z) { }
DECLARE_FIELD(FILE, creation_time, UINT32) { } DECLARE_FIELD(FILE, creation_time, UINT32) { }
DECLARE_FIELD(FILE, number, UINT16) { } DECLARE_FIELD(FILE, number, UINT16) { }
DECLARE_FIELD(FILE, other_time, UINT32) { } DECLARE_FIELD(FILE, other_time, UINT32) { }
DECLARE_FIELD(FILE, product_name, STRING) { }
// SESSION msg // SESSION msg
DECLARE_FIELD(SESSION, start_time, UINT32) { garmin->dive.time = data; } DECLARE_FIELD(SESSION, start_time, UINT32) { garmin->dive.time = data; }
@ -545,16 +550,19 @@ DECLARE_FIELD(RECORD, heart_rate, UINT8) // bpm
if (garmin->callback) { if (garmin->callback) {
dc_sample_value_t sample = {0}; dc_sample_value_t sample = {0};
sample.heartbeat = data; sample.heartbeat = data;
garmin->callback(DC_SAMPLE_HEARTBEAT, sample, garmin->userdata); garmin->callback(DC_SAMPLE_HEARTBEAT, &sample, garmin->userdata);
} }
} }
DECLARE_FIELD(RECORD, cadence, UINT8) { } // cadence
DECLARE_FIELD(RECORD, fract_cadence, UINT8) { } // fractional cadence
DECLARE_FIELD(RECORD, distance, UINT32) { } // Distance in 100 * m? WTF? DECLARE_FIELD(RECORD, distance, UINT32) { } // Distance in 100 * m? WTF?
DECLARE_FIELD(RECORD, speed, UINT16) { } // Speed (m/s?)
DECLARE_FIELD(RECORD, temperature, SINT8) // degrees C DECLARE_FIELD(RECORD, temperature, SINT8) // degrees C
{ {
if (garmin->callback) { if (garmin->callback) {
dc_sample_value_t sample = {0}; dc_sample_value_t sample = {0};
sample.temperature = data; sample.temperature = data;
garmin->callback(DC_SAMPLE_TEMPERATURE, sample, garmin->userdata); garmin->callback(DC_SAMPLE_TEMPERATURE, &sample, garmin->userdata);
} }
} }
DECLARE_FIELD(RECORD, abs_pressure, UINT32) {} // Pascal DECLARE_FIELD(RECORD, abs_pressure, UINT32) {} // Pascal
@ -563,7 +571,7 @@ DECLARE_FIELD(RECORD, depth, UINT32) // mm
if (garmin->callback) { if (garmin->callback) {
dc_sample_value_t sample = {0}; dc_sample_value_t sample = {0};
sample.depth = data / 1000.0; sample.depth = data / 1000.0;
garmin->callback(DC_SAMPLE_DEPTH, sample, garmin->userdata); garmin->callback(DC_SAMPLE_DEPTH, &sample, garmin->userdata);
} }
} }
DECLARE_FIELD(RECORD, next_stop_depth, UINT32) // mm DECLARE_FIELD(RECORD, next_stop_depth, UINT32) // mm
@ -581,7 +589,7 @@ DECLARE_FIELD(RECORD, tts, UINT32)
if (garmin->callback) { if (garmin->callback) {
dc_sample_value_t sample = {0}; dc_sample_value_t sample = {0};
sample.time = data; sample.time = data;
garmin->callback(DC_SAMPLE_TTS, sample, garmin->userdata); garmin->callback(DC_SAMPLE_TTS, &sample, garmin->userdata);
} }
} }
DECLARE_FIELD(RECORD, ndl, UINT32) // s DECLARE_FIELD(RECORD, ndl, UINT32) // s
@ -590,7 +598,7 @@ DECLARE_FIELD(RECORD, ndl, UINT32) // s
dc_sample_value_t sample = {0}; dc_sample_value_t sample = {0};
sample.deco.type = DC_DECO_NDL; sample.deco.type = DC_DECO_NDL;
sample.deco.time = data; sample.deco.time = data;
garmin->callback(DC_SAMPLE_DECO, sample, garmin->userdata); garmin->callback(DC_SAMPLE_DECO, &sample, garmin->userdata);
} }
} }
DECLARE_FIELD(RECORD, cns_load, UINT8) DECLARE_FIELD(RECORD, cns_load, UINT8)
@ -598,7 +606,7 @@ DECLARE_FIELD(RECORD, cns_load, UINT8)
if (garmin->callback) { if (garmin->callback) {
dc_sample_value_t sample = {0}; dc_sample_value_t sample = {0};
sample.cns = data / 100.0; sample.cns = data / 100.0;
garmin->callback(DC_SAMPLE_CNS, sample, garmin->userdata); garmin->callback(DC_SAMPLE_CNS, &sample, garmin->userdata);
} }
} }
DECLARE_FIELD(RECORD, n2_load, UINT16) { } // percent DECLARE_FIELD(RECORD, n2_load, UINT16) { } // percent
@ -635,9 +643,23 @@ DECLARE_FIELD(DEVICE_INFO, firmware, UINT16)
garmin->record_data.pending |= RECORD_DEVICE_INFO; garmin->record_data.pending |= RECORD_DEVICE_INFO;
} }
// ACTIVITY
DECLARE_FIELD(ACTIVITY, total_timer_time, UINT32) { }
DECLARE_FIELD(ACTIVITY, num_sessions, UINT16) { }
DECLARE_FIELD(ACTIVITY, type, ENUM) { }
DECLARE_FIELD(ACTIVITY, event, ENUM) { }
DECLARE_FIELD(ACTIVITY, event_type, ENUM) { }
DECLARE_FIELD(ACTIVITY, local_timestamp, UINT32)
{
int time_offset = data - garmin->record_data.timestamp;
garmin->dive.time_offset = time_offset;
}
DECLARE_FIELD(ACTIVITY, event_group, UINT8) { }
// SPORT // SPORT
DECLARE_FIELD(SPORT, sub_sport, ENUM) { DECLARE_FIELD(SPORT, sub_sport, ENUM) {
garmin->dive.sub_sport = (ENUM) data; garmin->dive.sub_sport = (ENUM) data;
garmin->dive.current_gasmix_usage = DC_USAGE_OPEN_CIRCUIT;
dc_divemode_t val; dc_divemode_t val;
switch (data) { switch (data) {
case 55: val = DC_DIVEMODE_GAUGE; case 55: val = DC_DIVEMODE_GAUGE;
@ -645,7 +667,10 @@ DECLARE_FIELD(SPORT, sub_sport, ENUM) {
case 56: case 56:
case 57: val = DC_DIVEMODE_FREEDIVE; case 57: val = DC_DIVEMODE_FREEDIVE;
break; break;
case 63: val = DC_DIVEMODE_CCR; case 63:
val = DC_DIVEMODE_CCR;
garmin->dive.current_gasmix_usage = DC_USAGE_DILUENT;
break; break;
default: val = DC_DIVEMODE_OC; default: val = DC_DIVEMODE_OC;
} }
@ -671,7 +696,10 @@ DECLARE_FIELD(DIVE_GAS, status, ENUM)
DECLARE_FIELD(DIVE_GAS, type, ENUM) DECLARE_FIELD(DIVE_GAS, type, ENUM)
{ {
// 0 - open circuit, 1 - CCR diluent // 0 - open circuit, 1 - CCR diluent
garmin->record_data.gas_type = data; if (data == 1)
garmin->record_data.gasmix.usage = DC_USAGE_DILUENT;
else
garmin->record_data.gasmix.usage = DC_USAGE_OPEN_CIRCUIT;
garmin->record_data.pending |= RECORD_GASMIX; garmin->record_data.pending |= RECORD_GASMIX;
} }
@ -771,12 +799,12 @@ DECLARE_FIELD(SENSOR_PROFILE, ant_channel_id, UINT32Z)
{ {
current_sensor(garmin)->sensor_id = data; current_sensor(garmin)->sensor_id = data;
} }
DECLARE_FIELD(SENSOR_PROFILE, name, STRING) { } // We don't pass in string types correctly DECLARE_FIELD(SENSOR_PROFILE, name, STRING) { }
DECLARE_FIELD(SENSOR_PROFILE, enabled, ENUM) DECLARE_FIELD(SENSOR_PROFILE, enabled, ENUM)
{ {
current_sensor(garmin)->sensor_enabled = data; current_sensor(garmin)->sensor_enabled = data;
} }
DECLARE_FIELD(SENSOR_PROFILE, sensor_type, UINT8) DECLARE_FIELD(SENSOR_PROFILE, sensor_type, ENUM)
{ {
// 28 is tank pod // 28 is tank pod
// start filling in next sensor after this record // start filling in next sensor after this record
@ -848,6 +876,17 @@ DECLARE_FIELD(EVENT, tank_pressure_reserve, UINT32Z) { } // sensor ID
DECLARE_FIELD(EVENT, tank_pressure_critical, UINT32Z) { } // sensor ID DECLARE_FIELD(EVENT, tank_pressure_critical, UINT32Z) { } // sensor ID
DECLARE_FIELD(EVENT, tank_pressure_lost, UINT32Z) { } // sensor ID DECLARE_FIELD(EVENT, tank_pressure_lost, UINT32Z) { } // sensor ID
// "Field description" (for developer fields)
DECLARE_FIELD(FIELD_DESCRIPTION, data_index, UINT8) { }
DECLARE_FIELD(FIELD_DESCRIPTION, field_definition, UINT8) { }
DECLARE_FIELD(FIELD_DESCRIPTION, base_type, UINT8) { }
DECLARE_FIELD(FIELD_DESCRIPTION, name, STRING) { } // "Depth"
DECLARE_FIELD(FIELD_DESCRIPTION, scale, UINT8) { }
DECLARE_FIELD(FIELD_DESCRIPTION, offset, SINT8) { }
DECLARE_FIELD(FIELD_DESCRIPTION, unit, STRING) { } // "feet"
DECLARE_FIELD(FIELD_DESCRIPTION, original_mesg, UINT16) { }
DECLARE_FIELD(FIELD_DESCRIPTION, original_field, UINT8) { }
struct msg_desc { struct msg_desc {
unsigned char maxfield; unsigned char maxfield;
const struct field_desc *field[]; const struct field_desc *field[];
@ -860,7 +899,7 @@ struct msg_desc {
static const struct msg_desc name##_msg_desc static const struct msg_desc name##_msg_desc
DECLARE_MESG(FILE) = { DECLARE_MESG(FILE) = {
.maxfield = 8, .maxfield = 9,
.field = { .field = {
SET_FIELD(FILE, 0, file_type, ENUM), SET_FIELD(FILE, 0, file_type, ENUM),
SET_FIELD(FILE, 1, manufacturer, UINT16), SET_FIELD(FILE, 1, manufacturer, UINT16),
@ -869,6 +908,7 @@ DECLARE_MESG(FILE) = {
SET_FIELD(FILE, 4, creation_time, UINT32), SET_FIELD(FILE, 4, creation_time, UINT32),
SET_FIELD(FILE, 5, number, UINT16), SET_FIELD(FILE, 5, number, UINT16),
SET_FIELD(FILE, 7, other_time, UINT32), SET_FIELD(FILE, 7, other_time, UINT32),
SET_FIELD(FILE, 8, product_name, STRING),
} }
}; };
@ -880,6 +920,7 @@ DECLARE_MESG(DEVICE_SETTINGS) = {
} }
}; };
DECLARE_MESG(USER_PROFILE) = { }; DECLARE_MESG(USER_PROFILE) = { };
DECLARE_MESG(HRM_PROFILE) = { };
DECLARE_MESG(ZONES_TARGET) = { }; DECLARE_MESG(ZONES_TARGET) = { };
DECLARE_MESG(SPORT) = { DECLARE_MESG(SPORT) = {
@ -926,8 +967,11 @@ DECLARE_MESG(RECORD) = {
SET_FIELD(RECORD, 1, position_long, SINT32), // 180 deg / 2**31 SET_FIELD(RECORD, 1, position_long, SINT32), // 180 deg / 2**31
SET_FIELD(RECORD, 2, altitude, UINT16), // 5 *m + 500 ? SET_FIELD(RECORD, 2, altitude, UINT16), // 5 *m + 500 ?
SET_FIELD(RECORD, 3, heart_rate, UINT8), // bpm SET_FIELD(RECORD, 3, heart_rate, UINT8), // bpm
SET_FIELD(RECORD, 4, cadence, UINT8), // cadence
SET_FIELD(RECORD, 5, distance, UINT32), // Distance in 100 * m? WTF? SET_FIELD(RECORD, 5, distance, UINT32), // Distance in 100 * m? WTF?
SET_FIELD(RECORD, 6, speed, UINT16), // m/s? Who knows..
SET_FIELD(RECORD, 13, temperature, SINT8), // degrees C SET_FIELD(RECORD, 13, temperature, SINT8), // degrees C
SET_FIELD(RECORD, 53, fract_cadence, UINT8), // fractional cadence
SET_FIELD(RECORD, 91, abs_pressure, UINT32), // Pascal SET_FIELD(RECORD, 91, abs_pressure, UINT32), // Pascal
SET_FIELD(RECORD, 92, depth, UINT32), // mm SET_FIELD(RECORD, 92, depth, UINT32), // mm
SET_FIELD(RECORD, 93, next_stop_depth, UINT32), // mm SET_FIELD(RECORD, 93, next_stop_depth, UINT32), // mm
@ -998,7 +1042,19 @@ DECLARE_MESG(DEVICE_INFO) = {
} }
}; };
DECLARE_MESG(ACTIVITY) = { }; DECLARE_MESG(ACTIVITY) = {
.maxfield = 7,
.field = {
SET_FIELD(ACTIVITY, 0, total_timer_time, UINT32),
SET_FIELD(ACTIVITY, 1, num_sessions, UINT16),
SET_FIELD(ACTIVITY, 2, type, ENUM),
SET_FIELD(ACTIVITY, 3, event, ENUM),
SET_FIELD(ACTIVITY, 4, event_type, ENUM),
SET_FIELD(ACTIVITY, 5, local_timestamp, UINT32),
SET_FIELD(ACTIVITY, 6, event_group, UINT8),
}
};
DECLARE_MESG(FILE_CREATOR) = { }; DECLARE_MESG(FILE_CREATOR) = { };
DECLARE_MESG(DIVE_SETTINGS) = { DECLARE_MESG(DIVE_SETTINGS) = {
@ -1041,7 +1097,7 @@ DECLARE_MESG(SENSOR_PROFILE) = {
SET_FIELD(SENSOR_PROFILE, 0, ant_channel_id, UINT32Z), // derived from the number engraved on the side SET_FIELD(SENSOR_PROFILE, 0, ant_channel_id, UINT32Z), // derived from the number engraved on the side
SET_FIELD(SENSOR_PROFILE, 2, name, STRING), SET_FIELD(SENSOR_PROFILE, 2, name, STRING),
SET_FIELD(SENSOR_PROFILE, 3, enabled, ENUM), SET_FIELD(SENSOR_PROFILE, 3, enabled, ENUM),
SET_FIELD(SENSOR_PROFILE, 52, sensor_type, UINT8), // 28 is tank pod SET_FIELD(SENSOR_PROFILE, 52, sensor_type, ENUM), // 28 is tank pod
SET_FIELD(SENSOR_PROFILE, 74, pressure_units, ENUM), // 0 is PSI, 1 is KPA (unused), 2 is Bar SET_FIELD(SENSOR_PROFILE, 74, pressure_units, ENUM), // 0 is PSI, 1 is KPA (unused), 2 is Bar
SET_FIELD(SENSOR_PROFILE, 75, rated_pressure, UINT16), SET_FIELD(SENSOR_PROFILE, 75, rated_pressure, UINT16),
SET_FIELD(SENSOR_PROFILE, 76, reserve_pressure, UINT16), SET_FIELD(SENSOR_PROFILE, 76, reserve_pressure, UINT16),
@ -1068,6 +1124,22 @@ DECLARE_MESG(TANK_SUMMARY) = {
} }
}; };
DECLARE_MESG(FIELD_DESCRIPTION) = {
.maxfield = 16,
.field = {
SET_FIELD(FIELD_DESCRIPTION, 0, data_index, UINT8),
SET_FIELD(FIELD_DESCRIPTION, 1, field_definition, UINT8),
SET_FIELD(FIELD_DESCRIPTION, 2, base_type, UINT8),
SET_FIELD(FIELD_DESCRIPTION, 3, name, STRING), // "Depth"
SET_FIELD(FIELD_DESCRIPTION, 6, scale, UINT8),
SET_FIELD(FIELD_DESCRIPTION, 7, offset, SINT8),
SET_FIELD(FIELD_DESCRIPTION, 8, unit, STRING), // "feet"
// Some kind of pointer to original field?
SET_FIELD(FIELD_DESCRIPTION, 14, original_mesg, UINT16),
SET_FIELD(FIELD_DESCRIPTION, 15, original_field, UINT8),
}
};
// Unknown global message ID's.. // Unknown global message ID's..
DECLARE_MESG(WTF_13) = { }; DECLARE_MESG(WTF_13) = { };
DECLARE_MESG(WTF_22) = { }; DECLARE_MESG(WTF_22) = { };
@ -1088,6 +1160,7 @@ static const struct {
SET_MESG( 0, FILE), SET_MESG( 0, FILE),
SET_MESG( 2, DEVICE_SETTINGS), SET_MESG( 2, DEVICE_SETTINGS),
SET_MESG( 3, USER_PROFILE), SET_MESG( 3, USER_PROFILE),
SET_MESG( 4, HRM_PROFILE),
SET_MESG( 7, ZONES_TARGET), SET_MESG( 7, ZONES_TARGET),
SET_MESG( 12, SPORT), SET_MESG( 12, SPORT),
SET_MESG( 13, WTF_13), SET_MESG( 13, WTF_13),
@ -1108,6 +1181,8 @@ static const struct {
SET_MESG(147, SENSOR_PROFILE), SET_MESG(147, SENSOR_PROFILE),
SET_MESG(206, FIELD_DESCRIPTION),
SET_MESG(216, WTF_216), SET_MESG(216, WTF_216),
SET_MESG(233, WTF_233), SET_MESG(233, WTF_233),
SET_MESG(258, DIVE_SETTINGS), SET_MESG(258, DIVE_SETTINGS),
@ -1142,14 +1217,6 @@ static const struct msg_desc *lookup_msg_desc(unsigned short msg, int local, con
return desc; return desc;
} }
static int traverse_compressed(struct garmin_parser_t *garmin,
const unsigned char *data, unsigned int size,
unsigned char type, unsigned int time)
{
fprintf(stderr, "Compressed record for local type %d:\n", type);
return -1;
}
static int all_data_inval(const unsigned char *data, int base_type, int len) static int all_data_inval(const unsigned char *data, int base_type, int len)
{ {
int base_size = base_type_info[base_type].type_size; int base_size = base_type_info[base_type].type_size;
@ -1241,20 +1308,17 @@ static int traverse_regular(struct garmin_parser_t *garmin,
if (!len) { if (!len) {
ERROR(garmin->base.context, "field with zero length\n"); ERROR(garmin->base.context, "field with zero length\n");
return -1; return total_len + size;
} }
if (size < len) { if (size < len) {
ERROR(garmin->base.context, "Data traversal size bigger than remaining data (%d vs %d)\n", len, size); ERROR(garmin->base.context, "Data traversal size bigger than remaining data (%d vs %d)\n", len, size);
return -1; return total_len + size;
} }
if (base_type > 16) { if (base_type > 16) {
ERROR(garmin->base.context, "Unknown base type %d\n", base_type); ERROR(garmin->base.context, "Unknown base type %d\n", base_type);
data += size; return total_len + size;
len -= size;
total_len += size;
continue;
} }
base_size = base_type_info[base_type].type_size; base_size = base_type_info[base_type].type_size;
if (len % base_size) { if (len % base_size) {
@ -1280,7 +1344,10 @@ static int traverse_regular(struct garmin_parser_t *garmin,
} }
if (field_desc) { if (field_desc) {
field_desc->parse(garmin, base_type, data); if (field_nr == 253 && !msg_desc->maxfield)
DEBUG(garmin->base.context, "Ignoring timestamp field for undefined message.");
else
field_desc->parse(garmin, base_type, data);
} else { } else {
unknown_field(garmin, data, msg_name, field_nr, base_type, len); unknown_field(garmin, data, msg_name, field_nr, base_type, len);
} }
@ -1290,6 +1357,31 @@ static int traverse_regular(struct garmin_parser_t *garmin,
size -= len; size -= len;
} }
for (int i = 0; i < desc->devfields; i++) {
int desc_idx = i + desc->nrfields;
const unsigned char *field = desc->fields[desc_idx];
unsigned int field_nr = field[0];
unsigned int len = field[1];
unsigned int type = field[2];
DEBUG(garmin->base.context, "Developer field %d %02x type %02x", i, field_nr, type);
if (!len) {
ERROR(garmin->base.context, " developer field with zero length\n");
return -1;
}
if (size < len) {
ERROR(garmin->base.context, " developer field bigger than remaining data (%d vs %d)\n", len, size);
return -1;
}
HEXDUMP(garmin->base.context, DC_LOGLEVEL_DEBUG, "data", data, len);
data += len;
total_len += len;
size -= len;
}
return total_len; return total_len;
} }
@ -1325,7 +1417,7 @@ static int traverse_definition(struct garmin_parser_t *garmin,
// data[1] tells us if this is big or little endian // data[1] tells us if this is big or little endian
garmin->is_big_endian = data[1] != 0; garmin->is_big_endian = data[1] != 0;
msg = array_uint_endian(data + 2, 2, garmin->is_big_endian); msg = garmin_value(garmin, data + 2, 2);
desc->msg_desc = lookup_msg_desc(msg, type, &desc->msg_name); desc->msg_desc = lookup_msg_desc(msg, type, &desc->msg_name);
fields = data[4]; fields = data[4];
DEBUG(garmin->base.context, "Define local type %d: %02x %s %04x %02x %s", DEBUG(garmin->base.context, "Define local type %d: %02x %s %04x %02x %s",
@ -1337,12 +1429,6 @@ static int traverse_definition(struct garmin_parser_t *garmin,
desc->nrfields = fields; desc->nrfields = fields;
len = 5 + fields*3; len = 5 + fields*3;
devfields = 0; devfields = 0;
if (record & 0x20) {
devfields = data[len];
len += 1 + devfields*3;
ERROR(garmin->base.context, "NO support for developer fields yet\n");
return -1;
}
for (int i = 0; i < fields; i++) { for (int i = 0; i < fields; i++) {
unsigned char *field = desc->fields[i]; unsigned char *field = desc->fields[i];
@ -1350,6 +1436,34 @@ static int traverse_definition(struct garmin_parser_t *garmin,
DEBUG(garmin->base.context, " %d: %02x %02x %02x", i, field[0], field[1], field[2]); DEBUG(garmin->base.context, " %d: %02x %02x %02x", i, field[0], field[1], field[2]);
} }
data += len;
/* Developer fields after the regular ones */
if (record & 0x20) {
devfields = data[0];
DEBUG(garmin->base.context, "Developer field (rec=%02x len=%d, devfields=%d)",
record, len, devfields);
/*
* one byte of dev field numbers, each three bytes in size
* (number/size/index)
*/
len += 1 + 3*devfields;
for (int i = 0; i < devfields; i++) {
int idx = fields + i;
unsigned char *field = desc->fields[idx];
if (idx > MAXFIELDS) {
ERROR(garmin->base.context, "Too many dev fields in description: %d+%d (max %d)\n", fields, i, MAXFIELDS);
return -1;
}
memcpy(field, data + (1+i*3), 3);
DEBUG(garmin->base.context, " %d: %02x %02x %02x", i, field[0], field[1], field[2]);
}
}
desc->devfields = devfields;
return len; return len;
} }
@ -1370,7 +1484,7 @@ traverse_data(struct garmin_parser_t *garmin)
if (len < FIT_NAME_SIZE) if (len < FIT_NAME_SIZE)
return DC_STATUS_IO; return DC_STATUS_IO;
DEBUG(garmin->base.context, "file %s", data); DEBUG(garmin->base.context, "file %.*s", FIT_NAME_SIZE, data);
data += FIT_NAME_SIZE; data += FIT_NAME_SIZE;
len -= FIT_NAME_SIZE; len -= FIT_NAME_SIZE;
@ -1414,10 +1528,20 @@ traverse_data(struct garmin_parser_t *garmin)
newtime += 0x20; newtime += 0x20;
time = newtime; time = newtime;
len = traverse_compressed(garmin, data, datasize, type, time); // Compressed records are like normal records
// with that added relative timestamp
DEBUG(garmin->base.context, "Compressed record for type %d", type);
if (!(garmin->type_desc + type)->msg_desc->maxfield)
DEBUG(garmin->base.context, "Ignoring timestamp field for undefined message.");
else
parse_ANY_timestamp(garmin, time);
len = traverse_regular(garmin, data, datasize, type, &time);
} else if (record & 0x40) { // Definition record? } else if (record & 0x40) { // Definition record?
len = traverse_definition(garmin, data, datasize, record); len = traverse_definition(garmin, data, datasize, record);
} else { // Normal data record } else { // Normal data record
DEBUG(garmin->base.context, "Regular record for type %d", record);
len = traverse_regular(garmin, data, datasize, record, &time); len = traverse_regular(garmin, data, datasize, record, &time);
} }
if (len <= 0 || len > datasize) if (len <= 0 || len > datasize)
@ -1471,10 +1595,8 @@ static void add_gps_string(garmin_parser_t *garmin, const char *desc, struct pos
} }
int int
garmin_parser_is_dive (dc_parser_t *abstract, const unsigned char *data, unsigned int size, dc_event_devinfo_t *devinfo_p) garmin_parser_is_dive (dc_parser_t *abstract, dc_event_devinfo_t *devinfo_p)
{ {
// set up the parser and extract data
dc_parser_set_data(abstract, data, size);
garmin_parser_t *garmin = (garmin_parser_t *) abstract; garmin_parser_t *garmin = (garmin_parser_t *) abstract;
if (devinfo_p) { if (devinfo_p) {
@ -1506,9 +1628,21 @@ static void add_sensor_string(garmin_parser_t *garmin, const char *desc, const s
} }
static dc_status_t static dc_status_t
garmin_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size) garmin_parser_set_data (garmin_parser_t *garmin, const unsigned char *data, unsigned int size)
{ {
garmin_parser_t *garmin = (garmin_parser_t *) abstract; // This list is empirical and somewhat speculative
// will have to be confirmed with Garmin
static const struct {
int id;
const char *name;
} models[] = {
{ 2859, "Descent Mk1" },
{ 2991, "Descent Mk1 APAC" },
{ 3258, "Descent Mk2(i)" },
{ 3542, "Descent Mk2s" },
{ 3702, "Descent Mk2 APAC" },
{ 4223, "Descent Mk3" },
};
/* Walk the data once without a callback to set up the core fields */ /* Walk the data once without a callback to set up the core fields */
garmin->callback = NULL; garmin->callback = NULL;
@ -1520,9 +1654,22 @@ garmin_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsign
traverse_data(garmin); traverse_data(garmin);
// Device information // Device information
dc_field_add_string_fmt(&garmin->cache, "Serial", "%u", garmin->dive.serial); if (garmin->dive.serial)
dc_field_add_string_fmt(&garmin->cache, "Firmware", "%u.%02u", dc_field_add_string_fmt(&garmin->cache, "Serial", "%u", garmin->dive.serial);
garmin->dive.firmware / 100, garmin->dive.firmware % 100); if (garmin->dive.firmware)
dc_field_add_string_fmt(&garmin->cache, "Firmware", "%u.%02u",
garmin->dive.firmware / 100, garmin->dive.firmware % 100);
if (garmin->dive.product) {
int i = 0;
for (i = 0; i < C_ARRAY_SIZE(models); i++)
if (models[i].id == garmin->dive.product)
break;
if (i < C_ARRAY_SIZE(models))
dc_field_add_string_fmt(&garmin->cache, "Model", "%s", models[i].name);
else
dc_field_add_string_fmt(&garmin->cache, "Model", "Unknown model ID: %u", garmin->dive.product);
}
// These seem to be the "real" GPS dive coordinates // These seem to be the "real" GPS dive coordinates
add_gps_string(garmin, "GPS1", &garmin->gps.SESSION.entry); add_gps_string(garmin, "GPS1", &garmin->gps.SESSION.entry);
@ -1549,10 +1696,8 @@ garmin_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsign
// //
// There's no way to match them up unless they are an identity // There's no way to match them up unless they are an identity
// mapping, so having two different ones doesn't actually work. // mapping, so having two different ones doesn't actually work.
if (garmin->dive.nr_sensor > garmin->cache.GASMIX_COUNT) { if (garmin->dive.nr_sensor > garmin->cache.TANK_COUNT)
DC_ASSIGN_FIELD(garmin->cache, GASMIX_COUNT, garmin->dive.nr_sensor); DC_ASSIGN_FIELD(garmin->cache, TANK_COUNT, garmin->dive.nr_sensor);
garmin->cache.initialized |= 1 << DC_FIELD_TANK_COUNT;
}
for (int i = 0; i < garmin->dive.nr_sensor; i++) { for (int i = 0; i < garmin->dive.nr_sensor; i++) {
static const char *name[] = { "Sensor 1", "Sensor 2", "Sensor 3", "Sensor 4", "Sensor 5" }; static const char *name[] = { "Sensor 1", "Sensor 2", "Sensor 3", "Sensor 4", "Sensor 5" };
@ -1586,10 +1731,22 @@ garmin_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime)
{ {
garmin_parser_t *garmin = (garmin_parser_t *) abstract; garmin_parser_t *garmin = (garmin_parser_t *) abstract;
dc_ticks_t time = 631065600 + (dc_ticks_t) garmin->dive.time; dc_ticks_t time = 631065600 + (dc_ticks_t) garmin->dive.time;
int timezone = DC_TIMEZONE_NONE;
// Show local time (time_offset) // Show local time (time_offset)
dc_datetime_gmtime(datetime, time + garmin->dive.time_offset); dc_datetime_gmtime(datetime, time + garmin->dive.time_offset);
datetime->timezone = DC_TIMEZONE_NONE;
/* See if we might have a valid timezone offset */
if (garmin->dive.time_offset || garmin->dive.utc_offset) {
int offset = garmin->dive.time_offset - garmin->dive.utc_offset;
/* 15-minute (900-second) offsets are real */
if ((offset % 900) == 0 &&
offset >= -12*60*60 &&
offset <= 14*60*60)
timezone = offset;
}
datetime->timezone = timezone;
return DC_STATUS_SUCCESS; return DC_STATUS_SUCCESS;
} }

406
src/hdlc.c Normal file
View File

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

50
src/hdlc.h Normal file
View File

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

View File

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

View File

@ -30,6 +30,7 @@
#include "array.h" #include "array.h"
#include "aes.h" #include "aes.h"
#include "platform.h" #include "platform.h"
#include "packet.h"
#define ISINSTANCE(device) dc_device_isinstance((device), &hw_ostc3_device_vtable) #define ISINSTANCE(device) dc_device_isinstance((device), &hw_ostc3_device_vtable)
@ -119,9 +120,6 @@ typedef struct hw_ostc3_device_t {
unsigned int firmware; unsigned int firmware;
unsigned char fingerprint[5]; unsigned char fingerprint[5];
hw_ostc3_state_t state; hw_ostc3_state_t state;
unsigned char cache[244];
unsigned int available;
unsigned int offset;
} hw_ostc3_device_t; } hw_ostc3_device_t;
typedef struct hw_ostc3_logbook_t { typedef struct hw_ostc3_logbook_t {
@ -207,41 +205,20 @@ static dc_status_t
hw_ostc3_read (hw_ostc3_device_t *device, dc_event_progress_t *progress, unsigned char data[], size_t size) hw_ostc3_read (hw_ostc3_device_t *device, dc_event_progress_t *progress, unsigned char data[], size_t size)
{ {
dc_status_t rc = DC_STATUS_SUCCESS; dc_status_t rc = DC_STATUS_SUCCESS;
dc_transport_t transport = dc_iostream_get_transport(device->iostream);
size_t nbytes = 0; size_t nbytes = 0;
while (nbytes < size) { while (nbytes < size) {
if (transport == DC_TRANSPORT_BLE) {
if (device->available == 0) {
// Read a packet into the cache.
size_t len = 0;
rc = dc_iostream_read (device->iostream, device->cache, sizeof(device->cache), &len);
if (rc != DC_STATUS_SUCCESS)
return rc;
device->available = len;
device->offset = 0;
}
}
// Set the minimum packet size. // Set the minimum packet size.
size_t length = (transport == DC_TRANSPORT_BLE) ? device->available : 1024; size_t length = 1024;
// Limit the packet size to the total size. // Limit the packet size to the total size.
if (nbytes + length > size) if (nbytes + length > size)
length = size - nbytes; length = size - nbytes;
if (transport == DC_TRANSPORT_BLE) { // Read the packet.
// Copy the data from the cached packet. rc = dc_iostream_read (device->iostream, data + nbytes, length, NULL);
memcpy (data + nbytes, device->cache + device->offset, length); if (rc != DC_STATUS_SUCCESS)
device->available -= length; return rc;
device->offset += length;
} else {
// Read the packet.
rc = dc_iostream_read (device->iostream, data + nbytes, length, NULL);
if (rc != DC_STATUS_SUCCESS)
return rc;
}
// Update and emit a progress event. // Update and emit a progress event.
if (progress) { if (progress) {
@ -259,12 +236,11 @@ static dc_status_t
hw_ostc3_write (hw_ostc3_device_t *device, dc_event_progress_t *progress, const unsigned char data[], size_t size) hw_ostc3_write (hw_ostc3_device_t *device, dc_event_progress_t *progress, const unsigned char data[], size_t size)
{ {
dc_status_t rc = DC_STATUS_SUCCESS; dc_status_t rc = DC_STATUS_SUCCESS;
dc_transport_t transport = dc_iostream_get_transport(device->iostream);
size_t nbytes = 0; size_t nbytes = 0;
while (nbytes < size) { while (nbytes < size) {
// Set the maximum packet size. // Set the maximum packet size.
size_t length = (transport == DC_TRANSPORT_BLE) ? sizeof(device->cache) : 64; size_t length = (device->hardware == OSTC4) ? 64 : 1024;
// Limit the packet size to the total size. // Limit the packet size to the total size.
if (nbytes + length > size) if (nbytes + length > size)
@ -313,7 +289,7 @@ hw_ostc3_transfer (hw_ostc3_device_t *device,
// Send the command. // Send the command.
unsigned char command[1] = {cmd}; unsigned char command[1] = {cmd};
status = hw_ostc3_write (device, NULL, command, sizeof (command)); status = dc_iostream_write (device->iostream, command, sizeof (command), NULL);
if (status != DC_STATUS_SUCCESS) { if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to send the command."); ERROR (abstract->context, "Failed to send the command.");
return status; return status;
@ -321,7 +297,7 @@ hw_ostc3_transfer (hw_ostc3_device_t *device,
// Read the echo. // Read the echo.
unsigned char echo[1] = {0}; unsigned char echo[1] = {0};
status = hw_ostc3_read (device, NULL, echo, sizeof (echo)); status = dc_iostream_read (device->iostream, echo, sizeof (echo), NULL);
if (status != DC_STATUS_SUCCESS) { if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to receive the echo."); ERROR (abstract->context, "Failed to receive the echo.");
return status; return status;
@ -407,14 +383,14 @@ hw_ostc3_transfer (hw_ostc3_device_t *device,
} }
} }
if (delay && device->available == 0) { if (delay) {
dc_iostream_poll (device->iostream, delay); dc_iostream_poll (device->iostream, delay);
} }
if (cmd != EXIT) { if (cmd != EXIT) {
// Read the ready byte. // Read the ready byte.
unsigned char answer[1] = {0}; unsigned char answer[1] = {0};
status = hw_ostc3_read (device, NULL, answer, sizeof (answer)); status = dc_iostream_read (device->iostream, answer, sizeof (answer), NULL);
if (status != DC_STATUS_SUCCESS) { if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to receive the ready byte."); ERROR (abstract->context, "Failed to receive the ready byte.");
return status; return status;
@ -439,6 +415,7 @@ hw_ostc3_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t *i
{ {
dc_status_t status = DC_STATUS_SUCCESS; dc_status_t status = DC_STATUS_SUCCESS;
hw_ostc3_device_t *device = NULL; hw_ostc3_device_t *device = NULL;
dc_transport_t transport = dc_iostream_get_transport (iostream);
if (out == NULL) if (out == NULL)
return DC_STATUS_INVALIDARGS; return DC_STATUS_INVALIDARGS;
@ -451,29 +428,36 @@ hw_ostc3_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t *i
} }
// Set the default values. // Set the default values.
device->iostream = iostream;
device->hardware = INVALID; device->hardware = INVALID;
device->feature = 0; device->feature = 0;
device->model = 0; device->model = 0;
device->serial = 0; device->serial = 0;
device->firmware = 0; device->firmware = 0;
memset (device->fingerprint, 0, sizeof (device->fingerprint)); memset (device->fingerprint, 0, sizeof (device->fingerprint));
memset (device->cache, 0, sizeof (device->cache));
device->available = 0; // Create the packet stream.
device->offset = 0; if (transport == DC_TRANSPORT_BLE) {
status = dc_packet_open (&device->iostream, context, iostream, 244, 20);
if (status != DC_STATUS_SUCCESS) {
ERROR (context, "Failed to create the packet stream.");
goto error_free;
}
} else {
device->iostream = iostream;
}
// Set the serial communication protocol (115200 8N1). // Set the serial communication protocol (115200 8N1).
status = dc_iostream_configure (device->iostream, 115200, 8, DC_PARITY_NONE, DC_STOPBITS_ONE, DC_FLOWCONTROL_NONE); status = dc_iostream_configure (device->iostream, 115200, 8, DC_PARITY_NONE, DC_STOPBITS_ONE, DC_FLOWCONTROL_NONE);
if (status != DC_STATUS_SUCCESS) { if (status != DC_STATUS_SUCCESS) {
ERROR (context, "Failed to set the terminal attributes."); ERROR (context, "Failed to set the terminal attributes.");
goto error_free; goto error_free_iostream;
} }
// Set the timeout for receiving data (3000ms). // Set the timeout for receiving data (3000ms).
status = dc_iostream_set_timeout (device->iostream, 3000); status = dc_iostream_set_timeout (device->iostream, 3000);
if (status != DC_STATUS_SUCCESS) { if (status != DC_STATUS_SUCCESS) {
ERROR (context, "Failed to set the timeout."); ERROR (context, "Failed to set the timeout.");
goto error_free; goto error_free_iostream;
} }
// Make sure everything is in a sane state. // Make sure everything is in a sane state.
@ -486,6 +470,10 @@ hw_ostc3_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t *i
return DC_STATUS_SUCCESS; return DC_STATUS_SUCCESS;
error_free_iostream:
if (transport == DC_TRANSPORT_BLE) {
dc_iostream_close (device->iostream);
}
error_free: error_free:
dc_device_deallocate ((dc_device_t *) device); dc_device_deallocate ((dc_device_t *) device);
return status; return status;
@ -548,14 +536,14 @@ hw_ostc3_device_init_service (hw_ostc3_device_t *device)
unsigned char answer[5] = {0}; unsigned char answer[5] = {0};
// Send the command and service key. // Send the command and service key.
status = hw_ostc3_write (device, NULL, command, sizeof (command)); status = dc_iostream_write (device->iostream, command, sizeof (command), NULL);
if (status != DC_STATUS_SUCCESS) { if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to send the command."); ERROR (abstract->context, "Failed to send the command.");
return status; return status;
} }
// Read the response. // Read the response.
status = hw_ostc3_read (device, NULL, answer, sizeof (answer)); status = dc_iostream_read (device->iostream, answer, sizeof (answer), NULL);
if (status != DC_STATUS_SUCCESS) { if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to receive the answer."); ERROR (abstract->context, "Failed to receive the answer.");
return status; return status;
@ -656,6 +644,15 @@ hw_ostc3_device_close (dc_device_t *abstract)
} }
} }
// Close the packet stream.
if (dc_iostream_get_transport (device->iostream) == DC_TRANSPORT_BLE) {
rc = dc_iostream_close (device->iostream);
if (rc != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to close the packet stream.");
dc_status_set_error(&status, rc);
}
}
return status; return status;
} }
@ -1506,7 +1503,7 @@ hw_ostc3_device_fwupdate3 (dc_device_t *abstract, const char *filename)
} }
static dc_status_t static dc_status_t
hw_ostc3_device_fwupdate4 (dc_device_t *abstract, const char *filename) hw_ostc3_device_fwupdate4 (dc_device_t *abstract, const char *filename, bool forceUpdate)
{ {
dc_status_t status = DC_STATUS_SUCCESS; dc_status_t status = DC_STATUS_SUCCESS;
hw_ostc3_device_t *device = (hw_ostc3_device_t *) abstract; hw_ostc3_device_t *device = (hw_ostc3_device_t *) abstract;
@ -1577,7 +1574,7 @@ hw_ostc3_device_fwupdate4 (dc_device_t *abstract, const char *filename)
// Upload the firmware blob. // Upload the firmware blob.
// The update is skipped if the two versions are already // The update is skipped if the two versions are already
// identical, or if the blob is not present on the device. // identical, or if the blob is not present on the device.
if (memcmp(data + offset + 12, fwinfo, sizeof(fwinfo)) != 0 && if ((memcmp(data + offset + 12, fwinfo, sizeof(fwinfo)) != 0 || forceUpdate) &&
!array_isequal(fwinfo, sizeof(fwinfo), 0xFF)) !array_isequal(fwinfo, sizeof(fwinfo), 0xFF))
{ {
status = hw_ostc3_transfer (device, &progress, S_UPLOAD, status = hw_ostc3_transfer (device, &progress, S_UPLOAD,
@ -1600,7 +1597,7 @@ error:
} }
dc_status_t dc_status_t
hw_ostc3_device_fwupdate (dc_device_t *abstract, const char *filename) hw_ostc3_device_fwupdate (dc_device_t *abstract, const char *filename, bool forceUpdate)
{ {
dc_status_t status = DC_STATUS_SUCCESS; dc_status_t status = DC_STATUS_SUCCESS;
hw_ostc3_device_t *device = (hw_ostc3_device_t *) abstract; hw_ostc3_device_t *device = (hw_ostc3_device_t *) abstract;
@ -1615,8 +1612,12 @@ hw_ostc3_device_fwupdate (dc_device_t *abstract, const char *filename)
} }
if (device->hardware == OSTC4) { if (device->hardware == OSTC4) {
return hw_ostc3_device_fwupdate4 (abstract, filename); return hw_ostc3_device_fwupdate4 (abstract, filename, forceUpdate);
} else { } else {
if (forceUpdate) {
return DC_STATUS_INVALIDARGS;
}
return hw_ostc3_device_fwupdate3 (abstract, filename); return hw_ostc3_device_fwupdate3 (abstract, filename);
} }
} }
@ -1718,6 +1719,10 @@ hw_ostc3_device_dump (dc_device_t *abstract, dc_buffer_t *buffer)
return rc; return rc;
} }
if (device->hardware == OSTC4) {
return DC_STATUS_UNSUPPORTED;
}
// Emit a device info event. // Emit a device info event.
dc_event_devinfo_t devinfo; dc_event_devinfo_t devinfo;
devinfo.firmware = device->firmware; 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); hw_ostc3_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream);
dc_status_t dc_status_t
hw_ostc3_parser_create (dc_parser_t **out, dc_context_t *context, unsigned int serial, 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, unsigned int serial);
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@ -39,13 +39,10 @@
#define MAXCONFIG 7 #define MAXCONFIG 7
#define NGASMIXES 15 #define NGASMIXES 15
#define OSTC4_CC_DILUENT_GAS_OFFSET 5
#define UNDEFINED 0xFFFFFFFF #define UNDEFINED 0xFFFFFFFF
#define ALL 0
#define FIXED 1
#define MANUAL 2
#define HEADER 1 #define HEADER 1
#define PROFILE 2 #define PROFILE 2
@ -116,10 +113,12 @@ typedef struct hw_ostc_layout_t {
} hw_ostc_layout_t; } hw_ostc_layout_t;
typedef struct hw_ostc_gasmix_t { typedef struct hw_ostc_gasmix_t {
unsigned int id;
unsigned int oxygen; unsigned int oxygen;
unsigned int helium; unsigned int helium;
unsigned int type; unsigned int type;
unsigned int enabled; unsigned int enabled;
unsigned int active;
unsigned int diluent; unsigned int diluent;
} hw_ostc_gasmix_t; } hw_ostc_gasmix_t;
@ -135,21 +134,23 @@ typedef struct hw_ostc_parser_t {
const hw_ostc_layout_t *layout; const hw_ostc_layout_t *layout;
unsigned int ngasmixes; unsigned int ngasmixes;
unsigned int nfixed; unsigned int nfixed;
unsigned int ndisabled;
unsigned int initial; unsigned int initial;
unsigned int initial_setpoint; unsigned int initial_setpoint;
unsigned int initial_cns; unsigned int initial_cns;
hw_ostc_gasmix_t gasmix[NGASMIXES]; hw_ostc_gasmix_t gasmix[NGASMIXES];
unsigned int current_divemode_ccr;
} hw_ostc_parser_t; } hw_ostc_parser_t;
static dc_status_t hw_ostc_parser_set_data (dc_parser_t *abstract, const unsigned char *data, unsigned int size);
static dc_status_t hw_ostc_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime); static dc_status_t hw_ostc_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime);
static dc_status_t hw_ostc_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value); static dc_status_t hw_ostc_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value);
static dc_status_t hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata); static dc_status_t hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata);
static dc_status_t hw_ostc_parser_internal_foreach (hw_ostc_parser_t *parser, dc_sample_callback_t callback, void *userdata);
static const dc_parser_vtable_t hw_ostc_parser_vtable = { static const dc_parser_vtable_t hw_ostc_parser_vtable = {
sizeof(hw_ostc_parser_t), sizeof(hw_ostc_parser_t),
DC_FAMILY_HW_OSTC, DC_FAMILY_HW_OSTC,
hw_ostc_parser_set_data, /* set_data */
NULL, /* set_clock */ NULL, /* set_clock */
NULL, /* set_atmospheric */ NULL, /* set_atmospheric */
NULL, /* set_density */ NULL, /* set_density */
@ -214,15 +215,10 @@ static const hw_ostc_layout_t hw_ostc_layout_ostc3 = {
}; };
static unsigned int 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; unsigned int count = parser->ngasmixes;
if (type == FIXED) {
count = parser->nfixed;
} else if (type == MANUAL) {
offset = parser->nfixed;
}
unsigned int i = offset; unsigned int i = offset;
while (i < count) { while (i < count) {
@ -234,6 +230,22 @@ hw_ostc_find_gasmix (hw_ostc_parser_t *parser, unsigned int o2, unsigned int he,
return i; return i;
} }
static unsigned int
hw_ostc_find_gasmix_fixed (hw_ostc_parser_t *parser, unsigned int id)
{
unsigned int offset = 0;
unsigned int count = parser->nfixed - parser->ndisabled;
unsigned int i = offset;
while (i < count) {
if (id == parser->gasmix[i].id)
break;
i++;
}
return i;
}
static unsigned int static unsigned int
hw_ostc_is_ccr (unsigned int divemode, unsigned int version) hw_ostc_is_ccr (unsigned int divemode, unsigned int version)
{ {
@ -315,19 +327,23 @@ hw_ostc_parser_cache (hw_ostc_parser_t *parser)
initial = data[31]; initial = data[31];
} }
for (unsigned int i = 0; i < ngasmixes; ++i) { for (unsigned int i = 0; i < ngasmixes; ++i) {
gasmix[i].id = i + 1;
gasmix[i].oxygen = data[25 + 2 * i]; gasmix[i].oxygen = data[25 + 2 * i];
gasmix[i].helium = 0; gasmix[i].helium = 0;
gasmix[i].type = 0; gasmix[i].type = 0;
gasmix[i].enabled = 1; gasmix[i].enabled = 1;
gasmix[i].active = 0;
gasmix[i].diluent = 0; gasmix[i].diluent = 0;
} }
} else if (version == 0x23 || version == 0x24) { } else if (version == 0x23 || version == 0x24) {
ngasmixes = 5; ngasmixes = 5;
for (unsigned int i = 0; i < ngasmixes; ++i) { for (unsigned int i = 0; i < ngasmixes; ++i) {
gasmix[i].id = i + 1;
gasmix[i].oxygen = data[28 + 4 * i + 0]; gasmix[i].oxygen = data[28 + 4 * i + 0];
gasmix[i].helium = data[28 + 4 * i + 1]; gasmix[i].helium = data[28 + 4 * i + 1];
gasmix[i].type = data[28 + 4 * i + 3]; gasmix[i].type = data[28 + 4 * i + 3];
gasmix[i].enabled = gasmix[i].type != 0; gasmix[i].enabled = gasmix[i].type != 0;
gasmix[i].active = 0;
gasmix[i].diluent = ccr; gasmix[i].diluent = ccr;
// Find the first gas marked as the initial gas. // Find the first gas marked as the initial gas.
if (initial == UNDEFINED && data[28 + 4 * i + 3] == 1) { if (initial == UNDEFINED && data[28 + 4 * i + 3] == 1) {
@ -346,6 +362,7 @@ hw_ostc_parser_cache (hw_ostc_parser_t *parser)
initial = data[31]; initial = data[31];
} }
for (unsigned int i = 0; i < ngasmixes; ++i) { for (unsigned int i = 0; i < ngasmixes; ++i) {
gasmix[i].id = i + 1;
gasmix[i].oxygen = data[19 + 2 * i + 0]; gasmix[i].oxygen = data[19 + 2 * i + 0];
gasmix[i].helium = data[19 + 2 * i + 1]; gasmix[i].helium = data[19 + 2 * i + 1];
gasmix[i].type = 0; gasmix[i].type = 0;
@ -354,6 +371,7 @@ hw_ostc_parser_cache (hw_ostc_parser_t *parser)
} else { } else {
gasmix[i].enabled = 1; gasmix[i].enabled = 1;
} }
gasmix[i].active = 0;
gasmix[i].diluent = ccr; gasmix[i].diluent = ccr;
} }
} }
@ -362,7 +380,6 @@ hw_ostc_parser_cache (hw_ostc_parser_t *parser)
ERROR(abstract->context, "Invalid initial gas mix."); ERROR(abstract->context, "Invalid initial gas mix.");
return DC_STATUS_DATAFORMAT; return DC_STATUS_DATAFORMAT;
} }
initial--; /* Convert to a zero based index. */
} else { } else {
WARNING(abstract->context, "No initial gas mix available."); WARNING(abstract->context, "No initial gas mix available.");
} }
@ -373,6 +390,7 @@ hw_ostc_parser_cache (hw_ostc_parser_t *parser)
parser->layout = layout; parser->layout = layout;
parser->ngasmixes = ngasmixes; parser->ngasmixes = ngasmixes;
parser->nfixed = ngasmixes; parser->nfixed = ngasmixes;
parser->ndisabled = 0;
parser->initial = initial; parser->initial = initial;
parser->initial_setpoint = initial_setpoint; parser->initial_setpoint = initial_setpoint;
parser->initial_cns = initial_cns; parser->initial_cns = initial_cns;
@ -385,7 +403,7 @@ hw_ostc_parser_cache (hw_ostc_parser_t *parser)
} }
static dc_status_t static dc_status_t
hw_ostc_parser_create_internal (dc_parser_t **out, dc_context_t *context, unsigned int serial, 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, unsigned int serial)
{ {
hw_ostc_parser_t *parser = NULL; hw_ostc_parser_t *parser = NULL;
@ -393,7 +411,7 @@ hw_ostc_parser_create_internal (dc_parser_t **out, dc_context_t *context, unsign
return DC_STATUS_INVALIDARGS; return DC_STATUS_INVALIDARGS;
// Allocate memory. // Allocate memory.
parser = (hw_ostc_parser_t *) dc_parser_allocate (context, &hw_ostc_parser_vtable); parser = (hw_ostc_parser_t *) dc_parser_allocate (context, &hw_ostc_parser_vtable, data, size);
if (parser == NULL) { if (parser == NULL) {
ERROR (context, "Failed to allocate memory."); ERROR (context, "Failed to allocate memory.");
return DC_STATUS_NOMEMORY; return DC_STATUS_NOMEMORY;
@ -408,14 +426,17 @@ hw_ostc_parser_create_internal (dc_parser_t **out, dc_context_t *context, unsign
parser->layout = NULL; parser->layout = NULL;
parser->ngasmixes = 0; parser->ngasmixes = 0;
parser->nfixed = 0; parser->nfixed = 0;
parser->ndisabled = 0;
parser->initial = 0; parser->initial = 0;
parser->initial_setpoint = 0; parser->initial_setpoint = 0;
parser->initial_cns = 0; parser->initial_cns = 0;
for (unsigned int i = 0; i < NGASMIXES; ++i) { for (unsigned int i = 0; i < NGASMIXES; ++i) {
parser->gasmix[i].id = 0;
parser->gasmix[i].oxygen = 0; parser->gasmix[i].oxygen = 0;
parser->gasmix[i].helium = 0; parser->gasmix[i].helium = 0;
parser->gasmix[i].type = 0; parser->gasmix[i].type = 0;
parser->gasmix[i].enabled = 0; parser->gasmix[i].enabled = 0;
parser->gasmix[i].active = 0;
parser->gasmix[i].diluent = 0; parser->gasmix[i].diluent = 0;
} }
parser->serial = serial; parser->serial = serial;
@ -427,44 +448,17 @@ hw_ostc_parser_create_internal (dc_parser_t **out, dc_context_t *context, unsign
dc_status_t dc_status_t
hw_ostc_parser_create (dc_parser_t **out, dc_context_t *context, unsigned int serial) hw_ostc_parser_create (dc_parser_t **out, dc_context_t *context, const unsigned char data[], size_t size, unsigned int serial)
{ {
return hw_ostc_parser_create_internal (out, context, serial, 0, 0); return hw_ostc_parser_create_internal (out, context, data, size, 0, 0, serial);
} }
dc_status_t dc_status_t
hw_ostc3_parser_create (dc_parser_t **out, dc_context_t *context, unsigned int serial, 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, unsigned int serial)
{ {
return hw_ostc_parser_create_internal (out, context, serial, 1, model); return hw_ostc_parser_create_internal (out, context, data, size, 1, model, serial);
} }
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 static dc_status_t
hw_ostc_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime) hw_ostc_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime)
{ {
@ -525,7 +519,7 @@ hw_ostc_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime)
return DC_STATUS_SUCCESS; return DC_STATUS_SUCCESS;
} }
#define BUFLEN 32 #define BUFLEN 64
static dc_status_t static dc_status_t
hw_ostc_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value) hw_ostc_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned int flags, void *value)
@ -540,7 +534,7 @@ hw_ostc_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned
// Cache the profile data. // Cache the profile data.
if (parser->cached < PROFILE) { if (parser->cached < PROFILE) {
rc = hw_ostc_parser_samples_foreach (abstract, NULL, NULL); rc = hw_ostc_parser_internal_foreach (parser, NULL, NULL);
if (rc != DC_STATUS_SUCCESS) if (rc != DC_STATUS_SUCCESS)
return rc; return rc;
} }
@ -571,13 +565,34 @@ hw_ostc_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned
return DC_STATUS_UNSUPPORTED; return DC_STATUS_UNSUPPORTED;
*((double *) value) = array_uint16_le (data + layout->avgdepth) / 100.0; *((double *) value) = array_uint16_le (data + layout->avgdepth) / 100.0;
break; break;
case DC_FIELD_TANK_COUNT:
case DC_FIELD_GASMIX_COUNT: case DC_FIELD_GASMIX_COUNT:
*((unsigned int *) value) = parser->ngasmixes; *((unsigned int *) value) = parser->ngasmixes;
break; break;
case DC_FIELD_GASMIX: case DC_FIELD_GASMIX:
if (flags >= parser->ngasmixes) {
return DC_STATUS_UNSUPPORTED;
}
gasmix->usage = parser->gasmix[flags].diluent ?
DC_USAGE_DILUENT : DC_USAGE_OPEN_CIRCUIT;
gasmix->oxygen = parser->gasmix[flags].oxygen / 100.0; gasmix->oxygen = parser->gasmix[flags].oxygen / 100.0;
gasmix->helium = parser->gasmix[flags].helium / 100.0; gasmix->helium = parser->gasmix[flags].helium / 100.0;
gasmix->nitrogen = 1.0 - gasmix->oxygen - gasmix->helium; gasmix->nitrogen = 1.0 - gasmix->oxygen - gasmix->helium;
break;
case DC_FIELD_TANK:
if (flags >= parser->ngasmixes || !parser->gasmix[flags].enabled) {
return DC_STATUS_UNSUPPORTED;
}
dc_tank_t *tank = (dc_tank_t *) value;
tank->volume = 0.0;
tank->gasmix = flags;
tank->workpressure = 0.0;
tank->type = DC_TANKINFO_METRIC;
break; break;
case DC_FIELD_SALINITY: case DC_FIELD_SALINITY:
if (salinity < 100 || salinity > 104) if (salinity < 100 || salinity > 104)
@ -800,18 +815,34 @@ hw_ostc_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsigned
} }
static dc_status_t static void hw_ostc_notify_bailout(hw_ostc_parser_t *parser, const unsigned char *data, unsigned int index, dc_sample_callback_t callback, void *userdata)
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; if (parser->current_divemode_ccr != parser->gasmix[index].diluent) {
dc_sample_value_t sample = {
.event.type = SAMPLE_EVENT_STRING,
.event.flags = SAMPLE_FLAGS_SEVERITY_INFO,
};
if (parser->gasmix[index].diluent) {
sample.event.name = "Switched to closed circuit";
} else {
sample.event.name = "Switched to open circuit bailout";
}
if (callback) {
callback(DC_SAMPLE_EVENT, &sample, userdata);
}
parser->current_divemode_ccr = parser->gasmix[index].diluent;
}
}
static dc_status_t
hw_ostc_parser_internal_foreach (hw_ostc_parser_t *parser, dc_sample_callback_t callback, void *userdata)
{
dc_parser_t *abstract = (dc_parser_t *) parser;
const unsigned char *data = abstract->data; const unsigned char *data = abstract->data;
unsigned int size = abstract->size; unsigned int size = abstract->size;
// Cache the parser data.
dc_status_t rc = hw_ostc_parser_cache (parser);
if (rc != DC_STATUS_SUCCESS)
return rc;
unsigned int version = parser->version; unsigned int version = parser->version;
unsigned int header = parser->header; unsigned int header = parser->header;
const hw_ostc_layout_t *layout = parser->layout; const hw_ostc_layout_t *layout = parser->layout;
@ -914,10 +945,11 @@ hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t call
// Get the CCR mode. // Get the CCR mode.
unsigned int ccr = hw_ostc_is_ccr (divemode, version); unsigned int ccr = hw_ostc_is_ccr (divemode, version);
parser->current_divemode_ccr = ccr;
unsigned int time = 0; unsigned int time = 0;
unsigned int nsamples = 0; unsigned int nsamples = 0;
unsigned int tank = parser->initial != UNDEFINED ? parser->initial : 0; unsigned int tank = parser->initial != UNDEFINED ? parser->initial - 1 : 0;
unsigned int offset = header; unsigned int offset = header;
if (version == 0x23 || version == 0x24) if (version == 0x23 || version == 0x24)
@ -929,31 +961,33 @@ hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t call
// Time (seconds). // Time (seconds).
time += samplerate; time += samplerate;
sample.time = time; sample.time = time * 1000;
if (callback) callback (DC_SAMPLE_TIME, sample, userdata); if (callback) callback (DC_SAMPLE_TIME, &sample, userdata);
// Initial gas mix. // Initial gas mix.
if (time == samplerate && parser->initial != UNDEFINED) { if (time == samplerate && parser->initial != UNDEFINED) {
sample.gasmix = parser->initial; unsigned int idx = hw_ostc_find_gasmix_fixed (parser, parser->initial);
if (callback) callback (DC_SAMPLE_GASMIX, sample, userdata); parser->gasmix[idx].active = 1;
sample.gasmix = idx;
if (callback) callback (DC_SAMPLE_GASMIX, &sample, userdata);
} }
// Initial setpoint (mbar). // Initial setpoint (mbar).
if (time == samplerate && parser->initial_setpoint != UNDEFINED) { if (time == samplerate && parser->initial_setpoint != UNDEFINED) {
sample.setpoint = parser->initial_setpoint / 100.0; sample.setpoint = parser->initial_setpoint / 100.0;
if (callback) callback (DC_SAMPLE_SETPOINT, sample, userdata); if (callback) callback (DC_SAMPLE_SETPOINT, &sample, userdata);
} }
// Initial CNS (%). // Initial CNS (%).
if (time == samplerate && parser->initial_cns != UNDEFINED) { if (time == samplerate && parser->initial_cns != UNDEFINED) {
sample.cns = parser->initial_cns / 100.0; sample.cns = parser->initial_cns / 100.0;
if (callback) callback (DC_SAMPLE_CNS, sample, userdata); if (callback) callback (DC_SAMPLE_CNS, &sample, userdata);
} }
// Depth (1/100 m). // Depth (1/100 m).
unsigned int depth = array_uint16_le (data + offset); unsigned int depth = array_uint16_le (data + offset);
sample.depth = depth / 100.0; sample.depth = depth / 100.0;
if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata); if (callback) callback (DC_SAMPLE_DEPTH, &sample, userdata);
offset += 2; offset += 2;
// Extended sample info. // Extended sample info.
@ -1012,7 +1046,7 @@ hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t call
break; break;
} }
if (sample.event.type && callback) if (sample.event.type && callback)
callback (DC_SAMPLE_EVENT, sample, userdata); callback (DC_SAMPLE_EVENT, &sample, userdata);
// Manual Gas Set & Change // Manual Gas Set & Change
if (events & 0x10) { if (events & 0x10) {
@ -1021,23 +1055,35 @@ hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t call
return DC_STATUS_DATAFORMAT; return DC_STATUS_DATAFORMAT;
} }
unsigned int o2 = data[offset]; unsigned int o2 = data[offset];
unsigned int diluent;
if (parser->model == OSTC4) {
// all manually added gas mixes on OSTC4 are OC gases
diluent = 0;
} else {
diluent = ccr;
}
unsigned int he = data[offset + 1]; 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, diluent);
if (idx >= parser->ngasmixes) { if (idx >= parser->ngasmixes) {
if (idx >= NGASMIXES) { if (idx >= NGASMIXES) {
ERROR (abstract->context, "Maximum number of gas mixes reached."); ERROR (abstract->context, "Maximum number of gas mixes reached.");
return DC_STATUS_NOMEMORY; return DC_STATUS_NOMEMORY;
} }
parser->gasmix[idx].id = 0;
parser->gasmix[idx].oxygen = o2; parser->gasmix[idx].oxygen = o2;
parser->gasmix[idx].helium = he; parser->gasmix[idx].helium = he;
parser->gasmix[idx].type = 0; parser->gasmix[idx].type = 0;
parser->gasmix[idx].enabled = 1; parser->gasmix[idx].enabled = 1;
parser->gasmix[idx].diluent = ccr; parser->gasmix[idx].active = 1;
parser->gasmix[idx].diluent = diluent;
parser->ngasmixes = idx + 1; parser->ngasmixes = idx + 1;
} }
sample.gasmix = idx; sample.gasmix = idx;
if (callback) callback (DC_SAMPLE_GASMIX, sample, userdata); if (callback) callback (DC_SAMPLE_GASMIX, &sample, userdata);
hw_ostc_notify_bailout(parser, data, idx, callback, userdata);
offset += 2; offset += 2;
length -= 2; length -= 2;
} }
@ -1048,15 +1094,23 @@ hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t call
ERROR (abstract->context, "Buffer overflow detected!"); ERROR (abstract->context, "Buffer overflow detected!");
return DC_STATUS_DATAFORMAT; return DC_STATUS_DATAFORMAT;
} }
unsigned int idx = data[offset]; unsigned int id = data[offset];
if (idx < 1 || idx > parser->nfixed) { if (id > parser->nfixed && id <= parser->nfixed + OSTC4_CC_DILUENT_GAS_OFFSET) {
ERROR(abstract->context, "Invalid gas mix (%u).", idx); // OSTC4 reports gas changes to another diluent with an offset
id -= OSTC4_CC_DILUENT_GAS_OFFSET;
}
if (id < 1 || id > parser->nfixed) {
ERROR(abstract->context, "Invalid gas mix (%u).", id);
return DC_STATUS_DATAFORMAT; return DC_STATUS_DATAFORMAT;
} }
idx--; /* Convert to a zero based index. */ unsigned int idx = hw_ostc_find_gasmix_fixed (parser, id);
parser->gasmix[idx].active = 1;
sample.gasmix = idx; sample.gasmix = idx;
if (callback) callback (DC_SAMPLE_GASMIX, sample, userdata); if (callback) callback (DC_SAMPLE_GASMIX, &sample, userdata);
tank = idx; tank = id - 1;
hw_ostc_notify_bailout(parser, data, idx, callback, userdata);
offset++; offset++;
length--; length--;
} }
@ -1069,7 +1123,7 @@ hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t call
return DC_STATUS_DATAFORMAT; return DC_STATUS_DATAFORMAT;
} }
sample.setpoint = data[offset] / 100.0; sample.setpoint = data[offset] / 100.0;
if (callback) callback (DC_SAMPLE_SETPOINT, sample, userdata); if (callback) callback (DC_SAMPLE_SETPOINT, &sample, userdata);
offset++; offset++;
length--; length--;
} }
@ -1083,22 +1137,27 @@ hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t call
unsigned int o2 = data[offset]; unsigned int o2 = data[offset];
unsigned int he = data[offset + 1]; unsigned int he = data[offset + 1];
unsigned int idx = hw_ostc_find_gasmix (parser, o2, he, 0, MANUAL); unsigned int idx = hw_ostc_find_gasmix_manual (parser, o2, he, 0);
if (idx >= parser->ngasmixes) { if (idx >= parser->ngasmixes) {
if (idx >= NGASMIXES) { if (idx >= NGASMIXES) {
ERROR (abstract->context, "Maximum number of gas mixes reached."); ERROR (abstract->context, "Maximum number of gas mixes reached.");
return DC_STATUS_NOMEMORY; return DC_STATUS_NOMEMORY;
} }
parser->gasmix[idx].id = 0;
parser->gasmix[idx].oxygen = o2; parser->gasmix[idx].oxygen = o2;
parser->gasmix[idx].helium = he; parser->gasmix[idx].helium = he;
parser->gasmix[idx].type = 0; parser->gasmix[idx].type = 0;
parser->gasmix[idx].enabled = 1; parser->gasmix[idx].enabled = 1;
parser->gasmix[idx].active = 1;
parser->gasmix[idx].diluent = 0; parser->gasmix[idx].diluent = 0;
parser->ngasmixes = idx + 1; parser->ngasmixes = idx + 1;
} }
sample.gasmix = idx; sample.gasmix = idx;
if (callback) callback (DC_SAMPLE_GASMIX, sample, userdata); if (callback) callback (DC_SAMPLE_GASMIX, &sample, userdata);
hw_ostc_notify_bailout(parser, data, idx, callback, userdata);
offset += 2; offset += 2;
length -= 2; length -= 2;
} }
@ -1130,7 +1189,7 @@ hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t call
case TEMPERATURE: case TEMPERATURE:
value = array_uint16_le (data + offset); value = array_uint16_le (data + offset);
sample.temperature = value / 10.0; sample.temperature = value / 10.0;
if (callback) callback (DC_SAMPLE_TEMPERATURE, sample, userdata); if (callback) callback (DC_SAMPLE_TEMPERATURE, &sample, userdata);
break; break;
case DECO: case DECO:
// Due to a firmware bug, the deco/ndl info is incorrect for // Due to a firmware bug, the deco/ndl info is incorrect for
@ -1145,7 +1204,8 @@ hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t call
sample.deco.depth = 0.0; sample.deco.depth = 0.0;
} }
sample.deco.time = data[offset + 1] * 60; sample.deco.time = data[offset + 1] * 60;
if (callback) callback (DC_SAMPLE_DECO, sample, userdata); sample.deco.tts = 0;
if (callback) callback (DC_SAMPLE_DECO, &sample, userdata);
break; break;
case PPO2: case PPO2:
for (unsigned int j = 0; j < 3; ++j) { for (unsigned int j = 0; j < 3; ++j) {
@ -1159,8 +1219,9 @@ hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t call
} }
if (count) { if (count) {
for (unsigned int j = 0; j < 3; ++j) { for (unsigned int j = 0; j < 3; ++j) {
sample.ppo2 = ppo2[j] / 100.0; sample.ppo2.sensor = i;
if (callback) callback (DC_SAMPLE_PPO2, sample, userdata); sample.ppo2.value = ppo2[j] / 100.0;
if (callback) callback (DC_SAMPLE_PPO2, &sample, userdata);
} }
} }
break; break;
@ -1169,7 +1230,7 @@ hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t call
sample.cns = array_uint16_le (data + offset) / 100.0; sample.cns = array_uint16_le (data + offset) / 100.0;
else else
sample.cns = data[offset] / 100.0; sample.cns = data[offset] / 100.0;
if (callback) callback (DC_SAMPLE_CNS, sample, userdata); if (callback) callback (DC_SAMPLE_CNS, &sample, userdata);
break; break;
case TANK: case TANK:
value = array_uint16_le (data + offset); value = array_uint16_le (data + offset);
@ -1182,7 +1243,7 @@ hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t call
(firmware >= OSTC3FW(10,40) && firmware <= OSTC3FW(10,50))) { (firmware >= OSTC3FW(10,40) && firmware <= OSTC3FW(10,50))) {
sample.pressure.value /= 10.0; sample.pressure.value /= 10.0;
} }
if (callback) callback (DC_SAMPLE_PRESSURE, sample, userdata); if (callback) callback (DC_SAMPLE_PRESSURE, &sample, userdata);
} }
break; break;
default: // Not yet used. default: // Not yet used.
@ -1202,7 +1263,7 @@ hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t call
return DC_STATUS_DATAFORMAT; return DC_STATUS_DATAFORMAT;
} }
sample.setpoint = data[offset] / 100.0; sample.setpoint = data[offset] / 100.0;
if (callback) callback (DC_SAMPLE_SETPOINT, sample, userdata); if (callback) callback (DC_SAMPLE_SETPOINT, &sample, userdata);
offset++; offset++;
length--; length--;
} }
@ -1216,22 +1277,27 @@ hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t call
unsigned int o2 = data[offset]; unsigned int o2 = data[offset];
unsigned int he = data[offset + 1]; unsigned int he = data[offset + 1];
unsigned int idx = hw_ostc_find_gasmix (parser, o2, he, 0, MANUAL); unsigned int idx = hw_ostc_find_gasmix_manual (parser, o2, he, 0);
if (idx >= parser->ngasmixes) { if (idx >= parser->ngasmixes) {
if (idx >= NGASMIXES) { if (idx >= NGASMIXES) {
ERROR (abstract->context, "Maximum number of gas mixes reached."); ERROR (abstract->context, "Maximum number of gas mixes reached.");
return DC_STATUS_NOMEMORY; return DC_STATUS_NOMEMORY;
} }
parser->gasmix[idx].id = 0;
parser->gasmix[idx].oxygen = o2; parser->gasmix[idx].oxygen = o2;
parser->gasmix[idx].helium = he; parser->gasmix[idx].helium = he;
parser->gasmix[idx].type = 0; parser->gasmix[idx].type = 0;
parser->gasmix[idx].enabled = 1; parser->gasmix[idx].enabled = 1;
parser->gasmix[idx].active = 1;
parser->gasmix[idx].diluent = 0; parser->gasmix[idx].diluent = 0;
parser->ngasmixes = idx + 1; parser->ngasmixes = idx + 1;
} }
sample.gasmix = idx; sample.gasmix = idx;
if (callback) callback (DC_SAMPLE_GASMIX, sample, userdata); if (callback) callback (DC_SAMPLE_GASMIX, &sample, userdata);
hw_ostc_notify_bailout(parser, data, idx, callback, userdata);
offset += 2; offset += 2;
length -= 2; length -= 2;
} }
@ -1249,7 +1315,50 @@ hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t call
return DC_STATUS_DATAFORMAT; return DC_STATUS_DATAFORMAT;
} }
// Remove the disabled gas mixes from the fixed gas mixes.
unsigned int ndisabled = 0, nenabled = 0;
unsigned int count = parser->nfixed - parser->ndisabled;
for (unsigned int i = 0; i < count; ++i) {
if (parser->gasmix[i].enabled || parser->gasmix[i].active) {
// Move the fixed gas mix.
parser->gasmix[nenabled] = parser->gasmix[i];
nenabled++;
} else {
ndisabled++;
}
}
// Move all the manual gas mixes.
memmove (parser->gasmix + nenabled, parser->gasmix + count,
(parser->ngasmixes - count) * sizeof (hw_ostc_gasmix_t));
memset (parser->gasmix + parser->ngasmixes - ndisabled, 0,
ndisabled * sizeof (hw_ostc_gasmix_t));
// Adjust the counts.
parser->ngasmixes -= ndisabled;
parser->ndisabled += ndisabled;
parser->cached = PROFILE; parser->cached = PROFILE;
return DC_STATUS_SUCCESS; return DC_STATUS_SUCCESS;
} }
static dc_status_t
hw_ostc_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata)
{
hw_ostc_parser_t *parser = (hw_ostc_parser_t *) abstract;
// Cache the header data.
dc_status_t rc = hw_ostc_parser_cache (parser);
if (rc != DC_STATUS_SUCCESS)
return rc;
// Cache the profile data.
if (parser->cached < PROFILE) {
rc = hw_ostc_parser_internal_foreach (parser, NULL, NULL);
if (rc != DC_STATUS_SUCCESS)
return rc;
}
return hw_ostc_parser_internal_foreach (parser, callback, userdata);
}

View File

@ -47,7 +47,6 @@
#include "context-private.h" #include "context-private.h"
#include "iostream-private.h" #include "iostream-private.h"
#include "iterator-private.h" #include "iterator-private.h"
#include "descriptor-private.h"
#include "array.h" #include "array.h"
#include "platform.h" #include "platform.h"
@ -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", INFO (context, "Discover: address=%08x, name=%s, charset=%02x, hints=%04x",
address, name, charset, hints); address, name, charset, hints);
if (!dc_descriptor_filter (descriptor, DC_TRANSPORT_IRDA, name, NULL)) { if (!dc_descriptor_filter (descriptor, DC_TRANSPORT_IRDA, name)) {
continue; continue;
} }

68
src/libdivecomputer.rc Normal file
View File

@ -0,0 +1,68 @@
/*
* libdivecomputer
*
* Copyright (C) 2010 Jef Driesen
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <winver.h>
#include <libdivecomputer/version.h>
#ifdef HAVE_VERSION_SUFFIX
#include "revision.h"
#endif
VS_VERSION_INFO VERSIONINFO
FILEVERSION DC_VERSION_MAJOR,DC_VERSION_MINOR,DC_VERSION_MICRO,0
PRODUCTVERSION DC_VERSION_MAJOR,DC_VERSION_MINOR,DC_VERSION_MICRO,0
FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
#ifdef HAVE_VERSION_SUFFIX
FILEFLAGS VS_FF_PRERELEASE
#else
FILEFLAGS 0
#endif
FILEOS VOS__WINDOWS32
FILETYPE VFT_DLL
FILESUBTYPE 0
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904B0"
BEGIN
VALUE "CompanyName", "The libdivecomputer developers"
VALUE "FileDescription", "A library for communication with various dive computers."
VALUE "FileVersion", DC_VERSION
VALUE "InternalName", "libdivecomputer"
VALUE "LegalCopyright", "Copyright © 2010 Jef Driesen"
VALUE "OriginalFilename", "libdivecomputer.dll"
VALUE "ProductName", "libdivecomputer"
VALUE "ProductVersion", DC_VERSION
#ifdef HAVE_VERSION_SUFFIX
VALUE "Comments", DC_VERSION_REVISION
#endif
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 1033, 1200
END
END

View File

@ -1,45 +0,0 @@
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <winver.h>
#ifdef HAVE_VERSION_SUFFIX
#include "revision.h"
#endif
VS_VERSION_INFO VERSIONINFO
FILEVERSION @DC_VERSION_MAJOR@,@DC_VERSION_MINOR@,@DC_VERSION_MICRO@,0
PRODUCTVERSION @DC_VERSION_MAJOR@,@DC_VERSION_MINOR@,@DC_VERSION_MICRO@,0
FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
#ifdef HAVE_VERSION_SUFFIX
FILEFLAGS VS_FF_PRERELEASE
#else
FILEFLAGS 0
#endif
FILEOS VOS__WINDOWS32
FILETYPE VFT_DLL
FILESUBTYPE 0
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904B0"
BEGIN
VALUE "CompanyName", "The libdivecomputer developers"
VALUE "FileDescription", "A library for communication with various dive computers."
VALUE "FileVersion", "@VERSION@"
VALUE "InternalName", "libdivecomputer"
VALUE "LegalCopyright", "Copyright © 2010 Jef Driesen"
VALUE "OriginalFilename", "libdivecomputer.dll"
VALUE "ProductName", "libdivecomputer"
VALUE "ProductVersion", "@VERSION@"
#ifdef HAVE_VERSION_SUFFIX
VALUE "Comments", DC_VERSION_REVISION
#endif
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 1033, 1200
END
END

View File

@ -34,6 +34,7 @@ dc_descriptor_get_product
dc_descriptor_get_type dc_descriptor_get_type
dc_descriptor_get_model dc_descriptor_get_model
dc_descriptor_get_transports dc_descriptor_get_transports
dc_descriptor_filter
dc_iostream_get_transport dc_iostream_get_transport
dc_iostream_set_timeout dc_iostream_set_timeout
@ -93,17 +94,11 @@ dc_parser_set_clock
dc_parser_set_atmospheric dc_parser_set_atmospheric
dc_parser_set_density dc_parser_set_density
dc_parser_get_type dc_parser_get_type
dc_parser_set_data
dc_parser_get_datetime dc_parser_get_datetime
dc_parser_get_field dc_parser_get_field
dc_parser_samples_foreach dc_parser_samples_foreach
dc_parser_destroy 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_open
dc_device_close dc_device_close
dc_device_dump dc_device_dump

View File

@ -158,7 +158,7 @@ liquivision_lynx_recv (liquivision_lynx_device_t *device, unsigned char data[],
// Verify the checksum. // Verify the checksum.
unsigned short crc = array_uint16_be (packet + 1 + size); unsigned short crc = array_uint16_be (packet + 1 + size);
unsigned short ccrc = checksum_crc16_ccitt (packet + 1, size, 0xffff); unsigned short ccrc = checksum_crc16_ccitt (packet + 1, size, 0xffff, 0x0000);
if (crc != ccrc) { if (crc != ccrc) {
ERROR (abstract->context, "Unexpected answer checksum (%04x %04x).", crc, ccrc); ERROR (abstract->context, "Unexpected answer checksum (%04x %04x).", crc, ccrc);
return DC_STATUS_PROTOCOL; return DC_STATUS_PROTOCOL;
@ -528,7 +528,7 @@ liquivision_lynx_device_foreach (dc_device_t *abstract, dc_dive_callback_t callb
memcpy (header + 0, device->info + 2, 4); memcpy (header + 0, device->info + 2, 4);
memcpy (header + 4, logbook + offset + 4, headersize - 4); memcpy (header + 4, logbook + offset + 4, headersize - 4);
unsigned int crc = array_uint32_le (logbook + offset + 0); unsigned int crc = array_uint32_le (logbook + offset + 0);
unsigned int ccrc = checksum_crc32b (header, headersize - unused); unsigned int ccrc = checksum_crc32 (header, headersize - unused);
if (crc != ccrc) { if (crc != ccrc) {
WARNING (abstract->context, "Invalid dive checksum (%08x %08x)", crc, ccrc); WARNING (abstract->context, "Invalid dive checksum (%08x %08x)", crc, ccrc);
status = DC_STATUS_DATAFORMAT; status = DC_STATUS_DATAFORMAT;

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); liquivision_lynx_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream);
dc_status_t 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 #ifdef __cplusplus
} }

View File

@ -117,7 +117,6 @@ struct liquivision_lynx_parser_t {
liquivision_lynx_tank_t tank[NTANKS]; 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_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_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); 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 = { static const dc_parser_vtable_t liquivision_lynx_parser_vtable = {
sizeof(liquivision_lynx_parser_t), sizeof(liquivision_lynx_parser_t),
DC_FAMILY_LIQUIVISION_LYNX, DC_FAMILY_LIQUIVISION_LYNX,
liquivision_lynx_parser_set_data, /* set_data */
NULL, /* set_clock */ NULL, /* set_clock */
NULL, /* set_atmospheric */ NULL, /* set_atmospheric */
NULL, /* set_density */ NULL, /* set_density */
@ -137,7 +135,7 @@ static const dc_parser_vtable_t liquivision_lynx_parser_vtable = {
dc_status_t 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; 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; return DC_STATUS_INVALIDARGS;
// Allocate memory. // 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) { if (parser == NULL) {
ERROR (context, "Failed to allocate memory."); ERROR (context, "Failed to allocate memory.");
return DC_STATUS_NOMEMORY; 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 static dc_status_t
liquivision_lynx_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime) 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; *((unsigned int *) value) = parser->ngasmixes;
break; break;
case DC_FIELD_GASMIX: case DC_FIELD_GASMIX:
gasmix->usage = DC_USAGE_NONE;
gasmix->helium = parser->gasmix[flags].helium / 100.0; gasmix->helium = parser->gasmix[flags].helium / 100.0;
gasmix->oxygen = parser->gasmix[flags].oxygen / 100.0; gasmix->oxygen = parser->gasmix[flags].oxygen / 100.0;
gasmix->nitrogen = 1.0 - gasmix->oxygen - gasmix->helium; gasmix->nitrogen = 1.0 - gasmix->oxygen - gasmix->helium;
@ -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->beginpressure = parser->tank[flags].beginpressure / 100.0;
tank->endpressure = parser->tank[flags].endpressure / 100.0; tank->endpressure = parser->tank[flags].endpressure / 100.0;
tank->gasmix = DC_GASMIX_UNKNOWN; tank->gasmix = DC_GASMIX_UNKNOWN;
tank->usage = DC_TANK_USAGE_NONE;
break; break;
default: default:
return DC_STATUS_UNSUPPORTED; return DC_STATUS_UNSUPPORTED;
@ -545,29 +522,29 @@ liquivision_lynx_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callba
// Time (seconds). // Time (seconds).
time += interval; time += interval;
sample.time = time; sample.time = time * 1000;
if (callback) callback (DC_SAMPLE_TIME, sample, userdata); if (callback) callback (DC_SAMPLE_TIME, &sample, userdata);
// Depth (1/100 m). // Depth (1/100 m).
sample.depth = value / 100.0; 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). // Temperature (1/10 °C).
int temperature = (signed short) array_uint16_le (data + offset); int temperature = (signed short) array_uint16_le (data + offset);
sample.temperature = temperature / 10.0; sample.temperature = temperature / 10.0;
if (callback) callback (DC_SAMPLE_TEMPERATURE, sample, userdata); if (callback) callback (DC_SAMPLE_TEMPERATURE, &sample, userdata);
// Gas mix // Gas mix
if (have_gasmix) { if (have_gasmix) {
sample.gasmix = gasmix_idx; sample.gasmix = gasmix_idx;
if (callback) callback (DC_SAMPLE_GASMIX, sample, userdata); if (callback) callback (DC_SAMPLE_GASMIX, &sample, userdata);
have_gasmix = 0; have_gasmix = 0;
} }
// Setpoint (1/10 bar). // Setpoint (1/10 bar).
if (have_setpoint) { if (have_setpoint) {
sample.setpoint = setpoint / 10.0; sample.setpoint = setpoint / 10.0;
if (callback) callback (DC_SAMPLE_SETPOINT, sample, userdata); if (callback) callback (DC_SAMPLE_SETPOINT, &sample, userdata);
have_setpoint = 0; have_setpoint = 0;
} }
@ -577,7 +554,7 @@ liquivision_lynx_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callba
if (have_pressure & (1 << i)) { if (have_pressure & (1 << i)) {
sample.pressure.tank = i; sample.pressure.tank = i;
sample.pressure.value = pressure[i] / 100.0; 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; 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.depth = 0.0;
} }
sample.deco.time = 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; 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); mares_darwin_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream, unsigned int model);
dc_status_t 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 #ifdef __cplusplus
} }

View File

@ -47,7 +47,6 @@ struct mares_darwin_parser_t {
unsigned int samplesize; 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_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_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); 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 = { static const dc_parser_vtable_t mares_darwin_parser_vtable = {
sizeof(mares_darwin_parser_t), sizeof(mares_darwin_parser_t),
DC_FAMILY_MARES_DARWIN, DC_FAMILY_MARES_DARWIN,
mares_darwin_parser_set_data, /* set_data */
NULL, /* set_clock */ NULL, /* set_clock */
NULL, /* set_atmospheric */ NULL, /* set_atmospheric */
NULL, /* set_density */ NULL, /* set_density */
@ -67,7 +65,7 @@ static const dc_parser_vtable_t mares_darwin_parser_vtable = {
dc_status_t 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; 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; return DC_STATUS_INVALIDARGS;
// Allocate memory. // 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) { if (parser == NULL) {
ERROR (context, "Failed to allocate memory."); ERROR (context, "Failed to allocate memory.");
return DC_STATUS_NOMEMORY; 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 static dc_status_t
mares_darwin_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime) 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; break;
case DC_FIELD_GASMIX: case DC_FIELD_GASMIX:
gasmix->usage = DC_USAGE_NONE;
gasmix->helium = 0.0; gasmix->helium = 0.0;
if (mode == NITROX) { if (mode == NITROX) {
gasmix->oxygen = p[0x0E] / 100.0; 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->gasmix = 0;
tank->beginpressure = array_uint16_be (p + 0x17); tank->beginpressure = array_uint16_be (p + 0x17);
tank->endpressure = array_uint16_be (p + 0x19); tank->endpressure = array_uint16_be (p + 0x19);
tank->usage = DC_TANK_USAGE_NONE;
} else { } else {
return DC_STATUS_UNSUPPORTED; return DC_STATUS_UNSUPPORTED;
} }
@ -242,17 +235,17 @@ mares_darwin_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t
// Surface Time (seconds). // Surface Time (seconds).
time += 20; time += 20;
sample.time = time; sample.time = time * 1000;
if (callback) callback (DC_SAMPLE_TIME, sample, userdata); if (callback) callback (DC_SAMPLE_TIME, &sample, userdata);
// Depth (1/10 m). // Depth (1/10 m).
sample.depth = depth / 10.0; sample.depth = depth / 10.0;
if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata); if (callback) callback (DC_SAMPLE_DEPTH, &sample, userdata);
// Gas change. // Gas change.
if (gasmix != gasmix_previous) { if (gasmix != gasmix_previous) {
sample.gasmix = gasmix; sample.gasmix = gasmix;
if (callback) callback (DC_SAMPLE_GASMIX, sample, userdata); if (callback) callback (DC_SAMPLE_GASMIX, &sample, userdata);
gasmix_previous = gasmix; gasmix_previous = gasmix;
} }
@ -262,7 +255,7 @@ mares_darwin_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t
sample.event.time = 0; sample.event.time = 0;
sample.event.flags = 0; sample.event.flags = 0;
sample.event.value = ascent; sample.event.value = ascent;
if (callback) callback (DC_SAMPLE_EVENT, sample, userdata); if (callback) callback (DC_SAMPLE_EVENT, &sample, userdata);
} }
// Deco violation // 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.time = 0;
sample.event.flags = 0; sample.event.flags = 0;
sample.event.value = 0; sample.event.value = 0;
if (callback) callback (DC_SAMPLE_EVENT, sample, userdata); if (callback) callback (DC_SAMPLE_EVENT, &sample, userdata);
} }
// Deco stop // 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.time = 0;
sample.deco.depth = 0.0; sample.deco.depth = 0.0;
if (callback) callback (DC_SAMPLE_DECO, sample, userdata); sample.deco.tts = 0;
if (callback) callback (DC_SAMPLE_DECO, &sample, userdata);
if (parser->samplesize == 3) { if (parser->samplesize == 3) {
unsigned int type = (time / 20 + 2) % 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]; pressure -= abstract->data[offset + 2];
sample.pressure.tank = 0; sample.pressure.tank = 0;
sample.pressure.value = pressure; 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 <string.h> // memcpy, memcmp
#include <stdlib.h> // malloc, free #include <stdlib.h> // malloc, free
#include <assert.h> // assert
#include "mares_iconhd.h" #include "mares_iconhd.h"
#include "context-private.h" #include "context-private.h"
@ -29,6 +28,7 @@
#include "array.h" #include "array.h"
#include "rbstream.h" #include "rbstream.h"
#include "platform.h" #include "platform.h"
#include "packet.h"
#define ISINSTANCE(device) dc_device_isinstance((device), &mares_iconhd_device_vtable) #define ISINSTANCE(device) dc_device_isinstance((device), &mares_iconhd_device_vtable)
@ -93,16 +93,13 @@ typedef struct mares_iconhd_device_t {
unsigned char version[140]; unsigned char version[140];
unsigned int model; unsigned int model;
unsigned int packetsize; unsigned int packetsize;
unsigned char cache[20];
unsigned int available;
unsigned int offset;
unsigned int splitcommand;
} mares_iconhd_device_t; } mares_iconhd_device_t;
static dc_status_t mares_iconhd_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size); static dc_status_t mares_iconhd_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size);
static dc_status_t mares_iconhd_device_read (dc_device_t *abstract, unsigned int address, unsigned char data[], unsigned int size); static dc_status_t mares_iconhd_device_read (dc_device_t *abstract, unsigned int address, unsigned char data[], unsigned int size);
static dc_status_t mares_iconhd_device_dump (dc_device_t *abstract, dc_buffer_t *buffer); static dc_status_t mares_iconhd_device_dump (dc_device_t *abstract, dc_buffer_t *buffer);
static dc_status_t mares_iconhd_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata); static dc_status_t mares_iconhd_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata);
static dc_status_t mares_iconhd_device_close (dc_device_t *abstract);
static const dc_device_vtable_t mares_iconhd_device_vtable = { static const dc_device_vtable_t mares_iconhd_device_vtable = {
sizeof(mares_iconhd_device_t), sizeof(mares_iconhd_device_t),
@ -113,7 +110,7 @@ static const dc_device_vtable_t mares_iconhd_device_vtable = {
mares_iconhd_device_dump, /* dump */ mares_iconhd_device_dump, /* dump */
mares_iconhd_device_foreach, /* foreach */ mares_iconhd_device_foreach, /* foreach */
NULL, /* timesync */ NULL, /* timesync */
NULL /* close */ mares_iconhd_device_close /* close */
}; };
static const mares_iconhd_layout_t mares_iconhd_layout = { static const mares_iconhd_layout_t mares_iconhd_layout = {
@ -178,104 +175,41 @@ mares_iconhd_get_model (mares_iconhd_device_t *device)
return model; return model;
} }
static dc_status_t
mares_iconhd_read (mares_iconhd_device_t *device, unsigned char data[], size_t size)
{
dc_status_t rc = DC_STATUS_SUCCESS;
dc_transport_t transport = dc_iostream_get_transport(device->iostream);
size_t nbytes = 0;
while (nbytes < size) {
if (transport == DC_TRANSPORT_BLE) {
if (device->available == 0) {
// Read a packet into the cache.
size_t len = 0;
rc = dc_iostream_read (device->iostream, device->cache, sizeof(device->cache), &len);
if (rc != DC_STATUS_SUCCESS)
return rc;
device->available = len;
device->offset = 0;
}
}
// Set the minimum packet size.
size_t length = (transport == DC_TRANSPORT_BLE) ? device->available : size - nbytes;
// Limit the packet size to the total size.
if (nbytes + length > size)
length = size - nbytes;
if (transport == DC_TRANSPORT_BLE) {
// Copy the data from the cached packet.
memcpy (data + nbytes, device->cache + device->offset, length);
device->available -= length;
device->offset += length;
} else {
// Read the packet.
rc = dc_iostream_read (device->iostream, data + nbytes, length, &length);
if (rc != DC_STATUS_SUCCESS)
return rc;
}
nbytes += length;
}
return rc;
}
static dc_status_t
mares_iconhd_write (mares_iconhd_device_t *device, const unsigned char data[], size_t size)
{
dc_status_t rc = DC_STATUS_SUCCESS;
dc_transport_t transport = dc_iostream_get_transport(device->iostream);
size_t nbytes = 0;
while (nbytes < size) {
// Set the maximum packet size.
size_t length = (transport == DC_TRANSPORT_BLE) ? sizeof(device->cache) : size - nbytes;
// Limit the packet size to the total size.
if (nbytes + length > size)
length = size - nbytes;
// Write the packet.
rc = dc_iostream_write (device->iostream, data + nbytes, length, &length);
if (rc != DC_STATUS_SUCCESS)
return rc;
nbytes += length;
}
return rc;
}
static dc_status_t static dc_status_t
mares_iconhd_packet (mares_iconhd_device_t *device, mares_iconhd_packet (mares_iconhd_device_t *device,
const unsigned char command[], unsigned int csize, unsigned char cmd,
const unsigned char data[], unsigned int size,
unsigned char answer[], unsigned int asize) unsigned char answer[], unsigned int asize)
{ {
dc_status_t status = DC_STATUS_SUCCESS; dc_status_t status = DC_STATUS_SUCCESS;
dc_device_t *abstract = (dc_device_t *) device; dc_device_t *abstract = (dc_device_t *) device;
unsigned int split_csize; dc_transport_t transport = dc_iostream_get_transport (device->iostream);
assert (csize >= 2);
if (device_is_cancelled (abstract)) if (device_is_cancelled (abstract))
return DC_STATUS_CANCELLED; return DC_STATUS_CANCELLED;
split_csize = device->splitcommand ? 2 : csize;
// Send the command header to the dive computer. // Send the command header to the dive computer.
status = mares_iconhd_write (device, command, split_csize); const unsigned char command[2] = {
cmd, cmd ^ XOR,
};
status = dc_iostream_write (device->iostream, command, sizeof(command), NULL);
if (status != DC_STATUS_SUCCESS) { if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to send the command."); ERROR (abstract->context, "Failed to send the command.");
return status; return status;
} }
// 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.");
return status;
}
}
// Receive the header byte. // Receive the header byte.
unsigned char header[1] = {0}; unsigned char header[1] = {0};
status = mares_iconhd_read (device, header, sizeof (header)); status = dc_iostream_read (device->iostream, header, sizeof (header), NULL);
if (status != DC_STATUS_SUCCESS) { if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to receive the answer."); ERROR (abstract->context, "Failed to receive the answer.");
return status; return status;
@ -287,17 +221,17 @@ mares_iconhd_packet (mares_iconhd_device_t *device,
return DC_STATUS_PROTOCOL; return DC_STATUS_PROTOCOL;
} }
// Send any remaining command payload to the dive computer. // Send the command payload to the dive computer.
if (csize > split_csize) { if (size && transport != DC_TRANSPORT_BLE) {
status = mares_iconhd_write (device, command + split_csize, csize - split_csize); status = dc_iostream_write (device->iostream, data, size, NULL);
if (status != DC_STATUS_SUCCESS) { if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to send the command."); ERROR (abstract->context, "Failed to send the command data.");
return status; return status;
} }
} }
// Read the packet. // Read the packet.
status = mares_iconhd_read (device, answer, asize); status = dc_iostream_read (device->iostream, answer, asize, NULL);
if (status != DC_STATUS_SUCCESS) { if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to receive the answer."); ERROR (abstract->context, "Failed to receive the answer.");
return status; return status;
@ -305,7 +239,7 @@ mares_iconhd_packet (mares_iconhd_device_t *device,
// Receive the trailer byte. // Receive the trailer byte.
unsigned char trailer[1] = {0}; unsigned char trailer[1] = {0};
status = mares_iconhd_read (device, trailer, sizeof (trailer)); status = dc_iostream_read (device->iostream, trailer, sizeof (trailer), NULL);
if (status != DC_STATUS_SUCCESS) { if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to receive the answer."); ERROR (abstract->context, "Failed to receive the answer.");
return status; return status;
@ -321,11 +255,11 @@ mares_iconhd_packet (mares_iconhd_device_t *device,
} }
static dc_status_t 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_transfer (mares_iconhd_device_t *device, unsigned char cmd, const unsigned char data[], unsigned int size, unsigned char answer[], unsigned int asize)
{ {
unsigned int nretries = 0; unsigned int nretries = 0;
dc_status_t rc = DC_STATUS_SUCCESS; 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)) != DC_STATUS_SUCCESS) {
// Automatically discard a corrupted packet, // Automatically discard a corrupted packet,
// and request a new one. // and request a new one.
if (rc != DC_STATUS_PROTOCOL && rc != DC_STATUS_TIMEOUT) if (rc != DC_STATUS_PROTOCOL && rc != DC_STATUS_TIMEOUT)
@ -338,8 +272,6 @@ mares_iconhd_transfer (mares_iconhd_device_t *device, const unsigned char comman
// Discard any garbage bytes. // Discard any garbage bytes.
dc_iostream_sleep (device->iostream, 100); dc_iostream_sleep (device->iostream, 100);
dc_iostream_purge (device->iostream, DC_DIRECTION_INPUT); dc_iostream_purge (device->iostream, DC_DIRECTION_INPUT);
device->available = 0;
device->offset = 0;
} }
return DC_STATUS_SUCCESS; return DC_STATUS_SUCCESS;
@ -362,23 +294,21 @@ mares_iconhd_read_object(mares_iconhd_device_t *device, dc_event_progress_t *pro
// Transfer the init packet. // Transfer the init packet.
unsigned char rsp_init[16]; unsigned char rsp_init[16];
unsigned char cmd_init[18] = { unsigned char cmd_init[16] = {
CMD_OBJ_INIT,
CMD_OBJ_INIT ^ XOR,
0x40, 0x40,
(index >> 0) & 0xFF, (index >> 0) & 0xFF,
(index >> 8) & 0xFF, (index >> 8) & 0xFF,
subindex & 0xFF subindex & 0xFF
}; };
memset (cmd_init + 6, 0x00, sizeof(cmd_init) - 6); 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));
if (status != DC_STATUS_SUCCESS) { if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to transfer the init packet."); ERROR (abstract->context, "Failed to transfer the init packet.");
return status; return status;
} }
// Verify the packet header. // 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."); ERROR (abstract->context, "Unexpected packet header.");
return DC_STATUS_PROTOCOL; return DC_STATUS_PROTOCOL;
} }
@ -426,8 +356,7 @@ mares_iconhd_read_object(mares_iconhd_device_t *device, dc_event_progress_t *pro
// Transfer the segment packet. // Transfer the segment packet.
unsigned char rsp_segment[1 + 504]; unsigned char rsp_segment[1 + 504];
unsigned char cmd_segment[] = {cmd, cmd ^ XOR}; status = mares_iconhd_transfer (device, cmd, NULL, 0, rsp_segment, len + 1);
status = mares_iconhd_transfer (device, cmd_segment, sizeof (cmd_segment), rsp_segment, len + 1);
if (status != DC_STATUS_SUCCESS) { if (status != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to transfer the segment packet."); ERROR (abstract->context, "Failed to transfer the segment packet.");
return status; return status;
@ -463,6 +392,7 @@ mares_iconhd_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_
{ {
dc_status_t status = DC_STATUS_SUCCESS; dc_status_t status = DC_STATUS_SUCCESS;
mares_iconhd_device_t *device = NULL; mares_iconhd_device_t *device = NULL;
dc_transport_t transport = dc_iostream_get_transport (iostream);
if (out == NULL) if (out == NULL)
return DC_STATUS_INVALIDARGS; return DC_STATUS_INVALIDARGS;
@ -475,63 +405,60 @@ mares_iconhd_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_
} }
// Set the default values. // Set the default values.
device->iostream = iostream;
device->layout = NULL; device->layout = NULL;
memset (device->fingerprint, 0, sizeof (device->fingerprint)); memset (device->fingerprint, 0, sizeof (device->fingerprint));
device->fingerprint_size = sizeof (device->fingerprint); device->fingerprint_size = sizeof (device->fingerprint);
memset (device->version, 0, sizeof (device->version)); memset (device->version, 0, sizeof (device->version));
device->model = 0; device->model = 0;
device->packetsize = 0; device->packetsize = 0;
memset (device->cache, 0, sizeof (device->cache));
device->available = 0;
device->offset = 0;
/* // Create the packet stream.
* At least the Mares Matrix needs the command to be split into if (transport == DC_TRANSPORT_BLE) {
* base and argument, with a wait for the ACK byte in between. status = dc_packet_open (&device->iostream, context, iostream, 20, 20);
* if (status != DC_STATUS_SUCCESS) {
* See commit 59bfb0f3189b ("Add support for the Mares Matrix") ERROR (context, "Failed to create the packet stream.");
* for details. goto error_free;
*/ }
device->splitcommand = 1; } else {
device->iostream = iostream;
}
// Set the serial communication protocol (115200 8E1). // Set the serial communication protocol (115200 8E1).
status = dc_iostream_configure (device->iostream, 115200, 8, DC_PARITY_EVEN, DC_STOPBITS_ONE, DC_FLOWCONTROL_NONE); status = dc_iostream_configure (device->iostream, 115200, 8, DC_PARITY_EVEN, DC_STOPBITS_ONE, DC_FLOWCONTROL_NONE);
if (status != DC_STATUS_SUCCESS) { if (status != DC_STATUS_SUCCESS) {
ERROR (context, "Failed to set the terminal attributes."); ERROR (context, "Failed to set the terminal attributes.");
goto error_free; goto error_free_iostream;
} }
// Set the timeout for receiving data (3000 ms). // Set the timeout for receiving data (3000 ms).
status = dc_iostream_set_timeout (device->iostream, 3000); status = dc_iostream_set_timeout (device->iostream, 3000);
if (status != DC_STATUS_SUCCESS) { if (status != DC_STATUS_SUCCESS) {
ERROR (context, "Failed to set the timeout."); ERROR (context, "Failed to set the timeout.");
goto error_free; goto error_free_iostream;
} }
// Clear the DTR line. // Clear the DTR line.
status = dc_iostream_set_dtr (device->iostream, 0); status = dc_iostream_set_dtr (device->iostream, 0);
if (status != DC_STATUS_SUCCESS) { if (status != DC_STATUS_SUCCESS) {
ERROR (context, "Failed to clear the DTR line."); ERROR (context, "Failed to clear the DTR line.");
goto error_free; goto error_free_iostream;
} }
// Clear the RTS line. // Clear the RTS line.
status = dc_iostream_set_rts (device->iostream, 0); status = dc_iostream_set_rts (device->iostream, 0);
if (status != DC_STATUS_SUCCESS) { if (status != DC_STATUS_SUCCESS) {
ERROR (context, "Failed to clear the RTS line."); ERROR (context, "Failed to clear the RTS line.");
goto error_free; goto error_free_iostream;
} }
// Make sure everything is in a sane state. // Make sure everything is in a sane state.
dc_iostream_purge (device->iostream, DC_DIRECTION_ALL); dc_iostream_purge (device->iostream, DC_DIRECTION_ALL);
// Send the version command. // Send the version command.
unsigned char command[] = {CMD_VERSION, CMD_VERSION ^ XOR}; status = mares_iconhd_transfer (device, CMD_VERSION, NULL, 0,
status = mares_iconhd_transfer (device, command, sizeof (command),
device->version, sizeof (device->version)); device->version, sizeof (device->version));
if (status != DC_STATUS_SUCCESS) { if (status != DC_STATUS_SUCCESS) {
goto error_free; goto error_free_iostream;
} }
// Autodetect the model using the version packet. // Autodetect the model using the version packet.
@ -540,9 +467,8 @@ mares_iconhd_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_
// Read the size of the flash memory. // Read the size of the flash memory.
unsigned int memsize = 0; unsigned int memsize = 0;
if (device->model == QUAD) { if (device->model == QUAD) {
unsigned char cmd_flash[] = {CMD_FLASHSIZE, CMD_FLASHSIZE ^ XOR};
unsigned char rsp_flash[4] = {0}; 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));
if (status != DC_STATUS_SUCCESS) { if (status != DC_STATUS_SUCCESS) {
WARNING (context, "Failed to read the flash memory size."); WARNING (context, "Failed to read the flash memory size.");
} else { } else {
@ -605,26 +531,37 @@ mares_iconhd_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_
*/ */
if (device->packetsize > 128) if (device->packetsize > 128)
device->packetsize = 128; device->packetsize = 128;
/*
* With BLE, don't wait for ACK before sending the arguments
* to a command.
*
* There is some timing issue that makes that take too long
* and causes the command to be aborted.
*/
device->splitcommand = 0;
} }
*out = (dc_device_t *) device; *out = (dc_device_t *) device;
return DC_STATUS_SUCCESS; return DC_STATUS_SUCCESS;
error_free_iostream:
if (transport == DC_TRANSPORT_BLE) {
dc_iostream_close (device->iostream);
}
error_free: error_free:
dc_device_deallocate ((dc_device_t *) device); dc_device_deallocate ((dc_device_t *) device);
return status; return status;
} }
static dc_status_t
mares_iconhd_device_close (dc_device_t *abstract)
{
mares_iconhd_device_t *device = (mares_iconhd_device_t *) abstract;
// Close the packet stream.
if (dc_iostream_get_transport (device->iostream) == DC_TRANSPORT_BLE) {
return dc_iostream_close (device->iostream);
}
return DC_STATUS_SUCCESS;
}
static dc_status_t static dc_status_t
mares_iconhd_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size) mares_iconhd_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size)
{ {
@ -656,7 +593,7 @@ mares_iconhd_device_read (dc_device_t *abstract, unsigned int address, unsigned
len = device->packetsize; len = device->packetsize;
// Read the packet. // Read the packet.
unsigned char command[] = {CMD_READ, CMD_READ ^ XOR, unsigned char command[] = {
(address ) & 0xFF, (address ) & 0xFF,
(address >> 8) & 0xFF, (address >> 8) & 0xFF,
(address >> 16) & 0xFF, (address >> 16) & 0xFF,
@ -665,7 +602,7 @@ mares_iconhd_device_read (dc_device_t *abstract, unsigned int address, unsigned
(len >> 8) & 0xFF, (len >> 8) & 0xFF,
(len >> 16) & 0xFF, (len >> 16) & 0xFF,
(len >> 24) & 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);
if (rc != DC_STATUS_SUCCESS) if (rc != DC_STATUS_SUCCESS)
return rc; return rc;

View File

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

View File

@ -267,7 +267,6 @@ static const mares_iconhd_layout_t horizon = {
0x54 + 8, /* tanks */ 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_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_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); static dc_status_t mares_iconhd_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t callback, void *userdata);
@ -275,7 +274,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 = { static const dc_parser_vtable_t mares_iconhd_parser_vtable = {
sizeof(mares_iconhd_parser_t), sizeof(mares_iconhd_parser_t),
DC_FAMILY_MARES_ICONHD, DC_FAMILY_MARES_ICONHD,
mares_iconhd_parser_set_data, /* set_data */
NULL, /* set_clock */ NULL, /* set_clock */
NULL, /* set_atmospheric */ NULL, /* set_atmospheric */
NULL, /* set_density */ NULL, /* set_density */
@ -299,7 +297,7 @@ mares_genius_isvalid (const unsigned char data[], size_t size, unsigned int type
} }
unsigned short crc = array_uint16_le(data + size - 6); unsigned short crc = array_uint16_le(data + size - 6);
unsigned short ccrc = checksum_crc16_ccitt(data + 4, size - 10, 0x0000); unsigned short ccrc = checksum_crc16_ccitt(data + 4, size - 10, 0x0000, 0x0000);
if (crc != ccrc) { if (crc != ccrc) {
return 0; return 0;
} }
@ -404,12 +402,12 @@ mares_iconhd_cache (mares_iconhd_parser_t *parser)
unsigned int samplerate = 0; unsigned int samplerate = 0;
if (parser->model == SMARTAPNEA) { if (parser->model == SMARTAPNEA) {
unsigned int idx = (settings & 0x0600) >> 9; unsigned int idx = (settings & 0x0600) >> 9;
interval = 1;
samplerate = 1 << idx; samplerate = 1 << idx;
interval = 1000 / samplerate;
} else { } else {
const unsigned int intervals[] = {1, 5, 10, 20}; const unsigned int intervals[] = {1, 5, 10, 20};
unsigned int idx = (settings & 0x0C00) >> 10; unsigned int idx = (settings & 0x0C00) >> 10;
interval = intervals[idx]; interval = intervals[idx] * 1000;
samplerate = 1; samplerate = 1;
} }
@ -628,7 +626,7 @@ mares_genius_cache (mares_iconhd_parser_t *parser)
parser->headersize = headersize; parser->headersize = headersize;
parser->settings = settings; parser->settings = settings;
parser->surftime = surftime * 60; parser->surftime = surftime * 60;
parser->interval = 5; parser->interval = 5000;
parser->samplerate = 1; parser->samplerate = 1;
parser->ntanks = ntanks; parser->ntanks = ntanks;
parser->ngasmixes = ngasmixes; parser->ngasmixes = ngasmixes;
@ -659,7 +657,7 @@ mares_iconhd_parser_cache (mares_iconhd_parser_t *parser)
} }
dc_status_t dc_status_t
mares_iconhd_parser_create (dc_parser_t **out, dc_context_t *context, unsigned int model, unsigned int serial) mares_iconhd_parser_create (dc_parser_t **out, dc_context_t *context, const unsigned char data[], size_t size, unsigned int model, unsigned int serial)
{ {
mares_iconhd_parser_t *parser = NULL; mares_iconhd_parser_t *parser = NULL;
@ -667,7 +665,7 @@ mares_iconhd_parser_create (dc_parser_t **out, dc_context_t *context, unsigned i
return DC_STATUS_INVALIDARGS; return DC_STATUS_INVALIDARGS;
// Allocate memory. // 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) { if (parser == NULL) {
ERROR (context, "Failed to allocate memory."); ERROR (context, "Failed to allocate memory.");
return DC_STATUS_NOMEMORY; return DC_STATUS_NOMEMORY;
@ -705,41 +703,6 @@ mares_iconhd_parser_create (dc_parser_t **out, dc_context_t *context, unsigned i
return DC_STATUS_SUCCESS; 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 static dc_status_t
mares_iconhd_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime) mares_iconhd_parser_get_datetime (dc_parser_t *abstract, dc_datetime_t *datetime)
{ {
@ -827,7 +790,7 @@ mares_iconhd_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsi
if (parser->layout->divetime != UNSUPPORTED) { if (parser->layout->divetime != UNSUPPORTED) {
*((unsigned int *) value) = array_uint16_le (p + parser->layout->divetime); *((unsigned int *) value) = array_uint16_le (p + parser->layout->divetime);
} else { } else {
*((unsigned int *) value) = parser->nsamples * parser->interval - parser->surftime; *((unsigned int *) value) = parser->nsamples * parser->interval / 1000 - parser->surftime;
} }
break; break;
case DC_FIELD_MAXDEPTH: case DC_FIELD_MAXDEPTH:
@ -837,6 +800,7 @@ mares_iconhd_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsi
*((unsigned int *) value) = parser->ngasmixes; *((unsigned int *) value) = parser->ngasmixes;
break; break;
case DC_FIELD_GASMIX: case DC_FIELD_GASMIX:
gasmix->usage = DC_USAGE_NONE;
gasmix->oxygen = parser->gasmix[flags].oxygen / 100.0; gasmix->oxygen = parser->gasmix[flags].oxygen / 100.0;
gasmix->helium = parser->gasmix[flags].helium / 100.0; gasmix->helium = parser->gasmix[flags].helium / 100.0;
gasmix->nitrogen = 1.0 - gasmix->oxygen - gasmix->helium; gasmix->nitrogen = 1.0 - gasmix->oxygen - gasmix->helium;
@ -864,6 +828,7 @@ mares_iconhd_parser_get_field (dc_parser_t *abstract, dc_field_type_t type, unsi
} else { } else {
tank->gasmix = DC_GASMIX_UNKNOWN; tank->gasmix = DC_GASMIX_UNKNOWN;
} }
tank->usage = DC_TANK_USAGE_NONE;
break; break;
case DC_FIELD_ATMOSPHERIC: case DC_FIELD_ATMOSPHERIC:
*((double *) value) = array_uint16_le (p + parser->layout->atmospheric) / (1000.0 * parser->layout->atmospheric_divisor); *((double *) value) = array_uint16_le (p + parser->layout->atmospheric) / (1000.0 * parser->layout->atmospheric_divisor);
@ -981,14 +946,6 @@ mares_iconhd_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t
const unsigned char *data = abstract->data; 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 // Previous gas mix - initialize with impossible value
unsigned int gasmix_previous = 0xFFFFFFFF; unsigned int gasmix_previous = 0xFFFFFFFF;
@ -1039,29 +996,30 @@ mares_iconhd_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t
unsigned int surftime = array_uint16_le (data + offset + 4); unsigned int surftime = array_uint16_le (data + offset + 4);
// Surface Time (seconds). // Surface Time (seconds).
time += surftime; time += surftime * 1000;
sample.time = time; sample.time = time;
if (callback) callback (DC_SAMPLE_TIME, sample, userdata); if (callback) callback (DC_SAMPLE_TIME, &sample, userdata);
// Surface Depth (0 m). // Surface Depth (0 m).
sample.depth = 0.0; sample.depth = 0.0;
if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata); if (callback) callback (DC_SAMPLE_DEPTH, &sample, userdata);
offset += parser->samplesize; offset += parser->samplesize;
nsamples++; 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 (seconds).
time += parser->interval; time += parser->interval;
sample.time = time; sample.time = time;
if (callback) callback (DC_SAMPLE_TIME, sample, userdata); if (callback) callback (DC_SAMPLE_TIME, &sample, userdata);
// Depth (1/10 m). // Depth (1/10 m).
unsigned int depth = array_uint16_le (data + offset); unsigned int depth = array_uint16_le (data + offset);
sample.depth = depth / 10.0; sample.depth = depth / 10.0;
if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata); if (callback) callback (DC_SAMPLE_DEPTH, &sample, userdata);
offset += 2 * parser->samplerate; offset += 2;
} }
} else if (parser->model != GENIUS && parser->model != HORIZON && parser->mode == ICONHD_FREEDIVE) { } else if (parser->model != GENIUS && parser->model != HORIZON && parser->mode == ICONHD_FREEDIVE) {
unsigned int maxdepth = array_uint16_le (data + offset + 0); unsigned int maxdepth = array_uint16_le (data + offset + 0);
@ -1069,22 +1027,22 @@ mares_iconhd_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t
unsigned int surftime = array_uint16_le (data + offset + 4); unsigned int surftime = array_uint16_le (data + offset + 4);
// Surface Time (seconds). // Surface Time (seconds).
time += surftime; time += surftime * 1000;
sample.time = time; sample.time = time;
if (callback) callback (DC_SAMPLE_TIME, sample, userdata); if (callback) callback (DC_SAMPLE_TIME, &sample, userdata);
// Surface Depth (0 m). // Surface Depth (0 m).
sample.depth = 0.0; sample.depth = 0.0;
if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata); if (callback) callback (DC_SAMPLE_DEPTH, &sample, userdata);
// Dive Time (seconds). // Dive Time (seconds).
time += divetime; time += divetime * 1000;
sample.time = time; sample.time = time;
if (callback) callback (DC_SAMPLE_TIME, sample, userdata); if (callback) callback (DC_SAMPLE_TIME, &sample, userdata);
// Maximum Depth (1/10 m). // Maximum Depth (1/10 m).
sample.depth = maxdepth / 10.0; sample.depth = maxdepth / 10.0;
if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata); if (callback) callback (DC_SAMPLE_DEPTH, &sample, userdata);
offset += parser->samplesize; offset += parser->samplesize;
nsamples++; nsamples++;
@ -1142,15 +1100,15 @@ mares_iconhd_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t
// Time (seconds). // Time (seconds).
time += parser->interval; time += parser->interval;
sample.time = time; sample.time = time;
if (callback) callback (DC_SAMPLE_TIME, sample, userdata); if (callback) callback (DC_SAMPLE_TIME, &sample, userdata);
// Depth (1/10 m). // Depth (1/10 m).
sample.depth = depth / 10.0; sample.depth = depth / 10.0;
if (callback) callback (DC_SAMPLE_DEPTH, sample, userdata); if (callback) callback (DC_SAMPLE_DEPTH, &sample, userdata);
// Temperature (1/10 °C). // Temperature (1/10 °C).
sample.temperature = temperature / 10.0; sample.temperature = temperature / 10.0;
if (callback) callback (DC_SAMPLE_TEMPERATURE, sample, userdata); if (callback) callback (DC_SAMPLE_TEMPERATURE, &sample, userdata);
// Current gas mix // Current gas mix
if (parser->ngasmixes > 0) { if (parser->ngasmixes > 0) {
@ -1160,7 +1118,7 @@ mares_iconhd_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t
} }
if (gasmix != gasmix_previous) { if (gasmix != gasmix_previous) {
sample.gasmix = gasmix; sample.gasmix = gasmix;
if (callback) callback (DC_SAMPLE_GASMIX, sample, userdata); if (callback) callback (DC_SAMPLE_GASMIX, &sample, userdata);
gasmix_previous = gasmix; gasmix_previous = gasmix;
} }
} }
@ -1171,7 +1129,7 @@ mares_iconhd_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t
sample.event.time = 0; sample.event.time = 0;
sample.event.flags = 0; sample.event.flags = 0;
sample.event.value = bookmark; sample.event.value = bookmark;
if (callback) callback (DC_SAMPLE_EVENT, sample, userdata); if (callback) callback (DC_SAMPLE_EVENT, &sample, userdata);
} }
if (parser->model == GENIUS || parser->model == HORIZON) { if (parser->model == GENIUS || parser->model == HORIZON) {
@ -1184,7 +1142,8 @@ mares_iconhd_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t
sample.deco.depth = 0.0; sample.deco.depth = 0.0;
} }
sample.deco.time = decotime * 60; sample.deco.time = decotime * 60;
if (callback) callback (DC_SAMPLE_DECO, sample, userdata); sample.deco.tts = tts;
if (callback) callback (DC_SAMPLE_DECO, &sample, userdata);
// Alarms // Alarms
for (unsigned int v = alarms, i = 0; v; v >>= 1, ++i) { for (unsigned int v = alarms, i = 0; v; v >>= 1, ++i) {
@ -1210,7 +1169,7 @@ mares_iconhd_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t
sample.event.time = 0; sample.event.time = 0;
sample.event.flags = 0; sample.event.flags = 0;
sample.event.value = 0; sample.event.value = 0;
if (callback) callback (DC_SAMPLE_EVENT, sample, userdata); if (callback) callback (DC_SAMPLE_EVENT, &sample, userdata);
} }
} }
} }
@ -1231,7 +1190,7 @@ mares_iconhd_parser_samples_foreach (dc_parser_t *abstract, dc_sample_callback_t
if (gasmix < parser->ntanks) { if (gasmix < parser->ntanks) {
sample.pressure.tank = gasmix; sample.pressure.tank = gasmix;
sample.pressure.value = pressure / 100.0; 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) { } else if (pressure != 0) {
WARNING (abstract->context, "Invalid tank with non-zero pressure."); WARNING (abstract->context, "Invalid tank with non-zero pressure.");
} }

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); mares_nemo_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream);
dc_status_t 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 #ifdef __cplusplus
} }

View File

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

View File

@ -26,6 +26,7 @@
#include "context-private.h" #include "context-private.h"
#include "device-private.h" #include "device-private.h"
#include "array.h" #include "array.h"
#include "packet.h"
#define ISINSTANCE(device) dc_device_isinstance((device), &mclean_extreme_device_vtable) #define ISINSTANCE(device) dc_device_isinstance((device), &mclean_extreme_device_vtable)
@ -391,6 +392,7 @@ mclean_extreme_device_open(dc_device_t **out, dc_context_t *context, dc_iostream
{ {
dc_status_t status = DC_STATUS_SUCCESS; dc_status_t status = DC_STATUS_SUCCESS;
mclean_extreme_device_t *device = NULL; mclean_extreme_device_t *device = NULL;
dc_transport_t transport = dc_iostream_get_transport (iostream);
if (out == NULL) if (out == NULL)
return DC_STATUS_INVALIDARGS; return DC_STATUS_INVALIDARGS;
@ -403,21 +405,31 @@ mclean_extreme_device_open(dc_device_t **out, dc_context_t *context, dc_iostream
} }
// Set the default values. // Set the default values.
device->iostream = iostream;
memset(device->fingerprint, 0, sizeof(device->fingerprint)); memset(device->fingerprint, 0, sizeof(device->fingerprint));
// Create the packet stream.
if (transport == DC_TRANSPORT_BLE) {
status = dc_packet_open (&device->iostream, context, iostream, 244, 244);
if (status != DC_STATUS_SUCCESS) {
ERROR (context, "Failed to create the packet stream.");
goto error_free;
}
} else {
device->iostream = iostream;
}
// Set the serial communication protocol (115200 8N1). // Set the serial communication protocol (115200 8N1).
status = dc_iostream_configure(device->iostream, 115200, 8, DC_PARITY_NONE, DC_STOPBITS_ONE, DC_FLOWCONTROL_NONE); status = dc_iostream_configure(device->iostream, 115200, 8, DC_PARITY_NONE, DC_STOPBITS_ONE, DC_FLOWCONTROL_NONE);
if (status != DC_STATUS_SUCCESS) { if (status != DC_STATUS_SUCCESS) {
ERROR(context, "Failed to set the terminal attributes."); ERROR(context, "Failed to set the terminal attributes.");
goto error_free; goto error_free_iostream;
} }
// Set the timeout for receiving data (1000ms). // Set the timeout for receiving data (1000ms).
status = dc_iostream_set_timeout(device->iostream, 1000); status = dc_iostream_set_timeout(device->iostream, 1000);
if (status != DC_STATUS_SUCCESS) { if (status != DC_STATUS_SUCCESS) {
ERROR(context, "Failed to set the timeout."); ERROR(context, "Failed to set the timeout.");
goto error_free; goto error_free_iostream;
} }
// Make sure everything is in a sane state. // Make sure everything is in a sane state.
@ -428,6 +440,10 @@ mclean_extreme_device_open(dc_device_t **out, dc_context_t *context, dc_iostream
return DC_STATUS_SUCCESS; return DC_STATUS_SUCCESS;
error_free_iostream:
if (transport == DC_TRANSPORT_BLE) {
dc_iostream_close (device->iostream);
}
error_free: error_free:
dc_device_deallocate((dc_device_t *)device); dc_device_deallocate((dc_device_t *)device);
return status; return status;
@ -438,14 +454,24 @@ mclean_extreme_device_close(dc_device_t *abstract)
{ {
dc_status_t status = DC_STATUS_SUCCESS; dc_status_t status = DC_STATUS_SUCCESS;
mclean_extreme_device_t *device = (mclean_extreme_device_t *)abstract; mclean_extreme_device_t *device = (mclean_extreme_device_t *)abstract;
dc_status_t rc = DC_STATUS_SUCCESS;
status = mclean_extreme_send(device, CMD_CLOSE, NULL, 0); rc = mclean_extreme_send(device, CMD_CLOSE, NULL, 0);
if (status != DC_STATUS_SUCCESS) { if (rc != DC_STATUS_SUCCESS) {
ERROR(abstract->context, "Failed to send the exit command."); ERROR(abstract->context, "Failed to send the exit command.");
return status; dc_status_set_error(&status, rc);
} }
return DC_STATUS_SUCCESS; // Close the packet stream.
if (dc_iostream_get_transport (device->iostream) == DC_TRANSPORT_BLE) {
rc = dc_iostream_close (device->iostream);
if (rc != DC_STATUS_SUCCESS) {
ERROR (abstract->context, "Failed to close the packet stream.");
dc_status_set_error(&status, rc);
}
}
return status;
} }
static dc_status_t static dc_status_t

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