Cpk assumes a measurement has both an upper and a lower specification limit. Plenty of real measurements have only one. Leakage current has a maximum and no minimum. Insulation resistance has a minimum and no maximum. CPL and CPU are the one-sided capability indices for exactly these cases.
Definitions
CPU (capability, upper) measures how much room sits between the process and the upper specification limit:
CPU = (USL - mean) / (3σ)
CPL (capability, lower) measures the room above the lower specification limit:
CPL = (mean - LSL) / (3σ)
Cpk is simply the worse of the two when both limits exist:
Cpk = min(CPU, CPL)
So CPL and CPU are not a different tool from Cpk. They are its two halves, and for a one-sided measurement only one half is defined.
Worked Example
A board's quiescent current has a maximum of 45 mA and no meaningful minimum. From 200 production units:
| Statistic | Value |
|---|---|
| Mean | 38.0 mA |
| Standard deviation | 1.8 mA |
| USL | 45 mA |
| LSL | none |
CPU = (45 - 38.0) / (3 × 1.8) = 7.0 / 5.4 = 1.30
CPU of 1.30 means about 4.3 standard deviations of headroom to the limit. Reporting a Cpk here would be wrong, because there is no lower limit for the calculation to use.
Interpreting the Number
| Value | Meaning | Typical response |
|---|---|---|
| < 1.00 | Process spread exceeds the available room | Escapes are routine; fix the process or the design |
| 1.00 - 1.33 | Marginal | Watch it; a small drift causes failures |
| 1.33 - 1.67 | Generally acceptable | Standard target for production |
| 1.67 - 2.00 | Comfortable | Consider whether the limit is too loose |
| > 2.00 | Very comfortable | The measurement may not be discriminating |
A very high CPU deserves a question rather than celebration: either the process is genuinely excellent, or the limit was set so loosely that the test cannot fail, in which case it is consuming cycle time without protecting anything.
Where One-Sided Limits Show Up
| Measurement | Limit type | Index |
|---|---|---|
| Leakage current | Maximum only | CPU |
| Quiescent current | Maximum only | CPU |
| Hipot leakage | Maximum only | CPU |
| Boot time | Maximum only | CPU |
| Harmonic distortion | Maximum only | CPU |
| BGA void percentage | Maximum only | CPU |
| Insulation resistance | Minimum only | CPL |
| Pull / bond strength | Minimum only | CPL |
| Battery capacity | Minimum only | CPL |
| Transmit power | Minimum only | CPL |
| RF sensitivity margin | Minimum only | CPL |
Recording a One-Sided Measurement
The index can only be computed if the data says the limit is one-sided. In a procedure, that means declaring a single validator rather than inventing a second bound.
procedure.yaml33 lines
name: Board Power Testversion: 1.0.0unit: serial_number: default_value: "PCBA000001" part_number: default_value: "PCBA-100"plugs: - name: dmm python: plugs.dmm:Multimetermain: - name: Quiescent Current python: phases.quiescent_current measurements: - name: Quiescent Current unit: mA description: Upper limit only; CPU applies validators: - operator: "<=" expected_value: 45.0 - name: Insulation Resistance python: phases.insulation measurements: - name: Insulation Resistance unit: "MΩ" description: Lower limit only; CPL applies validators: - operator: ">=" expected_value: 100.0def quiescent_current(measurements, dmm): measurements.quiescent_current = dmm.measure_dc_current_ma()def insulation(measurements, dmm): measurements.insulation_resistance = dmm.measure_insulation_mohm()class Multimeter: def measure_dc_current_ma(self) -> float: return 38.2 # Replace with a real instrument read def measure_insulation_mohm(self) -> float: return 480.0 # Replace with a real instrument readThis is the part teams most often get wrong. Adding a fabricated lower limit to a leakage measurement, so that a Cpk number appears, produces a figure that describes nothing. The honest record has one validator, and the analysis reports CPU.
Calculating It From Your Data
analysis/one_sided.py21 lines
import statisticsdef cpu(values: list[float], usl: float) -> float: """Capability against an upper specification limit.""" sigma = statistics.stdev(values) if sigma == 0: raise ValueError("Zero variation; check the measurement resolution") return (usl - statistics.mean(values)) / (3 * sigma)def cpl(values: list[float], lsl: float) -> float: """Capability against a lower specification limit.""" sigma = statistics.stdev(values) if sigma == 0: raise ValueError("Zero variation; check the measurement resolution") return (statistics.mean(values) - lsl) / (3 * sigma)currents = [38.1, 37.6, 39.0, 38.4, 37.9, 38.8, 38.2]print(f"CPU = {cpu(currents, usl=45.0):.2f}")Two cautions on the inputs:
Zero standard deviation is a measurement problem, not perfection. It usually means the instrument's resolution is coarser than the real variation, and the index is meaningless.
Capability assumes a stable process. Calculating CPU across a period that includes a process change averages two different distributions into one number that describes neither. Check the control chart first; capability is only meaningful once the process is in control.
Ppl and Ppu
CPL and CPU use short-term, within-subgroup variation. The long-term equivalents, using the overall standard deviation across everything produced, are PPL and PPU. The formulas are identical; only the sigma differs. See Ppl and Ppu and Cpk vs Ppk.
If You Already Run OpenHTF
A one-sided limit is expressed with a single bound, and the same rule applies about not inventing the other one:
import openhtf as htffrom openhtf.util import unitsfrom tofupilot.openhtf import upload@htf.measures( htf.Measurement("quiescent_current_mA") .in_range(maximum=45.0) .with_units(units.MILLIAMPERE) .doc("Upper limit only; CPU applies"),)def phase_quiescent(test): test.measurements.quiescent_current_mA = 38.2test = htf.Test(phase_quiescent, part_number="PCBA-100")test.add_output_callbacks(upload())test.execute(test_start=lambda: input("Scan serial: "))Install with pip install "tofupilot[openhtf]", or skip the callback and run it with tofupilot run.
Common Mistakes
| Mistake | Consequence | Fix |
|---|---|---|
| Inventing a second limit to get a Cpk | A capability number that describes nothing | Declare one validator; report CPU or CPL |
| Reporting Cpk for a one-sided measurement | Misleading, and usually wrong by construction | Use the index that matches the limit |
| Computing capability on an unstable process | The number averages two distributions | Check the control chart first |
| Ignoring a very high CPU | A test that cannot fail still costs cycle time | Review whether the limit is meaningful |
| Sigma of zero treated as excellent | The instrument cannot resolve the variation | Check measurement resolution |
