Created
April 16, 2015 11:09
-
-
Save adamgreig/0326b06edef87db97f84 to your computer and use it in GitHub Desktop.
PSK31 Modulator in C for HackRF
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 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