More GPSDO designs - Page 1
Forum section: electronics / projects

author: S57UUU
author_rank: Regular Contributor
timestamp: November 16, 2022, 06:30:05 pm
content: Hello!For about a year now, I have been working on a time/frequency reference for my SIDI interferometer. It is still not a finished project, much a work in progress, but I accumulated a lot of material, and decided to publish it. I was writing this as I went along. Later I tried to organized it into sections, etc, but it is still quite bloggy.http://lea.hamradio.si/~s57uuu/mischam/gpsy/index.htmlMarko Cebokli

author: MIS42N
author_rank: Frequent Contributor
timestamp: November 21, 2022, 11:43:11 am
content: I just read through GPSy Dances, epic story. There are parallels with my own journey, but I haven't gone quite as far.The development of disciplining algorithms is interesting. My GPSDO is self tuning, it does a calibration run by initially trying the maximum and minimum control voltages and noting the frequency errors. If they were for instance +16Hz at 3V and -8Hz at 0V, it then computes a nominal zero frequency error (in this case 1V) and slope (8Hz/V) then tries to halve the larger error (the +16Hz to +8Hz) by applying 1V+8*1V/8 [the calculated sensitivity] = 2V. Now there is a new pair of values (maybe +9Hz and -8Hz because the slope is not 100% linear) and do it again. The process is repeated (halve the largest) until both are in the region of +0.25Hz and -0.25Hz. This is close enough to the actual frequency to derive a pretty accurate sensitivity and a nominal start voltage. These are stored in non-volatile memory and used on all subsequent starts. The advantage of this approach is it works with any OCXO and caters for +ve or -ve control slopes. (I haven't tried it with a ceramic oscillator with +-50ppm tuning range, but it should handle it - the measuring loop cuts off as soon as the deviation is larger than 256 cycles, or has run for 256 seconds.)The second thing is the corrections are applied periodically based on accumulated GPS arrival times. The sweet spot with the OSC5A2B02 seems to be 512 seconds (the period has to be 2^n). My best GPSDO is applying corrections of maximum 4*10^-11 (0.4mHz), which is probably as good as one could expect from a cheap OCXO. The algorithm is somewhat complex in that readings are cascaded through a series of accumulators. First, 2 readings a second apart are compared. Then 2 values that are each the accumulation of 2 readings. Then 2 values that are each the accumulation of 4 readings, and so on until comparing 2 values that are each the accumulation of 256 readings. At each comparison there are guard values that, if exceeded, initiate a premature correction. This ensure a lock within 3 minutes of applying power, settle to better than 1 ppb after 15 minutes, usually stops hitting guard values within a day. There is no reason why the logic can't handle much longer periods if the OCXO was stable enough. I have some evidence that a good DOXCO will flywheel for 4096 or even 8192 seconds. Yet to try that.It looks like you will be buying a u-blox zed-ft9 or similar if you want the timing accuracy you are looking for. I think any single frequency (L1 band) GPS is going to have ionospheric problems. The dual frequency receivers can compensate.If you think reflections are causing any problems, the quadrafiliar antenna is better than the patch antenna at rejecting them.

author: S57UUU
author_rank: Regular Contributor
timestamp: November 21, 2022, 07:25:39 pm
content: Yeah, it's a bit of TLDR, but I wanted to record all of the blind alleys and dead ends I encountered, so that other people might avoid them.Characterizing the OCXO on startup is a good way to save the user from loop tuning, especially if many people build it and experiment with various OCXOs. Currently, I am also testing a bunch of different OCXOs and rubidiums, will make another TLDR web page, maybe in a month or two. The CTI OSCA5A2... are in fact not that bad, provided you put them in a benign environment (no air drafts and fast temperature changes). They also have quite low phase noise.I did not completely understand the second part, the two by two thing (I guess I'll have to take the time and dive into your code someday). If I understood right, you make a binary "tree" of comparisons. But what do you do after each comparison?In the future, I will probably end up with one of those expensive dual freq receivers (which also need a dual freq capable antenna, etc). Before that, I'll try the older "T" types, like LEA-6T or NEO-8T, etc - whatever I can get cheaply off the web :-)Some time ago, I bought a couple of Chinese QFH kits. They contain a PCB, a SMA connector, and some uncut wire, but no instructions. The length of the arms is very important in the QFH, so I'll have to look in the textbooks.

author: MIS42N
author_rank: Frequent Contributor
timestamp: November 21, 2022, 09:41:25 pm
content: I am writing up how the program works, here is a cut and paste about the tree:The logic for the accumulation is relatively straightforward. It is guided by a count of the seconds over which accumulation occurs, starting at second zero as the second after the last correction. This count is inspected as a binary number: e.g. second 27 is inspected as 11011.Each second:1. Place the phase error for the current second in E (for Error), set the level counter L to zero.2. Counting from the rightmost digit of the counter inspect binary digit number L.3. If the inspected digit and all digits in the counter to the left (i.e. for more significant levels) of the inspected digit are zero then this is the first value received at this level. Set the “previous accumulated value” (A) = E and go to instruction 6.4. Otherwise there is a previous accumulated value at this level. Calculate the errors using the previous accumulated value (A) and E as the two values. Test the calculated errors against predefined limits. If either limit is exceeded, initiate a correction. Otherwise, swap values A and E.5. If the inspected digit is 1, add A to E, increase the level L by 1, return to instruction 2.6. If the level is for the chosen maximum period (e.g. if applying corrections every 1024 seconds, the chosen level is 10) then initiate a correction, otherwise finish.With the two values, A and E, the frequency error is proportional to E-A (if the phase hasn't changed between them, then the frequency is right but not necessarily in phase with the reference (the 1pps))The phase error at the end of the measurement period is proportional to 3E-A [derived from double the average - A+E giving a value proportional to the phase error in the middle of the measuring period and adding the change due to frequency error (doubled) 2(E-A)]Knowing those values and the sensitivity, a correction is computed.The 'predefined limits' is a bit arbitrary. I aim to keep the phase error below 1 cycle (quite large), and the frequency error below 10^-9 (1ppb). The large phase error limit is because badly positioned antenna can cause the 1pps to wander quite a bit, and I don't want to trigger a correction unnecessarily. The values are not that important, as long as they cause rapid corrections during warmup but no (or minimal) corrections once warmed up.I record the phase error (to the nearest 25ns). Over a period of 3 days the 'good' GPSDO did not go outside +-50ns, which may be wandering OCXO or may be wandering 1pps. One person is using an earlier version of the GPSDO to test and adjust a rubidium. Because it is locked, measurement over long periods are still valid. He is still working on it, I'm looking forward to the outcome.

author: S57UUU
author_rank: Regular Contributor
timestamp: December 03, 2022, 10:59:05 am
content: I tried to write code accordng to your textual description, would the folloing be a good approximation?When I feed it an error of one on each tenth measurement, it does a correction every five steps.step0:do_correction(accumulator); sleep(1);start:count=-1;step1:error=measure();level=0;count++;step2:if (count < (1<<level)){accumulator=error; //step3if (level == maxlevel) goto step0;goto step1;}if (fabsf(error-accumulator) > predefined) goto step0; //step4swap(&accumulator,&error);if (count & (1<<level)) //step5{error += accumulator;level++;goto step2;}if (level == maxlevel) goto step0; //step6goto step1;The "step" labels correspond to the steps in your description.I attached the whole program below.

author: MIS42N
author_rank: Frequent Contributor
timestamp: December 04, 2022, 05:46:09 am
content: Quote from: S57UUU on December 03, 2022, 10:59:05 amMy C code is very rusty, but yes, it looks close but not right. 'Accumulator' is an array indexed by level. And it is a good idea to make 'predefined' an array also.I didn't say what units are used for the value 'error'. When programming, this can be a convenient unit that makes programming easy, but not necessarily making understanding it easy. My GPSDO measures error in 25ns units and that is used without converting to anything else. All my arithmetic is integer for speed. If you look at the following code, the only divisions are powers of 2, handled as shifts.However, to make the following easy, assume error is measured as a real number in cycles (i.e. 1 = 100ns between expected and actual arrival time of the 1pps using a 10MHz GPSDO: 15ns early becomes 0.15, 25ns late becomes -0.25 etc.)If you work through what the code is doing, Accumulator is the accumulated error of all errors for a period of 2^level seconds. And when 'error' is passed at that level, it is an accumulation of the next 2^level seconds. If no limits are reached, Accumulator+error is the accumulation of 2^(level+1) seconds, and is passed to the next level.Assume for example that the GPSDO frequency is off by 1Hz. Then the errors returned over 8 seconds will be (for example) -3,-2,-1,0,+1,+2,+3,+4 (cycles). Accumulator will add up the first 4 giving -6. When 'error' is passed to level 2 it will have a value of +10. Because the accumulation is in cycles, the frequency error can be determined by ('error' - Accumulator)/((2^level)^2)That becomes (10 - (-6))/((2^2)^2) reduces to (10+6)/(4^2) = 16/16 = 1 ... as expectedFor any particular level, the divisor ((2^level)^2) is fixed. So to detect a limit of 1Hz error or more at level 2, it is not necessary to do the division, just make predefined equal to 16.As important or maybe more important than a frequency error, is a phase error where the GPSDO is lagging or leading the 1pps. If for instance it was 1 cycle lagging the readings for 8 seconds would be -1,-1,-1,-1,-1,-1,-1,-1.The frequency error is (-4-(-4))/((2^2)^2) = 0. The phase error (approximately) is calculated as (Accumulator*3-'error')/2*level^2.That becomes ((3*-4)-(-4))/(2*(2^2)) reduce to (-12+4)/(2*4) = -8/8 = -1Work this for the first example is (3*10+6)/8 = 4.5 ... which is the phase error between the last 1 second measurement (+4) and the expected phase error (+5) for the next second because when there is a frequency error the phase error changes every measurement.I have a predefined limit for phase error at each level. Again the divisor is known so the limit can be tested just using the numerator.To use the measurement for correction, need to know the sensitivity (S) of the OCXO is V/Hz (e.g. an S of 0.1 means changing the voltage by 0.1V alters the frequency by 1Hz)Then to correct a frequency error is simply (-frequency error)*SCorrecting a phase error is not so straightforward, as it requires the introduction of a frequency error over a period of time. The adjustment is ((-phase error)*S)/(time to correct)If your code was correct, it would always hit the maxlevel because there is no frequency error, just a phase error of 0.1 (9 zeros and a 1 in 10 periods).I hope this makes sense. If not, ask questions.

author: S57UUU
author_rank: Regular Contributor
timestamp: December 04, 2022, 09:04:31 am
content: Thanks! My rusty brain will need some time to digest this...My program was not intended to be a simulation, as there is no feedback from do_correction() to measure(), therefore I did not care about scaling, I just wanted to uderstand the averaging algorithm.Will be back with more questions...

author: MIS42N
author_rank: Frequent Contributor
timestamp: December 04, 2022, 10:45:46 am
content: I made a booboo.The calculation of phase error is NOT (Accumulator*3-'error')/(2*(level^2)). It IS ('error'*3 - Accumulator)/(2*(level^2)).It is easier to understand if it is looked at as:The phase error in the middle of the measurement period is (Accumulator <the accumulation for the first half> + 'error' <the accumulation for the second half>)/(level^2*2)Have to divide by level^2 to convert accumulation to average. Have to divide by 2 because average over all is (first average + second average)/2But we don't want the phase error at the middle, we want it at the end. So it has to be adjusted by the (frequency error * length of time <seconds>) since the middle.The frequency error was ('error' - Accumulator)/((2^level)^2) and the time in seconds is 2^level so the phase change is ('error' - Accumulator)/(2^level).To match the denominator of the first calculation multiply numerator and denominator by 2:- (2 * 'error' - 2 * Accumulator)/(2 * (2^level))Adding the two together, the numerator is - (Accumulator <the accumulation for the first half> + 'error' <the accumulation for the second half>) + (2 * 'error' - 2 * Accumulator)collecting terms gives (3 * 'error' - Accumulator).Phew!My calculations are all integer, so I don't do any divisions (right shifts) until after multiplying by sensitivity. Sensitivity is expressed as adjustment to the 24 bit number that is fed to the pseudo DAC routine to get an 0.25Hz change of frequency (because all the measurements were in units of 25ns, which is a frequency error of 0.25Hz). The sensitivity is adjusted (by shifts) until it is at most a 13 bit signed value, and the number of shifts to do that is stored. The accumulated values are never more than a 16 bit signed value, so 32 bit arithmetic stays in range multiplying 16 bit and 13 bit. The final value is shifted in a single shift which is the sum of the shifts in the arithmetic and the shift to get the 13 bit sensitivity value. This ensures minimum loss of accuracy in the calculations.With a faster processor, arithmetic could be floating point and all the fiddling unnecessary. My aim is to get the result with an 8-bit processor. It is my hobby, lean mean assembler code. A bit useless these days. But the basic method is useful, could be coded for any processor.

author: S57UUU
author_rank: Regular Contributor
timestamp: February 08, 2023, 02:00:13 pm
content: I did some tests by connecting two identical GPSDOs to the same antenna, to see how much of the errors are generated internal to the GPSDOs. I added the description of this as a new section to my "GPSy dnaces" page here:http://lea.hamradio.si/~s57uuu/mischam/gpsy/index.html#common

author: MIS42N
author_rank: Frequent Contributor
timestamp: February 09, 2023, 07:00:37 am
content: Quote from: S57UUU on February 08, 2023, 02:00:13 pmlooking at the second to second variation, it seems the minimum step is about 0.3mHz or 3 parts in 10^-11. If it is assumed that the control voltage is from a 16 bit DAC, then multiply by 65536 to get the full pulling range - about 20Hz or 0.25Hz/V. That's about the sort of figure one would expect. Another way they specify it is EFC = ±1ppm (1 part per million of 10MHz is ±10Hz - 20Hz).The disciplining algorithm is pretty bad if it jerks the frequency around like that.I don't have the equipment to do this for my GPSDO, but because it only changes the control voltage at long intervals (512 seconds - more than 8 minutes) and I know by how much, I'd be confident in saying it does better than that (even though the OCXO is not as high spec). However, if two were compared the phase difference would be quite large at times. The reason for better control is I synthesize a 24 bit DAC so a 1 bit increase or decrease is invisible. This allows me to have long averaging periods. With a good signal and a cheap GPS module (NEO 7) I get maximum change of 0.4mHz and maximum phase error of half a cycle.I'm thinking I'd like to try a timing GPS module (LEA M8T maybe) as the performance appears to be limited by the consistency of the 1pps. I'm not worried by the 22ns jitter as statistically this averages to less than 2ns over the 512 seconds.

author: GigaJoe
author_rank: Frequent Contributor
timestamp: February 11, 2023, 12:44:33 am
content: I had mention in another topic , anywayas voltage control steps ideally as little as possible so maybe not a bad idea, to use a summing amplifier , from multiturn resistor as rough and small variation from DAC.from assembly point it would be not bad idea build these components as tight as possible and isolate it in a Styrofoam box.my similar implementation: LT1021 , OP07 , 1 multiturn, and 12-bit maxim dac, gives very sufficient result with resistors 50ppm , and Styrofoam box about 3.5 cm thick. steps was approx 0.45mV but it adjustable per implementation.

author: S57UUU
author_rank: Regular Contributor
timestamp: February 11, 2023, 10:57:04 am
content: Not sure, but it seems the telecoms/cellular guys are more concerned about long term drift, and probably want a quick re-lock after a loss of lock. As mentioned, I have two of these Trimbles, and they both behave the same, so it is unlikely a fault.Don't know what D/A is used on the Trimbles, the only two chips I can't find datasheets for are an SST343248 and a Kynix KY32-464WP. Otherwise, the main "players" are a Xemics 3330R GPS front end, a Xilinx XC3950 FPGA and a TI TMS320VC5509AZHH DSP. I guess they don't use PWM, because that by itself would need a longer time constant smoothing.In the "final" version of my MBSD I plan to use a MAX5719 20 bit DAC.Some time ago, I have bought a couple of NEO-8T modules on cut boards. I destroyed one when hot air desoldering (the 0402 stuff fell off the module, because of my jerky handling), the other one probably survived, but I haven't tested it yet. The "8" series uses a 48MHz clock, so for MBSD I must first make a 48 MHz VCXO synth. I want a very clean clock, so I don't plan to use DDS or similar stuff.I plan to slowly progress from plain navigation modules (NEO-6M) to timing modules (NEO-8T) and then dual freq ones (in the mean time, Ublox came out with the "10" series, but no datasheets yet onine). This way I can see the differences between them, and satisfy my curiosity :-)The 9 and 10 series are still very expensive, so I will wait a bit. They also need a dual frequency antenna, these are 70eur and up on Ebay.Currently, I have to relocate some of my stuff, so there will be some time before I can put up a decent antenna and start measurements again.