diff --git a/docs/source/devices/devices.rst b/docs/source/devices/devices.rst index 228f03f416667ec26ecbadd18270cac67dc19970..44846f540934c1ebcbb969cc33612a9e9274c632 100644 --- a/docs/source/devices/devices.rst +++ b/docs/source/devices/devices.rst @@ -14,5 +14,140 @@ Typically, ``N_RCUs == 32``, and ``N_Antennas_per_RCU == 3``. SST and XST ----------- -The ``sst == Device("LTS/SST/1")`` and ``xst == Device("LTS/XST/1")`` devices manages the SSTs (subband statistics) and XSTs (crosslet statistics), respectively, that are emitted by the Uniboards in SDP. By default, each device configures the statistics to be streamed to itself (the device), from where the user can obtain them. +The ``sst == Device("LTS/SST/1")`` and ``xst == Device("LTS/XST/1")`` devices manages the SSTs (subband statistics) and XSTs (crosslet statistics), respectively. The statistics are emitted piece-wise through UDP packets by the FPGAs on the Uniboards in SDP. By default, each device configures the statistics to be streamed to itself (the device), from where the user can obtain them. + +The statistics are exposed in two ways: as attributes, representing the most recently received values, and as a TCP stream. + +SST Statistics attributes +````````````````````````` + +The SSTs represent the amplitude of the signal in each subband, for each antenna, as an integer value. They are exposed through the following attributes: + +:sst_R: Amplitude of each subband, from each antenna. + + :type: ``uint64[N_ant][N_subbands]`` + +:sst_timestamp_R: Timestamp of the data, per antenna. + + :type: ``uint64[N_ant]`` + +:integration_interval_R: Timespan over which the SSTs were integrated, per antenna. + + :type: ``float32[N_ant]`` + +:subbands_calibrated_R: Whether the subband data was calibrated using the subband weights. + + :type: ``bool[N_ant]`` + +Typically, ``N_ant == 192``, and ``N_subbands == 512``. + +XST Statistics attributes +````````````````````````` + +The XSTs represent the cross-correlations between each pair of antennas, as complex values. The phases and amplitudes of the XSTs represent the phase and amplitude difference between the antennas, respectively. They are exposed as a matrix ``xst[a][b]``, of which only the triangle ``a<=b`` is filled, as the cross-correlation between antenna pairs ``(b,a)`` is equal to the complex conjugate of the cross-correlation of ``(a,b)``. The other triangle contains incidental values, but will be mostly 0. + +Complex values which cannot be represented in Tango attributes. Instead, the XST matrix is exposed as both their carthesian and polar parts: + +:xst_power_R, xst_phase_R: Amplitude and phase of the crosslet statistics. + + :type: ``float32[N_ant][N_ant]`` + +:xst_real_R, xst_imag_R: Real and imaginary parts of the crosslet statistics. + + :type: ``float32[N_ant][N_ant]`` + +:xst_blocks_R: Blocks of crosslet statistics, as received from the FPGA (see below). + + :type: ``int64[N_blocks][block_size]`` + +:xst_timestamp_R: Timestamp of each block. + + :type: ``int64[N_blocks]`` + +:integration_interval_R: Timespan over which the XSTs were integrated, for each block. + + :type: ``float32[N_blocks]`` + +Typically, ``N_ant == 192``. + +The metadata refers to the *blocks*, which are emitted by the FPGAs to represent the XSTs between 12 x 12 antennas. The following code converts block numbers to the indices of the first antenna pair in a block:: + + from common.baselines import baseline_from_index + + def first_antenna_pair(block_nr: int) -> int: + coarse_a, coarse_b = baseline_from_index(block_nr) + return (coarse_a * 12, coarse_b * 12) + +Conversely, to calculate the block index for an antenna pair ``(a,b)``, use:: + + from common.baselines import baseline_index + + def block_nr(a: int, b: int) -> int: + return baseline_index(a / 12, b / 12) + +The ``block_size`` is equal to the number of antennas, times two for the real and imaginary parts (stored consecutively), so ``12*12*2``. + +TCP stream +`````````` + +The TCP stream interface allows a user to subscribe to the statistics packet streams, combined into a single TCP stream. To do so, simply connect to the following port: + ++----------+----------------+ +| Device | TCP end point | ++==========+================+ +| SST | localhost:5101 | ++----------+----------------+ +| XST | localhost:5102 | ++----------+----------------+ + +The easiest way to capture this stream is to use our ``statistics_writer``, which will capture the statistics and store them in HDF5 file(s): the writer recognised packet boundaries, converts the packets into their respective matrix, and store a matrix for each received timestamp. Furthermore, packet header information is periodically recorded as HDF5 attributes:: + + cd devices/statistics_writer + python3 statistics_writer.py --mode SST --host localhost + +The correct port will automatically be chosen, depending on the given mode. See also ``statistics_writer.py -h`` for more information.a + +The writer can also parse a statistics stream stored in a file, so the stream can be captured and processed later using the writer. Capturing the stream could be done using ``netcat``:: + + nc localhost 5101 > SST-packets.bin + +Performance monitoring +````````````````````````` + +All statistics expose attributes that provide general statistics about the data received. The counters are set to 0 when the device is initialised: + +:last_packet_timestamp_R: Timestamp of the last received packets. + + :type: ``uint64`` + +:nof_packets_received_R: Number of UDP packets received. + + :type: ``uint64`` + +:nof_bytes_received_R: Number of bytes received (sum of UDP payload sizes). + + :type: ``uint64`` + +:nof_packets_dropped_R: Number of packets dropped because processing was overloaded. + + :type: ``uint64`` + +:nof_invalid_packets_R: Number of packets dropped because their header is invalid. + + :type: ``uint64`` + +:nof_payload_errors_R: Number of packets marked as having an invalid payload, per FPGA. + + :type: ``uint64[N_fpgas]`` + +:nof_valid_payloads_R: Number of valid packets with valid payloads received, per FPGA. + + :type: ``uint64[N_fpgas]`` + +Note that nominally:: + + nof_packets_received_R = nof_packets_dropped_R + + nof_invalid_packets_R + + sum(nof_payload_errors_R) + + sum(nof_valid_payloads_R)