OpenBTS 中 Timestamp 的工作机制(转)

OpenBTS 中 Timestamp 的工作机制

  (2012-07-17 16:17:55)

转载

Timestamp 是 OpenBTS 实现TDMA时隙同步的一个非常精彩的方案,对于其他SDR系统有很多参考价值。更多关于硬件接口部分代码的解读总结在一个小文档中(http://www.gnuradio.cc/read.php?tid-707.html)。本文整理自www.gnuradio.cc论坛中meteor的一些帖子,加上了我的理解。

由于计算机不能精确定时,OpenBTS采用时间戳Timestamp的方式来保证计算机能得到精确的定时。也就是计算机告诉USRP,我要在某个准确的时间发送信号,USRP就会在那个时间发送,不会受到计算机内部处理时延不确定的影响。

1.1    USRP与OpenBTS的接口

OpenBTS软件与USRP硬件通过USB交换IQ数据。两者之间交换的数据遵循以下格式。

首先,每个数据包的长度都是512字节,其中前8个字节是控制字段,后面504个字节是IQ数据。

1.1.1  接收方向

下图是数据包的构成:包括一些控制比特,数据包长度,时间戳,数据。

2B

2B

4B

504B

Control bits

Payload size

Time stamp

Data

前两个字节的控制比特包括:

Position

Number of bits

Usage

15

1

NULL

14

1

Underrun

11~13

3

NULL

5~10

6

RSSI: Received signal strength indication

0~4

5

CHAN: channel type indicator

在IQ数据部分,I路和Q路数据交替存放,每一个采样值用16bit即2字节表示。

在接收方向上,每次都收126个sample,连续不断的读入一个缓冲区,然后再从缓冲区中读出需要的数据,一般是一次读出4个时隙。对应的函数是USRPDevice.cpp中的:

int USRPDevice::readSamples(short *buf, int len, bool *overrun,TIMESTAMP timestamp,bool *underrun, unsigned *RSSI)

1.1.2  发射方向

与接收方向类似:

2B

2B

4B

504B

Control bits

Payload size

Time stamp

Data

 

Position

Number of bits

Usage

13~15

3

NULL

12

1

isStart: if it is ‘1’, this packet is the first packet in a long signal segment.

11

1

isEnd: if it is ‘1’, this packet is the last packet in a long signal segment.

5~10

6

RSSI: Received signal strength indication

0~4

5

CHAN: channel type indicator

 发射信号时,OpenBTS系统每次发送4个时隙的数据,一共625个sample。这625个sample被拆成5个包发送出去,126/126/126/126/121。对应的函数是USRPDevice.cpp中的:


int USRPDevice::writeSamples(short *buf, int len, bool *underrun,

                                         unsigned long long timestamp,

                                         bool isControl)

1.2    接口时延

在Transceiver,Radiointerface以及USRPDevice中都有各自的时间变量,用于记录这个模块当前的时刻。每个时间变量,都会根据输入时间参数来更新当前时间。

GSM::Time mTransmitDeadlineClock 表示目前该发送什么时间的数据包了,因为待发送的数据是按时间顺序排列在缓存里的,因此可以理解为是当前时间需要发送的数据包的序号,所以每发送完一次都需要加一个时隙。

因为PC到USRP有时延,因此我们必须提前把要发送的数据包发送到USRP中,USRP中也按时间顺序存储,这时候时间标号被翻译成了timestamp,而不是用gsm:time了。

但是我们也不能给数据太早,怎么办?这里采用了一个发送时延mtransmitlatency,用来控制给数据的速度,如果当前时间为radio->clock() 那么我们我们需要发送的数据是 radio->clock()+mtransmitlatency>mTransmitDeadlineClock 了,这个说明mTransmitDeadlineClock -radio->clock()已经小于我们的传送时延了,所以必须马上将数据发送出去。如果mTransmitDeadlineClock -radio->clock()仍然大于mtransmitlatency,那么说明数据包还不急着给USRP。

1.3    用Timestamp实现精确定时

计算机和USRP都维持一个timestamp的64位变量作为时间标记,为了便于区分

·         计算机端的timestamp我们称之为c_timestamp

·         USRP端的timestamp 我们称之为 u_timestamp

之前说到,在不同的模块中有不同的变量来记录时间。c_timestamp假设为其中某个模块的时间变量。它可以通过接收sample的数目进行更新,比如接收到了625个sample以后,c_timestamp = c_timestamp + 625。另外c_timestamp 也成了时间戳与GSM时钟的桥梁,因为c_timestamp每增加625,则时隙GSM时钟增加4个时隙。

u_timestamp 是USRP的FPGA中的一个硬件计数器,以采样速率计数。每增加一个sample,计数器值会增加1。

在接收方向上,即往计算机打包数据时,会将每一个包中第一个sample对应的u_timestamp封装在数据包头处。而这个值可以通过USRPDevice中的readsamples获取。

在发送方向上,发送的timestamp是PC在每发一个包的时候添加的,如果当前的timestamp=1000,那么我如果需要在1000个sample以后发送一组数据,那么发送数据的timestamp就是2000,USRP的FPGA的定时器到达2000的时候,就会将需要的数据发送出去。

 

下面以一个实例来说明。

首先下行方向。在USRPDevice.cpp的writeSamples函数中添加log。在

pkt[1] = timestamp & 0x0ffffffffll;

之后,记录timestamp的值。它表示,计算机发送给USRP的每一个数据包中,第一个sample的时刻。

于是我们在日志文件test.TRX.out中可以看到以下记录:

1337762965.3137 DEBUG 3078122352 USRPDevice.cpp:566:writeSamples: TIMESTAMP: 25000

1337762965.3137 DEBUG 3078122352 USRPDevice.cpp:566:writeSamples: TIMESTAMP: 25126

1337762965.3137 DEBUG 3078122352 USRPDevice.cpp:566:writeSamples: TIMESTAMP: 25252

1337762965.3138 DEBUG 3078122352 USRPDevice.cpp:566:writeSamples: TIMESTAMP: 25378

1337762965.3138 DEBUG 3078122352 USRPDevice.cpp:566:writeSamples: TIMESTAMP: 25504

1337762965.3156 DEBUG 3078122352 USRPDevice.cpp:566:writeSamples: TIMESTAMP: 25625

1337762965.3156 DEBUG 3078122352 USRPDevice.cpp:566:writeSamples: TIMESTAMP: 25751

1337762965.3156 DEBUG 3078122352 USRPDevice.cpp:566:writeSamples: TIMESTAMP: 25877

1337762965.3156 DEBUG 3078122352 USRPDevice.cpp:566:writeSamples: TIMESTAMP: 26003

1337762965.3157 DEBUG 3078122352 USRPDevice.cpp:566:writeSamples: TIMESTAMP: 26129

 

可以看出从25000这一时刻开始,依次发送了时间间隔为126, 126, 126, 126, 121, 126, 126, 126, 126, 121…的数据包。4*126+121=625个samples。这恰好是4个时隙的数据。因此可以看出4个时隙的数据是被拆分成5个数据包发出的。

再看上行方向。在USRPDevice.cpp的readSamples函数中添加log。在

TIMESTAMP pktTimestamp = usrp_to_host_u32(tmpBuf[1]);

之后,记录pktTimestamp的值。它表示,USRP发送给USRP的每一个数据包的第一个sample的时刻。

在日志文件test.TRX.out中可以看到以下记录:

1337762965.3179 DEBUG 3078122352 USRPDevice.cpp:413:readSamples: timestamp: 24822

1337762965.3180 DEBUG 3078122352 USRPDevice.cpp:413:readSamples: timestamp: 24948

1337762965.3180 DEBUG 3078122352 USRPDevice.cpp:413:readSamples: timestamp: 25074

1337762965.3180 DEBUG 3078122352 USRPDevice.cpp:413:readSamples: timestamp: 25200

1337762965.3196 DEBUG 3078122352 USRPDevice.cpp:413:readSamples: timestamp: 25326

1337762965.3197 DEBUG 3078122352 USRPDevice.cpp:413:readSamples: timestamp: 25452

1337762965.3197 DEBUG 3078122352 USRPDevice.cpp:413:readSamples: timestamp: 25578

1337762965.3197 DEBUG 3078122352 USRPDevice.cpp:413:readSamples: timestamp: 25704

1337762965.3198 DEBUG 3078122352 USRPDevice.cpp:413:readSamples: timestamp: 25830

1337762965.3226 DEBUG 3078122352 USRPDevice.cpp:413:readSamples: timestamp: 25956

1337762965.3227 DEBUG 3078122352 USRPDevice.cpp:413:readSamples: timestamp: 26082

1337762965.3227 DEBUG 3078122352 USRPDevice.cpp:413:readSamples: timestamp: 26208

1337762965.3228 DEBUG 3078122352 USRPDevice.cpp:413:readSamples: timestamp: 26334

1337762965.3228 DEBUG 3078122352 USRPDevice.cpp:413:readSamples: timestamp: 26460

 

可以发现,当计算机读取USB数据包时,并没有以625个samples为单位来读,而是每次都读取126个samples,存入临时缓冲区readBuf。然后再拷贝到USRPDevice的一个循环缓冲区data中。readSamples这个函数则会根据输入参数timestamp,从循环缓冲区data中读出625个sample。

1.4    时延校准

这一节是想说明USRPDevice中updateAlignment这个函数的作用。updateAlignment用于校准时延,这个时延指的是从FPGA的计数器到信号出现在天线之间的时延。因为这部分时延不在计数器的控制范围之内,所以需要校准一下。它的目的是要知道发送时间戳和接收时间戳之间的偏差是多少。


上图是时延问题的说明。

FPGA中有两个计数器分别是发射和接收的时间戳计数器。两个计数器是同步的,即同步增加,而且数值是一样的。但是一个数据包从FPGA出来,到真正完成DA变换,加上一些模拟器件的时延,到达天线发送出来,是有一定时延的。所以当发送和接收的时间戳相同时,它们并不是同时出现在空中的。

假设数据包A到达发射天线的时刻是A’,此时收到一个数据包B,时间是B’。 A’=B’。

当数据包B到达FPGA,打上了时间戳B。那么B与A实际上就是在同一时刻出现在空中,但B≠A。有一个变量记录了这个偏差,timestampOffset = B-A,它是USRPDevice的一个成员变量。

接下来看看updateAlighment做了些什么。updateAlignment函数会发送一个“Control”类型的数据包,其中包含时间戳timestamp_tx = T1。FGPA收到这个数据包之后,马上给数据包打上一个新的时间戳timestamp_rx = T2。接着在PC侧,收到一个返回的“Control”类型的数据包。然后更新如下变量:

 

timestamp -= timestampOffset;

timestampOffset = pktTimestamp - pingTimestamp + PINGOFFSET;

LOG(DEBUG) << "updating timestamp offset to: " << timestampOffset;

timestamp += timestampOffset;

isAligned = true;

 

PINGOFFSET是一个常数,等于272。它反映的是DA到AD之间的时延,是一个固定的时延。不同的FPGA image这个常数是不同的。所以 timestampOffset = T2-T1+PINGOFFSET。

 

用另外一个方法也可以测量这个timestampOffset。用USRPping来发送一个特殊的序列,把TX和RX设为同频,同时接收这个序列,然后检测这个特殊序列出现的时刻。同样可以算出timestampOffset。

1.5    USRP中FPGA的Verilog代码

FPGA的Verilog代码在这里:

http://gnuradio.org/redmine/projects/gnuradio/wiki/USRP_inband_FPGA

https://github.com/closehaulcom/usrp1_openbts

有不明白的地方可以看看源码。

分享:

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值