Engine and metrics

The protocol engine, the procedures and the metric catalogue.

handoverlab.engine.core

Protocol engine: time loop, radio-link monitoring, attempts and outcome labels.

One Engine simulates one handover rule on given radio samples. All users advance together on the step grid (10 ms by default); cell load and interference are coupled through the users' serving cells. The engine draws no random numbers, so two rules run on the same samples differ only through their decisions.

Per step 1. end of an execution (join the target if its SINR is at least Qout, otherwise an execution failure) or of a re-establishment; 2. radio-link monitoring: serving SINR smoothed over the Qout / Qin windows; N310 out-of-sync indications start T310, N311 in-sync indications stop it; expiry is a radio-link failure followed by re-establishment in the strongest cell; 3. procedure events (a handover command or a conditional configuration reaches the UE); 4. at measurement instants: L3 filter, outcome-label sampling, the handover procedure (see handoverlab.engine.procedures), load and delivered rate.

Outcome of a handover attempt (exactly one per attempt) too_late command or configuration lost, or link failure while the handover was in preparation too_early execution failed, or link failure in the target before the evaluation window closed, and the UE re-established in the source or the target wrong_cell the same, but the UE re-established in a third cell unnecessary completed; the source would not have failed during the window, and the stay was shorter than T_min of the service or the mean SINR gain over the window was below theta_Q useful completed and not unnecessary censored the run ended before the outcome was known

A link failure with no handover in flight is not an attempt; it counts as a failure ("rlf_without_handover" if the UE re-establishes in another cell, "coverage_rlf" if in the same cell). The SINR of the label uses the true received powers with all cells transmitting (label_load), so the label does not depend on what a rule does to the load.

Engine

Simulation of one rule. Feed the radio chunks in order, then call finish.

log_events is the event-log level (see handoverlab.eventlog): "off" (or False), "summary" (or True) keeps the event log for events, "full" also keeps the procedure trace for trace; the fingerprint is always available. debug turns on the invariant checks of handoverlab.engine.invariants (default: the environment variable HANDOVERLAB_DEBUG). Neither changes a result.

meas: np.ndarray property

L3-filtered RSRP (U, M) [dBm].

events: Optional[List[dict]] property

Event log as a list of dictionaries (if the engine was created with log_events).

trace: Optional[List[dict]] property

Procedure trace as a list of dictionaries (event-log level full only).

fingerprint: str property

SHA-256 of the canonical CSV of the event log (see handoverlab.eventlog).

trace_event(t, u, event, src, cell, value=np.nan, note='')

Add a row to the procedure trace (only at event-log level full).

new_flight(u, tgt, t, key=-1, npr=0, oe=np.nan)

A handover attempt of user u towards tgt starts at t.

start_exec(u, tgt, t)

The UE leaves the source cell and synchronises to tgt.

msg(t, n=1) -> None

Count n RRC messages sent at t (since 2.1.0): MeasurementReport, RRCReconfiguration (one per user and instant, whatever the number of conditional candidates it carries) and its RRCReconfigurationComplete; an LTM cell switch command is a MAC CE (TS 38.300 9.2.3.5.2), not counted.

fail(u, t, kind)

Mobility failure of user u: "rlf" (T310 expired), "cmd" (command lost) or "exec".

advance(ch) -> None

Simulate the steps of one radio chunk.

finish() -> Dict[str, float]

Close what is still open, check the invariants and return the metrics of the run.

rlm_indications(P, qo, qi, qout, qin, run, n_o, n_i, mon, t, rlm_t0)

Out-of-sync / in-sync indications of one indication instant as TS 38.331 and TS 38.133 specify (since 2.1.0).

An out-of-sync indication is sent when the filtered quality is below Qout, an in-sync indication when it is above Qin (TS 38.133 8.1.6), at most every max(10 ms, T_SSB). TS 38.331 clause 7.2: N310 counts the out-of-sync indications while T310 is stopped and is reset by an in-sync indication; N311 counts the in-sync indications while T310 runs and is reset by an out-of-sync indication; an instant with neither changes no counter (5.3.10.2, NOTE 2). T310 starts when N310 is reached (5.3.10.1) and stops when N311 is reached (5.3.10.2). mon are the users whose link is monitored, rlm_t0 the end of the monitoring restart on a new cell. Updates run, n_o, n_i in place (1-D or 2-D arrays) and returns the users whose T310 starts.

rlm_kernel(engine)

The compiled step kernel if run.backend is "numba" and it covers the engine's monitoring, else None.

It covers N310 = N311 = 1 without the monitoring load option (protocol.lm_load); other settings and debug mode use the NumPy path.

handoverlab.engine.procedures

Handover procedures: baseline, conditional, DAPS handover and L1/L2-triggered mobility.

A procedure holds the per-user state of one handover family and implements the protocol around the decisions of a rule. The engine calls it through a small interface (Procedure), so further procedures (for example L1/L2-triggered mobility) can be added without touching the engine.

Baseline handover (TR 36.839 failure model) Event A3 (offset per user and neighbour) held for the time-to-trigger -> measurement report -> preparation (prep_s) -> handover command. A command that arrives while T310 runs is received once the link reaches Qout (protocol.cmd_during_t310: receive, the default since 2.0.0; with lost it is lost, "too late"). The UE leaves the source, is interrupted for exec_s and joins the target when its SINR is at least Qout while T304 runs.

Conditional handover (TS 38.300 clause 9.2.3.4) The UE reports a neighbour once it passes the reporting offset (first report at once, then every report_interval_s). At a report the network prepares the neighbours whose advantage reaches the preparation offset, at most n_max (best first), and releases prepared cells that fell release_hys_db below it. The conditional configuration reaches the UE after prep_s (while T310 runs, as a command). The UE evaluates the execution condition of every prepared cell at each measurement and executes on its own when the time-to-trigger expires (since 2.1.0 also between two measurement instants). After an execution the other prepared cells are released; after a failure the UE recovers into a prepared cell it selects (cho_recovery_s; since 2.1.0 with T304, TS 38.331 5.3.7.3).

DAPS handover (TS 38.300 clause 9.2.3.2, TS 38.331 clause 5.3.5.8.3) Triggered and prepared as a baseline handover. During the execution the source link keeps carrying data (the user-plane interruption is interruption_s of the rule instead of exec_s) and is monitored (since 2.1.0: T310 is not stopped, a source RLF during T304 releases it). If the target cannot be joined before T304 expires and no source RLF was detected, the UE falls back to the source: the attempt ends as too_early with the cause daps_fallback and no re-establishment follows. Not from FR2 to FR2 and not for NTN (TS 38.300).

L1/L2-triggered mobility (TS 38.300 clause 9.2.3.5) Candidate cells are configured as the prepared cells of a conditional handover (L3 reports, preparation offset, n_max, release). The network evaluates the unfiltered L1 measurements: when a configured candidate is better than the serving cell by the switch offset for ttt_switch_s, it sends a cell switch command (MAC CE), which reaches the UE after cmd_delay_s (while T310 runs, as a baseline command). The execution takes exec_s of the rule (RACH-less, with early timing advance acquisition; 20 ms more between FR1 and FR2 since 2.1.0). The other candidates stay configured after a switch (TS 38.331 5.3.5.18.6).

Procedure

Interface between the engine and a handover family.

exec_time(u) -> float

Duration of an execution of user u [s].

exec_interruption(u, duration) -> float

User-plane interruption of an execution of the given duration [s].

step(t)

Called every step after radio-link monitoring (deliver commands or configurations).

measure(obs, counted)

Called at every measurement instant with the filtered measurements.

flight_info(u, tgt)

(memory key, prepared-set size, execution offset) of an attempt that starts at execution.

cancel_command(u)

A pending handover command of user u is void.

on_attach(u)

User u is connected to a new serving cell.

on_failure(u)

User u had a failure and starts re-establishment.

clear(u, used_cell, t)

Drop all preparations of user u (used_cell is the cell it went to, or -1).

after_execution(u, tgt, t)

A successful execution of user u towards tgt (since 2.1.0; by default clear).

User u has a radio-link failure while no attempt is in flight (called first).

recovers_into(u, cell) -> bool

True if a failed UE that selects cell recovers into a prepared cell.

ConditionalProcedure

Bases: Procedure

after_execution(u, tgt, t)

A successful execution towards tgt: release the configurations (the conditional ones are removed after a successful reconfiguration with sync, TS 38.331 5.3.5.3).

DAPSProcedure

Bases: BaselineProcedure

DAPS handover: a baseline handover whose source link stays up during the execution.

LTMProcedure

Bases: ConditionalProcedure

L1/L2-triggered mobility: candidates configured on L3 reports, cell switch on L1 reports.

after_execution(u, tgt, t)

TS 38.331 5.3.5.18.6 keeps the ltm-Config across an LTM cell switch: the other candidates stay configured; only the new serving cell leaves the candidate set (since 2.1.0; 2.0.0: all released).

CLTMProcedure

Bases: LTMProcedure

Conditional LTM: the UE executes a configured candidate when the L1 switch condition holds.

receive_during_t310(eng, u, wait, t) -> bool

A command that reaches user u while T310 runs, with protocol.cmd_during_t310 = "receive".

TS 38.331 5.3.5.5.2: the UE executes a received reconfiguration with sync and stops T310. The command is received at the first step with link-monitoring SINR >= Qout (the 10 % BLER reference of TS 38.133 8.1; HARQ and RLC retransmissions go on while T310 runs). Returns True while the command waits, and the caller then does nothing; after reception (T310 stopped) or with the default "lost" it returns False and the caller acts as before.

handoverlab.engine.batch

Several engines on one world, advanced by one step loop.

All rules of a job run on the same radio samples. EngineBatch stacks the per-user state of the radio-link monitoring of K engines into arrays of shape (K, U); each engine keeps views of its own row, so all of its event handling (executions, failures, procedures, measurement instants, outcome labels) runs unchanged. Per step, one set of array operations then serves all K engines:

  • serving SINR of every user of every engine;
  • the Qout / Qin filters, the N310 / T310 logic and the expiry of T310;
  • the check for ends of executions and re-establishments.

Only the interference sum of each engine (ln @ rc, a matrix-vector product per engine, as in Engine) and the work at events stay per engine. The interference weight of the serving cells changes only at measurement instants and when a user changes cell, so it is gathered only then.

Every operation is element by element on the same values and in the same order as in a single engine, so results are bit-identical; the golden event-log fingerprints and the regression tests check this. Engines in debug mode are not batched.

EngineBatch

Advance the engines of all rules of one world together (see the module text).

advance(ch) -> None

Simulate the steps of one radio chunk for every engine.

handoverlab.engine.metrics

Counters of a run and the metric catalogue.

Rates are given per UE-minute of the measured part of the run (after the warm-up), with the suffix _pm. Counts start with n_.

Counters

Bases: dict

Integer counters of a run; every name starts at zero.

summarize(C: Counters, acc: dict, ue_min: float, ue_s: float, n_users: int, step_s: float, open_preparations: int, n_sites: int = 1, l1_per_meas: int = 1, power=(0.0, 0.0)) -> Dict[str, float]

Metric dictionary of a finished run (see METRICS).