You measure the frequency (or period) of the controlled signal against the frequency of the reference. Then, drive your servo to keep this "frequency difference" within your control tolerance (in a digital system, you never have infinite precision on how much you can modify your timebase).
What do you care if *your* local clock display changes some epsilon
*after* a NBS-derived radio beacon *claims* the current time has changed? All you care is that "time passes" locally at the same RATE that it does at the reference.A PLL solution would always have your controlled signal trying to track the actual *phase* of the reference. So, any instantaneous changes in frequency in the reference disturb the controlled signal permanently (because the phase has been altered in the reference).
E.g., imagine getting zero crossings from a (50Hz -- easier math than 60) detector with individual periods of:
20, 20, 20, 20, 20, 20, 22, 20, 20, 20, 20, 20A *locked* FLL will have some phase relationship with this reference at the beginning of this sequence. It will persist until the one "bad" cycle is encountered. Thereafter, the phase relationship will change and hold this *new* relationship (the "bad" cycle can have whatever amount of effect on the controlled FREQUENCY that you wish -- based on your filter design -- including *none*).
A PLL would see a discontinuity in phase relationship at that "bad cycle". And, as the reference is now skewed wrt the controlled signal, the PLL will ALTER THE (instantaneous) FREQUENCY of the controlled signal to bring the phase of the reference and controlled signal back into lock.
[Because, Heaven forbid, we wouldn't want our time display to now be a fixed 0.1 seconds skewed wrt that radio reference -- instead of the fraction of a second that it had been previously! If you were a PLL, you *would*! And, would undertake extra control actions to rid yourself of this "abomination"]As to "exactly how": my most recent implementation feeds "LFC events" ostensibly sourced from an ISR (ick!) or hardware timer capture (less real-time impact on the system) to a low priority process that infers the LFC frequency from these "measurements" (assume the local timebase is stable in the short term). From this, the "time per jiffy" is computed and updated. That, in turn, drives the "wall clock" time.
[Changes to the jiffy frequency are handled very carefully > ]To accommodate lost cycles, line noise, etc. (actual banes are determined by the LFC hardware implementation and *it's* vulnerabilities), I interpose another module that examines the event stream from the ISR (etc.) and tweeks it to apply some commonsense to the signals. E.g., two events separated by
1ms probably represent noise. *One* of them should be elided (but, should it be the *first*? or the second??). Because this module can see the events before -- AND AFTER -- it can more intelligently decide which event to elide (though, "on average", either would yield the same long term result -- but NOT instantaneously!).Likewise, the module can inject LFC events IN THEIR ABSENCE retroactively. I.e., it need not inject an event *now* because it believes there SHOULD have been an LFC event "now". Rather, it can afford to wait for *future* events and then "backfill" the missing events -- in essence, rewriting history to keep the control loop running smoothly. I.e., I can have a lengthy dropout -- many LFC cycles "go missing" -- and, AFTER the LFC comes back, I can synthesize the LFC events that WOULD HAVE been generated and feed them into the event queue -- instead of trying to decide "gee, an LFC event *should* have occurred NOW.... and NOW... and NOW, again..."
[Just because events are occurring -- or expected to occur -- at a particular rate, doesn't mean they *have* to occur equidistant in time! So, my servo doesn't get updated for an extra 5 LFC cycles (of the 24hr*60min*60sec*60Hz that occur in a daily interval)... As long as I compensate for that in the calculations... Why make a hard real-time issue out of something that can be softened, considerably, and, as such, made more resilient?]At the same time, you run a similar loop against the RTC (which you keep to maintain time over power outages). You typically have LESS precision interacting with the RTC (it's notion of time isn't as precise as the timebase against which you have been capturing LFC events). So, you need to take measures to remove the "slop" in the data from the RTC (because it is being updated at a relatively slow rate wrt your high precision timebase and you ideally want to see when it's notion of "the current time" actually comes into effect -- when it's internal counters are updated).
Using the long term LFC as a reference, you compute a correction factor for the RTC's oscillator. You then make this a persistent value. And, periodically, update a persistent snapshot of "last known time that system was running".
All of these data are then used to compensate for the accumulated RTC errors that occurred while the device was powered *off*. If the device has been in the same general environment for that time, the same sort of "error" persists in the internal time tracking for the RTC. So, you get results that mimic those of the run-time correction algorithm (WITHOUT the "run time")
The algorithm that I use to accurately distribute time across the network is a refinement of all of this -- multiple levels of servos trying to keep lots of different devices "in agreement" to a very fine level of precision (which need not have any relationship to the "time" that
*you* see on your wall clock)