diff --git a/.gitignore b/.gitignore index 575a5fc..a069fdf 100644 --- a/.gitignore +++ b/.gitignore @@ -38,3 +38,6 @@ gps-sdr-sim-lut # Temporary files *.swp + +# Netbeans project folder +nbproject/* \ No newline at end of file diff --git a/README.md b/README.md index d84b18d..acb4921 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ GPS-SDR-SIM generates GPS baseband signal data streams, which can be converted to RF using software-defined radio (SDR) platforms, such as -[bladeRF](http://nuand.com/), [HackRF](https://github.com/mossmann/hackrf/wiki), and [USRP](http://www.ettus.com/). +[ADALM-Pluto](https://wiki.analog.com/university/tools/pluto), [bladeRF](http://nuand.com/), [HackRF](https://github.com/mossmann/hackrf/wiki), and [USRP](http://www.ettus.com/). ### Windows build instructions @@ -35,11 +35,11 @@ These files are then used to generate the simulated pseudorange and Doppler for the GPS satellites in view. This simulated range data is then used to generate the digitized I/Q samples for the GPS signal. -The bladeRF command line interface requires I/Q pairs stored as signed +The bladeRF and ADALM-Pluto command line interface requires I/Q pairs stored as signed 16-bit integers, while the hackrf_transfer and gps-sdr-sim-uhd.py support signed bytes. -HackRF and bladeRF require 2.6 MHz sample rate, while the USRP2 requires +HackRF, bladeRF and ADALM-Pluto require 2.6 MHz sample rate, while the USRP2 requires 2.5 MHz (an even integral decimator of 100 MHz). The simulation start time can be specified if the corresponding set of ephemerides @@ -91,6 +91,8 @@ The user motion can be specified in either dynamic or static mode: The TX port of a particular SDR platform is connected to the GPS receiver under test through a DC block and a fixed 50-60dB attenuator. +#### BladeRF + The simulated GPS signal file, named "gpssim.bin", can be loaded into the bladeRF for playback as shown below: @@ -106,29 +108,49 @@ tx start ``` You can also execute these commands via the `bladeRF-cli` script option as below: - ``` > bladeRF-cli -s bladerf.script ``` -For the HackRF: +#### HackRF: ``` > hackrf_transfer -t gpssim.bin -f 1575420000 -s 2600000 -a 1 -x 0 ``` -For UHD supported devices (tested with USRP2 only): +#### UHD supported devices (tested with USRP2 only): ``` > gps-sdr-sim-uhd.py -t gpssim.bin -s 2500000 -x 0 ``` -For LimeSDR (in case of 1 Msps 1-bit file, to get full BaseBand dynamic and low RF power): +#### LimeSDR (in case of 1 Msps 1-bit file, to get full BaseBand dynamic and low RF power): ``` > limeplayer -s 1000000 -b 1 -d 2047 -g 0.1 < ../circle.1b.1M.bin ``` +#### ADALM-Pluto (PlutoSDR): + +The ADALM-Pluto device is expected to have its network interface up and running and is accessible +via "pluto.local" by default. + +Default settings: +``` +> plutoplayer -t gpssim.bin +``` +Set TX attenuation: +``` +> plutoplayer -t gpssim.bin -a -30.0 +``` +Default -20.0dB. Applicable range 0.0dB to -80.0dB in 0.25dB steps. + +Set RF bandwidth: +``` +> plutoplayer -t gpssim.bin -b 3.0 +``` +Default 3.0MHz. Applicable range 1.0MHz to 5.0MHz. + ### License Copyright © 2015 Takuji Ebinuma diff --git a/player/Makefile b/player/Makefile index ab1ba11..e630021 100644 --- a/player/Makefile +++ b/player/Makefile @@ -1,5 +1,28 @@ -CC=gcc -O2 -Wall +DIALECT = -std=c11 +CFLAGS += $(DIALECT) -O3 -g -W -Wall +LIBS = -lm -limeplayer: limeplayer.c - $(CC) -o limeplayer limeplayer.c -lLimeSuite +CFLAGS += $(shell pkg-config --cflags libbladeRF) +CFLAGS += $(shell pkg-config --cflags libhackrf) +CFLAGS += $(shell pkg-config --cflags libiio libad9361) +.PHONY: all bladeplayer hackplayer limeplayer plutoplayer clean +all: bladeplayer hackplayer limeplayer plutoplayer + +%.o: %.c *.h + $(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@ + +bladeplayer: bladeplayer.o $(SDR_OBJ) $(COMPAT) + $(CC) -g -o $@ $^ $(LDFLAGS) $(LIBS) $(shell pkg-config --libs libbladeRF) + +hackplayer: hackplayer.o $(COMPAT) + $(CC) -g -o $@ $^ $(LDFLAGS) $(LIBS) $(shell pkg-config --libs libhackrf) + +limeplayer: limeplayer.o $(COMPAT) + $(CC) -g -o $@ $^ $(LDFLAGS) $(LIBS) -lLimeSuite + +plutoplayer: plutoplayer.o $(COMPAT) + $(CC) -g -o $@ $^ $(LDFLAGS) $(LIBS) $(shell pkg-config --libs libiio libad9361) + +clean: + rm -f *.o bladeplayer hackplayer limeplayer plutoplayer diff --git a/player/README.md b/player/README.md new file mode 100644 index 0000000..39dd7fd --- /dev/null +++ b/player/README.md @@ -0,0 +1,82 @@ +## Building + +Modified to build on Linux. + +``` +$ make all +``` +Will build all players if dependencies are met. + +### Dependencies - bladeRF +#### libbladeRF +``` +$ git clone https://github.com/Nuand/bladeRF.git +$ cd bladeRF +$ dpkg-buildpackage -b +``` +Or Nuand has some build/install instructions including an Ubuntu PPA +at https://github.com/Nuand/bladeRF/wiki/Getting-Started:-Linux + +#### Build + +``` +$ make bladeplayer +``` + +### Dependecies - hackRF +#### libhackrf + +``` +> git clone https://github.com/mossmann/hackrf.git +> mkdir hackrf/host/build +> cd hackrf/host/build +> cmake .. +> make +> sudo make install +> sudo ldconfig +``` +Build instructions https://github.com/mossmann/hackrf/tree/master/host + +#### Build + +``` +> make hackplayer +``` + +### Dependecies - lime + +LimeSuite https://github.com/myriadrf/LimeSuite + +Build instructions http://wiki.myriadrf.org/Lime_Suite + +:exclamation: Build not tested. + +### Dependecies - ADALM-Pluto +#### libiio + +Use the latest version from Github. +``` +$ git clone https://github.com/analogdevicesinc/libiio.git +$ cd libiio +$ cmake ./ +$ make all +$ sudo make install +``` +[How to build it in detail.](https://wiki.analog.com/resources/tools-software/linux-software/libiio) + +#### libad9361 + +Use of the latest Github version mandatory. +``` +$ git clone https://github.com/analogdevicesinc/libad9361-iio.git +$ cd libad9361-iio +$ cmake ./ +$ make all +$ sudo make install +``` + +#### Build + +``` +$ make plutoplayer +``` \ No newline at end of file diff --git a/player/bladeplayer.c b/player/bladeplayer.c index a04bf09..afe57a7 100644 --- a/player/bladeplayer.c +++ b/player/bladeplayer.c @@ -4,11 +4,8 @@ #include #include #include -#ifdef _WIN32 #include "getopt.h" -#else -#include -#endif +#include #define TX_FREQUENCY 1575420000 #define TX_SAMPLERATE 2600000 @@ -23,293 +20,279 @@ #define AMPLITUDE (1000) // Default amplitude for 12-bit I/Q -void usage(void) -{ - fprintf(stderr, "Usage: bladeplayer [options]\n" - " -f I/Q sampling data file (required)\n" - " -b I/Q data format [1/16] (default: 16)\n" - " -g TX VGA1 gain (default: %d)\n", - TX_VGA1); +void usage(void) { + fprintf(stderr, "Usage: bladeplayer [options]\n" + " -f I/Q sampling data file (required)\n" + " -b I/Q data format [1/16] (default: 16)\n" + " -g TX VGA1 gain (default: %d)\n", + TX_VGA1); - return; + 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 main(int argc, char *argv[]) { + int status; + char *devstr = NULL; + struct bladerf *dev = NULL; - int compressed = 0; - uint8_t *read_buffer; - size_t samples_read; - int16_t lut[256][8]; - int16_t amp = AMPLITUDE; - int i,k; + FILE *fp; + int16_t *tx_buffer; - int gain = TX_VGA1; - int result; - int data_format; - char txfile[128]; + enum state { + INIT, READ_FILE, PAD_TRAILING, DONE + }; + enum state state = INIT; - // Empty TX file name - txfile[0] = 0; + int compressed = 0; + uint8_t *read_buffer; + size_t samples_read; + int16_t lut[256][8]; + int16_t amp = AMPLITUDE; + uint32_t i, k; - if (argc<3) { - usage(); - exit(1); - } + int gain = TX_VGA1; + int result; + int data_format; + char txfile[128]; - 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; - } - } + // Empty TX file name + txfile[0] = 0; - // Open TX file. - if (txfile[0]==0) - { - printf("ERROR: I/Q sampling data file is not specified.\n"); - exit(1); - } + if (argc < 3) { + usage(); + exit(1); + } - fp = fopen(txfile, "rb"); + 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; + } + } - if (fp==NULL) { - fprintf(stderr, "ERROR: Failed to open TX file: %s\n", argv[1]); - exit(1); - } + // Open TX file. + if (txfile[0] == 0) { + printf("ERROR: I/Q sampling data file is not specified.\n"); + exit(1); + } - // Initializing device. - printf("Opening and initializing device...\n"); + fp = fopen(txfile, "rb"); - status = bladerf_open(&dev, devstr); - if (status != 0) { - fprintf(stderr, "Failed to open device: %s\n", bladerf_strerror(status)); - goto out; - } + if (fp == NULL) { + fprintf(stderr, "ERROR: Failed to open TX file: %s\n", argv[1]); + exit(1); + } - 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); - } + // Initializing device. + printf("Opening and initializing device...\n"); - 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_open(&dev, devstr); + if (status != 0) { + fprintf(stderr, "Failed to open device: %s\n", bladerf_strerror(status)); + goto out; + } - 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_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_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_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_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); - } + 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); + } - // Application code goes here. - printf("Running...\n"); + 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); + } - // 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; - } + 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); + } - // if compressed - read_buffer = (uint8_t*)malloc(SAMPLES_PER_BUFFER / 4); + // Application code goes here. + printf("Running...\n"); - if (read_buffer == NULL) { - fprintf(stderr, "Failed to allocate read buffer.\n"); - goto out; - } + // Allocate a buffer to hold each block of samples to transmit. + tx_buffer = (int16_t*) malloc(SAMPLES_PER_BUFFER * 2 * sizeof (int16_t)); - for (i=0; i<256; i++) - { - for (k=0; k<8; k++) - lut[i][k] = ((i>>(7-k))&0x1)?amp:-amp; - } + if (tx_buffer == NULL) { + fprintf(stderr, "Failed to allocate TX buffer.\n"); + goto out; + } - // 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 compressed + read_buffer = (uint8_t*) malloc(SAMPLES_PER_BUFFER / 4); - if (status != 0) { - fprintf(stderr, "Failed to configure TX sync interface: %s\n", bladerf_strerror(status)); - goto out; - } + if (read_buffer == NULL) { + fprintf(stderr, "Failed to allocate read buffer.\n"); + 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; - } + for (i = 0; i < 256; i++) { + for (k = 0; k < 8; k++) + lut[i][k] = ((i >> (7 - k))&0x1) ? amp : -amp; + } - // Keep writing samples while there is more data to send and no failures have occurred. - while (state != DONE && status == 0) { + // 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); - int16_t *tx_buffer_current = tx_buffer; - unsigned int buffer_samples_remaining = SAMPLES_PER_BUFFER; + if (status != 0) { + fprintf(stderr, "Failed to configure TX sync interface: %s\n", bladerf_strerror(status)); + goto out; + } - // if compressed - unsigned int read_samples_remaining = SAMPLES_PER_BUFFER / 4; + // 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 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; + // Keep writing samples while there is more data to send and no failures have occurred. + while (state != DONE && status == 0) { - switch(state) { - case INIT: - case READ_FILE: - // Read from the input file - if (compressed) - { - int16_t *write_buffer_current = tx_buffer; + int16_t *tx_buffer_current = tx_buffer; + unsigned int buffer_samples_remaining = SAMPLES_PER_BUFFER; - samples_read = fread(read_buffer, - sizeof(uint8_t), - read_samples_remaining, - fp); + // if compressed + unsigned int read_samples_remaining = SAMPLES_PER_BUFFER / 4; - samples_populated = samples_read * 4; - buffer_samples_remaining = read_samples_remaining * 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; - // Expand compressed data into TX buffer - for (i=0; i - #include #include +#include +#include #include - -#include - -#ifdef _WIN64 -typedef int64_t ssize_t; -#else -typedef int32_t ssize_t; -#endif - -typedef int bool; -#define true 1 -#define false 0 +#include +#include static hackrf_device* device = NULL; @@ -25,199 +13,188 @@ volatile uint32_t byte_count = 0; volatile bool do_exit = false; -static transceiver_mode_t transceiver_mode = TRANSCEIVER_MODE_TX; - #define FD_BUFFER_SIZE (8*1024) #define FREQ_ONE_MHZ (1000000ull) -BOOL WINAPI sighandler(int signum) -{ - if (CTRL_C_EVENT == signum) { - fprintf(stdout, "Caught signal %d\n", signum); - do_exit = true; - return TRUE; - } - return FALSE; +static void sighandler(int signum) { + fprintf(stdout, "Caught signal %d\n", signum); + do_exit = true; } int tx_callback(hackrf_transfer* transfer) { - size_t bytes_to_read; + size_t bytes_to_read; - if( fd != NULL ) - { - ssize_t bytes_read; - byte_count += transfer->valid_length; - bytes_to_read = transfer->valid_length; - - bytes_read = fread(transfer->buffer, 1, bytes_to_read, fd); - - if (bytes_read != bytes_to_read) { - return -1; // EOF - } else { - return 0; - } - } else { - return -1; - } + if (fd != NULL) { + size_t bytes_read; + byte_count += transfer->valid_length; + bytes_to_read = transfer->valid_length; + + bytes_read = fread(transfer->buffer, 1, bytes_to_read, fd); + + if (bytes_read != bytes_to_read) { + return -1; // EOF + } else { + return 0; + } + } else { + return -1; + } } static void usage() { - fprintf(stderr, "Usage: hackplayer [options]\n" - " -t Transmit data from file (required)\n"); + fprintf(stderr, "Usage: hackplayer [options]\n" + " -t Transmit data from file (required)\n"); - return; + return; } int main(int argc, char** argv) { - int opt; - int result; - const char* path = NULL; - uint32_t sample_rate_hz = 2600000; - uint32_t baseband_filter_bw_hz = 0; - unsigned int txvga_gain=0; - uint64_t freq_hz = 1575420000; - uint32_t amp_enable = 1; + int opt; + int result; + const char* path = NULL; + uint32_t sample_rate_hz = 2600000; + uint32_t baseband_filter_bw_hz = 0; + unsigned int txvga_gain = 0; + uint64_t freq_hz = 1575420000; + uint32_t amp_enable = 1; - while( (opt = getopt(argc, argv, "t:")) != EOF ) - { - result = HACKRF_SUCCESS; - switch( opt ) - { - case 't': - path = optarg; - break; - default: - printf("unknown argument '-%c %s'\n", opt, optarg); - usage(); - return EXIT_FAILURE; - } + while ((opt = getopt(argc, argv, "t:")) != EOF) { + result = HACKRF_SUCCESS; + switch (opt) { + case 't': + path = optarg; + break; + default: + printf("unknown argument '-%c %s'\n", opt, optarg); + usage(); + return EXIT_FAILURE; + } - if( result != HACKRF_SUCCESS ) { - printf("argument error: '-%c %s' %s (%d)\n", opt, optarg, hackrf_error_name(result), result); - usage(); - return EXIT_FAILURE; - } - } + if (result != HACKRF_SUCCESS) { + printf("argument error: '-%c %s' %s (%d)\n", opt, optarg, hackrf_error_name(result), result); + usage(); + return EXIT_FAILURE; + } + } - if( path == NULL ) { - printf("specify a path to a file to transmit\n"); - usage(); - return EXIT_FAILURE; - } + if (path == NULL) { + printf("specify a path to a file to transmit\n"); + usage(); + return EXIT_FAILURE; + } - // Compute default value depending on sample rate - baseband_filter_bw_hz = hackrf_compute_baseband_filter_bw_round_down_lt(sample_rate_hz); + // Compute default value depending on sample rate + baseband_filter_bw_hz = hackrf_compute_baseband_filter_bw_round_down_lt(sample_rate_hz); - result = hackrf_init(); - if( result != HACKRF_SUCCESS ) { - printf("hackrf_init() failed: %s (%d)\n", hackrf_error_name(result), result); - usage(); - return EXIT_FAILURE; - } + result = hackrf_init(); + if (result != HACKRF_SUCCESS) { + printf("hackrf_init() failed: %s (%d)\n", hackrf_error_name(result), result); + usage(); + return EXIT_FAILURE; + } - result = hackrf_open_by_serial(NULL, &device); - if( result != HACKRF_SUCCESS ) { - printf("hackrf_open() failed: %s (%d)\n", hackrf_error_name(result), result); - usage(); - return EXIT_FAILURE; - } + result = hackrf_open_by_serial(NULL, &device); + if (result != HACKRF_SUCCESS) { + printf("hackrf_open() failed: %s (%d)\n", hackrf_error_name(result), result); + usage(); + return EXIT_FAILURE; + } - fd = fopen(path, "rb"); - if( fd == NULL ) { - printf("Failed to open file: %s\n", path); - return EXIT_FAILURE; - } + fd = fopen(path, "rb"); + if (fd == NULL) { + printf("Failed to open file: %s\n", path); + return EXIT_FAILURE; + } - // Change fd buffer to have bigger one to store or read data on/to HDD - result = setvbuf(fd , NULL , _IOFBF , FD_BUFFER_SIZE); - if( result != 0 ) { - printf("setvbuf() failed: %d\n", result); - usage(); - return EXIT_FAILURE; - } + // Change fd buffer to have bigger one to store or read data on/to HDD + result = setvbuf(fd, NULL, _IOFBF, FD_BUFFER_SIZE); + if (result != 0) { + printf("setvbuf() failed: %d\n", result); + usage(); + return EXIT_FAILURE; + } - SetConsoleCtrlHandler( (PHANDLER_ROUTINE) sighandler, TRUE ); + signal(SIGINT, sighandler); - printf("call hackrf_sample_rate_set(%.03f MHz)\n", ((float)sample_rate_hz/(float)FREQ_ONE_MHZ)); - result = hackrf_set_sample_rate_manual(device, sample_rate_hz, 1); - if( result != HACKRF_SUCCESS ) { - printf("hackrf_sample_rate_set() failed: %s (%d)\n", hackrf_error_name(result), result); - usage(); - return EXIT_FAILURE; - } + printf("call hackrf_sample_rate_set(%.03f MHz)\n", ((float) sample_rate_hz / (float) FREQ_ONE_MHZ)); + result = hackrf_set_sample_rate_manual(device, sample_rate_hz, 1); + if (result != HACKRF_SUCCESS) { + printf("hackrf_sample_rate_set() failed: %s (%d)\n", hackrf_error_name(result), result); + usage(); + return EXIT_FAILURE; + } - printf("call hackrf_baseband_filter_bandwidth_set(%.03f MHz)\n", - ((float)baseband_filter_bw_hz/(float)FREQ_ONE_MHZ)); - result = hackrf_set_baseband_filter_bandwidth(device, baseband_filter_bw_hz); - if( result != HACKRF_SUCCESS ) { - printf("hackrf_baseband_filter_bandwidth_set() failed: %s (%d)\n", hackrf_error_name(result), result); - usage(); - return EXIT_FAILURE; - } + printf("call hackrf_baseband_filter_bandwidth_set(%.03f MHz)\n", + ((float) baseband_filter_bw_hz / (float) FREQ_ONE_MHZ)); + result = hackrf_set_baseband_filter_bandwidth(device, baseband_filter_bw_hz); + if (result != HACKRF_SUCCESS) { + printf("hackrf_baseband_filter_bandwidth_set() failed: %s (%d)\n", hackrf_error_name(result), result); + usage(); + return EXIT_FAILURE; + } - result = hackrf_set_txvga_gain(device, txvga_gain); - result |= hackrf_start_tx(device, tx_callback, NULL); + result = hackrf_set_txvga_gain(device, txvga_gain); + result |= hackrf_start_tx(device, tx_callback, NULL); - if( result != HACKRF_SUCCESS ) { - printf("hackrf_start_?x() failed: %s (%d)\n", hackrf_error_name(result), result); - usage(); - return EXIT_FAILURE; - } + if (result != HACKRF_SUCCESS) { + printf("hackrf_start_?x() failed: %s (%d)\n", hackrf_error_name(result), result); + usage(); + return EXIT_FAILURE; + } - printf("call hackrf_set_freq(%.03f MHz)\n", ((double)freq_hz/(double)FREQ_ONE_MHZ)); - result = hackrf_set_freq(device, freq_hz); - if( result != HACKRF_SUCCESS ) { - printf("hackrf_set_freq() failed: %s (%d)\n", hackrf_error_name(result), result); - usage(); - return EXIT_FAILURE; - } + printf("call hackrf_set_freq(%.03f MHz)\n", ((double) freq_hz / (double) FREQ_ONE_MHZ)); + result = hackrf_set_freq(device, freq_hz); + if (result != HACKRF_SUCCESS) { + printf("hackrf_set_freq() failed: %s (%d)\n", hackrf_error_name(result), result); + usage(); + return EXIT_FAILURE; + } - printf("call hackrf_set_amp_enable(%u)\n", amp_enable); - result = hackrf_set_amp_enable(device, (uint8_t)amp_enable); - if( result != HACKRF_SUCCESS ) { - printf("hackrf_set_amp_enable() failed: %s (%d)\n", hackrf_error_name(result), result); - usage(); - return EXIT_FAILURE; - } + printf("call hackrf_set_amp_enable(%u)\n", amp_enable); + result = hackrf_set_amp_enable(device, (uint8_t) amp_enable); + if (result != HACKRF_SUCCESS) { + printf("hackrf_set_amp_enable() failed: %s (%d)\n", hackrf_error_name(result), result); + usage(); + return EXIT_FAILURE; + } - printf("Stop with Ctrl-C\n"); - while( (hackrf_is_streaming(device) == HACKRF_TRUE) && (do_exit == false) ) { - // Show something? - } + printf("Stop with Ctrl-C\n"); + while ((hackrf_is_streaming(device) == HACKRF_TRUE) && (do_exit == false)) { + // Show something? + } - result = hackrf_is_streaming(device); - if (do_exit) { - printf("\nUser cancel, exiting...\n"); - } else { - printf("\nExiting... hackrf_is_streaming() result: %s (%d)\n", hackrf_error_name(result), result); - } + result = hackrf_is_streaming(device); + if (do_exit) { + printf("\nUser cancel, exiting...\n"); + } else { + printf("\nExiting... hackrf_is_streaming() result: %s (%d)\n", hackrf_error_name(result), result); + } - if(device != NULL) { - result = hackrf_stop_tx(device); - if( result != HACKRF_SUCCESS ) { - printf("hackrf_stop_tx() failed: %s (%d)\n", hackrf_error_name(result), result); - } else { - printf("hackrf_stop_tx() done\n"); - } + if (device != NULL) { + result = hackrf_stop_tx(device); + if (result != HACKRF_SUCCESS) { + printf("hackrf_stop_tx() failed: %s (%d)\n", hackrf_error_name(result), result); + } else { + printf("hackrf_stop_tx() done\n"); + } - result = hackrf_close(device); - if( result != HACKRF_SUCCESS ) - { - printf("hackrf_close() failed: %s (%d)\n", hackrf_error_name(result), result); - } else { - printf("hackrf_close() done\n"); - } - - hackrf_exit(); - printf("hackrf_exit() done\n"); - } + result = hackrf_close(device); + if (result != HACKRF_SUCCESS) { + printf("hackrf_close() failed: %s (%d)\n", hackrf_error_name(result), result); + } else { + printf("hackrf_close() done\n"); + } - if(fd != NULL) { - fclose(fd); - fd = NULL; - printf("fclose(fd) done\n"); - } + hackrf_exit(); + printf("hackrf_exit() done\n"); + } - printf("exit\n"); - return EXIT_SUCCESS; + if (fd != NULL) { + fclose(fd); + fd = NULL; + printf("fclose(fd) done\n"); + } + + printf("exit\n"); + return EXIT_SUCCESS; } diff --git a/player/plutoplayer.c b/player/plutoplayer.c new file mode 100644 index 0000000..9422851 --- /dev/null +++ b/player/plutoplayer.c @@ -0,0 +1,234 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#define NOTUSED(V) ((void) V) +#define MHZ(x) ((long long)(x*1000000.0 + .5)) +#define GHZ(x) ((long long)(x*1000000000.0 + .5)) +#define NUM_SAMPLES 2600000 +#define BUFFER_SIZE (NUM_SAMPLES * 2 * sizeof(int16_t)) + + +struct stream_cfg { + long long bw_hz; // Analog banwidth in Hz + long long fs_hz; // Baseband sample rate in Hz + long long lo_hz; // Local oscillator frequency in Hz + const char* rfport; // Port name + double gain_db; // Hardware gain +}; + +static void usage() { + fprintf(stderr, "Usage: plutoplayer [options]\n" + " -t Transmit data from file (required)\n" + " -a Set TX attenuation [dB] (default -20.0)" + " -b Set RF bandwidth [MHz] (default 5.0)"); + return; +} + +static bool stop = false; + +static void handle_sig(int sig) +{ + NOTUSED(sig); + stop = true; +} + +static char* readable_fs(double size, char *buf, size_t buf_size) { + int i = 0; + const char* units[] = {"B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"}; + while (size > 1024) { + size /= 1024; + i++; + } + snprintf(buf, buf_size, "%.*f %s", i, size, units[i]); + return buf; +} + +/* + * + */ +int main(int argc, char** argv) { + char buf[1024]; + int opt; + const char* path = NULL; + struct stream_cfg txcfg; + FILE *fp = NULL; + enum state {INIT, READ_FILE, PAD_TRAILING, DONE}; + + // TX stream default config + txcfg.bw_hz = MHZ(3.0); // 3.0 MHz RF bandwidth + txcfg.fs_hz = MHZ(2.6); // 2.6 MS/s TX sample rate + txcfg.lo_hz = GHZ(1.575420); // 1.57542 GHz RF frequency + txcfg.rfport = "A"; + txcfg.gain_db = -20.0; + + struct iio_context *ctx = NULL; + struct iio_device *tx = NULL; + struct iio_device *phydev = NULL; + struct iio_channel *tx0_i = NULL; + struct iio_channel *tx0_q = NULL; + struct iio_buffer *tx_buffer = NULL; + + while ((opt = getopt(argc, argv, "t:a:b:")) != EOF) { + switch (opt) { + case 't': + path = optarg; + break; + case 'a': + txcfg.gain_db = atof(optarg); + if(txcfg.gain_db > 0.0) txcfg.gain_db = 0.0; + if(txcfg.gain_db < -80.0) txcfg.gain_db = -80.0; + break; + case 'b': + txcfg.bw_hz = MHZ(atof(optarg)); + if(txcfg.bw_hz > MHZ(5.0)) txcfg.bw_hz = MHZ(5.0); + if(txcfg.bw_hz < MHZ(1.0)) txcfg.bw_hz = MHZ(1.0); + break; + default: + printf("Unknown argument '-%c %s'\n", opt, optarg); + usage(); + return EXIT_FAILURE; + } + } + + signal(SIGINT, handle_sig); + + if( path == NULL ) { + printf("Specify a path to a file to transmit\n"); + usage(); + return EXIT_FAILURE; + } + + fp = fopen(path, "rb"); + if (fp==NULL) { + fprintf(stderr, "ERROR: Failed to open TX file: %s\n", path); + return EXIT_FAILURE; + } + fseek(fp, 0L, SEEK_END); + size_t sz = ftell(fp); + fseek(fp, 0L, SEEK_SET); + readable_fs((double)sz, buf, sizeof(buf)); + printf("* Transmit file size: %s\n", buf); + + printf("* Acquiring IIO context\n"); + ctx = iio_create_default_context(); + if (ctx == NULL) { + ctx = iio_create_network_context("pluto.local"); + } + + if (ctx == NULL) { + iio_strerror(errno, buf, sizeof(buf)); + fprintf(stderr, "Failed creating IIO context: %s\n", buf); + return false; + } + + struct iio_scan_context *scan_ctx; + struct iio_context_info **info; + scan_ctx = iio_create_scan_context(NULL, 0); + if (scan_ctx) { + int info_count = iio_scan_context_get_info_list(scan_ctx, &info); + if(info_count > 0) { + printf("* Found %s\n", iio_context_info_get_description(info[0])); + iio_context_info_list_free(info); + } + iio_scan_context_destroy(scan_ctx); + } + + printf("* Acquiring devices\n"); + int device_count = iio_context_get_devices_count(ctx); + if (!device_count) { + fprintf(stderr, "No supported PLUTOSDR devices found.\n"); + goto error_exit; + } + fprintf(stderr, "* Context has %d device(s).\n", device_count); + + printf("* Acquiring TX device\n"); + tx = iio_context_find_device(ctx, "cf-ad9361-dds-core-lpc"); + if (tx == NULL) { + iio_strerror(errno, buf, sizeof(buf)); + fprintf(stderr, "Error opening PLUTOSDR TX device: %s\n", buf); + goto error_exit; + } + + iio_device_set_kernel_buffers_count(tx, 8); + + phydev = iio_context_find_device(ctx, "ad9361-phy"); + struct iio_channel* phy_chn = iio_device_find_channel(phydev, "voltage0", true); + iio_channel_attr_write(phy_chn, "rf_port_select", txcfg.rfport); + iio_channel_attr_write_longlong(phy_chn, "rf_bandwidth", txcfg.bw_hz); + iio_channel_attr_write_longlong(phy_chn, "sampling_frequency", txcfg.fs_hz); + iio_channel_attr_write_double(phy_chn, "hardwaregain", txcfg.gain_db); + + iio_channel_attr_write_bool( + iio_device_find_channel(phydev, "altvoltage0", true) + , "powerdown", true); // Turn OFF RX LO + + iio_channel_attr_write_longlong( + iio_device_find_channel(phydev, "altvoltage1", true) + , "frequency", txcfg.lo_hz); // Set TX LO frequency + + iio_channel_attr_write_longlong( + iio_device_find_channel(phydev, "altvoltage1", true) + , "frequency", txcfg.lo_hz); // Set TX LO frequency + + printf("* Initializing streaming channels\n"); + tx0_i = iio_device_find_channel(tx, "voltage0", true); + if (!tx0_i) + tx0_i = iio_device_find_channel(tx, "altvoltage0", true); + + tx0_q = iio_device_find_channel(tx, "voltage1", true); + if (!tx0_q) + tx0_q = iio_device_find_channel(tx, "altvoltage1", true); + + printf("* Enabling IIO streaming channels\n"); + iio_channel_enable(tx0_i); + iio_channel_enable(tx0_q); + + ad9361_set_bb_rate(iio_context_find_device(ctx, "ad9361-phy"), txcfg.fs_hz); + + printf("* Creating TX buffer\n"); + + tx_buffer = iio_device_create_buffer(tx, NUM_SAMPLES, false); + if (!tx_buffer) { + fprintf(stderr, "Could not create TX buffer.\n"); + goto error_exit; + } + + iio_channel_attr_write_bool( + iio_device_find_channel(iio_context_find_device(ctx, "ad9361-phy"), "altvoltage1", true) + , "powerdown", false); // Turn ON TX LO + + int32_t ntx = 0; + char *ptx_buffer = (char *)iio_buffer_start(tx_buffer); + + printf("* Transmit starts...\n"); + // Keep writing samples while there is more data to send and no failures have occurred. + while (!feof(fp) && !stop) { + fread(ptx_buffer, 1, BUFFER_SIZE,fp); + // Schedule TX buffer + ntx = iio_buffer_push(tx_buffer); + if (ntx < 0) { + printf("Error pushing buf %d\n", (int) ntx); + goto error_exit; + } + } + printf("Done.\n"); + +error_exit: + fclose(fp); + iio_channel_attr_write_bool( + iio_device_find_channel(iio_context_find_device(ctx, "ad9361-phy"), "altvoltage1", true) + , "powerdown", true); // Turn OFF TX LO + + if (tx_buffer) { iio_buffer_destroy(tx_buffer); } + if (tx0_i) { iio_channel_disable(tx0_i); } + if (tx0_q) { iio_channel_disable(tx0_q); } + if (ctx) { iio_context_destroy(ctx); } + return EXIT_SUCCESS; +} +