/******************************************************************************* OpenAirInterface Copyright(c) 1999 - 2014 Eurecom OpenAirInterface is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. OpenAirInterface is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenAirInterface.The full GNU General Public License is included in this distribution in the file called "COPYING". If not, see <http://www.gnu.org/licenses/>. Contact Information OpenAirInterface Admin: openair_admin@eurecom.fr OpenAirInterface Tech : openair_tech@eurecom.fr OpenAirInterface Dev : openair4g-devel@lists.eurecom.fr Address : Eurecom, Campus SophiaTech, 450 Route des Chappes, CS 50193 - 06904 Biot Sophia Antipolis cedex, FRANCE *******************************************************************************/ /*! \file PHY/LTE_TRANSPORT/sss.c * \brief Top-level routines for generating and decoding the secondary synchronization signal (SSS) V8.6 2009-03 * \author R. Knopp * \date 2011 * \version 0.1 * \company Eurecom * \email: knopp@eurecom.fr * \note * \warning */ #include "PHY/defs.h" #include "defs.h" #include "PHY/extern.h" //#define DEBUG_SSS int generate_sss(int32_t **txdataF, int16_t amp, LTE_DL_FRAME_PARMS *frame_parms, uint16_t symbol, uint16_t slot_offset) { uint8_t i,aa,Nsymb; int16_t *d,k; uint8_t Nid2; uint16_t Nid1; int16_t a; Nid2 = frame_parms->Nid_cell % 3; Nid1 = frame_parms->Nid_cell/3; if (slot_offset < 3) d = &d0_sss[62*(Nid2 + (Nid1*3))]; else d = &d5_sss[62*(Nid2 + (Nid1*3))]; Nsymb = (frame_parms->Ncp==NORMAL)?14:12; k = frame_parms->ofdm_symbol_size-3*12+5; a = (frame_parms->nb_antennas_tx == 1) ? amp : (amp*ONE_OVER_SQRT2_Q15)>>15; for (i=0; i<62; i++) { for (aa=0; aa<frame_parms->nb_antennas_tx; aa++) { ((int16_t*)txdataF[aa])[2*(slot_offset*Nsymb/2*frame_parms->ofdm_symbol_size + symbol*frame_parms->ofdm_symbol_size + k)] = (a * d[i]); ((int16_t*)txdataF[aa])[2*(slot_offset*Nsymb/2*frame_parms->ofdm_symbol_size + symbol*frame_parms->ofdm_symbol_size + k)+1] = 0; } k+=1; if (k >= frame_parms->ofdm_symbol_size) { k++; k-=frame_parms->ofdm_symbol_size; } } return(0); } int pss_ch_est(PHY_VARS_UE *phy_vars_ue, int32_t pss_ext[4][72], int32_t sss_ext[4][72]) { int16_t *pss; int16_t *pss_ext2,*sss_ext2,*sss_ext3,tmp_re,tmp_im,tmp_re2,tmp_im2; uint8_t aarx,i; LTE_DL_FRAME_PARMS *frame_parms = &phy_vars_ue->lte_frame_parms; switch (phy_vars_ue->lte_ue_common_vars.eNb_id) { case 0: pss = &primary_synch0[10]; break; case 1: pss = &primary_synch1[10]; break; case 2: pss = &primary_synch2[10]; break; default: pss = &primary_synch0[10]; break; } sss_ext3 = (int16_t*)&sss_ext[0][5]; for (aarx=0; aarx<frame_parms->nb_antennas_rx; aarx++) { sss_ext2 = (int16_t*)&sss_ext[aarx][5]; pss_ext2 = (int16_t*)&pss_ext[aarx][5]; for (i=0; i<62; i++) { // This is H*(PSS) = R* \cdot PSS tmp_re = (int16_t)(((pss_ext2[i<<1] * (int32_t)pss[i<<1])>>15) + ((pss_ext2[1+(i<<1)] * (int32_t)pss[1+(i<<1)])>>15)); tmp_im = (int16_t)(((pss_ext2[i<<1] * (int32_t)pss[1+(i<<1)])>>15) - ((pss_ext2[1+(i<<1)] * (int32_t)pss[(i<<1)])>>15)); // printf("H*(%d,%d) : (%d,%d)\n",aarx,i,tmp_re,tmp_im); // This is R(SSS) \cdot H*(PSS) tmp_re2 = (int16_t)(((tmp_re * (int32_t)sss_ext2[i<<1])>>15) - ((tmp_im * (int32_t)sss_ext2[1+(i<<1)]>>15))); tmp_im2 = (int16_t)(((tmp_re * (int32_t)sss_ext2[1+(i<<1)])>>15) + ((tmp_im * (int32_t)sss_ext2[(i<<1)]>>15))); // printf("SSSi(%d,%d) : (%d,%d)\n",aarx,i,sss_ext2[i<<1],sss_ext2[1+(i<<1)]); // printf("SSSo(%d,%d) : (%d,%d)\n",aarx,i,tmp_re2,tmp_im2); // MRC on RX antennas if (aarx==0) { sss_ext3[i<<1] = tmp_re2; sss_ext3[1+(i<<1)] = tmp_im2; } else { sss_ext3[i<<1] += tmp_re2; sss_ext3[1+(i<<1)] += tmp_im2; } } } // sss_ext now contains the compensated SSS return(0); } int pss_sss_extract(PHY_VARS_UE *phy_vars_ue, int32_t pss_ext[4][72], int32_t sss_ext[4][72]) { uint16_t rb,nb_rb=6; uint8_t i,aarx; int32_t *pss_rxF,*pss_rxF_ext; int32_t *sss_rxF,*sss_rxF_ext; LTE_DL_FRAME_PARMS *frame_parms = &phy_vars_ue->lte_frame_parms; int rx_offset = frame_parms->ofdm_symbol_size-3*12; uint8_t pss_symb,sss_symb; int32_t **rxdataF = phy_vars_ue->lte_ue_common_vars.rxdataF; if (frame_parms->frame_type == FDD) { pss_symb = 6-frame_parms->Ncp; sss_symb = pss_symb-1; } else { pss_symb = 2; sss_symb = frame_parms->symbols_per_tti-1; } for (aarx=0; aarx<frame_parms->nb_antennas_rx; aarx++) { //printf("extract_rbs: symbol_mod=%d, rx_offset=%d, ch_offset=%d\n",symbol_mod, // (rx_offset + (symbol*(frame_parms->ofdm_symbol_size)))*2, // LTE_CE_OFFSET+ch_offset+(symbol_mod*(frame_parms->ofdm_symbol_size))); pss_rxF = &rxdataF[aarx][(rx_offset + (pss_symb*(frame_parms->ofdm_symbol_size)))]; sss_rxF = &rxdataF[aarx][(rx_offset + (sss_symb*(frame_parms->ofdm_symbol_size)))]; pss_rxF_ext = &pss_ext[aarx][0]; sss_rxF_ext = &sss_ext[aarx][0]; for (rb=0; rb<nb_rb; rb++) { // skip DC carrier if (rb==3) { sss_rxF = &rxdataF[aarx][(1 + (sss_symb*(frame_parms->ofdm_symbol_size)))]; pss_rxF = &rxdataF[aarx][(1 + (pss_symb*(frame_parms->ofdm_symbol_size)))]; } for (i=0; i<12; i++) { pss_rxF_ext[i]=pss_rxF[i]; sss_rxF_ext[i]=sss_rxF[i]; } pss_rxF+=12; sss_rxF+=12; pss_rxF_ext+=12; sss_rxF_ext+=12; } } return(0); } int16_t phase_re[7] = {16383, 25101, 30791, 32767, 30791, 25101, 16383}; int16_t phase_im[7] = {-28378, -21063, -11208, 0, 11207, 21062, 28377}; int rx_sss(PHY_VARS_UE *phy_vars_ue,int32_t *tot_metric,uint8_t *flip_max,uint8_t *phase_max) { uint8_t i; int32_t pss_ext[4][72]; int32_t sss0_ext[4][72],sss5_ext[4][72]; uint8_t Nid2 = phy_vars_ue->lte_ue_common_vars.eNb_id; uint8_t flip,phase; uint16_t Nid1; int16_t *sss0,*sss5; LTE_DL_FRAME_PARMS *frame_parms=&phy_vars_ue->lte_frame_parms; int32_t metric; int16_t *d0,*d5; if (phy_vars_ue->lte_frame_parms.frame_type == FDD) { #ifdef DEBUG_SSS if (phy_vars_ue->lte_frame_parms.Ncp == NORMAL) msg("[PHY][UE%d] Doing SSS for FDD Normal Prefix\n",phy_vars_ue->Mod_id); else msg("[PHY][UE%d] Doing SSS for FDD Extended Prefix\n",phy_vars_ue->Mod_id); #endif // Do FFTs for SSS/PSS // SSS slot_fep(phy_vars_ue, (frame_parms->symbols_per_tti/2)-2, // second to last symbol of 0, // slot 0 phy_vars_ue->rx_offset, 0, 1); // PSS slot_fep(phy_vars_ue, (frame_parms->symbols_per_tti/2)-1, // last symbol of 0, // slot 0 phy_vars_ue->rx_offset, 0, 1); } else { // TDD #ifdef DEBUG_SSS if (phy_vars_ue->lte_frame_parms.Ncp == NORMAL) msg("[PHY][UE%d] Doing SSS for TDD Normal Prefix\n",phy_vars_ue->Mod_id); else msg("[PHY][UE%d] Doing SSS for TDD Extended Prefix\n",phy_vars_ue->Mod_id); #endif // SSS slot_fep(phy_vars_ue, (frame_parms->symbols_per_tti>>1)-1, // last symbol of 1, // slot 1 phy_vars_ue->rx_offset, 0, 1); // PSS slot_fep(phy_vars_ue, 2, // symbol 2 of 2, // slot 2 phy_vars_ue->rx_offset, 0, 1); } pss_sss_extract(phy_vars_ue, pss_ext, sss0_ext); /* write_output("rxsig0.m","rxs0",&phy_vars_ue->lte_ue_common_vars.rxdata[0][0],phy_vars_ue->lte_frame_parms.samples_per_tti,1,1); write_output("rxdataF0.m","rxF0",&phy_vars_ue->lte_ue_common_vars.rxdataF[0][0],2*14*phy_vars_ue->lte_frame_parms.ofdm_symbol_size,2,1); write_output("pss_ext0.m","pssext0",pss_ext,72,1,1); write_output("sss0_ext0.m","sss0ext0",sss0_ext,72,1,1); */ // get conjugated channel estimate from PSS (symbol 6), H* = R* \cdot PSS // and do channel estimation and compensation based on PSS pss_ch_est(phy_vars_ue, pss_ext, sss0_ext); // write_output("sss0_comp0.m","sss0comp0",sss0_ext,72,1,1); if (phy_vars_ue->lte_frame_parms.frame_type == FDD) { // FDD // SSS slot_fep(phy_vars_ue, (frame_parms->symbols_per_tti/2)-2, 10, phy_vars_ue->rx_offset, 0,1); // PSS slot_fep(phy_vars_ue, (frame_parms->symbols_per_tti/2)-1, 10, phy_vars_ue->rx_offset, 0,1); } else { // TDD // SSS slot_fep(phy_vars_ue, (frame_parms->symbols_per_tti>>1)-1, 11, phy_vars_ue->rx_offset, 0, 1); // PSS slot_fep(phy_vars_ue, 2, 12, phy_vars_ue->rx_offset, 0, 1); } pss_sss_extract(phy_vars_ue, pss_ext, sss5_ext); // write_output("sss5_ext0.m","sss5ext0",sss5_ext,72,1,1); // get conjugated channel estimate from PSS (symbol 6), H* = R* \cdot PSS // and do channel estimation and compensation based on PSS pss_ch_est(phy_vars_ue, pss_ext, sss5_ext); // now do the SSS detection based on the precomputed sequences in PHY/LTE_TRANSPORT/sss.h *tot_metric = -99999999; sss0 = (int16_t*)&sss0_ext[0][5]; sss5 = (int16_t*)&sss5_ext[0][5]; for (flip=0; flip<2; flip++) { // d0/d5 flip in RX frame for (phase=0; phase<7; phase++) { // phase offset between PSS and SSS for (Nid1 = 0 ; Nid1 <= 167; Nid1++) { // 168 possible Nid1 values metric = 0; if (flip==0) { d0 = &d0_sss[62*(Nid2 + (Nid1*3))]; d5 = &d5_sss[62*(Nid2 + (Nid1*3))]; } else { d5 = &d0_sss[62*(Nid2 + (Nid1*3))]; d0 = &d5_sss[62*(Nid2 + (Nid1*3))]; } // This is the inner product using one particular value of each unknown parameter for (i=0; i<62; i++) { metric += (int16_t)(((d0[i]*((((phase_re[phase]*(int32_t)sss0[i<<1])>>19)-((phase_im[phase]*(int32_t)sss0[1+(i<<1)])>>19)))) + (d5[i]*((((phase_re[phase]*(int32_t)sss5[i<<1])>>19)-((phase_im[phase]*(int32_t)sss5[1+(i<<1)])>>19)))))); } // if the current metric is better than the last save it if (metric > *tot_metric) { *tot_metric = metric; phy_vars_ue->lte_frame_parms.Nid_cell = Nid2+(3*Nid1); *phase_max = phase; *flip_max=flip; #ifdef DEBUG_SSS msg("(flip,phase,Nid1) (%d,%d,%d), metric_phase %d tot_metric %d, phase_max %d, flip_max %d\n",flip,phase,Nid1,metric,*tot_metric,*phase_max,*flip_max); #endif } } } } return(0); }