前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >[UWB之TDOA]lps-node-firmware代码中uwb_tdoa_anchor2.c代码解析

[UWB之TDOA]lps-node-firmware代码中uwb_tdoa_anchor2.c代码解析

作者头像
呱牛笔记
发布2023-05-02 15:29:31
5030
发布2023-05-02 15:29:31
举报
文章被收录于专栏:呱牛笔记

https://github.com/bitcraze/lps-node-firmware代码中uwb_tdoa_anchor2.c代码解析:

根据该文件的注释,TDOA测距的实现使用的是TDMA的原理,所谓的TDMA,就是根据时隙决定谁来发送什么类型的数据,或者决定由谁来使用该时隙,这里uwb_tdoa_anchor2的实现就是根据slot号决定由谁来进行tdoa测距的业务实现; 

代码语言:javascript
复制
/*
 * This anchor algorithm is using TDMA to divide frames in 8 timeslots. Each
 * anchor is sending a packet in one timeslot, anchor n sends its packet in
 * timeslot n. The slot time is of 2ms.
 *
 * Each packet contains (assuming the packet is sent by anchor n):
 *   - A list of 8 IDs that contains the sequence number of the packets
 *     - At index n: The sequence number of this packet
 *     - At index != n: The sequence number of the last packet received by
 *       anchor 'index'
 *   - A list of 8 timestamps that contains
 *     - At index n: The TX timestamp of the current packet in anchor n time
 *     - At index != n: The RX timestamp of all other packets from previous
 *                      frame in anchor n clock. If the previous packet was
 *                      invalid the timestamp is 0
 *   - A list of 7 distances, the distance from this anchor to the other
 *     anchors in the system expressed in this anchor clock. The distance to
 *     the current anchor is reserved.
 *
 * This is enough info for an observer to calculate the time of departure
 * of any packets in this anchor clock, and so to calculate the difference time
 * of arrivale of the packets at the tag.
 */

这里uwb模块就没有了anchor和tag的概念,统一都是anchor, 但有主anchor和从anchor两种角色,测距主要在主站完成,从站在固定的slot位发送测距请求,主站完成测距过程,如果在非指定的slot发送测距请求,则主站认为是无效的测距请求,不予处理;

主站和从站上电后,从站都处于RX状态,等待主站发送第一个同步时间戳的包,主站则发送第一个时间戳同步包:

代码语言:javascript
复制
static uint32_t tdoa2UwbEvent(dwDevice_t *dev, uwbEvent_t event)
{
  if (ctx.state == synchronizedState) {
    return slotStep(dev, event);
  } else {
    if (ctx.anchorId == 0) {
      //标识主站,发送第一个同步时间戳的包
      dwGetSystemTimestamp(dev, &ctx.tdmaFrameStart);
      ctx.tdmaFrameStart.full = TDMA_LAST_FRAME(ctx.tdmaFrameStart.full) + 2*TDMA_FRAME_LEN;
      ctx.state = synchronizedState;
      setupTx(dev);

      ctx.slotState = slotTxDone;
      updateSlot();
    } 
    //。。。。。
}

测距包的数据结构:

#define NSLOTS 8

#define TDMA_SLOT_BITS 26 // 26: 2ms timeslot

#define TDMA_NSLOT_BITS 3

// Timeout for receiving a packet in a timeslot

#define RECEIVE_TIMEOUT 300

// Timeout for receiving a service packet after we TX ours

#define RECEIVE_SERVICE_TIMEOUT 800

#define TS_TX_SIZE 4

// Packet formats

#define PACKET_TYPE_TDOA2 0x22

typedef struct rangePacket_s {

  uint8_t type;

  uint8_t pid[NSLOTS];  // Packet id of the timestamps

  uint8_t timestamps[NSLOTS][TS_TX_SIZE];  // Relevant time for anchors

  uint16_t distances[NSLOTS];

} __attribute__((packed)) rangePacket_t;

模块初始化代码:

代码语言:javascript
复制
static void setTxData(dwDevice_t *dev){
  //….
  if (firstEntry) {
    MAC80215_PACKET_INIT(txPacket, MAC802154_TYPE_DATA);

    memcpy(txPacket.sourceAddress, base_address, 8);
    txPacket.sourceAddress[0] = ctx.anchorId;
    memcpy(txPacket.destAddress, base_address, 8);
    txPacket.destAddress[0] = 0xff;

    txPacket.payload[0] = PACKET_TYPE_TDOA2;

    firstEntry = false;
  }
  //….
}

// Initialize/reset the agorithm
static void tdoa2Init(uwbConfig_t * config, dwDevice_t *dev)
{
  ctx.anchorId = config->address[0];
  ctx.state = syncTdmaState;
  ctx.slot = NSLOTS-1;
  ctx.nextSlot = 0;
  memset(ctx.txTimestamps, 0, sizeof(ctx.txTimestamps));
  memset(ctx.rxTimestamps, 0, sizeof(ctx.rxTimestamps));
}

其中主站和从站均使用如下的状态机:

// FSM states

enum state_e {

  syncTdmaState = 0, // Anchors 1 to 5 starts here and rise up to synchronizedState

  syncTimeState,

  synchronizedState, // Anchor 0 is always here!

};

enum slotState_e {

  slotRxDone,

  slotTxDone,

};

主站的状态机:

state

syncTdmaState ->  synchronizedState -> syncTdmaState

slotState

slotTxDone -> slotRxDone->slotTxDone

从站的状态机:

state

syncTdmaState ->  synchronizedState

slotState

slotRxDone -> slotTxDone->slotRxDone

ctx.anchorId = 0 的为主站

具体逻辑在 tdoa2UwbEvent方法中,

代码语言:javascript
复制
    if (ctx.anchorId == 0) {
      //标识主站
      dwGetSystemTimestamp(dev, &ctx.tdmaFrameStart);
      ctx.tdmaFrameStart.full = TDMA_LAST_FRAME(ctx.tdmaFrameStart.full) + 2*TDMA_FRAME_LEN;
      ctx.state = synchronizedState;
      setupTx(dev);

      ctx.slotState = slotTxDone;
      updateSlot();
    } else {
      //标识从站:也可以理解为被测距站
      switch (event) {
        case eventPacketReceived: {
            if (rxPacket.sourceAddress[0] == 0 && rxPacket.payload[0] == PACKET_TYPE_TDOA2) {
              //收到主站的同步请求
	}
        default:
          // Start the receiver waiting for a packet from anchor 0
          dwIdle(dev);
          dwSetReceiveWaitTimeout(dev, RECEIVE_TIMEOUT);
          dwWriteSystemConfigurationRegister(dev);

          dwNewReceive(dev);
          dwSetDefaults(dev);
          dwStartReceive(dev);
   }

从站在收到主站的同步时间戳请求后,修改开始发送时间,配置延时发送数据

代码语言:javascript
复制
              //收到主站的同步请求
              rangePacket_t * rangePacket = (rangePacket_t *)rxPacket.payload;

              // Resync local frame start to packet from anchor 0
              dwTime_t pkTxTime = { .full = 0 };
              memcpy(&pkTxTime, rangePacket->timestamps[0], TS_TX_SIZE);
              ctx.tdmaFrameStart.full = rxTime.full - (pkTxTime.full - TDMA_LAST_FRAME(pkTxTime.full));

              ctx.tdmaFrameStart.full += TDMA_FRAME_LEN;

              setupTx(dev);
              ctx.slotState = slotRxDone;
              ctx.state = synchronizedState;

每个槽位发送包的逻辑在这里:

代码语言:javascript
复制
// Setup the radio to send a packet in the next timeslot
static void setupTx(dwDevice_t *dev)
{
  ctx.packetIds[ctx.anchorId] = ctx.pid++;
  dwTime_t txTime = transmitTimeForSlot(ctx.nextSlot);
  ctx.txTimestamps[ctx.anchorId] = txTime.low32;

  dwSetReceiveWaitTimeout(dev, RECEIVE_SERVICE_TIMEOUT);
  dwWriteSystemConfigurationRegister(dev);

  dwNewTransmit(dev);
  dwSetDefaults(dev);
  setTxData(dev);
  dwSetTxRxTime(dev, txTime);

  dwWaitForResponse(dev, true);
  dwStartTransmit(dev);
}

关键是这行代码:

代码语言:javascript
复制
 dwTime_t txTime = transmitTimeForSlot(ctx.nextSlot);


/* Calculate the transmit time for a given timeslot in the current frame */
static dwTime_t transmitTimeForSlot(int slot)
{
  dwTime_t transmitTime = { .full = 0 };

  // Calculate start of the slot
  transmitTime.full = ctx.tdmaFrameStart.full + slot*TDMA_SLOT_LEN;
  // Add guard and preamble time
  transmitTime.full += TDMA_GUARD_LENGTH;
  transmitTime.full += PREAMBLE_LENGTH;

  // DW1000 can only schedule time with 9 LSB at 0, adjust for it
  adjustTxRxTime(&transmitTime);

  return transmitTime;
}

几点待优化考虑的点:

1、如果主站附件多于8个从站如何处理,从站标识如何确定,依靠取模解决,排队如何进行,或者说调整为最大的slot?

2、TDOA测距的精度如何确定?


TDMA,也就是时分多址,非常好理解,同样的一段频谱在同时同地给不同的人使用,那就会产生强干扰,那就不同时给不同的用户使用,就是同样的一段频谱在时间上进行划分(时隙),然后分给不同的用户使用,每个用户只在属于自己的时隙里通信,这样就可以避免掉同频干扰了,但如果时隙不够给不同的用户分配时,则需要排队,也就是排队进房间进行通信。

TDMA技术说明

多址技术分为频分多址FDMA、时分多址TDMA、码分多址CDMA、空分多址SDMA。

1.频分多址(FDMA)技术

是让不同的地球通信站占用不同频率的信道进行通信。因为各个用户使用着不同频率的信道,所以相互没有干扰。早期的移动通信就是采用这个技术。

2.时分多址(TDMA)技术

这种多址技术是让若干个地球站共同使用一个信道,但是我们把一个载波在不同的时间上进行切片,分为8个时隙给8个用户用,由于占用的时间不同,所以相互之间不会干扰。显然,在相同信道数的情况下,采用时分多址要比频分多址能容纳更多的用户。

3.码分多址(CDMA)技术

这种多址技术也是多个地球站共同使用一个信道。但是每个地球站都被分配有一个独特的“码序列”,与所有别的“码序列”都不相同且正交,所以各个用户相互之间也没有干扰。因为是靠不同的“码序列”来区分不同的地球站,所以叫做“码分多址”。采用CDMA技术可以比时分多址方式容纳更多的用户。 

https://blog.csdn.net/whushenlei/article/details/41745993

MAC TDMA系统的设计围绕着时钟同步和时隙调度两个方面。

时钟同步:在通信系统中时钟的同步是一个很重要的问题。

Beacon帧是WLAN网络中一种很重要的管理帧,将本地时钟的替换成接收到的时间戳从而完成了时钟的同步。在本系统中只保留一个路由节点的Beacon帧功能,从而使系统中所有的节点都与此时钟时间同步。

时隙调度:时隙调度是指节点只是特定的时间发送数据帧或管理帧,而在其他时刻处于等待状态。在传统的802.11协议中Beacon帧是通过这六个相应的定时器完成定时发送Beacon帧的,本方案正是利用了这六个定时器的来完成时隙调度。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021/03/30 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档