/* * Copyright (c) 2019-2023 Beijing Hanwei Innovation Technology Ltd. Co. and * its subsidiaries and affiliates (collectly called MKSEMI). * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form, except as embedded into an MKSEMI * integrated circuit in a product or a software update for such product, * must reproduce the above copyright notice, this list of conditions and * the following disclaimer in the documentation and/or other materials * provided with the distribution. * * 3. Neither the name of MKSEMI nor the names of its contributors may be used * to endorse or promote products derived from this software without * specific prior written permission. * * 4. This software, with or without modification, must only be used with a * MKSEMI integrated circuit. * * 5. Any software provided in binary form under this license must not be * reverse engineered, decompiled, modified and/or disassembled. * * THIS SOFTWARE IS PROVIDED BY MKSEMI "AS IS" AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL MKSEMI OR CONTRIBUTORS BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "mk_trace.h" #include "mk_power.h" #include "mk_uwb.h" #include "mk_clock.h" #include "uwb_api.h" #include "ul_tdoa.h" #include "lib_ranging.h" #include "lib_aoa.h" #include "mk_trng.h" #include struct UL_TDOA_ENV_T ul_tdoa_env; static struct UL_TDOA_CB_T ul_tdoa_cb; static struct UWB_OP_T op = { .session_configure = ul_tdoa_configure, .session_start = ul_tdoa_start, .session_stop = ul_tdoa_stop, .session_local_addr_set = uwbs_local_short_addr_set, .session_peer_addr_set = NULL, .session_responder_addr_add = NULL, .session_responder_list_clr = NULL, .session_dynamic_update_responder_list = NULL, .session_set_ccc_ursk = NULL, }; static void ul_tdoa_timer_callback(void *dev, uint32_t time); static uint8_t tx_msg[127] = {0}; static uint16_t tx_msg_len = 0; static void ul_tdoa_tx_process(struct MAC_HW_REPORT_T *tx_report); static void ul_tdoa_rx_process(struct MAC_HW_REPORT_T *rx_report); void app_session_init(void); int ul_tdoa_init(uint8_t handle_id) { /* store handler ID */ ul_tdoa_cb.handle_id = handle_id; /* init rx queue */ WSF_QUEUE_INIT(&ul_tdoa_cb.msg_queue); LOG_INFO(TRACE_MODULE_APP, "Ranging lib version: %s\r\n", MK8000_get_rangelib_version()); LOG_INFO(TRACE_MODULE_APP, "AoA lib version: %s\r\n", MK8000_get_aoalib_version()); return 0; } int ul_tdoa_deinit(void) { return 0; } // This function will be called by uwbapi_session_init() void app_session_init(void) { // register process handler for MAC TX done and RX done mac_register_process_handler(ul_tdoa_tx_process, ul_tdoa_rx_process); uwbs_handler_init(&op); } void ul_tdoa_configure(void) { ul_tdoa_env.stage = UL_TDOA_IDLE; ul_tdoa_env.ranging_period = MS_TO_PHY_TIMER_COUNT(uwb_app_config.session_param.ranging_interval); ul_tdoa_env.random_window = MS_TO_PHY_TIMER_COUNT(uwb_app_config.session_param.ul_tdoa_random_window); ul_tdoa_env.sequence_num = 0; ul_tdoa_env.lost_cnt = 0; uint32_t seed = 0; trng_open(); trng_get(&seed, 1, NULL); trng_close(); srand(seed); LOG_INFO(TRACE_MODULE_APP, "Random seed %u\r\n", seed); phy_rx_sts_switch_mode_set(uwb_app_config.ppdu_params.sts_pkt_cfg, STS_NEVER_SWITCH, 0, 0); uwbs_configure(PHY_TX | PHY_RX, uwb_app_config.session_param.tx_power_level); aoa_param_config(); } static uint32_t ul_tdoa_tx_offset_get(void) { uint32_t tx_offset_ms = (uint32_t)rand() % uwb_app_config.session_param.ul_tdoa_random_window; if (tx_offset_ms < 1) { tx_offset_ms = 1; } return MS_TO_PHY_TIMER_COUNT(tx_offset_ms); } static void ul_tdoa_pkt_construct(enum OWR_MESSAGE_TYPE_T type, int64_t tx_time) { uint8_t input[160]; uint8_t input_len = 0; /* Bits: 0-2 Frame Type 3 Long Frame Control 4-5 Destination Addressing mode 6-7 Source Addressing mode 8 PAN ID Present 9 Security Enabled 10 Sequence Number Suppression 11 Frame Pending 12-13 Frame Version 14 Ack Request 15 IE Present */ uint16_t frame_control = (1 << 15) | (1 << 10) | (1 << 9) | (2 << 6) | (1 << 3) | (5 << 0); input[input_len++] = frame_control & 0xff; input[input_len++] = (frame_control >> 8) & 0xff; uint16_t mac_addr = uwbs_local_short_addr_get(); input[input_len++] = mac_addr & 0xff; input[input_len++] = (mac_addr >> 8) & 0xff; // Auxiliary Security Header uint8_t sec_lvl = 6; uint8_t sec_control = (1 << 5) | (uint8_t)(sec_lvl << 0); input[input_len++] = sec_control; uint32_t phy_sts_index = ul_tdoa_env.sequence_num; uint32_t session_id = uwb_app_config.session_id; // Header IE - Measurement Report Message Type 1 uint16_t header_ie = (0x0 << 15) | (0x0 << 7) | (19 << 0); uint32_t vendor_oui = 0x5a18ff; input[input_len++] = header_ie & 0xff; input[input_len++] = (header_ie >> 8) & 0xff; input[input_len++] = vendor_oui & 0xff; input[input_len++] = (vendor_oui >> 8) & 0xff; input[input_len++] = (vendor_oui >> 16) & 0xff; input[input_len++] = 0x08; input[input_len++] = 0x08; input[input_len++] = 0x08; input[input_len++] = 0x08; input[input_len++] = 0x08; input[input_len++] = 0x08; input[input_len++] = 0x08; input[input_len++] = 0x08; input[input_len++] = session_id & 0xff; input[input_len++] = (session_id >> 8) & 0xff; input[input_len++] = (session_id >> 16) & 0xff; input[input_len++] = (session_id >> 24) & 0xff; input[input_len++] = phy_sts_index & 0xff; input[input_len++] = (phy_sts_index >> 8) & 0xff; input[input_len++] = (phy_sts_index >> 16) & 0xff; input[input_len++] = (phy_sts_index >> 24) & 0xff; header_ie = (0x7e << 7) | (0 << 0); // HT1 IE input[input_len++] = header_ie & 0xff; input[input_len++] = (header_ie >> 8) & 0xff; // Payload IE uint8_t payload_content_len = 16; uint16_t payload_ie = (1 << 15) | (0x2 << 11) | (payload_content_len); input[input_len++] = payload_ie & 0xff; input[input_len++] = (payload_ie >> 8) & 0xff; input[input_len++] = vendor_oui & 0xff; input[input_len++] = (vendor_oui >> 8) & 0xff; input[input_len++] = (vendor_oui >> 16) & 0xff; // OWR message type || UWB message ID uint8_t uwb_message_id = (uint8_t)(type << 4) | 0x7; input[input_len++] = uwb_message_id; // Reserved (4) || TX timestamp present (2) || Device ID Present (2) uint8_t message_control = (uint8_t)(2 << 2) | 1; input[input_len++] = message_control; uint32_t frame_number = ul_tdoa_env.sequence_num; input[input_len++] = frame_number & 0xff; input[input_len++] = (frame_number >> 8) & 0xff; input[input_len++] = (frame_number >> 16) & 0xff; input[input_len++] = (frame_number >> 24) & 0xff; uint8_t *device_id = &uwb_app_config.session_param.ul_tdoa_device_id[1]; input[input_len++] = device_id[0]; input[input_len++] = device_id[1]; input[input_len++] = tx_time & 0xff; input[input_len++] = (tx_time >> 8) & 0xff; input[input_len++] = (tx_time >> 16) & 0xff; input[input_len++] = (tx_time >> 24) & 0xff; input[input_len++] = (tx_time >> 32) & 0xff; input[input_len++] = (tx_time >> 40) & 0xff; input[input_len++] = (tx_time >> 48) & 0xff; input[input_len++] = (tx_time >> 56) & 0xff; memcpy(&tx_msg[0], &input[0], input_len); tx_msg_len = input_len; } void ul_tdoa_start(void) { ul_tdoa_env.anchor_point = phy_timer_count_get(); ul_tdoa_env.tx_offset = ul_tdoa_tx_offset_get(); enum DEV_ROLE_T role = uwb_app_config.session_param.device_role; if (role == DEV_ROLE_UT_SYNC_ANCHOR) { uint32_t curr_count = phy_timer_count_get(); uint32_t count = (uint32_t)(ul_tdoa_env.anchor_point + ul_tdoa_env.tx_offset - curr_count); if (count > ul_tdoa_env.random_window) { count = ul_tdoa_env.random_window; } if (count > MS_TO_PHY_TIMER_COUNT(2)) { power_on_radio(0, 1); ul_tdoa_env.stage = UL_TDOA_LISTEN; mac_rx(EVT_MODE_MAC_PHY_ASAP, 0, count - MS_TO_PHY_TIMER_COUNT(2)); mac_start(); } else { ul_tdoa_env.stage = UL_TDOA_TX_SYNC; phy_timer_target_set(ul_tdoa_env.anchor_point + ul_tdoa_env.tx_offset - UWB_PERIOD_PREFETCH_TIME, ul_tdoa_timer_callback); } } else if (role == DEV_ROLE_UT_TAG) { ul_tdoa_env.stage = UL_TDOA_TX_BLINK; phy_timer_target_set(ul_tdoa_env.anchor_point + ul_tdoa_env.tx_offset - UWB_PERIOD_PREFETCH_TIME, ul_tdoa_timer_callback); } else { power_on_radio(0, 1); ul_tdoa_env.stage = UL_TDOA_LISTEN; mac_rx(EVT_MODE_MAC_PHY_ASAP, 0, ul_tdoa_env.random_window); mac_start(); } ul_tdoa_env.enable = 1; LOG_INFO(TRACE_MODULE_APP, "UL-TDoA start, role %d\r\n", role); } void ul_tdoa_stop(void) { ul_tdoa_env.enable = 0; LOG_INFO(TRACE_MODULE_APP, "UL-TDoA stop\r\n"); } static void ul_tdoa_timer_callback(void *dev, uint32_t time) { ul_tdoa_env.sequence_num++; // Calculate tx timestamp int64_t tx_time = ranging_tx_time(ul_tdoa_env.anchor_point + ul_tdoa_env.tx_offset + phy_shr_duration()); enum OWR_MESSAGE_TYPE_T type = (uwb_app_config.session_param.device_role == DEV_ROLE_UT_SYNC_ANCHOR ? OWR_SYNV_UTM : OWR_BLINK_UTM); ul_tdoa_pkt_construct(type, tx_time); power_on_radio(1, 0); mac_tx(EVT_MODE_MAC_ASAP_PHY_FIX, ul_tdoa_env.anchor_point + ul_tdoa_env.tx_offset, 0, tx_msg, tx_msg_len); mac_start(); } void ul_tdoa_process(const struct MAC_HW_REPORT_T *ind) { enum DEV_ROLE_T role = uwb_app_config.session_param.device_role; if (role == DEV_ROLE_UT_SYNC_ANCHOR) { if (ul_tdoa_env.stage == UL_TDOA_LISTEN) { if (ind->err_code == UWB_RX_OK) { int64_t timestamp = ranging_rx_time(ind); ul_tdoa_rx_ind(ind->err_code, ind->pkt_data, ind->pkt_len, timestamp); } } else { // TX sync done - update next tx offset ul_tdoa_env.tx_offset = ul_tdoa_tx_offset_get(); ul_tdoa_env.anchor_point += ul_tdoa_env.ranging_period; LOG_INFO(TRACE_MODULE_APP, "UL-TDoA TX Sync %u\r\n", ul_tdoa_env.sequence_num); } uint32_t curr_count = phy_timer_count_get(); uint32_t count = (uint32_t)(ul_tdoa_env.anchor_point + ul_tdoa_env.tx_offset - curr_count); if (count > ul_tdoa_env.random_window) { count = ul_tdoa_env.random_window; } if (count > MS_TO_PHY_TIMER_COUNT(2)) { ul_tdoa_env.stage = UL_TDOA_LISTEN; mac_rx(EVT_MODE_MAC_PHY_ASAP, 0, count - MS_TO_PHY_TIMER_COUNT(2)); mac_start(); } else { ul_tdoa_env.stage = UL_TDOA_TX_SYNC; phy_timer_target_set(ul_tdoa_env.anchor_point + ul_tdoa_env.tx_offset - UWB_PERIOD_PREFETCH_TIME, ul_tdoa_timer_callback); power_off_radio(); } } else if (role == DEV_ROLE_UT_TAG) { // TX blink done - update next tx offset ul_tdoa_env.tx_offset = ul_tdoa_tx_offset_get(); ul_tdoa_env.anchor_point += ul_tdoa_env.ranging_period; phy_timer_target_set(ul_tdoa_env.anchor_point + ul_tdoa_env.tx_offset - UWB_PERIOD_PREFETCH_TIME, ul_tdoa_timer_callback); power_off_radio(); LOG_INFO(TRACE_MODULE_APP, "UL-TDoA TX Blink %u\r\n", ul_tdoa_env.sequence_num); } else { if (ind->err_code == UWB_RX_OK) { int64_t timestamp = ranging_rx_time(ind); ul_tdoa_rx_ind(ind->err_code, ind->pkt_data, ind->pkt_len, timestamp); } mac_rx(EVT_MODE_MAC_PHY_ASAP, 0, ul_tdoa_env.random_window); mac_start(); } } void ul_tdoa_rx_ind(uint16_t status, const uint8_t *data, uint16_t len, int64_t timestamp) { struct UL_TDOA_RX_IND_T *ind; if ((ind = WsfMsgAlloc(sizeof(struct UL_TDOA_RX_IND_T) + len)) != NULL) { ind->hdr.event = UL_TDOA_RX_IND_MSG; ind->status = status; ind->rx_len = len; ind->rx_timestamp = timestamp; if (data != NULL) { memcpy(ind->rx_data, data, len); } // Send the message WsfMsgSend(ul_tdoa_cb.handle_id, ind); } } static void ul_tdoa_tx_process(struct MAC_HW_REPORT_T *tx_report) { ul_tdoa_process(tx_report); } static void ul_tdoa_rx_process(struct MAC_HW_REPORT_T *rx_report) { ul_tdoa_process(rx_report); }