diff --git a/.github/workflows/deploy_cpp_libs.yml b/.github/workflows/deploy_cpp_libs.yml index 13c0400c7..e2eab8f35 100644 --- a/.github/workflows/deploy_cpp_libs.yml +++ b/.github/workflows/deploy_cpp_libs.yml @@ -64,10 +64,10 @@ jobs: steps: - name: Clone Repository uses: actions/checkout@v2 - - name: Setup Cmake - uses: jwlawson/actions-setup-cmake@v1.13 + - name: Setup CMake + uses: lukka/get-cmake@v4.3.2 with: - cmake-version: '3.21.x' + cmakeVersion: '3.21.4' - name: Install Ninja if: (matrix.os == 'macos-14') run: | diff --git a/.github/workflows/run_android.yml b/.github/workflows/run_android.yml index 5181196c0..9945618ec 100644 --- a/.github/workflows/run_android.yml +++ b/.github/workflows/run_android.yml @@ -24,10 +24,10 @@ jobs: sudo -H apt-get install -y ninja-build zip unzip python3-setuptools python3-pygments env: DEBIAN_FRONTEND: noninteractive - - name: Setup Cmake - uses: jwlawson/actions-setup-cmake@v1.4 + - name: Setup CMake + uses: lukka/get-cmake@v4.3.2 with: - cmake-version: '3.16.x' + cmakeVersion: '3.21.4' - name: Install NDK run: | echo "y" | sudo -H ${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager --install "ndk;${ANDROID_NDK_VERSION}" --sdk_root=${ANDROID_SDK_ROOT} diff --git a/.github/workflows/run_libftdi.yml b/.github/workflows/run_libftdi.yml index 7a27db389..b76ff8e92 100644 --- a/.github/workflows/run_libftdi.yml +++ b/.github/workflows/run_libftdi.yml @@ -27,10 +27,10 @@ jobs: sudo -H apt-get install -y python3-setuptools python3-pygments libftdi1-dev env: DEBIAN_FRONTEND: noninteractive - - name: Setup Cmake - uses: jwlawson/actions-setup-cmake@v1.4 + - name: Setup CMake + uses: lukka/get-cmake@v4.3.2 with: - cmake-version: '3.16.x' + cmakeVersion: '3.21.4' - name: Compile BrainFlow run: | mkdir $GITHUB_WORKSPACE/build diff --git a/.github/workflows/run_matlab.yml b/.github/workflows/run_matlab.yml index ece48d78b..14042196f 100644 --- a/.github/workflows/run_matlab.yml +++ b/.github/workflows/run_matlab.yml @@ -10,10 +10,10 @@ jobs: steps: - name: Check out repository uses: actions/checkout@v2 - - name: Setup Cmake - uses: jwlawson/actions-setup-cmake@v1.4 + - name: Setup CMake + uses: lukka/get-cmake@v4.3.2 with: - cmake-version: '3.16.x' + cmakeVersion: '3.21.4' - name: Compile BrainFlow run: | mkdir $GITHUB_WORKSPACE/build diff --git a/.github/workflows/run_unix.yml b/.github/workflows/run_unix.yml index 29f3f7746..fdb59cebc 100644 --- a/.github/workflows/run_unix.yml +++ b/.github/workflows/run_unix.yml @@ -55,10 +55,10 @@ jobs: with: version: '1' arch: 'default' - - name: Setup Cmake - uses: jwlawson/actions-setup-cmake@v1.13 + - name: Setup CMake + uses: lukka/get-cmake@v4.3.2 with: - cmake-version: '3.21.x' + cmakeVersion: '3.21.4' - name: Set up JDK 11 uses: actions/setup-java@v1 with: diff --git a/.github/workflows/valgrind.yml b/.github/workflows/valgrind.yml index b374d4c71..9dc098137 100644 --- a/.github/workflows/valgrind.yml +++ b/.github/workflows/valgrind.yml @@ -16,10 +16,10 @@ jobs: # compile and prepare env - name: Clone Repository uses: actions/checkout@v2 - - name: Setup Cmake - uses: jwlawson/actions-setup-cmake@v1.4 + - name: Setup CMake + uses: lukka/get-cmake@v4.3.2 with: - cmake-version: '3.16.x' + cmakeVersion: '3.21.4' - name: Install Dependencies run: | sudo -H apt-get update -y diff --git a/src/board_controller/muse/inc/muse_anthena.h b/src/board_controller/muse/inc/muse_anthena.h index 64c049a5b..984d507c7 100644 --- a/src/board_controller/muse/inc/muse_anthena.h +++ b/src/board_controller/muse/inc/muse_anthena.h @@ -42,9 +42,9 @@ class MuseAnthena : public BLELibBoard std::condition_variable cv; std::vector> notified_characteristics; std::pair control_characteristics; - bool timestamp_initialized; - uint32_t first_device_tick; - double first_host_timestamp; + double last_eeg_timestamp; + double last_aux_timestamp; + double last_anc_timestamp; double last_battery; std::string muse_preset; bool enable_low_latency; @@ -57,10 +57,12 @@ class MuseAnthena : public BLELibBoard std::string bytes_to_string (const uint8_t *data, size_t size); void handle_data_notification (const uint8_t *data, size_t size); void parse_sensor_payload ( - uint8_t tag, uint8_t sequence_num, uint32_t device_tick, const uint8_t *data, size_t size); + uint8_t tag, uint8_t sequence_num, double host_timestamp, const uint8_t *data, size_t size); bool get_sensor_config (uint8_t tag, SensorConfig &config); int get_optics_canonical_index (uint8_t tag, int channel); - double get_sample_timestamp (uint32_t device_tick, int sample_index, double sampling_rate); + void reset_timestamps (); + static double get_sample_timestamp (double last_timestamp, double current_timestamp, + int sample_index, int n_samples, double sampling_rate); public: MuseAnthena (int board_id, struct BrainFlowInputParams params); diff --git a/src/board_controller/muse/inc/muse_anthena_constants.h b/src/board_controller/muse/inc/muse_anthena_constants.h index 9c98e4d5b..9c5c0692e 100644 --- a/src/board_controller/muse/inc/muse_anthena_constants.h +++ b/src/board_controller/muse/inc/muse_anthena_constants.h @@ -11,6 +11,5 @@ // info for equations #define MUSE_ANTHENA_ACCELEROMETER_SCALE_FACTOR 0.0000610352 #define MUSE_ANTHENA_GYRO_SCALE_FACTOR -0.0074768 -#define MUSE_ANTHENA_DEVICE_CLOCK_HZ 256000.0 #define MUSE_ANTHENA_EEG_SCALE_FACTOR (1450.0 / 16383.0) #define MUSE_ANTHENA_OPTICS_SCALE_FACTOR 1.0 diff --git a/src/board_controller/muse/muse_anthena.cpp b/src/board_controller/muse/muse_anthena.cpp index 9cce37423..dd1b29e8a 100644 --- a/src/board_controller/muse/muse_anthena.cpp +++ b/src/board_controller/muse/muse_anthena.cpp @@ -263,9 +263,7 @@ MuseAnthena::MuseAnthena (int board_id, struct BrainFlowInputParams params) muse_adapter = NULL; muse_peripheral = NULL; is_streaming = false; - timestamp_initialized = false; - first_device_tick = 0; - first_host_timestamp = 0.0; + reset_timestamps (); last_battery = 0.0; muse_preset = "p1041"; enable_low_latency = true; @@ -486,9 +484,7 @@ int MuseAnthena::start_stream (int buffer_size, const char *streamer_params) if (res == (int)BrainFlowExitCodes::STATUS_OK) { std::lock_guard callback_guard (callback_lock); - timestamp_initialized = false; - first_device_tick = 0; - first_host_timestamp = 0.0; + reset_timestamps (); last_battery = 0.0; } if (res == (int)BrainFlowExitCodes::STATUS_OK) @@ -555,7 +551,7 @@ int MuseAnthena::stop_stream () res = (int)BrainFlowExitCodes::STREAM_ALREADY_RUN_ERROR; } is_streaming = false; - timestamp_initialized = false; + reset_timestamps (); return res; } @@ -744,8 +740,7 @@ void MuseAnthena::handle_data_notification (const uint8_t *data, size_t size) const uint8_t *packet = data + offset; uint8_t packet_index = packet[1]; - uint32_t device_tick = - cast_32bit_to_uint32_little_endian ((const unsigned char *)(packet + 2)); + double packet_host_timestamp = get_timestamp (); uint8_t primary_tag = packet[9]; const uint8_t *packet_data = packet + PACKET_HEADER_SIZE; size_t packet_data_size = packet_len - PACKET_HEADER_SIZE; @@ -758,8 +753,8 @@ void MuseAnthena::handle_data_notification (const uint8_t *data, size_t size) primary_config.variable_length ? packet_data_size : primary_config.data_len; if ((primary_data_len > 0) && (primary_data_len <= packet_data_size)) { - parse_sensor_payload ( - primary_tag, packet_index, device_tick, packet_data, primary_data_len); + parse_sensor_payload (primary_tag, packet_index, packet_host_timestamp, packet_data, + primary_data_len); packet_data_offset = primary_data_len; } else @@ -799,7 +794,7 @@ void MuseAnthena::handle_data_notification (const uint8_t *data, size_t size) break; } - parse_sensor_payload (tag, subpacket_index, device_tick, + parse_sensor_payload (tag, subpacket_index, packet_host_timestamp, packet_data + packet_data_offset + SUBPACKET_HEADER_SIZE, sensor_data_len); packet_data_offset += SUBPACKET_HEADER_SIZE + sensor_data_len; } @@ -809,7 +804,7 @@ void MuseAnthena::handle_data_notification (const uint8_t *data, size_t size) } void MuseAnthena::parse_sensor_payload ( - uint8_t tag, uint8_t sequence_num, uint32_t device_tick, const uint8_t *data, size_t size) + uint8_t tag, uint8_t sequence_num, double host_timestamp, const uint8_t *data, size_t size) { SensorConfig config; if (!get_sensor_config (tag, config)) @@ -872,10 +867,11 @@ void MuseAnthena::parse_sensor_payload ( } } } - package[(size_t)timestamp_channel] = - get_sample_timestamp (device_tick, sample, config.sampling_rate); + package[(size_t)timestamp_channel] = get_sample_timestamp ( + last_eeg_timestamp, host_timestamp, sample, config.n_samples, config.sampling_rate); push_package (package.data (), (int)BrainFlowPresets::DEFAULT_PRESET); } + last_eeg_timestamp = host_timestamp; return; } @@ -911,10 +907,11 @@ void MuseAnthena::parse_sensor_payload ( (double)raw * MUSE_ANTHENA_GYRO_SCALE_FACTOR; } } - package[(size_t)timestamp_channel] = - get_sample_timestamp (device_tick, sample, config.sampling_rate); + package[(size_t)timestamp_channel] = get_sample_timestamp ( + last_aux_timestamp, host_timestamp, sample, config.n_samples, config.sampling_rate); push_package (package.data (), (int)BrainFlowPresets::AUXILIARY_PRESET); } + last_aux_timestamp = host_timestamp; return; } @@ -947,26 +944,47 @@ void MuseAnthena::parse_sensor_payload ( } } - package[(size_t)timestamp_channel] = - get_sample_timestamp (device_tick, sample, config.sampling_rate); + package[(size_t)timestamp_channel] = get_sample_timestamp ( + last_anc_timestamp, host_timestamp, sample, config.n_samples, config.sampling_rate); push_package (package.data (), (int)BrainFlowPresets::ANCILLARY_PRESET); } + last_anc_timestamp = host_timestamp; } } -double MuseAnthena::get_sample_timestamp ( - uint32_t device_tick, int sample_index, double sampling_rate) +void MuseAnthena::reset_timestamps () { - if (!timestamp_initialized) + last_eeg_timestamp = -1.0; + last_aux_timestamp = -1.0; + last_anc_timestamp = -1.0; +} + +double MuseAnthena::get_sample_timestamp (double last_timestamp, double current_timestamp, + int sample_index, int n_samples, double sampling_rate) +{ + if ((n_samples <= 0) || (sampling_rate <= 0.0)) + { + return current_timestamp; + } + + if (last_timestamp <= 0.0) + { + return current_timestamp - (double)(n_samples - sample_index - 1) / sampling_rate; + } + + if (current_timestamp <= last_timestamp) + { + return current_timestamp; + } + + double sample_timestamp = last_timestamp + (double)(sample_index + 1) / sampling_rate; + if (sample_timestamp <= current_timestamp) { - timestamp_initialized = true; - first_device_tick = device_tick; - first_host_timestamp = get_timestamp (); + return sample_timestamp; } - uint32_t elapsed_ticks = device_tick - first_device_tick; - return first_host_timestamp + (double)elapsed_ticks / MUSE_ANTHENA_DEVICE_CLOCK_HZ + - (double)sample_index / sampling_rate; + double step = (current_timestamp - last_timestamp) / (double)n_samples; + return last_timestamp + step * (double)(sample_index + 1); } std::string MuseAnthena::bytes_to_string (const uint8_t *data, size_t size)