63 lines
3.5 KiB
Python
63 lines
3.5 KiB
Python
"""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)
|