Commit Graph

109 Commits

Author SHA1 Message Date
brianhansonxyz 1e4e95ca6b controlsd: longer park-grace cap + 3s suppression on engage attempt
Two-part fix for the alert burst on first engage:

1. Park-exit grace cap 8s -> 15s. The cold-spawn chain (modeld load
   thneed -> publish modelV2 -> plannerd publish longitudinalPlan ->
   frogpilot_process publish frogpilotPlan) often takes more than 8s,
   so the previous cap let commIssue start firing right when the user
   was trying to engage.

2. Track CS.cruiseState.enabled rising edge as last_engage_attempt_frame
   (separate from last_engaged_frame which only fires on successful
   transition into ENABLED_STATES). Suppress both controlsdLagging and
   commIssue events for the first 3s after either edge.

   Why two edges: NO_ENTRY alerts fire on engage *attempt* (before
   self.enabled flips), so the post-success grace alone doesn't cover
   the case where the user presses SET and gets blocked. Tracking the
   cruise-enabled rising edge in update_events (using self.CS_prev)
   catches that.

Result: pressing SET no longer pops "Controls Process Lagging: Reboot
Your Device" or "Communication Issue Between Processes" for the brief
window between the engage attempt and services settling. Real lag /
comm issues that persist beyond 3s still alert normally.
2026-05-04 20:03:06 -05:00
brianhansonxyz 8e4134c4ed controlsd: suppress controlsdLagging for 3s after engagement
The state transition into State.enabled briefly bumps the controlsd
loop time over its budget, which trips rk.lagging and triggers the
'Controls Process Lagging: Reboot Your Device' NoEntryAlert right as
the user takes their hands off the wheel. The lag clears within a
couple cycles and self-driving continues fine.

Track the rising edge of self.enabled, and skip the controlsdLagging
event for the first 3 seconds after engagement. All other lag triggers
(unrelated to engagement) still fire normally.
2026-05-04 19:52:03 -05:00
brianhansonxyz aac5f9d264 controlsd: gate park mode behind init + 10s post-init delay
Park mode is now suppressed until two things happen:
1. self.initialized flips True (controlsd's normal init path runs and
   calls self.card.initialize() — which is what wires CarInterface up
   so controls_update actually generates CAN sends).
2. 10 s of normal step() execution after init completes.

Without (1), park_mode_tick's controls_update was a silent no-op — the
"keepalive heartbeat" never reached the car's ECU, and downstream
publishes (carState, carParams, carOutput) never went out either,
which is why the UI was stuck on the splash even after a gear shift
to drive (scene.parked reads the same carState that wasn't being
published).

The 10 s buffer (2) also gives all only_onroad_active processes a
clean cold-start window before manager starts pausing them — no
risk of catching them mid-init when ParkMode flips on.
2026-05-04 19:48:14 -05:00
brianhansonxyz 5d18ad1e72 park mode: pause self-driving processes, keep car ECU heartbeat
When the car is in park (ignition still on), pause everything that
isn't strictly needed and keep controlsd in a minimal-cycle keepalive
mode so the car's steering ECU keeps seeing the LFA/LKAS CAN-FD
messages and stays in tester mode (no steering fault on shift to drive).

controlsd:
- Detects park gear, writes ParkMode to /dev/shm/params.
- park_mode_tick(): publishes a do-nothing CarControl through
  self.card (CarController.update unconditionally appends the
  steering messages every cycle, which is the actual heartbeat).
  Still runs clearpilot_state_control so the LFA/debug button +
  ScreenDisplayMode keep working in park.
- After park→drive, stay in keepalive-tick mode until SubMaster
  reports all services healthy (an 8s hard cap as safety net).
  Avoids the burst of commIssue alerts that would otherwise fire
  while modeld/plannerd/paramsd/torqued/dmonitoringd/calibrationd/
  frogpilot_process spin back up from cold.

manager / process_config:
- New gating helpers _park_mode(), only_onroad_active,
  driverview_active, always_run_unless_parked.
- Re-gated to only_onroad_active: modeld, sensord, soundd, locationd,
  calibrationd, torqued, paramsd, plannerd, radard, speed_logicd.
- Re-gated to driverview_active: dmonitoringmodeld, dmonitoringd.
- frogpilot_process → always_run_unless_parked (preserves offroad
  behavior, only pauses when ignition+parked).
- controlsd stays plain only_onroad — it's the writer + heartbeat.
- ParkMode registered in params.cc, defaulted to "0" in manager_init.

thermald + fan_controller (broken-tree rule, ported):
- thermald subscribes to carState; passes standstill, is_parked,
  cruise_engaged into fan_controller.update.
- New fan range rules:
    parked        → 0-100% (no floor, full cooling)
    cruise on     → 30-100%
    standstill    → 10-100%
    moving        → 30-100%
    ignition off  → 0-30% (existing)

dashcamd: already park-aware via gear-driven trip lifecycle — no
change needed.

Verified: build clean. Launched on bench: park mode kicks in on
startup (gearShifter=unknown initially → eventually park),
modeld/plannerd/paramsd/torqued/calibrationd/frogpilot_process
correctly disappear from the manager process list, gpsd/dashcamd/
ui/controlsd/pandad stay alive, ParkMode=1 in /dev/shm/params.
2026-05-04 19:36:38 -05:00
brianhansonxyz 0d1ceddad2 ui: restore ready_text.png splash sprite
ready.cc:75 loads /data/openpilot/selfdrive/clearpilot/theme/clearpilot/images/ready_text.png
to render the 'ready' text on the splash, but the asset never got copied
during the C++ UI port (b5a0b32). isNull() check meant it just silently drew
nothing. Copied verbatim from the broken tree.
stable_5_4_26
2026-05-04 19:07:54 -05:00
brianhansonxyz 20ea43f317 nightrider: render lane-change UI (was hidden by disengaged-skip)
During a lane change controlsd writes no_lat_lane_change=true; OnroadWindow
forces edgeColor to STATUS_DISENGAGED so the bg color flips to the disengaged
hue. drawLaneLines's nightrider early-return ("hide all when disengaged")
was matching that and skipping the entire frame, so nightrider showed nothing
during the lane change.

Fixes:
- Hoist is_no_lat_lane_change above the early-return.
- Skip the early-return during lane change, so lane lines + road edges +
  blind-spot path still render in nightrider while the maneuver is active
  (matching how nightrider looks when normally engaged).
- In the outline-only track-polygon branch: when in lane change, draw the
  track as a 4 px outline of CHANGE_LANE_PATH_COLOR (the same yellow used
  to fill the polygon in normal mode) instead of the usual 3 px light-blue
  guide. Hollow shape, thicker stroke — matches Brian's spec.

Normal-mode rendering is unchanged.
2026-05-04 19:07:54 -05:00
brianhansonxyz f6516eb4cc CLAUDE.md: bring up to date with current state
Reorganized 'Current State' to enumerate what's actually active in the
tree now (boot/build infra, suppressed logging, gpsd, UI port, display
modes, speed_logicd, sound, dashcamd) instead of the brief summary of
just the early commits.

Trimmed 'Pending' to remove items that are now ported (UI, gpsd, sound,
speed_logic, display modes, dashcamd, memory-param scaffolding). Kept
the still-pending items: HDA2 driving-behavior tweaks, CarSpeedLimit
publisher, telemetry, bench mode, power/thermal, session logging.

Notes added:
- Memory-param keys currently registered (so the next port doesn't
  re-register existing keys by accident)
- camerad gating change to always_run (and the power tradeoff)
- Branch policy: clearpilot is the canonical/main branch; do not
  abandon it for another
- pkill _text by comm in the launch chain
- speed-limit widget shows 0 until carstate.py CAN-FD writes
  CarSpeedLimit
2026-05-03 23:19:41 -05:00
brianhansonxyz 8a7a776f9b port dashcamd: hardware-encoded MP4 dashcam
VisionIPC frames from camerad → OMX H.264 hardware encoder → 3-min MP4
segments + SRT GPS subtitles in /data/media/0/videos/<trip>/. Manages
its own trip lifecycle (WAITING/RECORDING/IDLE_TIMEOUT) and writes
DashcamState/DashcamFrames memory params for the UI's Status window.
Honors DashcamShutdown for graceful close before power-off.

Files added:
- selfdrive/clearpilot/dashcamd.cc + SConscript

Files modified:
- selfdrive/frogpilot/screenrecorder/omx_encoder.{cc,h}: ported broken's
  version, which adds encode_frame_nv12() (direct NV12 input from camerad,
  alongside the existing encode_frame_rgba used by the disabled screen
  recorder) and simplifies the libyuv conversion paths to NEON-only since
  this device is aarch64.
- selfdrive/SConscript: register selfdrive/clearpilot/SConscript so the
  dashcamd binary is part of the build.
- selfdrive/manager/process_config.py:
  - camerad gating driverview → always_run so dashcamd can record the
    moment ignition+drive arrives without waiting for camera startup.
  - Register dashcamd as NativeProcess gated always_run.
- system/loggerd/deleter.py:
  - MIN_BYTES 5 GB → 9 GB to leave headroom for dashcam footage.
  - delete_oldest_video(): trip-aware cleanup. Drops entire oldest trip
    dir first; if only the active trip remains, drops oldest segment
    inside it; cleans up legacy flat .mp4s too.
  - cleanup_log2(): keeps /data/log2 session logs under 4 GB total.
  - Hooked into deleter_thread: video first when out of bytes/percent;
    log2 quota check on the idle path. New code uses print(stderr) per
    the no-cloudlog rule.

Verified: built clean, manager started, dashcamd in WAITING state
(DashcamState=waiting, DashcamFrames=0), camerad running, no errors.
2026-05-03 23:14:23 -05:00
brianhansonxyz 3f5172b58b controlsd: 5-state ScreenDisplayMode machine + park->drive auto-wake
Replaces the 3-state cycle in clearpilot_state_control with broken's
5-state machine:
- Onroad (drive gear): 0→4, 1→2, 2→3, 3→4, 4→2 (auto modes never
  reached via the button — gpsd's day/night calc is the only path back)
- Not in drive: anything except 3 → 3 (screen off), state 3 → 0 (auto)

Also tracks park→drive edge to auto-wake from screen-off (mode 3 → 0)
when shifting into drive, regardless of how the screen got turned off.

Speed/cruise overlay state intentionally left out — owned by
speed_logicd, not controlsd.

events.py: remove clp_debug_notice + EventName.clpDebug binding
(no more "Clearpilot Debug Function Executed (N)" on-screen message).
The dead commented-out events.add(EventName.clpDebug) and stray print
of buttonEvents in controlsd are also cleaned up.
2026-05-03 23:06:57 -05:00
brianhansonxyz b5a0b3221c port C++ UI from broken: ready/splash, ClearPilot menu, onroad widgets, nightrider
Wholesale port of the 10 UI files (main.cc, ui.{cc,h}, onroad.{cc,h},
home.{cc,h}, window.{cc,h}, ready.{cc,h}, SConscript). sidebar.{cc,h}
unchanged.

Brings in:
- splash/ready screen rendered via Qt directly (replaces stock home)
- ready page shown when started but gear=park
- ClearPilot offroad menu (Home/Dashcam/Debug panels) replacing stock
- onroad UI: speed widget reading ClearpilotSpeedDisplay (gpsLocation
  via speed_logicd), speed-limit widget, cruise-vs-limit warning sign
- nightrider mode: camera suppressed, lane lines/path drawn as 2px
  outlines only (ScreenDisplayMode 1 or 4)
- screen power: ScreenDisplayMode 3 forces setAwake(false)
- ignition off blanks screen immediately (tap still wakes)
- Qt-based RPC widget-tree dump server at ipc:///tmp/clearpilot_ui_rpc
- crash handler in main.cc with stack trace

Deviation from broken (3 sites): no_lat_lane_change is read from
paramsMemory (controlsd writes it there in baseline) instead of broken's
custom cereal field frogpilotCarControl.noLatLaneChange. Keeps the
existing self-driving wiring intact.

SConscript drops screenrecorder.cc from qt_src (broken did the same;
omx_encoder.cc still built for dashcamd's future link).

Build clean. UI hasn't been launched yet — that comes after the
controlsd button-handler commit lands.

Translation .ts files are auto-regenerated by Qt's lupdate; included.
2026-05-03 23:03:42 -05:00
brianhansonxyz 41c154014a add speed_logic daemon: write speed + cruise warning display params
Standalone managed process (selfdrive.clearpilot.speed_logicd) that
ticks SpeedState at 2 Hz from cereal subscriptions. Decoupled from
controlsd so self-driving timing isn't affected.

Inputs:
- gpsLocation.speed + hasFix (vehicle speed)
- carState.cruiseState (cruise speed, enabled, standstill)
- CarSpeedLimit memory param (will be populated when carstate.py CAN-FD
  decode is ported; 0 until then — speed-limit + warning logic
  naturally short-circuits)
- IsMetric persistent param (read once at startup)

Outputs (memory params, gated on change to avoid 2 Hz tmpfs churn):
- ClearpilotSpeedDisplay, ClearpilotHasSpeed, ClearpilotSpeedUnit
- ClearpilotSpeedLimitDisplay, ClearpilotIsMetric
- ClearpilotCruiseWarning, ClearpilotCruiseWarningSpeed
- ClearpilotPlayDing (one-shot trigger consumed by soundd)

speed_logic.py copied verbatim from broken; speed_logicd.py is new
glue. Registered as PythonProcess gated on only_onroad.
2026-05-03 22:52:39 -05:00
brianhansonxyz 5d2fe5248b soundd: mix ClearPilot ding alongside alerts; add ding.wav
Adds the ding sound effect that fires on speed-limit changes / cruise-
warning transitions (writers will set ClearpilotPlayDing="1" in
/dev/shm/params; soundd polls at ~2Hz, clears the flag, plays once).

The ding plays at MAX_VOLUME independent of the alert volume map and
mixes into the same output stream. Single-shot — no looping. Loaded
once at startup; failure to load just disables the ding (rest of soundd
keeps working).

print-to-stderr, not cloudlog (CLAUDE.md rule).
2026-05-03 22:50:46 -05:00
brianhansonxyz f687d712e7 gpsd: re-add day/night auto display-mode switch
Restores the IsDaylight + ScreenDisplayMode auto-switch logic that was
stripped during the initial gpsd port (since the display-modes feature
hadn't been touched yet).

NOAA solar-position calc against the current GPS fix:
- First calc immediately when system clock is valid + GPS has fix
- Every 30s thereafter
- Writes IsDaylight memory param on change (twice/day, gated)
- ScreenDisplayMode 0 + sunset → 1 (auto-nightrider)
- ScreenDisplayMode 1 + sunrise → 0 (auto-normal)
- Manual modes 2/3/4 are never touched

Print-to-stderr instead of cloudlog per CLAUDE.md.
2026-05-03 22:47:28 -05:00
brianhansonxyz 837c14e081 register clearpilot memory params and seed defaults in manager_init
Scaffolding for the upcoming UI port. Registers the keys the new C++
UI and SpeedState daemon will read/write, and seeds /dev/shm/params
defaults at manager start so first-readers don't see missing keys
before writers spin up.

Keys registered: CarIsMetric, ClearpilotCruiseWarning,
ClearpilotCruiseWarningSpeed, ClearpilotHasSpeed, ClearpilotIsMetric,
ClearpilotPlayDing, ClearpilotShowHealthMetrics,
ClearpilotSpeedDisplay, ClearpilotSpeedLimitDisplay,
ClearpilotSpeedUnit, DashcamFrames, DashcamShutdown, DashcamState,
IsDaylight, ModelFps, ModelStandby, ModelStandbyTs,
ScreenRecorderDebug, ShutdownTouchReset, TelemetryEnabled, VpnEnabled.

PERSISTENT registration here is purely a no-op placeholder: the actual
storage is /dev/shm/params (tmpfs), which clears on every reboot
regardless. Bench-mode keys (Bench*, ClpUiState, LogDirInitialized)
not registered yet — they'll come with their respective features.
2026-05-03 22:45:04 -05:00
brianhansonxyz 3ebfc29d35 port clearpilot gpsd; decouple self-driving from GPS
Adds the AT-command-based GPS daemon for the Quectel EC25 modem (the
device has no u-blox chip and qcomgpsd's diag interface hangs on this
hardware). Trimmed from broken's version: dropped cloudlog calls and
the IsDaylight / ScreenDisplayMode auto-switching (those belong to the
display-modes feature, port later).

Used solely for system clock initialization, on-screen UI speed, and
per-segment dashcam GPS metadata.

Self-driving must NOT consume gpsLocation — feeding it to locationd's
kalman filter screws up the math. Patch locationd to skip GPS:
- locationd_thread() no longer subscribes to gpsLocation/gpsLocationExternal
- handle_msg's GPS branches commented (dead code without subscription)
- the "save LastGPSPosition once a minute when gpsOK" block commented
  (dead because gpsOK is now permanently false)

Result: liveLocationKalman.gpsOK = false for all self-driving consumers
(controlsd, paramsd, torqued, frogpilot_planner). They already handle
that case. Other liveLocationKalman fields still publish from the
camera-odometry + IMU + calibration kalman state.

system/clearpilot/__init__.py added so system.clearpilot.gpsd is a
valid Python module.
2026-05-03 22:29:07 -05:00
brianhansonxyz 54c566c68f kill stale text-error window by comm too, not just path
selfdrive/ui/text is a shell wrapper that execs ./_text. After exec,
the running process's argv has no path component, so
pkill -f 'selfdrive/ui/text' silently misses it. Add pkill -x _text
alongside the existing path-pattern kills in build_only.sh,
launch_openpilot.sh, and launch_chffrplus.sh.
2026-05-03 22:28:33 -05:00
brianhansonxyz f28ba340f2 disable segment + camera + boot data logging
We don't use comma's upload/replay pipeline, and the segment recorder
was silently filling /data with 30 MB rlog files per minute (the 64 GB
we just cleared). Disable the writers entirely.

- process_config.py: comment out loggerd, encoderd, stream_encoderd
- manager.py: skip save_bootlog() at manager init (was writing
  boot info to /data/media/0/realdata/boot/<bootid>/)

deleter still runs for cleanup of any leftover data; logmessaged still
runs (in-memory log routing IPC). uploader was already disabled in
baseline. No process consumes loggerd/encoderd output onroad, so this
is purely a sink removal.
2026-05-03 22:13:06 -05:00
brianhansonxyz 6ec4c7bdac controlsd: ScreenDisplayMode init uses put_int, not put_bool
Every other reference treats ScreenDisplayMode as an int (range 0-4).
The init line was using put_bool(0) which happened to write the same
"0" string and read back fine via get_int, but the type mismatch was
misleading.
2026-05-03 22:08:08 -05:00
brianhansonxyz b57f2d8d70 add CLAUDE.md documenting current state, snapshot locations, and port roadmap
Records: where the broken-but-feature-complete tree lives
(/data/openpilot-broken-2026-05-03 + tag pre-reset-2026-05-03), where
the baseline source still sits (/data/clearpilot-baseline), the
working-baseline-2 tag, and a categorized roadmap of features in the
broken tree that still need to be ported (driving behavior, onroad UI,
speed/cruise logic, dashcamd, gpsd, telemetry, bench mode, power/
thermal, memory params, session logging).

Carries forward the operational guardrails from the previous CLAUDE.md
(no cloudlog, chown to comma, samba/git/test workflow, params/cereal
gotchas, device specifics) so future sessions don't re-learn them.
2026-05-03 21:58:42 -05:00
brianhansonxyz b287fd094e make baseline build & launch cleanly
- onroad.cc: fix screenDisplayMode reference to nvg->screenDisplayMode
  (baseline had a stale reference to the variable after it moved into
  AnnotatedCameraWidget). Restores the original conditional intent.
- ui/SConscript + home.cc: drop QtWebEngine include/link entirely
  (no longer used by any active code).
- build.py: BUILD_ONLY env var spawns the failure TextWindow fully
  detached (own session, /dev/null stdio) so build_only.sh exits and
  caller can capture stderr; spinner binary update uses temp+os.replace
  so a running spinner doesn't ETXTBSY the build.
- build_only.sh: tee build output to /tmp/build.log and propagate
  build.py's exit code via PIPESTATUS.

Verified: build_only.sh completes cleanly, launch_openpilot.sh boots
the manager and spawns the standard process set.
working-baseline-2
2026-05-03 21:55:48 -05:00
brianhansonxyz 5624898a92 port startup-related changes from broken tree
Restores the boot/launch chain customizations on top of the freshly-reset
baseline. Driving-model and per-feature changes (dashcamd, telemetry, gpsd,
bench mode, manager wiring) are deliberately left out and will be ported
piecemeal.

Brought in:
- launch_openpilot.sh: kill stale instances, run on_start.sh, background
  vpn-monitor + nice-monitor, BENCH_MODE pass-through
- launch_chffrplus.sh: source build_preflight.sh, kill stale text-error UI
- build_only.sh, build_preflight.sh
- system/clearpilot/on_start.sh: SSH keys, ssh.service enable, git.hanson.xyz
  Host config, WiFi radio on, run provision.sh
- system/clearpilot/provision.sh + provision_wrapper.sh: connectivity wait,
  apt install (openvpn, curl, ccrypt, nodejs), Claude Code installer, git
  remote fix, fast-forward origin/clearpilot, /data/quick_boot
- system/clearpilot/vpn-monitor.sh + vpn.ovpn: OpenVPN tunnel auto-connect
- system/clearpilot/nice-monitor.sh: keep claude processes at nice 19
- system/clearpilot/dev: id_ed25519.{cpt,pub.cpt}, GithubSshKeys, encrypt.sh
  (DongleId-keyed instead of hardware-serial)
- system/clearpilot/tools/{decrypt,encrypt}: switch key source to DongleId
- system/clearpilot/startup_logo/{bg.jpg, generate_logo.sh, set_logo.sh}
- selfdrive/ui/qt/spinner{,.cc,.h}: new spinner with logo

Removed (baseline-only flow superseded by broken's on_start.sh+provision.sh):
- system/clearpilot/dev/on_start.sh
- system/clearpilot/dev/on_start_brian.sh.cpt
- system/clearpilot/dev/provision.sh
2026-05-03 21:07:14 -05:00
brianhansonxyz c2ab0fa662 reset to pre-modification baseline; restart feature work from clean state
Restoring the working tree to the pristine pre-Claude baseline previously
preserved at /data/clearpilot (now /data/clearpilot-baseline). The prior
modified-but-broken tree is snapshotted at /data/openpilot-broken-2026-05-03
and tagged here as pre-reset-2026-05-03 for reference.

From here, features (UI changes, dashcam, telemetry, GPS, display modes,
speed logic, standstill power saving, etc.) will be re-introduced one at
a time with proper testing.
2026-05-03 20:53:51 -05:00
brianhansonxyz f7e602c00b controlsd: re-wire UI hooks on top of restored baseline
Adds back the UI plumbing that the baseline controlsd doesn't have, so
the existing UI features keep working without changing driving logic:

- ScreenDisplayMode 5-state machine (auto-normal, auto-nightrider,
  manual normal, screen-off, manual nightrider) driven by the LFA
  debug button + gear edges. Replaces baseline's simple 0..2 cycle.
  Pure params write — no actuator effect.
- Speed/cruise-warning overlay tick at ~2Hz feeding SpeedState, which
  writes ClearpilotSpeedDisplay / ClearpilotSpeedLimitDisplay /
  ClearpilotCruiseWarning for the onroad UI. Reads gpsLocation,
  CarSpeedLimit, and CS.cruiseState — no actuator effect.
- frogpilot_variables.no_lat_lane_change is now populated alongside
  the existing Params write, so the perf-optimized carcontroller (which
  takes the bit as an argument instead of re-reading Params on the
  100Hz hot path) still sees the lane-change suppression signal.
- ScreenDisplayMode init switched from put_bool to put_int (UI reads
  it as int).
- gpsLocation added to SubMaster (ignore_alive/avg_freq/valid set,
  since gpsd is a ClearPilot addition not present everywhere).

No changes to controlsd's lateral or longitudinal control paths.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
pre-reset-2026-05-03
2026-04-26 08:44:52 -05:00
brianhansonxyz 47321e3867 restore driving logic to pre-variable-fps baseline
Wholesale revert of driving-relevant files to the snapshot in
/projects/openpilot/archive/clearpilot (HEAD 980f0aa). Goal: get
known-good driving behavior back, then re-introduce optimizations
slowly to track down a "feels like the wheel pulls right" regression.

Files restored from baseline:
- selfdrive/controls/controlsd.py
- selfdrive/controls/lib/events.py
- selfdrive/controls/lib/longitudinal_planner.py
- selfdrive/modeld/modeld.py
- selfdrive/modeld/dmonitoringmodeld.py
- selfdrive/locationd/calibrationd.py
- selfdrive/locationd/paramsd.py
- selfdrive/locationd/torqued.py
- selfdrive/car/interfaces.py
- selfdrive/car/hyundai/carstate.py (CAN-FD telemetry preserved as a
  commented block for future re-enable)
- selfdrive/monitoring/dmonitoringd.py
- selfdrive/frogpilot/controls/frogpilot_planner.py
- common/realtime.py

Intentionally NOT restored (kept as current):
- selfdrive/thermald/* (fan/power tuning kept)
- selfdrive/car/hyundai/carcontroller.py + hyundaicanfd.py (perf-only
  hoist of no_lat_lane_change Params read; behavior-equivalent)
- cereal/services.py, cereal/custom.capnp (additive only)
- selfdrive/manager/*, common/params.cc (heavy ClearPilot
  infrastructure: bench mode, log dir, dashcamd, gpsd, params)
- All selfdrive/ui/, selfdrive/clearpilot/, system/clearpilot/

UI features will be re-wired in a follow-up commit.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-26 08:40:02 -05:00
brianhansonxyz 62a403d0f1 UI: shift speed-limit/cruise numbers down; modeld: keep 20fps at standstill-in-drive; health: FPS instead of LAG
prebuilt / build prebuilt (push) Has been cancelled
badges / create badges (push) Has been cancelled
- onroad: speed-limit sign and cruise over/under sign — shift the number
  ~10% further down inside the inner box (adjusted top inset 42→86).
- modeld: narrow the standby condition. Previously 0fps when
  standstill-or-parked; now 0fps only when parked. Standstill in drive
  (red light) continues to run at 20fps so lateral can engage/stay
  responsive and liveCalibration/paramsd keep seeing observations.
  Ignition-off still stops modeld at the manager level.
- Health overlay: replace LAG row with FPS (modeld framerate read from
  ModelFps memory param, which modeld already writes only on standby
  transition — no per-frame writes).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 12:54:56 -05:00
brianhansonxyz 7ee923b0e6 calibrationd: publish valid based on calStatus, not sm.all_checks
prebuilt / build prebuilt (push) Has been cancelled
badges / create badges (push) Has been cancelled
stale / stale (push) Has been cancelled
release / build master-ci (push) Has been cancelled
Previous behavior gated liveCalibration.valid on calibrationd's own
sm.all_checks(). Upstream freq glitches (e.g. carState polling-pattern
artifacts) flapped liveCalibration.valid to False, which cascaded into
locationd: its filterInitialized check requires sm.allAliveAndValid(),
so flapped valid kept locationd uninitialized. While uninitialized,
locationd still published liveLocationKalman but with empty/garbage
angularVelocityCalibrated fields. paramsd's Kalman drank the garbage
and converged to steerRatio ≈ 0, stiffnessFactor ≈ 0 — which
controlsd clamped to 0.1 each and fed into VM.calc_curvature,
producing nonsense curvature commands and visibly jerky steering.

"valid" semantically asks whether the calibration data is
trustworthy — that's a question about convergence (calStatus ==
calibrated), not about input freshness. Switching the gate removes
the cascade: once calibration completes, liveCalibration.valid stays
True stably, locationd initializes, paramsd gets clean observations,
steerRatio converges to the real value.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 14:40:02 -05:00
brianhansonxyz 6a79996a14 park CPU savings + fix early shutdown on virtual battery capacity
prebuilt / build prebuilt (push) Has been cancelled
badges / create badges (push) Has been cancelled
controlsd: in park+ignition-on, run full step() only every 10th cycle.
data_sample still runs every cycle (CAN parse, button detection) and
card.controls_update still runs every cycle (CAN TX heartbeat, counter
increments). Skipped cycles re-send cached controlsState/carControl so
downstream freq_ok stays OK. Button edge handling + display-mode
transitions extracted to handle_screen_mode() and called every cycle so
debug-button presses aren't dropped. controlsd: 55% → 30% CPU in park.

dmonitoringmodeld: subscribe to carState; at standstill, skip model.run
and re-publish last inference. driverStateV2 continues flowing at 10Hz
with known-good last face data (driver can't become distracted relative
to a stopped car). ~5% CPU saved.

power_monitoring: remove the `car_battery_capacity_uWh <= 0` shutdown
trigger. That virtual capacity counter floor-limits to 3e6 µWh on boot
and drains in ~12 min at typical device power, so a short drive (that
doesn't fully recharge the 30e6 µWh virtual cap) followed by a quick
store stop would trip shutdown well before the 30-min idle timer. The
real car-battery-voltage protection (low_voltage_shutdown at 11.8V with
60s debounce) is kept.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 13:46:33 -05:00
brianhansonxyz 426382960a dmonitoringd: narrow update_states gate to fix stuck face_detected
Original gate was sm.all_checks() which required modelV2 fresh and
liveCalibration.valid. Both fail spuriously in our setup:
- modelV2 stops at standstill/parked (two-state modeld)
- calibrationd propagates its own freq_ok glitches into liveCalibration.valid

Either condition froze DM pose updates — face_detected stuck False →
maybe_distracted True → awareness decayed to 0 within ~6s of engagement
("TAKE OVER" driver-distraction alert the moment controlsd engaged).

Narrow the gate to only the subs update_states actually consumes
(driverStateV2, carState, controlsState, liveCalibration), check only
alive+valid (skip freq_ok), and skip liveCalibration.valid — rpyCalib
presence is sufficient proof that calibration has produced output.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 13:21:07 -05:00
brianhansonxyz 54a2a3afc5 add ghost_frame1/2 sprites for manual editing
prebuilt / build prebuilt (push) Has been cancelled
badges / create badges (push) Has been cancelled
Generated from xscreensaver left-facing wag pair (pacman.c frames 5 & 6)
and recolored blue at 2x our bg.jpg sprite size. Saved in source
orientation (portrait, same as bg.jpg) for hand-editing later.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 11:47:01 -05:00
brianhansonxyz 4c8ef93b2b modeld two-state; UI: immediate blank on ignition off; READY splash on wake
- modeld: simplified to 0fps (standstill or parked) or 20fps. Removed
  4/10fps reduced-rate path, republish caching, FPCC/liveCalibration
  reads, and ModelFps per-cycle param writes.
- ui.cc updateWakefulness: ignition on→off now resets interactive_timeout
  to 0 for immediate screen blank. Tap still wakes via existing handler.
- home.cc offroadTransition(false): reset ready->has_driven to false so
  the READY text appears on fresh ignition, not the textless post-drive
  splash.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 11:20:48 -05:00
brianhansonxyz ba4176ffd0 controlsd: suppress freq-only cascade; modeld: variable rate w/ republish cache
prebuilt / build prebuilt (push) Has been cancelled
badges / create badges (push) Has been cancelled
release / build master-ci (push) Has been cancelled
- commIssue now fires only on real comms failure (not_alive or CAN RX
  timeout), not on self-declared valid=False cascades from MSGQ-conflate +
  slow-polling freq_ok artifacts. Car drives fine through these and the
  banner was false-positive.
- Add 5-cycle hysteresis (50ms) on commIssue / posenetInvalid /
  locationdTemporaryError / paramsdTemporaryError.
- Cascade-aware suppression: skip posenet/locationd/paramsd temporary
  errors when the only problem is freq_only_cascade (all alive, just
  freq/valid tripped).
- Remove debug _dbg_dump_alert_triggers helper and EVENTS/EVENT_NAME
  imports.
- Re-enable variable-rate modeld (4/10fps idle, 20fps when lat_active /
  lane_changing / calibrating) with republish caching so consumers get
  constant publish rate.
- Split lat_requested (modeld signal) from lat_engaged (actuator gate).
  Momentary steerFaultTemporary no longer drops modeld rate, preventing
  stale-prediction feedback loop on MDPS recovery. CC.latActive still
  respects the fault.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 21:11:52 -05:00
brianhansonxyz e86eafde15 diag: per-publisher valid=False logging; 30min shutdown; daylight fix; UI tweaks
prebuilt / build prebuilt (push) Has been cancelled
badges / create badges (push) Has been cancelled
stale / stale (push) Has been cancelled
CLAUDE.md: added a "Logging" rule — never use cloudlog (upstream cloud
pipeline, effectively a black hole for us), always use
print(..., file=sys.stderr, flush=True). Manager redirects each process's
stderr to /data/log2/current/{proc}.log. Prefix our lines with "CLP ".

Diagnostic logging — when a publisher sets its own msg.valid=False, log
which specific subscriber tripped the check. Only fires on transition
(True→False) so we don't spam. Covers the services whose cascades we've
been chasing:
  - frogpilot_planner (frogpilotPlan)
  - longitudinal_planner (longitudinalPlan)
  - paramsd (liveParameters)
  - calibrationd (liveCalibration)
  - torqued (liveTorqueParameters)
  - dmonitoringd (driverMonitoringState)

gpsd.is_daylight: fixed a day-boundary bug where the function would flip
to "night" at UTC midnight regardless of actual local sunset. At 85W
sunset is ~00:20 UTC next day, so between local 8pm and actual sunset
the function used *tomorrow's* sunrise/sunset and said night. Now checks
yesterday/today/tomorrow windows with UTC-day offsets.

ui/onroad.cc: nightrider tire-path outline is now light blue (#99CCFF)
at 3px (was white/status-tinted at 6px); lane lines 5% thinner (float
pen width).

thermald/power_monitoring: auto-shutdown timer 10min → 30min.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 19:31:09 -05:00
brianhansonxyz cf2b3fc637 modeld: revert to constant 20fps (except standby at standstill)
prebuilt / build prebuilt (push) Has been cancelled
badges / create badges (push) Has been cancelled
Disabled the variable-rate (4/10/20fps) logic — it caused persistent
downstream freq/valid cascades that broke paramsd's angle-offset
learner, calibrationd, and locationd's one-shot filterInitialized latch.
Symptom: user's "TAKE CONTROL IMMEDIATELY / Communication Issue"
banner during engaged driving, plus subtle right-pull (paramsd's fast
angle-offset adaptation was frozen all session).

Simpler model now:
  standstill → standby (0fps, paused)
  otherwise  → 20fps (no variable rate)

Republish-caching and FPCC.latRequested/noLatLaneChange plumbing left in
place so re-enabling variable rate later is a one-line change
(full_rate = True → the original full_rate expression).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 18:21:46 -05:00
brianhansonxyz 5b67d4798b fix: update services.py rates to match new thermald/gpsd publish rates
prebuilt / build prebuilt (push) Has been cancelled
badges / create badges (push) Has been cancelled
Root cause of "TAKE CONTROL IMMEDIATELY / Communication Issue Between
Processes" during driving: our 4Hz thermald and 2Hz gpsd changes pushed
deviceState, managerState, and gpsLocation above their declared rate
ceilings in cereal/services.py. SubMaster's freq_ok check requires
observed rate within [0.8*min, 1.2*max]; publishing faster than max also
fails the check — which trips all_freq_ok(), raises EventName.commIssue,
and fires the banner.

- deviceState: 2Hz → 4Hz  (matches thermald DT_TRML=0.25)
- gpsLocation: 1Hz → 2Hz  (matches gpsd 2Hz polling)
- managerState: 2Hz → 4Hz (gated on deviceState arrival, inherits rate)

Also:
- Re-enabled dashcamd in process_config.py (was disabled for the
  diagnostic test — dashcamd was innocent).
- Added a CLAUDE.md section documenting this class of bug so the next
  rate change updates services.py too.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 16:28:55 -05:00
brianhansonxyz d64a0f6420 fix: thermald crash on startup — CarState is in cereal.car, not cereal.log
The new fan control plumbing referenced log.CarState.GearShifter.park,
but CarState is defined in car.capnp and only DeviceState/PandaState
live in log.capnp. Thermald crashed immediately on first carState tick,
which meant deviceState stopped publishing entirely. The UI then read
stale/default values — freeSpacePercent=0 → "100% full" → "Out of Storage"
alert despite 65 GB actually free. The fan also stopped updating.

from cereal import car, log  (was just log)
car.CarState.GearShifter.park (was log.CarState...)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 16:12:35 -05:00
brianhansonxyz 4dae5804ab feat: 4Hz fan control with gear/cruise-aware clamps; move hot signals to cereal
Fan control rework (thermald → 4Hz):
- DT_TRML 0.5s → 0.25s (thermald loop + fan PID now at 4Hz)
- New clamp rules based on (gear, cruise_engaged, standstill):
    parked                                → 0-100%
    in drive + cruise engaged (any speed) → 30-100%
    in drive + cruise off + standstill    → 10-100%
    in drive + cruise off + moving        → 30-100%
- thermald now reads gearShifter (via carState) and controlsState.enabled,
  passes them to fan_controller.update()
- Removed BENCH_MODE special case — new rules cover bench automatically
- Removed ignition-based branches — gear is the correct signal

System health overlay:
- Subscribed UI to peripheralState so we can read fanSpeedRpm
- Added FAN row: actual fan% (RPM / 65) to sit alongside LAG/DROP/TEMP/CPU/MEM.
  Shows the real fan output vs. what the PID is asking for.

Migrate hot signals from paramsMemory to cereal (frogpilotCarControl):
- Added latRequested @3 and noLatLaneChange @4 to FrogPilotCarControl schema
- controlsd sets FPCC.latRequested / FPCC.noLatLaneChange (send-on-change
  already gates the IPC)
- modeld reads from sm['frogpilotCarControl'] (added to its subscribers)
  instead of paramsMemory (saves ~20 file-read syscalls/sec)
- carcontroller reads from frogpilot_variables (set in-process by controlsd)
  instead of paramsMemory (saves ~100 file-read syscalls/sec in 100Hz path).
  Dropped carcontroller's now-unused Params instance and import.
- UI (ui.cc, onroad.cc) reads from sm['frogpilotCarControl'].noLatLaneChange
- Removed LatRequested and no_lat_lane_change param registrations + defaults

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 16:01:39 -05:00
brianhansonxyz 25b4672fda feat: raise controlsdLagging alert threshold to 20ms avg
prebuilt / build prebuilt (push) Has been cancelled
badges / create badges (push) Has been cancelled
release / build master-ci (push) Has been cancelled
Stock rk.lagging fires at 11.1ms (90% of 10ms interval), which the
Hyundai CAN load routinely crosses during normal driving as carstate
parses 50-200 msgs/sec. That's normal, not a fault — the same code
behaved the same way at the fork baseline; we just made it visible with
the LAG overlay.

50Hz effective control is still tighter than any human reaction loop.
rk.lagging remains wired to the defensive skip paths (secondary camera
and radar checks) at the original tighter threshold, so we still avoid
false-alarming dependent events when briefly overloaded.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 22:46:07 -05:00
brianhansonxyz 7a0854387e ui: shift health overlay labels 50px left
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 22:35:22 -05:00
brianhansonxyz 83aed16a35 perf: suppress controlsdLagging on engage + gate frogpilotCarControl send
Two related fixes for the post-engage lag spike:

1. controlsdLagging suppressed during lat_engage_suppress window.
   Ratekeeper.lagging triggers when avg cycle duration over 100 cycles
   exceeds 11.1ms (90% of 10ms budget). The modeld 10→20fps ramp causes
   a legitimate transient where downstream services (plannerd, locationd,
   calibrationd, paramsd) each drain 2x the message rate, briefly pushing
   avg cycle time past the threshold. The underlying system isn't broken —
   it's correctly absorbing a scheduled workload transition.

2. frogpilotCarControl now sends only on change (+ 1Hz keepalive) instead
   of every 10ms. The message has 3 bool fields, of which speedLimitChanged
   code is entirely commented out, trafficModeActive flips only on UI
   button press, and alwaysOnLateral changes only on cruise/gear/brake
   edges. plannerd doesn't include frogpilotCarControl in its all_checks
   list so stale-freq detection isn't a concern. Saves ~7ms/sec of
   capnp build + zmq send work.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 22:35:16 -05:00
brianhansonxyz 6dede984dc perf: pin dashcamd to cores 0-3 (little cluster)
Previously ran unpinned (affinity mask 0xff) across all 8 cores. When it
landed on core 4 (controlsd) or 5 (plannerd/radard) or 7 (modeld), its
70MB/s frame copies and MP4 muxing caused cache/memory-bandwidth
contention with the RT-pinned processes. SCHED_FIFO prevented direct
preemption but not the cache thrash.

OMX offloads actual H.264 work to hardware so the main thread is
lightweight — fine on the little cluster.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 22:35:02 -05:00
brianhansonxyz 2ddb7fc764 feat: onroad health overlay + 2x tire path in nightrider
System health overlay:
- Lower-right 5-metric panel: LAG (controlsState.cumLagMs), DROP
  (modelV2.frameDropPerc), TEMP (deviceState.maxTempC), CPU (max core of
  deviceState.cpuUsagePercent), MEM (deviceState.memoryUsagePercent)
- Color-coded white→yellow→red by severity (LAG: 50/200ms, DROP: 5/15%,
  TEMP: 75/88°C, CPU: 75/90%, MEM: 70/85%)
- Toggle in ClearPilot → Debug → "System Health Overlay"
- New param ClearpilotShowHealthMetrics, PERSISTENT (disk, survives
  reboots), default false — re-polled every ~2s so toggle takes effect
  without process restart
- InterFont(90, Bold) to match speed limit numeric styling, 30px margin,
  40px between rows, black rounded background

Nightrider center lane path (the "tire track" polygon from
scene.track_vertices) is now drawn at 2x the width of other lines —
highlights the planned path distinctly against the otherwise stark
outline-only rendering.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 22:03:30 -05:00
brianhansonxyz 22ced0c558 cleanup: remove dead CarCruiseDisplayActual param
Audit of post-fork param additions found CarCruiseDisplayActual was
written every CAN cycle (gated) but only consumed by hyundaicanfd.py::
create_buttons_alt, which has `return` on line 1 and no active callers.
Write was pure waste. Removed registration, write path, cache field,
and the dead read.

Also dropped the now-unused `from openpilot.common.params import Params`
in hyundaicanfd.py.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 22:03:01 -05:00
brianhansonxyz ef4e02e354 feat: 10fps daytime / 4fps night modeld + 2Hz GPS
Reduced-rate modeld path now branches on IsDaylight:
- daylight: skip 1/2 frames → 10fps (better model responsiveness when
  lighting gives the net more signal)
- night: skip 4/5 frames → 4fps (unchanged, conservative for power)

IsDaylight is already in /dev/shm (memory) via gpsd.py. Gated the
IsDaylight write on change — it flips twice a day, no reason to rewrite
every 30s. GPS polling bumped from 1Hz → 2Hz.

ModelFps publishes "10" / "4" / "20" so longitudinal_planner's dt and
FCW-threshold scaling (if re-enabled) still track actual rate.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 21:41:29 -05:00
brianhansonxyz 02f25f83c4 perf: hoist Params read out of create_steering_messages 100Hz path
prebuilt / build prebuilt (push) Has been cancelled
badges / create badges (push) Has been cancelled
create_steering_messages was constructing a new Params("/dev/shm/params")
object and reading no_lat_lane_change on every CAN steering message build
— i.e. 100 allocations + 100 file reads per second. Now the Params
instance lives on CarController, and the value is read once per update()
cycle and passed as a parameter.

Audited all other hyundai CAN-FD integration code for similar patterns:
- carstate.py — already fixed (previous commit)
- carcontroller.py — other Params references are all in commented-out code
- hyundaicanfd.py::create_buttons_alt — dead code (early return), so the
  Params read there never executes; left as-is

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 21:34:52 -05:00
brianhansonxyz ceb3481cdc feat: add low-speed tier to cruise warning, gate param writes on change
Speed limit ≤25 mph now allows +8 over before warning (e.g. limit 25 →
33 is ok, warn at 34). Existing tiers unchanged: ≥50 → +9 ok warn at +10,
26-49 → +6 ok warn at +7.

Also gate all 7 speed_logic param writes on change (same pattern as the
earlier carstate/controlsd perf fix). Called at 2Hz so not as hot, but
unit/is_metric never change mid-drive and the cruise warning rarely
flips — no reason to thrash /dev/shm on every update.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 21:28:05 -05:00
brianhansonxyz bae022db43 fix: thermald crash blocking shutdown (getBool → get_bool)
Python Params uses snake_case; the C++ camelCase call raised
AttributeError, killing thermald_thread at the exact moment of shutdown.
Result: DoShutdown never got set, the 10-minute timer "worked" once (set
DashcamShutdown=True) and then thermald died silently. Device kept
draining the battery instead of powering down.

Caught because CLAUDE.md specifically flags this pattern as a common
source of silent failures between C++ and Python.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 21:20:53 -05:00
brianhansonxyz 1e36d7ec23 feat: disable FCW — stock AEB handles it better
Tucson's radar-based collision warning is more reliable than the comma
model/planner FCW and was producing false positives. Single-user fork in
a single car, so no need to keep both.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 21:11:39 -05:00
brianhansonxyz 6f3b1b1d2f docs: add py-spy profiling recipe to CLAUDE.md
prebuilt / build prebuilt (push) Has been cancelled
badges / create badges (push) Has been cancelled
stale / stale (push) Has been cancelled
Captures how we found the hot Params writes in carstate.py so the same
technique can be repeated. Includes the awk aggregators for per-callsite
and per-file-line breakdowns.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 17:35:03 -05:00
brianhansonxyz 7a1e157c9c perf: gate hot /dev/shm writes on change — controlsd 69%→28% CPU
py-spy showed per-cycle atomic param writes were the dominant cost. Each
put() is mkstemp+fsync+flock+rename+fsync_dir — fine when rare, ruinous at
100Hz. At park with no state changes, these writes were running anyway and
the flock contention was poisoning the whole system.

carstate.py (update + update_canfd): CarSpeedLimit, CarIsMetric,
  CarCruiseDisplayActual were written every CAN update. Now cached and
  written only on change.
controlsd.py: same fix for LatRequested and no_lat_lane_change. Also
  throttle the sentry crash-file stat() from 100Hz to 1Hz.

Also: suppress locationdTemporaryError/paramsdTemporaryError/posenetInvalid
on lat engage (same 2s window as commIssue), and tie suppression to the
LatRequested edge instead of CC.latActive (fires immediately, not after
the 250ms ramp-up delay).

Also: reset Ratekeeper when it falls >1s behind — the ~6s fingerprinting
stall at startup was poisoning the lag metric for the entire session.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 17:34:07 -05:00
brianhansonxyz 2d819c784b feat: ramp-up delay on lat engagement to prevent commIssue flash
prebuilt / build prebuilt (push) Has been cancelled
badges / create badges (push) Has been cancelled
Decouples "tell modeld to go fast" from "steering actually active":
- New LatRequested memory param — controlsd writes when lat would be active
- modeld reads LatRequested (not carControl.latActive) for FPS decision,
  so it switches to 20fps immediately on engage request
- controlsd delays CC.latActive becoming true by 250ms (5 frames @ 20fps)
  after LatRequested goes true, giving downstream services
  (longitudinalPlan, liveCalibration, etc.) time to stabilize at the new rate

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 16:46:37 -05:00