Update apps

This commit is contained in:
Willy-JL
2023-08-30 18:59:32 +02:00
parent 160ab755a2
commit ee37769ee2
308 changed files with 2314 additions and 801 deletions

View File

@@ -6,6 +6,6 @@ App(
stack_size=10 * 1024,
fap_category="GPIO",
fap_icon="mouse_10px.png",
fap_version="0.6",
fap_version="0.8",
sources=["*.c", "*.cc"],
)

View File

@@ -12,178 +12,219 @@
static const float CURSOR_SPEED = 1024.0 / (M_PI / 4);
static const float STABILIZE_BIAS = 16.0;
float g_yaw = 0;
float g_pitch = 0;
float g_dYaw = 0;
float g_dPitch = 0;
bool firstRead = true;
bool stabilize = true;
CalibrationData calibration;
cardboard::OrientationTracker tracker(10000000l); // 10 ms / 100 Hz
uint64_t ippms, ippms2;
class TrackingState {
private:
float yaw;
float pitch;
float dYaw;
float dPitch;
bool firstRead;
bool stabilize;
CalibrationData calibration;
cardboard::OrientationTracker tracker;
uint64_t ippus, ippus2;
static inline float clamp(float val)
{
while (val <= -M_PI) {
val += 2 * M_PI;
}
while (val >= M_PI) {
val -= 2 * M_PI;
}
return val;
}
static inline float highpass(float oldVal, float newVal)
{
if (!stabilize) {
return newVal;
}
float delta = clamp(oldVal - newVal);
float alpha = (float) std::max(0.0, 1 - std::pow(std::fabs(delta) * CURSOR_SPEED / STABILIZE_BIAS, 3.0));
return newVal + alpha * delta;
}
void sendCurrentState(MouseMoveCallback mouse_move, void *context)
{
float dX = g_dYaw * CURSOR_SPEED;
float dY = g_dPitch * CURSOR_SPEED;
// Scale the shift down to fit the protocol.
if (dX > 127) {
dY *= 127.0 / dX;
dX = 127;
}
if (dX < -127) {
dY *= -127.0 / dX;
dX = -127;
}
if (dY > 127) {
dX *= 127.0 / dY;
dY = 127;
}
if (dY < -127) {
dX *= -127.0 / dY;
dY = -127;
private:
float clamp(float val) {
while (val <= -M_PI) {
val += 2 * M_PI;
}
while (val >= M_PI) {
val -= 2 * M_PI;
}
return val;
}
const int8_t x = (int8_t)std::floor(dX + 0.5);
const int8_t y = (int8_t)std::floor(dY + 0.5);
mouse_move(x, y, context);
// Only subtract the part of the error that was already sent.
if (x != 0) {
g_dYaw -= x / CURSOR_SPEED;
}
if (y != 0) {
g_dPitch -= y / CURSOR_SPEED;
}
}
void onOrientation(cardboard::Vector4& quaternion)
{
float q1 = quaternion[0]; // X * sin(T/2)
float q2 = quaternion[1]; // Y * sin(T/2)
float q3 = quaternion[2]; // Z * sin(T/2)
float q0 = quaternion[3]; // cos(T/2)
float yaw = std::atan2(2 * (q0 * q3 - q1 * q2), (1 - 2 * (q1 * q1 + q3 * q3)));
float pitch = std::asin(2 * (q0 * q1 + q2 * q3));
// float roll = std::atan2(2 * (q0 * q2 - q1 * q3), (1 - 2 * (q1 * q1 + q2 * q2)));
if (yaw == NAN || pitch == NAN) {
// NaN case, skip it
return;
float highpass(float oldVal, float newVal) {
if (!stabilize) {
return newVal;
}
float delta = clamp(oldVal - newVal);
float alpha = (float) std::max(0.0, 1 - std::pow(std::fabs(delta) * CURSOR_SPEED / STABILIZE_BIAS, 3.0));
return newVal + alpha * delta;
}
if (firstRead) {
g_yaw = yaw;
g_pitch = pitch;
firstRead = false;
} else {
const float newYaw = highpass(g_yaw, yaw);
const float newPitch = highpass(g_pitch, pitch);
void sendCurrentState(MouseMoveCallback mouse_move, void *context) {
float dX = dYaw * CURSOR_SPEED;
float dY = dPitch * CURSOR_SPEED;
float dYaw = clamp(g_yaw - newYaw);
float dPitch = g_pitch - newPitch;
g_yaw = newYaw;
g_pitch = newPitch;
// Scale the shift down to fit the protocol.
if (dX > 127) {
dY *= 127.0 / dX;
dX = 127;
}
if (dX < -127) {
dY *= -127.0 / dX;
dX = -127;
}
if (dY > 127) {
dX *= 127.0 / dY;
dY = 127;
}
if (dY < -127) {
dX *= -127.0 / dY;
dY = -127;
}
// Accumulate the error locally.
g_dYaw += dYaw;
g_dPitch += dPitch;
const int8_t x = (int8_t)std::floor(dX + 0.5);
const int8_t y = (int8_t)std::floor(dY + 0.5);
mouse_move(x, y, context);
// Only subtract the part of the error that was already sent.
if (x != 0) {
dYaw -= x / CURSOR_SPEED;
}
if (y != 0) {
dPitch -= y / CURSOR_SPEED;
}
}
}
void onOrientation(cardboard::Vector4& quaternion) {
float q1 = quaternion[0]; // X * sin(T/2)
float q2 = quaternion[1]; // Y * sin(T/2)
float q3 = quaternion[2]; // Z * sin(T/2)
float q0 = quaternion[3]; // cos(T/2)
float yaw = std::atan2(2 * (q0 * q3 - q1 * q2), (1 - 2 * (q1 * q1 + q3 * q3)));
float pitch = std::asin(2 * (q0 * q1 + q2 * q3));
// float roll = std::atan2(2 * (q0 * q2 - q1 * q3), (1 - 2 * (q1 * q1 + q2 * q2)));
if (yaw == NAN || pitch == NAN) {
// NaN case, skip it
return;
}
if (firstRead) {
this->yaw = yaw;
this->pitch = pitch;
firstRead = false;
} else {
const float newYaw = highpass(this->yaw, yaw);
const float newPitch = highpass(this->pitch, pitch);
float dYaw = clamp(this->yaw - newYaw);
float dPitch = this->pitch - newPitch;
this->yaw = newYaw;
this->pitch = newPitch;
// Accumulate the error locally.
this->dYaw += dYaw;
this->dPitch += dPitch;
}
}
public:
TrackingState()
: yaw(0)
, pitch(0)
, dYaw(0)
, dPitch(0)
, firstRead(true)
, stabilize(true)
, tracker(10000000l) { // 10 ms / 100 Hz
ippus = furi_hal_cortex_instructions_per_microsecond();
ippus2 = ippus / 2;
}
void beginCalibration() {
calibration.reset();
}
bool stepCalibration() {
if (calibration.isComplete())
return true;
double vec[6];
if (imu_read(vec) & GYR_DATA_READY) {
cardboard::Vector3 data(vec[3], vec[4], vec[5]);
furi_delay_ms(9); // Artificially limit to ~100Hz
return calibration.add(data);
}
return false;
}
void saveCalibration() {
CalibrationMedian store;
cardboard::Vector3 median = calibration.getMedian();
store.x = median[0];
store.y = median[1];
store.z = median[2];
CALIBRATION_DATA_SAVE(&store);
}
void loadCalibration() {
CalibrationMedian store;
cardboard::Vector3 median = calibration.getMedian();
if (CALIBRATION_DATA_LOAD(&store)) {
median[0] = store.x;
median[1] = store.y;
median[2] = store.z;
}
tracker.SetCalibration(median);
}
void beginTracking() {
loadCalibration();
tracker.Resume();
}
void stepTracking(MouseMoveCallback mouse_move, void *context) {
double vec[6];
int ret = imu_read(vec);
if (ret != 0) {
uint64_t t = (DWT->CYCCNT * 1000llu + ippus2) / ippus;
if (ret & ACC_DATA_READY) {
cardboard::AccelerometerData adata
= { .system_timestamp = t, .sensor_timestamp_ns = t,
.data = cardboard::Vector3(vec[0], vec[1], vec[2]) };
tracker.OnAccelerometerData(adata);
}
if (ret & GYR_DATA_READY) {
cardboard::GyroscopeData gdata
= { .system_timestamp = t, .sensor_timestamp_ns = t,
.data = cardboard::Vector3(vec[3], vec[4], vec[5]) };
cardboard::Vector4 pose = tracker.OnGyroscopeData(gdata);
onOrientation(pose);
sendCurrentState(mouse_move, context);
}
}
}
void stopTracking() {
tracker.Pause();
}
};
static TrackingState g_state;
extern "C" {
void calibration_begin() {
calibration.reset();
g_state.beginCalibration();
FURI_LOG_I(TAG, "Calibrating");
}
bool calibration_step() {
if (calibration.isComplete())
return true;
double vec[6];
if (imu_read(vec) & GYR_DATA_READY) {
cardboard::Vector3 data(vec[3], vec[4], vec[5]);
furi_delay_ms(9); // Artificially limit to ~100Hz
return calibration.add(data);
}
return false;
return g_state.stepCalibration();
}
void calibration_end() {
CalibrationMedian store;
cardboard::Vector3 median = calibration.getMedian();
store.x = median[0];
store.y = median[1];
store.z = median[2];
CALIBRATION_DATA_SAVE(&store);
g_state.saveCalibration();
}
void tracking_begin() {
CalibrationMedian store;
cardboard::Vector3 median = calibration.getMedian();
if (CALIBRATION_DATA_LOAD(&store)) {
median[0] = store.x;
median[1] = store.y;
median[2] = store.z;
}
ippms = furi_hal_cortex_instructions_per_microsecond();
ippms2 = ippms / 2;
tracker.SetCalibration(median);
tracker.Resume();
g_state.beginTracking();
}
void tracking_step(MouseMoveCallback mouse_move, void *context) {
double vec[6];
int ret = imu_read(vec);
if (ret != 0) {
uint64_t t = (DWT->CYCCNT * 1000llu + ippms2) / ippms;
if (ret & ACC_DATA_READY) {
cardboard::AccelerometerData adata
= { .system_timestamp = t, .sensor_timestamp_ns = t,
.data = cardboard::Vector3(vec[0], vec[1], vec[2]) };
tracker.OnAccelerometerData(adata);
}
if (ret & GYR_DATA_READY) {
cardboard::GyroscopeData gdata
= { .system_timestamp = t, .sensor_timestamp_ns = t,
.data = cardboard::Vector3(vec[3], vec[4], vec[5]) };
cardboard::Vector4 pose = tracker.OnGyroscopeData(gdata);
onOrientation(pose);
sendCurrentState(mouse_move, context);
}
}
g_state.stepTracking(mouse_move, context);
}
void tracking_end() {
tracker.Pause();
g_state.stopTracking();
}
}

View File

@@ -89,7 +89,7 @@ Vector4 OrientationTracker::OnGyroscopeData(const GyroscopeData& event)
sensor_fusion_->ProcessGyroscopeSample(data);
return OrientationTracker::GetPose(data.sensor_timestamp_ns + sampling_period_ns_);
return GetPose(data.sensor_timestamp_ns + sampling_period_ns_);
}
} // namespace cardboard

View File

@@ -59,13 +59,14 @@ namespace {
// angle = norm(a)
// axis = a.normalized()
// If norm(a) == 0, it returns an identity rotation.
static inline Rotation RotationFromVector(const Vector3& a)
static inline void RotationFromVector(const Vector3& a, Rotation& r)
{
const double norm_a = Length(a);
if (norm_a < kEpsilon) {
return Rotation::Identity();
r = Rotation::Identity();
return;
}
return Rotation::FromAxisAndAngle(a / norm_a, norm_a);
r = Rotation::FromAxisAndAngle(a / norm_a, norm_a);
}
} // namespace
@@ -199,7 +200,8 @@ void SensorFusionEkf::ComputeMeasurementJacobian()
Vector3 delta = Vector3::Zero();
delta[dof] = kFiniteDifferencingEpsilon;
const Rotation epsilon_rotation = RotationFromVector(delta);
Rotation epsilon_rotation;
RotationFromVector(delta, epsilon_rotation);
const Vector3 delta_rotation
= ComputeInnovation(epsilon_rotation * current_state_.sensor_from_start_rotation);
@@ -263,7 +265,8 @@ void SensorFusionEkf::ProcessAccelerometerSample(const AccelerometerData& sample
* state_covariance_;
// Updates pose and associate covariance matrix.
const Rotation rotation_from_state_update = RotationFromVector(state_update_);
Rotation rotation_from_state_update;
RotationFromVector(state_update_, rotation_from_state_update);
current_state_.sensor_from_start_rotation
= rotation_from_state_update * current_state_.sensor_from_start_rotation;

View File

@@ -132,6 +132,11 @@ void bt_mouse_connection_status_changed_callback(BtStatus status, void* context)
BtMouse* bt_mouse = context;
bt_mouse->connected = (status == BtStatusConnected);
if(!bt_mouse->notifications) {
tracking_end();
return;
}
if(bt_mouse->connected) {
notification_internal_message(bt_mouse->notifications, &sequence_set_blue_255);
tracking_begin();
@@ -140,9 +145,6 @@ void bt_mouse_connection_status_changed_callback(BtStatus status, void* context)
tracking_end();
notification_internal_message(bt_mouse->notifications, &sequence_reset_blue);
}
//with_view_model(
// bt_mouse->view, void * model, { model->connected = connected; }, true);
}
bool bt_mouse_move(int8_t dx, int8_t dy, void* context) {
@@ -160,46 +162,6 @@ bool bt_mouse_move(int8_t dx, int8_t dy, void* context) {
return true;
}
void bt_mouse_enter_callback(void* context) {
furi_assert(context);
BtMouse* bt_mouse = context;
bt_mouse->bt = furi_record_open(RECORD_BT);
bt_mouse->notifications = furi_record_open(RECORD_NOTIFICATION);
bt_set_status_changed_callback(
bt_mouse->bt, bt_mouse_connection_status_changed_callback, bt_mouse);
furi_assert(bt_set_profile(bt_mouse->bt, BtProfileHidKeyboard));
furi_hal_bt_start_advertising();
}
bool bt_mouse_custom_callback(uint32_t event, void* context) {
UNUSED(event);
furi_assert(context);
BtMouse* bt_mouse = context;
tracking_step(bt_mouse_move, context);
furi_delay_ms(3); // Magic! Removing this will break the buttons
view_dispatcher_send_custom_event(bt_mouse->view_dispatcher, 0);
return true;
}
void bt_mouse_exit_callback(void* context) {
furi_assert(context);
BtMouse* bt_mouse = context;
tracking_end();
notification_internal_message(bt_mouse->notifications, &sequence_reset_blue);
furi_hal_bt_stop_advertising();
bt_set_profile(bt_mouse->bt, BtProfileSerial);
furi_record_close(RECORD_NOTIFICATION);
bt_mouse->notifications = NULL;
furi_record_close(RECORD_BT);
bt_mouse->bt = NULL;
}
static int8_t clamp(int t) {
if(t < -128) {
return -128;
@@ -279,6 +241,50 @@ void bt_mouse_thread_stop(BtMouse* bt_mouse) {
furi_thread_join(bt_mouse->thread);
furi_thread_free(bt_mouse->thread);
furi_mutex_free(bt_mouse->mutex);
bt_mouse->mutex = NULL;
bt_mouse->thread = NULL;
}
void bt_mouse_enter_callback(void* context) {
furi_assert(context);
BtMouse* bt_mouse = context;
bt_mouse->bt = furi_record_open(RECORD_BT);
bt_mouse->notifications = furi_record_open(RECORD_NOTIFICATION);
bt_set_status_changed_callback(
bt_mouse->bt, bt_mouse_connection_status_changed_callback, bt_mouse);
furi_assert(bt_set_profile(bt_mouse->bt, BtProfileHidKeyboard));
furi_hal_bt_start_advertising();
bt_mouse_thread_start(bt_mouse);
}
bool bt_mouse_custom_callback(uint32_t event, void* context) {
UNUSED(event);
furi_assert(context);
BtMouse* bt_mouse = context;
tracking_step(bt_mouse_move, context);
furi_delay_ms(3); // Magic! Removing this will break the buttons
view_dispatcher_send_custom_event(bt_mouse->view_dispatcher, 0);
return true;
}
void bt_mouse_exit_callback(void* context) {
furi_assert(context);
BtMouse* bt_mouse = context;
bt_mouse_thread_stop(bt_mouse);
tracking_end();
notification_internal_message(bt_mouse->notifications, &sequence_reset_blue);
furi_hal_bt_stop_advertising();
bt_set_profile(bt_mouse->bt, BtProfileSerial);
furi_record_close(RECORD_NOTIFICATION);
bt_mouse->notifications = NULL;
furi_record_close(RECORD_BT);
bt_mouse->bt = NULL;
}
BtMouse* bt_mouse_alloc(ViewDispatcher* view_dispatcher) {
@@ -293,13 +299,11 @@ BtMouse* bt_mouse_alloc(ViewDispatcher* view_dispatcher) {
view_set_enter_callback(bt_mouse->view, bt_mouse_enter_callback);
view_set_custom_callback(bt_mouse->view, bt_mouse_custom_callback);
view_set_exit_callback(bt_mouse->view, bt_mouse_exit_callback);
bt_mouse_thread_start(bt_mouse);
return bt_mouse;
}
void bt_mouse_free(BtMouse* bt_mouse) {
furi_assert(bt_mouse);
bt_mouse_thread_stop(bt_mouse);
view_free(bt_mouse->view);
free(bt_mouse);
}