1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
|
#!/usr/local/bin/bltwish
##!/usr/bin/tclsh
#package require Tk
#package require BLT
#!/usr/local/bin/tclsh8.5
# xinput query-state 11
# xinput test 11
# xinput test-xi2
# xinput list-props 11
# sudo tclsh to run this script
# This will increase the mouse polling rate to 1kHz:
# sudo modprobe -r usbhid && sudo modprobe usbhid mousepoll=1
# THEN UN-PLUG AND RE-PLUG MOUSE.
# See https://wiki.archlinux.org/index.php/Mouse_Polling_Rate for other settings.
# Set up tcl/tk with tools we need to graph the data:
namespace import blt::*
# Initialize vectors
set nsamp 250 ; # nominally, 1 second of data
#set nsamp 1000 ; # nominally, 1 second of data
set nsamp 6000 ; # nominally, 3 second of data at 500Hz
vector create utvec($nsamp) vtvec($nsamp) uvec($nsamp) vvec($nsamp)
vector create vzero($nsamp)
# "xinput list" provides a list of possible input devices.
# Device 11 is the mouse on my laptop.
# Default location (sometimes?)
set dev 9
# After setting polling rate with usbhid:
set dev 15
# Logitech wireless? It seems that after unplugging and replugging, the device comes back on 15:
#set dev 16
#set dev 10
#set dev 11
# Disable the mouse:
proc disablemouse dev {
# other pointers should continue to work.
exec xinput disable $dev
}
# Enable the mouse:
proc enablemouse dev {
exec xinput enable $dev
}
# Determine the file name for accessing the mouse:
proc getnode dev {
# On puppy linux, the mouse seems to show up at
# return /dev/sudo modprobe -r usbhid && sudo modprobe usbhid mousepoll=1
# THEN UN-PLUG AND RE-PLUG MOUSE.
# Mad CAT:
# return /dev/input/event8
# Titanium:
# return /dev/input/event3
return /dev/input/event2
# red button is here:
# return /dev/input/event2
# return /dev/input/event12
# Execute xinput and grab the line containing "Device Node"
set retstr [exec xinput list-props $dev | grep "Device Node"]
# Parse the result to pull out the file name which is within quotes
regexp {\"(.+?)\"} $retstr -> match
return $match
}
# Find out the polling rate on the device:
proc checkrate dev {
set devnode [getnode $dev]
# "xinput list-props 11" shows that the mouse is at /dev/input/event6
# We must run tclsh as root, "sudo tclsh", in order to access the mouse in the following:
# Open the channel and configure it for directly reading binary data.
set ms [open $devnode rb]
chan configure $ms -encoding binary -translation binary -buffering none
puts "Start moving the device and keep it in continuous motion."
set i 0
set count 0
set imax 1000
set dtsum 0.
while {$i < $imax} {
set buf [read $ms 16]
# binary scan $buf iissi tvsec tvusec type code value
binary scan $buf iissi tvsec tvusec type code value
puts "$tvsec $tvusec $type $code $value"
# See linux/linux.h for a desciption of the event structure:
# tvsec
# tvusec
# type = 0 (sync) 2 (rel)
# code = 0 (xaxis) 1 (yaxis)
# value = amount of change.
if {$type == 0} {
if {$count > 0} {
set dt [expr ($tvsec - $tsec)+($tvusec - $tusec)/1.e6]
set dtsum [expr $dtsum + $dt]
}
set tsec $tvsec
set tusec $tvusec
incr count
}
incr i
}
set rate [expr $dtsum/$count ] ; # Average dt over test period.
set rate [expr 1./$rate ] ; # convert rate to Hz = 1/sec
puts "Found polling rate = $rate Hz from $count samples."
# Close the channel:
close $ms
return
}
# Read the device and compute instantaneous and average velocity in device units.
# Note that the vectors utvec, vtvec, uvec, and vvec are global variables.
proc rawvel {dev nsamp} {
set devnode [getnode $dev]
::utvec set ::vzero
::vtvec set ::vzero
::uvec set ::vzero
::vvec set ::vzero
set uttmp [list]
set utmp [list]
set vttmp [list]
set vtmp [list]
# Open the channel and configure it for directly reading binary data.
# Need to put this in a "catch" statement with reminder to
# run tclsh as sudo.
set ms [open $devnode rb]
chan configure $ms -encoding binary -translation binary -buffering none
set i 0
set buf [read $ms 16]
# binary scan $buf iissi tvsec tvusec type code value
binary scan $buf iissi tsec tusec type code value
# puts "$tsec $tusec $type $code $value"
set u_tsec_old $tsec
set u_tusec_old $tusec
set v_tsec_old $tsec
set v_tusec_old $tusec
set t0 [expr $tsec + $tusec/1.e6]
set ucount 0
set vcount 0
while {$i < $nsamp} {
set buf [read $ms 16]
# binary scan $buf iissi tvsec tvusec type code value
binary scan $buf iissi tsec tusec type code value
# puts "$tsec $tusec $type $code $value"
# sec
# usec
# type = 0 (sync) 2 (rel)
# code = 0 (xaxis) 1 (yaxis)
# value = amount of change.
# Only record increments when a relative change is reported:
if {$type == 2} {
if {$code == 0} {
set u_dt [expr ($tsec - $u_tsec_old)+($tusec - $u_tusec_old)/1.e6]
set u_tsec_old $tsec
set u_tusec_old $tusec
lappend uttmp [expr ($tsec+$tusec/1.e6)-$t0]
# lappend utmp [expr $value/$u_dt]
# It seems that each value returned is a speed, so it is not necessary to divide
# by the time increment
lappend utmp $value
incr ucount
}
if {$code == 1} {
set v_dt [expr ($tsec - $v_tsec_old)+($tusec - $v_tusec_old)/1.e6]
set v_tsec_old $tsec
set v_tusec_old $tusec
lappend vttmp [expr ($tsec+$tusec/1.e6)-$t0]
# lappend vtmp [expr $value/$v_dt]
# It seems that each value returned is a speed, so it is not necessary to divide
# by the time increment
lappend vtmp $value
incr vcount
}
}
incr i
# puts "$ucount $vcount"
}
# Close the channel:
close $ms
::utvec set $uttmp
::uvec set $utmp
::vtvec set $vttmp
::vvec set $vtmp
if {[::uvec length] > 1} {
if {[::vvec length] > 1} {
set ubar [vector expr {mean(::uvec)}]
set vbar [vector expr {mean(::vvec)}]
::uvec set [vector expr {::uvec - $ubar}]
::vvec set [vector expr {::vvec - $vbar}]
set uvar [vector expr {mean(::uvec * ::uvec)}]
set vvar [vector expr {mean(::vvec * ::vvec)}]
if {[::uvec length] <= [::vvec length]} {
set lu [::uvec length]
set lu [expr $lu - 1]
::vvec set ::vvec(0:$lu)
::vtvec set ::vtvec(0:$lu)
set uvcov [vector expr {mean(::uvec * ::vvec)}]
} else {
set lu [::vvec length]
set lu [expr $lu - 1]
::uvec set ::uvec(0:$lu)
::utvec set ::utvec(0:$lu)
set uvcov [vector expr {mean(::uvec * ::vvec)}]
}
puts "$ubar $vbar $uvar $vvar $uvcov"
return "$ubar $vbar $uvar $vvar $uvcov"
}
}
return "#ERROR"
}
proc value_dialog {string} {
set w [toplevel .[clock seconds]]
wm resizable $w 0 0
wm title $w "Value request"
wm attributes $w -topmost true
label $w.l -text $string
entry $w.e -textvar $w -bg white
bind $w.e <Return> {set done 1}
button $w.ok -text OK -command {set done 1}
button $w.c -text Clear -command "set $w {}"
button $w.cancel -text Cancel -command "set $w {}; set done 1"
grid $w.l - - -sticky news
grid $w.e - - -sticky news
grid $w.ok $w.c $w.cancel
focus $w.e
vwait done
destroy $w
set ::$w
}
stripchart .s1 -height 2i -width 8i -bufferelements no
stripchart .s2 -height 2i -width 8i -bufferelements no
pack .s1 .s2
.s1 element create line1 -xdata utvec -ydata uvec -symbol none
.s2 element create line2 -xdata vtvec -ydata vvec -symbol none -color red
#.s1 axis configure y -min -16000 -max 16000
#.s2 axis configure y -min -16000 -max 16000
# Disable the selected device so you can keep using the computer while
# the mouse is collecting data:
# Does not work on puppy linux:
#disablemouse $dev
# Bind keypress event to write the next available out string to file.
global outnow
set outnow 0
bind . <Key> {global outnow ; set outnow 1}
set tstamp [clock format [clock seconds] -format %y%m%dT%H%M%S]
set fid [open ./Data/d${tstamp}.dat w]
set hdr [value_dialog "Enter header for output file:"]
puts $fid "###"
puts $fid "### Output of testdev.tcl on [exec hostname]."
puts $fid "### Edward D. Zaron"
puts $fid "### Dept of Civil and Environmental Engineering"
puts $fid "### Portland State University"
puts $fid "### Portland, OR USA"
puts $fid "### ezaron@pdx.edu"
puts $fid "### 503-725-2435"
puts $fid "###"
puts $fid "### $hdr"
puts $fid "### Columns are in instrument units:"
puts $fid "### ubar vbar uvar vvar uvcov"
puts $fid "###"
flush $fid
# Enter the event loop and collect data:
while {1 == 1} {
update idletasks
set out [rawvel $dev $nsamp]
# For some reason update idletasks is not enough to force
# evaluation of the bind script.
# update idletasks
update
puts $outnow
if {$outnow == 1} {
set xval [value_dialog "X Value:"]
puts "Writing data to file."
puts $fid "$xval $out"
set outnow 0
flush $fid
}
}
# How shall we despike or average?
## unused stuff below:
proc getselection {} {
set ret [.devlist curselection]
puts "You chose number $ret"
return $ret
}
proc selectdevice {} {
# Choose device:
set devname [split [exec xinput list --name-only] \n]
set devid [split [exec xinput list --id-only] \n]
listbox .devlist
set count 0
foreach id $devid {
set str "id = $id [lindex $devname $count]"
.devlist insert end $str
incr count
}
button .choose -text "Select" -command getselection
pack .devlist .choose -expand true -fill both
}
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|
|
|