Add typed field-flight prototype and preserve native qualification failures
This commit is contained in:
@@ -21,7 +21,7 @@ class Scene:
|
||||
self.position=[98.5,0.,98.5];self.angle=0.;self.speed=0.;self.held=False
|
||||
self.key_events=[];self.turns=[];self.notifications=[];self.focus=True
|
||||
self.gain=.085;self.acceleration=8.;self.braking=8.;self.move_scale=1.
|
||||
self.guard_cost=.22;self.ignore_turn=False;self.wrong_turn=False
|
||||
self.coarse=False;self.guard_cost=.22;self.ignore_turn=False;self.wrong_turn=False
|
||||
self.turn_delay=0.;self.pending_turns=[]
|
||||
self.stale=False;self.no_movement=False;self.changed_geometry=False
|
||||
self.context_lost=False;self.fail_down=False;self.coast_events=[]
|
||||
@@ -64,7 +64,7 @@ class Scene:
|
||||
self.held=down
|
||||
if down and self.fail_down:raise OSError('uncertain native press')
|
||||
def move(self,dx,dy,guard):
|
||||
if dy != 0 or not 1<=abs(dx)<=128:raise AssertionError('motion cap')
|
||||
if dy != 0 or not 1<=abs(dx)<=(384 if self.coarse else 128):raise AssertionError('motion cap')
|
||||
guard.check();guard.check()
|
||||
self.turns.append((dx,dy,self.now))
|
||||
if not self.ignore_turn:
|
||||
@@ -78,14 +78,15 @@ class Scene:
|
||||
batch=[s for s in self.history if s['sample'] > self.last_read] or [self.sample]
|
||||
self.last_read=batch[-1]['sample']
|
||||
return copy.deepcopy(batch)
|
||||
def run(self,report=None,calibration=None):
|
||||
def run(self,report=None,calibration=None,profile="standard"):
|
||||
self.coarse=profile=="coarse"
|
||||
self.report={} if report is None else report
|
||||
angle=math.radians(24*.085)
|
||||
calibration=calibration or {'nonce':'test','completed':True,'pixels':24,
|
||||
'baseline_direction':[0.,0.,1.], 'response_direction':[math.sin(angle),0.,math.cos(angle)],
|
||||
'ready_real_ms':2000,'ready_monotonic':0.,'observer_start_real_ms':0}
|
||||
return policy.perform_approach(self,self,self,self.observe,self.geometry,calibration,self.report,
|
||||
clock=self.clock,sleep=self.sleep,notify=self.notifications.append,key_state_busy=Busy)
|
||||
clock=self.clock,sleep=self.sleep,notify=self.notifications.append,key_state_busy=Busy,profile=profile)
|
||||
|
||||
|
||||
class ApproachTests(unittest.TestCase):
|
||||
@@ -325,5 +326,59 @@ class ApproachTests(unittest.TestCase):
|
||||
self.assertEqual([event[1] for event in scene.key_events],[True,False])
|
||||
self.assertFalse(proof['completed']);self.assertFalse(scene.held)
|
||||
|
||||
def test_explicit_coarse_profile_reduces_heading_requests_with_original_clocks(self):
|
||||
fine=Scene();standard=fine.run()
|
||||
coarse=Scene();result=coarse.run(profile='coarse')
|
||||
self.assertEqual(standard['profile'],'standard');self.assertEqual(result['profile'],'coarse')
|
||||
self.assertLess(len(coarse.turns),len(fine.turns));self.assertTrue(any(abs(dx)>128 for dx,_,_ in coarse.turns))
|
||||
self.assertTrue(all(abs(dx)<=384 and dy==0 for dx,dy,_ in coarse.turns))
|
||||
self.assertLessEqual(sum(abs(dx) for dx,_,_ in coarse.turns),2048)
|
||||
self.assertLessEqual(len(coarse.turns),16);self.assertLessEqual(len(coarse.key_events)//2,16)
|
||||
self.assertEqual(result['deadline_monotonic'],35);self.assertLess(coarse.now,35)
|
||||
self.assertLessEqual(result['cumulative_horizontal_m'],6);self.assertFalse(coarse.coast_events)
|
||||
self.assertTrue(all(key=='W' for key,_,_ in coarse.key_events))
|
||||
|
||||
def test_coarse_profile_never_hides_intermediate_native_overshoot(self):
|
||||
scene=Scene()
|
||||
def turn(dx,dy,guard):
|
||||
guard.check();guard.check();scene.angle+=36;scene.sleep(.2);scene.angle-=11
|
||||
scene.move=turn
|
||||
with self.assertRaisesRegex(policy.Refused,'angle bound'):scene.run(profile='coarse')
|
||||
self.assertEqual(scene.key_events,[])
|
||||
|
||||
def test_coarse_requires_known_profile_and_respects_focus_and_deadline(self):
|
||||
for profile in ('other',True,None):
|
||||
scene=Scene()
|
||||
with self.assertRaisesRegex(policy.Refused,'Unknown approach profile'):scene.run(profile=profile)
|
||||
self.assertEqual(scene.key_events,[]);self.assertEqual(scene.turns,[])
|
||||
scene=Scene();scene.focus=False
|
||||
with self.assertRaisesRegex(policy.Refused,'focus lost'):scene.run(profile='coarse')
|
||||
self.assertEqual(scene.key_events,[])
|
||||
scene=Scene();scene.now=35
|
||||
with self.assertRaisesRegex(policy.Refused,'Insufficient'):scene.run(profile='coarse')
|
||||
self.assertEqual(scene.key_events,[]);self.assertEqual(scene.turns,[])
|
||||
|
||||
def test_coarse_keeps_actual_hold_and_stopping_deadzone(self):
|
||||
scene=Scene();original=scene.send
|
||||
def slow(key,down):
|
||||
original(key,down)
|
||||
if down:scene.guard_cost=.41
|
||||
scene.send=slow
|
||||
with self.assertRaisesRegex(policy.Refused,'400ms'):scene.run(profile='coarse')
|
||||
self.assertEqual([e[1] for e in scene.key_events],[True,False])
|
||||
scene=Scene();scene.position=[102.7,0.,97.3];scene.angle=180.;scene.guard_cost=.33
|
||||
scene.sample=scene.snapshot();scene.history=[copy.deepcopy(scene.sample)]
|
||||
with self.assertRaisesRegex(policy.Refused,'measured stopping reserve'):scene.run(profile='coarse')
|
||||
self.assertEqual([e[1] for e in scene.key_events],[True,False])
|
||||
|
||||
def test_coarse_has_exact_sixteen_pulse_cap_not_unbounded_retries(self):
|
||||
scene=Scene();scene.guard_cost=.19
|
||||
result=scene.run(profile='coarse')
|
||||
self.assertTrue(result['completed']);self.assertEqual(len(scene.key_events),32)
|
||||
self.assertLess(scene.now,35);self.assertFalse(scene.held)
|
||||
scene=Scene();scene.guard_cost=.18
|
||||
with self.assertRaisesRegex(policy.Refused,'pulse budget'):scene.run(profile='coarse')
|
||||
self.assertEqual(len(scene.key_events),32);self.assertFalse(scene.held)
|
||||
|
||||
|
||||
if __name__=='__main__':unittest.main()
|
||||
|
||||
Reference in New Issue
Block a user