LCOV - code coverage report
Current view: top level - fddetdataformats/unittest - WIB2Frame_test.cxx (source / functions) Coverage Total Hit
Test: code.result Lines: 39.3 % 191 75
Test Date: 2025-12-21 13:07:08 Functions: 53.8 % 13 7

            Line data    Source code
       1              : /**
       2              :  * @file WIB2Frame_test.cxx WIB2Frame class Unit Tests
       3              :  *
       4              :  * This is part of the DUNE DAQ Application Framework, copyright 2020.
       5              :  * Licensing/copyright details are in the COPYING file that you should have
       6              :  * received with this code.
       7              :  */
       8              : 
       9              : #include "fddetdataformats/WIB2Frame.hpp"
      10              : 
      11              : /**
      12              :  * @brief Name of this test module
      13              :  */
      14              : #define BOOST_TEST_MODULE WIB2Frame_test // NOLINT
      15              : 
      16              : #include "boost/test/data/test_case.hpp"
      17              : #include "boost/test/unit_test.hpp"
      18              : 
      19              : #include <cstdint>
      20              : #include <string>
      21              : #include <vector>
      22              : 
      23              : // namespace wib2unpack contains functions for manipulating WIB2
      24              : // frames in the format described by EDMS document 2088713:
      25              : // (https://edms.cern.ch/document/2088713/4). The code here is taken
      26              : // from unpack.h in the dune-wib-firmware repository, written by Ben
      27              : // Land, and lightly modified to be closer to DUNE DAQ software
      28              : // conventions. It is used here as an independently-written check of the
      29              : // overlay code in WIB2Frame.hpp
      30              : namespace wib2unpack {
      31              : 
      32              : // Words in the binary format of the FELIX frame14 from the WIB
      33              : typedef struct
      34              : {
      35              :   uint32_t start_frame;    // NOLINT(build/unsigned)
      36              :   uint32_t wib_pre[4];     // NOLINT(build/unsigned)
      37              :   uint32_t femb_a_seg[56]; // NOLINT(build/unsigned)
      38              :   uint32_t femb_b_seg[56]; // NOLINT(build/unsigned)
      39              :   uint32_t wib_post[2];    // NOLINT(build/unsigned)
      40              :   uint32_t idle_frame;     // NOLINT(build/unsigned)
      41              : } frame14;
      42              : 
      43              : // Samples from the U, V, X channels in a femb_*_seg of a frame as 16bit arrays
      44              : typedef struct
      45              : {
      46              :   uint16_t u[40], v[40], x[48]; // NOLINT(build/unsigned)
      47              : } __attribute__((packed)) femb_data;
      48              : 
      49              : // Byte-aligned unpacked values in a felix_frame
      50              : typedef struct
      51              : {
      52              :   uint8_t link_mask, femb_valid, fiber_num, wib_num, frame_version, crate_num; // NOLINT(build/unsigned)
      53              :   uint32_t wib_data;                                                           // NOLINT(build/unsigned)
      54              :   uint64_t timestamp;                                                          // NOLINT(build/unsigned)
      55              :   femb_data femb[2];
      56              :   uint32_t crc20;  // NOLINT(build/unsigned)
      57              :   uint16_t flex12; // NOLINT(build/unsigned)
      58              :   uint32_t flex24; // NOLINT(build/unsigned)
      59              : } frame14_unpacked;
      60              : 
      61              : // Deframed data, where channels or u,v,x are time ordered uint16 samples for each channel
      62              : typedef struct
      63              : {
      64              :   size_t samples;
      65              :   std::vector<uint16_t> channels[2][128]; // NOLINT(build/unsigned)
      66              :   std::vector<uint64_t> timestamp;        // NOLINT(build/unsigned)
      67              :   uint8_t crate_num, wib_num;             // NOLINT(build/unsigned)
      68              : } channel_data;
      69              : 
      70              : typedef struct
      71              : {
      72              :   size_t samples;
      73              :   std::vector<uint16_t> u[2][40];  // NOLINT(build/unsigned)
      74              :   std::vector<uint16_t> v[2][40];  // NOLINT(build/unsigned)
      75              :   std::vector<uint16_t> x[2][48];  // NOLINT(build/unsigned)
      76              :   std::vector<uint64_t> timestamp; // NOLINT(build/unsigned)
      77              :   uint8_t crate_num, wib_num;      // NOLINT(build/unsigned)
      78              : } uvx_data;
      79              : 
      80              : void
      81            0 : unpack14(const uint32_t* packed, uint16_t* unpacked) // NOLINT(build/unsigned)
      82              : {
      83            0 :   for (size_t i = 0; i < 128; i++) { // i == n'th U,V,X value
      84            0 :     const size_t low_bit = i * 14;
      85            0 :     const size_t low_word = low_bit / 32;
      86            0 :     const size_t high_bit = (i + 1) * 14 - 1;
      87            0 :     const size_t high_word = high_bit / 32;
      88              :     // printf("word %li :: low %li (%li[%li]) high %li
      89              :     // (%li[%li])\n",i,low_bit,low_word,low_bit%32,high_bit,high_word,high_bit%32);
      90            0 :     if (low_word == high_word) { // all the bits are in the same word
      91            0 :       unpacked[i] = (packed[low_word] >> (low_bit % 32)) & 0x3FFF;
      92              :     } else { // some of the bits are in the next word
      93            0 :       size_t high_off = high_word * 32 - low_bit;
      94              :       // printf("pre_mask 0x%X post_mask 0x%X\n", (0x3FFF >> (14-high_off)), ((0x3FFF << high_off) & 0x3FFF) );
      95            0 :       unpacked[i] = (packed[low_word] >> (low_bit % 32)) & (0x3FFF >> (14 - high_off));
      96            0 :       unpacked[i] |= (packed[high_word] << high_off) & ((0x3FFF << high_off) & 0x3FFF);
      97              :     }
      98              :   }
      99            0 : }
     100              : 
     101              : void
     102          200 : repack14(const uint16_t* unpacked, uint32_t* packed) // NOLINT(build/unsigned)
     103              : {
     104              :   // zero packed data first
     105        11400 :   for (size_t i = 0; i < 56; i++)
     106        11200 :     packed[i] = 0;
     107        25800 :   for (size_t i = 0; i < 128; i++) { // i == n'th U,V,X value
     108        25600 :     const size_t low_bit = i * 14;
     109        25600 :     const size_t low_word = low_bit / 32;
     110        25600 :     const size_t high_bit = (i + 1) * 14 - 1;
     111        25600 :     const size_t high_word = high_bit / 32;
     112              :     // printf("word %li :: low %li (%li[%li]) high %li
     113              :     // (%li[%li])\n",i,low_bit,low_word,low_bit%32,high_bit,high_word,high_bit%32);
     114        25600 :     if (low_word == high_word) { // all the bits are in the same word
     115        16000 :       packed[low_word] |= (unpacked[i] & 0x3FFF) << (low_bit % 32);
     116              :     } else { // some of the bits are in the next word
     117         9600 :       size_t high_off = high_word * 32 - low_bit;
     118              :       // printf("pre_mask 0x%X post_mask 0x%X\n", (0x3FFF >> (14-high_off)), ((0x3FFF << high_off) & 0x3FFF) );
     119         9600 :       packed[low_word] |= (unpacked[i] & (0x3FFF >> (14 - high_off))) << (low_bit % 32);
     120         9600 :       packed[high_word] |= (unpacked[i] & ((0x3FFF << high_off) & 0x3FFF)) >> high_off;
     121              :     }
     122              :   }
     123          200 : }
     124              : 
     125              : void
     126            0 : unpack_frame(const frame14* frame, frame14_unpacked* data)
     127              : {
     128            0 :   data->crate_num = frame->wib_pre[0] & 0xFF;
     129            0 :   data->frame_version = (frame->wib_pre[0] >> 8) & 0xF;
     130            0 :   data->wib_num = (frame->wib_pre[0] >> 12) & 0x7;
     131            0 :   data->fiber_num = (frame->wib_pre[0] >> 15) & 0x1;
     132            0 :   data->femb_valid = (frame->wib_pre[0] >> 16) & 0x3;
     133            0 :   data->link_mask = (frame->wib_pre[0] >> 18) & 0xFF;
     134              : 
     135            0 :   data->wib_data = frame->wib_pre[1];
     136              : 
     137            0 :   data->timestamp = (static_cast<uint64_t>(frame->wib_pre[3]) << 32) | // NOLINT(build/unsigned)
     138            0 :                     (static_cast<uint64_t>(frame->wib_pre[2]));        // NOLINT(build/unsigned)
     139              : 
     140            0 :   unpack14(frame->femb_a_seg, reinterpret_cast<uint16_t*>(&data->femb[0])); // NOLINT
     141            0 :   unpack14(frame->femb_b_seg, reinterpret_cast<uint16_t*>(&data->femb[1])); // NOLINT
     142              : 
     143            0 :   data->crc20 = frame->wib_post[0] & 0xFFFFF;
     144            0 :   data->flex12 = (frame->wib_post[0] >> 20) & 0xFFF;
     145            0 :   data->flex24 = (frame->wib_post[1] >> 8) & 0xFFFFFF;
     146            0 : }
     147              : 
     148              : void
     149          100 : repack_frame(frame14_unpacked* data, frame14* frame)
     150              : {
     151          100 :   memset(frame, 0, sizeof(frame14)); // zero out frame
     152          100 :   frame->start_frame = 0x3C;
     153          100 :   frame->wib_pre[0] |= (data->crate_num & 0xFF);
     154          100 :   frame->wib_pre[0] |= (data->frame_version & 0xF) << 8;
     155          100 :   frame->wib_pre[0] |= (data->wib_num & 0x7) << 12;
     156          100 :   frame->wib_pre[0] |= (data->fiber_num & 0x1) << 15;
     157          100 :   frame->wib_pre[0] |= (data->femb_valid & 0x3) << 16;
     158          100 :   frame->wib_pre[0] |= (data->link_mask & 0xFF) << 18;
     159              : 
     160          100 :   frame->wib_pre[1] = 0xbabeface;
     161              : 
     162          100 :   frame->wib_pre[2] = static_cast<uint32_t>(data->timestamp & 0xFFFFFFFF);         // NOLINT(build/unsigned)
     163          100 :   frame->wib_pre[3] = static_cast<uint32_t>((data->timestamp >> 32) & 0xFFFFFFFF); // NOLINT(build/unsigned)
     164              : 
     165          100 :   repack14(reinterpret_cast<uint16_t*>(&data->femb[0]), frame->femb_a_seg); // NOLINT
     166          100 :   repack14(reinterpret_cast<uint16_t*>(&data->femb[1]), frame->femb_b_seg); // NOLINT
     167              : 
     168          100 :   frame->wib_post[0] |= data->crc20 & 0xFFFFF; // FIXME calculate crc of something
     169          100 :   frame->wib_post[0] |= (data->flex12 & 0xFFF) << 20;
     170          100 :   frame->wib_post[1] |= 0xDC;
     171          100 :   frame->wib_post[1] |= (data->flex24 & 0xFFFFFF) << 8;
     172          100 :   frame->idle_frame = 0xBC;
     173          100 : }
     174              : 
     175              : size_t u_to_ch[40] = { 20, 59,  19, 60,  18, 61,  17, 62,  16, 63,  4,  43,  3,  44,  2,  45,  1,  46,  0,  47,
     176              :                        68, 107, 67, 108, 66, 109, 65, 110, 64, 111, 84, 123, 83, 124, 82, 125, 81, 126, 80, 127 };
     177              : size_t v_to_ch[40] = { 25, 54,  24, 55,  23, 56,  22, 57,  21, 58,  9,  38,  8,  39,  7,  40,  6,  41,  5,  42,
     178              :                        73, 102, 72, 103, 71, 104, 70, 105, 69, 106, 89, 118, 88, 119, 87, 120, 86, 121, 85, 122 };
     179              : size_t x_to_ch[48] = { 31, 48,  30, 49,  29, 50,  28, 51,  27, 52,  26, 53,  15, 32,  14, 33,
     180              :                        13, 34,  12, 35,  11, 36,  10, 37,  79, 96,  78, 97,  77, 98,  76, 99,
     181              :                        75, 100, 74, 101, 95, 112, 94, 113, 93, 114, 92, 115, 91, 116, 90, 117 };
     182              : 
     183              : void
     184            0 : deframe_data(const frame14* frame_buf, size_t nframes, channel_data& data)
     185              : {
     186            0 :   data.samples = nframes;
     187            0 :   for (size_t i = 0; i < 128; i++) {
     188            0 :     data.channels[0][i].resize(nframes);
     189            0 :     data.channels[1][i].resize(nframes);
     190              :   }
     191            0 :   data.timestamp.resize(nframes);
     192              :   frame14_unpacked frame_data;
     193            0 :   for (size_t i = 0; i < nframes; i++) {
     194            0 :     unpack_frame(frame_buf + i, &frame_data);
     195            0 :     for (size_t j = 0; j < 48; j++) {
     196            0 :       int k;
     197            0 :       if (j < 40) {
     198            0 :         k = u_to_ch[j];
     199            0 :         data.channels[0][k][i] = frame_data.femb[0].u[j];
     200            0 :         data.channels[1][k][i] = frame_data.femb[1].u[j];
     201            0 :         k = v_to_ch[j];
     202            0 :         data.channels[0][k][i] = frame_data.femb[0].v[j];
     203            0 :         data.channels[1][k][i] = frame_data.femb[1].v[j];
     204              :       }
     205            0 :       k = x_to_ch[j];
     206            0 :       data.channels[0][k][i] = frame_data.femb[0].x[j];
     207            0 :       data.channels[1][k][i] = frame_data.femb[1].x[j];
     208              :     }
     209            0 :     data.timestamp[i] = frame_data.timestamp;
     210            0 :     ;
     211              :   }
     212            0 :   data.crate_num = frame_data.crate_num;
     213            0 :   data.wib_num = frame_data.wib_num;
     214            0 : }
     215              : 
     216              : void
     217            0 : deframe_data(const frame14* frame_buf, size_t nframes, uvx_data& data)
     218              : {
     219            0 :   data.samples = nframes;
     220            0 :   for (size_t i = 0; i < 48; i++) {
     221            0 :     if (i < 40) {
     222            0 :       data.u[0][i].resize(nframes);
     223            0 :       data.v[0][i].resize(nframes);
     224            0 :       data.u[1][i].resize(nframes);
     225            0 :       data.v[1][i].resize(nframes);
     226              :     }
     227            0 :     data.x[0][i].resize(nframes);
     228            0 :     data.x[1][i].resize(nframes);
     229              :   }
     230            0 :   data.timestamp.resize(nframes);
     231              :   frame14_unpacked frame_data;
     232            0 :   for (size_t i = 0; i < nframes; i++) {
     233            0 :     unpack_frame(frame_buf + i, &frame_data);
     234            0 :     for (size_t j = 0; j < 48; j++) {
     235            0 :       if (j < 40) {
     236            0 :         data.u[0][j][i] = frame_data.femb[0].u[j];
     237            0 :         data.v[0][j][i] = frame_data.femb[0].v[j];
     238            0 :         data.u[1][j][i] = frame_data.femb[1].u[j];
     239            0 :         data.v[1][j][i] = frame_data.femb[1].v[j];
     240              :       }
     241            0 :       data.x[0][j][i] = frame_data.femb[0].x[j];
     242            0 :       data.x[1][j][i] = frame_data.femb[1].x[j];
     243              :     }
     244            0 :     data.timestamp[i] = frame_data.timestamp;
     245              :   }
     246            0 :   data.crate_num = frame_data.crate_num;
     247            0 :   data.wib_num = frame_data.wib_num;
     248              :   // FIXME frame_version femb_valid link_mask fiber_num
     249            0 : }
     250              : 
     251              : void
     252            0 : reframe_data(frame14* frame_buf, size_t nframes, const channel_data& data)
     253              : {
     254            0 :   frame14_unpacked frame_data;
     255            0 :   for (size_t i = 0; i < nframes; i++) {
     256            0 :     for (size_t j = 0; j < 48; j++) {
     257            0 :       int k;
     258            0 :       if (j < 40) {
     259            0 :         k = u_to_ch[j];
     260            0 :         frame_data.femb[0].u[j] = data.channels[0][k][i];
     261            0 :         frame_data.femb[1].u[j] = data.channels[1][k][i];
     262            0 :         k = v_to_ch[j];
     263            0 :         frame_data.femb[0].v[j] = data.channels[0][k][i];
     264            0 :         frame_data.femb[1].v[j] = data.channels[1][k][i];
     265              :       }
     266            0 :       k = x_to_ch[j];
     267            0 :       frame_data.femb[0].x[j] = data.channels[0][k][i];
     268            0 :       frame_data.femb[1].x[j] = data.channels[1][k][i];
     269              :     }
     270            0 :     frame_data.timestamp = data.timestamp[i];
     271            0 :     frame_data.crate_num = data.crate_num;
     272            0 :     frame_data.wib_num = data.wib_num;
     273              :     // FIXME frame_version femb_valid link_mask fiber_num
     274            0 :     repack_frame(&frame_data, frame_buf + i);
     275              :   }
     276            0 : }
     277              : 
     278              : void
     279            0 : fake_data(frame14* buffer, size_t nframes)
     280              : {
     281            0 :   channel_data data;
     282            0 :   data.samples = nframes;
     283            0 :   data.wib_num = 37;
     284            0 :   data.crate_num = 57;
     285            0 :   for (size_t i = 0; i < 128; i++) {
     286            0 :     data.channels[0][i].resize(nframes);
     287            0 :     data.channels[1][i].resize(nframes);
     288            0 :     int phase = rand() % 500; // NOLINT(runtime/threadsafe_fn)
     289            0 :     for (size_t j = 0; j < nframes; j++) {
     290            0 :       data.channels[0][i][j] = 16384.0 * (sin(2 * 3.1415926 * j / (i * 5 + 100.0) + phase) + 1.0) / 2.0;
     291            0 :       data.channels[1][i][j] = 16384.0 * (cos(2 * 3.1415926 * j / (i * 5 + 100.0) + phase) + 1.0) / 2.0;
     292              :     }
     293              :   }
     294            0 :   data.timestamp.resize(nframes);
     295            0 :   for (size_t i = 0; i < nframes; i++)
     296            0 :     data.timestamp[i] = i;
     297            0 :   reframe_data(buffer, nframes, data);
     298            0 : }
     299              : 
     300              : } // namespace wib2unpack
     301              : 
     302              : using namespace dunedaq::fddetdataformats;
     303              : 
     304              : typedef std::array<uint16_t, 256> vals_type; // NOLINT(build/unsigned)
     305              : 
     306              : // Tell boost not to try to print vals_type objects with <<
     307              : BOOST_TEST_DONT_PRINT_LOG_VALUE(vals_type)
     308              : 
     309              : BOOST_AUTO_TEST_SUITE(WIB2Frame_test)
     310              : 
     311              : std::vector<vals_type>
     312            1 : make_vals()
     313              : {
     314            1 :   std::vector<vals_type> ret;
     315              : 
     316            1 :   std::default_random_engine e1(10);
     317            1 :   std::uniform_int_distribution<int> uniform_dist(0, (1 << 14) - 1);
     318              : 
     319            1 :   const int n_fuzz = 100;
     320          101 :   for (int i = 0; i < n_fuzz; ++i) {
     321              :     std::array<uint16_t, 256> vals; // NOLINT(build/unsigned)
     322        25700 :     for (size_t j = 0; j < vals.size(); ++j) {
     323        25600 :       vals[j] = uniform_dist(e1);
     324              :     }
     325          100 :     ret.push_back(vals);
     326              :   }
     327              : 
     328            1 :   return ret;
     329            0 : }
     330              : 
     331          200 : BOOST_DATA_TEST_CASE(CompareToUnpack, boost::unit_test::data::make(make_vals()), vals)
     332              : {
     333              :   // Check the WIB2 frame overlay code in WIB2Frame.hpp by comparing to
     334              :   // the output of the (independently-written) code in WIB2Unpack.hpp
     335              :   wib2unpack::frame14_unpacked unpacked;
     336              :   // Zero it out just to be on the safe side
     337          100 :   std::memset(&unpacked, 0, sizeof(wib2unpack::frame14_unpacked));
     338              : 
     339              :   wib2unpack::frame14 packed;
     340              :   // Zero it out just to be on the safe side
     341          100 :   std::memset(&packed, 0, sizeof(wib2unpack::frame14));
     342              : 
     343              :   // Fill the unpacked frame from the input `vals` array
     344          100 :   int counter = 0;
     345          300 :   for (int femb = 0; femb < 2; ++femb) {
     346         8200 :     for (int i = 0; i < 40; ++i) {
     347         8000 :       unpacked.femb[femb].u[i] = vals[counter];
     348         8000 :       counter++;
     349              :     }
     350         8200 :     for (int i = 0; i < 40; ++i) {
     351         8000 :       unpacked.femb[femb].v[i] = vals[counter];
     352         8000 :       counter++;
     353              :     }
     354         9800 :     for (int i = 0; i < 48; ++i) {
     355         9600 :       unpacked.femb[femb].x[i] = vals[counter];
     356         9600 :       counter++;
     357              :     }
     358              :   }
     359              : 
     360              :   // Create the packed array from the unpacked array
     361          100 :   wib2unpack::repack_frame(&unpacked, &packed);
     362              : 
     363              :   //WIB2Frame* wib2frame = reinterpret_cast<WIB2Frame*>(&packed); // NOLINT
     364          100 :   size_t num_errors = 0;
     365              : 
     366              :   // TODO: replace with some meaningul test in the future
     367              :   // for (int femb = 0; femb < 2; ++femb) {
     368              :   //   for (int i = 0; i < 40; ++i) {
     369              :   //     uint16_t gold = unpacked.femb[femb].u[i];  // NOLINT(build/unsigned)
     370              :   //     uint16_t test = wib2frame->get_u(femb, i); // NOLINT(build/unsigned)
     371              :   //     if (gold != test) {
     372              :   //       num_errors++;
     373              :   //       BOOST_CHECK_EQUAL(gold, test);
     374              :   //     }
     375              :   //   }
     376              :   //   for (int i = 0; i < 40; ++i) {
     377              :   //     uint16_t gold = unpacked.femb[femb].v[i];  // NOLINT(build/unsigned)
     378              :   //     uint16_t test = wib2frame->get_v(femb, i); // NOLINT(build/unsigned)
     379              :   //     if (gold != test) {
     380              :   //       num_errors++;
     381              :   //       BOOST_CHECK_EQUAL(gold, test);
     382              :   //     }
     383              :   //   }
     384              :   //   for (int i = 0; i < 48; ++i) {
     385              :   //     uint16_t gold = unpacked.femb[femb].x[i];  // NOLINT(build/unsigned)
     386              :   //     uint16_t test = wib2frame->get_x(femb, i); // NOLINT(build/unsigned)
     387              :   //     if (gold != test) {
     388              :   //       num_errors++;
     389              :   //       BOOST_CHECK_EQUAL(gold, test);
     390              :   //     }
     391              :   //   }
     392              :   // } // loop over femb
     393              : 
     394          100 :   BOOST_REQUIRE_EQUAL(num_errors, 0);
     395          100 : }
     396              : 
     397            2 : BOOST_AUTO_TEST_CASE(WIB2Frame_ADCDataMutators)
     398              : {
     399            1 :   std::random_device dev;
     400            1 :   std::mt19937 rng(dev());
     401            1 :   std::uniform_int_distribution<std::mt19937::result_type> dist(1,(1<<14)-1);
     402            1 :   std::vector<int> v;
     403          257 :   for(int i=0; i<256; i++) {
     404          256 :     v.push_back(dist(rng));
     405              :   }
     406              : 
     407            1 :   dunedaq::fddetdataformats::WIB2Frame wib2frame {};
     408          257 :   for(int i=0; i<256; i++) {
     409          256 :     wib2frame.set_adc(i, v[i]);
     410              :   }
     411              : 
     412          257 :   for(int i=0; i<256; i++) {
     413          256 :     BOOST_REQUIRE_EQUAL(wib2frame.get_adc(i), v[i]);
     414              :   }
     415              : 
     416            1 : }
     417              : 
     418              : BOOST_AUTO_TEST_SUITE_END()
        

Generated by: LCOV version 2.0-1