# Simple Phase Locked Loop

Here is a simple Phase Locked Loop, which is a circuit used in radio communications for synchronisation between transmitter and receiver.

The loop works by calculating the (phase) difference between the input signal, and a reference oscillator, and then adjusting the reference until the phase difference is zero. In this code, the adjustment is made by approximating a digital biquad filter's output - simply by multiplying by the Q factor of the filter.

My main concern with this code, is that it would require some re-writing if I wanted to extend it.

import numpy as np
import pdb

class SimPLL(object):
def __init__(self, lf_bandwidth):
self.phase_out = 0.0
self.freq_out = 0.0
self.vco = np.exp(1j*self.phase_out)
self.phase_difference = 0.0
self.bw = lf_bandwidth
self.beta = np.sqrt(lf_bandwidth)

def update_phase_estimate(self):
self.vco = np.exp(1j*self.phase_out)

def update_phase_difference(self, in_sig):
self.phase_difference = np.angle(in_sig*np.conj(self.vco))

def step(self, in_sig):
# Takes an instantaneous sample of a signal and updates the PLL's inner state
self.update_phase_difference(in_sig)
self.freq_out += self.bw * self.phase_difference
self.phase_out += self.beta * self.phase_difference + self.freq_out
self.update_phase_estimate()

def main():
import matplotlib.pyplot as plt
pll = SimPLL(0.002)
num_samples = 500
phi = 3.0
frequency_offset = -0.2
ref = []
out = []
diff = []
for i in range(0, num_samples - 1):
in_sig = np.exp(1j*phi)
phi += frequency_offset
pll.step(in_sig)
ref.append(in_sig)
out.append(pll.vco)
diff.append(pll.phase_difference)
#plt.plot(ref)
plt.plot(ref)
plt.plot(out)
plt.plot(diff)
plt.show()


Here is the output. Maybe in_sig is not a perfect name: signal_in would be easier to read in my opinion (putting the _in at the end is more consistent with you other variable names)

it would require some re-writing if I wanted to extend it.

What kind of extension ?

# Using iterators to get the samples

def sinusoid(initial_phi, frequency_offset):
"""Generates a sinusoidal signal"""
phi = initial_phi
while True:
yield np.exp(1j*phi)
phi += frequency_offset


When initially called, this function will return an iterator: iter_sin = sinusoid(3.0, -0.2)

And add set_signal_in and signal_out methods in your SimPLL class:

def set_signal_in(self, signal_in):
"""Set a iterator as input signal"""
self.signal_in = signal_in

def signal_out(self):
"""Generate the output steps"""
for sample_in in self.signal_in:
self.step(sample_in)
yield self.vco


(maybe signal_out could generate some tuples with the sample_in and phase_difference, or even yield self)

## Usage

You can then do a pll.set_signal_in(iter_sin).

If you have a list of data, you can then do pll.set_signal_in(list_of_values).

## Plotting

For plotting a limited amount of points, you may use itertools.islice

• Thank you! The main extensions I was thinking about were: handling non-sinusoidal (i.e. digital) signal_in, having different loop filters, and being able handle inputs as a whole array as well as on a per-sample basis. – Tom Kealy Feb 22 '16 at 11:17
• Your are welcome! The iterator suggestion might address some of your requests (with iter(list_of_values) for an array of samples): I updated my answer accordingly. To have different loop filters, I think you can create new child classes of SimPLL with another step method. Or create a PLL class without any step method (but the rest of the logic) and different child classes for the different loop filters. – oliverpool Feb 22 '16 at 11:41
• (the iter for the list of values is not necessary) – oliverpool Feb 22 '16 at 13:11