Compare commits

...

141 Commits

Author SHA1 Message Date
Guido Casati
1b85b63ff0 Initialize PDU session socket in start_sdap_tun_ue only for new SDAP entities 2025-04-28 10:31:57 +02:00
Guido Casati
23d22d6c95 Print errno in nr_sdap_rx_entity 2025-04-28 10:31:57 +02:00
Robert Schmidt
4da30019be Merge branch 'integration_2025_w16' into 'develop'
Integration: `2025.w16`

Closes #949, #929, and #941

See merge request oai/openairinterface5g!3392

* !3207 Create nrUE network interface from SDAP (rebased)
* !3293 Cleanup and refactor initial gNB configuration
* !3326 Enable SSB-SINR based CSI reporting
* !3330 NR UE: consider the ratio of PUSCH EPRE to DMRS EPRE when calculating PUSCH DMRS power scaling
* !3376 Correct block_length to Kprime for LDPC decoder
* !3390 fix IP address in 4G UE
* !3071 time management module
* !3254 Follow up on NAS Registration Accept lib
* !3121 Bugfix for number of PRBs in PUCCH format 2
* !3387 fix NTN LEO scenario
* !3393 rejecting DRB if not configured in PDCP-Config
* !3372 Add Delay Mechanism for Initial MIB/SIB Transmission in gNB Scheduler
2025-04-22 16:16:33 +00:00
Thomas Schlichter
8f93424e53 Adapt NTN-LEO config after integration branch runs 2025-04-22 15:47:41 +02:00
Robert Schmidt
d623cbed7d Merge remote-tracking branch 'origin/issue_949' into integration_2025_w16 (!3393)
rejecting DRB if not configured in PDCP-Config

Closes issue #949
2025-04-22 09:38:42 +02:00
Robert Schmidt
f5b0d9603b Merge remote-tracking branch 'origin/fix_ntn_leo' into integration_2025_w16 (!3387)
fix NTN LEO scenario

NR UE: in nr_slot_fep() perform FO compensation in local tmp_dft_in
array instead of global rxdata array.

This avoids problems if nr_slot_fep() is called more than once for an
OFDM symbol.
2025-04-22 09:38:07 +02:00
Robert Schmidt
884e60da69 Merge remote-tracking branch 'origin/remove-prach-delay' into integration_2025_w16 (!3372)
Add Delay Mechanism for Initial MIB/SIB Transmission in gNB Scheduler

- Delay MIB/SIB in gNB until PRACH noise converges
- Remove the first PRACH random access attempt delay in NR UE
2025-04-22 09:37:31 +02:00
Jaroslava Fiedlerova
3d5177e5f7 Merge remote-tracking branch 'origin/bugfix_pucch2_prb_size' into integration_2025_w16 (!3121)
Bugfix for number of PRBs in PUCCH format 2

[TS 38.213 - Section 9.2.1 PUCCH Resource Sets] If the format indicates
PUCCH-format2 or PUCCH-format3, the PUCCH format configured for a PUCCH resource
is PUCCH format 2 or PUCCH format 3, respectively, where the PUCCH resource also
includes a number of PRBs provided by nrofPRBs, a number of symbols for a PUCCH
transmission provided by nrofSymbols, and a first symbol for the PUCCH
transmission provided by startingSymbolIndex.

[TS 38.213 - Section 9.2.3 UE procedure for reporting HARQ-ACK] If a UE transmits
a PUCCH with O_ACK HARQ-ACK information bits and O_CRC bits using PUCCH format 2
or PUCCH format 3 in a PUCCH resource that includes M_PUCCH_RB PRBs, the UE
determines a number of PRBs M_PUCCH_RB_min for the PUCCH transmission to be the
minimum number of PRBs (...)

[TS 38.213 - Section 9.2.5.1 UE procedure for multiplexing HARQ-ACK or CSI and
SR in a PUCCH] If a UE transmits a PUCCH with O_ACK HARQ-ACK information bits,
O_SR = ⌈log2(K+1)⌉ SR bits, and O_CRC CRC bits using PUCCH format 2 or PUCCH
format 3 in a PUCCH resource that includes M_PUCCH_RB PRBs, the UE determines
a number of PRBs M_PUCCH_RB_min for the PUCCH transmission to be the minimum
number of PRBs (...)

[TS 38.213 - Section 9.2.5.2 UE procedure for multiplexing HARQ-ACK/SR/CSI in
a PUCCH] If a UE has HARQ-ACK, SR and wideband or sub-band CSI reports to
transmit and the UE determines a PUCCH resource with PUCCH format 2, or the UE
has HARQ-ACK, SR and wideband CSI reports to transmit and the UE determines a
PUCCH resource with PUCCH format 3 or PUCCH format 4, where (...) the UE
transmits the HARQ-ACK, SR, and CSI reports bits by selecting the minimum number
PUCCH M_PUCCH_RB_min of the M_PUCCH_RB PRBs satisfying (...)

[TS 38.213 - Section 9.2.5.2 UE procedure for multiplexing HARQ-ACK/SR/CSI in a
PUCCH] If a UE has one or more CSI reports and zero or more HARQ-ACK/SR
information bits to transmit in a PUCCH where the HARQ-ACK, if any, is in
response to a PDSCH reception without a corresponding PDCCH; if any of the CSI
reports are overlapping (...); else, the UE transmits the
O_ACK + O_SR + O_CSI + O_CRC bits in a PUCCH resource provided by
pucch-CSI-ResourceList and determined as described in clause 9.2.5

Therefore, the number of PRBs to be used by PUCCH-format2 is M_PUCCH_RB_min,
except if we have zero HARQ-ACK bits, or if we have any HARQ-ACK, is in response
to a PDSCH reception without a corresponding PDCCH. In this case, we must use
the resources indicated in the pucch-CSI-ResourceList and determined as described
in clause 9.2.5.

In OAI, we have:
asn1cSeqAdd(&csirep->reportConfigType.choice.periodic->pucch_CSI_ResourceList.list, pucchcsires);

Where the resource added to the list corresponds to:

pucchres2->pucch_ResourceId = *pucchressetid;
pucchres2->startingPRB = 0;
pucchres2->intraSlotFrequencyHopping = NULL;
pucchres2->secondHopPRB = NULL;
pucchres2->format.present = NR_PUCCH_Resource__format_PR_format2;
pucchres2->format.choice.format2 = calloc(1,sizeof(*pucchres2->format.choice.format2));
pucchres2->format.choice.format2->nrofPRBs = 8;
pucchres2->format.choice.format2->nrofSymbols = 1;
pucchres2->format.choice.format2->startingSymbolIndex = 13;

Currently, in develop branch, we get these prb_size for PUCCH-format2:

[NR_RRC]   NGAP_PDUSESSION_SETUP_RESP: sending the message
[NGAP]   pdusession_setup_resp_p: pdusession ID 1, gnb_addr 172.31.0.1, SIZE 4, TEID 1579669408
[NR_MAC]   (596.3) pucch_pdu->prb_size = 4
[NR_MAC]   (596.4) pucch_pdu->prb_size = 3
[NR_MAC]   (600.3) pucch_pdu->prb_size = 4
[NR_MAC]   (600.4) pucch_pdu->prb_size = 3
[NR_MAC]   (604.3) pucch_pdu->prb_size = 4
[NR_MAC]   (604.4) pucch_pdu->prb_size = 3
[NR_MAC]   (608.3) pucch_pdu->prb_size = 4

While in QXDM we have Num RB = 8, whenever we have zero HARQ-ACK bits:

UCI Request BMask =             ACK_NACK_RPT | CSI_RPT
               Start symbol = 13
               Num Symbols = 1
               Starting RB = 0
               REL16 DMRS EN = 0
               Dual Pol Status = 0
               Num RB = 2
               Freq Hopping Flag =          HOP_MOD_NEITHER
               Second Hop RB = NA
               Num HARQ ACK Bits = 1
               Num SR Bits = 0
               Num UCI P1 Bits = 8

UCI Request BMask =             ACK_NACK_RPT | CSI_RPT
               Start symbol = 13
               Num Symbols = 1
               Starting RB = 0
               REL16 DMRS EN = 0
               Dual Pol Status = 0
               Num RB = 3
               Freq Hopping Flag =          HOP_MOD_NEITHER
               Second Hop RB = NA
               Num HARQ ACK Bits = 3
               Num SR Bits = 0
               Num UCI P1 Bits = 13

UCI Request BMask =                            CSI_RPT
               Start symbol = 13
               Num Symbols = 1
               Starting RB = 0
               REL16 DMRS EN = 0
               Dual Pol Status = 0
               Num RB = 8
               Freq Hopping Flag =          HOP_MOD_NEITHER
               Second Hop RB = NA
               Num HARQ ACK Bits = 0
               Num SR Bits = 0
               Num UCI P1 Bits = 10

UCI Request BMask =                   CSI_RPT | SR_RPT
               Start symbol = 13
               Num Symbols = 1
               Starting RB = 0
               REL16 DMRS EN = 0
               Dual Pol Status = 0
               Num RB = 8
               Freq Hopping Flag =          HOP_MOD_NEITHER
               Second Hop RB = NA
               Num HARQ ACK Bits = 0
               Num SR Bits = 1
               Num UCI P1 Bits = 11

Therefore, this MR fixes the prb_size, so that the number of PUCCH-format2 PRBs matches correctly.
2025-04-18 14:24:07 +02:00
Jaroslava Fiedlerova
94e82200e3 Merge remote-tracking branch 'origin/nas-reg-accept-lib' into integration_2025_w16 (!3254)
Follow up on NAS Registration Accept lib

This MR is a follow up on !2984 (merged).

The goal is to enhance the NAS Registration Accept lib:
- Add regression test for NAS Registration Accept: the goal of this test is to
  prevent on the decoding of the NAS Registration Accept sent by the OAI CN5G
- Add equality check and free utility functions to NAS Registration Accept
  library
- Free NAS Registration Accept after use: heap memory is allocated for this
  message during decoding and shall be free after processing
- Remove harcoded Slice Differentiator (SD) in NAS decode_nssai_ie and update
  the processing logic: The SD stored in the allowed S-NSSAI list was hardcoded
  in the decoding function, however this IE is not mandatory but optional.
  Therefore, the check of the decoded message contents should take into account
  the presence of the field in the received message and in that scenario, check
  the stored value against the one from the UICC. If no SD was received, consider
  the UICC SD a valid one.
- Cleanup and other fixes to decode_registration_accept: (1) adopt byte array
  (2) move registration result enc/dec to registration accept library (3) check
  on buffer lengths (4) add defines whenever necessary (5) simplify the code
2025-04-18 14:21:22 +02:00
Bartosz Podrygajlo
84b9272979 Disable PRACH delay workaround in NR UE
Disable the PRACH workaround in NR UE. With the MIB/SIB delay
implemented on the gNB there is no longer a need to delay first
NR UE random access attempt.
2025-04-18 10:27:23 +02:00
Bartosz Podrygajlo
a3ef5293fb Add Delay Mechanism for Initial MIB/SIB Transmission in gNB Scheduler
This commit introduces a delay mechanism for the first MIB/SIB transmission
in the gNB scheduler to allow PRACH noise estimate to converge stabilize
before first PRACH preamble reception.

Key changes:
- Added the `num_scheduled_prach_rx` variable to gNB_MAC_INST to keep track
  of total number scheduled of PRACH receptions. Using uint64_t type to avoid
  overflow.
- Updated the `gNB_dlsch_ulsch_scheduler` function to conditionally schedule
  MIB/SIB transmissions if `num_scheduled_prach_rx` is at least 100.
  The value comes from OAIs implementation of PRACH noise estimate.
2025-04-18 10:26:36 +02:00
francescomani
ba2a0c4d1e rejecting DRB addition if DRB field not present at setup in PDCP-Config 2025-04-18 09:44:41 +02:00
Jaroslava Fiedlerova
c0d9212afc Merge remote-tracking branch 'origin/feature-time-manager' into integration_2025_w16 (!3071)
time management module

introduction of a time management module
2025-04-18 09:08:24 +02:00
Cedric Roux
b89b3abd13 API change: new arguments to time_manager_start() 2025-04-18 09:05:39 +02:00
Cedric Roux
4834d93006 fix typo 2025-04-18 09:02:55 +02:00
Cedric Roux
2f7eb6ebf9 replace many DevAssert with AssertFatal for better user experience 2025-04-18 09:02:55 +02:00
Cedric Roux
b487c4deb2 restructure the code for retrieval of configuration parameters 2025-04-18 09:02:55 +02:00
Cedric Roux
c9ad10f9f9 remove ngap from linking
It used to be necessary.
2025-04-18 09:02:55 +02:00
Cedric Roux
59be08e3e6 add link to time management module to the main readme 2025-04-18 09:02:55 +02:00
Cedric Roux
7cbedb1575 time management module
This commit introduces a time management module.

See doc/time_management.md for details.
2025-04-18 09:02:54 +02:00
Jaroslava Fiedlerova
9bccad31b5 Merge remote-tracking branch 'origin/hotfix-ip-address-lte-ue' into integration_2025_w16 (!3390)
fix IP address in 4G UE

The following error was appearing when running LTE UE:

inet_pton(): cannot convert -64.-74.0.2 to IPv4 network address

The allocated address by the core actually is 192.182.0.2.
On my host "char" is signed, leading to the above error (192 and 182
are seen as signed).
Let's force "ip_addr" unsigned and everyone is happy.
2025-04-18 08:10:28 +02:00
Jaroslava Fiedlerova
32d1026b16 Merge remote-tracking branch 'origin/fix-ldpcdecoder-compile-by-clang' into integration_2025_w16 (!3376)
Correct block_length to Kprime for LDPC decoder

When compile the OAI by CMake, it shows the following error:

../openair1/PHY/CODING/nrLDPC_decoder/nrLDPC_decoder_CL.c:353:40: error: no member named 'block_length' in 'struct nrLDPC_dec_params'
    int block_length    = p_decParams->block_length;
2025-04-18 08:09:23 +02:00
Jaroslava Fiedlerova
18372ae27e Merge remote-tracking branch 'origin/issue-929' into integration_2025_w16 (!3330)
NR UE: consider the ratio of PUSCH EPRE to DMRS EPRE when calculating PUSCH
DMRS power scaling

When calculating DMRS scaling ratio, we should consider PUSCH EPRE to DMRS
EPRE according to TS 38.214 Table 6.2.2-1.

Close #929
2025-04-17 23:03:13 +02:00
Jaroslava Fiedlerova
6e67c1ac20 Merge remote-tracking branch 'origin/NR_SSB_SINR' into integration_2025_w16 (!3326)
Enable SSB-SINR based CSI reporting

3GPP 38.331 release 16 specifies that If the field reportQuantity-r16 is present,
UE shall ignore reportQuantity (without suffix). reportQuantity-r16 configures
SSB based SINR as the reporting quantity.

set do_SINR = 1; in gNB params part of the config file to enable SSB based SINR
reporting.
2025-04-17 23:01:08 +02:00
Jaroslava Fiedlerova
315ed8e564 Merge remote-tracking branch 'origin/nr-gnb-config-refactor' into integration_2025_w16 (!3293)
Cleanup and refactor initial gNB configuration

This is follow-up on !3048 (merged). Major changes in this MR:
- Add config functions to reduce redundant code repeated in multiple places in the file
- Add macros to reduce redundant code in gNB configuration
- Refactor RCconfig_NR_NG
- Cleanup obsolete and commented out code
- Refactor includes in executables/nr-gnb.c
- Introduce platform type plmn_id_t for consistency across the stack: F1AP, NGAP can now use the same struct type
- And other minor formatting
2025-04-17 19:56:58 +02:00
Jaroslava Fiedlerova
ba2d1a880d Merge remote-tracking branch 'origin/sdap-changes' into integration_2025_w16 (!3207)
Create nrUE network interface from SDAP (rebased)

A rebase of !2485. Dropped a few refactors and SDAP manager changes.
Changes
1. Move tunnel interface read thread to SDAP for gNB (noS1) and UE (noS1 and SA).
2. Hold tunnel interface socket fd in SDAP entity instead of global variable.
3. Handle UE tunnel interface creation and IP configuration from NAS thread in noS1 and SA modes.
4. Modified UE tunnel interface naming scheme. Additional PDU sessions get oaitun_ue<id>p<pdusession_id>.

Closes #941
2025-04-17 19:53:57 +02:00
alexjiao
570c1b5639 Consider the ratio of PUSCH EPRE to DMRS EPRE when calculating PUSCH DMRS power scaling 2025-04-17 14:23:03 +02:00
Cedric Roux
b99fcdf289 fix IP address
The following error was appearing when running LTE UE:

    inet_pton(): cannot convert -64.-74.0.2 to IPv4 network address

The allocated address by the core actually is 192.182.0.2.

On my host "char" is signed, leading to the above error (192 and 182
are seen as signed).

Let's force "ip_addr" unsigned and everyone is happy.
2025-04-17 10:55:18 +02:00
Thomas Schlichter
1aa6aab120 NR UE: in nr_slot_fep() perform FO compensation in local tmp_dft_in array instead of global rxdata array
This avoids problems if nr_slot_fep() is called more than once for an OFDM symbol.
2025-04-16 17:42:11 +00:00
Thomas Schlichter
5f8aeaacc9 fix CI files for NTN LEO scenario 2025-04-16 17:00:36 +00:00
Bartosz Podrygajlo
1ab8a58a2f Refactor TUN Interface Management and Move to SDAP Layer
This commit refactors the TUN interface management by moving its creation
and configuration from the PDCP layer to the SDAP layer for UE.

Key changes include:

- Introduced `tun_generate_ue_ifname` and `tun_destroy` functions for better
  TUN interface lifecycle management.
- Added support for creating and destroying UE-specific TUN interfaces in SDAP.
- Refactored `nr_pdcp_module_init` to remove TUN-related logic and moved it
  to SDAP.
- Updated NAS and SDAP layers to handle PDU session-specific TUN interface
  creation and configuration.
- Enhanced SDAP entity management to include TUN socket handling and thread
  lifecycle.
- Updated NAS message handling to initialize default PDU session TUN
  interfaces in NOS1 mode.
- Use a single SDAP thread function for gNB and UE
- Allow noS1 mode also in SA on gNB

These changes improve modularity by isolating TUN interface management within
the SDAP layer, aligning with the 3GPP architecture, and simplifying the PDCP
layer.

Co-Authored-By: Robert Schmidt <robert.schmidt@openairinterface.org>
2025-04-16 15:03:18 +02:00
Thomas Schlichter
5ac3f59d61 NR UE: fix and improve PHY layer debugging 2025-04-16 12:59:13 +00:00
Guido Casati
a19c786600 Add regression test for NAS Registration Accept decoding
This test verifies that a NAS Registration Accept message, as received
from the OAI CN5G, is correctly decoded and matches the expected output
structure. It ensures that future changes to the NAS decoder do not
break compatibility or correctness with existing decoding behavior.
2025-04-16 09:44:59 +02:00
Guido Casati
3fe601fa6b Add equality check to NAS Registration Accept library 2025-04-15 19:54:23 +02:00
Guido Casati
73b89726ad Remove harcoded Slice Differentiator (SD) in NAS decode_nssai_ie and update the processing logic
The SD stored in the allowed S-NSSAI list was hardcoded in the decoding
function, however this IE is not mandatory but optional, therefore
the check of the decoded message contents should take into account
this and check the stored value against the one from the UICC only when the field is present.
If no SD was received, consider the UICC SD a valid one.

Also, add free function to Registration Accept library and adopt in stack to free NAS
Registration Accept after use: heap memory is allocated for this message during decoding
and shall be free after processing.
2025-04-15 19:54:23 +02:00
Guido Casati
c6271a23e4 Add number of slices to decoding of NSSAI 2025-04-15 19:54:23 +02:00
Guido Casati
51661bd83c Cleanup and other fixes to decode_registration_accept
* adopt byte array
* add check on minimum buffer length
* improve comments
* remove call to LOG utility functions
* replace magic numbers with defines
* refined includes
2025-04-15 19:54:23 +02:00
Guido Casati
5c4595e699 Refactor 5GS registration result enc/dec functions
* adopt byte_array
* move enc/dec functions to Registration Accept library
* simplify code
* log result received in Registration Accept
2025-04-15 19:54:19 +02:00
Raghavendra Dinavahi
bbcb31f64b Added description for do_SINR 2025-04-15 16:02:34 +02:00
Raghavendra Dinavahi
1666dced3e gNB changes- Extract SSB-SINR from received CSI report 2025-04-15 16:02:34 +02:00
Raghavendra Dinavahi
516a618816 UE changes- Generate SSB based SINR CSI report 2025-04-15 16:02:34 +02:00
Raghavendra Dinavahi
6e371b9e48 UE changes in PHY- Enable SSB based SINR measurement 2025-04-15 16:02:34 +02:00
Raghavendra Dinavahi
61778f3d55 gNB changes- Compute bitlen in case of SSB-SINR
compute SINR bitlen according to 38.212 table 6.3.1.1.2-6A
2025-04-15 16:02:34 +02:00
Raghavendra Dinavahi
2c164336bc gNB changes in RRC- Enable SSB-SINR based CSI reporting
3GPP 38.331 release 16 specifies that If the field reportQuantity-r16 is present, UE shall ignore reportQuantity (without suffix).
reportQuantity-r16 configures SSB based SINR as the reporting quantity.

set do_SINR = 1; in gNB params part of the config file to enable SSB based SINR reporting instead of the default RSRP based.
2025-04-15 16:02:34 +02:00
rmagueta
9e787a0ba2 Bugfix for number of PRBs in PUCCH format 2 2025-04-15 14:49:19 +01:00
Guido Casati
a48e64f6f8 Adopt snprintf across the code in openair2/GNB_APP/gnb_config.c
Replaced `sprintf` with `snprintf` in gnb_config.c to enhance memory safety
by preventing potential buffer overflows. `sprintf` can cause buffer overflows
by not checking the destination buffer size. In contrast, `snprintf` allows
specifying a maximum buffer size, ensuring no overflow occurs by limiting
the number of characters written, including the null terminator.
2025-04-15 15:28:36 +02:00
Guido Casati
7ee6feac42 Use config module macros in openair2/E1AP/e1ap_setup.c 2025-04-15 15:28:36 +02:00
Guido Casati
e6da64b1a3 Refactor RCconfig_NR_NG
* use macros from config module
* cleanup irrelevant code
2025-04-15 15:28:36 +02:00
Guido Casati
bf6dff025b Cleanup obsolete code in gNB_app_task 2025-04-15 15:28:36 +02:00
Guido Casati
65e9ad7fdf Move get_node_type after init_gNB 2025-04-15 15:28:36 +02:00
Guido Casati
33ad0f917e Cleanup obsolete commented out code in gnb_config.h 2025-04-15 15:28:36 +02:00
Guido Casati
54990c2bc1 Refactor includes in executables/nr-gnb.c
* used IWYU tools + manual review
* output from IWYU:

The full include-list for /code/executables/nr-gnb.c:
/#include <PHY/NR_ESTIMATION/nr_ul_estimation.h>           // for dump_nr_I0_...
/#include <common/utils/LOG/log.h>  // lines 39-39
/#include <common/utils/system.h>                          // for threadCreate
/#include <fcntl.h>                                        // for SEEK_SET
/#include <openair1/PHY/NR_TRANSPORT/nr_dlsch.h>           // for dump_pdsch_...
/#include <openair1/PHY/NR_TRANSPORT/nr_ulsch.h>           // for dump_pusch_...
/#include <pthread.h>                                      // for pthread_join
/#include <stdint.h>                                       // for int64_t
/#include <stdio.h>                                        // for NULL, fclose
/#include <string.h>                                       // for memset
/#include <time.h>                                         // for clock_gettime
/#include <unistd.h>                                       // for sleep, size_t
/#include "NR_PHY_INTERFACE/NR_IF_Module.h"                // for NR_UL_IND_t
/#include "PHY/INIT/nr_phy_init.h"                         // for init_DLSCH_...
/#include "PHY/MODULATION/nr_modulation.h"                 // for apply_nr_ro...
/#include "PHY/NR_TRANSPORT/nr_transport_proto.h"          // for reset_activ...
/#include "PHY/TOOLS/tools_defs.h"                         // for c16_t
/#include "PHY/defs_RU.h"                                  // for RU_t, proce...
/#include "PHY/defs_common.h"                              // for wait_sync
/#include "PHY/defs_gNB.h"                                 // for PHY_VARS_gNB
/#include "PHY/defs_nr_common.h"                           // for NR_DL_FRAME...
/#include "PHY/impl_defs_nr.h"                             // for NR_MIXED_SLOT
/#include "SCHED_NR/fapi_nr_l1.h"                          // for nr_schedule...
/#include "SCHED_NR/phy_frame_config_nr.h"                 // for nr_slot_select
/#include "SCHED_NR/sched_nr.h"                            // for L1_nr_prach...
/#include "T.h"                                            // for T3, T_PUT_p...
/#include "assertions.h"                                   // for AssertFatal
/#include "common/ran_context.h"                           // for RAN_CONTEXT_t
/#include "common/utils/LOG/log.h"                         // for NR_PHY, LOG_D
/#include "executables/softmodem-common.h"                 // for get_softmod...
/#include "nfapi/oai_integration/vendor_ext.h"             // for NFAPI_MODE
/#include "nfapi_nr_interface_scf.h"                       // for nfapi_nr_co...
/#include "notified_fifo.h"                                // for initNotifie...
/#include "thread-pool.h"                                  // for abortTpool
/#include "time_meas.h"                                    // for print_meas_log
/#include "utils.h"                                        // for calloc_or_fail
2025-04-15 15:28:36 +02:00
Guido Casati
1893974d1d Set defint64val in long conf params 2025-04-15 15:28:36 +02:00
Guido Casati
7c0dc93087 Add function to set antenna ports in gNB config
* reduce redundant code
2025-04-15 15:28:36 +02:00
Guido Casati
2e7fa8e8b1 New function to set PLMN in gNB config
* reduce redundant code repeated in multiple places in the file
2025-04-15 15:28:36 +02:00
Guido Casati
1cdd684e24 Use plmn_id_t in RRC struct 2025-04-15 15:28:36 +02:00
Guido Casati
ac3f9f3e32 Introduce platform type plmn_id_t for consistency across the stack
* F1AP, NGAP can now use the same struct type
* this improves maintainability
2025-04-15 15:28:34 +02:00
Guido Casati
c1f225abc1 Add set_snssai_config to replace redundant code in gnb_config.c 2025-04-15 15:27:39 +02:00
Guido Casati
2ee056bf45 Add definition for maximum number of slices according to 3GPP specs 2025-04-15 15:27:39 +02:00
Guido Casati
34fc1bb254 Set num_nssai to uint16_t in NGAP for consistency with other definitions 2025-04-15 15:27:39 +02:00
Guido Casati
7731f5bd2d Introduce macros to get parameters in gnb_config.c
* this commit introduces macro to reduce redundant code in gNB configuration
2025-04-15 15:27:38 +02:00
Guido Casati
49b3e2ee69 Cleanup commented out code in gnb_config.c and other minor formatting 2025-04-15 15:25:53 +02:00
Robert Schmidt
f3eb713084 Merge branch 'integration_2025_w15' into 'develop'
Integration: `2025.w15`

Closes #901, #943, #944, #942, and #939

See merge request oai/openairinterface5g!3374

* !3358 RedCap InitialBWP
* !3364 NR UE: fix PRACH generation with frequency resource id not 0 when msg1-FDM > 1 in SIB1
* !3365 nr pdcp: 'count' is 32 bits, use explicit type instead of generic 'int'
* !3366 Fix typo in gNB_scheduler_uci.c
* !3370 Doc: add information on how to avoid a common asan bug
* !3275 NAS service reject
* !3312 NR UE: enable concurrent UL slot processing
* !3368 Encapsulate cmake asn1c call, suppress useless output
* !3336 Fix logging in nr_dlsim
* !3281 Add support for 'dl_min_mcs' and 'ul_min_mcs' configuration parameters
* !3355 NR UE: add support for continuous frequency offset compensation (in particular NTN LEO Doppler)
* !3375 CI CoreNetwork fixes
* !3184 NR SIB2
* !3348 CI: Replacing Devkit server with a Grace Hopper in Aerial pipeline
2025-04-15 05:54:26 +00:00
Robert Schmidt
b790fb235d Add SIB2 and SIB19 to NTN-LEO config 2025-04-14 21:29:34 +02:00
Robert Schmidt
e085fb5fee Merge remote-tracking branch 'origin/aerial_to_gh' into integration_2025_w15 (!3348)
CI: Replacing Devkit server with a Grace Hopper in Aerial pipeline

Switch servers on the Aerial pipeline from Gigabyte (AMD) to Grace
Hopper (ARM), and make the necessary changes:

- Update XML, YAML, and gnb.conf
- Change the argument passed to the L1 in aerial_l1_entrypoint.sh to be
  compatible with the GH
- Update the Aerial setup in TESTBench.md and Aerial_FAPI_Split_Tutorial.md docs
- Decrease the tested throughput due to an issue with the RU. Will open
  another MR to increase it once the problem is fixed.
2025-04-14 18:18:18 +02:00
Robert Schmidt
98d4dff509 Merge remote-tracking branch 'origin/NR_gNB_SIB2' into integration_2025_w15 (!3184)
NR SIB2

- Transmission of SIB2
- Fixes for transmission and reception of SIB19

Closes #901
2025-04-14 17:01:28 +02:00
Robert Schmidt
5c5dbdf2d3 Merge remote-tracking branch 'origin/ci-refactor-cn-fixes' into integration_2025_w15 (!3375)
CI CoreNetwork fixes

Some minor fixes after !3322
2025-04-14 17:00:59 +02:00
Robert Schmidt
07f8158378 Merge remote-tracking branch 'origin/ntn_leo_freq_comp' into integration_2025_w15 (!3355)
NR UE: add support for continuous frequency offset compensation (in particular NTN LEO Doppler)

- move function add_noise() to file
  openair1/SIMULATION/TOOLS/phase_noise.c
- refactor (co)sinus calculation out of phase_noise() into get_sin_cos()
  in nr_common.c
- NR UE: continuously estimate the frequency offset using PBCH DMRS
- NR UE: add command line parameter --initial-fo to specify known
  initial frequency offset
- NR UE: add support for continuous DL frequency offset compensation
  --cont-fo-comp
- NR UE: add support for continuous UL frequency offset
  pre-compensation. Depending on the main source of frequency offset,
  pre-compensate the UL frequency offset (FO) accordingly. Therefore
  modify the flag --cont-fo-comp to accept an integer value, where
  * 1 specifies, that the main FO contribution comes from the accuracy of the local oscillator (LO)
  * 2 specifies, that the main FO contribution comes from Doppler shift
- NR UE: add PI controller to continuous frequency offset compensation
- rfsimulator: enable and correct NTN LEO doppler simulation
- use SIMD to speed up nr_fo_compensation()
- Extend NTN LEO example in RUNMODEM.md to include continuous Doppler
  compensation
- NR UE: in nr_adjust_synch_ue() extend the peak search to +/- cyclic
  prefix size, esp. for LEO NTN scenarios the timing drifts during
  initial sync, which is mostly compensated with
  --ntn-initial-time-drift. But there might be a residual time offset,
  and by extending the peak search window we make this more tolerant.
- NR UE: remove unused nr_slot_tx_offset, it is unused since commit
  f3e60a14
- NR UE: remove now unnecessary local variable
  update_ntn_system_information, directly use
  UE->ntn_config_message->update instead
2025-04-14 16:59:27 +02:00
Reem Bahsoun
42a4292149 Modify ci_infra.yaml and XML to start CN from Grace Hopper 2025-04-14 11:39:13 +02:00
Reem Bahsoun
b3a1e3d2e8 Doc update
- Update the Aerial tutorial to include Grace Hopper server and add reference to Gigabyte server setup instructions.
- Update the Aerial setup in the TESTBench.md
2025-04-14 11:39:08 +02:00
francescomani
7c4eabdb6e fix for DLSCH DMRS reference point (regression at the OAI UE introduced when testing with OAI gNB for a missing implementation in OAI gNB) 2025-04-14 11:28:00 +02:00
Robert Schmidt
75b237fb90 Add 5G RFsim NTN LEO CI Test 2025-04-14 10:45:40 +02:00
Robert Schmidt
890a6c702e Remove unnecessary fields in NTN-LEO gNB config 2025-04-14 10:42:10 +02:00
francescomani
9185c29e33 change ss0 configuration to schedule multiple sibs in ntn config files with small bw 2025-04-14 10:15:50 +02:00
Robert Schmidt
f3724ec451 Add SIBs in configuration doc and feature set 2025-04-14 10:15:50 +02:00
Robert Schmidt
e73f1f6220 F1AP: Correctly encode DU configuration update acknowledge SI container
Fixes: b91e1b0a12 ("Add enc/dec lib for F1AP gNB-DU Configuration
Update Acknowledge (Interface Management)")
2025-04-14 10:15:50 +02:00
Robert Schmidt
463942d90e F1AP: correctly encode SI containers
- encode all SI containers the encoding function gets (it's not the
  encoding function's job to enforce semantics)
-

Fixes: 30628b820c ("Add encoding/decoding lib for F1AP Setup Response
(Interface Management)")
2025-04-14 10:15:50 +02:00
Robert Schmidt
7edf52ca5b Use cp_f1ap_() functions to properly copy F1 messages 2025-04-14 10:15:50 +02:00
francescomani
a9e9dfbd06 assertions for wrong node type SIB input 2025-04-14 10:15:50 +02:00
francescomani
3b22c4a566 Revert tentative fix for double sib1 decoding because it doesn't behave as expected. Instead a new pending_sib variable is used for the same goal.
This reverts commit 35e9d1e0c2.
2025-04-14 10:15:50 +02:00
francescomani
2228c3966f enabling configuration of transmitted SIBs in NTN config file 2025-04-14 10:15:50 +02:00
francescomani
08da0bd2cc improvements at UE to receive multiple instances of otherSIB
for example SIB2 and SIB19 which are scheduled in separate messages
2025-04-14 10:15:50 +02:00
francescomani
e4cb8be023 do not schedule reception of DLSCH associated with DCI for SIB1 if no SIB1 is scheduled for reception
in this scenario we proably were scheduling reception of otherSIB in the same slot as SIB1 was present
2025-04-14 10:15:50 +02:00
francescomani
97ca739be2 remove hardcoded window size for otherSI in UE 2025-04-14 10:15:50 +02:00
francescomani
5e8f649963 check multiple DCI candidates with same length if SIB RNTI 2025-04-14 10:15:50 +02:00
francescomani
c633fa5855 increase number of candidates for otherSIB 2025-04-14 10:15:50 +02:00
francescomani
6af70bd4b9 fix buggy TBS function for SIB and set additional candidate for common search space 2025-04-14 10:15:50 +02:00
francescomani
04213f2981 schedule SIBs other than 1 (including fixes for SIB19) 2025-04-14 10:15:50 +02:00
francescomani
535d8e7765 other SIBs scheduling configuration in SIB1 2025-04-14 10:15:45 +02:00
Robert Schmidt
556396e56e Ping: use cn name when checking for CN IP address 2025-04-12 11:17:22 +02:00
Robert Schmidt
135b522cf4 CoreNetwork: do not look up IP for CN name printing
In various places, e.g., during initialization (before deployment), we
use the name of the CN object, previously including the IP address.
However, the IP might not be available (because the CN is not deployed),
so we would get warnings at this stage. Avoid this by not looking up the
IP.

There is now one place where we have to look up the IP address
separately.

Also, the exception in initialization might be misleading if we used
{self}, because we complain about no node being present, but the {self}
would include a non-existing node name => do not use it.
2025-04-12 11:17:22 +02:00
Robert Schmidt
cb175569eb Merge remote-tracking branch 'origin/xl_min_mcs' into integration_2025_w15 (!3281)
Add support for 'dl_min_mcs' and 'ul_min_mcs' configuration parameters

Add support for 'dl_min_mcs' and 'ul_min_mcs' configuration parameters,
they will enforce the gNB scheduler to select always a MCS equal or
above its values

In addition to use a min value for MCS, this MR also enables the testing
of a specific MCS value with COTS UE, by setting

    xl_min_mcs = xl_max_mcs

example:

    dl_min_mcs: 22
    dl_max_mcs: 22
    ul_min_mcs: 7
    ul_max_mcs: 7

In the example above, the scheduler will use always MCS 22 for downlink
and MCS 7 for uplink.
2025-04-12 11:03:56 +02:00
Robert Schmidt
ec65c7cbaa Merge remote-tracking branch 'origin/nr_dlsim_improve_logging' into integration_2025_w15 (!3336)
Fix logging in nr_dlsim

Added Python helper script to plot buffer data
2025-04-12 11:03:25 +02:00
Robert Schmidt
e7b46342d5 Merge remote-tracking branch 'origin/asn1c-default-no-output' into integration_2025_w15 (!3368)
Encapsulate the asn1c call from cmake in a cmake function run_asn1c().
As a notable change to the previous manual add_custom_command()
invocation, in suppresses the output of asn1c by default [1] and stores
it in a log file. In case of asn1c4 error, the output will output on
stdout.

Use the new run_asn1c() in all occasions for code generation.

[1] it is quite verbose, with many warnings that we cannot influence nor
suppress, and lists all generated files which is verified by the build
system automatically, anyway.
2025-04-11 16:29:27 +02:00
Robert Schmidt
26dcb876f5 Merge remote-tracking branch 'origin/nr-ue-slot-tx-cleanup' into integration_2025_w15 (!3312)
NR UE: enable concurrent UL slot processing

This commit introduces support for concurrent UL slot processing
Key changes:

- Increased number of UL actors from 1 to 2
- fix several issues related to writing to global variables inside processSlotTX
- Ensure in-order execution of if_module calls by utilizing the preexisting dynamic_barrier structure.

Note: for NTN, concurrent UL slot processing does not work. A flag
`--num-ul-actors X` has been introduced that allows to fix the number of
UL actors to `X` (default is 2).
2025-04-11 16:26:41 +02:00
Robert Schmidt
bc5f2ec99e Merge remote-tracking branch 'origin/nr-ue-nas-service-reject' into integration_2025_w15 (!3275)
Add 5GMM Service Reject enc/dec library and unit test

- introduce the library for the NAS Service Reject enc/dec
- add relevant unit test
- add handler
2025-04-11 16:25:52 +02:00
Sakthivel Velumani
d334a75c8a Fix memory leak and possible crash
nr_dlsim allocates memory on every SNR iteration without freeing it.
This can lead to memory exhaustion. Fix this by allocating only once
and reuse memory. Also, free it before exit.
2025-04-11 09:46:55 -04:00
Sakthivel Velumani
4adadedeb9 Fix logging to matlab/bin file
Add Python script to plot logged buffers. Modified write_file_matlab()
to add .bin extension if binary format is selected.
2025-04-11 09:46:50 -04:00
francescomani
4b23c7ea65 F1 transmission from CU to DU of SIB2 2025-04-11 15:37:08 +02:00
Chieh-Chun Chen
49d302960c Fix: modify block_length to Kprime 2025-04-11 14:40:52 +02:00
Robert Schmidt
2ab747eaa6 CI CoreNetwork: define function getName()
Some functionality (e.g., in cls_oaicitest.py) uses cn.getName(). Define
that function to avoid assertions.
2025-04-11 14:16:49 +02:00
Robert Schmidt
4296bfe34f Merge remote-tracking branch 'origin/info-asan-bug' into integration_2025_w15 (!3370)
Doc: add information on how to avoid a common asan bug

Closes #943
2025-04-11 07:38:55 +02:00
Robert Schmidt
b381d7ffae Merge remote-tracking branch 'origin/944-typo-in-gnb_scheduler_uci-c' into integration_2025_w15 (!3366)
Fix typo in gNB_scheduler_uci.c

Closes #944
2025-04-11 07:38:29 +02:00
Robert Schmidt
b9f5746df4 Merge remote-tracking branch 'origin/fix-count-nr-pdcp' into integration_2025_w15 (!3365)
nr pdcp: 'count' is 32 bits, use explicit type instead of generic 'int'

fix #942
2025-04-11 07:38:02 +02:00
Robert Schmidt
9e4aefda6b Merge remote-tracking branch 'origin/issue-939' into integration_2025_w15 (!3364)
NR UE: fix PRACH generation with frequency resource id not 0 when msg1-FDM > 1 in SIB1

When msg1-FDM > 1 (2, 4 or 8) in SIB1, the frequency resource id of
PRACH could be not 0, current PRACH generation only considers frequency
resource id = 0. This MR fixes it, verified with 3rd-party gNB.

Closes #939
2025-04-11 07:37:33 +02:00
Robert Schmidt
8d294d71b8 Merge remote-tracking branch 'origin/RedCap_InitialBWP' into integration_2025_w15 (!3358)
RedCap InitialBWP

Prevent RedCap UEs to move to DedicatedBWP

  This allows the coexistence of RedCap UEs and non-RedCap UEs in the
  same cell by having an InitialBWP with 20MHz and a larger
  DedicatedBWP.

Update example RedCap configuration file to have 20 MHz InitialBWP and
40 MHz DedicatedBWP
2025-04-11 07:36:51 +02:00
Thomas Schlichter
67482f5d85 CI: add NR UE conf file including NTN LEO channel model 2025-04-10 14:36:01 +00:00
Thomas Schlichter
6e06c817f4 NR UE: remove now unnecessary local variable update_ntn_system_information
directly use `UE->ntn_config_message->update` instead
2025-04-10 13:56:20 +00:00
Thomas Schlichter
997790f656 NR UE: remove unused nr_slot_tx_offset
`nr_slot_tx_offset` is unused since commit f3e60a1417
2025-04-10 13:56:20 +00:00
Thomas Schlichter
a9132a2f79 NR UE: in nr_adjust_synch_ue() extend the peak search to +/- cyclic prefix size
esp. for LEO NTN scenarios the timing drifts during initial sync, which is mostly compensated with --ntn-initial-time-drift.
But there might be a residual time offset, and by extending the peak search window we make this more tolerant.
2025-04-10 13:56:20 +00:00
Thomas Schlichter
4e3a7ad56e Extend NTN LEO example in RUNMODEM.md to include continuous Doppler compensation 2025-04-10 13:56:20 +00:00
Thomas Schlichter
29ed144764 use SIMD to speed up nr_fo_compensation() 2025-04-10 13:56:20 +00:00
Thomas Schlichter
d6585f3750 rfsimulator: enable and correct NTN LEO doppler simulation 2025-04-10 13:56:20 +00:00
Thomas Schlichter
18469302d8 NR UE: add PI controller to continuous frequency offset compensation 2025-04-10 13:56:20 +00:00
Thomas Schlichter
84712f5f92 NR UE: add support for continuous UL frequency offset pre-compensation
Depending on the main source of frequency offset, pre-compensate the UL frequency offset (FO) accordingly.
Therefore modify the flag `--cont-fo-comp` to accept an integer value, where
 - `1` specifies, that the main FO contribution comes from the accuracy of the local oscillator (LO)
 - `2` specifies, that the main FO contribution comes from Doppler shift
2025-04-10 13:56:13 +00:00
Thomas Schlichter
aac4642910 NR UE: add support for continuous DL frequency offset compensation (--cont-fo-comp) 2025-04-10 13:54:59 +00:00
Thomas Schlichter
616568a79d NR UE: add command line parameter --initial-fo to specify known initial frequency offset 2025-04-10 13:54:50 +00:00
Guido Casati
312f296b60 Add NAS handler for Service Reject message 2025-04-10 15:35:59 +02:00
Guido Casati
2b9782caa1 Add unit test for Service Reject 2025-04-10 15:35:59 +02:00
Guido Casati
5af4b89203 Add enc/dec function for NAS Service Reject library 2025-04-10 15:35:55 +02:00
Thomas Schlichter
d16507055f NR UE: continuously estimate the frequency offset using PBCH DMRS 2025-04-10 12:37:19 +00:00
Thomas Schlichter
dd827c7bd8 refactor (co)sinus calculation out of phase_noise() into openair1/PHY/TOOLS/get_sin_cos.c 2025-04-10 12:37:19 +00:00
Thomas Schlichter
9e4df97d84 move function add_noise() to file openair1/SIMULATION/TOOLS/phase_noise.c 2025-04-10 12:36:30 +00:00
luis_pereira87
f1811b5934 Move the initialization of bler_stats to 'add_new_nr_ue()' function 2025-04-10 13:18:29 +01:00
Bartosz Podrygajlo
afd49e703c Workaround for issues with NTN and NR UE concurrent UL processing
- Allow setting number of used UL actors in NR UE
 - Disable the second UL actor in NTN GEO testcase
2025-04-10 13:11:00 +02:00
Bartosz Podrygajlo
2d35fc90ac NR UE: enable concurrent UL slot processing
This commit introduces support for concurrent UL slot processing

Key changes:
 - Increased number of UL actors from 1 to 2
 - fix several issues related to writing to global variables inside
   processSlotTX
 - Ensure in-order execution of if_module calls by utilizing the
   preexisting dynamic_barrier structure.
2025-04-10 12:47:54 +02:00
luis_pereira87
b421d6eaa9 Set 'min_mcs' in 'bler_stats' when initializing and when no activity is detected, instead of hardcode it to '9' 2025-04-10 11:20:59 +01:00
Guido Casati
25bc1bd89a Adopt eq_gprs_timer in eq_service_accept 2025-04-10 11:13:42 +02:00
Guido Casati
ab3bb36357 Add eq_gprs_timer to common 5gmm_lib 2025-04-10 11:13:42 +02:00
luis_pereira87
699afafc6f Add support for 'dl_min_mcs' and 'ul_min_mcs' configuration parameters, they will enforce the gNB scheduler to select always a MCS equal or above its values 2025-04-10 09:11:17 +01:00
luis_pereira87
96b0cae63e Drop 'min_grant_mcs' configuration parameter 2025-04-10 09:11:17 +01:00
Robert Schmidt
b9a6d93bfa Doc: add information on how to avoid a common asan bug 2025-04-10 09:06:57 +02:00
Robert Schmidt
05d393a399 Encapsulate cmake asn1c call, suppress useless output
Encapsulate the asn1c call from cmake in a cmake function run_asn1c().
As a notable change to the previous manual add_custom_command()
invocation, in suppresses the output of asn1c by default [1] and stores
it in a log file. In case of asn1c4 error, the output will output on
stdout.

Use the new run_asn1c() in all occasions for code generation.

[1] it is quite verbose, with many warnings that we cannot influence nor
suppress, and lists all generated files which is verified by the build
system automatically, anyway.
2025-04-09 14:35:29 +00:00
alexjiao
683670ed91 Fix PRACH generation with frequency resource id not 0 when msg1-FDM > 1 in SIB1 2025-04-09 20:06:41 +08:00
Reem Bahsoun
52f0bbc165 Modify files in ci-scripts/yaml_files/sa_gnb_aerial
- Update the L1 (cuBB) version in the docker-compose.yaml
- Change the argument for the L1 config file (cuphycontroller.yaml) in the aerial_l1_entrypoint.sh
2025-04-09 10:21:46 +02:00
Reem Bahsoun
8e253e3435 Modify XML and gnb.conf
- Update the N2 and N3 IP addresses to match the Grace Hopper's in the gnb.conf.
- Remove the test XML file for pulling images in the ARM image builder.
- Update the Aerial XML file to pull ARM images.
- Decrease tested throughput (temporarily) due to problems with Foxconn RU
2025-04-09 10:21:37 +02:00
francescomani
bc27d1e691 improvements in SIB2 message (info taken from commercial gNB) 2025-04-08 18:09:18 +02:00
Justin Garey
0f9220b5ef Fixed typo in gNB_schedyler_uci.c 2025-04-08 15:30:52 +00:00
Cedric Roux
6504c038b2 nr pdcp: 'count' is 32 bits, use explicit type instead of generic 'int' 2025-04-08 09:41:36 +02:00
ndomingues
f24a576d81 Update RedCap configuration file to have 20 MHz InitialBWP and 40 MHz DedicatedBWP 2025-04-04 21:43:19 +01:00
ndomingues
a59cfae18c Prevent RedCap UEs to move to dedicated BWP 2025-04-04 21:43:19 +01:00
210 changed files with 7507 additions and 2940 deletions

View File

@@ -823,6 +823,7 @@ set(PHY_SRC_COMMON
${OPENAIR1_DIR}/PHY/TOOLS/sqrt.c
${OPENAIR1_DIR}/PHY/TOOLS/lut.c
${OPENAIR1_DIR}/PHY/TOOLS/simde_operations.c
${OPENAIR1_DIR}/PHY/TOOLS/get_sin_cos.c
)
set(PHY_SRC
@@ -1834,6 +1835,7 @@ target_link_libraries(nr-softmodem PRIVATE
ITTI ${NAS_UE_LIB} lte_rrc nr_rrc
ngap s1ap L2_LTE_NR L2_NR MAC_NR_COMMON NFAPI_LIB NFAPI_VNF_LIB NFAPI_PNF_LIB NFAPI_USER_LIB SIMU
x2ap f1ap m2ap m3ap e1ap shlib_loader
time_management
-Wl,--end-group z dl)
target_link_libraries(nr-softmodem PRIVATE pthread m CONFIG_LIB rt sctp)
@@ -1866,6 +1868,7 @@ add_executable(nr-cuup
target_link_libraries(nr-cuup PRIVATE
CONFIG_LIB ITTI SCTP_CLIENT
GTPV1U e1ap f1ap
time_management
z sctp dl pthread shlib_loader ${T_LIB})
target_link_libraries(nr-cuup PRIVATE asn1_lte_rrc_hdrs asn1_nr_rrc_hdrs)
if(E2_AGENT)
@@ -1897,6 +1900,7 @@ target_link_libraries(nr-uesoftmodem PRIVATE
nr_rrc SECURITY UTIL HASHTABLE SCHED_RU_LIB SCHED_NR_UE_LIB
PHY_COMMON PHY_NR_COMMON PHY_NR_UE NR_L2_UE MAC_NR_COMMON NFAPI_LIB
ITTI SIMU shlib_loader
time_management
-Wl,--end-group z dl)
target_link_libraries(nr-uesoftmodem PRIVATE pthread m CONFIG_LIB rt nr_ue_phy_meas)

View File

@@ -77,8 +77,8 @@ oc-cn5g-20897:
Undeploy: "! scripts/oc-cn5g-undeploy.sh /opt/oai-cn5g-fed-develop-2025-jan oaicicd-core-for-fhi72"
LogCollect: "! scripts/oc-cn5g-logcollect.sh /opt/oai-cn5g-fed-develop-2025-jan oaicicd-core-for-fhi72 %%log_dir%%"
oc-cn5g-20897-aerial2:
Host: aerial2
oc-cn5g-20897-aerial:
Host: gracehopper1-oai
NetworkScript: echo "inet 172.21.6.105"
RunIperf3Server: False
Deploy: "! scripts/oc-cn5g-deploy.sh /opt/oai-cn5g-fed-develop-2025-jan oaicicd-core-for-nvidia-aerial"

View File

@@ -49,7 +49,7 @@ class CoreNetwork:
self._host = c.get('Host').strip()
if self._host == "%%current_host%%":
if node is None:
raise Exception(f"core network {self} requires node, but none provided (cannot replace %%current_host%%)")
raise Exception(f"core network {cn_name} requires node, but none provided (cannot replace %%current_host%%)")
self._host = node
if d is not None:
raise Exception("directory handling not implemented")
@@ -66,7 +66,7 @@ class CoreNetwork:
logging.info(f'initialized core {self} from {filename}')
def __str__(self):
return f"{self._cn_name}@{self._host} [IP: {self.getIP()}]"
return f"{self._cn_name}@{self._host}"
def __repr__(self):
return self.__str__()
@@ -162,6 +162,9 @@ class CoreNetwork:
def getCmdPrefix(self):
return self._cmd_prefix or ""
def getName(self):
return self._cn_name
def getHost(self):
return self._host

View File

@@ -464,7 +464,7 @@ class OaiCiTest():
return (False, f"UE {ue.getName()} has no IP address")
svrIP = cn.getIP()
if not svrIP:
return (False, f"Iperf server {ue.getName()} has no IP address")
return (False, f"Iperf server {cn.getName()} has no IP address")
iperf_opt = self.iperf_args
jsonReport = "--json"
@@ -862,7 +862,7 @@ class OaiCiTest():
success, output = cn.deploy()
logging.info(f"deployment core network {core_name} success {success}, output:\n{output}")
if success:
msg = f"Started {cn}"
msg = f"Started {cn} [{cn.getIP()}]"
HTML.CreateHtmlTestRowQueue(core_name, 'OK', [msg])
else:
msg = f"deployment of core network {core_name} FAILED"

View File

@@ -186,8 +186,8 @@ gNBs =
NETWORK_INTERFACES :
{
GNB_IPV4_ADDRESS_FOR_NG_AMF = "172.21.16.131";
GNB_IPV4_ADDRESS_FOR_NGU = "172.21.16.131";
GNB_IPV4_ADDRESS_FOR_NG_AMF = "172.21.16.202";
GNB_IPV4_ADDRESS_FOR_NGU = "172.21.16.202";
GNB_PORT_FOR_S1U = 2152; # Spec 2152
};

View File

@@ -20,6 +20,8 @@ gNBs =
min_rxtxtime = 6;
num_dlharq = 32;
num_ulharq = 32;
cu_sibs = [2];
du_sibs = [19];
servingCellConfigCommon = (
{
@@ -49,7 +51,7 @@ gNBs =
initialDLBWPsubcarrierSpacing = 0;
#pdcch-ConfigCommon
initialDLBWPcontrolResourceSetZero = 2;
initialDLBWPsearchSpaceZero = 0;
initialDLBWPsearchSpaceZero = 2;
#uplinkConfigCommon
#frequencyInfoUL
@@ -149,7 +151,7 @@ gNBs =
#ext2
#ntn_Config_r17
cellSpecificKoffset_r17 = 40;
ta-Common-r17 = 4634000; # 18.87 ms
ta-Common-r17 = 4627000; # 18.84 ms
ta-CommonDrift-r17 = -230000; # -46 µs/s
positionX-r17 = 0;
positionY-r17 = -2166908; # -2816980.4 m
@@ -235,15 +237,6 @@ RUs = (
}
);
THREAD_STRUCT = (
{
#three config for level of parallelism "PARALLEL_SINGLE_THREAD", "PARALLEL_RU_L1_SPLIT", or "PARALLEL_RU_L1_TRX_SPLIT"
parallel_config = "PARALLEL_RU_L1_TRX_SPLIT";
#two option for worker "WORKER_DISABLE" or "WORKER_ENABLE"
worker_config = "WORKER_DISABLE";
}
);
rfsimulator :
{
serveraddr = "server";
@@ -296,6 +289,4 @@ log_config :
rlc_log_level ="info";
pdcp_log_level ="info";
rrc_log_level ="info";
ngap_log_level ="debug";
f1ap_log_level ="debug";
};

View File

@@ -19,6 +19,8 @@ gNBs =
sib1_tda = 5;
min_rxtxtime = 6;
disable_harq = 1;
cu_sibs = [2];
du_sibs = [19];
servingCellConfigCommon = (
{
@@ -48,7 +50,7 @@ gNBs =
initialDLBWPsubcarrierSpacing = 0;
#pdcch-ConfigCommon
initialDLBWPcontrolResourceSetZero = 2;
initialDLBWPsearchSpaceZero = 0;
initialDLBWPsearchSpaceZero = 2;
#uplinkConfigCommon
#frequencyInfoUL
@@ -233,15 +235,6 @@ RUs = (
}
);
THREAD_STRUCT = (
{
#three config for level of parallelism "PARALLEL_SINGLE_THREAD", "PARALLEL_RU_L1_SPLIT", or "PARALLEL_RU_L1_TRX_SPLIT"
parallel_config = "PARALLEL_RU_L1_TRX_SPLIT";
#two option for worker "WORKER_DISABLE" or "WORKER_ENABLE"
worker_config = "WORKER_DISABLE";
}
);
rfsimulator :
{
serveraddr = "server";
@@ -277,6 +270,4 @@ log_config :
rlc_log_level ="info";
pdcp_log_level ="info";
rrc_log_level ="info";
ngap_log_level ="debug";
f1ap_log_level ="debug";
};

View File

@@ -0,0 +1,35 @@
uicc0 = {
imsi = "208990100001100";
key = "fec86ba6eb707ed08905757b1bb44b8f";
opc= "C42449363BBAD02B66D16BC975D77CC1";
dnn= "oai";
nssai_sst=1;
}
position0 = {
x = 0.0;
y = 0.0;
z = 6377900.0;
}
thread-pool = "-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1"
#/* configuration for channel modelisation */
#/* To be included in main config file when */
#/* channel modelisation is used (rfsimulator with chanmod options enabled) */
channelmod = {
max_chan = 10;
modellist = "modellist_rfsimu_1";
modellist_rfsimu_1 = (
{ # DL, modify on UE side
model_name = "rfsimu_channel_enB0"
type = "SAT_LEO_TRANS";
noise_power_dB = -100;
},
{ # UL, modify on gNB side
model_name = "rfsimu_channel_ue0"
type = "SAT_LEO_TRANS";
noise_power_dB = -100;
}
);
};

View File

@@ -0,0 +1,118 @@
<!--
Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The OpenAirInterface Software Alliance licenses this file to You under
the OAI Public License, Version 1.1 (the "License"); you may not use this file
except in compliance with the License.
You may obtain a copy of the License at
http://www.openairinterface.org/?page_id=698
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
For more information about the OpenAirInterface (OAI) Software Alliance:
contact@openairinterface.org
-->
<testCaseList>
<htmlTabRef>rfsim-5gnr-ntn-Leo</htmlTabRef>
<htmlTabName>Monolithic SA NTN LEO gNB</htmlTabName>
<htmlTabIcon>wrench</htmlTabIcon>
<TestCaseRequestedList>
111111
100001
800813
000001
000002
000003
020001
020002
100001
222222
</TestCaseRequestedList>
<TestCaseExclusionList></TestCaseExclusionList>
<testCase id="111111">
<class>Pull_Local_Registry</class>
<desc>Pull Images from Local Registry</desc>
<svr_id>0</svr_id>
<images>oai-gnb-asan oai-nr-ue-asan</images>
</testCase>
<testCase id="800813">
<class>Create_Workspace</class>
<desc>Create new Workspace</desc>
<eNB_instance>0</eNB_instance>
<eNB_serverId>0</eNB_serverId>
</testCase>
<testCase id="000001">
<class>Deploy_Object</class>
<desc>Deploy OAI 5G CoreNetwork</desc>
<yaml_path>ci-scripts/yaml_files/5g_rfsimulator_ntn_leo</yaml_path>
<services>mysql oai-amf oai-smf oai-upf oai-ext-dn</services>
<eNB_instance>0</eNB_instance>
<eNB_serverId>0</eNB_serverId>
</testCase>
<testCase id="000002">
<class>Deploy_Object</class>
<desc>Deploy OAI 5G gNB+nrUE RF sim SA</desc>
<yaml_path>ci-scripts/yaml_files/5g_rfsimulator_ntn_leo</yaml_path>
<services>oai-gnb oai-nr-ue</services>
<eNB_instance>0</eNB_instance>
<eNB_serverId>0</eNB_serverId>
</testCase>
<testCase id="000003">
<class>Attach_UE</class>
<desc>Attach OAI UE (Wait for IP)</desc>
<id>rfsim5g_ue</id>
<nodes>cacofonix</nodes>
</testCase>
<testCase id="020001">
<class>Ping</class>
<desc>Ping ext-dn from NR-UE</desc>
<id>rfsim5g_ue</id>
<nodes>cacofonix</nodes>
<svr_id>rfsim5g_ext_dn</svr_id>
<svr_node>cacofonix</svr_node>
<ping_args>-c 20 -i 0.25</ping_args>
<ping_packetloss_threshold>5</ping_packetloss_threshold>
</testCase>
<testCase id="020002">
<class>Ping</class>
<desc>Ping NR-UE from ext-dn</desc>
<id>rfsim5g_ext_dn</id>
<nodes>cacofonix</nodes>
<svr_id>rfsim5g_ue</svr_id>
<svr_node>cacofonix</svr_node>
<ping_args>-c 20 -i 0.25</ping_args>
<ping_packetloss_threshold>5</ping_packetloss_threshold>
</testCase>
<testCase id="100001">
<class>Undeploy_Object</class>
<always_exec>true</always_exec>
<desc>Undeploy all OAI 5G stack</desc>
<yaml_path>ci-scripts/yaml_files/5g_rfsimulator_ntn_leo</yaml_path>
<eNB_instance>0</eNB_instance>
<eNB_serverId>0</eNB_serverId>
</testCase>
<testCase id="222222">
<class>Clean_Test_Server_Images</class>
<always_exec>true</always_exec>
<desc>Clean Test Images on Test Server</desc>
<svr_id>0</svr_id>
<images>oai-gnb-asan oai-nr-ue-asan</images>
</testCase>
</testCaseList>

View File

@@ -44,7 +44,7 @@
<testCase id="555000">
<class>DeployCoreNetwork</class>
<desc>Initialize 5G Core</desc>
<cn_id>oc-cn5g-20897-aerial2</cn_id>
<cn_id>oc-cn5g-20897-aerial</cn_id>
</testCase>
<testCase id="111111">
@@ -52,6 +52,7 @@
<desc>Pull Images from Local Registry</desc>
<svr_id>0</svr_id>
<images>oai-gnb-aerial</images>
<tag_prefix>arm_</tag_prefix>
</testCase>
<testCase id="800813">
@@ -89,16 +90,16 @@
<class>Ping</class>
<desc>Ping: 100pings in 20sec</desc>
<id>up2-aerial</id>
<svr_id>oc-cn5g-20897-aerial2</svr_id>
<svr_id>oc-cn5g-20897-aerial</svr_id>
<ping_args>-c 100 -i 0.2</ping_args>
<ping_packetloss_threshold>5</ping_packetloss_threshold>
</testCase>
<testCase id="000022">
<class>Iperf</class>
<desc>iperf (DL/UDP/600M)(30 sec)(single-ue profile)</desc>
<iperf_args>-u -b 600M -t 30 -R</iperf_args>
<svr_id>oc-cn5g-20897-aerial2</svr_id>
<desc>iperf (DL/UDP/500M)(30 sec)(single-ue profile)</desc>
<iperf_args>-u -b 500M -t 30 -R</iperf_args>
<svr_id>oc-cn5g-20897-aerial</svr_id>
<id>up2-aerial</id>
<iperf_packetloss_threshold>25</iperf_packetloss_threshold>
<iperf_bitrate_threshold>80</iperf_bitrate_threshold>
@@ -106,9 +107,9 @@
<testCase id="000033">
<class>Iperf</class>
<desc>iperf (UL/UDP/50M)(30 sec)(single-ue profile)</desc>
<iperf_args>-u -b 50M -t 30</iperf_args>
<svr_id>oc-cn5g-20897-aerial2</svr_id>
<desc>iperf (UL/UDP/40M)(30 sec)(single-ue profile)</desc>
<iperf_args>-u -b 40M -t 30</iperf_args>
<svr_id>oc-cn5g-20897-aerial</svr_id>
<id>up2-aerial</id>
<iperf_packetloss_threshold>25</iperf_packetloss_threshold>
<iperf_bitrate_threshold>80</iperf_bitrate_threshold>
@@ -144,7 +145,7 @@
<class>UndeployCoreNetwork</class>
<always_exec>true</always_exec>
<desc>Terminate 5G Core</desc>
<cn_id>oc-cn5g-20897-aerial2</cn_id>
<cn_id>oc-cn5g-20897-aerial</cn_id>
</testCase>
</testCaseList>

View File

@@ -1,49 +0,0 @@
<!--
Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The OpenAirInterface Software Alliance licenses this file to You under
the OAI Public License, Version 1.1 (the "License"); you may not use this file
except in compliance with the License.
You may obtain a copy of the License at
http://www.openairinterface.org/?page_id=698
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
For more information about the OpenAirInterface (OAI) Software Alliance:
contact@openairinterface.org
-->
<testCaseList>
<htmlTabRef>TEST-GH-AERIAL-SA</htmlTabRef>
<htmlTabName>Gracehopper AERIAL 100 MHz TDD SA</htmlTabName>
<htmlTabIcon>tasks</htmlTabIcon>
<TestCaseRequestedList>
111111
333333
</TestCaseRequestedList>
<TestCaseExclusionList></TestCaseExclusionList>
<testCase id="111111">
<class>Pull_Local_Registry</class>
<desc>Pull Images from Local Registry</desc>
<svr_id>0</svr_id>
<images>oai-gnb-aerial</images>
<tag_prefix>arm_</tag_prefix>
</testCase>
<testCase id="333333">
<class>Clean_Test_Server_Images</class>
<always_exec>true</always_exec>
<desc>Clean Test Images on Test Server</desc>
<svr_id>0</svr_id>
<images>oai-gnb-aerial</images>
</testCase>
</testCaseList>

View File

@@ -110,7 +110,7 @@ services:
- NET_ADMIN # for interface bringup
- NET_RAW # for ping
environment:
USE_ADDITIONAL_OPTIONS: --band 254 -C 2488400000 --CO -873500000 -r 25 --numerology 0 --ssb 60 --rfsim --rfsimulator.prop_delay 238.74 --rfsimulator.serveraddr 192.168.71.140 --log_config.global_log_options level,nocolor,time
USE_ADDITIONAL_OPTIONS: --band 254 -C 2488400000 --CO -873500000 -r 25 --numerology 0 --ssb 60 --rfsim --rfsimulator.prop_delay 238.74 --rfsimulator.serveraddr 192.168.71.140 --num-ul-actors 1 --log_config.global_log_options level,nocolor,time
depends_on:
- oai-gnb
networks:

View File

@@ -0,0 +1,152 @@
services:
mysql:
container_name: "rfsim5g-mysql"
image: mysql:8.0
volumes:
- ../5g_rfsimulator/oai_db.sql:/docker-entrypoint-initdb.d/oai_db.sql
- ../5g_rfsimulator/mysql-healthcheck.sh:/tmp/mysql-healthcheck.sh
environment:
- TZ=Europe/Paris
- MYSQL_DATABASE=oai_db
- MYSQL_USER=test
- MYSQL_PASSWORD=test
- MYSQL_ROOT_PASSWORD=linux
healthcheck:
test: /bin/bash -c "/tmp/mysql-healthcheck.sh"
interval: 10s
timeout: 5s
retries: 30
networks:
public_net:
ipv4_address: 192.168.71.131
oai-amf:
container_name: "rfsim5g-oai-amf"
image: oaisoftwarealliance/oai-amf:v2.0.0
environment:
- TZ=Europe/paris
volumes:
- ../5g_rfsimulator/mini_nonrf_config.yaml:/openair-amf/etc/config.yaml
depends_on:
- mysql
networks:
public_net:
ipv4_address: 192.168.71.132
oai-smf:
container_name: "rfsim5g-oai-smf"
image: oaisoftwarealliance/oai-smf:v2.0.0
environment:
- TZ=Europe/Paris
volumes:
- ../5g_rfsimulator/mini_nonrf_config.yaml:/openair-smf/etc/config.yaml
depends_on:
- oai-amf
networks:
public_net:
ipv4_address: 192.168.71.133
oai-upf:
container_name: "rfsim5g-oai-upf"
image: oaisoftwarealliance/oai-upf:v2.0.0
environment:
- TZ=Europe/Paris
volumes:
- ../5g_rfsimulator/mini_nonrf_config.yaml:/openair-upf/etc/config.yaml
depends_on:
- oai-smf
cap_add:
- NET_ADMIN
- SYS_ADMIN
cap_drop:
- ALL
privileged: true
networks:
public_net:
ipv4_address: 192.168.71.134
traffic_net:
ipv4_address: 192.168.72.134
oai-ext-dn:
privileged: true
container_name: rfsim5g-oai-ext-dn
image: oaisoftwarealliance/trf-gen-cn5g:focal
entrypoint: /bin/bash -c \
"iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE;"\
"ip route add 12.1.1.0/24 via 192.168.72.134 dev eth0; sleep infinity"
depends_on:
- oai-upf
networks:
traffic_net:
ipv4_address: 192.168.72.135
healthcheck:
test: /bin/bash -c "ping -c 2 192.168.72.134"
interval: 10s
timeout: 5s
retries: 5
oai-gnb:
image: ${REGISTRY:-oaisoftwarealliance}/${GNB_IMG:-oai-gnb}:${TAG:-develop}
container_name: rfsim5g-oai-gnb
cap_drop:
- ALL
environment:
USE_ADDITIONAL_OPTIONS: --rfsim --log_config.global_log_options level,nocolor,time
ASAN_OPTIONS: detect_leaks=0
depends_on:
- oai-ext-dn
networks:
public_net:
ipv4_address: 192.168.71.140
volumes:
- ../../conf_files/gnb.sa.band254.u0.25prb.rfsim.ntn-leo.conf:/opt/oai-gnb/etc/gnb.conf
healthcheck:
test: /bin/bash -c "pgrep nr-softmodem"
interval: 10s
timeout: 5s
retries: 5
oai-nr-ue:
image: ${REGISTRY:-oaisoftwarealliance}/${NRUE_IMG:-oai-nr-ue}:${TAG:-develop}
container_name: rfsim5g-oai-nr-ue
cap_drop:
- ALL
cap_add:
- NET_ADMIN # for interface bringup
- NET_RAW # for ping
environment:
USE_ADDITIONAL_OPTIONS:
--band 254 -C 2488400000 --CO -873500000 -r 25 --numerology 0 --ssb 60
--rfsim --rfsimulator.prop_delay 20 --rfsimulator.options chanmod
--rfsimulator.serveraddr 192.168.71.140
--time-sync-I 0.1 --ntn-initial-time-drift -46
--autonomous-ta --initial-fo 57340 --cont-fo-comp 2
--log_config.global_log_options level,nocolor,time
--num-ul-actors 1
depends_on:
- oai-gnb
networks:
public_net:
ipv4_address: 192.168.71.150
devices:
- /dev/net/tun:/dev/net/tun
volumes:
- ../../conf_files/nrue.uicc.ntn-leo.conf:/opt/oai-nr-ue/etc/nr-ue.conf
healthcheck:
test: /bin/bash -c "pgrep nr-uesoftmodem"
interval: 10s
timeout: 5s
retries: 5
networks:
public_net:
driver: bridge
name: rfsim5g-oai-public-net
ipam:
config:
- subnet: 192.168.71.128/26
driver_opts:
com.docker.network.bridge.name: "rfsim5g-public"
traffic_net:
driver: bridge
name: rfsim5g-oai-traffic-net
ipam:
config:
- subnet: 192.168.72.128/26
driver_opts:
com.docker.network.bridge.name: "rfsim5g-traffic"

View File

@@ -24,7 +24,7 @@ sudo -E echo start_server -uid 0 | sudo -E nvidia-cuda-mps-control
# Check if an argument is provided
if [ $# -eq 0 ]; then
# No argument provided, use default value
argument="P5G_FXN"
argument="P5G_FXN_GH"
else
# Argument provided, use it
argument="$1"

View File

@@ -23,7 +23,7 @@ services:
- ../../../cmake_targets/share:/opt/cuBB/share
userns_mode: host
ipc: "shareable"
image: cubb-build:24-1
image: cubb-build:24-3
environment:
- cuBB_SDK=/opt/nvidia/cuBB
command: bash -c "sudo rm -rf /tmp/phy.log && sudo chmod +x /opt/nvidia/cuBB/aerial_l1_entrypoint.sh && /opt/nvidia/cuBB/aerial_l1_entrypoint.sh"

View File

@@ -107,3 +107,18 @@ or directly with
")
endif()
endfunction()
function(run_asn1c ASN1C_GRAMMAR ASN1C_PREFIX)
set(options "")
set(oneValueArgs COMMENT)
set(multiValueArgs OUTPUT OPTIONS)
cmake_parse_arguments(ASN1C "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
get_filename_component(GRAMMAR_FILE ${ASN1C_GRAMMAR} NAME)
set(LOGFILE "${CMAKE_CURRENT_BINARY_DIR}/${GRAMMAR_FILE}.log")
add_custom_command(OUTPUT ${ASN1C_OUTPUT}
COMMAND ASN1C_PREFIX=${ASN1C_PREFIX} ${ASN1C_EXEC} ${ASN1C_OPTIONS} -D ${CMAKE_CURRENT_BINARY_DIR} ${ASN1C_GRAMMAR} > ${LOGFILE} 2>&1 || cat ${LOGFILE}
DEPENDS ${ASN1C_GRAMMAR}
COMMENT "Generating ${ASN1C_COMMENT} from ${GRAMMAR_FILE}"
)
endfunction()

View File

@@ -24,6 +24,12 @@
#include <stdint.h>
typedef struct plmn_id_s {
uint16_t mcc;
uint16_t mnc;
uint8_t mnc_digit_length;
} plmn_id_t;
typedef struct nssai_s {
uint8_t sst;
uint32_t sd;

View File

@@ -167,4 +167,20 @@ typedef struct paramlist_def {
int numelt ;
} paramlist_def_t;
/* Macros to get parameters with the config module API */
#define GET_PARAMS(param_def, param_desc, prefix) \
printf("Reading '%s' section from the config file\n", #param_def); \
paramdef_t param_def[] = param_desc; \
config_get(config_get_if(), param_def, sizeofArray(param_def), prefix);
/* Macros to get params lists with the config module API */
#define GET_PARAMS_LIST(param_list, param_def, param_desc, list_name, prefix, ...) \
paramdef_t param_def[] = param_desc; \
paramlist_def_t param_list = {list_name, NULL, 0}; \
__VA_OPT__(checkedparam_t config_check_##param_def[] = __VA_ARGS__; \
static_assert(sizeofArray(config_check_##param_def) == sizeofArray(param_def), \
"param_def_t array and corresponding checkedparam_t array should have the same size"); \
for (int i = 0; i < sizeofArray(param_def); ++i) param_def[i].chkPptr = &(config_check_##param_def[i]);) \
config_getlist(config_get_if(), &param_list, param_def, sizeofArray(param_def), prefix);
#endif /* INCLUDE_CONFIG_PARAMDESC_H */

View File

@@ -80,6 +80,8 @@
#define NB_RAB_MAX 11 /* from LTE_maxDRB in LTE_asn_constant.h */
#define RAB_OFFSET 0x000F
#define MAX_NUM_SLICES 1024
// RLC Entity
#define RLC_TX_MAXSIZE 10000000
#define RLC_RX_MAXSIZE 10000000
@@ -93,5 +95,6 @@
#define RETURNerror (-1)
#define DEFAULT_NAS_PATH "PWD"
#define UNUSED_VARIABLE(vARIABLE) (void)(vARIABLE)
#define NUM_PRACH_RX_FOR_NOISE_ESTIMATE 100
#endif /* __PLATFORM_CONSTANTS_H__ */

View File

@@ -13,6 +13,7 @@ add_subdirectory(T)
add_subdirectory(nr)
add_subdirectory(LOG)
add_subdirectory(threadPool)
add_subdirectory(time_manager)
add_library(utils utils.c system.c time_meas.c time_stat.c tun_if.c)
target_include_directories(utils PUBLIC .)
target_link_libraries(utils PRIVATE ${T_LIB})

View File

@@ -157,14 +157,19 @@ int write_file_matlab(const char *fname, const char *vname, void *data, int leng
//printf("Writing %d elements of type %d to %s\n",length,format,fname);
const int fname_len = strlen(fname);
char write_file_name[fname_len + 10];
const char *fn_format = (format & MATLAB_RAW) ? "%s.bin" : "%s";
snprintf(write_file_name, sizeof(write_file_name), fn_format, fname);
if (format == 10 || format ==11 || format == 12 || format == 13 || format == 14 || multiVec) {
fp = fopen(fname,"a+");
fp = fopen(write_file_name, "a+");
} else if (format != 10 && format !=11 && format != 12 && format != 13 && format != 14) {
fp = fopen(fname,"w+");
fp = fopen(write_file_name, "w+");
}
if (fp== NULL) {
printf("[OPENAIR][FILE OUTPUT] Cannot open file %s\n",fname);
printf("[OPENAIR][FILE OUTPUT] Cannot open file %s\n", write_file_name);
return(-1);
}

View File

@@ -36,6 +36,7 @@
#include "common/utils/LOG/log.h"
#include "nr_common.h"
#include <limits.h>
#include <math.h>
#define C_SRS_NUMBER (64)
#define B_SRS_NUMBER (4)
@@ -1427,3 +1428,15 @@ frequency_range_t get_freq_range_from_band(uint16_t band)
{
return band <= 256 ? FR1 : FR2;
}
float get_beta_dmrs_pusch(int num_cdm_groups_no_data, pusch_dmrs_type_t dmrs_type)
{
float beta_dmrs_pusch = 1.0;
if (num_cdm_groups_no_data == 2) {
beta_dmrs_pusch = powf(10.0, 3.0 / 20.0);
} else if (num_cdm_groups_no_data == 3) {
if (dmrs_type == pusch_dmrs_type2)
beta_dmrs_pusch = powf(10.0, 4.77 / 20.0);
}
return beta_dmrs_pusch;
}

View File

@@ -38,6 +38,7 @@
#include "assertions.h"
#include "common/utils/utils.h"
#define MAX_SI_GROUPS 3
#define NR_MAX_PDSCH_TBS 3824
#define MAX_NUM_BEAM_PERIODS 4
#define MAX_BWP_SIZE 275
@@ -93,6 +94,12 @@ static inline const char *rnti_types(nr_rnti_type_t rr)
// the total shift is 2 * 15, in dB scale thats 10log10(2^(15*2))
#define SQ15_SQUARED_NORM_FACTOR_DB 90.3089986992
typedef struct {
uint8_t *SIB_buffer;
int SIB_size;
int SIB_type;
} nr_SIBs_t;
typedef struct nr_bandentry_s {
int16_t band;
uint64_t ul_min;
@@ -123,6 +130,11 @@ typedef enum frequency_range_e {
FR2
} frequency_range_t;
typedef enum {
pusch_dmrs_type1 = 0,
pusch_dmrs_type2 = 1
} pusch_dmrs_type_t;
#define MAX_NUM_SLOTS_ALLOWED 80 // up to numerology 3 (120 KHz SCS) is supported
enum slot_type { TDD_NR_DOWNLINK_SLOT, TDD_NR_UPLINK_SLOT, TDD_NR_MIXED_SLOT };
@@ -295,6 +307,17 @@ frequency_range_t get_freq_range_from_freq(uint64_t freq);
frequency_range_t get_freq_range_from_arfcn(uint32_t arfcn);
frequency_range_t get_freq_range_from_band(uint16_t band);
/**
* @brief Calculates the scaling factor for the ratio of PUSCH EPRE to DMRS EPRE.
*
* @param num_cdm_groups_no_data The number of CDM groups without data.
* @param dmrs_type The DMRS type.
* @return The calculated beta scaling factor for the ratio of PUSCH EPRE to DMRS EPRE.
*
* @note The values are derived from TS 38.214 Table 6.2.2-1.
*/
float get_beta_dmrs_pusch(int num_cdm_groups_no_data, pusch_dmrs_type_t dmrs_type);
#define CEILIDIV(a,b) ((a+b-1)/b)
#define ROUNDIDIV(a,b) (((a<<1)+b)/(b<<1))

View File

@@ -0,0 +1,14 @@
add_library(time_management STATIC
time_manager.c)
add_library(time_management_core OBJECT
time_source.c
time_server.c
time_client.c)
target_link_libraries(time_management PUBLIC time_management_core)
target_link_libraries(time_management_core PRIVATE ${T_LIB})
if(ENABLE_TESTS)
add_subdirectory(tests)
endif()

View File

@@ -0,0 +1,11 @@
add_executable(test_time_manager_manual test_manual.c)
target_link_libraries(test_time_manager_manual time_management_core)
target_link_libraries(test_time_manager_manual LOG CONFIG_LIB)
add_executable(test_time_manager_auto test_auto.c)
target_link_libraries(test_time_manager_auto time_management)
target_link_libraries(test_time_manager_auto LOG CONFIG_LIB)
add_dependencies(tests test_time_manager_auto)
add_test(NAME time_management_tests
COMMAND ./test_time_manager_auto)

View File

@@ -0,0 +1,297 @@
/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.openairinterface.org/?page_id=698
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include "common/utils/time_manager/time_manager.h"
#include "common/utils/LOG/log.h"
#include "common/config/config_userapi.h"
configmodule_interface_t *uniqCfg = NULL;
void exit_function(const char *file, const char *function, const int line, const char *s, const int assert)
{
if (s != NULL) {
printf("%s:%d %s() Exiting OAI softmodem: %s\n", file, line, function, s);
}
if (assert)
abort();
else
exit(1);
}
static int rlc_tick_count = 0;
static int pdcp_tick_count = 0;
static int x2ap_tick_count = 0;
void nr_rlc_ms_tick(void)
{
rlc_tick_count++;
}
void nr_pdcp_ms_tick(void)
{
pdcp_tick_count++;
}
void x2ap_ms_tick(void)
{
x2ap_tick_count++;
}
/* return 1 if ok, 0 if error */
static int standalone_realtime(void)
{
time_manager_tick_function_t tick_functions[] = {
nr_pdcp_ms_tick,
nr_rlc_ms_tick,
x2ap_ms_tick
};
int tick_functions_count = sizeofArray(tick_functions);
time_manager_start(tick_functions, tick_functions_count, TIME_SOURCE_REALTIME);
sleep(1);
time_manager_finish();
LOG_I(UTIL,
"standalone realtime: rlc ticks: %d pdcp ticks: %d x2ap ticks: %d\n",
rlc_tick_count,
pdcp_tick_count,
x2ap_tick_count);
/* let's accept 1% deviation from the 1000 counts we expect */
return abs(rlc_tick_count-1000) < 10
&& abs(pdcp_tick_count - 1000) < 10
&& abs(x2ap_tick_count - 1000) < 10;
}
/* return 1 if ok, 0 if error */
static int standalone_iq_samples(void)
{
time_manager_tick_function_t tick_functions[] = {
nr_pdcp_ms_tick,
nr_rlc_ms_tick,
x2ap_ms_tick
};
int tick_functions_count = sizeofArray(tick_functions);
time_manager_start(tick_functions, tick_functions_count, TIME_SOURCE_IQ_SAMPLES);
/* let's pretend we have 2000 samples per second */
for (int i = 0; i < 1000; i++) {
time_manager_iq_samples(1, 2000);
usleep(10);
time_manager_iq_samples(1, 2000);
usleep(10);
}
time_manager_finish();
LOG_I(UTIL,
"standalone iq-samples: rlc ticks: %d pdcp ticks: %d x2ap ticks: %d\n",
rlc_tick_count,
pdcp_tick_count,
x2ap_tick_count);
/* exact count is required */
return rlc_tick_count == 1000
&& pdcp_tick_count == 1000
&& x2ap_tick_count == 1000;
}
/* return 1 if ok, 0 if error */
static int client_server(char *program_name, bool iq_samples_time_source)
{
LOG_I(UTIL, "client/server %s: launch sub-processes\n", iq_samples_time_source ? "iq-samples" : "realtime");
pid_t server;
pid_t client;
/* start server */
server = fork();
DevAssert(server >= 0);
if (server == 0) {
/* run as server */
if (iq_samples_time_source)
execl(program_name, program_name, "--time_management.mode", "server", "--time_management.time_source", "iq_samples", NULL);
else
execl(program_name, program_name, "--time_management.mode", "server", NULL);
exit(1);
}
/* wait a little bit so that server is ready to accept clients
* (this is hackish)
*/
usleep(100);
/* start client */
client = fork();
DevAssert(client >= 0);
if (client == 0) {
/* run as client */
execl(program_name, program_name, "--time_management.mode", "client", NULL);
exit(1);
}
/* wait for completion */
int ret = 1;
int status;
pid_t r = waitpid(server, &status, 0);
DevAssert(r == server);
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
ret = 0;
r = waitpid(client, &status, 0);
DevAssert(r == client);
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
ret = 0;
return ret;
}
/* return 1 if ok, 0 if error */
static int run_sub_client_server(bool iq_samples_time_source)
{
time_manager_tick_function_t tick_functions[] = {
nr_pdcp_ms_tick,
nr_rlc_ms_tick,
x2ap_ms_tick
};
int tick_functions_count = sizeofArray(tick_functions);
/* last argument doesn't matter, overwritten by args of execl in client_server() */
time_manager_start(tick_functions, tick_functions_count, TIME_SOURCE_REALTIME);
if (iq_samples_time_source) {
/* 'iq_samples_time_source' is true only for server
* wait a bit for client to be there otherwise server
* may finish too early
*/
usleep(100);
/* let's pretend we have 2000 samples per second */
for (int i = 0; i < 1000; i++) {
time_manager_iq_samples(1, 2000);
usleep(10);
time_manager_iq_samples(1, 2000);
usleep(10);
}
} else {
sleep(1);
}
time_manager_finish();
LOG_I(UTIL, "client/server: rlc ticks: %d pdcp ticks: %d x2ap ticks: %d\n", rlc_tick_count, pdcp_tick_count, x2ap_tick_count);
if (iq_samples_time_source)
return rlc_tick_count == 1000
&& pdcp_tick_count == 1000
&& x2ap_tick_count == 1000;
else
/* let's accept 1% deviation from the 1000 counts we expect */
return abs(rlc_tick_count-1000) < 10
&& abs(pdcp_tick_count - 1000) < 10
&& abs(x2ap_tick_count - 1000) < 10;
}
int main(int argc, char **argv)
{
if ((uniqCfg = load_configmodule(argc, argv, CONFIG_ENABLECMDLINEONLY)) == NULL) {
exit_fun("[SOFTMODEM] Error, configuration module init failed\n");
}
logInit();
/* for client/server tests, we use sub-processes with command line parameters */
if (argc > 1) {
/* if there are command line parameters then it's a sub-process */
rlc_tick_count = 0;
pdcp_tick_count = 0;
x2ap_tick_count = 0;
/* return value to the main test program
* let's do as for the shell: 0 means success and 1 means error
*/
/* we know we are in iq-samples time source mode if the last argument is "iq_samples" */
int success = run_sub_client_server(!strcmp(argv[argc - 1], "iq_samples")) == 1;
return 1 - success;
}
int err = 0;
/* test standalone realtime */
rlc_tick_count = 0;
pdcp_tick_count = 0;
x2ap_tick_count = 0;
int ret = standalone_realtime();
if (ret == 1)
LOG_I(UTIL, "standalone realtime: OK\n");
else {
LOG_E(UTIL, "standalone realtime: ERROR\n");
err = 1;
}
/* test standalone iq-samples */
rlc_tick_count = 0;
pdcp_tick_count = 0;
x2ap_tick_count = 0;
ret = standalone_iq_samples();
if (ret == 1)
LOG_I(UTIL, "standalone iq-samples: OK\n");
else {
LOG_E(UTIL, "standalone iq-samples: ERROR\n");
err = 1;
}
/* client/server tests are not written in a robust way
* they may fail depending on realtime on the machine
* de-activate if needed (if CI fails)
*/
/* test client/server realtime */
rlc_tick_count = 0;
pdcp_tick_count = 0;
x2ap_tick_count = 0;
ret = client_server(argv[0], false);
if (ret == 1)
LOG_I(UTIL, "client-server realtime: OK\n");
else {
LOG_E(UTIL, "client-server realtime: ERROR\n");
err = 1;
}
/* test client/server iq-samples */
rlc_tick_count = 0;
pdcp_tick_count = 0;
x2ap_tick_count = 0;
ret = client_server(argv[0], true);
if (ret == 1)
LOG_I(UTIL, "client-server iq-samples: OK\n");
else {
LOG_E(UTIL, "client-server iq-samples: ERROR\n");
err = 1;
}
logTerm();
return err;
}

View File

@@ -0,0 +1,175 @@
/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.openairinterface.org/?page_id=698
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
#include "../time_source.h"
#include "../time_server.h"
#include "../time_client.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
#include <stdbool.h>
#include <signal.h>
#include "common/utils/system.h"
#include "common/utils/LOG/log.h"
#include "common/config/config_userapi.h"
configmodule_interface_t *uniqCfg = NULL;
void exit_function(const char *file, const char *function, const int line, const char *s, const int assert)
{
printf("%s:%d:%s: %s\n", file, line, function, s);
fflush(stdout);
exit(1);
}
_Atomic bool test_exit;
static void *iq_generate_thread(void *ts)
{
time_source_t *time_source = ts;
while (!test_exit) {
printf("iq_generate_thread calls time_source_iq_add(time_source, 100, 10000)\n");
time_source_iq_add(time_source, 100, 10000);
sleep(1);
}
return NULL;
}
#define STANDALONE 0
#define SERVER 1
#define CLIENT 2
static void usage(void)
{
printf("options:\n");
printf(" -client\n");
printf(" run as client (default is standalone)\n");
printf(" -server\n");
printf(" run as server (default is standalone)\n");
printf(" -ip <ip address (default 127.0.0.1)>\n");
printf(" use this ip address for server\n");
printf(" -port <port (default 7473)\n");
printf(" use this port for server\n");
printf(" -realtime\n");
printf(" run with realtime clock (default is simulated iq samples)\n");
exit(0);
}
static void server_callback(void *callback_data)
{
printf("server_callback called (callback_data %p)\n", callback_data);
DevAssert(callback_data == (void *)1);
}
static void client_callback(void *callback_data)
{
printf("client_callback called (callback_data %p)\n", callback_data);
DevAssert(callback_data == (void *)2);
}
/* dummy sig handler to quit immediately when pressing enter
* (we send a signal to iq_generate_thread to interrupt sleep())
*/
static void sig_handle(int n)
{
/* nothing */
}
int main(int n, char **v)
{
time_source_t *ts = NULL;
time_server_t *server = NULL;
time_client_t *client = NULL;
int mode = STANDALONE;
char *server_ip = "127.0.0.1";
int server_port = 7473;
time_source_type_t time_source_type = TIME_SOURCE_IQ_SAMPLES;
pthread_t iq_thread;
for (int i = 1; i < n; i++) {
if (!strcmp(v[i], "-client")) { mode = CLIENT; continue; }
if (!strcmp(v[i], "-server")) { mode = SERVER; continue; }
if (!strcmp(v[i], "-ip")) { if (i>n-2) usage(); server_ip = v[++i]; continue; }
if (!strcmp(v[i], "-port")) { if (i>n-2) usage(); server_port = atoi(v[++i]); continue; }
if (!strcmp(v[i], "-realtime")) { time_source_type = TIME_SOURCE_REALTIME; continue; }
usage();
}
logInit();
/* hack: set log to info (logInit doesn't do it because we don't call load_configmodule()) */
for (int i = 0; i < MAX_LOG_COMPONENTS; i++)
g_log->log_component[i].level = OAILOG_INFO;
if (mode != CLIENT) {
ts = new_time_source(time_source_type);
if (time_source_type == TIME_SOURCE_IQ_SAMPLES) {
void *ret = signal(SIGHUP, sig_handle);
DevAssert(ret != SIG_ERR);
threadCreate(&iq_thread, iq_generate_thread, ts, "iq samples generator", -1, SCHED_OAI);
}
}
if (mode == SERVER) {
/* (void*)1 to check if callback data is passed correctly */
server = new_time_server(server_ip, server_port, server_callback, (void *)1);
time_server_attach_time_source(server, ts);
}
if (mode == CLIENT) {
/* (void*)2 to check if callback data is passed correctly */
client = new_time_client(server_ip, server_port, client_callback, (void *)2);
}
printf("main: press enter to quit\n");
getchar();
test_exit = true;
if (mode != CLIENT)
if (time_source_type == TIME_SOURCE_IQ_SAMPLES) {
(void)pthread_kill(iq_thread, SIGHUP);
int ret = pthread_join(iq_thread, NULL);
DevAssert(ret == 0);
}
if (mode != CLIENT) {
free_time_source(ts);
}
if (mode == SERVER) {
free_time_server(server);
}
if (mode == CLIENT) {
free_time_client(client);
}
logTerm();
return 0;
}

View File

@@ -0,0 +1,232 @@
/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.openairinterface.org/?page_id=698
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
#include "time_client.h"
#include <string.h>
#include <errno.h>
#include <stdbool.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <poll.h>
#include <sys/eventfd.h>
#include <unistd.h>
#include "common/utils/assertions.h"
#include "common/utils/LOG/log.h"
#include "common/utils/system.h"
typedef struct {
pthread_t thread_id;
int exit_fd;
struct sockaddr_in server_ip;
int server_port;
void (*callback)(void *);
void *callback_data;
} time_client_private_t;
static int connect_to_server(time_client_private_t *time_client)
{
int sock = -1;
int opts;
int r;
struct pollfd fds[2];
/* try forever (or exit requested) until we succeed */
while (1) {
sock = socket(AF_INET, SOCK_STREAM, 0);
AssertFatal(sock != -1, "socket() failed: %s\n", strerror(errno));
int v = 1;
r = setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &v, sizeof(v));
AssertFatal(r == 0, "setsockopt() failed: %s\n", strerror(errno));
/* unblock socket */
opts = fcntl(sock, F_GETFL);
AssertFatal(opts >= 0, "fcntl() failed: %s\n", strerror(errno));
opts |= O_NONBLOCK;
r = fcntl(sock, F_SETFL, opts);
AssertFatal(r == 0, "fctnl() failed: %s\n", strerror(errno));
if (connect(sock, (struct sockaddr *)&time_client->server_ip, sizeof(time_client->server_ip)) == 0)
break;
if (errno == EINPROGRESS) {
/* connection not completed yet, we need to wait */
fds[0].fd = sock;
fds[0].events = POLLOUT;
fds[1].fd = time_client->exit_fd;
fds[1].events = POLLIN;
r = poll(fds, 2, -1);
AssertFatal(r >= 0, "poll() failed: %s\n", strerror(errno));
/* do we need to exit? */
if (fds[1].revents) {
shutdown(sock, SHUT_RDWR);
close(sock);
sock = -1;
break;
}
/* check if connection is successful */
if (fds[0].revents & POLLOUT) {
int so_error;
socklen_t so_error_len = sizeof(so_error);
r = getsockopt(sock, SOL_SOCKET, SO_ERROR, &so_error, &so_error_len);
AssertFatal(r == 0 && so_error_len == sizeof(so_error),
"getsockopt() failed (r = %d) or so_error_len (= %d) is not %ld\n",
r, so_error_len, sizeof(so_error));
if (so_error == 0)
/* success */
break;
}
}
/* connection failed, try again in 1s (or stop if exit requested) */
close(sock);
sock = -1;
char server_address[256];
const char *ret = inet_ntop(AF_INET, &time_client->server_ip.sin_addr, server_address, sizeof(server_address));
AssertFatal(ret != NULL, "inet_ntop() failed: %s\n", strerror(errno));
LOG_W(UTIL, "time client: connection to %s:%d failed, try again in 1s\n", server_address, time_client->server_port);
/* sleep 1s (or exit requested) */
fds[0].fd = time_client->exit_fd;
fds[0].events = POLLIN;
r = poll(fds, 1, 1000);
AssertFatal(r >= 0, "poll() failed: %s\n", strerror(errno));
/* exit requested? */
if (fds[0].revents) {
shutdown(sock, SHUT_RDWR);
close(sock);
sock = -1;
break;
}
}
if (sock != -1) {
/* reblock socket */
opts &= ~O_NONBLOCK;
r = fcntl(sock, F_SETFL, opts);
AssertFatal(r == 0, "fcntl() failed: %s\n", strerror(errno));
}
return sock;
}
static void *time_client_thread(void *tc)
{
time_client_private_t *time_client = tc;
int ret;
int socket = connect_to_server(time_client);
/* if socket is -1 at this point, it means that exit was requested */
if (socket == -1)
return NULL;
while (1) {
struct pollfd polls[2];
/* polls[0] is for the socket */
polls[0].fd = socket;
polls[0].events = POLLIN;
/* polls[1] is for exit_fd */
polls[1].fd = time_client->exit_fd;
polls[1].events = POLLIN;
ret = poll(polls, 2, -1);
AssertFatal(ret >= 0, "poll() failed: %s\n", strerror(errno));
/* whatever is in revents for exit_fd means exit */
if (polls[1].revents)
break;
/* is socket fine? */
if (polls[0].revents & (POLLERR | POLLHUP)) {
reconnect:
/* socket not fine, reconnect */
shutdown(socket, SHUT_RDWR);
close(socket);
socket = connect_to_server(time_client);
/* connect only fails if exit requested, we can safely 'continue' */
continue;
}
/* here we only accept POLLIN */
AssertFatal(polls[0].revents == POLLIN, "poll() did not return expected POLLIN\n");
/* get the ticks from server */
uint8_t tick_count;
ret = read(socket, &tick_count, 1);
/* any error => reconnect */
if (ret == -1) {
LOG_E(UTIL, "time client: error %s, reconnect\n", strerror(errno));
goto reconnect;
}
/* if 0 is returned, it means socket has been closed by other end */
if (ret == 0) {
LOG_E(UTIL, "time client: socket closed by other end, reconnect\n");
goto reconnect;
}
DevAssert(ret == 1);
/* call callback once for each tick */
for (int i = 0; i < tick_count; i++)
time_client->callback(time_client->callback_data);
}
shutdown(socket, SHUT_RDWR);
close(socket);
return NULL;
}
time_client_t *new_time_client(const char *server_ip,
int server_port,
void (*callback)(void *),
void *callback_data)
{
time_client_private_t *tc;
int ret;
tc = calloc_or_fail(1, sizeof(*tc));
tc->exit_fd = eventfd(0, 0);
AssertFatal(tc->exit_fd != -1, "eventfd() failed: %s\n", strerror(errno));
ret = inet_aton(server_ip, &tc->server_ip.sin_addr);
AssertFatal(ret != 0, "inet_aton() failed: %s\n", strerror(errno));
tc->server_ip.sin_family = AF_INET;
tc->server_ip.sin_port = htons(server_port);
tc->server_port = server_port;
tc->callback = callback;
tc->callback_data = callback_data;
threadCreate(&tc->thread_id, time_client_thread, tc, "time client", -1, SCHED_OAI);
return tc;
}
void free_time_client(time_client_t *tc)
{
time_client_private_t *time_client = tc;
uint64_t v = 1;
int ret = write(time_client->exit_fd, &v, 8);
DevAssert(ret == 8);
ret = pthread_join(time_client->thread_id, NULL);
DevAssert(ret == 0);
close(time_client->exit_fd);
free(time_client);
}

View File

@@ -0,0 +1,36 @@
/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.openairinterface.org/?page_id=698
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
#ifndef COMMON_UTIL_TIME_MANAGER_TIME_CLIENT
#define COMMON_UTIL_TIME_MANAGER_TIME_CLIENT
/* opaque data type */
typedef void time_client_t;
time_client_t *new_time_client(const char *server_ip,
int server_port,
void (*callback)(void *),
void *callback_data);
void free_time_client(time_client_t *time_client);
#endif /* COMMON_UTIL_TIME_MANAGER_TIME_CLIENT */

View File

@@ -0,0 +1,205 @@
/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.openairinterface.org/?page_id=698
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
#include "time_manager.h"
#include "time_source.h"
#include "time_server.h"
#include "time_client.h"
#include "common/config/config_userapi.h"
#include "common/utils/assertions.h"
#include "common/utils/LOG/log.h"
/* global variables (some may be ununsed, depends on configuration) */
static time_source_t *time_source = NULL;
static time_server_t *time_server = NULL;
static time_client_t *time_client = NULL;
/* tick functions registered at initialization of the time manager */
static time_manager_tick_function_t *tick_functions;
static int tick_functions_count;
/* tick callback, called by time_source every 'millisecond' (can be
* actual millisecond or simulated millisecond)
*/
static void tick(void *unused)
{
for (int i = 0; i < tick_functions_count; i++)
tick_functions[i]();
}
#define TIME_SOURCE "time_source"
#define MODE "mode"
#define SERVER_IP "server_ip"
#define SERVER_PORT "server_port"
static paramdef_t config_parameters[] = {
{TIME_SOURCE, "time source", 0, .strptr = NULL, .defstrval = NULL, TYPE_STRING, 0},
{MODE, "time mode", 0, .strptr = NULL, .defstrval = NULL, TYPE_STRING, 0},
{SERVER_IP, "server ip", 0, .strptr = NULL, .defstrval = NULL, TYPE_STRING, 0},
{SERVER_PORT, "server port", 0, .iptr = NULL, .defintval = -1, TYPE_INT, 0},
};
static char *get_param_str(char *name)
{
int idx = config_paramidx_fromname(config_parameters, sizeofArray(config_parameters), name);
char *param;
if (config_parameters[idx].strptr != NULL)
param = *config_parameters[idx].strptr;
else
param = NULL;
return param;
}
static int get_param_int(char *name)
{
int idx = config_paramidx_fromname(config_parameters, sizeofArray(config_parameters), name);
return *config_parameters[idx].iptr;
}
void time_manager_start(time_manager_tick_function_t *_tick_functions,
int _tick_functions_count,
time_source_type_t time_source_type)
{
bool has_time_server = false;
bool has_time_client = false;
char *default_server_ip = "127.0.0.1";
int default_server_port = 7374;
char *server_ip = NULL;
int server_port = -1;
tick_functions_count = _tick_functions_count;
tick_functions = calloc_or_fail(tick_functions_count, sizeof(*tick_functions));
memcpy(tick_functions, _tick_functions, tick_functions_count * sizeof(*tick_functions));
/* retrieve configuration */
int ret = config_get(config_get_if(), config_parameters, sizeofArray(config_parameters), "time_management");
if (ret >= 0) {
char *param;
/* time source */
param = get_param_str(TIME_SOURCE);
if (param != NULL) {
if (!strcmp(param, "realtime")) {
time_source_type = TIME_SOURCE_REALTIME;
} else if (!strcmp(param, "iq_samples")) {
time_source_type = TIME_SOURCE_IQ_SAMPLES;
} else {
AssertFatal(0, "bad parameter 'time_source' in section 'time_management', unknown value \"%s\". Valid values are \"realtime\" and \"iq_samples\"\n", param);
}
}
/* mode */
param = get_param_str(MODE);
if (param != NULL) {
if (!strcmp(param, "standalone")) {
has_time_server = false;
has_time_client = false;
} else if (!strcmp(param, "server")) {
has_time_server = true;
has_time_client = false;
} else if (!strcmp(param, "client")) {
has_time_server = false;
has_time_client = true;
} else {
AssertFatal(0, "bad parameter 'mode' in section 'time_management', unknown value \"%s\". Valid values are \"standalone\", \"server\" and \"client\"\n", param);
}
}
/* server ip */
param = get_param_str(SERVER_IP);
if (param != NULL) {
server_ip = param;
}
/* server port */
int port = get_param_int(SERVER_PORT);
if (port != -1) {
server_port = port;
}
}
if (server_ip == NULL)
server_ip = default_server_ip;
if (server_port == -1)
server_port = default_server_port;
/* create entities, according to selected configuration */
if (!has_time_client) {
time_source = new_time_source(time_source_type);
time_source_set_callback(time_source, tick, NULL);
}
if (has_time_server) {
time_server = new_time_server(server_ip, server_port, tick, NULL);
time_server_attach_time_source(time_server, time_source);
}
if (has_time_client) {
time_client = new_time_client(server_ip, server_port, tick, NULL);
/* for the log below, there is no time source, we want to print 'server' as time source */
time_source_type = 2;
}
LOG_I(UTIL,
"time manager configuration: [time source: %s] [mode: %s] [server IP: %s} [server port: %d]%s\n",
(char *[]){"reatime", "iq_samples", "server"}[time_source_type],
has_time_server ? "server"
: has_time_client ? "client"
: "standalone",
server_ip,
server_port,
has_time_server || has_time_client ? "" : " (server IP/port not used)");
}
void time_manager_iq_samples(uint64_t iq_samples_count,
uint64_t iq_samples_per_second)
{
time_source_iq_add(time_source, iq_samples_count, iq_samples_per_second);
}
void time_manager_finish(void)
{
/* time source has to be shutdown first */
if (time_source != NULL) {
free_time_source(time_source);
time_source = NULL;
}
if (time_server != NULL) {
free_time_server(time_server);
time_server = NULL;
}
if (time_client != NULL) {
free_time_client(time_client);
time_client = NULL;
}
free(tick_functions);
tick_functions = NULL;
tick_functions_count = 0;
}

View File

@@ -19,40 +19,20 @@
* contact@openairinterface.org
*/
/*! \file FGSRegistrationResult.c
#ifndef COMMON_UTIL_TIME_MANAGER_TIME_MANAGER
#define COMMON_UTIL_TIME_MANAGER_TIME_MANAGER
\brief 5GS Registration result for registration request procedures
\author Yoshio INOUE, Masayuki HARADA
\email: yoshio.inoue@fujitsu.com,masayuki.harada@fujitsu.com
\date 2020
\version 0.1
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include "TLVEncoder.h"
#include "TLVDecoder.h"
#include "FGSRegistrationResult.h"
#include "time_source.h"
int decode_fgs_registration_result(FGSRegistrationResult *fgsregistrationresult, uint8_t iei, uint16_t value, uint32_t len)
{
int decoded = 0;
uint16_t *buffer = &value;
fgsregistrationresult->registrationresult = *buffer & 0x7;
fgsregistrationresult->smsallowed = *buffer & 0x8;
decoded = decoded + 2;
return decoded;
}
typedef void (*time_manager_tick_function_t)(void);
uint16_t encode_fgs_registration_result(const FGSRegistrationResult *fgsregistrationresult)
{
uint16_t bufferReturn;
uint16_t *buffer = &bufferReturn;
uint8_t encoded = 0;
*(buffer + encoded) = 0x00 | (fgsregistrationresult->smsallowed & 0x8) | (fgsregistrationresult->registrationresult & 0x7);
encoded = encoded + 2;
void time_manager_start(time_manager_tick_function_t *tick_functions,
int tick_functions_count,
time_source_type_t time_source);
void time_manager_iq_samples(uint64_t iq_samples_count,
uint64_t iq_samples_per_second);
void time_manager_finish(void);
return bufferReturn;
}
#endif /* COMMON_UTIL_TIME_MANAGER_TIME_MANAGER */

View File

@@ -0,0 +1,249 @@
/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.openairinterface.org/?page_id=698
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
#include "time_server.h"
#include <stdbool.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <poll.h>
#include <errno.h>
#include <sys/eventfd.h>
#include <unistd.h>
#include <string.h>
#include "common/utils/system.h"
#include "common/utils/LOG/log.h"
typedef struct {
pthread_t thread_id;
_Atomic bool exit;
int server_socket;
int tick_fd;
void (*callback)(void *);
void *callback_data;
} time_server_private_t;
static void *time_server_thread(void *ts)
{
time_server_private_t *time_server = ts;
struct pollfd *polls = NULL;
int *socket_fds = NULL;
int client_count = 0;
polls = calloc_or_fail(2, sizeof(struct pollfd));
while (!time_server->exit) {
/* polls[0] is to receive ticks */
polls[0].fd = time_server->tick_fd;
polls[0].events = POLLIN;
/* polls[1] is to accept connections from clients */
polls[1].fd = time_server->server_socket;
polls[1].events = POLLIN;
/* add clients */
for (int i = 0; i < client_count; i++) {
polls[i + 2].fd = socket_fds[i];
polls[i + 2].events = POLLIN;
}
int ret = poll(polls, client_count + 2, -1);
AssertFatal(ret >= 1 || (ret == -1 && errno == EINTR), "poll() failed\n");
if (time_server->exit)
break;
/* check tick */
if (polls[0].revents) {
/* do not accept errors */
AssertFatal(polls[0].revents == POLLIN, "poll() failed\n");
uint64_t ticks;
int ret = read(time_server->tick_fd, &ticks, 8);
DevAssert(ret == 8);
/* call the callback ticks time */
for (int i = 0; i < ticks; i++)
time_server->callback(time_server->callback_data);
/* send ticks by blocks of 255 max (what fits in a uint8_t) */
while (ticks) {
/* get min(ticks, 255) */
uint8_t count = ticks < 255 ? ticks : 255;
/* send to all clients */
for (int i = 0; i < client_count; i++) {
if (write(socket_fds[i], &count, 1) == 1)
continue;
/* error writing - remove client */
LOG_W(UTIL, "time server: client disconnects\n");
shutdown(socket_fds[i], SHUT_RDWR);
close(socket_fds[i]);
client_count--;
i--;
if (client_count == 0) {
free(socket_fds);
socket_fds = NULL;
} else {
memmove(&socket_fds[i + 1], &socket_fds[i + 2], sizeof(int) * (client_count - i - 1));
socket_fds = realloc(socket_fds, sizeof(int) * client_count);
}
polls = realloc(polls, sizeof(struct pollfd) * (client_count + 2));
AssertFatal(polls != NULL, "realloc() failed\n");
}
ticks -= count;
}
}
/* check new client */
if (polls[1].revents) {
/* do not accept errors */
AssertFatal(polls[1].revents == POLLIN, "poll() failed\n");
int new_socket = accept(time_server->server_socket, NULL, NULL);
AssertFatal(new_socket != -1, "accept() failed: %s\n", strerror(errno));
/* add client */
client_count++;
socket_fds = realloc(socket_fds, sizeof(int) * client_count);
AssertFatal(socket_fds != NULL, "realloc() failed\n");
socket_fds[client_count - 1] = new_socket;
polls = realloc(polls, sizeof(struct pollfd) * (client_count + 2));
AssertFatal(polls != NULL, "realloc() failed\n");
continue;
}
/* any event on a client socket is to be considered as an error */
for (int i = 0; i < client_count; i++) {
if (polls[i + 2].revents == 0)
continue;
LOG_W(UTIL,
"time server: error on client's socket, removing client (revents is %d, fd is %d i is %d)\n",
polls[i + 2].revents,
polls[i + 2].fd,
i);
shutdown(socket_fds[i], SHUT_RDWR);
close(socket_fds[i]);
client_count--;
i--;
if (client_count == 0) {
free(socket_fds);
socket_fds = NULL;
} else {
memmove(&socket_fds[i + 1], &socket_fds[i + 2], sizeof(int) * (client_count - i - 1));
socket_fds = realloc(socket_fds, sizeof(int) * client_count);
AssertFatal(socket_fds != NULL, "realloco() failed\n");
}
if (client_count)
memmove(&polls[i + 2 + 1], &polls[i + 2 + 2], sizeof(struct pollfd) * (client_count - i - 1));
polls = realloc(polls, sizeof(struct pollfd) * (client_count + 2));
AssertFatal(polls != NULL, "realloc() failed\n");
}
}
/* close all clients' sockets */
for (int i = 0; i < client_count; i++) {
shutdown(socket_fds[i], SHUT_RDWR);
close(socket_fds[i]);
}
free(polls);
free(socket_fds);
return NULL;
}
time_server_t *new_time_server(const char *ip,
int port,
void (*callback)(void *),
void *callback_data)
{
time_server_private_t *ts;
int ret;
ts = calloc_or_fail(1, sizeof(*ts));
ts->server_socket = socket(AF_INET, SOCK_STREAM, 0);
AssertFatal(ts->server_socket != -1, "socket() failed: %s\n", strerror(errno));
int v = 1;
ret = setsockopt(ts->server_socket, SOL_SOCKET, SO_REUSEADDR, &v, sizeof(v));
AssertFatal(ret == 0, "setsockopt() failed: %s\n", strerror(errno));
v = 1;
ret = setsockopt(ts->server_socket, IPPROTO_TCP, TCP_NODELAY, &v, sizeof(v));
AssertFatal(ret == 0, "setsockopt() failed: %s\n", strerror(errno));
ts->tick_fd = eventfd(0, 0);
AssertFatal(ts->tick_fd != -1, "eventfd() failed: %s\n", strerror(errno));
struct sockaddr_in ip_addr;
ip_addr.sin_family = AF_INET;
ip_addr.sin_port = htons(port);
ret = inet_aton(ip, &ip_addr.sin_addr);
AssertFatal(ret != 0, "inet_aton() failed: %s\n", strerror(errno));
ret = bind(ts->server_socket, (struct sockaddr *)&ip_addr, sizeof(ip_addr));
AssertFatal(ret == 0, "bind() failed: %s\n", strerror(errno));
ret = listen(ts->server_socket, 10); /* 10: arbitrary value */
AssertFatal(ret == 0, "listen() failed: %s\n", strerror(errno));
ts->callback = callback;
ts->callback_data = callback_data;
threadCreate(&ts->thread_id, time_server_thread, ts, "time server", -1, SCHED_OAI);
return ts;
}
void free_time_server(time_server_t *ts)
{
time_server_private_t *time_server = ts;
/* flag exit */
time_server->exit = true;
/* send a fake tick to wake up the server thread */
uint64_t v = 1;
int ret = write(time_server->tick_fd, &v, 8);
DevAssert(ret == 8);
/* wait for thread termination */
void *retval;
ret = pthread_join(time_server->thread_id, &retval);
/* free memory */
shutdown(time_server->server_socket, SHUT_RDWR);
close(time_server->server_socket);
close(time_server->tick_fd);
free(time_server);
}
static void time_server_tick_callback(void *ts)
{
time_server_private_t *time_server = ts;
uint64_t v = 1;
int ret = write(time_server->tick_fd, &v, 8);
DevAssert(ret == 8);
}
void time_server_attach_time_source(time_server_t *time_server,
time_source_t *time_source)
{
time_source_set_callback(time_source, time_server_tick_callback, time_server);
}

View File

@@ -0,0 +1,40 @@
/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.openairinterface.org/?page_id=698
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
#ifndef COMMON_UTIL_TIME_MANAGER_TIME_SERVER
#define COMMON_UTIL_TIME_MANAGER_TIME_SERVER
#include "time_source.h"
/* opaque data type */
typedef void time_server_t;
time_server_t *new_time_server(const char *ip,
int port,
void (*callback)(void *),
void *callback_data);
void free_time_server(time_server_t *time_server);
void time_server_attach_time_source(time_server_t *time_server,
time_source_t *time_source);
#endif /* COMMON_UTIL_TIME_MANAGER_TIME_SERVER */

View File

@@ -0,0 +1,263 @@
/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.openairinterface.org/?page_id=698
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
#include "time_source.h"
#include <stdlib.h>
#include <time.h>
#include <stdbool.h>
#include <errno.h>
#include "common/utils/assertions.h"
#include "common/utils/system.h"
#include "common/utils/utils.h"
typedef void (*callback_function_t)(void *callback_data);
typedef void * callback_data_t;
typedef struct {
time_source_type_t type;
volatile callback_function_t callback;
volatile callback_data_t callback_data;
_Atomic bool exit;
void (*terminate)(void *this);
pthread_t thread_id;
pthread_mutex_t mutex;
pthread_cond_t cond;
} time_source_common_t;
typedef struct {
time_source_common_t common;
} time_source_realtime_t;
typedef struct {
time_source_common_t common;
volatile uint64_t iq_samples_count;
volatile uint64_t iq_samples_per_second;
} time_source_iq_t;
static void init_mutex(time_source_common_t *t)
{
int ret = pthread_mutex_init(&t->mutex, NULL);
DevAssert(ret == 0);
}
static void init_cond(time_source_common_t *t)
{
int ret = pthread_cond_init(&t->cond, NULL);
DevAssert(ret == 0);
}
static void lock(time_source_common_t *t)
{
int ret = pthread_mutex_lock(&t->mutex);
DevAssert(ret == 0);
}
static void unlock(time_source_common_t *t)
{
int ret = pthread_mutex_unlock(&t->mutex);
DevAssert(ret == 0);
}
static void condwait(time_source_common_t *t)
{
int ret = pthread_cond_wait(&t->cond, &t->mutex);
DevAssert(ret == 0);
}
static void condsignal(time_source_common_t *t)
{
int ret = pthread_cond_signal(&t->cond);
DevAssert(ret == 0);
}
static void wait_thread_termination(time_source_common_t *t)
{
void *retval;
int ret = pthread_join(t->thread_id, &retval);
DevAssert(ret == 0);
}
static struct timespec next_ms(struct timespec t)
{
/* go to next millisecond */
t.tv_nsec += 1000000;
if (t.tv_nsec > 999999999) {
t.tv_nsec -= 1000000000;
t.tv_sec++;
}
return t;
}
static void *time_source_realtime_thread(void *ts)
{
time_source_realtime_t *time_source = ts;
struct timespec now;
if (clock_gettime(CLOCK_MONOTONIC, &now) == -1)
AssertFatal(0, "clock_gettime failed\n");
/* go to next millisecond */
now = next_ms(now);
while (!time_source->common.exit) {
/* sleep */
if (clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &now, NULL) == -1) {
/* accept interruption by signals and then simply restart the sleep */
if (errno == EINTR)
continue;
AssertFatal(0, "clock_nanosleep failed\n");
}
if (time_source->common.exit)
break;
/* tick */
lock(&time_source->common);
void (*callback)(void *callback_data) = time_source->common.callback;
void *callback_data = time_source->common.callback_data;
unlock(&time_source->common);
if (callback != NULL)
callback(callback_data);
/* go to next millisecond */
now = next_ms(now);
}
return (void *)0;
}
static void *time_source_iq_samples_thread(void *ts)
{
time_source_iq_t *time_source = ts;
while (!time_source->common.exit) {
/* wait until we have at least 1ms of IQ samples */
lock(&time_source->common);
while (!time_source->common.exit
&& (time_source->iq_samples_per_second == 0
|| time_source->iq_samples_count < time_source->iq_samples_per_second / 1000))
condwait(&time_source->common);
time_source->iq_samples_count -= time_source->iq_samples_per_second / 1000;
unlock(&time_source->common);
if (time_source->common.exit)
break;
/* tick */
lock(&time_source->common);
void (*callback)(void *callback_data) = time_source->common.callback;
void *callback_data = time_source->common.callback_data;
unlock(&time_source->common);
if (callback != NULL)
callback(callback_data);
}
return (void *)0;
}
static void time_source_iq_samples_terminate(void *ts)
{
time_source_iq_t *time_source = ts;
condsignal(&time_source->common);
}
time_source_t *new_time_source(time_source_type_t type)
{
time_source_t *ret = NULL;
void *(*thread_function)(void *) = NULL;
void (*terminate_function)(void *) = NULL;
char *thread_function_name = NULL;
switch (type) {
case TIME_SOURCE_REALTIME: {
time_source_realtime_t *ts = calloc_or_fail(1, sizeof(*ts));
thread_function = time_source_realtime_thread;
thread_function_name = "time source realtime";
ret = ts;
break;
}
case TIME_SOURCE_IQ_SAMPLES: {
time_source_iq_t *ts = calloc_or_fail(1, sizeof(*ts));
thread_function = time_source_iq_samples_thread;
terminate_function = time_source_iq_samples_terminate;
thread_function_name = "time source iq samples";
ret = ts;
break;
}
}
time_source_common_t *c = ret;
c->type = type;
init_mutex(c);
init_cond(c);
c->terminate = terminate_function;
threadCreate(&c->thread_id, thread_function, c, thread_function_name, -1, SCHED_OAI);
return ret;
}
void free_time_source(time_source_t *ts)
{
time_source_common_t *time_source = ts;
time_source->exit = true;
if (time_source->terminate != NULL)
time_source->terminate(time_source);
wait_thread_termination(time_source);
free(time_source);
}
void time_source_set_callback(time_source_t *ts,
void (*callback)(void *),
void *callback_data)
{
time_source_common_t *time_source = ts;
lock(time_source);
time_source->callback = callback;
time_source->callback_data = callback_data;
unlock(time_source);
}
void time_source_iq_add(time_source_t *ts,
uint64_t iq_samples_count,
uint64_t iq_samples_per_second)
{
if (ts == NULL)
return;
time_source_iq_t *time_source = ts;
if (time_source->common.type != TIME_SOURCE_IQ_SAMPLES)
return;
lock(&time_source->common);
time_source->iq_samples_count += iq_samples_count;
if (time_source->iq_samples_per_second == 0)
time_source->iq_samples_per_second = iq_samples_per_second;
AssertFatal(time_source->iq_samples_per_second == iq_samples_per_second, "unsupported change of value 'IQ samples per second'\n");
condsignal(&time_source->common);
unlock(&time_source->common);
}

View File

@@ -19,37 +19,29 @@
* contact@openairinterface.org
*/
/*! \file FGSRegistrationResult.h
#ifndef COMMON_UTIL_TIME_MANAGER_TIME_SOURCE
#define COMMON_UTIL_TIME_MANAGER_TIME_SOURCE
\brief 5GS Registration result for registration request procedures
\author Yoshio INOUE, Masayuki HARADA
\email: yoshio.inoue@fujitsu.com,masayuki.harada@fujitsu.com
\date 2020
\version 0.1
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include "OctetString.h"
/* opaque data type */
typedef void time_source_t;
#ifndef FGS_REGISTRATION_RESULT_H_
#define FGS_REGISTRATION_RESULT_H_
#define FGS_REGISTRATION_RESULT_3GPP 0b001
#define FGS_REGISTRATION_RESULT_NON_3GPP 0b010
#define FGS_REGISTRATION_RESULT_3GPP_AND_NON_3GPP 0b011
typedef enum {
TIME_SOURCE_REALTIME,
TIME_SOURCE_IQ_SAMPLES
} time_source_type_t;
typedef struct {
uint8_t iei;
uint8_t resultlength;
uint8_t spare: 4;
uint8_t smsallowed: 1;
uint8_t registrationresult: 3;
} FGSRegistrationResult;
time_source_t *new_time_source(time_source_type_t type);
uint16_t encode_fgs_registration_result(const FGSRegistrationResult *fgsregistrationresult);
void free_time_source(time_source_t *time_source);
int decode_fgs_registration_result(FGSRegistrationResult *fgsregistrationresult, uint8_t iei, uint16_t value, uint32_t len);
void time_source_set_callback(time_source_t *time_source,
void (*callback)(void *),
void *callback_data);
#endif /* FGS REGISTRATION RESULT_H_*/
void time_source_iq_add(time_source_t *time_source,
uint64_t iq_samples_count,
uint64_t iq_samples_per_second);
#endif /* COMMON_UTIL_TIME_MANAGER_TIME_SOURCE */

View File

@@ -38,7 +38,7 @@
int nas_sock_fd[MAX_MOBILES_PER_ENB * 2]; // Allocated for both LTE UE and NR UE.
int nas_sock_mbms_fd;
static int tun_alloc(const char *dev)
int tun_alloc(const char *dev)
{
struct ifreq ifr;
int fd, err;
@@ -157,13 +157,14 @@ static bool setInterfaceParameter(int sock_fd, const char *ifn, int af, const ch
return success;
}
/*
* \brief bring interface up (up != 0) or down (up == 0)
*/
typedef enum { INTERFACE_DOWN, INTERFACE_UP } if_action_t;
/*
* \brief bring interface up (up != 0) or down (up == 0)
*/
static bool change_interface_state(int sock_fd, const char *ifn, if_action_t if_action)
{
const char* action = if_action == INTERFACE_DOWN ? "DOWN" : "UP";
const char *action = if_action == INTERFACE_DOWN ? "DOWN" : "UP";
struct ifreq ifr = {0};
strncpy(ifr.ifr_name, ifn, sizeof(ifr.ifr_name));
@@ -189,6 +190,7 @@ fail_interface_state:
return false;
}
bool tun_config(const char* ifname, const char *ipv4, const char *ipv6)
{
AssertFatal(ipv4 != NULL || ipv6 != NULL, "need to have IP address, but none given\n");
@@ -262,3 +264,19 @@ int tun_generate_ifname(char *ifname, const char *ifprefix, int instance_id)
{
return snprintf(ifname, IFNAMSIZ, "%s%d", ifprefix, instance_id + 1);
}
int tun_generate_ue_ifname(char *ifname, int instance_id, int pdu_session_id)
{
char pdu_session_string[10];
snprintf(pdu_session_string, sizeof(pdu_session_string), "p%d", pdu_session_id);
return snprintf(ifname, IFNAMSIZ, "%s%d%s", "oaitun_ue", instance_id + 1, pdu_session_id == -1 ? "" : pdu_session_string);
}
void tun_destroy(const char *dev, int fd) {
if (change_interface_state(fd, dev, INTERFACE_DOWN) == 0) {
LOG_I(UTIL, "Interface %s is now down.\n", dev);
} else {
LOG_E(UTIL, "Could not bring interface %s down.\n", dev);
}
close(fd);
}

View File

@@ -34,6 +34,7 @@
* \param[in] instance_id unique instance number
*/
int tun_generate_ifname(char *ifname, const char *ifprefix, int instance_id);
int tun_generate_ue_ifname(char *ifname, int instance_id, int pdu_session_id);
/*!
* \brief This function initializes the TUN interface
@@ -73,4 +74,18 @@ bool tun_config(const char* ifname, const char *ipv4, const char *ipv6);
*/
void setup_ue_ipv4_route(const char* ifname, int instance_id, const char *ipv4);
/*!
* \brief This function allocates a TUN interface
* \param[in] dev name of the interface
* \return file descriptor of the allocated interface
*/
int tun_alloc(const char *dev);
/*!
* \brief This function destroys the TUN interface
* \param[in] dev name of the interface
* \param[in] fd file descriptor of the allocated interface
*/
void tun_destroy(const char *dev, int fd);
#endif /*TUN_IF_H_*/

View File

@@ -24,8 +24,11 @@ The hardware on which we have tried this tutorial:
|----------------------------------------------------------------------------|----------------------------------|--------------------------------------------------|
| Gigabyte Edge E251-U70 (Intel Xeon Gold 6240R, 2.4GHz, 24C48T, 96GB DDR4) |Ubuntu 22.04.3 LTS (5.15.0-72-lowlatency)| NVIDIA ConnectX®-6 Dx 22.38.1002 |
| Dell PowerEdge R750 (Dual Intel Xeon Gold 6336Y CPU @ 2.4G, 24C/48T (185W), 512GB RDIMM, 3200MT/s) |Ubuntu 22.04.3 LTS (5.15.0-72-lowlatency)| NVIDIA Converged Accelerator A100X (24.39.2048) |
| Supermicro Grace Hopper MGX ARS-111GL-NHR (Neoverse-V2, 3.4GHz, 72C/72T, 576GB LPDDR5) | Ubuntu 22.04.5 LTS (6.5.0-1019-nvidia-64k) |NVIDIA BlueField3 (32.41.1000)|
**NOTE**: These are not minimum hardware requirements. This is the configuration of our servers. The NIC card should support hardware PTP time stamping.
**NOTE**:
- These are not minimum hardware requirements. This is the configuration of our servers. The NIC card should support hardware PTP time stamping.
- Starting from tag [2025.w13](https://gitlab.eurecom.fr/oai/openairinterface5g/-/tree/2025.w13?ref_type=tags) of OAI, we are only testing with the Grace Hopper server.
PTP enabled switches and grandmaster clock we have tested with:
@@ -42,6 +45,7 @@ These are the radio units we've used for testing:
|-------------|------------------|
| Foxconn RPQN-7801E RU | 2.6.9r254 |
| Foxconn RPQN-7801E RU | 3.1.15_0p4 |
| Foxconn RPQN-7801E RU | v3.2.0q.551.12.E.rc2.srs-AIO |
The UEs that have been tested and confirmed working with Aerial are the following:
@@ -54,35 +58,30 @@ The UEs that have been tested and confirmed working with Aerial are the followin
| Apaltec | Tributo 5G-Dongle |
| OnePlus | Nord (AC2003) |
| Apple iPhone | 14 Pro (MQ0G3RX/A) (iOS 17.3) |
| Samsung | S23 Ultra |
## Configure your server
The first step is to obtain the NVIDIA Aerial SDK, you'll need to request access [here](https://catalog.ngc.nvidia.com/orgs/nvidia/containers/aerial-sdk).
After obtaining access to the SDK, you can refer to [this instructions page](https://docs.nvidia.com/aerial/aerial-ran-colab-ota/current/index.html) to setup the L1 components using NVIDIAs' SDK manager, or to [this instructions page](https://docs.nvidia.com/aerial/cuda-accelerated-ran/index.html) in order to setup and install the components manually.
The currently used Aerial Version is 24-1, which is also the one currently used by the SDK Manager.
To set up the L1 and install the components manually refer to this [instructions page](https://docs.nvidia.com/aerial/cuda-accelerated-ran/index.html).
**Note**:
- To configure the Gigabyte server please refer to these [instructions](https://gitlab.eurecom.fr/oai/openairinterface5g/-/blob/2025.w13/doc/Aerial_FAPI_Split_Tutorial.md)
- The last release to support the Gigabyte server is **Aerial CUDA-Accelerated RAN 24-1**.
### CPU allocation
Currently, the CPU isolation is setup the following:
| Server brand | Model | Nº of CPU Cores | Isolated CPUs |
|------------------|---------------|:---------------:|:--------------:|
| Grace Hopper MGX | ARS-111GL-NHR | 72 | 4-64 |
| Server brand | Model | Nº of CPU Cores | Isolated CPUs |
|--------------|-----------|:---------------:|:--------------:|
| Gigabyte | Edge E251-U70 | 24 | 2-10 |
**Gigabyte Edge E251-U70**
**Grace Hopper MGX ARS-111GL-NHR**
| Applicative Threads | Allocated CPUs |
|------------------------|----------------|
| Aerial L1 | 2,3,4,5,6,7,8 |
| PTP & PHC2SYS Services | 9 |
| PTP & PHC2SYS Services | 41 |
| OAI `nr-softmodem` | 13-20 |
## PTP configuration
1. You need to install the `linuxptp` debian package. It will install both ptp4l and phc2sys.
@@ -99,14 +98,10 @@ Once installed you can use this configuration file for ptp4l (`/etc/ptp4l.conf`)
domainNumber 24
slaveOnly 1
time_stamping hardware
tx_timestamp_timeout 1
logging_level 6
summary_interval 0
#priority1 127
tx_timestamp_timeout 30
[your_PTP_ENABLED_NIC]
network_transport L2
hybrid_e2e 0
```
@@ -120,7 +115,7 @@ Documentation=man:ptp4l
Restart=always
RestartSec=5s
Type=simple
ExecStart=taskset -c 9 /usr/sbin/ptp4l -f /etc/ptp.conf
ExecStart=taskset -c 41 /usr/sbin/ptp4l -f /etc/ptp.conf
[Install]
WantedBy=multi-user.target
@@ -149,17 +144,16 @@ WantedBy=multi-user.target
# Build OAI gNB
If installing with the Aerial SDK, you should already have the repository cloned in `~/openairinterface5g`, if
Clone OAI code base in a suitable repository, here we are cloning in `~/openairinterface5g` directory,
If it's not already cloned, the first step is to clone OAI repository
```bash
git clone https://gitlab.eurecom.fr/oai/openairinterface5g.git ~/openairinterface5g
cd ~/openairinterface5g/
```
## Get nvIPC sources from the L1 container
The library used for communication between L1 and L2 components is called nvIPC, and was developed by NVIDIA, as such, it is not open-source and can't be freely distributed.
In order to be able to use this library to achieve communication, we need to obtain the nvIPC source files from the L1 container (cuBB) and place it in out gNB project directory `~/openairinterface5g`.
The library used for communication between L1 and L2 components is called nvIPC, and is developed by NVIDIA. It is not open-source and can't be freely distributed.
In order to achieve this communication, we need to obtain the nvIPC source files from the L1 container (cuBB) and place it in the gNB project directory `~/openairinterface5g`.
This allows us to build and install this library when building the L2 docker container.
Check whether your L1 container is running:
@@ -177,7 +171,7 @@ cuBB
aerial@c_aerial_aerial:/opt/nvidia/cuBB#
```
After logging into the container, we need to pack the nvIPC sources and copy them to the host ( the command creates a tar.gz file with the following name format: nvipc_src.YYYY.MM.DD.tar.gz)
After logging into the container, you need to pack the nvIPC sources and copy them to the host ( the command creates a `tar.gz` file with the following name format: `nvipc_src.YYYY.MM.DD.tar.gz`)
```bash
~$ docker exec -it cuBB bash
aerial@c_aerial_aerial:/opt/nvidia/cuBB# cd cuPHY-CP/gt_common_libs
@@ -216,7 +210,7 @@ With the nvIPC sources in the project directory, the docker image can be built.
## Building OAI gNB docker image
In order to build the final image, there is an intermediary image to be built (ran-base)
In order to build the target image (`oai-gnb-aerial`), first you should build a common shared image (`ran-base`)
```bash
~$ cd ~/openairinterface5g/
~/openairinterface5g$ docker build . -f docker/Dockerfile.base.ubuntu22 --tag ran-base:latest
@@ -224,21 +218,23 @@ In order to build the final image, there is an intermediary image to be built (r
```
## Running the setup
In order to use Docker compose to automatically start and stop the setup, we need to first create a ready-made image of the L1, after compiling the L1 software and making the necessary adjustments to its configuration files.
The process of preparing the L1 is covered on NVIDIAs' documentation, and falls outside the scope of this document.
# Running the setup
### Prepare the L1 image
After preparing the L1 software, the container needs to be committed in order for an image in which the L1 is ready to be executed, and that can be referenced by a docker-compose.yaml file later:
*Note:* In preparing the L1 image, the default L1 configuration file is cuphycontroller_P5G_FXN.yaml, located in `/opt/nvidia/cuBB/cuPHY-CP/cuphycontroller/config/`.
This is the file where the RU MAC address needs to be applied before commiting the image
In order to use Docker Compose to automatically start and stop the setup, we first need to create a pre-built image of L1 after compiling the L1 software and making the necessary adjustments to its configuration files.
The process of preparing L1 is covered in NVIDIA's documentation and falls outside the scope of this document.
## Prepare the L1 image
After preparing the L1 software, the container needs to be committed to create an image where L1 is ready for execution, which can later be referenced by a `docker-compose.yaml` file.
*Note:* The default L1 configuration file is `cuphycontroller_P5G_FXN_GH.yaml`, located in `/opt/nvidia/cuBB/cuPHY-CP/cuphycontroller/config/`.
In this file the RU MAC address needs to be specified before commiting the image.
```bash
~$ docker commit nv-cubb cubb-build:24-1
~$ docker commit nv-cubb cubb-build:24-3
~$ docker image ls
..
cubb-build 24-1 824156e0334c 2 weeks ago 40.1GB
..
cubb-build 24-3 824156e0334c 2 weeks ago 23.9GB
-..
```
## Adapt the OAI-gNB configuration file to your system/workspace
@@ -255,14 +251,12 @@ The default amf_ip_address:ipv4 value is 192.168.70.132, when installing the CN5
Both 'GNB_IPV4_ADDRESS_FOR_NG_AMF' and 'GNB_IPV4_ADDRESS_FOR_NGU' need to be set to the IP address of the NIC referenced previously.
### Running docker compose
#### Aerial L1 entrypoint script
Before running docker-compose, we can check which L1 configuration file is to be used by cuphycontroller_scf.
This is set in the script [`aerial_l1_entrypoint.sh`](../ci-scripts/yaml_files/sa_gnb_aerial/aerial_l1_entrypoint.sh), which is used by the L1 container in order to start the L1 software.
The script begins by setting up some environment variables, restarting NVIDIA MPS, and finally running cuphycontroller_scf.
The L1 software is run with an argument that indicates which configuration file is to be used.
This argument may be changed by providing an argument to the [`aerial_l1_entrypoint.sh`](../ci-scripts/yaml_files/sa_gnb_aerial/aerial_l1_entrypoint.sh) call in [`docker-compose.yaml`](../ci-scripts/yaml_files/sa_gnb_aerial/docker-compose.yaml).
When no argument is provided (this is the default behaviour), it uses "P5G_FXN" as the cuphycontroller_scf argument.
## Running docker compose
### Aerial L1 entrypoint script
The `aerial_l1_entrypoint` script is used by the L1 container to start the L1 software and is called in the Docker Compose file.
It begins by setting up environment variables, restarting NVIDIA MPS, and finally running `cuphycontroller_scf`.
The L1 software is executed with an argument that specifies which configuration file to use. If not modified, the default argument is set to `P5G_FXN_GH`.
After building the gNB image, and preparing the configuration file, the setup can be run with the following command:
@@ -271,17 +265,18 @@ cd ci-scripts/yaml_files/sa_gnb_aerial/
docker compose up -d
```
This will start both containers, beginning with 'nv-cubb' and only after it being ready it starts 'oai-gnb-aerial'.
This will start both containers, beginning with `nv-cubb`, and `oai-gnb-aerial` will start only after it is ready.
The gNB logs can be followed with:
The logs can be followed using these commands:
```bash
docker logs -f oai-gnb-aerial
docker logs -f nv-cubb
```
#### Running with multiple L2s
### Running with multiple L2s
One L1 instance can support multiple L2 instances. See also the [aerial documentation](https://developer.nvidia.com/docs/gputelecom/aerial-sdk/text/cubb_quickstart/running_cubb-end-to-end.html#run-multiple-l2-instances-with-single-l1-instance) for more details.
In OAI the share memory prefix must be configured in the configuration file.
In OAI the shared memory prefix must be configured in the configuration file.
```bash
tr_s_preference = "aerial";
@@ -289,9 +284,9 @@ In OAI the share memory prefix must be configured in the configuration file.
```
### Stopping the setup
## Stopping the setup
Running the following command, will stop both containers, leaving the system ready to be run later:
Run the following command to stop and remove both containers, leaving the system ready to be restarted later:
```bash
cd ci-scripts/yaml_files/sa_gnb_aerial/
docker compose down

View File

@@ -105,7 +105,7 @@ These modes of operation are supported:
- MAC -> PHY configuration using NR FAPI P5 interface
- MAC <-> PHY data interface using FAPI P7 interface for BCH PDU, DCI PDU, PDSCH PDU
- Scheduler procedures for SIB1
- Generation of and scheduler procedures for MIB/SIB1
- Scheduler procedures for RA
- 4-Step RA
- Contention Free RA procedure
@@ -140,6 +140,7 @@ These modes of operation are supported:
- MAC scheduling of SR reception
- Intra-frequency handover
- Initial support for RedCap
- Scheduling of SIBs (2, 19)
## gNB RLC
@@ -174,7 +175,7 @@ These modes of operation are supported:
- NR RRC (38.331) Rel 17 messages using new [asn1c](https://github.com/mouse07410/asn1c)
- LTE RRC (36.331) also updated to Rel 15
- Generation of MIB/SIB1 (received from DU)
- Generation of system information (SIB2)
- RRC can configure PDCP, RLC, MAC
- Interface with GTP-U (tunnel creation/handling for S1-U (NSA), N3 (SA), F1 interfaces)
- Integration of RRC messages and procedures supporting UE 5G SA connection

View File

@@ -232,7 +232,9 @@ In the `MACRLCs` section of the gNB/DU configuration file:
In the `gNBs` section of the gNB/DU configuration file: some of the parameters
affect RRC configuration (CellGroupConfig) of a UE, and are therefore listed
here, *also they pertain to the DU*, i.e., the scheduler.
here, *also they pertain to the DU*, i.e., the scheduler. Note also that some
SIBs are configured at the DU and some at the CU; please consult the [RRC
configuration](../RRC/rrc-usage.md) as well for SIB configuration.
* `pdsch_AntennaPorts_XP` (default 1): number of XP logical antenna
ports in PDSCH (see also [`RUNMODEM.md`](../RUNMODEM.md))
@@ -248,6 +250,8 @@ here, *also they pertain to the DU*, i.e., the scheduler.
* `do_CSIRS` (default 0): flag whether to use channel-state information
reference signal (CSI-RS)
* `do_SRS` (default 0): flag whether to use sounding reference signal (SRS)
* `do_SINR` (default 0): flag whether to enable CSI reporting of SSB-SINR (introduced in rel16)
Default setting of CSI reporting quantity is SSB-RSRP.
* `min_rxtxtime` (default 2): minimum feedback time for UE to respond to
transmissions (k1 and k2 in 3GPP spec)
* `ul_prbblacklist`: PRBs that should not be used for UL scheduling. Cf with
@@ -267,6 +271,20 @@ here, *also they pertain to the DU*, i.e., the scheduler.
options are 2, 4, 6, 8, 10, 12, 32; **32 is a Rel-17 features**)
* `num_ulharq` (default 16): as `num_dlharq` for UL (other valid option is 32;
**32 is i Rel-17 feature**)
- `du_sibs` (default `[]`): list of SIBs to transmit in the cell. Currently,
SIB19 (for NTN) is supported.
| DL MIMO |`do_CSIRS`|`do_SINR`| CSI report Quantity |
| ---------------------------- | -------- | ------- | --------------------------------------------------|
| OFF (pdsch_AntennaPorts = 1) | 0 | 0 | SSB-RSRP |
| OFF (pdsch_AntennaPorts = 1) | 0 | 1 | SSB-SINR |
| OFF (pdsch_AntennaPorts = 1) | 1 | 0 | CSI-Reference signal RSRP |
| OFF (pdsch_AntennaPorts = 1) | 1 | 1 | CSI-Reference signal SINR (not supported yet) |
| ON (pdsch_AntennaPorts > 1) | 1 | 0 | cri-RI-PMI-CQI |
DL-MIMO is configured using following parameters:
`pdsch_AntennaPorts_XP` , `pdsch_AntennaPorts_N1` , `pdsch_AntennaPorts_N2`, `maxMIMO_layers`
(see also [`RUNMODEM.md`](../RUNMODEM.md))
## ServingCellConfigCommon parameters

View File

@@ -108,6 +108,7 @@ Legacy unmaintained files:
- The [shared object loader](../common/utils/DOC/loader.md)
- The [threadpool](../common/utils/threadPool/thread-pool.md) used in L1
- The [LDPC implementation](../openair1/PHY/CODING/DOC/LDPCImplementation.md) is a shared library
- The [time management](time_management.md) module
## Radios

View File

@@ -67,6 +67,9 @@ RRC-specific configuration
### cell-specific options
Note that some SIBS are configured at the CU and some at the DU; please consult
the [MAC configuration](../MAC/mac-usage.md) as well for SIB configuration.
- `gNB_ID` and `gNB_name`: ID and name of the gNB
- `tracking_area_code`: the current tracking area code in the range `[0x0001,
0xfffd]`
@@ -84,6 +87,8 @@ RRC-specific configuration
- `enable_sdap` (default: false): enable the use of the SDAP layer. If
deactivated, a transparent SDAP header is prepended to packets, but no
further processing is being done.
- `cu_sibs` (default: `[]`) list of SIBs to give to the DU for transmission.
Currently, SIB2 is supported.
### UE-specific configuration

View File

@@ -136,7 +136,15 @@ Here are some useful command line options for the NR UE:
| Parameter | Description |
|--------------------------|---------------------------------------------------------------------------------------------------------------|
| `--ue-scan-carrier` | Scan for cells in current bandwidth. This option can be used if the SSB position of the gNB is unknown. If multiple cells are detected, the UE will try to connect to the first cell. By default, this option is disabled and the UE attempts to only decode SSB given by `--ssb`. |
| `--ue-fo-compensation` | Enables the frequency offset compensation at the UE. Useful when running over the air and/or without an external clock/time source. |
| `--ue-fo-compensation` | Enables the initial frequency offset compensation at the UE. Useful when running over the air and/or without an external clock/time source. |
| `--cont-fo-comp` | Enables the continuous frequency offset (FO) estimation and compensation. Parameter value `1` specifies that the main FO contribution comes from the local oscillator's (LO) accuracy. Parameter value `2` specifies that the main FO contribution comes from Doppler shift. |
| `--initial-fo` | Sets the known initial frequency offset. Useful especially with large Doppler frequency, e.g. LEO satellite. |
| `--freq-sync-P` | Sets the coefficient for the Proportional part of the PI-controller for the continuous frequency offset compensation. Default value 0.01. |
| `--freq-sync-I` | Sets the coefficient for the Integrating part of the PI-controller for the continuous frequency offset compensation. Default value 0.001. |
| `--ntn-initial-time-drift` | Sets the initial NTN DL time drift (feeder link and service link), given in µs/s. |
| `--autonomous-ta` | Enables the autonomous TA update, based on DL drift (useful if main contribution to DL drift is movement, e.g. LEO satellite). |
| `--time-sync-P` | Sets the coefficient for the Proportional part of the PI-controller for the time synchronization. Default value 0.5. |
| `--time-sync-I` | Sets the coefficient for the Integrating part of the PI-controller for the time synchronization. Default value 0.0. |
| `--usrp-args` | Equivalent to the `sdr_addrs` field in the gNB config file. Used to identify the USRP and set some basic parameters (like the clock source). |
| `--clock-source` | Sets the clock source (internal or external). |
| `--time-source` | Sets the time source (internal or external). |
@@ -349,10 +357,16 @@ For LEO satellite scenarios, the parameter `--ntn-initial-time-drift` must be pr
This parameter provides the drift rate of the complete DL timing (incl. feeder link and service link) in µs/s.
Also, to perform an autonomous TA update based on the DL drift, the boolean parameter `--autonomous-ta` should be added in case of a LEO satellite scenario.
For LEO satellite scenario we assume the LO to be very accurate and the main FO contribution comes from Doppler shift.
Therefore, we use the command line parameter `--cont-fo-comp 2` to continuously compensate the DL Doppler and pre-compensate the UL Doppler.
The initial Doppler frequency offset must be provided via command line with the parameter `--initial-fo`.
For other information on optional NR UE command line options, please refer [here](#optional-nr-ue-command-line-options).
So an example NR UE command for FDD, 5MHz BW, 15 kHz SCS, transparent LEO satellite 5G NR NTN is this:
```
cd cmake_targets
sudo ./ran_build/build/nr-uesoftmodem -O ../targets/PROJECTS/GENERIC-NR-5GC/CONF/ue.conf --band 254 -C 2488400000 --CO -873500000 -r 25 --numerology 0 --ssb 60 --rfsim --rfsimulator.prop_delay 20 --rfsimulator.options chanmod --time-sync-I 0.1 --ntn-initial-time-drift -46 --autonomous-ta
sudo ./ran_build/build/nr-uesoftmodem -O ../targets/PROJECTS/GENERIC-NR-5GC/CONF/ue.conf --band 254 -C 2488400000 --CO -873500000 -r 25 --numerology 0 --ssb 60 --rfsim --rfsimulator.prop_delay 20 --rfsimulator.options chanmod --time-sync-I 0.1 --ntn-initial-time-drift -46 --autonomous-ta --initial-fo 57340 --cont-fo-comp 2
```
# Specific OAI modes

View File

@@ -298,7 +298,7 @@ Even, a future evolution could remove this global rlc layer: rlc can be only a l
When adding a UE, external code have to call `add_rlc_srb()` and/or `add_rlc_drb()`, to remove it: `rrc_rlc_remove_ue()`
Inside UE, channels called drd or srb can be created: ??? and deleted: rrc_rlc_config_req()
nr_rlc_tick() must be called periodically to manage the internal timers
nr_rlc_ms_tick() must be called periodically to manage the internal timers
successful_delivery() and max_retx_reached(): in ??? trigger, the RLC sends a itti message to RRC: RLC_SDU_INDICATION (neutralized by #if 0 right now)
@@ -329,7 +329,7 @@ To manage UE connections, `nr_pdcp_add_srbs()` is employed for adding UE SRBs in
## PDCP Tx flow
On the Tx side (downlink in gNB), the entry functions `nr_pdcp_data_req_drb()` and `nr_pdcp_data_req_srb()` are called by the upper layer. The upper layer could be GTP or a PDCP internal thread like `enb_tun_read_thread()`, which reads directly from the Linux socket if the 3GPP core implementation is skipped. The PDCP internals for `nr_pdcp_data_req_srb()` and `nr_pdcp_data_req_drb()` are thread-safe. Within these functions, the PDCP manager protects access to the SDU receiving function of PDCP (`recv_sdu()` callback, corresponding to `nr_pdcp_entity_recv_pdu()` for DRBs) using mutex. When necessary, the PDCP layer pushes this data to RLC by calling `rlc_data_req()`.
On the Tx side (downlink in gNB), the entry functions `nr_pdcp_data_req_drb()` and `nr_pdcp_data_req_srb()` are called by the upper layer. The upper layer could be GTP or a PDCP internal thread like `gnb_tun_read_thread()`, which reads directly from the Linux socket if the 3GPP core implementation is skipped. The PDCP internals for `nr_pdcp_data_req_srb()` and `nr_pdcp_data_req_drb()` are thread-safe. Within these functions, the PDCP manager protects access to the SDU receiving function of PDCP (`recv_sdu()` callback, corresponding to `nr_pdcp_entity_recv_pdu()` for DRBs) using mutex. When necessary, the PDCP layer pushes this data to RLC by calling `rlc_data_req()`.
## PDCP Rx flow

View File

@@ -19,10 +19,10 @@
| starsky | Starsky | eNB (B40) | b200mini (30A3E3C) |
| carabe | Carabe | UE (B7UE) | B200mini (30AE8C9) |
| avra | Avra | gNB (n78) | AW2S Jaguar (192.168.80.239) |
| aerial2 | Aerial2 | gNB (PNF/Nvidia CUBB + VNF) | Foxconn RU, _Nvidia Aerial SDK integrated_ |
| cacofonix | Cacofonix | gNB (n78, FHI7.2) | -- |
| matix | Matix | gNB (n77) | N310 |
| gracehopper1-oai | Gracehopper1 | build, gNB/Aerial | _Nvidia Aerial SDK integrated_ |
| gracehopper1-oai | Gracehopper1 | gNB (n78, PNF/Nvidia CUBB + VNF) | Foxconn RU, _Nvidia Aerial integration_ |
| gracehopper3-oai | Gracehopper3 | build | -- |
Note: The available resources, and their current usage, is indicated here:
- [Lockable resources of jenkins-oai](https://jenkins-oai.eurecom.fr/lockable-resources/):
@@ -127,7 +127,7 @@ information on how the images are built.
- build unit tests from `ci-scripts/docker/Dockerfile.unittest.ubuntu22`, and run them
- [RAN-Ubuntu-ARM-Image-Builder](https://jenkins-oai.eurecom.fr/job/RAN-Ubuntu-ARM-Image-Builder/)
~BUILD-ONLY ~4G-LTE ~5G-NR
- gracehopper1-oai: ARM Ubuntu 22 image build using docker
- gracehopper3-oai: ARM Ubuntu 22 image build using docker
- base image from `Dockerfile.base.ubuntu22`
- build image from `Dockerfile.build.ubuntu22`, followed by
- target image from `Dockerfile.gNB.ubuntu22`
@@ -199,7 +199,7 @@ information on how the images are built.
- OpenShift cluster for CN deployment and container images for gNB and UE deployment
- [RAN-SA-AERIAL-CN5G](https://jenkins-oai.eurecom.fr/job/RAN-SA-AERIAL-CN5G/)
~5G-NR
- 5G-NR SA test setup: OAI VNF + PNF/NVIDIA CUBB on Aerial2 + Foxconn RU, up2 + COTS UE (Quectel RM520N), OAI CN5G
- 5G-NR SA test setup: OAI VNF + PNF/NVIDIA CUBB on gracehopper1-oai + Foxconn RU, up2 + COTS UE (Quectel RM520N), OAI CN5G
- container images for gNB deployment
- [RAN-SA-2x2-Module-CN5G](https://jenkins-oai.eurecom.fr/view/RAN/job/RAN-SA-2x2-Module-CN5G/)
~5G-NR

View File

@@ -29,6 +29,25 @@ cd cmake_targets/ran_build/build
sudo LD_LIBRARY_PATH=. ./nr-softmodem ...
```
### Possible problems
On some systems with specific ASan versions, executing a program compiled with
ASan might fail with an error `DEADLYSIGNAL`, or simply a segmentation fault.
Note that since OAI compiles and runs some programs _during compilation_ (for
code generation), typically already the build is impacted.
The issue is related to Address Space Layout Randomization (ASLR) settings
in Linux, a security feature designed to make it more difficult for attackers
to predict the locations of processes in memory. In this case, it is possible
to avoid the bug by running
```
sudo sysctl vm.mmap_rnd_bits=30
```
which reduces the number of bits for memory mappings used to randomize mmap
base addresses. _This reduces the security of the system._
## Undefined Behavior Sanitizer (UBSAN)
[Undefined Behavior Sanitizer](https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html) (UBSAN) is a runtime undefined behavior checker. It uses compile-time instrumentation to catch undefined behavior by inserting code that performs specific checks before operations that may cause it. UBSAN can be activated with the `--sanitize-undefined` option or `-fsanitize=undefined`.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 92 KiB

After

Width:  |  Height:  |  Size: 80 KiB

View File

@@ -13,15 +13,15 @@
duplexer/.style={draw,fill=white},
]
\node[label=above:porcepix] (porcepix)
\node[label=above:cluster] (cluster)
{\includegraphics[width=1.2cm]{server}};
\node[above right=-0.8cm and 2cm of porcepix, label=above:aerial2] (aerial2)
\node[above right=-0.6cm and 2cm of cluster, label=above:gracehopper1-oai] (gracehopper1-oai)
{\includegraphics[width=1.2cm]{server}}
edge (porcepix);
\node[right=0.3cm of aerial2, label=above:Foxconn, draw,
edge (cluster);
\node[right=0.3cm of gracehopper1-oai, label=above:Foxconn, draw,
minimum width=1.7cm, minimum height=0.8cm] (foxconn)
{O-RU} edge (aerial2);
{O-RU} edge (gracehopper1-oai);
\node[below right=+0.45cm and 0.35cm of foxconn.east] (antf1)
{\includegraphics[width=0.3cm]{antenna}} edge (foxconn.east);
\node[below right=-0.1cm and 0.35cm of foxconn.east] (antf2)
@@ -31,7 +31,7 @@
\node[above right=+0.45cm and 0.35cm of foxconn.east] (antf4)
{\includegraphics[width=0.3cm]{antenna}} edge (foxconn.east);
\node[above right=+1.3cm and 2cm of porcepix, label=above:matix] (matix)
\node[above right=+1.5cm and 2cm of cluster, label=above:matix] (matix)
{\includegraphics[width=1.2cm]{server}};
\node[right=0.3cm of matix, label=above:N310] (n310a)
{\includegraphics[width=1.5cm]{n310}} edge (matix);
@@ -41,22 +41,9 @@
\node[above right=-0.1cm and 0.35cm of b77o.east] (anto2)
{\includegraphics[width=0.3cm]{antenna}} edge (b77o);
\node[below right=-0.6cm and 2cm of porcepix, label=above:obelix] (obelix)
\node[below right=-0.4cm and 2cm of cluster, label=above:cacofonix] (cacofonix)
{\includegraphics[width=1.2cm]{server}}
edge (porcepix);
\node[right=0.3cm of obelix, label=above:N310] (n310o)
{\includegraphics[width=1.5cm]{n310}} edge (obelix);
\node[right=.2cm of n310o, duplexer] (b78o) {n40} edge (n310o);
\node[below right=-0.1cm and 0.35cm of b78o.east] (anto1)
{\includegraphics[width=0.3cm]{antenna}} edge (b78o);
\node[above right=-0.1cm and 0.35cm of b78o.east] (anto2)
{\includegraphics[width=0.3cm]{antenna}} edge (b78o);
\node[below=2cm of porcepix, label=above:Cluster] (cluster)
{\includegraphics[width=1.2cm]{server}};
\node[right=2cm of cluster, label=above:cacofonix] (cacofonix)
{\includegraphics[width=1.2cm]{server}}
edge (cluster);
edge (cluster);
\node[right=0.3cm of cacofonix, label=above:VVDN, draw,
minimum width=1.7cm, minimum height=0.8cm] (vvdn)
{O-RU} edge (cacofonix);
@@ -69,7 +56,28 @@
\node[above right=+0.45cm and 0.35cm of vvdn.east] (antv4)
{\includegraphics[width=0.3cm]{antenna}} edge (vvdn.east);
\node[above right=+0.0cm and 5.0cm of n310o.east, label=above:RM520N-GL] (quectel)
\node[below=2cm of cluster, label=above:porcepix] (porcepix)
{\includegraphics[width=1.2cm]{server}};
\node[right=2cm of porcepix, label=above:obelix] (obelix)
{\includegraphics[width=1.2cm]{server}}
edge (porcepix);
\node[right=0.3cm of obelix, label=above:N310] (n310o)
{\includegraphics[width=1.5cm]{n310}} edge (obelix);
\node[right=.2cm of n310o, duplexer] (b78o) {n40} edge (n310o);
\node[below right=-0.1cm and 0.35cm of b78o.east] (anto1)
{\includegraphics[width=0.3cm]{antenna}} edge (b78o);
\node[above right=-0.1cm and 0.35cm of b78o.east] (anto2)
{\includegraphics[width=0.3cm]{antenna}} edge (b78o);
%\node[right=0.3cm of cacofonix, label=above:N310] (n310o)
% {\includegraphics[width=1.5cm]{n310}} edge (cacofonix);
\node[right=.2cm of n310o, duplexer] (b78o) {n40} edge (n310o);
\node[below right=-0.1cm and 0.35cm of b78o.east] (anto1)
{\includegraphics[width=0.3cm]{antenna}} edge (b78o);
\node[above right=-0.1cm and 0.35cm of b78o.east] (anto2)
{\includegraphics[width=0.3cm]{antenna}} edge (b78o);
\node[above right=+0.0cm and 5.0cm of vvdn.east, label=above:RM520N-GL] (quectel)
{\includegraphics[height=1.2cm]{quectel}};
\node[above left=-0.1cm and 0.8cm of quectel.west] (aq2)
{\includegraphics[width=0.3cm]{antenna}} edge (quectel);

286
doc/time_management.md Normal file
View File

@@ -0,0 +1,286 @@
# Time management module
[[_TOC_]]
## Introduction
Several parts of the 4G/5G protocol stack keep track of time, for example
for timeout events (in RRC, PDCP, or RLC for example).
The obvious simple way to deal with time is to use the computer's realtime
clock. This works well when we run the stack in realtime, with real
radio equipment. But in simulation the system may run faster or slower
than realtime, so we need another way to deal with time. A good solution
is then to base time on the current IQ data processed by the system. So time
becomes "IQ samples" based, not realtime.
The system may be decomposed into various components (CU, DU, RU, various
UEs) running either in realtime or simulation. Time may be distributed
to all these components from only one time source for accurate execution
of the scenario, or each component may have its own time source, either
based on realtime or IQ samples.
## Overall structure
The time management module is made of three main parts. The first one
is the time source. It can either be realtime or "IQ samples" based.
In one running scenario of the system, there can be one or more time
sources (imagine one for the gNB, and one per each UE). There can also
be only one time source, shared between various users of the time
manager.
The second part is the server. One server is attached to one time source
and receives a tick every millisecond (real or simulated) from the time
source. It distributes the tick to the various connected clients.
And finally the last part is the client, which connects to a single server
and receives ticks from it. The client does not have a time source attached
to it. It gets the time from the server.
It is possible for a program (monolithic gNB for example) to have only
a time source, without server or client.
## Configuration
To configure the time management module, add the following section in
the configuration file.
```
time_management = {
#valid time sources: realtime, iq_samples
time_source = realtime
#valid modes: standalone, server, client
mode = standalone
#set ip/port of server (for server mode, this is the address/port to bind to;
#for client mode, this is the address/port to connect to)
server_ip = "127.0.0.1"
server_port = 7374
}
```
If not set in the configuration file, some default is applied, depending
on the program run. (See the function `time_manager_start()` for details.)
It is also possible to pass configuration parameters on the command line.
For example add `--time_manager.time_source iq_samples` to the command line
to set the time source to `iq_samples`. The same syntax is applicable to the
other parameters.
## Examples
Here come some examples of configuration for typical use cases of OAI.
### Monolithic gNB realtime
This is a very simple example. The gNB runs in `standalone` mode with
a `realtime` time source.
```
time_management = {
time_source = realtime
mode = standalone
}
```
### Monolithic gNB "IQ samples time"
For this configuration, it's the same as the previous example, but the
time source is now `iq_samples`.
```
time_management = {
time_source = iq_samples
mode = standalone
}
```
### RF simulator CU/DU "IQ samples time"
Let's suppose the CU runs on a machine with IP address 1.2.3.4 and the DU
runs on a machine with IP address 10.11.12.13.
The DU has the time source and so is the server. The CU acts as a client.
Here comes the configuration for the CU.
```
time_management = {
mode = client
server_ip = "10.11.12.13"
server_port = 7374
}
```
And here is the configuration for the DU.
```
time_management = {
time_source = iq_samples
mode = server
server_ip = "10.11.12.13"
server_port = 7374
}
```
### RF simulator CU/DU "realtime"
Let's suppose the CU runs on a machine with IP address 1.2.3.4 and the DU
runs on a machine with IP address 10.11.12.13.
CU and DU can both run in standalone mode, with no exchange between them.
Any one of them can also acts as a server and the other one as a client.
Let's suppose for this example that the CU and the DU run in client/server
mode. Let's set the CU as the time source and so as the server. The DU acts
as a client.
Here comes the configuration for the CU.
```
time_management = {
time_source = realtime
mode = server
server_ip = "1.2.3.4"
server_port = 7374
}
```
And here is the configuration for the DU.
```
time_management = {
mode = client
server_ip = "1.2.3.4"
server_port = 7374
}
```
Note that the time management module is flexible. It's possible to
configure more complex setups. For example the time source can be a gNB
and several UEs can be connected as client.
One could also write a simple program acting as a time source and distributing
time to everyone (one or several gNB, several UEs).
## Programming API
Here comes the internal API of the time manager module. The (simpler) OAI
API is described afterwards.
### Time source
- opaque type: `time_source_t`
- create a time source:
```
time_source_t *new_time_source(time_source_type_t type)
```
where `type` is one of:
* `TIME_SOURCE_REALTIME`
* `TIME_SOURCE_IQ_SAMPLES`
- delete a time source:
```
void free_time_source(time_source_t *time_source)
```
- set millisecond callback:
```
void time_source_set_callback(time_source_t *time_source,
void (*callback)(void *),
void *callback_data)
```
where `callback` is a function that will be called for each millisecond using
the provided `callback_data`
- for IQ samples time management, the user of the time source must call
this function when it processes IQ samples:
```
void time_source_iq_add(time_source_t *time_source,
uint64_t iq_samples_count,
uint64_t iq_samples_per_second)
```
then the time source will generate a tick for each block for IQ samples
lasting one millisecond. (If you pass `iq_samples_count` lasting several
milliseconds then the time source will generate several ticks in a row,
one for each millisecond.)
### Server
- opaque type: `time_server_t`
- create a server:
```
time_server_t *new_time_server(const char *ip,
int port,
void (*callback)(void *),
void *callback_data)
```
where ip/port is the IP adress and port the server will listen to
and `callback` is a function that will be called for each millisecond using
the provided `callback_data`
- delete a server:
```
void free_time_server(time_server_t *time_server)
```
- attach a time source:
```
void time_server_attach_time_source(time_server_t *time_server,
time_source_t *time_source)
```
### Client
- opaque type: `time_client_t`
- create a client:
```
time_client_t *new_time_client(const char *server_ip,
int server_port,
void (*callback)(void *),
void *callback_data)
```
the client will connect on given server ip/port
and use the `callback` with `callback_data` for each tick received
- delete a client:
```
void free_time_client(time_client_t *time_client)
```
### Threading
Each component (time source, server, client) runs its own thread which will
be the one calling the configured callback. Be careful of using proper
synchronization techniques in your callback to have correct behavior with
the other threads of the program.
## OAI API
OAI uses the time manager through a simplified API. All the code is contained
in `time_manager.c`, together with some global variables (so that there is no
need to pass objects around, to limit risks of misuse).
Each program (for example: gnb, ue, cu, du) calls the `time_manager_start()`
function, passing the callback functions to be called when a tick is generated
(several callbacks can be passed) and a default time source. Based on this,
`time_manager_start()` initializes what is needed.
The function `time_manager_iq_samples()` is to be called by programs that
read IQ samples. It is called unconditionally. It may do nothing if the
configuration is to use realtime ticks and not IQ samples ticks.
When the program exits, it calls `timer_manager_finish()` which in turns stops
the various threads created by `time_manager_start()` and releases all the
allocated data.
Here comes the API.
- init the time manager:
```
void time_manager_start(time_manager_tick_function_t *tick_functions,
int tick_functions_count,
time_source_type_t time_source);
```
(for valid values of `client_type` and `running_mode` see in
`time_manager.h`)
- update IQ samples' based time manager:
```
void time_manager_iq_samples(uint64_t iq_samples_count,
uint64_t iq_samples_per_second)
```
- terminate the time manager:
```
void time_manager_finish(void)
```

View File

@@ -31,6 +31,7 @@
#include "openair2/F1AP/f1ap_ids.h"
#include "openair2/GNB_APP/gnb_config.h"
#include "nr_pdcp/nr_pdcp_oai_api.h"
#include "common/utils/time_manager/time_manager.h"
RAN_CONTEXT_t RC;
THREAD_STRUCT thread_struct;
@@ -139,6 +140,16 @@ int main(int argc, char **argv)
exit_fun("[SOFTMODEM] Error, configuration module init failed\n");
}
logInit();
// start time manager with some reasonable default for the running mode
// (may be overwritten in configuration file or command line)
void nr_pdcp_ms_tick(void);
time_manager_tick_function_t tick_functions[] = {
nr_pdcp_ms_tick
};
int tick_functions_count = 1;
time_manager_start(tick_functions, tick_functions_count, TIME_SOURCE_REALTIME);
// strdup to put the sring in the core file for post mortem identification
LOG_I(HW, "Version: %s\n", strdup(OAI_PACKAGE_VERSION));
set_softmodem_sighandler();
@@ -177,6 +188,8 @@ int main(int argc, char **argv)
printf("TYPE <CTRL-C> TO TERMINATE\n");
itti_wait_tasks_end(NULL);
time_manager_finish();
logClean();
printf("Bye.\n");
return 0;

View File

@@ -31,54 +31,45 @@
*/
#define _GNU_SOURCE
#include <pthread.h>
#undef MALLOC //there are two conflicting definitions, so we better make sure we don't use it at all
#include "assertions.h"
#include <common/utils/LOG/log.h>
#include <common/utils/system.h>
#include "PHY/types.h"
#include "PHY/INIT/nr_phy_init.h"
#include "PHY/defs_gNB.h"
#include "SCHED_NR/sched_nr.h"
#include "SCHED_NR/fapi_nr_l1.h"
#include "PHY/NR_TRANSPORT/nr_transport_proto.h"
#include "PHY/MODULATION/nr_modulation.h"
#include "PHY/NR_TRANSPORT/nr_dlsch.h"
#include "openair2/NR_PHY_INTERFACE/nr_sched_response.h"
#include "LAYER2/NR_MAC_gNB/mac_proto.h"
#include "radio/COMMON/common_lib.h"
#include "PHY/LTE_TRANSPORT/if4_tools.h"
#include "PHY/phy_extern.h"
#include "common/ran_context.h"
#include "RRC/LTE/rrc_extern.h"
#include "PHY_INTERFACE/phy_interface.h"
#include <fcntl.h> // for SEEK_SET
#include <pthread.h> // for pthread_join
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include "common/utils/LOG/log.h"
#include "common/utils/system.h"
#include "PHY/NR_ESTIMATION/nr_ul_estimation.h"
#include "openair1/PHY/NR_TRANSPORT/nr_dlsch.h"
#include "openair1/PHY/NR_TRANSPORT/nr_ulsch.h"
#include "NR_PHY_INTERFACE/NR_IF_Module.h"
#include "PHY/INIT/nr_phy_init.h"
#include "PHY/MODULATION/nr_modulation.h"
#include "PHY/NR_TRANSPORT/nr_transport_proto.h"
#include "PHY/TOOLS/tools_defs.h"
#include "PHY/defs_RU.h"
#include "PHY/defs_common.h"
#include "PHY/defs_gNB.h"
#include "PHY/defs_nr_common.h"
#include "PHY/impl_defs_nr.h"
#include "SCHED_NR/fapi_nr_l1.h"
#include "SCHED_NR/phy_frame_config_nr.h"
#include "SCHED_NR/sched_nr.h"
#include "assertions.h"
#include "common/ran_context.h"
#include "common/utils/LOG/log.h"
#include "UTIL/OTG/otg_tx.h"
#include "UTIL/OTG/otg_externs.h"
#include "common/utils/LOG/vcd_signal_dumper.h"
#include "UTIL/OPT/opt.h"
#include "gnb_paramdef.h"
#include <executables/softmodem-common.h>
#include "T.h"
#include "nfapi/oai_integration/vendor_ext.h"
#include "executables/softmodem-common.h"
#include <nfapi/oai_integration/nfapi_pnf.h>
#include <openair1/PHY/NR_TRANSPORT/nr_ulsch.h>
#include <openair1/PHY/NR_TRANSPORT/nr_dlsch.h>
#include <PHY/NR_ESTIMATION/nr_ul_estimation.h>
#include "nfapi/oai_integration/vendor_ext.h"
#include "nfapi_nr_interface_scf.h"
#include "notified_fifo.h"
#include "openair2/NR_PHY_INTERFACE/nr_sched_response.h"
#include "thread-pool.h"
#include "time_meas.h"
#include "utils.h"
// #define USRP_DEBUG 1
#include "executables/thread-common.h"
//#define TICK_TO_US(ts) (ts.diff)
#define TICK_TO_US(ts) (ts.trials==0?0:ts.diff/ts.trials)
#define L1STATSSTRLEN 16384
static void rx_func(processingData_L1_t *param);

View File

@@ -47,6 +47,7 @@
#include "SCHED_NR/sched_nr.h"
#include "common/utils/LOG/log.h"
#include "common/utils/time_manager/time_manager.h"
#include "common/utils/LOG/vcd_signal_dumper.h"
#include <executables/softmodem-common.h>
@@ -1249,6 +1250,10 @@ void *ru_thread(void *param)
slot++;
}
// pretend we have 1 iq sample per slot
// and so slots_per_frame * 100 iq samples per second (1 frame being 10ms)
time_manager_iq_samples(1, fp->slots_per_frame * 100);
// synchronization on input FH interface, acquire signals/data and block
LOG_D(PHY,"[RU_thread] read data: frame_rx = %d, tti_rx = %d\n", frame, slot);

View File

@@ -28,6 +28,7 @@
#include "PHY/TOOLS/smbv.h"
unsigned short config_frames[4] = {2,9,11,13};
#endif
#include "common/utils/time_manager/time_manager.h"
#ifdef ENABLE_AERIAL
#include "nfapi/oai_integration/aerial/fapi_nvIPC.h"
#endif
@@ -85,6 +86,7 @@ unsigned short config_frames[4] = {2,9,11,13};
#include "utils.h"
#include "x2ap_eNB.h"
#include "openair1/SCHED_NR/sched_nr.h"
#include "openair2/SDAP/nr_sdap/nr_sdap.h"
pthread_cond_t nfapi_sync_cond;
pthread_mutex_t nfapi_sync_mutex;
@@ -464,23 +466,14 @@ int start_L1L2(module_id_t gnb_id)
NR_BCCH_BCH_Message_t *mib = mac->common_channels[0].mib;
const NR_BCCH_DL_SCH_Message_t *sib1 = mac->common_channels[0].sib1;
/* update existing config in F1 Setup request structures */
f1ap_setup_req_t *sr = mac->f1_config.setup_req;
DevAssert(sr->num_cells_available == 1);
f1ap_served_cell_info_t *info = &sr->cell[0].info;
DevAssert(info->mode == F1AP_MODE_TDD);
/* update existing config in F1 Setup request structures */
DevAssert(scc->tdd_UL_DL_ConfigurationCommon != NULL);
info->tdd = read_tdd_config(scc); /* updates radio config */
/* send gNB-DU configuration update to RRC */
f1ap_gnb_du_configuration_update_t update = {
.transaction_id = 1,
.num_cells_to_modify = 1,
};
update.cell_to_modify[0].old_nr_cellid = info->nr_cellid;
update.cell_to_modify[0].info = *info;
update.cell_to_modify[0].sys_info = get_sys_info(mib, sib1);
mac->mac_rrc.gnb_du_configuration_update(&update);
prepare_du_configuration_update(mac, info, mib, sib1);
init_NR_RU(config_get_if(), NULL);
@@ -506,11 +499,8 @@ static void wait_nfapi_init(char *thread_name)
}
void init_pdcp(void) {
uint32_t pdcp_initmask = IS_SOFTMODEM_NOS1 ? ENB_NAS_USE_TUN_BIT : LINK_ENB_PDCP_TO_GTPV1U_BIT;
if (!NODE_IS_DU(get_node_type())) {
nr_pdcp_layer_init();
nr_pdcp_module_init(pdcp_initmask, 0);
}
}
@@ -529,9 +519,9 @@ static void initialize_agent(ngran_node_t node_type, e2_agent_args_t oai_args)
const gNB_RRC_INST* rrc = RC.nrrrc[0];
assert(rrc != NULL && "rrc cannot be NULL");
const int mcc = rrc->configuration.mcc[0];
const int mnc = rrc->configuration.mnc[0];
const int mnc_digit_len = rrc->configuration.mnc_digit_length[0];
const int mcc = rrc->configuration.plmn[0].mcc;
const int mnc = rrc->configuration.plmn[0].mnc;
const int mnc_digit_len = rrc->configuration.plmn[0].mnc_digit_length;
// const ngran_node_t node_type = rrc->node_type;
int nb_id = 0;
int cu_du_id = 0;
@@ -608,8 +598,6 @@ int main( int argc, char **argv ) {
char *pckg = strdup(OAI_PACKAGE_VERSION);
LOG_I(HW, "Version: %s\n", pckg);
// don't create if node doesn't connect to RRC/S1/GTP
const ngran_node_t node_type = get_node_type();
// Init RAN context
if (!(CONFIG_ISFLAGSET(CONFIG_ABORT)))
NRRCConfig();
@@ -624,6 +612,9 @@ int main( int argc, char **argv ) {
RCconfig_nr_prs();
}
// don't create if node doesn't connect to RRC/S1/GTP
const ngran_node_t node_type = get_node_type();
if (NFAPI_MODE != NFAPI_MODE_PNF) {
int ret = create_gNB_tasks(node_type, uniqCfg);
AssertFatal(ret == 0, "cannot create ITTI tasks\n");
@@ -638,6 +629,38 @@ int main( int argc, char **argv ) {
pthread_mutex_init(&sync_mutex, NULL);
}
// start time manager with some reasonable default for the running mode
// (may be overwritten in configuration file or command line)
void nr_pdcp_ms_tick(void);
void x2ap_ms_tick();
void nr_rlc_ms_tick(void);
time_manager_tick_function_t tick_functions[3];
int tick_functions_count = 0;
if (NODE_IS_MONOLITHIC(node_type)) {
/* monolithic */
tick_functions[tick_functions_count++] = nr_pdcp_ms_tick;
tick_functions[tick_functions_count++] = nr_rlc_ms_tick;
/* x2ap is enabled when in NSA mode */
if (get_softmodem_params()->nsa)
tick_functions[tick_functions_count++] = x2ap_ms_tick;
} else if (NODE_IS_CU(node_type)) {
/* CU */
tick_functions[tick_functions_count++] = nr_pdcp_ms_tick;
/* x2ap is enabled when in NSA mode */
if (get_softmodem_params()->nsa)
tick_functions[tick_functions_count++] = x2ap_ms_tick;
} else {
/* DU */
tick_functions[tick_functions_count++] = nr_rlc_ms_tick;
}
time_manager_start(tick_functions, tick_functions_count,
// iq_samples time source for monolithic/du with rfsim,
// realtime time source for other cases
IS_SOFTMODEM_RFSIM
&& (NODE_IS_MONOLITHIC(node_type) || NODE_IS_DU(node_type))
? TIME_SOURCE_IQ_SAMPLES
: TIME_SOURCE_REALTIME);
// start the main threads
number_of_cards = 1;
@@ -740,6 +763,8 @@ int main( int argc, char **argv ) {
pthread_cond_destroy(&nfapi_sync_cond);
pthread_mutex_destroy(&nfapi_sync_mutex);
time_manager_finish();
free(pckg);
logClean();
printf("Bye.\n");

View File

@@ -41,6 +41,8 @@
#include "instrumentation.h"
#include "common/utils/threadPool/notified_fifo.h"
#include "position_interface.h"
#include "nr_phy_common.h"
#include "common/utils/time_manager/time_manager.h"
/*
* NR SLOT PROCESSING SEQUENCE
@@ -104,7 +106,9 @@ static void *NRUE_phy_stub_standalone_pnf_task(void *arg);
static void start_process_slot_tx(void* arg) {
notifiedFIFO_elt_t *newTx = arg;
nr_rxtx_thread_data_t *curMsgTx = NotifiedFifoData(newTx);
pushNotifiedFIFO(&curMsgTx->UE->ul_actor.fifo, newTx);
int num_ul_actors_to_use =
get_nrUE_params()->num_ul_actors == 0 ? NUM_UL_ACTORS : min(get_nrUE_params()->num_ul_actors, NUM_UL_ACTORS);
pushNotifiedFIFO(&curMsgTx->UE->ul_actors[curMsgTx->proc.nr_slot_tx % num_ul_actors_to_use].fifo, newTx);
}
static size_t dump_L1_UE_meas_stats(PHY_VARS_NR_UE *ue, char *output, size_t max_len)
@@ -436,9 +440,13 @@ static void UE_synch(void *arg) {
((ret.rx_offset << 1) / fp->samples_per_subframe * fp->slots_per_subframe)
+ round((float)((ret.rx_offset << 1) % fp->samples_per_subframe) / fp->samples_per_slot0);
// rerun with new cell parameters and frequency-offset
// todo: the freq_offset computed on DL shall be scaled before being applied to UL
nr_rf_card_config_freq(cfg0, ul_carrier, dl_carrier, freq_offset);
if (get_nrUE_params()->cont_fo_comp) {
UE->freq_offset = freq_offset;
} else {
// rerun with new cell parameters and frequency-offset
nr_rf_card_config_freq(cfg0, ul_carrier, dl_carrier, freq_offset);
UE->rfdevice.trx_set_freq_func(&UE->rfdevice, cfg0);
}
if (get_nrUE_params()->agc) {
nr_ue_adjust_rx_gain(UE, cfg0, UE->adjust_rxgain);
@@ -452,7 +460,6 @@ static void UE_synch(void *arg) {
cfg0->rx_freq[0],
cfg0->tx_freq[0]);
UE->rfdevice.trx_set_freq_func(&UE->rfdevice, cfg0);
UE->is_synchronized = 1;
} else {
int gain_change = 0;
@@ -551,6 +558,15 @@ static void RU_write(nr_rxtx_thread_data_t *rxtxD, bool sl_tx_action)
writeBlockSize -= dummyBlockSize;
}
// pre-compensate UL frequency offset
if (flags != TX_BURST_INVALID && get_nrUE_params()->cont_fo_comp) {
double ul_freq_offset = -UE->freq_offset * ((double)fp->ul_CarrierFreq / (double)fp->dl_CarrierFreq);
if (get_nrUE_params()->cont_fo_comp == 2) // different from LO frequency error compensation, Doppler UL pre-compensation has to be negative
ul_freq_offset = -ul_freq_offset;
for (int i = 0; i < fp->nb_antennas_tx; i++)
nr_fo_compensation(ul_freq_offset, fp->samples_per_subframe, writeTimestamp, txp[i], txp[i], writeBlockSize);
}
int tmp = openair0_write_reorder(&UE->rfdevice, writeTimestamp, txp, writeBlockSize, fp->nb_antennas_tx, flags);
AssertFatal(tmp == writeBlockSize, "");
}
@@ -585,6 +601,7 @@ void processSlotTX(void *arg)
UE->if_inst->sl_indication(&sl_indication);
stop_meas(&UE->ue_ul_indication_stats);
}
dynamic_barrier_join(rxtxD->next_barrier);
if (phy_data.sl_tx_action) {
@@ -611,12 +628,14 @@ void processSlotTX(void *arg)
UE->if_inst->ul_indication(&ul_indication);
stop_meas(&UE->ue_ul_indication_stats);
}
dynamic_barrier_join(rxtxD->next_barrier);
phy_procedures_nrUE_TX(UE, proc, &phy_data);
}
} else {
dynamic_barrier_join(rxtxD->next_barrier);
}
RU_write(rxtxD, sl_tx_action);
dynamic_barrier_join(rxtxD->next_barrier);
TracyCZoneEnd(ctx);
}
@@ -656,7 +675,9 @@ static int handle_sync_req_from_mac(PHY_VARS_NR_UE *UE)
for (int i = 0; i < NUM_DL_ACTORS; i++) {
flush_actor(UE->dl_actors + i);
}
flush_actor(&UE->ul_actor);
for (int i = 0; i < NUM_UL_ACTORS; i++) {
flush_actor(UE->ul_actors + i);
}
clean_UE_harq(UE);
UE->is_synchronized = 0;
@@ -686,16 +707,6 @@ static int UE_dl_preprocessing(PHY_VARS_NR_UE *UE,
process_msg_rcc_to_mac(msg);
} while (msg);
if (IS_SOFTMODEM_NOS1 || IS_SA_MODE(get_softmodem_params())) {
/* send tick to RLC and PDCP every ms */
if (proc->nr_slot_rx % fp->slots_per_subframe == 0) {
void nr_rlc_tick(int frame, int subframe);
void nr_pdcp_tick(int frame, int subframe);
nr_rlc_tick(proc->frame_rx, proc->nr_slot_rx / fp->slots_per_subframe);
nr_pdcp_tick(proc->frame_rx, proc->nr_slot_rx / fp->slots_per_subframe);
}
}
if (UE->if_inst)
UE->if_inst->slot_indication(UE->Mod_id, false);
@@ -861,13 +872,12 @@ static inline int get_readBlockSize(uint16_t slot, NR_DL_FRAME_PARMS *fp) {
static inline void apply_ntn_config(PHY_VARS_NR_UE *UE,
NR_DL_FRAME_PARMS *fp,
int slot_nr,
bool *update_ntn_system_information,
int *duration_rx_to_tx,
int *timing_advance,
int *ntn_koffset)
{
if (*update_ntn_system_information) {
*update_ntn_system_information = false;
if (UE->ntn_config_message->update) {
UE->ntn_config_message->update = false;
double total_ta_ms = UE->ntn_config_message->ntn_config_params.ntn_total_time_advance_ms;
UE->timing_advance = fp->samples_per_subframe * total_ta_ms;
@@ -913,6 +923,8 @@ void *UE_thread(void *arg)
if (usrp_tx_thread == 1)
UE->rfdevice.trx_write_init(&UE->rfdevice);
InitSinLUT();
notifiedFIFO_t nf;
initNotifiedFIFO(&nf);
@@ -947,13 +959,9 @@ void *UE_thread(void *arg)
double ntn_init_time_drift = get_nrUE_params()->ntn_init_time_drift;
int ntn_koffset = 0;
int duration_rx_to_tx = NR_UE_CAPABILITY_SLOT_RX_TO_TX;
int nr_slot_tx_offset = 0;
bool update_ntn_system_information = false;
while (!oai_exit) {
nr_slot_tx_offset = 0;
if (syncRunning) {
notifiedFIFO_elt_t *res = pollNotifiedFIFO(&nf);
@@ -1078,11 +1086,9 @@ void *UE_thread(void *arg)
absolute_slot++;
TracyCFrameMark;
if (UE->ntn_config_message->update) {
UE->ntn_config_message->update = false;
update_ntn_system_information = true;
nr_slot_tx_offset = UE->ntn_config_message->ntn_config_params.cell_specific_k_offset << fp->numerology_index;
}
// pretend we have 1 iq sample per slot
// and so nb_slot_frame * 100 iq samples per second (1 frame being 10ms)
time_manager_iq_samples(1, nb_slot_frame * 100);
int slot_nr = absolute_slot % nb_slot_frame;
nr_rxtx_thread_data_t curMsg = {0};
@@ -1173,8 +1179,8 @@ void *UE_thread(void *arg)
shiftForNextFrame = ret;
pushNotifiedFIFO(&UE->dl_actors[curMsg.proc.nr_slot_rx % NUM_DL_ACTORS].fifo, newRx);
// apply new duration next run to avoid thread dead lock
apply_ntn_config(UE, fp, slot_nr, &update_ntn_system_information, &duration_rx_to_tx, &timing_advance, &ntn_koffset);
// apply new NTN timing information
apply_ntn_config(UE, fp, slot_nr, &duration_rx_to_tx, &timing_advance, &ntn_koffset);
// Start TX slot processing here. It runs in parallel with RX slot processing
// in current code, DURATION_RX_TO_TX constant is the limit to get UL data to encode from a RX slot
@@ -1185,7 +1191,6 @@ void *UE_thread(void *arg)
curMsgTx->writeBlockSize = writeBlockSize;
curMsgTx->proc.timestamp_tx = writeTimestamp;
curMsgTx->UE = UE;
curMsgTx->proc.nr_slot_tx_offset = nr_slot_tx_offset;
int slot = curMsgTx->proc.nr_slot_tx;
int slot_and_frame = slot + curMsgTx->proc.frame_tx * UE->frame_parms.slots_per_frame;

View File

@@ -60,6 +60,7 @@
unsigned short config_frames[4] = {2,9,11,13};
#endif
#include "common/utils/LOG/log.h"
#include "common/utils/time_manager/time_manager.h"
#include "common/utils/LOG/vcd_signal_dumper.h"
#include "UTIL/OPT/opt.h"
@@ -224,6 +225,8 @@ void set_options(int CC_id, PHY_VARS_NR_UE *UE){
UE->chest_freq = nrUE_params.chest_freq;
UE->chest_time = nrUE_params.chest_time;
UE->no_timing_correction = nrUE_params.no_timing_correction;
UE->initial_fo = nrUE_params.initial_fo;
UE->cont_fo_comp = nrUE_params.cont_fo_comp;
LOG_I(PHY,"Set UE_fo_compensation %d, UE_scan_carrier %d, UE_no_timing_correction %d \n, chest-freq %d, chest-time %d\n",
UE->UE_fo_compensation, UE->UE_scan_carrier, UE->no_timing_correction, UE->chest_freq, UE->chest_time);
@@ -299,25 +302,10 @@ void init_openair0()
static void init_pdcp(int ue_id)
{
uint32_t pdcp_initmask = (!IS_SOFTMODEM_NOS1) ? LINK_ENB_PDCP_TO_GTPV1U_BIT : LINK_ENB_PDCP_TO_GTPV1U_BIT;
/*if (IS_SOFTMODEM_RFSIM || (nfapi_getmode()==NFAPI_UE_STUB_PNF)) {
pdcp_initmask = pdcp_initmask | UE_NAS_USE_TUN_BIT;
}*/
// previous code was:
// if (IS_SOFTMODEM_NOKRNMOD)
// pdcp_initmask = pdcp_initmask | UE_NAS_USE_TUN_BIT;
// The kernel module (KRNMOD) has been removed from the project, so the 'if'
// was removed but the flag 'pdcp_initmask' was kept, as "no kernel module"
// was always set. further refactoring could take it out
pdcp_initmask = pdcp_initmask | UE_NAS_USE_TUN_BIT;
if (get_softmodem_params()->nsa && nr_rlc_module_init(0) != 0) {
LOG_I(RLC, "Problem at RLC initiation \n");
}
nr_pdcp_layer_init();
nr_pdcp_module_init(pdcp_initmask, ue_id);
}
// Stupid function addition because UE itti messages queues definition is common with eNB
@@ -447,6 +435,11 @@ int main(int argc, char **argv)
}
}
if (create_tasks_nrue(1) < 0) {
printf("cannot create ITTI tasks\n");
exit(-1); // need a softer mode
}
int mode_offset = get_softmodem_params()->nsa ? NUMBER_OF_UE_MAX : 1;
uint16_t node_number = get_softmodem_params()->node_number;
ue_id_g = (node_number == 0) ? 0 : node_number - 2;
@@ -465,9 +458,20 @@ int main(int argc, char **argv)
get_channel_model_mode(uniqCfg);
}
// Delay to allow the convergence of the IIR filter on PRACH noise measurements at gNB side
if (IS_SOFTMODEM_RFSIM && !get_softmodem_params()->phy_test)
sleep(3);
// start time manager with some reasonable default for the running mode
// (may be overwritten in configuration file or command line)
void nr_pdcp_ms_tick(void);
void nr_rlc_ms_tick(void);
time_manager_tick_function_t tick_functions[] = {
nr_pdcp_ms_tick,
nr_rlc_ms_tick
};
int tick_functions_count = 2;
time_manager_start(tick_functions, tick_functions_count,
// iq_samples time source for rfsim,
// realtime time source if not
IS_SOFTMODEM_RFSIM ? TIME_SOURCE_IQ_SAMPLES
: TIME_SOURCE_REALTIME);
if (!get_softmodem_params()->nsa && get_softmodem_params()->emulate_l1)
start_oai_nrue_threads();
@@ -508,7 +512,9 @@ int main(int argc, char **argv)
for (int i = 0; i < NUM_DL_ACTORS; i++) {
init_actor(&UE[CC_id]->dl_actors[i], "DL_", -1);
}
init_actor(&UE[CC_id]->ul_actor, "UL_", -1);
for (int i = 0; i < NUM_UL_ACTORS; i++) {
init_actor(&UE[CC_id]->ul_actors[i], "UL_", -1);
}
init_nr_ue_vars(UE[CC_id], inst);
if (UE[CC_id]->sl_mode) {
@@ -553,11 +559,6 @@ int main(int argc, char **argv)
// wait for end of program
printf("TYPE <CTRL-C> TO TERMINATE\n");
if (create_tasks_nrue(1) < 0) {
printf("cannot create ITTI tasks\n");
exit(-1); // need a softer mode
}
// Sleep a while before checking all parameters have been used
// Some are used directly in external threads, asynchronously
sleep(2);
@@ -578,7 +579,9 @@ int main(int argc, char **argv)
for (int CC_id = 0; CC_id < MAX_NUM_CCs; CC_id++) {
PHY_VARS_NR_UE *phy_vars = PHY_vars_UE_g[0][CC_id];
if (phy_vars) {
shutdown_actor(&phy_vars->ul_actor);
for (int i = 0; i < NUM_UL_ACTORS; i++) {
shutdown_actor(&phy_vars->ul_actors[i]);
}
for (int i = 0; i < NUM_DL_ACTORS; i++) {
shutdown_actor(&phy_vars->dl_actors[i]);
}
@@ -595,6 +598,9 @@ int main(int argc, char **argv)
}
free_nrLDPC_coding_interface(&nrLDPC_coding_interface);
time_manager_finish();
free(pckg);
return 0;
}

View File

@@ -14,7 +14,12 @@
#define CONFIG_HLP_TIME_SYNC_I "coefficient for Integrating part of time sync PI controller\n"
#define CONFIG_HLP_NTN_INIT_TIME_DRIFT "Initial NTN DL time drift (feeder link and service link), given in µs/s\n"
#define CONFIG_HLP_AUTONOMOUS_TA "Autonomously update TA based on DL drift (useful if main contribution to DL drift is movement, e.g. LEO satellite)\n"
#define CONFIG_HLP_INITIAL_FO "Initially compensated DL frequency offset (e.g. known Doppler shift in NTN LEO scenario)\n"
#define CONFIG_HLP_FREQ_SYNC_P "coefficient for Proportional part of continuous frequency offset compensation PI controller\n"
#define CONFIG_HLP_FREQ_SYNC_I "coefficient for Integrating part of continuous frequency offset compensation PI controller\n"
#define CONFIG_HLP_CONT_FO_COMP "Enable continuous frequency offset (FO) estimation and compensation and specify main FO source (1 = local oscillator, 2 = Doppler shift)\n"
#define CONFIG_HLP_AGC "Rx Gain control used for UE\n"
#define CONFIG_HLP_NUM_UL_ACTORS "Number of UL actors to use. Set to 0 to use the default value\n"
/***************************************************************************************************************************************/
/* command line options definitions, CMDLINE_XXXX_DESC macros are used to initialize paramdef_t arrays which are then used as argument
@@ -65,7 +70,12 @@
{"time-sync-I", CONFIG_HLP_TIME_SYNC_I, 0, .dblptr=&(nrUE_params.time_sync_I), .defdblval=0.0, TYPE_DOUBLE, 0}, \
{"ntn-initial-time-drift", CONFIG_HLP_NTN_INIT_TIME_DRIFT, 0, .dblptr=&(nrUE_params.ntn_init_time_drift), .defdblval=0.0, TYPE_DOUBLE, 0}, \
{"autonomous-ta", CONFIG_HLP_AUTONOMOUS_TA, PARAMFLAG_BOOL, .iptr=&(nrUE_params.autonomous_ta), .defintval=0, TYPE_INT, 0}, \
{"initial-fo", CONFIG_HLP_INITIAL_FO, 0, .dblptr=&(nrUE_params.initial_fo), .defdblval=0.0, TYPE_DOUBLE, 0}, \
{"freq-sync-P", CONFIG_HLP_FREQ_SYNC_P, 0, .dblptr=&(nrUE_params.freq_sync_P), .defdblval=0.01, TYPE_DOUBLE, 0}, \
{"freq-sync-I", CONFIG_HLP_FREQ_SYNC_I, 0, .dblptr=&(nrUE_params.freq_sync_I), .defdblval=0.001, TYPE_DOUBLE, 0}, \
{"cont-fo-comp", CONFIG_HLP_CONT_FO_COMP, 0, .iptr=&(nrUE_params.cont_fo_comp), .defintval=0, TYPE_INT, 0}, \
{"agc", CONFIG_HLP_AGC, PARAMFLAG_BOOL, .iptr=&(nrUE_params.agc), .defintval=0, TYPE_INT, 0}, \
{"num-ul-actors", CONFIG_HLP_NUM_UL_ACTORS, 0, .iptr=&nrUE_params.num_ul_actors, .defintval=0, TYPE_INT, 0}, \
}
// clang-format on
@@ -89,6 +99,10 @@ typedef struct {
double time_sync_I;
double ntn_init_time_drift;
int autonomous_ta;
double initial_fo;
double freq_sync_P;
double freq_sync_I;
int cont_fo_comp;
int agc;
char *usrp_args;
char *tx_subdev;
@@ -101,6 +115,7 @@ typedef struct {
double rx_gain_off;
int vcdflag;
int tx_max_power;
int num_ul_actors;
} nrUE_params_t;
extern uint64_t get_nrUE_optmask(void);
extern uint64_t set_nrUE_optmask(uint64_t bitmask);

View File

@@ -144,6 +144,7 @@ extern "C"
#define EMULATE_L1 softmodem_params.emulate_l1
#define CONTINUOUS_TX softmodem_params.continuous_tx
#define SYNC_REF softmodem_params.sync_ref
#define DEFAULT_PDU_ID softmodem_params.default_pdu_session_id
#define DEFAULT_RFCONFIG_FILE "/usr/local/etc/syriq/ue.band7.tm1.PRB100.NR40.dat";
@@ -188,6 +189,7 @@ extern int usrp_tx_thread;
{"E" , CONFIG_HLP_TQFS, PARAMFLAG_BOOL, .iptr=&softmodem_params.threequarter_fs, .defintval=0, TYPE_INT, 0}, \
{"imscope" , CONFIG_HLP_IMSCOPE, PARAMFLAG_BOOL, .uptr=&enable_imscope, .defintval=0, TYPE_UINT, 0}, \
{"imscope-record" , CONFIG_HLP_IMSCOPE_RECORD,PARAMFLAG_BOOL, .uptr=&enable_imscope_record, .defintval=0, TYPE_UINT, 0}, \
{"default_pdu_id", NULL, 0, .iptr=&DEFAULT_PDU_ID, .defintval=10, TYPE_INT, 0}, \
}
// clang-format on
@@ -234,6 +236,7 @@ extern int usrp_tx_thread;
{ .s5 = { NULL } }, \
{ .s5 = { NULL } }, \
{ .s5 = { NULL } }, \
{ .s5 = { NULL } }, \
}
// clang-format on
@@ -343,6 +346,7 @@ typedef struct {
uint32_t sync_ref;
int no_itti;
int threequarter_fs;
int default_pdu_session_id;
} softmodem_params_t;
#define IS_SA_MODE(sM_params) (!(sM_params)->phy_test && !(sM_params)->do_ra && !(sM_params)->nsa)

View File

@@ -129,6 +129,8 @@ typedef struct {
short rsrp_dBm;
long arfcn;
rlm_t radiolink_monitoring; // -1 no monitoring, 0 out_of_sync, 1 in_sync
// SINR value times 10 as reporting granularity is 0.5
float sinr_dB;
} fapi_nr_ssb_pdu_t;
typedef struct {

View File

@@ -350,7 +350,7 @@ int32_t LDPCdecoder(t_nrLDPC_dec_params *p_decParams,
uint16_t Zc = p_decParams->Z;
uint8_t BG = p_decParams->BG;
// uint8_t numMaxIter = p_decParams->numMaxIter;
int block_length = p_decParams->block_length;
int block_length = p_decParams->Kprime;
// e_nrLDPC_outMode outMode = p_decParams->outMode;
uint8_t row,col;
if(BG == 1){

View File

@@ -171,7 +171,6 @@ int init_nr_ue_signal(PHY_VARS_NR_UE *ue, int nb_connected_gNB)
NR_DL_FRAME_PARMS *const fp = &ue->frame_parms;
NR_UE_COMMON *const common_vars = &ue->common_vars;
NR_UE_PRACH **const prach_vars = ue->prach_vars;
NR_UE_SRS **const srs_vars = ue->srs_vars;
LOG_I(PHY, "Initializing UE vars for gNB TXant %u, UE RXant %u\n", fp->nb_antennas_tx, fp->nb_antennas_rx);
@@ -181,8 +180,10 @@ int init_nr_ue_signal(PHY_VARS_NR_UE *ue, int nb_connected_gNB)
AssertFatal( nb_connected_gNB <= NUMBER_OF_CONNECTED_gNB_MAX, "n_connected_gNB is too large" );
// init phy_vars_ue
for (int i = 0; i < fp->Lmax; i++)
for (int i = 0; i < fp->Lmax; i++) {
ue->measurements.ssb_rsrp_dBm[i] = INT_MIN;
ue->measurements.ssb_sinr_dB[i] = INT_MIN;
}
for (int i = 0; i < 4; i++) {
ue->rx_gain_max[i] = 135;
@@ -257,8 +258,6 @@ int init_nr_ue_signal(PHY_VARS_NR_UE *ue, int nb_connected_gNB)
// DLSCH
for (int gNB_id = 0; gNB_id < ue->n_connected_gNB; gNB_id++) {
prach_vars[gNB_id] = malloc16_clear(sizeof(NR_UE_PRACH));
srs_vars[gNB_id] = malloc16_clear(sizeof(NR_UE_SRS));
srs_vars[gNB_id]->active = false;
// ceil((NB_RB*8(max allocation per RB)*2(QPSK))/32)
ue->nr_csi_info = malloc16_clear(sizeof(nr_csi_info_t));
@@ -318,8 +317,6 @@ void term_nr_ue_signal(PHY_VARS_NR_UE *ue, int nb_connected_gNB)
free_and_zero(ue->nr_srs_info);
free_and_zero(ue->srs_vars[gNB_id]);
free_and_zero(ue->prach_vars[gNB_id]);
}
@@ -487,7 +484,6 @@ void clean_UE_harq(PHY_VARS_NR_UE *UE)
for (int harq_pid = 0; harq_pid < NR_MAX_ULSCH_HARQ_PROCESSES; harq_pid++) {
NR_UL_UE_HARQ_t *ul_harq_process = &UE->ul_harq_processes[harq_pid];
ul_harq_process->tx_status = NEW_TRANSMISSION_HARQ;
ul_harq_process->ULstatus = SCH_IDLE;
ul_harq_process->round = 0;
}
}

View File

@@ -25,15 +25,9 @@
#include "nr_modulation.h"
#include "PHY/LTE_ESTIMATION/lte_estimation.h"
#include "PHY/NR_UE_ESTIMATION/nr_estimation.h"
#include "PHY/nr_phy_common/inc/nr_phy_common.h"
#include <common/utils/LOG/log.h>
//#define DEBUG_FEP
/*#ifdef LOG_I
#undef LOG_I
#define LOG_I(A,B...) printf(A)
#endif*/
int nr_slot_fep(PHY_VARS_NR_UE *ue,
const NR_DL_FRAME_PARMS *frame_parms,
unsigned int slot,
@@ -43,7 +37,6 @@ int nr_slot_fep(PHY_VARS_NR_UE *ue,
uint32_t sample_offset,
c16_t **rxdata)
{
AssertFatal(symbol < frame_parms->symbols_per_slot, "slot_fep: symbol must be between 0 and %d\n", frame_parms->symbols_per_slot-1);
AssertFatal(slot < frame_parms->slots_per_frame, "slot_fep: Ns must be between 0 and %d\n", frame_parms->slots_per_frame - 1);
@@ -67,7 +60,7 @@ int nr_slot_fep(PHY_VARS_NR_UE *ue,
dft_size_idx_t dftsize = get_dft(frame_parms->ofdm_symbol_size);
// This is for misalignment issues
int32_t tmp_dft_in[8192] __attribute__ ((aligned (32)));
c16_t tmp_dft_in[frame_parms->ofdm_symbol_size] __attribute__ ((aligned (32)));
unsigned int rx_offset = frame_parms->get_samples_slot_timestamp(slot, frame_parms, 0);
unsigned int abs_symbol = slot * frame_parms->symbols_per_slot + symbol;
@@ -80,16 +73,10 @@ int nr_slot_fep(PHY_VARS_NR_UE *ue,
// use OFDM symbol from within 1/8th of the CP to avoid ISI
rx_offset -= (nb_prefix_samples / frame_parms->ofdm_offset_divisor);
#ifdef DEBUG_FEP
// if (ue->frame <100)
LOG_D(PHY,"slot_fep: slot %d, symbol %d, nb_prefix_samples %u, nb_prefix_samples0 %u, rx_offset %u energy %d\n",
Ns, symbol, nb_prefix_samples, nb_prefix_samples0, rx_offset, dB_fixed(signal_energy((int32_t *)&common_vars->rxdata[0][rx_offset],frame_parms->ofdm_symbol_size)));
#endif
rx_offset %= total_samples;
for (unsigned char aa=0; aa<frame_parms->nb_antennas_rx; aa++) {
int16_t *rxdata_ptr = (int16_t *)&rxdata[aa][rx_offset];
rx_offset %= total_samples;
c16_t *rxdata_ptr = &rxdata[aa][rx_offset];
// This happens only during initial sync
if (rx_offset + frame_parms->ofdm_symbol_size > total_samples) {
@@ -100,19 +87,25 @@ int nr_slot_fep(PHY_VARS_NR_UE *ue,
memcpy((void *)&tmp_dft_in[total_samples - rx_offset],
(void *)&rxdata[aa][0],
(frame_parms->ofdm_symbol_size - (total_samples - rx_offset)) * sizeof(int32_t));
rxdata_ptr = (int16_t *)tmp_dft_in;
rxdata_ptr = tmp_dft_in;
} else if ((rx_offset & 7) != 0) { // if input to dft is not 256-bit aligned
memcpy((void *)&tmp_dft_in[0], (void *)&rxdata[aa][rx_offset], frame_parms->ofdm_symbol_size * sizeof(int32_t));
rxdata_ptr = tmp_dft_in;
}
rxdata_ptr = (int16_t *)tmp_dft_in;
if (ue && ue->cont_fo_comp) {
start_meas_nr_ue_phy(ue, RX_FO_COMPENSATION_STATS);
nr_fo_compensation(ue->freq_offset, frame_parms->samples_per_subframe, rx_offset, rxdata_ptr, tmp_dft_in, frame_parms->ofdm_symbol_size);
rxdata_ptr = tmp_dft_in;
stop_meas_nr_ue_phy(ue, RX_FO_COMPENSATION_STATS);
}
if (ue)
start_meas_nr_ue_phy(ue, RX_DFT_STATS);
dft(dftsize,
rxdata_ptr,
(int16_t *)rxdata_ptr,
(int16_t *)&rxdataF[aa][frame_parms->ofdm_symbol_size*symbol],
1);
@@ -122,10 +115,6 @@ int nr_slot_fep(PHY_VARS_NR_UE *ue,
apply_nr_rotation_RX(frame_parms, rxdataF[aa], frame_parms->symbol_rotation[linktype], slot, N_RB, 0, symbol, 1);
}
#ifdef DEBUG_FEP
printf("slot_fep: done\n");
#endif
return 0;
}

View File

@@ -19,6 +19,7 @@
* contact@openairinterface.org
*/
#include "nr_common.h"
#include <string.h>
#include "nr_ul_estimation.h"
@@ -480,6 +481,9 @@ int nr_pusch_channel_estimation(PHY_VARS_gNB *gNB,
pusch_pdu->scid,
Ns,
symbol);
pusch_dmrs_type_t dmrs_type = pusch_pdu->dmrs_config_type == NFAPI_NR_DMRS_TYPE1 ? pusch_dmrs_type1 : pusch_dmrs_type2;
float beta_dmrs_pusch = get_beta_dmrs_pusch(pusch_pdu->num_dmrs_cdm_grps_no_data, dmrs_type);
int16_t dmrs_scaling = (1 / beta_dmrs_pusch) * (1 << 14);
nr_pusch_dmrs_rx(gNB,
Ns,
gold,
@@ -488,7 +492,8 @@ int nr_pusch_channel_estimation(PHY_VARS_gNB *gNB,
0,
nb_rb_pusch,
(pusch_pdu->bwp_start + pusch_pdu->rb_start) * NR_NB_SC_PER_RB,
pusch_pdu->dmrs_config_type);
pusch_pdu->dmrs_config_type,
dmrs_scaling);
} else { // if transform precoding or SC-FDMA is enabled in Uplink
// NR_SC_FDMA supports type1 DMRS so only 6 DMRS REs per RB possible
const int index = get_index_for_dmrs_lowpapr_seq(nb_rb_pusch * (NR_NB_SC_PER_RB / 2));

View File

@@ -34,6 +34,7 @@
//#define NR_PBCH_DMRS_LENGTH 144
//#define DEBUG_PUSCH
#include "PHY/TOOLS/tools_defs.h"
#include "refsig_defs_ue.h"
#include "PHY/defs_nr_UE.h"
#include "nr_refsig.h"
@@ -73,7 +74,8 @@ int nr_pusch_dmrs_rx(PHY_VARS_gNB *gNB,
unsigned char lp,
unsigned short nb_pusch_rb,
uint32_t re_offset,
uint8_t dmrs_type)
uint8_t dmrs_type,
int16_t dmrs_scaling)
{
typedef int array_of_w[2];
const array_of_w *wf = (dmrs_type == pusch_dmrs_type1) ? wf1 : wf2;
@@ -91,6 +93,7 @@ int nr_pusch_dmrs_rx(PHY_VARS_gNB *gNB,
int i = k + dmrs_offset;
int w = (wf[p - 1000][i & 1]) * (wt[p - 1000][lp]);
output[k] = get_modulated(nr_gold_pusch, i, w == 1);
output[k] = c16mulRealShift(output[k], dmrs_scaling, 14);
#ifdef DEBUG_PUSCH
printf("nr_pusch_dmrs_rx dmrs config type %d port %d nb_pusch_rb %d\n", dmrs_type, p, nb_pusch_rb);
printf("wf[%d] = %d wt[%d]= %d\n", i&1, wf[p-1000][i&1], lp, wt[p-1000][lp]);

View File

@@ -36,7 +36,8 @@ int nr_pusch_dmrs_rx(PHY_VARS_gNB *gNB,
unsigned char lp,
unsigned short nb_pusch_rb,
uint32_t re_offset,
uint8_t dmrs_type);
uint8_t dmrs_type,
int16_t dmrs_scaling);
void nr_generate_csi_rs(const NR_DL_FRAME_PARMS *frame_parms,
int32_t **dataF,

View File

@@ -730,7 +730,7 @@ static int do_one_dlsch(unsigned char *input_ptr, PHY_VARS_gNB *gNB, NR_gNB_DLSC
#endif
}
uint32_t dmrs_idx = rel15->rbStart;
if (rel15->rnti != SI_RNTI)
if (rel15->refPoint == 0)
dmrs_idx += rel15->BWPStart;
dmrs_idx *= dmrs_Type == NFAPI_NR_DMRS_TYPE1 ? 6 : 4;
c16_t txdataF_precoding[rel15->nrOfLayers][symbol_sz] __attribute__((aligned(64)));

View File

@@ -47,7 +47,7 @@ int nr_adjust_synch_ue(NR_DL_FRAME_PARMS *frame_parms,
VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_UE_ADJUST_SYNCH, VCD_FUNCTION_IN);
// search for maximum position within the cyclic prefix
for (int i = -frame_parms->nb_prefix_samples/2; i < frame_parms->nb_prefix_samples/2; i++) {
for (int i = -frame_parms->nb_prefix_samples; i < frame_parms->nb_prefix_samples; i++) {
int temp = 0;
int j = (i < 0) ? (i + frame_parms->ofdm_symbol_size) : i;

View File

@@ -1081,7 +1081,6 @@ void NFAPI_NR_DMRS_TYPE1_linear_interp(NR_DL_FRAME_PARMS *frame_parms,
c16_t *dl_ch,
unsigned short bwp_start_subcarrier,
unsigned short nb_rb_pdsch,
int8_t delta,
delay_t *delay,
uint32_t *nvar)
{
@@ -1094,30 +1093,23 @@ void NFAPI_NR_DMRS_TYPE1_linear_interp(NR_DL_FRAME_PARMS *frame_parms,
int nest_count = 0;
uint64_t noise_amp2 = 0;
for (int pilot_cnt = 0; pilot_cnt < 6 * nb_rb_pdsch; pilot_cnt++) {
if (pilot_cnt % 2 == 0) {
c16_t ch = c16mulShift(*pil, rxF[re_offset], 15);
pil++;
re_offset = (re_offset + 2) % frame_parms->ofdm_symbol_size;
ch = c16maddShift(*pil, rxF[re_offset], ch, 15);
ch = c16Shift(ch, 1);
pil++;
re_offset = (re_offset + 2) % frame_parms->ofdm_symbol_size;
for (int k = pilot_cnt << 1; k < (pilot_cnt << 1) + 4; k++) {
dl_ls_est[k] = ch;
}
}
for (int pilot_cnt = 0; pilot_cnt < 6 * nb_rb_pdsch; pilot_cnt += 2) {
c16_t ch_l = c16mulShift(*pil, rxF[re_offset], 15);
#ifdef DEBUG_PDSCH
printf("pilot %3d: pil -> (%6d,%6d), rxF -> (%4d,%4d), ch -> (%4d,%4d) \n",
pilot_cnt,
pil->r,
pil->i,
rxF[re_offset].r,
rxF[re_offset].i,
dl_ls_est[pilot_cnt << 1].r,
dl_ls_est[pilot_cnt << 1].i);
printf("pilot %3d: pil -> (%6d,%6d), rxF -> (%4d,%4d), ch -> (%4d,%4d) \n", pilot_cnt, pil->r, pil->i, rxF[re_offset].r, rxF[re_offset].i, ch_l.r, ch_l.i);
#endif
pil++;
re_offset = (re_offset + 2) % frame_parms->ofdm_symbol_size;
c16_t ch_r = c16mulShift(*pil, rxF[re_offset], 15);
#ifdef DEBUG_PDSCH
printf("pilot %3d: pil -> (%6d,%6d), rxF -> (%4d,%4d), ch -> (%4d,%4d) \n", pilot_cnt + 1, pil->r, pil->i, rxF[re_offset].r, rxF[re_offset].i, ch_r.r, ch_r.i);
#endif
c16_t ch = c16addShift(ch_l, ch_r, 1);
pil++;
re_offset = (re_offset + 2) % frame_parms->ofdm_symbol_size;
for (int k = pilot_cnt << 1; k < (pilot_cnt << 1) + 4; k++) {
dl_ls_est[k] = ch;
}
}
c16_t ch_estimates_time[frame_parms->ofdm_symbol_size] __attribute__((aligned(32)));
@@ -1247,8 +1239,6 @@ void NFAPI_NR_DMRS_TYPE2_linear_interp(NR_DL_FRAME_PARMS *frame_parms,
c16_t *dl_ch,
unsigned short bwp_start_subcarrier,
unsigned short nb_rb_pdsch,
int8_t delta,
unsigned short p,
delay_t *delay,
uint32_t *nvar)
{
@@ -1270,7 +1260,7 @@ void NFAPI_NR_DMRS_TYPE2_linear_interp(NR_DL_FRAME_PARMS *frame_parms,
re_offset = (re_offset + 1) % frame_parms->ofdm_symbol_size;
c16_t ch_r = c16mulShift(*pil, rxF[re_offset], 15);
#ifdef DEBUG_PDSCH
printf("pilot %3d: pil -> (%6d,%6d), rxF -> (%4d,%4d), ch -> (%4d,%4d) \n", pilot_cnt, pil->r, pil->i, rxF[re_offset].r, rxF[re_offset].i, ch_r.r, ch_r.i);
printf("pilot %3d: pil -> (%6d,%6d), rxF -> (%4d,%4d), ch -> (%4d,%4d) \n", pilot_cnt + 1, pil->r, pil->i, rxF[re_offset].r, rxF[re_offset].i, ch_r.r, ch_r.i);
#endif
c16_t ch = c16addShift(ch_l, ch_r, 1);
for (int k = 3 * pilot_cnt; k < (3 * pilot_cnt) + 6; k++) {
@@ -1427,9 +1417,8 @@ int nr_pdsch_channel_estimation(PHY_VARS_NR_UE *ue,
#ifdef DEBUG_PDSCH
printf(
"PDSCH Channel Estimation : gNB_id %d ch_offset %d, symbol_offset %d OFDM size %d, Ncp=%d, Ns=%d, bwp_start_subcarrier=%d "
"PDSCH Channel Estimation : ch_offset %d, symbol_offset %d OFDM size %d, Ncp=%d, Ns=%d, bwp_start_subcarrier=%d "
"symbol %d\n",
gNB_id,
ch_offset,
symbol_offset,
fp->ofdm_symbol_size,
@@ -1467,7 +1456,6 @@ int nr_pdsch_channel_estimation(PHY_VARS_NR_UE *ue,
dl_ch,
bwp_start_subcarrier,
nb_rb_pdsch,
delta,
&delay,
nvar);
@@ -1478,8 +1466,6 @@ int nr_pdsch_channel_estimation(PHY_VARS_NR_UE *ue,
dl_ch,
bwp_start_subcarrier,
nb_rb_pdsch,
delta,
p,
&delay,
nvar);

View File

@@ -111,11 +111,11 @@ void nr_ue_measurements(PHY_VARS_NR_UE *ue,
uint32_t pdsch_est_size,
int32_t dl_ch_estimates[][pdsch_est_size]);
int nr_ue_calculate_ssb_rsrp(const NR_DL_FRAME_PARMS *fp,
const UE_nr_rxtx_proc_t *proc,
const c16_t rxdataF[][fp->samples_per_slot_wCP],
int symbol_offset,
int ssb_start_subcarrier);
uint32_t nr_ue_calculate_ssb_rsrp(const NR_DL_FRAME_PARMS *fp,
const UE_nr_rxtx_proc_t *proc,
const c16_t rxdataF[][fp->samples_per_slot_wCP],
int symbol_offset,
int ssb_start_subcarrier);
void nr_ue_ssb_rsrp_measurements(PHY_VARS_NR_UE *ue,
uint8_t gNB_index,

View File

@@ -172,11 +172,11 @@ void nr_ue_measurements(PHY_VARS_NR_UE *ue,
// This function calculates:
// - SS reference signal received digital power in dB/RE
int nr_ue_calculate_ssb_rsrp(const NR_DL_FRAME_PARMS *fp,
const UE_nr_rxtx_proc_t *proc,
const c16_t rxdataF[][fp->samples_per_slot_wCP],
int symbol_offset,
int ssb_start_subcarrier)
uint32_t nr_ue_calculate_ssb_rsrp(const NR_DL_FRAME_PARMS *fp,
const UE_nr_rxtx_proc_t *proc,
const c16_t rxdataF[][fp->samples_per_slot_wCP],
int symbol_offset,
int ssb_start_subcarrier)
{
int k_start = 56;
int k_end = 183;
@@ -209,9 +209,7 @@ int nr_ue_calculate_ssb_rsrp(const NR_DL_FRAME_PARMS *fp,
LOG_D(PHY, "In %s: RSRP/nb_re: %d nb_re :%d\n", __FUNCTION__, rsrp, nb_re);
int rsrp_db_per_re = 10 * log10(rsrp);
return rsrp_db_per_re;
return rsrp;
}
// This function implements:
@@ -234,7 +232,8 @@ void nr_ue_ssb_rsrp_measurements(PHY_VARS_NR_UE *ue,
if (fp->half_frame_bit)
symbol_offset += (fp->slots_per_frame >> 1) * fp->symbols_per_slot;
int rsrp_db_per_re = nr_ue_calculate_ssb_rsrp(fp, proc, rxdataF, symbol_offset, fp->ssb_start_subcarrier);
uint32_t rsrp_avg = nr_ue_calculate_ssb_rsrp(fp, proc, rxdataF, symbol_offset, fp->ssb_start_subcarrier);
float rsrp_db_per_re = 10 * log10(rsrp_avg);
openair0_config_t *cfg0 = &openair0_cfg[0];
@@ -242,12 +241,18 @@ void nr_ue_ssb_rsrp_measurements(PHY_VARS_NR_UE *ue,
- ((int)cfg0->rx_gain[0] - (int)cfg0->rx_gain_offset[0])
- dB_fixed(fp->ofdm_symbol_size);
// to obtain non-integer dB value with a resoluion of 0.5dB
uint32_t signal_pwr = rsrp_avg > ue->measurements.n0_power_avg ? rsrp_avg - ue->measurements.n0_power_avg : 0;
int SNRtimes10 = dB_fixed_x10(signal_pwr) - dB_fixed_x10(ue->measurements.n0_power_avg);
ue->measurements.ssb_sinr_dB[ssb_index] = SNRtimes10 / 10.0;
LOG_D(PHY,
"[UE %d] ssb %d SS-RSRP: %d dBm/RE (%d dB/RE)\n",
"[UE %d] ssb %d SS-RSRP: %d dBm/RE (%f dB/RE), SS-SINR: %f dB\n",
ue->Mod_id,
ssb_index,
ue->measurements.ssb_rsrp_dBm[ssb_index],
rsrp_db_per_re);
rsrp_db_per_re,
ue->measurements.ssb_sinr_dB[ssb_index]);
}
// This function computes the received noise power

View File

@@ -640,6 +640,9 @@ void nr_dci_decoding_procedure(PHY_VARS_NR_UE *ue,
{
int e_rx_cand_idx = 0;
*dci_ind = (fapi_nr_dci_indication_t){.SFN = proc->frame_rx, .slot = proc->nr_slot_rx};
// if DCI for SIB we don't break after finding 1st DCI with that RNTI
// there might be SIB1 and otherSIB in the same slot with the same length
bool is_SI = rel15->rnti == SI_RNTI;
for (int j = 0; j < rel15->number_of_candidates; j++) {
int CCEind = rel15->CCE[j];
@@ -653,7 +656,7 @@ void nr_dci_decoding_procedure(PHY_VARS_NR_UE *ue,
int dci_length = rel15->dci_length_options[k];
int ind;
for (ind = 0; ind < dci_ind->number_of_dcis; ind++) {
if (rel15->rnti == dci_ind->dci_list[ind].rnti && dci_length == dci_ind->dci_list[ind].payloadSize) {
if (!is_SI && rel15->rnti == dci_ind->dci_list[ind].rnti && dci_length == dci_ind->dci_list[ind].payloadSize) {
break;
}
}

View File

@@ -576,6 +576,25 @@ int nr_rx_pdsch(PHY_VARS_NR_UE *ue,
*log2_maxh,
ue->phy_cpu_stats.cpu_time_stats[DLSCH_CHANNEL_COMPENSATION_STATS].p_time / (cpuf * 1000.0));
}
// Please keep it: useful for debugging
#ifdef DEBUG_PDSCH_RX
char filename[50];
snprintf(filename, 50, "rxdataF0_symb_%d_nr_slot_rx_%d.m", symbol, nr_slot_rx);
write_output(filename, "rxdataF0", &rxdataF[0][symbol * frame_parms->ofdm_symbol_size], frame_parms->ofdm_symbol_size, 1, 1);
snprintf(filename, 50, "dl_ch_estimates0_symb_%d_nr_slot_rx_%d.m", symbol, nr_slot_rx);
write_output(filename, "dl_ch_estimates0", &dl_ch_estimates[0][symbol * frame_parms->ofdm_symbol_size], frame_parms->ofdm_symbol_size, 1, 1);
snprintf(filename, 50, "rxdataF_ext0_symb_%d_nr_slot_rx_%d.m", symbol, nr_slot_rx);
write_output(filename, "rxdataF_ext0", &rxdataF_ext[0][0], rx_size_symbol, 1, 1);
snprintf(filename, 50, "dl_ch_estimates_ext0_symb_%d_nr_slot_rx_%d.m", symbol, nr_slot_rx);
write_output(filename, "dl_ch_estimates_ext0", &dl_ch_estimates_ext[0][0], rx_size_symbol, 1, 1);
snprintf(filename, 50, "rxdataF_comp00_symb_%d_nr_slot_rx_%d.m", symbol, nr_slot_rx);
write_output(filename, "rxdataF_comp00", &rxdataF_comp[0][0][symbol * rx_size_symbol], rx_size_symbol, 1, 1);
#endif
}
start_meas_nr_ue_phy(ue, DLSCH_MRC_MMSE_STATS);
@@ -694,32 +713,12 @@ int nr_rx_pdsch(PHY_VARS_NR_UE *ue,
rx_llr_layer_size,
layer_llr);
stop_meas_nr_ue_phy(ue, DLSCH_LAYER_DEMAPPING);
// Please keep it: useful for debugging
#ifdef DEBUG_PDSCH_RX
char filename[50];
uint8_t aa = 0;
snprintf(filename, 50, "rxdataF0_symb_%d_nr_slot_rx_%d.m", symbol, nr_slot_rx);
write_output(filename, "rxdataF0", &rxdataF[0][0], NR_SYMBOLS_PER_SLOT*frame_parms->ofdm_symbol_size, 1, 1);
snprintf(filename, 50, "dl_ch_estimates0%d_symb_%d_nr_slot_rx_%d.m", aa, symbol, nr_slot_rx);
write_output(filename, "dl_ch_estimates", &dl_ch_estimates[aa][0], NR_SYMBOLS_PER_SLOT*frame_parms->ofdm_symbol_size, 1, 1);
snprintf(filename, 50, "rxdataF_ext0%d_symb_%d_nr_slot_rx_%d.m", aa, symbol, nr_slot_rx);
write_output(filename, "rxdataF_ext", &rxdataF_ext[aa][0], NR_SYMBOLS_PER_SLOT*frame_parms->N_RB_DL*NR_NB_SC_PER_RB, 1, 1);
snprintf(filename, 50, "dl_ch_estimates_ext0%d_symb_%d_nr_slot_rx_%d.m", aa, symbol, nr_slot_rx);
write_output(filename, "dl_ch_estimates_ext00", &dl_ch_estimates_ext[aa][0], NR_SYMBOLS_PER_SLOT*frame_parms->N_RB_DL*NR_NB_SC_PER_RB, 1, 1);
snprintf(filename, 50, "rxdataF_comp0%d_symb_%d_nr_slot_rx_%d.m", aa, symbol, nr_slot_rx);
write_output(filename, "rxdataF_comp00", &rxdataF_comp[aa][0], NR_SYMBOLS_PER_SLOT*frame_parms->N_RB_DL*NR_NB_SC_PER_RB, 1, 1);
/*
for (int i=0; i < 2; i++){
snprintf(filename, 50, "llr%d_symb_%d_nr_slot_rx_%d.m", i, symbol, nr_slot_rx);
write_output(filename,"llr", &llr[i][0], (NR_SYMBOLS_PER_SLOT*nb_rb_pdsch*NR_NB_SC_PER_RB*dlsch1_harq->Qm) - 4*(nb_rb_pdsch*4*dlsch1_harq->Qm), 1, 0);
}
*/
#endif
if (UEScopeHasTryLock(ue)) {
metadata mt = {.frame = proc->frame_rx, .slot = proc->nr_slot_rx };
int total_valid_res = 0;
@@ -766,10 +765,12 @@ int nr_rx_pdsch(PHY_VARS_NR_UE *ue,
for (int a = 0; a < nbRx; a++) {
int offset = (void *)rxdataF_comp[0][a] - (void *)rxdataF_comp[0] + symbol * rx_size_symbol * sizeof(c16_t);
memcpy(ue->phy_sim_pdsch_rxdataF_comp + offset, rxdataF_comp[0][a] + symbol * rx_size_symbol, sizeof(c16_t) * rx_size_symbol);
memcpy(ue->phy_sim_pdsch_dl_ch_estimates + offset, dl_ch_estimates, pdsch_est_size * sizeof(c16_t));
memcpy((c16_t *)ue->phy_sim_pdsch_dl_ch_estimates + pdsch_est_size * a, dl_ch_estimates, pdsch_est_size * sizeof(c16_t));
}
if (ue->phy_sim_pdsch_dl_ch_estimates_ext)
memcpy(ue->phy_sim_pdsch_dl_ch_estimates_ext + symbol * rx_size_symbol, dl_ch_estimates_ext, sizeof(dl_ch_estimates_ext));
memcpy((c16_t *)ue->phy_sim_pdsch_dl_ch_estimates_ext + symbol * rx_size_symbol,
dl_ch_estimates_ext,
sizeof(dl_ch_estimates_ext));
return (0);
}

View File

@@ -199,6 +199,9 @@ void nr_scan_ssb(void *arg)
// initial sync performed on two successive frames, if pbch passes on first frame, no need to process second frame
// only one frame is used for simulation tools
for (int frame_id = 0; frame_id < ssbInfo->nFrames && !ssbInfo->syncRes.cell_detected; frame_id++) {
if (ssbInfo->freqOffset)
compensate_freq_offset(rxdata, fp, ssbInfo->freqOffset, frame_id);
/* process pss search on received buffer */
ssbInfo->syncRes.frame_id = frame_id;
int nid2;
@@ -268,7 +271,7 @@ void nr_scan_ssb(void *arg)
phase_tdd_ncp,
ssbInfo->syncRes.rx_offset);
#endif
ssbInfo->freqOffset = freq_offset_pss + freq_offset_sss;
ssbInfo->freqOffset += freq_offset_pss + freq_offset_sss;
if (ssbInfo->syncRes.cell_detected) { // we got sss channel
ssbInfo->syncRes.cell_detected = nr_pbch_detection(ssbInfo->proc,
@@ -282,7 +285,8 @@ void nr_scan_ssb(void *arg)
&ssbInfo->pbchResult,
rxdataF); // start pbch detection at first symbol after pss
if (ssbInfo->syncRes.cell_detected) {
int rsrp_db_per_re = nr_ue_calculate_ssb_rsrp(ssbInfo->fp, ssbInfo->proc, rxdataF, 0, ssbInfo->gscnInfo.ssbFirstSC);
uint32_t rsrp_avg = nr_ue_calculate_ssb_rsrp(ssbInfo->fp, ssbInfo->proc, rxdataF, 0, ssbInfo->gscnInfo.ssbFirstSC);
int rsrp_db_per_re = 10 * log10(rsrp_avg);
ssbInfo->adjust_rxgain = TARGET_RX_POWER - rsrp_db_per_re;
LOG_I(PHY, "pbch rx ok. rsrp:%d dB/RE, adjust_rxgain:%d dB\n", rsrp_db_per_re, ssbInfo->adjust_rxgain);
}
@@ -319,6 +323,7 @@ nr_initial_sync_t nr_initial_sync(UE_nr_rxtx_proc_t *proc,
.syncRes.cell_detected = false,
.nFrames = n_frames,
.foFlag = ue->UE_fo_compensation,
.freqOffset = ue->initial_fo,
.targetNidCell = ue->target_Nid_cell};
ssbInfo->rxdata = malloc16_clear(fp->nb_antennas_rx * sizeof(c16_t *));
for (int ant = 0; ant < fp->nb_antennas_rx; ant++) {

View File

@@ -525,3 +525,21 @@ int nr_rx_pbch(PHY_VARS_NR_UE *ue,
TracyCZoneEnd(ctx);
return 0;
}
double nr_ue_pbch_freq_offset(const NR_DL_FRAME_PARMS *frame_parms,
int estimateSz,
const c16_t dl_ch_estimates[][estimateSz])
{
const int i_ssb = frame_parms->ssb_index;
const int symbol_offset = nr_get_ssb_start_symbol(frame_parms, i_ssb) % frame_parms->symbols_per_slot;
const c16_t *dl_ch_est_symb1 = &dl_ch_estimates[0][(symbol_offset + 1) * frame_parms->ofdm_symbol_size];
const c16_t *dl_ch_est_symb3 = &dl_ch_estimates[0][(symbol_offset + 3) * frame_parms->ofdm_symbol_size];
const int nb_re = 240;
const c32_t dot_prod_res = dot_product(dl_ch_est_symb1, dl_ch_est_symb3, nb_re, 8);
const double res_phase = atan2(dot_prod_res.i, dot_prod_res.r);
const int samples_per_symbol = frame_parms->ofdm_symbol_size + frame_parms->nb_prefix_samples;
const double t_ofdm = samples_per_symbol / (frame_parms->samples_per_subframe * 1000.0); // symbol duration in sec
const double freq_offset = res_phase / (2 * M_PI * (3 - 1) * t_ofdm);
return freq_offset;
}

View File

@@ -68,7 +68,7 @@ int32_t generate_nr_prach(PHY_VARS_NR_UE *ue, uint8_t gNB_id, int frame, uint8_t
int Ncp = 0;
int prach_start, prach_sequence_length, prach_len, dftlen, mu, n_ra_prb, k, prachStartSymbol, sample_offset_slot;
fd_occasion = 0;
fd_occasion = prach_pdu->num_ra;
prach_len = 0;
dftlen = 0;
first_nonzero_root_idx = 0;

View File

@@ -204,6 +204,10 @@ int nr_rx_pbch(PHY_VARS_NR_UE *ue,
int rxdataFSize,
const struct complex16 rxdataF[][rxdataFSize]);
double nr_ue_pbch_freq_offset(const NR_DL_FRAME_PARMS *frame_parms,
int estimateSz,
const c16_t dl_ch_estimates[][estimateSz]);
#ifndef modOrder
#define modOrder(I_MCS,I_TBS) ((I_MCS-I_TBS)*2+2) // Find modulation order from I_TBS and I_MCS
#endif

View File

@@ -46,8 +46,6 @@ typedef enum {
typedef struct {
/// HARQ tx status
harq_result_t tx_status;
/// Status Flag indicating for this ULSCH (idle,active,disabled)
SCH_status_t ULstatus;
/// Last TPC command
uint8_t TPC;
/// Length of ACK information (bits)
@@ -87,6 +85,7 @@ typedef struct {
} NR_UL_UE_HARQ_t;
typedef struct {
SCH_status_t status;
/// NDAPI struct for UE
nfapi_nr_ue_pusch_pdu_t pusch_pdu;
// UL number of harq processes

View File

@@ -392,13 +392,15 @@ static void dmrs_amp_mult(const uint32_t dmrs_port,
const c16_t *mod_dmrs,
c16_t *mod_dmrs_out,
const uint32_t n_dmrs,
const pusch_dmrs_type_t dmrs_type)
const pusch_dmrs_type_t dmrs_type,
const unsigned int num_cdm_groups_no_data)
{
float beta_dmrs_pusch = get_beta_dmrs_pusch(num_cdm_groups_no_data, dmrs_type);
/* short array that hold amplitude for k_prime = 0 and k_prime = 1 */
int32_t alpha_dmrs[2] __attribute((aligned(16)));
for (int_fast8_t i = 0; i < sizeofArray(alpha_dmrs); i++) {
const int32_t a = Wf[i] * Wt * AMP;
alpha_dmrs[i] = a;
alpha_dmrs[i] = a * beta_dmrs_pusch;
}
/* multiply amplitude with complex DMRS vector */
@@ -440,9 +442,9 @@ static void map_symbols(const nr_phy_pxsch_params_t p,
c16_t mod_dmrs[ALNARS_16_4(n_dmrs)] __attribute((aligned(16)));
if (p.transform_precoding == transformPrecoder_disabled) {
nr_modulation(gold, n_dmrs * 2, DMRS_MOD_ORDER, (int16_t *)mod_dmrs);
dmrs_amp_mult(p.dmrs_port, p.Wt, p.Wf, mod_dmrs, mod_dmrs_amp, n_dmrs, p.dmrs_type);
dmrs_amp_mult(p.dmrs_port, p.Wt, p.Wf, mod_dmrs, mod_dmrs_amp, n_dmrs, p.dmrs_type, p.num_cdm_no_data);
} else {
dmrs_amp_mult(p.dmrs_port, p.Wt, p.Wf, dmrs_seq, mod_dmrs_amp, n_dmrs, p.dmrs_type);
dmrs_amp_mult(p.dmrs_port, p.Wt, p.Wf, dmrs_seq, mod_dmrs_amp, n_dmrs, p.dmrs_type, p.num_cdm_no_data);
}
} else if ((p.pdu_bit_map & PUSCH_PDU_BITMAP_PUSCH_PTRS) && ptrs_symbol) {
AssertFatal(p.transform_precoding == transformPrecoder_disabled, "PTRS NOT SUPPORTED IF TRANSFORM PRECODING IS ENABLED\n");
@@ -471,7 +473,7 @@ void nr_ue_ulsch_procedures(PHY_VARS_NR_UE *UE,
int harq_pid = phy_data->ulsch.pusch_pdu.pusch_data.harq_process_id;
if (UE->ul_harq_processes[harq_pid].ULstatus != ACTIVE)
if (phy_data->ulsch.status != ACTIVE)
return;
start_meas_nr_ue_phy(UE, PUSCH_PROC_STATS);
@@ -542,8 +544,6 @@ void nr_ue_ulsch_procedures(PHY_VARS_NR_UE *UE,
/////////////////////////ULSCH coding/////////////////////////
if (nr_ulsch_encoding(UE, &phy_data->ulsch, frame, slot, G, 1, ULSCH_ids) == -1) {
NR_UL_UE_HARQ_t *harq_process_ulsch = &UE->ul_harq_processes[harq_pid];
harq_process_ulsch->ULstatus = SCH_IDLE;
stop_meas_nr_ue_phy(UE, PUSCH_PROC_STATS);
return;
}
@@ -820,12 +820,7 @@ void nr_ue_ulsch_procedures(PHY_VARS_NR_UE *UE,
} // symbol loop
} // port loop
NR_UL_UE_HARQ_t *harq_process_ulsch = NULL;
harq_process_ulsch = &UE->ul_harq_processes[harq_pid];
harq_process_ulsch->ULstatus = SCH_IDLE;
stop_meas_nr_ue_phy(UE, PUSCH_PROC_STATS);
}
uint8_t nr_ue_pusch_common_procedures(PHY_VARS_NR_UE *UE,

View File

@@ -426,14 +426,15 @@ int generate_srs_nr(nfapi_nr_srs_pdu_t *srs_config_pdu,
int ue_srs_procedures_nr(PHY_VARS_NR_UE *ue,
const UE_nr_rxtx_proc_t *proc,
c16_t **txdataF,
nr_phy_data_tx_t *phy_data,
bool was_symbol_used[NR_NUMBER_OF_SYMBOLS_PER_SLOT])
{
if(!ue->srs_vars[0]->active) {
if(phy_data->srs_vars.active == false) {
return -1;
}
ue->srs_vars[0]->active = false;
nfapi_nr_srs_pdu_t *srs_config_pdu = (nfapi_nr_srs_pdu_t*)&ue->srs_vars[0]->srs_config_pdu;
// TODO: This is wrong: casting from UE fapi to gNB NFAPI srs pdu
nfapi_nr_srs_pdu_t *srs_config_pdu = (nfapi_nr_srs_pdu_t *)&phy_data->srs_vars.srs_config_pdu;
int first_srs_symbol = ue->frame_parms.symbols_per_slot - 1 - srs_config_pdu->time_start_position;
// Num consecutive SRS symbols according to 38.211 6.4.1.4.1

View File

@@ -78,7 +78,10 @@ int is_srs_period_nr(SRS_Resource_t *p_SRS_Resource,
@param rxtx context
@param current gNB_id identifier
@returns 0 if srs is transmitted -1 otherwise */
int ue_srs_procedures_nr(PHY_VARS_NR_UE *ue, const UE_nr_rxtx_proc_t *proc, c16_t **txdataF, bool was_symbol_used[14]);
int ue_srs_procedures_nr(PHY_VARS_NR_UE *ue,
const UE_nr_rxtx_proc_t *proc,
c16_t **txdataF,
nr_phy_data_tx_t *phy_data,
bool was_symbol_used[NR_NUMBER_OF_SYMBOLS_PER_SLOT]);
#endif /* SRS_MODULATION_NR_H */

View File

@@ -0,0 +1,70 @@
/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.openairinterface.org/?page_id=698
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
#include <stdint.h>
#include <math.h>
#include "tools_defs.h"
#define LUT_SIN_RESOL 128
// look-up table for the sine (cosine) function
static uint16_t LUTSin[LUT_SIN_RESOL + 1];
void InitSinLUT(void)
{
for (int i = 0; i < (LUT_SIN_RESOL + 1); i++) {
LUTSin[i] = round(sin((M_PI * i) / (2 * LUT_SIN_RESOL)) * (1 << 14)); // Format: Q14
}
}
// fast calculation of e^{i*2*pi*phase}
// ret.r == cosinus << 14
// ret.i == sinus << 14
c16_t get_sin_cos(double phase)
{
int index;
if (phase < 0) {
index = (int)(-phase * 4 * LUT_SIN_RESOL + 0.5) % (4 * LUT_SIN_RESOL);
index = 4 * LUT_SIN_RESOL - index;
} else {
index = (int)(phase * 4 * LUT_SIN_RESOL + 0.5) % (4 * LUT_SIN_RESOL);
}
c16_t ret;
if (index < 2 * LUT_SIN_RESOL) { // check for 1st and 2nd Quadrant
if (index < LUT_SIN_RESOL) { // 1st Quadrant
ret.r = LUTSin[LUT_SIN_RESOL - index];
ret.i = LUTSin[index];
} else { // 2nd Quadrant
ret.r = -LUTSin[index - LUT_SIN_RESOL];
ret.i = LUTSin[2 * LUT_SIN_RESOL - index];
}
} else { // 3rd and 4th Quadrant
if (index < 3 * LUT_SIN_RESOL) { // 3rd Quadrant
ret.r = -LUTSin[3 * LUT_SIN_RESOL - index];
ret.i = -LUTSin[index - 2 * LUT_SIN_RESOL];
} else { // 4th Quadrant
ret.r = LUTSin[index - 3 * LUT_SIN_RESOL];
ret.i = -LUTSin[4 * LUT_SIN_RESOL - index];
}
}
return ret;
}

View File

@@ -792,6 +792,13 @@ double interp(double x, double *xs, double *ys, int count);
void simde_mm128_separate_real_imag_parts(simde__m128i *out_re, simde__m128i *out_im, simde__m128i in0, simde__m128i in1);
void simde_mm256_separate_real_imag_parts(simde__m256i *out_re, simde__m256i *out_im, simde__m256i in0, simde__m256i in1);
void InitSinLUT(void);
// fast calculation of e^{i*2*pi*phase}
// ret.r == cosinus << 14
// ret.i == sinus << 14
c16_t get_sin_cos(double phase);
#ifdef __cplusplus
}
#endif

View File

@@ -82,6 +82,7 @@
/// suppress compiler warning for unused arguments
#define UNUSED(x) (void)x;
#define NUM_DL_ACTORS 4
#define NUM_UL_ACTORS 2
// Set the number of barriers for processSlotTX to 512. This value has to be at least 483 for NTN where
// DL-to-UL offset is up to 483. The selected value is also half of the frame range so that
@@ -139,6 +140,7 @@ typedef struct {
uint32_t rsrp[7];
short rsrp_dBm[7];
int ssb_rsrp_dBm[64];
float ssb_sinr_dB[64];
// common measurements
//! estimated noise power (linear)
unsigned int n0_power[NB_ANTENNAS_RX];
@@ -401,7 +403,6 @@ typedef struct PHY_VARS_NR_UE_s {
nr_synch_request_t synch_request;
NR_UE_PRACH *prach_vars[NUMBER_OF_CONNECTED_gNB_MAX];
NR_UE_SRS *srs_vars[NUMBER_OF_CONNECTED_gNB_MAX];
NR_UE_PRS *prs_vars[NR_MAX_PRS_COMB_SIZE];
uint8_t prs_active_gNBs;
NR_DL_UE_HARQ_t dl_harq_processes[2][NR_MAX_DLSCH_HARQ_PROCESSES];
@@ -460,6 +461,11 @@ typedef struct PHY_VARS_NR_UE_s {
int64_t max_pos_iir; /// Timing offset IIR filter
int max_pos_acc; /// Timing offset accumuluated error for PI filter
double initial_fo; /// initial frequency offset provided by the user
int cont_fo_comp; /// flag enabling the continuous frequency offset estimation and compensation
double freq_offset; /// currently compensated frequency offset
double freq_off_acc; /// accumulated frequency error (for PI controller)
/// Timing Advance updates variables
/// Timing advance update computed from the TA command signalled from gNB
int timing_advance;
@@ -514,7 +520,7 @@ typedef struct PHY_VARS_NR_UE_s {
sl_nr_ue_phy_params_t SL_UE_PHY_PARAMS;
Actor_t sync_actor;
Actor_t dl_actors[NUM_DL_ACTORS];
Actor_t ul_actor;
Actor_t ul_actors[NUM_UL_ACTORS];
ntn_config_message_t* ntn_config_message;
pthread_t main_thread;
pthread_t stat_thread;
@@ -525,10 +531,6 @@ typedef struct {
int gNB_id;
/// NR slot index within frame_tx [0 .. slots_per_frame - 1] to act upon for transmission
int nr_slot_tx;
/// NR slot index tx offset to resume
/// in case of NTN, tx_offset can be changed dynamically via SIB19
/// we need to notify the right tx thread slot based on TX offset change
int nr_slot_tx_offset;
int rx_slot_type;
/// NR slot index within frame_rx [0 .. slots_per_frame - 1] to act upon for transmission
int nr_slot_rx;
@@ -571,6 +573,7 @@ typedef struct {
typedef struct nr_phy_data_tx_s {
NR_UE_ULSCH_t ulsch;
NR_UE_PUCCH pucch_vars;
NR_UE_SRS srs_vars;
// Sidelink Rx action decided by MAC
sl_nr_tx_config_type_enum_t sl_tx_action;
@@ -597,7 +600,7 @@ typedef struct nr_rxtx_thread_data_s {
PHY_VARS_NR_UE *UE;
int writeBlockSize;
nr_phy_data_t phy_data;
dynamic_barrier_t *next_barrier;
dynamic_barrier_t* next_barrier;
} nr_rxtx_thread_data_t;
typedef struct LDPCDecode_ue_s {

View File

@@ -344,12 +344,6 @@ typedef enum {
* DESCRIPTION : configuration for PUSCH
*
************************************************************************/
typedef enum {
pusch_dmrs_type1 = 0,
pusch_dmrs_type2 = 1
} pusch_dmrs_type_t;
typedef enum {
txConfig_codebook = 1,
txConfig_nonCodebook = 2

View File

@@ -80,4 +80,6 @@ void nr_256qam_llr(int32_t *rxdataF_comp, int32_t *ch_mag, int32_t *ch_mag2, int
void freq2time(uint16_t ofdm_symbol_size, int16_t *freq_signal, int16_t *time_signal);
void nr_est_delay(int ofdm_symbol_size, const c16_t *ls_est, c16_t *ch_estimates_time, delay_t *delay);
unsigned int nr_get_tx_amp(int power_dBm, int power_max_dBm, int total_nb_rb, int nb_rb);
void nr_fo_compensation(double fo_Hz, int samples_per_ms, int sample_offset, const c16_t *rxdata_in, c16_t *rxdata_out, int size);
#endif

View File

@@ -28,6 +28,7 @@
#define FOREACH_NR_PHY_CPU_MEAS(FN) \
FN(RX_PDSCH_STATS),\
FN(DLSCH_RX_PDCCH_STATS),\
FN(RX_FO_COMPENSATION_STATS),\
FN(RX_DFT_STATS),\
FN(DLSCH_CHANNEL_ESTIMATION_STATS),\
FN(DLSCH_DECODING_STATS),\

View File

@@ -453,3 +453,41 @@ unsigned int nr_get_tx_amp(int power_dBm, int power_max_dBm, int total_nb_rb, in
}
return (0);
}
void nr_fo_compensation(double fo_Hz, int samples_per_ms, int sample_offset, const c16_t *rxdata_in, c16_t *rxdata_out, int size)
{
const double phase_inc = -fo_Hz / (samples_per_ms * 1000);
double phase = sample_offset * phase_inc;
phase -= (int)phase;
#if 1
// The bottleneck is the calculation of the complex rotation values using get_sin_cos().
// This code path does not compute these values for the complete OFDM symbol, but only for a smaller CHUNK size.
// After applying the rotation to a CHUNK size of the output, these rotation values are efficiently rotated further by `rot_vec`.
// Unfortunately, this propagates small errors from one chunk to the next.
// Therefore, there is a tradeoff between speed (better with small CHUNK sizes) and accuracy (better with large CHUNK sizes).
#define CHUNK 128
c16_t rot[CHUNK] __attribute__((aligned(32)));
for (int i = 0; i < CHUNK; i++) {
rot[i] = get_sin_cos(phase);
phase += phase_inc;
}
const c16_t rot_vec = get_sin_cos(CHUNK * phase_inc);
while (size > CHUNK) {
mult_complex_vectors(rxdata_in, rot, rxdata_out, CHUNK, 14);
rotate_cpx_vector(rot, &rot_vec, rot, CHUNK, 14);
rxdata_in += CHUNK;
rxdata_out += CHUNK;
size -= CHUNK;
}
mult_complex_vectors(rxdata_in, rot, rxdata_out, size, 14);
#else
// This code path computes the complex rotation values for the complete OFDM symbol using get_sin_cos().
// This is more accurate, but also slower than the code path above.
c16_t rot[size] __attribute__((aligned(32)));
for (int i = 0; i < size; i++) {
rot[i] = get_sin_cos(phase);
phase += phase_inc;
}
mult_complex_vectors(rxdata_in, rot, rxdata_out, size, 14);
#endif
}

View File

@@ -120,7 +120,8 @@ void prach_procedures(PHY_VARS_eNB *eNB,
*/
if (eNB->frame_parms.prach_emtc_config_common.prach_ConfigInfo.prach_CElevel_enable[0] == 1) {
if ((eNB->prach_energy_counter == 100) && (max_preamble_energy[0] > eNB->measurements.prach_I0 + eNB->prach_DTX_threshold_emtc[0])) {
if ((eNB->prach_energy_counter == NUM_PRACH_RX_FOR_NOISE_ESTIMATE)
&& (max_preamble_energy[0] > eNB->measurements.prach_I0 + eNB->prach_DTX_threshold_emtc[0])) {
eNB->UL_INFO.rach_ind_br.rach_indication_body.number_of_preambles++;
eNB->preamble_list_br[ind].preamble_rel8.timing_advance = max_preamble_delay[ind]; //
eNB->preamble_list_br[ind].preamble_rel8.preamble = max_preamble[ind];
@@ -142,7 +143,7 @@ void prach_procedures(PHY_VARS_eNB *eNB,
}
} */// ce_level
} else {
if ((eNB->prach_energy_counter == 100) &&
if ((eNB->prach_energy_counter == NUM_PRACH_RX_FOR_NOISE_ESTIMATE) &&
(max_preamble_energy[0] > eNB->measurements.prach_I0+eNB->prach_DTX_threshold)) {
LOG_D(PHY,"[eNB %d/%d][RAPROC] Frame %d, subframe %d Initiating RA procedure with preamble %d, energy %d.%d dB, delay %d\n",
eNB->Mod_id,
@@ -186,7 +187,8 @@ void prach_procedures(PHY_VARS_eNB *eNB,
if (frame==0) LOG_D(PHY,"prach_I0 = %d.%d dB\n",eNB->measurements.prach_I0/10,eNB->measurements.prach_I0%10);
if (eNB->prach_energy_counter < 100) eNB->prach_energy_counter++;
if (eNB->prach_energy_counter < NUM_PRACH_RX_FOR_NOISE_ESTIMATE)
eNB->prach_energy_counter++;
}
} // else br_flag

View File

@@ -145,7 +145,8 @@ void L1_nr_prach_procedures(PHY_VARS_gNB *gNB, int frame, int slot, nfapi_nr_rac
max_preamble_delay[0],
gNB->prach_energy_counter);
if ((gNB->prach_energy_counter == 100) && (max_preamble_energy[0] > gNB->measurements.prach_I0 + gNB->prach_thres)
if ((gNB->prach_energy_counter == NUM_PRACH_RX_FOR_NOISE_ESTIMATE)
&& (max_preamble_energy[0] > gNB->measurements.prach_I0 + gNB->prach_thres)
&& (rach_ind->number_of_pdus < MAX_NUM_NR_RX_RACH_PDUS)) {
LOG_A(NR_PHY,
"[RAPROC] %d.%d Initiating RA procedure with preamble %d, energy %d.%d dB (I0 %d, thres %d), delay %d start symbol "
@@ -185,7 +186,8 @@ void L1_nr_prach_procedures(PHY_VARS_gNB *gNB, int frame, int slot, nfapi_nr_rac
}
gNB->measurements.prach_I0 = ((gNB->measurements.prach_I0*900)>>10) + ((max_preamble_energy[0]*124)>>10);
if (frame==0) LOG_I(PHY,"prach_I0 = %d.%d dB\n",gNB->measurements.prach_I0/10,gNB->measurements.prach_I0%10);
if (gNB->prach_energy_counter < 100) gNB->prach_energy_counter++;
if (gNB->prach_energy_counter < NUM_PRACH_RX_FOR_NOISE_ESTIMATE)
gNB->prach_energy_counter++;
} //if prach_id>0
rach_ind->slot = prach_start_slot;
} // for NUMBER_OF_NR_PRACH_OCCASION_MAX

View File

@@ -518,7 +518,7 @@ static void nr_ue_scheduled_response_ul(PHY_VARS_NR_UE *phy, fapi_nr_ul_config_r
pdu->pusch_config_pdu.tx_request_body.pdu_length);
}
harq_process_ul_ue->ULstatus = ACTIVE;
phy_data->ulsch.status = ACTIVE;
pdu->pdu_type = FAPI_NR_UL_CONFIG_TYPE_DONE; // not handle it any more
} break;
@@ -549,8 +549,8 @@ static void nr_ue_scheduled_response_ul(PHY_VARS_NR_UE *phy, fapi_nr_ul_config_r
case FAPI_NR_UL_CONFIG_TYPE_SRS:
// srs config pdu
phy->srs_vars[0]->srs_config_pdu = pdu->srs_config_pdu;
phy->srs_vars[0]->active = true;
phy_data->srs_vars.srs_config_pdu = pdu->srs_config_pdu;
phy_data->srs_vars.active = true;
pdu->pdu_type = FAPI_NR_UL_CONFIG_TYPE_DONE; // not handle it any more
break;

View File

@@ -175,6 +175,7 @@ void nr_fill_rx_indication(fapi_nr_rx_indication_t *rx_ind,
rx->ssb_pdu.cell_id = frame_parms->Nid_cell;
rx->ssb_pdu.ssb_start_subcarrier = frame_parms->ssb_start_subcarrier;
rx->ssb_pdu.rsrp_dBm = ue->measurements.ssb_rsrp_dBm[frame_parms->ssb_index];
rx->ssb_pdu.sinr_dB = ue->measurements.ssb_sinr_dB[frame_parms->ssb_index];
rx->ssb_pdu.arfcn = get_ssb_arfcn(frame_parms);
rx->ssb_pdu.radiolink_monitoring = RLM_in_sync; // TODO to be removed from here
rx->ssb_pdu.decoded_pdu = true;
@@ -290,7 +291,7 @@ void phy_procedures_nrUE_TX(PHY_VARS_NR_UE *ue, const UE_nr_rxtx_proc_t *proc, n
nr_ue_ulsch_procedures(ue, frame_tx, slot_tx, phy_data, (c16_t **)&txdataF, was_symbol_used);
ue_srs_procedures_nr(ue, proc, (c16_t **)&txdataF, was_symbol_used);
ue_srs_procedures_nr(ue, proc, (c16_t **)&txdataF, phy_data, was_symbol_used);
pucch_procedures_ue_nr(ue, proc, phy_data, (c16_t **)&txdataF, was_symbol_used);
@@ -485,6 +486,7 @@ static int nr_ue_pdsch_procedures(PHY_VARS_NR_UE *ue,
c16_t rxdataF[][ue->frame_parms.samples_per_slot_wCP],
int G)
{
int frame_rx = proc->frame_rx;
int nr_slot_rx = proc->nr_slot_rx;
// We handle only one CW now
@@ -496,9 +498,10 @@ static int nr_ue_pdsch_procedures(PHY_VARS_NR_UE *ue,
NR_DL_UE_HARQ_t *dlsch0_harq = &ue->dl_harq_processes[0][harq_pid];
LOG_D(PHY,
"[UE %d] nr_slot_rx %d, harq_pid %d (%d), BWP start %d, rb_start %d, nb_rb %d, symbol_start %d, nb_symbols %d, DMRS mask "
"[UE %d] frame_rx %d, nr_slot_rx %d, harq_pid %d (%d), BWP start %d, rb_start %d, nb_rb %d, symbol_start %d, nb_symbols %d, DMRS mask "
"%x, Nl %d\n",
ue->Mod_id,
frame_rx,
nr_slot_rx,
harq_pid,
dlsch0_harq->status,
@@ -558,10 +561,9 @@ static int nr_ue_pdsch_procedures(PHY_VARS_NR_UE *ue,
nvar += nvar_tmp;
#if 0
///LOG_M: the channel estimation
int nr_frame_rx = proc->frame_rx;
char filename[100];
for (uint8_t aarx=0; aarx<ue->frame_parms.nb_antennas_rx; aarx++) {
sprintf(filename,"PDSCH_CHANNEL_frame%d_slot%d_sym%d_port%d_rx%d.m", nr_frame_rx, nr_slot_rx, m, nl, aarx);
sprintf(filename,"PDSCH_CHANNEL_frame%d_slot%d_sym%d_port%d_rx%d.m", frame_rx, nr_slot_rx, m, nl, aarx);
int **dl_ch_estimates = ue->pdsch_vars[gNB_id]->dl_ch_estimates;
LOG_M(filename,"channel_F",&dl_ch_estimates[nl*ue->frame_parms.nb_antennas_rx+aarx][ue->frame_parms.ofdm_symbol_size*m],ue->frame_parms.ofdm_symbol_size, 1, 1);
}
@@ -929,6 +931,17 @@ int pbch_pdcch_processing(PHY_VARS_NR_UE *ue, const UE_nr_rxtx_proc_t *proc, nr_
sampleShift =
nr_adjust_synch_ue(fp, ue, gNB_id, fp->ofdm_symbol_size, dl_ch_estimates_time, frame_rx, nr_slot_rx, 16384);
}
if (get_nrUE_params()->cont_fo_comp && pbchSuccess == 0) {
double freq_offset = nr_ue_pbch_freq_offset(fp, estimateSz, dl_ch_estimates);
LOG_D(PHY,"compensated frequency offset = %.3f Hz, detected residual frequency offset = %.3f Hz, accumulated frequency offset = %.3f Hz\n", ue->freq_offset, freq_offset, ue->freq_off_acc);
// PI controller
const double PID_P = get_nrUE_params()->freq_sync_P;
const double PID_I = get_nrUE_params()->freq_sync_I;
ue->freq_offset += freq_offset * PID_P + ue->freq_off_acc * PID_I;
ue->freq_off_acc += freq_offset;
}
}
LOG_D(PHY, "Doing N0 measurements in %s\n", __FUNCTION__);
nr_ue_rrc_measurements(ue, proc, rxdataF);

View File

@@ -958,6 +958,13 @@ printf("%d\n", slot);
fprintf(csv_file,"avg_round,eff_rate,eff_throughput,TBS\n");
}
//---------------
Sched_INFO = malloc(sizeof(*Sched_INFO));
if (Sched_INFO == NULL) {
LOG_E(PHY, "out of memory\n");
exit(1);
}
for (SNR = snr0; SNR < snr1 && !stop; SNR += .2) {
varArray_t *table_tx=initVarArray(1000,sizeof(double));
@@ -988,6 +995,9 @@ printf("%d\n", slot);
n_false_positive = 0;
if (n_trials== 1) num_rounds = 1;
NR_gNB_DLSCH_t *gNB_dlsch = &msgDataTx->dlsch[0][0];
nfapi_nr_dl_tti_pdsch_pdu_rel15_t *rel15 = &gNB_dlsch->harq_process.pdsch_pdu.pdsch_pdu_rel15;
for (trial = 0; trial < n_trials && !stop; trial++) {
errors_bit = 0;
@@ -1003,19 +1013,11 @@ printf("%d\n", slot);
int harq_pid = slot;
NR_DL_UE_HARQ_t *UE_harq_process = &UE->dl_harq_processes[0][harq_pid];
NR_gNB_DLSCH_t *gNB_dlsch = &msgDataTx->dlsch[0][0];
nfapi_nr_dl_tti_pdsch_pdu_rel15_t *rel15 = &gNB_dlsch->harq_process.pdsch_pdu.pdsch_pdu_rel15;
UE_harq_process->decodeResult = false;
round = 0;
UE_harq_process->DLround = round;
UE_harq_process->first_rx = 1;
Sched_INFO = malloc(sizeof(*Sched_INFO));
if (Sched_INFO == NULL) {
LOG_E(PHY, "out of memory\n");
exit(1);
}
memset(Sched_INFO, 0, sizeof(*Sched_INFO));
Sched_INFO->sched_response_id = -1;
@@ -1109,10 +1111,10 @@ printf("%d\n", slot);
sprintf(filename,"txsig%d.m", aa);//LOG_M
LOG_M(filename,"txs", &txdata[aa][slot_offset +frame_parms->ofdm_symbol_size+frame_parms->nb_prefix_samples0],6*(frame_parms->ofdm_symbol_size+frame_parms->nb_prefix_samples),1,1);
}
}
if (output_fd) {
printf("writing txdata to binary file\n");
fwrite(txdata[0],sizeof(int32_t),frame_length_complex_samples,output_fd);
if (output_fd) {
printf("writing txdata to binary file\n");
fwrite(txdata[0], sizeof(int32_t), frame_length_complex_samples, output_fd);
}
}
int txlev[n_tx];
@@ -1296,15 +1298,38 @@ printf("%d\n", slot);
}
if (n_trials == 1) {
unsigned int op_format = 1;
unsigned int dec = 1;
if (output_fd) { // Write in bin format
op_format |= MATLAB_RAW;
}
LOG_M("rxsig0.m","rxs0", UE->common_vars.rxdata[0], frame_length_complex_samples, 1, 1);
LOG_M("rxsig0.m", "rxs0", UE->common_vars.rxdata[0], frame_length_complex_samples, dec, op_format);
if (UE->frame_parms.nb_antennas_rx>1)
LOG_M("rxsig1.m","rxs1", UE->common_vars.rxdata[1], frame_length_complex_samples, 1, 1);
LOG_M("rxF0.m","rxF0", UE->phy_sim_rxdataF, frame_parms->samples_per_slot_wCP, 1, 1);
LOG_M("rxF_ext.m","rxFe",UE->phy_sim_pdsch_rxdataF_ext,g_rbSize*12*14,1,1);
LOG_M("chestF0.m","chF0",UE->phy_sim_pdsch_dl_ch_estimates_ext,g_rbSize*12*14,1,1);
write_output("rxF_comp.m","rxFc",UE->phy_sim_pdsch_rxdataF_comp,N_RB_DL*12*14,1,1);
LOG_M("rxF_llr.m","rxFllr",UE->phy_sim_pdsch_llr,available_bits,1,0);
LOG_M("rxsig1.m", "rxs1", UE->common_vars.rxdata[1], frame_length_complex_samples, dec, op_format);
LOG_M("rxF0.m", "rxF0", UE->phy_sim_rxdataF, frame_parms->samples_per_slot_wCP, dec, op_format);
LOG_M("rxF_ext.m", "rxFe", UE->phy_sim_pdsch_rxdataF_ext, g_rbSize * 12 * 14, dec, op_format);
const uint32_t numReSym = (g_rbSize * 12 + 15) & (~15);
const uint32_t numValidReSym = g_rbSize * 12;
{
const int s = rel15->StartSymbolIndex;
const int n = rel15->NrOfSymbols;
for (int i = s; i < s + n; i++) {
char fName[50];
snprintf(fName, sizeof(fName), "chestF0_ext_s%d.m", i);
LOG_M(fName,
"chF0_ext",
((c16_t *)UE->phy_sim_pdsch_dl_ch_estimates_ext) + (i * numReSym),
numValidReSym,
dec,
op_format);
snprintf(fName, sizeof(fName), "rxF_comp_s%d.m", i);
LOG_M(fName, "rxFc", ((c16_t *)UE->phy_sim_pdsch_rxdataF_comp) + (i * numReSym), numValidReSym, dec, op_format);
}
}
LOG_M("chestF0.m", "chF0", UE->phy_sim_pdsch_dl_ch_estimates, frame_parms->ofdm_symbol_size * 14, dec, op_format);
op_format = (op_format & (~1));
LOG_M("rxF_llr.m", "rxFllr", UE->phy_sim_pdsch_llr, available_bits, dec, op_format);
break;
}
@@ -1316,6 +1341,8 @@ printf("%d\n", slot);
n_errs = n_errors[0];
} // NSR
free(Sched_INFO);
free_channel_desc_scm(gNB2UE);
for (i = 0; i < n_tx; i++) {

View File

@@ -239,6 +239,7 @@ int main(int argc, char *argv[])
int threadCnt=0;
int max_ldpc_iterations = 5;
int num_antennas_per_thread = 1;
uint32_t log_format = 0;
if ((uniqCfg = load_configmodule(argc, argv, CONFIG_ENABLECMDLINEONLY)) == 0) {
exit_fun("[NR_ULSIM] Error, configuration module init failed\n");
}
@@ -250,8 +251,7 @@ int main(int argc, char *argv[])
InitSinLUT();
int c;
while ((c = getopt(argc, argv, "--:O:a:b:c:d:ef:g:h:i:k:m:n:p:q:r:s:t:u:v:w:y:z:A:C:F:G:H:I:M:N:PR:S:T:U:L:ZW:E:X:")) != -1) {
while ((c = getopt(argc, argv, "--:O:a:b:c:d:ef:g:h:i:jk:m:n:p:q:r:s:t:u:v:w:y:z:A:C:F:G:H:I:M:N:PR:S:T:U:L:ZW:E:X:")) != -1) {
/* ignore long options starting with '--', option '-O' and their arguments that are handled by configmodule */
/* with this opstring getopt returns 1 for non-option arguments, refer to 'man 3 getopt' */
if (c == 1 || c == '-' || c == 'O')
@@ -345,7 +345,11 @@ int main(int argc, char *argv[])
i+=2;
} while (optarg[i-1] == ',');
break;
case 'j':
log_format |= MATLAB_RAW;
break;
case 'k':
printf("Setting threequarter_fs_flag\n");
openair0_cfg[0].threequarter_fs= 1;
@@ -523,6 +527,7 @@ int main(int argc, char *argv[])
printf("-g Channel model configuration. Arguments list: Number of arguments = 3, {Channel model: [A] TDLA30, [B] TDLB100, [C] TDLC300}, {Correlation: [l] Low, [m] Medium, [h] High}, {Maximum Doppler shift} e.g. -g A,l,10\n");
printf("-h This message\n");
printf("-i Change channel estimation technique. Arguments list: Number of arguments=2, Frequency domain {0:Linear interpolation, 1:PRB based averaging}, Time domain {0:Estimates of last DMRS symbol, 1:Average of DMRS symbols}. e.g. -i 1,0\n");
printf("-j Save signal buffers in binary format.");
printf("-k 3/4 sampling\n");
printf("-m MCS value\n");
printf("-n Number of trials to simulate\n");
@@ -559,7 +564,7 @@ int main(int argc, char *argv[])
}
}
logInit();
set_glog(loglvl);
@@ -1214,12 +1219,12 @@ int main(int argc, char *argv[])
phy_procedures_nrUE_TX(UE, &UE_proc, &phy_data);
if (n_trials == 1) {
LOG_M("txsig0.m", "txs0", &UE->common_vars.txData[0][slot_offset], slot_length, 1, 1);
LOG_M("txsig0.m", "txs0", &UE->common_vars.txData[0][slot_offset], slot_length, 1, 1 | log_format);
if (precod_nbr_layers > 1) {
LOG_M("txsig1.m", "txs1", &UE->common_vars.txData[1][slot_offset], slot_length, 1, 1);
if (precod_nbr_layers == 4) {
LOG_M("txsig2.m", "txs2", &UE->common_vars.txData[2][slot_offset], slot_length, 1, 1);
LOG_M("txsig3.m", "txs3", &UE->common_vars.txData[3][slot_offset], slot_length, 1, 1);
LOG_M("txsig1.m", "txs1", &UE->common_vars.txData[1][slot_offset], slot_length, 1, 1 | log_format);
if (precod_nbr_layers == 4) {
LOG_M("txsig2.m", "txs2", &UE->common_vars.txData[2][slot_offset], slot_length, 1, 1 | log_format);
LOG_M("txsig3.m", "txs3", &UE->common_vars.txData[3][slot_offset], slot_length, 1, 1 | log_format);
}
}
}
@@ -1293,16 +1298,16 @@ int main(int argc, char *argv[])
ul_proc_error = phy_procedures_gNB_uespec_RX(gNB, frame, slot, &UL_INFO);
if (n_trials == 1 && round == 0) {
LOG_M("rxsig0.m", "rx0", &rxdata[0][slot_offset], slot_length, 1, 1);
LOG_M("rxsigF0.m", "rxsF0", gNB->common_vars.rxdataF[0][0], 14 * frame_parms->ofdm_symbol_size, 1, 1);
LOG_M("rxsig0.m", "rx0", &rxdata[0][slot_offset], slot_length, 1, 1 | log_format);
LOG_M("rxsigF0.m", "rxsF0", gNB->common_vars.rxdataF[0][0], 14 * frame_parms->ofdm_symbol_size, 1, 1 | log_format);
if (precod_nbr_layers > 1) {
LOG_M("rxsig1.m", "rx1", &rxdata[1][slot_offset], slot_length, 1, 1);
LOG_M("rxsigF1.m", "rxsF1", gNB->common_vars.rxdataF[0][1], 14 * frame_parms->ofdm_symbol_size, 1, 1);
LOG_M("rxsigF1.m", "rxsF1", gNB->common_vars.rxdataF[0][1], 14 * frame_parms->ofdm_symbol_size, 1, 1 | log_format);
if (precod_nbr_layers == 4) {
LOG_M("rxsig2.m", "rx2", &rxdata[2][slot_offset], slot_length, 1, 1);
LOG_M("rxsig3.m", "rx3", &rxdata[3][slot_offset], slot_length, 1, 1);
LOG_M("rxsigF2.m", "rxsF2", gNB->common_vars.rxdataF[0][2], 14 * frame_parms->ofdm_symbol_size, 1, 1);
LOG_M("rxsigF3.m", "rxsF3", gNB->common_vars.rxdataF[0][3], 14 * frame_parms->ofdm_symbol_size, 1, 1);
LOG_M("rxsigF2.m", "rxsF2", gNB->common_vars.rxdataF[0][2], 14 * frame_parms->ofdm_symbol_size, 1, 1 | log_format);
LOG_M("rxsigF3.m", "rxsF3", gNB->common_vars.rxdataF[0][3], 14 * frame_parms->ofdm_symbol_size, 1, 1 | log_format);
}
}
}
@@ -1316,71 +1321,69 @@ int main(int argc, char *argv[])
&pusch_vars->ul_ch_estimates[0][start_symbol * frame_parms->ofdm_symbol_size],
frame_parms->ofdm_symbol_size,
1,
1);
1 | log_format);
LOG_M("rxsigF0_comp.m",
"rxsF0_comp",
&pusch_vars->rxdataF_comp[0][start_symbol * (off + (NR_NB_SC_PER_RB * pusch_pdu->rb_size))],
nb_symb_sch * (off + (NR_NB_SC_PER_RB * pusch_pdu->rb_size)),
1,
1);
1 | log_format);
if (precod_nbr_layers == 2) {
LOG_M("chestF3.m",
"chF3",
&pusch_vars->ul_ch_estimates[3][start_symbol * frame_parms->ofdm_symbol_size],
frame_parms->ofdm_symbol_size,
1,
1);
1 | log_format);
LOG_M("rxsigF2_comp.m",
"rxsF2_comp",
&pusch_vars->rxdataF_comp[2][start_symbol * (off + (NR_NB_SC_PER_RB * pusch_pdu->rb_size))],
nb_symb_sch * (off + (NR_NB_SC_PER_RB * pusch_pdu->rb_size)),
1,
1);
1 | log_format);
}
if (precod_nbr_layers == 4) {
LOG_M("chestF5.m",
"chF5",
&pusch_vars->ul_ch_estimates[5][start_symbol * frame_parms->ofdm_symbol_size],
frame_parms->ofdm_symbol_size,
1,
1);
1 | log_format);
LOG_M("chestF10.m",
"chF10",
&pusch_vars->ul_ch_estimates[10][start_symbol * frame_parms->ofdm_symbol_size],
frame_parms->ofdm_symbol_size,
1,
1);
1 | log_format);
LOG_M("chestF15.m",
"chF15",
&pusch_vars->ul_ch_estimates[15][start_symbol * frame_parms->ofdm_symbol_size],
frame_parms->ofdm_symbol_size,
1,
1);
1 | log_format);
LOG_M("rxsigF4_comp.m",
"rxsF4_comp",
&pusch_vars->rxdataF_comp[4][start_symbol * (off + (NR_NB_SC_PER_RB * pusch_pdu->rb_size))],
nb_symb_sch * (off + (NR_NB_SC_PER_RB * pusch_pdu->rb_size)),
1,
1);
1 | log_format);
LOG_M("rxsigF8_comp.m",
"rxsF8_comp",
&pusch_vars->rxdataF_comp[8][start_symbol * (off + (NR_NB_SC_PER_RB * pusch_pdu->rb_size))],
nb_symb_sch * (off + (NR_NB_SC_PER_RB * pusch_pdu->rb_size)),
1,
1);
1 | log_format);
LOG_M("rxsigF12_comp.m",
"rxsF12_comp",
&pusch_vars->rxdataF_comp[12][start_symbol * (off + (NR_NB_SC_PER_RB * pusch_pdu->rb_size))],
nb_symb_sch * (off + (NR_NB_SC_PER_RB * pusch_pdu->rb_size)),
1,
1);
1 | log_format);
}
LOG_M("rxsigF0_llr.m",
@@ -1388,7 +1391,7 @@ int main(int argc, char *argv[])
&pusch_vars->llr[0],
precod_nbr_layers * (nb_symb_sch - 1) * NR_NB_SC_PER_RB * pusch_pdu->rb_size * mod_order,
1,
0);
0 | log_format);
}
if ((ulsch_gNB->last_iteration_cnt >= ulsch_gNB->max_ldpc_iterations) || ul_proc_error == 1) {

View File

@@ -143,31 +143,6 @@ void __attribute__ ((no_sanitize_address)) multipath_channel(channel_desc_t *des
#else
void add_noise(c16_t **rxdata,
const double **r_re,
const double **r_im,
const double sigma,
const int length,
const int slot_offset,
const double ts,
const int delay,
const uint16_t pdu_bit_map,
const uint16_t ptrs_bit_map,
const uint8_t nb_antennas_rx)
{
for (int i = 0; i < length; i++) {
for (int ap = 0; ap < nb_antennas_rx; ap++) {
c16_t *rxd = &rxdata[ap][slot_offset + i + delay];
rxd->r = r_re[ap][i] + sqrt(sigma / 2) * gaussZiggurat(0.0, 1.0); // convert to fixed point
rxd->i = r_im[ap][i] + sqrt(sigma / 2) * gaussZiggurat(0.0, 1.0);
/* Add phase noise if enabled */
if (pdu_bit_map & ptrs_bit_map) {
phase_noise(ts, &rxdata[ap][slot_offset + i + delay].r, &rxdata[ap][slot_offset + i + delay].i);
}
}
}
}
void __attribute__ ((no_sanitize_address)) multipath_channel(channel_desc_t *desc,
double *tx_sig_re[NB_ANTENNAS_TX],
double *tx_sig_im[NB_ANTENNAS_TX],

View File

@@ -23,47 +23,41 @@
#include "sim.h"
static uint16_t LUTSin[ResolSinCos+1];
/* linear phase noise model */
void phase_noise(double ts, int16_t * InRe, int16_t * InIm)
void phase_noise(double ts, int16_t *InRe, int16_t *InIm)
{
static uint64_t i=0;
int32_t x=0 ,y=0;
double fd = 300;//0.01*30000
int16_t SinValue = 0, CosValue= 0;
double IdxDouble = (double)(i*fd * ts * ResolSinCos * 4);
int16_t IdxModulo = ((int32_t)(IdxDouble>0 ? IdxDouble+0.5 : IdxDouble-0.5)) % (ResolSinCos*4);
IdxModulo = IdxModulo<0 ? IdxModulo+ResolSinCos*4 : IdxModulo;
if(IdxModulo<2*ResolSinCos) {//< 2 check for 1st and 2nd
if(IdxModulo < ResolSinCos) {// 1st Quadrant
SinValue = LUTSin[IdxModulo];
CosValue = LUTSin[ResolSinCos-IdxModulo];
}
else {// 2nd Quadrant
SinValue = LUTSin[2*ResolSinCos-IdxModulo];
CosValue = -LUTSin[IdxModulo-ResolSinCos];
}
}
else {// 3rd and 4th Quadrant
if(IdxModulo < 3*ResolSinCos) {// 3rd Quadrant
SinValue = -LUTSin[IdxModulo-2*ResolSinCos];
CosValue = -LUTSin[3*ResolSinCos-IdxModulo];
}
else {//4th Quadrant
SinValue = -LUTSin[4*ResolSinCos-IdxModulo];
CosValue = LUTSin[IdxModulo-3*ResolSinCos];
}
}
x = ( ((int32_t)InRe[0] * CosValue) - ((int32_t)InIm[0] * SinValue ));
y = ( ((int32_t)InIm[0] * CosValue) + ((int32_t)InRe[0] * SinValue ));
InRe[0]= (int16_t)(x>>14);
InIm[0]= (int16_t)(y>>14);
double phase = (double)(i * fd * ts);
c16_t rot = get_sin_cos(phase);
c16_t val = {.r = *InRe, .i = *InIm};
val = c16mulShift(val, rot, 14);
*InRe = val.r;
*InIm = val.i;
i++;
}
/* Initialisation function for SIN table values */
void InitSinLUT( void ) {
for ( int i=0; i<(ResolSinCos+1); i++ ) {
LUTSin[i] = sin((double)(M_PI*i)/(2*ResolSinCos)) * (1<<14); //Format: Q14
void add_noise(c16_t **rxdata,
const double **r_re,
const double **r_im,
const double sigma,
const int length,
const int slot_offset,
const double ts,
const int delay,
const uint16_t pdu_bit_map,
const uint16_t ptrs_bit_map,
const uint8_t nb_antennas_rx)
{
for (int i = 0; i < length; i++) {
for (int ap = 0; ap < nb_antennas_rx; ap++) {
c16_t *rxd = &rxdata[ap][slot_offset + i + delay];
rxd->r = r_re[ap][i] + sqrt(sigma / 2) * gaussZiggurat(0.0, 1.0); // convert to fixed point
rxd->i = r_im[ap][i] + sqrt(sigma / 2) * gaussZiggurat(0.0, 1.0);
/* Add phase noise if enabled */
if (pdu_bit_map & ptrs_bit_map) {
phase_noise(ts, &rxdata[ap][slot_offset + i + delay].r, &rxdata[ap][slot_offset + i + delay].i);
}
}
}
}

View File

@@ -1663,7 +1663,7 @@ channel_desc_t *new_channel_desc_scm(uint8_t nb_tx,
maxDoppler = 0;
chan_desc->sat_height = 600e3;
chan_desc->enable_dynamic_delay = true;
chan_desc->enable_dynamic_Doppler = false; // TODO: requires UE to support continuous Doppler estimation, compensation and pre-compensation
chan_desc->enable_dynamic_Doppler = true;
fill_channel_desc(chan_desc,nb_tx,
nb_rx,
nb_taps,
@@ -1762,6 +1762,11 @@ void set_channeldesc_name(channel_desc_t *cdesc,char *modelname) {
cdesc->model_name=strdup(modelname);
}
void set_channeldesc_direction(channel_desc_t *cdesc, bool is_uplink)
{
cdesc->is_uplink = is_uplink;
}
#ifdef DEBUG_CH_POWER
double accumulated_ch_power = 0;
int ch_power_count = 0;

View File

@@ -139,6 +139,8 @@ typedef struct {
float Doppler_phase_inc;
/// current Doppler phase of each RX antenna (for continuous phase from one block to the next)
float *Doppler_phase_cur;
/// flag indicating if channel direction is UL or DL
bool is_uplink;
} channel_desc_t;
typedef struct {
@@ -358,6 +360,7 @@ void free_channel_desc_scm(channel_desc_t *ch);
\param module_id identifies the channel model. should be define as a macro in simu.h
*/
void set_channeldesc_owner(channel_desc_t *cdesc, channelmod_moduleid_t module_id);
/**
\brief This function set a model name to a model descriptor, can be later used to identify a allocated channel model
\param cdesc points to the model descriptor
@@ -365,6 +368,13 @@ void set_channeldesc_owner(channel_desc_t *cdesc, channelmod_moduleid_t module_i
*/
void set_channeldesc_name(channel_desc_t *cdesc,char *modelname);
/**
\brief This function set a channel model direction to either uplink or downlink
\param cdesc points to the model descriptor
\param is_uplink indicates if this channel is applied in uplink (not downlink) direction
*/
void set_channeldesc_direction(channel_desc_t *cdesc, bool is_uplink);
/** \fn void get_cexp_doppler(struct complexd *cexp_doppler, channel_desc_t *chan_desc, const uint32_t length)
\brief This routine generates the complex exponential to apply the Doppler shift
\param cexp_doppler Output with the complex exponential of Doppler shift
@@ -379,32 +389,6 @@ void get_cexp_doppler(struct complexd *cexp_doppler, channel_desc_t *chan_desc,
*/
int random_channel(channel_desc_t *desc, uint8_t abstraction_flag);
/**
\brief Add AWGN noise and phase noise if enabled
\param rxdata output data with noise
\param r_re real part of input data without noise
\param r_im imaginary part of input data without noise
\param sigma noise power
\param length number of samples to apply the noise
\param slot_offset slot offset to start applying the noise
\param ts sampling time
\param delay introduce delay in terms of number of samples
\param pdu_bit_map bitmap indicating presence of optional PDUs
\param ptrs_bit_map
\param nb_antennas_rx number of receive antennas
*/
void add_noise(c16_t **rxdata,
const double **r_re,
const double **r_im,
const double sigma,
const int length,
const int slot_offset,
const double ts,
const int delay,
const uint16_t pdu_bit_map,
const uint16_t ptrs_bit_map,
const uint8_t nb_antennas_rx);
/**\fn void multipath_channel(channel_desc_t *desc,
double tx_sig_re[NB_ANTENNAS_TX],
double tx_sig_im[NB_ANTENANS_TX],
@@ -570,16 +554,38 @@ double N_RB2sampling_rate(uint16_t N_RB);
double N_RB2channel_bandwidth(uint16_t N_RB);
/* Linear phase noise model */
/*!
/**
\brief This function produce phase noise and add to input signal
\param ts Sampling time
\param ts Sampling time
\param *Re *Im Real and Imag part of the signal
*/
//look-up table for the sine (cosine) function
#define ResolSinCos 100
void InitSinLUT( void );
void phase_noise(double ts, int16_t * InRe, int16_t * InIm);
void phase_noise(double ts, int16_t *InRe, int16_t *InIm);
/**
\brief Add AWGN noise and phase noise if enabled
\param rxdata output data with noise
\param r_re real part of input data without noise
\param r_im imaginary part of input data without noise
\param sigma noise power
\param length number of samples to apply the noise
\param slot_offset slot offset to start applying the noise
\param ts sampling time
\param delay introduce delay in terms of number of samples
\param pdu_bit_map bitmap indicating presence of optional PDUs
\param ptrs_bit_map
\param nb_antennas_rx number of receive antennas
*/
void add_noise(c16_t **rxdata,
const double **r_re,
const double **r_im,
const double sigma,
const int length,
const int slot_offset,
const double ts,
const int delay,
const uint16_t pdu_bit_map,
const uint16_t ptrs_bit_map,
const uint8_t nb_antennas_rx);
void do_DL_sig(sim_t *sim,
uint16_t subframe,

View File

@@ -469,6 +469,10 @@ typedef ul_info_transfer_cnf_t dl_info_transfer_cnf_t;
*/
typedef ul_info_transfer_ind_t dl_info_transfer_ind_t;
typedef struct nas_nos1_msg_s {
int dummy; // This dummy element is to avoid CLANG warning: empty struct has size 0 in C, size 1 in C++
} nas_nos1_msg_t;
typedef struct nas_pdu_session_req_s {
int pdusession_id;
int pdusession_type;

View File

@@ -25,6 +25,7 @@
#include <netinet/in.h>
#include <netinet/sctp.h>
#include "common/5g_platform_types.h"
#include "common/platform_constants.h"
//-------------------------------------------------------------------------------------------//
// Defines to access message fields.
@@ -92,12 +93,6 @@ typedef struct f1ap_net_config_t {
uint16_t DUport;
} f1ap_net_config_t;
typedef struct f1ap_plmn_t {
uint16_t mcc;
uint16_t mnc;
uint8_t mnc_digit_length;
} f1ap_plmn_t;
typedef struct f1ap_cp_tnl_s {
in_addr_t tl_address; // currently only IPv4 supported
uint16_t port;
@@ -129,7 +124,7 @@ typedef struct f1ap_tdd_info_t {
typedef struct f1ap_served_cell_info_t {
// NR CGI
f1ap_plmn_t plmn;
plmn_id_t plmn;
uint64_t nr_cellid; // NR Global Cell Id
// NR Physical Cell Ids
@@ -140,7 +135,7 @@ typedef struct f1ap_served_cell_info_t {
// Number of slice support items (max 16, could be increased to as much as 1024)
uint16_t num_ssi;
nssai_t nssai[16];
nssai_t nssai[MAX_NUM_SLICES];
f1ap_mode_t mode;
union {
@@ -192,7 +187,7 @@ typedef struct f1ap_sib_msg_t {
} f1ap_sib_msg_t;
typedef struct served_cells_to_activate_s {
f1ap_plmn_t plmn;
plmn_id_t plmn;
// NR Global Cell Id
uint64_t nr_cellid;
/// NRPCI [int 0..1007]
@@ -239,7 +234,7 @@ typedef struct f1ap_gnb_cu_configuration_update_acknowledge_s {
uint16_t num_cells_failed_to_be_activated;
struct {
// NR CGI
f1ap_plmn_t plmn;
plmn_id_t plmn;
uint64_t nr_cellid;
uint16_t cause;
} cells_failed_to_be_activated[F1AP_MAX_NB_CELLS];
@@ -256,7 +251,7 @@ typedef struct f1ap_gnb_cu_configuration_update_acknowledge_s {
struct {
uint32_t gNB_CU_ue_id;
// NR CGI
f1ap_plmn_t ue_plmn;
plmn_id_t ue_plmn;
uint64_t ue_nr_cellid;
} dedicatedSIDeliveryNeededUEs[F1AP_MAX_NO_UE_ID];
} f1ap_gnb_cu_configuration_update_acknowledge_t;
@@ -283,7 +278,7 @@ typedef struct f1ap_gnb_du_configuration_update_s {
/// int cells_to_modify
uint16_t num_cells_to_modify;
struct {
f1ap_plmn_t old_plmn;
plmn_id_t old_plmn;
uint64_t old_nr_cellid; // NR Global Cell Id
f1ap_served_cell_info_t info;
f1ap_gnb_du_system_info_t *sys_info;
@@ -293,7 +288,7 @@ typedef struct f1ap_gnb_du_configuration_update_s {
uint16_t num_cells_to_delete;
struct {
// NR CGI
f1ap_plmn_t plmn;
plmn_id_t plmn;
uint64_t nr_cellid; // NR Global Cell Id
} cell_to_delete[F1AP_MAX_NB_CELLS];
/// gNB-DU unique ID, at least within a gNB-CU (0 .. 2^36 - 1)
@@ -333,7 +328,7 @@ typedef struct f1ap_dl_rrc_message_s {
typedef struct f1ap_initial_ul_rrc_message_s {
uint32_t gNB_DU_ue_id;
f1ap_plmn_t plmn;
plmn_id_t plmn;
/// nr cell id
uint64_t nr_cellid;
/// crnti
@@ -475,7 +470,7 @@ typedef struct f1ap_ue_context_setup_s {
uint32_t gNB_CU_ue_id;
uint32_t gNB_DU_ue_id;
// SpCell Info
f1ap_plmn_t plmn;
plmn_id_t plmn;
uint64_t nr_cellid;
uint8_t servCellIndex;
uint8_t *cellULConfigured;
@@ -549,7 +544,7 @@ typedef struct f1ap_paging_ind_s {
uint16_t ueidentityindexvalue;
uint64_t fiveg_s_tmsi;
uint8_t fiveg_s_tmsi_length;
f1ap_plmn_t plmn;
plmn_id_t plmn;
uint64_t nr_cellid;
uint8_t paging_drx;
} f1ap_paging_ind_t;

View File

@@ -435,10 +435,8 @@ typedef enum ngap_paging_ind_present_s {
} ngap_paging_ind_present_t;
typedef struct {
uint16_t mcc;
uint16_t mnc;
uint8_t mnc_digit_length;
uint8_t num_nssai;
plmn_id_t plmn;
uint16_t num_nssai;
nssai_t s_nssai[8];
} ngap_plmn_t;

Some files were not shown because too many files have changed in this diff Show More