nx_qc_spc.nx source
↩ module page · 111 lines · 3776 B
1// nx_qc_spc.nx -- FOOD-SCIENCE SUITE / QUALITY CONTROL rung. Statistical
2// process control for a production line: Shewhart X-bar / R control charts,
3// process capability (Cp / Cpk), and proximate-analysis conservation +
4// Kjeldahl protein. Turns "looks fine" into a measured, in-/out-of-control
5// verdict and a capability index.
6//
7// INTEGER-EXACT via the range method: sigma is estimated as Rbar/d2 using
8// the tabulated Shewhart constants (x1000), so NO square root is needed.
9// Control limits carried in milli-units for precision; Cp/Cpk x1000.
10//
11// X-bar: UCL/LCL = Xbarbar +/- A2 * Rbar
12// R: UCL = D4 * Rbar, LCL = D3 * Rbar
13// Cp = (USL-LSL)/(6 sigma), sigma = Rbar/d2
14// Cpk = min(USL-mean, mean-LSL)/(3 sigma) (capable if Cpk >= 1.33)
15//
16// THE exceed: an out-of-control point and a capability index are COMPUTED
17// from the subgroup ranges, not eyeballed; and proximate composition is
18// conserved to 100% by construction.
19//
20// grounded: shewhart_control_charts + astm_process_capability_cpk
21// + kjeldahl_nitrogen_to_protein_6.25
22// genealogy_id: statistical_process_control + nishi_food_science_suite
23
24import "nx_syscalls.nx"
25const QC_MAGIC_2282: i64 = 2282
26const QC_MAGIC_2114: i64 = 2114
27const QC_MAGIC_2059: i64 = 2059
28const QC_MAGIC_2326: i64 = 2326
29
30const QC_CAPABLE_CPK_MILLI: i64 = 1330 // Cpk >= 1.33 = capable
31const QC_KJELDAHL_FACTOR_X100: i64 = 625 // general N->protein factor 6.25
32
33// ===== Shewhart constants (x1000) for subgroup size n =============
34
35func qc_a2(n: i64) -> i64 {
36 if n == 4 { return 729 }
37 if n == 5 { return 577 }
38 return 577
39}
40func qc_d3(n: i64) -> i64 {
41 return 0 // 0 for n <= 6
42}
43func qc_d4(n: i64) -> i64 {
44 if n == 4 { return QC_MAGIC_2282 }
45 if n == 5 { return QC_MAGIC_2114 }
46 return QC_MAGIC_2114
47}
48func qc_d2(n: i64) -> i64 {
49 if n == 4 { return QC_MAGIC_2059 }
50 if n == 5 { return QC_MAGIC_2326 }
51 return QC_MAGIC_2326
52}
53
54// ===== X-bar / R control limits (milli-units) =====================
55
56func qc_xbar_ucl_milli(xbarbar: i64, a2: i64, rbar: i64) -> i64 {
57 return xbarbar * 1000 + a2 * rbar
58}
59func qc_xbar_lcl_milli(xbarbar: i64, a2: i64, rbar: i64) -> i64 {
60 return xbarbar * 1000 - a2 * rbar
61}
62
63// Is a subgroup mean x (base units) within the control limits?
64func qc_in_control(x: i64, ucl_milli: i64, lcl_milli: i64) -> i64 {
65 let xm: i64 = x * 1000
66 if xm > ucl_milli { return 0 }
67 if xm < lcl_milli { return 0 }
68 return 1
69}
70
71func qc_r_ucl_milli(d4: i64, rbar: i64) -> i64 {
72 return d4 * rbar
73}
74
75// ===== Process capability (x1000) =================================
76
77func qc_cp_milli(usl: i64, lsl: i64, d2: i64, rbar: i64) -> i64 {
78 if rbar <= 0 { return 0 }
79 return (usl - lsl) * d2 / (6 * rbar)
80}
81
82func qc_cpk_milli(usl: i64, lsl: i64, mean: i64, d2: i64, rbar: i64) -> i64 {
83 if rbar <= 0 { return 0 }
84 var a: i64 = usl - mean
85 let b: i64 = mean - lsl
86 if b < a { a = b }
87 return a * d2 / (3 * rbar)
88}
89
90func qc_is_capable(cpk_milli: i64) -> i64 {
91 if cpk_milli >= QC_CAPABLE_CPK_MILLI { return 1 }
92 return 0
93}
94
95// ===== Proximate analysis + Kjeldahl ==============================
96
97// Carbohydrate by difference: 100 - (moisture + protein + fat + ash).
98func qc_carb_by_difference(moist: i64, protein: i64, fat: i64, ash: i64) -> i64 {
99 return 100 - moist - protein - fat - ash
100}
101
102// Proximate fractions must conserve to 100%.
103func qc_proximate_conserves(moist: i64, protein: i64, fat: i64, ash: i64, carb: i64) -> i64 {
104 if moist + protein + fat + ash + carb == 100 { return 1 }
105 return 0
106}
107
108// Kjeldahl: crude protein % = nitrogen % x 6.25 (nitrogen given x10).
109func qc_protein_from_nitrogen(nitrogen_x10: i64) -> i64 {
110 return nitrogen_x10 * QC_KJELDAHL_FACTOR_X100 / 100
111}