Release 260111

This commit is contained in:
Comma Device
2026-01-11 18:23:29 +08:00
commit 3721ecbf8a
2601 changed files with 855070 additions and 0 deletions

0
scripts/__init__.py Normal file
View File

View File

@@ -0,0 +1,156 @@
#!/usr/bin/env python3
import time
from smbus2 import SMBus
from collections import namedtuple
# https://datasheets.maximintegrated.com/en/ds/MAX98089.pdf
AmpConfig = namedtuple('AmpConfig', ['name', 'value', 'register', 'offset', 'mask'])
EQParams = namedtuple('EQParams', ['K', 'k1', 'k2', 'c1', 'c2'])
def configs_from_eq_params(base, eq_params):
return [
AmpConfig("K (high)", (eq_params.K >> 8), base, 0, 0xFF),
AmpConfig("K (low)", (eq_params.K & 0xFF), base + 1, 0, 0xFF),
AmpConfig("k1 (high)", (eq_params.k1 >> 8), base + 2, 0, 0xFF),
AmpConfig("k1 (low)", (eq_params.k1 & 0xFF), base + 3, 0, 0xFF),
AmpConfig("k2 (high)", (eq_params.k2 >> 8), base + 4, 0, 0xFF),
AmpConfig("k2 (low)", (eq_params.k2 & 0xFF), base + 5, 0, 0xFF),
AmpConfig("c1 (high)", (eq_params.c1 >> 8), base + 6, 0, 0xFF),
AmpConfig("c1 (low)", (eq_params.c1 & 0xFF), base + 7, 0, 0xFF),
AmpConfig("c2 (high)", (eq_params.c2 >> 8), base + 8, 0, 0xFF),
AmpConfig("c2 (low)", (eq_params.c2 & 0xFF), base + 9, 0, 0xFF),
]
BASE_CONFIG = [
AmpConfig("MCLK prescaler", 0b01, 0x10, 4, 0b00110000),
AmpConfig("PM: enable speakers", 0b11, 0x4D, 4, 0b00110000),
AmpConfig("PM: enable DACs", 0b11, 0x4D, 0, 0b00000011),
AmpConfig("Enable PLL1", 0b1, 0x12, 7, 0b10000000),
AmpConfig("Enable PLL2", 0b1, 0x1A, 7, 0b10000000),
AmpConfig("DAI1: I2S mode", 0b00100, 0x14, 2, 0b01111100),
AmpConfig("DAI2: I2S mode", 0b00100, 0x1C, 2, 0b01111100),
AmpConfig("DAI1 Passband filtering: music mode", 0b1, 0x18, 7, 0b10000000),
AmpConfig("DAI1 voice mode gain (DV1G)", 0b00, 0x2F, 4, 0b00110000),
AmpConfig("DAI1 attenuation (DV1)", 0x0, 0x2F, 0, 0b00001111),
AmpConfig("DAI2 attenuation (DV2)", 0x0, 0x31, 0, 0b00001111),
AmpConfig("DAI2: DC blocking", 0b1, 0x20, 0, 0b00000001),
AmpConfig("DAI2: High sample rate", 0b0, 0x20, 3, 0b00001000),
AmpConfig("ALC enable", 0b1, 0x43, 7, 0b10000000),
AmpConfig("ALC/excursion limiter release time", 0b101, 0x43, 4, 0b01110000),
AmpConfig("ALC multiband enable", 0b1, 0x43, 3, 0b00001000),
AmpConfig("DAI1 EQ enable", 0b0, 0x49, 0, 0b00000001),
AmpConfig("DAI2 EQ clip detection disabled", 0b1, 0x32, 4, 0b00010000),
AmpConfig("DAI2 EQ attenuation", 0x5, 0x32, 0, 0b00001111),
AmpConfig("Excursion limiter upper corner freq", 0b100, 0x41, 4, 0b01110000),
AmpConfig("Excursion limiter lower corner freq", 0b00, 0x41, 0, 0b00000011),
AmpConfig("Excursion limiter threshold", 0b000, 0x42, 0, 0b00001111),
AmpConfig("Distortion limit (THDCLP)", 0x6, 0x46, 4, 0b11110000),
AmpConfig("Distortion limiter release time constant", 0b0, 0x46, 0, 0b00000001),
AmpConfig("Right DAC input mixer: DAI1 left", 0b0, 0x22, 3, 0b00001000),
AmpConfig("Right DAC input mixer: DAI1 right", 0b0, 0x22, 2, 0b00000100),
AmpConfig("Right DAC input mixer: DAI2 left", 0b1, 0x22, 1, 0b00000010),
AmpConfig("Right DAC input mixer: DAI2 right", 0b0, 0x22, 0, 0b00000001),
AmpConfig("DAI1 audio port selector", 0b10, 0x16, 6, 0b11000000),
AmpConfig("DAI2 audio port selector", 0b01, 0x1E, 6, 0b11000000),
AmpConfig("Enable left digital microphone", 0b1, 0x48, 5, 0b00100000),
AmpConfig("Enable right digital microphone", 0b1, 0x48, 4, 0b00010000),
AmpConfig("Enhanced volume smoothing disabled", 0b0, 0x49, 7, 0b10000000),
AmpConfig("Volume adjustment smoothing disabled", 0b0, 0x49, 6, 0b01000000),
AmpConfig("Zero-crossing detection disabled", 0b0, 0x49, 5, 0b00100000),
]
CONFIGS = {
"tici": [
AmpConfig("Right speaker output from right DAC", 0b1, 0x2C, 0, 0b11111111),
AmpConfig("Right Speaker Mixer Gain", 0b00, 0x2D, 2, 0b00001100),
AmpConfig("Right speaker output volume", 0x1c, 0x3E, 0, 0b00011111),
AmpConfig("DAI2 EQ enable", 0b1, 0x49, 1, 0b00000010),
*configs_from_eq_params(0x84, EQParams(0x274F, 0xC0FF, 0x3BF9, 0x0B3C, 0x1656)),
*configs_from_eq_params(0x8E, EQParams(0x1009, 0xC6BF, 0x2952, 0x1C97, 0x30DF)),
*configs_from_eq_params(0x98, EQParams(0x0F75, 0xCBE5, 0x0ED2, 0x2528, 0x3E42)),
*configs_from_eq_params(0xA2, EQParams(0x091F, 0x3D4C, 0xCE11, 0x1266, 0x2807)),
*configs_from_eq_params(0xAC, EQParams(0x0A9E, 0x3F20, 0xE573, 0x0A8B, 0x3A3B)),
],
"tizi": [
AmpConfig("Left speaker output from left DAC", 0b1, 0x2B, 0, 0b11111111),
AmpConfig("Right speaker output from right DAC", 0b1, 0x2C, 0, 0b11111111),
AmpConfig("Left Speaker Mixer Gain", 0b00, 0x2D, 0, 0b00000011),
AmpConfig("Right Speaker Mixer Gain", 0b00, 0x2D, 2, 0b00001100),
AmpConfig("Left speaker output volume", 0x17, 0x3D, 0, 0b00011111),
AmpConfig("Right speaker output volume", 0x17, 0x3E, 0, 0b00011111),
AmpConfig("DAI2 EQ enable", 0b0, 0x49, 1, 0b00000010),
AmpConfig("DAI2: DC blocking", 0b0, 0x20, 0, 0b00000001),
AmpConfig("ALC enable", 0b0, 0x43, 7, 0b10000000),
AmpConfig("DAI2 EQ attenuation", 0x2, 0x32, 0, 0b00001111),
AmpConfig("Excursion limiter upper corner freq", 0b001, 0x41, 4, 0b01110000),
AmpConfig("Excursion limiter threshold", 0b100, 0x42, 0, 0b00001111),
AmpConfig("Distortion limit (THDCLP)", 0x0, 0x46, 4, 0b11110000),
AmpConfig("Distortion limiter release time constant", 0b1, 0x46, 0, 0b00000001),
AmpConfig("Left DAC input mixer: DAI1 left", 0b0, 0x22, 7, 0b10000000),
AmpConfig("Left DAC input mixer: DAI1 right", 0b0, 0x22, 6, 0b01000000),
AmpConfig("Left DAC input mixer: DAI2 left", 0b1, 0x22, 5, 0b00100000),
AmpConfig("Left DAC input mixer: DAI2 right", 0b0, 0x22, 4, 0b00010000),
AmpConfig("Right DAC input mixer: DAI2 left", 0b0, 0x22, 1, 0b00000010),
AmpConfig("Right DAC input mixer: DAI2 right", 0b1, 0x22, 0, 0b00000001),
AmpConfig("Volume adjustment smoothing disabled", 0b1, 0x49, 6, 0b01000000),
],
}
class Amplifier:
AMP_I2C_BUS = 0
AMP_ADDRESS = 0x10
def __init__(self, debug=False):
self.debug = debug
def _get_shutdown_config(self, amp_disabled: bool) -> AmpConfig:
return AmpConfig("Global shutdown", 0b0 if amp_disabled else 0b1, 0x51, 7, 0b10000000)
def _set_configs(self, configs: list[AmpConfig]) -> None:
with SMBus(self.AMP_I2C_BUS) as bus:
for config in configs:
if self.debug:
print(f"Setting \"{config.name}\" to {config.value}:")
old_value = bus.read_byte_data(self.AMP_ADDRESS, config.register, force=True)
new_value = (old_value & (~config.mask)) | ((config.value << config.offset) & config.mask)
bus.write_byte_data(self.AMP_ADDRESS, config.register, new_value, force=True)
if self.debug:
print(f" Changed {hex(config.register)}: {hex(old_value)} -> {hex(new_value)}")
def set_configs(self, configs: list[AmpConfig]) -> bool:
# retry in case panda is using the amp
tries = 1
for i in range(1):
try:
self._set_configs(configs)
return True
except OSError:
print(f"Failed to set amp config, {tries - i - 1} retries left")
time.sleep(0.02)
return False
def set_global_shutdown(self, amp_disabled: bool) -> bool:
return self.set_configs([self._get_shutdown_config(amp_disabled), ])
def initialize_configuration(self, model: str) -> bool:
cfgs = [
self._get_shutdown_config(True),
*BASE_CONFIG,
*CONFIGS[model],
self._get_shutdown_config(False),
]
return self.set_configs(cfgs)
#if __name__ == "__main__":
#with open("/sys/firmware/devicetree/base/model") as f:
# model = f.read().strip('\x00')
#model = model.split('comma ')[-1]
#amp = Amplifier()
#amp.initialize_configuration(model)

1107
scripts/add/events_ko.py Normal file

File diff suppressed because it is too large Load Diff

1133
scripts/add/events_zh.py Normal file

File diff suppressed because it is too large Load Diff

11
scripts/apply-pr.sh Executable file
View File

@@ -0,0 +1,11 @@
#!/usr/bin/env bash
if [ $# -eq 0 ]; then
echo "usage: $0 <pull-request-number>"
exit 1
fi
BASE="https://github.com/commaai/openpilot/pull/"
PR_NUM="$(echo $1 | grep -o -E '[0-9]+')"
curl -L $BASE/$PR_NUM.patch | git apply -3

3
scripts/cell.sh Executable file
View File

@@ -0,0 +1,3 @@
#!/usr/bin/env bash
nmcli connection modify --temporary esim ipv4.route-metric 1 ipv6.route-metric 1
nmcli con up esim

16
scripts/checkout-pr.sh Executable file
View File

@@ -0,0 +1,16 @@
#!/usr/bin/env bash
set -e
if [ $# -eq 0 ]; then
echo "usage: $0 <pull-request-number>"
exit 1
fi
BASE="https://github.com/commaai/openpilot/pull/"
PR_NUM="$(echo $1 | grep -o -E '[0-9]+')"
BRANCH=tmp-pr${PR_NUM}
git branch -D -f $BRANCH || true
git fetch -u -f origin pull/$PR_NUM/head:$BRANCH
git switch $BRANCH
git reset --hard FETCH_HEAD

5
scripts/disable-powersave.py Executable file
View File

@@ -0,0 +1,5 @@
#!/usr/bin/env python3
from openpilot.system.hardware import HARDWARE
if __name__ == "__main__":
HARDWARE.set_power_save(False)

129
scripts/jenkins_loop_test.sh Executable file
View File

@@ -0,0 +1,129 @@
#!/usr/bin/env bash
set -e
YELLOW='\033[0;33m'
GREEN='\033[0;32m'
UNDERLINE='\033[4m'
BOLD='\033[1m'
NC='\033[0m'
BRANCH="master"
RUNS="20"
COOKIE_JAR=/tmp/cookies
CRUMB=$(curl -s --cookie-jar $COOKIE_JAR 'https://jenkins.comma.life/crumbIssuer/api/xml?xpath=concat(//crumbRequestField,":",//crumb)')
FIRST_LOOP=1
function loop() {
JENKINS_BRANCH="__jenkins_loop_${BRANCH}_$(date +%s)"
API_ROUTE="https://jenkins.comma.life/job/openpilot/job/$JENKINS_BRANCH"
for run in $(seq 1 $((RUNS / 2))); do
N=2
if [[ $FIRST_LOOP ]]; then
TEMP_DIR=$(mktemp -d)
GIT_LFS_SKIP_SMUDGE=1 git clone --quiet -b $BRANCH --depth=1 --no-tags git@github.com:commaai/openpilot $TEMP_DIR
git -C $TEMP_DIR checkout --quiet -b $JENKINS_BRANCH
echo "TESTING: $(date)" >> $TEMP_DIR/testing_jenkins
git -C $TEMP_DIR add testing_jenkins
git -C $TEMP_DIR commit --quiet -m "testing"
git -C $TEMP_DIR push --quiet -f origin $JENKINS_BRANCH
rm -rf $TEMP_DIR
FIRST_BUILD=1
echo ''
echo 'waiting on Jenkins...'
echo ''
sleep 90
FIRST_LOOP=""
fi
FIRST_BUILD=$(curl -s $API_ROUTE/api/json | jq .nextBuildNumber)
LAST_BUILD=$((FIRST_BUILD+N-1))
TEST_BUILDS=( $(seq $FIRST_BUILD $LAST_BUILD) )
# Start N new builds
for i in ${TEST_BUILDS[@]};
do
echo "Starting build $i"
curl -s --output /dev/null --cookie $COOKIE_JAR -H "$CRUMB" -X POST $API_ROUTE/build?delay=0sec
sleep 5
done
echo ""
# Wait for all builds to end
while true; do
sleep 30
count=0
for i in ${TEST_BUILDS[@]};
do
RES=$(curl -s -w "\n%{http_code}" --cookie $COOKIE_JAR -H "$CRUMB" $API_ROUTE/$i/api/json)
HTTP_CODE=$(tail -n1 <<< "$RES")
JSON=$(sed '$ d' <<< "$RES")
if [[ $HTTP_CODE == "200" ]]; then
STILL_RUNNING=$(echo $JSON | jq .inProgress)
if [[ $STILL_RUNNING == "true" ]]; then
echo -e "Build $i: ${YELLOW}still running${NC}"
continue
else
count=$((count+1))
echo -e "Build $i: ${GREEN}done${NC}"
fi
else
echo "No status for build $i"
fi
done
echo "See live results: ${API_ROUTE}/buildTimeTrend"
echo ""
if [[ $count -ge $N ]]; then
break
fi
done
done
}
function usage() {
echo ""
echo "Run the Jenkins tests multiple times on a specific branch"
echo ""
echo -e "${BOLD}${UNDERLINE}Options:${NC}"
echo -e " ${BOLD}-n, --n${NC}"
echo -e " Specify how many runs to do (default to ${BOLD}20${NC})"
echo -e " ${BOLD}-b, --branch${NC}"
echo -e " Specify which branch to run the tests against (default to ${BOLD}master${NC})"
echo ""
}
function _looper() {
if [[ $# -eq 0 ]]; then
usage
exit 0
fi
# parse Options
while [[ $# -gt 0 ]]; do
case $1 in
-n | --n ) shift 1; RUNS="$1"; shift 1 ;;
-b | --b | --branch | -branch ) shift 1; BRANCH="$1"; shift 1 ;;
* ) usage; exit 0 ;;
esac
done
echo ""
echo -e "You are about to start $RUNS Jenkins builds against the $BRANCH branch."
echo -e "If you expect this to run overnight, ${UNDERLINE}${BOLD}unplug the cold reboot power switch${NC} from the testing closet before."
echo ""
read -p "Press (y/Y) to confirm: " choice
if [[ "$choice" == "y" || "$choice" == "Y" ]]; then
loop
fi
}
_looper $@

7
scripts/launch_corolla.sh Executable file
View File

@@ -0,0 +1,7 @@
#!/usr/bin/env bash
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null && pwd)"
export FINGERPRINT="TOYOTA_COROLLA_TSS2"
export SKIP_FW_QUERY="1"
$DIR/../launch_openpilot.sh

View File

@@ -0,0 +1,10 @@
#!/usr/bin/env bash
FAIL=0
if grep -n '\(#\|//\)\([[:space:]]*\)NOMERGE' $@; then
echo -e "NOMERGE comments found! Remove them before merging\n"
FAIL=1
fi
exit $FAIL

View File

@@ -0,0 +1,10 @@
#!/usr/bin/env bash
FAIL=0
if grep -n '#include "third_party/raylib/include/raylib\.h"' $@ | grep -v '^system/ui/raylib/raylib\.h'; then
echo -e "Bad raylib include found! Use '#include \"system/ui/raylib/raylib.h\"' instead\n"
FAIL=1
fi
exit $FAIL

View File

@@ -0,0 +1,15 @@
#!/usr/bin/env bash
FAIL=0
if grep '^#!.*python' $@ | grep -v '#!/usr/bin/env python3$'; then
echo -e "Invalid shebang! Must use '#!/usr/bin/env python3'\n"
FAIL=1
fi
if grep '^#!.*bash' $@ | grep -v '#!/usr/bin/env bash$'; then
echo -e "Invalid shebang! Must use '#!/usr/bin/env bash'"
FAIL=1
fi
exit $FAIL

117
scripts/lint/lint.sh Executable file
View File

@@ -0,0 +1,117 @@
#!/usr/bin/env bash
set -e
RED='\033[0;31m'
GREEN='\033[0;32m'
UNDERLINE='\033[4m'
BOLD='\033[1m'
NC='\033[0m'
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
ROOT="$DIR/../../"
cd $ROOT
FAILED=0
IGNORED_FILES="uv\.lock|docs\/CARS.md"
IGNORED_DIRS="^third_party.*|^msgq.*|^msgq_repo.*|^opendbc.*|^opendbc_repo.*|^cereal.*|^panda.*|^rednose.*|^rednose_repo.*|^tinygrad.*|^tinygrad_repo.*|^teleoprtc.*|^teleoprtc_repo.*"
function run() {
shopt -s extglob
case $1 in
$SKIP | $RUN ) return 0 ;;
esac
echo -en "$1"
for ((i=0; i<$((50 - ${#1})); i++)); do
echo -n "."
done
shift 1;
CMD="$@"
set +e
log="$((eval "$CMD" ) 2>&1)"
if [[ $? -eq 0 ]]; then
echo -e "[${GREEN}${NC}]"
else
echo -e "[${RED}${NC}]"
echo "$log"
FAILED=1
fi
set -e
}
function run_tests() {
ALL_FILES=$1
PYTHON_FILES=$2
run "ruff" ruff check $ROOT --quiet
run "check_added_large_files" python3 -m pre_commit_hooks.check_added_large_files --enforce-all $ALL_FILES --maxkb=120
run "check_shebang_scripts_are_executable" python3 -m pre_commit_hooks.check_shebang_scripts_are_executable $ALL_FILES
run "check_shebang_format" $DIR/check_shebang_format.sh $ALL_FILES
run "check_nomerge_comments" $DIR/check_nomerge_comments.sh $ALL_FILES
if [[ -z "$FAST" ]]; then
run "mypy" mypy $PYTHON_FILES
run "codespell" codespell $ALL_FILES
fi
return $FAILED
}
function help() {
echo "A fast linter"
echo ""
echo -e "${BOLD}${UNDERLINE}Usage:${NC} op lint [TESTS] [OPTIONS]"
echo ""
echo -e "${BOLD}${UNDERLINE}Tests:${NC}"
echo -e " ${BOLD}ruff${NC}"
echo -e " ${BOLD}mypy${NC}"
echo -e " ${BOLD}codespell${NC}"
echo -e " ${BOLD}check_added_large_files${NC}"
echo -e " ${BOLD}check_shebang_scripts_are_executable${NC}"
echo ""
echo -e "${BOLD}${UNDERLINE}Options:${NC}"
echo -e " ${BOLD}-f, --fast${NC}"
echo " Skip slow tests"
echo -e " ${BOLD}-s, --skip${NC}"
echo " Specify tests to skip separated by spaces"
echo ""
echo -e "${BOLD}${UNDERLINE}Examples:${NC}"
echo " op lint mypy ruff"
echo " Only run the mypy and ruff tests"
echo ""
echo " op lint --skip mypy ruff"
echo " Skip the mypy and ruff tests"
echo ""
echo " op lint"
echo " Run all the tests"
}
SKIP=""
RUN=""
while [[ $# -gt 0 ]]; do
case $1 in
-f | --fast ) shift 1; FAST="1" ;;
-s | --skip ) shift 1; SKIP=" " ;;
-h | --help | -help | --h ) help; exit 0 ;;
* ) if [[ -n $SKIP ]]; then SKIP+="$1 "; else RUN+="$1 "; fi; shift 1 ;;
esac
done
RUN=$([ -z "$RUN" ] && echo "" || echo "!($(echo $RUN | sed 's/ /|/g'))")
SKIP="@($(echo $SKIP | sed 's/ /|/g'))"
GIT_FILES="$(git ls-files | sed -E "s/$IGNORED_FILES|$IGNORED_DIRS//g")"
ALL_FILES=""
for f in $GIT_FILES; do
if [[ -f $f ]]; then
ALL_FILES+="$f"$'\n'
fi
done
PYTHON_FILES=$(echo "$ALL_FILES" | grep --color=never '.py$' || true)
run_tests "$ALL_FILES" "$PYTHON_FILES"

7
scripts/post-commit Executable file
View File

@@ -0,0 +1,7 @@
#!/usr/bin/env bash
set -e
if [[ -f .git/hooks/post-commit.d/post-commit ]]; then
.git/hooks/post-commit.d/post-commit
fi
tools/op.sh lint --fast
echo ""

27
scripts/retry.sh Executable file
View File

@@ -0,0 +1,27 @@
#!/usr/bin/env bash
function fail {
echo $1 >&2
exit 1
}
function retry {
local n=1
local max=3 # 3 retries before failure
local delay=5 # delay between retries, 5 seconds
while true; do
echo "Running command '$@' with retry, attempt $n/$max"
"$@" && break || {
if [[ $n -lt $max ]]; then
((n++))
sleep $delay;
else
fail "The command has failed after $n attempts."
fi
}
done
}
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
retry "$@"
fi

7
scripts/stop_updater.sh Executable file
View File

@@ -0,0 +1,7 @@
#!/usr/bin/env sh
# Stop updater
pkill -2 -f system.updated.updated
# Remove pending update
rm -f /data/safe_staging/finalized/.overlay_consistent

4
scripts/update_now.sh Executable file
View File

@@ -0,0 +1,4 @@
#!/usr/bin/env sh
# Send SIGHUP to updater
pkill -1 -f system.updated

33
scripts/waste.py Executable file
View File

@@ -0,0 +1,33 @@
#!/usr/bin/env python3
import os
import time
import numpy as np
from multiprocessing import Process
from setproctitle import setproctitle
def waste(core):
os.sched_setaffinity(0, [core,])
m1 = np.zeros((200, 200)) + 0.8
m2 = np.zeros((200, 200)) + 1.2
i = 1
st = time.monotonic()
j = 0
while 1:
if (i % 100) == 0:
setproctitle(f"{core:3d}: {i:8d}")
lt = time.monotonic()
print(f"{core:3d}: {i:8d} {lt-st:f} {j:.2f}")
st = lt
i += 1
j = np.sum(np.matmul(m1, m2))
def main(gctx=None):
print("1-2 seconds is baseline")
for i in range(os.cpu_count()):
p = Process(target=waste, args=(i,))
p.start()
if __name__ == "__main__":
main()