From 66e705c0fe9e00c27cda892681ccbf9ccc0c4ab3 Mon Sep 17 00:00:00 2001 From: Filip Leonarski Date: Fri, 31 Jul 2026 15:02:11 +0200 Subject: [PATCH] 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) --- docs/FPGA_DATA_ANALYSIS.md | 7 ++++--- fpga/pcie_driver/jfjoch_fpga.h | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/FPGA_DATA_ANALYSIS.md b/docs/FPGA_DATA_ANALYSIS.md index 772fd84f..a392f0b4 100644 --- a/docs/FPGA_DATA_ANALYSIS.md +++ b/docs/FPGA_DATA_ANALYSIS.md @@ -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. diff --git a/fpga/pcie_driver/jfjoch_fpga.h b/fpga/pcie_driver/jfjoch_fpga.h index eab8ae03..e82603fb 100644 --- a/fpga/pcie_driver/jfjoch_fpga.h +++ b/fpga/pcie_driver/jfjoch_fpga.h @@ -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)