Debugging Analog Circuits: Methodical Fault Finding

A systematic approach to circuits that don't work. Learn the debugging workflow (observe, hypothesise, predict, test, fix, verify), power-up checks (current-limited supply, rail voltages, hot components), DC operating-point comparison against expected node voltages, signal tracing stage by stage, divide-and-conquer, fault signatures of opens, shorts and wrong parts, oscillation and instability hunting, noise and hum sources (ground loops, coupling, mains, switching), thermal and intermittent faults, and documenting findings — with Python comparing measured node voltages against a model to diagnose four faults in a CE amplifier.

Most analog faults are simple — a wrong resistor, a reversed diode, a missing ground, an oscillating op-amp — but they hide behind symptoms that look mysterious. Random probing wastes hours; a method finds them in minutes.

The single most powerful technique is to know what every node should be, then measure what it is.

An analogy

Debugging is like a doctor's diagnosis: check vital signs first (supplies, currents, temperatures), compare with what's normal (expected node voltages), form a hypothesis, and run the one test that confirms or rules it out.

First checks

Before anything else:

DC operating point and signal tracing

Compute or simulate expected node voltages and annotate the schematic. Measure each node: deviations point to the faulty region. For op-amp circuits, check that inputs are nearly equal and the output isn't stuck at a rail (loss of feedback, wrong supply, input beyond common-mode range). Then inject a known signal and follow it with a scope; use half-splitting in multi-stage chains.

Oscillation, noise and hum

Unexpected high supply current, warm op-amps, fuzzy DC levels, or DMM readings that change when you touch the probe all suggest oscillation — look with a scope at high bandwidth. Fixes: isolation resistors on capacitive loads, local supply bypassing, feedback capacitors, base-stopper resistors. Hum at 50/60 Hz and harmonics suggests ground loops or magnetic pickup; spikes at the switcher frequency suggest coupling from power stages; broadband noise suggests high impedances or a noisy part.

Intermittent and thermal faults

Use freeze spray and a hot-air gun to find temperature-sensitive faults, gentle flexing for cracked joints or connectors, long logging runs for rare events, and supply-voltage margining. Compare a failing board with a known-good one node by node. Always record what you found and why it happened, so design or production can fix the root cause.

Formulas

Worked example: Transistor state

Given: V_B 2.8 V, V_E 2.1 V, V_C 2.2 V

Find: Region.

Answer: Saturated

Worked example: Current from a drop

Given: 1.5 V across a 4.7 kΩ collector resistor

Find: I_C.

Answer: 0.32 mA

Worked example: Half-split

Given: 8-stage signal chain

Find: Tests to locate one faulty stage.

Answer: 3 measurements

Debugging workflow

  1. Observe:  symptom, conditions, when it started, what changed
  2. Safe power-up: current-limited supply, measure I_supply, feel/IR for hot parts
  3. DC check:  rails at the ICs, then node voltages vs expected (model / simulation)
  4. Signal trace: inject known signal, follow stage by stage (half-split)
  5. Hypothesis → predict → one decisive test (swap, lift a pin, add a probe)
  6. Fix → verify the original symptom AND regressions → document root cause

Discipline beats intuition: checking power and DC operating points catches the majority of faults before any AC measurement. Each test should split the possibilities roughly in half.

Fault signatures from node voltages

# Debugging by node voltages: compare measured DC levels with expected values to locate a fault
VCC, R1, R2, RC, RE, beta, VBE = 12.0, 82e3, 18e3, 5.6e3, 1.2e3, 150, 0.7
Vth, Rth = VCC * R2 / (R1 + R2), R1 * R2 / (R1 + R2)
IB = (Vth - VBE) / (Rth + (beta + 1) * RE)
IC = beta * IB
exp = {"VB": Vth - IB * Rth, "VE": (IC + IB) * RE, "VC": VCC - IC * RC}
print("expected:", {k: round(v, 2) for k, v in exp.items()})
cases = {
    "A": {"VB": 2.16, "VE": 0.00, "VC": 12.00},
    "B": {"VB": 2.16, "VE": 2.12, "VC": 2.12},
    "C": {"VB": 0.81, "VE": 0.11, "VC": 0.40},
    "D": {"VB": 0.00, "VE": 0.00, "VC": 12.00},
}
diagnosis = {
    "A": "no emitter current and VB unloaded -> transistor open (B-E open / wrong pinout)",
    "B": "VC = VE and VB - VE ~ 0 -> collector-emitter short (RC and RE form a plain divider)",
    "C": "VE tiny, VB ~ VE + 0.7, VC ~ VB - 0.4 -> only base current flows: RC or collector open",
    "D": "VB = 0 -> R1 open or base shorted to ground",
}
for k, m in cases.items():
    dev = {n: round(m[n] - exp[n], 2) for n in exp}
    print(f"case {k}: deviations {dev} -> {diagnosis[k]}")
# expected: {'VB': 2.05, 'VE': 1.35, 'VC': 5.74}
# case A: deviations {'VB': 0.11, 'VE': -1.35, 'VC': 6.26} -> no emitter current and VB unloaded -> transistor open (B-E open / wrong pinout)
# case B: deviations {'VB': 0.11, 'VE': 0.77, 'VC': -3.62} -> VC = VE and VB - VE ~ 0 -> collector-emitter short (RC and RE form a plain divider)
# case C: deviations {'VB': -1.24, 'VE': -1.24, 'VC': -5.34} -> VE tiny, VB ~ VE + 0.7, VC ~ VB - 0.4 -> only base current flows: RC or collector open
# case D: deviations {'VB': -2.05, 'VE': -1.35, 'VC': 6.26} -> VB = 0 -> R1 open or base shorted to ground

The model predicts V_B 2.05 V, V_E 1.35 V, V_C 5.74 V. Each fault leaves a distinctive fingerprint: an unloaded base divider with no emitter current means an open transistor; V_C = V_E means a C–E short; tiny V_E with V_B ≈ V_E + 0.7 means only base current flows (collector path open); V_B = 0 points to the bias resistor. Comparing patterns, not single numbers, identifies the fault.

Common mistakes

Applications

Key takeaways

Going further