aboutsummaryrefslogtreecommitdiff
path: root/module-design.typ
diff options
context:
space:
mode:
Diffstat (limited to 'module-design.typ')
-rw-r--r--module-design.typ808
1 files changed, 736 insertions, 72 deletions
diff --git a/module-design.typ b/module-design.typ
index 5288949..e42f231 100644
--- a/module-design.typ
+++ b/module-design.typ
@@ -2,6 +2,8 @@
#import "lib/apply_prefix.typ": apply-prefix
#import "lib/assert_aeq.typ": assert-aeq
#import "@preview/diverential:0.3.0": *
+#import "@preview/cetz:0.5.2"
+#import "@preview/cetz-plot:0.1.4": plot
= Module design <module-design>
@@ -109,7 +111,10 @@ operators.
table(
columns: 4,
table.header([Symbol], [Definition], [Value], [Unit]),
- [$k_B$], [Boltzmann constant], [$num("1.381e-23")$], [$unit("joule per kelvin")$],
+ [$k_B$],
+ [Boltzmann constant],
+ [$num("1.381e-23")$],
+ [$unit("joule per kelvin")$],
[$T$], [Absolute temperature], [N/A], [$unit(K)$],
[$e$], [Elementary charge], [$num("1.603e-19")$], [$unit(C)$],
@@ -130,9 +135,13 @@ operators.
[$L$], [Inductance], [$unit(H)$],
[$P$], [Power], [$unit(W)$],
[$f$], [Frequency], [$unit("Hz")$],
- [$omega$], [Angular frequency ($omega = 2 pi f$)], [$unit("radian per second")$],
+ [$omega$],
+ [Angular frequency ($omega = 2 pi f$)],
+ [$unit("radian per second")$],
- [$tau$], [Time constant ($tau = upright("RC") "or" upright("L/R")$)], [$unit(s)$],
+ [$tau$],
+ [Time constant ($tau = upright("RC") "or" upright("L/R")$)],
+ [$unit(s)$],
[$upright("BW")$], [Bandwidth], [$unit("Hz")$],
),
@@ -205,9 +214,13 @@ operators.
columns: 3,
table.header([Symbol], [Definition], [Typical unit]),
[$epsilon$], [Error (general)], [$unit("ppm")$ or $unit("uV")$],
- [$epsilon_"max"$], [Maximum allowable error], [$unit("ppm")$ or $unit("uV")$],
+ [$epsilon_"max"$],
+ [Maximum allowable error],
+ [$unit("ppm")$ or $unit("uV")$],
- [$epsilon_"total"$], [Total combined error], [$unit("ppm")$ or $unit("uV")$],
+ [$epsilon_"total"$],
+ [Total combined error],
+ [$unit("ppm")$ or $unit("uV")$],
[$epsilon_"systematic"$], [Systematic error component], [$unit("ppm")$],
[$epsilon_"random"$], [Random error component], [$unit("uVrms")$],
@@ -238,9 +251,13 @@ operators.
[Current noise spectral density],
[$unit("pico ampere per shertz")$ or $unit("femto ampere per shertz")$],
- [$e_(n,"white")$], [White noise component], [$unit("nano volt per shertz")$],
+ [$e_(n,"white")$],
+ [White noise component],
+ [$unit("nano volt per shertz")$],
- [$e_(n,1/f)$], [$1/f$ noise component], [$unit("nano volt per shertz")$ at $qty(1, "Hz")$],
+ [$e_(n,1/f)$],
+ [$1/f$ noise component],
+ [$unit("nano volt per shertz")$ at $qty(1, "Hz")$],
[$f_c$], [Noise corner frequency ($1/f$ to white)], [$unit("Hz")$],
@@ -261,7 +278,9 @@ operators.
[$f_"carrier"$], [Carrier frequency], [$unit("Hz")$],
[$f_0$], [Resonant or center frequency], [$unit("Hz")$],
[$phi$], [Phase], [$unit("radian")$ or $unit("degree")$],
- [$Delta phi$], [Phase error or shift], [$unit("radian")$ or $unit("degree")$],
+ [$Delta phi$],
+ [Phase error or shift],
+ [$unit("radian")$ or $unit("degree")$],
[$t_"settle"$], [Settling time], [$unit("s")$],
[$t_h$], [Timing jitter], [$unit("ps")$ or $unit("ns")$],
@@ -276,13 +295,21 @@ operators.
columns: 3,
table.header([Symbol], [Definition], [Typical unit]),
[$T$], [Temperature (absolute)], [$unit("K")$],
- [$delta upright(T)$], [Temperature difference], [$unit("celsius")$ or $unit("K")$],
+ [$delta upright(T)$],
+ [Temperature difference],
+ [$unit("celsius")$ or $unit("K")$],
- [$(Delta T)/(upright(d) y)$], [Vertical temperature gradient], [$unit("celsius per centi meter")$],
+ [$(Delta T)/(upright(d) y)$],
+ [Vertical temperature gradient],
+ [$unit("celsius per centi meter")$],
- [$theta_"conv"$], [Convective thermal resistance], [$unit("celsius per watt")$],
+ [$theta_"conv"$],
+ [Convective thermal resistance],
+ [$unit("celsius per watt")$],
- [$theta_"cond"$], [Conductive thermal resistance], [$unit("celsius per watt")$],
+ [$theta_"cond"$],
+ [Conductive thermal resistance],
+ [$unit("celsius per watt")$],
[$accent(Q, dot)$], [Heat flux], [$unit("W")$],
[$accent(m, dot)$], [Mass flow rate], [$unit("kilo gram per second")$],
@@ -1063,7 +1090,8 @@ $tau = qty(25, "us")$: it represents one Nyquist interval
$T_"sample" = qty(25, "us")$).
#let epsilon_noise = (
- apply-prefix(632.455, "micro") / calc.sqrt(2 * 20000 * apply-prefix(25, "micro"))
+ apply-prefix(632.455, "micro")
+ / calc.sqrt(2 * 20000 * apply-prefix(25, "micro"))
)
#assert-aeq(epsilon_noise, apply-prefix(632.455, "micro"))
$
@@ -1083,9 +1111,13 @@ errors. Both must be met, but they are separate budgets:
table(
columns: 3,
table.header([Budget], [Allocation], [Expressed as]),
- [Systematic error budget], [$qty(200, "uV")$ ($qty(10, "ppm")$)], [Offset, drift, gain error, nonlinearity],
+ [Systematic error budget],
+ [$qty(200, "uV")$ ($qty(10, "ppm")$)],
+ [Offset, drift, gain error, nonlinearity],
- [Random error budget], [$qty(632.455, "uVrms")$ ($qty(90, "dB")$)], [Thermal noise, $1/f$ noise, interference],
+ [Random error budget],
+ [$qty(632.455, "uVrms")$ ($qty(90, "dB")$)],
+ [Thermal noise, $1/f$ noise, interference],
),
caption: [Error budgets allocation],
) <table-snr-and-noise-floor-resolution>
@@ -1261,9 +1293,15 @@ $
====== Scaling law <resistor-thermal-noise-scaling-law>
-Thermal noise scales with $sqrt(R)$. Lower resistance values reduce noise but
-increase power consumption and loading effects. This creates a fundamental
-tradeoff.
+Thermal noise voltage scales with the square root of resistance ($sqrt(R)$),
+absolute temperature ($sqrt(T)$), and bandwidth ($sqrt(upright("BW"))$). At
+circuit outputs, referred-to-output thermal noise scales linearly with the
+stage's closed-loop noise gain ($G_N = 1 + R_f / R_"in"$). Decreasing
+resistance values by a factor of $10$ yelds a $sqrt(10) approx 3.16 times$
+($qty(-10, "dB")$) reduction in thermal noise voltage, but increases power
+dissipation ($P = V^2 / R$) and operational amplifier driving current demands by
+$10 times$, establishing a fundamental thermodynamic trade-off between noise
+floor and power/thermal loading.
====== Compensation strategy <resistor-thermal-noise-compensation-strategy>
@@ -1315,22 +1353,89 @@ servo loop.
table(
columns: 4,
table.header([Op-amp class], [$e_(n,"white")$], [$f_c$], [Notes]),
- [General purpose (TL07x)], [$18 unit("nano volt per shertz")$], [$qty(200, "Hz")$], [JFET input],
-
- [Low noise (OPA211)], [$1.1 unit("nano volt per shertz")$], [$qty(10, "Hz")$], [Bipolar input],
-
- [Precision low noise (LT1028)], [$0.85 unit("nano volt per shertz")$], [$qty(3.5, "Hz")$], [Bipolar input],
-
- [Chopper stabilized (LTC2057)], [$7 unit("nano volt per shertz")$], [$lt qty(1, "Hz")$], [No $1/f$ corner],
+ [General purpose (TL07x)],
+ [$18 unit("nano volt per shertz")$],
+ [$qty(200, "Hz")$],
+ [JFET input],
+
+ [Low noise (OPA211)],
+ [$1.1 unit("nano volt per shertz")$],
+ [$qty(10, "Hz")$],
+ [Bipolar input],
+
+ [Precision low noise (LT1028)],
+ [$0.85 unit("nano volt per shertz")$],
+ [$qty(3.5, "Hz")$],
+ [Bipolar input],
+
+ [Chopper stabilized (LTC2057)],
+ [$7 unit("nano volt per shertz")$],
+ [$lt qty(1, "Hz")$],
+ [No $1/f$ corner],
),
caption: [Op-amp voltage noise typical magnitudes],
) <table-opamp-voltage-noise-typical-magnitude>
+#figure(
+ cetz.canvas({
+ import cetz.draw: *
+
+ plot.plot(
+ size: (12, 6),
+ x-mode: "log",
+ x-label: [Frequency $f$ ($unit("Hz")$)],
+ y-label: [$e_n(f)$ ($unit("nano volt per shertz")$)],
+ x-min: 0.1,
+ x-max: 100000,
+ y-min: 0.5,
+ y-max: 50,
+ x-grid: "minor",
+ y-grid: "minor",
+ {
+ // General purpose (TL072): e_white = 18 nV/sqrt(Hz), fc = 200 Hz
+ plot.add(
+ style: (stroke: (dash: "solid")),
+ label: [TL07x],
+ domain: (0.1, 100000),
+ samples: 8000,
+ f => 18 * calc.sqrt(1 + 200 / f),
+ )
+ // Low noise (OPA211): e_white = 1.1 nV/sqrt(Hz), fc = 10 Hz
+ plot.add(
+ style: (stroke: (dash: "dashed")),
+ label: [OPA211],
+ domain: (0.1, 100000),
+ samples: 8000,
+ f => 1.1 * calc.sqrt(1 + 10 / f),
+ )
+ // Precision low noise (LT1028): e_white = 0.85 nV/sqrt(Hz), fc = 3.5 Hz
+ plot.add(
+ style: (stroke: (dash: "dotted")),
+ label: [LT1028],
+ domain: (0.1, 100000),
+ samples: 8000,
+ f => 0.85 * calc.sqrt(1 + 3.5 / f),
+ )
+ // Chopper stabilized (LTC2057): e_white = 7 nV/sqrt(Hz), fc = 1 Hz
+ plot.add(
+ style: (stroke: (dash: "dash-dotted")),
+ label: [LTC2057],
+ domain: (0.1, 100000),
+ samples: 8000,
+ f => 7 * calc.sqrt(1 + 1 / f),
+ )
+ },
+ )
+ }),
+ caption: [Comparison of operational amplifier voltage noise spectral density curves across frequency],
+) <figure-opamp-voltage-noise-mathematical-model>
+
For a precision op-amp ($1 unit("nano volt per shertz"), f_c = qty(10, "Hz")$)
over $qty(0.001, "Hz")$ to $qty(20, "kHz")$:
#let v_n_rms = calc.sqrt(
- calc.pow(calc.pow(10, -9), 2) * 20000 + (calc.pow(calc.pow(10, -9), 2) * 10 * calc.ln(20000 / 0.001)),
+ calc.pow(calc.pow(10, -9), 2) * 20000
+ + (calc.pow(calc.pow(10, -9), 2) * 10 * calc.ln(20000 / 0.001)),
)
#assert-aeq(v_n_rms, apply-prefix(0.43, "micro"))
$
@@ -1348,6 +1453,17 @@ $ V_(n,"out") = e_n (1+R_f/R_"in") $
For a unity-gain buffer ($R_f = 0$), $V_(n,"out") = e_n$. For a gain-of-10
amplifier, output noise is $10 times$ input voltage noise.
+====== Scaling law <opamp-voltage-noise-scaling-law>
+
+Output voltage noise scales linearly with closed-loop stage noise gain
+($G_N = 1 + R_f/R_"in"$). At high frequencies ($f gt.double f_c$), total
+integrated white noise scales with the square root of bandwidth
+($sqrt(f_H - f_L)$). In the low-frequency $1/f$ region ($f lt.double f_c$),
+integrated noise scales with the square root of the logarithmic observation
+time ratio ($sqrt(ln(f_H / f_L))$), accumulating equal noise power across every
+decade of frequency. Internal white noise density $e_(n,"white")$ scales with
+$sqrt(T)$ following transistor channel and junction thermal noise dynamics.
+
====== Compensation strategy <opamp-voltage-noise-compensation-strategy>
- Select low noise bipolar op-amps ($e_n < 2 unit("nano volt per shertz")$) for
@@ -1385,7 +1501,9 @@ $ V_(n,i,"rms") = i_n times Z_"source" times sqrt(upright("BW")) $
table(
columns: 3,
table.header([Op-amp class], [$i_n$], [Notes]),
- [Bipolar (LT1028)], [$1 unit("pico ampere per shertz")$], [Low voltage noise, moderate current noise],
+ [Bipolar (LT1028)],
+ [$1 unit("pico ampere per shertz")$],
+ [Low voltage noise, moderate current noise],
[JFET (OPA627)], [$2.5 unit("femto ampere per shertz")$], [Negligible],
@@ -1420,6 +1538,16 @@ Current noise flows through all passive networks connected to the op-amp inputs.
The total current-noise-induced voltage error is dominated by the highest
equivalent impedance node connected to either terminal.
+====== Scaling law <opamp-current-noise-scaling-law>
+
+Current-noise-induced voltage error scales linearly with equivalent source
+impedance $Z_"source"$ and spectral density $i_n$, and with the square root of
+bandwidth $sqrt(upright("BW"))$. Because shot noise density scales as
+$i_n prop sqrt(I_B)$, current noise in bipolar inputs scales with the square
+root of DC bias current. For JFET and CMOS inputs, where bias current doubles
+for every $qty(10, "dC")$ temperature increase, $i_n$ scales by $sqrt(2)$
+($approx plus qty(41, "percent")$) per $qty(10, "dC")$.
+
====== Compensation strategy <opamp-current-noise-compensation-strategy>
Current noise cannot be cancelled by differential substraction because $i_(n+)$
@@ -1447,13 +1575,13 @@ significantly greater threat to precision than DC supply variations.
The output error voltage $V_(n,"supply")$ resulting from supply ripple
($V_("ripple")$) is:
-$ V_(n,"supply") = V_("ripple") / (upright("PSRR")(f)) $
+$ V_(n,"supply") = V_"ripple" / (upright("PSRR")(f)) $
Where $upright("PSRR")$ expressed in $unit("dB")$ represents the ratio of AC
supply variation to output error variation:
$
- upright("PSRR")_(unit("dB")) = 20 log_10((Delta V_("supply"))/(Delta V_("out")))
+ upright("PSRR")_unit("dB") = 20 log_10((Delta V_"supply")/(Delta V_"out"))
$
====== Typical magnitude <power-supply-noise-coupling-typical-magnitude>
@@ -1461,7 +1589,9 @@ $
#figure(
table(
columns: 3,
- table.header([Op-amp], [$upright("PSRR")$ (DC)], [$upright("PSRR")$ ($qty(10, "kHz")$)]),
+ table.header(
+ [Op-amp], [$upright("PSRR")$ (DC)], [$upright("PSRR")$ ($qty(10, "kHz")$)]
+ ),
[TL072], [$qty(100, "dB")$], [$qty(80, "dB")$],
[OPA211], [$qty(130, "dB")$], [$qty(90, "dB")$],
[LT1028], [$qty(120, "dB")$], [$qty(90, "dB")$],
@@ -1469,6 +1599,71 @@ $
caption: [Power supply noise coupling typical magnitudes],
) <table-power-supply-noise-coupling-typical-magnitude>
+#figure(
+ cetz.canvas({
+ import cetz.draw: *
+
+ plot.plot(
+ size: (12, 6),
+ x-mode: "log",
+ y-mode: "linear",
+ x-label: [Frequency $f$ ($unit("Hz")$)],
+ y-label: [$upright("PSRR")(f)$ ($unit("dB")$)],
+ x-min: 1,
+ x-max: 1000000,
+ y-min: 0,
+ y-max: 140,
+ x-grid: "minor",
+ y-grid: "minor",
+ {
+ // 500 kHz PWAM Carrier vertical reference line
+ plot.add(
+ ((500000, 0), (500000, 140)),
+ style: (stroke: (paint: luma(120), thickness: 0.8pt, dash: "dashed")),
+ label: none,
+ )
+ // General purpose (TL07x): PSRR_DC = 100 dB, fp1 = 1 kHz, fp2 = 100 kHz
+ plot.add(
+ style: (stroke: (dash: "solid")),
+ label: [TL07x],
+ domain: (1, 1000000),
+ samples: 8000,
+ f => (
+ 100
+ - 10 * calc.log(1 + calc.pow(f / 1000, 2), base: 10)
+ - 10 * calc.log(1 + calc.pow(f / 100000, 2), base: 10)
+ ),
+ )
+ // Low noise (OPA211): PSRR_DC = 130 dB, fp1 = 300 Hz, fp2 = 50 kHz
+ plot.add(
+ style: (stroke: (dash: "dotted")),
+ label: [OPA211],
+ domain: (1, 1000000),
+ samples: 8000,
+ f => (
+ 130
+ - 10 * calc.log(1 + calc.pow(f / 300, 2), base: 10)
+ - 10 * calc.log(1 + calc.pow(f / 50000, 2), base: 10)
+ ),
+ )
+ // Precision low noise (LT1028): PSRR_DC = 120 dB, fp1 = 300 Hz, fp2 = 100 kHz
+ plot.add(
+ style: (stroke: (dash: "dashed")),
+ label: [LT1028],
+ domain: (1, 1000000),
+ samples: 8000,
+ f => (
+ 120
+ - 10 * calc.log(1 + calc.pow(f / 300, 2), base: 10)
+ - 10 * calc.log(1 + calc.pow(f / 100000, 2), base: 10)
+ ),
+ )
+ },
+ )
+ }),
+ caption: [Power supply rejection ratio ($upright("PSRR")$) degradation across frequency, highlighting the reduced rejection at the $qty(500, "kHz")$ PWAM carrier frequency],
+) <figure-power-supply-noise-coupling-mathematical-model>
+
For SAME analog supply cleanliness specification ($qty(500, "uVpp")$ ripple on
$plus.minus qty(15, "V")$ rails) with $qty(80, "dB") upright("PSRR")$ at
$qty(10, "kHz")$:
@@ -1496,6 +1691,14 @@ $
- High-gain stages (where output noise is further amplified by subsequent stages),
-PWAM modulation cores susceptible to $qty(500, "kHz")$ carrier rail modulation.
+====== Scaling law <power-supply-noise-coupling-scaling-law>
+
+Power supply coupling error scales linearly with rail ripple amplitude
+$V_"ripple"$ and stage closed-loop gain. Above the op-amp's dominant
+$upright("PSRR")$ corner frequency (typically $qtyrange(10, 100, "Hz")$),
+coupling error increases with frequency at $plus qty(20, "decibel per decade")$
+due to the single-pole degradation of internal open-loop gain.
+
====== Compensation strategy <power-supply-noise-coupling-compensation-strategy>
- Enforce power supply cleanliness specification per @power-distribution,
@@ -1525,7 +1728,7 @@ unwanted DC offset voltage that directly corrupts precision calculations.
The peak Rf voltage induced in an unshielded conductor of length $l$ exposed to
an electric field of strength $E$ is:
-$ V_("induced") approx E dot l dot eta $
+$ V_"induced" approx E dot l dot eta $
Where $eta$ is the antenna coupling efficiency ($0.01$ to $0.5$, depending on
frequency and geometry).
@@ -1533,7 +1736,7 @@ frequency and geometry).
The resulting demodulated DC offset error $V_(upright("DC"),"error")$ created by
junction rectification efficiency $alpha$ ($0.01$ to $0.1$) is:
-$ V_(upright("DC"),"error") = alpha dot V_("induced") $
+$ V_(upright("DC"),"error") = alpha dot V_"induced" $
====== Typical magnitude
<radiated-and-conducted-rf-interference-typical-magnitude>
@@ -1544,7 +1747,7 @@ specification limit) with coupling efficiency $eta = 0.1$:
#let v_induced = 3 * 1 * 0.1
#assert-aeq(v_induced, apply-prefix(300, "milli"))
-$ V_("induced") = 3 times 1 times 0.1 = qty(300, "mVpp") $
+$ V_"induced" = 3 times 1 times 0.1 = qty(300, "mVpp") $
Without input filtering, RFI demodulation with $alpha = 0.03$ produces a DC
offset error of:
@@ -1616,22 +1819,22 @@ with the signal path.
Ground loop errors operate under two distinct mechanisms:
-/ Intra-chassis return drops: Internal module return currents $I_("return")$
- flowing through shared backplane AGND plane resistance $R_("AGND")$ produce a
- systematic offset: $ V_("error","internal") = I_("return") dot R_("AGND") $
+/ Intra-chassis return drops: Internal module return currents $I_"return"$
+ flowing through shared backplane AGND plane resistance $R_upright("AGND")$ produce a
+ systematic offset: $ V_("error","internal") = I_"return" dot R_upright("AGND") $
/ Inter-chassis common-mode offset: External ground potential differences
$Delta V_G$ coupled via external cables are attenuated by the Common-Mode
Rejection Ratio (CMRR) of the Interface Module's input stage:
$
- V_("error","external") = (Delta V_G) / upright("CMRR")_("linear") = Delta V_G dot 10^(- upright("CMRR")_(unit("dB")) / 20)
+ V_("error","external") = (Delta V_G) / upright("CMRR")_"linear" = Delta V_G dot 10^(- upright("CMRR")_unit("dB") / 20)
$
====== Typical magnitude <ground-loop-currents-typical-magnitude>
-For intra-chassis module operation, $R_("AGND") lt.eq qty(0.5, "milli ohm")$
+For intra-chassis module operation, $R_upright("AGND") lt.eq qty(0.5, "milli ohm")$
(guaranteed by continuous Layer 2 copper planes and multi-pin DB-25 grounding
per @pcb-ground-planes and @backplane-connector). A
-$I_("return") = qty(100, "mA")$ analog return current produces:
+$I_"return" = qty(100, "mA")$ analog return current produces:
#let v_error_internal = apply-prefix(100, "milli") * apply-prefix(0.5, "milli")
#assert-aeq(v_error_internal, apply-prefix(50, "micro"))
@@ -1666,9 +1869,9 @@ $
====== Scaling law <ground-loop-currents-scaling-law>
Intra-chassis ground error scales linearly with module return current
-$I_("return")$ and AGND copper resistance $R_(upright("AGND"))$. Inter-chassis
+$I_"return"$ and AGND copper resistance $R_upright("AGND")$. Inter-chassis
ground error scales inversely with Interface Module
-$upright("CMRR")_(unit("dB"))$ at $qty(20, "decibel per decade")$.
+$upright("CMRR")_unit("dB")$ at $qty(20, "decibel per decade")$.
====== Compensation strategy <ground-loop-currents-compensation-strategy>
@@ -1677,7 +1880,7 @@ includes DC components. Mitigation relies on structural topology:
- Enforcing strict separation of AGND and DGND across backplane pins with zero
on-module bridging per @grounding-rules,
- Mandating continuous $qty(1, "oz")$ copper AGND planes on Layer 2 of all
- module PCBs to minimize $R_(upright("AGND"))$ per
+ module PCBs to minimize $R_upright("AGND")$ per
@pcb-ground-planes-agnd-plane,
- Mandating high-CMRR ($gt.eq qty(120, "dB")$) differential instrumentation
inputs or galvanic isolation on all external-facing interface modules per
@@ -1703,7 +1906,7 @@ $upright("DA")$ is modeled as a voltage source in series with the ideal
capacitance, decaying as a sum of exponential time constants:
$
- V_(upright("DA")) = V_("peak") dot upright("DA")% dot sum_i A_i e^(-t/(tau_i))
+ V_upright("DA") = V_"peak" dot upright("DA")% dot sum_i A_i e^(-t/(tau_i))
$
Where $upright("DA")%$ is the dielectric absorption coefficient and $tau_i$
@@ -1725,13 +1928,118 @@ represents the dielectric material relaxation time constants.
caption: [Capacitor dielectric absorption coefficients],
) <table-capacitor-dielectric-absorption-noise-typical-magnitude>
+#figure(
+ cetz.canvas({
+ import cetz.draw: *
+
+ plot.plot(
+ size: (12, 9),
+ y-mode: "log",
+ x-label: [Time $t$ ($unit("s")$)],
+ y-label: [Residual Voltage $V_upright("DA")$ ($unit("mV")$)],
+ x-min: 0.001,
+ x-max: 9,
+ y-min: 0.01,
+ y-max: 500,
+ x-grid: "minor",
+ y-grid: "minor",
+ {
+ // 200 uV (0.2 mV) Metrologic systematic error budget limit
+ plot.add(
+ ((0.001, 0.2), (10, 0.2)),
+ style: (
+ stroke: (paint: luma(120), thickness: 0.8pt, dash: "dash-dotted"),
+ ),
+ label: [Budget limit ($qty(200, "uV")$)],
+ )
+ // Ceramic X7R (DA = 2.5%, 10 V step -> 250 mV initial soakage peak)
+ plot.add(
+ style: (stroke: (dash: "solid")),
+ label: [Ceramic (X7R / Class 2)],
+ domain: (0.001, 10),
+ samples: 10000,
+ t => (
+ 250
+ * (
+ 0.5 * calc.exp(-t / 0.01)
+ + 0.3 * calc.exp(-t / 0.1)
+ + 0.2 * calc.exp(-t / 1.0)
+ )
+ ),
+ )
+ // Polypropylene PP (DA = 0.05%, 10 V step -> 5 mV initial soakage peak)
+ plot.add(
+ style: (stroke: (dash: "dotted")),
+ label: [Polypropylene (PP)],
+ domain: (0.001, 10),
+ samples: 10000,
+ t => (
+ 5
+ * (
+ 0.5 * calc.exp(-t / 0.01)
+ + 0.3 * calc.exp(-t / 0.1)
+ + 0.2 * calc.exp(-t / 1.0)
+ )
+ ),
+ )
+ // Polystyrene PP (DA = 0.002, 10 V step -> 2 mV initial soakage peak)
+ plot.add(
+ style: (stroke: (dash: "dashed")),
+ label: [Polystyrene (PS)],
+ domain: (0.001, 10),
+ samples: 10000,
+ t => (
+ 2
+ * (
+ 0.5 * calc.exp(-t / 0.01)
+ + 0.3 * calc.exp(-t / 0.1)
+ + 0.2 * calc.exp(-t / 1.0)
+ )
+ ),
+ )
+ // Teflon PTFE (DA = 0.001, 10 V step -> 1 mV initial soakage peak)
+ plot.add(
+ style: (stroke: (dash: "dash-dotted")),
+ label: [Teflon (PTFE)],
+ domain: (0.001, 10),
+ samples: 10000,
+ t => (
+ 1
+ * (
+ 0.5 * calc.exp(-t / 0.01)
+ + 0.3 * calc.exp(-t / 0.1)
+ + 0.2 * calc.exp(-t / 1.0)
+ )
+ ),
+ )
+ // Class 1 C0G/NP0 Ceramic (DA = 0.005%, 10 V step -> 0.5 mV initial soakage peak)
+ plot.add(
+ style: (stroke: (dash: (6pt, 2pt, 2pt, 2pt))),
+ label: [Class 1 (C0G/NP0)],
+ domain: (0.001, 10),
+ samples: 10000,
+ t => (
+ 0.5
+ * (
+ 0.5 * calc.exp(-t / 0.01)
+ + 0.3 * calc.exp(-t / 0.1)
+ + 0.2 * calc.exp(-t / 1.0)
+ )
+ ),
+ )
+ },
+ )
+ }),
+ caption: [Capacitor dielectric absorption residual voltage relaxation ($V_upright("DA")$) over time following a $qty(10, "V")$ step voltage reset, comparing dielectric classes against the $qty(200, "uV")$ systematic error budget],
+) <figure-capacitor-dielectric-absorption-noise-typical-magnitude>
+
For a standard polypropylene capacitor ($qty(0.05, "percent") upright("DA")$)
step-charged at $qty(10, "V")$:
#let v_da = 10 * 0.0005
#assert.eq(v_da, apply-prefix(5, "milli"))
$
- V_(upright("DA")) = qty(10, "V") times 0.0005 = qty(5, "mV") (qty(250, "ppm"))
+ V_upright("DA") = qty(10, "V") times 0.0005 = qty(5, "mV") (qty(250, "ppm"))
$
This single residual term exceeds our $qty(200, "uV")$ systematic error budget
@@ -1747,7 +2055,7 @@ by a factor of $25$.
====== Scaling law <capacitor-dielectric-absorption-noise-scaling-law>
Dielectric absorption error scales linearly with peak applied step voltage
-$V_("peak")$ and the dielectric material coefficient $upright("DA")$. It decays
+$V_"peak"$ and the dielectric material coefficient $upright("DA")$. It decays
logarithmically over time, making rapid reset/re-integration cycles particularly
vulnerable.
@@ -1784,16 +2092,16 @@ through two primary physical mechanisms:
Triboelectric induced noise voltage in a flexible cable is modeled as:
-$ V_("tribo") = k_("tribo") dv(L, t) $
+$ V_"tribo" = k_"tribo" dv(L, t) $
-Where $k_("tribo")$ is the cable's triboelectric coupling constant in
+Where $k_"tribo"$ is the cable's triboelectric coupling constant in
$unit("mV") / unit("meter per second")$ and $dv(L, t)$ is the rate of mechanical
deformation.
Piezoelectric microphonic voltage generated across a capacitor of capacitance
-$C$ subjected to vibrational force $F_("vib")$ is:
+$C$ subjected to vibrational force $F_"vib"$ is:
-$ V_("piezo") = (d_33 dot F_("vib")) / C $
+$ V_"piezo" = (d_33 dot F_"vib") / C $
Where $d_33$ is the longitudinal piezoelectric charge coefficient of the
dielectric material.
@@ -1802,22 +2110,22 @@ dielectric material.
<triboelectric-and-piezoelectric-effects-typical-magnitude>
For a standard PVC-insulated patch cable,
-$k_("tribo") approx qty(50, "milli volt per meter per second")$
+$k_"tribo" approx qty(50, "milli volt per meter per second")$
experiencing mild flexing ($dv(L, t) = qty(1, "milli meter per second")$):
#let v_tribo = apply-prefix(50, "milli") * apply-prefix(1, "milli")
#assert-aeq(v_tribo, apply-prefix(50, "micro"))
$
- V_("tribo") = num("50e-3") times num ("1e-3") = qty(50, "uV") (qty(2.5, "ppm"))
+ V_"tribo" = num("50e-3") times num ("1e-3") = qty(50, "uV") (qty(2.5, "ppm"))
$
Using a low-noise graphite-coated cable
-($k_("tribo") approx qty(1, "milli volt per meter per second")$):
+($k_"tribo" approx qty(1, "milli volt per meter per second")$):
#let v_tribo = apply-prefix(1, "milli") * apply-prefix(1, "milli")
#assert-aeq(v_tribo, apply-prefix(1, "micro"))
$
- V_("tribo") = num("1e-3") times num("1e-3") = qty(1, "uV") (qty(0.05, "ppm"))
+ V_"tribo" = num("1e-3") times num("1e-3") = qty(1, "uV") (qty(0.05, "ppm"))
$
For an X7R ceramic capacitor
@@ -1828,7 +2136,7 @@ $qty(100, "nF")$ node:
#let v_piezo = (apply-prefix(200, "pico") * 0.1) / apply-prefix(100, "nano")
#assert-aeq(v_piezo, apply-prefix(200, "micro"))
$
- V_("piezo") = (num("200e-12") times 0.1) / num("100e-9") = qty(200, "uV") (qty(10, "ppm"))
+ V_"piezo" = (num("200e-12") times 0.1) / num("100e-9") = qty(200, "uV") (qty(10, "ppm"))
$
====== Where it enters
@@ -1874,46 +2182,46 @@ signal traces into the signal path.
====== Mathematical model <pcb-leakage-currents-mathematical-model>
-Surface leakage current $I_("leak")$ flowing from a neighboring trace at
-potential $V_("trace")$ across surface insulation resistance $R_("leak")$ is:
+Surface leakage current $I_"leak"$ flowing from a neighboring trace at
+potential $V_"trace"$ across surface insulation resistance $R_"leak"$ is:
-$ I_("leak") = V_("trace") / R_("leak") $
+$ I_"leak" = V_"trace" / R_"leak" $
This stray current flows into the high-impedance node's equivalent source
-impedance $Z_("source")$, developing a systematic error voltage:
+impedance $Z_"source"$, developing a systematic error voltage:
$
- V_("error") = I_("leak") times Z_("source") = V_("trace") dot Z_("source")/R_("leak")
+ V_"error" = I_"leak" times Z_"source" = V_"trace" dot Z_"source"/R_"leak"
$
====== Typical magnitude <pcb-leakage-currents-typical-magnitude>
For a clean, dry FR-4 PCB at $qty(50, "percent") upright("RH")$, surface
resistance between adjacent traces is typically
-$R_("leak") approx qty(1, "tera ohm")$. Under high ambient humidity
+$R_"leak" approx qty(1, "tera ohm")$. Under high ambient humidity
($qty(70, "percent") upright("RH")$) or with uncleaned flux residue, surface
-resistance drops sharply to $R_("leak") approx qty(1, "giga ohm")$.
+resistance drops sharply to $R_"leak" approx qty(1, "giga ohm")$.
-For a $V_("trace") = qty(10, "V")$ potential adjacent to a
-$Z_("source") = qty(1, "mega ohm")$ input node (white banana jack boundary):
+For a $V_"trace" = qty(10, "V")$ potential adjacent to a
+$Z_"source" = qty(1, "mega ohm")$ input node (white banana jack boundary):
-- On a clean PCB ($R_("leak") = qty(1, "tera ohm")$):
+- On a clean PCB ($R_"leak" = qty(1, "tera ohm")$):
#let i_leak = 10 / apply-prefix(1, "tera")
#assert-aeq(i_leak, apply-prefix(10, "pico"))
#let v_error = i_leak * apply-prefix(1, "mega")
#assert-aeq(v_error, apply-prefix(10, "micro"))
$
- I_("leak") & = qty(10, "V") / qty(1, "tera ohm") = qty(10, "pA") \
- V_("error") & = qty(10, "pA") times qty(1, "mega ohm") = qty(10, "uV") (qty(0.5, "ppm"))
+ I_"leak" & = qty(10, "V") / qty(1, "tera ohm") = qty(10, "pA") \
+ V_"error" & = qty(10, "pA") times qty(1, "mega ohm") = qty(10, "uV") (qty(0.5, "ppm"))
$
-- On a contaminated PCB ($R_("leak") = qty(1, "giga ohm")$):
+- On a contaminated PCB ($R_"leak" = qty(1, "giga ohm")$):
#let i_leak = 10 / apply-prefix(1, "giga")
#assert-aeq(i_leak, apply-prefix(10, "nano"))
#let v_error = i_leak * apply-prefix(1, "mega")
#assert-aeq(v_error, apply-prefix(10, "milli"))
$
- I_("leak") & = qty(10, "V") / qty(1, "giga ohm") = qty(10, "nA") \
- V_("error") & = qty(10, "nA") times qty(1, "mega ohm") = qty(10, "mV") (qty(500, "ppm"))
+ I_"leak" & = qty(10, "V") / qty(1, "giga ohm") = qty(10, "nA") \
+ V_"error" & = qty(10, "nA") times qty(1, "mega ohm") = qty(10, "mV") (qty(500, "ppm"))
$
Contamination increases systematic DC error by $500 times$, exceeding the total
@@ -1997,7 +2305,13 @@ materials.
#figure(
table(
columns: 5,
- table.header([Inductor type], [$upright("DCR")$], [Core loss ($qty(10, "kHz")$)], [Self-resonance], [Notes]),
+ table.header(
+ [Inductor type],
+ [$upright("DCR")$],
+ [Core loss ($qty(10, "kHz")$)],
+ [Self-resonance],
+ [Notes],
+ ),
[Air core],
[High ($qtyrange(1, 100, "ohm")$)],
[None],
@@ -2010,9 +2324,17 @@ materials.
[$qtyrange(10, 100, "MHz")$],
[Designed for loss (filtering)],
- [Ferrite inductor], [$qtyrange(0.1, 10, "ohm")$], [Moderate], [$qtyrange(1, 50, "MHz")$], [General purpose],
+ [Ferrite inductor],
+ [$qtyrange(0.1, 10, "ohm")$],
+ [Moderate],
+ [$qtyrange(1, 50, "MHz")$],
+ [General purpose],
- [Powdered iron], [$qtyrange(0.5, 5, "ohm")$], [Low], [$qtyrange(1, 10, "MHz")$], [DC bias tolerant],
+ [Powdered iron],
+ [$qtyrange(0.5, 5, "ohm")$],
+ [Low],
+ [$qtyrange(1, 10, "MHz")$],
+ [DC bias tolerant],
[Laminated steel],
[$qtyrange(0.1, 1, "ohm")$],
@@ -2064,6 +2386,348 @@ instability. Mitigation relies on topological avoidance:
- Isolating magnetic components away from high impedance summing nodes to
prevent inductive coupling.
+===== Potentiometer wiper noise and wear
+<potentiometer-wiper-noise-and-wear>
+
+====== Physical mechanism
+<potentiometer-wiper-noise-and-wear-physical-mechanism>
+
+Potentiometers used in control modules introduce several noise and error
+mechanisms:
+/ Wiper contact noise: The sliding contact between wiper and resistive elements
+ creates intermittent micro-disconnections, appearing as broadband noise,
+/ Wiper contact resistance: The wiper-to-element contact has finite resistance
+ (typically $qtyrange(1, 100, "ohm")$) that varies with pressure,
+ contamination, and position,
+/ Resolution (wirewound): Wirewound potentiometers have discrete turn steps,
+ creating spatial quantization noise,
+/ Resistive element noise: Current flow through granular carbon or ceramic
+ matrix structures generates $1/f$ excess noise beyond Johnson noise,
+/ Mechanical wear: Repeated operation wears the resistive track and wiper material,
+ increasing contact resistance and noise density over time,
+/ Contamination: Airbone dust, flux residues, and oxidation on the element
+ create severe microphonic dropouts and static offset jumps.
+
+====== Mathematical model
+<potentiometer-wiper-noise-and-wear-mathematical-model>
+
+Total potentiometer output noise spectral density combines thermal noise and DC
+current-dependent excess noise characterized by the Current Noise Index
+($upright("CNI")$):
+
+$
+ V_"noise" = sqrt(4 k_B upright("TR") dot upright("BW")) + I dot R dot 10^(upright("CNI")/20) dot 10^-6
+$
+
+Where $upright("CNI")$ is typically specified in $unit("dB")$ (typically
+$qtyrange(-20, 20, "dB")$, with $qty(0, "dB")$ corresponding to
+$qty(1, "micro volt")$ of excess noise per $unit("V")$ of DC drop over a
+frequency decade).
+
+Wiper contact resistance variation during movement is modeled as:
+
+$ R_"contact" (t) = R_("contact",0) + Delta R_"contact" dot n(t) $
+
+Where $n(t)$ is a normalized random noise process bounded by wiper travel velocity.
+
+====== Typical magnitude <potentiometer-wiper-noise-and-wear-typical-magnitude>
+
+#figure(
+ table(
+ columns: 5,
+ table.header(
+ [Potentiometer type],
+ [Contact resistance],
+ [$upright("CNI")$],
+ [Wear life],
+ [Notes],
+ ),
+ [Carbon composition],
+ [$qtyrange(2, 50, "ohm")$],
+ [$qtyrange(0, 10, "dB")$],
+ [$10$k - $100$k cycles],
+ [Noisy, wear-prone],
+
+ [Cermet],
+ [$qtyrange(1, 10, "ohm")$],
+ [$qtyrange(-10, 0, "dB")$],
+ [$100$k - $1$M cycles],
+ [Good compromise],
+
+ [Conductive plastic],
+ [$qtyrange(0.5, 5, "ohm")$],
+ [$qtyrange(-20, -10, "dB")$],
+ [$1$M - $10$M cycles],
+ [Low noise, good wear],
+
+ [Wirewound],
+ [$qtyrange(0.1, 2, "ohm")$],
+ [Very low],
+ [$1$M+ cycles],
+ [Quantized, best for DC],
+
+ [Multi-turn (10T)],
+ [$qtyrange(0.5, 5, "ohm")$],
+ [$qtyrange(-15, -5, "dB")$],
+ [$100$k - $1$M cycles],
+ [High resolution],
+ ),
+ caption: [Potentiometer wiper noise and wear typical magnitudes],
+) <table-potentiometer-wiper-noise-and-wear-typical-magnitude>
+
+For a $qty(10, "kilo ohm")$ conductive plastic potentiometer at mid-scale
+($qty(5, "kilo ohm")$) carrying $qty(1, "mA")$ DC current:
+
+#let v_signal = apply-prefix(5, "kilo") * apply-prefix(1, "milli")
+#assert.eq(v_signal, 5)
+$ V_"signal" = qty(5, "kilo ohm") times qty(1, "mA") = qty(5, "V") $
+
+With $upright("CNI") = qty(-15, "dB")$ over $qty(20, "kHz")$ bandwidth:
+
+#let v_noise_excess = (
+ 5 * calc.pow(10, -15 / 20) * calc.sqrt(20000) * calc.pow(10, -6)
+)
+#assert-aeq(v_noise_excess, apply-prefix(125.74, "micro"))
+$
+ V_("noise","excess") & approx qty(5, "V") times 10^(-15/20) times sqrt(20000) dot 10^-6 \
+ V_("noise","excess") & approx qty(5, "V") times 0.1778 times num("141.4e-6") approx qty(125.74, "uV")
+$
+
+Excess noise alone consumes $qty(6.3, "ppm")$: $qty(63, "percent")$ of the
+$qty(10, "ppm")$ systematic budget if unbuffered. Furthermore, wiper contact
+resistance spikes during mechanical adjustment generate dynamic transient spikes
+of $qtyrange(0.1, 10, "mV")$.
+
+====== Where it enters
+<potentiometer-wiper-noise-and-wear-where-it-enters>
+
+- Front-panel manual controls in Control Modules per
+ @module-category-control-modules,
+- Joysticks, multi-turn dials, and linear fader interfaces,
+- External instrumentation level-matching controls on Interface Modules.
+
+====== Scaling law <potentiometer-wiper-noise-and-wear-scaling-law>
+
+Excess noise voltage scales linearly with applied DC voltage drop
+$V_upright("DC") = I dot R$ and exponentially with $upright("CNI")$. Contact
+resistance voltage drop scales linearly with wiper current $I_"wiper"$.
+
+====== Compensation strategy
+<potentiometer-wiper-noise-and-wear-compensation-strategy>
+
+Wiper contact noise and wear cannot be eliminated by software calibration.
+Mitigation relies on design rules:
+- Prohibiting potentiometers on Compute Modules per
+ @compute-modules-interface-requirements,
+- Mandating conductive plastic elements with $upright("CNI") lt.eq qty(-15, "dB")$
+ and $gt.eq 1 upright(M)$ cycle wear rating for Control Modules,
+- Buffering wiper outputs immediately with ultra-high input impedance JFET
+ op-amps ($I_"wiper" arrow 0$) to eliminate $I dot R_"contact"$ voltage drops,
+- Applying low-pass RC filtering ($f_c approx qty(10, "Hz")$) directly on
+ control voltage wiper lines to attenuate mechanical movement contact noise.
+
+===== Crosstalk <crosstalk>
+
+====== Physical mechanism <crosstalk-physical-mechanism>
+
+Crosstalk is the unintended electromagnetic or conductive coupling of signals
+between channels or adjacent circuits traces. Mechanisms include:
+/ Capacitive coupling: Electric field between adjacent conductors induce
+ displacement current:
+
+ $ I_"coupled" = C_"stray" dot dv(V, t) $
+
+/ Inductive coupling: Magnetic fields generated by signal currents induce
+ unwanted voltages in adjacent signal loops:
+
+ $ V_"induced" = M dot dv(I, t) $
+
+/ Common-impedance coupling: Shared conductive return paths (ground planes,
+ power distribution lines) cause signal currents from one stage to modulate the
+ ground or supply reference of another:
+
+ $ V_"coupled" = I_"signal" dot Z_"shared" $
+
+/ Radiation coupling: High-frequency switching or harmonics radiate
+ electromagnetic waves between unshielded PCB traces or cables.
+
+====== Mathematical model <crosstalk-mathematical-model>
+
+/ Capacitive crosstalk: Mutual capacitance $C_"mutual"$ between parallel PCB
+ traces of length $l$, separation $d$, height $h$ above a ground plane is:
+
+ $
+ C_"mutual" approx (epsilon.alt_0 epsilon.alt_r l)/(2 pi) dot ln((d + sqrt(d^2 - 4 h^2))/(2 h))^(-1)
+ $
+
+ For typical PCB geometries ($qty(5, "mil")$ trace width, $qty(10, "mil")$
+ spacing, $qty(10, "mil")$ dielectric height):
+
+ $ C_"mutual" approx qtyrange(1, 3, "pico farad per inch") $
+
+ The resulting coupled voltage into a victim node of impedance $Z_"victim"$ is:
+