Compare commits

...

10 Commits

Author SHA1 Message Date
Merkebu Girmay
2dbdad0e52 Update tutorial 2025-12-15 18:50:17 +00:00
Merkebu Girmay
961b2639ba Edit channel_modelling_external_channel.md 2025-11-19 01:48:59 +00:00
Merkebu Girmay
1498582214 Edit channel_modelling_external_channel.md 2025-11-18 21:18:34 +00:00
Merkebu Girmay
708109a2c4 Edit channel_modelling_external_channel.md 2025-11-18 21:13:30 +00:00
Merkebu Girmay
dd22cfb31a Readme update for workshop 2025-11-18 13:07:57 -08:00
Merkebu Girmay
15f1b77077 Edit channel_modelling_external_channel.md 2025-11-16 07:16:09 +00:00
Merkebu Girmay
54c5590eee Docs: add external channel modeling tutorial for VRTSIM workshop 2025-11-10 00:51:57 -08:00
Bartosz Podrygajlo
cf9b1b12dc Tutorial for vrtsim 2025-11-07 17:22:47 +01:00
Bartosz Podrygajlo
fea52dc3d0 vrtsim: Optimize channel modelling
Optimization pass for channel modelling on transmission in vrtsim. This
comes from a requirement of O-RU to perform channel modelling with minimum
latency.

- Reduced MAX_CHANNEL_LENGTH to 200 and introduced SAVED_SAMPLES_LEN
- Refactored channel modelling actor allocation to parallelize work better
- Improved batching and task creation for channel modelling
- Replaced static global saved_samples with local static buffer in vrtsim_write_with_chanmod
2025-11-07 14:49:51 +01:00
Bartosz Podrygajlo
48c10bee5c Fix tracy git tag 2025-11-07 14:49:40 +01:00
8 changed files with 600 additions and 88 deletions

View File

@@ -282,7 +282,7 @@ add_boolean_option(TRACY_ENABLE OFF "Enable tracy instrumentation" ON)
if (TRACY_ENABLE)
# the tracy version here should match the tracy server version
# below is latest release as of this commit
CPMAddPackage("gh:wolfpld/tracy#0.12.2")
CPMAddPackage("gh:wolfpld/tracy#v0.12.2")
endif()
set (OCP_ITTI ${OPENAIR_DIR}/common/utils/ocp_itti)

View File

@@ -0,0 +1,100 @@
# About
In this step usage of channel modelling with `vrtsim` will be explained
# Configure
Modify the gNB config file at `targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.conf`. Add the following at the end:
```
channelmod = {
max_chan=10;
modellist="modellist_rfsimu_1";
modellist_rfsimu_1 = (
{
model_name = "server_tx_channel_model"
type = "AWGN";
ploss_dB = 0;
noise_power_dB = 0;
forgetfact = 0;
offset = 0;
ds_tdl = 0;
}
);
};
```
# (Optional) Run UE with scope to visualize the impact
```
cmake ../../ -DENABLE_IMSCOPE=ON
```
```
cmake --build . --target imscope
```
Add `--imscope` to UE command line
# Run
```
sudo ./nr-softmodem -O ../../targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.conf --gNBs.[0].min_rxtxtime 6 --device.name vrtsim --vrtsim.role server --vrtsim.chanmod 1
```
```
sudo ./nr-uesoftmodem -C 3619200000 -r 106 --numerology 1 --ssb 516 --band 78 --device.name vrtsim
```
# Modifying the channel model
Try the following settings:
```
modellist_rfsimu_1 = (
{
model_name = "server_tx_channel_model"
type = "Rayleigh8";
ploss_dB = 0;
noise_power_dB = 0;
forgetfact = 0;
offset = 0;
ds_tdl = 0;
}
);
```
```
modellist_rfsimu_1 = (
{
model_name = "server_tx_channel_model"
type = "AWGN";
ploss_dB = -5;
noise_power_dB = 0;
forgetfact = 0;
offset = 0;
ds_tdl = 0;
}
);
```
```
modellist_rfsimu_1 = (
{
model_name = "server_tx_channel_model"
type = "AWGN";
ploss_dB = -10;
noise_power_dB = 0;
forgetfact = 0;
offset = 0;
ds_tdl = 0;
}
);
```
Add noise:
```
channelmod = {
max_chan=10;
modellist="modellist_rfsimu_1";
noise_power_dBFS = -42;
```

View File

@@ -0,0 +1,92 @@
## Option 1: Work via Emitter
### About
Precomputed 3GPP 38.901 TDL profiles (A to E) with statistical MIMO channels, plus a Python emitter that publishes taps to VRTSIM over a nanomsg PUB socket using FlatBuffers. Supports multiple antenna shapes (up to 64x64), speed presets (walking about 1.5 m/s, car about 30 m/s), spatial correlation, mutual coupling, LOS steering for D and E, and time-varying lognormal shadowing.
**Key Features:**
* Precomputed 3GPP 38.901 TDL taps (A to E) stored in a CIR database, published by a Python emitter over a PUB socket using FlatBuffers.
* Avoids on-the-fly channel synthesis during runs, which reduces CPU load and improves timing stability.
* Database contains profiles, delay spreads, antenna shapes, and speed presets, so scenarios are reproducible and easy to share.
* VRTSim consumes taps from the socket and applies them per slot, which decouples tap generation from application.
* Channel model is editable in the external repo, so new profiles or motion models can be added without touching VRTSim.
### Clone Channel Emulator
```bash
git clone https://gitlab.eurecom.fr/oai/raytracing-channel-emulator.git
```
### Setup Schema
```bash
python3 -m pip install --user flatbuffers nanomsg numpy
cd ~/raytracing-channel-emulator/server/api
flatc --python -o . taps.fbs # generates ./Phy/Taps.py
```
### Generate CIR DB
```bash
cd ~/raytracing-channel-emulator/server/external_taps
python3 CIR_generator.py --out ./cir_db.bin --demo
```
### Run CIR Emitter
```bash
python3 emit_from_db.py --bind tcp://127.0.0.1:5555 --model TDL-A --ds-ns 10 --nrx 1 --ntx 1 --interval 0.5
```
### Clone OAI
```bash
git clone https://gitlab.eurecom.fr/oai/openairinterface5g
git fetch origin vrtsim_cirdb_read:vrtsim_cirdb_read
git checkout vrtsim_cirdb_read
```
### Build
```bash
cd openairinterface5g/cmake_targets
cd build
cmake ../.. -GNinja -DCMAKE_BUILD_TYPE=RelWithDebInfo -DOAI_VRTSIM_TAPS_CLIENT=ON -DCMAKE_EXPORT_COMPILE_COMMANDS=ON
cmake --build . --target taps_client vrtsim rfsimulator nr-softmodem nr-uesoftmodem ldpc params_libconfig -j"$(nproc)"
```
### Run gNB
```bash
sudo ./nr-softmodem -O ../../targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.conf --device.name vrtsim --vrtsim.role server --vrtsim.taps-socket tcp://127.0.0.1:5555 --gNBs.[0].min_rxtxtime 6
```
### Run UE
```bash
sudo ./nr-uesoftmodem -C 3619200000 -r 106 --numerology 1 --ssb 516 --band 78 --device.name vrtsim --vrtsim.role client
```
---
## Option 2: Read DB Directly (In-process CIRDB)
### About
**Key Features:**
* Adds an in-process CIR database path in VRTSim that applies taps internally, so no external emitter is required and no socket exchange occurs.
* The dataset is produced offline using the CIR DB generator in the channel-emulator repo, then consumed by VRTSim for deterministic replay.
* Avoids on-the-fly channel synthesis during runs, which reduces CPU load and improves timing stability.
* Reduces transport jitter and context switches compared to a socket publisher, which helps at higher PRB, MIMO, and UE counts.
### Run gNB
```bash
sudo ./nr-softmodem \
-O ./../../targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.conf \
--device.name vrtsim \
--vrtsim.role server \
--gNBs.[0].min_rxtxtime 3 \
--vrtsim.cirdb 1 \
--vrtsim.cirdb_yaml /openairinterface5g/radio/vrtsim/cir_db.yaml \
--vrtsim.cirdb_file /openairinterface5g/radio/vrtsim/cir_db.bin \
--vrtsim.cirdb_model_id 0 \
--vrtsim.cirdb_ds_ns 10 \
--vrtsim.cirdb_speed_mps 1.5
```
### Run UE
```bash
sudo ./nr-uesoftmodem -C 3619200000 -r 106 --numerology 1 --ssb 516 --band 78 --device.name vrtsim --vrtsim.role client
```

View File

@@ -0,0 +1,145 @@
# About
In this step the basics of compiling and running `vrtsim` will be explained.
# About vrtsim
vrtsim is a realtime/fixed timescale split8 device emulator. The main difference between
rfsim and vrtsim is the timing constraints vrtsim forces on the applications that use it.
# Compiling
Start by making a new directory under `cmake_targets`
```bash
mkdir -p cmake_targets/build/
```
Compile vrtsim + RAN elements
```bash
cd /cmake_targets/build
cmake ../../ -GNinja
cmake --build . --target vrtsim nr-softmodem nr-uesoftmodem ldpc params_libconfig params_yaml
```
# Running
Run a basic testcase to verify connectivity, no core network present
```bash
sudo ./nr-softmodem -O ../../targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.conf --gNBs.[0].min_rxtxtime 6 --device.name vrtsim --vrtsim.role server
```
```bash
sudo ./nr-uesoftmodem -C 3619200000 -r 106 --numerology 1 --ssb 516 --band 78 --device.name vrtsim
```
# Verification
Goal is to have the UE connect for now (i.e. enter RRC Connected mode). The success depends on your
CPU speed.
## Example NR UE output when UE is connected
```
[PHY] Initial sync: pbch decoded sucessfully, ssb index 0
[PHY] pbch rx ok. rsrp:51 dB/RE, adjust_rxgain:-1 dB
[NR_PHY] Cell Detected with GSCN: 0, SSB SC offset: 516, SSB Ref: 0.000000, PSS Corr peak: 99 dB, PSS Corr Average: 61
[PHY] [UE0] In synch, rx_offset 444704 samples
[PHY] [UE 0] Measured Carrier Frequency offset 6 Hz
[PHY] Initial sync successful, PCI: 0
[PHY] HW: Configuring channel 0 (rf_chain 0): setting tx_freq 3619200006 Hz, rx_freq 3619200006 Hz, tune_offset 0
[PHY] Got synch: hw_slot_offset 29, carrier off 6 Hz, rxgain 110.000000 (DL 3619200006.000000 Hz, UL 3619200006.000000 Hz)
[PHY] UE synchronized! decoded_frame_rx=404 UE->init_sync_frame=0 trashed_frames=14
[PHY] Resynchronizing RX by 444704 samples
[HW] received write reorder clear context
[NR_RRC] SIB1 decoded
[NR_MAC] TDD period index = 6, based on the sum of dl_UL_TransmissionPeriodicity from Pattern1 (5.000000 ms) and Pattern2 (0.000000 ms): Total = 5.000000 ms
[NR_MAC] Set TDD configuration period to: 8 DL slots, 3 UL slots, 10 slots per period (NR_TDD_UL_DL_Pattern is 7 DL slots, 2 UL slots, 6 DL symbols, 4 UL symbols)
[NR_MAC] Configured 1 TDD patterns (total slots: pattern1 = 10, pattern2 = 0)
[PHY] N_TA_offset changed from 0 to 800
[MAC] Initialization of 4-Step CBRA procedure
[NR_MAC] PRACH scheduler: Selected RO Frame 421, Slot 19, Symbol 0, Fdm 0
[PHY] PRACH [UE 0] in frame.slot 421.19, placing PRACH in position 2828, Msg1/MsgA-Preamble frequency start 0 (k1 0), preamble_offset 6, first_nonzero_root_idx 0, preambleIndex = 27
[PHY] [UE 0] RAR-Msg2 decoded
[NR_MAC] [UE 0][RAPROC][RA-RNTI 010b] Got BI RAR subPDU 5 ms
[NR_MAC] [UE 0][RAPROC][RA-RNTI 010b] Got RAPID RAR subPDU
[NR_MAC] [UE 0][RAPROC][422.10] Found RAR with the intended RAPID 27
[MAC] received TA command 31
[NR_MAC] [RAPROC][422.19] RA-Msg3 transmitted
[MAC] [UE 0][423.10][RAPROC] 4-Step RA procedure succeeded. CBRA: Contention Resolution is successful.
[NR_RRC] [UE0][RAPROC] Logical Channel DL-CCCH (SRB0), Received NR_RRCSetup
[RLC] Added srb 1 to UE 0
[NR_RRC] State = NR_RRC_CONNECTED
```
## Example gNB output when connected
```
[NR_PHY] [RAPROC] 421.19 Initiating RA procedure with preamble 27, energy 31.7 dB (I0 0, thres 120), delay 0 start symbol 0 freq index 0
[NR_MAC] 421.19 UE RA-RNTI 010b TC-RNTI 2c32: initiating RA procedure
[NR_MAC] UE 2c32: Msg3 scheduled at 422.19 (422.10 TDA 0) start 0 RBs 8
[NR_MAC] UE 2c32: 422.10 Generating RA-Msg2 DCI, RA RNTI 0x10b, state 1, preamble_index(RAPID) 27, timing_offset = 0 (estimated distance 0.0 [m])
[NR_MAC] 422.10 Send RAR to RA-RNTI 010b
[NR_MAC] 422.19 PUSCH with TC_RNTI 0x2c32 received correctly
[MAC] [RAPROC] Received SDU for CCCH length 6 for UE 2c32
[RLC] Activated srb0 for UE 11314
[RLC] Added srb 1 to UE 11314
[NR_MAC] Activating scheduling Msg4 for TC_RNTI 0x2c32 (state WAIT_Msg3)
[NR_RRC] Decoding CCCH: RNTI 2c32, payload_size 6
[NR_RRC] [--] (cellID 0, UE ID 1 RNTI 2c32) Create UE context: CU UE ID 1 DU UE ID 11314 (rnti: 2c32, random ue id 1c14de2d3f000000)
[RRC] activate SRB 1 of UE 1
[NR_RRC] [DL] (cellID bc614e, UE ID 1 RNTI 2c32) Send RRC Setup
[NR_MAC] UE 2c32 Generate Msg4: feedback at 423.17, payload 225 bytes, next state nrRA_WAIT_Msg4_MsgB_ACK
[NR_MAC] 423.17 UE 2c32: Received Ack of Msg4. CBRA procedure succeeded (UE Connected)
[NR_MAC] Adding new UE context with RNTI 0x2c32
[NR_RRC] [UL] (cellID bc614e, UE ID 1 RNTI 2c32) Received RRCSetupComplete (RRC_CONNECTED reached)
```
## Troubleshooting:
In case the UE does not connect:
Interrupt the process using `CTRL + C` and analyze `vrtsim` stdout at the end. You should see the histogram
print from `vrtsim`
Example from a working configuration:
```
[HW] VRTSIM: Realtime issues: TX 0.00%, RX 0.00%
[HW] VRTSIM: Read/write too early (suspected radio implementaton error) TX: 0, RX: 0
[HW] VRTSIM: Average TX budget 913.111 uS (more is better)
[HW] VRTSIM: TX budget histogram: 6468 samples
[HW] Bin 0 [0.0 - 100.0uS]: 0
[HW] Bin 1 [100.0 - 200.0uS]: 0
[HW] Bin 2 [200.0 - 300.0uS]: 0
[HW] Bin 3 [300.0 - 400.0uS]: 0
[HW] Bin 4 [400.0 - 500.0uS]: 1
[HW] Bin 5 [500.0 - 600.0uS]: 0
[HW] Bin 6 [600.0 - 700.0uS]: 0
[HW] Bin 7 [700.0 - 800.0uS]: 1
[HW] Bin 8 [800.0 - 900.0uS]: 125
[HW] Bin 9 [900.0 - 1000.0uS]: 6242
[HW] Bin 10 [1000.0 - 1100.0uS]: 0
[HW] Bin 11 [1100.0 - 1200.0uS]: 0
```
The histogram above indicates if the application encountered any timing related issues. If the average TX budget
is low (near ~0) or bin 0 is being filled that could mean that your CPU is too slow.
Possible fixes:
- Try disabling idle states
```bash
sudo cpupower idle-set -D 0
```
- Change cpu frequency scaling governor to performance
```bash
sudo cpupower frequency-set --governor performance
```
- Modify this line in the code of the UE and rebuild:
```diff
-#define NR_UE_CAPABILITY_SLOT_RX_TO_TX (3)
+#define NR_UE_CAPABILITY_SLOT_RX_TO_TX (6)
```
If that doesnt work, adjust `--vrtsim.timescale <timescale>` argument on `nr-softmodem`.
Start lowering the timescale (e.g. to 0.2) to slow down time until the UE connects.

View File

@@ -0,0 +1,95 @@
# About
In this step using `iperf` to test the modems performance is explained.
# Running
## Option 1: --noS1 mode
Select this option on slower machines. This doesn't require core network.
1. Run gNB
```bash
sudo ./nr-softmodem -O ../../targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.conf --gNBs.[0].min_rxtxtime 6 --device.name vrtsim --vrtsim.role server --noS1 --do-ra
```
2. Create UE namespace
```bash
sudo ip netns add ue_ns
```
2. Run UE in the new namespace
```bash
sudo ip netns exec bash
sudo ./nr-uesoftmodem -C 3619200000 -r 106 --numerology 1 --ssb 516 --band 78 --device.name vrtsim --noS1 --do-ra
```
3. Run iperf server in the UE namespace
```bash
sudo ip netns exec bash
iperf -s -B 10.0.1.2
```
4. Run iperf client in host namespace
```bash
iperf -c 10.0.1.2 -B 10.0.1.1
```
## Option 2: with core network
1. Start `cn` in `doc/tutorial_resources/oai-cn5g` with `docker compose up`
2. Run gNB
```bash
sudo ./nr-softmodem -O ../../targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.conf --gNBs.[0].min_rxtxtime 6 --device.name vrtsim --vrtsim.role server
```
3. Verify connection to core network in gNB stdout
```
[NGAP] Send NGSetupRequest to AMF
[NGAP] 3584 -> 0000e000
[NGAP] Served GUAMIs for AMF OAI-AMF (assoc_id=3):
[NGAP] GUAMI:
[NGAP] PLMN: MCC=001, MNC=01
[NGAP] AMF Region ID: 1
[NGAP] AMF Set ID: 1
[NGAP] AMF Pointer: 1
[NGAP] Supported PLMN 0: MCC=001 MNC=01
[NGAP] Supported slice (PLMN 0): SST=0x01 SD=000
[NGAP] Received NGSetupResponse from AMF
```
4. Connect the UE.
```bash
sudo ./nr-uesoftmodem -C 3619200000 -r 106 --numerology 1 --ssb 516 --band 78 --device.name vrtsim --uicc0.imsi 001010000000001
```
5. Verify interface is created
```
[OIP] Interface oaitun_ue1 successfully configured, IPv4 10.0.0.2, IPv6 (null)
```
```bash
ifconfig | grep tun
```
6. Run iperf in oai-ext-dn container
```bash
docker exec -it oai-ext-dn bash
iperf -c 10.0.0.2 -B 192.168.70.135
```
## Troubleshooting
Use the same steps as from `first_steps.md`. This test adds additional requirements
on the CPU which might cause the UE to fail.

View File

@@ -219,7 +219,7 @@ void nr_ue_ssb_rsrp_measurements(PHY_VARS_NR_UE *ue,
int SNRtimes10 = dB_fixed_x10(signal_pwr) - dB_fixed_x10(ue->measurements.n0_power_avg);
ue->measurements.ssb_sinr_dB[ssb_index] = SNRtimes10 / 10.0;
LOG_D(PHY,
LOG_I(PHY,
"[UE %d] ssb %d SS-RSRP: %d dBm/RE (%f dB/RE), SS-SINR: %f dB\n",
ue->Mod_id,
ssb_index,

View File

@@ -20,6 +20,7 @@
*/
#include "PHY/TOOLS/tools_defs.h"
#include "notified_fifo.h"
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
@@ -54,7 +55,10 @@
typedef enum { ROLE_SERVER = 1, ROLE_CLIENT } role;
#define MAX_NUM_ANTENNAS_TX 4
#define MAX_CHANNEL_LENGTH (1 << 20)
#define MAX_CHANNEL_LENGTH 200
#define SAVED_SAMPLES_LEN (MAX_CHANNEL_LENGTH - 1)
#define BATCH_SIZE 4096
#define FIRST_BATCH_SIZE (BATCH_SIZE / 2)
#define ROLE_CLIENT_STRING "client"
#define ROLE_SERVER_STRING "server"
@@ -112,6 +116,7 @@ typedef struct {
uint64_t rx_samples_late;
uint64_t rx_early;
uint64_t rx_samples_total;
int num_tx_timing;
tx_timing_t *tx_timing;
peer_info_t peer_info;
int chanmod;
@@ -120,14 +125,12 @@ typedef struct {
int tx_num_channels;
int rx_num_channels;
channel_desc_t *channel_desc;
int num_chanmod_actors;
Actor_t *channel_modelling_actors;
char *taps_socket;
int client_num_rx_antennas;
} vrtsim_state_t;
// Sample history for channel impulse response
static c16_t saved_samples[MAX_NUM_ANTENNAS_TX][MAX_CHANNEL_LENGTH] __attribute__((aligned(32))) = {0};
static void histogram_add(histogram_t *histogram, double diff)
{
histogram->num_samples++;
@@ -312,10 +315,11 @@ static int vrtsim_connect(openair0_device *device)
}
// Handle channel modelling after number of RX antennas are known
int num_tx_stats = 1;
vrtsim_state->num_tx_timing = 1;
if (vrtsim_state->chanmod || vrtsim_state->taps_socket) {
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++) {
vrtsim_state->num_chanmod_actors = vrtsim_state->peer_info.num_rx_antennas * vrtsim_state->tx_num_channels;
vrtsim_state->channel_modelling_actors = calloc_or_fail(vrtsim_state->num_chanmod_actors, sizeof(Actor_t));
for (int i = 0; i < vrtsim_state->num_chanmod_actors; i++) {
init_actor(&vrtsim_state->channel_modelling_actors[i], "chanmod", -1);
}
if (vrtsim_state->taps_socket) {
@@ -327,10 +331,10 @@ static int vrtsim_connect(openair0_device *device)
} else {
load_channel_model(vrtsim_state);
}
num_tx_stats = vrtsim_state->peer_info.num_rx_antennas;
vrtsim_state->num_tx_timing = vrtsim_state->num_chanmod_actors;
}
vrtsim_state->tx_timing = calloc_or_fail(num_tx_stats, sizeof(tx_timing_t));
for (int i = 0; i < num_tx_stats; i++) {
vrtsim_state->tx_timing = calloc_or_fail(vrtsim_state->num_tx_timing, sizeof(tx_timing_t));
for (int i = 0; i < vrtsim_state->num_tx_timing; i++) {
vrtsim_state->tx_timing[i].tx_histogram.min_samples = 100;
// Set the histogram range to 3000uS. Anything above that is not interesting
vrtsim_state->tx_timing[i].tx_histogram.range = 3000.0;
@@ -371,25 +375,24 @@ typedef struct {
vrtsim_state_t *vrtsim_state;
openair0_timestamp timestamp;
c16_t *samples[MAX_NUM_ANTENNAS_TX];
c16_t *first_samples[MAX_NUM_ANTENNAS_TX];
int nsamps;
int nbAnt;
int flags;
int aarx;
int task_index;
int num_tasks;
int tx_timing_index;
c16_t samples_copy[1];
} channel_modelling_args_t;
static void perform_channel_modelling(void *arg)
{
channel_modelling_args_t *channel_modelling_args = arg;
vrtsim_state_t *vrtsim_state = channel_modelling_args->vrtsim_state;
int nsamps = channel_modelling_args->nsamps;
int aarx = channel_modelling_args->aarx;
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_modelling_args_t *args = arg;
vrtsim_state_t *vrtsim_state = args->vrtsim_state;
int nsamps = args->nsamps;
int aarx = args->aarx;
int nb_tx_ant = args->nbAnt;
channel_desc_t *channel_desc = vrtsim_state->channel_desc;
@@ -417,90 +420,149 @@ static void perform_channel_modelling(void *arg)
}
}
for (int aatx = 0; aatx < nb_tx_ant; aatx++) {
c16_t *previous_samples = saved_samples[aatx];
for (int i = 0; i < nsamps; i++) {
cf_t *impulse_response = channel_impulse_response_p[aatx];
for (int l = 0; l < channel_desc->channel_length; l++) {
int idx = i - l;
// TODO: Use AVX2 for this
c16_t tx_input = idx >= 0 ? input_samples[aatx][idx]
: previous_samples[(channel_modelling_args->timestamp + i + idx) % MAX_CHANNEL_LENGTH];
samples[i].r += tx_input.r * impulse_response[l].r - tx_input.i * impulse_response[l].i;
samples[i].i += tx_input.i * impulse_response[l].r + tx_input.r * impulse_response[l].i;
// Calculate number of batches to process per thread.
int num_batches_to_process = 0;
if (nsamps > args->task_index * FIRST_BATCH_SIZE) {
num_batches_to_process = 1;
int nsamps_after_first_batch = max(0, nsamps - args->num_tasks * FIRST_BATCH_SIZE);
int num_full_size_batches = nsamps_after_first_batch / (args->num_tasks * BATCH_SIZE);
int nsamps_left = nsamps_after_first_batch - (num_full_size_batches * BATCH_SIZE);
if (nsamps_left > args->task_index * BATCH_SIZE)
num_batches_to_process++;
num_batches_to_process += num_full_size_batches;
}
for (int batch_index = 0; batch_index < num_batches_to_process; batch_index++) {
int batch_start_offset;
int batch_size;
if (batch_index == 0) {
batch_start_offset = args->task_index * FIRST_BATCH_SIZE;
batch_size = FIRST_BATCH_SIZE;
} else {
batch_start_offset = args->num_tasks * (FIRST_BATCH_SIZE + (batch_index - 1) * BATCH_SIZE) + args->task_index * BATCH_SIZE;
batch_size = BATCH_SIZE;
}
int batch_nsamps = min(batch_size, nsamps - (batch_start_offset - args->timestamp));
int aligned_nsamps = ceil_mod(batch_nsamps, (512 / 8) / sizeof(cf_t));
cf_t samples[aligned_nsamps] __attribute__((aligned(64)));
get_noise_vector((float *)samples, batch_nsamps * 2);
c16_t **input_samples = batch_start_offset == 0 ? args->first_samples : (c16_t **)args->samples;
for (int aatx = 0; aatx < nb_tx_ant; aatx++) {
for (int i = 0; i < batch_nsamps; i++) {
cf_t *impulse_response = channel_impulse_response_p[aatx];
cf_t sample = {0, 0};
for (int l = 0; l < channel_desc->channel_length; l++) {
int idx = i - l;
// TODO: Use AVX2 for this
c16_t tx_input = input_samples[aatx][batch_start_offset + idx];
sample.r += tx_input.r * impulse_response[l].r - tx_input.i * impulse_response[l].i;
sample.i += tx_input.i * impulse_response[l].r + tx_input.r * impulse_response[l].i;
}
samples[i].r += sample.r;
samples[i].i += sample.i;
}
}
}
// Convert to c16_t
c16_t samples_out[aligned_nsamps] __attribute__((aligned(64)));
// 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++) {
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));
}
for (int i = 0; i < aligned_nsamps / 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++) {
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));
}
for (int i = 0; i < aligned_nsamps / 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++) {
samples_out[i].r = lroundf(samples[i].r);
samples_out[i].i = lroundf(samples[i].i);
}
for (int i = 0; i < nsamps; i++) {
samples_out[i].r = lroundf(samples[i].r);
samples_out[i].i = lroundf(samples[i].i);
}
#endif
vrtsim_write_internal(channel_modelling_args->vrtsim_state,
channel_modelling_args->timestamp,
samples_out,
channel_modelling_args->nsamps,
aarx,
channel_modelling_args->flags,
aarx);
vrtsim_write_internal(args->vrtsim_state,
args->timestamp + batch_start_offset,
samples_out,
batch_nsamps,
aarx,
args->flags,
args->tx_timing_index);
}
}
static int vrtsim_write_with_chanmod(vrtsim_state_t *vrtsim_state,
openair0_timestamp timestamp,
void **samplesVoid,
c16_t **samplesVoid,
int nsamps,
int nbAnt,
int flags)
{
AssertFatal(nbAnt < MAX_NUM_ANTENNAS_TX, "Number of antennas %d exceeds maximum %d\n", nbAnt, MAX_NUM_ANTENNAS_TX);
AssertFatal(nbAnt <= MAX_NUM_ANTENNAS_TX, "Number of antennas %d exceeds maximum %d\n", nbAnt, MAX_NUM_ANTENNAS_TX);
static int actor = 0;
// Sample history for channel impulse response
static c16_t saved_samples[MAX_NUM_ANTENNAS_TX][SAVED_SAMPLES_LEN] __attribute__((aligned(32))) = {0};
static openair0_timestamp last_timestamp = 0;
for (int aarx = 0; aarx < vrtsim_state->peer_info.num_rx_antennas; aarx++) {
notifiedFIFO_elt_t *task = newNotifiedFIFO_elt(sizeof(channel_modelling_args_t), 0, NULL, perform_channel_modelling);
channel_modelling_args_t *args = (channel_modelling_args_t *)NotifiedFifoData(task);
args->vrtsim_state = vrtsim_state;
args->timestamp = timestamp;
args->nsamps = nsamps;
args->nbAnt = nbAnt;
args->flags = flags;
args->aarx = aarx;
for (int i = 0; i < nbAnt; i++) {
args->samples[i] = samplesVoid[i];
for (int task_index = 0; task_index < vrtsim_state->tx_num_channels; task_index++) {
// Allocate extra space at the end of the message to copy historical samples
size_t extra_size = sizeof(c16_t) * (BATCH_SIZE + SAVED_SAMPLES_LEN) * nbAnt;
notifiedFIFO_elt_t *task = newNotifiedFIFO_elt(sizeof(channel_modelling_args_t) + extra_size, 0, NULL, perform_channel_modelling);
channel_modelling_args_t *args = NotifiedFifoData(task);
if (task_index == 0) {
for (int aatx = 0; aatx < nbAnt; aatx++) {
c16_t *buffer = (c16_t *)(args + 1) + aatx * (FIRST_BATCH_SIZE + SAVED_SAMPLES_LEN);
size_t gap_samples = timestamp - last_timestamp;
if (gap_samples > 0) {
size_t gap_samples_needed = min(SAVED_SAMPLES_LEN, gap_samples);
memset(&buffer[SAVED_SAMPLES_LEN - gap_samples_needed], 0, sizeof(c16_t) * gap_samples_needed);
if (gap_samples < SAVED_SAMPLES_LEN) {
size_t samples_from_saved = SAVED_SAMPLES_LEN - gap_samples_needed;
memcpy(&buffer[0], &saved_samples[aatx][SAVED_SAMPLES_LEN - samples_from_saved], sizeof(c16_t) * samples_from_saved);
}
} else {
memcpy(buffer, saved_samples[aatx], sizeof(c16_t) * SAVED_SAMPLES_LEN);
}
memcpy(&buffer[SAVED_SAMPLES_LEN], &samplesVoid[aatx][0], sizeof(c16_t) * FIRST_BATCH_SIZE);
args->first_samples[aatx] = buffer + SAVED_SAMPLES_LEN;
}
}
for (int i = 0; i < nbAnt; i++) {
args->samples[i] = samplesVoid[i];
}
args->vrtsim_state = vrtsim_state;
args->timestamp = timestamp;
args->nsamps = nsamps;
args->nbAnt = nbAnt;
args->flags = flags;
args->aarx = aarx;
args->task_index = task_index;
args->num_tasks = vrtsim_state->tx_num_channels;
actor = (actor + 1) % vrtsim_state->num_chanmod_actors;
args->tx_timing_index = actor;
pushNotifiedFIFO(&vrtsim_state->channel_modelling_actors[actor].fifo, task);
}
pushNotifiedFIFO(&vrtsim_state->channel_modelling_actors[aarx].fifo, task);
}
int start_index = timestamp % MAX_CHANNEL_LENGTH;
int end_index = min(start_index + nsamps, MAX_CHANNEL_LENGTH);
int cp_nsamps = end_index - start_index;
for (int aatx = 0; aatx < nbAnt; aatx++) {
c16_t *samples = (c16_t *)samplesVoid[aatx];
memcpy(&saved_samples[aatx][start_index], &samples[0], sizeof(c16_t) * cp_nsamps);
}
if (end_index < start_index + nsamps) {
// wrap around condition, write at beginning of buffer
cp_nsamps = nsamps - cp_nsamps; // remaining samples
start_index = 0;
// Save samples for next round
if (nsamps < MAX_CHANNEL_LENGTH) {
for (int aatx = 0; aatx < nbAnt; aatx++) {
c16_t *samples = (c16_t *)samplesVoid[aatx];
memcpy(&saved_samples[aatx][start_index], &samples[0], sizeof(c16_t) * cp_nsamps);
memmove(&saved_samples[aatx][0], &saved_samples[aatx][nsamps], sizeof(c16_t) * (SAVED_SAMPLES_LEN - nsamps));
memcpy(&saved_samples[aatx][SAVED_SAMPLES_LEN - nsamps], &samplesVoid[aatx][0], sizeof(c16_t) * nsamps);
}
} else {
for (int aatx = 0; aatx < nbAnt; aatx++) {
memcpy(saved_samples[aatx], &samplesVoid[aatx][nsamps - SAVED_SAMPLES_LEN], sizeof(c16_t) * (SAVED_SAMPLES_LEN));
}
}
last_timestamp = timestamp + nsamps;
return nsamps;
}
@@ -515,7 +577,7 @@ static int vrtsim_write(openair0_device *device, openair0_timestamp timestamp, v
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;
return channel_modelling ? vrtsim_write_with_chanmod(vrtsim_state, timestamp, samplesVoid, nsamps, nbAnt, flags)
return channel_modelling ? vrtsim_write_with_chanmod(vrtsim_state, timestamp, (c16_t **)samplesVoid, nsamps, nbAnt, flags)
: vrtsim_write_internal(vrtsim_state, timestamp, (c16_t *)samplesVoid[0], nsamps, 0, flags, 0);
}
@@ -569,18 +631,18 @@ static void vrtsim_end(openair0_device *device)
tx_timing_t *tx_timing = vrtsim_state->tx_timing;
if (vrtsim_state->chanmod || vrtsim_state->taps_socket) {
for (int i = 0; i < vrtsim_state->peer_info.num_rx_antennas; i++) {
for (int i = 0; i < vrtsim_state->num_chanmod_actors; i++) {
shutdown_actor(&vrtsim_state->channel_modelling_actors[i]);
}
free(vrtsim_state->channel_modelling_actors);
for (int i = 1; i < vrtsim_state->peer_info.num_rx_antennas; i++) {
for (int i = 1; i < vrtsim_state->num_tx_timing; i++) {
histogram_merge(&tx_timing->tx_histogram, &tx_timing[i].tx_histogram);
tx_timing->tx_early += tx_timing[i].tx_early;
tx_timing->tx_samples_late += tx_timing[i].tx_samples_late;
tx_timing->average_tx_budget += tx_timing[i].average_tx_budget;
tx_timing->tx_samples_total += tx_timing[i].tx_samples_total;
}
tx_timing->average_tx_budget /= vrtsim_state->peer_info.num_rx_antennas;
tx_timing->average_tx_budget /= vrtsim_state->num_tx_timing;
free_noise_device();
if (vrtsim_state->taps_socket) {
taps_client_stop();

View File

@@ -248,3 +248,21 @@ e2_agent = {
#sm_dir = "/path/where/the/SMs/are/located/"
sm_dir = "/usr/local/lib/flexric/"
};
channelmod = {
max_chan=10;
modellist="modellist_rfsimu_1";
modellist_rfsimu_1 = (
{
model_name = "server_tx_channel_model"
type = "AWGN";
ploss_dB = 0;
noise_power_dB = 0;
forgetfact = 0;
offset = 0;
ds_tdl = 0;
}
);
};