diff options
Diffstat (limited to 'Radio/HW/AirSpy/src')
-rw-r--r-- | Radio/HW/AirSpy/src/airspy.c | 2011 | ||||
-rw-r--r-- | Radio/HW/AirSpy/src/airspy.h | 224 | ||||
-rw-r--r-- | Radio/HW/AirSpy/src/airspy_commands.h | 145 | ||||
-rw-r--r-- | Radio/HW/AirSpy/src/filters.h | 134 | ||||
-rw-r--r-- | Radio/HW/AirSpy/src/iqconverter_float.c | 502 | ||||
-rw-r--r-- | Radio/HW/AirSpy/src/iqconverter_float.h | 47 | ||||
-rw-r--r-- | Radio/HW/AirSpy/src/iqconverter_int16.c | 205 | ||||
-rw-r--r-- | Radio/HW/AirSpy/src/iqconverter_int16.h | 45 |
8 files changed, 3313 insertions, 0 deletions
diff --git a/Radio/HW/AirSpy/src/airspy.c b/Radio/HW/AirSpy/src/airspy.c new file mode 100644 index 0000000..02c1542 --- /dev/null +++ b/Radio/HW/AirSpy/src/airspy.c @@ -0,0 +1,2011 @@ +/* +Copyright (c) 2013, Michael Ossmann <mike@ossmann.com> +Copyright (c) 2012, Jared Boone <jared@sharebrained.com> +Copyright (c) 2014, Youssef Touil <youssef@airspy.com> +Copyright (c) 2014, Benjamin Vernoux <bvernoux@airspy.com> +Copyright (c) 2015, Ian Gilmour <ian@sdrsharp.com> + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + Neither the name of AirSpy nor the names of its contributors may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "libusb.h" + +#if _MSC_VER > 1700 // To avoid error with Visual Studio 2017/2019 or more define which define timespec as it is already defined in pthread.h +#define HAVE_STRUCT_TIMESPEC +#endif + +#include <pthread.h> + +#include "airspy.h" +#include "iqconverter_float.h" +#include "iqconverter_int16.h" +#include "filters.h" + +#ifndef bool +typedef int bool; +#define true 1 +#define false 0 +#endif + +#define PACKET_SIZE (12) +#define UNPACKED_SIZE (16) +#define RAW_BUFFER_COUNT (8) + +#ifdef AIRSPY_BIG_ENDIAN +#define TO_LE(x) __builtin_bswap32(x) +#else +#define TO_LE(x) x +#endif + +#define SAMPLE_RESOLUTION 12 +#define SAMPLE_ENCAPSULATION 16 + +#define SAMPLE_SHIFT (SAMPLE_ENCAPSULATION - SAMPLE_RESOLUTION) +#define SAMPLE_SCALE (1.0f / (1 << (15 - SAMPLE_SHIFT))) + +#define SERIAL_NUMBER_UNUSED (0ULL) +#define FILE_DESCRIPTOR_UNUSED (-1) + +#define USB_PRODUCT_ID (2) +#define STR_DESCRIPTOR_SIZE (250) + +#define MIN_SAMPLERATE_BY_VALUE (1000000) +#define SAMPLE_TYPE_IS_IQ(x) ((x) == AIRSPY_SAMPLE_FLOAT32_IQ || (x) == AIRSPY_SAMPLE_INT16_IQ) + +#define LIBUSB_CTRL_TIMEOUT_MS (500) + +typedef struct { + uint32_t freq_hz; +} set_freq_params_t; + +typedef struct airspy_device +{ + libusb_context* usb_context; + libusb_device_handle* usb_device; + struct libusb_transfer** transfers; + airspy_sample_block_cb_fn callback; + volatile bool streaming; + volatile bool stop_requested; + pthread_t transfer_thread; + pthread_t consumer_thread; + bool transfer_thread_running; + bool consumer_thread_running; + pthread_cond_t consumer_cv; + pthread_mutex_t consumer_mp; + uint32_t supported_samplerate_count; + uint32_t *supported_samplerates; + uint32_t transfer_count; + uint32_t buffer_size; + uint32_t dropped_buffers; + uint32_t dropped_buffers_queue[RAW_BUFFER_COUNT]; + uint16_t *received_samples_queue[RAW_BUFFER_COUNT]; + volatile int received_samples_queue_head; + volatile int received_samples_queue_tail; + volatile int received_buffer_count; + void *output_buffer; + uint16_t *unpacked_samples; + bool packing_enabled; + iqconverter_float_t *cnv_f; + iqconverter_int16_t *cnv_i; + void* ctx; + enum airspy_sample_type sample_type; +} airspy_device_t; + +static const uint16_t airspy_usb_vid = 0x1d50; +static const uint16_t airspy_usb_pid = 0x60a1; + +#define STR_PREFIX_SERIAL_AIRSPY_SIZE (10) + +#define SERIAL_AIRSPY_EXPECTED_SIZE (26) + +#define GAIN_COUNT (22) + +uint8_t airspy_linearity_vga_gains[GAIN_COUNT] = { 13, 12, 11, 11, 11, 11, 11, 10, 10, 10, 10, 10, 10, 10, 10, 10, 9, 8, 7, 6, 5, 4 }; +uint8_t airspy_linearity_mixer_gains[GAIN_COUNT] = { 12, 12, 11, 9, 8, 7, 6, 6, 5, 0, 0, 1, 0, 0, 2, 2, 1, 1, 1, 1, 0, 0 }; +uint8_t airspy_linearity_lna_gains[GAIN_COUNT] = { 14, 14, 14, 13, 12, 10, 9, 9, 8, 9, 8, 6, 5, 3, 1, 0, 0, 0, 0, 0, 0, 0 }; +uint8_t airspy_sensitivity_vga_gains[GAIN_COUNT] = { 13, 12, 11, 10, 9, 8, 7, 6, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4 }; +uint8_t airspy_sensitivity_mixer_gains[GAIN_COUNT] = { 12, 12, 12, 12, 11, 10, 10, 9, 9, 8, 7, 4, 4, 4, 3, 2, 2, 1, 0, 0, 0, 0 }; +uint8_t airspy_sensitivity_lna_gains[GAIN_COUNT] = { 14, 14, 14, 14, 14, 14, 14, 14, 14, 13, 12, 12, 9, 9, 8, 7, 6, 5, 3, 2, 1, 0 }; + +static int cancel_transfers(airspy_device_t* device) +{ + uint32_t transfer_index; + + if (device->transfers != NULL) + { + for (transfer_index = 0; transfer_index<device->transfer_count; transfer_index++) + { + if (device->transfers[transfer_index] != NULL) + { + libusb_cancel_transfer(device->transfers[transfer_index]); + } + } + return AIRSPY_SUCCESS; + } + else { + return AIRSPY_ERROR_OTHER; + } +} + +static int free_transfers(airspy_device_t* device) +{ + int i; + uint32_t transfer_index; + + if (device->transfers != NULL) + { + // libusb_close() should free all transfers referenced from this array. + for (transfer_index = 0; transfer_index < device->transfer_count; transfer_index++) + { + if (device->transfers[transfer_index] != NULL) + { + free(device->transfers[transfer_index]->buffer); + libusb_free_transfer(device->transfers[transfer_index]); + device->transfers[transfer_index] = NULL; + } + } + free(device->transfers); + device->transfers = NULL; + + if (device->output_buffer != NULL) + { + free(device->output_buffer); + device->output_buffer = NULL; + } + + if (device->unpacked_samples != NULL) + { + free(device->unpacked_samples); + device->unpacked_samples = NULL; + } + + for (i = 0; i < RAW_BUFFER_COUNT; i++) + { + if (device->received_samples_queue[i] != NULL) + { + free(device->received_samples_queue[i]); + device->received_samples_queue[i] = NULL; + } + } + } + + return AIRSPY_SUCCESS; +} + +static int allocate_transfers(airspy_device_t* const device) +{ + int i; + size_t sample_count; + uint32_t transfer_index; + + if (device->transfers == NULL) + { + for (i = 0; i < RAW_BUFFER_COUNT; i++) + { + device->received_samples_queue[i] = (uint16_t *)malloc(device->buffer_size); + if (device->received_samples_queue[i] == NULL) + { + return AIRSPY_ERROR_NO_MEM; + } + + memset(device->received_samples_queue[i], 0, device->buffer_size); + } + + if (device->packing_enabled) + { + sample_count = ((device->buffer_size / 2) * 4) / 3; + } + else + { + sample_count = device->buffer_size / 2; + } + + device->output_buffer = (float *)malloc(sample_count * sizeof(float)); + if (device->output_buffer == NULL) + { + return AIRSPY_ERROR_NO_MEM; + } + + if (device->packing_enabled) + { + device->unpacked_samples = (uint16_t*)malloc(sample_count * sizeof(uint16_t)); + if (device->unpacked_samples == NULL) + { + return AIRSPY_ERROR_NO_MEM; + } + } + + device->transfers = (struct libusb_transfer**) calloc(device->transfer_count, sizeof(struct libusb_transfer)); + if (device->transfers == NULL) + { + return AIRSPY_ERROR_NO_MEM; + } + + for (transfer_index = 0; transfer_index<device->transfer_count; transfer_index++) + { + device->transfers[transfer_index] = libusb_alloc_transfer(0); + if (device->transfers[transfer_index] == NULL) + { + return AIRSPY_ERROR_LIBUSB; + } + + libusb_fill_bulk_transfer( + device->transfers[transfer_index], + device->usb_device, + 0, + (unsigned char*)malloc(device->buffer_size), + device->buffer_size, + NULL, + device, + 0 + ); + + if (device->transfers[transfer_index]->buffer == NULL) + { + return AIRSPY_ERROR_NO_MEM; + } + } + return AIRSPY_SUCCESS; + } + else + { + return AIRSPY_ERROR_BUSY; + } +} + +static int prepare_transfers(airspy_device_t* device, const uint_fast8_t endpoint_address, libusb_transfer_cb_fn callback) +{ + int error; + uint32_t transfer_index; + if (device->transfers != NULL) + { + for (transfer_index = 0; transfer_index<device->transfer_count; transfer_index++) + { + device->transfers[transfer_index]->endpoint = endpoint_address; + device->transfers[transfer_index]->callback = callback; + + error = libusb_submit_transfer(device->transfers[transfer_index]); + if (error != 0) + { + return AIRSPY_ERROR_LIBUSB; + } + } + return AIRSPY_SUCCESS; + } + else { + // This shouldn't happen. + return AIRSPY_ERROR_OTHER; + } +} + +static void convert_samples_int16(uint16_t *src, int16_t *dest, int count) +{ + int i; + for (i = 0; i < count; i += 4) + { + dest[i + 0] = (src[i + 0] - 2048) << SAMPLE_SHIFT; + dest[i + 1] = (src[i + 1] - 2048) << SAMPLE_SHIFT; + dest[i + 2] = (src[i + 2] - 2048) << SAMPLE_SHIFT; + dest[i + 3] = (src[i + 3] - 2048) << SAMPLE_SHIFT; + } +} + +static void convert_samples_float(uint16_t *src, float *dest, int count) +{ + int i; + for (i = 0; i < count; i += 4) + { + dest[i + 0] = (src[i + 0] - 2048) * SAMPLE_SCALE; + dest[i + 1] = (src[i + 1] - 2048) * SAMPLE_SCALE; + dest[i + 2] = (src[i + 2] - 2048) * SAMPLE_SCALE; + dest[i + 3] = (src[i + 3] - 2048) * SAMPLE_SCALE; + } +} + +static inline void unpack_samples(uint32_t *input, uint16_t *output, int length) +{ + int i, j; + + for (i = 0, j = 0; j < length; i += 3, j += 8) + { + output[j + 0] = (input[i] >> 20) & 0xfff; + output[j + 1] = (input[i] >> 8) & 0xfff; + output[j + 2] = ((input[i] & 0xff) << 4) | ((input[i + 1] >> 28) & 0xf); + output[j + 3] = ((input[i + 1] & 0xfff0000) >> 16); + output[j + 4] = ((input[i + 1] & 0xfff0) >> 4); + output[j + 5] = ((input[i + 1] & 0xf) << 8) | ((input[i + 2] & 0xff000000) >> 24); + output[j + 6] = ((input[i + 2] >> 12) & 0xfff); + output[j + 7] = ((input[i + 2] & 0xfff)); + } +} + +static void* consumer_threadproc(void *arg) +{ + int sample_count; + uint16_t* input_samples; + uint32_t dropped_buffers; + airspy_device_t* device = (airspy_device_t*)arg; + airspy_transfer_t transfer; + +#ifdef _WIN32 + + SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST); + +#endif + + pthread_mutex_lock(&device->consumer_mp); + + while (device->streaming && !device->stop_requested) + { + while (device->received_buffer_count == 0 && device->streaming && !device->stop_requested) + { + pthread_cond_wait(&device->consumer_cv, &device->consumer_mp); + } + if (!device->streaming || device->stop_requested) + { + break; + } + + input_samples = device->received_samples_queue[device->received_samples_queue_tail]; + dropped_buffers = device->dropped_buffers_queue[device->received_samples_queue_tail]; + device->received_samples_queue_tail = (device->received_samples_queue_tail + 1) & (RAW_BUFFER_COUNT - 1); + + pthread_mutex_unlock(&device->consumer_mp); + + if (device->packing_enabled) + { + sample_count = ((device->buffer_size / 2) * 4) / 3; + + if (device->sample_type != AIRSPY_SAMPLE_RAW) + { + unpack_samples((uint32_t*)input_samples, device->unpacked_samples, sample_count); + + input_samples = device->unpacked_samples; + } + } + else + { + sample_count = device->buffer_size / 2; + } + + switch (device->sample_type) + { + case AIRSPY_SAMPLE_FLOAT32_IQ: + convert_samples_float(input_samples, (float *)device->output_buffer, sample_count); + iqconverter_float_process(device->cnv_f, (float *) device->output_buffer, sample_count); + sample_count /= 2; + transfer.samples = device->output_buffer; + break; + + case AIRSPY_SAMPLE_FLOAT32_REAL: + convert_samples_float(input_samples, (float *)device->output_buffer, sample_count); + transfer.samples = device->output_buffer; + break; + + case AIRSPY_SAMPLE_INT16_IQ: + convert_samples_int16(input_samples, (int16_t *)device->output_buffer, sample_count); + iqconverter_int16_process(device->cnv_i, (int16_t *) device->output_buffer, sample_count); + sample_count /= 2; + transfer.samples = device->output_buffer; + break; + + case AIRSPY_SAMPLE_INT16_REAL: + convert_samples_int16(input_samples, (int16_t *)device->output_buffer, sample_count); + transfer.samples = device->output_buffer; + break; + + case AIRSPY_SAMPLE_UINT16_REAL: + case AIRSPY_SAMPLE_RAW: + transfer.samples = input_samples; + break; + + case AIRSPY_SAMPLE_END: + // Just to shut GCC's moaning + break; + } + + transfer.device = device; + transfer.ctx = device->ctx; + transfer.sample_count = sample_count; + transfer.sample_type = device->sample_type; + transfer.dropped_samples = (uint64_t) dropped_buffers * (uint64_t) sample_count; + + if (device->callback(&transfer) != 0) + { + device->streaming = false; + } + + pthread_mutex_lock(&device->consumer_mp); + device->received_buffer_count--; + } + + device->streaming = false; + + pthread_mutex_unlock(&device->consumer_mp); + + return NULL; +} + +static void airspy_libusb_transfer_callback(struct libusb_transfer* usb_transfer) +{ + uint16_t *temp; + airspy_device_t* device = (airspy_device_t*)usb_transfer->user_data; + + if (!device->streaming || device->stop_requested) + { + return; + } + + if (usb_transfer->status == LIBUSB_TRANSFER_COMPLETED && usb_transfer->actual_length == usb_transfer->length) + { + pthread_mutex_lock(&device->consumer_mp); + + if (device->received_buffer_count < RAW_BUFFER_COUNT) + { + temp = device->received_samples_queue[device->received_samples_queue_head]; + device->received_samples_queue[device->received_samples_queue_head] = (uint16_t *)usb_transfer->buffer; + usb_transfer->buffer = (uint8_t *)temp; + + device->dropped_buffers_queue[device->received_samples_queue_head] = device->dropped_buffers; + device->dropped_buffers = 0; + + device->received_samples_queue_head = (device->received_samples_queue_head + 1) & (RAW_BUFFER_COUNT - 1); + device->received_buffer_count++; + + pthread_cond_signal(&device->consumer_cv); + } + else + { + device->dropped_buffers++; + } + + pthread_mutex_unlock(&device->consumer_mp); + + if (libusb_submit_transfer(usb_transfer) != 0) + { + device->streaming = false; + } + } + else + { + device->streaming = false; + } +} + +static void* transfer_threadproc(void* arg) +{ + airspy_device_t* device = (airspy_device_t*)arg; + int error; + struct timeval timeout = { 0, 500000 }; + +#ifdef _WIN32 + + SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST); + +#endif + + while (device->streaming && !device->stop_requested) + { + error = libusb_handle_events_timeout_completed(device->usb_context, &timeout, NULL); + if (error < 0) + { + if (error != LIBUSB_ERROR_INTERRUPTED) + device->streaming = false; + } + } + + device->streaming = false; + + return NULL; +} + +static int kill_io_threads(airspy_device_t* device) +{ + struct timeval timeout = { 0, 0 }; + + if (device->stop_requested) + { + device->stop_requested = false; + device->streaming = false; + cancel_transfers(device); + + pthread_mutex_lock(&device->consumer_mp); + pthread_cond_signal(&device->consumer_cv); + pthread_mutex_unlock(&device->consumer_mp); + + if (device->transfer_thread_running) { + pthread_join(device->transfer_thread, NULL); + device->transfer_thread_running = false; + } + if (device->consumer_thread_running) { + pthread_join(device->consumer_thread, NULL); + device->consumer_thread_running = false; + } + + libusb_handle_events_timeout_completed(device->usb_context, &timeout, NULL); + } + + return AIRSPY_SUCCESS; +} + +static int create_io_threads(airspy_device_t* device, airspy_sample_block_cb_fn callback) +{ + int result; + pthread_attr_t attr; + + if (!device->streaming && !device->stop_requested) + { + device->callback = callback; + device->streaming = true; + + result = prepare_transfers(device, LIBUSB_ENDPOINT_IN | 1, (libusb_transfer_cb_fn)airspy_libusb_transfer_callback); + if (result != AIRSPY_SUCCESS) + { + return result; + } + + device->received_samples_queue_head = 0; + device->received_samples_queue_tail = 0; + device->received_buffer_count = 0; + + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); + + result = pthread_create(&device->consumer_thread, &attr, consumer_threadproc, device); + if (result != 0) + { + return AIRSPY_ERROR_THREAD; + } + device->consumer_thread_running = true; + + result = pthread_create(&device->transfer_thread, &attr, transfer_threadproc, device); + if (result != 0) + { + return AIRSPY_ERROR_THREAD; + } + device->transfer_thread_running = true; + + pthread_attr_destroy(&attr); + } + else { + return AIRSPY_ERROR_BUSY; + } + + return AIRSPY_SUCCESS; +} + +static void airspy_open_exit(airspy_device_t* device) +{ + if (device->usb_device != NULL) + { + libusb_release_interface(device->usb_device, 0); + libusb_close(device->usb_device); + device->usb_device = NULL; + } + libusb_exit(device->usb_context); + device->usb_context = NULL; +} + +static void airspy_open_device(airspy_device_t* device, + int* ret, + uint16_t vid, + uint16_t pid, + uint64_t serial_number_val) +{ + int i; + int result; + libusb_device_handle** libusb_dev_handle; + int serial_number_len; + libusb_device_handle* dev_handle; + libusb_device *dev; + libusb_device** devices = NULL; + + ssize_t cnt; + int serial_descriptor_index; + struct libusb_device_descriptor device_descriptor; + unsigned char serial_number[SERIAL_AIRSPY_EXPECTED_SIZE + 1]; + + libusb_dev_handle = &device->usb_device; + *libusb_dev_handle = NULL; + + cnt = libusb_get_device_list(device->usb_context, &devices); + if (cnt < 0) + { + *ret = AIRSPY_ERROR_NOT_FOUND; + return; + } + + i = 0; + while ((dev = devices[i++]) != NULL) + { + libusb_get_device_descriptor(dev, &device_descriptor); + + if ((device_descriptor.idVendor == vid) && + (device_descriptor.idProduct == pid)) + { + if (serial_number_val != SERIAL_NUMBER_UNUSED) + { + serial_descriptor_index = device_descriptor.iSerialNumber; + if (serial_descriptor_index > 0) + { + if (libusb_open(dev, libusb_dev_handle) != 0) + { + *libusb_dev_handle = NULL; + continue; + } + dev_handle = *libusb_dev_handle; + serial_number_len = libusb_get_string_descriptor_ascii(dev_handle, + serial_descriptor_index, + serial_number, + sizeof(serial_number)); + if (serial_number_len == SERIAL_AIRSPY_EXPECTED_SIZE) + { + uint64_t serial = 0; + // use same code to determine device's serial number as in airspy_list_devices() + { + char *start, *end; + + serial_number[SERIAL_AIRSPY_EXPECTED_SIZE] = 0; + start = (char*)(serial_number + STR_PREFIX_SERIAL_AIRSPY_SIZE); + end = NULL; + serial = strtoull(start, &end, 16); + } + + if (serial == serial_number_val) + { +#ifdef __linux__ + /* Check whether a kernel driver is attached to interface #0. If so, we'll + * need to detach it. + */ + if (libusb_kernel_driver_active(dev_handle, 0)) + { + libusb_detach_kernel_driver(dev_handle, 0); + } +#endif + result = libusb_set_configuration(dev_handle, 1); + if (result != 0) + { + libusb_close(dev_handle); + *libusb_dev_handle = NULL; + continue; + } + result = libusb_claim_interface(dev_handle, 0); + if (result != 0) + { + libusb_close(dev_handle); + *libusb_dev_handle = NULL; + continue; + } + break; + } + else + { + libusb_close(dev_handle); + *libusb_dev_handle = NULL; + continue; + } + } + else + { + libusb_close(dev_handle); + *libusb_dev_handle = NULL; + continue; + } + } + } + else + { + if (libusb_open(dev, libusb_dev_handle) == 0) + { + dev_handle = *libusb_dev_handle; +#ifdef __linux__ + /* Check whether a kernel driver is attached to interface #0. If so, we'll + * need to detach it. + */ + if (libusb_kernel_driver_active(dev_handle, 0)) + { + libusb_detach_kernel_driver(dev_handle, 0); + } +#endif + result = libusb_set_configuration(dev_handle, 1); + if (result != 0) + { + libusb_close(dev_handle); + *libusb_dev_handle = NULL; + continue; + } + result = libusb_claim_interface(dev_handle, 0); + if (result != 0) + { + libusb_close(dev_handle); + *libusb_dev_handle = NULL; + continue; + } + break; + } + } + } + } + libusb_free_device_list(devices, 1); + + dev_handle = device->usb_device; + if (dev_handle == NULL) + { + *ret = AIRSPY_ERROR_NOT_FOUND; + return; + } + + *ret = AIRSPY_SUCCESS; + return; +} + +static void airspy_open_device_fd(airspy_device_t* device, + int* ret, + int fd) +{ + int result = -1; + +#ifdef __ANDROID__ + result = libusb_wrap_sys_device(device->usb_context, (intptr_t)fd, &device->usb_device); +#else + device->usb_device = NULL; + *ret = AIRSPY_ERROR_UNSUPPORTED; + return; +#endif + + if (result != 0 || device->usb_device == NULL) + { + *ret = AIRSPY_ERROR_LIBUSB; + return; + } + +#ifdef __linux__ + /* Check whether a kernel driver is attached to interface #0. If so, we'll + * need to detach it. + */ + if (libusb_kernel_driver_active(device->usb_device, 0)) + { + libusb_detach_kernel_driver(device->usb_device, 0); + } +#endif + + result = libusb_set_configuration(device->usb_device, 1); + if (result != 0) + { + libusb_close(device->usb_device); + device->usb_device = NULL; + *ret = AIRSPY_ERROR_LIBUSB; + return; + } + + result = libusb_claim_interface(device->usb_device, 0); + if (result != 0) + { + libusb_close(device->usb_device); + device->usb_device = NULL; + *ret = AIRSPY_ERROR_LIBUSB; + return; + } + + *ret = AIRSPY_SUCCESS; + return; +} + +static int airspy_read_samplerates_from_fw(struct airspy_device* device, uint32_t* buffer, const uint32_t len) +{ + int result; + + result = libusb_control_transfer( + device->usb_device, + LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, + AIRSPY_GET_SAMPLERATES, + 0, + len, + (unsigned char*)buffer, + (len > 0 ? len : 1) * sizeof(uint32_t), + LIBUSB_CTRL_TIMEOUT_MS); + + if (result < 1) + { + return AIRSPY_ERROR_OTHER; + } + + return AIRSPY_SUCCESS; +} + +static int airspy_open_init(airspy_device_t** device, uint64_t serial_number, int fd) +{ + airspy_device_t* lib_device; + int libusb_error; + int result; + + *device = NULL; + + lib_device = (airspy_device_t*)calloc(1, sizeof(airspy_device_t)); + if (lib_device == NULL) + { + return AIRSPY_ERROR_NO_MEM; + } + +#ifdef __ANDROID__ + // LibUSB does not support device discovery on android + libusb_set_option(NULL, LIBUSB_OPTION_NO_DEVICE_DISCOVERY, NULL); +#endif + + libusb_error = libusb_init(&lib_device->usb_context); + if (libusb_error != 0) + { + free(lib_device); + return AIRSPY_ERROR_LIBUSB; + } + + if (fd == FILE_DESCRIPTOR_UNUSED) { + airspy_open_device(lib_device, + &result, + airspy_usb_vid, + airspy_usb_pid, + serial_number); + } + else { + airspy_open_device_fd(lib_device, + &result, + fd); + } + + if (lib_device->usb_device == NULL) + { + libusb_exit(lib_device->usb_context); + free(lib_device); + return result; + } + + lib_device->transfers = NULL; + lib_device->callback = NULL; + lib_device->transfer_count = 16; + lib_device->buffer_size = 262144; + lib_device->packing_enabled = false; + lib_device->streaming = false; + lib_device->stop_requested = false; + lib_device->sample_type = AIRSPY_SAMPLE_FLOAT32_IQ; + + result = airspy_read_samplerates_from_fw(lib_device, &lib_device->supported_samplerate_count, 0); + if (result == AIRSPY_SUCCESS) + { + lib_device->supported_samplerates = (uint32_t *) malloc(lib_device->supported_samplerate_count * sizeof(uint32_t)); + result = airspy_read_samplerates_from_fw(lib_device, lib_device->supported_samplerates, lib_device->supported_samplerate_count); + if (result != AIRSPY_SUCCESS) + { + free(lib_device->supported_samplerates); + } + } + + if (result != AIRSPY_SUCCESS) + { + lib_device->supported_samplerate_count = 2; + lib_device->supported_samplerates = (uint32_t *) malloc(lib_device->supported_samplerate_count * sizeof(uint32_t)); + lib_device->supported_samplerates[0] = 10000000; + lib_device->supported_samplerates[1] = 2500000; + } + + airspy_set_packing(lib_device, 0); + + result = allocate_transfers(lib_device); + if (result != 0) + { + airspy_open_exit(lib_device); + free(lib_device->supported_samplerates); + free(lib_device); + return AIRSPY_ERROR_NO_MEM; + } + + lib_device->cnv_f = iqconverter_float_create(HB_KERNEL_FLOAT, HB_KERNEL_FLOAT_LEN); + lib_device->cnv_i = iqconverter_int16_create(HB_KERNEL_INT16, HB_KERNEL_INT16_LEN); + + pthread_cond_init(&lib_device->consumer_cv, NULL); + pthread_mutex_init(&lib_device->consumer_mp, NULL); + + *device = lib_device; + + return AIRSPY_SUCCESS; +} + +#ifdef __cplusplus +extern "C" +{ +#endif + + void ADDCALL airspy_lib_version(airspy_lib_version_t* lib_version) + { + lib_version->major_version = AIRSPY_VER_MAJOR; + lib_version->minor_version = AIRSPY_VER_MINOR; + lib_version->revision = AIRSPY_VER_REVISION; + } + + /* airspy_init() deprecated */ + int ADDCALL airspy_init(void) + { + return AIRSPY_SUCCESS; + } + + /* airspy_exit() deprecated */ + int ADDCALL airspy_exit(void) + { + return AIRSPY_SUCCESS; + } + +int airspy_list_devices(uint64_t *serials, int count) +{ + libusb_device_handle* libusb_dev_handle; + struct libusb_context *context; + libusb_device** devices = NULL; + libusb_device *dev; + struct libusb_device_descriptor device_descriptor; + + int serial_descriptor_index; + int serial_number_len; + int output_count; + int i; + unsigned char serial_number[SERIAL_AIRSPY_EXPECTED_SIZE + 1]; + + if (serials) + { + memset(serials, 0, sizeof(uint64_t) * count); + } + +#ifdef __ANDROID__ + // LibUSB does not support device discovery on android + libusb_set_option(NULL, LIBUSB_OPTION_NO_DEVICE_DISCOVERY, NULL); +#endif + + if (libusb_init(&context) != 0) + { + return AIRSPY_ERROR_LIBUSB; + } + + if (libusb_get_device_list(context, &devices) < 0) + { + return AIRSPY_ERROR_NOT_FOUND; + } + + i = 0; + output_count = 0; + while ((dev = devices[i++]) != NULL && (!serials || output_count < count)) + { + libusb_get_device_descriptor(dev, &device_descriptor); + + if ((device_descriptor.idVendor == airspy_usb_vid) && + (device_descriptor.idProduct == airspy_usb_pid)) + { + serial_descriptor_index = device_descriptor.iSerialNumber; + if (serial_descriptor_index > 0) + { + if (libusb_open(dev, &libusb_dev_handle) != 0) + { + continue; + } + + serial_number_len = libusb_get_string_descriptor_ascii(libusb_dev_handle, + serial_descriptor_index, + serial_number, + sizeof(serial_number)); + + if (serial_number_len == SERIAL_AIRSPY_EXPECTED_SIZE) + { + char *start, *end; + uint64_t serial; + + serial_number[SERIAL_AIRSPY_EXPECTED_SIZE] = 0; + start = (char*)(serial_number + STR_PREFIX_SERIAL_AIRSPY_SIZE); + end = NULL; + serial = strtoull(start, &end, 16); + if (serial == 0 && start == end) + { + libusb_close(libusb_dev_handle); + continue; + } + + if (serials) + { + serials[output_count] = serial; + } + output_count++; + } + + libusb_close(libusb_dev_handle); + } + } + } + + libusb_free_device_list(devices, 1); + libusb_exit(context); + return output_count; +} + + int ADDCALL airspy_open_sn(airspy_device_t** device, uint64_t serial_number) + { + int result; + + result = airspy_open_init(device, serial_number, FILE_DESCRIPTOR_UNUSED); + return result; + } + + int ADDCALL airspy_open_fd(airspy_device_t** device, int fd) + { + int result; + + result = airspy_open_init(device, SERIAL_NUMBER_UNUSED, fd); + return result; + } + + int ADDCALL airspy_open(airspy_device_t** device) + { + int result; + + result = airspy_open_init(device, SERIAL_NUMBER_UNUSED, FILE_DESCRIPTOR_UNUSED); + return result; + } + + int ADDCALL airspy_close(airspy_device_t* device) + { + int result; + + result = AIRSPY_SUCCESS; + + if (device != NULL) + { + result = airspy_stop_rx(device); + + iqconverter_float_free(device->cnv_f); + iqconverter_int16_free(device->cnv_i); + + pthread_cond_destroy(&device->consumer_cv); + pthread_mutex_destroy(&device->consumer_mp); + + free_transfers(device); + airspy_open_exit(device); + free(device->supported_samplerates); + free(device); + } + + return result; + } + + int ADDCALL airspy_get_samplerates(struct airspy_device* device, uint32_t* buffer, const uint32_t len) + { + uint32_t i; + + if (len == 0) + { + *buffer = device->supported_samplerate_count; + } + else if (len <= device->supported_samplerate_count) + { + memcpy(buffer, device->supported_samplerates, len * sizeof(uint32_t)); + + if (!SAMPLE_TYPE_IS_IQ(device->sample_type)) + { + for (i = 0; i < len; i++) + { + buffer[i] *= 2; + } + } + } + else + { + return AIRSPY_ERROR_INVALID_PARAM; + } + + return AIRSPY_SUCCESS; + } + + int ADDCALL airspy_set_samplerate(airspy_device_t* device, uint32_t samplerate) + { + int result; + uint8_t retval; + uint8_t length; + uint32_t i; + + if (samplerate >= MIN_SAMPLERATE_BY_VALUE) + { + for (i = 0; i < device->supported_samplerate_count; i++) + { + if (samplerate == device->supported_samplerates[i]) + { + samplerate = i; + break; + } + } + + if (samplerate >= MIN_SAMPLERATE_BY_VALUE) + { + if (SAMPLE_TYPE_IS_IQ(device->sample_type)) + { + samplerate *= 2; + } + samplerate /= 1000; + } + } + + libusb_clear_halt(device->usb_device, LIBUSB_ENDPOINT_IN | 1); + + length = 1; + + result = libusb_control_transfer( + device->usb_device, + LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, + AIRSPY_SET_SAMPLERATE, + 0, + samplerate, + &retval, + length, + LIBUSB_CTRL_TIMEOUT_MS); + + if (result < length) + { + return AIRSPY_ERROR_LIBUSB; + } + else { + return AIRSPY_SUCCESS; + } + } + + int ADDCALL airspy_set_receiver_mode(airspy_device_t* device, receiver_mode_t value) + { + int result; + result = libusb_control_transfer( + device->usb_device, + LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, + AIRSPY_RECEIVER_MODE, + value, + 0, + NULL, + 0, + LIBUSB_CTRL_TIMEOUT_MS); + + if (result != 0) + { + return AIRSPY_ERROR_LIBUSB; + } + else { + return AIRSPY_SUCCESS; + } + } + + int ADDCALL airspy_start_rx(airspy_device_t* device, airspy_sample_block_cb_fn callback, void* ctx) + { + int result; + + iqconverter_float_reset(device->cnv_f); + iqconverter_int16_reset(device->cnv_i); + + memset(device->dropped_buffers_queue, 0, RAW_BUFFER_COUNT * sizeof(uint32_t)); + device->dropped_buffers = 0; + + result = airspy_set_receiver_mode(device, RECEIVER_MODE_OFF); + if (result != AIRSPY_SUCCESS) + { + return result; + } + + libusb_clear_halt(device->usb_device, LIBUSB_ENDPOINT_IN | 1); + + result = airspy_set_receiver_mode(device, RECEIVER_MODE_RX); + if (result == AIRSPY_SUCCESS) + { + device->ctx = ctx; + result = create_io_threads(device, callback); + } + + return result; + } + + int ADDCALL airspy_stop_rx(airspy_device_t* device) + { + int result1, result2; + + device->stop_requested = true; + result1 = airspy_set_receiver_mode(device, RECEIVER_MODE_OFF); + result2 = kill_io_threads(device); + + if (result1 != AIRSPY_SUCCESS) + { + return result1; + } + return result2; + } + + int ADDCALL airspy_si5351c_read(airspy_device_t* device, uint8_t register_number, uint8_t* value) + { + uint8_t temp_value; + int result; + + temp_value = 0; + result = libusb_control_transfer( + device->usb_device, + LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, + AIRSPY_SI5351C_READ, + 0, + register_number, + (unsigned char*)&temp_value, + 1, + LIBUSB_CTRL_TIMEOUT_MS); + + if (result < 1) + { + return AIRSPY_ERROR_LIBUSB; + } + else { + *value = temp_value; + return AIRSPY_SUCCESS; + } + } + + int ADDCALL airspy_si5351c_write(airspy_device_t* device, uint8_t register_number, uint8_t value) + { + int result; + + result = libusb_control_transfer( + device->usb_device, + LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, + AIRSPY_SI5351C_WRITE, + value, + register_number, + NULL, + 0, + LIBUSB_CTRL_TIMEOUT_MS); + + if (result != 0) + { + return AIRSPY_ERROR_LIBUSB; + } + else { + return AIRSPY_SUCCESS; + } + } + + int ADDCALL airspy_r820t_read(airspy_device_t* device, uint8_t register_number, uint8_t* value) + { + int result; + + result = libusb_control_transfer( + device->usb_device, + LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, + AIRSPY_R820T_READ, + 0, + register_number, + (unsigned char*)value, + 1, + LIBUSB_CTRL_TIMEOUT_MS); + + if (result < 1) + { + return AIRSPY_ERROR_LIBUSB; + } + else { + return AIRSPY_SUCCESS; + } + } + + int ADDCALL airspy_r820t_write(airspy_device_t* device, uint8_t register_number, uint8_t value) + { + int result; + + result = libusb_control_transfer( + device->usb_device, + LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, + AIRSPY_R820T_WRITE, + value, + register_number, + NULL, + 0, + LIBUSB_CTRL_TIMEOUT_MS); + + if (result != 0) + { + return AIRSPY_ERROR_LIBUSB; + } + else { + return AIRSPY_SUCCESS; + } + } + + int ADDCALL airspy_gpio_read(airspy_device_t* device, airspy_gpio_port_t port, airspy_gpio_pin_t pin, uint8_t* value) + { + int result; + uint8_t port_pin; + + port_pin = ((uint8_t)port) << 5; + port_pin = port_pin | pin; + + result = libusb_control_transfer( + device->usb_device, + LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, + AIRSPY_GPIO_READ, + 0, + port_pin, + (unsigned char*)value, + 1, + LIBUSB_CTRL_TIMEOUT_MS); + + if (result < 1) + { + return AIRSPY_ERROR_LIBUSB; + } + else { + return AIRSPY_SUCCESS; + } + } + + int ADDCALL airspy_gpio_write(airspy_device_t* device, airspy_gpio_port_t port, airspy_gpio_pin_t pin, uint8_t value) + { + int result; + uint8_t port_pin; + + port_pin = ((uint8_t)port) << 5; + port_pin = port_pin | pin; + + result = libusb_control_transfer( + device->usb_device, + LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, + AIRSPY_GPIO_WRITE, + value, + port_pin, + NULL, + 0, + LIBUSB_CTRL_TIMEOUT_MS); + + if (result != 0) + { + return AIRSPY_ERROR_LIBUSB; + } + else { + return AIRSPY_SUCCESS; + } + } + + + int ADDCALL airspy_gpiodir_read(airspy_device_t* device, airspy_gpio_port_t port, airspy_gpio_pin_t pin, uint8_t* value) + { + int result; + uint8_t port_pin; + + port_pin = ((uint8_t)port) << 5; + port_pin = port_pin | pin; + + result = libusb_control_transfer( + device->usb_device, + LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, + AIRSPY_GPIODIR_READ, + 0, + port_pin, + (unsigned char*)value, + 1, + LIBUSB_CTRL_TIMEOUT_MS); + + if (result < 1) + { + return AIRSPY_ERROR_LIBUSB; + } + else { + return AIRSPY_SUCCESS; + } + } + + int ADDCALL airspy_gpiodir_write(airspy_device_t* device, airspy_gpio_port_t port, airspy_gpio_pin_t pin, uint8_t value) + { + int result; + uint8_t port_pin; + + port_pin = ((uint8_t)port) << 5; + port_pin = port_pin | pin; + + result = libusb_control_transfer( + device->usb_device, + LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, + AIRSPY_GPIODIR_WRITE, + value, + port_pin, + NULL, + 0, + LIBUSB_CTRL_TIMEOUT_MS); + + if (result != 0) + { + return AIRSPY_ERROR_LIBUSB; + } + else { + return AIRSPY_SUCCESS; + } + } + + int ADDCALL airspy_spiflash_erase(airspy_device_t* device) + { + int result; + result = libusb_control_transfer( + device->usb_device, + LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, + AIRSPY_SPIFLASH_ERASE, + 0, + 0, + NULL, + 0, + LIBUSB_CTRL_TIMEOUT_MS); + + if (result != 0) + { + return AIRSPY_ERROR_LIBUSB; + } + else { + return AIRSPY_SUCCESS; + } + } + + int ADDCALL airspy_spiflash_erase_sector(airspy_device_t* device, const uint16_t sector_num) + { + int result; + result = libusb_control_transfer( + device->usb_device, + LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, + AIRSPY_SPIFLASH_ERASE_SECTOR, + sector_num, + 0, + NULL, + 0, + LIBUSB_CTRL_TIMEOUT_MS); + + if (result != 0) + { + return AIRSPY_ERROR_LIBUSB; + } + else { + return AIRSPY_SUCCESS; + } + } + + int ADDCALL airspy_spiflash_write(airspy_device_t* device, const uint32_t address, const uint16_t length, unsigned char* const data) + { + int result; + + if (address > 0x0FFFFF) + { + return AIRSPY_ERROR_INVALID_PARAM; + } + + result = libusb_control_transfer( + device->usb_device, + LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, + AIRSPY_SPIFLASH_WRITE, + address >> 16, + address & 0xFFFF, + data, + length, + LIBUSB_CTRL_TIMEOUT_MS); + + if (result < length) + { + return AIRSPY_ERROR_LIBUSB; + } + else { + return AIRSPY_SUCCESS; + } + } + + int ADDCALL airspy_spiflash_read(airspy_device_t* device, const uint32_t address, const uint16_t length, unsigned char* data) + { + int result; + + result = libusb_control_transfer( + device->usb_device, + LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, + AIRSPY_SPIFLASH_READ, + address >> 16, + address & 0xFFFF, + data, + length, + LIBUSB_CTRL_TIMEOUT_MS); + + if (result < length) + { + return AIRSPY_ERROR_LIBUSB; + } + else { + return AIRSPY_SUCCESS; + } + } + + int ADDCALL airspy_board_id_read(airspy_device_t* device, uint8_t* value) + { + int result; + result = libusb_control_transfer( + device->usb_device, + LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, + AIRSPY_BOARD_ID_READ, + 0, + 0, + value, + 1, + LIBUSB_CTRL_TIMEOUT_MS); + + if (result < 1) + { + return AIRSPY_ERROR_LIBUSB; + } + else { + return AIRSPY_SUCCESS; + } + } + + int ADDCALL airspy_version_string_read(airspy_device_t* device, char* version, uint8_t length) + { +#define VERSION_LOCAL_SIZE (128) + int result; + char version_local[VERSION_LOCAL_SIZE] = ""; + + result = libusb_control_transfer( + device->usb_device, + LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, + AIRSPY_VERSION_STRING_READ, + 0, + 0, + (unsigned char*)version_local, + (VERSION_LOCAL_SIZE - 1), + LIBUSB_CTRL_TIMEOUT_MS); + + if (result < 0) + { + return AIRSPY_ERROR_LIBUSB; + } + else + { + if (length > 0) + { + const int num_bytes_to_copy = (length > VERSION_LOCAL_SIZE ? VERSION_LOCAL_SIZE : length) - 1; + memcpy(version, version_local, num_bytes_to_copy); + version[num_bytes_to_copy] = 0; + return AIRSPY_SUCCESS; + } + else + { + return AIRSPY_ERROR_INVALID_PARAM; + } + } + } + + int ADDCALL airspy_board_partid_serialno_read(airspy_device_t* device, airspy_read_partid_serialno_t* read_partid_serialno) + { + uint8_t length; + int result; + + length = sizeof(airspy_read_partid_serialno_t); + result = libusb_control_transfer( + device->usb_device, + LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, + AIRSPY_BOARD_PARTID_SERIALNO_READ, + 0, + 0, + (unsigned char*)read_partid_serialno, + length, + LIBUSB_CTRL_TIMEOUT_MS); + + if (result < length) + { + return AIRSPY_ERROR_LIBUSB; + } + else { + + read_partid_serialno->part_id[0] = TO_LE(read_partid_serialno->part_id[0]); + read_partid_serialno->part_id[1] = TO_LE(read_partid_serialno->part_id[1]); + read_partid_serialno->serial_no[0] = TO_LE(read_partid_serialno->serial_no[0]); + read_partid_serialno->serial_no[1] = TO_LE(read_partid_serialno->serial_no[1]); + read_partid_serialno->serial_no[2] = TO_LE(read_partid_serialno->serial_no[2]); + read_partid_serialno->serial_no[3] = TO_LE(read_partid_serialno->serial_no[3]); + + return AIRSPY_SUCCESS; + } + } + + int ADDCALL airspy_set_sample_type(struct airspy_device* device, enum airspy_sample_type sample_type) + { + device->sample_type = sample_type; + return AIRSPY_SUCCESS; + } + + int ADDCALL airspy_set_freq(airspy_device_t* device, const uint32_t freq_hz) + { + set_freq_params_t set_freq_params; + uint8_t length; + int result; + + set_freq_params.freq_hz = TO_LE(freq_hz); + length = sizeof(set_freq_params_t); + + result = libusb_control_transfer( + device->usb_device, + LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, + AIRSPY_SET_FREQ, + 0, + 0, + (unsigned char*)&set_freq_params, + length, + LIBUSB_CTRL_TIMEOUT_MS); + + if (result < length) + { + return AIRSPY_ERROR_LIBUSB; + } + else { + return AIRSPY_SUCCESS; + } + } + + int ADDCALL airspy_set_conversion_filter_float32(struct airspy_device* device, const float *kernel, const uint32_t len) + { + if (device->streaming) + { + return AIRSPY_ERROR_BUSY; + } + + iqconverter_float_free(device->cnv_f); + device->cnv_f = iqconverter_float_create(kernel, len); + + return AIRSPY_SUCCESS; + } + + int ADDCALL airspy_set_conversion_filter_int16(struct airspy_device* device, const int16_t *kernel, const uint32_t len) + { + if (device->streaming) + { + return AIRSPY_ERROR_BUSY; + } + + iqconverter_int16_free(device->cnv_i); + device->cnv_i = iqconverter_int16_create(kernel, len); + + return AIRSPY_SUCCESS; + } + + int ADDCALL airspy_set_lna_gain(airspy_device_t* device, uint8_t value) + { + int result; + uint8_t retval; + uint8_t length; + + if (value > 14) + value = 14; + + length = 1; + + result = libusb_control_transfer( + device->usb_device, + LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, + AIRSPY_SET_LNA_GAIN, + 0, + value, + &retval, + length, + LIBUSB_CTRL_TIMEOUT_MS); + + if (result < length) + { + return AIRSPY_ERROR_LIBUSB; + } + else { + return AIRSPY_SUCCESS; + } + } + + int ADDCALL airspy_set_mixer_gain(airspy_device_t* device, uint8_t value) + { + int result; + uint8_t retval; + uint8_t length; + + if (value > 15) + value = 15; + + length = 1; + + result = libusb_control_transfer( + device->usb_device, + LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, + AIRSPY_SET_MIXER_GAIN, + 0, + value, + &retval, + length, + LIBUSB_CTRL_TIMEOUT_MS); + + if (result < length) + { + return AIRSPY_ERROR_LIBUSB; + } + else { + return AIRSPY_SUCCESS; + } + } + + int ADDCALL airspy_set_vga_gain(airspy_device_t* device, uint8_t value) + { + int result; + uint8_t retval; + uint8_t length; + + if (value > 15) + value = 15; + + length = 1; + + result = libusb_control_transfer( + device->usb_device, + LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, + AIRSPY_SET_VGA_GAIN, + 0, + value, + &retval, + length, + LIBUSB_CTRL_TIMEOUT_MS); + + if (result < length) + { + return AIRSPY_ERROR_LIBUSB; + } + else { + return AIRSPY_SUCCESS; + } + } + + int ADDCALL airspy_set_lna_agc(airspy_device_t* device, uint8_t value) + { + int result; + uint8_t retval; + uint8_t length; + + length = 1; + + result = libusb_control_transfer( + device->usb_device, + LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, + AIRSPY_SET_LNA_AGC, + 0, + value, + &retval, + length, + LIBUSB_CTRL_TIMEOUT_MS); + + if (result < length) + { + return AIRSPY_ERROR_LIBUSB; + } + else { + return AIRSPY_SUCCESS; + } + } + + int ADDCALL airspy_set_mixer_agc(airspy_device_t* device, uint8_t value) + { + int result; + uint8_t retval; + uint8_t length; + + length = 1; + + result = libusb_control_transfer( + device->usb_device, + LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, + AIRSPY_SET_MIXER_AGC, + 0, + value, + &retval, + length, + LIBUSB_CTRL_TIMEOUT_MS); + + if (result < length) + { + return AIRSPY_ERROR_LIBUSB; + } + else { + return AIRSPY_SUCCESS; + } + } + + int ADDCALL airspy_set_linearity_gain(struct airspy_device* device, uint8_t value) + { + int rc; + + if (value >= GAIN_COUNT) + { + value = GAIN_COUNT - 1; + } + + value = GAIN_COUNT - 1 - value; + + rc = airspy_set_mixer_agc(device, 0); + if (rc < 0) + return rc; + + rc = airspy_set_lna_agc(device, 0); + if (rc < 0) + return rc; + + rc = airspy_set_vga_gain(device, airspy_linearity_vga_gains[value]); + if (rc < 0) + return rc; + + rc = airspy_set_mixer_gain(device, airspy_linearity_mixer_gains[value]); + if (rc < 0) + return rc; + + rc = airspy_set_lna_gain(device, airspy_linearity_lna_gains[value]); + if (rc < 0) + return rc; + + return AIRSPY_SUCCESS; + } + + int ADDCALL airspy_set_sensitivity_gain(struct airspy_device* device, uint8_t value) + { + int rc; + + if (value >= GAIN_COUNT) + { + value = GAIN_COUNT - 1; + } + + value = GAIN_COUNT - 1 - value; + + rc = airspy_set_mixer_agc(device, 0); + if (rc < 0) + return rc; + + rc = airspy_set_lna_agc(device, 0); + if (rc < 0) + return rc; + + rc = airspy_set_vga_gain(device, airspy_sensitivity_vga_gains[value]); + if (rc < 0) + return rc; + + rc = airspy_set_mixer_gain(device, airspy_sensitivity_mixer_gains[value]); + if (rc < 0) + return rc; + + rc = airspy_set_lna_gain(device, airspy_sensitivity_lna_gains[value]); + if (rc < 0) + return rc; + + return AIRSPY_SUCCESS; + } + + int ADDCALL airspy_set_rf_bias(airspy_device_t* device, uint8_t value) + { + return airspy_gpio_write(device, GPIO_PORT1, GPIO_PIN13, value); + } + + int ADDCALL airspy_set_packing(airspy_device_t* device, uint8_t value) + { + int result; + uint8_t retval; + bool packing_enabled; + + if (device->streaming) + { + return AIRSPY_ERROR_BUSY; + } + + result = libusb_control_transfer( + device->usb_device, + LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, + AIRSPY_SET_PACKING, + 0, + value, + &retval, + 1, + LIBUSB_CTRL_TIMEOUT_MS); + + if (result < 1) + { + return AIRSPY_ERROR_LIBUSB; + } + + packing_enabled = value ? true : false; + if (packing_enabled != device->packing_enabled) + { + cancel_transfers(device); + free_transfers(device); + + device->packing_enabled = packing_enabled; + device->buffer_size = packing_enabled ? (6144 * 24) : 262144; + + result = allocate_transfers(device); + if (result != 0) + { + return AIRSPY_ERROR_NO_MEM; + } + } + + return AIRSPY_SUCCESS; + } + + int ADDCALL airspy_is_streaming(airspy_device_t* device) + { + return (device->streaming == true && device->stop_requested == false); + } + + const char* ADDCALL airspy_error_name(enum airspy_error errcode) + { + switch (errcode) + { + case AIRSPY_SUCCESS: + return "AIRSPY_SUCCESS"; + + case AIRSPY_TRUE: + return "AIRSPY_TRUE"; + + case AIRSPY_ERROR_INVALID_PARAM: + return "AIRSPY_ERROR_INVALID_PARAM"; + + case AIRSPY_ERROR_NOT_FOUND: + return "AIRSPY_ERROR_NOT_FOUND"; + + case AIRSPY_ERROR_BUSY: + return "AIRSPY_ERROR_BUSY"; + + case AIRSPY_ERROR_NO_MEM: + return "AIRSPY_ERROR_NO_MEM"; + + case AIRSPY_ERROR_LIBUSB: + return "AIRSPY_ERROR_LIBUSB"; + + case AIRSPY_ERROR_THREAD: + return "AIRSPY_ERROR_THREAD"; + + case AIRSPY_ERROR_STREAMING_THREAD_ERR: + return "AIRSPY_ERROR_STREAMING_THREAD_ERR"; + + case AIRSPY_ERROR_STREAMING_STOPPED: + return "AIRSPY_ERROR_STREAMING_STOPPED"; + + case AIRSPY_ERROR_OTHER: + return "AIRSPY_ERROR_OTHER"; + + default: + return "airspy unknown error"; + } + } + + const char* ADDCALL airspy_board_id_name(enum airspy_board_id board_id) + { + switch (board_id) + { + case AIRSPY_BOARD_ID_PROTO_AIRSPY: + return "AIRSPY"; + + case AIRSPY_BOARD_ID_INVALID: + return "Invalid Board ID"; + + default: + return "Unknown Board ID"; + } + } + +#ifdef __cplusplus +} // __cplusplus defined. +#endif diff --git a/Radio/HW/AirSpy/src/airspy.h b/Radio/HW/AirSpy/src/airspy.h new file mode 100644 index 0000000..573298b --- /dev/null +++ b/Radio/HW/AirSpy/src/airspy.h @@ -0,0 +1,224 @@ +/* +Copyright (c) 2012, Jared Boone <jared@sharebrained.com> +Copyright (c) 2013, Michael Ossmann <mike@ossmann.com> +Copyright (c) 2013-2016, Benjamin Vernoux <bvernoux@airspy.com> +Copyright (C) 2013-2016, Youssef Touil <youssef@airspy.com> + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + Neither the name of AirSpy nor the names of its contributors may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef __AIRSPY_H__ +#define __AIRSPY_H__ + +#include <stdint.h> +#include "airspy_commands.h" + +#define AIRSPY_VERSION "1.0.12" +#define AIRSPY_VER_MAJOR 1 +#define AIRSPY_VER_MINOR 0 +#define AIRSPY_VER_REVISION 12 + +#ifdef _WIN32 + #define ADD_EXPORTS + + /* You should define ADD_EXPORTS *only* when building the DLL. */ + #ifdef ADD_EXPORTS + #define ADDAPI __declspec(dllexport) + #else + #define ADDAPI __declspec(dllimport) + #endif + + /* Define calling convention in one place, for convenience. */ + #define ADDCALL __cdecl + +#else /* _WIN32 not defined. */ + + /* Define with no value on non-Windows OSes. */ + #define ADDAPI + #define ADDCALL + +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + +enum airspy_error +{ + AIRSPY_SUCCESS = 0, + AIRSPY_TRUE = 1, + AIRSPY_ERROR_INVALID_PARAM = -2, + AIRSPY_ERROR_NOT_FOUND = -5, + AIRSPY_ERROR_BUSY = -6, + AIRSPY_ERROR_NO_MEM = -11, + AIRSPY_ERROR_UNSUPPORTED = -12, + AIRSPY_ERROR_LIBUSB = -1000, + AIRSPY_ERROR_THREAD = -1001, + AIRSPY_ERROR_STREAMING_THREAD_ERR = -1002, + AIRSPY_ERROR_STREAMING_STOPPED = -1003, + AIRSPY_ERROR_OTHER = -9999, +}; + +enum airspy_board_id +{ + AIRSPY_BOARD_ID_PROTO_AIRSPY = 0, + AIRSPY_BOARD_ID_INVALID = 0xFF, +}; + +enum airspy_sample_type +{ + AIRSPY_SAMPLE_FLOAT32_IQ = 0, /* 2 * 32bit float per sample */ + AIRSPY_SAMPLE_FLOAT32_REAL = 1, /* 1 * 32bit float per sample */ + AIRSPY_SAMPLE_INT16_IQ = 2, /* 2 * 16bit int per sample */ + AIRSPY_SAMPLE_INT16_REAL = 3, /* 1 * 16bit int per sample */ + AIRSPY_SAMPLE_UINT16_REAL = 4, /* 1 * 16bit unsigned int per sample */ + AIRSPY_SAMPLE_RAW = 5, /* Raw packed samples from the device */ + AIRSPY_SAMPLE_END = 6 /* Number of supported sample types */ +}; + +#define MAX_CONFIG_PAGE_SIZE (0x10000) + +struct airspy_device; + +typedef struct { + struct airspy_device* device; + void* ctx; + void* samples; + int sample_count; + uint64_t dropped_samples; + enum airspy_sample_type sample_type; +} airspy_transfer_t, airspy_transfer; + +typedef struct { + uint32_t part_id[2]; + uint32_t serial_no[4]; +} airspy_read_partid_serialno_t; + +typedef struct { + uint32_t major_version; + uint32_t minor_version; + uint32_t revision; +} airspy_lib_version_t; + +typedef int (*airspy_sample_block_cb_fn)(airspy_transfer* transfer); + +extern ADDAPI void ADDCALL airspy_lib_version(airspy_lib_version_t* lib_version); +/* airspy_init() deprecated */ +extern ADDAPI int ADDCALL airspy_init(void); +/* airspy_exit() deprecated */ +extern ADDAPI int ADDCALL airspy_exit(void); + +extern ADDAPI int ADDCALL airspy_list_devices(uint64_t *serials, int count); + +extern ADDAPI int ADDCALL airspy_open_sn(struct airspy_device** device, uint64_t serial_number); +extern ADDAPI int ADDCALL airspy_open_fd(struct airspy_device** device, int fd); +extern ADDAPI int ADDCALL airspy_open(struct airspy_device** device); +extern ADDAPI int ADDCALL airspy_close(struct airspy_device* device); + +/* Use airspy_get_samplerates(device, buffer, 0) to get the number of available sample rates. It will be returned in the first element of buffer */ +extern ADDAPI int ADDCALL airspy_get_samplerates(struct airspy_device* device, uint32_t* buffer, const uint32_t len); + +/* Parameter samplerate can be either the index of a samplerate or directly its value in Hz within the list returned by airspy_get_samplerates() */ +extern ADDAPI int ADDCALL airspy_set_samplerate(struct airspy_device* device, uint32_t samplerate); + +extern ADDAPI int ADDCALL airspy_set_conversion_filter_float32(struct airspy_device* device, const float *kernel, const uint32_t len); +extern ADDAPI int ADDCALL airspy_set_conversion_filter_int16(struct airspy_device* device, const int16_t *kernel, const uint32_t len); + +extern ADDAPI int ADDCALL airspy_start_rx(struct airspy_device* device, airspy_sample_block_cb_fn callback, void* rx_ctx); +extern ADDAPI int ADDCALL airspy_stop_rx(struct airspy_device* device); + +/* return AIRSPY_TRUE if success */ +extern ADDAPI int ADDCALL airspy_is_streaming(struct airspy_device* device); + +extern ADDAPI int ADDCALL airspy_si5351c_write(struct airspy_device* device, uint8_t register_number, uint8_t value); +extern ADDAPI int ADDCALL airspy_si5351c_read(struct airspy_device* device, uint8_t register_number, uint8_t* value); + +extern ADDAPI int ADDCALL airspy_config_write(struct airspy_device* device, const uint8_t page_index, const uint16_t length, unsigned char *data); +extern ADDAPI int ADDCALL airspy_config_read(struct airspy_device* device, const uint8_t page_index, const uint16_t length, unsigned char *data); + +extern ADDAPI int ADDCALL airspy_r820t_write(struct airspy_device* device, uint8_t register_number, uint8_t value); +extern ADDAPI int ADDCALL airspy_r820t_read(struct airspy_device* device, uint8_t register_number, uint8_t* value); + +/* Parameter value shall be 0=clear GPIO or 1=set GPIO */ +extern ADDAPI int ADDCALL airspy_gpio_write(struct airspy_device* device, airspy_gpio_port_t port, airspy_gpio_pin_t pin, uint8_t value); +/* Parameter value corresponds to GPIO state 0 or 1 */ +extern ADDAPI int ADDCALL airspy_gpio_read(struct airspy_device* device, airspy_gpio_port_t port, airspy_gpio_pin_t pin, uint8_t* value); + +/* Parameter value shall be 0=GPIO Input direction or 1=GPIO Output direction */ +extern ADDAPI int ADDCALL airspy_gpiodir_write(struct airspy_device* device, airspy_gpio_port_t port, airspy_gpio_pin_t pin, uint8_t value); +extern ADDAPI int ADDCALL airspy_gpiodir_read(struct airspy_device* device, airspy_gpio_port_t port, airspy_gpio_pin_t pin, uint8_t* value); + +extern ADDAPI int ADDCALL airspy_spiflash_erase(struct airspy_device* device); +extern ADDAPI int ADDCALL airspy_spiflash_write(struct airspy_device* device, const uint32_t address, const uint16_t length, unsigned char* const data); +extern ADDAPI int ADDCALL airspy_spiflash_read(struct airspy_device* device, const uint32_t address, const uint16_t length, unsigned char* data); + +extern ADDAPI int ADDCALL airspy_board_id_read(struct airspy_device* device, uint8_t* value); +/* Parameter length shall be at least 128bytes to avoid possible string clipping */ +extern ADDAPI int ADDCALL airspy_version_string_read(struct airspy_device* device, char* version, uint8_t length); + +extern ADDAPI int ADDCALL airspy_board_partid_serialno_read(struct airspy_device* device, airspy_read_partid_serialno_t* read_partid_serialno); + +extern ADDAPI int ADDCALL airspy_set_sample_type(struct airspy_device* device, enum airspy_sample_type sample_type); + +/* Parameter freq_hz shall be between 24000000(24MHz) and 1750000000(1.75GHz) */ +extern ADDAPI int ADDCALL airspy_set_freq(struct airspy_device* device, const uint32_t freq_hz); + +/* Parameter value shall be between 0 and 15 */ +extern ADDAPI int ADDCALL airspy_set_lna_gain(struct airspy_device* device, uint8_t value); + +/* Parameter value shall be between 0 and 15 */ +extern ADDAPI int ADDCALL airspy_set_mixer_gain(struct airspy_device* device, uint8_t value); + +/* Parameter value shall be between 0 and 15 */ +extern ADDAPI int ADDCALL airspy_set_vga_gain(struct airspy_device* device, uint8_t value); + +/* Parameter value: + 0=Disable LNA Automatic Gain Control + 1=Enable LNA Automatic Gain Control +*/ +extern ADDAPI int ADDCALL airspy_set_lna_agc(struct airspy_device* device, uint8_t value); +/* Parameter value: + 0=Disable MIXER Automatic Gain Control + 1=Enable MIXER Automatic Gain Control +*/ +extern ADDAPI int ADDCALL airspy_set_mixer_agc(struct airspy_device* device, uint8_t value); + +/* Parameter value: 0..21 */ +extern ADDAPI int ADDCALL airspy_set_linearity_gain(struct airspy_device* device, uint8_t value); + +/* Parameter value: 0..21 */ +extern ADDAPI int ADDCALL airspy_set_sensitivity_gain(struct airspy_device* device, uint8_t value); + +/* Parameter value shall be 0=Disable BiasT or 1=Enable BiasT */ +extern ADDAPI int ADDCALL airspy_set_rf_bias(struct airspy_device* dev, uint8_t value); + +/* Parameter value shall be 0=Disable Packing or 1=Enable Packing */ +extern ADDAPI int ADDCALL airspy_set_packing(struct airspy_device* device, uint8_t value); + +extern ADDAPI const char* ADDCALL airspy_error_name(enum airspy_error errcode); +extern ADDAPI const char* ADDCALL airspy_board_id_name(enum airspy_board_id board_id); + +/* Parameter sector_num shall be between 2 & 13 (sector 0 & 1 are reserved) */ +extern ADDAPI int ADDCALL airspy_spiflash_erase_sector(struct airspy_device* device, const uint16_t sector_num); + +#ifdef __cplusplus +} // __cplusplus defined. +#endif + +#endif//__AIRSPY_H__ diff --git a/Radio/HW/AirSpy/src/airspy_commands.h b/Radio/HW/AirSpy/src/airspy_commands.h new file mode 100644 index 0000000..21194f8 --- /dev/null +++ b/Radio/HW/AirSpy/src/airspy_commands.h @@ -0,0 +1,145 @@ +/* +Copyright (c) 2013-2016, Benjamin Vernoux <bvernoux@airspy.com> + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + Neither the name of AirSpy nor the names of its contributors may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef __AIRSPY_COMMANDS_H__ +#define __AIRSPY_COMMANDS_H__ + +#include <stdint.h> + +#ifdef __cplusplus +extern "C" +{ +#endif + +typedef enum +{ + RECEIVER_MODE_OFF = 0, + RECEIVER_MODE_RX = 1 +} receiver_mode_t; + +/* + Note: airspy_samplerate_t is now obsolete and left for backward compatibility. + The list of supported sample rates should be retrieved at run time by calling airspy_get_samplerates(). + Refer to the Airspy Tools for illustrations. +*/ +typedef enum +{ + AIRSPY_SAMPLERATE_10MSPS = 0, /* 12bits 10MHz IQ */ + AIRSPY_SAMPLERATE_2_5MSPS = 1, /* 12bits 2.5MHz IQ */ + AIRSPY_SAMPLERATE_END = 2 /* End index for sample rate (corresponds to number of samplerate) */ +} airspy_samplerate_t; + + +#define AIRSPY_CONF_CMD_SHIFT_BIT (3) // Up to 3bits=8 samplerates (airspy_samplerate_t enum shall not exceed 7) + +// Commands (usb vendor request) shared between Firmware and Host. +#define AIRSPY_CMD_MAX (27) +typedef enum +{ + AIRSPY_INVALID = 0 , + AIRSPY_RECEIVER_MODE = 1 , + AIRSPY_SI5351C_WRITE = 2 , + AIRSPY_SI5351C_READ = 3 , + AIRSPY_R820T_WRITE = 4 , + AIRSPY_R820T_READ = 5 , + AIRSPY_SPIFLASH_ERASE = 6 , + AIRSPY_SPIFLASH_WRITE = 7 , + AIRSPY_SPIFLASH_READ = 8 , + AIRSPY_BOARD_ID_READ = 9 , + AIRSPY_VERSION_STRING_READ = 10, + AIRSPY_BOARD_PARTID_SERIALNO_READ = 11, + AIRSPY_SET_SAMPLERATE = 12, + AIRSPY_SET_FREQ = 13, + AIRSPY_SET_LNA_GAIN = 14, + AIRSPY_SET_MIXER_GAIN = 15, + AIRSPY_SET_VGA_GAIN = 16, + AIRSPY_SET_LNA_AGC = 17, + AIRSPY_SET_MIXER_AGC = 18, + AIRSPY_MS_VENDOR_CMD = 19, + AIRSPY_SET_RF_BIAS_CMD = 20, + AIRSPY_GPIO_WRITE = 21, + AIRSPY_GPIO_READ = 22, + AIRSPY_GPIODIR_WRITE = 23, + AIRSPY_GPIODIR_READ = 24, + AIRSPY_GET_SAMPLERATES = 25, + AIRSPY_SET_PACKING = 26, + AIRSPY_SPIFLASH_ERASE_SECTOR = AIRSPY_CMD_MAX +} airspy_vendor_request; + +typedef enum +{ + CONFIG_CALIBRATION = 0, + //CONFIG_META = 1, +} airspy_common_config_pages_t; + +typedef enum +{ + GPIO_PORT0 = 0, + GPIO_PORT1 = 1, + GPIO_PORT2 = 2, + GPIO_PORT3 = 3, + GPIO_PORT4 = 4, + GPIO_PORT5 = 5, + GPIO_PORT6 = 6, + GPIO_PORT7 = 7 +} airspy_gpio_port_t; + +typedef enum +{ + GPIO_PIN0 = 0, + GPIO_PIN1 = 1, + GPIO_PIN2 = 2, + GPIO_PIN3 = 3, + GPIO_PIN4 = 4, + GPIO_PIN5 = 5, + GPIO_PIN6 = 6, + GPIO_PIN7 = 7, + GPIO_PIN8 = 8, + GPIO_PIN9 = 9, + GPIO_PIN10 = 10, + GPIO_PIN11 = 11, + GPIO_PIN12 = 12, + GPIO_PIN13 = 13, + GPIO_PIN14 = 14, + GPIO_PIN15 = 15, + GPIO_PIN16 = 16, + GPIO_PIN17 = 17, + GPIO_PIN18 = 18, + GPIO_PIN19 = 19, + GPIO_PIN20 = 20, + GPIO_PIN21 = 21, + GPIO_PIN22 = 22, + GPIO_PIN23 = 23, + GPIO_PIN24 = 24, + GPIO_PIN25 = 25, + GPIO_PIN26 = 26, + GPIO_PIN27 = 27, + GPIO_PIN28 = 28, + GPIO_PIN29 = 29, + GPIO_PIN30 = 30, + GPIO_PIN31 = 31 +} airspy_gpio_pin_t; + +#ifdef __cplusplus +} // __cplusplus defined. +#endif + +#endif//__AIRSPY_COMMANDS_H__ diff --git a/Radio/HW/AirSpy/src/filters.h b/Radio/HW/AirSpy/src/filters.h new file mode 100644 index 0000000..30da6e4 --- /dev/null +++ b/Radio/HW/AirSpy/src/filters.h @@ -0,0 +1,134 @@ +/* +Copyright (C) 2014, Youssef Touil <youssef@airspy.com> + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#ifndef FILTERS_H +#define FILTERS_H + +#include <stdint.h> + +#define HB_KERNEL_FLOAT_LEN 47 + +const float HB_KERNEL_FLOAT[HB_KERNEL_FLOAT_LEN] = +{ + -0.000998606272947510, + 0.000000000000000000, + 0.001695637278417295, + 0.000000000000000000, + -0.003054430179754289, + 0.000000000000000000, + 0.005055504379767936, + 0.000000000000000000, + -0.007901319195893647, + 0.000000000000000000, + 0.011873357051047719, + 0.000000000000000000, + -0.017411159379930066, + 0.000000000000000000, + 0.025304817427568772, + 0.000000000000000000, + -0.037225225204559217, + 0.000000000000000000, + 0.057533286997004301, + 0.000000000000000000, + -0.102327462004259350, + 0.000000000000000000, + 0.317034472508947400, + 0.500000000000000000, + 0.317034472508947400, + 0.000000000000000000, + -0.102327462004259350, + 0.000000000000000000, + 0.057533286997004301, + 0.000000000000000000, + -0.037225225204559217, + 0.000000000000000000, + 0.025304817427568772, + 0.000000000000000000, + -0.017411159379930066, + 0.000000000000000000, + 0.011873357051047719, + 0.000000000000000000, + -0.007901319195893647, + 0.000000000000000000, + 0.005055504379767936, + 0.000000000000000000, + -0.003054430179754289, + 0.000000000000000000, + 0.001695637278417295, + 0.000000000000000000, + -0.000998606272947510 +}; + +#define HB_KERNEL_INT16_LEN 47 + +const int16_t HB_KERNEL_INT16[HB_KERNEL_INT16_LEN] = +{ + -33, + 0, + 56, + 0, + -100, + 0, + 166, + 0, + -259, + 0, + 389, + 0, + -571, + 0, + 829, + 0, + -1220, + 0, + 1885, + 0, + -3353, + 0, + 10389, + 16384, + 10389, + 0, + -3353, + 0, + 1885, + 0, + -1220, + 0, + 829, + 0, + -571, + 0, + 389, + 0, + -259, + 0, + 166, + 0, + -100, + 0, + 56, + 0, + -33 +}; + +#endif // FILTERS_H diff --git a/Radio/HW/AirSpy/src/iqconverter_float.c b/Radio/HW/AirSpy/src/iqconverter_float.c new file mode 100644 index 0000000..c656cdb --- /dev/null +++ b/Radio/HW/AirSpy/src/iqconverter_float.c @@ -0,0 +1,502 @@ +/* +Copyright (C) 2014, Youssef Touil <youssef@airspy.com> + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#include "iqconverter_float.h" +#include <stdlib.h> +#include <string.h> + +#include <stdio.h> + +#if defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR) + #include <malloc.h> + #define _aligned_malloc __mingw_aligned_malloc + #define _aligned_free __mingw_aligned_free + #define _inline inline + #define FIR_STANDARD +#elif defined(__APPLE__) + #include <malloc/malloc.h> + #define _aligned_malloc(size, alignment) malloc(size) + #define _aligned_free(mem) free(mem) + #define _inline inline + #define FIR_STANDARD +#elif defined(__FreeBSD__) + #define USE_SSE2 +#include <immintrin.h> + #define _inline inline + #define _aligned_free(mem) free(mem) +void *_aligned_malloc(size_t size, size_t alignment) +{ + void *result; + if (posix_memalign(&result, alignment, size) == 0) + return result; + return 0; +} +#elif defined(__GNUC__) && !defined(__MINGW64_VERSION_MAJOR) + #include <malloc.h> + #define _aligned_malloc(size, alignment) memalign(alignment, size) + #define _aligned_free(mem) free(mem) + #define _inline inline +#else + #if (_MSC_VER >= 1800) + //#define USE_SSE2 + //#include <immintrin.h> + #endif +#endif + +#define SIZE_FACTOR 32 +#define DEFAULT_ALIGNMENT 16 +#define HPF_COEFF 0.01f + +#if defined(_MSC_VER) + #define ALIGNED __declspec(align(DEFAULT_ALIGNMENT)) +#else + #define ALIGNED +#endif + +iqconverter_float_t *iqconverter_float_create(const float *hb_kernel, int len) +{ + int i, j; + size_t buffer_size; + iqconverter_float_t *cnv = (iqconverter_float_t *) _aligned_malloc(sizeof(iqconverter_float_t), DEFAULT_ALIGNMENT); + + cnv->len = len / 2 + 1; + cnv->hbc = hb_kernel[len / 2]; + + buffer_size = cnv->len * sizeof(float); + + cnv->fir_kernel = (float *) _aligned_malloc(buffer_size, DEFAULT_ALIGNMENT); + cnv->fir_queue = (float *) _aligned_malloc(buffer_size * SIZE_FACTOR, DEFAULT_ALIGNMENT); + cnv->delay_line = (float *) _aligned_malloc(buffer_size / 2, DEFAULT_ALIGNMENT); + + iqconverter_float_reset(cnv); + + for (i = 0, j = 0; i < cnv->len; i++, j += 2) + { + cnv->fir_kernel[i] = hb_kernel[j]; + } + + return cnv; +} + +void iqconverter_float_free(iqconverter_float_t *cnv) +{ + _aligned_free(cnv->fir_kernel); + _aligned_free(cnv->fir_queue); + _aligned_free(cnv->delay_line); + _aligned_free(cnv); +} + +void iqconverter_float_reset(iqconverter_float_t *cnv) +{ + cnv->avg = 0.0f; + cnv->fir_index = 0; + cnv->delay_index = 0; + memset(cnv->delay_line, 0, cnv->len * sizeof(float) / 2); + memset(cnv->fir_queue, 0, cnv->len * sizeof(float) * SIZE_FACTOR); +} + +static _inline float process_fir_taps(const float *kernel, const float *queue, int len) +{ + int i; + +#ifdef USE_SSE2 + + __m128 acc = _mm_set_ps(0, 0, 0, 0); + +#else + + float sum = 0.0f; + +#endif + + if (len >= 8) + { + int it = len >> 3; + +#ifdef USE_SSE2 + + for (i = 0; i < it; i++)
+ {
+ __m128 head1 = _mm_loadu_ps(queue);
+ __m128 kern1 = _mm_load_ps(kernel);
+ __m128 head2 = _mm_loadu_ps(queue + 4);
+ __m128 kern2 = _mm_load_ps(kernel + 4);
+
+ __m128 mul1 = _mm_mul_ps(kern1, head1);
+ __m128 mul2 = _mm_mul_ps(kern2, head2);
+
+ mul1 = _mm_add_ps(mul1, mul2);
+
+ acc = _mm_add_ps(acc, mul1);
+
+ queue += 8;
+ kernel += 8;
+ } + +#else + + for (i = 0; i < it; i++) + { + sum += kernel[0] * queue[0] + + kernel[1] * queue[1] + + kernel[2] * queue[2] + + kernel[3] * queue[3] + + kernel[4] * queue[4] + + kernel[5] * queue[5] + + kernel[6] * queue[6] + + kernel[7] * queue[7];
+
+ queue += 8;
+ kernel += 8; + } + +#endif + len &= 7; + } + + if (len >= 4) + { + +#ifdef USE_SSE2 + + __m128 head = _mm_loadu_ps(queue);
+ __m128 kern = _mm_load_ps(kernel);
+ __m128 mul = _mm_mul_ps(kern, head); + acc = _mm_add_ps(acc, mul); + +#else + + sum += kernel[0] * queue[0] + + kernel[1] * queue[1] + + kernel[2] * queue[2] + + kernel[3] * queue[3]; + +#endif + + kernel += 4; + queue += 4; + len &= 3; + }
+
+#ifdef USE_SSE2 +
+ __m128 t = _mm_add_ps(acc, _mm_movehl_ps(acc, acc));
+ acc = _mm_add_ss(t, _mm_shuffle_ps(t, t, 1)); + +#ifdef __FreeBSD__ + float sum = acc[0]; +#else + float sum = acc.m128_f32[0]; +#endif + +#endif + + if (len >= 2) + { + sum += kernel[0] * queue[0] + + kernel[1] * queue[1]; + + //kernel += 2; + //queue += 2; + //len &= 1; + } + + //if (len >= 1) + //{ + // sum += kernel[0] * queue[0]; + //} + + return sum; +} + +static void fir_interleaved_4(iqconverter_float_t *cnv, float *samples, int len) +{ + int i; + int fir_index = cnv->fir_index; + int fir_len = cnv->len; + float *fir_kernel = cnv->fir_kernel; + float *fir_queue = cnv->fir_queue; + float *queue; + float acc; + + for (i = 0; i < len; i += 2) + { + queue = fir_queue + fir_index; + + queue[0] = samples[i]; + + acc = fir_kernel[0] * (queue[0] + queue[4 - 1]) + + fir_kernel[1] * (queue[1] + queue[4 - 2]); + + samples[i] = acc; + + if (--fir_index < 0) + { + fir_index = fir_len * (SIZE_FACTOR - 1); + memcpy(fir_queue + fir_index + 1, fir_queue, (fir_len - 1) * sizeof(float)); + } + } + + cnv->fir_index = fir_index; +} + +static void fir_interleaved_8(iqconverter_float_t *cnv, float *samples, int len) +{ + int i; + int fir_index = cnv->fir_index; + int fir_len = cnv->len; + float *fir_kernel = cnv->fir_kernel; + float *fir_queue = cnv->fir_queue; + float *queue; + float acc; + + for (i = 0; i < len; i += 2) + { + queue = fir_queue + fir_index; + + queue[0] = samples[i]; + + acc = fir_kernel[0] * (queue[0] + queue[8 - 1]) + + fir_kernel[1] * (queue[1] + queue[8 - 2]) + + fir_kernel[2] * (queue[2] + queue[8 - 3]) + + fir_kernel[3] * (queue[3] + queue[8 - 4]); + + samples[i] = acc; + + if (--fir_index < 0) + { + fir_index = fir_len * (SIZE_FACTOR - 1); + memcpy(fir_queue + fir_index + 1, fir_queue, (fir_len - 1) * sizeof(float)); + } + } + + cnv->fir_index = fir_index; +} + +static void fir_interleaved_12(iqconverter_float_t *cnv, float *samples, int len) +{ + int i; + int fir_index = cnv->fir_index; + int fir_len = cnv->len; + float *fir_kernel = cnv->fir_kernel; + float *fir_queue = cnv->fir_queue; + float *queue; + float acc = 0; + + for (i = 0; i < len; i += 2) + { + queue = fir_queue + fir_index; + + queue[0] = samples[i]; + + acc = fir_kernel[0] * (queue[0] + queue[12 - 1]) + + fir_kernel[1] * (queue[1] + queue[12 - 2]) + + fir_kernel[2] * (queue[2] + queue[12 - 3]) + + fir_kernel[3] * (queue[3] + queue[12 - 4]) + + fir_kernel[4] * (queue[4] + queue[12 - 5]) + + fir_kernel[5] * (queue[5] + queue[12 - 6]); + + samples[i] = acc; + + if (--fir_index < 0) + { + fir_index = fir_len * (SIZE_FACTOR - 1); + memcpy(fir_queue + fir_index + 1, fir_queue, (fir_len - 1) * sizeof(float)); + } + } + + cnv->fir_index = fir_index; +} + +static void fir_interleaved_24(iqconverter_float_t *cnv, float *samples, int len) +{ + int i; + int fir_index = cnv->fir_index; + int fir_len = cnv->len; + float *fir_kernel = cnv->fir_kernel; + float *fir_queue = cnv->fir_queue; + float *queue; + float acc = 0; + + for (i = 0; i < len; i += 2) + { + queue = fir_queue + fir_index; + + queue[0] = samples[i]; + + acc = fir_kernel[0] * (queue[0] + queue[24 - 1]) + + fir_kernel[1] * (queue[1] + queue[24 - 2]) + + fir_kernel[2] * (queue[2] + queue[24 - 3]) + + fir_kernel[3] * (queue[3] + queue[24 - 4]) + + fir_kernel[4] * (queue[4] + queue[24 - 5]) + + fir_kernel[5] * (queue[5] + queue[24 - 6]) + + fir_kernel[6] * (queue[6] + queue[24 - 7]) + + fir_kernel[7] * (queue[7] + queue[24 - 8]) + + fir_kernel[8] * (queue[8] + queue[24 - 9]) + + fir_kernel[9] * (queue[9] + queue[24 - 10]) + + fir_kernel[10] * (queue[10] + queue[24 - 11]) + + fir_kernel[11] * (queue[11] + queue[24 - 12]); + + samples[i] = acc; + + if (--fir_index < 0) + { + fir_index = fir_len * (SIZE_FACTOR - 1); + memcpy(fir_queue + fir_index + 1, fir_queue, (fir_len - 1) * sizeof(float)); + } + } + + cnv->fir_index = fir_index; +} + +static void fir_interleaved_generic(iqconverter_float_t *cnv, float *samples, int len) +{ + int i; + int fir_index = cnv->fir_index; + int fir_len = cnv->len; + float *fir_kernel = cnv->fir_kernel; + float *fir_queue = cnv->fir_queue; + float *queue; + + for (i = 0; i < len; i += 2) + { + queue = fir_queue + fir_index; + + queue[0] = samples[i]; + + samples[i] = process_fir_taps(fir_kernel, queue, fir_len); + + if (--fir_index < 0) + { + fir_index = fir_len * (SIZE_FACTOR - 1); + memcpy(fir_queue + fir_index + 1, fir_queue, (fir_len - 1) * sizeof(float)); + } + } + + cnv->fir_index = fir_index; +} + +static void fir_interleaved(iqconverter_float_t *cnv, float *samples, int len) +{ + switch (cnv->len) + { + case 4: + fir_interleaved_4(cnv, samples, len); + break; + case 8: + fir_interleaved_8(cnv, samples, len); + break; + case 12: + fir_interleaved_12(cnv, samples, len); + break; + case 24: + fir_interleaved_24(cnv, samples, len); + break; + default: + fir_interleaved_generic(cnv, samples, len); + break; + } +} + +static void delay_interleaved(iqconverter_float_t *cnv, float *samples, int len) +{ + int i; + ALIGNED int index; + ALIGNED int half_len; + ALIGNED float res; + + half_len = cnv->len >> 1; + index = cnv->delay_index; + + for (i = 0; i < len; i += 2) + { + res = cnv->delay_line[index]; + cnv->delay_line[index] = samples[i]; + samples[i] = res; + + if (++index >= half_len) + { + index = 0; + } + } + + cnv->delay_index = index; +} + +#define SCALE (0.01f) + +static void remove_dc(iqconverter_float_t *cnv, float *samples, int len) +{ + int i; + ALIGNED float avg = cnv->avg; + + for (i = 0; i < len; i++) + { + samples[i] -= avg; + avg += SCALE * samples[i]; + } + + cnv->avg = avg; +} + +static void translate_fs_4(iqconverter_float_t *cnv, float *samples, int len) +{ + int i; + ALIGNED float hbc = cnv->hbc; + +#ifdef USE_SSE2 + + float *buf = samples; + ALIGNED __m128 vec; + ALIGNED __m128 rot = _mm_set_ps(hbc, 1.0f, -hbc, -1.0f); + + for (i = 0; i < len / 4; i++, buf +=4) + { + vec = _mm_loadu_ps(buf); + vec = _mm_mul_ps(vec, rot); + _mm_storeu_ps(buf, vec); + } + +#else + + int j; + + for (i = 0; i < len / 4; i++) + { + j = i << 2; + samples[j + 0] = -samples[j + 0]; + samples[j + 1] = -samples[j + 1] * hbc; + //samples[j + 2] = samples[j + 2]; + samples[j + 3] = samples[j + 3] * hbc; + } + +#endif + + fir_interleaved(cnv, samples, len); + delay_interleaved(cnv, samples + 1, len); +} + +void iqconverter_float_process(iqconverter_float_t *cnv, float *samples, int len) +{ + remove_dc(cnv, samples, len); + translate_fs_4(cnv, samples, len); +} diff --git a/Radio/HW/AirSpy/src/iqconverter_float.h b/Radio/HW/AirSpy/src/iqconverter_float.h new file mode 100644 index 0000000..f82b8b3 --- /dev/null +++ b/Radio/HW/AirSpy/src/iqconverter_float.h @@ -0,0 +1,47 @@ +/* +Copyright (C) 2014, Youssef Touil <youssef@airspy.com> + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#ifndef IQCONVERTER_FLOAT_H +#define IQCONVERTER_FLOAT_H + +#include <stdint.h> + +#define IQCONVERTER_NZEROS 2 +#define IQCONVERTER_NPOLES 2 + +typedef struct { + float avg; + float hbc; + int len; + int fir_index; + int delay_index; + float *fir_kernel; + float *fir_queue; + float *delay_line; +} iqconverter_float_t; + +iqconverter_float_t *iqconverter_float_create(const float *hb_kernel, int len); +void iqconverter_float_free(iqconverter_float_t *cnv); +void iqconverter_float_reset(iqconverter_float_t *cnv); +void iqconverter_float_process(iqconverter_float_t *cnv, float *samples, int len); + +#endif // IQCONVERTER_FLOAT_H diff --git a/Radio/HW/AirSpy/src/iqconverter_int16.c b/Radio/HW/AirSpy/src/iqconverter_int16.c new file mode 100644 index 0000000..abb3109 --- /dev/null +++ b/Radio/HW/AirSpy/src/iqconverter_int16.c @@ -0,0 +1,205 @@ +/* +Copyright (C) 2014, Youssef Touil <youssef@airspy.com> + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#include "iqconverter_int16.h" +#include <stdlib.h> +#include <string.h> + +#if defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR) + #include <malloc.h> + #define _aligned_malloc __mingw_aligned_malloc + #define _aligned_free __mingw_aligned_free + #define _inline inline +#elif defined(__APPLE__) + #include <malloc/malloc.h> + #define _aligned_malloc(size, alignment) malloc(size) + #define _aligned_free(mem) free(mem) + #define _inline inline +#elif defined(__FreeBSD__) + #define _inline inline + #define _aligned_free(mem) free(mem) +void * _aligned_malloc(size_t size, size_t alignment); +#elif defined(__GNUC__) && !defined(__MINGW64_VERSION_MAJOR) + #include <malloc.h> + #define _aligned_malloc(size, alignment) memalign(alignment, size) + #define _aligned_free(mem) free(mem) + #define _inline inline +#endif + +#define SIZE_FACTOR 16 +#define DEFAULT_ALIGNMENT 16 + +iqconverter_int16_t *iqconverter_int16_create(const int16_t *hb_kernel, int len) +{ + int i; + size_t buffer_size; + iqconverter_int16_t *cnv = (iqconverter_int16_t *) _aligned_malloc(sizeof(iqconverter_int16_t), DEFAULT_ALIGNMENT); + + cnv->len = len / 2 + 1; + + buffer_size = cnv->len * sizeof(int32_t); + + cnv->fir_kernel = (int32_t *) _aligned_malloc(buffer_size, DEFAULT_ALIGNMENT); + cnv->fir_queue = (int32_t *) _aligned_malloc(buffer_size * SIZE_FACTOR, DEFAULT_ALIGNMENT); + cnv->delay_line = (int16_t *) _aligned_malloc(buffer_size / 4, DEFAULT_ALIGNMENT); + + iqconverter_int16_reset(cnv); + + for (i = 0; i < cnv->len; i++) + { + cnv->fir_kernel[i] = hb_kernel[i * 2]; + } + + return cnv; +} + +void iqconverter_int16_free(iqconverter_int16_t *cnv) +{ + _aligned_free(cnv->fir_kernel); + _aligned_free(cnv->fir_queue); + _aligned_free(cnv->delay_line); + _aligned_free(cnv); +} + +void iqconverter_int16_reset(iqconverter_int16_t *cnv) +{ + cnv->fir_index = 0; + cnv->delay_index = 0; + cnv->old_x = 0; + cnv->old_y = 0; + cnv->old_e = 0; + memset(cnv->delay_line, 0, cnv->len * sizeof(int16_t) / 4); + memset(cnv->fir_queue, 0, cnv->len * sizeof(int16_t) * SIZE_FACTOR); +} + +static void fir_interleaved(iqconverter_int16_t *cnv, int16_t *samples, int len) +{ + int i; + int j; + int fir_index; + int fir_len; + int32_t *queue; + int32_t acc; + + fir_len = cnv->len; + fir_index = cnv->fir_index; + + for (i = 0; i < len; i += 2) + { + queue = cnv->fir_queue + fir_index; + + queue[0] = samples[i]; + + acc = 0; + + // Auto vectorization works on VS2012, VS2013 and GCC + for (j = 0; j < fir_len; j++) + { + acc += cnv->fir_kernel[j] * queue[j]; + } + + if (--fir_index < 0) + { + fir_index = cnv->len * (SIZE_FACTOR - 1); + memcpy(cnv->fir_queue + fir_index + 1, cnv->fir_queue, (cnv->len - 1) * sizeof(int32_t)); + } + + samples[i] = acc >> 15; + } + + cnv->fir_index = fir_index; +} + +static void delay_interleaved(iqconverter_int16_t *cnv, int16_t *samples, int len) +{ + int i; + int index; + int half_len; + int16_t res; + + half_len = cnv->len >> 1; + index = cnv->delay_index; + + for (i = 0; i < len; i += 2) + { + res = cnv->delay_line[index]; + cnv->delay_line[index] = samples[i]; + samples[i] = res; + + if (++index >= half_len) + { + index = 0; + } + } + + cnv->delay_index = index; +} + +static void remove_dc(iqconverter_int16_t *cnv, int16_t *samples, int len) +{ + int i; + int32_t u, old_e; + int16_t x, y, w, s, old_x, old_y; + + old_x = cnv->old_x; + old_y = cnv->old_y; + old_e = cnv->old_e; + + for (i = 0; i < len; i++) + { + x = samples[i]; + w = x - old_x; + u = old_e + (int32_t) old_y * 32100; + s = u >> 15; + y = w + s; + old_e = u - (s << 15); + old_x = x; + old_y = y; + samples[i] = y; + } + + cnv->old_x = old_x; + cnv->old_y = old_y; + cnv->old_e = old_e; +} + +static void translate_fs_4(iqconverter_int16_t *cnv, int16_t *samples, int len) +{ + int i; + + for (i = 0; i < len; i += 4) + { + samples[i + 0] = -samples[i + 0]; + samples[i + 1] = -samples[i + 1] >> 1; + //samples[i + 2] = samples[i + 2]; + samples[i + 3] = samples[i + 3] >> 1; + } + + fir_interleaved(cnv, samples, len); + delay_interleaved(cnv, samples + 1, len); +} + +void iqconverter_int16_process(iqconverter_int16_t *cnv, int16_t *samples, int len) +{ + remove_dc(cnv, samples, len); + translate_fs_4(cnv, samples, len); +} diff --git a/Radio/HW/AirSpy/src/iqconverter_int16.h b/Radio/HW/AirSpy/src/iqconverter_int16.h new file mode 100644 index 0000000..10c867d --- /dev/null +++ b/Radio/HW/AirSpy/src/iqconverter_int16.h @@ -0,0 +1,45 @@ +/* +Copyright (C) 2014, Youssef Touil <youssef@airspy.com> + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#ifndef IQCONVERTER_INT16_H +#define IQCONVERTER_INT16_H + +#include <stdint.h> + +typedef struct { + int len; + int fir_index; + int delay_index; + int16_t old_x; + int16_t old_y; + int32_t old_e; + int32_t *fir_kernel; + int32_t *fir_queue; + int16_t *delay_line; +} iqconverter_int16_t; + +iqconverter_int16_t *iqconverter_int16_create(const int16_t *hb_kernel, int len); +void iqconverter_int16_free(iqconverter_int16_t *cnv); +void iqconverter_int16_reset(iqconverter_int16_t *cnv); +void iqconverter_int16_process(iqconverter_int16_t *cnv, int16_t *samples, int len); + +#endif // IQCONVERTER_INT16_H |