Monado OpenXR Runtime
Loading...
Searching...
No Matches
contactglove_calibration.h
Go to the documentation of this file.
1// Copyright 2026, Beyley Cardellio
2// SPDX-License-Identifier: BSL-1.0
3/*!
4 * @file
5 * @brief ContactGlove device calibration implementation.
6 * @author Beyley Cardellio <ep1cm1n10n123@gmail.com>
7 * @ingroup drv_contactglove
8 */
9
10#pragma once
11
12#include "math/m_api.h"
13#include "math/m_vec2.h"
14
15#include "util/u_logging.h"
16#include "util/u_var.h"
17
19
20#include <float.h>
21
22
23#ifdef __cplusplus
24extern "C" {
25#endif
26
27// Forward declare
29
30
31//! The default deadzone used on the ContactGlove2, straight from the spec document.
32#define CONTACTGLOVE2_DEFAULT_DEADZONE 0.1f
33
34/*
35 *
36 * File structures
37 *
38 */
39
40//! The magic for the calibration file, written to disk in big endian so on-disk visibly matches source code.
41#define CONTACTGLOVE_CALIBRATION_MAGIC 0x15D04C50
42
44{
45 //! Initial structure is added.
47};
48
49//! The current version of the calibration file, increment with breaking changes
50#define CONTACTGLOVE_CALIBRATION_CURRENT_VERSION CONTACTGLOVE_CALIBRATION_VERSION_INITIAL
51
52//! Represents the calibration of a single flex sensor
54{
55 //! The minimum value, 0.0
56 uint16_t min;
57 //! The max value recorded, 1.0
58 uint16_t max;
59};
60
61//! Represents the calibration of the joystick
63{
64 //! The X value when the stick is at rest
66 //! The Y value when the stick is at rest
68
69 //! The distance from center where 1.0 is defined.
70 uint8_t range;
71
72 //! The normalized calibrated forward vector, in uncalibrated space.
74
75 //! Deadzone from 0-1
76 float deadzone;
77};
78
79//! Represents the calibration of the trigger
81{
82 //! The minimum value of the trigger, 0.0
83 uint8_t min;
84 //! The maximum value of the trigger, 1.0
85 uint8_t max;
86};
87
88//! A full calibration
90{
91 struct contactglove_sensor_calibration flex_sensors[CONTACTGLOVE2_SENSOR_COUNT];
92 struct contactglove_stick_calibration joystick;
94};
95
96/*
97 *
98 * Helper functions
99 *
100 */
101
102//! Applies a flex sensor calibration to a raw ADC value, clamped between 0 and 1.
103static inline float
105 int index,
106 const uint16_t *adc_values)
107{
108 const struct contactglove_sensor_calibration sensor_calib = calibration.flex_sensors[index];
109
110 return (CLAMP(adc_values[index], sensor_calib.min, sensor_calib.max) - sensor_calib.min) /
111 (float)(sensor_calib.max - sensor_calib.min);
112}
113
114//! Applies joystick calibration, returns an OpenXR joystick position
115static inline struct xrt_vec2
117 const uint8_t stick_x,
118 const uint8_t stick_y)
119{
120 const int8_t from_center_x = (int8_t)(((int16_t)stick_x) - calibration.stick_center_x);
121 const int8_t from_center_y = (int8_t)(((int16_t)stick_y) - calibration.stick_center_y);
122
123 const struct xrt_vec2 from_center = {
124 .x = (float)from_center_x,
125 .y = (float)from_center_y,
126 };
127
128 const struct xrt_vec2 raw_dir =
129 m_vec2_clamp_scalar(m_vec2_div_scalar(from_center, (float)calibration.range), -1.0f, 1.0f);
130
131 // Rotate raw_dir by the inverse of forward_vector so that if raw_dir == forward_vector we get (0.0, 1.0)
132 const struct xrt_vec2 rotated = {
133 .x = m_vec2_cross(raw_dir, calibration.forward_vector),
134 .y = m_vec2_dot(raw_dir, calibration.forward_vector),
135 };
136
137 // Apply deadzone
138 const float raw_len = m_vec2_len(rotated);
139 const float deadzone = CLAMP(calibration.deadzone, 0.0f, 1.0f - FLT_EPSILON);
140 if (raw_len <= deadzone) {
141 return XRT_C11_COMPOUND(struct xrt_vec2) XRT_VEC2_ZERO;
142 }
143
144 // Remap distance deadzone -> 1 to 0 -> 1, clamping length to 1.0
145 const float remapped_len = CLAMP((raw_len - deadzone) / (1.0f - deadzone), 0.0f, 1.0f);
146
147 // Remap the vector's length, inverting X (seems necessary here)
148 return m_vec2_mul(m_vec2_mul_scalar(rotated, remapped_len / raw_len), (struct xrt_vec2){.x = -1, .y = 1});
149}
150
151//! Applies a trigger calibration to the raw ADC value, following the algorithm laid out in the spec document.
152static inline float
154{
155 // SPEC: To avoid immediate saturation at the extremes, the host applies a small internal margin when converting
156 // raw values
157 if (calibration.max > calibration.min) {
158 calibration.min += 5;
159 calibration.max -= 5;
160 } else if (calibration.max <= calibration.min) {
161 calibration.min -= 5;
162 calibration.max += 5;
163 }
164
165 // The stored values are used to normalize trigger input:
166 // v = (raw - min) / (max - min)
167
168 float f = (CLAMP(adc_value, calibration.min, calibration.max) - calibration.min) /
169 (float)(calibration.max - calibration.min);
170
171 // SPEC: The normalized value is then clamped to [0, 1] and passed through a log curve for response shaping
172 // @note We don't apply the clamp 0-1 in the normalized range since we do it beforehand.
173
174 // @note They don't specify which log curve but this one seems very reasonable and likely close enough.
175 f = log10f(1.0f + (9.0f * f));
176
177 return f;
178}
179
180/*
181 *
182 * Wizard
183 *
184 */
185
186enum contactglove_calibration_wizard_step
187{
188 CONTACTGLOVE_CALIBRATION_WIZARD_NOT_CALIBRATING,
189 CONTACTGLOVE_CALIBRATION_WIZARD_CALIBRATING_FLEX_SENSORS,
190 CONTACTGLOVE_CALIBRATION_WIZARD_CALIBRATING_JOYSTICK_CENTER,
191 CONTACTGLOVE_CALIBRATION_WIZARD_CALIBRATING_JOYSTICK_RANGE,
192 CONTACTGLOVE_CALIBRATION_WIZARD_CALIBRATING_JOYSTICK_DEADZONE,
193 CONTACTGLOVE_CALIBRATION_WIZARD_CALIBRATING_JOYSTICK_FORWARD,
194 CONTACTGLOVE_CALIBRATION_WIZARD_CALIBRATING_TRIGGER,
195 CONTACTGLOVE_CALIBRATION_WIZARD_WRITING_OUTPUT,
196};
197
199{
200 struct contactglove_sensor_calibration sensors[CONTACTGLOVE2_SENSOR_COUNT];
201};
202
204{
205 uint8_t x;
206 uint8_t y;
207};
208
210{
211 uint8_t up_range;
212 uint8_t down_range;
213 uint8_t left_range;
214 uint8_t right_range;
215};
216
221
222/*!
223 * A type which manages the state of the currently-running calibration step
224 */
226{
227 enum u_logging_level log_level;
228 struct contactglove_device *glove;
229
230 bool button_state;
231
232 bool calibrating_magnetra2;
233
234 enum contactglove_calibration_wizard_step step;
235 union {
240 struct xrt_vec2 joystick_forward;
242 } state;
243 struct contactglove_calibration working;
244
245 //! The currently applied calibration
247
248 struct u_var_button u_var_start_button;
249};
250
251
252void
253contactglove_calibration_wizard_init(struct contactglove_calibration_wizard *wizard, struct contactglove_device *glove);
254
255void
256contactglove_calibration_wizard_deinit(struct contactglove_calibration_wizard *wizard);
257
258void
259contactglove_calibration_wizard_start(struct contactglove_calibration_wizard *wizard, bool calibrating_magnetra2);
260
261void
262contactglove_calibration_wizard_push_button(struct contactglove_calibration_wizard *wizard, bool button);
263
264void
265contactglove_calibration_wizard_push_flex_sensors(struct contactglove_calibration_wizard *wizard,
266 const uint16_t *adc_values);
267
268void
269contactglove_calibration_wizard_push_joystick(struct contactglove_calibration_wizard *wizard,
270 uint8_t adc_x,
271 uint8_t adc_y);
272
273void
274contactglove_calibration_wizard_push_trigger(struct contactglove_calibration_wizard *wizard, uint8_t adc);
275
276#ifdef __cplusplus
277}
278#endif
static float contactglove_apply_trigger_calibration(struct contactglove_trigger_calibration calibration, uint8_t adc_value)
Applies a trigger calibration to the raw ADC value, following the algorithm laid out in the spec docu...
Definition contactglove_calibration.h:153
contactglove_calibration_version
Definition contactglove_calibration.h:44
@ CONTACTGLOVE_CALIBRATION_VERSION_INITIAL
Initial structure is added.
Definition contactglove_calibration.h:46
static float contactglove_apply_sensor_calibration(const struct contactglove_calibration calibration, int index, const uint16_t *adc_values)
Applies a flex sensor calibration to a raw ADC value, clamped between 0 and 1.
Definition contactglove_calibration.h:104
static struct xrt_vec2 contactglove_apply_stick_calibration(const struct contactglove_stick_calibration calibration, const uint8_t stick_x, const uint8_t stick_y)
Applies joystick calibration, returns an OpenXR joystick position.
Definition contactglove_calibration.h:116
Implementation of ContactGlove device driver.
u_logging_level
Logging level enum.
Definition u_logging.h:45
#define CLAMP(X, A, B)
X clamped to the range [A, B].
Definition m_api.h:78
C interface to math library.
C vec2 math library.
Definition contactglove_calibration.h:199
Definition contactglove_calibration.h:204
Definition contactglove_calibration.h:218
Definition contactglove_calibration.h:210
A type which manages the state of the currently-running calibration step.
Definition contactglove_calibration.h:226
struct contactglove_calibration calibration
The currently applied calibration.
Definition contactglove_calibration.h:246
A full calibration.
Definition contactglove_calibration.h:90
A single ContactGlove glove.
Definition contactglove_internal.h:83
Represents the calibration of a single flex sensor.
Definition contactglove_calibration.h:54
uint16_t max
The max value recorded, 1.0.
Definition contactglove_calibration.h:58
uint16_t min
The minimum value, 0.0.
Definition contactglove_calibration.h:56
Represents the calibration of the joystick.
Definition contactglove_calibration.h:63
uint8_t stick_center_y
The Y value when the stick is at rest.
Definition contactglove_calibration.h:67
uint8_t range
The distance from center where 1.0 is defined.
Definition contactglove_calibration.h:70
struct xrt_vec2 forward_vector
The normalized calibrated forward vector, in uncalibrated space.
Definition contactglove_calibration.h:73
uint8_t stick_center_x
The X value when the stick is at rest.
Definition contactglove_calibration.h:65
float deadzone
Deadzone from 0-1.
Definition contactglove_calibration.h:76
Represents the calibration of the trigger.
Definition contactglove_calibration.h:81
uint8_t max
The maximum value of the trigger, 1.0.
Definition contactglove_calibration.h:85
uint8_t min
The minimum value of the trigger, 0.0.
Definition contactglove_calibration.h:83
Simple pushable button.
Definition u_var.h:86
A 2 element vector with single floats.
Definition xrt_defines.h:268
#define XRT_VEC2_ZERO
All-zero value for xrt_vec2.
Definition xrt_defines.h:280
Basic logging functionality.
Variable tracking code.