The low distortion oscillator problem

Feb 05, 2025 Last reply: 1 year ago 13 Replies

There have been quite a few postings about 1kHz low distortion sine wave oscillators.



The problem is that if you want a get stable output from a sine wave oscillator you have to add a non-linear element to control the gain around the oscillating circuit.



In the original example - the Hewlett Packard sine wave oscillator which got the company going - the non-linear element was the filament in an incandescent lamp whose resistance increased as it got hotter when the circuit put more current through it. It had enough thermal mass that the resistance didn't change much over a single cycle of the sine wave.



The popular option today is a FET where you can modulate the channel resistance by changing the gate-to-channel voltage. The channel resistance isn't completely independent of the current through the channel - it tends to increase a bit with current, independent of the polarity of the current. There's also some ripple on the control voltage applied to the FET gate.



It can still work very well.



I like precision four quadrant multipliers. You can set one up to add a controlled amplitude copy of the output to vary the gain around the oscillating loop - which is handy at start-up - or subtract it from the output. This means that you can trim the oscillating loop so that the multiplier normally only contributes the minimal correction required to compensate for component drift and temperature excursions.



I've set up an LTSpice simulation which illustrates the point, but it used an AD734 as it's analog multiplier, which was horribly expensive at the time and is $A72.99 now.



In theory you could use good quality DAC to generate the correction waveform. It's going to have more distortion than a good quality analog oscillator, but if you can keep the correction waveform small enough the extra distortion introduced will be less than the distortion coming from the basic oscillator.



If you got fancy, you could use the DAC to generate a distorted waveform which precisely compensated for the distortions introduced by the analog part of the oscillator. You'd have to throw in a precision A/D converter to find out what they were, which would make for a very complicated circuit which would be a pain to set up, and not all that cheap.


What limits the amplitude?

Counter-example?

Clipping is a non-linear process. The most linear op amp becomes non-linear as soon as its output hits the supply rails.

<snip>

"JM" snipped-for-privacy@gmail.com wrote in message news: snipped-for-privacy@4ax.com...

There are plenty of examples out there claiming to be a stable output sinewave oscillator, with no obvious non-linear element.

formatting link
But a quick simulation of one of them (a 2kHz oscillator) shows that it's not even 40dB down at 4kHz.

Maybe folow that with a Chebychev low pass filter with a zero in the stop band at 4kHz.

Version 4 SHEET 1 1224 680 WIRE 896 -192 -16 -192 WIRE 896 -160 896 -192 WIRE 720 -48 160 -48 WIRE 896 -48 896 -80 WIRE 896 -48 720 -48 WIRE 720 -16 720 -48 WIRE 160 16 160 -48 WIRE 896 16 896 -48 WIRE 960 16 896 16 WIRE 1088 16 1024 16 WIRE 1136 16 1088 16 WIRE 896 96 896 16 WIRE 1136 96 1136 16 WIRE -16 112 -16 -192 WIRE 160 144 160 80 WIRE 224 144 160 144 WIRE 336 144 288 144 WIRE 400 144 336 144 WIRE 512 144 464 144 WIRE 592 144 512 144 WIRE 720 144 720 64 WIRE 720 144 656 144 WIRE 736 144 720 144 WIRE 832 144 736 144 WIRE 160 224 160 144 WIRE 336 224 336 144 WIRE 512 224 512 144 WIRE 736 224 736 144 WIRE -16 336 -16 192 WIRE 160 336 160 304 WIRE 160 336 -16 336 WIRE 336 336 336 304 WIRE 336 336 160 336 WIRE 512 336 512 304 WIRE 512 336 336 336 WIRE 736 336 736 304 WIRE 736 336 512 336 WIRE 896 336 896 192 WIRE 896 336 736 336 WIRE 1136 336 1136 176 WIRE 1136 336 896 336 WIRE -16 352 -16 336 FLAG -16 352 0 FLAG 1088 16 output SYMBOL voltage -16 96 R0 WINDOW 123 0 0 Left 0 WINDOW 39 0 0 Left 0 SYMATTR InstName V1 SYMATTR Value 12 SYMBOL npn 832 96 R0 SYMATTR InstName Q1 SYMATTR Value BC547C SYMBOL res 880 -176 R0 SYMATTR InstName R1 SYMATTR Value 2k SYMBOL cap 144 16 R0 SYMATTR InstName C1 SYMATTR Value 200n SYMBOL res 144 208 R0 SYMATTR InstName R2 SYMATTR Value 510 SYMBOL cap 288 128 R90 WINDOW 0 0 32 VBottom 2 WINDOW 3 32 32 VTop 2 SYMATTR InstName C2 SYMATTR Value 100n SYMBOL res 320 208 R0 SYMATTR InstName R3 SYMATTR Value 510 SYMBOL cap 464 128 R90 WINDOW 0 0 32 VBottom 2 WINDOW 3 32 32 VTop 2 SYMATTR InstName C3 SYMATTR Value 100n SYMBOL res 496 208 R0 SYMATTR InstName R4 SYMATTR Value 510 SYMBOL cap 656 128 R90 WINDOW 0 0 32 VBottom 2 WINDOW 3 32 32 VTop 2 SYMATTR InstName C4 SYMATTR Value 100n SYMBOL res 720 208 R0 SYMATTR InstName R5 SYMATTR Value 1k SYMBOL cap 1024 0 R90 WINDOW 0 0 32 VBottom 2 WINDOW 3 32 32 VTop 2 SYMATTR InstName C5 SYMATTR Value 90n SYMBOL res 1120 80 R0 SYMATTR InstName R7 SYMATTR Value 1Meg SYMBOL res 704 -32 R0 SYMATTR InstName R8 SYMATTR Value 10k TEXT 32 -96 Left 2 !.tran 0 20 1m 1u uic TEXT 32 -144 Left 2 !.options plotwinsize=0 numdgt=7 method=trap

Version 4 SHEET 1 3020 1316 WIRE 208 -192 -160 -192 WIRE 928 -192 288 -192 WIRE -160 -80 -160 -192 WIRE 208 -80 -160 -80 WIRE 528 -80 288 -80 WIRE -160 32 -160 -80 WIRE -48 32 -160 32 WIRE 96 32 32 32 WIRE 208 32 96 32 WIRE 336 32 288 32 WIRE 384 32 336 32 WIRE 528 32 528 -80 WIRE 528 32 448 32 WIRE 608 32 528 32 WIRE 736 32 688 32 WIRE 784 32 736 32 WIRE 928 32 928 -192 WIRE 928 32 848 32 WIRE -160 144 -160 32 WIRE -48 144 -160 144 WIRE 336 144 336 32 WIRE 384 144 336 144 WIRE 736 144 736 32 WIRE 784 144 736 144 WIRE -336 160 -336 144 WIRE -336 160 -400 160 WIRE 96 160 96 32 WIRE 96 160 16 160 WIRE 528 160 528 32 WIRE 528 160 448 160 WIRE 928 160 928 32 WIRE 928 160 848 160 WIRE 1216 160 928 160 WIRE -400 176 -400 160 WIRE -336 176 -336 160 WIRE -48 176 -160 176 WIRE 384 176 336 176 WIRE 784 176 736 176 WIRE 336 208 336 176 WIRE 736 208 736 176 WIRE -160 288 -160 176 WIRE -64 288 -160 288 WIRE 528 288 528 160 WIRE 528 288 16 288 WIRE 1264 288 528 288 WIRE -160 368 -160 288 WIRE 272 400 192 400 WIRE 416 400 336 400 WIRE 928 448 928 160 WIRE 928 448 848 448 WIRE 192 496 192 400 WIRE 272 496 192 496 WIRE 416 496 416 400 WIRE 416 496 336 496 WIRE 544 496 496 496 WIRE 672 496 624 496 WIRE 1040 512 848 512 WIRE 1120 512 1040 512 WIRE 1216 512 1120 512 WIRE 1040 560 1040 512 WIRE 1216 560 1216 512 WIRE 1120 576 1120 512 WIRE 1264 576 1264 288 WIRE 192 592 192 496 WIRE 240 592 192 592 WIRE 320 592 304 592 WIRE 416 592 416 496 WIRE 416 592 400 592 WIRE -208 640 -240 640 WIRE -160 640 -160 448 WIRE -160 640 -208 640 WIRE 1216 656 1216 640 WIRE 1264 656 1264 624 WIRE 1264 656 1216 656 WIRE 192 672 192 592 WIRE -240 688 -240 640 WIRE -160 688 -160 640 WIRE 1040 688 1040 640 WIRE 1120 688 1120 640 WIRE 1216 688 1216 656 WIRE 416 704 416 592 WIRE 416 704 336 704 WIRE 496 704 496 496 WIRE 496 704 416 704 WIRE 544 704 496 704 WIRE 672 704 624 704 WIRE 928 704 736 704 WIRE -16 720 -48 720 WIRE 32 720 -16 720 WIRE 192 720 192 672 WIRE 192 720 112 720 WIRE 272 720 192 720 WIRE 416 736 336 736 WIRE -16 768 -16 720 WIRE 416 768 416 736 WIRE 928 768 928 704 WIRE -240 816 -240 768 WIRE -208 816 -240 816 WIRE -160 816 -160 768 WIRE -160 816 -208 816 WIRE -160 864 -160 816 WIRE -16 864 -16 832 FLAG 336 208 0 FLAG 736 208 0 FLAG -400 176 0 FLAG -336 64 vcc FLAG -336 256 vee FLAG 1216 160 vout FLAG 192 672 vx FLAG -208 640 vt FLAG -208 816 vb FLAG -160 864 0 FLAG -16 864 0 FLAG 1216 688 0 FLAG 1120 688 0 FLAG 1040 688 0 FLAG 416 768 0 FLAG 928 848 0 FLAG -16 128 vcc FLAG -16 192 vee FLAG 416 128 vcc FLAG 416 192 vee FLAG 816 128 vcc FLAG 816 192 vee FLAG 304 688 vcc FLAG 304 752 vee FLAG -48 720 vg FLAG 928 704 vc SYMBOL res 48 16 R90 WINDOW 0 0 56 VBottom 2 WINDOW 3 32 56 VTop 2 SYMATTR InstName R1 SYMATTR Value 10K SYMBOL res 304 16 R90 WINDOW 0 0 56 VBottom 2 WINDOW 3 32 56 VTop 2 SYMATTR InstName R2 SYMATTR Value 16K SYMBOL res 704 16 R90 WINDOW 0 0 56 VBottom 2 WINDOW 3 32 56 VTop 2 SYMATTR InstName R3 SYMATTR Value 16K SYMBOL cap 448 16 R90 WINDOW 0 0 32 VBottom 2 WINDOW 3 32 32 VTop 2 SYMATTR InstName C1 SYMATTR Value 10n SYMBOL cap 848 16 R90 WINDOW 0 0 32 VBottom 2 WINDOW 3 32 32 VTop 2 SYMATTR InstName C2 SYMATTR Value 10n SYMBOL res 304 -208 R90 WINDOW 0 0 56 VBottom 2 WINDOW 3 32 56 VTop 2 SYMATTR InstName R4 SYMATTR Value 10K SYMBOL res 304 -96 R90 WINDOW 0 0 56 VBottom 2 WINDOW 3 32 56 VTop 2 SYMATTR InstName R5 SYMATTR Value 800k SYMBOL res 32 272 R90 WINDOW 0 0 56 VBottom 2 WINDOW 3 32 56 VTop 2 SYMATTR InstName R6 SYMATTR Value 30k SYMBOL res -176 352 R0 SYMATTR InstName R7 SYMATTR Value 100 SYMBOL voltage -336 48 R0 SYMATTR InstName V1 SYMATTR Value 15 SYMBOL voltage -336 160 R0 SYMATTR InstName V2 SYMATTR Value 15 SYMBOL cap 336 480 R90 WINDOW 0 0 32 VBottom 2 WINDOW 3 32 32 VTop 2 SYMATTR InstName C3 SYMATTR Value 1u SYMBOL res 528 720 R270 WINDOW 0 32 56 VTop 2 WINDOW 3 0 56 VBottom 2 SYMATTR InstName R10 SYMATTR Value 47k SYMBOL cap 304 576 R90 WINDOW 0 0 32 VBottom 2 WINDOW 3 32 32 VTop 2 SYMATTR InstName C4 SYMATTR Value 10u SYMBOL res 416 576 R90 WINDOW 0 0 56 VBottom 2 WINDOW 3 32 56 VTop 2 SYMATTR InstName R12 SYMATTR Value 22k SYMBOL res -224 672 M0 SYMATTR InstName R8 SYMATTR Value 10Meg SYMBOL res 640 480 R90 WINDOW 0 0 56 VBottom 2 WINDOW 3 32 56 VTop 2 SYMATTR InstName R13 SYMATTR Value 47k SYMBOL res 128 704 R90 WINDOW 0 0 56 VBottom 2 WINDOW 3 32 56 VTop 2 SYMATTR InstName R11 SYMATTR Value 1k SYMBOL g 1216 544 M0 WINDOW 3 -255 133 Left 2 SYMATTR Value table{-2m,0,2m,1} SYMATTR InstName G1 SYMBOL cap 1136 576 M0 SYMATTR InstName C7 SYMATTR Value 2p SYMBOL res 1056 544 M0 SYMATTR InstName R14 SYMATTR Value 1 SYMBOL SpecialFunctions\\sample 768 480 M0 SYMATTR InstName A1 SYMBOL cap -32 768 R0 SYMATTR InstName C8 SYMATTR Value 10u SYMBOL voltage 928 752 R0 WINDOW 123 0 0 Left 0 WINDOW 39 0 0 Left 0 SYMATTR InstName V4 SYMATTR Value PWL(0 0 3 -6) SYMBOL OpAmps\\LT1679 -16 160 R0 SYMATTR InstName U1 SYMBOL OpAmps\\LT1679 416 160 R0 SYMATTR InstName U2 SYMBOL OpAmps\\LT1679 816 160 R0 SYMATTR InstName U3 SYMBOL OpAmps\\LT1679 304 720 M0 SYMATTR InstName U4 SYMBOL bi -160 688 R0 WINDOW 3 -236 218 Left 2 SYMATTR Value I={v(vt,vb)/(-100*limit(-1m,v(vg)-0.7,-15))} SYMATTR InstName B1 SYMBOL diode 672 720 R270 WINDOW 0 32 32 VTop 2 WINDOW 3 0 32 VBottom 2 SYMATTR InstName D1 SYMATTR Value 1N914 SYMBOL diode 272 416 R270 WINDOW 0 32 32 VTop 2 WINDOW 3 0 32 VBottom 2 SYMATTR InstName D2 SYMATTR Value 1N914 TEXT 1048 -104 Left 2 !.tran 0 5 0 1u startup TEXT 1048 -128 Left 2 !.options plotwinsize=0 numdgt=15 TEXT 1056 -80 Left 2 !.save v(vout) v(vg) v(vc)

Very funny, The sample and hold at A1 is an obviously a non-liner element, and B1 is even more obscure, but "limit" is a non-linear operation.

Saving only the nodes voltages you want us to pay attention to is a pretty transparent trick. I saved a few more - not enough to have a particularly clear idea of what you are doing, but quite enough to be confident that it isn't any kind of counter example.

You can also use an LM13700 to build a "solid-state lightbulb"! A grounded virtual resistance with a logarithmic V/I curve.

It's obviously clipping on the supply rail. It's pretty subtle clipping

- the top of the sine wave is sightly, but perceptibly, flatter that the bottom, but it does stick out like a sore thumb in the Fourier transform.

We had a long discussion of this in one of the myriad other 1-kHz oscillator threads. One approach is to use a comparator+integrator to control the tail current source (suitably cascoded).

The key is for the gain-setting mechanism to be outside the oscillator loop, so that it doesn't get run through its range on each cycle. The bias of the active device does change some, of course, but that's harder to avoid.

Cheers

Phil Hobbs

But where can I buy those linear diodes?

The idea of using a s/h to pick off the sine amplitude, for level feedback, is interesting. Properly done, it should result in a zero-ripple amplitude signal.

Or use an active full-wave rectifier to get the average, and filter the heck out of that.

I suspect that nobody needs a way-sub-PPM THD sine wave, so it's pretty much a game.

One might Spice using an ohmic mosfet or two as a low distortion variable resistor. The i/v curves look awfully straight around zero.

I spent some quality time with that complementary Class AB car stereo amp of JT’s last summer, and the more time I spent, the more impressed I was.

Its bias loop used an LM311 comparator sensing the minimum collector current at the zero crossing, and charged up a biggish cap that set the voltage between the PNP and NPN bases. Every time it got too low, the comparator dumped a bit of charge into the cap, and a bleed resistor took it out again.

Lots of us have done similar things, e.g. the class-H TEC driver in our LC120 laser controller. The really nifty thing about Jim’s circuit was that it measured what you actually care about, namely the minimum class-A current right at the crossover point, rather than some DC average that depends on the waveform, power supply droop, and other stuff with bupkis to do with the crossover distortion. It worked brilliantly, according to the spherical cows.

Something like that, measuring the instantaneous peak voltage of our oscillator, would do an excellent job of regulating the tail current to keep the amplitude constant.

Cheers

Phil Hobbs

My NMR gradient coil drivers had PPM current accuracy and microsecond settling. I used many parallel mosfets with an opamp per fet to turn each one into an essentially ideal device, zero threshold voltage. That's fairly easy to bias to zero-deadband class AB.

formatting link

Looks like a beast.

Would have been hard to do in 1968, though!

Cheers

Phil Hobbs

Join the Discussion

Have something to add? Share your thoughts — no account required.

Didn't find your answer?

Ask the community — no account required