Skip to content

Instantly share code, notes, and snippets.

@adamgreig
Created April 16, 2015 11:09
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save adamgreig/0326b06edef87db97f84 to your computer and use it in GitHub Desktop.
Save adamgreig/0326b06edef87db97f84 to your computer and use it in GitHub Desktop.
PSK31 Modulator in C for HackRF
#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;
}
/*
* 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",
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment