Files
templates/python/set_devices/signal_reconstruction.py

63 lines
3.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""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)