318 lines
8.7 KiB
C
318 lines
8.7 KiB
C
#define _CRT_SECURE_NO_WARNINGS
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <libbladeRF.h>
|
|
#ifdef _WIN32
|
|
#include "getopt.h"
|
|
#else
|
|
#include <getopt.h>
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
#endif
|
|
|
|
#define TX_FREQUENCY 1575420000
|
|
#define TX_SAMPLERATE 2600000
|
|
#define TX_BANDWIDTH 2500000
|
|
#define TX_VGA1 -25
|
|
#define TX_VGA2 0
|
|
|
|
#define NUM_BUFFERS 32
|
|
#define SAMPLES_PER_BUFFER (32 * 1024)
|
|
#define NUM_TRANSFERS 16
|
|
#define TIMEOUT_MS 1000
|
|
|
|
#define AMPLITUDE (1000) // Default amplitude for 12-bit I/Q
|
|
|
|
void usage(void)
|
|
{
|
|
fprintf(stderr, "Usage: bladeplayer [options]\n"
|
|
" -f <tx_file> I/Q sampling data file (required)\n"
|
|
" -b <iq_bits> I/Q data format [1/16] (default: 16)\n"
|
|
" -g <tx_vga1> TX VGA1 gain (default: %d)\n",
|
|
TX_VGA1);
|
|
|
|
return;
|
|
}
|
|
int main(int argc, char *argv[])
|
|
{
|
|
int status;
|
|
char *devstr = NULL;
|
|
struct bladerf *dev = NULL;
|
|
|
|
FILE *fp;
|
|
int16_t *tx_buffer;
|
|
enum state {INIT, READ_FILE, PAD_TRAILING, DONE};
|
|
enum state state = INIT;
|
|
|
|
int compressed = 0;
|
|
uint8_t *read_buffer;
|
|
size_t samples_read;
|
|
int16_t lut[256][8];
|
|
int16_t amp = AMPLITUDE;
|
|
uint32_t i,k;
|
|
|
|
int gain = TX_VGA1;
|
|
int result;
|
|
int data_format;
|
|
char txfile[128];
|
|
|
|
// Empty TX file name
|
|
txfile[0] = 0;
|
|
|
|
if (argc<3) {
|
|
usage();
|
|
exit(1);
|
|
}
|
|
|
|
while ((result=getopt(argc,argv,"g:b:f:"))!=-1)
|
|
{
|
|
switch (result)
|
|
{
|
|
case 'g':
|
|
gain = atoi(optarg);
|
|
if (gain>-4 || gain<-35)
|
|
{
|
|
printf("ERROR: Invalid TX VGA1 gain.\n");
|
|
exit(1);
|
|
}
|
|
break;
|
|
case 'b':
|
|
data_format = atoi(optarg);
|
|
if (data_format!=1 && data_format!=16)
|
|
{
|
|
printf("ERROR: Invalid I/Q data format.\n");
|
|
exit(1);
|
|
}
|
|
else if (data_format==1)
|
|
compressed = 1;
|
|
break;
|
|
case 'f':
|
|
strcpy(txfile, optarg);
|
|
break;
|
|
case ':':
|
|
case '?':
|
|
usage();
|
|
exit(1);
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Open TX file.
|
|
if (txfile[0]==0)
|
|
{
|
|
printf("ERROR: I/Q sampling data file is not specified.\n");
|
|
exit(1);
|
|
}
|
|
|
|
fp = fopen(txfile, "rb");
|
|
|
|
if (fp==NULL) {
|
|
fprintf(stderr, "ERROR: Failed to open TX file: %s\n", argv[1]);
|
|
exit(1);
|
|
}
|
|
|
|
// Initializing device.
|
|
printf("Opening and initializing device...\n");
|
|
|
|
status = bladerf_open(&dev, devstr);
|
|
if (status != 0) {
|
|
fprintf(stderr, "Failed to open device: %s\n", bladerf_strerror(status));
|
|
goto out;
|
|
}
|
|
|
|
status = bladerf_set_frequency(dev, BLADERF_MODULE_TX, TX_FREQUENCY);
|
|
if (status != 0) {
|
|
fprintf(stderr, "Faield to set TX frequency: %s\n", bladerf_strerror(status));
|
|
goto out;
|
|
}
|
|
else {
|
|
printf("TX frequency: %u Hz\n", TX_FREQUENCY);
|
|
}
|
|
|
|
status = bladerf_set_sample_rate(dev, BLADERF_MODULE_TX, TX_SAMPLERATE, NULL);
|
|
if (status != 0) {
|
|
fprintf(stderr, "Failed to set TX sample rate: %s\n", bladerf_strerror(status));
|
|
goto out;
|
|
}
|
|
else {
|
|
printf("TX sample rate: %u sps\n", TX_SAMPLERATE);
|
|
}
|
|
|
|
status = bladerf_set_bandwidth(dev, BLADERF_MODULE_TX, TX_BANDWIDTH, NULL);
|
|
if (status != 0) {
|
|
fprintf(stderr, "Failed to set TX bandwidth: %s\n", bladerf_strerror(status));
|
|
goto out;
|
|
}
|
|
else {
|
|
printf("TX bandwidth: %u Hz\n", TX_BANDWIDTH);
|
|
}
|
|
|
|
status = bladerf_set_txvga1(dev, gain);
|
|
if (status != 0) {
|
|
fprintf(stderr, "Failed to set TX VGA1 gain: %s\n", bladerf_strerror(status));
|
|
goto out;
|
|
}
|
|
else {
|
|
printf("TX VGA1 gain: %d dB\n", gain);
|
|
}
|
|
|
|
status = bladerf_set_txvga2(dev, TX_VGA2);
|
|
if (status != 0) {
|
|
fprintf(stderr, "Failed to set TX VGA2 gain: %s\n", bladerf_strerror(status));
|
|
goto out;
|
|
}
|
|
else {
|
|
printf("TX VGA2 gain: %d dB\n", TX_VGA2);
|
|
}
|
|
|
|
// Application code goes here.
|
|
printf("Running...\n");
|
|
|
|
// Allocate a buffer to hold each block of samples to transmit.
|
|
tx_buffer = (int16_t*)malloc(SAMPLES_PER_BUFFER * 2 * sizeof(int16_t));
|
|
|
|
if (tx_buffer == NULL) {
|
|
fprintf(stderr, "Failed to allocate TX buffer.\n");
|
|
goto out;
|
|
}
|
|
|
|
// if compressed
|
|
read_buffer = (uint8_t*)malloc(SAMPLES_PER_BUFFER / 4);
|
|
|
|
if (read_buffer == NULL) {
|
|
fprintf(stderr, "Failed to allocate read buffer.\n");
|
|
goto out;
|
|
}
|
|
|
|
for (i=0; i<256; i++)
|
|
{
|
|
for (k=0; k<8; k++)
|
|
lut[i][k] = ((i>>(7-k))&0x1)?amp:-amp;
|
|
}
|
|
|
|
// Configure the TX module for use with the synchronous interface.
|
|
status = bladerf_sync_config(dev,
|
|
BLADERF_MODULE_TX,
|
|
BLADERF_FORMAT_SC16_Q11,
|
|
NUM_BUFFERS,
|
|
SAMPLES_PER_BUFFER,
|
|
NUM_TRANSFERS,
|
|
TIMEOUT_MS);
|
|
|
|
if (status != 0) {
|
|
fprintf(stderr, "Failed to configure TX sync interface: %s\n", bladerf_strerror(status));
|
|
goto out;
|
|
}
|
|
|
|
// We must always enable the modules *after* calling bladerf_sync_config().
|
|
status = bladerf_enable_module(dev, BLADERF_MODULE_TX, true);
|
|
if (status != 0) {
|
|
fprintf(stderr, "Failed to enable TX module: %s\n", bladerf_strerror(status));
|
|
goto out;
|
|
}
|
|
|
|
// Keep writing samples while there is more data to send and no failures have occurred.
|
|
while (state != DONE && status == 0) {
|
|
|
|
int16_t *tx_buffer_current = tx_buffer;
|
|
unsigned int buffer_samples_remaining = SAMPLES_PER_BUFFER;
|
|
|
|
// if compressed
|
|
unsigned int read_samples_remaining = SAMPLES_PER_BUFFER / 4;
|
|
|
|
// Keep adding to the buffer until it is full or a failure occurs
|
|
while (buffer_samples_remaining > 0 && status == 0 && state != DONE) {
|
|
size_t samples_populated = 0;
|
|
|
|
switch(state) {
|
|
case INIT:
|
|
case READ_FILE:
|
|
// Read from the input file
|
|
if (compressed)
|
|
{
|
|
int16_t *write_buffer_current = tx_buffer;
|
|
|
|
samples_read = fread(read_buffer,
|
|
sizeof(uint8_t),
|
|
read_samples_remaining,
|
|
fp);
|
|
|
|
samples_populated = samples_read * 4;
|
|
buffer_samples_remaining = read_samples_remaining * 4;
|
|
|
|
// Expand compressed data into TX buffer
|
|
for (i=0; i<samples_read; i++)
|
|
{
|
|
memcpy(write_buffer_current, lut[read_buffer[i]], 8);
|
|
|
|
// Advance the write buffer pointer
|
|
write_buffer_current += 8;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
samples_populated = fread(tx_buffer_current,
|
|
2 * sizeof(int16_t),
|
|
buffer_samples_remaining,
|
|
fp);
|
|
}
|
|
|
|
// If the end of the file was reached, pad the rest of the buffer and finish.
|
|
if (feof(fp)) {
|
|
state = PAD_TRAILING;
|
|
}
|
|
// Check for errors
|
|
else if (ferror(fp)) {
|
|
status = errno;
|
|
}
|
|
|
|
break;
|
|
|
|
case PAD_TRAILING:
|
|
// Populate the remainder of the buffer with zeros.
|
|
memset(tx_buffer_current, 0, buffer_samples_remaining * 2 * sizeof(uint16_t));
|
|
|
|
state = DONE;
|
|
break;
|
|
|
|
case DONE:
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// Advance the buffer pointer.
|
|
buffer_samples_remaining -= (unsigned int)samples_populated;
|
|
tx_buffer_current += (2 * samples_populated);
|
|
}
|
|
|
|
// If there were no errors, transmit the data buffer.
|
|
if (status == 0) {
|
|
bladerf_sync_tx(dev, tx_buffer, SAMPLES_PER_BUFFER, NULL, TIMEOUT_MS);
|
|
}
|
|
}
|
|
|
|
// Disable TX module, shutting down our underlying TX stream.
|
|
status = bladerf_enable_module(dev, BLADERF_MODULE_TX, false);
|
|
if (status != 0) {
|
|
fprintf(stderr, "Failed to disable TX module: %s\n", bladerf_strerror(status));
|
|
}
|
|
|
|
// Free up our resources
|
|
free(tx_buffer);
|
|
|
|
// if compressed
|
|
free(read_buffer);
|
|
|
|
// Close TX file
|
|
fclose(fp);
|
|
|
|
out:
|
|
printf("Closing device...\n");
|
|
bladerf_close(dev);
|
|
|
|
return(0);
|
|
}
|