/* Partial correlation SSS estimation. * Returns m0 and m1 estimates * * Source: "SSS Detection Method for Initial Cell Search in 3GPP LTE FDD/TDD Dual Mode Receiver" * Jung-In Kim, Jung-Su Han, Hee-Jin Roh and Hyung-Jin Choi */ int srslte_sss_synch_m0m1_partial(srslte_sss_synch_t *q, cf_t *input, uint32_t M, cf_t ce[2*SRSLTE_SSS_N], uint32_t *m0, float *m0_value, uint32_t *m1, float *m1_value) { int ret = SRSLTE_ERROR_INVALID_INPUTS; if (q != NULL && input != NULL && m0 != NULL && m1 != NULL && M <= MAX_M) { cf_t y[2][SRSLTE_SSS_N]; extract_pair_sss(q, input, ce, y); corr_all_sz_partial(y[0], q->fc_tables[q->N_id_2].s, M, q->corr_output_m0); *m0 = srslte_vec_max_fi(q->corr_output_m0, SRSLTE_SSS_N); if (m0_value) { *m0_value = q->corr_output_m0[*m0]; } srslte_vec_prod_cfc(y[1], q->fc_tables[q->N_id_2].z1[*m0], y[1], SRSLTE_SSS_N); corr_all_sz_partial(y[1], q->fc_tables[q->N_id_2].s, M, q->corr_output_m1); *m1 = srslte_vec_max_fi(q->corr_output_m1, SRSLTE_SSS_N); if (m1_value) { *m1_value = q->corr_output_m1[*m1]; } ret = SRSLTE_SUCCESS; } return ret; }
float compute_peak_sidelobe(srslte_pss_t *q, uint32_t corr_peak_pos, uint32_t conv_output_len) { // Find end of peak lobe to the right int pl_ub = corr_peak_pos+1; while(q->conv_output_avg[pl_ub+1] <= q->conv_output_avg[pl_ub] && pl_ub < conv_output_len) { pl_ub ++; } // Find end of peak lobe to the left int pl_lb; if (corr_peak_pos > 2) { pl_lb = corr_peak_pos-1; while(q->conv_output_avg[pl_lb-1] <= q->conv_output_avg[pl_lb] && pl_lb > 1) { pl_lb --; } } else { pl_lb = 0; } int sl_distance_right = conv_output_len-1-pl_ub; if (sl_distance_right < 0) { sl_distance_right = 0; } int sl_distance_left = pl_lb; int sl_right = pl_ub+srslte_vec_max_fi(&q->conv_output_avg[pl_ub], sl_distance_right); int sl_left = srslte_vec_max_fi(q->conv_output_avg, sl_distance_left); float side_lobe_value = SRSLTE_MAX(q->conv_output_avg[sl_right], q->conv_output_avg[sl_left]); return q->conv_output_avg[corr_peak_pos]/side_lobe_value; }
/* Differential SSS estimation. * Returns m0 and m1 estimates * * Source: "SSS Detection Method for Initial Cell Search in 3GPP LTE FDD/TDD Dual Mode Receiver" * Jung-In Kim, Jung-Su Han, Hee-Jin Roh and Hyung-Jin Choi * */ int srslte_sss_synch_m0m1_diff_coh(srslte_sss_synch_t *q, cf_t *input, cf_t ce[2*SRSLTE_SSS_N], uint32_t *m0, float *m0_value, uint32_t *m1, float *m1_value) { int ret = SRSLTE_ERROR_INVALID_INPUTS; if (q != NULL && input != NULL && m0 != NULL && m1 != NULL) { cf_t yprod[SRSLTE_SSS_N]; cf_t y[2][SRSLTE_SSS_N]; extract_pair_sss(q, input, ce, y); srslte_vec_prod_conj_ccc(&y[0][1], y[0], yprod, SRSLTE_SSS_N - 1); corr_all_zs(yprod, q->fc_tables[q->N_id_2].sd, q->corr_output_m0); *m0 = srslte_vec_max_fi(q->corr_output_m0, SRSLTE_SSS_N); if (m0_value) { *m0_value = q->corr_output_m0[*m0]; } srslte_vec_prod_cfc(y[1], q->fc_tables[q->N_id_2].z1[*m0], y[1], SRSLTE_SSS_N); srslte_vec_prod_conj_ccc(&y[1][1], y[1], yprod, SRSLTE_SSS_N - 1); corr_all_zs(yprod, q->fc_tables[q->N_id_2].sd, q->corr_output_m1); *m1 = srslte_vec_max_fi(q->corr_output_m1, SRSLTE_SSS_N); if (m1_value) { *m1_value = q->corr_output_m1[*m1]; } ret = SRSLTE_SUCCESS; } return ret; }
// TODO: Improve this implementation void srslte_vec_norm_cfc(cf_t *x, float amplitude, cf_t *y, uint32_t len) { // We should use fabs() here but is statistically should be similar float *xp = (float*) x; uint32_t idx = srslte_vec_max_fi(xp, 2*len); float max = xp[idx]; // Normalize before TX srslte_vec_sc_prod_cfc(x, amplitude/max, y, len); }
/** Performs time-domain PSS correlation. * Returns the index of the PSS correlation peak in a subframe. * The frame starts at corr_peak_pos-subframe_size/2. * The value of the correlation is stored in corr_peak_value. * * Input buffer must be subframe_size long. */ int srslte_pss_synch_find_pss(srslte_pss_synch_t *q, cf_t *input, float *corr_peak_value) { int ret = SRSLTE_ERROR_INVALID_INPUTS; if (q != NULL && input != NULL) { uint32_t corr_peak_pos; uint32_t conv_output_len; if (!srslte_N_id_2_isvalid(q->N_id_2)) { fprintf(stderr, "Error finding PSS peak, Must set N_id_2 first\n"); return SRSLTE_ERROR; } /* Correlate input with PSS sequence */ if (q->frame_size >= q->fft_size) { #ifdef CONVOLUTION_FFT memcpy(q->tmp_input, input, q->frame_size * sizeof(cf_t)); conv_output_len = srslte_conv_fft_cc_run(&q->conv_fft, q->tmp_input, q->pss_signal_time[q->N_id_2], q->conv_output); #else conv_output_len = srslte_conv_cc(input, q->pss_signal_time[q->N_id_2], q->conv_output, q->frame_size, q->fft_size); #endif } else { for (int i=0;i<q->frame_size;i++) { q->conv_output[i] = srslte_vec_dot_prod_ccc(q->pss_signal_time[q->N_id_2], &input[i], q->fft_size); } conv_output_len = q->frame_size; } #ifdef SRSLTE_PSS_ABS_SQUARE srslte_vec_abs_square_cf(q->conv_output, q->conv_output_abs, conv_output_len-1); #else srslte_vec_abs_cf(q->conv_output, q->conv_output_abs, conv_output_len-1); #endif srslte_vec_sc_prod_fff(q->conv_output_abs, q->ema_alpha, q->conv_output_abs, conv_output_len-1); srslte_vec_sc_prod_fff(q->conv_output_avg, 1-q->ema_alpha, q->conv_output_avg, conv_output_len-1); srslte_vec_sum_fff(q->conv_output_abs, q->conv_output_avg, q->conv_output_avg, conv_output_len-1); /* Find maximum of the absolute value of the correlation */ corr_peak_pos = srslte_vec_max_fi(q->conv_output_avg, conv_output_len-1); // save absolute value q->peak_value = q->conv_output_avg[corr_peak_pos]; #ifdef SRSLTE_PSS_RETURN_PSR // Find second side lobe // Find end of peak lobe to the right int pl_ub = corr_peak_pos+1; while(q->conv_output_avg[pl_ub+1] <= q->conv_output_avg[pl_ub] && pl_ub < conv_output_len) { pl_ub ++; } // Find end of peak lobe to the left int pl_lb; if (corr_peak_pos > 2) { pl_lb = corr_peak_pos-1; while(q->conv_output_avg[pl_lb-1] <= q->conv_output_avg[pl_lb] && pl_lb > 1) { pl_lb --; } } else { pl_lb = 0; } int sl_distance_right = conv_output_len-1-pl_ub; if (sl_distance_right < 0) { sl_distance_right = 0; } int sl_distance_left = pl_lb; int sl_right = pl_ub+srslte_vec_max_fi(&q->conv_output_avg[pl_ub], sl_distance_right); int sl_left = srslte_vec_max_fi(q->conv_output_avg, sl_distance_left); float side_lobe_value = SRSLTE_MAX(q->conv_output_avg[sl_right], q->conv_output_avg[sl_left]); if (corr_peak_value) { *corr_peak_value = q->conv_output_avg[corr_peak_pos]/side_lobe_value; DEBUG("peak_pos=%2d, pl_ub=%2d, pl_lb=%2d, sl_right: %2d, sl_left: %2d, PSR: %.2f/%.2f=%.2f\n", corr_peak_pos, pl_ub, pl_lb, sl_right,sl_left, q->conv_output_avg[corr_peak_pos], side_lobe_value,*corr_peak_value); } #else if (corr_peak_value) { *corr_peak_value = q->conv_output_avg[corr_peak_pos]; } #endif if (q->frame_size >= q->fft_size) { ret = (int) corr_peak_pos; } else { ret = (int) corr_peak_pos + q->fft_size; } } return ret; }
/** Performs time-domain PSS correlation. * Returns the index of the PSS correlation peak in a subframe. * The frame starts at corr_peak_pos-subframe_size/2. * The value of the correlation is stored in corr_peak_value. * * Input buffer must be subframe_size long. */ int srslte_pss_find_pss(srslte_pss_t *q, const cf_t *input, float *corr_peak_value) { int ret = SRSLTE_ERROR_INVALID_INPUTS; if (q != NULL && input != NULL) { uint32_t corr_peak_pos; uint32_t conv_output_len; if (!srslte_N_id_2_isvalid(q->N_id_2)) { ERROR("Error finding PSS peak, Must set N_id_2 first\n"); return SRSLTE_ERROR; } /* Correlate input with PSS sequence * * We do not reverse time-domain PSS signal because it's conjugate is symmetric. * The conjugate operation on pss_signal_time has been done in srslte_pss_init_N_id_2 * This is why we can use FFT-based convolution */ if (q->frame_size >= q->fft_size) { #ifdef CONVOLUTION_FFT memcpy(q->tmp_input, input, (q->frame_size * q->decimate) * sizeof(cf_t)); if(q->decimate > 1) { srslte_filt_decim_cc_execute(&(q->filter), q->tmp_input, q->filter.downsampled_input, q->filter.filter_output , (q->frame_size * q->decimate)); conv_output_len = srslte_conv_fft_cc_run_opt(&q->conv_fft, q->filter.filter_output,q->pss_signal_freq_full[q->N_id_2], q->conv_output); } else { conv_output_len = srslte_conv_fft_cc_run_opt(&q->conv_fft, q->tmp_input, q->pss_signal_freq_full[q->N_id_2], q->conv_output); } #else conv_output_len = srslte_conv_cc(input, q->pss_signal_time[q->N_id_2], q->conv_output, q->frame_size, q->fft_size); #endif } else { for (int i=0;i<q->frame_size;i++) { q->conv_output[i] = srslte_vec_dot_prod_ccc(q->pss_signal_time[q->N_id_2], &input[i], q->fft_size); } conv_output_len = q->frame_size; } // Compute modulus square srslte_vec_abs_square_cf(q->conv_output, q->conv_output_abs, conv_output_len-1); // If enabled, average the absolute value from previous calls if (q->ema_alpha < 1.0 && q->ema_alpha > 0.0) { srslte_vec_sc_prod_fff(q->conv_output_abs, q->ema_alpha, q->conv_output_abs, conv_output_len-1); srslte_vec_sc_prod_fff(q->conv_output_avg, 1-q->ema_alpha, q->conv_output_avg, conv_output_len-1); srslte_vec_sum_fff(q->conv_output_abs, q->conv_output_avg, q->conv_output_avg, conv_output_len-1); } else { memcpy(q->conv_output_avg, q->conv_output_abs, sizeof(float)*(conv_output_len-1)); } /* Find maximum of the absolute value of the correlation */ corr_peak_pos = srslte_vec_max_fi(q->conv_output_avg, conv_output_len-1); // save absolute value q->peak_value = q->conv_output_avg[corr_peak_pos]; #ifdef SRSLTE_PSS_RETURN_PSR if (corr_peak_value) { *corr_peak_value = compute_peak_sidelobe(q, corr_peak_pos, conv_output_len); } #else if (corr_peak_value) { *corr_peak_value = q->conv_output_avg[corr_peak_pos]; } #endif if(q->decimate >1) { int decimation_correction = (q->filter.num_taps - 2); corr_peak_pos = corr_peak_pos - decimation_correction; corr_peak_pos = corr_peak_pos*q->decimate; } if (q->frame_size >= q->fft_size) { ret = (int) corr_peak_pos; } else { ret = (int) corr_peak_pos + q->fft_size; } } return ret; }
void srslte_agc_process(srslte_agc_t *q, cf_t *signal, uint32_t len) { float gain_db = 10*log10(q->gain); float gain_uhd_db = 1.0; //float gain_uhd = 1.0; float y = 0; // Apply current gain to input signal if (!q->uhd_handler) { srslte_vec_sc_prod_cfc(signal, q->gain, signal, len); } else { if (q->gain < 1) { q->gain = 1.0; } if (isinf(gain_db) || isnan(gain_db)) { q->gain = 10.0; } else { gain_uhd_db = q->set_gain_callback(q->uhd_handler, gain_db); q->gain = pow(10, gain_uhd_db/10); } } float *t; switch(q->mode) { case SRSLTE_AGC_MODE_ENERGY: y = sqrtf(crealf(srslte_vec_dot_prod_conj_ccc(signal, signal, len))/len); break; case SRSLTE_AGC_MODE_PEAK_AMPLITUDE: t = (float*) signal; y = t[srslte_vec_max_fi(t, 2*len)];// take only positive max to avoid abs() (should be similar) break; default: fprintf(stderr, "Unsupported AGC mode\n"); return; } if (q->nof_frames > 0) { q->y_tmp[q->frame_cnt++] = y; if (q->frame_cnt == q->nof_frames) { q->frame_cnt = 0; switch(q->mode) { case SRSLTE_AGC_MODE_ENERGY: y = srslte_vec_acc_ff(q->y_tmp, q->nof_frames)/q->nof_frames; break; case SRSLTE_AGC_MODE_PEAK_AMPLITUDE: y = q->y_tmp[srslte_vec_max_fi(q->y_tmp, q->nof_frames)]; break; default: fprintf(stderr, "Unsupported AGC mode\n"); return; } } } double gg = 1.0; if (q->isfirst) { q->y_out = y; q->isfirst = false; } else { if (q->frame_cnt == 0) { q->y_out = (1-q->bandwidth) * q->y_out + q->bandwidth * y; if (!q->lock) { gg = expf(-0.5*q->bandwidth*logf(q->y_out/q->target)); q->gain *= gg; } INFO("AGC gain: %.2f (%.2f) y_out=%.3f, y=%.3f target=%.1f gg=%.2f\n", gain_db, gain_uhd_db, q->y_out, y, q->target, gg); } } }
static int srslte_pdsch_codeword_decode(srslte_pdsch_t *q, srslte_pdsch_cfg_t *cfg, srslte_softbuffer_rx_t *softbuffer, uint16_t rnti, uint8_t *data, uint32_t codeword_idx, uint32_t tb_idx, bool *ack) { srslte_ra_nbits_t *nbits = &cfg->nbits[tb_idx]; srslte_ra_mcs_t *mcs = &cfg->grant.mcs[tb_idx]; uint32_t rv = cfg->rv[tb_idx]; int ret = SRSLTE_ERROR_INVALID_INPUTS; if (softbuffer && data && ack) { INFO("Decoding PDSCH SF: %d (CW%d -> TB%d), Mod %s, NofBits: %d, NofSymbols: %d, NofBitsE: %d, rv_idx: %d\n", cfg->sf_idx, codeword_idx, tb_idx, srslte_mod_string(mcs->mod), mcs->tbs, nbits->nof_re, nbits->nof_bits, rv); /* demodulate symbols * The MAX-log-MAP algorithm used in turbo decoding is unsensitive to SNR estimation, * thus we don't need tot set it in the LLRs normalization */ srslte_demod_soft_demodulate_s(mcs->mod, q->d[codeword_idx], q->e[codeword_idx], nbits->nof_re); /* Select scrambling sequence */ srslte_sequence_t *seq = get_user_sequence(q, rnti, codeword_idx, cfg->sf_idx, nbits->nof_bits); /* Bit scrambling */ srslte_scrambling_s_offset(seq, q->e[codeword_idx], 0, nbits->nof_bits); uint32_t qm = nbits->nof_bits/nbits->nof_re; switch(cfg->grant.mcs[tb_idx].mod) { case SRSLTE_MOD_BPSK: qm = 1; break; case SRSLTE_MOD_QPSK: qm = 2; break; case SRSLTE_MOD_16QAM: qm = 4; break; case SRSLTE_MOD_64QAM: qm = 6; break; default: ERROR("No modulation"); } int16_t *e = q->e[codeword_idx]; if (q->csi_enabled) { const uint32_t csi_max_idx = srslte_vec_max_fi(q->csi[codeword_idx], nbits->nof_bits / qm); float csi_max = 1.0f; if (csi_max_idx < nbits->nof_bits / qm) { csi_max = q->csi[codeword_idx][csi_max_idx]; } for (int i = 0; i < nbits->nof_bits / qm; i++) { const float csi = q->csi[codeword_idx][i] / csi_max; for (int k = 0; k < qm; k++) { e[qm * i + k] = (int16_t) ((float) e[qm * i + k] * csi); } } } /* Return */ ret = srslte_dlsch_decode2(&q->dl_sch, cfg, softbuffer, q->e[codeword_idx], data, tb_idx); q->last_nof_iterations[codeword_idx] = srslte_sch_last_noi(&q->dl_sch); if (ret == SRSLTE_SUCCESS) { *ack = true; } else if (ret == SRSLTE_ERROR) { *ack = false; ret = SRSLTE_SUCCESS; } else if (ret == SRSLTE_ERROR_INVALID_INPUTS) { *ack = false; ret = SRSLTE_ERROR; } } else { ERROR("Detected NULL pointer in TB%d &softbuffer=%p &data=%p &ack=%p", codeword_idx, softbuffer, (void*)data, ack); } return ret; }