| [[code_architecture]] |
| == Code Architecture |
| |
| [[fig-code-architecture-general]] |
| .General overview of main OsmoTRX components |
| [graphviz] |
| ---- |
| digraph hierarchy { |
| node[shape=record,style=filled,fillcolor=gray95] |
| edge[dir=back, arrowtail=empty] |
| |
| 2[label = "{Transceiver|+ constructor()\l+ destructor()\l+ init()\l+ numChans()\l+ receiveFIFO()\l+ setSignalHandler()}"] |
| 3[label = "{RadioInterface|...}"] |
| 4[label = "{RadioInterfaceResamp|...}"] |
| 5[label = "{RadioInterfaceMulti|...}"] |
| 6[label = "{RadioDevice|...}"] |
| 7[label = "{UHDDevice|...}"] |
| 8[label = "{LMSDevice|...}"] |
| 9[label = "{USRPDevice|...}"] |
| |
| 2->3[arrowtail=odiamond] |
| 3->4[constraint=false] |
| 3->5[constraint=false] |
| 3->6[arrowtail=odiamond] |
| 6->7 |
| 6->8 |
| 6->9 |
| } |
| ---- |
| |
| [[fig-code-architecture-threads]] |
| .Example of thread architecture with OsmoTRX configured to use 2 logical RF channels (Trx=Transceiver, RI=RadioIface) |
| [graphviz] |
| ---- |
| digraph hierarchy { |
| node[shape=record,style=filled,fillcolor=gray95] |
| |
| trans [label="Transceiver"]; |
| radioiface [label="RadioInterface"]; |
| radiodev [label="RadioDevice"]; |
| |
| trans:nw->trans:ne [label="Trx.ControlServiceLoop_0"]; |
| trans:nw->trans:ne [label="Trx.ControlServiceLoop_1"]; |
| trans:w->radioiface:w [label="Trx.TxPriorityQueueServiceLoop_0"]; |
| trans:w->radioiface:w [label="Trx.TxPriorityQueueServiceLoop_1"]; |
| radioiface:e->trans:e [label="Trx.RxServiceLoop_0"]; |
| radioiface:e->trans:e [label="Trx.RxServiceLoop_1"]; |
| radioiface->radiodev[label="RI.AlignRadioServiceLoop"]; |
| radioiface:sw->radiodev:nw [label="Trx.TxLowerLoop"]; |
| radiodev:ne->radioiface:se [label="Trx.RxLowerLoop"]; |
| } |
| ---- |
| |
| [[code_component_transceiver]] |
| === Transceiver |
| |
| The Transceiver is the main component managing the other components running in |
| the OsmoTRX process. There's a unique instance per process. |
| |
| This class is quite complex from code point of view, as it starts lots of |
| different threads and hence the interaction with this class from the outside is |
| quite limited. Only interaction possible is to: |
| |
| * `Transceiver()`: Create an instance through its constructor, at this time most |
| configuration is handed to it. |
| * `init()`: Start running all the threads. |
| * `receiveFIFO()`: Attach a `radioInterface` channel FIFO in order to use it. |
| * `setSignalHandler()`: Used to set up a callback to receive certain events |
| asynchronously from the Transceiver. No assumptions can be made about from |
| which thread is the callback being called, which means multi-thread locking |
| precautions may be required in certain cases, similar to usual signal handler |
| processing. One important event received through this path is for instance |
| when the Transceiver detected a fatal error which requires it to stop. Since |
| it cannot stop itself (see destructor below), stopping procedure must be |
| delegated to the user who created the instance. |
| * `~Transceiver()`: The destructor, which stops all running threads created at |
| `init()` time. Destroying the object is the only way to stop the `Transceiver` |
| completely, and must be called from a thread not managed by the |
| `Transceiver`, otherwise it will deadlock. Usually it is stopped from the main |
| thread, the one that called the constructor during startup. |
| |
| During `init()` time, `Transceiver` will create a noticeable amount of threads, |
| which may vary depending on the amount of RF channels requested. |
| |
| Static amount of Threads (1 per `Transceiver` instance): |
| |
| * `RxLowerLoop`: This thread is responsible for reading bursts from the |
| `RadioInterface`, storing them into its FIFO and sending Clock Indications |
| (<<trx_if_clock_ind>>) to _osmo-bts_trx_. |
| * `TxLowerLoop`: Manages pushing bursts from buffers in the FIFO into the |
| `RadioInterface` at expected correct time based on the Transceiver clock. |
| |
| Dynamic amount of Threads (1 per RF logical channel on the `Transceiver` instance): |
| |
| * `ControlServiceLoop`: Handles commands from the Per-ARFCN Control Interface |
| socket (<<trx_if_control>>). Each thread is responsible for managing one |
| socket related to one ARFCN or which is the same, to one RF logical channel. |
| These are the only threads expected to use the private `start()` and `stop()` |
| methods of the `Transceiver()` class, since those methods don't stop any of |
| the `ControlServiceLoop` threads as they must keep running to handle new |
| commands (for instance, to re-start processing samples with the _POWERON_ |
| command). |
| * `RxServiceLoop`: Each thread of this type pulls bursts from the |
| `RadioInterface` FIFO for one specific logical RF channel and handles it |
| according to the slot and burst correlation type, finally sending proper data |
| over the TRX Manager UDP socket (<<trx_if>>). |
| * `TxPriorityQueueServiceLoop`: Blocks reading from one ARFCN specific TRX |
| Manager UDP socket (<<trx_if>>), and fills the `RadioInterface` with it |
| setting clock related information. |
| |
| [[code_component_radioiface]] |
| === RadioInterface |
| |
| The `RadioInterface` sits between the `Transceiver` and the `RadioDevice`, and |
| provides extra features to the pipe like channelizers, resamplers, Tx/Rx |
| synchronization on some devices, etc. |
| |
| If the `RadioDevice` it drives requires it (only _USRP1_ so far), the |
| `RadioIntercace` will start and manage a thread internally called |
| `AlignRadioServiceLoop` which will align current RX and TX timestamps. |
| |
| Different features are offered through different `RadioInterface` subclasses |
| which are selected based on configuration and device detected at runtime. Using |
| these features may impact on the amount of CPU required to run the entire pipe. |
| |
| ==== RadioInterfaceResamp |
| |
| This subclass of `RadioInterface` is automatically selected when some known |
| specific UHD are to be used, since they require resampling to work properly. |
| Some of this devices are for instance Ettus B100, USRP2 and X3XX models. |
| |
| ==== RadioInterfaceMulti |
| |
| This subclass of `RadioInterface` is used when <<multiarfcn_mode>> is requested. |
| |
| [[code_component_radiodev]] |
| === RadioDevice |
| |
| The `RadioDevice` class is responsible for driving the actual Hardware device. |
| It is actually only an interface, and it is implemented in each backend which in |
| turn becomes a specific OsmoTRX binary, see <<trx_backends>>. |