前言
笔者计划通过无线定位系列文章、系统的描述 TDOA 无线定位和混合定位相关技术知识点,
并以实践来验证此定位系统精度。
笔者从实践出发、本篇直接走读无线定位系统关键节点、网关 SX1302 源码框架,并在源码走读过程
中、着重分析与无线定位相关的PPS时间的来龙去脉、并在后期文章中以实际代码讲解 TDOA 无线定位
实现过程及多网关综合定位内容,敬请期待。
semtech 公司在 2020年06月份推出 LR1110\LR1120 两款GNSS、WIFI和Lora(LR-HFSS)混合
定位芯片、并提供’定位云服务’的接入、国内与腾讯云合作,腾讯云也提供定位云服务接入,这是
笔者对混合无线定位技术背景简单描述、此用意看官自行审度。
第1节 主程序代码走读
主线程基本功能:
<1>. 读取 *.conf.json 文件内容、并解析内容把变量赋值到相关全局变量中;
<2>. 启动各子线程、子线程清单如下所述;
<3>. 固定周期定时检测gps的时间戳、并上报网关的状态信息;
<4>. 等待退出信号量、网络断开信号量和各子线程退出.
子线程清单.
/* threads */
void thread_up(void); //> 上行线程:负责接收lora模块的数据、并把数据通过网络上传至网络服务器;
void thread_down(void); //> 下行线程:负责接收服务器的数据,并把数据通过lora无线下方给终端模块;
void thread_jit(void); //> jit 下行数据处理线程:
void thread_gps(void); //> gps 线程时间同步线程
void thread_valid(void); //> 时钟校正线程
void thread_spectral_scan(void); //> SCAN扫描线程:
主程序源码基本功能就这么多,笔者就不贴出源码对照了,下面进入我们本章主题 thread_jit 线程的代码走读。
第2节 thread_jit 程序框架描述
此线程是负责发送 queue 中数据内容至 Lora 模块的线程,此数据来源是 thread_down 线程填充至队列中,
其他线程如果有数据需要下方、也可以向此队列中填充数据。下面源码中笔者注释、内容比较简短清晰的。
2.2 thread_jit 程序框架
/* --- THREAD 3: CHECKING PACKETS TO BE SENT FROM JIT QUEUE AND SEND THEM --- */
void thread_jit(void) {
int result = LGW_HAL_SUCCESS;
struct lgw_pkt_tx_s pkt;
int pkt_index = -1;
uint32_t current_concentrator_time;
enum jit_error_e jit_result;
enum jit_pkt_type_e pkt_type;
uint8_t tx_status;
int i;
while (!exit_sig && !quit_sig) {
wait_ms(10);
for (i = 0; i < LGW_RF_CHAIN_NB; i++) {
/* transfer data and metadata to the concentrator, and schedule TX */
pthread_mutex_lock(&mx_concent);
lgw_get_instcnt(¤t_concentrator_time);
pthread_mutex_unlock(&mx_concent);
//> 根据时间戳提取队列中待发送数据index
jit_result = jit_peek(&jit_queue[i], current_concentrator_time, &pkt_index);
if (jit_result == JIT_ERROR_OK) {
if (pkt_index > -1) {
//> 提取指定 index 数据帧内容
jit_result = jit_dequeue(&jit_queue[i], pkt_index, &pkt, &pkt_type);
if (jit_result == JIT_ERROR_OK) {
/* update beacon stats */
if (pkt_type == JIT_PKT_TYPE_BEACON) {
/* Compensate breacon frequency with xtal error */
pthread_mutex_lock(&mx_xcorr);
pkt.freq_hz = (uint32_t)(xtal_correct * (double)pkt.freq_hz);
MSG_DEBUG(DEBUG_BEACON, "beacon_pkt.freq_hz=%u (xtal_correct=%.15lf)\n", pkt.freq_hz, xtal_correct);
pthread_mutex_unlock(&mx_xcorr);
/* Update statistics */
pthread_mutex_lock(&mx_meas_dw);
meas_nb_beacon_sent += 1;
pthread_mutex_unlock(&mx_meas_dw);
MSG("INFO: Beacon dequeued (count_us=%u)\n", pkt.count_us);
}
/* check if concentrator is free for sending new packet */
pthread_mutex_lock(&mx_concent); /* may have to wait for a fetch to finish */
//> 检测 网关 rf_chain 状态
result = lgw_status(pkt.rf_chain, TX_STATUS, &tx_status);
pthread_mutex_unlock(&mx_concent); /* free concentrator ASAP */
if (result == LGW_HAL_ERROR) {
MSG("WARNING: [jit%d] lgw_status failed\n", i);
} else {
if (tx_status == TX_EMITTING) {
MSG("ERROR: concentrator is currently emitting on rf_chain %d\n", i);
print_tx_status(tx_status);
continue;
} else if (tx_status == TX_SCHEDULED) {
MSG("WARNING: a downlink was already scheduled on rf_chain %d, overwritting it...\n", i);
print_tx_status(tx_status);
} else {
/* Nothing to do */
}
}
/* send packet to concentrator */
pthread_mutex_lock(&mx_concent); /* may have to wait for a fetch to finish */
if (spectral_scan_params.enable == true) {
result = lgw_spectral_scan_abort(); //> 终止 scan
if (result != LGW_HAL_SUCCESS) {
MSG("WARNING: [jit%d] lgw_spectral_scan_abort failed\n", i);
}
}
//> 发送数据至 Lora 模块
result = lgw_send(&pkt);
pthread_mutex_unlock(&mx_concent); /* free concentrator ASAP */
if (result != LGW_HAL_SUCCESS) {
pthread_mutex_lock(&mx_meas_dw);
meas_nb_tx_fail += 1;
pthread_mutex_unlock(&mx_meas_dw);
MSG("WARNING: [jit] lgw_send failed on rf_chain %d\n", i);
continue;
} else {
pthread_mutex_lock(&mx_meas_dw);
meas_nb_tx_ok += 1;
pthread_mutex_unlock(&mx_meas_dw);
MSG_DEBUG(DEBUG_PKT_FWD, "lgw_send done on rf_chain %d: count_us=%u\n", i, pkt.count_us);
}
} else {
MSG("ERROR: jit_dequeue failed on rf_chain %d with %d\n", i, jit_result);
}
}
} else if (jit_result == JIT_ERROR_EMPTY) {
/* Do nothing, it can happen */
} else {
MSG("ERROR: jit_peek failed on rf_chain %d with %d\n", i, jit_result);
}
}
}
MSG("\nINFO: End of JIT thread\n");
}
函数 lgw_send(&pkt); 调用到
int sx1302_send(lgw_radio_type_t radio_type, struct lgw_tx_gain_lut_s * tx_lut, bool lwan_public, struct lgw_conf_rxif_s * context_fsk, struct lgw_pkt_tx_s * pkt_data) ;
摘录函数中选择lora、fsk模式、及配置 rf前端芯片代码,如下:
/* Select the proper modem */
switch (pkt_data->modulation) {
case MOD_CW:
err = lgw_reg_w(SX1302_REG_TX_TOP_GEN_CFG_0_MODULATION_TYPE(pkt_data->rf_chain), 0x00);
CHECK_ERR(err);
err = lgw_reg_w(SX1302_REG_TX_TOP_TX_RFFE_IF_CTRL_TX_IF_SRC(pkt_data->rf_chain), 0x00);
CHECK_ERR(err);
break;
case MOD_LORA:
err = lgw_reg_w(SX1302_REG_TX_TOP_GEN_CFG_0_MODULATION_TYPE(pkt_data->rf_chain), 0x00);
CHECK_ERR(err);
err = lgw_reg_w(SX1302_REG_TX_TOP_TX_RFFE_IF_CTRL_TX_IF_SRC(pkt_data->rf_chain), 0x01);
CHECK_ERR(err);
break;
case MOD_FSK:
err = lgw_reg_w(SX1302_REG_TX_TOP_GEN_CFG_0_MODULATION_TYPE(pkt_data->rf_chain), 0x01);
CHECK_ERR(err);
err = lgw_reg_w(SX1302_REG_TX_TOP_TX_RFFE_IF_CTRL_TX_IF_SRC(pkt_data->rf_chain), 0x02);
CHECK_ERR(err);
break;
default:
DEBUG_MSG("ERROR: modulation type not supported\n");
return LGW_REG_ERROR;
}
/* Find the proper index in the TX gain LUT according to requested rf_power */
for (pow_index = tx_lut->size-1; pow_index > 0; pow_index--) {
if (tx_lut->lut[pow_index].rf_power <= pkt_data->rf_power) {
break;
}
}
DEBUG_PRINTF("INFO: selecting TX Gain LUT index %u\n", pow_index);
/* loading calibrated Tx DC offsets */
err = lgw_reg_w(SX1302_REG_TX_TOP_TX_RFFE_IF_I_OFFSET_I_OFFSET(pkt_data->rf_chain), tx_lut->lut[pow_index].offset_i);
CHECK_ERR(err);
err = lgw_reg_w(SX1302_REG_TX_TOP_TX_RFFE_IF_Q_OFFSET_Q_OFFSET(pkt_data->rf_chain), tx_lut->lut[pow_index].offset_q);
CHECK_ERR(err);
DEBUG_PRINTF("INFO: Applying IQ offset (i:%d, q:%d)\n", tx_lut->lut[pow_index].offset_i, tx_lut->lut[pow_index].offset_q);
/* Set the power parameters to be used for TX */
switch (radio_type) { //>根据配置文件、选择 radio_type 是SX1250 还是SX1255, 笔者选用 MiniPCIE sx1302 的发送模块 sx1250模块;
case LGW_RADIO_TYPE_SX1250:
pa_en = (tx_lut->lut[pow_index].pa_gain > 0) ? 1 : 0; /* only 1 bit used to control the external PA */
power = (pa_en << 6) | tx_lut->lut[pow_index].pwr_idx;
break;
case LGW_RADIO_TYPE_SX1255:
case LGW_RADIO_TYPE_SX1257:
power = (tx_lut->lut[pow_index].pa_gain << 6) | (tx_lut->lut[pow_index].dac_gain << 4) | tx_lut->lut[pow_index].mix_gain;
break;
default:
DEBUG_MSG("ERROR: radio type not supported\n");
return LGW_REG_ERROR;
}
err = lgw_reg_w(SX1302_REG_TX_TOP_AGC_TX_PWR_AGC_TX_PWR(pkt_data->rf_chain), power);
CHECK_ERR(err);
/* Set the power parameters to be used for TX */
switch (radio_type) {
case LGW_RADIO_TYPE_SX1250:
pa_en = (tx_lut->lut[pow_index].pa_gain > 0) ? 1 : 0; /* only 1 bit used to control the external PA */
power = (pa_en << 6) | tx_lut->lut[pow_index].pwr_idx;
break;
case LGW_RADIO_TYPE_SX1255:
case LGW_RADIO_TYPE_SX1257:
power = (tx_lut->lut[pow_index].pa_gain << 6) | (tx_lut->lut[pow_index].dac_gain << 4) | tx_lut->lut[pow_index].mix_gain;
break;
default:
DEBUG_MSG("ERROR: radio type not supported\n");
return LGW_REG_ERROR;
}
err = lgw_reg_w(SX1302_REG_TX_TOP_AGC_TX_PWR_AGC_TX_PWR(pkt_data->rf_chain), power);
CHECK_ERR(err);
/* Set digital gain */
err = lgw_reg_w(SX1302_REG_TX_TOP_TX_RFFE_IF_IQ_GAIN_IQ_GAIN(pkt_data->rf_chain), tx_lut->lut[pow_index].dig_gain);
CHECK_ERR(err);
/* Set Tx frequency */
if (radio_type == LGW_RADIO_TYPE_SX1255) {
freq_reg = SX1302_FREQ_TO_REG(pkt_data->freq_hz * 2);
} else {
freq_reg = SX1302_FREQ_TO_REG(pkt_data->freq_hz);
}
err = lgw_reg_w(SX1302_REG_TX_TOP_TX_RFFE_IF_FREQ_RF_H_FREQ_RF(pkt_data->rf_chain), (freq_reg >> 16) & 0xFF);
CHECK_ERR(err);
err = lgw_reg_w(SX1302_REG_TX_TOP_TX_RFFE_IF_FREQ_RF_M_FREQ_RF(pkt_data->rf_chain), (freq_reg >> 8) & 0xFF);
CHECK_ERR(err);
err = lgw_reg_w(SX1302_REG_TX_TOP_TX_RFFE_IF_FREQ_RF_L_FREQ_RF(pkt_data->rf_chain), (freq_reg >> 0) & 0xFF);
CHECK_ERR(err);
/* Set AGC bandwidth and modulation type*/
switch (pkt_data->modulation) {
case MOD_LORA:
mod_bw = pkt_data->bandwidth;
break;
case MOD_CW:
case MOD_FSK:
mod_bw = (0x01 << 7) | pkt_data->bandwidth;
break;
default:
printf("ERROR: Modulation not supported\n");
return LGW_REG_ERROR;
}
err = lgw_reg_w(SX1302_REG_TX_TOP_AGC_TX_BW_AGC_TX_BW(pkt_data->rf_chain), mod_bw);
CHECK_ERR(err);
/* Set AGC bandwidth and modulation type*/
switch (pkt_data->modulation) {
case MOD_LORA:
mod_bw = pkt_data->bandwidth;
break;
case MOD_CW:
case MOD_FSK:
mod_bw = (0x01 << 7) | pkt_data->bandwidth;
break;
default:
printf("ERROR: Modulation not supported\n");
return LGW_REG_ERROR;
}
err = lgw_reg_w(SX1302_REG_TX_TOP_AGC_TX_BW_AGC_TX_BW(pkt_data->rf_chain), mod_bw);
CHECK_ERR(err);
根据配置文件、网关选择通讯模式、参数、增益配置,接下来看看,Lora、FSK两种模式、数据下发具体配置;
/* Configure modem */
switch (pkt_data->modulation) {
case MOD_CW:
/* Set frequency deviation */
freq_dev = ceil(fabs( (float)pkt_data->freq_offset / 10) ) * 10e3;
printf("CW: f_dev %d Hz\n", (int)(freq_dev));
fdev_reg = SX1302_FREQ_TO_REG(freq_dev);
err = lgw_reg_w(SX1302_REG_TX_TOP_TX_RFFE_IF_FREQ_DEV_H_FREQ_DEV(pkt_data->rf_chain), (fdev_reg >> 8) & 0xFF);
CHECK_ERR(err);
err = lgw_reg_w(SX1302_REG_TX_TOP_TX_RFFE_IF_FREQ_DEV_L_FREQ_DEV(pkt_data->rf_chain), (fdev_reg >> 0) & 0xFF);
CHECK_ERR(err);
/* Send frequency deviation to AGC fw for radio config */
fdev_reg = SX1250_FREQ_TO_REG(freq_dev);
err = lgw_reg_w(SX1302_REG_AGC_MCU_MCU_MAIL_BOX_WR_DATA_BYTE2_MCU_MAIL_BOX_WR_DATA, (fdev_reg >> 16) & 0xFF); /* Needed by AGC to configure the sx1250 */
CHECK_ERR(err);
err = lgw_reg_w(SX1302_REG_AGC_MCU_MCU_MAIL_BOX_WR_DATA_BYTE1_MCU_MAIL_BOX_WR_DATA, (fdev_reg >> 8) & 0xFF); /* Needed by AGC to configure the sx1250 */
CHECK_ERR(err);
err = lgw_reg_w(SX1302_REG_AGC_MCU_MCU_MAIL_BOX_WR_DATA_BYTE0_MCU_MAIL_BOX_WR_DATA, (fdev_reg >> 0) & 0xFF); /* Needed by AGC to configure the sx1250 */
CHECK_ERR(err);
/* Set the frequency offset (ratio of the frequency deviation)*/
printf("CW: IF test mod freq %d\n", (int)(((float)pkt_data->freq_offset*1e3*64/(float)freq_dev)));
err = lgw_reg_w(SX1302_REG_TX_TOP_TX_RFFE_IF_TEST_MOD_FREQ(pkt_data->rf_chain), (int)(((float)pkt_data->freq_offset*1e3*64/(float)freq_dev)));
CHECK_ERR(err);
break;
case MOD_LORA:
/* Set bandwidth */
freq_dev = lgw_bw_getval(pkt_data->bandwidth) / 2;
fdev_reg = SX1302_FREQ_TO_REG(freq_dev);
err = lgw_reg_w(SX1302_REG_TX_TOP_TX_RFFE_IF_FREQ_DEV_H_FREQ_DEV(pkt_data->rf_chain), (fdev_reg >> 8) & 0xFF);
CHECK_ERR(err);
err = lgw_reg_w(SX1302_REG_TX_TOP_TX_RFFE_IF_FREQ_DEV_L_FREQ_DEV(pkt_data->rf_chain), (fdev_reg >> 0) & 0xFF);
CHECK_ERR(err);
err = lgw_reg_w(SX1302_REG_TX_TOP_TXRX_CFG0_0_MODEM_BW(pkt_data->rf_chain), pkt_data->bandwidth);
CHECK_ERR(err);
/* Preamble length */
if (pkt_data->preamble == 0) { /* if not explicit, use recommended LoRa preamble size */
pkt_data->preamble = STD_LORA_PREAMBLE;
} else if (pkt_data->preamble < MIN_LORA_PREAMBLE) { /* enforce minimum preamble size */
pkt_data->preamble = MIN_LORA_PREAMBLE;
DEBUG_MSG("Note: preamble length adjusted to respect minimum LoRa preamble size\n");
}
err = lgw_reg_w(SX1302_REG_TX_TOP_TXRX_CFG1_3_PREAMBLE_SYMB_NB(pkt_data->rf_chain), (pkt_data->preamble >> 8) & 0xFF); /* MSB */
CHECK_ERR(err);
err = lgw_reg_w(SX1302_REG_TX_TOP_TXRX_CFG1_2_PREAMBLE_SYMB_NB(pkt_data->rf_chain), (pkt_data->preamble >> 0) & 0xFF); /* LSB */
CHECK_ERR(err);
/* LoRa datarate */
err = lgw_reg_w(SX1302_REG_TX_TOP_TXRX_CFG0_0_MODEM_SF(pkt_data->rf_chain), pkt_data->datarate);
CHECK_ERR(err);
/* Chirp filtering */
chirp_lowpass = (pkt_data->datarate < 10) ? 6 : 7;
err = lgw_reg_w(SX1302_REG_TX_TOP_TX_CFG0_0_CHIRP_LOWPASS(pkt_data->rf_chain), (int32_t)chirp_lowpass);
CHECK_ERR(err);
/* Coding Rate */
err = lgw_reg_w(SX1302_REG_TX_TOP_TXRX_CFG0_1_CODING_RATE(pkt_data->rf_chain), pkt_data->coderate);
CHECK_ERR(err);
/* Start LoRa modem */
err = lgw_reg_w(SX1302_REG_TX_TOP_TXRX_CFG0_2_MODEM_EN(pkt_data->rf_chain), 1);
CHECK_ERR(err);
err = lgw_reg_w(SX1302_REG_TX_TOP_TXRX_CFG0_2_CADRXTX(pkt_data->rf_chain), 2);
CHECK_ERR(err);
err = lgw_reg_w(SX1302_REG_TX_TOP_TXRX_CFG1_1_MODEM_START(pkt_data->rf_chain), 1);
CHECK_ERR(err);
err = lgw_reg_w(SX1302_REG_TX_TOP_TX_CFG0_0_CONTINUOUS(pkt_data->rf_chain), 0);
CHECK_ERR(err);
/* Modulation options */
err = lgw_reg_w(SX1302_REG_TX_TOP_TX_CFG0_0_CHIRP_INVERT(pkt_data->rf_chain), (pkt_data->invert_pol) ? 1 : 0);
CHECK_ERR(err);
err = lgw_reg_w(SX1302_REG_TX_TOP_TXRX_CFG0_2_IMPLICIT_HEADER(pkt_data->rf_chain), (pkt_data->no_header) ? 1 : 0);
CHECK_ERR(err);
err = lgw_reg_w(SX1302_REG_TX_TOP_TXRX_CFG0_2_CRC_EN(pkt_data->rf_chain), (pkt_data->no_crc) ? 0 : 1);
CHECK_ERR(err);
/* Syncword */
if ((lwan_public == false) || (pkt_data->datarate == DR_LORA_SF5) || (pkt_data->datarate == DR_LORA_SF6)) {
DEBUG_MSG("Setting LoRa syncword 0x12\n");
err = lgw_reg_w(SX1302_REG_TX_TOP_FRAME_SYNCH_0_PEAK1_POS(pkt_data->rf_chain), 2);
CHECK_ERR(err);
err = lgw_reg_w(SX1302_REG_TX_TOP_FRAME_SYNCH_1_PEAK2_POS(pkt_data->rf_chain), 4);
CHECK_ERR(err);
} else {
DEBUG_MSG("Setting LoRa syncword 0x34\n");
err = lgw_reg_w(SX1302_REG_TX_TOP_FRAME_SYNCH_0_PEAK1_POS(pkt_data->rf_chain), 6);
CHECK_ERR(err);
err = lgw_reg_w(SX1302_REG_TX_TOP_FRAME_SYNCH_1_PEAK2_POS(pkt_data->rf_chain), 8);
CHECK_ERR(err);
}
/* Set Fine Sync for SF5/SF6 */
if ((pkt_data->datarate == DR_LORA_SF5) || (pkt_data->datarate == DR_LORA_SF6)) {
DEBUG_MSG("Enable Fine Sync\n");
err = lgw_reg_w(SX1302_REG_TX_TOP_TXRX_CFG0_2_FINE_SYNCH_EN(pkt_data->rf_chain), 1);
CHECK_ERR(err);
} else {
DEBUG_MSG("Disable Fine Sync\n");
err = lgw_reg_w(SX1302_REG_TX_TOP_TXRX_CFG0_2_FINE_SYNCH_EN(pkt_data->rf_chain), 0);
CHECK_ERR(err);
}
/* Set Payload length */
err = lgw_reg_w(SX1302_REG_TX_TOP_TXRX_CFG0_3_PAYLOAD_LENGTH(pkt_data->rf_chain), pkt_data->size);
CHECK_ERR(err);
/* Set PPM offset (low datarate optimization) */
err = lgw_reg_w(SX1302_REG_TX_TOP_TXRX_CFG0_1_PPM_OFFSET_HDR_CTRL(pkt_data->rf_chain), 0);
CHECK_ERR(err);
if (SET_PPM_ON(pkt_data->bandwidth, pkt_data->datarate)) {
DEBUG_MSG("Low datarate optimization ENABLED\n");
err = lgw_reg_w(SX1302_REG_TX_TOP_TXRX_CFG0_1_PPM_OFFSET(pkt_data->rf_chain), 1);
CHECK_ERR(err);
} else {
DEBUG_MSG("Low datarate optimization DISABLED\n");
err = lgw_reg_w(SX1302_REG_TX_TOP_TXRX_CFG0_1_PPM_OFFSET(pkt_data->rf_chain), 0);
CHECK_ERR(err);
}
break;
case MOD_FSK: //> FSK 无线通讯
CHECK_NULL(context_fsk);
/* Set frequency deviation */
freq_dev = pkt_data->f_dev * 1e3;
fdev_reg = SX1302_FREQ_TO_REG(freq_dev);
err = lgw_reg_w(SX1302_REG_TX_TOP_TX_RFFE_IF_FREQ_DEV_H_FREQ_DEV(pkt_data->rf_chain), (fdev_reg >> 8) & 0xFF);
CHECK_ERR(err);
err = lgw_reg_w(SX1302_REG_TX_TOP_TX_RFFE_IF_FREQ_DEV_L_FREQ_DEV(pkt_data->rf_chain), (fdev_reg >> 0) & 0xFF);
CHECK_ERR(err);
/* Send frequency deviation to AGC fw for radio config */
fdev_reg = SX1250_FREQ_TO_REG(freq_dev);
err = lgw_reg_w(SX1302_REG_AGC_MCU_MCU_MAIL_BOX_WR_DATA_BYTE2_MCU_MAIL_BOX_WR_DATA, (fdev_reg >> 16) & 0xFF); /* Needed by AGC to configure the sx1250 */
CHECK_ERR(err);
err = lgw_reg_w(SX1302_REG_AGC_MCU_MCU_MAIL_BOX_WR_DATA_BYTE1_MCU_MAIL_BOX_WR_DATA, (fdev_reg >> 8) & 0xFF); /* Needed by AGC to configure the sx1250 */
CHECK_ERR(err);
err = lgw_reg_w(SX1302_REG_AGC_MCU_MCU_MAIL_BOX_WR_DATA_BYTE0_MCU_MAIL_BOX_WR_DATA, (fdev_reg >> 0) & 0xFF); /* Needed by AGC to configure the sx1250 */
CHECK_ERR(err);
/* Modulation parameters */
err = lgw_reg_w(SX1302_REG_TX_TOP_FSK_CFG_0_PKT_MODE(pkt_data->rf_chain), 1); /* Variable length */
CHECK_ERR(err);
err = lgw_reg_w(SX1302_REG_TX_TOP_FSK_CFG_0_CRC_EN(pkt_data->rf_chain), (pkt_data->no_crc) ? 0 : 1);
CHECK_ERR(err);
err = lgw_reg_w(SX1302_REG_TX_TOP_FSK_CFG_0_CRC_IBM(pkt_data->rf_chain), 0); /* CCITT CRC */
CHECK_ERR(err);
err = lgw_reg_w(SX1302_REG_TX_TOP_FSK_CFG_0_DCFREE_ENC(pkt_data->rf_chain), 2); /* Whitening Encoding */
CHECK_ERR(err);
err = lgw_reg_w(SX1302_REG_TX_TOP_FSK_MOD_FSK_GAUSSIAN_EN(pkt_data->rf_chain), 1);
CHECK_ERR(err);
err = lgw_reg_w(SX1302_REG_TX_TOP_FSK_MOD_FSK_GAUSSIAN_SELECT_BT(pkt_data->rf_chain), 2);
CHECK_ERR(err);
err = lgw_reg_w(SX1302_REG_TX_TOP_FSK_MOD_FSK_REF_PATTERN_EN(pkt_data->rf_chain), 1);
CHECK_ERR(err);
err = lgw_reg_w(SX1302_REG_TX_TOP_FSK_MOD_FSK_REF_PATTERN_SIZE(pkt_data->rf_chain), context_fsk->sync_word_size - 1);
CHECK_ERR(err);
/* Syncword */
fsk_sync_word_reg = context_fsk->sync_word << (8 * (8 - context_fsk->sync_word_size));
err = lgw_reg_w(SX1302_REG_TX_TOP_FSK_REF_PATTERN_BYTE0_FSK_REF_PATTERN(pkt_data->rf_chain), (uint8_t)(fsk_sync_word_reg >> 0));
CHECK_ERR(err);
err = lgw_reg_w(SX1302_REG_TX_TOP_FSK_REF_PATTERN_BYTE1_FSK_REF_PATTERN(pkt_data->rf_chain), (uint8_t)(fsk_sync_word_reg >> 8));
CHECK_ERR(err);
err = lgw_reg_w(SX1302_REG_TX_TOP_FSK_REF_PATTERN_BYTE2_FSK_REF_PATTERN(pkt_data->rf_chain), (uint8_t)(fsk_sync_word_reg >> 16));
CHECK_ERR(err);
err = lgw_reg_w(SX1302_REG_TX_TOP_FSK_REF_PATTERN_BYTE3_FSK_REF_PATTERN(pkt_data->rf_chain), (uint8_t)(fsk_sync_word_reg >> 24));
CHECK_ERR(err);
err = lgw_reg_w(SX1302_REG_TX_TOP_FSK_REF_PATTERN_BYTE4_FSK_REF_PATTERN(pkt_data->rf_chain), (uint8_t)(fsk_sync_word_reg >> 32));
CHECK_ERR(err);
err = lgw_reg_w(SX1302_REG_TX_TOP_FSK_REF_PATTERN_BYTE5_FSK_REF_PATTERN(pkt_data->rf_chain), (uint8_t)(fsk_sync_word_reg >> 40));
CHECK_ERR(err);
err = lgw_reg_w(SX1302_REG_TX_TOP_FSK_REF_PATTERN_BYTE6_FSK_REF_PATTERN(pkt_data->rf_chain), (uint8_t)(fsk_sync_word_reg >> 48));
CHECK_ERR(err);
err = lgw_reg_w(SX1302_REG_TX_TOP_FSK_REF_PATTERN_BYTE7_FSK_REF_PATTERN(pkt_data->rf_chain), (uint8_t)(fsk_sync_word_reg >> 56));
CHECK_ERR(err);
err = lgw_reg_w(SX1302_REG_TX_TOP_FSK_MOD_FSK_PREAMBLE_SEQ(pkt_data->rf_chain), 0);
CHECK_ERR(err);
/* Set datarate */
fsk_br_reg = 32000000 / pkt_data->datarate;
buff[0] = (uint8_t)(fsk_br_reg >> 8);
buff[1] = (uint8_t)(fsk_br_reg >> 0);
err = lgw_reg_wb(SX1302_REG_TX_TOP_FSK_BIT_RATE_MSB_BIT_RATE(pkt_data->rf_chain), buff, 2);
CHECK_ERR(err);
/* Preamble length */
if (pkt_data->preamble == 0) { /* if not explicit, use LoRaWAN preamble size */
pkt_data->preamble = STD_FSK_PREAMBLE;
} else if (pkt_data->preamble < MIN_FSK_PREAMBLE) { /* enforce minimum preamble size */
pkt_data->preamble = MIN_FSK_PREAMBLE;
DEBUG_MSG("Note: preamble length adjusted to respect minimum FSK preamble size\n");
}
buff[0] = (uint8_t)(pkt_data->preamble >> 8);
buff[1] = (uint8_t)(pkt_data->preamble >> 0);
err = lgw_reg_wb(SX1302_REG_TX_TOP_FSK_PREAMBLE_SIZE_MSB_PREAMBLE_SIZE(pkt_data->rf_chain), buff, 2);
CHECK_ERR(err);
/* Set Payload length */
err = lgw_reg_w(SX1302_REG_TX_TOP_FSK_PKT_LEN_PKT_LENGTH(pkt_data->rf_chain), pkt_data->size);
CHECK_ERR(err);
break;
default:
printf("ERROR: Modulation not supported\n");
return LGW_REG_ERROR;
在选定网关的通讯模式后、配置发送前端参数,发送数据如下,
/* Set TX start delay */
err = sx1302_tx_set_start_delay(pkt_data->rf_chain, radio_type, pkt_data->modulation, pkt_data->bandwidth, chirp_lowpass, &tx_start_delay);
CHECK_ERR(err);
/* Write payload in transmit buffer */
err = lgw_reg_w(SX1302_REG_TX_TOP_TX_CTRL_WRITE_BUFFER(pkt_data->rf_chain), 0x01);
CHECK_ERR(err);
mem_addr = REG_SELECT(pkt_data->rf_chain, 0x5300, 0x5500);
if (pkt_data->modulation == MOD_FSK) {
err = lgw_mem_wb(mem_addr, (uint8_t *)(&(pkt_data->size)), 1); /* insert payload size in the packet for FSK variable mode (1 byte) */
CHECK_ERR(err);
err = lgw_mem_wb(mem_addr+1, &(pkt_data->payload[0]), pkt_data->size);
CHECK_ERR(err);
} else {
err = lgw_mem_wb(mem_addr, &(pkt_data->payload[0]), pkt_data->size);
CHECK_ERR(err);
}
err = lgw_reg_w(SX1302_REG_TX_TOP_TX_CTRL_WRITE_BUFFER(pkt_data->rf_chain), 0x00);
CHECK_ERR(err);
/* Trigger transmit */
DEBUG_PRINTF("Start Tx: Freq:%u %s%u size:%u preamb:%u\n", pkt_data->freq_hz, (pkt_data->modulation == MOD_LORA) ? "SF" : "DR:", pkt_data->datarate, pkt_data->size, pkt_data->preamble);
switch (pkt_data->tx_mode) {
case IMMEDIATE:
err = lgw_reg_w(SX1302_REG_TX_TOP_TX_TRIG_TX_TRIG_IMMEDIATE(pkt_data->rf_chain), 0x00); /* reset state machine */
CHECK_ERR(err);
err = lgw_reg_w(SX1302_REG_TX_TOP_TX_TRIG_TX_TRIG_IMMEDIATE(pkt_data->rf_chain), 0x01);
CHECK_ERR(err);
break;
case TIMESTAMPED:
count_us = pkt_data->count_us * 32 - tx_start_delay;
DEBUG_PRINTF("--> programming trig delay at %u (%u)\n", pkt_data->count_us - (tx_start_delay / 32), count_us);
err = lgw_reg_w(SX1302_REG_TX_TOP_TIMER_TRIG_BYTE0_TIMER_DELAYED_TRIG(pkt_data->rf_chain), (uint8_t)((count_us >> 0) & 0x000000FF));
CHECK_ERR(err);
err = lgw_reg_w(SX1302_REG_TX_TOP_TIMER_TRIG_BYTE1_TIMER_DELAYED_TRIG(pkt_data->rf_chain), (uint8_t)((count_us >> 8) & 0x000000FF));
CHECK_ERR(err);
err = lgw_reg_w(SX1302_REG_TX_TOP_TIMER_TRIG_BYTE2_TIMER_DELAYED_TRIG(pkt_data->rf_chain), (uint8_t)((count_us >> 16) & 0x000000FF));
CHECK_ERR(err);
err = lgw_reg_w(SX1302_REG_TX_TOP_TIMER_TRIG_BYTE3_TIMER_DELAYED_TRIG(pkt_data->rf_chain), (uint8_t)((count_us >> 24) & 0x000000FF));
CHECK_ERR(err);
err = lgw_reg_w(SX1302_REG_TX_TOP_TX_TRIG_TX_TRIG_DELAYED(pkt_data->rf_chain), 0x00); /* reset state machine */
CHECK_ERR(err);
err = lgw_reg_w(SX1302_REG_TX_TOP_TX_TRIG_TX_TRIG_DELAYED(pkt_data->rf_chain), 0x01);
CHECK_ERR(err);
break;
case ON_GPS:
err = lgw_reg_w(SX1302_REG_TX_TOP_TX_TRIG_TX_TRIG_GPS(pkt_data->rf_chain), 0x00); /* reset state machine */
CHECK_ERR(err);
err = lgw_reg_w(SX1302_REG_TX_TOP_TX_TRIG_TX_TRIG_GPS(pkt_data->rf_chain), 0x01);
CHECK_ERR(err);
break;
default:
printf("ERROR: TX mode not supported\n");
return LGW_REG_ERROR;
}
/* Flush write (USB BULK mode) */
err = lgw_com_flush();
CHECK_ERR(err);
/* Setting back to SINGLE BULK write mode */
err = lgw_com_set_write_mode(LGW_COM_WRITE_MODE_SINGLE);
CHECK_ERR(err);
/* Compute time spent in this function */
_meas_time_stop(2, tm, __FUNCTION__);
return LGW_REG_SUCCESS;
由代码可以看出,网关发送数据发送有三种模式,立即发送、延时方式、ON_GPS发送,其中ON_GPS发送模式还需要再研究下。
第3节 总结
线程 thread_jit 固定周期检查queue队列中待发送数据、如果有数据就提取数据、出队列、发送数据。
如果本篇文章对您有所启发或帮助、请给笔者点赞助力、鼓励笔者坚持把此系列内容尽快梳理、分享出来。
谢谢。