Skip to content

Commit 0553ae1

Browse files
committed
Initial commit
1 parent 3930cc1 commit 0553ae1

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+24229
-0
lines changed

air_mouse.c

+143
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
#include "air_mouse.h"
2+
3+
#include <furi.h>
4+
#include <dolphin/dolphin.h>
5+
6+
#include "tracking/imu/imu.h"
7+
8+
#define TAG "AirMouseApp"
9+
10+
enum AirMouseSubmenuIndex {
11+
AirMouseSubmenuIndexBtMouse,
12+
AirMouseSubmenuIndexUsbMouse,
13+
AirMouseSubmenuIndexCalibration,
14+
};
15+
16+
void air_mouse_submenu_callback(void* context, uint32_t index) {
17+
furi_assert(context);
18+
AirMouse* app = context;
19+
if (index == AirMouseSubmenuIndexBtMouse) {
20+
app->view_id = AirMouseViewBtMouse;
21+
view_dispatcher_switch_to_view(app->view_dispatcher, AirMouseViewBtMouse);
22+
} else if (index == AirMouseSubmenuIndexUsbMouse) {
23+
app->view_id = AirMouseViewUsbMouse;
24+
view_dispatcher_switch_to_view(app->view_dispatcher, AirMouseViewUsbMouse);
25+
} else if (index == AirMouseSubmenuIndexCalibration) {
26+
app->view_id = AirMouseViewCalibration;
27+
view_dispatcher_switch_to_view(app->view_dispatcher, AirMouseViewCalibration);
28+
}
29+
}
30+
31+
void air_mouse_dialog_callback(DialogExResult result, void* context) {
32+
furi_assert(context);
33+
AirMouse* app = context;
34+
if (result == DialogExResultLeft) {
35+
view_dispatcher_switch_to_view(app->view_dispatcher, VIEW_NONE); // Exit
36+
} else if (result == DialogExResultRight) {
37+
view_dispatcher_switch_to_view(app->view_dispatcher, app->view_id); // Show last view
38+
} else if (result == DialogExResultCenter) {
39+
view_dispatcher_switch_to_view(app->view_dispatcher, AirMouseViewSubmenu); // Menu
40+
}
41+
}
42+
43+
uint32_t air_mouse_exit_confirm_view(void* context) {
44+
UNUSED(context);
45+
return AirMouseViewExitConfirm;
46+
}
47+
48+
uint32_t air_mouse_exit(void* context) {
49+
UNUSED(context);
50+
return VIEW_NONE;
51+
}
52+
53+
AirMouse* air_mouse_app_alloc() {
54+
AirMouse* app = malloc(sizeof(AirMouse));
55+
56+
// Gui
57+
app->gui = furi_record_open(RECORD_GUI);
58+
59+
// View dispatcher
60+
app->view_dispatcher = view_dispatcher_alloc();
61+
view_dispatcher_enable_queue(app->view_dispatcher);
62+
view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
63+
64+
// Submenu view
65+
app->submenu = submenu_alloc();
66+
submenu_add_item(app->submenu, "Bluetooth", AirMouseSubmenuIndexBtMouse, air_mouse_submenu_callback, app);
67+
submenu_add_item(app->submenu, "USB", AirMouseSubmenuIndexUsbMouse, air_mouse_submenu_callback, app);
68+
submenu_add_item(app->submenu, "Calibration", AirMouseSubmenuIndexCalibration, air_mouse_submenu_callback, app);
69+
view_set_previous_callback(submenu_get_view(app->submenu), air_mouse_exit);
70+
view_dispatcher_add_view(app->view_dispatcher, AirMouseViewSubmenu, submenu_get_view(app->submenu));
71+
72+
// Dialog view
73+
app->dialog = dialog_ex_alloc();
74+
dialog_ex_set_result_callback(app->dialog, air_mouse_dialog_callback);
75+
dialog_ex_set_context(app->dialog, app);
76+
dialog_ex_set_left_button_text(app->dialog, "Exit");
77+
dialog_ex_set_right_button_text(app->dialog, "Stay");
78+
dialog_ex_set_center_button_text(app->dialog, "Menu");
79+
dialog_ex_set_header(app->dialog, "Close Current App?", 16, 12, AlignLeft, AlignTop);
80+
view_dispatcher_add_view(app->view_dispatcher, AirMouseViewExitConfirm, dialog_ex_get_view(app->dialog));
81+
82+
// Bluetooth view
83+
app->bt_mouse = bt_mouse_alloc(app->view_dispatcher);
84+
view_set_previous_callback(bt_mouse_get_view(app->bt_mouse), air_mouse_exit_confirm_view);
85+
view_dispatcher_add_view(app->view_dispatcher, AirMouseViewBtMouse, bt_mouse_get_view(app->bt_mouse));
86+
87+
// USB view
88+
app->usb_mouse = usb_mouse_alloc(app->view_dispatcher);
89+
view_set_previous_callback(usb_mouse_get_view(app->usb_mouse), air_mouse_exit_confirm_view);
90+
view_dispatcher_add_view(app->view_dispatcher, AirMouseViewUsbMouse, usb_mouse_get_view(app->usb_mouse));
91+
92+
// Calibration view
93+
app->calibration = calibration_alloc(app->view_dispatcher);
94+
view_set_previous_callback(calibration_get_view(app->calibration), air_mouse_exit_confirm_view);
95+
view_dispatcher_add_view(app->view_dispatcher, AirMouseViewCalibration, calibration_get_view(app->calibration));
96+
97+
app->view_id = AirMouseViewSubmenu;
98+
view_dispatcher_switch_to_view(app->view_dispatcher, app->view_id);
99+
100+
return app;
101+
}
102+
103+
void air_mouse_app_free(AirMouse* app) {
104+
furi_assert(app);
105+
106+
// Free views
107+
view_dispatcher_remove_view(app->view_dispatcher, AirMouseViewSubmenu);
108+
submenu_free(app->submenu);
109+
view_dispatcher_remove_view(app->view_dispatcher, AirMouseViewExitConfirm);
110+
dialog_ex_free(app->dialog);
111+
view_dispatcher_remove_view(app->view_dispatcher, AirMouseViewBtMouse);
112+
bt_mouse_free(app->bt_mouse);
113+
view_dispatcher_remove_view(app->view_dispatcher, AirMouseViewUsbMouse);
114+
usb_mouse_free(app->usb_mouse);
115+
view_dispatcher_remove_view(app->view_dispatcher, AirMouseViewCalibration);
116+
calibration_free(app->calibration);
117+
view_dispatcher_free(app->view_dispatcher);
118+
119+
// Close records
120+
furi_record_close(RECORD_GUI);
121+
app->gui = NULL;
122+
123+
// Free rest
124+
free(app);
125+
}
126+
127+
int32_t air_mouse_app(void* p) {
128+
UNUSED(p);
129+
130+
AirMouse* app = air_mouse_app_alloc();
131+
if (!imu_begin()) {
132+
air_mouse_app_free(app);
133+
return -1;
134+
}
135+
136+
DOLPHIN_DEED(DolphinDeedPluginStart);
137+
view_dispatcher_run(app->view_dispatcher);
138+
139+
imu_end();
140+
air_mouse_app_free(app);
141+
142+
return 0;
143+
}

air_mouse.h

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#pragma once
2+
3+
#include <gui/gui.h>
4+
#include <gui/view.h>
5+
#include <gui/view_dispatcher.h>
6+
#include <gui/modules/submenu.h>
7+
#include <gui/modules/dialog_ex.h>
8+
9+
#include "views/bt_mouse.h"
10+
#include "views/usb_mouse.h"
11+
#include "views/calibration.h"
12+
13+
typedef struct {
14+
Gui* gui;
15+
ViewDispatcher* view_dispatcher;
16+
Submenu* submenu;
17+
DialogEx* dialog;
18+
BtMouse* bt_mouse;
19+
UsbMouse* usb_mouse;
20+
Calibration* calibration;
21+
uint32_t view_id;
22+
} AirMouse;
23+
24+
typedef enum {
25+
AirMouseViewSubmenu,
26+
AirMouseViewBtMouse,
27+
AirMouseViewUsbMouse,
28+
AirMouseViewCalibration,
29+
AirMouseViewExitConfirm,
30+
} AirMouseView;

application.fam

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
App(
2+
appid="air_mouse",
3+
name="Air Mouse",
4+
apptype=FlipperAppType.EXTERNAL,
5+
entry_point="air_mouse_app",
6+
stack_size=10 * 1024,
7+
fap_category="Tools",
8+
icon="A_Plugins_14",
9+
)

tracking/calibration_data.cc

+85
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
#include <furi.h>
2+
#include <furi_hal.h>
3+
4+
#define TAG "tracker"
5+
6+
#include "calibration_data.h"
7+
8+
#include <cmath>
9+
#include <algorithm>
10+
11+
// Student's distribution T value for 95% (two-sided) confidence interval.
12+
static const double Tn = 1.960;
13+
14+
// Number of samples (degrees of freedom) for the corresponding T values.
15+
static const int Nn = 200;
16+
17+
void CalibrationData::reset()
18+
{
19+
complete = false;
20+
count = 0;
21+
sum = Vector::Zero();
22+
sumSq = Vector::Zero();
23+
mean = Vector::Zero();
24+
median = Vector::Zero();
25+
sigma = Vector::Zero();
26+
delta = Vector::Zero();
27+
xData.clear();
28+
yData.clear();
29+
zData.clear();
30+
}
31+
32+
bool CalibrationData::add(Vector& data)
33+
{
34+
if (complete) {
35+
return true;
36+
}
37+
38+
xData.push_back(data[0]);
39+
yData.push_back(data[1]);
40+
zData.push_back(data[2]);
41+
42+
sum += data;
43+
sumSq += data * data;
44+
count++;
45+
46+
if (count >= Nn) {
47+
calcDelta();
48+
complete = true;
49+
}
50+
51+
return complete;
52+
}
53+
54+
static inline double medianOf(std::vector<double>& list)
55+
{
56+
std::sort(list.begin(), list.end());
57+
int count = list.size();
58+
int middle = count / 2;
59+
return (count % 2 == 1) ? list[middle] : (list[middle - 1] + list[middle]) / 2.0l;
60+
}
61+
62+
void CalibrationData::calcDelta()
63+
{
64+
median.Set(medianOf(xData), medianOf(yData), medianOf(zData));
65+
66+
mean = sum / count;
67+
Vector m2 = mean * mean;
68+
Vector d = sumSq / count - m2;
69+
Vector s2 = (d * count) / (count - 1);
70+
sigma = Vector(std::sqrt(d[0]), std::sqrt(d[1]), std::sqrt(d[2]));
71+
Vector s = Vector(std::sqrt(s2[0]), std::sqrt(s2[1]), std::sqrt(s2[2]));
72+
delta = s * Tn / std::sqrt((double)count);
73+
Vector low = mean - delta;
74+
Vector high = mean + delta;
75+
76+
FURI_LOG_I(TAG,
77+
"M[x] = { %f ... %f } // median = %f // avg = %f // delta = %f // sigma = %f",
78+
low[0], high[0], median[0], mean[0], delta[0], sigma[0]);
79+
FURI_LOG_I(TAG,
80+
"M[y] = { %f ... %f } // median = %f // avg = %f // delta = %f // sigma = %f",
81+
low[1], high[1], median[1], mean[1], delta[1], sigma[1]);
82+
FURI_LOG_I(TAG,
83+
"M[z] = { %f ... %f } // median = %f // avg = %f // delta = %f // sigma = %f",
84+
low[2], high[2], median[2], mean[2], delta[2], sigma[2]);
85+
}

tracking/calibration_data.h

+108
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
#pragma once
2+
3+
#include <toolbox/saved_struct.h>
4+
#include <storage/storage.h>
5+
#include <vector>
6+
7+
#include "util/vector.h"
8+
9+
#define CALIBRATION_DATA_VER (1)
10+
#define CALIBRATION_DATA_FILE_NAME ".calibration.data"
11+
#define CALIBRATION_DATA_PATH INT_PATH(CALIBRATION_DATA_FILE_NAME)
12+
#define CALIBRATION_DATA_MAGIC (0x23)
13+
14+
#define CALIBRATION_DATA_SAVE(x) \
15+
saved_struct_save( \
16+
CALIBRATION_DATA_PATH, \
17+
(x), \
18+
sizeof(CalibrationMedian), \
19+
CALIBRATION_DATA_MAGIC, \
20+
CALIBRATION_DATA_VER)
21+
22+
#define CALIBRATION_DATA_LOAD(x) \
23+
saved_struct_load( \
24+
CALIBRATION_DATA_PATH, \
25+
(x), \
26+
sizeof(CalibrationMedian), \
27+
CALIBRATION_DATA_MAGIC, \
28+
CALIBRATION_DATA_VER)
29+
30+
typedef struct {
31+
double x;
32+
double y;
33+
double z;
34+
} CalibrationMedian;
35+
36+
typedef cardboard::Vector3 Vector;
37+
38+
/**
39+
* Helper class to gather some stats and store the calibration data. Right now it calculates a lot
40+
* more stats than actually needed. Some of them are used for logging the sensors quality (and
41+
* filing bugs), other may be required in the future, e.g. for bias.
42+
*/
43+
class CalibrationData {
44+
45+
public:
46+
/**
47+
* Check if the sensors were calibrated before.
48+
*
49+
* @return {@code true} if calibration data is available, or {@code false} otherwise.
50+
*/
51+
bool isComplete() { return complete; }
52+
53+
/** Prepare to collect new calibration data. */
54+
void reset();
55+
56+
/**
57+
* Retrieve the median gyroscope readings.
58+
*
59+
* @return Three-axis median vector.
60+
*/
61+
Vector getMedian() { return median; }
62+
63+
/**
64+
* Retrieve the mean gyroscope readings.
65+
*
66+
* @return Three-axis mean vector.
67+
*/
68+
Vector getMean() { return mean; }
69+
70+
/**
71+
* Retrieve the standard deviation of gyroscope readings.
72+
*
73+
* @return Three-axis standard deviation vector.
74+
*/
75+
Vector getSigma() { return sigma; }
76+
77+
/**
78+
* Retrieve the confidence interval size of gyroscope readings.
79+
*
80+
* @return Three-axis confidence interval size vector.
81+
*/
82+
Vector getDelta() { return delta; }
83+
84+
/**
85+
* Add a new gyroscope reading to the stats.
86+
*
87+
* @param data gyroscope values vector.
88+
* @return {@code true} if we now have enough data for calibration, or {@code false} otherwise.
89+
*/
90+
bool add(Vector& data);
91+
92+
private:
93+
// Calculates the confidence interval (mean +- delta) and some other related values, like
94+
// standard deviation, etc. See https://en.wikipedia.org/wiki/Student%27s_t-distribution
95+
void calcDelta();
96+
97+
int count;
98+
bool complete;
99+
Vector sum;
100+
Vector sumSq;
101+
Vector mean;
102+
Vector median;
103+
Vector sigma;
104+
Vector delta;
105+
std::vector<double> xData;
106+
std::vector<double> yData;
107+
std::vector<double> zData;
108+
};

0 commit comments

Comments
 (0)