sidescanpy.io.jsf.targets#
EdgeTech Discover SQLite .target catalog reader.
Discover writes one SQLite database per target pick (Target-NNNN.target),
holding a raw JSF ping-sample window plus position metadata. Schema
(Target, PingChannel, MutableTarget, MutableTag tables) was
reverse-engineered by hand and verified against real Discover target files.
Known gotchas this reader accounts for:
Target.TargetIdis a per-file primary key that always resets to 1 — never a cross-file identifier. The real identity is the filename’s own number (Target-NNNN.target), used asTarget.target_idhere.PingChannel’s ~200-row window is usually a clean contiguous run ofPingSequenceNumbervalues, but not always. One real file seen so far (Target-0002.target) has two things wrong at once: (1) a uint32 rollover artifact — 104 of its rows, and the target’s own ``MutableTarget.PingSequenceNumber``, are stored astrue_value + 2**32(e.g. raw4294971779for a true4483, confirmed by the corrected value’s neighbors forming a clean contiguous run); (2) an unrelated, disjoint cluster of 94 rows (4846-4939) mixed into the same file’sPingChanneltable, nowhere near the target’s real window. Correcting the rollover and then walking outward from the target’s own (corrected) ping to find its contiguous run handles both at once — no fixed distance threshold from the target’s raw ping is reliable here, since the target’s own raw value is exactly what’s corrupted.position_confidencedowngrades to"approximate"(recording the exclusion inextra_attributes) whenever this filtering actually removes rows.MutableTarget.Description/MutableTag.Tagare commonly empty — this is normal (no classification was ever recorded), not a read failure;labelstaysNonerather than a placeholder string.
Position formula (validated visually against 6+ real targets): within the ping window sorted by
sequence number, the target’s row is the index of its own
PingSequenceNumber; its column is
round(SlantRangeDistanceMeters / PingMetersPerSample) using the
target’s own ping’s PingMetersPerSample (not a window-wide constant,
though it typically doesn’t vary within one window).
Decode (decode_target_window(); raw values, native orientation –
TVG and display orientation live in export.target_window): SampleBytes -> np.frombuffer(dtype="<i2").astype(float) *
2 ** (-JsfWeightingFactor) — real JSF sample amplitudes, not a rendered
image. Expect a leading run of exact-zero samples (blanking/ring-down);
not a bug.
2026 rshomberg@uri.edu
Contents
Read every |
|
Re-decode the raw ping-sample window for |
- sidescanpy.io.jsf.targets.read_discover_targets(directory)#
Read every
Target-NNNN.targetfile indirectory.Returns one
Targetper file, sorted by filename. Files that fail to parse are skipped with a warning rather than aborting the whole batch (matches the family’s “clip/skip rather than error on one bad row” convention elsewhere in this library).
- sidescanpy.io.jsf.targets.decode_target_window(target)#
Re-decode the raw ping-sample window for
targetfrom its source SQLite catalog.Returns a
(n_pings, n_samples)float array of real JSF sample amplitudes (not a rendered/processed image) — samples per ping are truncated to the shortest row in the window, in the rare case they differ. Rowtarget.extra_attributes["window_row"]is the target’s own ping within this array; columntarget.sampleis its computed position within each ping.