Page 1 of 1

Higher Resolution Timer

Posted: Fri Dec 17, 2004 6:59 pm
by Paul
Anyone know of a way to execute tasks at a higher resolution than milliseconds?
I need to send some data at very specific intervals... 2.4ms, .4ms, 1.2ms and .8ms, which means GetTickCount_() is not accurate enough.

Ideas? Suggestions?
Thanks.

Posted: Fri Dec 17, 2004 8:31 pm
by dmoc
Poss using win32 api for the high performance counter and your own little scheduler? Maybe don't rely on threads. Also you may need to take into account any latency in your app. Are you controlling something or maybe sending midi commands?

Re: Higher Resolution Timer

Posted: Fri Dec 17, 2004 8:47 pm
by PB
> Anyone know of a way to execute tasks at a higher resolution than milliseconds?

According to Microsoft, the QueryPerformanceTimer API has a resolution of
microseconds, as opposed to milliseconds; but the frequency depends on the
PC being used. See http://support.microsoft.com/kb/q172338/ for details.

Posted: Fri Dec 17, 2004 9:26 pm
by Psychophanta
I have interest on this too. But i don't know how. :roll:

Posted: Sat Dec 18, 2004 1:27 am
by Sparkie
I converted some code from the ^MS link provided by PB^ and it seems to work. It uses a hi-res timer to time a Delay(1000) call. I get results ranging from 0.995139 to 1.000909 seconds for the 1 second delay.

This is new territory for me so DO NOT take this snippet as 100% accuate :P

Code: Select all

Procedure.s HiResTimer(xNum)
  Ctr1.LARGE_INTEGER
  Ctr2.LARGE_INTEGER
  Freq.LARGE_INTEGER
  Overhead.LARGE_INTEGER
  A.l
  i.l
  If QueryPerformanceFrequency_(Freq)
    QueryPerformanceCounter_(Ctr1)
    QueryPerformanceCounter_(Ctr2)
    Overhead\lowpart = Ctr2\lowpart - Ctr1\lowpart  ; determine API overhead
    QueryPerformanceCounter_(Ctr1)                  ; start time loop
    Delay(xNum)
    QueryPerformanceCounter_(Ctr2)                  ; end time loop
    TimerInfo$ = "Delay(" + Str(xNum) + ") took " + StrF((Ctr2\lowpart - Ctr1\lowpart - Overhead\lowpart) / Freq\lowpart) + " seconds"
    result$ = TimerInfo$
  Else
    result$ = "Error occured"
  EndIf
  ProcedureReturn result$
EndProcedure
MessageRequester("Hi_Res Timer", HiResTimer(1000))

Posted: Sat Dec 18, 2004 1:37 am
by Paul
I need to send pulses down the LPT port.

Switch on for 2.4ms
Switch off for .4ms
Switch on for 1.2ms
Switch off for .8ms
etc.

Various sequences of these on/off (0 volt/5 volt) control a device. The times must be exact to conform to a specific protocol.

So I guess I would need something to determine how many loop it takes for 1ms (based on speed of computer), then count loops to create 2.4ms delay, .4ms delay, etc.

Posted: Sat Dec 18, 2004 3:32 am
by Magi
Hi Paul,

It's not PureBasic but may be of help...
This code provides high resolution timing into the microsecond realm, much better than GetTickCount() which is generally in the 10's of milliseconds (10,000's of microseconds):
Use at your convenience. I tested this code on a win98 system and it worked, too. Not sure whether that can be counted upon though.
Also compiles with PB/CC, just replace the msgbox calls with stdout.
http://www.powerbasic.com/support/forum ... 02208.html

Lots of other timer source code at this site, hope it helps.

Regards,

Marc

Posted: Sat Dec 18, 2004 1:46 pm
by Psychophanta
I think i've got it. :wink:
Perhaps this can help you Paul (see the last part):

Code: Select all

; determine timer max resolution: 
If QueryPerformanceFrequency_(Freq.LARGE_INTEGER) 
  periodns.f=1000000000/(Pow(2,32)*Freq\highpart+Freq\lowpart) 
  MessageRequester("","Resolution (used time per tick) in this machine is: "+StrF(periodns)+" nanosecs") 
Else 
  MessageRequester("","No High-res timer allowed"):End 
EndIf 

; determine API call delay: 
QueryPerformanceCounter_(Ctr1.LARGE_INTEGER) 
QueryPerformanceCounter_(Ctr2.LARGE_INTEGER) 
calldelay.LARGE_INTEGER\lowpart = Ctr2\lowpart - Ctr1\lowpart 
MessageRequester("","API call used time was ~ "+StrF(calldelay\lowpart*periodns)+" nanosecs") 

; determine for-next loop time: 
QueryPerformanceCounter_(Ctr1.LARGE_INTEGER) 
For t=1 To 100000:Next 
QueryPerformanceCounter_(Ctr2.LARGE_INTEGER) 
MessageRequester("","for-next loop used time was ~ "+StrF((Ctr2\lowpart-Ctr1\lowpart-calldelay\lowpart)*periodns)+" nanosecs") 

; perform about 1.234567 millisecond delay: 
ticks.f=1.234567/periodns.f 
ticks.f*1000000
QueryPerformanceCounter_(Ctr1.LARGE_INTEGER):Ctr1\lowpart&$7FFFFFFF 
Repeat 
  QueryPerformanceCounter_(Ctr2.LARGE_INTEGER):Ctr2\lowpart&$7FFFFFFF 
Until Ctr2\lowpart-Ctr1\lowpart+calldelay\lowpart>=ticks.f 
MessageRequester("","OK!")

Posted: Sat Dec 18, 2004 8:28 pm
by Psychophanta
This way is more accurate:

Code: Select all

;-determine timer max resolution:
If QueryPerformanceFrequency_(Freq.LARGE_INTEGER)
  periodns.f=1000000000/(Pow(2,32)*Freq\highpart+Freq\lowpart)
  ;MessageRequester("","Resolution (used time per tick) in this machine is: "+Str(periodns)+" nanosecs")
Else
  MessageRequester("","No High-res timer allowed"):End
EndIf

;-determine API call delay:
QueryPerformanceCounter_(Ctr1.LARGE_INTEGER)
QueryPerformanceCounter_(Ctr2.LARGE_INTEGER)
calldelay.LARGE_INTEGER\lowpart = Ctr2\lowpart - Ctr1\lowpart
;MessageRequester("","API call used time was ~ "+StrF(calldelay\lowpart*periodns)+" nanosecs") 


;-perform hires-timing delay:
Standby.f=ValF(InputRequester("","Input wished delay time in milliseconds","0.655"))
Standby.f*1000000;    <- pass nanosecs to millisecs

!fld dword[v_Standby]
!fdiv dword[v_periodns]; <-now in st0 is the #ticks to perform
QueryPerformanceCounter_(t1.LARGE_INTEGER)
!fild qword[v_t1]   ;<-now in st0 is checkpoint1,in st1 is #ticks
!fsub qword[v_calldelay]  ;substract the time used by QueryPerformanceCounter_() function call
!@@:
QueryPerformanceCounter_(t2.LARGE_INTEGER)
!fild qword[v_t2] ;<-now in st0 is checkpoint2, in st1 is checkpoint1 and in st2 is #ticks
!fsub st0,st1   ;<- checkpoint2 - checkpoint1 - calldelay to st0
!fcomip st2     ;<-compare #ticks
!jc @r          ;<-continue polling until #ticks is reached
!fstp st0
!fstp st0


MessageRequester("","That's it!")

Posted: Sun Dec 19, 2004 3:25 am
by kns
Any chance of seeing examples of more generic functions for the high resolution timers being discussed? That is, simple reuable functions to start, stop, wait, and wait until key/button pressed. Or perhaps PB will soon include such functionality with the high performance counter....

Thanks,
Ken