https://github.com/bitcraze/lps-node-firmware代码中uwb_tdoa_anchor2.c代码解析:
根据该文件的注释,TDOA测距的实现使用的是TDMA的原理,所谓的TDMA,就是根据时隙决定谁来发送什么类型的数据,或者决定由谁来使用该时隙,这里uwb_tdoa_anchor2的实现就是根据slot号决定由谁来进行tdoa测距的业务实现;
/*
* 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状态,等待主站发送第一个同步时间戳的包,主站则发送第一个时间戳同步包:
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;
模块初始化代码:
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方法中,
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);
}
从站在收到主站的同步时间戳请求后,修改开始发送时间,配置延时发送数据
//收到主站的同步请求
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;
每个槽位发送包的逻辑在这里:
// 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);
}
关键是这行代码:
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帧的,本方案正是利用了这六个定时器的来完成时隙调度。