Skip to content
Test Types & Methods

EMC Pre-Compliance Testing: A Complete Guide

Learn how to set up EMC pre-compliance tests, log radiated and conducted emissions data, and track EMC margins with TofuPilot.

JJulien Buteau
advanced12 min readMarch 14, 2026

EMC Pre-Compliance Testing: A Complete Guide

Failing EMC certification costs weeks of delay and tens of thousands in retest fees. Pre-compliance testing in your own lab catches problems early, when they're cheap to fix. TofuPilot tracks your EMC test data so you can monitor margins, compare design revisions, and build confidence before the formal test.

What EMC Testing Covers

TestStandardWhat it measures
Radiated emissionsCISPR 32, FCC Part 15RF energy radiated from the product
Conducted emissionsCISPR 32, FCC Part 15RF noise on power lines
Radiated immunityIEC 61000-4-3Resistance to external RF fields
ESD immunityIEC 61000-4-2Resistance to electrostatic discharge
Surge immunityIEC 61000-4-5Resistance to power line surges
Conducted immunityIEC 61000-4-6Resistance to RF on cables

Pre-compliance focuses on emissions (radiated and conducted) because they're the most common failure modes and can be tested with relatively affordable equipment.

Pre-Compliance Equipment

EquipmentPurposeTypical cost
Near-field probe setLocate emission sources on PCB$500-2,000
Spectrum analyzerMeasure frequency content$5,000-30,000
LISN (Line Impedance Stabilization Network)Conducted emissions measurement$2,000-5,000
EMC antenna (biconical + log-periodic)Radiated emissions measurement$3,000-8,000
Shielded enclosure or open area test siteControlled measurement environment$10,000+

You don't need a fully equipped chamber for pre-compliance. A spectrum analyzer with near-field probes catches most issues at the board level.

Logging EMC Data to TofuPilot

Radiated Emissions Scan

emc_radiated_test.py
from tofupilot import TofuPilotClient

client = TofuPilotClient()

# Peak emissions at key frequencies
emissions = [
    {"freq_mhz": 30, "level_dbuv_m": 28.5, "limit_dbuv_m": 40.0},
    {"freq_mhz": 100, "level_dbuv_m": 32.1, "limit_dbuv_m": 43.5},
    {"freq_mhz": 230, "level_dbuv_m": 35.8, "limit_dbuv_m": 46.0},
    {"freq_mhz": 500, "level_dbuv_m": 22.3, "limit_dbuv_m": 46.0},
    {"freq_mhz": 1000, "level_dbuv_m": 18.7, "limit_dbuv_m": 50.0},
]

measurements = []
all_pass = True
for e in emissions:
    margin = e["limit_dbuv_m"] - e["level_dbuv_m"]
    passed = margin > 0
    if not passed:
        all_pass = False
    measurements.append({
        "name": f"radiated_{e['freq_mhz']}mhz",
        "value": e["level_dbuv_m"],
        "unit": "dBuV/m",
        "limit_high": e["limit_dbuv_m"],
    })
    measurements.append({
        "name": f"margin_{e['freq_mhz']}mhz",
        "value": margin,
        "unit": "dB",
        "limit_low": 6.0,  # 6dB margin target
    })

client.create_run(
    procedure_id="EMC-RADIATED-PRECOMPLIANCE",
    unit_under_test={
        "serial_number": "PROTO-REV-C",
        "part_number": "PRODUCT-V2",
    },
    run_passed=all_pass,
    steps=[{
        "name": "Radiated Emissions 30MHz-1GHz",
        "step_type": "measurement",
        "status": all_pass,
        "measurements": measurements,
    }],
)

Conducted Emissions

emc_conducted_test.py
# Conducted emissions on power input lines
conducted = [
    {"freq_mhz": 0.15, "level_dbuv": 52.3, "limit_avg": 56.0, "limit_qp": 66.0},
    {"freq_mhz": 0.5, "level_dbuv": 48.7, "limit_avg": 46.0, "limit_qp": 56.0},
    {"freq_mhz": 5.0, "level_dbuv": 38.2, "limit_avg": 46.0, "limit_qp": 56.0},
    {"freq_mhz": 30.0, "level_dbuv": 32.1, "limit_avg": 46.0, "limit_qp": 56.0},
]

measurements = []
for c in conducted:
    measurements.append({
        "name": f"conducted_avg_{c['freq_mhz']}mhz",
        "value": c["level_dbuv"],
        "unit": "dBuV",
        "limit_high": c["limit_avg"],
    })

client.create_run(
    procedure_id="EMC-CONDUCTED-PRECOMPLIANCE",
    unit_under_test={"serial_number": "PROTO-REV-C"},
    run_passed=True,
    steps=[{
        "name": "Conducted Emissions 150kHz-30MHz",
        "step_type": "measurement",
        "status": True,
        "measurements": measurements,
    }],
)

Tracking EMC Margins

The goal of pre-compliance isn't just pass/fail. It's knowing your margin. A product that passes with 2dB of margin will likely fail at the test house (measurement uncertainty alone is 3-5dB).

MarginRisk levelAction
> 10 dBLowProceed to certification
6-10 dBMediumProceed, but have mitigation plans ready
3-6 dBHighFix before certification
< 3 dBVery highWill likely fail certification

Track margins in TofuPilot across design revisions. If Rev A had 4dB margin at 230MHz and Rev B has 8dB after adding a filter, you can see the improvement quantitatively.

Comparing Design Revisions

EMC performance changes with every board revision, layout change, and component swap. TofuPilot lets you compare emissions data across revisions side by side.

Rev A (no filter): Rev B (added pi filter): ┌─────────────────────┐ ┌─────────────────────┐ │ 230MHz: 42.1 dBuV/m │ │ 230MHz: 35.8 dBuV/m │ │ Limit: 46.0 dBuV/m │ │ Limit: 46.0 dBuV/m │ │ Margin: 3.9 dB ⚠ │ │ Margin: 10.2 dB ✓ │ └─────────────────────┘ └─────────────────────┘

The pi filter bought 6.3dB of margin at 230MHz. This kind of data-driven design feedback is only possible when you track every test iteration.

Common EMC Failure Modes

FailureTypical causeFix
Harmonics of switching frequencyDC-DC converter, clock oscillatorAdd input/output filtering, spread-spectrum clocking
Cable-radiated emissionsUnshielded cables acting as antennasAdd common-mode chokes, use shielded cables
High-frequency broadbandFast digital edges, poor groundingSlow edge rates, improve ground plane
Low-frequency conductedSwitching power supply rippleAdd differential-mode filter on input

Track which failure modes appear across products in TofuPilot. If every product fails at the 3rd harmonic of the switching frequency, standardize your input filter design.

ESD Testing

ESD is the most common immunity test failure. Log ESD test results systematically.

esd_test.py
esd_levels = [
    {"mode": "contact", "voltage_kv": 4, "result": "pass"},
    {"mode": "contact", "voltage_kv": 6, "result": "pass"},
    {"mode": "contact", "voltage_kv": 8, "result": "fail_recoverable"},
    {"mode": "air", "voltage_kv": 8, "result": "pass"},
    {"mode": "air", "voltage_kv": 15, "result": "pass"},
]

measurements = []
for test in esd_levels:
    passed = test["result"] == "pass"
    measurements.append({
        "name": f"esd_{test['mode']}_{test['voltage_kv']}kv",
        "value": 1 if passed else 0,
        "unit": "pass/fail",
        "limit_low": 1,
    })

client.create_run(
    procedure_id="EMC-ESD-PRECOMPLIANCE",
    unit_under_test={"serial_number": "PROTO-REV-C"},
    run_passed=all(t["result"] == "pass" for t in esd_levels),
    steps=[{
        "name": "ESD Immunity per IEC 61000-4-2",
        "step_type": "measurement",
        "status": True,
        "measurements": measurements,
    }],
)

From Pre-Compliance to Certification

Pre-compliance data in TofuPilot serves as your engineering notebook for EMC. When you arrive at the test house:

  • You know your margins at every frequency
  • You know which design changes improved or degraded performance
  • You have a history of every test iteration
  • If you fail, you have data to guide the fix instead of starting from scratch

More Guides

Put this guide into practice