Examples / TI op amp handbook / Differentiators
Low noise differentiator
SBOA092B page 62, Low Noise: the differentiator with stop (1 kΩ RI and 0.1 µF CI in series, 100 kΩ RO) with a 0.001 µF CO across RO.
E_O / E_I = -j 2π f R_O C_I / ((1 + j 2π f R_I C_I)(1 + j 2π f R_O C_O))R_I C_I = R_O C_OThe circuit
Section titled “The circuit”The schematic is drawn by copperhead’s
drafting engine from this circuit’s netlist, with KiCad’s own library symbols,
and it opens in KiCad as figure/low_noise_differentiator.kicad_sch.
The op amp is KiCad’s generic one, since the handbook’s are ideal, and each
terminal is a test point named as the program names it. KiCad reads back from
the sheet exactly the connections the circuit has; draw_figures.py refuses to write
one that does not.
The interconnect view is fang’s own projection. It names the parts as the program does, so it reads against the code below.
What the program says
Section titled “What the program says”The page’s rule is a constraint, and the figure’s parts meet it exactly, both products 100 µs:
require(equals(product(r_i, c_i), product(r_o, c_o)))So the two poles sit together at f_corner = 1.59 kHz, the “double high
frequency cutoff”. Where the circuit with stop flattens at R_O/R_I = 100, this
one peaks at a_peak = R_O/(2 R_I) = 50 (each pole takes √2) and falls at
20 dB per decade above it: a decade up the gain is a_decade = 1000/101 =
9.90. The derivative below is unchanged, passing unity at f_unity = 15.9 Hz.
The page’s phrase “drift compensating resistor” beside the rule names no part in the figure, and the program does not invent one. Nothing was chosen.
What the simulation found
Section titled “What the simulation found”out/simulation.txt, from the deck under
out/spice/:
Measured in response | Measured | Claimed |
|---|---|---|
| unity-gain frequency, rising | 15.92 Hz | 15.92 Hz (f_unity), holds |
| gain at 1.59 kHz | 50 | 50 (a_peak), holds |
| largest gain anywhere | 50 | 50 (a_peak), holds |
| gain at 15.9 kHz | 9.751 | 9.901 (a_decade) ± 2%, holds |
| unity-gain frequency, falling | 156.6 kHz | not a claim |
The top of the response is a single point at the corner, as the double pole says. A decade above it the op amp’s loop gain is only about 60 and takes 1.5% off, which is why that claim is held to 2%.
Running it
Section titled “Running it”fang check examples/ti_opamp_handbook/differentiators/low_noise_differentiator/low_noise_differentiator.pypython examples/regenerate.py ti_opamp_handbook/differentiators/low_noise_differentiator # needs ngspiceThe whole program
Section titled “The whole program”"""The low-noise differentiator, SBOA092B page 62.Show 21 more lines
E_O / E_I = -j 2 pi f R_O C_I / ((1 + j 2 pi f R_I C_I)(1 + j 2 pi f R_O C_O))
The differentiator with stop, with a 0.001 uF C_O across R_O. The page asksfor R_I C_I = R_O C_O, and its parts meet it: 1 kOhm x 0.1 uF and 100 kOhm x0.001 uF are both 100 us. So the two poles sit together at1/(2 pi 100 us) = 1.59 kHz, the "double high frequency cutoff": where thecircuit with stop flattens at R_O/R_I = 100, this one turns over and falls at20 dB per decade, so the noise the plain differentiator amplifies most is cutinstead.
With both poles at one frequency the top of the response is a single point,not a plateau. At f_c the two poles each take a factor of sqrt(2) and turnthe phase by 90 degrees between them, so the gain there is(R_O/R_I) / 2 = 50, real and inverted. A decade above, 2 pi f R_O C_I is 1000and the poles divide it by 101.
The figure gives every value, so nothing was chosen. The page's phrase "driftcompensating resistor" beside R_I C_I = R_O C_O names no part in the figure,and the program does not invent one."""
import sysfrom pathlib import Path
# The handbook's shared parts and bench live in the folder above the sections.sys.path.insert(0, str(Path(__file__).resolve().parents[2]))
from decimal import Decimal
from fang.lang import Hz, Parameter, System, kHz, kOhm, nF, require, uFfrom fang.parts import Capacitor, Resistorfrom fang.rationale import Citesfrom fang.simulation import ACSweep
from handbook import ( Bench, Claim, Ground, OpAmp, Run, Terminal, corner, equals, over, product, ratio, total, within,)
class LowNoiseDifferentiator(System): """E_I through R_I and C_I into the summing point, R_O and C_O across it."""
figure = Cites( "R_I C_I = R_O C_O drift compensating resistor. Double high frequency cutoff", document="SBOA092B, Handbook of Operational Amplifier Applications", locator="page 62, Low Noise", )
f_corner = Parameter( "Hz", default=Decimal("1.59155") * kHz, description="1/(2 pi R_I C_I) = 1/(2 pi R_O C_O): both poles", ) f_unity = Parameter( "Hz", default=Decimal("15.9155") * Hz, description="1/(2 pi R_O C_I), gain 1" ) a_peak = Parameter( "1", default=50 * ratio, description="R_O / (2 R_I): the gain at f_corner" ) a_decade = Parameter( "1", default=Decimal("9.90099") * ratio, description="10 R_O / R_I / (1 + 10^2): the gain a decade above f_corner", )
e_in = Terminal() common = Terminal() e_out = Terminal() r_in = Resistor(resistance=1 * kOhm) c_in = Capacitor(capacitance=Decimal("0.1") * uF) r_out = Resistor(resistance=100 * kOhm) c_out = Capacitor(capacitance=1 * nF) amp = OpAmp() ground = Ground()
def architecture(self): self.e_in.probe >> self.r_in.p1 self.r_in.p2 >> self.c_in.p1 self.c_in.p2 >> self.amp.inverting.signal self.amp.inverting.signal >> self.r_out.p1 self.amp.inverting.signal >> self.c_out.p1 self.r_out.p2 >> self.amp.output.signal self.c_out.p2 >> self.amp.output.signal self.amp.output.signal >> self.e_out.probe self.amp.non_inverting.signal >> self.ground.node self.common.probe >> self.ground.node
def constraints(self): r_i, c_i = self.r_in.resistance, self.c_in.capacitance r_o, c_o = self.r_out.resistance, self.c_out.capacitance # The page's rule, which puts the two poles together. require(equals(product(r_i, c_i), product(r_o, c_o))) require(within(self.f_corner, corner(r_i, c_i), 0.00001)) require(within(self.f_unity, corner(r_o, c_i), 0.00001)) # At f_corner, 2 pi f R_O C_I is R_O/R_I and each pole divides by # |1 + j| = sqrt(2), so the two together halve it. require(equals(self.a_peak, over(r_o, product(2 * ratio, r_i)))) require( within( self.a_decade, over(product(10 * ratio, over(r_o, r_i)), total(1 * ratio, 100 * ratio)), 0.00001, ) )
BENCH = Bench( page=62, title="Low Noise Differentiator", runs=[ Run( "response", ACSweep(points=200, start="1", stop="1meg"), drive={"e_in": "DC 0 AC 1"}, measure={ "f_unity": "when vm({e_out.1})=1", "gain_corner": "find vm({e_out.1}) at=1591.55", "gain_peak": "max vm({e_out.1})", "gain_decade": "find vm({e_out.1}) at=15915.5", "f_unity_high": "when vm({e_out.1})=1 fall=1", }, claims=[ Claim("f_unity", "f_unity", within=0.001, unit="Hz"), Claim("gain_corner", "a_peak", within=0.001, note="Half of R_O/R_I: both poles at once, sqrt(2) each"), Claim("gain_peak", "a_peak", within=0.001, note="and it is the top of the response: no plateau"), Claim("gain_decade", "a_decade", within=0.02, note=( "1000/101: falling at 20 dB per decade, where the circuit " "with stop holds near 100. 2%: a decade up, the op amp's " "loop gain is only about 60 and takes 1.5% off." )), ], units={"f_unity_high": "Hz"}, note=( "The second unity crossing, near 159 kHz, is where the falling " "gain passes 1 on its way down; it is not a claim of the page." ), ), ],)The files it writes
Section titled “The files it writes”The parts, then the nets and the pads on them.
C1 0.1 uF -C2 1 nF -GND1 Ground -R1 1 kOhm -R2 100 kOhm -TP1 Terminal -TP2 Terminal -TP3 Terminal -U1 OpAmp -Net-(C1-Pad1) C1.1 R1.2Net-(C1-Pad2) C1.2 C2.1 R2.1 U1.IN-Net-(C2-Pad2) C2.2 R2.2 TP3.1 U1.OUTNet-(GND1-Pad1) GND1.1 TP1.1 U1.IN+Net-(R1-Pad1) R1.1 TP2.1Every check that ran, and every one left undecided.
5 checks, 0 failed, 0 undecidedWhat the elaborated graph contains, by entity kind.
1 block 9 component 20 connection 5 constraint 1 evidence 3 interface 15 pin 15 port 69 totalsnapshot sha256:78302d128bc7071ac6de815293df4873bf30c865e4f3b9fbbb50ac88a40d51c9All of it, including the KiCad netlist, is in
examples/ti_opamp_handbook/differentiators/low_noise_differentiator/out/. Rebuild it with:
fang build examples/ti_opamp_handbook/differentiators/low_noise_differentiator/low_noise_differentiator.py