/* CP detection algorithm taken from: * "SSS Detection Method for Initial Cell Search in 3GPP LTE FDD/TDD Dual Mode Receiver" * by Jung-In Kim et al. */ srslte_cp_t srslte_sync_detect_cp(srslte_sync_t *q, const cf_t *input, uint32_t peak_pos) { float R_norm=0, R_ext=0, C_norm=0, C_ext=0; float M_norm=0, M_ext=0; uint32_t cp_norm_len = SRSLTE_CP_LEN_NORM(7, q->fft_size); uint32_t cp_ext_len = SRSLTE_CP_LEN_EXT(q->fft_size); uint32_t nof_symbols = peak_pos/(q->fft_size+cp_ext_len); if (nof_symbols > 3) { nof_symbols = 3; } if (nof_symbols > 0) { const cf_t *input_cp_norm = &input[peak_pos-nof_symbols*(q->fft_size+cp_norm_len)]; const cf_t *input_cp_ext = &input[peak_pos-nof_symbols*(q->fft_size+cp_ext_len)]; for (int i=0;i<nof_symbols;i++) { R_norm += crealf(srslte_vec_dot_prod_conj_ccc(&input_cp_norm[q->fft_size], input_cp_norm, cp_norm_len)); C_norm += cp_norm_len * srslte_vec_avg_power_cf(input_cp_norm, cp_norm_len); input_cp_norm += q->fft_size+cp_norm_len; } if (C_norm > 0) { M_norm = R_norm/C_norm; } q->M_norm_avg = SRSLTE_VEC_EMA(M_norm/nof_symbols, q->M_norm_avg, CP_EMA_ALPHA); for (int i=0;i<nof_symbols;i++) { R_ext += crealf(srslte_vec_dot_prod_conj_ccc(&input_cp_ext[q->fft_size], input_cp_ext, cp_ext_len)); C_ext += cp_ext_len * srslte_vec_avg_power_cf(input_cp_ext, cp_ext_len); input_cp_ext += q->fft_size+cp_ext_len; } if (C_ext > 0) { M_ext = R_ext/C_ext; } q->M_ext_avg = SRSLTE_VEC_EMA(M_ext/nof_symbols, q->M_ext_avg, CP_EMA_ALPHA); if (q->M_norm_avg > q->M_ext_avg) { return SRSLTE_CP_NORM; } else if (q->M_norm_avg < q->M_ext_avg) { return SRSLTE_CP_EXT; } else { if (R_norm > R_ext) { return SRSLTE_CP_NORM; } else { return SRSLTE_CP_EXT; } } } else { return SRSLTE_CP_NORM; } }
int main(int argc, char **argv) { int ret; cf_t *sf_buffer; prog_args_t prog_args; srslte_cell_t cell; int64_t sf_cnt; srslte_ue_sync_t ue_sync; srslte_ue_mib_t ue_mib; void *uhd; srslte_ue_dl_t ue_dl; srslte_ofdm_t fft; srslte_chest_dl_t chest; uint32_t nframes=0; uint32_t nof_trials = 0; uint32_t sfn = 0; // system frame number int n; uint8_t bch_payload[SRSLTE_BCH_PAYLOAD_LEN]; uint32_t sfn_offset; float rssi_utra=0,rssi=0, rsrp=0, rsrq=0, snr=0; cf_t *ce[SRSLTE_MAX_PORTS]; if (parse_args(&prog_args, argc, argv)) { exit(-1); } if (prog_args.uhd_gain > 0) { printf("Opening UHD device...\n"); if (cuhd_open(prog_args.uhd_args, &uhd)) { fprintf(stderr, "Error opening uhd\n"); exit(-1); } cuhd_set_rx_gain(uhd, prog_args.uhd_gain); } else { printf("Opening UHD device with threaded RX Gain control ...\n"); if (cuhd_open_th(prog_args.uhd_args, &uhd, false)) { fprintf(stderr, "Error opening uhd\n"); exit(-1); } cuhd_set_rx_gain(uhd, 50); } sigset_t sigset; sigemptyset(&sigset); sigaddset(&sigset, SIGINT); sigprocmask(SIG_UNBLOCK, &sigset, NULL); signal(SIGINT, sig_int_handler); cuhd_set_master_clock_rate(uhd, 30.72e6); /* set receiver frequency */ cuhd_set_rx_freq(uhd, (double) prog_args.uhd_freq); cuhd_rx_wait_lo_locked(uhd); printf("Tunning receiver to %.3f MHz\n", (double ) prog_args.uhd_freq/1000000); uint32_t ntrial=0; do { ret = cuhd_search_and_decode_mib(uhd, &cell_detect_config, prog_args.force_N_id_2, &cell); if (ret < 0) { fprintf(stderr, "Error searching for cell\n"); exit(-1); } else if (ret == 0 && !go_exit) { printf("Cell not found after %d trials. Trying again (Press Ctrl+C to exit)\n", ntrial++); } } while (ret == 0 && !go_exit); if (go_exit) { exit(0); } /* set sampling frequency */ int srate = srslte_sampling_freq_hz(cell.nof_prb); if (srate != -1) { if (srate < 10e6) { cuhd_set_master_clock_rate(uhd, 4*srate); } else { cuhd_set_master_clock_rate(uhd, srate); } printf("Setting sampling rate %.2f MHz\n", (float) srate/1000000); float srate_uhd = cuhd_set_rx_srate(uhd, (double) srate); if (srate_uhd != srate) { fprintf(stderr, "Could not set sampling rate\n"); exit(-1); } } else { fprintf(stderr, "Invalid number of PRB %d\n", cell.nof_prb); exit(-1); } INFO("Stopping UHD and flushing buffer...\n",0); cuhd_stop_rx_stream(uhd); cuhd_flush_buffer(uhd); if (srslte_ue_sync_init(&ue_sync, cell, cuhd_recv_wrapper, uhd)) { fprintf(stderr, "Error initiating ue_sync\n"); return -1; } if (srslte_ue_dl_init(&ue_dl, cell)) { fprintf(stderr, "Error initiating UE downlink processing module\n"); return -1; } if (srslte_ue_mib_init(&ue_mib, cell)) { fprintf(stderr, "Error initaiting UE MIB decoder\n"); return -1; } /* Configure downlink receiver for the SI-RNTI since will be the only one we'll use */ srslte_ue_dl_set_rnti(&ue_dl, SRSLTE_SIRNTI); /* Initialize subframe counter */ sf_cnt = 0; if (srslte_ofdm_rx_init(&fft, cell.cp, cell.nof_prb)) { fprintf(stderr, "Error initiating FFT\n"); return -1; } if (srslte_chest_dl_init(&chest, cell)) { fprintf(stderr, "Error initiating channel estimator\n"); return -1; } int sf_re = SRSLTE_SF_LEN_RE(cell.nof_prb, cell.cp); cf_t *sf_symbols = srslte_vec_malloc(sf_re * sizeof(cf_t)); for (int i=0;i<SRSLTE_MAX_PORTS;i++) { ce[i] = srslte_vec_malloc(sizeof(cf_t) * sf_re); } cuhd_start_rx_stream(uhd); float rx_gain_offset = 0; /* Main loop */ while ((sf_cnt < prog_args.nof_subframes || prog_args.nof_subframes == -1) && !go_exit) { ret = srslte_ue_sync_get_buffer(&ue_sync, &sf_buffer); if (ret < 0) { fprintf(stderr, "Error calling srslte_ue_sync_work()\n"); } /* srslte_ue_sync_get_buffer returns 1 if successfully read 1 aligned subframe */ if (ret == 1) { switch (state) { case DECODE_MIB: if (srslte_ue_sync_get_sfidx(&ue_sync) == 0) { srslte_pbch_decode_reset(&ue_mib.pbch); n = srslte_ue_mib_decode(&ue_mib, sf_buffer, bch_payload, NULL, &sfn_offset); if (n < 0) { fprintf(stderr, "Error decoding UE MIB\n"); return -1; } else if (n == SRSLTE_UE_MIB_FOUND) { srslte_pbch_mib_unpack(bch_payload, &cell, &sfn); printf("Decoded MIB. SFN: %d, offset: %d\n", sfn, sfn_offset); sfn = (sfn + sfn_offset)%1024; state = DECODE_SIB; } } break; case DECODE_SIB: /* We are looking for SI Blocks, search only in appropiate places */ if ((srslte_ue_sync_get_sfidx(&ue_sync) == 5 && (sfn%2)==0)) { n = srslte_ue_dl_decode_rnti_rv(&ue_dl, sf_buffer, data, srslte_ue_sync_get_sfidx(&ue_sync), SRSLTE_SIRNTI, ((int) ceilf((float)3*(((sfn)/2)%4)/2))%4); if (n < 0) { fprintf(stderr, "Error decoding UE DL\n");fflush(stdout); return -1; } else if (n == 0) { printf("CFO: %+6.4f KHz, SFO: %+6.4f Khz, NOI: %.2f, PDCCH-Det: %.3f\r", srslte_ue_sync_get_cfo(&ue_sync)/1000, srslte_ue_sync_get_sfo(&ue_sync)/1000, srslte_sch_average_noi(&ue_dl.pdsch.dl_sch), (float) ue_dl.nof_detected/nof_trials); nof_trials++; } else { printf("Decoded SIB1. Payload: "); srslte_vec_fprint_byte(stdout, data, n/8);; state = MEASURE; } } break; case MEASURE: if (srslte_ue_sync_get_sfidx(&ue_sync) == 5) { /* Run FFT for all subframe data */ srslte_ofdm_rx_sf(&fft, sf_buffer, sf_symbols); srslte_chest_dl_estimate(&chest, sf_symbols, ce, srslte_ue_sync_get_sfidx(&ue_sync)); rssi = SRSLTE_VEC_EMA(srslte_vec_avg_power_cf(sf_buffer,SRSLTE_SF_LEN(srslte_symbol_sz(cell.nof_prb))),rssi,0.05); rssi_utra = SRSLTE_VEC_EMA(srslte_chest_dl_get_rssi(&chest),rssi_utra,0.05); rsrq = SRSLTE_VEC_EMA(srslte_chest_dl_get_rsrq(&chest),rsrq,0.05); rsrp = SRSLTE_VEC_EMA(srslte_chest_dl_get_rsrp(&chest),rsrp,0.05); snr = SRSLTE_VEC_EMA(srslte_chest_dl_get_snr(&chest),snr,0.05); nframes++; } if ((nframes%100) == 0 || rx_gain_offset == 0) { if (cuhd_has_rssi(uhd)) { rx_gain_offset = 10*log10(rssi)-cuhd_get_rssi(uhd); } else { rx_gain_offset = cuhd_get_rx_gain(uhd); } } // Plot and Printf if ((nframes%10) == 0) { printf("CFO: %+8.4f KHz, SFO: %+8.4f Khz, RSSI: %5.1f dBm, RSSI/ref-symbol: %+5.1f dBm, " "RSRP: %+5.1f dBm, RSRQ: %5.1f dB, SNR: %5.1f dB\r", srslte_ue_sync_get_cfo(&ue_sync)/1000, srslte_ue_sync_get_sfo(&ue_sync)/1000, 10*log10(rssi*1000) - rx_gain_offset, 10*log10(rssi_utra*1000)- rx_gain_offset, 10*log10(rsrp*1000) - rx_gain_offset, 10*log10(rsrq), 10*log10(snr)); if (srslte_verbose != SRSLTE_VERBOSE_NONE) { printf("\n"); } } break; } if (srslte_ue_sync_get_sfidx(&ue_sync) == 9) { sfn++; if (sfn == 1024) { sfn = 0; } } } else if (ret == 0) { printf("Finding PSS... Peak: %8.1f, FrameCnt: %d, State: %d\r", srslte_sync_get_peak_value(&ue_sync.sfind), ue_sync.frame_total_cnt, ue_sync.state); } sf_cnt++; } // Main loop srslte_ue_sync_free(&ue_sync); cuhd_close(uhd); printf("\nBye\n"); exit(0); }
/** Finds the PSS sequence previously defined by a call to srslte_sync_set_N_id_2() * around the position find_offset in the buffer input. * Returns 1 if the correlation peak exceeds the threshold set by srslte_sync_set_threshold() * or 0 otherwise. Returns a negative number on error (if N_id_2 has not been set) * * The maximum of the correlation peak is always stored in *peak_position */ int srslte_sync_find(srslte_sync_t *q, cf_t *input, uint32_t find_offset, uint32_t *peak_position) { int ret = SRSLTE_ERROR_INVALID_INPUTS; if (q != NULL && input != NULL && srslte_N_id_2_isvalid(q->N_id_2) && fft_size_isvalid(q->fft_size)) { int peak_pos; ret = SRSLTE_SUCCESS; if (peak_position) { *peak_position = 0; } /* Estimate CFO using CP */ if (q->enable_cfo_corr) { uint32_t cp_offset = srslte_cp_synch(&q->cp_synch, input, q->nof_symbols, q->nof_symbols, SRSLTE_CP_LEN_NORM(1,q->fft_size)); cf_t cp_corr_max = srslte_cp_synch_corr_output(&q->cp_synch, cp_offset); float cfo = -carg(cp_corr_max) / M_PI / 2; /* compute cumulative moving average CFO */ INFO("cp_offset_pos=%d, abs=%f, cfo=%f, mean_cfo=%f, nof_symb=%d\n", cp_offset, cabs(cp_corr_max), cfo, q->mean_cfo, q->nof_symbols); if (q->mean_cfo) { q->mean_cfo = SRSLTE_VEC_EMA(cfo, q->mean_cfo, q->cfo_ema_alpha); } else { q->mean_cfo = cfo; } /* Correct CFO with the averaged CFO estimation */ srslte_cfo_correct(&q->cfocorr, input, input, -q->mean_cfo / q->fft_size); } if (q->find_cfo_i && q->enable_cfo_corr) { float peak_value; float max_peak_value = -99; peak_pos = 0; srslte_pss_synch_t *pss_obj[3] = {&q->pss_i[0], &q->pss, &q->pss_i[1]}; for (int cfo_i=0;cfo_i<3;cfo_i++) { srslte_pss_synch_set_N_id_2(pss_obj[cfo_i], q->N_id_2); int p = srslte_pss_synch_find_pss(pss_obj[cfo_i], &input[find_offset], &peak_value); if (peak_value > max_peak_value) { max_peak_value = peak_value; peak_pos = p; q->peak_value = peak_value; q->cfo_i = cfo_i-1; } } if (q->cfo_i != 0) { srslte_vec_prod_ccc(input, q->cfo_i_corr[q->cfo_i<0?0:1], input, q->frame_size); INFO("Compensating cfo_i=%d\n", q->cfo_i); } } else { srslte_pss_synch_set_N_id_2(&q->pss, q->N_id_2); peak_pos = srslte_pss_synch_find_pss(&q->pss, &input[find_offset], &q->peak_value); if (peak_pos < 0) { fprintf(stderr, "Error calling finding PSS sequence\n"); return SRSLTE_ERROR; } } q->mean_peak_value = SRSLTE_VEC_EMA(q->peak_value, q->mean_peak_value, MEANPEAK_EMA_ALPHA); if (peak_position) { *peak_position = (uint32_t) peak_pos; } /* If peak is over threshold, compute CFO and SSS */ if (q->peak_value >= q->threshold) { // Try to detect SSS if (q->sss_en) { // Set an invalid N_id_1 indicating SSS is yet to be detected q->N_id_1 = 1000; if (sync_sss(q, input, find_offset + peak_pos, q->cp) < 0) { DEBUG("No space for SSS processing. Frame starts at %d\n", peak_pos); } } if (q->detect_cp) { if (peak_pos + find_offset >= 2*(q->fft_size + SRSLTE_CP_LEN_EXT(q->fft_size))) { srslte_sync_set_cp(q, srslte_sync_detect_cp(q, input, peak_pos + find_offset)); } else { DEBUG("Not enough room to detect CP length. Peak position: %d\n", peak_pos); } } // Return 1 (peak detected) even if we couldn't estimate CFO and SSS ret = 1; } else { ret = 0; } DEBUG("SYNC ret=%d N_id_2=%d find_offset=%d pos=%d peak=%.2f threshold=%.2f sf_idx=%d, CFO=%.3f KHz\n", ret, q->N_id_2, find_offset, peak_pos, q->peak_value, q->threshold, q->sf_idx, 15*(q->cfo_i+q->mean_cfo)); } else if (srslte_N_id_2_isvalid(q->N_id_2)) { fprintf(stderr, "Must call srslte_sync_set_N_id_2() first!\n"); } return ret; }
/** Finds the PSS sequence previously defined by a call to srslte_sync_set_N_id_2() * around the position find_offset in the buffer input. * Returns 1 if the correlation peak exceeds the threshold set by srslte_sync_set_threshold() * or 0 otherwise. Returns a negative number on error (if N_id_2 has not been set) * * The maximum of the correlation peak is always stored in *peak_position */ int srslte_sync_find(srslte_sync_t *q, cf_t *input, uint32_t find_offset, uint32_t *peak_position) { int ret = SRSLTE_ERROR_INVALID_INPUTS; if (q != NULL && input != NULL && srslte_N_id_2_isvalid(q->N_id_2) && fft_size_isvalid(q->fft_size)) { int peak_pos; ret = SRSLTE_SUCCESS; if (peak_position) { *peak_position = 0; } srslte_pss_synch_set_N_id_2(&q->pss, q->N_id_2); peak_pos = srslte_pss_synch_find_pss(&q->pss, &input[find_offset], &q->peak_value); if (peak_pos < 0) { fprintf(stderr, "Error calling finding PSS sequence\n"); return SRSLTE_ERROR; } q->mean_peak_value = SRSLTE_VEC_EMA(q->peak_value, q->mean_peak_value, MEANPEAK_EMA_ALPHA); if (peak_position) { *peak_position = (uint32_t) peak_pos; } /* If peak is over threshold, compute CFO and SSS */ if (q->peak_value >= q->threshold) { // Make sure we have enough space to estimate CFO if (peak_pos + find_offset >= q->fft_size) { float cfo = srslte_pss_synch_cfo_compute(&q->pss, &input[find_offset+peak_pos-q->fft_size]); /* compute cumulative moving average CFO */ q->mean_cfo = SRSLTE_VEC_EMA(cfo, q->mean_cfo, CFO_EMA_ALPHA); } else { DEBUG("No space for CFO computation. Frame starts at \n",peak_pos); } /* Correct CFO with the averaged CFO estimation */ if (q->correct_cfo) { srslte_cfo_correct(&q->cfocorr, input, input, -q->mean_cfo / q->fft_size); } // Try to detect SSS if (q->sss_en) { // Set an invalid N_id_1 indicating SSS is yet to be detected q->N_id_1 = 1000; if (sync_sss(q, input, find_offset + peak_pos, q->cp) < 0) { DEBUG("No space for SSS processing. Frame starts at %d\n", peak_pos); } } if (q->detect_cp) { if (peak_pos + find_offset >= 2*(q->fft_size + SRSLTE_CP_LEN_EXT(q->fft_size))) { q->cp = srslte_sync_detect_cp(q, input, peak_pos + find_offset); } else { DEBUG("Not enough room to detect CP length. Peak position: %d\n", peak_pos); } } // Return 1 (peak detected) even if we couldn't estimate CFO and SSS ret = 1; } else { ret = 0; } DEBUG("SYNC ret=%d N_id_2=%d find_offset=%d pos=%d peak=%.2f threshold=%.2f sf_idx=%d, CFO=%.3f KHz\n", ret, q->N_id_2, find_offset, peak_pos, q->peak_value, q->threshold, q->sf_idx, 15*q->mean_cfo); } else if (srslte_N_id_2_isvalid(q->N_id_2)) { fprintf(stderr, "Must call srslte_sync_set_N_id_2() first!\n"); } return ret; }
static int track_peak_ok(srslte_ue_sync_t *q, uint32_t track_idx) { /* Make sure subframe idx is what we expect */ if ((q->sf_idx != srslte_sync_get_sf_idx(&q->strack)) && q->decode_sss_on_track && srslte_sync_sss_detected(&q->strack)) { INFO("Warning: Expected SF idx %d but got %d! (%d frames)\n", q->sf_idx, srslte_sync_get_sf_idx(&q->strack), q->frame_no_cnt); q->frame_no_cnt++; if (q->frame_no_cnt >= TRACK_MAX_LOST) { INFO("\n%d frames lost. Going back to FIND\n", (int) q->frame_no_cnt); q->state = SF_FIND; } } else { q->frame_no_cnt = 0; } // Get sampling time offset q->last_sample_offset = ((int) track_idx - (int) q->strack.max_offset/2 - (int) q->strack.fft_size); // Adjust sampling time every q->sample_offset_correct_period subframes uint32_t frame_idx = 0; if (q->sample_offset_correct_period) { frame_idx = q->frame_ok_cnt%q->sample_offset_correct_period; q->mean_sample_offset += (float) q->last_sample_offset/q->sample_offset_correct_period; } else { q->mean_sample_offset = q->last_sample_offset; } // Compute cumulative moving average time offset */ if (!frame_idx) { // Adjust RF sampling time based on the mean sampling offset q->next_rf_sample_offset = (int) round(q->mean_sample_offset); // Reset PSS averaging if correcting every a period longer than 1 if (q->sample_offset_correct_period > 1) { srslte_sync_reset(&q->strack); } // Compute SFO based on mean sample offset if (q->sample_offset_correct_period) { q->mean_sample_offset /= q->sample_offset_correct_period; } q->mean_sfo = SRSLTE_VEC_EMA(q->mean_sample_offset, q->mean_sfo, q->sfo_ema); if (q->next_rf_sample_offset) { INFO("Time offset adjustment: %d samples (%.2f), mean SFO: %.2f Hz, %.5f samples/5-sf, ema=%f, length=%d\n", q->next_rf_sample_offset, q->mean_sample_offset, srslte_ue_sync_get_sfo(q), q->mean_sfo, q->sfo_ema, q->sample_offset_correct_period); } q->mean_sample_offset = 0; } /* If the PSS peak is beyond the frame (we sample too slowly), discard the offseted samples to align next frame */ if (q->next_rf_sample_offset > 0 && q->next_rf_sample_offset < MAX_TIME_OFFSET) { DEBUG("Positive time offset %d samples.\n", q->next_rf_sample_offset); if (q->recv_callback(q->stream, dummy, (uint32_t) q->next_rf_sample_offset, &q->last_timestamp) < 0) { fprintf(stderr, "Error receiving from USRP\n"); return SRSLTE_ERROR; } q->next_rf_sample_offset = 0; } q->peak_idx = q->sf_len/2 + q->last_sample_offset; q->frame_ok_cnt++; return 1; }
/* Decode a transport block according to 36.212 5.3.2 * */ static int decode_tb(srslte_sch_t *q, srslte_softbuffer_rx_t *softbuffer, srslte_cbsegm_t *cb_segm, uint32_t Qm, uint32_t rv, uint32_t nof_e_bits, float *e_bits, uint8_t *data) { uint8_t parity[24]; uint8_t *p_parity = parity; uint32_t par_rx, par_tx; uint32_t i; uint32_t cb_len, rp, wp, rlen, F, n_e; if (q != NULL && data != NULL && softbuffer != NULL && e_bits != NULL && cb_segm != NULL) { if (cb_segm->tbs == 0 || cb_segm->C == 0) { return SRSLTE_SUCCESS; } rp = 0; rp = 0; wp = 0; uint32_t Gp = nof_e_bits / Qm; uint32_t gamma=Gp; if (cb_segm->C>0) { gamma = Gp%cb_segm->C; } bool early_stop = true; for (i = 0; i < cb_segm->C && early_stop; i++) { /* Get read/write lengths */ if (i < cb_segm->C2) { cb_len = cb_segm->K2; } else { cb_len = cb_segm->K1; } if (cb_segm->C == 1) { rlen = cb_len; } else { rlen = cb_len - 24; } if (i == 0) { F = cb_segm->F; } else { F = 0; } if (i <= cb_segm->C - gamma - 1) { n_e = Qm * (Gp/cb_segm->C); } else { n_e = Qm * ((uint32_t) ceilf((float) Gp/cb_segm->C)); } INFO("CB#%d: cb_len: %d, rlen: %d, wp: %d, rp: %d, F: %d, E: %d\n", i, cb_len, rlen - F, wp, rp, F, n_e); /* Rate Unmatching */ if (srslte_rm_turbo_rx(softbuffer->buffer_f[i], softbuffer->buff_size, &e_bits[rp], n_e, (float*) q->cb_out, 3 * cb_len + 12, rv, F)) { fprintf(stderr, "Error in rate matching\n"); return SRSLTE_ERROR; } if (SRSLTE_VERBOSE_ISDEBUG()) { DEBUG("CB#%d RMOUT: ", i); srslte_vec_fprint_f(stdout, q->cb_out, 3*cb_len+12); } /* Turbo Decoding with CRC-based early stopping */ q->nof_iterations = 0; uint32_t len_crc; uint8_t *cb_in_ptr; srslte_crc_t *crc_ptr; early_stop = false; srslte_tdec_reset(&q->decoder, cb_len); do { srslte_tdec_iteration(&q->decoder, (float*) q->cb_out, cb_len); q->nof_iterations++; if (cb_segm->C > 1) { len_crc = cb_len; cb_in_ptr = q->cb_in; crc_ptr = &q->crc_cb; } else { len_crc = cb_segm->tbs+24; cb_in_ptr = &q->cb_in[F]; crc_ptr = &q->crc_tb; } srslte_tdec_decision(&q->decoder, q->cb_in, cb_len); /* Check Codeblock CRC and stop early if incorrect */ if (!srslte_crc_checksum(crc_ptr, cb_in_ptr, len_crc)) { early_stop = true; } } while (q->nof_iterations < SRSLTE_PDSCH_MAX_TDEC_ITERS && !early_stop); q->average_nof_iterations = SRSLTE_VEC_EMA((float) q->nof_iterations, q->average_nof_iterations, 0.2); if (SRSLTE_VERBOSE_ISDEBUG()) { DEBUG("CB#%d IN: ", i); srslte_vec_fprint_b(stdout, q->cb_in, cb_len); } // If CB CRC is not correct, early_stop will be false and wont continue with rest of CBs /* Copy data to another buffer, removing the Codeblock CRC */ if (i < cb_segm->C - 1) { memcpy(&data[wp], &q->cb_in[F], (rlen - F) * sizeof(uint8_t)); } else { DEBUG("Last CB, appending parity: %d to %d from %d and 24 from %d\n", rlen - F - 24, wp, F, rlen - 24); /* Append Transport Block parity bits to the last CB */ memcpy(&data[wp], &q->cb_in[F], (rlen - F - 24) * sizeof(uint8_t)); memcpy(parity, &q->cb_in[rlen - 24], 24 * sizeof(uint8_t)); } /* Set read/write pointers */ wp += (rlen - F); rp += n_e; } if (!early_stop) { INFO("CB %d failed. TB is erroneous.\n",i-1); return SRSLTE_ERROR; } else { INFO("END CB#%d: wp: %d, rp: %d\n", i, wp, rp); // Compute transport block CRC par_rx = srslte_crc_checksum(&q->crc_tb, data, cb_segm->tbs); // check parity bits par_tx = srslte_bit_unpack(&p_parity, 24); if (!par_rx) { INFO("\n\tCAUTION!! Received all-zero transport block\n\n", 0); } if (par_rx == par_tx) { INFO("TB decoded OK\n",i); return SRSLTE_SUCCESS; } else { INFO("Error in TB parity\n",i); return SRSLTE_ERROR; } } } else { return SRSLTE_ERROR_INVALID_INPUTS; } }
/** Finds the PSS sequence previously defined by a call to srslte_sync_set_N_id_2() * around the position find_offset in the buffer input. * * Returns 1 if the correlation peak exceeds the threshold set by srslte_sync_set_threshold() * or 0 otherwise. Returns a negative number on error (if N_id_2 has not been set) * * The input signal is not modified. Any CFO correction is done in internal buffers * * The maximum of the correlation peak is always stored in *peak_position */ srslte_sync_find_ret_t srslte_sync_find(srslte_sync_t *q, const cf_t *input, uint32_t find_offset, uint32_t *peak_position) { srslte_sync_find_ret_t ret = SRSLTE_SYNC_ERROR; int peak_pos = 0; if (!q) { return SRSLTE_ERROR_INVALID_INPUTS; } if (input != NULL && srslte_N_id_2_isvalid(q->N_id_2) && fft_size_isvalid(q->fft_size)) { if (peak_position) { *peak_position = 0; } const cf_t *input_ptr = input; /* First CFO estimation stage is integer. * Finds max PSS correlation for shifted +1/0/-1 integer versions. * This should only used once N_id_2 is set */ if (q->cfo_i_enable) { if (cfo_i_estimate(q, input_ptr, find_offset, &peak_pos, &q->cfo_i_value) < 0) { fprintf(stderr, "Error calling finding PSS sequence at : %d \n", peak_pos); return SRSLTE_ERROR; } // Correct it using precomputed signal and store in buffer (don't modify input signal) if (q->cfo_i_value != 0) { srslte_vec_prod_ccc((cf_t*) input_ptr, q->cfo_i_corr[q->cfo_i_value<0?0:1], q->temp, q->frame_size); INFO("Compensating cfo_i=%d\n", q->cfo_i_value); input_ptr = q->temp; } } /* Second stage is coarse fractional CFO estimation using CP. * In case of multi-cell, this can lead to incorrect estimations if CFO from different cells is different */ if (q->cfo_cp_enable) { float cfo_cp = cfo_cp_estimate(q, input_ptr); if (!q->cfo_cp_is_set) { q->cfo_cp_mean = cfo_cp; q->cfo_cp_is_set = true; } else { /* compute exponential moving average CFO */ q->cfo_cp_mean = SRSLTE_VEC_EMA(cfo_cp, q->cfo_cp_mean, q->cfo_ema_alpha); } INFO("CP-CFO: estimated=%f, mean=%f\n", cfo_cp, q->cfo_cp_mean); /* Correct CFO with the averaged CFO estimation */ srslte_cfo_correct(&q->cfo_corr_frame, input_ptr, q->temp, -q->cfo_cp_mean / q->fft_size); input_ptr = q->temp; } /* Find maximum of PSS correlation. If Integer CFO is enabled, correlation is already done */ if (!q->cfo_i_enable) { srslte_pss_set_N_id_2(&q->pss, q->N_id_2); peak_pos = srslte_pss_find_pss(&q->pss, &input_ptr[find_offset], q->threshold>0?&q->peak_value:NULL); if (peak_pos < 0) { fprintf(stderr, "Error calling finding PSS sequence at : %d \n", peak_pos); return SRSLTE_ERROR; } } INFO("PSS: id=%d, peak_pos=%d, peak_value=%f\n", q->N_id_2, peak_pos, q->peak_value); // Save peak position if (peak_position) { *peak_position = (uint32_t) peak_pos; } // In case of decimation, this compensates for the constant time shift caused by the low pass filter if(q->decimate && peak_pos < 0) { peak_pos = 0 ;//peak_pos + q->decimate*(2);// replace 2 with q->filter_size -2; } /* If peak is over threshold, compute CFO and SSS */ if (q->peak_value >= q->threshold || q->threshold == 0) { if (q->cfo_pss_enable && peak_pos >= q->fft_size) { // Filter central bands before PSS-based CFO estimation const cf_t *pss_ptr = &input_ptr[find_offset + peak_pos - q->fft_size]; if (q->pss_filtering_enabled) { srslte_pss_filter(&q->pss, pss_ptr, q->pss_filt); pss_ptr = q->pss_filt; } // PSS-based CFO estimation q->cfo_pss = srslte_pss_cfo_compute(&q->pss, pss_ptr); if (!q->cfo_pss_is_set) { q->cfo_pss_mean = q->cfo_pss; q->cfo_pss_is_set = true; } else if (15000*fabsf(q->cfo_pss) < MAX_CFO_PSS_OFFSET) { q->cfo_pss_mean = SRSLTE_VEC_EMA(q->cfo_pss, q->cfo_pss_mean, q->cfo_ema_alpha); } INFO("PSS-CFO: filter=%s, estimated=%f, mean=%f\n", q->pss_filtering_enabled?"yes":"no", q->cfo_pss, q->cfo_pss_mean); } // If there is enough space for CP and SSS estimation if (peak_pos + find_offset >= 2 * (q->fft_size + SRSLTE_CP_LEN_EXT(q->fft_size))) { // If SSS search is enabled, correlate SSS sequence if (q->sss_en) { // Set an invalid N_id_1 indicating SSS is yet to be detected q->N_id_1 = 1000; int sss_idx = find_offset + peak_pos - 2 * q->fft_size - SRSLTE_CP_LEN(q->fft_size, (SRSLTE_CP_ISNORM(q->cp) ? SRSLTE_CP_NORM_LEN : SRSLTE_CP_EXT_LEN)); const cf_t *sss_ptr = &input_ptr[sss_idx]; // Correct CFO if detected in PSS if (q->cfo_pss_enable) { srslte_cfo_correct(&q->cfo_corr_symbol, sss_ptr, q->sss_filt, -q->cfo_pss_mean / q->fft_size); // Equalize channel if estimated in PSS if (q->sss_channel_equalize && q->pss.chest_on_filter && q->pss_filtering_enabled) { srslte_vec_prod_ccc(&q->sss_filt[q->fft_size/2-SRSLTE_PSS_LEN/2], q->pss.tmp_ce, &q->sss_filt[q->fft_size/2-SRSLTE_PSS_LEN/2], SRSLTE_PSS_LEN); } sss_ptr = q->sss_filt; } if (sync_sss_symbol(q, sss_ptr) < 0) { fprintf(stderr, "Error correlating SSS\n"); return -1; } } // Detect CP length if (q->detect_cp) { srslte_sync_set_cp(q, srslte_sync_detect_cp(q, input_ptr, peak_pos + find_offset)); } else { DEBUG("Not enough room to detect CP length. Peak position: %d\n", peak_pos); } ret = SRSLTE_SYNC_FOUND; } else { ret = SRSLTE_SYNC_FOUND_NOSPACE; } } else { ret = SRSLTE_SYNC_NOFOUND; } DEBUG("SYNC ret=%d N_id_2=%d find_offset=%d frame_len=%d, pos=%d peak=%.2f threshold=%.2f sf_idx=%d, CFO=%.3f kHz\n", ret, q->N_id_2, find_offset, q->frame_size, peak_pos, q->peak_value, q->threshold, q->sf_idx, 15*(srslte_sync_get_cfo(q))); } else if (srslte_N_id_2_isvalid(q->N_id_2)) { fprintf(stderr, "Must call srslte_sync_set_N_id_2() first!\n"); } return ret; }