Добавить общее ядро DEIF, эмуляцию приборов и переносимые панели Python/Qt. Расширить загрузчики F2812 и F28335, обновление Flash и профиль STM Invertor. Добавить альтернативный МНК Eigen, модели неисправностей и диагностику сигналов. Обновить справочники устройств, проекты CCS и регрессионные проверки.
211 lines
19 KiB
Markdown
211 lines
19 KiB
Markdown
# Сравнение сигналов с CSV-эталоном
|
||
|
||
Общее ядро `set_devices.signal_reference` и Qt-порт
|
||
`set_devices.qt_ports.reference_comparison` сравнивают измеренную запись с
|
||
эталоном. Они не импортируют приложение, не выбирают его каталоги и не
|
||
требуют конкретного движка отрисовки. Ядро использует только стандартную
|
||
библиотеку Python; Qt-порт поддерживает PySide6 и PySide2.
|
||
|
||
## Численный API
|
||
|
||
`Comparison.analysis` содержит локальный параметрический отчёт:
|
||
`summary`, `details`, `attention`, `findings`, `features`, `model_report`.
|
||
Он рассчитывается `set_devices.signal_diagnostics` по тому же интервалу,
|
||
временному сдвигу и допускам. Исходные метрики используют все общие отсчёты;
|
||
объём выборки подбора моделей явно указан в `model_report`.
|
||
Добавлено необязательное поле с пустым словарём по умолчанию: прежний
|
||
конструктор `Comparison` с пятью аргументами сохраняет совместимость.
|
||
|
||
Для одинаковых известных обозначений `Uce`, `Uge`, `Ic` анализ различает
|
||
повторные полные переходы, усиленный звон Uce после выключения, отсутствие
|
||
переключения, растянутые фронты и временной сдвиг. Общие причины включают
|
||
смещение, шум и неопределённое изменение формы. Разные известные физические
|
||
каналы, недостаточное число точек и неполное покрытие отражаются в выводе.
|
||
На неизвестных каналах аппаратные гипотезы о снаббере и транзисторе не строятся.
|
||
|
||
`set_devices.signal_fault_models` подбирает смещение, масштаб, дрейф,
|
||
задержку, растяжение фронтов, постоянный уровень, кусочные прерывания и
|
||
затухающий отклик RLC. `set_devices.signal_joint_models` добавляет локальную
|
||
ВАХ `Uce=U0+Rdiff*Ic`, остаточный ток выключенного состояния и совпадение
|
||
провалов управления/проводимости по трём каналам.
|
||
|
||
`compare(..., circuit=..., channels=...)` принимает необязательные известные
|
||
`inductance_h`, `capacitance_f`, `resistance_ohm` в СИ и словарь связанных
|
||
каналов `dict(measured=..., reference=...)`. GUI формирует эти снимки сам.
|
||
Однозначность физических имён и единиц проверяется до совместного анализа.
|
||
|
||
Глобальные модели сравниваются по BIC общей обучающей выборки и RMSE на
|
||
каждой третьей отложенной точке. JSON сохраняет всех конкурентов, число
|
||
параметров, формулы, критерии и ограничения. BIC не интерпретируется как
|
||
вероятность аппаратного отказа. Без известного элемента R/L/C не выдаются
|
||
три произвольных номинала: доступны только LC и R/L последовательного
|
||
эквивалента полюсов.
|
||
|
||
`ComparisonAnalysis` в `qt_ports.reference_results` показывает одну строку
|
||
и кнопку отчёта с таблицей моделей и подробным текстом по образцу Android GUI. Компонент
|
||
используется и над исходным графиком, и в полной форме сравнения; изменение
|
||
входов, чтение другого эталона и ошибки очищают прежний анализ. JSON-экспорт
|
||
обоих видов включает `analysis` рядом с `metrics`. Ядро не использует сеть
|
||
или обученную модель; пороги признаков и ограничения указаны в подробностях.
|
||
|
||
`Trace(name, times, values)` содержит имя, строго возрастающие временные
|
||
метки в миллисекундах и амплитуды в единицах канала. `trace_from_pairs`
|
||
создаёт такой снимок из пар `(time_ms, value)`; необязательный `transform`
|
||
применяет множитель или иное преобразование амплитуды.
|
||
|
||
`read_reference(path, cancel=...)` потоково читает UTF-8 CSV с необязательным
|
||
BOM. Первый столбец — `time_ms` либо `timestamp` с ISO-временем и явным
|
||
часовым поясом. Остальные столбцы имеют непустые уникальные имена каналов.
|
||
Поддерживаются запятая, точка с запятой и табуляция. Пустые ячейки и NaN
|
||
сохраняются как пропуски; повторное время, бесконечности и повреждённые
|
||
строки отклоняются.
|
||
|
||
```python
|
||
from set_devices.signal_reference import compare, read_reference, trace_from_pairs # Ядро можно использовать без Qt и графика.
|
||
measured = trace_from_pairs('U (V)', [(0., 2.), (1., 4.), (2., 6.)]) # Время задано в миллисекундах, амплитуда — в вольтах.
|
||
reference = read_reference('reference.csv')['U (V)'] # Столбец выбирается по полному имени канала.
|
||
result = compare(measured, reference, absolute_tolerance=.1, relative_tolerance_pct=2.) # Допуск равен 0,1 В плюс 2% модуля эталона.
|
||
print(result.metrics['rmse']) # Показатель рассчитан по всем корректным точкам измерения в общем интервале.
|
||
```
|
||
|
||
`compare` возвращает `Comparison`: нормализованные кривые `measured` и
|
||
`reference`, остаток `difference`, эталон на сетке остатка `expected` и
|
||
словарь `metrics`. Статистика включает покрытие, пропуски, среднее смещение,
|
||
MAE, RMSE, СКО без смещения, максимальную ошибку и выходы за допуск.
|
||
|
||
Эталон линейно интерполируется на исходную сетку измерения без
|
||
экстраполяции и соединения через NaN. Остаток равен измерению минус эталон.
|
||
Допуск равен `absolute_tolerance + relative_tolerance_pct / 100 * abs(reference)`;
|
||
равенство границе допуска не считается превышением. Экранная децимация
|
||
не участвует в расчёте.
|
||
|
||
По умолчанию начала записей совмещаются. `align_start=False` сохраняет
|
||
различие абсолютных начал; положительный `shift_ms` сдвигает эталон вправо.
|
||
`interval=(start_ms, stop_ms)` задаётся от начала измерения, обе границы
|
||
включены. Callback `cancel()` прерывает подготовку, чтение и сравнение через
|
||
`InterruptedError`.
|
||
|
||
## Наложение на существующий график
|
||
|
||
Для добавления эталона в уже работающий график используйте
|
||
`set_devices.qt_ports.reference_overlay.ReferenceOverlayPanel`. Это
|
||
компактные параметры без canvas. Оригинальный график
|
||
остаётся у потребителя и продолжает показывать измерение при загрузке и
|
||
изменении эталона.
|
||
|
||
Конструктор принимает `sources`, `parent`, `source_path` и необязательный
|
||
`path_provider` с тем же смыслом, что в таблице ниже. `load_reference(path)`
|
||
автоматически читает CSV, совмещает выбранные каналы и рассчитывает показатели
|
||
в рабочем потоке. Отдельной кнопки запуска расчёта нет. Изменение канала,
|
||
сдвига, совмещения начал или допусков обновляет результат автоматически.
|
||
|
||
Сигнал `changed` сообщает потребителю, что `overlay()` вернёт актуальные
|
||
спецификации и точки одной дополнительной кривой. Потребитель **добавляет**
|
||
их к оригинальным рядам и общей легенде своего canvas. `RESULT_KEY`
|
||
отличает эталон от измерений; оранжевая кривая имеет подпись
|
||
`Эталон: <канал>`. Временные метки эталона уже находятся в исходной шкале
|
||
измерения: начало оригинала не обнуляется. Потребитель применяет своё
|
||
перебазирование короткой развёртки к обеим кривым одинаково.
|
||
|
||
`set_sources(sources)` обновляет кадр прибора или преобразование амплитуд,
|
||
сохраняя загруженный эталон. Сигнал `remove_requested` предлагает владельцу
|
||
удалить дополнительный слой и параметры. `report()` и раскрываемая строка
|
||
«Допуски и отчёт» предоставляют актуальную статистику и экспорт JSON.
|
||
Если общей части времени нет, кривая остаётся доступна, а показатели
|
||
заменяются объяснением отсутствия перекрытия. `close()` останавливает worker,
|
||
не закрывая график потребителя.
|
||
|
||
Истории, переданные в одном снимке `sources`, должны быть неизменяемыми;
|
||
для нового кадра передайте новую историю через `set_sources`. Порт
|
||
поддерживает PySide6 и PySide2 без импорта приложения.
|
||
|
||
### Подробные показатели рядом с исходным графиком
|
||
|
||
`ReferenceOverlayPanel.results` — виджет `ComparisonResults` с полной
|
||
таблицей всех 14 показателей: покрытие, пропуски, смещение, MAE, RMSE,
|
||
СКО, максимум и его время, нормированная ошибка и выходы за допуск.
|
||
Потребитель размещает его в существующей боковой области рядом с canvas;
|
||
в SETGUI он открывается автоматически во вкладке «Отклонения и расчёты».
|
||
Сигнал `results_requested` кнопки «Расчёты» позволяет вернуться к этой
|
||
области после выбора обычной легенды.
|
||
|
||
Флажок `results.delta_curve` добавляет в `overlay()` кривую с ключом
|
||
`DIFFERENCE_KEY`. Её временные метки находятся в той же исходной шкале,
|
||
что эталон и оригинал. Исходные кривые сохраняются, отдельный canvas не
|
||
создаётся. Кнопка JSON рядом с таблицей экспортирует тот же актуальный
|
||
результат. Изменение параметров и ошибки очищают устаревшие показатели.
|
||
|
||
`comparison_rows(metrics)` из `qt_ports.reference_results` задаёт единые
|
||
подписи, порядок и единицы как для этой таблицы, так и для прежнего диалога.
|
||
|
||
## Совместимые Qt-панель и отдельное окно
|
||
|
||
`ReferenceComparisonPanel` — обычный `QWidget` для существующей страницы
|
||
графика. Кнопка «К записи» испускает `back_requested`; потребитель сам
|
||
скрывает панель и возвращает исходный вид. При повторном показе той же
|
||
панели сохраняются эталон, параметры и рассчитанные показатели.
|
||
|
||
`ReferenceComparisonDialog` — отдельный `QDialog` с тем же расчётом и
|
||
настройками. Оба конструктора принимают:
|
||
|
||
| Параметр | Контракт |
|
||
| --- | --- |
|
||
| `sources` | Последовательность пар `(source, points)`: `source.name` — имя, `source.display_value(value)` — преобразование амплитуды, `points` — полная неизменяемая история `(time_ms, value)` |
|
||
| `parent` | Родительский QWidget; для встроенной панели передайте страницу графика |
|
||
| `source_path` | Происхождение измерения для JSON-отчёта, по умолчанию пустая строка |
|
||
| `plot_factory` | Обязательная фабрика без аргументов, возвращающая настроенный QWidget графика |
|
||
| `series_factory` | Обязательная фабрика `factory(key, name, color=..., timestamps=..., values=...)`, создающая кривую потребителя |
|
||
| `path_provider` | Необязательный callback `filename → начальный путь`; пустое имя используется для открытия эталона, `reference_comparison.json` — для отчёта |
|
||
|
||
```python
|
||
from set_devices.qt_ports.reference_comparison import ReferenceComparisonPanel # Qt загружается только при импорте конкретного порта.
|
||
panel = ReferenceComparisonPanel( # Один компонент обслуживает выбор каналов, параметры, фоновый расчёт и экспорт.
|
||
sources=recording_channels, # Потребитель фиксирует полные истории и преобразование амплитуд до создания панели.
|
||
parent=chart_page, # Сравнение размещается в существующей рабочей области.
|
||
source_path=recording_filename, # Имя исходной записи попадёт в отчёт.
|
||
plot_factory=create_comparison_canvas, # Адаптер создаёт график с единой шкалой Y для обеих кривых.
|
||
series_factory=create_series, # Контейнеры кривых принадлежат используемому движку отрисовки.
|
||
path_provider=recording_path, # Папки и правила хранения остаются политикой потребителя.
|
||
) # Все функции и объекты адаптера задаются приложением до этого вызова.
|
||
chart_page.layout().addWidget(panel, 1) # Панель занимает свободное место страницы.
|
||
panel.back_requested.connect(show_recording) # Обработчик скрывает сравнение и возвращает исходный график.
|
||
```
|
||
|
||
Canvas должен предоставлять изменяемый список `_series`, методы
|
||
`reset_interaction()`, `_sync_bounds()`, стандартные `update()` и `close()`,
|
||
флажки `axis_lock_x` и `axis_lock_y` с `setChecked()`, поле `axis_limits`,
|
||
`visible_bounds.left/right` в миллисекундах и `viewport` типа
|
||
`protocan.plot.Viewport`. `_sync_bounds()` задаёт конечный ненулевой диапазон
|
||
X даже для пустого графика. Обе наложенные кривые используют **одну шкалу Y**;
|
||
автоматическое разделение каналов на независимые оси отключает адаптер.
|
||
Дополнительные кнопки canvas, подписи и правила снимков также настраиваются
|
||
в `plot_factory`.
|
||
|
||
Панель передаёт фабрике кривых полные массивы: их экранное сокращение остаётся
|
||
ответственностью canvas. Временные поля интерфейса показывают микросекунды,
|
||
ядро и JSON-отчёт используют миллисекунды. Явно различающиеся единицы в
|
||
скобках имён каналов блокируют сравнение; автоматической конвертации нет.
|
||
|
||
Истории и настройки источников нельзя менять, пока панель использует их
|
||
снимок. При смене записи или преобразования канала закройте старую панель
|
||
и создайте новую. `close()` встроенной панели отменяет и дожидается worker;
|
||
уничтожение родителя также останавливает поток до удаления дочерних объектов.
|
||
Отдельный диалог откладывает закрытие до завершения кооперативной отмены.
|
||
|
||
`load_reference(path)` позволяет открыть эталон программно, `report()`
|
||
возвращает актуальный словарь отчёта либо `None`. Изменение входных
|
||
параметров очищает старые кривые и показатели до следующего расчёта.
|
||
|
||
## Проверки
|
||
|
||
`python/tests/test_reference_comparison_port.py` проверяет внедрение
|
||
стороннего canvas, преобразование амплитуд, отклонение и экспорт с внешним
|
||
provider путей, а также API отдельного диалога. Его можно запустить из
|
||
каталога `templates/python` командой
|
||
`python -m unittest discover -s tests -p test_reference_comparison_port.py`.
|
||
|
||
`python/tests/test_shared_library_boundary.py` проверяет независимость
|
||
вычислительных модулей от GUI и Qt. Интеграционные проверки потребителя
|
||
дополнительно покрывают полные истории, общий масштаб Y, отмену, смену
|
||
записи, возврат к графику и полноэкранный режим.
|