C# .NET timing : millisecond accuracy for data logging

So recently I was doing a data-logging project where I need to fetch data from a PIC at 100ms intervals via a C# program ( .net 2.0 ). It needed to be accurate down to the millsecond level pretty much.
I started with Windows.Forms.Timer to time the thing but found that when I was setting 100ms intervals for the timer it was actually calling my method every 107ms or so on average, which was not acceptable for what I was doing. I tried System.Threading.Timer also but I got a similar result. I realise that .net is not a real-time operating system, but I mean come on.
After reading a bit ( e.g. http://www.codeproject.com/KB/system/timers_intro.aspx ), I figured that there seems to be issues expecting anything less than 10ms accuracy due to the way .net is scheduling things.  Some guy on code project ( http://www.codeproject.com/KB/dotnet/MicroTimer.aspx ) has written a timer that is accurate down to the microsecond level which looked promising. So I started to use this for a while, but it seemed to need high processor requirements which meant that on my Toshiba Satellite A210 things were getting messy and the timing was not working properly ( generally it was ok on my Phenom II dual core ).
Anyway after a bit more digging, I found out that there is code for a multimedia timer in winmm.dll ( win32 library ) and so I imported these functions into my C# ( .NET Framework Solutions and pinvoke.net are awesome resources for this aspect of .NET programming by the way ), and wow this timer seemed to do the trick. Now when I ask for events to fire every 100ms, they fire pretty much every 100ms ( on my machine it is creeping backwards by about 10ms every 10 minutes or 60ms an hour ), which is great for me on this project. Also, check out this article from another guy who talks about this timer.

Also I decided to throw all the timers I had tried into one class to make it easy to switch between using the different timers without having to keep changing bits of code for start and stop ( which was an issue when I was experimenting ). Hopefully someone else can use this class that abstracts these timers ( you will also need this MicroTimer class from Code Project if you want the MicroTimer to be included in this )…

public class ATimer
{
 /// <summary>
 /// Timer type.
 /// 0 = System.Threading ( default )
 /// 1 = Windows.Forms
 /// 2 = Microtimer ( custom code ).
 /// 3 = Multimedia timer ( windows mm dll ).
 /// </summary>
 private int _timerType;

 private System.Threading.Timer _timer0;

 private System.Windows.Forms.Timer _timer1;

 private MicroTimer _timer2;

 private int _interval;

 private ElapsedTimer0Delegate _elapsedTimer0Handler;

 private ElapsedTimer1Delegate _elapsedTimer1Handler;

 private ElapsedTimer2Delegate _elapsedTimer2Handler;

 private ElapsedTimer3Delegate _elapsedTimer3Handler;

 private ElapsedTimerDelegate _elapsedTimerHandler;

 private bool _enabled;

 public ATimer(int timerType, int intervalMS, ElapsedTimerDelegate callback)
 {
 _timerType = timerType;

 _interval = intervalMS;

 _elapsedTimerHandler = callback;

 if (timerType == 0)
 {
 _elapsedTimer0Handler = Timer0Handler;
 }
 else if (timerType == 1)
 {
 _elapsedTimer1Handler = Timer1Handler;

 _timer1 = new System.Windows.Forms.Timer();

 _timer1.Interval = _interval;

 _timer1.Tick += Timer1Handler;
 }
 else if (timerType == 2)
 {
 _timer2 = new MicroTimer();

 _timer2.Interval = _interval * 1000;

 _timer2.MicroTimerElapsed += Timer2Handler;

 }
 else if (timerType == 3)
 {
 }
 }

 public delegate void ElapsedTimerDelegate();

 public delegate void ElapsedTimer0Delegate( object sender );

 public delegate void ElapsedTimer1Delegate( object sender, EventArgs e );

 public delegate void ElapsedTimer2Delegate( object sender, MicroTimerEventArgs e );

 public delegate void ElapsedTimer3Delegate( int tick, TimeSpan span );

 //private delegate void TestEventHandler(int tick, TimeSpan span);

 public void Timer0Handler(object sender)
 {
 _elapsedTimerHandler();
 }

 public void Timer1Handler( object sender, EventArgs e )
 {
 _elapsedTimerHandler();
 }

 public void Timer2Handler(object sender, MicroTimerEventArgs e)
 {
 _elapsedTimerHandler();
 }

 private void Timer3Handler(int id, int msg, IntPtr user, int dw1, int dw2)
 {

 _elapsedTimerHandler();

 }

 public void Start()
 {
 if (_timerType == 0)
 {
 _timer0 = new System.Threading.Timer( (new TimerCallback( _elapsedTimer0Handler )),
                                       null, 0, _interval);
 }
 else if (_timerType == 1)
 {
 _timer1.Start();
 }
 else if (_timerType == 2)
 {
 _timer2.Enabled = true;
 }
 else if (_timerType == 3)
 {
 timeBeginPeriod(1);
 mHandler = new TimerEventHandler( Timer3Handler );
 mTimerId = timeSetEvent( _interval , 0, mHandler, IntPtr.Zero, EVENT_TYPE);
 mTestStart = DateTime.Now;
 mTestTick = 0;
 }
 }

 public void Stop()
 {
 if ( _timerType == 0 )
 {
 _timer0.Change(System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite);
 }
 else if ( _timerType == 1 )
 {
 _timer1.Stop();
 }
 else if (_timerType == 2)
 {
 _timer2.Stop();
 }
 else if (_timerType == 3)
 {
 int err = timeKillEvent(mTimerId);
 timeEndPeriod(1);
 mTimerId = 0;
 }
 }

 private int mTimerId;
 private TimerEventHandler mHandler; 
 private int mTestTick;
 private DateTime mTestStart;

 // P/Invoke declarations
 private delegate void TimerEventHandler(int id, int msg, IntPtr user, int dw1, int dw2);

 private const int TIME_PERIODIC = 1;
 private const int EVENT_TYPE = TIME_PERIODIC;// + 0x100;  // TIME_KILL_SYNCHRONOUS causes a hang ?!
 [DllImport("winmm.dll")]
 private static extern int timeSetEvent( int delay, int resolution,
                                         TimerEventHandler handler, IntPtr user, int eventType);
 [DllImport("winmm.dll")]
 private static extern int timeKillEvent(int id);
 [DllImport("winmm.dll")]
 private static extern int timeBeginPeriod(int msec);
 [DllImport("winmm.dll")]
 private static extern int timeEndPeriod(int msec);
}

Then, in order to use the timer in some code in some other class, you can do:

ATimer.ElapsedTimerDelegate callback = Test;
// This sets a reference to a function called “Test” that will be the function to call when the timer interval elapses ( change this to be whatever you want to call when the time elapses )

ATimer timer = new ATimer( 3, 100, callback );
// This creates the Timer to fire every 100 milliseconds the Test method. Also note “3″ as the first parameter indicates that we want to use the Multimedia timer. ( 0 = System.Forms.Timer, 1 = System.Thread.Time, 2 = Micro Timer etc. ).

Then of course to start the timer we need…

timer.Start();

and timer.Stop(); will stop the timer.

So hopefully it will be useful to someone to have the different types of timer encapsulated into one class. There is more that could be done on the class of course like passing event arguments around that contain data or whatever, but this is easily implemented.

This entry was posted in C# / .NET, Technical / Programming and tagged , , , , , . Bookmark the permalink.

7 Responses to C# .NET timing : millisecond accuracy for data logging

  1. sura says:

    This just saved my day!!
    thank you very very much. I am running the multimedia timer every 5ms, and so far no problem, and seems like the performance is also ok.

    • mikeyn says:

      Yes it is very accurate compared to the built-in .NET timers. From what I could tell it was creeping back a bit by about 60ms or so every hour but this was acceptable for me.

      • sura says:

        btw, did you try using two multimedia timers in one application? I just tried this, and am getting this exception. No idea of how to get around it
        ‘A callback was made on a garbage collected delegate of type ‘SL_LIBOMV_Connector!SL_LIBOMV_Connector.ATimer+TimerEventHandler::Invoke’. This may cause application crashes, corruption and data loss. When passing delegates to unmanaged code, they must be kept alive by the managed application until it is guaranteed that they will never be called.’

        • mikeyn says:

          I think this issue is similar to one I had and there is a post on another blog about it here:

          http://www.softwareinteractions.com/blog/2009/12/7/using-the-multimedia-timer-from-c.html

          Basically because the callback function is part of managed code, the garbage collector can screw things up and leave the Timer object with an invalid reference to the callback function.

          To sort this,
          A field needs declaring in the timer class…

          private GCHandle _gcHandle;

          and then in the constructor of the Timer , add the line

          _gcHandle = GCHandle.Alloc(timerCallback);

          Then add code that handles disposing and destruction,

          private bool _disposed = false;

          public void Dispose()
          {
          Dispose(true);

          GC.SuppressFinalize(this);
          }

          private void Dispose(bool disposing)
          {
          if (!_disposed)
          {
          if (disposing)
          {
          Stop();

          _gcHandle.Free();
          }

          }

          _disposed = true;
          }

          ~ATimer()
          {
          Dispose( false );
          }

          This stopped me getting any errors to do with issue. I should have added this earlier, I just didn’t update the blog post because I was busy.

          • sura says:

            Thanks. I started using the code in the given link, coz it is much more concise. With the bug fix, everything works fine.

  2. sura says:

    Hi Again,
    Some more close observations on the multimedia timer:

    Seems like I cannot use this multimedia timer with very small timer intervals (e.g. 5 milliseconds). The smallest interval that is working for me is 15 milliseconds, which is much better than the normal .Net timer anyway.
    The precision is also not very accurate at this level. When I use a 15 millisecond timer interval, sometimes the timer fires twice within 1 millisecond, or it takes double the interval time (i.e. 30 milliseconds) to fire. However, the occurrence of such deviations are very low, therefore on average I get 15 millisecond timer intervals.
    OTOH, if I use a smaller interval (say 5 milliseconds), the timer fires about 3-5times per 1 millisecond, however, the gap between two distinct timer values is about 20 milliseconds.

    having said this all, this problem might be due to the way I am using your code, or I miss to use some tuning parameter. This is how I call a multimedia timer:

    MultimediaTimer timer1;
    timer1 = new MultimediaTimer((uint)15);
    timer1.Elapsed += new MultimediaElapsedEventHandler(ReleaseSnapshots);
    timer1.Start();

    • mikeyn says:

      How long does the callback function take to execute? If this is more than the timer interval then there may be problems. Otherwise I can’t see why you should have problems at 15ms second intervals. Are you using my code with the garbage collecting stuff in there and the dispose stuff? That needs to be in there.

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>