pre merge cleanup
This commit is contained in:
+42
-13
@@ -60,7 +60,12 @@ PREC_FLAG_BG_SUB = 0x01
|
||||
CACH_MAGIC = b"CACH"
|
||||
CACH_HDR_FMT = ">4sBB" # magic, cach_version, block_flags
|
||||
CACH_HDR_SIZE = struct.calcsize(CACH_HDR_FMT)
|
||||
CACH_VERSION = 1
|
||||
# v2 added the SFFT pad factor. Writers always emit v2; readers still accept
|
||||
# v1, whose FFT block is natural-resolution by construction. An older reader
|
||||
# meeting a v2 tail rejects the whole section on the version check and
|
||||
# recomputes — no cache, but no misreading either.
|
||||
CACH_VERSION = 2
|
||||
CACH_VERSION_MIN = 1
|
||||
CACH_FLAG_DC = 0x01
|
||||
CACH_FLAG_FFT = 0x02
|
||||
|
||||
@@ -69,9 +74,12 @@ SDCB_HDR_FMT = ">4sBH" # magic, reserved, n_stored
|
||||
SDCB_HDR_SIZE = struct.calcsize(SDCB_HDR_FMT)
|
||||
|
||||
SFFT_MAGIC = b"SFFT"
|
||||
SFFT_HDR_FMT = ">4sBH" # magic, flags, n_stored
|
||||
SFFT_HDR_FMT_V1 = ">4sBH" # magic, flags, n_stored
|
||||
SFFT_HDR_FMT = ">4sBHH" # magic, flags, n_stored, pad_factor
|
||||
SFFT_HDR_SIZE = struct.calcsize(SFFT_HDR_FMT)
|
||||
SFFT_FLAG_BG_SUB = 0x01
|
||||
# The viewer clamps its pad factor to this, and the field is a uint16.
|
||||
PAD_FACTOR_MAX = 256
|
||||
|
||||
# Fixed channel indices into the .sras data array (CH1=RF, CH3/CH4=Bias DC)
|
||||
CH1_IDX, CH3_IDX, CH4_IDX = 0, 1, 2
|
||||
@@ -169,7 +177,9 @@ class SrasFile:
|
||||
``precomputed_dc3_mv`` / ``precomputed_dc4_mv`` / ``precomputed_freq_mhz``,
|
||||
always as ragged per-angle lists (``list[np.ndarray | None]``, one entry
|
||||
per angle, ``None`` where that angle was never stored) regardless of
|
||||
source version.
|
||||
source version. The settings the FFT images were computed under travel
|
||||
with them as ``precomputed_bg_sub`` / ``precomputed_pad_factor``, since a
|
||||
stored image is only usable by a view asking for the same two.
|
||||
"""
|
||||
|
||||
def __init__(self, path: str):
|
||||
@@ -226,6 +236,9 @@ class SrasFile:
|
||||
self.precomputed_dc4_mv: list[np.ndarray | None] = [None] * n_angles
|
||||
self.precomputed_dc3_mv: list[np.ndarray | None] = [None] * n_angles
|
||||
self.precomputed_bg_sub: bool = False
|
||||
# Zero-padding the stored FFT images were computed at; 1 = natural
|
||||
# resolution, which is all a v5 PREC or CACH v1 tail can hold.
|
||||
self.precomputed_pad_factor: int = 1
|
||||
|
||||
def cached_dc_mv(self, angle_idx: int, ch_idx: int) -> np.ndarray | None:
|
||||
"""A stored DC image (already in mV) for (angle, channel), or None."""
|
||||
@@ -496,16 +509,18 @@ class SrasFile:
|
||||
return end
|
||||
|
||||
def _read_cache_block(self, f, hdr_fmt: str, magic: bytes,
|
||||
stores: list[list]) -> int | None:
|
||||
stores: list[list]) -> tuple | None:
|
||||
"""Read one CACH sub-block header, then its per-angle image entries
|
||||
into *stores* (one list per image the block stores per angle).
|
||||
|
||||
Returns the header's flags byte, or None if the block is malformed.
|
||||
Every sub-block header is (magic, flags, n_stored, *extras). Returns
|
||||
everything after the magic, so a caller that has extras knows how to
|
||||
read them, or None if the block is malformed.
|
||||
"""
|
||||
raw = f.read(struct.calcsize(hdr_fmt))
|
||||
if len(raw) < struct.calcsize(hdr_fmt):
|
||||
return None
|
||||
block_magic, flags, n_stored = struct.unpack(hdr_fmt, raw)
|
||||
block_magic, flags, n_stored, *extras = struct.unpack(hdr_fmt, raw)
|
||||
if block_magic != magic:
|
||||
return None
|
||||
for _ in range(n_stored):
|
||||
@@ -515,7 +530,7 @@ class SrasFile:
|
||||
shape = self.image_shape(angle_idx)
|
||||
for store in stores:
|
||||
store[angle_idx] = _read_f32_image(f, shape)
|
||||
return flags
|
||||
return (flags, *extras)
|
||||
|
||||
def _parse_cach_section(self, offset: int):
|
||||
"""Parse the v7 CACH tail that holds precomputed DC/FFT images."""
|
||||
@@ -525,7 +540,8 @@ class SrasFile:
|
||||
if len(header_raw) < CACH_HDR_SIZE:
|
||||
return
|
||||
magic, cach_version, block_flags = struct.unpack(CACH_HDR_FMT, header_raw)
|
||||
if magic != CACH_MAGIC or cach_version != CACH_VERSION:
|
||||
if (magic != CACH_MAGIC
|
||||
or not CACH_VERSION_MIN <= cach_version <= CACH_VERSION):
|
||||
return
|
||||
|
||||
if block_flags & CACH_FLAG_DC:
|
||||
@@ -535,17 +551,23 @@ class SrasFile:
|
||||
return
|
||||
|
||||
if block_flags & CACH_FLAG_FFT:
|
||||
flags = self._read_cache_block(
|
||||
f, SFFT_HDR_FMT, SFFT_MAGIC, [self.precomputed_freq_mhz])
|
||||
if flags is None:
|
||||
# v1 has no pad field: those images are natural-resolution.
|
||||
hdr_fmt = SFFT_HDR_FMT if cach_version >= 2 else SFFT_HDR_FMT_V1
|
||||
header = self._read_cache_block(
|
||||
f, hdr_fmt, SFFT_MAGIC, [self.precomputed_freq_mhz])
|
||||
if header is None:
|
||||
return
|
||||
flags, *extras = header
|
||||
self.precomputed_bg_sub = bool(flags & SFFT_FLAG_BG_SUB)
|
||||
self.precomputed_pad_factor = (
|
||||
min(max(1, extras[0]), PAD_FACTOR_MAX) if extras else 1)
|
||||
|
||||
def write_v7_cache(self, *,
|
||||
new_dc3_mv: list[np.ndarray | None] | None = None,
|
||||
new_dc4_mv: list[np.ndarray | None] | None = None,
|
||||
new_freq_mhz: list[np.ndarray | None] | None = None,
|
||||
new_bg_sub: bool | None = None):
|
||||
new_bg_sub: bool | None = None,
|
||||
new_pad_factor: int | None = None):
|
||||
"""Store computed DC and/or FFT images into this file's CACH tail,
|
||||
in place, converting a v6 source to v7 (or updating an existing v7
|
||||
file). Only the block(s) passed in are recomputed; whichever block
|
||||
@@ -565,6 +587,11 @@ class SrasFile:
|
||||
final_dc4 = new_dc4_mv if new_dc4_mv is not None else self.precomputed_dc4_mv
|
||||
final_freq = new_freq_mhz if new_freq_mhz is not None else self.precomputed_freq_mhz
|
||||
final_bg_sub = new_bg_sub if new_bg_sub is not None else self.precomputed_bg_sub
|
||||
final_pad = (new_pad_factor if new_pad_factor is not None
|
||||
else self.precomputed_pad_factor)
|
||||
if not 1 <= final_pad <= PAD_FACTOR_MAX:
|
||||
raise ValueError(
|
||||
f"pad factor {final_pad} outside 1..{PAD_FACTOR_MAX}")
|
||||
|
||||
dc_entries = [a for a in range(self.n_angles) if final_dc3[a] is not None]
|
||||
fft_entries = [a for a in range(self.n_angles) if final_freq[a] is not None]
|
||||
@@ -584,7 +611,8 @@ class SrasFile:
|
||||
|
||||
if fft_entries:
|
||||
fft_flags = SFFT_FLAG_BG_SUB if final_bg_sub else 0
|
||||
payload += struct.pack(SFFT_HDR_FMT, SFFT_MAGIC, fft_flags, len(fft_entries))
|
||||
payload += struct.pack(SFFT_HDR_FMT, SFFT_MAGIC, fft_flags,
|
||||
len(fft_entries), final_pad)
|
||||
for a in fft_entries:
|
||||
payload += struct.pack(">H", a)
|
||||
payload += final_freq[a].astype(">f4").tobytes()
|
||||
@@ -612,6 +640,7 @@ class SrasFile:
|
||||
self.precomputed_dc4_mv = final_dc4
|
||||
self.precomputed_freq_mhz = final_freq
|
||||
self.precomputed_bg_sub = final_bg_sub
|
||||
self.precomputed_pad_factor = final_pad
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Axes helpers
|
||||
|
||||
Reference in New Issue
Block a user