From 84f78f5d714eb61d838c6c2f6b1f38ccc54fe64e Mon Sep 17 00:00:00 2001 From: Philemon Prevot <philemon.prevot@lis-lab.fr> Date: Fri, 25 Apr 2025 10:08:49 +0200 Subject: [PATCH] Correct continuous recording and add debug hex_dump method, plus change header sizes selection and correct timestamp logging --- src/cleanexit.h | 5 ++ src/filewriter.cpp | 111 +++++++++++++++++++++------------------------ src/filewriter.h | 19 ++++---- src/main.cpp | 66 +++++++++++++++++++-------- src/recorder.cpp | 82 +++++++++++++++++++++++---------- src/recorder.h | 8 +++- 6 files changed, 178 insertions(+), 113 deletions(-) diff --git a/src/cleanexit.h b/src/cleanexit.h index 24a88dd..f74687c 100644 --- a/src/cleanexit.h +++ b/src/cleanexit.h @@ -12,6 +12,11 @@ */ void allow_clean_exit(); +/** + * Sets exit signal to extern for access from the main loop + */ +extern std::atomic<bool> _exit_requested; // Exit flag + /** * Check if the application was requested to terminate. * \returns whether the application was requested to terminate. diff --git a/src/filewriter.cpp b/src/filewriter.cpp index 7c2538d..6a943d3 100644 --- a/src/filewriter.cpp +++ b/src/filewriter.cpp @@ -16,8 +16,8 @@ #include <cstring> #include <cmath> -FileWriter::FileWriter(std::string &filename_template, size_t qhb_version, size_t num_channels, size_t sample_rate, size_t depth) : - filename_template(filename_template), qhb_version(qhb_version), num_channels(num_channels), sample_rate(sample_rate), depth(depth) { +FileWriter::FileWriter(std::string &filename_template, size_t qhb_version, size_t num_channels, size_t sample_rate, size_t depth, uint64_t packet_timestamp) : + filename_template(filename_template), qhb_version(qhb_version), num_channels(num_channels), sample_rate(sample_rate), depth(depth), packet_timestamp(packet_timestamp) { // nothing } @@ -82,7 +82,7 @@ WavFileWriter::WavFileWriter(std::string &filename_template, size_t qhb_version, WavFileWriter::WavFileWriter(std::string &filename_template, size_t qhb_version, size_t num_channels, size_t sample_rate, size_t depth, size_t expected_num_samples) : - FileWriter(filename_template, qhb_version, num_channels, sample_rate, depth), + FileWriter(filename_template, qhb_version, num_channels, sample_rate, depth, 0), outfile(generate_filename(), std::ios::out | std::ios::binary | std::ios::trunc), samples_written(0) { // check if we could open the file @@ -122,8 +122,8 @@ void WavFileWriter::write(uint8_t *samples, size_t num_samples, uint8_t *imu_dat samples_written += num_samples /(num_channels * depth); } -IMUFileWriter::IMUFileWriter(std::string &filename_template, size_t qhb_version, size_t num_channels, size_t sample_rate,size_t depth, size_t timestamp) : - FileWriter(filename_template, qhb_version, num_channels, sample_rate, depth), +IMUFileWriter::IMUFileWriter(std::string &filename_template, size_t qhb_version, size_t num_channels, size_t sample_rate, size_t depth, uint64_t packet_timestamp) : + FileWriter(filename_template, qhb_version, num_channels, sample_rate, depth, packet_timestamp), outfile(generate_filename(), std::ios::out | std::ios::trunc), last_timestamp(0), @@ -153,33 +153,43 @@ float IMUFileWriter::GetFloatSafe(const unsigned char *p, int index) { return result; } +unsigned char IMUFileWriter::CalculateChecksum(int msgFunction, + int msgPayloadLength, const unsigned char msgPayload[]) +{ + unsigned char checksum = 0; + checksum ^= static_cast<unsigned char>(0xFE); + checksum ^= static_cast<unsigned char>(msgFunction >> 8); + checksum ^= static_cast<unsigned char>(msgFunction >> 0); + checksum ^= static_cast<unsigned char>(msgPayloadLength >> 8); + checksum ^= static_cast<unsigned char>(msgPayloadLength >> 0); + for (int i = 0; i < msgPayloadLength; i++) { + checksum ^= msgPayload[i]; + } + return checksum; +} + + void IMUFileWriter::DecodeMessage(unsigned char c) { switch (rcvState) { case StateReception::Waiting: - if (c == 0xFE) + if (c == 0xFE) { rcvState = StateReception::FunctionMSB; - std::cout << "Batch start : "; - std::cout << std::hex << std::setfill('0') << std::setw(2) << c << " "; + } break; case StateReception::FunctionMSB: msgDecodedFunction = static_cast<int16_t>(c << 8); rcvState = StateReception::FunctionLSB; - std::cout << std::hex << std::setfill('0') << std::setw(2) << c << " "; break; case StateReception::FunctionLSB: msgDecodedFunction += static_cast<int16_t>(c << 0); rcvState = StateReception::PayloadLengthMSB; - std::cout << std::hex << std::setfill('0') << std::setw(2) << c << " "; break; case StateReception::PayloadLengthMSB: msgDecodedPayloadLength = static_cast<uint16_t>(c << 8); rcvState = StateReception::PayloadLengthLSB; - std::cout << std::hex << std::setfill('0') << std::setw(2) << c << " "; break; case StateReception::PayloadLengthLSB: msgDecodedPayloadLength += static_cast<uint16_t>(c << 0); - std::cout << std::hex << std::setfill('0') << std::setw(2) << c << " "; - std::cout << "PayloadLength : " << msgDecodedPayloadLength << "\n"; if (msgDecodedPayloadLength > 0) { if (msgDecodedPayloadLength < 1024) { msgDecodedPayloadIndex = 0; @@ -187,38 +197,15 @@ void IMUFileWriter::DecodeMessage(unsigned char c) { if (msgDecodedPayload == nullptr) { throw std::bad_alloc(); // Handle memory allocation failure } - rcvState = StateReception::SoftwareMajorRev; - std::cout << "StateReception : SoftwareMajorRev\n"; + rcvState = StateReception::Payload; } else { rcvState = StateReception::Waiting; - std::cout << "StateReception : Waiting\n"; } } else - rcvState = StateReception::Decode; - std::cout << "StateReception : Decode\n"; - break; - case StateReception::SoftwareMajorRev: - softwareMajorRev = static_cast<u_int8_t>(c); - rcvState = StateReception::SoftwareMinorRev; - std::cout << std::hex << std::setfill('0') << std::setw(2) << c << " "; - break; - case StateReception::SoftwareMinorRev: - softwareMinorRev = static_cast<u_int8_t>(c); - rcvState = StateReception::TimestampB; - std::cout << std::hex << std::setfill('0') << std::setw(2) << c << " "; - break; - case StateReception::TimestampB: - last_timestamp += static_cast<uint64_t>(c << 8 * (8 - timestampByteReceived)); - std::cout << std::hex << std::setfill('0') << std::setw(2) << c << " "; - timestampByteReceived += 1; - if (timestampByteReceived == 8) - { - timestampByteReceived = 0; - rcvState = StateReception::Payload; - } + rcvState = StateReception::CheckSum; break; case StateReception::Payload: - std::cout << std::hex << std::setfill('0') << std::setw(2) << c << " "; + { if (msgDecodedPayloadIndex > msgDecodedPayloadLength) { //Erreur @@ -228,18 +215,24 @@ void IMUFileWriter::DecodeMessage(unsigned char c) { msgDecodedPayload[msgDecodedPayloadIndex++] = c; if (msgDecodedPayloadIndex >= msgDecodedPayloadLength) { - rcvState = StateReception::Decode; + rcvState = StateReception::CheckSum; msgDecodedPayloadIndex = 0; } break; - case StateReception::Decode: - { - //Lance l'event de fin de decodage - ProcessDecodedMessage(msgDecodedFunction, msgDecodedPayloadLength, msgDecodedPayload); - msgDecoded++; - last_timestamp = 0; - rcvState = StateReception::Waiting; } + case StateReception::CheckSum: + { + unsigned char calculatedChecksum = CalculateChecksum(msgDecodedFunction, msgDecodedPayloadLength, msgDecodedPayload); + unsigned char receivedChecksum = c; + if (calculatedChecksum == receivedChecksum) { + //Lance l'event de fin de decodage + ProcessDecodedMessage(msgDecodedFunction, msgDecodedPayloadLength, msgDecodedPayload); + msgDecoded++; + } else { + std::cerr << "Checksum error" << std::endl; + } + rcvState = StateReception::Waiting; + } break; default: rcvState = StateReception::Waiting; @@ -307,7 +300,6 @@ void IMUFileWriter::ProcessDecodedMessage(int msgFunction, int msgPayloadLength, } break; case static_cast<short>(HS_DATA_PACKET_FULL_TIMESTAMP_V2): { - outfile << "PACKET TIMESTAMP: " << last_timestamp; IMUFileWriter::SensorType sensorType = static_cast<SensorType>(msgPayload[0]); unsigned char id = msgPayload[1]; unsigned char nbChannels = msgPayload[2]; @@ -485,14 +477,16 @@ void IMUFileWriter::ProcessDecodedMessage(int msgFunction, int msgPayloadLength, void IMUFileWriter::write(uint8_t *sample, size_t size, uint8_t *imu_data) { if(this->qhb_version == 3) { - for(int i=0; i<size-1; i++) + std::cout << static_cast<int>(this->packet_timestamp) << std::endl; + outfile << "PACKET TIMESTAMP: " << this->packet_timestamp << "\n"; + for(int i=0; i<this->additionnal_data_size; i++) { DecodeMessage(imu_data[i]); } } else { uint8_t *imu_data_cur(imu_data); - while(imu_data_cur + frame_size + 5 < imu_data + additional_data_size){ + while(imu_data_cur + frame_size + 5 < imu_data + additionnal_data_size){ if(!(imu_data_cur[0]==0xFE && imu_data_cur[1]==0x0A && imu_data_cur[2]==0x0A && imu_data_cur[5]==0x08)) { // skip trame if header is incorrect imu_data_cur += frame_size + 5; @@ -526,7 +520,7 @@ size_t IMUFileWriter::get_last_timestamp(){ SplitWavFileWriter::SplitWavFileWriter(std::string &filename_template, size_t qhb_version, size_t num_channels, size_t sample_rate, size_t depth, size_t samples_per_file) : - FileWriter(filename_template, qhb_version, num_channels, sample_rate, depth), + FileWriter(filename_template, qhb_version, num_channels, sample_rate, depth, 0), samples_per_file(samples_per_file), current_file(nullptr), current_file_samples_written(0) { @@ -568,8 +562,8 @@ void SplitWavFileWriter::write(uint8_t *samples, size_t num_samples, uint8_t *im SplitIMUWavFileWriter::SplitIMUWavFileWriter(std::string &filename_template, std::string &imu_name_template, size_t qhb_version, - size_t num_channels, size_t sample_rate, size_t depth, size_t samples_per_file): - FileWriter(filename_template, qhb_version, num_channels, sample_rate, depth), + size_t num_channels, size_t sample_rate, size_t depth, size_t samples_per_file, uint64_t packet_timestamp): + FileWriter(filename_template, qhb_version, num_channels, sample_rate, depth, packet_timestamp), imu_name_template(imu_name_template), samples_per_file(samples_per_file), current_file(nullptr), @@ -591,16 +585,16 @@ SplitIMUWavFileWriter::~SplitIMUWavFileWriter() { void SplitIMUWavFileWriter::write(uint8_t *samples, size_t num_samples, uint8_t* imu_data) { size_t available = num_samples /(num_channels * depth); // start as many new files as required to write out the samples - while (current_file_samples_written + available >= samples_per_file) { + while ((current_file_samples_written + available >= samples_per_file) && (samples_per_file != 0)) { if (!current_file) { current_file = new WavFileWriter(filename_template, qhb_version, num_channels, sample_rate, depth, samples_per_file); if (imu_file) { max_timestamp = imu_file->get_last_timestamp(); delete imu_file; - imu_file = new IMUFileWriter(imu_name_template, qhb_version, num_channels, sample_rate, depth, max_timestamp); + imu_file = new IMUFileWriter(imu_name_template, qhb_version, num_channels, sample_rate, depth, this->packet_timestamp); } } - if (!imu_file) imu_file = new IMUFileWriter(imu_name_template, qhb_version, num_channels, sample_rate, depth, max_timestamp); + if (!imu_file) imu_file = new IMUFileWriter(imu_name_template, qhb_version, num_channels, sample_rate, depth, this->packet_timestamp); // write out as much as fits into the current file and move the pointer size_t missing = samples_per_file - current_file_samples_written; current_file->write(samples, missing * num_channels * depth, imu_data); @@ -623,14 +617,13 @@ void SplitIMUWavFileWriter::write(uint8_t *samples, size_t num_samples, uint8_t* if (imu_file) { max_timestamp = imu_file->get_last_timestamp(); delete imu_file; - imu_file = new IMUFileWriter(imu_name_template, qhb_version, num_channels, sample_rate, depth, max_timestamp); + imu_file = new IMUFileWriter(imu_name_template, qhb_version, num_channels, sample_rate, depth, this->packet_timestamp); } } - if (!imu_file) imu_file = new IMUFileWriter(imu_name_template, qhb_version, num_channels, sample_rate, depth, max_timestamp); + if (!imu_file) imu_file = new IMUFileWriter(imu_name_template, qhb_version, num_channels, sample_rate, depth, this->packet_timestamp); current_file->write(samples, available * num_channels * depth, imu_data); if (imu_data) imu_file->write(samples, available * num_channels * depth, imu_data); imu_data = nullptr; current_file_samples_written += available; } - } diff --git a/src/filewriter.h b/src/filewriter.h index e584a30..cae305b 100644 --- a/src/filewriter.h +++ b/src/filewriter.h @@ -36,7 +36,7 @@ protected: public: /** Abstract constructor to be used by subclasses. */ - FileWriter(std::string &filename_template, size_t qhb_version, size_t num_channels, size_t sample_rate, size_t depth); + FileWriter(std::string &filename_template, size_t qhb_version, size_t num_channels, size_t sample_rate, size_t depth, uint64_t packet_timestamp); virtual ~FileWriter(); /** Writes out the given vector of 8-bit samples. @@ -44,6 +44,10 @@ public: */ void write(std::vector<uint8_t> &samples, std::vector<uint8_t> &imu_data); virtual void write(uint8_t *samples, size_t num_samples, uint8_t *imu_data) = 0; + + /** Save the last packet timestamp + */ + uint64_t packet_timestamp = 0; }; @@ -148,7 +152,7 @@ class IMUFileWriter: public FileWriter { // const std::string header = "Timestamp,ax,ay,az,gx,gy,gz,mx,my,mz\n"; const std::string header = "Sensor Type,TimeStamp(ms) or Time, val0,val1,val2,val3,val4,val5,val6,val7\n"; const size_t frame_size = 32; - const size_t additional_data_size = 736; + const size_t additionnal_data_size = 736; private: enum class SensorType { Unknown = 0, @@ -186,7 +190,6 @@ private: }; std::ofstream outfile; - size_t last_timestamp = 0; unsigned int lastAccelTimeStamp = 0; unsigned int lastGyroTimeStamp = 0; unsigned int lastMagTimeStamp = 0; @@ -203,11 +206,8 @@ private: FunctionLSB, PayloadLengthMSB, PayloadLengthLSB, - SoftwareMajorRev, - SoftwareMinorRev, - TimestampB, Payload, - Decode + CheckSum, }; StateReception rcvState; @@ -239,11 +239,12 @@ public: * \param[in] samples_per_file The target number of samples (per channel) * that will be written to a file before starting the next one. */ + size_t last_timestamp = 0; unsigned char CalculateChecksum(int msgFunction, int msgPayloadLength, const unsigned char msgPayload[]); void DecodeMessage(unsigned char c); - IMUFileWriter(std::string &filename_template, size_t qhb_version, size_t num_channels, size_t sample_rate, size_t depth, size_t timestamp); + IMUFileWriter(std::string &filename_template, size_t qhb_version, size_t num_channels, size_t sample_rate, size_t depth, uint64_t packet_timestamp); void write(uint8_t *samples, size_t num_samples, uint8_t *imu_data) override; size_t get_last_timestamp(); }; @@ -276,7 +277,7 @@ public: * that will be written to a file before starting the next one. */ SplitIMUWavFileWriter(std::string &filename_template, std::string &imu_name_template, size_t qhb_version, size_t num_channels, - size_t sample_rate, size_t depth, size_t samples_per_file); + size_t sample_rate, size_t depth, size_t samples_per_file, uint64_t packet_timestamp); ~SplitIMUWavFileWriter() override; void write(uint8_t *samples, size_t num_samples, uint8_t *imu_data) override; diff --git a/src/main.cpp b/src/main.cpp index e8a6f59..b905fbc 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -8,6 +8,7 @@ #include <string> #include <memory> #include <cstring> +#include <atomic> #include "recorder.h" #include "filewriter.h" #include "cleanexit.h" @@ -44,6 +45,28 @@ void print_usage(char *name) { int record(size_t qhb_version, size_t channels, size_t rate, size_t depth, size_t filter, std::string &filename, std::string &imu_name, float chunklen, float totallen, size_t device, bool verbose, size_t accelSamplingFrequency, size_t gyroSamplingFrequency, size_t magSamplingFrequency, size_t accelRangeScale, size_t gyroRangeScale, size_t magRangeScale) { + /** + * @brief Set device, initialize writers and loop the message reception and file writing methods + * + * @param qhb_version : version of the QHB card used for recording + * @param channels : number of channels recording + * @param rate : audio sampling rate + * @param depth : audio data bit precision (either 16 or 24) + * @param filter : number of filter used + * @param filename : address of the audio filename to use + * @param imu_name : address of the imu filename to use + * @param chunklen : duration of individual files if given, else defaults to 0 + * @param totallen : total recording duration if given, else defaults to 0 + * @param device : index of the device to use if several QHB Cards are plugged in + * @param verbose : boolean enabling the printing of status messages + * @param accelSamplingFrequency : accelerometer sampling frequency + * @param gyroSamplingFrequency : gyroscope sampling frequency + * @param magSamplingFrequency : magnetometer sampling frequency + * @param accelRangeScale : range of the accelerometer values + * @param gyroRangeScale : range of the gyroscope values + * @param magRangeScale : range of the magnetometer values + */ + // Initialize the recorder object and select the device to use JasonRecorder recorder = JasonRecorder(verbose); std::cout << "Found " << recorder.get_device_count() << " JASON card(s)." << std::endl; if (recorder.get_device_count() == 0) { @@ -51,28 +74,28 @@ int record(size_t qhb_version, size_t channels, size_t rate, size_t depth, size_ return 2; } try { - // prepare the device + // Prepare the device std::cout << "Selecting device number " << device << "..." << std::endl; recorder.set_device(device); - // prepare the file writer + // Prepare the file writer std::unique_ptr<FileWriter> filewriter; - if (chunklen > 0 && imu_name.empty()) { + if (chunklen > 0 && imu_name.empty()) { // When we only record audio, in several files of chunklen length // implementation note: in C++14 we would use std::make_unique<SplitWavFileWriter>(...) filewriter.reset(new SplitWavFileWriter(filename, qhb_version, channels, rate, depth, chunklen * rate)); } - else if (chunklen > 0) { + else if (chunklen > 0) { // When we record audio and imu, in several files of chunklen length // implementation note: in C++14 we would use std::make_unique<SplitWavFileWriter>(...) - filewriter.reset(new SplitIMUWavFileWriter(filename, imu_name, qhb_version, channels, rate, depth, chunklen * rate)); + filewriter.reset(new SplitIMUWavFileWriter(filename, imu_name, qhb_version, channels, rate, depth, chunklen * rate, 0)); } - else if (imu_name.empty()){ + else if (imu_name.empty()){ // When we only record audio data, in one file only filewriter.reset(new WavFileWriter(filename, qhb_version, channels, rate, depth, totallen * rate)); } - else{ - filewriter.reset(new SplitIMUWavFileWriter(filename, imu_name, qhb_version, channels, rate, depth, totallen * rate)); + else{ // When we record both audio and imu in one file only + filewriter.reset(new SplitIMUWavFileWriter(filename, imu_name, qhb_version, channels, rate, depth, totallen * rate, 0)); } - // start the recording loop + // Start the recording and writing main loop std::cout << "Starting to record..." << std::endl; - allow_clean_exit(); + allow_clean_exit(); // Handles for killing signals and clean program termination size_t total_samples_wanted = totallen * rate; size_t total_samples_read = 0; size_t failed_attempts = 0; @@ -81,21 +104,23 @@ int record(size_t qhb_version, size_t channels, size_t rate, size_t depth, size_ std::vector<std::uint8_t> imu_data; try { std::cout << "Setting recording format to " << channels << " channels at " << rate << " Hz " << (8 * depth) << " bits" << std::endl; + // Send start and parameters messages to QHB recorder.start_recording(qhb_version, channels, rate, depth, filter, accelSamplingFrequency, gyroSamplingFrequency, magSamplingFrequency, accelRangeScale, gyroRangeScale, magRangeScale); - // we will record until we have enough (or forever, if totallen == 0) - while ((total_samples_wanted == 0) || (total_samples_read < total_samples_wanted)) { - if (exit_requested()) { + // Loop until we have enough samples (or forever, if totallen == 0) + while (((total_samples_wanted == 0) || (total_samples_read < total_samples_wanted))) { + if (exit_requested()) { // In case of killing signal received std::cout << "Termination requested." << std::endl; break; } - recorder.get_samples(samples, imu_data, false, 500); + // Get audio and imu samples + recorder.get_samples(samples, imu_data, &filewriter->packet_timestamp, false, 500); if (!samples.empty()) { total_samples_read += samples.size() / sample_size; // if we have too much now, crop the last packet if ((total_samples_wanted > 0) && (total_samples_read > total_samples_wanted)) { samples.resize(samples.size() - (total_samples_read - total_samples_wanted) * sample_size); } - // pass it on to the file writer + // Pass it on to the file writer filewriter->write(samples, imu_data); failed_attempts = 0; } @@ -107,6 +132,7 @@ int record(size_t qhb_version, size_t channels, size_t rate, size_t depth, size_ } } } + // Send stop message to QHB recorder.stop_recording(); std::cout << "Stopped recording." << std::endl; } @@ -123,12 +149,15 @@ int record(size_t qhb_version, size_t channels, size_t rate, size_t depth, size_ } int main(int argc, char *argv[]) { + /** + * @brief Main function. Retrieve program arguments and hand them over to the record function. + */ if (argc < 5) { print_usage(argv[0]); return 2; } - // parse command line options + // Parse mandatory command line options int qhb_version; try { qhb_version = std::stoi(argv[1]); @@ -165,14 +194,15 @@ int main(int argc, char *argv[]) { } std::string filename = argv[4]; + // Program args initialization int i=5; - int device(0), bit_depth(16), filter(0); float chunklen(0), totallen(0); float accelSamplingFrequency(25), gyroSamplingFrequency(25), magSamplingFrequency(20); float accelRangeScale(8), gyroRangeScale(250), magRangeScale(12); std::string imu_name; bool verbose(false); + // Retrieve program args while (i < argc){ try{ if (strcmp(argv[i], "--chunk_len") == 0 || strcmp(argv[i], "-c") == 0) { @@ -225,6 +255,6 @@ int main(int argc, char *argv[]) { } i++; } - // hand over to the recording function + // Hand over to the recording function return record(qhb_version, num_channels, rate, bit_depth/8, filter, filename, imu_name, chunklen, totallen, device, verbose, accelSamplingFrequency, gyroSamplingFrequency, magSamplingFrequency, accelRangeScale, gyroRangeScale, magRangeScale); } diff --git a/src/recorder.cpp b/src/recorder.cpp index 2f02a81..086fc9e 100644 --- a/src/recorder.cpp +++ b/src/recorder.cpp @@ -9,10 +9,12 @@ #include <unistd.h> #include <stdexcept> #include <iostream> +#include <iomanip> #include <vector> #include <array> #include <algorithm> #include <fstream> +#include <cstring> JasonRecorder::JasonRecorder(bool verbose) : verbose(verbose) { // create libusb context @@ -137,18 +139,6 @@ void JasonRecorder::start_recording(int qhb_version, std::uint8_t num_channels, send_message(START_ID, payload1); } else if (qhb_version == 3) { - std::vector<std::uint8_t> payload1 = { - START, - (std::uint8_t) ((sample_rate >> 24) & 0xFF), - (std::uint8_t) ((sample_rate >> 16) & 0xFF), - (std::uint8_t) ((sample_rate >> 8) & 0xFF), - (std::uint8_t) (sample_rate & 0xFF), - (std::uint8_t) (num_channels), - (std::uint8_t) (8 * depth), - (std::uint8_t) (1 * num_filter)}; - send_message(START_ID, payload1); - std::cout << "Start message sent\n"; - // std::array<unsigned char, 4> accelSamplingRateBytes = {0}; // std::array<unsigned char, 4> accelRangeScaleBytes = {0}; // getBytesFromFloat(accelSamplingRateBytes, accelSamplingRate); @@ -187,7 +177,7 @@ void JasonRecorder::start_recording(int qhb_version, std::uint8_t num_channels, // }; // send_message(SET_SENSOR, payload3); // std::cout << "Gyro set\n"; - // sleep(0.01); + // sleep(0.1); // std::array<unsigned char, 4> magSamplingRateBytes = {0}; // std::array<unsigned char, 4> magRangeScaleBytes = {0}; @@ -207,11 +197,21 @@ void JasonRecorder::start_recording(int qhb_version, std::uint8_t num_channels, // }; // send_message(SET_SENSOR, payload4); // std::cout << "Mag set\n"; - // sleep(0.01); - // libusb_clear_halt(handle, ENDPOINT_SEND); + std::vector<std::uint8_t> payload1 = { + START, + (std::uint8_t) ((sample_rate >> 24) & 0xFF), + (std::uint8_t) ((sample_rate >> 16) & 0xFF), + (std::uint8_t) ((sample_rate >> 8) & 0xFF), + (std::uint8_t) (sample_rate & 0xFF), + (std::uint8_t) (num_channels), + (std::uint8_t) (8 * depth), + (std::uint8_t) (1 * num_filter)}; + send_message(START_ID, payload1); + std::cout << "Start message sent\n"; } + this->qhb_version = qhb_version; this->num_channels = num_channels; this->sample_rate = sample_rate; this->depth = depth; @@ -249,7 +249,23 @@ size_t JasonRecorder::receive_message(uint8_t *buffer, size_t max_wait) { return received; } -void JasonRecorder::get_samples(std::vector<std::uint8_t> &samples, std::vector<std::uint8_t> &imu_data, bool planar, size_t max_wait) { +void JasonRecorder::hex_dump(const uint8_t* data, size_t size) { + /** + * @brief Debugging method that prints out the data in a hex format, in the same way as the Hex Editor + * + * @param data : pointer to the start of a data buffer + * @param size : quantity of bytes that you want to print out + */ + for (size_t i = 0; i < size; ++i) { + if (i % 16 == 0) std::cout << std::setw(4) << std::setfill('0') << std::hex << i << ": "; + std::cout << std::setw(2) << std::setfill('0') << std::hex + << static_cast<int>(data[i]) << " "; + if ((i + 1) % 16 == 0) std::cout << std::endl; + } + if (size % 16 != 0) std::cout << std::endl; +} + +void JasonRecorder::get_samples(std::vector<std::uint8_t> &samples, std::vector<std::uint8_t> &imu_data, uint64_t *packet_timestamp, bool planar, size_t max_wait) { if (!num_channels || !sample_rate) { throw std::logic_error("must call set_format() first"); } @@ -261,26 +277,42 @@ void JasonRecorder::get_samples(std::vector<std::uint8_t> &samples, std::vector< //size_t length = buffer[1] << 8 + buffer[2]; if (buffer[0] != FRAME_START); // invalid message else if ((((std::uint16_t) buffer[1] << 8 )|(buffer[2])) == DATA_ID) { - std::cout << "IMU Data buffer\n"; - // find the beginning and length of the samples in the buffer - size_t start = this->additional_data_size + 6; + size_t audio_start = this->additionnal_data_buffer_size; + size_t imu_start = 0; + if (this->qhb_version == 3) + { + imu_start = this->additionnal_buffer_header_size_v3; + // Retrieve the packet timestamp for later writing + uint64_t packet_timestamp_value = 0; + // hex_dump(buffer.data(), 16); + for (int i = 0; i < 8; ++i){ + packet_timestamp_value |= static_cast<uint64_t>(buffer[7 + i]) << (8 * (8 - i)); + } + memcpy(packet_timestamp, &packet_timestamp_value, 8); + // std::cout << static_cast<int>(*packet_timestamp) << std::endl; + } + else if (this->qhb_version == 2) + { + imu_start = this->additionnal_buffer_header_size_v2; + } + imu_data.resize(0); - imu_data.reserve(this->additional_data_size); - imu_data.insert(imu_data.begin(), &buffer[6], &buffer[start]); - size_t num_samples = (received - start); - num_samples = (num_samples / (num_channels * this->depth)) * num_channels * this->depth; + imu_data.reserve(this->additionnal_data_buffer_size); + imu_data.insert(imu_data.begin(), &buffer[0], &buffer[audio_start]); + size_t num_samples = (received - audio_start); + num_samples = (num_samples / (num_channels * this->depth)) * num_channels * this->depth; // Mais d'où ça sort ça encore TODO: Voir si ça marche toujours quand on l'enlève (y'a pas de raison) // copy data to provided vector if (planar || (num_channels == 1)) { // copy out directly samples.resize(0); samples.reserve(num_samples); - samples.insert(samples.end(), &buffer[start], &buffer[start] + num_samples); + samples.insert(samples.end(), &buffer[audio_start], &buffer[audio_start] + num_samples); } else { // convert from blocked channels to interleaved channels samples.resize(num_samples); - JasonRecorder::interleave_channels(&buffer[start], + JasonRecorder::interleave_channels(&buffer[audio_start], samples.data(), num_samples, this->num_channels, this->depth); } diff --git a/src/recorder.h b/src/recorder.h index 98a7a48..4891b43 100644 --- a/src/recorder.h +++ b/src/recorder.h @@ -41,6 +41,7 @@ class JasonRecorder { const std::uint16_t GYRO = 0x02; const std::uint16_t MAG = 0x03; private: + void hex_dump(const uint8_t* data, size_t size); // libusb handles struct libusb_context *ctx; std::vector<struct libusb_device*> devices; @@ -78,7 +79,10 @@ private: size_t receive_message(uint8_t *buffer, size_t max_wait = 0); // device state, as far as known - size_t additional_data_size = 730; + size_t additionnal_data_buffer_size = 736; // Size of the AdditionnalBuffer data + size_t additionnal_buffer_header_size_v3 = 16; // Size of the AdditionnalBuffer header for QHBv3 + size_t additionnal_buffer_header_size_v2 = 6; // Size of the AdditionnalBuffer header for QHBv2 + std::uint8_t qhb_version = 0; std::uint8_t num_channels = 0; std::uint8_t depth = 0; std::uint8_t num_filter = 0; @@ -119,7 +123,7 @@ public: * or if a different type of packet was received, returns with an empty * samples vector. Set to zero to wait indefinitely for a sample packet. */ - void get_samples(std::vector<std::uint8_t> &samples, std::vector<std::uint8_t> &imu_data, bool planar=false, size_t max_wait=0); + void get_samples(std::vector<std::uint8_t> &samples, std::vector<std::uint8_t> &imu_data, uint64_t *packet_timestamp, bool planar=false, size_t max_wait=0); /** Converts samples from planar to interleaved form. * \param[in] input A pointer to the planar samples to read. * \param[out] output A pointer to write the interleaved samples to. -- GitLab