123 lines
4.3 KiB
Python
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 |
|
|
"""
|