-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathlaunch.py
More file actions
131 lines (107 loc) · 3.97 KB
/
Copy pathlaunch.py
File metadata and controls
131 lines (107 loc) · 3.97 KB
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
"""Orientation-independent, cancellable vehicle launch detection."""
import math
import time
CALIBRATION_SAMPLES = 20
SAMPLE_INTERVAL_MS = 20
FILTER_ALPHA = 0.35
TRIGGER_SAMPLES = 3
DEFAULT_TIMEOUT_SEC = 30
def _ticks_ms(clock):
ticks_ms = getattr(clock, "ticks_ms", None)
if ticks_ms is not None:
return ticks_ms()
monotonic = getattr(clock, "monotonic", None)
if monotonic is not None:
return int(monotonic() * 1000)
return int(clock.time() * 1000)
def _ticks_diff(clock, current, previous):
ticks_diff = getattr(clock, "ticks_diff", None)
if ticks_diff is not None:
return ticks_diff(current, previous)
return current - previous
def _sleep_ms(clock, milliseconds):
sleep_ms = getattr(clock, "sleep_ms", None)
if sleep_ms is not None:
sleep_ms(milliseconds)
else:
clock.sleep(milliseconds / 1000)
def _timed_out(clock, started_at, timeout_sec):
if timeout_sec is None:
return False
elapsed_ms = _ticks_diff(clock, _ticks_ms(clock), started_at)
return elapsed_ms >= int(timeout_sec * 1000)
def _should_exit(cancel_check, clock, started_at, timeout_sec):
return (
(cancel_check is not None and cancel_check())
or _timed_out(clock, started_at, timeout_sec)
)
def _acceleration(sample):
if len(sample) < 3:
raise ValueError("Accelerometer sample must contain x, y, and z axes")
return float(sample[0]), float(sample[1]), float(sample[2])
def accel_launch(
qmi8658,
sensitivity=0,
cancel_check=None,
timeout_sec=DEFAULT_TIMEOUT_SEC,
clock=time,
calibration_samples=CALIBRATION_SAMPLES,
sample_interval_ms=SAMPLE_INTERVAL_MS,
filter_alpha=FILTER_ALPHA,
trigger_samples=TRIGGER_SAMPLES,
baseline=None,
):
"""Wait for a sustained acceleration-vector change and return its outcome.
``sensitivity`` is a threshold in g relative to a stationary baseline. The
baseline removes gravity and device mounting orientation. Both positive and
negative acceleration are detected because the filtered vector magnitude is
used. ``False`` means the wait was cancelled or timed out.
"""
threshold = float(sensitivity)
if threshold <= 0:
return True
if calibration_samples < 1:
raise ValueError("calibration_samples must be positive")
if trigger_samples < 1:
raise ValueError("trigger_samples must be positive")
if sample_interval_ms < 0:
raise ValueError("sample_interval_ms cannot be negative")
if filter_alpha <= 0 or filter_alpha > 1:
raise ValueError("filter_alpha must be greater than 0 and at most 1")
started_at = _ticks_ms(clock)
if baseline is None:
baseline = [0.0, 0.0, 0.0]
for _ in range(calibration_samples):
if _should_exit(cancel_check, clock, started_at, timeout_sec):
return False
axes = _acceleration(qmi8658.Read_XYZ())
baseline[0] += axes[0]
baseline[1] += axes[1]
baseline[2] += axes[2]
_sleep_ms(clock, sample_interval_ms)
baseline[0] /= calibration_samples
baseline[1] /= calibration_samples
baseline[2] /= calibration_samples
else:
baseline = _acceleration(baseline)
filtered = [0.0, 0.0, 0.0]
consecutive = 0
while True:
if _should_exit(cancel_check, clock, started_at, timeout_sec):
return False
axes = _acceleration(qmi8658.Read_XYZ())
for index in range(3):
delta = axes[index] - baseline[index]
filtered[index] += filter_alpha * (delta - filtered[index])
magnitude = math.sqrt(
(filtered[0] * filtered[0])
+ (filtered[1] * filtered[1])
+ (filtered[2] * filtered[2])
)
if magnitude >= threshold:
consecutive += 1
if consecutive >= trigger_samples:
return True
else:
consecutive = 0
_sleep_ms(clock, sample_interval_ms)