2013-10-09 3 views
9

Я разрабатываю приложение, в котором пользователь может что-то увидеть и должен реагировать, нажав клавишу на клавиатуре. Время реакции имеет решающее значение, и чем точнее, тем лучше.WPF keyDown time time

Я написал пример приложение WPF Inf только несколько строк кода, чтобы проверить настройки по умолчанию:

namespace Test 
{ 
    /// <summary> 
    /// Interaction logic for MainWindow.xaml 
    /// </summary> 
    public partial class MainWindow : Window 
    { 
    private Stopwatch sw; 
    public MainWindow() 
    { 
     InitializeComponent(); 
     sw = new Stopwatch(); 
     sw.Start(); 
     this.KeyDown += OnKeyDown; 
    } 

    private void OnKeyDown(object sender, KeyEventArgs keyEventArgs) 
    { 
     sw.Stop(); 

     lbl.Content = sw.ElapsedMilliseconds; 
     sw.Restart(); 
    } 
    } 
} 

lbl является просто простой этикетки.

Странно то, что когда я нажимаю, например, пространство и удерживаю его, значение lbl изменяется в диапазоне: 30-33.

Итак, я не могу предсказать, какова точность ответа? Невозможно ли иметь, например, 1 мс точность? Пользователь занимает пространство и в то же время (например, точность 1 мс). Я могу обработать его в обработчике событий?

Основной вопрос:

скажем, у меня есть обработчик событий ключ-вниз:

Test_KeyDown(object sender, KeyEventArgs keyEventArgs) 
{ 
    time = stopwatch.elapsed(); 
    stopwatch.Restart(); 
} 

что минимальное значение «времени», которое может произойти, возможно? Могу ли я быть уверенным, что значение времени равно 1 мс? В этом методе я запускаю секундомер, но потом мне нужно подождать - как долго - для обновления графического интерфейса?

+0

Не знаете, что вы пытаетесь измерить там, и как вы это делаете. 'KeyDown' не поднимается непрерывно, пока ключ не работает (я думаю, это будет' KeyPress'), поэтому удерживать пробел не нужно обновлять ярлык несколько раз, не так ли? Если вы имеете в виду, что перед тем, как показывать окно, вы удерживаете пробел, тогда вы сравниваете код между конструктором, пока окно не станет окончательно активным и не накинет события. – jods

+0

Насколько я знаю, отправка события происходит так же быстро, как это может быть и работает почти так же, как это делает любое другое приложение Windows: клавиша, находящаяся вниз, обрабатывается ОС, которая затем отправляет сообщение в очередь вашего окна сообщений, который затем накачивается, и соответствующее событие поднимается на сфокусированном контроле. Точность достаточно хороша по сравнению с временами реакции человека, если вы не выполняете работу над потоком пользовательского интерфейса. – jods

+0

Скорость нажатой клавиши будет зависеть от конфигурации окна частоты повторения клавиш. http://windows.microsoft.com/is-is/windows-xp/help/adjust-the-character-repeat-rate – Tony

ответ

5

Во-первых, если это Stopwatch.IsHighResolutiontrue, то Stopwatch использует QueryPerformanceCounter, и он может измерять временные интервалы с разрешением < 1 мс.

Во-вторых, когда вы нажимаете и удерживаете клавишу пробела, Windows начинает отправлять сообщения WM_KEYDOWN несколько раз, и ваш секундомер будет измерять интервал между этими сообщениями. Этот интервал определяется ключом реестра HKCU\Control Panel\Keyboard\KeyboardSpeed.

Значение по умолчанию - 31, что является самой быстрой частотой повторения, что означает примерно 30 символов в секунду. Вот почему вы измеряли интервалы приблизительно 1000/30 = 33 мс.

Если вы установите его на 0, то есть на самую медленную скорость повторения, что означает примерно 2 символа в секунду, тогда вы должны измерять ок. Интервал 500 мс. Я проверил ваш код с этим параметром, и я получил 500 мс. (Не забудьте перезагрузить Windows после смены KeyboardSpeed!)

Вам нужно поймать одно событие keydown, а не повторные события, поэтому вам не нужно менять настройку KeyboardSpeed. Ваша программа должна просто показать объект пользователю, запустить секундомер и остановить его, если произойдет событие keydown. ElapsedMilliseconds даст время реакции. Измерьте его несколько раз и используйте среднее значение.

Проблема в том, что даже если QueryPerformanceCounter точно измеряет прошедшее время, происходит задержка, вызванная клавиатурой и самой Windows, что увеличит измеренное время реакции. Кроме того, задержка не является постоянной: если Windows занята в тот момент, когда она должна обрабатывать событие keydown, тогда задержка будет больше. Поэтому, если вы серьезно относитесь к этой задаче, вы должны откалибровать свою программу.

Я имею в виду, вы должны купить или построить небольшое электронное устройство на микроконтроллере, которое включает светодиод и определяет время, прошедшее между включением светодиода и нажатием кнопки.Сделайте 10-20 (тем лучше) время измерения реакции с этим устройством и с тем же испытуемым, выполните еще 10-20 измерений с вашей программой. Разница между ними даст вам задержку, вызванную клавиатурой и Windows. Эта разница может быть вычтена из времени реакции, измеренного вашей программой.

(Можно спросить, почему вы не должны использовать небольшое, но точное электронное устройство вместо приложения Windows. Во-первых, изготовление и продажа программного обеспечения намного проще и дешевле, чем производство и продажа оборудования. Во-вторых, визуальный объект может быть сложным (например, шахматной доской), а сложные объекты могут отображаться гораздо эффективнее на ПК.)

+0

Возможно, я предлагаю изменить реестр пользователей, возможно, не лучший способ сделать это, но просто прочитав это значение и обнаружив холдинг указанного ключа. – SeToY

+0

@SeToY Я изменил реестр, чтобы проверить мою гипотезу о промежутке 33 мс, измеренном ОП, когда пространство было нажато непрерывно. Точность измерения временного интервала не связана с этим, поэтому OP не должен изменять реестр пользователя. – kol

11

Ваш тест, безусловно, недействителен, он просто измеряет частоту повторения клавиатуры, как указано несколькими другими участниками. Но это полезно для его непредвиденной выгоды, вы можете увидеть, что у вас не будет проблем с клавиатурой.

Большинство событий в Windows происходят со скоростью, определяемой по тактовой частоте прерывания. Который по умолчанию тикает 64 раза в секунду, каждые 15.625 миллисекунд. Это пробуждает ядро, и оно идет посмотреть, нужно ли что-то делать, ища работу, чтобы перейти к ядру процессора. Как правило, нет ничего общего, и ядро ​​отключается с помощью инструкции HLT. Пока не произойдет следующее прерывание.

Таким образом, проблема в том, что ваш тест никогда не может быть более точным, чем 15.625 миллисекунд. И ваше наблюдение совпадает, случайно, то, что вы видите, в два раза больше. Но на самом деле это не так, и вы можете использовать свою программу, чтобы это увидеть. Используйте Панель управления + Клавиатура и настройте ползунок Repeat Rate. Обратите внимание, как вы можете настроить его и изменить свой номер на значения, не кратные 15.625.

Это не совсем случайный случай, контроллер клавиатуры также генерирует прерывание, как и часы. У вас есть положительное доказательство того, что это прерывание уже достаточно хорошо, чтобы ваша программа снова активировалась. И вы можете сказать, что сам контроллер клавиатуры достаточно быстро сканирует клавиатурную матрицу. Ваша панель ошибок с клавиатуры не будет больше +/- 2 мсек, о шуме, который вы видите в отображаемом номере. Если у вас есть клавиатура, которая сканирует медленнее, вы можете устранить ее с помощью этого теста.


У вас гораздо больше проблем с видео. Видеоадаптер обычно обновляет ЖК-монитор со скоростью 60 обновлений в секунду. В худшем случае испытуемый не смог бы физически видеть изображение за 17 мс. И ЖК-мониторы сами по себе не так быстр, дешевые имеют время отклика 16 мс или хуже. Побочный эффект кристалла в жидкости не способен быстро перевернуться.

Устранение ошибки частоты обновления требует, чтобы программа синхронизировалась с vertical blanking interval. Что-то вы можете сделать с DirectX. Вы можете найти высококлассные ЖК-мониторы, которые имеют время отклика около 4 мс, популярное у геймеров.

1

Я хотел бы указать еще один инструмент для отслеживания вашего времени здесь. Поскольку вы рассматриваете тестирование ответа приложения, и, как кто-то упомянул об этом, это связано с сообщениями ОС, вы можете использовать Spy ++ для просмотра времени этих сообщений. Я скопировал вывод нажатой области в окно, которое я слушал только для сообщений клавиатуры, включив все выходные данные. Я нажал пробел один раз и отпустил так быстро, как мог, на клавиатуре USB, которая через док-станцию. Вы можете видеть, что для обработки вниз и вверх потребовалось ~ .05ms.

<00001> 00090902 P WM_KEYDOWN nVirtKey:VK_SPACE cRepeat:1 ScanCode:39 fExtended:0 fAltDown:0 fRepeat:0 fUp:0 [wParam:00000020 lParam:00390001 time:1:07:38.116 point:(183, 290)] 
<00002> 00090902 P WM_CHAR chCharCode:'32' (32) cRepeat:1 ScanCode:39 fExtended:0 fAltDown:0 fRepeat:0 fUp:0 [wParam:00000020 lParam:00390001 time:1:07:38.116 point:(183, 290)] 
<00003> 00090902 P WM_KEYUP nVirtKey:VK_SPACE cRepeat:1 ScanCode:39 fExtended:0 fAltDown:0 fRepeat:1 fUp:1 [wParam:00000020 lParam:C0390001 time:1:07:38.163 point:(183, 290)] 

Spy ++ - это инструмент, предоставляемый Visual Studio. Вы можете найти его в C:\Program Files\Microsoft Visual Studio XYZ\Common7\Tools\spyxx.exe, где XYZ - 8, 9,0 и 10,0, что я могу подтвердить.

Что бы вы могли сделать для дальнейшего тестирования таймингов, было бы, если бы Spy ++ прослушивал команды клавиатуры и WM_PAINT или что-то в этом роде, чтобы узнать, как быстро программа реагирует на сообщение с клавиатурой с изменением пользовательского интерфейса.

Например, ниже находится чистый журнал, после того, как он имеет калькулятор с 3+3, затем нажимая Enter. Вы видите, что калькулятор смог вычислить и отобразить до .062ms, которые потребовались между KeyDown и KeyUp для обработки.

<00001> 00090902 P WM_KEYDOWN nVirtKey:VK_RETURN cRepeat:1 ScanCode:1C fExtended:1 fAltDown:0 fRepeat:0 fUp:0 [wParam:0000000D lParam:011C0001 time:1:19:12.539 point:(179, 283)] 
<00002> 00090902 S WM_PAINT hdc:00000000 [wParam:00000000 lParam:00000000] 
<00003> 00090902 R WM_PAINT lResult:00000000 
<00004> 00090902 S WM_PAINT hdc:00000000 [wParam:00000000 lParam:00000000] 
<00005> 00090902 R WM_PAINT lResult:00000000 
<00006> 00090902 S WM_PAINT hdc:00000000 [wParam:00000000 lParam:00000000] 
<00007> 00090902 R WM_PAINT lResult:00000000 
<00008> 00090902 S WM_PAINT hdc:00000000 [wParam:00000000 lParam:00000000] 
<00009> 00090902 R WM_PAINT lResult:00000000 
<00010> 00090902 P WM_KEYUP nVirtKey:VK_RETURN cRepeat:1 ScanCode:1C fExtended:1 fAltDown:0 fRepeat:1 fUp:1 [wParam:0000000D lParam:C11C0001 time:1:19:12.601 point:(179, 283)] 

edit- In Spy ++ Предлагаю перейти к параметрам ведения журнала, который показывает диалоговое окно параметров сообщений. Перейдите на вкладку «Сообщения», нажмите «Очистить все», отметьте «Клавиатура», а затем прокрутите список и выберите «WM_PAINT». Таким образом, у вас есть только нужные сообщения, иначе вы будете наводнены ими.