前段时间我在做低功耗蓝牙的时候看到了这个网页

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 timecr = 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;elsecurrent_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;elsecurrent_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;elsecurrent_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;elsecurrent_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;} elsepacket_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解调相关推荐

  1. Portapack应用开发教程(十七)nrf24l01发射

    以前用portapack实现过nrf24l01的接收,接下去想试试发射功能. 我在ymfc-mini-drone week6_future_experiment_哔哩哔哩_bilibili里面讲过gr ...

  2. 小程序云开发教程七:贴子的详情及评论功能

    我们先看看界面: 我们如果要实现评论功能, 先看一下总的数据结构: 那么需要什么参数呢? 参数如下 comment: 评论内容username: 用户名time: 评论时间userId: 用户idid ...

  3. 微信公众号开发教程(七)JSSDK-监听分享朋友圈事件

    作者:陈惠,叩丁狼教育高级讲师.原创文章,转载请注明出处. 微信JS-SDK是微信公众平台 面向网页开发者提供的基于微信内的网页开发工具包. 通过使用微信JS-SDK,网页开发者可借助微信高效地使用拍 ...

  4. Portapack应用开发教程(六)低功耗蓝牙解调

    前段时间我本来想解调apc220,这样我可以用portapack接收数传数据,但是这个模块资料太少. 后来我想到其实四轴里也经常用nrf24l01或者低功耗蓝牙来做数传,这两种模块也都是fsk.而且更 ...

  5. Portapack应用开发教程(十二) SSTV接收机 A

    接下来我要讲一个高难度应用,SSTV接收机. 很多ham都喜欢玩sstv,一般方式都是用一个对讲机来接收地面波或者卫星发出的信号,然后用手机录制对讲机发出来的声音,再把这个录音放到电脑旁边播放来解调( ...

  6. Portapack应用开发教程(十) 猎狐功能和RSSI数值显示

    视频参见:https://www.bilibili.com/video/BV1Y541147Sv 可以看到当我在analog audio的app里,在nfm模式下,我可以点击RSSI按钮,这时候发出的 ...

  7. Portapack应用开发教程(十二) SSTV接收机 B

    上一篇帖子代码贴了太多太长了,所以重新开一篇帖子. 我终于把解调和解码程序合并到一起去了. #include <stdio.h> #include <stdint.h> #in ...

  8. Portapack应用开发教程(十五) APRS接收

    APRS功能可以用对讲机来传输数字信息,以便在没有运营商网络的环境下共享位置,实现类似微信的位置共享功能,也可以用它来发短消息. 有人买直接带aprs的对讲机,比较贵.也有人用普通对讲机和手机通过音频 ...

  9. Portapack应用开发教程(十八)NavTex接收 B

    解调做完后,我开始研究解码. 我找到了这个资源不错.可以用来解码音频文件里的navtex文字信息. GitHub - pd0wm/navtex: Simple Navtex decoder 它用的ip ...

最新文章

  1. Webpack 资源管理
  2. Redis 21问,你接得住不?
  3. python好不好用_但python以前不太好用
  4. 车载wince系统刷界面ui_2020年值得关注的10个UI设计趋势!
  5. 在C#中用RX库和await来实现直观的状态机
  6. mac python2.7升级到3.7_Mac 升级 Python2.7 到 Python3.5
  7. Linux性能调优集合
  8. [转]【NODE】用WS模块创建加密的WS服务(WSS)
  9. golang 腾讯云直播开发日记(一)
  10. android tracert命令详解,Tracert命令详解
  11. win10实现宽带转WiFi
  12. .m3u8视频格式转换
  13. vc++实现内核级进程保护
  14. 【\u20a 错误解决】
  15. Redis解决高并发(秒杀抢红包)
  16. 手机、浏览器的分辨率、状态栏参数
  17. post和get传参(重点)
  18. 7.2 IDEA 没有Java EE
  19. 哈工大《大数据计算基础》期末考试2021
  20. Hadoop集群塔建常见bug

热门文章

  1. 手把手带你撸一个校园APP(六):失物招领二手交易模块
  2. 干货 | LIDAR、ToF相机、双目相机如何科学选择?
  3. 《文明6》引言科普 引言出处讲解 【转】
  4. 根据流程图写python程序_根的解释|根的意思|汉典“根”字的基本解释
  5. 桌面计算机图标无响应,win7系统电脑鼠标点击桌面图标没反应怎么办【图文】...
  6. 基于Visual C++2010 与office2010开发办公自动化 2 -自动生成excel与word并打开
  7. oracle 2019 ocp,关于Oracle数据库管理2019 OCP专家认证
  8. 世界上最全的防醉酒+解酒法(为了关心的人,一定要看看哦)
  9. linux文件系统实现原理简述【转】
  10. Day644.Spring框架开发双刃剑 -Java业务开发常见错误