Examples / TI op amp handbook / Additional circuits
Precision rectifier
SBOA092B page 88, Precision Rectifier: an inverting amplifier with RI (2 kΩ) in and two feedback paths, each an RO (10 kΩ) behind a diode. EO is taken from the upper path, between its RO and its diode.
E_O peak = -(R_O / R_I) E_I peak = -5 E_I peakThe 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/precision_rectifier.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 diodes are the circuit, so the program records its reading of them
(reading): both point up the page, the upper from the op amp output to E_O,
the lower from the lower R_O into the output. A positive E_I drives the output
low, the lower path closes the loop, and E_O rests on the virtual ground at
zero. A negative E_I closes the loop through the upper path and E_O = -5 E_I:
the positive hump the page sketches. The claim is a_v = -5, held to the
parts by
require(equals(self.a_v, negative(over(self.r_out.resistance, self.r_in.resistance))))What the simulation found
Section titled “What the simulation found”out/simulation.txt, from the decks under
out/spice/:
| Run | Measured | Claimed |
|---|---|---|
transfer, E_O / E_I at E_I = -1 V | -5 | -5 (a_v), holds |
transfer, E_O at E_I = +1 V | -19.7 µV | 0 ± 1 mV, holds |
half_wave, peak of E_O, 1 V sine in | 5 V | 5 V, holds |
half_wave, E_O at the positive peak of E_I | -19.8 µV | 0 ± 1 mV, holds |
half_wave, lowest E_O | -8.8 mV | not a claim |
Neither half shows a diode drop: both diodes sit inside the loop. The one excursion below zero is a spike of a few microseconds at the zero crossing going positive, while the op amp output swings across two diode drops and the upper diode recovers.
Running it
Section titled “Running it”fang check examples/ti_opamp_handbook/additional/precision_rectifier/precision_rectifier.pypython examples/regenerate.py ti_opamp_handbook/additional/precision_rectifier # needs ngspiceThe whole program
Section titled “The whole program”"""The precision rectifier, SBOA092B page 88 (top).Show 18 more lines
E_O peak = -(R_O / R_I) E_I peak = -5 E_I peak
An inverting amplifier with R_I 2 kOhm in and two feedback paths, each anR_O of 10 kOhm behind a diode, so the op amp closes its loop through one pathon each half of the input. The output terminal is taken from the upper pathonly, between its R_O and its diode.
The figure is small and the diodes are the whole circuit, so the programrecords its reading as a decision (`reading`): the upper diode points from theop amp output up to E_O, the lower one from the lower R_O up to the op ampoutput. Read that way, a positive E_I drives the output low, the lower diodecloses the loop, and E_O sits on the virtual ground through its R_O at zero.A negative E_I drives the output high, the upper diode closes the loop throughthe upper R_O, and E_O = -5 E_I, a positive hump five times the negative halfof the input. That is the half-wave the page sketches, and the diode drops sitinside the loop, so they do not show at E_O."""
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 fang.lang import Parameter, System, kOhm, requirefrom fang.parts import Resistorfrom fang.rationale import Chooses, Citesfrom fang.simulation import DCSweep, Transient
from handbook import ( Bench, Claim, Ground, OpAmp, Run, SignalDiode, Terminal, equals, negative, over, ratio,)
class PrecisionRectifier(System): """E_I through R_I into the summing point, two diode-steered R_O paths back."""
figure = Cites( "E_O peak = -(R_O/R_I) E_I peak = -5 E_I peak. Half wave with " "amplification if desired. Placing rectifiers in feedback loop " "decreases non-linearity to very small value.", document="SBOA092B, Handbook of Operational Amplifier Applications", locator="page 88, Precision Rectifier", )
reading = Chooses( "Which way do the two diodes point?", selected=( "both point up the page: the upper from the op amp output to the E_O " "node, the lower from the lower R_O to the op amp output" ), alternatives=[ { "reading": "both reversed", "reason": ( "the triangles point up with the bar above them; reversed, E_O " "would be a negative hump for a positive E_I, not the positive " "hump the page sketches" ), }, ], rationale=( "under this reading only one path conducts at a time, which is what " "makes the other half of the output zero", "E_O is a positive hump, -5 times the negative half of E_I, as the " "sketch under the figure draws it", ), )
bench = Chooses( "What drives E_I?", selected="a DC sweep from -2 V to 2 V, and a 1 V peak, 1 kHz sine", alternatives=[ { "option": "a 2 V peak sine", "reason": "5 x 2 V is 10 V, near enough the swing to muddy the peak", }, ], rationale=( "the sweep gives the two slopes exactly; the sine shows the half-wave", ), )
a_v = Parameter( "1", default=-5 * ratio, description="E_O / E_I on the half that conducts (E_I negative)", )
e_in = Terminal() e_out = Terminal() r_in = Resistor(resistance=2 * kOhm) r_out = Resistor(resistance=10 * kOhm) r_out_lower = Resistor(resistance=10 * kOhm) d_out = SignalDiode() d_clamp = SignalDiode() amp = OpAmp() ground = Ground()
def architecture(self): self.e_in.probe >> self.r_in.p1 self.r_in.p2 >> self.amp.inverting.signal self.amp.non_inverting.signal >> self.ground.node
# The upper path: R_O to the E_O node, the diode up to it from the output. self.amp.inverting.signal >> self.r_out.p1 self.r_out.p2 >> self.e_out.probe self.d_out.p2 >> self.e_out.probe self.d_out.p1 >> self.amp.output.signal
# The lower path: R_O, and the diode up from it into the output. self.amp.inverting.signal >> self.r_out_lower.p1 self.r_out_lower.p2 >> self.d_clamp.p1 self.d_clamp.p2 >> self.amp.output.signal
def constraints(self): require(equals(self.a_v, negative(over(self.r_out.resistance, self.r_in.resistance))))
BENCH = Bench( page=88, title="Precision Rectifier", runs=[ Run( "transfer", DCSweep(source="VDRIVE_e_in", start="-2", stop="2", step="0.01"), drive={"e_in": "DC 0"}, measure={ "e_at_minus_1": "find v({e_out.1}) at=-1", "e_at_plus_1": "find v({e_out.1}) at=1", "gain_negative": "e_at_minus_1 / -1", }, claims=[ Claim("gain_negative", "a_v", within=0.001), Claim("e_at_plus_1", 0, within=1e-3, absolute=True, unit="V", note="the other half: the upper diode is off and E_O rests " "on the virtual ground through R_O"), ], units={"e_at_minus_1": "V"}, ), Run( "half_wave", Transient(stop="3m", step="1u"), drive={"e_in": "SIN(0 1 1k)"}, measure={ "e_peak": "max v({e_out.1}) from=1m to=3m", "e_off": "find v({e_out.1}) at=2.25m", "e_glitch": "min v({e_out.1}) from=1m to=3m", }, claims=[ Claim("e_peak", 5, within=0.002, unit="V", note="-5 times the -1 V peak of E_I"), Claim("e_off", 0, within=1e-3, absolute=True, unit="V", note="at the positive peak of E_I: E_O stays at zero, " "without a diode drop"), ], units={"e_glitch": "V"}, note=( "E_I is a 1 V peak, 1 kHz sine; the last two cycles are measured. " "The one excursion below zero is a spike of a few microseconds as " "E_I crosses zero going positive, while the op amp output swings " "across the two diode drops and the upper diode recovers. It is " "reported, not claimed." ), ), ],)The files it writes
Section titled “The files it writes”The parts, then the nets and the pads on them.
D1 SignalDiode -D2 SignalDiode -GND1 Ground -R1 2 kOhm -R2 10 kOhm -R3 10 kOhm -TP1 Terminal -TP2 Terminal -U1 OpAmp -Net-(D1-PadA) D1.A R3.2Net-(D1-PadK) D1.K D2.A U1.OUTNet-(D2-PadK) D2.K R2.2 TP2.1Net-(GND1-Pad1) GND1.1 U1.IN+Net-(R1-Pad1) R1.1 TP1.1Net-(R1-Pad2) R1.2 R2.1 R3.1 U1.IN-Every check that ran, and every one left undecided.
1 checks, 0 failed, 0 undecidedWhat the elaborated graph contains, by entity kind.
1 block 9 component 20 connection 1 constraint 2 decision 1 evidence 3 interface 16 pin 16 port 69 totalsnapshot sha256:7216bdcc61dcb01a3b955775df96f902c470fb2497942ba9470adac4db7402eeAll of it, including the KiCad netlist, is in
examples/ti_opamp_handbook/additional/precision_rectifier/out/. Rebuild it with:
fang build examples/ti_opamp_handbook/additional/precision_rectifier/precision_rectifier.py