|
libe3 0.2.5
C++ library for E3AP RAN vendor integration
|
The dApp control loop as it runs over the E3 interface alone, with no RIC and no E2 involvement.
Read latrec.md first for the recorder, the clock model, and the stage-catalog conventions.
A19a and A19b fork from A18, they are not a sequence — see Two distinct terminations below. The dotted edges are cross-boundary hops (the two E3 wire crossings, and the acknowledgment tail running back from RAN to the dApp), not pipeline boxes.
E3SM and E3AP are always separate boxes. The Service Model codec runs in the RAN stack or in the dApp; the E3AP codec runs inside libE3. They are never collapsed into one "encode" stage, on either leg, in either direction. Keeping them apart is what makes the library's own cost separable from the integration's.
A1 is not IQ-specific. "E3 data recording" is whatever raw input a Service Model captures — IQ samples for a spectrum-sensing SM, but equally cell load, mobility statistics, or anything else an SM chooses to report.
The report branch terminates at the E2-E3 bridge, which is where Path B begins.
Segments name identifiers from the stage catalog in include/libe3/latrec.h. The catalog names operations, so the same identifier appears wherever that operation is performed; which component performed it is read off the ring that recorded it.
| # | Box | Owner | Segment |
|---|---|---|---|
| A1 | E3 data recording | the data source | RECORD_BEGIN to PROCESS_BEGIN |
| A2 | Processing | the Service Model | PROCESS_BEGIN to ENCODE_E3SM_BEGIN |
| A3 | Encode E3SM | the Service Model | ENCODE_E3SM_BEGIN to ENCODE_E3SM_DONE |
| A4 | Encode E3AP | libe3 | EMIT_ENTER to ENQUEUE, then DEQUEUE to ENCODE_E3AP_DONE |
| A5 | Queuing | libe3 outbound queue | ENQUEUE to DEQUEUE |
| A6 | Delivery | libe3 connector send | ENCODE_E3AP_DONE to SEND_DONE |
| — | E3 wire | — | SEND_DONE (RAN) to RECV (dApp) |
| A7 | Recv | libe3 (dApp role) | RECV |
| A8 | Decode E3AP | libe3 | RECV to DECODE_E3AP_DONE |
| — | libe3 dispatch | libe3 (dApp role) | DECODE_E3AP_DONE to DELIVER_BEGIN |
| A9 | Decode E3SM | dApp application | DELIVER_BEGIN to DECODE_E3SM_DONE |
| A10 | Process | dApp application | DECODE_E3SM_DONE to PROCESS_DONE |
DELIVER_BEGIN and DELIVER_DONE bracket A9 and A10 together as libe3 sees them: the library's inbound filter passed, and the application's handler returned.
SEND_DONE marks the return of the connector's send(). Over ZeroMQ PUB that is a copy into ZeroMQ's own queue, not a socket write: the wire transfer happens later on ZeroMQ's internal I/O thread. The SEND_DONE to RECV segment therefore contains that hop for ZeroMQ but not for the POSIX connectors, and any comparison between the two must say so.
| # | Box | Owner | Segment |
|---|---|---|---|
| A11 | Create control / create report | dApp application | CREATE_OUTPUT |
| A12 | Encode E3SM | dApp application | ENCODE_E3SM_BEGIN to ENCODE_E3SM_DONE |
| A13 | Encode E3AP | libe3 (dApp role) | EMIT_ENTER to ENQUEUE, then DEQUEUE to ENCODE_E3AP_DONE |
| A14 | Queuing | libe3 | ENQUEUE to DEQUEUE |
| A15 | Delivery | libe3 | ENCODE_E3AP_DONE to SEND_DONE |
| — | E3 wire | — | SEND_DONE (dApp) to RECV (RAN) |
| A16 | Recv | libe3 (RAN role) | RECV |
| A17 | Decode E3AP | libe3 | RECV to DECODE_E3AP_DONE |
| — | libe3 dispatch | libe3 (RAN role) | DECODE_E3AP_DONE to DECODE_E3SM_BEGIN |
| A18 | Decode E3SM | RAN Service Model | DECODE_E3SM_BEGIN to DECODE_E3SM_DONE |
| A19a | Apply control | the RAN stack | DECODE_E3SM_DONE to APPLY_CONTROL_DONE, then LIVE_ON_AIR |
| A19b | Queue report | libe3 report worker | REPORT_QUEUED to REPORT_DONE |
| A20 | E2-E3 bridge | the RAN's dApp function | BRIDGE_IN to BRIDGE_OUT. Hands over to Path B. |
| — | Acknowledgment tail | RAN and dApp | ACK_SENT to ACK_RECV |
A12 and A3 share one identifier pair, and A18 and A9 share another: encoding a Service Model payload is the same operation whichever direction it travels in, and aux2 carries the PDU type that separates an indication from a control or a report.
At A11 a report is also assigned its E3-SequenceID, which is mandatory on E3-DAppReport. On a deployment with no xApp it is simply carried and ignored; where an xApp exists it becomes the identifier for the whole procedure that follows, so a Path A report is where a Path B loop's chain begins — see path-b-e2-e3-loop.md. A control the dApp decided on its own carries no sequenceId; one re-issued on an xApp's behalf carries that procedure's.
A19a (apply control) and A19b (queue report) are alternative endings, not a sequence. A control action terminates on the air at LIVE_ON_AIR; a dApp report terminates by being queued for the report worker and, if the deployment has an xApp, handed to the E2-E3 bridge at A20. The two must be reported as separate legs and never summed.
APPLY_CONTROL_DONE and LIVE_ON_AIR are also distinct: the first is the control handler installing the change, the second is the first scheduler tick that actually put it on air. They run on different threads, and a tick can land between them.
| Key | Span | Meaning |
|---|---|---|
| total_ind_up | A1 to A10 | input available to dApp decision made |
| total_libe3_up | A4 to A8 | the library's share of the forward leg |
| total_ctrl_down | A11 to A19a | dApp decision to control live on the air |
| total_libe3_down | A13 to A17 | the library's share of the return leg |
| total_report_up | A11 to A20 | dApp report built to handed to the bridge |
| total_e2e | A1 to A19a | the headline number: input to control on the air |
total_libe3_up and total_libe3_down are the two spans that are a property of libE3 itself rather than of the integrating stack.
The loop shape is fixed; who owns each box is not. Some illustrative deployments:
| Deployment | A1 to A3 owner | A9 to A12 owner (dApp) | A18 to A19a owner | Notes |
|---|---|---|---|---|
| Simple SM | the Simple Service Model shipped in examples/sm_simple, self-paced | the same Simple SM on the dApp side | the same Simple SM | Both roles in one process for the coarse benchmark, two processes for a ring capture. The variant where link / transport / encoding can be swept freely. |
| RAN stack | a gNB's PHY plus its reporting Service Models | a dApp framework | the RAN's Service Model to its scheduler | The variant that reaches the air, so LIVE_ON_AIR is meaningful. |
| Split capture pipeline | a capture hook feeding a separate controller process | the dApp under test | not necessarily wired | A1 to A3 span two processes joined through shared memory. |
| Accelerated L1 | an L1 data lake feeding an accelerated dApp's E3 manager | accelerated CPU and GPU dApps | not necessarily wired | Can be measured with and without libE3 framing to compare the library against a hand-rolled integration over the same stage set. |
Where a dApp already frames E3AP by hand, the libE3-backed path can be built as a second backend selected at build time, so the same binary shape is measurable both ways over the same stage set. Comparing the two at A4 to A8 and A13 to A17 measures directly what the library costs relative to the hand-rolled version.