Channels

CommChannel (abstract base)

class netrl.CommChannel[source]

Bases: ABC

Abstract interface for a communication channel simulation.

Contract

  • transmit(obs, step) is called exactly once per env.step() with the raw observation produced by the wrapped environment and the current integer step counter.

  • flush(step) is called exactly once per env.step() (after transmit) and returns all packets whose scheduled arrival_step <= step.

  • reset() is called on env.reset(); must clear all pending packets and any internal channel state.

  • get_channel_info() returns a diagnostic dict for logging; minimum keys: {“state”: str, “pending_count”: int}.

Fixed-delay channels (GEChannel) guarantee at most one packet returned by flush() per step. Variable-delay channels may return more.

abstractmethod transmit(obs, step, packet_size=None)[source]

Simulate transmission of obs at integer step step.

The channel decides whether the packet is lost and, if not, computes a delivery step (>= step) and queues the packet internally.

Parameters:
  • obs (np.ndarray Raw observation from the wrapped env.)

  • step (int Current integer step counter (0-indexed).)

  • packet_size (int | None Payload size in bytes for this packet.) – None means use the channel’s default. Channels that do not model packet-size effects (GE, Perfect) silently ignore it.

Return type:

None

abstractmethod flush(step)[source]

Return all packets whose arrival_step <= step.

Each element is a tuple (arrival_step: int, obs: np.ndarray). Returns an empty list when no packet is due.

Parameters:

step (int Current integer step counter.)

Return type:

List[Tuple[int, ndarray]]

abstractmethod reset()[source]

Clear pending packets and reset internal state. Called on env.reset(). Must NOT re-seed the RNG.

Return type:

None

abstractmethod get_channel_info()[source]

Return a diagnostic dict for logging or debugging.

Minimum keys: {“state”: str, “pending_count”: int}.

Return type:

dict


NetworkConfig

class netrl.NetworkConfig(p_gb=0.1, p_bg=0.3, loss_good=0.01, loss_bad=0.2, delay_steps=0, buffer_size=10, seed=42)[source]

Bases: object

All parameters required to instantiate a Gilbert-Elliott channel and an ObservationBuffer.

Gilbert-Elliott two-state Markov chain

States: GOOD (0), BAD (1).

Transition matrix:

GOOD -> BAD with probability p_gb per step BAD -> GOOD with probability p_bg per step (self-loops: 1 - p_gb and 1 - p_bg respectively)

Steady-state probability of BAD = p_gb / (p_gb + p_bg).

In state GOOD, each packet is LOST with probability loss_good. In state BAD, each packet is LOST with probability loss_bad.

Successfully transmitted packets arrive after exactly delay_steps steps.

p_gb: float = 0.1

Probability of transitioning Good -> Bad per step.

p_bg: float = 0.3

Probability of transitioning Bad -> Good per step.

loss_good: float = 0.01

Packet loss probability in the Good state.

loss_bad: float = 0.2

Packet loss probability in the Bad state.

__init__(p_gb=0.1, p_bg=0.3, loss_good=0.01, loss_bad=0.2, delay_steps=0, buffer_size=10, seed=42)
Parameters:
Return type:

None

delay_steps: int = 0

Fixed one-way propagation delay expressed in environment steps.

buffer_size: int = 10

Number of time slots retained in the ObservationBuffer (window length).

seed: int = 42

RNG seed forwarded to the C++ Gilbert-Elliott backend.

validate()[source]

Raise ValueError if any parameter is outside its valid range.

Return type:

None

Parameters:
Parameter reference

Parameter

Default

Description

p_gb

0.1

Good → Bad transition probability per step.

p_bg

0.3

Bad → Good transition probability per step.

loss_good

0.01

Packet loss probability in the Good state.

loss_bad

0.20

Packet loss probability in the Bad state.

delay_steps

3

Fixed one-way propagation delay (integer steps).

buffer_size

10

Number of slots in the observation sliding window.

seed

42

RNG seed for the C++ GE channel core.


GEChannel

class netrl.GEChannel(config)[source]

Bases: CommChannel

Gilbert-Elliott channel backed by the C++ pybind11 extension netcomm.

The Markov chain state (Good/Bad), RNG, and pending packet queue all live inside the C++ GEChannelImpl object for atomicity and reproducibility.

Parameters:

config (NetworkConfig) – Channel and buffer configuration. delay_steps, p_gb, p_bg, loss_good, loss_bad, and seed are forwarded to the C++ backend.

Raises:

ImportError – If the netcomm C++ extension has not been built. Run pip install -e . or python setup.py build_ext –inplace.

__init__(config)[source]
Parameters:

config (NetworkConfig)

Return type:

None

transmit(obs, step, packet_size=None)[source]

Simulate transmission of obs at integer step step.

The channel decides whether the packet is lost and, if not, computes a delivery step (>= step) and queues the packet internally.

Parameters:
  • obs (np.ndarray Raw observation from the wrapped env.)

  • step (int Current integer step counter (0-indexed).)

  • packet_size (int | None Payload size in bytes for this packet.) – None means use the channel’s default. Channels that do not model packet-size effects (GE, Perfect) silently ignore it.

Return type:

None

flush(step)[source]

Return all packets whose arrival_step <= step.

Each element is a tuple (arrival_step: int, obs: np.ndarray). Returns an empty list when no packet is due.

Parameters:

step (int Current integer step counter.)

Return type:

List[Tuple[int, ndarray]]

reset()[source]

Clear pending packets and reset internal state. Called on env.reset(). Must NOT re-seed the RNG.

Return type:

None

get_channel_info()[source]

Return a diagnostic dict for logging or debugging.

Minimum keys: {“state”: str, “pending_count”: int}.

Return type:

dict

PerfectChannel

class netrl.PerfectChannel(config=None)[source]

Bases: CommChannel

Lossless, zero-delay channel for debugging and baselines.

Does not require the C++ extension. Every transmitted packet is immediately available at the same step via flush().

Parameters:

config (NetworkConfig | None)

__init__(config=None)[source]
Parameters:

config (NetworkConfig | None)

Return type:

None

transmit(obs, step, packet_size=None)[source]

Simulate transmission of obs at integer step step.

The channel decides whether the packet is lost and, if not, computes a delivery step (>= step) and queues the packet internally.

Parameters:
  • obs (np.ndarray Raw observation from the wrapped env.)

  • step (int Current integer step counter (0-indexed).)

  • packet_size (int | None Payload size in bytes for this packet.) – None means use the channel’s default. Channels that do not model packet-size effects (GE, Perfect) silently ignore it.

Return type:

None

flush(step)[source]

Return all packets whose arrival_step <= step.

Each element is a tuple (arrival_step: int, obs: np.ndarray). Returns an empty list when no packet is due.

Parameters:

step (int Current integer step counter.)

Return type:

List[Tuple[int, ndarray]]

reset()[source]

Clear pending packets and reset internal state. Called on env.reset(). Must NOT re-seed the RNG.

Return type:

None

get_channel_info()[source]

Return a diagnostic dict for logging or debugging.

Minimum keys: {“state”: str, “pending_count”: int}.

Return type:

dict


NS3WiFiChannelFastConfig

class netrl.NS3WiFiChannelFastConfig(distance_m=15.0, step_duration_ms=2.0, tx_power_dbm=20.0, loss_exponent=3.0, max_retries=7, packet_size_bytes=256)[source]

Bases: object

Configuration for the fast NS3WiFiChannel (pybind11 binding).

This is a direct C++ binding with no subprocess overhead.

Parameters:
  • distance_m (float) – STA-to-AP distance in metres. Default: 15.0

  • step_duration_ms (float) – Environment step duration in milliseconds. Default: 2.0

  • tx_power_dbm (float) – TX power in dBm. Default: 20.0

  • loss_exponent (float) – Path-loss exponent. Default: 3.0

  • max_retries (int) – Maximum MAC retransmission attempts. Default: 7

  • packet_size_bytes (int) – Default packet size in bytes. Default: 256

distance_m: float = 15.0
step_duration_ms: float = 2.0
tx_power_dbm: float = 20.0
loss_exponent: float = 3.0
max_retries: int = 7
packet_size_bytes: int = 256
__init__(distance_m=15.0, step_duration_ms=2.0, tx_power_dbm=20.0, loss_exponent=3.0, max_retries=7, packet_size_bytes=256)
Parameters:
  • distance_m (float)

  • step_duration_ms (float)

  • tx_power_dbm (float)

  • loss_exponent (float)

  • max_retries (int)

  • packet_size_bytes (int)

Return type:

None

NS3WiFiChannelFast

class netrl.NS3WiFiChannelFast(config, distance_m=15.0, step_duration_ms=2.0, tx_power_dbm=20.0, loss_exponent=3.0, max_retries=7, packet_size_bytes=256)[source]

Bases: CommChannel

Fast WiFi simulator using pybind11 (direct C++ binding).

This is a drop-in replacement for NS3WifiChannel (subprocess version) with 10-50x better performance.

Differences from subprocess version: - No subprocess overhead - Faster communication - Multiple packets per flush supported natively - All existing code works unchanged!

Parameters:
__init__(config, distance_m=15.0, step_duration_ms=2.0, tx_power_dbm=20.0, loss_exponent=3.0, max_retries=7, packet_size_bytes=256)[source]

Create fast WiFi channel (pybind11 binding).

Parameters:
  • config (NetworkConfig Buffer and channel config.)

  • distance_m (float STA-AP distance (meters).)

  • step_duration_ms (float Environment step duration (ms).)

  • tx_power_dbm (float TX power (dBm).)

  • loss_exponent (float Path-loss exponent.)

  • max_retries (int MAC retry limit.)

  • packet_size_bytes (int Default packet size (bytes).)

Return type:

None

transmit(obs, step, packet_size=None)[source]

Simulate transmission of obs at integer step step.

The channel decides whether the packet is lost and, if not, computes a delivery step (>= step) and queues the packet internally.

Parameters:
  • obs (np.ndarray Raw observation from the wrapped env.)

  • step (int Current integer step counter (0-indexed).)

  • packet_size (int | None Payload size in bytes for this packet.) – None means use the channel’s default. Channels that do not model packet-size effects (GE, Perfect) silently ignore it.

Return type:

None

flush(step)[source]

Return all packets whose arrival_step <= step.

Each element is a tuple (arrival_step: int, obs: np.ndarray). Returns an empty list when no packet is due.

Parameters:

step (int Current integer step counter.)

Return type:

List[Tuple[int, ndarray]]

reset()[source]

Clear pending packets and reset internal state. Called on env.reset(). Must NOT re-seed the RNG.

Return type:

None

get_channel_info()[source]

Get diagnostic information.

Return type:

dict


NS3WifiConfig

class netrl.NS3WifiConfig(distance_m=10.0, step_duration_ms=1.0, tx_power_dbm=20.0, loss_exponent=3.0, max_retries=7, packet_size_bytes=64, max_pending_steps=200, sim_binary='')[source]

Bases: object

Configuration for the NS3WifiChannel — ns-3 802.11a single-hop WiFi link.

Physical / channel parameters

These are forwarded as command-line arguments to the ns3_wifi_sim subprocess at startup and stay fixed for the lifetime of the channel (until reset).

distance_mfloat

STA-to-AP Euclidean distance in metres. Larger distances raise path loss and increase both loss probability and MAC retransmission delay. For 802.11a at 20 dBm TX, usable range with path-loss exponent 3 is roughly 0–80 m depending on the loss model.

step_duration_msfloat

Width of one environment step in ns-3 simulation time (milliseconds). Each env step = one ns-3 interval. Smaller values give finer temporal resolution of WiFi delay (e.g. 1 ms ≈ 1 MAC frame time at 54 Mbps), while larger values (e.g. 10 ms) let many retransmissions complete within a single step, giving mostly zero-step delay with occasional drops. Recommended: 1–10 ms.

tx_power_dbmfloat

Transmit power of the STA in dBm.

loss_exponentfloat

Path-loss exponent n for the log-distance model (PL(d) = PL_ref + 10*n*log10(d/d_ref)). Typical values: 2 (free space), 3 (indoor/outdoor mixed), 4 (dense indoor).

max_retriesint

Maximum number of MAC-layer retransmission attempts before a frame is declared undeliverable and dropped. Each retry adds ~3–5 ms of delay (DIFS + backoff + ACK timeout). ns-3 default is 7.

packet_size_bytesint

Total UDP payload in bytes for the probe packet. The first 4 bytes carry the step_id; the remainder is padding. Larger packets have higher collision probability and longer transmission time.

Timing / Python side

max_pending_stepsint

Maximum number of env steps a transmitted packet is allowed to be in-flight before it is considered lost on the Python side and removed from the pending dictionary. Must be at least ceil(max_retransmission_delay_ms / step_duration_ms). Default 200 steps is conservative for step_duration_ms >= 1.

sim_binarystr

Path to the compiled ns3_wifi_sim binary. If empty (default), the channel auto-detects it at <project_root>/src/ns3_wifi_sim.

distance_m: float = 10.0
step_duration_ms: float = 1.0
tx_power_dbm: float = 20.0
loss_exponent: float = 3.0
max_retries: int = 7
packet_size_bytes: int = 64
max_pending_steps: int = 200

Steps after which an unacknowledged transmission is declared lost.

__init__(distance_m=10.0, step_duration_ms=1.0, tx_power_dbm=20.0, loss_exponent=3.0, max_retries=7, packet_size_bytes=64, max_pending_steps=200, sim_binary='')
Parameters:
  • distance_m (float)

  • step_duration_ms (float)

  • tx_power_dbm (float)

  • loss_exponent (float)

  • max_retries (int)

  • packet_size_bytes (int)

  • max_pending_steps (int)

  • sim_binary (str)

Return type:

None

sim_binary: str = ''

Absolute path to the ns3_wifi_sim binary. If empty, auto-detected as <project_root>/src/ns3_wifi_sim.

validate()[source]

Raise ValueError for out-of-range parameters.

Return type:

None

Parameters:
  • distance_m (float)

  • step_duration_ms (float)

  • tx_power_dbm (float)

  • loss_exponent (float)

  • max_retries (int)

  • packet_size_bytes (int)

  • max_pending_steps (int)

  • sim_binary (str)

NS3WifiChannel

class netrl.NS3WifiChannel(config, ns3_config=None)[source]

Bases: CommChannel

CommChannel implementation backed by an ns-3 802.11a WiFi simulation.

Each instance manages exactly one subprocess running ns3_wifi_sim. The subprocess is started on construction and stays alive until reset() rebuilds the simulation or the Python object is garbage-collected.

Parameters:
  • config (NetworkConfig NetRL shared config (buffer_size, seed etc.).) – Note: GE-specific fields (p_gb, p_bg, …) are not used here — WiFi channel physics determine loss and delay.

  • ns3_config (NS3WifiConfig ns-3-specific physical-layer parameters.) – If None, defaults are used.

__init__(config, ns3_config=None)[source]
Parameters:
Return type:

None

transmit(obs, step, packet_size=None)[source]

Instruct ns-3 to simulate sending the observation at env step step.

The probe packet carrying the step_id is scheduled inside the ns-3 subprocess at simulation time step * step_ms + 0.01 * step_ms. The observation is stored locally; ns-3 only carries the 4-byte step_id, not the full observation data.

Parameters:
  • obs (np.ndarray Raw observation from the wrapped env.)

  • step (int Current integer step counter (0-indexed).)

  • packet_size (int | None Payload size in bytes. None uses the) – default from NS3WifiConfig.packet_size_bytes.

Return type:

None

flush(step)[source]

Advance the ns-3 simulation to the end of env step step and return all observations whose packets arrived within this window.

Observations from earlier steps that had multi-step delays (due to WiFi MAC retransmissions) may appear here.

Additionally, pending observations older than max_pending_steps are expired (considered permanently lost).

Parameters:

step (int Current integer step counter.)

Returns:

  • List of (arrival_step, obs) tuples. arrival_step is set to step

  • (the flush step) regardless of the exact ns-3 arrival time within

  • the window, because the CommChannel contract only requires that

  • return values have arrival_step <= step.

Return type:

List[Tuple[int, ndarray]]

reset()[source]

Restart the ns-3 simulation (simulation time → 0) and clear all pending / arrived state. Called on env.reset().

The subprocess stays alive; only the ns-3 internal simulator state is destroyed and rebuilt. This is equivalent to starting a new wireless scenario.

Return type:

None

get_channel_info()[source]

Return diagnostic information about the channel state.

Does not query the subprocess (would require an extra round-trip); derives info from Python-side bookkeeping instead.

Return type:

dict


NS3MmWaveConfig

class netrl.NS3MmWaveConfig(distance_m=50.0, frequency_ghz=28.0, bandwidth_ghz=0.2, tx_power_dbm=23.0, enb_tx_power_dbm=30.0, noise_figure_db=9.0, enb_noise_figure_db=5.0, scenario='UMa', harq_enabled=True, rlc_am_enabled=False, packet_size_bytes=64, step_duration_ms=1.0, max_pending_steps=500, sim_binary='')[source]

Bases: object

Configuration for NS3MmWaveChannel — ns-3 5G mmWave single-hop link.

All parameters are forwarded as command-line arguments to the ns3_mmwave_sim subprocess. Build it once with:

bash src/build_ns3_mmwave_sim.sh

Physical-layer parameters

distance_mfloat

UE-to-eNB Euclidean distance in metres. At 28 GHz with path-loss exponent ~3.5 (Urban Macro), usable range before complete link failure is roughly 0–200 m. The model uses height-dependent LOS/NLOS probability from the configured 3GPP scenario.

frequency_ghzfloat

Centre carrier frequency in GHz. Common 5G mmWave bands:

Band

Frequency

Range

n257 n258 n260 n261

26.5 – 29.5 GHz 24.25 – 27.5 GHz 37 – 40 GHz 27.5 – 28.35 GHz

~150 m ~200 m ~100 m ~150 m

bandwidth_ghzfloat

Component carrier bandwidth in GHz. Typical NR mmWave values and approximate peak PHY throughput at 28 GHz with 64-QAM: 0.05 GHz → ~350 Mbps, 0.1 GHz → ~700 Mbps, 0.2 GHz → ~1.4 Gbps, 0.4 GHz → ~2.8 Gbps, 0.8 GHz → ~5.6 Gbps.

tx_power_dbmfloat

UE transmit power in dBm. Typical 5G mmWave UE: 23 dBm. Higher power improves uplink SNR and reduces retransmissions.

enb_tx_power_dbmfloat

eNB (gNB) transmit power in dBm. Typical: 30 dBm. Affects downlink quality (not directly observed here since we measure uplink), but influences SINR feedback and scheduling.

noise_figure_dbfloat

UE receiver noise figure in dB. Typical mmWave UE: 7–10 dB. Higher values degrade received SINR and increase packet errors.

enb_noise_figure_dbfloat

eNB receiver noise figure in dB. Typical mmWave base station: 5 dB.

scenariostr

3GPP TR 38.901 propagation scenario. Determines the LOS/NLOS probability model and path-loss formula:

"RMa" Rural Macro — low density, long range "UMa" Urban Macro — default, typical outdoor city "UMi-StreetCanyon" Urban Micro — dense urban, street level "InH-OfficeMixed" Indoor Hotspot — mixed LOS/NLOS office "InH-OfficeOpen" Indoor Hotspot — open-plan office

harq_enabledbool

Enable Hybrid ARQ (HARQ) with incremental redundancy. When True, retransmissions combine with previous attempts to improve decoding probability. Adds 1–5 ms per retransmission round.

rlc_am_enabledbool

Enable RLC Acknowledged Mode (AM). Adds RLC-level retransmissions on top of HARQ, increasing reliability at the cost of additional delay. Recommended False for delay-sensitive observations.

Timing / Python-side parameters

step_duration_msfloat

Width of one environment step in ns-3 simulation time (ms). Each env step = one ns-3 time window.

step_duration_ms

Behaviour

0.5 – 1 ms

2 – 5 ms

10+ ms

~1 TTI; single HARQ round per step; realistic per-packet delay variation 2–5 HARQ rounds fit per step; most packets arrive same step or are dropped Very coarse; near-zero delay variation

packet_size_bytesint

Default UDP payload in bytes for the probe packet. The first 4 bytes carry the step_id; the remainder is padding. Larger packets have longer transmission time (especially at lower MCS) and higher block error probability.

max_pending_stepsint

Maximum env steps a transmitted packet may be in-flight before it is declared lost on the Python side. The mmWave MAC + HARQ can take up to ~10 ms to complete one transmission attempt, and with rlc_am_enabled=True additional RLC retransmissions are possible. Default 500 is conservative for step_duration_ms >= 1.

sim_binarystr

Absolute path to the ns3_mmwave_sim binary. Auto-detected as <project_root>/src/ns3_mmwave_sim when empty.

distance_m: float = 50.0
frequency_ghz: float = 28.0
bandwidth_ghz: float = 0.2
tx_power_dbm: float = 23.0
enb_tx_power_dbm: float = 30.0
noise_figure_db: float = 9.0
enb_noise_figure_db: float = 5.0
scenario: str = 'UMa'
harq_enabled: bool = True
rlc_am_enabled: bool = False
packet_size_bytes: int = 64
step_duration_ms: float = 1.0
max_pending_steps: int = 500
sim_binary: str = ''
validate()[source]

Raise ValueError if any parameter is outside its valid range.

Return type:

None

__init__(distance_m=50.0, frequency_ghz=28.0, bandwidth_ghz=0.2, tx_power_dbm=23.0, enb_tx_power_dbm=30.0, noise_figure_db=9.0, enb_noise_figure_db=5.0, scenario='UMa', harq_enabled=True, rlc_am_enabled=False, packet_size_bytes=64, step_duration_ms=1.0, max_pending_steps=500, sim_binary='')
Parameters:
  • distance_m (float)

  • frequency_ghz (float)

  • bandwidth_ghz (float)

  • tx_power_dbm (float)

  • enb_tx_power_dbm (float)

  • noise_figure_db (float)

  • enb_noise_figure_db (float)

  • scenario (str)

  • harq_enabled (bool)

  • rlc_am_enabled (bool)

  • packet_size_bytes (int)

  • step_duration_ms (float)

  • max_pending_steps (int)

  • sim_binary (str)

Return type:

None

Parameters:
  • distance_m (float)

  • frequency_ghz (float)

  • bandwidth_ghz (float)

  • tx_power_dbm (float)

  • enb_tx_power_dbm (float)

  • noise_figure_db (float)

  • enb_noise_figure_db (float)

  • scenario (str)

  • harq_enabled (bool)

  • rlc_am_enabled (bool)

  • packet_size_bytes (int)

  • step_duration_ms (float)

  • max_pending_steps (int)

  • sim_binary (str)

NS3MmWaveChannel

class netrl.NS3MmWaveChannel(config, ns3_config=None)[source]

Bases: CommChannel

CommChannel implementation backed by an ns-3 5G mmWave EPC simulation.

Each instance manages exactly one subprocess running ns3_mmwave_sim. The subprocess is started on construction, performs a 500 ms warm-up (UE attachment + bearer establishment), then emits READY. It stays alive until reset() rebuilds the simulation or the Python object is garbage-collected.

Parameters:
  • config (NetworkConfig NetRL shared config (buffer_size, seed …).) – Note: GE-specific fields are ignored here — 5G mmWave channel physics determine loss and delay.

  • ns3_config (NS3MmWaveConfig 5G-specific physical-layer parameters.) – If None, defaults are used.

__init__(config, ns3_config=None)[source]
Parameters:
Return type:

None

transmit(obs, step, packet_size=None)[source]

Instruct ns-3 to simulate sending the observation at env step step.

Parameters:
  • obs (np.ndarray Raw observation from the wrapped env.)

  • step (int Current integer step counter (0-indexed).)

  • packet_size (int | None Payload size in bytes. None uses the) – default from NS3MmWaveConfig.packet_size_bytes.

Return type:

None

flush(step)[source]

Advance the ns-3 simulation to the end of env step step and return all observations whose packets arrived within this window.

Packets from earlier steps that experienced multi-step delays (HARQ retransmissions, RLC AM retransmissions) may appear here.

Pending observations older than max_pending_steps are expired (considered permanently lost — MAC exhausted all retransmit attempts).

Parameters:

step (int Current integer step counter.)

Return type:

List of (arrival_step, obs) tuples.

reset()[source]

Restart the ns-3 simulation (simulation time → 0) and clear all pending / arrived state. Called on env.reset().

The subprocess stays alive; the ns-3 internal state is destroyed and rebuilt (new warm-up of 500 ms simulation time).

Return type:

None

get_channel_info()[source]

Return diagnostic information about the channel state.

Return type:

dict


NS3LenaConfig

class netrl.NS3LenaConfig(distance_m=50.0, frequency_ghz=28.0, bandwidth_ghz=0.1, ue_tx_power_dbm=23.0, gnb_tx_power_dbm=30.0, scenario='UMa', numerology=3, shadowing_enabled=False, packet_size_bytes=64, step_duration_ms=1.0, max_pending_steps=500, sim_binary='')[source]

Bases: object

Configuration for NS3LenaChannel — ns-3 5G-LENA single-cell NR link.

Parameters are forwarded to the ns3_lena_sim subprocess. Build it once with:

bash src/build_ns3_lena_sim.sh
Parameters:
  • distance_m (float)

  • frequency_ghz (float)

  • bandwidth_ghz (float)

  • ue_tx_power_dbm (float)

  • gnb_tx_power_dbm (float)

  • scenario (str)

  • numerology (int)

  • shadowing_enabled (bool)

  • packet_size_bytes (int)

  • step_duration_ms (float)

  • max_pending_steps (int)

  • sim_binary (str)

distance_m: float = 50.0
frequency_ghz: float = 28.0
bandwidth_ghz: float = 0.1
ue_tx_power_dbm: float = 23.0
gnb_tx_power_dbm: float = 30.0
scenario: str = 'UMa'
numerology: int = 3
shadowing_enabled: bool = False
packet_size_bytes: int = 64
step_duration_ms: float = 1.0
max_pending_steps: int = 500
sim_binary: str = ''
validate()[source]

Raise ValueError if any parameter is outside its valid range.

Return type:

None

__init__(distance_m=50.0, frequency_ghz=28.0, bandwidth_ghz=0.1, ue_tx_power_dbm=23.0, gnb_tx_power_dbm=30.0, scenario='UMa', numerology=3, shadowing_enabled=False, packet_size_bytes=64, step_duration_ms=1.0, max_pending_steps=500, sim_binary='')
Parameters:
  • distance_m (float)

  • frequency_ghz (float)

  • bandwidth_ghz (float)

  • ue_tx_power_dbm (float)

  • gnb_tx_power_dbm (float)

  • scenario (str)

  • numerology (int)

  • shadowing_enabled (bool)

  • packet_size_bytes (int)

  • step_duration_ms (float)

  • max_pending_steps (int)

  • sim_binary (str)

Return type:

None

NS3LenaChannel

class netrl.NS3LenaChannel(config, ns3_config=None)[source]

Bases: CommChannel

CommChannel implementation backed by an ns-3 5G-LENA NR simulation.

Parameters:
__init__(config, ns3_config=None)[source]
Parameters:
Return type:

None

transmit(obs, step, packet_size=None)[source]

Simulate transmission of obs at integer step step.

The channel decides whether the packet is lost and, if not, computes a delivery step (>= step) and queues the packet internally.

Parameters:
  • obs (np.ndarray Raw observation from the wrapped env.)

  • step (int Current integer step counter (0-indexed).)

  • packet_size (int | None Payload size in bytes for this packet.) – None means use the channel’s default. Channels that do not model packet-size effects (GE, Perfect) silently ignore it.

Return type:

None

flush(step)[source]

Return all packets whose arrival_step <= step.

Each element is a tuple (arrival_step: int, obs: np.ndarray). Returns an empty list when no packet is due.

Parameters:

step (int Current integer step counter.)

Return type:

List[Tuple[int, ndarray]]

reset()[source]

Clear pending packets and reset internal state. Called on env.reset(). Must NOT re-seed the RNG.

Return type:

None

get_channel_info()[source]

Return a diagnostic dict for logging or debugging.

Minimum keys: {“state”: str, “pending_count”: int}.

Return type:

dict


NS3WifiMultiUEConfig

class netrl.NS3WifiMultiUEConfig(n_ues=2, distances_m=<factory>, step_duration_ms=1.0, tx_power_dbm=20.0, loss_exponent=3.0, max_retries=7, packet_size_bytes=64, max_pending_steps=200, sim_binary='')[source]

Bases: object

Configuration for a multi-UE ns-3 802.11a infrastructure WiFi network.

Topology: N UEs (STAs) all associated with a single AP (central node). All UEs share the same 802.11a channel and contend via CSMA/CA, producing realistic multi-node uplink behaviour.

Parameters:
  • n_ues (int) – Number of UE nodes (STAs). Must match len(node_ids) passed to CentralNode and the factory returned by make_multi_ue_wifi_factory().

  • distances_m (List[float]) – Euclidean distance (metres) from each UE to the AP. Element i is the distance for UE i. If fewer values than n_ues are given, the last value is repeated for the remaining UEs.

  • step_duration_ms (float) – Width of one environment step in ns-3 simulation time (ms). See NS3WifiConfig for guidance on choosing this value.

  • tx_power_dbm (float) – Transmit power of every STA in dBm.

  • loss_exponent (float) – Path-loss exponent for the log-distance model (2 = free space, 3 = mixed indoor/outdoor, 4 = dense indoor).

  • max_retries (int) – Maximum MAC-layer retransmission attempts per frame.

  • packet_size_bytes (int) – UDP payload in bytes. The first 8 bytes carry the (ue_id, step_id) header; the rest is padding. Must be >= 8.

  • max_pending_steps (int) – A transmitted packet not acknowledged after this many env steps is considered lost on the Python side and purged from memory.

  • sim_binary (str) – Path to the compiled ns3_wifi_multi_ue_sim binary. If empty, auto-detected as <project_root>/src/ns3_wifi_multi_ue_sim.

n_ues: int = 2
distances_m: List[float]
step_duration_ms: float = 1.0
tx_power_dbm: float = 20.0
loss_exponent: float = 3.0
max_retries: int = 7
packet_size_bytes: int = 64
max_pending_steps: int = 200
sim_binary: str = ''
validate()[source]

Raise ValueError for out-of-range or inconsistent parameters.

Return type:

None

__init__(n_ues=2, distances_m=<factory>, step_duration_ms=1.0, tx_power_dbm=20.0, loss_exponent=3.0, max_retries=7, packet_size_bytes=64, max_pending_steps=200, sim_binary='')
Parameters:
Return type:

None

make_multi_ue_wifi_factory

netrl.make_multi_ue_wifi_factory(ns3_cfg)[source]

Create a channel_factory callable for use with CentralNode.

A single ns3_wifi_multi_ue_sim subprocess is started immediately. Each call to the returned factory creates one NS3WifiUEChannel proxy (indexed sequentially: 0, 1, 2, …) that delegates to the shared backend.

The factory must be called exactly ns3_cfg.n_ues times — once per node_id registered with CentralNode — so that len(node_ids) == n_ues.

Parameters:

ns3_cfg (NS3WifiMultiUEConfig) – Physical-layer and network configuration.

Returns:

A factory suitable for CentralNode’s channel_factory parameter.

Return type:

Callable[[NetworkConfig], CommChannel]

Example

ns3_cfg = NS3WifiMultiUEConfig(
    n_ues=3,
    distances_m=[10.0, 30.0, 60.0],
    step_duration_ms=2.0,
)
factory = make_multi_ue_wifi_factory(ns3_cfg)

central = CentralNode(
    node_ids=["ue_0", "ue_1", "ue_2"],
    obs_shape=(4,),
    obs_dtype=np.float32,
    config=NetworkConfig(buffer_size=10),
    channel_factory=factory,
)

NS3WifiUEChannel

class netrl.NS3WifiUEChannel(ue_id, backend, config)[source]

Bases: CommChannel

CommChannel implementation for a single UE in the multi-UE WiFi network.

All instances created by make_multi_ue_wifi_factory() share one NS3WifiMultiUEBackend (and therefore one ns-3 subprocess). This ensures that all UEs contend for the same simulated wireless medium.

Parameters:
  • ue_id (int Zero-based index of this UE.)

  • backend (NS3WifiMultiUEBackend Shared subprocess manager.)

  • config (NetworkConfig NetRL shared config (used for metadata).)

__init__(ue_id, backend, config)[source]
Parameters:
Return type:

None

transmit(obs, step, packet_size=None)[source]

Transmit obs from this UE at env step step.

Stores the observation locally (keyed by step_id) and instructs the shared backend to schedule a packet send in the ns-3 simulation.

Parameters:
Return type:

None

flush(step)[source]

Return observations whose packets arrived at the AP during step step.

Queries the shared backend (which sends FLUSH to ns-3 at most once per step and caches results for subsequent calls). Any pending observations older than max_pending_steps are expired.

Return type:

List of (arrival_step, obs) tuples.

Parameters:

step (int)

reset()[source]

Clear local state and notify the backend that this UE has reset.

The backend sends RESET to the subprocess once all n_ues channels have called reset(), ensuring a single coordinated simulation reset.

Return type:

None

get_channel_info()[source]

Return diagnostic information for this UE channel.

Return type:

dict

NS3WifiMultiUEBackend

class netrl.NS3WifiMultiUEBackend(ns3_cfg)[source]

Bases: object

Manages the ns3_wifi_multi_ue_sim subprocess shared by all UE channels.

Two coordination mechanisms are needed because CentralNode calls each CommChannel in a loop:

Flush coordination

flush(step) sends “FLUSH <step>” to the subprocess exactly once per step. Subsequent calls for the same step return a cached result. CentralNode guarantees strictly increasing step values, so the cache is valid for the lifetime of the step.

Reset coordination

request_reset() is called by each NS3WifiUEChannel on reset(). The RESET command is sent to the subprocess only after all n_ues channels have called request_reset(), ensuring a single clean reset.

Parameters:

ns3_cfg (NS3WifiMultiUEConfig)

__init__(ns3_cfg)[source]
Parameters:

ns3_cfg (NS3WifiMultiUEConfig)

Return type:

None

transmit(ue_id, step_id, pkt_size)[source]

Send TRANSMIT <ue_id> <step_id> <pkt_size> and wait for OK.

Parameters:
Return type:

None

flush(step)[source]

Advance the simulation to the end of env step step and return arrived packet identifiers grouped by UE.

The first call for a given step sends FLUSH to the subprocess and caches the result; subsequent calls for the same step return the cache.

Returns:

step_ids that arrived at the AP in this flush window, per UE.

Return type:

Dict[ue_id -> List[step_id]]

Parameters:

step (int)

request_reset()[source]

Called once by each NS3WifiUEChannel on reset().

Sends RESET to the subprocess only after every UE channel has called this method, guaranteeing exactly one RESET per env.reset().

Return type:

None