1 /*************************************************************************//**
2 * @file
3 * @brief This file is part of the AFBR-S50 API.
4 * @details Defines macros to work with pixel and ADC channel masks.
5 *
6 * @copyright
7 *
8 * Copyright (c) 2023, Broadcom Inc.
9 * All rights reserved.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions are met:
13 *
14 * 1. Redistributions of source code must retain the above copyright notice, this
15 * list of conditions and the following disclaimer.
16 *
17 * 2. Redistributions in binary form must reproduce the above copyright notice,
18 * this list of conditions and the following disclaimer in the documentation
19 * and/or other materials provided with the distribution.
20 *
21 * 3. Neither the name of the copyright holder nor the names of its
22 * contributors may be used to endorse or promote products derived from
23 * this software without specific prior written permission.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
26 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
28 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
31 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
32 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
33 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
34 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35 *****************************************************************************/
36
37
38 #ifndef ARGUS_MAP_H
39 #define ARGUS_MAP_H
40 #ifdef __cplusplus
41 extern "C" {
42 #endif
43
44 /*!***************************************************************************
45 * @defgroup argus_map Pixel Channel Mapping
46 * @ingroup argus_api
47 *
48 * @brief Pixel Channel Mapping
49 *
50 * @details The ADC Channels of each pixel or auxiliary channel on the device
51 * are numbered in a way that is convenient on the chip architecture.
52 * The macros in this module are defined in order to map between the
53 * chip internal channel number (ch) to the two-dimensional
54 * x-y-indices or one-dimensional n-index representation.
55 *
56 * @addtogroup argus_map
57 * @{
58 *****************************************************************************/
59
60 #include "utility/int_math.h"
61 #include <stdbool.h>
62 #include <assert.h>
63
64
65 /*!***************************************************************************
66 * @brief The device pixel field size in x direction (long edge).
67 *****************************************************************************/
68 #define ARGUS_PIXELS_X 8
69
70 /*!***************************************************************************
71 * @brief The device pixel field size in y direction (short edge).
72 *****************************************************************************/
73 #define ARGUS_PIXELS_Y 4
74
75 /*!***************************************************************************
76 * @brief The total device pixel count.
77 *****************************************************************************/
78 #define ARGUS_PIXELS ((ARGUS_PIXELS_X)*(ARGUS_PIXELS_Y))
79
80
81 /*!*****************************************************************************
82 * @brief Macro to determine the pixel ADC channel number from the x-z-indices.
83 * @param x The x-index of the pixel.
84 * @param y The y-index of the pixel.
85 * @return The ADC channel number of the pixel.
86 ******************************************************************************/
87 #define PIXEL_XY2CH(x, y) ((((y) << 3U) & 0x10U) | (((x) ^ 0x07U) << 1U) | ((y) & 0x01U))
88
89 /*!*****************************************************************************
90 * @brief Macro to determine the pixel x-index from the ADC channel number.
91 * @param c The ADC channel number of the pixel.
92 * @return The x-index of the pixel.
93 ******************************************************************************/
94 #define PIXEL_CH2X(c) ((((c) >> 1U) ^ 0x07U) & 0x07U)
95
96 /*!*****************************************************************************
97 * @brief Macro to determine the pixel y-index from the ADC channel number.
98 * @param c The ADC channel number of the pixel.
99 * @return The y-index of the pixel.
100 ******************************************************************************/
101 #define PIXEL_CH2Y(c) ((((c) >> 3U) & 0x02U) | ((c) & 0x01U))
102
103
104 /*!*****************************************************************************
105 * @brief Macro to determine the n-index from the x-y-indices.
106 * @param x The x-index of the pixel.
107 * @param y The y-index of the pixel.
108 * @return The n-index of the pixel.
109 ******************************************************************************/
110 #define PIXEL_XY2N(x, y) (((x) << 2U) | (y))
111
112 /*!*****************************************************************************
113 * @brief Macro to determine the pixel x-index from the n-index.
114 * @param n The n-index of the pixel.
115 * @return The x-index number of the pixel.
116 ******************************************************************************/
117 #define PIXEL_N2X(n) ((n) >> 2U)
118
119 /*!*****************************************************************************
120 * @brief Macro to determine the pixel y-index from the n-index.
121 * @param n The n-index of the pixel.
122 * @return The y-index number of the pixel.
123 ******************************************************************************/
124 #define PIXEL_N2Y(n) ((n) & 0x03U)
125
126
127 /*!*****************************************************************************
128 * @brief Macro to determine the pixel n-index from the ADC channel number.
129 * @param n The n-index of the pixel.
130 * @return The ADC channel number of the pixel.
131 ******************************************************************************/
132 #define PIXEL_N2CH(n) ((((n) << 3U) & 0x10U) | ((((n) >> 1U) ^ 0x0EU) & 0x0EU) | ((n) & 0x01U))
133
134 /*!*****************************************************************************
135 * @brief Macro to determine the pixel
136 * @param c The ADC channel number of the pixel.
137 * @return The n-index of the pixel.
138 ******************************************************************************/
139 #define PIXEL_CH2N(c) (((((c) << 1U) ^ 0x1CU) & 0x1CU) | (((c) >> 3U) & 0x02U) | ((c) & 0x01U))
140
141
142 /*!*****************************************************************************
143 * @brief Macro to create a pixel mask given by the pixels n-index.
144 * @param n n-index of the pixel.
145 * @return The pixel mask with only n-index pixel set.
146 ******************************************************************************/
147 #define PIXELN_MASK(n) (0x01U << (n))
148
149 /*!*****************************************************************************
150 * @brief Macro to determine if a pixel given by the n-index is enabled in a pixel mask.
151 * @param msk 32-bit pixel mask
152 * @param n n-index of the pixel.
153 * @return True if the pixel (n) is enabled.
154 ******************************************************************************/
155 #define PIXELN_ISENABLED(msk, n) (((msk) >> (n)) & 0x01U)
156
157 /*!*****************************************************************************
158 * @brief Macro to enable a pixel given by the n-index in a pixel mask.
159 * @param msk 32-bit pixel mask
160 * @param n n-index of the pixel to enable.
161 ******************************************************************************/
162 #define PIXELN_ENABLE(msk, n) ((msk) |= (PIXELN_MASK(n)))
163
164 /*!*****************************************************************************
165 * @brief Macro disable a pixel given by the n-index in a pixel mask.
166 * @param msk 32-bit pixel mask
167 * @param n n-index of the pixel to disable.
168 ******************************************************************************/
169 #define PIXELN_DISABLE(msk, n) ((msk) &= (~PIXELN_MASK(n)))
170
171
172 /*!*****************************************************************************
173 * @brief Macro to create a pixel mask given by the pixels ADC channel number.
174 * @param c The ADC channel number of the pixel.
175 * @return The 32-bit pixel mask with only pixel ADC channel set.
176 ******************************************************************************/
177 #define PIXELCH_MASK(c) (0x01U << (PIXEL_CH2N(c)))
178
179 /*!*****************************************************************************
180 * @brief Macro to determine if an ADC pixel channel is enabled from a pixel mask.
181 * @param msk The 32-bit pixel mask
182 * @param c The ADC channel number of the pixel.
183 * @return True if the specified pixel ADC channel is enabled.
184 ******************************************************************************/
185 #define PIXELCH_ISENABLED(msk, c) (PIXELN_ISENABLED(msk, PIXEL_CH2N(c)))
186
187 /*!*****************************************************************************
188 * @brief Macro to enable an ADC pixel channel in a pixel mask.
189 * @param msk The 32-bit pixel mask
190 * @param c The pixel ADC channel number to enable.
191 ******************************************************************************/
192 #define PIXELCH_ENABLE(msk, c) (PIXELN_ENABLE(msk, PIXEL_CH2N(c)))
193
194 /*!*****************************************************************************
195 * @brief Macro to disable an ADC pixel channel in a pixel mask.
196 * @param msk The 32-bit pixel mask
197 * @param c The pixel ADC channel number to disable.
198 ******************************************************************************/
199 #define PIXELCH_DISABLE(msk, c) (PIXELN_DISABLE(msk, PIXEL_CH2N(c)))
200
201
202 /*!*****************************************************************************
203 * @brief Macro to create a pixel mask given by the pixel x-y-indices.
204 * @param x x-index of the pixel.
205 * @param y y-index of the pixel.
206 * @return The 32-bit pixel mask with only pixel ADC channel set.
207 ******************************************************************************/
208 #define PIXELXY_MASK(x, y) (0x01U << (PIXEL_XY2N(x, y)))
209
210 /*!*****************************************************************************
211 * @brief Macro to determine if a pixel given by the x-y-indices is enabled in a pixel mask.
212 * @param msk 32-bit pixel mask
213 * @param x x-index of the pixel.
214 * @param y y-index of the pixel.
215 * @return True if the pixel (x,y) is enabled.
216 ******************************************************************************/
217 #define PIXELXY_ISENABLED(msk, x, y) (PIXELN_ISENABLED(msk, PIXEL_XY2N(x, y)))
218
219 /*!*****************************************************************************
220 * @brief Macro to enable a pixel given by the x-y-indices in a pixel mask.
221 * @param msk 32-bit pixel mask
222 * @param x x-index of the pixel to enable.
223 * @param y y-index of the pixel to enable.
224 ******************************************************************************/
225 #define PIXELXY_ENABLE(msk, x, y) (PIXELN_ENABLE(msk, PIXEL_XY2N(x, y)))
226
227 /*!*****************************************************************************
228 * @brief Macro disable a pixel given by the x-y-indices in a pixel mask.
229 * @param msk 32-bit pixel mask
230 * @param x x-index of the pixel to disable.
231 * @param y y-index of the pixel to disable.
232 ******************************************************************************/
233 #define PIXELXY_DISABLE(msk, x, y) (PIXELN_DISABLE(msk, PIXEL_XY2N(x, y)))
234
235
236 /*!*****************************************************************************
237 * @brief Macro to determine if an ADC channel is enabled in a channel mask.
238 * @param msk 32-bit channel mask
239 * @param ch channel number of the ADC channel.
240 * @return True if the ADC channel is enabled.
241 ******************************************************************************/
242 #define CHANNELN_ISENABLED(msk, ch) (((msk) >> ((ch) - 32U)) & 0x01U)
243
244 /*!*****************************************************************************
245 * @brief Macro to determine if an ADC channel is enabled in a channel mask.
246 * @param msk 32-bit channel mask
247 * @param ch channel number of the ADC channel to enabled.
248 ******************************************************************************/
249 #define CHANNELN_ENABLE(msk, ch) ((msk) |= (0x01U << ((ch) - 32U)))
250
251 /*!*****************************************************************************
252 * @brief Macro to determine if an ADC channel is disabled in a channel mask.
253 * @param msk 32-bit channel mask
254 * @param ch channel number of the ADC channel to disable.
255 ******************************************************************************/
256 #define CHANNELN_DISABLE(msk, ch) ((msk) &= (~(0x01U << ((ch) - 32U))))
257
258
259 /*!*****************************************************************************
260 * @brief Macro to determine the number of enabled pixel/channels in a mask
261 * via a popcount algorithm.
262 * @param pxmsk 32-bit pixel mask
263 * @return The count of enabled pixel channels.
264 ******************************************************************************/
265 #define PIXEL_COUNT(pxmsk) popcount(pxmsk)
266
267 /*!*****************************************************************************
268 * @brief Macro to determine the number of enabled channels via a popcount
269 * algorithm.
270 * @param pxmsk 32-bit pixel mask
271 * @param chmsk 32-bit channel mask
272 * @return The count of enabled ADC channels.
273 ******************************************************************************/
274 #define CHANNEL_COUNT(pxmsk, chmsk) (popcount(pxmsk) + popcount(chmsk))
275
276 /*!*****************************************************************************
277 * @brief Converts a raw ADC channel mask to a x-y-sorted pixel mask.
278 * @param msk The raw ADC channel mask to be converted.
279 * @return The converted x-y-sorted pixel mask.
280 ******************************************************************************/
ChannelToPixelMask(uint32_t msk)281 static inline uint32_t ChannelToPixelMask(uint32_t msk)
282 {
283 uint32_t res = 0;
284 for (uint_fast8_t n = 0; n < 32; n += 2)
285 {
286 res |= ((msk >> PIXEL_N2CH(n)) & 0x3U) << n; // sets 2 bits at once
287 }
288 return res;
289 }
290
291 /*!*****************************************************************************
292 * @brief Converts a x-y-sorted pixel mask to a raw ADC channel mask.
293 * @param msk The x-y-sorted pixel channel mask to be converted.
294 * @return The converted raw ADC channel mask.
295 ******************************************************************************/
PixelToChannelMask(uint32_t msk)296 static inline uint32_t PixelToChannelMask(uint32_t msk)
297 {
298 uint32_t res = 0;
299 for (uint_fast8_t ch = 0; ch < 32; ch += 2)
300 {
301 res |= ((msk >> PIXEL_CH2N(ch)) & 0x3U) << ch; // sets 2 bits at once
302 }
303 return res;
304 }
305
306
307 /*!*****************************************************************************
308 * @brief Shifts a pixel mask by a given offset.
309 *
310 * @details This moves the selected pixel pattern by a specified number of
311 * pixels in x and y direction.
312 * If the shift in y direction is odd (e.g +1), the pattern will be
313 * shifted by +0.5 or -0.5 in x direction due to the hexagonal shape
314 * of the pixel field. Thus, a center pixel (usually the Golden Pixel)
315 * is determined that is used to determine if the pattern is shifted
316 * by +0.5 or -0.5 pixels in x direction. The center pixel is then
317 * always shifted without changing the x index and the surrounding
318 * pixels are adopting its x index accordingly.
319 *
320 * Example: Consider the flower pattern, i.e. the Golden Pixel (e.g.
321 * 5/2) is selected and all is direct neighbors (i.e. 5/1, 6/1, 6/2,
322 * 6/3, 5/3, 4/2). If the pattern is shifted by -1 in y direction, the
323 * new Golden Pixel would be 5/1. Now all surrounding pixels are
324 * selected, namely 4/0, 4/1, 4/2, 5/0, 5/2, 6/1). This yields again
325 * the flower around the Golden Pixel.
326 *
327 * Thus, the pixels can not all be shifted by the same dx/dy values due
328 * to the hexagonal shape of the pixel field, e.g. the upper right
329 * neighbor of 5/2 is 5/1 but the upper right neighbor of 5/1 is NOT
330 * 5/0 but 4/0!
331 * This happens only if the shift in y direction is an odd number.
332 * The algorithm to determine new indices is as follows:
333 * - If the shift in y direction is even (e.g. +2, -2), no compensation
334 * of the hexagonal shape is needed; skip compensation, simply
335 * add/subtract indices.
336 * - If the center pixel y index is even, pixels that will have even y
337 * index after the shift will be additionally shifted by -1 in x
338 * direction.
339 * - If the center pixel y index is odd, pixel that will have odd y
340 * index after the shift will be additionally shifted by +1 in x
341 * direction.
342 *
343 * @see Please also refer to the function #Argus_GetCalibrationGoldenPixel
344 * to obtain the current Golden Pixel location.
345 *
346 * @param pixel_mask The x-y-sorted pixel mask to be shifted.
347 * @param dx The number of pixel to shift in x direction.
348 * @param dy The number of pixel to shift in y direction.
349 * @param center_y The center y index of the pattern that is shifted.
350 * @return The shifted pixel mask.
351 ******************************************************************************/
ShiftSelectedPixels(const uint32_t pixel_mask,const int8_t dx,const int8_t dy,const uint8_t center_y)352 static inline uint32_t ShiftSelectedPixels(const uint32_t pixel_mask,
353 const int8_t dx,
354 const int8_t dy,
355 const uint8_t center_y)
356 {
357 if (dx == 0 && dy == 0) return pixel_mask;
358
359 uint32_t shifted_mask = 0;
360
361 for (int8_t x = 0; x < ARGUS_PIXELS_X; ++x)
362 {
363 for (int8_t y = 0; y < ARGUS_PIXELS_Y; ++y)
364 {
365 int8_t x_src = (int8_t)(x - dx);
366 int8_t y_src = (int8_t)(y - dy);
367
368 if (dy & 0x1)
369 {
370 /* Compensate for hexagonal pixel shape. */
371 if ((center_y & 0x1) && (y & 0x1))
372 {
373 x_src--;
374 }
375 if (!(center_y & 0x1) && !(y & 0x1))
376 {
377 x_src++;
378 }
379 }
380
381 if (x_src < 0 || x_src >= ARGUS_PIXELS_X) continue;
382 if (y_src < 0 || y_src >= ARGUS_PIXELS_Y) continue;
383
384 if (PIXELXY_ISENABLED(pixel_mask, x_src, y_src))
385 {
386 PIXELXY_ENABLE(shifted_mask, x, y);
387 }
388 }
389 }
390
391 return shifted_mask;
392 }
393
394 /*!*****************************************************************************
395 * @brief Fills a pixel mask to a specified number of pixels around a center pixel.
396 *
397 * @details The pixel mask is iteratively filled with the nearest pixel to a
398 * specified center pixel until a specified number of pixels is achieved.
399 * The distance between two pixel is determined via a quadratic metric,
400 * i.e. dx^2 + dy^2. Pixels towards the lower x indices are preferred.
401 *
402 * Note that the distance of only calculated approximately, e.g. the
403 * y distance of pixels is considered to be 2 instead of cos(60)*2.
404 *
405 * Nothing is done if the number of pixels already exceeds the specified
406 * /p pixel_count parameter.
407 *
408 * @see Please also refer to the function #Argus_GetCalibrationGoldenPixel
409 * to obtain the current Golden Pixel location.
410 *
411 * @param pixel_mask The x-y-sorted pixel mask to be filled with pixels.
412 * @param pixel_count The final number of pixels in the pixel mask.
413 * @param center_x The center pixel x-index.
414 * @param center_y The center pixel y-index.
415 * @return The filled pixel mask with at least /p pixel_count pixels selected.
416 ******************************************************************************/
FillPixelMask(uint32_t pixel_mask,const uint8_t pixel_count,const uint8_t center_x,const uint8_t center_y)417 static inline uint32_t FillPixelMask(uint32_t pixel_mask,
418 const uint8_t pixel_count,
419 const uint8_t center_x,
420 const uint8_t center_y)
421 {
422 assert(pixel_count <= ARGUS_PIXELS);
423 assert(center_x < ARGUS_PIXELS_X);
424 assert(center_y < ARGUS_PIXELS_Y);
425
426 if (pixel_count == ARGUS_PIXELS) return 0xFFFFFFFFU;
427
428 /* If the pattern was shifted towards boundaries, the pixel count may have
429 * decreased. In this case, the pixels closest to the reference pixel are
430 * selected. Pixel towards lower x index are prioritized. */
431 while (pixel_count > PIXEL_COUNT(pixel_mask))
432 {
433 int32_t min_dist = INT32_MAX;
434 int8_t min_x = -1;
435 int8_t min_y = -1;
436
437 /* Find nearest not selected pixel. */
438 for(int8_t x = 0; x < ARGUS_PIXELS_X; ++x)
439 {
440 for (int8_t y = 0; y < ARGUS_PIXELS_Y; ++y)
441 {
442 if (!PIXELXY_ISENABLED(pixel_mask, x, y))
443 {
444 int32_t distx = (x - center_x) << 1;
445 if (!(y & 0x1)) distx++;
446 if (!(center_y & 0x1)) distx--;
447
448 const int32_t disty = (y - center_y) << 1;
449 int32_t dist = distx * distx + disty * disty;
450
451 if (dist < min_dist)
452 {
453 min_dist = dist;
454 min_x = (int8_t)x;
455 min_y = (int8_t)y;
456 }
457 }
458 }
459 }
460
461 assert(min_x >= 0 && min_x < ARGUS_PIXELS_X);
462 assert(min_y >= 0 && min_y < ARGUS_PIXELS_Y);
463 assert(!PIXELXY_ISENABLED(pixel_mask, min_x, min_y));
464 PIXELXY_ENABLE(pixel_mask, min_x, min_y);
465 }
466
467 return pixel_mask;
468 }
469
470 /*!*****************************************************************************
471 * @brief Fills a pixel mask with the direct neighboring pixels around a pixel.
472 *
473 * @details The pixel mask is iteratively filled with the direct neighbors of the
474 * specified center pixel.
475 *
476 * Note that the function is able to handle corner and edge pixels and
477 * also to handle odd/even lines (which have different layouts)
478 *
479 * @param x The selected pixel x-index.
480 * @param y The selected pixel y-index.
481 * @return The filled pixel mask with all direct neighbors of the selected pixel.
482 ******************************************************************************/
GetAdjacentPixelsMask(const uint_fast8_t x,const uint_fast8_t y)483 static inline uint32_t GetAdjacentPixelsMask(const uint_fast8_t x,
484 const uint_fast8_t y)
485 {
486 assert(x < ARGUS_PIXELS_X);
487 assert(y < ARGUS_PIXELS_Y);
488
489 uint32_t mask = 0u;
490
491 bool isXEdgeLow = (x == 0);
492 bool isXEdgeHigh = (x == (ARGUS_PIXELS_X - 1));
493 bool isYEdgeLow = (y == 0);
494 bool isYEdgeHigh = (y == (ARGUS_PIXELS_Y - 1));
495
496 if (y % 2 == 0)
497 {
498 if (!isYEdgeLow) PIXELXY_ENABLE(mask, x, y - 1);
499 if ((!isXEdgeHigh) && (!isYEdgeLow)) PIXELXY_ENABLE(mask, x + 1, y - 1);
500 if (!isXEdgeHigh) PIXELXY_ENABLE(mask, x + 1, y);
501 if ((!isXEdgeHigh) && (!isYEdgeHigh)) PIXELXY_ENABLE(mask, x + 1, y + 1);
502 if (!isYEdgeHigh) PIXELXY_ENABLE(mask, x, y + 1);
503 if (!isXEdgeLow) PIXELXY_ENABLE(mask, x - 1, y);
504 }
505 else
506 {
507 if ((!isXEdgeLow) && (!isYEdgeLow)) PIXELXY_ENABLE(mask, x - 1, y - 1);
508 if (!isYEdgeLow) PIXELXY_ENABLE(mask, x, y - 1);
509 if (!isXEdgeHigh) PIXELXY_ENABLE(mask, x + 1, y);
510 if (!isYEdgeHigh) PIXELXY_ENABLE(mask, x, y + 1);
511 if ((!isXEdgeLow) && (!isYEdgeHigh)) PIXELXY_ENABLE(mask, x - 1, y + 1);
512 if (!isXEdgeLow) PIXELXY_ENABLE(mask, x - 1, y);
513 }
514
515 return mask;
516 }
517
518
519 /*! @} */
520 #ifdef __cplusplus
521 } // extern "C"
522 #endif
523 #endif /* ARGUS_MAP_H */
524