mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-10-04 13:24:13 +08:00
Recognize verified passive ELM327 diagnostic startup without waiving faults
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
# Passive observer safety evidence
|
||||
|
||||
The helper accepts two exact observed variants with genuine `CarParams.passive`,
|
||||
configured noOutput/parameter0, inactive fresh control/state signals and fault-free
|
||||
pandas: all actual noOutput, or all actual ELM327/parameter1 under the documented
|
||||
passive startup conditions. It is not authorization, calibration acceptance,
|
||||
coexistence acceptance, engagement support, or proof of zero CAN traffic.
|
||||
|
||||
The earlier assumption that normal passive startup must reach actual noOutput was
|
||||
incorrect. Source in `selfdrive/pandad/panda_safety.cc` initializes every panda to
|
||||
ELM327 parameter1 before waiting for FirmwareQueryDone and ControlsReady. Upstream
|
||||
`selfdrive/car/card.py` sets ControlsReady inside controls_update, while passive
|
||||
step skips controls_update. Thus the normal passive path can remain in ELM327.
|
||||
Parameter1 means no OBD multiplexing; enabling OBD multiplexing changes the primary
|
||||
panda parameter to0. Only the explicit parameter1 baseline is accepted here.
|
||||
|
||||
The collector supplies current read-only `passive_startup` values:
|
||||
`controls_ready=false`, `firmware_query_done=true`,
|
||||
`obd_multiplexing_enabled=false`. Missing helper evidence fails closed; native
|
||||
Params absent-bool semantics are the collector's responsibility. No helper writes
|
||||
these flags or attempts to force a safety transition.
|
||||
|
||||
**ELM327 is not noOutput.** `opendbc/safety/modes/elm327.h` permits eight-byte
|
||||
messages to diagnostic addresses 0x600–0x7FF, 0x18DB33F1, 0x18DAxxF1, and0x24B.
|
||||
For0x24B it additionally restricts the leading ISO-TP nibble to0–3. Ordinary
|
||||
allowed diagnostic addresses accept arbitrary payload content; this must not be
|
||||
described as an all-TX-disabled safety mode. The mode uses nooutput_init and
|
||||
requires observed controlsAllowed=false, but has its own diagnostic TX hook.
|
||||
RoadScore's observer integration must continue to publish no CAN/control messages.
|
||||
|
||||
Faults remain an independent blocker, including interruptRateCan2. ELM allowance
|
||||
does not waive safetyRxChecksInvalid, stale/invalid messages, active/always-on
|
||||
lateral flags, calibration, model health, local-model placement, resource/link
|
||||
bounds or parked coexistence. Mixed observed safety modes are rejected as a
|
||||
transition/unestablished baseline. Mode identity includes actual ELM parameter1,
|
||||
so a noOutput authorization pin cannot silently carry over to ELM.
|
||||
@@ -41,9 +41,19 @@ def evaluate_passive_observer(snapshot, requested_mode):
|
||||
pandas=snapshot.get('pandas')
|
||||
if not isinstance(pandas,list) or not pandas:
|
||||
reasons.append('Actual panda safety evidence is missing');pandas=[]
|
||||
actual_modes={p.get('safetyModel') for p in pandas if isinstance(p,dict)}
|
||||
elm=actual_modes=={'elm327'}
|
||||
if len(actual_modes)>1:reasons.append('Mixed panda startup safety modes are not an established passive baseline')
|
||||
if elm:
|
||||
startup=_mapping(snapshot.get('passive_startup'))
|
||||
if startup.get('controls_ready') is not False:reasons.append('ELM passive baseline requires actual ControlsReady=false')
|
||||
if startup.get('firmware_query_done') is not True:reasons.append('ELM passive baseline requires completed firmware query')
|
||||
if startup.get('obd_multiplexing_enabled') is not False:reasons.append('ELM parameter-1 baseline requires OBD multiplexing disabled')
|
||||
for index,panda in enumerate(pandas):
|
||||
if not isinstance(panda,dict):reasons.append(f'Panda {index} evidence is malformed');continue
|
||||
if panda.get('safetyModel')!='noOutput':reasons.append(f'Panda {index} is not actually noOutput')
|
||||
if panda.get('safetyModel') not in ('noOutput','elm327'):reasons.append(f'Panda {index} is not in a supported passive safety mode')
|
||||
if panda.get('safetyModel')=='elm327' and (type(panda.get('safetyParam')) is not int or panda['safetyParam']!=1):
|
||||
reasons.append(f'Panda {index} is not the expected ELM327 parameter-1 startup state')
|
||||
if panda.get('controlsAllowed') is not False:reasons.append(f'Panda {index} permits or has unknown actuation')
|
||||
if panda.get('faults')!=[]:reasons.append(f'Panda {index} fault state is not explicitly clear')
|
||||
if panda.get('safetyRxChecksInvalid') is not False:reasons.append(f'Panda {index} safety receive checks are invalid or unknown')
|
||||
@@ -60,10 +70,10 @@ def evaluate_passive_observer(snapshot, requested_mode):
|
||||
# baseline hashes. Runtime freshness/booleans are validated above, not hashed.
|
||||
material={'mode':MODE,'passive':cp.get('passive'),'notCar':cp.get('notCar'),
|
||||
'dashcamOnly':cp.get('dashcamOnly'),'configured_safety':configs,
|
||||
'actual_panda_safety':[p.get('safetyModel') if isinstance(p,dict) else None for p in pandas]}
|
||||
'actual_panda_safety':[{'model':p.get('safetyModel'),'parameter':p.get('safetyParam') if p.get('safetyModel')=='elm327' else 0} if isinstance(p,dict) else None for p in pandas]}
|
||||
mode_identity=hashlib.sha256(json.dumps(material,sort_keys=True,separators=(',',':')).encode()).hexdigest() if not reasons else None
|
||||
return {'mode':MODE,'safety_eligible':not reasons,'reasons':reasons,'mode_identity':mode_identity,
|
||||
'engagement_available':False,'label':'Passive observer — vehicle engagement unavailable'}
|
||||
'engagement_available':False,'diagnostic_tx_possible':elm,'actual_passive_variant':'elm327-diagnostic' if elm else 'noOutput','label':'Passive observer — vehicle engagement unavailable'}
|
||||
|
||||
|
||||
def authorization_mode_matches(record, assessment):
|
||||
|
||||
@@ -69,3 +69,38 @@ def test_explicit_mode_no_auto_inference_and_input_unchanged():
|
||||
def test_malformed_fields_fail_closed(field):
|
||||
snapshot=healthy();snapshot[field]=None
|
||||
assert not evaluate_passive_observer(snapshot,MODE)['safety_eligible']
|
||||
|
||||
|
||||
def elm_baseline():
|
||||
snapshot=healthy();snapshot['pandas'][0].update(safetyModel='elm327',safetyParam=1)
|
||||
snapshot['passive_startup']={'controls_ready':False,'firmware_query_done':True,'obd_multiplexing_enabled':False}
|
||||
return snapshot
|
||||
|
||||
|
||||
def test_exact_upstream_passive_elm_state_is_diagnostic_not_tx_disabled():
|
||||
result=evaluate_passive_observer(elm_baseline(),MODE)
|
||||
assert result['safety_eligible'] and result['diagnostic_tx_possible']
|
||||
assert result['actual_passive_variant']=='elm327-diagnostic'
|
||||
assert not result['engagement_available']
|
||||
nooutput=evaluate_passive_observer(healthy(),MODE)
|
||||
assert result['mode_identity']!=nooutput['mode_identity']
|
||||
|
||||
@pytest.mark.parametrize('field,value',[('controls_ready',True),('controls_ready',None),('firmware_query_done',False),('firmware_query_done',None),('obd_multiplexing_enabled',True),('obd_multiplexing_enabled',None)])
|
||||
def test_elm_requires_exact_observed_startup_conditions(field,value):
|
||||
snapshot=elm_baseline();snapshot['passive_startup'][field]=value
|
||||
assert not evaluate_passive_observer(snapshot,MODE)['safety_eligible']
|
||||
|
||||
@pytest.mark.parametrize('parameter',(0,2,None,True,'1'))
|
||||
def test_elm_parameter_mismatch_rejected(parameter):
|
||||
snapshot=elm_baseline();snapshot['pandas'][0]['safetyParam']=parameter
|
||||
assert not evaluate_passive_observer(snapshot,MODE)['safety_eligible']
|
||||
|
||||
|
||||
def test_elm_does_not_waive_faults_or_active_flags():
|
||||
for field,value in [('faults',['interruptRateCan2']),('controlsAllowed',True),('safetyRxChecksInvalid',True)]:
|
||||
snapshot=elm_baseline();snapshot['pandas'][0][field]=value
|
||||
assert not evaluate_passive_observer(snapshot,MODE)['safety_eligible']
|
||||
snapshot=elm_baseline();snapshot['control']['latActive']=True
|
||||
assert not evaluate_passive_observer(snapshot,MODE)['safety_eligible']
|
||||
snapshot=elm_baseline();snapshot['pandas'].append(deepcopy(healthy()['pandas'][0]))
|
||||
assert not evaluate_passive_observer(snapshot,MODE)['safety_eligible']
|
||||
|
||||
Reference in New Issue
Block a user