pyixblue 0.9.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,94 @@
1
+ # PyPI publish — OIDC Trusted Publishing.
2
+ #
3
+ # First publish: register a pending publisher at
4
+ # https://pypi.org/manage/account/publishing/ with claims matching THIS file:
5
+ # Owner=TTCMarine Repository=pyixblue Workflow=publish.yml Environment=pypi
6
+ # Without it the publish step fails `invalid-publisher`.
7
+
8
+ name: Publish to PyPI
9
+
10
+ on:
11
+ push:
12
+ tags:
13
+ - "v*" # release on a version tag only — never on ordinary pushes
14
+
15
+ permissions: {} # least privilege: grant per-job below
16
+
17
+ concurrency: # never let two tag pushes publish the same project at once
18
+ group: pypi-publish-${{ github.repository }}
19
+ cancel-in-progress: false # let an in-flight publish finish; queue the next
20
+
21
+ jobs:
22
+ build:
23
+ name: Build + audit (locked)
24
+ runs-on: ubuntu-latest
25
+ permissions:
26
+ contents: read
27
+ steps:
28
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
29
+ with:
30
+ persist-credentials: false
31
+
32
+ - uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
33
+ with:
34
+ enable-cache: false
35
+
36
+ - name: Sync locked environment
37
+ run: uv sync --locked --no-dev
38
+
39
+ - name: Audit dependencies
40
+ run: uvx pip-audit --progress-spinner off
41
+
42
+ - name: Build sdist + wheel
43
+ run: uv build
44
+
45
+ - name: Validate artifacts (PyPI ingestion check)
46
+ run: uvx twine check dist/*
47
+
48
+ - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
49
+ with:
50
+ name: dist
51
+ path: dist/
52
+ if-no-files-found: error
53
+
54
+ publish:
55
+ name: Publish to PyPI (trusted publishing)
56
+ needs: build
57
+ runs-on: ubuntu-latest
58
+ environment: pypi
59
+ permissions:
60
+ id-token: write # OIDC for trusted publishing — the only elevated grant
61
+ contents: read
62
+ steps:
63
+ - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
64
+ with:
65
+ name: dist
66
+ path: dist/
67
+
68
+ - uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2
69
+ with:
70
+ attestations: true # PEP 740 signed provenance
71
+
72
+ verify:
73
+ name: Verify on PyPI
74
+ needs: publish
75
+ runs-on: ubuntu-latest
76
+ permissions:
77
+ contents: read
78
+ steps:
79
+ - name: Confirm version is queryable on PyPI
80
+ env:
81
+ DIST_NAME: "pyixblue"
82
+ run: |
83
+ version="${GITHUB_REF_NAME#v}"
84
+ echo "Verifying ${DIST_NAME}==${version} is live on PyPI..."
85
+ for attempt in 1 2 3 4 5; do
86
+ if curl -fsS "https://pypi.org/pypi/${DIST_NAME}/${version}/json" >/dev/null; then
87
+ echo "OK ${DIST_NAME}==${version} is live"
88
+ exit 0
89
+ fi
90
+ echo " attempt ${attempt}: not visible yet, waiting 15s..."
91
+ sleep 15
92
+ done
93
+ echo "FAIL ${DIST_NAME}==${version} not visible after 75s"
94
+ exit 1
@@ -0,0 +1,13 @@
1
+ # Python-generated files
2
+ __pycache__/
3
+ *.py[oc]
4
+ build/
5
+ dist/
6
+ wheels/
7
+ *.egg-info
8
+
9
+ # Virtual environments
10
+ .venv
11
+
12
+ # Test and tool caches
13
+ .pytest_cache/
@@ -0,0 +1 @@
1
+ 3.14
pyixblue-0.9.1/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 pyixblue contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,108 @@
1
+ Metadata-Version: 2.5
2
+ Name: pyixblue
3
+ Version: 0.9.1
4
+ Summary: Python parser for the iXblue stdbin protocol
5
+ License: MIT License
6
+
7
+ Copyright (c) 2026 pyixblue contributors
8
+
9
+ Permission is hereby granted, free of charge, to any person obtaining a copy
10
+ of this software and associated documentation files (the "Software"), to deal
11
+ in the Software without restriction, including without limitation the rights
12
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13
+ copies of the Software, and to permit persons to whom the Software is
14
+ furnished to do so, subject to the following conditions:
15
+
16
+ The above copyright notice and this permission notice shall be included in all
17
+ copies or substantial portions of the Software.
18
+
19
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25
+ SOFTWARE.
26
+ License-File: LICENSE
27
+ Requires-Python: >=3.11
28
+ Provides-Extra: dev
29
+ Requires-Dist: pytest>=8; extra == 'dev'
30
+ Description-Content-Type: text/markdown
31
+
32
+ # pyixblue
33
+
34
+ Python parser for the iXblue stdbin output protocol (versions 2 through 5).
35
+
36
+ stdbin is the binary navigation output of iXblue inertial systems. This package
37
+ reassembles and decodes those frames. It does not open a socket or a serial
38
+ port; pass in the bytes you receive over UDP, TCP, or serial.
39
+
40
+ The block layout and checksum follow the MIT-licensed
41
+ [ixblue_stdbin_decoder](https://github.com/ixblue/ixblue_stdbin_decoder).
42
+
43
+ ## Install
44
+
45
+ ```sh
46
+ pip install .
47
+ ```
48
+
49
+ From a checkout, with the development tests:
50
+
51
+ ```sh
52
+ uv sync
53
+ ```
54
+
55
+ ## Usage
56
+
57
+ ```python
58
+ from pyixblue import StdBinDecoder
59
+
60
+ decoder = StdBinDecoder()
61
+ decoder.add_new_data(chunk) # bytes-like; a chunk may be a partial frame
62
+ while decoder.parse_next_frame(): # False when more bytes are needed
63
+ header = decoder.last_header
64
+ if header.message_type == "nav":
65
+ nav = decoder.last_nav # missing blocks are None
66
+ if nav.position is not None:
67
+ print(nav.position.latitude_deg, nav.position.longitude_deg)
68
+ else:
69
+ payload = decoder.last_answer
70
+ ```
71
+
72
+ `parse_next_frame` raises `StdBinError` when a frame is recognized but its
73
+ protocol version or checksum is invalid. The decoder drops the bad version
74
+ byte, or the whole frame after a bad checksum, so the next call can resync.
75
+
76
+ Navigation, extended-navigation, and external-sensor blocks are present only
77
+ when their mask bit is set. INS algorithm and system status words are `IntFlag`
78
+ values (`1 << bit`), so they compare equal to the raw integer:
79
+
80
+ ```python
81
+ from pyixblue.models import InsAlgorithmStatus1
82
+
83
+ if nav.ins_algorithm_status is not None:
84
+ status = nav.ins_algorithm_status.status1
85
+ if InsAlgorithmStatus1.NAVIGATION in status:
86
+ print(hex(status))
87
+ ```
88
+
89
+ ## Protocol
90
+
91
+ Supported output frames:
92
+
93
+ | Kind | Magic | Versions |
94
+ | --- | --- | --- |
95
+ | Navigation | `IX` | 2, 3, 4, 5 |
96
+ | Answer | `AN` | 3, 4, 5 |
97
+
98
+ Multi-byte fields are big-endian. The checksum is the low 32 bits of the sum of
99
+ every preceding byte, stored as a big-endian `uint32`.
100
+
101
+ Version 3 adds the extended-navigation mask. Versions 4 and 5 add the
102
+ navigation-data size. The header also keeps the frame `counter`, which the
103
+ reference C++ header discards.
104
+
105
+ ## License
106
+
107
+ MIT. Sample frames under `tests/fixtures/` were converted from the
108
+ ixblue_stdbin_decoder test logs (MIT, Copyright (c) 2019 iXblue SAS).
@@ -0,0 +1,77 @@
1
+ # pyixblue
2
+
3
+ Python parser for the iXblue stdbin output protocol (versions 2 through 5).
4
+
5
+ stdbin is the binary navigation output of iXblue inertial systems. This package
6
+ reassembles and decodes those frames. It does not open a socket or a serial
7
+ port; pass in the bytes you receive over UDP, TCP, or serial.
8
+
9
+ The block layout and checksum follow the MIT-licensed
10
+ [ixblue_stdbin_decoder](https://github.com/ixblue/ixblue_stdbin_decoder).
11
+
12
+ ## Install
13
+
14
+ ```sh
15
+ pip install .
16
+ ```
17
+
18
+ From a checkout, with the development tests:
19
+
20
+ ```sh
21
+ uv sync
22
+ ```
23
+
24
+ ## Usage
25
+
26
+ ```python
27
+ from pyixblue import StdBinDecoder
28
+
29
+ decoder = StdBinDecoder()
30
+ decoder.add_new_data(chunk) # bytes-like; a chunk may be a partial frame
31
+ while decoder.parse_next_frame(): # False when more bytes are needed
32
+ header = decoder.last_header
33
+ if header.message_type == "nav":
34
+ nav = decoder.last_nav # missing blocks are None
35
+ if nav.position is not None:
36
+ print(nav.position.latitude_deg, nav.position.longitude_deg)
37
+ else:
38
+ payload = decoder.last_answer
39
+ ```
40
+
41
+ `parse_next_frame` raises `StdBinError` when a frame is recognized but its
42
+ protocol version or checksum is invalid. The decoder drops the bad version
43
+ byte, or the whole frame after a bad checksum, so the next call can resync.
44
+
45
+ Navigation, extended-navigation, and external-sensor blocks are present only
46
+ when their mask bit is set. INS algorithm and system status words are `IntFlag`
47
+ values (`1 << bit`), so they compare equal to the raw integer:
48
+
49
+ ```python
50
+ from pyixblue.models import InsAlgorithmStatus1
51
+
52
+ if nav.ins_algorithm_status is not None:
53
+ status = nav.ins_algorithm_status.status1
54
+ if InsAlgorithmStatus1.NAVIGATION in status:
55
+ print(hex(status))
56
+ ```
57
+
58
+ ## Protocol
59
+
60
+ Supported output frames:
61
+
62
+ | Kind | Magic | Versions |
63
+ | --- | --- | --- |
64
+ | Navigation | `IX` | 2, 3, 4, 5 |
65
+ | Answer | `AN` | 3, 4, 5 |
66
+
67
+ Multi-byte fields are big-endian. The checksum is the low 32 bits of the sum of
68
+ every preceding byte, stored as a big-endian `uint32`.
69
+
70
+ Version 3 adds the extended-navigation mask. Versions 4 and 5 add the
71
+ navigation-data size. The header also keeps the frame `counter`, which the
72
+ reference C++ header discards.
73
+
74
+ ## License
75
+
76
+ MIT. Sample frames under `tests/fixtures/` were converted from the
77
+ ixblue_stdbin_decoder test logs (MIT, Copyright (c) 2019 iXblue SAS).
@@ -0,0 +1,21 @@
1
+ [project]
2
+ name = "pyixblue"
3
+ version = "0.9.1"
4
+ description = "Python parser for the iXblue stdbin protocol"
5
+ readme = "README.md"
6
+ license = { file = "LICENSE" }
7
+ requires-python = ">=3.11"
8
+ dependencies = []
9
+
10
+ [project.optional-dependencies]
11
+ dev = ["pytest>=8"]
12
+
13
+ [dependency-groups]
14
+ dev = ["pytest>=8"]
15
+
16
+ [build-system]
17
+ requires = ["hatchling"]
18
+ build-backend = "hatchling.build"
19
+
20
+ [tool.pytest.ini_options]
21
+ testpaths = ["tests"]
@@ -0,0 +1,15 @@
1
+ """Parse iXblue stdbin navigation and answer frames."""
2
+
3
+ from pyixblue.decoder import StdBinDecoder
4
+ from pyixblue.errors import StdBinError
5
+ from pyixblue.models import MessageType, NavData, NavHeader
6
+
7
+ __all__ = [
8
+ "MessageType",
9
+ "NavData",
10
+ "NavHeader",
11
+ "StdBinDecoder",
12
+ "StdBinError",
13
+ ]
14
+
15
+ __version__ = "0.9.1"
@@ -0,0 +1,242 @@
1
+ """Declarative stdbin memory-block table.
2
+
3
+ Sizes and bit positions match the reference ``MemoryBlockParser`` constructors.
4
+ Blocks are stored in bit order, which is the order they are packed on the wire.
5
+ Logbook is the 40 bytes the reference parser actually reads (``int32``,
6
+ ``uint32``, and 32 text bytes), not the 12-byte guard declared on that parser.
7
+ ``vehicle_attitude_heading_deviation`` is included even though the reference
8
+ decoder constructs no parser for it, so a set extended-mask bit 4 is consumed.
9
+ """
10
+
11
+ from collections.abc import Callable
12
+ from dataclasses import dataclass, fields
13
+ from struct import calcsize
14
+ from typing import Any
15
+
16
+ from pyixblue.models import (
17
+ AccelerationGeographicFrame,
18
+ AccelerationVesselFrame,
19
+ AccelerationVesselFrameDeviation,
20
+ AhrsAlgorithmStatus,
21
+ AhrsSystemStatus,
22
+ AhrsUserStatus,
23
+ AttitudeHeading,
24
+ AttitudeHeadingDeviation,
25
+ AttitudeQuaternion,
26
+ AttitudeQuaternionDeviation,
27
+ CourseSpeedOverGround,
28
+ CurrentGeographicFrame,
29
+ CurrentGeographicFrameDeviation,
30
+ Depth,
31
+ Dmi,
32
+ DvlGroundSpeed,
33
+ DvlWaterSpeed,
34
+ Emlog,
35
+ EventMarker,
36
+ Gnss,
37
+ HeadingRollPitchRate,
38
+ HeaveSurgeSwaySpeed,
39
+ InsAlgorithmStatus,
40
+ InsAlgorithmStatus1,
41
+ InsAlgorithmStatus2,
42
+ InsAlgorithmStatus3,
43
+ InsAlgorithmStatus4,
44
+ InsSystemStatus,
45
+ InsSystemStatus1,
46
+ InsSystemStatus2,
47
+ InsSystemStatus3,
48
+ InsUserStatus,
49
+ Lbl,
50
+ LogBook,
51
+ Position,
52
+ PositionDeviation,
53
+ RawAccelerationVesselFrame,
54
+ RawRotationRateVesselFrame,
55
+ RealTimeHeaveSurgeSway,
56
+ RotationAccelerationVesselFrame,
57
+ RotationAccelerationVesselFrameDeviation,
58
+ RotationRateVesselFrame,
59
+ RotationRateVesselFrameDeviation,
60
+ SensorStatus,
61
+ SmartHeave,
62
+ SoundVelocity,
63
+ SpeedGeographicFrame,
64
+ SpeedGeographicFrameDeviation,
65
+ SpeedVesselFrame,
66
+ SystemDate,
67
+ Temperatures,
68
+ TurretAngles,
69
+ Usbl,
70
+ Utc,
71
+ VehicleAttitudeHeading,
72
+ VehicleAttitudeHeadingDeviation,
73
+ VehiclePosition,
74
+ VehiclePositionDeviation,
75
+ Vtg,
76
+ )
77
+
78
+
79
+ @dataclass(frozen=True, slots=True)
80
+ class Block:
81
+ """One optional memory block identified by a mask bit."""
82
+
83
+ field: str
84
+ bit: int
85
+ fmt: str
86
+ build: Callable[[tuple[Any, ...]], Any]
87
+
88
+ @property
89
+ def size(self) -> int:
90
+ return calcsize(self.fmt)
91
+
92
+
93
+ def _build(
94
+ cls: type[Any], converters: tuple[Callable[[int], Any] | None, ...] = ()
95
+ ) -> Callable[[tuple[Any, ...]], Any]:
96
+ names = tuple(item.name for item in fields(cls))
97
+
98
+ def build(values: tuple[Any, ...]) -> Any:
99
+ converted: list[Any] = []
100
+ for index, value in enumerate(values):
101
+ converter = converters[index] if index < len(converters) else None
102
+ converted.append(converter(value) if converter is not None else value)
103
+ return cls(**dict(zip(names, converted, strict=True)))
104
+
105
+ return build
106
+
107
+
108
+ def _block(
109
+ field: str,
110
+ bit: int,
111
+ fmt: str,
112
+ cls: type[Any],
113
+ converters: tuple[Callable[[int], Any] | None, ...] = (),
114
+ ) -> Block:
115
+ return Block(field, bit, fmt, _build(cls, converters))
116
+
117
+
118
+ def _sorted(blocks: tuple[Block, ...]) -> tuple[Block, ...]:
119
+ return tuple(sorted(blocks, key=lambda block: block.bit))
120
+
121
+
122
+ NAVIGATION_BLOCKS = _sorted(
123
+ (
124
+ _block("attitude_heading", 0, ">fff", AttitudeHeading),
125
+ _block("attitude_heading_deviation", 1, ">fff", AttitudeHeadingDeviation),
126
+ _block("rt_heave_surge_sway", 2, ">ffff", RealTimeHeaveSurgeSway),
127
+ _block("smart_heave", 3, ">If", SmartHeave),
128
+ _block("heading_roll_pitch_rate", 4, ">fff", HeadingRollPitchRate),
129
+ _block("rotation_rate_vessel_frame", 5, ">fff", RotationRateVesselFrame),
130
+ _block("acceleration_vessel_frame", 6, ">fff", AccelerationVesselFrame),
131
+ _block("position", 7, ">ddBf", Position),
132
+ _block("position_deviation", 8, ">ffff", PositionDeviation),
133
+ _block("speed_geographic_frame", 9, ">fff", SpeedGeographicFrame),
134
+ _block("speed_geographic_frame_deviation", 10, ">fff", SpeedGeographicFrameDeviation),
135
+ _block("current_geographic_frame", 11, ">ff", CurrentGeographicFrame),
136
+ _block("current_geographic_frame_deviation", 12, ">ff", CurrentGeographicFrameDeviation),
137
+ _block("system_date", 13, ">BBH", SystemDate),
138
+ _block("sensor_status", 14, ">II", SensorStatus),
139
+ _block(
140
+ "ins_algorithm_status",
141
+ 15,
142
+ ">IIII",
143
+ InsAlgorithmStatus,
144
+ (
145
+ InsAlgorithmStatus1,
146
+ InsAlgorithmStatus2,
147
+ InsAlgorithmStatus3,
148
+ InsAlgorithmStatus4,
149
+ ),
150
+ ),
151
+ _block(
152
+ "ins_system_status",
153
+ 16,
154
+ ">III",
155
+ InsSystemStatus,
156
+ (InsSystemStatus1, InsSystemStatus2, InsSystemStatus3),
157
+ ),
158
+ _block("ins_user_status", 17, ">I", InsUserStatus),
159
+ _block("ahrs_algorithm_status", 18, ">I", AhrsAlgorithmStatus),
160
+ _block("ahrs_system_status", 19, ">III", AhrsSystemStatus),
161
+ _block("ahrs_user_status", 20, ">I", AhrsUserStatus),
162
+ _block("heave_surge_sway_speed", 21, ">fff", HeaveSurgeSwaySpeed),
163
+ _block("speed_vessel_frame", 22, ">fff", SpeedVesselFrame),
164
+ _block("acceleration_geographic_frame", 23, ">fff", AccelerationGeographicFrame),
165
+ _block("course_speed_over_ground", 24, ">ff", CourseSpeedOverGround),
166
+ _block("temperatures", 25, ">fff", Temperatures),
167
+ _block("attitude_quaternion", 26, ">ffff", AttitudeQuaternion),
168
+ _block("attitude_quaternion_deviation", 27, ">fff", AttitudeQuaternionDeviation),
169
+ _block("raw_acceleration_vessel_frame", 28, ">fff", RawAccelerationVesselFrame),
170
+ _block(
171
+ "acceleration_vessel_frame_deviation",
172
+ 29,
173
+ ">fff",
174
+ AccelerationVesselFrameDeviation,
175
+ ),
176
+ _block(
177
+ "rotation_rate_vessel_frame_deviation",
178
+ 30,
179
+ ">fff",
180
+ RotationRateVesselFrameDeviation,
181
+ ),
182
+ )
183
+ )
184
+
185
+ EXTENDED_BLOCKS = _sorted(
186
+ (
187
+ _block(
188
+ "rotation_acceleration_vessel_frame",
189
+ 0,
190
+ ">fff",
191
+ RotationAccelerationVesselFrame,
192
+ ),
193
+ _block(
194
+ "rotation_acceleration_vessel_frame_deviation",
195
+ 1,
196
+ ">fff",
197
+ RotationAccelerationVesselFrameDeviation,
198
+ ),
199
+ _block("raw_rotation_rate_vessel_frame", 2, ">fff", RawRotationRateVesselFrame),
200
+ _block("vehicle_attitude_heading", 3, ">fff", VehicleAttitudeHeading),
201
+ _block(
202
+ "vehicle_attitude_heading_deviation",
203
+ 4,
204
+ ">fff",
205
+ VehicleAttitudeHeadingDeviation,
206
+ ),
207
+ _block("vehicle_position", 5, ">ddBf", VehiclePosition),
208
+ _block("vehicle_position_deviation", 6, ">ffff", VehiclePositionDeviation),
209
+ )
210
+ )
211
+
212
+ EXTERNAL_BLOCKS = _sorted(
213
+ (
214
+ _block("utc", 0, ">IB", Utc),
215
+ _block("gnss1", 1, ">iBBddffffff", Gnss),
216
+ _block("gnss2", 2, ">iBBddffffff", Gnss),
217
+ _block("gnss_manual", 3, ">iBBddffffff", Gnss),
218
+ _block("emlog1", 4, ">iBff", Emlog),
219
+ _block("emlog2", 5, ">iBff", Emlog),
220
+ _block("usbl1", 6, ">iB8sddfffff", Usbl),
221
+ _block("usbl2", 7, ">iB8sddfffff", Usbl),
222
+ _block("usbl3", 8, ">iB8sddfffff", Usbl),
223
+ _block("depth", 9, ">iff", Depth),
224
+ _block("dvl_ground_speed1", 10, ">iBffffffff", DvlGroundSpeed),
225
+ _block("dvl_water_speed1", 11, ">iBfffffff", DvlWaterSpeed),
226
+ _block("sound_velocity", 12, ">if", SoundVelocity),
227
+ _block("dmi", 13, ">ii", Dmi),
228
+ _block("lbl1", 14, ">iB8sddfff", Lbl),
229
+ _block("lbl2", 15, ">iB8sddfff", Lbl),
230
+ _block("lbl3", 16, ">iB8sddfff", Lbl),
231
+ _block("lbl4", 17, ">iB8sddfff", Lbl),
232
+ _block("event_marker_a", 18, ">iBI", EventMarker),
233
+ _block("event_marker_b", 19, ">iBI", EventMarker),
234
+ _block("event_marker_c", 20, ">iBI", EventMarker),
235
+ _block("dvl_ground_speed2", 21, ">iBffffffff", DvlGroundSpeed),
236
+ _block("dvl_water_speed2", 22, ">iBfffffff", DvlWaterSpeed),
237
+ _block("turret_angles", 24, ">ifff", TurretAngles),
238
+ _block("vtg1", 25, ">iBfff", Vtg),
239
+ _block("vtg2", 26, ">iBfff", Vtg),
240
+ _block("log_book", 27, ">iI32s", LogBook),
241
+ )
242
+ )