main_loop.cc 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  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. float g_yaw = 0;
  11. float g_pitch = 0;
  12. float g_dYaw = 0;
  13. float g_dPitch = 0;
  14. bool firstRead = true;
  15. bool stabilize = true;
  16. CalibrationData calibration;
  17. cardboard::OrientationTracker tracker(10000000l); // 10 ms / 100 Hz
  18. uint64_t ippms, ippms2;
  19. static inline float clamp(float val)
  20. {
  21. while (val <= -M_PI) {
  22. val += 2 * M_PI;
  23. }
  24. while (val >= M_PI) {
  25. val -= 2 * M_PI;
  26. }
  27. return val;
  28. }
  29. static inline float highpass(float oldVal, float newVal)
  30. {
  31. if (!stabilize) {
  32. return newVal;
  33. }
  34. float delta = clamp(oldVal - newVal);
  35. float alpha = (float) std::max(0.0, 1 - std::pow(std::fabs(delta) * CURSOR_SPEED / STABILIZE_BIAS, 3.0));
  36. return newVal + alpha * delta;
  37. }
  38. void sendCurrentState(MouseMoveCallback mouse_move, void *context)
  39. {
  40. float dX = g_dYaw * CURSOR_SPEED;
  41. float dY = g_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. g_dYaw -= x / CURSOR_SPEED;
  65. }
  66. if (y != 0) {
  67. g_dPitch -= y / CURSOR_SPEED;
  68. }
  69. }
  70. void onOrientation(cardboard::Vector4& quaternion)
  71. {
  72. float q1 = quaternion[0]; // X * sin(T/2)
  73. float q2 = quaternion[1]; // Y * sin(T/2)
  74. float q3 = quaternion[2]; // Z * sin(T/2)
  75. float q0 = quaternion[3]; // cos(T/2)
  76. float yaw = std::atan2(2 * (q0 * q3 - q1 * q2), (1 - 2 * (q1 * q1 + q3 * q3)));
  77. float pitch = std::asin(2 * (q0 * q1 + q2 * q3));
  78. // float roll = std::atan2(2 * (q0 * q2 - q1 * q3), (1 - 2 * (q1 * q1 + q2 * q2)));
  79. if (yaw == NAN || pitch == NAN) {
  80. // NaN case, skip it
  81. return;
  82. }
  83. if (firstRead) {
  84. g_yaw = yaw;
  85. g_pitch = pitch;
  86. firstRead = false;
  87. } else {
  88. const float newYaw = highpass(g_yaw, yaw);
  89. const float newPitch = highpass(g_pitch, pitch);
  90. float dYaw = clamp(g_yaw - newYaw);
  91. float dPitch = g_pitch - newPitch;
  92. g_yaw = newYaw;
  93. g_pitch = newPitch;
  94. // Accumulate the error locally.
  95. g_dYaw += dYaw;
  96. g_dPitch += dPitch;
  97. }
  98. }
  99. extern "C" {
  100. void calibration_begin() {
  101. calibration.reset();
  102. FURI_LOG_I(TAG, "Calibrating");
  103. }
  104. bool calibration_step() {
  105. if (calibration.isComplete())
  106. return true;
  107. double vec[6];
  108. if (imu_read(vec) & GYR_DATA_READY) {
  109. cardboard::Vector3 data(vec[3], vec[4], vec[5]);
  110. furi_delay_ms(9); // Artificially limit to ~100Hz
  111. return calibration.add(data);
  112. }
  113. return false;
  114. }
  115. void calibration_end() {
  116. CalibrationMedian store;
  117. cardboard::Vector3 median = calibration.getMedian();
  118. store.x = median[0];
  119. store.y = median[1];
  120. store.z = median[2];
  121. CALIBRATION_DATA_SAVE(&store);
  122. }
  123. void tracking_begin() {
  124. CalibrationMedian store;
  125. cardboard::Vector3 median = calibration.getMedian();
  126. if (CALIBRATION_DATA_LOAD(&store)) {
  127. median[0] = store.x;
  128. median[1] = store.y;
  129. median[2] = store.z;
  130. }
  131. ippms = furi_hal_cortex_instructions_per_microsecond();
  132. ippms2 = ippms / 2;
  133. tracker.SetCalibration(median);
  134. tracker.Resume();
  135. }
  136. void tracking_step(MouseMoveCallback mouse_move, void *context) {
  137. double vec[6];
  138. int ret = imu_read(vec);
  139. if (ret != 0) {
  140. uint64_t t = (DWT->CYCCNT * 1000llu + ippms2) / ippms;
  141. if (ret & ACC_DATA_READY) {
  142. cardboard::AccelerometerData adata
  143. = { .system_timestamp = t, .sensor_timestamp_ns = t,
  144. .data = cardboard::Vector3(vec[0], vec[1], vec[2]) };
  145. tracker.OnAccelerometerData(adata);
  146. }
  147. if (ret & GYR_DATA_READY) {
  148. cardboard::GyroscopeData gdata
  149. = { .system_timestamp = t, .sensor_timestamp_ns = t,
  150. .data = cardboard::Vector3(vec[3], vec[4], vec[5]) };
  151. cardboard::Vector4 pose = tracker.OnGyroscopeData(gdata);
  152. onOrientation(pose);
  153. sendCurrentState(mouse_move, context);
  154. }
  155. }
  156. }
  157. void tracking_end() {
  158. tracker.Pause();
  159. }
  160. }