Go Down

Topic: Time Fine Correction Function (In seconds per day) (Read 3377 times) previous topic - next topic

gerg

Not tested at all, but this is a little more optimized. It will no longer calculate deviation when it won't be used. And, we no longer do next calculations when its simply not needed. For the typical case, it should be a real win and an almost no difference when drift correction is not required.

Can also be a stack/ram win to move some of the variable declarations (nm, nf) into the scope where they are actually used. Meaning, no extra cost unless they are actually required. I leave that as an exercise to be done, if its appealing.

Code: [Select]
#if defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
SIGNAL(TIM0_OVF_vect)
#else
SIGNAL(TIMER0_OVF_vect)
#endif
{
  // copy these to local variables so they can be stored in registers
  // (volatile variables must be read from memory on every access)
  unsigned long m = timer0_millis;
  unsigned char f = timer0_fract;
  unsigned long nm = nextMillisToPerformTimeCorrectionInteger;
  unsigned int  nf = nextMillisToPerformTimeCorrectionFract;

  m += MILLIS_INC;
  f += FRACT_INC;
  if (f >= FRACT_MAX) {
     f -= FRACT_MAX;
     m += 1;
  }

  if( deviationInSecondsPerDay != 0 ) {
    if (m >= nm)
      {
        nm += stepMillisToPerformTimeCorrectionInteger;
        nf += stepMillisToPerformTimeCorrectionFract;
        if (nf >= 1000)
          {
            nf -= 1000;
            nm += 1;
          }
        m += ((deviationInSecondsPerDay > 0) ? (-1) : 1);
      }

    nextMillisToPerformTimeCorrectionFract = nf;
    nextMillisToPerformTimeCorrectionInteger = nm;
  }

  timer0_fract = f;
  timer0_millis = m;
  timer0_overflow_count++;
}


This is to stop global pollution.
Code: [Select]

static volatile int deviationInSecondsPerDay = 0;
static volatile unsigned long stepMillisToPerformTimeCorrectionInteger = 0;
static volatile unsigned int  stepMillisToPerformTimeCorrectionFract = 0;
static volatile unsigned long nextMillisToPerformTimeCorrectionInteger = 0;
static volatile unsigned int  nextMillisToPerformTimeCorrectionFract = 0;
http://maniacalbits.blogspot.com

odometer

#16
May 24, 2012, 07:14 am Last Edit: May 24, 2012, 07:15 am by odometer Reason: 1
I agree that clock drift needs to be addressed. But I would go about it a different way than you are doing.

Example:
You say, "This is a 16 megahertz oscillator with an error of +90 seconds / day".
I say, "Don't call it a 16 megahertz oscillator with an error. It is a 16 016 667 Hz oscillator."

I suggest keeping track of time as oscillator cycles (maybe rounded to the nearest 64 or 128 or something), and then lazily converting to milliseconds or whatever. If you wish to avoid division, don't convert by saying "XX XXX XXX cycles = 1 second"; instead use the reciprocal and say "1 cycle = XXXXX.X picoseconds", and multiply to get total picoseconds.


westfw

Be careful not to repeat the mistakes of the past, though.  The old code kept a tick count and converted to milliseconds whenever millis() was called, and as a result it had very weird wrap-around behavior, because the math to convert to milliseconds overflowed long before the counter overflowed.

I suppose that since the code still keeps a tick count, it would be pretty easy to write an alternative to millis() that uses 64bit math (or whatever) to convert to a tuned higher-accuracy time.
Code: [Select]
unsigned long long adjusted_picoseconds_per_tick = 62500ULL*256*64;   // default val.  adjust from elsewhere
unsigned long long get_clock_picoseconds()
{
   unsigned long long picos = timer0_overflow_count;
   picos *= adjusted_picoseconds_per_tick;
   return picos;
}
unsigned long high_accuracy_millis()
{
   unsigned long x;
   x = get_clock_picoseconds()/1000000000ULL;
   return x;
}

I hear that the current 64bit math support is "big", but you wouldn't have to use it if you didn't need it.
At this level of precision, perhaps you ought to read the chip temperature and use that to index into an array of "adjusted_picosecond" values.  :-;

odometer

#18
May 24, 2012, 09:06 am Last Edit: May 24, 2012, 09:09 am by odometer Reason: 1

Be careful not to repeat the mistakes of the past, though.  The old code kept a tick count and converted to milliseconds whenever millis() was called, and as a result it had very weird wrap-around behavior, because the math to convert to milliseconds overflowed long before the counter overflowed.

I suppose that since the code still keeps a tick count, it would be pretty easy to write an alternative to millis() that uses 64bit math (or whatever) to convert to a tuned higher-accuracy time.
Code: [Select]
unsigned long long adjusted_picoseconds_per_tick = 62500ULL*256*64;   // default val.  adjust from elsewhere
unsigned long long get_clock_picoseconds()
{
  unsigned long long picos = timer0_overflow_count;
  picos *= adjusted_picoseconds_per_tick;
  return picos;
}
unsigned long high_accuracy_millis()
{
  unsigned long x;
  x = get_clock_picoseconds()/1000000000ULL;
  return x;
}

I hear that the current 64bit math support is "big", but you wouldn't have to use it if you didn't need it.
At this level of precision, perhaps you ought to read the chip temperature and use that to index into an array of "adjusted_picosecond" values.  :-;



No, you don't want to use straight picoseconds in a 64-bit integer.
2**64 picoseconds = about 213.5 days
i.e., nasty surprise after about 7 months

I suggest keeping a count of ticks, as well as a count of "overflows" (with 1 overflow = either 2**31 ticks or 2**32 ticks). Then, when the time in human units (milliseconds, seconds, whatever) is requested, you convert the "overflows" to human units, and separately convert the leftover ticks to human units, and then add the two results.

I wonder if the number of ticks per "overflow" absolutely has to be a power of two.
If it doesn't have to be a power of two, then set 1 "overflow" equal to 1 second (or just barely over 1 second). I think that this will make the conversion easier.

odometer

I just realized that what I suggested in my last post would involve way too much math.

I have another idea:
http://arduino.cc/forum/index.php/topic,107254.0.html

Go Up