Merge branch 'refs/heads/master-at-17032025' into sync-17032025

# Conflicts:
#	opendbc_repo
#	selfdrive/car/card.py
#	selfdrive/car/tests/test_car_interfaces.py
#	selfdrive/car/tests/test_models.py
#	selfdrive/controls/controlsd.py
#	selfdrive/controls/lib/tests/test_latcontrol.py
#	selfdrive/pandad/panda.h
#	system/manager/process_config.py
Sync: `commaai/opendbc:master` into `sunnypilot/opendbc:master-new`
This commit is contained in:
DevTekVE
2025-03-17 09:11:51 +01:00
committed by Jason Wen
29 changed files with 157 additions and 88 deletions
+23 -5
View File
@@ -237,7 +237,7 @@ SpectraCamera::SpectraCamera(SpectraMaster *master, const CameraConfig &config)
: m(master),
enabled(config.enabled),
cc(config) {
ife_buf_depth = (cc.output_type == ISP_RAW_OUTPUT) ? 4 : VIPC_BUFFER_COUNT;
ife_buf_depth = VIPC_BUFFER_COUNT;
assert(ife_buf_depth < MAX_IFE_BUFS);
}
@@ -266,7 +266,7 @@ int SpectraCamera::clear_req_queue() {
req_mgr_flush_request.link_hdl = link_handle;
req_mgr_flush_request.flush_type = CAM_REQ_MGR_FLUSH_TYPE_ALL;
int ret = do_cam_control(m->video0_fd, CAM_REQ_MGR_FLUSH_REQ, &req_mgr_flush_request, sizeof(req_mgr_flush_request));
LOGD("flushed all req: %d", ret);
LOGD("flushed all req: %d", ret); // returns a "time until timeout" on clearing the workq
for (int i = 0; i < MAX_IFE_BUFS; ++i) {
destroySyncObjectAt(i);
@@ -1330,7 +1330,16 @@ bool SpectraCamera::handle_camera_event(const cam_req_mgr_message *event_data) {
uint64_t timestamp = event_data->u.frame_msg.timestamp; // timestamped in the kernel's SOF IRQ callback
//LOGD("handle cam %d ts %lu req id %lu frame id %lu", cc.camera_num, timestamp, request_id, frame_id_raw);
if (stress_test("skipping SOF event")) return false;
// if there's a lag, some more frames could have already come in before
// we cleared the queue, so we'll still get them with valid (> 0) request IDs.
if (timestamp < last_requeue_ts) {
LOGD("skipping frame: ts before requeue / cam %d ts %lu req id %lu frame id %lu", cc.camera_num, timestamp, request_id, frame_id_raw);
return false;
}
if (stress_test("skipping SOF event")) {
return false;
}
if (!validateEvent(request_id, frame_id_raw)) {
return false;
@@ -1381,7 +1390,7 @@ bool SpectraCamera::validateEvent(uint64_t request_id, uint64_t frame_id_raw) {
if (request_id != request_id_last + 1) {
LOGE("camera %d requests skipped %ld -> %ld", cc.camera_num, request_id_last, request_id);
clearAndRequeue(request_id_last + 1);
clearAndRequeue(request_id + 1);
return false;
}
}
@@ -1392,6 +1401,7 @@ void SpectraCamera::clearAndRequeue(uint64_t from_request_id) {
// clear everything, then queue up a fresh set of frames
LOGW("clearing and requeuing camera %d from %lu", cc.camera_num, from_request_id);
clear_req_queue();
last_requeue_ts = nanos_since_boot();
for (uint64_t id = from_request_id; id < from_request_id + ife_buf_depth; ++id) {
enqueue_frame(id);
}
@@ -1402,12 +1412,19 @@ bool SpectraCamera::waitForFrameReady(uint64_t request_id) {
int buf_idx = request_id % ife_buf_depth;
assert(sync_objs_ife[buf_idx]);
if (stress_test("sync sleep time")) {
util::sleep_for(350);
return false;
}
auto waitForSync = [&](uint32_t sync_obj, int timeout_ms, const char *sync_type) {
double st = millis_since_boot();
struct cam_sync_wait sync_wait = {};
sync_wait.sync_obj = sync_obj;
sync_wait.timeout_ms = stress_test(sync_type) ? 1 : timeout_ms;
bool ret = do_sync_control(m->cam_sync_fd, CAM_SYNC_WAIT, &sync_wait, sizeof(sync_wait)) == 0;
if (!ret) LOGE("camera %d %s failed", cc.camera_num, sync_type);
double et = millis_since_boot();
if (!ret) LOGE("camera %d %s failed after %.2fms", cc.camera_num, sync_type, et-st);
return ret;
};
@@ -1419,6 +1436,7 @@ bool SpectraCamera::waitForFrameReady(uint64_t request_id) {
// BPS is typically 7ms
success = waitForSync(sync_objs_bps[buf_idx], 50, "BPS sync");
}
return success;
}
+10 -4
View File
@@ -181,6 +181,7 @@ public:
int sync_objs_ife[MAX_IFE_BUFS] = {};
int sync_objs_bps[MAX_IFE_BUFS] = {};
uint64_t request_id_last = 0;
uint64_t last_requeue_ts = 0;
uint64_t frame_id_raw_last = 0;
int invalid_request_count = 0;
bool skip_expected = true;
@@ -202,11 +203,16 @@ private:
inline static bool first_frame_synced = false;
// a mode for stressing edge cases: realignment, sync failures, etc.
inline bool stress_test(const char* log) {
static double prob = std::stod(util::getenv("SPECTRA_ERROR_PROB", "-1"));;
bool triggered = (prob > 0) && ((static_cast<double>(rand()) / RAND_MAX) < prob);
inline bool stress_test(std::string log) {
static double last_trigger = 0;
static double prob = std::stod(util::getenv("SPECTRA_ERROR_PROB", "-1"));
static double dt = std::stod(util::getenv("SPECTRA_ERROR_DT", "1"));
bool triggered = (prob > 0) && \
((static_cast<double>(rand()) / RAND_MAX) < prob) && \
(millis_since_boot() - last_trigger) > dt;
if (triggered) {
LOGE("stress test (cam %d): %s", cc.camera_num, log);
last_trigger = millis_since_boot();
LOGE("stress test (cam %d): %s", cc.camera_num, log.c_str());
}
return triggered;
}
+5 -1
View File
@@ -221,8 +221,12 @@ class PythonProcess(ManagerProcess):
if self.proc is not None:
return
# TODO: this is just a workaround for this tinygrad check:
# https://github.com/tinygrad/tinygrad/blob/ac9c96dae1656dc220ee4acc39cef4dd449aa850/tinygrad/device.py#L26
name = self.name if "modeld" not in self.name else "MainProcess"
cloudlog.info(f"starting python {self.module}")
self.proc = Process(name=self.name, target=self.launcher, args=(self.module, self.name))
self.proc = Process(name=name, target=self.launcher, args=(self.module, self.name))
self.proc.start()
self.watchdog_seen = False
self.shutting_down = False
+2 -4
View File
@@ -105,10 +105,8 @@ procs = [
PythonProcess("micd", "system.micd", iscar),
PythonProcess("timed", "system.timed", always_run, enabled=not PC),
# TODO: Make python process once TG allows opening QCOM from child pro
# https://github.com/tinygrad/tinygrad/blob/ac9c96dae1656dc220ee4acc39cef4dd449aa850/tinygrad/device.py#L26
NativeProcess("modeld", "selfdrive/modeld", ["./modeld.py"], and_(only_onroad, is_stock_model)),
NativeProcess("dmonitoringmodeld", "selfdrive/modeld", ["./dmonitoringmodeld.py"], driverview, enabled=(WEBCAM or not PC)),
PythonProcess("modeld", "selfdrive.modeld.modeld", and_(only_onroad, is_stock_model)),
PythonProcess("dmonitoringmodeld", "selfdrive.modeld.dmonitoringmodeld", driverview, enabled=(WEBCAM or not PC)),
NativeProcess("sensord", "system/sensord", ["./sensord"], only_onroad, enabled=not PC),
NativeProcess("ui", "selfdrive/ui", ["./ui"], always_run, watchdog_max_dt=(5 if not PC else None)),