From 7087c543f391a8b9dbe646359d212b14876484f4 Mon Sep 17 00:00:00 2001 From: Lorenzo Cucchi <lorenzo.cucchi@skywarder.eu> Date: Thu, 18 May 2023 22:49:38 +0200 Subject: [PATCH] [VN300] Initial commit files definition --- src/shared/sensors/VN300/VN300.cpp | 496 +++++++++++++++++++++++++++ src/shared/sensors/VN300/VN300.h | 423 +++++++++++++++++++++++ src/shared/sensors/VN300/VN300Data.h | 105 ++++++ 3 files changed, 1024 insertions(+) create mode 100644 src/shared/sensors/VN300/VN300.cpp create mode 100644 src/shared/sensors/VN300/VN300.h create mode 100644 src/shared/sensors/VN300/VN300Data.h diff --git a/src/shared/sensors/VN300/VN300.cpp b/src/shared/sensors/VN300/VN300.cpp new file mode 100644 index 000000000..16a4946c8 --- /dev/null +++ b/src/shared/sensors/VN300/VN300.cpp @@ -0,0 +1,496 @@ +/* Copyright (c) 2023 Skyward Experimental Rocketry + * Author: Lorenzo Cucchi + * + * 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 "VN300.h" + +#include <drivers/timer/TimestampTimer.h> + +namespace Boardcore +{ +VN300::VN300(USARTType *portNumber, USARTInterface::Baudrate baudRate, + CRCOptions crc, uint16_t samplePeriod) + : portNumber(portNumber), baudRate(baudRate), crc(crc) +{ + this->samplePeriod = samplePeriod; +} + +bool VN300::init() +{ + SensorErrors backup = lastError; + + // If already initialized + if (isInit) + { + lastError = SensorErrors::ALREADY_INIT; + LOG_WARN(logger, "Sensor vn300 already initilized"); + return true; + } + + // Allocate the receive vector + recvString = new char[recvStringMaxDimension]; + + // Allocate the pre loaded strings based on the user selected crc + if (crc == CRCOptions::CRC_ENABLE_16) + { + preSampleImuString = new string("$VNRRG,15*92EA\n"); + preSampleTempPressString = new string("$VNRRG,54*4E0F\n"); + } + else + { + preSampleImuString = new string("$VNRRG,15*77\n"); + preSampleTempPressString = new string("$VNRRG,54*72\n"); + } + + // Set the error to init fail and if the init process goes without problem + // i restore it to the last error + lastError = SensorErrors::INIT_FAIL; + + if (recvString == NULL) + { + LOG_ERR(logger, "Unable to initialize the receive vn300 string"); + return false; + } + + if (!configDefaultSerialPort()) + { + LOG_ERR(logger, "Unable to config the default vn100 serial port"); + return false; + } + + if (!setCrc(false)) + { + LOG_ERR(logger, "Unable to set the vn300 user selected CRC"); + return false; + } + + if (!disableAsyncMessages(false)) + { + LOG_ERR(logger, "Unable to disable async messages from vn100"); + return false; + } + + if (!configUserSerialPort()) + { + LOG_ERR(logger, "Unable to config the user vn100 serial port"); + return false; + } + + // I need to repeat this in case of a non default + // serial port communication at the beginning + if (!setCrc(true)) + { + LOG_ERR(logger, "Unable to set the vn100 user selected CRC"); + return false; + } + + if (!disableAsyncMessages(true)) + { + LOG_ERR(logger, "Unable to disable async messages from vn100"); + return false; + } + + if (!this->start()) + { + LOG_ERR(logger, "Unable to start the sampling thread"); + return false; + } + + // Set the isInit flag true + isInit = true; + + // All good i restore the actual last error + lastError = backup; + + return true; +} + +void VN300::run() +{ + while (!shouldStop()) + { + long long initialTime = miosix::getTick(); + + // Sample the data locking the mutex + miosix::Lock<FastMutex> l(mutex); + threadSample = sampleData(); + + // Sleep for the sampling period + miosix::Thread::sleepUntil(initialTime + samplePeriod); + } +} + +VN300Data VN300::sampleData() +{ + if (!isInit) + { + lastError = SensorErrors::NOT_INIT; + LOG_WARN(logger, + "Unable to sample due to not initialized vn100 sensor"); + return lastSample; + } + + // Before sampling i check for errors + if (lastError != SensorErrors::NO_ERRORS) + { + return lastSample; + } + + // Returns Quaternion, Magnetometer, Accelerometer and Gyro + if (!(serialInterface->writeString(preSampleImuString->c_str()))) + { + // If something goes wrong i return the last sampled data + return lastSample; + } + + // Wait some time + // TODO dimension the time + miosix::Thread::sleep(1); + + if (!recvStringCommand(recvString, recvStringMaxDimension)) + { + // If something goes wrong i return the last sampled data + return lastSample; + } + + if (!verifyChecksum(recvString, recvStringLength)) + { + LOG_WARN(logger, "Vn100 sampling message invalid checksum"); + // If something goes wrong i return the last sampled data + return lastSample; + } + + // Now i have to parse the data + QuaternionData quat = sampleQuaternion(); + MagnetometerData mag = sampleMagnetometer(); + AccelerometerData acc = sampleAccelerometer(); + GyroscopeData gyro = sampleGyroscope(); + + // Returns Magnetometer, Accelerometer, Gyroscope, Temperature and Pressure + // (UNCOMPENSATED) DO NOT USE THESE MAGNETOMETER, ACCELEROMETER AND + // GYROSCOPE VALUES + if (!(serialInterface->writeString(preSampleTempPressString->c_str()))) + { + // If something goes wrong i return the last sampled data + return lastSample; + } + + // Wait some time + // TODO dimension the time + miosix::Thread::sleep(1); + + if (!recvStringCommand(recvString, recvStringMaxDimension)) + { + // If something goes wrong i return the last sampled data + return lastSample; + } + + if (!verifyChecksum(recvString, recvStringLength)) + { + LOG_WARN(logger, "Vn100 sampling message invalid checksum"); + // If something goes wrong i return the last sampled data + return lastSample; + } + + // Parse the data + TemperatureData temp = sampleTemperature(); + PressureData press = samplePressure(); + + return VN100Data(quat, mag, acc, gyro, temp, press); +} + +bool VN300::disableAsyncMessages(bool waitResponse) +{ + // Command string + std::string command = + "VNWRG,06,00"; // Put 0 in register number 6 (ASYNC Config) + + // Send the command + if (!sendStringCommand(command)) + { + return false; + } + + // Read the answer + if (waitResponse) + { + recvStringCommand(recvString, recvStringMaxDimension); + } + + return true; +} + +bool VN300::AsyncPauseCommand(bool waitResponse, bool selection) +{ + if (selection) + { + if (!sendStringCommand(ASYNC_PAUSE_COMMAND)) + { + return false; + } + } + else + { + if (!sendStringCommand(ASYNC_RESUME_COMMAND)) + { + return false; + } + } + + // Read the answer + if (waitResponse) + { + recvStringCommand(recvString, recvStringMaxDimension); + } + + return true; +} + +bool VN300::configDefaultSerialPort() +{ + // Initial default settings + serialInterface = new USART(portNumber, USARTInterface::Baudrate::B115200); + + // Check correct serial init + return serialInterface->init(); +} + +/** + * Even if the user configured baudrate is the default, I want to reset the + * buffer to clean the junk. + */ +bool VN300::configUserSerialPort() +{ + std::string command; + + // I format the command to change baud rate + command = fmt::format("{}{}", "VNWRG,5,", static_cast<int>(baudRate)); + + // I can send the command + if (!sendStringCommand(command)) + { + return false; + } + + // Destroy the serial object + delete serialInterface; + + // I can open the serial with user's baud rate + serialInterface = new USART(portNumber, baudRate); + + // Check correct serial init + return serialInterface->init(); +} + +bool VN300::setCrc(bool waitResponse) +{ + // Command for the crc change + std::string command; + CRCOptions backup = crc; + + // Check what type of crc is selected + if (crc == CRCOptions::CRC_ENABLE_16) + { + // The 3 inside the command is the 16bit select. The others are default + // values + command = "VNRRG,30,0,0,0,0,3,0,1"; + } + else + { + // Even if the CRC is not enabled i put the 8 bit + // checksum because i need to know how many 'X' add at the end + // of every command sent + command = "VNRRG,30,0,0,0,0,1,0,1"; + } + + // I need to send the command in both crc because i don't know what type + // of crc is previously selected. So in order to get the command accepted + // i need to do it two times with different crc. + crc = CRCOptions::CRC_ENABLE_8; + + // Send the command + if (!sendStringCommand(command)) + { + return false; + } + + // Read the answer + if (waitResponse) + { + recvStringCommand(recvString, recvStringMaxDimension); + } + + crc = CRCOptions::CRC_ENABLE_16; + + // Send the command + if (!sendStringCommand(command)) + { + return false; + } + + // Read the answer + if (waitResponse) + { + recvStringCommand(recvString, recvStringMaxDimension); + } + + // Restore the crc + crc = backup; + + return true; +} + +bool VN300::sendStringCommand(std::string command) +{ + if (crc == CRCOptions::CRC_ENABLE_8) + { + char checksum[4]; // 2 hex + \n + \0 + // I convert the calculated checksum in hex using itoa + itoa(calculateChecksum8((uint8_t *)command.c_str(), command.length()), + checksum, 16); + checksum[2] = '\n'; + checksum[3] = '\0'; + // I concatenate + command = fmt::format("{}{}{}{}", "$", command, "*", checksum); + } + else if (crc == CRCOptions::CRC_ENABLE_16) + { + char checksum[6]; // 4 hex + \n + \0 + // I convert the calculated checksum in hex using itoa + itoa(calculateChecksum16((uint8_t *)command.c_str(), command.length()), + checksum, 16); + checksum[4] = '\n'; + checksum[5] = '\0'; + // I concatenate + command = fmt::format("{}{}{}{}", "$", command, "*", checksum); + } + else + { + // No checksum, i add only 'XX' at the end and not 'XXXX' because + // in cas of CRC_NO the enabled crc is 8 bit + command = fmt::format("{}{}{}", "$", command, "*XX\n"); + } + + // I send the final command + if (!serialInterface->writeString(command.c_str())) + { + return false; + } + + // Wait some time + // TODO dimension the time + miosix::Thread::sleep(500); + + return true; +} + +bool VN300::verifyChecksum(char *command, int length) +{ + int checksumOffset = 0; + + // I look for the checksum position + while (checksumOffset < length && command[checksumOffset] != '*') + { + checksumOffset++; + } + + if (checksumOffset == length) + { + // The command doesn't have any checksum + TRACE("No checksum in the command!\n"); + return false; + } + + // Check based on the user selected crc type + if (crc == CRCOptions::CRC_ENABLE_16) + { + if (length != checksumOffset + 5) // 4 hex chars + 1 of position + { + TRACE("16 bit Checksum wrong length: %d != %d --> %s\n", length, + checksumOffset + 5, command); + return false; + } + + // Calculate the checksum and verify (comparison between numerical + // checksum to avoid string bugs e.g 0856 != 865) + if (strtol(command + checksumOffset + 1, NULL, 16) != + calculateChecksum16((uint8_t *)(command + 1), checksumOffset - 1)) + { + TRACE("Different checksum: %s\n", command); + return false; + } + } + else if (crc == CRCOptions::CRC_ENABLE_8) + { + if (length != checksumOffset + 3) // 2 hex chars + 1 of position + { + TRACE("8 bit Checksum wrong length: %d != %d --> %s\n", length, + checksumOffset + 3, command); + return false; + } + + // Calculate the checksum and verify (comparison between numerical + // checksum to avoid string bugs e.g 0856 != 865) + if (strtol(command + checksumOffset + 1, NULL, 16) != + calculateChecksum8((uint8_t *)(command + 1), checksumOffset - 1)) + { + TRACE("Different checksum: %s\n", command); + return false; + } + } + + return true; +} + +uint8_t VN300::calculateChecksum8(uint8_t *message, int length) +{ + int i; + uint8_t result = 0x00; + + // Iterate and XOR all of the elements + for (i = 0; i < length; i++) + { + //^ = XOR Operation + result ^= message[i]; + } + + return result; +} + +uint16_t VN300::calculateChecksum16(uint8_t *message, int length) +{ + int i; + uint16_t result = 0x0000; + + // Apply the datasheet definition of CRC16-CCITT + for (i = 0; i < length; i++) + { + result = (uint8_t)(result >> 8) | (result << 8); + result ^= message[i]; + result ^= (uint8_t)(result & 0xff) >> 4; + result ^= result << 12; + result ^= (result & 0x00ff) << 5; + } + + return result; +} + +} // namespace Boardcore \ No newline at end of file diff --git a/src/shared/sensors/VN300/VN300.h b/src/shared/sensors/VN300/VN300.h new file mode 100644 index 000000000..b503615de --- /dev/null +++ b/src/shared/sensors/VN300/VN300.h @@ -0,0 +1,423 @@ +/* Copyright (c) 2023 Skyward Experimental Rocketry + * Author: Lorenzo Cucchi + * + * 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. + */ + +#pragma once + +/** + * @brief Driver for the VN300 IMU. + * + * The VN-300 is a miniature, surface-mount, high-performance GPS-Aided Inertial + * Navigation System (GPS/INS). Incorporating the latest solid-state MEMS sensor + * technology, the VN-300 combines a set of 3-axis accelerometers, 3-axis gyros, + * 3-axis magnetometer, a barometric pressure sensor, two separate 50-channel L1 + * GPS receivers, as well as a 32-bit processor into a miniature aluminum + * enclosure. + * The VN300 supports both binary and ASCII encoding for communication but via + * serial and with the asynchronous mode disabled only ASCII is available. The + * protocol also provides two algorithms to verify the integrity of the messages + * (8 bit checksum and 16 bit CRC-CCITT) both selectable by the user using the + * constructor method. The serial communication also can be established with + * various baud rates: + * - 9600 + * - 19200 + * - 38400 + * - 57600 + * - 115200 + * - 128000 + * - 230400 + * - 460800 + * - 921600 + */ + +#include <ActiveObject.h> +#include <diagnostic/PrintLogger.h> +#include <fmt/format.h> +#include <sensors/Sensor.h> +#include <string.h> +#include <utils/Debug.h> + +#include "VN300Data.h" +#include "drivers/usart/USART.h" + +namespace Boardcore +{ +/** + * @brief Driver class for VN300 + */ +class VN300 : public Sensor<VN300Data>, public ActiveObject +{ +public: + /** + * @brief Checksum Options + */ + enum class CRCOptions : uint8_t + { + CRC_NO = 0x00, + CRC_ENABLE_8 = 0x08, + CRC_ENABLE_16 = 0x10 + }; + /** + * @brief Async options + * ASYNC_NO corresponds to no Async communication + * ASYNC_P1 corresponds to Async comm only on serial port 1 + * ASYNC_P2 corresponds to Async comm only on serial port 2 + * ASYNC_BOTH both serial port are used + */ + enum class AsyncOptions : uint16_t + { + ASYNC_NO = 0x00, + ASYNC_P1 = 0x01, + ASYNC_P2 = 0x02, + ASYNC_BOTH = 0x03 + }; + + enum class RateDivisor : uint16_t + { + RDIV_400_HZ = 0x01, + RDIV_200_HZ = 0x02, + RDIV_100_HZ = 0x04, + RDIV_50_HZ = 0x08, + RDIV_40_HZ = 0x0A + }; + + /** + * @brief Group's bit for binary register setup + */ + enum OutputGroup : uint16_t + { + GROUP_1 = 1 << 0, + GROUP_2 = 1 << 1, + GROUP_3 = 1 << 2, + GROUP_4 = 1 << 3, + GROUP_5 = 1 << 4, + GROUP_6 = 1 << 5, + GROUP_7 = 1 << 6 + }; + + /** + * @brief Common Group + */ + enum GroupField_1 : uint16_t + { + T_START = 1 << 0, + T_GPS = 1 << 1, + T_SYNC = 1 << 2, + Y_P_R = 1 << 3, + QUAT = 1 << 4, + ANG_RATE = 1 << 5, + POS = 1 << 6, + VEL = 1 << 7, + ACCEL = 1 << 8, + IMU = 1 << 9, + MAGPRES = 1 << 10, + DELT_TH = 1 << 11, + INS_STAT = 1 << 12, + SYNC_IN_CNT = 1 << 13, + T_GPS_PPS = 1 << 14 + }; + + /** + * @brief TIME Group + */ + enum GroupField_2 : uint16_t + { + T_START = 1 << 0, + T_GPS = 1 << 1, + GPS_TOW = 1 << 2, + GPS_WEEK = 1 << 3, + T_SYNC = 1 << 4, + T_GPS_PPS = 1 << 5, + T_UTC = 1 << 6, + SYNC_IN_CNT = 1 << 7, + SYNC_OUT_CNT = 1 << 8, + T_STATUS = 1 << 9, + }; + + /** + * @brief IMU Group + */ + enum GroupField_3 : uint16_t + { + IMU_STAT = 1 << 0, + UNC_MAG = 1 << 1, + UNC_ACC = 1 << 2, + UNC_GYRO = 1 << 3, + TEMP = 1 << 4, + PRES = 1 << 5, + DELTA_TH = 1 << 6, + DELTA_VEL = 1 << 7, + MAG = 1 << 8, + ACCEL = 1 << 9, + ANG_RATE = 1 << 10, + }; + + /** + * @brief GNSS1 Group + */ + enum GroupField_4 : uint16_t + { + UTC = 1 << 0, + TOW = 1 << 1, + WEEK = 1 << 2, + N_SATS = 1 << 3, + FIX = 1 << 4, + POS_LLA = 1 << 5, + POS_ECEF = 1 << 6, + VEL_NED = 1 << 7, + VEL_ECEF = 1 << 8, + POS_U = 1 << 9, + VEL_U = 1 << 10, + TIME_U = 1 << 11, + TIME_INFO = 1 << 12, + DOP = 1 << 13, + SAT_INFO = 1 << 14, + RAW_MEAS = 1 << 15 + }; + + /** + * @brief Attitude Group + */ + enum GroupField_5 : uint16_t + { + RESERVED = 1 << 0, + Y_P_R = 1 << 1, + QUAT = 1 << 2, + DCM = 1 << 3, + MAG_NED = 1 << 4, + ACC_NED = 1 << 5, + LIN_ACC_BOD = 1 << 6, + LIN_ACC_NED = 1 << 7, + YprU = 1 << 8, + RESERVED = 1 << 9, + RESERVED = 1 << 10, + RESERVED = 1 << 11 + }; + + /** + * @brief INS Group + */ + enum GroupField_6 : uint16_t + { + INS_STATUS = 1 << 0, + POS_LLA = 1 << 1, + POS_ECEF = 1 << 2, + VEL_BODY = 1 << 3, + VEL_NED = 1 << 4, + VEL_ECEF = 1 << 5, + MAG_ECEF = 1 << 6, + ACC_ECEF = 1 << 7, + LIN_ACC_ECEF = 1 << 8, + POS_U = 1 << 9, + VEL_U = 1 << 10 + }; + + /** + * @brief GNSS2 Group + */ + enum GroupField_7 : uint16_t + { + UTC = 1 << 0, + TOW = 1 << 1, + WEEK = 1 << 2, + N_SATS = 1 << 3, + FIX = 1 << 4, + POS_LLA = 1 << 5, + POS_ECEF = 1 << 6, + VEL_NED = 1 << 7, + VEL_ECEF = 1 << 8, + POS_U = 1 << 9, + VEL_U = 1 << 10, + TIME_U = 1 << 11, + TIME_INFO = 1 << 12, + DOP = 1 << 13, + SAT_INFO = 1 << 14, + RAW_MEAS = 1 << 15 + }; + + VN300(USARTType *portNumber = USART1, + USART::Baudrate baudRate = USART::Baudrate::B921600, + CRCOptions crc = CRCOptions::CRC_ENABLE_8, + uint16_t samplePeriod = 50); + + bool init() override; + + bool sampleRaw(); + + bool closeAndReset(); + + bool selfTest() override; + +private: + /** + * @brief Active object method, about the thread execution + */ + void run() override; + + /** + * @brief Sampling method used by the thread + * + * @return VN100Data The sampled data + */ + VN300Data sampleData(); + + /** + * @brief Disables the async messages that the vn100 is default configured + * to send at 40Hz on startup. + * + * @param waitResponse If true wait for a serial response. + * + * @return True if operation succeeded. + */ + bool disableAsyncMessages(bool waitResponse = true); + + /** + * @brief Pause async messages in order to write command without receiving + * continuous data transmission + * + * @param waitResponse If true wait for a serial response. + * + * @param selection if true pause the async output, if false restart the + * output + * + * @return True if operation succeeded. + * + */ + bool VN300::AsyncPauseCommand(bool waitResponse = false, bool selection); + + /** + * @brief Configures the default serial communication. + * + * @return True if operation succeeded. + */ + bool configDefaultSerialPort(); + + /** + * @brief Configures the user defined serial communication. + * + * @return True if operation succeeded. + */ + bool configUserSerialPort(); + + /** + * @brief Sets the user selected crc method. + * + * @param waitResponse If true wait for a serial response. + * + * @return True if operation succeeded. + */ + bool setCrc(bool waitResponse = true); + + /** + * @brief Method implementation of self test. + * + * @return True if operation succeeded. + */ + bool selfTestImpl(); + + /** + * @brief Sends the command to the sensor with the correct checksum added + * so '*' symbol is not needed at the end of the string as well as the '$' + * at the beginning of the command. + * + * @param command Command to send. + * + * @return True if operation succeeded. + */ + bool sendStringCommand(std::string command); + + /** + * @brief Receives a command from the VN100 serialInterface->recv() but + * swaps the first \n with a \0 to close the message. + * + * @param command The char array which will be filled with the command. + * @param maxLength Maximum length for the command array. + * + * @return True if operation succeeded. + */ + bool recvStringCommand(char *command, int maxLength); + + /** + * @brief Method to verify the crc validity of a command. + * + * @param command The char array which contains the command. + * @param maxLength Maximum length for the command array. + * + * @return True if operation succeeded. + */ + bool verifyChecksum(char *command, int maxLength); + + /** + * @brief Calculate the 8bit checksum on the given array. + * + * @param command Command on which compute the crc. + * @param length Array length. + * + * @return The 8 bit checksum. + */ + uint8_t calculateChecksum8(uint8_t *message, int length); + + /** + * @brief Calculate the 16bit array on the given array. + * + * @param command Command on which compute the crc. + * @param length Array length. + * + * @return The 16 bit CRC16-CCITT error check. + */ + uint16_t calculateChecksum16(uint8_t *message, int length); + + USARTType *portNumber; + USART::Baudrate baudRate; + uint16_t samplePeriod; + CRCOptions crc; + bool isInit = false; + + std::string ASYNC_PAUSE_COMMAND = "VNASY,0"; + std::string ASYNC_RESUME_COMMAND = "VNASY,1"; + /** + * @brief Pointer to the received string by the sensor. Allocated 1 time + * only (200 bytes). + */ + char *recvString = nullptr; + + /** + * @brief Actual strlen() of the recvString. + */ + unsigned int recvStringLength = 0; + + /** + * @brief Serial interface that is needed to communicate + * with the sensor via ASCII codes. + */ + USARTInterface *serialInterface = nullptr; + + /** + * @brief Mutex to synchronize the reading and writing of the threadSample + */ + mutable miosix::FastMutex mutex; + VN300Data threadSample; + + PrintLogger logger = Logging::getLogger("vn300"); + + static const unsigned int recvStringMaxDimension = 200; +}; +} // namespace Boardcore \ No newline at end of file diff --git a/src/shared/sensors/VN300/VN300Data.h b/src/shared/sensors/VN300/VN300Data.h new file mode 100644 index 000000000..3df6e3968 --- /dev/null +++ b/src/shared/sensors/VN300/VN300Data.h @@ -0,0 +1,105 @@ +/* Copyright (c) 2023 Skyward Experimental Rocketry + * Author: Lorenzo Cucchi + * + * 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. + */ + +#pragma once + +#include <sensors/SensorData.h> + +namespace Boardcore +{ + +/** + * @brief Structure to handle quaternion data + */ +struct QuaternionData +{ + uint64_t quatTimestamp; + float quatX; + float quatY; + float quatZ; + float quatW; +}; + +/** + * @brief Structure to handle quaternion data + */ + +struct VN300Data : public QuaternionData, + public MagnetometerData, + public AccelerometerData, + public GyroscopeData, + public TemperatureData, + public PressureData +{ + + /** + * @brief Void parameters constructor + */ + // cppcheck-suppress uninitDerivedMemberVar + VN300Data() + : QuaternionData{0, 0.0, 0.0, 0.0, 0.0}, MagnetometerData{0, 0.0, 0.0, + 0.0}, + AccelerometerData{0, 0.0, 0.0, 0.0}, GyroscopeData{0, 0.0, 0.0, 0.0}, + TemperatureData{0, 0.0}, PressureData{0, 0.0} + { + } + + /** + * @brief Constructor with parameters + * + * @param single data structures for all the data + */ + // cppcheck-suppress passedByValue + // cppcheck-suppress uninitDerivedMemberVar + VN300Data(QuaternionData quat, MagnetometerData magData, + AccelerometerData accData, GyroscopeData gyro, + TemperatureData temp, PressureData pres) + : QuaternionData(quat), MagnetometerData(magData), + AccelerometerData(accData), GyroscopeData(gyro), + TemperatureData(temp), PressureData(pres) + { + } + + static std::string header() + { + return "quatTimestamp,quatX,quatY,quatZ,quatW,magneticFieldTimestamp," + "magneticFieldX,magneticFieldY,magneticFieldZ," + "accelerationTimestamp,accelerationX,accelerationY," + "accelerationZ,angularSpeedTimestamp,angularSpeedX," + "angularSpeedY,angularSpeedZ,temperatureTimestamp," + "temperature,pressureTimestamp,pressure\n"; + } + void print(std::ostream& os) const + { + os << quatTimestamp << "," << quatX << "," << quatY << "," << quatZ + << "," << quatW << "," << magneticFieldTimestamp << "," + << magneticFieldX << "," << magneticFieldY << "," << magneticFieldZ + << "," << accelerationTimestamp << "," << accelerationX << "," + << accelerationY << "," << accelerationZ << "," + << angularSpeedTimestamp << "," << angularSpeedX << "," + << angularSpeedY << "," << angularSpeedZ << "," + << temperatureTimestamp << "," << temperature << "," + << pressureTimestamp << "," << pressure << "\n"; + } +}; + +} // namespace Boardcore \ No newline at end of file -- GitLab