backwash/backwash/ova_bin.py
2025-12-30 02:51:57 -08:00

123 lines
4.3 KiB
Python

from typing import IO
from dataclasses import dataclass
from pathlib import Path
import logging
import numpy
from numpy.typing import NDArray
from numpy.fft import fftshift, fftfreq
from .utils import C0
logger = logging.getLogger(__name__)
@dataclass
class OVABINData:
jones: NDArray[numpy.complex128] # jones matrix data [2 x 2 x n_pts] # TODO frequency domain or time?
df_GHz: float # delta between frequency points
max_freq_GHz: float # sweep's max frequency (shortest wavelength)
meas_date: NDArray[numpy.int16] # 8 items: year month [week?] day hour min sec ms
def time_ns(self, size: int) -> NDArray[numpy.float64]:
# To be used with tdr curve generated with e.g. fft(fftshift(data.jones))
tt_ns = fftshift(fftfreq(size, d=self.df_GHz))
return tt_ns
@property
def wl_range_nm(self) -> NDArray[numpy.float64]:
freq_range = numpy.asarray([
self.max_freq_GHz - self.df_GHz * self.jones.shape[2],
self.max_freq_GHz,
])
return C0 / freq_range
@property
def ctr_freq_GHz(self) -> float:
return self.max_freq_GHz - self.df_GHz * self.jones.shape[2] / 2
@property
def freqs(self) -> NDArray[numpy.float64]:
return numpy.arange(self.jones.shape[2], dtype=numpy.float64)[::-1] * self.df_GHz + self.max_freq_GHz
@property
def wls(self) -> NDArray[numpy.float64]:
# To be used with spectrum generated with e.g. fft(fftshift(data.te))
return C0 / self.freqs
def window(self, size: int) -> NDArray[numpy.float64]:
# Hann window for use before time domain fft
nn = numpy.linspace(0, 1, size)
alpha = 0.500
scale = 1
ham = alpha - (1 - alpha) * numpy.cos(2 * numpy.pi * nn)
return scale * ham
@staticmethod
def read(file: str | IO[bytes] | Path) -> 'OVABINData':
raise NotImplementedError('Still WIP')
def fromfile(*args, **kwargs) -> NDArray:
if not isinstance(file, str | Path):
file.seek(0)
return numpy.fromfile(file, *args, **kwargs)
magic = fromfile(dtype=numpy.uint8, offset=0x10, count=7)
if (magic != [0x40] + [ord(cc) for cc in 'S33OVA']).any():
logger.warning(f'Unexpected magic bytes: {magic}')
metadata = fromfile(dtype=numpy.float64, offset=0x00, count=2)
data_count = fromfile(dtype=numpy.int32, offset=0x18, count=1)
meas_date = fromfile(dtype=numpy.int16, offset=0x49, count=8)
arr = fromfile(dtype=numpy.float64, offset=0x59, count=data_count * 2 * 4)
ng = fromfile(dtype=numpy.float32, offset=0x21, count=1)
max_freq_GHz, df_GHz = metadata
arrs = numpy.split(arr, 4 * 2)
jones = (arrs[0::2] + 1j * arrs[1::2]).reshape(2, 2, -1) # TODO confirm
result = OVABINData(
jones = jones,
df_GHz = df_GHz,
max_freq_GHz = max_freq_GHz,
meas_date = meas_date,
)
return result
"""
Notes on .bin file format (OVA)
================================
0x00: 9D A8 5A 6B C8 D0 0C 41 13 23 63 4D 22 74 C4 3F
|max_freq_GHZ (f64)------ | f64 ??----------------- |
C0/freq = wl_nm 0.1597... df_GHz
not sure what changes this, if anything
0x10: 40 53 33 33 4F 56 41 00 00 00 01 00 00 00 00 00
| S 3 3 | O V A | ? | data_count |??|f64------
0x10000 = biggest range (58.97nm)
this * 8bytes * 2(complex) * 4 (jones matrix) = data length in bytes
0x20: 00 00 80 20 40 01 00 00 00 00 00 00 00 00 00 00
-DUT length (m)|----........|
average count
0x30: 00 00 00 00 00 00 38 98 40 00 00 00 00 00 00 00
???????????
0x40: 00 00 00 00 00 00 00 00 00 E7 07 0A 00 05 00 06
|year month week? day-
acquisition timestamp
0x50: 00 12 00 0F 00 0F 00 23 00 F7 27 6F E5 17 A1 A7
--- hr minute sec ms | f64 data starts----
(offset 0x59)
0x400040:
3F C8 E2 3E 89 8A A4 8C BF A9 4D A8 8A D6 9B 94
0x400050:
3F 8E BB 79 0C C2 84 92 3F 4E 4F 4E 45 40
|N O N E |
"""