概述
前段时间我在做低功耗蓝牙的时候看到了这个网页
https://wiki.bitcraze.io/misc:hacks:hackrf
但是我当时没有成功解调nrf24l01,反而成功搞定蓝牙了。
后来我用了我自己新买的小四轴也没解调成功,所以我打算老老实实地重现那个网页上的步骤。
正好我以前买过crazyflie的产品(小四轴和crazyradio模块),我就找出了这个crazyradio模块来重复实验。
期间我还看到了几个好的资源。
https://www.bitcraze.io/2015/06/sniffing-crazyflies-radio-with-hackrf-blue/
这个也是同一网站的相关内容,但是更详细。
http://blog.cyberexplorer.me/2014/01/sniffing-and-decoding-nrf24l01-and.html
这是nrf24l01和btle的解码程序作者的文章。从这篇文章,我发现nrf24l01解码程序的输入必须是2M的数据。
然后根据nrf24l01的发射机硬件选择的速率模式来决定降采样率。如果nrf24l01设置的速度是2M那么降采样率就是1,如果是1M那么降采样率是2,如果是256K那么降采样率就是8。这个值不能自动判断,需要根据发射机事先指定好。
另外在实验过程中,我还发现一个坑。gnuradio流图输出的fifo,在每次实验前都需要清空再新建,否则里面的数据就是老的。无法以此来判断是否准确解码。
我的开发计划分为4步:
1.重复实验crazyflie网页上的步骤,使用crazyradio来发射,然后用gnuradio流图来做fm解调输出给fifo,再用nrf解码程序来解码。
2.合并gnuradio的fm解调程序和nrf解码程序,这样在电脑上用一个单独的程序就能解调crazyradio发射的数据了。
3.把这个独立的程序搬到portapack里,解调crazyradio的数据。
4.用portapack去解调小四轴的遥控器指令。
目前我第一步已经实现了。
首先,我下载了crazyflie的库,这样就能用python语言来控制它,并发出自己想要的数据。
https://github.com/bitcraze/crazyradio-firmware
不用编译安装,但是需要安装一些依赖
sudo apt install python-usb
然后到lib下,可以看到crazyradio.py,这就是我们自己写的Python程序要调用的库了。
然后在同一个文件夹下新建我们自己的python程序try.py
from crazyradio import Crazyradio
import time
cr = Crazyradio()
cr.set_channel(26)
cr.set_data_rate(cr.DR_250KPS)
while True:
cr.send_packet((0,1,2,3,4))
time.sleep(0.1)
然后运行
sudo python try.py
这样就开始发射了。
接下来画一个流图
如果你不想自己画可以下载下面的流图,不过它的参数和我不太一样,你要把参数改成和我一样的。
https://wiki.bitcraze.io/_media/misc:hacks:nrf24_demod.grc.zip
再然后就可以参照我以前的文章,编译NRF24-BTLE-Decoder程序,就是我前面说的nrf解码程序。
搞定以后的运行顺序是:
sudo rm /tmp/fifo
mkfifo /tmp/fifo
然后运行gnuradio流图
cat /tmp/fifo | ./nrf24-btle-decoder -d 8
这样你就能看到终端里的这样的输出了
你可以看到大多数数据包的地址是E7E7E7E7E7,就是我们发的数据包了,数据是00 01 02 03 04,这正好是我们前面的python程序里发射的0 1 2 3 4了。
少数数据包不对可能是干扰导致的。
这样第一步就做完了。
第二步:
我尝试了合并gnuradio解调和nrf解码
hackrf_nrf.cpp:
#include <signal.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <math.h>
#include <pthread.h>
#include <libhackrf/hackrf.h>
#include <iostream>
#include <stdint.h>
#include <time.h>
#include <sys/time.h>
#include <stdbool.h>
#include <inttypes.h>
#define MAXIMUM_BUF_LENGTH (16 * 16384)
/* Global variables */
int32_t g_threshold = 0; // Quantization threshold
int g_srate = 8;
uint8_t channel_number = 38;
int skipSamples = 1000;
/* Ring Buffer */
#define RB_SIZE 1000
int rb_head=-1;
int16_t *rb_buf;
using namespace std;
static volatile bool do_exit = false;
hackrf_device *device;
uint32_t freq;
uint32_t hardware_sample_rate;
uint16_t buf16[MAXIMUM_BUF_LENGTH];
int16_t buffer[MAXIMUM_BUF_LENGTH];
int lp_len;
int rate_in;
int16_t result_demod[MAXIMUM_BUF_LENGTH];
int result_demod_len;
int pre_r, pre_j;
void sigint_callback_handler(int signum)
{
cout << "Caught signal" << endl;
do_exit = true;
}
void multiply(int ar, int aj, int br, int bj, int *cr, int *cj)
{
*cr = ar*br - aj*bj;
*cj = aj*br + ar*bj;
}
int polar_discriminant(int ar, int aj, int br, int bj)
{
int cr, cj;
double angle;
multiply(ar, aj, br, -bj, &cr, &cj);
angle = atan2((double)cj, (double)cr);
return (int)(angle / 3.14159 * (1<<14));
}
int rx_callback(hackrf_transfer* transfer)
{
for (int i = 0; i < transfer->valid_length; i++)
{
double sample = (int8_t)(transfer->buffer[i]) + 1;
buf16[i] = (int16_t)sample; //s->buf16[i] = (int16_t)buf[i] - 127; s->buf16[i] -127~128 uint16_t, unsigned for negative?
}
memcpy(buffer, buf16, 2*transfer->valid_length);
lp_len = transfer->valid_length;
//fm demod //rate = rate_in = 2M
int i, pcm;
pcm = polar_discriminant(buffer[0], buffer[1], pre_r, pre_j);
result_demod[0] = (int16_t)pcm;
for (i = 2; i < (lp_len-1); i += 2)
{
pcm = polar_discriminant(buffer[i], buffer[i+1], buffer[i-2], buffer[i-1]);
result_demod[i/2] = (int16_t)pcm;
}
pre_r = buffer[lp_len - 2];
pre_j = buffer[lp_len - 1];
result_demod_len = lp_len/2;
int i4;
for (i4 = 0; i4 < result_demod_len; i4 += 1)
{
int16_t cursamp = (int16_t) (result_demod[i4]);
rb_head++;
rb_head=(rb_head)%RB_SIZE;
rb_buf[(rb_head)%RB_SIZE]=(int)cursamp;
skipSamples = skipSamples - 1;
if (skipSamples<1)
{
int32_t threshold_tmp=0;
for (int c=0;c<8*g_srate;c++)
{
threshold_tmp = threshold_tmp + (int32_t)rb_buf[(rb_head+c)%RB_SIZE];
}
g_threshold = (int32_t)threshold_tmp/(8*g_srate);
int transitions=0;
if (rb_buf[(rb_head + 9*g_srate)%RB_SIZE] > g_threshold)
{
for (int c=0;c<8;c++)
{
if (rb_buf[(rb_head + c*g_srate)%RB_SIZE] > rb_buf[(rb_head + (c+1)*g_srate)%RB_SIZE])
transitions = transitions + 1;
}
}
else
{
for (int c=0;c<8;c++)
{
if (rb_buf[(rb_head + c*g_srate)%RB_SIZE] < rb_buf[(rb_head + (c+1)*g_srate)%RB_SIZE])
transitions = transitions + 1;
}
}
bool packet_detected=false;
if (transitions==4 && abs(g_threshold)<15500)
{
int packet_length = 0;
uint8_t tmp_buf[10];
uint8_t packet_data[500];
uint8_t packet_packed[50];
uint16_t pcf;
uint32_t packet_crc;
uint32_t calced_crc;
uint64_t packet_addr_l;
/* extract address */
packet_addr_l=0;
for (int t=0;t<5;t++)
{
bool current_bit;
uint8_t byte=0;
for (int c=0;c<8;c++)
{
if (rb_buf[(rb_head+(1*8+t*8+c)*g_srate)%RB_SIZE] > g_threshold)
current_bit = true;
else
current_bit = false;
byte |= current_bit << (7-c);
}
tmp_buf[t]=byte;
}
for (int t=0;t<5;t++) packet_addr_l|=((uint64_t)tmp_buf[t])<<(4-t)*8;
/* extract pcf */
for (int t=0;t<2;t++)
{
bool current_bit;
uint8_t byte=0;
for (int c=0;c<8;c++)
{
if (rb_buf[(rb_head+(6*8+t*8+c)*g_srate)%RB_SIZE] > g_threshold)
current_bit = true;
else
current_bit = false;
byte |= current_bit << (7-c);
}
tmp_buf[t]=byte;
}
pcf = tmp_buf[0]<<8 | tmp_buf[1];
pcf >>=7;
/* extract packet length, avoid excessive length packets */
if(packet_length == 0)
packet_length=(int)pcf>>3;
if (packet_length>32)
packet_detected = false;
/* extract data */
for (int t=0;t<packet_length;t++)
{
bool current_bit;
uint8_t byte=0;
for (int c=0;c<8;c++)
{
if (rb_buf[(rb_head+(6*8+9+t*8+c)*g_srate)%RB_SIZE] > g_threshold)
current_bit = true;
else
current_bit = false;
byte |= current_bit << (7-c);
}
packet_data[t]=byte;
}
/* Prepare packed bit stream for CRC calculation */
uint64_t packet_header=packet_addr_l;
packet_header<<=9;
packet_header|=pcf;
for (int c=0;c<7;c++){
packet_packed[c]=(packet_header>>((6-c)*8))&0xFF;
}
for (int c=0;c<packet_length;c++){
packet_packed[c+7]=packet_data[c];
}
/* calculate packet crc */
const uint8_t* data = packet_packed;
size_t data_len = 7+packet_length;
bool bit;
uint8_t cc;
uint_fast16_t crc=0x3C18;
while (data_len--) {
cc = *data++;
for (uint8_t i = 0x80; i > 0; i >>= 1)
{
bit = crc & 0x8000;
if (cc & i)
{
bit = !bit;
}
crc <<= 1;
if (bit)
{
crc ^= 0x1021;
}
}
crc &= 0xffff;
}
calced_crc = (uint16_t)(crc & 0xffff);
/* extract crc */
for (int t=0;t<2;t++)
{
bool current_bit;
uint8_t byte=0;
for (int c=0;c<8;c++)
{
if (rb_buf[(rb_head+((6+packet_length)*8+9+t*8+c)*g_srate)%RB_SIZE] > g_threshold)
current_bit = true;
else
current_bit = false;
byte |= current_bit << (7-c);
}
tmp_buf[t]=byte;
}
packet_crc = tmp_buf[0]<<8 | tmp_buf[1];
/* NRF24L01+ packet found, dump information */
if (packet_crc==calced_crc)
{
printf("NRF24, Threshold:%"PRId32", Address: 0x%08"PRIX64" ",g_threshold,packet_addr_l);
printf("length:%d, pid:%d, no_ack:%d, CRC:0x%04X data:",packet_length,(pcf&0b110)>>1,pcf&0b1,packet_crc);
for (int c=0;c<packet_length;c++) printf("%02X ",packet_data[c]);
printf("n");
packet_detected = true;
}
else
packet_detected = false;
}
if (packet_detected)
{
skipSamples=20;
}
}
}
return 0;
}
int main(int argc, char **argv)
{
signal(SIGINT, &sigint_callback_handler);
int result;
rb_buf = (int16_t *)malloc(RB_SIZE*2);
freq = 2426e6;
rate_in = 2000000;
hardware_sample_rate = (uint32_t)(rate_in);
result = hackrf_init();
if( result != HACKRF_SUCCESS )
{
cout << "hackrf_init() failed" << endl;
return EXIT_FAILURE;
}
result = hackrf_open(&device);
if( result != HACKRF_SUCCESS )
{
cout << "hackrf_open() failed" << endl;
return EXIT_FAILURE;
}
result = hackrf_set_lna_gain(device, 14);
if( result != HACKRF_SUCCESS )
{
cout << "hackrf_set_lna_gain() failed" << endl;
return EXIT_FAILURE;
}
result = hackrf_set_vga_gain(device, 20);
if( result != HACKRF_SUCCESS )
{
cout << "hackrf_set_vga_gain() failed" << endl;
return EXIT_FAILURE;
}
/* Set the frequency */
result = hackrf_set_freq(device, freq);
if( result != HACKRF_SUCCESS )
{
cout << "hackrf_set_freq() failed" << endl;
return EXIT_FAILURE;
}
/* Set the sample rate */
result = hackrf_set_sample_rate(device, hardware_sample_rate);
if( result != HACKRF_SUCCESS )
{
cout << "hackrf_set_sample_rate() failed" << endl;
return EXIT_FAILURE;
}
result = hackrf_set_baseband_filter_bandwidth(device, hardware_sample_rate);
if( result != HACKRF_SUCCESS )
{
cout << "hackrf_baseband_filter_bandwidth_set() failed" << endl;
return EXIT_FAILURE;
}
fprintf(stderr, "Output at %u Hz.n", rate_in);
usleep(100000);
result = hackrf_start_rx(device, rx_callback, NULL);
while ((hackrf_is_streaming(device) == HACKRF_TRUE) && (do_exit == false))
{
usleep(100000);
}
if (do_exit)
{
fprintf(stderr, "nUser cancel, exiting...n");
}
else
{
fprintf(stderr, "nLibrary error, exiting...n");
}
result = hackrf_close(device);
if(result != HACKRF_SUCCESS)
{
cout << "hackrf_close() failed" << endl;
}
else
{
cout << "hackrf_close() done" << endl;
}
hackrf_exit();
cout << "hackrf_exit() done" << endl;
return 0;
}
g++ hackrf_nrf.cpp -o hackrf_nrf -lhackrf -pthread
./hackrf_nrf
记得增益不能太高,否则反而收不到
第三步:
我把合并完的代码仿照btle加入到portapack里了,本来需要的是8MHz降采样4倍到2MHz这样才能解调2MPS的信号,但是实际发现不行,只能4MHz降采样4倍,这样理论上只能解调1MPS和250KPS,实际我发现连1MPS都不行,所以暂时只能解调250KPS。
红圈里就是我的crazyradio发射的数据包被解出来了。
后续我可能会解调一下实际小四轴的遥控器。
代码已经上传github了,改的部分和btle差不多,主要是ui_nrf_rx.cpp ui_nrf_rx.hpp proc_nrfrx.cpp proc_nrfrx.hpp这四个文件。
第四步:
下载安装crazyflie-clients-python,它会给你安装一个小飞机地面站,可以用crazyradio来和crazyflie双向通信,观察数传信息,如果有操纵杆也可以控制飞机。
git clone https://github.com/bitcraze/crazyflie-clients-python.git
#如果pip3命令安装这个包的时候,提示internal之类的错误,要运行下面两行
wget https://bootstrap.pypa.io/get-pip.py --no-check-certificate
sudo python3 get-pip.py
#然后再次用pip3安装
pip3 install -e . --user
#装完就能运行了,记得加sudo,否则无法访问crazyradio
sudo ./cfclient
另外,如果想深入研究这个地面站如何调用nrf24l01来发数据,建议看一下
https://github.com/bitcraze/crazyflie-lib-python
这里的代码会生成cflib,在装crazyflie-clients-python的时候会自动安装编译好的版本。所以这个crazyflie-lib-python仅仅供观察代码,不要重复安装,如果想要自己编译这个包来安装,需要在安装crazyflie-clients-python之前来做,步骤也不太一样。
视频:https://www.bilibili.com/video/BV1vT4y1G7S7
我这里暂时没找到游戏操纵杆,所以今天演示的只是数传数据,控制使用的是Iphone经过蓝牙连接手机,而不是usb操纵杆。
最后
以上就是美好眼神为你收集整理的Portapack应用开发教程(七)nrf24l01解调的全部内容,希望文章能够帮你解决Portapack应用开发教程(七)nrf24l01解调所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复