pys7k 0.1.0__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 (42) hide show
  1. pys7k-0.1.0/LICENSE +28 -0
  2. pys7k-0.1.0/MANIFEST.in +2 -0
  3. pys7k-0.1.0/PKG-INFO +358 -0
  4. pys7k-0.1.0/PYS7K/__init__.py +22 -0
  5. pys7k-0.1.0/PYS7K/__main__.py +7 -0
  6. pys7k-0.1.0/PYS7K/bitfields.py +193 -0
  7. pys7k-0.1.0/PYS7K/derived.py +500 -0
  8. pys7k-0.1.0/PYS7K/fields.py +387 -0
  9. pys7k-0.1.0/PYS7K/frame.py +207 -0
  10. pys7k-0.1.0/PYS7K/kluster.py +641 -0
  11. pys7k-0.1.0/PYS7K/optional_data.py +143 -0
  12. pys7k-0.1.0/PYS7K/records/__init__.py +8 -0
  13. pys7k-0.1.0/PYS7K/records/base.py +261 -0
  14. pys7k-0.1.0/PYS7K/records/legacy.py +80 -0
  15. pys7k-0.1.0/PYS7K/records/sbes.py +192 -0
  16. pys7k-0.1.0/PYS7K/records/sensors.py +357 -0
  17. pys7k-0.1.0/PYS7K/records/sonar.py +688 -0
  18. pys7k-0.1.0/PYS7K/records/status.py +438 -0
  19. pys7k-0.1.0/PYS7K/records/system.py +117 -0
  20. pys7k-0.1.0/PYS7K/records/watercolumn.py +828 -0
  21. pys7k-0.1.0/PYS7K/registry.py +318 -0
  22. pys7k-0.1.0/PYS7K/s7k.py +816 -0
  23. pys7k-0.1.0/PYS7K/verification.py +124 -0
  24. pys7k-0.1.0/README.md +315 -0
  25. pys7k-0.1.0/pyproject.toml +3 -0
  26. pys7k-0.1.0/pys7k.egg-info/PKG-INFO +358 -0
  27. pys7k-0.1.0/pys7k.egg-info/SOURCES.txt +40 -0
  28. pys7k-0.1.0/pys7k.egg-info/dependency_links.txt +1 -0
  29. pys7k-0.1.0/pys7k.egg-info/entry_points.txt +2 -0
  30. pys7k-0.1.0/pys7k.egg-info/requires.txt +2 -0
  31. pys7k-0.1.0/pys7k.egg-info/top_level.txt +1 -0
  32. pys7k-0.1.0/requirements.txt +2 -0
  33. pys7k-0.1.0/setup.cfg +4 -0
  34. pys7k-0.1.0/setup.py +54 -0
  35. pys7k-0.1.0/tests/helpers.py +40 -0
  36. pys7k-0.1.0/tests/test_derived_kluster.py +240 -0
  37. pys7k-0.1.0/tests/test_fields_frame.py +125 -0
  38. pys7k-0.1.0/tests/test_real_data.py +135 -0
  39. pys7k-0.1.0/tests/test_real_data_writers.py +277 -0
  40. pys7k-0.1.0/tests/test_records.py +335 -0
  41. pys7k-0.1.0/tests/test_records_phase23.py +350 -0
  42. pys7k-0.1.0/tests/test_s7k.py +394 -0
pys7k-0.1.0/LICENSE ADDED
@@ -0,0 +1,28 @@
1
+ BSD 3-Clause License
2
+
3
+ Copyright (c) 2026, Val Schmidt, University of New Hampshire
4
+
5
+ Redistribution and use in source and binary forms, with or without
6
+ modification, are permitted provided that the following conditions are met:
7
+
8
+ 1. Redistributions of source code must retain the above copyright notice, this
9
+ list of conditions and the following disclaimer.
10
+
11
+ 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ this list of conditions and the following disclaimer in the documentation
13
+ and/or other materials provided with the distribution.
14
+
15
+ 3. Neither the name of the copyright holder nor the names of its
16
+ contributors may be used to endorse or promote products derived from
17
+ this software without specific prior written permission.
18
+
19
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,2 @@
1
+ include LICENSE README.md requirements.txt
2
+ recursive-include tests *.py
pys7k-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,358 @@
1
+ Metadata-Version: 2.4
2
+ Name: pys7k
3
+ Version: 0.1.0
4
+ Summary: Pure-Python library and command line utility for indexing, reading, and writing Teledyne RESON s7k sonar data files.
5
+ Home-page: https://github.com/valschmidt/pys7k
6
+ Author: Val Schmidt
7
+ Author-email: Val.Schmidt@unh.edu
8
+ License: BSD-3-Clause
9
+ Project-URL: Source, https://github.com/valschmidt/pys7k
10
+ Project-URL: Issue Tracker, https://github.com/valschmidt/pys7k/issues
11
+ Keywords: hydrography multibeam sonar teledyne reson s7k 7k
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Intended Audience :: Science/Research
14
+ Classifier: Natural Language :: English
15
+ Classifier: Operating System :: OS Independent
16
+ Classifier: Programming Language :: Python
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.9
19
+ Classifier: Programming Language :: Python :: 3.10
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Programming Language :: Python :: 3.13
23
+ Classifier: Topic :: Scientific/Engineering :: GIS
24
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
25
+ Requires-Python: >=3.9
26
+ Description-Content-Type: text/markdown
27
+ License-File: LICENSE
28
+ Requires-Dist: pandas
29
+ Requires-Dist: numpy
30
+ Dynamic: author
31
+ Dynamic: author-email
32
+ Dynamic: classifier
33
+ Dynamic: description
34
+ Dynamic: description-content-type
35
+ Dynamic: home-page
36
+ Dynamic: keywords
37
+ Dynamic: license
38
+ Dynamic: license-file
39
+ Dynamic: project-url
40
+ Dynamic: requires-dist
41
+ Dynamic: requires-python
42
+ Dynamic: summary
43
+
44
+ <img src="https://raw.githubusercontent.com/valschmidt/pys7k/main/docs/images/ccom-jhc-logo.png" alt="UNH Center for Coastal and Ocean Mapping / Joint Hydrographic Center" width="96" align="right">
45
+
46
+ # pys7k
47
+
48
+ pys7k is a pure-Python library and command line utility for reading and
49
+ writing Teledyne RESON s7k sonar files. It follows the 7k Data Format
50
+ Definition (DFD) version 3.10. It needs only numpy and pandas.
51
+
52
+ pys7k has two goals.
53
+
54
+ * **Forensic accuracy.** Records are reported exactly as they are stored:
55
+ DFD field names, native units, raw bit fields, reserved fields and all.
56
+ Every record can be written back byte for byte, including record types
57
+ pys7k does not understand.
58
+ * **Ease of use.** Converted values (degrees, travel times, datetimes,
59
+ named flags, water column images) come from a separate set of batch
60
+ functions in `PYS7K.derived`. They never change the raw records.
61
+
62
+ ## Installation
63
+
64
+ ```
65
+ pip install pys7k
66
+ ```
67
+
68
+ ## Command line
69
+
70
+ `s7k.py -f FILE -V` indexes a file and prints a table of its record types.
71
+ "Decoded" says whether a type is decoded field by field, and "Real Data"
72
+ whether its decoder has been checked against real files. Add `-c` to
73
+ verify every record's checksum.
74
+
75
+ ```
76
+ $ s7k.py -f uam_7125_7027_7028_nav.s7k -V -c
77
+ File: uam_7125_7027_7028_nav.s7k
78
+ File size: 356131 bytes
79
+ Total records: 65
80
+ Protocol version(s): 5
81
+ Devices: 0/0 (Unspecified, 1 records), 7000/0 (7k sonar source, 4 records), 7003/0 (Teledyne PDS, 25 records), 7125/0 (SeaBat 7125, 35 records)
82
+ Time span: 2012-11-28T19:09:22.014999+00:00 to 2012-11-28T19:35:56.467998+00:00 (1594.5 s)
83
+
84
+ Name Count Total Bytes Min Bytes Max Bytes % of File Decoded Real Data Checksum OK Checksum Bad No Checksum
85
+ RecordType
86
+ 7028 Snippet Data 5 113078 22598 22642 31.75 yes yes 5 0 0
87
+ 7017 Detection Data Setup 5 87858 17558 17592 24.67 yes yes 5 0 0
88
+ 7027 Raw Detection Data 5 57089 11409 11431 16.03 yes yes 5 0 0
89
+ ...
90
+ 7777 Filler (file repair) 1 390 390 390 0.11 raw 0 0 1
91
+ ```
92
+
93
+ `s7k.py -f FILE -p [RECORDTYPE]` prints records as text for debugging. The
94
+ record type may be a number or a name. `-n` limits the number of records
95
+ and `-r` limits the rows printed for each table. Lines starting with `#`
96
+ are annotations, not stored values.
97
+
98
+ ```
99
+ $ s7k.py -f uam_7125_7027_7028_nav.s7k -p 1003 -n 1
100
+ === 1003 Position record=28 offset=143796 size=104 ===
101
+ -- Frame --
102
+ ProtocolVersion : 5
103
+ Offset : 60
104
+ ...
105
+ DeviceIdentifier : 7003
106
+ Flags : 1
107
+ ...
108
+ # time: 2012-11-28T19:34:20.691999+00:00
109
+ # device: Teledyne PDS
110
+ -- RTH --
111
+ DatumIdentifier : 0
112
+ Latency : 0.0
113
+ LatitudeOrNorthing : 0.7518556113456243
114
+ LongitudeOrEasting : -1.234361627814439
115
+ HeightRelativeToDatumOrHeight : 0.0
116
+ PositionTypeFlag : 0
117
+ UTMZone : 0
118
+ QualityFlag : 0
119
+ PositioningMethod : 0
120
+ NumberOfSatellites : None
121
+ -- Checksum: 4663 # verifies
122
+ ```
123
+
124
+ `NumberOfSatellites` is `None` because this 7125 file writes the older
125
+ 36-byte 1003 record, which ends before that field.
126
+
127
+ Other options:
128
+
129
+ * `-i` prints the full record index as CSV.
130
+ * `--catalog` compares the file's 7300 catalog with the index.
131
+ * Record types with no decoder are printed as ASCII text, with each
132
+ non-printable byte shown as `.`.
133
+
134
+ ## Library
135
+
136
+ ### Opening and indexing
137
+
138
+ ```python
139
+ from PYS7K import s7k
140
+
141
+ S = s7k("uam_7125_7027_7028_nav.s7k")
142
+ S.index_file()
143
+ print(S.Index.head(3))
144
+ ```
145
+
146
+ ```
147
+ Offset Size RecordType DeviceId SystemEnumerator Time ProtocolVersion RecordVersion Flags OptionalDataOffset OptionalDataIdentifier
148
+ 0 0 402 7200 7000 0 1.354131e+09 5 1 0 386 7300
149
+ 1 402 100 7022 7000 0 1.354130e+09 5 1 1 0 0
150
+ 2 502 3944 7001 7000 0 1.354130e+09 5 1 1 0 0
151
+ ```
152
+
153
+ The file is memory-mapped. Indexing walks the record frames with one
154
+ precompiled `struct` unpack per record, then gathers every frame field at
155
+ once with numpy. A 1 GB file indexes in about 0.05 s.
156
+
157
+ `index_file(full=True)` adds `PingNumber` and `MultiPingSequence` columns
158
+ (-1 where a record has none). `index_file(verify_checksums=True)` adds a
159
+ `ChecksumValid` column. It is missing (NA) for records whose frame says no
160
+ checksum is present. Both together take about 0.25 s per GB.
161
+
162
+ A damaged file does not stop indexing. pys7k skips to the next sync
163
+ pattern and lists each skipped byte range in `S.Gaps`. Use
164
+ `index_file(strict=True)` to raise an error instead.
165
+
166
+ ### Reading records
167
+
168
+ ```python
169
+ for rec in S.iter_records(7027):
170
+ ...
171
+ ```
172
+
173
+ `iter_records` accepts record types (numbers or names), devices, and a
174
+ time range. Each record is a dictionary:
175
+
176
+ ```python
177
+ rec = next(S.iter_records(7027))
178
+ {k: type(v).__name__ for k, v in rec.items()}
179
+ ```
180
+
181
+ ```
182
+ {'RecordType': 'int', 'Frame': 'dict', 'RTH': 'dict', 'RD': 'dict',
183
+ 'OptionalData': 'NoneType', 'Unparsed': 'dict', 'Checksum': 'int',
184
+ 'ChecksumValid': 'bool'}
185
+ ```
186
+
187
+ * `rec["Frame"]` holds every field of the 64-byte Data Record Frame.
188
+ * `rec["RTH"]` holds the record type header.
189
+ * `rec["RD"]` holds the record data. Repeated structures are dictionaries
190
+ of numpy arrays, for example `rec["RD"]["Detections"]["DetectionPoint"]`.
191
+ * `rec["OptionalData"]` holds the optional data (DFD Appendix A), or `None`.
192
+ * `rec["Unparsed"]` holds any bytes past the known layout. For record types
193
+ with no decoder, `rec["Unparsed"]["Body"]` holds the whole body.
194
+ * `rec["Checksum"]` is the stored checksum, and `rec["ChecksumValid"]`
195
+ whether it verifies (None when Flags bit 0 says there is no checksum).
196
+
197
+ Field sets follow the sizes the record declares. This 2012 file writes
198
+ 22-byte 7027 detections, so `Intensity`, `MinLimit` and `MaxLimit` are
199
+ absent:
200
+
201
+ ```python
202
+ rec["RTH"]["DataFieldSize"], sorted(rec["RD"]["Detections"])
203
+ ```
204
+
205
+ ```
206
+ (22, ['BeamDescriptor', 'DetectionPoint', 'Flags', 'Quality', 'RxAngle', 'Uncertainty'])
207
+ ```
208
+
209
+ Every record type documented in DFD 3.10 has a decoder. 7038 (raw IQ
210
+ element data) also has one, although DFD 3.10 lists it as reserved. Its
211
+ layout comes from the CCOM 7125 MATLAB reader and was checked against 7125
212
+ files.
213
+
214
+ ### Converted values
215
+
216
+ `PYS7K.derived` computes values for many records at once, which is much
217
+ faster than converting each record as it is read.
218
+
219
+ ```python
220
+ from PYS7K import derived as D
221
+
222
+ d = D.read_detections(S, degrees=True)
223
+ d["TwoWayTravelTime"].shape, d["TwoWayTravelTime"][0, :3], d["RxAngle"][0, :3]
224
+ ```
225
+
226
+ ```
227
+ ((5, 512), array([0.02279599, 0.02266362, 0.02254943]), array([-60.30000407, -60.20000327, -60.10000247]))
228
+ ```
229
+
230
+ Available functions:
231
+
232
+ | Function | Records | Returns |
233
+ |---|---|---|
234
+ | `read_attitude` | 1012, 1013, 1016 | Attitude and heading arrays by source |
235
+ | `read_navigation` | 1003, 1015 | Positions (degrees only where geographic) |
236
+ | `read_detections` | 7027 | Ping-by-detection arrays, travel times, flag fields |
237
+ | `read_bathymetry_7006` | 7006 | Ping-by-beam arrays |
238
+ | `read_beam_geometry` | 7004 | Ping-by-beam angles |
239
+ | `read_runtime` | 7503 or 7000 | A DataFrame of settings |
240
+ | `read_svp`, `read_sound_velocity` | 1009, 7610 | Profiles and surface sound velocity |
241
+ | `read_installation` | 7030 | Installation parameters |
242
+ | `read_snippets` | 7028 | Each beam's snippet |
243
+ | `read_sidescan` | 7007, 7057 | Port and starboard series |
244
+ | `read_watercolumn` | 7008, 7018, 7042, 7048 | A samples-by-beams image |
245
+ | `gather_header` | any header-only record | Every header field, read straight from the file |
246
+ | `record_times` | any | Frame times as seconds and datetimes |
247
+
248
+ `PYS7K.bitfields.decode_bits` splits bit fields into named parts, for
249
+ example `decode_bits(rec["RTH"]["ControlFlags"], CONTROL_FLAGS_7000)`.
250
+
251
+ ### Writing
252
+
253
+ ```python
254
+ from PYS7K import new, s7k
255
+
256
+ out = s7k("copy.s7k")
257
+ out.OpenFiletoWrite()
258
+ for rec in S.iter_records():
259
+ out.write_record(rec)
260
+ out.closeFile(catalog=True)
261
+ ```
262
+
263
+ `new(record_type)` returns a template record with every field set to zero.
264
+ Fill it in and pass it to `write_record`. The encoder recomputes the
265
+ frame's Size and offsets. The checksum is computed when Flags bit 0 is set
266
+ and written as stored otherwise. A record read with a checksum that did not
267
+ verify keeps its stored checksum, so unmodified records always come back
268
+ byte for byte. Use `write_record(rec, "recompute")` to repair a checksum.
269
+ `closeFile(catalog=True)` appends a 7300 file catalog and points the
270
+ 7200 file header at it.
271
+
272
+ ## Kluster
273
+
274
+ `PYS7K.kluster.X7kRead` can stand in for NOAA's
275
+ `HSTB.drivers.prr3.X7kRead`. It returns the same dictionary from
276
+ `sequential_read_records()` and keeps prr3's record preferences and
277
+ conversions. On the sample data its ping, attitude and navigation arrays
278
+ match prr3's to float precision. Its module docstring lists nine places
279
+ where it deliberately differs from prr3, for example converting the 7027
280
+ TxAngle to degrees like every other angle.
281
+
282
+ ## Notes on the DFD
283
+
284
+ pys7k is experimental. Its interfaces may change between releases. Please
285
+ check results against other tools before relying on them, and report any
286
+ discrepancies.
287
+
288
+ The decoders have been checked against real files from SeaBat 7101, 7111,
289
+ 7125, T20-P and T50-R sonars, NORBIT WBMS and STX sonars, and files written
290
+ by Reson RDR 1.2 and 2.0, 7kCenter 3.7 to 6.3, Teledyne PDS 2.3 to 3.9,
291
+ HYPACK and Fledermaus, dated 2005 to 2024. `PYS7K/verification.py` lists
292
+ the evidence for each record type, and the "Real Data" column of
293
+ `s7k.py -V` shows whether a file's record types have been checked this way.
294
+ Record types without real samples (most phase 3 records, the SBES records,
295
+ 7047 and others) have been tested with synthetic records
296
+ only.
297
+
298
+ These points were checked against real data.
299
+
300
+ * The checksum flag is Flags bit 0. The DFD's checksum row says bit 1,
301
+ a leftover from the protocol 2 and 3 frames (DFD 0.42, 0.50), which did
302
+ use bit 1. DFD 0.53 moved the flag to bit 0.
303
+ * The 7200, 7300, 7777 and 7038 records carry no checksum. Neither do 7008
304
+ (Reson recorders and HYPACK), 7011, 7018 or 7058 records, nor any NORBIT
305
+ record.
306
+ * The 7503 record has no ReceiveBeamWidth field. Its full 3.10 layout is
307
+ 260 bytes.
308
+ * 7k logger files leave the 7200 pointer to the 7300 catalog at zero, and
309
+ RDR 1.2 writes only 6 bytes of 7200 optional data.
310
+ * 7027 detections are 22, 26 or 34 bytes, depending on the writer: the
311
+ DFD 2.20-2.22, 2.31-2.41 and 3.x forms.
312
+
313
+ Writers do not always follow the DFD. pys7k reports what is stored, writes
314
+ it back byte for byte, and points out these cases:
315
+
316
+ * Wrong stored checksums. Records keep their stored checksum on writing
317
+ (`rec["ChecksumValid"]` is False), and `s7k.py -p` recognizes HYPACK's
318
+ habit of storing Size - 4, and PDS 7610 checksums computed before the
319
+ frame's Reserved4 field was filled in.
320
+ * Fledermaus navigation files have no checksum field at all. This is
321
+ detected per file (`S.ChecksumTrailer`).
322
+ * Teledyne PDS 7028 records place their optional data before the offset
323
+ the frame gives. `optional_data.recover_misplaced_optional_data` reads it.
324
+ * A 2011 file stores attitude in degrees, not radians.
325
+ `derived.read_attitude` warns about values that cannot be radians.
326
+ * Some records follow an older DFD rather than 3.10. pys7k picks the
327
+ layout from the record itself: the 44-byte 7041 header of DFD
328
+ 2.20-2.31 (7kCenter 5.2), and shorter 7002, 7610 and 1003 records that
329
+ end where the older DFDs ended them.
330
+ * Protocol version 2 files (2003) predate DFD 3.10. They are decoded with
331
+ the 72-byte frame and the 1003/1004 layouts of DFD 0.42.
332
+ * 2010 files store 7048 beam by beam with zero padding, and pad 7012 with
333
+ stale samples. No DFD describes either; both are detected and kept.
334
+
335
+ The older DFDs used for this are archived with MB-System
336
+ (<https://www.mbari.org/technology/mb-system/documentation/data-format-specifications/>).
337
+ `PYS7K/verification.py` lists what they explained and what they did not
338
+ (7026, 7777, 6-byte 7200 optional data, the PDS 7028 placement).
339
+
340
+ ## Tests
341
+
342
+ ```
343
+ python scripts/make_test_subsets.py --defaults # optional: cut real-data test files into data/
344
+ python scripts/make_test_subsets.py --writers --root DIR # optional: writer samples into data/writers/
345
+ pytest
346
+ ```
347
+
348
+ Most tests build synthetic records with the writer and always run. Tests
349
+ on real data use the small files in `data/` and are skipped when that
350
+ folder is empty. `make_test_subsets.py --writers` also copies samples of
351
+ files from many writers and systems, named in a local list
352
+ (`data/writer_samples.json`, not in the repository), into `data/writers/`,
353
+ record for record, so their quirks are kept;
354
+ `tests/test_real_data_writers.py` checks each one.
355
+
356
+ ## License
357
+
358
+ BSD 3-Clause. See LICENSE.
@@ -0,0 +1,22 @@
1
+ """
2
+ pys7k: a pure-Python reader and writer for Teledyne RESON s7k sonar files
3
+ (7k Data Format Definition 3.10).
4
+ """
5
+
6
+ from .frame import (FLAG_CHECKSUM_VALID, FLAG_RECORDED_DATA, S7KChecksumError,
7
+ S7KError, S7KFormatError, checksum, epoch_to_time7k,
8
+ time7k_to_epoch)
9
+ from .registry import (DEVICE_IDENTIFIERS, RECORD_CODECS,
10
+ RECORD_TYPE_DESCRIPTIONS, decode_record, encode_record,
11
+ new, record_name)
12
+ from .s7k import main, resolve_record_type, s7k
13
+
14
+ __version__ = "0.1.0"
15
+
16
+ __all__ = [
17
+ "s7k", "main", "new", "decode_record", "encode_record", "record_name",
18
+ "resolve_record_type", "checksum", "time7k_to_epoch", "epoch_to_time7k",
19
+ "RECORD_CODECS", "RECORD_TYPE_DESCRIPTIONS", "DEVICE_IDENTIFIERS",
20
+ "FLAG_CHECKSUM_VALID", "FLAG_RECORDED_DATA",
21
+ "S7KError", "S7KFormatError", "S7KChecksumError",
22
+ ]
@@ -0,0 +1,7 @@
1
+ """Allow ``python -m PYS7K -f FILE ...``."""
2
+
3
+ import sys
4
+
5
+ from .s7k import main
6
+
7
+ sys.exit(main())
@@ -0,0 +1,193 @@
1
+ """
2
+ Named bit-field specifications from DFD 3.10, and a vectorized decoder.
3
+
4
+ Records keep bit fields as the integers stored in the file. These specs let
5
+ you expand them when you want named values::
6
+
7
+ from PYS7K.bitfields import decode_bits, CONTROL_FLAGS_7000
8
+ decode_bits(rec["RTH"]["ControlFlags"], CONTROL_FLAGS_7000)
9
+ {'AutoRangeMethod': 0, 'AutoBottomDetectionFilterMethod': 0, ...}
10
+
11
+ Each spec is a list of ``(name, first_bit, width)``. ``decode_bits`` works
12
+ on a scalar or on a numpy array (for example a column of per-detection
13
+ flags), returning a dictionary of the same shape.
14
+ """
15
+
16
+ import numpy as np
17
+
18
+ __all__ = [
19
+ "decode_bits",
20
+ "CONTROL_FLAGS_7000", "TRANSMIT_FLAGS_7000", "RECEIVE_FLAGS_7000",
21
+ "FLAGS_7006", "QUALITY_7006",
22
+ "FLAGS_7017", "DETECTION_FLAGS_7017",
23
+ "FLAGS_7027", "DETECTION_FLAGS_7027", "QUALITY_7027",
24
+ "FLAGS_7028", "DATA_SAMPLE_TYPE_7008", "FLAGS_7042",
25
+ "FRAME_FLAGS", "BIT_FIELDS",
26
+ ]
27
+
28
+
29
+ def decode_bits(value, spec):
30
+ """
31
+ Split an integer bit field (or an array of them) into named parts.
32
+
33
+ :param value: an int or an integer numpy array.
34
+ :param spec: a list of ``(name, first_bit, width)``.
35
+ :return: a dictionary mapping each name to an int, or to an array the
36
+ shape of ``value``.
37
+ """
38
+ scalar = np.isscalar(value)
39
+ v = np.asarray(value).astype(np.uint64)
40
+ out = {}
41
+ for name, bit, width in spec:
42
+ part = (v >> np.uint64(bit)) & np.uint64((1 << width) - 1)
43
+ out[name] = int(part) if scalar else part.astype(np.uint32 if width > 8 else np.uint8)
44
+ return out
45
+
46
+
47
+ # Table 5: DRF Flags. Bit 0 is the checksum flag (verified on real data;
48
+ # the DFD's checksum row says "bit 1", which is a typo).
49
+ FRAME_FLAGS = [
50
+ ("ChecksumValid", 0, 1),
51
+ ("RecordedData", 15, 1),
52
+ ]
53
+
54
+ # Table 39: 7000 Control flags (also used by 7503).
55
+ CONTROL_FLAGS_7000 = [
56
+ ("AutoRangeMethod", 0, 4),
57
+ ("AutoBottomDetectionFilterMethod", 4, 4),
58
+ ("BottomDetectionRangeFilterEnabled", 8, 1),
59
+ ("BottomDetectionDepthFilterEnabled", 9, 1),
60
+ ("ReceiverGainAutoGain", 10, 1),
61
+ ("ReceiverGainFixedGain", 11, 1),
62
+ ("ReceiverGainReserved", 12, 1),
63
+ ("Reserved13", 13, 1),
64
+ ("TriggerOutHighForRxDuration", 14, 1),
65
+ ("SystemActive", 15, 1),
66
+ ("ReservedBottomDetection", 16, 4),
67
+ ("PipeGatingFilter", 20, 1),
68
+ ("AdaptiveGateDepthFilterFixed", 21, 1),
69
+ ("AdaptiveGate", 22, 1),
70
+ ("AdaptiveGateDepthFilter", 23, 1),
71
+ ("TriggerOut", 24, 1),
72
+ ("TriggerInEdgeNegative", 25, 1),
73
+ ("PPSEdgeNegative", 26, 1),
74
+ ("TimestampState", 27, 2), # 0 n/a, 1 error, 2 warning, 3 ok
75
+ ("DepthFilterFollowsSeafloor", 29, 1),
76
+ ("ReducedCoverageForConstantSpacing", 30, 1),
77
+ ("Simulator", 31, 1),
78
+ ]
79
+
80
+ # Table 39: Transmit flags.
81
+ TRANSMIT_FLAGS_7000 = [
82
+ ("PitchStabilizationMethod", 0, 4),
83
+ ("YawStabilizationMethod", 4, 4),
84
+ ]
85
+
86
+ # Table 39: Receive flags.
87
+ RECEIVE_FLAGS_7000 = [
88
+ ("RollCompensation", 0, 1),
89
+ ("HeaveCompensation", 2, 1),
90
+ ("DynamicFocusingMethod", 4, 4),
91
+ ("DopplerCompensationMethod", 8, 4),
92
+ ("MatchFilteringMethod", 12, 4),
93
+ ("TVGMethod", 16, 4),
94
+ ("MultiPingMode", 20, 4),
95
+ ]
96
+
97
+ # Table 46: 7006 Flags, and Table 47: 7006 Quality.
98
+ FLAGS_7006 = [
99
+ ("LayerCompensation", 0, 1),
100
+ ("XYZCompensation", 1, 1),
101
+ ]
102
+ QUALITY_7006 = [
103
+ ("BrightnessPass", 0, 1),
104
+ ("ColinearityPass", 1, 1),
105
+ ("IntensityDetectionUsed", 2, 1),
106
+ ("PhaseDetectionUsed", 3, 1),
107
+ ("InternalUse", 4, 1),
108
+ ("PDSNadirFilterFail", 5, 1),
109
+ ]
110
+
111
+ # Table 60: 7017 Flags, and Table 61: per-detection Flags.
112
+ FLAGS_7017 = [
113
+ ("UserDepthFilterEnabled", 0, 1),
114
+ ("UserRangeFilterEnabled", 1, 1),
115
+ ("AutomaticFilterEnabled", 2, 1),
116
+ ("NadirSearchLimitsEnabled", 3, 1),
117
+ ("AutomaticWindowLimitsEnabled", 4, 1),
118
+ ("QualityFilterEnabled", 5, 1),
119
+ ("MultiDetectionEnabled", 6, 1),
120
+ ]
121
+ DETECTION_FLAGS_7017 = [
122
+ ("AutomaticLimitsValid", 0, 1),
123
+ ("UserDefinedLimitsValid", 1, 1),
124
+ ("QualityType", 2, 7),
125
+ ("QualityPassesUserCriteria", 9, 1),
126
+ ("MagnitudeDetect", 10, 1),
127
+ ("PhaseDetect", 11, 1),
128
+ ("DetectionPriority", 16, 4),
129
+ ]
130
+
131
+ # Table 71: 7027 Flags, Table 72: per-detection Flags, Table 73: Quality
132
+ # (when the quality type is 1).
133
+ FLAGS_7027 = [
134
+ ("UncertaintyMethod", 0, 4), # 0 none, 1 Rob Hare, 2 Ifremer
135
+ ("MultiDetectionEnabled", 4, 1),
136
+ ("HasSnippetsDetectionPointFlag", 6, 1),
137
+ ]
138
+ DETECTION_FLAGS_7027 = [
139
+ ("MagnitudeDetect", 0, 1),
140
+ ("PhaseDetect", 1, 1),
141
+ ("QualityType", 2, 7),
142
+ ("DetectionPriority", 9, 4),
143
+ ("NotUsedInSnippet", 14, 1),
144
+ ]
145
+ QUALITY_7027 = [
146
+ ("BrightnessPass", 0, 1),
147
+ ("ColinearityPass", 1, 1),
148
+ ]
149
+
150
+ # Table 74: 7028 Flags.
151
+ FLAGS_7028 = [("Samples32Bit", 0, 1)]
152
+
153
+ # Table 50: 7008 Data sample type.
154
+ DATA_SAMPLE_TYPE_7008 = [
155
+ ("Magnitude", 0, 4), # 0 none, 2 16-bit, 3 32-bit
156
+ ("Phase", 4, 4), # 0 none, 2 16-bit, 3 32-bit
157
+ ("IQ", 8, 4), # 0 none, 1 16-bit, 2 32-bit
158
+ ("ElementData", 12, 3), # 0 beamformed, 1 element data
159
+ ]
160
+
161
+ # Table 82: 7042 Flags.
162
+ FLAGS_7042 = [
163
+ ("UseMaximumBottomDetection", 0, 1),
164
+ ("MagnitudeOnly", 1, 1),
165
+ ("Magnitude8BitdB", 2, 1),
166
+ ("Reserved3", 3, 1),
167
+ ("DownsamplingDivisor", 4, 4),
168
+ ("DownsamplingType", 8, 4), # 0 none, 1 middle, 2 peak, 3 average
169
+ ("Data32Bit", 12, 1),
170
+ ("CompressionFactorAvailable", 13, 1),
171
+ ("SegmentNumbersAvailable", 14, 1),
172
+ ("FirstSampleContainsRxDelay", 15, 1),
173
+ ]
174
+
175
+ # Bit fields indexed by (record type, section, field name), for tools that
176
+ # want to expand every known bit field in a record.
177
+ BIT_FIELDS = {
178
+ (7000, "RTH", "ControlFlags"): CONTROL_FLAGS_7000,
179
+ (7000, "RTH", "TransmitFlags"): TRANSMIT_FLAGS_7000,
180
+ (7000, "RTH", "ReceiveFlags"): RECEIVE_FLAGS_7000,
181
+ (7503, "RTH", "ControlFlags"): CONTROL_FLAGS_7000,
182
+ (7503, "RTH", "TransmitFlags"): TRANSMIT_FLAGS_7000,
183
+ (7503, "RTH", "ReceiveFlags"): RECEIVE_FLAGS_7000,
184
+ (7006, "RTH", "Flags"): FLAGS_7006,
185
+ (7006, "Beams", "Quality"): QUALITY_7006,
186
+ (7008, "RTH", "DataSampleType"): DATA_SAMPLE_TYPE_7008,
187
+ (7017, "RTH", "Flags"): FLAGS_7017,
188
+ (7017, "Detections", "Flags"): DETECTION_FLAGS_7017,
189
+ (7027, "RTH", "Flags"): FLAGS_7027,
190
+ (7027, "Detections", "Flags"): DETECTION_FLAGS_7027,
191
+ (7028, "RTH", "Flags"): FLAGS_7028,
192
+ (7042, "RTH", "Flags"): FLAGS_7042,
193
+ }