[[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 (<>) 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 (<>). 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 (<>). * `TxPriorityQueueServiceLoop`: Blocks reading from one ARFCN specific TRX Manager UDP socket (<>), 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 <> 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 <>.