169 lines
6.0 KiB
Python
169 lines
6.0 KiB
Python
"""Binary decoders for raw/preprocessed/result payload collections."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import numpy as np
|
|
|
|
from python_app.models.dataset_model import (
|
|
ComboKey,
|
|
ResultBlock,
|
|
ResultCollection,
|
|
ResultPayload,
|
|
SweepCollection,
|
|
TraceData,
|
|
)
|
|
from python_app.orchestration.shm.binary_cursor import ByteCursor
|
|
|
|
RAW_MAGIC = 0x32574152
|
|
PREPROC_MAGIC = 0x32525050
|
|
RESULT_MAGIC = 0x314C5352
|
|
|
|
|
|
def decode_trace_collection(payload: bytes, expected_magic: int) -> SweepCollection:
|
|
"""Decode one raw/preprocessed collection from binary payload."""
|
|
cursor = ByteCursor(payload)
|
|
magic = cursor.read_u32()
|
|
if magic != expected_magic:
|
|
raise ValueError("Unexpected trace collection magic")
|
|
|
|
collection_id = cursor.read_u64()
|
|
monotonic_ns = cursor.read_u64()
|
|
trace_count = cursor.read_u32()
|
|
|
|
traces: list[TraceData] = []
|
|
for _ in range(trace_count):
|
|
input_pos = cursor.read_u32()
|
|
output_pos = cursor.read_u32()
|
|
point_count = cursor.read_u32()
|
|
|
|
freq_bytes = point_count * 4
|
|
freq = np.frombuffer(cursor.read_bytes(freq_bytes), dtype="<f4").astype(np.float32, copy=False)
|
|
|
|
interleaved_bytes = point_count * 8
|
|
s11_interleaved = np.frombuffer(cursor.read_bytes(interleaved_bytes), dtype="<f4")
|
|
s21_interleaved = np.frombuffer(cursor.read_bytes(interleaved_bytes), dtype="<f4")
|
|
s11 = (s11_interleaved[0::2] + 1j * s11_interleaved[1::2]).astype(np.complex64, copy=False)
|
|
s21 = (s21_interleaved[0::2] + 1j * s21_interleaved[1::2]).astype(np.complex64, copy=False)
|
|
|
|
traces.append(
|
|
TraceData(
|
|
combo=ComboKey(input=input_pos, output=output_pos),
|
|
frequency_hz=freq,
|
|
s11=s11,
|
|
s21=s21,
|
|
)
|
|
)
|
|
|
|
capture_start_ns = 0
|
|
capture_end_ns = 0
|
|
if cursor.remaining_bytes() == 16:
|
|
capture_start_ns = cursor.read_u64()
|
|
capture_end_ns = cursor.read_u64()
|
|
elif cursor.remaining_bytes() != 0:
|
|
raise ValueError("Unexpected trailing bytes in trace collection")
|
|
|
|
return SweepCollection(
|
|
collection_id=collection_id,
|
|
monotonic_ns=monotonic_ns,
|
|
traces=traces,
|
|
capture_start_ns=capture_start_ns,
|
|
capture_end_ns=capture_end_ns,
|
|
)
|
|
|
|
|
|
def decode_result_collection(payload: bytes) -> ResultCollection:
|
|
"""Decode one processed result collection from binary payload."""
|
|
|
|
def read_payload(cursor: ByteCursor) -> ResultPayload:
|
|
"""Decode one result payload from stream."""
|
|
kind = cursor.read_u8()
|
|
name_size = cursor.read_u16()
|
|
name = cursor.read_bytes(name_size).decode("utf-8")
|
|
|
|
if kind == 1:
|
|
point_count = cursor.read_u32()
|
|
freq = np.frombuffer(cursor.read_bytes(point_count * 4), dtype="<f4").astype(np.float32, copy=False)
|
|
interleaved = np.frombuffer(cursor.read_bytes(point_count * 8), dtype="<f4")
|
|
trace = (interleaved[0::2] + 1j * interleaved[1::2]).astype(np.complex64, copy=False)
|
|
return ResultPayload(
|
|
processing_name=name,
|
|
kind=kind,
|
|
frequency_hz=freq,
|
|
trace=trace,
|
|
)
|
|
if kind == 2:
|
|
return ResultPayload(
|
|
processing_name=name,
|
|
kind=kind,
|
|
scalar_value=cursor.read_f32(),
|
|
)
|
|
if kind == 3:
|
|
x_count = cursor.read_u32()
|
|
y_count = cursor.read_u32()
|
|
image_x_axis = np.frombuffer(cursor.read_bytes(x_count * 4), dtype="<f4").astype(np.float32, copy=False)
|
|
image_y_axis = np.frombuffer(cursor.read_bytes(y_count * 4), dtype="<f4").astype(np.float32, copy=False)
|
|
value_count = x_count * y_count
|
|
image_values = np.frombuffer(cursor.read_bytes(value_count * 4), dtype="<f4").astype(np.float32, copy=False)
|
|
image = image_values.reshape((y_count, x_count)) if value_count > 0 else np.zeros((0, 0), dtype=np.float32)
|
|
return ResultPayload(
|
|
processing_name=name,
|
|
kind=kind,
|
|
image_x_axis=image_x_axis,
|
|
image_y_axis=image_y_axis,
|
|
image=image,
|
|
)
|
|
if kind == 4:
|
|
table_columns = cursor.read_u32()
|
|
table_rows = cursor.read_u32()
|
|
value_count = table_columns * table_rows
|
|
table_values = np.frombuffer(cursor.read_bytes(value_count * 4), dtype="<f4").astype(np.float32, copy=False)
|
|
table = (
|
|
table_values.reshape((table_rows, table_columns))
|
|
if value_count > 0 and table_columns > 0
|
|
else np.zeros((0, 0), dtype=np.float32)
|
|
)
|
|
return ResultPayload(
|
|
processing_name=name,
|
|
kind=kind,
|
|
table=table,
|
|
)
|
|
raise ValueError(f"Unsupported result payload kind: {kind}")
|
|
|
|
cursor = ByteCursor(payload)
|
|
magic = cursor.read_u32()
|
|
if magic != RESULT_MAGIC:
|
|
raise ValueError("Unexpected result collection magic")
|
|
|
|
collection_id = cursor.read_u64()
|
|
monotonic_ns = cursor.read_u64()
|
|
collection_payload_count = cursor.read_u32()
|
|
block_count = cursor.read_u32()
|
|
|
|
collection_payloads: list[ResultPayload] = []
|
|
for _ in range(collection_payload_count):
|
|
collection_payloads.append(read_payload(cursor))
|
|
|
|
blocks: list[ResultBlock] = []
|
|
for _ in range(block_count):
|
|
input_pos = cursor.read_u32()
|
|
output_pos = cursor.read_u32()
|
|
payload_count = cursor.read_u32()
|
|
|
|
payloads: list[ResultPayload] = []
|
|
for _ in range(payload_count):
|
|
payloads.append(read_payload(cursor))
|
|
|
|
blocks.append(
|
|
ResultBlock(
|
|
combo=ComboKey(input=input_pos, output=output_pos),
|
|
payloads=payloads,
|
|
)
|
|
)
|
|
|
|
return ResultCollection(
|
|
collection_id=collection_id,
|
|
monotonic_ns=monotonic_ns,
|
|
collection_payloads=collection_payloads,
|
|
blocks=blocks,
|
|
)
|