Расширить DEIF, загрузку CAN и общую обработку сигналов

Добавить общее ядро DEIF, эмуляцию приборов и переносимые панели Python/Qt.
Расширить загрузчики F2812 и F28335, обновление Flash и профиль STM Invertor.
Добавить альтернативный МНК Eigen, модели неисправностей и диагностику сигналов.
Обновить справочники устройств, проекты CCS и регрессионные проверки.
This commit is contained in:
2026-10-24 15:40:51 +03:00
parent a5e46d4627
commit 41e24629ce
170 changed files with 43762 additions and 281 deletions
+19 -7
View File
@@ -21,7 +21,7 @@ except ImportError:
QVBoxLayout, QWidget,
)
from set_devices.signal_reconstruction import MAX_OUTPUT_POINTS, METHODS
from set_devices.signal_reconstruction import BACKENDS, MAX_OUTPUT_POINTS, METHODS # Используем единый список вариантов расчёта для панели и подписей результатов.
from set_devices.plot_processing import Snapshot, prepare, process, write_csv
RESULT_COLORS = ("#FF70D0", "#50D5FF", "#FFD166", "#83E377", "#B99AFF", "#FF9870")
@@ -78,6 +78,11 @@ class SignalProcessingPanel(QWidget):
self.method.addItem(title, key)
self.method.setCurrentIndex(2)
row.addWidget(self.method)
self.backend = QComboBox() # Явный переключатель позволяет сравнить прежний МНК и новый Eigen на одном снимке данных.
for key, title in BACKENDS.items(): # Порядок оставляет исходный решатель значением по умолчанию.
self.backend.addItem(title, key) # В данных элемента хранится стабильный ключ, независимый от русского названия.
self.backend.setToolTip("Вариант полиномиального МНК. Рассчитайте оба: кривые сохранятся вместе, подписи и время помогут сравнению.") # Объясняем практический способ сравнения непосредственно рядом с выбором.
row.addWidget(self.backend) # Располагаем вариант рядом с методом, к которому он относится.
row.addStretch(1)
layout.addLayout(row)
row = QHBoxLayout()
@@ -110,15 +115,18 @@ class SignalProcessingPanel(QWidget):
"Полином МНК — сглаживание шумных данных. Линейная — отрезки между точками.\n"
"PCHIP — сохранение формы без выбросов. Сплайн — гладкая кривая, возможны выбросы.\n"
"Восстановление — оценка между измерениями; утраченные детали не определяются однозначно.")
for widget in (self.channel, self.method):
widget.currentIndexChanged.connect(self._parameters_changed)
for widget in (self.channel, self.method, self.backend): # Смена решателя делает текущий запрос новым, сохраняя предыдущие слои.
widget.currentIndexChanged.connect(self._parameters_changed) # Общая проверка предотвращает принятие результата для уже изменённого выбора.
for widget in (self.degree, self.count):
widget.valueChanged.connect(self._parameters_changed)
self._update_actions()
def _signature(self):
return prepare(self._snapshot, self.channel.currentData(), self.method.currentData(),
self.count.value(), self.degree.value())
self.count.value(), self.degree.value(), self._backend()) # Вариант участвует в идентичности снимка и результата.
def _backend(self): # Для методов без альтернативы всегда используем исходный алгоритм, сохраняя последний выбор МНК в списке.
return self.backend.currentData() if self.method.currentData() == "polynomial" else "native" # Переключение на интерполяцию не выдаёт её за расчёт Eigen.
def _current_request_for(self, signature):
if not self.preserve_on_view_change or signature is None:
@@ -126,7 +134,7 @@ class SignalProcessingPanel(QWidget):
# Validate the original calculation domain, independently of navigation.
return prepare(replace(self._snapshot, x_range=signature.x_range),
self.channel.currentData(), self.method.currentData(),
self.count.value(), self.degree.value())
self.count.value(), self.degree.value(), self._backend()) # Сохранённое окно проверяется вместе с текущим вариантом решателя.
def set_snapshot(self, snapshot):
previous = self._job.signature if self._job is not None else self._signature()
@@ -154,7 +162,7 @@ class SignalProcessingPanel(QWidget):
# depend on samples/units, not the source curve's checkbox.
source = replace(source, series=tuple(replace(series, visible=True)
for series in source.series))
if self.preserve_on_data_change or prepare(source, request.series.key, request.method, request.output_count, request.degree) == request:
if self.preserve_on_data_change or prepare(source, request.series.key, request.method, request.output_count, request.degree, request.backend) == request: # Проверяем каждый слой с его собственным решателем, чтобы оба варианта оставались на графике.
retained.append(result)
if len(retained) != len(self._results):
self._results = retained
@@ -173,6 +181,7 @@ class SignalProcessingPanel(QWidget):
def _update_actions(self):
self.degree.setEnabled(self.method.currentData() == "polynomial")
self.backend.setEnabled(self.method.currentData() == "polynomial") # Выбор решателя доступен только для метода МНК, где есть два разных алгоритма.
request = self._signature()
self.apply_button.setEnabled(self._job is None and request is not None and
len(request.series.points) >= 2)
@@ -250,9 +259,12 @@ class SignalProcessingPanel(QWidget):
duplicates = result.input_count - result.unique_count
self.status.setText(
f"{self.channel.currentText()}: {result.unique_count} исходных → {len(result.points)} расчётных точек; "
f"СКО на измерениях: {result.rmse:.6g}. "
f"СКО на измерениях: {result.rmse:.6g}; расчёт: {result.elapsed_ms:.3f} мс. " # Показываем точность и время одного и того же результата, включая преобразование буферов.
+ (f"Повторы X усреднены: {duplicates}. " if duplicates else "")
+ f"Расчётных кривых: {len(self._results)}. Исходные данные сохранены.")
comparable = [curve for curve in self._results if replace(curve.request, backend=signature.backend) == signature] if signature.method == "polynomial" else [] # Сопоставляем только одинаковые данные, степень, сетку и окно; различается исключительно решатель.
if len(comparable) > 1: # Сравнение появляется после явного расчёта обоих вариантов на одном наборе данных.
self.status.setText(self.status.text() + "\n" + " | ".join(f"{BACKENDS[curve.request.backend]}: СКО {curve.rmse:.6g}, {curve.elapsed_ms:.3f} мс" for curve in comparable)) # Сохраняем метрики обоих запусков рядом, не объявляя победителя по единичному замеру.
self._update_actions()
self.changed.emit()