Netduino home hardware projects downloads community

Jump to content


The Netduino forums have been replaced by new forums at community.wildernesslabs.co. This site has been preserved for archival purposes only and the ability to make new accounts or posts has been turned off.
Photo

Timing Accuracy Issues


  • Please log in to reply
8 replies to this topic

#1 Nobby

Nobby

    Advanced Member

  • Members
  • PipPipPip
  • 70 posts

Posted 13 July 2012 - 03:54 AM

I'm currently looking into factors affecting the accuracy of Netduino timing. Precision isn't a massive factor, tenths of a second is fine. I've tried two approaches for timing -Using a timer interrupt with constant frequency/Thread.Sleep and then making timing variable adjustments -Implementing Chris Walker's System::Diagnostics::StopWatch and using ElapsedMilliseconds The first approach is the easiest but overhead causes loss in accuracy. The second approach would have the higher accuracy but has a dependency on the framework runtime overhead. System::TimeSpan::TicksPerMillisecond is also fixed at 10,000 instead of being device specific. At the moment, I'm curious as to what factors cause the runtime DateTime clock to slip. I have an application with various medium performance threads running, one being a timing thread. Over a space of 15mins, the timer has fallen more than 10 seconds behind my sports watch and a synchronised timer on a PC application I have running. The Netduino is not being debugged, has release build code deployed to the target board and is broadcasting its time through UART to be observed. Considering the crystal freq of the Netduino Plus, I'm unsure as to the massive accuracy/overhead issues with timing here. I'm currently using the 4.2 framework and firmware associated with that framework.

#2 Chris Walker

Chris Walker

    Secret Labs Staff

  • Moderators
  • 7767 posts
  • LocationNew York, NY

Posted 13 July 2012 - 04:34 AM

Hi Nobby, Is the elapsed time from timers/Stopwatch not matching the increase in DateTime.Now? Losing 10 seconds in 15 minutes is a lot (~1.1%). Can you post a quick code snippet showing how you're calculating and writing out the time? We should be able to run it on our Netduinos to reproduce... Finally...just curious: if you reflash the mainboard with Netduino firmware (instead of Netduino Plus) do you get the same results? Chris

#3 Nobby

Nobby

    Advanced Member

  • Members
  • PipPipPip
  • 70 posts

Posted 13 July 2012 - 05:01 AM

Hi Nobby,

Is the elapsed time from timers/Stopwatch not matching the increase in DateTime.Now?

Losing 10 seconds in 15 minutes is a lot (~1.1%).

Can you post a quick code snippet showing how you're calculating and writing out the time? We should be able to run it on our Netduinos to reproduce...

Finally...just curious: if you reflash the mainboard with Netduino firmware (instead of Netduino Plus) do you get the same results?

Chris


Hey Chris,

Due to the nature of my project, I can't share code but I have created a new project which is dedicated to the task of timing using Ticks.

The project has three threads. The main program thread, aka Main(), sits in a for(;;) loop with a Thread.Sleep(500) call. It does this after it initialises a class that manages the time measurements.

The second thread is fed a time object and reduces its time until it reaches zero or less. The thread is executed with default priority through a lambda expression.

 private static void timerFunc(Clock clock)
        {
            if (clock == null) return;

            long startTicks = Microsoft.SPOT.Hardware.Utility.GetMachineTime().Ticks;
            long endTicks, delta;
            while (clock.time > 0)
            {
                endTicks = Microsoft.SPOT.Hardware.Utility.GetMachineTime().Ticks;
                delta = (endTicks - startTicks) / m_tick; //where m_tick is System.TimeStamp.TicksPerMillisecond

                clock.time -= delta;

                startTicks = Microsoft.SPOT.Hardware.Utility.GetMachineTime().Ticks;
                Thread.Sleep(50);
            }            
        }

The third thread is one that runs in the clock class. It sleeps for 100ms and then uses COM1 to write, in ASCII text, the value of the time of the format "mm:ss.f".

I just started a 15min test against my sports wristwatch and PC application. 10mins into the test, clock.time is eight seconds higher than the timer on my watch.

I haven't tried flashing my Netduino Plus with the Netduino firmware. I have a regular Netduino to use and a handfull of Netduino Plus devices I can downgrade if necessary.

Currently, I'm stuck with the newest firmware for my commercial project. Purely because of the Socket runtime code footprint providing a much needed 10K.

Thanks for having a look at this

#4 Nobby

Nobby

    Advanced Member

  • Members
  • PipPipPip
  • 70 posts

Posted 13 July 2012 - 05:19 AM

Another observation I have made recently in the Netduino plus was similar to this issue. It was before I had upgraded the firmware so it was still running 2.1.xxxxx and .Net Framework 4.1. I had a TTL VGA board and I was displaying the value of DateTime.Now on a screen directly from the Netduino through COM1. On startup, the Netduino would perform a one-time synchronisation of Date & Time from my PC with the help of an application I had wrote to communicate with the Netduino. It had millisecond percision. After about 5 minutes or so, I noticed that DateTime.Now had lagged behind my PC by a few seconds. After leaving it there idle for nearly a day, it was a long way behind. Initially I beleived the discrepency to be with flucutations of time data when my PC was synchronising with my Domain Controller time server until I disabled time synchronisation on my PC and had the same result as well as using my stopwatch as a reference. I'm about to try this project on a standard Netduino running 2.1.xxxx

#5 Chris Walker

Chris Walker

    Secret Labs Staff

  • Moderators
  • 7767 posts
  • LocationNew York, NY

Posted 13 July 2012 - 05:21 AM

Hey Nobby,

Thank you for the additional information. Let's boil it down a bit more, to isolate any potential code or threading issues...

Can you please try creating an app which simply does the following:
  • Create and open an instance of the SerialPort class
  • In a loop: write the current time to the serial port and then sleep 500ms

And then share that code along with your results? If that basic case is losing 1+ seconds per minute...or if it's not...then we'll have a good basis for diagnostics.

Thank you, Nobby.

Chris

#6 Nobby

Nobby

    Advanced Member

  • Members
  • PipPipPip
  • 70 posts

Posted 13 July 2012 - 05:31 AM

Hey Nobby,

Thank you for the additional information. Let's boil it down a bit more, to isolate any potential code or threading issues...

Can you please try creating an app which simply does the following:

  • Create and open an instance of the SerialPort class
  • In a loop: write the current time to the serial port and then sleep 500ms

And then share that code along with your results? If that basic case is losing 1+ seconds per minute...or if it's not...then we'll have a good basis for diagnostics.

Thank you, Nobby.

Chris


Getting onto that right now.

#7 Nobby

Nobby

    Advanced Member

  • Members
  • PipPipPip
  • 70 posts

Posted 13 July 2012 - 07:50 AM

I setup and experiment that follows the guidelines you provided. I created a simple PC app to track any time slip over an elapsed time. Each experiment itteration was roughly 4 mins long. Attached are images showing the differences between different timing strategies.

-mainThread.png shows your experiment rules. Over four mins, there is no clock drift between PC and Netduino DateTime.Now

-threadedMachineTime.png shows the same experiment except data transmission is done every 100ms on one thread and the current machine time is stored in clock.time on another thread every 50ms. There is no drift apart from 100ms synchronisation conflicts between threads every now and then which go back to zero anyway.

-stopwatch.png System.Diagnostics.StopWatch approach shows a drift of about 200ms every two minutes. Clock.time is incremented by stop_ticks - start_ticks after a thread.sleep(50). The itterative code is shown below. It should essentially always match the netduino machine time. There is only 1 line of code overhead separating

for(;;)
{                
                endTicks = Microsoft.SPOT.Hardware.Utility.GetMachineTime().Ticks;
                delta = endTicks - startTicks;
                startTicks = Microsoft.SPOT.Hardware.Utility.GetMachineTime().Ticks;

                clock.time += delta;  
                //clock.time = Microsoft.SPOT.Hardware.Utility.GetMachineTime().Ticks; //this was used in the second experiment
                Thread.Sleep(50);
}   

-internalDrift.png shows how clock.time drifts away from the Netduino system time. The netduino experiment code was modified to only transmit the drift between Microsoft.SPOT.Hardware.Utility.GetMachineTime().Ticks and clock.time(shown in the Current Column for Netduino). The results show that the Netduino clock doesn't drift compared to the PC clock rather clock.time is drifting behind the Netduino clock. As you can see from the code above though, there's nothing to it. Certainly no reason for such a rediculous lack of accuracy. Even if I changed the code so that start_ticks = end_ticks after calculating delta, it doesn't make much of a difference. I did notice that if I reduce or increase the sleep time from 50 down to 25 or up to 100, the accuracy of timing was affected but minimally.

What can you tell me about operations and overhead with large datatypes such as System.Long? Could it be significant?

I will restructure this experiment to perform stopwatch-based approach in the main thread and see what results I get.

Attached Files



#8 Nobby

Nobby

    Advanced Member

  • Members
  • PipPipPip
  • 70 posts

Posted 13 July 2012 - 12:41 PM

I've nailed the culprit on this one but no explaination as to the adverse result obtained. Take the code snipet below:

            long startTicks = Microsoft.SPOT.Hardware.Utility.GetMachineTime().Ticks, endTicks=0, delta=0;                        
            int sleepTime = 50, choice=1;
            
            clock.start = startTicks;

            for(;;)
            {
                if (choice == 0)
                {
                    Thread.Sleep(sleepTime); //Sleep to regulate timing intervals. 50ms here.

                    endTicks = Utility.GetMachineTime().Ticks; //Get the current Netduino time
                    delta = endTicks - startTicks; //Calculate the time difference
                    startTicks = endTicks; //Assume significant overhead from the previous line of code

                    clock.time += delta; //Increment instance member time value
                }
                else if (choice == 1)
                {
                    Thread.Sleep(sleepTime);

                    endTicks = Microsoft.SPOT.Hardware.Utility.GetMachineTime().Ticks;
                    delta = endTicks - startTicks;
                    startTicks = Utility.GetMachineTime().Ticks; //Assume no overhead from the previous line of code and this line as well

                    clock.time += delta;
                }
                else if (choice == 2)
                {
                    //Choices 2 & 3 just involve a different order of operations but have the same logic as 1 & 2
                    endTicks = Utility.GetMachineTime().Ticks;
                    delta = endTicks - startTicks;                    
                    startTicks = endTicks; //Assume overhead from the previous line of code

                    clock.time += delta;

                    Thread.Sleep(sleepTime);
                }
                else if (choice == 3)
                {
                    endTicks = Utility.GetMachineTime().Ticks;
                    delta = endTicks - startTicks;
                    startTicks = Utility.GetMachineTime().Ticks; //Assume no overhead from last and this line of code

                    clock.time += delta;

                    Thread.Sleep(sleepTime);
                }
            }

I performed some optimisations on my original timing code which was not optimised at the time. There was still drift. The results were fairly conclussive though. Choices 0 & 2 provided identical and ideal results. There was no slip between clock.time, the Netduino time and the PC time.

Choices 1 & 3 resulted in the slip of 100ms every 30seconds. The only difference in code was using startTicks = Utility.GetMachineTime().Ticks vs startTicks = endTicks. The previous line of code, delta = endTick - startTicks and the alternative code in choices 1 & 3 were resulting in what appears to be significant overhead. Based on the results and a loop interval of roughly 50ms, it works out to be roughly 166us of slip per itteration.

Not being a huge fanatic of investigating the inner workings of the micro framework, I'm just going to accept that I can't be lazy in design with respect to these things. Failing longer duration tests for choices 0 & 2, There's no evidence of any chronic issue with timing accuracy.

#9 Nobby

Nobby

    Advanced Member

  • Members
  • PipPipPip
  • 70 posts

Posted 14 July 2012 - 08:44 AM

The final observation I made with time slip came down to precision rather than accuracy. Initially my timing precision was in milliseconds. Naturally I elected to just utilise TimeSpan.Ticks/TicksPerMillisecond. The major issue with cumulative timing measurements here is a significant degree of time slip due to rounding-down. The slip increases as the sampling frequency increases. An example of slip due to measurement error was about 100ms every four mins for a interval measurement frequency of 20Hz. I still only require millisecond precision but now use 100 nanosecond precision for higher frequency timing intervals.




0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users

home    hardware    projects    downloads    community    where to buy    contact Copyright © 2016 Wilderness Labs Inc.  |  Legal   |   CC BY-SA
This webpage is licensed under a Creative Commons Attribution-ShareAlike License.