Portapack application development tutorial (6) low energy Bluetooth demodulation

Some time ago, I wanted to demodulate apc220 so that I can use portapack to receive digital data, but this module has too little information.

Later, I thought that in fact, nrf24l01 or Bluetooth low energy is often used for data transmission in the four-axis, and these two modules are also fsk. And better yet, someone has already written the demodulation of nrf and btle, but this person uses rtlsdr to receive and then stdout output to his decoding program.

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

I knew about this package before, but it has been useless, because rtlsdr does not support 2.4g and needs a down converter to change it to the supported range. Later, I found an article on the official website of crazyflie (a well-known foreign open source small four-axis) explaining how to use gnuradio to decode hackrf packets to the decoder.

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

I imitated the operation of the article and found that it really works (but there is a cache delay write problem).

Just recently I wrote a pure c program for hackrf demodulation fm (no need for gnuradio) imitating rtl_fm. In this way, I can directly use a C++ program to directly demodulate btle or nrf24l01 with hackrf.

Not only that, but I also wrote all the Bluetooth demodulations together (all the function calls are removed), so it will be much more convenient to add it to the portapack later.

Not much nonsense, the code:

#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;
                        else
                            current_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;
                        else
                            current_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 packet
                    uint8_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;
                        else
                            current_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 packet
                    crc[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;
                } 
                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; // chan 38
    //freq = 2402e6; // chan 37
    //freq = 2480e6;   // chan 39
    rate_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;
}

Friends who are interested can try it. The following is how to compile and use.

g++ hackrf_ble_combined.cpp -o hackrf_ble -lhackrf -pthread

./hackrf_ble

Then I started moving to the portapack.

I refer to the two programs afsk_rx and wfm.

The code can be viewed in my github portapack repo, I will not post it.

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

But I still encounter problems. One is that my sampling rate must be 4MHz. In fact, 1MHz is enough. But now there is a format type problem. I must use the downsampling function in the portapack library to convert the format type and downsampling. The minimum function is 4 times. I need a 1MHz output, so I must have a 4MHz input. In this way, noise from other frequencies will come in.

There is another problem. I skipped the crc check. Sometimes the mac address has a few digits wrong. If I use crc to check, the result will be much less, but the first three digits of the mac address of the final result are correct, and the last three digits are also wrong, and it is very different from the real value. So there is room for optimization.

The following two pictures are the comparison between the Mac and the computer running hcitool lescan using the Bluetooth chip search results received by portapack

I changed the interface in the picture below to make it easier to observe, but you can see that there are some errors in the bottom two lines of the mac address

 

 

Guess you like

Origin blog.csdn.net/shukebeta008/article/details/105024278