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.
- pyixblue-0.9.1/.github/workflows/publish.yml +94 -0
- pyixblue-0.9.1/.gitignore +13 -0
- pyixblue-0.9.1/.python-version +1 -0
- pyixblue-0.9.1/LICENSE +21 -0
- pyixblue-0.9.1/PKG-INFO +108 -0
- pyixblue-0.9.1/README.md +77 -0
- pyixblue-0.9.1/pyproject.toml +21 -0
- pyixblue-0.9.1/src/pyixblue/__init__.py +15 -0
- pyixblue-0.9.1/src/pyixblue/blocks.py +242 -0
- pyixblue-0.9.1/src/pyixblue/decoder.py +187 -0
- pyixblue-0.9.1/src/pyixblue/errors.py +5 -0
- pyixblue-0.9.1/src/pyixblue/models.py +741 -0
- pyixblue-0.9.1/tests/fixtures/ATTRIBUTION.txt +1 -0
- pyixblue-0.9.1/tests/fixtures/stdbin_v2.bin +0 -0
- pyixblue-0.9.1/tests/fixtures/stdbin_v3.bin +0 -0
- pyixblue-0.9.1/tests/fixtures/stdbin_v4.bin +0 -0
- pyixblue-0.9.1/tests/fixtures/stdbin_v5.bin +0 -0
- pyixblue-0.9.1/tests/test_blocks.py +138 -0
- pyixblue-0.9.1/tests/test_decoder.py +970 -0
- pyixblue-0.9.1/uv.lock +86 -0
|
@@ -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 @@
|
|
|
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.
|
pyixblue-0.9.1/PKG-INFO
ADDED
|
@@ -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).
|
pyixblue-0.9.1/README.md
ADDED
|
@@ -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
|
+
)
|