diff --git a/doc/manuals/chapters/code-architecture.adoc b/doc/manuals/chapters/code-architecture.adoc new file mode 100644 index 00000000..18d0e3ab --- /dev/null +++ b/doc/manuals/chapters/code-architecture.adoc @@ -0,0 +1,141 @@ +[[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 <>. diff --git a/doc/manuals/osmotrx-usermanual.adoc b/doc/manuals/osmotrx-usermanual.adoc index a713e4a6..14f55148 100644 --- a/doc/manuals/osmotrx-usermanual.adoc +++ b/doc/manuals/osmotrx-usermanual.adoc @@ -29,6 +29,8 @@ include::chapters/trx-devices.adoc[] include::chapters/trx-backends.adoc[] +include::chapters/code-architecture.adoc[] + include::../common/chapters/trx_if.adoc[] include::../common/chapters/port_numbers.adoc[]