carrier.cir · 32 lines
* 1 kHz triangle-wave carrier for a PWM heater drive, 2 V peak-to-peak
* Self-oscillating relaxation oscillator: a rail-limited inverting Schmitt
* comparator switches a constant current into/out of a timing capacitor,
* so the capacitor voltage is a linear triangle. Powered only by DC rails;
* no PULSE/SIN source is used, the oscillation comes from the circuit.
.param VCC=15
.param VEE=-15
.param VTH=1.00172 ; comparator threshold -> ~2 Vpp triangle
.param I0=0.40069m ; timing current -> ~1 kHz
.param BETA=0.089042 ; VTH / (0.75*VCC)
.param A=1e5 ; comparator gain
* DC power rails
VCC vcc 0 DC {VCC}
VEE vee 0 DC {VEE}
* Inverting Schmitt trigger; output swing is limited by the DC rails.
Bcomp mid 0 V = max(0.75*V(vee), min(0.75*V(vcc), A*(-V(tri)+BETA*V(sq))))
Rout mid sq 100
Cout sq 0 1n
* Constant-current charge / discharge of the timing capacitor.
Bcur tri 0 I = -(V(sq)/(0.75*VCC))*I0
Ct tri 0 100n IC=0.5
.tran 1u 20m uic
.control
run
wrdata carrier.txt v(tri)
.endc
.end1000.6 Hz and 1.99 V from an ideal comparator and an integrator, on the first render.
modeldeepseek/deepseek-v4.1-flashtaskspice-frother-triangle@v1date2026-09-22rounds1 of 8 allowedcost$0.0507thinkingmediumtokens507,603 in, 28,702 out, of which 19,122 reasoningtime6 min 45 s
DC supplies onlydecided by a number, from the filepass
1 kHzdecided by a number, from the filepass
2 V peak to peakdecided by a number, from the filepass
Straight, even rampsdecided by a number, from the filepass
There is no post about this one, since only some results need one, so the picture, the source and the scoring are here instead.
A milk frother's heater is driven by PWM, and the PWM needs a 1 kHz triangle-wave carrier of 2 V peak to peak. Build that oscillator in ngspice using only DC supplies, so the oscillation comes from the circuit itself, and simulate 20 ms. In a .control block, save the carrier voltage with wrdata to a .txt file so it can be plotted.Frozen at v1. This model got this and nothing else.
The netlist, re-run in ngspice offline and the written carrier read back. A triangle comes from integrating a square wave, so a PULSE into an RC, whose ramps curve, fails, and so does any source that is told the time. How it is scored, and this run's files on GitHub.

