This document gives some details on how FRIBDAQ SRS readout works. The version of FRIBDAQ with the SRS readout considered here is the same as the latest used during the S800 commissioning, 12.1-001 in FRIB bullseye container, see also [this branch](https://github.com/FRIBDAQ/NSCLDAQ/tree/12.1-srs). The SRS readout program requires to have the SRS slow control software with special features, see below. In a nutshell, FRIBDAQ SRS readout program commands the SRS slow control software and reads data from the SRS daq port (udp) via an udp broker. The slow control is the interface with the SRS hardware, from that e.g. trigger and clock modes, TAC slope, gain, etc. can be set. The udp broker binds to the daq port and processes datagrams, segmenting them into individual hits and attributing a timestamp to each hit. Then, hits with the same timestamp (+/- one timestamp unit) are grouped together and written to a single ringitem which is put into a ringbuffer. One can decide to use the raw ringbuffers, one per FEC, or pass them to the FRIBDAQ event builder for time sorting between FEC. How the readout is organized: ```mermaid flowchart LR subgraph 1[FRIBDAQ Readout app.] id1["Interfaced by ReadoutGUI
> Tcp client
> UdpBroker control commands"]; end subgraph 2[Slow control] id2["> SRS hardware interface
> Tcp server"]; end subgraph 3[FRIBDAQ upd broker] id3["> Listen to SRS daq port
> Parse udp datagram
> Define hit
> send single hit to sorter"]; end subgraph 4[FRIBDAQ sorter] id4["> Group in time hits
> Do sanity checks
> Write grouped hits into ring buffer"]; end subgraph 5[FRIBDAQ pipeline] id5((fec10)); id6((fec...)); id7["> Sort grouped hits from different FEC
> Write built events into ring buffer"]; id8((fecB)); end style id1 text-align:left style id2 text-align:left style id3 text-align:left style id5 text-align:left style id6 text-align:left id1 <-. tcp .-> id2 id1 <--> id3 id3 --> id4 id4 --> id5 id4 --> id6 id5 --> id7 id6 --> id7 id7 --> id8 id5 -.-> idFree["Other analysis"] id6 -.-> idFree["Other analysis"] id8 --> Analysis ``` Details on the different parts of the readout: - [Readout application](#readout-app) - [Slow control](#slow-control) - [Udp broker](#udp-broker) - [Hit sorter](#hit-sorter)
# Readout application
The `CReadoutAppSRS` class is derived from the base class `CReadoutMain` that is the application class for readout. Other than `SetupReadout`, member functions are not developped yet. `SetupReadout` was overrided to read source parameters defined in ".settings.tcl" file. It looks for a parameter called `configFile` set to the absolute path of the configuration file for SRS slow control. That is a way to make sure the slow control and the FRIBDAQ SRS readout are initialized with the same parameters. A dummy event trigger for SRS is established in `SetupReadout`, every three seconds it is emitting a trigger. Since we are streaming udp datagrams, the definition of an event/trigger is tricky and I (Simon) didn't define what should be the trigger. An event segment is created in `SetupReadout`, from the class `CEventSegmentSRS` derived from base class `CEventSegment`. `CEventSegmentSRS` does the interface between the slow control and the udp broker class. The `CEventSegmentSRS::configure` function will initiate a tcp client connection with the slow control tcp server. This tcp connection allows to communicate e.g. commands and status with the slow control. Thus, the address of the configuration file is provided to the slow control and its initialization is requested. In return, the slow control provides the FEC ids connected, the trigger mode, the clock mode and period. Still in `configure` function, the udp broker class `UDPBrokerDerived` is started on a thread because in its core udp datagrams are read within a while loop. Ring buffers are created via `UDPBrokerDerived`, one for each FEC ids returned by the slow control, the names of those rings are fixed to "fecNN" where NN is 10+fecID. The `CEventSegmentSRS::onBegin` function will tell the slow control to start sending data on udp daq port and udp datagram reading is enabled by `UDPBrokerDerived::begin` (ref. Udp broker section). The `CEventSegmentSRS::onEnd` function will tell the slow control to stop sending data on udp daq port and udp datagram reading is suspended by `UDPBrokerDerived::end` (ref. Udp broker section). The `CEventSegmentSRS::onPause` and `CEventSegmentSRS::onResume` functions do not affect the slow control acquisition but pause/resume the udp datagram reading by `UDPBrokerDerived::pause` and `UDPBrokerDerived::resume` (ref. Udp broker section). `CEventSegmentSRS::parseResponse` function interprets the slow control tcp server response to CEventSegmentSRS commands. [Go to top](#toTop) # Slow control
The original version was developed with the support of the RD51 collaboration at CERN. Modifications have been added to enable control of the slow control system by external software, and allow to use external trigger mode. This is done via a TCP server/client connection, where the TCP server is in the slow control. For that, a class `tcpServer` has been implemented and `daq_window` has another function `externalCmd` to excecute callbacks corresponding to the commands received by `tcpServer`. During excecution of the slow control paired with FRIBDAQ SRS Readout, all the buttons and functionnalities are still enabled, but you will need to go through the initialization (`Start` of ReadoutGUI) with an updated configuration file if one of the following parameters needs to be changed : fecIds, triggerIn, triggerInInv, extClock,clockPeriod. One of the reasons is that a ring buffer is created for each active FEC at initialization. [Go to top](#toTop) # Udp broker
The `UDPBrokerDerived` class used in FRIBDAQ SRS Readout is derived from `UDPBrokerBase` and is specific to SRS udp datagram reading. The initialization of `UDPBrokerDerived` happens within `CEventSegmentSRS::configure`, the SRS daq port is passed and the channels map is constructed. Note, there is hardcoded maps for S800 DC but one can also define a map with a text file. The function `UDPBrokerDerived::addSink` creates a ring buffer for each active FEC. The source id is SId = FEC number + 10, the resulting number is also used to name the ring buffer (fecSId). The `run` function called in `CEventSegmentSRS::configure` is `UDPBrokerBase::run`, which binds a udp socket to the SRS daq port and runs the `mainLoop` function. `UDPBrokerDerived::mainLoop` is basically a while loop that can be skipped according to flags (e.g. to pause or stop a run). The fecId is extracted from the datagram header and passed to `UDPBrokerDerived::makeRingItems` function along with the datagram and the ring buffer to fill. `UDPBrokerDerived::makeRingItems` is also a while loop that iterates over all srs data segments (hits and markers) contained in the received datagram. The purpose is to send, to a time sorting class `SRSSorter`, hit timestamp followed by hits that have been assigned hit markers and mapped channels. The size of the original srs data segment is 6 bytes, to which is added 4 bytes of hit marker and 2 bytes of mapped channel, in total each hit is 12 bytes. The data parsing within the 'makeRingItems' while loop is done by `UDPBrokerDerived::getExtraData`. In the latter, by default, the hit timestamp is set to the fine value `fineTS = markerSRS[idx].fecTimestamp + triggerOffset*4096 + bcid`, or to `fineTS = markerSRS[idx].fecTimestamp` in external trigger mode. `UDPBrokerDerived::begin`, `end`, `pause` and `resume` functions are similar. Counters and flags are reset, and a state change item is sent to all ringbuffers. [Go to top](#toTop) # Hit sorter
The `SRSSorter` class expects that hits from the same FEC are already time ordered. Although its name, rather than sorting, this class groups hits according to their timestamp, do sanity checks and write data into ring buffer. The current sanity checks are: - put into ring buffer an item that has a timestamp equal or greater than the previous item. - delete ring item that has too many hits (exceeding a certain limit, currently 960), not written to ring buffer. The ring item body should be a succession of 12 bytes hits. A ring item with an empty body is written to the ring buffer if no hit data follow a trigger marker. [Go to top](#toTop)