23#include <zephyr/kernel.h>
24#include <zephyr/bluetooth/bluetooth.h>
25#include <zephyr/bluetooth/hci.h>
26#include <zephyr/bluetooth/mesh.h>
27#include <zephyr/sys/byteorder.h>
28#include <zephyr/sys/util.h>
32#include <zephyr/sys/util.h>
34#if defined(CONFIG_BLUESYNC_USED_IN_MESH)
35#include <zephyr/bluetooth/mesh.h>
45#if defined(CONFIG_BLUESYNC_TEST_BABBLESIM_SUPPORT)
49#include <zephyr/logging/log.h>
52#define BT_UNIT_MS_TO_TICKS(_ms) ((_ms) * 8 / 5)
53#define BLUESYNC_BT_MIN_ADV_INT BT_UNIT_MS_TO_TICKS(30)
54#define BLUESYNC_BT_MAX_ADV_INT BT_UNIT_MS_TO_TICKS(60)
55#define BLUESYNC_BT_SCAN_INT BT_UNIT_MS_TO_TICKS(60)
56#define BLUESYNC_BT_SCAN_WIND_SIZE BT_UNIT_MS_TO_TICKS(60)
92 .current_round_id = 0xFF,
93 .adv_param = BT_LE_ADV_PARAM_INIT(
94 BT_LE_ADV_OPT_EXT_ADV |
95 BT_LE_ADV_OPT_USE_IDENTITY |
101 .scan_param = BT_LE_SCAN_PARAM_INIT(
102 BT_LE_SCAN_TYPE_PASSIVE,
103 BT_LE_SCAN_OPT_CODED |
104 BT_LE_SCAN_OPT_NO_1M,
127#
if defined(CONFIG_BLUESYNC_TEST_BABBLESIM_SUPPORT)
133#if defined(CONFIG_BLUESYNC_TEST_BABBLESIM_SUPPORT)
134 elem->remote_est_ticks[pos] = est_ticks;
157 size_t min_nb_timestamp)
160 double sum_x = 0, sum_y = 0;
173 sum_x += (double)
local->timer_ticks[i];
181 LOG_ERR(
"No valid data in history for regression.");
183 }
else if (n < min_nb_timestamp) {
184 LOG_ERR(
"Not enough valid samples in history (min = %zu, got = %zu)", min_nb_timestamp, n);
188 double mean_x = sum_x / n;
189 double mean_y = sum_y / n;
191 double sum_cov = 0.0;
192 double sum_var = 0.0;
204 double x = (double)
local->timer_ticks[i] - mean_x;
212 if (fabs(sum_var) < 1e-12) {
213 LOG_ERR(
"Variance too small → numerical instability (%e)", sum_var);
217 *slope = sum_cov / sum_var;
218 *offset = mean_y - (*slope * mean_x);
228#if defined(CONFIG_BLUESYNC_TEST_BABBLESIM_SUPPORT)
260 LOG_DBG(
"method: %s", __func__);
262#if defined(CONFIG_BLUESYNC_TEST_BABBLESIM_SUPPORT)
269 double slope_ticks = 0.0;
270 double offset_ticks = 0.0;
290 k_sem_give(&bluesync_end_sync_sem);
295 int ret = k_msgq_get(&bluesync_rx_msgq, &msg, K_FOREVER);
297 LOG_ERR(
"No message available (err: %d)", ret);
325 LOG_ERR(
"Index too large: %u (max %u)", current_timeslot_idx,
SLOT_NUMBER);
330 if(current_timeslot_idx >= 0 && current_timeslot_idx <
SLOT_NUMBER){
334 , current_timeslot_idx
336#
if defined(CONFIG_BLUESYNC_TEST_BABBLESIM_SUPPORT)
337 , msg.master_estimation_ticks
345 if(current_timeslot_idx >= 1 && current_timeslot_idx <=
SLOT_NUMBER){
349 , current_timeslot_idx-1
351#
if defined(CONFIG_BLUESYNC_TEST_BABBLESIM_SUPPORT)
363 uint8_t current_timeslot_idx,
370 .index_timeslot = current_timeslot_idx,
371 .master_timer_ticks = time_ticks
383 bt_pkt[0].type = BT_DATA_FLAGS;
384 bt_pkt[0].data_len = 1;
385 bt_pkt[0].data = (uint8_t []) { BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR };
387 bt_pkt[1].type = BT_DATA_MANUFACTURER_DATA;
395 struct bt_data bt_packet[2];
405 int err = bt_le_ext_adv_set_data(
param.
adv, bt_packet, ARRAY_SIZE(bt_packet), NULL, 0);
407 LOG_ERR(
"Failed to set advertising data (err %d)", err);
411 struct bt_le_ext_adv_start_param start = {
415#if defined(CONFIG_BLUESYNC_USED_IN_MESH)
416 bt_mesh_scan_disable();
419 err = bt_le_ext_adv_start(
param.
adv, &start);
421 LOG_ERR(
"Failed to start extended advertising (err %d)", err);
428void scan_cb(
const bt_addr_le_t *addr, int8_t rssi,
struct net_buf_simple *buf){
432 LOG_ERR(
"Error: Not enough data for extended advertising");
437 uint8_t len_ad_flag = net_buf_simple_pull_u8(buf);
438 if (len_ad_flag > 0){
439 net_buf_simple_pull_mem(buf, len_ad_flag);
442 uint8_t len = net_buf_simple_pull_u8(buf);
443 if (len > buf->len) {
444 LOG_ERR(
"Error: Buffer length mismatch");
447 uint8_t type = net_buf_simple_pull_u8(buf);
448 if (type == BT_DATA_MANUFACTURER_DATA){
450 net_buf_simple_pull_be16(buf);
453 LOG_ERR(
"Error: Unexpected manufacturer data size");
461 int ret = k_msgq_put(&bluesync_rx_msgq, &msg, K_NO_WAIT);
464 LOG_ERR(
"Failed to put message in msgq");
468 LOG_ERR(
"Error: Unsupported advertising data type (0x%02X)", type);
472#if !defined(CONFIG_BLUESYNC_USED_IN_MESH)
474 uint8_t adv_type,
struct net_buf_simple *buf){
475 if (adv_type == BT_GAP_ADV_TYPE_EXT_ADV){
482#if defined(CONFIG_BLUESYNC_USED_IN_MESH)
483 bt_mesh_register_aux_scan_cb(BT_GAP_ADV_TYPE_EXT_ADV, &
scan_cb);
487 LOG_ERR(
"Failed to start scanning (err %d)", err);
489 LOG_DBG(
"Scanning started.");
495#if defined(CONFIG_BLUESYNC_USED_IN_MESH)
496 bt_mesh_unregister_aux_scan_cb();
498 int err = bt_le_scan_stop();
499 if (err == -EALREADY) {
500 LOG_ERR(
"Scanning was not active.");
502 LOG_ERR(
"Failed to stop scanning (err %d)", err);
504 LOG_DBG(
"Scanning stopped.");
514 k_sleep(K_MSEC(CONFIG_BLUESYNC_ADV_INT_MS));
518void adv_sent_cb(
struct bt_le_ext_adv *adv,
struct bt_le_ext_adv_sent_info *info)
522 if (info->num_sent >= 1) {
530#if defined(CONFIG_BLUESYNC_USED_IN_MESH)
531 bt_mesh_scan_enable();
543 LOG_ERR(
"Failed to create extended advertising set (err %d)", err);
551 LOG_DBG(
"method: %s",__func__);
558 LOG_DBG(
"method: %s",__func__);
562 LOG_DBG(
"method: %s",__func__);
575 LOG_DBG(
"method: %s",__func__);
583 LOG_DBG(
"method: %s",__func__);
600 struct k_poll_event events[] = {
601 K_POLL_EVENT_INITIALIZER(K_POLL_TYPE_SEM_AVAILABLE,
602 K_POLL_MODE_NOTIFY_ONLY,
603 &bluesync_start_new_sync_sem),
604 K_POLL_EVENT_INITIALIZER(K_POLL_TYPE_MSGQ_DATA_AVAILABLE,
605 K_POLL_MODE_NOTIFY_ONLY,
607 K_POLL_EVENT_INITIALIZER(K_POLL_TYPE_SEM_AVAILABLE,
608 K_POLL_MODE_NOTIFY_ONLY,
609 &bluesync_end_sync_sem),
616 k_sem_take(&bluesync_role_assign_sem, K_FOREVER);
621 k_poll(events, ARRAY_SIZE(events), K_FOREVER);
623 if(events[0].state == K_POLL_STATE_SEM_AVAILABLE){
624 k_sem_take(&bluesync_start_new_sync_sem, K_NO_WAIT);
628 if(events[1].state == K_POLL_STATE_MSGQ_DATA_AVAILABLE){
632 if(events[2].state == K_POLL_STATE_SEM_AVAILABLE){
633 k_sem_take(&bluesync_end_sync_sem, K_NO_WAIT);
638 events[0].state = K_POLL_STATE_NOT_READY;
639 events[1].state = K_POLL_STATE_NOT_READY;
640 events[2].state = K_POLL_STATE_NOT_READY;
647 LOG_DBG(
"bluesync init");
650 K_THREAD_STACK_SIZEOF(bluesync_thread_stack),
652 CONFIG_BLUESYNC_THREAD_PRIORITY, 0, K_NO_WAIT);
653 k_thread_name_set(thread_id,
"bluesync_thread");
659 k_sem_give(&bluesync_role_assign_sem);
662 LOG_ERR(
"Wrong type given");
674 k_sem_give(&bluesync_start_new_sync_sem);
void bs_scan_wait_for_sync_handler(void)
static void bluesync_send_adv()
void drift_estimation_handler(struct k_timer *timer_id)
static bluesync_status_t bluesync_encode_msg(struct bt_data *bt_pkt, uint8_t current_round_id, uint8_t current_timeslot_idx, int64_t time_ticks)
void bluesync_thread_fnt(void *arg1, void *arg2, void *arg3)
static void bluesync_store_current_burst()
LOG_MODULE_REGISTER(bluesync, CONFIG_BLUESYNC_LOG_LEVEL)
static void bluesync_scan_start()
void bs_stop_handler(void)
void adv_sent_cb(struct bt_le_ext_adv *adv, struct bt_le_ext_adv_sent_info *info)
void scan_cb(const bt_addr_le_t *addr, int8_t rssi, struct net_buf_simple *buf)
void bs_update_handler(void)
K_MSGQ_DEFINE(bluesync_rx_msgq, sizeof(struct bluesync_msg_client), 17, 1)
K_THREAD_STACK_DEFINE(bluesync_thread_stack, CONFIG_BLUESYNC_THREAD_STACK_SIZE)
struct bs_sm_handlers handlers
#define BLUESYNC_BT_MAX_ADV_INT
static bluesync_status_t calculate_lr_from_history(double *slope, double *offset, size_t min_nb_timestamp)
static uint8_t bt_packet_buf[sizeof(uint16_t)+sizeof(struct bluesync_msg)]
static void bluesync_scan_stop()
static void bluesync_adv_process()
static void bluesync_scan_packet_process()
void bs_adv_handler(void)
K_SEM_DEFINE(bluesync_end_sync_sem, 0, 1)
static void reset_bluesync_timestamps(bluesync_timestamps_t *elem)
#define BLUESYNC_BT_MIN_ADV_INT
static void bluesync_decode_msg(struct bluesync_msg_client *msg, struct net_buf_simple *buf)
static void bluesync_reset_param()
static void bt_scan_cb(const bt_addr_le_t *addr, int8_t rssi, uint8_t adv_type, struct net_buf_simple *buf)
static void add_bluesync_timestamps(bluesync_timestamps_t *elem, uint8_t pos, int64_t ticks)
#define BLUESYNC_BT_SCAN_INT
static void bluesync_init_adv()
#define BLUESYNC_BT_SCAN_WIND_SIZE
static bluesync_status_t end_sync_timeslot_process()
static struct bt_le_ext_adv_cb bt_callbacks
static struct bluesync_param param
void bitwise_and_bitfields(uint8_t *result, const bluesync_timestamps_t *rcv, const bluesync_timestamps_t *local, size_t num_bytes)
Perform a bitwise AND between two bitfields and store the result.
void set_bit(uint8_t *bitfield, size_t bit_index)
Set a specific bit in the bitfield.
bool is_bit_set(uint8_t *bitfield, size_t bit_index)
Check if a specific bit is set in the bitfield.
void statistic_bluesync_status(bluesync_timestamps_t *elem_master, bluesync_timestamps_t *elem_slave, size_t size)
Analyze and log synchronization statistics based on a series of timestamps.
bs_sm_state_t bs_state_machine_get_state()
Get the current state.
void bs_state_machine_init(struct bs_sm_handlers *handlers)
Init the state machine by passing the list of callbacks.
void bs_state_machine_set_role(bluesync_role_t role)
Indicate the role to the state machine.
bluesync_role_t bs_state_machine_get_role()
Get the node role.
void bs_state_machine_run(bs_sm_event_t event)
Annonce an event to the state machine.
bs_sm_state_t
Type definition representing the states.
void bluesync_set_role(bluesync_role_t role)
Sets the operational role of the BlueSync node.
void bluesync_start_net_sync_with_unix_epoch_us(uint64_t unix_epoch_us)
Starts a synchronization round using a known UNIX epoch time.
void bluesync_start_net_sync()
Starts a BlueSync synchronization round as the time authority.
bluesync_role_t
Defines the operational roles for a BlueSync node.
void bluesync_init()
Initializes the BlueSync time synchronization module.
@ BLUESYNC_AUTHORITY_ROLE
Public API for the BlueSync time synchronization module.
void set_new_epoch_unix_ref(uint64_t epoch_ref_us)
Set the new epoch unix ref value. Before the authority node starts a new network synchronisation,...
void apply_timer_sync(double new_slope, double new_offset)
Apply the synchronisation parameters. Once the LR is made, The result of it gives a drift value (slop...
uint64_t get_logical_time_ticks()
Get the logical time ticks value. This is the corrected ticks value after synchronisation....
static struct local_time local
@ BLUESYNC_NO_ENOUGH_DATA_STATUS
@ BLUESYNC_NO_VALID_DATA_STATUS
@ BLUESYNC_SUCCESS_STATUS
@ BLUESYNC_DENOMINATOR_TOO_SMALL
#define BURST_WINDOWS_SIZE
#define MY_MANUFACTURER_ID
#define BLUESYNC_TIMESTAMP_ARRAY_SIZE
#define NB_BYTES_BITFIELD
Encapsulated BlueSync message received by the client during synchronization.
uint64_t client_timer_ticks
Message structure used in BlueSync time synchronization exchanges.
uint64_t master_timer_ticks
struct k_mutex local_history_mutex
bluesync_timestamps_t local
struct k_thread bluesync_thread
struct bt_le_scan_param scan_param
struct k_timer drift_estimation_timer
struct k_mutex rcv_history_mutex
bluesync_timestamps_t local_history[BURST_WINDOWS_SIZE]
struct bt_le_ext_adv * adv
bluesync_timestamps_t rcv
struct k_mutex local_mutex
bluesync_timestamps_t rcv_history[BURST_WINDOWS_SIZE]
struct bt_le_adv_param adv_param
uint64_t timer_ticks[SLOT_NUMBER]
uint8_t bitfield[NB_BYTES_BITFIELD]
Struct defining the callbacks of the state machine.
void(* bs_scan_wait_for_sync_cb)(void)
This callback is called when entering in wait_for_sync state.