Skip to content

Instantly share code, notes, and snippets.

@adamgreig
Created April 16, 2015 11:09
Show Gist options
  • Select an option

  • Save adamgreig/0326b06edef87db97f84 to your computer and use it in GitHub Desktop.

Select an option

Save adamgreig/0326b06edef87db97f84 to your computer and use it in GitHub Desktop.

Revisions

  1. adamgreig created this gist Apr 16, 2015.
    275 changes: 275 additions & 0 deletions hrfpsk31.c
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,275 @@
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <math.h>
    #include "libhackrf/hackrf.h"

    #include "psk31_varicode.h"

    /*
    * Turn a null terminated ASCII string `instr` into a null terminated
    * string of '0's and '1's representing the PSK31 varicode for the input.
    * Allocates memory for the output which should be freed elsewhere.
    */
    void str_to_bits(const char* instr, char** outstr)
    {
    char* outbits = *outstr = malloc(strlen(instr)*12 + 40);
    if(outbits == NULL) {
    fprintf(stderr, "Could not allocate memory for varicode bits\n");
    exit(EXIT_FAILURE);
    }

    int i=0, j, k;

    /* Header of 0s */
    for(j=0; j<20; j++) {
    outbits[i++] = '0';
    }

    /* Encode the message, with 00 between letters */
    for(j=0; j<strlen(instr); j++) {
    const char* varicode_bits = psk31_varicode[(int)instr[j]];
    for(k=0; k<strlen(varicode_bits); k++) {
    outbits[i++] = varicode_bits[k];
    }
    outbits[i++] = '0';
    outbits[i++] = '0';
    }

    /* Tail of 0s */
    for(j=0; j<20; j++) {
    outbits[i++] = '0';
    }

    /* NULL terminate */
    outbits[i] = 0;
    }

    /*
    * Turn a null terminated string `bits` containing '0's and '1's
    * into `outlen` IQ samples for BPSK, `outbuf`.
    * Note that `outlen` is set to the number of IQ samples, i.e. half the
    * number of bytes in `outbuf`.
    * Allocates memory (possibly lots of memory) for the IQ samples, which
    * should be freed elsewhere.
    * Modulation:
    * '0': swap phase, smoothed by a cosine
    * '1': maintain phase
    * Output: I carries data, Q constantly 0
    */
    void bits_to_iq(char* bits, uint8_t** outbuf, int* outlen)
    {
    *outlen = strlen(bits) * 256000 * 2;
    *outbuf = malloc(*outlen);
    int8_t *buf = (int8_t*)(*outbuf);
    if(*outbuf == NULL) {
    fprintf(stderr, "Could not allocate memory for IQ buffer\n");
    exit(EXIT_FAILURE);
    }

    int i, j, phase = 1;
    for(i=0; i<strlen(bits); i++) {
    if(bits[i] == '1') {
    for(j=0; j<256000; j++) {
    buf[i*256000*2 + 2*j] = phase*50;
    buf[i*256000*2 + 2*j + 1] = 0;
    }
    } else {
    for(j=0; j<256000; j++) {
    buf[i*256000*2 + 2*j] = phase *
    (int8_t)(50.0f * cosf(M_PI*(float)j/256000.0f));
    buf[i*256000*2 + 2*j + 1] = 0;
    }
    phase *= -1;
    }
    }
    }

    /*
    * Store context for the TX callback.
    * A pointer to the overall buffer, the index into it and its length.
    */
    typedef struct {
    uint8_t* buffer;
    int last_idx;
    int length;
    } txbuf;

    /*
    * TX callback.
    * Writes `tx_ctx->buffer` into `transfer->buffer` incrementally.
    * Fills the final transfer with zeros after emptying buffer.
    */
    int tx_cb(hackrf_transfer* transfer)
    {
    txbuf *tb = (txbuf*)transfer->tx_ctx;
    int bl = transfer->buffer_length;
    int left = tb->length - tb->last_idx;
    if(left > bl) {
    memcpy(transfer->buffer, tb->buffer + tb->last_idx, bl);
    tb->last_idx += bl;
    return 0;
    } else {
    memcpy(transfer->buffer, tb->buffer + tb->last_idx, left);
    memset(transfer->buffer + left, '0', bl - left);
    return 1;
    }
    }

    /*
    * Start up the HackRF, set all the things we care to set.
    */
    int setup_hackrf(hackrf_device** devp)
    {
    int result;
    uint8_t board_id;
    char version[255 + 1];
    hackrf_device* dev;
    read_partid_serialno_t read_partid_serialno;

    /* Init libusb etc */
    result = hackrf_init();
    if(result != HACKRF_SUCCESS) {
    fprintf(stderr, "hackrf_init() failed: %s (%d)\n",
    hackrf_error_name(result), result);
    return EXIT_FAILURE;
    }

    /* Connect to actual HackRF */
    result = hackrf_open(&dev);
    if(result != HACKRF_SUCCESS) {
    fprintf(stderr, "hackrf_open() failed: %s (%d)\n",
    hackrf_error_name(result), result);
    return EXIT_FAILURE;
    }
    printf(" Found HackRF board.\n");

    /* Read board ID */
    result = hackrf_board_id_read(dev, &board_id);
    if(result != HACKRF_SUCCESS) {
    fprintf(stderr, "hackrf_board_id_read() failed: %s (%d)\n",
    hackrf_error_name(result), result);
    return EXIT_FAILURE;
    }
    printf(" Board ID number: %d (%s)\n", board_id,
    hackrf_board_id_name(board_id));

    /* Read board version number */
    result = hackrf_version_string_read(dev, &version[0], 255);
    if (result != HACKRF_SUCCESS) {
    fprintf(stderr, "hackrf_version_string_read() failed: %s (%d)\n",
    hackrf_error_name(result), result);
    return EXIT_FAILURE;
    }
    printf(" Firmware Version: %s\n", version);

    /* Read part and serial numbers */
    result = hackrf_board_partid_serialno_read(dev, &read_partid_serialno);
    if (result != HACKRF_SUCCESS) {
    fprintf(stderr, "hackrf_board_partid_serialno_read() fail: %s (%d)\n",
    hackrf_error_name(result), result);
    return EXIT_FAILURE;
    }
    printf(" Part ID Number: 0x%08x 0x%08x\n",
    read_partid_serialno.part_id[0],
    read_partid_serialno.part_id[1]);
    printf(" Serial Number: 0x%08x 0x%08x 0x%08x 0x%08x\n",
    read_partid_serialno.serial_no[0],
    read_partid_serialno.serial_no[1],
    read_partid_serialno.serial_no[2],
    read_partid_serialno.serial_no[3]);

    /* Set frequency to 434.650.000MHz */
    uint32_t freq = 434650000;
    result = hackrf_set_freq(dev, freq);
    if(result != HACKRF_SUCCESS) {
    fprintf(stderr, "hackrf_set_freq() failed: %s (%d)\n",
    hackrf_error_name(result), result);
    return EXIT_FAILURE;
    }
    printf(" Frequency set to %dHz\n", freq);

    /* Set sample rate to 8MS/s */
    double sample_rate = 8000000.0f;
    result = hackrf_set_sample_rate(dev, sample_rate);
    if(result != HACKRF_SUCCESS) {
    fprintf(stderr, "hackrf_set_sample_rate() failed: %s (%d)\n",
    hackrf_error_name(result), result);
    return EXIT_FAILURE;
    }
    printf(" Sample rate set to %.0eS/s\n", sample_rate);

    /* Set baseband bandwidth to something suitable (tiny) */
    uint32_t baseband_bw = hackrf_compute_baseband_filter_bw(3000);
    result = hackrf_set_baseband_filter_bandwidth(dev, baseband_bw);
    if(result != HACKRF_SUCCESS) {
    fprintf(stderr, "hackrf_set_baseband_filter_bandwidth() failed:"
    " %s (%d)\n", hackrf_error_name(result), result);
    return EXIT_FAILURE;
    }
    printf(" Baseband bandwidth filter set to %dHz\n", baseband_bw);

    /* Set TX VGA gain to something that works */
    uint32_t tx_vga = 35;
    result = hackrf_set_txvga_gain(dev, tx_vga);
    if(result != HACKRF_SUCCESS) {
    fprintf(stderr, "hackrf_set_txvga_gain() failed: %s (%d)\n",
    hackrf_error_name(result), result);
    return EXIT_FAILURE;
    }
    printf(" TX VGA gain set to %ddB\n", tx_vga);

    /* Set RF amp off */
    uint8_t tx_rf_amp = 0;
    result = hackrf_set_amp_enable(dev, tx_rf_amp);
    if(result != HACKRF_SUCCESS) {
    fprintf(stderr, "hackrf_set_amp_enable() failed: %s (%d)\n",
    hackrf_error_name(result), result);
    return EXIT_FAILURE;
    }
    printf(" TX RF amp set to %d\n", tx_rf_amp);

    *devp = dev;
    return EXIT_SUCCESS;

    }

    int main(int argc, const char* argv[])
    {
    if(argc != 2) {
    printf("Usage: %s <msg to transmit>\n", argv[0]);
    return EXIT_FAILURE;
    }

    printf("Setting up HackRF...\n");
    hackrf_device* dev = NULL;
    if(setup_hackrf(&dev)) {
    hackrf_close(dev);
    hackrf_exit();
    return EXIT_FAILURE;
    }

    printf("Generating buffers...\n");
    char* bits;
    str_to_bits(argv[1], &bits);
    int length;
    uint8_t *buf;
    bits_to_iq(bits, &buf, &length);

    txbuf tb;
    tb.buffer = buf;
    tb.last_idx = 0;
    tb.length = length;

    printf("Starting TX...\n");
    hackrf_start_tx(dev, tx_cb, (void*)&tb);

    while(hackrf_is_streaming(dev) == HACKRF_TRUE);

    printf("TX complete\n");
    hackrf_stop_tx(dev);
    hackrf_close(dev);
    hackrf_exit();
    return EXIT_SUCCESS;
    }
    135 changes: 135 additions & 0 deletions psk31_varicode.h
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,135 @@
    /*
    * PSK31 Varicode
    * http://aintel.bi.ehu.es/psk31.html
    */

    const char *psk31_varicode[] = {
    "1010101011",
    "1011011011",
    "1011101101",
    "1101110111",
    "1011101011",
    "1101011111",
    "1011101111",
    "1011111101",
    "1011111111",
    "11101111",
    "11101",
    "1101101111",
    "1011011101",
    "11111",
    "1101110101",
    "1110101011",
    "1011110111",
    "1011110101",
    "1110101101",
    "1110101111",
    "1101011011",
    "1101101011",
    "1101101101",
    "1101010111",
    "1101111011",
    "1101111101",
    "1110110111",
    "1101010101",
    "1101011101",
    "1110111011",
    "1011111011",
    "1101111111",
    "1",
    "111111111",
    "101011111",
    "111110101",
    "111011011",
    "1011010101",
    "1010111011",
    "101111111",
    "11111011",
    "11110111",
    "101101111",
    "111011111",
    "1110101",
    "110101",
    "1010111",
    "110101111",
    "10110111",
    "10111101",
    "11101101",
    "11111111",
    "101110111",
    "101011011",
    "101101011",
    "110101101",
    "110101011",
    "110110111",
    "11110101",
    "110111101",
    "111101101",
    "1010101",
    "111010111",
    "1010101111",
    "1010111101",
    "1111101",
    "11101011",
    "10101101",
    "10110101",
    "1110111",
    "11011011",
    "11111101",
    "101010101",
    "1111111",
    "111111101",
    "101111101",
    "11010111",
    "10111011",
    "11011101",
    "10101011",
    "11010101",
    "111011101",
    "10101111",
    "1101111",
    "1101101",
    "101010111",
    "110110101",
    "101011101",
    "101110101",
    "101111011",
    "1010101101",
    "111110111",
    "111101111",
    "111111011",
    "1010111111",
    "101101101",
    "1011011111",
    "1011",
    "1011111",
    "101111",
    "101101",
    "11",
    "111101",
    "1011011",
    "101011",
    "1101",
    "111101011",
    "10111111",
    "11011",
    "111011",
    "1111",
    "111",
    "111111",
    "110111111",
    "10101",
    "10111",
    "101",
    "110111",
    "1111011",
    "1101011",
    "11011111",
    "1011101",
    "111010101",
    "1010110111",
    "110111011",
    "1010110101",
    "1011010111",
    "1110110101",
    };