summaryrefslogtreecommitdiff
path: root/Radio/HW/AirSpy
diff options
context:
space:
mode:
Diffstat (limited to 'Radio/HW/AirSpy')
-rw-r--r--Radio/HW/AirSpy/src/airspy.c2011
-rw-r--r--Radio/HW/AirSpy/src/airspy.h224
-rw-r--r--Radio/HW/AirSpy/src/airspy_commands.h145
-rw-r--r--Radio/HW/AirSpy/src/filters.h134
-rw-r--r--Radio/HW/AirSpy/src/iqconverter_float.c502
-rw-r--r--Radio/HW/AirSpy/src/iqconverter_float.h47
-rw-r--r--Radio/HW/AirSpy/src/iqconverter_int16.c205
-rw-r--r--Radio/HW/AirSpy/src/iqconverter_int16.h45
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