1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00
RIOT/examples/twr_aloha/control.c

308 lines
9.4 KiB
C

/*
* Copyright (C) 2020 Inria
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup examples
* @{
*
* @file
*
* @author Francisco Molina <francois-xavier.molina@inria.fr>
*
* @}
*/
#include <assert.h>
#include <math.h>
#include <string.h>
#include <stdio.h>
#include "control.h"
#include "uwb/uwb.h"
#include "uwb_rng/uwb_rng.h"
#include "mutex.h"
#include "timex.h"
#include "fmt.h"
#include "test_utils/result_output.h"
#include "event/callback.h"
#include "event/periodic.h"
#include "net/ieee802154.h"
#include "net/l2util.h"
#define IEEE802154_SHORT_ADDRESS_LEN_STR_MAX \
(sizeof("00:00"))
/* setup timeout to the maximum value */
#define UWB_CORE_RNG_RX_TIMEOUT 0xfffff
/* forward declaration */
static bool _complete_cb(struct uwb_dev *inst, struct uwb_mac_interface *cbs);
static bool _rx_timeout_cb(struct uwb_dev *inst, struct uwb_mac_interface *cbs);
/* currently there can only be one so lets just keep a pointer to it */
struct uwb_rng_instance *_rng;
struct uwb_dev *_udev;
/* forward declaration */
static void _rng_listen(void *arg);
static void _rng_request(void *arg);
static event_callback_t _rng_listen_event = EVENT_CALLBACK_INIT(_rng_listen, NULL);
#define TWR_STATUS_INITIATOR (1 << 0)
#define TWR_STATUS_RESPONDER (1 << 1)
static uint8_t _status;
typedef struct uwb_core_rng_event {
event_callback_t event; /**< the event callback */
event_periodic_t periodic; /**< the periodic event struct */
twr_protocol_t proto; /**< protocol to use */
uint16_t addr; /**< dest short address */
} uwb_core_rng_event_t;
static uwb_core_rng_event_t _rng_request_event = {
.event = EVENT_CALLBACK_INIT(_rng_request, &_rng_request_event),
.addr = 0xffff,
.proto = TWR_PROTOCOL_SS,
};
/* Structure of extension callbacks common for mac layer */
static struct uwb_mac_interface _uwb_mac_cbs = (struct uwb_mac_interface){
.id = UWBEXT_APP0,
.complete_cb = _complete_cb,
.rx_timeout_cb = _rx_timeout_cb,
};
/* callback to offload printing */
static void _print_rng_data_cb(void *arg)
{
uwb_core_rng_data_t *rng_data = (uwb_core_rng_data_t *)arg;
turo_t ctx;
char addr_str[IEEE802154_SHORT_ADDRESS_LEN_STR_MAX];
uint8_t buffer[IEEE802154_SHORT_ADDRESS_LEN];
turo_init(&ctx);
turo_dict_open(&ctx);
turo_dict_key(&ctx, "t");
turo_u32(&ctx, rng_data->time);
turo_dict_key(&ctx, "src");
byteorder_htobebufs(buffer, rng_data->src);
l2util_addr_to_str(buffer, IEEE802154_SHORT_ADDRESS_LEN, addr_str);
turo_string(&ctx, addr_str);
turo_dict_key(&ctx, "dst");
byteorder_htobebufs(buffer, rng_data->dest);
l2util_addr_to_str(buffer, IEEE802154_SHORT_ADDRESS_LEN, addr_str);
turo_string(&ctx, addr_str);
turo_dict_key(&ctx, "d_cm");
turo_s32(&ctx, rng_data->d_cm);
#if IS_USED(MODULE_UWB_CORE_RNG_TRX_INFO)
turo_dict_key(&ctx, "tof");
turo_float(&ctx, rng_data->tof);
turo_dict_key(&ctx, "los");
turo_float(&ctx, rng_data->los);
turo_dict_key(&ctx, "rssi");
turo_float(&ctx, rng_data->rssi);
#endif
turo_dict_close(&ctx);
print_str("\n");
}
static uwb_core_rng_data_t _rng_data;
static event_callback_t _print_rng_data_event = EVENT_CALLBACK_INIT(
_print_rng_data_cb, &_rng_data);
/**
* @brief Range request complete callback.
*
* This callback is part of the struct uwb_mac_interface extension
* interface and invoked of the completion of a range request in the
* context of this example. The struct uwb_mac_interface is in the
* interrupt context and is used to schedule events an event queue.
*
* Processing should be kept to a minimum given the interrupt context.
* All algorithms activities should be deferred to a thread on an event
* queue. The callback should return true if and only if it can determine
* if it is the sole recipient of this event.
*
* @note The MAC extension interface is a linked-list of callbacks,
* subsequentcallbacks on the list will be not be called if the callback
* returns true.
*
* @param[in] inst Pointer to struct uwb_dev.
* @param[in] cbs Pointer to struct uwb_mac_interface.
* *
* @return true if valid recipient for the event, false otherwise
*/
static bool _complete_cb(struct uwb_dev *inst, struct uwb_mac_interface *cbs)
{
(void)cbs;
if (inst->fctrl != FCNTL_IEEE_RANGE_16 &&
inst->fctrl != (FCNTL_IEEE_RANGE_16 | UWB_FCTRL_ACK_REQUESTED)) {
/* on completion of a range request setup an event to keep listening */
event_post(uwb_core_get_eventq(), &_rng_listen_event.super);
return false;
}
/* get received frame */
struct uwb_rng_instance *rng = (struct uwb_rng_instance *)cbs->inst_ptr;
rng->idx_current = (rng->idx) % rng->nframes;
twr_frame_t *frame = rng->frames[rng->idx_current];
/* parse data*/
uwb_core_rng_data_t data;
/* with ss twr, the last frame will hold the initiator as the src address,
with ds twr, it will hold the responder address */
if (IS_ACTIVE(CONFIG_TWR_PRINTF_INITIATOR_ONLY) &&
((frame->code < UWB_DATA_CODE_DS_TWR && frame->src_address != _udev->uid) ||
(frame->code >= UWB_DATA_CODE_DS_TWR && frame->dst_address != _udev->uid))) {
event_post(uwb_core_get_eventq(), &_rng_listen_event.super);
return true;
}
data.src = frame->src_address;
data.dest = frame->dst_address;
data.time = ztimer_now(ZTIMER_MSEC);
float range_f =
uwb_rng_tof_to_meters(uwb_rng_twr_to_tof(rng, rng->idx_current));
/* convert from float meters to cm */
data.d_cm = ((int32_t)(range_f * 100));
#if IS_USED(MODULE_UWB_CORE_RNG_TRX_INFO)
data.rssi = (int16_t)uwb_calc_rssi(inst, inst->rxdiag);
data.fppl = uwb_calc_fppl(inst, inst->rxdiag);
data.tof = uwb_rng_twr_to_tof(rng, rng->idx_current);
data.los = uwb_estimate_los(rng->dev_inst, data.rssi, data.fppl);
#endif
/* offload range data printing */
memcpy(&_rng_data, &data, sizeof(uwb_core_rng_data_t));
event_post(uwb_core_get_eventq(), &_print_rng_data_event.super);
/* on completion of a range request setup an event to keep listening */
event_post(uwb_core_get_eventq(), &_rng_listen_event.super);
return true;
}
/**
* @brief API for receive timeout callback.
*
* @param[in] inst Pointer to struct uwb_dev.
* @param[in] cbs Pointer to struct uwb_mac_interface.
*
* @return true on success
*/
static bool _rx_timeout_cb(struct uwb_dev *inst, struct uwb_mac_interface *cbs)
{
(void)inst;
(void)cbs;
event_post(uwb_core_get_eventq(), &_rng_listen_event.super);
return true;
}
/**
* @brief An event callback to update the remaining listening time
*/
static void _rng_listen(void *arg)
{
(void)arg;
if (!ztimer_is_set(ZTIMER_MSEC, &_rng_request_event.periodic.timer.timer)) {
_status &= ~TWR_STATUS_INITIATOR;
}
if (_status & TWR_STATUS_RESPONDER) {
/* wake up if needed */
if (_udev->status.sleeping) {
uwb_wakeup(_udev);
}
/* start listening */
if (dpl_sem_get_count(&_rng->sem) == 1) {
uwb_rng_listen(_rng, UWB_CORE_RNG_RX_TIMEOUT, UWB_NONBLOCKING);
}
}
else {
/* go to sleep if possible */
if (_rng_request_event.periodic.timer.interval > CONFIG_TWR_MIN_IDLE_SLEEP_MS) {
uwb_sleep_config(_udev);
uwb_enter_sleep(_udev);
}
}
}
void uwb_core_rng_listen_enable(void)
{
_status |= TWR_STATUS_RESPONDER;
/* post event to start listening */
event_post(uwb_core_get_eventq(), &_rng_listen_event.super);
}
void uwb_core_rng_listen_disable(void)
{
_status &= ~TWR_STATUS_RESPONDER;
}
/**
* @brief An event callback to send a range request
*/
static void _rng_request(void *arg)
{
uwb_core_rng_event_t *event = (uwb_core_rng_event_t *)arg;
/* wake up if needed */
if (_udev->status.sleeping) {
uwb_wakeup(_udev);
}
/* force transition to trxoff instead of waiting for rng_listen operations
to finish */
if (dpl_sem_get_count(&_rng->sem) == 0) {
uwb_phy_forcetrxoff(_udev);
}
uwb_rng_request(_rng, event->addr, (uwb_dataframe_code_t)event->proto);
}
void uwb_core_rng_start(uint16_t addr, twr_protocol_t proto, uint32_t interval,
uint32_t count)
{
event_periodic_stop(&_rng_request_event.periodic);
_rng_request_event.proto = proto;
_rng_request_event.addr = addr;
_status |= TWR_STATUS_INITIATOR;
event_periodic_set_count(&_rng_request_event.periodic, count);
event_periodic_start(&_rng_request_event.periodic, interval);
}
void uwb_core_rng_init(void)
{
_udev = uwb_dev_idx_lookup(0);
_rng = (struct uwb_rng_instance *)uwb_mac_find_cb_inst_ptr(_udev, UWBEXT_RNG);
assert(_rng);
/* set up local mac callbacks */
_uwb_mac_cbs.inst_ptr = _rng;
uwb_mac_append_interface(_udev, &_uwb_mac_cbs);
/* init request periodic event */
event_periodic_init(&_rng_request_event.periodic, ZTIMER_MSEC,
uwb_core_get_eventq(), &_rng_request_event.event.super);
/* set initial status */
_status = 0;
/* got to sleep on boot */
struct uwb_dev *_udev = uwb_dev_idx_lookup(0);
uwb_sleep_config(_udev);
uwb_enter_sleep(_udev);
}
uint32_t uwb_core_rng_req_remaining(void)
{
/* doesn't matter if its not atomic */
return _rng_request_event.periodic.count;
}