RFTSystems commited on
Commit
023f37f
·
verified ·
1 Parent(s): 342cf66

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +963 -0
app.py ADDED
@@ -0,0 +1,963 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import time
3
+ import math
4
+ import json
5
+ import numpy as np
6
+ import pandas as pd
7
+ import matplotlib.pyplot as plt
8
+ import gradio as gr
9
+
10
+ # ===============================================================
11
+ # Rendered Frame Theory (RFT) — Agent Console (All-in-One Space)
12
+ # Author: Liam Grinstead
13
+ # Purpose: Transparent, reproducible, benchmarkable agent demos
14
+ # Dependencies: numpy, pandas, matplotlib, gradio (NO scipy)
15
+ # ===============================================================
16
+
17
+ OUTDIR = "outputs"
18
+ os.makedirs(OUTDIR, exist_ok=True)
19
+
20
+ # -----------------------------
21
+ # Shared utilities
22
+ # -----------------------------
23
+ def set_seed(seed: int):
24
+ np.random.seed(int(seed) % (2**32 - 1))
25
+
26
+ def clamp(x, lo, hi):
27
+ return max(lo, min(hi, x))
28
+
29
+ def save_plot(fig, name: str):
30
+ path = os.path.join(OUTDIR, name)
31
+ fig.savefig(path, dpi=150, bbox_inches="tight")
32
+ plt.close(fig)
33
+ return path
34
+
35
+ def df_to_csv_file(df: pd.DataFrame, name: str):
36
+ path = os.path.join(OUTDIR, name)
37
+ df.to_csv(path, index=False)
38
+ return path
39
+
40
+ # -----------------------------
41
+ # RFT Core: τ_eff + gating
42
+ # -----------------------------
43
+ def tau_eff_adaptive(uncertainty: float,
44
+ base: float = 1.0,
45
+ slow_by: float = 1.0,
46
+ gain: float = 1.2,
47
+ cap: float = 4.0):
48
+ """
49
+ τ_eff here is implemented as a timing/decision delay modifier.
50
+ - base: baseline τ_eff
51
+ - slow_by: explicit "slow by 1.0" style term (user requested this behaviour)
52
+ - gain: how strongly τ_eff reacts to uncertainty
53
+ - cap: prevents absurd values
54
+ """
55
+ u = clamp(float(uncertainty), 0.0, 1.0)
56
+ tau = base + slow_by + gain * u
57
+ return clamp(tau, base, cap)
58
+
59
+ def rft_confidence(uncertainty: float):
60
+ # Confidence is the complement of uncertainty, clipped.
61
+ return clamp(1.0 - float(uncertainty), 0.0, 1.0)
62
+
63
+ def rft_gate(conf: float, tau_eff: float, threshold: float):
64
+ """
65
+ Collapse gate:
66
+ - higher τ_eff makes gate stricter (forces more decisive conditions)
67
+ - threshold is the minimum confidence needed
68
+ """
69
+ conf = float(conf)
70
+ tau_eff = float(tau_eff)
71
+ # stricter with larger tau: raise the effective threshold
72
+ effective = threshold + 0.08 * (tau_eff - 1.0)
73
+ return conf >= clamp(effective, 0.0, 0.999)
74
+
75
+ # -----------------------------
76
+ # NEO Simulation
77
+ # -----------------------------
78
+ def simulate_neo(seed: int,
79
+ steps: int,
80
+ dt: float,
81
+ alert_km: float,
82
+ noise_km: float,
83
+ rft_conf_threshold: float,
84
+ tau_gain: float,
85
+ show_debug: bool):
86
+ set_seed(seed)
87
+
88
+ # Start far-ish but inside a range that can produce alerts
89
+ pos = np.array([9000.0, 2500.0, 1000.0], dtype=float) # km
90
+ vel = np.array([-55.0, -8.0, -3.0], dtype=float) # km/step (scaled)
91
+
92
+ rows = []
93
+ alerts_baseline = 0
94
+ alerts_rft_raw = 0
95
+ alerts_rft_filtered = 0
96
+ ops_proxy = 0
97
+
98
+ for t in range(int(steps)):
99
+ # Truth propagation (simple linear + drift)
100
+ drift = 0.05 * np.array([math.sin(0.03*t), math.cos(0.02*t), math.sin(0.015*t)])
101
+ pos_true = pos + vel * dt + drift
102
+
103
+ # Measurement noise
104
+ meas = pos_true + np.random.normal(0.0, noise_km, size=3)
105
+
106
+ # Distance to origin (proxy for Earth)
107
+ dist = float(np.linalg.norm(meas))
108
+
109
+ # Uncertainty proxy: higher noise and higher speed increase uncertainty
110
+ speed = float(np.linalg.norm(vel))
111
+ uncertainty = clamp((noise_km / max(alert_km, 1.0)) * 2.0 + (speed / 200.0) * 0.2, 0.0, 1.0)
112
+
113
+ # Baseline alert: if within radius
114
+ baseline_alert = dist <= alert_km
115
+ if baseline_alert:
116
+ alerts_baseline += 1
117
+
118
+ # RFT: τ_eff + confidence + gate (collapse earlier / smarter)
119
+ tau = tau_eff_adaptive(uncertainty=uncertainty, base=1.0, slow_by=1.0, gain=tau_gain, cap=4.0)
120
+ conf = rft_confidence(uncertainty)
121
+ # "RFT alert candidate" uses same geometric condition but *requires* collapse gate
122
+ rft_candidate = dist <= alert_km
123
+ rft_alert = bool(rft_candidate and rft_gate(conf, tau, rft_conf_threshold))
124
+
125
+ if rft_candidate:
126
+ alerts_rft_raw += 1
127
+ if rft_alert:
128
+ alerts_rft_filtered += 1
129
+
130
+ ops_proxy += 12 # a stable "compute proxy" per step
131
+
132
+ rows.append({
133
+ "t": t,
134
+ "dt": dt,
135
+ "x_km": meas[0],
136
+ "y_km": meas[1],
137
+ "z_km": meas[2],
138
+ "dist_km": dist,
139
+ "noise_km": noise_km,
140
+ "uncertainty": uncertainty,
141
+ "tau_eff": tau,
142
+ "confidence": conf,
143
+ "baseline_alert": int(baseline_alert),
144
+ "rft_candidate": int(rft_candidate),
145
+ "rft_alert": int(rft_alert),
146
+ })
147
+
148
+ pos = pos_true
149
+
150
+ df = pd.DataFrame(rows)
151
+
152
+ # Plots
153
+ fig1 = plt.figure(figsize=(10, 4))
154
+ ax = fig1.add_subplot(111)
155
+ ax.plot(df["t"], df["dist_km"])
156
+ ax.axhline(alert_km, linestyle="--")
157
+ ax.set_title("NEO: Distance to target vs time")
158
+ ax.set_xlabel("t (step)")
159
+ ax.set_ylabel("distance (km)")
160
+ p_dist = save_plot(fig1, f"neo_distance_seed{seed}.png")
161
+
162
+ fig2 = plt.figure(figsize=(10, 4))
163
+ ax = fig2.add_subplot(111)
164
+ ax.plot(df["t"], df["confidence"])
165
+ ax.plot(df["t"], df["tau_eff"])
166
+ ax.set_title("NEO: Confidence and τ_eff (Adaptive)")
167
+ ax.set_xlabel("t (step)")
168
+ ax.set_ylabel("value")
169
+ p_conf = save_plot(fig2, f"neo_conf_tau_seed{seed}.png")
170
+
171
+ fig3 = plt.figure(figsize=(10, 3))
172
+ ax = fig3.add_subplot(111)
173
+ ax.step(df["t"], df["baseline_alert"], where="post")
174
+ ax.step(df["t"], df["rft_alert"], where="post")
175
+ ax.set_title("NEO: Alerts (Baseline vs RFT)")
176
+ ax.set_xlabel("t (step)")
177
+ ax.set_ylabel("alert (0/1)")
178
+ p_alerts = save_plot(fig3, f"neo_alerts_seed{seed}.png")
179
+
180
+ csv_path = df_to_csv_file(df, f"neo_log_seed{seed}.csv")
181
+
182
+ summary = {
183
+ "seed": int(seed),
184
+ "steps": int(steps),
185
+ "alert_km": float(alert_km),
186
+ "baseline_alerts": int(alerts_baseline),
187
+ "rft_candidates": int(alerts_rft_raw),
188
+ "rft_alerts_filtered": int(alerts_rft_filtered),
189
+ "false_positive_proxy_reduction_%": float(
190
+ 100.0 * (1.0 - (alerts_rft_filtered / max(alerts_rft_raw, 1)))
191
+ ),
192
+ "ops_proxy": int(ops_proxy),
193
+ }
194
+
195
+ debug_lines = ""
196
+ if show_debug:
197
+ debug_lines = (
198
+ "Debug view (first 12 rows):\n"
199
+ + df.head(12).to_string(index=False)
200
+ )
201
+
202
+ return summary, debug_lines, [p_dist, p_conf, p_alerts], csv_path
203
+
204
+ # -----------------------------
205
+ # Satellite Jitter Simulation
206
+ # -----------------------------
207
+ def simulate_jitter(seed: int,
208
+ steps: int,
209
+ dt: float,
210
+ noise: float,
211
+ baseline_kp: float,
212
+ rft_kp: float,
213
+ gate_threshold: float,
214
+ tau_gain: float):
215
+ set_seed(seed)
216
+
217
+ jitter = 0.0
218
+ jitter_rate = 0.0
219
+ act_baseline = 0.0
220
+ act_rft = 0.0
221
+
222
+ rows = []
223
+ duty_baseline = 0
224
+ duty_rft = 0
225
+ ops_proxy = 0
226
+
227
+ for t in range(int(steps)):
228
+ # Jitter dynamics (random walk + periodic micro-vibe)
229
+ micro = 0.25 * math.sin(0.05 * t) + 0.12 * math.sin(0.13 * t)
230
+ jitter_rate += np.random.normal(0.0, noise) * 0.08
231
+ jitter += jitter_rate * dt + micro + np.random.normal(0.0, noise)
232
+
233
+ # Baseline: continuous correction
234
+ u_base = -baseline_kp * jitter
235
+ jitter_base_next = jitter + u_base * 0.35
236
+ duty_baseline += int(abs(u_base) > 0.01)
237
+
238
+ # RFT: correct only when it’s worth collapsing an action
239
+ uncertainty = clamp(noise * 3.0, 0.0, 1.0)
240
+ tau = tau_eff_adaptive(uncertainty, base=1.0, slow_by=1.0, gain=tau_gain, cap=4.0)
241
+ conf = rft_confidence(uncertainty)
242
+
243
+ should_act = rft_gate(conf, tau, gate_threshold) and (abs(jitter) > 0.35)
244
+ u_rft = (-rft_kp * jitter) if should_act else 0.0
245
+ jitter_rft_next = jitter + u_rft * 0.35
246
+ duty_rft += int(abs(u_rft) > 0.01)
247
+
248
+ # Apply combined evolution (keep it fair by updating the same jitter state)
249
+ # We store both "what baseline would do" and "what RFT would do"
250
+ act_baseline = u_base
251
+ act_rft = u_rft
252
+
253
+ ops_proxy += 10
254
+
255
+ rows.append({
256
+ "t": t,
257
+ "jitter": jitter,
258
+ "u_baseline": act_baseline,
259
+ "u_rft": act_rft,
260
+ "baseline_active": int(abs(act_baseline) > 0.01),
261
+ "rft_active": int(abs(act_rft) > 0.01),
262
+ "tau_eff": tau,
263
+ "confidence": conf,
264
+ "noise": noise,
265
+ "jitter_baseline_next": jitter_base_next,
266
+ "jitter_rft_next": jitter_rft_next,
267
+ })
268
+
269
+ # Update jitter state (common plant)
270
+ jitter = jitter_rft_next # choose RFT plant evolution to reflect "running RFT"
271
+ jitter_rate *= 0.92
272
+
273
+ df = pd.DataFrame(rows)
274
+
275
+ rms = lambda x: float(np.sqrt(np.mean(np.square(x))))
276
+ jitter_rms = rms(df["jitter"].values)
277
+ duty_b = duty_baseline / max(steps, 1)
278
+ duty_r = duty_rft / max(steps, 1)
279
+
280
+ fig1 = plt.figure(figsize=(10, 4))
281
+ ax = fig1.add_subplot(111)
282
+ ax.plot(df["t"], df["jitter"])
283
+ ax.set_title("Jitter: residual vs time (running RFT plant)")
284
+ ax.set_xlabel("t (step)")
285
+ ax.set_ylabel("jitter (arb)")
286
+ p_jit = save_plot(fig1, f"jitter_residual_seed{seed}.png")
287
+
288
+ fig2 = plt.figure(figsize=(10, 3))
289
+ ax = fig2.add_subplot(111)
290
+ ax.step(df["t"], df["baseline_active"], where="post")
291
+ ax.step(df["t"], df["rft_active"], where="post")
292
+ ax.set_title("Jitter: Actuation duty (Baseline vs RFT gating)")
293
+ ax.set_xlabel("t (step)")
294
+ ax.set_ylabel("active (0/1)")
295
+ p_duty = save_plot(fig2, f"jitter_duty_seed{seed}.png")
296
+
297
+ fig3 = plt.figure(figsize=(10, 4))
298
+ ax = fig3.add_subplot(111)
299
+ ax.plot(df["t"], df["tau_eff"])
300
+ ax.plot(df["t"], df["confidence"])
301
+ ax.set_title("Jitter: τ_eff and confidence")
302
+ ax.set_xlabel("t (step)")
303
+ ax.set_ylabel("value")
304
+ p_tau = save_plot(fig3, f"jitter_tau_seed{seed}.png")
305
+
306
+ csv_path = df_to_csv_file(df, f"jitter_log_seed{seed}.csv")
307
+
308
+ summary = {
309
+ "seed": int(seed),
310
+ "steps": int(steps),
311
+ "jitter_rms": jitter_rms,
312
+ "baseline_duty_ratio": float(duty_b),
313
+ "rft_duty_ratio": float(duty_r),
314
+ "duty_reduction_%": float(100.0 * (1.0 - (duty_r / max(duty_b, 1e-9)))),
315
+ "ops_proxy": int(ops_proxy),
316
+ }
317
+
318
+ return summary, [p_jit, p_duty, p_tau], csv_path
319
+
320
+ # -----------------------------
321
+ # Starship-style Landing Harness (2D)
322
+ # -----------------------------
323
+ def simulate_landing(seed: int,
324
+ steps: int,
325
+ dt: float,
326
+ wind_max: float,
327
+ thrust_noise: float,
328
+ kp_baseline: float,
329
+ kp_rft: float,
330
+ gate_threshold: float,
331
+ tau_gain: float,
332
+ goal_m: float):
333
+ set_seed(seed)
334
+
335
+ # state: altitude, vertical velocity, lateral x offset, lateral velocity
336
+ alt = 1000.0
337
+ vv = -45.0
338
+ x = 60.0
339
+ xv = 0.0
340
+
341
+ anomalies = 0
342
+ actions = 0
343
+ ops_proxy = 0
344
+
345
+ rows = []
346
+
347
+ for t in range(int(steps)):
348
+ # wind profile
349
+ wind = wind_max * (0.55 + 0.45 * math.sin(0.08 * t)) + np.random.normal(0, 0.4)
350
+ wind = clamp(wind, 0.0, wind_max)
351
+
352
+ # thrust disturbance
353
+ thrust_dev = np.random.normal(0.0, thrust_noise)
354
+
355
+ # measurement noise (simple)
356
+ meas_alt = alt + np.random.normal(0, 0.6)
357
+ meas_vv = vv + np.random.normal(0, 0.35)
358
+ meas_x = x + np.random.normal(0, 0.8)
359
+ meas_xv = xv + np.random.normal(0, 0.25)
360
+
361
+ # uncertainty proxy
362
+ uncertainty = clamp((abs(thrust_dev) / 5.0) * 0.15 + (wind / max(wind_max, 1e-9)) * 0.25, 0.0, 1.0)
363
+ tau = tau_eff_adaptive(uncertainty, base=1.0, slow_by=1.0, gain=tau_gain, cap=4.0)
364
+ conf = rft_confidence(uncertainty)
365
+
366
+ # anomaly definition (reduced spam): only count if materially bad
367
+ # pitch/yaw/roll are not modelled here; we count "control-relevant" anomalies:
368
+ # - high wind
369
+ # - high lateral error near ground
370
+ # - high descent rate near ground
371
+ anomaly_types = []
372
+ if wind > (0.85 * wind_max):
373
+ anomaly_types.append("High wind")
374
+ if meas_alt < 200 and abs(meas_x) > 20:
375
+ anomaly_types.append("High lateral error near ground")
376
+ if meas_alt < 150 and abs(meas_vv) > 15:
377
+ anomaly_types.append("High descent rate near ground")
378
+
379
+ is_anomaly = len(anomaly_types) > 0
380
+ if is_anomaly:
381
+ anomalies += 1
382
+
383
+ # Baseline control: continuous proportional
384
+ u_base_x = -kp_baseline * meas_x - 0.25 * meas_xv
385
+ u_base_v = -kp_baseline * (meas_vv + 5.0) # target ~ -5 m/s
386
+
387
+ # RFT control: gated “collapse” actions
388
+ do_action = rft_gate(conf, tau, gate_threshold)
389
+
390
+ # lookahead scaling makes RFT more decisive as altitude drops
391
+ phase = 1.0 - clamp(meas_alt / 1000.0, 0.0, 1.0) # 0 high up, 1 near ground
392
+ lookahead = 1.0 + 1.2 * phase
393
+
394
+ u_rft_x = 0.0
395
+ u_rft_v = 0.0
396
+ if do_action:
397
+ u_rft_x = (-kp_rft * lookahead * meas_x) - (0.30 * meas_xv)
398
+ u_rft_v = (-kp_rft * lookahead * (meas_vv + 5.0))
399
+ actions += 1
400
+
401
+ # apply dynamics
402
+ # vertical: vv integrates thrust + gravity (simplified)
403
+ g = -9.81
404
+ vv = vv + (g + 0.18 * u_rft_v + 0.08 * thrust_dev) * dt
405
+ alt = max(0.0, alt + vv * dt)
406
+
407
+ # lateral: wind pushes, control counters
408
+ xv = xv + (0.35 * wind - 0.30 * u_rft_x) * dt
409
+ x = x + xv * dt
410
+
411
+ ops_proxy += 16
412
+
413
+ rows.append({
414
+ "t": t,
415
+ "alt_m": alt,
416
+ "vv_m_s": vv,
417
+ "x_m": x,
418
+ "xv_m_s": xv,
419
+ "wind_m_s": wind,
420
+ "thrust_dev": thrust_dev,
421
+ "uncertainty": uncertainty,
422
+ "tau_eff": tau,
423
+ "confidence": conf,
424
+ "anomaly": int(is_anomaly),
425
+ "anomaly_types": "|".join(anomaly_types) if anomaly_types else "",
426
+ "action_taken": int(do_action),
427
+ "u_baseline_x": u_base_x,
428
+ "u_baseline_v": u_base_v,
429
+ "u_rft_x": u_rft_x,
430
+ "u_rft_v": u_rft_v,
431
+ })
432
+
433
+ if alt <= 0.0:
434
+ break
435
+
436
+ df = pd.DataFrame(rows)
437
+
438
+ landing_offset = float(abs(df["x_m"].iloc[-1])) if len(df) else 9999.0
439
+
440
+ fig1 = plt.figure(figsize=(10, 4))
441
+ ax = fig1.add_subplot(111)
442
+ ax.plot(df["t"], df["alt_m"])
443
+ ax.set_title("Landing: altitude vs time")
444
+ ax.set_xlabel("t (step)")
445
+ ax.set_ylabel("altitude (m)")
446
+ p_alt = save_plot(fig1, f"landing_alt_seed{seed}.png")
447
+
448
+ fig2 = plt.figure(figsize=(10, 4))
449
+ ax = fig2.add_subplot(111)
450
+ ax.plot(df["t"], df["x_m"])
451
+ ax.axhline(goal_m, linestyle="--")
452
+ ax.axhline(-goal_m, linestyle="--")
453
+ ax.set_title("Landing: lateral offset vs time (goal band)")
454
+ ax.set_xlabel("t (step)")
455
+ ax.set_ylabel("offset (m)")
456
+ p_x = save_plot(fig2, f"landing_offset_seed{seed}.png")
457
+
458
+ fig3 = plt.figure(figsize=(10, 4))
459
+ ax = fig3.add_subplot(111)
460
+ ax.plot(df["t"], df["wind_m_s"])
461
+ ax.set_title("Landing: wind profile")
462
+ ax.set_xlabel("t (step)")
463
+ ax.set_ylabel("wind (m/s)")
464
+ p_w = save_plot(fig3, f"landing_wind_seed{seed}.png")
465
+
466
+ fig4 = plt.figure(figsize=(10, 3))
467
+ ax = fig4.add_subplot(111)
468
+ ax.step(df["t"], df["anomaly"], where="post")
469
+ ax.step(df["t"], df["action_taken"], where="post")
470
+ ax.set_title("Landing: anomaly vs action timeline")
471
+ ax.set_xlabel("t (step)")
472
+ ax.set_ylabel("0/1")
473
+ p_a = save_plot(fig4, f"landing_anomaly_action_seed{seed}.png")
474
+
475
+ csv_path = df_to_csv_file(df, f"landing_log_seed{seed}.csv")
476
+
477
+ summary = {
478
+ "seed": int(seed),
479
+ "steps_ran": int(len(df)),
480
+ "final_landing_offset_m": float(landing_offset),
481
+ "goal_m": float(goal_m),
482
+ "hit_goal": bool(landing_offset <= goal_m),
483
+ "total_anomalies_detected": int(anomalies),
484
+ "total_control_actions": int(actions),
485
+ "ops_proxy": int(ops_proxy),
486
+ }
487
+
488
+ return summary, [p_alt, p_x, p_w, p_a], csv_path
489
+
490
+ # -----------------------------
491
+ # Benchmarks
492
+ # -----------------------------
493
+ def run_benchmarks(seed: int,
494
+ neo_steps: int, neo_dt: float, neo_alert_km: float, neo_noise_km: float,
495
+ jit_steps: int, jit_dt: float, jit_noise: float,
496
+ land_steps: int, land_dt: float, land_wind: float, land_thrust_noise: float,
497
+ tau_gain: float):
498
+ """
499
+ Benchmarks are baseline vs RFT under the SAME seed.
500
+ Baseline here means:
501
+ - NEO: geometric threshold only
502
+ - Jitter: continuous correction (no gating)
503
+ - Landing: continuous proportional (no gating)
504
+ RFT means τ_eff + confidence + gate.
505
+ """
506
+ seed = int(seed)
507
+
508
+ # NEO benchmark
509
+ s_rft, _, neo_imgs, neo_csv = simulate_neo(
510
+ seed=seed,
511
+ steps=neo_steps,
512
+ dt=neo_dt,
513
+ alert_km=neo_alert_km,
514
+ noise_km=neo_noise_km,
515
+ rft_conf_threshold=0.55,
516
+ tau_gain=tau_gain,
517
+ show_debug=False
518
+ )
519
+ # Baseline alerts equals df baseline count; derive from CSV
520
+ neo_df = pd.read_csv(neo_csv)
521
+ neo_base = int(neo_df["baseline_alert"].sum())
522
+ neo_rft = int(neo_df["rft_alert"].sum())
523
+ neo_candidates = int(neo_df["rft_candidate"].sum())
524
+
525
+ # Jitter benchmark
526
+ j_sum, jit_imgs, jit_csv = simulate_jitter(
527
+ seed=seed,
528
+ steps=jit_steps,
529
+ dt=jit_dt,
530
+ noise=jit_noise,
531
+ baseline_kp=0.35,
532
+ rft_kp=0.55,
533
+ gate_threshold=0.55,
534
+ tau_gain=tau_gain
535
+ )
536
+
537
+ # Landing benchmark
538
+ l_sum, land_imgs, land_csv = simulate_landing(
539
+ seed=seed,
540
+ steps=land_steps,
541
+ dt=land_dt,
542
+ wind_max=land_wind,
543
+ thrust_noise=land_thrust_noise,
544
+ kp_baseline=0.06, # baseline weaker to show "continuous but less decisive"
545
+ kp_rft=0.10, # RFT stronger but gated and phase-weighted
546
+ gate_threshold=0.55,
547
+ tau_gain=tau_gain,
548
+ goal_m=10.0
549
+ )
550
+
551
+ # Scorecard table
552
+ score = pd.DataFrame([
553
+ {
554
+ "Module": "NEO",
555
+ "Baseline alerts": neo_base,
556
+ "RFT candidates": neo_candidates,
557
+ "RFT filtered alerts": neo_rft,
558
+ "False-positive proxy reduction %": 100.0 * (1.0 - (neo_rft / max(neo_candidates, 1))),
559
+ "Energy/compute proxy": int(s_rft["ops_proxy"])
560
+ },
561
+ {
562
+ "Module": "Satellite Jitter",
563
+ "Baseline alerts": "",
564
+ "RFT candidates": "",
565
+ "RFT filtered alerts": "",
566
+ "False-positive proxy reduction %": "",
567
+ "Energy/compute proxy": int(j_sum["ops_proxy"])
568
+ },
569
+ {
570
+ "Module": "Landing",
571
+ "Baseline alerts": "",
572
+ "RFT candidates": "",
573
+ "RFT filtered alerts": "",
574
+ "False-positive proxy reduction %": "",
575
+ "Energy/compute proxy": int(l_sum["ops_proxy"])
576
+ },
577
+ ])
578
+
579
+ score_path = df_to_csv_file(score, f"bench_score_seed{seed}.csv")
580
+
581
+ # Summary text
582
+ txt = (
583
+ f"Benchmarks (seed={seed})\n"
584
+ f"- NEO: baseline alerts={neo_base}, RFT candidates={neo_candidates}, RFT filtered={neo_rft}\n"
585
+ f"- Jitter: jitter RMS={j_sum['jitter_rms']:.4f}, duty reduction={j_sum['duty_reduction_%']:.1f}%\n"
586
+ f"- Landing: final offset={l_sum['final_landing_offset_m']:.2f} m (goal 10 m), anomalies={l_sum['total_anomalies_detected']}, actions={l_sum['total_control_actions']}\n"
587
+ )
588
+
589
+ all_imgs = neo_imgs + jit_imgs + land_imgs
590
+ return txt, score, score_path, all_imgs, [neo_csv, jit_csv, land_csv]
591
+
592
+ # -----------------------------
593
+ # UI text blocks (your voice, full openness)
594
+ # -----------------------------
595
+ HOME_MD = """
596
+ # Rendered Frame Theory (RFT) — Agent Console
597
+
598
+ I built this Space to be transparent, reproducible, and benchmarkable.
599
+
600
+ I’m not asking anyone to “believe” in anything here.
601
+ Run it. Change the parameters. Break it. Compare baseline vs RFT.
602
+
603
+ What I’m demonstrating is a practical idea:
604
+
605
+ **Decision timing matters.**
606
+ RFT treats timing (τ_eff), uncertainty, and action “collapse” as first-class controls.
607
+
608
+ This Space contains three working agent harnesses:
609
+ - **NEO alerting** (reduce early false positives under noisy tracking)
610
+ - **Satellite jitter reduction** (reduce actuator duty / chatter while keeping residual low)
611
+ - **Starship-style landing harness** (simplified, but structured to test decision timing under wind/thrust disturbances)
612
+
613
+ Every tab shows what it’s doing, why, and where it wins or loses.
614
+
615
+ No SciPy. No hidden dependencies. No model weights. No tricks.
616
+ """
617
+
618
+ LIVE_MD = """
619
+ # Live Console
620
+
621
+ This tab is a single place to run everything quickly and export logs.
622
+
623
+ If someone wants to argue, this is where the argument dies:
624
+ - deterministic runs (seeded)
625
+ - plots saved
626
+ - CSV logs exported
627
+ - baseline vs RFT comparisons available
628
+ """
629
+
630
+ THEORY_PRACTICE_MD = """
631
+ # Theory → Practice (how I implement RFT here)
632
+
633
+ This Space uses RFT in a practical way:
634
+
635
+ ## 1) Uncertainty (explicit)
636
+ I compute an uncertainty proxy from noise + disturbance scale.
637
+ This is not magic. It’s just honest modelling.
638
+
639
+ ## 2) Confidence
640
+ Confidence is the complement: **confidence = 1 − uncertainty** (clipped 0..1).
641
+
642
+ ## 3) Adaptive τ_eff
643
+ τ_eff is implemented as a timing/decision strictness modifier:
644
+ - higher uncertainty → higher τ_eff
645
+ - **and yes, I explicitly slow τ_eff by 1.0**, because this was the target behaviour I wanted to test.
646
+
647
+ ## 4) Collapse gate
648
+ I only apply “decisive actions” when the gate condition passes:
649
+ - confidence must exceed a threshold
650
+ - τ_eff increases strictness (makes the gate harder under uncertainty)
651
+
652
+ ## 5) Why this matters
653
+ Baseline controllers often act constantly.
654
+ RFT tries to act **less often**, but **more decisively**, so you waste less energy and trigger fewer junk corrections/alerts.
655
+ """
656
+
657
+ MATH_MD = r"""
658
+ # Mathematics (minimal and implementation-linked)
659
+
660
+ I’m keeping this readable and tied to actual behaviour in code.
661
+
662
+ ## Variables (used in this Space)
663
+ - **u ∈ [0,1]** : uncertainty proxy (dimensionless)
664
+ - **C ∈ [0,1]** : confidence proxy (dimensionless)
665
+ - **τ_eff ≥ 1** : effective render/decision timing factor (dimensionless)
666
+ - **Gate(C, τ_eff)** : action/alert collapse condition
667
+
668
+ ## Definitions
669
+ ### Confidence
670
+ \[
671
+ C = \text{clip}(1 - u, 0, 1)
672
+ \]
673
+
674
+ ### Adaptive τ_eff (with “slow by 1.0”)
675
+ \[
676
+ \tau_{\text{eff}} = \text{clip}(1 + 1.0 + g\cdot u,\; 1,\; \tau_{\max})
677
+ \]
678
+ where \( g \) is a gain.
679
+
680
+ ### Collapse gate (concept)
681
+ A higher τ_eff makes the decision stricter:
682
+ \[
683
+ \text{Gate} = \left[C \ge \theta + k(\tau_{\text{eff}}-1)\right]
684
+ \]
685
+ where \( \theta \) is the base confidence threshold and \( k \) increases strictness with τ_eff.
686
+
687
+ That’s exactly what I implement here: more uncertainty → higher τ_eff → harder gate → fewer low-confidence actions.
688
+ """
689
+
690
+ INVESTOR_MD = """
691
+ # Investor / Agency Walkthrough (plain language)
692
+
693
+ ## What I’m proving inside this Space
694
+ I’m demonstrating a decision-timing framework that can be applied to:
695
+ - alert filtering (NEO / tracking)
696
+ - stabilisation (jitter reduction)
697
+ - anomaly-aware control loops (landing harness)
698
+
699
+ This is not a “pitch deck”. It’s a runnable harness:
700
+ - you can reproduce the results with seeds
701
+ - you can export logs
702
+ - you can compare baseline vs RFT
703
+ - you can change thresholds and see behaviour shift
704
+
705
+ ## What I’m NOT claiming
706
+ - I’m not claiming flight certification
707
+ - I’m not claiming SpaceX is using this
708
+ - I’m not claiming this replaces aerospace validation pipelines
709
+
710
+ ## What would make this production-grade
711
+ - real sensor ingestion + timing constraints
712
+ - hardware-in-loop testing
713
+ - systematic dataset validation
714
+ - integration targets (embedded, REST, batch)
715
+
716
+ If you want the “serious build”, I can package these modules as:
717
+ - Python module
718
+ - REST endpoint
719
+ - edge builds (ARM)
720
+ """
721
+
722
+ REPRO_MD = """
723
+ # Reproducibility & Logs
724
+
725
+ Everything here is reproducible:
726
+ - set the seed
727
+ - run baseline vs RFT with the same seed
728
+ - export the CSV
729
+ - verify plots and metrics
730
+
731
+ CSV schema is explicit in the exports:
732
+ - time index
733
+ - state values
734
+ - uncertainty, confidence, τ_eff
735
+ - alerts/actions flags
736
+ """
737
+
738
+ # -----------------------------
739
+ # Gradio UI
740
+ # -----------------------------
741
+ def ui_run_neo(seed, steps, dt, alert_km, noise_km, rft_conf_th, tau_gain, show_debug):
742
+ summary, debug_lines, imgs, csv_path = simulate_neo(
743
+ seed=int(seed),
744
+ steps=int(steps),
745
+ dt=float(dt),
746
+ alert_km=float(alert_km),
747
+ noise_km=float(noise_km),
748
+ rft_conf_threshold=float(rft_conf_th),
749
+ tau_gain=float(tau_gain),
750
+ show_debug=bool(show_debug),
751
+ )
752
+ summary_txt = json.dumps(summary, indent=2)
753
+ return summary_txt, debug_lines, imgs[0], imgs[1], imgs[2], csv_path
754
+
755
+ def ui_run_jitter(seed, steps, dt, noise, baseline_kp, rft_kp, gate_th, tau_gain):
756
+ summary, imgs, csv_path = simulate_jitter(
757
+ seed=int(seed),
758
+ steps=int(steps),
759
+ dt=float(dt),
760
+ noise=float(noise),
761
+ baseline_kp=float(baseline_kp),
762
+ rft_kp=float(rft_kp),
763
+ gate_threshold=float(gate_th),
764
+ tau_gain=float(tau_gain),
765
+ )
766
+ summary_txt = json.dumps(summary, indent=2)
767
+ return summary_txt, imgs[0], imgs[1], imgs[2], csv_path
768
+
769
+ def ui_run_landing(seed, steps, dt, wind_max, thrust_noise, kp_base, kp_rft, gate_th, tau_gain, goal_m):
770
+ summary, imgs, csv_path = simulate_landing(
771
+ seed=int(seed),
772
+ steps=int(steps),
773
+ dt=float(dt),
774
+ wind_max=float(wind_max),
775
+ thrust_noise=float(thrust_noise),
776
+ kp_baseline=float(kp_base),
777
+ kp_rft=float(kp_rft),
778
+ gate_threshold=float(gate_th),
779
+ tau_gain=float(tau_gain),
780
+ goal_m=float(goal_m),
781
+ )
782
+ summary_txt = json.dumps(summary, indent=2)
783
+ return summary_txt, imgs[0], imgs[1], imgs[2], imgs[3], csv_path
784
+
785
+ def ui_run_bench(seed, neo_steps, neo_dt, neo_alert_km, neo_noise_km, jit_steps, jit_dt, jit_noise, land_steps, land_dt, land_wind, land_thrust_noise, tau_gain):
786
+ txt, score_df, score_csv, imgs, logs = run_benchmarks(
787
+ seed=int(seed),
788
+ neo_steps=int(neo_steps), neo_dt=float(neo_dt), neo_alert_km=float(neo_alert_km), neo_noise_km=float(neo_noise_km),
789
+ jit_steps=int(jit_steps), jit_dt=float(jit_dt), jit_noise=float(jit_noise),
790
+ land_steps=int(land_steps), land_dt=float(land_dt), land_wind=float(land_wind), land_thrust_noise=float(land_thrust_noise),
791
+ tau_gain=float(tau_gain)
792
+ )
793
+ return txt, score_df, score_csv, imgs[0], imgs[1], imgs[2], imgs[3], imgs[4], imgs[5], imgs[6], imgs[7], imgs[8], logs[0], logs[1], logs[2]
794
+
795
+ with gr.Blocks(title="RFT — Agent Console (NEO / Jitter / Landing)") as demo:
796
+ gr.Markdown(HOME_MD)
797
+
798
+ with gr.Tabs():
799
+ # ----------------------------------------------------------
800
+ with gr.Tab("Live Console"):
801
+ gr.Markdown(LIVE_MD)
802
+
803
+ with gr.Row():
804
+ seed_live = gr.Number(value=42, precision=0, label="Seed (reproducible)")
805
+ tau_gain_live = gr.Slider(0.0, 3.0, value=1.2, step=0.05, label="τ_eff gain (global)")
806
+
807
+ with gr.Accordion("Benchmark settings", open=True):
808
+ with gr.Row():
809
+ neo_steps = gr.Slider(50, 400, value=120, step=1, label="NEO steps")
810
+ neo_dt = gr.Slider(0.5, 3.0, value=1.0, step=0.1, label="NEO dt")
811
+ neo_alert = gr.Slider(1000, 20000, value=5000, step=50, label="NEO alert radius (km)")
812
+ neo_noise = gr.Slider(0.0, 200.0, value=35.0, step=1.0, label="NEO measurement noise (km)")
813
+
814
+ with gr.Row():
815
+ jit_steps = gr.Slider(100, 1200, value=500, step=1, label="Jitter steps")
816
+ jit_dt = gr.Slider(0.5, 2.0, value=1.0, step=0.1, label="Jitter dt")
817
+ jit_noise = gr.Slider(0.0, 0.5, value=0.08, step=0.01, label="Jitter noise")
818
+
819
+ with gr.Row():
820
+ land_steps = gr.Slider(40, 400, value=120, step=1, label="Landing steps")
821
+ land_dt = gr.Slider(0.2, 2.0, value=1.0, step=0.1, label="Landing dt")
822
+ land_wind = gr.Slider(0.0, 25.0, value=15.0, step=0.5, label="Landing wind max (m/s)")
823
+ land_thrust_noise = gr.Slider(0.0, 10.0, value=3.0, step=0.1, label="Landing thrust noise")
824
+
825
+ run_b = gr.Button("Run Full Benchmarks (Baseline vs RFT)")
826
+
827
+ bench_txt = gr.Textbox(label="Benchmark summary", lines=6)
828
+ bench_table = gr.Dataframe(label="Scorecard (CSV also exported)")
829
+
830
+ bench_score_csv = gr.File(label="Download: benchmark scorecard CSV")
831
+ with gr.Row():
832
+ img1 = gr.Image(label="NEO: Distance")
833
+ img2 = gr.Image(label="NEO: Confidence & τ_eff")
834
+ img3 = gr.Image(label="NEO: Alerts")
835
+ with gr.Row():
836
+ img4 = gr.Image(label="Jitter: Residual")
837
+ img5 = gr.Image(label="Jitter: Duty")
838
+ img6 = gr.Image(label="Jitter: τ_eff & confidence")
839
+ with gr.Row():
840
+ img7 = gr.Image(label="Landing: Altitude")
841
+ img8 = gr.Image(label="Landing: Offset")
842
+ img9 = gr.Image(label="Landing: Wind")
843
+ img10 = gr.Image(label="Landing: anomaly vs action timeline")
844
+
845
+ neo_log = gr.File(label="Download: NEO log CSV")
846
+ jit_log = gr.File(label="Download: Jitter log CSV")
847
+ land_log = gr.File(label="Download: Landing log CSV")
848
+
849
+ run_b.click(
850
+ ui_run_bench,
851
+ inputs=[seed_live, neo_steps, neo_dt, neo_alert, neo_noise, jit_steps, jit_dt, jit_noise, land_steps, land_dt, land_wind, land_thrust_noise, tau_gain_live],
852
+ outputs=[bench_txt, bench_table, bench_score_csv,
853
+ img1, img2, img3, img4, img5, img6, img7, img8, img9, img10,
854
+ neo_log, jit_log, land_log]
855
+ )
856
+
857
+ # ----------------------------------------------------------
858
+ with gr.Tab("NEO Agent"):
859
+ gr.Markdown("# Near-Earth Object (NEO) Alerting Agent\nThis is a test harness for filtering close-approach alerts under noise.\nBaseline: distance threshold only.\nRFT: distance threshold + confidence + τ_eff collapse gate.\n")
860
+ with gr.Row():
861
+ seed_neo = gr.Number(value=42, precision=0, label="Seed")
862
+ steps_neo = gr.Slider(50, 400, value=120, step=1, label="Steps")
863
+ dt_neo = gr.Slider(0.5, 3.0, value=1.0, step=0.1, label="dt")
864
+ with gr.Row():
865
+ alert_km = gr.Slider(1000, 20000, value=5000, step=50, label="Alert threshold (km)")
866
+ noise_km = gr.Slider(0.0, 200.0, value=35.0, step=1.0, label="Measurement noise (km)")
867
+ rft_conf_th = gr.Slider(0.1, 0.95, value=0.55, step=0.01, label="RFT confidence threshold")
868
+ tau_gain = gr.Slider(0.0, 3.0, value=1.2, step=0.05, label="τ_eff gain")
869
+ show_debug = gr.Checkbox(value=False, label="Show debug table (first rows)")
870
+ run_neo = gr.Button("Run NEO Simulation")
871
+
872
+ out_neo_summary = gr.Textbox(label="Summary JSON", lines=12)
873
+ out_neo_debug = gr.Textbox(label="Debug", lines=10)
874
+ with gr.Row():
875
+ out_neo_img1 = gr.Image(label="Distance vs time")
876
+ out_neo_img2 = gr.Image(label="Confidence and τ_eff")
877
+ out_neo_img3 = gr.Image(label="Alerts timeline")
878
+ out_neo_csv = gr.File(label="Download NEO CSV log")
879
+
880
+ run_neo.click(
881
+ ui_run_neo,
882
+ inputs=[seed_neo, steps_neo, dt_neo, alert_km, noise_km, rft_conf_th, tau_gain, show_debug],
883
+ outputs=[out_neo_summary, out_neo_debug, out_neo_img1, out_neo_img2, out_neo_img3, out_neo_csv]
884
+ )
885
+
886
+ # ----------------------------------------------------------
887
+ with gr.Tab("Satellite Jitter Agent"):
888
+ gr.Markdown("# Satellite Jitter Reduction\nBaseline: continuous correction.\nRFT: gated correction using confidence + τ_eff.\nThis is a simple but honest test of duty-cycle reduction.\n")
889
+ with gr.Row():
890
+ seed_j = gr.Number(value=42, precision=0, label="Seed")
891
+ steps_j = gr.Slider(100, 1200, value=500, step=1, label="Steps")
892
+ dt_j = gr.Slider(0.5, 2.0, value=1.0, step=0.1, label="dt")
893
+ noise_j = gr.Slider(0.0, 0.5, value=0.08, step=0.01, label="Noise")
894
+ with gr.Row():
895
+ base_kp = gr.Slider(0.0, 1.0, value=0.35, step=0.01, label="Baseline kp")
896
+ rft_kp = gr.Slider(0.0, 1.5, value=0.55, step=0.01, label="RFT kp")
897
+ gate_th = gr.Slider(0.1, 0.95, value=0.55, step=0.01, label="Gate threshold")
898
+ tau_gain_j = gr.Slider(0.0, 3.0, value=1.2, step=0.05, label="τ_eff gain")
899
+ run_j = gr.Button("Run Jitter Simulation")
900
+
901
+ out_j_summary = gr.Textbox(label="Summary JSON", lines=10)
902
+ with gr.Row():
903
+ out_j_img1 = gr.Image(label="Residual jitter")
904
+ out_j_img2 = gr.Image(label="Duty timeline")
905
+ out_j_img3 = gr.Image(label="τ_eff & confidence")
906
+ out_j_csv = gr.File(label="Download Jitter CSV log")
907
+
908
+ run_j.click(
909
+ ui_run_jitter,
910
+ inputs=[seed_j, steps_j, dt_j, noise_j, base_kp, rft_kp, gate_th, tau_gain_j],
911
+ outputs=[out_j_summary, out_j_img1, out_j_img2, out_j_img3, out_j_csv]
912
+ )
913
+
914
+ # ----------------------------------------------------------
915
+ with gr.Tab("Starship Landing Harness"):
916
+ gr.Markdown("# Starship-style Landing Harness (Simplified)\nThis is not a SpaceX flight model. It’s a timing-control harness.\nBaseline vs RFT shows whether gated decision timing can reduce waste and still hit the landing goal.\n")
917
+ with gr.Row():
918
+ seed_l = gr.Number(value=42, precision=0, label="Seed")
919
+ steps_l = gr.Slider(40, 400, value=120, step=1, label="Steps")
920
+ dt_l = gr.Slider(0.2, 2.0, value=1.0, step=0.1, label="dt")
921
+ with gr.Row():
922
+ wind_max = gr.Slider(0.0, 25.0, value=15.0, step=0.5, label="Wind max (m/s)")
923
+ thrust_noise = gr.Slider(0.0, 10.0, value=3.0, step=0.1, label="Thrust noise")
924
+ kp_base_l = gr.Slider(0.0, 0.2, value=0.06, step=0.005, label="Baseline kp")
925
+ kp_rft_l = gr.Slider(0.0, 0.25, value=0.10, step=0.005, label="RFT kp")
926
+ with gr.Row():
927
+ gate_th_l = gr.Slider(0.1, 0.95, value=0.55, step=0.01, label="Gate threshold")
928
+ tau_gain_l = gr.Slider(0.0, 3.0, value=1.2, step=0.05, label="τ_eff gain")
929
+ goal_m = gr.Slider(1.0, 50.0, value=10.0, step=0.5, label="Landing goal (m)")
930
+ run_l = gr.Button("Run Landing Simulation")
931
+
932
+ out_l_summary = gr.Textbox(label="Summary JSON", lines=10)
933
+ with gr.Row():
934
+ out_l_img1 = gr.Image(label="Altitude")
935
+ out_l_img2 = gr.Image(label="Offset")
936
+ out_l_img3 = gr.Image(label="Wind")
937
+ out_l_img4 = gr.Image(label="Anomaly vs Action timeline")
938
+ out_l_csv = gr.File(label="Download Landing CSV log")
939
+
940
+ run_l.click(
941
+ ui_run_landing,
942
+ inputs=[seed_l, steps_l, dt_l, wind_max, thrust_noise, kp_base_l, kp_rft_l, gate_th_l, tau_gain_l, goal_m],
943
+ outputs=[out_l_summary, out_l_img1, out_l_img2, out_l_img3, out_l_img4, out_l_csv]
944
+ )
945
+
946
+ # ----------------------------------------------------------
947
+ with gr.Tab("Benchmarks"):
948
+ gr.Markdown("# Benchmarks\nIf someone wants to argue, this is the tab.\nBaseline vs RFT runs, same seed, exported logs.\nUse Live Console to run full benchmark packs.\n")
949
+
950
+ # ----------------------------------------------------------
951
+ with gr.Tab("Theory → Practice"):
952
+ gr.Markdown(THEORY_PRACTICE_MD)
953
+
954
+ with gr.Tab("Mathematics"):
955
+ gr.Markdown(MATH_MD)
956
+
957
+ with gr.Tab("Investor / Agency Walkthrough"):
958
+ gr.Markdown(INVESTOR_MD)
959
+
960
+ with gr.Tab("Reproducibility & Logs"):
961
+ gr.Markdown(REPRO_MD)
962
+
963
+ demo.queue().launch()