/*******************************************************************************
    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@eurecom.fr
  
  Address      : Eurecom, Campus SophiaTech, 450 Route des Chappes, CS 50193 - 06904 Biot Sophia Antipolis cedex, FRANCE

 *******************************************************************************/

/*! \file PHY/LTE_TRANSPORT/drs_modulation.c
* \brief Top-level routines for generating the Demodulation Reference Signals from 36-211, V8.6 2009-03
* \author R. Knopp, F. Kaltenberger, A. Bhamri
* \date 2011
* \version 0.1
* \company Eurecom
* \email: knopp@eurecom.fr,florian.kaltenberger@eurecom.fr,ankit.bhamri@eurecom.fr
* \note
* \warning
*/
#include "PHY/defs.h"
#include "PHY/extern.h"
#include <emmintrin.h>
#include <xmmintrin.h>
//#define DEBUG_DRS

int generate_drs_pusch(PHY_VARS_UE *phy_vars_ue,
		       uint8_t eNB_id,
		       short amp,
		       unsigned int subframe,
		       unsigned int first_rb,
		       unsigned int nb_rb,
		       uint8_t ant) {

  uint16_t k,l,Msc_RS,Msc_RS_idx,rb,drs_offset;
  uint16_t * Msc_idx_ptr;
  int subframe_offset,re_offset,symbol_offset;

  //uint32_t phase_shift; // phase shift for cyclic delay in DM RS
  //uint8_t alpha_ind;

  int16_t alpha_re[12] = {32767, 28377, 16383,     0,-16384,  -28378,-32768,-28378,-16384,    -1, 16383, 28377};
  int16_t alpha_im[12] = {0,     16383, 28377, 32767, 28377,   16383,     0,-16384,-28378,-32768,-28378,-16384};

  uint8_t cyclic_shift,cyclic_shift0,cyclic_shift1; 
  LTE_DL_FRAME_PARMS *frame_parms = &phy_vars_ue->lte_frame_parms;
  mod_sym_t *txdataF = phy_vars_ue->lte_ue_common_vars.txdataF[ant];
  uint32_t u,v,alpha_ind;
  uint32_t u0=frame_parms->pusch_config_common.ul_ReferenceSignalsPUSCH.grouphop[subframe<<1];
  uint32_t u1=frame_parms->pusch_config_common.ul_ReferenceSignalsPUSCH.grouphop[1+(subframe<<1)];
  uint32_t v0=frame_parms->pusch_config_common.ul_ReferenceSignalsPUSCH.seqhop[subframe<<1];
  uint32_t v1=frame_parms->pusch_config_common.ul_ReferenceSignalsPUSCH.seqhop[1+(subframe<<1)];
  int32_t ref_re,ref_im;
  uint8_t harq_pid = subframe2harq_pid(frame_parms,phy_vars_ue->frame_tx,subframe);

  cyclic_shift0 = (frame_parms->pusch_config_common.ul_ReferenceSignalsPUSCH.cyclicShift +
		   phy_vars_ue->ulsch_ue[eNB_id]->harq_processes[harq_pid]->n_DMRS2 +
		   phy_vars_ue->lte_frame_parms.pusch_config_common.ul_ReferenceSignalsPUSCH.nPRS[subframe<<1]+
		   ((phy_vars_ue->ulsch_ue[0]->cooperation_flag==2)?10:0)+
		   ant*6) % 12;
  //  printf("PUSCH.cyclicShift %d, n_DMRS2 %d, nPRS %d\n",frame_parms->pusch_config_common.ul_ReferenceSignalsPUSCH.cyclicShift,phy_vars_ue->ulsch_ue[eNB_id]->n_DMRS2,phy_vars_ue->lte_frame_parms.pusch_config_common.ul_ReferenceSignalsPUSCH.nPRS[subframe<<1]);
  cyclic_shift1 = (frame_parms->pusch_config_common.ul_ReferenceSignalsPUSCH.cyclicShift +
		   phy_vars_ue->ulsch_ue[eNB_id]->harq_processes[harq_pid]->n_DMRS2 +
		   phy_vars_ue->lte_frame_parms.pusch_config_common.ul_ReferenceSignalsPUSCH.nPRS[(subframe<<1)+1]+
		   ((phy_vars_ue->ulsch_ue[0]->cooperation_flag==2)?10:0)+
		   ant*6) % 12;

  //       cyclic_shift0 = 0;
  //        cyclic_shift1 = 0;
  Msc_RS = 12*nb_rb;    

#ifdef USER_MODE
  Msc_idx_ptr = (uint16_t*) bsearch(&Msc_RS, dftsizes, 33, sizeof(uint16_t), compareints);
  if (Msc_idx_ptr)
    Msc_RS_idx = Msc_idx_ptr - dftsizes;
  else {
    msg("generate_drs_pusch: index for Msc_RS=%d not found\n",Msc_RS);
    return(-1);
  }
#else
  uint8_t b;
  for (b=0;b<33;b++) 
    if (Msc_RS==dftsizes[b])
      Msc_RS_idx = b;
#endif
#ifdef DEBUG_DRS
  msg("[PHY] drs_modulation: Msc_RS = %d, Msc_RS_idx = %d,cyclic_shift %d, u0 %d, v0 %d, u1 %d, v1 %d,cshift0 %d,cshift1 %d\n",Msc_RS, Msc_RS_idx,cyclic_shift,u0,v0,u1,v1,cyclic_shift0,cyclic_shift1);

#endif


  for (l = (3 - frame_parms->Ncp),u=u0,v=v0,cyclic_shift=cyclic_shift0; 
       l<frame_parms->symbols_per_tti; 
       l += (7 - frame_parms->Ncp),u=u1,v=v1,cyclic_shift=cyclic_shift1) {

    drs_offset = 0;  //  msg("drs_modulation: Msc_RS = %d, Msc_RS_idx = %d\n",Msc_RS, Msc_RS_idx);


#ifdef IFFT_FPGA_UE
    re_offset = frame_parms->N_RB_DL*12/2;
    subframe_offset = subframe*frame_parms->symbols_per_tti*frame_parms->N_RB_UL*12;
    symbol_offset = subframe_offset + frame_parms->N_RB_UL*12*l;
#else
    re_offset = frame_parms->first_carrier_offset;
    subframe_offset = subframe*frame_parms->symbols_per_tti*frame_parms->ofdm_symbol_size;
    symbol_offset = subframe_offset + frame_parms->ofdm_symbol_size*l;
#endif
    
#ifdef DEBUG_DRS
    msg("generate_drs_pusch: symbol_offset %d, subframe offset %d, cyclic shift %d\n",symbol_offset,subframe_offset,cyclic_shift);
#endif
    alpha_ind = 0;
    for (rb=0;rb<frame_parms->N_RB_UL;rb++) {

      if ((rb >= first_rb) && (rb<(first_rb+nb_rb))) {

#ifdef DEBUG_DRS	
	msg("generate_drs_pusch: doing RB %d, re_offset=%d, drs_offset=%d,cyclic shift %d\n",rb,re_offset,drs_offset,cyclic_shift);
#endif

#ifdef IFFT_FPGA_UE
	if (cyclic_shift == 0) {
	  for (k=0;k<12;k++) {
	    if ((ul_ref_sigs[u][v][Msc_RS_idx][drs_offset<<1] >= 0) && (ul_ref_sigs[u][v][Msc_RS_idx][(drs_offset<<1)+1] >= 0)) 
	      txdataF[symbol_offset+re_offset] = (mod_sym_t) 1;
	    else if ((ul_ref_sigs[u][v][Msc_RS_idx][drs_offset<<1] >= 0) && (ul_ref_sigs[u][v][Msc_RS_idx][(drs_offset<<1)+1] < 0)) 
	      txdataF[symbol_offset+re_offset] = (mod_sym_t) 2;
	    else if ((ul_ref_sigs[u][v][Msc_RS_idx][drs_offset<<1] < 0) && (ul_ref_sigs[u][v][Msc_RS_idx][(drs_offset<<1)+1] >= 0)) 
	      txdataF[symbol_offset+re_offset] = (mod_sym_t) 3;
	    else if ((ul_ref_sigs[u][v][Msc_RS_idx][drs_offset<<1] < 0) && (ul_ref_sigs[u][v][Msc_RS_idx][(drs_offset<<1)+1] < 0)) 
	      txdataF[symbol_offset+re_offset] = (mod_sym_t) 4;
	    re_offset++;
	    drs_offset++;
	    if (re_offset >= frame_parms->N_RB_UL*12)
	      re_offset=0;
	  }
	}
	else if(cyclic_shift == 6 ) {
	  for (k=0;k<12;k++) {
	    if(k%2 == 0) {
	      if ((ul_ref_sigs[u][v][Msc_RS_idx][drs_offset<<1] >= 0) && (ul_ref_sigs[u][v][Msc_RS_idx][(drs_offset<<1)+1] >= 0)) 
		txdataF[symbol_offset+re_offset] = (mod_sym_t) 4;
	      else if ((ul_ref_sigs[u][v][Msc_RS_idx][drs_offset<<1] >= 0) && (ul_ref_sigs[u][v][Msc_RS_idx][(drs_offset<<1)+1] < 0)) 
		txdataF[symbol_offset+re_offset] = (mod_sym_t) 3;
	      else if ((ul_ref_sigs[u][v][Msc_RS_idx][drs_offset<<1] < 0) && (ul_ref_sigs[u][v][Msc_RS_idx][(drs_offset<<1)+1] >= 0)) 
		txdataF[symbol_offset+re_offset] = (mod_sym_t) 2;
	      else if ((ul_ref_sigs[u][v][Msc_RS_idx][drs_offset<<1] < 0) && (ul_ref_sigs[u][v][Msc_RS_idx][(drs_offset<<1)+1] < 0)) 
		txdataF[symbol_offset+re_offset] = (mod_sym_t) 1;
	    }
	    else {
	      if ((ul_ref_sigs[u][v][Msc_RS_idx][drs_offset<<1] >= 0) && (ul_ref_sigs[u][v][Msc_RS_idx][(drs_offset<<1)+1] >= 0)) 
		txdataF[symbol_offset+re_offset] = (mod_sym_t) 1;
	      else if ((ul_ref_sigs[u][v][Msc_RS_idx][drs_offset<<1] >= 0) && (ul_ref_sigs[u][v][Msc_RS_idx][(drs_offset<<1)+1] < 0)) 
		txdataF[symbol_offset+re_offset] = (mod_sym_t) 2;
	      else if ((ul_ref_sigs[u][v][Msc_RS_idx][drs_offset<<1] < 0) && (ul_ref_sigs[u][v][Msc_RS_idx][(drs_offset<<1)+1] >= 0)) 
		txdataF[symbol_offset+re_offset] = (mod_sym_t) 3;
	      else if ((ul_ref_sigs[u][v][Msc_RS_idx][drs_offset<<1] < 0) && (ul_ref_sigs[u][v][Msc_RS_idx][(drs_offset<<1)+1] < 0)) 
		txdataF[symbol_offset+re_offset] = (mod_sym_t) 4;
	    }
	    
	    re_offset++;
	    drs_offset++;
	    if (re_offset >= frame_parms->N_RB_UL*12)
	      re_offset=0;
	  }
	}
#else  //IFFT_FPGA_UE

	for (k=0;k<12;k++) {
	  ref_re = (int32_t) ul_ref_sigs[u][v][Msc_RS_idx][drs_offset<<1];
	  ref_im = (int32_t) ul_ref_sigs[u][v][Msc_RS_idx][(drs_offset<<1)+1];

	  ((int16_t*) txdataF)[2*(symbol_offset + re_offset)]   = (int16_t) (((ref_re*alpha_re[alpha_ind]) - 
								      (ref_im*alpha_im[alpha_ind]))>>15);
	  ((int16_t*) txdataF)[2*(symbol_offset + re_offset)+1] = (int16_t) (((ref_re*alpha_im[alpha_ind]) + 
								      (ref_im*alpha_re[alpha_ind]))>>15);
	  ((short*) txdataF)[2*(symbol_offset + re_offset)]   = (short) ((((short*) txdataF)[2*(symbol_offset + re_offset)]*(int32_t)amp)>>15);
	  ((short*) txdataF)[2*(symbol_offset + re_offset)+1] = (short) ((((short*) txdataF)[2*(symbol_offset + re_offset)+1]*(int32_t)amp)>>15);

	  
	  alpha_ind = (alpha_ind + cyclic_shift);
	  if (alpha_ind > 11)
	    alpha_ind-=12;
      
#ifdef DEBUG_DRS
	  msg("symbol_offset %d, alpha_ind %d , re_offset %d : (%d,%d)\n",
	      symbol_offset,
	      alpha_ind,
	      re_offset,
	      ((short*) txdataF)[2*(symbol_offset + re_offset)],
	      ((short*) txdataF)[2*(symbol_offset + re_offset)+1]);
	  
#endif  // DEBUG_DRS
	  re_offset++;
	  drs_offset++;
	  if (re_offset >= frame_parms->ofdm_symbol_size)
	    re_offset = 0;
	}
    
#endif // IFFT_FPGA_UE
      }
      else {
	re_offset+=12; // go to next RB
	
	// check if we crossed the symbol boundary and skip DC
#ifdef IFFT_FPGA_UE
	if (re_offset >= frame_parms->N_RB_DL*12) {
	  if (frame_parms->N_RB_DL&1)  // odd number of RBs 
	    re_offset=6;
	  else                         // even number of RBs (doesn't straddle DC)
	    re_offset=0;  
	}
#else
	if (re_offset >= frame_parms->ofdm_symbol_size) {
	  if (frame_parms->N_RB_DL&1)  // odd number of RBs 
	    re_offset=6;
	  else                         // even number of RBs (doesn't straddle DC)
	    re_offset=0;  
	}
#endif
      }
    }
  }
  return(0);
}