// // Created by thejackimonster on 30.03.23. // // Copyright (c) 2023 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 "device3.h" #include #include #include #include #include #include #include "crc32.h" struct device3_calibration_t { FusionMatrix gyroscopeMisalignment; FusionVector gyroscopeSensitivity; FusionVector gyroscopeOffset; FusionMatrix accelerometerMisalignment; FusionVector accelerometerSensitivity; FusionVector accelerometerOffset; FusionMatrix softIronMatrix; FusionVector hardIronOffset; FusionQuaternion noises; }; static bool send_payload(device3_type* device, uint8_t size, uint8_t* payload) { if (!device->claimed) { return false; } int payload_size = size; if (payload_size > device->max_packet_size_out) { payload_size = device->max_packet_size_out; } int transferred = 0; int error = libusb_bulk_transfer( device->handle, device->endpoint_address_out, payload, payload_size, &transferred, 0 ); if ((0 != error) || (transferred != payload_size)) { perror("ERROR: sending payload failed\n"); return false; } return (transferred == size); } static bool recv_payload(device3_type* device, uint8_t size, uint8_t* payload) { if (!device->claimed) { return false; } int payload_size = size; if (payload_size > device->max_packet_size_in) { payload_size = device->max_packet_size_in; } int transferred = 0; int error = libusb_bulk_transfer( device->handle, device->endpoint_address_in, payload, device->max_packet_size_in, &transferred, 0 ); if (transferred >= payload_size) { transferred = payload_size; } if (error == LIBUSB_ERROR_TIMEOUT) { return false; } if ((0 != error) || (transferred != payload_size)) { perror("ERROR: receiving payload failed\n"); return false; } return (transferred == size); } #define DEVICE3_MSG_GET_CAL_DATA_LENGTH 0x14 #define DEVICE3_MSG_CAL_DATA_GET_NEXT_SEGMENT 0x15 #define DEVICE3_MSG_ALLOCATE_CAL_DATA_BUFFER 0x16 #define DEVICE3_MSG_WRITE_CAL_DATA_SEGMENT 0x17 #define DEVICE3_MSG_FREE_CAL_BUFFER 0x18 #define DEVICE3_MSG_START_IMU_DATA 0x19 #define DEVICE3_MSG_GET_STATIC_ID 0x1A #define DEVICE3_MSG_UNKNOWN 0x1D struct __attribute__((__packed__)) device3_payload_packet_t { uint8_t head; uint32_t checksum; uint16_t length; uint8_t msgid; uint8_t data [56]; }; typedef struct device3_payload_packet_t device3_payload_packet_type; static bool send_payload_msg(device3_type* device, uint8_t msgid, uint8_t len, const uint8_t* data) { static device3_payload_packet_type packet; const uint16_t packet_len = 3 + len; const uint16_t payload_len = 5 + packet_len; packet.head = 0xAA; packet.length = packet_len; packet.msgid = msgid; memcpy(packet.data, data, len); packet.checksum = crc32_checksum((const uint8_t*) (&packet.length), packet.length); return send_payload(device, payload_len, (uint8_t*) (&packet)); } static bool send_payload_msg_signal(device3_type* device, uint8_t msgid, uint8_t signal) { return send_payload_msg(device, msgid, 1, &signal); } static bool recv_payload_msg(device3_type* device, uint8_t msgid, uint8_t len, uint8_t* data) { static device3_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; if (!recv_payload(device, payload_len, (uint8_t*) (&packet))) { return false; } if (packet.msgid != msgid) { return false; } 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) != 3)) { 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; } device3_type* device3_open(device3_event_callback callback) { device3_type* device = (device3_type*) malloc(sizeof(device3_type)); if (!device) { perror("Not allocated!\n"); return NULL; } memset(device, 0, sizeof(device3_type)); device->vendor_id = 0x3318; device->product_id = 0x0424; device->callback = callback; if (0 != libusb_init((libusb_context**) &(device->context))) { perror("No context!\n"); return device; } device->handle = libusb_open_device_with_vid_pid( device->context, device->vendor_id, device->product_id ); if (!device->handle) { perror("No handle!\n"); return device; } libusb_device* dev = libusb_get_device(device->handle); if (!dev) { perror("No dev!\n"); return device; } struct libusb_device_descriptor desc; if (0 != libusb_get_device_descriptor(dev, &desc)) { perror("No desc!\n"); return device; } struct libusb_config_descriptor* config; for (uint8_t i = 0; i < desc.bNumConfigurations; i++) { if (0 != libusb_get_config_descriptor(dev, i, &config)) { continue; } const struct libusb_interface* interface; for (uint8_t j = 0; j < config->bNumInterfaces; j++) { interface = &(config->interface[j]); const struct libusb_interface_descriptor* setting; for (int k = 0; k < interface->num_altsetting; k++) { setting = &(interface->altsetting[k]); if (LIBUSB_CLASS_HID != setting->bInterfaceClass) { continue; } if (3 != setting->bInterfaceNumber) { continue; } device->interface_number = setting->bInterfaceNumber; if (2 != setting->bNumEndpoints) { continue; } device->endpoint_address_in = setting->endpoint[0].bEndpointAddress; device->max_packet_size_in = setting->endpoint[0].wMaxPacketSize; device->endpoint_address_out = setting->endpoint[1].bEndpointAddress; device->max_packet_size_out = setting->endpoint[1].wMaxPacketSize; } } } if (3 != device->interface_number) { perror("No interface!\n"); return device; } if (1 == libusb_kernel_driver_active(device->handle, device->interface_number)) { if (0 == libusb_detach_kernel_driver(device->handle, device->interface_number)) { device->detached = true; } else { perror("Not detached!\n"); return device; } } if (0 == libusb_claim_interface(device->handle, device->interface_number)) { device->claimed = true; } if (!send_payload_msg(device, DEVICE3_MSG_GET_STATIC_ID, 0, NULL)) { return device; } uint32_t static_id = 0; if (recv_payload_msg(device, DEVICE3_MSG_GET_STATIC_ID, 4, (uint8_t*) &static_id)) { device->static_id = static_id; } else { device->static_id = 0x20220101; } device->calibration = malloc(sizeof(device3_calibration_type)); device3_reset_calibration(device); if (!send_payload_msg(device, DEVICE3_MSG_GET_CAL_DATA_LENGTH, 0, NULL)) { return device; } uint32_t calibration_len = 0; if (recv_payload_msg(device, DEVICE3_MSG_GET_CAL_DATA_LENGTH, 4, (uint8_t*) &calibration_len)) { char* calibration_data = malloc(calibration_len); uint32_t position = 0; while (position < calibration_len) { const uint32_t remaining = (calibration_len - position); if (!send_payload_msg(device, DEVICE3_MSG_CAL_DATA_GET_NEXT_SEGMENT, 0, NULL)) { break; } const uint8_t next = (remaining > 56? 56 : remaining); if (!recv_payload_msg(device, DEVICE3_MSG_CAL_DATA_GET_NEXT_SEGMENT, next, (uint8_t*) calibration_data + position)) { break; } position += next; } 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")); FusionVector gyro_p_mag = json_object_get_vector(json_object_object_get(dev1, "gyro_p_mag")); FusionQuaternion gyro_q_mag = json_object_get_quaternion(json_object_object_get(dev1, "gyro_q_mag")); FusionQuaternion imu_noises = json_object_get_quaternion(json_object_object_get(dev1, "imu_noises")); FusionVector mag_bias = json_object_get_vector(json_object_object_get(dev1, "mag_bias")); 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")); FusionMatrix gyro_misalignment = FusionQuaternionToMatrix(accel_q_gyro); FusionQuaternion accel_q_mag = FusionQuaternionMultiply(accel_q_gyro, gyro_q_mag); device->calibration->gyroscopeMisalignment = gyro_misalignment; device->calibration->gyroscopeSensitivity = scale_gyro; device->calibration->gyroscopeOffset = gyro_bias; device->calibration->accelerometerSensitivity = scale_accel; device->calibration->accelerometerOffset = accel_bias; FusionMatrix mag_misalignment = FusionQuaternionToMatrix(accel_q_mag); mag_bias = FusionMatrixMultiplyVector(mag_misalignment, mag_bias); device->calibration->softIronMatrix = mag_misalignment; device->calibration->hardIronOffset = FusionVectorAdd(gyro_p_mag, mag_bias); device->calibration->noises = imu_noises; json_tokener_free(tokener); free(calibration_data); } if (!send_payload_msg_signal(device, DEVICE3_MSG_START_IMU_DATA, 0x1)) { return device; } const uint32_t SAMPLE_RATE = 1000; device->offset = malloc(sizeof(FusionOffset)); device->ahrs = malloc(sizeof(FusionAhrs)); FusionOffsetInitialise((FusionOffset*) device->offset, SAMPLE_RATE); FusionAhrsInitialise((FusionAhrs*) device->ahrs); const FusionAhrsSettings settings = { .convention = FusionConventionNwu, .gain = 0.5f, .accelerationRejection = 10.0f, .magneticRejection = 20.0f, .rejectionTimeout = 5 * SAMPLE_RATE, /* 5 seconds */ }; FusionAhrsSetSettings((FusionAhrs*) device->ahrs, &settings); return device; } void device3_reset_calibration(device3_type* device) { if (!device) { perror("No device!\n"); return; } if (!device->calibration) { perror("Not allocated!\n"); return; } 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->accelerometerMisalignment.array[2][2] = -1.0f; 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; } int device3_load_calibration(device3_type* device, const char* path) { if (!device) { perror("No device!\n"); return -1; } if (!device->calibration) { perror("Not allocated!\n"); return -2; } FILE* file = fopen(path, "rb"); if (!file) { perror("No file opened!\n"); return -3; } size_t count; count = fread(device->calibration, 1, sizeof(device3_calibration_type), file); if (sizeof(device3_calibration_type) != count) { perror("Not fully loaded!\n"); } if (0 != fclose(file)) { perror("No file closed!\n"); return -4; } return 0; } int device3_save_calibration(device3_type* device, const char* path) { if (!device) { perror("No device!\n"); return -1; } if (!device->calibration) { perror("Not allocated!\n"); return -2; } FILE* file = fopen(path, "wb"); if (!file) { perror("No file opened!\n"); return -3; } size_t count; count = fwrite(device->calibration, 1, sizeof(device3_calibration_type), file); if (sizeof(device3_calibration_type) != count) { perror("Not fully saved!\n"); } if (0 != fclose(file)) { perror("No file closed!\n"); return -4; } return 0; } static void device3_callback(device3_type* device, uint64_t timestamp, device3_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[0]) | (data[1] << 8); 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 void readIMU_from_packet(const device3_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_swap(packet->magnetic_x); int16_t magnet_y = pack16bit_signed_swap(packet->magnetic_y); int16_t magnet_z = pack16bit_signed_swap(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 apply_calibration(const device3_type* device, FusionVector* gyroscope, FusionVector* accelerometer, FusionVector* magnetometer) { FusionMatrix gyroscopeMisalignment; FusionVector gyroscopeSensitivity; FusionVector gyroscopeOffset; FusionMatrix accelerometerMisalignment; FusionVector accelerometerSensitivity; FusionVector accelerometerOffset; 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; 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; accelerometerMisalignment.array[2][2] = -1.0f; softIronMatrix = FUSION_IDENTITY_MATRIX; hardIronOffset = FUSION_VECTOR_ZERO; } *gyroscope = FusionCalibrationInertial( *gyroscope, gyroscopeMisalignment, gyroscopeSensitivity, gyroscopeOffset ); *accelerometer = FusionCalibrationInertial( *accelerometer, accelerometerMisalignment, accelerometerSensitivity, accelerometerOffset ); *magnetometer = FusionCalibrationMagnetic( *magnetometer, softIronMatrix, hardIronOffset ); } int device3_calibrate(device3_type* device, uint32_t iterations, bool gyro, bool accel, bool magnet) { if (!device) { perror("No device!\n"); return -1; } if (!device->claimed) { perror("Not claimed!\n"); return -2; } if (device->max_packet_size_in != sizeof(device3_packet_type)) { perror("Not proper size!\n"); return -3; } device3_packet_type packet; int transferred, error; bool initialized = false; FusionVector cal_gyroscope; FusionVector cal_accelerometer; FusionVector cal_magnetometer [2]; const float factor = iterations > 0? 1.0f / ((float) iterations) : 0.0f; while (iterations > 0) { memset(&packet, 0, sizeof(device3_packet_type)); transferred = 0; error = libusb_bulk_transfer( device->handle, device->endpoint_address_in, (uint8_t*) &packet, device->max_packet_size_in, &transferred, 0 ); if (error == LIBUSB_ERROR_TIMEOUT) { continue; } if ((0 != error) || (device->max_packet_size_in != transferred)) { perror("Not expected issue!\n"); return -4; } if ((packet.signature[0] != 0x01) || (packet.signature[1] != 0x02)) { continue; } FusionVector gyroscope; FusionVector accelerometer; FusionVector magnetometer; readIMU_from_packet(&packet, &gyroscope, &accelerometer, &magnetometer); if (initialized) { cal_gyroscope = FusionVectorAdd(cal_gyroscope, gyroscope); cal_accelerometer = FusionVectorAdd(cal_accelerometer, accelerometer); } else { cal_gyroscope = gyroscope; cal_accelerometer = accelerometer; } apply_calibration(device, &gyroscope, &accelerometer, &magnetometer); if (initialized) { cal_magnetometer[0].axis.x = min(cal_magnetometer[0].axis.x, magnetometer.axis.x); cal_magnetometer[0].axis.y = min(cal_magnetometer[0].axis.y, magnetometer.axis.y); cal_magnetometer[0].axis.z = min(cal_magnetometer[0].axis.z, magnetometer.axis.z); cal_magnetometer[1].axis.x = max(cal_magnetometer[1].axis.x, magnetometer.axis.x); cal_magnetometer[1].axis.y = max(cal_magnetometer[1].axis.y, magnetometer.axis.y); cal_magnetometer[1].axis.z = max(cal_magnetometer[1].axis.z, magnetometer.axis.z); } else { cal_magnetometer[0] = magnetometer; cal_magnetometer[1] = magnetometer; initialized = true; } iterations--; } if (factor > 0.0f) { if (gyro) { device->calibration->gyroscopeOffset = FusionVectorAdd( device->calibration->gyroscopeOffset, FusionVectorMultiplyScalar( cal_gyroscope, factor ) ); } if (accel) { device->calibration->accelerometerOffset = FusionVectorAdd( device->calibration->accelerometerOffset, FusionVectorMultiplyScalar( cal_accelerometer, factor ) ); } if (magnet) { device->calibration->hardIronOffset = FusionVectorAdd( device->calibration->hardIronOffset, FusionVectorMultiplyScalar( FusionVectorAdd(cal_magnetometer[0], cal_magnetometer[1]), 0.5f ) ); } } return 0; } int device3_read(device3_type* device, int timeout) { if (!device) { perror("No device!\n"); return -1; } if (!device->claimed) { perror("Not claimed!\n"); return -2; } if (device->max_packet_size_in != sizeof(device3_packet_type)) { perror("Not proper size!\n"); return -3; } device3_packet_type packet; memset(&packet, 0, sizeof(device3_packet_type)); int transferred = 0; int error = libusb_bulk_transfer( device->handle, device->endpoint_address_in, (uint8_t*) &packet, device->max_packet_size_in, &transferred, timeout ); if (error == LIBUSB_ERROR_TIMEOUT) { return 1; } if ((0 != error) || (device->max_packet_size_in != transferred)) { perror("Not expected issue!\n"); return -4; } const uint64_t timestamp = packet.timestamp; if ((packet.signature[0] == 0xaa) && (packet.signature[1] == 0x53)) { device3_callback(device, timestamp, DEVICE3_EVENT_INIT); return 0; } if ((packet.signature[0] != 0x01) || (packet.signature[1] != 0x02)) { perror("Not matching signature!\n"); return -5; } 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); gyroscope = FusionOffsetUpdate((FusionOffset*) device->offset, gyroscope); FusionAhrsUpdate((FusionAhrs*) device->ahrs, gyroscope, accelerometer, magnetometer, deltaTime); device3_callback(device, timestamp, DEVICE3_EVENT_UPDATE); return 0; } device3_vec3_type device3_get_earth_acceleration(const device3_ahrs_type* ahrs) { FusionVector acceleration = FusionAhrsGetEarthAcceleration((const FusionAhrs*) ahrs); device3_vec3_type a; a.x = acceleration.axis.x; a.y = acceleration.axis.y; a.z = acceleration.axis.z; return a; } device3_vec3_type device3_get_linear_acceleration(const device3_ahrs_type* ahrs) { FusionVector acceleration = FusionAhrsGetLinearAcceleration((const FusionAhrs*) ahrs); device3_vec3_type a; a.x = acceleration.axis.x; a.y = acceleration.axis.y; a.z = acceleration.axis.z; return a; } device3_quat_type device3_get_orientation(const device3_ahrs_type* ahrs) { FusionQuaternion quaternion = FusionAhrsGetQuaternion((const FusionAhrs*) ahrs); device3_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; } device3_vec3_type device3_get_euler(device3_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); device3_vec3_type e; e.x = euler.angle.pitch; e.y = euler.angle.roll; e.z = euler.angle.yaw; return e; } void device3_close(device3_type* device) { if (!device) { perror("No device!\n"); return; } if (device->calibration) { free(device->calibration); } if (device->ahrs) { free(device->ahrs); } if (device->offset) { free(device->offset); } if ((device->claimed) && (0 == libusb_release_interface(device->handle, device->interface_number))) { device->claimed = false; } if ((device->detached) && (0 == libusb_attach_kernel_driver(device->handle, device->interface_number))) { device->detached = false; } if (device->handle) { libusb_close(device->handle); device->handle = NULL; } if (device->context) { libusb_exit(device->context); device->context = NULL; } free(device); }