Tuesday, July 23, 2024

Single parameter controller for a GPS disciplined oscillator phase locked loop

Introduction

In a previous post I described some of my experiments with building a GPS disciplined oscillator. In my experiments, I lock a voltage controlled oscillator to the PPS signal of a GPS receiver. The locking is achieved by a control loop, which observes the phase error between GPS and the VCO and produces a control signal to adjust the frequency of the VCO.

The GPS PPS signal is very noisy at higher frequencies (above say 1 milli-Hz), so the controller must have a low bandwidth to reject the higher frequency noise. Also, since I don't want to spend a lot of time tuning the controller, I want the bandwidth to be the only parameter.

VCO phase error model

The VCO has a control voltage input \(u\), which controls its frequency \(f_\text{vco}\). This relationship is well approximated as linear around an operating point. Thus near our target frequency \(f_\text{target}\) we can model the VCO frequency as \[f_\text{vco}(t) = f_\text{target} + g (u(t) - u_o),\] in which \(g\) is the VCO gain and \(u_o\) is the needed control value to attain the target frequency. The frequency error is thus given by \[ f(t) = f_\text{target} - f_\text{ocxo}(t) = -g (u(t) - u_o) \]

As the goal is to achieve phase lock, the quantity of interest is actually the phase error. We take the unit of phase to be a full cycle. This makes the phase error \(e\) simply the integral of the frequency error over time. \[e'(t) = - g (u(t) - u_o) \]

As the controller will be implemented in the digital domain, \(u\) will be piecewise constant. This allows us to discretize the model as the recurrence \[ e_{n+1} = e_n - \Delta t g (u_n - u_o),\] in which \(\Delta t\) is the time between the discrete changes of \(u\), \(u_n\) is the value of \(u\) at \(t \in [n \Delta t, (n+1) \Delta t) \) and \(e_n = e(n \Delta t)\).

Controller

The simplest form of controller which can control the phase error to zero is a PI controller. Here the control is determined as a linear combination of the phase error \(e\) and its integral \(i\) as \[ u_n = P e_n + I i_n, \] in which \(P\) and \(I\) are tuning parameters of the controller.

Unfortunately for us, our phase error measurement is very noisy at short time scales. With the simple PI controller the noise would couple straight to the control output through the P term. We thus need to consider something else. One could obviously use a longer interval between the measurements, but that introduces its own set of problems. Our solution is to instead apply a simple first order low-pass filter to the phase error measurement and then combine that with a PI controller.

Since the filter and the controller are implemented in the digital domain, let's transition now to an entirely discretized domain and ignore the continuous domain altogether. In this context, we take our low-pass filter as \[ \hat{e}_{n+1} = (1-\alpha) \hat{e}_n + \alpha e_n, \] in which \(\hat{e}\) is the filtered phase error and \(0 < \alpha < 1\) is a parameter defining the filter bandwidth.

The integral of the filtered phase error, similarly, is considered only in the discretized sense, and is given as \[ \hat{i}_{n+1} = \hat{i}_n + \hat{e}_n \]

The control law of the controller is then \[ u_n = P\hat{e}_n + I\hat{i}_n, \] in which \(P\) and \(I\) are tuning parameters to set the behavior of the controller.

Combining the VCO phase error model with the controller gives the model of the entire system as \[ \begin{equation} \begin{pmatrix} e_{n+1} \\ \hat{e}_{n+1} \\ \hat{i}_{n+1} \end{pmatrix} = \begin{pmatrix} 1 & -\Delta t g P & -\Delta t g I \\ \alpha & 1-\alpha & 0 \\ 0 & 1 & 1 \end{pmatrix} \begin{pmatrix} e_n \\ \hat{e}_n \\ \hat{i}_n \end{pmatrix} + \begin{pmatrix} \Delta t g u_o \\ 0 \\ 0 \end{pmatrix} \end{equation} \]

For the controller to work, we want the errors to converge. The dynamics of the convergence are determined by the eigenvalues of the matrix. For simplicity, we'll choose all three eigenvalues as \(r\), where \(0 < r < 1\).

The characteristic polynomial of the matrix is \[ \lambda^3 + (\alpha - 3) \lambda^2 + (\Delta t g P \alpha - 2\alpha + 3) \lambda + \Delta t g I \alpha - \Delta t g P \alpha + \alpha - 1. \] On the other hand, to have the desired eigenvalues, we want the characteristic polynomial to be \[ \lambda^3 - 3r\lambda^2 + 3r^2 \lambda - r^3. \] Matching the coefficients, we get the equations \[ \begin{align} \alpha - 3 &= -3r \\ \Delta t g P \alpha - 2\alpha + 3 &= 3r^2 \\ \Delta t g I \alpha - \Delta t g P \alpha + \alpha - 1 &= -r^3 \end{align} \]

Solving those equations yield \[ \begin{align} \alpha &= 3(1-r) \\ P &= \frac{1-r}{\Delta t g} \\ I &= \frac{(1-r)^2}{3 \Delta t g} \end{align} \]

The resulting matrix is unfortunately non-diagonalizable, making analyzing the resulting dynamics a bit tedious. The controller however appears to be well-behaved and produces convergence without too much overshoot.

The following figures show simulated responses from a controller with \( r = 0.001 \).

Impulse response of the controller against phase error change
Impulse response of the controller against control offset change

Sunday, April 28, 2024

The confusing I2C bit rate register of CH32V003

The CH32V003 reference manual does not explain some key information for using the I2C peripheral. The issue is mainly with the I2C1_CKCFGR register, but there is also confusion around the FREQ field of the I2C1_CTLR2 register.

Digging a bit, it turns out that WCH appears to be using the same I2C peripheral IP as Gigadevices and Puya use in many of their microcontrollers. Both of the other vendors have a bit more documentation in their manuals.

The explanation of the I2C1_CKCFGR register turns out to be fairly simple, and the values used by the I2C examples going around the net appear to be correct. Its documentation in the reference manual says the following

However, what is left unsaid is the following

ModeDutyT_high clock cyclesT_low clock cycles
F/S=0EitherCCRCCR
F/S=1DUTY=0CCR2*CCR
F/S=1DUTY=19*CCR16*CCR

The bitrate is just BR = F_APB1 / (T_high + T_low), and thus the magical factors of 2, 3 and 25 used in the examples are explained. This also explains where the quoted duty cycles of 33% and 36% come from.

This still leaves the FREQ field in I2C1_CTLR2 to be explained. It is described as

The Gigadevices and Puya documentation agree with WCH's documentation on this: you're expected to program the field with a value, which is the peripheral clock frequency given in integer megahertz.

All examples for the CH32V003 I've seen on the net, however, program it as F_APB1 / 2000000, which is half of the correct value. Also, many of the examples document the value 2000000 as the frequency of the peripheral, which is not correct at all. WCH's documentation is worded in less obvious terms than the other vendors, which may be the source of the confusion.

But what does this register do? If it's possible to write only half of the correct value to the field and still have the peripheral seemingly work, it can't be very critical. This is only covered in the Puya documentation, and still with not enough detail. The English translation states:

This register must be configured with the value of the APB clock frequency to generate data setup and hold times that are compatible with the I2C protocol.
Not sure why the timings given in the I2C1_CKCFGR are not enough to guarantee proper setup and hold, but apparently there is additional signal conditioning which this field affects.

Sunday, March 3, 2024

GPS disciplined oscillator

Introduction

The best (overall) frequency reference I have is in my Racal-Dana 1992 frequency counter, which has the ovenized oscillator option 04A. It's great, but calibrating it with the equipment I have is an annoying task. I calibrate it against GPS. However, as GPS time is stable enough only in the long term, it requires measuring a lot of GPS PPS samples. This takes a long time, and even longer if I want to adjust the time base. This got me thinking if I could automate the process, and after some thought I realized the best approach would be a GPS disciplined oscillator.

While there are commercial GPSDO devices for sale, I can't justify the cost for buying one. I could however design and build one. Turns out I can justify much higher cost for such a project, as there's a a lot of benefit in the learning experience.

My goal would be to produce 10MHz +- 1ppb. I also want to keep the phase noise low, but that's somewhat secondary and without quantitative requirements.

The GPS modules I've used are really old Fastrax parts. They are specified to 50ns RMS jitter on the PPS output. Not great, but workable. This pushes the integration time to around 1 hour to get down to 1 ns jitter. I would not trust the PPS pulse below a 1 hour window anyway (more on that later). Anyway, this puts a requirement on the stability of the oscillator: it would need to be stable (better than +-1ppb) for much longer than the GPS integration period - say 10 hours. This puts me well into the OCXO domain.

DIY OCXO - fail

I happen to have some old VCTCXOs (voltage controlled temperature compensated crystal oscillator), which I found thrown out years ago. They seemed high quality and expensive (from Rakon), so I always wanted to find a use for them. I first thought to build temperature control around one of those and convert it to an OCXO. Using a DS18B20 and PWM controlling a heater I managed to get the temperature to be controlled well within 0.1 degrees Celsius. However, I could not get the crystal oscillator to behave though. I kept the control voltage grounded to make sure any noise on the control voltage wasn't causing the issue. In the end it may have been due to the supply voltage sensitivity of the part, which is not too good at +-300 ppb for +-5% supply voltage. This would require about 1 mV stability for the supply voltage for my application.

Rakon VCTCXO frequency and temperature, moving average over 5 minutes
Measured with Racal-Dana 1992 and DS18B20

Looking around the internets, I found that all the cool cats are playing with OSC5A2B02 OCXOs, which are available on Aliexpress for very cheap. So I ordered some. At about 3€ a piece it didn't seem like a too big investment.

OSC5A2B02 testing

The OSC5A2B02 is much less sensitive to supply variations (+-2ppb for +-5% supply voltage), so just about any regulator suffices. What is important to note however is the control voltage sensitivity, which is about 1000 ppb per volt, or equivalently 1ppb per millivolt. For the initial tests, I simply grounded the control voltage to eliminate it's contribution.

OSC5A2B02 frequency (insulated in PE foam), moving average over 5 minutes
Measured with Racal-Dana 1992

The variation was much larger than the datasheet of the oscillator promised. This is when I realized, that I actually didn't have a clear idea of what the stability of my frequency counter was, and that I could just be seeing it's variation.

To get a better idea of the frequency counter variation, and thus the true stability of the OCXO, I connected a GPS PPS on the B channel of the frequency counter.

OSC5A2B02 frequency (top) and GPS PPS (bottom), moving average over 20 minutes
Measured with Racal-Dana 1992

Bingo! Turns out most of the variation observed in the OSC5A2B02 frequency is actually due to variation of my frequency counter! Who would have guessed that a 3€ Aliexpress OCXO today is so much better than an instrument worth thousands in the 1980s.

Normalizing the OCXO frequency with the PPS frequency (after heavy filtering), the OCXO appears to be within +-1ppb over a day as long as there is no control voltage variation.

Phase noise and jitter

The OCXO datasheet gives some spot values for the phase noise:

  • -80 dBc @ 1 Hz
  • -120 dBc @ 10 Hz
  • -140 dBc @ 100 Hz
  • -145 dBc @ 1 kHz
  • -150 dBc @ 10 kHz
Integrating over this range gives ~0.3 mrad RMS of jitter, or about 4.8 ps.

Control voltage generation

As said earlier, the control voltage is about 1 ppb per millivolt over a range of 4 volts total. Using 16 bit discretization for the range leads to about 0.06 ppb resolution. To get a ballpark figure of how least significant bit transitions affect the phase noise, let's consider the case of modulating the 10 MHz carrier by +-0.03 ppb ( = +-0.3 mHz) at 1 Hz frequency (assume single tone sinusoidal modulation for simplicity). This is given by

\[ y(t) = \cos(2 \pi f_c t + A \sin(2 \pi f_m t)) \]

In which \( A = \frac{\Delta f}{f_m} = \frac{0.3 \text{mHz}}{1 \text{Hz}} = 3 \cdot 10^{-4} \).

Using the sum of angles identity for cosine, we get

\[ y(t) = \cos(2 \pi f_c t) \cos(A \sin(2 \pi f_m t)) - \sin(2 \pi f_c t) \sin(A \sin(2 \pi f_m t)) \]

Since \( A << 1 \) we can approximate \( \cos(A \sin(2 \pi f_m t)) \approx 1 \) and \( \sin(A \sin(2 \pi f_m t)) \approx A \sin(2 \pi f_m t) \). This gives

\[ y(t) \approx \cos(2 \pi f_c t) - A \sin(2 \pi f_c t) \sin(2 \pi f_m t) \]

And finally, by the sine product identity we get

\[ y(t) \approx \cos(2 \pi f_c t) + \frac{A}{2} ( \cos(2 \pi (f_c+f_m) t) - \cos(2 \pi (f_c - f_m) t) ) \]

Thus considering the single sideband phase noise, we see that the modulating frequency is attenuated by \( \frac{A}{2} \) with respect to the carrier, which is -76.5 dBc with our numbers - or an additive 3.3 ps. So not great, but not terrible - especially if we minimize the occurrence of transitions by adding some hysteresis. Anyway, we shouldn't use any less than 16 bits of resolution.

The next issue is how to implement a 16 bit DAC cheaply. Here a pretty obvious candidate is to use PWM. Proper DAC chips with 16 bits cost close to 10 euros, while PWM and a lot of filtering can be achieved with less than a euro. Much less than 1 Hz of bandwidth is perfectly fine, so the question is just how much filtering is needed. Assume the PWM repetition period is 500 Hz and amplitude is 4V. Take then the attenuation of the filter as \(G\). Recalling that the sensitivity of the OCXO is 1 ppm per volt, the power of such a modulation relative to the carrier is given by \( 0.05 G \). To push the additive jitter down to the same scale as the intrinsic jitter of the oscillator, the attenuation needs to be at least 44 dB. This should be easily achievable with simple RC filters.

The main issue with the control voltage is thus the stability requirement. The control voltage reference should remain within 1 mV over several hours. This could be combated with a high stability voltage reference. Problem is that those are expensive, and I would like to keep everything as cheap as possible. As we already have a temperature control loop (in the OCXO itself that is), we might as well use that to keep a voltage reference at constant temperature also. Turns out a TL431C has a typical stability of 4 mV over the entire temperature range, and hopefully better than 1 mV when kept at constant voltage. Also turns out TL431s are really cheap.

Microcontroller

The plan is to implement the PLL using a microcontroller. The uC would be clocked from the OCXO and it times the interval between PPS pulses. This allows determining the OCXO frequency.

The OCXO works on 5V. It would be useful if the microcontroller would also operate on a 5V supply. It also needs to have hardware input capture features, allowing precise measurement of the PPS period against the OCXO frequency. And like everything else, it must be cheap. As luck would have it, I recently ordered 50 units of CH32V003 controllers for 0.20€ per piece (including shipping). They check all the boxes for this project. The even have an internal PLL to allow doubling the 10 MHz OCXO clock for improved timing resolution.

Prototype


I implemented a very quick hack of the system on a CH32V003. It is clocked from the OSC5A2B02 with an internal PLL configured to double the frequency. This clock is used to drive a timer peripheral, which is configured for input capture from the PPS pulse. Timer resolution is increased in software from the HW provided 16 bits to 32 bits - otherwise a full PPS period could not be counted.

After each captured PPS pulse, a simple validation is performed to try to ignore erroneous pulses. A simple PI controller then controls to minimize the frequency error (frequency lock loop). The controller is just parametrized by the control bandwidth and was designed to have critically damped dynamics. Controlling just the frequency still leaves an uncontrolled phase error. I'll be looking into that too, but so far it is of no concern.

The DAC is implemented with a 16 bit PWM running at 305 Hz and filtered with a ~0.2 Hz (-3 dB) first order lowpass filter. The filtering leaves a lot to desire, and this will have to be improved for the final product. The voltage reference is provided by a jellybean TL431C, which is not temperature controlled.

Crude schematic of the prototype voltage control

Operating the control voltage as shown in the figure above allows reducing the full swing range, which increases resolution.

Experiments with the prototype have shown better than expected performance. Though I'm still lacking a data channel for the GPS data, which means that I don't know when the PPS is valid, nor can I keep absolute phase. This leads to some bad samples passing validation and causing trouble.

Closed loop controlled frequency, locked to GPS PPS
GPS PPS moving average over 5 minutes

The figure above shows closed loop control of the OCXO locking on to the GPS PPS frequency. The initial condition was deliberately set about 100ppb off to see the dynamics of the control. The bandwidth of the controller was chosen as ~4 mHz for this experiment to perform the experiment at a reasonable speed. This is too wide a bandwidth for proper operation though. From the plots, we see that the dynamics are nearly critically damped, with only very minor overshoot. This is good enough for me!

75 hour long term stability experiment
GPS PPS moving average over 1 hour

The next experiment was a long term stability test. Here I set the controller bandwidth to ~0.1 mHz to better reject noise in the PPS signal. During the experiment, there were a few moments at which the PPS became invalid. This causes the sudden spikes seen in the error graph. Regardless, the error remained well within +-1 ppb, which was the design goal. Also, the prototype only implements very simple control voltage filtering and the voltage reference is still just at room temperature. The design goal thus seems very much achievable, and possibly going down to +-0.25 ppb is possible with more care taken. The control voltage graph is computational, based on an assumed ideal voltage. I don't know for sure if the drift seen in it is due to the OCXO really needing to be adjusted or the voltage reference drifting and needing compensation, but my guess is on the voltage reference.