diff --git a/ci-scripts/conf_files/gnb-cu.sa.band78.106prb.conf b/ci-scripts/conf_files/gnb-cu.sa.band78.106prb.conf index aded044911..063078d4d8 100644 --- a/ci-scripts/conf_files/gnb-cu.sa.band78.106prb.conf +++ b/ci-scripts/conf_files/gnb-cu.sa.band78.106prb.conf @@ -12,17 +12,17 @@ gNBs = gNB_name = "cu-rfsim"; - // Tracking area code, 0x0000 and 0xfffe are reserved values + tracking_area_code = 1; plmn_list = ({ mcc = 208; mnc = 99; mnc_length = 2; snssaiList = ({ sst = 1, sd = 0xffffff }) }); - @include "neighbour-config.conf" + # @include "neighbour-config.conf" nr_cellid = 12345678L; tr_s_preference = "f1"; - local_s_address = "192.168.71.150"; - remote_s_address = "192.168.71.171"; + local_s_address = "192.168.71.140"; + remote_s_address = "192.168.71.141"; local_s_portd = 2153; remote_s_portd = 2153; @@ -40,8 +40,8 @@ gNBs = NETWORK_INTERFACES : { - GNB_IPV4_ADDRESS_FOR_NG_AMF = "192.168.71.150"; - GNB_IPV4_ADDRESS_FOR_NGU = "192.168.71.150"; + GNB_IPV4_ADDRESS_FOR_NG_AMF = "192.168.71.140"; + GNB_IPV4_ADDRESS_FOR_NGU = "192.168.71.140"; GNB_PORT_FOR_S1U = 2152; # Spec 2152 }; } diff --git a/ci-scripts/conf_files/gnb.sa.band78.106prb.rfsim.4x4.conf b/ci-scripts/conf_files/gnb.sa.band78.106prb.rfsim.4x4.conf index ee0e667bab..5f02548259 100644 --- a/ci-scripts/conf_files/gnb.sa.band78.106prb.rfsim.4x4.conf +++ b/ci-scripts/conf_files/gnb.sa.band78.106prb.rfsim.4x4.conf @@ -25,10 +25,11 @@ gNBs = ////////// Physical parameters: - pdsch_AntennaPorts_XP = 4; + pdsch_AntennaPorts_XP = 2; pusch_AntennaPorts = 4; - do_CSIRS = 0; # used to be 1 - do_SRS = 1; + pdsch_AntennaPorts_N1 = 2; + do_CSIRS = 1; # used to be 1 + do_SRS = 0; min_rxtxtime = 6; pdcch_ConfigSIB1 = ( @@ -256,7 +257,6 @@ MACRLCs = ( #pucch_TargetSNRx10 = 200; ulsch_max_frame_inactivity = 0; dl_max_mcs = 30; - min_grant_prb = 10; } ); @@ -264,8 +264,9 @@ L1s = ( { num_cc = 1; tr_n_preference = "local_mac"; + pusch_proc_threads = 8; prach_dtx_threshold = 120; - #pucch0_dtx_threshold = 120; + pucch0_dtx_threshold = 120; } ); @@ -281,17 +282,19 @@ RUs = ( max_rxgain = 75; eNB_instances = [0]; ##beamforming 1x4 matrix: 1 layer x 4 antennas - bf_weights = [0x00007fff, 0x0000, 0x0000, 0x0000]; + # bf_weights = [0x00007fff, 0x0000, 0x0000, 0x0000]; #clock_src = "internal"; # sdr_addrs = "mgmt_addr=192.168.10.2,addr=192.168.10.2,clock_source=internal,time_source=internal" + bf_weights = [0x00007fff, 0x0000, 0x0000, 0x0000, 0x00000000, 0x00007fff, 0x0000, 0x0000, 0x0000, 0x0000, 0x00007fff, 0x0000, 0x0000, 0x0000, 0x0000, 0x00007fff]; sdr_addrs = "serial=XXXXXXX"; + clock_src = "internal"; } ); THREAD_STRUCT = ( { #three config for level of parallelism "PARALLEL_SINGLE_THREAD", "PARALLEL_RU_L1_SPLIT", or "PARALLEL_RU_L1_TRX_SPLIT" - parallel_config = "PARALLEL_SINGLE_THREAD"; + parallel_config = "PARALLEL_RU_L1_TRX_SPLIT"; #two option for worker "WORKER_DISABLE" or "WORKER_ENABLE" worker_config = "WORKER_ENABLE"; } @@ -300,6 +303,8 @@ THREAD_STRUCT = ( rfsimulator : { serveraddr = "server"; + modelname = "AWGN"; + IQfile = "/tmp/rfsimulator.iqs"; }; security = { @@ -328,114 +333,54 @@ log_config : rlc_log_level ="info"; pdcp_log_level ="info"; rrc_log_level ="info"; - ngap_log_level ="debug"; - f1ap_log_level ="debug"; + ngap_log_level ="info"; + f1ap_log_level ="info"; }; -# Channel model - minimal noise for antenna muting tests -# channelmod = { -# max_chan = 10; +# channelmod = { +# // 2 descriptors per UE (DL+UL). 10 UEs => 20. Keep margin. +# max_chan = 32; # modellist = "modellist_antenna_muting"; - + # modellist_antenna_muting = ( -# { -# model_name = "rfsimu_channel_enB0" -# type = "AWGN"; -# ploss_dB = 0; -# noise_power_dB = -50; -# forgetfact = 0; -# offset = 0; -# ds_tdl = 0; -# }, -# { -# model_name = "rfsimu_channel_ue0" -# type = "AWGN"; -# ploss_dB = 0; -# noise_power_dB = -50; -# forgetfact = 0; -# offset = 0; -# ds_tdl = 0; -# }, -# { -# model_name = "rfsimu_channel_ue1" -# type = "AWGN"; -# ploss_dB = 0; -# noise_power_dB = -50; -# forgetfact = 0; -# offset = 0; -# ds_tdl = 0; -# }, -# { -# model_name = "rfsimu_channel_ue2" -# type = "AWGN"; -# ploss_dB = 0; -# noise_power_dB = -50; -# forgetfact = 0; -# offset = 0; -# ds_tdl = 0; -# }, -# { -# model_name = "rfsimu_channel_ue3" -# type = "AWGN"; -# ploss_dB = 0; -# noise_power_dB = -50; -# forgetfact = 0; -# offset = 0; -# ds_tdl = 0; -# }, -# { -# model_name = "rfsimu_channel_ue4" -# type = "AWGN"; -# ploss_dB = 0; -# noise_power_dB = -50; -# forgetfact = 0; -# offset = 0; -# ds_tdl = 0; -# }, -# { -# model_name = "rfsimu_channel_ue5" -# type = "AWGN"; -# ploss_dB = 0; -# noise_power_dB = -50; -# forgetfact = 0; -# offset = 0; -# ds_tdl = 0; -# }, -# { -# model_name = "rfsimu_channel_ue6" -# type = "AWGN"; -# ploss_dB = 0; -# noise_power_dB = -50; -# forgetfact = 0; -# offset = 0; -# ds_tdl = 0; -# }, -# { -# model_name = "rfsimu_channel_ue7" -# type = "AWGN"; -# ploss_dB = 0; -# noise_power_dB = -50; -# forgetfact = 0; -# offset = 0; -# ds_tdl = 0; -# }, -# { -# model_name = "rfsimu_channel_ue8" -# type = "AWGN"; -# ploss_dB = 0; -# noise_power_dB = -50; -# forgetfact = 0; -# offset = 0; -# ds_tdl = 0; -# }, -# { -# model_name = "rfsimu_channel_ue9" -# type = "AWGN"; -# ploss_dB = 0; -# noise_power_dB = -50; -# forgetfact = 0; -# offset = 0; -# ds_tdl = 0; -# } +# // UE0 +# { model_name="rfsimu_dl_ue0"; type="AWGN"; ploss_dB=20; noise_power_dB=-10; forgetfact=0; offset=0; ds_tdl=0; }, +# { model_name="rfsimu_ul_ue0"; type="AWGN"; ploss_dB=20; noise_power_dB=-10; forgetfact=0; offset=0; ds_tdl=0; }, + +# // UE1 +# { model_name="rfsimu_dl_ue1"; type="AWGN"; ploss_dB=20; noise_power_dB=-10; forgetfact=0; offset=0; ds_tdl=0; }, +# { model_name="rfsimu_ul_ue1"; type="AWGN"; ploss_dB=20; noise_power_dB=-10; forgetfact=0; offset=0; ds_tdl=0; }, + +# // UE2 +# { model_name="rfsimu_dl_ue2"; type="AWGN"; ploss_dB=20; noise_power_dB=-10; forgetfact=0; offset=0; ds_tdl=0; }, +# { model_name="rfsimu_ul_ue2"; type="AWGN"; ploss_dB=20; noise_power_dB=-10; forgetfact=0; offset=0; ds_tdl=0; }, + +# // UE3 +# { model_name="rfsimu_dl_ue3"; type="AWGN"; ploss_dB=20; noise_power_dB=-10; forgetfact=0; offset=0; ds_tdl=0; }, +# { model_name="rfsimu_ul_ue3"; type="AWGN"; ploss_dB=20; noise_power_dB=-10; forgetfact=0; offset=0; ds_tdl=0; }, + +# // UE4 +# { model_name="rfsimu_dl_ue4"; type="AWGN"; ploss_dB=20; noise_power_dB=-10; forgetfact=0; offset=0; ds_tdl=0; }, +# { model_name="rfsimu_ul_ue4"; type="AWGN"; ploss_dB=20; noise_power_dB=-10; forgetfact=0; offset=0; ds_tdl=0; }, + +# // UE5 +# { model_name="rfsimu_dl_ue5"; type="AWGN"; ploss_dB=20; noise_power_dB=-10; forgetfact=0; offset=0; ds_tdl=0; }, +# { model_name="rfsimu_ul_ue5"; type="AWGN"; ploss_dB=20; noise_power_dB=-10; forgetfact=0; offset=0; ds_tdl=0; }, + +# // UE6 +# { model_name="rfsimu_dl_ue6"; type="AWGN"; ploss_dB=20; noise_power_dB=-10; forgetfact=0; offset=0; ds_tdl=0; }, +# { model_name="rfsimu_ul_ue6"; type="AWGN"; ploss_dB=20; noise_power_dB=-10; forgetfact=0; offset=0; ds_tdl=0; }, + +# // UE7 +# { model_name="rfsimu_dl_ue7"; type="AWGN"; ploss_dB=20; noise_power_dB=-10; forgetfact=0; offset=0; ds_tdl=0; }, +# { model_name="rfsimu_ul_ue7"; type="AWGN"; ploss_dB=20; noise_power_dB=-10; forgetfact=0; offset=0; ds_tdl=0; }, + +# // UE8 +# { model_name="rfsimu_dl_ue8"; type="AWGN"; ploss_dB=20; noise_power_dB=-10; forgetfact=0; offset=0; ds_tdl=0; }, +# { model_name="rfsimu_ul_ue8"; type="AWGN"; ploss_dB=20; noise_power_dB=-10; forgetfact=0; offset=0; ds_tdl=0; }, + +# // UE9 +# { model_name="rfsimu_dl_ue9"; type="AWGN"; ploss_dB=20; noise_power_dB=-10; forgetfact=0; offset=0; ds_tdl=0; }, +# { model_name="rfsimu_ul_ue9"; type="AWGN"; ploss_dB=20; noise_power_dB=-10; forgetfact=0; offset=0; ds_tdl=0; } # ); # }; diff --git a/ci-scripts/conf_files/nrue.uicc.yaml b/ci-scripts/conf_files/nrue.uicc.yaml index 0d4640833c..ad7d7c3779 100644 --- a/ci-scripts/conf_files/nrue.uicc.yaml +++ b/ci-scripts/conf_files/nrue.uicc.yaml @@ -9,23 +9,51 @@ uicc0: 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) */ +#/* Realistic channel model for antenna muting tests */ +#/* With realistic noise/path loss, antenna muting will show throughput reduction */ channelmod: max_chan: 10 modellist: modellist_rfsimu_1 modellist_rfsimu_1: - model_name: rfsimu_channel_enB0 type: AWGN - ploss_dB: 20 - noise_power_dB: -4 + ploss_dB: 20 # Realistic path loss + noise_power_dB: -10 # Moderate noise to show antenna muting impact forgetfact: 0 offset: 0 ds_tdl: 0 - model_name: rfsimu_channel_ue0 type: AWGN + ploss_dB: 20 # Realistic path loss + noise_power_dB: -10 # Moderate noise + forgetfact: 0 + offset: 0 + ds_tdl: 0 + - model_name: rfsimu_channel_ue1 + type: AWGN ploss_dB: 20 - noise_power_dB: -2 + noise_power_dB: -10 + forgetfact: 0 + offset: 0 + ds_tdl: 0 + - model_name: rfsimu_channel_ue2 + type: AWGN + ploss_dB: 20 + noise_power_dB: -10 + forgetfact: 0 + offset: 0 + ds_tdl: 0 + - model_name: rfsimu_channel_ue3 + type: AWGN + ploss_dB: 20 + noise_power_dB: -10 + forgetfact: 0 + offset: 0 + ds_tdl: 0 + - model_name: rfsimu_channel_ue4 + type: AWGN + ploss_dB: 20 + noise_power_dB: -10 forgetfact: 0 offset: 0 ds_tdl: 0 diff --git a/docker/Dockerfile.base.ubuntu b/docker/Dockerfile.base.ubuntu index 336b797c12..48b298eb17 100644 --- a/docker/Dockerfile.base.ubuntu +++ b/docker/Dockerfile.base.ubuntu @@ -47,3 +47,4 @@ RUN /bin/sh oaienv && \ cd cmake_targets && \ mkdir -p log && \ ./build_oai -I -w USRP --install-optional-packages + diff --git a/docker/Dockerfile.nakaolab.rebuild b/docker/Dockerfile.nakaolab.rebuild deleted file mode 100644 index 6214466558..0000000000 --- a/docker/Dockerfile.nakaolab.rebuild +++ /dev/null @@ -1,111 +0,0 @@ -# -# Dockerfile to rebuild nakaolab image with O-RAN antenna muting support -# -# Usage: -# cd /home/maki/openairinterface5g -# docker build -f docker/Dockerfile.nakaolab.rebuild -t nakaolab:latest . -# - -FROM ubuntu:noble AS builder - -ENV DEBIAN_FRONTEND=noninteractive -ENV TZ=Asia/Tokyo - -# Install build dependencies -RUN apt-get update && apt-get install -y \ - build-essential \ - cmake \ - ninja-build \ - ccache \ - git \ - libsctp-dev \ - libconfig-dev \ - libboost-all-dev \ - libblas-dev \ - liblapack-dev \ - liblapacke-dev \ - libyaml-cpp-dev \ - libusb-1.0-0-dev \ - python3 \ - python3-pip \ - python3-dev \ - xxd \ - libpcre2-dev \ - bison \ - flex \ - libssl-dev \ - libatlas-base-dev \ - && pip3 install pyyaml --break-system-packages \ - && apt-get clean && rm -rf /var/lib/apt/lists/* - -# Copy source code -WORKDIR /oai-ran -COPY . . - -# Build gNB and UE with rfsimulator -RUN cd cmake_targets && \ - ./build_oai --ninja --gNB --nrUE -w SIMU -c && \ - echo "Build complete" - -# Build FlexRIC if exists -RUN if [ -d "openair2/E2AP/flexric" ]; then \ - cd openair2/E2AP/flexric && \ - mkdir -p build && cd build && \ - cmake .. && make -j$(nproc) && \ - make install; \ - fi - -# -# Final image -# -FROM ubuntu:noble - -ENV DEBIAN_FRONTEND=noninteractive -ENV TZ=Asia/Tokyo - -# Install runtime dependencies -RUN apt-get update && apt-get install -y \ - libsctp1 \ - libconfig9 \ - libboost-chrono1.83.0 \ - libboost-date-time1.83.0 \ - libboost-filesystem1.83.0 \ - libboost-regex1.83.0 \ - libboost-serialization1.83.0 \ - libboost-thread1.83.0 \ - libboost-system1.83.0 \ - libboost-program-options1.83.0 \ - libblas3 \ - liblapack3 \ - libyaml-cpp0.8 \ - libusb-1.0-0 \ - net-tools \ - iputils-ping \ - iproute2 \ - iperf \ - iperf3 \ - netcat-openbsd \ - procps \ - gdb \ - python3 \ - python3-six \ - python3-requests \ - && apt-get clean && rm -rf /var/lib/apt/lists/* - -# Copy built binaries and libraries -WORKDIR /oai-ran -COPY --from=builder /oai-ran/cmake_targets/ran_build /oai-ran/cmake_targets/ran_build -COPY --from=builder /oai-ran/targets /oai-ran/targets -COPY --from=builder /oai-ran/ci-scripts/conf_files /oai-ran/ci-scripts/conf_files - -# Copy FlexRIC if built -COPY --from=builder /usr/local/lib/flexric /usr/local/lib/flexric -COPY --from=builder /usr/local/etc/flexric /usr/local/etc/flexric -RUN echo "/usr/local/lib/flexric" >> /etc/ld.so.conf.d/flexric.conf && ldconfig || true - -# Copy FlexRIC binaries -RUN mkdir -p /oai-ran/openair2/E2AP/flexric/build/examples -COPY --from=builder /oai-ran/openair2/E2AP/flexric/build/examples /oai-ran/openair2/E2AP/flexric/build/examples - -ENTRYPOINT ["/bin/bash"] - diff --git a/openair1/PHY/NR_TRANSPORT/nr_antenna_control.c b/openair1/PHY/NR_TRANSPORT/nr_antenna_control.c index 83b59fe983..27de013828 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_antenna_control.c +++ b/openair1/PHY/NR_TRANSPORT/nr_antenna_control.c @@ -122,23 +122,28 @@ static void process_command(const char *cmd, char *response, size_t resp_size) } } else if (strcmp(p, "STATUS") == 0) { + bool in_recovery = nr_is_in_recovery_mode(g_ctrl.ant_state); snprintf(response, resp_size, "=== Antenna Status ===\n" "TX Array: %s\n" " Antennas: %d configured, %d active\n" " Bitmask: 0x%02X\n" + " Antenna 0 protected: %s\n" "RX Array: %s\n" " Antennas: %d configured, %d active\n" " Bitmask: 0x%02X\n" + "Recovery Mode: %s\n" "Backend: %s\n", g_ctrl.ant_state->tx_array_name, g_ctrl.ant_state->num_tx_antennas, nr_get_active_tx_antenna_count(g_ctrl.ant_state), nr_get_tx_antenna_bitmask(g_ctrl.ant_state), + g_ctrl.ant_state->config.protect_antenna0 ? "YES" : "NO", g_ctrl.ant_state->rx_array_name, g_ctrl.ant_state->num_rx_antennas, nr_get_active_rx_antenna_count(g_ctrl.ant_state), nr_get_rx_antenna_bitmask(g_ctrl.ant_state), + in_recovery ? "ACTIVE (use identity matrix)" : "OFF", g_ctrl.backend == ANTENNA_CTRL_BACKEND_MPLANE ? "O-RAN M-Plane NETCONF" : g_ctrl.backend == ANTENNA_CTRL_BACKEND_TEST_SOCKET ? "Test Socket (NOT O-RAN)" : "None"); @@ -158,17 +163,24 @@ static void process_command(const char *cmd, char *response, size_t resp_size) snprintf(response, resp_size, "OK: Statistics reset\n"); } else if (strcmp(p, "RESET") == 0 || strcmp(p, "UNMUTE ALL") == 0) { - // Reset all antennas to active state - uint8_t all_tx = (1 << g_ctrl.ant_state->num_tx_antennas) - 1; - uint8_t all_rx = (1 << g_ctrl.ant_state->num_rx_antennas) - 1; - nr_set_tx_antenna_bitmask(g_ctrl.ant_state, all_tx); - nr_set_rx_antenna_bitmask(g_ctrl.ant_state, all_rx); + // Reset all antennas to active state with recovery mode + nr_unmute_all_tx_antennas(g_ctrl.ant_state); + nr_unmute_all_rx_antennas(g_ctrl.ant_state); snprintf(response, resp_size, - "OK: All antennas unmuted\n" + "OK: All antennas unmuted (recovery mode: %u ms)\n" " TX mask: 0x%02X (%d antennas)\n" " RX mask: 0x%02X (%d antennas)\n", - all_tx, g_ctrl.ant_state->num_tx_antennas, - all_rx, g_ctrl.ant_state->num_rx_antennas); + g_ctrl.ant_state->recovery.recovery_duration_ms, + nr_get_tx_antenna_bitmask(g_ctrl.ant_state), g_ctrl.ant_state->num_tx_antennas, + nr_get_rx_antenna_bitmask(g_ctrl.ant_state), g_ctrl.ant_state->num_rx_antennas); + } + else if (strcmp(p, "PROTECT ANT0 ON") == 0) { + nr_set_antenna0_protection(g_ctrl.ant_state, true); + snprintf(response, resp_size, "OK: Antenna 0 protection enabled (SSB)\n"); + } + else if (strcmp(p, "PROTECT ANT0 OFF") == 0) { + nr_set_antenna0_protection(g_ctrl.ant_state, false); + snprintf(response, resp_size, "OK: Antenna 0 protection disabled (WARNING: may cause UE sync loss)\n"); } else if (strcmp(p, "SYNC") == 0) { // Synchronize to RU (only works with M-Plane backend) @@ -180,26 +192,15 @@ static void process_command(const char *cmd, char *response, size_t resp_size) } else if (strcmp(p, "HELP") == 0) { snprintf(response, resp_size, - "=== O-RAN Antenna Control Interface ===\n" - "Commands:\n" - " MUTE TX - Mute TX antenna (0-based)\n" - " UNMUTE TX - Unmute TX antenna\n" - " MUTE RX - Mute RX antenna\n" - " UNMUTE RX - Unmute RX antenna\n" - " SET TX MASK - Set TX bitmask (O-RAN compliant)\n" - " SET RX MASK - Set RX bitmask (O-RAN compliant)\n" - " STATUS - Show current antenna state\n" - " STATS - Show energy saving statistics\n" - " RESET STATS - Reset statistics\n" - " RESET / UNMUTE ALL - Unmute all antennas (restore default)\n" - " SYNC - Sync state to RU (M-Plane only)\n" - " HELP - Show this help\n" - " QUIT - Close connection\n" - "\n" - "Example: SET TX MASK 03 (enables antennas 0,1; mutes 2,3)\n" - "\n" - "NOTE: This test socket interface is NOT O-RAN compliant.\n" - " For production, use NETCONF via radio/fhi_72/mplane/\n"); + "=== Antenna Control ===\n" + "MUTE TX/RX - Mute antenna\n" + "UNMUTE TX/RX - Unmute antenna\n" + "SET TX/RX MASK - Set bitmask\n" + "STATUS - Show state\n" + "STATS - Show statistics\n" + "RESET - Unmute all\n" + "PROTECT ANT0 ON/OFF\n" + "HELP/QUIT\n"); } else if (strcmp(p, "QUIT") == 0 || strcmp(p, "EXIT") == 0) { snprintf(response, resp_size, "BYE\n"); diff --git a/openair1/PHY/NR_TRANSPORT/nr_antenna_state.c b/openair1/PHY/NR_TRANSPORT/nr_antenna_state.c index d3f5ecf5db..e149f2ba5c 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_antenna_state.c +++ b/openair1/PHY/NR_TRANSPORT/nr_antenna_state.c @@ -84,10 +84,19 @@ void nr_init_antenna_state(nr_antenna_state_t *state, state->stats.last_update_timestamp = get_timestamp_us(); + // Initialize recovery mode + state->recovery.in_recovery = false; + state->recovery.recovery_start_ts = 0; + state->recovery.recovery_duration_ms = 500; // 500ms default recovery time + + // Protect antenna 0 by default (needed for SSB/PSS/SSS) + state->config.protect_antenna0 = true; + LOG_I(PHY, "[ANTENNA_STATE] Initialized: TX antennas=%d (mask=0x%02X, array=%s), " - "RX antennas=%d (mask=0x%02X, array=%s)\n", + "RX antennas=%d (mask=0x%02X, array=%s), antenna0_protected=%d\n", state->num_tx_antennas, state->tx_antenna_bitmask, state->tx_array_name, - state->num_rx_antennas, state->rx_antenna_bitmask, state->rx_array_name); + state->num_rx_antennas, state->rx_antenna_bitmask, state->rx_array_name, + state->config.protect_antenna0); } void nr_free_antenna_state(nr_antenna_state_t *state) @@ -153,13 +162,19 @@ uint8_t nr_set_rx_antenna_bitmask(nr_antenna_state_t *state, uint8_t bitmask) uint8_t nr_get_tx_antenna_bitmask(const nr_antenna_state_t *state) { if (state == NULL) return 0xFF; - return state->tx_antenna_bitmask; + pthread_mutex_lock((pthread_mutex_t*)&state->lock); + uint8_t m = state->tx_antenna_bitmask; + pthread_mutex_unlock((pthread_mutex_t*)&state->lock); + return m; } uint8_t nr_get_rx_antenna_bitmask(const nr_antenna_state_t *state) { if (state == NULL) return 0xFF; - return state->rx_antenna_bitmask; + pthread_mutex_lock((pthread_mutex_t*)&state->lock); + uint8_t m = state->rx_antenna_bitmask; + pthread_mutex_unlock((pthread_mutex_t*)&state->lock); + return m; } bool nr_is_tx_antenna_active(const nr_antenna_state_t *state, uint8_t ant_id) @@ -190,6 +205,13 @@ void nr_mute_tx_antenna(nr_antenna_state_t *state, uint8_t ant_id) { if (state == NULL || ant_id >= state->num_tx_antennas) return; + // Protect antenna 0 if configured (needed for SSB/PSS/SSS transmission) + if (ant_id == 0 && state->config.protect_antenna0) { + LOG_W(PHY, "[ANTENNA_STATE] Cannot mute TX antenna 0: protected for SSB transmission\n"); + LOG_W(PHY, "[ANTENNA_STATE] Use 'SET TX MASK' to override protection if needed\n"); + return; + } + uint8_t new_mask = state->tx_antenna_bitmask & ~(1 << ant_id); nr_set_tx_antenna_bitmask(state, new_mask); } @@ -294,3 +316,81 @@ void nr_reset_antenna_stats(nr_antenna_state_t *state) pthread_mutex_unlock(&state->lock); } +void nr_unmute_all_tx_antennas(nr_antenna_state_t *state) +{ + if (state == NULL) return; + + pthread_mutex_lock(&state->lock); + + uint8_t old_mask = state->tx_antenna_bitmask; + uint8_t all_on = (1 << state->num_tx_antennas) - 1; + state->tx_antenna_bitmask = all_on; + + // Enter recovery mode if we were muted + if (old_mask != all_on) { + state->recovery.in_recovery = true; + state->recovery.recovery_start_ts = get_timestamp_us(); + LOG_I(PHY, "[ANTENNA_STATE] Entering recovery mode for %u ms after unmuting TX antennas\n", + state->recovery.recovery_duration_ms); + } + + state->stats.tx_mute_events++; + state->stats.last_update_timestamp = get_timestamp_us(); + + LOG_I(PHY, "[ANTENNA_STATE] All TX antennas unmuted: 0x%02X -> 0x%02X (array=%s)\n", + old_mask, all_on, state->tx_array_name); + + pthread_mutex_unlock(&state->lock); +} + +void nr_unmute_all_rx_antennas(nr_antenna_state_t *state) +{ + if (state == NULL) return; + + pthread_mutex_lock(&state->lock); + + uint8_t old_mask = state->rx_antenna_bitmask; + uint8_t all_on = (1 << state->num_rx_antennas) - 1; + state->rx_antenna_bitmask = all_on; + + state->stats.rx_mute_events++; + state->stats.last_update_timestamp = get_timestamp_us(); + + LOG_I(PHY, "[ANTENNA_STATE] All RX antennas unmuted: 0x%02X -> 0x%02X (array=%s)\n", + old_mask, all_on, state->rx_array_name); + + pthread_mutex_unlock(&state->lock); +} + +bool nr_is_in_recovery_mode(const nr_antenna_state_t *state) +{ + if (state == NULL) return false; + + if (!state->recovery.in_recovery) return false; + + // Check if recovery period has elapsed + uint64_t now = get_timestamp_us(); + uint64_t elapsed_us = now - state->recovery.recovery_start_ts; + uint64_t duration_us = (uint64_t)state->recovery.recovery_duration_ms * 1000ULL; + + if (elapsed_us >= duration_us) { + // Recovery complete - cast away const for state update + nr_antenna_state_t *s = (nr_antenna_state_t *)state; + s->recovery.in_recovery = false; + LOG_I(PHY, "[ANTENNA_STATE] Recovery mode complete after %lu ms\n", elapsed_us / 1000); + return false; + } + + return true; +} + +void nr_set_antenna0_protection(nr_antenna_state_t *state, bool protect) +{ + if (state == NULL) return; + + pthread_mutex_lock(&state->lock); + state->config.protect_antenna0 = protect; + LOG_I(PHY, "[ANTENNA_STATE] Antenna 0 protection %s\n", protect ? "enabled" : "disabled"); + pthread_mutex_unlock(&state->lock); +} + diff --git a/openair1/PHY/NR_TRANSPORT/nr_antenna_state.h b/openair1/PHY/NR_TRANSPORT/nr_antenna_state.h index 742dc35bf3..406368ee84 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_antenna_state.h +++ b/openair1/PHY/NR_TRANSPORT/nr_antenna_state.h @@ -72,6 +72,18 @@ typedef struct { char tx_array_name[64]; char rx_array_name[64]; + /// Recovery mode: after unmuting, allow CSI to update before using full capacity + struct { + bool in_recovery; ///< Currently in recovery mode + uint64_t recovery_start_ts; ///< Timestamp when recovery started (us) + uint32_t recovery_duration_ms; ///< Recovery duration in milliseconds (default 500ms) + } recovery; + + /// Configuration flags + struct { + bool protect_antenna0; ///< Never mute antenna 0 (needed for SSB) + } config; + } nr_antenna_state_t; /** @@ -222,6 +234,35 @@ void nr_get_antenna_stats(const nr_antenna_state_t *state, */ void nr_reset_antenna_stats(nr_antenna_state_t *state); +/** + * @brief Unmute all TX antennas and enter recovery mode + * @param state Pointer to antenna state structure + * + * This initiates a recovery period during which the scheduler + * uses conservative settings to allow UE CSI reports to update. + */ +void nr_unmute_all_tx_antennas(nr_antenna_state_t *state); + +/** + * @brief Unmute all RX antennas + * @param state Pointer to antenna state structure + */ +void nr_unmute_all_rx_antennas(nr_antenna_state_t *state); + +/** + * @brief Check if system is in recovery mode after unmuting + * @param state Pointer to antenna state structure + * @return true if in recovery mode + */ +bool nr_is_in_recovery_mode(const nr_antenna_state_t *state); + +/** + * @brief Set antenna 0 protection (SSB antenna) + * @param state Pointer to antenna state structure + * @param protect true to prevent muting antenna 0 + */ +void nr_set_antenna0_protection(nr_antenna_state_t *state, bool protect); + #ifdef __cplusplus } #endif diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c index 7312380303..222bc33671 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c @@ -793,10 +793,10 @@ static void pf_dl(gNB_MAC_INST *mac, while (rbStart < rbStop && (rballoc_mask[rbStart + bwp_start] & slbitmap)) rbStart++; - uint16_t max_rbSize = 10; + uint16_t max_rbSize = 1; - /* while (rbStart + max_rbSize <= rbStop && !(rballoc_mask[rbStart + max_rbSize + bwp_start] & slbitmap)) */ - /* max_rbSize++; */ + while (rbStart + max_rbSize <= rbStop && !(rballoc_mask[rbStart + max_rbSize + bwp_start] & slbitmap)) + max_rbSize++; if (max_rbSize < min_rbSize) { LOG_D(NR_MAC, diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c index 910bd7ba03..a5e4c771fd 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c @@ -223,15 +223,23 @@ uint16_t get_pm_index(const gNB_MAC_INST *nrmac, return 0; } - // O-RAN MPLANE-TRX-CONTROL: When antennas are muted, the UE's PMI report - // is based on the full antenna array and may be invalid. Use identity matrix. + // O-RAN MPLANE-TRX-CONTROL: Handle antenna muting and recovery mode if (nrmac->antenna_state) { int active_tx = nr_get_active_tx_antenna_count(nrmac->antenna_state); int configured_tx = nrmac->antenna_state->num_tx_antennas; - if (active_tx < configured_tx) { - LOG_D(NR_MAC, "[ANTENNA_MUTE] Using identity matrix: %d/%d TX antennas active\n", - active_tx, configured_tx); - return 0; // Use identity matrix when any antennas are muted + + // During recovery mode after unmuting, use identity matrix to allow CSI update + if (nr_is_in_recovery_mode(nrmac->antenna_state)) { + LOG_D(NR_MAC, "[ANTENNA_MUTE] Recovery mode active, using identity matrix\n"); + return 0; + } + + // When antennas are muted and we're using fewer layers than configured, + // use identity matrix for robustness (UE's PMI based on old config) + if (active_tx < configured_tx && layers < configured_tx) { + LOG_D(NR_MAC, "[ANTENNA_MUTE] Using identity matrix: %d/%d TX antennas active, %d layers\n", + active_tx, configured_tx, layers); + return 0; } } const int report_id = sched_ctrl->CSI_report.cri_ri_li_pmi_cqi_report.csi_report_id; diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c index aabfe90dc1..9d9000245e 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c @@ -2176,8 +2176,8 @@ static int pf_ul(gNB_MAC_INST *nrmac, /* if it's for inactivity, min_grant_prb is enough, otherwise check what * would be the maximum */ - /* uint16_t max_rbSize = iterator->sched_inactive ? min_rb : bi.bwpSize; */ - uint16_t max_rbSize = min_rb; + uint16_t max_rbSize = iterator->sched_inactive ? min_rb : bi.bwpSize; + /* uint16_t max_rbSize = min_rb; */ uint16_t available_rb = 1; while (rbStart + available_rb < bi.bwpSize && !(rballoc_mask[rbStart + bi.bwpStart + available_rb] & slbitmap) && available_rb < max_rbSize) available_rb++; diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.273prb.fhi72.4x4-liteon.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.273prb.fhi72.4x4-liteon.conf index 3c9780130f..09b40913f9 100644 --- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.273prb.fhi72.4x4-liteon.conf +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.273prb.fhi72.4x4-liteon.conf @@ -250,11 +250,11 @@ log_config : }; fhi_72 = { - dpdk_devices = ("0000:c1:11.0", "0000:c1:11.1"); # one VF can be used as well + dpdk_devices = ("0000:01:02.0", "0000:01:02.1"); # one VF can be used as well system_core = 0; io_core = 1; worker_cores = (2); - ru_addr = ("e8:c7:4f:25:81:b3", "e8:c7:4f:25:81:b3"); + ru_addr = ("e8:c7:4f:25:7f:f6", "e8:c7:4f:25:81:b3"); mtu = 9600; # note: 1500 works as well, with both E and F releases fh_config = ({ T1a_cp_dl = (285, 429); diff --git a/tools/scripts/traffic_runner.sh b/tools/scripts/traffic_runner.sh index 96570253cb..1fe7596dd4 100755 --- a/tools/scripts/traffic_runner.sh +++ b/tools/scripts/traffic_runner.sh @@ -41,9 +41,9 @@ iperf_udp_ul_forever() { ( iperf -u -c "$IPERF_HOST_UDP" -B "$OAITUN_IP" -p "$IP # ---------------- profiles ---------------- do_boring() { - iperf_udp_dl_forever 15M + iperf_udp_dl_forever 3M wait - # iperf -c "$IPERF_HOST" -p "$IPERF_PORT" -B "$OAITUN_IP" -u -b 25M -t "$LONG_T" + # iperf -c "$IPERF_HOST" -p "$IPERF_PORT" -B "$OAITUN_IP" -u -b 25M -t "$LONG_T" } do_browsing() {