From ff7ac327cd2d7a1b77a3e9b59d4bcdb8b34b3e43 Mon Sep 17 00:00:00 2001
From: Francesco Mani <francesco.mani@eurecom.fr>
Date: Wed, 5 Aug 2020 12:00:34 +0200
Subject: [PATCH] first version of pusch power control

---
 cmake_targets/CMakeLists.txt                  |  2 +-
 ...djust_sync_gNB.c => nr_measurements_gNB.c} | 36 ++++++++-
 openair1/PHY/NR_ESTIMATION/nr_ul_estimation.h |  2 +
 .../PHY/NR_TRANSPORT/nr_ulsch_demodulation.c  | 15 ++--
 openair1/PHY/defs_gNB.h                       | 12 ++-
 openair1/SCHED_NR/phy_procedures_nr_gNB.c     | 76 ++++++++++++++++++-
 openair2/COMMON/rrc_messages_types.h          |  5 +-
 openair2/GNB_APP/RRC_nr_paramsvalues.h        |  2 +
 openair2/GNB_APP/gnb_config.c                 |  7 ++
 openair2/LAYER2/NR_MAC_gNB/config.c           |  5 +-
 .../LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c |  6 +-
 .../NR_MAC_gNB/gNB_scheduler_primitives.c     | 12 +++
 .../LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c   | 11 ++-
 openair2/LAYER2/NR_MAC_gNB/mac_proto.h        |  5 +-
 openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h       |  4 +
 openair2/RRC/NR/nr_rrc_defs.h                 |  1 +
 openair2/RRC/NR/rrc_gNB.c                     |  2 +
 openair2/RRC/NR/rrc_gNB_nsa.c                 |  1 +
 .../GENERIC-LTE-EPC/CONF/testing_gnb.conf     |  3 +-
 19 files changed, 179 insertions(+), 28 deletions(-)
 rename openair1/PHY/NR_ESTIMATION/{nr_adjust_sync_gNB.c => nr_measurements_gNB.c} (63%)

diff --git a/cmake_targets/CMakeLists.txt b/cmake_targets/CMakeLists.txt
index 7e59f38e8d8..495c3c8fcec 100644
--- a/cmake_targets/CMakeLists.txt
+++ b/cmake_targets/CMakeLists.txt
@@ -1547,7 +1547,7 @@ set(PHY_SRC_UE
   ${OPENAIR1_DIR}/PHY/NR_REFSIG/ptrs_nr.c
   ${OPENAIR1_DIR}/PHY/NR_UE_ESTIMATION/filt16a_32.c
   ${OPENAIR1_DIR}/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c
-  ${OPENAIR1_DIR}/PHY/NR_ESTIMATION/nr_adjust_sync_gNB.c
+  ${OPENAIR1_DIR}/PHY/NR_ESTIMATION/nr_measurements_gNB.c
   ${OPENAIR1_DIR}/PHY/TOOLS/file_output.c
   ${OPENAIR1_DIR}/PHY/TOOLS/cadd_vv.c
   #${OPENAIR1_DIR}/PHY/TOOLS/lte_dfts.c
diff --git a/openair1/PHY/NR_ESTIMATION/nr_adjust_sync_gNB.c b/openair1/PHY/NR_ESTIMATION/nr_measurements_gNB.c
similarity index 63%
rename from openair1/PHY/NR_ESTIMATION/nr_adjust_sync_gNB.c
rename to openair1/PHY/NR_ESTIMATION/nr_measurements_gNB.c
index 9c4d1fcc93b..6671b1d1e48 100644
--- a/openair1/PHY/NR_ESTIMATION/nr_adjust_sync_gNB.c
+++ b/openair1/PHY/NR_ESTIMATION/nr_measurements_gNB.c
@@ -19,7 +19,7 @@
  *      contact@openairinterface.org
  */
 
-/*! \file PHY/NR_ESTIMATION/nr_adjust_sync_gNB.c
+/*! \file PHY/NR_ESTIMATION/nr_measurements_gNB.c
 * \brief TA estimation for TA updates
 * \author Ahmed Hussein
 * \date 2019
@@ -69,3 +69,37 @@ int nr_est_timing_advance_pusch(PHY_VARS_gNB* gNB, int UE_id)
   return max_pos - sync_pos;
 }
 
+
+void gNB_I0_measurements(PHY_VARS_gNB *gNB) {
+
+  NR_DL_FRAME_PARMS *frame_parms = &gNB->frame_parms;
+  NR_gNB_COMMON *common_vars = &gNB->common_vars;
+  PHY_MEASUREMENTS_gNB *measurements = &gNB->measurements;
+  uint32_t *rb_mask = gNB->rb_mask_ul;
+  int symbol = gNB->ulmask_symb;
+  int rb, offset, nb_rb;
+  uint32_t n0_power_tot, n0_subband_power_temp=0;
+  int32_t *ul_ch;
+
+  if (symbol>-1) {
+    n0_power_tot = 0;
+    for (int aarx=0; aarx<frame_parms->nb_antennas_rx; aarx++) {
+      nb_rb = 0;
+      for (rb=0; rb<frame_parms->N_RB_UL; rb++) {
+        if ((rb_mask[rb>>5]&(1<<(rb&31))) == 0) {  // check that rb was not used in this subframe
+          nb_rb++;
+          offset = (frame_parms->first_carrier_offset + (rb*12))%frame_parms->ofdm_symbol_size;
+          offset += (symbol*frame_parms->ofdm_symbol_size);
+          ul_ch  = &common_vars->rxdataF[aarx][offset];
+          //TODO what about DC?
+          n0_subband_power_temp += signal_energy_nodc(ul_ch,12);
+        }
+      }
+      measurements->n0_subband_power[aarx] = n0_subband_power_temp/nb_rb;
+      measurements->n0_subband_power_dB[aarx] = dB_fixed(measurements->n0_subband_power[aarx]);
+      n0_power_tot += measurements->n0_subband_power[aarx];
+    }
+    //measurements->n0_subband_power_tot_dB = dB_fixed(n0_power_tot);
+  }
+}
+
diff --git a/openair1/PHY/NR_ESTIMATION/nr_ul_estimation.h b/openair1/PHY/NR_ESTIMATION/nr_ul_estimation.h
index b1fd412ebf1..17d8c998c9c 100644
--- a/openair1/PHY/NR_ESTIMATION/nr_ul_estimation.h
+++ b/openair1/PHY/NR_ESTIMATION/nr_ul_estimation.h
@@ -47,6 +47,8 @@ int nr_pusch_channel_estimation(PHY_VARS_gNB *gNB,
                                 unsigned short bwp_start_subcarrier,
                                 nfapi_nr_pusch_pdu_t *pusch_pdu);
 
+void gNB_I0_measurements(PHY_VARS_gNB *gNB);
+
 int nr_est_timing_advance_pusch(PHY_VARS_gNB* phy_vars_gNB, int UE_id);
 
 #endif
diff --git a/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c b/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c
index 0375ce5c4e1..74df3c943ec 100644
--- a/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c
+++ b/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c
@@ -1077,7 +1077,7 @@ int nr_rx_pusch(PHY_VARS_gNB *gNB,
   //--------------------- Channel estimation ---------------------
   //----------------------------------------------------------
   start_meas(&gNB->ulsch_channel_estimation_stats);
-  if (dmrs_symbol_flag == 1)
+  if (dmrs_symbol_flag == 1) {
     nr_pusch_channel_estimation(gNB,
                                 nr_tti_rx,
                                 0, // p
@@ -1085,6 +1085,14 @@ int nr_rx_pusch(PHY_VARS_gNB *gNB,
                                 ulsch_id,
                                 bwp_start_subcarrier,
                                 rel15_ul);
+
+    for (aarx = 0; aarx < frame_parms->nb_antennas_rx; aarx++) {
+      gNB->pusch_vars[ulsch_id]->ulsch_power[aarx] = signal_energy(&gNB->pusch_vars[ulsch_id]->ul_ch_estimates[aarx][symbol*frame_parms->ofdm_symbol_size],
+                                                                   rel15_ul->rb_size*12);
+      if (gNB->pusch_vars[ulsch_id]->ulsch_power[aarx]==1) return (1);
+    }
+
+  }
   stop_meas(&gNB->ulsch_channel_estimation_stats);
   //----------------------------------------------------------
   //--------------------- RBs extraction ---------------------
@@ -1145,11 +1153,6 @@ int nr_rx_pusch(PHY_VARS_gNB *gNB,
     stop_meas(&gNB->ulsch_channel_compensation_stats);
 
 
-    int rxsig = signal_energy(&gNB->pusch_vars[ulsch_id]->rxdataF_comp[0][(symbol*rel15_ul->rb_size*12)],
-                              rel15_ul->rb_size*12);
-
-    if (rxsig==1) return (rxsig);
-
 #ifdef NR_SC_FDMA
     nr_idft(&((uint32_t*)gNB->pusch_vars[ulsch_id]->rxdataF_ext[0])[symbol * rel15_ul->rb_size * NR_NB_SC_PER_RB], nb_re_pusch);
 #endif
diff --git a/openair1/PHY/defs_gNB.h b/openair1/PHY/defs_gNB.h
index e2cdebff3ad..d8d076789f1 100644
--- a/openair1/PHY/defs_gNB.h
+++ b/openair1/PHY/defs_gNB.h
@@ -588,10 +588,10 @@ typedef struct {
   unsigned short n0_power_tot_dB;
   //! estimated avg noise power (dB)
   short n0_power_tot_dBm;
-  //! estimated avg noise power per RB per RX ant (lin)
-  unsigned short n0_subband_power[MAX_NUM_RU_PER_gNB][275];
-  //! estimated avg noise power per RB per RX ant (dB)
-  unsigned short n0_subband_power_dB[MAX_NUM_RU_PER_gNB][275];
+  //! estimated avg noise power per RX ant (lin)
+  unsigned short n0_subband_power[MAX_NUM_RU_PER_gNB];
+  //! estimated avg noise power per RX ant (dB)
+  unsigned short n0_subband_power_dB[MAX_NUM_RU_PER_gNB];
   //! estimated avg noise power per RB (dB)
   short n0_subband_power_tot_dB[275];
   //! estimated avg noise power per RB (dBm)
@@ -715,6 +715,10 @@ typedef struct PHY_VARS_gNB_s {
   /// PUSCH DMRS
   uint32_t ****nr_gold_pusch_dmrs;
 
+  // Mask of occupied RBs
+  uint32_t rb_mask_ul[9];
+  int ulmask_symb;
+
   /// Indicator set to 0 after first SR
   uint8_t first_sr[NUMBER_OF_NR_SR_MAX];
 
diff --git a/openair1/SCHED_NR/phy_procedures_nr_gNB.c b/openair1/SCHED_NR/phy_procedures_nr_gNB.c
index 38ec74eeeb8..486ef4fc6ba 100644
--- a/openair1/SCHED_NR/phy_procedures_nr_gNB.c
+++ b/openair1/SCHED_NR/phy_procedures_nr_gNB.c
@@ -310,10 +310,12 @@ void nr_fill_indication(PHY_VARS_gNB *gNB, int frame, int slot_rx, int ULSCH_id,
   if (timing_advance_update < 0)  timing_advance_update = 0;
   if (timing_advance_update > 63) timing_advance_update = 63;
 
-  LOG_D(PHY, "Estimated timing advance PUSCH is  = %d, timing_advance_update is %d \n", sync_pos,timing_advance_update);
+  LOG_I(PHY, "Estimated timing advance PUSCH is  = %d, timing_advance_update is %d \n", sync_pos,timing_advance_update);
 
   // estimate UL_CQI for MAC (from antenna port 0 only)
-  int SNRtimes10 = dB_fixed_times10(gNB->pusch_vars[ULSCH_id]->ulsch_power[0]) - 300;//(10*gNB->measurements.n0_power_dB[0]);
+  int SNRtimes10 = dB_fixed_times10(gNB->pusch_vars[ULSCH_id]->ulsch_power[0]) - (10*gNB->measurements.n0_subband_power_dB[0]);
+
+  LOG_I(PHY, "Estimated SNR for PUSCH is = %d dB\n", SNRtimes10/10);
 
   if      (SNRtimes10 < -640) cqi=0;
   else if (SNRtimes10 >  635) cqi=255;
@@ -359,6 +361,71 @@ void nr_fill_indication(PHY_VARS_gNB *gNB, int frame, int slot_rx, int ULSCH_id,
   pthread_mutex_unlock(&gNB->UL_INFO_mutex);
 }
 
+// Function to fill UL RB mask to be used for N0 measurements
+void fill_ul_rb_mask(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx) {
+
+  int rb2, rb, nb_rb;
+  for (int symbol=0;symbol<14;symbol++) {
+
+    nb_rb = 0;
+    for (int m=0;m<9;m++) gNB->rb_mask_ul[m] = 0;
+    gNB->ulmask_symb = -1;
+
+    for (int i=0;i<NUMBER_OF_NR_PUCCH_MAX;i++){
+      NR_gNB_PUCCH_t *pucch = gNB->pucch[i];
+      if (pucch) {
+        if ((pucch->active == 1) &&
+	    (pucch->frame == frame_rx) &&
+	    (pucch->slot == slot_rx) ) {
+
+          nfapi_nr_pucch_pdu_t  *pucch_pdu = &pucch[i].pucch_pdu;
+          if ((symbol>=pucch_pdu->start_symbol_index) &&
+              (symbol<(pucch_pdu->start_symbol_index + pucch_pdu->nr_of_symbols))){
+            for (rb=0; rb<pucch_pdu->prb_size; rb++) {
+              rb2 = rb+pucch_pdu->prb_start;
+              gNB->rb_mask_ul[rb2>>5] |= (1<<(rb2&31));
+            }
+            nb_rb+=pucch_pdu->prb_size;
+            gNB->ulmask_symb = symbol;
+          }
+        }
+      }
+    }
+    for (int ULSCH_id=0;ULSCH_id<NUMBER_OF_NR_ULSCH_MAX;ULSCH_id++) {
+      NR_gNB_ULSCH_t *ulsch = gNB->ulsch[ULSCH_id][0];
+      int harq_pid;
+      NR_UL_gNB_HARQ_t *ulsch_harq;
+
+      if ((ulsch) &&
+          (ulsch->rnti > 0)) {
+        for (harq_pid=0;harq_pid<NR_MAX_ULSCH_HARQ_PROCESSES;harq_pid++) {
+          ulsch_harq = ulsch->harq_processes[harq_pid];
+          AssertFatal(ulsch_harq!=NULL,"harq_pid %d is not allocated\n",harq_pid);
+          if ((ulsch_harq->status == NR_ACTIVE) &&
+            (ulsch_harq->frame == frame_rx) &&
+            (ulsch_harq->slot == slot_rx) &&
+            (ulsch_harq->handled == 0)){
+            uint8_t symbol_start = ulsch_harq->ulsch_pdu.start_symbol_index;
+            uint8_t symbol_end = symbol_start + ulsch_harq->ulsch_pdu.nr_of_symbols;
+            if ((symbol>=symbol_start) &&
+                (symbol<symbol_end)){
+              for (rb=0; rb<ulsch_harq->ulsch_pdu.rb_size; rb++) {
+                rb2 = rb+ulsch_harq->ulsch_pdu.rb_start;
+                gNB->rb_mask_ul[rb2>>5] |= (1<<(rb2&31));
+              }
+              nb_rb+=ulsch_harq->ulsch_pdu.rb_size;
+              gNB->ulmask_symb = symbol;
+            }
+          }
+        }
+      }
+      //TODO Add check for PRACH as well?
+    }
+    if (nb_rb<gNB->frame_parms.N_RB_UL)
+      return;
+  }
+}
+
 void phy_procedures_gNB_common_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx) {
 
   uint8_t symbol;
@@ -385,6 +452,9 @@ void phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx)
   VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_PROCEDURES_gNB_UESPEC_RX,1);
   LOG_D(PHY,"phy_procedures_gNB_uespec_RX frame %d, slot %d\n",frame_rx,slot_rx);
 
+  fill_ul_rb_mask(gNB, frame_rx, slot_rx);
+  gNB_I0_measurements(gNB);
+
   for (int i=0;i<NUMBER_OF_NR_PUCCH_MAX;i++){
     NR_gNB_PUCCH_t *pucch = gNB->pucch[i];
     if (pucch) {
@@ -431,7 +501,7 @@ void phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx)
         (ulsch->rnti > 0)) {
       // for for an active HARQ process
       for (harq_pid=0;harq_pid<NR_MAX_ULSCH_HARQ_PROCESSES;harq_pid++) {
-	    ulsch_harq = ulsch->harq_processes[harq_pid];
+	ulsch_harq = ulsch->harq_processes[harq_pid];
     	AssertFatal(ulsch_harq!=NULL,"harq_pid %d is not allocated\n",harq_pid);
     	if ((ulsch_harq->status == NR_ACTIVE) &&
           (ulsch_harq->frame == frame_rx) &&
diff --git a/openair2/COMMON/rrc_messages_types.h b/openair2/COMMON/rrc_messages_types.h
index 7582de65ba4..ebd5fb70d4d 100644
--- a/openair2/COMMON/rrc_messages_types.h
+++ b/openair2/COMMON/rrc_messages_types.h
@@ -403,8 +403,9 @@ typedef struct NRRrcConfigurationReq_s {
   uint16_t                mnc[PLMN_LIST_MAX_SIZE];
   uint8_t                 mnc_digit_length[PLMN_LIST_MAX_SIZE];
   NR_ServingCellConfigCommon_t *scc;
-  int                          ssb_SubcarrierOffset;
-  int                          pdsch_AntennaPorts;
+  int                     ssb_SubcarrierOffset;
+  int                     pdsch_AntennaPorts;
+  int                     pusch_TargetSNRx10;
 } gNB_RrcConfigurationReq;
 
 
diff --git a/openair2/GNB_APP/RRC_nr_paramsvalues.h b/openair2/GNB_APP/RRC_nr_paramsvalues.h
index bc4c4cf9d94..793d059ed7f 100644
--- a/openair2/GNB_APP/RRC_nr_paramsvalues.h
+++ b/openair2/GNB_APP/RRC_nr_paramsvalues.h
@@ -40,6 +40,7 @@
 #define GNB_CONFIG_STRING_GNB_LIST                              "gNBs"
 #define GNB_CONFIG_STRING_SSBSUBCARRIEROFFSET                   "ssb_SubcarrierOffset"
 #define GNB_CONFIG_STRING_PDSCHANTENNAPORTS                     "pdsch_AntennaPorts"
+#define GNB_CONFIG_STRING_PUSCHTARGETPOWX10                     "pusch_TargetSNRx10"
 #define GNB_CONFIG_STRING_SERVINGCELLCONFIGCOMMON               "servingCellConfigCommon"
 #define GNB_CONFIG_STRING_PHYSCELLID                            "physCellId"
 #define GNB_CONFIG_STRING_NTIMINGADVANCEOFFSET                  "n_TimingAdvanceOffset"
@@ -225,6 +226,7 @@
 
 #define SSBPARAMS_DESC {{GNB_CONFIG_STRING_SSBSUBCARRIEROFFSET,NULL,0,iptr:&ssb_SubcarrierOffset,defintval:0,TYPE_INT,0}}
 #define PDSCHANTENNAPARAMS_DESC {{GNB_CONFIG_STRING_PDSCHANTENNAPORTS,NULL,0,iptr:&pdsch_AntennaPorts,defintval:1,TYPE_INT,0}}
+#define TARGETPOWER_DESC {{GNB_CONFIG_STRING_PUSCHTARGETPOWX10,NULL,0,iptr:&pusch_TargetSNRx10,defintval:200,TYPE_INT,0}}
 
 #define SCCPARAMS_DESC(scc) { \
 {GNB_CONFIG_STRING_PHYSCELLID,NULL,0,i64ptr:scc->physCellId,defint64val:0,TYPE_INT64,0/*0*/}, \
diff --git a/openair2/GNB_APP/gnb_config.c b/openair2/GNB_APP/gnb_config.c
index 61aa10e47b7..2ef536eae71 100644
--- a/openair2/GNB_APP/gnb_config.c
+++ b/openair2/GNB_APP/gnb_config.c
@@ -522,6 +522,7 @@ void RCconfig_NRRRC(MessageDef *msg_p, uint32_t i, gNB_RRC_INST *rrc) {
   NR_ServingCellConfigCommon_t *scc = calloc(1,sizeof(NR_ServingCellConfigCommon_t));
   int ssb_SubcarrierOffset = 31;
   int pdsch_AntennaPorts = 1;
+  int pusch_TargetSNRx10 = 200;
   uint64_t ssb_bitmap=0xff;
   memset((void*)scc,0,sizeof(NR_ServingCellConfigCommon_t));
   prepare_scc(scc);
@@ -531,6 +532,8 @@ void RCconfig_NRRRC(MessageDef *msg_p, uint32_t i, gNB_RRC_INST *rrc) {
   paramlist_def_t SSBsParamList = {GNB_CONFIG_STRING_SSBSUBCARRIEROFFSET, NULL, 0};
   paramdef_t PDSCHANTENNAParams[] = PDSCHANTENNAPARAMS_DESC;
   paramlist_def_t PDSCHANTENNAParamList = {GNB_CONFIG_STRING_PDSCHANTENNAPORTS, NULL, 0};
+  paramdef_t PUSCHTARGETPOWParams[] = TARGETPOWER_DESC;
+  paramlist_def_t PUSCHTARGETPOWParamList = {GNB_CONFIG_STRING_PDSCHANTENNAPORTS, NULL, 0};
    ////////// Physical parameters
 
 
@@ -585,6 +588,8 @@ void RCconfig_NRRRC(MessageDef *msg_p, uint32_t i, gNB_RRC_INST *rrc) {
     config_getlist(&PDSCHANTENNAParamList, NULL, 0, aprefix);
     if (PDSCHANTENNAParamList.numelt > 0) config_get(PDSCHANTENNAParams,sizeof(PDSCHANTENNAParams)/sizeof(paramdef_t),aprefix);
 
+    config_getlist(&PUSCHTARGETPOWParamList, NULL, 0, aprefix);
+    if (PUSCHTARGETPOWParamList.numelt > 0) config_get(PUSCHTARGETPOWParams,sizeof(PUSCHTARGETPOWParams)/sizeof(paramdef_t),aprefix);
 
     config_getlist(&SCCsParamList, NULL, 0, aprefix);
     if (SCCsParamList.numelt > 0) {    
@@ -668,6 +673,8 @@ void RCconfig_NRRRC(MessageDef *msg_p, uint32_t i, gNB_RRC_INST *rrc) {
 	NRRRC_CONFIGURATION_REQ (msg_p).ssb_SubcarrierOffset = ssb_SubcarrierOffset;
         printf("pdsch_AntennaPorts %d\n",pdsch_AntennaPorts);
 	NRRRC_CONFIGURATION_REQ (msg_p).pdsch_AntennaPorts = pdsch_AntennaPorts;
+        printf("pusch_TargetSNRx10 %d\n",pusch_TargetSNRx10);
+	NRRRC_CONFIGURATION_REQ (msg_p).pusch_TargetSNRx10 = pusch_TargetSNRx10;
 	NRRRC_CONFIGURATION_REQ (msg_p).scc = scc;	   
 	  
       }//
diff --git a/openair2/LAYER2/NR_MAC_gNB/config.c b/openair2/LAYER2/NR_MAC_gNB/config.c
index 36b81927f80..3cfc71afacb 100644
--- a/openair2/LAYER2/NR_MAC_gNB/config.c
+++ b/openair2/LAYER2/NR_MAC_gNB/config.c
@@ -328,11 +328,11 @@ void config_common(int Mod_idP, int pdsch_AntennaPorts, NR_ServingCellConfigComm
 int rrc_mac_config_req_gNB(module_id_t Mod_idP, 
 			   int ssb_SubcarrierOffset,
                            int pdsch_AntennaPorts,
+                           int pusch_tgt_snrx10,
                            NR_ServingCellConfigCommon_t *scc,
 			   int add_ue,
 			   uint32_t rnti,
-			   NR_CellGroupConfig_t *secondaryCellGroup
-                           ){
+			   NR_CellGroupConfig_t *secondaryCellGroup){
 
   if (scc != NULL ) {
     AssertFatal((scc->ssb_PositionsInBurst->present > 0) && (scc->ssb_PositionsInBurst->present < 4), "SSB Bitmap type %d is not valid\n",scc->ssb_PositionsInBurst->present);
@@ -353,6 +353,7 @@ int rrc_mac_config_req_gNB(module_id_t Mod_idP,
       }
     }
   
+    RC.nrmac[Mod_idP]->pusch_target_snrx10 = pusch_tgt_snrx10;
   
     NR_PHY_Config_t phycfg;
     phycfg.Mod_id = Mod_idP;
diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c
index c421fa1b070..7a549c6f254 100644
--- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c
+++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_phytest.c
@@ -477,7 +477,7 @@ void config_uldci(NR_BWP_Uplink_t *ubwp,
                   nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu_rel15,
                   dci_pdu_rel15_t *dci_pdu_rel15,
                   int *dci_formats, int *rnti_types,
-                  int time_domain_assignment,
+                  int time_domain_assignment, uint8_t tpc,
                   int n_ubwp, int bwp_id) {
 
   switch(dci_formats[(pdcch_pdu_rel15->numDlDci)-1]) {
@@ -519,7 +519,7 @@ void config_uldci(NR_BWP_Uplink_t *ubwp,
       // mcs
       dci_pdu_rel15->mcs = pusch_pdu->mcs_index;
       // tpc command for pusch
-      dci_pdu_rel15->tpc = 1; //TODO
+      dci_pdu_rel15->tpc = tpc;
       // SRS resource indicator
       if (ubwp->bwp_Dedicated->pusch_Config->choice.setup->txConfig != NULL) {
         if (*ubwp->bwp_Dedicated->pusch_Config->choice.setup->txConfig == NR_PUSCH_Config__txConfig_codebook)
@@ -1101,7 +1101,7 @@ void schedule_fapi_ul_pdu(int Mod_idP,
     }
     else {
       dci_pdu_rel15_t *dci_pdu_rel15 = calloc(MAX_DCI_CORESET,sizeof(dci_pdu_rel15_t));
-      config_uldci(ubwp,pusch_pdu,pdcch_pdu_rel15,&dci_pdu_rel15[0],dci_formats,rnti_types,time_domain_assignment,n_ubwp,bwp_id);
+      config_uldci(ubwp,pusch_pdu,pdcch_pdu_rel15,&dci_pdu_rel15[0],dci_formats,rnti_types,time_domain_assignment,UE_list->UE_sched_ctrl[UE_id].tpc0,n_ubwp,bwp_id);
       fill_dci_pdu_rel15(scc,secondaryCellGroup,pdcch_pdu_rel15,dci_pdu_rel15,dci_formats,rnti_types,pusch_pdu->bwp_size,bwp_id);
       free(dci_pdu_rel15);
     }
diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c
index 809bf30c8a0..8e71ef087e7 100644
--- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c
+++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c
@@ -1485,6 +1485,18 @@ int add_new_nr_ue(module_id_t mod_idP, rnti_t rntiP){
   return -1;
 }
 
+
+uint8_t nr_get_tpc(int target, uint8_t cqi, int incr) {
+  // al values passed to this function are x10
+
+  int snrx10 = (cqi*5) - 640;
+  if (snrx10 > target + incr) return 0; // decrease 1dB
+  if (snrx10 < target - incr) return 2; // increase 1dB
+  if (snrx10 < target - (3*incr)) return 3; // increase 3dB
+  return 1; // no change
+}
+
+
 void get_pdsch_to_harq_feedback(int Mod_idP,
                                 int UE_id,
                                 NR_SearchSpace__searchSpaceType_PR ss_type,
diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c
index 89c3fe69fe0..7fe0d63efa0 100644
--- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c
+++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c
@@ -276,6 +276,7 @@ void nr_rx_sdu(const module_id_t gnb_mod_idP,
   UE_id = find_nr_UE_id(gnb_mod_idP, current_rnti);
   gNB_mac = RC.nrmac[gnb_mod_idP];
   UE_list = &gNB_mac->UE_list;
+  int target_snrx10 = gNB_mac->pusch_target_snrx10;
 
   if (UE_id != -1) {
     UE_scheduling_control = &(UE_list->UE_sched_ctrl[UE_id]);
@@ -291,17 +292,19 @@ void nr_rx_sdu(const module_id_t gnb_mod_idP,
           ul_cqi);
 
 #if defined(ENABLE_MAC_PAYLOAD_DEBUG)
-  LOG_I(MAC, "Printing received UL MAC payload at gNB side: %d \n");
-  for (int i = 0; i < sdu_lenP ; i++) {
+    LOG_I(MAC, "Printing received UL MAC payload at gNB side: %d \n");
+    for (int i = 0; i < sdu_lenP ; i++) {
 	  //harq_process_ul_ue->a[i] = (unsigned char) rand();
 	  //printf("a[%d]=0x%02x\n",i,harq_process_ul_ue->a[i]);
 	  printf("%02x ",(unsigned char)sduP[i]);
-  }
-  printf("\n");
+    }
+    printf("\n");
 #endif
 
     if (sduP != NULL){
+      UE_scheduling_control->tpc0 = nr_get_tpc(target_snrx10,ul_cqi,30);
       UE_scheduling_control->ta_update = timing_advance;
+      LOG_I(MAC, "[UE %d] PUSCH TPC %d and TA %d\n",UE_id,UE_scheduling_control->tpc0,UE_scheduling_control->ta_update);
       LOG_D(MAC, "Received PDU at MAC gNB \n");
       nr_process_mac_pdu(gnb_mod_idP, CC_idP, frameP, sduP, sdu_lenP);
     }
diff --git a/openair2/LAYER2/NR_MAC_gNB/mac_proto.h b/openair2/LAYER2/NR_MAC_gNB/mac_proto.h
index a033363e655..e1afd926f9a 100644
--- a/openair2/LAYER2/NR_MAC_gNB/mac_proto.h
+++ b/openair2/LAYER2/NR_MAC_gNB/mac_proto.h
@@ -50,6 +50,7 @@ void config_common(int Mod_idP,
 int rrc_mac_config_req_gNB(module_id_t Mod_idP, 
 			   int ssb_SubcarrierOffset,
                            int pdsch_AntennaPorts,
+                           int tgt_snrx10,
                            NR_ServingCellConfigCommon_t *scc,
 			   int nsa_flag,
 			   uint32_t rnti,
@@ -137,7 +138,7 @@ void config_uldci(NR_BWP_Uplink_t *ubwp,
                   nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdcch_pdu_rel15,
                   dci_pdu_rel15_t *dci_pdu_rel15,
                   int *dci_formats, int *rnti_types,
-                  int time_domain_assignment,
+                  int time_domain_assignment, uint8_t tpc,
                   int n_ubwp, int bwp_id);
 
 void configure_fapi_dl_Tx(module_id_t Mod_idP,
@@ -228,6 +229,8 @@ void find_aggregation_candidates(uint8_t *aggregation_level,
                                  uint8_t *nr_of_candidates,
                                  NR_SearchSpace_t *ss);
 
+uint8_t nr_get_tpc(int target, uint8_t cqi, int incr);
+
 int get_spf(nfapi_nr_config_request_scf_t *cfg);
 
 int to_absslot(nfapi_nr_config_request_scf_t *cfg,int frame,int slot);
diff --git a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h
index 35d62a17fb4..457b9bbdfe4 100644
--- a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h
+++ b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h
@@ -288,6 +288,8 @@ typedef struct {
   NR_sched_pusch *sched_pusch;
   uint16_t ta_timer;
   int16_t ta_update;
+  uint8_t tpc0;
+  uint8_t tpc1;
   uint8_t current_harq_pid;
   NR_UE_harq_t harq_processes[NR_MAX_NB_HARQ_PROCESSES];
   NR_UE_ul_harq_t ul_harq_processes[NR_MAX_NB_HARQ_PROCESSES];
@@ -335,6 +337,8 @@ typedef struct gNB_MAC_INST_s {
   NR_TAG_t                        *tag;
   /// Pointer to IF module instance for PHY
   NR_IF_Module_t                  *if_inst;
+  /// Pusch target SNR
+  int                             pusch_target_snrx10;
   /// TA command
   int                             ta_command;
   /// MAC CE flag indicating TA length
diff --git a/openair2/RRC/NR/nr_rrc_defs.h b/openair2/RRC/NR/nr_rrc_defs.h
index eeb16c30874..b8f38dff734 100644
--- a/openair2/RRC/NR/nr_rrc_defs.h
+++ b/openair2/RRC/NR/nr_rrc_defs.h
@@ -379,6 +379,7 @@ typedef struct {
   NR_BCCH_BCH_Message_t                     mib;
   int ssb_SubcarrierOffset;                  
   int pdsch_AntennaPorts;
+  int pusch_TargetSNRx10;
   NR_BCCH_DL_SCH_Message_t                  *siblock1;
   NR_ServingCellConfigCommon_t              *servingcellconfigcommon;
   NR_CellGroupConfig_t                      *secondaryCellGroup[MAX_NR_RRC_UE_CONTEXTS];
diff --git a/openair2/RRC/NR/rrc_gNB.c b/openair2/RRC/NR/rrc_gNB.c
index 1f341cfdf9c..b6e1f9da141 100644
--- a/openair2/RRC/NR/rrc_gNB.c
+++ b/openair2/RRC/NR/rrc_gNB.c
@@ -186,6 +186,7 @@ static void init_NR_SI(gNB_RRC_INST *rrc) {
   rrc_mac_config_req_gNB(rrc->module_id,
                          rrc->carrier.ssb_SubcarrierOffset,
                          rrc->carrier.pdsch_AntennaPorts,
+                         rrc->carrier.pusch_TargetSNRx10,
                          (NR_ServingCellConfigCommon_t *)rrc->carrier.servingcellconfigcommon,
                          0,
                          0, // WIP hardcoded rnti
@@ -265,6 +266,7 @@ char openair_rrc_gNB_configuration(const module_id_t gnb_mod_idP, gNB_RrcConfigu
   rrc->carrier.servingcellconfigcommon = configuration->scc;
   rrc->carrier.ssb_SubcarrierOffset = configuration->ssb_SubcarrierOffset;
   rrc->carrier.pdsch_AntennaPorts = configuration->pdsch_AntennaPorts;
+  rrc->carrier.pusch_TargetSNRx10 = configuration->pusch_TargetSNRx10;
   /// System Information INIT
   LOG_I(NR_RRC, PROTOCOL_NR_RRC_CTXT_FMT" Checking release \n",PROTOCOL_NR_RRC_CTXT_ARGS(&ctxt));
   init_NR_SI(rrc);
diff --git a/openair2/RRC/NR/rrc_gNB_nsa.c b/openair2/RRC/NR/rrc_gNB_nsa.c
index 22e31aa48ab..7f5641fd0ba 100644
--- a/openair2/RRC/NR/rrc_gNB_nsa.c
+++ b/openair2/RRC/NR/rrc_gNB_nsa.c
@@ -238,6 +238,7 @@ void rrc_add_nsa_user(gNB_RRC_INST *rrc,struct rrc_gNB_ue_context_s *ue_context_
   rrc_mac_config_req_gNB(rrc->module_id,
                          rrc->carrier.ssb_SubcarrierOffset,
                          rrc->carrier.pdsch_AntennaPorts,
+                         rrc->carrier.pusch_TargetSNRx10,
                          NULL,
                          1, // add_ue flag
                          ue_context_p->ue_id_rnti,
diff --git a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/testing_gnb.conf b/targets/PROJECTS/GENERIC-LTE-EPC/CONF/testing_gnb.conf
index c8062fab47c..24172547a00 100644
--- a/targets/PROJECTS/GENERIC-LTE-EPC/CONF/testing_gnb.conf
+++ b/targets/PROJECTS/GENERIC-LTE-EPC/CONF/testing_gnb.conf
@@ -23,7 +23,8 @@ gNBs =
 
     ssb_SubcarrierOffset                                      = 31; //0;
     pdsch_AntennaPorts                                        = 1;
-	
+    pusch_TargetSNRx10                                        = 200;
+
     servingCellConfigCommon = (
     {
  #spCellConfigCommon
-- 
GitLab