Jump to content

Accurate Timer


NiTroGen
 Share

Recommended Posts

I am working on a very accurate timer with accuracy upto ms. However, it still gives around 4 ms error on my Athlon 2100+ (idle). I have set it with High priority and tried writing using AdLibEnable()/AdLibDisable()., but no help.

Here is a simple test

ProcessSetPriority(@AutoItExe,4)
$StartTime = TimerInit()
$delay=416
$soundFreq=500
while 1
    $TimeDifference = $delay - TimerDiff($StartTime) 
    If $TimeDifference < 0 Then
        ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : TimerDiff($startTime) = ' & TimerDiff($StartTime) & @CRLF);### Debug Console
        $StartTime = TimerInit()
        Beep($soundFreq, 100)
    EndIf
WEnd

Here is the result

@@ Debug(8) : TimerDiff($startTime) = 420.433881959858
@@ Debug(8) : TimerDiff($startTime) = 417.418973640505
@@ Debug(8) : TimerDiff($startTime) = 416.124675063451
@@ Debug(8) : TimerDiff($startTime) = 416.191163960783
@@ Debug(8) : TimerDiff($startTime) = 416.032205210439
@@ Debug(8) : TimerDiff($startTime) = 416.692065611691
@@ Debug(8) : TimerDiff($startTime) = 416.751011650922
@@ Debug(8) : TimerDiff($startTime) = 416.032205210439
@@ Debug(8) : TimerDiff($startTime) = 422.75261241303

Anyone can give me some new idea?

Link to comment
Share on other sites

For high-resolution Windows timers that have up to the nanosecond accuracy you can use the following functions(you need to do a DLL-call):

QueryPerformanceFrequency

QueryPerformanceCounter

and here are the corresponding MSDN links:

http://msdn2.microsoft.com/en-us/library/ms644905.aspx

http://msdn2.microsoft.com/en-us/library/ms644904.aspx

Link to comment
Share on other sites

For high-resolution Windows timers that have up to the nanosecond accuracy you can use the following functions(you need to do a DLL-call):

QueryPerformanceFrequency

QueryPerformanceCounter

and here are the corresponding MSDN links:

http://msdn2.microsoft.com/en-us/library/ms644905.aspx

http://msdn2.microsoft.com/en-us/library/ms644904.aspx

Correct me if I'm wrong but aren't TimerInit() and TimerDiff() based off those two functions?

www.itoady.com

A* (A-star) Searching Algorithm - A.I. Artificial Intelligence bot path finding

Link to comment
Share on other sites

Correct me if I'm wrong but aren't TimerInit() and TimerDiff() based off those two functions?

It most cetainly does:
///////////////////////////////////////////////////////////////////////////////
// TimerInit()
// Returns a floating point value that is a baseline system time.
// Starts tracking a high performance counter allowing for accurate timers.
///////////////////////////////////////////////////////////////////////////////

AUT_RESULT AutoIt_Script::F_TimerInit(VectorVariant &vParams, Variant &vResult)
{
    __int64 now;

    if (!QueryPerformanceCounter((LARGE_INTEGER *)&now))
        return AUT_OK;

    vResult = (double)now;

    return AUT_OK;

} // TimerInit()

///////////////////////////////////////////////////////////////////////////////
// TimerDiff(Baseline)
// Takes the time difference between now and Baseline and returns the result.
///////////////////////////////////////////////////////////////////////////////

AUT_RESULT AutoIt_Script::F_TimerDiff(VectorVariant &vParams, Variant &vResult)
{
    __int64 freq, now;

    if (!QueryPerformanceFrequency((LARGE_INTEGER *)&freq))
        return AUT_OK;

    if (!QueryPerformanceCounter((LARGE_INTEGER *)&now))
        return AUT_OK;

    vResult = (((double)now - vParams[0].fValue()) / (double)freq) * 1000.0;

    return AUT_OK;

} // TimerDiff()

@NiTroGen, the same code gave me:

@@ Debug(8) : TimerDiff($startTime) = 416.024244212438
@@ Debug(8) : TimerDiff($startTime) = 416.024360417612
@@ Debug(8) : TimerDiff($startTime) = 416.024142078983
@@ Debug(8) : TimerDiff($startTime) = 416.024503858375
@@ Debug(8) : TimerDiff($startTime) = 416.026355424421
@@ Debug(8) : TimerDiff($startTime) = 416.031213345438
@@ Debug(8) : TimerDiff($startTime) = 416.022971402633
@@ Debug(8) : TimerDiff($startTime) = 416.023870177031
@@ Debug(8) : TimerDiff($startTime) = 416.026211075806
@@ Debug(8) : TimerDiff($startTime) = 416.023836586473
@@ Debug(8) : TimerDiff($startTime) = 416.027840671811
@@ Debug(8) : TimerDiff($startTime) = 416.02621561507
@@ Debug(8) : TimerDiff($startTime) = 416.026092600999
@@ Debug(8) : TimerDiff($startTime) = 416.022873354517
@@ Debug(8) : TimerDiff($startTime) = 416.02594098956
@@ Debug(8) : TimerDiff($startTime) = 416.025518837948
@@ Debug(8) : TimerDiff($startTime) = 416.025945528824
@@ Debug(8) : TimerDiff($startTime) = 416.027456650023
@@ Debug(8) : TimerDiff($startTime) = 416.023802541988

There are times when it can jump up to odd amounts though when i do it over a long period of time. I am curious as to why the counter needs to be so precise? Could you please explain in more detail what it is exactly that you are trying to do that requires such high precision and we will tell you if there are any alternatives to doing what you are doing that would require less precision.

- The Kandie Man ;-)

"So man has sown the wind and reaped the world. Perhaps in the next few hours there will no remembrance of the past and no hope for the future that might have been." & _"All the works of man will be consumed in the great fire after which he was created." & _"And if there is a future for man, insensitive as he is, proud and defiant in his pursuit of power, let him resolve to live it lovingly, for he knows well how to do so." & _"Then he may say once more, 'Truly the light is sweet, and what a pleasant thing it is for the eyes to see the sun.'" - The Day the Earth Caught Fire

Link to comment
Share on other sites

It most cetainly does:

///////////////////////////////////////////////////////////////////////////////
// TimerInit()
// Returns a floating point value that is a baseline system time.
// Starts tracking a high performance counter allowing for accurate timers.
///////////////////////////////////////////////////////////////////////////////

AUT_RESULT AutoIt_Script::F_TimerInit(VectorVariant &vParams, Variant &vResult)
{
    __int64 now;

    if (!QueryPerformanceCounter((LARGE_INTEGER *)&now))
        return AUT_OK;

    vResult = (double)now;

    return AUT_OK;

} // TimerInit()

///////////////////////////////////////////////////////////////////////////////
// TimerDiff(Baseline)
// Takes the time difference between now and Baseline and returns the result.
///////////////////////////////////////////////////////////////////////////////

AUT_RESULT AutoIt_Script::F_TimerDiff(VectorVariant &vParams, Variant &vResult)
{
    __int64 freq, now;

    if (!QueryPerformanceFrequency((LARGE_INTEGER *)&freq))
        return AUT_OK;

    if (!QueryPerformanceCounter((LARGE_INTEGER *)&now))
        return AUT_OK;

    vResult = (((double)now - vParams[0].fValue()) / (double)freq) * 1000.0;

    return AUT_OK;

} // TimerDiff()

@NiTroGen, the same code gave me:

@@ Debug(8) : TimerDiff($startTime) = 416.024244212438
@@ Debug(8) : TimerDiff($startTime) = 416.024360417612
@@ Debug(8) : TimerDiff($startTime) = 416.024142078983
@@ Debug(8) : TimerDiff($startTime) = 416.024503858375
@@ Debug(8) : TimerDiff($startTime) = 416.026355424421
@@ Debug(8) : TimerDiff($startTime) = 416.031213345438
@@ Debug(8) : TimerDiff($startTime) = 416.022971402633
@@ Debug(8) : TimerDiff($startTime) = 416.023870177031
@@ Debug(8) : TimerDiff($startTime) = 416.026211075806
@@ Debug(8) : TimerDiff($startTime) = 416.023836586473
@@ Debug(8) : TimerDiff($startTime) = 416.027840671811
@@ Debug(8) : TimerDiff($startTime) = 416.02621561507
@@ Debug(8) : TimerDiff($startTime) = 416.026092600999
@@ Debug(8) : TimerDiff($startTime) = 416.022873354517
@@ Debug(8) : TimerDiff($startTime) = 416.02594098956
@@ Debug(8) : TimerDiff($startTime) = 416.025518837948
@@ Debug(8) : TimerDiff($startTime) = 416.025945528824
@@ Debug(8) : TimerDiff($startTime) = 416.027456650023
@@ Debug(8) : TimerDiff($startTime) = 416.023802541988

There are times when it can jump up to odd amounts though when i do it over a long period of time. I am curious as to why the counter needs to be so precise? Could you please explain in more detail what it is exactly that you are trying to do that requires such high precision and we will tell you if there are any alternatives to doing what you are doing that would require less precision.

- The Kandie Man ;-)

Wooo! Very accurate timing! Maybe my computer is too crappy. Or my cheap motherboard gives me bad timing?

Actually, I need a little program to give a beep/play a wav files under a fixed time interval. 416ms is the shortest interval required.

I have attached the whole program for your reference. Please let me know your result when playing a wave files.

**OOpps, line 103 should be "If @error Then"

Edited by NiTroGen
Link to comment
Share on other sites

After looking at your script, I couldn't find any practical solutions for increasing the accuracy. I changed the adlib function around, did tests to find the fastest loop type, but when it came down to it there simply wasn't a good solution. The accuracy usually varies only slightly and only maybe by about 10ms. 10ms is 1/100th of a second and to a human is indistinguishable. Any further precision would require help from external libraries and would not be practical for application such as this. Also, I found your original post to have the most accurate timing, but this is also probably because it was a simple script with no GUI handling or anything like that.

Sorry I couldn't find a better practical way of doing this in AutoIt.

- The Kandie Man ;-)

"So man has sown the wind and reaped the world. Perhaps in the next few hours there will no remembrance of the past and no hope for the future that might have been." & _"All the works of man will be consumed in the great fire after which he was created." & _"And if there is a future for man, insensitive as he is, proud and defiant in his pursuit of power, let him resolve to live it lovingly, for he knows well how to do so." & _"Then he may say once more, 'Truly the light is sweet, and what a pleasant thing it is for the eyes to see the sun.'" - The Day the Earth Caught Fire

Link to comment
Share on other sites

After looking at your script, I couldn't find any practical solutions for increasing the accuracy. I changed the adlib function around, did tests to find the fastest loop type, but when it came down to it there simply wasn't a good solution. The accuracy usually varies only slightly and only maybe by about 10ms. 10ms is 1/100th of a second and to a human is indistinguishable. Any further precision would require help from external libraries and would not be practical for application such as this. Also, I found your original post to have the most accurate timing, but this is also probably because it was a simple script with no GUI handling or anything like that.

Sorry I couldn't find a better practical way of doing this in AutoIt.

- The Kandie Man ;-)

Hey KM, you have proved that my computer is not that crap! Thanks :)

Would you post your console output as well? (Both using Beep and SoundPlay)

Link to comment
Share on other sites

Hey KM, you have proved that my computer is not that crap! Thanks :)

Would you post your console output as well? (Both using Beep and SoundPlay)

Sure. Using your unmodified code provided in the zip file the output is as follows:

Using Beep:

CODE
@@ Debug(135) : $delay = 416

422.383655469814

425.707515660463

422.795295960054

416.260572855197

424.412053109396

422.781886064458

423.767980027236

423.131923286428

425.363879709487

422.792737630504

423.771467998184

423.754028597367

423.768354516568

424.720556513845

423.306102133454

422.251554244212

422.780177031321

422.795754425783

417.893249659555

422.794171130277

415.932906491148

424.742760326827

422.773953245574

422.812894235134

423.130301407172

417.541291874716

422.782085792102

419.239088969587

424.378531093963

422.773027689514

418.881930549251

424.731210621879

Using the .wav file:

CODE
@@ Debug(135) : $delay = 416

417.518198365865

424.730346799818

416.240759872901

423.474174307762

426.429482977758

424.017288697231

424.121273263731

420.464720835225

421.813876985928

417.899923740354

421.802278256922

421.023340898774

415.762064457558

421.796547889242

416.930138901498

420.849139809351

422.77808987744

415.940080344984

421.797333635951

416.924788470268

422.784982296868

429.483661824784

424.880694961416

421.800510213345

422.146773490695

421.471908306854

416.930139355424

423.751259192011

421.810121652292

424.296451656832

422.246960054471

418.899111211984

421.786801180209

417.166920108942

422.53982796187

420.830442124376

421.892652292329

424.663318202451

As you can see there is variation, but the variation is usually never more than 10ms. As a result, the beep will never be more than 1/100th of a second off que.

- The Kandie Man ;-)

"So man has sown the wind and reaped the world. Perhaps in the next few hours there will no remembrance of the past and no hope for the future that might have been." & _"All the works of man will be consumed in the great fire after which he was created." & _"And if there is a future for man, insensitive as he is, proud and defiant in his pursuit of power, let him resolve to live it lovingly, for he knows well how to do so." & _"Then he may say once more, 'Truly the light is sweet, and what a pleasant thing it is for the eyes to see the sun.'" - The Day the Earth Caught Fire

Link to comment
Share on other sites

If you want to manually adjust then heres something i found to help.

;====================================================================
; How to simulate the Timer functions in Autoit using DLL calls
;====================================================================

Global $starttime = _TimerInit() 
While 1
    ToolTip(_TimerDiff($starttime))
WEnd

;====================================================================
; Functions in Autoit
;    TimerInit() 
;    TimerDiff()
; Functions in kernal32.dll
;    _QueryPerformanceFrequency()
;    _QueryPerformanceCounter()
;====================================================================

Func _TimerInit()
    Local $var = DllStructCreate("int64")
    DllCall("kernel32.dll", "int", "QueryPerformanceCounter", "ptr", DllStructGetPtr($var, 1))
    Local $starttime = DllStructGetData($var, 1)
    $startvar = 0
    Return $starttime
EndFunc

Func _TimerDiff($timer)
    Return 1000 * ( _QueryPerformanceCounter() - $timer ) / _QueryPerformanceFrequency()
EndFunc

Func _QueryPerformanceFrequency()
    Local $var = DllStructCreate("int64")
    DllCall("kernel32.dll", "int", "QueryPerformanceFrequency", "ptr", DllStructGetPtr($var, 1))
    Local $returnval = DllStructGetData($var, 1)
    $var = 0
    Return $returnval
EndFunc

Func _QueryPerformanceCounter()
    Local $var = DllStructCreate("int64")
    DllCall("kernel32.dll", "int", "QueryPerformanceCounter", "ptr", DllStructGetPtr($var, 1))
    Local $returnval = DllStructGetData($var, 1)
    $var = 0
    Return $returnval
EndFunc

www.itoady.com

A* (A-star) Searching Algorithm - A.I. Artificial Intelligence bot path finding

Link to comment
Share on other sites

Thanks The Kandie Man

If you want to manually adjust then heres something i found to help.

;====================================================================
; How to simulate the Timer functions in Autoit using DLL calls
;====================================================================

Global $starttime = _TimerInit() 
While 1
    ToolTip(_TimerDiff($starttime))
WEnd

;====================================================================
; Functions in Autoit
;    TimerInit() 
;    TimerDiff()
; Functions in kernal32.dll
;    _QueryPerformanceFrequency()
;    _QueryPerformanceCounter()
;====================================================================

Func _TimerInit()
    Local $var = DllStructCreate("int64")
    DllCall("kernel32.dll", "int", "QueryPerformanceCounter", "ptr", DllStructGetPtr($var, 1))
    Local $starttime = DllStructGetData($var, 1)
    $startvar = 0
    Return $starttime
EndFunc

Func _TimerDiff($timer)
    Return 1000 * ( _QueryPerformanceCounter() - $timer ) / _QueryPerformanceFrequency()
EndFunc

Func _QueryPerformanceFrequency()
    Local $var = DllStructCreate("int64")
    DllCall("kernel32.dll", "int", "QueryPerformanceFrequency", "ptr", DllStructGetPtr($var, 1))
    Local $returnval = DllStructGetData($var, 1)
    $var = 0
    Return $returnval
EndFunc

Func _QueryPerformanceCounter()
    Local $var = DllStructCreate("int64")
    DllCall("kernel32.dll", "int", "QueryPerformanceCounter", "ptr", DllStructGetPtr($var, 1))
    Local $returnval = DllStructGetData($var, 1)
    $var = 0
    Return $returnval
EndFunc
@Toady

Great code! I will try to mod it.

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...