mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-09-11 10:43:46 +08:00
Tesla: clarify Galaxy-only validation
Keep the wake-on-CAN test documentation aligned with the Galaxy-only integration. Co-authored-by: AngusBell97 <124716116+AngusBell97@users.noreply.github.com>
This commit is contained in:
committed by
firestar5683
parent
9832c3de4f
commit
58d2b6838d
@@ -55,8 +55,8 @@ lacks the extra non-OFF wake; optional `--regression-ref REV` can test a histori
|
||||
version that masked the DRIVE edge if available locally.
|
||||
|
||||
Host tests cover Tesla detection, stale identity, firmware-selection combinations,
|
||||
missing files and normal update/signature behaviour. Galaxy/native settings tests
|
||||
cover Tesla-only visibility and capability checks, parked-only writes, required
|
||||
missing files and normal update/signature behaviour. Galaxy settings tests cover
|
||||
Tesla-only visibility and capability checks, parked-only writes, required
|
||||
confirmation, cancellation and firmware preflight. `host_policy.py` exercises
|
||||
actual shutdown methods with synthetic clocks and Params, without device access.
|
||||
|
||||
|
||||
@@ -34,7 +34,6 @@ def main():
|
||||
pm = SimpleNamespace(low_voltage_start_time=None,car_voltage_mV=14000,car_battery_capacity_uWh=30e6,
|
||||
params=SimpleNamespace(get_bool=lambda name:values[name]))
|
||||
toggles = SimpleNamespace(device_shutdown_time=3600,low_voltage_shutdown=11.8)
|
||||
# Wake is deliberately independent, but not a supported input to this policy.
|
||||
pm.wake_on_can = True
|
||||
reason = lambda: ns['shutdown_reason'](pm,False,True,1.,False,toggles)
|
||||
observed = {}
|
||||
@@ -44,12 +43,9 @@ def main():
|
||||
if result and 'first_shutdown' not in observed:
|
||||
observed['first_shutdown']={'monotonic_s':t,'offroad_s':t-1,'reason':result,'fresh_awake':True}
|
||||
assert observed['first_shutdown']=={'monotonic_s':3602,'offroad_s':3601,'reason':'offroad_timeout','fresh_awake':True}
|
||||
# The one-hour constant is NOT an unconditional one-hour timeout.
|
||||
toggles.device_shutdown_time=0
|
||||
assert reason() is None
|
||||
observed['no_timeout_healthy_after_hour']=reason()
|
||||
# Existing emergency/protective inputs stay intact; never bypass by changing ignition
|
||||
# or setting DisablePowerDown to implement stay-awake.
|
||||
pm.car_battery_capacity_uWh=0
|
||||
assert reason()=='battery_capacity_exhausted'
|
||||
observed['exhausted']=reason()
|
||||
@@ -64,7 +60,7 @@ def main():
|
||||
toggles.device_shutdown_time=3600
|
||||
for awake in [True,False]:
|
||||
pm.wake_on_can=awake
|
||||
assert reason()=='offroad_timeout' # same answer; no telemetry seam exists yet
|
||||
assert reason()=='offroad_timeout'
|
||||
observed['contract']='Wake-only CAN deliberately does not inhibit stock host shutdown.'
|
||||
observed['source_sha256']=hashlib.sha256(path.read_bytes()).hexdigest()
|
||||
observed['thermal']='Not modeled here; unchanged host thermal protection must be independently preserved in any extension.'
|
||||
|
||||
@@ -27,11 +27,9 @@ def between(text, start, end):
|
||||
def generate(read):
|
||||
can, main, boot, cuatro = [read(p) for p in FILES]
|
||||
independent_wake = 'bool recent_heartbeat, bool wake)' in boot
|
||||
# Historical two-argument definitions must not include today's declaration.
|
||||
if not independent_wake:
|
||||
boot = boot.replace('#include "bootkick_declarations.h"', '')
|
||||
globals_ = between(can, 'bool ignition_can = false;', '\nbool can_silent')
|
||||
# Added wake globals immediately precede ignition globals in candidate.
|
||||
if 'bool wake_on_can = false;' in can:
|
||||
globals_ = between(can, 'bool wake_on_can = false;', '\nbool can_silent')
|
||||
else:
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
// Included after verbatim production functions. Each scenario gets a fresh process.
|
||||
static void tick(void) { timer.SR = 1U; tick_handler(); }
|
||||
static void second(void) { for (int i=0; i<8; i++) tick(); }
|
||||
static CANPacket_t packet(unsigned bus, unsigned addr, unsigned dlc, unsigned state, unsigned counter) {
|
||||
CANPacket_t p = {0}; p.bus=bus; p.addr=addr; p.data_len_code=dlc;
|
||||
p.data[0]=state<<5; p.data[6]=counter<<4;
|
||||
// Tesla DBC checksum: address bytes plus seven payload bytes, modulo 256.
|
||||
unsigned sum=(addr & 255U)+(addr >> 8U);
|
||||
for (unsigned i=0; i<7; i++) sum+=p.data[i];
|
||||
p.data[7]=sum & 255U;
|
||||
@@ -16,13 +14,13 @@ static void tesla(unsigned state, unsigned counter) {
|
||||
static void pair(unsigned state) { tesla(state,14); tesla(state,15); }
|
||||
static void wake_case(void) {
|
||||
assert(!wake_on_can && !ignition_can);
|
||||
tesla(2,14); assert(!wake_on_can && !ignition_can); // prime only
|
||||
tesla(2,14); assert(!wake_on_can && !ignition_can);
|
||||
for (unsigned state=0; state<4; state++) {
|
||||
tesla(state,(15+state)%16);
|
||||
assert(wake_on_can == (state != 0)); assert(ignition_can == (state == 3));
|
||||
assert(wake_on_can_cnt == 0 && ignition_can_cnt == 0);
|
||||
}
|
||||
tesla(0,3); assert(!wake_on_can && !ignition_can); // accepted OFF clears immediately
|
||||
tesla(0,3); assert(!wake_on_can && !ignition_can);
|
||||
}
|
||||
static void invalid_case(void) {
|
||||
pair(2); assert(wake_on_can); wake_on_can_cnt=2;
|
||||
@@ -37,32 +35,31 @@ static void invalid_case(void) {
|
||||
}
|
||||
CANPacket_t p=packet(0,0x220,8,0,0); ignition_can_hook(&p);
|
||||
assert(wake_on_can && wake_on_can_cnt==2);
|
||||
tesla(0,15); assert(wake_on_can && wake_on_can_cnt==2); // duplicate
|
||||
tesla(0,7); assert(wake_on_can && wake_on_can_cnt==2); // jump
|
||||
tesla(0,8); assert(!wake_on_can && wake_on_can_cnt==0); // reacquire after jump
|
||||
tesla(0,15); assert(wake_on_can && wake_on_can_cnt==2);
|
||||
tesla(0,7); assert(wake_on_can && wake_on_can_cnt==2);
|
||||
tesla(0,8); assert(!wake_on_can && wake_on_can_cnt==0);
|
||||
tesla(2,9); assert(wake_on_can);
|
||||
wake_on_can_cnt=2;
|
||||
p=packet(0,0x221,8,2,10); p.data[7]^=1; ignition_can_hook(&p);
|
||||
assert(wake_on_can && wake_on_can_cnt==2); // invalid frames cannot refresh wake
|
||||
tesla(0,11); assert(wake_on_can && wake_on_can_cnt==2); // valid frame primes again
|
||||
assert(wake_on_can && wake_on_can_cnt==2);
|
||||
tesla(0,11); assert(wake_on_can && wake_on_can_cnt==2);
|
||||
tesla(0,12); assert(!wake_on_can && wake_on_can_cnt==0);
|
||||
}
|
||||
|
||||
static void disabled_case(void) {
|
||||
for (unsigned state=0; state<4; state++) {
|
||||
pair(state);
|
||||
assert(!wake_on_can); // default and other manufacturers never gain Tesla wake
|
||||
assert(ignition_can == (state==3)); // stock DRIVE remains independent of opt-in
|
||||
assert(!wake_on_can);
|
||||
assert(ignition_can == (state==3));
|
||||
}
|
||||
}
|
||||
static void checksum_case(void) {
|
||||
CANPacket_t p=packet(0,0x221,8,2,14);
|
||||
assert(p.data[7]==0x43); // address 0x23 + ACCESSORY 0x40 + counter 0xE0
|
||||
assert(p.data[7]==0x43);
|
||||
p.data[7]^=1; ignition_can_hook(&p);
|
||||
tesla(2,15); assert(!wake_on_can); // bad checksum cannot prime a wake
|
||||
tesla(2,0); assert(wake_on_can); // two valid consecutive frames, including wrap
|
||||
tesla(2,15); assert(!wake_on_can);
|
||||
tesla(2,0); assert(wake_on_can);
|
||||
tesla(0,1); assert(!wake_on_can);
|
||||
// Corruption in any payload/checksum byte cannot create a wake or prime it.
|
||||
for (unsigned i=0; i<8; i++) {
|
||||
p=packet(0,0x221,8,2,2); p.data[i]^=1; ignition_can_hook(&p);
|
||||
assert(!wake_on_can);
|
||||
@@ -78,8 +75,7 @@ static void checksum_case(void) {
|
||||
p=packet(0,0x221,8,2,8+i); p.data[7]^=1; ignition_can_hook(&p);
|
||||
second();
|
||||
}
|
||||
assert(!wake_on_can); // checksum-invalid traffic ages out
|
||||
// Do not change the stock DRIVE decoder while hardening the extra wake path.
|
||||
assert(!wake_on_can);
|
||||
p=packet(0,0x221,8,3,12); p.data[7]^=1; ignition_can_hook(&p);
|
||||
assert(ignition_can && !wake_on_can);
|
||||
}
|
||||
@@ -88,9 +84,8 @@ static void stale_case(void) {
|
||||
pair(2);
|
||||
for (unsigned i=1; i<=3; i++) { second(); assert(wake_on_can && wake_on_can_cnt==i); }
|
||||
second(); assert(!wake_on_can && wake_on_can_cnt==4);
|
||||
// Staleness does not reset the decoder's static previous counter (reference parity).
|
||||
tesla(2,15); assert(!wake_on_can && wake_on_can_cnt==4);
|
||||
tesla(2,0); assert(wake_on_can && wake_on_can_cnt==0); // wrap after stale
|
||||
tesla(2,0); assert(wake_on_can && wake_on_can_cnt==0);
|
||||
second(); assert(wake_on_can_cnt==1);
|
||||
tesla(0,1); assert(!wake_on_can && wake_on_can_cnt==0);
|
||||
wake_on_can_cnt=UINT32_MAX; second(); assert(!wake_on_can && wake_on_can_cnt==0);
|
||||
@@ -113,19 +108,19 @@ static void drive_watchdog_case(void) {
|
||||
tesla(3,4); second(); assert(heartbeat_counter==5 && heartbeat_lost);
|
||||
}
|
||||
static void boot_case(void) {
|
||||
legacy_bootkick(false,false); assert(observed_boot==BOOT_BOOTKICK); // first power-on
|
||||
legacy_bootkick(false,false); assert(observed_boot==BOOT_BOOTKICK);
|
||||
for (int i=0; i<30; i++) legacy_bootkick(false,false);
|
||||
assert(!bootkick_reset_triggered); // no first-boot reset
|
||||
assert(!bootkick_reset_triggered);
|
||||
legacy_bootkick(false,true); assert(observed_boot==BOOT_STANDBY);
|
||||
legacy_bootkick(true,false); assert(observed_boot==BOOT_BOOTKICK);
|
||||
for (int i=0; i<18; i++) { legacy_bootkick(true,false); assert(observed_boot==BOOT_BOOTKICK); }
|
||||
legacy_bootkick(true,false); assert(observed_boot==BOOT_RESET && bootkick_reset_triggered);
|
||||
assert(gpio_level); // Cuatro RESET is deasserted bootkick, NOT a separate reset pin
|
||||
assert(gpio_level);
|
||||
for (int i=0; i<4; i++) { legacy_bootkick(true,false); assert(observed_boot==BOOT_RESET); }
|
||||
legacy_bootkick(true,false); assert(observed_boot==BOOT_BOOTKICK && !gpio_level);
|
||||
legacy_bootkick(false,true); legacy_bootkick(true,false);
|
||||
for (int i=0; i<30; i++) legacy_bootkick(true,false);
|
||||
assert(observed_boot==BOOT_BOOTKICK); // only one reset per MCU boot
|
||||
assert(observed_boot==BOOT_BOOTKICK);
|
||||
}
|
||||
static void cancel_reset_case(int serial) {
|
||||
legacy_bootkick(false,true); legacy_bootkick(true,false);
|
||||
@@ -135,7 +130,6 @@ static void cancel_reset_case(int serial) {
|
||||
assert(!bootkick_reset_triggered && observed_boot==BOOT_BOOTKICK);
|
||||
}
|
||||
static void existing_case(void) {
|
||||
// Harness insertion still wakes with ignition false.
|
||||
legacy_bootkick(false,true); harness.status=1;
|
||||
legacy_bootkick(false,false); assert(observed_boot==BOOT_BOOTKICK);
|
||||
legacy_bootkick(false,true); assert(observed_boot==BOOT_STANDBY);
|
||||
@@ -156,17 +150,15 @@ static void other_cars_case(void) {
|
||||
gm_remote_start_boots_comma=true;
|
||||
p=packet(0,0xC9,8,0,0); p.data[6]=0x10; ignition_can_hook(&p); assert(ignition_can);
|
||||
p.data[6]=0; ignition_can_hook(&p); assert(!ignition_can);
|
||||
// Pre-AP checksum and counter remain active.
|
||||
p=packet(0,0x101,3,0,0); p.data[0]=8; p.data[1]=14; p.data[2]=24; ignition_can_hook(&p);
|
||||
assert(!ignition_can); p.data[1]=15; p.data[2]=25; ignition_can_hook(&p); assert(ignition_can);
|
||||
p.data[0]=0; p.data[1]=0; p.data[2]=99; ignition_can_hook(&p); assert(ignition_can);
|
||||
p.data[1]=1; p.data[2]=3; ignition_can_hook(&p); assert(!ignition_can);
|
||||
// Rivian sequential modulo-15, then Mazda.
|
||||
p=packet(0,0x152,8,0,0); p.data[1]=14; p.data[7]=0x10; ignition_can_hook(&p);
|
||||
assert(!ignition_can); p.data[1]=0; ignition_can_hook(&p); assert(ignition_can);
|
||||
p=packet(0,0x9E,8,0,0); ignition_can_hook(&p); assert(!ignition_can);
|
||||
p.data[0]=0xC0; ignition_can_hook(&p); assert(ignition_can);
|
||||
assert(!wake_on_can); // none of these set Tesla-only wake flag
|
||||
assert(!wake_on_can);
|
||||
#ifdef PANDA_HKG_REMOTE_START
|
||||
ignition_can=false; legacy_bootkick(false,true);
|
||||
p=packet(1,0x384,8,0,0); p.data[3]=1; ignition_can_hook(&p);
|
||||
@@ -177,26 +169,23 @@ static void other_cars_case(void) {
|
||||
#endif
|
||||
}
|
||||
static void drive_edge_case(void) {
|
||||
// Prolonged awake, SOM shuts down while fresh ACCESSORY CAN continues.
|
||||
pair(2); second(); heartbeat_counter=0; second(); assert(observed_boot==BOOT_STANDBY);
|
||||
for (unsigned i=0; i<3700; i++) { tesla(2,i%16); second(); }
|
||||
assert(wake_on_can && !ignition_can && observed_boot==BOOT_STANDBY);
|
||||
tesla(3,4); second(); assert(ignition_can && wake_on_can);
|
||||
assert(observed_boot==BOOT_BOOTKICK); // Stock DRIVE edge must survive held wake.
|
||||
assert(observed_boot==BOOT_BOOTKICK);
|
||||
puts("PASS: fresh ACCESSORY through SOM shutdown preserves later DRIVE bootkick");
|
||||
tesla(0,5); second(); tesla(2,6); second();
|
||||
assert(observed_boot==BOOT_BOOTKICK); // true low->high recovers
|
||||
assert(observed_boot==BOOT_BOOTKICK);
|
||||
heartbeat_counter=0; second(); assert(observed_boot==BOOT_STANDBY);
|
||||
// Car sleep / absent or rejected traffic must age wake out and permit re-wake.
|
||||
for (unsigned i=0; i<5; i++) {
|
||||
tesla(2,6); // repeated counter cannot keep the wake fresh
|
||||
tesla(2,6);
|
||||
second();
|
||||
}
|
||||
assert(!wake_on_can && !ignition_can && observed_boot==BOOT_STANDBY);
|
||||
tesla(2,7); second(); assert(wake_on_can && observed_boot==BOOT_BOOTKICK);
|
||||
}
|
||||
static void stock_drive_case(void) {
|
||||
// This exact physical-input sequence passes both stock and candidate.
|
||||
pair(2); second(); heartbeat_counter=0; second();
|
||||
for (unsigned i=0; i<3700; i++) { tesla(2,i%16); second(); }
|
||||
assert(!ignition_can && observed_boot==BOOT_STANDBY);
|
||||
@@ -205,25 +194,24 @@ static void stock_drive_case(void) {
|
||||
}
|
||||
static void independent_edges_case(void) {
|
||||
test_bootkick(false,true,false); assert(observed_boot==BOOT_STANDBY);
|
||||
test_bootkick(false,true,true); assert(observed_boot==BOOT_BOOTKICK); // edge beats heartbeat
|
||||
test_bootkick(false,true,true); assert(observed_boot==BOOT_BOOTKICK);
|
||||
test_bootkick(false,true,true); assert(observed_boot==BOOT_STANDBY);
|
||||
test_bootkick(false,false,true); assert(observed_boot==BOOT_STANDBY); // held wake is not level-triggered
|
||||
test_bootkick(true,true,true); assert(observed_boot==BOOT_BOOTKICK); // independent ignition edge
|
||||
test_bootkick(false,false,true); assert(observed_boot==BOOT_STANDBY);
|
||||
test_bootkick(true,true,true); assert(observed_boot==BOOT_BOOTKICK);
|
||||
test_bootkick(true,true,false); assert(observed_boot==BOOT_STANDBY);
|
||||
test_bootkick(true,false,true); assert(observed_boot==BOOT_STANDBY); // no new wake kick while started
|
||||
test_bootkick(false,false,true); assert(observed_boot==BOOT_STANDBY); // falling ignition is not wake edge
|
||||
test_bootkick(true,false,true); assert(observed_boot==BOOT_STANDBY);
|
||||
test_bootkick(false,false,true); assert(observed_boot==BOOT_STANDBY);
|
||||
test_bootkick(false,false,false); assert(observed_boot==BOOT_STANDBY);
|
||||
test_bootkick(false,false,true); assert(observed_boot==BOOT_BOOTKICK);
|
||||
test_bootkick(false,true,true); assert(observed_boot==BOOT_STANDBY);
|
||||
harness.status=1;
|
||||
test_bootkick(false,true,true); assert(observed_boot==BOOT_BOOTKICK); // harness still beats heartbeat
|
||||
test_bootkick(false,true,true); assert(observed_boot==BOOT_BOOTKICK);
|
||||
}
|
||||
static void wake_reset_case(void) {
|
||||
test_bootkick(false,true,false);
|
||||
test_bootkick(false,false,true);
|
||||
for (int i=0; i<18; i++) { test_bootkick(false,false,true); assert(observed_boot==BOOT_BOOTKICK); }
|
||||
test_bootkick(false,false,true); assert(observed_boot==BOOT_RESET && bootkick_reset_triggered);
|
||||
// Ignition, wake, harness and heartbeat cannot interrupt the active reset.
|
||||
for (int i=0; i<4; i++) {
|
||||
harness.status=(i%2)+1;
|
||||
test_bootkick(i%2,true,i%2); assert(observed_boot==BOOT_RESET);
|
||||
@@ -231,7 +219,7 @@ static void wake_reset_case(void) {
|
||||
test_bootkick(true,false,true); assert(observed_boot==BOOT_BOOTKICK);
|
||||
test_bootkick(false,true,false); test_bootkick(false,false,true);
|
||||
for (int i=0; i<30; i++) test_bootkick(false,false,true);
|
||||
assert(observed_boot==BOOT_BOOTKICK); // no second reset
|
||||
assert(observed_boot==BOOT_BOOTKICK);
|
||||
}
|
||||
static void boot_trace_case(void) {
|
||||
uint32_t rng=0x87654321;
|
||||
|
||||
Reference in New Issue
Block a user