Phase locked loops


#1

This is a reply to Daniel’s question about “phase locked loops” but somehow I cannot find the original thread.

Hi Daniel,
Very interesting/intriguing question, I started to play with some ideas and I end up with this.

Basically a PLL like the CD4046B http://www.ti.com/lit/an/scha002a/scha002a.pdf has three building blocks:
a phase comparator (the chip above has two different types), a low pass filter and a VCO. Since the LPF and VCO are obvious in sc the trick is in the phase comparator which “compares the phase and frequency of the signal input with the VCO frequency and generates an error voltage proportional to the phase and frequency difference of the input signal and the VCO. The error voltage, Ve(t), is filtered and applied to the control input of the VCO. Vd(t) varies in a direction that reduces the frequency difference between the VCO and signal-input frequency”

The phase comparator type I is described as a “exclusive-OR network that operates analogously to an overdriven balanced mixer”

Since we don’t have such a thing as “BinaryOpUGen(‘XOR’, a, b);” in the scsynth I figured it out that the XOR logic is actually a sum with module 2 like this:

XOR logic is

true xor: true;//false
false xor: false;//false
true xor: false;//true
false xor: true;//true

0==0 xor: (0==0);//false
1==0 xor: (0==1);//false
0==1 xor: (1==1);//true
0==0 xor: (0==1);//true

Equivalent to:

(1+1)%2//0
(0+0)%2//0
(0+1)%2//1
(1+0)%2//1

So I made this sketch, you have to oscillators, one of it is getting the frequency through the filtered phase comparator output, the other is the external one.

For the filter, I tried OnePole but of course at high values is slower and we end up basically with ascending glissandi so lag feels more convenient for this application. Playing with the lag time reveals the errors (bubbling) of the phase comparator typical of the PLL.

The mapping of the frequency should have different settings, for example in the a-196 PLL from Doepfer is a switch of three ranges. That could be refined in the code.

The external oscillator could be replaced by an In.ar

How does it sound?

Greetings,

Alejandro Olarte

Server.default.options.blockSize_(2);

(

Ndef(\pll,

{

var oscRef, oscExt, output, pc, freqfbk=10;

freqfbk = LocalIn.ar(1,0);

oscRef = LFPulse.ar(freqfbk, 0, 0.5);

oscExt = LFPulse.ar(MouseX.kr(2,1000), 0, 0.5);

pc = (oscRef+oscExt)%2;

pc = pc.floor.lag(\lag.ar(0.15));

//pc = OnePole.ar(pc,0.99999);

pc = pc.linexp(0,1,1,10000);

//pc.poll;

LocalOut.ar(pc);

//DC.ar(0)+pc;

oscRef

}

).playN(0)

);

Ndef(\pll).scope;

Ndef(\pll).set(\lag, 1);

Has anyone experiences with implementing such in SC ?
Regards
Daniel


http://daniel-mayer.at



#2

Hello Alejandro,

that’s a very inspiring patch, thank you so much for sharing !
For slow frequency changes the adaption works very well, sometimes latching into partials, as can be expected from theory.

Server.default.options.blockSize_(2);

(
Ndef(\pll, {
	var oscRef, oscExt, output, pc, freqsrc, freqfbk=10, freqQ;

	freqfbk = LocalIn.ar(1,0);
	freqsrc = LFDNoise3.ar(0.2).range(20, 200);
	freqQ = freqfbk / freqsrc;
	freqQ.poll(label: \freqQ);
		
	oscRef = LFPulse.ar(freqfbk, 0, 0.5);
	oscExt = LFPulse.ar(freqsrc, 0, 0.5);

	pc = (oscRef+oscExt)%2;
	pc = pc.floor.lag(\lag.ar(0.15));

	// pc = OnePole.ar(pc,0.99999);

	pc = pc.linexp(0,1,1,10000);
	LocalOut.ar(pc);
	oscRef * 0.1
}).playN(0)
)

Certainly a lot of things we can play with this model !

Sincerely

Daniel