I tried to write code accordng to your textual description, would the folloing be a good approximation?

My 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[level] 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[level]+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[2] 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[level])/((2^level)^2)

That becomes (10 - (-6))/((2^2)^2) reduces to (10+6)/(4^2) = 16/16 = 1 ... as expected

For 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[2] 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[level]*3-'error')/2*level^2.

That becomes ((3*-4)-(-4))/(2*(2^2)) reduce to (-12+4)/(2*4) = -8/8 = -1

Work 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)*S

Correcting 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.