Correct timing error for wrong TOF THUB clock

This plot demonstrates the premise on which this proposed time error correction and its calibration is based:



Assuming that the BunchCrossingNumber is something that resets with the start of each fill, here's some pseudocode:
double BunchCrossingRolloverTime = pow(2,32) / RHIC_clock_frequency;

// We need to be careful with getting the bunch crossing number correct near
// the 2^32 rollover of BunchCrossingNumber. If we're within +/-10% of the time
// to start a new cycle, determine whether we really have a new cycle by whether
// BunchCrossingNumber is low or high.
double BunchCrossingCycleGuess = (Event_unix_time - FillStartTime) / BunchCrossingRolloverTime;
double BunchCrossingCycleNearestInt = round(BunchCrossingCycleGuess);
int LowerBunchCrossingCycleGuess = floor(BunchCrossingCycleGuess);
int UpperBunchCrossingCycleGuess = LowerBunchCrossingCycleGuess;
if (BunchCrossingCycleNearestInt > BunchCrossingCycleGuess && BunchCrossingCycleNearestInt - BunchCrossingCycleGuess < 0.1)
  UpperBunchCrossingCycleGuess++;
else if (BunchCrossingCycleNearestInt < BunchCrossingCycleGuess && BunchCrossingCycleGuess - BunchCrossingCycleNearestInt < 0.1)
  LowerBunchCrossingCycleGuess--;
// else we are confident about the cycle number from using floor()
long long BunchCrossingCycle = (BunchCrossingNumber > (2U << 30) ?
                                LowerBunchCrossingCycleGuess     :
                                UpperBunchCrossingCycleGuess     );

long long FullBunchCrossingNumber = (BunchCrossingCycle << 32) + ((long long) BunchCrossingNumber);

// Determine a time_drift_per_BunchCrossing
// This could either be calibrated, or these quantities may be known
double one_wrong_clock_tick = 1.0 / wrong_clock_frequency;
double one_right_clock_tick = 1.0 / right_clock_frequency;
double time_drift_per_second = (one_wrong_clock_tick - one_right_clock_tick) * right_clock_frequency;
double time_drift_per_BunchCrossing = time_drift_per_second / RHIC_clock_frequency;

double event_offset = fmod((time_drift_per_BunchCrossing * FullBunchCrossingNumber) - phase, event_offset_max);
First, I note that the value of time_drift_per_BunchCrossing may be known from the associated quantities, or it may be calibrated as the slope of the sawtooth model in the above plot if that gives better accuracy than a calculation. It likely only needs a single calibration for the entire dataset, not fill-by fill.

Second, the value of event_offset_max is the maximum value that the clock counter can reach. I understand this to be a 22-bit counter of a clock where each clock tick is 24.414 ps = 25 ns / 210. So event_offset_max = 222 * 25 ns / 210 = 212 * 25 ns = 102.4 μs. This number may need better accuracy than this and could be part of the calibration. It is the amplitude of the sawtooth model in the above plot, and it likely only needs a single calibration for the entire dataset, not fill-by-fill.

In principle, FillStartTime can be chosen for each fill such that the phase is 0, and then only the FillStartTime is needed in a database for each fill. In practice, a FillStartTime of some sort is helpful just to do the calibration, and that FillStartTime needs to be within about a half minute of the the start of a cycle for BunchCrossingNumber so that we properly determine rollover cycles of the BunchCrossingNumber. Using a FillStartTime that is close to the real start time of the fill is also helpful in keeping integers from being unnecessarily large, which can lead to computational errors by exceeding the available bits available in integer or even long long data types.

It is possible to use some reasonable value of FillStartTime to do the calibration of a fill, determine a phase, and then adjust FillStartTime to account for the phase such that we can assume phase=0. Thus only one number, the FillStartTime and not the phase, could be saved in the database. But this requires finding a FillStarTime that satisfies both the phase of zero and is within half a minute of a new cycle for the BunchCrossingNumber, so it's really going to just be easier to keep both a FillStartTime and a phase in the database and not add that complication to the calibration.

The value of phase for each fill should be constrained to be in the range [0,event_offset_max / time_drift_per_second).

-Gene