loragw_cal.c 43 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037
  1. /*
  2. / _____) _ | |
  3. ( (____ _____ ____ _| |_ _____ ____| |__
  4. \____ \| ___ | (_ _) ___ |/ ___) _ \
  5. _____) ) ____| | | || |_| ____( (___| | | |
  6. (______/|_____)_|_|_| \__)_____)\____)_| |_|
  7. (C)2019 Semtech
  8. Description:
  9. LoRa concentrator radio calibration functions
  10. License: Revised BSD License, see LICENSE.TXT file include in the project
  11. */
  12. /* -------------------------------------------------------------------------- */
  13. /* --- DEPENDANCIES --------------------------------------------------------- */
  14. #include <stdint.h> /* C99 types */
  15. #include <stdio.h> /* printf fprintf */
  16. #include <math.h> /* log10 */
  17. #include "loragw_reg.h"
  18. #include "loragw_aux.h"
  19. #include "loragw_hal.h"
  20. #include "loragw_sx1302.h"
  21. #include "loragw_sx125x.h"
  22. #include "loragw_cal.h"
  23. /* -------------------------------------------------------------------------- */
  24. /* --- DEBUG FLAGS ---------------------------------------------------------- */
  25. #define TX_CALIB_DONE_BY_HAL 0
  26. /* -------------------------------------------------------------------------- */
  27. /* --- PRIVATE MACROS ------------------------------------------------------- */
  28. #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
  29. #if DEBUG_CAL == 1
  30. #define DEBUG_MSG(str) fprintf(stdout, str)
  31. #define DEBUG_PRINTF(fmt, args...) fprintf(stdout,"%s:%d: "fmt, __FUNCTION__, __LINE__, args)
  32. #define CHECK_NULL(a) if(a==NULL){fprintf(stderr,"%s:%d: ERROR: NULL POINTER AS ARGUMENT\n", __FUNCTION__, __LINE__);return LGW_SPI_ERROR;}
  33. #else
  34. #define DEBUG_MSG(str)
  35. #define DEBUG_PRINTF(fmt, args...)
  36. #define CHECK_NULL(a) if(a==NULL){return LGW_SPI_ERROR;}
  37. #endif
  38. /* -------------------------------------------------------------------------- */
  39. /* --- PRIVATE CONSTANTS ---------------------------------------------------- */
  40. #define CAL_TX_TONE_FREQ_HZ 250000
  41. #define CAL_ITER 3 /* Number of calibration iterations */
  42. #define CAL_TX_CORR_DURATION 0 /* 0:1ms, 1:2ms, 2:4ms, 3:8ms */
  43. /* -------------------------------------------------------------------------- */
  44. /* --- PRIVATE VARIABLES -------------------------------------------- */
  45. /* Record Rx IQ mismatch corrections from calibration */
  46. static int8_t rf_rx_image_amp[LGW_RF_CHAIN_NB] = {0, 0};
  47. static int8_t rf_rx_image_phi[LGW_RF_CHAIN_NB] = {0, 0};
  48. /* -------------------------------------------------------------------------- */
  49. /* --- PRIVATE FUNCTIONS DEFINITION ----------------------------------------- */
  50. void cal_rx_result_init(struct lgw_sx125x_cal_rx_result_s *res_rx_min, struct lgw_sx125x_cal_rx_result_s *res_rx_max);
  51. void cal_rx_result_sort(struct lgw_sx125x_cal_rx_result_s *res_rx, struct lgw_sx125x_cal_rx_result_s *res_rx_min, struct lgw_sx125x_cal_rx_result_s *res_rx_max);
  52. bool cal_rx_result_assert(struct lgw_sx125x_cal_rx_result_s *res_rx_min, struct lgw_sx125x_cal_rx_result_s *res_rx_max);
  53. int sx125x_cal_rx_image(uint8_t rf_chain, uint32_t freq_hz, bool use_loopback, uint8_t radio_type, struct lgw_sx125x_cal_rx_result_s * res);
  54. void cal_tx_result_init(struct lgw_sx125x_cal_tx_result_s *res_tx_min, struct lgw_sx125x_cal_tx_result_s *res_tx_max);
  55. void cal_tx_result_sort(struct lgw_sx125x_cal_tx_result_s *res_tx, struct lgw_sx125x_cal_tx_result_s *res_tx_min, struct lgw_sx125x_cal_tx_result_s *res_tx_max);
  56. bool cal_tx_result_assert(struct lgw_sx125x_cal_tx_result_s *res_tx_min, struct lgw_sx125x_cal_tx_result_s *res_tx_max);
  57. int sx125x_cal_tx_dc_offset(uint8_t rf_chain, uint32_t freq_hz, uint8_t dac_gain, uint8_t mix_gain, uint8_t radio_type, struct lgw_sx125x_cal_tx_result_s * res);
  58. /* -------------------------------------------------------------------------- */
  59. /* --- PUBLIC FUNCTIONS DEFINITION ------------------------------------------ */
  60. int sx1302_cal_start(uint8_t version, struct lgw_conf_rxrf_s * rf_chain_cfg, struct lgw_tx_gain_lut_s * txgain_lut) {
  61. int i, j, k;
  62. uint8_t val;
  63. bool cal_status = false;
  64. uint8_t x_max;
  65. int x_max_idx;
  66. uint8_t dac_gain[LGW_RF_CHAIN_NB][TX_GAIN_LUT_SIZE_MAX];
  67. uint8_t mix_gain[LGW_RF_CHAIN_NB][TX_GAIN_LUT_SIZE_MAX];
  68. int8_t offset_i[LGW_RF_CHAIN_NB][TX_GAIN_LUT_SIZE_MAX];
  69. int8_t offset_q[LGW_RF_CHAIN_NB][TX_GAIN_LUT_SIZE_MAX];
  70. uint8_t nb_gains[LGW_RF_CHAIN_NB];
  71. bool unique_gains;
  72. struct lgw_sx125x_cal_rx_result_s cal_rx[CAL_ITER], cal_rx_min, cal_rx_max;
  73. struct lgw_sx125x_cal_tx_result_s cal_tx[CAL_ITER], cal_tx_min, cal_tx_max;
  74. /* Wait for AGC fw to be started, and VERSION available in mailbox */
  75. sx1302_agc_wait_status(0x01); /* fw has started, VERSION is ready in mailbox */
  76. sx1302_agc_mailbox_read(0, &val);
  77. if (val != version) {
  78. printf("ERROR: wrong CAL fw version (%d)\n", val);
  79. return LGW_HAL_ERROR;
  80. }
  81. printf("CAL FW VERSION: %d\n", val);
  82. /* notify CAL that it can resume */
  83. sx1302_agc_mailbox_write(3, 0xFF);
  84. /* Wait for AGC to acknoledge */
  85. sx1302_agc_wait_status(0x00);
  86. printf("CAL: started\n");
  87. /* Run Rx image calibration */
  88. for (i = 0; i < LGW_RF_CHAIN_NB; i++) {
  89. if (rf_chain_cfg[i].enable) {
  90. /* Calibration using the other radio for Tx */
  91. if (rf_chain_cfg[0].type == rf_chain_cfg[1].type) {
  92. cal_rx_result_init(&cal_rx_min, &cal_rx_max);
  93. for (j = 0; j < CAL_ITER; j++) {
  94. sx125x_cal_rx_image(i, rf_chain_cfg[i].freq_hz, false, rf_chain_cfg[i].type, &cal_rx[j]);
  95. cal_rx_result_sort(&cal_rx[j], &cal_rx_min, &cal_rx_max);
  96. }
  97. cal_status = cal_rx_result_assert(&cal_rx_min, &cal_rx_max);
  98. }
  99. /* If failed or different radios, run calibration using RF loopback (assuming that it is better than no calibration) */
  100. if ((cal_status == false) || (rf_chain_cfg[0].type != rf_chain_cfg[1].type)) {
  101. cal_rx_result_init(&cal_rx_min, &cal_rx_max);
  102. for (j = 0; j < CAL_ITER; j++) {
  103. sx125x_cal_rx_image(i, rf_chain_cfg[i].freq_hz, true, rf_chain_cfg[i].type, &cal_rx[j]);
  104. cal_rx_result_sort(&cal_rx[j], &cal_rx_min, &cal_rx_max);
  105. }
  106. cal_status = cal_rx_result_assert(&cal_rx_min, &cal_rx_max);
  107. }
  108. if (cal_status == false) {
  109. DEBUG_MSG("*********************************************\n");
  110. DEBUG_PRINTF("ERROR: Rx image calibration of radio %d failed\n",i);
  111. DEBUG_MSG("*********************************************\n");
  112. return LGW_HAL_ERROR;
  113. }
  114. /* Use the results of the best iteration */
  115. x_max = 0;
  116. x_max_idx = 0;
  117. for (j=0; j<CAL_ITER; j++) {
  118. if (cal_rx[j].rej > x_max) {
  119. x_max = cal_rx[j].rej;
  120. x_max_idx = j;
  121. }
  122. }
  123. rf_rx_image_amp[i] = cal_rx[x_max_idx].amp;
  124. rf_rx_image_phi[i] = cal_rx[x_max_idx].phi;
  125. DEBUG_PRINTF("INFO: Rx image calibration of radio %d succeeded. Improved image rejection from %2d to %2d dB (Amp:%3d Phi:%3d)\n", i, cal_rx[x_max_idx].rej_init, cal_rx[x_max_idx].rej, cal_rx[x_max_idx].amp, cal_rx[x_max_idx].phi);
  126. } else {
  127. rf_rx_image_amp[i] = 0;
  128. rf_rx_image_phi[i] = 0;
  129. }
  130. }
  131. /* Apply calibrated IQ mismatch compensation */
  132. lgw_reg_w(SX1302_REG_RADIO_FE_IQ_COMP_AMP_COEFF_RADIO_A_AMP_COEFF, (int32_t)rf_rx_image_amp[0]);
  133. lgw_reg_w(SX1302_REG_RADIO_FE_IQ_COMP_PHI_COEFF_RADIO_A_PHI_COEFF, (int32_t)rf_rx_image_phi[0]);
  134. lgw_reg_w(SX1302_REG_RADIO_FE_IQ_COMP_AMP_COEFF_RADIO_B_AMP_COEFF, (int32_t)rf_rx_image_amp[1]);
  135. lgw_reg_w(SX1302_REG_RADIO_FE_IQ_COMP_PHI_COEFF_RADIO_B_PHI_COEFF, (int32_t)rf_rx_image_phi[1]);
  136. /* Get List of unique combinations of DAC and mixer gains */
  137. for (k = 0; k < LGW_RF_CHAIN_NB; k++) {
  138. nb_gains[k] = 0;
  139. for (i = 0; i < txgain_lut[k].size; i++) {
  140. unique_gains = true;
  141. for (j = 0; j < nb_gains[k]; j++) {
  142. if ((txgain_lut[k].lut[i].dac_gain == dac_gain[k][j]) && (txgain_lut[k].lut[i].mix_gain == mix_gain[k][j])) {
  143. unique_gains = false;
  144. }
  145. }
  146. if (unique_gains) {
  147. dac_gain[k][nb_gains[k]] = txgain_lut[k].lut[i].dac_gain;
  148. mix_gain[k][nb_gains[k]] = txgain_lut[k].lut[i].mix_gain;
  149. nb_gains[k] += 1;
  150. }
  151. }
  152. }
  153. /* Run Tx image calibration */
  154. for (i = 0; i < LGW_RF_CHAIN_NB; i++) {
  155. if (rf_chain_cfg[i].tx_enable) {
  156. for (j = 0; j < nb_gains[i]; j++) {
  157. cal_tx_result_init(&cal_tx_min, &cal_tx_max);
  158. for (k = 0; k < CAL_ITER; k++){
  159. sx125x_cal_tx_dc_offset(i, rf_chain_cfg[i].freq_hz, dac_gain[i][j], mix_gain[i][j], rf_chain_cfg[i].type, &cal_tx[k]);
  160. cal_tx_result_sort(&cal_tx[k], &cal_tx_min, &cal_tx_max);
  161. }
  162. cal_status = cal_tx_result_assert(&cal_tx_min, &cal_tx_max);
  163. if (cal_status == false) {
  164. DEBUG_MSG("*********************************************\n");
  165. DEBUG_PRINTF("ERROR: Tx DC offset calibration of radio %d for DAC gain %d and mixer gain %2d failed\n", i, dac_gain[i][j], mix_gain[i][j]);
  166. DEBUG_MSG("*********************************************\n");
  167. return LGW_HAL_ERROR;
  168. }
  169. /* Use the results of the best iteration */
  170. x_max = 0;
  171. x_max_idx = 0;
  172. for (k = 0; k < CAL_ITER; k++) {
  173. if (cal_tx[k].rej > x_max) {
  174. x_max = cal_tx[k].rej;
  175. x_max_idx = k;
  176. }
  177. }
  178. offset_i[i][j] = cal_tx[x_max_idx].offset_i;
  179. offset_q[i][j] = cal_tx[x_max_idx].offset_q;
  180. DEBUG_PRINTF("INFO: Tx DC offset calibration of radio %d for DAC gain %d and mixer gain %2d succeeded. Improved DC rejection by %2d dB (I:%4d Q:%4d)\n", i, dac_gain[i][j], mix_gain[i][j], cal_tx[x_max_idx].rej, cal_tx[x_max_idx].offset_i, cal_tx[x_max_idx].offset_q);
  181. }
  182. }
  183. }
  184. /* Fill DC offsets in Tx LUT */
  185. for (k = 0; k < LGW_RF_CHAIN_NB; k++) {
  186. for (i = 0; i < txgain_lut[k].size; i++) {
  187. for (j = 0; j < nb_gains[k]; j++) {
  188. if ((txgain_lut[k].lut[i].dac_gain == dac_gain[k][j]) && (txgain_lut[k].lut[i].mix_gain == mix_gain[k][j])) {
  189. break;
  190. }
  191. }
  192. txgain_lut[k].lut[i].offset_i = offset_i[k][j];
  193. txgain_lut[k].lut[i].offset_q = offset_q[k][j];
  194. }
  195. }
  196. printf("-------------------------------------------------------------------\n");
  197. printf("Radio calibration completed:\n");
  198. printf(" RadioA: amp:%d phi:%d\n", rf_rx_image_amp[0], rf_rx_image_phi[0]);
  199. printf(" RadioB: amp:%d phi:%d\n", rf_rx_image_amp[1], rf_rx_image_phi[1]);
  200. for (k = 0; k < LGW_RF_CHAIN_NB; k++) {
  201. printf(" TX calibration params for rf_chain %d:\n", k);
  202. for (i = 0; i < txgain_lut[k].size; i++) {
  203. printf(" -- power:%d\tdac:%u\tmix:%u\toffset_i:%d\toffset_q:%d\n", txgain_lut[k].lut[i].rf_power, txgain_lut[k].lut[i].dac_gain, txgain_lut[k].lut[i].mix_gain, txgain_lut[k].lut[i].offset_i, txgain_lut[k].lut[i].offset_q);
  204. }
  205. }
  206. printf("-------------------------------------------------------------------\n");
  207. return LGW_HAL_SUCCESS;
  208. }
  209. /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
  210. int sx125x_cal_rx_image(uint8_t rf_chain, uint32_t freq_hz, bool use_loopback, uint8_t radio_type, struct lgw_sx125x_cal_rx_result_s * res) {
  211. uint8_t rx, tx;
  212. uint32_t rx_freq_hz, tx_freq_hz;
  213. uint32_t rx_freq_int, rx_freq_frac;
  214. uint32_t tx_freq_int, tx_freq_frac;
  215. uint8_t rx_pll_locked, tx_pll_locked;
  216. uint8_t rx_threshold = 8; /* Used by AGC to set decimation gain to increase signal and its image: value is MSB => x * 256 */
  217. printf("\n%s: rf_chain:%u, freq_hz:%u, loopback:%d, radio_type:%d\n", __FUNCTION__, rf_chain, freq_hz, use_loopback, radio_type);
  218. /* Indentify which radio is transmitting the test tone */
  219. rx = rf_chain;
  220. if (use_loopback == true) {
  221. tx = rf_chain;
  222. } else {
  223. tx = 1-rf_chain;
  224. }
  225. /* Set PLL frequencies */
  226. rx_freq_hz = freq_hz;
  227. tx_freq_hz = freq_hz + CAL_TX_TONE_FREQ_HZ;
  228. switch (radio_type) {
  229. case LGW_RADIO_TYPE_SX1255:
  230. rx_freq_int = rx_freq_hz / (SX125x_32MHz_FRAC << 7); /* integer part, gives the MSB */
  231. rx_freq_frac = ((rx_freq_hz % (SX125x_32MHz_FRAC << 7)) << 9) / SX125x_32MHz_FRAC; /* fractional part, gives middle part and LSB */
  232. tx_freq_int = tx_freq_hz / (SX125x_32MHz_FRAC << 7); /* integer part, gives the MSB */
  233. tx_freq_frac = ((tx_freq_hz % (SX125x_32MHz_FRAC << 7)) << 9) / SX125x_32MHz_FRAC; /* fractional part, gives middle part and LSB */
  234. break;
  235. case LGW_RADIO_TYPE_SX1257:
  236. rx_freq_int = rx_freq_hz / (SX125x_32MHz_FRAC << 8); /* integer part, gives the MSB */
  237. rx_freq_frac = ((rx_freq_hz % (SX125x_32MHz_FRAC << 8)) << 8) / SX125x_32MHz_FRAC; /* fractional part, gives middle part and LSB */
  238. tx_freq_int = tx_freq_hz / (SX125x_32MHz_FRAC << 8); /* integer part, gives the MSB */
  239. tx_freq_frac = ((tx_freq_hz % (SX125x_32MHz_FRAC << 8)) << 8) / SX125x_32MHz_FRAC; /* fractional part, gives middle part and LSB */
  240. break;
  241. default:
  242. DEBUG_PRINTF("ERROR: UNEXPECTED VALUE %d FOR RADIO TYPE\n", radio_type);
  243. return LGW_HAL_ERROR;
  244. }
  245. sx125x_reg_w(SX125x_REG_FRF_RX_MSB, 0xFF & rx_freq_int, rx);
  246. sx125x_reg_w(SX125x_REG_FRF_RX_MID, 0xFF & (rx_freq_frac >> 8), rx);
  247. sx125x_reg_w(SX125x_REG_FRF_RX_LSB, 0xFF & rx_freq_frac, rx);
  248. sx125x_reg_w(SX125x_REG_FRF_TX_MSB, 0xFF & tx_freq_int, tx);
  249. sx125x_reg_w(SX125x_REG_FRF_TX_MID, 0xFF & (tx_freq_frac >> 8), tx);
  250. sx125x_reg_w(SX125x_REG_FRF_TX_LSB, 0xFF & tx_freq_frac, tx);
  251. /* Radio settings for calibration */
  252. //sx125x_reg_w(SX125x_RX_ANA_GAIN__LNA_ZIN, 1, rx); /* Default: 1 */
  253. //sx125x_reg_w(SX125x_RX_ANA_GAIN__BB_GAIN, 15, rx); /* Default: 15 */
  254. //sx125x_reg_w(SX125x_RX_ANA_GAIN__LNA_GAIN, 1, rx); /* Default: 1 */
  255. sx125x_reg_w(SX125x_REG_RX_BW__BB_BW, 0, rx);
  256. sx125x_reg_w(SX125x_REG_RX_BW__ADC_TRIM, 6, rx);
  257. //sx125x_reg_w(SX125x_RX_BW__ADC_BW, 7, rx); /* Default: 7 */
  258. sx125x_reg_w(SX125x_REG_RX_PLL_BW__PLL_BW, 0, rx);
  259. sx125x_reg_w(SX125x_REG_TX_BW__PLL_BW, 0, tx);
  260. //sx125x_reg_w(SX125x_TX_BW__ANA_BW, 0, tx); /* Default: 0 */
  261. sx125x_reg_w(SX125x_REG_TX_DAC_BW, 5, tx);
  262. //sx125x_reg_w(SX125x_CLK_SELECT__DAC_CLK_SELECT, 0, tx); /* Use internal clock, in case no Tx connection from SX1302, Default: 0 */
  263. if (use_loopback == true) {
  264. sx125x_reg_w(SX125x_REG_TX_GAIN__DAC_GAIN, 3, tx);
  265. sx125x_reg_w(SX125x_REG_TX_GAIN__MIX_GAIN, 10, tx); //8
  266. sx125x_reg_w(SX125x_REG_CLK_SELECT__RF_LOOPBACK_EN, 1, tx);
  267. sx125x_reg_w(SX125x_REG_MODE, 15, tx);
  268. } else {
  269. sx125x_reg_w(SX125x_REG_TX_GAIN__DAC_GAIN, 3, tx);
  270. sx125x_reg_w(SX125x_REG_TX_GAIN__MIX_GAIN, 15, tx);
  271. sx125x_reg_w(SX125x_REG_MODE, 3, rx);
  272. sx125x_reg_w(SX125x_REG_MODE, 13, tx);
  273. }
  274. wait_ms(10);
  275. sx125x_reg_r(SX125x_REG_MODE_STATUS__RX_PLL_LOCKED, &rx_pll_locked, rx);
  276. sx125x_reg_r(SX125x_REG_MODE_STATUS__TX_PLL_LOCKED, &tx_pll_locked, tx);
  277. if ((rx_pll_locked == 0) || (tx_pll_locked == 0)) {
  278. DEBUG_MSG("ERROR: PLL failed to lock\n");
  279. return LGW_HAL_ERROR;
  280. }
  281. /* Trig calibration */
  282. /* Select radio to be connected to the Signal Analyzer (warning: RadioA:1, RadioB:0) */
  283. lgw_reg_w(SX1302_REG_RADIO_FE_SIG_ANA_CFG_RADIO_SEL, (rf_chain == 0) ? 1 : 0);
  284. /* Set calibration parameters */
  285. sx1302_agc_mailbox_write(2, rf_chain); /* Set RX test config: radioA:0 radioB:1 */
  286. sx1302_agc_mailbox_write(1, CAL_TX_TONE_FREQ_HZ * 64e-6); /* Set frequency */
  287. sx1302_agc_mailbox_write(0, CAL_TX_CORR_DURATION);
  288. sx1302_agc_mailbox_write(3, 0x00);
  289. sx1302_agc_mailbox_write(3, 0x01);
  290. sx1302_agc_wait_status(0x01);
  291. sx1302_agc_mailbox_write(3, 0x02);
  292. sx1302_agc_wait_status(0x02);
  293. sx1302_agc_mailbox_write(3, 0x03);
  294. sx1302_agc_wait_status(0x03);
  295. sx1302_agc_mailbox_write(2, 0); /* dec_gain (not used) */
  296. sx1302_agc_mailbox_write(1, rx_threshold);
  297. sx1302_agc_mailbox_write(3, 0x04);
  298. /* Get calibration results */
  299. sx1302_agc_wait_status(0x06);
  300. uint8_t threshold, cal_dec_gain, rx_sig_1, rx_sig_0;
  301. sx1302_agc_mailbox_read(3, &threshold);
  302. sx1302_agc_mailbox_read(2, &cal_dec_gain);
  303. sx1302_agc_mailbox_read(1, &rx_sig_1);
  304. sx1302_agc_mailbox_read(0, &rx_sig_0);
  305. DEBUG_PRINTF("threshold:%u, cal_dec_gain:%u, rx_sig:%u\n", threshold * 256, cal_dec_gain, rx_sig_1 * 256 + rx_sig_0);
  306. sx1302_agc_mailbox_write(3, 0x06);
  307. sx1302_agc_wait_status(0x07);
  308. uint8_t rx_img_init_0, rx_img_init_1, amp, phi;
  309. sx1302_agc_mailbox_read(3, &rx_img_init_1);
  310. sx1302_agc_mailbox_read(2, &rx_img_init_0);
  311. sx1302_agc_mailbox_read(1, &amp);
  312. sx1302_agc_mailbox_read(0, &phi);
  313. DEBUG_PRINTF("rx_img_init_0:%u, rx_img_init_1:%u, amp:%d, phi:%d\n", rx_img_init_0, rx_img_init_1, (int8_t)amp, (int8_t)phi);
  314. sx1302_agc_mailbox_write(3, 0x07);
  315. sx1302_agc_wait_status(0x08);
  316. uint8_t rx_img_0, rx_img_1, rx_noise_raw_0, rx_noise_raw_1;
  317. float rx_img, rx_noise_raw, rx_img_init, rx_sig;
  318. sx1302_agc_mailbox_read(3, &rx_img_1);
  319. sx1302_agc_mailbox_read(2, &rx_img_0);
  320. sx1302_agc_mailbox_read(1, &rx_noise_raw_1);
  321. sx1302_agc_mailbox_read(0, &rx_noise_raw_0);
  322. DEBUG_PRINTF("rx_img_1:%u, rx_img_0:%u, rx_noise_raw_1:%u, rx_noise_raw_0:%u\n", rx_img_1, rx_img_0, rx_noise_raw_1, rx_noise_raw_0);
  323. rx_sig = (float)rx_sig_1 * 256 + (float)rx_sig_0;
  324. rx_noise_raw = (float)rx_noise_raw_1 * 256 + (float)rx_noise_raw_0;
  325. rx_img_init = (float)rx_img_init_1 * 256 + (float)rx_img_init_0;
  326. rx_img = (float)rx_img_1 * 256 + (float)rx_img_0;
  327. DEBUG_PRINTF("rx_img:%u, rx_noise_raw:%u\n", (uint16_t)rx_img, (uint16_t)rx_noise_raw);
  328. sx1302_agc_mailbox_write(3, 0x08);
  329. res->amp = (int8_t)amp;
  330. res->phi = (int8_t)phi;
  331. res->snr = (uint16_t)(20 * log10(rx_sig/rx_noise_raw));
  332. res->rej_init = (uint16_t)(20 * log10(rx_sig/rx_img_init));
  333. res->rej = (uint16_t)(20 * log10(rx_sig/rx_img));
  334. DEBUG_PRINTF("snr:%u, rej:%u, rej_init:%u\n", res->snr, res->rej, res->rej_init);
  335. /* Wait for calibration to be completed */
  336. DEBUG_MSG(" CAL: waiting for RX calibration to complete...\n");
  337. sx1302_agc_wait_status((rf_chain == 0) ? 0x11 : 0x22);
  338. DEBUG_MSG("CAL: RX Calibration Done\n");
  339. printf("%s, RESULT: rf_chain:%u amp:%d phi:%d\n", __FUNCTION__, rf_chain, res->amp, res->phi);
  340. return LGW_HAL_SUCCESS;
  341. }
  342. /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
  343. int sx125x_cal_tx_dc_offset(uint8_t rf_chain, uint32_t freq_hz, uint8_t dac_gain, uint8_t mix_gain, uint8_t radio_type, struct lgw_sx125x_cal_tx_result_s * res) {
  344. uint32_t rx_freq_hz, tx_freq_hz;
  345. uint32_t rx_freq_int, rx_freq_frac;
  346. uint32_t tx_freq_int, tx_freq_frac;
  347. uint8_t rx_pll_locked, tx_pll_locked;
  348. uint16_t reg;
  349. uint8_t tx_threshold = 64;
  350. int i;
  351. printf("\n%s: rf_chain:%u, freq_hz:%u, dac_gain:%u, mix_gain:%u, radio_type:%d\n", __FUNCTION__, rf_chain, freq_hz, dac_gain, mix_gain, radio_type);
  352. /* Set PLL frequencies */
  353. rx_freq_hz = freq_hz - CAL_TX_TONE_FREQ_HZ;
  354. tx_freq_hz = freq_hz;
  355. switch (radio_type) {
  356. case LGW_RADIO_TYPE_SX1255:
  357. rx_freq_int = rx_freq_hz / (SX125x_32MHz_FRAC << 7); /* integer part, gives the MSB */
  358. rx_freq_frac = ((rx_freq_hz % (SX125x_32MHz_FRAC << 7)) << 9) / SX125x_32MHz_FRAC; /* fractional part, gives middle part and LSB */
  359. tx_freq_int = tx_freq_hz / (SX125x_32MHz_FRAC << 7); /* integer part, gives the MSB */
  360. tx_freq_frac = ((tx_freq_hz % (SX125x_32MHz_FRAC << 7)) << 9) / SX125x_32MHz_FRAC; /* fractional part, gives middle part and LSB */
  361. break;
  362. case LGW_RADIO_TYPE_SX1257:
  363. rx_freq_int = rx_freq_hz / (SX125x_32MHz_FRAC << 8); /* integer part, gives the MSB */
  364. rx_freq_frac = ((rx_freq_hz % (SX125x_32MHz_FRAC << 8)) << 8) / SX125x_32MHz_FRAC; /* fractional part, gives middle part and LSB */
  365. tx_freq_int = tx_freq_hz / (SX125x_32MHz_FRAC << 8); /* integer part, gives the MSB */
  366. tx_freq_frac = ((tx_freq_hz % (SX125x_32MHz_FRAC << 8)) << 8) / SX125x_32MHz_FRAC; /* fractional part, gives middle part and LSB */
  367. break;
  368. default:
  369. DEBUG_PRINTF("ERROR: UNEXPECTED VALUE %d FOR RADIO TYPE\n", radio_type);
  370. return LGW_HAL_ERROR;
  371. }
  372. sx125x_reg_w(SX125x_REG_FRF_RX_MSB, 0xFF & rx_freq_int, rf_chain);
  373. sx125x_reg_w(SX125x_REG_FRF_RX_MID, 0xFF & (rx_freq_frac >> 8), rf_chain);
  374. sx125x_reg_w(SX125x_REG_FRF_RX_LSB, 0xFF & rx_freq_frac, rf_chain);
  375. sx125x_reg_w(SX125x_REG_FRF_TX_MSB, 0xFF & tx_freq_int, rf_chain);
  376. sx125x_reg_w(SX125x_REG_FRF_TX_MID, 0xFF & (tx_freq_frac >> 8), rf_chain);
  377. sx125x_reg_w(SX125x_REG_FRF_TX_LSB, 0xFF & tx_freq_frac, rf_chain);
  378. /* Radio settings for calibration */
  379. //sx125x_reg_w(SX125x_RX_ANA_GAIN__LNA_ZIN, 1, rf_chain); /* Default: 1 */
  380. //sx125x_reg_w(SX125x_RX_ANA_GAIN__BB_GAIN, 15, rf_chain); /* Default: 15 */
  381. //sx125x_reg_w(SX125x_RX_ANA_GAIN__LNA_GAIN, 1, rf_chain); /* Default: 1 */
  382. sx125x_reg_w(SX125x_REG_RX_BW__BB_BW, 0, rf_chain);
  383. sx125x_reg_w(SX125x_REG_RX_BW__ADC_TRIM, 6, rf_chain);
  384. //sx125x_reg_w(SX125x_RX_BW__ADC_BW, 7, rf_chain); /* Default: 7 */
  385. sx125x_reg_w(SX125x_REG_RX_PLL_BW__PLL_BW, 0, rf_chain);
  386. sx125x_reg_w(SX125x_REG_TX_BW__PLL_BW, 0, rf_chain);
  387. //sx125x_reg_w(SX125x_TX_BW__ANA_BW, 0, rf_chain); /* Default: 0 */
  388. sx125x_reg_w(SX125x_REG_TX_DAC_BW, 5, rf_chain);
  389. sx125x_reg_w(SX125x_REG_CLK_SELECT__DAC_CLK_SELECT, 1, rf_chain); /* Use external clock from SX1302 */
  390. sx125x_reg_w(SX125x_REG_TX_GAIN__DAC_GAIN, dac_gain, rf_chain);
  391. sx125x_reg_w(SX125x_REG_TX_GAIN__MIX_GAIN, mix_gain, rf_chain);
  392. sx125x_reg_w(SX125x_REG_CLK_SELECT__RF_LOOPBACK_EN, 1, rf_chain);
  393. sx125x_reg_w(SX125x_REG_MODE, 15, rf_chain);
  394. wait_ms(1);
  395. sx125x_reg_r(SX125x_REG_MODE_STATUS__RX_PLL_LOCKED, &rx_pll_locked, rf_chain);
  396. sx125x_reg_r(SX125x_REG_MODE_STATUS__TX_PLL_LOCKED, &tx_pll_locked, rf_chain);
  397. if ((rx_pll_locked == 0) || (tx_pll_locked == 0)) {
  398. DEBUG_MSG("ERROR: PLL failed to lock\n");
  399. return LGW_HAL_ERROR;
  400. }
  401. /* Trig calibration */
  402. /* Select radio to be connected to the Signal Analyzer (warning: RadioA:1, RadioB:0) */
  403. lgw_reg_w(SX1302_REG_RADIO_FE_SIG_ANA_CFG_RADIO_SEL, (rf_chain == 0) ? 1 : 0);
  404. reg = REG_SELECT(rf_chain, SX1302_REG_TX_TOP_A_TX_RFFE_IF_CTRL_TX_MODE,
  405. SX1302_REG_TX_TOP_B_TX_RFFE_IF_CTRL_TX_MODE);
  406. lgw_reg_w(reg, 0);
  407. reg = REG_SELECT(rf_chain, SX1302_REG_TX_TOP_A_TX_TRIG_TX_TRIG_IMMEDIATE,
  408. SX1302_REG_TX_TOP_B_TX_TRIG_TX_TRIG_IMMEDIATE);
  409. lgw_reg_w(reg, 1);
  410. lgw_reg_w(reg, 0);
  411. reg = REG_SELECT(rf_chain, SX1302_REG_RADIO_FE_CTRL0_RADIO_A_DC_NOTCH_EN,
  412. SX1302_REG_RADIO_FE_CTRL0_RADIO_B_DC_NOTCH_EN);
  413. lgw_reg_w(reg, 1);
  414. #if TX_CALIB_DONE_BY_HAL /* For debug */
  415. lgw_reg_w(SX1302_REG_RADIO_FE_SIG_ANA_CFG_FORCE_HAL_CTRL, 1);
  416. agc_cal_tx_dc_offset(rf_chain, CAL_TX_TONE_FREQ_HZ * 64e-6, rf_rx_image_amp[rf_chain], rf_rx_image_phi[rf_chain], tx_threshold, 0, &(res->offset_i), &(res->offset_q), &(res->rej));
  417. lgw_reg_w(SX1302_REG_RADIO_FE_SIG_ANA_CFG_FORCE_HAL_CTRL, 0);
  418. #else
  419. /* Set calibration parameters */
  420. sx1302_agc_mailbox_write(2, rf_chain + 2); /* Set TX test config: radioA:2 radioB:3 */
  421. sx1302_agc_mailbox_write(1, CAL_TX_TONE_FREQ_HZ * 64e-6); /* Set frequency */
  422. sx1302_agc_mailbox_write(0, 0); /* correlation duration: 0:1ms, 1:2ms, 2:4ms, 3:8ms) */
  423. sx1302_agc_mailbox_write(3, 0x00); /* sync */
  424. sx1302_agc_mailbox_write(3, 0x01); /* sync */
  425. sx1302_agc_wait_status(0x01);
  426. sx1302_agc_mailbox_write(2, rf_rx_image_amp[rf_chain]); /* amp */
  427. sx1302_agc_mailbox_write(1, rf_rx_image_phi[rf_chain]); /* phi */
  428. sx1302_agc_mailbox_write(3, 0x02); /* sync */
  429. sx1302_agc_wait_status(0x02);
  430. sx1302_agc_mailbox_write(2, 0); /* i offset init */
  431. sx1302_agc_mailbox_write(1, 0); /* q offset init */
  432. sx1302_agc_mailbox_write(3, 0x03); /* sync */
  433. sx1302_agc_wait_status(0x03);
  434. sx1302_agc_mailbox_write(2, 0);
  435. sx1302_agc_mailbox_write(1, tx_threshold);
  436. sx1302_agc_mailbox_write(3, 0x04); /* sync */
  437. /* Get calibration results */
  438. sx1302_agc_wait_status(0x06);
  439. uint8_t threshold, cal_dec_gain, tx_sig_0, tx_sig_1;
  440. sx1302_agc_mailbox_read(3, &threshold);
  441. sx1302_agc_mailbox_read(2, &cal_dec_gain);
  442. sx1302_agc_mailbox_read(1, &tx_sig_1);
  443. sx1302_agc_mailbox_read(0, &tx_sig_0);
  444. DEBUG_PRINTF("threshold:%u, cal_dec_gain:%u, tx_sig:%u\n", threshold * 256, cal_dec_gain, tx_sig_0 * 256 + tx_sig_1);
  445. sx1302_agc_mailbox_write(3, 0x06); /* sync */
  446. sx1302_agc_wait_status(0x07);
  447. uint8_t tx_dc_0, tx_dc_1, offset_i, offset_q;
  448. float tx_sig, tx_dc;
  449. sx1302_agc_mailbox_read(3, &tx_dc_1);
  450. sx1302_agc_mailbox_read(2, &tx_dc_0);
  451. sx1302_agc_mailbox_read(1, &offset_i);
  452. sx1302_agc_mailbox_read(0, &offset_q);
  453. tx_sig = (float)tx_sig_1 * 256 + (float)tx_sig_0;
  454. tx_dc = (float)tx_dc_1 * 256 + (float)tx_dc_0;
  455. res->rej = (uint16_t)(20 * log10(tx_sig/tx_dc));
  456. res->offset_i = (int8_t)offset_i;
  457. res->offset_q = (int8_t)offset_q;
  458. DEBUG_PRINTF("tx_dc:%u, offset_i:%d, offset_q:%d\n", tx_dc_0 * 256 + tx_dc_1, (int8_t)offset_i, (int8_t)offset_q);
  459. sx1302_agc_mailbox_write(3, 0x07); /* sync */
  460. /* -----------------------------------------------*/
  461. /* DEBUG: Get IQ offsets selected for iterations */
  462. uint8_t index[12];
  463. sx1302_agc_wait_status(0x08);
  464. sx1302_agc_mailbox_read(3, &index[0]);
  465. sx1302_agc_mailbox_read(2, &index[1]);
  466. sx1302_agc_mailbox_read(1, &index[2]);
  467. sx1302_agc_mailbox_read(0, &index[3]);
  468. sx1302_agc_mailbox_write(3, 0x08); /* sync */
  469. sx1302_agc_wait_status(0x09);
  470. sx1302_agc_mailbox_read(3, &index[4]);
  471. sx1302_agc_mailbox_read(2, &index[5]);
  472. sx1302_agc_mailbox_read(1, &index[6]);
  473. sx1302_agc_mailbox_read(0, &index[7]);
  474. sx1302_agc_mailbox_write(3, 0x09); /* sync */
  475. sx1302_agc_wait_status(0x0a);
  476. sx1302_agc_mailbox_read(3, &index[8]);
  477. sx1302_agc_mailbox_read(2, &index[9]);
  478. sx1302_agc_mailbox_read(1, &index[10]);
  479. sx1302_agc_mailbox_read(0, &index[11]);
  480. sx1302_agc_mailbox_write(3, 0x0a); /* sync */
  481. #if DEBUG_CAL == 1
  482. int16_t lut_calib[9] = {64, 43, 28, 19, 13, 8, 6, 4, 2};
  483. int16_t offset_i_tmp = 0;
  484. int16_t offset_q_tmp = 0;
  485. printf("IQ sequence:\n");
  486. for (i = 0; i < 9; i++) {
  487. if (index[i] == 0) {
  488. offset_i_tmp = offset_i_tmp + 0;
  489. offset_q_tmp = offset_q_tmp + 0;
  490. }else if(index[i] == 1) {
  491. offset_i_tmp = offset_i_tmp + lut_calib[i];
  492. offset_q_tmp = offset_q_tmp + lut_calib[i];
  493. }else if(index[i] == 2) {
  494. offset_i_tmp = offset_i_tmp + lut_calib[i];
  495. offset_q_tmp = offset_q_tmp - lut_calib[i];
  496. }else if(index[i] == 3) {
  497. offset_i_tmp = offset_i_tmp - lut_calib[i];
  498. offset_q_tmp = offset_q_tmp + lut_calib[i];
  499. }else if(index[i] == 4) {
  500. offset_i_tmp = offset_i_tmp - lut_calib[i];
  501. offset_q_tmp = offset_q_tmp - lut_calib[i];
  502. }
  503. printf("i:%d q:%d\n", offset_i_tmp, offset_q_tmp);
  504. }
  505. printf("\n");
  506. #endif /* DEBUG_CAL */
  507. /* -----------------------------------------------*/
  508. /* -----------------------------------------------*/
  509. /* DEBUG: Get TX_SIG returned by siognal analyzer */
  510. uint8_t msb[40];
  511. uint8_t lsb[40];
  512. for (i = 0; i < 20; i++) {
  513. sx1302_agc_wait_status(0x0c + i);
  514. sx1302_agc_mailbox_read(3, &msb[2*i]);
  515. sx1302_agc_mailbox_read(2, &lsb[2*i]);
  516. sx1302_agc_mailbox_read(1, &msb[2*i+1]);
  517. sx1302_agc_mailbox_read(0, &lsb[2*i+1]);
  518. sx1302_agc_mailbox_write(3, 0x0c + i); /* sync */
  519. }
  520. sx1302_agc_wait_status(0x0c + 20);
  521. #if DEBUG_CAL == 1
  522. printf("TX_SIG values returned by signal analyzer:");
  523. for (i = 0; i < 40; i++) {
  524. if (i%5 == 0) {
  525. printf("\n");
  526. }
  527. printf("%u ", msb[i] * 256 + lsb[i]);
  528. }
  529. printf("\n");
  530. #endif /* DEBUG_CAL */
  531. sx1302_agc_mailbox_write(3, 0x0c + 20); /* sync */
  532. /* -----------------------------------------------*/
  533. printf("%s: RESULT: offset_i:%d offset_q:%d rej:%u\n", __FUNCTION__, res->offset_i, res->offset_q, res->rej);
  534. /* Wait for calibration to be completed */
  535. DEBUG_MSG("waiting for TX calibration to complete...\n");
  536. sx1302_agc_wait_status((rf_chain == 0) ? 0x33 : 0x44);
  537. #endif /* TX_CALIB_DONE_BY_HAL */
  538. DEBUG_MSG("TX Calibration Done\n");
  539. return LGW_HAL_SUCCESS;
  540. }
  541. /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
  542. void cal_rx_result_init(struct lgw_sx125x_cal_rx_result_s *res_rx_min, struct lgw_sx125x_cal_rx_result_s *res_rx_max) {
  543. res_rx_min->amp = 31;
  544. res_rx_min->phi = 31;
  545. res_rx_min->rej = 255;
  546. res_rx_min->rej_init = 255;
  547. res_rx_min->snr = 255;
  548. res_rx_max->amp = -32;
  549. res_rx_max->phi = -32;
  550. res_rx_max->rej = 0;
  551. res_rx_max->rej_init = 0;
  552. res_rx_max->snr = 0;
  553. }
  554. /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
  555. void cal_rx_result_sort(struct lgw_sx125x_cal_rx_result_s *res_rx, struct lgw_sx125x_cal_rx_result_s *res_rx_min, struct lgw_sx125x_cal_rx_result_s *res_rx_max) {
  556. if (res_rx->amp < res_rx_min->amp)
  557. res_rx_min->amp = res_rx->amp;
  558. if (res_rx->phi < res_rx_min->phi)
  559. res_rx_min->phi = res_rx->phi;
  560. if (res_rx->rej < res_rx_min->rej)
  561. res_rx_min->rej = res_rx->rej;
  562. if (res_rx->rej_init < res_rx_min->rej_init)
  563. res_rx_min->rej_init = res_rx->rej_init;
  564. if (res_rx->snr < res_rx_min->snr)
  565. res_rx_min->snr = res_rx->snr;
  566. if (res_rx->amp > res_rx_max->amp)
  567. res_rx_max->amp = res_rx->amp;
  568. if (res_rx->phi > res_rx_max->phi)
  569. res_rx_max->phi = res_rx->phi;
  570. if (res_rx->rej > res_rx_max->rej)
  571. res_rx_max->rej = res_rx->rej;
  572. if (res_rx->rej_init > res_rx_max->rej_init)
  573. res_rx_max->rej_init = res_rx->rej_init;
  574. if (res_rx->snr > res_rx_max->snr)
  575. res_rx_max->snr = res_rx->snr;
  576. }
  577. /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
  578. bool cal_rx_result_assert(struct lgw_sx125x_cal_rx_result_s *res_rx_min, struct lgw_sx125x_cal_rx_result_s *res_rx_max) {
  579. if ( ((res_rx_max->amp - res_rx_min->amp) > 4)
  580. || ((res_rx_max->phi - res_rx_min->phi) > 4)
  581. || (res_rx_min->rej < 50)
  582. || (res_rx_min->snr < 50) )
  583. return false;
  584. else
  585. return true;
  586. }
  587. /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
  588. void cal_tx_result_init(struct lgw_sx125x_cal_tx_result_s *res_tx_min, struct lgw_sx125x_cal_tx_result_s *res_tx_max) {
  589. res_tx_min->offset_i = 127;
  590. res_tx_min->offset_q = 127;
  591. res_tx_min->rej = 255;
  592. res_tx_min->sig = 255;
  593. res_tx_max->offset_i = -128;
  594. res_tx_max->offset_q = -128;
  595. res_tx_max->rej = 0;
  596. res_tx_max->sig = 0;
  597. }
  598. /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
  599. void cal_tx_result_sort(struct lgw_sx125x_cal_tx_result_s *res_tx, struct lgw_sx125x_cal_tx_result_s *res_tx_min, struct lgw_sx125x_cal_tx_result_s *res_tx_max) {
  600. if (res_tx->offset_i < res_tx_min->offset_i)
  601. res_tx_min->offset_i = res_tx->offset_i;
  602. if (res_tx->offset_q < res_tx_min->offset_q)
  603. res_tx_min->offset_q = res_tx->offset_q;
  604. if (res_tx->rej < res_tx_min->rej)
  605. res_tx_min->rej = res_tx->rej;
  606. if (res_tx->sig < res_tx_min->sig)
  607. res_tx_min->sig = res_tx->sig;
  608. if (res_tx->offset_i > res_tx_max->offset_i)
  609. res_tx_max->offset_i = res_tx->offset_i;
  610. if (res_tx->offset_q > res_tx_max->offset_q)
  611. res_tx_max->offset_q = res_tx->offset_q;
  612. if (res_tx->rej > res_tx_max->rej)
  613. res_tx_max->rej = res_tx->rej;
  614. if (res_tx->sig > res_tx_max->sig)
  615. res_tx_max->sig = res_tx->sig;
  616. }
  617. bool cal_tx_result_assert(struct lgw_sx125x_cal_tx_result_s *res_tx_min, struct lgw_sx125x_cal_tx_result_s *res_tx_max) {
  618. if ( ((res_tx_max->offset_i - res_tx_min->offset_i) > 4)
  619. || ((res_tx_max->offset_q - res_tx_min->offset_q) > 4)
  620. || (res_tx_min->rej < 10) )
  621. return false;
  622. else
  623. return true;
  624. }
  625. /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
  626. #if TX_CALIB_DONE_BY_HAL
  627. int8_t clip_8b(int8_t val1, int8_t val2) {
  628. int16_t a, b;
  629. a = (int16_t)val1;
  630. b = (int16_t)val2;
  631. if ( (a + b) > 127 ) {
  632. return 127;
  633. } else if ( (a+b) < -128 ) {
  634. return -128;
  635. } else {
  636. return (int8_t)(a+b);
  637. }
  638. }
  639. /* This functions implements what is being done by CAL fw for TX calibration */
  640. void agc_cal_tx_dc_offset(uint8_t rf_chain, signed char freq, char amp_hal, char phi_hal, char level_reqired, char precision, int8_t * offset_i_res, int8_t * offset_q_res, uint16_t * rej) {
  641. signed char offset_i_set[9];
  642. signed char offset_q_set[9];
  643. signed char offset_i;
  644. signed char offset_q;
  645. const signed char span[] = {64, 43, 28, 19, 13, 8, 6, 4, 2};
  646. char dec_gain;
  647. char i, j; // loop variables
  648. char idx; // max/min variables
  649. uint16_t reg;
  650. int32_t abs_corr_max_i16;
  651. int32_t abs_corr_min_i16;
  652. int32_t abs_corr_i16;
  653. int32_t tx_sig_i16;
  654. int32_t tx_dc_i16;
  655. int DEC_GAIN_MAX = 11;
  656. int DEC_GAIN_MIN = 7;
  657. int32_t val;
  658. int32_t abs_lsb, abs_msb;
  659. reg = REG_SELECT(rf_chain, SX1302_REG_RADIO_FE_IQ_COMP_AMP_COEFF_RADIO_A_AMP_COEFF,
  660. SX1302_REG_RADIO_FE_IQ_COMP_AMP_COEFF_RADIO_B_AMP_COEFF);
  661. lgw_reg_w(reg, amp_hal);
  662. reg = REG_SELECT(rf_chain, SX1302_REG_RADIO_FE_IQ_COMP_PHI_COEFF_RADIO_A_PHI_COEFF,
  663. SX1302_REG_RADIO_FE_IQ_COMP_PHI_COEFF_RADIO_B_PHI_COEFF);
  664. lgw_reg_w(reg, phi_hal);
  665. lgw_reg_w(SX1302_REG_RADIO_FE_SIG_ANA_FREQ_FREQ, freq);
  666. lgw_reg_w(SX1302_REG_RADIO_FE_SIG_ANA_CFG_DURATION, precision);
  667. lgw_reg_w(SX1302_REG_RADIO_FE_SIG_ANA_CFG_EN, 1);
  668. // Set dec gain and signal analyser according to potential maximum DC level
  669. offset_i_set[0] = 0;
  670. offset_q_set[0] = 0;
  671. offset_i_set[1] = -span[0];
  672. offset_q_set[1] = -span[0];
  673. offset_i_set[2] = -span[0];
  674. offset_q_set[2] = span[0];
  675. offset_i_set[3] = span[0];
  676. offset_q_set[3] = -span[0];
  677. offset_i_set[4] = span[0];
  678. offset_q_set[4] = span[0];
  679. for (i = DEC_GAIN_MAX; i >= DEC_GAIN_MIN; i--) {
  680. dec_gain = i;
  681. /* ------------ */
  682. reg = REG_SELECT(rf_chain, SX1302_REG_RADIO_FE_CTRL0_RADIO_A_HOST_FILTER_GAIN,
  683. SX1302_REG_RADIO_FE_CTRL0_RADIO_B_HOST_FILTER_GAIN);
  684. lgw_reg_w(reg, dec_gain);
  685. /* ------------ */
  686. reg = REG_SELECT(rf_chain, SX1302_REG_TX_TOP_A_TX_RFFE_IF_I_OFFSET_I_OFFSET,
  687. SX1302_REG_TX_TOP_B_TX_RFFE_IF_I_OFFSET_I_OFFSET);
  688. lgw_reg_w(reg, (int8_t)offset_i_set[0]);
  689. reg = REG_SELECT(rf_chain, SX1302_REG_TX_TOP_A_TX_RFFE_IF_Q_OFFSET_Q_OFFSET,
  690. SX1302_REG_TX_TOP_B_TX_RFFE_IF_Q_OFFSET_Q_OFFSET);
  691. lgw_reg_w(reg, (int8_t)offset_q_set[0]);
  692. /* ------------ */
  693. lgw_reg_w(SX1302_REG_RADIO_FE_SIG_ANA_CFG_START, 0);
  694. lgw_reg_w(SX1302_REG_RADIO_FE_SIG_ANA_CFG_START, 1);
  695. do {
  696. lgw_reg_r(SX1302_REG_RADIO_FE_SIG_ANA_CFG_VALID, &val);
  697. wait_ms(1);
  698. /* TODO: addtimeout */
  699. } while (val == 0);
  700. lgw_reg_r(SX1302_REG_RADIO_FE_SIG_ANA_ABS_LSB_CORR_ABS_OUT, &abs_lsb);
  701. lgw_reg_r(SX1302_REG_RADIO_FE_SIG_ANA_ABS_MSB_CORR_ABS_OUT, &abs_msb);
  702. /* ------------ */
  703. abs_corr_max_i16 = abs_msb * 256 + abs_lsb;
  704. idx = 0;
  705. for (j = 1; j < 5; j++) {
  706. reg = REG_SELECT(rf_chain, SX1302_REG_TX_TOP_A_TX_RFFE_IF_I_OFFSET_I_OFFSET,
  707. SX1302_REG_TX_TOP_B_TX_RFFE_IF_I_OFFSET_I_OFFSET);
  708. lgw_reg_w(reg, (int8_t)offset_i_set[j]);
  709. reg = REG_SELECT(rf_chain, SX1302_REG_TX_TOP_A_TX_RFFE_IF_Q_OFFSET_Q_OFFSET,
  710. SX1302_REG_TX_TOP_B_TX_RFFE_IF_Q_OFFSET_Q_OFFSET);
  711. lgw_reg_w(reg, (int8_t)offset_q_set[j]);
  712. lgw_reg_w(SX1302_REG_RADIO_FE_SIG_ANA_CFG_START, 0);
  713. lgw_reg_w(SX1302_REG_RADIO_FE_SIG_ANA_CFG_START, 1);
  714. do {
  715. lgw_reg_r(SX1302_REG_RADIO_FE_SIG_ANA_CFG_VALID, &val);
  716. wait_ms(1);
  717. } while (val == 0);
  718. lgw_reg_r(SX1302_REG_RADIO_FE_SIG_ANA_ABS_LSB_CORR_ABS_OUT, &abs_lsb);
  719. lgw_reg_r(SX1302_REG_RADIO_FE_SIG_ANA_ABS_MSB_CORR_ABS_OUT, &abs_msb);
  720. abs_corr_i16 = abs_msb * 256 + abs_lsb;
  721. if (abs_corr_i16 > abs_corr_max_i16) {
  722. abs_corr_max_i16 = abs_corr_i16;
  723. idx = j;
  724. }
  725. }
  726. if (abs_corr_max_i16 > (level_reqired * 256)) {
  727. break;
  728. }
  729. }
  730. printf("dec_gain:%d\n", dec_gain);
  731. // store the max results
  732. tx_sig_i16 = abs_corr_max_i16;
  733. printf("tx_sig:%d\n", tx_sig_i16);
  734. // Calbration algorithm
  735. offset_i = 0;
  736. offset_q = 0;
  737. for (i = 0; i<sizeof (span); i++) {
  738. offset_i_set[0] = offset_i;
  739. offset_q_set[0] = offset_q;
  740. offset_i_set[1] = clip_8b(offset_i, span[i]);
  741. offset_q_set[1] = clip_8b(offset_q, span[i]);
  742. offset_i_set[2] = clip_8b(offset_i, span[i]);
  743. offset_q_set[2] = clip_8b(offset_q, -span[i]);
  744. offset_i_set[3] = clip_8b(offset_i, -span[i]);
  745. offset_q_set[3] = clip_8b(offset_q, span[i]);
  746. offset_i_set[4] = clip_8b(offset_i, -span[i]);
  747. offset_q_set[4] = clip_8b(offset_q, -span[i]);
  748. //center point
  749. reg = REG_SELECT(rf_chain, SX1302_REG_TX_TOP_A_TX_RFFE_IF_I_OFFSET_I_OFFSET,
  750. SX1302_REG_TX_TOP_B_TX_RFFE_IF_I_OFFSET_I_OFFSET);
  751. lgw_reg_w(reg, (int8_t)offset_i_set[0]);
  752. reg = REG_SELECT(rf_chain, SX1302_REG_TX_TOP_A_TX_RFFE_IF_Q_OFFSET_Q_OFFSET,
  753. SX1302_REG_TX_TOP_B_TX_RFFE_IF_Q_OFFSET_Q_OFFSET);
  754. lgw_reg_w(reg, (int8_t)offset_q_set[0]);
  755. lgw_reg_w(SX1302_REG_RADIO_FE_SIG_ANA_CFG_START, 0);
  756. lgw_reg_w(SX1302_REG_RADIO_FE_SIG_ANA_CFG_START, 1);
  757. do {
  758. lgw_reg_r(SX1302_REG_RADIO_FE_SIG_ANA_CFG_VALID, &val);
  759. wait_ms(1);
  760. } while (val == 0);
  761. lgw_reg_r(SX1302_REG_RADIO_FE_SIG_ANA_ABS_LSB_CORR_ABS_OUT, &abs_lsb);
  762. lgw_reg_r(SX1302_REG_RADIO_FE_SIG_ANA_ABS_MSB_CORR_ABS_OUT, &abs_msb);
  763. abs_corr_min_i16 = abs_msb * 256 + abs_lsb;
  764. printf("abs_corr_min_i16:%d ", abs_corr_min_i16);
  765. idx = 0;
  766. //four points around
  767. for (j = 1; j < 5; j++) {
  768. reg = REG_SELECT(rf_chain, SX1302_REG_TX_TOP_A_TX_RFFE_IF_I_OFFSET_I_OFFSET,
  769. SX1302_REG_TX_TOP_B_TX_RFFE_IF_I_OFFSET_I_OFFSET);
  770. lgw_reg_w(reg, (int8_t)offset_i_set[j]);
  771. reg = REG_SELECT(rf_chain, SX1302_REG_TX_TOP_A_TX_RFFE_IF_Q_OFFSET_Q_OFFSET,
  772. SX1302_REG_TX_TOP_B_TX_RFFE_IF_Q_OFFSET_Q_OFFSET);
  773. lgw_reg_w(reg, (int8_t)offset_q_set[j]);
  774. lgw_reg_w(SX1302_REG_RADIO_FE_SIG_ANA_CFG_START, 0);
  775. lgw_reg_w(SX1302_REG_RADIO_FE_SIG_ANA_CFG_START, 1);
  776. do {
  777. lgw_reg_r(SX1302_REG_RADIO_FE_SIG_ANA_CFG_VALID, &val);
  778. wait_ms(1);
  779. } while (val == 0);
  780. lgw_reg_r(SX1302_REG_RADIO_FE_SIG_ANA_ABS_LSB_CORR_ABS_OUT, &abs_lsb);
  781. lgw_reg_r(SX1302_REG_RADIO_FE_SIG_ANA_ABS_MSB_CORR_ABS_OUT, &abs_msb);
  782. abs_corr_i16 = abs_msb * 256 + abs_lsb;
  783. printf("abs_corr_i16:%d ", abs_corr_i16);
  784. if (abs_corr_i16 < abs_corr_min_i16) {
  785. abs_corr_min_i16 = abs_corr_i16;
  786. idx = j;
  787. }
  788. }
  789. printf("\n");
  790. offset_i = offset_i_set[idx];
  791. offset_q = offset_q_set[idx];
  792. }
  793. offset_i_set[0] = clip_8b(offset_i, -1);
  794. offset_q_set[0] = clip_8b(offset_q, -1);
  795. offset_i_set[1] = clip_8b(offset_i, -1);
  796. offset_q_set[1] = offset_q;
  797. offset_i_set[2] = clip_8b(offset_i, -1);
  798. offset_q_set[2] = clip_8b(offset_q, 1);
  799. offset_i_set[3] = offset_i;
  800. offset_q_set[3] = clip_8b(offset_q, -1);
  801. offset_i_set[4] = offset_i;
  802. offset_q_set[4] = offset_q;
  803. offset_i_set[5] = offset_i;
  804. offset_q_set[5] = clip_8b(offset_q, 1);
  805. offset_i_set[6] = clip_8b(offset_i, 1);
  806. offset_q_set[6] = clip_8b(offset_q, -1);
  807. offset_i_set[7] = clip_8b(offset_i, 1);
  808. offset_q_set[7] = offset_q;
  809. offset_i_set[8] = clip_8b(offset_i, 1);
  810. offset_q_set[8] = clip_8b(offset_q, 1);
  811. //center point
  812. idx = 0;
  813. reg = REG_SELECT(rf_chain, SX1302_REG_TX_TOP_A_TX_RFFE_IF_I_OFFSET_I_OFFSET,
  814. SX1302_REG_TX_TOP_B_TX_RFFE_IF_I_OFFSET_I_OFFSET);
  815. lgw_reg_w(reg, (int8_t)offset_i_set[0]);
  816. reg = REG_SELECT(rf_chain, SX1302_REG_TX_TOP_A_TX_RFFE_IF_Q_OFFSET_Q_OFFSET,
  817. SX1302_REG_TX_TOP_B_TX_RFFE_IF_Q_OFFSET_Q_OFFSET);
  818. lgw_reg_w(reg, (int8_t)offset_q_set[0]);
  819. lgw_reg_w(SX1302_REG_RADIO_FE_SIG_ANA_CFG_START, 0);
  820. lgw_reg_w(SX1302_REG_RADIO_FE_SIG_ANA_CFG_START, 1);
  821. do {
  822. lgw_reg_r(SX1302_REG_RADIO_FE_SIG_ANA_CFG_VALID, &val);
  823. wait_ms(1);
  824. } while (val == 0);
  825. lgw_reg_r(SX1302_REG_RADIO_FE_SIG_ANA_ABS_LSB_CORR_ABS_OUT, &abs_lsb);
  826. lgw_reg_r(SX1302_REG_RADIO_FE_SIG_ANA_ABS_MSB_CORR_ABS_OUT, &abs_msb);
  827. abs_corr_min_i16 = abs_msb * 256 + abs_lsb;
  828. //8 points around
  829. for (j = 1; j < 9; j++) {
  830. reg = REG_SELECT(rf_chain, SX1302_REG_TX_TOP_A_TX_RFFE_IF_I_OFFSET_I_OFFSET,
  831. SX1302_REG_TX_TOP_B_TX_RFFE_IF_I_OFFSET_I_OFFSET);
  832. lgw_reg_w(reg, (int8_t)offset_i_set[j]);
  833. reg = REG_SELECT(rf_chain, SX1302_REG_TX_TOP_A_TX_RFFE_IF_Q_OFFSET_Q_OFFSET,
  834. SX1302_REG_TX_TOP_B_TX_RFFE_IF_Q_OFFSET_Q_OFFSET);
  835. lgw_reg_w(reg, (int8_t)offset_q_set[j]);
  836. lgw_reg_w(SX1302_REG_RADIO_FE_SIG_ANA_CFG_START, 0);
  837. lgw_reg_w(SX1302_REG_RADIO_FE_SIG_ANA_CFG_START, 1);
  838. do {
  839. lgw_reg_r(SX1302_REG_RADIO_FE_SIG_ANA_CFG_VALID, &val);
  840. wait_ms(1);
  841. } while (val == 0);
  842. lgw_reg_r(SX1302_REG_RADIO_FE_SIG_ANA_ABS_LSB_CORR_ABS_OUT, &abs_lsb);
  843. lgw_reg_r(SX1302_REG_RADIO_FE_SIG_ANA_ABS_MSB_CORR_ABS_OUT, &abs_msb);
  844. abs_corr_i16 = abs_msb * 256 + abs_lsb;
  845. if (abs_corr_i16 < abs_corr_min_i16) {
  846. abs_corr_min_i16 = abs_corr_i16;
  847. idx = j;
  848. }
  849. }
  850. offset_i = offset_i_set[idx];
  851. offset_q = offset_q_set[idx];
  852. tx_dc_i16 = abs_corr_min_i16;
  853. printf("tx_dc:%d\n", tx_dc_i16);
  854. // Return results of calibration
  855. *rej = 20 * log10(tx_sig_i16/(tx_dc_i16 + 1));
  856. *offset_i_res = (int8_t)offset_i;
  857. *offset_q_res = (int8_t)offset_q;
  858. printf("offset_i:%d offset_q:%d rej:%u\n", offset_i, offset_q, *rej);
  859. }
  860. #endif /* TX_CALIB_DONE_BY_HAL */
  861. /* --- EOF ------------------------------------------------------------------ */