Doorbell receiver source code

3 minute read

This wasn’t possible without rtl-sdr

SDR is a NESDR SMArTee v2 (RTL2832U, R820T2)

I wrote this to test out the library, to capture signals to a file and to see how easy it is to write code using my SDR device.

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <getopt.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <complex.h>
#include <math.h>
#include <stdbool.h>
#include <inttypes.h>
#include <sys/time.h>
#include <limits.h>

#include "rtl-sdr.h"

rtlsdr_dev_t *device;
FILE *record_file;

#define SAMPLE_RATE 250000
#define FREQUENCY 433920000
#define USE_RAW_DATA_FILE 0

typedef enum {
    short_pulse,
    short_gap,
    sync_gap,
    long_pulse,
    long_gap
} ook_modulation;

const uint32_t short_width = 360;
const uint32_t long_width = 1070;
const uint32_t gap_limit = 1200;

int get_sample_file(uint8_t *buffer, int buffer_length) {
    int len;
    len = fread(buffer, sizeof(uint8_t), buffer_length, record_file);
    return len == buffer_length;
}

int get_samples_rtl_sdr(uint8_t *buffer, int buffer_length) {
    int length;
    //length = fread(buffer, sizeof(uint8_t), buffer_length, record_file);
    rtlsdr_read_sync(device, buffer, buffer_length, &length);
    return length == buffer_length;
}

int32_t remove_dc_bias(int32_t sample, double weight, int32_t *previous_average) {
    *previous_average = (int32_t) round(weight * sample + (1 - weight) * *previous_average);
    return sample - *previous_average;
}

int32_t get_envelope(int32_t sample, double weight, int32_t previous_average) {
    return (int32_t) round(weight * sample + (1 - weight) * previous_average);
}

int32_t is_pulse(uint32_t envelope, uint32_t threshold) {
    if (envelope > threshold) {
        return true;
    }

    return false;
}

int main() {
    if (1) {
	printf("Using file.\n");
        record_file = fopen("good_sample_3", "r");

        if (!record_file) {
            return 3;
        }
    } else {
        if (!rtlsdr_get_device_count()) {
            return 1;
        }

        if (rtlsdr_open(&device, 0) != false) {
            return 2;
        }

        // Disable automatic gain control on the RTL2832U chip
	    printf("Setting AGC mode.\n");
        rtlsdr_set_agc_mode(device, 0);
        // Auto gain
	    printf("Setting gain mode.\n");
        rtlsdr_set_tuner_gain_mode(device, 0);
	
	    printf("Setting frequency and sample rate.\n");
        rtlsdr_set_center_freq(device, FREQUENCY);
        rtlsdr_set_sample_rate(device, SAMPLE_RATE);
	
	    printf("Flushing buffer.\n");
        // Flush the device buffer
        rtlsdr_reset_buffer(device);
    }

    size_t buffer_length = 2048;
    uint8_t *buffer = malloc(2048);

    // Signal is as follows:
    // |¯|________|¯¯|__|¯|_|¯|_|¯¯|_|¯¯|_|¯¯|_|¯¯|_|¯¯|__|¯|_|¯|_|¯¯|__|¯|__|¯|__|¯|_|¯¯|_|¯¯|_|¯¯|_|¯¯|
    ook_modulation protocol[] = {
            short_pulse,
            sync_gap,
            long_pulse,
            long_gap,
            short_pulse,
            long_gap,
            short_pulse,
            short_gap,
            long_pulse,
            short_gap,
            long_pulse,
            short_gap,
            long_pulse,
            short_gap,
            long_pulse,
            short_gap,
            long_pulse,
            long_gap,
            short_pulse,
            long_gap,
            short_pulse,
            short_gap,
            long_pulse,
            long_gap,
            short_pulse,
            long_gap,
            short_pulse,
            long_gap,
            short_pulse,
            short_gap,
            long_pulse,
            short_gap,
            long_pulse,
            short_gap,
            long_pulse,
            short_gap,
            long_pulse
    };

    int protocol_index = 0;
    int protocol_length = 37;

    if (get_sample_file(buffer, buffer_length) == false) {
        return 5;
    }

    int32_t dc_avg = buffer[0];
    int32_t env_avg = buffer[0];
    int start = 1;

    // Keep track of the duration of the current protocol step; pulse or the absence of a pulse
    uint32_t duration  = 0;
    // We will allow this amount of variance in the envelope signal when stepping through a pulse
    uint32_t tolerance = 0;
    // Threshold
    uint32_t threshold = 50;

    printf("Loop begin.\n");
    FILE *test = fopen("test", "w+");

    do {
        if (protocol_index + 1 == protocol_length) {
            fprintf(stdout, "OOK protocol match.\n");
            protocol_index = 0;
            tolerance = 0;
        }

        for (int i = start; i < buffer_length; i++) {
            int32_t sample = buffer[i];
            int32_t dc_sample = abs(remove_dc_bias(sample, 0.03, &dc_avg));
            env_avg = abs(get_envelope(dc_sample, 0.10, env_avg));
            uint8_t v = (uint8_t) env_avg;

            ook_modulation part = protocol[protocol_index];
    	    fwrite(&sample, sizeof(v), 1, test);

            switch (part) {
                case short_pulse: {
                    if (is_pulse(env_avg, threshold - tolerance)) {
                        tolerance = 10;
                        duration++;
                    } else {
                        if (duration > 130) {
                            protocol_index++;
                        } else {
                            protocol_index = 0;
                        }

                        tolerance = 0;
                        duration = 0;
                    }
                    break;
                }
                case short_gap: {
                    if (!is_pulse(env_avg, threshold - tolerance)) {
                        duration++;
                    } else {
                        if (duration > 130) {
                            protocol_index++;
                        } else {
                            protocol_index = 0;
                        }

                        tolerance = 0;
                        duration = 0;
                    }

                    break;
                }
                case long_pulse: {
                    if (is_pulse(env_avg, threshold - tolerance)) {
                        tolerance = 10;
                        duration++;
                    } else {
                        if (duration > 270) {
                            protocol_index++;
                        } else {
                            protocol_index = 0;
                        }

                        tolerance = 0;
                        duration = 0;
                    }

                    break;
                }
                case sync_gap: {
                    if (!is_pulse(env_avg, threshold - tolerance)) {
                        duration++;
                    } else {
                        if (duration > 2850) {
                            protocol_index++;
                        } else {
                            protocol_index = 0;
                        }

                        tolerance = 0;
                        duration = 0;
                    }

                    break;
                }
                case long_gap: {
                    if (!is_pulse(env_avg, threshold - tolerance)) {
                        duration++;
                    } else {
                        if (duration > 270) {
                            protocol_index++;
                        } else {
                            protocol_index = 0;
                        }

                        tolerance = 0;
                        duration = 0;
                    }

                    break;
                }
                default: {
                    printf("Finished reading file!\n");
                    exit(1);
                }
            }
        }

        start = 0;
    } while (get_samples_rtl_sdr(buffer, buffer_length) == true);

    return 0;
}

Updated: