This commit is contained in:
2026-01-08 09:17:26 +00:00
parent 85fef10860
commit cf4ca86169
35 changed files with 2493 additions and 118 deletions
+226 -22
View File
@@ -20,10 +20,10 @@
*/
/*! \file PHY/NR_TRANSPORT/nr_antenna_control.c
* \brief Test control interface implementation for antenna muting
* \brief Antenna control interface implementation with O-RAN M-Plane support
* \author OpenAirInterface
* \date 2025
* \version 1.0
* \version 2.0
*/
#include "nr_antenna_control.h"
@@ -40,15 +40,31 @@
#include <errno.h>
#include <signal.h>
// Server state
// Try to include M-Plane header if available
#ifdef OAI_MPLANE
#include "radio/fhi_72/mplane/antenna-control-mplane.h"
#include "radio/fhi_72/mplane/ru-mplane-api.h"
#endif
// Global control state
static struct {
pthread_t thread;
int server_fd;
nr_antenna_state_t *ant_state;
volatile bool running;
int port;
nr_antenna_ctrl_backend_t backend;
#ifdef OAI_MPLANE
ru_session_t *ru_session;
#else
void *ru_session;
#endif
} g_ctrl = {0};
// ============================================================================
// Test Socket Server Implementation (Development/Testing Mode)
// ============================================================================
// Process a single command and generate response
static void process_command(const char *cmd, char *response, size_t resp_size)
{
@@ -113,7 +129,8 @@ static void process_command(const char *cmd, char *response, size_t resp_size)
" Bitmask: 0x%02X\n"
"RX Array: %s\n"
" Antennas: %d configured, %d active\n"
" Bitmask: 0x%02X\n",
" Bitmask: 0x%02X\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),
@@ -121,7 +138,10 @@ static void process_command(const char *cmd, char *response, size_t resp_size)
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));
nr_get_rx_antenna_bitmask(g_ctrl.ant_state),
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");
}
else if (strcmp(p, "STATS") == 0) {
uint64_t tx_events, rx_events, muted_slots;
@@ -137,9 +157,30 @@ static void process_command(const char *cmd, char *response, size_t resp_size)
nr_reset_antenna_stats(g_ctrl.ant_state);
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);
snprintf(response, resp_size,
"OK: All antennas unmuted\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);
}
else if (strcmp(p, "SYNC") == 0) {
// Synchronize to RU (only works with M-Plane backend)
if (nr_antenna_control_sync_to_ru(g_ctrl.ant_state)) {
snprintf(response, resp_size, "OK: Synchronized to RU\n");
} else {
snprintf(response, resp_size, "ERROR: Sync failed (M-Plane backend required)\n");
}
}
else if (strcmp(p, "HELP") == 0) {
snprintf(response, resp_size,
"=== O-RAN Antenna Control Test Interface ===\n"
"=== O-RAN Antenna Control Interface ===\n"
"Commands:\n"
" MUTE TX <ant_id> - Mute TX antenna (0-based)\n"
" UNMUTE TX <ant_id> - Unmute TX antenna\n"
@@ -150,10 +191,15 @@ static void process_command(const char *cmd, char *response, size_t resp_size)
" 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");
"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");
}
else if (strcmp(p, "QUIT") == 0 || strcmp(p, "EXIT") == 0) {
snprintf(response, resp_size, "BYE\n");
@@ -172,10 +218,11 @@ static void handle_client(int client_fd)
char buffer[256];
char response[1024];
// Send welcome message
// Send welcome message with warning about non-compliance
const char *welcome =
"=== OAI Antenna Control Server ===\n"
"O-RAN WG4 MPLANE-TRX-CONTROL Test Interface\n"
"=== OAI Antenna Control Test Server ===\n"
"WARNING: This is a TEST interface, NOT O-RAN compliant!\n"
"For production, use NETCONF via radio/fhi_72/mplane/\n"
"Type HELP for commands.\n\n";
send(client_fd, welcome, strlen(welcome), 0);
@@ -216,7 +263,7 @@ static void *server_thread(void *arg)
struct sockaddr_in client_addr;
socklen_t addr_len = sizeof(client_addr);
LOG_I(PHY, "[ANTENNA_CTRL] Server listening on port %d\n", g_ctrl.port);
LOG_I(PHY, "[ANTENNA_CTRL] Test server listening on port %d\n", g_ctrl.port);
while (g_ctrl.running) {
// Accept connection with timeout
@@ -241,10 +288,50 @@ static void *server_thread(void *arg)
return NULL;
}
bool nr_antenna_control_start(nr_antenna_state_t *ant_state, int port)
// ============================================================================
// Public API Implementation
// ============================================================================
bool nr_antenna_control_init_mplane(nr_antenna_state_t *ant_state, void *ru_session)
{
#ifdef OAI_MPLANE
if (g_ctrl.running) {
LOG_W(PHY, "[ANTENNA_CTRL] Control interface already active\n");
return false;
}
if (ant_state == NULL || ru_session == NULL) {
LOG_E(PHY, "[ANTENNA_CTRL] Invalid parameters\n");
return false;
}
g_ctrl.ant_state = ant_state;
g_ctrl.ru_session = (ru_session_t *)ru_session;
g_ctrl.backend = ANTENNA_CTRL_BACKEND_MPLANE;
g_ctrl.running = true;
// Check if RU supports TRX control feature
if (!mplane_supports_trx_control(g_ctrl.ru_session)) {
LOG_W(PHY, "[ANTENNA_CTRL] RU does not support MPLANE-TRX-CONTROL feature\n");
LOG_W(PHY, "[ANTENNA_CTRL] Antenna muting may not work on this RU\n");
}
LOG_I(PHY, "[ANTENNA_CTRL] Initialized with O-RAN M-Plane NETCONF backend\n");
LOG_I(PHY, "[ANTENNA_CTRL] TX array: %s, RX array: %s\n",
ant_state->tx_array_name, ant_state->rx_array_name);
return true;
#else
LOG_E(PHY, "[ANTENNA_CTRL] M-Plane support not compiled in (OAI_MPLANE not defined)\n");
LOG_E(PHY, "[ANTENNA_CTRL] Use nr_antenna_control_start_test_server() for testing\n");
return false;
#endif
}
bool nr_antenna_control_start_test_server(nr_antenna_state_t *ant_state, int port)
{
if (g_ctrl.running) {
LOG_W(PHY, "[ANTENNA_CTRL] Server already running\n");
LOG_W(PHY, "[ANTENNA_CTRL] Control interface already active\n");
return false;
}
@@ -253,8 +340,15 @@ bool nr_antenna_control_start(nr_antenna_state_t *ant_state, int port)
return false;
}
LOG_W(PHY, "[ANTENNA_CTRL] ======================================================\n");
LOG_W(PHY, "[ANTENNA_CTRL] Starting TEST socket server - NOT O-RAN COMPLIANT!\n");
LOG_W(PHY, "[ANTENNA_CTRL] For production O-RAN compliance, use M-Plane NETCONF\n");
LOG_W(PHY, "[ANTENNA_CTRL] See: radio/fhi_72/mplane/antenna-control-mplane.h\n");
LOG_W(PHY, "[ANTENNA_CTRL] ======================================================\n");
g_ctrl.ant_state = ant_state;
g_ctrl.port = (port > 0) ? port : NR_ANTENNA_CONTROL_PORT;
g_ctrl.port = (port > 0) ? port : NR_ANTENNA_CONTROL_TEST_PORT;
g_ctrl.backend = ANTENNA_CTRL_BACKEND_TEST_SOCKET;
// Create socket
g_ctrl.server_fd = socket(AF_INET, SOCK_STREAM, 0);
@@ -296,28 +390,43 @@ bool nr_antenna_control_start(nr_antenna_state_t *ant_state, int port)
return false;
}
LOG_I(PHY, "[ANTENNA_CTRL] Server started on port %d\n", g_ctrl.port);
LOG_I(PHY, "[ANTENNA_CTRL] Test server started on port %d\n", g_ctrl.port);
LOG_I(PHY, "[ANTENNA_CTRL] Connect with: nc localhost %d\n", g_ctrl.port);
return true;
}
bool nr_antenna_control_start(nr_antenna_state_t *ant_state, int port)
{
LOG_W(PHY, "[ANTENNA_CTRL] nr_antenna_control_start() is DEPRECATED\n");
LOG_W(PHY, "[ANTENNA_CTRL] Use nr_antenna_control_init_mplane() for O-RAN compliance\n");
LOG_W(PHY, "[ANTENNA_CTRL] or nr_antenna_control_start_test_server() for testing\n");
return nr_antenna_control_start_test_server(ant_state, port);
}
void nr_antenna_control_stop(void)
{
if (!g_ctrl.running) return;
g_ctrl.running = false;
// Close server socket to unblock accept()
if (g_ctrl.server_fd >= 0) {
close(g_ctrl.server_fd);
g_ctrl.server_fd = -1;
if (g_ctrl.backend == ANTENNA_CTRL_BACKEND_TEST_SOCKET) {
// Close server socket to unblock accept()
if (g_ctrl.server_fd >= 0) {
close(g_ctrl.server_fd);
g_ctrl.server_fd = -1;
}
// Wait for thread
pthread_join(g_ctrl.thread, NULL);
}
// Wait for thread
pthread_join(g_ctrl.thread, NULL);
g_ctrl.backend = ANTENNA_CTRL_BACKEND_NONE;
g_ctrl.ant_state = NULL;
g_ctrl.ru_session = NULL;
LOG_I(PHY, "[ANTENNA_CTRL] Server stopped\n");
LOG_I(PHY, "[ANTENNA_CTRL] Control interface stopped\n");
}
bool nr_antenna_control_is_running(void)
@@ -325,3 +434,98 @@ bool nr_antenna_control_is_running(void)
return g_ctrl.running;
}
nr_antenna_ctrl_backend_t nr_antenna_control_get_backend(void)
{
return g_ctrl.backend;
}
bool nr_antenna_control_sync_to_ru(nr_antenna_state_t *ant_state)
{
#ifdef OAI_MPLANE
if (g_ctrl.backend != ANTENNA_CTRL_BACKEND_MPLANE || g_ctrl.ru_session == NULL) {
LOG_W(PHY, "[ANTENNA_CTRL] M-Plane backend not active, cannot sync to RU\n");
return false;
}
if (ant_state == NULL) {
ant_state = g_ctrl.ant_state;
}
if (ant_state == NULL) {
return false;
}
// Send TX antenna mask to RU
antenna_ctrl_result_t result;
result = mplane_set_tx_antenna_mask(g_ctrl.ru_session,
ant_state->tx_array_name,
nr_get_tx_antenna_bitmask(ant_state),
ant_state->num_tx_antennas);
if (result != ANTENNA_CTRL_SUCCESS) {
LOG_W(PHY, "[ANTENNA_CTRL] Failed to sync TX mask: %s\n",
antenna_ctrl_result_str(result));
return false;
}
// Send RX antenna mask to RU
result = mplane_set_rx_antenna_mask(g_ctrl.ru_session,
ant_state->rx_array_name,
nr_get_rx_antenna_bitmask(ant_state),
ant_state->num_rx_antennas);
if (result != ANTENNA_CTRL_SUCCESS) {
LOG_W(PHY, "[ANTENNA_CTRL] Failed to sync RX mask: %s\n",
antenna_ctrl_result_str(result));
return false;
}
LOG_I(PHY, "[ANTENNA_CTRL] Successfully synchronized antenna masks to RU\n");
return true;
#else
LOG_W(PHY, "[ANTENNA_CTRL] M-Plane not compiled in\n");
return false;
#endif
}
bool nr_antenna_control_sync_from_ru(nr_antenna_state_t *ant_state)
{
#ifdef OAI_MPLANE
if (g_ctrl.backend != ANTENNA_CTRL_BACKEND_MPLANE || g_ctrl.ru_session == NULL) {
LOG_W(PHY, "[ANTENNA_CTRL] M-Plane backend not active, cannot sync from RU\n");
return false;
}
if (ant_state == NULL) {
ant_state = g_ctrl.ant_state;
}
if (ant_state == NULL) {
return false;
}
uint8_t tx_mask, rx_mask;
antenna_ctrl_result_t result;
// Get TX antenna mask from RU
result = mplane_get_tx_antenna_mask(g_ctrl.ru_session,
ant_state->tx_array_name,
&tx_mask);
if (result == ANTENNA_CTRL_SUCCESS) {
nr_set_tx_antenna_bitmask(ant_state, tx_mask);
}
// Get RX antenna mask from RU
result = mplane_get_rx_antenna_mask(g_ctrl.ru_session,
ant_state->rx_array_name,
&rx_mask);
if (result == ANTENNA_CTRL_SUCCESS) {
nr_set_rx_antenna_bitmask(ant_state, rx_mask);
}
LOG_I(PHY, "[ANTENNA_CTRL] Synchronized antenna masks from RU: TX=0x%02X, RX=0x%02X\n",
tx_mask, rx_mask);
return true;
#else
LOG_W(PHY, "[ANTENNA_CTRL] M-Plane not compiled in\n");
return false;
#endif
}
+110 -17
View File
@@ -20,13 +20,29 @@
*/
/*! \file PHY/NR_TRANSPORT/nr_antenna_control.h
* \brief Test control interface for antenna muting (simulates O1 NETCONF interface)
* \brief O-RAN WG4 compliant antenna control interface
* \author OpenAirInterface
* \date 2025
* \version 1.0
* \note This provides a simple socket-based interface to test antenna muting
* without requiring a full NETCONF/M-plane implementation.
* In production, use the O-RAN O1 interface via radio/fhi_72/mplane/
* \version 2.0
*
* This module provides antenna control for MIMO muting / energy saving.
*
* Two modes of operation are supported:
*
* 1. O-RAN M-PLANE NETCONF (Production/O-RAN Compliant):
* When linked with the M-Plane library (radio/fhi_72/mplane/), uses
* real NETCONF sessions to control antenna masks via O-RAN YANG models:
* - o-ran-uplane-conf.yang tx-array-antenna-mask-config
* - o-ran-uplane-conf.yang rx-array-antenna-mask-config
* - Requires MPLANE-TRX-CONTROL feature support on RU
*
* 2. Test Socket Interface (Development/Testing):
* A simple TCP socket interface for testing without NETCONF infrastructure.
* NOT O-RAN compliant, but useful for development and simulation.
* Enable with nr_antenna_control_start_test_server().
*
* For O-RAN compliant deployments, use the M-Plane interface directly
* via radio/fhi_72/mplane/antenna-control-mplane.h
*/
#ifndef NR_ANTENNA_CONTROL_H
@@ -39,22 +55,56 @@
extern "C" {
#endif
/// Default port for the antenna control server
#define NR_ANTENNA_CONTROL_PORT 9999
/// Default port for the test socket server (development mode only)
#define NR_ANTENNA_CONTROL_TEST_PORT 9999
/**
* @brief Start the antenna control server thread
* @brief Antenna control backend type
*/
typedef enum {
ANTENNA_CTRL_BACKEND_NONE = 0, ///< No external control
ANTENNA_CTRL_BACKEND_MPLANE, ///< O-RAN M-Plane NETCONF (production)
ANTENNA_CTRL_BACKEND_TEST_SOCKET ///< Test socket interface (development)
} nr_antenna_ctrl_backend_t;
/**
* @brief O-RAN M-Plane session handle (opaque)
*
* Creates a simple TCP server that accepts commands to control antenna state.
* This simulates the O-RAN O1 interface for testing purposes.
* This is a forward declaration for ru_session_t from the M-Plane library.
* When the M-Plane library is linked, this allows direct NETCONF control.
*/
typedef struct ru_session ru_session_t;
/**
* @brief Initialize antenna control with O-RAN M-Plane NETCONF backend
*
* Commands supported:
* This is the O-RAN compliant production interface. It connects the
* antenna state to a real NETCONF session to an O-RU.
*
* When antenna masks are changed via nr_set_tx_antenna_bitmask() or
* similar functions, the changes are automatically propagated to the
* RU via NETCONF edit-config operations.
*
* @param ant_state Pointer to the antenna state to control
* @param ru_session Active M-Plane NETCONF session (from radio/fhi_72/mplane/)
* @return true if initialization successful
*/
bool nr_antenna_control_init_mplane(nr_antenna_state_t *ant_state,
void *ru_session);
/**
* @brief Start the test socket server (development mode only)
*
* Creates a simple TCP server for testing antenna control without
* NETCONF infrastructure. This is NOT O-RAN compliant.
*
* Commands supported (for backwards compatibility):
* MUTE TX <ant_id> - Mute TX antenna
* UNMUTE TX <ant_id> - Unmute TX antenna
* MUTE RX <ant_id> - Mute RX antenna
* UNMUTE RX <ant_id> - Unmute RX antenna
* SET TX MASK <hex> - Set TX antenna bitmask (O-RAN compliant)
* SET RX MASK <hex> - Set RX antenna bitmask (O-RAN compliant)
* SET TX MASK <hex> - Set TX antenna bitmask
* SET RX MASK <hex> - Set RX antenna bitmask
* STATUS - Get current antenna state
* STATS - Get energy saving statistics
* RESET STATS - Reset statistics
@@ -63,23 +113,66 @@ extern "C" {
* @param ant_state Pointer to the antenna state to control
* @param port TCP port to listen on (use 0 for default)
* @return true if server started successfully
*
* @note This function logs a warning that it's not O-RAN compliant
*/
bool nr_antenna_control_start_test_server(nr_antenna_state_t *ant_state, int port);
/**
* @brief Legacy function - start antenna control server
*
* @deprecated Use nr_antenna_control_init_mplane() for O-RAN compliance
* or nr_antenna_control_start_test_server() for testing.
*
* This function starts the test socket server for backwards compatibility.
* It logs a deprecation warning.
*/
bool nr_antenna_control_start(nr_antenna_state_t *ant_state, int port);
/**
* @brief Stop the antenna control server
* @brief Stop the antenna control interface
*
* Stops the test socket server if running.
* For M-Plane backend, this disconnects from NETCONF notifications.
*/
void nr_antenna_control_stop(void);
/**
* @brief Check if antenna control server is running
* @return true if server is running
* @brief Check if antenna control is active
* @return true if control interface is running
*/
bool nr_antenna_control_is_running(void);
/**
* @brief Get current antenna control backend type
* @return Backend type currently in use
*/
nr_antenna_ctrl_backend_t nr_antenna_control_get_backend(void);
/**
* @brief Synchronize local antenna state to RU via M-Plane NETCONF
*
* Sends the current antenna bitmask configuration to the RU.
* Only applicable when using the M-Plane NETCONF backend.
*
* @param ant_state Pointer to the antenna state
* @return true if synchronization successful or not applicable
*/
bool nr_antenna_control_sync_to_ru(nr_antenna_state_t *ant_state);
/**
* @brief Synchronize RU antenna state to local via M-Plane NETCONF
*
* Reads the current antenna bitmask from the RU and updates local state.
* Only applicable when using the M-Plane NETCONF backend.
*
* @param ant_state Pointer to the antenna state
* @return true if synchronization successful or not applicable
*/
bool nr_antenna_control_sync_from_ru(nr_antenna_state_t *ant_state);
#ifdef __cplusplus
}
#endif
#endif /* NR_ANTENNA_CONTROL_H */
-1
View File
@@ -203,7 +203,6 @@ void nr_feptx_prec(RU_t *ru, int frame_tx, int slot_tx)
// Check if ALL TX antennas are muted (complete transmission stop)
int active_tx_count = nr_get_active_tx_antenna_count(ant_state);
// If there is no digital beamforming we just need to copy the data to RU
if (ru->config.dbt_config.num_dig_beams == 0 || ru->gNB_list[0]->common_vars.analog_bf) {
for (int b = 0; b < ru->num_beams_period; b++) {
for (int i = 0; i < ru->nb_tx; ++i) {