Skip to content

Commit 9279cd3

Browse files
committed
Add phase-locked loop class
1 parent bea1541 commit 9279cd3

File tree

2 files changed

+165
-0
lines changed

2 files changed

+165
-0
lines changed

SignalTest/PLL.cs

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
7+
namespace SignalTest
8+
{
9+
class PLL
10+
{
11+
private int _sampleRate;
12+
private Vco _vco;
13+
private BiQuadraticFilter _armFilter;
14+
private BiQuadraticFilter _notchFilter;
15+
private Integrator _piPhase;
16+
private float _errSample;
17+
private float _lockSample;
18+
private BiQuadraticFilter _lockFilter;
19+
private bool _isTrackingEnabled;
20+
private float _errScale;
21+
private bool _useIntegrator;
22+
23+
24+
public double CarrierFrequency
25+
{
26+
get { return _vco.GetFrequency(); }
27+
set
28+
{
29+
_vco.SetCenterFrequency(value);
30+
_notchFilter.reconfigure(value * 2);
31+
}
32+
}
33+
34+
public double SpanHz
35+
{
36+
get { return _vco.GetSpanWidth(); }
37+
set
38+
{
39+
_vco.SetSpanWidth(value);
40+
_lockFilter.reconfigure(value);
41+
}
42+
}
43+
44+
public double ArmFilterHz
45+
{
46+
get { return _armFilter.frequency(); }
47+
set { _armFilter.reconfigure(value); }
48+
}
49+
50+
public float ProportionalGain
51+
{
52+
get { return _piPhase.ProportionalGain; }
53+
set { _piPhase.ProportionalGain = value; }
54+
}
55+
56+
public float IntegrationGain
57+
{
58+
get { return _piPhase.IntegratorGain; }
59+
set { _piPhase.IntegratorGain = value; }
60+
}
61+
62+
public float ErrorScaler
63+
{
64+
get { return _errScale; }
65+
set { _errScale = value; }
66+
}
67+
68+
public bool IsLocked
69+
{
70+
get { return Math.Abs(_lockSample) <= 0.05f; }
71+
}
72+
73+
public bool IsTrackingEnabled
74+
{
75+
get { return _isTrackingEnabled; }
76+
set { _isTrackingEnabled = value; }
77+
}
78+
79+
80+
/// <summary>
81+
/// Creates a new Phase-Locked Loop
82+
/// </summary>
83+
/// <param name="sampleRate">The sampling rate of the signal stream</param>
84+
/// <param name="carrierFrequency">The center frequency of the PLL</param>
85+
/// <param name="spanHz">VCO bandwidth</param>
86+
/// <param name="useIntegrator">If true, an the phase error is integrated (type 2 PLL), otherwise is a type 1 PLL</param>
87+
public PLL(int sampleRate, float carrierFrequency, float spanHz, bool useIntegrator)
88+
{
89+
_sampleRate = sampleRate;
90+
_vco = new Vco(sampleRate, carrierFrequency, spanHz);
91+
_lockFilter = new BiQuadraticFilter(BiQuadraticFilter.Type.LOWPASS, 50, sampleRate, 0.707);
92+
93+
_armFilter = new BiQuadraticFilter(BiQuadraticFilter.Type.LOWPASS, 63, sampleRate, 0.707);
94+
95+
// Notch filter at 2x carrier frequency
96+
_notchFilter = new BiQuadraticFilter(BiQuadraticFilter.Type.NOTCH, Math.Min(carrierFrequency * 2, (sampleRate / 2) - 10), sampleRate, 0.707);
97+
98+
_piPhase = new Integrator(0.707f, (1f / sampleRate) * 10f);
99+
100+
_isTrackingEnabled = true;
101+
_errScale = 1f;
102+
_useIntegrator = useIntegrator;
103+
}
104+
105+
106+
public void Process(float sample)
107+
{
108+
// Increment VCO
109+
_vco.Next();
110+
111+
// Multiply with oscillator
112+
float signal = (float)_vco.Cos() * sample;
113+
signal *= _errScale;
114+
_errSample = signal;
115+
116+
// Notch out 2x carrier
117+
signal = (float)_notchFilter.filter(signal);
118+
119+
// Low-pass to get phase error
120+
float phaseError = (float)_armFilter.filter(signal);
121+
122+
//phaseError *= _errScale;
123+
//_errSample = phaseError;
124+
125+
126+
// Low-pass the non-integrated error signal to determine if the loop is locked
127+
_lockSample = (float)_lockFilter.filter(phaseError);
128+
129+
if (_useIntegrator)
130+
phaseError = _piPhase.Process(phaseError);
131+
132+
// Tune the VCO to correct the phase and frequency errors
133+
if (_isTrackingEnabled)
134+
_vco.Tune(phaseError);
135+
}
136+
137+
public float Carrier()
138+
{
139+
return (float)_vco.Cos();
140+
}
141+
142+
public float Error()
143+
{
144+
return _errSample;
145+
}
146+
147+
public float LockSignal()
148+
{
149+
return _lockSample;
150+
}
151+
152+
public void Reset()
153+
{
154+
// Reset all filters to remove past data
155+
_armFilter.reset();
156+
_lockFilter.reset();
157+
158+
_lockSample = 0f;
159+
_errSample = 0f;
160+
161+
_vco.Reset();
162+
}
163+
}
164+
}

SignalTest/SignalTest.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
<Compile Include="Downsampler.cs" />
5454
<Compile Include="Gardner.cs" />
5555
<Compile Include="Integrator.cs" />
56+
<Compile Include="PLL.cs" />
5657
<Compile Include="Program.cs" />
5758
<Compile Include="Properties\AssemblyInfo.cs" />
5859
<Compile Include="PseudoRandom.cs" />

0 commit comments

Comments
 (0)