mirror of
https://gitlab.eurecom.fr/oai/openairinterface5g.git
synced 2026-07-13 04:30:28 +00:00
Compare commits
40 Commits
23796f054f
...
4bd0e3234d
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4bd0e3234d | ||
|
|
c4125d1c74 | ||
|
|
09c9bb998f | ||
|
|
6d4573905a | ||
|
|
dc01a3540a | ||
|
|
c3f6af1008 | ||
|
|
633b6de92a | ||
|
|
ad5e941bc9 | ||
|
|
75d7fa275d | ||
|
|
829d6fe7cf | ||
|
|
db7dbda8e2 | ||
|
|
f448088c8d | ||
|
|
a3268a9c2f | ||
|
|
716c29fe1f | ||
|
|
3cbf777835 | ||
|
|
813a90bd21 | ||
|
|
0548191fac | ||
|
|
145ab730c0 | ||
|
|
5f2108edb0 | ||
|
|
b7ab31854e | ||
|
|
f7d0ec0960 | ||
|
|
7032477784 | ||
|
|
7e0c0fe18b | ||
|
|
5a478deac4 | ||
|
|
40024d4caa | ||
|
|
3fb74dc66e | ||
|
|
82c3c04018 | ||
|
|
df5a9facbe | ||
|
|
71c692d727 | ||
|
|
159c219bdd | ||
|
|
ecfe769205 | ||
|
|
21329e1b70 | ||
|
|
0aa3f89c52 | ||
|
|
cd79eec3ec | ||
|
|
a1049e8dd1 | ||
|
|
28786af8bf | ||
|
|
eb1ae91051 | ||
|
|
0f50702483 | ||
|
|
7a0489842b | ||
|
|
a8de5c4c7a |
@@ -855,7 +855,6 @@ set(NR_PHY_SRC_RU
|
||||
${OPENAIR1_DIR}/PHY/MODULATION/nr_beamforming.c
|
||||
${OPENAIR1_DIR}/PHY/MODULATION/slot_fep_nr.c
|
||||
${OPENAIR1_DIR}/PHY/INIT/nr_init_ru.c
|
||||
${OPENAIR1_DIR}/PHY/if4_tools.c
|
||||
)
|
||||
|
||||
set(PHY_SRC_UE
|
||||
@@ -1769,7 +1768,8 @@ target_link_libraries(lte-uesoftmodem PRIVATE asn1_nr_rrc_hdrs asn1_lte_rrc_hdrs
|
||||
# force the generation of ASN.1 so that we don't need to wait during the build
|
||||
target_link_libraries(lte-uesoftmodem PRIVATE
|
||||
asn1_lte_rrc asn1_s1ap asn1_m2ap asn1_m3ap asn1_x2ap)
|
||||
# nr RRU
|
||||
|
||||
if (OAI_RU_FRONTHAUL)
|
||||
add_executable(nr-oru
|
||||
${OPENAIR_DIR}/executables/nr-ru.c
|
||||
${OPENAIR_DIR}/openair1/PHY/INIT/nr_parms.c
|
||||
@@ -1778,14 +1778,15 @@ add_executable(nr-oru
|
||||
${OPENAIR_DIR}/openair1/SCHED_NR/nr_ru_procedures.c
|
||||
${OPENAIR_DIR}/openair1/SCHED/phy_procedures_lte_common.c
|
||||
${OPENAIR_DIR}/executables/main_nr_ru.c
|
||||
${OPENAIR_DIR}/executables/nr-oru.c
|
||||
)
|
||||
target_link_libraries(nr-oru PRIVATE
|
||||
UTIL NR_PHY_RU PHY_NR shlib_loader dl
|
||||
UTIL NR_PHY_RU PHY_NR shlib_loader dl oru_fh MAC_NR_COMMON
|
||||
radio_common softmodem_common nfapi_pnf_lib)
|
||||
target_link_libraries(nr-oru PRIVATE pthread m CONFIG_LIB rt ${T_LIB} utils
|
||||
barrier actor nfapi_user_lib)
|
||||
target_link_libraries(nr-oru PRIVATE asn1_nr_rrc_hdrs asn1_lte_rrc_hdrs nr_phy_common time_management)
|
||||
|
||||
endif()
|
||||
# nr-softmodem
|
||||
###################################################
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<!-- SPDX-License-Identifier: CC-BY-4.0 -->
|
||||
|
||||
<h1 align="center">
|
||||
<a href="https://lfnetworking.org/projects/duranta/"><img src="https://raw.githubusercontent.com/duranta-project/governance/main/logos/Duranta-OAI-Combined.png" alt="Duranta OAI" width="550"></a>
|
||||
<a href="https://lfnetworking.org/projects/duranta/"><img src="https://raw.githubusercontent.com/duranta-project/governance/main/logos/Duranta-Logo-Color.png" alt="Duranta OAI" width="550"></a>
|
||||
</h1>
|
||||
|
||||
<p align="center">
|
||||
|
||||
@@ -14,7 +14,7 @@ description: A Helm chart for physical simulators network function
|
||||
# pipeline. Library charts do not define any templates and therefore cannot be deployed.
|
||||
type: application
|
||||
|
||||
icon: http://www.openairinterface.org/wp-content/uploads/2015/06/cropped-oai_final_logo.png
|
||||
icon: https://raw.githubusercontent.com/duranta-project/governance/main/logos/Duranta-Logo-Color.png
|
||||
|
||||
# This is the chart version. This version number should be incremented each time you make changes
|
||||
# to the chart and its templates, including the app version.
|
||||
|
||||
@@ -14,7 +14,7 @@ description: A Helm subchart for 4G physims network function
|
||||
# pipeline. Library charts do not define any templates and therefore cannot be deployed.
|
||||
type: application
|
||||
|
||||
icon: http://www.openairinterface.org/wp-content/uploads/2015/06/cropped-oai_final_logo.png
|
||||
icon: https://raw.githubusercontent.com/duranta-project/governance/main/logos/Duranta-Logo-Color.png
|
||||
|
||||
# This is the chart version. This version number should be incremented each time you make changes
|
||||
# to the chart and its templates, including the app version.
|
||||
|
||||
@@ -14,7 +14,7 @@ description: A Helm chart for physical simulators network function
|
||||
# pipeline. Library charts do not define any templates and therefore cannot be deployed.
|
||||
type: application
|
||||
|
||||
icon: http://www.openairinterface.org/wp-content/uploads/2015/06/cropped-oai_final_logo.png
|
||||
icon: https://raw.githubusercontent.com/duranta-project/governance/main/logos/Duranta-Logo-Color.png
|
||||
|
||||
# This is the chart version. This version number should be incremented each time you make changes
|
||||
# to the chart and its templates, including the app version.
|
||||
|
||||
@@ -14,7 +14,7 @@ description: A Helm subchart for 5G physims network function
|
||||
# pipeline. Library charts do not define any templates and therefore cannot be deployed.
|
||||
type: application
|
||||
|
||||
icon: http://www.openairinterface.org/wp-content/uploads/2015/06/cropped-oai_final_logo.png
|
||||
icon: https://raw.githubusercontent.com/duranta-project/governance/main/logos/Duranta-Logo-Color.png
|
||||
|
||||
# This is the chart version. This version number should be incremented each time you make changes
|
||||
# to the chart and its templates, including the app version.
|
||||
|
||||
@@ -75,12 +75,12 @@ class HTMLManagement():
|
||||
self.htmlFile.write(' <tr style="border-collapse: collapse; border: none;">\n')
|
||||
self.htmlFile.write(' <td style="border-collapse: collapse; border: none;">\n')
|
||||
self.htmlFile.write(' <a href="http://www.openairinterface.org/">\n')
|
||||
self.htmlFile.write(' <img src="http://www.openairinterface.org/wp-content/uploads/2016/03/cropped-oai_final_logo2.png" alt="" border="none" height=50 width=150>\n')
|
||||
self.htmlFile.write(' <img src="https://raw.githubusercontent.com/duranta-project/governance/main/logos/Duranta-Logo-Color.png" alt="" border="none" style="margin-right: 2rem;" width=150>\n')
|
||||
self.htmlFile.write(' </img>\n')
|
||||
self.htmlFile.write(' </a>\n')
|
||||
self.htmlFile.write(' </td>\n')
|
||||
self.htmlFile.write(' <td style="border-collapse: collapse; border: none; vertical-align: center;">\n')
|
||||
self.htmlFile.write(' <b><font size = "6">Job Summary -- Job: TEMPLATE_JOB_NAME -- Build-ID: TEMPLATE_BUILD_ID</font></b>\n')
|
||||
self.htmlFile.write(' <b><font size = "6">TEMPLATE_JOB_NAME -- Build-ID: TEMPLATE_BUILD_ID</font></b>\n')
|
||||
self.htmlFile.write(' </td>\n')
|
||||
self.htmlFile.write(' </tr>\n')
|
||||
self.htmlFile.write(' </table>\n')
|
||||
|
||||
@@ -1,20 +1,6 @@
|
||||
<!-- SPDX-License-Identifier: CC-BY-4.0 -->
|
||||
|
||||
<table style="border-collapse: collapse; border: none;">
|
||||
<tr style="border-collapse: collapse; border: none;">
|
||||
<td style="border-collapse: collapse; border: none;">
|
||||
<a href="http://www.openairinterface.org/">
|
||||
<img src="../../../doc/images/oai_final_logo.png" alt="" border=3 height=50 width=150>
|
||||
</img>
|
||||
</a>
|
||||
</td>
|
||||
<td style="border-collapse: collapse; border: none; vertical-align: center;">
|
||||
<b><font size = "5">OAI Full Stack 4G-LTE RF simulation with containers</font></b>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
This page is only valid for an `Ubuntu 22` host.
|
||||
OAI Full Stack 4G-LTE RF simulation with containers
|
||||
|
||||
**Table of Contents**
|
||||
|
||||
|
||||
@@ -1,22 +1,6 @@
|
||||
<!-- SPDX-License-Identifier: CC-BY-4.0 -->
|
||||
|
||||
<table style="border-collapse: collapse; border: none;">
|
||||
<tr style="border-collapse: collapse; border: none;">
|
||||
<td style="border-collapse: collapse; border: none;">
|
||||
<a href="http://www.openairinterface.org/">
|
||||
<img src="../../../doc/images/oai_final_logo.png" alt="" border=3 height=50 width=150>
|
||||
</img>
|
||||
</a>
|
||||
</td>
|
||||
<td style="border-collapse: collapse; border: none; vertical-align: center;">
|
||||
<b><font size = "5">OAI Full Stack 5G-NR RF simulation with containers</font></b>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
This page is only valid for an `Ubuntu 22` host.
|
||||
|
||||
**NOTE: this version (2023-01-27) has been updated for the `v1.5.0` version of the `OAI 5G CN`.**
|
||||
OAI Full Stack 5G-NR RF simulation with containers
|
||||
|
||||
**Table of Contents**
|
||||
|
||||
|
||||
@@ -1,18 +1,6 @@
|
||||
<!-- SPDX-License-Identifier: CC-BY-4.0 -->
|
||||
|
||||
<table style="border-collapse: collapse; border: none;">
|
||||
<tr style="border-collapse: collapse; border: none;">
|
||||
<td style="border-collapse: collapse; border: none;">
|
||||
<a href="http://www.openairinterface.org/">
|
||||
<img src="../../../doc/images/oai_final_logo.png" alt="" border=3 height=50 width=150>
|
||||
</img>
|
||||
</a>
|
||||
</td>
|
||||
<td style="border-collapse: collapse; border: none; vertical-align: center;">
|
||||
<b><font size = "5">OAI O-RAN 7.2 Front-haul Docker Compose</font></b>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
OAI O-RAN 7.2 Front-haul Docker Compose
|
||||
|
||||

|
||||
|
||||
|
||||
@@ -201,6 +201,7 @@ function main() {
|
||||
shift;;
|
||||
--nrRU)
|
||||
NRRU=1
|
||||
CMAKE_CMD="$CMAKE_CMD -DOAI_RU_FRONTHAUL=ON"
|
||||
TARGET_LIST="$TARGET_LIST nr-oru"
|
||||
echo_info "Will compile NR O-RU"
|
||||
shift;;
|
||||
|
||||
@@ -39,8 +39,9 @@ typedef enum { NON_DYNAMIC, DYNAMIC } fiveQI_t;
|
||||
/* 5QI (5G QoS Identifier) - 3GPP TS 23.501 §5.7.2.1
|
||||
* Range: 0..255
|
||||
* - Standardized 5QI values: have one-to-one mapping to standardized 5G QoS characteristics (Table 5.7.4-1)
|
||||
* - Pre-configured 5QI values: pre-configured in the AN
|
||||
* - Dynamically assigned 5QI values: require signaling of QoS characteristics as part of QoS profile */
|
||||
* - Pre-configured 5QI values: pre-configured in the AN (not in Table 5.7.4-1)
|
||||
* - Dynamically assigned 5QI values: require signaling of QoS characteristics as part of QoS profile
|
||||
* OAI implements standardized non-dynamic 5QI only. */
|
||||
#define MIN_FIVEQI 0
|
||||
#define MAX_STANDARDIZED_FIVEQI 90
|
||||
#define MAX_FIVEQI 255
|
||||
|
||||
@@ -45,6 +45,8 @@
|
||||
|
||||
#define NR_MAX_NB_PDU_SESSIONS (256)
|
||||
|
||||
#define NR_MAX_NB_ALLOWED_SNSSAI (8) /* Maximum number of allowed S-NSSAI in TS 38.413 */
|
||||
|
||||
#define MAX_DRBS_PER_UE (32) /* Maximum number of Data Radio Bearers per UE
|
||||
* defined for NGAP in TS 38.413 - maxnoofDRBs */
|
||||
#define MAX_PDUS_PER_UE (8) /* Maximum number of PDU Sessions per UE */
|
||||
|
||||
@@ -108,6 +108,7 @@ typedef struct {
|
||||
sdu_size_t head_sdu_remaining_size_to_send; /*!< \brief remaining size of sdu: could be the total size or the remaining size of
|
||||
already segmented sdu */
|
||||
bool head_sdu_is_segmented; /*!< \brief 0 if head SDU has not been segmented, 1 if already segmented */
|
||||
uint64_t oldest_sdu_arrival_ms; /*!< \brief Timestamp (ms, RLC time) of oldest SDU in tx/retx queue, 0 if empty */
|
||||
} mac_rlc_status_resp_t;
|
||||
|
||||
#define SDU_CONFIRM_NO false
|
||||
|
||||
@@ -51,7 +51,7 @@ PROJECT_BRIEF = "Full experimental OpenSource LTE and NR implementation
|
||||
# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy
|
||||
# the logo to the output directory.
|
||||
|
||||
PROJECT_LOGO = @CMAKE_SOURCE_DIR@/doc/images/oai_logo.png
|
||||
PROJECT_LOGO =
|
||||
|
||||
# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path
|
||||
# into which the generated documentation will be written. If a relative path is
|
||||
|
||||
@@ -61,7 +61,7 @@ Correct (Markdown):
|
||||
**Bold text**
|
||||
*Italic text*
|
||||
|
||||
 instead of <img src="./images/oai_final_logo.png" alt="OAI Logo">
|
||||

|
||||
|
||||
| Feature | Description |
|
||||
|---------------|---------------------------------|
|
||||
@@ -75,7 +75,7 @@ Incorrect (HTML):
|
||||
<b>Bold text</b>
|
||||
<i>Italic text</i>
|
||||
|
||||
<img src="./images/oai_final_logo.png" alt="OAI Logo">
|
||||
<img src="./path/to/image.png" alt="some image">
|
||||
|
||||
<table style="border-collapse: collapse; border: none;">
|
||||
<tr>
|
||||
@@ -114,7 +114,7 @@ Incorrect (HTML):
|
||||
is](https://www.kernel.org/doc/html/latest/process/submitting-patches.html#separate-your-changes).
|
||||
- See [OAI CN5G configuration files](#22-oai-cn5g-configuration-files) for
|
||||
details.
|
||||
- 
|
||||
- 
|
||||
```
|
||||
|
||||
## Inline code for technical elements
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 271 KiB After Width: | Height: | Size: 161 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 361 KiB After Width: | Height: | Size: 209 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 24 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 9.4 KiB |
@@ -1,20 +1,6 @@
|
||||
<!-- SPDX-License-Identifier: CC-BY-4.0 -->
|
||||
|
||||
<table style="border-collapse: collapse; border: none;">
|
||||
<tr style="border-collapse: collapse; border: none;">
|
||||
<td style="border-collapse: collapse; border: none;">
|
||||
<a href="http://www.openairinterface.org/">
|
||||
<img src="../doc/images/oai_final_logo.png" alt="" border=3 height=50 width=150>
|
||||
</img>
|
||||
</a>
|
||||
</td>
|
||||
<td style="border-collapse: collapse; border: none; vertical-align: center;">
|
||||
<b><font size = "5">OAI Docker/Podman Build and Usage Procedures</font></b>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
---
|
||||
OAI Docker/Podman Build and Usage Procedures
|
||||
|
||||
**Table of Contents**
|
||||
|
||||
|
||||
@@ -22,6 +22,9 @@
|
||||
#include <executables/softmodem-common.h>
|
||||
#include <executables/thread-common.h>
|
||||
#include "executables/nr-softmodem.h"
|
||||
#include "nr-oru.h"
|
||||
#include "openair1/PHY/INIT/nr_phy_init.h"
|
||||
#include "openair1/SCHED_NR/sched_nr.h"
|
||||
|
||||
pthread_cond_t sync_cond;
|
||||
pthread_mutex_t sync_mutex;
|
||||
@@ -33,6 +36,14 @@ int sf_ahead = 4;
|
||||
int emulate_rf = 0;
|
||||
|
||||
RAN_CONTEXT_t RC;
|
||||
|
||||
extern void kill_NR_RU_proc(int inst);
|
||||
extern void set_function_spec_param(RU_t *ru);
|
||||
extern void start_NR_RU();
|
||||
extern void init_NR_RU(configmodule_interface_t *cfg, char *);
|
||||
void fill_rf_config(RU_t *ru, char *rf_config_file);
|
||||
void fill_split7_2_config(split7_config_t *split7, const nfapi_nr_config_request_scf_t *config, const NR_DL_FRAME_PARMS *fp);
|
||||
|
||||
int64_t uplink_frequency_offset[MAX_NUM_CCs][4];
|
||||
|
||||
void nfapi_setmode(nfapi_mode_t nfapi_mode)
|
||||
@@ -106,26 +117,15 @@ struct timespec timespec_sub(struct timespec, struct timespec)
|
||||
struct timespec t = {0};
|
||||
return t;
|
||||
};
|
||||
|
||||
void perform_symbol_rotation(const int nsymb, const int numerology_index, double f0, c16_t *symbol_rotation)
|
||||
{
|
||||
return;
|
||||
}
|
||||
void init_timeshift_rotation(const int ofdm_symbol_size,
|
||||
const int nb_prefix_samples,
|
||||
const uint ofdm_offset_divisor,
|
||||
c16_t *timeshift_symbol_rotation)
|
||||
{
|
||||
return;
|
||||
};
|
||||
int beam_index_allocation(bool das,
|
||||
int fapi_beam_index,
|
||||
NR_gNB_COMMON *common_vars,
|
||||
int slot,
|
||||
void beam_index_allocation(uint16_t fapi_beam_index,
|
||||
int ant,
|
||||
int num_ports,
|
||||
int symbols_per_slot,
|
||||
int bitmap_symbols)
|
||||
int slot,
|
||||
uint16_t bitmap_symbols,
|
||||
int num_ant_max,
|
||||
uint16_t **ant_beam_id_list)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
uint16_t get_first_ant_idx(bool das, uint16_t num_ports_beams, uint16_t beam_id, uint16_t fapi_start_port)
|
||||
{
|
||||
@@ -135,6 +135,12 @@ void nr_fill_du(uint16_t N_ZC, const uint16_t *prach_root_sequence_map, uint16_t
|
||||
{
|
||||
return;
|
||||
};
|
||||
|
||||
static void sig_handler(int sig_num)
|
||||
{
|
||||
oai_exit = 1;
|
||||
}
|
||||
|
||||
uint16_t nr_du[838];
|
||||
|
||||
uint64_t downlink_frequency[MAX_NUM_CCs][4];
|
||||
@@ -171,6 +177,7 @@ int main(int argc, char **argv)
|
||||
printf("About to Init RU threads\n");
|
||||
|
||||
lock_memory_to_ram();
|
||||
load_dftslib();
|
||||
|
||||
RC.nb_RU = 1;
|
||||
RC.ru = malloc(sizeof(RC.ru));
|
||||
@@ -178,24 +185,77 @@ int main(int argc, char **argv)
|
||||
init_NR_RU(config_get_if(), NULL);
|
||||
|
||||
RU_t *ru = RC.ru[0];
|
||||
ORU_t oru = {0};
|
||||
oru.ru = ru;
|
||||
int ret = get_oru_options(&oru);
|
||||
AssertFatal(ret == 0, "Cannot configure oru, check your config file/cmdline");
|
||||
ru->numerology = oru.numerology;
|
||||
oru_init_frame_parms(&oru);
|
||||
NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms;
|
||||
nr_dump_frame_parms(fp);
|
||||
init_symbol_rotation(fp);
|
||||
init_timeshift_rotation(fp->ofdm_symbol_size, fp->nb_prefix_samples, fp->ofdm_offset_divisor, fp->timeshift_symbol_rotation);
|
||||
ru->if_south = LOCAL_RF;
|
||||
nr_phy_init_RU(oru.ru);
|
||||
fill_rf_config(ru, ru->rf_config_file);
|
||||
ru->N_TA_offset = set_default_nta_offset(fp->freq_range, fp->samples_per_subframe);
|
||||
|
||||
while (oai_exit == 0)
|
||||
/* set PRACH configuration */
|
||||
nfapi_nr_prach_config_t *prach_config = &ru->config.prach_config;
|
||||
prach_config->prach_ConfigurationIndex.value = oru.prach_config_index;
|
||||
prach_config->num_prach_fd_occasions_list[0].k1.value = oru.prach_msg1_freq;
|
||||
prach_config->prach_sequence_length.value = 1;
|
||||
prach_config->prach_sub_c_spacing.value = 1;
|
||||
prach_config->num_prach_fd_occasions.value = 1;
|
||||
|
||||
reset_meas(&oru.rx_prach);
|
||||
oru.prach_info = get_nr_prach_occasion_info_from_index(oru.prach_config_index, FR1, fp->frame_type);
|
||||
LOG_A(PHY, "PRACH configuration index %d\n", oru.prach_config_index);
|
||||
LOG_A(PHY,
|
||||
"PRACH format %d start_symbol %d duration %d\n",
|
||||
oru.prach_info.format,
|
||||
oru.prach_info.start_symbol,
|
||||
oru.prach_info.N_dur);
|
||||
prepare_prach_item(&oru);
|
||||
|
||||
oru.fronthaul = oru_fh_init(&oru.fh_config);
|
||||
AssertFatal(oru.fronthaul != NULL, "Cannot configure oru fronthaul, check your config file/cmdline");
|
||||
|
||||
LOG_I(PHY, "starting vrtsim\n");
|
||||
ret = openair0_load(&ru->rfdevice, "vrtsim", &ru->openair0_cfg, NULL);
|
||||
AssertFatal(ret == 0, "RU %u: openair0_load() ret %d: cannot initialize vrtsim\n", ru->idx, ret);
|
||||
ret = ru->rfdevice.trx_start_func(&ru->rfdevice);
|
||||
AssertFatal(ret == 0, "RU %u: trx_start_func() ret %d: cannot start vrtsim\n", ru->idx, ret);
|
||||
|
||||
threadCreate(&oru.north_read_thread, oru_north_read_thread, (void *)&oru, "north_read_thread", -1, OAI_PRIORITY_RT_MAX);
|
||||
threadCreate(&oru.south_read_thread, oru_south_read_thread, (void *)&oru, "south_read_thread", -1, OAI_PRIORITY_RT_MAX);
|
||||
usleep(1000);
|
||||
oru_fh_start(oru.fronthaul);
|
||||
|
||||
// Signal handler
|
||||
signal(SIGINT, sig_handler);
|
||||
|
||||
while (oai_exit == 0) {
|
||||
oru_fh_print_stats(oru.fronthaul);
|
||||
sleep(1);
|
||||
// stop threads
|
||||
}
|
||||
|
||||
kill_NR_RU_proc(0);
|
||||
pthread_join(oru.north_read_thread, NULL);
|
||||
pthread_join(oru.south_read_thread, NULL);
|
||||
|
||||
end_configmodule(uniqCfg);
|
||||
oru_fh_stop(oru.fronthaul);
|
||||
|
||||
if (ru->rfdevice.trx_stop_func) {
|
||||
ru->rfdevice.trx_stop_func(&ru->rfdevice);
|
||||
ru->rfdevice.trx_stop_func = NULL;
|
||||
}
|
||||
|
||||
if (ru->rfdevice.trx_end_func) {
|
||||
ru->rfdevice.trx_end_func(&ru->rfdevice);
|
||||
ru->rfdevice.trx_end_func = NULL;
|
||||
}
|
||||
|
||||
if (ru->ifdevice.trx_end_func) {
|
||||
ru->ifdevice.trx_end_func(&ru->ifdevice);
|
||||
ru->ifdevice.trx_end_func = NULL;
|
||||
}
|
||||
end_configmodule(uniqCfg);
|
||||
|
||||
logClean();
|
||||
printf("Bye.\n");
|
||||
|
||||
766
executables/nr-oru.c
Normal file
766
executables/nr-oru.c
Normal file
@@ -0,0 +1,766 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: LicenseRef-CSSL-1.0
|
||||
*/
|
||||
|
||||
#include "common/config/config_userapi.h"
|
||||
#include "common/utils/system.h"
|
||||
#include "nr-oru.h"
|
||||
#include "openair1/PHY/defs_nr_common.h"
|
||||
#include "PHY/NR_TRANSPORT/nr_transport_proto.h"
|
||||
#include "oru_packet_processor.h"
|
||||
#include <time.h>
|
||||
#include "openair1/PHY/MODULATION/nr_modulation.h"
|
||||
#include "openair1/SCHED_NR/sched_nr.h"
|
||||
#include "openair1/PHY/MODULATION/modulation_common.h"
|
||||
#include "openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.h"
|
||||
|
||||
#define CONFIG_SECTION_ORU "ORUs.[0]"
|
||||
|
||||
#define CONFIG_STRING_ORU_TX_BW_LIST "tx_bw"
|
||||
#define CONFIG_STRING_ORU_RX_BW_LIST "rx_bw"
|
||||
#define CONFIG_STRING_ORU_CARRIER_TX_LIST "carrier_tx"
|
||||
#define CONFIG_STRING_ORU_CARRIER_RX_LIST "carrier_rx"
|
||||
#define CONFIG_STRING_ORU_FRAME_TYPE "frame_type"
|
||||
#define CONFIG_STRING_ORU_PRACH_CONFIGID "prach_config_index"
|
||||
#define CONFIG_STRING_ORU_PRACH_MSG1FREQ "prach_msg1_start"
|
||||
#define CONFIG_STRING_ORU_NUMEROLOGY "mu"
|
||||
#define CONFIG_STRING_ORU_TDD_PERIOD "tdd_period"
|
||||
#define CONFIG_STRING_ORU_NUM_DL_SLOTS "num_dl_slots"
|
||||
#define CONFIG_STRING_ORU_NUM_UL_SLOTS "num_ul_slots"
|
||||
#define CONFIG_STRING_ORU_NUM_DL_SYMBOLS "num_dl_symbols"
|
||||
#define CONFIG_STRING_ORU_NUM_UL_SYMBOLS "num_ul_symbols"
|
||||
|
||||
#define HLP_ORU_TX_BW "set the TX bandwidth list per component carrier"
|
||||
#define HLP_ORU_RX_BW "set the RX bandwidth list per component carrier"
|
||||
#define HLP_ORU_CARRIER_TX "set the TX carrier frequencies per component carrier"
|
||||
#define HLP_ORU_CARRIER_RX "set the RX carrier frequencies per component carrier"
|
||||
#define HLP_ORU_FRAMETYPE "set the Frame type TDD/FDD of all component carriers"
|
||||
#define HLP_ORU_PRACH_CONFIGID "set the PRACH configuration id of all component carriers"
|
||||
#define HLP_ORU_PRACH_MSG1FREQ "set the PRACH MSG1 frequency of all component carriers"
|
||||
#define HLP_ORU_NUMEROLOGY "set the numerology of the RU"
|
||||
#define HLP_ORU_TDD_PERIOD "set the 3GPP TDD periodificty 0-9"
|
||||
#define HLP_ORU_NUM_DL_SLOTS "set the number of DL Slots in TDD"
|
||||
#define HLP_ORU_NUM_UL_SLOTS "set the number of UL Slots in TDD"
|
||||
#define HLP_ORU_NUM_DL_SYMBOLS "set the number of DL symbols in the mixed slot"
|
||||
#define HLP_ORU_NUM_UL_SYMBOLS "set the number of UL symbols in the mixed slot"
|
||||
|
||||
// clang-format off
|
||||
#define CMDLINE_PARAMS_DESC_ORU \
|
||||
{ \
|
||||
{CONFIG_STRING_ORU_TX_BW_LIST, HLP_ORU_TX_BW, 0, .iptr=NULL, .defintarrayval=DEFBW, TYPE_INTARRAY, 0}, \
|
||||
{CONFIG_STRING_ORU_RX_BW_LIST, HLP_ORU_RX_BW, 0, .iptr=NULL, .defintarrayval=DEFBW, TYPE_INTARRAY, 0}, \
|
||||
{CONFIG_STRING_ORU_CARRIER_TX_LIST, HLP_ORU_CARRIER_TX, 0, .iptr=NULL, .defintarrayval=DEFCARRIER, TYPE_INTARRAY, 0}, \
|
||||
{CONFIG_STRING_ORU_CARRIER_RX_LIST, HLP_ORU_CARRIER_RX, 0, .iptr=NULL, .defintarrayval=DEFCARRIER, TYPE_INTARRAY, 0}, \
|
||||
{CONFIG_STRING_ORU_FRAME_TYPE, HLP_ORU_FRAMETYPE, 0, .uptr=NULL, .defintval=1, TYPE_UINT, 0}, \
|
||||
{CONFIG_STRING_ORU_PRACH_CONFIGID, HLP_ORU_PRACH_CONFIGID, 0, .uptr=NULL, .defintval=152, TYPE_UINT, 0}, \
|
||||
{CONFIG_STRING_ORU_PRACH_MSG1FREQ, HLP_ORU_PRACH_MSG1FREQ, 0, .uptr=NULL, .defintval=0, TYPE_UINT, 0}, \
|
||||
{CONFIG_STRING_ORU_NUMEROLOGY, HLP_ORU_NUMEROLOGY, 0, .uptr=NULL, .defintval=1, TYPE_UINT, 0}, \
|
||||
{CONFIG_STRING_ORU_TDD_PERIOD, HLP_ORU_TDD_PERIOD, 0, .uptr=NULL, .defintval=5, TYPE_UINT, 0}, \
|
||||
{CONFIG_STRING_ORU_NUM_DL_SLOTS, HLP_ORU_NUM_DL_SLOTS, 0, .uptr=NULL, .defintval=3, TYPE_UINT, 0}, \
|
||||
{CONFIG_STRING_ORU_NUM_UL_SLOTS, HLP_ORU_NUM_UL_SLOTS, 0, .uptr=NULL, .defintval=1, TYPE_UINT, 0}, \
|
||||
{CONFIG_STRING_ORU_NUM_DL_SYMBOLS, HLP_ORU_NUM_DL_SYMBOLS, 0, .uptr=NULL, .defintval=7, TYPE_UINT, 0}, \
|
||||
{CONFIG_STRING_ORU_NUM_UL_SYMBOLS, HLP_ORU_NUM_UL_SYMBOLS, 0, .uptr=NULL, .defintval=3, TYPE_UINT, 0}, \
|
||||
}
|
||||
// clang-format on
|
||||
|
||||
#define CONFIG_SECTION_ORU_FH "ORUs.[0].fronthaul"
|
||||
|
||||
#define CONFIG_STRING_ORU_DPDK_DEVICES "dpdk_devices"
|
||||
#define CONFIG_STRING_RX_CORE "rx_core"
|
||||
#define CONFIG_STRING_EXTRA_EAL_ARGS "extra_eal_args"
|
||||
#define CONFIG_STRING_DU_MAC_ADDRESSES "du_mac_addr"
|
||||
#define CONFIG_STRING_MTU "mtu"
|
||||
#define CONFIG_STRING_T2A_UP "T2a_up"
|
||||
#define CONFIG_STRING_T2A_CP "T2a_cp"
|
||||
#define CONFIG_STRING_PRACH_EAXC_OFFSET "prach_eaxc_offset"
|
||||
|
||||
#define HLP_DPDK_DEVICES "DPDK devices to use for the O-RU."
|
||||
#define HLP_RX_CORE "The CPU core to be used to deploy dpdk RX worker for O-RU."
|
||||
#define HLP_EXTRA_EAL_ARGS "Extra arguments passed to RTE_EAL_INIT."
|
||||
#define HLP_DU_MAC_ADDRESSES "DU MAC addreses, used to prepare Ethernet headers."
|
||||
#define HLP_MTU "MTU for RX and TX."
|
||||
#define HLP_PRACH_EAXC_OFFSET "PRACH eAxC offset."
|
||||
|
||||
// clang-format off
|
||||
#define CMDLINE_PARAMS_DESC_ORU_FH \
|
||||
{ \
|
||||
{CONFIG_STRING_ORU_DPDK_DEVICES, HLP_DPDK_DEVICES, PARAMFLAG_MANDATORY, .strptr=NULL, .defstrval=NULL, TYPE_STRINGLIST, 0}, \
|
||||
{CONFIG_STRING_RX_CORE, HLP_RX_CORE, PARAMFLAG_MANDATORY, .iptr=NULL, .defintval=-1, TYPE_INT, 0}, \
|
||||
{CONFIG_STRING_EXTRA_EAL_ARGS, HLP_EXTRA_EAL_ARGS, 0, .strptr=NULL, .defstrval=NULL, TYPE_STRINGLIST, 0}, \
|
||||
{CONFIG_STRING_DU_MAC_ADDRESSES, HLP_DU_MAC_ADDRESSES, PARAMFLAG_MANDATORY, .strptr=NULL, .defstrval=NULL, TYPE_STRINGLIST, 0}, \
|
||||
{CONFIG_STRING_MTU, HLP_MTU, 0, .iptr=NULL, .defintval=9600, TYPE_INT, 0}, \
|
||||
{CONFIG_STRING_T2A_UP, "", 0, .iptr=NULL, .defintarrayval=NULL, TYPE_INTARRAY, 0}, \
|
||||
{CONFIG_STRING_T2A_CP, "", 0, .iptr=NULL, .defintarrayval=NULL, TYPE_INTARRAY, 0}, \
|
||||
{CONFIG_STRING_PRACH_EAXC_OFFSET, HLP_PRACH_EAXC_OFFSET, 0, .iptr=NULL, .defintval=0, TYPE_INT, 0} \
|
||||
}
|
||||
// clang-format on
|
||||
|
||||
extern void set_scs_parameters(NR_DL_FRAME_PARMS *fp, int mu, int N_RB_DL, int ssb_case);
|
||||
void tx_rf_symbols(RU_t *ru, int frame, int slot, uint64_t timestamp, int start_symbol, int num_symbols);
|
||||
|
||||
void prepare_prach_item(ORU_t *oru)
|
||||
{
|
||||
AssertFatal(oru->ru != NULL, "ORU not configured\n");
|
||||
AssertFatal(oru->ru->nr_frame_parms != NULL, "ORU not configured\n");
|
||||
NR_DL_FRAME_PARMS *fp = oru->ru->nr_frame_parms;
|
||||
RU_t *ru = oru->ru;
|
||||
prach_item_t *prach_item = &oru->prach_item;
|
||||
prach_item->num_slots = oru->prach_info.format < 4 ? get_long_prach_dur(oru->prach_info.format, fp->numerology_index) : 1;
|
||||
prach_item->msg1_frequencystart = oru->prach_msg1_freq;
|
||||
prach_item->mu = fp->numerology_index;
|
||||
nfapi_nr_config_request_scf_t *cfg = &ru->config;
|
||||
prach_item->prach_sequence_length = cfg->prach_config.prach_sequence_length.value;
|
||||
prach_item->restricted_set = 0;
|
||||
prach_item->numerology_index = fp->numerology_index;
|
||||
prach_item->nb_rx = ru->nb_rx;
|
||||
prach_item->rx_prach = &oru->rx_prach;
|
||||
|
||||
// Fill PRACH PDU
|
||||
nfapi_nr_prach_pdu_t *prach_pdu = &prach_item->pdu;
|
||||
prach_pdu->prach_start_symbol = oru->prach_info.start_symbol;
|
||||
prach_pdu->num_prach_ocas = 1; // TODO: Hardcoded.
|
||||
|
||||
uint16_t format0 = oru->prach_info.format & 0xff;
|
||||
uint16_t format1 = (oru->prach_info.format >> 8) & 0xff;
|
||||
if (format1 != 0xff) {
|
||||
switch (format0) {
|
||||
case 0xa1:
|
||||
prach_pdu->prach_format = 11;
|
||||
break;
|
||||
case 0xa2:
|
||||
prach_pdu->prach_format = 12;
|
||||
break;
|
||||
case 0xa3:
|
||||
prach_pdu->prach_format = 13;
|
||||
break;
|
||||
default:
|
||||
AssertFatal(1 == 0, "Only formats A1/B1 A2/B2 A3/B3 are valid for dual format");
|
||||
}
|
||||
} else {
|
||||
switch (format0) {
|
||||
case 0:
|
||||
prach_pdu->prach_format = 0;
|
||||
break;
|
||||
case 1:
|
||||
prach_pdu->prach_format = 1;
|
||||
break;
|
||||
case 2:
|
||||
prach_pdu->prach_format = 2;
|
||||
break;
|
||||
case 3:
|
||||
prach_pdu->prach_format = 3;
|
||||
break;
|
||||
case 0xa1:
|
||||
prach_pdu->prach_format = 4;
|
||||
break;
|
||||
case 0xa2:
|
||||
prach_pdu->prach_format = 5;
|
||||
break;
|
||||
case 0xa3:
|
||||
prach_pdu->prach_format = 6;
|
||||
break;
|
||||
case 0xb1:
|
||||
prach_pdu->prach_format = 7;
|
||||
break;
|
||||
case 0xb4:
|
||||
prach_pdu->prach_format = 8;
|
||||
break;
|
||||
case 0xc0:
|
||||
prach_pdu->prach_format = 9;
|
||||
break;
|
||||
case 0xc2:
|
||||
prach_pdu->prach_format = 10;
|
||||
break;
|
||||
default:
|
||||
AssertFatal(1 == 0, "Invalid PRACH format");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int get_oru_options(ORU_t *oru)
|
||||
{
|
||||
int DEFBW[] = {273};
|
||||
int DEFCARRIER[] = {3430560};
|
||||
paramdef_t param[] = CMDLINE_PARAMS_DESC_ORU;
|
||||
int nump = sizeofArray(param);
|
||||
|
||||
int ret = config_get(config_get_if(), param, nump, CONFIG_SECTION_ORU);
|
||||
if (ret <= 0) {
|
||||
LOG_E(NR_PHY, "problem reading section \"%s\"\n", CONFIG_SECTION_ORU);
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (int i = 0; i < oru->ru->num_bands; i++) {
|
||||
oru->bw_tx[i] = gpd(param, nump, CONFIG_STRING_ORU_TX_BW_LIST)->iptr[i];
|
||||
oru->bw_rx[i] = gpd(param, nump, CONFIG_STRING_ORU_RX_BW_LIST)->iptr[i];
|
||||
oru->carrier_freq_tx[i] = gpd(param, nump, CONFIG_STRING_ORU_CARRIER_TX_LIST)->iptr[i];
|
||||
oru->carrier_freq_rx[i] = gpd(param, nump, CONFIG_STRING_ORU_CARRIER_RX_LIST)->iptr[i];
|
||||
}
|
||||
oru->frame_type = *gpd(param, nump, CONFIG_STRING_ORU_FRAME_TYPE)->iptr;
|
||||
oru->prach_config_index = *gpd(param, nump, CONFIG_STRING_ORU_PRACH_CONFIGID)->iptr;
|
||||
oru->prach_msg1_freq = *gpd(param, nump, CONFIG_STRING_ORU_PRACH_MSG1FREQ)->iptr;
|
||||
oru->numerology = *gpd(param, nump, CONFIG_STRING_ORU_NUMEROLOGY)->iptr;
|
||||
oru->tdd_period = *gpd(param, nump, CONFIG_STRING_ORU_TDD_PERIOD)->iptr;
|
||||
oru->num_DL_slots = *gpd(param, nump, CONFIG_STRING_ORU_NUM_DL_SLOTS)->iptr;
|
||||
oru->num_UL_slots = *gpd(param, nump, CONFIG_STRING_ORU_NUM_UL_SLOTS)->iptr;
|
||||
oru->num_DL_symbols = *gpd(param, nump, CONFIG_STRING_ORU_NUM_DL_SYMBOLS)->iptr;
|
||||
oru->num_UL_symbols = *gpd(param, nump, CONFIG_STRING_ORU_NUM_UL_SYMBOLS)->iptr;
|
||||
|
||||
paramdef_t fh_param[] = CMDLINE_PARAMS_DESC_ORU_FH;
|
||||
nump = sizeofArray(fh_param);
|
||||
oru_fh_config_t *fh_cfg = &oru->fh_config;
|
||||
ret = config_get(config_get_if(), fh_param, nump, CONFIG_SECTION_ORU_FH);
|
||||
if (ret <= 0) {
|
||||
printf("problem reading section \"%s\"\n", CONFIG_SECTION_ORU_FH);
|
||||
return -1;
|
||||
}
|
||||
|
||||
oru_fh_dpdk_config_t *dpdk_conf = &fh_cfg->dpdk_conf;
|
||||
int num_dpdk_devices = gpd(fh_param, nump, CONFIG_STRING_ORU_DPDK_DEVICES)->numelt;
|
||||
dpdk_conf->num_dpdk_devices = num_dpdk_devices;
|
||||
AssertFatal(num_dpdk_devices > 0 && num_dpdk_devices <= 2,
|
||||
"Invalid number of DPDK devices (%d). Configure 1 or 2 devices\n",
|
||||
num_dpdk_devices);
|
||||
for (int i = 0; i < num_dpdk_devices; i++) {
|
||||
dpdk_conf->dpdk_devices[i] = gpd(fh_param, nump, CONFIG_STRING_ORU_DPDK_DEVICES)->strlistptr[i];
|
||||
}
|
||||
dpdk_conf->extra_eal_args = gpd(fh_param, nump, CONFIG_STRING_EXTRA_EAL_ARGS)->strlistptr;
|
||||
dpdk_conf->num_extra_eal_args = gpd(fh_param, nump, CONFIG_STRING_EXTRA_EAL_ARGS)->numelt;
|
||||
|
||||
fh_cfg->num_du_mac_addrs = gpd(fh_param, nump, CONFIG_STRING_DU_MAC_ADDRESSES)->numelt;
|
||||
for (int i = 0; i < fh_cfg->num_du_mac_addrs; i++) {
|
||||
fh_cfg->du_mac_addrs[i] = gpd(fh_param, nump, CONFIG_STRING_DU_MAC_ADDRESSES)->strlistptr[i];
|
||||
AssertFatal(strlen(fh_cfg->du_mac_addrs[i]) == 17, "Invalid MAC address\n");
|
||||
}
|
||||
fh_cfg->enable_compression = false;
|
||||
fh_cfg->rx_core = *gpd(fh_param, nump, CONFIG_STRING_RX_CORE)->iptr;
|
||||
fh_cfg->mtu = *gpd(fh_param, nump, CONFIG_STRING_MTU)->iptr;
|
||||
fh_cfg->num_prbs = oru->bw_tx[0];
|
||||
fh_cfg->numerology = oru->numerology;
|
||||
fh_cfg->prach_eaxc_offset = *gpd(fh_param, nump, CONFIG_STRING_PRACH_EAXC_OFFSET)->iptr;
|
||||
|
||||
AssertFatal(gpd(fh_param, nump, CONFIG_STRING_T2A_UP)->numelt == 2, "Two parameters required for %s\n", CONFIG_STRING_T2A_UP);
|
||||
fh_cfg->T2a_up_min_uS = gpd(fh_param, nump, CONFIG_STRING_T2A_UP)->iptr[0];
|
||||
fh_cfg->T2a_up_max_uS = gpd(fh_param, nump, CONFIG_STRING_T2A_UP)->iptr[1];
|
||||
AssertFatal(fh_cfg->T2a_up_min_uS <= fh_cfg->T2a_up_max_uS,
|
||||
"T2a max (%d) has to be greater than T2a min (%d)\n",
|
||||
fh_cfg->T2a_up_max_uS,
|
||||
fh_cfg->T2a_up_min_uS);
|
||||
|
||||
AssertFatal(gpd(fh_param, nump, CONFIG_STRING_T2A_CP)->numelt == 2, "Two parameters required for %s\n", CONFIG_STRING_T2A_CP);
|
||||
fh_cfg->T2a_cp_min_uS = gpd(fh_param, nump, CONFIG_STRING_T2A_CP)->iptr[0];
|
||||
fh_cfg->T2a_cp_max_uS = gpd(fh_param, nump, CONFIG_STRING_T2A_CP)->iptr[1];
|
||||
AssertFatal(fh_cfg->T2a_cp_min_uS <= fh_cfg->T2a_cp_max_uS,
|
||||
"T2a max (%d) has to be greater than T2a min (%d)\n",
|
||||
fh_cfg->T2a_cp_max_uS,
|
||||
fh_cfg->T2a_cp_min_uS);
|
||||
|
||||
oru_fh_tdd_pattern_t *tdd_pattern = &fh_cfg->tdd_pattern;
|
||||
tdd_pattern->num_dl_slots = oru->num_DL_slots;
|
||||
tdd_pattern->num_ul_slots = oru->num_UL_slots;
|
||||
tdd_pattern->num_dl_symbols = oru->num_DL_symbols;
|
||||
tdd_pattern->num_ul_symbols = oru->num_UL_symbols;
|
||||
int num_slots_frame = (1 << oru->numerology) * NR_NUMBER_OF_SUBFRAMES_PER_FRAME;
|
||||
int num_period_frame = get_nb_periods_per_frame(oru->tdd_period);
|
||||
int num_slots_period = num_slots_frame / num_period_frame;
|
||||
tdd_pattern->tdd_pattern_length_slots = num_slots_period;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void oru_init_frame_parms(ORU_t *oru)
|
||||
{
|
||||
RU_t *ru = oru->ru;
|
||||
NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms;
|
||||
|
||||
fp->frame_type = oru->frame_type;
|
||||
ru->config.cell_config.frame_duplex_type.value = oru->frame_type;
|
||||
ru->config.cell_config.frame_duplex_type.tl.tag = 0x100D;
|
||||
fp->N_RB_DL = oru->bw_tx[0];
|
||||
ru->config.ssb_config.scs_common.value = ru->numerology;
|
||||
ru->config.carrier_config.dl_grid_size[ru->config.ssb_config.scs_common.value].value = oru->bw_tx[0];
|
||||
fp->N_RB_UL = oru->bw_rx[0];
|
||||
ru->config.carrier_config.ul_grid_size[ru->config.ssb_config.scs_common.value].value = oru->bw_rx[0];
|
||||
fp->numerology_index = ru->numerology;
|
||||
LOG_I(NR_PHY,
|
||||
"Set RU frame type to %s, N_RB_DL %d, N_RB_UL %d, mu %d\n",
|
||||
oru->frame_type == TDD ? "TDD" : "FDD",
|
||||
oru->bw_tx[0],
|
||||
oru->bw_rx[0],
|
||||
ru->numerology);
|
||||
set_scs_parameters(fp, fp->numerology_index, oru->bw_tx[0], 0);
|
||||
fp->slots_per_frame = 10 * fp->slots_per_subframe;
|
||||
fp->nb_antennas_rx = ru->nb_rx;
|
||||
fp->nb_antennas_tx = ru->nb_tx;
|
||||
fp->symbols_per_slot = 14;
|
||||
fp->samples_per_subframe_wCP = fp->ofdm_symbol_size * fp->symbols_per_slot * fp->slots_per_subframe;
|
||||
fp->samples_per_frame_wCP = 10 * fp->samples_per_subframe_wCP;
|
||||
fp->samples_per_slot_wCP = fp->symbols_per_slot * fp->ofdm_symbol_size;
|
||||
fp->samples_per_slotN0 = (fp->nb_prefix_samples + fp->ofdm_symbol_size) * fp->symbols_per_slot;
|
||||
fp->samples_per_slot0 =
|
||||
fp->nb_prefix_samples0 + ((fp->symbols_per_slot - 1) * fp->nb_prefix_samples) + (fp->symbols_per_slot * fp->ofdm_symbol_size);
|
||||
fp->samples_per_subframe = (fp->nb_prefix_samples0 + fp->ofdm_symbol_size) * 2
|
||||
+ (fp->nb_prefix_samples + fp->ofdm_symbol_size) * (fp->symbols_per_slot * fp->slots_per_subframe - 2);
|
||||
fp->samples_per_frame = 10 * fp->samples_per_subframe;
|
||||
fp->freq_range = (oru->carrier_freq_tx[0] < 6e6) ? FR1 : FR2;
|
||||
|
||||
fp->dl_CarrierFreq = (double)oru->carrier_freq_tx[0] * 1000;
|
||||
fp->ul_CarrierFreq = (double)oru->carrier_freq_rx[0] * 1000;
|
||||
fp->Ncp = NORMAL;
|
||||
fp->ofdm_offset_divisor = 8;
|
||||
|
||||
// Split 7.2 parameters
|
||||
ru->config.prach_config.num_prach_fd_occasions.value = 1;
|
||||
ru->config.prach_config.prach_ConfigurationIndex.value = oru->prach_config_index;
|
||||
ru->config.prach_config.prach_ConfigurationIndex.tl.tag = 0x1029;
|
||||
ru->config.prach_config.num_prach_fd_occasions_list = malloc(sizeof(*ru->config.prach_config.num_prach_fd_occasions_list));
|
||||
ru->config.prach_config.num_prach_fd_occasions_list[0].k1.value = oru->prach_msg1_freq;
|
||||
if (ru->config.cell_config.frame_duplex_type.value == 1 /* TDD */) {
|
||||
ru->config.tdd_table.tdd_period.value = oru->tdd_period;
|
||||
ru->config.tdd_table.tdd_period.tl.tag = 0x1026;
|
||||
int numb_slots_frame = (1 << ru->numerology) * NR_NUMBER_OF_SUBFRAMES_PER_FRAME;
|
||||
int numb_period_frame = get_nb_periods_per_frame(oru->tdd_period);
|
||||
int numb_slots_period = numb_slots_frame / numb_period_frame;
|
||||
ru->config.tdd_table.max_tdd_periodicity_list =
|
||||
malloc(sizeof(*ru->config.tdd_table.max_tdd_periodicity_list) * (numb_slots_frame));
|
||||
for (int n = 0; n < numb_slots_frame; n++) {
|
||||
int s = 0;
|
||||
int p = n % numb_slots_period;
|
||||
if (p < oru->num_DL_slots) {
|
||||
ru->config.tdd_table.max_tdd_periodicity_list[n].max_num_of_symbol_per_slot_list =
|
||||
malloc(sizeof(*ru->config.tdd_table.max_tdd_periodicity_list[n].max_num_of_symbol_per_slot_list) * NR_SYMBOLS_PER_SLOT);
|
||||
for (s = 0; s < 14; s++)
|
||||
ru->config.tdd_table.max_tdd_periodicity_list[n].max_num_of_symbol_per_slot_list[s].slot_config.value = 0;
|
||||
} else if (p == oru->num_DL_slots) {
|
||||
ru->config.tdd_table.max_tdd_periodicity_list[n].max_num_of_symbol_per_slot_list =
|
||||
malloc(sizeof(*ru->config.tdd_table.max_tdd_periodicity_list[n].max_num_of_symbol_per_slot_list) * NR_SYMBOLS_PER_SLOT);
|
||||
for (s = 0; s < oru->num_DL_symbols; s++)
|
||||
ru->config.tdd_table.max_tdd_periodicity_list[n].max_num_of_symbol_per_slot_list[s].slot_config.value = 0;
|
||||
for (; s < NR_SYMBOLS_PER_SLOT - oru->num_UL_symbols; s++)
|
||||
ru->config.tdd_table.max_tdd_periodicity_list[n].max_num_of_symbol_per_slot_list[s].slot_config.value = 2;
|
||||
for (; s < NR_SYMBOLS_PER_SLOT; s++)
|
||||
ru->config.tdd_table.max_tdd_periodicity_list[n].max_num_of_symbol_per_slot_list[s].slot_config.value = 1;
|
||||
} else {
|
||||
ru->config.tdd_table.max_tdd_periodicity_list[n].max_num_of_symbol_per_slot_list =
|
||||
malloc(sizeof(*ru->config.tdd_table.max_tdd_periodicity_list[n].max_num_of_symbol_per_slot_list) * NR_SYMBOLS_PER_SLOT);
|
||||
for (s = 0; s < NR_SYMBOLS_PER_SLOT; s++)
|
||||
ru->config.tdd_table.max_tdd_periodicity_list[n].max_num_of_symbol_per_slot_list[s].slot_config.value = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void fft_and_cp_insertion(NR_DL_FRAME_PARMS *fp, c16_t *txdataF, c16_t *txdata, int slot, int symbol)
|
||||
{
|
||||
if (fp->Ncp == 1) {
|
||||
PHY_ofdm_mod((int *)txdataF, (int *)txdata, fp->ofdm_symbol_size, 1, fp->nb_prefix_samples, CYCLIC_PREFIX);
|
||||
} else {
|
||||
if (fp->numerology_index != 0) {
|
||||
if (!(slot % (fp->slots_per_subframe / 2)) && (symbol == 0)) {
|
||||
PHY_ofdm_mod((int *)txdataF, (int *)txdata, fp->ofdm_symbol_size, 1, fp->nb_prefix_samples0, CYCLIC_PREFIX);
|
||||
} else {
|
||||
PHY_ofdm_mod((int *)txdataF, (int *)txdata, fp->ofdm_symbol_size, 1, fp->nb_prefix_samples, CYCLIC_PREFIX);
|
||||
}
|
||||
} else {
|
||||
if (symbol % 0x7) {
|
||||
PHY_ofdm_mod((int *)txdataF, (int *)txdata, fp->ofdm_symbol_size, 1, fp->nb_prefix_samples, CYCLIC_PREFIX);
|
||||
} else {
|
||||
PHY_ofdm_mod((int *)txdataF, (int *)txdata, fp->ofdm_symbol_size, 1, fp->nb_prefix_samples0, CYCLIC_PREFIX);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void dl_symbol_process(RU_t *ru, int frame, int slot, int symbol, c16_t **txDataF, int64_t timestamp)
|
||||
{
|
||||
NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms;
|
||||
uint32_t slot_offset = get_samples_slot_timestamp(fp, slot);
|
||||
uint32_t symbol_offset = get_samples_symbol_timestamp(fp, slot, symbol);
|
||||
|
||||
__attribute__((aligned(64))) c16_t txdataF_shifted[fp->ofdm_symbol_size];
|
||||
memset(txdataF_shifted, 0, sizeof(txdataF_shifted));
|
||||
c16_t *rotation = fp->symbol_rotation[0] + (slot % fp->slots_per_subframe) * fp->symbols_per_slot + symbol;
|
||||
for (int aatx = 0; aatx < ru->nb_tx; aatx++) {
|
||||
// Phase compensation
|
||||
rotate_cpx_vector(txDataF[aatx], *rotation, txDataF[aatx], fp->N_RB_DL * NR_NB_SC_PER_RB, 15);
|
||||
// FFT Shift
|
||||
const int num_samp_half = fp->N_RB_DL * NR_NB_SC_PER_RB / 2;
|
||||
const int first_carrier_offset = fp->ofdm_symbol_size - num_samp_half;
|
||||
memcpy(txdataF_shifted + first_carrier_offset, txDataF[aatx], num_samp_half * sizeof(c16_t));
|
||||
memcpy(txdataF_shifted, txDataF[aatx] + num_samp_half, num_samp_half * sizeof(c16_t));
|
||||
fft_and_cp_insertion(ru->nr_frame_parms,
|
||||
txdataF_shifted,
|
||||
(c16_t *)&ru->common.txdata[aatx][slot_offset + symbol_offset],
|
||||
slot,
|
||||
symbol);
|
||||
}
|
||||
tx_rf_symbols(ru, frame, slot, timestamp, symbol, 1);
|
||||
}
|
||||
|
||||
void *oru_north_read_thread(void *arg)
|
||||
{
|
||||
ORU_t *oru = (ORU_t *)arg;
|
||||
|
||||
RU_t *ru = (RU_t *)oru->ru;
|
||||
NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms;
|
||||
|
||||
__attribute__((aligned(64))) c16_t txDataF[ru->nb_tx][fp->N_RB_DL * NR_NB_SC_PER_RB];
|
||||
memset(txDataF, 0, sizeof(txDataF));
|
||||
c16_t *txDataF_ptr[ru->nb_tx];
|
||||
for (int aatx = 0; aatx < ru->nb_tx; aatx++) {
|
||||
txDataF_ptr[aatx] = txDataF[aatx];
|
||||
}
|
||||
uint32_t start_frame, start_slot;
|
||||
uint64_t start_hyper_frame;
|
||||
struct timespec utc_anchor_point;
|
||||
oru_fh_get_utc_anchor_point(oru->fronthaul, &start_hyper_frame, &start_frame, &start_slot, &utc_anchor_point);
|
||||
AssertFatal(ru->rfdevice.get_timestamp != NULL, "rfdevice has no capability to translate UTC timestamp to sample index\n");
|
||||
int64_t start_timestamp = ru->rfdevice.get_timestamp(&ru->rfdevice, &utc_anchor_point);
|
||||
// subtract the start_frame and start_slot from the timestamp simplify calculation below.
|
||||
start_timestamp -= (start_frame * fp->samples_per_frame + get_samples_slot_timestamp(fp, start_slot));
|
||||
// Now start_timestamp points to the start sample of the frame 0 slot 0 symbol 0 of hyperframe 0
|
||||
LOG_A(PHY, "DL thread started: start_timestamp %ld, start_frame %d, start_slot %d\n", start_timestamp, start_frame, start_slot);
|
||||
|
||||
while (!oai_exit) {
|
||||
int frame = -1, slot = -1, symbol = -1;
|
||||
uint64_t hyper_frame;
|
||||
int ret = oru_fh_tx_read_symbol(oru->fronthaul, (uint32_t **)txDataF_ptr, ru->nb_tx, &hyper_frame, &frame, &slot, &symbol);
|
||||
if (ret != 0) {
|
||||
LOG_E(PHY, "[RU_thread] read data error: frame %d, slot %d, symbol %d\n", frame, slot, symbol);
|
||||
continue;
|
||||
}
|
||||
if (start_hyper_frame > hyper_frame) {
|
||||
continue;
|
||||
}
|
||||
uint64_t num_frames = (hyper_frame - start_hyper_frame) * 1024 + frame;
|
||||
int64_t timestamp = start_timestamp + num_frames * fp->samples_per_frame + get_samples_slot_timestamp(fp, slot)
|
||||
+ get_samples_symbol_timestamp(fp, slot, symbol);
|
||||
if (timestamp < 0) {
|
||||
continue;
|
||||
}
|
||||
dl_symbol_process(ru, frame, slot, symbol, txDataF_ptr, timestamp);
|
||||
if (frame % 256 == 0 && slot == 0 && symbol == 0) {
|
||||
LOG_I(PHY, "[RU_thread] read data: frame %d, slot %d, symbol %d\n", frame, slot, symbol);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Returns PRACH symbol that was received in current frame, slot and symbol.
|
||||
// If no PRACH symbol was received, returns -1
|
||||
int get_prach_symbol(ORU_t *oru, int frame, int slot, int symbol, int numerology)
|
||||
{
|
||||
uint16_t RA_sfn_index;
|
||||
AssertFatal(oru->ru->nr_frame_parms->frame_type == TDD, "Only supports TDD\n");
|
||||
if (get_nr_prach_sched_from_info(oru->prach_info, oru->prach_config_index, frame, slot, numerology, FR1, &RA_sfn_index, true)) {
|
||||
int format = oru->prach_item.pdu.prach_format;
|
||||
int start_symbol = oru->prach_item.pdu.prach_start_symbol;
|
||||
symbol -= start_symbol;
|
||||
// TODO: Support more PRACH formats
|
||||
AssertFatal(format == 8, "only support format B4\n");
|
||||
// TODO: This is not exactly the case but it is correct
|
||||
if (symbol >= 0 && symbol < 12) {
|
||||
return symbol;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void receive_prach(ORU_t *oru, int frame, int slot, int symbol, int prach_symbol)
|
||||
{
|
||||
RU_t *ru = oru->ru;
|
||||
NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms;
|
||||
oru->prach_item.frame = frame;
|
||||
oru->prach_item.slot = slot;
|
||||
|
||||
c16_t rxdataF[ru->nb_rx][NR_PRACH_SEQ_LEN_L];
|
||||
memset(rxdataF, 0, sizeof(rxdataF));
|
||||
|
||||
rx_nr_prach_ru_rep(&oru->prach_item,
|
||||
ru->common.rxdata,
|
||||
fp,
|
||||
ru->N_TA_offset,
|
||||
prach_symbol,
|
||||
0, // prachOccasion
|
||||
rxdataF);
|
||||
|
||||
c16_t *rxdataF_ptr[ru->nb_rx];
|
||||
for (int aarx = 0; aarx < ru->nb_rx; aarx++) {
|
||||
rxdataF_ptr[aarx] = rxdataF[aarx];
|
||||
}
|
||||
oru_fh_rx_send_prach(oru->fronthaul, (uint32_t **)rxdataF_ptr, ru->nb_rx, frame, slot, symbol);
|
||||
}
|
||||
|
||||
#define MAX_PENDING_UL_JOBS 64
|
||||
|
||||
typedef struct {
|
||||
ul_job_t job;
|
||||
bool active;
|
||||
int symbols_sent;
|
||||
} ul_pending_t;
|
||||
|
||||
static void receive_pusch(ORU_t *oru, int frame, int slot, int symbol, ul_job_t *job)
|
||||
{
|
||||
RU_t *ru = oru->ru;
|
||||
NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms;
|
||||
int aarx = job->antenna_id;
|
||||
|
||||
if (aarx < 0 || aarx >= fp->nb_antennas_rx) {
|
||||
LOG_W(PHY, "[ORU south] receive_pusch: invalid antenna_id %d\n", aarx);
|
||||
return;
|
||||
}
|
||||
|
||||
// CP removal + FFT → full ofdm_symbol_size frequency-domain output
|
||||
c16_t rxdataF_fft[fp->ofdm_symbol_size] __attribute__((aligned(32)));
|
||||
nr_symbol_fep_ul(fp, (c16_t *)ru->common.rxdata[aarx], rxdataF_fft, symbol, slot, ru->N_TA_offset);
|
||||
|
||||
// Phase decompensation (conjugate rotation for UL)
|
||||
apply_nr_rotation_symbol_RX(fp->symbols_per_slot,
|
||||
fp->slots_per_subframe,
|
||||
fp->timeshift_symbol_rotation,
|
||||
fp->first_carrier_offset,
|
||||
rxdataF_fft,
|
||||
fp->symbol_rotation[link_type_ul],
|
||||
fp->N_RB_UL,
|
||||
slot,
|
||||
symbol);
|
||||
|
||||
// Inverse FFT shift: split format → contiguous PRB format sent to DU.
|
||||
// DL TX shift: contiguous[0..N/2-1] → FFT_input[first_carrier_offset..] (negative freqs)
|
||||
// contiguous[N/2..N-1] → FFT_input[0..N/2-1] (positive freqs)
|
||||
// UL RX inverse: FFT_out[first_carrier_offset..] → contiguous[0..N/2-1]
|
||||
// FFT_out[0..N/2-1] → contiguous[N/2..N-1]
|
||||
const int num_samp_half = fp->N_RB_UL * NR_NB_SC_PER_RB / 2;
|
||||
const int first_carrier_offset = fp->ofdm_symbol_size - num_samp_half;
|
||||
c16_t rxdataF[fp->N_RB_UL * NR_NB_SC_PER_RB];
|
||||
memcpy(rxdataF, rxdataF_fft + first_carrier_offset, num_samp_half * sizeof(c16_t));
|
||||
memcpy(rxdataF + num_samp_half, rxdataF_fft, num_samp_half * sizeof(c16_t));
|
||||
|
||||
oru_fh_rx_send_pusch(oru->fronthaul, (uint32_t *)rxdataF, symbol, job);
|
||||
}
|
||||
|
||||
#define UL_WORK_QUEUE_DEPTH 128
|
||||
|
||||
typedef struct {
|
||||
ORU_t *oru;
|
||||
int frame;
|
||||
int slot;
|
||||
int symbol;
|
||||
ul_job_t job;
|
||||
} ul_work_item_t;
|
||||
|
||||
typedef struct {
|
||||
ul_work_item_t ring[UL_WORK_QUEUE_DEPTH];
|
||||
int head;
|
||||
int tail;
|
||||
int count;
|
||||
pthread_mutex_t lock;
|
||||
pthread_cond_t work_available;
|
||||
pthread_cond_t space_available;
|
||||
bool running;
|
||||
} ul_work_queue_t;
|
||||
|
||||
static void *ul_worker_thread(void *arg)
|
||||
{
|
||||
ul_work_queue_t *q = arg;
|
||||
while (1) {
|
||||
pthread_mutex_lock(&q->lock);
|
||||
while (q->count == 0 && q->running)
|
||||
pthread_cond_wait(&q->work_available, &q->lock);
|
||||
if (!q->running && q->count == 0) {
|
||||
pthread_mutex_unlock(&q->lock);
|
||||
break;
|
||||
}
|
||||
ul_work_item_t item = q->ring[q->head];
|
||||
q->head = (q->head + 1) % UL_WORK_QUEUE_DEPTH;
|
||||
q->count--;
|
||||
pthread_cond_signal(&q->space_available);
|
||||
pthread_mutex_unlock(&q->lock);
|
||||
|
||||
receive_pusch(item.oru, item.frame, item.slot, item.symbol, &item.job);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void dispatch_ul_work(ul_work_queue_t *q, ORU_t *oru, int frame, int slot, int symbol, const ul_job_t *job)
|
||||
{
|
||||
pthread_mutex_lock(&q->lock);
|
||||
while (q->count == UL_WORK_QUEUE_DEPTH)
|
||||
pthread_cond_wait(&q->space_available, &q->lock);
|
||||
q->ring[q->tail] = (ul_work_item_t){.oru = oru, .frame = frame, .slot = slot, .symbol = symbol, .job = *job};
|
||||
q->tail = (q->tail + 1) % UL_WORK_QUEUE_DEPTH;
|
||||
q->count++;
|
||||
pthread_cond_signal(&q->work_available);
|
||||
pthread_mutex_unlock(&q->lock);
|
||||
}
|
||||
|
||||
void *oru_south_read_thread(void *arg)
|
||||
{
|
||||
ORU_t *oru = arg;
|
||||
RU_t *ru = oru->ru;
|
||||
NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms;
|
||||
struct timespec utc_anchor_point;
|
||||
AssertFatal(ru->rfdevice.get_timestamp != NULL, "rfdevice has no capability to translate UTC timestamp to sample index\n");
|
||||
uint32_t start_frame, start_slot;
|
||||
uint64_t hyper_frame;
|
||||
oru_fh_get_utc_anchor_point(oru->fronthaul, &hyper_frame, &start_frame, &start_slot, &utc_anchor_point);
|
||||
int64_t start_timestamp = ru->rfdevice.get_timestamp(&ru->rfdevice, &utc_anchor_point);
|
||||
|
||||
const int num_samples = 3000;
|
||||
c16_t throwaway_samples[ru->nb_rx][num_samples];
|
||||
void *rxp[ru->nb_rx];
|
||||
for (int i = 0; i < ru->nb_rx; i++)
|
||||
rxp[i] = throwaway_samples[i];
|
||||
|
||||
openair0_timestamp_t timestamp;
|
||||
int num_samples_read = ru->rfdevice.trx_read_func(&ru->rfdevice, ×tamp, rxp, num_samples, ru->nb_rx);
|
||||
AssertFatal(num_samples_read == num_samples, "Unexpected number of samples received\n");
|
||||
openair0_timestamp_t next_timestamp = timestamp + num_samples_read;
|
||||
while (next_timestamp > start_timestamp) {
|
||||
start_timestamp += get_samples_slot_duration(fp, start_slot, 1);
|
||||
start_slot++;
|
||||
if (start_slot == fp->slots_per_frame) {
|
||||
start_slot = 0;
|
||||
start_frame++;
|
||||
if (start_frame == 1024) {
|
||||
start_frame = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
while (next_timestamp < start_timestamp) {
|
||||
int num_samples_to_read = min(num_samples, (int)(start_timestamp - next_timestamp));
|
||||
int num_samples_read = ru->rfdevice.trx_read_func(&ru->rfdevice, ×tamp, rxp, num_samples_to_read, ru->nb_rx);
|
||||
AssertFatal(num_samples_read == num_samples_to_read, "Unexpected number of samples received\n");
|
||||
next_timestamp += num_samples_read;
|
||||
}
|
||||
|
||||
AssertFatal(next_timestamp == start_timestamp, "O-RU South thread could not sync to UTC anchor point\n");
|
||||
|
||||
int slot = start_slot;
|
||||
int frame = start_frame;
|
||||
|
||||
// Worker pool: one thread per RX antenna so all antennas in a symbol process in parallel.
|
||||
const int num_workers = ru->nb_rx;
|
||||
pthread_t workers[num_workers];
|
||||
ul_work_queue_t work_queue = {
|
||||
.lock = PTHREAD_MUTEX_INITIALIZER,
|
||||
.work_available = PTHREAD_COND_INITIALIZER,
|
||||
.space_available = PTHREAD_COND_INITIALIZER,
|
||||
.running = true,
|
||||
};
|
||||
for (int i = 0; i < num_workers; i++) {
|
||||
char name[32];
|
||||
snprintf(name, sizeof(name), "ul_worker_%d", i);
|
||||
threadCreate(&workers[i], ul_worker_thread, &work_queue, name, -1, OAI_PRIORITY_RT_MAX);
|
||||
}
|
||||
|
||||
ul_pending_t pending_ul[MAX_PENDING_UL_JOBS] = {0};
|
||||
|
||||
while (!oai_exit) {
|
||||
int rx_slot_type = nr_slot_select(&ru->config, frame, slot);
|
||||
for (int symbol = 0; symbol < 14; symbol++) {
|
||||
int samples_to_read = get_samples_symbol_duration(fp, slot, symbol, 1);
|
||||
size_t offset = get_samples_slot_timestamp(fp, slot) + get_samples_symbol_timestamp(fp, slot, symbol);
|
||||
c16_t *rxp[fp->nb_antennas_rx];
|
||||
for (int aarx = 0; aarx < fp->nb_antennas_rx; aarx++) {
|
||||
rxp[aarx] = (c16_t *)&ru->common.rxdata[aarx][offset];
|
||||
}
|
||||
|
||||
openair0_timestamp_t timestamp;
|
||||
int num_samples_read = ru->rfdevice.trx_read_func(&ru->rfdevice, ×tamp, (void **)rxp, samples_to_read, ru->nb_rx);
|
||||
AssertFatal(num_samples_read == samples_to_read, "Unexpected number of samples received\n");
|
||||
LOG_D(PHY,
|
||||
"[ORU south] read data: frame %d, slot %d, symbol %d, timestamp %ld num_symbols %d, samples %d\n",
|
||||
frame,
|
||||
slot,
|
||||
symbol,
|
||||
timestamp,
|
||||
1,
|
||||
num_samples_read);
|
||||
|
||||
// Drain the UL job ring
|
||||
ul_job_t new_job;
|
||||
while (oru_fh_poll_ul_job(oru->fronthaul, &new_job) == 0) {
|
||||
bool added = false;
|
||||
for (int i = 0; i < MAX_PENDING_UL_JOBS; i++) {
|
||||
if (!pending_ul[i].active) {
|
||||
pending_ul[i] = (ul_pending_t){.job = new_job, .active = true, .symbols_sent = 0};
|
||||
added = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!added)
|
||||
LOG_W(PHY, "[ORU south] UL pending queue full, dropping job frame=%d slot=%d sym=%d\n",
|
||||
new_job.frame, new_job.slot_in_frame, new_job.symbol);
|
||||
}
|
||||
|
||||
if (rx_slot_type == NR_UPLINK_SLOT || rx_slot_type == NR_MIXED_SLOT) {
|
||||
|
||||
// Process pending jobs whose next symbol matches the current one
|
||||
for (int i = 0; i < MAX_PENDING_UL_JOBS; i++) {
|
||||
if (!pending_ul[i].active)
|
||||
continue;
|
||||
ul_job_t *j = &pending_ul[i].job;
|
||||
|
||||
// Skip jobs scheduled for a future slot or frame, and drop jobs in the past
|
||||
int slots_per_frame = fp->slots_per_frame;
|
||||
int total_slots = 1024 * slots_per_frame;
|
||||
int diff_slots = (j->frame * slots_per_frame + j->slot_in_frame) - (frame * slots_per_frame + slot);
|
||||
if (diff_slots < -total_slots / 2) {
|
||||
diff_slots += total_slots;
|
||||
} else if (diff_slots > total_slots / 2) {
|
||||
diff_slots -= total_slots;
|
||||
}
|
||||
|
||||
if (diff_slots > 0) {
|
||||
// Future slot: skip
|
||||
continue;
|
||||
}
|
||||
if (diff_slots < 0) {
|
||||
// Past slot: drop
|
||||
LOG_W(PHY, "[ORU south] missed UL slot %d.%d (now %d.%d), dropping job ant=%d\n",
|
||||
j->frame, j->slot_in_frame, frame, slot, j->antenna_id);
|
||||
pending_ul[i].active = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Same slot: check symbol
|
||||
int expected_symbol = j->symbol + pending_ul[i].symbols_sent;
|
||||
if (expected_symbol < symbol) {
|
||||
LOG_W(PHY, "[ORU south] missed UL symbol %d (now %d), dropping job ant=%d\n",
|
||||
expected_symbol, symbol, j->antenna_id);
|
||||
pending_ul[i].active = false;
|
||||
} else if (expected_symbol == symbol) {
|
||||
dispatch_ul_work(&work_queue, oru, frame, slot, symbol, j);
|
||||
if (++pending_ul[i].symbols_sent == j->num_symbols)
|
||||
pending_ul[i].active = false;
|
||||
}
|
||||
// expected_symbol > symbol: job spans multiple symbols, revisit next iteration
|
||||
}
|
||||
}
|
||||
int prach_symbol = get_prach_symbol(oru, frame, slot, symbol, ru->numerology);
|
||||
if (prach_symbol != -1)
|
||||
receive_prach(oru, frame, slot, symbol, prach_symbol);
|
||||
}
|
||||
slot++;
|
||||
if (slot == fp->slots_per_frame) {
|
||||
slot = 0;
|
||||
frame++;
|
||||
if (frame == 1024) {
|
||||
frame = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&work_queue.lock);
|
||||
work_queue.running = false;
|
||||
pthread_cond_broadcast(&work_queue.work_available);
|
||||
pthread_mutex_unlock(&work_queue.lock);
|
||||
for (int i = 0; i < num_workers; i++)
|
||||
pthread_join(workers[i], NULL);
|
||||
pthread_cond_destroy(&work_queue.work_available);
|
||||
pthread_cond_destroy(&work_queue.space_available);
|
||||
pthread_mutex_destroy(&work_queue.lock);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
59
executables/nr-oru.h
Normal file
59
executables/nr-oru.h
Normal file
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: LicenseRef-CSSL-1.0
|
||||
*/
|
||||
|
||||
#ifndef __NR_ORU_H__
|
||||
#define __NR_ORU_H__
|
||||
#include "openair1/PHY/defs_RU.h"
|
||||
#include <pthread.h>
|
||||
#include "oru_fh.h"
|
||||
#include "openair2/LAYER2/NR_MAC_COMMON/nr_prach_config.h"
|
||||
#include "openair1/PHY/defs_gNB.h"
|
||||
|
||||
typedef struct {
|
||||
RU_t *ru;
|
||||
/// tx carrier
|
||||
uint64_t carrier_freq_tx[MAX_BANDS_PER_RRU];
|
||||
/// rx carrier
|
||||
uint64_t carrier_freq_rx[MAX_BANDS_PER_RRU];
|
||||
/// tx BW in PRBs
|
||||
int bw_tx[MAX_BANDS_PER_RRU];
|
||||
/// rx BW in PRBs
|
||||
int bw_rx[MAX_BANDS_PER_RRU];
|
||||
/// 3GPP FRAME Type FDD/TDD
|
||||
int frame_type;
|
||||
/// 3GPP PRACH configuration index
|
||||
int prach_config_index;
|
||||
/// 3GPP MSG1 Start frequency
|
||||
int prach_msg1_freq;
|
||||
/// 3GPP TDD periodicity (0.5 ms, 1 0.625ms, 2 1ms, 3 1.25ms, 4 2ms,5 2.5ms, 6 5ms, 7 10ms, 8 3ms, 9 4ms
|
||||
int tdd_period;
|
||||
/// number of DL slots
|
||||
int num_DL_slots;
|
||||
/// number of UL slots
|
||||
int num_UL_slots;
|
||||
/// number of DL symbols
|
||||
int num_DL_symbols;
|
||||
/// number of UL symbols
|
||||
int num_UL_symbols;
|
||||
int numerology;
|
||||
|
||||
pthread_t north_read_thread;
|
||||
pthread_t south_read_thread;
|
||||
oru_fh_config_t fh_config;
|
||||
void *fronthaul;
|
||||
|
||||
// PRACH related
|
||||
nr_prach_info_t prach_info;
|
||||
time_stats_t rx_prach;
|
||||
time_stats_t rx;
|
||||
prach_item_t prach_item;
|
||||
} ORU_t;
|
||||
|
||||
int get_oru_options(ORU_t *oru);
|
||||
void oru_init_frame_parms(ORU_t *oru);
|
||||
void *oru_north_read_thread(void *arg);
|
||||
void *oru_south_read_thread(void *arg);
|
||||
void prepare_prach_item(ORU_t *oru);
|
||||
|
||||
#endif
|
||||
@@ -21,8 +21,6 @@
|
||||
#include "radio/COMMON/common_lib.h"
|
||||
#include "radio/ETHERNET/ethernet_lib.h"
|
||||
|
||||
#include "PHY/if4_tools.h"
|
||||
|
||||
#include "PHY/defs_nr_common.h"
|
||||
#include "PHY/phy_extern.h"
|
||||
#include "PHY/NR_TRANSPORT/nr_transport_proto.h"
|
||||
@@ -76,15 +74,6 @@ void fh_if5_south_out(RU_t *ru, int frame, int slot, uint64_t timestamp)
|
||||
ru->ifdevice.trx_write_func2(&ru->ifdevice, timestamp, buffs, 0, get_samples_per_slot(slot, ru->nr_frame_parms), 0, ru->nb_tx);
|
||||
}
|
||||
|
||||
// southbound IF4p5 fronthaul
|
||||
void fh_if4p5_south_out(RU_t *ru, int frame, int slot, uint64_t timestamp)
|
||||
{
|
||||
LOG_D(PHY,"Sending IF4p5 for frame %d subframe %d\n",ru->proc.frame_tx,ru->proc.tti_tx);
|
||||
|
||||
if ((nr_slot_select(&ru->config, ru->proc.frame_tx, ru->proc.tti_tx) & NR_DOWNLINK_SLOT) > 0)
|
||||
send_IF4p5(ru,frame, slot, IF4p5_PDLFFT);
|
||||
}
|
||||
|
||||
/*************************************************************/
|
||||
/* Input Fronthaul from south RCC/RAU */
|
||||
|
||||
@@ -148,196 +137,6 @@ void fh_if5_south_in(RU_t *ru, int *frame, int *tti)
|
||||
rxmeas.tv_nsec);
|
||||
}
|
||||
|
||||
// Synchronous if4p5 from south
|
||||
void fh_if4p5_south_in(RU_t *ru,
|
||||
int *frame,
|
||||
int *slot) {
|
||||
NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms;
|
||||
RU_proc_t *proc = &ru->proc;
|
||||
int f,sl;
|
||||
uint16_t packet_type;
|
||||
uint32_t symbol_number=0;
|
||||
uint32_t symbol_mask_full=0;
|
||||
|
||||
do { // Blocking, we need a timeout on this !!!!!!!!!!!!!!!!!!!!!!!
|
||||
recv_IF4p5(ru, &f, &sl, &packet_type, &symbol_number);
|
||||
|
||||
if (packet_type == IF4p5_PULFFT) proc->symbol_mask[sl] = proc->symbol_mask[sl] | (1<<symbol_number);
|
||||
else if (packet_type == IF4p5_PULTICK) {
|
||||
if ((proc->first_rx == 0) && (f != *frame))
|
||||
LOG_E(PHY, "rx_fh_if4p5: PULTICK received frame %d != expected %d\n", f, *frame);
|
||||
|
||||
if ((proc->first_rx == 0) && (sl != *slot))
|
||||
LOG_E(PHY, "rx_fh_if4p5: PULTICK received subframe %d != expected %d (first_rx %d)\n", sl, *slot, proc->first_rx);
|
||||
|
||||
break;
|
||||
} else if (packet_type == IF4p5_PRACH) {
|
||||
// nothing in RU for RAU
|
||||
}
|
||||
|
||||
LOG_D(PHY,"rx_fh_if4p5: subframe %d symbol mask %x\n",*slot,proc->symbol_mask[sl]);
|
||||
} while(proc->symbol_mask[sl] != symbol_mask_full);
|
||||
|
||||
//caculate timestamp_rx, timestamp_tx based on frame and subframe
|
||||
proc->tti_rx = sl;
|
||||
proc->frame_rx = f;
|
||||
proc->timestamp_rx = (proc->frame_rx * fp->samples_per_subframe * 10) + get_samples_slot_timestamp(fp, proc->tti_rx);
|
||||
// proc->timestamp_tx = proc->timestamp_rx + (4*fp->samples_per_subframe);
|
||||
proc->tti_tx = (sl+ru->sl_ahead)%fp->slots_per_frame;
|
||||
proc->frame_tx = (sl > (fp->slots_per_frame - 1 - (ru->sl_ahead))) ? (f + 1) & 1023 : f;
|
||||
|
||||
if (proc->first_rx == 0) {
|
||||
if (proc->tti_rx != *slot) {
|
||||
LOG_E(PHY,"Received Timestamp (IF4p5) doesn't correspond to the time we think it is (proc->tti_rx %d, subframe %d)\n",proc->tti_rx,*slot);
|
||||
exit_fun("Exiting");
|
||||
}
|
||||
|
||||
if (proc->frame_rx != *frame) {
|
||||
LOG_E(PHY,"Received Timestamp (IF4p5) doesn't correspond to the time we think it is (proc->frame_rx %d frame %d)\n",proc->frame_rx,*frame);
|
||||
exit_fun("Exiting");
|
||||
}
|
||||
} else {
|
||||
proc->first_rx = 0;
|
||||
*frame = proc->frame_rx;
|
||||
*slot = proc->tti_rx;
|
||||
}
|
||||
|
||||
proc->symbol_mask[proc->tti_rx] = 0;
|
||||
LOG_D(PHY,"RU %d: fh_if4p5_south_in sleeping ...\n",ru->idx);
|
||||
}
|
||||
|
||||
// asynchronous inbound if4p5 fronthaul from south
|
||||
void fh_if4p5_south_asynch_in(RU_t *ru,int *frame,int *slot) {
|
||||
NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms;
|
||||
RU_proc_t *proc = &ru->proc;
|
||||
uint16_t packet_type;
|
||||
uint32_t symbol_number = 0;
|
||||
uint32_t symbol_mask = (1 << fp->symbols_per_slot) - 1;
|
||||
uint32_t prach_rx = 0;
|
||||
|
||||
do { // Blocking, we need a timeout on this !!!!!!!!!!!!!!!!!!!!!!!
|
||||
recv_IF4p5(ru, &proc->frame_rx, &proc->tti_rx, &packet_type, &symbol_number);
|
||||
if (proc->first_rx != 0) {
|
||||
*frame = proc->frame_rx;
|
||||
*slot = proc->tti_rx;
|
||||
proc->first_rx = 0;
|
||||
} else {
|
||||
if (proc->frame_rx != *frame) {
|
||||
LOG_E(PHY,"frame_rx %d is not what we expect %d\n",proc->frame_rx,*frame);
|
||||
exit_fun("Exiting");
|
||||
}
|
||||
if (proc->tti_rx != *slot) {
|
||||
LOG_E(PHY,"tti_rx %d is not what we expect %d\n",proc->tti_rx,*slot);
|
||||
exit_fun("Exiting");
|
||||
}
|
||||
}
|
||||
|
||||
if (packet_type == IF4p5_PULFFT)
|
||||
symbol_mask &= ~(1 << symbol_number);
|
||||
else if (packet_type == IF4p5_PRACH)
|
||||
prach_rx &= ~0x1;
|
||||
} while (symbol_mask > 0 || prach_rx > 0); // haven't received all PUSCH symbols and PRACH information
|
||||
}
|
||||
|
||||
/*************************************************************/
|
||||
/* Input Fronthaul from North RRU */
|
||||
|
||||
// RRU IF4p5 TX fronthaul receiver. Assumes an if_device on input and if or rf device on output
|
||||
// receives one subframe's worth of IF4p5 OFDM symbols and OFDM modulates
|
||||
void fh_if4p5_north_in(RU_t *ru,int *frame,int *slot)
|
||||
{
|
||||
uint32_t symbol_number=0;
|
||||
uint32_t symbol_mask, symbol_mask_full;
|
||||
uint16_t packet_type;
|
||||
/// **** incoming IF4p5 from remote RCC/RAU **** ///
|
||||
symbol_number = 0;
|
||||
symbol_mask = 0;
|
||||
symbol_mask_full = (1<<(ru->nr_frame_parms->symbols_per_slot))-1;
|
||||
|
||||
do {
|
||||
recv_IF4p5(ru, frame, slot, &packet_type, &symbol_number);
|
||||
symbol_mask = symbol_mask | (1<<symbol_number);
|
||||
} while (symbol_mask != symbol_mask_full);
|
||||
}
|
||||
|
||||
void fh_if5_north_asynch_in(RU_t *ru, int *frame, int *slot)
|
||||
{
|
||||
AssertFatal(1 == 0, "Shouldn't get here\n");
|
||||
}
|
||||
|
||||
void fh_if4p5_north_asynch_in(RU_t *ru,int *frame,int *slot) {
|
||||
NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms;
|
||||
nfapi_nr_config_request_scf_t *cfg = &ru->config;
|
||||
RU_proc_t *proc = &ru->proc;
|
||||
uint16_t packet_type;
|
||||
uint32_t symbol_mask_full = 0;
|
||||
int slot_tx,frame_tx;
|
||||
LOG_D(PHY, "%s(ru:%p frame, subframe)\n", __FUNCTION__, ru);
|
||||
uint32_t symbol_number = 0;
|
||||
uint32_t symbol_mask = 0;
|
||||
|
||||
// symbol_mask_full = ((subframe_select(fp,*slot) == SF_S) ? (1<<fp->dl_symbols_in_S_subframe) : (1<<fp->symbols_per_slot))-1;
|
||||
do {
|
||||
recv_IF4p5(ru, &frame_tx, &slot_tx, &packet_type, &symbol_number);
|
||||
|
||||
if (((nr_slot_select(cfg, frame_tx, slot_tx) & NR_DOWNLINK_SLOT) > 0) && (symbol_number == 0))
|
||||
start_meas(&ru->rx_fhaul);
|
||||
|
||||
LOG_D(PHY,"slot %d (%d): frame %d, slot %d, symbol %d\n",
|
||||
*slot,nr_slot_select(cfg,frame_tx,*slot),frame_tx,slot_tx,symbol_number);
|
||||
|
||||
if (proc->first_tx != 0) {
|
||||
*frame = frame_tx;
|
||||
*slot = slot_tx;
|
||||
proc->first_tx = 0;
|
||||
} else {
|
||||
AssertFatal(frame_tx == *frame,
|
||||
"frame_tx %d is not what we expect %d\n",frame_tx,*frame);
|
||||
AssertFatal(slot_tx == *slot,
|
||||
"slot_tx %d is not what we expect %d\n",slot_tx,*slot);
|
||||
}
|
||||
|
||||
if (packet_type == IF4p5_PDLFFT) {
|
||||
symbol_mask = symbol_mask | (1<<symbol_number);
|
||||
} else
|
||||
AssertFatal(false, "Illegal IF4p5 packet type (should only be IF4p5_PDLFFT%d\n", packet_type);
|
||||
} while (symbol_mask != symbol_mask_full);
|
||||
|
||||
if ((nr_slot_select(cfg, frame_tx, slot_tx) & NR_DOWNLINK_SLOT) > 0)
|
||||
stop_meas(&ru->rx_fhaul);
|
||||
|
||||
proc->tti_tx = slot_tx;
|
||||
proc->frame_tx = frame_tx;
|
||||
|
||||
if (frame_tx == 0 && slot_tx == 0)
|
||||
proc->frame_tx_unwrap += 1024;
|
||||
|
||||
proc->timestamp_tx =
|
||||
((uint64_t)frame_tx + proc->frame_tx_unwrap) * fp->samples_per_subframe * 10 + get_samples_slot_timestamp(fp, slot_tx);
|
||||
LOG_D(PHY, "RU %d/%d TST %lu, frame %d, subframe %d\n", ru->idx, 0, proc->timestamp_tx, frame_tx, slot_tx);
|
||||
|
||||
if (ru->feptx_ofdm)
|
||||
ru->feptx_ofdm(ru, frame_tx, slot_tx);
|
||||
|
||||
if (ru->fh_south_out)
|
||||
ru->fh_south_out(ru, frame_tx, slot_tx, proc->timestamp_tx);
|
||||
}
|
||||
|
||||
void fh_if5_north_out(RU_t *ru)
|
||||
{
|
||||
/// **** send_IF5 of rxdata to BBU **** ///
|
||||
AssertFatal(1 == 0, "Shouldn't get here\n");
|
||||
}
|
||||
|
||||
// RRU IF4p5 northbound interface (RX)
|
||||
void fh_if4p5_north_out(RU_t *ru)
|
||||
{
|
||||
RU_proc_t *proc=&ru->proc;
|
||||
start_meas(&ru->tx_fhaul);
|
||||
send_IF4p5(ru, proc->frame_rx, proc->tti_rx, IF4p5_PULFFT);
|
||||
stop_meas(&ru->tx_fhaul);
|
||||
}
|
||||
|
||||
static void rx_rf(RU_t *ru, int *frame, int *slot)
|
||||
{
|
||||
RU_proc_t *proc = &ru->proc;
|
||||
@@ -494,7 +293,7 @@ static radio_tx_gpio_flag_t get_gpio_flags(RU_t *ru, int slot)
|
||||
return flags_gpio;
|
||||
}
|
||||
|
||||
void tx_rf(RU_t *ru, int frame,int slot, uint64_t timestamp)
|
||||
void tx_rf_symbols(RU_t *ru, int frame, int slot, uint64_t timestamp, int start_symbol, int num_symbols)
|
||||
{
|
||||
RU_proc_t *proc = &ru->proc;
|
||||
NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms;
|
||||
@@ -523,20 +322,21 @@ void tx_rf(RU_t *ru, int frame,int slot, uint64_t timestamp)
|
||||
|
||||
AssertFatal(txsymb > 0, "illegal txsymb %d\n", txsymb);
|
||||
|
||||
if (fp->slots_per_subframe == 1) {
|
||||
if (txsymb <= 7)
|
||||
siglen = (fp->ofdm_symbol_size + fp->nb_prefix_samples0) + (txsymb - 1) * (fp->ofdm_symbol_size + fp->nb_prefix_samples);
|
||||
else
|
||||
siglen = 2 * (fp->ofdm_symbol_size + fp->nb_prefix_samples0) + (txsymb - 2) * (fp->ofdm_symbol_size + fp->nb_prefix_samples);
|
||||
} else {
|
||||
if(slot%(fp->slots_per_subframe/2))
|
||||
siglen = txsymb * (fp->ofdm_symbol_size + fp->nb_prefix_samples);
|
||||
else
|
||||
siglen = (fp->ofdm_symbol_size + fp->nb_prefix_samples0) + (txsymb - 1) * (fp->ofdm_symbol_size + fp->nb_prefix_samples);
|
||||
if (txsymb < start_symbol) {
|
||||
// No DL symbols in this transmission
|
||||
return;
|
||||
}
|
||||
|
||||
//+ ru->end_of_burst_delay;
|
||||
int end_symbol = start_symbol + num_symbols - 1;
|
||||
if (end_symbol >= txsymb) {
|
||||
flags_burst = TX_BURST_END;
|
||||
} else {
|
||||
flags_burst = TX_BURST_MIDDLE;
|
||||
}
|
||||
|
||||
int num_symbols_this_transmission = min(txsymb, end_symbol) - start_symbol + 1;
|
||||
|
||||
siglen = get_samples_symbol_duration(fp, slot, start_symbol, num_symbols_this_transmission);
|
||||
} else if (slot_type == NR_DOWNLINK_SLOT) {
|
||||
int prevslot_type = nr_slot_select(cfg,frame,(slot+(fp->slots_per_frame-1))%fp->slots_per_frame);
|
||||
int nextslot_type = nr_slot_select(cfg,frame,(slot+1)%fp->slots_per_frame);
|
||||
@@ -548,9 +348,11 @@ void tx_rf(RU_t *ru, int frame,int slot, uint64_t timestamp)
|
||||
} else {
|
||||
flags_burst = proc->first_tx == 1 ? TX_BURST_START : TX_BURST_MIDDLE;
|
||||
}
|
||||
siglen = get_samples_symbol_duration(fp, slot, start_symbol, num_symbols);
|
||||
}
|
||||
} else { // FDD
|
||||
flags_burst = proc->first_tx == 1 ? TX_BURST_START : TX_BURST_MIDDLE;
|
||||
siglen = get_samples_symbol_duration(fp, slot, start_symbol, num_symbols);
|
||||
}
|
||||
|
||||
if (ru->openair0_cfg.gpio_controller != RU_GPIO_CONTROL_NONE)
|
||||
@@ -561,8 +363,9 @@ void tx_rf(RU_t *ru, int frame,int slot, uint64_t timestamp)
|
||||
|
||||
int nt = ru->nb_tx;
|
||||
void *txp[nt];
|
||||
uint32_t time_offset = get_samples_slot_timestamp(fp, slot) + get_samples_symbol_timestamp(fp, slot, start_symbol);
|
||||
for (int i = 0; i < nt; i++)
|
||||
txp[i] = (void *)&ru->common.txdata[i][get_samples_slot_timestamp(fp, slot)] - sf_extension * sizeof(int32_t);
|
||||
txp[i] = (void *)&ru->common.txdata[i][time_offset] - sf_extension * sizeof(int32_t);
|
||||
|
||||
// prepare tx buffer pointers
|
||||
uint32_t txs = ru->rfdevice.trx_write_func(&ru->rfdevice,
|
||||
@@ -586,7 +389,12 @@ void tx_rf(RU_t *ru, int frame,int slot, uint64_t timestamp)
|
||||
10 * log10((double)signal_energy(txp[0], siglen + sf_extension)));
|
||||
}
|
||||
|
||||
static void fill_rf_config(RU_t *ru, char *rf_config_file)
|
||||
void tx_rf(RU_t *ru, int frame, int slot, uint64_t timestamp)
|
||||
{
|
||||
tx_rf_symbols(ru, frame, slot, timestamp, 0, 14);
|
||||
}
|
||||
|
||||
void fill_rf_config(RU_t *ru, char *rf_config_file)
|
||||
{
|
||||
NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms;
|
||||
nfapi_nr_config_request_scf_t *config = &ru->config; //tmp index
|
||||
@@ -649,7 +457,7 @@ static void fill_rf_config(RU_t *ru, char *rf_config_file)
|
||||
}
|
||||
}
|
||||
|
||||
static void fill_split7_2_config(split7_config_t *split7, const nfapi_nr_config_request_scf_t *config, const NR_DL_FRAME_PARMS *fp)
|
||||
void fill_split7_2_config(split7_config_t *split7, const nfapi_nr_config_request_scf_t *config, const NR_DL_FRAME_PARMS *fp)
|
||||
{
|
||||
const nfapi_nr_prach_config_t *prach_config = &config->prach_config;
|
||||
const nfapi_nr_tdd_table_t *tdd_table = &config->tdd_table;
|
||||
@@ -833,7 +641,7 @@ void *ru_thread(void *param)
|
||||
ret = openair0_transport_load(&ru->ifdevice, &ru->openair0_cfg, &ru->eth_params);
|
||||
AssertFatal(ret == 0, "RU %u: openair0_transport_init() ret %d: cannot initialize transport protocol\n", ru->idx, ret);
|
||||
|
||||
if (ru->ifdevice.get_internal_parameter != NULL) {
|
||||
if (ru->ifdevice.get_internal_parameter) {
|
||||
/* it seems the device can "overwrite" (request?) to set the callbacks
|
||||
* for fh_south_in()/fh_south_out() differently */
|
||||
void *t = ru->ifdevice.get_internal_parameter("fh_if4p5_south_in");
|
||||
@@ -842,8 +650,6 @@ void *ru_thread(void *param)
|
||||
t = ru->ifdevice.get_internal_parameter("fh_if4p5_south_out");
|
||||
if (t != NULL)
|
||||
ru->fh_south_out = t;
|
||||
} else {
|
||||
malloc_IF4p5_buffer(ru);
|
||||
}
|
||||
|
||||
int cpu = sched_getcpu();
|
||||
@@ -1088,40 +894,7 @@ void set_function_spec_param(RU_t *ru)
|
||||
switch (ru->if_south) {
|
||||
case LOCAL_RF: // this is an RU with integrated RF (RRU, gNB)
|
||||
reset_meas(&ru->rx_fhaul);
|
||||
if (ru->function == NGFI_RRU_IF5) { // IF5 RRU
|
||||
ru->do_prach = 0; // no prach processing in RU
|
||||
ru->fh_north_in = NULL; // no shynchronous incoming fronthaul from north
|
||||
ru->fh_north_out = fh_if5_north_out; // need only to do send_IF5 reception
|
||||
ru->fh_south_out = tx_rf; // send output to RF
|
||||
ru->fh_north_asynch_in = fh_if5_north_asynch_in; // TX packets come asynchronously
|
||||
ru->feprx = NULL; // nothing (this is a time-domain signal)
|
||||
ru->feptx_ofdm = NULL; // nothing (this is a time-domain signal)
|
||||
ru->feptx_prec = NULL; // nothing (this is a time-domain signal)
|
||||
ru->nr_start_if = nr_start_if; // need to start the if interface for if5
|
||||
ru->ifdevice.host_type = RRU_HOST;
|
||||
ru->rfdevice.host_type = RRU_HOST;
|
||||
ru->ifdevice.eth_params = &ru->eth_params;
|
||||
reset_meas(&ru->rx_fhaul);
|
||||
reset_meas(&ru->tx_fhaul);
|
||||
reset_meas(&ru->compression);
|
||||
reset_meas(&ru->transport);
|
||||
} else if (ru->function == NGFI_RRU_IF4p5) {
|
||||
ru->do_prach = 1; // do part of prach processing in RU
|
||||
ru->fh_north_in = NULL; // no synchronous incoming fronthaul from north
|
||||
ru->fh_north_out = fh_if4p5_north_out; // send_IF4p5 on reception
|
||||
ru->fh_south_out = tx_rf; // send output to RF
|
||||
ru->fh_north_asynch_in = fh_if4p5_north_asynch_in; // TX packets come asynchronously
|
||||
ru->feprx = nr_fep_tp; // this is frequency-shift + DFTs
|
||||
ru->feptx_ofdm = nr_feptx_tp; // this is fep with idft only (no precoding in RRU)
|
||||
ru->feptx_prec = NULL;
|
||||
ru->nr_start_if = nr_start_if; // need to start the if interface for if4p5
|
||||
ru->ifdevice.host_type = RRU_HOST;
|
||||
ru->rfdevice.host_type = RRU_HOST;
|
||||
ru->ifdevice.eth_params = &ru->eth_params;
|
||||
reset_meas(&ru->tx_fhaul);
|
||||
reset_meas(&ru->compression);
|
||||
reset_meas(&ru->transport);
|
||||
} else if (ru->function == gNodeB_3GPP) {
|
||||
AssertFatal(ru->function == gNodeB_3GPP, "ru->function %d not supported for LOCAL_RF\n", ru->function);
|
||||
ru->do_prach = 0; // no prach processing in RU
|
||||
ru->feprx = nr_fep_tp; // this is frequency-shift + DFTs
|
||||
ru->feptx_ofdm = nr_feptx_tp; // this is fep with idft and precoding
|
||||
@@ -1135,7 +908,6 @@ void set_function_spec_param(RU_t *ru)
|
||||
ru->start_rf = start_rf; // need to start the local RF interface
|
||||
ru->stop_rf = stop_rf;
|
||||
ru->start_write_thread = start_write_thread; // starting RF TX in different thread
|
||||
}
|
||||
break;
|
||||
|
||||
case REMOTE_IF5: // the remote unit is IF5 RRU
|
||||
@@ -1153,7 +925,6 @@ void set_function_spec_param(RU_t *ru)
|
||||
ru->nr_start_if = nr_start_if; // need to start if interface for IF5
|
||||
ru->ifdevice.host_type = RAU_HOST;
|
||||
ru->ifdevice.eth_params = &ru->eth_params;
|
||||
|
||||
break;
|
||||
|
||||
case REMOTE_IF4p5:
|
||||
@@ -1161,9 +932,9 @@ void set_function_spec_param(RU_t *ru)
|
||||
ru->feprx = NULL; // DFTs
|
||||
ru->feptx_prec = nr_feptx_prec; // Precoding operation
|
||||
ru->feptx_ofdm = NULL; // no OFDM mod
|
||||
ru->fh_south_in = fh_if4p5_south_in; // synchronous IF4p5 reception
|
||||
ru->fh_south_out = fh_if4p5_south_out; // synchronous IF4p5 transmission
|
||||
ru->fh_south_asynch_in = (ru->if_timing == synch_to_other) ? fh_if4p5_south_in : NULL; // asynchronous UL if synch_to_other
|
||||
ru->fh_south_in = NULL;
|
||||
ru->fh_south_out = NULL;
|
||||
ru->fh_south_asynch_in = NULL;
|
||||
ru->fh_north_out = NULL;
|
||||
ru->fh_north_asynch_in = NULL;
|
||||
ru->start_rf = NULL; // no local RF
|
||||
@@ -1369,47 +1140,14 @@ static void NRRCconfig_RU(configmodule_interface_t *cfg)
|
||||
ru->openair0_cfg.tune_offset = get_softmodem_params()->tune_offset;
|
||||
|
||||
if (strcmp(*param[RU_LOCAL_RF_IDX].strptr, "yes") == 0) {
|
||||
if (!config_isparamset(param, RU_LOCAL_IF_NAME_IDX)) {
|
||||
AssertFatal(!config_isparamset(param, RU_LOCAL_IF_NAME_IDX), "RU_TRANSPORT_PREFERENCE not supported for local RF\n");
|
||||
ru->if_south = LOCAL_RF;
|
||||
ru->function = gNodeB_3GPP;
|
||||
LOG_D(PHY, "Setting function for RU %d to gNodeB_3GPP\n", j);
|
||||
} else {
|
||||
ru->eth_params.local_if_name = strdup(*param[RU_LOCAL_IF_NAME_IDX].strptr);
|
||||
ru->eth_params.my_addr = strdup(*param[RU_LOCAL_ADDRESS_IDX].strptr);
|
||||
ru->eth_params.remote_addr = strdup(*param[RU_REMOTE_ADDRESS_IDX].strptr);
|
||||
ru->eth_params.my_portc = *param[RU_LOCAL_PORTC_IDX].uptr;
|
||||
ru->eth_params.remote_portc = *param[RU_REMOTE_PORTC_IDX].uptr;
|
||||
ru->eth_params.my_portd = *param[RU_LOCAL_PORTD_IDX].uptr;
|
||||
ru->eth_params.remote_portd = *param[RU_REMOTE_PORTD_IDX].uptr;
|
||||
char *str = *param[RU_TRANSPORT_PREFERENCE_IDX].strptr;
|
||||
if (strcmp(str, "udp") == 0) {
|
||||
ru->if_south = LOCAL_RF;
|
||||
ru->function = NGFI_RRU_IF5;
|
||||
ru->eth_params.transp_preference = ETH_UDP_MODE;
|
||||
LOG_D(PHY, "Setting function for RU %d to NGFI_RRU_IF5 (udp)\n", j);
|
||||
} else if (strcmp(str, "raw") == 0) {
|
||||
ru->if_south = LOCAL_RF;
|
||||
ru->function = NGFI_RRU_IF5;
|
||||
ru->eth_params.transp_preference = ETH_RAW_MODE;
|
||||
LOG_D(PHY, "Setting function for RU %d to NGFI_RRU_IF5 (raw)\n", j);
|
||||
} else if (strcmp(str, "udp_if4p5") == 0) {
|
||||
ru->if_south = LOCAL_RF;
|
||||
ru->function = NGFI_RRU_IF4p5;
|
||||
ru->eth_params.transp_preference = ETH_UDP_IF4p5_MODE;
|
||||
LOG_D(PHY, "Setting function for RU %d to NGFI_RRU_IF4p5 (udp)\n", j);
|
||||
} else if (strcmp(str, "raw_if4p5") == 0) {
|
||||
ru->if_south = LOCAL_RF;
|
||||
ru->function = NGFI_RRU_IF4p5;
|
||||
ru->eth_params.transp_preference = ETH_RAW_IF4p5_MODE;
|
||||
LOG_D(PHY, "Setting function for RU %d to NGFI_RRU_IF4p5 (raw)\n", j);
|
||||
}
|
||||
}
|
||||
|
||||
ru->max_pdschReferenceSignalPower = *param[RU_MAX_RS_EPRE_IDX].uptr;
|
||||
ru->max_rxgain = *param[RU_MAX_RXGAIN_IDX].uptr;
|
||||
ru->sf_extension = *param[RU_SF_EXTENSION_IDX].uptr;
|
||||
} // strcmp(local_rf, "yes") == 0
|
||||
else {
|
||||
} else { // strcmp(local_rf, "yes") == 0
|
||||
char *str = *param[RU_TRANSPORT_PREFERENCE_IDX].strptr;
|
||||
LOG_D(PHY, "RU %d: Transport %s\n", j, str);
|
||||
ru->eth_params.local_if_name = strdup(*param[RU_LOCAL_IF_NAME_IDX].strptr);
|
||||
@@ -1432,10 +1170,6 @@ static void NRRCconfig_RU(configmodule_interface_t *cfg)
|
||||
ru->if_south = REMOTE_IF5;
|
||||
ru->function = NGFI_RAU_IF5;
|
||||
ru->eth_params.transp_preference = ETH_RAW_MODE;
|
||||
} else if (strcmp(str, "udp_if4p5") == 0) {
|
||||
ru->if_south = REMOTE_IF4p5;
|
||||
ru->function = NGFI_RAU_IF4p5;
|
||||
ru->eth_params.transp_preference = ETH_UDP_IF4p5_MODE;
|
||||
} else if (strcmp(str, "raw_if4p5") == 0) {
|
||||
ru->if_south = REMOTE_IF4p5;
|
||||
ru->function = NGFI_RAU_IF4p5;
|
||||
|
||||
@@ -223,12 +223,12 @@ void oru_fh_cleanup(void *handle)
|
||||
free(fh);
|
||||
}
|
||||
|
||||
int oru_fh_tx_read_symbol(void *handle, uint32_t **txdataF, int nb_tx, int *frame, int *slot, int *symbol)
|
||||
int oru_fh_tx_read_symbol(void *handle, uint32_t **txdataF, int nb_tx, uint64_t *hyper_frame, int *frame, int *slot, int *symbol)
|
||||
{
|
||||
if (!handle)
|
||||
return -1;
|
||||
oru_fh_t *fh = (oru_fh_t *)handle;
|
||||
read_dl_iq(fh->packet_processor, txdataF, nb_tx, frame, slot, symbol);
|
||||
read_dl_iq(fh->packet_processor, txdataF, nb_tx, hyper_frame, frame, slot, symbol);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -240,7 +240,7 @@ int oru_fh_get_ready_jobs(void *handle)
|
||||
return get_ready_job_count(fh->packet_processor);
|
||||
}
|
||||
|
||||
int oru_fh_get_utc_anchor_point(void *handle, uint32_t *frame, uint32_t *slot, struct timespec *ts)
|
||||
int oru_fh_get_utc_anchor_point(void *handle, uint64_t *hyper_frame, uint32_t *frame, uint32_t *slot, struct timespec *ts)
|
||||
{
|
||||
if (!handle || !frame || !slot || !ts)
|
||||
return -1;
|
||||
@@ -249,6 +249,7 @@ int oru_fh_get_utc_anchor_point(void *handle, uint32_t *frame, uint32_t *slot, s
|
||||
absolute_gps_symbol -= absolute_gps_symbol % NR_SYMBOLS_PER_SLOT; // Round down to start of current slot
|
||||
uint64_t absolute_slot = absolute_gps_symbol / NR_SYMBOLS_PER_SLOT;
|
||||
uint32_t slots_per_frame = 10 << fh->cfg.numerology;
|
||||
*hyper_frame = (absolute_slot / slots_per_frame) / 1024;
|
||||
*frame = (absolute_slot / slots_per_frame) % 1024;
|
||||
*slot = absolute_slot % slots_per_frame;
|
||||
|
||||
@@ -267,11 +268,17 @@ void oru_fh_rx_send_prach(void *handle, uint32_t **prachF, int nb_rx, int frame,
|
||||
write_prach_iq(fh->packet_processor, prachF, nb_rx, frame, slot, symbol);
|
||||
}
|
||||
|
||||
void oru_fh_rx_send_pusch(void *handle, uint32_t **puschF, int nb_rx, int frame, int slot, int symbol)
|
||||
int oru_fh_poll_ul_job(void *handle, ul_job_t *job) {
|
||||
oru_fh_t *fh = (oru_fh_t *)handle;
|
||||
AssertFatal(fh, "Invalid handle\n");
|
||||
return poll_ul_job(fh->packet_processor, job);
|
||||
}
|
||||
|
||||
void oru_fh_rx_send_pusch(void *handle, uint32_t *puschF, int symbol_index, const ul_job_t *job)
|
||||
{
|
||||
oru_fh_t *fh = (oru_fh_t *)handle;
|
||||
AssertFatal(fh, "Invalid handle\n");
|
||||
write_ul_iq(fh->packet_processor, puschF, nb_rx, frame, slot, symbol);
|
||||
write_ul_iq(fh->packet_processor, puschF, symbol_index, job);
|
||||
}
|
||||
|
||||
int oru_fh_start(void *handle)
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#include "oru_io.h"
|
||||
#include <stdint.h>
|
||||
#include "oru_packet_processor.h"
|
||||
|
||||
typedef struct {
|
||||
char *dpdk_devices[MAX_RU_PORTS];
|
||||
@@ -74,23 +75,25 @@ int oru_fh_get_ready_jobs(void *handle);
|
||||
* @param handle Pointer to the fronthaul handle.
|
||||
* @param txdataF Array of pointers to buffers to store the received frequency-domain IQ samples (per TX antenna).
|
||||
* @param nb_tx Number of TX antennas.
|
||||
* @param hyper_frame Absolute GPS hyper-frame number
|
||||
* @param frame Pointer to store the frame number of the read symbol.
|
||||
* @param slot Pointer to store the slot number of the read symbol.
|
||||
* @param symbol Pointer to store the symbol number.
|
||||
* @return 0 on success or -1 on failure
|
||||
*/
|
||||
int oru_fh_tx_read_symbol(void *handle, uint32_t **txdataF, int nb_tx, int *frame, int *slot, int *symbol);
|
||||
int oru_fh_tx_read_symbol(void *handle, uint32_t **txdataF, int nb_tx, uint64_t *hyper_frame, int *frame, int *slot, int *symbol);
|
||||
|
||||
/**
|
||||
* @brief Get the UTC anchor point mapping between 5G time and system time.
|
||||
*
|
||||
* @param handle Pointer to the fronthaul handle.
|
||||
* @param frame Pointer to store the reference frame number.
|
||||
* @param hyper_frame Pointer to store the reference hyperframe number (1024 frames each)
|
||||
* @param slot Pointer to store the reference slot number.
|
||||
* @param ts Pointer to a timespec structure to store the corresponding system time.
|
||||
* @return 0 on success, negative on error.
|
||||
*/
|
||||
int oru_fh_get_utc_anchor_point(void *handle, uint32_t* frame, uint32_t* slot, struct timespec *ts);
|
||||
int oru_fh_get_utc_anchor_point(void *handle, uint64_t *hyper_frame, uint32_t* frame, uint32_t* slot, struct timespec *ts);
|
||||
|
||||
/**
|
||||
* @brief Send PRACH symbol data (U-Plane) over the Fronthaul interface.
|
||||
@@ -104,17 +107,25 @@ int oru_fh_get_utc_anchor_point(void *handle, uint32_t* frame, uint32_t* slot, s
|
||||
*/
|
||||
void oru_fh_rx_send_prach(void *handle, uint32_t **prachF, int nb_rx, int frame, int slot, int symbol);
|
||||
|
||||
/**
|
||||
* @brief Poll for pending UL jobs
|
||||
*
|
||||
* @param handle Pointer to the fronthaul handle.
|
||||
* @param job Job descriptor
|
||||
*
|
||||
* @returns 0 if successful, -1 otherwise
|
||||
*/
|
||||
int oru_fh_poll_ul_job(void *handle, ul_job_t *job);
|
||||
|
||||
/**
|
||||
* @brief Send PUSCH symbol data (U-Plane) over the Fronthaul interface.
|
||||
*
|
||||
* @param handle Pointer to the fronthaul handle.
|
||||
* @param puschF Array of pointers to buffers containing the frequency-domain PUSCH IQ samples.
|
||||
* @param nb_rx Number of RX antennas.
|
||||
* @param frame Target frame number.
|
||||
* @param slot Target slot number.
|
||||
* @param symbol Target symbol number.
|
||||
* @param symbol Absolute symbol index within slot (0–13); must be within the job's symbol range
|
||||
* @param job Job descriptor
|
||||
*/
|
||||
void oru_fh_rx_send_pusch(void *handle, uint32_t **puschF, int nb_rx, int frame, int slot, int symbol);
|
||||
void oru_fh_rx_send_pusch(void *handle, uint32_t *puschF, int symbol, const ul_job_t *job);
|
||||
|
||||
/**
|
||||
* @brief Start the O-RU Fronthaul processing threads and loops.
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <time.h>
|
||||
#include "assertions.h"
|
||||
#include "log.h"
|
||||
#include <rte_ring.h>
|
||||
@@ -16,8 +17,21 @@
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <stdatomic.h>
|
||||
|
||||
#define PRACH_ERR_LOG_RATELIMIT 10000
|
||||
|
||||
#define RATELIMIT(n, block) \
|
||||
do { \
|
||||
static _Atomic unsigned long counter = 0; \
|
||||
unsigned long current = atomic_fetch_add_explicit(&counter, 1, memory_order_relaxed); \
|
||||
if (current % (n) == 0) { \
|
||||
block \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define DL_JOB_RING_SIZE 128
|
||||
#define UL_JOB_RING_SIZE 128
|
||||
#define MAX_CONCURRENT_DL_JOBS (DL_JOB_RING_SIZE - 1)
|
||||
#define NUM_CONCURRENT_DL_SYMBOL_WINDOWS MAX_CONCURRENT_DL_JOBS
|
||||
#define NUM_CONCURRENT_UL_SYMBOL_WINDOWS 128
|
||||
@@ -28,13 +42,7 @@
|
||||
#define SYMBOL_BITMASK_SIZE ((NR_SYMBOLS_PER_SLOT * MAX_TDD_PATTERN_LENGTH_MS * MAX_SLOTS_PER_MS + 7) / 8)
|
||||
#define MAX_RX_FRAGMENTS 4
|
||||
#define MAX_MBUFS_PER_SYMBOL 64
|
||||
|
||||
typedef enum {
|
||||
SYM_UL_IDLE,
|
||||
SYM_UL_ACTIVE,
|
||||
SYM_UL_READY,
|
||||
SYM_UL_EMPTY,
|
||||
} ul_symbol_job_state_t;
|
||||
#define MAX_SLOTS_PER_FRAME 160
|
||||
|
||||
typedef struct {
|
||||
struct {
|
||||
@@ -54,43 +62,43 @@ typedef struct {
|
||||
} dl_symbol_job_t;
|
||||
|
||||
typedef struct {
|
||||
struct {
|
||||
bool cplane_received;
|
||||
int section_id;
|
||||
int num_prb;
|
||||
int start_prb;
|
||||
} per_antenna[MAX_ANTENNAS];
|
||||
uint64_t absolute_symbol;
|
||||
ul_symbol_job_state_t state;
|
||||
} ul_symbol_job_t;
|
||||
typedef struct {
|
||||
struct {
|
||||
bool cplane_received;
|
||||
bool active;
|
||||
uint64_t start_absolute_symbol;
|
||||
uint32_t num_symbols;
|
||||
int section_id;
|
||||
int num_prb;
|
||||
int start_prb;
|
||||
int filter_id;
|
||||
} per_antenna[MAX_ANTENNAS];
|
||||
uint64_t absolute_symbol;
|
||||
} prach_symbol_job_t;
|
||||
} prach_job_t;
|
||||
typedef struct {
|
||||
_Atomic(uint64_t) dl_tdd_mismatch;
|
||||
_Atomic(uint64_t) ul_tdd_mismatch;
|
||||
_Atomic(uint64_t) ul_cplane_missing;
|
||||
_Atomic(uint64_t) prach_cplane_missing;
|
||||
_Atomic(uint64_t) prach_cplane_missing_ant;
|
||||
_Atomic(uint64_t) prach_cplane_missing_inactive;
|
||||
_Atomic(uint64_t) prach_cplane_missing_stale;
|
||||
_Atomic(uint64_t) prach_cplane_missing_early;
|
||||
_Atomic(uint64_t) prach_out_of_mbufs;
|
||||
_Atomic(uint64_t) prach_jobs_pool_exhausted;
|
||||
_Atomic(uint64_t) out_of_mbufs;
|
||||
_Atomic(uint64_t) total_uplane_sent;
|
||||
_Atomic(int64_t) ul_uplane_ota_delay_sum;
|
||||
_Atomic(uint64_t) ul_uplane_ota_delay_count;
|
||||
} thread_safe_stats_t;
|
||||
|
||||
typedef struct {
|
||||
dl_symbol_job_t dl_symbol_jobs[MAX_CONCURRENT_DL_JOBS];
|
||||
dl_symbol_job_t *dl_symbol_rx_window[NUM_CONCURRENT_DL_SYMBOL_WINDOWS];
|
||||
bool was_dl_symbol_completed[NUM_CONCURRENT_DL_SYMBOL_WINDOWS];
|
||||
ul_symbol_job_t ul_symbol_jobs[NUM_CONCURRENT_UL_SYMBOL_WINDOWS];
|
||||
prach_symbol_job_t prach_jobs[NUM_CONCURRENT_UL_SYMBOL_WINDOWS];
|
||||
prach_job_t prach_jobs[MAX_SLOTS_PER_FRAME][MAX_ANTENNAS];
|
||||
uint64_t current_absolute_symbol;
|
||||
uint64_t last_pushed_symbol;
|
||||
struct rte_ring *dl_free_jobs;
|
||||
struct rte_ring *dl_ready_jobs;
|
||||
struct rte_ring *ul_free_jobs;
|
||||
struct rte_ring *ul_ready_jobs;
|
||||
ul_job_t ul_jobs_pool[UL_JOB_RING_SIZE];
|
||||
uint32_t T2a_min_cp_sym_diff;
|
||||
uint32_t T2a_max_cp_sym_diff;
|
||||
uint32_t T2a_min_up_dl_sym_diff;
|
||||
@@ -169,6 +177,13 @@ void *init_packet_processor(int numerology,
|
||||
for (int i = 0; i < MAX_CONCURRENT_DL_JOBS; i++) {
|
||||
rte_ring_enqueue(ctx->dl_free_jobs, (void *)&ctx->dl_symbol_jobs[i]);
|
||||
}
|
||||
ctx->ul_ready_jobs = rte_ring_create("ul_ready_jobs", UL_JOB_RING_SIZE, rte_socket_id(), 0);
|
||||
AssertFatal(ctx->ul_ready_jobs != NULL, "Failed to create ring ul_ready_jobs\n");
|
||||
ctx->ul_free_jobs = rte_ring_create("ul_free_jobs", UL_JOB_RING_SIZE, rte_socket_id(), 0);
|
||||
AssertFatal(ctx->ul_free_jobs != NULL, "Failed to create ring ul_free_jobs\n");
|
||||
for (int i = 0; i < UL_JOB_RING_SIZE - 1; i++) {
|
||||
rte_ring_enqueue(ctx->ul_free_jobs, (void *)&ctx->ul_jobs_pool[i]);
|
||||
}
|
||||
|
||||
ctx->eaxcid_config = (struct xran_eaxcid_config){.mask_cuPortId = 0xF000,
|
||||
.mask_bandSectorId = 0x0F00,
|
||||
@@ -201,6 +216,12 @@ void cleanup_packet_processor(void *context)
|
||||
if (ctx->dl_free_jobs) {
|
||||
rte_ring_free(ctx->dl_free_jobs);
|
||||
}
|
||||
if (ctx->ul_ready_jobs) {
|
||||
rte_ring_free(ctx->ul_ready_jobs);
|
||||
}
|
||||
if (ctx->ul_free_jobs) {
|
||||
rte_ring_free(ctx->ul_free_jobs);
|
||||
}
|
||||
free(ctx);
|
||||
}
|
||||
}
|
||||
@@ -465,6 +486,7 @@ static void handle_dl_cplane_packet(oru_packet_processor_context_t *ctx,
|
||||
}
|
||||
if (job->per_antenna[ant_id].cplane_received) {
|
||||
ctx->stats.cplane_err_dup++;
|
||||
ctx->stats.cplane_err_dup_dl++;
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -507,30 +529,25 @@ static void handle_ul_cplane_packet(oru_packet_processor_context_t *ctx,
|
||||
ctx->stats.ul_tdd_mismatch++;
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < num_symbols; i++) {
|
||||
uint32_t job_index = (target_absolute_symbol + i) % NUM_CONCURRENT_UL_SYMBOL_WINDOWS;
|
||||
ul_symbol_job_t *job = &ctx->ul_symbol_jobs[job_index];
|
||||
if (job->state == SYM_UL_IDLE) {
|
||||
job->absolute_symbol = target_absolute_symbol + i;
|
||||
job->state = SYM_UL_ACTIVE;
|
||||
for (int j = 0; j < MAX_ANTENNAS; j++) {
|
||||
job->per_antenna[j].cplane_received = false;
|
||||
}
|
||||
} else if (job->state == SYM_UL_READY) {
|
||||
ul_job_t *ul_job = NULL;
|
||||
if (rte_ring_dequeue(ctx->ul_free_jobs, (void **)&ul_job) == 0) {
|
||||
memset(ul_job, 0, sizeof(*ul_job));
|
||||
ul_job->response_payload.section_id = section->hdr.u1.common.sectionId;
|
||||
ul_job->response_payload.comp_method = hdr->udComp.udCompMeth;
|
||||
ul_job->response_payload.iq_width = hdr->udComp.udIqWidth == 0 ? 16 : hdr->udComp.udIqWidth;
|
||||
uint64_t absolute_gps_symbol = target_absolute_symbol;
|
||||
ul_job->hyper_frame = absolute_gps_symbol / (1024 * (10 * (1 << ctx->numerology) * 14));
|
||||
ul_job->frame = (absolute_gps_symbol / (10 * (1 << ctx->numerology) * 14)) % 1024;
|
||||
ul_job->slot_in_frame = (absolute_gps_symbol % (10 * (1 << ctx->numerology) * 14)) / 14;
|
||||
ul_job->symbol = absolute_gps_symbol % 14;
|
||||
ul_job->num_symbols = num_symbols;
|
||||
ul_job->antenna_id = ant_id;
|
||||
ul_job->num_prb = section->hdr.u1.common.numPrbc == 0 ? ctx->num_prb : section->hdr.u1.common.numPrbc;
|
||||
ul_job->start_prb = section->hdr.u1.common.startPrbc;
|
||||
int ret = rte_ring_enqueue(ctx->ul_ready_jobs, (void *)ul_job);
|
||||
AssertFatal(ret == 0, "Failed to enqueue ul_job to ul_ready_jobs ring\n");
|
||||
} else {
|
||||
ctx->stats.application_too_slow++;
|
||||
return;
|
||||
}
|
||||
if (job->per_antenna[ant_id].cplane_received) {
|
||||
ctx->stats.cplane_err_dup++;
|
||||
return;
|
||||
}
|
||||
job->absolute_symbol = target_absolute_symbol + i;
|
||||
if (!job->per_antenna[ant_id].cplane_received) {
|
||||
job->per_antenna[ant_id].cplane_received = true;
|
||||
job->per_antenna[ant_id].section_id = section->hdr.u1.common.sectionId;
|
||||
job->per_antenna[ant_id].num_prb = section->hdr.u1.common.numPrbc == 0 ? ctx->num_prb : section->hdr.u1.common.numPrbc;
|
||||
job->per_antenna[ant_id].start_prb = section->hdr.u1.common.startPrbc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -541,6 +558,8 @@ void handle_prach_cplane_packet(oru_packet_processor_context_t *ctx,
|
||||
{
|
||||
if (hdr->cmnhdr.numOfSections != 1) {
|
||||
ctx->stats.cplane_err_hdr++;
|
||||
RATELIMIT(PRACH_ERR_LOG_RATELIMIT,
|
||||
{ LOG_W(HW, "PRACH CP: Invalid numOfSections %d (expected 1)\n", hdr->cmnhdr.numOfSections); });
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -550,11 +569,14 @@ void handle_prach_cplane_packet(oru_packet_processor_context_t *ctx,
|
||||
struct xran_cp_radioapp_section3 *section = (void *)rte_pktmbuf_adj(pkt, sizeof(struct xran_cp_radioapp_section3_header));
|
||||
if (section == NULL) {
|
||||
ctx->stats.cplane_err_hdr++;
|
||||
RATELIMIT(PRACH_ERR_LOG_RATELIMIT, { LOG_W(HW, "PRACH CP: Failed to adjust mbuf for section3 header\n"); });
|
||||
return;
|
||||
}
|
||||
*((uint64_t *)section) = rte_be_to_cpu_64(*((uint64_t *)section));
|
||||
int aarx = ant_id - ctx->prach_eaxc_offset;
|
||||
if (aarx < 0 || aarx >= MAX_ANTENNAS) {
|
||||
RATELIMIT(PRACH_ERR_LOG_RATELIMIT,
|
||||
{ LOG_W(HW, "PRACH CP: Invalid aarx %d (ant_id %d, eaxc_offset %d)\n", aarx, ant_id, ctx->prach_eaxc_offset); });
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -574,26 +596,34 @@ void handle_prach_cplane_packet(oru_packet_processor_context_t *ctx,
|
||||
txrx_window_histogram_count(&ctx->stats.prach_cplane_hist, diff);
|
||||
uint64_t target_absolute_symbol = ctx->current_absolute_symbol + diff;
|
||||
|
||||
for (int i = 0; i < num_symbols; i++) {
|
||||
uint32_t job_index = (target_absolute_symbol + i) % NUM_CONCURRENT_UL_SYMBOL_WINDOWS;
|
||||
prach_symbol_job_t *job = &ctx->prach_jobs[job_index];
|
||||
|
||||
if (job->absolute_symbol != target_absolute_symbol + i) {
|
||||
job->absolute_symbol = target_absolute_symbol + i;
|
||||
for (int j = 0; j < MAX_ANTENNAS; j++) {
|
||||
job->per_antenna[j].cplane_received = false;
|
||||
}
|
||||
if (slot_in_frame < 0 || slot_in_frame >= MAX_SLOTS_PER_FRAME) {
|
||||
RATELIMIT(PRACH_ERR_LOG_RATELIMIT, { LOG_W(HW, "PRACH CP: Invalid slot_in_frame %d\n", slot_in_frame); });
|
||||
return;
|
||||
}
|
||||
|
||||
if (job->per_antenna[aarx].cplane_received) {
|
||||
continue;
|
||||
}
|
||||
job->per_antenna[aarx].cplane_received = true;
|
||||
job->per_antenna[aarx].section_id = section->hdr.u1.common.sectionId;
|
||||
job->per_antenna[aarx].num_prb = section->hdr.u1.common.numPrbc == 0 ? ctx->num_prb : section->hdr.u1.common.numPrbc;
|
||||
job->per_antenna[aarx].start_prb = section->hdr.u1.common.startPrbc;
|
||||
job->per_antenna[aarx].filter_id = hdr->cmnhdr.field.filterIndex;
|
||||
prach_job_t *job = &ctx->prach_jobs[slot_in_frame][aarx];
|
||||
if (job->active && job->start_absolute_symbol == target_absolute_symbol) {
|
||||
ctx->stats.cplane_err_dup++;
|
||||
ctx->stats.cplane_err_dup_prach++;
|
||||
RATELIMIT(PRACH_ERR_LOG_RATELIMIT, {
|
||||
LOG_W(HW, "PRACH CP: Duplicate packet for slot %d, aarx %d, start_symbol %lu\n", slot_in_frame, aarx, target_absolute_symbol);
|
||||
});
|
||||
return;
|
||||
}
|
||||
job->active = true;
|
||||
job->start_absolute_symbol = target_absolute_symbol;
|
||||
job->num_symbols = num_symbols;
|
||||
job->section_id = section->hdr.u1.common.sectionId;
|
||||
job->num_prb = section->hdr.u1.common.numPrbc == 0 ? ctx->num_prb : section->hdr.u1.common.numPrbc;
|
||||
job->start_prb = section->hdr.u1.common.startPrbc;
|
||||
job->filter_id = hdr->cmnhdr.field.filterIndex;
|
||||
RATELIMIT(PRACH_ERR_LOG_RATELIMIT, {
|
||||
LOG_A(HW,
|
||||
"PRACH JOB added slot_in_frame %d, aarx %d target_absolute_symbol %lu\n",
|
||||
slot_in_frame,
|
||||
aarx,
|
||||
target_absolute_symbol);
|
||||
});
|
||||
}
|
||||
|
||||
void handle_cplane_packet(void *context, void *pkt)
|
||||
@@ -633,20 +663,24 @@ void handle_cplane_packet(void *context, void *pkt)
|
||||
}
|
||||
*((uint64_t *)section) = rte_be_to_cpu_64(*((uint64_t *)section));
|
||||
if (hdr->cmnhdr.field.dataDirection == XRAN_DIR_DL) {
|
||||
ctx->stats.cplane_received_dl++;
|
||||
handle_dl_cplane_packet(ctx, pkt, hdr, section, ant_id);
|
||||
} else {
|
||||
ctx->stats.cplane_received_ul++;
|
||||
handle_ul_cplane_packet(ctx, pkt, hdr, section, ant_id);
|
||||
}
|
||||
rte_pktmbuf_free(pkt);
|
||||
return;
|
||||
}
|
||||
case XRAN_CP_SECTIONTYPE_3: {
|
||||
ctx->stats.cplane_received_prach++;
|
||||
struct xran_cp_radioapp_section3_header *hdr = (struct xran_cp_radioapp_section3_header *)apphdr;
|
||||
handle_prach_cplane_packet(ctx, pkt, hdr, ant_id);
|
||||
rte_pktmbuf_free(pkt);
|
||||
return;
|
||||
}
|
||||
default:
|
||||
ctx->stats.cplane_received_other++;
|
||||
rte_pktmbuf_free(pkt);
|
||||
return;
|
||||
}
|
||||
@@ -656,19 +690,38 @@ static void print_histogram(const char *name, txrx_histogram_t *hist, uint32_t w
|
||||
{
|
||||
if (hist->count == 0)
|
||||
return;
|
||||
printf(" %s (mean: %.2f symbols) window start: %u, end %u:\n", name, (double)hist->sum / hist->count, window_start, window_end);
|
||||
char buf[4096];
|
||||
int len = snprintf(buf,
|
||||
sizeof(buf),
|
||||
" %s (mean: %.2f symbols) window [%u, %u]:",
|
||||
name,
|
||||
(double)hist->sum / hist->count,
|
||||
window_start,
|
||||
window_end);
|
||||
bool first = true;
|
||||
for (int i = 0; i < HIST_SIZE; i++) {
|
||||
if (hist->hist[i] > 0) {
|
||||
int bucket = i - HIST_SIZE / 2;
|
||||
char bin_str[64];
|
||||
int bin_len = 0;
|
||||
if (i == 0) {
|
||||
printf(" <= %+d : %lu\n", bucket, hist->hist[i]);
|
||||
bin_len = snprintf(bin_str, sizeof(bin_str), "%s<=%+d:%lu", first ? " " : ", ", bucket, hist->hist[i]);
|
||||
} else if (i == HIST_SIZE - 1) {
|
||||
printf(" >= %+d : %lu\n", bucket, hist->hist[i]);
|
||||
bin_len = snprintf(bin_str, sizeof(bin_str), "%s>=%+d:%lu", first ? " " : ", ", bucket, hist->hist[i]);
|
||||
} else {
|
||||
printf(" %+d : %lu\n", bucket, hist->hist[i]);
|
||||
bin_len = snprintf(bin_str, sizeof(bin_str), "%s%+d:%lu", first ? " " : ", ", bucket, hist->hist[i]);
|
||||
}
|
||||
first = false;
|
||||
if (len + bin_len < sizeof(buf)) {
|
||||
strcpy(buf + len, bin_str);
|
||||
len += bin_len;
|
||||
} else {
|
||||
break; // buffer full
|
||||
}
|
||||
}
|
||||
}
|
||||
LOG_I(HW, "%s\n", buf);
|
||||
memset(hist, 0, sizeof(*hist));
|
||||
}
|
||||
|
||||
void print_packet_processor_stats(void *context)
|
||||
@@ -677,39 +730,73 @@ void print_packet_processor_stats(void *context)
|
||||
if (ctx == NULL)
|
||||
return;
|
||||
|
||||
printf("ORU Packet Processor Stats:\n");
|
||||
printf(" Total C-Plane Packets received: %lu\n", ctx->stats.total_cplane);
|
||||
printf(" Total U-Plane Packets received: %lu\n", ctx->stats.total_uplane_received);
|
||||
printf(" Total U-Plane Packets sent: %lu\n", ctx->thread_safe_stats.total_uplane_sent);
|
||||
LOG_I(HW, "ORU Packet Processor Stats:\n");
|
||||
LOG_I(HW,
|
||||
" Total C-Plane Packets received: %lu (DL: %lu, UL: %lu, PRACH: %lu, Other: %lu)\n",
|
||||
ctx->stats.total_cplane,
|
||||
ctx->stats.cplane_received_dl,
|
||||
ctx->stats.cplane_received_ul,
|
||||
ctx->stats.cplane_received_prach,
|
||||
ctx->stats.cplane_received_other);
|
||||
LOG_I(HW, " Total U-Plane Packets received: %lu\n", ctx->stats.total_uplane_received);
|
||||
LOG_I(HW, " Total U-Plane Packets sent: %lu\n", ctx->thread_safe_stats.total_uplane_sent);
|
||||
if (ctx->thread_safe_stats.ul_uplane_ota_delay_count > 0)
|
||||
LOG_I(HW,
|
||||
" UL U-Plane OTA delay (mean symbols): %.2f (%lu packets)\n",
|
||||
(double)(int64_t)ctx->thread_safe_stats.ul_uplane_ota_delay_sum
|
||||
/ (double)ctx->thread_safe_stats.ul_uplane_ota_delay_count,
|
||||
(uint64_t)ctx->thread_safe_stats.ul_uplane_ota_delay_count);
|
||||
|
||||
if (ctx->stats.cplane_err_hdr > 0)
|
||||
printf(" C-Plane Header Errors: %lu\n", ctx->stats.cplane_err_hdr);
|
||||
LOG_I(HW, " C-Plane Header Errors: %lu\n", ctx->stats.cplane_err_hdr);
|
||||
if (ctx->stats.cplane_err_ver > 0)
|
||||
printf(" C-Plane Protocol Version Errors: %lu\n", ctx->stats.cplane_err_ver);
|
||||
LOG_I(HW, " C-Plane Protocol Version Errors: %lu\n", ctx->stats.cplane_err_ver);
|
||||
if (ctx->stats.cplane_err_early > 0)
|
||||
printf(" C-Plane Timing Early Errors: %lu\n", ctx->stats.cplane_err_early);
|
||||
LOG_I(HW, " C-Plane Timing Early Errors: %lu\n", ctx->stats.cplane_err_early);
|
||||
if (ctx->stats.cplane_err_late > 0)
|
||||
printf(" C-Plane Timing Late Errors: %lu\n", ctx->stats.cplane_err_late);
|
||||
LOG_I(HW, " C-Plane Timing Late Errors: %lu\n", ctx->stats.cplane_err_late);
|
||||
if (ctx->stats.cplane_err_dup > 0)
|
||||
printf(" C-Plane Duplicate Packet Errors: %lu\n", ctx->stats.cplane_err_dup);
|
||||
LOG_I(HW,
|
||||
" C-Plane Duplicate Packet Errors: %lu (DL: %lu, UL: %lu, PRACH: %lu)\n",
|
||||
ctx->stats.cplane_err_dup,
|
||||
ctx->stats.cplane_err_dup_dl,
|
||||
ctx->stats.cplane_err_dup_ul,
|
||||
ctx->stats.cplane_err_dup_prach);
|
||||
if (ctx->stats.uplane_err_early > 0)
|
||||
printf(" U-Plane Timing Early Errors: %lu\n", ctx->stats.uplane_err_early);
|
||||
LOG_I(HW, " U-Plane Timing Early Errors: %lu\n", ctx->stats.uplane_err_early);
|
||||
if (ctx->stats.uplane_err_late > 0)
|
||||
printf(" U-Plane Timing Late Errors: %lu\n", ctx->stats.uplane_err_late);
|
||||
LOG_I(HW, " U-Plane Timing Late Errors: %lu\n", ctx->stats.uplane_err_late);
|
||||
if (ctx->stats.uplane_err_dup > 0)
|
||||
printf(" U-Plane Duplicate Packet Errors: %lu\n", ctx->stats.uplane_err_dup);
|
||||
LOG_I(HW, " U-Plane Duplicate Packet Errors: %lu\n", ctx->stats.uplane_err_dup);
|
||||
if (ctx->stats.uplane_missing_cplane > 0)
|
||||
printf(" U-Plane Missing C-Plane Errors: %lu\n", ctx->stats.uplane_missing_cplane);
|
||||
LOG_I(HW, " U-Plane Missing C-Plane Errors: %lu\n", ctx->stats.uplane_missing_cplane);
|
||||
if (ctx->stats.dl_tdd_mismatch + ctx->thread_safe_stats.dl_tdd_mismatch > 0)
|
||||
printf(" DL TDD Mismatch Errors: %lu\n", ctx->stats.dl_tdd_mismatch + ctx->thread_safe_stats.dl_tdd_mismatch);
|
||||
LOG_I(HW, " DL TDD Mismatch Errors: %lu\n", ctx->stats.dl_tdd_mismatch + ctx->thread_safe_stats.dl_tdd_mismatch);
|
||||
if (ctx->stats.ul_tdd_mismatch + ctx->thread_safe_stats.ul_tdd_mismatch > 0)
|
||||
printf(" UL TDD Mismatch Errors: %lu\n", ctx->stats.ul_tdd_mismatch + ctx->thread_safe_stats.ul_tdd_mismatch);
|
||||
LOG_I(HW, " UL TDD Mismatch Errors: %lu\n", ctx->stats.ul_tdd_mismatch + ctx->thread_safe_stats.ul_tdd_mismatch);
|
||||
if (ctx->stats.ul_cplane_missing + ctx->thread_safe_stats.ul_cplane_missing > 0)
|
||||
printf(" UL C-Plane Missing Errors: %lu\n", ctx->stats.ul_cplane_missing + ctx->thread_safe_stats.ul_cplane_missing);
|
||||
LOG_I(HW, " UL C-Plane Missing Errors: %lu\n", ctx->stats.ul_cplane_missing + ctx->thread_safe_stats.ul_cplane_missing);
|
||||
if (ctx->stats.prach_cplane_missing + ctx->thread_safe_stats.prach_cplane_missing > 0)
|
||||
LOG_I(HW,
|
||||
" PRACH C-Plane Missing Errors: %lu (Never Received: %lu, Stale: %lu, Early: %lu)\n",
|
||||
ctx->stats.prach_cplane_missing + ctx->thread_safe_stats.prach_cplane_missing,
|
||||
ctx->stats.prach_cplane_missing_inactive + ctx->thread_safe_stats.prach_cplane_missing_inactive,
|
||||
ctx->stats.prach_cplane_missing_stale + ctx->thread_safe_stats.prach_cplane_missing_stale,
|
||||
ctx->stats.prach_cplane_missing_early + ctx->thread_safe_stats.prach_cplane_missing_early);
|
||||
if (ctx->stats.prach_cplane_missing_ant + ctx->thread_safe_stats.prach_cplane_missing_ant > 0)
|
||||
LOG_I(HW,
|
||||
" PRACH Ant C-Plane Missing Errors: %lu\n",
|
||||
ctx->stats.prach_cplane_missing_ant + ctx->thread_safe_stats.prach_cplane_missing_ant);
|
||||
if (ctx->stats.prach_out_of_mbufs + ctx->thread_safe_stats.prach_out_of_mbufs > 0)
|
||||
LOG_I(HW, " PRACH Out Of Mbufs Errors: %lu\n", ctx->stats.prach_out_of_mbufs + ctx->thread_safe_stats.prach_out_of_mbufs);
|
||||
if (ctx->stats.prach_jobs_pool_exhausted + ctx->thread_safe_stats.prach_jobs_pool_exhausted > 0)
|
||||
LOG_I(HW,
|
||||
" PRACH Jobs Pool Exhausted Errors: %lu\n",
|
||||
ctx->stats.prach_jobs_pool_exhausted + ctx->thread_safe_stats.prach_jobs_pool_exhausted);
|
||||
if (ctx->stats.out_of_mbufs + ctx->thread_safe_stats.out_of_mbufs > 0)
|
||||
printf(" Out Of Mbufs Errors: %lu\n", ctx->stats.out_of_mbufs + ctx->thread_safe_stats.out_of_mbufs);
|
||||
LOG_I(HW, " Out Of Mbufs Errors: %lu\n", ctx->stats.out_of_mbufs + ctx->thread_safe_stats.out_of_mbufs);
|
||||
if (ctx->stats.application_too_slow > 0)
|
||||
printf(" Application Too Slow Errors: %lu\n", ctx->stats.application_too_slow);
|
||||
LOG_I(HW, " Application Too Slow Errors: %lu\n", ctx->stats.application_too_slow);
|
||||
|
||||
print_histogram("DL C-Plane", &ctx->stats.dl_cplane_hist, ctx->T2a_max_cp_sym_diff, ctx->T2a_min_cp_sym_diff);
|
||||
print_histogram("DL U-Plane", &ctx->stats.dl_uplane_hist, ctx->T2a_max_up_dl_sym_diff, ctx->T2a_min_up_dl_sym_diff);
|
||||
@@ -725,8 +812,17 @@ void get_packet_processor_stats(void *context, oru_packet_processor_stats_t *out
|
||||
out_stats->dl_tdd_mismatch += ctx->thread_safe_stats.dl_tdd_mismatch;
|
||||
out_stats->ul_tdd_mismatch += ctx->thread_safe_stats.ul_tdd_mismatch;
|
||||
out_stats->ul_cplane_missing += ctx->thread_safe_stats.ul_cplane_missing;
|
||||
out_stats->prach_cplane_missing += ctx->thread_safe_stats.prach_cplane_missing;
|
||||
out_stats->prach_cplane_missing_ant += ctx->thread_safe_stats.prach_cplane_missing_ant;
|
||||
out_stats->prach_cplane_missing_inactive += ctx->thread_safe_stats.prach_cplane_missing_inactive;
|
||||
out_stats->prach_cplane_missing_stale += ctx->thread_safe_stats.prach_cplane_missing_stale;
|
||||
out_stats->prach_cplane_missing_early += ctx->thread_safe_stats.prach_cplane_missing_early;
|
||||
out_stats->prach_out_of_mbufs += ctx->thread_safe_stats.prach_out_of_mbufs;
|
||||
out_stats->prach_jobs_pool_exhausted += ctx->thread_safe_stats.prach_jobs_pool_exhausted;
|
||||
out_stats->out_of_mbufs += ctx->thread_safe_stats.out_of_mbufs;
|
||||
out_stats->total_uplane_sent = ctx->thread_safe_stats.total_uplane_sent;
|
||||
out_stats->ul_uplane_ota_delay_sum += ctx->thread_safe_stats.ul_uplane_ota_delay_sum;
|
||||
out_stats->ul_uplane_ota_delay_count += ctx->thread_safe_stats.ul_uplane_ota_delay_count;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -739,7 +835,7 @@ static void unpack_iq(c16_t *txdataF, void *iqdata, int start_prb, int num_prb)
|
||||
}
|
||||
}
|
||||
|
||||
void read_dl_iq(void *context, uint32_t **txdataF, int nb_tx, int *frame, int *slot, int *symbol)
|
||||
void read_dl_iq(void *context, uint32_t **txdataF, int nb_tx, uint64_t *hyper_frame, int *frame, int *slot, int *symbol)
|
||||
{
|
||||
oru_packet_processor_context_t *ctx = (oru_packet_processor_context_t *)context;
|
||||
if (ctx == NULL)
|
||||
@@ -754,6 +850,7 @@ void read_dl_iq(void *context, uint32_t **txdataF, int nb_tx, int *frame, int *s
|
||||
uint64_t absolute_gps_symbol = job->absolute_symbol;
|
||||
int numerology = ctx->numerology;
|
||||
int num_symbols_per_frame = NR_NUMBER_OF_SUBFRAMES_PER_FRAME * (1 << numerology) * NR_SYMBOLS_PER_SLOT;
|
||||
*hyper_frame = (absolute_gps_symbol / num_symbols_per_frame) / 1024;
|
||||
*frame = (absolute_gps_symbol / num_symbols_per_frame) % 1024;
|
||||
*slot = (absolute_gps_symbol % num_symbols_per_frame) / NR_SYMBOLS_PER_SLOT;
|
||||
*symbol = absolute_gps_symbol % NR_SYMBOLS_PER_SLOT;
|
||||
@@ -834,47 +931,66 @@ void fill_data_section_header(struct data_section_hdr *data_section_hdr, int num
|
||||
data_section_hdr->fields.all_bits = rte_cpu_to_be_32(data_section_hdr->fields.all_bits);
|
||||
}
|
||||
|
||||
void write_ul_iq(void *context, uint32_t **txdataF, int nb_rx, int frame, int slot_in_frame, int symbol)
|
||||
int poll_ul_job(void *context, ul_job_t *job)
|
||||
{
|
||||
oru_packet_processor_context_t *ctx = (oru_packet_processor_context_t *)context;
|
||||
if (ctx == NULL)
|
||||
return;
|
||||
int numerology = ctx->numerology;
|
||||
int num_symbols_per_frame = NR_NUMBER_OF_SUBFRAMES_PER_FRAME * (1 << numerology) * NR_SYMBOLS_PER_SLOT;
|
||||
uint32_t current_symbol_in_frame = ctx->current_absolute_symbol % num_symbols_per_frame;
|
||||
int symbol_in_frame = NR_SYMBOLS_PER_SLOT * slot_in_frame + symbol;
|
||||
int32_t diff = symbol_in_frame - current_symbol_in_frame;
|
||||
if (diff < -num_symbols_per_frame / 2) {
|
||||
diff += num_symbols_per_frame;
|
||||
} else if (diff > num_symbols_per_frame / 2) {
|
||||
diff -= num_symbols_per_frame;
|
||||
if (ctx == NULL || job == NULL) {
|
||||
return -1;
|
||||
}
|
||||
uint64_t target_absolute_symbol = ctx->current_absolute_symbol + diff;
|
||||
bool is_ul_symbol = test_bit(ctx->ul_symbol_bitmask, target_absolute_symbol % ctx->symbol_bitmask_length);
|
||||
if (!is_ul_symbol) {
|
||||
ctx->thread_safe_stats.ul_tdd_mismatch++;
|
||||
return;
|
||||
}
|
||||
ul_symbol_job_t *job = &ctx->ul_symbol_jobs[target_absolute_symbol % NUM_CONCURRENT_UL_SYMBOL_WINDOWS];
|
||||
if (job->state == SYM_UL_IDLE) {
|
||||
ctx->thread_safe_stats.ul_cplane_missing++;
|
||||
ul_job_t *dequeued_job = NULL;
|
||||
if (rte_ring_dequeue(ctx->ul_ready_jobs, (void **)&dequeued_job) == 0) {
|
||||
*job = *dequeued_job;
|
||||
rte_ring_enqueue(ctx->ul_free_jobs, (void *)dequeued_job);
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void write_ul_iq(void *context, uint32_t *rxdataF, int symbol, const ul_job_t *job)
|
||||
{
|
||||
oru_packet_processor_context_t *ctx = (oru_packet_processor_context_t *)context;
|
||||
if (ctx == NULL || job == NULL)
|
||||
return;
|
||||
AssertFatal(symbol >= job->symbol && symbol < job->symbol + job->num_symbols && symbol < NR_SYMBOLS_PER_SLOT,
|
||||
"Symbol %d outside of job range [%d, %d)\n",
|
||||
symbol,
|
||||
job->symbol,
|
||||
job->symbol + job->num_symbols);
|
||||
|
||||
// Delay from OTA symbol to packet send: current timer symbol minus the absolute symbol of this UL symbol.
|
||||
const int slots_per_frame = NR_NUMBER_OF_SUBFRAMES_PER_FRAME * (1 << ctx->numerology);
|
||||
const uint64_t ota_absolute_symbol = (uint64_t)job->hyper_frame * 1024ULL * slots_per_frame * NR_SYMBOLS_PER_SLOT
|
||||
+ (uint64_t)job->frame * slots_per_frame * NR_SYMBOLS_PER_SLOT
|
||||
+ (uint64_t)job->slot_in_frame * NR_SYMBOLS_PER_SLOT
|
||||
+ (uint64_t)symbol;
|
||||
int64_t delay = (int64_t)ctx->current_absolute_symbol - (int64_t)ota_absolute_symbol;
|
||||
atomic_fetch_add_explicit(&ctx->thread_safe_stats.ul_uplane_ota_delay_sum, delay, memory_order_relaxed);
|
||||
atomic_fetch_add_explicit(&ctx->thread_safe_stats.ul_uplane_ota_delay_count, 1, memory_order_relaxed);
|
||||
|
||||
int aarx = job->antenna_id;
|
||||
if (aarx < 0 || aarx >= MAX_ANTENNAS) {
|
||||
LOG_W(HW, "ORU: Invalid antenna index %d\n", aarx);
|
||||
return;
|
||||
}
|
||||
|
||||
const bool use_comp = (job->response_payload.comp_method != 0);
|
||||
|
||||
int section_id = job->response_payload.section_id;
|
||||
int total_ul_rbs = job->num_prb;
|
||||
int start_prb_base = job->start_prb;
|
||||
int frame = job->frame;
|
||||
int slot_in_frame = job->slot_in_frame;
|
||||
int mu = ctx->numerology;
|
||||
|
||||
struct rte_mbuf *mbufs[MAX_MBUFS_PER_SYMBOL];
|
||||
uint32_t num_mbufs = 0;
|
||||
for (int aarx = 0; aarx < nb_rx; aarx++) {
|
||||
if (!job->per_antenna[aarx].cplane_received) {
|
||||
ctx->thread_safe_stats.ul_cplane_missing++;
|
||||
continue;
|
||||
}
|
||||
int section_id = job->per_antenna[aarx].section_id;
|
||||
int total_ul_rbs = job->per_antenna[aarx].num_prb;
|
||||
int start_prb_base = job->per_antenna[aarx].start_prb;
|
||||
|
||||
int rbs_sent = 0;
|
||||
size_t overhead = sizeof(struct rte_ether_hdr) + sizeof(struct xran_ecpri_hdr) + sizeof(struct radio_app_common_hdr)
|
||||
+ sizeof(struct data_section_hdr) + sizeof(struct data_section_compression_hdr);
|
||||
+ sizeof(struct data_section_hdr);
|
||||
if (use_comp) {
|
||||
overhead += sizeof(struct data_section_compression_hdr);
|
||||
}
|
||||
int max_prb_per_packet = (int)((ctx->mtu - overhead) / (NR_NB_SC_PER_RB * sizeof(int32_t)));
|
||||
|
||||
while (rbs_sent < total_ul_rbs) {
|
||||
@@ -889,8 +1005,10 @@ void write_ul_iq(void *context, uint32_t **txdataF, int nb_rx, int frame, int sl
|
||||
break;
|
||||
}
|
||||
|
||||
size_t header_length = sizeof(struct xran_ecpri_hdr) + sizeof(struct radio_app_common_hdr) + sizeof(struct data_section_hdr)
|
||||
+ sizeof(struct data_section_compression_hdr);
|
||||
size_t header_length = sizeof(struct xran_ecpri_hdr) + sizeof(struct radio_app_common_hdr) + sizeof(struct data_section_hdr);
|
||||
if (use_comp) {
|
||||
header_length += sizeof(struct data_section_compression_hdr);
|
||||
}
|
||||
const uint num_sc = num_ul_rbs * NR_NB_SC_PER_RB;
|
||||
size_t data_len = sizeof(int32_t) * num_sc;
|
||||
|
||||
@@ -898,8 +1016,6 @@ void write_ul_iq(void *context, uint32_t **txdataF, int nb_rx, int frame, int sl
|
||||
if (buf == NULL) {
|
||||
LOG_W(HW, "ORU: Failed to append data to mbuf (insufficient space)\n");
|
||||
rte_pktmbuf_free(pkt);
|
||||
// Do not increment rbs_sent, just break and let the next symbol or antenna be processed,
|
||||
// or we could reduce max_prb_per_packet. Breaking is safer to prevent infinite loop.
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -911,16 +1027,8 @@ void write_ul_iq(void *context, uint32_t **txdataF, int nb_rx, int frame, int sl
|
||||
mbufs[num_mbufs++] = pkt;
|
||||
|
||||
struct xran_ecpri_hdr *ecpri_header = (struct xran_ecpri_hdr *)buf;
|
||||
// eCPRI payload size excludes the 4-byte common header
|
||||
uint16_t ecpri_payload_size = (uint16_t)(header_length - 4 + data_len);
|
||||
fill_ecpri_header(ecpri_header,
|
||||
&ctx->eaxcid_config,
|
||||
ECPRI_IQ_DATA,
|
||||
ecpri_payload_size,
|
||||
0,
|
||||
aarx,
|
||||
ctx->pusch_seq_id[aarx]++,
|
||||
0);
|
||||
fill_ecpri_header(ecpri_header, &ctx->eaxcid_config, ECPRI_IQ_DATA, ecpri_payload_size, 0, aarx, ctx->pusch_seq_id[aarx]++, 0);
|
||||
|
||||
struct radio_app_common_hdr *radio_app_header = (struct radio_app_common_hdr *)(ecpri_header + 1);
|
||||
fill_radio_app_header(radio_app_header, 0, XRAN_DIR_UL, frame, slot_in_frame, symbol, mu);
|
||||
@@ -928,26 +1036,29 @@ void write_ul_iq(void *context, uint32_t **txdataF, int nb_rx, int frame, int sl
|
||||
struct data_section_hdr *data_section_header = (struct data_section_hdr *)(radio_app_header + 1);
|
||||
fill_data_section_header(data_section_header, num_ul_rbs, start_prb_base + rbs_sent, section_id);
|
||||
|
||||
void *iq_data_start;
|
||||
if (use_comp) {
|
||||
struct data_section_compression_hdr *compression_header = (struct data_section_compression_hdr *)(data_section_header + 1);
|
||||
compression_header->ud_comp_hdr.ud_comp_meth = 0;
|
||||
compression_header->ud_comp_hdr.ud_iq_width = XRAN_CONVERT_IQWIDTH(16);
|
||||
compression_header->ud_comp_hdr.ud_comp_meth = job->response_payload.comp_method;
|
||||
compression_header->ud_comp_hdr.ud_iq_width = XRAN_CONVERT_IQWIDTH(job->response_payload.iq_width);
|
||||
compression_header->rsrvd = 0;
|
||||
iq_data_start = (void *)(compression_header + 1);
|
||||
} else {
|
||||
iq_data_start = (void *)(data_section_header + 1);
|
||||
}
|
||||
|
||||
void *iq_data_start = (void *)(compression_header + 1);
|
||||
uint16_t *src = (uint16_t *)&txdataF[aarx][(start_prb_base + rbs_sent) * NR_NB_SC_PER_RB];
|
||||
uint16_t *src = (uint16_t *)&rxdataF[(start_prb_base + rbs_sent) * NR_NB_SC_PER_RB];
|
||||
uint16_t *dst = (uint16_t *)iq_data_start;
|
||||
for (int i = 0; i < num_sc * 2; i++) {
|
||||
*dst++ = rte_cpu_to_be_16(*src++);
|
||||
}
|
||||
rbs_sent += num_ul_rbs;
|
||||
}
|
||||
}
|
||||
|
||||
if (num_mbufs > 0) {
|
||||
ctx->send_func(ctx->io_controller, mbufs, num_mbufs);
|
||||
ctx->thread_safe_stats.total_uplane_sent += num_mbufs;
|
||||
for (int i = 0; i < MAX_ANTENNAS; i++) {
|
||||
job->per_antenna[i].cplane_received = false;
|
||||
}
|
||||
job->state = SYM_UL_IDLE;
|
||||
}
|
||||
|
||||
void write_prach_iq(void *context, uint32_t **txdataF, int nb_rx, int frame, int slot_in_frame, int symbol)
|
||||
@@ -956,29 +1067,67 @@ void write_prach_iq(void *context, uint32_t **txdataF, int nb_rx, int frame, int
|
||||
if (ctx == NULL)
|
||||
return;
|
||||
|
||||
int mu = ctx->numerology;
|
||||
uint64_t absolute_symbol = (frame * NR_NUMBER_OF_SUBFRAMES_PER_FRAME * (1 << mu) + slot_in_frame) * NR_SYMBOLS_PER_SLOT + symbol;
|
||||
uint32_t job_index = absolute_symbol % NUM_CONCURRENT_UL_SYMBOL_WINDOWS;
|
||||
prach_symbol_job_t *job = &ctx->prach_jobs[job_index];
|
||||
|
||||
if (job->absolute_symbol != absolute_symbol)
|
||||
return;
|
||||
|
||||
int numerology = ctx->numerology;
|
||||
int num_symbols_per_frame = NR_NUMBER_OF_SUBFRAMES_PER_FRAME * (1 << numerology) * NR_SYMBOLS_PER_SLOT;
|
||||
uint32_t current_symbol_in_frame = ctx->current_absolute_symbol % num_symbols_per_frame;
|
||||
int symbol_in_frame = NR_SYMBOLS_PER_SLOT * slot_in_frame + symbol;
|
||||
int32_t diff = symbol_in_frame - current_symbol_in_frame;
|
||||
if (diff < -num_symbols_per_frame / 2) {
|
||||
diff += num_symbols_per_frame;
|
||||
} else if (diff > num_symbols_per_frame / 2) {
|
||||
diff -= num_symbols_per_frame;
|
||||
}
|
||||
uint64_t target_absolute_symbol = ctx->current_absolute_symbol + diff;
|
||||
struct rte_mbuf *mbufs[MAX_MBUFS_PER_SYMBOL];
|
||||
uint32_t num_mbufs = 0;
|
||||
|
||||
for (int aarx = 0; aarx < nb_rx; aarx++) {
|
||||
if (!job->per_antenna[aarx].cplane_received)
|
||||
if (slot_in_frame < 0 || slot_in_frame >= MAX_SLOTS_PER_FRAME) {
|
||||
RATELIMIT(PRACH_ERR_LOG_RATELIMIT, { LOG_W(HW, "PRACH UP: Invalid slot_in_frame %d\n", slot_in_frame); });
|
||||
continue;
|
||||
}
|
||||
prach_job_t *job = &ctx->prach_jobs[slot_in_frame][aarx];
|
||||
if (!job->active || target_absolute_symbol < job->start_absolute_symbol
|
||||
|| target_absolute_symbol >= job->start_absolute_symbol + job->num_symbols) {
|
||||
ctx->thread_safe_stats.prach_cplane_missing++;
|
||||
if (!job->active) {
|
||||
ctx->thread_safe_stats.prach_cplane_missing_inactive++;
|
||||
RATELIMIT(PRACH_ERR_LOG_RATELIMIT,
|
||||
{ LOG_W(HW, "PRACH UP: Missing C-Plane - Inactive job for slot %d, aarx %d\n", slot_in_frame, aarx); });
|
||||
} else if (target_absolute_symbol < job->start_absolute_symbol) {
|
||||
ctx->thread_safe_stats.prach_cplane_missing_early++;
|
||||
RATELIMIT(PRACH_ERR_LOG_RATELIMIT, {
|
||||
LOG_W(HW,
|
||||
"PRACH UP: Missing C-Plane - Early symbol %lu (job start %lu) for slot %d, aarx %d\n",
|
||||
target_absolute_symbol,
|
||||
job->start_absolute_symbol,
|
||||
slot_in_frame,
|
||||
aarx);
|
||||
});
|
||||
} else {
|
||||
ctx->thread_safe_stats.prach_cplane_missing_stale++;
|
||||
RATELIMIT(PRACH_ERR_LOG_RATELIMIT, {
|
||||
LOG_W(HW,
|
||||
"PRACH UP: Missing C-Plane - Stale symbol %lu (job end %lu) for slot %d, aarx %d\n",
|
||||
target_absolute_symbol,
|
||||
job->start_absolute_symbol + job->num_symbols,
|
||||
slot_in_frame,
|
||||
aarx);
|
||||
});
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
int section_id = job->per_antenna[aarx].section_id;
|
||||
int num_ul_rbs = job->per_antenna[aarx].num_prb;
|
||||
int start_prb = job->per_antenna[aarx].start_prb;
|
||||
int filter_id = job->per_antenna[aarx].filter_id;
|
||||
int section_id = job->section_id;
|
||||
int num_ul_rbs = job->num_prb;
|
||||
int start_prb = job->start_prb;
|
||||
int filter_id = job->filter_id;
|
||||
|
||||
struct rte_mbuf *pkt = ctx->alloc_func(ctx->io_controller);
|
||||
if (pkt == NULL) {
|
||||
ctx->thread_safe_stats.prach_out_of_mbufs++;
|
||||
ctx->thread_safe_stats.out_of_mbufs++;
|
||||
RATELIMIT(PRACH_ERR_LOG_RATELIMIT, { LOG_W(HW, "PRACH UP: Failed to allocate mbuf\n"); });
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -988,7 +1137,10 @@ void write_prach_iq(void *context, uint32_t **txdataF, int nb_rx, int frame, int
|
||||
|
||||
char *buf = rte_pktmbuf_append(pkt, (uint16_t)(header_length + data_len));
|
||||
if (buf == NULL) {
|
||||
ctx->thread_safe_stats.prach_out_of_mbufs++;
|
||||
ctx->thread_safe_stats.out_of_mbufs++;
|
||||
rte_pktmbuf_free(pkt);
|
||||
RATELIMIT(PRACH_ERR_LOG_RATELIMIT, { LOG_W(HW, "PRACH UP: Failed to append data to mbuf\n"); });
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1007,7 +1159,7 @@ void write_prach_iq(void *context, uint32_t **txdataF, int nb_rx, int frame, int
|
||||
0);
|
||||
|
||||
struct radio_app_common_hdr *radio_app_header = (struct radio_app_common_hdr *)(ecpri_header + 1);
|
||||
fill_radio_app_header(radio_app_header, filter_id, XRAN_DIR_UL, frame, slot_in_frame, symbol, mu);
|
||||
fill_radio_app_header(radio_app_header, filter_id, XRAN_DIR_UL, frame, slot_in_frame, symbol, numerology);
|
||||
|
||||
struct data_section_hdr *data_section_header = (struct data_section_hdr *)(radio_app_header + 1);
|
||||
fill_data_section_header(data_section_header, num_ul_rbs, start_prb, section_id);
|
||||
@@ -1018,8 +1170,6 @@ void write_prach_iq(void *context, uint32_t **txdataF, int nb_rx, int frame, int
|
||||
for (int i = 0; i < prach_length * 2; i++) {
|
||||
*dst++ = rte_cpu_to_be_16(*src++);
|
||||
}
|
||||
|
||||
job->per_antenna[aarx].cplane_received = false;
|
||||
}
|
||||
|
||||
if (num_mbufs > 0) {
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
* SPDX-License-Identifier: LicenseRef-CSSL-1.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
@@ -17,8 +19,30 @@ typedef struct {
|
||||
uint64_t count;
|
||||
} txrx_histogram_t;
|
||||
|
||||
typedef struct {
|
||||
int section_id;
|
||||
int comp_method;
|
||||
int iq_width;
|
||||
} opaque_response_data_t;
|
||||
|
||||
typedef struct {
|
||||
opaque_response_data_t response_payload;
|
||||
uint64_t hyper_frame;
|
||||
int frame;
|
||||
int slot_in_frame;
|
||||
int symbol;
|
||||
int num_symbols;
|
||||
int antenna_id;
|
||||
int start_prb;
|
||||
int num_prb;
|
||||
} ul_job_t;
|
||||
|
||||
typedef struct {
|
||||
uint64_t total_cplane;
|
||||
uint64_t cplane_received_dl;
|
||||
uint64_t cplane_received_ul;
|
||||
uint64_t cplane_received_prach;
|
||||
uint64_t cplane_received_other;
|
||||
uint64_t total_uplane_received;
|
||||
uint64_t total_uplane_sent;
|
||||
uint64_t cplane_err_hdr; // apphdr or section extraction
|
||||
@@ -26,6 +50,9 @@ typedef struct {
|
||||
uint64_t cplane_err_early;
|
||||
uint64_t cplane_err_late;
|
||||
uint64_t cplane_err_dup; // duplicate cplane
|
||||
uint64_t cplane_err_dup_dl;
|
||||
uint64_t cplane_err_dup_ul;
|
||||
uint64_t cplane_err_dup_prach;
|
||||
uint64_t uplane_err_late;
|
||||
uint64_t uplane_err_early;
|
||||
uint64_t uplane_err_dup;
|
||||
@@ -35,10 +62,19 @@ typedef struct {
|
||||
uint64_t ul_tdd_mismatch;
|
||||
uint64_t out_of_mbufs;
|
||||
uint64_t ul_cplane_missing;
|
||||
uint64_t prach_cplane_missing;
|
||||
uint64_t prach_cplane_missing_ant;
|
||||
uint64_t prach_cplane_missing_inactive;
|
||||
uint64_t prach_cplane_missing_stale;
|
||||
uint64_t prach_cplane_missing_early;
|
||||
uint64_t prach_out_of_mbufs;
|
||||
uint64_t prach_jobs_pool_exhausted;
|
||||
txrx_histogram_t dl_uplane_hist;
|
||||
txrx_histogram_t dl_cplane_hist;
|
||||
txrx_histogram_t ul_cplane_hist;
|
||||
txrx_histogram_t prach_cplane_hist;
|
||||
int64_t ul_uplane_ota_delay_sum;
|
||||
uint64_t ul_uplane_ota_delay_count;
|
||||
} oru_packet_processor_stats_t;
|
||||
|
||||
typedef void *(*alloc_func_t)(void *io_controller);
|
||||
@@ -60,7 +96,7 @@ void *init_packet_processor(int numerology,
|
||||
void *io_controller,
|
||||
size_t mtu,
|
||||
int prach_eaxc_offset);
|
||||
void write_ul_iq(void *context, uint32_t **txdataF, int nb_rx, int frame, int slot_in_frame, int symbol);
|
||||
void write_ul_iq(void *context, uint32_t *rxdataF, int symbol, const ul_job_t *job);
|
||||
void write_prach_iq(void *context, uint32_t **txdataF, int nb_rx, int frame, int slot_in_frame, int symbol);
|
||||
void cleanup_packet_processor(void *context);
|
||||
void handle_absolute_symbol_tick(void *context, uint64_t absolute_symbol);
|
||||
@@ -68,8 +104,9 @@ void handle_uplane_packet(void *context, void *pkt);
|
||||
void handle_cplane_packet(void *context, void *pkt);
|
||||
void print_packet_processor_stats(void *context);
|
||||
void get_packet_processor_stats(void *context, oru_packet_processor_stats_t *out_stats);
|
||||
void read_dl_iq(void *context, uint32_t **txdataF, int nb_tx, int *frame, int *slot, int *symbol);
|
||||
void read_dl_iq(void *context, uint32_t **txdataF, int nb_tx, uint64_t *hyper_frame, int *frame, int *slot, int *symbol);
|
||||
int get_ready_job_count(void *context);
|
||||
int poll_ul_job(void *context, ul_job_t *job);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <unistd.h>
|
||||
#include "common/config/config_userapi.h"
|
||||
#include <rte_eal.h>
|
||||
#include <assert.h>
|
||||
|
||||
// OAI Linkage Satisfiers
|
||||
void exit_function(const char *file, const char *function, const int line, const char *s, const int assertflag)
|
||||
@@ -84,20 +85,32 @@ int main(int argc, char **argv)
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Testing UTC Anchor Point and Hyper-frame
|
||||
printf("Testing UTC Anchor Point and Hyper-frame...\n");
|
||||
uint64_t hf;
|
||||
uint32_t f, s;
|
||||
struct timespec ts;
|
||||
if (oru_fh_get_utc_anchor_point(handle, &hf, &f, &s, &ts) < 0) {
|
||||
fprintf(stderr, "FAIL: oru_fh_get_utc_anchor_point failed\n");
|
||||
oru_fh_cleanup(handle);
|
||||
return 1;
|
||||
}
|
||||
printf("UTC Anchor Point: hf=%lu, f=%u, s=%u, ts=%ld.%09ld\n", hf, f, s, ts.tv_sec, ts.tv_nsec);
|
||||
assert(f < 1024);
|
||||
assert(s < (10 << cfg.numerology));
|
||||
|
||||
printf("Running live loop for 2 seconds...\n");
|
||||
uint32_t *txData[1];
|
||||
txData[0] = malloc(273 * 12 * sizeof(uint32_t));
|
||||
for (int i = 0; i < 2000; i++) {
|
||||
if (i % 400 == 0) {
|
||||
oru_fh_rx_send_pusch(handle, txData, 1, 0, i / 400, 0);
|
||||
}
|
||||
|
||||
uint64_t start_cycles = rte_get_timer_cycles();
|
||||
uint64_t target_cycles = start_cycles + (rte_get_timer_hz() / 1000);
|
||||
while (rte_get_timer_cycles() < target_cycles) {
|
||||
int f, s, sym;
|
||||
uint64_t hf;
|
||||
while (oru_fh_get_ready_jobs(handle) > 0) {
|
||||
oru_fh_tx_read_symbol(handle, txData, 1, &f, &s, &sym);
|
||||
oru_fh_tx_read_symbol(handle, txData, 1, &hf, &f, &s, &sym);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -235,8 +235,9 @@ void test_cplane_uplane_match()
|
||||
txdataF[0] = output_iq;
|
||||
|
||||
int frame, slot, symbol;
|
||||
uint64_t hyper_frame;
|
||||
do {
|
||||
read_dl_iq(ctx, txdataF, 1, &frame, &slot, &symbol);
|
||||
read_dl_iq(ctx, txdataF, 1, &hyper_frame, &frame, &slot, &symbol);
|
||||
} while (!(frame == (target_sym / num_symbols_per_frame) % 1024 && symbol == target_sym % 14));
|
||||
|
||||
assert(symbol == target_sym % 14);
|
||||
@@ -343,8 +344,9 @@ void test_frame_wrap_around()
|
||||
txdataF[0] = output_iq;
|
||||
|
||||
int frame, slot, symbol;
|
||||
uint64_t hyper_frame;
|
||||
do {
|
||||
read_dl_iq(ctx, txdataF, 1, &frame, &slot, &symbol);
|
||||
read_dl_iq(ctx, txdataF, 1, &hyper_frame, &frame, &slot, &symbol);
|
||||
} while (!(frame == (target_sym / num_symbols_per_frame) % 1024 && symbol == target_sym % 14));
|
||||
|
||||
assert(frame == (target_sym / num_symbols_per_frame) % 1024);
|
||||
@@ -456,9 +458,10 @@ void test_cplane_14_symbols()
|
||||
|
||||
for (int i = 0; i < 14; i++) {
|
||||
int frame, slot, symbol;
|
||||
uint64_t hyper_frame;
|
||||
uint64_t sym_i = target_sym + i;
|
||||
do {
|
||||
read_dl_iq(ctx, txdataF, 1, &frame, &slot, &symbol);
|
||||
read_dl_iq(ctx, txdataF, 1, &hyper_frame, &frame, &slot, &symbol);
|
||||
} while (!(frame == (sym_i / num_symbols_per_frame) % 1024 && symbol == sym_i % 14));
|
||||
|
||||
assert(frame == (sym_i / num_symbols_per_frame) % 1024);
|
||||
@@ -566,8 +569,9 @@ void test_other_bw_4ant_prb_offset()
|
||||
txdataF[3] = out_iq3;
|
||||
|
||||
int frame, slot, symbol;
|
||||
uint64_t hyper_frame;
|
||||
do {
|
||||
read_dl_iq(ctx, txdataF, 4, &frame, &slot, &symbol);
|
||||
read_dl_iq(ctx, txdataF, 4, &hyper_frame, &frame, &slot, &symbol);
|
||||
} while (!(frame == frameId && symbol == startSymbolId));
|
||||
|
||||
// Verify memory contents for each antenna
|
||||
@@ -611,7 +615,7 @@ void test_send_mbuf_no_frag(void *io_controller, struct rte_mbuf **mbufs, uint32
|
||||
sent_sec->fields.all_bits = rte_be_to_cpu_32(sent_sec->fields.all_bits);
|
||||
|
||||
size_t expected_header_len = sizeof(struct xran_ecpri_hdr) + sizeof(struct radio_app_common_hdr)
|
||||
+ sizeof(struct data_section_hdr) + sizeof(struct data_section_compression_hdr);
|
||||
+ sizeof(struct data_section_hdr);
|
||||
|
||||
// In this test, we expect exactly 20 PRBs in a single packet
|
||||
assert(sent_sec->fields.num_prbu == (uint8_t)XRAN_CONVERT_NUMPRBC(20));
|
||||
@@ -659,7 +663,7 @@ void test_send_mbuf_frag(void *io_controller, struct rte_mbuf **mbufs, uint32_t
|
||||
sent_sec->fields.all_bits = rte_be_to_cpu_32(sent_sec->fields.all_bits);
|
||||
|
||||
size_t expected_header_len = sizeof(struct xran_ecpri_hdr) + sizeof(struct radio_app_common_hdr)
|
||||
+ sizeof(struct data_section_hdr) + sizeof(struct data_section_compression_hdr);
|
||||
+ sizeof(struct data_section_hdr);
|
||||
|
||||
if (g_packets_sent == 1) {
|
||||
// First fragment: 30 PRBs
|
||||
@@ -688,7 +692,7 @@ void test_send_mbuf_large_mtu(void *io_controller, struct rte_mbuf **mbufs, uint
|
||||
sent_sec->fields.all_bits = rte_be_to_cpu_32(sent_sec->fields.all_bits);
|
||||
|
||||
size_t expected_header_len = sizeof(struct xran_ecpri_hdr) + sizeof(struct radio_app_common_hdr)
|
||||
+ sizeof(struct data_section_hdr) + sizeof(struct data_section_compression_hdr);
|
||||
+ sizeof(struct data_section_hdr);
|
||||
|
||||
// With MTU 9600, 100 PRBs should fit in a single packet
|
||||
assert(g_packets_sent == 1);
|
||||
@@ -721,7 +725,7 @@ void test_send_mbuf_prb_offset(void *io_controller, struct rte_mbuf **mbufs, uin
|
||||
|
||||
// Check data: should match the offset in txdataF
|
||||
uint32_t *iq =
|
||||
(uint32_t *)((uint8_t *)sent_sec + sizeof(struct data_section_hdr) + sizeof(struct data_section_compression_hdr));
|
||||
(uint32_t *)((uint8_t *)sent_sec + sizeof(struct data_section_hdr));
|
||||
assert(rte_be_to_cpu_32(iq[0]) == 0x10101010);
|
||||
|
||||
rte_pktmbuf_free(mbuf);
|
||||
@@ -779,15 +783,20 @@ void test_uplink_basic()
|
||||
|
||||
handle_cplane_packet(ctx, c_mbuf);
|
||||
|
||||
// 2. Call write_ul_iq
|
||||
uint32_t *txdataF[1];
|
||||
// 2. Poll the UL job created by handle_cplane_packet and call write_ul_iq
|
||||
ul_job_t job;
|
||||
int poll_ret = poll_ul_job(ctx, &job);
|
||||
assert(poll_ret == 0);
|
||||
assert(job.num_prb == 20);
|
||||
assert(job.start_prb == 0);
|
||||
assert(job.antenna_id == 0);
|
||||
|
||||
uint32_t iq_input[100 * 12];
|
||||
for (int i = 0; i < 100 * 12; i++) {
|
||||
iq_input[i] = 0x11223344;
|
||||
}
|
||||
txdataF[0] = iq_input;
|
||||
|
||||
write_ul_iq(ctx, txdataF, 1, frameId, slot_in_frame, startSymbolId);
|
||||
write_ul_iq(ctx, iq_input, startSymbolId, &job);
|
||||
|
||||
oru_packet_processor_stats_t stats;
|
||||
get_packet_processor_stats(ctx, &stats);
|
||||
@@ -850,15 +859,20 @@ void test_uplink_fragmentation()
|
||||
|
||||
handle_cplane_packet(ctx, c_mbuf);
|
||||
|
||||
// 2. Call write_ul_iq
|
||||
uint32_t *txdataF[1];
|
||||
// 2. Poll the UL job created by handle_cplane_packet and call write_ul_iq
|
||||
ul_job_t job;
|
||||
int poll_ret = poll_ul_job(ctx, &job);
|
||||
assert(poll_ret == 0);
|
||||
assert(job.num_prb == 40);
|
||||
assert(job.start_prb == 0);
|
||||
assert(job.antenna_id == 0);
|
||||
|
||||
uint32_t iq_input[100 * 12];
|
||||
for (int i = 0; i < 100 * 12; i++) {
|
||||
iq_input[i] = 0x11223344;
|
||||
}
|
||||
txdataF[0] = iq_input;
|
||||
|
||||
write_ul_iq(ctx, txdataF, 1, frameId, slot_in_frame, startSymbolId);
|
||||
write_ul_iq(ctx, iq_input, startSymbolId, &job);
|
||||
|
||||
oru_packet_processor_stats_t stats;
|
||||
get_packet_processor_stats(ctx, &stats);
|
||||
@@ -936,15 +950,20 @@ void test_uplink_large_mtu()
|
||||
|
||||
handle_cplane_packet(ctx, c_mbuf);
|
||||
|
||||
// 2. Call write_ul_iq
|
||||
uint32_t *txdataF[1];
|
||||
// 2. Poll the UL job created by handle_cplane_packet and call write_ul_iq
|
||||
ul_job_t job;
|
||||
int poll_ret = poll_ul_job(ctx, &job);
|
||||
assert(poll_ret == 0);
|
||||
assert(job.num_prb == 100);
|
||||
assert(job.start_prb == 0);
|
||||
assert(job.antenna_id == 0);
|
||||
|
||||
uint32_t iq_input[num_prb * 12];
|
||||
for (int i = 0; i < num_prb * 12; i++) {
|
||||
iq_input[i] = 0x55667788;
|
||||
}
|
||||
txdataF[0] = iq_input;
|
||||
|
||||
write_ul_iq(ctx, txdataF, 1, frameId, slot_in_frame, startSymbolId);
|
||||
write_ul_iq(ctx, iq_input, startSymbolId, &job);
|
||||
|
||||
oru_packet_processor_stats_t stats;
|
||||
get_packet_processor_stats(ctx, &stats);
|
||||
@@ -1021,14 +1040,19 @@ void test_uplink_prb_offset()
|
||||
|
||||
handle_cplane_packet(ctx, c_mbuf);
|
||||
|
||||
// 2. Call write_ul_iq
|
||||
uint32_t *txdataF[1];
|
||||
// 2. Poll the UL job created by handle_cplane_packet and call write_ul_iq
|
||||
ul_job_t job;
|
||||
int poll_ret = poll_ul_job(ctx, &job);
|
||||
assert(poll_ret == 0);
|
||||
assert(job.num_prb == 20);
|
||||
assert(job.start_prb == 10);
|
||||
assert(job.antenna_id == 0);
|
||||
|
||||
uint32_t iq_input[100 * 12];
|
||||
memset(iq_input, 0, sizeof(iq_input));
|
||||
iq_input[10 * 12] = 0x10101010; // Mark PRB 10
|
||||
txdataF[0] = iq_input;
|
||||
|
||||
write_ul_iq(ctx, txdataF, 1, frameId, slot_in_frame, startSymbolId);
|
||||
write_ul_iq(ctx, iq_input, startSymbolId, &job);
|
||||
|
||||
oru_packet_processor_stats_t stats;
|
||||
get_packet_processor_stats(ctx, &stats);
|
||||
@@ -1129,6 +1153,102 @@ void test_prach_generation()
|
||||
printf("PRACH generation passed!\n");
|
||||
}
|
||||
|
||||
void test_hyper_frame_calculation()
|
||||
{
|
||||
printf("Testing hyper-frame calculation...\n");
|
||||
int mu = 1; // 30kHz
|
||||
int slots_per_subframe = 1 << mu;
|
||||
void *ctx = init_packet_processor(mu, 273, 200, 400, 100, 300, 2, 2, 0, 0, 5, test_alloc_mbuf, test_send_mbuf, NULL, 1500, 0);
|
||||
assert(ctx != NULL);
|
||||
|
||||
int num_symbols_per_frame = 10 * slots_per_subframe * 14; // 280
|
||||
|
||||
// One hyper-frame has 1024 frames. So 1024 * 280 = 286720 symbols.
|
||||
// Target absolute symbol index: 3 * 286720 + 5 * 280 + 1 * 14 + 7 = 860160 + 1400 + 14 + 7 = 861581.
|
||||
uint64_t target_sym = 861581;
|
||||
uint64_t current_sym = target_sym - 7;
|
||||
handle_absolute_symbol_tick(ctx, current_sym);
|
||||
|
||||
// 1. Send C-plane packet for target_sym
|
||||
struct rte_mbuf *c_mbuf = rte_pktmbuf_alloc(mp);
|
||||
struct xran_ecpri_hdr *ecpri = (struct xran_ecpri_hdr *)rte_pktmbuf_append(c_mbuf, sizeof(struct xran_ecpri_hdr));
|
||||
ecpri->ecpri_xtc_id = xran_compose_cid(&g_eaxcid_config, 0, 0, 0, 0);
|
||||
|
||||
struct xran_cp_radioapp_section1_header *apphdr =
|
||||
(struct xran_cp_radioapp_section1_header *)rte_pktmbuf_append(c_mbuf, sizeof(struct xran_cp_radioapp_section1_header));
|
||||
memset(apphdr, 0, sizeof(*apphdr));
|
||||
apphdr->cmnhdr.field.dataDirection = XRAN_DIR_DL;
|
||||
apphdr->cmnhdr.field.payloadVer = XRAN_PAYLOAD_VER;
|
||||
|
||||
apphdr->cmnhdr.field.frameId = (target_sym / num_symbols_per_frame) % 256;
|
||||
int slot_in_frame = (target_sym % num_symbols_per_frame) / 14;
|
||||
apphdr->cmnhdr.field.subframeId = slot_in_frame / slots_per_subframe;
|
||||
apphdr->cmnhdr.field.slotId = slot_in_frame % slots_per_subframe;
|
||||
apphdr->cmnhdr.field.startSymbolId = target_sym % 14;
|
||||
apphdr->cmnhdr.sectionType = XRAN_CP_SECTIONTYPE_1;
|
||||
apphdr->cmnhdr.field.all_bits = rte_cpu_to_be_32(apphdr->cmnhdr.field.all_bits);
|
||||
|
||||
struct xran_cp_radioapp_section1 *sec =
|
||||
(struct xran_cp_radioapp_section1 *)rte_pktmbuf_append(c_mbuf, sizeof(struct xran_cp_radioapp_section1));
|
||||
memset(sec, 0, sizeof(*sec));
|
||||
sec->hdr.u.s1.numSymbol = 1;
|
||||
sec->hdr.u1.common.numPrbc = 1;
|
||||
*((uint64_t *)sec) = rte_be_to_cpu_64(*((uint64_t *)sec));
|
||||
|
||||
handle_cplane_packet(ctx, c_mbuf);
|
||||
|
||||
// 2. Send U-plane packet for target_sym
|
||||
current_sym += 3;
|
||||
handle_absolute_symbol_tick(ctx, current_sym);
|
||||
|
||||
struct rte_mbuf *u_mbuf = rte_pktmbuf_alloc(mp);
|
||||
struct xran_ecpri_hdr *u_ecpri = (struct xran_ecpri_hdr *)rte_pktmbuf_append(u_mbuf, sizeof(struct xran_ecpri_hdr));
|
||||
u_ecpri->ecpri_xtc_id = xran_compose_cid(&g_eaxcid_config, 0, 0, 0, 0);
|
||||
|
||||
struct radio_app_common_hdr *u_app =
|
||||
(struct radio_app_common_hdr *)rte_pktmbuf_append(u_mbuf, sizeof(struct radio_app_common_hdr));
|
||||
u_app->frame_id = (target_sym / num_symbols_per_frame) % 256;
|
||||
u_app->sf_slot_sym.subframe_id = slot_in_frame / slots_per_subframe;
|
||||
u_app->sf_slot_sym.slot_id = slot_in_frame % slots_per_subframe;
|
||||
u_app->sf_slot_sym.symb_id = target_sym % 14;
|
||||
u_app->sf_slot_sym.value = rte_cpu_to_be_16(u_app->sf_slot_sym.value);
|
||||
|
||||
struct data_section_hdr *u_data = (struct data_section_hdr *)rte_pktmbuf_append(u_mbuf, sizeof(struct data_section_hdr));
|
||||
u_data->fields.num_prbu = 1;
|
||||
u_data->fields.start_prbu = 0;
|
||||
u_data->fields.sect_id = 0;
|
||||
u_data->fields.all_bits = rte_cpu_to_be_32(u_data->fields.all_bits);
|
||||
|
||||
// IQ Data
|
||||
uint16_t *iq = (uint16_t *)rte_pktmbuf_append(u_mbuf, 1 * 12 * 4);
|
||||
assert(iq != NULL);
|
||||
iq[0] = 0xAAAA;
|
||||
|
||||
handle_uplane_packet(ctx, u_mbuf);
|
||||
|
||||
// 3. Advance to trigger window expiry and job completion
|
||||
current_sym += 10;
|
||||
handle_absolute_symbol_tick(ctx, current_sym);
|
||||
|
||||
uint32_t *txdataF[1] = {0};
|
||||
uint32_t output_iq[273 * 12] = {0};
|
||||
txdataF[0] = output_iq;
|
||||
|
||||
int frame, slot, symbol;
|
||||
uint64_t hyper_frame = 0xFFFFFFFF;
|
||||
do {
|
||||
read_dl_iq(ctx, txdataF, 1, &hyper_frame, &frame, &slot, &symbol);
|
||||
} while (!(frame == (target_sym / num_symbols_per_frame) % 1024 && symbol == target_sym % 14));
|
||||
|
||||
assert(hyper_frame == 3);
|
||||
assert(frame == 5);
|
||||
assert(slot == 1);
|
||||
assert(symbol == 7);
|
||||
|
||||
cleanup_packet_processor(ctx);
|
||||
printf("Hyper-frame calculation test passed!\n");
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
setup_dpdk(argc, argv);
|
||||
@@ -1155,6 +1275,8 @@ int main(int argc, char **argv)
|
||||
usleep(10000);
|
||||
test_prach_generation();
|
||||
usleep(10000);
|
||||
test_hyper_frame_calculation();
|
||||
usleep(10000);
|
||||
|
||||
printf("All tests passed!\n");
|
||||
return 0;
|
||||
|
||||
@@ -197,16 +197,23 @@ int main(int argc, char *argv[])
|
||||
|
||||
// For UL verification, we call write_ul_iq for every symbol.
|
||||
// It will internally check TDD and UL C-plane presence.
|
||||
ul_job_t job;
|
||||
int poll_ret = poll_ul_job(ctx, &job);
|
||||
if (poll_ret == 0) {
|
||||
for (int i = 0; i < job.num_symbols; i++) {
|
||||
write_ul_iq(ctx, txdataF[0], job.symbol + i, &job);
|
||||
}
|
||||
}
|
||||
int frame = (s / 280) % 1024;
|
||||
int slot = (s / 14) % 20;
|
||||
int sym = s % 14;
|
||||
write_ul_iq(ctx, txdataF, MAX_ANTENNAS, frame, slot, sym);
|
||||
write_prach_iq(ctx, txdataF, MAX_ANTENNAS, frame, slot, sym);
|
||||
|
||||
// Drain ready jobs to prevent ring overflow
|
||||
int f, sl, sy;
|
||||
uint64_t hf;
|
||||
while (get_ready_job_count(ctx) > 0) {
|
||||
read_dl_iq(ctx, txdataF, MAX_ANTENNAS, &f, &sl, &sy);
|
||||
read_dl_iq(ctx, txdataF, MAX_ANTENNAS, &hf, &f, &sl, &sy);
|
||||
}
|
||||
}
|
||||
last_tick_sym = current_sym;
|
||||
@@ -230,16 +237,11 @@ int main(int argc, char *argv[])
|
||||
// Flush remaining symbols
|
||||
for (int i = 0; i < 100; i++) {
|
||||
handle_absolute_symbol_tick(ctx, ++last_tick_sym);
|
||||
int frame = (last_tick_sym / 280) % 1024;
|
||||
int slot = (last_tick_sym / 14) % 20;
|
||||
int sym = last_tick_sym % 14;
|
||||
write_ul_iq(ctx, txdataF, MAX_ANTENNAS, frame, slot, sym);
|
||||
write_prach_iq(ctx, txdataF, MAX_ANTENNAS, frame, slot, sym);
|
||||
|
||||
// Drain ready jobs to prevent ring overflow during flush
|
||||
int f, sl, sy;
|
||||
uint64_t hf;
|
||||
while (get_ready_job_count(ctx) > 0) {
|
||||
read_dl_iq(ctx, txdataF, MAX_ANTENNAS, &f, &sl, &sy);
|
||||
read_dl_iq(ctx, txdataF, MAX_ANTENNAS, &hf, &f, &sl, &sy);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@ int nr_get_ssb_start_symbol(const NR_DL_FRAME_PARMS *fp, uint8_t i_ssb)
|
||||
return symbol;
|
||||
}
|
||||
|
||||
static void set_scs_parameters(NR_DL_FRAME_PARMS *fp, int mu, int N_RB_DL, int ssb_case)
|
||||
void set_scs_parameters(NR_DL_FRAME_PARMS *fp, int mu, int N_RB_DL, int ssb_case)
|
||||
{
|
||||
switch(mu) {
|
||||
case NR_MU_0: //15kHz scs
|
||||
|
||||
@@ -12,6 +12,202 @@
|
||||
#include "PHY/NR_TRANSPORT/nr_transport_common_proto.h"
|
||||
#include "openair1/PHY/NR_TRANSPORT/nr_prach.h"
|
||||
|
||||
typedef struct {
|
||||
int reps;
|
||||
int Ncp;
|
||||
int dftlen;
|
||||
int N_ZC;
|
||||
int k;
|
||||
dft_size_idx_t dftsize;
|
||||
int sample_offset_slot;
|
||||
} prach_ru_params_t;
|
||||
|
||||
static prach_ru_params_t get_prach_ru_params(prach_item_t *p,
|
||||
int prachStartSymbol,
|
||||
NR_DL_FRAME_PARMS *fp)
|
||||
{
|
||||
prach_ru_params_t par = {0};
|
||||
const int sum = fp->ofdm_symbol_size + fp->nb_prefix_samples;
|
||||
const int sum0 = fp->ofdm_symbol_size + fp->nb_prefix_samples0;
|
||||
if (prachStartSymbol == 0) {
|
||||
par.sample_offset_slot = 0;
|
||||
} else if (fp->slots_per_subframe == 1) {
|
||||
if (prachStartSymbol <= 7)
|
||||
par.sample_offset_slot = sum * (prachStartSymbol - 1) + sum0;
|
||||
else
|
||||
par.sample_offset_slot = sum * (prachStartSymbol - 2) + sum0 * 2;
|
||||
} else {
|
||||
if (!(p->slot % (fp->slots_per_subframe / 2)))
|
||||
par.sample_offset_slot = sum * (prachStartSymbol - 1) + sum0;
|
||||
else
|
||||
par.sample_offset_slot = sum * prachStartSymbol;
|
||||
}
|
||||
|
||||
int mu = p->numerology_index;
|
||||
|
||||
if (p->prach_sequence_length == 0) {
|
||||
switch (p->pdu.prach_format) {
|
||||
case 0:
|
||||
par.reps = 1;
|
||||
par.Ncp = 3168;
|
||||
par.dftlen = 24576;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
par.reps = 2;
|
||||
par.Ncp = 21024;
|
||||
par.dftlen = 24576;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
par.reps = 4;
|
||||
par.Ncp = 4688;
|
||||
par.dftlen = 24576;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
par.reps = 4;
|
||||
par.Ncp = 3168;
|
||||
par.dftlen = 6144;
|
||||
break;
|
||||
|
||||
default:
|
||||
AssertFatal(1 == 0, "Illegal prach format %d for length 839\n", p->pdu.prach_format);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
switch (p->pdu.prach_format) {
|
||||
case 4: // A1
|
||||
par.reps = 2;
|
||||
par.Ncp = 288 >> mu;
|
||||
break;
|
||||
|
||||
case 5: // A2
|
||||
par.reps = 4;
|
||||
par.Ncp = 576 >> mu;
|
||||
break;
|
||||
|
||||
case 6: // A3
|
||||
par.reps = 6;
|
||||
par.Ncp = 864 >> mu;
|
||||
break;
|
||||
|
||||
case 7: // B1
|
||||
par.reps = 2;
|
||||
par.Ncp = 216 >> mu;
|
||||
break;
|
||||
|
||||
case 8: // B4
|
||||
par.reps = 12;
|
||||
par.Ncp = 936 >> mu;
|
||||
break;
|
||||
|
||||
case 9: // C0
|
||||
par.reps = 1;
|
||||
par.Ncp = 1240 >> mu;
|
||||
break;
|
||||
|
||||
case 10: // C2
|
||||
par.reps = 4;
|
||||
par.Ncp = 2048 >> mu;
|
||||
break;
|
||||
|
||||
default:
|
||||
AssertFatal(1 == 0, "unknown prach format %x\n", p->pdu.prach_format);
|
||||
break;
|
||||
}
|
||||
par.dftlen = 2048 >> mu;
|
||||
}
|
||||
|
||||
if (p->numerology_index == 0) {
|
||||
if (prachStartSymbol == 0 || prachStartSymbol == 7)
|
||||
par.Ncp += 16;
|
||||
} else {
|
||||
if (p->slot % (fp->slots_per_subframe / 2) == 0 && prachStartSymbol == 0)
|
||||
par.Ncp += 16;
|
||||
}
|
||||
|
||||
switch(fp->samples_per_subframe) {
|
||||
case 7680:
|
||||
// 5 MHz @ 7.68 Ms/s
|
||||
par.Ncp >>= 2;
|
||||
par.dftlen >>= 2;
|
||||
break;
|
||||
|
||||
case 15360:
|
||||
// 10, 15 MHz @ 15.36 Ms/s
|
||||
par.Ncp >>= 1;
|
||||
par.dftlen >>= 1;
|
||||
break;
|
||||
|
||||
case 23040:
|
||||
// 20 MHz @ 23.04 Ms/s
|
||||
par.Ncp = (par.Ncp * 3) / 4;
|
||||
par.dftlen = (par.dftlen * 3) / 4;
|
||||
break;
|
||||
|
||||
case 30720:
|
||||
// 20, 25, 30 MHz @ 30.72 Ms/s
|
||||
break;
|
||||
|
||||
case 46080:
|
||||
// 40 MHz @ 46.08 Ms/s
|
||||
par.Ncp = (par.Ncp*3)/2;
|
||||
par.dftlen = (par.dftlen*3)/2;
|
||||
break;
|
||||
|
||||
case 61440:
|
||||
// 40, 50, 60 MHz @ 61.44 Ms/s
|
||||
par.Ncp <<= 1;
|
||||
par.dftlen <<= 1;
|
||||
break;
|
||||
|
||||
case 92160:
|
||||
// 50, 60, 70, 80, 90 MHz @ 92.16 Ms/s
|
||||
par.Ncp *= 3;
|
||||
par.dftlen *= 3;
|
||||
break;
|
||||
|
||||
case 122880:
|
||||
// 70, 80, 90, 100 MHz @ 122.88 Ms/s
|
||||
par.Ncp <<= 2;
|
||||
par.dftlen <<= 2;
|
||||
break;
|
||||
|
||||
case 184320:
|
||||
// 100 MHz @ 184.32 Ms/s
|
||||
par.Ncp = par.Ncp*6;
|
||||
par.dftlen = par.dftlen*6;
|
||||
break;
|
||||
|
||||
case 245760:
|
||||
// 200 MHz @ 245.76 Ms/s
|
||||
par.Ncp <<= 3;
|
||||
par.dftlen <<= 3;
|
||||
break;
|
||||
|
||||
default:
|
||||
AssertFatal(1==0,"sample rate %f MHz not supported for numerology %d\n", fp->samples_per_subframe / 1000.0, mu);
|
||||
}
|
||||
|
||||
par.dftsize = get_dft(par.dftlen);
|
||||
par.N_ZC = (p->prach_sequence_length == 0) ? 839 : 139;
|
||||
|
||||
const unsigned int K = get_prach_K(p->prach_sequence_length, p->pdu.prach_format, p->numerology_index, p->mu);
|
||||
const uint8_t kbar = get_PRACH_k_bar(p->mu, p->numerology_index);
|
||||
|
||||
int n_ra_prb = p->msg1_frequencystart;
|
||||
int k = (12*n_ra_prb) - 6*fp->N_RB_UL;
|
||||
|
||||
if (k<0) k+=(fp->ofdm_symbol_size);
|
||||
|
||||
k*=K;
|
||||
k+=kbar;
|
||||
par.k = k;
|
||||
|
||||
return par;
|
||||
}
|
||||
|
||||
void init_nr_prach(PHY_VARS_gNB *gNB)
|
||||
{
|
||||
int num_prach = 16;
|
||||
@@ -114,6 +310,38 @@ void nr_schedule_rx_prach(PHY_VARS_gNB *gNB, int SFN, int Slot, nfapi_nr_prach_p
|
||||
LOG_W(NR_PHY, "%4d.%2d PRACH occ queue is full: dropping PRACH request\n", SFN, Slot);
|
||||
}
|
||||
|
||||
static void rx_nr_prach_ru_internal_rep(prach_item_t *p,
|
||||
int ant_offset,
|
||||
int32_t **rxdata,
|
||||
NR_DL_FRAME_PARMS *fp,
|
||||
int N_TA_offset,
|
||||
int rep,
|
||||
const prach_ru_params_t *params,
|
||||
c16_t (*rxsigF)[NR_PRACH_SEQ_LEN_L])
|
||||
{
|
||||
AssertFatal(rep >= 0 && rep < params->reps, "rep %d is out of range (reps = %d)\n", rep, params->reps);
|
||||
|
||||
int slot2 = p->prach_sequence_length ? p->slot : p->slot;
|
||||
int sample_offset = get_samples_slot_timestamp(fp, slot2) + params->sample_offset_slot - N_TA_offset + params->Ncp + rep * params->dftlen;
|
||||
|
||||
for (int aa = 0; aa < p->nb_rx; aa++) {
|
||||
int idx = ant_offset + aa;
|
||||
c16_t *prach2 = (c16_t *)&rxdata[idx][sample_offset];
|
||||
|
||||
// do DFT for the specific repetition
|
||||
c16_t tmp[params->dftlen] __attribute__((aligned(32)));
|
||||
dft(params->dftsize, (int16_t *)prach2, (int16_t *)tmp, 1);
|
||||
// Coherent combining of PRACH repetitions (assumes channel does not change, to be revisted for "long" PRACH)
|
||||
LOG_D(PHY, "Doing PRACH combining of repetition %d/%d N_ZC %d\n", rep, params->reps, params->N_ZC);
|
||||
int k2 = params->k;
|
||||
for (int j = 0; j < params->N_ZC; j++, k2++) {
|
||||
if (k2 == params->dftlen)
|
||||
k2 = 0;
|
||||
rxsigF[aa][j] = c16add(rxsigF[aa][j], tmp[k2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void rx_nr_prach_ru_internal(prach_item_t *p,
|
||||
int prachStartSymbol,
|
||||
int prachOccasion,
|
||||
@@ -122,229 +350,9 @@ static void rx_nr_prach_ru_internal(prach_item_t *p,
|
||||
int N_TA_offset,
|
||||
bool das)
|
||||
{
|
||||
int sample_offset_slot;
|
||||
const int sum = fp->ofdm_symbol_size + fp->nb_prefix_samples;
|
||||
const int sum0 = fp->ofdm_symbol_size + fp->nb_prefix_samples0;
|
||||
if (prachStartSymbol == 0) {
|
||||
sample_offset_slot = 0;
|
||||
} else if (fp->slots_per_subframe == 1) {
|
||||
if (prachStartSymbol <= 7)
|
||||
sample_offset_slot = sum * (prachStartSymbol - 1) + sum0;
|
||||
else
|
||||
sample_offset_slot = sum * (prachStartSymbol - 2) + sum0 * 2;
|
||||
} else {
|
||||
if (!(p->slot % (fp->slots_per_subframe / 2)))
|
||||
sample_offset_slot = sum * (prachStartSymbol - 1) + sum0;
|
||||
else
|
||||
sample_offset_slot = sum * prachStartSymbol;
|
||||
}
|
||||
|
||||
LOG_D(PHY,
|
||||
"frame %d, slot %d: doing rx_nr_prach_ru for format %d, numRA %d, prachStartSymbol %d, prachOccasion %d\n",
|
||||
p->frame,
|
||||
p->slot,
|
||||
p->pdu.prach_format,
|
||||
p->pdu.num_ra,
|
||||
prachStartSymbol,
|
||||
prachOccasion);
|
||||
int reps;
|
||||
int Ncp;
|
||||
int dftlen;
|
||||
int mu = p->numerology_index;
|
||||
|
||||
if (p->prach_sequence_length == 0) {
|
||||
LOG_D(PHY,
|
||||
"PRACH in %d.%d, format %d, msg1_frequencyStart %d\n",
|
||||
p->frame,
|
||||
p->slot,
|
||||
p->pdu.prach_format,
|
||||
p->msg1_frequencystart);
|
||||
switch (p->pdu.prach_format) {
|
||||
case 0:
|
||||
reps = 1;
|
||||
Ncp = 3168;
|
||||
dftlen = 24576;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
reps = 2;
|
||||
Ncp = 21024;
|
||||
dftlen = 24576;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
reps = 4;
|
||||
Ncp = 4688;
|
||||
dftlen = 24576;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
reps = 4;
|
||||
Ncp = 3168;
|
||||
dftlen = 6144;
|
||||
break;
|
||||
|
||||
default:
|
||||
AssertFatal(1 == 0, "Illegal prach format %d for length 839\n", p->pdu.prach_format);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
LOG_D(PHY,
|
||||
"PRACH in %d.%d, format %s, msg1_frequencyStart %d,startSymbol %d\n",
|
||||
p->frame,
|
||||
p->slot,
|
||||
prachfmt[p->pdu.prach_format],
|
||||
p->msg1_frequencystart,
|
||||
prachStartSymbol);
|
||||
switch (p->pdu.prach_format) {
|
||||
case 4: // A1
|
||||
reps = 2;
|
||||
Ncp = 288 >> mu;
|
||||
break;
|
||||
|
||||
case 5: // A2
|
||||
reps = 4;
|
||||
Ncp = 576 >> mu;
|
||||
break;
|
||||
|
||||
case 6: // A3
|
||||
reps = 6;
|
||||
Ncp = 864 >> mu;
|
||||
break;
|
||||
|
||||
case 7: // B1
|
||||
reps = 2;
|
||||
Ncp = 216 >> mu;
|
||||
break;
|
||||
|
||||
/*
|
||||
// B2 and B3 do not exist in FAPI
|
||||
case 4: //B2
|
||||
reps = 4;
|
||||
Ncp = 360 >> mu;
|
||||
break;
|
||||
|
||||
case 5: //B3
|
||||
reps = 6;
|
||||
Ncp = 504 >> mu;
|
||||
break;
|
||||
*/
|
||||
|
||||
case 8: // B4
|
||||
reps = 12;
|
||||
Ncp = 936 >> mu;
|
||||
break;
|
||||
|
||||
case 9: // C0
|
||||
reps = 1;
|
||||
Ncp = 1240 >> mu;
|
||||
break;
|
||||
|
||||
case 10: // C2
|
||||
reps = 4;
|
||||
Ncp = 2048 >> mu;
|
||||
break;
|
||||
|
||||
default:
|
||||
AssertFatal(1 == 0, "unknown prach format %x\n", p->pdu.prach_format);
|
||||
break;
|
||||
}
|
||||
dftlen = 2048 >> mu;
|
||||
}
|
||||
|
||||
//actually what we should be checking here is how often the current prach crosses a 0.5ms boundary.
|
||||
//I am not quite sure for which paramter set this would be the case,
|
||||
//so I will ignore it for now and just check if the prach starts on a 0.5ms boundary
|
||||
if (p->numerology_index == 0) {
|
||||
if (prachStartSymbol == 0 || prachStartSymbol == 7)
|
||||
Ncp += 16;
|
||||
} else {
|
||||
if (p->slot % (fp->slots_per_subframe / 2) == 0 && prachStartSymbol == 0)
|
||||
Ncp += 16;
|
||||
}
|
||||
|
||||
switch(fp->samples_per_subframe) {
|
||||
case 7680:
|
||||
// 5 MHz @ 7.68 Ms/s
|
||||
Ncp >>= 2;
|
||||
dftlen >>= 2;
|
||||
break;
|
||||
|
||||
case 15360:
|
||||
// 10, 15 MHz @ 15.36 Ms/s
|
||||
Ncp >>= 1;
|
||||
dftlen >>= 1;
|
||||
break;
|
||||
|
||||
case 23040:
|
||||
// 20 MHz @ 23.04 Ms/s
|
||||
Ncp = (Ncp * 3) / 4;
|
||||
dftlen = (dftlen * 3) / 4;
|
||||
break;
|
||||
|
||||
case 30720:
|
||||
// 20, 25, 30 MHz @ 30.72 Ms/s
|
||||
break;
|
||||
|
||||
case 46080:
|
||||
// 40 MHz @ 46.08 Ms/s
|
||||
Ncp = (Ncp*3)/2;
|
||||
dftlen = (dftlen*3)/2;
|
||||
break;
|
||||
|
||||
case 61440:
|
||||
// 40, 50, 60 MHz @ 61.44 Ms/s
|
||||
Ncp <<= 1;
|
||||
dftlen <<= 1;
|
||||
break;
|
||||
|
||||
case 92160:
|
||||
// 50, 60, 70, 80, 90 MHz @ 92.16 Ms/s
|
||||
Ncp *= 3;
|
||||
dftlen *= 3;
|
||||
break;
|
||||
|
||||
case 122880:
|
||||
// 70, 80, 90, 100 MHz @ 122.88 Ms/s
|
||||
Ncp <<= 2;
|
||||
dftlen <<= 2;
|
||||
break;
|
||||
|
||||
case 184320:
|
||||
// 100 MHz @ 184.32 Ms/s
|
||||
Ncp = Ncp*6;
|
||||
dftlen = dftlen*6;
|
||||
break;
|
||||
|
||||
case 245760:
|
||||
// 200 MHz @ 245.76 Ms/s
|
||||
Ncp <<= 3;
|
||||
dftlen <<= 3;
|
||||
break;
|
||||
|
||||
default:
|
||||
AssertFatal(1==0,"sample rate %f MHz not supported for numerology %d\n", fp->samples_per_subframe / 1000.0, mu);
|
||||
}
|
||||
|
||||
const dft_size_idx_t dftsize = get_dft(dftlen);
|
||||
|
||||
// Do forward transform
|
||||
if (LOG_DEBUGFLAG(DEBUG_PRACH)) {
|
||||
LOG_D(PHY, "rx_prach: Doing PRACH FFT for nb_rx:%d Ncp:%d dftlen:%d\n", p->nb_rx, Ncp, dftlen);
|
||||
}
|
||||
|
||||
const unsigned int K = get_prach_K(p->prach_sequence_length, p->pdu.prach_format, p->numerology_index, p->mu);
|
||||
const uint8_t kbar = get_PRACH_k_bar(p->mu, p->numerology_index);
|
||||
|
||||
int n_ra_prb = p->msg1_frequencystart;
|
||||
int k = (12*n_ra_prb) - 6*fp->N_RB_UL;
|
||||
|
||||
int N_ZC = (p->prach_sequence_length == 0) ? 839 : 139;
|
||||
|
||||
if (k<0) k+=(fp->ofdm_symbol_size);
|
||||
|
||||
k*=K;
|
||||
k+=kbar;
|
||||
prach_ru_params_t params = get_prach_ru_params(p, prachStartSymbol, fp);
|
||||
c16_t rxsigF_tmp[p->nb_rx][NR_PRACH_SEQ_LEN_L];
|
||||
memset(rxsigF_tmp, 0, sizeof(rxsigF_tmp));
|
||||
|
||||
const uint8_t num_beams = p->pdu.beamforming.dig_bf_interface;
|
||||
// When more than one beams, then each occasion is on one beam
|
||||
@@ -353,36 +361,20 @@ static void rx_nr_prach_ru_internal(prach_item_t *p,
|
||||
AssertFatal(prachOccasion < num_beams, "Num of PRACH Occasions must be same as number of beams in beamforming mode\n");
|
||||
ant_offset = prachOccasion * p->nb_rx;
|
||||
}
|
||||
|
||||
// TODO: Remove assumption of contiguous ports after DAS is properly handled in beamforming
|
||||
uint16_t ant_start =
|
||||
get_first_ant_idx(das,
|
||||
p->nb_rx,
|
||||
p->pdu.beamforming.prgs_list[0].dig_bf_interface_list[0].beam_idx,
|
||||
p->pdu.param_v4.numSpatialStreamIndices > 0 ? p->pdu.param_v4.spatialStreamIndices[ant_offset] : 0);
|
||||
for (int aa = 0; aa < p->nb_rx; aa++) {
|
||||
// Fixme: slot or slot makes no sense ???
|
||||
int slot2 = p->prach_sequence_length ? p->slot : p->slot;
|
||||
int idx = ant_start + aa;
|
||||
c16_t *prach = (c16_t *)&rxdata[idx][get_samples_slot_timestamp(fp, slot2) + sample_offset_slot - N_TA_offset];
|
||||
|
||||
// do DFT
|
||||
c16_t *prach2 = prach + Ncp;
|
||||
c16_t rxsigF_tmp[N_ZC];
|
||||
memset(rxsigF_tmp, 0, sizeof(rxsigF_tmp));
|
||||
for (int i = 0; i < reps; i++, prach2 += dftlen) {
|
||||
c16_t tmp[dftlen] __attribute__((aligned(32)));
|
||||
dft(dftsize, (int16_t *)prach2, (int16_t *)tmp, 1);
|
||||
// Coherent combining of PRACH repetitions (assumes channel does not change, to be revisted for "long" PRACH)
|
||||
LOG_D(PHY, "Doing PRACH combining of %d reptitions N_ZC %d\n", reps, N_ZC);
|
||||
// if (k+N_ZC > dftlen) { // PRACH signal is split around DC
|
||||
int k2 = k;
|
||||
for (int j = 0; j < N_ZC; j++, k2++) {
|
||||
if (k2 == dftlen)
|
||||
k2 = 0;
|
||||
rxsigF_tmp[j] = c16add(rxsigF_tmp[j], tmp[k2]);
|
||||
for (int rep = 0; rep < params.reps; rep++) {
|
||||
rx_nr_prach_ru_internal_rep(p, ant_start, rxdata, fp, N_TA_offset, rep, ¶ms, rxsigF_tmp);
|
||||
}
|
||||
}
|
||||
memcpy(p->prach_buf[aa][prachOccasion], rxsigF_tmp, sizeof(rxsigF_tmp));
|
||||
|
||||
for (int aa = 0; aa < p->nb_rx; aa++) {
|
||||
memcpy(p->prach_buf[aa][prachOccasion], rxsigF_tmp[aa], sizeof(c16_t) * params.N_ZC);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -399,6 +391,20 @@ void rx_nr_prach_ru(prach_item_t *p, int32_t **rxdata, NR_DL_FRAME_PARMS *fp, in
|
||||
}
|
||||
}
|
||||
|
||||
void rx_nr_prach_ru_rep(prach_item_t *p,
|
||||
int32_t **rxdata,
|
||||
NR_DL_FRAME_PARMS *fp,
|
||||
int N_TA_offset,
|
||||
int rep,
|
||||
int prachOccasion,
|
||||
c16_t (*rxsigF)[NR_PRACH_SEQ_LEN_L])
|
||||
{
|
||||
int N_dur = get_nr_prach_duration(p->pdu.prach_format);
|
||||
int prachStartSymbol = p->pdu.prach_start_symbol + prachOccasion * N_dur;
|
||||
prach_ru_params_t params = get_prach_ru_params(p, prachStartSymbol, fp);
|
||||
rx_nr_prach_ru_internal_rep(p, 0, rxdata, fp, N_TA_offset, rep, ¶ms, rxsigF);
|
||||
}
|
||||
|
||||
rx_prach_out_t rx_nr_prach(const prach_item_t *in, int occasion)
|
||||
{
|
||||
rx_prach_out_t out = {};
|
||||
|
||||
@@ -124,6 +124,13 @@ typedef struct rx_prach_out {
|
||||
rx_prach_out_t rx_nr_prach(const prach_item_t *, int occasion);
|
||||
|
||||
void rx_nr_prach_ru(prach_item_t *, int32_t **, NR_DL_FRAME_PARMS *frame_parms, int N_TA_offset, bool das);
|
||||
void rx_nr_prach_ru_rep(prach_item_t *p,
|
||||
int32_t **rxdata,
|
||||
NR_DL_FRAME_PARMS *fp,
|
||||
int N_TA_offset,
|
||||
int rep,
|
||||
int prachOccasion,
|
||||
c16_t (*rxsigF)[NR_PRACH_SEQ_LEN_L]);
|
||||
|
||||
void nr_fill_pucch(PHY_VARS_gNB *gNB,
|
||||
int frame,
|
||||
|
||||
@@ -882,9 +882,12 @@ static void map_overlapped_ack(uci_on_pusch_bit_type_t *template,
|
||||
const int32_t num_ack_remaining = G_ack - ack_bits_marked;
|
||||
if (num_ack_remaining <= 0)
|
||||
continue;
|
||||
const uint32_t d_factor_re = get_d_factor_re(num_ack_remaining, num_reserved_bits_on_sym);
|
||||
for (uint32_t i = 0; i < num_reserved_bits_on_sym && ack_bits_marked < G_ack; i += d_factor_re) {
|
||||
uint32_t pos = reserved_indices_on_this_sym[i];
|
||||
const uint32_t num_reserved_re = num_reserved_bits_on_sym / Qm;
|
||||
const uint32_t num_ack_re_remaining = num_ack_remaining / Qm;
|
||||
const uint32_t d_factor_re = get_d_factor_re(num_ack_re_remaining, num_reserved_re);
|
||||
for (uint32_t re = 0; re < num_reserved_re && ack_bits_marked < G_ack; re += d_factor_re) {
|
||||
for (int b = 0; b < Qm; b++) {
|
||||
uint32_t pos = reserved_indices_on_this_sym[re * Qm + b];
|
||||
int bit_in_group = pos % Qm;
|
||||
if (template[pos] == BIT_TYPE_ULSCH) // puncturing ULSCH
|
||||
template[pos] = (bit_in_group >= placeholder_start) ? BIT_TYPE_ACK_PLACEHOLDER : BIT_TYPE_ACK_RESERVED;
|
||||
@@ -894,6 +897,7 @@ static void map_overlapped_ack(uci_on_pusch_bit_type_t *template,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
|
||||
@@ -733,26 +733,15 @@ void nr_generate_pucch2(c16_t **txdataF,
|
||||
const int seq_sz = ((startingPRB + pucch_pdu->prb_size) * NR_PUCCH_DMRS_RB * DMRS_MOD_ORDER + 31) / 32;
|
||||
uint32_t *seq = gold_cache(temp_x2, seq_sz);
|
||||
for (int rb=0; rb<pucch_pdu->prb_size; rb++) {
|
||||
//startingPRB = startingPRB + rb;
|
||||
const bool nb_rb_is_even = frame_parms->N_RB_DL & 1;
|
||||
const bool nb_rb_is_even = !(frame_parms->N_RB_DL & 1);
|
||||
const int halfRBs = frame_parms->N_RB_DL / 2;
|
||||
const int baseRB = rb + startingPRB;
|
||||
int re_offset = (l + startingSymbolIndex) * frame_parms->ofdm_symbol_size + 12 * baseRB;
|
||||
if (nb_rb_is_even) {
|
||||
if (baseRB < halfRBs) // if number RBs in bandwidth is even and current PRB is lower band
|
||||
re_offset += frame_parms->first_carrier_offset;
|
||||
else
|
||||
re_offset -= halfRBs;
|
||||
} else {
|
||||
if (baseRB < halfRBs) // if number RBs in bandwidth is odd and current PRB is lower band
|
||||
re_offset += frame_parms->first_carrier_offset;
|
||||
else if (baseRB > halfRBs) // if number RBs in bandwidth is odd and current PRB is upper band
|
||||
re_offset += -halfRBs + 6;
|
||||
int re_offset = (l + startingSymbolIndex) * frame_parms->ofdm_symbol_size + NR_NB_SC_PER_RB * baseRB;
|
||||
if (baseRB > halfRBs)
|
||||
re_offset -= (nb_rb_is_even) ? (halfRBs * NR_NB_SC_PER_RB) : (halfRBs * NR_NB_SC_PER_RB + 6);
|
||||
else
|
||||
re_offset += frame_parms->first_carrier_offset;
|
||||
}
|
||||
|
||||
//txptr = &txdataF[0][re_offset];
|
||||
int k=0;
|
||||
#ifdef DEBUG_NR_PUCCH_TX
|
||||
int kk=0;
|
||||
|
||||
@@ -53,6 +53,8 @@ MESSAGE_DEF(NGAP_HANDOVER_CANCEL, MESSAGE_PRIORITY_MED, ngap_handover_cancel_t,
|
||||
MESSAGE_DEF(NGAP_UL_RAN_STATUS_TRANSFER, MESSAGE_PRIORITY_MED, ngap_ran_status_transfer_t, ngap_ul_ran_status_transfer)
|
||||
MESSAGE_DEF(NGAP_DL_RAN_STATUS_TRANSFER, MESSAGE_PRIORITY_MED, ngap_ran_status_transfer_t, ngap_dl_ran_status_transfer)
|
||||
MESSAGE_DEF(NGAP_RECONNECT_TIMER, MESSAGE_PRIORITY_MED, IttiMsgText, ngap_gNB_amf_data)
|
||||
MESSAGE_DEF(NGAP_PATH_SWITCH_REQ, MESSAGE_PRIORITY_MED, ngap_path_switch_req_t, ngap_path_switch_req)
|
||||
MESSAGE_DEF(NGAP_PATH_SWITCH_REQ_ACK, MESSAGE_PRIORITY_MED, ngap_path_switch_req_ack_t, ngap_path_switch_req_ack)
|
||||
|
||||
/* NGAP -> RRC messages */
|
||||
MESSAGE_DEF(NGAP_DOWNLINK_NAS , MESSAGE_PRIORITY_MED, ngap_downlink_nas_t , ngap_downlink_nas )
|
||||
|
||||
@@ -47,6 +47,8 @@
|
||||
#define NGAP_HANDOVER_NOTIFY(mSGpTR) (mSGpTR)->ittiMsg.ngap_handover_notify
|
||||
#define NGAP_HANDOVER_CANCEL(mSGpTR) (mSGpTR)->ittiMsg.ngap_handover_cancel
|
||||
#define NGAP_HANDOVER_CANCEL_ACK(mSGpTR) (mSGpTR)->ittiMsg.ngap_handover_cancel_ack
|
||||
#define NGAP_PATH_SWITCH_REQ(mSGpTR) (mSGpTR)->ittiMsg.ngap_path_switch_req
|
||||
#define NGAP_PATH_SWITCH_REQ_ACK(mSGpTR) (mSGpTR)->ittiMsg.ngap_path_switch_req_ack
|
||||
|
||||
#define NGAP_UE_CONTEXT_RELEASE_REQ(mSGpTR) (mSGpTR)->ittiMsg.ngap_ue_release_req
|
||||
#define NGAP_PDUSESSION_RELEASE_COMMAND(mSGpTR) (mSGpTR)->ittiMsg.ngap_pdusession_release_command
|
||||
@@ -669,6 +671,69 @@ typedef struct {
|
||||
uint64_t amf_ue_ngap_id;
|
||||
} ngap_handover_cancel_ack_t;
|
||||
|
||||
/* Path Switch Request 9.2.3.8 3GPP TS 38.413 */
|
||||
typedef struct ngap_path_switch_req_s {
|
||||
// RAN UE NGAP ID
|
||||
uint32_t gNB_ue_ngap_id;
|
||||
// Source AMF UE NGAP ID
|
||||
uint64_t amf_ue_ngap_id;
|
||||
// User Location Information
|
||||
user_location_information_t user_info;
|
||||
// UE Security Capabilities
|
||||
ngap_security_capabilities_t security_capabilities;
|
||||
// Number of pdusession to be switched in the downlink list
|
||||
uint16_t nb_of_pdusessions;
|
||||
// List of PDU Session Resource to be Switched in Downlink
|
||||
pdusession_setup_t pdusessions_tobeswitched[NR_MAX_NB_PDU_SESSIONS];
|
||||
} ngap_path_switch_req_t;
|
||||
|
||||
typedef enum ngap_security_ind_s {
|
||||
NGAP_SECURITY_REQUIRED = 0,
|
||||
NGAP_SECURITY_PREFERRED = 1,
|
||||
NGAP_SECURITY_NOT_NEEDED = 2,
|
||||
} ngap_security_ind_t;
|
||||
|
||||
/* 9.3.1.27 3GPP TS 38.413 */
|
||||
typedef struct security_ind_s {
|
||||
ngap_security_ind_t integrity_protection_ind;
|
||||
ngap_security_ind_t confidentiality_protection_ind;
|
||||
} security_ind_t;
|
||||
|
||||
/* 9.3.4.9 3GPP TS 38.413 */
|
||||
typedef struct path_switch_request_ack_transfer_s {
|
||||
// UL NG-U UP TNL Information (O)
|
||||
gtpu_tunnel_t *n3_incoming;
|
||||
// Security Indication (O)
|
||||
security_ind_t *security_ind;
|
||||
} path_switch_request_ack_transfer_t;
|
||||
|
||||
/* Path Switch Request Acknowledge 9.2.3.9 3GPP TS 38.413
|
||||
* PDU Session Resource Switched Item */
|
||||
typedef struct path_switch_request_ack_pdusession_s {
|
||||
// PDU Session ID (M)
|
||||
int pdusession_id;
|
||||
// Path Switch Request Acknowledge Transfer (M)
|
||||
path_switch_request_ack_transfer_t pathSwitchReqAckTransfer;
|
||||
} path_switch_request_ack_pdusession_t;
|
||||
|
||||
/* Path Switch Request Acknowledge 9.2.3.9 3GPP TS 38.413 */
|
||||
typedef struct ngap_path_switch_req_ack_s {
|
||||
// AMF UE NGAP ID (M)
|
||||
uint64_t amf_ue_ngap_id;
|
||||
// RAN UE NGAP ID (M)
|
||||
uint32_t gNB_ue_ngap_id;
|
||||
// Security Context - Next-Hop Chaining Count (M)
|
||||
uint8_t nh_ncc;
|
||||
// Security Context - Next-Hop NH (M)
|
||||
uint8_t next_security_key[SECURITY_KEY_LENGTH];
|
||||
// List of PDU Session Resource Switched (M)
|
||||
uint16_t nb_of_pdusessions;
|
||||
path_switch_request_ack_pdusession_t pdusessions_switched[NR_MAX_NB_PDU_SESSIONS];
|
||||
// Allowed NSSAI (M)
|
||||
uint8_t nb_allowed_nssais;
|
||||
nssai_t allowed_nssai[NR_MAX_NB_ALLOWED_SNSSAI];
|
||||
} ngap_path_switch_req_ack_t;
|
||||
|
||||
typedef struct ngap_ue_cap_info_ind_s {
|
||||
uint32_t gNB_ue_ngap_id;
|
||||
byte_array_t ue_radio_cap;
|
||||
|
||||
@@ -233,10 +233,16 @@ bool e1_decode_qos_flow_to_setup(qos_flow_to_setup_t *out, const E1AP_QoS_Flow_Q
|
||||
const E1AP_QoSFlowLevelQoSParameters_t *qosParams = &in->qoSFlowLevelQoSParameters;
|
||||
const E1AP_QoS_Characteristics_t *qoS_Characteristics = &qosParams->qoS_Characteristics;
|
||||
switch (qoS_Characteristics->present) {
|
||||
case E1AP_QoS_Characteristics_PR_non_Dynamic_5QI:
|
||||
case E1AP_QoS_Characteristics_PR_non_Dynamic_5QI: {
|
||||
const int fiveqi = qoS_Characteristics->choice.non_Dynamic_5QI->fiveQI;
|
||||
if (fiveqi < MIN_FIVEQI || fiveqi > MAX_FIVEQI) {
|
||||
PRINT_ERROR("non-dynamic fiveQI %d out of range %d..%d\n", fiveqi, MIN_FIVEQI, MAX_FIVEQI);
|
||||
return false;
|
||||
}
|
||||
qos_char->qos_type = NON_DYNAMIC;
|
||||
qos_char->non_dynamic.fiveqi = qoS_Characteristics->choice.non_Dynamic_5QI->fiveQI;
|
||||
qos_char->non_dynamic.fiveqi = fiveqi;
|
||||
break;
|
||||
}
|
||||
case E1AP_QoS_Characteristics_PR_dynamic_5QI: {
|
||||
E1AP_Dynamic5QIDescriptor_t *dynamic5QI = qoS_Characteristics->choice.dynamic_5QI;
|
||||
qos_char->qos_type = DYNAMIC;
|
||||
|
||||
@@ -434,7 +434,12 @@ static bool decode_qos_flow_param(const F1AP_QoSFlowLevelQoSParameters_t *f1ap,
|
||||
if (f1ap->qoS_Characteristics.present == F1AP_QoS_Characteristics_PR_non_Dynamic_5QI) {
|
||||
out->qos_type = NON_DYNAMIC;
|
||||
const F1AP_NonDynamic5QIDescriptor_t *nondyn = f1ap->qoS_Characteristics.choice.non_Dynamic_5QI;
|
||||
out->nondyn.fiveQI = nondyn->fiveQI;
|
||||
const int fiveqi = nondyn->fiveQI;
|
||||
if (fiveqi < MIN_FIVEQI || fiveqi > MAX_FIVEQI) {
|
||||
PRINT_ERROR("non-dynamic fiveQI %d out of range %d..%d\n", fiveqi, MIN_FIVEQI, MAX_FIVEQI);
|
||||
return false;
|
||||
}
|
||||
out->nondyn.fiveQI = fiveqi;
|
||||
} else {
|
||||
_EQ_CHECK_INT(f1ap->qoS_Characteristics.present, F1AP_QoS_Characteristics_PR_dynamic_5QI);
|
||||
const F1AP_Dynamic5QIDescriptor_t *d = f1ap->qoS_Characteristics.choice.dynamic_5QI;
|
||||
@@ -757,7 +762,7 @@ static F1AP_DRBs_ToBeSetupMod_List_t encode_drbs_to_setupmod(int n, const f1ap_d
|
||||
F1AP_QoSInformation_ExtIEs_t *qos_ext_ie = calloc_or_fail(1, sizeof(*qos_ext_ie));
|
||||
it->qoSInformation.choice.choice_extension = (struct F1AP_ProtocolIE_SingleContainer *)qos_ext_ie;
|
||||
qos_ext_ie->id = F1AP_ProtocolIE_ID_id_DRB_Information;
|
||||
qos_ext_ie->criticality = F1AP_Criticality_reject;
|
||||
qos_ext_ie->criticality = F1AP_Criticality_ignore;
|
||||
qos_ext_ie->value.present = F1AP_QoSInformation_ExtIEs__value_PR_DRB_Information;
|
||||
qos_ext_ie->value.choice.DRB_Information = encode_drb_info_nr(&drb->nr);
|
||||
|
||||
|
||||
@@ -442,6 +442,7 @@ static int collect_dl_candidates(gNB_MAC_INST *mac,
|
||||
int n = 0;
|
||||
const frame_structure_t *fs = &mac->frame_structure;
|
||||
const float dl_slots_per_s = (float)get_dl_slots_per_period(fs) / fs->numb_slots_period * fs->numb_slots_frame * 100;
|
||||
const uint64_t now_ms = get_nr_rlc_current_time();
|
||||
|
||||
UE_iterator (UE_list, UE) {
|
||||
if (n >= max_candidates)
|
||||
@@ -527,6 +528,11 @@ static int collect_dl_candidates(gNB_MAC_INST *mac,
|
||||
.alloc_beam_idx = 0,
|
||||
.alloc_new_beam = false,
|
||||
};
|
||||
nr_dl_candidate_t *c = &candidates[n - 1];
|
||||
FOR_EACH_SEQ_ARR(const nr_lc_config_t *, lc, &sched_ctrl->lc_config) {
|
||||
const uint64_t ts = sched_ctrl->rlc_status[lc->lcid].oldest_sdu_arrival_ms;
|
||||
c->hol_delay_ms[lc->lcid] = (ts > 0 && ts <= now_ms) ? (now_ms - ts) : 0;
|
||||
}
|
||||
} else {
|
||||
/* new transmission candidate */
|
||||
if (sched_ctrl->available_dl_harq.head < 0)
|
||||
@@ -572,8 +578,11 @@ static int collect_dl_candidates(gNB_MAC_INST *mac,
|
||||
.alloc_new_beam = false,
|
||||
};
|
||||
nr_dl_candidate_t *c = &candidates[n - 1];
|
||||
for (int lcid = 0; lcid < NR_MAX_NUM_LCID; lcid++)
|
||||
c->pending_bytes_per_lcid[lcid] = sched_ctrl->rlc_status[lcid].bytes_in_buffer;
|
||||
FOR_EACH_SEQ_ARR(const nr_lc_config_t *, lc, &sched_ctrl->lc_config) {
|
||||
c->pending_bytes_per_lcid[lc->lcid] = sched_ctrl->rlc_status[lc->lcid].bytes_in_buffer;
|
||||
const uint64_t ts = sched_ctrl->rlc_status[lc->lcid].oldest_sdu_arrival_ms;
|
||||
c->hol_delay_ms[lc->lcid] = (ts > 0 && ts <= now_ms) ? (now_ms - ts) : 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -265,8 +265,8 @@ static int get_non_dynamic_priority(int fiveqi)
|
||||
for (int i = 0; i < sizeofArray(qos_fiveqi); ++i)
|
||||
if (qos_fiveqi[i] == fiveqi)
|
||||
return qos_priority[i];
|
||||
AssertFatal(false, "illegal 5QI value %d\n", fiveqi);
|
||||
return 0;
|
||||
LOG_W(NR_MAC, "unsupported non-dynamic 5QI %d\n", fiveqi);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static NR_QoS_config_t get_qos_config(const f1ap_qos_flow_param_t *qos)
|
||||
@@ -319,6 +319,8 @@ static int handle_ue_context_drbs_setup(NR_UE_info_t *UE,
|
||||
int prio = 100;
|
||||
for (int q = 0; q < drb->nr.flows_len; ++q) {
|
||||
c.qos_config[q] = get_qos_config(&drb->nr.flows[q].param);
|
||||
if (c.qos_config[q].priority < 0)
|
||||
continue;
|
||||
prio = min(prio, c.qos_config[q].priority);
|
||||
}
|
||||
c.priority = prio;
|
||||
|
||||
@@ -917,6 +917,7 @@ struct nr_dl_candidate {
|
||||
int retx_rbSize; ///< RBs needed for retx, 0 for new tx
|
||||
uint32_t pending_bytes; ///< total bytes waiting in RLC buffers
|
||||
uint32_t pending_bytes_per_lcid[NR_MAX_NUM_LCID]; ///< per-LCID bytes waiting in RLC buffers
|
||||
uint64_t hol_delay_ms[NR_MAX_NUM_LCID]; ///< per-LCID head-of-line delay in milliseconds (age of oldest SDU)
|
||||
float avg_throughput; ///< EWMA goodput in bps (dl_thr_ue)
|
||||
float bler; ///< current BLER estimate
|
||||
int current_mcs; ///< current MCS state (retx: from HARQ, new tx: from BLER tracker)
|
||||
|
||||
@@ -78,6 +78,7 @@ typedef struct {
|
||||
int status_size;
|
||||
int tx_size;
|
||||
int retx_size;
|
||||
uint64_t oldest_sdu_arrival_ms; /* timestamp (ms, from sdu->arrival_ms) of oldest SDU in tx/retx queue, 0 if empty */
|
||||
} nr_rlc_entity_buffer_status_t;
|
||||
|
||||
typedef struct nr_rlc_entity_s {
|
||||
|
||||
@@ -1777,6 +1777,12 @@ nr_rlc_entity_buffer_status_t nr_rlc_entity_am_buffer_status(
|
||||
ret.tx_size = entity->common.bstatus.tx_size;
|
||||
ret.retx_size = entity->common.bstatus.retx_size;
|
||||
|
||||
ret.oldest_sdu_arrival_ms = 0;
|
||||
if (entity->retransmit_list && entity->retransmit_list->sdu)
|
||||
ret.oldest_sdu_arrival_ms = entity->retransmit_list->sdu->arrival_ms;
|
||||
else if (entity->tx_list && entity->tx_list->sdu)
|
||||
ret.oldest_sdu_arrival_ms = entity->tx_list->sdu->arrival_ms;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1851,6 +1857,8 @@ void nr_rlc_entity_am_recv_sdu(nr_rlc_entity_t *_entity,
|
||||
|
||||
if (entity->common.avg_time_is_on)
|
||||
sdu->sdu->time_of_arrival = time_average_now();
|
||||
|
||||
sdu->sdu->arrival_ms = entity->t_current;
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
@@ -88,6 +88,10 @@ nr_rlc_entity_buffer_status_t nr_rlc_entity_tm_buffer_status(nr_rlc_entity_t *_e
|
||||
ret.tx_size = entity->common.bstatus.tx_size;
|
||||
ret.retx_size = 0;
|
||||
|
||||
ret.oldest_sdu_arrival_ms = 0;
|
||||
if (entity->tx_list && entity->tx_list->sdu)
|
||||
ret.oldest_sdu_arrival_ms = entity->tx_list->sdu->arrival_ms;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -147,6 +151,8 @@ void nr_rlc_entity_tm_recv_sdu(nr_rlc_entity_t *_entity,
|
||||
|
||||
if (entity->common.avg_time_is_on)
|
||||
sdu->sdu->time_of_arrival = time_average_now();
|
||||
|
||||
sdu->sdu->arrival_ms = entity->t_current;
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
@@ -534,6 +534,10 @@ nr_rlc_entity_buffer_status_t nr_rlc_entity_um_buffer_status(nr_rlc_entity_t *_e
|
||||
ret.tx_size = entity->common.bstatus.tx_size;
|
||||
ret.retx_size = 0;
|
||||
|
||||
ret.oldest_sdu_arrival_ms = 0;
|
||||
if (entity->tx_list && entity->tx_list->sdu)
|
||||
ret.oldest_sdu_arrival_ms = entity->tx_list->sdu->arrival_ms;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -594,6 +598,8 @@ void nr_rlc_entity_um_recv_sdu(nr_rlc_entity_t *_entity,
|
||||
|
||||
if (entity->common.avg_time_is_on)
|
||||
sdu->sdu->time_of_arrival = time_average_now();
|
||||
|
||||
sdu->sdu->arrival_ms = entity->t_current;
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
@@ -47,7 +47,7 @@ void unlock_nr_rlc_current_time(void)
|
||||
AssertFatal(0, "error locking mutex");
|
||||
}
|
||||
|
||||
static uint64_t get_nr_rlc_current_time(void)
|
||||
uint64_t get_nr_rlc_current_time(void)
|
||||
{
|
||||
lock_nr_rlc_current_time();
|
||||
|
||||
@@ -267,10 +267,12 @@ static mac_rlc_status_resp_t _nr_rlc_status_ind(nr_rlc_ue_t *ue, frame_t frame,
|
||||
// Fix me: temproary reduction meanwhile cpu cost of this computation is optimized
|
||||
buf_stat = rb->buffer_status(rb, 1000 * 1000);
|
||||
ret.bytes_in_buffer = buf_stat.status_size + buf_stat.retx_size + buf_stat.tx_size;
|
||||
ret.oldest_sdu_arrival_ms = buf_stat.oldest_sdu_arrival_ms;
|
||||
} else {
|
||||
if (!(frame % 128) || channel_idP == 0) //to suppress this warning message
|
||||
LOG_W(RLC, "Radio Bearer (channel ID %d) is NULL for UE %d\n", channel_idP, ue->ue_id);
|
||||
ret.bytes_in_buffer = 0;
|
||||
ret.oldest_sdu_arrival_ms = 0;
|
||||
}
|
||||
|
||||
ret.pdus_in_buffer = 0;
|
||||
|
||||
@@ -81,6 +81,8 @@ int nr_rlc_tx_list_occupancy(int ue_id, logical_chan_id_t lcid);
|
||||
|
||||
void nr_rlc_activate_avg_time_to_tx(const int ue_id, const logical_chan_id_t channel_id, const bool is_on);
|
||||
|
||||
uint64_t get_nr_rlc_current_time(void);
|
||||
|
||||
void nr_rlc_srb_recv_sdu(const int ue_id, const logical_chan_id_t channel_id, unsigned char *buf, int size);
|
||||
|
||||
bool nr_rlc_activate_srb0(int ue_id,
|
||||
|
||||
@@ -25,6 +25,11 @@ typedef struct nr_rlc_sdu_t {
|
||||
* transmission is used for statistics
|
||||
*/
|
||||
uint64_t time_of_arrival; /* unit microsecond */
|
||||
|
||||
/* RLC time when the SDU was first received .Used for the HOL-delay
|
||||
* in MAC. TODO: unify with time_of_arrival (feed E2AP/KPM in us)
|
||||
*/
|
||||
uint64_t arrival_ms;
|
||||
} nr_rlc_sdu_t;
|
||||
|
||||
typedef struct nr_rlc_sdu_segment_t {
|
||||
|
||||
@@ -54,7 +54,7 @@ void print_ue_mac_stats(const module_id_t mod, const int frame_rx, const int slo
|
||||
cur += snprintf(cur, end - cur, " DL harq: %lu", mac->stats.dl.rounds[0]);
|
||||
int nb;
|
||||
for (nb = NR_MAX_HARQ_ROUNDS_FOR_STATS - 1; nb > 1; nb--)
|
||||
if (mac->stats.ul.rounds[nb])
|
||||
if (mac->stats.dl.rounds[nb])
|
||||
break;
|
||||
for (int i = 1; i < nb + 1; i++)
|
||||
cur += snprintf(cur, end - cur, "/%lu", mac->stats.dl.rounds[i]);
|
||||
|
||||
@@ -1080,6 +1080,7 @@ static void nr_rrc_apply_qos_add_modify(gNB_RRC_INST *rrc,
|
||||
const non_dynamic_5qi_t *in_non_dynamic = &q_in->qos_characteristics.non_dynamic;
|
||||
const dynamic_5qi_t *in_dynamic = &q_in->qos_characteristics.dynamic;
|
||||
if (q_in->fiveQI_type == NON_DYNAMIC && !is_5qi_standardized(in_non_dynamic->fiveQI)) {
|
||||
// This is a pre-configured 5QI, not a standardized non-dynamic value: not implemented
|
||||
LOG_W(NR_RRC,
|
||||
"QoS flow QFI=%d: 5QI %u is not a standardized value (1-9, 65-90). Skipping QoS flow.\n",
|
||||
q_in->qfi,
|
||||
|
||||
@@ -84,8 +84,8 @@ nr_rrc_qos_t *add_qos(seq_arr_t *qos, const pdusession_level_qos_parameter_t *in
|
||||
return existing;
|
||||
}
|
||||
|
||||
// Validate 5QI value (TS 23.501 allows dynamically assigned 5QIs)
|
||||
if (in->fiveQI_type == NON_DYNAMIC && !is_5qi_standardized(in->qos_characteristics.non_dynamic.fiveQI)) {
|
||||
// This is a pre-configured 5QI, not a standardized non-dynamic value: not implemented
|
||||
LOG_W(NR_RRC,
|
||||
"QoS flow QFI=%d: 5QI %d is not a standardized value. Skipping QoS flow.\n",
|
||||
in->qfi,
|
||||
|
||||
@@ -5,6 +5,7 @@ add_library(fgs_5gmm_lib OBJECT
|
||||
fgs_service_request.c
|
||||
fgmm_service_accept.c
|
||||
fgmm_service_reject.c
|
||||
fgmm_registration_reject.c
|
||||
RegistrationRequest.c
|
||||
RegistrationAccept.c
|
||||
fgmm_identity_request.c
|
||||
|
||||
53
openair3/NAS/NR_UE/5GS/5GMM/MSG/fgmm_registration_reject.c
Normal file
53
openair3/NAS/NR_UE/5GS/5GMM/MSG/fgmm_registration_reject.c
Normal file
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: LicenseRef-CSSL-1.0
|
||||
*/
|
||||
|
||||
#include "fgmm_registration_reject.h"
|
||||
#include "common/platform_types.h"
|
||||
#include "common/utils/ds/byte_array.h"
|
||||
#include "common/utils/eq_check.h"
|
||||
#include "fgmm_lib.h"
|
||||
|
||||
#define REGISTRATION_REJECT_MIN_LEN 1
|
||||
|
||||
/** @brief Encode Registration Reject (8.2.7 of 3GPP TS 24.501) */
|
||||
int encode_fgs_registration_reject(byte_array_t *buffer, const fgs_registration_reject_msg_t *msg)
|
||||
{
|
||||
if (buffer->len < REGISTRATION_REJECT_MIN_LEN) {
|
||||
PRINT_ERROR("Failed to encode Registration Reject: missing Cause IE!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return encode_fgs_nas_cause(buffer, &msg->cause);
|
||||
}
|
||||
|
||||
/** @brief Decode Registration Reject (8.2.7 of 3GPP TS 24.501) — mandatory 5GMM cause only */
|
||||
int decode_fgs_registration_reject(fgs_registration_reject_msg_t *msg, const byte_array_t *buffer)
|
||||
{
|
||||
if (buffer->len < REGISTRATION_REJECT_MIN_LEN) {
|
||||
PRINT_ERROR("Nothing to decode: missing Cause IE!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int decoded = decode_fgs_nas_cause(&msg->cause, buffer);
|
||||
if (decoded < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (buffer->len > decoded) {
|
||||
PRINT_ERROR("Optional Registration Reject IEs present but not handled\n");
|
||||
}
|
||||
|
||||
return decoded;
|
||||
}
|
||||
|
||||
bool eq_registration_reject(const fgs_registration_reject_msg_t *a, const fgs_registration_reject_msg_t *b)
|
||||
{
|
||||
_EQ_CHECK_INT(a->cause, b->cause);
|
||||
return true;
|
||||
}
|
||||
|
||||
void free_fgs_registration_reject(fgs_registration_reject_msg_t *msg)
|
||||
{
|
||||
UNUSED(msg);
|
||||
}
|
||||
20
openair3/NAS/NR_UE/5GS/5GMM/MSG/fgmm_registration_reject.h
Normal file
20
openair3/NAS/NR_UE/5GS/5GMM/MSG/fgmm_registration_reject.h
Normal file
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: LicenseRef-CSSL-1.0
|
||||
*/
|
||||
|
||||
#ifndef FGS_REGISTRATION_REJECT_H
|
||||
#define FGS_REGISTRATION_REJECT_H
|
||||
|
||||
#include "fgmm_lib.h"
|
||||
#include "common/utils/ds/byte_array.h"
|
||||
|
||||
typedef struct {
|
||||
cause_id_t cause;
|
||||
} fgs_registration_reject_msg_t;
|
||||
|
||||
int decode_fgs_registration_reject(fgs_registration_reject_msg_t *msg, const byte_array_t *buffer);
|
||||
int encode_fgs_registration_reject(byte_array_t *buffer, const fgs_registration_reject_msg_t *msg);
|
||||
bool eq_registration_reject(const fgs_registration_reject_msg_t *a, const fgs_registration_reject_msg_t *b);
|
||||
void free_fgs_registration_reject(fgs_registration_reject_msg_t *msg);
|
||||
|
||||
#endif /* FGS_REGISTRATION_REJECT_H */
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "fgs_service_request.h"
|
||||
#include "fgmm_service_accept.h"
|
||||
#include "fgmm_service_reject.h"
|
||||
#include "fgmm_registration_reject.h"
|
||||
#include "fgmm_authentication_failure.h"
|
||||
#include "FGSNASSecurityModeReject.h"
|
||||
#include "fgmm_authentication_reject.h"
|
||||
@@ -258,6 +259,33 @@ static void test_service_reject(void)
|
||||
free_fgs_service_reject(&orig);
|
||||
}
|
||||
|
||||
/** @brief Test NAS Registration Reject enc/dec (plain AMF reject: cause only) */
|
||||
static void test_registration_reject(void)
|
||||
{
|
||||
fgs_registration_reject_msg_t orig = {
|
||||
.cause = Illegal_UE,
|
||||
};
|
||||
|
||||
uint8_t expected_enc[] = {0x03};
|
||||
|
||||
uint8_t buf[64] = {0};
|
||||
byte_array_t buffer = {.buf = buf, .len = sizeof(expected_enc)};
|
||||
|
||||
int encoded_length = encode_fgs_registration_reject(&buffer, &orig);
|
||||
AssertFatal(encoded_length == sizeofArray(expected_enc),
|
||||
"encode_fgs_registration_reject() failed: %d != %ld\n",
|
||||
encoded_length,
|
||||
sizeofArray(expected_enc));
|
||||
AssertFatal(memcmp(buffer.buf, expected_enc, buffer.len) == 0, "Encoding mismatch!\n");
|
||||
|
||||
fgs_registration_reject_msg_t dec = {0};
|
||||
int decoded_length = decode_fgs_registration_reject(&dec, &buffer);
|
||||
AssertFatal(decoded_length >= 0, "decode_fgs_registration_reject() failed\n");
|
||||
AssertFatal(eq_registration_reject(&orig, &dec),
|
||||
"test_registration_reject() failed: original and decoded messages do not match\n");
|
||||
free_fgs_registration_reject(&dec);
|
||||
}
|
||||
|
||||
/** @brief Test NAS Authentication Failure enc/dec */
|
||||
static void test_auth_failure(void)
|
||||
{
|
||||
@@ -373,6 +401,7 @@ int main()
|
||||
test_service_request();
|
||||
test_service_accept();
|
||||
test_service_reject();
|
||||
test_registration_reject();
|
||||
test_auth_failure();
|
||||
test_auth_reject();
|
||||
test_security_mode_reject();
|
||||
|
||||
@@ -49,6 +49,7 @@
|
||||
#include "fgs_nas_utils.h"
|
||||
#include "fgmm_service_accept.h"
|
||||
#include "fgmm_service_reject.h"
|
||||
#include "fgmm_registration_reject.h"
|
||||
#include "fgmm_authentication_reject.h"
|
||||
#include "ds/byte_array.h"
|
||||
#include "key_nas_deriver.h"
|
||||
@@ -128,6 +129,9 @@ static bool unprotected_allowed(byte_array_t buffer, fgs_nas_msg_t msg_type)
|
||||
case FGS_DEREGISTRATION_ACCEPT_UE_ORIGINATING: // for non switch off: deregistration type IE set to NORMAL_DEREGISTRATION
|
||||
return true;
|
||||
case FGS_REGISTRATION_REJECT:
|
||||
// unprotected if the 5GMM cause is not #76 (9.11.3.2)
|
||||
return buffer.len >= sizeof(fgmm_msg_header_t) + 1
|
||||
&& buffer.buf[3] != Not_authorized_for_this_CAG_or_authorized_for_CAG_cells_only;
|
||||
case FGS_SERVICE_REJECT:
|
||||
// unprotected if the 5GMM cause is not #76
|
||||
return buffer.buf[4] != Not_authorized_for_this_CAG_or_authorized_for_CAG_cells_only;
|
||||
@@ -2133,6 +2137,64 @@ static void handle_service_reject(nr_ue_nas_t *nas, const byte_array_t *buffer)
|
||||
LOG_E(NAS, "Received NAS Service Reject message with cause %s\n", fgmm_cause_s[msg.cause].text);
|
||||
}
|
||||
|
||||
/** @brief Handle Registration Reject (8.2.7 / 5.5.1.2.5 of 3GPP TS 24.501)
|
||||
* @todo Per §5.5.1.2.5: forbidden PLMN/TAI, registration attempt counter
|
||||
* @todo N1 NAS signalling release per §5.3.1.3
|
||||
* @todo Optional IEs T3346/T3502, EAP per §5.4.1.2.2.11 */
|
||||
static void handle_registration_reject(nr_ue_nas_t *nas, const byte_array_t *buffer)
|
||||
{
|
||||
DevAssert(buffer && buffer->buf);
|
||||
if (nas->termination_procedure) {
|
||||
return; // already in termination procedure, ignore registration reject
|
||||
}
|
||||
|
||||
fgs_registration_reject_msg_t msg = {0};
|
||||
|
||||
if (buffer->len < sizeof(fgmm_msg_header_t)) {
|
||||
LOG_E(NAS, "Failed to extract Registration Reject message body: buffer length too short\n");
|
||||
return;
|
||||
}
|
||||
|
||||
const uint8_t *pdu_buffer = buffer->buf;
|
||||
uint32_t msg_length = buffer->len;
|
||||
const uint8_t *end = pdu_buffer + msg_length;
|
||||
|
||||
if (pdu_buffer[1] != PLAIN_5GS_MSG) {
|
||||
fgs_nas_message_security_header_t sp_header = {0};
|
||||
int decoded = decode_5gs_security_protected_header(&sp_header, pdu_buffer, msg_length);
|
||||
if (decoded < 0) {
|
||||
LOG_E(NAS, "Registration Reject: failed to decode security protected header\n");
|
||||
return;
|
||||
}
|
||||
pdu_buffer += decoded;
|
||||
}
|
||||
|
||||
fgmm_msg_header_t mm_header = {0};
|
||||
int decoded = decode_5gmm_msg_header(&mm_header, pdu_buffer, end - pdu_buffer);
|
||||
if (decoded < 0) {
|
||||
LOG_E(NAS, "Registration Reject: failed to decode NAS message header\n");
|
||||
return;
|
||||
}
|
||||
if (mm_header.message_type != FGS_REGISTRATION_REJECT) {
|
||||
LOG_E(NAS, "Expected NAS message type FGS_REGISTRATION_REJECT, got %#x\n", mm_header.message_type);
|
||||
return;
|
||||
}
|
||||
pdu_buffer += decoded;
|
||||
|
||||
const byte_array_t ba = {.buf = (uint8_t *)pdu_buffer, .len = end - pdu_buffer};
|
||||
if (decode_fgs_registration_reject(&msg, &ba) < 0) {
|
||||
LOG_E(NAS, "Registration Reject: failed to decode NAS message body\n");
|
||||
free_fgs_registration_reject(&msg);
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_E(NAS, "Received Registration Reject cause: %s\n", print_info(msg.cause, fgmm_cause_s, sizeofArray(fgmm_cause_s)));
|
||||
free_fgs_registration_reject(&msg);
|
||||
nas->fiveGMM_state = FGS_DEREGISTERED;
|
||||
nas->termination_procedure = true;
|
||||
send_nas_detach_req(nas, false);
|
||||
}
|
||||
|
||||
void *nas_nrue(void *args_p)
|
||||
{
|
||||
UNUSED(args_p);
|
||||
@@ -2398,23 +2460,9 @@ void *nas_nrue(void *args_p)
|
||||
case FGS_PDU_SESSION_ESTABLISHMENT_REJ:
|
||||
LOG_E(NAS, "Received PDU Session Establishment reject\n");
|
||||
break;
|
||||
case FGS_REGISTRATION_REJECT: {
|
||||
|
||||
if (pdu_length < 18) {
|
||||
LOG_E(NAS, "Received Registration reject message too short\n");
|
||||
case FGS_REGISTRATION_REJECT:
|
||||
handle_registration_reject(nas, &buffer);
|
||||
break;
|
||||
}
|
||||
|
||||
uint8_t cause = pdu_buffer[17];
|
||||
if (cause >= sizeof(cause_text_info) / sizeof(cause_text_info[0])) {
|
||||
LOG_E(NAS, "Received Registration reject cause %d unknown\n", cause);
|
||||
break;
|
||||
}
|
||||
|
||||
LOG_E(NAS, "Received Registration reject cause: %s\n", cause_text_info[cause].text);
|
||||
exit(1);
|
||||
break;
|
||||
}
|
||||
case FGS_SERVICE_ACCEPT: {
|
||||
handle_service_accept(nas, &buffer);
|
||||
break;
|
||||
|
||||
@@ -133,6 +133,14 @@ nssai_t decode_ngap_nssai(const NGAP_S_NSSAI_t *in)
|
||||
return nssai;
|
||||
}
|
||||
|
||||
void encode_ngap_security_capabilities(NGAP_UESecurityCapabilities_t *out, const ngap_security_capabilities_t *in)
|
||||
{
|
||||
ENCRALG_TO_BIT_STRING(in->nRencryption_algorithms, &out->nRencryptionAlgorithms);
|
||||
ENCRALG_TO_BIT_STRING(in->eUTRAencryption_algorithms, &out->eUTRAencryptionAlgorithms);
|
||||
INTPROTALG_TO_BIT_STRING(in->nRintegrity_algorithms, &out->nRintegrityProtectionAlgorithms);
|
||||
INTPROTALG_TO_BIT_STRING(in->eUTRAintegrity_algorithms, &out->eUTRAintegrityProtectionAlgorithms);
|
||||
}
|
||||
|
||||
ngap_security_capabilities_t decode_ngap_security_capabilities(const NGAP_UESecurityCapabilities_t *in)
|
||||
{
|
||||
ngap_security_capabilities_t out = {0};
|
||||
|
||||
@@ -41,6 +41,7 @@ pdusession_level_qos_parameter_t fill_qos(uint8_t qfi, const NGAP_QosFlowLevelQo
|
||||
void *decode_pdusession_transfer(const asn_TYPE_descriptor_t *td, const OCTET_STRING_t buf);
|
||||
bool decodePDUSessionResourceSetup(pdusession_transfer_t *out, const OCTET_STRING_t in);
|
||||
byte_array_t encode_ngap_pdusession_setup_response_transfer(const pdusession_setup_t *pdusession);
|
||||
void encode_ngap_security_capabilities(NGAP_UESecurityCapabilities_t *out, const ngap_security_capabilities_t *in);
|
||||
|
||||
bool eq_ngap_plmn(const plmn_id_t *a, const plmn_id_t *b);
|
||||
|
||||
|
||||
@@ -281,6 +281,56 @@ void ngap_gNB_handle_sctp_data_ind(sctp_data_ind_t *sctp_data_ind) {
|
||||
AssertFatal (result == EXIT_SUCCESS, "Failed to free memory (%d)!\n", result);
|
||||
}
|
||||
|
||||
static int ngap_gNB_path_switch_request(instance_t instance, ngap_path_switch_req_t *msg)
|
||||
{
|
||||
DevAssert(msg != NULL);
|
||||
NGAP_DEBUG("Triggered Pathswitch Request\n");
|
||||
|
||||
/*Retrieve the NGAP gNB instance associated with Mod_id */
|
||||
ngap_gNB_instance_t *ngap_gNB_instance_p = ngap_gNB_get_instance(instance);
|
||||
DevAssert(ngap_gNB_instance_p != NULL);
|
||||
|
||||
NGAP_NGAP_PDU_t *pdu = encode_ng_path_switch_request(msg);
|
||||
DevAssert(pdu != NULL);
|
||||
|
||||
if (LOG_DEBUGFLAG(DEBUG_ASN1))
|
||||
xer_fprint(stdout, &asn_DEF_NGAP_NGAP_PDU, pdu);
|
||||
|
||||
byte_array_t out = { .buf = NULL, .len = 0 };
|
||||
if (ngap_gNB_encode_pdu(pdu, &out.buf, (uint32_t *)&out.len) < 0) {
|
||||
NGAP_ERROR("Failed to encode Path Switch Request message\n");
|
||||
ASN_STRUCT_FREE(asn_DEF_NGAP_NGAP_PDU, pdu);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ngap_gNB_amf_data_t *amf_ref = ngap_gNB_get_AMF_from_instance(ngap_gNB_instance_p);
|
||||
if (!amf_ref) {
|
||||
NGAP_ERROR("Failed to fetch AMF for current NGAP instance\n");
|
||||
ASN_STRUCT_FREE(asn_DEF_NGAP_NGAP_PDU, pdu);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Create and then store the NGAP UE context
|
||||
ngap_gNB_ue_context_t ue_context_p = {
|
||||
.amf_ref = amf_ref,
|
||||
.gNB_ue_ngap_id = msg->gNB_ue_ngap_id,
|
||||
.amf_ue_ngap_id = msg->amf_ue_ngap_id,
|
||||
.gNB_instance = ngap_gNB_instance_p,
|
||||
.ue_state = NGAP_UE_CONNECTED,
|
||||
};
|
||||
ngap_store_ue_context(&ue_context_p);
|
||||
|
||||
/* UE associated signalling -> use the allocated stream */
|
||||
ngap_gNB_itti_send_sctp_data_req(ngap_gNB_instance_p->instance,
|
||||
ue_context_p.amf_ref->assoc_id,
|
||||
out.buf,
|
||||
out.len,
|
||||
ue_context_p.tx_stream);
|
||||
NGAP_INFO("Sent Path Switch Request to AMF\n");
|
||||
ASN_STRUCT_FREE(asn_DEF_NGAP_NGAP_PDU, pdu);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** @brief UE Mobility Management: callback for Handover Required */
|
||||
int ngap_handover_required(instance_t instance, ngap_handover_required_t *msg)
|
||||
{
|
||||
@@ -613,6 +663,10 @@ void *ngap_gNB_process_itti_msg(void *notUsed)
|
||||
ngap_gNB_pdusession_release_resp(instance, &NGAP_PDUSESSION_RELEASE_RESPONSE(received_msg));
|
||||
break;
|
||||
|
||||
case NGAP_PATH_SWITCH_REQ:
|
||||
ngap_gNB_path_switch_request(instance, &NGAP_PATH_SWITCH_REQ(received_msg));
|
||||
break;
|
||||
|
||||
case NGAP_HANDOVER_REQUIRED:
|
||||
if (ngap_handover_required(instance, &NGAP_HANDOVER_REQUIRED(received_msg)) < 0) {
|
||||
NGAP_ERROR("Handover Required failure: indication to RRC is not sent!\n");
|
||||
|
||||
@@ -1092,6 +1092,54 @@ static int ngap_gNB_handle_handover_cancel_ack(sctp_assoc_t assoc_id, uint32_t s
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** @brief Handler for NGAP Path Switch Request Acknowledge
|
||||
* AMF -> NG-RAN Node */
|
||||
static int ngap_gNB_handle_ng_path_switch_request_ack(sctp_assoc_t assoc_id, uint32_t stream, NGAP_NGAP_PDU_t *pdu)
|
||||
{
|
||||
NGAP_INFO("Received NG Path Switch Request Acknowledge\n");
|
||||
ngap_gNB_amf_data_t *amf_desc_p = NULL;
|
||||
DevAssert(pdu != NULL);
|
||||
|
||||
if ((amf_desc_p = ngap_gNB_get_AMF(NULL, assoc_id, 0)) == NULL) {
|
||||
NGAP_ERROR("[SCTP %u] Received Path Switch Request "
|
||||
"Acknowledge for non existing AMF context\n", assoc_id);
|
||||
return -1;
|
||||
}
|
||||
MessageDef *message_p = itti_alloc_new_message(TASK_NGAP, 0, NGAP_PATH_SWITCH_REQ_ACK);
|
||||
ngap_path_switch_req_ack_t *msg = &NGAP_PATH_SWITCH_REQ_ACK(message_p);
|
||||
memset(msg, 0, sizeof(*msg));
|
||||
if (decode_ng_path_switch_request_acknowledge(msg, pdu) < 0) {
|
||||
NGAP_ERROR("Failed to decode NG Path Switch Request Acknowledge\n");
|
||||
free_ng_path_switch_req_ack(msg);
|
||||
itti_free(TASK_NGAP, message_p);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ngap_gNB_ue_context_t *ue_desc_p = ngap_get_ue_context(msg->gNB_ue_ngap_id);
|
||||
if (!ue_desc_p) {
|
||||
NGAP_ERROR("[SCTP %u] Received Path Switch Request Acknowledge for non "
|
||||
"existing UE context (gNB_ue_ngap_id %d)\n",
|
||||
assoc_id,
|
||||
msg->gNB_ue_ngap_id);
|
||||
free_ng_path_switch_req_ack(msg);
|
||||
itti_free(TASK_NGAP, message_p);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ue_desc_p->rx_stream = stream;
|
||||
if (ue_desc_p->amf_ue_ngap_id != msg->amf_ue_ngap_id) {
|
||||
NGAP_ERROR("UE context amf_ue_ngap_id is different from that of the message (%ld != %ld)",
|
||||
ue_desc_p->amf_ue_ngap_id,
|
||||
msg->amf_ue_ngap_id);
|
||||
free_ng_path_switch_req_ack(msg);
|
||||
itti_free(TASK_NGAP, message_p);
|
||||
return -1;
|
||||
}
|
||||
|
||||
itti_send_msg_to_task(TASK_RRC_GNB, amf_desc_p->ngap_gNB_instance->instance, message_p);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Handle NGAP Paging message from AMF
|
||||
* @param assoc_id SCTP association ID
|
||||
@@ -1542,7 +1590,7 @@ const ngap_message_decoded_callback ngap_messages_callback[][3] = {
|
||||
{0, 0, 0}, /* OverloadStart */
|
||||
{0, 0, 0}, /* OverloadStop */
|
||||
{ngap_gNB_handle_paging, 0, 0}, /* Paging */
|
||||
{0, 0, 0}, /* PathSwitchRequest */
|
||||
{0, ngap_gNB_handle_ng_path_switch_request_ack, 0}, /* PathSwitchRequest */
|
||||
{ngap_gNB_handle_pdusession_modify_request, 0, 0}, /* PDUSessionResourceModify */
|
||||
{0, 0, 0}, /* PDUSessionResourceModifyIndication */
|
||||
{ngap_gNB_handle_pdusession_release_command, 0, 0}, /* PDUSessionResourceRelease */
|
||||
|
||||
@@ -732,3 +732,184 @@ NGAP_NGAP_PDU_t *encode_ng_ul_ran_status_transfer(const ngap_ran_status_transfer
|
||||
|
||||
return pdu;
|
||||
}
|
||||
|
||||
/** @brief Encode NGAP Path Switch Request (9.2.3.8 3GPP TS 38.413) NG-RAN node -> AMF */
|
||||
NGAP_NGAP_PDU_t *encode_ng_path_switch_request(const ngap_path_switch_req_t *msg)
|
||||
{
|
||||
NGAP_NGAP_PDU_t *pdu = calloc_or_fail(1,sizeof(*pdu));
|
||||
|
||||
/* Prepare the NGAP message to encode */
|
||||
pdu->present = NGAP_NGAP_PDU_PR_initiatingMessage;
|
||||
asn1cCalloc(pdu->choice.initiatingMessage, head);
|
||||
head->procedureCode = NGAP_ProcedureCode_id_PathSwitchRequest;
|
||||
head->criticality = NGAP_Criticality_reject;
|
||||
head->value.present = NGAP_InitiatingMessage__value_PR_PathSwitchRequest;
|
||||
NGAP_PathSwitchRequest_t *out = &head->value.choice.PathSwitchRequest;
|
||||
|
||||
// RAN UE NGAP ID (M)
|
||||
asn1cSequenceAdd(out->protocolIEs.list, NGAP_PathSwitchRequestIEs_t, ie1);
|
||||
ie1->id = NGAP_ProtocolIE_ID_id_RAN_UE_NGAP_ID;
|
||||
ie1->criticality = NGAP_Criticality_reject;
|
||||
ie1->value.present = NGAP_PathSwitchRequestIEs__value_PR_RAN_UE_NGAP_ID;
|
||||
ie1->value.choice.RAN_UE_NGAP_ID = msg->gNB_ue_ngap_id;
|
||||
|
||||
// Source AMF UE NGAP ID (M)
|
||||
asn1cSequenceAdd(out->protocolIEs.list, NGAP_PathSwitchRequestIEs_t, ie2);
|
||||
ie2->id = NGAP_ProtocolIE_ID_id_SourceAMF_UE_NGAP_ID;
|
||||
ie2->criticality = NGAP_Criticality_reject;
|
||||
ie2->value.present = NGAP_PathSwitchRequestIEs__value_PR_AMF_UE_NGAP_ID;
|
||||
asn_uint642INTEGER(&ie2->value.choice.AMF_UE_NGAP_ID, msg->amf_ue_ngap_id);
|
||||
|
||||
// User Location Information (M)
|
||||
{
|
||||
asn1cSequenceAdd(out->protocolIEs.list, NGAP_PathSwitchRequestIEs_t, ie3);
|
||||
ie3->id = NGAP_ProtocolIE_ID_id_UserLocationInformation;
|
||||
ie3->criticality = NGAP_Criticality_ignore;
|
||||
ie3->value.present = NGAP_PathSwitchRequestIEs__value_PR_UserLocationInformation;
|
||||
ie3->value.choice.UserLocationInformation.present = NGAP_UserLocationInformation_PR_userLocationInformationNR;
|
||||
asn1cCalloc(ie3->value.choice.UserLocationInformation.choice.userLocationInformationNR, userinfo_nr_p);
|
||||
|
||||
// NR user location information
|
||||
const target_ran_node_id_t *target = &msg->user_info.target_ng_ran;
|
||||
// CGI (M)
|
||||
MACRO_GNB_ID_TO_CELL_IDENTITY(target->targetgNBId, msg->user_info.nrCellIdentity, &userinfo_nr_p->nR_CGI.nRCellIdentity);
|
||||
MCC_MNC_TO_TBCD(target->plmn_identity.mcc,
|
||||
target->plmn_identity.mnc,
|
||||
target->plmn_identity.mnc_digit_length,
|
||||
&userinfo_nr_p->nR_CGI.pLMNIdentity);
|
||||
// TAI (M)
|
||||
INT24_TO_OCTET_STRING(target->tac, &userinfo_nr_p->tAI.tAC);
|
||||
MCC_MNC_TO_PLMNID(target->plmn_identity.mcc,
|
||||
target->plmn_identity.mnc,
|
||||
target->plmn_identity.mnc_digit_length,
|
||||
&userinfo_nr_p->tAI.pLMNIdentity);
|
||||
}
|
||||
|
||||
// UE Security Capabilities (M)
|
||||
asn1cSequenceAdd(out->protocolIEs.list, NGAP_PathSwitchRequestIEs_t, ie4);
|
||||
ie4->id = NGAP_ProtocolIE_ID_id_UESecurityCapabilities;
|
||||
ie4->criticality = NGAP_Criticality_ignore;
|
||||
ie4->value.present = NGAP_PathSwitchRequestIEs__value_PR_UESecurityCapabilities;
|
||||
encode_ngap_security_capabilities(&ie4->value.choice.UESecurityCapabilities, &msg->security_capabilities);
|
||||
|
||||
// PDU Session Resource to be Switched in Downlink List (M)
|
||||
{
|
||||
asn1cSequenceAdd(out->protocolIEs.list, NGAP_PathSwitchRequestIEs_t, ie5);
|
||||
ie5->id = NGAP_ProtocolIE_ID_id_PDUSessionResourceToBeSwitchedDLList;
|
||||
ie5->criticality = NGAP_Criticality_reject;
|
||||
ie5->value.present = NGAP_PathSwitchRequestIEs__value_PR_PDUSessionResourceToBeSwitchedDLList;
|
||||
|
||||
for (const pdusession_setup_t *pdu = msg->pdusessions_tobeswitched; pdu < msg->pdusessions_tobeswitched + msg->nb_of_pdusessions; pdu++) {
|
||||
asn1cSequenceAdd(ie5->value.choice.PDUSessionResourceToBeSwitchedDLList.list, NGAP_PDUSessionResourceToBeSwitchedDLItem_t, item);
|
||||
item->pDUSessionID = pdu->pdusession_id;
|
||||
/* dLQoSFlowPerTNLInformation */
|
||||
NGAP_PathSwitchRequestTransfer_t transfer = {0};
|
||||
transfer.dL_NGU_UP_TNLInformation.present = NGAP_UPTransportLayerInformation_PR_gTPTunnel;
|
||||
asn1cCalloc(transfer.dL_NGU_UP_TNLInformation.choice.gTPTunnel, tmp);
|
||||
GTP_TEID_TO_ASN1(pdu->n3_outgoing.teid, &tmp->gTP_TEID);
|
||||
tnl_to_bitstring(&tmp->transportLayerAddress, pdu->n3_outgoing.addr);
|
||||
for (int j = 0; j < pdu->nb_of_qos_flow; j++) {
|
||||
asn1cSequenceAdd(transfer.qosFlowAcceptedList.list, NGAP_QosFlowAcceptedItem_t, qosItem);
|
||||
qosItem->qosFlowIdentifier = pdu->associated_qos_flows[j].qfi;
|
||||
}
|
||||
|
||||
void *buf;
|
||||
ssize_t encoded = aper_encode_to_new_buffer(&asn_DEF_NGAP_PathSwitchRequestTransfer, NULL, &transfer, &buf);
|
||||
AssertFatal(encoded > 0, "ASN1 message encoding failed !\n");
|
||||
item->pathSwitchRequestTransfer.buf = buf;
|
||||
item->pathSwitchRequestTransfer.size = encoded;
|
||||
|
||||
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_NGAP_PathSwitchRequestTransfer, &transfer);
|
||||
}
|
||||
}
|
||||
return pdu;
|
||||
}
|
||||
|
||||
void free_ng_path_switch_req_ack(ngap_path_switch_req_ack_t *msg)
|
||||
{
|
||||
if (msg == NULL)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < msg->nb_of_pdusessions; i++) {
|
||||
path_switch_request_ack_transfer_t *transfer = &msg->pdusessions_switched[i].pathSwitchReqAckTransfer;
|
||||
|
||||
free(transfer->n3_incoming);
|
||||
transfer->n3_incoming = NULL;
|
||||
|
||||
free(transfer->security_ind);
|
||||
transfer->security_ind = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/** @brief Decode NGAP Path Switch Request Acknowledge (9.2.3.9 3GPP TS 38.413) */
|
||||
int decode_ng_path_switch_request_acknowledge(ngap_path_switch_req_ack_t *msg, NGAP_NGAP_PDU_t *pdu)
|
||||
{
|
||||
DevAssert(pdu != NULL);
|
||||
NGAP_PathSwitchRequestAcknowledgeIEs_t *ie;
|
||||
NGAP_PathSwitchRequestAcknowledge_t *container = &pdu->choice.successfulOutcome->value.choice.PathSwitchRequestAcknowledge;
|
||||
|
||||
// AMF UE NGAP ID (M)
|
||||
NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_PathSwitchRequestAcknowledgeIEs_t, ie, container, NGAP_ProtocolIE_ID_id_AMF_UE_NGAP_ID, true);
|
||||
asn_INTEGER2ulong(&(ie->value.choice.AMF_UE_NGAP_ID), &msg->amf_ue_ngap_id);
|
||||
|
||||
// RAN UE NGAP ID (M)
|
||||
NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_PathSwitchRequestAcknowledgeIEs_t, ie, container, NGAP_ProtocolIE_ID_id_RAN_UE_NGAP_ID, true);
|
||||
msg->gNB_ue_ngap_id = ie->value.choice.RAN_UE_NGAP_ID;
|
||||
|
||||
// Security Context (M)
|
||||
NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_PathSwitchRequestAcknowledgeIEs_t, ie, container, NGAP_ProtocolIE_ID_id_SecurityContext, true);
|
||||
msg->nh_ncc = ie->value.choice.SecurityContext.nextHopChainingCount;
|
||||
memcpy(msg->next_security_key, ie->value.choice.SecurityContext.nextHopNH.buf, ie->value.choice.SecurityContext.nextHopNH.size);
|
||||
|
||||
// PDU Session Resource Switched List (M)
|
||||
NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_PathSwitchRequestAcknowledgeIEs_t, ie, container, NGAP_ProtocolIE_ID_id_PDUSessionResourceSwitchedList, true);
|
||||
msg->nb_of_pdusessions = ie->value.choice.PDUSessionResourceSwitchedList.list.count;
|
||||
|
||||
for (int i = 0; i < msg->nb_of_pdusessions; ++i) {
|
||||
NGAP_PDUSessionResourceSwitchedItem_t *item = ie->value.choice.PDUSessionResourceSwitchedList.list.array[i];
|
||||
path_switch_request_ack_pdusession_t *switched = &msg->pdusessions_switched[i];
|
||||
// PDU Session ID (M)
|
||||
switched->pdusession_id = item->pDUSessionID;
|
||||
// Path Switch Request Acknowledge Transfer (M)
|
||||
NGAP_PathSwitchRequestAcknowledgeTransfer_t *psrAckTransfer = NULL;
|
||||
asn_dec_rval_t dec_rval = uper_decode_complete(NULL,
|
||||
&asn_DEF_NGAP_PathSwitchRequestAcknowledgeTransfer,
|
||||
(void **)&psrAckTransfer,
|
||||
item->pathSwitchRequestAcknowledgeTransfer.buf,
|
||||
item->pathSwitchRequestAcknowledgeTransfer.size);
|
||||
if (dec_rval.code != RC_OK) {
|
||||
NGAP_ERROR("Failed to decode Path Switch Request Acknowledge Transfer\n");
|
||||
ASN_STRUCT_FREE(asn_DEF_NGAP_PathSwitchRequestAcknowledgeTransfer, psrAckTransfer);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// UL NG-U UP TNL Information (O)
|
||||
if (psrAckTransfer->uL_NGU_UP_TNLInformation != NULL) {
|
||||
NGAP_UPTransportLayerInformation_t *up_tnl = psrAckTransfer->uL_NGU_UP_TNLInformation;
|
||||
if (up_tnl->present == NGAP_UPTransportLayerInformation_PR_gTPTunnel && up_tnl->choice.gTPTunnel != NULL) {
|
||||
switched->pathSwitchReqAckTransfer.n3_incoming = calloc_or_fail(1, sizeof(*switched->pathSwitchReqAckTransfer.n3_incoming));
|
||||
OCTET_STRING_TO_INT32(&(up_tnl->choice.gTPTunnel->gTP_TEID), switched->pathSwitchReqAckTransfer.n3_incoming->teid);
|
||||
bitstring_to_tnl(&switched->pathSwitchReqAckTransfer.n3_incoming->addr, up_tnl->choice.gTPTunnel->transportLayerAddress);
|
||||
}
|
||||
}
|
||||
|
||||
// Security Indication (O)
|
||||
if (psrAckTransfer->securityIndication != NULL) {
|
||||
switched->pathSwitchReqAckTransfer.security_ind = calloc_or_fail(1, sizeof(*switched->pathSwitchReqAckTransfer.security_ind));
|
||||
switched->pathSwitchReqAckTransfer.security_ind->integrity_protection_ind = psrAckTransfer->securityIndication->integrityProtectionIndication;
|
||||
switched->pathSwitchReqAckTransfer.security_ind->confidentiality_protection_ind = psrAckTransfer->securityIndication->confidentialityProtectionIndication;
|
||||
}
|
||||
|
||||
ASN_STRUCT_FREE(asn_DEF_NGAP_PathSwitchRequestAcknowledgeTransfer, psrAckTransfer);
|
||||
}
|
||||
|
||||
// Allowed NSSAI (M)
|
||||
NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_PathSwitchRequestAcknowledgeIEs_t, ie, container, NGAP_ProtocolIE_ID_id_AllowedNSSAI, true);
|
||||
NGAP_DEBUG("AllowedNSSAI.list.count %d\n", ie->value.choice.AllowedNSSAI.list.count);
|
||||
msg->nb_allowed_nssais = ie->value.choice.AllowedNSSAI.list.count;
|
||||
for (int i = 0; i < msg->nb_allowed_nssais; ++i) {
|
||||
msg->allowed_nssai[i] = decode_ngap_nssai(&ie->value.choice.AllowedNSSAI.list.array[i]->s_NSSAI);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -20,5 +20,8 @@ NGAP_NGAP_PDU_t *encode_ng_handover_notify(const ngap_handover_notify_t *msg);
|
||||
NGAP_NGAP_PDU_t *encode_ng_ul_ran_status_transfer(const ngap_ran_status_transfer_t *msg);
|
||||
NGAP_NGAP_PDU_t *encode_ng_handover_cancel(const ngap_handover_cancel_t *msg);
|
||||
int decode_ng_handover_cancel_ack(ngap_handover_cancel_ack_t *out, const NGAP_NGAP_PDU_t *pdu);
|
||||
NGAP_NGAP_PDU_t *encode_ng_path_switch_request(const ngap_path_switch_req_t *msg);
|
||||
int decode_ng_path_switch_request_acknowledge(ngap_path_switch_req_ack_t *msg, NGAP_NGAP_PDU_t *pdu);
|
||||
void free_ng_path_switch_req_ack(ngap_path_switch_req_ack_t *msg);
|
||||
|
||||
#endif /* NGAP_GNB_MOBILITY_MANAGEMENT_H_ */
|
||||
|
||||
@@ -97,4 +97,9 @@
|
||||
#include "NGAP_DRBStatusDL12.h"
|
||||
#include "NGAP_QosFlowListWithCause.h"
|
||||
#include "NGAP_QosFlowWithCauseItem.h"
|
||||
#include "NGAP_PDUSessionResourceToBeSwitchedDLItem.h"
|
||||
#include "NGAP_QosFlowAcceptedItem.h"
|
||||
#include "NGAP_PathSwitchRequestTransfer.h"
|
||||
#include "NGAP_PDUSessionResourceSwitchedItem.h"
|
||||
#include "NGAP_PathSwitchRequestAcknowledgeTransfer.h"
|
||||
#endif // NGAP_MSG_INCLUDES_H
|
||||
|
||||
@@ -1,18 +1,6 @@
|
||||
<!-- SPDX-License-Identifier: CC-BY-4.0 -->
|
||||
|
||||
<table style="border-collapse: collapse; border: none;">
|
||||
<tr style="border-collapse: collapse; border: none;">
|
||||
<td style="border-collapse: collapse; border: none;">
|
||||
<a href="http://www.openairinterface.org/">
|
||||
<img src="../doc/images/oai_final_logo.png" alt="" border=3 height=50 width=150>
|
||||
</img>
|
||||
</a>
|
||||
</td>
|
||||
<td style="border-collapse: collapse; border: none; vertical-align: center;">
|
||||
<b><font size = "5">OpenShift Build and Usage Procedures</font></b>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
OpenShift Build and Usage Procedures
|
||||
|
||||
[[_TOC_]]
|
||||
|
||||
|
||||
@@ -147,6 +147,19 @@ int openair0_transport_load(openair0_device_t *device, openair0_config_t *openai
|
||||
return rc;
|
||||
}
|
||||
|
||||
int openair0_load(openair0_device_t *device, char *name, openair0_config_t *openair0_cfg, eth_params_t *eth_params)
|
||||
{
|
||||
loader_shlibfunc_t shlib_fdesc[1];
|
||||
int ret = 0;
|
||||
|
||||
shlib_fdesc[0].fname = eth_params == NULL ? "device_init" : "transport_init";
|
||||
|
||||
ret = load_module_shlib(name, shlib_fdesc, 1, NULL);
|
||||
AssertFatal((ret >= 0), "Library %s couldn't be loaded\n", name);
|
||||
|
||||
return ((devfunc_t)shlib_fdesc[0].fptr)(device, openair0_cfg, eth_params);
|
||||
}
|
||||
|
||||
static void writerEnqueue(re_order_t *ctx, openair0_timestamp_t timestamp, void **txp, int nsamps, int nbAnt, int flags)
|
||||
{
|
||||
pthread_mutex_lock(&ctx->mutex_store);
|
||||
|
||||
@@ -535,6 +535,10 @@ struct openair0_device {
|
||||
*/
|
||||
int (*trx_stop_func)(openair0_device_t *device);
|
||||
|
||||
/*! \brief Get timestamp from timespec
|
||||
*/
|
||||
openair0_timestamp_t (*get_timestamp)(openair0_device_t *device, struct timespec *ts);
|
||||
|
||||
/* Functions API related to UE*/
|
||||
|
||||
/*! \brief Set RX feaquencies
|
||||
@@ -661,6 +665,7 @@ const char *get_devname(int devtype);
|
||||
int openair0_device_load(openair0_device_t *device, openair0_config_t *openair0_cfg);
|
||||
/*! \brief Initialize transport protocol . It returns 0 if OK */
|
||||
int openair0_transport_load(openair0_device_t *device, openair0_config_t *openair0_cfg, eth_params_t *eth_params);
|
||||
int openair0_load(openair0_device_t *device, char *name, openair0_config_t *openair0_cfg, eth_params_t *eth_params);
|
||||
|
||||
/*! \brief Set RX frequencies
|
||||
* \param device the hardware to use
|
||||
|
||||
@@ -44,16 +44,26 @@ class VRTSIMTest : public ::testing::TestWithParam<VRTSIMTestCase> {
|
||||
openair0_device_t client_device = {0};
|
||||
openair0_config_t server_config = {0};
|
||||
openair0_config_t client_config = {0};
|
||||
std::string socket_path;
|
||||
std::string shm_name;
|
||||
|
||||
void SetUp() override
|
||||
{
|
||||
const auto ¶m = GetParam();
|
||||
|
||||
std::string unique_suffix = std::to_string(getpid()) + "_" + std::to_string(rand());
|
||||
socket_path = "/tmp/vrtsim_connection_" + unique_suffix;
|
||||
shm_name = "vrtsim_channel_" + unique_suffix;
|
||||
|
||||
// Setup server
|
||||
std::vector<const char *> server_argv = {"--vrtsim.role",
|
||||
"server",
|
||||
"--vrtsim.disable-timing-thread",
|
||||
"1",
|
||||
"--vrtsim.connection_descriptor",
|
||||
socket_path.c_str(),
|
||||
"--vrtsim.shm_channel_name",
|
||||
shm_name.c_str(),
|
||||
"--vrtsim.ue_config.[0].antennas",
|
||||
param.ue_antennas.c_str()};
|
||||
cfg1 = load_configmodule(server_argv.size(), (char **)server_argv.data(), CONFIG_ENABLECMDLINEONLY);
|
||||
@@ -65,7 +75,12 @@ class VRTSIMTest : public ::testing::TestWithParam<VRTSIMTestCase> {
|
||||
ASSERT_EQ(server_device.trx_start_func(&server_device), 0);
|
||||
|
||||
// Setup client
|
||||
std::vector<const char *> client_argv = {"--vrtsim.role", "client"};
|
||||
std::vector<const char *> client_argv = {"--vrtsim.role",
|
||||
"client",
|
||||
"--vrtsim.connection_descriptor",
|
||||
socket_path.c_str(),
|
||||
"--vrtsim.shm_channel_name",
|
||||
shm_name.c_str()};
|
||||
cfg2 = load_configmodule(client_argv.size(), (char **)client_argv.data(), CONFIG_ENABLECMDLINEONLY);
|
||||
uniqCfg = cfg2;
|
||||
client_config.tx_num_channels = param.client_tx;
|
||||
@@ -85,6 +100,7 @@ class VRTSIMTest : public ::testing::TestWithParam<VRTSIMTestCase> {
|
||||
end_configmodule(cfg1);
|
||||
if (cfg2)
|
||||
end_configmodule(cfg2);
|
||||
uniqCfg = nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -180,17 +196,27 @@ class VRTSIMMultiUETest : public ::testing::TestWithParam<VRTSIMMultiTestCase> {
|
||||
std::vector<openair0_device_t> client_devices;
|
||||
openair0_config_t server_config = {0};
|
||||
std::vector<openair0_config_t> client_configs;
|
||||
std::string socket_path;
|
||||
std::string shm_name;
|
||||
|
||||
void SetUp() override
|
||||
{
|
||||
const auto ¶m = GetParam();
|
||||
|
||||
std::string unique_suffix = std::to_string(getpid()) + "_" + std::to_string(rand());
|
||||
socket_path = "/tmp/vrtsim_connection_" + unique_suffix;
|
||||
shm_name = "vrtsim_channel_" + unique_suffix;
|
||||
|
||||
// Setup server
|
||||
std::vector<std::string> server_args_store = {"test_vrtsim",
|
||||
"--vrtsim.role",
|
||||
"server",
|
||||
"--vrtsim.disable-timing-thread",
|
||||
"1",
|
||||
"--vrtsim.connection_descriptor",
|
||||
socket_path,
|
||||
"--vrtsim.shm_channel_name",
|
||||
shm_name,
|
||||
"--vrtsim.num_ues",
|
||||
std::to_string(param.num_ues)};
|
||||
for (int i = 0; i < param.num_ues; i++) {
|
||||
@@ -217,7 +243,15 @@ class VRTSIMMultiUETest : public ::testing::TestWithParam<VRTSIMMultiTestCase> {
|
||||
cfg_clients.resize(param.num_ues);
|
||||
|
||||
for (int i = 0; i < param.num_ues; i++) {
|
||||
std::vector<std::string> client_args_store = {"test_vrtsim", "--vrtsim.role", "client", "--vrtsim.ue_id", std::to_string(i)};
|
||||
std::vector<std::string> client_args_store = {"test_vrtsim",
|
||||
"--vrtsim.role",
|
||||
"client",
|
||||
"--vrtsim.connection_descriptor",
|
||||
socket_path,
|
||||
"--vrtsim.shm_channel_name",
|
||||
shm_name,
|
||||
"--vrtsim.ue_id",
|
||||
std::to_string(i)};
|
||||
std::vector<const char *> client_argv;
|
||||
for (const auto &arg : client_args_store) {
|
||||
client_argv.push_back(arg.c_str());
|
||||
@@ -254,6 +288,7 @@ class VRTSIMMultiUETest : public ::testing::TestWithParam<VRTSIMMultiTestCase> {
|
||||
if (cfg_server) {
|
||||
end_configmodule(cfg_server);
|
||||
}
|
||||
uniqCfg = nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -272,7 +272,7 @@ static void *vrtsim_timing_job(void *arg)
|
||||
shm_td_iq_channel_produce_samples(vrtsim_state->channel, samples_to_produce);
|
||||
last_sample_index = sample_index;
|
||||
}
|
||||
usleep(1);
|
||||
usleep(20);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -1100,6 +1100,14 @@ __attribute__((__visibility__("default"))) void vrtsim_produce_samples(openair0_
|
||||
shm_td_iq_channel_produce_samples(vrtsim_state->channel, num_samples);
|
||||
}
|
||||
|
||||
openair0_timestamp_t vrtsim_get_timestamp(openair0_device_t *device, struct timespec *ts)
|
||||
{
|
||||
vrtsim_state_t *vrtsim_state = (vrtsim_state_t *)device->priv;
|
||||
int64_t diff = (ts->tv_sec - vrtsim_state->start_ts.tv_sec) * 1000000000 + (ts->tv_nsec - vrtsim_state->start_ts.tv_nsec);
|
||||
double diff_samples = vrtsim_state->sample_rate * vrtsim_state->timescale * diff / 1e9;
|
||||
return diff_samples;
|
||||
}
|
||||
|
||||
__attribute__((__visibility__("default"))) int device_init(openair0_device_t *device, openair0_config_t *openair0_cfg)
|
||||
{
|
||||
randominit();
|
||||
@@ -1119,6 +1127,9 @@ __attribute__((__visibility__("default"))) int device_init(openair0_device_t *de
|
||||
device->trx_write_beams_func = vrtsim_write_beams;
|
||||
device->trx_set_beams = vrtsim_set_beams;
|
||||
device->trx_set_beams2 = vrtsim_set_beams2;
|
||||
if (vrtsim_state->role == ROLE_SERVER) {
|
||||
device->get_timestamp = vrtsim_get_timestamp;
|
||||
}
|
||||
|
||||
device->type = RFSIMULATOR;
|
||||
device->openair0_cfg = &openair0_cfg[0];
|
||||
|
||||
Reference in New Issue
Block a user