nx_ferment_thermal_test.nx source
↩ module page · 75 lines · 3115 B
1// nx_ferment_thermal_test.nx -- smoke for nx_ferment_thermal (R1).
2//
3// Proves hardware temperature control that is SAFE BY CONSTRUCTION:
4// - unsafe setpoints are clamped below culture-kill (1-2)
5// - command is heat-only (4) and bounded (5-6)
6// - a room-temp vessel is driven into the 43 C yogurt neighborhood (7-8)
7// - temperature NEVER reaches culture-kill dynamically (9, 12)
8// - a 70 C request is clamped to 54 C and the loop converges to the
9// CLAMPED safe temp, never the unsafe target (10-12)
10// Exit code = failed assertion number; 0 = all pass.
11
12import "nx_syscalls.nx"
13import "nx_pid.nx"
14import "nx_ferment_safety.nx"
15import "nx_ferment_thermal.nx"
16
17func main() -> i64 {
18 let env: *NxFermentSafetyEnvelope = nx_ferment_safety_envelope_default(1)
19
20 // --- safety clamp: 60 C requested (> 55 C kill) -> 54 C (kill - 1) ---
21 if nx_ferment_thermal_safe_setpoint(env, 60000) != 54000 { return 1 }
22 // --- a safe setpoint passes through unchanged ---
23 if nx_ferment_thermal_safe_setpoint(env, 43000) != 43000 { return 2 }
24
25 // yogurt controller: target 43 C; gentle, anti-windup-bounded PI.
26 // heater_max 800 * heat_gain 1.0 = max 800 milli-C/step < 1000 margin
27 // => a single step can never cross culture-kill.
28 let t: *NxFermentThermal = nx_ferment_thermal_new(env, 43000,
29 800, 8, 0, 1638400, 800)
30 if t.setpoint_milli_c != 43000 { return 3 }
31
32 // --- heat-only: above setpoint / ceiling -> command 0 ---
33 if nx_ferment_thermal_step(t, 60000) != 0 { return 4 }
34 nx_pid_reset(t.pid)
35
36 // --- below setpoint -> positive, bounded command ---
37 var c0: i64 = nx_ferment_thermal_step(t, 20000)
38 if c0 <= 0 { return 5 }
39 if c0 > t.heater_max { return 6 }
40 nx_pid_reset(t.pid)
41
42 // --- closed-loop sim: ambient 20 C, heat_gain 1.0, loss 160/Q ---
43 var temp: i64 = 20000
44 let amb: i64 = 20000
45 var maxtemp: i64 = temp
46 var step: i64 = 0
47 while step < 400 {
48 let cmd: i64 = nx_ferment_thermal_step(t, temp)
49 temp = nx_ferment_plant_step(temp, cmd, amb, 16384, 160)
50 if temp > maxtemp { maxtemp = temp }
51 step = step + 1
52 }
53 if temp < 38000 { return 7 } // climbed >= 18 C of the 23 C gap
54 if temp > 50000 { return 8 } // did not run away past +7 C
55 if maxtemp >= 55000 { return 9 } // SAFETY: never reached culture-kill
56
57 // --- unsafe 70 C request: clamped to 54 C; loop converges to the
58 // CLAMPED temp and never breaches kill ---
59 let t2: *NxFermentThermal = nx_ferment_thermal_new(env, 70000,
60 800, 8, 0, 1638400, 800)
61 if t2.setpoint_milli_c != 54000 { return 10 }
62 var temp2: i64 = 20000
63 var max2: i64 = temp2
64 var s2: i64 = 0
65 while s2 < 600 {
66 let cmd2: i64 = nx_ferment_thermal_step(t2, temp2)
67 temp2 = nx_ferment_plant_step(temp2, cmd2, amb, 16384, 160)
68 if temp2 > max2 { max2 = temp2 }
69 s2 = s2 + 1
70 }
71 if temp2 < 49000 { return 11 } // reached the clamped-safe neighborhood
72 if max2 >= 55000 { return 12 } // SAFETY: never breached culture-kill
73
74 return 0
75}