mirror of
https://github.com/gnss-sdr/gnss-sdr
synced 2025-01-06 07:20:34 +00:00
Add work in the AD9361 manager
This commit is contained in:
parent
09078b6486
commit
43a8090dc6
@ -38,6 +38,30 @@
|
|||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#define MIN_ADC_CLK 25000000UL // 25 MHz
|
||||||
|
#define MAX_ADC_CLK 640000000UL // 640 MHz
|
||||||
|
#define MIN_DATA_RATE MIN_ADC_CLK / 48
|
||||||
|
#define MAX_DATA_RATE 61440000UL // Output of FIR (RX)
|
||||||
|
#define MAX_DAC_CLK (MAX_ADC_CLK / 2)
|
||||||
|
#define MIN_DAC_CLK 25000000UL // 25 MHz
|
||||||
|
#define MAX_RX_HB1 122880000UL
|
||||||
|
#define MAX_RX_HB2 245760000UL
|
||||||
|
#define MAX_RX_HB3 320000000UL
|
||||||
|
#define MAX_TX_HB1 122880000UL
|
||||||
|
#define MAX_TX_HB2 245760000UL
|
||||||
|
#define MAX_TX_HB3 320000000UL
|
||||||
|
#define MAX_FIR MAX_DATA_RATE * 2
|
||||||
|
#define MAX_BBPLL_DIV 64
|
||||||
|
#define MIN_BBPLL_FREQ 714928500UL // 715 MHz - 100ppm
|
||||||
|
#define MAX_BBPLL_FREQ 1430143000UL // 1430 MHz + 100ppm
|
||||||
|
#define check(val, min, max) ((val) <= (max) ? (val) >= (min) : false)
|
||||||
|
|
||||||
|
const uint64_t TX_MAX_PATH_RATES[] = {MAX_DAC_CLK, MAX_TX_HB3, MAX_TX_HB2, MAX_TX_HB1, MAX_FIR};
|
||||||
|
const uint64_t TX_MIN_PATH_RATES[] = {MIN_DAC_CLK, 0, 0, 0, 0};
|
||||||
|
const uint64_t RX_MAX_PATH_RATES[] = {MAX_ADC_CLK, MAX_RX_HB3, MAX_RX_HB2, MAX_RX_HB1, MAX_FIR};
|
||||||
|
const uint64_t RX_MIN_PATH_RATES[] = {MIN_ADC_CLK, 0, 0, 0, 0};
|
||||||
|
|
||||||
|
uint64_t max_rate_found;
|
||||||
|
|
||||||
/* check return value of attr_write function */
|
/* check return value of attr_write function */
|
||||||
void errchk(int v, const char *what)
|
void errchk(int v, const char *what)
|
||||||
@ -71,6 +95,508 @@ struct iio_device *get_ad9361_phy(struct iio_context *ctx)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void set_max_taps(struct filter_design_parameters *fdpTX,
|
||||||
|
struct filter_design_parameters *fdpRX)
|
||||||
|
{
|
||||||
|
// RX side
|
||||||
|
int N, M, K;
|
||||||
|
if (fdpRX->HB3 == 3)
|
||||||
|
N = 16 * floor(fdpRX->converter_rate / (fdpRX->Rdata));
|
||||||
|
else
|
||||||
|
N = 16 * floor(fdpRX->converter_rate / (2 * fdpRX->Rdata));
|
||||||
|
if (N > 128)
|
||||||
|
N = 128;
|
||||||
|
// TX side
|
||||||
|
if (fdpTX->FIR == 1)
|
||||||
|
M = 64;
|
||||||
|
else
|
||||||
|
M = 128;
|
||||||
|
K = 16 * floor(fdpTX->converter_rate * fdpTX->DAC_div / (2 * fdpTX->Rdata));
|
||||||
|
if (K < M)
|
||||||
|
M = K;
|
||||||
|
|
||||||
|
// Pick the smallest
|
||||||
|
if (M > N)
|
||||||
|
{
|
||||||
|
fdpTX->maxTaps = N;
|
||||||
|
fdpRX->maxTaps = N;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fdpTX->maxTaps = M;
|
||||||
|
fdpRX->maxTaps = M;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
double calculate_rfbw(double pll_rate, double caldiv, bool TX,
|
||||||
|
double *rcaldiv)
|
||||||
|
{
|
||||||
|
double rfbw, min_rfbw, max_rfbw, scale;
|
||||||
|
if (TX)
|
||||||
|
{
|
||||||
|
scale = 1.6;
|
||||||
|
min_rfbw = 1250000;
|
||||||
|
max_rfbw = 40000000;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
scale = 1.4;
|
||||||
|
min_rfbw = 400000;
|
||||||
|
max_rfbw = 56000000;
|
||||||
|
}
|
||||||
|
rfbw =
|
||||||
|
(double)round((pll_rate / caldiv) * (2 / (scale * (2 * M_PI) / log(2))));
|
||||||
|
|
||||||
|
// If the RF bandwidth is outside the range of acceptable values we modify
|
||||||
|
// the divider value until it falls into an acceptable range.
|
||||||
|
while ((rfbw < min_rfbw) || (rfbw > max_rfbw))
|
||||||
|
{
|
||||||
|
if (rfbw < min_rfbw)
|
||||||
|
caldiv = caldiv - 1;
|
||||||
|
else
|
||||||
|
caldiv = caldiv + 1;
|
||||||
|
|
||||||
|
if ((caldiv < 1) || (caldiv > 511))
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Calibration divider out of bounds (1 - 511): %f\n", caldiv);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
rfbw = calculate_rfbw(pll_rate, caldiv, TX, rcaldiv);
|
||||||
|
}
|
||||||
|
*rcaldiv = caldiv;
|
||||||
|
return rfbw;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_rates(uint64_t *rx_path_clks,
|
||||||
|
uint64_t *tx_path_clks, int DAC_div, uint64_t *rates,
|
||||||
|
int dec_table_index)
|
||||||
|
{
|
||||||
|
int k;
|
||||||
|
|
||||||
|
// Check if ADC will run faster in config
|
||||||
|
if (rates[1] > max_rate_found)
|
||||||
|
max_rate_found = rates[1];
|
||||||
|
else
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (k = 0; k < 6; k++)
|
||||||
|
{
|
||||||
|
rx_path_clks[k] = rates[k];
|
||||||
|
tx_path_clks[k] = rates[k];
|
||||||
|
|
||||||
|
if (k > 0)
|
||||||
|
{ // Adjust HB's for DAC divider setting
|
||||||
|
if ((dec_table_index < 2) && (k < 4))
|
||||||
|
tx_path_clks[k] = rates[k] / DAC_div;
|
||||||
|
else if ((dec_table_index < 4) && (k < 3))
|
||||||
|
tx_path_clks[k] = rates[k] / DAC_div;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int check_dac_adc_config(uint64_t pll_bb, int PLL_mult,
|
||||||
|
int dec_table_index)
|
||||||
|
{
|
||||||
|
// Need to determine if DAC divider is required and if ADC and DAC rates
|
||||||
|
// can be satisfied
|
||||||
|
uint64_t with_dd, without_dd;
|
||||||
|
bool a, b, c;
|
||||||
|
|
||||||
|
with_dd = pll_bb / PLL_mult / 2;
|
||||||
|
without_dd = pll_bb / PLL_mult / 1;
|
||||||
|
|
||||||
|
a = check(with_dd, MIN_DAC_CLK, MAX_DAC_CLK);
|
||||||
|
b = check(without_dd, MIN_ADC_CLK, MAX_ADC_CLK);
|
||||||
|
c = check(without_dd, MIN_DAC_CLK, MAX_DAC_CLK);
|
||||||
|
|
||||||
|
if (c && b)
|
||||||
|
return 1; // Run without dac div
|
||||||
|
else if (a && b && (dec_table_index < 5))
|
||||||
|
return 2; // Run with dac div
|
||||||
|
else
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool check_rates(int FIR __attribute__((unused)), const int *HB_configs, uint64_t samp_rate,
|
||||||
|
uint64_t *rates)
|
||||||
|
{
|
||||||
|
int j;
|
||||||
|
bool c = true;
|
||||||
|
|
||||||
|
rates[5] = samp_rate;
|
||||||
|
for (j = 4; j > 0; j--)
|
||||||
|
{
|
||||||
|
rates[j] = rates[j + 1] * HB_configs[j - 1];
|
||||||
|
if (j > 1)
|
||||||
|
{
|
||||||
|
c &= check(rates[j], TX_MIN_PATH_RATES[j - 1], TX_MAX_PATH_RATES[j - 1]);
|
||||||
|
c &= check(rates[j], RX_MIN_PATH_RATES[j - 1], RX_MAX_PATH_RATES[j - 1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int determine_pll_div(uint64_t *rates)
|
||||||
|
{
|
||||||
|
// Determine necessary PLL multiplier
|
||||||
|
uint64_t tmp;
|
||||||
|
int PLL_mult = MAX_BBPLL_DIV;
|
||||||
|
while (PLL_mult > 1)
|
||||||
|
{
|
||||||
|
tmp = (uint64_t)rates[1] * PLL_mult;
|
||||||
|
if (check(tmp, MIN_BBPLL_FREQ, MAX_BBPLL_FREQ))
|
||||||
|
{
|
||||||
|
rates[0] = (uint64_t)rates[1] * PLL_mult;
|
||||||
|
return PLL_mult;
|
||||||
|
}
|
||||||
|
PLL_mult >>= 1;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int determine_path_rates_with_fir(uint64_t sample_rate,
|
||||||
|
uint64_t rate_gov,
|
||||||
|
uint64_t *rx_path_clks,
|
||||||
|
uint64_t *tx_path_clks,
|
||||||
|
int FIR)
|
||||||
|
{
|
||||||
|
uint64_t rates[6];
|
||||||
|
int PLL_mult, k;
|
||||||
|
|
||||||
|
max_rate_found = 0UL;
|
||||||
|
|
||||||
|
const int HB_configs[][4] = {
|
||||||
|
{3, 2, 2, FIR}, //12
|
||||||
|
{2, 2, 2, FIR}, //8
|
||||||
|
{3, 2, 1, FIR}, //6
|
||||||
|
{2, 2, 1, FIR}, //4
|
||||||
|
{2, 1, 1, FIR}, //2
|
||||||
|
{3, 1, 1, FIR}, //3
|
||||||
|
{1, 1, 1, FIR}, //1
|
||||||
|
};
|
||||||
|
|
||||||
|
// RX Path:
|
||||||
|
// BBPLL -> /PLL_div -> /HB3 -> /HB2 -> /HB1 -> /FIR
|
||||||
|
// TX Path:
|
||||||
|
// BBPLL -> /(PLL_div*DAC_div) -> /HB3 -> /HB2 -> /HB1 -> /FIR
|
||||||
|
|
||||||
|
// Cycle through possible decimations from highest to lowest
|
||||||
|
for (k = 0; k < 7; k++)
|
||||||
|
{
|
||||||
|
// HB3 cannot be 3 if rate_gov enabled
|
||||||
|
if ((rate_gov > 0) && HB_configs[k][0] == 3)
|
||||||
|
continue;
|
||||||
|
// Check if HB and FIR rates are valid
|
||||||
|
if (check_rates(FIR, HB_configs[k], sample_rate, rates))
|
||||||
|
{
|
||||||
|
// Calculate PLL divider for configuration
|
||||||
|
PLL_mult = determine_pll_div(rates);
|
||||||
|
if (PLL_mult > 0)
|
||||||
|
{
|
||||||
|
// Determine DAC divider setting and check ADC/DAC settings
|
||||||
|
int dac_div = check_dac_adc_config(rates[0], PLL_mult, k);
|
||||||
|
// printf("dac_div: %d\n",dac_div);
|
||||||
|
if (dac_div > 0)
|
||||||
|
set_rates(rx_path_clks, tx_path_clks, dac_div, rates, k);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (max_rate_found == 0UL)
|
||||||
|
return -EINVAL;
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int ad9361_calculate_rf_clock_chain(uint64_t sample_rate,
|
||||||
|
uint64_t rate_gov,
|
||||||
|
uint64_t *rx_path_clks,
|
||||||
|
uint64_t *tx_path_clks)
|
||||||
|
{
|
||||||
|
int ret, k;
|
||||||
|
int FIR[] = {4, 2, 1};
|
||||||
|
|
||||||
|
// Check desired rate within bounds
|
||||||
|
if (!check(sample_rate, MIN_DATA_RATE, MAX_DATA_RATE))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
// Rate selection will try to:
|
||||||
|
// 1. Utilize the maximum decimation in the FIR
|
||||||
|
// 2. Run the ADC/DAC as fast as possible
|
||||||
|
// 3. Use the most decimation possible starting with HB3(closest to ADC)->HB1
|
||||||
|
|
||||||
|
// Cycle through available FIR settings
|
||||||
|
for (k = 0; k < 3; k++)
|
||||||
|
{
|
||||||
|
ret = determine_path_rates_with_fir(sample_rate, rate_gov, rx_path_clks,
|
||||||
|
tx_path_clks, FIR[k]);
|
||||||
|
if (ret == 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int apply_custom_filter(struct iio_device *dev, unsigned dec_tx,
|
||||||
|
unsigned dec_rx, short *tapsTx,
|
||||||
|
short *tapsRx, unsigned taps,
|
||||||
|
uint64_t rate,
|
||||||
|
int gain_tx, int gain_rx,
|
||||||
|
uint64_t wnom_tx, uint64_t wnom_rx)
|
||||||
|
{
|
||||||
|
struct iio_channel *chanTX, *chanRX;
|
||||||
|
long long current_rate;
|
||||||
|
int ret, i, enable, len = 0;
|
||||||
|
char *buf;
|
||||||
|
|
||||||
|
chanTX = iio_device_find_channel(dev, "voltage0", true);
|
||||||
|
if (chanTX == NULL)
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
ret = iio_channel_attr_read_longlong(chanTX, "sampling_frequency", ¤t_rate);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = ad9361_get_trx_fir_enable(dev, &enable);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
if (enable)
|
||||||
|
{
|
||||||
|
if (current_rate <= (25000000 / 12))
|
||||||
|
iio_channel_attr_write_longlong(chanTX, "sampling_frequency", 3000000);
|
||||||
|
|
||||||
|
ret = ad9361_set_trx_fir_enable(dev, false);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
buf = (char *)malloc(FIR_BUF_SIZE);
|
||||||
|
if (!buf)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
|
||||||
|
len += snprintf(buf + len, FIR_BUF_SIZE - len, "RX 3 GAIN %d DEC %d\n", gain_rx,
|
||||||
|
dec_rx);
|
||||||
|
len += snprintf(buf + len, FIR_BUF_SIZE - len, "TX 3 GAIN %d INT %d\n", gain_tx,
|
||||||
|
dec_tx);
|
||||||
|
|
||||||
|
for (i = 0; i < (int)taps; i++)
|
||||||
|
len += snprintf(buf + len, FIR_BUF_SIZE - len, "%d,%d\n", tapsRx[i], tapsTx[i]);
|
||||||
|
|
||||||
|
len += snprintf(buf + len, FIR_BUF_SIZE - len, "\n");
|
||||||
|
|
||||||
|
ret = iio_device_attr_write_raw(dev, "filter_fir_config", buf, len);
|
||||||
|
free(buf);
|
||||||
|
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
if (rate <= (25000000 / 12))
|
||||||
|
{
|
||||||
|
int dacrate, txrate, max;
|
||||||
|
char readbuf[100];
|
||||||
|
ret = iio_device_attr_read(dev, "tx_path_rates", readbuf, sizeof(readbuf));
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
ret = sscanf(readbuf, "BBPLL:%*d DAC:%d T2:%*d T1:%*d TF:%*d TXSAMP:%d",
|
||||||
|
&dacrate, &txrate);
|
||||||
|
if (ret != 2)
|
||||||
|
return -EFAULT;
|
||||||
|
if (txrate == 0)
|
||||||
|
return -EINVAL;
|
||||||
|
max = (dacrate / txrate) * 16;
|
||||||
|
if (max < taps)
|
||||||
|
iio_channel_attr_write_longlong(chanTX, "sampling_frequency", 3000000);
|
||||||
|
|
||||||
|
|
||||||
|
ret = ad9361_set_trx_fir_enable(dev, true);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
ret = iio_channel_attr_write_longlong(chanTX, "sampling_frequency", rate);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ret = iio_channel_attr_write_longlong(chanTX, "sampling_frequency", rate);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
ret = ad9361_set_trx_fir_enable(dev, true);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
chanRX = iio_device_find_channel(dev, "voltage0", false);
|
||||||
|
if (chanRX == NULL)
|
||||||
|
return -ENODEV;
|
||||||
|
ret = iio_channel_attr_write_longlong(chanTX, "rf_bandwidth", wnom_tx);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
ret = iio_channel_attr_write_longlong(chanRX, "rf_bandwidth", wnom_rx);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int ad9361_set_bb_rate_custom_filter_auto(struct iio_device *dev,
|
||||||
|
uint64_t rate)
|
||||||
|
{
|
||||||
|
struct filter_design_parameters fdpTX;
|
||||||
|
struct filter_design_parameters fdpRX;
|
||||||
|
short taps_tx[128];
|
||||||
|
short taps_rx[128];
|
||||||
|
int ret, num_taps_tx, num_taps_rx, gain_tx, gain_rx;
|
||||||
|
unsigned dec_tx, dec_rx, num_taps;
|
||||||
|
|
||||||
|
ret = ad9361_calculate_rf_clock_chain_fdp(&fdpTX, &fdpRX, rate);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = ad9361_generate_fir_taps(&fdpRX, taps_rx, &num_taps_rx, &gain_rx);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = ad9361_generate_fir_taps(&fdpTX, taps_tx, &num_taps_tx, &gain_tx);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
dec_tx = (unsigned)fdpTX.FIR;
|
||||||
|
dec_rx = (unsigned)fdpRX.FIR;
|
||||||
|
num_taps = (unsigned)fdpTX.maxTaps;
|
||||||
|
ret = apply_custom_filter(dev, dec_tx, dec_rx, taps_tx, taps_rx, num_taps,
|
||||||
|
rate, gain_tx, gain_rx, fdpTX.wnom, fdpRX.wnom);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int build_configuration(struct filter_design_parameters *fdpTX,
|
||||||
|
struct filter_design_parameters *fdpRX,
|
||||||
|
uint64_t sample_rate,
|
||||||
|
uint64_t Fpass,
|
||||||
|
uint64_t Fstop,
|
||||||
|
uint64_t wnomTX,
|
||||||
|
uint64_t wnomRX)
|
||||||
|
{
|
||||||
|
double div, max;
|
||||||
|
uint64_t rx_path_clk[6];
|
||||||
|
uint64_t tx_path_clk[6];
|
||||||
|
uint64_t *path_clk;
|
||||||
|
struct filter_design_parameters *fdp;
|
||||||
|
int ret, k;
|
||||||
|
uint64_t rate_gov = 0;
|
||||||
|
|
||||||
|
ret = ad9361_calculate_rf_clock_chain((uint64_t)sample_rate,
|
||||||
|
rate_gov, rx_path_clk, tx_path_clk);
|
||||||
|
if (ret < 0)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
for (k = 0; k < 2; k++)
|
||||||
|
{
|
||||||
|
if (k > 0)
|
||||||
|
{
|
||||||
|
path_clk = tx_path_clk;
|
||||||
|
fdp = fdpTX;
|
||||||
|
fdp->RxTx = "Tx";
|
||||||
|
fdp->DAC_div = (double)rx_path_clk[1] / tx_path_clk[1];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
path_clk = rx_path_clk;
|
||||||
|
fdp = fdpRX;
|
||||||
|
fdp->RxTx = "Rx";
|
||||||
|
fdp->DAC_div = 1.0;
|
||||||
|
}
|
||||||
|
// Map rates and dividers
|
||||||
|
fdp->PLL_rate = (double)path_clk[0];
|
||||||
|
fdp->converter_rate = (double)path_clk[1];
|
||||||
|
fdp->PLL_mult = (double)path_clk[0] / path_clk[1];
|
||||||
|
fdp->HB3 = (double)path_clk[1] / path_clk[2];
|
||||||
|
fdp->HB2 = (double)path_clk[2] / path_clk[3];
|
||||||
|
fdp->HB1 = (double)path_clk[3] / path_clk[4];
|
||||||
|
fdp->FIR = (double)path_clk[4] / path_clk[5];
|
||||||
|
|
||||||
|
// Set default parameters
|
||||||
|
fdp->Rdata = (double)path_clk[5];
|
||||||
|
fdp->Type = "Lowpass";
|
||||||
|
fdp->int_FIR = 1;
|
||||||
|
fdp->Apass = 0.5;
|
||||||
|
fdp->Astop = 80;
|
||||||
|
fdp->phEQ = -1;
|
||||||
|
fdp->FIRdBmin = 0;
|
||||||
|
// Define filter design specifications
|
||||||
|
fdp->Fpass = (double)Fpass;
|
||||||
|
fdp->Fstop = (double)Fstop;
|
||||||
|
fdp->Fcenter = 0.0;
|
||||||
|
if (k > 0)
|
||||||
|
fdp->wnom = (double)wnomTX;
|
||||||
|
else
|
||||||
|
fdp->wnom = (double)wnomRX;
|
||||||
|
// Determine default analog bandwidth
|
||||||
|
div = ceil((fdp->PLL_rate / fdp->wnom) * (log(2) / (2 * M_PI)));
|
||||||
|
max = (div > 1) ? div : 1.0;
|
||||||
|
fdp->caldiv = (max > 511) ? 511.0 : max;
|
||||||
|
fdp->RFbw = calculate_rfbw(fdp->PLL_rate, fdp->caldiv, k > 0, &(fdp->caldiv));
|
||||||
|
|
||||||
|
if (fdp->RFbw < 0)
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
set_max_taps(fdpTX, fdpRX);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int ad9361_set_bb_rate_custom_filter_manual(struct iio_device *dev,
|
||||||
|
uint64_t rate, uint64_t Fpass,
|
||||||
|
uint64_t Fstop, uint64_t wnom_tx, uint64_t wnom_rx)
|
||||||
|
{
|
||||||
|
struct filter_design_parameters fdpTX;
|
||||||
|
struct filter_design_parameters fdpRX;
|
||||||
|
short taps_tx[128];
|
||||||
|
short taps_rx[128];
|
||||||
|
int ret, num_taps_tx, num_taps_rx, gain_tx, gain_rx;
|
||||||
|
unsigned dec_tx, dec_rx, num_taps;
|
||||||
|
|
||||||
|
if (Fpass >= Fstop)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
ret = build_configuration(&fdpTX, &fdpRX, rate, Fpass, Fstop, wnom_tx,
|
||||||
|
wnom_rx);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = ad9361_generate_fir_taps(&fdpRX, taps_rx, &num_taps_rx, &gain_rx);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = ad9361_generate_fir_taps(&fdpTX, taps_tx, &num_taps_tx, &gain_tx);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
dec_tx = (unsigned)fdpTX.FIR;
|
||||||
|
dec_rx = (unsigned)fdpRX.FIR;
|
||||||
|
num_taps = (unsigned)fdpTX.maxTaps;
|
||||||
|
|
||||||
|
ret = apply_custom_filter(dev, dec_tx, dec_rx, taps_tx, taps_rx, num_taps,
|
||||||
|
rate, gain_tx, gain_rx, wnom_tx, wnom_rx);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* finds AD9361 streaming IIO devices */
|
/* finds AD9361 streaming IIO devices */
|
||||||
bool get_ad9361_stream_dev(struct iio_context *ctx, enum iodev d, struct iio_device **dev)
|
bool get_ad9361_stream_dev(struct iio_context *ctx, enum iodev d, struct iio_device **dev)
|
||||||
{
|
{
|
||||||
@ -195,17 +721,17 @@ bool config_ad9361_rx_local(uint64_t bandwidth_,
|
|||||||
// RX stream config
|
// RX stream config
|
||||||
// Stream configurations
|
// Stream configurations
|
||||||
struct stream_cfg rxcfg;
|
struct stream_cfg rxcfg;
|
||||||
rxcfg.bw_hz = bandwidth_; // 2 MHz rf bandwidth
|
rxcfg.bw_hz = bandwidth_;
|
||||||
rxcfg.fs_hz = sample_rate_; // 2.5 MS/s rx sample rate
|
rxcfg.fs_hz = sample_rate_;
|
||||||
rxcfg.lo_hz = freq_; // 2.5 GHz rf frequency
|
rxcfg.lo_hz = freq_;
|
||||||
rxcfg.rfport = rf_port_select_.c_str(); // port A (select for rf freq.)
|
rxcfg.rfport = rf_port_select_.c_str();
|
||||||
|
|
||||||
std::cout << "AD9361 Acquiring IIO LOCAL context\n";
|
std::cout << "AD9361 Acquiring IIO LOCAL context\n";
|
||||||
struct iio_context *ctx;
|
struct iio_context *ctx;
|
||||||
// Streaming devices
|
// Streaming devices
|
||||||
struct iio_device *rx;
|
struct iio_device *rx;
|
||||||
struct iio_channel *rx0_i;
|
struct iio_channel *rx_chan1;
|
||||||
struct iio_channel *rx0_q;
|
struct iio_channel *rx_chan2;
|
||||||
|
|
||||||
ctx = iio_create_default_context();
|
ctx = iio_create_default_context();
|
||||||
if (!ctx)
|
if (!ctx)
|
||||||
@ -236,21 +762,21 @@ bool config_ad9361_rx_local(uint64_t bandwidth_,
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::cout << "* Initializing AD9361 IIO streaming channels\n";
|
std::cout << "* Initializing AD9361 IIO streaming channels\n";
|
||||||
if (!get_ad9361_stream_ch(ctx, RX, rx, 0, &rx0_i))
|
if (!get_ad9361_stream_ch(ctx, RX, rx, 0, &rx_chan1))
|
||||||
{
|
{
|
||||||
std::cout << "RX chan i not found\n";
|
std::cout << "RX channel 1 not found\n";
|
||||||
throw std::runtime_error("RX chan i not found");
|
throw std::runtime_error("RX channel 1 not found");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!get_ad9361_stream_ch(ctx, RX, rx, 1, &rx0_q))
|
if (!get_ad9361_stream_ch(ctx, RX, rx, 1, &rx_chan2))
|
||||||
{
|
{
|
||||||
std::cout << "RX chan q not found\n";
|
std::cout << "RX channel 2 not found\n";
|
||||||
throw std::runtime_error("RX chan q not found");
|
throw std::runtime_error("RX channel 2 not found");
|
||||||
}
|
}
|
||||||
|
|
||||||
std::cout << "* Enabling IIO streaming channels\n";
|
std::cout << "* Enabling IIO streaming channels\n";
|
||||||
iio_channel_enable(rx0_i);
|
iio_channel_enable(rx_chan1);
|
||||||
iio_channel_enable(rx0_q);
|
iio_channel_enable(rx_chan2);
|
||||||
|
|
||||||
struct iio_device *ad9361_phy;
|
struct iio_device *ad9361_phy;
|
||||||
ad9361_phy = iio_context_find_device(ctx, "ad9361-phy");
|
ad9361_phy = iio_context_find_device(ctx, "ad9361-phy");
|
||||||
@ -334,8 +860,8 @@ bool config_ad9361_rx_remote(const std::string &remote_host,
|
|||||||
struct iio_context *ctx;
|
struct iio_context *ctx;
|
||||||
// Streaming devices
|
// Streaming devices
|
||||||
struct iio_device *rx;
|
struct iio_device *rx;
|
||||||
struct iio_channel *rx0_i;
|
struct iio_channel *rx_chan1;
|
||||||
struct iio_channel *rx0_q;
|
struct iio_channel *rx_chan2;
|
||||||
|
|
||||||
ctx = iio_create_network_context(remote_host.c_str());
|
ctx = iio_create_network_context(remote_host.c_str());
|
||||||
if (!ctx)
|
if (!ctx)
|
||||||
@ -365,13 +891,13 @@ bool config_ad9361_rx_remote(const std::string &remote_host,
|
|||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
std::cout << "* Initializing AD9361 IIO streaming channels\n";
|
std::cout << "* Initializing AD9361 IIO streaming channels\n";
|
||||||
if (!get_ad9361_stream_ch(ctx, RX, rx, 0, &rx0_i))
|
if (!get_ad9361_stream_ch(ctx, RX, rx, 0, &rx_chan1))
|
||||||
{
|
{
|
||||||
std::cout << "RX chan i not found\n";
|
std::cout << "RX chan i not found\n";
|
||||||
throw std::runtime_error("RX chan i not found");
|
throw std::runtime_error("RX chan i not found");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!get_ad9361_stream_ch(ctx, RX, rx, 1, &rx0_q))
|
if (!get_ad9361_stream_ch(ctx, RX, rx, 1, &rx_chan2))
|
||||||
{
|
{
|
||||||
std::cout << "RX chan q not found\n";
|
std::cout << "RX chan q not found\n";
|
||||||
throw std::runtime_error("RX chan q not found");
|
throw std::runtime_error("RX chan q not found");
|
||||||
@ -400,20 +926,18 @@ bool config_ad9361_rx_remote(const std::string &remote_host,
|
|||||||
// set bw
|
// set bw
|
||||||
//params.push_back("in_voltage_rf_bandwidth=" + boost::to_string(bandwidth));
|
//params.push_back("in_voltage_rf_bandwidth=" + boost::to_string(bandwidth));
|
||||||
}
|
}
|
||||||
//wr_ch_str(rx0_i, "rf_port_select", rf_port_select_.c_str());
|
//wr_ch_str(rx_chan1, "rf_port_select", rf_port_select_.c_str());
|
||||||
ret = iio_device_attr_write(ad9361_phy, "in_voltage0_rf_port_select", rf_port_select_.c_str());
|
ret = iio_device_attr_write(ad9361_phy, "in_voltage0_rf_port_select", rf_port_select_.c_str());
|
||||||
if (ret)
|
if (ret)
|
||||||
{
|
{
|
||||||
throw std::runtime_error("Unable to set rf_port_select");
|
throw std::runtime_error("Unable to set rf_port_select");
|
||||||
}
|
}
|
||||||
wr_ch_lli(rx0_i, "rf_bandwidth", bandwidth_);
|
wr_ch_lli(rx_chan1, "rf_bandwidth", bandwidth_);
|
||||||
|
if (!get_lo_chan(ctx, RX, &rx_chan1))
|
||||||
//if (!get_lo_chan(ctx, "RX", &chn))
|
{
|
||||||
// {
|
return false;
|
||||||
// return false;
|
}
|
||||||
// }
|
wr_ch_lli(rx_chan1, "frequency", freq_);
|
||||||
//wr_ch_lli(chn, "frequency", freq_);
|
|
||||||
// in_voltage0_rf_port_select
|
|
||||||
}
|
}
|
||||||
else if (filter_source_ == "File")
|
else if (filter_source_ == "File")
|
||||||
{
|
{
|
||||||
@ -433,13 +957,17 @@ bool config_ad9361_rx_remote(const std::string &remote_host,
|
|||||||
{
|
{
|
||||||
throw std::runtime_error("Unable to set rf_port_select");
|
throw std::runtime_error("Unable to set rf_port_select");
|
||||||
}
|
}
|
||||||
wr_ch_lli(rx0_i, "rf_bandwidth", bandwidth_);
|
wr_ch_lli(rx_chan1, "rf_bandwidth", bandwidth_);
|
||||||
wr_ch_lli(rx0_i, "frequency", freq_);
|
if (!get_lo_chan(ctx, RX, &rx_chan1))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
wr_ch_lli(rx_chan1, "frequency", freq_);
|
||||||
}
|
}
|
||||||
else if (filter_source_ == "Design")
|
else if (filter_source_ == "Design")
|
||||||
{
|
{
|
||||||
ret = ad9361_set_bb_rate_custom_filter_manual(
|
ret = ad9361_set_bb_rate_custom_filter_manual(
|
||||||
ad9361_phy, sample_rate_, Fpass_, Fstop_, bandwidth_, bandwidth_);
|
ad9361_phy, sample_rate_, static_cast<uint64_t>(Fpass_), static_cast<uint64_t>(Fstop_), bandwidth_, bandwidth_);
|
||||||
if (ret)
|
if (ret)
|
||||||
{
|
{
|
||||||
throw std::runtime_error("Unable to set BB rate");
|
throw std::runtime_error("Unable to set BB rate");
|
||||||
@ -449,8 +977,12 @@ bool config_ad9361_rx_remote(const std::string &remote_host,
|
|||||||
{
|
{
|
||||||
throw std::runtime_error("Unable to set rf_port_select");
|
throw std::runtime_error("Unable to set rf_port_select");
|
||||||
}
|
}
|
||||||
wr_ch_lli(rx0_i, "rf_bandwidth", bandwidth_);
|
wr_ch_lli(rx_chan1, "rf_bandwidth", bandwidth_);
|
||||||
wr_ch_lli(rx0_i, "frequency", freq_);
|
if (!get_lo_chan(ctx, RX, &rx_chan1))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
wr_ch_lli(rx_chan1, "frequency", freq_);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -468,8 +1000,8 @@ bool config_ad9361_rx_remote(const std::string &remote_host,
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::cout << "* Enabling IIO streaming channels\n";
|
std::cout << "* Enabling IIO streaming channels\n";
|
||||||
iio_channel_enable(rx0_i);
|
iio_channel_enable(rx_chan1);
|
||||||
iio_channel_enable(rx0_q);
|
iio_channel_enable(rx_chan2);
|
||||||
|
|
||||||
ret = iio_device_attr_write(ad9361_phy, "trx_rate_governor", "nominal");
|
ret = iio_device_attr_write(ad9361_phy, "trx_rate_governor", "nominal");
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
@ -528,7 +1060,6 @@ bool config_ad9361_rx_remote(const std::string &remote_host,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::cout << "End of AD9361 RX configuration.\n";
|
std::cout << "End of AD9361 RX configuration.\n";
|
||||||
|
|
||||||
iio_context_destroy(ctx);
|
iio_context_destroy(ctx);
|
||||||
|
@ -37,6 +37,7 @@
|
|||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
#define FIR_BUF_SIZE 8192
|
||||||
|
|
||||||
/* RX is input, TX is output */
|
/* RX is input, TX is output */
|
||||||
enum iodev
|
enum iodev
|
||||||
@ -48,7 +49,7 @@ enum iodev
|
|||||||
/* common RX and TX streaming params */
|
/* common RX and TX streaming params */
|
||||||
struct stream_cfg
|
struct stream_cfg
|
||||||
{
|
{
|
||||||
int64_t bw_hz; // Analog banwidth in Hz
|
int64_t bw_hz; // Analog bandwidth in Hz
|
||||||
int64_t fs_hz; // Baseband sample rate in Hz
|
int64_t fs_hz; // Baseband sample rate in Hz
|
||||||
int64_t lo_hz; // Local oscillator frequency in Hz
|
int64_t lo_hz; // Local oscillator frequency in Hz
|
||||||
const char *rfport; // Port name
|
const char *rfport; // Port name
|
||||||
@ -64,9 +65,6 @@ void wr_ch_lli(struct iio_channel *chn, const char *what, int64_t val);
|
|||||||
/* write attribute: string */
|
/* write attribute: string */
|
||||||
void wr_ch_str(struct iio_channel *chn, const char *what, const char *str);
|
void wr_ch_str(struct iio_channel *chn, const char *what, const char *str);
|
||||||
|
|
||||||
/* helper function generating channel names */
|
|
||||||
char *get_ch_name(const char *type, int id, char *tmpstr);
|
|
||||||
|
|
||||||
/* returns ad9361 phy device */
|
/* returns ad9361 phy device */
|
||||||
struct iio_device *get_ad9361_phy(struct iio_context *ctx);
|
struct iio_device *get_ad9361_phy(struct iio_context *ctx);
|
||||||
|
|
||||||
@ -127,11 +125,58 @@ bool config_ad9361_lo_remote(const std::string &remote_host,
|
|||||||
double scale_dds_dbfs_,
|
double scale_dds_dbfs_,
|
||||||
double phase_dds_deg_);
|
double phase_dds_deg_);
|
||||||
|
|
||||||
|
|
||||||
bool ad9361_disable_lo_remote(const std::string &remote_host);
|
bool ad9361_disable_lo_remote(const std::string &remote_host);
|
||||||
|
|
||||||
bool ad9361_disable_lo_local();
|
bool ad9361_disable_lo_local();
|
||||||
|
|
||||||
bool load_fir_filter(std::string &filter, struct iio_device *phy);
|
bool load_fir_filter(std::string &filter, struct iio_device *phy);
|
||||||
|
|
||||||
|
int ad9361_set_bb_rate_custom_filter_manual(struct iio_device *dev,
|
||||||
|
uint64_t rate, uint64_t Fpass,
|
||||||
|
uint64_t Fstop, uint64_t wnom_tx, uint64_t wnom_rx);
|
||||||
|
|
||||||
|
int ad9361_set_bb_rate_custom_filter_auto(struct iio_device *dev,
|
||||||
|
uint64_t rate);
|
||||||
|
|
||||||
|
int apply_custom_filter(struct iio_device *dev, unsigned dec_tx,
|
||||||
|
unsigned dec_rx, short *tapsTx,
|
||||||
|
short *tapsRx, unsigned taps,
|
||||||
|
uint64_t rate,
|
||||||
|
int gain_tx, int gain_rx,
|
||||||
|
uint64_t wnom_tx, uint64_t wnom_rx);
|
||||||
|
|
||||||
|
int ad9361_calculate_rf_clock_chain(uint64_t sample_rate,
|
||||||
|
uint64_t rate_gov,
|
||||||
|
uint64_t *rx_path_clks,
|
||||||
|
uint64_t *tx_path_clks);
|
||||||
|
|
||||||
|
int determine_path_rates_with_fir(uint64_t sample_rate,
|
||||||
|
uint64_t rate_gov,
|
||||||
|
uint64_t *rx_path_clks,
|
||||||
|
uint64_t *tx_path_clks,
|
||||||
|
uint64_t tmp,
|
||||||
|
int FIR);
|
||||||
|
|
||||||
|
bool check_rates(int FIR, const int *HB_configs, uint64_t samp_rate,
|
||||||
|
uint64_t *rates);
|
||||||
|
|
||||||
|
int determine_pll_div(uint64_t *rates);
|
||||||
|
|
||||||
|
int check_dac_adc_config(uint64_t pll_bb, int PLL_mult,
|
||||||
|
int dec_table_index);
|
||||||
|
|
||||||
|
double calculate_rfbw(double pll_rate, double caldiv, bool TX,
|
||||||
|
double *rcaldiv);
|
||||||
|
|
||||||
|
int build_configuration(struct filter_design_parameters *fdpTX,
|
||||||
|
struct filter_design_parameters *fdpRX,
|
||||||
|
uint64_t sample_rate,
|
||||||
|
uint64_t Fpass,
|
||||||
|
uint64_t Fstop,
|
||||||
|
uint64_t wnomTX,
|
||||||
|
uint64_t wnomRX);
|
||||||
|
|
||||||
|
void set_max_taps(struct filter_design_parameters *fdpTX,
|
||||||
|
struct filter_design_parameters *fdpRX);
|
||||||
|
|
||||||
#endif // GNSS_SDR_AD9361_MANAGER_H_
|
#endif // GNSS_SDR_AD9361_MANAGER_H_
|
||||||
|
Loading…
Reference in New Issue
Block a user