main_loop.cc 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. #include "main_loop.h"
  2. #include <furi.h>
  3. #include <furi_hal.h>
  4. #include "imu/imu.h"
  5. #include "orientation_tracker.h"
  6. #include "calibration_data.h"
  7. #define TAG "tracker"
  8. static const float CURSOR_SPEED = 1024.0 / (M_PI / 4);
  9. static const float STABILIZE_BIAS = 16.0;
  10. class TrackingState {
  11. private:
  12. float yaw;
  13. float pitch;
  14. float dYaw;
  15. float dPitch;
  16. bool firstRead;
  17. bool stabilize;
  18. CalibrationData calibration;
  19. cardboard::OrientationTracker tracker;
  20. uint64_t ippus, ippus2;
  21. private:
  22. float clamp(float val) {
  23. while (val <= -M_PI) {
  24. val += 2 * M_PI;
  25. }
  26. while (val >= M_PI) {
  27. val -= 2 * M_PI;
  28. }
  29. return val;
  30. }
  31. float highpass(float oldVal, float newVal) {
  32. if (!stabilize) {
  33. return newVal;
  34. }
  35. float delta = clamp(oldVal - newVal);
  36. float alpha = (float) std::max(0.0, 1 - std::pow(std::fabs(delta) * CURSOR_SPEED / STABILIZE_BIAS, 3.0));
  37. return newVal + alpha * delta;
  38. }
  39. void sendCurrentState(MouseMoveCallback mouse_move, void *context) {
  40. float dX = dYaw * CURSOR_SPEED;
  41. float dY = dPitch * CURSOR_SPEED;
  42. // Scale the shift down to fit the protocol.
  43. if (dX > 127) {
  44. dY *= 127.0 / dX;
  45. dX = 127;
  46. }
  47. if (dX < -127) {
  48. dY *= -127.0 / dX;
  49. dX = -127;
  50. }
  51. if (dY > 127) {
  52. dX *= 127.0 / dY;
  53. dY = 127;
  54. }
  55. if (dY < -127) {
  56. dX *= -127.0 / dY;
  57. dY = -127;
  58. }
  59. const int8_t x = (int8_t)std::floor(dX + 0.5);
  60. const int8_t y = (int8_t)std::floor(dY + 0.5);
  61. mouse_move(x, y, context);
  62. // Only subtract the part of the error that was already sent.
  63. if (x != 0) {
  64. dYaw -= x / CURSOR_SPEED;
  65. }
  66. if (y != 0) {
  67. dPitch -= y / CURSOR_SPEED;
  68. }
  69. }
  70. void onOrientation(cardboard::Vector4& quaternion) {
  71. float q1 = quaternion[0]; // X * sin(T/2)
  72. float q2 = quaternion[1]; // Y * sin(T/2)
  73. float q3 = quaternion[2]; // Z * sin(T/2)
  74. float q0 = quaternion[3]; // cos(T/2)
  75. float yaw = std::atan2(2 * (q0 * q3 - q1 * q2), (1 - 2 * (q1 * q1 + q3 * q3)));
  76. float pitch = std::asin(2 * (q0 * q1 + q2 * q3));
  77. // float roll = std::atan2(2 * (q0 * q2 - q1 * q3), (1 - 2 * (q1 * q1 + q2 * q2)));
  78. if (yaw == NAN || pitch == NAN) {
  79. // NaN case, skip it
  80. return;
  81. }
  82. if (firstRead) {
  83. this->yaw = yaw;
  84. this->pitch = pitch;
  85. firstRead = false;
  86. } else {
  87. const float newYaw = highpass(this->yaw, yaw);
  88. const float newPitch = highpass(this->pitch, pitch);
  89. float dYaw = clamp(this->yaw - newYaw);
  90. float dPitch = this->pitch - newPitch;
  91. this->yaw = newYaw;
  92. this->pitch = newPitch;
  93. // Accumulate the error locally.
  94. this->dYaw += dYaw;
  95. this->dPitch += dPitch;
  96. }
  97. }
  98. public:
  99. TrackingState()
  100. : yaw(0)
  101. , pitch(0)
  102. , dYaw(0)
  103. , dPitch(0)
  104. , firstRead(true)
  105. , stabilize(true)
  106. , tracker(10000000l) { // 10 ms / 100 Hz
  107. ippus = furi_hal_cortex_instructions_per_microsecond();
  108. ippus2 = ippus / 2;
  109. }
  110. void beginCalibration() {
  111. calibration.reset();
  112. }
  113. bool stepCalibration() {
  114. if (calibration.isComplete())
  115. return true;
  116. double vec[6];
  117. if (imu_read(vec) & GYR_DATA_READY) {
  118. cardboard::Vector3 data(vec[3], vec[4], vec[5]);
  119. furi_delay_ms(9); // Artificially limit to ~100Hz
  120. return calibration.add(data);
  121. }
  122. return false;
  123. }
  124. void saveCalibration() {
  125. CalibrationMedian store;
  126. cardboard::Vector3 median = calibration.getMedian();
  127. store.x = median[0];
  128. store.y = median[1];
  129. store.z = median[2];
  130. CALIBRATION_DATA_SAVE(&store);
  131. }
  132. void loadCalibration() {
  133. CalibrationMedian store;
  134. cardboard::Vector3 median = calibration.getMedian();
  135. if (CALIBRATION_DATA_LOAD(&store)) {
  136. median[0] = store.x;
  137. median[1] = store.y;
  138. median[2] = store.z;
  139. }
  140. tracker.SetCalibration(median);
  141. }
  142. void beginTracking() {
  143. loadCalibration();
  144. tracker.Resume();
  145. }
  146. void stepTracking(MouseMoveCallback mouse_move, void *context) {
  147. double vec[6];
  148. int ret = imu_read(vec);
  149. if (ret != 0) {
  150. uint64_t t = (DWT->CYCCNT * 1000llu + ippus2) / ippus;
  151. if (ret & ACC_DATA_READY) {
  152. cardboard::AccelerometerData adata
  153. = { .system_timestamp = t, .sensor_timestamp_ns = t,
  154. .data = cardboard::Vector3(vec[0], vec[1], vec[2]) };
  155. tracker.OnAccelerometerData(adata);
  156. }
  157. if (ret & GYR_DATA_READY) {
  158. cardboard::GyroscopeData gdata
  159. = { .system_timestamp = t, .sensor_timestamp_ns = t,
  160. .data = cardboard::Vector3(vec[3], vec[4], vec[5]) };
  161. cardboard::Vector4 pose = tracker.OnGyroscopeData(gdata);
  162. onOrientation(pose);
  163. sendCurrentState(mouse_move, context);
  164. }
  165. }
  166. }
  167. void stopTracking() {
  168. tracker.Pause();
  169. }
  170. };
  171. static TrackingState g_state;
  172. extern "C" {
  173. void calibration_begin() {
  174. g_state.beginCalibration();
  175. FURI_LOG_I(TAG, "Calibrating");
  176. }
  177. bool calibration_step() {
  178. return g_state.stepCalibration();
  179. }
  180. void calibration_end() {
  181. g_state.saveCalibration();
  182. }
  183. void tracking_begin() {
  184. g_state.beginTracking();
  185. }
  186. void tracking_step(MouseMoveCallback mouse_move, void *context) {
  187. g_state.stepTracking(mouse_move, context);
  188. }
  189. void tracking_end() {
  190. g_state.stopTracking();
  191. }
  192. }