Extract reusable device protocols, ports and logic analyzers from SETGUI
This commit is contained in:
119
python/set_devices/spectrum_demo.py
Normal file
119
python/set_devices/spectrum_demo.py
Normal file
@@ -0,0 +1,119 @@
|
||||
"""@file spectrum_demo.py
|
||||
@brief Демонстрационный источник спектра для проверки вкладки без прибора.
|
||||
|
||||
Повторяет то, что передаёт прошивка: основной тон, несколько гармоник
|
||||
и шумовая подложка. Нужен, чтобы вкладку можно было открыть и убедиться
|
||||
в правильности отрисовки, не имея под рукой ни платы, ни датчика.
|
||||
|
||||
Источник намеренно живёт рядом с разбором потока, а не внутри вкладки:
|
||||
он выдаёт те же самые строки, что и прибор, и проходит через тот же
|
||||
разбор. Иначе демонстрация проверяла бы отрисовку, но не формат.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import math
|
||||
import random
|
||||
|
||||
from set_devices.spectrum_stream import (
|
||||
Harmonic,
|
||||
HarmonicsFrame,
|
||||
build_harmonics_line,
|
||||
build_spectrum_line,
|
||||
)
|
||||
|
||||
#: Число элементов спектра в демонстрационном кадре.
|
||||
DEMO_BINS = 128
|
||||
|
||||
#: Частота выборки демонстрационного источника, Гц.
|
||||
DEMO_SAMPLE_RATE = 20000
|
||||
|
||||
#: Размер преобразования, которому соответствует спектр.
|
||||
DEMO_FFT_SIZE = 512
|
||||
|
||||
#: Верхняя граница шкалы уровней, дБ относительно милливольта.
|
||||
DEMO_TOP_DBMV = 60.0
|
||||
|
||||
#: Размах шкалы уровней, дБ.
|
||||
DEMO_RANGE_DB = 60.0
|
||||
|
||||
|
||||
class SpectrumDemoSource:
|
||||
"""@brief Выдаёт строки спектра и гармоник, как это делал бы прибор."""
|
||||
|
||||
def __init__(self, seed: int = 1) -> None:
|
||||
self._seq = 0
|
||||
self._random = random.Random(seed)
|
||||
self._phase = 0.0
|
||||
|
||||
def next_lines(self) -> str:
|
||||
"""@brief Формирует очередную пару строк.
|
||||
|
||||
@return Строка спектра и строка гармоник, склеенные подряд.
|
||||
|
||||
Основной тон медленно плывёт по частоте, чтобы на экране было
|
||||
видно движение и стало заметно, если отрисовка где-то подвисает.
|
||||
"""
|
||||
self._seq += 1
|
||||
self._phase += 0.05
|
||||
|
||||
fundamental_hz = 1000.0 + 200.0 * math.sin(self._phase)
|
||||
fundamental_mv = 400.0
|
||||
bin_width = (DEMO_SAMPLE_RATE / 2.0) / DEMO_BINS
|
||||
|
||||
# Уровни: шумовая подложка около 3 мВ плюс узкие пики на основном
|
||||
# тоне и его гармониках.
|
||||
amplitudes = [self._random.uniform(1.5, 4.0) for _ in range(DEMO_BINS)]
|
||||
|
||||
harmonics: list[Harmonic] = []
|
||||
for order in range(1, 5):
|
||||
frequency = fundamental_hz * order
|
||||
if frequency >= DEMO_SAMPLE_RATE / 2.0:
|
||||
break
|
||||
|
||||
amplitude = fundamental_mv / (order * order)
|
||||
index = int(round(frequency / bin_width))
|
||||
if 0 <= index < DEMO_BINS:
|
||||
amplitudes[index] = amplitude
|
||||
# Соседние элементы приподняты: у реального окна энергия
|
||||
# тона всегда занимает несколько элементов, и совсем узкий
|
||||
# пик выглядел бы неправдоподобно.
|
||||
for neighbour in (index - 1, index + 1):
|
||||
if 0 <= neighbour < DEMO_BINS:
|
||||
amplitudes[neighbour] = max(
|
||||
amplitudes[neighbour], amplitude * 0.45
|
||||
)
|
||||
|
||||
if order >= 2:
|
||||
harmonics.append(
|
||||
Harmonic(
|
||||
index=order,
|
||||
frequency_hz=frequency,
|
||||
amplitude_mv=amplitude,
|
||||
relative_db=20.0 * math.log10(amplitude / fundamental_mv),
|
||||
)
|
||||
)
|
||||
|
||||
levels = [20.0 * math.log10(max(value, 1e-3)) for value in amplitudes]
|
||||
|
||||
spectrum = build_spectrum_line(
|
||||
seq=self._seq,
|
||||
sample_rate_hz=DEMO_SAMPLE_RATE,
|
||||
fft_size=DEMO_FFT_SIZE,
|
||||
levels_db=levels,
|
||||
top_dbmv=DEMO_TOP_DBMV,
|
||||
range_db=DEMO_RANGE_DB,
|
||||
)
|
||||
|
||||
total = math.sqrt(sum(h.amplitude_mv**2 for h in harmonics))
|
||||
frame = HarmonicsFrame(
|
||||
seq=self._seq,
|
||||
fundamental_hz=fundamental_hz,
|
||||
fundamental_mv=fundamental_mv,
|
||||
thd_percent=100.0 * total / fundamental_mv,
|
||||
snr_db=42.0,
|
||||
rms_mv=fundamental_mv / math.sqrt(2.0),
|
||||
harmonics=tuple(harmonics),
|
||||
)
|
||||
|
||||
return spectrum + build_harmonics_line(frame)
|
||||
Reference in New Issue
Block a user