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