1272 lines
38 KiB
C
1272 lines
38 KiB
C
//
|
|
// Created by thejackimonster on 30.03.23.
|
|
//
|
|
// Copyright (c) 2023-2025 thejackimonster. All rights reserved.
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
// of this software and associated documentation files (the "Software"), to deal
|
|
// in the Software without restriction, including without limitation the rights
|
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
// copies of the Software, and to permit persons to whom the Software is
|
|
// furnished to do so, subject to the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included in
|
|
// all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
// THE SOFTWARE.
|
|
//
|
|
|
|
#include "device_imu.h"
|
|
#include "device.h"
|
|
|
|
#include <Fusion/FusionAxes.h>
|
|
#include <Fusion/FusionMath.h>
|
|
#include <float.h>
|
|
#include <json-c/json_object.h>
|
|
#include <json-c/json_types.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include <Fusion/Fusion.h>
|
|
#include <json-c/json.h>
|
|
|
|
#include <hidapi/hidapi.h>
|
|
|
|
#include "crc32.h"
|
|
#include "hid_ids.h"
|
|
|
|
#define GRAVITY_G (9.806f)
|
|
|
|
#ifndef NDEBUG
|
|
#define device_imu_error(msg) fprintf(stderr, "ERROR: %s\n", msg)
|
|
#else
|
|
#define device_imu_error(msg) (0)
|
|
#endif
|
|
|
|
struct device_imu_camera_sensor_t {
|
|
FusionMatrix cameraMisalignment;
|
|
FusionVector cameraOffset;
|
|
|
|
uint16_t resolution [2];
|
|
|
|
float cc [2];
|
|
float fc [2];
|
|
|
|
uint32_t num_kc;
|
|
float* kc;
|
|
};
|
|
|
|
struct device_imu_camera_t {
|
|
uint32_t num_sensors;
|
|
device_imu_camera_sensor_type* sensors;
|
|
};
|
|
|
|
struct device_imu_camera_calibration_t {
|
|
uint32_t num_cameras;
|
|
device_imu_camera_type *cameras;
|
|
};
|
|
|
|
struct device_imu_calibration_t {
|
|
FusionMatrix gyroscopeMisalignment;
|
|
FusionVector gyroscopeSensitivity;
|
|
FusionVector gyroscopeOffset;
|
|
|
|
FusionMatrix accelerometerMisalignment;
|
|
FusionVector accelerometerSensitivity;
|
|
FusionVector accelerometerOffset;
|
|
|
|
FusionMatrix magnetometerMisalignment;
|
|
FusionVector magnetometerSensitivity;
|
|
FusionVector magnetometerOffset;
|
|
|
|
FusionMatrix softIronMatrix;
|
|
FusionVector hardIronOffset;
|
|
|
|
FusionQuaternion noises;
|
|
|
|
device_imu_camera_calibration_type cam;
|
|
};
|
|
|
|
static bool send_payload(device_imu_type* device, uint16_t size, const uint8_t* payload) {
|
|
int payload_size = size;
|
|
if (payload_size > device->max_payload_size) {
|
|
payload_size = device->max_payload_size;
|
|
}
|
|
|
|
int transferred = hid_write(device->handle, payload, payload_size);
|
|
|
|
if (transferred != payload_size) {
|
|
device_imu_error("Sending payload failed");
|
|
return false;
|
|
}
|
|
|
|
return (transferred == size);
|
|
}
|
|
|
|
static bool recv_payload(device_imu_type* device, uint16_t size, uint8_t* payload) {
|
|
int payload_size = size;
|
|
if (payload_size > device->max_payload_size) {
|
|
payload_size = device->max_payload_size;
|
|
}
|
|
|
|
int transferred = hid_read(device->handle, payload, payload_size);
|
|
|
|
if (transferred >= payload_size) {
|
|
transferred = payload_size;
|
|
}
|
|
|
|
if (transferred == 0) {
|
|
return false;
|
|
}
|
|
|
|
if (transferred != payload_size) {
|
|
device_imu_error("Receiving payload failed");
|
|
return false;
|
|
}
|
|
|
|
return (transferred == size);
|
|
}
|
|
|
|
struct __attribute__((__packed__)) device_imu_payload_packet_t {
|
|
uint8_t head;
|
|
uint32_t checksum;
|
|
uint16_t length;
|
|
uint8_t msgid;
|
|
uint8_t data [512 - 8];
|
|
};
|
|
|
|
typedef struct device_imu_payload_packet_t device_imu_payload_packet_type;
|
|
|
|
static bool send_payload_msg(device_imu_type* device, uint8_t msgid, uint16_t len, const uint8_t* data) {
|
|
static device_imu_payload_packet_type packet;
|
|
|
|
const uint16_t packet_len = 3 + len;
|
|
const uint16_t payload_len = 5 + packet_len;
|
|
|
|
packet.head = 0xAA;
|
|
packet.length = htole16(packet_len);
|
|
packet.msgid = msgid;
|
|
|
|
memcpy(packet.data, data, len);
|
|
packet.checksum = htole32(
|
|
crc32_checksum(
|
|
(const uint8_t*) (&packet.length),
|
|
packet.length
|
|
)
|
|
);
|
|
|
|
return send_payload(device, payload_len, (uint8_t*) (&packet));
|
|
}
|
|
|
|
static bool send_payload_msg_signal(device_imu_type* device, uint8_t msgid, uint8_t signal) {
|
|
return send_payload_msg(device, msgid, 1, &signal);
|
|
}
|
|
|
|
static bool recv_payload_msg(device_imu_type* device, uint8_t msgid, uint16_t len, uint8_t* data) {
|
|
static device_imu_payload_packet_type packet;
|
|
|
|
packet.head = 0;
|
|
packet.length = 0;
|
|
packet.msgid = 0;
|
|
|
|
const uint16_t packet_len = 3 + len;
|
|
const uint16_t payload_len = 5 + packet_len;
|
|
|
|
do {
|
|
if (!recv_payload(device, payload_len, (uint8_t*) (&packet))) {
|
|
return false;
|
|
}
|
|
} while (packet.msgid != msgid);
|
|
|
|
memcpy(data, packet.data, len);
|
|
return true;
|
|
}
|
|
|
|
static FusionVector json_object_get_vector(struct json_object* obj) {
|
|
if ((!json_object_is_type(obj, json_type_array)) ||
|
|
(json_object_array_length(obj) != 3)) {
|
|
return FUSION_VECTOR_ZERO;
|
|
}
|
|
|
|
FusionVector vector;
|
|
vector.axis.x = (float) json_object_get_double(json_object_array_get_idx(obj, 0));
|
|
vector.axis.y = (float) json_object_get_double(json_object_array_get_idx(obj, 1));
|
|
vector.axis.z = (float) json_object_get_double(json_object_array_get_idx(obj, 2));
|
|
return vector;
|
|
}
|
|
|
|
static FusionQuaternion json_object_get_quaternion(struct json_object* obj) {
|
|
if ((!json_object_is_type(obj, json_type_array)) ||
|
|
(json_object_array_length(obj) != 4)) {
|
|
return FUSION_IDENTITY_QUATERNION;
|
|
}
|
|
|
|
FusionQuaternion quaternion;
|
|
quaternion.element.x = (float) json_object_get_double(json_object_array_get_idx(obj, 0));
|
|
quaternion.element.y = (float) json_object_get_double(json_object_array_get_idx(obj, 1));
|
|
quaternion.element.z = (float) json_object_get_double(json_object_array_get_idx(obj, 2));
|
|
quaternion.element.w = (float) json_object_get_double(json_object_array_get_idx(obj, 3));
|
|
return quaternion;
|
|
}
|
|
|
|
static uint32_t json_object_get_array_f32(struct json_object* obj, float** array, uint32_t n) {
|
|
if ((!json_object_is_type(obj, json_type_array)) ||
|
|
((n > 0) && (json_object_array_length(obj) != n))) {
|
|
return 0;
|
|
}
|
|
|
|
if (n == 0) {
|
|
n = json_object_array_length(obj);
|
|
*array = malloc(sizeof(float) * n);
|
|
}
|
|
|
|
for (uint32_t i = 0; i < n; i++) {
|
|
(*array)[i] = (float) json_object_get_double(json_object_array_get_idx(obj, i));
|
|
}
|
|
|
|
return n;
|
|
}
|
|
|
|
static uint32_t json_object_get_array_u16(struct json_object* obj, uint16_t** array, uint32_t n) {
|
|
if ((!json_object_is_type(obj, json_type_array)) ||
|
|
((n > 0) && (json_object_array_length(obj) != n))) {
|
|
return 0;
|
|
}
|
|
|
|
if (n == 0) {
|
|
n = json_object_array_length(obj);
|
|
*array = malloc(sizeof(uint16_t) * n);
|
|
}
|
|
|
|
for (uint32_t i = 0; i < n; i++) {
|
|
(*array)[i] = (uint16_t) json_object_get_int(json_object_array_get_idx(obj, i));
|
|
}
|
|
|
|
return n;
|
|
}
|
|
|
|
static void init_device_imu_camera(device_imu_camera_type *camera, json_object *cam) {
|
|
uint32_t num_sensors = json_object_get_int(json_object_object_get(cam, "num_of_cameras"));
|
|
|
|
device_imu_camera_sensor_type *sensors = NULL;
|
|
|
|
if (num_sensors > 0) {
|
|
sensors = malloc(sizeof(device_imu_camera_sensor_type) * num_sensors);
|
|
}
|
|
|
|
if (!sensors) {
|
|
num_sensors = 0;
|
|
}
|
|
|
|
for (uint32_t n = 0; n < num_sensors; n++) {
|
|
device_imu_camera_sensor_type *sensor = &(sensors[n]);
|
|
|
|
char device_name [64];
|
|
snprintf(device_name, 64, "device_%u", (n + 1));
|
|
|
|
struct json_object* dev = json_object_object_get(cam, device_name);
|
|
|
|
FusionQuaternion imu_q_cam = json_object_get_quaternion(json_object_object_get(dev, "imu_q_cam"));
|
|
FusionVector cam_offset = json_object_get_vector(json_object_object_get(dev, "imu_p_cam"));
|
|
|
|
sensor->cameraMisalignment = FusionQuaternionToMatrix(imu_q_cam);
|
|
sensor->cameraOffset = cam_offset;
|
|
|
|
uint16_t* resolution = sensor->resolution;
|
|
float* cc = sensor->cc;
|
|
float* fc = sensor->fc;
|
|
float* kc = sensor->kc;
|
|
|
|
json_object_get_array_u16(json_object_object_get(dev, "resolution"), &resolution, 2);
|
|
|
|
json_object_get_array_f32(json_object_object_get(dev, "cc"), &cc, 2);
|
|
json_object_get_array_f32(json_object_object_get(dev, "fc"), &fc, 2);
|
|
|
|
sensor->num_kc = json_object_get_array_f32(json_object_object_get(dev, "kc"), &kc, 0);
|
|
sensor->kc = kc;
|
|
}
|
|
|
|
camera->num_sensors = num_sensors;
|
|
camera->sensors = sensors;
|
|
}
|
|
|
|
device_imu_error_type device_imu_open(device_imu_type* device, device_imu_event_callback callback) {
|
|
if (!device) {
|
|
device_imu_error("No device");
|
|
return DEVICE_IMU_ERROR_NO_DEVICE;
|
|
}
|
|
|
|
memset(device, 0, sizeof(device_imu_type));
|
|
device->vendor_id = xreal_vendor_id;
|
|
device->product_id = 0;
|
|
device->callback = callback;
|
|
|
|
if (!device_init()) {
|
|
device_imu_error("Not initialized");
|
|
return DEVICE_IMU_ERROR_NOT_INITIALIZED;
|
|
}
|
|
|
|
struct hid_device_info* info = hid_enumerate(
|
|
device->vendor_id,
|
|
device->product_id
|
|
);
|
|
|
|
struct hid_device_info* it = info;
|
|
while (it) {
|
|
int interface_id = xreal_imu_interface_id(it->product_id);
|
|
if (interface_id != -1 && it->interface_number == interface_id) {
|
|
#ifndef NDEBUG
|
|
printf("Found IMU device with product_id 0x%x on interface %d\n", it->product_id, interface_id);
|
|
#endif
|
|
device->product_id = it->product_id;
|
|
device->handle = hid_open_path(it->path);
|
|
device->max_payload_size = xreal_imu_max_payload_size(device->product_id);
|
|
break;
|
|
}
|
|
|
|
it = it->next;
|
|
}
|
|
|
|
hid_free_enumeration(info);
|
|
|
|
if (!device->handle) {
|
|
device_imu_error("No handle");
|
|
return DEVICE_IMU_ERROR_NO_HANDLE;
|
|
}
|
|
|
|
if ((!send_payload_msg_signal(device, DEVICE_IMU_MSG_START_IMU_DATA, 0x0)) ||
|
|
(!recv_payload_msg(device, DEVICE_IMU_MSG_START_IMU_DATA, 0, NULL))) {
|
|
device_imu_error("Failed sending payload to stop imu data stream");
|
|
return DEVICE_IMU_ERROR_PAYLOAD_FAILED;
|
|
}
|
|
|
|
device_imu_clear(device);
|
|
|
|
if (!send_payload_msg(device, DEVICE_IMU_MSG_GET_STATIC_ID, 0, NULL)) {
|
|
device_imu_error("Failed sending payload to get static id");
|
|
return DEVICE_IMU_ERROR_PAYLOAD_FAILED;
|
|
}
|
|
|
|
uint32_t static_id = 0;
|
|
if (recv_payload_msg(device, DEVICE_IMU_MSG_GET_STATIC_ID, 4, (uint8_t*) &static_id)) {
|
|
device->static_id = static_id;
|
|
} else {
|
|
device->static_id = 0x20220101;
|
|
}
|
|
|
|
device->calibration = malloc(sizeof(device_imu_calibration_type));
|
|
memset(device->calibration, 0, sizeof(device_imu_calibration_type));
|
|
|
|
device_imu_reset_calibration(device);
|
|
|
|
if (!send_payload_msg(device, DEVICE_IMU_MSG_GET_CAL_DATA_LENGTH, 0, NULL)) {
|
|
device_imu_error("Failed sending payload to get calibration data length");
|
|
return DEVICE_IMU_ERROR_PAYLOAD_FAILED;
|
|
}
|
|
|
|
uint32_t calibration_len = 0;
|
|
if (recv_payload_msg(device, DEVICE_IMU_MSG_GET_CAL_DATA_LENGTH, 4, (uint8_t*) &calibration_len)) {
|
|
const uint16_t max_packet_size = (device->max_payload_size - 8);
|
|
char* calibration_data = malloc(calibration_len + 1);
|
|
|
|
uint32_t position = 0;
|
|
while (position < calibration_len) {
|
|
const uint32_t remaining = (calibration_len - position);
|
|
|
|
if (!send_payload_msg(device, DEVICE_IMU_MSG_CAL_DATA_GET_NEXT_SEGMENT, 0, NULL)) {
|
|
break;
|
|
}
|
|
|
|
const uint16_t next = (remaining > max_packet_size? max_packet_size : remaining);
|
|
|
|
if (!recv_payload_msg(device, DEVICE_IMU_MSG_CAL_DATA_GET_NEXT_SEGMENT, next, (uint8_t*) calibration_data + position)) {
|
|
break;
|
|
}
|
|
|
|
position += next;
|
|
}
|
|
|
|
calibration_data[calibration_len] = '\0';
|
|
|
|
struct json_tokener* tokener = json_tokener_new();
|
|
struct json_object* root = json_tokener_parse_ex(tokener, calibration_data, calibration_len);
|
|
struct json_object* imu = json_object_object_get(root, "IMU");
|
|
struct json_object* dev1 = json_object_object_get(imu, "device_1");
|
|
|
|
FusionVector accel_bias = json_object_get_vector(json_object_object_get(dev1, "accel_bias"));
|
|
FusionQuaternion accel_q_gyro = json_object_get_quaternion(json_object_object_get(dev1, "accel_q_gyro"));
|
|
FusionVector gyro_bias = json_object_get_vector(json_object_object_get(dev1, "gyro_bias"));
|
|
FusionQuaternion gyro_q_mag = json_object_get_quaternion(json_object_object_get(dev1, "gyro_q_mag"));
|
|
FusionVector mag_bias = json_object_get_vector(json_object_object_get(dev1, "mag_bias"));
|
|
FusionQuaternion imu_noises = json_object_get_quaternion(json_object_object_get(dev1, "imu_noises"));
|
|
FusionVector scale_accel = json_object_get_vector(json_object_object_get(dev1, "scale_accel"));
|
|
FusionVector scale_gyro = json_object_get_vector(json_object_object_get(dev1, "scale_gyro"));
|
|
FusionVector scale_mag = json_object_get_vector(json_object_object_get(dev1, "scale_mag"));
|
|
|
|
const FusionQuaternion accel_q_mag = FusionQuaternionMultiply(accel_q_gyro, gyro_q_mag);
|
|
|
|
device->calibration->gyroscopeMisalignment = FusionQuaternionToMatrix(accel_q_gyro);
|
|
device->calibration->gyroscopeSensitivity = scale_gyro;
|
|
device->calibration->gyroscopeOffset = gyro_bias;
|
|
|
|
device->calibration->accelerometerMisalignment = FUSION_IDENTITY_MATRIX;
|
|
device->calibration->accelerometerSensitivity = scale_accel;
|
|
device->calibration->accelerometerOffset = accel_bias;
|
|
|
|
device->calibration->magnetometerMisalignment = FusionQuaternionToMatrix(accel_q_mag);
|
|
device->calibration->magnetometerSensitivity = scale_mag;
|
|
device->calibration->magnetometerOffset = mag_bias;
|
|
|
|
device->calibration->noises = imu_noises;
|
|
|
|
struct json_object* rgb = json_object_object_get(root, "RGB_camera");
|
|
struct json_object* slam = json_object_object_get(root, "SLAM_camera");
|
|
|
|
uint32_t num_cameras_rgb = json_object_get_int(json_object_object_get(rgb, "num_of_cameras"));
|
|
uint32_t num_cameras_slam = json_object_get_int(json_object_object_get(slam, "num_of_cameras"));
|
|
|
|
const uint32_t num_cameras = (num_cameras_rgb > 0? 1 : 0) + (num_cameras_slam > 0? 1 : 0);
|
|
|
|
device_imu_camera_type *cameras = NULL;
|
|
uint32_t camera_index = 0;
|
|
|
|
if (num_cameras > 0) {
|
|
cameras = malloc(sizeof(device_imu_camera_type) * num_cameras);
|
|
}
|
|
|
|
if ((cameras) && (num_cameras_rgb > 0)) {
|
|
init_device_imu_camera(&(cameras[camera_index++]), rgb);
|
|
}
|
|
|
|
if ((cameras) && (num_cameras_slam > 0)) {
|
|
init_device_imu_camera(&(cameras[camera_index++]), slam);
|
|
}
|
|
|
|
device->calibration->cam.num_cameras = cameras? num_cameras : 0;
|
|
device->calibration->cam.cameras = cameras;
|
|
|
|
json_tokener_free(tokener);
|
|
free(calibration_data);
|
|
}
|
|
|
|
if ((!send_payload_msg_signal(device, DEVICE_IMU_MSG_START_IMU_DATA, 0x1)) ||
|
|
(!recv_payload_msg(device, DEVICE_IMU_MSG_START_IMU_DATA, 0, NULL))) {
|
|
device_imu_error("Failed sending payload to start imu data stream");
|
|
return DEVICE_IMU_ERROR_PAYLOAD_FAILED;
|
|
}
|
|
|
|
const uint32_t SAMPLE_RATE = 1000;
|
|
|
|
device->offset = malloc(sizeof(FusionOffset));
|
|
device->ahrs = malloc(sizeof(FusionAhrs));
|
|
|
|
if (device->offset) {
|
|
FusionOffsetInitialise((FusionOffset*) device->offset, SAMPLE_RATE);
|
|
}
|
|
|
|
FusionAhrsInitialise((FusionAhrs*) device->ahrs);
|
|
|
|
const FusionAhrsSettings settings = {
|
|
.convention = FusionConventionNed,
|
|
.gain = 0.5f,
|
|
.accelerationRejection = 10.0f,
|
|
.magneticRejection = 20.0f,
|
|
.recoveryTriggerPeriod = 5 * SAMPLE_RATE, /* 5 seconds */
|
|
};
|
|
|
|
FusionAhrsSetSettings((FusionAhrs*) device->ahrs, &settings);
|
|
return DEVICE_IMU_ERROR_NO_ERROR;
|
|
}
|
|
|
|
device_imu_error_type device_imu_reset_calibration(device_imu_type* device) {
|
|
if (!device) {
|
|
device_imu_error("No device");
|
|
return DEVICE_IMU_ERROR_NO_DEVICE;
|
|
}
|
|
|
|
if (!device->calibration) {
|
|
device_imu_error("Not allocated");
|
|
return DEVICE_IMU_ERROR_NO_ALLOCATION;
|
|
}
|
|
|
|
device->calibration->gyroscopeMisalignment = FUSION_IDENTITY_MATRIX;
|
|
device->calibration->gyroscopeSensitivity = FUSION_VECTOR_ONES;
|
|
device->calibration->gyroscopeOffset = FUSION_VECTOR_ZERO;
|
|
|
|
device->calibration->accelerometerMisalignment = FUSION_IDENTITY_MATRIX;
|
|
device->calibration->accelerometerSensitivity = FUSION_VECTOR_ONES;
|
|
device->calibration->accelerometerOffset = FUSION_VECTOR_ZERO;
|
|
|
|
device->calibration->magnetometerMisalignment = FUSION_IDENTITY_MATRIX;
|
|
device->calibration->magnetometerSensitivity = FUSION_VECTOR_ONES;
|
|
device->calibration->magnetometerOffset = FUSION_VECTOR_ZERO;
|
|
|
|
device->calibration->softIronMatrix = FUSION_IDENTITY_MATRIX;
|
|
device->calibration->hardIronOffset = FUSION_VECTOR_ZERO;
|
|
|
|
device->calibration->noises = FUSION_IDENTITY_QUATERNION;
|
|
device->calibration->noises.element.w = 0.0f;
|
|
|
|
if (device->calibration->cam.cameras) {
|
|
for (uint32_t i = 0; i < device->calibration->cam.num_cameras; i++) {
|
|
if (!device->calibration->cam.cameras[i].sensors) {
|
|
continue;
|
|
}
|
|
|
|
for (uint32_t j = 0; j < device->calibration->cam.cameras[i].num_sensors; j++) {
|
|
if (!device->calibration->cam.cameras[i].sensors[j].kc) {
|
|
continue;
|
|
}
|
|
|
|
free(device->calibration->cam.cameras[i].sensors[j].kc);
|
|
|
|
device->calibration->cam.cameras[i].sensors[j].num_kc = 0;
|
|
device->calibration->cam.cameras[i].sensors[j].kc = NULL;
|
|
}
|
|
|
|
free(device->calibration->cam.cameras[i].sensors);
|
|
|
|
device->calibration->cam.cameras[i].num_sensors = 0;
|
|
device->calibration->cam.cameras[i].sensors = NULL;
|
|
}
|
|
|
|
free(device->calibration->cam.cameras);
|
|
}
|
|
|
|
device->calibration->cam.num_cameras = 0;
|
|
device->calibration->cam.cameras = NULL;
|
|
}
|
|
|
|
device_imu_error_type device_imu_load_calibration(device_imu_type* device, const char* path) {
|
|
if (!device) {
|
|
device_imu_error("No device");
|
|
return DEVICE_IMU_ERROR_NO_DEVICE;
|
|
}
|
|
|
|
if (!device->calibration) {
|
|
device_imu_error("Not allocated");
|
|
return DEVICE_IMU_ERROR_NO_ALLOCATION;
|
|
}
|
|
|
|
FILE* file = fopen(path, "rb");
|
|
if (!file) {
|
|
device_imu_error("No file opened");
|
|
return DEVICE_IMU_ERROR_FILE_NOT_OPEN;
|
|
}
|
|
|
|
device_imu_error_type result = DEVICE_IMU_ERROR_NO_ERROR;
|
|
const size_t calibration_size = (
|
|
sizeof(device_imu_calibration_type) - sizeof(device_imu_camera_calibration_type)
|
|
);
|
|
|
|
size_t count;
|
|
count = fread(device->calibration, 1, calibration_size, file);
|
|
|
|
if (calibration_size != count) {
|
|
device_imu_error("Not fully loaded");
|
|
result = DEVICE_IMU_ERROR_LOADING_FAILED;
|
|
}
|
|
|
|
if (0 != fclose(file)) {
|
|
device_imu_error("No file closed");
|
|
return DEVICE_IMU_ERROR_FILE_NOT_CLOSED;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
device_imu_error_type device_imu_save_calibration(device_imu_type* device, const char* path) {
|
|
if (!device) {
|
|
device_imu_error("No device");
|
|
return DEVICE_IMU_ERROR_NO_DEVICE;
|
|
}
|
|
|
|
if (!device->calibration) {
|
|
device_imu_error("Not allocated");
|
|
return DEVICE_IMU_ERROR_NO_ALLOCATION;
|
|
}
|
|
|
|
FILE* file = fopen(path, "wb");
|
|
if (!file) {
|
|
device_imu_error("No file opened");
|
|
return DEVICE_IMU_ERROR_FILE_NOT_OPEN;
|
|
}
|
|
|
|
device_imu_error_type result = DEVICE_IMU_ERROR_NO_ERROR;
|
|
const size_t calibration_size = (
|
|
sizeof(device_imu_calibration_type) - sizeof(device_imu_camera_calibration_type)
|
|
);
|
|
|
|
size_t count;
|
|
count = fwrite(device->calibration, 1, calibration_size, file);
|
|
|
|
if (calibration_size != count) {
|
|
device_imu_error("Not fully saved");
|
|
result = DEVICE_IMU_ERROR_SAVING_FAILED;
|
|
}
|
|
|
|
if (0 != fclose(file)) {
|
|
device_imu_error("No file closed");
|
|
return DEVICE_IMU_ERROR_FILE_NOT_CLOSED;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static void device_imu_callback(device_imu_type* device,
|
|
uint64_t timestamp,
|
|
device_imu_event_type event) {
|
|
if (!device->callback) {
|
|
return;
|
|
}
|
|
|
|
device->callback(timestamp, event, device->ahrs);
|
|
}
|
|
|
|
static int32_t pack32bit_signed(const uint8_t* data) {
|
|
uint32_t unsigned_value = (data[0]) | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
|
|
return ((int32_t) unsigned_value);
|
|
}
|
|
|
|
static int32_t pack24bit_signed(const uint8_t* data) {
|
|
uint32_t unsigned_value = (data[0]) | (data[1] << 8) | (data[2] << 16);
|
|
if ((data[2] & 0x80) != 0) unsigned_value |= (0xFF << 24);
|
|
return ((int32_t) unsigned_value);
|
|
}
|
|
|
|
static int16_t pack16bit_signed(const uint8_t* data) {
|
|
uint16_t unsigned_value = (data[1] << 8) | (data[0]);
|
|
return (int16_t) unsigned_value;
|
|
}
|
|
|
|
static int32_t pack32bit_signed_swap(const uint8_t* data) {
|
|
uint32_t unsigned_value = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | (data[3]);
|
|
return ((int32_t) unsigned_value);
|
|
}
|
|
|
|
static int16_t pack16bit_signed_swap(const uint8_t* data) {
|
|
uint16_t unsigned_value = (data[0] << 8) | (data[1]);
|
|
return (int16_t) unsigned_value;
|
|
}
|
|
|
|
static int16_t pack16bit_signed_bizarre(const uint8_t* data) {
|
|
uint16_t unsigned_value = (data[0]) | ((data[1] ^ 0x80) << 8);
|
|
return (int16_t) unsigned_value;
|
|
}
|
|
|
|
static void readIMU_from_packet(const device_imu_packet_type* packet,
|
|
FusionVector* gyroscope,
|
|
FusionVector* accelerometer,
|
|
FusionVector* magnetometer) {
|
|
int32_t vel_m = pack16bit_signed(packet->angular_multiplier);
|
|
int32_t vel_d = pack32bit_signed(packet->angular_divisor);
|
|
|
|
int32_t vel_x = pack24bit_signed(packet->angular_velocity_x);
|
|
int32_t vel_y = pack24bit_signed(packet->angular_velocity_y);
|
|
int32_t vel_z = pack24bit_signed(packet->angular_velocity_z);
|
|
|
|
gyroscope->axis.x = (float) vel_x * (float) vel_m / (float) vel_d;
|
|
gyroscope->axis.y = (float) vel_y * (float) vel_m / (float) vel_d;
|
|
gyroscope->axis.z = (float) vel_z * (float) vel_m / (float) vel_d;
|
|
|
|
int32_t accel_m = pack16bit_signed(packet->acceleration_multiplier);
|
|
int32_t accel_d = pack32bit_signed(packet->acceleration_divisor);
|
|
|
|
int32_t accel_x = pack24bit_signed(packet->acceleration_x);
|
|
int32_t accel_y = pack24bit_signed(packet->acceleration_y);
|
|
int32_t accel_z = pack24bit_signed(packet->acceleration_z);
|
|
|
|
accelerometer->axis.x = (float) accel_x * (float) accel_m / (float) accel_d;
|
|
accelerometer->axis.y = (float) accel_y * (float) accel_m / (float) accel_d;
|
|
accelerometer->axis.z = (float) accel_z * (float) accel_m / (float) accel_d;
|
|
|
|
int32_t magnet_m = pack16bit_signed_swap(packet->magnetic_multiplier);
|
|
int32_t magnet_d = pack32bit_signed_swap(packet->magnetic_divisor);
|
|
|
|
int16_t magnet_x = pack16bit_signed_bizarre(packet->magnetic_x);
|
|
int16_t magnet_y = pack16bit_signed_bizarre(packet->magnetic_y);
|
|
int16_t magnet_z = pack16bit_signed_bizarre(packet->magnetic_z);
|
|
|
|
magnetometer->axis.x = (float) magnet_x * (float) magnet_m / (float) magnet_d;
|
|
magnetometer->axis.y = (float) magnet_y * (float) magnet_m / (float) magnet_d;
|
|
magnetometer->axis.z = (float) magnet_z * (float) magnet_m / (float) magnet_d;
|
|
}
|
|
|
|
#define min(x, y) ((x) < (y)? (x) : (y))
|
|
#define max(x, y) ((x) > (y)? (x) : (y))
|
|
|
|
static void pre_biased_coordinate_system(FusionVector* v) {
|
|
*v = FusionAxesSwap(*v, FusionAxesAlignmentNXNZNY);
|
|
}
|
|
|
|
static void post_biased_coordinate_system(const FusionVector* v, FusionVector* res) {
|
|
*res = FusionAxesSwap(*v, FusionAxesAlignmentPZPXPY);
|
|
}
|
|
|
|
static void iterate_iron_offset_estimation(const FusionVector* magnetometer, FusionMatrix* softIronMatrix, FusionVector* hardIronOffset) {
|
|
static FusionVector max = { FLT_MIN, FLT_MIN, FLT_MIN };
|
|
static FusionVector min = { FLT_MAX, FLT_MAX, FLT_MAX };
|
|
|
|
for (int i = 0; i < 3; i++) {
|
|
max.array[i] = max(max.array[i], magnetometer->array[i]);
|
|
min.array[i] = min(min.array[i], magnetometer->array[i]);
|
|
}
|
|
|
|
const float mx = (max.axis.x - min.axis.x) / 2.0f;
|
|
const float my = (max.axis.y - min.axis.y) / 2.0f;
|
|
const float mz = (max.axis.z - min.axis.z) / 2.0f;
|
|
|
|
const float cx = (min.axis.x + max.axis.x) / 2.0f;
|
|
const float cy = (min.axis.y + max.axis.y) / 2.0f;
|
|
const float cz = (min.axis.z + max.axis.z) / 2.0f;
|
|
|
|
memset(softIronMatrix, 0, sizeof(*softIronMatrix));
|
|
|
|
softIronMatrix->element.xx = 1.0f / mx;
|
|
softIronMatrix->element.yy = 1.0f / my;
|
|
softIronMatrix->element.zz = 1.0f / mz;
|
|
|
|
hardIronOffset->axis.x = cx;
|
|
hardIronOffset->axis.y = cy;
|
|
hardIronOffset->axis.z = cz;
|
|
}
|
|
|
|
static void apply_calibration(const device_imu_type* device,
|
|
FusionVector* gyroscope,
|
|
FusionVector* accelerometer,
|
|
FusionVector* magnetometer) {
|
|
FusionMatrix gyroscopeMisalignment;
|
|
FusionVector gyroscopeSensitivity;
|
|
FusionVector gyroscopeOffset;
|
|
|
|
FusionMatrix accelerometerMisalignment;
|
|
FusionVector accelerometerSensitivity;
|
|
FusionVector accelerometerOffset;
|
|
|
|
FusionMatrix magnetometerMisalignment;
|
|
FusionVector magnetometerSensitivity;
|
|
FusionVector magnetometerOffset;
|
|
|
|
FusionMatrix softIronMatrix;
|
|
FusionVector hardIronOffset;
|
|
|
|
if (device->calibration) {
|
|
gyroscopeMisalignment = device->calibration->gyroscopeMisalignment;
|
|
gyroscopeSensitivity = device->calibration->gyroscopeSensitivity;
|
|
gyroscopeOffset = device->calibration->gyroscopeOffset;
|
|
|
|
accelerometerMisalignment = device->calibration->accelerometerMisalignment;
|
|
accelerometerSensitivity = device->calibration->accelerometerSensitivity;
|
|
accelerometerOffset = device->calibration->accelerometerOffset;
|
|
|
|
magnetometerMisalignment = device->calibration->magnetometerMisalignment;
|
|
magnetometerSensitivity = device->calibration->magnetometerSensitivity;
|
|
magnetometerOffset = device->calibration->magnetometerOffset;
|
|
|
|
softIronMatrix = device->calibration->softIronMatrix;
|
|
hardIronOffset = device->calibration->hardIronOffset;
|
|
} else {
|
|
gyroscopeMisalignment = FUSION_IDENTITY_MATRIX;
|
|
gyroscopeSensitivity = FUSION_VECTOR_ONES;
|
|
gyroscopeOffset = FUSION_VECTOR_ZERO;
|
|
|
|
accelerometerMisalignment = FUSION_IDENTITY_MATRIX;
|
|
accelerometerSensitivity = FUSION_VECTOR_ONES;
|
|
accelerometerOffset = FUSION_VECTOR_ZERO;
|
|
|
|
magnetometerMisalignment = FUSION_IDENTITY_MATRIX;
|
|
magnetometerSensitivity = FUSION_VECTOR_ONES;
|
|
magnetometerOffset = FUSION_VECTOR_ZERO;
|
|
|
|
softIronMatrix = FUSION_IDENTITY_MATRIX;
|
|
hardIronOffset = FUSION_VECTOR_ZERO;
|
|
}
|
|
|
|
gyroscopeOffset = FusionVectorMultiplyScalar(
|
|
gyroscopeOffset,
|
|
FusionRadiansToDegrees(1.0f)
|
|
);
|
|
|
|
accelerometerOffset = FusionVectorMultiplyScalar(
|
|
accelerometerOffset,
|
|
1.0f / GRAVITY_G
|
|
);
|
|
|
|
FusionVector g = *gyroscope;
|
|
FusionVector a = *accelerometer;
|
|
FusionVector m = *magnetometer;
|
|
|
|
pre_biased_coordinate_system(&g);
|
|
pre_biased_coordinate_system(&a);
|
|
pre_biased_coordinate_system(&m);
|
|
|
|
g = FusionCalibrationInertial(
|
|
g,
|
|
gyroscopeMisalignment,
|
|
gyroscopeSensitivity,
|
|
gyroscopeOffset
|
|
);
|
|
|
|
a = FusionCalibrationInertial(
|
|
a,
|
|
accelerometerMisalignment,
|
|
accelerometerSensitivity,
|
|
accelerometerOffset
|
|
);
|
|
|
|
m = FusionCalibrationInertial(
|
|
m,
|
|
magnetometerMisalignment,
|
|
magnetometerSensitivity,
|
|
magnetometerOffset
|
|
);
|
|
|
|
iterate_iron_offset_estimation(
|
|
&m,
|
|
&softIronMatrix,
|
|
&hardIronOffset
|
|
);
|
|
|
|
if (device->calibration) {
|
|
device->calibration->softIronMatrix = softIronMatrix;
|
|
device->calibration->hardIronOffset = hardIronOffset;
|
|
}
|
|
|
|
m = FusionCalibrationMagnetic(
|
|
m,
|
|
softIronMatrix,
|
|
hardIronOffset
|
|
);
|
|
|
|
post_biased_coordinate_system(&g, gyroscope);
|
|
post_biased_coordinate_system(&a, accelerometer);
|
|
post_biased_coordinate_system(&m, magnetometer);
|
|
}
|
|
|
|
device_imu_error_type device_imu_clear(device_imu_type* device) {
|
|
return device_imu_read(device, 10);
|
|
}
|
|
|
|
device_imu_error_type device_imu_calibrate(device_imu_type* device, uint32_t iterations, bool gyro, bool accel, bool magnet) {
|
|
if (!device) {
|
|
device_imu_error("No device");
|
|
return DEVICE_IMU_ERROR_NO_DEVICE;
|
|
}
|
|
|
|
if (!device->handle) {
|
|
device_imu_error("No handle");
|
|
return DEVICE_IMU_ERROR_NO_HANDLE;
|
|
}
|
|
|
|
if (!device->calibration) {
|
|
device_imu_error("No calibration allocated");
|
|
return DEVICE_IMU_ERROR_NO_ALLOCATION;
|
|
}
|
|
|
|
if (sizeof(device_imu_packet_type) > device->max_payload_size) {
|
|
device_imu_error("Not proper size");
|
|
return DEVICE_IMU_ERROR_WRONG_SIZE;
|
|
}
|
|
|
|
device_imu_packet_type packet;
|
|
int transferred;
|
|
|
|
bool initialized = false;
|
|
|
|
FusionVector cal_gyroscope;
|
|
FusionVector cal_accelerometer;
|
|
|
|
FusionMatrix softIronMatrix;
|
|
FusionVector hardIronOffset;
|
|
|
|
const float factor = iterations > 0? 1.0f / ((float) iterations) : 0.0f;
|
|
|
|
FusionVector prev_accel;
|
|
while (iterations > 0) {
|
|
memset(&packet, 0, sizeof(device_imu_packet_type));
|
|
|
|
transferred = hid_read(
|
|
device->handle,
|
|
(uint8_t*) &packet,
|
|
sizeof(device_imu_packet_type)
|
|
);
|
|
|
|
if (transferred == -1) {
|
|
device_imu_error("Device may be unplugged");
|
|
return DEVICE_IMU_ERROR_UNPLUGGED;
|
|
}
|
|
|
|
if (transferred == 0) {
|
|
continue;
|
|
}
|
|
|
|
if (sizeof(device_imu_packet_type) != transferred) {
|
|
device_imu_error("Unexpected packet size");
|
|
return DEVICE_IMU_ERROR_UNEXPECTED;
|
|
}
|
|
|
|
if ((packet.signature[0] != 0x01) || (packet.signature[1] != 0x02)) {
|
|
continue;
|
|
}
|
|
|
|
FusionVector gyroscope;
|
|
FusionVector accelerometer;
|
|
FusionVector magnetometer;
|
|
|
|
readIMU_from_packet(&packet, &gyroscope, &accelerometer, &magnetometer);
|
|
|
|
pre_biased_coordinate_system(&gyroscope);
|
|
pre_biased_coordinate_system(&accelerometer);
|
|
pre_biased_coordinate_system(&magnetometer);
|
|
|
|
if (initialized) {
|
|
cal_gyroscope = FusionVectorAdd(cal_gyroscope, gyroscope);
|
|
cal_accelerometer = FusionVectorAdd(cal_accelerometer, FusionVectorSubtract(accelerometer, prev_accel));
|
|
} else {
|
|
cal_gyroscope = gyroscope;
|
|
cal_accelerometer = FUSION_VECTOR_ZERO;
|
|
}
|
|
|
|
prev_accel = accelerometer;
|
|
|
|
iterate_iron_offset_estimation(
|
|
&magnetometer,
|
|
&softIronMatrix,
|
|
&hardIronOffset
|
|
);
|
|
|
|
iterations--;
|
|
}
|
|
|
|
if (factor > 0.0f) {
|
|
if (gyro) {
|
|
device->calibration->gyroscopeOffset = FusionVectorAdd(
|
|
device->calibration->gyroscopeOffset,
|
|
FusionVectorMultiplyScalar(
|
|
cal_gyroscope,
|
|
FusionDegreesToRadians(factor)
|
|
)
|
|
);
|
|
}
|
|
|
|
if (accel) {
|
|
device->calibration->accelerometerOffset = FusionVectorAdd(
|
|
device->calibration->accelerometerOffset,
|
|
FusionVectorMultiplyScalar(
|
|
cal_accelerometer,
|
|
factor * GRAVITY_G
|
|
)
|
|
);
|
|
}
|
|
|
|
if (magnet) {
|
|
device->calibration->softIronMatrix = softIronMatrix;
|
|
device->calibration->hardIronOffset = hardIronOffset;
|
|
}
|
|
}
|
|
|
|
return DEVICE_IMU_ERROR_NO_ERROR;
|
|
}
|
|
|
|
device_imu_error_type device_imu_read(device_imu_type* device, int timeout) {
|
|
if (!device) {
|
|
device_imu_error("No device");
|
|
return DEVICE_IMU_ERROR_NO_DEVICE;
|
|
}
|
|
|
|
if (!device->handle) {
|
|
device_imu_error("No handle");
|
|
return DEVICE_IMU_ERROR_NO_HANDLE;
|
|
}
|
|
|
|
if (sizeof(device_imu_packet_type) > device->max_payload_size) {
|
|
device_imu_error("Not proper size");
|
|
return DEVICE_IMU_ERROR_WRONG_SIZE;
|
|
}
|
|
|
|
device_imu_packet_type packet;
|
|
memset(&packet, 0, sizeof(device_imu_packet_type));
|
|
|
|
int transferred = hid_read_timeout(
|
|
device->handle,
|
|
(uint8_t*) &packet,
|
|
sizeof(device_imu_packet_type),
|
|
timeout
|
|
);
|
|
|
|
if (transferred == -1) {
|
|
device_imu_error("Device may be unplugged");
|
|
return DEVICE_IMU_ERROR_UNPLUGGED;
|
|
}
|
|
|
|
if (transferred == 0) {
|
|
return DEVICE_IMU_ERROR_NO_ERROR;
|
|
}
|
|
|
|
if (sizeof(device_imu_packet_type) != transferred) {
|
|
device_imu_error("Unexpected packet size");
|
|
return DEVICE_IMU_ERROR_UNEXPECTED;
|
|
}
|
|
|
|
const uint64_t timestamp = le64toh(packet.timestamp);
|
|
|
|
if ((packet.signature[0] == 0xaa) && (packet.signature[1] == 0x53)) {
|
|
device_imu_callback(device, timestamp, DEVICE_IMU_EVENT_INIT);
|
|
return DEVICE_IMU_ERROR_NO_ERROR;
|
|
}
|
|
|
|
if ((packet.signature[0] != 0x01) || (packet.signature[1] != 0x02)) {
|
|
device_imu_error("Not matching signature");
|
|
return DEVICE_IMU_ERROR_WRONG_SIGNATURE;
|
|
}
|
|
|
|
const uint64_t delta = timestamp - device->last_timestamp;
|
|
const float deltaTime = (float) ((double) delta / 1e9);
|
|
|
|
device->last_timestamp = timestamp;
|
|
|
|
int16_t temperature = pack16bit_signed(packet.temperature);
|
|
|
|
// According to the ICM-42688-P datasheet: (offset: 25 °C, sensitivity: 132.48 LSB/°C)
|
|
device->temperature = ((float) temperature) / 132.48f + 25.0f;
|
|
|
|
FusionVector gyroscope;
|
|
FusionVector accelerometer;
|
|
FusionVector magnetometer;
|
|
|
|
readIMU_from_packet(&packet, &gyroscope, &accelerometer, &magnetometer);
|
|
apply_calibration(device, &gyroscope, &accelerometer, &magnetometer);
|
|
|
|
if (device->offset) {
|
|
gyroscope = FusionOffsetUpdate((FusionOffset*) device->offset, gyroscope);
|
|
}
|
|
|
|
#ifndef NDEBUG
|
|
printf("G: %.2f %.2f %.2f\n", gyroscope.axis.x, gyroscope.axis.y, gyroscope.axis.z);
|
|
printf("A: %.2f %.2f %.2f\n", accelerometer.axis.x, accelerometer.axis.y, accelerometer.axis.z);
|
|
printf("M: %.2f %.2f %.2f\n", magnetometer.axis.x, magnetometer.axis.y, magnetometer.axis.z);
|
|
#endif
|
|
|
|
if (device->ahrs) {
|
|
if (isnan(magnetometer.axis.x) || isnan(magnetometer.axis.x) || isnan(magnetometer.axis.x)) {
|
|
FusionAhrsUpdateNoMagnetometer((FusionAhrs*) device->ahrs, gyroscope, accelerometer, deltaTime);
|
|
} else {
|
|
/* The magnetometer seems to make results of sensor fusion generally worse. So it is not used currently. */
|
|
// FusionAhrsUpdate((FusionAhrs*) device->ahrs, gyroscope, accelerometer, magnetometer, deltaTime);
|
|
FusionAhrsUpdateNoMagnetometer((FusionAhrs*) device->ahrs, gyroscope, accelerometer, deltaTime);
|
|
}
|
|
|
|
const device_imu_quat_type orientation = device_imu_get_orientation(device->ahrs);
|
|
|
|
// TODO: fix detection of this case; quat.x as a nan value is only a side-effect of some issue with ahrs or
|
|
// the gyro/accel/magnet readings
|
|
if (isnan(orientation.x) || isnan(orientation.y) || isnan(orientation.z) || isnan(orientation.w)) {
|
|
device_imu_error("Invalid orientation reading");
|
|
return DEVICE_IMU_ERROR_INVALID_VALUE;
|
|
}
|
|
}
|
|
|
|
device_imu_callback(device, timestamp, DEVICE_IMU_EVENT_UPDATE);
|
|
return DEVICE_IMU_ERROR_NO_ERROR;
|
|
}
|
|
|
|
device_imu_vec3_type device_imu_get_earth_acceleration(const device_imu_ahrs_type* ahrs) {
|
|
FusionVector acceleration = ahrs? FusionAhrsGetEarthAcceleration((const FusionAhrs*) ahrs) : FUSION_VECTOR_ZERO;
|
|
device_imu_vec3_type a;
|
|
a.x = acceleration.axis.x;
|
|
a.y = acceleration.axis.y;
|
|
a.z = acceleration.axis.z;
|
|
return a;
|
|
}
|
|
|
|
device_imu_vec3_type device_imu_get_linear_acceleration(const device_imu_ahrs_type* ahrs) {
|
|
FusionVector acceleration = ahrs? FusionAhrsGetLinearAcceleration((const FusionAhrs*) ahrs) : FUSION_VECTOR_ZERO;
|
|
device_imu_vec3_type a;
|
|
a.x = acceleration.axis.x;
|
|
a.y = acceleration.axis.y;
|
|
a.z = acceleration.axis.z;
|
|
return a;
|
|
}
|
|
|
|
device_imu_quat_type device_imu_get_orientation(const device_imu_ahrs_type* ahrs) {
|
|
FusionQuaternion quaternion = ahrs? FusionAhrsGetQuaternion((const FusionAhrs*) ahrs) : FUSION_IDENTITY_QUATERNION;
|
|
device_imu_quat_type q;
|
|
q.x = quaternion.element.x;
|
|
q.y = quaternion.element.y;
|
|
q.z = quaternion.element.z;
|
|
q.w = quaternion.element.w;
|
|
return q;
|
|
}
|
|
|
|
device_imu_euler_type device_imu_get_euler(device_imu_quat_type quat) {
|
|
FusionQuaternion quaternion;
|
|
quaternion.element.x = quat.x;
|
|
quaternion.element.y = quat.y;
|
|
quaternion.element.z = quat.z;
|
|
quaternion.element.w = quat.w;
|
|
FusionEuler euler = FusionQuaternionToEuler(quaternion);
|
|
device_imu_euler_type e;
|
|
e.roll = euler.angle.roll;
|
|
e.pitch = euler.angle.pitch;
|
|
e.yaw = euler.angle.yaw;
|
|
return e;
|
|
}
|
|
|
|
uint32_t device_imu_get_num_of_cameras(device_imu_type *device) {
|
|
if (!device->calibration) {
|
|
return 0;
|
|
}
|
|
|
|
return device->calibration->cam.num_cameras;
|
|
}
|
|
|
|
const device_imu_camera_type* device_imu_get_camera(const device_imu_type *device, uint32_t index) {
|
|
if ((!device->calibration) || (!device->calibration->cam.cameras)) {
|
|
return NULL;
|
|
}
|
|
|
|
return &(device->calibration->cam.cameras[index]);
|
|
}
|
|
|
|
uint32_t device_imu_camera_get_num_of_sensors(const device_imu_camera_type *camera) {
|
|
if (!camera) {
|
|
return 0;
|
|
}
|
|
|
|
return camera->num_sensors;
|
|
}
|
|
|
|
const device_imu_camera_sensor_type* device_imu_camera_get_sensor(const device_imu_camera_type *camera, uint32_t index) {
|
|
if (!camera->sensors) {
|
|
return NULL;
|
|
}
|
|
|
|
return &(camera->sensors[index]);
|
|
}
|
|
|
|
device_imu_mat3x3_type device_imu_sensor_get_rotation(const device_imu_camera_sensor_type *sensor) {
|
|
device_imu_mat3x3_type rotation;
|
|
rotation.m[0] = sensor->cameraMisalignment.array[0][0];
|
|
rotation.m[1] = sensor->cameraMisalignment.array[0][1];
|
|
rotation.m[2] = sensor->cameraMisalignment.array[0][2];
|
|
rotation.m[3] = sensor->cameraMisalignment.array[1][0];
|
|
rotation.m[4] = sensor->cameraMisalignment.array[1][1];
|
|
rotation.m[5] = sensor->cameraMisalignment.array[1][2];
|
|
rotation.m[6] = sensor->cameraMisalignment.array[2][0];
|
|
rotation.m[7] = sensor->cameraMisalignment.array[2][1];
|
|
rotation.m[8] = sensor->cameraMisalignment.array[2][2];
|
|
return rotation;
|
|
}
|
|
|
|
device_imu_vec3_type device_imu_sensor_get_position(const device_imu_camera_sensor_type *sensor) {
|
|
device_imu_vec3_type position;
|
|
position.x = sensor->cameraOffset.axis.x;
|
|
position.y = sensor->cameraOffset.axis.y;
|
|
position.z = sensor->cameraOffset.axis.z;
|
|
return position;
|
|
}
|
|
|
|
device_imu_size_type device_imu_sensor_get_resolution(const device_imu_camera_sensor_type *sensor) {
|
|
device_imu_size_type resolution;
|
|
resolution.width = sensor->resolution[0];
|
|
resolution.height = sensor->resolution[1];
|
|
return resolution;
|
|
}
|
|
|
|
device_imu_vec2_type device_imu_sensor_get_cc(const device_imu_camera_sensor_type *sensor) {
|
|
device_imu_vec2_type cc;
|
|
cc.x = sensor->cc[0];
|
|
cc.y = sensor->cc[1];
|
|
return cc;
|
|
}
|
|
|
|
device_imu_vec2_type device_imu_sensor_get_fc(const device_imu_camera_sensor_type *sensor) {
|
|
device_imu_vec2_type fc;
|
|
fc.x = sensor->fc[0];
|
|
fc.y = sensor->fc[1];
|
|
return fc;
|
|
}
|
|
|
|
device_imu_error_type device_imu_sensor_get_kc(const device_imu_camera_sensor_type *sensor, uint32_t *num_kc, float *kc) {
|
|
if ((!sensor) || (!num_kc)) {
|
|
device_imu_error("Wrong argument");
|
|
return DEVICE_IMU_ERROR_NO_ALLOCATION;
|
|
}
|
|
|
|
if (!kc) {
|
|
*num_kc = sensor->num_kc;
|
|
} else {
|
|
uint32_t n = *num_kc;
|
|
|
|
if (sensor->num_kc < n) {
|
|
n = sensor->num_kc;
|
|
}
|
|
|
|
for (uint32_t i = 0; i < *num_kc; i++) {
|
|
kc[i] = sensor->kc[i];
|
|
}
|
|
}
|
|
|
|
return DEVICE_IMU_ERROR_NO_ERROR;
|
|
}
|
|
|
|
device_imu_error_type device_imu_close(device_imu_type* device) {
|
|
if (!device) {
|
|
device_imu_error("No device");
|
|
return DEVICE_IMU_ERROR_NO_DEVICE;
|
|
}
|
|
|
|
if (device->calibration) {
|
|
if (device->calibration->cam.cameras) {
|
|
for (uint32_t i = 0; i < device->calibration->cam.num_cameras; i++) {
|
|
if (!device->calibration->cam.cameras[i].sensors) {
|
|
continue;
|
|
}
|
|
|
|
for (uint32_t j = 0; j < device->calibration->cam.cameras[i].num_sensors; j++) {
|
|
if (!device->calibration->cam.cameras[i].sensors[j].kc) {
|
|
continue;
|
|
}
|
|
|
|
free(device->calibration->cam.cameras[i].sensors[j].kc);
|
|
}
|
|
|
|
free(device->calibration->cam.cameras[i].sensors);
|
|
}
|
|
|
|
free(device->calibration->cam.cameras);
|
|
}
|
|
|
|
free(device->calibration);
|
|
}
|
|
|
|
if (device->ahrs) {
|
|
free(device->ahrs);
|
|
}
|
|
|
|
if (device->offset) {
|
|
free(device->offset);
|
|
}
|
|
|
|
if (device->handle) {
|
|
if ((!send_payload_msg_signal(device, DEVICE_IMU_MSG_START_IMU_DATA, 0x0)) ||
|
|
(!recv_payload_msg(device, DEVICE_IMU_MSG_START_IMU_DATA, 0, NULL))) {
|
|
device_imu_error("Failed sending payload to stop imu data stream");
|
|
}
|
|
|
|
hid_close(device->handle);
|
|
}
|
|
|
|
memset(device, 0, sizeof(device_imu_type));
|
|
device_exit();
|
|
|
|
return DEVICE_IMU_ERROR_NO_ERROR;
|
|
}
|