kopia lustrzana https://github.com/cariboulabs/cariboulite
1316 wiersze
55 KiB
C
1316 wiersze
55 KiB
C
#ifndef ZF_LOG_LEVEL
|
|
#define ZF_LOG_LEVEL ZF_LOG_VERBOSE
|
|
#endif
|
|
#define ZF_LOG_DEF_SRCLOC ZF_LOG_SRCLOC_LONG
|
|
#define ZF_LOG_TAG "CARIBOULITE Radio"
|
|
#include "zf_log/zf_log.h"
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <string.h>
|
|
#include <linux/random.h>
|
|
#include <sys/ioctl.h>
|
|
|
|
#include "cariboulite_internal.h"
|
|
#include "cariboulite_radio.h"
|
|
#include "cariboulite_events.h"
|
|
#include "cariboulite_setup.h"
|
|
|
|
|
|
#define GET_MODEM_CH(rad_ch) ((rad_ch)==cariboulite_channel_s1g ? at86rf215_rf_channel_900mhz : at86rf215_rf_channel_2400mhz)
|
|
#define GET_SMI_CH(rad_ch) ((rad_ch)==cariboulite_channel_s1g ? caribou_smi_channel_900 : caribou_smi_channel_2400)
|
|
|
|
static float sample_rate_middles[] = {3000e3, 1666e3, 1166e3, 900e3, 733e3, 583e3, 450e3};
|
|
static float rx_bandwidth_middles[] = {225e3, 281e3, 356e3, 450e3, 562e3, 706e3, 893e3, 1125e3, 1406e3, 1781e3, 2250e3};
|
|
static float tx_bandwidth_middles[] = {90e3, 112e3, 142e3, 180e3, 225e3, 282e3, 357e3, 450e3, 562e3, 712e3, 900e3};
|
|
|
|
|
|
//=========================================================================
|
|
int cariboulite_radio_init(cariboulite_radio_state_st* radio, sys_st *sys, cariboulite_channel_en type)
|
|
{
|
|
memset (radio, 0, sizeof(cariboulite_radio_state_st));
|
|
|
|
radio->sys = sys;
|
|
radio->active = false;
|
|
radio->channel_direction = cariboulite_channel_dir_rx;
|
|
radio->type = type;
|
|
radio->cw_output = false;
|
|
radio->lo_output = false;
|
|
radio->tx_loopback_anabled = false;
|
|
radio->smi_channel_id = GET_SMI_CH(type);
|
|
|
|
// activation of the channel
|
|
cariboulite_radio_activate_channel(radio, cariboulite_channel_dir_rx, true);
|
|
usleep(1000);
|
|
cariboulite_radio_activate_channel(radio, cariboulite_channel_dir_rx, false);
|
|
|
|
return 0;
|
|
}
|
|
|
|
//=========================================================================
|
|
int cariboulite_radio_dispose(cariboulite_radio_state_st* radio)
|
|
{
|
|
cariboulite_radio_activate_channel(radio, cariboulite_channel_dir_rx, false);
|
|
|
|
at86rf215_radio_set_state( &radio->sys->modem,
|
|
GET_MODEM_CH(radio->type),
|
|
at86rf215_radio_state_cmd_trx_off);
|
|
radio->state = cariboulite_radio_state_cmd_trx_off;
|
|
|
|
// Type specific
|
|
if (radio->type == cariboulite_channel_hif)
|
|
{
|
|
caribou_fpga_set_io_ctrl_mode (&radio->sys->fpga, 0, caribou_fpga_io_ctrl_rfm_low_power);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
//=======================================================================================
|
|
int cariboulite_radio_ext_ref ( sys_st *sys, cariboulite_ext_ref_freq_en ref)
|
|
{
|
|
switch(ref)
|
|
{
|
|
case cariboulite_ext_ref_26mhz:
|
|
ZF_LOGD("Setting ext_ref = 26MHz");
|
|
at86rf215_set_clock_output(&sys->modem, at86rf215_drive_current_8ma, at86rf215_clock_out_freq_26mhz);
|
|
rffc507x_setup_reference_freq(&sys->mixer, 26e6);
|
|
break;
|
|
case cariboulite_ext_ref_32mhz:
|
|
ZF_LOGD("Setting ext_ref = 32MHz");
|
|
at86rf215_set_clock_output(&sys->modem, at86rf215_drive_current_8ma, at86rf215_clock_out_freq_32mhz);
|
|
rffc507x_setup_reference_freq(&sys->mixer, 32e6);
|
|
break;
|
|
case cariboulite_ext_ref_off:
|
|
ZF_LOGD("Setting ext_ref = OFF");
|
|
at86rf215_set_clock_output(&sys->modem, at86rf215_drive_current_8ma, at86rf215_clock_out_freq_off);
|
|
break;
|
|
default:
|
|
return -1;
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
//=========================================================================
|
|
int cariboulite_radio_sync_information(cariboulite_radio_state_st* radio)
|
|
{
|
|
if (cariboulite_radio_get_mod_state (radio, NULL) != 0) { return -1; }
|
|
if (cariboulite_radio_get_rx_gain_control(radio, NULL, NULL) != 0) { return -1; }
|
|
if (cariboulite_radio_get_rx_bandwidth(radio, NULL) != 0) { return -1; }
|
|
if (cariboulite_radio_get_tx_power(radio, NULL) != 0) { return -1; }
|
|
if (cariboulite_radio_get_rssi(radio, NULL) != 0) { return -1; }
|
|
if (cariboulite_radio_get_energy_det(radio, NULL) != 0) { return -1; }
|
|
return 0;
|
|
}
|
|
|
|
//=========================================================================
|
|
int cariboulite_radio_get_mod_state (cariboulite_radio_state_st* radio, cariboulite_radio_state_cmd_en *state)
|
|
{
|
|
radio->state = (cariboulite_radio_state_cmd_en)at86rf215_radio_get_state(&radio->sys->modem, GET_MODEM_CH(radio->type));
|
|
|
|
if (state) *state = radio->state;
|
|
return 0;
|
|
}
|
|
|
|
//=========================================================================
|
|
int cariboulite_radio_get_mod_intertupts (cariboulite_radio_state_st* radio, cariboulite_radio_irq_st **irq_table)
|
|
{
|
|
at86rf215_irq_st irq = {0};
|
|
at86rf215_get_irqs(&radio->sys->modem, &irq, 0);
|
|
|
|
memcpy (&radio->interrupts,
|
|
(radio->type == cariboulite_channel_s1g) ? (&irq.radio09) : (&irq.radio24),
|
|
sizeof(cariboulite_radio_irq_st));
|
|
|
|
if (irq_table) *irq_table = &radio->interrupts;
|
|
|
|
return 0;
|
|
}
|
|
|
|
//=========================================================================
|
|
int cariboulite_radio_set_rx_gain_control(cariboulite_radio_state_st* radio,
|
|
bool rx_agc_on,
|
|
int rx_gain_value_db)
|
|
{
|
|
int control_gain_val = (int)roundf((float)(rx_gain_value_db) / 3.0f);
|
|
if (control_gain_val < 0) control_gain_val = 0;
|
|
if (control_gain_val > 23) control_gain_val = 23;
|
|
|
|
at86rf215_radio_agc_ctrl_st rx_gain_control =
|
|
{
|
|
.agc_measure_source_not_filtered = 1,
|
|
.avg = at86rf215_radio_agc_averaging_32,
|
|
.reset_cmd = 0,
|
|
.freeze_cmd = 0,
|
|
.enable_cmd = rx_agc_on,
|
|
.att = at86rf215_radio_agc_relative_atten_21_db,
|
|
.gain_control_word = control_gain_val,
|
|
};
|
|
|
|
at86rf215_radio_setup_agc(&radio->sys->modem, GET_MODEM_CH(radio->type), &rx_gain_control);
|
|
radio->rx_agc_on = rx_agc_on;
|
|
radio->rx_gain_value_db = rx_gain_value_db;
|
|
return 0;
|
|
}
|
|
|
|
//=========================================================================
|
|
int cariboulite_radio_get_rx_gain_control(cariboulite_radio_state_st* radio,
|
|
bool *rx_agc_on,
|
|
int *rx_gain_value_db)
|
|
{
|
|
at86rf215_radio_agc_ctrl_st agc_ctrl = {0};
|
|
at86rf215_radio_get_agc(&radio->sys->modem, GET_MODEM_CH(radio->type), &agc_ctrl);
|
|
|
|
radio->rx_agc_on = agc_ctrl.enable_cmd;
|
|
radio->rx_gain_value_db = agc_ctrl.gain_control_word * 3;
|
|
if (rx_agc_on) *rx_agc_on = radio->rx_agc_on;
|
|
if (rx_gain_value_db) *rx_gain_value_db = radio->rx_gain_value_db;
|
|
return 0;
|
|
}
|
|
|
|
//=========================================================================
|
|
int cariboulite_radio_get_rx_gain_limits(cariboulite_radio_state_st* radio,
|
|
int *rx_min_gain_value_db,
|
|
int *rx_max_gain_value_db,
|
|
int *rx_gain_value_resolution_db)
|
|
{
|
|
if (rx_min_gain_value_db) *rx_min_gain_value_db = 0;
|
|
if (rx_max_gain_value_db) *rx_max_gain_value_db = 23*3;
|
|
if (rx_gain_value_resolution_db) *rx_gain_value_resolution_db = 3;
|
|
return 0;
|
|
}
|
|
|
|
//=========================================================================
|
|
int cariboulite_radio_set_rx_bandwidth(cariboulite_radio_state_st* radio,
|
|
cariboulite_radio_rx_bw_en rx_bw)
|
|
{
|
|
cariboulite_radio_f_cut_en fcut = cariboulite_radio_rx_f_cut_half_fs;
|
|
|
|
/*if (rx_bw == cariboulite_radio_rx_bw_2500KHz || rx_bw == cariboulite_radio_rx_bw_2000KHz)
|
|
{
|
|
fcut = cariboulite_radio_rx_f_cut_half_fs; // 2MHz cuttof
|
|
}
|
|
else if (rx_bw == cariboulite_radio_rx_bw_1562KHz ||
|
|
rx_bw == cariboulite_radio_rx_bw_1250KHz ||
|
|
rx_bw == cariboulite_radio_rx_bw_1000KHz)
|
|
{
|
|
fcut = cariboulite_radio_rx_f_cut_0_75_half_fs; // 1.5MHz cuttof
|
|
}
|
|
else if (rx_bw == cariboulite_radio_rx_bw_787KHz ||
|
|
rx_bw == cariboulite_radio_rx_bw_625KHz)
|
|
{
|
|
fcut = cariboulite_radio_rx_f_cut_0_5_half_fs; // 1MHz cuttof
|
|
}
|
|
else
|
|
{
|
|
fcut = cariboulite_radio_rx_f_cut_0_25_half_fs; // 500kHz cuttof
|
|
}*/
|
|
|
|
|
|
radio->rx_fcut = fcut;
|
|
|
|
at86rf215_radio_set_rx_bw_samp_st cfg =
|
|
{
|
|
.inverter_sign_if = 0,
|
|
.shift_if_freq = 1, // A value of one configures the receiver to shift the IF frequency
|
|
// by factor of 1.25. This is useful to place the image frequency according
|
|
// to channel scheme. This increases the IF frequency to max 2.5MHz
|
|
// thus places the internal LO fasr away from the signal => lower noise
|
|
.bw = (at86rf215_radio_rx_bw_en)rx_bw,
|
|
.fcut = (at86rf215_radio_f_cut_en)radio->rx_fcut,
|
|
.fs = (at86rf215_radio_sample_rate_en)radio->rx_fs,
|
|
};
|
|
at86rf215_radio_set_rx_bandwidth_sampling(&radio->sys->modem, GET_MODEM_CH(radio->type), &cfg);
|
|
radio->rx_bw = rx_bw;
|
|
return 0;
|
|
}
|
|
|
|
//=========================================================================
|
|
int cariboulite_radio_set_rx_bandwidth_flt(cariboulite_radio_state_st* radio, float bw_hz)
|
|
{
|
|
cariboulite_radio_rx_bw_en bw = cariboulite_radio_rx_bw_200KHz;
|
|
|
|
if (bw_hz <= rx_bandwidth_middles[0]) bw = cariboulite_radio_rx_bw_200KHz;
|
|
else if (bw_hz <= rx_bandwidth_middles[1]) bw = cariboulite_radio_rx_bw_250KHz;
|
|
else if (bw_hz <= rx_bandwidth_middles[2]) bw = cariboulite_radio_rx_bw_312KHz;
|
|
else if (bw_hz <= rx_bandwidth_middles[3]) bw = cariboulite_radio_rx_bw_400KHz;
|
|
else if (bw_hz <= rx_bandwidth_middles[4]) bw = cariboulite_radio_rx_bw_500KHz;
|
|
else if (bw_hz <= rx_bandwidth_middles[5]) bw = cariboulite_radio_rx_bw_625KHz;
|
|
else if (bw_hz <= rx_bandwidth_middles[6]) bw = cariboulite_radio_rx_bw_787KHz;
|
|
else if (bw_hz <= rx_bandwidth_middles[7]) bw = cariboulite_radio_rx_bw_1000KHz;
|
|
else if (bw_hz <= rx_bandwidth_middles[8]) bw = cariboulite_radio_rx_bw_1250KHz;
|
|
else if (bw_hz <= rx_bandwidth_middles[9]) bw = cariboulite_radio_rx_bw_1562KHz;
|
|
else if (bw_hz <= rx_bandwidth_middles[10]) bw = cariboulite_radio_rx_bw_2000KHz;
|
|
else bw = cariboulite_radio_rx_bw_2500KHz;
|
|
|
|
return cariboulite_radio_set_rx_bandwidth(radio, bw);
|
|
}
|
|
|
|
//=========================================================================
|
|
int cariboulite_radio_get_rx_bandwidth(cariboulite_radio_state_st* radio,
|
|
cariboulite_radio_rx_bw_en *rx_bw)
|
|
{
|
|
at86rf215_radio_set_rx_bw_samp_st cfg = {0};
|
|
at86rf215_radio_get_rx_bandwidth_sampling(&radio->sys->modem, GET_MODEM_CH(radio->type), &cfg);
|
|
radio->rx_bw = (cariboulite_radio_rx_bw_en)cfg.bw;
|
|
radio->rx_fcut = (cariboulite_radio_f_cut_en)cfg.fcut;
|
|
radio->rx_fs = (cariboulite_radio_sample_rate_en)cfg.fs;
|
|
if (rx_bw) *rx_bw = radio->rx_bw;
|
|
return 0;
|
|
}
|
|
|
|
//=========================================================================
|
|
int cariboulite_radio_get_rx_bandwidth_flt(cariboulite_radio_state_st* radio, float* bw_hz)
|
|
{
|
|
cariboulite_radio_rx_bw_en bw;
|
|
cariboulite_radio_get_rx_bandwidth(radio, &bw);
|
|
|
|
if (bw_hz == NULL) return 0;
|
|
|
|
switch(bw)
|
|
{
|
|
case cariboulite_radio_rx_bw_200KHz: *bw_hz = 200e5; break;
|
|
case cariboulite_radio_rx_bw_250KHz: *bw_hz = 250e5; break;
|
|
case cariboulite_radio_rx_bw_312KHz: *bw_hz = 312e5; break;
|
|
case cariboulite_radio_rx_bw_400KHz: *bw_hz = 400e5; break;
|
|
case cariboulite_radio_rx_bw_500KHz: *bw_hz = 500e5; break;
|
|
case cariboulite_radio_rx_bw_625KHz: *bw_hz = 625e5; break;
|
|
case cariboulite_radio_rx_bw_787KHz: *bw_hz = 787e5; break;
|
|
case cariboulite_radio_rx_bw_1000KHz: *bw_hz = 1000e5; break;
|
|
case cariboulite_radio_rx_bw_1250KHz: *bw_hz = 1250e5; break;
|
|
case cariboulite_radio_rx_bw_1562KHz: *bw_hz = 1562e5; break;
|
|
case cariboulite_radio_rx_bw_2000KHz: *bw_hz = 2000e5; break;
|
|
case cariboulite_radio_rx_bw_2500KHz:
|
|
default: *bw_hz = 2500e5; break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
//=========================================================================
|
|
int cariboulite_radio_set_rx_samp_cutoff(cariboulite_radio_state_st* radio,
|
|
cariboulite_radio_sample_rate_en rx_sample_rate,
|
|
cariboulite_radio_f_cut_en rx_cutoff)
|
|
{
|
|
at86rf215_radio_set_rx_bw_samp_st cfg =
|
|
{
|
|
.inverter_sign_if = 0, // A value of one configures the receiver to implement the inverted-sign
|
|
// IF frequency. Use default setting for normal operation
|
|
.shift_if_freq = 1, // A value of one configures the receiver to shift the IF frequency
|
|
// by factor of 1.25. This is useful to place the image frequency according
|
|
// to channel scheme.
|
|
.bw = (at86rf215_radio_rx_bw_en)radio->rx_bw, // keep the same
|
|
.fcut = (at86rf215_radio_f_cut_en)rx_cutoff,
|
|
.fs = (at86rf215_radio_sample_rate_en)rx_sample_rate,
|
|
};
|
|
at86rf215_radio_set_rx_bandwidth_sampling(&radio->sys->modem, GET_MODEM_CH(radio->type), &cfg);
|
|
radio->rx_fs = rx_sample_rate;
|
|
radio->rx_fcut = rx_cutoff;
|
|
|
|
// setup the smi sample rate for timeout
|
|
uint32_t sample_rate = 4000000;
|
|
switch(rx_sample_rate)
|
|
{
|
|
case cariboulite_radio_rx_sample_rate_4000khz: sample_rate = 4000000; break;
|
|
case cariboulite_radio_rx_sample_rate_2000khz: sample_rate = 2000000; break;
|
|
case cariboulite_radio_rx_sample_rate_1333khz: sample_rate = 1333000; break;
|
|
case cariboulite_radio_rx_sample_rate_1000khz: sample_rate = 1000000; break;
|
|
case cariboulite_radio_rx_sample_rate_800khz: sample_rate = 800000; break;
|
|
case cariboulite_radio_rx_sample_rate_666khz: sample_rate = 666000; break;
|
|
case cariboulite_radio_rx_sample_rate_500khz: sample_rate = 500000; break;
|
|
case cariboulite_radio_rx_sample_rate_400khz: sample_rate = 400000; break;
|
|
default: sample_rate = 4000000; break;
|
|
}
|
|
|
|
caribou_smi_set_sample_rate(&radio->sys->smi, sample_rate);
|
|
return 0;
|
|
}
|
|
|
|
//=========================================================================
|
|
static cariboulite_radio_sample_rate_en sample_rate_from_flt(float sample_rate_hz)
|
|
{
|
|
cariboulite_radio_sample_rate_en sample_rate = cariboulite_radio_rx_sample_rate_4000khz;
|
|
if (sample_rate_hz >= sample_rate_middles[0]) sample_rate = cariboulite_radio_rx_sample_rate_4000khz;
|
|
else if (sample_rate_hz >= sample_rate_middles[1]) sample_rate = cariboulite_radio_rx_sample_rate_2000khz;
|
|
else if (sample_rate_hz >= sample_rate_middles[2]) sample_rate = cariboulite_radio_rx_sample_rate_1333khz;
|
|
else if (sample_rate_hz >= sample_rate_middles[3]) sample_rate = cariboulite_radio_rx_sample_rate_1000khz;
|
|
else if (sample_rate_hz >= sample_rate_middles[4]) sample_rate = cariboulite_radio_rx_sample_rate_800khz;
|
|
else if (sample_rate_hz >= sample_rate_middles[5]) sample_rate = cariboulite_radio_rx_sample_rate_666khz;
|
|
else if (sample_rate_hz >= sample_rate_middles[6]) sample_rate = cariboulite_radio_rx_sample_rate_500khz;
|
|
else sample_rate = cariboulite_radio_rx_sample_rate_400khz;
|
|
return sample_rate;
|
|
}
|
|
|
|
//=========================================================================
|
|
static float sample_rate_to_flt(cariboulite_radio_sample_rate_en sample_rate)
|
|
{
|
|
float sample_rate_hz = 4000000;
|
|
switch(sample_rate)
|
|
{
|
|
case cariboulite_radio_rx_sample_rate_4000khz: sample_rate_hz = 4000000; break;
|
|
case cariboulite_radio_rx_sample_rate_2000khz: sample_rate_hz = 2000000; break;
|
|
case cariboulite_radio_rx_sample_rate_1333khz: sample_rate_hz = 1333000; break;
|
|
case cariboulite_radio_rx_sample_rate_1000khz: sample_rate_hz = 1000000; break;
|
|
case cariboulite_radio_rx_sample_rate_800khz: sample_rate_hz = 800000; break;
|
|
case cariboulite_radio_rx_sample_rate_666khz: sample_rate_hz = 666000; break;
|
|
case cariboulite_radio_rx_sample_rate_500khz: sample_rate_hz = 500000; break;
|
|
case cariboulite_radio_rx_sample_rate_400khz: sample_rate_hz = 400000; break;
|
|
default: sample_rate_hz = 4000000; break;
|
|
}
|
|
return sample_rate_hz;
|
|
}
|
|
|
|
//=========================================================================
|
|
int cariboulite_radio_set_rx_sample_rate_flt(cariboulite_radio_state_st* radio, float sample_rate_hz)
|
|
{
|
|
cariboulite_radio_sample_rate_en rx_sample_rate = sample_rate_from_flt(sample_rate_hz);
|
|
cariboulite_radio_f_cut_en rx_cutoff = radio->rx_fcut;
|
|
return cariboulite_radio_set_rx_samp_cutoff(radio, rx_sample_rate, rx_cutoff);
|
|
}
|
|
|
|
//=========================================================================
|
|
int cariboulite_radio_get_rx_samp_cutoff(cariboulite_radio_state_st* radio,
|
|
cariboulite_radio_sample_rate_en *rx_sample_rate,
|
|
cariboulite_radio_f_cut_en *rx_cutoff)
|
|
{
|
|
cariboulite_radio_get_rx_bandwidth(radio, NULL);
|
|
if (rx_sample_rate) *rx_sample_rate = (cariboulite_radio_sample_rate_en)radio->rx_fs;
|
|
if (rx_cutoff) *rx_cutoff = (cariboulite_radio_f_cut_en)radio->rx_fcut;
|
|
return 0;
|
|
}
|
|
|
|
//=========================================================================
|
|
int cariboulite_radio_get_rx_sample_rate_flt(cariboulite_radio_state_st* radio, float *sample_rate_hz)
|
|
{
|
|
cariboulite_radio_sample_rate_en rx_sample_rate;
|
|
cariboulite_radio_get_rx_samp_cutoff(radio, &rx_sample_rate, NULL);
|
|
|
|
if (sample_rate_hz == NULL) return 0;
|
|
|
|
*sample_rate_hz = sample_rate_to_flt(rx_sample_rate);
|
|
return 0;
|
|
}
|
|
|
|
//=========================================================================
|
|
int cariboulite_radio_set_tx_power(cariboulite_radio_state_st* radio, int tx_power_dbm)
|
|
{
|
|
float x = tx_power_dbm;
|
|
float tx_power_ctrl_model;
|
|
int tx_power_ctrl = 0;
|
|
|
|
if (radio->type == cariboulite_channel_s1g)
|
|
{
|
|
if (tx_power_dbm < -14) tx_power_dbm = -14;
|
|
if (tx_power_dbm > 14) tx_power_dbm = 14;
|
|
|
|
x = tx_power_dbm;
|
|
tx_power_ctrl_model = roundf(0.001502f*x*x*x + 0.020549f*x*x + 0.991045f*x + 13.727758f);
|
|
tx_power_ctrl = (int)tx_power_ctrl_model;
|
|
if (tx_power_ctrl < 0) tx_power_ctrl = 0;
|
|
if (tx_power_ctrl > 31) tx_power_ctrl = 31;
|
|
}
|
|
else if (radio->type == cariboulite_channel_hif)
|
|
{
|
|
if (tx_power_dbm < -12) tx_power_dbm = -12;
|
|
if (tx_power_dbm > 14) tx_power_dbm = 14;
|
|
|
|
x = tx_power_dbm;
|
|
tx_power_ctrl_model = roundf(0.000710f*x*x*x*x + 0.010521f*x*x*x + 0.015169f*x*x + 0.914333f*x + 12.254084f);
|
|
tx_power_ctrl = (int)tx_power_ctrl_model;
|
|
if (tx_power_ctrl < 0) tx_power_ctrl = 0;
|
|
if (tx_power_ctrl > 31) tx_power_ctrl = 31;
|
|
}
|
|
|
|
/*tx_power_ctrl = tx_power_dbm + 17;
|
|
if (tx_power_ctrl < 0) tx_power_ctrl = 0;
|
|
if (tx_power_ctrl > 31) tx_power_ctrl = 31;*/
|
|
|
|
at86rf215_radio_tx_ctrl_st cfg =
|
|
{
|
|
.pa_ramping_time = at86rf215_radio_tx_pa_ramp_16usec,
|
|
.current_reduction = at86rf215_radio_pa_current_reduction_0ma, // we can use this to gain some more
|
|
// granularity with the tx gain control
|
|
.tx_power = tx_power_ctrl,
|
|
.analog_bw = (at86rf215_radio_tx_cut_off_en)radio->tx_bw, // same as before
|
|
.digital_bw = (at86rf215_radio_f_cut_en)radio->tx_fcut, // same as before
|
|
.fs = (at86rf215_radio_sample_rate_en)radio->tx_fs, // same as before
|
|
.direct_modulation = 0,
|
|
};
|
|
|
|
at86rf215_radio_setup_tx_ctrl(&radio->sys->modem, GET_MODEM_CH(radio->type), &cfg);
|
|
radio->tx_power = tx_power_dbm;
|
|
|
|
return 0;
|
|
}
|
|
|
|
//=========================================================================
|
|
int cariboulite_radio_get_tx_power(cariboulite_radio_state_st* radio, int *tx_power_dbm)
|
|
{
|
|
at86rf215_radio_tx_ctrl_st cfg = {0};
|
|
at86rf215_radio_get_tx_ctrl(&radio->sys->modem, GET_MODEM_CH(radio->type), &cfg);
|
|
|
|
float x = cfg.tx_power;
|
|
float actual_model = 0;
|
|
|
|
if (radio->type == cariboulite_channel_s1g)
|
|
{
|
|
actual_model = -0.000546f*x*x*x + 0.014352f*x*x + 0.902754f*x - 13.954753f;
|
|
}
|
|
else if (radio->type == cariboulite_channel_hif)
|
|
{
|
|
actual_model = 0.000031f*x*x*x*x - 0.002344f*x*x*x + 0.040478f*x*x + 0.712209f*x - 11.168502;
|
|
}
|
|
|
|
/*actual_model = x - 17;
|
|
if (actual_model < -17) actual_model = -17;
|
|
if (actual_model > 14) actual_model = 14;*/
|
|
|
|
radio->tx_power = (int)(actual_model);
|
|
radio->tx_bw = (cariboulite_radio_tx_cut_off_en)cfg.analog_bw;
|
|
radio->tx_fcut = (cariboulite_radio_f_cut_en)cfg.digital_bw;
|
|
radio->tx_fs = (cariboulite_radio_sample_rate_en)cfg.fs;
|
|
|
|
if (tx_power_dbm) *tx_power_dbm = radio->tx_power;
|
|
return 0;
|
|
}
|
|
|
|
//=========================================================================
|
|
int cariboulite_radio_set_tx_bandwidth(cariboulite_radio_state_st* radio,
|
|
cariboulite_radio_tx_cut_off_en tx_bw)
|
|
{
|
|
at86rf215_radio_tx_ctrl_st cfg =
|
|
{
|
|
.pa_ramping_time = at86rf215_radio_tx_pa_ramp_16usec,
|
|
.current_reduction = at86rf215_radio_pa_current_reduction_0ma, // we can use this to gain some more
|
|
// granularity with the tx gain control
|
|
.tx_power = 18 + radio->tx_power, // same as before
|
|
.analog_bw = (at86rf215_radio_tx_cut_off_en)tx_bw,
|
|
.digital_bw = (at86rf215_radio_f_cut_en)radio->tx_fcut, // same as before
|
|
.fs = (at86rf215_radio_sample_rate_en)radio->tx_fs, // same as before
|
|
.direct_modulation = 0,
|
|
};
|
|
|
|
at86rf215_radio_setup_tx_ctrl(&radio->sys->modem, GET_MODEM_CH(radio->type), &cfg);
|
|
radio->tx_bw = tx_bw;
|
|
|
|
return 0;
|
|
}
|
|
|
|
//=========================================================================
|
|
int cariboulite_radio_set_tx_bandwidth_flt(cariboulite_radio_state_st* radio, float tx_bw)
|
|
{
|
|
cariboulite_radio_tx_cut_off_en bw = cariboulite_radio_tx_cut_off_80khz;
|
|
|
|
if (tx_bw <= tx_bandwidth_middles[0]) bw = cariboulite_radio_tx_cut_off_80khz;
|
|
else if (tx_bw <= tx_bandwidth_middles[1]) bw = cariboulite_radio_tx_cut_off_100khz;
|
|
else if (tx_bw <= tx_bandwidth_middles[2]) bw = cariboulite_radio_tx_cut_off_125khz;
|
|
else if (tx_bw <= tx_bandwidth_middles[3]) bw = cariboulite_radio_tx_cut_off_160khz;
|
|
else if (tx_bw <= tx_bandwidth_middles[4]) bw = cariboulite_radio_tx_cut_off_200khz;
|
|
else if (tx_bw <= tx_bandwidth_middles[5]) bw = cariboulite_radio_tx_cut_off_250khz;
|
|
else if (tx_bw <= tx_bandwidth_middles[6]) bw = cariboulite_radio_tx_cut_off_315khz;
|
|
else if (tx_bw <= tx_bandwidth_middles[7]) bw = cariboulite_radio_tx_cut_off_400khz;
|
|
else if (tx_bw <= tx_bandwidth_middles[8]) bw = cariboulite_radio_tx_cut_off_500khz;
|
|
else if (tx_bw <= tx_bandwidth_middles[9]) bw = cariboulite_radio_tx_cut_off_625khz;
|
|
else if (tx_bw <= tx_bandwidth_middles[10]) bw = cariboulite_radio_tx_cut_off_800khz;
|
|
else bw = cariboulite_radio_tx_cut_off_1000khz;
|
|
|
|
return cariboulite_radio_set_tx_bandwidth(radio, bw);
|
|
}
|
|
|
|
//=========================================================================
|
|
int cariboulite_radio_get_tx_bandwidth(cariboulite_radio_state_st* radio,
|
|
cariboulite_radio_tx_cut_off_en *tx_bw)
|
|
{
|
|
cariboulite_radio_get_tx_power(radio, NULL);
|
|
if (tx_bw) *tx_bw = radio->tx_bw;
|
|
return 0;
|
|
}
|
|
|
|
//=========================================================================
|
|
int cariboulite_radio_get_tx_bandwidth_flt(cariboulite_radio_state_st* radio, float *tx_bw)
|
|
{
|
|
cariboulite_radio_tx_cut_off_en bw;
|
|
cariboulite_radio_get_tx_bandwidth(radio, &bw);
|
|
|
|
if (tx_bw == NULL) return 0;
|
|
|
|
switch(bw)
|
|
{
|
|
case cariboulite_radio_tx_cut_off_80khz: *tx_bw = 80e5; break;
|
|
case cariboulite_radio_tx_cut_off_100khz: *tx_bw = 100e5; break;
|
|
case cariboulite_radio_tx_cut_off_125khz: *tx_bw = 125e5; break;
|
|
case cariboulite_radio_tx_cut_off_160khz: *tx_bw = 160e5; break;
|
|
case cariboulite_radio_tx_cut_off_200khz: *tx_bw = 200e5; break;
|
|
case cariboulite_radio_tx_cut_off_250khz: *tx_bw = 250e5; break;
|
|
case cariboulite_radio_tx_cut_off_315khz: *tx_bw = 315e5; break;
|
|
case cariboulite_radio_tx_cut_off_400khz: *tx_bw = 400e5; break;
|
|
case cariboulite_radio_tx_cut_off_500khz: *tx_bw = 500e5; break;
|
|
case cariboulite_radio_tx_cut_off_625khz: *tx_bw = 625e5; break;
|
|
case cariboulite_radio_tx_cut_off_800khz: *tx_bw = 800e5; break;
|
|
case cariboulite_radio_tx_cut_off_1000khz:
|
|
default: *tx_bw = 1000e5; break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
//=========================================================================
|
|
int cariboulite_radio_set_tx_samp_cutoff(cariboulite_radio_state_st* radio,
|
|
cariboulite_radio_sample_rate_en tx_sample_rate,
|
|
cariboulite_radio_f_cut_en tx_cutoff)
|
|
{
|
|
uint8_t sample_gap = 0;
|
|
at86rf215_radio_tx_ctrl_st cfg =
|
|
{
|
|
.pa_ramping_time = at86rf215_radio_tx_pa_ramp_16usec,
|
|
.current_reduction = at86rf215_radio_pa_current_reduction_0ma, // we can use this to gain some more
|
|
// granularity with the tx gain control
|
|
.tx_power = 18 + radio->tx_power, // same as before
|
|
.analog_bw = (at86rf215_radio_tx_cut_off_en)radio->tx_bw, // same as before
|
|
.digital_bw = (at86rf215_radio_f_cut_en)tx_cutoff,
|
|
.fs = (at86rf215_radio_sample_rate_en)tx_sample_rate,
|
|
.direct_modulation = 0,
|
|
};
|
|
|
|
at86rf215_radio_setup_tx_ctrl(&radio->sys->modem, GET_MODEM_CH(radio->type), &cfg);
|
|
radio->tx_fcut = (cariboulite_radio_f_cut_en)tx_cutoff;
|
|
radio->tx_fs = tx_sample_rate;
|
|
|
|
|
|
// setup the new sample rate in the FPGA
|
|
switch (tx_sample_rate)
|
|
{
|
|
case cariboulite_radio_rx_sample_rate_4000khz: sample_gap = 0; break;
|
|
case cariboulite_radio_rx_sample_rate_2000khz: sample_gap = 1; break;
|
|
case cariboulite_radio_rx_sample_rate_1333khz: sample_gap = 2; break;
|
|
case cariboulite_radio_rx_sample_rate_1000khz: sample_gap = 3; break;
|
|
case cariboulite_radio_rx_sample_rate_800khz: sample_gap = 4; break;
|
|
case cariboulite_radio_rx_sample_rate_666khz: sample_gap = 5; break;
|
|
case cariboulite_radio_rx_sample_rate_500khz: sample_gap = 7; break;
|
|
case cariboulite_radio_rx_sample_rate_400khz: sample_gap = 9; break;
|
|
default: sample_gap = 0; break;
|
|
}
|
|
|
|
caribou_fpga_set_sys_ctrl_tx_sample_gap (&radio->sys->fpga, sample_gap);
|
|
return 0;
|
|
}
|
|
|
|
//=========================================================================
|
|
int cariboulite_radio_set_tx_samp_cutoff_flt(cariboulite_radio_state_st* radio, float sample_rate_hz)
|
|
{
|
|
cariboulite_radio_sample_rate_en tx_sample_rate = sample_rate_from_flt(sample_rate_hz);
|
|
cariboulite_radio_f_cut_en tx_cutoff = radio->tx_fcut;
|
|
return cariboulite_radio_set_tx_samp_cutoff(radio, tx_sample_rate, tx_cutoff);
|
|
}
|
|
|
|
//=========================================================================
|
|
int cariboulite_radio_get_tx_samp_cutoff(cariboulite_radio_state_st* radio,
|
|
cariboulite_radio_sample_rate_en *tx_sample_rate,
|
|
cariboulite_radio_f_cut_en *tx_cutoff)
|
|
{
|
|
uint8_t sample_gap = 0;
|
|
cariboulite_radio_get_tx_power(radio, NULL);
|
|
if (tx_sample_rate) *tx_sample_rate = radio->tx_fs;
|
|
if (tx_cutoff) *tx_cutoff = radio->tx_fcut;
|
|
|
|
// make sure that the sample rate matched the fpga gap
|
|
switch (radio->tx_fs)
|
|
{
|
|
case at86rf215_radio_rx_sample_rate_4000khz: sample_gap = 0; break;
|
|
case at86rf215_radio_rx_sample_rate_2000khz: sample_gap = 1; break;
|
|
case at86rf215_radio_rx_sample_rate_1333khz: sample_gap = 2; break;
|
|
case at86rf215_radio_rx_sample_rate_1000khz: sample_gap = 3; break;
|
|
case at86rf215_radio_rx_sample_rate_800khz: sample_gap = 4; break;
|
|
case at86rf215_radio_rx_sample_rate_666khz: sample_gap = 5; break;
|
|
case at86rf215_radio_rx_sample_rate_500khz: sample_gap = 7; break;
|
|
case at86rf215_radio_rx_sample_rate_400khz: sample_gap = 9; break;
|
|
default: sample_gap = 0; break;
|
|
}
|
|
|
|
caribou_fpga_set_sys_ctrl_tx_sample_gap (&radio->sys->fpga, sample_gap);
|
|
|
|
return 0;
|
|
}
|
|
|
|
//=========================================================================
|
|
int cariboulite_radio_get_tx_samp_cutoff_flt(cariboulite_radio_state_st* radio, float *sample_rate_hz)
|
|
{
|
|
cariboulite_radio_sample_rate_en tx_sample_rate;
|
|
cariboulite_radio_get_tx_samp_cutoff(radio, &tx_sample_rate, NULL);
|
|
|
|
if (sample_rate_hz == NULL) return 0;
|
|
|
|
*sample_rate_hz = sample_rate_to_flt(tx_sample_rate);
|
|
return 0;
|
|
}
|
|
|
|
|
|
//=========================================================================
|
|
int cariboulite_radio_get_rssi(cariboulite_radio_state_st* radio, float *rssi_dbm)
|
|
{
|
|
float rssi = at86rf215_radio_get_rssi_dbm(&radio->sys->modem, GET_MODEM_CH(radio->type));
|
|
if (rssi >= -127.0 && rssi <= 4) // register only valid values
|
|
{
|
|
radio->rx_rssi = rssi;
|
|
if (rssi_dbm) *rssi_dbm = rssi;
|
|
return 0;
|
|
}
|
|
|
|
// if error maintain the older number and return error
|
|
if (rssi_dbm) *rssi_dbm = radio->rx_rssi;
|
|
|
|
return -1;
|
|
}
|
|
|
|
//=========================================================================
|
|
int cariboulite_radio_get_energy_det(cariboulite_radio_state_st* radio, float *energy_det_val)
|
|
{
|
|
at86rf215_radio_energy_detection_st det = {0};
|
|
at86rf215_radio_get_energy_detection(&radio->sys->modem, GET_MODEM_CH(radio->type), &det);
|
|
|
|
if (det.energy_detection_value >= -127.0 && det.energy_detection_value <= 4) // register only valid values
|
|
{
|
|
radio->rx_energy_detection_value = det.energy_detection_value;
|
|
if (energy_det_val) *energy_det_val = radio->rx_energy_detection_value;
|
|
return 0;
|
|
}
|
|
|
|
if (energy_det_val) *energy_det_val = radio->rx_energy_detection_value;
|
|
|
|
return -1;
|
|
}
|
|
|
|
//======================================================================
|
|
typedef struct {
|
|
int bit_count; /* number of bits of entropy in data */
|
|
int byte_count; /* number of bytes of data in array */
|
|
unsigned char buf[1];
|
|
} entropy_t;
|
|
|
|
static int add_entropy(uint8_t byte)
|
|
{
|
|
int rand_fid = open("/dev/urandom", O_RDWR);
|
|
if (rand_fid != 0)
|
|
{
|
|
// error opening device
|
|
ZF_LOGE("Opening /dev/urandom device file failed");
|
|
return -1;
|
|
}
|
|
|
|
entropy_t ent = {
|
|
.bit_count = 8,
|
|
.byte_count = 1,
|
|
.buf = {byte},
|
|
};
|
|
|
|
if (ioctl(rand_fid, RNDADDENTROPY, &ent) != 0)
|
|
{
|
|
ZF_LOGE("IOCTL to /dev/urandom device file failed");
|
|
}
|
|
|
|
if (close(rand_fid) !=0 )
|
|
{
|
|
ZF_LOGE("Closing /dev/urandom device file failed");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
//=========================================================================
|
|
int cariboulite_radio_get_rand_val(cariboulite_radio_state_st* radio, uint8_t *rnd)
|
|
{
|
|
radio->random_value = at86rf215_radio_get_random_value(&radio->sys->modem, GET_MODEM_CH(radio->type));
|
|
if (rnd) *rnd = radio->random_value;
|
|
|
|
// add the random number to the system entropy. why not :)
|
|
add_entropy(radio->random_value);
|
|
return 0;
|
|
}
|
|
|
|
//=================================================
|
|
// FREQUENCY CONVERSION LOGIC
|
|
//=================================================
|
|
|
|
//=================================================
|
|
bool cariboulite_radio_wait_mixer_lock(cariboulite_radio_state_st* radio, int retries)
|
|
{
|
|
rffc507x_device_status_st stat = {0};
|
|
|
|
// applicable only in 6G / FULL version
|
|
if (radio->sys->board_info.numeric_product_id != system_type_cariboulite_full)
|
|
{
|
|
ZF_LOGW("Saved by the bell. We shouldn't be here!");
|
|
return true;
|
|
}
|
|
|
|
// applicable only to the 6G channel
|
|
if (radio->type != cariboulite_channel_hif)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
int relock_retries = retries;
|
|
do
|
|
{
|
|
rffc507x_readback_status(&radio->sys->mixer, NULL, &stat);
|
|
rffc507x_print_stat(&stat);
|
|
if (!stat.pll_lock) rffc507x_relock(&radio->sys->mixer);
|
|
} while (!stat.pll_lock && relock_retries--);
|
|
|
|
return stat.pll_lock;
|
|
}
|
|
|
|
//=========================================================================
|
|
int cariboulite_radio_set_modem_state(cariboulite_radio_state_st* radio, cariboulite_radio_state_cmd_en state)
|
|
{
|
|
switch (state)
|
|
{
|
|
case cariboulite_radio_cmd_nop: ZF_LOGD("Setup Modem state to %d (nop)", state); break;
|
|
case cariboulite_radio_cmd_sleep: ZF_LOGD("Setup Modem state to %d (sleep)", state); break;
|
|
case cariboulite_radio_state_cmd_trx_off: ZF_LOGD("Setup Modem state to %d (trx off)", state); break;
|
|
case cariboulite_radio_state_cmd_tx_prep: ZF_LOGD("Setup Modem state to %d (tx prep)", state); break;
|
|
case cariboulite_radio_state_cmd_tx: ZF_LOGD("Setup Modem state to %d (tx)", state); break;
|
|
case cariboulite_radio_state_cmd_rx: ZF_LOGD("Setup Modem state to %d (rx)", state); break;
|
|
case cariboulite_radio_state_transition: ZF_LOGD("Setup Modem state to %d (transition)", state); break;
|
|
case cariboulite_radio_state_cmd_reset: ZF_LOGD("Setup Modem state to %d (reset)", state); break;
|
|
default: ZF_LOGD("Setup Modem state to %d (unknown)", state); break;
|
|
}
|
|
at86rf215_radio_set_state( &radio->sys->modem,
|
|
GET_MODEM_CH(radio->type),
|
|
(at86rf215_radio_state_cmd_en)state);
|
|
radio->state = state;
|
|
return 0;
|
|
}
|
|
|
|
//=================================================
|
|
bool cariboulite_radio_wait_modem_lock(cariboulite_radio_state_st* radio, int retries)
|
|
{
|
|
at86rf215_radio_pll_ctrl_st cfg = {0};
|
|
int relock_retries = retries;
|
|
do
|
|
{
|
|
at86rf215_radio_get_pll_ctrl(&radio->sys->modem, GET_MODEM_CH(radio->type), &cfg);
|
|
} while (!cfg.pll_locked && relock_retries--);
|
|
|
|
return cfg.pll_locked;
|
|
}
|
|
|
|
//=================================================
|
|
bool cariboulite_radio_wait_for_lock( cariboulite_radio_state_st* radio, bool *mod, bool *mix, int retries)
|
|
{
|
|
bool mix_lock = true, mod_lock = true;
|
|
if (radio->type == cariboulite_channel_hif && mix != NULL)
|
|
{
|
|
mix_lock = cariboulite_radio_wait_mixer_lock(radio, retries);
|
|
*mix = mix_lock;
|
|
}
|
|
|
|
if (mod != NULL)
|
|
{
|
|
mod_lock = cariboulite_radio_wait_modem_lock(radio, retries);
|
|
if (mod) *mod = mod_lock;
|
|
}
|
|
|
|
return mix_lock && mod_lock;
|
|
}
|
|
|
|
//=========================================================================
|
|
cariboulite_ext_ref_freq_en cariboulite_radio_find_best_ref_freq(double f)
|
|
{
|
|
return cariboulite_ext_ref_32mhz;
|
|
double f_rf_mod_32 = (f / 32e6);
|
|
double f_rf_mod_26 = (f / 26e6);
|
|
f_rf_mod_32 -= (uint64_t)(f_rf_mod_32);
|
|
f_rf_mod_26 -= (uint64_t)(f_rf_mod_26);
|
|
f_rf_mod_32 *= 32e6;
|
|
f_rf_mod_26 *= 26e6;
|
|
if (f_rf_mod_32 > 16e6) f_rf_mod_32 = 32e6 - f_rf_mod_32;
|
|
if (f_rf_mod_26 > 13e6) f_rf_mod_26 = 26e6 - f_rf_mod_26;
|
|
return f_rf_mod_32 > f_rf_mod_26 ? cariboulite_ext_ref_32mhz : cariboulite_ext_ref_26mhz;
|
|
}
|
|
|
|
#define FREQ_IN_ISM_S1G_RANGE(f) (((f)>=CARIBOULITE_S1G_MIN1&&(f)<=CARIBOULITE_S1G_MAX1)||((f)>=CARIBOULITE_S1G_MIN2&&(f)<=CARIBOULITE_S1G_MAX2))
|
|
#define FREQ_IN_ISM_24G_RANGE(f) ((f)>=CARIBOULITE_2G4_MIN&&(f)<=CARIBOULITE_2G4_MAX)
|
|
|
|
//=========================================================================
|
|
int cariboulite_radio_set_frequency(cariboulite_radio_state_st* radio,
|
|
bool break_before_make,
|
|
double *freq)
|
|
{
|
|
double f_rf = *freq;
|
|
double modem_act_freq = 0.0;
|
|
double lo_act_freq = 0.0;
|
|
double act_freq = 0.0;
|
|
cariboulite_ext_ref_freq_en ext_ref_choice = cariboulite_radio_find_best_ref_freq(f_rf);
|
|
cariboulite_conversion_dir_en conversion_direction = conversion_dir_none;
|
|
|
|
//--------------------------------------------------------------------------------
|
|
// SUB 1GHZ CONFIGURATION
|
|
//--------------------------------------------------------------------------------
|
|
if (radio->type == cariboulite_channel_s1g)
|
|
{
|
|
if (FREQ_IN_ISM_S1G_RANGE(f_rf))
|
|
{
|
|
if (break_before_make) cariboulite_radio_set_modem_state(radio, cariboulite_radio_state_cmd_trx_off);
|
|
|
|
modem_act_freq = at86rf215_setup_channel (&radio->sys->modem,
|
|
at86rf215_rf_channel_900mhz,
|
|
(uint32_t)f_rf);
|
|
|
|
radio->if_frequency = 0;
|
|
radio->lo_pll_locked = 1;
|
|
radio->if_frequency = modem_act_freq;
|
|
radio->actual_rf_frequency = radio->if_frequency;
|
|
radio->requested_rf_frequency = f_rf;
|
|
radio->rf_frequency_error = radio->actual_rf_frequency - radio->requested_rf_frequency;
|
|
|
|
// return actual frequency
|
|
*freq = radio->actual_rf_frequency;
|
|
}
|
|
else
|
|
{
|
|
ZF_LOGE("Unsupported frequency for the ISM S1G channel - %.2f Hz, deactivating channel", f_rf);
|
|
cariboulite_radio_activate_channel(radio, radio->channel_direction, false);
|
|
return -1;
|
|
}
|
|
}
|
|
//--------------------------------------------------------------------------------
|
|
// ISM 2.4 GHZ CONFIGURATION
|
|
//--------------------------------------------------------------------------------
|
|
else if (radio->type == cariboulite_channel_hif &&
|
|
radio->sys->board_info.numeric_product_id == system_type_cariboulite_ism)
|
|
{
|
|
if (FREQ_IN_ISM_24G_RANGE(f_rf))
|
|
{
|
|
if (break_before_make) cariboulite_radio_set_modem_state(radio, cariboulite_radio_state_cmd_trx_off);
|
|
|
|
modem_act_freq = at86rf215_setup_channel (&radio->sys->modem,
|
|
at86rf215_rf_channel_2400mhz,
|
|
(uint32_t)f_rf);
|
|
|
|
radio->if_frequency = 0;
|
|
radio->lo_pll_locked = true;
|
|
radio->if_frequency = modem_act_freq;
|
|
radio->actual_rf_frequency = radio->if_frequency;
|
|
radio->requested_rf_frequency = f_rf;
|
|
radio->rf_frequency_error = radio->actual_rf_frequency - radio->requested_rf_frequency;
|
|
|
|
// return actual frequency
|
|
*freq = radio->actual_rf_frequency;
|
|
}
|
|
else
|
|
{
|
|
ZF_LOGE("Unsupported frequency for the ISM HiF channel - %.2f Hz, deactivating channel", f_rf);
|
|
cariboulite_radio_activate_channel(radio, radio->channel_direction, false);
|
|
return -1;
|
|
}
|
|
}
|
|
//--------------------------------------------------------------------------------
|
|
// FULL 30-6GHz CONFIGURATION
|
|
//--------------------------------------------------------------------------------
|
|
else if (radio->type == cariboulite_channel_hif &&
|
|
radio->sys->board_info.numeric_product_id == system_type_cariboulite_full)
|
|
{
|
|
// Changing the frequency may sometimes need to break RX / TX
|
|
if (break_before_make)
|
|
{
|
|
// make sure that during the transition the modem is not transmitting and then
|
|
// verify that the FE is in low power mode
|
|
cariboulite_radio_set_modem_state(radio, cariboulite_radio_state_cmd_trx_off);
|
|
caribou_fpga_set_io_ctrl_mode (&radio->sys->fpga, 0, caribou_fpga_io_ctrl_rfm_low_power);
|
|
}
|
|
|
|
// Decide the conversion direction and IF/RF/LO
|
|
//-------------------------------------
|
|
if (f_rf >= CARIBOULITE_6G_MIN &&
|
|
f_rf < (CARIBOULITE_2G4_MIN) )
|
|
{
|
|
// Setup the reference frequency as calculated before
|
|
cariboulite_radio_ext_ref (radio->sys, ext_ref_choice);
|
|
rffc507x_calibrate(&radio->sys->mixer);
|
|
|
|
// region #1 - UP CONVERSION
|
|
uint32_t modem_freq = CARIBOULITE_2G4_MAX;
|
|
modem_act_freq = (double)at86rf215_setup_channel (&radio->sys->modem,
|
|
at86rf215_rf_channel_2400mhz,
|
|
modem_freq);
|
|
|
|
// setup mixer LO according to the actual modem frequency
|
|
//lo_act_freq = rffc507x_set_frequency(&radio->sys->mixer, modem_act_freq - f_rf);
|
|
//act_freq = modem_act_freq - lo_act_freq;
|
|
|
|
lo_act_freq = rffc507x_set_frequency(&radio->sys->mixer, modem_act_freq + f_rf);
|
|
act_freq = lo_act_freq - modem_act_freq;
|
|
|
|
// setup fpga RFFE <= upconvert (tx / rx)
|
|
conversion_direction = conversion_dir_up;
|
|
caribou_smi_invert_iq(&radio->sys->smi, true);
|
|
}
|
|
//-------------------------------------
|
|
else if ( f_rf >= CARIBOULITE_2G4_MIN &&
|
|
f_rf < CARIBOULITE_2G4_MAX )
|
|
{
|
|
cariboulite_radio_ext_ref (radio->sys, cariboulite_ext_ref_off);
|
|
// region #2 - bypass mode
|
|
// setup modem frequency <= f_rf
|
|
modem_act_freq = (double)at86rf215_setup_channel (&radio->sys->modem,
|
|
at86rf215_rf_channel_2400mhz,
|
|
(uint32_t)f_rf);
|
|
lo_act_freq = 0;
|
|
act_freq = modem_act_freq;
|
|
conversion_direction = conversion_dir_none;
|
|
caribou_smi_invert_iq(&radio->sys->smi, true);
|
|
}
|
|
//-------------------------------------
|
|
else if ( f_rf >= (CARIBOULITE_2G4_MAX) &&
|
|
f_rf < CARIBOULITE_6G_MAX )
|
|
{
|
|
// Setup the reference frequency as calculated before
|
|
cariboulite_radio_ext_ref (radio->sys, ext_ref_choice);
|
|
rffc507x_calibrate(&radio->sys->mixer);
|
|
|
|
// region #3 - DOWN-CONVERSION
|
|
uint32_t modem_freq = CARIBOULITE_2G4_MIN;
|
|
modem_act_freq = (double)at86rf215_setup_channel (&radio->sys->modem,
|
|
at86rf215_rf_channel_2400mhz,
|
|
modem_freq);
|
|
|
|
// setup mixer LO to according to actual modem frequency
|
|
lo_act_freq = rffc507x_set_frequency(&radio->sys->mixer, f_rf - modem_act_freq);
|
|
act_freq = lo_act_freq + modem_act_freq;
|
|
|
|
// setup fpga RFFE <= downconvert (tx / rx)
|
|
conversion_direction = conversion_dir_down;
|
|
caribou_smi_invert_iq(&radio->sys->smi, true);
|
|
}
|
|
//-------------------------------------
|
|
else
|
|
{
|
|
ZF_LOGE("Unsupported frequency for 6GHz channel - %.2f Hz, deactivating channel", f_rf);
|
|
cariboulite_radio_activate_channel(radio, radio->channel_direction, false);
|
|
return -1;
|
|
}
|
|
|
|
// Setup the frontend
|
|
// This step takes the current radio direction of communication
|
|
// and the down/up conversion decision made before to setup the RF front-end
|
|
switch (conversion_direction)
|
|
{
|
|
case conversion_dir_up:
|
|
if (radio->channel_direction == cariboulite_channel_dir_rx)
|
|
{
|
|
caribou_fpga_set_io_ctrl_mode (&radio->sys->fpga, 0, caribou_fpga_io_ctrl_rfm_rx_lowpass);
|
|
}
|
|
else if (radio->channel_direction == cariboulite_channel_dir_tx)
|
|
{
|
|
caribou_fpga_set_io_ctrl_mode (&radio->sys->fpga, 0, caribou_fpga_io_ctrl_rfm_tx_lowpass);
|
|
}
|
|
break;
|
|
case conversion_dir_none:
|
|
caribou_fpga_set_io_ctrl_mode (&radio->sys->fpga, 0, caribou_fpga_io_ctrl_rfm_bypass);
|
|
break;
|
|
case conversion_dir_down:
|
|
if (radio->channel_direction == cariboulite_channel_dir_rx)
|
|
{
|
|
caribou_fpga_set_io_ctrl_mode (&radio->sys->fpga, 0, caribou_fpga_io_ctrl_rfm_rx_hipass);
|
|
}
|
|
else if (radio->channel_direction == cariboulite_channel_dir_tx)
|
|
{
|
|
caribou_fpga_set_io_ctrl_mode (&radio->sys->fpga, 0, caribou_fpga_io_ctrl_rfm_tx_hipass);
|
|
}
|
|
break;
|
|
default: break;
|
|
}
|
|
|
|
// Make sure the LO and the IF PLLs are locked
|
|
cariboulite_radio_set_modem_state(radio, cariboulite_radio_state_cmd_tx_prep);
|
|
if (!cariboulite_radio_wait_for_lock(radio, &radio->modem_pll_locked,
|
|
lo_act_freq > CARIBOULITE_MIN_LO ? &radio->lo_pll_locked : NULL,
|
|
100))
|
|
{
|
|
if (!radio->lo_pll_locked) ZF_LOGE("PLL MIXER failed to lock LO frequency (%.2f Hz), deactivating", lo_act_freq);
|
|
if (!radio->modem_pll_locked) ZF_LOGE("PLL MODEM failed to lock IF frequency (%.2f Hz), deactivating", modem_act_freq);
|
|
cariboulite_radio_activate_channel(radio, radio->channel_direction, false);
|
|
return -1;
|
|
}
|
|
|
|
// Update the actual frequencies
|
|
radio->lo_frequency = lo_act_freq;
|
|
radio->if_frequency = modem_act_freq;
|
|
radio->actual_rf_frequency = act_freq;
|
|
radio->requested_rf_frequency = f_rf;
|
|
radio->rf_frequency_error = radio->actual_rf_frequency - radio->requested_rf_frequency;
|
|
if (freq) *freq = act_freq;
|
|
}
|
|
|
|
ZF_LOGD("Frequency setting CH: %d, Wanted: %.2f Hz, Set: %.2f Hz (MOD: %.2f, MIX: %.2f)",
|
|
radio->type, f_rf, act_freq, modem_act_freq, lo_act_freq);
|
|
|
|
// reactivate the channel if it was active before the frequency change request was issued
|
|
return cariboulite_radio_activate_channel(radio, radio->channel_direction, radio->active);
|
|
}
|
|
|
|
//=========================================================================
|
|
int cariboulite_radio_get_frequency(cariboulite_radio_state_st* radio,
|
|
double *freq, double *lo, double* i_f)
|
|
{
|
|
if (freq) *freq = radio->actual_rf_frequency;
|
|
if (lo) *lo = radio->lo_frequency;
|
|
if (i_f) *i_f = radio->if_frequency;
|
|
return 0;
|
|
}
|
|
|
|
//=========================================================================
|
|
int cariboulite_radio_activate_channel(cariboulite_radio_state_st* radio,
|
|
cariboulite_channel_dir_en dir,
|
|
bool activate)
|
|
{
|
|
int ret = 0;
|
|
radio->channel_direction = dir;
|
|
radio->active = activate;
|
|
|
|
int cal_i, cal_q;
|
|
ZF_LOGD("Activating channel %d, dir = %s, activate = %d", radio->type, radio->channel_direction==cariboulite_channel_dir_rx?"RX":"TX", activate);
|
|
|
|
// then deactivate the modem's stream
|
|
cariboulite_radio_set_modem_state(radio, cariboulite_radio_state_cmd_trx_off);
|
|
ret = caribou_smi_set_driver_streaming_state(&radio->sys->smi, smi_stream_idle);
|
|
|
|
// DEACTIVATION
|
|
if (!activate)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
// ACTIVATION STEPS
|
|
if (radio->state != cariboulite_radio_state_cmd_tx_prep)
|
|
{
|
|
// deactivate the channel and prep it for pll lock
|
|
cariboulite_radio_set_modem_state(radio, cariboulite_radio_state_cmd_tx_prep);
|
|
|
|
at86rf215_radio_get_tx_iq_calibration(&radio->sys->modem, GET_MODEM_CH(radio->type), &cal_i, &cal_q);
|
|
|
|
ZF_LOGD("Setup Modem state tx_prep");
|
|
radio->modem_pll_locked = cariboulite_radio_wait_modem_lock(radio, 5);
|
|
if (!radio->modem_pll_locked)
|
|
{
|
|
ZF_LOGE("PLL didn't lock");
|
|
|
|
// deactivate the channel if this happens
|
|
cariboulite_radio_set_modem_state(radio, cariboulite_radio_state_cmd_trx_off);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
//===========================================================
|
|
// ACTIVATE RX
|
|
//===========================================================
|
|
// Activate the channel according to the configurations
|
|
// RX on both channels looks the same
|
|
if (radio->channel_direction == cariboulite_channel_dir_rx)
|
|
{
|
|
at86rf215_iq_interface_config_st modem_iq_config = {
|
|
.loopback_enable = radio->tx_loopback_anabled,
|
|
.drv_strength = at86rf215_iq_drive_current_4ma,
|
|
.common_mode_voltage = at86rf215_iq_common_mode_v_ieee1596_1v2,
|
|
.tx_control_with_iq_if = false,
|
|
.radio09_mode = at86rf215_iq_if_mode,
|
|
.radio24_mode = at86rf215_iq_if_mode,
|
|
.clock_skew = at86rf215_iq_clock_data_skew_4_906ns,
|
|
};
|
|
|
|
// Setup the IQ stream properties
|
|
smi_stream_state_en smi_state = smi_stream_idle;
|
|
if (radio->smi_channel_id == caribou_smi_channel_900)
|
|
{
|
|
modem_iq_config.radio09_mode = at86rf215_iq_if_mode;
|
|
modem_iq_config.radio24_mode = at86rf215_baseband_mode;
|
|
modem_iq_config.clock_skew = at86rf215_iq_clock_data_skew_4_906ns;
|
|
smi_state = smi_stream_rx_channel_0;
|
|
}
|
|
else if (radio->smi_channel_id == caribou_smi_channel_2400)
|
|
{
|
|
modem_iq_config.radio09_mode = at86rf215_baseband_mode;
|
|
modem_iq_config.radio24_mode = at86rf215_iq_if_mode;
|
|
modem_iq_config.clock_skew = at86rf215_iq_clock_data_skew_4_906ns;
|
|
smi_state = smi_stream_rx_channel_1;
|
|
}
|
|
|
|
at86rf215_setup_iq_if(&radio->sys->modem, &modem_iq_config);
|
|
|
|
// configure FPGA with the correct rx channel
|
|
caribou_fpga_set_smi_channel (&radio->sys->fpga, radio->type == cariboulite_channel_s1g? caribou_fpga_smi_channel_0 : caribou_fpga_smi_channel_1);
|
|
caribou_fpga_set_smi_ctrl_data_direction(&radio->sys->fpga, 1);
|
|
|
|
// turn on the modem RX
|
|
cariboulite_radio_set_modem_state(radio, cariboulite_radio_state_cmd_rx);
|
|
|
|
// turn on the SMI stream
|
|
if (caribou_smi_set_driver_streaming_state(&radio->sys->smi, smi_state) != 0)
|
|
{
|
|
ZF_LOGD("Failed to configure modem with cmd_rx");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
//===========================================================
|
|
// ACTIVATE TX
|
|
//===========================================================
|
|
else if (radio->channel_direction == cariboulite_channel_dir_tx)
|
|
{
|
|
at86rf215_iq_interface_config_st modem_iq_config =
|
|
{
|
|
.loopback_enable = radio->tx_loopback_anabled,
|
|
.drv_strength = at86rf215_iq_drive_current_4ma,
|
|
.common_mode_voltage = at86rf215_iq_common_mode_v_ieee1596_1v2,
|
|
.tx_control_with_iq_if = !radio->cw_output,
|
|
.radio09_mode = at86rf215_iq_if_mode,
|
|
.radio24_mode = at86rf215_iq_if_mode,
|
|
.clock_skew = at86rf215_iq_clock_data_skew_2_906ns,
|
|
};
|
|
at86rf215_setup_iq_if(&radio->sys->modem, &modem_iq_config);
|
|
|
|
// if its an LO frequency output from the mixer - no need for modem output
|
|
// LO applicable only to the channel with the mixer
|
|
if (radio->lo_output &&
|
|
radio->type == cariboulite_channel_hif &&
|
|
radio->sys->board_info.numeric_product_id == system_type_cariboulite_full)
|
|
{
|
|
// here we need to configure lo bypass on the mixer
|
|
rffc507x_output_lo(&radio->sys->mixer, 1);
|
|
}
|
|
// otherwise we need the modem
|
|
else if (radio->cw_output)
|
|
{
|
|
ZF_LOGD("Transmitting with cw_output");
|
|
cariboulite_radio_set_modem_state(radio, cariboulite_radio_state_cmd_tx_prep);
|
|
|
|
if (radio->sys->board_info.numeric_product_id == system_type_cariboulite_full)
|
|
{
|
|
// make sure the mixer doesn't bypass the lo
|
|
rffc507x_output_lo(&radio->sys->mixer, 0);
|
|
}
|
|
|
|
cariboulite_radio_set_tx_bandwidth(radio, cariboulite_radio_tx_cut_off_80khz);
|
|
|
|
// CW output - constant I/Q values override
|
|
at86rf215_radio_set_tx_dac_input_iq(&radio->sys->modem,
|
|
GET_MODEM_CH(radio->type),
|
|
1, 0x7E,
|
|
1, 0x3F);
|
|
|
|
// transition to state TX
|
|
cariboulite_radio_set_modem_state(radio, cariboulite_radio_state_cmd_tx);
|
|
}
|
|
else
|
|
{
|
|
ZF_LOGD("Transmitting with iq");
|
|
cariboulite_radio_set_modem_state(radio, cariboulite_radio_state_cmd_tx_prep);
|
|
|
|
cariboulite_radio_set_tx_bandwidth(radio, radio->tx_bw);
|
|
|
|
// CW output - disable
|
|
at86rf215_radio_set_tx_dac_input_iq(&radio->sys->modem,
|
|
GET_MODEM_CH(radio->type),
|
|
0, 0x7E,
|
|
0, 0x3F);
|
|
|
|
// apply the state
|
|
caribou_smi_set_driver_streaming_state(&radio->sys->smi, smi_stream_tx_channel);
|
|
caribou_fpga_set_smi_ctrl_data_direction (&radio->sys->fpga, 0);
|
|
//cariboulite_radio_set_modem_state(radio, cariboulite_radio_state_cmd_tx);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
//=========================================================================
|
|
int cariboulite_radio_set_cw_outputs(cariboulite_radio_state_st* radio, bool lo_out, bool cw_out)
|
|
{
|
|
if (radio->lo_output && radio->type == cariboulite_channel_hif)
|
|
{
|
|
radio->lo_output = lo_out;
|
|
}
|
|
else
|
|
{
|
|
radio->lo_output = false;
|
|
}
|
|
radio->cw_output = cw_out;
|
|
|
|
return 0;
|
|
}
|
|
|
|
//=========================================================================
|
|
int cariboulite_radio_get_cw_outputs(cariboulite_radio_state_st* radio, bool *lo_out, bool *cw_out)
|
|
{
|
|
if (lo_out) *lo_out = radio->lo_output;
|
|
if (cw_out) *cw_out = radio->cw_output;
|
|
|
|
return 0;
|
|
}
|
|
|
|
//=========================================================================
|
|
// I/O Functions
|
|
//=========================================================================
|
|
int cariboulite_radio_read_samples(cariboulite_radio_state_st* radio,
|
|
cariboulite_sample_complex_int16* buffer,
|
|
cariboulite_sample_meta* metadata,
|
|
size_t length)
|
|
{
|
|
int ret = 0;
|
|
|
|
// CaribouSMI read
|
|
ret = caribou_smi_read( &radio->sys->smi,
|
|
radio->smi_channel_id,
|
|
(caribou_smi_sample_complex_int16*)buffer,
|
|
(caribou_smi_sample_meta*)metadata,
|
|
length);
|
|
if (ret < 0)
|
|
{
|
|
// -2 reserved for debug mode
|
|
if (ret == -1) {ZF_LOGE("SMI reading operation failed");}
|
|
else if (ret == -2) {}
|
|
else if (ret == -3) {ZF_LOGE("SMI data synchronization failed");}
|
|
|
|
}
|
|
else if (ret == 0)
|
|
{
|
|
ZF_LOGD("SMI reading operation returned timeout");
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
//=========================================================================
|
|
int cariboulite_radio_write_samples(cariboulite_radio_state_st* radio,
|
|
cariboulite_sample_complex_int16* buffer,
|
|
size_t length)
|
|
{
|
|
// Caribou SMI write
|
|
int ret = caribou_smi_write(&radio->sys->smi,
|
|
radio->smi_channel_id,
|
|
(caribou_smi_sample_complex_int16*)buffer,
|
|
length);
|
|
if (ret < 0)
|
|
{
|
|
ZF_LOGE("SMI writing operation failed");
|
|
}
|
|
else if (ret == 0)
|
|
{
|
|
ZF_LOGD("SMI writing operation returned timeout");
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
//=========================================================================
|
|
size_t cariboulite_radio_get_native_mtu_size_samples(cariboulite_radio_state_st* radio)
|
|
{
|
|
size_t num_samples = caribou_smi_get_native_batch_samples(&radio->sys->smi);
|
|
//printf("DEBUG: native num samples: %lu\n", num_samples);
|
|
return num_samples;
|
|
}
|