Higher Resolution Timer

Everything else that doesn't fall into one of the other PB categories.
User avatar
Paul
PureBasic Expert
PureBasic Expert
Posts: 1282
Joined: Fri Apr 25, 2003 4:34 pm
Location: Canada
Contact:

Higher Resolution Timer

Post 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.
dmoc
Enthusiast
Enthusiast
Posts: 739
Joined: Sat Apr 26, 2003 12:40 am

Post 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?
PB
PureBasic Expert
PureBasic Expert
Posts: 7581
Joined: Fri Apr 25, 2003 5:24 pm

Re: Higher Resolution Timer

Post 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.
I compile using 5.31 (x86) on Win 7 Ultimate (64-bit).
"PureBasic won't be object oriented, period" - Fred.
User avatar
Psychophanta
Always Here
Always Here
Posts: 5153
Joined: Wed Jun 11, 2003 9:33 pm
Location: Anare
Contact:

Post by Psychophanta »

I have interest on this too. But i don't know how. :roll:
http://www.zeitgeistmovie.com

while (world==business) world+=mafia;
Sparkie
PureBatMan Forever
PureBatMan Forever
Posts: 2307
Joined: Tue Feb 10, 2004 3:07 am
Location: Ohio, USA

Post 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))
What goes around comes around.

PB 5.21 LTS (x86) - Windows 8.1
User avatar
Paul
PureBasic Expert
PureBasic Expert
Posts: 1282
Joined: Fri Apr 25, 2003 4:34 pm
Location: Canada
Contact:

Post 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.
Magi
User
User
Posts: 25
Joined: Sun Apr 27, 2003 12:57 am
Location: Burlington, Canada

Post 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
User avatar
Psychophanta
Always Here
Always Here
Posts: 5153
Joined: Wed Jun 11, 2003 9:33 pm
Location: Anare
Contact:

Post 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!")
Last edited by Psychophanta on Sun Dec 19, 2004 12:22 pm, edited 1 time in total.
http://www.zeitgeistmovie.com

while (world==business) world+=mafia;
User avatar
Psychophanta
Always Here
Always Here
Posts: 5153
Joined: Wed Jun 11, 2003 9:33 pm
Location: Anare
Contact:

Post 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!")
Last edited by Psychophanta on Sun Dec 19, 2004 12:21 pm, edited 1 time in total.
http://www.zeitgeistmovie.com

while (world==business) world+=mafia;
kns
User
User
Posts: 54
Joined: Sat Apr 26, 2003 2:06 am

Post 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
Post Reply