Merge pull request #40 from open-sdr/pre-release

iq capture feature
This commit is contained in:
Jiao Xianjun 2020-10-19 11:16:32 +02:00 committed by GitHub
commit eb15f15baf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 430 additions and 39 deletions

View File

@ -25,7 +25,8 @@ Openwifi code has dual licenses. AGPLv3 is the opensource license. For non-opens
- Easy to change bandwidth and frequency:
- 2MHz for 802.11ah in sub-GHz
- 10MHz for 802.11p/vehicle in 5.9GHz
- CSI (Channel State Information, also freq offset, equalizer) [[CSI notes](doc/app_notes/csi.md)]
- CSI (Channel State Information, freq offset, equalizer to computer) [[CSI notes](doc/app_notes/csi.md)]
- IQ capture (real-time AGC, RSSI, IQ sample to computer) [[IQ notes](doc/app_notes/iq.md)]
- On roadmap: **802.11ax**
**Performance (AP: openwifi at channel 44, client: TL-WDN4200 N900 USB Dongle):**

View File

@ -149,7 +149,7 @@ After FPGA receives a packet, no matter the FCS/CRC is correct or not it will ra
- frame filtering
The FPGA frame filtering configuration is done in real-time by function openwifi_configure_filter() in sdr.c. This is needed by [mac80211 frame filtering](https://www.kernel.org/doc/html/v4.9/80211/mac80211.html#frame-filtering). The filter_flag together with **HIGH_PRIORITY_DISCARD_FLAG** finally go to pkt_filter_ctl.v of xpu module in FPGA, and control how FPGA does frame filtering. Openwifi has the capability to capture all received packets even if the CRC is bad. You just need to set the NIC to monitor mode by iwconfig command (check monitor_ch.sh in user_space directory). In monitor mode, openwifi_configure_filter() will set **MONITOR_ALL** to the frame filtering module pkt_filter_ctl.v in FPGA. This makes sure transfer all received packets to Linux mac80211 via rx interrupt.
The FPGA frame filtering configuration is done in real-time by function openwifi_configure_filter() in sdr.c. The filter_flag together with **HIGH_PRIORITY_DISCARD_FLAG** finally go to pkt_filter_ctl.v of xpu module in FPGA, and control how FPGA does frame filtering. Openwifi has the capability to capture all received packets even if the CRC is bad. You just need to set the NIC to monitor mode by iwconfig command (check monitor_ch.sh in user_space directory). In monitor mode, openwifi_configure_filter() will set **MONITOR_ALL** to the frame filtering module pkt_filter_ctl.v in FPGA. This makes sure transfer all received packets to Linux mac80211 via rx interrupt.
- main rx interrupt operations in openwifi_rx_interrupt()
- get raw content from DMA buffer. When Linux receives interrupt from FPGA rx_intf module, the content has been ready in Linux DMA buffer

View File

@ -90,7 +90,7 @@ We extend the **CSI** (Channel State Information) to **CSI** (Chip State Informa
```
side_ch_ctl gN
```
The interval will become N*100ms
The interval will become N*1ms
## Config the num_eq
The num_eq (number of equalizer output) is configurable in case you don't need so many equalizer informations. The valid value is 0~8. You should align the num_eq value at the side_ch.ko, side_info_display.py and test_side_info_file_display.m.
@ -105,6 +105,17 @@ We extend the **CSI** (Channel State Information) to **CSI** (Chip State Informa
```
- When use the Matlab script, please change the num_eq variable in the script to 3 (3 is just an example).
## Compile the side channel driver and user space program
- side_ch.ko
```
$OPENWIFI_DIR/driver/side_ch/make_driver.sh $OPENWIFI_DIR $XILINX_DIR ARCH_BIT
(For Zynq 7000, ARCH_BIT should be 32, for Zynq MPSoC, ARCH_BIT should be 64)
```
- side_ch_ctl (take user_space/side_ch_ctl_src/side_ch_ctl.c and compile it on board!)
```
gcc -o side_ch_ctl side_ch_ctl.c
```
## Run the CSI together with modes other than monitor
The openwifi CSI feature could run with not only monitor mode but also other modes, such as AP-Client or ad-hoc mode. After the communication functionality is fully up in those modes, you can start CSI feature from "**insmod side_ch.ko**" and "**./side_ch_ctl g**" on board as described in the previous sections to extract CSI to your computer.

Binary file not shown.

After

Width:  |  Height:  |  Size: 126 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

139
doc/app_notes/iq.md Normal file
View File

@ -0,0 +1,139 @@
We implement the **IQ sample capture** with interesting extensions: many **trigger conditions**; **RSSI**, RF chip **AGC** **status (lock/unlock)** and **gain**.
## Quick start
- Power on the SDR board.
- Connect a computer to the SDR board via Ethernet cable. The computer should have static IP 192.168.10.1. Open a terminal on the computer, and then in the terminal:
```
ssh root@192.168.10.122
(password: openwifi)
cd openwifi
./wgd.sh
(Wait for the script completed)
./monitor_ch.sh sdr0 11
(Monitor on channel 11. You can change 11 to other channel that is busy)
insmod side_ch.ko iq_len_init=8187
(for zed, adrv9364z7020, zc702 board, 8187 should be 4095 because they have smaller FPGA)
./side_ch_ctl wh11d4094
(Above command is needed only when you run with zed, adrv9364z7020, zc702 board)
./side_ch_ctl g
```
You should see on board outputs like:
```
loop 64 side info count 61
loop 128 side info count 99
...
```
If the second number (61, 99, ...) is not zero and keeps increasing, that means the IQ sample is going to the computer smoothly.
- Open another terminal on the computer, and run:
```
cd openwifi/user_space/side_ch_ctl_src
python3 iq_capture.py
(for zed, adrv9364z7020, zc702 board, add 4095 as parameter!)
```
The python script needs "matplotlib.pyplot" and "numpy" packages installed. Now you should see 3 figures showing run-time **IQ sample**, **AGC gain and lock status** and **RSSI (uncalibrated)**. Meanwhile the python script prints the **timestamp**.
While running, all informations are also stored into a file **iq.txt**. A matlab script **test_iq_file_display.m** is offered to help you do analysis on the IQ Information offline. For zed, adrv9364z7020, zc702 board, do not forget to change the **iq_len** in the matlab script to 4095.
## Understand the IQ capture feature
The IQ information is extracted via the openwifi **side channel** infrastructure. This figure explains the related modules (also related source code file name) and how the information goes from the SDR board to the computer.
![](./iq-architecture.jpg)
The IQ information format is shown in this figure.
![](./iq-information-format.jpg)
For each element, the actual size is 64bit.
- timestamp: 64bit TSF timer value when the capture is triggered.
- IQ
- The first two 16bit are used for I/Q sample
- The 3rd 16bit is AD9361 AGC gain (bit7 -- lock/unlock; bit6~0 -- gain value)
- The 4th 16bit is RSSI (half dB, uncalibrated). Please check xpu.v and sdr.c to understand how the raw RSSI value is finally calibrated and reported to Linux mac80211.
The python and Matlab scripts are recommended for you to understand the IQ packet format precisely.
## Config the IQ capture and interval
The quick start guide capture a period of history IQ when the packet FCS checksum is checked by Wifi receiver (no matter pass or fail). To initiate the capture with different trigger condition and length, configuration command should be issued before executing "**side_ch_ctl g**". The configuration command is realized by feeding a different parameter to "**side_ch_ctl**". The main parameters that are configurable are explained in this figure.
![](./iq-capture-parameter.jpg)
**iq_len** is the number of IQ samples captured per trigger condition met. The capture is started from the time **pre_trigger_len** IQ samples before the trigger moment. **iq_len** is set only one time when you insert the side_ch.ko. Please check the next section for **iq_len** configuration. This section introduces the setting of pre_trigger_len and trigger condition.
- pre_trigger_len
```
./side_ch_ctl wh11dY
```
The parameter **Y** specifies the pre_trigger_len. Valid range 0 ~ 8190. It is limited by the FPGA fifo size. For **small FPGA** (zed_fmcs2, adrv9364z7020, zc702), valid range is 0 ~ **4094**.
- trigger condition
```
./side_ch_ctl wh8dY
```
The parameter **Y** specifies the trigger condition. Valid range 0 ~ 15, which is explained in this table.
value|meaning
-----|-------
0 |receiver gives FCS checksum result. no matter pass/fail
1 |receiver gives FCS checksum result. pass
2 |receiver gives FCS checksum result. fail
3 |receiver gives SIGNAL field checksum result. no matter pass/fail
4 |receiver gives SIGNAL field checksum result. pass
5 |receiver gives SIGNAL field checksum result. fail
6 |receiver gives SIGNAL field checksum result. no matter pass/fail. HT packet
7 |receiver gives SIGNAL field checksum result. no matter pass/fail. non-HT packet
8 |receiver gives long preamble detected
9 |receiver gives short preamble detected
10|RSSI (half dB uncalibrated) goes above the threshold
11|RSSI (half dB uncalibrated) goes below the threshold
12|AD9361 AGC from lock to unlock
13|AD9361 AGC from unlock to lock
14|AD9361 AGC gain goes above the threshold
15|AD9361 AGC gain goes below the threshold
To set the RSSI threshold
```
./side_ch_ctl wh9dY
```
The parameter **Y** specifies the RSSI threshold. Valid range 0 ~ 2047.
To set the AGC gain threshold
```
./side_ch_ctl wh10dY
```
The parameter **Y** specifies the AGC gain threshold. Valid range 0 ~ 127.
The command "**side_ch_ctl g**" will perform IQ capture every 100ms until you press ctrl+C. To use a different capture interval:
```
side_ch_ctl gN
```
The interval will become N*1ms
## Config the iq_len
The **iq_len** (number of IQ sample per capture) is configurable in case you want less IQ samples per capture so that it can be triggered more times during a specific analysis period. The valid value is 1~**8187**. For **small FPGA** (zed_fmcs2, adrv9364z7020, zc702), valid range is 0 ~ **4095**. It is independant form pre_trigger_len, and it can be less than pre_trigger_len if you want. You should align the **iq_len** value at the side_ch.ko, iq_capture.py and test_iq_file_display.m.
- When insert the kernel module, use:
```
insmod side_ch.ko iq_len_init=3000
```
Here 3000 is an example. **ATTENTION:** You need to specify **iq_len_init** explicitly to turn on IQ capture, which will turn off CSI. Insert the side_ch.ko without any parameter will run CSI mode.
- When launch the python script, use:
```
python3 iq_capture.py 3000
```
- When use the matlab script, please change the **iq_len** variable in the script to 3000.
## Compile the side channel driver and user space program
- side_ch.ko
```
$OPENWIFI_DIR/driver/side_ch/make_driver.sh $OPENWIFI_DIR $XILINX_DIR ARCH_BIT
(For Zynq 7000, ARCH_BIT should be 32, for Zynq MPSoC, ARCH_BIT should be 64)
```
- side_ch_ctl (take user_space/side_ch_ctl_src/side_ch_ctl.c and compile it on board!)
```
gcc -o side_ch_ctl side_ch_ctl.c
```
## Run the IQ capture together with modes other than monitor
The openwifi IQ capture feature could run with not only monitor mode but also other modes, such as AP-Client or ad-hoc mode. After the communication functionality is fully up in those modes, you can start IQ capture from "**insmod side_ch.ko**" and "**./side_ch_ctl g**" on board as described in the previous sections to extract IQ information to your computer.
## Map the IQ information to the WiFi packet
If you want to relate the IQ information to the WiFi packet, you need to capture WiFi packets (tcpdump/wireshark/etc) while capturing IQ. Then you can relate the timestamp between WiFi packet and IQ information. Please be noticed that the timestamp in the IQ information is the moment when capture is triggered, which could be different from the timestamp reported in the packet capture program. But since they share the same time base (TSF timer), you can relate them easily by analyzing the WiFi packet and IQ sample sequence.
Please learn the python and Matlab script to extract IQ information per capture according to your requirement.

View File

@ -28,10 +28,14 @@
#include "side_ch.h"
static int num_eq_init = 8; // should be 0~8
static int iq_len_init = 0; //if iq_len>0, iq capture enabled, csi disabled
module_param(num_eq_init, int, 0);
MODULE_PARM_DESC(num_eq_init, "num_eq_init. 0~8. number of equalizer output (52 each) appended to CSI");
module_param(iq_len_init, int, 0);
MODULE_PARM_DESC(iq_len_init, "iq_len_init. if iq_len_init>0, iq capture enabled, csi disabled");
static void __iomem *base_addr; // to store driver specific base address needed for mmu to translate virtual address to physical address in our FPGA design
struct dma_chan *chan_to_pl = NULL;
@ -71,12 +75,12 @@ static inline void SIDE_CH_REG_NUM_DMA_SYMBOL_write(u32 value){
reg_write(SIDE_CH_REG_NUM_DMA_SYMBOL_ADDR, value);
}
static inline u32 SIDE_CH_REG_START_DMA_TO_PS_read(void){
return reg_read(SIDE_CH_REG_START_DMA_TO_PS_ADDR);
static inline u32 SIDE_CH_REG_IQ_CAPTURE_read(void){
return reg_read(SIDE_CH_REG_IQ_CAPTURE_ADDR);
}
static inline void SIDE_CH_REG_START_DMA_TO_PS_write(u32 value){
reg_write(SIDE_CH_REG_START_DMA_TO_PS_ADDR, value);
static inline void SIDE_CH_REG_IQ_CAPTURE_write(u32 value){
reg_write(SIDE_CH_REG_IQ_CAPTURE_ADDR, value);
}
static inline u32 SIDE_CH_REG_NUM_EQ_read(void){
@ -111,6 +115,46 @@ static inline void SIDE_CH_REG_ADDR2_TARGET_write(u32 value){
reg_write(SIDE_CH_REG_ADDR2_TARGET_ADDR, value);
}
static inline u32 SIDE_CH_REG_IQ_TRIGGER_read(void){
return reg_read(SIDE_CH_REG_IQ_TRIGGER_ADDR);
}
static inline void SIDE_CH_REG_IQ_TRIGGER_write(u32 value){
reg_write(SIDE_CH_REG_IQ_TRIGGER_ADDR, value);
}
static inline u32 SIDE_CH_REG_RSSI_TH_read(void){
return reg_read(SIDE_CH_REG_RSSI_TH_ADDR);
}
static inline void SIDE_CH_REG_RSSI_TH_write(u32 value){
reg_write(SIDE_CH_REG_RSSI_TH_ADDR, value);
}
static inline u32 SIDE_CH_REG_GAIN_TH_read(void){
return reg_read(SIDE_CH_REG_GAIN_TH_ADDR);
}
static inline void SIDE_CH_REG_GAIN_TH_write(u32 value){
reg_write(SIDE_CH_REG_GAIN_TH_ADDR, value);
}
static inline u32 SIDE_CH_REG_PRE_TRIGGER_LEN_read(void){
return reg_read(SIDE_CH_REG_PRE_TRIGGER_LEN_ADDR);
}
static inline void SIDE_CH_REG_PRE_TRIGGER_LEN_write(u32 value){
reg_write(SIDE_CH_REG_PRE_TRIGGER_LEN_ADDR, value);
}
static inline u32 SIDE_CH_REG_IQ_LEN_read(void){
return reg_read(SIDE_CH_REG_IQ_LEN_ADDR);
}
static inline void SIDE_CH_REG_IQ_LEN_write(u32 value){
reg_write(SIDE_CH_REG_IQ_LEN_ADDR, value);
}
static inline u32 SIDE_CH_REG_M_AXIS_DATA_COUNT_read(void){
return reg_read(SIDE_CH_REG_M_AXIS_DATA_COUNT_ADDR);
}
@ -298,7 +342,7 @@ static int init_side_channel(void) {
return(0);
}
static int get_side_info(int num_eq) {
static int get_side_info(int num_eq, int iq_len) {
// int err = 0;//, i;
struct scatterlist chan_to_ps_sg[1];
enum dma_status status;
@ -323,7 +367,10 @@ static int get_side_info(int num_eq) {
set_user_nice(current, 10);
num_dma_symbol_per_trans = HEADER_LEN + CSI_LEN + num_eq*EQUALIZER_LEN;
if (iq_len>0)
num_dma_symbol_per_trans = 1+iq_len;
else
num_dma_symbol_per_trans = HEADER_LEN + CSI_LEN + num_eq*EQUALIZER_LEN;
//set number of dma symbols expected to ps
num_dma_symbol = SIDE_CH_REG_M_AXIS_DATA_COUNT_read();
printk("%s get_side_info m axis data count %d per trans %d\n", side_ch_compatible_str, num_dma_symbol, num_dma_symbol_per_trans);
@ -420,8 +467,8 @@ static void side_ch_nl_recv_msg(struct sk_buff *skb) {
pid = nlh->nlmsg_pid; /*pid of sending process */
if (action_flag==ACTION_SIDE_INFO_GET) {
res = get_side_info(num_eq_init);
printk(KERN_INFO "%s recv msg: get_side_info(%d) res %d\n", side_ch_compatible_str, num_eq_init, res);
res = get_side_info(num_eq_init, iq_len_init);
printk(KERN_INFO "%s recv msg: get_side_info(%d,%d) res %d\n", side_ch_compatible_str, num_eq_init, iq_len_init, res);
if (res>0) {
msg_size = res;
// printk("%s recv msg: %d %d %d %d %d %d %d %d\n", side_ch_compatible_str, msg[0], msg[1], msg[2], msg[3], msg[4], msg[5], msg[6], msg[7]);
@ -513,6 +560,28 @@ static int dev_probe(struct platform_device *pdev) {
}
//-----------------initialize fpga----------------
printk("%s dev_probe: num_eq_init %d iq_len_init %d\n",side_ch_compatible_str, num_eq_init, iq_len_init);
// disable potential any action from side channel
SIDE_CH_REG_MULTI_RST_write(4);
// SIDE_CH_REG_CONFIG_write(0X6001); // match addr1 and addr2; bit12 FC; bit13 addr1; bit14 addr2
SIDE_CH_REG_CONFIG_write(0x7001); // the most strict condition to prevent side channel action
SIDE_CH_REG_IQ_TRIGGER_write(10); // set iq trigger to rssi, which will never happen when rssi_th is 0
SIDE_CH_REG_NUM_EQ_write(num_eq_init); // capture CSI + 8*equalizer by default
if (iq_len_init>0) {//initialize the side channel into iq capture mode
//Max UDP 65507 bytes; (65507/8)-1 = 8187
if (iq_len_init>8187) {
iq_len_init = 8187;
printk("%s dev_probe: limit iq_len_init to 8187!\n",side_ch_compatible_str);
}
SIDE_CH_REG_IQ_CAPTURE_write(1);
SIDE_CH_REG_PRE_TRIGGER_LEN_write(8190);
SIDE_CH_REG_IQ_LEN_write(iq_len_init);
SIDE_CH_REG_IQ_TRIGGER_write(0); // trigger is set to fcs ok/nok (both)
}
SIDE_CH_REG_CONFIG_write(0x0001); // allow all packets by default; bit12 FC; bit13 addr1; bit14 addr2
//rst
for (i=0;i<8;i++)
SIDE_CH_REG_MULTI_RST_write(0);
@ -543,11 +612,6 @@ static int dev_probe(struct platform_device *pdev) {
err = init_side_channel();
printk("%s dev_probe: init_side_channel() err %d\n",side_ch_compatible_str, err);
printk("%s dev_probe: num_eq_init %d\n",side_ch_compatible_str, num_eq_init);
// SIDE_CH_REG_CONFIG_write(0X6001); // match addr1 and addr2; bit12 FC; bit13 addr1; bit14 addr2
SIDE_CH_REG_CONFIG_write(0x0001); // match all packets by default; bit12 FC; bit13 addr1; bit14 addr2
SIDE_CH_REG_NUM_EQ_write(num_eq_init); // capture CSI + 8*equalizer by default
return(err);
// err = dma_loopback_test(7, 512);

View File

@ -8,15 +8,20 @@ const char *side_ch_compatible_str = "sdr,side_ch";
#define EQUALIZER_LEN (56-4) // for non HT, four {32767,32767} will be padded to achieve 52 (non HT should have 48)
#define HEADER_LEN 2 //timestamp and frequency offset
#define MAX_NUM_DMA_SYMBOL 4096 //align with side_ch.v side_ch.h
#define MAX_NUM_DMA_SYMBOL 8192 //align with side_ch.v side_ch.h
#define SIDE_CH_REG_MULTI_RST_ADDR (0*4)
#define SIDE_CH_REG_CONFIG_ADDR (1*4)
#define SIDE_CH_REG_NUM_DMA_SYMBOL_ADDR (2*4) //low 16bit to PS; high 16bit to PL
#define SIDE_CH_REG_START_DMA_TO_PS_ADDR (3*4)
#define SIDE_CH_REG_IQ_CAPTURE_ADDR (3*4)
#define SIDE_CH_REG_NUM_EQ_ADDR (4*4)
#define SIDE_CH_REG_FC_TARGET_ADDR (5*4)
#define SIDE_CH_REG_ADDR1_TARGET_ADDR (6*4)
#define SIDE_CH_REG_ADDR2_TARGET_ADDR (7*4)
#define SIDE_CH_REG_IQ_TRIGGER_ADDR (8*4)
#define SIDE_CH_REG_RSSI_TH_ADDR (9*4)
#define SIDE_CH_REG_GAIN_TH_ADDR (10*4)
#define SIDE_CH_REG_PRE_TRIGGER_LEN_ADDR (11*4)
#define SIDE_CH_REG_IQ_LEN_ADDR (12*4)
#define SIDE_CH_REG_M_AXIS_DATA_COUNT_ADDR (20*4)

View File

@ -623,7 +623,7 @@
#address-cells = <0x1>;
#size-cells = <0x1>;
ranges;
/*
i2c@41600000 {
compatible = "xlnx,axi-iic-1.01.b", "xlnx,xps-iic-2.00.a";
reg = <0x41600000 0x10000>;
@ -679,7 +679,6 @@
};
};
/*
dma@43000000 {
compatible = "adi,axi-dmac-1.00.a";
reg = <0x43000000 0x10000>;
@ -740,7 +739,6 @@
linux,phandle = <0x13>;
phandle = <0x13>;
};
*/
axi-i2s@77600000 {
compatible = "adi,axi-i2s-1.00.a";
@ -754,7 +752,7 @@
phandle = <0x15>;
};
/*axi-sysid-0@45000000 {
axi-sysid-0@45000000 {
compatible = "adi,axi-sysid-1.00.a";
reg = <0x45000000 0x10000>;
};*/

View File

@ -0,0 +1,122 @@
#
# openwifi side info receive and display program
# Xianjun jiao. putaoshu@msn.com; xianjun.jiao@imec.be
#
import os
import sys
import socket
import numpy as np
import matplotlib.pyplot as plt
def display_iq(iq_capture, agc_gain, rssi_half_db):
fig_iq_capture = plt.figure(0)
fig_iq_capture.clf()
plt.xlabel("sample")
plt.ylabel("I/Q")
plt.title("I (blue) and Q (red) capture")
plt.plot(iq_capture.real, 'b')
plt.plot(iq_capture.imag, 'r')
plt.ylim(-32767, 32767)
fig_iq_capture.show()
plt.pause(0.0001)
agc_gain_lock = np.copy(agc_gain)
agc_gain_lock[agc_gain>127] = 80 # agc lock
agc_gain_lock[agc_gain<=127] = 0 # agc not lock
agc_gain_value = np.copy(agc_gain)
agc_gain_value[agc_gain>127] = agc_gain[agc_gain>127] - 128
fig_agc_gain = plt.figure(1)
fig_agc_gain.clf()
plt.xlabel("sample")
plt.ylabel("gain/lock")
plt.title("AGC gain (blue) and lock status (red)")
plt.plot(agc_gain_value, 'b')
plt.plot(agc_gain_lock, 'r')
plt.ylim(0, 82)
fig_agc_gain.show()
plt.pause(0.0001)
fig_rssi_half_db = plt.figure(2)
fig_rssi_half_db.clf()
plt.xlabel("sample")
plt.ylabel("dB")
plt.title("RSSI half dB (uncalibrated)")
plt.plot(rssi_half_db)
plt.ylim(100, 270)
fig_rssi_half_db.show()
plt.pause(0.0001)
def parse_iq(iq, iq_len):
# print(len(iq), iq_len)
num_dma_symbol_per_trans = 1 + iq_len
num_int16_per_trans = num_dma_symbol_per_trans*4 # 64bit per dma symbol
num_trans = round(len(iq)/num_int16_per_trans)
# print(len(iq), iq.dtype, num_trans)
iq = iq.reshape([num_trans, num_int16_per_trans])
timestamp = iq[:,0] + pow(2,16)*iq[:,1] + pow(2,32)*iq[:,2] + pow(2,48)*iq[:,3]
iq_capture = iq[:,4::4] + iq[:,5::4]*1j
agc_gain = iq[:,6::4]
rssi_half_db = iq[:,7::4]
# print(num_trans, iq_len, iq_capture.shape, agc_gain.shape, rssi_half_db.shape)
iq_capture = iq_capture.reshape([num_trans*iq_len,])
agc_gain = agc_gain.reshape([num_trans*iq_len,])
rssi_half_db = rssi_half_db.reshape([num_trans*iq_len,])
return timestamp, iq_capture, agc_gain, rssi_half_db
UDP_IP = "192.168.10.1" #Local IP to listen
UDP_PORT = 4000 #Local port to listen
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # UDP
sock.bind((UDP_IP, UDP_PORT))
# align with side_ch_control.v and all related user space, remote files
MAX_NUM_DMA_SYMBOL = 8192
if len(sys.argv)<2:
print("Assume iq_len = 8187! (Max UDP 65507 bytes; (65507/8)-1 = 8187)")
iq_len = 8187
else:
iq_len = int(sys.argv[1])
print(iq_len)
# print(type(num_eq))
if iq_len>8187:
iq_len = 8187
print('Limit iq_len to 8187! (Max UDP 65507 bytes; (65507/8)-1 = 8187)')
num_dma_symbol_per_trans = 1 + iq_len
num_byte_per_trans = 8*num_dma_symbol_per_trans
if os.path.exists("iq.txt"):
os.remove("iq.txt")
iq_fd=open('iq.txt','a')
while True:
try:
data, addr = sock.recvfrom(MAX_NUM_DMA_SYMBOL*8) # buffer size
# print(addr)
test_residual = len(data)%num_byte_per_trans
# print(len(data)/8, num_dma_symbol_per_trans, test_residual)
if (test_residual != 0):
print("Abnormal length")
iq = np.frombuffer(data, dtype='int16')
np.savetxt(iq_fd, iq)
timestamp, iq_capture, agc_gain, rssi_half_db = parse_iq(iq, iq_len)
print(timestamp)
display_iq(iq_capture, agc_gain, rssi_half_db)
except KeyboardInterrupt:
print('User quit')
break
print('close()')
side_info_fd.close()
sock.close()

View File

@ -18,7 +18,7 @@
#include <arpa/inet.h>
// #define NETLINK_USER 31
#define MAX_NUM_DMA_SYMBOL 4096 //align with side_ch.v side_ch.h
#define MAX_NUM_DMA_SYMBOL 8192 //align with side_ch.v side_ch.h
#define MAX_PAYLOAD (8*MAX_NUM_DMA_SYMBOL) /* maximum payload size*/
struct sockaddr_nl src_addr, dest_addr;
@ -134,8 +134,8 @@ long int hextoi_my(char *para) {
// write 987 to hardware register 3: wh3d987 (w--write; h--hardware; 3 --register idx; d--decimal; 987--value)
// write 0x3db to software register 19: ws19h3db (w--write; s--software; 19--register idx; h--hex; 3db--value 0x3db)
// read software register 23: rs23 (r-- read; s--software; 23--register idx)
// get csi and equalizer output: g4 (g-- get; 4--every 4*100ms; no/wrong input means default 100ms)
int parse_para_string(char *para, int *action_flag, int *reg_type, int *reg_idx, unsigned int *reg_val, int *interval_100ms) {
// get csi and equalizer output: g400 (g-- get; 400--every 400ms; no/wrong input means default 100ms)
int parse_para_string(char *para, int *action_flag, int *reg_type, int *reg_idx, unsigned int *reg_val, int *interval_ms) {
int i, para_string_len, num_char_reg_idx, num_char_reg_val, hex_flag;
para_string_len = strlen(para);
@ -150,17 +150,17 @@ int parse_para_string(char *para, int *action_flag, int *reg_type, int *reg_idx,
(*action_flag) = ACTION_SIDE_INFO_GET;
if (para_string_len == 1) { // no explict input
(*interval_100ms) = 1;
printf("The default 1*100ms side info getting period is taken!\n");
(*interval_ms) = 100;
printf("The default 100ms side info getting period is taken!\n");
return(0);
}
// there is something input
(*interval_100ms) = atoi_my(para+1);
if ( (*interval_100ms)<0 ) { // for invalid input, we set it to the default 100ms = 1*100ms;
(*interval_100ms) = 1;
(*interval_ms) = atoi_my(para+1);
if ( (*interval_ms)<0 ) { // for invalid input, we set it to the default 100ms
(*interval_ms) = 100;
printf("Invalid side info getting period!\n");
printf("The default 1*100ms side info getting period is taken!\n");
printf("The default 100ms side info getting period is taken!\n");
}
return(0);
@ -269,7 +269,7 @@ void print_usage(void) {
printf("write 987 to hardware register 3: wh3d987 (w--write; h--hardware; 3 --register idx; d--decimal; 987--value)\n");
printf("write 0x3db to software register 19: ws19h3db (w--write; s--software; 19--register idx; h--hex; 3db--value 0x3db)\n");
printf(" read software register 23: rs23 (r-- read; s--software; 23--register idx)\n");
printf(" get csi and equalizer output: g2 (g-- get; 4--every 4*100ms; no/wrong input means default 100ms)\n");
printf(" get csi and equalizer output: g400 (g-- get; 400--every 400ms; no/wrong input means default 100ms)\n");
}
volatile bool do_exit = false;
@ -282,7 +282,7 @@ void sigint_callback_handler(int signum)
int main(const int argc, char * const argv[])
{
int action_flag, reg_type, reg_idx, interval_100ms, s, side_info_size, socket_ok = 1, loop_count=0, side_info_count=0;
int action_flag, reg_type, reg_idx, interval_ms, s, side_info_size, socket_ok = 1, loop_count=0, side_info_count=0;
unsigned int reg_val, *cmd_buf;
unsigned short port;
struct sockaddr_in server;
@ -294,9 +294,9 @@ int main(const int argc, char * const argv[])
return(ret);
}
ret = parse_para_string(argv[1], &action_flag, &reg_type, &reg_idx, &reg_val, &interval_100ms);
ret = parse_para_string(argv[1], &action_flag, &reg_type, &reg_idx, &reg_val, &interval_ms);
printf("parse: ret %d\n", ret);
printf(" tx: action_flag %d reg_type %d reg_idx %d reg_val %u interval_100ms %d\n", action_flag, reg_type, reg_idx, reg_val, interval_100ms);
printf(" tx: action_flag %d reg_type %d reg_idx %d reg_val %u interval_ms %d\n", action_flag, reg_type, reg_idx, reg_val, interval_ms);
if (ret<0) {
printf("Wrong input!\n");
print_usage();
@ -378,7 +378,7 @@ int main(const int argc, char * const argv[])
// printf("Received message payload: %s\n", (char *)NLMSG_DATA(nlh));
side_info_size = nlh->nlmsg_len-NLMSG_HDRLEN;
// printf("%d %d %d %d %d %d %d %d\n", cmd_buf[0], cmd_buf[1], cmd_buf[2], cmd_buf[3], cmd_buf[4], cmd_buf[5], cmd_buf[6], cmd_buf[7]);
// printf("num_dma_symbol %d\n", side_info_size/8);
if (action_flag!=ACTION_SIDE_INFO_GET) {
printf(" rx: size %d val %d 0x%08x\n", side_info_size, cmd_buf[0], cmd_buf[0]);
@ -394,7 +394,7 @@ int main(const int argc, char * const argv[])
if ((loop_count%64) == 0)
printf("loop %d side info count %d\n", loop_count, side_info_count);
usleep(interval_100ms*100*1000);
usleep(interval_ms*1000);
}
close(s);

View File

@ -114,6 +114,7 @@ sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # UDP
sock.bind((UDP_IP, UDP_PORT))
# align with side_ch_control.v and all related user space, remote files
MAX_NUM_DMA_SYMBOL = 8192
CSI_LEN = 56 # length of single CSI
EQUALIZER_LEN = (56-4) # for non HT, four {32767,32767} will be padded to achieve 52 (non HT should have 48)
HEADER_LEN = 2 # timestamp and frequency offset
@ -135,7 +136,7 @@ side_info_fd=open('side_info.txt','a')
while True:
try:
data, addr = sock.recvfrom(32768) # buffer size
data, addr = sock.recvfrom(MAX_NUM_DMA_SYMBOL*8) # buffer size
# print(addr)
print(len(data), num_byte_per_trans)
test_residual = len(data)%num_byte_per_trans

View File

@ -0,0 +1,47 @@
% Xianjun Jiao. xianjun.jiao@imec.be; putaoshu@msn.com
clear all;
close all;
iq_len = 8187;
a = load('iq.txt');
len_a = floor(length(a)/4)*4;
a = a(1:len_a);
b = reshape(a, [4, length(a)/4])';
num_data_in_each_iq_capture = 1 + iq_len;
num_iq_capture = floor(size(b,1)/num_data_in_each_iq_capture);
iq_capture = zeros(iq_len, num_iq_capture);
timestamp = zeros(1, num_iq_capture);
agc_gain = zeros(iq_len, num_iq_capture);
rssi_half_db = zeros(iq_len, num_iq_capture);
for i=1:num_iq_capture
sp = (i-1)*num_data_in_each_iq_capture + 1;
ep = i*num_data_in_each_iq_capture;
timestamp(i) = b(sp,1) + (2^16)*b(sp,2) + (2^32)*b(sp,3) + (2^48)*b(sp,4);
iq_capture(:,i) = b((sp+1):ep,1) + 1i.*b((sp+1):ep,2);
agc_gain(:,i) = b((sp+1):ep,3);
rssi_half_db(:,i) = b((sp+1):ep,4);
end
iq_capture = iq_capture(:);
agc_gain = agc_gain(:);
rssi_half_db = rssi_half_db(:);
agc_gain_lock = zeros(iq_len*num_iq_capture,1);
agc_gain_lock(agc_gain>127) = 1;
agc_gain_value = agc_gain;
agc_gain_value(agc_gain_value>127) = agc_gain_value(agc_gain_value>127) - 128;
figure; plot(timestamp); title('time stamp (TSF value)'); ylabel('us'); xlabel('packet'); grid on;
figure; plot(rssi_half_db); title('RSSI half dB (uncalibrated)'); xlabel('sample'); ylabel('dB'); grid on;
figure;
plot(real(iq_capture)); hold on; plot(imag(iq_capture),'r'); title('I (blue) Q (red) sample'); xlabel('sample'); ylabel('I/Q'); grid on;
figure;
subplot(2,1,1); plot(agc_gain_lock); title('AGC lock status from AD9361'); xlabel('sample'); ylabel('status'); grid on;
subplot(2,1,2); plot(agc_gain_value); title('AGC gain from AD9361'); xlabel('sample'); ylabel('gain'); grid on;

View File

@ -6,9 +6,12 @@ close all;
num_eq = 8;
a = load('side_info.txt');
len_a = floor(length(a)/4)*4;
a = a(1:len_a);
b = reshape(a, [4, length(a)/4])';
num_data_in_each_side_info = 2+56+num_eq*52;
num_side_info = size(b,1)/num_data_in_each_side_info;
num_side_info = floor(size(b,1)/num_data_in_each_side_info);
side_info = zeros(num_data_in_each_side_info, num_side_info);
timestamp = zeros(1, num_side_info);