Skip to content

Commit

Permalink
Save device timestamp/serial for each event.
Browse files Browse the repository at this point in the history
  • Loading branch information
wavexx committed Apr 21, 2015
1 parent e90eb10 commit 7a4d24e
Show file tree
Hide file tree
Showing 8 changed files with 60 additions and 28 deletions.
33 changes: 25 additions & 8 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -117,12 +117,20 @@ Troubleshooting
Version changes
~~~~~~~~~~~~~~~

1.8:

* DrawingRecorder file format 1.5.
* DrawingRecorder now also includes lower-level packet timestamps/serials
on Windows which are better suited for analysis.

1.7:

* DrawingRecorder file format 1.4.
* Fixed file encoding issues when saving unicode data.

1.6:

* DrawingRecorder file format 1.4.
* DrawingRecorder/StylusProfiler now output JSON-encoded files using the same
data structure. Saving/loading times are drastically improved.
* Improved time resolution of the event stream under Windows.
Expand Down Expand Up @@ -165,10 +173,11 @@ Version changes
1.3:

* DrawingRecorder file format 1.2.
* Several tools for data analysis have been added (``drwstats``,
* Several tools for data inspection have been added (``drwstats``,
``drwrenderer`` and ``drwstackrenderer``).
* Tools for analysis and DrawingVisualizer can now use 'dump' files to speed-up
loading/saving time. ``drwconvert`` can convert between YaML/dump formats.
* Tools for data inspection and DrawingVisualizer can now use 'dump' files to
speed-up loading/saving time. ``drwconvert`` can be used to convert between
YaML/dump formats.
* A simple tool to generate and check IDs with a Verhoeff check digit
(patient/table/stylus ID) has been added (``genverhoeff``).
* In DrawingVisualizer, the speed is now sampled to give more accurate results.
Expand Down Expand Up @@ -224,11 +233,12 @@ Version changes
Known issues
~~~~~~~~~~~~

* 1.0/1.1: Quantization of event's timestamps: the "stamp" value of the event
stream is badly quantized due to it not coming directly from the tablet.
Unfortunately QT4 does not offer event timestamps. One must currently derive
the device's event rate instead of relying on the timestamp for proper
analysis.
* 1.0-1.7: Timestamp issues: due to internal limitations, the "stamp" value of
the event stream has significant jitter as it's originating from the
recorder's event loop itself. Until DR 1.6 the effective resolution on
Windows platforms was also lower than 10ms. DR 1.6 introduced higher-quality
timestamps on Windows, and DR 1.8 also added "dev_stamp" and "dev_serial"
which are suitable for accurate time analysis.
* 1.0: Tablet enter/leave events not properly tracked: proximity events are
still missing from the event stream, meaning that holes in the "trace"
require post-processing to be detected, and doing so it not easy due to the
Expand Down Expand Up @@ -587,6 +597,13 @@ Chunks introduced with format 1.4:
+ ``version`` moved from ``extra_data/orig_version`` in DrawingRecorder 1.5.
+ Any field name from the output of ``drwstats``.

Chunks introduced with format 1.5:

* ``recording/events``:

+ ``dev_stamp`` (optional): accurate event timestamp (in MS)
+ ``dev_serial`` (optional): event serial number


Profiler ``prof.json.gz`` File format
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down
4 changes: 2 additions & 2 deletions src/lib/DrawingRecorder/Consts.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@
# Basic versioning
APP_ORG = "EURAC" # application organization
APP_NAME = "DrawingRecorder" # application name
APP_VERSION = "1.7" # application version
APP_VERSION = "1.8" # application version
APP_DELAY = 1. / 20. # general refresh delay for lengthy operations

# File formats
FORMAT_VERSION = "1.4" # file format version
FORMAT_VERSION = "1.5" # file format version
FF_RECORDING = "rec" # recording type
FF_PROFILE = "prof" # profiling type

Expand Down
16 changes: 14 additions & 2 deletions src/lib/DrawingRecorder/Data.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,14 +140,16 @@ def __init__(self, oid, tablet_id, stylus_id, cpoints, ctilts, stamp=None):

class RecordingEvent(object):
def __init__(self, typ, coords_drawing, coords_trans, pressure,
tilt_drawing, tilt_trans, stamp):
tilt_drawing, tilt_trans, stamp, dev_stamp, dev_serial):
self.typ = typ
self.coords_drawing = coords_drawing
self.coords_trans = coords_trans
self.pressure = pressure
self.tilt_drawing = tilt_drawing
self.tilt_trans = tilt_trans
self.stamp = stamp
self.dev_stamp = dev_stamp
self.dev_serial = dev_serial


@classmethod
Expand All @@ -163,6 +165,12 @@ def serialize(cls, event):
data['tdraw'] = list(event.tilt_drawing)
data['ttrans'] = list(event.tilt_trans)

# device stamp/serial (fmt 1.5)
if event.dev_stamp is not None:
data['dev_stamp'] = event.dev_stamp
if event.dev_serial is not None:
data['dev_serial'] = event.dev_serial

return data


Expand All @@ -181,8 +189,12 @@ def deserialize(cls, event):
tilt_drawing = tuple(tilt_drawing)
tilt_trans = tuple(tilt_trans)

# device stamp/serial (fmt 1.5)
dev_stamp = event.get('dev_stamp')
dev_serial = event.get('dev_serial')

return RecordingEvent(typ, coords_drawing, coords_trans, pressure,
tilt_drawing, tilt_trans, stamp)
tilt_drawing, tilt_trans, stamp, dev_stamp, dev_serial)



Expand Down
3 changes: 2 additions & 1 deletion src/lib/DrawingRecorder/DrawingWindow.py
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,8 @@ def extTabletEvent(self, ev):
[coords_drawing.x(), coords_drawing.y()],
[coords_trans.x(), coords_trans.y()],
ev.pressure, self.dw._drawing_tilt,
self.dw._trans_tilt, ev.os_stamp))
self.dw._trans_tilt, ev.os_stamp,
ev.dev_stamp, ev.dev_serial))

if not self.dw._drawing_state:
self.old_trans_pos = None
Expand Down
4 changes: 3 additions & 1 deletion src/lib/QExtTabletWindow/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,12 @@ def __init__(self, *args, **kwargs):
super(QExtTabletException, self).__init__(*args, **kwargs)

class QExtTabletEvent(QtCore.QEvent):
def __init__(self, subtype, os_stamp, pressure, position, tilt):
def __init__(self, subtype, os_stamp, dev_stamp, dev_serial, pressure, position, tilt):
super(QExtTabletEvent, self).__init__(EVENT_TYPE)
self.subtype = subtype
self.os_stamp = os_stamp
self.dev_stamp = dev_stamp
self.dev_serial = dev_serial
self.pressure = pressure
self.position = position
self.tilt = tilt
2 changes: 1 addition & 1 deletion src/lib/QExtTabletWindow/generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ def eventFilter(self, receiver, event):
def handleEvent(self, receiver, event):
win = self._app.activeWindow()
if win and win in self._windows:
ev = QExtTabletEvent(event.type(), HiResTime.now(), event.pressure(),
ev = QExtTabletEvent(event.type(), HiResTime.now(), None, None, event.pressure(),
event.hiResGlobalPos(), (event.xTilt(), event.yTilt()))
win.event(ev)

Expand Down
2 changes: 1 addition & 1 deletion src/lib/QExtTabletWindow/libwintab.py
Original file line number Diff line number Diff line change
Expand Up @@ -375,7 +375,7 @@ def wtinfo(category, index, buffer):
buffer = ctypes.create_unicode_buffer(size)
assert(size <= ctypes.sizeof(buffer))
lib.WTInfoW(category, index, ctypes.byref(buffer))
if not isinstance(buffer, ctypes.Structure):
if 'value' in dir(buffer):
buffer = buffer.value
if type(buffer) is unicode:
buffer = buffer.rstrip()
Expand Down
24 changes: 12 additions & 12 deletions src/lib/QExtTabletWindow/wintab.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,7 @@
# Low-level handler
class QExtTabletManager(QtCore.QObject):
_instance = None
_events = set([QtCore.QEvent.TabletEnterProximity,
QtCore.QEvent.TabletLeaveProximity,
QtCore.QEvent.TabletMove,
QtCore.QEvent.TabletPress,
QtCore.QEvent.TabletRelease])
_ctxs = None

def __init__(self):
super(QExtTabletManager, self).__init__()
Expand Down Expand Up @@ -119,6 +115,7 @@ def __init__(self, window, device_n):

# Axis
self.np_axis = wtinfo(self.device_idx, libwintab.DVC_NPRESSURE, libwintab.AXIS())
self.or_axis = wtinfo(self.device_idx, libwintab.DVC_ORIENTATION, (libwintab.AXIS * 3)())
self.x_axis = wtinfo(self.device_idx, libwintab.DVC_X, libwintab.AXIS())
self.y_axis = wtinfo(self.device_idx, libwintab.DVC_Y, libwintab.AXIS())

Expand Down Expand Up @@ -153,7 +150,7 @@ def handleMsg(self, msg):

if msg.message == WT_PROXIMITY:
subtype = EventSubtypes.ENTER if (msg.lParam & 0xFF) else EventSubtypes.LEAVE
ev = QExtTabletEvent(subtype, now, 0., QtCore.QPointF(), (0., 0.))
ev = QExtTabletEvent(subtype, now, None, None, 0., QtCore.QPointF(), (0., 0.))
self.window.event(ev)

# process any available packet, irregardless of the message type
Expand All @@ -168,8 +165,8 @@ def handleMsg(self, msg):
self.cursors[packet.pkCursor] = cursor
cursor = self.cursors[packet.pkCursor]

# We copy QT's formulas here to be result-compatible, even though
# we undo most of them later. Note that press also lacks bias ajustment(!)
# We copy QT's formulas verbatim here to be result-compatible, even though
# we undo most of them later. Note that press also lacks bias adjustment(!)
press = float(packet.pkNormalPressure) / float(self.np_axis.axMax - self.np_axis.axMin)

pos_x = float(packet.pkX - self.x_axis.axMin) / float(self.x_axis.axMax - self.x_axis.axMin)
Expand All @@ -179,10 +176,12 @@ def handleMsg(self, msg):

rad_azim = (packet.pkOrientation.orAzimuth / 10.) * (math.pi / 180.)
tan_alt = tan((abs(packet.pkOrientation.orAltitude / 10.)) * (math.pi / 180.))
tilt_x = int(atan(sin(rad_azim) / tan_alt) * (180. / math.pi))
tilt_y = int(atan(cos(rad_azim) / tan_alt) * (180. / math.pi))
deg_x = atan(sin(rad_azim) / tan_alt)
deg_y = atan(cos(rad_azim) / tan_alt)
tilt_x = int(deg_x * (180. / math.pi))
tilt_y = int(-deg_y * (180. / math.pi))

# use the primary button state to determine enter/leave events
# use the primary button state to determine press/release events
if not (packet.pkChanged & libwintab.PK_BUTTONS):
subtype = EventSubtypes.MOVE
else:
Expand All @@ -195,7 +194,8 @@ def handleMsg(self, msg):
else:
subtype = EventSubtypes.RELEASE

ev = QExtTabletEvent(subtype, now, press, pos, (tilt_x, tilt_y))
ev = QExtTabletEvent(subtype, now, packet.pkTime, packet.pkSerialNumber,
press, pos, (tilt_x, tilt_y))
self.window.event(ev)


Expand Down

0 comments on commit 7a4d24e

Please sign in to comment.