84 lines
7.8 KiB
Python
84 lines
7.8 KiB
Python
"""Independent yaw alignment and exact native lock-selection verification."""
|
|
import math
|
|
import re
|
|
from ordinary_look_evidence import validate_look_evidence
|
|
from ordinary_gate_evidence import join_gate_snapshots, validate_gate_geometry
|
|
from prison_approach_evidence import validate_approach_evidence, need, number, horizontal, norm, yaw, angle, neutral
|
|
|
|
|
|
def validate_alignment_evidence(content, nonce, look_proof, approach_proof, proof):
|
|
result={'verified':False,'errors':[],'scope':'stationary yaw-only alignment and native selection of the original prison lock after a verified interior approach; no F hold, unlock, exit or multiplayer qualification','held_action_verified':False,'escape_verified':False}
|
|
try:
|
|
approach=validate_approach_evidence(content,nonce,look_proof,approach_proof)
|
|
need(approach['verified'],'prior native approach is unverified: '+str(approach['errors']))
|
|
calibration=validate_look_evidence(content,nonce,look_proof)
|
|
need(calibration['verified'],'same-run native calibration is unverified: '+str(calibration['errors']))
|
|
gain=calibration['response_yaw_degrees']/24
|
|
need(.01<=abs(gain)<=.5,'native calibration gain outside alignment range')
|
|
need(type(proof) is dict and not proof.get('events') and proof.get('requested') is True and proof.get('completed') is True and proof.get('nonce')==nonce and not proof.get('error'),'alignment did not complete under this nonce')
|
|
start=number(proof['started_monotonic']);finish=number(proof['finished_monotonic']);deadline=number(proof['deadline_monotonic'])
|
|
need(approach_proof['finished_monotonic']<=start<finish<=deadline<=start+8,'alignment helper interval outside original stage bound')
|
|
data=join_gate_snapshots(content,nonce);samples=data['snapshots'];first=proof['initial_sample'];last=proof['final_sample'];route_end=approach['last_sample']
|
|
need(type(first) is int and type(last) is int and route_end<=first<last and first in samples and last in samples,'native alignment references invalid')
|
|
geo=validate_gate_geometry(content,nonce,route_end,last)
|
|
need(geo['verified'],'native alignment geometry invalid: '+str(geo['errors']))
|
|
queued=re.findall(r'ERR INPUT OBSERVE: run='+re.escape(nonce)+r' sample=0 real=(\d+) world=[^ ]+ QUEUED(?:\r?$)',content,re.MULTILINE)
|
|
need(len(queued)==1,'original native QUEUED clock missing or ambiguous')
|
|
queued=int(queued[0]);ready=data['ready']['real_ms']
|
|
original_host_end=number(approach_proof['deadline_monotonic'])-35+(45000-(ready-queued))/1000-.5
|
|
need(deadline<=original_host_end,'alignment deadline renews original observation lifetime')
|
|
reference=samples[route_end]['input']['origin'];last_real=None
|
|
for index in range(route_end,last+1):
|
|
need(index in samples,'missing native interstage/alignment observation')
|
|
s=samples[index];g=s['geometry'];a=s['action']
|
|
need(neutral(s) and math.dist(reference,s['input']['origin'])<=.05,'body moved during stationary alignment')
|
|
need(queued<=s['real_ms']<queued+45000,'original native observation lifetime exceeded')
|
|
need(g['door_state']==0 and g['door_control']==0 and a['pressed']==0 and not a['performing'] and a['progress']==0,'gate operated during alignment-only observation')
|
|
if last_real is not None:need(0<s['real_ms']-last_real<=500,'native alignment sample gap')
|
|
last_real=s['real_ms']
|
|
refs=proof['samples'];need(type(refs) is list and 2<=len(refs)<=450,'bounded alignment references missing')
|
|
ids=[r['sample'] for r in refs];need(all(type(i) is int for i in ids) and ids==list(range(first,last+1)),'alignment references not contiguous')
|
|
receipts={};prior=0
|
|
for r in refs:
|
|
i=r['sample'];received=number(r['received_monotonic'])
|
|
need(type(r['real_ms']) is int and r['real_ms']==samples[i]['real_ms'] and prior<=received<=finish,'alignment native/receipt clocks disagree')
|
|
receipts[i]=received;prior=received
|
|
need(0<=finish-receipts[last]<=.5,'final native alignment observation is stale')
|
|
# Historical samples bridge the verified approach into the stage; they
|
|
# cannot be made newly received or consume a fictitious renewed lease.
|
|
current=[i for i in ids if receipts[i]>=start]
|
|
native_begin=current[0] if current else first
|
|
need(samples[last]['real_ms']-samples[native_begin]['real_ms']<=8000,'current native alignment interval exceeds eight seconds')
|
|
def heading(s):
|
|
eye=s['look']['camera'];handle=s['geometry']['context']
|
|
need(.25<=horizontal(eye,handle)<=5,'native handle bearing is degenerate or out of scope')
|
|
return math.degrees(math.atan2(handle[0]-eye[0],handle[2]-eye[2]))
|
|
motions=proof['motions'];need(type(motions) is list and len(motions)<=6,'too many alignment motions')
|
|
old_index=first;old_time=start;pixel_sum=0
|
|
for motion in motions:
|
|
dx=motion['dx'];at=number(motion['monotonic']);before=motion['after_sample'];after=motion['response_sample']
|
|
need(type(dx) is int and 1<=abs(dx)<=128 and type(motion['dy']) is int and motion['dy']==0,'alignment motion is not bounded horizontal input')
|
|
need(type(before) is int and type(after) is int and old_index<=before<after<=last,'alignment motion references reversed')
|
|
need(old_time<=at<=finish and before in receipts and after in receipts and receipts[before]<=at<=receipts[after] and at-receipts[before]<=.5 and receipts[after]-at<=1.5,'alignment input timestamps contradict native samples')
|
|
baseline=yaw(samples[before]['look']['direction']);wanted=angle(heading(samples[before]),baseline)
|
|
change=angle(yaw(samples[after]['look']['direction']),baseline)
|
|
need(abs(wanted)>3 and dx*gain*wanted>0 and .05<=abs(change)<=15 and change*dx*gain>0,'native turn did not follow same-run signed calibration toward handle')
|
|
for i in range(before,after+1):
|
|
d=angle(yaw(samples[i]['look']['direction']),baseline)
|
|
need(abs(d)<=15 and (abs(d)<.05 or d*dx*gain>0),'intermediate native turn reversed or exceeded bound')
|
|
need(after-1>=before and 100<=samples[after]['real_ms']-samples[after-1]['real_ms']<=500 and abs(angle(yaw(samples[after]['look']['direction']),yaw(samples[after-1]['look']['direction'])))<=.2,'native turn response did not settle over consecutive samples')
|
|
need(samples[after]['real_ms']-samples[before]['real_ms']<=2500,'native turn response interval is unbounded')
|
|
old_index=after;old_time=at;pixel_sum+=abs(dx)
|
|
need(pixel_sum<=768,'alignment pixel budget exceeded')
|
|
pair=proof['selection_samples'];need(type(pair) is list and len(pair)==2 and all(type(i) is int for i in pair) and first<=pair[0] and pair[1]==pair[0]+1 and pair[1]<=last,'selection lacks consecutive references')
|
|
one,two=(samples[i] for i in pair)
|
|
need(100<=two['real_ms']-one['real_ms']<=500 and abs(angle(yaw(two['look']['direction']),yaw(one['look']['direction'])))<=.2,'selection view did not settle')
|
|
for i in range(pair[0],last+1):
|
|
s=samples[i];a=s['action']
|
|
need(a['selected'] and a['context_selected'] and a['cached_performable'] and a['progress_bound'] and a['duration']==4,'original native lock/context not selected and bound')
|
|
need(abs(angle(heading(s),yaw(s['look']['direction'])))<=3,'view not aligned to actual handle bearing')
|
|
result.update(verified=True,first_sample=first,last_sample=last,selection_samples=pair,turn_count=len(motions),native_duration_ms=samples[last]['real_ms']-samples[first]['real_ms'],final_yaw_error_degrees=abs(angle(heading(samples[last]),yaw(samples[last]['look']['direction']))))
|
|
except (ValueError,KeyError,TypeError,OverflowError,IndexError) as error:
|
|
result['errors'].append(str(error))
|
|
return result
|