cariboulabs-cariboulite/software/libcariboulite/src/cariboulite_radio.c

1033 wiersze
40 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.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)
//=========================================================================
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->smi_channel_id = GET_SMI_CH(type);
// activation of the channel
if (cariboulite_radio_activate_channel(radio, cariboulite_channel_dir_rx, true) != 0)
{
return -1;
}
usleep(10000);
return cariboulite_radio_activate_channel(radio, cariboulite_channel_dir_rx, false);
}
//=========================================================================
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 = at86rf215_radio_state_cmd_trx_off;
// Type specific
if (radio->type == cariboulite_channel_6g)
{
caribou_fpga_set_io_ctrl_mode (&radio->sys->fpga, 0, caribou_fpga_io_ctrl_rfm_low_power);
}
}
//=======================================================================================
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);
default:
return -1;
break;
}
}
//=========================================================================
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, at86rf215_radio_state_cmd_en *state)
{
radio->state = 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, at86rf215_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(at86rf215_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,
at86rf215_radio_rx_bw_en rx_bw)
{
at86rf215_radio_f_cut_en fcut = at86rf215_radio_rx_f_cut_half_fs;
// Automatically calculate the digital f_cut
if (rx_bw >= at86rf215_radio_rx_bw_BW160KHZ_IF250KHZ && rx_bw <= at86rf215_radio_rx_bw_BW500KHZ_IF500KHZ)
fcut = at86rf215_radio_rx_f_cut_0_25_half_fs;
else if (rx_bw >= at86rf215_radio_rx_bw_BW630KHZ_IF1000KHZ && rx_bw <= at86rf215_radio_rx_bw_BW630KHZ_IF1000KHZ)
fcut = at86rf215_radio_rx_f_cut_0_375_half_fs;
else if (rx_bw >= at86rf215_radio_rx_bw_BW800KHZ_IF1000KHZ && rx_bw <= at86rf215_radio_rx_bw_BW1000KHZ_IF1000KHZ)
fcut = at86rf215_radio_rx_f_cut_0_5_half_fs;
else if (rx_bw >= at86rf215_radio_rx_bw_BW1250KHZ_IF2000KHZ && rx_bw <= at86rf215_radio_rx_bw_BW1250KHZ_IF2000KHZ)
fcut = at86rf215_radio_rx_f_cut_0_75_half_fs;
else
fcut = at86rf215_radio_rx_f_cut_half_fs;
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 = rx_bw,
.fcut = radio->rx_fcut, // keep the same
.fs = radio->rx_fs, // keep the same
};
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_get_rx_bandwidth(cariboulite_radio_state_st* radio,
at86rf215_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 = cfg.bw;
radio->rx_fcut = cfg.fcut;
radio->rx_fs = cfg.fs;
if (rx_bw) *rx_bw = radio->rx_bw;
return 0;
}
//=========================================================================
int cariboulite_radio_set_rx_samp_cutoff(cariboulite_radio_state_st* radio,
at86rf215_radio_sample_rate_en rx_sample_rate,
at86rf215_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 = radio->rx_bw, // keep the same
.fcut = rx_cutoff,
.fs = 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;
return 0;
}
//=========================================================================
int cariboulite_radio_get_rx_samp_cutoff(cariboulite_radio_state_st* radio,
at86rf215_radio_sample_rate_en *rx_sample_rate,
at86rf215_radio_f_cut_en *rx_cutoff)
{
cariboulite_radio_get_rx_bandwidth(radio, NULL);
if (rx_sample_rate) *rx_sample_rate = radio->rx_fs;
if (rx_cutoff) *rx_cutoff = radio->rx_fcut;
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 > 12) tx_power_dbm = 12;
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_6g)
{
if (tx_power_dbm < -12) tx_power_dbm = -12;
if (tx_power_dbm > 9) tx_power_dbm = 9;
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;
}
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 = radio->tx_bw, // same as before
.digital_bw = radio->tx_fcut, // same as before
.fs = 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;
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_6g)
{
actual_model = 0.000031f*x*x*x*x - 0.002344f*x*x*x + 0.040478f*x*x + 0.712209f*x - 11.168502;
}
radio->tx_power = (int)(actual_model);
radio->tx_bw = cfg.analog_bw;
radio->tx_fcut = cfg.digital_bw;
radio->tx_fs = 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,
at86rf215_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 = tx_bw,
.digital_bw = radio->tx_fcut, // same as before
.fs = 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_get_tx_bandwidth(cariboulite_radio_state_st* radio,
at86rf215_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_set_tx_samp_cutoff(cariboulite_radio_state_st* radio,
at86rf215_radio_sample_rate_en tx_sample_rate,
at86rf215_radio_f_cut_en tx_cutoff)
{
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 = radio->tx_bw, // same as before
.digital_bw = tx_cutoff,
.fs = tx_sample_rate,
.direct_modulation = 0,
};
at86rf215_radio_setup_tx_ctrl(&radio->sys->modem, GET_MODEM_CH(radio->type), &cfg);
radio->tx_fcut = tx_cutoff;
radio->tx_fs = tx_sample_rate;
return 0;
}
//=========================================================================
int cariboulite_radio_get_tx_samp_cutoff(cariboulite_radio_state_st* radio,
at86rf215_radio_sample_rate_en *tx_sample_rate,
at86rf215_radio_f_cut_en *tx_cutoff)
{
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;
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_6g)
{
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;
}
//=================================================
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_6g && 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;
}
//=========================================================================
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;
int error = 0;
cariboulite_ext_ref_freq_en ext_ref_choice = cariboulite_ext_ref_off;
cariboulite_conversion_dir_en conversion_direction = conversion_dir_none;
//--------------------------------------------------------------------------------
// SUB 1GHZ CONFIGURATION
//--------------------------------------------------------------------------------
if (radio->type == cariboulite_channel_s1g)
{
if ( (f_rf >= CARIBOULITE_S1G_MIN1 && f_rf <= CARIBOULITE_S1G_MAX1) ||
(f_rf >= CARIBOULITE_S1G_MIN2 && f_rf <= CARIBOULITE_S1G_MAX2) )
{
// setup modem frequency <= f_rf
if (break_before_make)
{
at86rf215_radio_set_state(&radio->sys->modem,
at86rf215_rf_channel_900mhz,
at86rf215_radio_state_cmd_trx_off);
radio->state = at86rf215_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->modem_pll_locked = cariboulite_radio_wait_modem_lock(radio, 3);
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 S1G channel - %.2f Hz", f_rf);
error = -1;
}
}
//--------------------------------------------------------------------------------
// ISM 2.4 GHZ CONFIGURATION
//--------------------------------------------------------------------------------
else if (radio->type == cariboulite_channel_6g &&
radio->sys->board_info.numeric_product_id == system_type_cariboulite_ism)
{
if (f_rf >= CARIBOULITE_2G4_MIN && f_rf <= CARIBOULITE_2G4_MAX)
{
// setup modem frequency <= f_rf
if (break_before_make)
{
at86rf215_radio_set_state(&radio->sys->modem,
at86rf215_rf_channel_2400mhz,
at86rf215_radio_state_cmd_trx_off);
radio->state = at86rf215_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->modem_pll_locked = cariboulite_radio_wait_modem_lock(radio, 3);
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 HiF channel - %.2f Hz", f_rf);
error = -1;
}
}
//--------------------------------------------------------------------------------
// FULL 30-6GHz CONFIGURATION
//--------------------------------------------------------------------------------
else if (radio->type == cariboulite_channel_6g &&
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
at86rf215_radio_set_state( &radio->sys->modem,
at86rf215_rf_channel_2400mhz,
at86rf215_radio_state_cmd_trx_off);
radio->state = at86rf215_radio_state_cmd_trx_off;
caribou_fpga_set_io_ctrl_mode (&radio->sys->fpga, 0, caribou_fpga_io_ctrl_rfm_low_power);
}
// Calculate the best ext_ref
double f_rf_mod_32 = (f_rf / 32e6);
double f_rf_mod_26 = (f_rf / 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;
ext_ref_choice = f_rf_mod_32 > f_rf_mod_26 ? cariboulite_ext_ref_32mhz : cariboulite_ext_ref_26mhz;
cariboulite_radio_ext_ref (radio->sys, ext_ref_choice);
// Decide the conversion direction and IF/RF/LO
//-------------------------------------
if (f_rf >= CARIBOULITE_6G_MIN &&
f_rf < (CARIBOULITE_2G4_MIN) )
{
// 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 = lo_act_freq - modem_act_freq;
// setup fpga RFFE <= upconvert (tx / rx)
conversion_direction = conversion_dir_up;
}
//-------------------------------------
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;
}
//-------------------------------------
else if ( f_rf >= (CARIBOULITE_2G4_MAX) &&
f_rf < CARIBOULITE_6G_MAX )
{
// region #3 - DOWN-CONVERSION
// setup modem frequency <= CARIBOULITE_2G4_MIN
modem_act_freq = (double)at86rf215_setup_channel (&radio->sys->modem,
at86rf215_rf_channel_2400mhz,
(uint32_t)(CARIBOULITE_2G4_MIN));
// 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;
}
//-------------------------------------
else
{
ZF_LOGE("unsupported frequency for 6GHz channel - %.2f Hz", f_rf);
error = -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
at86rf215_radio_set_state( &radio->sys->modem,
GET_MODEM_CH(radio->type),
at86rf215_radio_state_cmd_tx_prep);
radio->state = at86rf215_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)", lo_act_freq);
if (!radio->modem_pll_locked) ZF_LOGE("PLL MODEM failed to lock IF frequency (%.2f Hz)", modem_act_freq);
error = 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;
}
if (error >= 0)
{
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;
}
//=========================================================================
int cariboulite_radio_activate_channel(cariboulite_radio_state_st* radio,
cariboulite_channel_dir_en dir,
bool activate)
{
radio->channel_direction = dir;
radio->active = activate;
ZF_LOGD("Activating channel %d, dir = %s, activate = %d", radio->type, radio->channel_direction==cariboulite_channel_dir_rx?"RX":"TX", activate);
// Deactivation first
if (activate == false)
{
int ret = 0;
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_io_ctrl_dig (&radio->sys->fpga, (radio->type == cariboulite_channel_s1g) ? 0 : 1, 0);
// if we deactivate, first shut off the smi stream
ret = caribou_smi_set_driver_streaming_state(&radio->sys->smi, smi_stream_idle);
usleep(30000);
// then deactivate the modem's stream
at86rf215_radio_set_state( &radio->sys->modem,
GET_MODEM_CH(radio->type),
at86rf215_radio_state_cmd_trx_off);
radio->state = at86rf215_radio_state_cmd_trx_off;
//caribou_smi_set_driver_streaming_state(&radio->sys->smi, smi_stream_idle);
ZF_LOGD("Setup Modem state trx_off");
return ret;
}
// if the channel state is active, turn it off before reactivating
if (radio->state != at86rf215_radio_state_cmd_tx_prep)
{
at86rf215_radio_set_state( &radio->sys->modem,
GET_MODEM_CH(radio->type),
at86rf215_radio_state_cmd_tx_prep);
radio->state = at86rf215_radio_state_cmd_tx_prep;
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_activate_channel(radio, radio->channel_direction, false);
return -1;
}
usleep(10000);
}
//===========================================================
// 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 = 0,
.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,
};
at86rf215_setup_iq_if(&radio->sys->modem, &modem_iq_config);
at86rf215_radio_set_state( &radio->sys->modem,
GET_MODEM_CH(radio->type),
at86rf215_radio_state_cmd_rx);
radio->state = at86rf215_radio_state_cmd_rx;
ZF_LOGD("Setup Modem state cmd_rx");
usleep(10000);
// after modem is activated turn on the the smi stream
smi_stream_state_en smi_state = smi_stream_idle;
if (radio->smi_channel_id == caribou_smi_channel_900)
smi_state = smi_stream_rx_channel_0;
else if (radio->smi_channel_id == caribou_smi_channel_2400)
smi_state = smi_stream_rx_channel_1;
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_io_ctrl_dig (&radio->sys->fpga, radio->type == cariboulite_channel_s1g? 0 : 1, 0);
// apply the state
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 = 0,
.drv_strength = at86rf215_iq_drive_current_4ma,
.common_mode_voltage = at86rf215_iq_common_mode_v_ieee1596_1v2,
.tx_control_with_iq_if = true,
.radio09_mode = at86rf215_iq_if_mode,
.radio24_mode = at86rf215_iq_if_mode,
.clock_skew = at86rf215_iq_clock_data_skew_4_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_6g &&
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->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, radio->cw_output?at86rf215_radio_tx_cut_off_80khz:radio->tx_bw);
// CW output - constant I/Q values override
at86rf215_radio_set_tx_dac_input_iq(&radio->sys->modem,
GET_MODEM_CH(radio->type),
radio->cw_output, 0x7E,
radio->cw_output, 0x3F);
// transition to state TX
at86rf215_radio_set_state(&radio->sys->modem,
GET_MODEM_CH(radio->type),
at86rf215_radio_state_cmd_tx);
radio->state = at86rf215_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_6g)
{
radio->lo_output = lo_out;
}
else
{
radio->lo_output = false;
}
radio->cw_output = cw_out;
if (cw_out)
{
radio->channel_direction = cariboulite_channel_dir_tx;
}
else
{
radio->channel_direction = cariboulite_channel_dir_rx;
}
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,
caribou_smi_sample_complex_int16* buffer,
caribou_smi_sample_meta* metadata,
size_t length)
{
int ret = 0;
// CaribouSMI read
ret = caribou_smi_read(&radio->sys->smi, radio->smi_channel_id, buffer, metadata, length);
if (ret < 0)
{
// -2 reserved for debug mode
if (ret == -1) ZF_LOGE("SMI reading operation failed");
}
else if (ret == 0)
{
ZF_LOGD("SMI reading operation returned timeout");
}
return ret;
}
//=========================================================================
int cariboulite_radio_write_samples(cariboulite_radio_state_st* radio,
caribou_smi_sample_complex_int16* buffer,
size_t length)
{
// Caribou SMI write
int ret = caribou_smi_write(&radio->sys->smi, radio->smi_channel_id, 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)
{
return caribou_smi_get_native_batch_samples(&radio->sys->smi);
}