前段时间我本来想解调apc220,这样我可以用portapack接收数传数据,但是这个模块资料太少。

后来我想到其实四轴里也经常用nrf24l01或者低功耗蓝牙来做数传,这两种模块也都是fsk。而且更好的是已经有人写了nrf和btle的解调了,只不过这个人使用的是rtlsdr做接收然后stdout输出给他的解码程序。

https://github.com/omriiluz/NRF24-BTLE-Decoder

我之前就知道这个包,但是一直没有用,因为rtlsdr不支持2.4g,需要一个下变频器变到支持的范围。后来我找到crazyflie(国外知名开源小四轴)官网上有讲解如何用gnuradio把hackrf的数据包给解码器解码的文章。

https://wiki.bitcraze.io/misc:hacks:hackrf#sniffing_nrf24_with_gnu_radio_and_hackrf

我仿照文章的操作发现真的可以用(不过有个缓存延缓写入问题)。

正好近期我又仿照rtl_fm写了个hackrf解调fm的纯c程序(不需要gnuradio)。这样我就可以直接用一个c++程序实现hackrf直接解调btle或者nrf24l01了。

不但如此,我还把蓝牙解调全部写到了一起(把函数调用都去掉了),这样后面加入到portapack中会方便好多。

废话不多说,上代码:

#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
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 =  1M  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]=(int)cursamp;skipSamples = skipSamples - 1;if (skipSamples<1){   int32_t threshold_tmp=0;for (int c=0;c<8;c++){threshold_tmp = threshold_tmp + (int32_t)rb_buf[(rb_head+c)%RB_SIZE];}g_threshold = (int32_t)threshold_tmp/8;int transitions=0;if (rb_buf[(rb_head+9)%RB_SIZE] > g_threshold){for (int c=0;c<8;c++){if (rb_buf[(rb_head + c)%RB_SIZE] > rb_buf[(rb_head + c + 1)%RB_SIZE])transitions = transitions + 1;}}else{for (int c=0;c<8;c++){if (rb_buf[(rb_head + c)%RB_SIZE] < rb_buf[(rb_head + c + 1)%RB_SIZE])transitions = transitions + 1;}}bool packet_detected=false;if ( transitions==4 && abs(g_threshold)<15500){uint8_t packet_data[500];int packet_length;uint32_t packet_crc;uint32_t calced_crc;uint64_t packet_addr_l;uint8_t crc[3];uint8_t packet_header_arr[2];/* extract address */packet_addr_l=0;for (int i=0;i<4;i++) {                   bool current_bit;uint8_t byte=0;for (int c=0;c<8;c++){if (rb_buf[(rb_head + (i+1)*8 + c)%RB_SIZE] > g_threshold)current_bit = true;elsecurrent_bit = false;byte |= current_bit << (7-c);}uint8_t byte_temp = (uint8_t) (((byte * 0x0802LU & 0x22110LU) | (byte * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16);packet_addr_l|=((uint64_t)byte_temp)<<(8*i);}if (freq == 2402e6){channel_number = 37;}else if (freq == 2426e6){channel_number = 38;}else if (freq = 2480e6){channel_number = 39;}/* extract pdu header */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 + 5*8+t*8 + c)%RB_SIZE] > g_threshold)current_bit = true;elsecurrent_bit = false;byte |= current_bit << (7-c);}packet_header_arr[t] = byte;}/* whiten header only so we can extract pdu length *///BTLEWhiten(packet_header_arr, 2, channel_number);uint8_t byte_temp2 = (uint8_t) (((channel_number * 0x0802LU & 0x22110LU) | (channel_number * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16);uint8_t lfsr_1 = byte_temp2 | 2;int header_length = 2;int header_counter = 0;while(header_length--){for(uint8_t i = 0x80; i; i >>= 1){if(lfsr_1 & 0x80){lfsr_1 ^= 0x11;(packet_header_arr[header_counter]) ^= i;}lfsr_1 <<= 1;}header_counter = header_counter + 1;}if (packet_addr_l==0x8E89BED6){  // Advertisement packetuint8_t byte_temp3 = (uint8_t) (((packet_header_arr[1] * 0x0802LU & 0x22110LU) | (packet_header_arr[1] * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16);packet_length=byte_temp3&0x3F;} else {packet_length=0;}/* extract and whiten pdu+crc */for (int t=0;t<packet_length+2+3;t++){bool current_bit;uint8_t byte=0;for (int c=0;c<8;c++){if (rb_buf[(rb_head + 5*8+t*8 + c)%RB_SIZE] > g_threshold)current_bit = true;elsecurrent_bit = false;byte |= current_bit << (7-c);}packet_data[t] = byte;}//BTLEWhiten(packet_data, packet_length+2+3, channel_number);uint8_t byte_temp4 = (uint8_t) (((channel_number * 0x0802LU & 0x22110LU) | (channel_number * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16);uint8_t lfsr_2 = byte_temp4 | 2;int pdu_crc_length = packet_length+2+3;int pdu_crc_counter = 0;while(pdu_crc_length--){for(uint8_t i = 0x80; i; i >>= 1){if(lfsr_2 & 0x80){lfsr_2 ^= 0x11;(packet_data[pdu_crc_counter]) ^= i;}lfsr_2 <<= 1;}pdu_crc_counter = pdu_crc_counter + 1;}if (packet_addr_l==0x8E89BED6){  // Advertisement packetcrc[0]=crc[1]=crc[2]=0x55;}else {crc[0]=crc[1]=crc[2]=0;}/* calculate packet crc *///calced_crc=BTLECrc(packet_data, packet_length+2, crc);uint8_t v, t, d, crc_length;uint32_t crc_result=0;crc_length = packet_length + 2;int counter = 0;while(crc_length--){uint8_t byte_temp5 = (uint8_t) (((packet_data[counter] * 0x0802LU & 0x22110LU) | (packet_data[counter] * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16);d = byte_temp5;for(v = 0; v < 8; v++, d >>= 1){t = crc[0] >> 7;crc[0] <<= 1;if(crc[1] & 0x80) crc[0] |= 1;crc[1] <<= 1;if(crc[2] & 0x80) crc[1] |= 1;crc[2] <<= 1;if(t != (d & 1)){crc[2] ^= 0x5B;crc[1] ^= 0x06;}}counter = counter + 1;}for (v=0;v<3;v++) crc_result=(crc_result<<8)|crc[v];calced_crc = crc_result;packet_crc=0;for (int c=0;c<3;c++) packet_crc=(packet_crc<<8)|packet_data[packet_length+2+c];/* BTLE packet found, dump information */if (packet_crc==calced_crc){//printf("MAC: ");uint8_t mac_data[6];int counter = 0;for (int i = 7; i >= 2; i--) {uint8_t byte_temp6 = (uint8_t) (((packet_data[i] * 0x0802LU & 0x22110LU) | (packet_data[i] * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16);mac_data[counter] = byte_temp6;counter = counter + 1;}for (int i = 0; i < 6; i++){printf("%02X ", mac_data[i]);}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; // chan 38//freq = 2402e6; // chan 37//freq = 2480e6;   // chan 39rate_in = 1000000;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, 40);if( result != HACKRF_SUCCESS ) {cout << "hackrf_set_lna_gain() failed" << endl;return EXIT_FAILURE;}result = hackrf_set_vga_gain(device, 26);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_ble_combined.cpp -o hackrf_ble -lhackrf -pthread./hackrf_ble

接下来我就开始往portapack上搬了。

我参考了afsk_rx和wfm两个程序。

代码可以上我的github portapack repo里看,我就不贴了。

/firmware/application/apps/ui_btle_rx.cpp
/firmware/application/apps/ui_btle_rx.hpp
/firmware/baseband/proc_btlerx.cpp
/firmware/baseband/proc_btlerx.hpp

不过我还是会碰到问题,一个是我现在采样率必须是4MHz,其实1MHz就够了,但是现在有个格式类型的问题,我必须用portapack库里的降采样函数来转格式类型,降采样函数最低4倍,我需要1MHz的输出,就必须要有4MHz的输入,这样其实会有其他频点的噪声进来。

还有个问题,我现在跳过了crc校验,mac地址有时候有几位是错的。如果我用crc校验,结果会少好多,但是最后结果的mac地址前三位是对的,后三位也是错的,而且与真实值差别很大。所以这个还有优化空间。

下面两个图片是portapack收到的mac和电脑运行hcitool lescan使用蓝牙芯片搜索的结果对比

下面图片我又改了改界面,更方便观察了,但是你可以看到最下面两行mac地址有一些错误的地方

Portapack应用开发教程(六)低功耗蓝牙解调相关推荐

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

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

  2. 微信公众号开发教程(六)获取微信用户信息-网页授权

    作者:陈惠,叩丁狼教育高级讲师.原创文章,转载请注明出处. 在学习网页授权之前,我们先来了解下这次的需求: 我们的应用中有一个用来显示个人信息的页面,当微信用户在微信app中打开这个页面,希望可以获取 ...

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

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

  4. android低耗能蓝牙开发,Android BLE低功耗蓝牙开发

    最近做了一个智能硬件开发(针灸仪)的项目,有一部分涉及到低功耗蓝牙的开发,就是通过蓝牙和设备进行数据的交互,比如控制改设备的LED的开关,设备的开关机,设置设备的时间和温度等,下面就项目中遇到的坑一一 ...

  5. 【Android】蓝牙开发——BLE(低功耗蓝牙)(附完整Demo)

    目录 目录 前言 一.相关概念介绍 二.实战开发 三.项目演示 四.Demo案例源码地址 五.更新记录 1.2020/12/29 :修改 setupService()中错误 2.2021/05/14 ...

  6. Portapack应用开发教程(七)nrf24l01解调

    前段时间我在做低功耗蓝牙的时候看到了这个网页 https://wiki.bitcraze.io/misc:hacks:hackrf 但是我当时没有成功解调nrf24l01,反而成功搞定蓝牙了. 后来我 ...

  7. Portapack应用开发教程 (十六)Debug程序 H hackrf固件比较

    portapack固件的芯片驱动看得差不多了.我们知道了这些芯片分为3类,iic通信.软件spi.硬件spi. 接下来我们打开hackrf固件,看看flash芯片是哪类.然后看看它与同类别的其它芯片有 ...

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

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

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

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

最新文章

  1. 夜间奇异规范:夜间高效自监督单目深度估计(ICCV 2021)
  2. 范凯:一个二次创业者的失败
  3. 详解DPoS共识算法
  4. 为技术匠人打call!用匠人精神,打造技术文化
  5. aspose word 获取标题_Word干货|多级标题的自动编号怎么添加?
  6. 网易云深度学习第二课Notebook3
  7. HDU 4932 Miaomiao#39;s Geometry(推理)
  8. (日常搬砖)ubuntu18.04风扇断断续续响,提示 ‘GPU fan error‘
  9. vue小练习(网页汇率器)
  10. 信息系统项目管理师-常见计算题考点汇总
  11. 计算机图形学——Liang-Barsky算法
  12. Aurora使用技巧详解
  13. 局域网中广播风暴产生原因及解决方法
  14. 阴阳是世界的生成之理,亦是其发展之理。道生一,一生二,二生三,三生万物。...
  15. 金融证券基础(三)----回撤
  16. 企业安全最佳实践:多层级对抗DDoS攻击
  17. 用python写一段计算autocad多段线长度的代码
  18. Hibernate----初识
  19. RSPO棕榈油供应链认证体系
  20. python批量检索文献pubmed_PubMed快速检索文献,学学这些技巧!

热门文章

  1. 百度浏览器奔跑的北极熊
  2. 史上最厉害的“1+2”!这个270年前出现的大难题,已经60多年没有出现好消息了..........
  3. 【错误记录】Android Studio 编译报错 ( Execution failed for task ‘:APP_MIDI:lintVitalRelease‘. )
  4. August 4th 羽毛球拍
  5. 机器学习入门15--关联规则
  6. Associative Embedding: End-to-End Learning forJoint Detection and Grouping论文笔记
  7. 疯狂讲义java 李刚_推荐:疯狂java讲义--李刚著作(4)
  8. 【算法】斐波那契数列与跳台阶
  9. gif动图怎么制作?怎么把视频做成gif?
  10. VS2008如何解决 error PRJ0003 : 生成“cmd.exe”时出错。