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