Distortion, Linearity & Dynamic Range

How nonlinearity corrupts signals. Learn linear vs nonlinear transfer functions, harmonic distortion (HD2, HD3, THD, THD+N), intermodulation distortion (IMD, IP3), sources of distortion (exponential V_BE, square-law MOSFETs, clipping, slew limiting, crossover, capacitor voltage coefficient, thermal), even vs odd harmonics and differential cancellation, dynamic range, SINAD, SFDR and ENOB, and techniques to improve linearity (degeneration, feedback, differential circuits, predistortion, better components) — with Python computing harmonics of a BJT stage and a differential pair by DFT.

A perfectly linear circuit outputs only the frequencies it receives. Real devices bend the signal slightly, creating harmonics and intermodulation products that add false tones, audible harshness, adjacent-channel interference or measurement errors.

Noise sets the smallest signal you can handle; distortion sets the largest. Between them lies the dynamic range.

An analogy

Distortion is like a funhouse mirror: a sine wave (a person) goes in and comes out stretched in some places — the stretching can be described as extra 'harmonic' shapes added to the original reflection.

What distortion looks like

On a scope: flattened tops (clipping), asymmetric halves (even harmonics), a notch at zero crossings (crossover), triangular slopes (slew limiting). On a spectrum analyser: extra lines at 2f, 3f, … and IMD products. Audio uses THD+N in %; RF uses dBc and intercept points; data converters use SFDR, SINAD and ENOB.

Sources of distortion

Common culprits:

Improving linearity

Keep signals small relative to device nonlinearities (degeneration spreads the signal across a linear resistor), use differential topologies to cancel even orders, apply negative feedback (distortion ÷ (1 + T), but only where loop gain is high — it falls at high frequency), choose linear components (C0G/film capacitors, metal-film resistors), operate away from rails and slew limits, and use digital predistortion in RF power amplifiers.

Dynamic range metrics

Dynamic range spans from the noise floor to the maximum undistorted signal. SNR excludes harmonics; SINAD includes them (and gives ENOB); SFDR is the distance from the carrier to the largest spur — often the limit in radio receivers and spectrum analysers. In RF, IP3 (the extrapolated point where IMD3 equals the fundamental) and P1dB characterise linearity; spurious-free dynamic range ≈ (2/3)(IIP3 − noise floor).

Formulas

Worked example: THD from harmonics

Given: HD2 1%, HD3 0.5%

Find: THD.

Answer: 1.12%

Worked example: ENOB

Given: SINAD 74 dB

Find: ENOB.

Answer: ≈ 12.0 bits

Worked example: Feedback effect

Given: Open-loop THD 3%, loop gain 300

Find: Closed-loop THD.

Answer: ≈ 0.01%

Harmonic and intermodulation products

  Input: f1 (and f2 for two-tone)
  y = a1·x + a2·x² + a3·x³ + …
  a2 → 2f1, f2 ± f1, DC shift          (even order: cancelled by differential circuits)
  a3 → 3f1, 2f1 − f2, 2f2 − f1          (odd order: IMD3 falls in-band, hard to filter)
  Spectrum:  |f1|  ·  |2f1| · |3f1| …   THD = √(ΣV_harm²)/V_fund

Expanding the transfer function in a power series shows which products appear. Second-order terms create harmonics and sum/difference tones; third-order terms create products right next to the wanted signals (IMD3), which filtering cannot remove — the key concern in RF and multi-tone systems.

Harmonics of BJT and differential-pair stages

import math
# Distortion: harmonics of a BJT (exponential) stage computed by DFT, THD, and ADC dynamic metrics
VT = 0.02585
def harmonics(f, N=1024, M=5):
    x = [f(math.sin(2 * math.pi * n / N)) for n in range(N)]
    amps = []
    for h in range(1, M + 1):
        re = sum(x[n] * math.cos(2 * math.pi * h * n / N) for n in range(N)) * 2 / N
        im = sum(x[n] * math.sin(2 * math.pi * h * n / N) for n in range(N)) * 2 / N
        amps.append(math.hypot(re, im))
    return amps
for vp_mv in (1, 5, 10):
    a = harmonics(lambda s: math.exp(vp_mv / 1000 * s / VT))
    thd = math.sqrt(sum(v**2 for v in a[1:])) / a[0]
    print(f"BJT vbe = {vp_mv:2d} mV pk: HD2 {a[1]/a[0]*100:.2f}%, HD3 {a[2]/a[0]*100:.3f}%, THD {thd*100:.2f}%")
print(f"rule of thumb HD2 ~ v/(4VT) = {10/1000/(4*VT)*100:.1f}% at 10 mV")
# Differential pair (tanh) is odd-symmetric: even harmonics cancel
a = harmonics(lambda s: math.tanh(20e-3 * s / (2 * VT)))
print(f"diff pair 20 mV pk: HD2 {a[1]/a[0]*100:.4f}%, HD3 {a[2]/a[0]*100:.2f}%")
# ADC metrics: SINAD -> ENOB; ideal SNR of 16 bits
sinad = 86.0
print(f"SINAD {sinad} dB -> ENOB {(sinad-1.76)/6.02:.2f} bits; ideal 16-bit SNR {6.02*16+1.76:.1f} dB")
# BJT vbe =  1 mV pk: HD2 0.97%, HD3 0.006%, THD 0.97%
# BJT vbe =  5 mV pk: HD2 4.83%, HD3 0.156%, THD 4.83%
# BJT vbe = 10 mV pk: HD2 9.61%, HD3 0.618%, THD 9.63%
# rule of thumb HD2 ~ v/(4VT) = 9.7% at 10 mV
# diff pair 20 mV pk: HD2 0.0000%, HD3 1.20%
# SINAD 86.0 dB -> ENOB 13.99 bits; ideal 16-bit SNR 98.1 dB

A single BJT stage with just 10 mV at the base already produces ~10% second-harmonic distortion — close to the v/(4V_T) rule. The differential pair's odd-symmetric tanh curve cancels even harmonics entirely, leaving 1.2% HD3 at 20 mV. An ADC with 86 dB SINAD behaves like an ideal 14-bit converter, even if it outputs 16 bits.

Common mistakes

Applications

Key takeaways

Going further