diff --git a/ci-scripts/conf_files/gnb.sa.band254.u0.25prb.rfsim.ntn.conf b/ci-scripts/conf_files/gnb.sa.band254.u0.25prb.rfsim.ntn.conf
index a9256ebfa1f4af40e6e45f15ab09384dafa20400..1d0bba606dad18cd01ce62803edd74cbde5539cb 100644
--- a/ci-scripts/conf_files/gnb.sa.band254.u0.25prb.rfsim.ntn.conf
+++ b/ci-scripts/conf_files/gnb.sa.band254.u0.25prb.rfsim.ntn.conf
@@ -70,7 +70,7 @@ gNBs =
         initialULBWPsubcarrierSpacing                               = 0;
       #rach-ConfigCommon
         #rach-ConfigGeneric
-          prach_ConfigurationIndex                                  = 98;
+          prach_ConfigurationIndex                                  = 7;
 #prach_msg1_FDM
 #0 = one, 1=two, 2=four, 3=eight
           prach_msg1_FDM                                            = 0;
@@ -96,11 +96,11 @@ gNBs =
         rsrp_ThresholdSSB                                           = 19;
 #prach-RootSequenceIndex_PR
 #1 = 839, 2 = 139
-        prach_RootSequenceIndex_PR                                  = 2;
+        prach_RootSequenceIndex_PR                                  = 1;
         prach_RootSequenceIndex                                     = 1;
         # SCS for msg1, can only be 15 for 30 kHz < 6 GHz, takes precendence over the one derived from prach-ConfigIndex
-        #
-        msg1_SubcarrierSpacing                                      = 0;
+        # not allowed when format < 4
+        # msg1_SubcarrierSpacing                                      = 0;
 # restrictedSetConfig
 # 0=unrestricted, 1=restricted type A, 2=restricted type B
         restrictedSetConfig                                         = 0;
diff --git a/common/utils/LOG/DOC/rtusage.md b/common/utils/LOG/DOC/rtusage.md
index 7d5bd94b8c11c69a6038c36338e90a0e17c8da9f..c1cd959137879a0d1a15298f547ca82a6a29241f 100644
--- a/common/utils/LOG/DOC/rtusage.md
+++ b/common/utils/LOG/DOC/rtusage.md
@@ -19,8 +19,11 @@ The following options can be specified to trigger the information added in the h
 - `thread_id`: add the thread ID
 - `function`: add the function name
 - `line_num`: adds the (source code) line number
-- `time`: add the time since process started
-- `wall_clock`: add the system-wide clock time that measures real (i.e., wall-clock) time (`time` and `wall_clock` are mutually exclusive)
+- `time`: add the time since the system started in format `ss.ssssss` (seconds and microseconds sourced from `CLOCK_MONOTONIC`)
+- `wall_clock`: add the system-wide clock time that measures real (i.e., wall-clock) time in format `ss.ssssss` (seconds and microseconds since 1970-01-01 00:00:00 Coordinated Universal Time (UTC))
+- `utc_time`: add the UTC (Coordinated Universal Time) time in format `YYYY-MM-DD hh:mm:ss.ssssss UTC`. Note that this time is independent of the current time zone (it shows GMT). Also, printing this time has additional overhead compared to other time methods (due to time conversion and formatting).
+
+Note: `time`, `utc_time` and `wall_clock` are mutually exclusive and cannot be used together.
 
 ### Component specific parameters
 | name | type | default | description |
diff --git a/common/utils/LOG/log.c b/common/utils/LOG/log.c
index b8349fb35855af7d1cf89f0e46c56cf84552551a..148da232aeaa3774c04bcceb0139c5dbc469667f 100644
--- a/common/utils/LOG/log.c
+++ b/common/utils/LOG/log.c
@@ -103,7 +103,8 @@ static const unsigned int FLAG_FILE_LINE = 1 << 4;
 static const unsigned int FLAG_TIME = 1 << 5;
 static const unsigned int FLAG_THREAD_ID = 1 << 6;
 static const unsigned int FLAG_REAL_TIME = 1 << 7;
-static const unsigned int FLAG_INITIALIZED = 1 << 8;
+static const unsigned int FLAG_UTC_TIME = 1 << 8;
+static const unsigned int FLAG_INITIALIZED = 1 << 9;
 
 /** @}*/
 static mapping log_options[] = {{"nocolor", FLAG_NOCOLOR},
@@ -114,6 +115,7 @@ static mapping log_options[] = {{"nocolor", FLAG_NOCOLOR},
                                       {"time", FLAG_TIME},
                                       {"thread_id", FLAG_THREAD_ID},
                                       {"wall_clock", FLAG_REAL_TIME},
+                                      {"utc_time", FLAG_UTC_TIME},
                                       {NULL, -1}};
 mapping * log_option_names_ptr(void)
 {
@@ -500,8 +502,8 @@ int logInit (void)
     memset(&(g_log->log_component[i]),0,sizeof(log_component_t));
   }
 
-  AssertFatal(!((g_log->flag & FLAG_TIME) && (g_log->flag & FLAG_REAL_TIME)),
-		   "Invalid log options: time and wall_clock both set but are mutually exclusive\n");
+  AssertFatal(__builtin_popcount(g_log->flag & (FLAG_TIME | FLAG_REAL_TIME | FLAG_UTC_TIME)) <= 1,
+          "Invalid log options: time, wall_clock and utc_time are mutually exclusive\n");
 
   g_log->flag =  g_log->flag | FLAG_INITIALIZED;
   return 0;
@@ -545,15 +547,24 @@ static inline int log_header(log_component_t *c,
     l[0] = 0;
 
   // output time information
-  char timeString[32];
-  if ((flag & FLAG_TIME) || (flag & FLAG_REAL_TIME)) {
+  char timeString[64];
+  if ((flag & FLAG_TIME) || (flag & FLAG_REAL_TIME) || (flag & FLAG_UTC_TIME)) {
     struct timespec t;
     const clockid_t clock = flag & FLAG_TIME ? CLOCK_MONOTONIC : CLOCK_REALTIME;
     if (clock_gettime(clock, &t) == -1)
+       abort();
+    if (flag & FLAG_UTC_TIME) {
+      struct tm utc_time;
+      if (gmtime_r(&t.tv_sec, &utc_time) == NULL)
         abort();
-    snprintf(timeString, sizeof(timeString), "%lu.%06lu ",
-             t.tv_sec,
-             t.tv_nsec / 1000);
+      snprintf(timeString, sizeof(timeString), "%04d-%02d-%02d %02d:%02d:%02d.%06lu UTC ",
+               utc_time.tm_year + 1900, utc_time.tm_mon + 1, utc_time.tm_mday,
+               utc_time.tm_hour, utc_time.tm_min, utc_time.tm_sec, t.tv_nsec / 1000);
+    } else {
+      snprintf(timeString, sizeof(timeString), "%lu.%06lu ",
+               t.tv_sec,
+               t.tv_nsec / 1000);
+    }
   } else {
     timeString[0] = 0;
   }
diff --git a/common/utils/nr/nr_common.c b/common/utils/nr/nr_common.c
index 1a2820f236b179397632a4f224f8b8c9598794f9..a3e0dcd5807e6a5f30d018cb1641c79af06923de 100644
--- a/common/utils/nr/nr_common.c
+++ b/common/utils/nr/nr_common.c
@@ -1287,6 +1287,58 @@ int get_scan_ssb_first_sc(const double fc, const int nbRB, const int nrBand, con
   return numGscn;
 }
 
+// Table 38.211 6.3.3.1-1
+static uint8_t long_prach_dur[4] = {1, 3, 4, 1}; // 0.9, 2.28, 3.35, 0.9 ms
+
+uint8_t get_long_prach_dur(unsigned int format, unsigned int mu)
+{
+  AssertFatal(format < 4, "Invalid long PRACH format %d\n", format);
+  const int num_slots_subframe = (1 << mu);
+  const int prach_dur_subframes = long_prach_dur[format];
+  return (prach_dur_subframes * num_slots_subframe);
+}
+
+// Table 38.211 6.3.3.2-1
+uint8_t get_PRACH_k_bar(unsigned int delta_f_RA_PRACH, unsigned int delta_f_PUSCH)
+{
+  uint8_t k_bar = 0;
+  if (delta_f_RA_PRACH > 3) { // Rel 15 max PRACH SCS is 120 kHz, 4 and 5 are 1.25 and 5 kHz
+    // long formats
+    DevAssert(delta_f_PUSCH < 3);
+    DevAssert(delta_f_RA_PRACH < 6);
+    const uint8_t k_bar_table[3][2] = {{7, 12},
+                                       {1, 10},
+                                       {133, 7}};
+
+    k_bar = k_bar_table[delta_f_PUSCH][delta_f_RA_PRACH - 4];
+  } else {
+    if (delta_f_RA_PRACH == 3 && delta_f_PUSCH == 4) // \delta f_RA == 120 kHz AND \delta f == 480 kHz
+      k_bar = 1;
+    else if (delta_f_RA_PRACH == 3 && delta_f_PUSCH == 5) // \delta f_RA == 120 kHz AND \delta f == 960 kHz
+      k_bar = 23;
+    else
+      k_bar = 2;
+  }
+  return k_bar;
+}
+
+// K according to 38.211 5.3.2
+unsigned int get_prach_K(int prach_sequence_length, int prach_fmt_id, int pusch_mu, int prach_mu)
+{
+  unsigned int K = 1;
+  if (prach_sequence_length == 0) {
+    if (prach_fmt_id == 3)
+      K = (15 << pusch_mu) / 5;
+    else
+      K = (15 << pusch_mu) / 1.25;
+  } else if (prach_sequence_length == 1) {
+    K = (15 << pusch_mu) / (15 << prach_mu);
+  } else {
+    AssertFatal(0, "Invalid PRACH sequence length %d\n", prach_sequence_length);
+  }
+  return K;
+}
+
 int get_delay_idx(int delay, int max_delay_comp)
 {
   int delay_idx = max_delay_comp + delay;
diff --git a/common/utils/nr/nr_common.h b/common/utils/nr/nr_common.h
index 6d24f08d45423e2f94626cc839c06926376b7ae5..5d5c6a363cd9c47e8541a8d832c73a9a5177678c 100644
--- a/common/utils/nr/nr_common.h
+++ b/common/utils/nr/nr_common.h
@@ -287,6 +287,9 @@ void check_ssb_raster(uint64_t freq, int band, int scs);
 int get_smallest_supported_bandwidth_index(int scs, frequency_range_t frequency_range, int n_rbs);
 unsigned short get_m_srs(int c_srs, int b_srs);
 unsigned short get_N_b_srs(int c_srs, int b_srs);
+uint8_t get_long_prach_dur(unsigned int format, unsigned int num_slots_subframe);
+uint8_t get_PRACH_k_bar(unsigned int delta_f_RA_PRACH, unsigned int delta_f_PUSCH);
+unsigned int get_prach_K(int prach_sequence_length, int prach_fmt_id, int pusch_mu, int prach_mu);
 
 int get_slot_idx_in_period(const int slot, const frame_structure_t *fs);
 
diff --git a/executables/nr-ru.c b/executables/nr-ru.c
index f3311ecb655f1dc6dc225ff6f0cd597ce5598eab..e665fd70bb8a6788f3d229adcfd970faf59d1da6 100644
--- a/executables/nr-ru.c
+++ b/executables/nr-ru.c
@@ -1308,24 +1308,30 @@ void *ru_thread(void *param)
         if (prach_id >= 0) {
           VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_RU_PRACH_RX, 1 );
 
-          T(T_GNB_PHY_PRACH_INPUT_SIGNAL, T_INT(proc->frame_rx), T_INT(proc->tti_rx), T_INT(0),
-          T_BUFFER(&ru->common.rxdata[0][fp->get_samples_slot_timestamp(proc->tti_rx-1,fp,0)]/*-ru->N_TA_offset*/, fp->get_samples_per_slot(proc->tti_rx,fp)*4*2));
-          int N_dur = get_nr_prach_duration(ru->prach_list[prach_id].fmt);
-
-          for (int prach_oc = 0; prach_oc<ru->prach_list[prach_id].num_prach_ocas; prach_oc++) {
-            int prachStartSymbol = ru->prach_list[prach_id].prachStartSymbol + prach_oc * N_dur;
+          T(T_GNB_PHY_PRACH_INPUT_SIGNAL,
+            T_INT(proc->frame_rx),
+            T_INT(proc->tti_rx),
+            T_INT(0),
+            T_BUFFER(&ru->common.rxdata[0][fp->get_samples_slot_timestamp(proc->tti_rx - 1, fp, 0) /*-ru->N_TA_offset*/],
+                     (fp->get_samples_per_slot(proc->tti_rx - 1, fp) + fp->get_samples_per_slot(proc->tti_rx, fp)) * 4));
+          RU_PRACH_list_t *p = ru->prach_list + prach_id;
+          int N_dur = get_nr_prach_duration(p->fmt);
+
+          for (int prach_oc = 0; prach_oc < p->num_prach_ocas; prach_oc++) {
+            int prachStartSymbol = p->prachStartSymbol + prach_oc * N_dur;
             //comment FK: the standard 38.211 section 5.3.2 has one extra term +14*N_RA_slot. This is because there prachStartSymbol is given wrt to start of the 15kHz slot or 60kHz slot. Here we work slot based, so this function is anyway only called in slots where there is PRACH. Its up to the MAC to schedule another PRACH PDU in the case there are there N_RA_slot \in {0,1}.
             rx_nr_prach_ru(ru,
-                           ru->prach_list[prach_id].fmt, //could also use format
-                           ru->prach_list[prach_id].numRA,
+                           p->fmt, // could also use format
+                           p->numRA,
                            prachStartSymbol,
+                           p->slot,
                            prach_oc,
                            proc->frame_rx,
                            proc->tti_rx);
           }
           free_nr_ru_prach_entry(ru,prach_id);
           VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_RU_PRACH_RX, 0);
-        } // end if (prach_id > 0)
+        } // end if (prach_id >= 0)
       } // end if (ru->feprx)
     } // end if (slot_type == NR_UPLINK_SLOT || slot_type == NR_MIXED_SLOT) {
 
diff --git a/nfapi/open-nFAPI/nfapi/public_inc/fapi_nr_ue_interface.h b/nfapi/open-nFAPI/nfapi/public_inc/fapi_nr_ue_interface.h
index 9679caa18c9c1283fc8940f7413b10b844508db0..881d7fad8e0c60ad347915408cd0393bea71bc92 100644
--- a/nfapi/open-nFAPI/nfapi/public_inc/fapi_nr_ue_interface.h
+++ b/nfapi/open-nFAPI/nfapi/public_inc/fapi_nr_ue_interface.h
@@ -691,6 +691,7 @@ typedef struct
   fapi_nr_num_prach_fd_occasions_t* num_prach_fd_occasions_list;
   uint8_t ssb_per_rach;//SSB-per-RACH-occasion Value: 0: 1/8 1:1/4, 2:1/2 3:1 4:2 5:4, 6:8 7:16
   uint8_t prach_multiple_carriers_in_a_band;//0 = disabled 1 = enabled
+  uint8_t root_seq_computed; // flag set and used only in PHY to indicate if table is computed with this config
 
 } fapi_nr_prach_config_t;
 
diff --git a/openair1/PHY/INIT/nr_init_ue.c b/openair1/PHY/INIT/nr_init_ue.c
index 5a595af8abe74bd9bf9967635cd3fb0f28535c5c..019e386fd0944dc230f3dd97eb5eb402b308930e 100644
--- a/openair1/PHY/INIT/nr_init_ue.c
+++ b/openair1/PHY/INIT/nr_init_ue.c
@@ -271,7 +271,6 @@ int init_nr_ue_signal(PHY_VARS_NR_UE *ue, int nb_connected_gNB)
   }
 
   ue->init_averaging = 1;
-  init_nr_prach_tables(839);
   init_symbol_rotation(fp);
   init_timeshift_rotation(fp);
 
diff --git a/openair1/PHY/NR_TRANSPORT/nr_dlsch.c b/openair1/PHY/NR_TRANSPORT/nr_dlsch.c
index ebb6322029d9105bb0999c782bb899d1ecb5adce..30ce9cdc5e41e09ac41acf2de178e611d4c0273b 100644
--- a/openair1/PHY/NR_TRANSPORT/nr_dlsch.c
+++ b/openair1/PHY/NR_TRANSPORT/nr_dlsch.c
@@ -97,7 +97,7 @@ typedef union {
   c16_t s[2];
 } amp_t;
 
-static inline int interleave_with_0_signal_first(c16_t *output, c16_t *mod_dmrs, const int amp_dmrs, int sz)
+static inline int interleave_with_0_signal_first(c16_t *output, c16_t *mod_dmrs, const int16_t amp_dmrs, int sz)
 {
 #ifdef DEBUG_DLSCH_MAPPING
   printf("doing DMRS pattern for port 0 : d0 0 d1 0 ... dNm2 0 dNm1 0 (ul %d, rr %d)\n", upper_limit, remaining_re);
@@ -149,7 +149,7 @@ static inline int interleave_with_0_signal_first(c16_t *output, c16_t *mod_dmrs,
   return 0;
 }
 
-static inline int interleave_with_0_start_with_0(c16_t *output, c16_t *mod_dmrs, const int amp_dmrs, int sz)
+static inline int interleave_with_0_start_with_0(c16_t *output, c16_t *mod_dmrs, const int16_t amp_dmrs, int sz)
 {
 #ifdef DEBUG_DLSCH_MAPPING
   printf("doing DMRS pattern for port 2 : 0 d0 0 d1 ... 0 dNm2 0 dNm1\n");
@@ -258,7 +258,7 @@ static inline int interleave_signals(c16_t *output, c16_t *signal1, const int am
 static inline int dmrs_case00(c16_t *output,
                               c16_t *txl,
                               c16_t *mod_dmrs,
-                              const int amp_dmrs,
+                              const int16_t amp_dmrs,
                               const int amp,
                               int sz,
                               int start_sc,
@@ -349,7 +349,7 @@ static inline int do_onelayer(NR_DL_FRAME_PARMS *frame_parms,
                               uint16_t dlPtrsSymPos,
                               int n_ptrs,
                               int amp,
-                              int amp_dmrs,
+                              int16_t amp_dmrs,
                               int l_prime,
                               nfapi_nr_dmrs_type_e dmrs_Type,
                               c16_t *dmrs_start)
@@ -559,7 +559,7 @@ static int do_one_dlsch(unsigned char *input_ptr, PHY_VARS_gNB *gNB, NR_gNB_DLSC
   const int symbol_sz=frame_parms->ofdm_symbol_size;
   const int dmrs_Type = rel15->dmrsConfigType;
   const int nb_re_dmrs = rel15->numDmrsCdmGrpsNoData * (rel15->dmrsConfigType == NFAPI_NR_DMRS_TYPE1 ? 6 : 4);
-  const int amp_dmrs = (int)((double)amp * sqrt(rel15->numDmrsCdmGrpsNoData)); // 3GPP TS 38.214 Section 4.1: Table 4.1-1
+  const int16_t amp_dmrs = min((double)amp * sqrt(rel15->numDmrsCdmGrpsNoData), INT16_MAX); // 3GPP TS 38.214 Section 4.1: Table 4.1-1
   LOG_D(PHY,
         "pdsch: BWPStart %d, BWPSize %d, rbStart %d, rbsize %d\n",
         rel15->BWPStart,
diff --git a/openair1/PHY/NR_TRANSPORT/nr_prach.c b/openair1/PHY/NR_TRANSPORT/nr_prach.c
index 9e3df3f2234e8553cd0c5a82469485db3d3d34cc..858257893f8712f558d3d0c6341a485a7593da51 100644
--- a/openair1/PHY/NR_TRANSPORT/nr_prach.c
+++ b/openair1/PHY/NR_TRANSPORT/nr_prach.c
@@ -43,6 +43,7 @@ void init_prach_list(PHY_VARS_gNB *gNB)
     gNB->prach_vars.list[i].frame = -1;
     gNB->prach_vars.list[i].slot = -1;
     gNB->prach_vars.list[i].beam_nb = -1;
+    gNB->prach_vars.list[i].num_slots = -1;
   }
 }
 
@@ -51,24 +52,20 @@ void free_nr_prach_entry(PHY_VARS_gNB *gNB, int prach_id)
   gNB->prach_vars.list[prach_id].frame = -1;
   gNB->prach_vars.list[prach_id].slot = -1;
   gNB->prach_vars.list[prach_id].beam_nb = -1;
+  gNB->prach_vars.list[prach_id].num_slots = -1;
 }
 
 int16_t find_nr_prach(PHY_VARS_gNB *gNB,int frame, int slot, find_type_t type) {
 
   AssertFatal(gNB!=NULL,"gNB is null\n");
   for (uint16_t i=0; i<NUMBER_OF_NR_PRACH_MAX; i++) {
-    LOG_D(PHY,"searching for PRACH in %d.%d prach_index %d=> %d.%d\n", frame,slot,i,
-	  gNB->prach_vars.list[i].frame,gNB->prach_vars.list[i].slot);
-		if((type == SEARCH_EXIST_OR_FREE) &&
-		  (gNB->prach_vars.list[i].frame == -1) &&
-		  (gNB->prach_vars.list[i].slot == -1)) {
-		  return i;
-		}
-    else if ((type == SEARCH_EXIST) &&
-		  (gNB->prach_vars.list[i].frame == frame) &&
-                  (gNB->prach_vars.list[i].slot  == slot)) {
-		  return i;
-		}
+    gNB_PRACH_list_t *p = gNB->prach_vars.list + i;
+    LOG_D(PHY, "searching for PRACH in %d.%d prach_index %d=> %d.%d\n", frame, slot, i, p->frame, p->slot);
+    if ((type == SEARCH_EXIST_OR_FREE) && (p->frame == -1) && (p->slot == -1)) {
+      return i;
+    } else if ((type == SEARCH_EXIST) && (p->frame == frame) && (p->slot + p->num_slots - 1 == slot)) {
+      return i;
+    }
   }
   return -1;
 }
@@ -80,6 +77,8 @@ void nr_fill_prach(PHY_VARS_gNB *gNB, int SFN, int Slot, nfapi_nr_prach_pdu_t *p
   gNB_PRACH_list_t *prach = &gNB->prach_vars.list[prach_id];
   prach->frame = SFN;
   prach->slot = Slot;
+  const int format = prach_pdu->prach_format;
+  prach->num_slots = (format < 4) ? get_long_prach_dur(format, gNB->frame_parms.numerology_index) : 1;
   prach->beam_nb = 0;
   if (gNB->common_vars.beam_id) {
     int fapi_beam_idx = prach_pdu->beamforming.prgs_list[0].dig_bf_interface_list[0].beam_idx;
@@ -98,6 +97,7 @@ void init_prach_ru_list(RU_t *ru)
   for (int i = 0; i < NUMBER_OF_NR_RU_PRACH_MAX; i++) {
     ru->prach_list[i].frame = -1;
     ru->prach_list[i].slot = -1;
+    ru->prach_list[i].num_slots = -1;
   }
   pthread_mutex_init(&ru->prach_list_mutex, NULL);
 }
@@ -107,16 +107,12 @@ int16_t find_nr_prach_ru(RU_t *ru,int frame,int slot, find_type_t type)
   AssertFatal(ru != NULL, "ru is null\n");
   pthread_mutex_lock(&ru->prach_list_mutex);
   for (int i = 0; i < NUMBER_OF_NR_RU_PRACH_MAX; i++) {
-    LOG_D(PHY,"searching for PRACH in %d.%d : prach_index %d=> %d.%d\n",
-          frame,
-          slot,
-          i,
-	  ru->prach_list[i].frame,ru->prach_list[i].slot);
-    if((type == SEARCH_EXIST_OR_FREE) && (ru->prach_list[i].frame == -1) && (ru->prach_list[i].slot == -1)) {
+    RU_PRACH_list_t *p = ru->prach_list + i;
+    LOG_D(PHY, "searching for PRACH in %d.%d : prach_index %d=> %d.%d\n", frame, slot, i, p->frame, p->slot);
+    if ((type == SEARCH_EXIST_OR_FREE) && (p->frame == -1) && (p->slot == -1)) {
       pthread_mutex_unlock(&ru->prach_list_mutex);
       return i;
-    }	
-    else if ((type == SEARCH_EXIST) && (ru->prach_list[i].frame == frame) && (ru->prach_list[i].slot  == slot)) {
+    } else if ((type == SEARCH_EXIST) && (p->frame == frame) && (p->slot + p->num_slots - 1 == slot)) {
       pthread_mutex_unlock(&ru->prach_list_mutex);
       return i;
     }
@@ -135,7 +131,9 @@ void nr_fill_prach_ru(RU_t *ru, int SFN, int Slot, nfapi_nr_prach_pdu_t *prach_p
   pthread_mutex_lock(&ru->prach_list_mutex);
   ru->prach_list[prach_id].frame = SFN;
   ru->prach_list[prach_id].slot = Slot;
-  ru->prach_list[prach_id].fmt = prach_pdu->prach_format;
+  const int fmt = prach_pdu->prach_format;
+  ru->prach_list[prach_id].num_slots = (fmt < 4) ? get_long_prach_dur(fmt, ru->nr_frame_parms->numerology_index) : 1;
+  ru->prach_list[prach_id].fmt = fmt;
   ru->prach_list[prach_id].numRA = prach_pdu->num_ra;
   ru->prach_list[prach_id].prachStartSymbol = prach_pdu->prach_start_symbol;
   ru->prach_list[prach_id].num_prach_ocas = prach_pdu->num_prach_ocas;
@@ -150,8 +148,14 @@ void free_nr_ru_prach_entry(RU_t *ru, int prach_id)
   pthread_mutex_unlock(&ru->prach_list_mutex);
 }
 
-
-void rx_nr_prach_ru(RU_t *ru, int prachFormat, int numRA, int prachStartSymbol, int prachOccasion, int frame, int slot)
+void rx_nr_prach_ru(RU_t *ru,
+                    int prachFormat,
+                    int numRA,
+                    int prachStartSymbol,
+                    int prachStartSlot,
+                    int prachOccasion,
+                    int frame,
+                    int slot)
 {
   AssertFatal(ru != NULL,"ru is null\n");
 
@@ -161,6 +165,7 @@ void rx_nr_prach_ru(RU_t *ru, int prachFormat, int numRA, int prachStartSymbol,
   int16_t *prach[ru->nb_rx];
   int prach_sequence_length = ru->config.prach_config.prach_sequence_length.value;
   int msg1_frequencystart = ru->config.prach_config.num_prach_fd_occasions_list[numRA].k1.value;
+  const uint8_t prach_mu = ru->config.prach_config.prach_sub_c_spacing.value;
 
   int sample_offset_slot;
   if (prachStartSymbol == 0) {
@@ -193,7 +198,7 @@ void rx_nr_prach_ru(RU_t *ru, int prachFormat, int numRA, int prachStartSymbol,
 
   for (int aa=0; aa<ru->nb_rx; aa++){ 
     if (prach_sequence_length == 0)
-      slot2 = (slot / fp->slots_per_subframe) * fp->slots_per_subframe;
+      slot2 = prachStartSlot;
     prach[aa] = (int16_t*)&ru->common.rxdata[aa][fp->get_samples_slot_timestamp(slot2, fp, 0) + sample_offset_slot - ru->N_TA_offset];
   } 
 
@@ -387,18 +392,9 @@ void rx_nr_prach_ru(RU_t *ru, int prachFormat, int numRA, int prachStartSymbol,
     LOG_D(PHY, "rx_prach: Doing PRACH FFT for nb_rx:%d Ncp:%d dftlen:%d\n", ru->nb_rx, Ncp, dftlen);
   }
 
-  // Note: Assumes PUSCH SCS @ 30 kHz, take values for formats 0-2 and adjust for others below
-  int kbar = 1;
-  int K    = 24;
-  if (prach_sequence_length == 0 && prachFormat == 3) { 
-    K=4;
-    kbar=10;
-  }
-  else if (prach_sequence_length == 1) {
-    // Note: Assumes that PRACH SCS is same as PUSCH SCS
-    K=1;
-    kbar=2;
-  }
+  const unsigned int K = get_prach_K(prach_sequence_length, prachFormat, fp->numerology_index, prach_mu);
+  const uint8_t kbar = get_PRACH_k_bar(prach_mu, fp->numerology_index);
+
   int n_ra_prb            = msg1_frequencystart;
   int k                   = (12*n_ra_prb) - 6*fp->N_RB_UL;
 
@@ -433,7 +429,6 @@ void rx_nr_prach_ru(RU_t *ru, int prachFormat, int numRA, int prachStartSymbol,
     }
     memcpy((void*)rxsigF2,(void *)rxsigF_tmp,N_ZC<<2);
   }
-
 }
 
 void rx_nr_prach(PHY_VARS_gNB *gNB,
@@ -542,7 +537,8 @@ void rx_nr_prach(PHY_VARS_gNB *gNB,
       }
     } else { // This is the high-speed case
       new_dft = 0;
-      nr_fill_du(N_ZC,prach_root_sequence_map);
+      uint16_t nr_du[NR_PRACH_SEQ_LEN_L - 1];
+      nr_fill_du(N_ZC, prach_root_sequence_map, nr_du);
       // set preamble_offset to initial rootSequenceIndex and look if we need more root sequences for this
       // preamble index and find the corresponding cyclic shift
       // Check if all shifts for that root have been processed
diff --git a/openair1/PHY/NR_TRANSPORT/nr_prach.h b/openair1/PHY/NR_TRANSPORT/nr_prach.h
index ba170e593d0a81a86c19fb59fa178257b28cbdab..cddf5abbbc1259dece80dca7a84e81ac18aa3a6a 100644
--- a/openair1/PHY/NR_TRANSPORT/nr_prach.h
+++ b/openair1/PHY/NR_TRANSPORT/nr_prach.h
@@ -30,9 +30,6 @@
  * \warning
  */
 
-extern c16_t nr_ru[839]; // quantized roots of unity
-extern uint16_t nr_du[838];
-
 static const char* const prachfmt[] = {"0", "1", "2", "3", "A1", "A2", "A3", "B1", "B4", "C0", "C2", "A1/B1", "A2/B2", "A3/B3"};
 
 /*************************************
diff --git a/openair1/PHY/NR_TRANSPORT/nr_prach_common.c b/openair1/PHY/NR_TRANSPORT/nr_prach_common.c
index 95d312293a82fbad04e1cf24ae8fc35ab6c0a859..b680d826faf099df1bad0402e350fad5c6be904e 100644
--- a/openair1/PHY/NR_TRANSPORT/nr_prach_common.c
+++ b/openair1/PHY/NR_TRANSPORT/nr_prach_common.c
@@ -38,12 +38,34 @@
 #include "common/utils/LOG/log.h"
 #include "common/utils/LOG/vcd_signal_dumper.h"
 #include "T.h"
-c16_t nr_ru[839]; // quantized roots of unity
-static uint32_t nr_ZC_inv[839]; // multiplicative inverse for roots u
-uint16_t nr_du[838];
+
+/* Extended Euclidean Algorithm to compute modulo inverse */
+static int modulo_multiplicative_inverse(const int a, const int m)
+{
+  DevAssert(a < m);
+  int t = 0;
+  int newt = 1;
+  int r = m;
+  int newr = a;
+
+  while (newr != 0) {
+    const int q = r / newr;
+    int tmp = t;
+    t = newt;
+    newt = tmp - q * newt;
+    tmp = r;
+    r = newr;
+    newr = tmp - q * newr;
+  }
+
+  AssertFatal(r <= 1, "Modulo inverse doesn't exist for a %d, m %d\n", a, m);
+  t = (t < 0) ? t + m : t;
+  LOG_D(PHY, "Modulo %d inverse of %d is %d\n", m, a, t);
+  return t;
+}
 
 // This function computes the du
-void nr_fill_du(uint16_t N_ZC, const uint16_t *prach_root_sequence_map)
+void nr_fill_du(uint16_t N_ZC, const uint16_t* prach_root_sequence_map, uint16_t nr_du[NR_PRACH_SEQ_LEN_L - 1])
 {
 
   uint16_t iu,u,p;
@@ -64,19 +86,15 @@ void nr_fill_du(uint16_t N_ZC, const uint16_t *prach_root_sequence_map)
 void compute_nr_prach_seq(uint8_t short_sequence, uint8_t num_sequences, uint8_t rootSequenceIndex, c16_t X_u[64][839])
 {
   // Compute DFT of x_u => X_u[k] = x_u(inv(u)*k)^* X_u[k] = exp(j\pi u*inv(u)*k*(inv(u)*k+1)/N_ZC)
-  unsigned int k,inv_u,i;
   int N_ZC;
 
-  const uint16_t *prach_root_sequence_map;
-  uint16_t u;
+  const uint16_t* prach_root_sequence_map;
 
   VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_UE_COMPUTE_PRACH, VCD_FUNCTION_IN);
 
   LOG_D(PHY,"compute_prach_seq: prach short sequence %x, num_sequences %d, rootSequenceIndex %d\n", short_sequence, num_sequences, rootSequenceIndex);
 
-  N_ZC = (short_sequence) ? 139 : 839;
-  
-  init_nr_prach_tables(N_ZC);
+  N_ZC = (short_sequence) ? NR_PRACH_SEQ_LEN_S : NR_PRACH_SEQ_LEN_L;
 
   if (short_sequence) {
     // FIXME cannot be reached
@@ -87,7 +105,7 @@ void compute_nr_prach_seq(uint8_t short_sequence, uint8_t num_sequences, uint8_t
 
   LOG_D( PHY, "compute_prach_seq: done init prach_tables\n" );
 
-  for (i=0; i<num_sequences; i++) {
+  for (int i = 0; i < num_sequences; i++) {
     int index = (rootSequenceIndex+i) % (N_ZC-1);
 
     if (short_sequence) {
@@ -98,58 +116,22 @@ void compute_nr_prach_seq(uint8_t short_sequence, uint8_t num_sequences, uint8_t
       DevAssert( index < sizeof(prach_root_sequence_map_0_3) / sizeof(prach_root_sequence_map_0_3[0]) );
     }
 
-    u = prach_root_sequence_map[index];
+    const uint16_t u = prach_root_sequence_map[index];
     LOG_D(PHY,"prach index %d => u=%d\n",index,u);
-    inv_u = nr_ZC_inv[u]; // multiplicative inverse of u
-
+    const int inv_u = modulo_multiplicative_inverse(u, N_ZC); // multiplicative inverse of u
 
     // X_u[0] stores the first ZC sequence where the root u has a non-zero number of shifts
     // for the unrestricted case X_u[0] is the first root indicated by the rootSequenceIndex
 
-    for (k=0; k<N_ZC; k++) {
+    const int zc_inv_2 = modulo_multiplicative_inverse(2, N_ZC);
+    for (int k = 0; k < N_ZC; k++) {
+      const unsigned int j = (((k * (1 + (inv_u * k))) % N_ZC) * zc_inv_2) % N_ZC;
+      const double w = 2 * M_PI * (double)j / N_ZC;
+      const c16_t ru = {.r = (int16_t)(floor(32767.0 * cos(w))), .i = (int16_t)(floor(32767.0 * sin(w)))};
       // multiply by inverse of 2 (required since ru is exp[j 2\pi n])
-      X_u[i][k] = nr_ru[(((k * (1 + (inv_u * k))) % N_ZC) * nr_ZC_inv[2]) % N_ZC];
+      X_u[i][k] = ru;
     }
   }
 
   VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_UE_COMPUTE_PRACH, VCD_FUNCTION_OUT);
-
-}
-
-void init_nr_prach_tables(int N_ZC)
-{
-
-  int i,m;
-
-  // Compute the modular multiplicative inverse 'iu' of u s.t. iu*u = 1 mod N_ZC
-  nr_ZC_inv[0] = 0;
-  nr_ZC_inv[1] = 1;
-
-  for (i=2; i<N_ZC; i++) {
-    for (m=2; m<N_ZC; m++)
-      if (((i*m)%N_ZC) == 1) {
-        nr_ZC_inv[i] = m;
-        break;
-      }
-
-#ifdef PRACH_DEBUG
-
-    if (i<16)
-      printf("i %d : inv %d\n",i,nr_ZC_inv[i]);
-
-#endif
-  }
-
-  // Compute quantized roots of unity
-  for (i=0; i<N_ZC; i++) {
-    nr_ru[i].r = (int16_t)(floor(32767.0 * cos(2 * M_PI * (double)i / N_ZC)));
-    nr_ru[i].i = (int16_t)(floor(32767.0 * sin(2 * M_PI * (double)i / N_ZC)));
-#ifdef PRACH_DEBUG
-
-    if (i<16)
-      printf("i %d : runity %d,%d\n", i, nr_ru[i].r, nr_ru[i].i);
-
-#endif
-  }
 }
-
diff --git a/openair1/PHY/NR_TRANSPORT/nr_transport_common_proto.h b/openair1/PHY/NR_TRANSPORT/nr_transport_common_proto.h
index 79b3daa212f7719ff25084b579c6da6c59694385..04dbb10cd9926f100fe4ae4fe83ee18291b9f601 100644
--- a/openair1/PHY/NR_TRANSPORT/nr_transport_common_proto.h
+++ b/openair1/PHY/NR_TRANSPORT/nr_transport_common_proto.h
@@ -67,9 +67,7 @@ uint32_t nr_get_E(uint32_t G, uint8_t C, uint8_t Qm, uint8_t Nl, uint8_t r);
 
 void compute_nr_prach_seq(uint8_t short_sequence, uint8_t num_sequences, uint8_t rootSequenceIndex, c16_t X_u[64][839]);
 
-void nr_fill_du(uint16_t N_ZC, const uint16_t *prach_root_sequence_map);
-
-void init_nr_prach_tables(int N_ZC);
+void nr_fill_du(uint16_t N_ZC, const uint16_t *prach_root_sequence_map, uint16_t nr_du[NR_PRACH_SEQ_LEN_L - 1]);
 
 void nr_codeword_scrambling(uint8_t *in,
                             uint32_t size,
@@ -84,4 +82,5 @@ void nr_codeword_unscrambling_init(int16_t *s, uint32_t size, uint8_t q, uint32_
 /**@}*/
 
 void init_pucch2_luts(void);
+void set_prach_tables(int N_ZC, c16_t** ru, uint32_t** zc_inv);
 #endif
diff --git a/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h b/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h
index 6dbf6e565d9826f41dc9fa00448bdc72f3a534b8..c1f769b34c260ee8819cd99d8590c408cf60af89 100644
--- a/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h
+++ b/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h
@@ -275,7 +275,8 @@ void rx_nr_prach_ru(RU_t *ru,
                     int prach_fmt,
                     int numRA,
                     int prachStartSymbol,
-		    int prachOccasion,
+                    int prachStartSlot,
+                    int prachOccasion,
                     int frame,
                     int subframe);
 
diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_prach.c b/openair1/PHY/NR_UE_TRANSPORT/nr_prach.c
index 91f8f81f3c7fcdb4945b5327b584ac102d4302b5..c2953dd1967988df18973aec01cfaf7482c1d536 100644
--- a/openair1/PHY/NR_UE_TRANSPORT/nr_prach.c
+++ b/openair1/PHY/NR_UE_TRANSPORT/nr_prach.c
@@ -63,17 +63,17 @@ int32_t generate_nr_prach(PHY_VARS_NR_UE *ue, uint8_t gNB_id, int frame, uint8_t
   const uint16_t *prach_root_sequence_map;
   uint16_t preamble_shift = 0, preamble_index0, n_shift_ra, n_shift_ra_bar, d_start=INT16_MAX, numshift, N_ZC, u, offset, offset2, first_nonzero_root_idx;
   c16_t prach[(4688 + 4 * 24576) * 2] __attribute__((aligned(32))) = {0};
-  int16_t prachF_tmp[(4688+4*24576)*4*2] __attribute__((aligned(32))) = {0};
+  c16_t prachF_tmp[(4688 + 4 * 24576) * 4] __attribute__((aligned(32))) = {0};
 
   int Ncp = 0;
-  int prach_start, prach_sequence_length, i, prach_len, dftlen, mu, kbar, K, n_ra_prb, k, prachStartSymbol, sample_offset_slot;
+  int prach_start, prach_sequence_length, prach_len, dftlen, mu, n_ra_prb, k, prachStartSymbol, sample_offset_slot;
 
   fd_occasion             = 0;
   prach_len               = 0;
   dftlen                  = 0;
   first_nonzero_root_idx = 0;
-  int16_t amp = ue->prach_vars[gNB_id]->amp;
-  int16_t *prachF = prachF_tmp;
+  int16_t amp = prach_pdu->prach_tx_power;
+  c16_t *prachF = prachF_tmp;
   Mod_id                  = ue->Mod_id;
   prach_sequence_length   = nrUE_config->prach_config.prach_sequence_length;
   N_ZC                    = (prach_sequence_length == 0) ? 839:139;
@@ -83,18 +83,19 @@ int32_t generate_nr_prach(PHY_VARS_NR_UE *ue, uint8_t gNB_id, int frame, uint8_t
   n_ra_prb                = nrUE_config->prach_config.num_prach_fd_occasions_list[fd_occasion].k1,//prach_pdu->freq_msg1;
   NCS                     = prach_pdu->num_cs;
   prach_fmt_id            = prach_pdu->prach_format;
-  preamble_index          = prach_pdu->ra_PreambleIndex;
-  kbar                    = 1;
-  K                       = 24;
+  preamble_index = prach_pdu->ra_PreambleIndex;
   k                       = 12*n_ra_prb - 6*fp->N_RB_UL;
   prachStartSymbol        = prach_pdu->prach_start_symbol;
 
   LOG_D(PHY,"Generate NR PRACH %d.%d\n", frame, slot);
 
-  compute_nr_prach_seq(nrUE_config->prach_config.prach_sequence_length,
-                       nrUE_config->prach_config.num_prach_fd_occasions_list[fd_occasion].num_root_sequences,
-                       nrUE_config->prach_config.num_prach_fd_occasions_list[fd_occasion].prach_root_sequence_index,
-                       ue->X_u);
+  if (nrUE_config->prach_config.root_seq_computed == 0) {
+    compute_nr_prach_seq(nrUE_config->prach_config.prach_sequence_length,
+                         nrUE_config->prach_config.num_prach_fd_occasions_list[fd_occasion].num_root_sequences,
+                         nrUE_config->prach_config.num_prach_fd_occasions_list[fd_occasion].prach_root_sequence_index,
+                         ue->X_u);
+    nrUE_config->prach_config.root_seq_computed = 1;
+  }
 
   if (prachStartSymbol == 0) {
     sample_offset_slot = 0;
@@ -137,7 +138,8 @@ int32_t generate_nr_prach(PHY_VARS_NR_UE *ue, uint8_t gNB_id, int frame, uint8_t
     #endif
 
     not_found = 1;
-    nr_fill_du(N_ZC,prach_root_sequence_map);
+    uint16_t nr_du[NR_PRACH_SEQ_LEN_L - 1];
+    nr_fill_du(N_ZC, prach_root_sequence_map, nr_du);
     preamble_index0 = preamble_index;
     // set preamble_offset to initial rootSequenceIndex and look if we need more root sequences for this
     // preamble index and find the corresponding cyclic shift
@@ -210,20 +212,14 @@ int32_t generate_nr_prach(PHY_VARS_NR_UE *ue, uint8_t gNB_id, int frame, uint8_t
   //  nsymb = (frame_parms->Ncp==0) ? 14:12;
   //  subframe_offset = (unsigned int)frame_parms->ofdm_symbol_size*slot*nsymb;
 
-  if (prach_sequence_length == 0 && prach_fmt_id == 3) {
-    K = 4;
-    kbar = 10;
-  } else if (prach_sequence_length == 1) {
-    K = 1;
-    kbar = 2;
-  }
+  const unsigned int K = get_prach_K(prach_sequence_length, prach_fmt_id, fp->numerology_index, mu);
+  const uint8_t kbar = get_PRACH_k_bar(mu, fp->numerology_index);
 
-  if (k<0)
+  if (k < 0)
     k += fp->ofdm_symbol_size;
 
   k *= K;
   k += kbar;
-  k *= 2;
 
   LOG_I(PHY,
         "PRACH [UE %d] in frame.slot %d.%d, placing PRACH in position %d, Msg1/MsgA-Preamble frequency start %d (k1 %d), "
@@ -231,7 +227,7 @@ int32_t generate_nr_prach(PHY_VARS_NR_UE *ue, uint8_t gNB_id, int frame, uint8_t
         Mod_id,
         frame,
         slot,
-        k,
+        k * 2,
         n_ra_prb,
         nrUE_config->prach_config.num_prach_fd_occasions_list[fd_occasion].k1,
         preamble_offset,
@@ -262,7 +258,7 @@ int32_t generate_nr_prach(PHY_VARS_NR_UE *ue, uint8_t gNB_id, int frame, uint8_t
       break;
 
     default:
-      AssertFatal(1==0, "Illegal PRACH format %d for sequence length 839\n", prach_fmt_id);
+      AssertFatal(1 == 0, "Illegal PRACH format %d for sequence length 839\n", prach_fmt_id);
       break;
     }
   } else {
@@ -413,12 +409,14 @@ int32_t generate_nr_prach(PHY_VARS_NR_UE *ue, uint8_t gNB_id, int frame, uint8_t
 
     if (offset2 >= N_ZC)
       offset2 -= N_ZC;
-    const int32_t Xu_re = (Xu[offset].r * amp) >> 15;
-    const int32_t Xu_im = (Xu[offset].i * amp) >> 15;
-    prachF[k++] = (Xu_re * nr_ru[offset2].r - Xu_im * nr_ru[offset2].i) >> 15;
-    prachF[k++] = (Xu_im * nr_ru[offset2].r + Xu_re * nr_ru[offset2].i) >> 15;
-
-    if (k==dftlen) k=0;
+    const c16_t Xu_t = c16xmulConstShift(Xu[offset], amp, 15);
+    const double w = 2 * M_PI * (double)offset2 / N_ZC;
+    const c16_t ru = {.r = (int16_t)(floor(32767.0 * cos(w))), .i = (int16_t)(floor(32767.0 * sin(w)))};
+    const c16_t p = c16mulShift(Xu_t, ru, 15);
+    prachF[k++] = p;
+
+    if (k * 2 == dftlen)
+      k = 0;
   }
 
   #if defined (PRACH_WRITE_OUTPUT_DEBUG)
@@ -429,7 +427,7 @@ int32_t generate_nr_prach(PHY_VARS_NR_UE *ue, uint8_t gNB_id, int frame, uint8_t
   // This is after cyclic prefix
     c16_t *prach2 = prach + Ncp;
     const idft_size_idx_t idft_size = get_idft(dftlen);
-    idft(idft_size, prachF, (int16_t *)prach, 1);
+    idft(idft_size, (int16_t *)prachF, (int16_t *)prach, 1);
     memmove(prach2, prach, (dftlen << 2));
 
     if (prach_sequence_length == 0) {
@@ -504,21 +502,17 @@ int32_t generate_nr_prach(PHY_VARS_NR_UE *ue, uint8_t gNB_id, int frame, uint8_t
     }
   }
 
-  #ifdef NR_PRACH_DEBUG
-    LOG_I(PHY, "PRACH [UE %d] N_RB_UL %d prach_start %d, prach_len %d\n", Mod_id,
-      fp->N_RB_UL,
-      prach_start,
-      prach_len);
-  #endif
+#ifdef NR_PRACH_DEBUG
+  LOG_I(PHY, "PRACH [UE %d] N_RB_UL %d prach_start %d, prach_len %d\n", Mod_id, fp->N_RB_UL, prach_start, prach_len);
+#endif
 
-    for (i = 0; i < prach_len; i++)
-      ue->common_vars.txData[0][prach_start + i] = prach[i];
+  memcpy(ue->common_vars.txData[0] + prach_start, prach, sizeof(c16_t) * prach_len);
 
 #ifdef PRACH_WRITE_OUTPUT_DEBUG
-    LOG_M("prach_tx0.m", "prachtx0", prach+(Ncp<<1), prach_len-Ncp, 1, 1);
-    LOG_M("Prach_txsig.m","txs",(int16_t*)(&ue->common_vars.txdata[0][prach_start]), 2*(prach_start+prach_len), 1, 1)
-  #endif
+  LOG_M("prach_tx0.m", "prachtx0", prach + (Ncp << 1), prach_len - Ncp, 1, 1);
+  LOG_M("Prach_txsig.m", "txs", (int16_t *)(&ue->common_vars.txdata[0][prach_start]), 2 * (prach_start + prach_len), 1, 1)
+#endif
 
-  return signal_energy((int*)prach, 256);
+  return signal_energy((int *)prach, 256);
 }
 
diff --git a/openair1/PHY/defs_RU.h b/openair1/PHY/defs_RU.h
index 167326e8116c4cb600722c782c48850c439f6565..1417180cb389b4ffc2c725a803702da91b46a6e5 100644
--- a/openair1/PHY/defs_RU.h
+++ b/openair1/PHY/defs_RU.h
@@ -195,6 +195,7 @@ typedef struct {
   int numRA;
   int prachStartSymbol;
   int num_prach_ocas;
+  int num_slots;
 } RU_PRACH_list_t;
 
 #define NUMBER_OF_NR_RU_PRACH_MAX 8
@@ -426,7 +427,6 @@ typedef enum {
   WAIT_RESYNCH     = 3
 } rru_cmd_t;
 
-
 typedef struct RU_t_s {
   /// ThreadPool for RU        
   tpool_t *threadPool;
diff --git a/openair1/PHY/defs_gNB.h b/openair1/PHY/defs_gNB.h
index 7f3e4f12e173634a86a167be5f85e85ad01e3099..eb0a1acc0e92ef4e038acf7145f2ca27075b280e 100644
--- a/openair1/PHY/defs_gNB.h
+++ b/openair1/PHY/defs_gNB.h
@@ -141,6 +141,8 @@ typedef struct {
   int slot;
   // identifier for concurrent beams
   int beam_nb;
+  // prach duration in slots
+  int num_slots;
   nfapi_nr_prach_pdu_t pdu;  
 } gNB_PRACH_list_t;
 
diff --git a/openair1/PHY/defs_nr_UE.h b/openair1/PHY/defs_nr_UE.h
index 172c608d3853cad5273ce8efc9677316530fa831..542c94f63d3854e521a8c748eea400f6c6881138 100644
--- a/openair1/PHY/defs_nr_UE.h
+++ b/openair1/PHY/defs_nr_UE.h
@@ -298,6 +298,7 @@ typedef struct {
 typedef struct {
   int16_t amp;
   bool active;
+  int num_prach_slots;
   fapi_nr_ul_config_prach_pdu prach_pdu;
 } NR_UE_PRACH;
 
diff --git a/openair1/PHY/impl_defs_nr.h b/openair1/PHY/impl_defs_nr.h
index 16d5854604ea327c0f059c9c485c36e971dd33d7..123dbe162a356a478c12129a55c8a483bf2efb8f 100644
--- a/openair1/PHY/impl_defs_nr.h
+++ b/openair1/PHY/impl_defs_nr.h
@@ -56,6 +56,8 @@ static const uint8_t N_slot_subframe[MU_NUMBER] = {1, 2, 4, 8, 16};
 
 #define  NB_DL_DATA_TO_UL_ACK              (8) /* size of table TS 38.213 Table 9.2.3-1 */
 
+#define NR_PRACH_SEQ_LEN_L 839
+#define NR_PRACH_SEQ_LEN_S 139
 
 /***********************************************************************
 *
diff --git a/openair1/PHY/nr_phy_common/inc/nr_ue_phy_meas.h b/openair1/PHY/nr_phy_common/inc/nr_ue_phy_meas.h
index f89320087c0da92e46ccc83991a4f0daca9813ad..09e66e8adf4d910e8b3b11d8fe02784aed61569b 100644
--- a/openair1/PHY/nr_phy_common/inc/nr_ue_phy_meas.h
+++ b/openair1/PHY/nr_phy_common/inc/nr_ue_phy_meas.h
@@ -51,7 +51,8 @@
   FN(ULSCH_RATE_MATCHING_STATS),\
   FN(ULSCH_INTERLEAVING_STATS),\
   FN(ULSCH_ENCODING_STATS),\
-  FN(OFDM_MOD_STATS)
+  FN(OFDM_MOD_STATS),\
+  FN(PRACH_GEN_STATS)
 
 typedef enum {
   FOREACH_NR_PHY_CPU_MEAS(NOOP),
diff --git a/openair1/SCHED_NR/nr_prach_procedures.c b/openair1/SCHED_NR/nr_prach_procedures.c
index 12ec6a0b422a39468880f43280162997daeebde9..ad4204bdee441e48859a017587969a5e916f8309 100644
--- a/openair1/SCHED_NR/nr_prach_procedures.c
+++ b/openair1/SCHED_NR/nr_prach_procedures.c
@@ -111,11 +111,11 @@ void L1_nr_prach_procedures(PHY_VARS_gNB *gNB, int frame, int slot, nfapi_nr_rac
 
   ru=gNB->RU_list[0];
 
-  int prach_id=find_nr_prach(gNB,frame,slot,SEARCH_EXIST);
-
-  if (prach_id>=0) {
+  int prach_id = find_nr_prach(gNB, frame, slot, SEARCH_EXIST);
+  if (prach_id >= 0) {
     LOG_D(NR_PHY,"%d.%d Got prach entry id %d\n",frame,slot,prach_id);
     nfapi_nr_prach_pdu_t *prach_pdu = &gNB->prach_vars.list[prach_id].pdu;
+    const int prach_start_slot = gNB->prach_vars.list[prach_id].slot;
     uint8_t prachStartSymbol;
     uint8_t N_dur = get_nr_prach_duration(prach_pdu->prach_format);
 
@@ -151,7 +151,7 @@ void L1_nr_prach_procedures(PHY_VARS_gNB *gNB, int frame, int slot, nfapi_nr_rac
               "[RAPROC] %d.%d Initiating RA procedure with preamble %d, energy %d.%d dB (I0 %d, thres %d), delay %d start symbol "
               "%u freq index %u\n",
               frame,
-              slot,
+              prach_start_slot,
               max_preamble[0],
               max_preamble_energy[0] / 10,
               max_preamble_energy[0] % 10,
@@ -187,6 +187,7 @@ void L1_nr_prach_procedures(PHY_VARS_gNB *gNB, int frame, int slot, nfapi_nr_rac
       if (frame==0) LOG_I(PHY,"prach_I0 = %d.%d dB\n",gNB->measurements.prach_I0/10,gNB->measurements.prach_I0%10);
       if (gNB->prach_energy_counter < 100) gNB->prach_energy_counter++;
     } //if prach_id>0
-  } //for NUMBER_OF_NR_PRACH_OCCASION_MAX
+    rach_ind->slot = prach_start_slot;
+  } // for NUMBER_OF_NR_PRACH_OCCASION_MAX
   VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_ENB_PRACH_RX,0);
 }
diff --git a/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c b/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c
index 809c534acc30975af0c897c3443d926dadb3dd9f..b57bf4eff34e0ff39ecd95b9b26892c0221a4979 100644
--- a/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c
+++ b/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c
@@ -294,14 +294,19 @@ void phy_procedures_nrUE_TX(PHY_VARS_NR_UE *ue, const UE_nr_rxtx_proc_t *proc, n
   pucch_procedures_ue_nr(ue, proc, phy_data, (c16_t **)&txdataF);
 
   LOG_D(PHY, "Sending Uplink data \n");
-  start_meas_nr_ue_phy(ue, OFDM_MOD_STATS);
-  nr_ue_pusch_common_procedures(ue,
-                                proc->nr_slot_tx,
-                                &ue->frame_parms,
-                                ue->frame_parms.nb_antennas_tx,
-                                (c16_t **)txdataF,
-                                link_type_ul);
-  stop_meas_nr_ue_phy(ue, OFDM_MOD_STATS);
+
+  // Don't do OFDM Mod if txdata contains prach
+  const NR_UE_PRACH *prach_var = ue->prach_vars[proc->gNB_id];
+  if (!prach_var->active) {
+    start_meas_nr_ue_phy(ue, OFDM_MOD_STATS);
+    nr_ue_pusch_common_procedures(ue,
+                                  proc->nr_slot_tx,
+                                  &ue->frame_parms,
+                                  ue->frame_parms.nb_antennas_tx,
+                                  (c16_t **)txdataF,
+                                  link_type_ul);
+    stop_meas_nr_ue_phy(ue, OFDM_MOD_STATS);
+  }
 
   nr_ue_prach_procedures(ue, proc);
 
@@ -1168,36 +1173,57 @@ void nr_ue_prach_procedures(PHY_VARS_NR_UE *ue, const UE_nr_rxtx_proc_t *proc)
 
   VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_PROCEDURES_UE_TX_PRACH, VCD_FUNCTION_IN);
 
-  if (ue->prach_vars[gNB_id]->active) {
-    fapi_nr_ul_config_prach_pdu *prach_pdu = &ue->prach_vars[gNB_id]->prach_pdu;
-    ue->tx_power_dBm[nr_slot_tx] = prach_pdu->prach_tx_power;
-
-    LOG_D(PHY, "In %s: [UE %d][RAPROC][%d.%d]: Generating PRACH Msg1 (preamble %d, P0_PRACH %d)\n",
-          __FUNCTION__,
-          mod_id,
-          frame_tx,
-          nr_slot_tx,
-          prach_pdu->ra_PreambleIndex,
-          ue->tx_power_dBm[nr_slot_tx]);
-
-    ue->prach_vars[gNB_id]->amp = AMP;
-
-    VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_UE_GENERATE_PRACH, VCD_FUNCTION_IN);
-
-    prach_power = generate_nr_prach(ue, gNB_id, frame_tx, nr_slot_tx);
-
-    VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_UE_GENERATE_PRACH, VCD_FUNCTION_OUT);
+  NR_UE_PRACH *prach_var = ue->prach_vars[gNB_id];
+  if (prach_var->active) {
+    fapi_nr_ul_config_prach_pdu *prach_pdu = &prach_var->prach_pdu;
+    // Generate PRACH in first slot. For L839, the following slots are also filled in this slot.
+    if (prach_pdu->prach_slot == nr_slot_tx) {
+      ue->tx_power_dBm[nr_slot_tx] = prach_pdu->prach_tx_power;
+
+      LOG_D(PHY,
+            "In %s: [UE %d][RAPROC][%d.%d]: Generating PRACH Msg1 (preamble %d, P0_PRACH %d)\n",
+            __FUNCTION__,
+            mod_id,
+            frame_tx,
+            nr_slot_tx,
+            prach_pdu->ra_PreambleIndex,
+            ue->tx_power_dBm[nr_slot_tx]);
+
+      prach_var->amp = AMP;
+
+      VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_UE_GENERATE_PRACH, VCD_FUNCTION_IN);
+
+      start_meas_nr_ue_phy(ue, PRACH_GEN_STATS);
+      prach_power = generate_nr_prach(ue, gNB_id, frame_tx, nr_slot_tx);
+      stop_meas_nr_ue_phy(ue, PRACH_GEN_STATS);
+      if (cpumeas(CPUMEAS_GETSTATE)) {
+        LOG_D(PHY,
+              "[SFN %d.%d] PRACH Proc %5.2f\n",
+              proc->frame_tx,
+              proc->nr_slot_tx,
+              ue->phy_cpu_stats.cpu_time_stats[PRACH_GEN_STATS].p_time / (cpuf * 1000.0));
+      }
 
-    LOG_D(PHY, "In %s: [UE %d][RAPROC][%d.%d]: Generated PRACH Msg1 (TX power PRACH %d dBm, digital power %d dBW (amp %d)\n",
-      __FUNCTION__,
-      mod_id,
-      frame_tx,
-      nr_slot_tx,
-      ue->tx_power_dBm[nr_slot_tx],
-      dB_fixed(prach_power),
-      ue->prach_vars[gNB_id]->amp);
+      VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_UE_GENERATE_PRACH, VCD_FUNCTION_OUT);
+
+      LOG_D(PHY,
+            "In %s: [UE %d][RAPROC][%d.%d]: Generated PRACH Msg1 (TX power PRACH %d dBm, digital power %d dBW (amp %d)\n",
+            __FUNCTION__,
+            mod_id,
+            frame_tx,
+            nr_slot_tx,
+            ue->tx_power_dBm[nr_slot_tx],
+            dB_fixed(prach_power),
+            ue->prach_vars[gNB_id]->amp);
+
+      // set duration of prach slots so we know when to skip OFDM modulation
+      const int prach_format = ue->prach_vars[gNB_id]->prach_pdu.prach_format;
+      const int prach_slots = (prach_format < 4) ? get_long_prach_dur(prach_format, ue->frame_parms.numerology_index) : 1;
+      prach_var->num_prach_slots = prach_slots;
+    }
 
-    ue->prach_vars[gNB_id]->active = false;
+    // set as inactive in the last slot
+    prach_var->active = !(nr_slot_tx == (prach_pdu->prach_slot + prach_var->num_prach_slots - 1));
   }
 
   VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_PROCEDURES_UE_TX_PRACH, VCD_FUNCTION_OUT);
diff --git a/openair1/SIMULATION/NR_PHY/prachsim.c b/openair1/SIMULATION/NR_PHY/prachsim.c
index fd2c1bcf773374cc5e5d976973c1e9517979cb0c..ce00f6ae9e761ed51e062d0f6028360498a6ded3 100644
--- a/openair1/SIMULATION/NR_PHY/prachsim.c
+++ b/openair1/SIMULATION/NR_PHY/prachsim.c
@@ -562,6 +562,7 @@ int main(int argc, char **argv){
   prach_config->num_prach_fd_occasions_list[fd_occasion].k1.value                        = msg1_frequencystart;
   prach_config->restricted_set_config.value                                              = restrictedSetConfig;
   prach_config->prach_sequence_length.value                                              = prach_sequence_length;
+  prach_config->prach_sub_c_spacing.value                                                = mu;
   prach_pdu->num_cs                                                                      = get_NCS(NCS_config, format0, restrictedSetConfig);
   prach_config->num_prach_fd_occasions_list[fd_occasion].num_root_sequences.value        = 1+(64/(N_ZC/prach_pdu->num_cs));
   prach_pdu->prach_format                                                                = prach_format;
@@ -593,7 +594,7 @@ int main(int argc, char **argv){
   ue_prach_config        = &UE->nrUE_config.prach_config;
   txdata = UE->common_vars.txData;
 
-  UE->prach_vars[0]->amp        = AMP;
+  ue_prach_pdu->prach_tx_power = AMP;
   ue_prach_pdu->root_seq_id     = rootSequenceIndex;
   ue_prach_pdu->num_cs          = get_NCS(NCS_config, format0, restrictedSetConfig);
   ue_prach_pdu->restricted_set  = restrictedSetConfig;
@@ -774,7 +775,7 @@ int main(int argc, char **argv){
 	  }
 	}
 	
-        rx_nr_prach_ru(ru, prach_format, numRA, prachStartSymbol, prachOccasion, frame, slot);
+        rx_nr_prach_ru(ru, prach_format, numRA, prachStartSymbol, slot, prachOccasion, frame, slot);
 
         for (int i = 0; i < ru->nb_rx; ++i)
           gNB->prach_vars.rxsigF[i] = ru->prach_rxsigF[prachOccasion][i];
diff --git a/openair1/SIMULATION/TOOLS/DOC/channel_simulation.md b/openair1/SIMULATION/TOOLS/DOC/channel_simulation.md
index 30cc68e66262302e28a4f3c88cc542091479d765..e3e3bd3189528865f3dfae558ac379c47c550ea7 100644
--- a/openair1/SIMULATION/TOOLS/DOC/channel_simulation.md
+++ b/openair1/SIMULATION/TOOLS/DOC/channel_simulation.md
@@ -90,7 +90,11 @@ channelmod = {
 };
 ```
 
-where `rfsimu_channel_ue0` will be activated on server side (i.e. eNB/gNB) for uplink and `rfsimu_channel_enB0` will be activated on client side (i.e. UE) for downlink.
+where `rfsimu_channel_ue0` will be activated on server side for uplink and `rfsimu_channel_enB0` will be activated on client side for downlink.
+
+Use `rfsimu_channel_ue1`, `rfsimu_channel_ue2`, etc. if you want to use different channel models for each client. The client connection order determines its channel model.
+
+The server could be either the UE or the gNB, the channel name suffix does not depend on the application but on the rfsimulators role (server/client).
 
 ## Edit the configuration file
 
diff --git a/openair2/GNB_APP/gnb_config.c b/openair2/GNB_APP/gnb_config.c
index b345277aec5333e70f002a4ac3e276a91132a8cd..2ff5ec0da688d7e087480cb394c46c2f9626c045 100644
--- a/openair2/GNB_APP/gnb_config.c
+++ b/openair2/GNB_APP/gnb_config.c
@@ -1014,7 +1014,7 @@ void RCconfig_NR_L1(void)
       gNB->L1_rx_thread_core = *(L1_ParamList.paramarray[j][L1_RX_THREAD_CORE].iptr);
       gNB->L1_tx_thread_core = *(L1_ParamList.paramarray[j][L1_TX_THREAD_CORE].iptr);
       LOG_I(NR_PHY, "L1_RX_THREAD_CORE %d (%d)\n", *(L1_ParamList.paramarray[j][L1_RX_THREAD_CORE].iptr), L1_RX_THREAD_CORE);
-      gNB->TX_AMP = (int16_t)(32767.0 / pow(10.0, .05 * (double)(*L1_ParamList.paramarray[j][L1_TX_AMP_BACKOFF_dB].uptr)));
+      gNB->TX_AMP = min(32767.0 / pow(10.0, .05 * (double)(*L1_ParamList.paramarray[j][L1_TX_AMP_BACKOFF_dB].uptr)), INT16_MAX);
       gNB->phase_comp = *L1_ParamList.paramarray[j][L1_PHASE_COMP].uptr;
       gNB->dmrs_num_antennas_per_thread = *(L1_ParamList.paramarray[j][NUM_ANTENNAS_PER_THREAD].uptr);
       LOG_I(NR_PHY, "TX_AMP = %d (-%d dBFS)\n", gNB->TX_AMP, *L1_ParamList.paramarray[j][L1_TX_AMP_BACKOFF_dB].uptr);
diff --git a/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c b/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c
index ccee11d37e4eba2365c1b9d683c106c9b6132c8e..2c9a2b02c085def14db7bc3f31be3d103ef25825 100644
--- a/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c
+++ b/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c
@@ -701,51 +701,33 @@ uint16_t get_NCS(uint8_t index, uint16_t format0, uint8_t restricted_set_config)
   }
 }
 
-//from 38.211 Table 6.3.3.2-1
-static const int16_t N_RA_RB[16] = {6, 3, 2, 24, 12, 6, 12, 6, 3, 24, 12, 6, 12, 6, 24, 12};
-
+//from 38.211 Table 6.3.3.2-1            // 15  30 60 120 240 480
+static const unsigned int N_RA_RB[6][6] = {{12,  6, 3, -1, -1, -1},
+                                           {24, 12, 6, -1, -1, -1},
+                                           {-1, -1, 12, 6, -1, -1},
+                                           {-1, -1, 24, 12, 3, 2},
+                                           // L839
+                                           {6,   3, 2, -1, -1, -1},
+                                           {24, 12, 6, -1, -1, -1}};
 /* Function to get number of RBs required for prach occasion based on
  * 38.211 Table 6.3.3.2-1 */
-int16_t get_N_RA_RB (int delta_f_RA_PRACH, int delta_f_PUSCH)
+unsigned int get_N_RA_RB(const unsigned int delta_f_RA_PRACH, const unsigned int delta_f_PUSCH)
 {
-  int8_t index = 0;
-  switch(delta_f_RA_PRACH) {
-    case 0 :
-      index = 6;
-      if (delta_f_PUSCH == 0)
-        index += 0;
-      else if(delta_f_PUSCH == 1)
-        index += 1;
-      else
-        index += 2;
-      break;
-    case 1 :
-      index = 9;
-      if (delta_f_PUSCH == 0)
-        index += 0;
-      else if(delta_f_PUSCH == 1)
-        index += 1;
-      else
-        index += 2;
-      break;
-    case 2 :
-      index = 11;
-      if (delta_f_PUSCH == 2)
-        index += 0;
-      else
-        index += 1;
-      break;		
-    case 3:
-      index = 13;
-      if (delta_f_PUSCH == 2)
-        index += 0;
-      else
-        index += 1;
-      break;
-    default : index = 10;/*30khz prach scs and 30khz pusch scs*/
-  }
-  return N_RA_RB[index];
-}	
+  DevAssert(delta_f_PUSCH < 6);
+  DevAssert(delta_f_RA_PRACH < 6);
+  unsigned int n_rb;
+  n_rb = N_RA_RB[delta_f_RA_PRACH][delta_f_PUSCH];
+  DevAssert(n_rb != -1);
+  return n_rb;
+}
+
+// frome Table 6.3.3.1-1
+unsigned int get_delta_f_RA_long(const unsigned int format)
+{
+  DevAssert(format < 4);
+  return (format == 3) ? 5 : 4;
+}
+
 // Table 6.3.3.2-2: Random access configurations for FR1 and paired spectrum/supplementary uplink
 // the column 5, (SFN_nbr is a bitmap where we set bit to '1' in the position of the subframe where the RACH can be sent.
 // E.g. in row 4, and column 5 we have set value 512 ('1000000000') which means RACH can be sent at subframe 9.
@@ -1773,6 +1755,27 @@ int get_nr_prach_occasion_info_from_index(uint8_t index,
   }
 }
 
+uint16_t get_nr_prach_format_from_index(uint8_t index, uint32_t pointa, uint8_t unpaired)
+{
+  uint8_t format2 = 0xff;
+  uint16_t format;
+  if (pointa > 2016666) { // FR2
+    if (table_6_3_3_2_4_prachConfig_Index[index][1] != -1)
+      format2 = (uint8_t)table_6_3_3_2_4_prachConfig_Index[index][1];
+    format = ((uint8_t)table_6_3_3_2_4_prachConfig_Index[index][0]) | (format2 << 8);
+  } else {
+    if (unpaired) {
+      if (table_6_3_3_2_3_prachConfig_Index[index][1] != -1)
+        format2 = (uint8_t)table_6_3_3_2_3_prachConfig_Index[index][1];
+      format = ((uint8_t)table_6_3_3_2_3_prachConfig_Index[index][0]) | (format2 << 8);
+    } else {
+      if (table_6_3_3_2_2_prachConfig_Index[index][1] != -1)
+        format2 = (uint8_t)table_6_3_3_2_2_prachConfig_Index[index][1];
+      format = ((uint8_t)table_6_3_3_2_2_prachConfig_Index[index][0]) | (format2 << 8);
+    }
+  }
+  return format;
+}
 
 int get_nr_prach_info_from_index(uint8_t index,
                                  int frame,
@@ -1861,10 +1864,13 @@ int get_nr_prach_info_from_index(uint8_t index,
         }
         if ((s_map >> subframe) & 0x01 ) {
          *N_RA_slot = table_6_3_3_2_3_prachConfig_Index[index][6]; // Number of RACH slots within a subframe
-          if (mu == 1 && index >= 67) {
-            if ((*N_RA_slot <= 1) && (slot % 2 == 0))
+          if (index >= 67) {
+            if ((mu == 1) && (*N_RA_slot <= 1) && (slot % 2 == 0))
               return 0; // no prach in even slots @ 30kHz for 1 prach per subframe 
-          } 
+          } else {
+            if ((slot % 2) && (mu > 0))
+              return 0; // slot does not contain start symbol of this prach time resource
+          }
           if (start_symbol != NULL && N_t_slot != NULL && N_dur != NULL && format != NULL) {
             *config_period = x;
             *start_symbol = table_6_3_3_2_3_prachConfig_Index[index][5];
@@ -5266,7 +5272,9 @@ int get_FeedbackDisabled(NR_DownlinkHARQ_FeedbackDisabled_r17_t *downlinkHARQ_Fe
   return (downlinkHARQ_FeedbackDisabled_r17->buf[byte_index] >> (7 - bit_index)) & 1;
 }
 
-int nr_get_prach_mu(const NR_MsgA_ConfigCommon_r16_t *msgacc, const NR_RACH_ConfigCommon_t *rach_ConfigCommon)
+int nr_get_prach_or_ul_mu(const NR_MsgA_ConfigCommon_r16_t *msgacc,
+                          const NR_RACH_ConfigCommon_t *rach_ConfigCommon,
+                          const int ul_mu)
 {
   int mu;
 
@@ -5278,7 +5286,8 @@ int nr_get_prach_mu(const NR_MsgA_ConfigCommon_r16_t *msgacc, const NR_RACH_Conf
     // Choose Subcarrier Spacing of configuration file of 4-Step
     mu = *rach_ConfigCommon->msg1_SubcarrierSpacing;
   } else
-    AssertFatal(false, "PRACH subcarrier spacing mandatory present for L139, not supported otherwise\n");
+    // Return invalid UL mu
+    mu = ul_mu;
 
   return mu;
 }
diff --git a/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.h b/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.h
index cb0d5a961135125473ee07f6a9cf3f15d8bb946a..dced6f465a55249692ccb9a4e48f0863b99ec2b0 100644
--- a/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.h
+++ b/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.h
@@ -141,6 +141,8 @@ void find_aggregation_candidates(uint8_t *aggregation_level,
                                  const NR_SearchSpace_t *ss,
                                  int maxL);
 
+uint16_t get_nr_prach_format_from_index(uint8_t index, uint32_t pointa, uint8_t unpaired);
+
 int get_nr_prach_info_from_index(uint8_t index,
                                  int frame,
                                  int slot,
@@ -262,7 +264,8 @@ uint16_t compute_pucch_prb_size(uint8_t format,
 float get_max_code_rate(NR_PUCCH_MaxCodeRate_t *maxCodeRate);
 int get_f3_dmrs_symbols(NR_PUCCH_Resource_t *pucchres, NR_PUCCH_Config_t *pucch_Config);
 
-int16_t get_N_RA_RB (int delta_f_RA_PRACH,int delta_f_PUSCH);
+unsigned int get_delta_f_RA_long(const unsigned int format);
+unsigned int get_N_RA_RB(const unsigned int delta_f_RA_PRACH, const unsigned int delta_f_PUSCH);
 
 void find_period_offset_SR(const NR_SchedulingRequestResourceConfig_t *SchedulingReqRec, int *period, int *offset);
 
@@ -334,6 +337,6 @@ int get_nrofHARQ_ProcessesForPDSCH(const NR_UE_ServingCell_Info_t *sc_info);
 
 int get_nrofHARQ_ProcessesForPUSCH(const NR_UE_ServingCell_Info_t *sc_info);
 
-int nr_get_prach_mu(const NR_MsgA_ConfigCommon_r16_t *msgacc, const NR_RACH_ConfigCommon_t *rach_ConfigCommon);
+int nr_get_prach_or_ul_mu(const NR_MsgA_ConfigCommon_r16_t *msgacc, const NR_RACH_ConfigCommon_t *rach_ConfigCommon, const int ul_mu);
 
 #endif
diff --git a/openair2/LAYER2/NR_MAC_UE/config_ue.c b/openair2/LAYER2/NR_MAC_UE/config_ue.c
index 79f6aadf641af31354f5552496c4882e0b139b5b..dd826efb79b2ce0ca75def71da91adde7ccbea57 100644
--- a/openair2/LAYER2/NR_MAC_UE/config_ue.c
+++ b/openair2/LAYER2/NR_MAC_UE/config_ue.c
@@ -190,7 +190,7 @@ static void config_common_ue_sa(NR_UE_MAC_INST_t *mac, NR_ServingCellConfigCommo
     int config_index = rach_ConfigCommon->rach_ConfigGeneric.prach_ConfigurationIndex;
     const int64_t *prach_config_info_p = get_prach_config_info(mac->frequency_range, config_index, frame_type);
     int format = prach_config_info_p[0];
-    cfg->prach_config.prach_sub_c_spacing = format == 3 ? 5 : 4;
+    cfg->prach_config.prach_sub_c_spacing = get_delta_f_RA_long(format);
   }
 
   cfg->prach_config.restricted_set_config = rach_ConfigCommon->restrictedSetConfig;
diff --git a/openair2/LAYER2/NR_MAC_UE/main_ue_nr.c b/openair2/LAYER2/NR_MAC_UE/main_ue_nr.c
index 9147249c1e0657b711b8f0527091b776bb2a6596..35167b6173d47dcaebc86a54f8e9f9acb15c8a34 100644
--- a/openair2/LAYER2/NR_MAC_UE/main_ue_nr.c
+++ b/openair2/LAYER2/NR_MAC_UE/main_ue_nr.c
@@ -70,7 +70,6 @@ void nr_ue_init_mac(NR_UE_MAC_INST_t *mac)
   mac->p_Max_alt = INT_MIN;
   mac->n_ta_offset = -1;
   mac->ntn_ta.ntn_params_changed = false;
-  pthread_mutex_init(&mac->if_mutex, NULL);
   reset_mac_inst(mac);
 
   // need to inizialize because might not been setup (optional timer)
@@ -137,6 +136,8 @@ NR_UE_MAC_INST_t *nr_l2_init_ue(int nb_inst)
     NR_UE_MAC_INST_t *mac = &nr_ue_mac_inst[j];
     mac->ue_id = j;
     nr_ue_init_mac(mac);
+    int ret = pthread_mutex_init(&mac->if_mutex, NULL);
+    AssertFatal(ret == 0, "Mutex init failed\n");
     nr_ue_mac_default_configs(mac);
     if (IS_SA_MODE(get_softmodem_params()))
       ue_init_config_request(mac, get_slots_per_frame_from_scs(get_softmodem_params()->numerology));
diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ra_procedures.c b/openair2/LAYER2/NR_MAC_UE/nr_ra_procedures.c
index b3174e3bd33cf3ba8b5073c78d935c18af966168..2740e1d3c7f2de5c09abbf274c10ba856dd63867 100644
--- a/openair2/LAYER2/NR_MAC_UE/nr_ra_procedures.c
+++ b/openair2/LAYER2/NR_MAC_UE/nr_ra_procedures.c
@@ -76,9 +76,18 @@ void init_RA(NR_UE_MAC_INST_t *mac,
   fapi_nr_config_request_t *cfg = &mac->phy_config.config_req;
 
   prach_resources->RA_PREAMBLE_BACKOFF = 0;
-  AssertFatal(nr_rach_ConfigCommon && nr_rach_ConfigCommon->msg1_SubcarrierSpacing,
-              "Cannot handle yet the scenario without msg1_SubcarrierSpacing (L839)\n");
-  NR_SubcarrierSpacing_t prach_scs = *nr_rach_ConfigCommon->msg1_SubcarrierSpacing;
+  NR_SubcarrierSpacing_t prach_scs;
+  int scs_for_pcmax; // for long prach the UL BWP SCS is used for calculating RA_PCMAX
+  if (nr_rach_ConfigCommon && nr_rach_ConfigCommon->msg1_SubcarrierSpacing) {
+    prach_scs = *nr_rach_ConfigCommon->msg1_SubcarrierSpacing;
+    scs_for_pcmax = prach_scs;
+  } else {
+    const unsigned int index = rach_ConfigGeneric->prach_ConfigurationIndex;
+    const unsigned int unpaired = mac->phy_config.config_req.cell_config.frame_duplex_type;
+    const unsigned int format = get_format0(index, unpaired, mac->frequency_range);
+    prach_scs = get_delta_f_RA_long(format);
+    scs_for_pcmax = mac->current_UL_BWP->scs;
+  }
   int n_prbs = get_N_RA_RB(prach_scs, mac->current_UL_BWP->scs);
   int start_prb = rach_ConfigGeneric->msg1_FrequencyStart + mac->current_UL_BWP->BWPStart;
   // PRACH shall be as specified for QPSK modulated DFT-s-OFDM of equivalent RB allocation (38.101-1)
@@ -89,8 +98,8 @@ void init_RA(NR_UE_MAC_INST_t *mac,
                                            mac->current_UL_BWP->channel_bandwidth,
                                            2,
                                            false,
-                                           prach_scs,
-                                           cfg->carrier_config.dl_grid_size[prach_scs],
+                                           scs_for_pcmax,
+                                           cfg->carrier_config.dl_grid_size[scs_for_pcmax],
                                            true,
                                            n_prbs,
                                            start_prb);
@@ -212,57 +221,16 @@ void init_RA(NR_UE_MAC_INST_t *mac,
 }
 
 /* TS 38.321 subclause 7.3 - return DELTA_PREAMBLE values in dB */
-static int8_t nr_get_DELTA_PREAMBLE(NR_UE_MAC_INST_t *mac, int CC_id, uint16_t prach_format)
+static int8_t nr_get_DELTA_PREAMBLE(const NR_UE_MAC_INST_t *mac, int CC_id, uint16_t prach_format)
 {
-  NR_RACH_ConfigCommon_t *nr_rach_ConfigCommon = mac->current_UL_BWP->rach_ConfigCommon;
-  NR_SubcarrierSpacing_t scs = *nr_rach_ConfigCommon->msg1_SubcarrierSpacing;
+  const NR_RACH_ConfigCommon_t *nr_rach_ConfigCommon = mac->current_UL_BWP->rach_ConfigCommon;
   int prach_sequence_length = nr_rach_ConfigCommon->prach_RootSequenceIndex.present - 1;
-  uint8_t prachConfigIndex, mu;
 
   AssertFatal(CC_id == 0, "Transmission on secondary CCs is not supported yet\n");
 
-  // SCS configuration from msg1_SubcarrierSpacing and table 4.2-1 in TS 38.211
-
-  switch (scs){
-    case NR_SubcarrierSpacing_kHz15:
-    mu = 0;
-    break;
-
-    case NR_SubcarrierSpacing_kHz30:
-    mu = 1;
-    break;
-
-    case NR_SubcarrierSpacing_kHz60:
-    mu = 2;
-    break;
-
-    case NR_SubcarrierSpacing_kHz120:
-    mu = 3;
-    break;
-
-    case NR_SubcarrierSpacing_kHz240:
-    mu = 4;
-    break;
-
-    case NR_SubcarrierSpacing_kHz480_v1700:
-    mu = 5;
-    break;
-
-    case NR_SubcarrierSpacing_kHz960_v1700:
-    mu = 6;
-    break;
-
-    case NR_SubcarrierSpacing_spare1:
-    mu = 7;
-    break;
-
-    default:
-    AssertFatal(1 == 0,"Unknown msg1_SubcarrierSpacing %lu\n", scs);
-  }
-
   // Preamble formats given by prach_ConfigurationIndex and tables 6.3.3.2-2 and 6.3.3.2-2 in TS 38.211
 
-  prachConfigIndex = nr_rach_ConfigCommon->rach_ConfigGeneric.prach_ConfigurationIndex;
+  unsigned int prachConfigIndex = nr_rach_ConfigCommon->rach_ConfigGeneric.prach_ConfigurationIndex;
 
   if (prach_sequence_length == 0) {
     AssertFatal(prach_format < 4, "Illegal PRACH format %d for sequence length 839\n", prach_format);
@@ -278,8 +246,21 @@ static int8_t nr_get_DELTA_PREAMBLE(NR_UE_MAC_INST_t *mac, int CC_id, uint16_t p
 
       case 2:
       return -6;
+
+      default:
+      AssertFatal(1 == 0, "[UE %d] ue_procedures.c: FATAL, Illegal preambleFormat %d, prachConfigIndex %d\n",
+                  mac->ue_id,
+                  prach_format,
+                  prachConfigIndex);
     }
   } else {
+    // SCS configuration from msg1_SubcarrierSpacing and table 4.2-1 in TS 38.211
+
+    AssertFatal(nr_rach_ConfigCommon->msg1_SubcarrierSpacing, "msg1_SubcarrierSpacing required but missing\n");
+    NR_SubcarrierSpacing_t scs = *nr_rach_ConfigCommon->msg1_SubcarrierSpacing;
+    AssertFatal(scs >= NR_SubcarrierSpacing_kHz15 && scs <= NR_SubcarrierSpacing_spare1, "Unknown msg1_SubcarrierSpacing %lu\n", scs);
+    const unsigned int mu = scs;
+
     switch (prach_format) { // short preamble formats
       case 0:
       case 3:
diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c
index e3407c95b3e9d68f85a7a71378db14b766372a63..846d344584287bc860907e6f54a03149617754b2 100644
--- a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c
+++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c
@@ -1849,7 +1849,8 @@ static void build_ro_list(NR_UE_MAC_INST_t *mac)
   int unpaired = mac->phy_config.config_req.cell_config.frame_duplex_type;
 
   const int64_t *prach_config_info_p = get_prach_config_info(mac->frequency_range, config_index, unpaired);
-  int mu = nr_get_prach_mu(mac->current_UL_BWP->msgA_ConfigCommon_r16, setup);
+  const int ul_mu = mac->current_UL_BWP->scs;
+  const int mu = nr_get_prach_or_ul_mu(mac->current_UL_BWP->msgA_ConfigCommon_r16, setup, ul_mu);
 
   // Identify the proper PRACH Configuration Index table according to the operating frequency
   LOG_D(NR_MAC,"mu = %u, PRACH config index  = %u, unpaired = %u\n", mu, config_index, unpaired);
@@ -1891,7 +1892,7 @@ static void build_ro_list(NR_UE_MAC_INST_t *mac)
     format = ((uint8_t) prach_config_info_p[0]) | (format2<<8);
 
     slot_shift_for_map = mu;
-    if ( (mu == 1) && (prach_config_info_p[6] <= 1) )
+    if ( (mu == 1) && (prach_config_info_p[6] == 1) && ((format & 0xff) > 3) )
       // no prach in even slots @ 30kHz for 1 prach per subframe
       even_slot_invalid = true;
     else
@@ -2955,10 +2956,16 @@ static void nr_ue_prach_scheduler(NR_UE_MAC_INST_t *mac, frame_t frameP, sub_fra
       nr_get_prach_resources(mac, 0, 0, &ra->prach_resources, ra->rach_ConfigDedicated);
       pdu->prach_config_pdu.ra_PreambleIndex = ra->ra_PreambleIndex;
       pdu->prach_config_pdu.prach_tx_power = get_prach_tx_power(mac);
-      mac->ra.ra_rnti = nr_get_ra_rnti(pdu->prach_config_pdu.prach_start_symbol,
-                                       pdu->prach_config_pdu.prach_slot,
-                                       pdu->prach_config_pdu.num_ra,
-                                       0);
+      unsigned int slot_RA;
+      // 3GPP TS 38.321 Section 5.1.3 says t_id for RA-RNTI depends on mu as specified in clause 5.3.2 in TS 38.211
+      // so mu = 0 for prach format < 4.
+      if (pdu->prach_config_pdu.prach_format < 4) {
+        unsigned int slots_per_sf = (1 << mac->current_UL_BWP->scs);
+        slot_RA = pdu->prach_config_pdu.prach_slot / slots_per_sf;
+      } else {
+        slot_RA = pdu->prach_config_pdu.prach_slot;
+      }
+      mac->ra.ra_rnti = nr_get_ra_rnti(pdu->prach_config_pdu.prach_start_symbol, slot_RA, pdu->prach_config_pdu.num_ra, 0);
       release_ul_config(pdu, false);
       nr_scheduled_response_t scheduled_response = {.ul_config = mac->ul_config_request + slotP,
                                                     .mac = mac,
@@ -3006,7 +3013,7 @@ static void nr_ue_prach_scheduler(NR_UE_MAC_INST_t *mac, frame_t frameP, sub_fra
 
         // Compute MsgB RNTI
         ra->MsgB_rnti =
-            nr_get_MsgB_rnti(prach_occasion_info_p->start_symbol, prach_occasion_info_p->slot, prach_occasion_info_p->fdm, 0);
+            nr_get_MsgB_rnti(prach_occasion_info_p->start_symbol, slot_RA, prach_occasion_info_p->fdm, 0);
         LOG_D(NR_MAC, "ra->ra_state %s\n", nrra_ue_text[ra->ra_state]);
         ra->ra_state = nrRA_WAIT_MSGB;
         ra->t_crnti = 0;
diff --git a/openair2/LAYER2/NR_MAC_gNB/config.c b/openair2/LAYER2/NR_MAC_gNB/config.c
index 862bf563df433271576784df15656b68aef4d6c1..218226eb2cbeb32063c68e578ed0fa06d955566c 100644
--- a/openair2/LAYER2/NR_MAC_gNB/config.c
+++ b/openair2/LAYER2/NR_MAC_gNB/config.c
@@ -475,7 +475,8 @@ static void config_common(gNB_MAC_INST *nrmac,
     }
   }
 
-  frame_type_t frame_type = get_frame_type(*frequencyInfoDL->frequencyBandList.list.array[0], *scc->ssbSubcarrierSpacing);
+  NR_FreqBandIndicatorNR_t band = *frequencyInfoDL->frequencyBandList.list.array[0];
+  frame_type_t frame_type = get_frame_type(band, *scc->ssbSubcarrierSpacing);
   nrmac->common_channels[0].frame_type = frame_type;
 
   // Cell configuration
@@ -511,18 +512,26 @@ static void config_common(gNB_MAC_INST *nrmac,
   cfg->prach_config.prach_sequence_length.tl.tag = NFAPI_NR_CONFIG_PRACH_SEQUENCE_LENGTH_TAG;
   cfg->num_tlv++;
 
+  cfg->prach_config.prach_ConfigurationIndex.value = rach_ConfigCommon->rach_ConfigGeneric.prach_ConfigurationIndex;
+  cfg->prach_config.prach_ConfigurationIndex.tl.tag = NFAPI_NR_CONFIG_PRACH_CONFIG_INDEX_TAG;
+  cfg->num_tlv++;
+
   if (rach_ConfigCommon->msg1_SubcarrierSpacing)
     cfg->prach_config.prach_sub_c_spacing.value = *rach_ConfigCommon->msg1_SubcarrierSpacing;
-  else
-    cfg->prach_config.prach_sub_c_spacing.value = frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->subcarrierSpacing;
+  else {
+    // If absent, use SCS as derived from the prach-ConfigurationIndex (for 839)
+    int config_index = rach_ConfigCommon->rach_ConfigGeneric.prach_ConfigurationIndex;
+    int frame_type = get_frame_type(band, frequencyInfoUL->scs_SpecificCarrierList.list.array[0]->subcarrierSpacing);
+    const int64_t *prach_config_info_p = get_prach_config_info(frequency_range, config_index, frame_type);
+    int format = prach_config_info_p[0];
+    cfg->prach_config.prach_sub_c_spacing.value = get_delta_f_RA_long(format);
+  }
+
   cfg->prach_config.prach_sub_c_spacing.tl.tag = NFAPI_NR_CONFIG_PRACH_SUB_C_SPACING_TAG;
   cfg->num_tlv++;
   cfg->prach_config.restricted_set_config.value = rach_ConfigCommon->restrictedSetConfig;
   cfg->prach_config.restricted_set_config.tl.tag = NFAPI_NR_CONFIG_RESTRICTED_SET_CONFIG_TAG;
   cfg->num_tlv++;
-  cfg->prach_config.prach_ConfigurationIndex.value = rach_ConfigCommon->rach_ConfigGeneric.prach_ConfigurationIndex;
-  cfg->prach_config.prach_ConfigurationIndex.tl.tag = NFAPI_NR_CONFIG_PRACH_CONFIG_INDEX_TAG;
-  cfg->num_tlv++;
 
   switch (rach_ConfigCommon->rach_ConfigGeneric.msg1_FDM) {
     case 0:
@@ -543,10 +552,6 @@ static void config_common(gNB_MAC_INST *nrmac,
   cfg->prach_config.num_prach_fd_occasions.tl.tag = NFAPI_NR_CONFIG_NUM_PRACH_FD_OCCASIONS_TAG;
   cfg->num_tlv++;
 
-  cfg->prach_config.prach_ConfigurationIndex.value = rach_ConfigCommon->rach_ConfigGeneric.prach_ConfigurationIndex;
-  cfg->prach_config.prach_ConfigurationIndex.tl.tag = NFAPI_NR_CONFIG_PRACH_CONFIG_INDEX_TAG;
-  cfg->num_tlv++;
-
   cfg->prach_config.num_prach_fd_occasions_list = (nfapi_nr_num_prach_fd_occasions_t *)malloc(
        cfg->prach_config.num_prach_fd_occasions.value * sizeof(nfapi_nr_num_prach_fd_occasions_t));
   for (int i = 0; i < cfg->prach_config.num_prach_fd_occasions.value; i++) {
@@ -588,6 +593,14 @@ static void config_common(gNB_MAC_INST *nrmac,
   cfg->prach_config.ssb_per_rach.tl.tag = NFAPI_NR_CONFIG_SSB_PER_RACH_TAG;
   cfg->num_tlv++;
 
+  // compute and store prach duration in slots from rach_ConfigCommon
+  NR_RACH_ConfigGeneric_t *rachConfig =
+      &scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup->rach_ConfigGeneric;
+  NR_COMMON_channels_t *cc = nrmac->common_channels;
+  const uint32_t pointA = scc->downlinkConfigCommon->frequencyInfoDL->absoluteFrequencyPointA;
+  const int prach_fmt = (get_nr_prach_format_from_index(rachConfig->prach_ConfigurationIndex, pointA, cc->frame_type) & 0xff);
+  cc->prach_len = (prach_fmt < 4) ? get_long_prach_dur(prach_fmt, *scc->ssbSubcarrierSpacing) : 1;
+
   // SSB Table Configuration
 
   cfg->ssb_table.ssb_offset_point_a.value =
diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c
index 49f88373dfb94ad08c832b18b2e58e7d25b184f5..d3455ad8e8b39a694c7d153223a619098705f875 100644
--- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c
+++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c
@@ -223,11 +223,11 @@ void gNB_dlsch_ulsch_scheduler(module_id_t module_idP, frame_t frame, sub_frame_
   if (get_softmodem_params()->phy_test == 0) {
     /* we need to make sure that resources for PRACH are free. To avoid that
        e.g. PUSCH has already been scheduled, make sure we schedule before
-       anything else: below, we simply assume an advance one frame (minus one
-       slot, because otherwise we would allocate the current slot in
+       anything else: below, we simply assume an advance one frame (minus
+       prach duration, because otherwise we would allocate the current slot in
        UL_tti_req_ahead), but be aware that, e.g., K2 is allowed to be larger
        (schedule_nr_prach will assert if resources are not free). */
-    const sub_frame_t n_slots_ahead = slots_frame - 1 + get_NTN_Koffset(scc);
+    const int n_slots_ahead = slots_frame - cc->prach_len + get_NTN_Koffset(scc);
     const frame_t f = (frame + (slot + n_slots_ahead) / slots_frame) % 1024;
     const sub_frame_t s = (slot + n_slots_ahead) % slots_frame;
     schedule_nr_prach(module_idP, f, s);
diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c
index a8c434b62c665d44f49ee2239d471821a660bdb9..6e776b123a08e97bf235428ccbad38a6f324c405 100644
--- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c
+++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c
@@ -92,7 +92,8 @@ static int16_t ssb_index_from_prach(module_id_t module_idP,
   NR_MsgA_ConfigCommon_r16_t *msgacc = NULL;
   if (scc->uplinkConfigCommon->initialUplinkBWP->ext1 && scc->uplinkConfigCommon->initialUplinkBWP->ext1->msgA_ConfigCommon_r16)
     msgacc = scc->uplinkConfigCommon->initialUplinkBWP->ext1->msgA_ConfigCommon_r16->choice.setup;
-  int mu = nr_get_prach_mu(msgacc, rach_ConfigCommon);
+  const int ul_mu = scc->uplinkConfigCommon->frequencyInfoUL->scs_SpecificCarrierList.list.array[0]->subcarrierSpacing;
+  const int mu = nr_get_prach_or_ul_mu(msgacc, rach_ConfigCommon, ul_mu);
 
   get_nr_prach_info_from_index(config_index,
 			       (int)frameP,
@@ -199,7 +200,8 @@ void find_SSB_and_RO_available(gNB_MAC_INST *nrmac)
   NR_MsgA_ConfigCommon_r16_t *msgacc = NULL;
   if (scc->uplinkConfigCommon->initialUplinkBWP->ext1 && scc->uplinkConfigCommon->initialUplinkBWP->ext1->msgA_ConfigCommon_r16)
     msgacc = scc->uplinkConfigCommon->initialUplinkBWP->ext1->msgA_ConfigCommon_r16->choice.setup;
-  int mu = nr_get_prach_mu(msgacc, rach_ConfigCommon);
+  const int ul_mu = scc->uplinkConfigCommon->frequencyInfoUL->scs_SpecificCarrierList.list.array[0]->subcarrierSpacing;
+  const int mu = nr_get_prach_or_ul_mu(msgacc, rach_ConfigCommon, ul_mu);
 
   // prach is scheduled according to configuration index and tables 6.3.3.2.2 to 6.3.3.2.4
   get_nr_prach_occasion_info_from_index(config_index,
@@ -354,6 +356,27 @@ static void schedule_nr_MsgA_pusch(NR_UplinkConfigCommon_t *uplinkConfigCommon,
   UL_tti_req->n_pdus += 1;
 }
 
+static void fill_vrb(const frame_t frame,
+                     const sub_frame_t slot,
+                     int nb_rb,
+                     int beam_idx,
+                     int vrb_size,
+                     int slots_frame,
+                     int rb_start,
+                     int start_symb,
+                     int num_symb,
+                     NR_COMMON_channels_t *cc)
+{
+  const int index = ul_buffer_index(frame, slot, slots_frame, vrb_size);
+  uint16_t *vrb_map_UL = &cc->vrb_map_UL[beam_idx][index * MAX_BWP_SIZE];
+  for (int i = 0; i < nb_rb; ++i) {
+    AssertFatal(
+        !(vrb_map_UL[rb_start + i] & SL_to_bitmap(start_symb, num_symb)),
+        "PRACH resources are already occupied!\n");
+    vrb_map_UL[rb_start + i] |= SL_to_bitmap(start_symb, num_symb);
+  }
+}
+
 void schedule_nr_prach(module_id_t module_idP, frame_t frameP, sub_frame_t slotP)
 {
   gNB_MAC_INST *gNB = RC.nrmac[module_idP];
@@ -366,7 +389,6 @@ void schedule_nr_prach(module_id_t module_idP, frame_t frameP, sub_frame_t slotP
   NR_MsgA_ConfigCommon_r16_t *msgacc = NULL;
   if (scc->uplinkConfigCommon->initialUplinkBWP->ext1 && scc->uplinkConfigCommon->initialUplinkBWP->ext1->msgA_ConfigCommon_r16)
     msgacc = scc->uplinkConfigCommon->initialUplinkBWP->ext1->msgA_ConfigCommon_r16->choice.setup;
-  int mu = nr_get_prach_mu(msgacc, rach_ConfigCommon);
   int slots_frame = gNB->frame_structure.numb_slots_frame;
   int index = ul_buffer_index(frameP, slotP, slots_frame, gNB->UL_tti_req_ahead_size);
   nfapi_nr_ul_tti_request_t *UL_tti_req = &RC.nrmac[module_idP]->UL_tti_req_ahead[0][index];
@@ -385,6 +407,8 @@ void schedule_nr_prach(module_id_t module_idP, frame_t frameP, sub_frame_t slotP
     int bwp_start = NRRIV2PRBOFFSET(scc->uplinkConfigCommon->initialUplinkBWP->genericParameters.locationAndBandwidth, MAX_BWP_SIZE);
 
     uint8_t fdm = cfg->prach_config.num_prach_fd_occasions.value;
+    const int ul_mu = scc->uplinkConfigCommon->frequencyInfoUL->scs_SpecificCarrierList.list.array[0]->subcarrierSpacing;
+    const int mu = nr_get_prach_or_ul_mu(msgacc, rach_ConfigCommon, ul_mu);
     // prach is scheduled according to configuration index and tables 6.3.3.2.2 to 6.3.3.2.4
     if (get_nr_prach_info_from_index(config_index,
                                      (int)frameP,
@@ -554,13 +578,29 @@ void schedule_nr_prach(module_id_t module_idP, frame_t frameP, sub_frame_t slotP
       // block resources in vrb_map_UL
       const int mu_pusch = scc->uplinkConfigCommon->frequencyInfoUL->scs_SpecificCarrierList.list.array[0]->subcarrierSpacing;
       const int16_t n_ra_rb = get_N_RA_RB(cfg->prach_config.prach_sub_c_spacing.value, mu_pusch);
-      index = ul_buffer_index(frameP, slotP, slots_frame, gNB->vrb_map_UL_size);
-      uint16_t *vrb_map_UL = &cc->vrb_map_UL[beam.idx][index * MAX_BWP_SIZE];
-      for (int i = 0; i < n_ra_rb * fdm; ++i) {
-        AssertFatal(
-            !(vrb_map_UL[bwp_start + rach_ConfigGeneric->msg1_FrequencyStart + i] & SL_to_bitmap(start_symbol, N_t_slot * N_dur)),
-            "PRACH resources are already occupied!\n");
-        vrb_map_UL[bwp_start + rach_ConfigGeneric->msg1_FrequencyStart + i] |= SL_to_bitmap(start_symbol, N_t_slot * N_dur);
+      // mark PRBs as occupied for current and future slots if prach extends beyond current slot
+      int total_prach_slots;
+      if (format0 < 4) {
+        N_dur = 14; // number of PRACH symbols in PRACH slot
+        total_prach_slots = get_long_prach_dur(format0, mu_pusch);
+        AssertFatal(slotP + total_prach_slots - 1 < slots_frame, "PRACH cannot extend across frames\n");
+      } else {
+        // TODO: to be revisited for format B4 (also extends beyond current slot for FR1 30kHz SCS and FR2)
+        AssertFatal((format != 0xb4) || (mu_pusch < 1), "Format B4 not supported for this PUSCH SCS\n");
+        total_prach_slots = 1;
+      }
+      // reserve PRBs occupied by PRACH in all PRACH slot.
+      for (int i = 0; i < total_prach_slots; i++) {
+        fill_vrb(frameP,
+                 slotP + i,
+                 n_ra_rb * fdm,
+                 beam.idx,
+                 gNB->vrb_map_UL_size,
+                 slots_frame,
+                 bwp_start + rach_ConfigGeneric->msg1_FrequencyStart,
+                 start_symbol,
+                 N_t_slot * N_dur,
+                 cc);
       }
     }
   }
@@ -721,17 +761,37 @@ void nr_initiate_ra_proc(module_id_t module_idP,
   ra->preamble_index = preamble_index;
   ra->timing_offset = timing_offset;
   ra->msg3_TPC = nr_get_msg3_tpc(preamble_power);
-  uint8_t ul_carrier_id = 0; // 0 for NUL 1 for SUL
-  ra->RA_rnti = nr_get_ra_rnti(symbol, slotP, freq_index, ul_carrier_id);
 
   NR_ServingCellConfigCommon_t *scc = cc->ServingCellConfigCommon;
-  if (scc->uplinkConfigCommon->initialUplinkBWP->ext1 && scc->uplinkConfigCommon->initialUplinkBWP->ext1->msgA_ConfigCommon_r16) {
-    ra->ra_type = RA_2_STEP;
-    ra->ra_state = nrRA_WAIT_MsgA_PUSCH;
-    ra->MsgB_rnti = nr_get_MsgB_rnti(symbol, slotP, freq_index, ul_carrier_id);
-  } else {
-    ra->ra_type = RA_4_STEP;
-    ra->ra_state = nrRA_Msg2;
+  {
+    // 3GPP TS 38.321 Section 5.1.3(a) says t_id for RA-RNTI depends on mu as specified in clause 5.3.2 in TS 38.211
+    // so mu = 0 for prach format < 4.
+    NR_RACH_ConfigCommon_t *rach_ConfigCommon = scc->uplinkConfigCommon->initialUplinkBWP->rach_ConfigCommon->choice.setup;
+    NR_MsgA_ConfigCommon_r16_t *msgacc = NULL;
+    if (scc->uplinkConfigCommon->initialUplinkBWP->ext1 && scc->uplinkConfigCommon->initialUplinkBWP->ext1->msgA_ConfigCommon_r16)
+      msgacc = scc->uplinkConfigCommon->initialUplinkBWP->ext1->msgA_ConfigCommon_r16->choice.setup;
+    const int ul_mu = scc->uplinkConfigCommon->frequencyInfoUL->scs_SpecificCarrierList.list.array[0]->subcarrierSpacing;
+    const int mu = nr_get_prach_or_ul_mu(msgacc, rach_ConfigCommon, ul_mu);
+    uint8_t index = rach_ConfigCommon->rach_ConfigGeneric.prach_ConfigurationIndex;
+    uint16_t prach_format =
+        get_nr_prach_format_from_index(index, scc->downlinkConfigCommon->frequencyInfoDL->absoluteFrequencyPointA, cc->frame_type);
+    unsigned int slot_RA;
+    if ((prach_format & 0xff) < 4) {
+      unsigned int slots_per_sf = (1 << mu);
+      slot_RA = slotP / slots_per_sf;
+    } else {
+      slot_RA = slotP;
+    }
+    uint8_t ul_carrier_id = 0; // 0 for NUL 1 for SUL
+    ra->RA_rnti = nr_get_ra_rnti(symbol, slot_RA, freq_index, ul_carrier_id);
+    if (scc->uplinkConfigCommon->initialUplinkBWP->ext1 && scc->uplinkConfigCommon->initialUplinkBWP->ext1->msgA_ConfigCommon_r16) {
+      ra->ra_type = RA_2_STEP;
+      ra->ra_state = nrRA_WAIT_MsgA_PUSCH;
+      ra->MsgB_rnti = nr_get_MsgB_rnti(symbol, slot_RA, freq_index, ul_carrier_id);
+    } else {
+      ra->ra_type = RA_4_STEP;
+      ra->ra_state = nrRA_Msg2;
+    }
   }
 
   int index = ra - cc->ra;
diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c
index 6dac12a01b2700f46e4d13f409cee994cc2f0d59..dcb7bb2cb1e0dc811485730a8670bca7486bdf1c 100644
--- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c
+++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c
@@ -1911,7 +1911,7 @@ static void pf_ul(module_id_t module_id,
       sched_ctrl->ul_bler_stats.mcs = sched_pusch->mcs;
     } else {
       sched_pusch->mcs = get_mcs_from_bler(bo, stats, &sched_ctrl->ul_bler_stats, max_mcs, frame);
-      LOG_D(NR_MAC,"%d.%d starting mcs %d bleri %f\n", frame, slot, sched_pusch->mcs, sched_ctrl->ul_bler_stats.bler);
+      LOG_D(NR_MAC, "%d.%d starting mcs %d bler %f\n", frame, slot, sched_pusch->mcs, sched_ctrl->ul_bler_stats.bler);
     }
     /* Schedule UE on SR or UL inactivity and no data (otherwise, will be scheduled
      * based on data to transmit) */
@@ -1973,7 +1973,7 @@ static void pf_ul(module_id_t module_id,
       fill_pdcch_vrb_map(nrmac, CC_id, &sched_ctrl->sched_pdcch, CCEIndex, sched_ctrl->aggregation_level, dci_beam.idx);
 
       NR_sched_pusch_t *sched_pusch = &sched_ctrl->sched_pusch;
-      sched_pusch->mcs = min(nrmac->min_grant_mcs, sched_pusch->mcs);
+      sched_pusch->mcs = max(nrmac->min_grant_mcs, sched_pusch->mcs);
       update_ul_ue_R_Qm(sched_pusch->mcs, current_BWP->mcs_table, current_BWP->pusch_Config, &sched_pusch->R, &sched_pusch->Qm);
       sched_pusch->rbStart = rbStart;
       sched_pusch->rbSize = min_rb;
diff --git a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h
index b9045e15a0fe1c45e9238596fd32d534e0d20c23..218831d8c3fc0495779d891e482c5e4967c0177f 100644
--- a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h
+++ b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h
@@ -301,6 +301,8 @@ typedef struct {
   uint8_t ssb_index[MAX_NUM_OF_SSB];
   //CB preambles for each SSB
   int cb_preambles_per_ssb;
+  /// Max prach length in slots
+  int prach_len;
 } NR_COMMON_channels_t;
 
 // SP ZP CSI-RS Resource Set Activation/Deactivation MAC CE
diff --git a/radio/rfsimulator/README.md b/radio/rfsimulator/README.md
index dac8b155ada7e4457802e333e920777fa3ee600f..ae46034cecbcfddce34e83f192e6a2247427c7a2 100644
--- a/radio/rfsimulator/README.md
+++ b/radio/rfsimulator/README.md
@@ -69,16 +69,17 @@ the corresponding section in the configuration file.
 
 The RF simulator is using the configuration module, and its parameters are defined in a specific section called "rfsimulator". Add the following options to the command line in order to enable different RFSim features:
 
-| CL option                       | usage                                                                          | default                |
-|:---------------------           |:-------------------------------------------------------------------------------|----:                   |
-|`--rfsimulator.serveraddr <addr>`| IPv4v6 address or DNS name to connect to, or `server` to behave as a IPv4v6 TCP server | 127.0.0.1      |
-|`--rfsimulator.serverport <port>`| port number to connect to or to listen on (eNB, which behaves as a tcp server) | 4043                   |
-|`--rfsimulator.options`          | list of comma separated run-time options, two are supported: `chanmod`, `saviq`| all options disabled   |
-|`--rfsimulator.options saviq`    | store IQs to a file for future replay                                          | disabled               |
-|`--rfsimulator.options chanmod`  | enable the channel model                                                       | disabled               |
-|`--rfsimulator.IQfile <file>`    | path to a file to store the IQ samples to (only with `saviq`)                  | `/tmp/rfsimulator.iqs` |
-|`--rfsimulator.prop_delay`       | simulated receive-path (gNB: UL, UE: DL) propagation delay in ms               | 0                      |
-|`--rfsimulator.wait_timeout`     | wait timeout when no UE is connected                                           | 1                      |
+| CL option                        | usage                                                                          | default                |
+|:---------------------            |:-------------------------------------------------------------------------------|----:                   |
+|`--rfsimulator.serveraddr <addr>` | IPv4v6 address or DNS name to connect to, or `server` to behave as a IPv4v6 TCP server | 127.0.0.1      |
+|`--rfsimulator.serverport <port>` | port number to connect to or to listen on (eNB, which behaves as a tcp server) | 4043                   |
+|`--rfsimulator.options`           | list of comma separated run-time options, two are supported: `chanmod`, `saviq`| all options disabled   |
+|`--rfsimulator.options saviq`     | store IQs to a file for future replay                                          | disabled               |
+|`--rfsimulator.options chanmod`   | enable the channel model                                                       | disabled               |
+|`--rfsimulator.IQfile <file>`     | path to a file to store the IQ samples to (only with `saviq`)                  | `/tmp/rfsimulator.iqs` |
+|`--rfsimulator.prop_delay`        | simulated receive-path (gNB: UL, UE: DL) propagation delay in ms               | 0                      |
+|`--rfsimulator.wait_timeout`      | wait timeout when no UE is connected                                           | 1                      |
+|`--rfismulator.hanging-workaround`| Enable workaround to de-block potentially hanging server on new client connection.  | 0                 |
 
 Please refer to this document [`SIMULATION/TOOLS/DOC/channel_simulation.md`](../../openair1/SIMULATION/TOOLS/DOC/channel_simulation.md) for information about using the RFSimulator options to run the simulator with a channel model.
 
diff --git a/radio/rfsimulator/simulator.c b/radio/rfsimulator/simulator.c
index c733f4c8a26e11fa5b0cbf8024742a88e9f1421d..b11f4034caf4bcb7615d0e752e1da4571aadbdd9 100644
--- a/radio/rfsimulator/simulator.c
+++ b/radio/rfsimulator/simulator.c
@@ -101,6 +101,8 @@ typedef enum { SIMU_ROLE_SERVER = 1, SIMU_ROLE_CLIENT } simuRole;
 #define RFSIM_CONFIG_HELP_OPTIONS     " list of comma separated options to enable rf simulator functionalities. Available options: \n"\
   "        chanmod:   enable channel modelisation\n"\
   "        saviq:     enable saving written iqs to a file\n"
+#define CONFIG_HELP_HANG_WORKAROUND "Enable workaroud for server mode hanging on new client connection.\n"
+
 /*-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
 /*                                            configuration parameters for the rfsimulator device                                                                              */
 /*   optname                     helpstr                     paramflags           XXXptr                               defXXXval                          type         numelt  */
@@ -117,6 +119,7 @@ typedef enum { SIMU_ROLE_SERVER = 1, SIMU_ROLE_CLIENT } simuRole;
     {"offset",                 "<channel offset in samps>\n",         simOpt,  .u64ptr=&(rfsimulator->chan_offset),    .defint64val=0,                   TYPE_UINT64,    0 },\
     {"prop_delay",             "<propagation delay in ms>\n",         simOpt,  .dblptr=&(rfsimulator->prop_delay_ms),  .defdblval=0.0,                   TYPE_DOUBLE,    0 },\
     {"wait_timeout",           "<wait timeout if no UE connected>\n", simOpt,  .iptr=&(rfsimulator->wait_timeout),     .defintval=1,                     TYPE_INT,       0 },\
+    {"hanging-workaround",     CONFIG_HELP_HANG_WORKAROUND,           simOpt,  .iptr=&rfsimulator->hanging_workaround, .defintval=0,                     TYPE_INT,       0 },\
   };
 
 static void getset_currentchannels_type(char *buf, int debug, webdatadef_t *tdata, telnet_printfunc_t prnt);
@@ -181,6 +184,7 @@ typedef struct {
   poll_telnetcmdq_func_t poll_telnetcmdq;
   int wait_timeout;
   double prop_delay_ms;
+  int hanging_workaround;
 } rfsimulator_state_t;
 
 
@@ -256,11 +260,17 @@ static int allocCirBuf(rfsimulator_state_t *bridge, int sock)
       tableNor(rand);
       init_done=true;
     }
-    char *modelname = (bridge->role == SIMU_ROLE_SERVER) ? "rfsimu_channel_ue0" : "rfsimu_channel_enB0";
+    char modelname[30];
+    snprintf(modelname, sizeofArray(modelname), "rfsimu_channel_%s%d", (bridge->role == SIMU_ROLE_SERVER) ? "ue" : "enB", nb_ue);
     ptr->channel_model = find_channel_desc_fromname(modelname); // path_loss in dB
     if (!ptr->channel_model) {
-      LOG_E(HW, "Channel model %s not found, check config file\n", modelname);
-      return -1;
+      // Use legacy method to find channel model - this will use the same channel model for all clients
+      char *legacy_model_name = (bridge->role == SIMU_ROLE_SERVER) ? "rfsimu_channel_ue0" : "rfsimu_channel_enB0";
+      ptr->channel_model = find_channel_desc_fromname(legacy_model_name);
+      if (!ptr->channel_model) {
+        LOG_E(HW, "Channel model %s/%s not found, check config file\n", modelname, legacy_model_name);
+        return -1;
+      }
     }
 
     set_channeldesc_owner(ptr->channel_model, RFSIMU_MODULEID);
@@ -994,7 +1004,7 @@ static int rfsimulator_read(openair0_device *device, openair0_timestamp *ptimest
               t->nextRxTstamp + nsamps);
         flushInput(t, 3, nsamps);
       }
-      if (loops++ > 10 && t->role == SIMU_ROLE_SERVER) {
+      if (t->hanging_workaround && loops++ > 10 && t->role == SIMU_ROLE_SERVER) {
         // Just start producing samples. The clients will catch up.
         have_to_wait = false;
         LOG_W(HW,