Расширить DEIF, загрузку CAN и общую обработку сигналов
Добавить общее ядро DEIF, эмуляцию приборов и переносимые панели Python/Qt. Расширить загрузчики F2812 и F28335, обновление Flash и профиль STM Invertor. Добавить альтернативный МНК Eigen, модели неисправностей и диагностику сигналов. Обновить справочники устройств, проекты CCS и регрессионные проверки.
This commit is contained in:
@@ -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()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user