Vehicle fingerprint selection

This commit is contained in:
James
2025-12-01 12:00:00 -07:00
parent 59241611b8
commit db9bcb2967
4 changed files with 146 additions and 5 deletions
+131
View File
@@ -1,5 +1,95 @@
#include <QRegularExpression>
#include "frogpilot/ui/qt/offroad/vehicle_settings.h"
QStringList getCarNames(const QString &carMake, QMap<QString, QString> &carModels) {
static const QHash<QString, QString> makeToFolder = {
{"acura", "honda"},
{"audi", "volkswagen"},
{"buick", "gm"},
{"cadillac", "gm"},
{"chevrolet", "gm"},
{"chrysler", "chrysler"},
{"cupra", "volkswagen"},
{"dodge", "chrysler"},
{"ford", "ford"},
{"genesis", "hyundai"},
{"gmc", "gm"},
{"holden", "gm"},
{"honda", "honda"},
{"hyundai", "hyundai"},
{"jeep", "chrysler"},
{"kia", "hyundai"},
{"lexus", "toyota"},
{"lincoln", "ford"},
{"man", "volkswagen"},
{"mazda", "mazda"},
{"nissan", "nissan"},
{"peugeot", "psa"},
{"ram", "chrysler"},
{"rivian", "rivian"},
{"seat", "volkswagen"},
{"škoda", "volkswagen"},
{"subaru", "subaru"},
{"tesla", "tesla"},
{"toyota", "toyota"},
{"volkswagen", "volkswagen"}
};
QStringList carNames;
const QString folder = makeToFolder.value(carMake.toLower());
if (folder.isEmpty()) {
return carNames;
}
QFile file(QString("../../opendbc/car/%1/values.py").arg(folder));
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
return carNames;
}
QString content = file.readAll();
file.close();
static const QRegularExpression commentRe("#[^\\n]*");
static const QRegularExpression footnoteRe("footnotes=\\[[^\\]]*\\],\\s*");
content.remove(commentRe).remove(footnoteRe);
static const QRegularExpression platformRe("(\\w+)\\s*=\\s*\\w+\\s*\\(");
QRegularExpressionMatchIterator platformIt = platformRe.globalMatch(content);
QVector<QPair<int, QString>> platforms;
while (platformIt.hasNext()) {
QRegularExpressionMatch match = platformIt.next();
platforms.append({match.capturedStart(), match.captured(1)});
}
platforms.append({content.length(), QString()});
static const QRegularExpression carNameRe("CarDocs\\w*\\s*\\(\\s*\"([^\"]+)\"");
const QString lowerMake = carMake.toLower();
for (int i = 0; i < platforms.size() - 1; ++i) {
int start = platforms[i].first;
int end = platforms[i + 1].first;
const QString &platformName = platforms[i].second;
QRegularExpressionMatchIterator carIt = carNameRe.globalMatch(
content.mid(start, end - start)
);
while (carIt.hasNext()) {
QString carName = carIt.next().captured(1);
if (carName.startsWith(carMake, Qt::CaseInsensitive)) {
carModels[carName] = platformName;
carNames.append(carName);
}
}
}
carNames.sort(Qt::CaseInsensitive);
return carNames;
}
FrogPilotVehiclesPanel::FrogPilotVehiclesPanel(FrogPilotSettingsWindow *parent, bool forceOpen) : FrogPilotListWidget(parent), parent(parent) {
forceOpenDescriptions = forceOpen;
@@ -12,6 +102,37 @@ FrogPilotVehiclesPanel::FrogPilotVehiclesPanel(FrogPilotSettingsWindow *parent,
vehiclesLayout->addWidget(vehiclesPanel);
QStringList makes = {
"Acura", "Audi", "Buick", "Cadillac", "Chevrolet", "Chrysler", "CUPRA",
"Dodge", "Ford", "Genesis", "GMC", "Holden", "Honda", "Hyundai", "Jeep",
"Kia", "Lexus", "Lincoln", "MAN", "Mazda", "Nissan", "Peugeot", "Ram",
"Rivian", "SEAT", "Škoda", "Subaru", "Tesla", "Toyota", "Volkswagen"
};
ButtonControl *selectMakeButton = new ButtonControl(tr("Car Make"), tr("SELECT"));
QObject::connect(selectMakeButton, &ButtonControl::clicked, [makes, selectMakeButton, this]() {
QString makeSelection = MultiOptionDialog::getSelection(tr("Choose your car make"), makes, "", this);
if (!makeSelection.isEmpty()) {
params.put("CarMake", makeSelection.toStdString());
selectMakeButton->setValue(makeSelection);
}
});
settingsList->addItem(selectMakeButton);
ButtonControl *selectModelButton = new ButtonControl(tr("Car Model"), tr("SELECT"));
QObject::connect(selectModelButton, &ButtonControl::clicked, [selectModelButton, this]() {
QString modelSelection = MultiOptionDialog::getSelection(tr("Choose your car model"), getCarNames(QString::fromStdString(params.get("CarMake")).toLower(), carModels), "", this);
if (!modelSelection.isEmpty()) {
params.put("CarModel", carModels.value(modelSelection).toStdString());
params.put("CarModelName", modelSelection.toStdString());
selectModelButton->setValue(modelSelection);
}
});
settingsList->addItem(selectModelButton);
forceFingerprint = new ParamControl("ForceFingerprint", tr("Disable Automatic Fingerprint Detection"), tr("<b>Force the selected fingerprint</b> and prevent it from ever changing."), "");
settingsList->addItem(forceFingerprint);
disableOpenpilotLong = new ParamControl("DisableOpenpilotLongitudinal", tr("Disable openpilot Longitudinal Control"), tr("<b>Disable openpilot longitudinal</b> and use the car's stock ACC instead."), "");
QObject::connect(disableOpenpilotLong, &ToggleControl::toggleFlipped, [parent, this](bool state) {
if (state) {
@@ -195,10 +316,18 @@ FrogPilotVehiclesPanel::FrogPilotVehiclesPanel(FrogPilotSettingsWindow *parent,
openDescriptions(forceOpenDescriptions, toggles);
QObject::connect(uiState(), &UIState::offroadTransition, [selectMakeButton, selectModelButton, this]() {
std::thread([selectMakeButton, selectModelButton, this]() {
selectMakeButton->setValue(QString::fromStdString(params.get("CarMake", true)));
selectModelButton->setValue(QString::fromStdString(params.get(params.get("CarModelName").empty() ? "CarModel" : "CarModelName", true)));
}).detach();
});
QObject::connect(parent, &FrogPilotSettingsWindow::closeSubPanel, [vehiclesLayout, vehiclesPanel, this] {
if (forceOpenDescriptions) {
openDescriptions(forceOpenDescriptions, toggles);
disableOpenpilotLong->showDescription();
forceFingerprint->showDescription();
}
vehiclesLayout->setCurrentWidget(vehiclesPanel);
});
@@ -208,6 +337,7 @@ FrogPilotVehiclesPanel::FrogPilotVehiclesPanel(FrogPilotSettingsWindow *parent,
void FrogPilotVehiclesPanel::showEvent(QShowEvent *event) {
if (forceOpenDescriptions) {
disableOpenpilotLong->showDescription();
forceFingerprint->showDescription();
}
QStringList detected;
@@ -298,6 +428,7 @@ void FrogPilotVehiclesPanel::updateToggles() {
}
disableOpenpilotLong->setVisible((parent->hasOpenpilotLongitudinal || parent->openpilotLongitudinalControlDisabled) && !parent->hasAlphaLongitudinal && parent->tuningLevel >= parent->frogpilotToggleLevels["DisableOpenpilotLongitudinal"].toBool());
forceFingerprint->setVisible(parent->tuningLevel >= parent->frogpilotToggleLevels["ForceFingerprint"].toBool());
openDescriptions(forceOpenDescriptions, toggles);
@@ -35,6 +35,9 @@ private:
FrogPilotSettingsWindow *parent;
ParamControl *disableOpenpilotLong;
ParamControl *forceFingerprint;
Params params;
QMap<QString, QString> carModels;
};
+11 -4
View File
@@ -14,6 +14,7 @@ from opendbc.car.mock.values import CAR as MOCK
from opendbc.car.toyota.values import ToyotaFrogPilotFlags
from opendbc.car.values import BRANDS
from opendbc.car.vin import get_vin, is_valid_vin, VIN_UNKNOWN
from openpilot.common.params import Params
FRAME_FINGERPRINT = 100 # 1s
@@ -156,12 +157,18 @@ def fingerprint(can_recv: CanRecvCallable, can_send: CanSendCallable, set_obd_mu
def get_car(can_recv: CanRecvCallable, can_send: CanSendCallable, set_obd_multiplexing: ObdCallback, alpha_long_allowed: bool,
is_release: bool, num_pandas: int = 1, cached_params: CarParamsT | None = None, frogpilot_toggles: SimpleNamespace = None):
is_release: bool, params: Params, num_pandas: int = 1, cached_params: CarParamsT | None = None, frogpilot_toggles: SimpleNamespace = None):
candidate, fingerprints, vin, car_fw, source, exact_match = fingerprint(can_recv, can_send, set_obd_multiplexing, num_pandas, cached_params)
if candidate is None:
carlog.error({"event": "car doesn't match any fingerprints", "fingerprints": repr(fingerprints)})
candidate = "MOCK"
if candidate is None or frogpilot_toggles.force_fingerprint:
if frogpilot_toggles.force_fingerprint:
candidate = frogpilot_toggles.car_model
else:
carlog.error({"event": "car doesn't match any fingerprints", "fingerprints": repr(fingerprints)})
candidate = "MOCK"
else:
params.put_nonblocking("CarMake", candidate.split('_')[0].title())
params.put_nonblocking("CarModel", str(candidate))
if frogpilot_toggles.block_user:
candidate = "MOCK"
+1 -1
View File
@@ -106,7 +106,7 @@ class Car:
with car.CarParams.from_bytes(cached_params_raw) as _cached_params:
cached_params = _cached_params
self.CI = get_car(*self.can_callbacks, obd_callback(self.params), alpha_long_allowed, is_release, num_pandas, cached_params, get_frogpilot_toggles())
self.CI = get_car(*self.can_callbacks, obd_callback(self.params), alpha_long_allowed, is_release, self.params, num_pandas, cached_params, get_frogpilot_toggles())
self.RI = interfaces[self.CI.CP.carFingerprint].RadarInterface(self.CI.CP)
self.CP = self.CI.CP