Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Delphi: Общие вопросы > Замерить реакцию человека


Автор: MYL 17.12.2006, 15:36
Моя цель - написать программу, которая позволит определить реакцию человека. На экране появляется красный круг. Человек сликает на него - цвет меняется с красного на зелёный. Программа замеряет то время, которое потребовалось человеку, чтобы нажатать на круг. 
Я залез на Delphi Russian Knowledge Base from Vit Version 2.3, и нашёл раздел "Как реализовать сверхточный таймер?"  Но мне всё равно непонятно, как это применить в моей ситуации. Привожу выдержку из заметки:

Windows is not a real time operating system so it is not really able to reliably achieve high accuracy timing without using a device driver. The best I have been able to get is a few nanoseconds using QueryPerformanceCounter. This is the procedure I use: 
Код

var 
  WaitCal: Int64; 

procedure Wait(ns: Integer); 
var 
  Counter, Freq, WaitUntil: Int64; 
begin 
  if QueryPerformanceCounter(Counter) then 
  begin 
    QueryPerformanceFrequency(Freq); 
    WaitUntil := Counter + WaitCal + (ns * (Freq div 1000000)); 
    while Counter < WaitUntil do 
      QueryPerformanceCounter(Counter); 
  end 
  else 
    Sleep(ns div 1000); 
end; 

To get improved accuracy do this a little while before using Wait() 
Код

var 
  Start, Finish: Int64; 

Application.ProcessMessages; 
Sleep(10); 
QueryPerformanceCounter(Start); 
Wait(0); 
QueryPerformanceCounter(Finish); 
WaitCal := Start - Finish; 


Автор: gambit 17.12.2006, 20:15
А я не понимаю зачем тебе нужен такой геморой со сверх точным таймером. запускаеш таймер и как только пользлватель произвел действия останавливаеш его.

Автор: 000denmoroz000 17.12.2006, 21:03
MYL, 
объявляешь переменную count:
count:Int64;
ставишь на форму таймер, ставишь интревал 100 и Enabled - false.
на событие Timer1.timer пишешь:
Код

inc(count);

При нажатии на круг:
Код

timer1.Enabled:=true;

При повторном нажатии:
Код

timer1.Enabled:=false;
showmessage('Таймер проработал:'+floattostr(count*100/1000)+' c.');


Уменьшая интревал - добавишь точности.

Автор: gambit 17.12.2006, 21:13
000denmoroz000, Правельно именно это я и имел ввиду.

Автор: Alexeis 18.12.2006, 14:40
В этом случае, лучше воспользоваться мультимедийным таймером. Не уверен, что точности обычного таймера будет достаточно. Обычный таймер работает с погрешностью 10мс, согласитесь если время реакции может быть  менее 100 мс, то не такая уж высокая точность. Или оставить как есть используя 
QueryPerformanceCounter

После изменения цвета засечь текущее значение счетчика, после нажатия на кнопку засечь во второй раз. Посчитать их разницу и разделить это все на значение QueryPerformanceFrequency. Полученная величина будет измеряться в секундах.

Автор: x77 18.12.2006, 14:47
а зачем здесь вообще таймер? 

Код

var
  StartTime: TDateTime;
  EndTime: TDateTime;

...

procedure Start;
begin
  ShowRedCircle;
  StartTime := Time;
end;

procedure Form1Button1Click (..);
begin
  EndTime := Time;
end;


собственно, всё. в момент показа засекаем время, в момент клика засекаем время. имеем разницу (EndTime - StartTime) с точностью до миллисекунд. а таймер тут не нужен в принципе.

Автор: Alexeis 18.12.2006, 15:17
Цитата(x77 @  18.12.2006,  14:47 Найти цитируемый пост)
с точностью до миллисекунд.
 г.м. чет сомневаюсь, что до мс. Наверняка значение обновляется по тому же системному таймеру, т.е. с периодом 10мс. Впрочем утверждать не берусь ...

Автор: x77 18.12.2006, 15:23
alexeis1, вместо Time можно юзать GetTickCount. не суть важно. важно, что замерить кол-во времени можно и без таймера, и отработка по педальному клику будет всяко быстрее (точнее), чем отлов по таймеру.

Автор: Alexeis 18.12.2006, 15:24
Да конечно же с точностью до 10мс! Вот лог чтения милисекунд функцией Time
Код

507
507
507
507
507
507
507
507
507
507
507
507
507
507
507
507
517
517
517
517
517
517
517
517
517
517
517
517
517
517
517
517
517
517
517
517
517
517
527
527
527
527
527
527
527
527
527
527
527
527
527
527
527
527
527
527
527
527
527
537
537
537
537
537
537
537
537
537
537
537
537
537
537
537
537
537
537
537
537
537
537
547
547
547
547
547
547
547
547
547
547
547
547
547
547
547
547
547
547
547
547
547
557
557
557
557
557
557
557
557
557
557
557
557
557
557
557
557
557
557
557
557
557
557
567
567
567
567
567
567
567
567
567
567
567
567
567
567
567
567
567
567
567
567
567
567
577
577
577
577
577
577
577
577
577
577
577
577
577
577
577
577
577
577
577
577
577
587
587
587
587
587
587
587
587
587
587
587
587
587
587
587
587
587
587
587
587
587
587
597
597
597
597
597
597
597
597
597
597
597
597
597
597
597
597
597
597
597
597
597
597
607
607
607
607
607
607
607
607
607
607
607
607
607
607
607
607
607
607
607
607
607
607
617
617
617
617
617
617
617
617
617
617
617
617
617
617
617
617
617
617
617
617
617
617
627
627
627
627
627
627
627
627
627
627
627
627
627
627
627
627
627
627
627
627
637
637
637
637
637
637
637
637
637
637
637
637
637
637
637
637
637
637
637
637
637
637
647
647
647
647
647
647
647
647
647
647
647
647
647
647
647
647
647
647
647
647
647
647
657
657
657
657
657
657
657
657
657
657
657
657
657
657
657
657
657
657
657
657
657
657
667
667
667
667
667
667
667
667
667
667
667
667
667
667
677
677
677
677
677
677
677
677
677
677
677
677
677
677
677
677
677
677
677
677
687
687
687
687
687
687
687
687
687
687
687
687
687
687
687
687
687
687
687
687
687
697
697
707
717
717
717
717
717
717
717
717
727
727
727
727
727
727
727
727
727
727
727
727
727
727
727
727
727
727
727
727
727
727
737
737
737
737
737
737
737
737
737
737
737
737
737
737
737
737
737
737
737
737
737
737
747
747
747
747
747
747
747
747
747
747
747
747
747
747
747
747
747
747
747

Комментарии думаю излишни.

Добавлено @ 15:27 
Цитата(x77 @  18.12.2006,  15:23 Найти цитируемый пост)
alexeis1, вместо Time можно юзать GetTickCount.

Он тоже дает с точностью 10 мс. Это я точно проверял.
Так что лучше QueryPerformanceCounter думаю не найти. Он самый точный!

Автор: Snowy 18.12.2006, 15:31
А нужна ли такая точность?
ИМХО 1/100 секунды - достаточная точность для измерения человека smile

Автор: Alexeis 18.12.2006, 16:41
Кто хочет пусть проверит свою
Код

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ExtCtrls;

type
  TForm1 = class(TForm)
    shp1: TShape;
    btn1: TButton;
    lbl1: TLabel;
    procedure btn1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}
 var
   fl : boolean = true;
   t  : Integer;

procedure TForm1.btn1Click(Sender: TObject);
begin
  if fl
  then
    Begin
      sleep(1000 + random(4000));
      Shp1.Brush.Color := clRed;
      t := GetTickCount;
    end
  else
    Begin
      lbl1.Caption := IntToStr(GetTickCount - t);
      Shp1.Brush.Color := clWhite;
    end;
  fl := not fl;
end;


procedure TForm1.FormCreate(Sender: TObject);
begin
 randomize;
end;


Но я один раз выдал 90мс, естественно +- 10мс. Т.е. с точностью хуже 10%. Решайте сами достаточна ли вам такая точность. Мне кажется, что нет. Думаю отдельные геймеры выдадут и 60мс.

Автор: MYL 18.12.2006, 19:13
Я понимаю, как работать с GetTickCount, но как работает QueryPerformanceCounter?

Нажимаем на кнопку, через некоторое время появляется красный круг
Код

StartTime:=GetTickCount;

Человек сликает на круг, мы меняем цвет на зелёный и пишем
Код

EndTime:=GetTickCount;
our_GetTickTime:=endtime-starttime;


Но если я вместо GetTickCount буду использовать QueryPerformanceCounter, то получу какие-то странные цифры, которые мне ни о чём не говорят. К примеру 
Нажимаем на кнопку, через некоторое время появляется красный круг
Код

QueryPerformanceCounter(Start);

Человек сликает на круг, мы меняем цвет на зелёный и пишем
Код

QueryPerformanceCounter(Finish);
our_PerfTime:=Finish-Start;


И два полученных результата не равны между собой, более того, если GetTickCount даёт похожий на правду результат, то QueryPerformanceCounter выдаёт совершенно непонятные цифры. 
В общем, как правильно использовать QueryPerformanceCounter?

Автор: _hunter 18.12.2006, 19:50
MYL, а MSDN почитать (или по форуму поискать)? выяснилось бы что QueryPerformanceCounter() отдает время в "тиках". "размер" тика отдает QueryPerformanceFrequency()

Автор: Alexeis 18.12.2006, 21:04
Цитата(MYL @  18.12.2006,  19:13 Найти цитируемый пост)
Я понимаю, как работать с GetTickCount, но как работает QueryPerformanceCounter?

Блин а почитать лень? 
Цитата(alexeis1 @  18.12.2006,  14:40 Найти цитируемый пост)
Или оставить как есть используя 
QueryPerformanceCounter

После изменения цвета засечь текущее значение счетчика, после нажатия на кнопку засечь во второй раз. Посчитать их разницу и разделить это все на значение QueryPerformanceFrequency. Полученная величина будет измеряться в секундах.


Автор: MYL 18.12.2006, 23:33
The QueryPerformanceFrequency function retrieves the frequency of the high-resolution performance counter, if one exists. 

BOOL QueryPerformanceFrequency(

    LARGE_INTEGER *lpFrequency    // address of current frequency
   );    

Parameters
lpFrequency

Points to a variable that the function sets, in counts per second, to the current performance-counter frequency. If the installed hardware does not support a high-resolution performance counter, this parameter can be to zero. 

В общем, что ставить в скобках QueryPerformanceFrequency ?

Автор: MYL 18.12.2006, 23:57
А, вроде понял надо написать
Код

var
freg:int64;
begin
  QueryPerformanceFrequency(freg);
   our_time:=(finish-start)/freg;
end;


У меня ещё вопрос. А если мою программу будут запускать на совершенно разных машинах, то будут ли результаты одинаковыми, если, допустим, у всех людей реакция будет одинаковая? То есть, предположим, два варианта:
1. У человека Celeron 333 и десять запущенных приложений
2. У человека Intel Core 
Если у двух человек одинаковая реакция, то будут ли на этих двух машинах одинаковые результаты? 

В самом первом моём сообщении - пример из Delphi Russian Knowledge Base from Vit Version 2.3. Насколько я понимаю, там считают WaitCal, чтобы избежать разницы? 
То есть мне надо считать 
Код

our_time:=(finish-start)/freg-WaitCal;

Или я ошибаюсь? 

Автор: Alexeis 19.12.2006, 01:17
Разница будет только в точности измерений, но в данном случае это не существенно.

Автор: cardinal 19.12.2006, 02:25
Если надо сверхточное время замерить, как ты говоришь, то тебе скорее вообще в раздел "программирование контроллеров" потому, что сверхточного времени в многопоточной системе а ля Windows, которая к тому же стоит на разных машинах быть не может... smile (чисто мое ИМХО)

Автор: W4FhLF 19.12.2006, 13:05
Цитата

чисто мое ИМХО


Правильно рассуждаешь. Потери на многопоточности и как результат неточность измерений могут дел натворить таких, что получатся далеко не объективные результаты. Хотя опять же при грамотном выполнении задачи и на Celeron и на Core Duo результаты будут довольно точными. Но если накладывать реальные условия(т.е. могут быть запущены 10 приложений или ещё что другое), то конечно весь этот тест не стоит ничего. 
Что касается точности GetTickCount то её вполне достаточно, тем более, если учитывать что два её срабатывания как раз укладываются во время прохождения импульса от сетчатки до мозга и от мозга до пальца составляет ~20 мс, прошу заметить, что это только лишь  время движения импульсов и только лишь в том случае, если человек полностью сосредоточен на задаче.


Автор: Alexeis 19.12.2006, 13:31
Цитата(W4FhLF @  19.12.2006,  13:05 Найти цитируемый пост)
Что касается точности GetTickCount то её вполне достаточно

Я же протестировал! При 90мкс погрешность больше 10%. Может для вас этого и достаточно, но для более менее серьезных исследований сомневаюсь! И еще, установите приоритет программе Real Time. Так чтобы ее как можно меньше прерывали другие программы. Это серьезно может повлиять на точность. 

Автор: W4FhLF 19.12.2006, 14:45
alexeis1
Цитата

Я же протестировал! При 90мкс погрешность больше 10%. Может для вас этого и достаточно, но для более менее серьезных исследований сомневаюсь! И еще, установите приоритет программе Real Time. Так чтобы ее как можно меньше прерывали другие программы. Это серьезно может повлиять на точность. 


Ну во-первых, где это у нас речь шла о 90мкс? 
Во-вторых, суть Вашего теста мне не ясна, чтобы измерить точность GetTickCount Вы использую другую функцию(Sleep), которая может давать чудовищные для данного случая погрешности. Или я неправильно понял сути?
В-третьих, кто определил серьёзность исследований? Вы? При серьёзных исследованиях используются отдельные аппратные таймеры или по-крайней мере комплексы, которые работают в реальнои режиме процессора + учитывается масса других факторов, о которых здесь и небыло упомянуто. 
В-четвертых, ненужно придираться к отрывкам, я не одной лишь цитируемой фразой ограничился в своих рассуждениях, читайте и вникайте целиком в ответ.
 Прошу заметить, что я не настаиваю на её использовании и не говорю, что она безупречна, я лишь сказал, само собой, если есть возможность использова более точные средства, то это нужно делать, но опять же, повторяю, многозадачность может свести всю точность на нет.

Автор: MYL 19.12.2006, 14:47
Цитата

И еще, установите приоритет программе Real Time.

Как?

Добавлено @ 14:53 
Цитата

или по-крайней мере комплексы, которые работают в реальнои режиме процессора

Может быть, такие программы уже существуют? Но где их взять? 

Цитата

В-третьих, кто определил серьёзность исследований

Ничего серьёзного, но боюсь, что мою программку может запустить на разных машинах более 1 тысячи человек (без шуток). И я боюсь, что везде результаты могут быть разными. Вот поэтому я и спрашиваю...

Добавлено @ 14:57 
и у меня нет времени изучать ни MSDN, ни  контроллеры, ни системы в реальном времени....  Мне бы её как-нибудь написать, чтобы потом не было стыдно... Вот и вся задача! 

Автор: W4FhLF 19.12.2006, 15:01
Повысить приоритет ты конечно можешь, но это съест кучу времени у других процессов, ощутишь ли ты при этом значительную прибавку в точности сказать сложно. 
В остальном конечно использую связку QueryPerformanceCounter/QueryPerformanceFrequency, ничего лучше не придумаешь.

Автор: Alexeis 19.12.2006, 15:09
Цитата(W4FhLF @  19.12.2006,  14:45 Найти цитируемый пост)
Ну во-первых, где это у нас речь шла о 90мкс? 

http://forum.vingrad.ru/index.php?showtopic=127774&unread=1&kw=%E2%F0%E5%EC%FF-%F2%E0%E9%EC%E5%F0-%F2%EE%F7%ED%EE%F1%F2%FC-drkb-%F0%E5%E0%EA%F6%E8%FF&st=15&hl=#

Цитата(W4FhLF @  19.12.2006,  14:45 Найти цитируемый пост)
суть Вашего теста мне не ясна, чтобы измерить точность GetTickCount Вы использую другую функцию(Sleep), которая может давать чудовищные для данного случая погрешности. Или я неправильно понял сути?

Жмешь на кнопку через неопределенное время (от 1 до 5ти секунд) меняется цвет круглишка, сразу после этого считывается количество тиков, после повторного нажатия на кнопку число тиков замеряется еще раз. Разница выводится на экран.

Автор: W4FhLF 19.12.2006, 15:17
Ну наверно всё-таки не мкс, а мс.

Так, ну понял. А как Ваш тест характеризует точность данной функции?


Автор: Alexeis 19.12.2006, 15:22
MYL, SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL)

Добавлено @ 15:26 
Цитата(W4FhLF @  19.12.2006,  15:17 Найти цитируемый пост)
Ну наверно всё-таки не мкс, а мс.

Ага их самых. 
Цитата(W4FhLF @  19.12.2006,  15:17 Найти цитируемый пост)
Так, ну понял. А как Ваш тест характеризует точность данной функции?

Нет он служит для оценки реакции человека. Сопоставима ли она с точностью GetTickCount(); 
90мс сопоставимо с 10мс имхо.

Автор: W4FhLF 19.12.2006, 15:33
Цитата

Нет он служит для оценки реакции человека. Сопоставима ли она с точностью GetTickCount(); 
90мс сопоставимо с 10мс имхо.


Ну вот, а о чём тогда спор? Я ведь изначально сказал, что достаточно этой функции.

Автор: Alexeis 19.12.2006, 15:42
Звиняюсь не с точностью а с погрешностью 10мс это же погрешность...

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)