Add native QCustomPlot renderer and shared plot adapters
This commit is contained in:
@@ -9,6 +9,7 @@ See c/set-protocol/include/set_spectrum.h for filtering and normalization semant
|
||||
"""
|
||||
from __future__ import annotations
|
||||
import ctypes
|
||||
import math
|
||||
from dataclasses import dataclass
|
||||
from enum import IntEnum
|
||||
|
||||
@@ -63,11 +64,40 @@ class NativeSpectrum:
|
||||
self._peak.restype = ctypes.c_int
|
||||
|
||||
def analyze(self, times, values, *, max_size=4096, window=Window.HANN, filter=Filter.NONE,
|
||||
low_hz=10.0, high_hz=100.0, remove_mean=True) -> Spectrum:
|
||||
if type(max_size) is not int or not 16 <= max_size <= 16384 or max_size & (max_size - 1):
|
||||
raise ValueError("FFT size must be a power of two in 16..16384")
|
||||
low_hz=10.0, high_hz=100.0, remove_mean=True, full_record=False) -> Spectrum:
|
||||
if type(max_size) is not int or not 16 <= max_size <= 2097152 or max_size & (max_size - 1):
|
||||
raise ValueError("FFT size must be a power of two in 16..2097152")
|
||||
if len(times) != len(values):
|
||||
raise ValueError("Timestamp and value counts differ")
|
||||
if full_record and len(times) >= 16:
|
||||
source_count = len(times)
|
||||
n = 1 << (min(source_count, max_size).bit_length() - 1)
|
||||
dt = (times[-1] - times[0]) / (source_count - 1)
|
||||
if not math.isfinite(dt) or dt <= 0:
|
||||
raise ValueError("Invalid spectrum timestamps")
|
||||
if any(not math.isfinite(value) for value in values) or any(
|
||||
not math.isfinite(times[i]) or abs((times[i] - times[i - 1]) / dt - 1) > .05
|
||||
for i in range(1, source_count)):
|
||||
raise ValueError("Full-record FFT requires uniformly spaced finite samples")
|
||||
# Integrate piecewise-constant samples over equal fractional bins.
|
||||
# Unlike picking every kth sample, this averages before decimation.
|
||||
width = source_count / n
|
||||
averaged = []
|
||||
index = 0
|
||||
position = 0.
|
||||
for output_index in range(n):
|
||||
end = (output_index + 1) * width
|
||||
total = 0.
|
||||
while position < end and index < source_count:
|
||||
stop = min(end, index + 1.)
|
||||
total += values[index] * (stop - position)
|
||||
position = stop
|
||||
if position >= index + 1:
|
||||
index += 1
|
||||
averaged.append(total / width)
|
||||
start = times[0]
|
||||
times = [start + ((i + .5) * width - .5) * dt for i in range(n)]
|
||||
values = averaged
|
||||
count = min(len(times), max_size)
|
||||
origin = times[len(times) - count] if count else 0.0
|
||||
t = (ctypes.c_double * count)(*(value - origin for value in times[-count:])) if count else (ctypes.c_double * 0)()
|
||||
|
||||
Reference in New Issue
Block a user