uint32_t get_pucch_symbol(uint32_t m, srslte_pucch_format_t format, srslte_cp_t cp) { switch (format) { case SRSLTE_PUCCH_FORMAT_1: case SRSLTE_PUCCH_FORMAT_1A: case SRSLTE_PUCCH_FORMAT_1B: if (m < 5) { if (SRSLTE_CP_ISNORM(cp)) { return pucch_symbol_format1_cpnorm[m]; } else { return pucch_symbol_format1_cpext[m]; } } break; case SRSLTE_PUCCH_FORMAT_2: case SRSLTE_PUCCH_FORMAT_2A: case SRSLTE_PUCCH_FORMAT_2B: if (m < 6) { if (SRSLTE_CP_ISNORM(cp)) { return pucch_symbol_format2_cpnorm[m]; } else { return pucch_symbol_format2_cpext[m]; } } break; } return 0; }
uint32_t srslte_phich_nsf(srslte_phich_t *q) { if (SRSLTE_CP_ISNORM(q->cell.cp)) { return SRSLTE_PHICH_NORM_NSF; } else { return SRSLTE_PHICH_EXT_NSF; } }
/** * Returns the number of REGs in a PRB * 36.211 Section 6.2.4 */ int regs_num_x_symbol(uint32_t symbol, uint32_t nof_port, srslte_cp_t cp) { switch (symbol) { case 0: return 2; case 1: switch (nof_port) { case 1: case 2: return 3; case 4: return 2; default: fprintf(stderr, "Invalid number of ports %d\n", nof_port); return SRSLTE_ERROR; } break; case 2: return 3; case 3: if (SRSLTE_CP_ISNORM(cp)) { return 3; } else { return 2; } default: fprintf(stderr, "Invalid symbol %d\n", symbol); return SRSLTE_ERROR; } }
/* Returns 1 if the SSS is found, 0 if not and -1 if there is not enough space * to correlate */ int sync_sss_symbol(srslte_sync_t *q, const cf_t *input) { int ret; srslte_sss_set_N_id_2(&q->sss, q->N_id_2); switch(q->sss_alg) { case SSS_DIFF: srslte_sss_m0m1_diff(&q->sss, input, &q->m0, &q->m0_value, &q->m1, &q->m1_value); break; case SSS_PARTIAL_3: srslte_sss_m0m1_partial(&q->sss, input, 3, NULL, &q->m0, &q->m0_value, &q->m1, &q->m1_value); break; case SSS_FULL: srslte_sss_m0m1_partial(&q->sss, input, 1, NULL, &q->m0, &q->m0_value, &q->m1, &q->m1_value); break; } q->sf_idx = srslte_sss_subframe(q->m0, q->m1); ret = srslte_sss_N_id_1(&q->sss, q->m0, q->m1); if (ret >= 0) { q->N_id_1 = (uint32_t) ret; DEBUG("SSS detected N_id_1=%d, sf_idx=%d, %s CP\n", q->N_id_1, q->sf_idx, SRSLTE_CP_ISNORM(q->cp)?"Normal":"Extended"); return 1; } else { q->N_id_1 = 1000; return SRSLTE_SUCCESS; } }
/* Map PUCCH symbols to physical resources according to 5.4.3 in 36.211 */ static int pucch_put(srslte_pucch_t *q, srslte_pucch_format_t format, uint32_t n_pucch, cf_t *output) { int ret = SRSLTE_ERROR_INVALID_INPUTS; if (q && output) { ret = SRSLTE_ERROR; uint32_t nsymbols = SRSLTE_CP_ISNORM(q->cell.cp)?SRSLTE_CP_NORM_NSYMB:SRSLTE_CP_EXT_NSYMB; // Determine m uint32_t m = srslte_pucch_m(&q->pucch_cfg, format, n_pucch, q->cell.cp); uint32_t N_sf_0 = get_N_sf(format, 0, q->shortened); for (uint32_t ns=0;ns<2;ns++) { uint32_t N_sf = get_N_sf(format, ns%2, q->shortened); // Determine n_prb uint32_t n_prb = m/2; if ((m+ns)%2) { n_prb = q->cell.nof_prb-1-m/2; } if (n_prb < q->cell.nof_prb) { for (uint32_t i=0;i<N_sf;i++) { uint32_t l = get_pucch_symbol(i, format, q->cell.cp); memcpy(&output[SRSLTE_RE_IDX(q->cell.nof_prb, l+ns*nsymbols, n_prb*SRSLTE_NRE)], &q->z[i*SRSLTE_NRE+ns*N_sf_0*SRSLTE_NRE], SRSLTE_NRE*sizeof(cf_t)); } } else { return SRSLTE_ERROR; } } ret = SRSLTE_SUCCESS; } return ret; }
/* Calculates alpha for format 1/a/b according to 5.5.2.2.2 (is_dmrs=true) or 5.4.1 (is_dmrs=false) of 36.211 */ float srslte_pucch_alpha_format1(uint32_t n_cs_cell[SRSLTE_NSLOTS_X_FRAME][SRSLTE_CP_NORM_NSYMB], srslte_pucch_cfg_t *cfg, uint32_t n_pucch, srslte_cp_t cp, bool is_dmrs, uint32_t ns, uint32_t l, uint32_t *n_oc_ptr, uint32_t *n_prime_ns) { uint32_t c = SRSLTE_CP_ISNORM(cp)?3:2; uint32_t N_prime = (n_pucch < c*cfg->N_cs/cfg->delta_pucch_shift)?cfg->N_cs:SRSLTE_NRE; uint32_t n_prime = n_pucch; if (n_pucch >= c*cfg->N_cs/cfg->delta_pucch_shift) { n_prime = (n_pucch-c*cfg->N_cs/cfg->delta_pucch_shift)%(c*SRSLTE_NRE/cfg->delta_pucch_shift); } if (ns%2) { if (n_pucch >= c*cfg->N_cs/cfg->delta_pucch_shift) { n_prime = (c*(n_prime+1))%(c*SRSLTE_NRE/cfg->delta_pucch_shift+1)-1; } else { uint32_t d=SRSLTE_CP_ISNORM(cp)?2:0; uint32_t h=(n_prime+d)%(c*N_prime/cfg->delta_pucch_shift); n_prime = (h/c)+(h%c)*N_prime/cfg->delta_pucch_shift; } } if (n_prime_ns) { *n_prime_ns = n_prime; } uint32_t n_oc_div = (!is_dmrs && SRSLTE_CP_ISEXT(cp))?2:1; uint32_t n_oc = n_prime*cfg->delta_pucch_shift/N_prime; if (!is_dmrs && SRSLTE_CP_ISEXT(cp)) { n_oc *= 2; } if (n_oc_ptr) { *n_oc_ptr = n_oc; } uint32_t n_cs = 0; if (SRSLTE_CP_ISNORM(cp)) { n_cs = (n_cs_cell[ns][l]+(n_prime*cfg->delta_pucch_shift+(n_oc%cfg->delta_pucch_shift))%N_prime)%SRSLTE_NRE; } else { n_cs = (n_cs_cell[ns][l]+(n_prime*cfg->delta_pucch_shift+n_oc/n_oc_div)%N_prime)%SRSLTE_NRE; } return 2 * M_PI * (n_cs) / SRSLTE_NRE; }
/** * Transforms input samples into output OFDM symbols. * Performs FFT on a each symbol and removes CP. */ void srslte_ofdm_rx_slot(srslte_ofdm_t *q, cf_t *input, cf_t *output) { uint32_t i; for (i=0;i<q->nof_symbols;i++) { input += SRSLTE_CP_ISNORM(q->cp)?SRSLTE_CP_LEN_NORM(i, q->symbol_sz):SRSLTE_CP_LEN_EXT(q->symbol_sz); srslte_dft_run_c(&q->fft_plan, input, q->tmp); memcpy(output, &q->tmp[q->nof_guards], q->nof_re * sizeof(cf_t)); input += q->symbol_sz; output += q->nof_re; } }
void srslte_sync_set_cp(srslte_sync_t *q, srslte_cp_t cp) { q->cp = cp; q->cp_len = SRSLTE_CP_ISNORM(q->cp)?SRSLTE_CP_LEN_NORM(1,q->fft_size):SRSLTE_CP_LEN_EXT(q->fft_size); if (q->frame_size < q->fft_size) { q->nof_symbols = 1; } else { q->nof_symbols = q->frame_size/(q->fft_size+q->cp_len)-1; } }
/** * Transforms input OFDM symbols into output samples. * Performs FFT on a each symbol and adds CP. */ void srslte_ofdm_tx_slot(srslte_ofdm_t *q, cf_t *input, cf_t *output) { uint32_t i, cp_len; for (i=0;i<q->nof_symbols;i++) { cp_len = SRSLTE_CP_ISNORM(q->cp)?SRSLTE_CP_LEN_NORM(i, q->symbol_sz):SRSLTE_CP_LEN_EXT(q->symbol_sz); memcpy(&q->tmp[q->nof_guards], input, q->nof_re * sizeof(cf_t)); srslte_dft_run_c(&q->fft_plan, q->tmp, &output[cp_len]); input += q->nof_re; /* add CP */ memcpy(output, &output[q->symbol_sz], cp_len * sizeof(cf_t)); output += q->symbol_sz + cp_len; } }
/* Decide the most likely cell based on the mode */ static void get_cell(srslte_ue_cellsearch_t * q, uint32_t nof_detected_frames, srslte_ue_cellsearch_result_t *found_cell) { uint32_t i, j; bzero(q->mode_counted, nof_detected_frames); bzero(q->mode_ntimes, sizeof(uint32_t) * nof_detected_frames); /* First find mode of CELL IDs */ for (i = 0; i < nof_detected_frames; i++) { uint32_t cnt = 1; for (j=i+1;j<nof_detected_frames;j++) { if (q->candidates[j].cell_id == q->candidates[i].cell_id && !q->mode_counted[j]) { q->mode_counted[j]=1; cnt++; } } q->mode_ntimes[i] = cnt; } uint32_t max_times=0, mode_pos=0; for (i=0;i<nof_detected_frames;i++) { if (q->mode_ntimes[i] > max_times) { max_times = q->mode_ntimes[i]; mode_pos = i; } } found_cell->cell_id = q->candidates[mode_pos].cell_id; /* Now in all these cell IDs, find most frequent CP */ uint32_t nof_normal = 0; found_cell->peak = 0; for (i=0;i<nof_detected_frames;i++) { if (q->candidates[i].cell_id == found_cell->cell_id) { if (SRSLTE_CP_ISNORM(q->candidates[i].cp)) { nof_normal++; } } // average absolute peak value found_cell->peak += q->candidates[i].peak; } found_cell->peak /= nof_detected_frames; if (nof_normal > q->mode_ntimes[mode_pos]/2) { found_cell->cp = SRSLTE_CP_NORM; } else { found_cell->cp = SRSLTE_CP_EXT; } found_cell->mode = (float) q->mode_ntimes[mode_pos]/nof_detected_frames; // PSR is already averaged so take the last value found_cell->psr = q->candidates[nof_detected_frames-1].psr; // CFO is also already averaged found_cell->cfo = q->candidates[nof_detected_frames-1].cfo; }
/* Returns 1 if the SSS is found, 0 if not and -1 if there is not enough space * to correlate */ int sync_sss(srslte_sync_t *q, cf_t *input, uint32_t peak_pos, srslte_cp_t cp) { int sss_idx, ret; srslte_sss_synch_set_N_id_2(&q->sss, q->N_id_2); /* Make sure we have enough room to find SSS sequence */ sss_idx = (int) 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)); if (sss_idx < 0) { DEBUG("Not enough room to decode CP SSS (sss_idx=%d, peak_pos=%d)\n", sss_idx, peak_pos); return SRSLTE_ERROR; } DEBUG("Searching SSS around sss_idx: %d, peak_pos: %d\n", sss_idx, peak_pos); switch(q->sss_alg) { case SSS_DIFF: srslte_sss_synch_m0m1_diff(&q->sss, &input[sss_idx], &q->m0, &q->m0_value, &q->m1, &q->m1_value); break; case SSS_PARTIAL_3: srslte_sss_synch_m0m1_partial(&q->sss, &input[sss_idx], 3, NULL, &q->m0, &q->m0_value, &q->m1, &q->m1_value); break; case SSS_FULL: srslte_sss_synch_m0m1_partial(&q->sss, &input[sss_idx], 1, NULL, &q->m0, &q->m0_value, &q->m1, &q->m1_value); break; } q->sf_idx = srslte_sss_synch_subframe(q->m0, q->m1); ret = srslte_sss_synch_N_id_1(&q->sss, q->m0, q->m1); if (ret >= 0) { q->N_id_1 = (uint32_t) ret; DEBUG("SSS detected N_id_1=%d, sf_idx=%d, %s CP\n", q->N_id_1, q->sf_idx, SRSLTE_CP_ISNORM(q->cp)?"Normal":"Extended"); return 1; } else { q->N_id_1 = 1000; return SRSLTE_SUCCESS; } }
/* Generates UCI-ACK bits and computes position in q bits */ static int uci_ulsch_interleave_ack_gen(uint32_t ack_q_bit_idx, uint32_t Qm, uint32_t H_prime_total, uint32_t N_pusch_symbs, srslte_cp_t cp, srslte_uci_bit_t *ack_bits) { const uint32_t ack_column_set_norm[4] = {2, 3, 8, 9}; const uint32_t ack_column_set_ext[4] = {1, 2, 6, 7}; if (H_prime_total/N_pusch_symbs >= 1+ack_q_bit_idx/4) { uint32_t row = H_prime_total/N_pusch_symbs-1-ack_q_bit_idx/4; uint32_t colidx = (3*ack_q_bit_idx)%4; uint32_t col = SRSLTE_CP_ISNORM(cp)?ack_column_set_norm[colidx]:ack_column_set_ext[colidx]; for(uint32_t k=0; k<Qm; k++) { ack_bits[k].position = row *Qm + (H_prime_total/N_pusch_symbs)*col*Qm + k; } return SRSLTE_SUCCESS; } else { fprintf(stderr, "Error interleaving UCI-ACK bit idx %d for H_prime_total=%d and N_pusch_symbs=%d\n", ack_q_bit_idx, H_prime_total, N_pusch_symbs); return SRSLTE_ERROR; } }
// Compute m according to Section 5.4.3 of 36.211 uint32_t srslte_pucch_m(srslte_pucch_cfg_t *cfg, srslte_pucch_format_t format, uint32_t n_pucch, srslte_cp_t cp) { uint32_t m=0; switch (format) { case SRSLTE_PUCCH_FORMAT_1: case SRSLTE_PUCCH_FORMAT_1A: case SRSLTE_PUCCH_FORMAT_1B: m = cfg->n_rb_2; uint32_t c=SRSLTE_CP_ISNORM(cp)?3:2; if (n_pucch >= c*cfg->N_cs/cfg->delta_pucch_shift) { m = (n_pucch-c*cfg->N_cs/cfg->delta_pucch_shift)/(c*SRSLTE_NRE/cfg->delta_pucch_shift) +cfg->n_rb_2+(uint32_t)ceilf((float) cfg->N_cs/8); } break; case SRSLTE_PUCCH_FORMAT_2: case SRSLTE_PUCCH_FORMAT_2A: case SRSLTE_PUCCH_FORMAT_2B: m = n_pucch/SRSLTE_NRE; break; } return m; }
/* Inserts UCI-RI bits into the correct positions in the g buffer before interleaving */ static int uci_ulsch_interleave_ri_gen(uint32_t ri_q_bit_idx, uint32_t Qm, uint32_t H_prime_total, uint32_t N_pusch_symbs, srslte_cp_t cp, srslte_uci_bit_t *ri_bits) { static uint32_t ri_column_set_norm[4] = {1, 4, 7, 10}; static uint32_t ri_column_set_ext[4] = {0, 3, 5, 8}; if (H_prime_total/N_pusch_symbs >= 1+ri_q_bit_idx/4) { uint32_t row = H_prime_total/N_pusch_symbs-1-ri_q_bit_idx/4; uint32_t colidx = (3*ri_q_bit_idx)%4; uint32_t col = SRSLTE_CP_ISNORM(cp)?ri_column_set_norm[colidx]:ri_column_set_ext[colidx]; for(uint32_t k=0; k<Qm; k++) { ri_bits[k].position = row *Qm + (H_prime_total/N_pusch_symbs)*col*Qm + k; } return SRSLTE_SUCCESS; } else { fprintf(stderr, "Error interleaving UCI-RI bit idx %d for H_prime_total=%d and N_pusch_symbs=%d\n", ri_q_bit_idx, H_prime_total, N_pusch_symbs); return SRSLTE_ERROR; } }
/* Shifts the signal after the iFFT or before the FFT. * Freq_shift is relative to inter-carrier spacing. * Caution: This function shall not be called during run-time */ int srslte_ofdm_set_freq_shift(srslte_ofdm_t *q, float freq_shift) { q->shift_buffer = srslte_vec_malloc(sizeof(cf_t) * SRSLTE_SF_LEN(q->symbol_sz)); if (!q->shift_buffer) { perror("malloc"); return -1; } cf_t *ptr = q->shift_buffer; for (uint32_t n=0;n<2;n++) { for (uint32_t i=0;i<q->nof_symbols;i++) { uint32_t cplen = SRSLTE_CP_ISNORM(q->cp)?SRSLTE_CP_LEN_NORM(i, q->symbol_sz):SRSLTE_CP_LEN_EXT(q->symbol_sz); for (uint32_t t=0;t<q->symbol_sz+cplen;t++) { ptr[t] = cexpf(I*2*M_PI*((float) t-(float)cplen)*freq_shift/q->symbol_sz); } ptr += q->symbol_sz+cplen; } } /* Disable DC carrier addition */ srslte_dft_plan_set_dc(&q->fft_plan, false); q->freq_shift = true; return SRSLTE_SUCCESS; }
int main(int argc, char **argv) { cf_t *buffer; int frame_cnt, n; void *uhd; srslte_pss_synch_t pss; srslte_cfo_t cfocorr, cfocorr64; srslte_sss_synch_t sss; int32_t flen; int peak_idx, last_peak; float peak_value; float mean_peak; uint32_t nof_det, nof_nodet, nof_nopeak, nof_nopeakdet; cf_t ce[SRSLTE_PSS_LEN]; parse_args(argc, argv); if (N_id_2_sync == -1) { N_id_2_sync = cell_id%3; } uint32_t N_id_2 = cell_id%3; uint32_t N_id_1 = cell_id/3; #ifndef DISABLE_GRAPHICS if (!disable_plots) init_plots(); #endif float srate = 15000.0*fft_size; flen = srate*5/1000; printf("Opening UHD device...\n"); if (cuhd_open(uhd_args, &uhd)) { fprintf(stderr, "Error opening uhd\n"); exit(-1); } if (srate < 10e6) { cuhd_set_master_clock_rate(uhd, 4*srate); } else { cuhd_set_master_clock_rate(uhd, srate); } printf("Set RX rate: %.2f MHz\n", cuhd_set_rx_srate(uhd, srate) / 1000000); printf("Set RX gain: %.1f dB\n", cuhd_set_rx_gain(uhd, uhd_gain)); printf("Set RX freq: %.2f MHz\n", cuhd_set_rx_freq(uhd, uhd_freq) / 1000000); cuhd_rx_wait_lo_locked(uhd); buffer = malloc(sizeof(cf_t) * flen * 2); if (!buffer) { perror("malloc"); exit(-1); } if (srslte_pss_synch_init_fft(&pss, flen, fft_size)) { fprintf(stderr, "Error initiating PSS\n"); exit(-1); } if (srslte_pss_synch_set_N_id_2(&pss, N_id_2_sync)) { fprintf(stderr, "Error setting N_id_2=%d\n",N_id_2_sync); exit(-1); } srslte_cfo_init(&cfocorr, flen); srslte_cfo_init(&cfocorr64, flen); if (srslte_sss_synch_init(&sss, fft_size)) { fprintf(stderr, "Error initializing SSS object\n"); return SRSLTE_ERROR; } srslte_sss_synch_set_N_id_2(&sss, N_id_2); printf("N_id_2: %d\n", N_id_2); cuhd_start_rx_stream(uhd); printf("Frame length %d samples\n", flen); printf("PSS detection threshold: %.2f\n", threshold); nof_det = nof_nodet = nof_nopeak = nof_nopeakdet = 0; frame_cnt = 0; last_peak = 0; mean_peak = 0; int peak_offset = 0; float cfo; float mean_cfo = 0; uint32_t m0, m1; uint32_t sss_error1 = 0, sss_error2 = 0, sss_error3 = 0; uint32_t cp_is_norm = 0; srslte_sync_t ssync; bzero(&ssync, sizeof(srslte_sync_t)); ssync.fft_size = fft_size; while(frame_cnt < nof_frames || nof_frames == -1) { n = cuhd_recv(uhd, buffer, flen - peak_offset, 1); if (n < 0) { fprintf(stderr, "Error receiving samples\n"); exit(-1); } peak_idx = srslte_pss_synch_find_pss(&pss, buffer, &peak_value); if (peak_idx < 0) { fprintf(stderr, "Error finding PSS peak\n"); exit(-1); } mean_peak = SRSLTE_VEC_CMA(peak_value, mean_peak, frame_cnt); if (peak_value >= threshold) { nof_det++; if (peak_idx >= fft_size) { // Estimate CFO cfo = srslte_pss_synch_cfo_compute(&pss, &buffer[peak_idx-fft_size]); mean_cfo = SRSLTE_VEC_CMA(cfo, mean_cfo, frame_cnt); // Correct CFO srslte_cfo_correct(&cfocorr, buffer, buffer, -mean_cfo / fft_size); // Estimate channel if (srslte_pss_synch_chest(&pss, &buffer[peak_idx-fft_size], ce)) { fprintf(stderr, "Error computing channel estimation\n"); exit(-1); } // Find SSS int sss_idx = peak_idx-2*fft_size-(SRSLTE_CP_ISNORM(cp)?SRSLTE_CP_LEN(fft_size, SRSLTE_CP_NORM_LEN):SRSLTE_CP_LEN(fft_size, SRSLTE_CP_EXT_LEN)); if (sss_idx >= 0 && sss_idx < flen-fft_size) { srslte_sss_synch_m0m1_partial(&sss, &buffer[sss_idx], 3, NULL, &m0, &m0_value, &m1, &m1_value); if (srslte_sss_synch_N_id_1(&sss, m0, m1) != N_id_1) { sss_error2++; } INFO("Partial N_id_1: %d\n", srslte_sss_synch_N_id_1(&sss, m0, m1)); srslte_sss_synch_m0m1_diff(&sss, &buffer[sss_idx], &m0, &m0_value, &m1, &m1_value); if (srslte_sss_synch_N_id_1(&sss, m0, m1) != N_id_1) { sss_error3++; } INFO("Diff N_id_1: %d\n", srslte_sss_synch_N_id_1(&sss, m0, m1)); srslte_sss_synch_m0m1_partial(&sss, &buffer[sss_idx], 1, NULL, &m0, &m0_value, &m1, &m1_value); if (srslte_sss_synch_N_id_1(&sss, m0, m1) != N_id_1) { sss_error1++; } INFO("Full N_id_1: %d\n", srslte_sss_synch_N_id_1(&sss, m0, m1)); } // Estimate CP if (peak_idx > 2*(fft_size + SRSLTE_CP_LEN_EXT(fft_size))) { srslte_cp_t cp = srslte_sync_detect_cp(&ssync, buffer, peak_idx); if (SRSLTE_CP_ISNORM(cp)) { cp_is_norm++; } } } else { INFO("No space for CFO computation. Frame starts at \n",peak_idx); } if(srslte_sss_synch_subframe(m0,m1) == 0) { #ifndef DISABLE_GRAPHICS if (!disable_plots) do_plots_sss(sss.corr_output_m0, sss.corr_output_m1); #endif } } else { nof_nodet++; } if (frame_cnt > 100) { if (abs(last_peak-peak_idx) > 4) { if (peak_value >= threshold) { nof_nopeakdet++; } nof_nopeak++; } } frame_cnt++; printf("[%5d]: Pos: %5d, PSR: %4.1f (~%4.1f) Pdet: %4.2f, " "FA: %4.2f, CFO: %+4.1f KHz SSSmiss: %4.2f/%4.2f/%4.2f CPNorm: %.0f%%\r", frame_cnt, peak_idx, peak_value, mean_peak, (float) nof_det/frame_cnt, (float) nof_nopeakdet/frame_cnt, mean_cfo*15, (float) sss_error1/nof_det,(float) sss_error2/nof_det,(float) sss_error3/nof_det, (float) cp_is_norm/nof_det * 100); if (SRSLTE_VERBOSE_ISINFO()) { printf("\n"); } #ifndef DISABLE_GRAPHICS if (!disable_plots) do_plots(pss.conv_output_avg, pss.conv_output_avg[peak_idx], pss.fft_size+pss.frame_size-1, ce); #endif last_peak = peak_idx; } srslte_pss_synch_free(&pss); free(buffer); cuhd_close(uhd); printf("Ok\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 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; }
/** * Initializes REGs structure. * Sets all REG indices and initializes PCFICH, PHICH and PDCCH REGs * Returns 0 if OK, -1 on error */ int srslte_regs_init(srslte_regs_t *h, srslte_cell_t cell) { int ret = SRSLTE_ERROR_INVALID_INPUTS; uint32_t i, k; uint32_t j[4], jmax, prb; uint32_t n[4], vo; uint32_t max_ctrl_symbols; if (h != NULL && srslte_cell_isvalid(&cell)) { bzero(h, sizeof(srslte_regs_t)); ret = SRSLTE_ERROR; max_ctrl_symbols = cell.nof_prb<10?4:3; vo = cell.id % 3; h->cell = cell; h->max_ctrl_symbols = max_ctrl_symbols; h->cfi_initiated = false; h->phich_res = cell.phich_resources; h->phich_len = cell.phich_length; h->nof_regs = 0; for (i = 0; i < max_ctrl_symbols; i++) { n[i] = regs_num_x_symbol(i, h->cell.nof_ports, h->cell.cp); if (n[i] == -1) { goto clean_and_exit; } h->nof_regs += h->cell.nof_prb * n[i]; } INFO("Indexing %d REGs. CellId: %d, %d PRB, CP: %s\n", h->nof_regs, h->cell.id, h->cell.nof_prb, SRSLTE_CP_ISNORM(h->cell.cp)?"Normal":"Extended"); h->regs = malloc(sizeof(srslte_regs_reg_t) * h->nof_regs); if (!h->regs) { perror("malloc"); goto clean_and_exit; } /* Sort REGs according to PDCCH mapping, beggining from the lowest l index then k */ bzero(j, sizeof(int) * 4); k = i = prb = jmax = 0; while (k < h->nof_regs) { if (n[i] == 3 || (n[i] == 2 && jmax != 1)) { if (regs_reg_init(&h->regs[k], i, j[i], prb * SRSLTE_NRE, n[i], vo)) { fprintf(stderr, "Error initializing REGs\n"); goto clean_and_exit; } /*DEBUG("Available REG #%3d: l=%d, prb=%d, nreg=%d (k0=%d)\n", k, i, prb, j[i], h->regs[k].k0); */ j[i]++; k++; } i++; if (i == max_ctrl_symbols) { i = 0; jmax++; } if (jmax == 3) { prb++; bzero(j, sizeof(int) * 4); jmax = 0; } } if (regs_pcfich_init(h)) { fprintf(stderr, "Error initializing PCFICH REGs\n"); goto clean_and_exit; } if (regs_phich_init(h)) { fprintf(stderr, "Error initializing PHICH REGs\n"); goto clean_and_exit; } if (regs_pdcch_init(h)) { fprintf(stderr, "Error initializing PDCCH REGs\n"); goto clean_and_exit; } ret = SRSLTE_SUCCESS; } clean_and_exit: if (ret != SRSLTE_SUCCESS) { srslte_regs_free(h); } return ret; }
int main(int argc, char **argv) { srslte_phich_t phich; srslte_regs_t regs; int i, j; cf_t *ce[SRSLTE_MAX_PORTS]; int nof_re; cf_t *slot_symbols[SRSLTE_MAX_PORTS]; uint8_t ack[50][SRSLTE_PHICH_NORM_NSEQUENCES], ack_rx; uint32_t nsf; float distance; int cid, max_cid; uint32_t ngroup, nseq, max_nseq; parse_args(argc,argv); max_nseq = SRSLTE_CP_ISNORM(cell.cp)?SRSLTE_PHICH_NORM_NSEQUENCES:SRSLTE_PHICH_EXT_NSEQUENCES; nof_re = SRSLTE_CP_NORM_NSYMB * cell.nof_prb * SRSLTE_NRE; /* init memory */ for (i=0;i<SRSLTE_MAX_PORTS;i++) { ce[i] = malloc(sizeof(cf_t) * nof_re); if (!ce[i]) { perror("malloc"); exit(-1); } for (j=0;j<nof_re;j++) { ce[i][j] = 1; } slot_symbols[i] = malloc(sizeof(cf_t) * nof_re); if (!slot_symbols[i]) { perror("malloc"); exit(-1); } } if (cell.id == 1000) { cid = 0; max_cid = 503; } else { cid = cell.id; max_cid = cell.id; } while(cid <= max_cid) { cell.id = cid; printf("Testing CellID=%d...\n", cid); if (srslte_regs_init(®s, cell)) { fprintf(stderr, "Error initiating regs\n"); exit(-1); } if (srslte_phich_init(&phich, ®s, cell)) { fprintf(stderr, "Error creating PBCH object\n"); exit(-1); } for (nsf=0;nsf<10;nsf++) { srslte_phich_reset(&phich, slot_symbols); /* Transmit all PHICH groups and sequence numbers */ for (ngroup=0;ngroup<srslte_phich_ngroups(&phich);ngroup++) { for (nseq=0;nseq<max_nseq;nseq++) { ack[ngroup][nseq] = rand()%2; srslte_phich_encode(&phich, ack[ngroup][nseq], ngroup, nseq, nsf, slot_symbols); } } /* combine outputs */ for (i=1;i<cell.nof_ports;i++) { for (j=0;j<nof_re;j++) { slot_symbols[0][j] += slot_symbols[i][j]; } } /* Receive all PHICH groups and sequence numbers */ for (ngroup=0;ngroup<srslte_phich_ngroups(&phich);ngroup++) { for (nseq=0;nseq<max_nseq;nseq++) { if (srslte_phich_decode(&phich, slot_symbols[0], ce, 0, ngroup, nseq, nsf, &ack_rx, &distance)<0) { printf("Error decoding ACK\n"); exit(-1); } INFO("%d/%d, ack_tx: %d, ack_rx: %d, ns: %d, distance: %f\n", ngroup, nseq, ack[ngroup][nseq], ack_rx, nsf, distance); if (ack[ngroup][nseq] != ack_rx) { printf("Invalid received ACK: %d!=%d\n", ack[ngroup][nseq], ack_rx); exit(-1); } if (distance < 1.5) { printf("Error\n"); exit(-1); } } } } srslte_phich_free(&phich); srslte_regs_free(®s); cid++; } for (i=0;i<SRSLTE_MAX_PORTS;i++) { free(ce[i]); free(slot_symbols[i]); } printf("OK\n"); exit(0); }
/** * 36.211 6.6.1 */ int srslte_sequence_pbch(srslte_sequence_t *seq, srslte_cp_t cp, uint32_t cell_id) { bzero(seq, sizeof(srslte_sequence_t)); return srslte_sequence_LTE_pr(seq, SRSLTE_CP_ISNORM(cp)?1920:1728, cell_id); }