Go Down

Topic: Millis Accuracy Again (Read 7577 times) previous topic - next topic

orly_andico

I have also reproduced this problem on a Digilent Max32 - which definitely has a crystal...  this is truly weird because millis() is implemented using a core timer on the PICMX, not via ISR.

Coding Badly

Here's my problem: over a worm cycle, I cannot get the measured rotational speed to match 86164 seconds. It varies with every run, e.g. 86300, 85950, 86000... about 1000ppm difference.


You clearly have a bug in your sketch.  Until you post your code, all this talk of crystals, resonators, and RTC is a waste of time.  You need to get the bug fixed before deciding if you do or do not need a more accurate timepiece.

To put this matter in perspective, I have an ATtiny13 sitting on my desk, running from an older generation of the internal oscillator, that is more accurate.

http://arduino.cc/forum/index.php/topic,160899.msg1204803.html#msg1204803
http://arduino.cc/forum/index.php/topic,160899.msg1205750.html#msg1205750
...post your code already.

orly_andico

#47
Apr 18, 2013, 10:33 am Last Edit: Apr 18, 2013, 11:20 am by orly_andico Reason: 1
Here is the latest copy - https://dl.dropboxusercontent.com/u/63497702/read_encoder_v32.7z

It is spread over multiple tabs, so I couldn't figure out how to paste them all into one code block.

The key variables are _tstart (time when we finished calibrating the encoder and are now measuring angles for real) and tcnv (the time the current angle was measured).

So, if we know when we started measuring the angle (_tstart) and we know the time the current angle was taken (tcnv), we just multiply by the sidereal tracking rate (_trackingRate), which is 15.04 arc-seconds per second and we know what the encoder angle ought to be.

And any deviations from that, is a mechanical error that we can correct.

Note that tcnv is set in read_encoder (when we get the current encoder reading) and _tstart is set by the set_origin() function. Both are set from millis().

The problem is because millis() can't be trusted, the theoretical encoder angle drifts monotonically in one direction (but not at a constant rate, because that I could fix).

Code: [Select]

void read_encoder(long &A, long &B, long &tcnv) {
 int reading;
 int i;

 long t0, t1;

 t0 = exttimer_millis();

 // this should finish in 5ms or less @ 32ksps
 for (i = 0; i < OVERSAMPLING; i++) {
   reading = read_adc(1);
   A += reading;

   reading = read_adc(2);
   B += reading;
 }

 A = A / OVERSAMPLING;
 B = B / OVERSAMPLING;

 t1 = exttimer_millis();
 
 // tcnv should be in milliseconds
 tcnv = (t0 + t1) / 2;
}

long calc_full_angle (long theta, long tcnv) {
 // calculate the full encoder angle (in DECI-arcseconds!)
 _current_encoder_angle = (long) (get_quadrature_count() * 648) + (theta % 648);

 if (_cal) {
   _current_encoder_angle -= _origin_encoder_angle;

   // don't use seconds directly as angles here are in DECI-arcseconds
   double tElapsed = ((double) (tcnv - _tstart)) / 100;
   _theoretical_encoder_angle = (long) (_trackingRate * tElapsed) + 0.5;
 }
 else {
   _theoretical_encoder_angle = _current_encoder_angle;
 }

 return ( _current_encoder_angle );
}

GoForSmoke


2) Everything I read is that millis() shouldn't lose so much time.  But I see what I see.  My laptop clock could be wrong (even though its actively NTP-syncing).  But the telescope tracking is off, and this is detectable by watching a star (the ultimate reference).  So I know the Arduino is forcing the tracking off, which can only be explained by its losing time.
No, I'm saying that the SOFTWARE won't lose time. Everyone knows the resonator is only accurate to 0.5%, which could be up to be seven minutes over a day or 18 seconds per hour or 6 seconds per 1200 second period (twice what you measured)


Accuracy is within +/-5%.

Some at the right temperature have a chance of being very close to perfect (at the right temperature) as much as they do being off by a full tolerance.

Nick Gammon on multitasking Arduinos:
1) http://gammon.com.au/blink
2) http://gammon.com.au/serial
3) http://gammon.com.au/interrupts

orly_andico

Note that the version of the code posted above uses exttimer_millis()

which is my homebrew version of millis() that doesn't do any better than the library version.

But regular millis() can be substituted everywhere (and originally was) and code functionality is not affected.

As you can also see in the main loop I am just printing out millis()  and this value is drifting with respect to time-stamping from RealTerm.

The drift is around 5 seconds over a 1439-second interval. This is huge (5 seconds error is 226 arc-seconds of angular displacement of the telescope mount, I need to maintain at least 2 arc-seconds over that time period).

afremont

#50
Apr 18, 2013, 11:34 am Last Edit: Apr 18, 2013, 11:49 am by afremont Reason: 1
The ceramic resonator is accurate to .5% (5000ppm).  At room temperature, no device should be this much off, though the "initial tolerance" might suggest it to be so.   My personal experience with Murata Ceralock resonators is that they are surprisingly accurate, but I haven't tested them all.

The part number I am referring to is CSTCE16M0V53-R0
Experience, it's what you get when you were expecting something else.

orly_andico

Sidereal rate should be 86164 seconds.

I need to fudge with figures about

85950 = -2483ppm
86050 = -1323ppm
86300 = 1578ppm

to get the track to not drift. These are all within range for a ceramic resonator.

But.. and I seem to keep repeating myself here.. the same problem exists with a Digilent Maxkit32, which has a +/- 30ppm crystal.

GoForSmoke

Your homebrew version uses the AVR clock, it should have the same trouble. The source is *internal*.

I doubt that you understand about one ISR finishing and the one queued up starting either. Most ISR's I've seen run in a few microseconds or less considering the average instruction runs in 1 or 2 cycles at 16 cycles per microsecond. Gee, 1 clock edge being off by .5% or less, and the error is not cumulative... that clock to trigger an ISR to change a variable that your code doesn't check every microsecond anyway.

How did you do with that random atan() version I posted? The values wandered between 1063 and 1062 if I didn't interact with my PC. If I did then every so often I'd see 1062. What did you get?

@Afremont:
Quote

The ceramic resonator is accurate to .5% (5000ppm).  At room temperature, no device should be this much off.  Being 5000ppm in error is worst case due to temp, age etc....


Problem's in the software and we don't see all the code so it's like an Easter Egg hunt without the back yard.


Nick Gammon on multitasking Arduinos:
1) http://gammon.com.au/blink
2) http://gammon.com.au/serial
3) http://gammon.com.au/interrupts

orly_andico

All the code is in the Dropbox link, the code is in tabs so it's an entire folder worth of files.

I got 1063/1064 with the random atan.

But the extra 63/64 is not the problem - that is the amount of time taken to run the atan loop. I know that will cause the loop time to be greater than delay(1000).  I don't care about that and can budget for it.

I don't expect or want every iteration of my loop to run in exactly 1000 milliseconds. I do care that the millis() count falls behind wall clock time.

afremont



@Afremont:
Quote

The ceramic resonator is accurate to .5% (5000ppm).  At room temperature, no device should be this much off.  Being 5000ppm in error is worst case due to temp, age etc....


Problem's in the software and we don't see all the code so it's like an Easter Egg hunt without the back yard.



I agree, his error seems too high and appears to be too inconsistent, whether it's "in specs" or not, assuming he's using a "real" board.
Experience, it's what you get when you were expecting something else.

afremont

#55
Apr 18, 2013, 11:58 am Last Edit: Apr 18, 2013, 12:58 pm by afremont Reason: 1

All the code is in the Dropbox link, the code is in tabs so it's an entire folder worth of files.

I got 1063/1064 with the random atan.

But the extra 63/64 is not the problem - that is the amount of time taken to run the atan loop. I know that will cause the loop time to be greater than delay(1000).  I don't care about that and can budget for it.

I don't expect or want every iteration of my loop to run in exactly 1000 milliseconds. I do care that the millis() count falls behind wall clock time.


How can you budget for something that doesn't take the same amount of time each time?  Floating point routines are not fixed execution time routines.  Until you start controlling your loop interval with something like:
Code: [Select]

unsigned long previousRollTime = millis();  //  some place to start
void loop() {
 while(millis() - previousRollTime < 1000) {   //  burn up the rest of the previous second
   // do some repetitive stuff that you want to get done while waiting out the second tick
   // like accumulating serial data in a buffer, or checking for user input, or whatever
 }  // end while
 previousRollTime += 1000;  // adjust next roll time
 //  do a bunch more stuff that you want done once per second
 //  you have to be able to get it all done in enough time to catch the next roll minus
 //  how much time you want to spend in the wait loop doing the repetitive things
}  // end loop

you won't get any real consistency.  Even though the individual calls fall behind wall time 39 out of 40 times, overall it is accurate in that there are 1000 per second on average.
Experience, it's what you get when you were expecting something else.

orly_andico

#56
Apr 18, 2013, 01:02 pm Last Edit: Apr 18, 2013, 01:04 pm by orly_andico Reason: 1
I don't care or need for the routines to execute in fixed time. It doesn't matter how much time atan takes.

I need to just know the current time.  Accurately. Doesn't matter how long my routine takes to execute, it's irrelevant, because knowing the correct time will let me calculate my values correctly.

Let me try another analogy.

At time T1 I measure the position of the encoder. Say it's X.

At time T2 (some indeterminate time later) I measure the value again. It is Y.

So I can calculate the velocity = ( Y - X ) / ( T2 - T1 ).

Regardless of how much time has passed between T2 and T1.

But if T2 and T1 are not accurate then my computed value is meaningless.  But the amount of time elapsed between T2 and T1 is irrelevant.

What I meant by "budget" was, if millis() is consistently inaccurate, I can budget for that, e.g. if it's always 1000ppm slow.

Nick Gammon

millis has a certain amount of creep because the timer fires every 1.024 mS.

What is wrong with using micros?
Please post technical questions on the forum, not by personal message. Thanks!

More info:
http://www.gammon.com.au/electronics

orly_andico

I also tried micros() / 1000

Same creep.  Someone here suggested micros() / 1000UL.

Not sure if that would make any difference.

afremont

#59
Apr 18, 2013, 01:21 pm Last Edit: Apr 18, 2013, 01:32 pm by afremont Reason: 1

I don't care or need for the routines to execute in fixed time. It doesn't matter how much time atan takes.

I need to just know the current time.  Accurately. Doesn't matter how long my routine takes to execute, it's irrelevant, because knowing the correct time will let me calculate my values correctly.

Let me try another analogy.

At time T1 I measure the position of the encoder. Say it's X.

At time T2 (some indeterminate time later) I measure the value again. It is Y.

So I can calculate the velocity = ( Y - X ) / ( T2 - T1 ).

Regardless of how much time has passed between T2 and T1.

But if T2 and T1 are not accurate then my computed value is meaningless.  But the amount of time elapsed between T2 and T1 is irrelevant.

What I meant by "budget" was, if millis() is consistently inaccurate, I can budget for that, e.g. if it's always 1000ppm slow.


I showed you a way to keep track of time accurately while still doing asynchronous functions, but not using an RTC.  You also wouldn't need to keep track of how long atan() takes or anything else for that matter.  Of course it will only be as accurate as your resonator.   Did you even read what I wrote a couple of posts back about millis()?  Since you obviously don't want or need my help, good luck but you're making this harder than it needs to be.

@Nick   I already went into great detail about millis(), the 1024uS interval and how it corrects itself so that it is accurate in the long run.

EDIT:  OP, if you use micros() to take the T1 and T2 readings, then you will be within 4uS of reality on each reading.  Would that be close enough?  If you need to run a timer for 86000 seconds then you will need a TCXO or a Chronodot or the like.  If you need 2ppm accuracy, then you're not going to get it any other way.
Experience, it's what you get when you were expecting something else.

Go Up