Compare commits

..

33 Commits

Author SHA1 Message Date
firestarsdog 0bdae4b250 ELMo & Friends 2026-09-04 13:46:04 -04:00
firestar5683 901b93ac57 yeetit 2026-09-04 10:46:11 -05:00
firestar5683 6850a8cdba blows chunks 2026-09-04 00:13:38 -05:00
firestar5683 dd7ac353bd Team Noah 2026-09-03 23:31:33 -05:00
firestar5683 fed4ce6ee0 cleanup
Original PRs: #115, #116, #117, and #118 by @1454
2026-09-03 15:22:26 -05:00
firestar5683 ab6351541f build 2026-09-03 15:15:10 -05:00
1454 26ce46ba1d controls: smooth CEM to ACC handoff
Blend MPC back in over the final 5 mph of the CEM limit while preserving strong E2E braking. Slew positive acceleration and freeze the integrator during the experimental-mode exit so the handoff stays smooth. Keep the existing lead and confidence gates on the release path.

Original PR: #115 by @1454
2026-09-03 15:12:30 -05:00
1454 c4ce84d037 gm: clarify Silverado standstill engagement comment
Original PR: #118 by @1454
2026-09-03 15:07:48 -05:00
1454 fbb0fccb0e Allow Silverado CC-long buttons at a complete stop.
minEnableSpeed is 0, so a strict greater-than check never armed actuation at 0 m/s.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-03 15:07:33 -05:00
1454 f43cbab52e Keep Silverado CC min-enable at 0.
CC-long used to overwrite that floor back to 24 mph, which blocked the same from-stop engage path as the camera truck.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-03 15:07:33 -05:00
1454 4a50ca040f Allow Silverado cruise engage at a stop.
Drop the 5 kph minEnableSpeed so SET is not blocked while creeping below 3 mph.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-03 15:07:32 -05:00
1454 815267797c Stop LongPitch from stacking hill throttle on a positive planner command.
Honor the toggle on pedal-long cars and cap leftover uphill grade feedforward so a 10-speed does not skip-shift 10-8.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-03 15:07:28 -05:00
1454 02e0f1ec9a soundd: narrow custom clip fallback errors
Original PR: #116 by @1454
2026-09-03 15:07:26 -05:00
1454 509e6876f1 Never let one broken clip abort soundd startup.
Any decode failure on a candidate now falls through to the next path or skip.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-03 15:07:15 -05:00
1454 b321dd71e6 Skip truncated WAV payloads that cannot decode as samples.
Reject odd-length PCM and catch ValueError so a broken theme file cannot take soundd down.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-03 15:07:14 -05:00
1454 cc8c0dfb16 Keep stock critical alerts if goat or a theme WAV fails.
Catch truncated files that raise EOFError, and only replace a critical chime with goat when that clip actually loaded.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-03 15:07:14 -05:00
1454 ffb2b2c1e3 Fall back to stock sounds when a custom clip is invalid.
Skip empty WAVs so playback never divides by zero, and keep packaged critical alerts if a theme file is malformed.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-03 15:07:14 -05:00
1454 91d4b27aa3 Keep soundd alive when custom theme clips are missing.
Skip missing or invalid wavs instead of crashing at startup so stock chimes still play on a fresh image.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-03 15:07:14 -05:00
firestar5683 693df09c88 build 2026-09-03 15:04:45 -05:00
firestar5683 d2fb362876 what's that mean, dumby 2026-09-03 15:01:53 -05:00
firestar5683 f18cf22104 that's not a power button 2026-09-03 12:27:31 -05:00
firestarsdog 352b23ddf5 Not quite a pollo bowl 2026-09-03 01:09:51 -04:00
Prabhaav Pillai c7a3e3297a cookie to authenticate mobile 2026-09-03 00:16:13 -04:00
Prabhaav Pillai b390513ad1 Add command to launch local Galaxy web UI in host_tool_runner.sh 2026-09-02 23:59:25 -04:00
Prabhaav Pillai 6f5e267493 Mobile Friendly Galaxy 2026-09-02 23:40:08 -04:00
firestar5683 bb3b1429eb I thought you said weast 2026-09-02 22:07:29 -05:00
firestar5683 3b4a570564 build 2026-09-02 20:06:05 -05:00
firestar5683 a3bdcf2417 nope 2026-09-02 20:05:29 -05:00
firestar5683 f553b8071d subuwu 2026-09-02 17:37:45 -05:00
firestar5683 f7fad2a4d5 build 2026-09-02 17:31:48 -05:00
firestar5683 5fe8b17467 weh 2026-09-02 17:31:24 -05:00
firestar5683 51d8062c36 hi 2026-09-02 13:56:15 -05:00
firestar5683 5122b8df42 h 2026-09-02 11:54:07 -05:00
506 changed files with 42500 additions and 10051 deletions
Binary file not shown.
+26
View File
@@ -259,6 +259,32 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"CustomAccelProfile45MPH", {PERSISTENT, FLOAT, "1.0", "1.0", 3}},
{"CustomAccelProfile56MPH", {PERSISTENT, FLOAT, "0.8", "0.8", 3}},
{"CustomAccelProfile89MPH", {PERSISTENT, FLOAT, "0.6", "0.6", 3}},
{"CustomAccelProfileBreakpointsInitialized", {PERSISTENT, BOOL, "0", "0", 3}},
{"CustomAccelProfilePointCount", {PERSISTENT, INT, "7", "7", 3}},
{"CustomAccelProfileBreakpoint1MPH", {PERSISTENT, FLOAT, "0.0", "0.0", 3}},
{"CustomAccelProfileBreakpoint2MPH", {PERSISTENT, FLOAT, "11.184681", "11.184681", 3}},
{"CustomAccelProfileBreakpoint3MPH", {PERSISTENT, FLOAT, "22.369363", "22.369363", 3}},
{"CustomAccelProfileBreakpoint4MPH", {PERSISTENT, FLOAT, "33.554044", "33.554044", 3}},
{"CustomAccelProfileBreakpoint5MPH", {PERSISTENT, FLOAT, "44.738726", "44.738726", 3}},
{"CustomAccelProfileBreakpoint6MPH", {PERSISTENT, FLOAT, "55.923407", "55.923407", 3}},
{"CustomAccelProfileBreakpoint7MPH", {PERSISTENT, FLOAT, "89.477452", "89.477452", 3}},
{"CustomAccelProfileBreakpoint8MPH", {PERSISTENT, FLOAT, "100.662133", "100.662133", 3}},
{"CustomAccelProfileBreakpoint9MPH", {PERSISTENT, FLOAT, "111.846815", "111.846815", 3}},
{"CustomAccelProfileBreakpoint10MPH", {PERSISTENT, FLOAT, "123.031496", "123.031496", 3}},
{"CustomAccelProfileBreakpoint11MPH", {PERSISTENT, FLOAT, "134.216178", "134.216178", 3}},
{"CustomAccelProfileBreakpoint12MPH", {PERSISTENT, FLOAT, "145.400859", "145.400859", 3}},
{"CustomAccelProfilePoint1Accel", {PERSISTENT, FLOAT, "3.0", "3.0", 3}},
{"CustomAccelProfilePoint2Accel", {PERSISTENT, FLOAT, "2.5", "2.5", 3}},
{"CustomAccelProfilePoint3Accel", {PERSISTENT, FLOAT, "2.0", "2.0", 3}},
{"CustomAccelProfilePoint4Accel", {PERSISTENT, FLOAT, "1.5", "1.5", 3}},
{"CustomAccelProfilePoint5Accel", {PERSISTENT, FLOAT, "1.0", "1.0", 3}},
{"CustomAccelProfilePoint6Accel", {PERSISTENT, FLOAT, "0.8", "0.8", 3}},
{"CustomAccelProfilePoint7Accel", {PERSISTENT, FLOAT, "0.6", "0.6", 3}},
{"CustomAccelProfilePoint8Accel", {PERSISTENT, FLOAT, "0.55", "0.55", 3}},
{"CustomAccelProfilePoint9Accel", {PERSISTENT, FLOAT, "0.5", "0.5", 3}},
{"CustomAccelProfilePoint10Accel", {PERSISTENT, FLOAT, "0.45", "0.45", 3}},
{"CustomAccelProfilePoint11Accel", {PERSISTENT, FLOAT, "0.4", "0.4", 3}},
{"CustomAccelProfilePoint12Accel", {PERSISTENT, FLOAT, "0.35", "0.35", 3}},
{"CustomCruise", {PERSISTENT, FLOAT, "1.0", "1.0", 2, SETTINGS_SIMPLE}},
{"CustomCruiseLong", {PERSISTENT, FLOAT, "5.0", "5.0", 2, SETTINGS_SIMPLE}},
{"CustomPersonalities", {PERSISTENT, BOOL, "0", "0", 2}},
Binary file not shown.
+125 -161
View File
@@ -1,146 +1,128 @@
# StarPilot Unified Model Rebuild
# StarPilot Model Rebuild
This workflow rebuilds StarPilot driving and driver-monitoring artifacts for the vendored tinygrad revision. Driving-model behavior versions remain manifest metadata; every runtime driving artifact uses the `tinygrad_single_v1` layout.
This is the supported workflow for changing the vendored tinygrad revision and
releasing a new model manifest generation. A manifest generation represents one
tinygrad ABI. Model behavior versions (`v8` through `v16`) are independent and
must remain unchanged when only tinygrad changes.
## Safety
The current generation is **v25**, pinned to tinygrad
`e837e367aac9e1a66e689f4f32ce20ca9367df13`. The supported compiler is
`comma@192.168.3.110`; never substitute another comma without explicit approval.
- The supported build device is `comma@192.168.3.109`.
- Never run these commands against `192.168.3.110`.
- Normal artifacts target QCOM. External-GPU artifacts must be compiled explicitly and tagged in the manifest.
- Keep source ONNX files and compiled PKLs on the T5 workspace, not the comma.
## Release Contract
## Workspace
- Keep every existing StarPilot model ID stable across manifest generations.
- Store HF artifacts under `models/v25/<model-id>/`.
- Store GitHub fallback artifacts on the `Models` branch under `v25/<model-id>/`.
- Name every logical artifact `<model-id>_driving_tinygrad.pkl`.
- Publish native chunks as `.chunkNNofNN` plus `.chunkmanifest`.
- Serialize every v25 driving artifact out-of-band; this applies to normal QCOM
models as well as external-GPU models.
- Include `artifact_sha256` and `artifact_chunk_count` in the manifest.
- Set `uses_external_gpu: true` only for models compiled for Chestnut.
- Do not rename an artifact from another tinygrad revision. PKLs must either use
the exact v25 pin or be rebuilt with it.
- Do not add models absent from the existing StarPilot catalog unless the
release explicitly requests them.
The default workspace is:
The downloader checks Hugging Face first and GitHub second. There is no GitLab
fallback. The HF manifest lives only at `manifests/model_names_v25.json`, old
artifacts live under `models/v24/`, and current artifacts live under
`models/v25/`. The v25 downloader never probes unversioned or v24 artifact
paths; missing v25 artifacts fail safely instead of loading an incompatible
pickle.
## Tinygrad Bump
1. Record the exact tinygrad commit used by the compatible source catalog.
2. Replace `tinygrad_repo/` from that commit, excluding nested Git metadata.
3. Write the full SHA to `tinygrad_repo/TINYGRAD_COMMIT`.
4. Review upstream `modeld`, compiler, parser, and camera-warp changes. Merge
required ABI changes into StarPilot's existing multi-model runtime; never
replace StarPilot `modeld.py` wholesale.
5. Increment `MANIFEST_CANDIDATES` to a new single version. Do not fall back to
the prior manifest because its PKLs target a different tinygrad ABI.
6. Sync the exact tree to the compiler before building anything:
```bash
./dev sync
rsync -az --delete --exclude=.git --exclude=__pycache__ -e ssh \
tinygrad_repo/ comma@192.168.3.110:/data/openpilot/tinygrad_repo/
rsync -az -e ssh selfdrive/modeld/ \
comma@192.168.3.110:/data/openpilot/selfdrive/modeld/
rsync -az -e ssh scripts/model_compiler.py \
comma@192.168.3.110:/data/openpilot/scripts/model_compiler.py
rsync -az -e ssh models comma@192.168.3.110:/data/openpilot/models
```
Confirm the device marker before compiling:
```bash
ssh comma@192.168.3.110 \
'cat /data/openpilot/tinygrad_repo/TINYGRAD_COMMIT'
```
## Reuse Compatible Artifacts
Reusing an artifact is preferred when its catalog records the exact same
tinygrad SHA and exact same source-model commit. Display names and release dates
are not sufficient proof. Copy compatible chunks server-side so the Mac never
stores a second multi-gigabyte artifact, but rename every destination chunk to
the stable StarPilot model ID.
Example:
```bash
hf buckets cp \
'hf://datasets/<source>/<path>/<source-file>.chunk01of02' \
'hf://buckets/StarPilot-Driving/StarPilot-Resources/models/v25/pop223/pop223_driving_tinygrad.pkl.chunk01of02'
```
Write `2` to `pop223_driving_tinygrad.pkl.chunkmanifest`, upload it last, and
put the source artifact's full SHA-256 and chunk count into the v25 manifest.
Upload the manifest only after every listed artifact directory is complete.
## Compile Missing Models
Archived sources live under:
```text
/Volumes/T5/StarPilot-Model-Rebuild-2026-06-22/
hf://buckets/StarPilot-Driving/StarPilot-Resources/onnx/<source-id>/
```
Important directories:
- `onnx/<model-id>/`: ID-prefixed source ONNX files.
- `compiled/`: completed unified driving PKLs.
- `driver-monitoring/`: DM ONNX, model PKL, metadata, and camera warps.
- `ready-for-resources/`: flat repository-upload handoff.
- Oversized models are represented by repository-safe `.p00`, `.p01`, and `.sha256` files in `ready-for-resources/`.
- `logs/`: one remote compilation log per model.
- `results/`: source and artifact checksum records.
- `manifests/`: source `model_names_v22.json` and namespaced release `model_names_v23.json`.
- The v23 manifest and compiled artifacts are published together in the resource repository's `Models` branch.
## Initialize And Extract
Stage one model at a time in `/data/openpilot/uncompiledmodels`; this avoids
filling the comma and prevents `./models` from selecting stale input files.
```bash
python3 scripts/model_rebuild_pipeline.py init
python3 scripts/model_rebuild_pipeline.py extract \
--base-manifest /path/to/model_names_v21.json
./models --<model-id> --version <behavior-version>
./models --<gpu-model-id> --version v16 --gpu
```
Extraction streams Git blobs directly to disk. LFS pointers are resolved from the local object cache or fetched by object ID, then checked against the pointer SHA-256 and size. Binary ONNX data is never stored in a shell variable.
To retry one source:
The default input is a single supercombo ONNX. For legacy sources use:
```bash
python3 scripts/model_rebuild_pipeline.py extract \
--model pop22 \
--base-manifest /path/to/model_names_v21.json
./models --<model-id> --input-format split --version <behavior-version>
```
The original catalog sources are defined in `scripts/model_source_map_v22.json`.
Recovered late-model and supercombo sources, including RDF2, are defined in
`scripts/model_source_map_v23.json`. The v23 map is intentionally separate so
adding a recovered iteration cannot alter the older model source history.
Every non-local release build emits an OOB artifact as native chunks and removes
the temporary full PKL. `./models --local-<id>` intentionally keeps one OOB PKL
for local use.
## Compile
Compile one model:
The resumable bulk helper is:
```bash
STAR_PILOT_MODEL_REMOTE=comma@192.168.3.110 \
python3 scripts/model_rebuild_pipeline.py compile \
--model pop22 \
--base-manifest /path/to/model_names_v21.json
--workspace /Volumes/T5/StarPilot-Model-Rebuild \
--source-map scripts/model_source_map_v25.json \
--base-manifest ~/StarPilot-Resources/model_names_v25.json
```
Compile or resume the full catalog:
Failures are recorded under `results/`; rerun the same command to resume.
```bash
python3 scripts/model_rebuild_pipeline.py compile \
--base-manifest /path/to/model_names_v21.json
```
## Driver Monitoring And Default
Existing artifacts are skipped unless `--force` is passed. Each model is staged in its own remote input directory, compiled on `.109`, copied back to the T5, hashed, and copied into `ready-for-resources/`. Failures are written to `results/<id>_failure.json`; rerunning the same command resumes incomplete models.
Validate one or all completed artifacts with synthetic camera inputs on QCOM:
```bash
python3 scripts/model_rebuild_pipeline.py validate \
--model pop22 \
--base-manifest /path/to/model_names_v21.json
```
The lower-level device compiler also supports direct use:
```bash
./models --model pop22 --input-format split --version v11
./models --model deeprl3v2 --input-format supercombo --version v15
```
For a model that cannot run on the device GPU, compile with the USB AMD GPU attached:
```bash
./models --lebowski --gpu
```
The ASM2464PD bridge must run the current tinygrad custom firmware from
https://github.com/tinygrad/asm2464pd-firmware. Its USB product string starts
with `custom`; the legacy `USB 3.2 PCIe TinyEnclosure` patch is not compatible
with comma's current external-GPU runtime. Firmware flashing is a separate,
explicit hardware setup step and StarPilot never performs it automatically.
The dynamic flag (`--lebowski` above) sets the output and manifest model ID;
when only one source model is staged, its ONNX filename does not need to match
that ID. Input format and behavior version are inferred. `--external-gpu`
remains available as a compatibility alias for `--gpu`.
This emits a streaming out-of-band pickle and keeps QCOM available for camera warps. Its manifest entry must include:
```json
{
"id": "lebowski",
"uses_external_gpu": true
}
```
Only tagged models activate the external GPU. If the GPU or artifact is unavailable, runtime falls back to the built-in model; all untagged models retain the existing QCOM path.
`--version` records behavioral semantics only. It does not change artifact layout.
If the compiled PKL exceeds 100 MiB, `./models` automatically keeps the full
local PKL and creates 95 MiB upload parts beside it:
```text
deeprl3v2_driving_tinygrad.pkl
deeprl3v2_driving_tinygrad.pkl.p00
deeprl3v2_driving_tinygrad.pkl.p01
deeprl3v2_driving_tinygrad.pkl.sha256
```
To split an already compiled artifact:
```bash
./models --split-artifact /path/to/deeprl3v2_driving_tinygrad.pkl \
--output-dir /path/to/upload-ready
```
Upload only the numbered parts and checksum when the full PKL exceeds the
repository limit. The downloader reassembles into a temporary file, verifies
the companion SHA-256, and atomically installs the final PKL. No manifest field
is required for multipart artifacts.
## Driver Monitoring
Stage the current DM ONNX in `uncompiledmodels`, then run:
Driver monitoring is built once per tinygrad generation:
```bash
./models --dm \
@@ -148,61 +130,43 @@ Stage the current DM ONNX in `uncompiledmodels`, then run:
--output-dir /tmp/dm_artifacts
```
This builds:
Replace these four files together:
- `dmonitoring_model_tinygrad.pkl`
- `dmonitoring_model_metadata.pkl`
- `dm_warp_1928x1208_tinygrad.pkl`
- `dm_warp_1344x760_tinygrad.pkl`
All four files must be updated together.
Recompile RDF V4 with the same pin and replace the built-in
`selfdrive/modeld/models/driving_tinygrad.pkl` native chunk set. Never commit a
full built-in PKL over the repository limit.
## Manifest
## Validation
Generate the base manifest after compilation, then namespace the release artifacts as v23:
Run repository tests first:
```bash
python3 scripts/model_rebuild_pipeline.py manifest \
--base-manifest /path/to/model_names_v21.json
./dev sync
./.venv/bin/pytest -q -n0 \
starpilot/assets/tests/test_model_pipeline.py \
common/tests/test_file_chunker.py \
scripts/tests/test_model_release.py
```
```bash
python3 scripts/namespace_model_artifacts.py \
--workspace /Volumes/T5/StarPilot-Model-Rebuild-2026-06-22 \
--base-manifest /Volumes/T5/StarPilot-Model-Rebuild-2026-06-22/manifests/model_names_v22.json \
--manifest-version v23 --suffix 3
```
For representative v8, v11, v12, v15, v16, and GPU artifacts, validate both
camera resolutions on real QCOM and require finite plan, lane-line, road-edge,
lead, pose, and action outputs. Then start `modeld` and confirm stable
`modelV2` publication. Validate DM `driverStateV2` at both resolutions.
The namespace command changes IDs such as `tr1422` to `tr14223`, renames the
compiled and upload-ready files, and writes an ID map. It preserves display
names and behavioral versions. The current model manager requests v23 only;
the manifest is fetched from `Models/model_names_v23.json`, while v22 remains
available for devices that have not updated yet.
## Device Migration
After importing newly compiled sources, normalize the release namespace before
copying files into either resource repository:
When `ModelManifestVersion` changes, the model manager retains the selected
model ID but deletes every non-local downloaded driving artifact from the old
generation, including full PKLs, `.pNN` parts, native chunks, and chunk
manifests. It then downloads that ID's v25 chunks. Local models and DM files are
not deleted. If the selected v25 artifact cannot be downloaded and verified,
the manager selects the built-in RDF V4 model.
```bash
python3 scripts/reconcile_v23_artifacts.py \
--workspace /Volumes/T5/StarPilot-Model-Rebuild-2026-06-22
```
This maps recovered source IDs to their v23 release IDs, removes duplicate
macOS metadata files, and adds `rdf23` for Regret Driven Framework V2. It does
not overwrite a conflicting artifact.
Repository-hosted multipart files are discovered by naming convention, so no
size, hash, format, or part-count metadata is required.
`uses_external_gpu` is optional and defaults to `false`.
## Runtime Verification
Compilation validates JIT capture/replay, pickle round-trip, finite outputs, metadata slices, and both camera warps. Before release:
1. Select representative v8, v11, v12, v15, and supercombo models.
2. Confirm `modeld` stays running.
3. Confirm finite `modelV2` path, lane-line, lead, pose, and action data.
4. Confirm `driverStateV2` on both supported camera resolutions.
5. Test download, selection, deletion, randomization, migration, and fallback in both device UIs and Galaxy.
The built-in RDF artifact is `selfdrive/modeld/models/driving_tinygrad.pkl`. If migration cannot download the selected v23 artifact, StarPilot switches to that built-in model.
Test this explicitly before release by starting with a v24 selected model and
checking that no v24 driving artifact remains under `/data/models` after the
v25 manifest is applied.
+1 -1
View File
@@ -21,7 +21,7 @@ fi
export QCOM_PRIORITY=12
if [ -z "$AGNOS_VERSION" ]; then
export AGNOS_VERSION="19.6.16"
export AGNOS_VERSION="19.6.19"
fi
if [ -z "$AGNOS_ACCEPTED_VERSIONS" ]; then
@@ -18,6 +18,26 @@ AVERAGE_ROAD_ROLL = 0.06 # ~3.4 degrees, 6% superelevation. higher actual roll
MAX_LATERAL_ACCEL = ISO_LATERAL_ACCEL - (ACCELERATION_DUE_TO_GRAVITY * AVERAGE_ROAD_ROLL) # ~2.4 m/s^2
class FordStockCruiseButton:
"""Resolve Ford's context-sensitive cancel/resume switch for stock ACC."""
def __init__(self):
self.pressed = False
self.cancel = False
self.resume = False
def update(self, pressed: bool, cruise_available: bool, cruise_enabled: bool) -> tuple[bool, bool]:
if pressed and not self.pressed:
self.cancel = cruise_available and cruise_enabled
self.resume = cruise_available and not cruise_enabled
elif not pressed:
self.cancel = False
self.resume = False
self.pressed = pressed
return self.cancel, self.resume
def apply_ford_angle(desired_angle_deg: float, current_angle_deg: float) -> float:
relative_angle = desired_angle_deg - current_angle_deg
return float(np.clip(relative_angle, -5.8, 5.8))
@@ -85,6 +105,7 @@ class CarController(CarControllerBase):
self.ford_lateral = None if CP.flags & FordFlags.LKA_STEERING else FordLateralController(CP)
self.ford_shadow_curvature = 0.0
self.ford_lateral_announced_mode = FordLateralMode.native
self.stock_cruise_button = FordStockCruiseButton()
def update(self, CC, CS, now_nanos, starpilot_toggles):
can_sends = []
@@ -100,9 +121,23 @@ class CarController(CarControllerBase):
self.ford_lateral.update_inputs()
### acc buttons ###
stock_cancel = False
stock_resume = False
if not self.CP.openpilotLongitudinalControl:
stock_cancel, stock_resume = self.stock_cruise_button.update(
bool(CS.buttons_stock_values["CcAslButtnCnclResPress"]),
CS.out.cruiseState.available,
CS.out.cruiseState.enabled,
)
if CC.cruiseControl.cancel:
can_sends.append(fordcan.create_button_msg(self.packer, self.CAN.camera, CS.buttons_stock_values, cancel=True))
can_sends.append(fordcan.create_button_msg(self.packer, self.CAN.main, CS.buttons_stock_values, cancel=True))
elif (stock_cancel or stock_resume) and (self.frame % CarControllerParams.BUTTONS_STEP) == 0:
can_sends.append(fordcan.create_button_msg(
self.packer, self.CAN.camera, CS.buttons_stock_values, cancel=stock_cancel, resume=stock_resume))
can_sends.append(fordcan.create_button_msg(
self.packer, self.CAN.main, CS.buttons_stock_values, cancel=stock_cancel, resume=stock_resume))
elif CC.cruiseControl.resume and (self.frame % CarControllerParams.BUTTONS_STEP) == 0:
can_sends.append(fordcan.create_button_msg(self.packer, self.CAN.camera, CS.buttons_stock_values, resume=True))
can_sends.append(fordcan.create_button_msg(self.packer, self.CAN.main, CS.buttons_stock_values, resume=True))
@@ -9,6 +9,7 @@ import pytest
from opendbc.car import Bus, gen_empty_fingerprint
from opendbc.can import CANPacker
from opendbc.car.ford import fordcan
from opendbc.car.ford.carcontroller import FordStockCruiseButton
from opendbc.car.gps import FORD_MACH_E_GPS_MESSAGES, get_car_gps_config, parse_ford_can_gps
from opendbc.car.structs import CarParams
from opendbc.car.fw_versions import build_fw_dict
@@ -19,6 +20,24 @@ from opendbc.car.ford.fingerprints import FW_VERSIONS
Ecu = CarParams.Ecu
def test_stock_cruise_button_latches_context_until_release():
button = FordStockCruiseButton()
assert button.update(True, cruise_available=True, cruise_enabled=True) == (True, False)
assert button.update(True, cruise_available=True, cruise_enabled=False) == (True, False)
assert button.update(False, cruise_available=True, cruise_enabled=False) == (False, False)
assert button.update(True, cruise_available=True, cruise_enabled=False) == (False, True)
assert button.update(True, cruise_available=True, cruise_enabled=True) == (False, True)
assert button.update(False, cruise_available=True, cruise_enabled=True) == (False, False)
def test_stock_cruise_button_ignores_press_with_cruise_master_off():
button = FordStockCruiseButton()
assert button.update(True, cruise_available=False, cruise_enabled=False) == (False, False)
ECU_ADDRESSES = {
Ecu.eps: 0x730, # Power Steering Control Module (PSCM)
Ecu.abs: 0x760, # Anti-Lock Brake System (ABS)
+16 -5
View File
@@ -172,7 +172,7 @@ def should_send_cc_button_spam(CP, CC, CS):
return (
bool(CP.flags & GMFlags.CC_LONG.value) and
CC.longActive and
CS.out.vEgo > CP.minEnableSpeed
CS.out.vEgo >= CP.minEnableSpeed
)
@@ -256,6 +256,17 @@ def shape_truck_pitch_accel(pitch_accel: float, v_ego: float, enabled: bool) ->
return pitch_accel * scale
MAX_UPHILL_GRADE_FF = 0.20
def limit_grade_feedforward(planner_accel: float, pitch_accel: float) -> float:
if pitch_accel > 0.0 and planner_accel > 0.0:
return 0.0
if pitch_accel > MAX_UPHILL_GRADE_FF:
return MAX_UPHILL_GRADE_FF
return pitch_accel
def shape_truck_friction_brake(apply_brake: int, accel_cmd: float, stopping: bool, active: bool) -> tuple[int, bool]:
if apply_brake <= 0:
return 0, False
@@ -1003,14 +1014,13 @@ class CarController(CarControllerBase):
self.truck_follow_accel = 0.0
else:
long_pitch_enabled = bool(getattr(starpilot_toggles, "long_pitch", True))
pedal_long_path = bool(self.CP.enableGasInterceptorDEPRECATED and (self.CP.flags & GMFlags.PEDAL_LONG.value))
long_pitch_for_powertrain = long_pitch_enabled or pedal_long_path
if self.is_volt:
if long_pitch_for_powertrain and len(CC.orientationNED) == 3 and CS.out.vEgo > self.CP.vEgoStopping:
if long_pitch_enabled and len(CC.orientationNED) == 3 and CS.out.vEgo > self.CP.vEgoStopping:
volt_pitch_accel = math.sin(CC.orientationNED[1]) * ACCELERATION_DUE_TO_GRAVITY
else:
volt_pitch_accel = 0.0
volt_pitch_accel = limit_grade_feedforward(accel, volt_pitch_accel)
aero_drag_accel = (0.5 * self.coeffDrag * self.frontalArea * self.airDensity * CS.out.vEgo ** 2) / self.mass
accel_cmd = float(np.clip(accel + aero_drag_accel + volt_pitch_accel, self.params.ACCEL_MIN, self.params.ACCEL_MAX))
@@ -1031,7 +1041,7 @@ class CarController(CarControllerBase):
if self.apply_brake > 0:
self.apply_gas = self.params.INACTIVE_REGEN
else:
if long_pitch_for_powertrain and len(CC.orientationNED) == 3 and CS.out.vEgo > self.CP.vEgoStopping:
if long_pitch_enabled and len(CC.orientationNED) == 3 and CS.out.vEgo > self.CP.vEgoStopping:
accel_due_to_pitch = math.sin(CC.orientationNED[1]) * ACCELERATION_DUE_TO_GRAVITY
else:
accel_due_to_pitch = 0.0
@@ -1048,6 +1058,7 @@ class CarController(CarControllerBase):
not self.CP.enableGasInterceptorDEPRECATED
)
accel_due_to_pitch = shape_truck_pitch_accel(accel_due_to_pitch, CS.out.vEgo, truck_long_smoothing)
accel_due_to_pitch = limit_grade_feedforward(actuators.accel, accel_due_to_pitch)
accel_input = actuators.accel + accel_due_to_pitch
if truck_long_smoothing:
accel_input = shape_truck_positive_accel(
+3 -4
View File
@@ -500,9 +500,7 @@ class CarInterface(CarInterfaceBase):
ret.flags |= GMFlags.PEDAL_LONG.value
elif candidate in (CAR.CHEVROLET_SILVERADO, CAR.CHEVROLET_SILVERADO_CC):
# On the Bolt, the ECM and camera independently check that you are either above 5 kph or at a stop
# with foot on brake to allow engagement, but this platform only has that check in the camera.
# TODO: check if this is split by EV/ICE with more platforms in the future
ret.minEnableSpeed = 0.
CarInterfaceBase.configure_torque_tune(candidate, ret.lateralTuning)
elif candidate in (CAR.CHEVROLET_EQUINOX, CAR.CHEVROLET_EQUINOX_CC):
@@ -666,7 +664,8 @@ class CarInterface(CarInterfaceBase):
ret.alphaLongitudinalAvailable = False
ret.openpilotLongitudinalControl = not disable_openpilot_long
ret.pcmCruise = False
ret.minEnableSpeed = 24 * CV.MPH_TO_MS
if candidate not in (CAR.CHEVROLET_SILVERADO, CAR.CHEVROLET_SILVERADO_CC):
ret.minEnableSpeed = 24 * CV.MPH_TO_MS
ret.radarUnavailable = True
ret.safetyConfigs[0].safetyParam |= GMSafetyFlags.FLAG_GM_CC_LONG.value
@@ -53,6 +53,7 @@ from opendbc.car.gm.carcontroller import (
get_testing_ground_1_brake_switch_bias,
get_acc_dashboard_status_active,
get_stock_cc_active_for_cancel,
limit_grade_feedforward,
shape_bolt_acc_pedal_low_speed_friction,
shape_truck_friction_brake,
shape_truck_pitch_accel,
@@ -895,6 +896,20 @@ def test_shape_truck_pitch_accel_is_inactive_without_truck_tuning():
assert shape_truck_pitch_accel(-0.30, 30.0, False) == pytest.approx(-0.30)
def test_limit_grade_feedforward_does_not_stack_on_positive_planner():
assert limit_grade_feedforward(0.40, 0.50) == 0.0
def test_limit_grade_feedforward_caps_uphill_hold():
assert limit_grade_feedforward(0.0, 0.50) == pytest.approx(0.20)
assert limit_grade_feedforward(-0.10, 0.50) == pytest.approx(0.20)
def test_limit_grade_feedforward_keeps_downhill_help():
assert limit_grade_feedforward(0.40, -0.30) == pytest.approx(-0.30)
assert limit_grade_feedforward(-0.20, -0.30) == pytest.approx(-0.30)
def test_shape_truck_friction_brake_suppresses_boundary_chatter():
assert shape_truck_friction_brake(14, -0.3, False, False) == (0, False)
@@ -303,11 +303,36 @@ class TestGMInterface:
assert car_params.openpilotLongitudinalControl
assert not car_params.enableGasInterceptorDEPRECATED
assert car_params.minEnableSpeed == pytest.approx(0.0)
assert list(car_params.longitudinalTuning.kpBP) == pytest.approx([0.0, 5.0, 15.0, 35.0])
assert list(car_params.longitudinalTuning.kpV) == pytest.approx([0.02, 0.03, 0.028, 0.022])
assert list(car_params.longitudinalTuning.kiBP) == pytest.approx([0.0, 5.0, 15.0, 35.0])
assert list(car_params.longitudinalTuning.kiV) == pytest.approx([0.20, 0.18, 0.13, 0.08])
def test_silverado_camera_acc_allows_engage_from_stop(self):
CarInterface = interfaces[CAR.CHEVROLET_SILVERADO]
fingerprint = _empty_fingerprint()
fingerprint[0] = FINGERPRINTS[CAR.CHEVROLET_SILVERADO][0].copy()
car_params = CarInterface.get_params(CAR.CHEVROLET_SILVERADO, fingerprint, [], alpha_long=False, is_release=False,
docs=False, starpilot_toggles=_test_starpilot_toggles())
assert car_params.minEnableSpeed == pytest.approx(0.0)
def test_silverado_cc_allows_engage_from_stop(self):
CarInterface = interfaces[CAR.CHEVROLET_SILVERADO_CC]
car_params = CarInterface.get_params(
CAR.CHEVROLET_SILVERADO_CC,
_empty_fingerprint(),
[],
alpha_long=False,
is_release=False,
docs=False,
starpilot_toggles=_test_starpilot_toggles(),
)
assert car_params.minEnableSpeed == pytest.approx(0.0)
def test_blazer_uses_softer_low_speed_stop_hold_tune(self):
CarInterface = interfaces[CAR.CHEVROLET_BLAZER]
fingerprint = _empty_fingerprint()
@@ -603,6 +628,13 @@ class TestGMCarController:
assert not should_send_cc_button_spam(SimpleNamespace(flags=GMFlags.CC_LONG.value, minEnableSpeed=10.0), cc, cs)
assert not should_send_cc_button_spam(SimpleNamespace(flags=0, minEnableSpeed=10.0), cc, cs)
def test_cc_button_spam_allows_standstill_when_min_enable_is_zero(self):
cp = SimpleNamespace(flags=GMFlags.CC_LONG.value, minEnableSpeed=0.0)
cc = SimpleNamespace(longActive=True)
cs = SimpleNamespace(out=SimpleNamespace(vEgo=0.0, cruiseState=SimpleNamespace(enabled=False)))
assert should_send_cc_button_spam(cp, cc, cs)
def test_volt_cc_redneck_spam_is_mirrored_to_camera_bus(self):
packer = CANPacker(DBC[CAR.CHEVROLET_VOLT_CC][Bus.pt])
controller = SimpleNamespace(frame=int(0.3 / DT_CTRL), last_button_frame=0, apply_speed=0, malibu_button_phase=0)
@@ -8,7 +8,7 @@ from opendbc.car.lateral import apply_driver_steer_torque_limits, apply_steer_an
from opendbc.car.common.conversions import Conversions as CV
from opendbc.car.hyundai import hyundaicanfd, hyundaican
from opendbc.car.hyundai.hyundaicanfd import CanBus
from opendbc.car.hyundai.values import HyundaiFlags, Buttons, CarControllerParams, CAR, CANFD_ANGLE_LONGITUDINAL_CAR, \
from opendbc.car.hyundai.values import HyundaiFlags, HyundaiStarPilotFlags, Buttons, CarControllerParams, CAR, CANFD_ANGLE_LONGITUDINAL_CAR, \
CANFD_RADAR_LIVE_LONGITUDINAL_CAR, CANFD_ALT_BUTTONS_RESUME_CAR, kia_ev6_gt_line_longitudinal_tuning, \
KIA_EV6_GT_LINE_LONG_TUNING_TESTING_GROUND_ID
from opendbc.car.interfaces import CarControllerBase
@@ -777,6 +777,8 @@ class CarController(CarControllerBase):
left_lane_warning, right_lane_warning, lka_icon))
if self.CP.carFingerprint == CAR.KIA_RAY_EV:
self._ray_lkas11_active = True
if getattr(self.FPCP, "flags", 0) & HyundaiStarPilotFlags.HAS_LKAS12:
can_sends.append(hyundaican.create_lkas12(self.packer, CS.lkas12))
# Button messages
if not self.long_active_ecu:
@@ -850,9 +852,6 @@ class CarController(CarControllerBase):
)
# steering control
# The first-generation Electrified GV70 expects the synthesized LKAS status
# payload. Forwarding its stock status bits leaves lane-safety state asserted
# while StarPilot is suppressing the stock LFA path.
preserve_stock_lkas = bool(self.CP.flags & HyundaiFlags.CANFD_LKA_STEERING) and \
not self.long_active_ecu and self.CP.carFingerprint != CAR.GENESIS_GV70_ELECTRIFIED_1ST_GEN and \
preserve_stock_canfd_lkas_status(self.CP.carFingerprint)
+13 -4
View File
@@ -36,6 +36,8 @@ CLASSIC_MEDIA_BUTTON_CARS = frozenset({
def get_non_scc_cruise_signals(CP) -> tuple[str, str, str, str, str, str]:
if CP.carFingerprint == CAR.KIA_RAY_EV:
return "LABEL11", "CC_React", "LABEL11", "CC_Engaged", "E_EMS11", "Cruise_Limit_Target"
if CP.flags & HyundaiFlags.EV:
return "LABEL11", "CC_React", "EMS12", "ACC_ACT", "E_EMS11", "Cruise_Limit_Target"
if CP.flags & HyundaiFlags.HYBRID:
@@ -142,6 +144,7 @@ class CarState(CarStateBase):
self.msg_364 = {}
self.lfa_block_msg = {}
self.stock_lkas_msg = {}
self.lkas12 = {}
self.stock_lfa_msg = {}
self.stock_lfahda_cluster_msg = {}
self.stock_camera_lead_visible = False
@@ -347,9 +350,7 @@ class CarState(CarStateBase):
# cruise state
no_scc = bool(self.CP.flags & HyundaiFlags.NON_SCC)
if self.CP.carFingerprint == CAR.KIA_RAY_EV:
pass
elif no_scc:
if no_scc:
cruise_available_msg, cruise_available_sig, cruise_enabled_msg, cruise_enabled_sig, cruise_speed_msg, cruise_speed_sig = get_non_scc_cruise_signals(self.CP)
ret.cruiseState.available = cp.vl[cruise_available_msg][cruise_available_sig] != 0
ret.cruiseState.enabled = cp.vl[cruise_enabled_msg][cruise_enabled_sig] != 0
@@ -440,6 +441,8 @@ class CarState(CarStateBase):
self.lkas11 = {}
else:
self.lkas11 = copy.copy(cp_cam.vl["LKAS11"])
if getattr(self.FPCP, "flags", 0) & HyundaiStarPilotFlags.HAS_LKAS12:
self.lkas12 = copy.copy(cp_cam.vl["LKAS12"])
self.clu11 = copy.copy(cp.vl["CLU11"])
self.steer_state = cp.vl["MDPS12"]["CF_Mdps_ToiActive"] # 0 NOT ACTIVE, 1 ACTIVE
if not self.main_cruise_tracking:
@@ -722,6 +725,12 @@ class CarState(CarStateBase):
("BCM_PO_11", 0),
("CLU13", 0),
]
if CP.carFingerprint == CAR.KIA_RAY_EV:
msgs += [
("LABEL11", 10),
("E_EMS11", 100),
("ELECT_GEAR", 100),
]
if CP.carFingerprint in CLASSIC_MEDIA_BUTTON_CARS:
# Steering-wheel media switches are event-driven on the refresh Elantra.
msgs.append(("GW_SWRC_PE", 0))
@@ -730,7 +739,7 @@ class CarState(CarStateBase):
parsers = {
Bus.pt: CANParser(DBC[CP.carFingerprint][Bus.pt], msgs, 0),
Bus.cam: CANParser(DBC[CP.carFingerprint][Bus.pt], [], 2),
Bus.cam: CANParser(DBC[CP.carFingerprint][Bus.pt], [("LKAS12", 0)], 2),
}
if CP.carFingerprint in ALT_BUS_LDA_BUTTON_CARS:
parsers[Bus.alt] = CANParser(DBC[CP.carFingerprint][Bus.pt], [("CLU13", 0)], 1)
@@ -81,6 +81,10 @@ def create_lkas11(packer, frame, CP, apply_torque, steer_req,
values["CF_Lkas_LdwsActivemode"] = 2
if CP.carFingerprint == CAR.KIA_RAY_EV:
if not enabled:
values["CF_Lkas_LdwsActivemode"] = lkas11["CF_Lkas_LdwsActivemode"]
values["CF_Lkas_LdwsSysState"] = lkas11["CF_Lkas_LdwsSysState"]
values["CF_Lkas_FcwOpt_USM"] = lkas11["CF_Lkas_FcwOpt_USM"]
values["CF_Lkas_LdwsOpt_USM"] = 0
values["CF_Lkas_Chksum"] = 0
@@ -102,6 +106,19 @@ def create_lkas11(packer, frame, CP, apply_torque, steer_req,
return packer.make_can_msg("LKAS11", 0, values)
def create_lkas12(packer, lkas12):
values = {s: lkas12[s] for s in (
"CF_Lkas_TsrSlifOpt",
"CF_LkasTsrStatus",
"CF_Lkas_TsrSpeed_Display_Clu",
"CF_LkasTsrSpeed_Display_Navi",
"CF_Lkas_TsrAddinfo_Display",
"CF_Lkas_Daw_USM",
) if s in lkas12}
values["CF_LkasDawStatus"] = 0
return packer.make_can_msg("LKAS12", 0, values)
def create_checksum_can_canfd_blended(packer, bus, addr, values):
dat = packer.make_can_msg(addr, bus, values)[1]
return hyundai_checksum(dat[1:8])
@@ -63,61 +63,6 @@ def _update_checksum(packer, address: int, dat: bytearray) -> None:
_set_value(dat, sig_checksum, checksum)
def _set_little_endian_bits(dat: bytearray, lsb: int, size: int, value: int) -> None:
"""Write the legacy HDA-II field layout without changing the generated DBC aliases."""
value &= (1 << size) - 1
bit = lsb
remaining = size
while remaining:
byte = bit // 8
shift = bit % 8
chunk_size = min(remaining, 8 - shift)
mask = ((1 << chunk_size) - 1) << shift
dat[byte] = (dat[byte] & ~mask) | ((value & ((1 << chunk_size) - 1)) << shift)
value >>= chunk_size
bit += chunk_size
remaining -= chunk_size
def _create_gv70_lka_status_msg(packer, CAN, message_name: str, bus: int, enabled: bool,
lat_active: bool, apply_torque: int):
values = {
"LKA_MODE": 2,
"LKA_ICON": 2 if enabled else 1,
"TORQUE_REQUEST": apply_torque,
"STEER_REQ": 1 if lat_active else 0,
"LKA_ASSIST": 0,
"STEER_MODE": 0,
"DAMP_FACTOR": 100,
}
address, raw, _ = packer.make_can_msg(message_name, bus, values)
dat = bytearray(raw)
legacy_fields = (
(24, 3, 2),
(27, 3, 0),
(30, 2, 0),
(32, 2, 0),
(34, 2, 0),
(36, 2, 0),
(38, 3, 2 if enabled else 1),
(52, 2, 1 if lat_active else 0),
(54, 2, 0),
(56, 1, 0),
(60, 4, 0),
(80, 2, 0),
)
for lsb, size, value in legacy_fields:
_set_little_endian_bits(dat, lsb, size, value)
_set_little_endian_bits(dat, 64 if message_name == "LKAS" else 104, 8, 100)
if message_name == "LKAS":
_set_little_endian_bits(dat, 84, 3, 0)
_update_checksum(packer, address, dat)
return address, bytes(dat), bus
def _create_angle_lfa_msg(packer, CAN, values, apply_angle: float, lat_active: bool, torque_reduction_gain: float):
address = packer.dbc.name_to_msg["LFA"].address
dat = packer.pack(address, values)
@@ -156,13 +101,6 @@ def create_steering_messages(packer, CP, CAN, enabled, lat_active, apply_torque,
if lka_icon is None:
lka_icon = 2 if enabled else 1
if CP.carFingerprint == CAR.GENESIS_GV70_ELECTRIFIED_1ST_GEN and CP.flags & HyundaiFlags.CANFD_LKA_STEERING:
ret = []
if CP.openpilotLongitudinalControl:
ret.append(_create_gv70_lka_status_msg(packer, CAN, "LFA", CAN.ECAN, enabled, lat_active, apply_torque))
ret.append(_create_gv70_lka_status_msg(packer, CAN, "LKAS", CAN.ACAN, enabled, lat_active, apply_torque))
return ret
angle_lkas_alt = CP.flags & HyundaiFlags.CANFD_ANGLE_STEERING and CP.flags & HyundaiFlags.CANFD_LKA_STEERING_ALT
control_values = {
@@ -209,7 +209,9 @@ class CarInterface(CarInterfaceBase):
ret.enableBsm = 0x58b in fingerprint[CAN.ECAN]
# Send LFA message on cars with HDA
if 0x485 in fingerprint[CAN.CAM]:
if 0x485 in fingerprint[CAN.CAM] and (
candidate != CAR.KIA_RAY_EV or fingerprint[CAN.CAM][0x485] == 4
):
ret.flags |= HyundaiFlags.SEND_LFA.value
# These cars use the FCA11 message for the AEB and FCW signals, all others use SCC12
@@ -710,7 +710,7 @@ class TestHyundaiFingerprint:
assert parser.vl["LKAS11"]["CF_Lkas_LdwsActivemode"] == 0
assert parser.vl["LKAS11"]["CF_Lkas_FcwOpt_USM"] == 0
def test_kia_ray_ev_preserves_stock_lkas_option(self):
def test_kia_ray_ev_preserves_stock_inactive_lkas_status(self):
fingerprint = gen_empty_fingerprint()
fingerprint[2][0x485] = 4
CP = CarInterface.get_params(CAR.KIA_RAY_EV, fingerprint, [], False, False, False, None)
@@ -718,15 +718,46 @@ class TestHyundaiFingerprint:
packer = CANPacker(DBC[CP.carFingerprint][Bus.pt])
parser = CANParser(DBC[CP.carFingerprint][Bus.pt], [("LKAS11", 0)], 0)
lkas11 = parser.vl["LKAS11"]
lkas11.update({
"CF_Lkas_LdwsActivemode": 0,
"CF_Lkas_LdwsSysState": 1,
"CF_Lkas_FcwOpt_USM": 1,
})
msg = hyundaican.create_lkas11(
packer, 0, CP, 0, True, False, parser.vl["LKAS11"], False, 4, False,
packer, 0, CP, 0, True, False, lkas11, False, 4, False,
True, True, 0, 0, 2,
)
parser.update([(1, [msg])])
assert parser.vl["LKAS11"]["CF_Lkas_LdwsActivemode"] == 0
assert parser.vl["LKAS11"]["CF_Lkas_LdwsSysState"] == 1
assert parser.vl["LKAS11"]["CF_Lkas_LdwsOpt_USM"] == 0
assert parser.vl["LKAS11"]["CF_Lkas_FcwOpt_USM"] == 1
def test_kia_ray_ev_uses_active_lkas_status_when_enabled(self):
fingerprint = gen_empty_fingerprint()
fingerprint[2][0x485] = 4
CP = CarInterface.get_params(CAR.KIA_RAY_EV, fingerprint, [], False, False, False, None)
packer = CANPacker(DBC[CP.carFingerprint][Bus.pt])
parser = CANParser(DBC[CP.carFingerprint][Bus.pt], [("LKAS11", 0)], 0)
lkas11 = parser.vl["LKAS11"]
lkas11.update({
"CF_Lkas_LdwsActivemode": 0,
"CF_Lkas_LdwsSysState": 1,
"CF_Lkas_FcwOpt_USM": 1,
})
msg = hyundaican.create_lkas11(
packer, 0, CP, 0, True, False, lkas11, False, 4, True,
True, True, 0, 0, 2,
)
parser.update([(1, [msg])])
assert parser.vl["LKAS11"]["CF_Lkas_LdwsActivemode"] == 3
assert parser.vl["LKAS11"]["CF_Lkas_LdwsSysState"] == 4
assert parser.vl["LKAS11"]["CF_Lkas_LdwsOpt_USM"] == 0
assert parser.vl["LKAS11"]["CF_Lkas_FcwOpt_USM"] == 1
assert parser.vl["LKAS11"]["CF_Lkas_FcwOpt_USM"] == 2
def test_kia_ray_ev_delays_first_lkas11(self):
fingerprint = gen_empty_fingerprint()
@@ -796,6 +827,60 @@ class TestHyundaiFingerprint:
palisade_2023 = CarInterface.get_params(CAR.HYUNDAI_PALISADE_2023, gen_empty_fingerprint(), [], True, False, False, None)
assert palisade_2023.safetyConfigs[-1].safetyParam & HyundaiStarPilotSafetyFlags.HAS_LDA_BUTTON
def test_lkas12_da_warning_is_filtered_for_camera_fingerprint(self):
fingerprint = gen_empty_fingerprint()
fingerprint[2][0x53E] = 6
CP = CarInterface.get_params(CAR.HYUNDAI_SONATA_HYBRID, fingerprint, [], False, False, False, None)
FPCP = CarInterface.get_starpilot_params(CAR.HYUNDAI_SONATA_HYBRID, fingerprint, [], CP, get_test_toggles())
assert FPCP.flags & HyundaiStarPilotFlags.HAS_LKAS12
packer = CANPacker(DBC[CP.carFingerprint][Bus.pt])
stock = {
"CF_Lkas_TsrSlifOpt": 3,
"CF_LkasTsrStatus": 2,
"CF_Lkas_TsrSpeed_Display_Clu": 80,
"CF_LkasTsrSpeed_Display_Navi": 70,
"CF_Lkas_TsrAddinfo_Display": 1,
"CF_Lkas_Daw_USM": 0,
"CF_LkasDawStatus": 1,
}
msg = hyundaican.create_lkas12(packer, stock)
parser = CANParser(DBC[CP.carFingerprint][Bus.pt], [("LKAS12", 0)], 0)
parser.update([(1, [msg])])
assert parser.can_valid
assert parser.vl["LKAS12"]["CF_LkasDawStatus"] == 0
assert parser.vl["LKAS12"]["CF_Lkas_TsrSpeed_Display_Clu"] == 80
no_lkas12 = CarInterface.get_params(CAR.HYUNDAI_SONATA_HYBRID, gen_empty_fingerprint(), [], False, False, False, None)
no_lkas12_fpcp = CarInterface.get_starpilot_params(
CAR.HYUNDAI_SONATA_HYBRID, gen_empty_fingerprint(), [], no_lkas12, get_test_toggles(),
)
assert not (no_lkas12_fpcp.flags & HyundaiStarPilotFlags.HAS_LKAS12)
def test_ray_ev_does_not_treat_eight_byte_53e_as_lkas12(self):
fingerprint = gen_empty_fingerprint()
fingerprint[2][0x53E] = 8
CP = CarInterface.get_params(CAR.KIA_RAY_EV, fingerprint, [], False, False, False, None)
FPCP = CarInterface.get_starpilot_params(CAR.KIA_RAY_EV, fingerprint, [], CP, get_test_toggles())
assert not (FPCP.flags & HyundaiStarPilotFlags.HAS_LKAS12)
def test_ray_ev_does_not_treat_eight_byte_485_as_lfa(self):
fingerprint = gen_empty_fingerprint()
fingerprint[2][0x485] = 8
CP = CarInterface.get_params(CAR.KIA_RAY_EV, fingerprint, [], False, False, False, None)
assert not (CP.flags & HyundaiFlags.SEND_LFA)
def test_non_ray_legacy_platform_keeps_53e_lkas12_detection(self):
fingerprint = gen_empty_fingerprint()
fingerprint[2][0x53E] = 8
CP = CarInterface.get_params(CAR.HYUNDAI_SONATA_HYBRID, fingerprint, [], False, False, False, None)
FPCP = CarInterface.get_starpilot_params(CAR.HYUNDAI_SONATA_HYBRID, fingerprint, [], CP, get_test_toggles())
assert FPCP.flags & HyundaiStarPilotFlags.HAS_LKAS12
def test_carnival_lka_button_does_not_enable_angle_steering_safety(self):
fingerprint = gen_empty_fingerprint()
fingerprint[0][0x391] = 8
@@ -864,7 +949,7 @@ class TestHyundaiFingerprint:
(CAR.HYUNDAI_ELANTRA_2022_NON_SCC, ("EMS16", "LVR12"), ()),
(CAR.HYUNDAI_ELANTRA_HEV_2022_NON_SCC, ("E_CRUISE_CONTROL", "ELECT_GEAR"), ("EMS16",)),
(CAR.HYUNDAI_KONA_EV_NON_SCC, ("LABEL11", "EMS12", "E_EMS11"), ()),
(CAR.KIA_RAY_EV, ("E_EMS11",), ("LABEL11", "EMS12", "SCC11", "SCC12")),
(CAR.KIA_RAY_EV, ("LABEL11", "E_EMS11", "ELECT_GEAR"), ("EMS12", "SCC11", "SCC12")),
])
def test_non_scc_cruise_message_selection(self, candidate, expected_msgs, unexpected_msgs):
toggles = get_test_toggles()
@@ -883,6 +968,26 @@ class TestHyundaiFingerprint:
assert not ret.cruiseState.enabled
assert ret.cruiseState.speed == 0
def test_kia_ray_ev_decodes_cruise_state(self):
toggles = get_test_toggles()
CP = CarInterface.get_params(CAR.KIA_RAY_EV, gen_empty_fingerprint(), [], True, False, False, toggles)
FPCP = CarInterface.get_starpilot_params(CAR.KIA_RAY_EV, gen_empty_fingerprint(), [], CP, toggles)
car_state = CarState(CP, FPCP)
can_parsers = car_state.get_can_parsers(CP)
packer = CANPacker(DBC[CP.carFingerprint][Bus.pt])
can_parsers[Bus.pt].update([(1_000_000_000, [
packer.make_can_msg("LABEL11", 0, {"CC_React": 1, "CC_Engaged": 1}),
packer.make_can_msg("E_EMS11", 0, {"Cruise_Limit_Target": 10, "Accel_Pedal_Pos": 0}),
packer.make_can_msg("ELECT_GEAR", 0, {"Elect_Gear_Shifter": 5}),
])])
ret, _ = car_state.update(can_parsers, toggles)
assert ret.cruiseState.available
assert ret.cruiseState.enabled
assert ret.cruiseState.speed == pytest.approx(10 * 0.2777778)
def test_hyundai_redneck_cruise_availability(self, monkeypatch):
class FakeParams:
def __init__(self, *args, **kwargs):
@@ -2288,7 +2393,7 @@ class TestHyundaiFingerprint:
assert parser.vl["LKAS_ALT"]["ADAS_ACIAnglTqRedcGainVal"] == pytest.approx(0.0)
assert parser.vl["LKAS_ALT"]["ADAS_StrAnglReqVal"] == pytest.approx(8.5)
def test_gv70_electrified_synthesizes_lkas_status_payload(self):
def test_gv70_electrified_uses_generic_lkas_status_payload(self):
CP = CarParams.new_message()
CP.carFingerprint = CAR.GENESIS_GV70_ELECTRIFIED_1ST_GEN
CP.flags = int(HyundaiFlags.CANFD | HyundaiFlags.EV | HyundaiFlags.CANFD_LKA_STEERING)
@@ -2331,9 +2436,11 @@ class TestHyundaiFingerprint:
parser.update([(1, lkas_msgs)])
assert parser.can_valid
assert parser.vl["LKAS"]["HAS_LANE_SAFETY"] == 0
assert parser.vl["LKAS"]["DAMP_FACTOR"] == 100
assert parser.vl["LKAS"]["DAMP_FACTOR"] == 0
assert parser.vl["LKAS"]["TORQUE_REQUEST"] == 0
assert parser.vl["LKAS"]["STEER_REQ"] == 1
assert parser.vl["LKAS"]["STEER_MODE"] == 0
assert parser.vl["LKAS"]["NEW_SIGNAL_2"] == 0
CP.openpilotLongitudinalControl = True
lfa_parser = CANParser(DBC[CP.carFingerprint][Bus.pt], [("LFA", 0)], can_bus.ECAN)
@@ -124,6 +124,7 @@ class HyundaiStarPilotSafetyFlags(IntFlag):
class HyundaiStarPilotFlags(IntFlag):
SPEED_LIMIT_AVAILABLE = 1
MAIN_CRUISE_STATE_TRACKING = 2 ** 2
HAS_LKAS12 = 2 ** 9
class HyundaiFlags(IntFlag):
+3
View File
@@ -240,6 +240,9 @@ class CarInterfaceBase(ABC):
if 0x1FA in fingerprint[CAN.ECAN]:
fp_ret.flags |= HyundaiStarPilotFlags.SPEED_LIMIT_AVAILABLE.value
if candidate != HYUNDAI.KIA_RAY_EV and not (CP.flags & HyundaiFlags.CANFD) and 0x53E in fingerprint[2]:
fp_ret.flags |= HyundaiStarPilotFlags.HAS_LKAS12.value
fp_ret.redneckCruiseAvailable = bool(CP.flags & HyundaiFlags.NON_SCC) and not bool(CP.flags & HyundaiFlags.CANFD_ALT_BUTTONS)
if fp_ret.redneckCruiseAvailable and params.get_bool("RedneckCruise"):
fp_ret.pcmCruiseSpeed = False
@@ -39,6 +39,7 @@ _STOP_START_PULSE_FRAMES = 30
_STOP_START_PULSE_PERIOD_FRAMES = 5
_AVH_STARTUP_DELAY_FRAMES = _STOP_START_STARTUP_DELAY_FRAMES
_AVH_STARTUP_DEADLINE_FRAMES = _STOP_START_STARTUP_DEADLINE_FRAMES
_AVH_PULSE_MESSAGES = 15 # Match the native 10 Hz AVH frame for roughly 1.5 seconds
def get_safety_CP():
@@ -92,6 +93,7 @@ class CarController(CarControllerBase):
self.avh_attempted = False
self.avh_request_started = False
self.avh_last_counter = None
self.avh_messages_sent = 0
def _stop_start_off_request(self, CC, CS, starpilot_toggles):
"""Send one bounded Subaru Stop/Start OFF request after ignition.
@@ -149,7 +151,7 @@ class CarController(CarControllerBase):
return msg
def _avh_on_request(self, CC, CS, starpilot_toggles):
"""Send one bounded Subaru AVH ON request after ignition.
"""Send a bounded Subaru AVH ON pulse after ignition.
The AVH button frame was identified on the 2025 Legacy only. Keep this
independent from Stop/Start so the existing Outback request is unchanged.
@@ -181,15 +183,20 @@ class CarController(CarControllerBase):
self.avh_request_started = True
self.avh_last_counter = int(avh_msg.get("COUNTER", 0)) % 0x10
if self.avh_messages_sent >= _AVH_PULSE_MESSAGES:
self.avh_attempted = True
return None
counter = int(avh_msg.get("COUNTER", 0)) % 0x10
if counter == self.avh_last_counter:
return None
self.avh_attempted = True
msg = subarucan.create_avh_control(
self.packer, avh_msg, raw_dat=avh_dat,
counter=counter, bus=CanBus.alt_for_cp(self.CP),
)
self.avh_last_counter = counter
self.avh_messages_sent += 1
return msg
def _reset_legacy_2025_handoff(self):
@@ -292,7 +292,7 @@ def test_stop_start_request_is_bounded_and_uses_live_dashlights(platform, expect
assert controller.stop_start_acknowledged
def test_avh_request_sets_observed_bit_and_is_bounded():
def test_avh_request_sets_observed_bit_and_pulses_at_native_rate():
CP = CarInterface.get_non_essential_params(CAR.SUBARU_LEGACY_2025)
controller = CarController({}, CP)
controller.frame = 101
@@ -318,6 +318,8 @@ def test_avh_request_sets_observed_bit_and_is_bounded():
out=SimpleNamespace(
standstill=True,
gearShifter=structs.CarState.GearShifter.park,
vEgoRaw=0.0,
steeringAngleDeg=0.0,
),
)
toggles = SimpleNamespace(subaru_stop_start_off=False, subaru_avh_on=True, subaru_sng=False)
@@ -341,6 +343,37 @@ def test_avh_request_sets_observed_bit_and_is_bounded():
assert parser.vl["AVH"]["AVH"] == 1
assert parser.vl["AVH"]["COUNTER"] == 0
controller.frame = 104
_, can_sends = controller.update(CC, CS, 0, toggles)
assert not any(msg[0] == 0x32b for msg in can_sends)
avh_msgs = []
for counter in range(1, 15):
CS.avh_msg["COUNTER"] = counter
raw_dat = bytearray.fromhex("14001c4208800000")
raw_dat[1] = counter
raw_dat[0] = ((0x32B & 0xFF) + ((0x32B >> 8) & 0xFF) + sum(raw_dat[1:])) & 0xFF
CS.avh_dat = bytes(raw_dat)
controller.frame = 103 + (counter * 10)
_, can_sends = controller.update(CC, CS, 0, toggles)
sent = [msg for msg in can_sends if msg[0] == 0x32b]
assert len(sent) == 1
avh_msgs.extend(sent)
assert len(avh_msgs) == 14
assert [msg[1][1] & 0x0F for msg in avh_msgs] == list(range(1, 15))
assert all(msg[1][5] & 0x20 for msg in avh_msgs)
assert not controller.avh_attempted
CS.avh_msg["COUNTER"] = 15
CS.avh_dat = bytes.fromhex("230f1c4208800000")
controller.frame = 253
_, can_sends = controller.update(CC, CS, 0, toggles)
assert not any(msg[0] == 0x32b for msg in can_sends)
assert controller.avh_attempted
CS.avh_msg["COUNTER"] = 0
CS.avh_dat = bytes.fromhex("14001c4208800000")
controller.frame = 131
_, can_sends = controller.update(CC, CS, 0, toggles)
assert not any(msg[0] == 0x32b for msg in can_sends)
@@ -1500,6 +1500,7 @@ BO_ 913 BCM_PO_11: 8 Vector__XXX
BO_ 1426 LABEL11: 8 XXX
SG_ CC_React : 34|1@1+ (1,0) [0|1] "" XXX
SG_ CC_Engaged : 35|1@1+ (1,0) [0|1] "" XXX
BO_ 910 WHL_SPD12_FS: 5 iBAU
SG_ CRC : 0|8@1+ (1,0) [0|0] "" Vector__XXX
@@ -1500,6 +1500,7 @@ BO_ 913 BCM_PO_11: 8 Vector__XXX
BO_ 1426 LABEL11: 8 XXX
SG_ CC_React : 34|1@1+ (1,0) [0|1] "" XXX
SG_ CC_Engaged : 35|1@1+ (1,0) [0|1] "" XXX
BO_ 910 WHL_SPD12_FS: 5 iBAU
SG_ CRC : 0|8@1+ (1,0) [0|0] "" Vector__XXX
+11 -2
View File
@@ -89,6 +89,8 @@ static bool ford_get_quality_flag_valid(const CANPacket_t *msg) {
static bool ford_lka_steering = false;
static bool ford_extended_lateral = false;
static bool ford_angle_mode = false;
static bool ford_longitudinal = false;
static bool ford_cancel_resume_button = false;
static int16_t ford_shadow_curvature = 0;
// Curvature rate limits
@@ -219,6 +221,10 @@ static void ford_rx_hook(const CANPacket_t *msg) {
acc_main_on = (cruise_state == 3U) || cruise_engaged;
}
if (msg->addr == FORD_Steering_Data_FD1) {
ford_cancel_resume_button = ((msg->data[2] >> 5) & 1U) != 0U;
}
}
}
@@ -276,7 +282,8 @@ static bool ford_tx_hook(const CANPacket_t *msg) {
// if cancel button is pressed when cruise isn't engaged.
bool violation = false;
violation |= ((msg->data[1] >> 0) & 1U) && !cruise_engaged_prev; // Signal: CcAslButtnCnclPress (cancel)
violation |= ((msg->data[3] >> 1) & 1U) && !controls_allowed; // Signal: CcAsllButtnResPress (resume)
bool stock_resume_from_driver = !ford_longitudinal && acc_main_on && ford_cancel_resume_button;
violation |= ((msg->data[3] >> 1) & 1U) && !(controls_allowed || stock_resume_from_driver); // Signal: CcAsllButtnResPress (resume)
if (violation) {
tx = false;
@@ -415,6 +422,7 @@ static safety_config ford_init(uint16_t param) {
{.msg = {{FORD_Yaw_Data_FD1, 0, 8, 100U, .max_counter = 255U}, { 0 }, { 0 }}},
// These messages have no counter or checksum
{.msg = {{FORD_EngBrakeData, 0, 8, 10U, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}},
{.msg = {{FORD_Steering_Data_FD1, 0, 8, 10U, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}},
{.msg = {{FORD_EngVehicleSpThrottle, 0, 8, 100U, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}},
{.msg = {{FORD_DesiredTorqBrk, 0, 8, 50U, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}},
};
@@ -454,10 +462,11 @@ static safety_config ford_init(uint16_t param) {
ford_lka_steering = GET_FLAG(param, FORD_PARAM_LKA_STEERING);
ford_extended_lateral = false;
ford_angle_mode = false;
ford_cancel_resume_button = false;
ford_shadow_curvature = 0;
ford_desired_path_angle_last = 0;
bool ford_longitudinal = false;
ford_longitudinal = false;
#ifdef ALLOW_DEBUG
const uint16_t FORD_PARAM_LONGITUDINAL = 1;
@@ -29,6 +29,7 @@ const LongitudinalLimits HYUNDAI_LONG_LIMITS = {
{0x340, 0, 8, .check_relay = true}, /* LKAS11 Bus 0 */ \
{0x4F1, scc_bus, 4, .check_relay = false}, /* CLU11 Bus 0 (radar-SCC) or 2 (camera-SCC) */ \
{0x485, 0, (can_refresh) ? 8 : 4, .check_relay = true}, /* LFAHDA_MFC Bus 0 */ \
{0x53E, 0, 6, .check_relay = false}, /* LKAS12 replacement after camera advertises it */ \
#define HYUNDAI_LONG_COMMON_TX_MSGS(scc_bus, can_refresh) \
HYUNDAI_COMMON_TX_MSGS(scc_bus, can_refresh) \
@@ -140,6 +141,12 @@ static uint32_t hyundai_get_checksum(const CANPacket_t *msg) {
return chksum;
}
static void hyundai_rx_all_hook(const CANPacket_t *msg) {
if ((msg->addr == 0x53EU) && (msg->bus == 2U) && (GET_LEN(msg) == 6U)) {
hyundai_has_lkas12 = true;
}
}
static uint32_t hyundai_compute_checksum(const CANPacket_t *msg) {
uint8_t chksum = 0;
if (msg->addr == 0x386U) {
@@ -284,6 +291,10 @@ static bool hyundai_tx_hook(const CANPacket_t *msg) {
bool tx = true;
if ((msg->addr == 0x53EU) && !hyundai_has_lkas12) {
tx = false;
}
// FCA11: Block any potential actuation. The blended HDA II layout uses
// different static fields, but its explicit AEB/FCA request bits stay zero.
if (msg->addr == 0x38DU) {
@@ -695,6 +706,7 @@ static safety_config hyundai_legacy_init(uint16_t param) {
const safety_hooks hyundai_hooks = {
.init = hyundai_init,
.rx = hyundai_rx_hook,
.rx_all = hyundai_rx_all_hook,
.tx = hyundai_tx_hook,
.get_counter = hyundai_get_counter,
.get_checksum = hyundai_get_checksum,
@@ -704,6 +716,7 @@ const safety_hooks hyundai_hooks = {
const safety_hooks hyundai_legacy_hooks = {
.init = hyundai_legacy_init,
.rx = hyundai_rx_hook,
.rx_all = hyundai_rx_all_hook,
.tx = hyundai_tx_hook,
.get_counter = hyundai_get_counter,
.get_checksum = hyundai_get_checksum,
@@ -63,6 +63,9 @@ bool hyundai_cancel_button_enable = false;
extern bool hyundai_can_refresh_msgs;
bool hyundai_can_refresh_msgs = false;
extern bool hyundai_has_lkas12;
bool hyundai_has_lkas12 = false;
extern bool hyundai_elantra_hev_2024;
bool hyundai_elantra_hev_2024 = false;
@@ -106,6 +109,7 @@ void hyundai_common_init(uint16_t param) {
hyundai_non_scc = GET_FLAG(param, HYUNDAI_PARAM_NON_SCC);
hyundai_cancel_button_enable = GET_FLAG(param, HYUNDAI_PARAM_CANCEL_BTN_ENABLE);
hyundai_can_refresh_msgs = GET_FLAG(param, HYUNDAI_PARAM_CAN_REFRESH_MSGS);
hyundai_has_lkas12 = false;
hyundai_elantra_hev_2024 = hyundai_can_refresh_msgs && hyundai_hybrid_gas_signal && hyundai_camera_scc;
hyundai_aol_main_lkas_sync = false;
@@ -75,6 +75,7 @@ class TestFordSafetyBase(common.CarSafetyTest):
MSG_LateralMotionControl2, MSG_IPMA_Data]}
STEER_MESSAGE = 0
STOCK_LONGITUDINAL = False
# Curvature control limits
LKA_STEERING = False
@@ -199,6 +200,17 @@ class TestFordSafetyBase(common.CarSafetyTest):
}
return self.packer.make_can_msg_safety("Steering_Data_FD1", bus, values)
def _combined_cancel_resume_msg(self, pressed: bool):
values = {"CcAslButtnCnclResPress": int(pressed)}
return self.packer.make_can_msg_safety("Steering_Data_FD1", 0, values)
def _pcm_main_on_msg(self, main_on: bool):
values = {
"BpedDrvAppl_D_Actl": 1,
"CcStat_D_Actl": 3 if main_on else 0,
}
return self.packer.make_can_msg_safety("EngBrakeData", 0, values)
def test_rx_hook(self):
# checksum, counter, and quality flag checks
for quality_flag in [True, False]:
@@ -381,6 +393,25 @@ class TestFordSafetyBase(common.CarSafetyTest):
for bus in (0, 2):
self.assertEqual(enabled, self._tx(self._acc_button_msg(Buttons.CANCEL, bus)))
def test_stock_resume_relay_requires_physical_button_and_cruise_main(self):
self.safety.set_controls_allowed(False)
self._rx(self._pcm_main_on_msg(True))
for bus in (0, 2):
self.assertFalse(self._tx(self._acc_button_msg(Buttons.RESUME, bus)))
self._rx(self._combined_cancel_resume_msg(True))
for bus in (0, 2):
self.assertEqual(self.STOCK_LONGITUDINAL, self._tx(self._acc_button_msg(Buttons.RESUME, bus)))
self._rx(self._combined_cancel_resume_msg(False))
for bus in (0, 2):
self.assertFalse(self._tx(self._acc_button_msg(Buttons.RESUME, bus)))
self._rx(self._pcm_main_on_msg(False))
self._rx(self._combined_cancel_resume_msg(True))
for bus in (0, 2):
self.assertFalse(self._tx(self._acc_button_msg(Buttons.RESUME, bus)))
def _toggle_aol(self, toggle_on):
# EngBrakeData, CcStat_D_Actl is the cruise state
# 3 is standby (main on), 5 is active (engaged)
@@ -394,6 +425,7 @@ class TestFordSafetyBase(common.CarSafetyTest):
class TestFordCANFDStockSafety(TestFordSafetyBase):
STEER_MESSAGE = MSG_LateralMotionControl2
STOCK_LONGITUDINAL = True
TX_MSGS = [
[MSG_Steering_Data_FD1, 0], [MSG_Steering_Data_FD1, 2], [MSG_ACCDATA_3, 0], [MSG_Lane_Assist_Data1, 0],
@@ -446,6 +478,7 @@ class TestFordCANFDStockSafety(TestFordSafetyBase):
class TestFordStockSafety(TestFordSafetyBase):
STEER_MESSAGE = MSG_LateralMotionControl
STOCK_LONGITUDINAL = True
TX_MSGS = [
[MSG_Steering_Data_FD1, 0], [MSG_Steering_Data_FD1, 2], [MSG_ACCDATA_3, 0], [MSG_Lane_Assist_Data1, 0],
@@ -427,6 +427,18 @@ def test_hyundai_starpilot_rx_sources():
safety.safety_rx_hook(libsafety_py.make_CANPacket(0x421, 0, bytes(8)))
def test_hyundai_lkas12_tx_requires_stock_camera_message():
safety = libsafety_py.libsafety
assert safety.set_safety_hooks(CarParams.SafetyModel.hyundai, 0) == 0
safety.init_tests()
lkas12 = libsafety_py.make_CANPacket(0x53E, 0, bytes(6))
assert not safety.safety_tx_hook(lkas12)
safety.safety_rx_hook(libsafety_py.make_CANPacket(0x53E, 2, bytes(6)))
assert safety.safety_tx_hook(lkas12)
class TestHyundaiLongitudinalSafety(HyundaiLongitudinalBase, TestHyundaiSafety):
TX_MSGS = [[0x340, 0], [0x4F1, 0], [0x485, 0], [0x420, 0], [0x421, 0], [0x50A, 0], [0x389, 0], [0x4A2, 0], [0x38D, 0], [0x483, 0], [0x7D0, 0]]
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+1 -1
View File
@@ -1,2 +1,2 @@
extern const uint8_t gitversion[19];
const uint8_t gitversion[19] = "DEV-3ebc6b99-DEBUG";
const uint8_t gitversion[19] = "DEV-26ce46ba-DEBUG";
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+1 -1
View File
@@ -1 +1 @@
DEV-3ebc6b99-DEBUG
DEV-26ce46ba-DEBUG

Some files were not shown because too many files have changed in this diff Show More