docs: correct the FPGA analysis limits and the square-root coefficient

The azimuthal bin limit is FPGA_INTEGRATION_BIN_COUNT = 2048, not 1024.
There are 16 ROIs, not 64, and the map is a 16-bit per-pixel mask, so a
pixel belongs to any subset of them rather than to exactly one.

The lossy transform is round(sqrt(N*N*X)) = round(N*sqrt(X)): the HLS
squares the sqrtmult register before multiplying. The doc said sqrt(N*X),
which is off by sqrt(N), and the register comment claimed the value was
"minus one" and "should be square of the coeff" - both wrong, the host
writes N and the FPGA squares it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-31 15:02:11 +02:00
co-authored by Claude Opus 5
parent 164f15c903
commit 66e705c0fe
2 changed files with 5 additions and 4 deletions
+4 -3
View File
@@ -31,7 +31,7 @@ To implement azimuthal integration, FPGA is able to sum pixels based on a provid
This way Jungfraujoch implements azimuthal integration with solid angle and polarization corrections.
Corrections were implemented according to formulas developed by [Jensen et al. (J. Synchr. Rad., 29, 1420-1428, 2022)](https://journals.iucr.org/s/issues/2022/06/00/fv5148/).
Given FPGA limitations, split-pixels cannot be implemented and number of bins is limited as 1024 per detector module.
Given FPGA limitations, split-pixels cannot be implemented and number of bins is limited as 2048 per detector module.
This way 2D azimuthal integration, as needed for example by SAS-TT, cannot be currently implemented with the FPGA card and needs to be done on a CPU.
One needs to be careful with per-pixel corrections - their acceptable range is constrained by 16-bit pixed point integer implementation
and is tuned for standard SAXS/WAXS range.
@@ -58,7 +58,7 @@ In vertical direction the area is flexible - it is 15 lines above and below of t
Given very large box size, approximation are made, for example that `N ≈ N-1` in calculating standard deviation.
## Region-of-interest (ROI) integration
Each pixel in a module can be assigned to one of 64 ROIs. For each ROIs, sum, sum of squares, max count, and number of valid pixels will be calculated.
There are 16 ROIs, and the ROI map holds a 16-bit mask per pixel, so a pixel can belong to any subset of them (including none). For each ROI, sum, sum of squares, max count, and number of valid pixels will be calculated.
Jungfraujoch also calculates X and Y values weighted by pixel values, though this feature is not properly tested at the moment and not integrated in downstream analysis.
ROIs are not specific to the FPGA path. The same ROI definitions — box, circle, and azimuthal
@@ -79,4 +79,5 @@ Valid pixels are not masked, not saturated, not error pixels.
## Square root compression
Jungfraujoch FPGA includes lossy compression preserving counting statistic properties of X-ray image, while reducing bit width of an image.
Scheme was described in [Wakonig et al., J. Appl. Cryst., 53, 574-586, 2020](https://doi.org/10.1107/S1600576720001776).
Pixel value `X` is replaced with `sqrt(N*X)`, where `N` is integer constant in range 1 to 16.
Pixel value `X` is replaced with `round(sqrt(N*N*X))`, i.e. `round(N*sqrt(X))`, where `N` is integer constant in range 1 to 16.
`N` is what the host writes to the `sqrtmult` register; the FPGA squares it before multiplying the pixel value.
+1 -1
View File
@@ -106,7 +106,7 @@ struct DataCollectionConfig {
uint32_t nframes; // Number of frames for data collection
uint32_t nstorage_cells; // Number of storage cells minus one (0 = 1SC, 1 = 2SC, ..., 15 = 16SC)
uint32_t nsummation; // Summation of frames minus one (0 = no summation, 1 = 2 frames, 2 = 3 frames, ..., 255 = 256 frames)
uint32_t sqrtmult; // Multiplication factor (minus one) of pixel value BEFORE taking square root function - should be square of the coeff used in other work
uint32_t sqrtmult; // Coefficient N of the lossy square-root transform, range 1 to 16. The FPGA squares it, so the pixel value becomes round(sqrt(N*N*X)) = round(N*sqrt(X)). Write N here, not N*N.
int32_t pxlthreshold_min; // pixels with values lower than threshold are set to zero (after conversion to photons; before summation)
int32_t pxlthreshold_max; // pixels with values higher than threshold are set as overload (after conversion to photons; before summation)
int32_t data_stream; // number of the data stream (checked against upper 8-bit of UDP destination port)