pyfastnet 2.0.16__tar.gz → 2.0.17__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.
Files changed (34) hide show
  1. {pyfastnet-2.0.16/pyfastnet.egg-info → pyfastnet-2.0.17}/PKG-INFO +1 -1
  2. {pyfastnet-2.0.16 → pyfastnet-2.0.17}/fastnet_decoder/__init__.py +1 -1
  3. {pyfastnet-2.0.16 → pyfastnet-2.0.17}/fastnet_decoder/decode_fastnet.py +76 -0
  4. {pyfastnet-2.0.16 → pyfastnet-2.0.17}/fastnet_decoder/frame_buffer.py +17 -4
  5. {pyfastnet-2.0.16 → pyfastnet-2.0.17/pyfastnet.egg-info}/PKG-INFO +1 -1
  6. {pyfastnet-2.0.16 → pyfastnet-2.0.17}/LICENSE +0 -0
  7. {pyfastnet-2.0.16 → pyfastnet-2.0.17}/README.md +0 -0
  8. {pyfastnet-2.0.16 → pyfastnet-2.0.17}/fastnet_decoder/logger.py +0 -0
  9. {pyfastnet-2.0.16 → pyfastnet-2.0.17}/fastnet_decoder/mappings.py +0 -0
  10. {pyfastnet-2.0.16 → pyfastnet-2.0.17}/fastnet_decoder/utils.py +0 -0
  11. {pyfastnet-2.0.16 → pyfastnet-2.0.17}/pyfastnet.egg-info/SOURCES.txt +0 -0
  12. {pyfastnet-2.0.16 → pyfastnet-2.0.17}/pyfastnet.egg-info/dependency_links.txt +0 -0
  13. {pyfastnet-2.0.16 → pyfastnet-2.0.17}/pyfastnet.egg-info/requires.txt +0 -0
  14. {pyfastnet-2.0.16 → pyfastnet-2.0.17}/pyfastnet.egg-info/top_level.txt +0 -0
  15. {pyfastnet-2.0.16 → pyfastnet-2.0.17}/pyproject.toml +0 -0
  16. {pyfastnet-2.0.16 → pyfastnet-2.0.17}/setup.cfg +0 -0
  17. {pyfastnet-2.0.16 → pyfastnet-2.0.17}/tests/test_autopilot.py +0 -0
  18. {pyfastnet-2.0.16 → pyfastnet-2.0.17}/tests/test_awa.py +0 -0
  19. {pyfastnet-2.0.16 → pyfastnet-2.0.17}/tests/test_aws.py +0 -0
  20. {pyfastnet-2.0.16 → pyfastnet-2.0.17}/tests/test_battery.py +0 -0
  21. {pyfastnet-2.0.16 → pyfastnet-2.0.17}/tests/test_boatspeed.py +0 -0
  22. {pyfastnet-2.0.16 → pyfastnet-2.0.17}/tests/test_depth_frame.py +0 -0
  23. {pyfastnet-2.0.16 → pyfastnet-2.0.17}/tests/test_format07_msb_regression.py +0 -0
  24. {pyfastnet-2.0.16 → pyfastnet-2.0.17}/tests/test_format08_layout.py +0 -0
  25. {pyfastnet-2.0.16 → pyfastnet-2.0.17}/tests/test_heading.py +0 -0
  26. {pyfastnet-2.0.16 → pyfastnet-2.0.17}/tests/test_heel_trim.py +0 -0
  27. {pyfastnet-2.0.16 → pyfastnet-2.0.17}/tests/test_latlon_frame.py +0 -0
  28. {pyfastnet-2.0.16 → pyfastnet-2.0.17}/tests/test_sea_temp.py +0 -0
  29. {pyfastnet-2.0.16 → pyfastnet-2.0.17}/tests/test_sog.py +0 -0
  30. {pyfastnet-2.0.16 → pyfastnet-2.0.17}/tests/test_tidal.py +0 -0
  31. {pyfastnet-2.0.16 → pyfastnet-2.0.17}/tests/test_timer.py +0 -0
  32. {pyfastnet-2.0.16 → pyfastnet-2.0.17}/tests/test_twa.py +0 -0
  33. {pyfastnet-2.0.16 → pyfastnet-2.0.17}/tests/test_twd.py +0 -0
  34. {pyfastnet-2.0.16 → pyfastnet-2.0.17}/tests/test_tws.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyfastnet
3
- Version: 2.0.16
3
+ Version: 2.0.17
4
4
  Summary: A Python library for decoding FastNet protocol data streams.
5
5
  Author-email: Alex Salmon <alex@ivila.net>
6
6
  License: MIT License
@@ -1,4 +1,4 @@
1
- __version__ = "2.0.16"
1
+ __version__ = "2.0.17"
2
2
 
3
3
  from .frame_buffer import FrameBuffer
4
4
  from .decode_fastnet import decode_frame, decode_ascii_frame
@@ -1,4 +1,5 @@
1
1
  import datetime
2
+ import logging
2
3
  from .mappings import ADDRESS_LOOKUP, COMMAND_LOOKUP, CHANNEL_LOOKUP, FORMAT_SIZE_MAP
3
4
  from .mappings import SEGMENT_A, SEGMENT_B, AUTOPILOT_MODES
4
5
  from .logger import logger
@@ -109,6 +110,81 @@ def decode_frame(frame: bytes) -> dict:
109
110
  return {"error": "Decoding failure"}
110
111
 
111
112
 
113
+ def probe_frame(frame: bytes) -> None:
114
+ """
115
+ Speculatively unpack a non-broadcast frame using the same channel-record
116
+ assembly as Broadcast frames, for reverse-engineering only.
117
+
118
+ The body layout of non-broadcast commands (pilot messages, NMEA-sourced
119
+ data, etc.) is NOT known to match the Broadcast channel-record format, so
120
+ everything decoded here is a guess: results are logged at DEBUG and are
121
+ never queued or assigned to channels. The point is to eyeball whether the
122
+ same (channel_id, format_byte, data...) structure holds for these frames.
123
+ """
124
+ if not logger.isEnabledFor(logging.DEBUG):
125
+ return
126
+ try:
127
+ to_address = frame[0]
128
+ from_address = frame[1]
129
+ command = frame[3]
130
+ body = frame[5:-1]
131
+
132
+ to_name = ADDRESS_LOOKUP.get(to_address, f"Unknown (0x{to_address:02X})")
133
+ from_name = ADDRESS_LOOKUP.get(from_address, f"Unknown (0x{from_address:02X})")
134
+ cmd_name = COMMAND_LOOKUP.get(command, f"Unknown (0x{command:02X})")
135
+
136
+ logger.debug(
137
+ f" PROBE cmd={cmd_name} {to_name}←{from_name} body=[{body.hex()}]"
138
+ )
139
+
140
+ index = 0
141
+ while index < len(body):
142
+ if index + 1 >= len(body):
143
+ logger.debug(f" PROBE trailing byte [{body[index:].hex()}]")
144
+ break
145
+
146
+ channel_id = body[index]
147
+ format_byte = body[index + 1]
148
+ channel_name = CHANNEL_LOOKUP.get(channel_id, f"Unknown (0x{channel_id:02X})")
149
+ index += 2
150
+
151
+ data_length = FORMAT_SIZE_MAP.get(format_byte & 0x0F, 0)
152
+ if data_length == 0:
153
+ logger.debug(
154
+ f" PROBE 0x{channel_id:02X} {channel_name} "
155
+ f"fmt=0x{format_byte:02X} unknown format, stop "
156
+ f"remaining=[{body[index:].hex()}]"
157
+ )
158
+ break
159
+ if index + data_length > len(body):
160
+ logger.debug(
161
+ f" PROBE 0x{channel_id:02X} {channel_name} "
162
+ f"fmt=0x{format_byte:02X} incomplete need={data_length}B "
163
+ f"have={len(body) - index}B remaining=[{body[index:].hex()}]"
164
+ )
165
+ break
166
+
167
+ data_bytes = body[index:index + data_length]
168
+ index += data_length
169
+
170
+ decoded = decode_format_and_data(channel_id, format_byte, data_bytes)
171
+ if decoded:
172
+ logger.debug(
173
+ f" PROBE 0x{channel_id:02X} {channel_name} "
174
+ f"fmt=0x{format_byte:02X} data=[{data_bytes.hex()}] "
175
+ f"value={decoded['value']} display='{decoded['display_text']}' "
176
+ f"layout={decoded['layout']}"
177
+ )
178
+ else:
179
+ logger.debug(
180
+ f" PROBE 0x{channel_id:02X} {channel_name} "
181
+ f"fmt=0x{format_byte:02X} data=[{data_bytes.hex()}] (no decode)"
182
+ )
183
+
184
+ except Exception as e:
185
+ logger.debug(f" PROBE error: {e} [{frame.hex()}]")
186
+
187
+
112
188
  def decode_ascii_frame(frame: bytes) -> dict:
113
189
  try:
114
190
  to_address = frame[0]
@@ -1,7 +1,7 @@
1
1
  import logging
2
2
  from .utils import calculate_checksum
3
3
  from .mappings import COMMAND_LOOKUP, IGNORED_COMMANDS
4
- from .decode_fastnet import decode_frame, decode_ascii_frame
4
+ from .decode_fastnet import decode_frame, decode_ascii_frame, probe_frame
5
5
  from .logger import logger
6
6
  from queue import Queue, Full
7
7
 
@@ -84,9 +84,22 @@ class FrameBuffer:
84
84
  self.decode_and_queue_frame(frame, command_name)
85
85
 
86
86
  def decode_and_queue_frame(self, frame, command_name):
87
- """Decode a frame and add it to the queue if valid."""
88
- decoder = decode_ascii_frame if command_name == "LatLon" else decode_frame
89
- decoded_frame = decoder(frame)
87
+ """
88
+ Decode a frame and add it to the queue if valid.
89
+
90
+ Only Broadcast frames carry the channel-record body layout, and LatLon
91
+ frames carry ASCII; those are decoded and queued. Every other command
92
+ (pilot messages, NMEA-sourced data, etc.) has an unknown body structure,
93
+ so it is never queued — it is only probed speculatively for debugging.
94
+ """
95
+ if command_name == "Broadcast":
96
+ decoded_frame = decode_frame(frame)
97
+ elif command_name == "LatLon":
98
+ decoded_frame = decode_ascii_frame(frame)
99
+ else:
100
+ probe_frame(frame)
101
+ return
102
+
90
103
  if decoded_frame and "values" in decoded_frame:
91
104
  try:
92
105
  self.frame_queue.put_nowait(decoded_frame)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyfastnet
3
- Version: 2.0.16
3
+ Version: 2.0.17
4
4
  Summary: A Python library for decoding FastNet protocol data streams.
5
5
  Author-email: Alex Salmon <alex@ivila.net>
6
6
  License: MIT License
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes