trejo@nprdc.arpa (Leonard J. Trejo) (07/21/89)
Say, we're trying to measure human reaction times (<1ms) by timing the interval between a video field and a keypress (on the keyboard) on our PC-compatible (Compaq 386/25) using the C subroutine call "ftime()", which purports to provide millisecond accuracy (MSC 5.1). Some net-news discussions I've followed say that the precision of ftime() may actually be _worse_ than 1 ms (say 8 ms), even though the time returned is measured in ms. So, I thought I'd ask to see how others accurately measure intervals on PC's. Would you be kind enough to send me a short description of how you do it and/or some example programs? L. J. T. ============================================================================ ARPANET : trejo@nprdc.navy.mil UUCP: ucsd!nprdc!trejo U.S. Mail: Leonard J. Trejo, Ph. D. Phone: (619) 553-7711 Neuroscience Laboratory (AV) 553-7711 NPRDC, Code 141 San Diego, CA 92152-6800
Bob.Stout@p6.f506.n106.z1.fidonet.org (Bob Stout) (07/24/89)
In an article of <20 Jul 89 21:39:07 GMT>, (Leonard J. Trejo) writes: >Say, we're trying to measure human reaction times (<1ms) by timing the >interval between a video field and a keypress (on the keyboard) on >our PC-compatible (Compaq 386/25) using the C subroutine call >"ftime()", which purports to provide millisecond accuracy (MSC 5.1). Most PC C compilers use the basic system clock interrupt interval (18.2 Hz or 55 msec.) for their minimum timing resolution. Following is a public domain source for achieving 1 usec. timing accuracy in a PC or close compatible. I've used it on Compaqs before and it should work just fine for your application. Note that this code has a few rough spots. I adapted it for use in a commercial library which is considerably cleaner. This should get you going, but if you want more info on the library (it's currently only available for TC and ZTC although MSC should be out in the next few months), let me know... --------------------------------- Cut here --------------------------------- /* clock.c -- Microsecond resolution clock routine. */ /* */ /* Written by David L. Fox. */ /* Implements in C the timer chip tweaking described by */ /* Byron Sheppard, _Byte_, Jan 1987, p 157-164. */ /* Replaces standard clock() from the library. */ /* The definition of CLK_TCK in time.h may have to */ /* be changed to 1000000L. */ /* Does not correctly handle intervals spanning */ /* midnight or intervals greater than about 6 hrs. */ #include <time.h> /* Interrupt handling and i/o ports are compiler dependent. */ /* The following set of preprocessor directives selects the */ /* correct include files and macros for various compilers. */ #ifdef __ZTC__ #include <dos.h> #include <int.h> #define inportb inp #define outportb outp #else #ifdef __TURBOC__ #include <dos.h> #define int_off disable #define int_on enable #else #error Unknown compiler #endif #endif /* Constants */ #define CONTVAL 0x34 /* == 00110100 Control byte for 8253 timer. */ /* Sets timer 0 to 2-byte read/write, mode 2, binary. */ #define T0DATA 0x40 /* Timer 0 data port address. */ #define TMODE 0x43 /* Timer mode port address. */ #define BIOS_DS 0x40 /* BIOS data segment. */ #define B_TIKP 0x6c /* Address of BIOS (18.2/s) tick count. */ #define SCALE 10000 /* Scale factor for timer ticks. */ /* The following values assume 18.2 BIOS ticks per second resulting from the 8253 being clocked at 1.19 MHz. */ #define us_BTIK 54925 /* Micro sec per BIOS clock tick. */ #define f_BTIK 4595 /* Fractional part of micro sec per BIOS tick.*/ #define us_TTIK 8381 /* Micro sec per timer tick * SCALE. (4/4.77 MHz) */ clock_t clock(void) { unsigned char msb, lsb; unsigned int tim_ticks; static int init = 0; unsigned long count, us_tmp; static unsigned long init_count; if (0 == init) { init = 1; /* This is the first call, have to set up timer. */ int_off(); outportb(TMODE, CONTVAL); /* Write new control byte to timer. */ outportb(T0DATA, 0); /* Initial count = 0 = 65636. */ outportb(T0DATA, 0); init_count = *(unsigned long int far *)MK_FP(BIOS_DS, B_TIKP); int_on(); return 0; /* First call returns zero. */ } int_off(); /* Don't want an interrupt while getting time. */ outportb(TMODE, 0); /* Latch count. */ lsb = inportb(T0DATA); /* Read count. */ msb = inportb(T0DATA); /* Get BIOS tick count (read BIOS ram directly for speed and to avoid turning on interrupts). */ count = *(unsigned long far *)MK_FP(BIOS_DS, B_TIKP) - init_count; int_on(); /* Interrupts back on. */ tim_ticks = (unsigned)-1 - ((msb << 8) | lsb); us_tmp = count*us_BTIK; return us_tmp + ((long)tim_ticks*us_TTIK + us_tmp%SCALE)/SCALE; }