Go Down

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

PeterH


The rest of the numbers are computed in my code and not relevant to this discussion.


Again, I suggest you run the same test with the extraneous code removed and see whether the problem is inherent in your Arduino, or caused by something you're doing. And then post the actual code together with the output which demonstrates the problem.

CrossRoads

Quote

That sample doesn't seem to handle the micros() rollover... or am I just not seeing it?

You are not seeing it.When micros() goes from FFFFFFFF to 00000000 and beyond, the math of
00000010 - FFFFFF10 = 00000100
works out correctly because the digits above the 32nd bit are dropped.
micros() rolls over about every 72 minutes I think, I've let this run 24 hours at a time tracking against the official US time, and seen very low drift on 16 MHz crystal equipped Duemilanove.
Quote

In any case.. I don't doubt that the code you've shown is accurate, but it is only doing serial output once per second.

Feel free to have it print out faster. Change these 2 lines for hundredths or tenths.

if (prior_seconds != ones_seconds){
    prior_seconds = ones_seconds;   // show time update once/second


Designing & building electrical circuits for over 25 years.  Screw Shield for Mega/Due/Uno,  Bobuino with ATMega1284P, & other '328P & '1284P creations & offerings at  my website.

Erdin

For someone who is interested, I made a sketch to demonstrate the arithmatic rollover yesterday.
Try the second sketch at the bottom, http://playground.arduino.cc/Code/TimingRollover

orly_andico

Reduced the ADC activity to 32 reads per 1 second (instead of the previous 128 reads per 0.25 seconds). This of course also reduced the arc-tangent calculations from 1 per 250ms, to 1 per second.

Now over a 4200-second period, millis() has lost 10 seconds.  This is much less than before.

It does seem that SPI activity and/or the arc-tangent calculation causes millis() and micros() to lose ticks.

GoForSmoke


I converted the code to use micros() - everywhere in my code where I used millis()  I now use micros() / 1000.


micros() returns an unsigned long so try micros() / 1000UL.

Other technical, a resonator is affected by temperature more than a crystal.

You are using floats aren't you? Arduino floats and doubles are both 4 bytes and s-l-o-o-o-w-w.
If you can, just pass raw data (much less of that, hey?) post process on a PC. Results will be more accurate.

Lastly, search on the (free) Processing language for PC. Arduino is an example of the Wiring side of Processing and Wiring, the full set.
1) http://gammon.com.au/blink  <-- tasking Arduino 1-2-3
2) http://gammon.com.au/serial <-- techniques howto
3) http://gammon.com.au/interrupts
Your sketch can sense ongoing process events in time.
Your sketch can make events to control it over time.

orly_andico

#20
Apr 17, 2013, 04:48 pm Last Edit: Apr 17, 2013, 04:53 pm by orly_andico Reason: 1
Here is an extremely simple program which seems to show strong variation in millis() output - about 1.1 seconds over a period of 176 seconds.

Code: [Select]

// try to demonstrate millis loss of ticks
// by using arctangent

#include <math.h>

double v[300];

void setup() {
 Serial.begin(115200);
}

void loop() {
 int i;
 
 for (i = 0; i < 300; i++) {
   v[i] = atan((double) i / 20);
 }
 
 Serial.println(millis());
 delay(1000);
}


Or is there something wrong with my interpretation?

Edit: and I need to do the floats on the Arduino. The whole concept of this box is a stand-alone small unit that corrects telescope mechanical errors in real time.


So.. since it seems established that I'm losing millis() / micros() ticks due to my processing.. what recourse do I have?  does an ISR just get delayed by other activity? or is it blocked entirely?

CrossRoads

Poorly written test example.
There is code that runs from the end of
delay(1000);
to the start of the serial print - it does not happen in zero time.

A more accurate test is as I posted earlier. You capture millis, or micros, add a fixed amount to it, and then watch for that number to be equalled or exceeded.
Designing & building electrical circuits for over 25 years.  Screw Shield for Mega/Due/Uno,  Bobuino with ATMega1284P, & other '328P & '1284P creations & offerings at  my website.

orly_andico

#22
Apr 17, 2013, 05:08 pm Last Edit: Apr 17, 2013, 05:13 pm by orly_andico Reason: 1
Am not sure I understand..

Certainly there is code between the delay(1000) and Serial.print() - that code is definitely in there to simulate the processing I'm doing.

I certainly do not expect the value of millis() to increase by 1000 every time.

I am logging the output with RealTerm and asking RealTerm to timestamp every line.

The value of millis() and the Unix timestamp from RealTerm must coincide if millis() is measuring elapsed time properly.  But they do not, millis() falls behind even over a short period, I expect this deficit would increase with more time.

This is assuming that the RealTerm Unix timestamp from my laptop is correct.

GoForSmoke

Slightly changed, the cleanups probably don't make a difference in timing while the main change is to display elapsed time and save me work.

1st time I get 62
after that I get 1063 and 1064 consistently unless I do certain actions on the PC and then I see 1062. It's somewhat likely that some change at the PC end affects how fast the Arduino can get data out though I wouldn't expect that, but when I don't touch the PC I don't see 1062 in the output either.

Code: [Select]
// try to demonstrate millis loss of ticks
// by using arctangent

#include <math.h>

double v[300]; // Arduino double is the same as Arduino float, 32-bits
unsigned long nowMillis, lastMillis = 0UL;

void setup()
{
 Serial.begin(115200);
}

void loop()
{
 int i;
 
 for (i = 0; i < 300; i++)
 {
   v[i] = atan((double) i / 20.0); // floating-point constants should always have a decimal
 }                                             // you do know that atan() takes radians?
 
 nowMillis = millis();
 Serial.println(nowMillis - lastMillis);
 lastMillis = nowMillis;
 delay(1000);
}
1) http://gammon.com.au/blink  <-- tasking Arduino 1-2-3
2) http://gammon.com.au/serial <-- techniques howto
3) http://gammon.com.au/interrupts
Your sketch can sense ongoing process events in time.
Your sketch can make events to control it over time.

CrossRoads

Try this, will print a number out once a second, should have very little variation in that number.
Should execute your code at consistent times also.
Code: [Select]

unsigned long currentmicros = 0;
unsigned long nextmicros = 0;
unsigned long interval = 250000UL; // 250mS

void setup()

{
 Serial.begin(57600);
 nextmicros = micros();
}

void loop()

{

 currentmicros = micros(); // read the time.

 if ((currentmicros - nextmicros) >= interval) { // xx milliseconds have gone by

   nextmicros = nextmicros + interval; // update for the next comparison

Serial.println(currentmicros);

// do your stuff that takes a few mS

 }  // end time interval check

} // end loop
Designing & building electrical circuits for over 25 years.  Screw Shield for Mega/Due/Uno,  Bobuino with ATMega1284P, & other '328P & '1284P creations & offerings at  my website.

Sembazuru


Here is an extremely simple program which seems to show strong variation in millis() output - about 1.1 seconds over a period of 176 seconds.

Code: [Select]

// try to demonstrate millis loss of ticks
// by using arctangent

#include <math.h>

double v[300];

void setup() {
 Serial.begin(115200);
}

void loop() {
 int i;
 
 for (i = 0; i < 300; i++) {
   v[i] = atan((double) i / 20);
 }
 
 Serial.println(millis());
 delay(1000);
}



As CrossRoads says, your loop isn't 1000 mills long, it's (1000 + <amount of time to do the Serial.println()> + <amount of time to negotiate a loop>) mills long.

Quote

Or is there something wrong with my interpretation?

Edit: and I need to do the floats on the Arduino. The whole concept of this box is a stand-alone small unit that corrects telescope mechanical errors in real time.


Really? Fixed precision won't work for you for the brunt of your calculations? I.E. use longs for your calculations, and know that all calculated values are (to pull a precision out of thin air) a factor of 10,000 greater than the desired value.

Quote

So.. since it seems established that I'm losing millis() / micros() ticks due to my processing.. what recourse do I have?  does an ISR just get delayed by other activity? or is it blocked entirely?


I think you have it backwards. An ISR delays other activity until it is complete. This other activity is all other processing including any other interrupt based operation like counting the mills() and micros() ticks, or capturing an interrupt to trigger one of your other ISRs. (I'm not sure if the ticks counters are actually separate internal ISRs, or if one internal ISR increments micros each time and mills each 1000th time, or if it is hardwired digital logic on the chip die. That amount of detail isn't necessary to understand that the counters that both functions mills() and micros() access pause counting while an ISR is being serviced.)

This is where an external (to the uP) counting clock comes in. It's counter should (in theory) never pause. You use it by querying it's count and acting if it is greater than or equal to the last query + your delay. There may be some jitter of not acting precisely in sync with the external counter, and you can never act at the precise instant of the counter. But, you can be assured of being able to act every time at: (desired counter value + transmission and comparison time) + jitter where jitter = (0 to a small handful of clock cycles depending on if the check is delayed slightly by other parts of the code and/or the check isn't perfectly in sync with the clock but this is why there is the greater than part of your check). Since the "transmission and comparison time" is basically a static value it cancels out of delta timing. And since the clock never pauses counting that jitter should average out to just a slight delay in absolute timing and asymptotically vanish in delta t. If you think the jitter value might actually be greater than your delay step, compare the calculated delay to the desired delay and see how many steps are needed to process. (For example in an analog clock, do I move the second hand 1 place or more?) And/or make the rest of your code work faster so the actual time between count checks is never to rarely greater than the delay you want between actions.

Now selecting the correct chip/module is a whole other ball-game. Because it sounds like this is intended to be a module in a box on a tripod out in the middle of a field, there will be expected wide temperature variations. You should probably try to find a temperature compensated crystal oscillators (TCXO) similar to what is used on the chronodot, but one that provides a count at a higher frequency than 1Hz. I would also try to find one that uses the same communications protocol (I2C, SPI, UART with addressing, etc) as the other modules in your circuit for both conservation of pins and ease in coding. Off the top of my head, I'd check to see what Dallas has.
http://www.catb.org/jargon/html/I/I-didn-t-change-anything-.html

orly_andico

I need to have a time value that is accurate, end of story. Because calculating the (expected) position of the RA axis requires that I know how much time has elapsed.  Without knowing the expected value, I cannot measure the error.

In my full code, millis() / micros() is losing so much time that the box is useless.

I suppose I could use longs. I would have to re-implement arc-tangent using a Taylor expansion.  But I don't need the speed since I only need to calculate all those floats about 5X per second. I do need the Arduino's concept of time to be accurate.   :smiley-eek-blue:

GoForSmoke

Arduino allows the 64-bit integer types long long and unsigned long long. You can do high-precision fixed-point with those, faster than with 32-bit float/double. Problem may be in using 8 bytes per stored value instead of 4.

You will have to make your own fixed-point atan/trig table in flash but look at it this way, lookup is incredibly faster than calculate and you will be using the methods of Charles H. Moore the astronomer in 1959. Look him up!

1) http://gammon.com.au/blink  <-- tasking Arduino 1-2-3
2) http://gammon.com.au/serial <-- techniques howto
3) http://gammon.com.au/interrupts
Your sketch can sense ongoing process events in time.
Your sketch can make events to control it over time.

CrossRoads

If you need it that accurate I would use one of the Maxim I2C/SPI RTC, like DS3231/3234.
Those spit out very accurate times, read it as often as you want to keep the arduino sync'ed.
http://www.maximintegrated.com/datasheet/index.mvp/id/4051
Accuracy ±2ppm from 0°C to +40°C
Accuracy ±3.5ppm from -40°C to +85°C

Compare its frequency stability to the best crstal one can find
http://www.digikey.com/product-detail/en/9B-16.000MEEJ-B/887-1244-ND/2207664
Frequency Stability ±10ppm
Frequency Tolerance ±10ppm
Operating Temperature -20°C ~ 70°C

Designing & building electrical circuits for over 25 years.  Screw Shield for Mega/Due/Uno,  Bobuino with ATMega1284P, & other '328P & '1284P creations & offerings at  my website.

orly_andico

Maybe I'll go back to a lookup table.

I initially started this project on an Uno and didn't want to use RAM for LUT (didn't think about the flash..)

Actually I think a Taylor expansion will be good enough, it can be computed in fixed point.

But my problem isn't arctan! it's the timekeeping!  Taylor expansion, using fixed point, etc. etc. are all solutions to a problem that I don't have, because I am not sure if it's the arctan routine itself that is causing lost ticks... it could be the SPI library (which I can't show in reproducible code because my SPI routine requires the existence of the MCP3304 ADC).

I've rolled my own millis() using TimerOne. I'll see if that works (I don't expect it will, but...)

The new timer code is in its own module, so I can add a ChronoDot or DS1307 later.  I figure a 1PPS RTC is good enough, I'll interpolate the milliseconds from... millis()!

I could also use a DS1390 which has 10ms resolution, but they are not available from the local Farnell/Newark/Element14.

Go Up