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

**********************************************************************/
/*! \file PHY/LTE_TRANSPORT/npbch_NB_IoT.c
* \Fucntions for the generation of broadcast channel (NPBCH) for NB_IoT,	 TS 36-212, V13.4.0 2017-02
* \author M. KANJ
* \date 2017
* \version 0.0
* \company bcom
* \email: matthieu.kanj@b-com.com
* \note
* \warning
*/

#include "PHY/defs.h"
#include "PHY/CODING/extern.h"
#include "PHY/CODING/lte_interleaver_inline.h"
#include "defs.h"
#include "extern.h"
#include "PHY/extern.h"
#include "PHY/sse_intrin.h"

#ifdef PHY_ABSTRACTION
#include "SIMULATION/TOOLS/defs.h"
#endif

#ifdef OPENAIR2
#include "PHY_INTERFACE/defs.h"
#endif

#define NPBCH_A 34                             // 34 for NB-IoT and 24 for LTE

int allocate_npbch_REs_in_RB(NB_IOT_DL_FRAME_PARMS *frame_parms,
                            int32_t **txdataF,
                            uint32_t *jj,
                            uint32_t symbol_offset,
                            uint8_t *x0,
                            uint8_t pilots,
                            int16_t amp,
			    unsigned short id_offset,
                            uint32_t *re_allocated)  //  not used variable ??!!
{
  MIMO_mode_t mimo_mode = (frame_parms->mode1_flag==1)?SISO:ALAMOUTI;
  uint32_t tti_offset,aa;
  uint8_t re, diff_re;
  int16_t gain_lin_QPSK;
  uint8_t first_re,last_re;
  int32_t tmp_sample1,tmp_sample2;
  gain_lin_QPSK = (int16_t)((amp*ONE_OVER_SQRT2_Q15)>>15);
  first_re=0;
  last_re=12;

  for (re=first_re; re<last_re; re++) {      		// re varies between 0 and 12 sub-carriers

    tti_offset = symbol_offset + re;				// symbol_offset = 512 * L ,  re_offset = 512 - 3*12  , re
	
	if (pilots != 1 || re%3 != id_offset)  			// if re is not a pilot
	{
													//	diff_re = re%3 - id_offset;  
      if (mimo_mode == SISO) {  								//SISO mapping
        *re_allocated = *re_allocated + 1;						// variable incremented but never used
			for (aa=0; aa<frame_parms->nb_antennas_tx; aa++) {
					((int16_t*)&txdataF[aa][tti_offset])[0] += (x0[*jj]==1) ? (-gain_lin_QPSK) : gain_lin_QPSK; //I //b_i
			}
			*jj = *jj + 1;
			for (aa=0; aa<frame_parms->nb_antennas_tx; aa++) {
				((int16_t*)&txdataF[aa][tti_offset])[1] += (x0[*jj]==1) ? (-gain_lin_QPSK) : gain_lin_QPSK; //Q //b_{i+1}
			}
			*jj = *jj + 1;	
      } else if (mimo_mode == ALAMOUTI) {
			*re_allocated = *re_allocated + 1;

			((int16_t*)&tmp_sample1)[0] = (x0[*jj]==1) ? (-gain_lin_QPSK) : gain_lin_QPSK;
			*jj=*jj+1;
			((int16_t*)&tmp_sample1)[1] = (x0[*jj]==1) ? (-gain_lin_QPSK) : gain_lin_QPSK;
			*jj=*jj+1;

			// second antenna position n -> -x1*

			((int16_t*)&tmp_sample2)[0] = (x0[*jj]==1) ? (gain_lin_QPSK) : -gain_lin_QPSK;
			*jj=*jj+1;
			((int16_t*)&tmp_sample2)[1] = (x0[*jj]==1) ? (-gain_lin_QPSK) : gain_lin_QPSK;
			*jj=*jj+1;

			// normalization for 2 tx antennas
			((int16_t*)&txdataF[0][tti_offset])[0] += (int16_t)((((int16_t*)&tmp_sample1)[0]*ONE_OVER_SQRT2_Q15)>>15);
			((int16_t*)&txdataF[0][tti_offset])[1] += (int16_t)((((int16_t*)&tmp_sample1)[1]*ONE_OVER_SQRT2_Q15)>>15);
			((int16_t*)&txdataF[1][tti_offset])[0] += (int16_t)((((int16_t*)&tmp_sample2)[0]*ONE_OVER_SQRT2_Q15)>>15);
			((int16_t*)&txdataF[1][tti_offset])[1] += (int16_t)((((int16_t*)&tmp_sample2)[1]*ONE_OVER_SQRT2_Q15)>>15);

			// fill in the rest of the ALAMOUTI precoding
			if ( pilots != 1 || (re+1)%3 != id_offset) {
				((int16_t *)&txdataF[0][tti_offset+1])[0] += -((int16_t *)&txdataF[1][tti_offset])[0]; //x1
				((int16_t *)&txdataF[0][tti_offset+1])[1] += ((int16_t *)&txdataF[1][tti_offset])[1];
				((int16_t *)&txdataF[1][tti_offset+1])[0] += ((int16_t *)&txdataF[0][tti_offset])[0];  //x0*
				((int16_t *)&txdataF[1][tti_offset+1])[1] += -((int16_t *)&txdataF[0][tti_offset])[1];
			} else {
				((int16_t *)&txdataF[0][tti_offset+2])[0] += -((int16_t *)&txdataF[1][tti_offset])[0]; //x1
				((int16_t *)&txdataF[0][tti_offset+2])[1] += ((int16_t *)&txdataF[1][tti_offset])[1];
				((int16_t *)&txdataF[1][tti_offset+2])[0] += ((int16_t *)&txdataF[0][tti_offset])[0];  //x0*
				((int16_t *)&txdataF[1][tti_offset+2])[1] += -((int16_t *)&txdataF[0][tti_offset])[1];
		
				re++;														// skip pilots
				*re_allocated = *re_allocated + 1;
			}
			re++;  															// adjacent carriers are taken care of by precoding
			*re_allocated = *re_allocated + 1;   							// incremented variable but never used
		}
    }
  }

  return(0);
}
/**********************************************************
**********************************************************/
int generate_npbch(NB_IoT_eNB_NPBCH *eNB_npbch,
                  int32_t **txdataF,
                  int amp,
                  NB_IOT_DL_FRAME_PARMS *frame_parms,
                  uint8_t *npbch_pdu,
                  uint8_t frame_mod64
				  unsigned short NB_IoT_RB_ID)
{
  int i, l;
  uint32_t npbch_D,npbch_E;
  uint8_t npbch_a[5];   							// 34/8 =4.25 => 4 bytes and 2 bits
  uint8_t RCC;
  unsigned short bandwidth_even_odd;
  unsigned short NB_IoT_start, RB_IoT_ID;
  uint32_t nsymb = 14;
  uint32_t pilots;
  uint32_t second_pilot = 4;
  uint32_t jj=0;
  uint32_t re_allocated=0;
  uint32_t rb, symbol_offset;
  uint16_t amask=0;
  npbch_D  = 16+NPBCH_A;
  npbch_E  = 1600; 									
									
	if (frame_mod64==0) {
		bzero(npbch_a,5);      									// initializing input data stream , filling with zeros
		bzero(eNB_npbch->npbch_e,pbch_E);						// filling with "0" the table pbch_e[1600]
		memset(eNB_npbch->npbch_d,LTE_NULL,96);					// filling with "2" the first 96 elements of table pbch_d[216]
		
		for (i=0; i<5; i++) 									// set input bits stream
		{	
			npbch_a[5-i-1] = npbch_pdu[i];        				// & 0xc0   //??/** in LTE 24 bits with 3 bytes, but in NB_IoT 34 bits will require 4 bytes+2 bits !! to check
		}
	
		if (frame_parms->mode1_flag == 1)						// setting CRC mask depending on the number of used eNB antennas 
			amask = 0x0000;
		else {
			switch (frame_parms->nb_antennas_tx_eNB) {			// *****???? better replacing nb_antennas_tx_eNB by nb_antennas_tx_eNB_NB_IoT
				case 1:
					amask = 0x0000;
				break;
				case 2:
					amask = 0xffff;
				break;
			}
		}
		
		ccode_encode_NB_IoT(NPBCH_A,2,npbch_a,eNB_npbch->npbch_d+96,amask);						// step 1 CRC Attachment
		RCC = sub_block_interleaving_cc_NB_IoT(npbch_D,eNB_npbch->npbch_d+96,eNB_npbch->npbch_w);   	// step 2 Channel Coding
		lte_rate_matching_cc_NB_IoT(RCC,npbch_E,eNB_npbch->npbch_w,eNB_npbch->npbch_e);				// step 3 Rate Matching
		npbch_scrambling(frame_parms,															// step 4 Scrambling
						eNB_npbch->npbch_e,
						npbch_E);

	}
	// testing if the total number of RBs is even or odd 
		bandwidth_even_odd = frame_parms->N_RB_DL % 2; 	 	// 0 even, 1 odd
		RB_IoT_ID = NB_IoT_RB_ID;
	// step  5, 6, 7   									 	// modulation and mapping (slot 1, symbols 0..3)
	for (l=3; l<14; l++) { 								 	// loop on OFDM symbols
			
		if((l>=4 && l<=8) || (l>=11 && l<=13))
		{
			pilots =1;
		} else {
			pilots=0;
		}
		
		id_offset = frame_parms->Nid_cell % 3;    		// Cell_ID_NB_IoT % 3
		
		if(RB_IoT_ID < (frame_parms->N_RB_DL/2))
		{
			NB_IoT_start = frame_parms->ofdm_symbol_size - 12*(frame_parms->N_RB_DL/2) - (bandwidth_even_odd*6) + 12*(RB_IoT_ID%(ceil(frame_parms->N_RB_DL/(float)2)));
		} else {
			NB_IoT_start = (bandwidth_even_odd*6) + 12*(RB_IoT_ID%(ceil(frame_parms->N_RB_DL/(float)2)));
		}
		
		symbol_offset = frame_parms->ofdm_symbol_size*l + NB_IoT_start;  						// symbol_offset = 512 * L + NB_IOT_RB start
		
		allocate_npbch_REs_in_RB(frame_parms,
								txdataF,
								&jj,
								symbol_offset,
								&eNB_npbch->npbch_e[(frame_mod64/8)*(npbch_E>>3)],
								pilots,
								amp,
								id_offset,
								&re_allocated);
			
	
	}
return(0);
}
/**********************************************************
**********************************************************/
void npbch_scrambling(NB_IOT_DL_FRAME_PARMS *frame_parms,
                     uint8_t *npbch_e,
                     uint32_t length)  // 1600
{
  int i;
  uint8_t reset;
  uint32_t x1, x2, s=0;
  reset = 1;
  x2 = frame_parms->Nid_cell;
  for (i=0; i<length; i++) {
    if ((i&0x1f)==0) {
      s = lte_gold_generic_NB_IoT(&x1, &x2, reset);
      reset = 0;
    }
    npbch_e[i] = (npbch_e[i]&1) ^ ((s>>(i&0x1f))&1);
  }
}