code wiki / _hdl_build / nx_atm_trans_gate.nx
nx_atm_trans_gate.nx source
↩ module page · 120 lines · 5657 B
1// nx_atm_trans_gate.nx -- GATE for the nx_atmosphere transmittance rung (2026-08-26).
2// Accept rule, PRE-DECLARED (before the lib compiled): the i64 march must reproduce the
3// 20,000-sample double-precision reference (computed from the same [bytes] medium table,
4// knowledge/library/sky_volumetrics_impl_spec_2026-08-26.md) within +-64 q16 at four KAT
5// angles; expneg must match exp() KATs; ozone tent endpoints exact; transmittance monotone
6// in mu; and a single-species control must DIVERGE from the KATs, proving they discriminate
7// the full medium. license_tier: ORIGINAL No hw writes (Rule 26).
8import "nx_syscalls.nx"
9import "nx_atmosphere.nx"
10import "nx_gate_verdict.nx"
11
12const XT_TOL: i64 = 64
13// double-reference KATs, r = Rb, computed 2026-08-26 (20k-sample midpoint, same constants)
14const XT_Z_R: i64 = 61627
15const XT_Z_G: i64 = 56860
16const XT_Z_B: i64 = 49959
17const XT_M5_R: i64 = 57985
18const XT_M5_G: i64 = 49405
19const XT_M5_B: i64 = 38160
20const XT_S_R: i64 = 26627
21const XT_S_G: i64 = 8647
22const XT_S_B: i64 = 1059
23const XT_H_R: i64 = 6975
24const XT_H_G: i64 = 628
25const XT_H_B: i64 = 3
26
27func xt_abs(v: i64) -> i64 { if v < 0 { return 0 - v } return v }
28
29func xt_kat(mu_q16: i64, kr: i64, kg: i64, kb: i64, buf: *i64) -> i64 {
30 atm_trans_q16(ATM_RB_Q16, mu_q16, buf)
31 gv_puts(" mu_q16=" as *u8); gv_num(mu_q16)
32 gv_puts(" got=" as *u8); gv_num(buf[0]); gv_puts("/" as *u8); gv_num(buf[1]); gv_puts("/" as *u8); gv_num(buf[2])
33 gv_puts(" ref=" as *u8); gv_num(kr); gv_puts("/" as *u8); gv_num(kg); gv_puts("/" as *u8); gv_num(kb); gv_puts("\n" as *u8)
34 if xt_abs(buf[0] - kr) > XT_TOL { return 0 }
35 if xt_abs(buf[1] - kg) > XT_TOL { return 0 }
36 if xt_abs(buf[2] - kb) > XT_TOL { return 0 }
37 return 1
38}
39
40func main() -> i64 {
41 let ctr: *i64 = gv_ctr()
42 let buf: *i64 = sys_mmap(64 * 8) as *i64
43
44 // T1 expneg KATs vs exp(): e^-1=24109, e^-2=8869, e^-8=22 (q16), e^-0=Q
45 var t1: i64 = 1
46 if atm_expneg_q16(0) != 65536 { t1 = 0 }
47 if xt_abs(atm_expneg_q16(65536) - 24109) > 40 { t1 = 0 }
48 if xt_abs(atm_expneg_q16(131072) - 8869) > 24 { t1 = 0 }
49 if xt_abs(atm_expneg_q16(524288) - 22) > 4 { t1 = 0 }
50 gv_check("T1 EXPNEG KATs: e^-1, e^-2, e^-8 match exp() within the named Taylor bound" as *u8, t1, ctr)
51
52 // T2 ozone tent endpoints: 0 at 10 km and 40 km, Q at 25 km
53 var t2: i64 = 1
54 if atm_dens_q16(2, 655360) > 66 { t2 = 0 }
55 if atm_dens_q16(2, 2621440) > 66 { t2 = 0 }
56 if xt_abs(atm_dens_q16(2, 1638400) - 65536) > 66 { t2 = 0 }
57 gv_check("T2 OZONE TENT: zero at 10/40 km, unity at the 25 km peak -- the fixture reaches the condition" as *u8, t2, ctr)
58
59 // T3..T6 transmittance KATs at four angles
60 gv_check("T3 KAT zenith mu=1: i64 march matches the 20k-sample double reference within 64 q16" as *u8, xt_kat(65536, XT_Z_R, XT_Z_G, XT_Z_B, buf), ctr)
61 gv_check("T4 KAT mu=0.5" as *u8, xt_kat(32768, XT_M5_R, XT_M5_G, XT_M5_B, buf), ctr)
62 gv_check("T5 KAT sunset mu=0.05: the 25:1 R:B ratio the page reddening derives from" as *u8, xt_kat(3277, XT_S_R, XT_S_G, XT_S_B, buf), ctr)
63 gv_check("T6 KAT horizon mu=0" as *u8, xt_kat(0, XT_H_R, XT_H_G, XT_H_B, buf), ctr)
64
65 // T7 monotone in mu: T rises with mu for each channel over a sweep
66 var t7: i64 = 1
67 var prevR: i64 = 0 - 1
68 var prevG: i64 = 0 - 1
69 var prevB: i64 = 0 - 1
70 var m9: i64 = 0
71 while m9 <= 65536 {
72 atm_trans_q16(ATM_RB_Q16, m9, buf)
73 if buf[0] < prevR { t7 = 0 }
74 if buf[1] < prevG { t7 = 0 }
75 if buf[2] < prevB { t7 = 0 }
76 prevR = buf[0]; prevG = buf[1]; prevB = buf[2]
77 m9 = m9 + 4096
78 }
79 gv_check("T7 MONOTONE: transmittance is non-decreasing in mu on every channel" as *u8, t7, ctr)
80
81 // T8 determinism
82 var t8: i64 = 1
83 atm_trans_q16(ATM_RB_Q16, 3277, buf)
84 let a0: i64 = buf[0]
85 atm_trans_q16(ATM_RB_Q16, 3277, buf)
86 if buf[0] != a0 { t8 = 0 }
87 gv_check("T8 DETERMINISM: same inputs, same bytes" as *u8, t8, ctr)
88
89 // T9 neg-control: a rayleigh-only tau at zenith green must DIVERGE from the full-medium KAT
90 // by far more than the tolerance -- the KATs discriminate the 3-species medium, not just shape.
91 var i9: i64 = 0
92 var tauG9: i64 = 0
93 let dt9: i64 = 5242880 / 500
94 while i9 < 500 {
95 let h9: i64 = i9 * dt9 + dt9 / 2
96 tauG9 = tauG9 + ((ATM_SRAY_G_Q24 * atm_dens_q16(0, h9)) / 65536 * dt9) / 65536
97 i9 = i9 + 1
98 }
99 let gOnly: i64 = atm_expneg_q16(tauG9 / 256)
100 gv_puts(" rayleigh-only zenith G=" as *u8); gv_num(gOnly)
101 gv_puts(" full-medium ref=" as *u8); gv_num(XT_Z_G); gv_puts("\n" as *u8)
102 var t9: i64 = 0
103 if xt_abs(gOnly - XT_Z_G) > 1000 { t9 = 1 }
104 gv_check("T9 neg-control-single-species-diverges: dropping mie+ozone moves zenith G by >1000 q16, so a wrong medium cannot pass the KATs" as *u8, t9, ctr)
105
106 // T10 sun LUT bake: row 0 near horizon KAT, last row near zenith KAT, rows monotone in R
107 let lut: *i64 = sys_mmap(ATM_SUNLUT_N * 24) as *i64
108 atm_bake_sun_lut(lut)
109 var t10: i64 = 1
110 if xt_abs(lut[0] - XT_H_R) > 128 { t10 = 0 }
111 if xt_abs(lut[(ATM_SUNLUT_N - 1) * 3] - XT_Z_R) > 128 { t10 = 0 }
112 var r10: i64 = 1
113 while r10 < ATM_SUNLUT_N {
114 if lut[r10 * 3] < lut[(r10 - 1) * 3] { t10 = 0 }
115 r10 = r10 + 1
116 }
117 gv_check("T10 SUN LUT: 64 rows bracket the horizon and zenith KATs (offset-radius tolerance 128) and rise monotonically" as *u8, t10, ctr)
118
119 return gv_verdict("ATMTRANS" as *u8, ctr, "the transmittance rung: i64 3-species Beer-Lambert march reproducing the double reference at pre-declared KATs, with a diverging single-species control" as *u8)
120}