code wiki / (root) / nx_ac_fdd_test.nx

nx_ac_fdd_test.nx source

↩ module page · 157 lines · 5315 B

1// nx_ac_fdd_test.nx -- gate for the refrigerant/air-side FDD verdict. 2// 3// Proves each published fault signature (NIST AFDD / ASHRAE APAR) fires under 4// Title-24-default thresholds, precedence holds (frozen coil first), and the 5// verdict TRACKS the inputs (liar-kill: same high superheat, low vs high 6// subcooling -> undercharge vs liquid-restriction, not a constant). 7// 8// expect_exit: 0 9// license_tier: ORIGINAL 10 11import "nx_syscalls_x86_64.nx" 12import "nx_ac_fdd.nx" 13 14// nominal reading -> NOMINAL verdict; each test overrides one channel 15func fdd_reset_in(p: *FddInput) -> i64 { 16 p.superheat_mC = 8333 17 p.subcool_mC = 5556 18 p.suction_sat_mC = 5000 19 p.evap_split_mC = 11000 20 p.cond_over_ambient_mC = 15000 21 p.cfm_per_ton = 400 22 p.cph_x10 = 20 23 return 0 24} 25 26// Title 24 / NIST default thresholds 27func fdd_reset_th(t: *FddThresh) -> i64 { 28 t.target_superheat_mC = 8333 29 t.superheat_band_mC = 2778 30 t.target_subcool_mC = 5556 31 t.subcool_band_mC = 2778 32 t.split_ceiling_mC = 13889 33 t.cond_over_ambient_ceiling_mC = 25000 34 t.cfm_per_ton_floor = 350 35 t.max_cph_x10 = 35 36 t.freeze_suction_sat_mC = 0 37 return 0 38} 39 40func main() -> i64 { 41 if nx_fdd_verdict_is_valid(NX_FDD_NOMINAL) != 1 { return 1 } 42 if nx_fdd_verdict_is_valid(NX_FDD_N) != 0 { return 2 } 43 if nx_fdd_verdict_is_valid(-1) != 0 { return 3 } 44 45 let rd: *FddInput = sys_mmap(128) as *FddInput 46 let th: *FddThresh = sys_mmap(128) as *FddThresh 47 let o: *FddOut = sys_mmap(64) as *FddOut 48 fdd_reset_th(th) 49 50 // ===== NOMINAL baseline ========================================= 51 fdd_reset_in(rd) 52 nx_ac_fdd_analyze(rd, th, o) 53 if o.verdict != NX_FDD_NOMINAL { return 10 } 54 55 // ===== UNDERCHARGE: high superheat + low subcooling ============= 56 fdd_reset_in(rd) 57 rd.superheat_mC = 17000 58 rd.subcool_mC = 1500 59 nx_ac_fdd_analyze(rd, th, o) 60 if o.verdict != NX_FDD_UNDERCHARGE { return 20 } 61 if o.sh_high != 1 { return 21 } 62 if o.sc_low != 1 { return 22 } 63 64 // ===== UNDERCHARGE with subcool sensor ABSENT =================== 65 fdd_reset_in(rd) 66 rd.superheat_mC = 17000 67 rd.subcool_mC = NX_FDD_ABSENT 68 nx_ac_fdd_analyze(rd, th, o) 69 if o.verdict != NX_FDD_UNDERCHARGE { return 25 } 70 71 // ===== LIQUID_RESTRICTION: high superheat AND high subcooling === 72 fdd_reset_in(rd) 73 rd.superheat_mC = 17000 74 rd.subcool_mC = 11000 75 nx_ac_fdd_analyze(rd, th, o) 76 if o.verdict != NX_FDD_LIQUID_RESTRICTION { return 30 } 77 if o.sh_high != 1 { return 31 } 78 if o.sc_high != 1 { return 32 } 79 80 // ===== OVERCHARGE: high subcooling, superheat not high ========== 81 fdd_reset_in(rd) 82 rd.superheat_mC = 6000 83 rd.subcool_mC = 12000 84 nx_ac_fdd_analyze(rd, th, o) 85 if o.verdict != NX_FDD_OVERCHARGE { return 40 } 86 87 // ===== FOULED_CONDENSER: condensing far above ambient =========== 88 fdd_reset_in(rd) 89 rd.superheat_mC = NX_FDD_ABSENT 90 rd.subcool_mC = NX_FDD_ABSENT 91 rd.cond_over_ambient_mC = 30000 92 nx_ac_fdd_analyze(rd, th, o) 93 if o.verdict != NX_FDD_FOULED_CONDENSER { return 50 } 94 95 // ===== LOW_AIRFLOW: high evaporator split ======================= 96 fdd_reset_in(rd) 97 rd.evap_split_mC = 16000 98 nx_ac_fdd_analyze(rd, th, o) 99 if o.verdict != NX_FDD_LOW_AIRFLOW { return 60 } 100 101 // ===== LOW_AIRFLOW: CFM/ton below the 350 floor ================= 102 fdd_reset_in(rd) 103 rd.evap_split_mC = 11000 104 rd.cfm_per_ton = 300 105 nx_ac_fdd_analyze(rd, th, o) 106 if o.verdict != NX_FDD_LOW_AIRFLOW { return 65 } 107 108 // ===== FROZEN_COIL: suction saturation below 0 C ================ 109 fdd_reset_in(rd) 110 rd.suction_sat_mC = -2000 111 nx_ac_fdd_analyze(rd, th, o) 112 if o.verdict != NX_FDD_FROZEN_COIL { return 70 } 113 114 // ===== SHORT_CYCLING: cycles/hr over ceiling ==================== 115 fdd_reset_in(rd) 116 rd.cph_x10 = 120 117 nx_ac_fdd_analyze(rd, th, o) 118 if o.verdict != NX_FDD_SHORT_CYCLING { return 80 } 119 120 // ===== INSUFFICIENT_DATA: no channels =========================== 121 rd.superheat_mC = NX_FDD_ABSENT 122 rd.subcool_mC = NX_FDD_ABSENT 123 rd.suction_sat_mC = NX_FDD_ABSENT 124 rd.evap_split_mC = NX_FDD_ABSENT 125 rd.cond_over_ambient_mC = NX_FDD_ABSENT 126 rd.cfm_per_ton = NX_FDD_ABSENT 127 rd.cph_x10 = NX_FDD_ABSENT 128 nx_ac_fdd_analyze(rd, th, o) 129 if o.verdict != NX_FDD_INSUFFICIENT_DATA { return 90 } 130 131 // ===== PRECEDENCE: frozen coil beats a charge fault ============= 132 fdd_reset_in(rd) 133 rd.suction_sat_mC = -2000 134 rd.superheat_mC = 17000 135 rd.subcool_mC = 11000 136 nx_ac_fdd_analyze(rd, th, o) 137 if o.verdict != NX_FDD_FROZEN_COIL { return 100 } 138 139 // ===== LIAR-KILL: verdict tracks subcooling ===================== 140 // identical high superheat; only subcooling changes -> the diagnosis 141 // must change (undercharge vs liquid restriction), not stay constant. 142 fdd_reset_in(rd) 143 rd.superheat_mC = 17000 144 rd.subcool_mC = 1500 145 nx_ac_fdd_analyze(rd, th, o) 146 let v_low_sc: i64 = o.verdict 147 fdd_reset_in(rd) 148 rd.superheat_mC = 17000 149 rd.subcool_mC = 11000 150 nx_ac_fdd_analyze(rd, th, o) 151 let v_high_sc: i64 = o.verdict 152 if v_low_sc != NX_FDD_UNDERCHARGE { return 110 } 153 if v_high_sc != NX_FDD_LIQUID_RESTRICTION { return 111 } 154 if v_low_sc == v_high_sc { return 112 } 155 156 return 0 157}