"""Thin host adapter for the shared C99 reconstruction/DAC algorithms.""" from __future__ import annotations import ctypes as C from dataclasses import dataclass from functools import lru_cache from protocan.native import NativeProtocol METHODS = {"polynomial": "Аппроксимация: полином МНК", "linear": "Интерполяция: линейная", "pchip": "Интерполяция: PCHIP", "spline": "Восстановление: кубический сплайн"} @dataclass class Reconstruction: points: list input_count: int unique_count: int rmse: float pieces: tuple = () @lru_cache(maxsize=1) def library(): try: lib = NativeProtocol().lib pointer = C.POINTER(C.c_double) lib.set_signal_reconstruct.argtypes = [pointer, pointer, C.c_size_t, C.c_int, C.c_uint, C.c_size_t, C.c_int, pointer, pointer, pointer, pointer, C.c_size_t] lib.set_signal_reconstruct.restype = C.c_int lib.set_signal_dac12.argtypes = [pointer, C.c_size_t, C.c_double, C.POINTER(C.c_uint16)] lib.set_signal_dac12.restype = C.c_int return lib except (AttributeError, OSError, RuntimeError) as error: raise ValueError("Пересоберите SETProtocol с set_signal.c и set_wavegen.c") from error def reconstruct(points, method="pchip", output_count=1000, degree=2, *, endpoint=True, with_model=False): if method not in METHODS or type(output_count) is not int or not 2 <= output_count <= 10000: raise ValueError("Неизвестный метод или число выходных точек вне 2…10000") count = len(points) if not 2 <= count <= 100000 or type(degree) is not int or not 1 <= degree <= 5: raise ValueError("Нужно 2…100000 исходных точек, степень 1…5") x, y = (C.c_double * count)(*(p[0] for p in points)), (C.c_double * count)(*(p[1] for p in points)) ox, oy, meta = (C.c_double * output_count)(), (C.c_double * output_count)(), (C.c_double * 3)() work = (C.c_double * (14 * count + 128))() code = library().set_signal_reconstruct(x, y, count, list(METHODS).index(method), degree, output_count, int(endpoint), ox, oy, meta, work, len(work)) if code: raise ValueError({1: "Недостаточно точек для выбранной степени или неверные параметры", 2: "Нужны конечные значения и минимум две различные временные точки", 3: "Неустойчивая аппроксимация: уменьшите степень"}.get(code, "Ошибка расчёта")) pieces = () if with_model: from .plot_formula import reconstruction_pieces if method == 'polynomial' and output_count <= degree: pieces = reconstruct(points, method, degree + 1, degree, with_model=True).pieces else: pieces = reconstruction_pieces(work, count, int(meta[1]), method, degree, oy, endpoint) return Reconstruction(list(zip(ox, oy)), int(meta[0]), int(meta[1]), meta[2], pieces) def dac12(volts, vref=3.3): data = (C.c_double * len(volts))(*volts) codes = (C.c_uint16 * len(volts))() if library().set_signal_dac12(data, len(data), vref, codes): raise ValueError("Расчётная кривая выходит за диапазон ЦАП 0…Vref; измените точки или метод") return tuple(codes)