summaryrefslogtreecommitdiff
path: root/Radio/HW/BladeRF/common/src
diff options
context:
space:
mode:
Diffstat (limited to 'Radio/HW/BladeRF/common/src')
-rw-r--r--Radio/HW/BladeRF/common/src/conversions.c730
-rw-r--r--Radio/HW/BladeRF/common/src/dc_calibration.c1745
-rw-r--r--Radio/HW/BladeRF/common/src/devcfg.c638
-rw-r--r--Radio/HW/BladeRF/common/src/log.c98
-rw-r--r--Radio/HW/BladeRF/common/src/osx/clock_gettime.c59
-rw-r--r--Radio/HW/BladeRF/common/src/parse.c455
-rw-r--r--Radio/HW/BladeRF/common/src/range.c74
-rw-r--r--Radio/HW/BladeRF/common/src/sha256.c343
-rw-r--r--Radio/HW/BladeRF/common/src/str_queue.c193
-rw-r--r--Radio/HW/BladeRF/common/src/windows/clock_gettime.c58
-rw-r--r--Radio/HW/BladeRF/common/src/windows/getopt_long.c326
-rw-r--r--Radio/HW/BladeRF/common/src/windows/gettimeofday.c49
-rw-r--r--Radio/HW/BladeRF/common/src/windows/mkdtemp.c250
-rw-r--r--Radio/HW/BladeRF/common/src/windows/nanosleep.c41
-rw-r--r--Radio/HW/BladeRF/common/src/windows/setenv.c50
15 files changed, 5109 insertions, 0 deletions
diff --git a/Radio/HW/BladeRF/common/src/conversions.c b/Radio/HW/BladeRF/common/src/conversions.c
new file mode 100644
index 0000000..ad08520
--- /dev/null
+++ b/Radio/HW/BladeRF/common/src/conversions.c
@@ -0,0 +1,730 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (c) 2017-2018 Nuand LLC.
+ *
+ * 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 "conversions.h"
+
+int str2version(const char *str, struct bladerf_version *version)
+{
+ unsigned long tmp;
+ const char *start = str;
+ char *end;
+
+ /* Major version */
+ errno = 0;
+ tmp = strtoul(start, &end, 10);
+ if (errno != 0 || tmp > UINT16_MAX || end == start || *end != '.') {
+ return -1;
+ }
+ version->major = (uint16_t)tmp;
+
+ /* Minor version */
+ if (end[0] == '\0' || end[1] == '\0') {
+ return -1;
+ }
+ errno = 0;
+ start = &end[1];
+ tmp = strtoul(start, &end, 10);
+ if (errno != 0 || tmp > UINT16_MAX || end == start || *end != '.') {
+ return -1;
+ }
+ version->minor = (uint16_t)tmp;
+
+ /* Patch version */
+ if (end[0] == '\0' || end[1] == '\0') {
+ return -1;
+ }
+ errno = 0;
+ start = &end[1];
+ tmp = strtoul(start, &end, 10);
+ if (errno != 0 || tmp > UINT16_MAX || end == start ||
+ (*end != '-' && *end != '\0')) {
+ return -1;
+ }
+ version->patch = (uint16_t)tmp;
+
+ version->describe = str;
+
+ return 0;
+}
+
+const char *devspeed2str(bladerf_dev_speed speed)
+{
+ switch (speed) {
+ case BLADERF_DEVICE_SPEED_HIGH:
+ /* Yeah, the USB IF actually spelled it "Hi" instead of "High".
+ * I know. It hurts me too. */
+ return "Hi-Speed";
+
+ case BLADERF_DEVICE_SPEED_SUPER:
+ /* ...and no hyphen :( */
+ return "SuperSpeed";
+
+ default:
+ return "Unknown";
+ }
+}
+
+bladerf_log_level str2loglevel(const char *str, bool *ok)
+{
+ bladerf_log_level level = BLADERF_LOG_LEVEL_ERROR;
+ bool valid = true;
+
+ if (!strcasecmp(str, "critical")) {
+ level = BLADERF_LOG_LEVEL_CRITICAL;
+ } else if (!strcasecmp(str, "error")) {
+ level = BLADERF_LOG_LEVEL_ERROR;
+ } else if (!strcasecmp(str, "warning")) {
+ level = BLADERF_LOG_LEVEL_WARNING;
+ } else if (!strcasecmp(str, "info")) {
+ level = BLADERF_LOG_LEVEL_INFO;
+ } else if (!strcasecmp(str, "debug")) {
+ level = BLADERF_LOG_LEVEL_DEBUG;
+ } else if (!strcasecmp(str, "verbose")) {
+ level = BLADERF_LOG_LEVEL_VERBOSE;
+ } else {
+ valid = false;
+ }
+
+ *ok = valid;
+ return level;
+}
+
+const char *module2str(bladerf_module m)
+{
+ switch (m) {
+ case BLADERF_MODULE_RX:
+ return "RX";
+ case BLADERF_MODULE_TX:
+ return "TX";
+ default:
+ return "Unknown";
+ }
+}
+
+bladerf_module str2module(const char *str)
+{
+ if (!strcasecmp(str, "RX")) {
+ return BLADERF_MODULE_RX;
+ } else if (!strcasecmp(str, "TX")) {
+ return BLADERF_MODULE_TX;
+ } else {
+ return BLADERF_MODULE_INVALID;
+ }
+}
+
+const char *channel2str(bladerf_channel ch)
+{
+ switch (ch) {
+ case BLADERF_CHANNEL_RX(0):
+ return "RX1";
+ case BLADERF_CHANNEL_TX(0):
+ return "TX1";
+ case BLADERF_CHANNEL_RX(1):
+ return "RX2";
+ case BLADERF_CHANNEL_TX(1):
+ return "TX2";
+ default:
+ return "Unknown";
+ }
+}
+
+bladerf_channel str2channel(char const *str)
+{
+ bladerf_channel rv;
+
+ if (strcasecmp(str, "rx") == 0 || strcasecmp(str, "rx1") == 0) {
+ rv = BLADERF_CHANNEL_RX(0);
+ } else if (strcasecmp(str, "rx2") == 0) {
+ rv = BLADERF_CHANNEL_RX(1);
+ } else if (strcasecmp(str, "tx") == 0 || strcasecmp(str, "tx1") == 0) {
+ rv = BLADERF_CHANNEL_TX(0);
+ } else if (strcasecmp(str, "tx2") == 0) {
+ rv = BLADERF_CHANNEL_TX(1);
+ } else {
+ rv = BLADERF_CHANNEL_INVALID;
+ }
+
+ return rv;
+}
+
+bladerf_direction channel2direction (bladerf_channel ch) {
+ if (ch == BLADERF_CHANNEL_RX(0) || ch == BLADERF_CHANNEL_RX(1)) {
+ return BLADERF_RX;
+ } else {
+ return BLADERF_TX;
+ }
+}
+
+const char *direction2str(bladerf_direction dir)
+{
+ switch (dir) {
+ case BLADERF_RX:
+ return "RX";
+ case BLADERF_TX:
+ return "TX";
+ default:
+ return "Unknown";
+ }
+}
+
+const char *trigger2str(bladerf_trigger_signal trigger)
+{
+ switch (trigger) {
+ case BLADERF_TRIGGER_J71_4:
+ return "J71-4";
+
+ case BLADERF_TRIGGER_J51_1:
+ return "J51-1";
+
+ case BLADERF_TRIGGER_MINI_EXP_1:
+ return "MiniExp-1";
+
+ case BLADERF_TRIGGER_USER_0:
+ return "User-0";
+
+ case BLADERF_TRIGGER_USER_1:
+ return "User-1";
+
+ case BLADERF_TRIGGER_USER_2:
+ return "User-2";
+
+ case BLADERF_TRIGGER_USER_3:
+ return "User-3";
+
+ case BLADERF_TRIGGER_USER_4:
+ return "User-4";
+
+ case BLADERF_TRIGGER_USER_5:
+ return "User-5";
+
+ case BLADERF_TRIGGER_USER_6:
+ return "User-6";
+
+ case BLADERF_TRIGGER_USER_7:
+ return "User-7";
+
+ default:
+ return "Unknown";
+ }
+}
+
+bladerf_trigger_signal str2trigger(const char *str)
+{
+ if (!strcasecmp("J71-4", str)) {
+ return BLADERF_TRIGGER_J71_4;
+ } else if (!strcasecmp("J51-1", str)) {
+ return BLADERF_TRIGGER_J51_1;
+ } else if (!strcasecmp("Miniexp-1", str)) {
+ return BLADERF_TRIGGER_MINI_EXP_1;
+ } else if (!strcasecmp("User-0", str)) {
+ return BLADERF_TRIGGER_USER_0;
+ } else if (!strcasecmp("User-1", str)) {
+ return BLADERF_TRIGGER_USER_1;
+ } else if (!strcasecmp("User-2", str)) {
+ return BLADERF_TRIGGER_USER_2;
+ } else if (!strcasecmp("User-3", str)) {
+ return BLADERF_TRIGGER_USER_3;
+ } else if (!strcasecmp("User-4", str)) {
+ return BLADERF_TRIGGER_USER_4;
+ } else if (!strcasecmp("User-5", str)) {
+ return BLADERF_TRIGGER_USER_5;
+ } else if (!strcasecmp("User-6", str)) {
+ return BLADERF_TRIGGER_USER_6;
+ } else if (!strcasecmp("User-7", str)) {
+ return BLADERF_TRIGGER_USER_7;
+ } else {
+ return BLADERF_TRIGGER_INVALID;
+ }
+}
+
+const char *triggerrole2str(bladerf_trigger_role role)
+{
+ switch (role) {
+ case BLADERF_TRIGGER_ROLE_MASTER:
+ return "Master";
+ case BLADERF_TRIGGER_ROLE_SLAVE:
+ return "Slave";
+ case BLADERF_TRIGGER_ROLE_DISABLED:
+ return "Disabled";
+ default:
+ return "Unknown";
+ }
+}
+
+bladerf_trigger_role str2triggerrole(const char *str)
+{
+ if (!strcasecmp("Master", str)) {
+ return BLADERF_TRIGGER_ROLE_MASTER;
+ } else if (!strcasecmp("Slave", str)) {
+ return BLADERF_TRIGGER_ROLE_SLAVE;
+ } else if (!strcasecmp("Disabled", str) || !strcasecmp("Off", str)) {
+ return BLADERF_TRIGGER_ROLE_DISABLED;
+ } else {
+ return BLADERF_TRIGGER_ROLE_INVALID;
+ }
+}
+
+int str2loopback(const char *str, bladerf_loopback *loopback)
+{
+ int status = 0;
+
+ if (!strcasecmp("firmware", str)) {
+ *loopback = BLADERF_LB_FIRMWARE;
+ } else if (!strcasecmp("bb_txlpf_rxvga2", str)) {
+ *loopback = BLADERF_LB_BB_TXLPF_RXVGA2;
+ } else if (!strcasecmp("bb_txlpf_rxlpf", str)) {
+ *loopback = BLADERF_LB_BB_TXLPF_RXLPF;
+ } else if (!strcasecmp("bb_txvga1_rxvga2", str)) {
+ *loopback = BLADERF_LB_BB_TXVGA1_RXVGA2;
+ } else if (!strcasecmp("bb_txvga1_rxlpf", str)) {
+ *loopback = BLADERF_LB_BB_TXVGA1_RXLPF;
+ } else if (!strcasecmp("rf_lna1", str)) {
+ *loopback = BLADERF_LB_RF_LNA1;
+ } else if (!strcasecmp("rf_lna2", str)) {
+ *loopback = BLADERF_LB_RF_LNA2;
+ } else if (!strcasecmp("rf_lna3", str)) {
+ *loopback = BLADERF_LB_RF_LNA3;
+ } else if (!strcasecmp("rfic_bist", str)) {
+ *loopback = BLADERF_LB_RFIC_BIST;
+ } else if (!strcasecmp("none", str)) {
+ *loopback = BLADERF_LB_NONE;
+ } else {
+ status = -1;
+ }
+
+ return status;
+}
+
+char const *loopback2str(bladerf_loopback loopback)
+{
+ switch (loopback) {
+ case BLADERF_LB_BB_TXLPF_RXVGA2:
+ return "bb_txlpf_rxvga2";
+
+ case BLADERF_LB_BB_TXLPF_RXLPF:
+ return "bb_txlpf_rxlpf";
+
+ case BLADERF_LB_BB_TXVGA1_RXVGA2:
+ return "bb_txvga1_rxvga2";
+
+ case BLADERF_LB_BB_TXVGA1_RXLPF:
+ return "bb_txvga1_rxlpf";
+
+ case BLADERF_LB_RF_LNA1:
+ return "rf_lna1";
+
+ case BLADERF_LB_RF_LNA2:
+ return "rf_lna2";
+
+ case BLADERF_LB_RF_LNA3:
+ return "rf_lna3";
+
+ case BLADERF_LB_FIRMWARE:
+ return "firmware";
+
+ case BLADERF_LB_NONE:
+ return "none";
+
+ case BLADERF_LB_RFIC_BIST:
+ return "rfic_bist";
+
+ default:
+ return "unknown";
+ }
+}
+
+int str2lnagain(const char *str, bladerf_lna_gain *gain)
+{
+ *gain = BLADERF_LNA_GAIN_MAX;
+
+ if (!strcasecmp("max", str) || !strcasecmp("BLADERF_LNA_GAIN_MAX", str)) {
+ *gain = BLADERF_LNA_GAIN_MAX;
+ return 0;
+ } else if (!strcasecmp("mid", str) ||
+ !strcasecmp("BLADERF_LNA_GAIN_MID", str)) {
+ *gain = BLADERF_LNA_GAIN_MID;
+ return 0;
+ } else if (!strcasecmp("bypass", str) ||
+ !strcasecmp("BLADERF_LNA_GAIN_BYPASS", str)) {
+ *gain = BLADERF_LNA_GAIN_BYPASS;
+ return 0;
+ } else {
+ *gain = BLADERF_LNA_GAIN_UNKNOWN;
+ return -1;
+ }
+}
+
+char const *tuningmode2str(bladerf_tuning_mode mode)
+{
+ switch (mode) {
+ case BLADERF_TUNING_MODE_HOST:
+ return "Host";
+ case BLADERF_TUNING_MODE_FPGA:
+ return "FPGA";
+ default:
+ return "Unknown";
+ }
+}
+
+const char *backend_description(bladerf_backend b)
+{
+ switch (b) {
+ case BLADERF_BACKEND_ANY:
+ return "Any";
+
+ case BLADERF_BACKEND_LINUX:
+ return "Linux kernel driver";
+
+ case BLADERF_BACKEND_LIBUSB:
+ return "libusb";
+
+ case BLADERF_BACKEND_CYPRESS:
+ return "Cypress driver";
+
+ case BLADERF_BACKEND_DUMMY:
+ return "Dummy";
+
+ default:
+ return "Unknown";
+ }
+}
+
+void sc16q11_to_float(const int16_t *in, float *out, unsigned int n)
+{
+ unsigned int i;
+
+ for (i = 0; i < (2 * n); i += 2) {
+ out[i] = (float)in[i] * (1.0f / 2048.0f);
+ out[i + 1] = (float)in[i + 1] * (1.0f / 2048.0f);
+ }
+}
+
+void float_to_sc16q11(const float *in, int16_t *out, unsigned int n)
+{
+ unsigned int i;
+
+ for (i = 0; i < (2 * n); i += 2) {
+ out[i] = (int16_t)(in[i] * 2048.0f);
+ out[i + 1] = (int16_t)(in[i + 1] * 2048.0f);
+ }
+}
+
+bladerf_cal_module str_to_bladerf_cal_module(const char *str)
+{
+ bladerf_cal_module module = BLADERF_DC_CAL_INVALID;
+
+ if (!strcasecmp(str, "lpf_tuning") || !strcasecmp(str, "lpftuning") ||
+ !strcasecmp(str, "tuning")) {
+ module = BLADERF_DC_CAL_LPF_TUNING;
+ } else if (!strcasecmp(str, "tx_lpf") || !strcasecmp(str, "txlpf")) {
+ module = BLADERF_DC_CAL_TX_LPF;
+ } else if (!strcasecmp(str, "rx_lpf") || !strcasecmp(str, "rxlpf")) {
+ module = BLADERF_DC_CAL_RX_LPF;
+ } else if (!strcasecmp(str, "rx_vga2") || !strcasecmp(str, "rxvga2")) {
+ module = BLADERF_DC_CAL_RXVGA2;
+ }
+
+ return module;
+}
+
+const char *smb_mode_to_str(bladerf_smb_mode mode)
+{
+ switch (mode) {
+ case BLADERF_SMB_MODE_DISABLED:
+ return "Disabled";
+
+ case BLADERF_SMB_MODE_OUTPUT:
+ return "Output";
+
+ case BLADERF_SMB_MODE_INPUT:
+ return "Input";
+
+ case BLADERF_SMB_MODE_UNAVAILBLE:
+ return "Unavailable";
+
+ default:
+ return "Unknown";
+ }
+};
+
+bladerf_smb_mode str_to_smb_mode(const char *str)
+{
+ if (!strcasecmp(str, "disabled") || !strcasecmp(str, "off")) {
+ return BLADERF_SMB_MODE_DISABLED;
+ } else if (!strcasecmp(str, "output")) {
+ return BLADERF_SMB_MODE_OUTPUT;
+ } else if (!strcasecmp(str, "input")) {
+ return BLADERF_SMB_MODE_INPUT;
+ } else if (!strcasecmp(str, "unavailable")) {
+ return BLADERF_SMB_MODE_UNAVAILBLE;
+ } else {
+ return BLADERF_SMB_MODE_INVALID;
+ }
+}
+
+bladerf_tuning_mode str_to_tuning_mode(const char *str)
+{
+ if (!strcasecmp(str, "fpga")) {
+ return BLADERF_TUNING_MODE_FPGA;
+ } else if (!strcasecmp(str, "host")) {
+ return BLADERF_TUNING_MODE_HOST;
+ } else {
+ return BLADERF_TUNING_MODE_INVALID;
+ }
+}
+
+unsigned int str2uint(const char *str,
+ unsigned int min,
+ unsigned int max,
+ bool *ok)
+{
+ unsigned int ret;
+ char *optr;
+
+ errno = 0;
+ ret = strtoul(str, &optr, 0);
+
+ if (errno == ERANGE || (errno != 0 && ret == 0)) {
+ *ok = false;
+ return 0;
+ }
+
+ if (str == optr) {
+ *ok = false;
+ return 0;
+ }
+
+ if (ret >= min && ret <= max) {
+ *ok = true;
+ return (unsigned int)ret;
+ }
+
+ *ok = false;
+ return 0;
+}
+
+int str2int(const char *str, int min, int max, bool *ok)
+{
+ long int ret;
+ char *optr;
+
+ errno = 0;
+ ret = strtol(str, &optr, 0);
+
+ if ((errno == ERANGE && (ret == LONG_MAX || ret == LONG_MIN)) ||
+ (errno != 0 && ret == 0)) {
+ *ok = false;
+ return 0;
+ }
+
+ if (str == optr) {
+ *ok = false;
+ return 0;
+ }
+
+ if (ret >= min && ret <= max) {
+ *ok = true;
+ return (int)ret;
+ }
+
+ *ok = false;
+ return 0;
+}
+
+uint64_t str2uint64(const char *str, uint64_t min, uint64_t max, bool *ok)
+{
+ uint64_t ret;
+ char *optr;
+
+ errno = 0;
+ ret = (uint64_t)strtod(str, &optr);
+
+ if ((errno == ERANGE && ret == ULONG_MAX) || (errno != 0 && ret == 0)) {
+ *ok = false;
+ return 0;
+ }
+
+ if (str == optr) {
+ *ok = false;
+ return 0;
+ }
+
+ if (ret >= min && ret <= max) {
+ *ok = true;
+ return ret;
+ }
+
+ *ok = false;
+ return 0;
+}
+
+double str2double(const char *str, double min, double max, bool *ok)
+{
+ double ret;
+ char *optr;
+
+ errno = 0;
+ ret = strtod(str, &optr);
+
+ if (errno == ERANGE || (errno != 0 && ret == 0)) {
+ *ok = false;
+ return NAN;
+ }
+
+ if (str == optr) {
+ *ok = false;
+ return NAN;
+ }
+
+ if (ret >= min && ret <= max) {
+ *ok = true;
+ return ret;
+ }
+
+ *ok = false;
+ return NAN;
+}
+
+static uint64_t suffix_multiplier(const char *str,
+ const struct numeric_suffix *suffixes,
+ const size_t num_suff,
+ bool *ok)
+{
+ unsigned i;
+
+ *ok = true;
+
+ if (!strlen(str))
+ return 1;
+
+ for (i = 0; i < num_suff; i++) {
+ if (!strcasecmp(suffixes[i].suffix, str)) {
+ return suffixes[i].multiplier;
+ }
+ }
+
+ *ok = false;
+ return 0;
+}
+
+unsigned int str2uint_suffix(const char *str,
+ unsigned int min,
+ unsigned int max,
+ const struct numeric_suffix *suffixes,
+ const size_t num_suff,
+ bool *ok)
+{
+ uint64_t mult;
+ unsigned int rv;
+ double val;
+ char *optr;
+
+ errno = 0;
+ val = strtod(str, &optr);
+
+ if (errno == ERANGE || (errno != 0 && val == 0)) {
+ *ok = false;
+ return 0;
+ }
+
+ if (str == optr) {
+ *ok = false;
+ return 0;
+ }
+
+ mult = suffix_multiplier(optr, suffixes, num_suff, ok);
+ if (!*ok)
+ return false;
+
+ rv = (unsigned int)(val * mult);
+
+ if (rv >= min && rv <= max) {
+ return rv;
+ }
+
+ *ok = false;
+ return 0;
+}
+
+uint64_t str2uint64_suffix(const char *str,
+ uint64_t min,
+ uint64_t max,
+ const struct numeric_suffix *suffixes,
+ const size_t num_suff,
+ bool *ok)
+{
+ uint64_t mult, rv;
+ long double val;
+ char *optr;
+
+ errno = 0;
+ val = strtold(str, &optr);
+
+ if (errno == ERANGE && (errno != 0 && val == 0)) {
+ *ok = false;
+ return 0;
+ }
+
+ if (str == optr) {
+ *ok = false;
+ return 0;
+ }
+
+ mult = suffix_multiplier(optr, suffixes, num_suff, ok);
+ if (!*ok)
+ return false;
+
+ rv = (uint64_t)(val * mult);
+ if (rv >= min && rv <= max) {
+ return rv;
+ }
+
+ *ok = false;
+ return 0;
+}
+
+int str2bool(const char *str, bool *val)
+{
+ unsigned int i;
+
+ char *str_true[] = { "true", "t", "one", "1", "enable", "en", "on" };
+ char *str_false[] = { "false", "f", "zero", "0", "disable", "dis", "off" };
+
+ for (i = 0; i < ARRAY_SIZE(str_true); i++) {
+ if (!strcasecmp(str_true[i], str)) {
+ *val = true;
+ return 0;
+ }
+ }
+
+ for (i = 0; i < ARRAY_SIZE(str_false); i++) {
+ if (!strcasecmp(str_false[i], str)) {
+ *val = false;
+ return 0;
+ }
+ }
+
+ return BLADERF_ERR_INVAL;
+}
diff --git a/Radio/HW/BladeRF/common/src/dc_calibration.c b/Radio/HW/BladeRF/common/src/dc_calibration.c
new file mode 100644
index 0000000..c746c3c
--- /dev/null
+++ b/Radio/HW/BladeRF/common/src/dc_calibration.c
@@ -0,0 +1,1745 @@
+/**
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (c) 2015 Nuand LLC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+#include <limits.h>
+
+#define _USE_MATH_DEFINES /* Required for MSVC */
+#include <math.h>
+
+#include <libbladeRF.h>
+
+#include "dc_calibration.h"
+#include "conversions.h"
+
+struct complexf {
+ float i;
+ float q;
+};
+
+struct gain_mode {
+ bladerf_lna_gain lna_gain;
+ int rxvga1, rxvga2;
+};
+/*******************************************************************************
+ * Debug items
+ ******************************************************************************/
+
+/* Enable this to print diagnostic and debug information */
+//#define ENABLE_DC_CALIBRATION_DEBUG
+//#define ENABLE_DC_CALIBRATION_VERBOSE
+
+#ifndef PR_DBG
+# ifdef ENABLE_DC_CALIBRATION_DEBUG
+# define PR_DBG(...) fprintf(stderr, " " __VA_ARGS__)
+# else
+# define PR_DBG(...) do {} while (0)
+# endif
+#endif
+
+#ifndef PR_VERBOSE
+# ifdef ENABLE_DC_CALIBRATION_VERBOSE
+# define PR_VERBOSE(...) fprintf(stderr, " " __VA_ARGS__)
+# else
+# define PR_VERBOSE(...) do {} while (0)
+# endif
+#endif
+
+/*******************************************************************************
+ * Debug routines for saving samples
+ ******************************************************************************/
+
+//#define ENABLE_SAVE_SC16Q11
+#ifdef ENABLE_SAVE_SC16Q11
+static void save_sc16q11(const char *name, int16_t *samples, unsigned int count)
+{
+ FILE *out = fopen(name, "wb");
+ if (!out) {
+ return;
+ }
+
+ fwrite(samples, 2 * sizeof(samples[0]), count, out);
+ fclose(out);
+}
+#else
+# define save_sc16q11(name, samples, count) do {} while (0)
+#endif
+
+//#define ENABLE_SAVE_COMPLEXF
+#ifdef ENABLE_SAVE_COMPLEXF
+static void save_complexf(const char *name, struct complexf *samples,
+ unsigned int count)
+{
+ unsigned int n;
+ FILE *out = fopen(name, "wb");
+ if (!out) {
+ return;
+ }
+
+ for (n = 0; n < count; n++) {
+ fwrite(&samples[n].i, sizeof(samples[n].i), 1, out);
+ fwrite(&samples[n].q, sizeof(samples[n].q), 1, out);
+ }
+
+ fclose(out);
+}
+#else
+# define save_complexf(name, samples, count) do {} while (0)
+#endif
+
+
+/*******************************************************************************
+ * LMS6002D DC offset calibration
+ ******************************************************************************/
+
+/* We've found that running samples through the LMS6 tends to be required
+ * for the TX LPF calibration to converge */
+static inline int tx_lpf_dummy_tx(struct bladerf *dev)
+{
+ int status;
+ int retval = 0;
+ struct bladerf_metadata meta;
+ int16_t zero_sample[] = { 0, 0 };
+
+ bladerf_loopback loopback_backup;
+ struct bladerf_rational_rate sample_rate_backup;
+
+ memset(&meta, 0, sizeof(meta));
+
+ status = bladerf_get_loopback(dev, &loopback_backup);
+ if (status != 0) {
+ return status;
+ }
+
+ status = bladerf_get_rational_sample_rate(dev, BLADERF_MODULE_TX,
+ &sample_rate_backup);
+ if (status != 0) {
+ return status;
+ }
+
+ status = bladerf_set_loopback(dev, BLADERF_LB_BB_TXVGA1_RXVGA2);
+ if (status != 0) {
+ goto out;
+ }
+
+ status = bladerf_set_sample_rate(dev, BLADERF_MODULE_TX, 3000000, NULL);
+ if (status != 0) {
+ goto out;
+ }
+
+ status = bladerf_sync_config(dev, BLADERF_MODULE_TX,
+ BLADERF_FORMAT_SC16_Q11_META,
+ 64, 16384, 16, 1000);
+ if (status != 0) {
+ goto out;
+ }
+
+ status = bladerf_enable_module(dev, BLADERF_MODULE_TX, true);
+ if (status != 0) {
+ goto out;
+ }
+
+ meta.flags = BLADERF_META_FLAG_TX_BURST_START |
+ BLADERF_META_FLAG_TX_BURST_END |
+ BLADERF_META_FLAG_TX_NOW;
+
+ status = bladerf_sync_tx(dev, zero_sample, 1, &meta, 2000);
+ if (status != 0) {
+ goto out;
+ }
+
+out:
+ status = bladerf_enable_module(dev, BLADERF_MODULE_TX, false);
+ if (status != 0 && retval == 0) {
+ retval = status;
+ }
+
+ status = bladerf_set_rational_sample_rate(dev, BLADERF_MODULE_TX,
+ &sample_rate_backup, NULL);
+ if (status != 0 && retval == 0) {
+ retval = status;
+ }
+
+ status = bladerf_set_loopback(dev, loopback_backup);
+ if (status != 0 && retval == 0) {
+ retval = status;
+ }
+
+ return retval;
+}
+
+static int cal_tx_lpf(struct bladerf *dev)
+{
+ int status;
+
+ status = tx_lpf_dummy_tx(dev);
+ if (status == 0) {
+ status = bladerf_calibrate_dc(dev, BLADERF_DC_CAL_TX_LPF);
+ }
+
+ return status;
+}
+
+int dc_calibration_lms6(struct bladerf *dev, const char *module_str)
+{
+ int status;
+ bladerf_cal_module module;
+
+ if (!strcasecmp(module_str, "all")) {
+ status = bladerf_calibrate_dc(dev, BLADERF_DC_CAL_LPF_TUNING);
+ if (status != 0) {
+ return status;
+ }
+
+ status = cal_tx_lpf(dev);
+ if (status != 0) {
+ return status;
+ }
+
+ status = bladerf_calibrate_dc(dev, BLADERF_DC_CAL_RX_LPF);
+ if (status != 0) {
+ return status;
+ }
+
+ status = bladerf_calibrate_dc(dev, BLADERF_DC_CAL_RXVGA2);
+ } else {
+ module = str_to_bladerf_cal_module(module_str);
+ if (module == BLADERF_DC_CAL_INVALID) {
+ return BLADERF_ERR_INVAL;
+ }
+
+ if (module == BLADERF_DC_CAL_TX_LPF) {
+ status = cal_tx_lpf(dev);
+ } else {
+ status = bladerf_calibrate_dc(dev, module);
+ }
+ }
+
+ return status;
+}
+
+
+
+/*******************************************************************************
+ * Shared utility routines
+ ******************************************************************************/
+
+/* Round float to int16_t */
+static inline int16_t float_to_int16(float val)
+{
+ if ((val - 0.5) <= INT16_MIN) {
+ return INT16_MIN;
+ }
+ if ((val + 0.5) >= INT16_MAX) {
+ return INT16_MAX;
+ }
+ return val >= 0 ? (int16_t)(val + 0.5) : (int16_t)(val - 0.5);
+}
+
+/* Convert ms to samples */
+#define MS_TO_SAMPLES(ms_, rate_) (\
+ (unsigned int) (ms_ * ((uint64_t) rate_) / 1000) \
+)
+
+/* RX samples, retrying if the machine is struggling to keep up. */
+static int rx_samples(struct bladerf *dev, int16_t *samples,
+ unsigned int count, uint64_t *ts, uint64_t ts_inc)
+{
+ int status = 0;
+ struct bladerf_metadata meta;
+ int retry = 0;
+ const int max_retries = 10;
+ bool overrun = true;
+
+ memset(&meta, 0, sizeof(meta));
+ meta.timestamp = *ts;
+
+ while (status == 0 && overrun && retry < max_retries) {
+ meta.timestamp = *ts;
+ status = bladerf_sync_rx(dev, samples, count, &meta, 2000);
+
+ if (status == BLADERF_ERR_TIME_PAST) {
+ status = bladerf_get_timestamp(dev, BLADERF_MODULE_RX, ts);
+ if (status != 0) {
+ return status;
+ } else {
+ *ts += 20 * ts_inc;
+ retry++;
+ status = 0;
+ }
+ } else if (status == 0) {
+ overrun = (meta.flags & BLADERF_META_STATUS_OVERRUN) != 0;
+ if (overrun) {
+ *ts += count + ts_inc;
+ retry++;
+ }
+ } else {
+ return status;
+ }
+ }
+
+ if (retry >= max_retries) {
+ status = BLADERF_ERR_IO;
+ } else if (status == 0) {
+ *ts += count + ts_inc;
+ }
+
+ return status;
+}
+
+
+
+/*******************************************************************************
+ * RX DC offset calibration
+ ******************************************************************************/
+
+#define RX_CAL_RATE (3000000)
+#define RX_CAL_BW (1500000)
+#define RX_CAL_TS_INC (MS_TO_SAMPLES(15, RX_CAL_RATE))
+#define RX_CAL_COUNT (MS_TO_SAMPLES(5, RX_CAL_RATE))
+
+#define RX_CAL_MAX_SWEEP_LEN (2 * 2048 / 32) /* -2048 : 32 : 2048 */
+
+struct rx_cal {
+ struct bladerf *dev;
+
+ int16_t *samples;
+ unsigned int num_samples;
+
+ int16_t *corr_sweep;
+
+ uint64_t ts;
+
+ uint64_t tx_freq;
+};
+
+struct rx_cal_backup {
+ struct bladerf_rational_rate rational_sample_rate;
+ unsigned int bandwidth;
+ uint64_t tx_freq;
+};
+
+static int get_rx_cal_backup(struct bladerf *dev, struct rx_cal_backup *b)
+{
+ int status;
+
+ status = bladerf_get_rational_sample_rate(dev, BLADERF_MODULE_RX,
+ &b->rational_sample_rate);
+ if (status != 0) {
+ return status;
+ }
+
+ status = bladerf_get_bandwidth(dev, BLADERF_MODULE_RX, &b->bandwidth);
+ if (status != 0) {
+ return status;
+ }
+
+ status = bladerf_get_frequency(dev, BLADERF_MODULE_TX, &b->tx_freq);
+ if (status != 0) {
+ return status;
+ }
+
+ return status;
+}
+
+static int set_rx_cal_backup(struct bladerf *dev, struct rx_cal_backup *b)
+{
+ int status;
+ int retval = 0;
+
+ status = bladerf_set_rational_sample_rate(dev, BLADERF_MODULE_RX,
+ &b->rational_sample_rate, NULL);
+ if (status != 0 && retval == 0) {
+ retval = status;
+ }
+
+ status = bladerf_set_bandwidth(dev, BLADERF_MODULE_RX, b->bandwidth, NULL);
+ if (status != 0 && retval == 0) {
+ retval = status;
+ }
+
+ status = bladerf_set_frequency(dev, BLADERF_MODULE_TX, b->tx_freq);
+ if (status != 0 && retval == 0) {
+ retval = status;
+ }
+
+ return retval;
+}
+
+/* Ensure TX >= 1 MHz away from the RX frequency to avoid any potential
+ * artifacts from the PLLs interfering with one another */
+static int rx_cal_update_frequency(struct rx_cal *cal, uint64_t rx_freq)
+{
+ int status = 0;
+ uint64_t f_diff;
+
+ if (rx_freq < cal->tx_freq) {
+ f_diff = cal->tx_freq - rx_freq;
+ } else {
+ f_diff = rx_freq - cal->tx_freq;
+ }
+
+ PR_DBG("Set F_RX = %u\n", rx_freq);
+ PR_DBG("F_diff(RX, TX) = %u\n", f_diff);
+
+ if (f_diff < 1000000) {
+ if (rx_freq >= (BLADERF_FREQUENCY_MIN + 1000000)) {
+ cal->tx_freq = rx_freq - 1000000;
+ } else {
+ cal->tx_freq = rx_freq + 1000000;
+ }
+
+ status = bladerf_set_frequency(cal->dev, BLADERF_MODULE_TX,
+ cal->tx_freq);
+ if (status != 0) {
+ return status;
+ }
+
+ PR_DBG("Adjusted TX frequency: %u\n", cal->tx_freq);
+ }
+
+ status = bladerf_set_frequency(cal->dev, BLADERF_MODULE_RX, rx_freq);
+ if (status != 0) {
+ return status;
+ }
+
+ cal->ts += RX_CAL_TS_INC;
+
+ return status;
+}
+
+static inline void sample_mean(int16_t *samples, size_t count,
+ float *mean_i, float *mean_q)
+{
+ int64_t accum_i = 0;
+ int64_t accum_q = 0;
+
+ size_t n;
+
+
+ if (count == 0) {
+ assert(!"Invalid count (0) provided to sample_mean()");
+ *mean_i = 0;
+ *mean_q = 0;
+ return;
+ }
+
+ for (n = 0; n < (2 * count); n += 2) {
+ accum_i += samples[n];
+ accum_q += samples[n + 1];
+ }
+
+ *mean_i = ((float) accum_i) / count;
+ *mean_q = ((float) accum_q) / count;
+}
+
+static inline int set_rx_dc_corr(struct bladerf *dev, int16_t i, int16_t q)
+{
+ int status;
+
+ status = bladerf_set_correction(dev, BLADERF_MODULE_RX,
+ BLADERF_CORR_LMS_DCOFF_I, i);
+ if (status != 0) {
+ return status;
+ }
+
+ status = bladerf_set_correction(dev, BLADERF_MODULE_RX,
+ BLADERF_CORR_LMS_DCOFF_Q, q);
+ return status;
+}
+
+/* Get the mean for one of the coarse estimate points. If it seems that this
+ * value might be (or close) causing us to clamp, adjust it and retry */
+static int rx_cal_coarse_means(struct rx_cal *cal, int16_t *corr_value,
+ float *mean_i, float *mean_q)
+{
+ int status;
+ const int16_t mean_limit_high = 2000;
+ const int16_t mean_limit_low = -mean_limit_high;
+ const int16_t corr_limit = 128;
+ bool retry = false;
+
+ do {
+ status = set_rx_dc_corr(cal->dev, *corr_value, *corr_value);
+ if (status != 0) {
+ return status;
+ }
+
+ status = rx_samples(cal->dev, cal->samples, cal->num_samples,
+ &cal->ts, RX_CAL_TS_INC);
+ if (status != 0) {
+ return status;
+ }
+
+ sample_mean(cal->samples, cal->num_samples, mean_i, mean_q);
+
+ if (*mean_i > mean_limit_high || *mean_q > mean_limit_high ||
+ *mean_i < mean_limit_low || *mean_q < mean_limit_low ) {
+
+ if (*corr_value < 0) {
+ retry = (*corr_value <= -corr_limit);
+ } else {
+ retry = (*corr_value >= corr_limit);
+ }
+
+ if (retry) {
+ PR_DBG("Coarse estimate point Corr=%4d yields extreme means: "
+ "(%4f, %4f). Retrying...\n",
+ *corr_value, *mean_i, *mean_q);
+
+ *corr_value = *corr_value / 2;
+ }
+ } else {
+ retry = false;
+ }
+ } while (retry);
+
+ if (retry) {
+ PR_DBG("Non-ideal values are being used.\n");
+ }
+
+ return 0;
+}
+
+/* Estimate the DC correction values that yield zero DC offset via a linear
+ * approximation */
+static int rx_cal_coarse_estimate(struct rx_cal *cal,
+ int16_t *i_est, int16_t *q_est)
+{
+ int status;
+ int16_t x1 = -2048;
+ int16_t x2 = 2048;
+ float y1i, y1q, y2i, y2q;
+ float mi, mq;
+ float bi, bq;
+ float i_guess, q_guess;
+
+ status = rx_cal_coarse_means(cal, &x1, &y1i, &y1q);
+ if (status != 0) {
+ *i_est = 0;
+ *q_est = 0;
+ return status;
+ }
+
+ PR_VERBOSE("Means for x1=%d: y1i=%f, y1q=%f\n", x1, y1i, y1q);
+
+ status = rx_cal_coarse_means(cal, &x2, &y2i, &y2q);
+ if (status != 0) {
+ *i_est = 0;
+ *q_est = 0;
+ return status;
+ }
+
+ PR_VERBOSE("Means for x2: y2i=%f, y2q=%f\n", y2i, y2q);
+
+ mi = (y2i - y1i) / (x2 - x1);
+ mq = (y2q - y1q) / (x2 - x1);
+
+ bi = y1i - mi * x1;
+ bq = y1q - mq * x1;
+
+ PR_VERBOSE("mi=%f, bi=%f, mq=%f, bq=%f\n", mi, bi, mq, bq);
+
+ i_guess = -bi/mi + 0.5f;
+ if (i_guess < -2048) {
+ i_guess = -2048;
+ } else if (i_guess > 2048) {
+ i_guess = 2048;
+ }
+
+ q_guess = -bq/mq + 0.5f;
+ if (q_guess < -2048) {
+ q_guess = -2048;
+ } else if (q_guess > 2048) {
+ q_guess = 2048;
+ }
+
+ *i_est = (int16_t) i_guess;
+ *q_est = (int16_t) q_guess;
+
+ PR_DBG("Coarse estimate: I=%d, Q=%d\n", *i_est, *q_est);
+
+ return 0;
+}
+
+static void init_rx_cal_sweep(int16_t *corr, unsigned int *sweep_len,
+ int16_t i_est, int16_t q_est)
+{
+ unsigned int actual_len = 0;
+ unsigned int i;
+
+ int16_t sweep_min, sweep_max, sweep_val;
+
+ /* LMS6002D RX DC calibrations have a limited range. libbladeRF throws away
+ * the lower 5 bits. */
+ const int16_t sweep_inc = 32;
+
+ const int16_t min_est = (i_est < q_est) ? i_est : q_est;
+ const int16_t max_est = (i_est > q_est) ? i_est : q_est;
+
+ sweep_min = min_est - 12 * 32;
+ if (sweep_min < -2048) {
+ sweep_min = -2048;
+ }
+
+ sweep_max = max_est + 12 * 32;
+ if (sweep_max > 2048) {
+ sweep_max = 2048;
+ }
+
+ /* Given that these lower bits are thrown away, it can be confusing to
+ * see that values change in their LSBs that don't matter. Therefore,
+ * we'll adjust to muliples of sweep_inc */
+ sweep_min = (sweep_min / 32) * 32;
+ sweep_max = (sweep_max / 32) * 32;
+
+
+ PR_DBG("Sweeping [%d : %d : %d]\n", sweep_min, sweep_inc, sweep_max);
+
+ sweep_val = sweep_min;
+ for (i = 0; sweep_val < sweep_max && i < RX_CAL_MAX_SWEEP_LEN; i++) {
+ corr[i] = sweep_val;
+ sweep_val += sweep_inc;
+ actual_len++;
+ }
+
+ *sweep_len = actual_len;
+}
+
+static int save_gains(struct rx_cal *cal, struct gain_mode *gain) {
+ int status;
+
+ status = bladerf_get_lna_gain(cal->dev, &gain->lna_gain);
+ if (status != 0) {
+ return status;
+ }
+
+ status = bladerf_get_rxvga1(cal->dev, &gain->rxvga1);
+ if (status != 0) {
+ return status;
+ }
+
+ status = bladerf_get_rxvga2(cal->dev, &gain->rxvga2);
+ if (status != 0) {
+ return status;
+ }
+
+ return status;
+}
+
+static int load_gains(struct rx_cal *cal, struct gain_mode *gain) {
+ int status;
+
+ status = bladerf_set_lna_gain(cal->dev, gain->lna_gain);
+ if (status != 0) {
+ return status;
+ }
+
+ status = bladerf_set_rxvga1(cal->dev, gain->rxvga1);
+ if (status != 0) {
+ return status;
+ }
+
+ status = bladerf_set_rxvga2(cal->dev, gain->rxvga2);
+ if (status != 0) {
+ return status;
+ }
+
+ return status;
+}
+
+static int rx_cal_dc_off(struct rx_cal *cal, struct gain_mode *gains,
+ int16_t *dc_i, int16_t *dc_q)
+{
+ int status = BLADERF_ERR_UNEXPECTED;
+
+ float mean_i, mean_q;
+
+ status = load_gains(cal, gains);
+ if (status != 0) {
+ return status;
+ }
+
+ status = rx_samples(cal->dev, cal->samples, cal->num_samples,
+ &cal->ts, RX_CAL_TS_INC);
+ if (status != 0) {
+ return status;
+ }
+
+ sample_mean(cal->samples, cal->num_samples, &mean_i, &mean_q);
+ *dc_i = float_to_int16(mean_i);
+ *dc_q = float_to_int16(mean_q);
+
+ return 0;
+}
+
+static int rx_cal_sweep(struct rx_cal *cal,
+ int16_t *corr, unsigned int sweep_len,
+ int16_t *result_i, int16_t *result_q,
+ float *error_i, float *error_q)
+{
+ int status = BLADERF_ERR_UNEXPECTED;
+ unsigned int n;
+
+ int16_t min_corr_i = 0;
+ int16_t min_corr_q = 0;
+
+ float mean_i, mean_q;
+ float min_val_i, min_val_q;
+
+ min_val_i = min_val_q = 2048;
+
+ for (n = 0; n < sweep_len; n++) {
+ status = set_rx_dc_corr(cal->dev, corr[n], corr[n]);
+ if (status != 0) {
+ return status;
+ }
+
+ status = rx_samples(cal->dev, cal->samples, cal->num_samples,
+ &cal->ts, RX_CAL_TS_INC);
+ if (status != 0) {
+ return status;
+ }
+
+ sample_mean(cal->samples, cal->num_samples, &mean_i, &mean_q);
+
+ PR_VERBOSE(" Corr=%4d, Mean_I=%4.2f, Mean_Q=%4.2f\n",
+ corr[n], mean_i, mean_q);
+
+ /* Not using fabs() to avoid adding a -lm dependency */
+ if (mean_i < 0) {
+ mean_i = -mean_i;
+ }
+
+ if (mean_q < 0) {
+ mean_q = -mean_q;
+ }
+
+ if (mean_i < min_val_i) {
+ min_val_i = mean_i;
+ min_corr_i = corr[n];
+ }
+
+ if (mean_q < min_val_q) {
+ min_val_q = mean_q;
+ min_corr_q = corr[n];
+ }
+ }
+
+ *result_i = min_corr_i;
+ *result_q = min_corr_q;
+ *error_i = min_val_i;
+ *error_q = min_val_q;
+
+ return 0;
+}
+
+static int perform_rx_cal(struct rx_cal *cal, struct dc_calibration_params *p)
+{
+ int status;
+ int16_t i_est, q_est;
+ unsigned int sweep_len = RX_CAL_MAX_SWEEP_LEN;
+ struct gain_mode saved_gains;
+
+ struct gain_mode agc_gains[] = {
+ { .lna_gain = BLADERF_LNA_GAIN_MAX, .rxvga1 = 30, .rxvga2 = 15 }, /* AGC Max Gain */
+ { .lna_gain = BLADERF_LNA_GAIN_MID, .rxvga1 = 30, .rxvga2 = 0 }, /* AGC Mid Gain */
+ { .lna_gain = BLADERF_LNA_GAIN_MID, .rxvga1 = 12, .rxvga2 = 0 } /* AGC Min Gain */
+ };
+
+ status = rx_cal_update_frequency(cal, p->frequency);
+ if (status != 0) {
+ return status;
+ }
+
+ /* Get an initial guess at our correction values */
+ status = rx_cal_coarse_estimate(cal, &i_est, &q_est);
+ if (status != 0) {
+ return status;
+ }
+
+ /* Perform a finer sweep of correction values */
+ init_rx_cal_sweep(cal->corr_sweep, &sweep_len, i_est, q_est);
+
+ /* Advance our timestmap just to account for any time we may have lost */
+ cal->ts += RX_CAL_TS_INC;
+
+ status = rx_cal_sweep(cal, cal->corr_sweep, sweep_len,
+ &p->corr_i, &p->corr_q,
+ &p->error_i, &p->error_q);
+
+ if (status != 0) {
+ return status;
+ }
+
+ /* Apply the nominal correction values */
+ status = set_rx_dc_corr(cal->dev, p->corr_i, p->corr_q);
+ if (status != 0) {
+ return status;
+ }
+
+ bladerf_fpga_size fpga_size;
+ status = bladerf_get_fpga_size(cal->dev, &fpga_size);
+ if (status != 0) {
+ return status;
+ }
+
+ if (fpga_size != BLADERF_FPGA_40KLE &&
+ fpga_size != BLADERF_FPGA_115KLE) {
+ return 0;
+ }
+
+ /* Measure DC correction for AGC */
+ status = save_gains(cal, &saved_gains);
+ if (status != 0) {
+ return status;
+ }
+
+ status = rx_cal_dc_off(cal, &agc_gains[2], &p->min_dc_i, &p->min_dc_q);
+ if (status != 0) {
+ return status;
+ }
+
+ status = rx_cal_dc_off(cal, &agc_gains[1], &p->mid_dc_i, &p->mid_dc_q);
+ if (status != 0) {
+ return status;
+ }
+
+ status = rx_cal_dc_off(cal, &agc_gains[0], &p->max_dc_i, &p->max_dc_q);
+ if (status != 0) {
+ return status;
+ }
+
+ status = load_gains(cal, &saved_gains);
+
+ return status;
+}
+
+static int rx_cal_init_state(struct bladerf *dev,
+ const struct rx_cal_backup *backup,
+ struct rx_cal *state)
+{
+ int status;
+
+ state->dev = dev;
+
+ state->num_samples = RX_CAL_COUNT;
+
+ state->samples = malloc(2 * sizeof(state->samples[0]) * RX_CAL_COUNT);
+ if (state->samples == NULL) {
+ return BLADERF_ERR_MEM;
+ }
+
+ state->corr_sweep = malloc(sizeof(state->corr_sweep[0]) * RX_CAL_MAX_SWEEP_LEN);
+ if (state->corr_sweep == NULL) {
+ return BLADERF_ERR_MEM;
+ }
+
+ state->tx_freq = backup->tx_freq;
+
+ status = bladerf_get_timestamp(dev, BLADERF_MODULE_RX, &state->ts);
+ if (status != 0) {
+ return status;
+ }
+
+ /* Schedule first RX well into the future */
+ state->ts += 20 * RX_CAL_TS_INC;
+
+ return status;
+}
+
+static int rx_cal_init(struct bladerf *dev)
+{
+ int status;
+
+ status = bladerf_set_sample_rate(dev, BLADERF_MODULE_RX, RX_CAL_RATE, NULL);
+ if (status != 0) {
+ return status;
+ }
+
+ status = bladerf_set_bandwidth(dev, BLADERF_MODULE_RX, RX_CAL_BW, NULL);
+ if (status != 0) {
+ return status;
+ }
+
+ status = bladerf_sync_config(dev, BLADERF_MODULE_RX,
+ BLADERF_FORMAT_SC16_Q11_META,
+ 64, 16384, 16, 1000);
+ if (status != 0) {
+ return status;
+ }
+
+ status = bladerf_enable_module(dev, BLADERF_MODULE_RX, true);
+ if (status != 0) {
+ return status;
+ }
+
+ return status;
+}
+
+int dc_calibration_rx(struct bladerf *dev,
+ struct dc_calibration_params *params,
+ size_t params_count, bool print_status)
+{
+ int status = 0;
+ int retval = 0;
+ struct rx_cal state;
+ struct rx_cal_backup backup;
+ size_t i;
+
+ memset(&state, 0, sizeof(state));
+
+ status = get_rx_cal_backup(dev, &backup);
+ if (status != 0) {
+ return status;
+ }
+
+ status = rx_cal_init(dev);
+ if (status != 0) {
+ goto out;
+ }
+
+ status = rx_cal_init_state(dev, &backup, &state);
+ if (status != 0) {
+ goto out;
+ }
+
+ for (i = 0; i < params_count && status == 0; i++) {
+ status = perform_rx_cal(&state, &params[i]);
+
+ if (status == 0 && print_status) {
+# ifdef DEBUG_DC_CALIBRATION
+ const char sol = '\n';
+ const char eol = '\n';
+# else
+ const char sol = '\r';
+ const char eol = '\0';
+# endif
+ printf("%cCalibrated @ %10" PRIu64 " Hz: I=%4d (Error: %4.2f), "
+ "Q=%4d (Error: %4.2f) ",
+ sol,
+ params[i].frequency,
+ params[i].corr_i, params[i].error_i,
+ params[i].corr_q, params[i].error_q);
+ printf("DC-LUT: Max (I=%3d, Q=%3d) Mid (I=%3d, Q=%3d)"
+ " Min (I=%3d, Q=%3d)%c",
+ params[i].max_dc_i, params[i].max_dc_q, params[i].mid_dc_i, params[i].mid_dc_q,
+ params[i].min_dc_i, params[i].min_dc_q, eol);
+ fflush(stdout);
+ }
+ }
+
+ if (print_status) {
+ putchar('\n');
+ }
+
+out:
+ free(state.samples);
+ free(state.corr_sweep);
+
+ retval = status;
+
+ status = bladerf_enable_module(dev, BLADERF_MODULE_RX, false);
+ if (status != 0 && retval == 0) {
+ retval = status;
+ }
+
+ status = set_rx_cal_backup(dev, &backup);
+ if (status != 0 && retval == 0) {
+ retval = status;
+ }
+
+ return retval;
+}
+
+
+
+/*******************************************************************************
+ * TX DC offset calibration
+ ******************************************************************************/
+
+#define TX_CAL_RATE (4000000)
+
+#define TX_CAL_RX_BW (3000000)
+#define TX_CAL_RX_LNA (BLADERF_LNA_GAIN_MAX)
+#define TX_CAL_RX_VGA1 (25)
+#define TX_CAL_RX_VGA2 (0)
+
+#define TX_CAL_TX_BW (1500000)
+
+#define TX_CAL_TS_INC (MS_TO_SAMPLES(15, TX_CAL_RATE))
+#define TX_CAL_COUNT (MS_TO_SAMPLES(5, TX_CAL_RATE))
+
+#define TX_CAL_CORR_SWEEP_LEN (4096 / 16) /* -2048:16:2048 */
+
+#define TX_CAL_DEFAULT_LB (BLADERF_LB_RF_LNA1)
+
+struct tx_cal_backup {
+ uint64_t rx_freq;
+ struct bladerf_rational_rate rx_sample_rate;
+ unsigned int rx_bandwidth;
+
+ bladerf_lna_gain rx_lna;
+ int rx_vga1;
+ int rx_vga2;
+
+ struct bladerf_rational_rate tx_sample_rate;
+ unsigned int tx_bandwidth;
+
+ bladerf_loopback loopback;
+};
+
+struct tx_cal {
+ struct bladerf *dev;
+ int16_t *samples; /* Raw samples */
+ unsigned int num_samples; /* Number of raw samples */
+ struct complexf *filt; /* Filter state */
+ struct complexf *filt_out; /* Filter output */
+ struct complexf *post_mix; /* Post-filter, mixed to baseband */
+ int16_t *sweep; /* Correction sweep */
+ float *mag; /* Magnitude results from sweep */
+ uint64_t ts; /* Timestamp */
+ bladerf_loopback loopback; /* Current loopback mode */
+ bool rx_low; /* RX tuned lower than TX */
+};
+
+/* Filter used to isolate contribution of TX LO leakage in received
+ * signal. 15th order Equiripple FIR with Fs=4e6, Fpass=1, Fstop=1e6
+ */
+static const float tx_cal_filt[] = {
+ 0.000327949366768f, 0.002460188536582f, 0.009842382390924f,
+ 0.027274728394777f, 0.057835200476419f, 0.098632713294830f,
+ 0.139062540460741f, 0.164562494987592f, 0.164562494987592f,
+ 0.139062540460741f, 0.098632713294830f, 0.057835200476419f,
+ 0.027274728394777f, 0.009842382390924f, 0.002460188536582f,
+ 0.000327949366768f,
+};
+
+static const unsigned int tx_cal_filt_num_taps =
+ (sizeof(tx_cal_filt) / sizeof(tx_cal_filt[0]));
+
+static inline int set_tx_dc_corr(struct bladerf *dev, int16_t i, int16_t q)
+{
+ int status;
+
+ status = bladerf_set_correction(dev, BLADERF_MODULE_TX,
+ BLADERF_CORR_LMS_DCOFF_I, i);
+ if (status != 0) {
+ return status;
+ }
+
+ status = bladerf_set_correction(dev, BLADERF_MODULE_TX,
+ BLADERF_CORR_LMS_DCOFF_Q, q);
+ return status;
+}
+
+static int get_tx_cal_backup(struct bladerf *dev, struct tx_cal_backup *b)
+{
+ int status;
+
+ status = bladerf_get_frequency(dev, BLADERF_MODULE_RX, &b->rx_freq);
+ if (status != 0) {
+ return status;
+ }
+
+ status = bladerf_get_rational_sample_rate(dev, BLADERF_MODULE_RX,
+ &b->rx_sample_rate);
+ if (status != 0) {
+ return status;
+ }
+
+ status = bladerf_get_bandwidth(dev, BLADERF_MODULE_RX, &b->rx_bandwidth);
+ if (status != 0) {
+ return status;
+ }
+
+ status = bladerf_get_lna_gain(dev, &b->rx_lna);
+ if (status != 0) {
+ return status;
+ }
+
+ status = bladerf_get_rxvga1(dev, &b->rx_vga1);
+ if (status != 0) {
+ return status;
+ }
+
+ status = bladerf_get_rxvga2(dev, &b->rx_vga2);
+ if (status != 0) {
+ return status;
+ }
+
+ status = bladerf_get_rational_sample_rate(dev, BLADERF_MODULE_TX,
+ &b->tx_sample_rate);
+ if (status != 0) {
+ return status;
+ }
+
+ status = bladerf_get_loopback(dev, &b->loopback);
+
+ return status;
+}
+
+static int set_tx_cal_backup(struct bladerf *dev, struct tx_cal_backup *b)
+{
+ int status;
+ int retval = 0;
+
+ status = bladerf_set_loopback(dev, b->loopback);
+ if (status != 0 && retval == 0) {
+ retval = status;
+ }
+
+ status = bladerf_set_frequency(dev, BLADERF_MODULE_RX, b->rx_freq);
+ if (status != 0 && retval == 0) {
+ retval = status;
+ }
+
+ status = bladerf_set_rational_sample_rate(dev, BLADERF_MODULE_RX,
+ &b->rx_sample_rate, NULL);
+ if (status != 0 && retval == 0) {
+ retval = status;
+ }
+
+ status = bladerf_set_bandwidth(dev, BLADERF_MODULE_RX,
+ b->rx_bandwidth, NULL);
+ if (status != 0 && retval == 0) {
+ retval = status;
+ }
+
+ status = bladerf_set_lna_gain(dev, b->rx_lna);
+ if (status != 0 && retval == 0) {
+ retval = status;
+ }
+
+ status = bladerf_set_rxvga1(dev, b->rx_vga1);
+ if (status != 0 && retval == 0) {
+ retval = status;
+ }
+
+ status = bladerf_set_rxvga2(dev, b->rx_vga2);
+ if (status != 0 && retval == 0) {
+ retval = status;
+ }
+
+ status = bladerf_set_rational_sample_rate(dev, BLADERF_MODULE_TX,
+ &b->tx_sample_rate, NULL);
+ if (status != 0 && retval == 0) {
+ retval = status;
+ }
+
+ return retval;
+}
+
+static int tx_cal_update_frequency(struct tx_cal *state, uint64_t freq)
+{
+ int status;
+ bladerf_loopback lb;
+ uint64_t rx_freq;
+
+ status = bladerf_set_frequency(state->dev, BLADERF_MODULE_TX, freq);
+ if (status != 0) {
+ return status;
+ }
+
+ rx_freq = freq - 1000000;
+ if (rx_freq < BLADERF_FREQUENCY_MIN) {
+ rx_freq = freq + 1000000;
+ state->rx_low = false;
+ } else {
+ state->rx_low = true;
+ }
+
+ status = bladerf_set_frequency(state->dev, BLADERF_MODULE_RX, rx_freq);
+ if (status != 0) {
+ return status;
+ }
+
+ if (freq < 1500000000) {
+ lb = BLADERF_LB_RF_LNA1;
+ PR_DBG("Switching to RF LNA1 loopback.\n");
+ } else {
+ lb = BLADERF_LB_RF_LNA2;
+ PR_DBG("Switching to RF LNA2 loopback.\n");
+ }
+
+ if (state->loopback != lb) {
+ status = bladerf_set_loopback(state->dev, lb);
+ if (status == 0) {
+ state->loopback = lb;
+ }
+ }
+
+ return status;
+}
+
+static int apply_tx_cal_settings(struct bladerf *dev)
+{
+ int status;
+
+ status = bladerf_set_sample_rate(dev, BLADERF_MODULE_RX, TX_CAL_RATE, NULL);
+ if (status != 0) {
+ return status;
+ }
+
+ status = bladerf_set_bandwidth(dev, BLADERF_MODULE_RX, TX_CAL_RX_BW, NULL);
+ if (status != 0) {
+ return status;
+ }
+
+ status = bladerf_set_lna_gain(dev, TX_CAL_RX_LNA);
+ if (status != 0) {
+ return status;
+ }
+
+ status = bladerf_set_rxvga1(dev, TX_CAL_RX_VGA1);
+ if (status != 0) {
+ return status;
+ }
+
+ status = bladerf_set_rxvga2(dev, TX_CAL_RX_VGA2);
+ if (status != 0) {
+ return status;
+ }
+
+ status = bladerf_set_sample_rate(dev, BLADERF_MODULE_TX, TX_CAL_RATE, NULL);
+ if (status != 0) {
+ return status;
+ }
+
+ status = bladerf_set_loopback(dev, TX_CAL_DEFAULT_LB);
+ if (status != 0) {
+ return status;
+ }
+
+ return status;
+}
+
+/* We just need to flush some zeros through the system to hole the DAC at
+ * 0+0j and remain there while letting it underrun. This alleviates the
+ * need to worry about continuously TX'ing zeros. */
+static int tx_cal_tx_init(struct bladerf *dev)
+{
+ int status;
+ int16_t zero_sample[] = { 0, 0 };
+ struct bladerf_metadata meta;
+
+ memset(&meta, 0, sizeof(meta));
+
+ status = bladerf_sync_config(dev, BLADERF_MODULE_TX,
+ BLADERF_FORMAT_SC16_Q11_META,
+ 4, 16384, 2, 1000);
+ if (status != 0) {
+ return status;
+ }
+
+ status = bladerf_enable_module(dev, BLADERF_MODULE_TX, true);
+ if (status != 0) {
+ return status;
+ }
+
+ meta.flags = BLADERF_META_FLAG_TX_BURST_START |
+ BLADERF_META_FLAG_TX_BURST_END |
+ BLADERF_META_FLAG_TX_NOW;
+
+ status = bladerf_sync_tx(dev, &zero_sample, 1, &meta, 2000);
+ return status;
+}
+
+static int tx_cal_rx_init(struct bladerf *dev)
+{
+ int status;
+
+ status = bladerf_sync_config(dev, BLADERF_MODULE_RX,
+ BLADERF_FORMAT_SC16_Q11_META,
+ 64, 16384, 32, 1000);
+ if (status != 0) {
+ return status;
+ }
+
+ status = bladerf_enable_module(dev, BLADERF_MODULE_RX, true);
+ return status;
+}
+
+static void tx_cal_state_deinit(struct tx_cal *cal)
+{
+ free(cal->sweep);
+ free(cal->mag);
+ free(cal->samples);
+ free(cal->filt);
+ free(cal->filt_out);
+ free(cal->post_mix);
+}
+
+/* This should be called immediately preceding the cal routines */
+static int tx_cal_state_init(struct bladerf *dev, struct tx_cal *cal)
+{
+ int status;
+
+ cal->dev = dev;
+ cal->num_samples = TX_CAL_COUNT;
+ cal->loopback = TX_CAL_DEFAULT_LB;
+
+ /* Interleaved SC16 Q11 samples */
+ cal->samples = malloc(2 * sizeof(cal->samples[0]) * cal->num_samples);
+ if (cal->samples == NULL) {
+ return BLADERF_ERR_MEM;
+ }
+
+ /* Filter state */
+ cal->filt = malloc(2 * sizeof(cal->filt[0]) * tx_cal_filt_num_taps);
+ if (cal->filt == NULL) {
+ return BLADERF_ERR_MEM;
+ }
+
+ /* Filter output */
+ cal->filt_out = malloc(sizeof(cal->filt_out[0]) * cal->num_samples);
+ if (cal->filt_out == NULL) {
+ return BLADERF_ERR_MEM;
+ }
+
+ /* Post-mix */
+ cal->post_mix = malloc(sizeof(cal->post_mix[0]) * cal->num_samples);
+ if (cal->post_mix == NULL) {
+ return BLADERF_ERR_MEM;
+ }
+
+ /* Correction sweep and results */
+ cal->sweep = malloc(sizeof(cal->sweep[0]) * TX_CAL_CORR_SWEEP_LEN);
+ if (cal->sweep == NULL) {
+ return BLADERF_ERR_MEM;
+ }
+
+ cal->mag = malloc(sizeof(cal->mag[0]) * TX_CAL_CORR_SWEEP_LEN);
+ if (cal->mag == NULL) {
+ return BLADERF_ERR_MEM;
+ }
+
+ /* Set initial RX in the future */
+ status = bladerf_get_timestamp(cal->dev, BLADERF_MODULE_RX, &cal->ts);
+ if (status == 0) {
+ cal->ts += 20 * TX_CAL_TS_INC;
+ }
+
+ return status;
+}
+
+/* Filter samples
+ * Input: state->post_mix
+ * Output: state->filt_out
+ */
+static void tx_cal_filter(struct tx_cal *state)
+{
+ unsigned int n, m;
+ struct complexf *ins1, *ins2;
+ struct complexf *curr; /* Current filter state */
+ const struct complexf *filt_end = &state->filt[2 * tx_cal_filt_num_taps];
+
+ /* Reset filter state */
+ ins1 = &state->filt[0];
+ ins2 = &state->filt[tx_cal_filt_num_taps];
+ memset(state->filt, 0, 2 * sizeof(state->filt[0]) * tx_cal_filt_num_taps);
+
+ for (n = 0; n < state->num_samples; n++) {
+ /* Insert sample */
+ *ins1 = *ins2 = state->post_mix[n];
+
+ /* Convolve */
+ state->filt_out[n].i = 0;
+ state->filt_out[n].q = 0;
+ curr = ins2;
+
+ for (m = 0; m < tx_cal_filt_num_taps; m++, curr--) {
+ state->filt_out[n].i += tx_cal_filt[m] * curr->i;
+ state->filt_out[n].q += tx_cal_filt[m] * curr->q;
+ }
+
+ /* Update insertion points */
+ ins2++;
+ if (ins2 == filt_end) {
+ ins1 = &state->filt[0];
+ ins2 = &state->filt[tx_cal_filt_num_taps];
+ } else {
+ ins1++;
+ }
+
+ }
+}
+
+/* Deinterleave, scale, and mix with an -Fs/4 tone to shift TX DC offset out at
+ * Fs/4 to baseband.
+ * Input: state->samples
+ * Output: state->post_mix
+ */
+static void tx_cal_mix(struct tx_cal *state)
+{
+ unsigned int n, m;
+ int mix_state;
+ float scaled_i, scaled_q;
+
+ /* Mix with -Fs/4 if RX is tuned "lower" than TX, and Fs/4 otherwise */
+ const int mix_state_inc = state->rx_low ? 1 : -1;
+ mix_state = 0;
+
+ for (n = 0, m = 0; n < (2 * state->num_samples); n += 2, m++) {
+ scaled_i = state->samples[n] / 2048.0f;
+ scaled_q = state->samples[n+1] / 2048.0f;
+
+ switch (mix_state) {
+ case 0:
+ state->post_mix[m].i = scaled_i;
+ state->post_mix[m].q = scaled_q;
+ break;
+
+ case 1:
+ state->post_mix[m].i = scaled_q;
+ state->post_mix[m].q = -scaled_i;
+ break;
+
+ case 2:
+ state->post_mix[m].i = -scaled_i;
+ state->post_mix[m].q = -scaled_q;
+ break;
+
+ case 3:
+ state->post_mix[m].i = -scaled_q;
+ state->post_mix[m].q = scaled_i;
+ break;
+ }
+
+ mix_state = (mix_state + mix_state_inc) & 0x3;
+ }
+}
+
+static int tx_cal_avg_magnitude(struct tx_cal *state, float *avg_mag)
+{
+ int status;
+ const unsigned int start = (tx_cal_filt_num_taps + 1) / 2;
+ unsigned int n;
+ float accum;
+
+ /* Fetch samples at the current settings */
+ status = rx_samples(state->dev, state->samples, state->num_samples,
+ &state->ts, TX_CAL_TS_INC);
+ if (status != 0) {
+ return status;
+ }
+
+ /* Deinterleave & mix TX's DC offset contribution to baseband */
+ tx_cal_mix(state);
+
+ /* Filter out everything other than the TX DC offset's contribution */
+ tx_cal_filter(state);
+
+ /* Compute the power (magnitude^2 to alleviate need for square root).
+ * We skip samples here to account for the group delay of the filter;
+ * the initial samples will be ramping up. */
+ accum = 0;
+ for (n = start; n < state->num_samples; n++) {
+ const struct complexf *s = &state->filt_out[n];
+ const float m = (float) sqrt(s->i * s->i + s->q * s->q);
+ accum += m;
+ }
+
+ *avg_mag = (accum / (state->num_samples - start));
+
+ /* Scale this back up to DAC/ADC counts, just for convenience */
+ *avg_mag *= 2048.0;
+
+ return status;
+}
+
+/* Apply the correction value and read the TX DC offset magnitude */
+static int tx_cal_measure_correction(struct tx_cal *state,
+ bladerf_correction c,
+ int16_t value, float *mag)
+{
+ int status;
+
+ status = bladerf_set_correction(state->dev, BLADERF_MODULE_TX, c, value);
+ if (status != 0) {
+ return status;
+ }
+
+ state->ts += TX_CAL_TS_INC;
+
+ status = tx_cal_avg_magnitude(state, mag);
+ if (status == 0) {
+ PR_VERBOSE(" Corr=%5d, Avg_magnitude=%f\n", value, *mag);
+ }
+
+ return status;
+}
+
+static int tx_cal_get_corr(struct tx_cal *state, bool i_ch,
+ int16_t *corr_value, float *error_value)
+{
+ int status;
+ unsigned int n;
+ int16_t corr;
+ float mag[4];
+ float m1, m2, b1, b2;
+ int16_t range_min, range_max;
+ int16_t min_corr;
+ float min_mag;
+
+ const int16_t x[4] = { -1800, -1000, 1000, 1800 };
+
+ const bladerf_correction corr_module =
+ i_ch ? BLADERF_CORR_LMS_DCOFF_I : BLADERF_CORR_LMS_DCOFF_Q;
+
+ PR_DBG("Getting coarse estimate for %c\n", i_ch ? 'I' : 'Q');
+
+ for (n = 0; n < 4; n++) {
+ status = tx_cal_measure_correction(state, corr_module, x[n], &mag[n]);
+ if (status != 0) {
+ return status;
+ }
+
+ }
+
+ m1 = (mag[1] - mag[0]) / (x[1] - x[0]);
+ b1 = mag[0] - m1 * x[0];
+
+ m2 = (mag[3] - mag[2]) / (x[3] - x[2]);
+ b2 = mag[2] - m2 * x[2];
+
+ PR_VERBOSE(" m1=%3.8f, b1=%3.8f, m2=%3.8f, b=%3.8f\n", m1, b1, m2, b2);
+
+ if (m1 < 0 && m2 > 0) {
+ const int16_t tmp = (int16_t)((b2 - b1) / (m1 - m2) + 0.5);
+ const int16_t corr_est = (tmp / 16) * 16;
+
+ /* Number of points to sweep on either side of our estimate */
+ const unsigned int n_sweep = 10;
+
+ PR_VERBOSE(" corr_est=%d\n", corr_est);
+
+ range_min = corr_est - 16 * n_sweep;
+ if (range_min < -2048) {
+ range_min = -2048;
+ }
+
+ range_max = corr_est + 16 * n_sweep;
+ if (range_max > 2048) {
+ range_max = 2048;
+ }
+
+ } else {
+ /* The frequency and gain combination have yielded a set of
+ * points that do not form intersecting lines. This may be indicative
+ * of a case where the LMS6 DC bias settings can't pull the DC offset
+ * to a zero-crossing. We'll just do a slow, full scan to find
+ * a minimum */
+ PR_VERBOSE(" Could not compute estimate. Performing full sweep.\n");
+ range_min = -2048;
+ range_max = 2048;
+ }
+
+
+ PR_DBG("Performing correction value sweep: [%-5d : 16 :%5d]\n",
+ range_min, range_max);
+
+ min_corr = 0;
+ min_mag = 2048;
+
+ for (n = 0, corr = range_min;
+ corr <= range_max && n < TX_CAL_CORR_SWEEP_LEN;
+ n++, corr += 16) {
+
+ float tmp;
+
+ status = tx_cal_measure_correction(state, corr_module, corr, &tmp);
+ if (status != 0) {
+ return status;
+ }
+
+ if (tmp < 0) {
+ tmp = -tmp;
+ }
+
+ if (tmp < min_mag) {
+ min_corr = corr;
+ min_mag = tmp;
+ }
+ }
+
+ /* Leave the device set to the minimum */
+ status = bladerf_set_correction(state->dev, BLADERF_MODULE_TX,
+ corr_module, min_corr);
+ if (status == 0) {
+ *corr_value = min_corr;
+ *error_value = min_mag;
+ }
+
+ return status;
+}
+
+static int perform_tx_cal(struct tx_cal *state, struct dc_calibration_params *p)
+{
+ int status = 0;
+
+ status = tx_cal_update_frequency(state, p->frequency);
+ if (status != 0) {
+ return status;
+ }
+
+ state->ts += TX_CAL_TS_INC;
+
+ /* Perform I calibration */
+ status = tx_cal_get_corr(state, true, &p->corr_i, &p->error_i);
+ if (status != 0) {
+ return status;
+ }
+
+ /* Perform Q calibration */
+ status = tx_cal_get_corr(state, false, &p->corr_q, &p->error_q);
+ if (status != 0) {
+ return status;
+ }
+
+ /* Re-do I calibration to try to further fine-tune result */
+ status = tx_cal_get_corr(state, true, &p->corr_i, &p->error_i);
+ if (status != 0) {
+ return status;
+ }
+
+ /* Apply the resulting nominal values */
+ status = set_tx_dc_corr(state->dev, p->corr_i, p->corr_q);
+
+ return status;
+}
+
+int dc_calibration_tx(struct bladerf *dev,
+ struct dc_calibration_params *params,
+ size_t num_params, bool print_status)
+{
+ int status = 0;
+ int retval = 0;
+ struct tx_cal_backup backup;
+ struct tx_cal state;
+ size_t i;
+
+ memset(&state, 0, sizeof(state));
+
+ /* Backup the device state prior to making changes */
+ status = get_tx_cal_backup(dev, &backup);
+ if (status != 0) {
+ return status;
+ }
+
+ /* Configure the device for our TX cal operation */
+ status = apply_tx_cal_settings(dev);
+ if (status != 0) {
+ goto out;
+ }
+
+ /* Enable TX and run zero samples through the device */
+ status = tx_cal_tx_init(dev);
+ if (status != 0) {
+ goto out;
+ }
+
+ /* Enable RX */
+ status = tx_cal_rx_init(dev);
+ if (status != 0) {
+ goto out;
+ }
+
+ /* Initialize calibration state information and resources */
+ status = tx_cal_state_init(dev, &state);
+ if (status != 0) {
+ goto out;
+ }
+
+ for (i = 0; i < num_params && status == 0; i++) {
+ status = perform_tx_cal(&state, &params[i]);
+
+ if (status == 0 && print_status) {
+# ifdef DEBUG_DC_CALIBRATION
+ const char sol = '\n';
+ const char eol = '\n';
+# else
+ const char sol = '\r';
+ const char eol = '\0';
+# endif
+ printf("%cCalibrated @ %10" PRIu64 " Hz: "
+ "I=%4d (Error: %4.2f), "
+ "Q=%4d (Error: %4.2f) %c",
+ sol,
+ params[i].frequency,
+ params[i].corr_i, params[i].error_i,
+ params[i].corr_q, params[i].error_q,
+ eol);
+ fflush(stdout);
+ }
+ }
+
+ if (print_status) {
+ putchar('\n');
+ }
+
+out:
+ retval = status;
+
+ status = bladerf_enable_module(dev, BLADERF_MODULE_RX, false);
+ if (status != 0 && retval == 0) {
+ retval = status;
+ }
+
+ status = bladerf_enable_module(dev, BLADERF_MODULE_TX, false);
+ if (status != 0 && retval == 0) {
+ retval = status;
+ }
+
+ tx_cal_state_deinit(&state);
+
+ status = set_tx_cal_backup(dev, &backup);
+ if (status != 0 && retval == 0) {
+ retval = status;
+ }
+
+ return retval;
+}
+
+int dc_calibration(struct bladerf *dev, bladerf_module module,
+ struct dc_calibration_params *params,
+ size_t num_params, bool show_status)
+{
+ int status;
+
+ switch (module) {
+ case BLADERF_MODULE_RX:
+ status = dc_calibration_rx(dev, params, num_params, show_status);
+ break;
+
+ case BLADERF_MODULE_TX:
+ status = dc_calibration_tx(dev, params, num_params, show_status);
+ break;
+
+ default:
+ status = BLADERF_ERR_INVAL;
+ }
+
+ return status;
+}
diff --git a/Radio/HW/BladeRF/common/src/devcfg.c b/Radio/HW/BladeRF/common/src/devcfg.c
new file mode 100644
index 0000000..497c54b
--- /dev/null
+++ b/Radio/HW/BladeRF/common/src/devcfg.c
@@ -0,0 +1,638 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2014-2015 Nuand LLC
+ *
+ * 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 <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <limits.h>
+#include <string.h>
+#include <errno.h>
+#include <getopt.h>
+
+#include "devcfg.h"
+#include "conversions.h"
+
+#define DEVCFG_DFLT_TX_FREQ 1000000000
+#define DEVCFG_DFLT_TXVGA1 (-14)
+#define DEVCFG_DFLT_TXVGA2 0
+
+#define DEVCFG_DFLT_RX_FREQ 1000000000
+#define DEVCFG_DFLT_LNAGAIN BLADERF_LNA_GAIN_MAX
+#define DEVCFG_DFLT_RXVGA1 30
+#define DEVCFG_DFLT_RXVGA2 0
+
+#define DEVCFG_DFLT_SAMPLERATE 2000000
+#define DEVCFG_DFLT_BANDWIDTH 1500000
+
+#define DEVCFG_DFLT_SAMPLES_PER_BUF 8192
+#define DEVCFG_DFLT_NUM_BUFFERS 64
+#define DEVCFG_DFLT_NUM_TRANSFERS 16
+#define DEVCFG_DFLT_STREAM_TIMEMOUT_MS 5000
+#define DEVCFG_DFLT_SYNC_TIMEOUT_MS (2 * DEVCFG_DFLT_STREAM_TIMEMOUT_MS)
+
+#define OPTION_HELP 'h'
+#define OPTION_DEVICE 'd'
+#define OPTION_LOOPBACK 'l'
+#define OPTION_VERBOSITY 'v'
+#define OPTION_SAMPLERATE 's'
+#define OPTION_RX_SAMPLERATE 0x80
+#define OPTION_TX_SAMPLERATE 0x81
+#define OPTION_BANDWIDTH 'b'
+#define OPTION_RX_BANDWIDTH 0x82
+#define OPTION_TX_BANDWIDTH 0x83
+#define OPTION_FREQUENCY 'f'
+#define OPTION_RX_FREQUENCY 0x84
+#define OPTION_TX_FREQUENCY 0x85
+#define OPTION_LNAGAIN 0x86
+#define OPTION_RXVGA1 0x87
+#define OPTION_RXVGA2 0x88
+#define OPTION_TXVGA1 0x89
+#define OPTION_TXVGA2 0x8a
+#define OPTION_SAMPLES_PER_BUF 0x91
+#define OPTION_NUM_BUFFERS 0x92
+#define OPTION_NUM_TRANSFERS 0x93
+#define OPTION_STREAM_TIMEOUT 0x94
+#define OPTION_SYNC_TIMEOUT 0x95
+#define OPTION_ENABLE_XB200 0x96
+
+static const numeric_suffix hz_suffixes[] = {
+ { "K", 1000 },
+ { "KHz", 1000 },
+ { "M", 1000000 },
+ { "MHz", 1000000 },
+ { "G", 1000000000 },
+ { "GHz", 1000000000 },
+};
+
+static const struct option devcfg_long_options[] =
+{
+ { "help", no_argument, 0, OPTION_HELP },
+ { "device", required_argument, 0, OPTION_DEVICE },
+ { "verbosity", required_argument, 0, OPTION_VERBOSITY },
+
+ { "samplerate", required_argument, 0, OPTION_SAMPLERATE },
+ { "rx-samplerate", required_argument, 0, OPTION_RX_SAMPLERATE },
+ { "tx-samplerate", required_argument, 0, OPTION_TX_SAMPLERATE },
+
+ { "bandwidth", required_argument, 0, OPTION_BANDWIDTH },
+ { "rx-bandwidth", required_argument, 0, OPTION_RX_BANDWIDTH },
+ { "tx-bandwidth", required_argument, 0, OPTION_TX_BANDWIDTH },
+
+ { "frequency", required_argument, 0, OPTION_FREQUENCY },
+ { "rx-frequency", required_argument, 0, OPTION_RX_FREQUENCY },
+ { "tx-frequency", required_argument, 0, OPTION_TX_FREQUENCY },
+
+ { "lna-gain", required_argument, 0, OPTION_LNAGAIN },
+ { "rxvga1-gain", required_argument, 0, OPTION_RXVGA1 },
+ { "rxvga2-gain", required_argument, 0, OPTION_RXVGA2 },
+
+ { "txvga1-gain", required_argument, 0, OPTION_TXVGA1 },
+ { "txvga2-gain", required_argument, 0, OPTION_TXVGA2 },
+
+ { "loopback", required_argument, 0, OPTION_LOOPBACK },
+
+ { "xb200", no_argument, 0, OPTION_ENABLE_XB200 },
+
+ { "samples-per-buffer", required_argument, 0, OPTION_SAMPLES_PER_BUF },
+ { "num-buffers", required_argument, 0, OPTION_NUM_BUFFERS },
+ { "num-transfers", required_argument, 0, OPTION_NUM_TRANSFERS },
+ { "stream-timeout", required_argument, 0, OPTION_STREAM_TIMEOUT },
+ { "sync-timeout", required_argument, 0, OPTION_SYNC_TIMEOUT },
+};
+
+void devcfg_init(struct devcfg *c)
+{
+ c->device_specifier = NULL;
+
+ c->tx_frequency = DEVCFG_DFLT_TX_FREQ;
+ c->tx_bandwidth = DEVCFG_DFLT_BANDWIDTH;
+ c->tx_samplerate = DEVCFG_DFLT_SAMPLERATE;
+ c->txvga1 = DEVCFG_DFLT_TXVGA1;
+ c->txvga2 = DEVCFG_DFLT_TXVGA2;
+
+ c->rx_frequency = DEVCFG_DFLT_TX_FREQ;
+ c->rx_bandwidth = DEVCFG_DFLT_BANDWIDTH;
+ c->rx_samplerate = DEVCFG_DFLT_SAMPLERATE;
+ c->lnagain = DEVCFG_DFLT_LNAGAIN;
+ c->rxvga1 = DEVCFG_DFLT_RXVGA1;
+ c->rxvga2 = DEVCFG_DFLT_RXVGA2;
+
+ c->loopback = BLADERF_LB_NONE;
+
+ c->verbosity = BLADERF_LOG_LEVEL_INFO;
+
+ c->enable_xb200 = false;
+
+ c->samples_per_buffer = DEVCFG_DFLT_SAMPLES_PER_BUF;
+ c->num_buffers = DEVCFG_DFLT_NUM_BUFFERS;
+ c->num_transfers = DEVCFG_DFLT_NUM_TRANSFERS;
+ c->stream_timeout_ms = DEVCFG_DFLT_STREAM_TIMEMOUT_MS;
+ c->sync_timeout_ms = DEVCFG_DFLT_SYNC_TIMEOUT_MS;
+}
+
+void devcfg_deinit(struct devcfg *c)
+{
+ free(c->device_specifier);
+}
+
+void devcfg_print_common_help(const char *title)
+{
+ if (title != NULL) {
+ printf("%s", title);
+ }
+
+ printf(" -h, --help Show this help text.\n");
+ printf(" -d, --device <str> Open the specified device.\n");
+ printf(" -v, --verbosity <level> Set the libbladeRF verbosity level.\n");
+ printf(" --xb200 Enable the XB-200. This will remain enabled\n");
+ printf(" until the device is power-cycled.\n");
+ printf("\n");
+ printf(" -f, --frequency <freq> Set RX and TX to the specified frequency.\n");
+ printf(" --rx-frequency <freq> Set RX to the specified frequency.\n");
+ printf(" --tx-frequency <freq> Set TX to the specified frequency.\n");
+ printf("\n");
+ printf(" -s, --samplerate <rate> Set RX and TX to the specified sample rate.\n");
+ printf(" --rx-samplerate <rate> Set RX to the specified sample rate.\n");
+ printf(" --tx-samplerate <rate> Set RX to the specified sample rate.\n");
+ printf("\n");
+ printf(" -b, --bandwidth <bw> Set RX and TX to the specified bandwidth.\n");
+ printf(" --rx-bandwidth <bw> Set RX to the specified bandwidth.\n");
+ printf(" --tx-bandwidth <bw> Set TX to the specified bandwidth.\n");
+ printf("\n");
+ printf(" --lna-gain <gain> Set the RX LNA to the specified gain.\n");
+ printf(" Options are: bypass, mid, max\n");
+ printf(" --rxvga1-gain <gain> Set RX VGA1 to the specified gain.\n");
+ printf(" --rxvga2-gain <gain> Set RX VGA2 to the specified gain.\n");
+ printf(" --txvga1-gain <gain> Set TX VGA1 to the specified gain.\n");
+ printf(" --txvga2-gain <gain> Set TX VGA2 to the specified gain.\n");
+ printf("\n");
+ printf(" --num-buffers <n> Allocate <n> sample buffers.\n");
+ printf(" --samples-per-buffer <n> Allocate <n> samples in each sample buffer.\n");
+ printf(" --num-transfers <n> Utilize up to <n> simultaneous USB transfers.\n");
+ printf(" --stream-timeout <n> Set stream timeout to <n> milliseconds.\n");
+ printf(" --sync-timeout <n> Set sync function timeout to <n> milliseconds.\n");
+
+}
+
+struct option * devcfg_get_long_options(const struct option *app_options)
+{
+ struct option *ret;
+ size_t app_size = 0;
+ const size_t devcfg_size = sizeof(devcfg_long_options);
+
+ while (app_options[app_size].name != NULL) {
+ app_size++;
+ }
+
+ /* Account for 0-entry */
+ app_size = (app_size + 1) * sizeof(app_options[0]);
+
+ ret = malloc(devcfg_size + app_size);
+
+ if (ret != NULL) {
+ memcpy(ret, &devcfg_long_options, devcfg_size);
+ memcpy(ret + ARRAY_SIZE(devcfg_long_options) , app_options, app_size);
+ }
+
+ return ret;
+}
+
+int devcfg_handle_args(int argc, char **argv, const char *option_str,
+ const struct option *long_options, struct devcfg *config)
+{
+ int c;
+ bool ok;
+ int status;
+ unsigned int freq_min;
+
+ while ((c = getopt_long(argc, argv, option_str, long_options, NULL)) >= 0) {
+
+ if (config->enable_xb200) {
+ freq_min = BLADERF_FREQUENCY_MIN_XB200;
+ } else {
+ freq_min = BLADERF_FREQUENCY_MIN;
+ }
+
+ switch (c) {
+
+ case OPTION_HELP:
+ return 1;
+ break;
+
+ case OPTION_DEVICE:
+ if (config->device_specifier == NULL) {
+ config->device_specifier = strdup(optarg);
+ if (config->device_specifier == NULL) {
+ perror("strdup");
+ return -1;
+ }
+ }
+ break;
+
+ case OPTION_LOOPBACK:
+ status = str2loopback(optarg, &config->loopback);
+ if (status != 0) {
+ fprintf(stderr, "Invalid loopback mode: %s\n", optarg);
+ return -1;
+ }
+ break;
+
+ case OPTION_VERBOSITY:
+ config->verbosity = str2loglevel(optarg, &ok);
+ if (!ok) {
+ fprintf(stderr, "Invalid verbosity level: %s\n", optarg);
+ return -1;
+ }
+ break;
+
+ case OPTION_ENABLE_XB200:
+ config->enable_xb200 = true;
+ break;
+
+ case OPTION_FREQUENCY:
+ config->rx_frequency =
+ str2uint_suffix(optarg,
+ freq_min,
+ BLADERF_FREQUENCY_MAX,
+ hz_suffixes, ARRAY_SIZE(hz_suffixes),
+ &ok);
+
+ if (!ok) {
+ fprintf(stderr, "Invalid frequency: %s\n", optarg);
+ return -1;
+ } else {
+ config->tx_frequency = config->rx_frequency;
+ }
+ break;
+
+ case OPTION_RX_FREQUENCY:
+ config->rx_frequency =
+ str2uint_suffix(optarg,
+ freq_min,
+ BLADERF_FREQUENCY_MAX,
+ hz_suffixes, ARRAY_SIZE(hz_suffixes),
+ &ok);
+
+ if (!ok) {
+ fprintf(stderr, "Invalid RX frequency: %s\n", optarg);
+ return -1;
+ }
+ break;
+
+ case OPTION_TX_FREQUENCY:
+ config->tx_frequency =
+ str2uint_suffix(optarg,
+ freq_min,
+ BLADERF_FREQUENCY_MAX,
+ hz_suffixes, ARRAY_SIZE(hz_suffixes),
+ &ok);
+
+ if (!ok) {
+ fprintf(stderr, "Invalid TX frequency: %s\n", optarg);
+ return -1;
+ }
+ break;
+
+ case OPTION_SAMPLERATE:
+ config->rx_samplerate =
+ str2uint_suffix(optarg,
+ BLADERF_SAMPLERATE_MIN,
+ BLADERF_SAMPLERATE_REC_MAX,
+ hz_suffixes, ARRAY_SIZE(hz_suffixes),
+ &ok);
+ if (!ok) {
+ fprintf(stderr, "Invalid sample rate: %s\n", optarg);
+ return -1;
+ } else {
+ config->tx_samplerate = config->rx_samplerate;
+ }
+ break;
+
+ case OPTION_RX_SAMPLERATE:
+ config->rx_samplerate =
+ str2uint_suffix(optarg,
+ BLADERF_SAMPLERATE_MIN,
+ BLADERF_SAMPLERATE_REC_MAX,
+ hz_suffixes, ARRAY_SIZE(hz_suffixes),
+ &ok);
+ if (!ok) {
+ fprintf(stderr, "Invalid RX sample rate: %s\n", optarg);
+ return -1;
+ }
+ break;
+
+ case OPTION_TX_SAMPLERATE:
+ config->tx_samplerate =
+ str2uint_suffix(optarg,
+ BLADERF_SAMPLERATE_MIN,
+ BLADERF_SAMPLERATE_REC_MAX,
+ hz_suffixes, ARRAY_SIZE(hz_suffixes),
+ &ok);
+ if (!ok) {
+ fprintf(stderr, "Invalid TX sample rate: %s\n", optarg);
+ return -1;
+ }
+ break;
+
+ case OPTION_BANDWIDTH:
+ config->rx_bandwidth =
+ str2uint_suffix(optarg,
+ BLADERF_BANDWIDTH_MIN,
+ BLADERF_BANDWIDTH_MAX,
+ hz_suffixes, ARRAY_SIZE(hz_suffixes),
+ &ok);
+ if (!ok) {
+ fprintf(stderr, "Invalid bandwidth: %s\n", optarg);
+ return -1;
+ } else {
+ config->tx_bandwidth = config->rx_bandwidth;
+ }
+ break;
+
+ case OPTION_RX_BANDWIDTH:
+ config->rx_bandwidth =
+ str2uint_suffix(optarg,
+ BLADERF_BANDWIDTH_MIN,
+ BLADERF_BANDWIDTH_MAX,
+ hz_suffixes, ARRAY_SIZE(hz_suffixes),
+ &ok);
+ if (!ok) {
+ fprintf(stderr, "Invalid RX bandwidth: %s\n", optarg);
+ return -1;
+ }
+ break;
+
+ case OPTION_TX_BANDWIDTH:
+ config->tx_bandwidth =
+ str2uint_suffix(optarg,
+ BLADERF_BANDWIDTH_MIN,
+ BLADERF_BANDWIDTH_MAX,
+ hz_suffixes, ARRAY_SIZE(hz_suffixes),
+ &ok);
+ if (!ok) {
+ fprintf(stderr, "Invalid TX bandwidth: %s\n", optarg);
+ return -1;
+ }
+ break;
+
+ case OPTION_LNAGAIN:
+ status = str2lnagain(optarg, &config->lnagain);
+ if (status != 0) {
+ fprintf(stderr, "Invalid RX LNA gain: %s\n", optarg);
+ return -1;
+ }
+ break;
+
+ case OPTION_RXVGA1:
+ config->rxvga1 = str2int(optarg,
+ BLADERF_RXVGA1_GAIN_MIN,
+ BLADERF_RXVGA1_GAIN_MAX,
+ &ok);
+
+ if (!ok) {
+ fprintf(stderr, "Invalid RXVGA1 gain: %s\n", optarg);
+ return -1;
+ }
+ break;
+
+ case OPTION_RXVGA2:
+ config->rxvga2 = str2int(optarg,
+ BLADERF_RXVGA2_GAIN_MIN,
+ BLADERF_RXVGA2_GAIN_MAX,
+ &ok);
+
+ if (!ok) {
+ fprintf(stderr, "Invalid RXVGA1 gain: %s\n", optarg);
+ return -1;
+ }
+ break;
+
+ case OPTION_TXVGA1:
+ config->txvga1 = str2int(optarg,
+ BLADERF_TXVGA1_GAIN_MIN,
+ BLADERF_TXVGA1_GAIN_MAX,
+ &ok);
+
+ if (!ok) {
+ fprintf(stderr, "Invalid TXVGA1 gain: %s\n", optarg);
+ return -1;
+ }
+ break;
+
+ case OPTION_TXVGA2:
+ config->txvga2 = str2int(optarg,
+ BLADERF_TXVGA2_GAIN_MIN,
+ BLADERF_TXVGA2_GAIN_MAX,
+ &ok);
+
+ if (!ok) {
+ fprintf(stderr, "Invalid TXVGA2 gain: %s\n", optarg);
+ return -1;
+ }
+ break;
+
+ case OPTION_NUM_BUFFERS:
+ config->num_buffers = str2uint(optarg, 1, UINT_MAX, &ok);
+ if (!ok) {
+ fprintf(stderr, "Invalid buffer count: %s\n", optarg);
+ return -1;
+ }
+ break;
+
+ case OPTION_SAMPLES_PER_BUF:
+ config->samples_per_buffer = str2uint(optarg,
+ 1024, UINT_MAX, &ok);
+ if (!ok) {
+ fprintf(stderr, "Invalid buffer size (in samples): %s\n",
+ optarg);
+ return -1;
+ }
+ break;
+
+ case OPTION_NUM_TRANSFERS:
+ config->num_transfers = str2uint(optarg, 1, UINT_MAX, &ok);
+
+ if (!ok) {
+ fprintf(stderr, "Invalid transfer count: %s\n", optarg);
+ return -1;
+ }
+ break;
+
+ case OPTION_STREAM_TIMEOUT:
+ config->stream_timeout_ms = str2uint(optarg, 0, UINT_MAX, &ok);
+ if (!ok) {
+ fprintf(stderr, "Invalid stream timeout: %s\n", optarg);
+ return -1;
+ }
+ break;
+
+ case OPTION_SYNC_TIMEOUT:
+ config->sync_timeout_ms = str2uint(optarg, 0, UINT_MAX, &ok);
+ if (!ok) {
+ fprintf(stderr, "Invalid sync function timeout: %s\n", optarg);
+ return -1;
+ }
+ break;
+ }
+ }
+
+ return 0;
+}
+
+int devcfg_apply(struct bladerf *dev, const struct devcfg *c)
+{
+ int status;
+ const char *board_name;
+
+ board_name = bladerf_get_board_name(dev);
+
+ bladerf_log_set_verbosity(c->verbosity);
+
+ status = bladerf_set_loopback(dev, c->loopback);
+ if (status != 0) {
+ fprintf(stderr, "Failed to set loopback: %s\n",
+ bladerf_strerror(status));
+ return -1;
+ }
+
+ if (c->enable_xb200) {
+ status = bladerf_expansion_attach(dev, BLADERF_XB_200);
+ if (status != 0) {
+ fprintf(stderr, "Failed to attach XB-200.\n");
+ return -1;
+ }
+ }
+
+ status = bladerf_set_frequency(dev, BLADERF_MODULE_RX, c->rx_frequency);
+ if (status != 0) {
+ fprintf(stderr, "Failed to set RX frequency: %s\n",
+ bladerf_strerror(status));
+ return -1;
+ }
+
+ status = bladerf_set_frequency(dev, BLADERF_MODULE_TX, c->tx_frequency);
+ if (status != 0) {
+ fprintf(stderr, "Failed to set TX frequency: %s\n",
+ bladerf_strerror(status));
+ return -1;
+ }
+
+ status = bladerf_set_sample_rate(dev, BLADERF_MODULE_RX, c->rx_samplerate, NULL);
+ if (status != 0) {
+ fprintf(stderr, "Failed to set RX sample rate:%s\n",
+ bladerf_strerror(status));
+ return -1;
+ }
+
+ status = bladerf_set_sample_rate(dev, BLADERF_MODULE_TX, c->tx_samplerate, NULL);
+ if (status != 0) {
+ fprintf(stderr, "Failed to set TX sample rate: %s\n",
+ bladerf_strerror(status));
+ return -1;
+ }
+
+ status = bladerf_set_bandwidth(dev, BLADERF_MODULE_RX, c->rx_bandwidth, NULL);
+ if (status != 0) {
+ fprintf(stderr, "Failed to set RX bandwidth: %s\n",
+ bladerf_strerror(status));
+ return -1;
+ }
+
+ status = bladerf_set_bandwidth(dev, BLADERF_MODULE_TX, c->tx_bandwidth, NULL);
+ if (status != 0) {
+ fprintf(stderr, "Failed to set RX bandwidth: %s\n",
+ bladerf_strerror(status));
+ return -1;
+ }
+
+ if (strcasecmp(board_name, "bladerf1") == 0) {
+ status = bladerf_set_lna_gain(dev, c->lnagain);
+ if (status != 0) {
+ fprintf(stderr, "Failed to set RX LNA gain: %s\n",
+ bladerf_strerror(status));
+ return -1;
+ }
+
+ status = bladerf_set_rxvga1(dev, c->rxvga1);
+ if (status != 0) {
+ fprintf(stderr, "Failed to set RX VGA1 gain: %s\n",
+ bladerf_strerror(status));
+ return -1;
+ }
+
+ status = bladerf_set_rxvga2(dev, c->rxvga2);
+ if (status != 0) {
+ fprintf(stderr, "Failed to set RX VGA2 gain: %s\n",
+ bladerf_strerror(status));
+ return -1;
+ }
+
+ status = bladerf_set_txvga1(dev, c->txvga1);
+ if (status != 0) {
+ fprintf(stderr, "Failed to set TX VGA1 gain: %s\n",
+ bladerf_strerror(status));
+ return -1;
+ }
+
+ status = bladerf_set_txvga2(dev, c->txvga2);
+ if (status != 0) {
+ fprintf(stderr, "Failed to set TX VGA2 gain: %s\n",
+ bladerf_strerror(status));
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+int devcfg_perform_sync_config(struct bladerf *dev,
+ bladerf_module module,
+ bladerf_format format,
+ const struct devcfg *config,
+ bool enable_module)
+{
+ int status = bladerf_sync_config(dev, module, format,
+ config->num_buffers,
+ config->samples_per_buffer,
+ config->num_transfers,
+ config->stream_timeout_ms);
+
+ if (status != 0) {
+ fprintf(stderr, "Failed to configure %s: %s\n",
+ module == BLADERF_MODULE_RX ? "RX" : "TX",
+ bladerf_strerror(status));
+ return -1;
+ }
+
+ if (enable_module) {
+ status = bladerf_enable_module(dev, module, true);
+ if (status != 0) {
+ fprintf(stderr, "Failed to enable %s module: %s\n",
+ module == BLADERF_MODULE_RX ? "RX" : "TX",
+ bladerf_strerror(status));
+ }
+ }
+
+ return 0;
+}
diff --git a/Radio/HW/BladeRF/common/src/log.c b/Radio/HW/BladeRF/common/src/log.c
new file mode 100644
index 0000000..2872620
--- /dev/null
+++ b/Radio/HW/BladeRF/common/src/log.c
@@ -0,0 +1,98 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (c) 2013 Nuand LLC.
+ *
+ * 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.
+ */
+#ifdef LOGGING_ENABLED
+#include <log.h>
+#if !defined(WIN32) && !defined(__CYGWIN__) && defined(LOG_SYSLOG_ENABLED)
+#include <syslog.h>
+#endif
+#include <stdio.h>
+#include <stdarg.h>
+
+static bladerf_log_level filter_level = BLADERF_LOG_LEVEL_INFO;
+
+void log_write(bladerf_log_level level, const char *format, ...)
+{
+ /* Only process this message if its level exceeds the current threshold */
+ if (level >= filter_level)
+ {
+ va_list args;
+
+ /* Write the log message */
+ va_start(args, format);
+#if defined(WIN32) || defined(__CYGWIN__)
+ vfprintf(stderr, format, args);
+#else
+# if defined (LOG_SYSLOG_ENABLED)
+ {
+ int syslog_level;
+
+ switch (level) {
+ case BLADERF_LOG_LEVEL_VERBOSE:
+ case BLADERF_LOG_LEVEL_DEBUG:
+ syslog_level = LOG_DEBUG;
+ break;
+
+ case BLADERF_LOG_LEVEL_INFO:
+ syslog_level = LOG_INFO;
+ break;
+
+ case BLADERF_LOG_LEVEL_WARNING:
+ syslog_level = LOG_WARNING;
+ break;
+
+ case BLADERF_LOG_LEVEL_ERROR:
+ syslog_level = LOG_ERR;
+ break;
+
+ case BLADERF_LOG_LEVEL_CRITICAL:
+ syslog_level = LOG_CRIT;
+ break;
+
+ default:
+ /* Shouldn't be used, so just route it to a low level */
+ syslog_level = LOG_DEBUG;
+ break;
+ }
+
+ vsyslog(syslog_level | LOG_USER, format, args);
+ }
+# else
+ vfprintf(stderr, format, args);
+# endif
+#endif
+ va_end(args);
+ }
+}
+
+void log_set_verbosity(bladerf_log_level level)
+{
+ filter_level = level;
+}
+
+bladerf_log_level log_get_verbosity()
+{
+ return filter_level;
+}
+#endif
diff --git a/Radio/HW/BladeRF/common/src/osx/clock_gettime.c b/Radio/HW/BladeRF/common/src/osx/clock_gettime.c
new file mode 100644
index 0000000..5e7200e
--- /dev/null
+++ b/Radio/HW/BladeRF/common/src/osx/clock_gettime.c
@@ -0,0 +1,59 @@
+/*
+ * clock_gettime() wrapper for OSX based upon jbenet's github "gist":
+ * https://gist.github.com/jbenet/1087739
+ *
+ * Copyright (c) 2011 Juan Batiz-Benet (https://github.com/jbenet)
+ * Copyright (c) 2013-2017 Nuand LLC
+ *
+ * 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 "clock_gettime.h"
+#include <errno.h>
+#include <mach/clock.h>
+#include <mach/mach.h>
+
+int clock_gettime(clockid_t clk_id, struct timespec *tp)
+{
+ kern_return_t ret;
+ clock_serv_t cclock;
+ mach_timespec_t mts;
+
+ ret = host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock);
+ if (ret != 0) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ ret = clock_get_time(cclock, &mts);
+ if (ret != 0) {
+ goto clock_gettime_out;
+ }
+
+ tp->tv_sec = mts.tv_sec;
+ tp->tv_nsec = mts.tv_nsec;
+
+clock_gettime_out:
+ if (mach_port_deallocate(mach_task_self(), cclock) != 0 || ret != 0) {
+ errno = EINVAL;
+ return -1;
+ } else {
+ return 0;
+ }
+}
diff --git a/Radio/HW/BladeRF/common/src/parse.c b/Radio/HW/BladeRF/common/src/parse.c
new file mode 100644
index 0000000..dd6abe1
--- /dev/null
+++ b/Radio/HW/BladeRF/common/src/parse.c
@@ -0,0 +1,455 @@
+/*
+ * Copyright (c) 2017 Nuand LLC.
+ *
+ * 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 "parse.h"
+#include "conversions.h"
+#include "log.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+static char **add_arg(
+ char **argv, int argc, const char *buf, int start, int end, int quote_count)
+{
+ char **rv;
+ char *d_ptr;
+ int i;
+ int len;
+
+ char c;
+ char quote_char;
+
+ quote_char = 0;
+
+ rv = (char **)realloc(argv, sizeof(char *) * (argc + 1));
+ if (rv == NULL) {
+ return NULL;
+ }
+
+ rv[argc] = NULL;
+
+ len = end - start + 1;
+
+ d_ptr = (char *)malloc(len + 1 - quote_count * 2);
+ if (d_ptr == NULL) {
+ free(rv);
+ return NULL;
+ }
+
+ rv[argc - 1] = d_ptr;
+
+ for (i = 0; i < len; i++) {
+ c = buf[start + i];
+
+ if (!quote_char) {
+ /* We are not in a quote, copy everything but quote chars */
+ if (c == '"' || c == '\'') {
+ quote_char = c;
+ } else {
+ *d_ptr++ = c;
+ }
+ } else {
+ /* We are in a quote, copy everything but the current quote char */
+ if (c == quote_char) {
+ quote_char = 0;
+ } else {
+ *d_ptr++ = c;
+ }
+ }
+ }
+ *d_ptr = '\0';
+
+ return rv;
+}
+
+int str2args(const char *line, char comment_char, char ***argv)
+{
+ char **rv;
+ int argc;
+
+ unsigned i;
+ size_t len;
+
+ bool in_arg;
+ char c;
+ char quote_char;
+
+ int arg_start;
+ int quote_count;
+
+ rv = NULL;
+ argc = 0;
+
+ quote_char = 0;
+
+ arg_start = 0;
+ quote_count = 0;
+
+ len = strlen(line);
+
+ in_arg = false;
+
+ for (i = 0; i < len; i++) {
+ c = line[i];
+
+ if (in_arg) {
+ /* Found the end of a quote! */
+ if (quote_char) {
+ if (quote_char == c) {
+ quote_char = 0;
+ }
+ continue;
+ }
+
+ /* Found the beginning of a quote! */
+ if (c == '\'' || c == '"') {
+ quote_count++;
+ quote_char = c;
+ continue;
+ }
+
+ /* Found whitespace outside of quote */
+ if (c == ' ' || c == '\t' || c == '\r' || c == '\n') {
+ in_arg = false;
+ argc++;
+ rv = add_arg(rv, argc, line, arg_start, i - 1, quote_count);
+ if (rv == NULL)
+ return -1;
+ }
+ } else {
+ if (c == comment_char) {
+ break;
+ }
+ /* Enter a new argument */
+ if (c != ' ' && c != '\t' && c != '\r' && c != '\n') {
+ /* If first argument is a tick it means we're in a quote */
+ if (c == '\'' || c == '"') {
+ quote_char = c;
+ } else {
+ quote_char = 0;
+ }
+ quote_count = 0;
+ arg_start = i;
+ in_arg = true;
+ }
+ /* else this is still whitespace */
+ }
+ }
+
+ /* reached the end of the line, check to see if current arg needs to
+ * be closed */
+ if (in_arg) {
+ if (quote_char) {
+ free_args(argc, rv);
+ return -2;
+ } else {
+ argc++;
+ rv = add_arg(rv, argc, line, arg_start, i - 1, quote_count);
+ if (rv == NULL) {
+ return -1;
+ }
+ }
+ }
+
+ *argv = rv;
+
+ return argc;
+}
+
+void free_args(int argc, char **argv)
+{
+ int i;
+ for (i = 0; i < argc; i++) {
+ free(argv[i]);
+ }
+ free(argv);
+}
+
+static struct config_options *add_opt(
+ struct config_options *optv, int optc, char *key, char *val, int lineno)
+{
+ struct config_options *rv;
+ char *ptr1, *ptr2;
+ rv = (struct config_options *)realloc(optv,
+ sizeof(struct config_options) * optc);
+ if (rv == NULL) {
+ return NULL;
+ }
+
+ ptr1 = (char *)malloc(strlen(key) + 1);
+ if (ptr1 == NULL) {
+ free(rv);
+ return NULL;
+ }
+ strcpy(ptr1, key);
+ rv[optc - 1].key = ptr1;
+
+ ptr2 = (char *)malloc(strlen(val) + 1);
+ if (ptr2 == NULL) {
+ free(ptr1);
+ free(rv);
+ return NULL;
+ }
+ strcpy(ptr2, val);
+ rv[optc - 1].value = ptr2;
+
+ rv[optc - 1].lineno = lineno;
+
+ return rv;
+}
+
+bool update_match(struct bladerf *dev, char *str)
+{
+ size_t len;
+ int status;
+ struct bladerf_devinfo info;
+ bladerf_fpga_size fpga_size;
+
+ status = bladerf_get_devinfo(dev, &info);
+ if (status < 0)
+ return false;
+
+ bladerf_get_fpga_size(dev, &fpga_size);
+ if (status < 0)
+ return false;
+
+ str++;
+ len = strlen(str);
+ if (str[len - 1] == ']')
+ str[len - 1] = '\0';
+
+ if (!strcmp(str, "x40")) {
+ return fpga_size == BLADERF_FPGA_40KLE;
+ } else if (!strcmp(str, "x115")) {
+ return fpga_size == BLADERF_FPGA_115KLE;
+ }
+
+ status = bladerf_devstr_matches(str, &info);
+
+ return status == 1;
+}
+
+int str2options(struct bladerf *dev,
+ const char *buf,
+ size_t buf_sz,
+ struct config_options **opts)
+{
+ char *line;
+ char *d_ptr;
+ int line_num;
+ char c;
+ unsigned i;
+
+ struct config_options *optv;
+ int optc;
+
+ char **line_argv;
+ int line_argc;
+
+ bool match;
+
+ match = true;
+
+ optv = NULL;
+ optc = 0;
+
+ line_num = 1;
+
+ line = malloc(buf_sz + 1);
+ if (!line)
+ return BLADERF_ERR_MEM;
+
+ d_ptr = line;
+
+ for (i = 0; i < buf_sz; i++) {
+ c = buf[i];
+ if (c == '\n') {
+ /* deal with the old line */
+ *d_ptr = 0;
+ line_argc = str2args(line, '#', &line_argv);
+ if (line_argc < 0)
+ goto out;
+
+ /* handle line */
+ if (line_argc > 3) {
+ log_error("Too many arguments in bladeRF.conf on line %d\n",
+ line_num);
+ goto out;
+ } else if (match && line_argc == 2) {
+ optc++;
+ optv =
+ add_opt(optv, optc, line_argv[0], line_argv[1], line_num);
+ if (!optv) {
+ optc = -1;
+ goto out;
+ }
+ } else if (line_argc == 1) {
+ if (*line_argv[0] != '[') {
+ log_error("Expecting scoping line (requires [ and ]) on "
+ "line %d\n",
+ line_num);
+ }
+ match = update_match(dev, line_argv[0]);
+ }
+
+ /* free line */
+ free_args(line_argc, line_argv);
+
+ /* setup to capture the next line */
+ line_num++;
+ d_ptr = line;
+ } else {
+ *d_ptr++ = c;
+ }
+ }
+
+ if (opts)
+ *opts = optv;
+
+out:
+ free(line);
+ return optc;
+}
+
+void free_opts(struct config_options *optv, int optc)
+{
+ int i;
+
+ for (i = 0; i < optc; i++) {
+ free(optv[i].key);
+ free(optv[i].value);
+ }
+ free(optv);
+}
+
+int csv2int(const char *line, int ***args)
+{
+ const char delim[] = " \r\n\t,.:"; /* supported delimiters */
+ const size_t MAXLEN = 128; /* max line length (with newline and null) */
+ static size_t arglen = 2; /* tunable: initial expected column count */
+ char *myline = NULL; /* local copy of 'line' */
+ char *parsestr = NULL; /* ptr to 'myline' on first strtok_r */
+ char *saveptr = NULL; /* strtok_r state pointer */
+ int **argout = NULL; /* array of output values */
+ size_t count = 0; /* count of tokens extracted */
+ size_t i;
+
+ // Validity check
+ if (NULL == line) {
+ log_debug("line is null\n");
+ return 0;
+ }
+
+ if (NULL == args) {
+ log_error("args is null\n");
+ goto fail;
+ }
+
+ // strtok_r doesn't respect const, so make a copy of 'line'
+ myline = calloc(MAXLEN, 1);
+ if (NULL == myline) {
+ log_error("could not calloc myline\n");
+ goto fail;
+ }
+
+ myline = strncpy(myline, line, MAXLEN - 1);
+
+ // Initial allocation of argout
+ argout = malloc(arglen * sizeof(int *));
+ if (NULL == argout) {
+ log_error("could not malloc argout\n");
+ goto fail;
+ }
+
+ // Loop over input until strtok_r returns a NULL
+ for (i = 0, parsestr = myline; true; ++i, parsestr = NULL) {
+ char *token = NULL; /* return token from strtok_r */
+ bool ok;
+
+ token = strtok_r(parsestr, delim, &saveptr);
+ if (NULL == token) {
+ break;
+ }
+
+ // Expand argout if necessary
+ if (i >= arglen) {
+ arglen *= 2;
+ log_verbose("expanding allocation to %zu column(s)\n", arglen);
+ int **newargout = realloc(argout, arglen * sizeof(int *));
+ if (NULL == newargout) {
+ log_error("could not realloc(argout,%zu)\n", arglen);
+ goto fail;
+ }
+ argout = newargout;
+ }
+
+ // Allocate memory for this value
+ argout[i] = malloc(sizeof(int));
+ if (NULL == argout[i]) {
+ log_error("could not malloc argout[%zu]\n", i);
+ goto fail;
+ }
+
+ // Update the count now, in case str2int fails and we need to dealloc
+ ++count;
+
+ // Parse token into an integer value
+ *argout[i] = str2int(token, INT32_MIN, INT32_MAX, &ok);
+ if (!ok) {
+ log_error("str2int failed on '%s'\n", token);
+ goto fail;
+ }
+ }
+
+ // Success!
+ *args = argout;
+ free(myline);
+
+ // If arglen is too big, cut it in half next time...
+ if (count <= (arglen / 2)) {
+ arglen /= 2;
+ log_verbose("decreasing future arglen to %zu\n", arglen);
+ }
+
+ return (int)count;
+
+fail:
+ // Deallocate everything...
+ free(myline);
+ free_csv2int((int)count, argout);
+ return -1;
+}
+
+void free_csv2int(int argc, int **args)
+{
+ int i;
+
+ if (NULL == args) {
+ return;
+ }
+
+ for (i = 0; i < argc; ++i) {
+ free(args[i]);
+ }
+
+ free(args);
+}
diff --git a/Radio/HW/BladeRF/common/src/range.c b/Radio/HW/BladeRF/common/src/range.c
new file mode 100644
index 0000000..c16fe22
--- /dev/null
+++ b/Radio/HW/BladeRF/common/src/range.c
@@ -0,0 +1,74 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2018 Nuand LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifdef BLADERF_NIOS_BUILD
+#include "devices.h"
+#endif // BLADERF_NIOS_BUILD
+
+/* Avoid building this in low-memory situations */
+#if !defined(BLADERF_NIOS_BUILD) || defined(BLADERF_NIOS_LIBAD936X)
+
+#include "range.h"
+
+#if !defined(BLADERF_NIOS_BUILD) && !defined(BLADERF_NIOS_PC_SIMULATION)
+#include "log.h"
+#endif
+
+bool is_within_range(struct bladerf_range const *range, int64_t value)
+{
+ if (NULL == range) {
+ log_error("%s: range is null\n", __FUNCTION__);
+ return false;
+ }
+
+ return (__scale(range, value) >= range->min &&
+ __scale(range, value) <= range->max);
+}
+
+int64_t clamp_to_range(struct bladerf_range const *range, int64_t value)
+{
+ if (NULL == range) {
+ log_error("%s: range is null\n", __FUNCTION__);
+ return value;
+ }
+
+ if (__scale(range, value) < range->min) {
+ log_debug("%s: Requested value %" PRIi64
+ " is below range [%g,%g], clamping to %" PRIi64 "\n",
+ __FUNCTION__, value, __unscale(range, range->min),
+ __unscale(range, range->max),
+ __unscale_int64(range, range->min));
+ value = __unscale_int64(range, range->min);
+ }
+
+ if (__scale(range, value) > range->max) {
+ log_debug("%s: Requested value %" PRIi64
+ " is above range [%g,%g], clamping to %" PRIi64 "\n",
+ __FUNCTION__, value, __unscale(range, range->min),
+ __unscale(range, range->max),
+ __unscale_int64(range, range->max));
+ value = __unscale_int64(range, range->max);
+ }
+
+ return value;
+}
+
+#endif // !defined(BLADERF_NIOS_BUILD) || defined(BLADERF_NIOS_LIBAD936X)
diff --git a/Radio/HW/BladeRF/common/src/sha256.c b/Radio/HW/BladeRF/common/src/sha256.c
new file mode 100644
index 0000000..b65c7ff
--- /dev/null
+++ b/Radio/HW/BladeRF/common/src/sha256.c
@@ -0,0 +1,343 @@
+/*-
+ * Copyright 2005 Colin Percival
+ * Copyright 2013 Daniel Gröber
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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 <stdint.h>
+#include <string.h>
+
+#include "sha256.h"
+
+#if BLADERF_BIG_ENDIAN == 1
+
+/* Copy a vector of big-endian uint32_t into a vector of bytes */
+#define be32enc_vect(dst, src, len) \
+ memcpy((void *)dst, (const void *)src, (size_t)len)
+
+/* Copy a vector of bytes into a vector of big-endian uint32_t */
+#define be32dec_vect(dst, src, len) \
+ memcpy((void *)dst, (const void *)src, (size_t)len)
+
+#else
+
+/* From libbsd/include/bsd/sys/endian.h */
+/*
+ * Copyright © 2011 Guillem Jover <guillem@hadrons.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``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 AUTHOR 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.
+ */
+
+static __inline uint32_t
+be32dec(const void *pp)
+{
+ uint8_t const *p = (uint8_t const *)pp;
+
+ return (((unsigned)p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
+}
+
+static __inline void
+be32enc(void *pp, uint32_t u)
+{
+ uint8_t *p = (uint8_t *)pp;
+
+ p[0] = (u >> 24) & 0xff;
+ p[1] = (u >> 16) & 0xff;
+ p[2] = (u >> 8) & 0xff;
+ p[3] = u & 0xff;
+}
+/* END from libbsd/include/bsd/sys/endian.h */
+
+/*
+ * Encode a length len/4 vector of (uint32_t) into a length len vector of
+ * (unsigned char) in big-endian form. Assumes len is a multiple of 4.
+ */
+static void
+be32enc_vect(unsigned char *dst, const uint32_t *src, size_t len)
+{
+ size_t i;
+
+ for (i = 0; i < len / 4; i++)
+ be32enc(dst + i * 4, src[i]);
+}
+
+/*
+ * Decode a big-endian length len vector of (unsigned char) into a length
+ * len/4 vector of (uint32_t). Assumes len is a multiple of 4.
+ */
+static void
+be32dec_vect(uint32_t *dst, const unsigned char *src, size_t len)
+{
+ size_t i;
+
+ for (i = 0; i < len / 4; i++)
+ dst[i] = be32dec(src + i * 4);
+}
+
+#endif
+
+/* Elementary functions used by SHA256 */
+#define Ch(x, y, z) ((x & (y ^ z)) ^ z)
+#define Maj(x, y, z) ((x & (y | z)) | (y & z))
+#define SHR(x, n) (x >> n)
+#define ROTR(x, n) ((x >> n) | (x << (32 - n)))
+#define S0(x) (ROTR(x, 2) ^ ROTR(x, 13) ^ ROTR(x, 22))
+#define S1(x) (ROTR(x, 6) ^ ROTR(x, 11) ^ ROTR(x, 25))
+#define s0(x) (ROTR(x, 7) ^ ROTR(x, 18) ^ SHR(x, 3))
+#define s1(x) (ROTR(x, 17) ^ ROTR(x, 19) ^ SHR(x, 10))
+
+/* SHA256 round function */
+#define RND(a, b, c, d, e, f, g, h, k) \
+ t0 = h + S1(e) + Ch(e, f, g) + k; \
+ t1 = S0(a) + Maj(a, b, c); \
+ d += t0; \
+ h = t0 + t1;
+
+/* Adjusted round function for rotating state */
+#define RNDr(S, W, i, k) \
+ RND(S[(64 - i) % 8], S[(65 - i) % 8], \
+ S[(66 - i) % 8], S[(67 - i) % 8], \
+ S[(68 - i) % 8], S[(69 - i) % 8], \
+ S[(70 - i) % 8], S[(71 - i) % 8], \
+ W[i] + k)
+
+/*
+ * SHA256 block compression function. The 256-bit state is transformed via
+ * the 512-bit input block to produce a new state.
+ */
+static void
+SHA256_Transform(uint32_t * state, const unsigned char block[64])
+{
+ uint32_t W[64];
+ uint32_t S[8];
+ uint32_t t0, t1;
+ int i;
+
+ /* 1. Prepare message schedule W. */
+ be32dec_vect(W, block, 64);
+ for (i = 16; i < 64; i++)
+ W[i] = s1(W[i - 2]) + W[i - 7] + s0(W[i - 15]) + W[i - 16];
+
+ /* 2. Initialize working variables. */
+ memcpy(S, state, 32);
+
+ /* 3. Mix. */
+ RNDr(S, W, 0, 0x428a2f98);
+ RNDr(S, W, 1, 0x71374491);
+ RNDr(S, W, 2, 0xb5c0fbcf);
+ RNDr(S, W, 3, 0xe9b5dba5);
+ RNDr(S, W, 4, 0x3956c25b);
+ RNDr(S, W, 5, 0x59f111f1);
+ RNDr(S, W, 6, 0x923f82a4);
+ RNDr(S, W, 7, 0xab1c5ed5);
+ RNDr(S, W, 8, 0xd807aa98);
+ RNDr(S, W, 9, 0x12835b01);
+ RNDr(S, W, 10, 0x243185be);
+ RNDr(S, W, 11, 0x550c7dc3);
+ RNDr(S, W, 12, 0x72be5d74);
+ RNDr(S, W, 13, 0x80deb1fe);
+ RNDr(S, W, 14, 0x9bdc06a7);
+ RNDr(S, W, 15, 0xc19bf174);
+ RNDr(S, W, 16, 0xe49b69c1);
+ RNDr(S, W, 17, 0xefbe4786);
+ RNDr(S, W, 18, 0x0fc19dc6);
+ RNDr(S, W, 19, 0x240ca1cc);
+ RNDr(S, W, 20, 0x2de92c6f);
+ RNDr(S, W, 21, 0x4a7484aa);
+ RNDr(S, W, 22, 0x5cb0a9dc);
+ RNDr(S, W, 23, 0x76f988da);
+ RNDr(S, W, 24, 0x983e5152);
+ RNDr(S, W, 25, 0xa831c66d);
+ RNDr(S, W, 26, 0xb00327c8);
+ RNDr(S, W, 27, 0xbf597fc7);
+ RNDr(S, W, 28, 0xc6e00bf3);
+ RNDr(S, W, 29, 0xd5a79147);
+ RNDr(S, W, 30, 0x06ca6351);
+ RNDr(S, W, 31, 0x14292967);
+ RNDr(S, W, 32, 0x27b70a85);
+ RNDr(S, W, 33, 0x2e1b2138);
+ RNDr(S, W, 34, 0x4d2c6dfc);
+ RNDr(S, W, 35, 0x53380d13);
+ RNDr(S, W, 36, 0x650a7354);
+ RNDr(S, W, 37, 0x766a0abb);
+ RNDr(S, W, 38, 0x81c2c92e);
+ RNDr(S, W, 39, 0x92722c85);
+ RNDr(S, W, 40, 0xa2bfe8a1);
+ RNDr(S, W, 41, 0xa81a664b);
+ RNDr(S, W, 42, 0xc24b8b70);
+ RNDr(S, W, 43, 0xc76c51a3);
+ RNDr(S, W, 44, 0xd192e819);
+ RNDr(S, W, 45, 0xd6990624);
+ RNDr(S, W, 46, 0xf40e3585);
+ RNDr(S, W, 47, 0x106aa070);
+ RNDr(S, W, 48, 0x19a4c116);
+ RNDr(S, W, 49, 0x1e376c08);
+ RNDr(S, W, 50, 0x2748774c);
+ RNDr(S, W, 51, 0x34b0bcb5);
+ RNDr(S, W, 52, 0x391c0cb3);
+ RNDr(S, W, 53, 0x4ed8aa4a);
+ RNDr(S, W, 54, 0x5b9cca4f);
+ RNDr(S, W, 55, 0x682e6ff3);
+ RNDr(S, W, 56, 0x748f82ee);
+ RNDr(S, W, 57, 0x78a5636f);
+ RNDr(S, W, 58, 0x84c87814);
+ RNDr(S, W, 59, 0x8cc70208);
+ RNDr(S, W, 60, 0x90befffa);
+ RNDr(S, W, 61, 0xa4506ceb);
+ RNDr(S, W, 62, 0xbef9a3f7);
+ RNDr(S, W, 63, 0xc67178f2);
+
+ /* 4. Mix local working variables into global state */
+ for (i = 0; i < 8; i++)
+ state[i] += S[i];
+}
+
+static unsigned char PAD[64] = {
+ 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+/* Add padding and terminating bit-count. */
+static void
+SHA256_Pad(SHA256_CTX * ctx)
+{
+ unsigned char len[8];
+ uint32_t r, plen;
+
+ /*
+ * Convert length to a vector of bytes -- we do this now rather
+ * than later because the length will change after we pad.
+ */
+ be32enc_vect(len, ctx->count, 8);
+
+ /* Add 1--64 bytes so that the resulting length is 56 mod 64 */
+ r = (ctx->count[1] >> 3) & 0x3f;
+ plen = (r < 56) ? (56 - r) : (120 - r);
+ SHA256_Update(ctx, PAD, (size_t)plen);
+
+ /* Add the terminating bit-count */
+ SHA256_Update(ctx, len, 8);
+}
+
+/* SHA-256 initialization. Begins a SHA-256 operation. */
+void
+SHA256_Init(SHA256_CTX * ctx)
+{
+
+ /* Zero bits processed so far */
+ ctx->count[0] = ctx->count[1] = 0;
+
+ /* Magic initialization constants */
+ ctx->state[0] = 0x6A09E667;
+ ctx->state[1] = 0xBB67AE85;
+ ctx->state[2] = 0x3C6EF372;
+ ctx->state[3] = 0xA54FF53A;
+ ctx->state[4] = 0x510E527F;
+ ctx->state[5] = 0x9B05688C;
+ ctx->state[6] = 0x1F83D9AB;
+ ctx->state[7] = 0x5BE0CD19;
+}
+
+/* Add bytes into the hash */
+void
+SHA256_Update(SHA256_CTX * ctx, const void *in, size_t len)
+{
+ uint32_t bitlen[2];
+ uint32_t r;
+ const unsigned char *src = in;
+
+ /* Number of bytes left in the buffer from previous updates */
+ r = (ctx->count[1] >> 3) & 0x3f;
+
+ /* Convert the length into a number of bits */
+ bitlen[1] = ((uint32_t)len) << 3;
+ bitlen[0] = (uint32_t)(len >> 29);
+
+ /* Update number of bits */
+ if ((ctx->count[1] += bitlen[1]) < bitlen[1])
+ ctx->count[0]++;
+ ctx->count[0] += bitlen[0];
+
+ /* Handle the case where we don't need to perform any transforms */
+ if (len < 64 - r) {
+ memcpy(&ctx->buf[r], src, len);
+ return;
+ }
+
+ /* Finish the current block */
+ memcpy(&ctx->buf[r], src, 64 - r);
+ SHA256_Transform(ctx->state, ctx->buf);
+ src += 64 - r;
+ len -= 64 - r;
+
+ /* Perform complete blocks */
+ while (len >= 64) {
+ SHA256_Transform(ctx->state, src);
+ src += 64;
+ len -= 64;
+ }
+
+ /* Copy left over data into buffer */
+ memcpy(ctx->buf, src, len);
+}
+
+/*
+ * SHA-256 finalization. Pads the input data, exports the hash value,
+ * and clears the context state.
+ */
+void
+SHA256_Final(unsigned char digest[32], SHA256_CTX * ctx)
+{
+
+ /* Add padding */
+ SHA256_Pad(ctx);
+
+ /* Write the hash */
+ be32enc_vect(digest, ctx->state, 32);
+
+ /* Clear the context state */
+ memset((void *)ctx, 0, sizeof(*ctx));
+}
diff --git a/Radio/HW/BladeRF/common/src/str_queue.c b/Radio/HW/BladeRF/common/src/str_queue.c
new file mode 100644
index 0000000..d5b14cd
--- /dev/null
+++ b/Radio/HW/BladeRF/common/src/str_queue.c
@@ -0,0 +1,193 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (c) 2014 Nuand LLC
+ *
+ * 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 <string.h>
+#include <stdlib.h>
+#include "str_queue.h"
+#include "host_config.h"
+
+struct str_queue_entry {
+ char *str;
+ struct str_queue_entry *next;
+};
+
+void str_queue_init(struct str_queue *q)
+{
+ memset(q, 0, sizeof(q[0]));
+}
+
+void str_queue_deinit(struct str_queue *q)
+{
+ char *str;
+
+ do {
+ str = str_queue_deq(q);
+ free(str);
+ } while (str != NULL);
+
+ q->head = NULL;
+ q->tail = NULL;
+}
+
+int str_queue_enq(struct str_queue *q, const char *str)
+{
+ struct str_queue_entry *entry = malloc(sizeof(entry[0]));
+ if (entry == NULL) {
+ return -1;
+ }
+
+ entry->str = strdup(str);
+ if (entry->str == NULL) {
+ free(entry);
+ return -1;
+ }
+ entry->next = NULL;
+
+ if (q->head == NULL) {
+ q->head = entry;
+ }
+
+ if (q->tail != NULL) {
+ q->tail->next = entry;
+ }
+
+ q->tail = entry;
+ return 0;
+}
+
+char * str_queue_deq(struct str_queue *q)
+{
+ char *ret;
+ struct str_queue_entry *entry;
+
+ entry = q->head;
+ if (entry == NULL) {
+ ret = NULL;
+ } else {
+ q->head = entry->next;
+ if (q->head == NULL) {
+ q->tail = NULL;
+ }
+
+ ret = entry->str;
+ free(entry);
+ }
+
+ return ret;
+}
+
+bool str_queue_empty(struct str_queue *q)
+{
+ return q->head == NULL || q->tail == NULL;
+}
+
+#if TEST_STR_QUEUE
+#include <stdio.h>
+
+#define STR_NULL "(null)"
+#define STR_1 "This is test string one."
+#define STR_2 "A second string queue test string"
+#define STR_3 "String thr33, this be"
+
+#define CHECK_EMPTY(q) do { \
+ if (str_queue_empty(q) != true) { \
+ fprintf(stderr, "Queue not empty!\n"); \
+ return EXIT_FAILURE; \
+ } \
+ } while (0)
+
+#define CHECK_NONEMPTY(q) do { \
+ if (str_queue_empty(q) != false) { \
+ fprintf(stderr, "Queue unexpectedly empty!\n"); \
+ return EXIT_FAILURE; \
+ } \
+ } while (0)
+
+#define ENQ(q, str) do { \
+ int status_ = str_queue_enq(q, str); \
+ if (status_ != 0) { \
+ fprintf(stderr, "Failed to enqueue %s\n", str); \
+ return EXIT_FAILURE; \
+ } else { \
+ printf("Enqueued: %s\n", str); \
+ } \
+ } while (0)
+
+#define DEQ(q, exp) do { \
+ char * str_; \
+ bool failed_; \
+ if (!strcmp(exp, STR_NULL)) { \
+ CHECK_EMPTY(q); \
+ } else { \
+ CHECK_NONEMPTY(q); \
+ } \
+ str_ = str_queue_deq(q); \
+ printf("Dequeued: %s\n", str_); \
+ if (str_ == NULL) { \
+ failed_ = strcmp(exp, STR_NULL) != 0; \
+ } else { \
+ failed_ = strcmp(str_, exp) != 0; \
+ } \
+ if (failed_) { \
+ fprintf(stderr, "Dequeue failed.\n"); \
+ return EXIT_FAILURE; \
+ } \
+ } while (0)
+
+int main(void)
+{
+ char *str;
+ struct str_queue q;
+
+ str_queue_init(&q);
+ str = str_queue_deq(&q);
+
+
+ DEQ(&q, STR_NULL);
+ ENQ(&q, STR_1);
+ DEQ(&q, STR_1);
+ DEQ(&q, STR_NULL);
+ DEQ(&q, STR_NULL);
+
+ ENQ(&q, STR_1);
+ ENQ(&q, STR_2);
+ DEQ(&q, STR_1);
+ DEQ(&q, STR_2);
+ DEQ(&q, STR_NULL);
+
+ ENQ(&q, STR_1);
+ ENQ(&q, STR_2);
+ DEQ(&q, STR_1);
+ ENQ(&q, STR_3);
+ DEQ(&q, STR_2);
+ ENQ(&q, STR_1);
+ DEQ(&q, STR_3);
+ DEQ(&q, STR_1);
+ DEQ(&q, STR_NULL);
+ DEQ(&q, STR_NULL);
+
+ str_queue_deinit(&q);
+ return 0;
+}
+#endif
diff --git a/Radio/HW/BladeRF/common/src/windows/clock_gettime.c b/Radio/HW/BladeRF/common/src/windows/clock_gettime.c
new file mode 100644
index 0000000..803799e
--- /dev/null
+++ b/Radio/HW/BladeRF/common/src/windows/clock_gettime.c
@@ -0,0 +1,58 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (c) 2013 Nuand LLC
+ *
+ * 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 WIN32
+# error "This file is intended for use with WIN32 systems only."
+#endif
+
+#include <Windows.h>
+#include <errno.h>
+#include "clock_gettime.h"
+#include "ptw32_timespec.h"
+
+int clock_gettime(clockid_t clk_id, struct timespec *tp)
+{
+ BOOL success;
+ FILETIME file_time;
+ SYSTEMTIME system_time;
+
+ if (clk_id != CLOCK_REALTIME) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ GetSystemTime(&system_time);
+ success = SystemTimeToFileTime(&system_time, &file_time);
+
+ if (!success) {
+ /* For lack of a better or more compliant return value... */
+ errno = EINVAL;
+ return -1;
+ }
+
+ ptw32_filetime_to_timespec(&file_time, tp);
+
+ return 0;
+}
+
diff --git a/Radio/HW/BladeRF/common/src/windows/getopt_long.c b/Radio/HW/BladeRF/common/src/windows/getopt_long.c
new file mode 100644
index 0000000..7a683fb
--- /dev/null
+++ b/Radio/HW/BladeRF/common/src/windows/getopt_long.c
@@ -0,0 +1,326 @@
+/** @file getopt_long.c
+ ** @brief getopt_long - Definition
+ ** @author Andrea Vedaldi
+ **/
+
+/*
+Copyright (C) 2007-12 Andrea Vedaldi and Brian Fulkerson.
+All rights reserved.
+
+This file is part of the VLFeat library and is made available under
+the terms of the BSD license (see legal/licenses/LICENSE.BSD.vlfeat).
+
+*/
+
+/**
+@file getopt_long.h
+@brief getopt_long
+@author Andrea Vedaldi
+
+This is a drop-in replacament of GNU getopt_long meant to be used
+on platforms that do not support such functionality.
+**/
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "getopt.h"
+
+int opterr = 1 ;
+int optind = 1 ;
+int optopt ;
+char * optarg ;
+int optreset ;
+
+#define BADCH '?'
+#define BADARG ':'
+#define EEND -1
+#define EMSG ""
+
+/** @brief Parse long options (BSD style)
+ ** @param argc number of arguments.
+ ** @param argv pointer to the vector of arguments.
+ ** @param optstring list of abbreviated options
+ ** @param longopts list of long options.
+ ** @param longindex index of current option in @a longopts.
+ ** @return the code of the next option.
+ **
+ ** This function extract long and short options from the argument
+ ** list @a argv of @a argc entries.
+ **
+ ** A short options sequence is introduced by a single dash character
+ ** @c -. Each short option is described by a single character in the
+ ** string @a optstring, possibly followed by a @c : character to
+ ** denote a (mandatory) argument of the short option. A short option
+ ** with an argument cannot appear in the middle of a short option
+ ** sequence, but only at the end.
+ **
+ ** A long option is introduced by a double dash @c --. Each long
+ ** option is described by an instance of the ::option structure in
+ ** the @a longopts table (the last entry must be filled with zeroes
+ ** to denote the end).
+ **
+ ** Illegal options and missing arguments cause the function to skip
+ ** the option and return '?'. If ::opterr is @c true (default), the
+ ** function prints an error message to @a stderr. Finally, if @a
+ ** optstring has a leading @c :, then error messages are suppressed
+ ** and a missing argument causes @a : to be returned.
+ **
+ ** @remark The function is currently <em>not</em> thread safe.
+ **/
+
+VL_EXPORT int
+getopt_long(int argc, char *const argv[],
+ const char *optstring,
+ const struct option * longopts,
+ int *longindex)
+{
+ static char *place = EMSG; /* option letter processing */
+ static int optbegin = 0 ;
+ static int optend = 0 ;
+ const char *oli; /* option letter list index */
+ int has_colon = 0 ;
+ int ret_val = 0 ;
+
+ /*
+ A semicolon at the beginning of optstring has a special meaning.
+ If we find one, we annote and remove it.
+ */
+ has_colon = optstring && optstring[0] == ':' ;
+ if (has_colon) ++ optstring ;
+
+ /*
+ Here we are either processing a short option sequence or
+ we start processing a new option. This is indicated by optreset.
+ */
+
+ if (optreset || *place == '\0') {
+
+ /* ---------------------------------------------------------------
+ * Look for next short/long option
+ * ------------------------------------------------------------ */
+ optreset = 0 ;
+
+ /* no more arguments ? */
+ if (optind >= argc) {
+ place = EMSG ;
+ return -1 ;
+ }
+
+ /* next argument that may hold an option */
+ optbegin = optind ;
+
+ /* ---------------------------------------------------------------
+ * Look for an option to parse
+ * ------------------------------------------------------------ */
+
+ parse_option_at_optbegin :
+
+ /* place points to the candidate option */
+ place = argv [optbegin] ;
+
+ /* an option is introduced by '-' */
+ if (place [0] != '-') {
+ /* this argument is not an option: try next argument */
+ ++ optbegin ;
+ if (optbegin >= argc) {
+ /* no more arguments to look for options */
+ place = EMSG ;
+ return -1 ;
+ }
+ goto parse_option_at_optbegin ;
+ }
+
+ /* consume leading `-' */
+ ++ place ;
+
+ /* assume the option is composed of one argument only */
+ optend = optbegin + 1 ;
+
+ /* assume no argument */
+ optarg = 0 ;
+
+ /* ---------------------------------------------------------------
+ * option `--'
+ * ------------------------------------------------------------ */
+
+ /* this special option (void long option) ends the option processing */
+ if (place[0] &&
+ place[0] == '-' &&
+ place[1] == '\0') {
+
+ optind = optend ;
+ place = EMSG ;
+ ret_val = -1 ;
+ goto done_option ;
+ }
+
+ /* ---------------------------------------------------------------
+ * long option
+ * ------------------------------------------------------------ */
+
+ if (place[0] &&
+ place[0] == '-' &&
+ place[1] ) {
+
+ size_t namelen ;
+ int i ;
+
+ /* consume second `-' */
+ ++ place ;
+
+ /* count characters before `=' */
+ namelen = strcspn(place, "=") ;
+
+ /* scan longopts for this option */
+ for (i = 0 ; longopts[i].name != NULL ; ++ i) {
+
+ if (strlen ( longopts[i].name) == namelen &&
+ strncmp (place, longopts[i].name, namelen) == 0 ) {
+
+ /* save back long option index */
+ if (longindex) *longindex = i ;
+
+ /* process long option argument */
+ if (longopts[i].has_arg == required_argument ||
+ longopts[i].has_arg == optional_argument) {
+
+ /* --option=value style */
+ if (place[namelen] == '=') {
+ optarg = place + namelen + 1 ;
+ }
+
+ /* --option value style (only required_argument) */
+ else if (longopts[i].has_arg == required_argument) {
+ /* missing argument ? */
+ if (optbegin >= argc - 1) {
+ if (! has_colon && opterr)
+ fprintf(stderr,
+ "%s: option requires an argument -- %s\n",
+ argv[0], place);
+ place = EMSG ;
+ ret_val = has_colon ? BADARG : BADCH ;
+ goto done_option ;
+ }
+ optarg = argv [optend] ;
+ ++ optend ;
+ }
+ }
+
+ /* determine return value */
+ if (longopts[i].flag == NULL) {
+ ret_val = longopts[i].val ;
+ }
+ else {
+ *longopts[i].flag = longopts[i].val;
+ ret_val = 0 ;
+ }
+
+ /* mark sequence closed */
+ place = EMSG ;
+ goto done_option ;
+ } /* if match */
+
+ } /* scan longoptions */
+
+ /* no matching option found */
+ if (! has_colon && opterr)
+ fprintf(stderr,
+ "%s: illegal option -- %s\n", argv[0], place) ;
+ place = EMSG ;
+ ret_val = BADCH ;
+ goto done_option ;
+ }
+ } /* end new option */
+
+ /* -----------------------------------------------------------------
+ * Finish short option sequence
+ * -------------------------------------------------------------- */
+ optopt = (int) *place++ ;
+
+ /* search charcater in option list */
+ oli = strchr(optstring, optopt);
+
+ /* short option not found */
+ if (!oli) {
+
+ if (! has_colon && opterr)
+ fprintf(stderr,
+ "%s: illegal option -- %c\n",
+ argv[0], optopt);
+
+ if (*place) {
+ /* more short options in the list */
+ return BADCH ;
+ }
+
+ else {
+ /* error occured as last option in the list */
+ place = EMSG ;
+ ret_val = BADCH ;
+ goto done_option ;
+ }
+ } /* end short option not found */
+
+ if (oli[1] != ':') {
+ /* short option with no argument */
+
+ if (*place) {
+ /* more short options in the list */
+ return optopt ;
+ }
+ else {
+ /* last option in the list */
+ place = EMSG ;
+ ret_val = optopt ;
+ goto done_option ;
+ }
+
+ } else {
+ /* short option with argument */
+
+ /* -ovalue style */
+ if (*place) {
+ optarg = place ;
+ place = EMSG ;
+ ret_val = optopt ;
+ goto done_option ;
+ }
+ /* -o value style: missing argument */
+ else if (optbegin >= argc - 1) {
+ if (! has_colon && opterr)
+ fprintf(stderr,
+ "%s: option requires an argument -- %c\n",
+ argv[0], optopt);
+ place = EMSG ;
+ ret_val = has_colon ? BADARG : BADCH ;
+ goto done_option ;
+ }
+
+ /* -o value style: process argument */
+ optarg = argv [optend] ;
+ ++ optend ;
+ place = EMSG ;
+ ret_val = optopt ;
+ goto done_option ;
+ } /* short with argument */
+
+ done_option :
+ {
+ int pos = optend - optbegin ; /* n of circular shifts */
+ int c = pos ;
+
+ while (c --) {
+ int i ;
+ char *tmp = argv [optend - 1] ;
+ for (i = optend - 1 ; i > optind ; -- i) {
+ ((char**)argv) [i] = argv [i-1] ;
+ }
+ ((char**)argv) [optind] = tmp ;
+ }
+ optind += pos ;
+ }
+
+ return ret_val ;
+}
diff --git a/Radio/HW/BladeRF/common/src/windows/gettimeofday.c b/Radio/HW/BladeRF/common/src/windows/gettimeofday.c
new file mode 100644
index 0000000..144ee2f
--- /dev/null
+++ b/Radio/HW/BladeRF/common/src/windows/gettimeofday.c
@@ -0,0 +1,49 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (c) 2023 Nuand LLC
+ *
+ * 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 WIN32
+#error "This file is intended for use with WIN32 systems only."
+#endif
+
+#include <windows.h>
+#include <stdint.h>
+int gettimeofday(struct timeval *tp, struct timezone *tzp)
+{
+ // Note: some broken versions only have 8 trailing zero's, the correct epoch
+ // has 9 trailing zero's
+ static const uint64_t EPOCH = ((uint64_t)116444736000000000ULL);
+
+ SYSTEMTIME system_time;
+ FILETIME file_time;
+ uint64_t time;
+
+ GetSystemTime(&system_time);
+ SystemTimeToFileTime(&system_time, &file_time);
+ time = ((uint64_t)file_time.dwLowDateTime);
+ time += ((uint64_t)file_time.dwHighDateTime) << 32;
+
+ tp->tv_sec = (long)((time - EPOCH) / 10000000L);
+ tp->tv_usec = (long)(system_time.wMilliseconds * 1000);
+ return 0;
+}
diff --git a/Radio/HW/BladeRF/common/src/windows/mkdtemp.c b/Radio/HW/BladeRF/common/src/windows/mkdtemp.c
new file mode 100644
index 0000000..0e2f804
--- /dev/null
+++ b/Radio/HW/BladeRF/common/src/windows/mkdtemp.c
@@ -0,0 +1,250 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/Nuand/bladeRF
+ *
+ * Copyright (c) 2018 Nuand LLC
+ *
+ * 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.
+ */
+
+// _CRT_RAND_S must be defined before including stdlib.h, to enable rand_s
+#define _CRT_RAND_S
+#include <stdlib.h>
+
+#ifdef WINDOWS_MKDTEMP_TEST_SUITE
+// Running in standalone test mode
+
+#include <stdbool.h>
+#include <stdio.h>
+#define __debug(...) fprintf(stderr, __VA_ARGS__)
+
+#ifndef WIN32
+// Running standalone test on non-Windows OS
+#include <time.h>
+#include <unistd.h>
+#define errno_t int
+errno_t rand_s(unsigned int *randomValue)
+{
+ *randomValue = rand();
+ return 0;
+}
+#endif // WIN32
+
+#else
+// Building as part of a library
+
+#ifndef WIN32
+#error "This file is intended for use with WIN32 systems only."
+#endif // WIN32
+
+#define __debug(...)
+
+#endif // WINDOWS_MKDTEMP_TEST_SUITE
+
+#include <errno.h>
+#include <string.h>
+#include <sys/stat.h>
+#include "host_config.h"
+
+// The concepts of F_OK and S_IRWXU do not exist on Win32
+#ifndef F_OK
+#define F_OK 0
+#endif
+
+#ifndef S_IRWXU
+#define S_IRWXU 00700
+#endif
+
+/* h/t https://stackoverflow.com/a/14598879 */
+static int random_number(int min_num, int max_num)
+{
+ int result = 0, low_num = 0, hi_num = 0;
+ unsigned int randomValue;
+
+ if (min_num < max_num) {
+ low_num = min_num;
+ hi_num = max_num + 1; // include max_num in output
+ } else {
+ low_num = max_num + 1; // include max_num in output
+ hi_num = min_num;
+ }
+
+ // Use rand_s() on Windows, so that we don't have to deal with srand()
+ errno_t err = rand_s(&randomValue);
+ if (0 != err) {
+ return -1;
+ }
+
+ result = (randomValue % (hi_num - low_num)) + low_num;
+ return result;
+}
+
+char *mkdtemp(char *template)
+{
+ size_t const TEMPL_LEN = 6;
+ char const TEMPL_CHAR = 'X';
+
+ if (strlen(template) <= TEMPL_LEN) {
+ // template is too short
+ errno = EINVAL;
+ goto error;
+ }
+
+ // Loop through the end of the template, replacing 'X' with random char
+ for (size_t i = strlen(template) - TEMPL_LEN; i < strlen(template); ++i) {
+ // The last TEMPL_LEN characters MUST be 'X'
+ if (template[i] != TEMPL_CHAR) {
+ errno = EINVAL;
+ goto error;
+ }
+
+ // Pick a random letter
+ if (random_number(0, 1)) {
+ template[i] = (char)random_number('A', 'Z');
+ } else {
+ template[i] = (char)random_number('a', 'z');
+ }
+ }
+
+ // Error out if the file already exists
+ if (access(template, F_OK) != -1) {
+ __debug("%s: failed: %s exists\n", __FUNCTION__, template);
+ errno = EEXIST;
+ goto error;
+ }
+
+ // Try to create the directory...
+ if (0 != mkdir(template, S_IRWXU)) {
+ int errsv = errno;
+ __debug("%s: mkdir() failed: %s\n", __FUNCTION__, strerror(errsv));
+ goto error;
+ }
+
+ // Success!
+ errno = 0;
+ return template;
+
+error:
+ return NULL;
+}
+
+#ifdef WINDOWS_MKDTEMP_TEST_SUITE
+/**
+ * These functions are intended to verify proper operation of the test suite.
+ */
+static bool test(char *template, bool expect_success)
+{
+ char *rv = mkdtemp(template);
+
+ if (NULL == rv) {
+ int errsv = errno;
+ printf("%s: mkdtemp failed: %s\n", __FUNCTION__, strerror(errsv));
+ return (false == expect_success);
+ } else {
+ printf("%s: mkdtemp created: %s\n", __FUNCTION__, rv);
+
+ if (0 != rmdir(rv)) {
+ int errsv = errno;
+ printf("%s: rmdir failed: %s\n", __FUNCTION__, strerror(errsv));
+ }
+
+ return (true == expect_success);
+ }
+}
+
+int main(int argc, char *argv[])
+{
+#ifndef WIN32
+ srand(time(NULL));
+#endif // WIN32
+
+ int success = 0, failure = 0;
+
+ // Normal: should pass
+ char template1[] = "/tmp/asdf.XXXXXX";
+ if (test(template1, true)) {
+ printf("*** Test case 1: PASS\n");
+ ++success;
+ } else {
+ printf("*** Test case 1: FAIL\n");
+ ++failure;
+ }
+
+ // Too short: should fail
+ char template2[] = "XXXXXX";
+ if (test(template2, false)) {
+ printf("*** Test case 2: PASS\n");
+ ++success;
+ } else {
+ printf("*** Test case 2: FAIL\n");
+ ++failure;
+ }
+
+ // Not enough replacement Xs: should fail
+ char template3[] = "/tmp/asdf.XXXXX";
+ if (test(template3, false)) {
+ printf("*** Test case 3: PASS\n");
+ ++success;
+ } else {
+ printf("*** Test case 3: FAIL\n");
+ ++failure;
+ }
+
+ // Make sure it only replaces the end: should pass
+ char template4[] = "/tmp/asdfXXXXXX.XXXXXX";
+ if (test(template4, true)) {
+ printf("*** Test case 4: PASS\n");
+ ++success;
+ } else {
+ printf("*** Test case 4: FAIL\n");
+ ++failure;
+ }
+
+ // Really long: should fail
+ char template5[] = "/tmp/asdfaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaXXXXXX";
+ if (test(template5, false)) {
+ printf("*** Test case 5: PASS\n");
+ ++success;
+ } else {
+ printf("*** Test case 5: FAIL\n");
+ ++failure;
+ }
+
+ // Unwriteable path: should fail
+ char template6[] = "/asdfkjavblkjadv/asdf.XXXX";
+ if (test(template6, false)) {
+ printf("*** Test case 6: PASS\n");
+ ++success;
+ } else {
+ printf("*** Test case 6: FAIL\n");
+ ++failure;
+ }
+
+ printf("TEST SUMMARY: Success=%d, Failure=%d\n", success, failure);
+
+ return failure;
+}
+#endif // WINDOWS_MKDTEMP_TEST_SUITE
diff --git a/Radio/HW/BladeRF/common/src/windows/nanosleep.c b/Radio/HW/BladeRF/common/src/windows/nanosleep.c
new file mode 100644
index 0000000..12d470a
--- /dev/null
+++ b/Radio/HW/BladeRF/common/src/windows/nanosleep.c
@@ -0,0 +1,41 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (c) 2018 Nuand LLC
+ *
+ * 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 WIN32
+#error "This file is intended for use with WIN32 systems only."
+#endif
+
+#include "nanosleep.h"
+#include <windows.h>
+
+int nanosleep(const struct timespec *req, struct timespec *rem)
+{
+ DWORD sleep_ms;
+
+ sleep_ms = ((DWORD)req->tv_sec * 1000) + ((DWORD)req->tv_nsec / 1000000);
+
+ Sleep(sleep_ms);
+
+ return 0;
+}
diff --git a/Radio/HW/BladeRF/common/src/windows/setenv.c b/Radio/HW/BladeRF/common/src/windows/setenv.c
new file mode 100644
index 0000000..1f7eb8c
--- /dev/null
+++ b/Radio/HW/BladeRF/common/src/windows/setenv.c
@@ -0,0 +1,50 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/Nuand/bladeRF
+ *
+ * Copyright (c) 2018 Nuand LLC
+ *
+ * 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 WIN32
+#error "This file is intended for use with WIN32 systems only."
+#endif // WIN32
+
+#include <stdlib.h>
+
+int setenv(const char *name, const char *value, int overwrite)
+{
+ errno_t rv = 0;
+ size_t envsize = 0;
+
+ if (!overwrite) {
+ // Test for existence
+ rv = getenv_s(&envsize, NULL, 0, name);
+ if (rv != 0 || envsize != 0) {
+ return rv;
+ }
+ }
+ return _putenv_s(name, value);
+}
+
+int unsetenv(const char *name)
+{
+ return _putenv_s(name, "");
+}