From ccf5021072d1bcb3af6cd4b6c7dbbf2008b0e405 Mon Sep 17 00:00:00 2001 From: Merkebu Girmay Date: Thu, 30 Oct 2025 07:55:33 -0700 Subject: [PATCH 1/3] radio/vrtsim: Add CIRDB support for precomputed channel taps Integrate Channel Impulse Response Database (CIRDB) to enable playback of precomputed realistic channel taps in vrtsim, with runtime selection via YAML metadata. Features: - Read precomputed channel taps from binary database with YAML sidecar - Runtime enable via --vrtsim.cirdb with optional paths (cirdb_yaml, cirdb_file, cirdb_path) - Channel parameter configuration: model_id (TDL-A..E), delay spread (ds_ns), UE speed (speed_mps) - Fallback to existing channel model options when disabled - Time-driven snapshot progression based on elapsed simulation time Fixes: - RNG initialization: Add randominit() call before gaussZiggurat() - FlatBuffers linking: Handle both 'FlatBuffers' and 'flatbuffers' package names when working with external channel emmulator - Address review comments: use pthread directly, update calloc_or_fail, simplify CMake --- radio/vrtsim/CMakeLists.txt | 30 +- radio/vrtsim/cirdb_provider.c | 348 ++++++++++++++++++ radio/vrtsim/cirdb_provider.h | 60 +++ radio/vrtsim/cirdb_yaml.cpp | 152 ++++++++ radio/vrtsim/cirdb_yaml.h | 61 +++ radio/vrtsim/vrtsim.c | 92 ++++- .../gnb.sa.band78.fr1.106PRB.usrpb210.conf | 12 + 7 files changed, 745 insertions(+), 10 deletions(-) create mode 100644 radio/vrtsim/cirdb_provider.c create mode 100644 radio/vrtsim/cirdb_provider.h create mode 100644 radio/vrtsim/cirdb_yaml.cpp create mode 100644 radio/vrtsim/cirdb_yaml.h diff --git a/radio/vrtsim/CMakeLists.txt b/radio/vrtsim/CMakeLists.txt index 71d18cc5bc..9726743361 100644 --- a/radio/vrtsim/CMakeLists.txt +++ b/radio/vrtsim/CMakeLists.txt @@ -1,13 +1,36 @@ +# Noise device static lib add_library(noise_device STATIC noise_device.c) target_link_libraries(noise_device PRIVATE log_headers) -add_library(vrtsim MODULE vrtsim.c) -set_target_properties(vrtsim PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) -target_link_libraries(vrtsim PRIVATE SIMU shm_td_iq_channel actor noise_device log_headers) +# vrtsim module +add_library(vrtsim MODULE + vrtsim.c + cirdb_provider.c + cirdb_yaml.cpp +) +set_target_properties(vrtsim PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) + +find_package(yaml-cpp REQUIRED) + +# Link libraries - always link yaml-cpp +target_link_libraries(vrtsim + PRIVATE + SIMU + shm_td_iq_channel + actor + noise_device + log_headers + pthread + yaml-cpp +) + +# Optional taps client add_boolean_option(OAI_VRTSIM_TAPS_CLIENT OFF "Enable taps client" ON) if (OAI_VRTSIM_TAPS_CLIENT) + target_compile_definitions(vrtsim PRIVATE ENABLE_TAPS_CLIENT=1) target_link_libraries(vrtsim PRIVATE taps_client) + file(DOWNLOAD "https://gitlab.eurecom.fr/oai/raytracing-channel-emulator/-/raw/59589ad2a534881810b2aa7dd81e63066245bb8d/server/api/taps.fbs?inline=false" ${CMAKE_SOURCE_DIR}/taps.fbs @@ -21,6 +44,7 @@ if (OAI_VRTSIM_TAPS_CLIENT) DEPENDS ${TAPS_IF_PATH} COMMENT "Generating flatbuffers API from ${TAPS_IF_PATH}" ) + add_library(taps_api INTERFACE) target_include_directories(taps_api INTERFACE ${CMAKE_CURRENT_BINARY_DIR}) diff --git a/radio/vrtsim/cirdb_provider.c b/radio/vrtsim/cirdb_provider.c new file mode 100644 index 0000000000..39a7bff29c --- /dev/null +++ b/radio/vrtsim/cirdb_provider.c @@ -0,0 +1,348 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include "common/utils/LOG/log.h" +#include "common/utils/assertions.h" +#include "SIMULATION/TOOLS/sim.h" + +#include "cirdb_provider.h" +#include "cirdb_yaml.h" + +#define NUM_TAPS_BUFFERS 4 +#define MAX_L_PUBLISH 8 + +static inline double dabs(double x) +{ + return x < 0.0 ? -x : x; +} + +typedef struct { + void *taps_blob; /* contiguous complexf for all links */ + channel_desc_t *ch; /* ch_ps points into taps_blob */ +} cirdb_buffer_t; + +typedef struct { + /* I/O */ + FILE *fh; + + /* Selected entry meta */ + int model_id; + int n_tx; + int n_rx; + int L_full; + int S; + double fs_hz; + double snapshot_dt_s; + float ds_ns; + float speed_mps; + uint64_t sel_offset; + uint64_t sel_nbytes; + + /* Publication control */ + uint32_t L_out; + uint32_t snap_idx; + + /* Shape in publication */ + int num_tx; + int num_rx; + + /* Double buffering for readers */ + cirdb_buffer_t bufs[NUM_TAPS_BUFFERS]; + int cur; + + /* Temporary read buffer for one snapshot of full L_full taps */ + void *snapshot_tmp; + size_t snapshot_tmp_bytes; + + /* Published pointer location owned by caller */ + channel_desc_t **channel_desc_out; + + /* Bookkeeping of last step computed from elapsed time */ + int64_t last_step_applied; /* -1 until first update */ +} cirdb_g; + +static cirdb_g G; + +static inline void fread_exact(void *dst, size_t sz, FILE *fh) +{ + size_t r = fread(dst, 1, sz, fh); + if (r != sz) { + LOG_E(HW, "CIRDB short read: expected %zu got %zu errno=%d\n", sz, r, errno); + abort(); + } +} + +static inline size_t cf_bytes(size_t n) +{ + return n * sizeof(struct complexf); +} + +/* Point channel_desc ch_ps into a compacted taps buffer */ +static void point_channel_desc(channel_desc_t *ch, struct complexf *base, int num_tx, int num_rx, size_t L_out) +{ + for (int aarx = 0; aarx < num_rx; aarx++) { + for (int aatx = 0; aatx < num_tx; aatx++) { + int link = aarx + num_rx * aatx; + ch->ch_ps[link] = &base[link * L_out]; + } + } + ch->channel_length = L_out; + ch->path_loss_dB = 0; + ch->nb_tx = num_tx; + ch->nb_rx = num_rx; +} + +/* Copy first L_out taps per link from a full L_full snapshot */ +static void compact_L_out(struct complexf *dst, + const struct complexf *src_full, + int num_tx, + int num_rx, + size_t L_full, + size_t L_out) +{ + for (int aatx = 0; aatx < num_tx; aatx++) { + for (int aarx = 0; aarx < num_rx; aarx++) { + size_t link = aarx + num_rx * aatx; + const struct complexf *src = &src_full[link * L_full]; + struct complexf *dst_link = &dst[link * L_out]; + memcpy(dst_link, src, cf_bytes(L_out)); + } + } +} + +/* Load snapshot index s into the next publication buffer and flip */ +static void load_snapshot_and_publish(uint32_t s) +{ + uint64_t stride = (uint64_t)G.n_tx * G.n_rx * G.L_full * sizeof(struct complexf); + uint64_t off = G.sel_offset + (uint64_t)s * stride; + + if (fseeko(G.fh, (off_t)off, SEEK_SET) != 0) { + LOG_E(HW, "CIRDB fseoko failed errno=%d\n", errno); + abort(); + } + fread_exact(G.snapshot_tmp, (size_t)stride, G.fh); + + int next = (G.cur + 1) % NUM_TAPS_BUFFERS; + cirdb_buffer_t *buf = &G.bufs[next]; + + compact_L_out((struct complexf *)buf->taps_blob, (const struct complexf *)G.snapshot_tmp, G.n_tx, G.n_rx, G.L_full, G.L_out); + + point_channel_desc(buf->ch, (struct complexf *)buf->taps_blob, G.n_tx, G.n_rx, G.L_out); + + *G.channel_desc_out = buf->ch; + G.cur = next; +} + +void cirdb_connect(int id, + int num_tx_antennas, + int num_rx_antennas, + const cirdb_select_opts_t *sel, + channel_desc_t **channel_desc_out) +{ + (void)id; + memset(&G, 0, sizeof(G)); + G.channel_desc_out = channel_desc_out; + G.num_tx = num_tx_antennas; + G.num_rx = num_rx_antennas; + G.cur = 0; + G.last_step_applied = -1; + + /* Require explicit paths in cirdb_select_opts_t */ + AssertFatal(sel != NULL, "cirdb_select_opts_t must not be NULL\n"); + AssertFatal(sel->bin_path && sel->bin_path[0], "CIRDB requires explicit bin_path in cirdb_select_opts_t\n"); + AssertFatal(sel->yaml_path && sel->yaml_path[0], "CIRDB requires explicit yaml_path in cirdb_select_opts_t\n"); + + const char *use_bin = sel->bin_path; + const char *sidecar = sel->yaml_path; + + LOG_I(HW, "CIRDB: using database %s with metadata %s\n", use_bin, sidecar); + + G.fh = fopen(use_bin, "rb"); + AssertFatal(G.fh != NULL, "Cannot open CIRDB database file '%s': %s\n", use_bin, strerror(errno)); + + /* Selection request */ + int want_model_id = sel->want_model_id; + AssertFatal(want_model_id >= 0 && want_model_id <= 4, + "Invalid model_id=%d (valid: 0..4)\n", + want_model_id); + float want_ds = (sel && sel->want_ds_ns > 0) ? sel->want_ds_ns : -1.0f; + float want_speed = (sel && sel->want_speed_mps > 0) ? sel->want_speed_mps : -1.0f; + + LOG_I(HW, + "CIRDB: Searching for entry with model_id=%d, antennas=%dx%d, DS=%.1fns, speed=%.1fm/s\n", + want_model_id, + G.num_tx, + G.num_rx, + want_ds, + want_speed); + + cirdb_entry_meta_t m = (cirdb_entry_meta_t){0}; + cirdb_select_req_t req = {.want_model_id = want_model_id, + .want_tx = G.num_tx, + .want_rx = G.num_rx, + .want_ds_ns = want_ds, + .want_speed_mps = want_speed, + .allow_shape_swap = 0, + .w_ds = 1.0f, + .w_speed = 0.2f, + .yaml_path = sidecar}; + + int ok = cirdb_yaml_select(&req, &m); + + // STRICT validation: fail if no match found + AssertFatal(ok > 0, + "No suitable CIR entry found in YAML %s\n" + " Requested: model_id=%d, antennas=%dx%d, DS=%.1fns, speed=%.1fm/s\n" + " Possible causes:\n" + " - No entry with model_id=%d in database\n" + " - No entry with antenna configuration %dx%d in database\n" + " Check the YAML file for available entries\n", + sidecar, + want_model_id, + G.num_tx, + G.num_rx, + want_ds, + want_speed, + want_model_id, + G.num_tx, + G.num_rx); + + // STRICT validation: antenna dimensions must match exactly + AssertFatal(m.n_tx == G.num_tx && m.n_rx == G.num_rx, + "CIRDB antenna mismatch: requested %dx%d but selected entry has %dx%d\n" + " This should never happen - indicates a bug in selection logic\n", + G.num_tx, + G.num_rx, + m.n_tx, + m.n_rx); + + // STRICT validation: model_id must match if specified + if (want_model_id >= 0) { + AssertFatal(m.model_id == want_model_id, + "CIRDB model_id mismatch: requested %d but selected entry has %d\n" + " This should never happen - indicates a bug in selection logic\n", + want_model_id, + m.model_id); + } + + // FLEXIBLE: warn if delay spread doesn't match exactly (using closest available) + if (want_ds > 0 && dabs(m.ds_ns - want_ds) > 0.1) { + LOG_W(HW, "CIRDB: Requested delay spread %.1f ns not available in database, using closest match: %.1f ns\n", want_ds, m.ds_ns); + } + + // FLEXIBLE: warn if speed doesn't match exactly (using closest available) + if (want_speed > 0 && dabs(m.speed_mps - want_speed) > 0.1) { + LOG_W(HW, + "CIRDB: Requested speed %.1f m/s not available in database, using closest match: %.1f m/s\n", + want_speed, + m.speed_mps); + } + + G.model_id = m.model_id; + G.n_tx = m.n_tx; + G.n_rx = m.n_rx; + G.L_full = m.L; + G.S = m.S; + G.fs_hz = m.fs_hz; + G.snapshot_dt_s = m.snapshot_dt_s; + G.ds_ns = m.ds_ns; + G.speed_mps = m.speed_mps; + G.sel_offset = m.offset_bytes; + G.sel_nbytes = m.nbytes; + G.L_out = (m.L <= MAX_L_PUBLISH ? (uint32_t)m.L : MAX_L_PUBLISH); + G.snap_idx = 0; + + G.snapshot_tmp_bytes = (size_t)G.n_tx * G.n_rx * G.L_full * sizeof(struct complexf); + G.snapshot_tmp = calloc_or_fail(1, G.snapshot_tmp_bytes); + + for (int i = 0; i < NUM_TAPS_BUFFERS; i++) { + size_t blob_cf = (size_t)G.n_tx * G.n_rx * G.L_out; + G.bufs[i].taps_blob = calloc_or_fail(blob_cf, sizeof(struct complexf)); + G.bufs[i].ch = calloc_or_fail(1, sizeof(channel_desc_t)); + G.bufs[i].ch->ch_ps = calloc_or_fail(G.n_rx * G.n_tx, sizeof(struct complexf *)); + G.bufs[i].ch->nb_tx = G.n_tx; + G.bufs[i].ch->nb_rx = G.n_rx; + } + + if (G.S > 0) { + load_snapshot_and_publish(0); + } + + LOG_I(HW, + "CIRDB: Selected entry - model=%d DS=%.3fns shape=%ux%u L=%u/%u S=%u fs=%.0f dt=%.6fs speed=%.3fm/s\n", + G.model_id, + G.ds_ns, + G.n_tx, + G.n_rx, + G.L_out, + G.L_full, + G.S, + G.fs_hz, + G.snapshot_dt_s, + G.speed_mps); +} + +void cirdb_update(uint64_t ns_since_start) +{ + if (!G.channel_desc_out || !*G.channel_desc_out) + return; + if (G.S <= 0) + return; + + double dt_s = (G.snapshot_dt_s > 0.0 ? G.snapshot_dt_s : 0.5); + if (dt_s <= 0.0) + dt_s = 0.5; + + double steps_f = (ns_since_start * 1e-9) / dt_s; + int64_t step = (int64_t)(steps_f >= 0.0 ? steps_f : 0.0); + + if (step != G.last_step_applied) { + uint32_t s = (uint32_t)(step % G.S); + load_snapshot_and_publish(s); + G.snap_idx = s; + G.last_step_applied = step; + } +} + +void cirdb_stop(void) +{ + for (int i = 0; i < NUM_TAPS_BUFFERS; i++) { + free(G.bufs[i].taps_blob); + if (G.bufs[i].ch) { + free(G.bufs[i].ch->ch_ps); + free(G.bufs[i].ch); + } + } + free(G.snapshot_tmp); + if (G.fh) + fclose(G.fh); + memset(&G, 0, sizeof(G)); + G.last_step_applied = -1; +} diff --git a/radio/vrtsim/cirdb_provider.h b/radio/vrtsim/cirdb_provider.h new file mode 100644 index 0000000000..42e49f3c5e --- /dev/null +++ b/radio/vrtsim/cirdb_provider.h @@ -0,0 +1,60 @@ +/* + * 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 OAI_VRTSIM_CIRDB_PROVIDER_H +#define OAI_VRTSIM_CIRDB_PROVIDER_H + +#include +#include "SIMULATION/TOOLS/sim.h" + +/* + Selection and timing options: + + want_model_id: TDL family index. Allowed: 0 (TDL-A), 1 (TDL-B), 2 (TDL-C), 3 (TDL-D), 4 (TDL-E). + Set < 0 to express no preference. + want_ds_ns: RMS delay spread in ns. Typical values: 30, 100, 300. Nonnegative float. + Set < 0 to express no preference. + want_speed_mps: UE speed in m/s. Typical: walking ≈ 1.5, urban 3..10, vehicular 10..30. + Set < 0 to express no preference. +*/ +typedef struct { + const char *yaml_path; /* optional absolute path to vrtsim.yaml */ + const char *bin_path; /* optional absolute path to cir_db.bin */ + + int want_model_id; /* 0..4, or <0 for no preference */ + float want_ds_ns; /* nonnegative, or <0 for no preference */ + float want_speed_mps; /* nonnegative, or <0 for no preference */ +} cirdb_select_opts_t; + +/* Initialize provider and publish snapshot 0 through channel_desc_out. */ +void cirdb_connect(int id, + int num_tx_antennas, + int num_rx_antennas, + const cirdb_select_opts_t *sel, + channel_desc_t **channel_desc_out); + +/* Advance snapshot selection based on elapsed nanoseconds since the start. */ +void cirdb_update(uint64_t ns_since_start); + +/* Tear down and free resources. */ +void cirdb_stop(void); + +#endif /* OAI_VRTSIM_CIRDB_PROVIDER_H */ diff --git a/radio/vrtsim/cirdb_yaml.cpp b/radio/vrtsim/cirdb_yaml.cpp new file mode 100644 index 0000000000..aa95b09505 --- /dev/null +++ b/radio/vrtsim/cirdb_yaml.cpp @@ -0,0 +1,152 @@ +/* + * 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 "cirdb_yaml.h" +#include +#include +#include +extern "C" { +#include "common/utils/LOG/log.h" +} + +static inline double dabs(double x) +{ + return x < 0 ? -x : x; +} + +int cirdb_yaml_scan(const char *yaml_path) +{ + YAML::Node root; + try { + root = YAML::LoadFile(yaml_path); + } catch (const YAML::Exception &e) { + LOG_E(HW, "CIRDB YAML parse error in %s: %s\n", yaml_path, e.what()); + return -1; + } + auto ents = root["entries"]; + if (!ents || !ents.IsSequence()) { + LOG_E(HW, "CIRDB YAML: missing or invalid 'entries' in %s\n", yaml_path); + return -1; + } + + int n = static_cast(ents.size()); + LOG_I(HW, "CIRDB YAML: %d entries in %s\n", n, yaml_path); + return n; +} + +int cirdb_yaml_select(const cirdb_select_req_t *req, cirdb_entry_meta_t *out) +{ + if (!req || !out || !req->yaml_path) { + LOG_E(HW, "CIRDB YAML: invalid arguments to cirdb_yaml_select\n"); + return -1; + } + + YAML::Node root; + try { + root = YAML::LoadFile(req->yaml_path); + } catch (const YAML::Exception &e) { + LOG_E(HW, "CIRDB YAML parse error in %s: %s\n", req->yaml_path, e.what()); + return -1; + } + + auto ents = root["entries"]; + if (!ents || !ents.IsSequence()) { + LOG_E(HW, "CIRDB YAML: missing or invalid 'entries' in %s\n", req->yaml_path); + return -1; + } + + bool found = false; + double best_cost = 1e300; + cirdb_entry_meta_t best{}; + + // Determine if specific parameters are required + bool want_specific_model = (req->want_model_id >= 0); + bool want_specific_antennas = (req->want_tx > 0 && req->want_rx > 0); + + for (std::size_t i = 0; i < ents.size(); ++i) { + const YAML::Node e = ents[i]; + int pair_order = e["pair_order"].as(0); + + int model_id = e["model_id"].as(-1); + + // STRICT: model_id must match if specified + if (want_specific_model && model_id != req->want_model_id) + continue; + + int n_tx = e["n_tx"].as(0); + int n_rx = e["n_rx"].as(0); + + // STRICT: antenna dimensions must match exactly if specified + bool shape_ok = true; + if (want_specific_antennas) { + shape_ok = (n_tx == req->want_tx && n_rx == req->want_rx); + bool shape_swap_ok = req->allow_shape_swap ? (n_tx == req->want_rx && n_rx == req->want_tx) : false; + shape_ok = shape_ok || shape_swap_ok; + + if (!shape_ok) + continue; // Skip entries with wrong antenna configuration + } + + // STRICT: antenna dimensions must match exactly if specified + if (want_specific_antennas) { + if (n_tx != req->want_tx || n_rx != req->want_rx) + continue; // Skip entries with wrong antenna configuration + } + + float ds_ns = e["ds_ns"].as(0.0f); + float sp = e["speed_mps"].as(0.0f); + + // FLEXIBLE: delay spread and speed - find closest match using cost function + double cds = 0.0; + double csp = 0.0; + + if (req->want_ds_ns > 0) + cds = dabs((double)ds_ns - (double)req->want_ds_ns); + + if (req->want_speed_mps > 0) + csp = dabs((double)sp - (double)req->want_speed_mps); + + double cost = (req->w_ds > 0 ? req->w_ds : 1.0) * cds + (req->w_speed > 0 ? req->w_speed : 0.2) * csp; + + if (!found || cost < best_cost) { + best_cost = cost; + found = true; + + best.model_id = model_id; + best.n_tx = n_tx; + best.n_rx = n_rx; + best.L = e["L"].as(0); + best.S = e["S"].as(0); + best.fs_hz = e["fs_hz"].as(0.0); + best.snapshot_dt_s = e["snapshot_dt_s"].as(0.0); + best.ds_ns = ds_ns; + best.speed_mps = sp; + best.pair_order = pair_order; + best.offset_bytes = e["offset_bytes"].as(0); + best.nbytes = e["nbytes"].as(0); + } + } + + if (!found) + return 0; + *out = best; + return 1; +} diff --git a/radio/vrtsim/cirdb_yaml.h b/radio/vrtsim/cirdb_yaml.h new file mode 100644 index 0000000000..e3cf61f8cf --- /dev/null +++ b/radio/vrtsim/cirdb_yaml.h @@ -0,0 +1,61 @@ +/* + * 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 + */ + +#pragma once +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + int want_model_id; // 0..4 for TDL-A..E, or -1 for any + int want_tx; // exact match + int want_rx; // exact match + float want_ds_ns; // nearest + float want_speed_mps; // nearest + int allow_shape_swap; // allow TX/RX swap when matching + float w_ds; // DS distance weight + float w_speed; // speed distance weight + const char *yaml_path; // sidecar path +} cirdb_select_req_t; + +typedef struct { + int model_id; + int n_tx; + int n_rx; + int L; // taps per link in file + int S; // snapshots + double fs_hz; + double snapshot_dt_s; + float ds_ns; + float speed_mps; + int pair_order; + uint64_t offset_bytes; + uint64_t nbytes; +} cirdb_entry_meta_t; + +int cirdb_yaml_select(const cirdb_select_req_t *req, cirdb_entry_meta_t *out); +int cirdb_yaml_scan(const char *yaml_path); + +#ifdef __cplusplus +} +#endif diff --git a/radio/vrtsim/vrtsim.c b/radio/vrtsim/vrtsim.c index f2f9cfef1d..89e3b431aa 100644 --- a/radio/vrtsim/vrtsim.c +++ b/radio/vrtsim/vrtsim.c @@ -51,6 +51,8 @@ #include "noise_device.h" #include "simde/x86/avx512.h" #include "taps_client.h" +#include "cirdb_provider.h" +#include "cirdb_yaml.h" // Simulator role typedef enum { ROLE_SERVER = 1, ROLE_CLIENT } role; @@ -79,6 +81,15 @@ typedef enum { ROLE_SERVER = 1, ROLE_CLIENT } role; {"chanmod", "Enable channel modelling", 0, .iptr = &vrtsim_state->chanmod, .defintval = 0, TYPE_INT, 0}, \ {"taps-socket", TAPS_SOCKET_HLP, 0, .strptr = &vrtsim_state->taps_socket, .defstrval = NULL, TYPE_STRING, 0}, \ {"client-num-rx-antennas", CLIENT_NUM_RX_HLP, 0, .iptr = &vrtsim_state->client_num_rx_antennas, .defintval = 1, TYPE_INT, 0}, \ + /* CIR DB enable and paths */ \ + {"cirdb", "Use CIR database for channel taps (1 yes, 0 no)", 0, .iptr = &vrtsim_state->use_cirdb, .defintval = 0, TYPE_INT, 0}, \ + {"cirdb-path", "Directory that holds vrtsim.yaml and cir_db.bin", 0, .strptr = &vrtsim_state->cirdb_path, .defstrval = NULL, TYPE_STRING, 0}, \ + {"cirdb_yaml", "Absolute path to CIR DB YAML file (optional, overrides cirdb-path)", 0, .strptr = &vrtsim_state->cirdb_yaml, .defstrval = NULL, TYPE_STRING, 0}, \ + {"cirdb_file", "Absolute path to CIR DB binary file (optional, overrides cirdb-path)", 0, .strptr = &vrtsim_state->cirdb_file, .defstrval = NULL, TYPE_STRING, 0}, \ + /* CIR DB selection knobs */ \ + {"cirdb_model_id", "Preferred TDL model id 0..4", 0, .iptr = &vrtsim_state->cirdb_model_id, .defintval = 0, TYPE_INT, 0}, \ + {"cirdb_ds_ns", "Desired RMS delay spread in ns", 0, .dblptr = &vrtsim_state->cirdb_ds_ns, .defdblval = 10.0, TYPE_DOUBLE, 0}, \ + {"cirdb_speed_mps", "Desired speed in m/s", 0, .dblptr = &vrtsim_state->cirdb_speed_mps, .defdblval = 1.5, TYPE_DOUBLE, 0}, \ }; // clang-format on @@ -126,6 +137,14 @@ typedef struct { char *taps_socket; int client_num_rx_antennas; struct timespec start_ts; + /* CIR DB state */ + int use_cirdb; + char *cirdb_path; + char *cirdb_yaml; + char *cirdb_file; + int cirdb_model_id; + double cirdb_ds_ns; + double cirdb_speed_mps; } vrtsim_state_t; static void histogram_add(histogram_t *histogram, double diff) @@ -166,7 +185,7 @@ static void load_channel_model(vrtsim_state_t *vrtsim_state) char *model_name = vrtsim_state->role == ROLE_CLIENT ? "client_tx_channel_model" : "server_tx_channel_model"; vrtsim_state->channel_desc = find_channel_desc_fromname(model_name); AssertFatal(vrtsim_state->channel_desc != NULL, - "Could not find model name %s. Make sure it is present in the config file", + "Could not find model name %s. Make sure it is present in the config file\n", model_name); LOG_I(HW, "Channel model %s parameters: path_loss_dB=%.2f, nb_tx=%d, nb_rx=%d, channel_length=%d\n", @@ -201,6 +220,9 @@ static void vrtsim_readconfig(vrtsim_state_t *vrtsim_state) AssertFatal(false, "Invalid configuration: Build with OAI_VRTSIM_TAPS_CLIENT to use taps socket\n"); } #endif + if (vrtsim_state->use_cirdb) { + LOG_A(HW, "VRTSIM: CIR DB is enabled at runtime\n"); + } } static void *vrtsim_timing_job(void *arg) @@ -305,7 +327,7 @@ static int vrtsim_connect(openair0_device_t *device) // Handle channel modelling after number of RX antennas are known int num_tx_stats = 1; - if (vrtsim_state->chanmod || vrtsim_state->taps_socket) { + if (vrtsim_state->chanmod || vrtsim_state->taps_socket || vrtsim_state->use_cirdb) { vrtsim_state->channel_modelling_actors = calloc_or_fail(vrtsim_state->peer_info.num_rx_antennas, sizeof(Actor_t)); for (int i = 0; i < vrtsim_state->peer_info.num_rx_antennas; i++) { init_actor(&vrtsim_state->channel_modelling_actors[i], "chanmod", -1); @@ -316,6 +338,53 @@ static int vrtsim_connect(openair0_device_t *device) device->openair0_cfg[0].tx_num_channels, vrtsim_state->peer_info.num_rx_antennas, &vrtsim_state->channel_desc); + } else if (vrtsim_state->use_cirdb) { + const char *yaml_path = NULL; + const char *bin_path = NULL; + + if (vrtsim_state->cirdb_yaml && vrtsim_state->cirdb_yaml[0]) + yaml_path = vrtsim_state->cirdb_yaml; + if (vrtsim_state->cirdb_file && vrtsim_state->cirdb_file[0]) + bin_path = vrtsim_state->cirdb_file; + + char yaml_buf[PATH_MAX]; + char bin_buf[PATH_MAX]; + + if (!yaml_path || !bin_path) { + const char *base = vrtsim_state->cirdb_path; + if (base && base[0]) { + if (!yaml_path) { + snprintf(yaml_buf, sizeof(yaml_buf), "%s/%s", base, "vrtsim.yaml"); + yaml_path = yaml_buf; + } + if (!bin_path) { + snprintf(bin_buf, sizeof(bin_buf), "%s/%s", base, "cir_db.bin"); + bin_path = bin_buf; + } + } + } + + cirdb_select_opts_t sel = (cirdb_select_opts_t){0}; + sel.yaml_path = yaml_path; + sel.bin_path = bin_path; + AssertFatal(vrtsim_state->cirdb_model_id >= 0 && vrtsim_state->cirdb_model_id <= 4, + "Invalid cirdb_model_id=%d (valid: 0..4)\n", + vrtsim_state->cirdb_model_id); + sel.want_model_id = vrtsim_state->cirdb_model_id; + sel.want_ds_ns = (float)(vrtsim_state->cirdb_ds_ns > 0.0 ? vrtsim_state->cirdb_ds_ns : -1.0); + sel.want_speed_mps = (float)(vrtsim_state->cirdb_speed_mps > 0.0 ? vrtsim_state->cirdb_speed_mps : -1.0); + + LOG_A(HW, + "VRTSIM: CIR DB select yaml='%s' bin='%s'\n", + sel.yaml_path ? sel.yaml_path : "(auto)", + sel.bin_path ? sel.bin_path : "(auto)"); + + cirdb_connect(0, + device->openair0_cfg[0].tx_num_channels, + vrtsim_state->peer_info.num_rx_antennas, + &sel, + &vrtsim_state->channel_desc); + LOG_A(HW, "VRTSIM: channel taps via CIR DB\n"); } else { load_channel_model(vrtsim_state); } @@ -392,9 +461,15 @@ static void perform_channel_modelling(void *arg) return; } + if (vrtsim_state->use_cirdb) { + double seconds = (double)channel_modelling_args->timestamp / vrtsim_state->sample_rate; + uint64_t elapsed_ns = (uint64_t)(seconds * 1e9 + 0.5); + cirdb_update(elapsed_ns); + } + cf_t channel_impulse_response[nb_tx_ant][channel_desc->channel_length]; cf_t *channel_impulse_response_p[nb_tx_ant]; - if (!vrtsim_state->taps_socket) { + if (!vrtsim_state->taps_socket && !vrtsim_state->use_cirdb) { const float pathloss_linear = powf(10, channel_desc->path_loss_dB / 20.0); // Convert channel impulse response to float + apply pathloss for (int aatx = 0; aatx < nb_tx_ant; aatx++) { @@ -543,7 +618,7 @@ static int vrtsim_write(openair0_device_t *device, AssertFatal(timestamp >= 0, "Timestamp must be non-negative, got %ld\n", timestamp); timestamp -= device->openair0_cfg->command_line_sample_advance; vrtsim_state_t *vrtsim_state = (vrtsim_state_t *)device->priv; - bool channel_modelling = vrtsim_state->chanmod || vrtsim_state->taps_socket; + bool channel_modelling = vrtsim_state->chanmod || vrtsim_state->taps_socket || vrtsim_state->use_cirdb; return channel_modelling ? vrtsim_write_with_chanmod(vrtsim_state, timestamp, samplesVoid, nsamps, nbAnt, flags) : vrtsim_write_internal(vrtsim_state, timestamp, (c16_t *)samplesVoid[0], nsamps, 0, flags, 0); } @@ -609,7 +684,7 @@ static void vrtsim_end(openair0_device_t *device) } tx_timing_t *tx_timing = vrtsim_state->tx_timing; - if (vrtsim_state->chanmod || vrtsim_state->taps_socket) { + if (vrtsim_state->chanmod || vrtsim_state->taps_socket || vrtsim_state->use_cirdb) { for (int i = 0; i < vrtsim_state->peer_info.num_rx_antennas; i++) { shutdown_actor(&vrtsim_state->channel_modelling_actors[i]); } @@ -623,7 +698,9 @@ static void vrtsim_end(openair0_device_t *device) } tx_timing->average_tx_budget /= vrtsim_state->peer_info.num_rx_antennas; free_noise_device(); - if (vrtsim_state->taps_socket) { + if (vrtsim_state->use_cirdb) { + cirdb_stop(); + } else if (vrtsim_state->taps_socket) { taps_client_stop(); } } @@ -711,8 +788,9 @@ __attribute__((__visibility__("default"))) int device_init(openair0_device_t *de vrtsim_state->tx_num_channels = openair0_cfg->tx_num_channels; vrtsim_state->rx_num_channels = openair0_cfg->rx_num_channels; - if (vrtsim_state->chanmod || vrtsim_state->taps_socket) { + if (vrtsim_state->chanmod || vrtsim_state->taps_socket || vrtsim_state->use_cirdb) { init_channelmod(); + randominit(); int noise_power_dBFS = get_noise_power_dBFS(); int16_t noise_power = noise_power_dBFS == INVALID_DBFS_VALUE ? 0 : (int16_t)(32767.0 / powf(10.0, .05 * -noise_power_dBFS)); LOG_A(HW, "VRTSIM: Noise power %d sample value\n", noise_power); diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.conf index 7f750d7259..ec036d30ca 100644 --- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.conf +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.conf @@ -248,3 +248,15 @@ e2_agent = { #sm_dir = "/path/where/the/SMs/are/located/" sm_dir = "/usr/local/lib/flexric/" }; + +vrtsim : +{ + #role = "server"; # "server" or "client" + #client-num-rx-antennas = 1; # publish expected client RX count + #cirdb = 1; # Enable CIR DB taps + # cirdb_yaml = "/path/to/vrtsim.yaml"; + # cirdb_file = "/path/to/cir_db.bin"; + cirdb_model_id = 0; # 0..4 for 3GPP TDL-A..E + cirdb_ds_ns = 10.0; # RMS delay spread in ns (10, 30) + cirdb_speed_mps = 1.5; # UE speed in m/s (1.5, 30) +}; From a707094196ae6b3e52a32440b77af2ff38fd8c5f Mon Sep 17 00:00:00 2001 From: Merkebu Girmay Date: Fri, 16 Jan 2026 22:56:14 -0800 Subject: [PATCH 2/3] vrtsim: implement batched TX writes for channel modelling - Move buffer allocation per-batch - Convert and write per batch - Fix saved_samples gap handling bug --- radio/vrtsim/vrtsim.c | 48 +++++++++++++++++++++++-------------------- 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/radio/vrtsim/vrtsim.c b/radio/vrtsim/vrtsim.c index 89e3b431aa..9a0cd779dc 100644 --- a/radio/vrtsim/vrtsim.c +++ b/radio/vrtsim/vrtsim.c @@ -450,11 +450,6 @@ static void perform_channel_modelling(void *arg) int nb_tx_ant = channel_modelling_args->nbAnt; c16_t **input_samples = (c16_t **)channel_modelling_args->samples; - int aligned_nsamps = ceil_mod(nsamps, (512 / 8) / sizeof(cf_t)); - cf_t samples[aligned_nsamps] __attribute__((aligned(64))); - // Apply noise from global settings - get_noise_vector((float *)samples, nsamps * 2); - channel_desc_t *channel_desc = vrtsim_state->channel_desc; if (channel_desc == NULL) { @@ -488,16 +483,25 @@ static void perform_channel_modelling(void *arg) } for (int batch_index = 0; batch_index < channel_modelling_args->num_batches; batch_index++) { - int start_sample = batch_index * channel_modelling_args->batch_size; - int num_samples = min(channel_modelling_args->batch_size, nsamps - start_sample); + const int start_sample = batch_index * channel_modelling_args->batch_size; + const int num_samples = min(channel_modelling_args->batch_size, nsamps - start_sample); if (start_sample >= nsamps) { break; } + + const int aligned_batch = ceil_mod(num_samples, (512 / 8) / sizeof(cf_t)); + cf_t samples[aligned_batch] __attribute__((aligned(64))); + memset(samples, 0, sizeof(samples)); + + // Apply noise from global settings (only valid samples) + get_noise_vector((float *)samples, num_samples * 2); + for (int aatx = 0; aatx < nb_tx_ant; aatx++) { - for (int i = start_sample; i < start_sample + num_samples; i++) { - cf_t *impulse_response = channel_impulse_response_p[aatx]; + cf_t *impulse_response = channel_impulse_response_p[aatx]; + for (int i = 0; i < num_samples; i++) { + const int gi = start_sample + i; for (int l = 0; l < channel_desc->channel_length; l++) { - int idx = i - l; + const int idx = gi - l; // TODO: Use AVX2 for this c16_t tx_input = idx >= 0 ? input_samples[aatx][idx] : channel_modelling_args->saved_samples[aatx][SAVED_SAMPLES_LEN + idx]; @@ -507,31 +511,32 @@ static void perform_channel_modelling(void *arg) } } - // Convert to c16_t - c16_t samples_out[aligned_nsamps] __attribute__((aligned(64))); - #if defined(__AVX512F__) - for (int i = 0; i < aligned_nsamps / 8; i++) { + // Convert to c16_t (only once per batch) + c16_t samples_out[aligned_batch] __attribute__((aligned(64))); +#if defined(__AVX512F__) + for (int i = 0; i < aligned_batch / 8; i++) { simde__m512 *in = (simde__m512 *)&samples[i * 8]; simde__m256i *out = (simde__m256i *)&samples_out[i * 8]; *out = simde_mm512_cvtsepi32_epi16(simde_mm512_cvtps_epi32(*in)); } - #elif defined(__AVX2__) - for (int i = 0; i < aligned_nsamps / 4; i++) { +#elif defined(__AVX2__) + for (int i = 0; i < aligned_batch / 4; i++) { simde__m256 *in = (simde__m256 *)&samples[i * 4]; simde__m128i *out = (simde__m128i *)&samples_out[i * 4]; *out = simde_mm256_cvtsepi32_epi16(simde_mm256_cvtps_epi32(*in)); } - #else - for (int i = 0; i < nsamps; i++) { +#else + for (int i = 0; i < num_samples; i++) { samples_out[i].r = lroundf(samples[i].r); samples_out[i].i = lroundf(samples[i].i); } - #endif +#endif + // Write the batch as a single contiguous transfer vrtsim_write_internal(channel_modelling_args->vrtsim_state, - channel_modelling_args->timestamp, + channel_modelling_args->timestamp + start_sample, samples_out, - channel_modelling_args->nsamps, + num_samples, aarx, channel_modelling_args->flags, aarx); @@ -582,7 +587,6 @@ static int vrtsim_write_with_chanmod(vrtsim_state_t *vrtsim_state, for (int aatx = 0; aatx < nbAnt; aatx++) memcpy(&args->saved_samples[aatx][0], saved_samples[aatx], sizeof(c16_t) * SAVED_SAMPLES_LEN); } - memcpy(args->saved_samples, saved_samples, sizeof(saved_samples)); pushNotifiedFIFO(&vrtsim_state->channel_modelling_actors[aarx].fifo, task); } From 33cc04b3e18dcb33280496867f88e9f4343e7630 Mon Sep 17 00:00:00 2001 From: Merkebu Girmay Date: Fri, 23 Jan 2026 18:13:51 -0800 Subject: [PATCH 3/3] Add CIRDB playback test pipeline Increase retx threshold in vrtsim with CIRDB. --- .../xml_files/container_5g_vrtsim_cirdb.xml | 130 +++++++++++++ .../5g_vrtsim_cirdb/docker-compose.yaml | 183 ++++++++++++++++++ 2 files changed, 313 insertions(+) create mode 100644 ci-scripts/xml_files/container_5g_vrtsim_cirdb.xml create mode 100644 ci-scripts/yaml_files/5g_vrtsim_cirdb/docker-compose.yaml diff --git a/ci-scripts/xml_files/container_5g_vrtsim_cirdb.xml b/ci-scripts/xml_files/container_5g_vrtsim_cirdb.xml new file mode 100644 index 0000000000..84a6a527f7 --- /dev/null +++ b/ci-scripts/xml_files/container_5g_vrtsim_cirdb.xml @@ -0,0 +1,130 @@ + + + vrtsim-5gnr-cirdb + Monolithic gNB vrtsim + CIRDB + wrench + + + Pull_Local_Registry + Pull Images from Local Registry + localhost + oai-gnb oai-nr-ue + arm_ + + + + Create_Workspace + Create new Workspace + localhost + + + + Deploy_Object + Deploy OAI 5G CoreNetwork + localhost + ci-scripts/yaml_files/5g_vrtsim_cirdb + mysql oai-amf oai-smf oai-upf oai-ext-dn + + + + Deploy_Object + Deploy OAI 5G gNB+UE RF sim SA + localhost + ci-scripts/yaml_files/5g_vrtsim_cirdb + oai-gnb oai-nr-ue + + + + Attach_UE + Attach OAI UE (Wait for IP) + rfsim5g_ue + localhost + + + + Ping + Ping ext-dn from NR-UE + rfsim5g_ue + localhost + rfsim5g_ext_dn + localhost + -c 20 -i 0.25 + 5 + + + + Ping + Ping NR-UE from ext-dn + rfsim5g_ext_dn + localhost + rfsim5g_ue + localhost + -c 20 -i 0.25 + 5 + + + + Iperf + Iperf (DL/3Mbps/UDP)(20 sec) + -u -b 3M -t 20 -R + rfsim5g_ue + localhost + rfsim5g_ext_dn + localhost + 5 + 90 + + + + Iperf + Iperf (UL/1Mbps/UDP)(20 sec) + -u -b 1M -t 20 + rfsim5g_ue + localhost + rfsim5g_ext_dn + localhost + 5 + 90 + + + + Undeploy_Object + true + Undeploy all OAI 5G stack + localhost + ci-scripts/yaml_files/5g_vrtsim_cirdb + 10,0,0,0 + 1,0,0,0 + + + + Clean_Test_Server_Images + true + Clean Test Images on Test Server + localhost + oai-gnb oai-nr-ue + + + + diff --git a/ci-scripts/yaml_files/5g_vrtsim_cirdb/docker-compose.yaml b/ci-scripts/yaml_files/5g_vrtsim_cirdb/docker-compose.yaml new file mode 100644 index 0000000000..0d7b509450 --- /dev/null +++ b/ci-scripts/yaml_files/5g_vrtsim_cirdb/docker-compose.yaml @@ -0,0 +1,183 @@ +services: + # CIR Generator service - generates CIRDB files on every CI run + cir-generator: + image: oaisoftwarealliance/cir-generator:test + environment: + GENERATOR_ARGS: "--demo" + OUTPUT: "--out /opt/output/cir_db.bin" + volumes: + - cirdb_data:/opt/output + + mysql: + container_name: "rfsim5g-mysql" + image: mysql:8.0 + init: true + 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 + start_period: 10s + start_interval: 500ms + retries: 30 + networks: + public_net: + ipv4_address: 192.168.71.131 + + oai-amf: + container_name: "rfsim5g-oai-amf" + image: oaisoftwarealliance/oai-amf:v2.1.10 + 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.1.10 + 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.1.10 + init: true + 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 + interface_name: eth0 + traffic_net: + ipv4_address: 192.168.72.134 + interface_name: eth1 + + oai-ext-dn: + privileged: true + container_name: rfsim5g-oai-ext-dn + image: oaisoftwarealliance/trf-gen-cn5g:latest + init: true + 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 + start_period: 10s + start_interval: 500ms + + oai-gnb: + image: ${REGISTRY-oaisoftwarealliance/}${GNB_IMG:-oai-gnb}:${TAG:-develop} + container_name: rfsim5g-oai-gnb + cap_drop: + - ALL + environment: + USE_ADDITIONAL_OPTIONS: -E --log_config.global_log_options level,nocolor,time --device.name vrtsim --vrtsim.role server --vrtsim.cirdb 1 --vrtsim.cirdb_file /cirdb/cir_db.bin --vrtsim.cirdb_yaml /cirdb/cir_db.yaml --vrtsim.cirdb_model_id 0 --vrtsim.cirdb_ds_ns 1 --vrtsim.cirdb_speed_mps 1.5 + depends_on: + cir-generator: + condition: service_completed_successfully + oai-ext-dn: + condition: service_started + networks: + public_net: + ipv4_address: 192.168.71.140 + volumes: + - ../../conf_files/gnb.sa.band78.106prb.vrtsim.2x2.yaml:/opt/oai-gnb/etc/gnb.yaml + - cirdb_data:/cirdb:ro + - tmp_data:/tmp/ + healthcheck: + test: /bin/bash -c "pgrep nr-softmodem" + interval: 10s + timeout: 5s + start_period: 10s + start_interval: 500ms + retries: 5 + ipc: host + + 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 + - NET_RAW + environment: + USE_ADDITIONAL_OPTIONS: -E -r 106 --numerology 1 --band 78 -C 3619200000 --ssb 516 --uicc0.imsi 208990100001100 --log_config.global_log_options level,nocolor,time --device.name vrtsim --vrtsim.role client + depends_on: + - oai-gnb + networks: + public_net: + ipv4_address: 192.168.71.150 + devices: + - /dev/net/tun:/dev/net/tun + volumes: + - ../../conf_files/nrue.vrtsim.chanmod.yaml:/opt/oai-nr-ue/etc/nr-ue.yaml + - tmp_data:/tmp/ + healthcheck: + test: /bin/bash -c "pgrep nr-uesoftmodem" + interval: 10s + timeout: 5s + retries: 5 + start_period: 10s + start_interval: 500ms + ipc: host + +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" + +volumes: + tmp_data: + cirdb_data: