DUNE-DAQ
DUNE Trigger and Data Acquisition software
Loading...
Searching...
No Matches
Utils.hpp
Go to the documentation of this file.
1
12
13#ifndef FDDETDATAFORMATS_INCLUDE_FDDETDATAFORMATS_UTILS_HPP_
14#define FDDETDATAFORMATS_INCLUDE_FDDETDATAFORMATS_UTILS_HPP_
15
18
19#include <algorithm>
20#include <cassert>
21#include <format>
22#include <limits>
23#include <stdexcept> // Provides std::out_of_range
24
26
27// get_adc_2d_as_1d will fetch an ADC value from a physical 1-d C++
28// array of WordTypes which can contain a logical 2-d array of
29// ADCs. It exists because the "blob of bytes" in DAPHNE streams
30// represent a logical 2-d array of ADC values: starting with ADC #0
31// values in four channels side-by-side, followed by ADC #1 values
32// in four channels side-by-side, etc.
33
34// Of course, for other readout types, a logical 1-d array of ADCs
35// is simply a special case which this function can handle; the
36// "NChannels" template parameter just needs to be set to 1. In this
37// way, it can handle all of our get_adc needs.
38
39template<typename WordType, int NWords, int BitsPerADC, int ADCSPerChannel, int NChannels>
40WordType
41get_adc_2d_as_1d(const int i_adc, const int i_channel, const WordType (&adc_matrix)[NWords]) // NOLINT(modernize-avoid-c-arrays)
42{
43
44 static_assert(std::is_integral_v<WordType> && std::is_unsigned_v<WordType>,
45 "WordType must be an unsigned integral type");
46
47 constexpr int bits_per_word = std::numeric_limits<WordType>::digits; // Fine since we know integer is unsigned
48
49 static_assert(BitsPerADC > 0 && BitsPerADC <= bits_per_word);
50 static_assert(ADCSPerChannel * NChannels * BitsPerADC == NWords * bits_per_word);
51
52 if (i_channel < 0 || i_channel >= NChannels) {
53 throw std::out_of_range(
54 std::format("Requested channel of {} is out of channel range 0-{}", i_channel, NChannels - 1));
55 }
56
57 if (i_adc < 0 || i_adc >= ADCSPerChannel) {
58 throw std::out_of_range(std::format("Requested ADC index of {} if out of range 0-{}", i_adc, ADCSPerChannel - 1));
59 }
60
61 // find absolute index in frame
62 int i_abs = i_adc * NChannels + i_channel;
63
64 if constexpr (BitsPerADC == bits_per_word) {
65 return adc_matrix[i_abs];
66 } else {
67
68 // The index of the first (and sometimes only) word containing the required ADC value
69 int i_word = BitsPerADC * i_abs / bits_per_word;
70 assert(i_word < NWords);
71
72 // Where in the word the lowest bit of our ADC value is located
73 int first_bit_position = (BitsPerADC * i_abs) % bits_per_word;
74
75 // How many bits of our desired ADC are located in the `i_word`th word
76 int bits_from_first_word = std::min(BitsPerADC, bits_per_word - first_bit_position);
77
78 WordType adc_val = adc_matrix[i_word] >> first_bit_position; // NOLINT(build/unsigned)
79
80 if (bits_from_first_word < BitsPerADC) {
81 assert(i_word < NWords - 1);
82 adc_val |= adc_matrix[i_word + 1] << bits_from_first_word;
83 }
84
85 // Mask out all but the lowest BitsPerADC bits;
86 return adc_val & ((static_cast<WordType>(1) << BitsPerADC) - 1);
87
88 } // if BitsPerADC != bits_per_word
89}
90
91// See above comment on "get_adc_2d_as_1d" to understand the structure "set_adc_2d_as_1d" is working with
92
93template<typename WordType, int NWords, int BitsPerADC, int ADCSPerChannel, int NChannels>
94void
95set_adc_2d_as_1d(const int i_adc, const int i_channel, const WordType adc_val, WordType (&adc_matrix)[NWords]) // NOLINT(modernize-avoid-c-arrays)
96{
97 static_assert(std::is_integral_v<WordType> && std::is_unsigned_v<WordType>,
98 "WordType must be an unsigned integral type");
99
100 constexpr int bits_per_word = std::numeric_limits<WordType>::digits; // Fine since we know integer is unsigned
101
102 static_assert(BitsPerADC > 0 && BitsPerADC <= bits_per_word);
103 static_assert(ADCSPerChannel * NChannels * BitsPerADC == NWords * bits_per_word);
104
105 if (i_channel < 0 || i_channel >= NChannels) {
106 throw std::out_of_range(
107 std::format("Requested channel of {} is out of channel range 0-{}", i_channel, NChannels - 1));
108 }
109
110 if (i_adc < 0 || i_adc >= ADCSPerChannel) {
111 throw std::out_of_range(std::format("Requested ADC index of {} is out of range 0-{}", i_adc, ADCSPerChannel - 1));
112 }
113
114 if constexpr (BitsPerADC < bits_per_word) {
115 if (adc_val >= (static_cast<WordType>(1) << BitsPerADC)) {
116 throw std::out_of_range(std::format(
117 "Requested ADC value of {} exceeds max value of {}", adc_val, (static_cast<WordType>(1) << BitsPerADC) - 1));
118 }
119 }
120
121 // find absolute index in frame
122 int i_abs = i_adc * NChannels + i_channel;
123
124 if constexpr (BitsPerADC == bits_per_word) {
125 adc_matrix[i_abs] = adc_val;
126 } else {
127
128 // The index of the first (and sometimes only) word containing the required ADC value
129 int i_word = BitsPerADC * i_abs / bits_per_word;
130 assert(i_word < NWords);
131
132 // Where in the word the lowest bit of our ADC value is located
133 int first_bit_position = (BitsPerADC * i_abs) % bits_per_word;
134
135 // How many bits of our desired ADC are located in the `i_word`th word
136 int bits_in_first_word = std::min(BitsPerADC, bits_per_word - first_bit_position);
137
138 WordType mask = ((static_cast<WordType>(1) << bits_in_first_word) - 1) << first_bit_position;
139
140 adc_matrix[i_word] = (adc_matrix[i_word] & ~mask) | ((static_cast<WordType>(adc_val) << first_bit_position) & mask);
141
142 // If we didn't put the full 14 bits in this word, we need to put the rest in the next word
143 if (bits_in_first_word < BitsPerADC) {
144 assert(i_word < NWords - 1);
145 int bits_in_second_word = BitsPerADC - bits_in_first_word;
146 WordType mask2 = (static_cast<WordType>(1) << bits_in_second_word) - 1;
147 adc_matrix[i_word + 1] = (adc_matrix[i_word + 1] & ~mask2) | ((adc_val >> bits_in_first_word) & mask2);
148 }
149 } // BitsPerADC != bits_per_word
150}
151
152// This get_adc is a convenience wrapper around get_adc_2d_as_1d for when
153// the logical array of ADCs is 1-d rather than 2-d
154
155template<typename WordType, int NWords, int BitsPerADC>
156WordType
157get_adc_1d(const int i_adc, const WordType (&adc_array)[NWords]) // NOLINT(modernize-avoid-c-arrays)
158{
159
160 static_assert(std::is_integral_v<WordType> && std::is_unsigned_v<WordType>,
161 "WordType must be an unsigned integral type");
162
163 constexpr int bits_per_word = std::numeric_limits<WordType>::digits; // Fine since we know integer is unsigned
164
165 static_assert(BitsPerADC > 0);
166 constexpr int num_adcs = NWords * bits_per_word / BitsPerADC;
167
169}
170
171// get_adc_2d can be used when the data is represented as a 2-d array.
172// Rows and Columns refer to physical rows and columns of WordType
173// in the adc_matrix object; i_sample and i_adc refer to logical rows
174// and columns of ADC values in the event. However, logical rows
175// align with physical rows, so this can't be used, e.g., for DAPHNE
176// streaming data - get_adc_2d_as_1d needs to be used for that.
177
178template<typename WordType, int Rows, int Columns, int BitsPerADC>
179WordType
180get_adc_2d(const int i_sample, const int i_adc, const WordType (&adc_matrix)[Rows][Columns]) // NOLINT(modernize-avoid-c-arrays)
181{
182
183 if (i_sample < 0 || i_sample >= Rows) {
184 throw std::out_of_range(
185 std::format("Requested index of {}th 1-d ADC array is outside of allowed range 0-{}", i_sample, Rows - 1));
186 }
187
188 return get_adc_1d<WordType, Columns, BitsPerADC>(i_adc, adc_matrix[i_sample]);
189}
190
191// This set_adc is a convenience wrapper around set_adc_2d_as_1d for when
192// the logical array of ADCs is 1-d rather than 2-d
193
194template<typename WordType, int NWords, int BitsPerADC>
195void
196set_adc_1d(const int i_adc, WordType adc_val, WordType (&adc_array)[NWords]) // NOLINT(modernize-avoid-c-arrays)
197{
198 static_assert(std::is_integral_v<WordType> && std::is_unsigned_v<WordType>,
199 "WordType must be an unsigned integral type");
200
201 constexpr int bits_per_word = std::numeric_limits<WordType>::digits; // Fine since we know integer is unsigned
202
203 static_assert(BitsPerADC > 0 && BitsPerADC <= bits_per_word);
204 static_assert((NWords * bits_per_word) % BitsPerADC == 0);
205
206 constexpr int num_adcs = NWords * bits_per_word / BitsPerADC;
207
209}
210
211// Rows and Columns refer to physical rows and columns of WordType
212// in the adc_matrix object; i_sample and i_adc refer to logical
213// rows and columns of ADC values in the event
214
215template<typename WordType, int Rows, int Columns, int BitsPerADC>
216void
217set_adc_2d(const int i_sample, const int i_adc, WordType adc_val, WordType (&adc_matrix)[Rows][Columns]) // NOLINT(modernize-avoid-c-arrays)
218{
219 if (i_sample < 0 || i_sample >= Rows) {
220 throw std::out_of_range(
221 std::format("Requested index of {}th 1-d ADC array is outside of allowed range 0-{}", i_sample, Rows - 1));
222 }
223
224 set_adc_1d<WordType, Columns, BitsPerADC>(i_adc, adc_val, adc_matrix[i_sample]);
225}
226
227} // namespace dunedaq::fddetdataformats
228
229#endif // FDDETDATAFORMATS_INCLUDE_FDDETDATAFORMATS_UTILS_HPP_
void set_adc_2d(const int i_sample, const int i_adc, WordType adc_val, WordType(&adc_matrix)[Rows][Columns])
Definition Utils.hpp:217
WordType get_adc_2d_as_1d(const int i_adc, const int i_channel, const WordType(&adc_matrix)[NWords])
Definition Utils.hpp:41
WordType get_adc_1d(const int i_adc, const WordType(&adc_array)[NWords])
Definition Utils.hpp:157
void set_adc_1d(const int i_adc, WordType adc_val, WordType(&adc_array)[NWords])
Definition Utils.hpp:196
void set_adc_2d_as_1d(const int i_adc, const int i_channel, const WordType adc_val, WordType(&adc_matrix)[NWords])
Definition Utils.hpp:95
WordType get_adc_2d(const int i_sample, const int i_adc, const WordType(&adc_matrix)[Rows][Columns])
Definition Utils.hpp:180