-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Expand file tree
/
Copy pathMotionController.cpp
More file actions
171 lines (143 loc) · 5.34 KB
/
MotionController.cpp
File metadata and controls
171 lines (143 loc) · 5.34 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
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
#include "components/motion/MotionController.h"
#include <task.h>
#include "utility/Math.h"
using namespace Pinetime::Controllers;
namespace {
constexpr inline int32_t Clamp(int32_t val, int32_t min, int32_t max) {
return val < min ? min : (val > max ? max : val);
}
// only returns meaningful values if inputs are acceleration due to gravity
int16_t DegreesRolled(int16_t y, int16_t z, int16_t prevY, int16_t prevZ) {
int16_t prevYAngle = Pinetime::Utility::Asin(Clamp(prevY * 32, -32767, 32767));
int16_t yAngle = Pinetime::Utility::Asin(Clamp(y * 32, -32767, 32767));
if (z < 0 && prevZ < 0) {
return yAngle - prevYAngle;
}
if (prevZ < 0) {
if (y < 0) {
return -prevYAngle - yAngle - 180;
}
return -prevYAngle - yAngle + 180;
}
if (z < 0) {
if (y < 0) {
return prevYAngle + yAngle + 180;
}
return prevYAngle + yAngle - 180;
}
return prevYAngle - yAngle;
}
}
void MotionController::AdvanceDay() {
--nbSteps; // Higher index = further in the past
SetSteps(Days::Today, 0);
carrySteps = 0;
if (service != nullptr) {
service->OnNewStepCountValue(NbSteps(Days::Today));
}
}
void MotionController::Update(int16_t x, int16_t y, int16_t z, uint32_t nbSteps) {
// Offset the sensor value by whatever we are carrying forward
nbSteps += carrySteps;
uint32_t oldSteps = NbSteps(Days::Today);
if (oldSteps != nbSteps && service != nullptr) {
service->OnNewStepCountValue(nbSteps);
}
if (service != nullptr && (xHistory[0] != x || yHistory[0] != y || zHistory[0] != z)) {
service->OnNewMotionValues(x, y, z);
}
lastTime = time;
time = xTaskGetTickCount();
xHistory++;
xHistory[0] = x;
yHistory++;
yHistory[0] = y;
zHistory++;
zHistory[0] = z;
// Update accumulated speed
// Currently polling at 10Hz, if this ever goes faster scalar and EMA might need adjusting
int32_t speed = std::abs(zHistory[0] - zHistory[histSize - 1] + ((yHistory[0] - yHistory[histSize - 1]) / 2) +
((xHistory[0] - xHistory[histSize - 1]) / 4)) *
100 / (time - lastTime);
// integer version of (.2 * speed) + ((1 - .2) * accumulatedSpeed);
accumulatedSpeed = speed / 5 + accumulatedSpeed * 4 / 5;
stats = GetAccelStats();
int32_t deltaSteps = nbSteps - oldSteps;
if (deltaSteps > 0) {
currentTripSteps += deltaSteps;
}
SetSteps(Days::Today, nbSteps);
}
MotionController::AccelStats MotionController::GetAccelStats() const {
AccelStats stats;
for (uint8_t i = 0; i < AccelStats::numHistory; i++) {
stats.xMean += xHistory[histSize - i];
stats.yMean += yHistory[histSize - i];
stats.zMean += zHistory[histSize - i];
stats.prevXMean += xHistory[1 + i];
stats.prevYMean += yHistory[1 + i];
stats.prevZMean += zHistory[1 + i];
}
stats.xMean /= AccelStats::numHistory;
stats.yMean /= AccelStats::numHistory;
stats.zMean /= AccelStats::numHistory;
stats.prevXMean /= AccelStats::numHistory;
stats.prevYMean /= AccelStats::numHistory;
stats.prevZMean /= AccelStats::numHistory;
for (uint8_t i = 0; i < AccelStats::numHistory; i++) {
stats.xVariance += (xHistory[histSize - i] - stats.xMean) * (xHistory[histSize - i] - stats.xMean);
stats.yVariance += (yHistory[histSize - i] - stats.yMean) * (yHistory[histSize - i] - stats.yMean);
stats.zVariance += (zHistory[histSize - i] - stats.zMean) * (zHistory[histSize - i] - stats.zMean);
}
stats.xVariance /= AccelStats::numHistory;
stats.yVariance /= AccelStats::numHistory;
stats.zVariance /= AccelStats::numHistory;
return stats;
}
bool MotionController::ShouldRaiseWake() const {
constexpr uint32_t varianceThresh = 56 * 56;
constexpr int16_t xThresh = 384;
constexpr int16_t yThresh = -64;
constexpr int16_t rollDegreesThresh = -45;
if (std::abs(stats.xMean) > xThresh) {
return false;
}
// if the variance is below the threshold, the accelerometer values can be considered to be from acceleration due to gravity
if (stats.yVariance > varianceThresh || (stats.yMean < -724 && stats.zVariance > varianceThresh) || stats.yMean > yThresh) {
return false;
}
return DegreesRolled(stats.yMean, stats.zMean, stats.prevYMean, stats.prevZMean) < rollDegreesThresh;
}
bool MotionController::ShouldLowerSleep() const {
if ((stats.xMean > 887 && DegreesRolled(stats.xMean, stats.zMean, stats.prevXMean, stats.prevZMean) > 30) ||
(stats.xMean < -887 && DegreesRolled(stats.xMean, stats.zMean, stats.prevXMean, stats.prevZMean) < -30)) {
return true;
}
if (stats.yMean < 724 || DegreesRolled(stats.yMean, stats.zMean, stats.prevYMean, stats.prevZMean) < 30) {
return false;
}
for (uint8_t i = AccelStats::numHistory + 1; i < yHistory.Size(); i++) {
if (yHistory[i] < 265) {
return false;
}
}
return true;
}
void MotionController::Init(Pinetime::Drivers::Bma421::DeviceTypes types) {
switch (types) {
case Drivers::Bma421::DeviceTypes::BMA421:
this->deviceType = DeviceTypes::BMA421;
break;
case Drivers::Bma421::DeviceTypes::BMA425:
this->deviceType = DeviceTypes::BMA425;
break;
default:
this->deviceType = DeviceTypes::Unknown;
break;
}
}
void MotionController::Restore(uint32_t carrySteps, uint32_t carryTripSteps) {
this->carrySteps = carrySteps;
SetSteps(Days::Today, carrySteps);
currentTripSteps = carryTripSteps;
}