Merge remote-tracking branch 'origin/oran_beamforming_static_weights' into integration_2026_w13 (!3681)
Configure DigitalBeamTable in OAI and pass down to Aerial L1 With this MR it will be possible to define a Beamtable in OAI and pass it down to a L1 (Aerial or OAI) which then will either use them locally to apply static beamforming (Split 8 with USRP) or pass them down to the RU using 7.2 split with Section Extension 1.
This commit is contained in:
@@ -77,6 +77,7 @@
|
||||
#define CONFIG_STRING_MACRLC_BEAM_DURATION "beam_duration"
|
||||
#define CONFIG_STRING_MACRLC_BEAMS_PERIOD "beams_per_period"
|
||||
#define CONFIG_STRING_MACRLC_BEAM_WEIGHTS_LIST "beam_weights"
|
||||
#define CONFIG_STRING_MACRLC_DBT_FILE "dbt_file"
|
||||
#define CONFIG_STRING_MACRLC_PUSCH_RSSI_THRESHOLD "pusch_RSSI_Threshold"
|
||||
#define CONFIG_STRING_MACRLC_PUCCH_RSSI_THRESHOLD "pucch_RSSI_Threshold"
|
||||
#define CONFIG_STRING_MACRLC_STATS_MAX_UE "stats_max_ue"
|
||||
@@ -97,6 +98,7 @@
|
||||
#define HLP_MACRLC_AB "Flag to enable analog beamforming"
|
||||
#define HLP_MACRLC_BEAM_DURATION "number of consecutive slots for a given set of beams"
|
||||
#define HLP_MACRLC_BEAMS_PERIOD "set of beams that can be simultaneously allocated in a period"
|
||||
#define HLP_MACRLC_DBT_FILE "File path to CSV file to read digital beamforming table"
|
||||
#define HLP_MACRLC_PUSCH_RSSI_THRESHOLD "Limits PUSCH TPC commands based on RSSI to prevent ADC railing. Value range [-1280, 0], unit 0.1 dBm/dBFS"
|
||||
#define HLP_MACRLC_PUCCH_RSSI_THRESHOLD "Limits PUCCH TPC commands based on RSSI to prevent ADC railing. Value range [-1280, 0], unit 0.1 dBm/dBFS"
|
||||
#define HLP_MACRLC_STATS_MAX_UE "Maximum number of UEs before disabling periodical output (0 to disable)"
|
||||
@@ -147,6 +149,7 @@
|
||||
{CONFIG_STRING_MACRLC_BEAM_DURATION, HLP_MACRLC_BEAM_DURATION, 0, .u8ptr=NULL, .defintval=1, TYPE_UINT8, 0}, \
|
||||
{CONFIG_STRING_MACRLC_BEAMS_PERIOD, HLP_MACRLC_BEAMS_PERIOD, 0, .u8ptr=NULL, .defintval=1, TYPE_UINT8, 0}, \
|
||||
{CONFIG_STRING_MACRLC_BEAM_WEIGHTS_LIST, NULL, 0, .iptr=NULL, .defintarrayval=0, TYPE_INTARRAY,0}, \
|
||||
{CONFIG_STRING_MACRLC_DBT_FILE, HLP_MACRLC_DBT_FILE, 0, .strptr=NULL, .defstrval=NULL, TYPE_STRING, 0}, \
|
||||
{CONFIG_STRING_MACRLC_PUSCH_RSSI_THRESHOLD, HLP_MACRLC_PUSCH_RSSI_THRESHOLD, \
|
||||
0, .iptr=NULL, .defintval=0, TYPE_INT, 0}, \
|
||||
{CONFIG_STRING_MACRLC_PUCCH_RSSI_THRESHOLD, HLP_MACRLC_PUCCH_RSSI_THRESHOLD, \
|
||||
@@ -195,9 +198,10 @@
|
||||
#define MACRLC_ANALOG_BEAM_DURATION_IDX 37
|
||||
#define MACRLC_ANALOG_BEAMS_PERIOD_IDX 38
|
||||
#define MACRLC_BEAMWEIGHTS_IDX 39
|
||||
#define MACRLC_PUSCH_RSSI_THRES_IDX 40
|
||||
#define MACRLC_PUCCH_RSSI_THRES_IDX 41
|
||||
#define MACRLC_STATS_MAX_UE_IDX 42
|
||||
#define MACRLC_DBT_FILE_IDX 40
|
||||
#define MACRLC_PUSCH_RSSI_THRES_IDX 41
|
||||
#define MACRLC_PUCCH_RSSI_THRES_IDX 42
|
||||
#define MACRLC_STATS_MAX_UE_IDX 43
|
||||
|
||||
#define MACRLCPARAMS_CHECK { \
|
||||
{ .s5 = { NULL } }, \
|
||||
@@ -243,6 +247,7 @@
|
||||
{ .s5 = { NULL } }, \
|
||||
{ .s5 = { NULL } }, \
|
||||
{ .s5 = { NULL } }, \
|
||||
{ .s5 = { NULL } }, \
|
||||
{ .s2 = { config_check_intrange, {-1280, 0}} }, /* PUSCH RSSI threshold range */ \
|
||||
{ .s2 = { config_check_intrange, {-1280, 0}} }, /* PUCCH RSSI threshold range */ \
|
||||
{ .s5 = { NULL } }, \
|
||||
|
||||
@@ -28,6 +28,9 @@
|
||||
*/
|
||||
|
||||
#include "gnb_config.h"
|
||||
#include <ctype.h>
|
||||
#include <complex.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <math.h>
|
||||
#include <stdbool.h>
|
||||
@@ -1366,6 +1369,166 @@ static void get_bwp_config(nr_mac_config_t *configuration, const NR_ServingCellC
|
||||
}
|
||||
}
|
||||
|
||||
static bool parse_complex_token(const char *tok, double complex *out)
|
||||
{
|
||||
double re = 0.0;
|
||||
double im = 0.0;
|
||||
if (sscanf(tok, "%lf%lfi", &re, &im) == 2 || sscanf(tok, "%lf%lfj", &re, &im) == 2) {
|
||||
*out = re + I * im;
|
||||
return true;
|
||||
}
|
||||
if (sscanf(tok, "%lfi", &im) == 1 || sscanf(tok, "%lfj", &im) == 1) {
|
||||
*out = I * im;
|
||||
return true;
|
||||
}
|
||||
if (sscanf(tok, "%lf", &re) == 1) {
|
||||
*out = re;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static double complex **read_dbt_from_csv(const char *filename,
|
||||
int *num_beams,
|
||||
int *num_weights_per_beam,
|
||||
uint16_t **beam_ids)
|
||||
{
|
||||
FILE *fp = fopen(filename, "r");
|
||||
AssertFatal(fp != NULL, "Failed to open DBT CSV file '%s'\n", filename);
|
||||
|
||||
char line[16384];
|
||||
AssertFatal(fgets(line, sizeof(line), fp) != NULL, "Failed to read DBT CSV header from '%s'\n", filename);
|
||||
|
||||
int weights = 1;
|
||||
for (char *p = line; *p; ++p)
|
||||
if (*p == ',')
|
||||
weights++;
|
||||
weights -= 1; // first column is beam ID
|
||||
AssertFatal(weights > 0, "DBT CSV '%s' has no weight columns\n", filename);
|
||||
|
||||
int beams = 0;
|
||||
while (fgets(line, sizeof(line), fp))
|
||||
beams++;
|
||||
AssertFatal(beams > 0, "No DBT beam rows found in CSV file '%s'\n", filename);
|
||||
|
||||
rewind(fp);
|
||||
AssertFatal(fgets(line, sizeof(line), fp) != NULL, "Failed to reread DBT CSV header from '%s'\n", filename);
|
||||
|
||||
double complex **table = calloc_or_fail(beams, sizeof(*table));
|
||||
uint16_t *ids = calloc_or_fail(beams, sizeof(*ids));
|
||||
|
||||
int b = 0;
|
||||
while (fgets(line, sizeof(line), fp)) {
|
||||
line[strcspn(line, "\r\n")] = '\0';
|
||||
char *saveptr = NULL;
|
||||
char *tok = strtok_r(line, ",", &saveptr);
|
||||
AssertFatal(tok != NULL, "Malformed DBT row %d in '%s'\n", b + 1, filename);
|
||||
|
||||
errno = 0;
|
||||
char *endptr = NULL;
|
||||
long beam_id = strtol(tok, &endptr, 10);
|
||||
AssertFatal(endptr != tok && errno != ERANGE && *endptr == '\0' && beam_id >= 0 && beam_id <= UINT16_MAX,
|
||||
"Invalid DBT beam id '%s' in file '%s', beam row %d\n",
|
||||
tok,
|
||||
filename,
|
||||
b + 1);
|
||||
ids[b] = (uint16_t)beam_id;
|
||||
|
||||
table[b] = calloc_or_fail(weights, sizeof(*table[b]));
|
||||
for (int w = 0; w < weights; ++w) {
|
||||
tok = strtok_r(NULL, ",", &saveptr);
|
||||
AssertFatal(tok != NULL, "Missing DBT weight in file '%s', beam row %d column %d\n", filename, b + 1, w + 1);
|
||||
AssertFatal(parse_complex_token(tok, &table[b][w]),
|
||||
"Invalid DBT complex weight '%s' in file '%s', beam row %d column %d\n",
|
||||
tok,
|
||||
filename,
|
||||
b + 1,
|
||||
w + 1);
|
||||
}
|
||||
AssertFatal(strtok_r(NULL, ",", &saveptr) == NULL,
|
||||
"Too many DBT columns in file '%s', beam row %d\n",
|
||||
filename,
|
||||
b + 1);
|
||||
b++;
|
||||
}
|
||||
AssertFatal(b == beams, "Parsed %d DBT rows but counted %d in file '%s'\n", b, beams, filename);
|
||||
|
||||
fclose(fp);
|
||||
*num_beams = beams;
|
||||
*num_weights_per_beam = weights;
|
||||
*beam_ids = ids;
|
||||
return table;
|
||||
}
|
||||
|
||||
#define DBT_PARAMS_DESC { \
|
||||
{"", "beam", 0, .strlistptr=NULL, .defstrlistval=NULL, TYPE_STRINGLIST, 0}, \
|
||||
}
|
||||
|
||||
static double complex **read_dbt_from_config(const char *prefix,
|
||||
int *num_beams,
|
||||
int *num_weights_per_beam,
|
||||
uint16_t **beam_ids)
|
||||
{
|
||||
paramdef_t dbt_params[] = DBT_PARAMS_DESC;
|
||||
paramlist_def_t dbt_list = {"dbt", NULL, 0};
|
||||
int ret = config_getlist(config_get_if(), &dbt_list, dbt_params, sizeofArray(dbt_params), prefix);
|
||||
if (ret <= 0) {
|
||||
*num_beams = 0;
|
||||
*num_weights_per_beam = 0;
|
||||
*beam_ids = NULL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const int beams = ret;
|
||||
int weights = 0;
|
||||
double complex **table = calloc_or_fail(beams, sizeof(*table));
|
||||
uint16_t *ids = calloc_or_fail(beams, sizeof(*ids));
|
||||
|
||||
for (int b = 0; b < beams; ++b) {
|
||||
paramdef_t *row = &dbt_list.paramarray[b][0];
|
||||
AssertFatal(row->strlistptr != NULL, "Invalid dbt row %d in section '%s': missing string list\n", b, prefix);
|
||||
AssertFatal(row->numelt >= 2,
|
||||
"Invalid dbt row %d in section '%s': expected beam_id + >=1 weights, got %d elements\n",
|
||||
b,
|
||||
prefix,
|
||||
row->numelt);
|
||||
|
||||
const int row_weights = row->numelt - 1;
|
||||
if (weights == 0)
|
||||
weights = row_weights;
|
||||
AssertFatal(weights == row_weights,
|
||||
"Inconsistent dbt row width in section '%s': row %d has %d weights, expected %d\n",
|
||||
prefix,
|
||||
b,
|
||||
row_weights,
|
||||
weights);
|
||||
|
||||
errno = 0;
|
||||
char *endptr = NULL;
|
||||
long beam_id = strtol(row->strlistptr[0], &endptr, 10);
|
||||
AssertFatal(endptr != row->strlistptr[0] && errno != ERANGE && *endptr == '\0' && beam_id >= 0 && beam_id <= UINT16_MAX,
|
||||
"Invalid dbt beam id '%s' in section '%s', row %d\n",
|
||||
row->strlistptr[0],
|
||||
prefix,
|
||||
b);
|
||||
ids[b] = (uint16_t)beam_id;
|
||||
|
||||
table[b] = calloc_or_fail(weights, sizeof(*table[b]));
|
||||
for (int w = 0; w < weights; ++w)
|
||||
AssertFatal(parse_complex_token(row->strlistptr[w + 1], &table[b][w]),
|
||||
"Invalid dbt complex weight '%s' in section '%s', row %d col %d\n",
|
||||
row->strlistptr[w + 1],
|
||||
prefix,
|
||||
b,
|
||||
w + 1);
|
||||
}
|
||||
|
||||
*num_beams = beams;
|
||||
*num_weights_per_beam = weights;
|
||||
*beam_ids = ids;
|
||||
return table;
|
||||
}
|
||||
|
||||
void RCconfig_nr_macrlc(configmodule_interface_t *cfg)
|
||||
{
|
||||
int j = 0;
|
||||
@@ -1634,6 +1797,21 @@ void RCconfig_nr_macrlc(configmodule_interface_t *cfg)
|
||||
for (int b = 0; b < n; b++)
|
||||
config.bw_list[b] = b;
|
||||
}
|
||||
config.bt.num_beams = 0;
|
||||
config.bt.num_weights_per_beam = 0;
|
||||
config.bt.beam_ids = NULL;
|
||||
config.bt.beam_weights = NULL;
|
||||
char **fptr = MacRLC_ParamList.paramarray[j][MACRLC_DBT_FILE_IDX].strptr;
|
||||
if (fptr && *fptr && **fptr != '\0') {
|
||||
LOG_I(GNB_APP, "loading DBT table from file %s\n", *fptr);
|
||||
config.bt.beam_weights =
|
||||
read_dbt_from_csv(*fptr, &config.bt.num_beams, &config.bt.num_weights_per_beam, &config.bt.beam_ids);
|
||||
} else {
|
||||
char prefix[MAX_OPTNAME_SIZE * 2 + 8];
|
||||
snprintf(prefix, sizeof(prefix), CONFIG_STRING_MACRLC_LIST ".[%d]", j);
|
||||
config.bt.beam_weights =
|
||||
read_dbt_from_config(prefix, &config.bt.num_beams, &config.bt.num_weights_per_beam, &config.bt.beam_ids);
|
||||
}
|
||||
// triggers also PHY initialization in case we have L1 via FAPI
|
||||
nr_mac_config_scc(RC.nrmac[j], scc, &config);
|
||||
} // for (j=0;j<RC.nb_nr_macrlc_inst;j++)
|
||||
|
||||
Reference in New Issue
Block a user