Source code for at_py.readwrite.ts

"""Time-series reader (port of Matlab ``read_ts.m`` text path)."""

from __future__ import annotations

from dataclasses import dataclass

import numpy as np


[docs] @dataclass(frozen=True) class TsPosR: """Receiver depth vector for time-series output (meters, float64).""" z: np.ndarray # float64 receiver depths
[docs] @dataclass(frozen=True) class TsPos: """Nested position struct matching Matlab ``Pos`` (here only receiver depths).""" r: TsPosR
[docs] @dataclass(frozen=True) class TsReadResult: """ASCII time-series file: title, receiver depths, time vector, and RTS matrix.""" plot_title: str pos: TsPos tout: np.ndarray # float64, shape (nt,) rts: np.ndarray # float64, shape (nt, nrz)
[docs] def read_ts(data: str | bytes, *, encoding: str = "utf-8") -> TsReadResult: """Parse ASCII time-series data (Matlab ``read_ts.m`` non-``.mat`` path).""" text = data.decode(encoding, errors="replace") if isinstance(data, bytes) else data lines = text.splitlines() if len(lines) < 2: raise ValueError("time-series file too short") plot_title = lines[0].strip() toks = " ".join(lines[1:]).split() if not toks: raise ValueError("time-series file missing numeric body") i = 0 try: nrz = int(float(toks[i])) i += 1 except (ValueError, IndexError) as e: raise ValueError("time-series file missing receiver depth count") from e if nrz < 0: raise ValueError("time-series file has negative receiver depth count") if len(toks) < i + nrz: raise ValueError("time-series file missing receiver depths") rz = np.array([float(t) for t in toks[i : i + nrz]], dtype=np.float64) i += nrz rem = toks[i:] nrow = nrz + 1 if nrow <= 0: raise ValueError("invalid row count in time-series file") if len(rem) == 0: temp = np.zeros((nrow, 0), dtype=np.float64) else: if len(rem) % nrow != 0: raise ValueError( f"time-series payload has {len(rem)} values; expected multiple of {nrow}" ) vals = np.array([float(t) for t in rem], dtype=np.float64) temp = vals.reshape((nrow, len(rem) // nrow), order="F") tout = temp[0, :].copy() rts = temp[1:, :].T.copy() return TsReadResult( plot_title=plot_title, pos=TsPos(r=TsPosR(z=rz)), tout=tout, rts=rts, )
[docs] def read_ts_bytes(data: bytes, *, encoding: str = "utf-8") -> TsReadResult: """Like :func:`read_ts` for UTF-8 (or other) encoded bytes.""" return read_ts(data, encoding=encoding)