How to Build a PPAP Test Package with TofuPilot
Your customer sent a PPAP request. You need control plans, Cpk reports, MSA studies, and dimensional data, all tied to specific part numbers and production runs. Most teams spend days pulling this together from spreadsheets and disconnected systems.
TofuPilot captures the test data you need for PPAP submissions as part of your normal production workflow. No extra data entry required.
What Is PPAP
Production Part Approval Process (PPAP) is a standardized framework, defined in AIAG's PPAP manual, that proves your manufacturing process consistently produces parts meeting customer specifications. It's required across automotive (IATF 16949), and increasingly adopted in aerospace, medical devices, and industrial electronics.
A PPAP submission typically includes 18 elements. Several depend directly on test data:
| PPAP Element | What It Needs | TofuPilot Source |
|---|---|---|
| Control Plan | Test steps, limits, frequencies | Test procedure definitions |
| Process Capability Study | Cpk/Ppk for critical dimensions | Measurement history |
| MSA / Gage R&R | Measurement system analysis | Repeated measurement data |
| Dimensional Results | Measured vs. spec for each characteristic | Run measurements with limits |
| Material/Performance Test Results | Functional test pass/fail with data | Test run results |
Prerequisites
- A TofuPilot account with production test data
- Python 3.8+ with the
tofupilotclient installed - At least 30 production runs for process capability calculations
Step 1: Structure Your Tests Around Critical Characteristics
PPAP reviewers care about Critical-to-Quality (CTQ) characteristics. Map each CTQ to a measurement in your test procedure.
import openhtf as htf
from openhtf.util import units
# Map each CTQ characteristic to a measurement with spec limits
@htf.measures(
htf.Measurement("output_voltage_5v")
.with_units(units.VOLT)
.in_range(4.95, 5.05)
.doc("CTQ-001: 5V rail accuracy"),
htf.Measurement("current_draw_idle")
.with_units(units.AMPERE)
.in_range(0.0, 0.150)
.doc("CTQ-002: Idle current consumption"),
htf.Measurement("rise_time_ms")
.with_units(units.MILLISECOND)
.at_most(5.0)
.doc("CTQ-003: Power-on rise time"),
)
def power_rail_test(test):
# Your instrument control code here
test.measurements.output_voltage_5v = 5.01
test.measurements.current_draw_idle = 0.098
test.measurements.rise_time_ms = 3.2Use the .doc() field to store the CTQ identifier. This makes it easy to trace from PPAP documentation back to actual test results.
Step 2: Collect Process Capability Data
PPAP Level 3 submissions require process capability studies. You need at least 30 consecutive parts (AIAG recommends 300 for Ppk).
TofuPilot calculates Cp and Cpk automatically from your measurement data. To pull capability data for your PPAP package:
from tofupilot import TofuPilotClient
client = TofuPilotClient()
# Get runs for a specific part number and procedure
runs = client.get_runs(
procedure_id="power-board-fct",
unit_part_number="PWR-200-R3",
limit=300,
)
# Extract measurement values for each CTQ
voltage_values = [
r.measurements["output_voltage_5v"].value
for r in runs
if r.measurements.get("output_voltage_5v")
]
print(f"Sample size: {len(voltage_values)}")
print(f"Mean: {sum(voltage_values) / len(voltage_values):.4f}")TofuPilot's dashboard shows Cpk directly on measurement charts. Screenshot those for your submission, or export the raw data.
Step 3: Run Gage R&R Studies
Measurement System Analysis (MSA) proves your test equipment is capable. A typical Gage R&R study uses 10 parts, 3 operators, 3 trials each.
import openhtf as htf
from tofupilot import TofuPilotClient
client = TofuPilotClient()
# Tag each run with operator and trial info for MSA
for operator in ["OP-A", "OP-B", "OP-C"]:
for trial in range(1, 4):
for part_sn in part_serial_numbers:
# Run the test
result = run_test(part_sn)
# Upload with metadata for MSA grouping
client.create_run(
procedure_id="gage-rr-voltage",
unit_under_test={
"serial_number": part_sn,
"part_number": "PWR-200-R3",
},
run_passed=result.passed,
measurements=result.measurements,
properties={
"operator": operator,
"trial": trial,
"study": "MSA-2026-Q1",
},
)Filter by the study property in TofuPilot to pull all Gage R&R data for analysis.
Step 4: Generate the Control Plan
Your control plan documents what you test, how you test it, and what limits you use. Pull this directly from your test definitions:
from tofupilot import TofuPilotClient
client = TofuPilotClient()
# Get procedure definition with all measurements
procedure = client.get_procedure("power-board-fct")
print("| CTQ | Measurement | Lower Limit | Upper Limit | Unit | Method |")
print("|-----|-------------|-------------|-------------|------|--------|")
for m in procedure.measurements:
print(
f"| {m.doc or '-'} | {m.name} | "
f"{m.lower_limit or '-'} | {m.upper_limit or '-'} | "
f"{m.unit or '-'} | Automated FCT |"
)This table goes directly into your PPAP control plan. When limits change, regenerate it from the same source of truth.
Step 5: Package Dimensional and Performance Results
PPAP requires actual measured values for a sample of parts (typically 5 from a significant production run).
from tofupilot import TofuPilotClient
client = TofuPilotClient()
# Get the first 5 passing runs from the initial production run
sample_runs = client.get_runs(
procedure_id="power-board-fct",
unit_part_number="PWR-200-R3",
passed=True,
limit=5,
)
for run in sample_runs:
print(f"SN: {run.unit.serial_number}")
for name, m in run.measurements.items():
status = "PASS" if m.passed else "FAIL"
print(f" {name}: {m.value} {m.unit or ''} [{status}]")Maintaining Your PPAP Package
PPAP isn't a one-time exercise. When you change your process, you may need to resubmit.
Track what triggers a PPAP update:
- Engineering change: New part revision, updated test limits
- Process change: New test equipment, different station configuration
- Supplier change: New component source affecting test characteristics
Because TofuPilot versions your test procedures and tracks station configurations, you can always trace which PPAP submission maps to which test setup. When a customer asks "what changed since your last submission," the answer is in your run history.