Compare commits

...

230 Commits

Author SHA1 Message Date
ab2e290658 Merge branch 'developer' into jf_h5reader
All checks were successful
Build on RHEL8 / build (push) Successful in 4m37s
Build on RHEL9 / build (push) Successful in 5m6s
2025-04-30 16:19:22 +02:00
1b0e891912 Merge pull request #1200 from slsdetectorgroup/fix/dbitreorder_should_only_be_defined_for_chip_and_xilinx
All checks were successful
Build on RHEL8 / build (push) Successful in 4m38s
Build on RHEL9 / build (push) Successful in 5m26s
Fix/dbitreorder should only be defined for chip and xilinx
2025-04-30 13:38:19 +02:00
292d65491a Merge branch 'developer' into fix/dbitreorder_should_only_be_defined_for_chip_and_xilinx 2025-04-30 12:17:46 +02:00
36faec6ad3 removed log as error already printed 2025-04-30 12:17:16 +02:00
01d3dc7115 Polish data structure 2025-04-30 11:01:24 +02:00
062002243e added error message on receiver side, throw error 2025-04-30 10:54:34 +02:00
98b1e287a4 moved dbitoffset, dbitreorder and dbitlist to GeneralData
All checks were successful
Build on RHEL9 / build (push) Successful in 3m25s
Build on RHEL8 / build (push) Successful in 4m48s
2025-04-30 09:08:07 +02:00
e1f46d4747 Merge pull request #1193 from slsdetectorgroup/dev/automate_version_number
All checks were successful
Build on RHEL9 / build (push) Successful in 4m4s
Build on RHEL8 / build (push) Successful in 4m46s
Dev/automate version number
2025-04-29 12:01:06 +02:00
d8c3fa0df3 Merge branch 'dev/automate_version_number' of github.com:slsdetectorgroup/slsDetectorPackage into dev/automate_version_number 2025-04-29 11:15:56 +02:00
27530fca31 version now supports . before postfix 2025-04-29 11:14:52 +02:00
cc7f13a10e Merge branch 'developer' into dev/automate_version_number
All checks were successful
Build on RHEL9 / build (push) Successful in 3m35s
Build on RHEL8 / build (push) Successful in 5m3s
2025-04-25 13:49:33 +02:00
625f4353fb Dev/gitea docker (#1194)
All checks were successful
Build on RHEL9 / build (push) Successful in 3m36s
Build on RHEL8 / build (push) Successful in 5m3s
* gitea workflows for RH8 and RH9
* using our docker images
2025-04-25 12:04:45 +02:00
2815913d10 added regex pattern matching to version in toml file 2025-04-25 10:48:16 +02:00
5fa2402ec5 Dev/allow localhost for virtual tests (#1190)
* remove the check for localhost being used in rx_hostname for python test for simulators, run rx_arping test only if hostname is not 'localhost'

* fix tests for fpath: cannot set back to empty anymore (empty is default)

* default rx_hostname arg = localhost, and default settings path =../../settingsdir

* changed virtual tests script for better printout on exceptions

* fix for catching generaltests exceptions and exiting instead of continuing

* fix minor

* fixed shared memeory tests to include current env and fixed prints for errors

---------

Co-authored-by: Erik Fröjdh <erik.frojdh@gmail.com>
2025-04-25 10:14:15 +02:00
4d7d3c9138 upgrading to c++17 from c++11 and patch command has to be found before applying patch on libzmq (#1195) 2025-04-25 09:09:49 +02:00
c3f1d05033 bug did not support version 0.0.0
Some checks failed
Native CMake Build / Configure and build using cmake (push) Failing after 3s
2025-04-24 09:16:32 +02:00
0e4cb7cbcd normalized version to PEP 440 specification in update_version.py 2025-04-24 08:45:18 +02:00
bace9edf89 updatet regex pattern to support postfix
Some checks failed
Native CMake Build / Configure and build using cmake (push) Failing after 3s
2025-04-23 17:09:06 +02:00
99735c3ee5 got typo in github workflow
Some checks failed
Native CMake Build / Configure and build using cmake (push) Failing after 3s
2025-04-23 14:46:12 +02:00
2571397c70 saving changes in git workflow failed 2025-04-23 14:38:51 +02:00
497c3abfc2 managed to load VERSION file in yaml file - simplifies things 2025-04-23 14:26:26 +02:00
fca31cc432 updated github workflow scripts to support automatic version numbering with environment variable
Some checks failed
Native CMake Build / Configure and build using cmake (push) Failing after 24s
2025-04-22 17:05:06 +02:00
8fe4a78feb mistakenly set version back to 0.0.0 2025-04-22 15:43:01 +02:00
ee170fa2e0 Merge branch 'developer' into dev/automate_version_number 2025-04-22 14:06:22 +02:00
da760b2b93 version number automated for python build 2025-04-22 14:00:45 +02:00
2c8c2a46ea Merge pull request #1159 from slsdetectorgroup/dev/issue_dont_reorder_digital_data
Some checks failed
Native CMake Build / Configure and build using cmake (push) Failing after 11s
Dev/issue dont reorder digital data
2025-04-11 14:48:02 +02:00
9625a8058c Merge branch 'developer' into dev/issue_dont_reorder_digital_data 2025-04-11 14:30:11 +02:00
4d8bdae836 updated update_image_size in xilinx 2025-04-11 12:38:28 +02:00
3297707ab7 clang-format with clang-format version 17 2025-04-11 11:38:56 +02:00
598154645c changed font size in GUI 2025-04-11 11:03:52 +02:00
9d8f9a9ba9 autogenerated commands and make format 2025-04-11 10:45:02 +02:00
68f163b757 Merge pull request #1186 from slsdetectorgroup/dev/911/fix_m3_trimbits_badchannels
dev: m3: fix trimbits and badchannels
2025-04-11 10:40:46 +02:00
b8c5bb2045 Merge branch 'developer' into dev/911/fix_m3_trimbits_badchannels 2025-04-11 10:39:37 +02:00
b45df191e5 Merge pull request #1189 from slsdetectorgroup/dev/911/slsmultireceiver_verbose_size
dev: multi receiver: verbose option
2025-04-11 10:38:44 +02:00
01cc745787 update the comment about how to modify data on a data call back from the receiver 2025-04-11 10:38:17 +02:00
f9bc2eb126 removed Gotthard stuff 2025-04-11 10:32:31 +02:00
4c86ad3198 added sanity check to only enable for chipttestboard and xilinx 2025-04-11 10:27:26 +02:00
5be0724f82 got rid of Reorder function 2025-04-10 17:52:16 +02:00
7c652498e4 got rid of cast to uint64 2025-04-10 17:34:39 +02:00
721d536350 fixed warnings
Some checks failed
Native CMake Build / Configure and build using cmake (push) Failing after 13s
2025-04-10 13:31:47 +02:00
361437428d commenting out the example in receiver data call back changing size as it affects users using debugging mode to print out headers 2025-04-10 13:13:23 +02:00
aadbfeaf2d Merge branch 'developer' into dev/issue_dont_reorder_digital_data 2025-04-10 12:16:29 +02:00
f119d14e7c added check for proper memory allocation 2025-04-10 11:29:01 +02:00
f8b12201f8 binary in 2025-04-09 18:21:54 +02:00
585c92be66 Merge branch 'developer' into dev/911/fix_m3_trimbits_badchannels 2025-04-09 18:21:19 +02:00
7c8639b8ae formatting
Some checks failed
Native CMake Build / Configure and build using cmake (push) Failing after 12s
2025-04-09 18:20:58 +02:00
a138b5b365 m3 server fix for trimbits and badchannels that are shifted by 1 2025-04-09 18:10:01 +02:00
9fde62ae30 merged developer into feature and solved merge conflicts 2025-04-09 09:39:47 +02:00
29fe988583 imagedata is now allocated on the heap 2025-04-09 09:31:38 +02:00
6740d9b363 alignedData now uses std::align_alloc 2025-04-09 09:20:05 +02:00
1d1b55b864 changed documentation 2025-04-08 15:57:11 +02:00
c32732b22e Merge pull request #1177 from slsdetectorgroup/pattern_docu
Some checks failed
Native CMake Build / Configure and build using cmake (push) Failing after 14s
Pattern documentation
2025-04-08 11:29:20 +02:00
1e6b6fef0a Merge branch 'developer' into pattern_docu 2025-04-08 10:46:14 +02:00
5ab2c1693e Fixed broken import in typecaster.h (#1181)
Some checks failed
Native CMake Build / Configure and build using cmake (push) Failing after 28s
- Fixed the broken import _slsdet --> slsdet._slsdet caused by a previous upgrade
- Added tests that exercises the conversion from python to C++ and from C++ to python
- Python unit tests now run in CI (!)
2025-04-03 12:00:57 +02:00
0b3cd499a8 Dhanya's comments
Some checks failed
CMake / Configure and build using cmake (push) Failing after 11s
2025-04-02 10:18:57 +02:00
884e17f0c4 Merge pull request #1164 from slsdetectorgroup/dev/scikitbuild
Some checks failed
Native CMake Build / Configure and build using cmake (push) Failing after 10s
Dev/scikitbuild
2025-04-01 17:27:10 +02:00
d0ccf236c0 added sync, renamed action 2025-04-01 17:20:35 +02:00
396b955db7 Merge branch 'dev/scikitbuild' of github.com:slsdetectorgroup/slsDetectorPackage into dev/scikitbuild 2025-04-01 16:58:14 +02:00
5f14eb32aa added sls_detector bin 2025-04-01 16:57:47 +02:00
cfec7c18ec Merge branch 'developer' into dev/scikitbuild 2025-04-01 14:38:06 +02:00
04583acb21 reverted to scikit-build in pyproject.toml 2025-04-01 14:17:07 +02:00
95e11d668a switched patch tool
Some checks failed
Native CMake Build / Configure and build using cmake (push) Failing after 9s
2025-03-28 18:26:45 +01:00
772e58c743 Merge pull request #1179 from slsdetectorgroup/dev/pmod_910
Some checks failed
CMake / Configure and build using cmake (push) Failing after 9s
adding pmodules for 9.1.0 rhl8
2025-03-28 17:20:28 +01:00
470e2633c3 adding pmodules for 9.1.0 rhl8 2025-03-28 16:32:37 +01:00
3312adddd1 removed compiler version
Some checks failed
Native CMake Build / Configure and build using cmake (push) Failing after 9s
2025-03-28 15:43:09 +01:00
46152d2419 added mythen3 pattern word table
Some checks failed
CMake / Configure and build using cmake (push) Failing after 9s
2025-03-28 09:35:25 +01:00
b5c82783d6 patching libzmq and cleaned up cmake
Some checks failed
Native CMake Build / Configure and build using cmake (push) Failing after 9s
2025-03-28 09:26:55 +01:00
dc85a48864 update xilinxCtb pattern bit mapping
Some checks failed
CMake / Configure and build using cmake (push) Failing after 9s
2025-03-27 18:03:02 +01:00
dc8a34592a started pattern docu 2025-03-27 17:03:36 +01:00
96ae1a1cca found bug needed to refresh member variables
Some checks failed
CMake / Configure and build using cmake (push) Failing after 9s
2025-03-20 16:47:42 +01:00
3793e7b7d4 solved merge conflict 2025-03-20 16:16:35 +01:00
713e4f6822 added dbitreorder flag to chip test board gui 2025-03-20 16:12:01 +01:00
fada23365e added workflow for python lib
Some checks failed
Native CMake Build / Configure and build using cmake (push) Failing after 8s
2025-03-20 13:56:46 +01:00
6dd0a5b0dd added zlib 2025-03-20 13:39:51 +01:00
c3b197f209 removed conda build pin 2025-03-20 13:28:46 +01:00
9f49ac6457 fixed typo 2025-03-20 13:12:42 +01:00
0b3ead6353 conda build of main library 2025-03-20 13:10:27 +01:00
8b3625fc01 added dbitreorder flag to chip test board gui
Some checks failed
CMake / Configure and build using cmake (push) Failing after 7s
2025-03-20 11:00:40 +01:00
0da508a8b7 added back some python versions
Some checks failed
CMake / Configure and build using cmake (push) Failing after 9s
2025-03-19 22:05:19 +01:00
608eb1a436 cleaned meta yaml 2025-03-19 22:03:22 +01:00
c0bc6fe25a Merge branch 'dev/scikitbuild' of github.com:slsdetectorgroup/slsDetectorPackage into dev/scikitbuild 2025-03-19 22:00:00 +01:00
a0d540fd72 restored comments, cleanup 2025-03-19 21:59:30 +01:00
46a46b65e5 Merge branch 'developer' into dev/scikitbuild 2025-03-19 21:53:29 +01:00
4f62b1a05c separated the recipes 2025-03-19 21:49:12 +01:00
45dadf8b90 Merge pull request #1163 from slsdetectorgroup/dev/fix_tests_real_g2
Some checks failed
CMake / Configure and build using cmake (push) Failing after 8s
dev: fixed tests for real gotthard2. change in reg address to test
2025-03-19 17:16:00 +01:00
e8e84a4e72 fixed tests for real gotthard2. change in reg address to test 2025-03-19 17:12:21 +01:00
2f390971e6 WI{ 2025-03-19 16:37:37 +01:00
b7e17d1320 added reorder to documentation, added flag to master binary and hdf5 file
Some checks failed
CMake / Configure and build using cmake (push) Failing after 11s
2025-03-19 11:51:28 +01:00
c54b1cb6af clang format 2025-03-19 08:37:07 +01:00
ddb89bce34 Merge branch 'dev/issue_dont_reorder_digital_data' of github.com:slsdetectorgroup/slsDetectorPackage into dev/issue_dont_reorder_digital_data 2025-03-19 08:24:33 +01:00
f056f9d31b reserved enough size in the fifo buffer to reorder all added proper 4 byte alignment 2025-03-18 21:42:34 +01:00
3e2d34bec7 Merge branch 'developer' into jf_h5reader
Some checks failed
CMake / Configure and build using cmake (push) Failing after 14s
2025-03-18 18:57:21 +01:00
5e8a354194 roll back debug comments 2025-03-18 18:56:01 +01:00
8c35fc16b9 Implement copy constructor in analogDetector
- avoid default shallow copy of pointer members triggered by copy of derived class
- as a result avoid double-free bugs
2025-03-18 18:15:43 +01:00
ac0394e176 extensive debugging 2025-03-18 17:20:12 +01:00
d9a50ad9f4 WIP 2025-03-18 13:21:46 +01:00
ce0450d498 Merge pull request #1158 from slsdetectorgroup/dev/jf_timing_decoder_only_hw2.0
Some checks failed
CMake / Configure and build using cmake (push) Failing after 10s
get timin g info decoder not supported for jf 2.0
2025-03-18 12:35:23 +01:00
7b531059a0 even get is not supported for timing info decoder for jungfrau hw v1.0. only hw v2.0 is uspported 2025-03-18 12:33:07 +01:00
bc187bb198 moved compiled extension into slsdet 2025-03-18 10:56:03 +01:00
eb8c34f53b skeleton pyproject.toml 2025-03-18 10:33:51 +01:00
d9a50705e4 Merge pull request #1148 from slsdetectorgroup/dev/cmd_for_ctb_reorder
Some checks failed
CMake / Configure and build using cmake (push) Failing after 1m19s
Dev/cmd for ctb reorder
2025-03-17 17:10:52 +01:00
f4626c2c81 fix tests from merge 2025-03-17 15:45:59 +01:00
e0a48e1e75 implemented proper alignment in reorder function before casting to uint64_t ptr 2025-03-17 15:39:31 +01:00
ec4eb1978e merge fix from dev/issue_dont_reorder_digital_data 2025-03-17 15:28:22 +01:00
842b376801 fixed rx_dbitreorder cmd line tests 2025-03-17 15:23:02 +01:00
13b2cada66 ctb reorder default being true in dataprocessor 2025-03-17 15:20:40 +01:00
dd6354ff94 Merge branch 'developer' into jf_h5reader 2025-03-17 12:19:48 +01:00
3c2f149c22 Adding patterntools to slsdet (#1142)
* added patterntools from mythen3tools
* refactored do use implementation from slsSupportLib
2025-03-17 08:46:26 +01:00
0a5b5aac4b added unit tests for dataprocessor rearranging functions 2025-03-14 14:04:55 +01:00
18d781c35a Merge branch 'developer' into jf_h5reader 2025-03-14 12:03:21 +01:00
9c111fbeab deal with EuXFEL hdf5 (frames, 16)-size frame index datasets 2025-03-14 12:02:49 +01:00
222d9e4839 debug 2025-03-13 16:49:55 +01:00
9508dd1adc Debug 2025-03-13 16:14:12 +01:00
cf62f8cae8 Debug 2025-03-13 16:10:32 +01:00
08c10679e6 Debug 2025-03-13 16:06:50 +01:00
2b68b2158b Debug 2025-03-13 16:02:24 +01:00
6e5b058fc1 merge fix 2025-03-13 13:17:54 +01:00
ace2b3a938 Merge branch 'dev/issue_dont_reorder_digital_data' into dev/cmd_for_ctb_reorder 2025-03-13 13:16:29 +01:00
5a8213024e Merge pull request #1145 from slsdetectorgroup/dev/xilinx_ctb_api
add xilinx ctb api to --versions command
2025-03-13 13:15:04 +01:00
bd66228b30 minor to check workflow 2025-03-13 11:14:01 +01:00
721d296712 Merge branch 'developer' into jf_h5reader 2025-03-13 11:01:47 +01:00
62dd5c8d4e Enforce compiler support for c++17 standard 2025-03-13 11:01:20 +01:00
e1c9754cd2 Added test for rx_dbitreorder command 2025-03-12 17:31:37 +01:00
ff101e19cd added pybind for it 2025-03-12 17:31:33 +01:00
e8ac048114 ctb: added command 'rx_dbitreorder' that sets a flag in the receiver to set the reorder flag. By default it is 1. Setting to false means 'do not reorder' and to keep what the board spits out, which is that all signals in a sample are grouped together 2025-03-12 17:31:20 +01:00
3c79e8d7b2 trailing bits are removed even if reorder false and bitlist empty 2025-03-12 16:38:18 +01:00
a74fb2bcd1 added function Reorder 2025-03-12 16:12:00 +01:00
63bb79d727 added first rather generic test for Rearrange Function, could be changed to a parametrized test 2025-03-12 10:51:18 +01:00
8d87a6ee4e used clang-formating 2025-03-11 16:32:31 +01:00
ab01940769 add xilinx ctb api to --versions command 2025-03-11 15:51:46 +01:00
23aa9c2814 added reorder variable, changed function ArrangeDBitData to support reordering and no reordering. Moved transceiver data such that it is contiguous with rearranged digital data 2025-03-11 12:07:10 +01:00
35c37998f3 tweak file name 2025-03-11 11:55:53 +01:00
010f736e80 Merge pull request #1141 from slsdetectorgroup/dev/jf_firmware_dates_1.6_2.6
Dev/jf firmware dates 1.6 2.6
2025-03-11 09:35:39 +01:00
4dcbcad435 jungfrau server binary for fw 1.6 and 2.6 2025-03-11 09:34:37 +01:00
8f8a92b9c5 jungfrau firmware dates for fw v 1.6 and fw2.6 2025-03-11 09:32:44 +01:00
9b13ddf6be Merge branch 'developer' into jf_h5reader 2025-03-10 16:46:42 +01:00
49db2fbee7 Fix tiff filename 2025-03-10 16:46:04 +01:00
684eee984d removed CMakeFiles folder. mistakenly added to repo (#1137) 2025-03-10 14:27:08 +01:00
297c3752e3 Dev/remove gotthard i (#1108)
* slsSupportLib done, at receiver rooting out in implementation

* removed from receiver and client

* removed everywhere except gui, python and client(commands.yaml and Detector.h)

* updated python

* fixed autocomplete to print what the issue is if there is one with ToString when running the autocomplete script to generate fixed.json. updated readme.md in generator folder

* formatting

* removed enums for dacs

* udpating autocomplete and generating commands

* removed gotthard from docs and release notes

* removed dac test

* bug from removing g1

* fixed virtual test for xilinx, was minor. so in this PR

* gui done

* binary in merge fix

* formatting and removing enums

* updated fixed and dump.json

* bash autocomplete

* updated doc on command line generation

* removing increments in dac enums for backward compatibility. Not required

* removed ROI from rxParameters  (only in g1), not needed to be backward compatible

* removed the phase shift option from det server staruip
2025-03-10 14:24:33 +01:00
670d4dbce4 Merge branch 'developer' into jf_h5reader 2025-03-10 12:24:33 +01:00
a0e5304df6 Modify tiff file name 2025-03-10 12:23:51 +01:00
fa504e6675 default filepath is now an empty string, SetupWriter will throw an error if path not set (#1133)
Co-authored-by: Dhanya Thattil <dhanya.thattil@psi.ch>
2025-03-10 12:00:22 +01:00
2b8fbad47d reduce compiler warnings 2025-03-07 22:01:44 +01:00
a095a4ffce Fix double-free 2025-03-07 21:49:05 +01:00
d74da4cf04 Fix implicit conversion float->double compiler warning 2025-03-07 11:48:15 +01:00
9321d6d03a add SC to CMake 2025-03-06 20:32:45 +01:00
45a9a74137 Merge branch 'developer' into jf_h5reader 2025-03-06 17:14:17 +01:00
bede3003b6 Edit default rank 2025-03-06 17:06:45 +01:00
b9b4f1ae35 changed line 66 in docs/conf.py.in to app.add_css_file(filename) (#1131)
Co-authored-by: AliceMazzoleni99 <l_mazzol_a@pc17378.psi.ch>
2025-03-06 14:42:17 +01:00
3c2062f23e Dev/m3 default period 0 (#1127)
* m3: default period 0, merge fix from 9.1.0

* from previous commit
2025-03-04 16:07:28 +01:00
e7247f1fee formatting 2025-03-04 10:48:13 +01:00
905a509a17 update xilinx regs (#1123)
Co-authored-by: Martin Mueller <martin.mueller@psi.ch>
2025-03-04 10:38:14 +01:00
b4dc1dde6c patternX to pattern (#1120)
* pattern is not an issue for yaml. changing patternX to pattern everywhere

* added patternX to deprecated
2025-02-27 10:42:21 +01:00
964ab19b42 Dev/multi id ignored in config (#1115)
* warn that the multi id is ignored in config file? or throw?

* throw and not just warn with printouts as that could be silent as well

* throw for any multi id command in the file.
2025-02-27 10:41:17 +01:00
ce8911de10 when creating the python bindings for Detector class, it should ignore the assignment operators (#1107) 2025-02-26 12:21:06 +01:00
d1e5b0bc42 Dev: udpated help on multi module and multi command help (#1119)
* udpated help on multi module and multi command help

* fixed issues with empty lines and other syntax with docuemntation

* fixed some warningsin documentation

* some changes to documentation about command line usage

* minor
2025-02-26 11:14:15 +01:00
4b3ed22f76 jf binary in, jf: removed check to allow chipv1.0 also to set comp disable time (#1118) 2025-02-24 09:39:19 +01:00
aad1ab0cf4 file from Anna: could be reduce print out in zmq processing when using energy threshold (#1113) 2025-02-21 12:49:33 +01:00
117637863d Xilinxctb/update reg (#1084)
* updated RegisterDefs.h from firmware update

* Revert "updated RegisterDefs.h from firmware update"

This reverts commit 64f1b2546e.

* updated registers and had it formatted

* Revert "updated registers and had it formatted"

This reverts commit 1641b705b0.

* udpated registers from firmware, reading config file in server (chip config, reset chip, enable_clock_pattern) specific for matterhorn,this is done when powering on chip, removed startreadout, fixed status register bits, updated firmware version

* fix for patioctrl allowed for zxilinx and adding readout pattern for scientists that like to push the acquire button

* fixing default enable clock and readout pattern for xilinx (patioctrl has to be 32 bit)

* Xilinxctb/first image (#1094)

* reduce xilinxCTB readout done checks to single register, increased clockEna pattern limits, clear FPGA FiFos and counters on powerchip, disable counters 1-3 in matterhorn configuration

* change print of xilinxctb server

* remove acquisition done check

---------

Co-authored-by: Martin Mueller <martin.mueller@psi.ch>

* binary xilinx in

* formatting

* added reset of udp buffer FIFO to xilinxCTB

---------

Co-authored-by: Martin Mueller <72937414+mmarti04@users.noreply.github.com>
Co-authored-by: Martin Mueller <martin.mueller@psi.ch>
2025-02-18 11:30:51 +01:00
e933a25453 Dev/frame synchronizer (#968)
* skeleton of structure for callbacks and funcitons updated

* updated callback header structures and implemented in receiver

* fixed bugs

* minor

* formatting

* wip: draft of frame synchronizer, semaphores not done yet

* signal handler not affecting semaphore inside lambda function

* finally works with sem

* install targets cmake error fix

* removed modified callback and instead passing by reference instead of value to the oriignal receiver data callback

* reducing the number of data call backs. incoming from developer

* added json header to receiver start acquiistion call back

* WIP: of synchronisation (#969)

* WIP of synchronisation

* working so far if everything goes right

* added all information into json headers

* valid json

* allow frame synchronizer to have access to static libzmq when compiling on conda (libzeromq-devel not installed by default

* upto date with multirecieverapp for invalid arguments and help

* formatting

* remove warnings

* changes to print

* removed prints

* no need for print frames to be called

* minor

* commnet

* adding json header in start callback, imagesize in data callback and formatted

* startcallback returns an unused int (changed to exceptions and forgotten in last modification to callbacks likely)

* fixed sanitizer issues. 1 left for ctrl+C
- zmq_msg_t should be deleted, not freed. Same with the char arrays and semaphores.

* fixed sanitizer issues and made it more readable

* moving clearing old frames to new startacq just in case it has to process soem frames before the callback

* fix cherry-pick merge of fixing sanitizer thread issues but has start callbacks signature change.fixed

---------

Co-authored-by: Felix Engelmann <felix-github@nlogn.org>
2025-02-18 11:28:21 +01:00
f1f369b48c dev jf : bunch id decoder and auto comp disable (#1097)
* jf wip: bunch id decoder only in pcb v2.0 check and comments

* auto comp disable the same way for both chip versions. compdisabletime also available for 1.1 now

* fixed tests

* formatting

* binary in
2025-02-18 11:17:38 +01:00
6b149244d3 added documentation of order in hostname command in quick start guide, udp header format for row and column values and in command line hostname (#1099) 2025-02-18 11:11:33 +01:00
436d180e93 dont usleep if no transmission delay (#1101) 2025-02-18 11:10:06 +01:00
c43a4030a5 removed .str().c_str() (#1090) 2025-02-11 09:14:56 +01:00
5fd319725e include <mutex> 2025-02-10 20:05:13 +01:00
fb77bc7f1d Rework for mutex safety and RAII compliance 2025-02-10 20:01:36 +01:00
8a5bd21ecc Add constructor printout (debug) 2025-02-10 19:08:13 +01:00
cb99aa40cb Adapt main() for storage cell case 2025-02-10 17:45:05 +01:00
41bb7ca1ed Adapt file pointer setting + pedestal reading 2025-02-10 16:50:09 +01:00
d85d4f94d6 Adapt constructor for multithreaded storage cell use 2025-02-10 16:11:30 +01:00
ea288a1939 Globally implement Copy() function for deep copy of detector objects 2025-02-10 16:10:53 +01:00
be607e8a2e Fix missing includes 2025-02-07 21:49:06 +01:00
7396ceed54 Create copy constructor for singlePhotonDetector that creates a new mutex 2025-02-07 21:09:31 +01:00
243c555798 Remove explicit class member analogDetector<uint16_t>* dd[MAXTHREADS] 2025-02-07 16:32:09 +01:00
7b1e74f4a4 Adapt get and writeImage methods to storage cell data 2025-02-07 15:33:31 +01:00
fcbb87b71a Adapt multithreaded cluster finder to storage cell data - continued (non-functional) 2025-02-06 20:42:36 +01:00
3a1945116a First (unfinished) attempt to adapt multithreaded cluster finding to storage cell data; REVERT BACK TO PREVIOUS COMMIT IF THIS FAILS! 2025-02-05 20:17:46 +01:00
884104156b Minor debugging 2025-02-04 18:15:33 +01:00
830ce66a4c Remove redundant default constructor and destructor of HDF5File reader class 2025-02-03 18:52:39 +01:00
69e1702493 Clear up warnings 2025-02-03 18:36:57 +01:00
027534cc2f comments 2025-02-03 18:01:13 +01:00
633f83f9c7 Minor include cleanup 2025-02-03 17:35:50 +01:00
b73684612b Merge branch 'developer' into jf_h5reader 2025-02-03 17:29:11 +01:00
71ca4e9f0f Generalize HDF5 reader for 3- or 4-dimensional datasets 2025-02-03 17:28:33 +01:00
6e826d2840 Pyctbgui/taborder pattern (#1082)
* tab order for pattern tab
2025-01-31 17:05:58 +01:00
315d49f8df ctb: patwaittime and exptime (#1076)
* cli: patwaittime also takes time argument, api: patwaitclocks and patwaitinterval, tcp: patwaitinterval is 2 functions for set and get, patwaitclocks remains a single for backward compatibility with -1 for get, server (loadpattern): clks using member names (needs to be refactored). needs tobe discussed what to do with pattern files.

* all tests passed

* fixed test 
* exptime deprecated for ctb and xilinx

* pyctbgui..not there yet

* fixed in pyctbgui

* removed redundant warning for ctb and xilinx exptime in Detector class (already in module class handling all exptime signatures), patwait, patloop and patnloop have to be non inferrable commands because of support for old commands (level as suffix)

* fix formatting error from command line parsing

* fix tests for patwaittime
2025-01-31 16:48:32 +01:00
25854f7736 Init problem fixed, add comments 2024-12-03 16:56:47 +01:00
bc6814ad0b Initialize image for all threads 2024-12-03 16:23:24 +01:00
7608d6392c Merge branch 'developer' into jf_h5reader 2024-12-02 15:06:48 +01:00
adcaf6b3df Debug getImage() 2024-12-02 15:06:09 +01:00
577362f95e Merge branch 'developer' into jf_h5reader 2024-11-21 16:34:08 +01:00
cfa0b003ba include <memory> 2024-11-21 16:33:38 +01:00
266ace44a2 Include fmt a CMake dependency 2024-11-08 16:35:04 +01:00
a8a4cc641e Merge branch 'developer' into jf_h5reader 2024-11-08 12:01:43 +01:00
d2dead070c Revert previous datatype change to int 2024-11-08 12:01:08 +01:00
0583a0703c Datatype change long long int also for moenchZMqProcess; these datatype changes need to be discussed as they could have fruther impact! 2024-10-29 17:51:09 +01:00
19f4c07642 Datatype change long long int also for multiThreadedInterpolatingDetector; note that this change could have further impact on local code 2024-10-29 17:32:21 +01:00
b587e95717 Merge branch 'developer' into jf_h5reader 2024-10-29 12:03:17 +01:00
f3a3fb09bf Merge branch 'developer' into jf_h5reader 2024-10-11 17:15:33 +02:00
f0fc547c6f Fix segmentation fault caused by double delete of threads and resolve memory leaks 2024-10-11 17:14:55 +02:00
3150276bfe Add frame number 2024-10-11 17:12:55 +02:00
7b4740ced2 program runs (fixed detector size in jungfrauLGADStrixelsDataQuadH5.h) 2024-09-05 17:03:31 +02:00
16bfcc17ce Fetch json from remote 2024-09-02 18:16:57 +02:00
a9c366890b add vs code and rhel 8 2024-09-02 12:00:12 +02:00
4ae2edf686 Compiles 2024-08-28 12:10:04 +02:00
69fb5876cb almost compiles, error: undefined reference to vtable for jungfrauLGADStrixelsDataQuadH5 2024-08-26 18:50:56 +02:00
543b311c06 Merge branch 'developer' into jf_h5reader 2024-08-23 11:44:05 +02:00
669e5f3c7a intermediate 2024-08-23 11:43:32 +02:00
a6a4ea7f21 first draft 2024-08-07 11:05:23 +02:00
31f6a6de61 Merge branch 'developer' into jf_singlemodule 2024-08-06 13:58:16 +02:00
9d2d4f49d4 Merge branch 'developer' into jf_singlemodule 2024-07-26 15:56:23 +02:00
76ee52a3ca edit strixel mapping quad 2024-03-14 15:20:58 +01:00
732c54d273 Merge branch 'developer' into jf_singlemodule 2024-02-26 16:44:23 +01:00
c0cbccd7d4 minor 2024-02-26 16:41:58 +01:00
783b95b1f3 Merge branch 'developer' into jf_singlemodule 2024-02-01 16:45:46 +01:00
d8e3e38c33 Compile strixel quad in cluster finder 2024-02-01 16:41:57 +01:00
7a03e56f09 Data structures 2024-02-01 16:38:26 +01:00
9bc20ab9b4 modify RawDataProcess_filetxt 2023-10-06 12:29:48 +02:00
95b7be6842 modify RawDataProcess 2023-10-06 12:18:33 +02:00
51d34063ac create single chip data map 2023-10-06 12:08:26 +02:00
d2fcc1f344 Merge branch 'developer' into jf_json_rxroi 2023-10-06 12:02:16 +02:00
2b93ef4565 minor 2023-10-06 11:55:20 +02:00
78cbe8e660 fix jungfrauModuleData empty if statement 2023-08-04 16:12:27 +02:00
0ee54be252 rx_roi for module data 2023-08-04 15:31:30 +02:00
f90a9b7520 Read rx_ROI from json master 2023-08-03 18:25:43 +02:00
558c2de85c Merge branch 'developer' into jf_json_rxroi 2023-08-03 18:24:08 +02:00
877a648d9e Merge branch 'developer' into jf_json_rxroi 2023-07-28 12:18:29 +02:00
9ea5101299 Merge branch 'developer' into jf_json_rxroi 2023-07-20 16:56:38 +02:00
c57f10c242 Merge branch 'developer' into jf_json_rxroi 2023-07-20 16:40:53 +02:00
6d6b6b98df Fix strixel mapping and interpolation 2023-07-20 11:12:13 +02:00
218 changed files with 30318 additions and 30662 deletions

View File

@ -0,0 +1,29 @@
name: Build on RHEL8
on:
push:
workflow_dispatch:
permissions:
contents: read
jobs:
build:
runs-on: "ubuntu-latest"
container:
image: gitea.psi.ch/detectors/rhel8-detectors-dev
steps:
- name: Clone repository
run: |
echo Cloning ${{ github.ref_name }}
git clone https://${{secrets.GITHUB_TOKEN}}@gitea.psi.ch/${{ github.repository }}.git --branch=${{ github.ref_name }} .
- name: Build library
run: |
mkdir build && cd build
cmake .. -DSLS_USE_PYTHON=ON -DSLS_USE_TESTS=ON
make -j 2
- name: C++ unit tests
working-directory: ${{gitea.workspace}}/build
run: ctest

View File

@ -0,0 +1,27 @@
name: Build on RHEL9
on:
push:
workflow_dispatch:
permissions:
contents: read
jobs:
build:
runs-on: "ubuntu-latest"
container:
image: gitea.psi.ch/detectors/rhel9-detectors-dev
steps:
- uses: actions/checkout@v4
- name: Build library
run: |
mkdir build && cd build
cmake .. -DSLS_USE_PYTHON=ON -DSLS_USE_TESTS=ON
make -j 2
- name: C++ unit tests
working-directory: ${{gitea.workspace}}/build
run: ctest

View File

@ -1,4 +1,4 @@
name: CMake
name: Native CMake Build
on: [push, pull_request]
@ -14,7 +14,13 @@ jobs:
runs-on: ubuntu-latest
name: Configure and build using cmake
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: 3.12
cache: 'pip'
- run: pip install pytest numpy
- uses: awalsh128/cache-apt-pkgs-action@latest
with:
packages: libhdf5-dev qtbase5-dev qt5-qmake libqt5svg5-dev libpng-dev libtiff-dev
@ -27,12 +33,15 @@ jobs:
- name: Build
# Build your program with the given configuration
run: cmake --build ${{github.workspace}}/build -j2 --config ${{env.BUILD_TYPE}}
run: cmake --build ${{github.workspace}}/build -j4 --config ${{env.BUILD_TYPE}}
- name: Test
- name: C++ unit tests
working-directory: ${{github.workspace}}/build
# Execute tests defined by the CMake configuration.
# See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail
run: ctest -C ${{env.BUILD_TYPE}} -j1
- name: Python unit tests
working-directory: ${{github.workspace}}/build/bin
run: |
python -m pytest ${{github.workspace}}/python/tests

42
.github/workflows/conda_library.yaml vendored Normal file
View File

@ -0,0 +1,42 @@
name: Build slsdetlib
on: [pull_request]
jobs:
build:
strategy:
fail-fast: false
matrix:
platform: [ubuntu-latest, ] # macos-12, windows-2019]
python-version: ["3.12",]
runs-on: ${{ matrix.platform }}
# The setup-miniconda action needs this to activate miniconda
defaults:
run:
shell: "bash -l {0}"
steps:
- uses: actions/checkout@v4
- name: Get conda
uses: conda-incubator/setup-miniconda@v3.0.4
with:
python-version: ${{ matrix.python-version }}
channels: conda-forge
- name: Prepare
run: conda install conda-build conda-verify pytest anaconda-client
- name: Disable upload
run: conda config --set anaconda_upload no
- name: Build
run: conda build conda-recipes/main-library --output-folder build_output
- name: Upload all Conda packages
uses: actions/upload-artifact@v4
with:
name: conda-packages
path: build_output/** # Uploads all packages

42
.github/workflows/conda_python.yaml vendored Normal file
View File

@ -0,0 +1,42 @@
name: slsdet
on: [pull_request]
jobs:
build:
strategy:
fail-fast: false
matrix:
platform: [ubuntu-latest, ] # macos-12, windows-2019]
python-version: ["3.12",]
runs-on: ${{ matrix.platform }}
# The setup-miniconda action needs this to activate miniconda
defaults:
run:
shell: "bash -l {0}"
steps:
- uses: actions/checkout@v4
- name: Get conda
uses: conda-incubator/setup-miniconda@v3.0.4
with:
python-version: ${{ matrix.python-version }}
channels: conda-forge
- name: Prepare
run: conda install conda-build conda-verify pytest anaconda-client
- name: Disable upload
run: conda config --set anaconda_upload no
- name: Build
run: conda build conda-recipes/python-client --output-folder build_output
- name: Upload all Conda packages
uses: actions/upload-artifact@v4
with:
name: conda-packages
path: build_output/** # Uploads all packages

View File

@ -1 +0,0 @@
# This file is generated by cmake for dependency checking of the CMakeCache.txt file

View File

@ -1,6 +1,6 @@
# SPDX-License-Identifier: LGPL-3.0-or-other
# Copyright (C) 2021 Contributors to the SLS Detector Package
cmake_minimum_required(VERSION 3.14)
cmake_minimum_required(VERSION 3.15)
project(slsDetectorPackage)
# Read VERSION file into project version
@ -29,20 +29,44 @@ include(FetchContent)
option(SLS_FETCH_ZMQ_FROM_GITHUB "Fetch zmq from github" OFF)
option(SLS_FETCH_PYBIND11_FROM_GITHUB "Fetch pybind11 from github" OFF)
# Allow FetchContent_Populate to be called with a single argument
# otherwise deprecated warning is issued
# Note: From cmake 3.28 we can pass EXCLUDE_FROM_ALL to FetchContent_Declare
# and avoid direct use of Populate
if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.30")
cmake_policy(SET CMP0169 OLD)
endif()
# Patch libzmq to set minimum cmake version to 3.15 to avoid warnings
# with newer cmake versions
# Patch is applied in the FetchContent_Declare
set(SLS_LIBZMQ_VERSION "4.3.4")
find_program(PATCH_EXECUTABLE patch)
if(NOT PATCH_EXECUTABLE)
message(FATAL_ERROR "The 'patch' tool is required for patching lib zeromq. Please install it.")
endif()
if(SLS_FETCH_ZMQ_FROM_GITHUB)
# Opt in to pull down a zmq version from github instead of
# using the bundled verison
# using the bundled version
FetchContent_Declare(
libzmq
GIT_REPOSITORY https://github.com/zeromq/libzmq.git
GIT_TAG v4.3.4
GIT_TAG v${SLS_LIBZMQ_VERSION}
PATCH_COMMAND ${CMAKE_COMMAND} -E chdir <SOURCE_DIR> patch -p1 < ${CMAKE_CURRENT_SOURCE_DIR}/libs/libzmq/libzmq_cmake_version.patch
UPDATE_DISCONNECTED 1
)
else()
# Standard behaviour use libzmq included in this repo (libs/libzmq)
FetchContent_Declare(
libzmq
URL ${CMAKE_CURRENT_SOURCE_DIR}/libs/libzmq/libzmq-4.3.4.tar.gz
URL ${CMAKE_CURRENT_SOURCE_DIR}/libs/libzmq/libzmq-${SLS_LIBZMQ_VERSION}.tar.gz
URL_HASH MD5=cc20b769ac10afa352e5ed2769bb23b3
PATCH_COMMAND ${CMAKE_COMMAND} -E chdir <SOURCE_DIR> patch -p1 < ${CMAKE_CURRENT_SOURCE_DIR}/libs/libzmq/libzmq_cmake_version.patch
UPDATE_DISCONNECTED 1
)
endif()
@ -54,6 +78,11 @@ set(ENABLE_CPACK OFF CACHE BOOL "")
set(ENABLE_CLANG OFF CACHE BOOL "")
set(ENABLE_CURVE OFF CACHE BOOL "")
set(ENABLE_DRAFTS OFF CACHE BOOL "")
set(ENABLE_PRECOMPILED OFF CACHE BOOL "")
set(WITH_DOC OFF CACHE BOOL "")
set(WITH_DOCS OFF CACHE BOOL "")
# Using GetProperties and Populate to be able to exclude zmq
# from install (not possible with FetchContent_MakeAvailable(libzmq))
@ -191,7 +220,7 @@ endif()
# to control options for the libraries
if(NOT TARGET slsProjectOptions)
add_library(slsProjectOptions INTERFACE)
target_compile_features(slsProjectOptions INTERFACE cxx_std_11)
target_compile_features(slsProjectOptions INTERFACE cxx_std_17)
endif()
if (NOT TARGET slsProjectWarnings)
@ -249,8 +278,8 @@ endif()
if(SLS_USE_SANITIZER)
target_compile_options(slsProjectOptions INTERFACE -fsanitize=address,undefined -fno-omit-frame-pointer)
target_link_libraries(slsProjectOptions INTERFACE -fsanitize=address,undefined)
# target_compile_options(slsProjectOptions INTERFACE -fsanitize=thread)
# target_link_libraries(slsProjectOptions INTERFACE -fsanitize=thread)
#target_compile_options(slsProjectOptions INTERFACE -fsanitize=thread -fno-omit-frame-pointer)
#target_link_libraries(slsProjectOptions INTERFACE -fsanitize=thread)
endif()
@ -304,7 +333,8 @@ if (SLS_USE_INTEGRATION_TESTS)
endif (SLS_USE_INTEGRATION_TESTS)
if (SLS_USE_PYTHON)
find_package (Python 3.8 COMPONENTS Interpreter Development)
find_package (Python 3.8 COMPONENTS Interpreter Development.Module REQUIRED)
set(PYBIND11_FINDPYTHON ON) # Needed for RH8
if(SLS_FETCH_PYBIND11_FROM_GITHUB)
FetchContent_Declare(
pybind11
@ -329,9 +359,15 @@ if (SLS_USE_CTBGUI)
add_subdirectory(pyctbgui)
endif(SLS_USE_CTBGUI)
configure_file( .clang-tidy
${CMAKE_BINARY_DIR}/.clang-tidy
)
# Workaround for file note being copied to build directory
# when issuing a python -m build
# TODO! Proper fix
if(EXISTS ".clang-tidy")
configure_file(.clang-tidy
${CMAKE_BINARY_DIR}/.clang-tidy
)
endif()
if (SLS_BUILD_EXAMPLES)
add_subdirectory(sample)

View File

@ -39,7 +39,6 @@ This document describes the differences between vx.x.x and vx.0.2
Jungfrau 9.0.0
Mythen3 9.0.0
Gotthard2 9.0.0
Gotthard 9.0.0
Moench 9.0.0
@ -75,9 +74,6 @@ This document describes the differences between vx.x.x and vx.0.2
Moench 26.10.2023 (v2.0) (updated in 9.0.0)
Gotthard 08.02.2018 (50um and 25um Master)
09.02.2018 (25 um Slave)
Detector Upgrade
----------------
@ -90,8 +86,6 @@ This document describes the differences between vx.x.x and vx.0.2
Gotthard2 via command <.rbf>
Moench via command <.pof>
Gotthard cannot be upgraded remotely
Except Eiger,
upgrade
Using command 'programfpga' or

View File

@ -1,11 +0,0 @@
# SPDX-License-Identifier: LGPL-3.0-or-other
# Copyright (C) 2021 Contributors to the SLS Detector Package
echo "|<-------- starting python build"
cd python
# copy VERSION into slsdet for installation
cp ../VERSION slsdet/VERSION
${PYTHON} setup.py install

View File

@ -1,8 +0,0 @@
python:
- 3.8
- 3.9
- 3.10
- 3.11
- 3.12
- 3.13

View File

@ -1,11 +0,0 @@
# SPDX-License-Identifier: LGPL-3.0-or-other
# Copyright (C) 2021 Contributors to the SLS Detector Package
mkdir $PREFIX/lib
mkdir $PREFIX/bin
mkdir $PREFIX/include
cp build/bin/ctbGui $PREFIX/bin/.
cp build/bin/libctbRootLib.so $PREFIX/lib/.

View File

@ -1,125 +0,0 @@
package:
name: sls_detector_software
version: {{ environ.get('GIT_DESCRIBE_TAG', '') }}
source:
path: ..
build:
number: 0
binary_relocation: True
rpaths:
- lib/
requirements:
build:
- {{ compiler('c') }}
- {{compiler('cxx')}}
- cmake
- qt 5.*
- xorg-libx11
- xorg-libice
- xorg-libxext
- xorg-libsm
- xorg-libxau
- xorg-libxrender
- xorg-libxfixes
- {{ cdt('mesa-libgl-devel') }} # [linux]
- {{ cdt('mesa-libegl-devel') }} # [linux]
- {{ cdt('mesa-dri-drivers') }} # [linux]
- {{ cdt('libselinux') }} # [linux]
- {{ cdt('libxdamage') }} # [linux]
- {{ cdt('libxxf86vm') }} # [linux]
- expat
host:
- libstdcxx-ng
- libgcc-ng
- xorg-libx11
- xorg-libice
- xorg-libxext
- xorg-libsm
- xorg-libxau
- xorg-libxrender
- xorg-libxfixes
- expat
run:
- libstdcxx-ng
- libgcc-ng
outputs:
- name: slsdetlib
script: copy_lib.sh
requirements:
build:
- {{ compiler('c') }}
- {{compiler('cxx')}}
- libstdcxx-ng
- libgcc-ng
run:
- libstdcxx-ng
- libgcc-ng
- name: slsdet
script: build_pylib.sh
requirements:
build:
- python
- {{ compiler('c') }}
- {{compiler('cxx')}}
- {{ pin_subpackage('slsdetlib', exact=True) }}
- setuptools
- pybind11=2.13
host:
- python
- {{ pin_subpackage('slsdetlib', exact=True) }}
- setuptools
- pybind11=2.13
run:
- libstdcxx-ng
- libgcc-ng
- python
- numpy
- {{ pin_subpackage('slsdetlib', exact=True) }}
test:
imports:
- slsdet
- name: slsdetgui
script: copy_gui.sh
requirements:
build:
- {{ compiler('c') }}
- {{compiler('cxx')}}
- {{ pin_subpackage('slsdetlib', exact=True) }}
run:
- {{ pin_subpackage('slsdetlib', exact=True) }}
- qt 5.*
- expat
- name: moenchzmq
script: copy_moench.sh
requirements:
build:
- {{ compiler('c') }}
- {{compiler('cxx')}}
- {{ pin_subpackage('slsdetlib', exact=True) }}
run:
- {{ pin_subpackage('slsdetlib', exact=True) }}
- expat

View File

@ -1,3 +0,0 @@
# SPDX-License-Identifier: LGPL-3.0-or-other
# Copyright (C) 2021 Contributors to the SLS Detector Package
ctest -j2

View File

@ -8,7 +8,7 @@ if [ ! -d "install" ]; then
mkdir install
fi
cd build
cmake .. \
cmake .. -G Ninja \
-DCMAKE_PREFIX_PATH=$CONDA_PREFIX \
-DCMAKE_INSTALL_PREFIX=install \
-DSLS_USE_TEXTCLIENT=ON \
@ -18,7 +18,7 @@ cmake .. \
-DSLS_USE_TESTS=ON \
-DSLS_USE_PYTHON=OFF \
-DCMAKE_BUILD_TYPE=Release \
-DSLS_USE_HDF5=OFF\
-DSLS_USE_HDF5=OFF \
NCORES=$(getconf _NPROCESSORS_ONLN)
echo "Building using: ${NCORES} cores"

View File

@ -0,0 +1,13 @@
c_compiler:
- gcc # [linux]
c_stdlib:
- sysroot # [linux]
cxx_compiler:
- gxx # [linux]
c_stdlib_version: # [linux]
- 2.17 # [linux]

View File

@ -4,7 +4,6 @@
mkdir -p $PREFIX/lib
mkdir -p $PREFIX/bin
mkdir -p $PREFIX/include/sls
# mkdir $PREFIX/include/slsDetectorPackage
#Shared and static libraries
cp build/install/lib/* $PREFIX/lib/
@ -15,8 +14,10 @@ cp build/install/bin/sls_detector_acquire_zmq $PREFIX/bin/.
cp build/install/bin/sls_detector_get $PREFIX/bin/.
cp build/install/bin/sls_detector_put $PREFIX/bin/.
cp build/install/bin/sls_detector_help $PREFIX/bin/.
cp build/install/bin/sls_detector $PREFIX/bin/.
cp build/install/bin/slsReceiver $PREFIX/bin/.
cp build/install/bin/slsMultiReceiver $PREFIX/bin/.
cp build/install/bin/slsFrameSynchronizer $PREFIX/bin/.
cp build/install/include/sls/* $PREFIX/include/sls

View File

@ -0,0 +1,77 @@
source:
path: ../..
{% set version = load_file_regex(load_file = 'VERSION', regex_pattern = '(\d+(?:\.\d+)*(?:[\+\w\.]+))').group(1) %}
package:
name: sls_detector_software
version: {{ version }}
build:
number: 0
binary_relocation: True
rpaths:
- lib/
requirements:
build:
- {{ compiler('c') }}
- {{ stdlib("c") }}
- {{ compiler('cxx') }}
- git
- cmake
- ninja
- qt 5.*
host:
- libstdcxx-ng
- libgcc-ng
- libgl-devel # [linux]
- libtiff
- zlib
run:
- libstdcxx-ng
- libgcc-ng
outputs:
- name: slsdetlib
script: copy_lib.sh
requirements:
build:
- {{ compiler('c') }}
- {{ stdlib("c") }}
- {{ compiler('cxx') }}
run:
- libstdcxx-ng
- libgcc-ng
- name: slsdetgui
script: copy_gui.sh
requirements:
build:
- {{ compiler('c') }}
- {{compiler('cxx')}}
- {{ pin_subpackage('slsdetlib', exact=True) }}
run:
- {{ pin_subpackage('slsdetlib', exact=True) }}
- qt 5.*
- name: moenchzmq
script: copy_moench.sh
requirements:
build:
- {{ compiler('c') }}
- {{compiler('cxx')}}
- {{ pin_subpackage('slsdetlib', exact=True) }}
run:
- {{ pin_subpackage('slsdetlib', exact=True) }}

View File

@ -0,0 +1,16 @@
python:
- 3.11
- 3.12
- 3.13
c_compiler:
- gcc # [linux]
c_stdlib:
- sysroot # [linux]
cxx_compiler:
- gxx # [linux]
c_stdlib_version: # [linux]
- 2.17 # [linux]

View File

@ -0,0 +1,45 @@
source:
path: ../..
{% set version = load_file_regex(load_file = 'VERSION', regex_pattern = '(\d+(?:\.\d+)*(?:[\+\w\.]+))').group(1) %}
package:
name: slsdet
version: {{ version }}
build:
number: 0
script:
- unset CMAKE_GENERATOR && {{ PYTHON }} -m pip install . -vv # [not win]
requirements:
build:
- python {{python}}
- {{ compiler('c') }}
- {{ stdlib("c") }}
- {{ compiler('cxx') }}
host:
- cmake
- ninja
- python {{python}}
- pip
- scikit-build-core
- pybind11 >=2.13.0
- fmt
- zeromq
- nlohmann_json
- catch2
run:
- python {{python}}
- numpy
test:
imports:
- slsdet
about:
summary: An example project built with pybind11 and scikit-build.
# license_file: LICENSE

View File

@ -40,6 +40,7 @@ set(SPHINX_SOURCE_FILES
src/pydetector.rst
src/pyenums.rst
src/pyexamples.rst
src/pyPatternGenerator.rst
src/servers.rst
src/receiver_api.rst
src/result.rst
@ -53,6 +54,7 @@ set(SPHINX_SOURCE_FILES
src/serverdefaults.rst
src/quick_start_guide.rst
src/troubleshooting.rst
src/pattern.rst
src/receivers.rst
src/slsreceiver.rst
src/udpheader.rst

View File

@ -63,4 +63,4 @@ html_static_path = ['static']
def setup(app):
app.add_stylesheet('css/extra.css') # may also be an URL
app.add_css_file('css/extra.css') # may also be an URL

View File

@ -21,7 +21,7 @@ print('\n\n\n\n SERVER CSV')
src = Path('@CMAKE_SOURCE_DIR@')/'slsDetectorServers/'
detectors = ['Mythen3', 'Gotthard2', 'Eiger',
'Jungfrau', 'Moench', 'Gotthard', 'Ctb']
'Jungfrau', 'Moench', 'Ctb']
for det in detectors:

View File

@ -320,61 +320,6 @@ Moench
}
}
Gotthard I
^^^^^^^^^^^
.. code-block:: text
{
"Version": 7.2,
"Timestamp": "Wed Nov 13 15:16:19 2024",
"Detector Type": "Gotthard",
"Timing Mode": "auto",
"Geometry": {
"x": 1,
"y": 1
},
"Image Size in bytes": 2560,
"Pixels": {
"x": 1280,
"y": 1
},
"Max Frames Per File": 20000,
"Frame Discard Policy": "nodiscard",
"Frame Padding": 1,
"Scan Parameters": "[disabled]",
"Total Frames": 1,
"Receiver Roi": {
"xmin": 4294967295,
"xmax": 4294967295,
"ymin": 4294967295,
"ymax": 4294967295
},
"Exptime": "1.00001ms",
"Period": "1s",
"Detector Roi": {
"xmin": 4294967295,
"xmax": 4294967295
},
"Frames in File": 1,
"Frame Header Format": {
"Frame Number": "8 bytes",
"SubFrame Number/ExpLength": "4 bytes",
"Packet Number": "4 bytes",
"Bunch ID": "8 bytes",
"Timestamp": "8 bytes",
"Module Id": "2 bytes",
"Row": "2 bytes",
"Column": "2 bytes",
"Reserved": "2 bytes",
"Debug": "4 bytes",
"Round Robin Number": "2 bytes",
"Detector Type": "1 byte",
"Header Version": "1 byte",
"Packets Caught Mask": "64 bytes"
}
}
Chip Test Board
^^^^^^^^^^^^^^^
@ -414,6 +359,7 @@ Chip Test Board
"Digital Flag": 0,
"Digital Samples": 1000,
"Dbit Offset": 0,
"Dbit Reorder": 1,
"Dbit Bitset": 0,
"Transceiver Mask": "0x3",
"Transceiver Flag": 0,

View File

@ -4,11 +4,54 @@ Command line interface
Usage
-------------
Commands can be used either with sls_detector_get or sls_detector_put
The syntax is *'[detector index]-[module index]:[command]'*, where the indices are by default '0', when not specified.
Module index
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Modules are indexed based on their order in the hostname command. They are used to configure a specific module within a detector and are followed by a ':' in syntax.
.. code-block::
# Applies to all modules of detector 0
sls_detector_put exptime 5s
# Applies to only the 4th module
sls_detector_put 3:exptime 5s
Detector index
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
This index is useful when configuring multiple detectors from a single host. Each detector uses a unique shared memory identified by a detector index, derived again from the hostname command. It is followed by a '-'.
.. code-block::
# For detector with index 2 in shared memory
sls_detector_put 2-hostname bchip133+bchip123+bchip456
# Without '-', the detector index defaults to 0
sls_detector_put hostname bchip133+bchip123+bchip456
# Accessing all modules with detector index 2
sls_detector_put 2-exptime
# Starting acquisition only for detector with index 2
sls_detector_put 2-start
# Applies only to the 2nd detector, 4th module
sls_detector_put 1-3:exptime 5s
Command Execution
^^^^^^^^^^^^^^^^^^^^^^^
Commands can be executed using:
* **sls_detector_put**: setting values
* **sls_detector_get**: getting values
* **sls_detector**: automatically infers based on the number of arguments.
* **sls_detector_help**: gets help on the specific command
* **sls_detector_acquire**: initiates acquisition with the detector. This command blocks until the entire acquisition process is completed.
.. code-block::
sls_detector_get exptime
Help
--------
@ -28,12 +71,15 @@ Help
# list of deprecated commands
list deprecated
# autocompletion
# bash_autocomplete.sh or zsh_autocomplete.sh must be sourced from the
# main package folder to enable auto completion of commands and arguments
# for the command line on that shell.
source bash_autocomplete.sh
Autocompletion
---------------
bash_autocomplete.sh or zsh_autocomplete.sh must be sourced from the main package folder to enable auto completion of commands and arguments for the command line on that shell.
.. code-block::
source bash_autocomplete.sh
Commands

View File

@ -13,7 +13,6 @@ containing results from all modules. (:ref:`Result class<Result Class>`)
Here are some :ref:`examples <Cplusplus Api Examples>` on how to use the API.
.. _Cplusplus Api Examples:
.. doxygenclass:: sls::Detector
:members:
:undoc-members:

View File

@ -131,42 +131,6 @@ Program from console
Gotthard I
-----------
Download
^^^^^^^^^^^^^
- detector server corresponding to package in slsDetectorPackage/serverBin
- `pof files <https://github.com/slsdetectorgroup/slsDetectorFirmware>`__
.. _firmware upgrade using blaster for blackfin:
Upgrade
^^^^^^^^
.. warning ::
| Gotthard firmware cannot be upgraded remotely and requires the use of USB-Blaster.
| It is generally updated by us.
#. Download `Altera Quartus software or Quartus programmer <https://fpgasoftware.intel.com/20.1/?edition=standard&platform=linux&product=qprogrammer#tabs-4>`__.
#. Start Quartus programmer, click on Hardware Setup. In the "Currently selected hardware" window, select USB-Blaster.
#. In the Mode combo box, select "Active Serial Programming".
#. Plug the end of your USB-Blaster with the adaptor provided to the connector 'AS config' on the Gotthard board.
#. Click on 'Add file'. Select programming (pof) file provided by us.
#. Check "Program/Configure" and "Verify". Push the start button. Wait until the programming process is finished.
#. In case of error messages, check the polarity of cable (that pin1 corresponds) and that the correct programming connector is selected.
#. Reboot the detector.
Mythen III
-----------
@ -396,7 +360,3 @@ How to get back mtd3 drive remotely (udpating kernel)
more /proc/mtd # verify mtd3 is listed
Last Resort using USB Blaster
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
If none of these steps work, the last resort might be physically upgrading the firmware using a USB blaster, which also requires opening up the detector. Instructions for all the blackfin detectors are the same as the one for :ref:`gotthard firmware upgrade <firmware upgrade using blaster for blackfin>`.

View File

@ -36,10 +36,12 @@ Welcome to slsDetectorPackage's documentation!
pydetector
pyenums
pyexamples
pyPatternGenerator
pattern
.. toctree::
:caption: Command line
:maxdepth: 2
:maxdepth: 1
commandline
quick_start_guide

View File

@ -1,5 +1,6 @@
.. _master file attributes:
Master File Attributes
=======================
@ -287,56 +288,6 @@ Moench
| Frame Header Format | Expected frame header format for the data files |
+-----------------------+-------------------------------------------------+
Gotthard I
^^^^^^^^^^^
+-----------------------+-------------------------------------------------+
| **Key** | **Description** |
+-----------------------+-------------------------------------------------+
| Version | Version of the master file |
| | Current value:8.0 |
+-----------------------+-------------------------------------------------+
| Timestamp | Timestamp of creation of master file |
+-----------------------+-------------------------------------------------+
| Detector Type | Detector type |
+-----------------------+-------------------------------------------------+
| Timing Mode | Timing Mode |
+-----------------------+-------------------------------------------------+
| Geometry | Number of UDP ports in x and y dimension for |
| | complete detector |
+-----------------------+-------------------------------------------------+
| Image Size in bytes | Image size in bytes per UDP port |
+-----------------------+-------------------------------------------------+
| Pixels | Number of pixels in x and y dimension |
| | per UDP port |
+-----------------------+-------------------------------------------------+
| Max Frames Per File | Maximum frames per file |
+-----------------------+-------------------------------------------------+
| Frame Discard Policy | Receiever Frame discard policy |
| | for partial frames |
+-----------------------+-------------------------------------------------+
| Frame Padding | Receiver Frame padding enable |
| | for partial frames |
+-----------------------+-------------------------------------------------+
| Scan Parameters | Scanning mode on detector |
+-----------------------+-------------------------------------------------+
| Total Frames | Total number of frames and triggers expected |
+-----------------------+-------------------------------------------------+
| Receiver Roi | Receiver ROI in file including xmax and ymax |
+-----------------------+-------------------------------------------------+
| Exptime | Exposure time |
+-----------------------+-------------------------------------------------+
| Period | Period between frames |
+-----------------------+-------------------------------------------------+
| Detector Roi | Roi in detector restricted to an ADC. |
| | Includes xmax |
+-----------------------+-------------------------------------------------+
| Burst Mode | Burst mode of detector |
+-----------------------+-------------------------------------------------+
| Frames in File | Number of frames written to file by Receiver 0 |
+-----------------------+-------------------------------------------------+
| Frame Header Format | Expected frame header format for the data files |
+-----------------------+-------------------------------------------------+
Chip Test Board
^^^^^^^^^^^^^^^
@ -394,6 +345,9 @@ Chip Test Board
+-----------------------+-------------------------------------------------+
| Dbit Offset | Digital offset of valid data in bytes |
+-----------------------+-------------------------------------------------+
| Dbit Reorder | Reorder such that it groups each signal (0-63) |
| | from all the different samples together |
+-----------------------+-------------------------------------------------+
| Dbit Bitset | Digital 64 bit mask of bits enabled in receiver |
+-----------------------+-------------------------------------------------+
| Transceiver Mask | Mask of channels enabled in Transceiver |

122
docs/src/pattern.rst Normal file
View File

@ -0,0 +1,122 @@
Pattern
========================
This is a test and development feature implemented only for Ctb, Xilinx_Ctb and Mythen3.
A pattern is a sequence of 64-bit words which is executed using a clock on the FPGA. Each of the 64 bits is connected to a pin or internal signal of the FPGA. The purpose of a pattern is to provide a way to change these 64 signals with precise timing. Commands run by the detector server could manipulate the same signals as the pattern, but they cannot enforce a change in a specific clock cycle.
**Usage**
A pattern is written to memory on the FPGA using the patword command.
.. code-block::
patword 0x0000 0x000000000000000A
patword 0x0001 0x000000000000000B
patword 0x0002 0x000000000000000C
patword 0x0003 0x000000000000000D
patword 0x0004 0x000000000000000E
The example above writes a five-word pattern into FPGA memory. The first argument is the memory address, the second argument is the content to be written into this address. Before executing a pattern one has to set the address limits of the pattern:
.. code-block::
patlimits 0x0000 0x0004
This instructs the firmware to execute the commands from address 0 to 4 (including 0 and 4). The execution can be started from the pyctbgui or with the commands
.. code-block::
start [Ctb, Xilinx_Ctb]
patternstart [Mythen3]
The maximal number of patword addresses is 8192. However, it is possible to extend the length of the pattern sequence using loops and wait commands. Loops can be configured with the following commands:
.. code-block::
patloop 0 0x0001 0x0003
patnloop 0 7
The first argument of both commands is the ID of the loop. Ctb and Xilinx_Ctb can have 6 loops (ID 0-5), Mythen3 can have 4 loop definitions. The commands above configure the loop with ID 0 to run 7 times and jump from the patword with address 3 to the patword with address 1. Important: If patnloop is set to 1, the addresses 0x1-0x3 will execute exactly once; if it is set to 0, the pattern addresses will be skipped.
The same idea is used to introduce wait times. The example below causes the patword at address 0x0002 to be active for 9 clock cycles before the execution continues.
.. code-block::
patwait 0 0x0002
patwaittime 0 9
Waits can be placed inside a loop and loops can be nested.
**patioctrl**
The function of each bit in the sequence of 64-bit words depends on the connected detector and firmware version. Some of the 64 bits might connect directly to pads of a chip. The patioctrl command is used to configure the direction of some of these signals (not all of them !! See tables below). Signals where the corresponding bit in the argument of patioctrl is set to 1 will be driven from the FPGA.
**patsetbit and patmask**
The functions patsetbit and patmask can be used to ignore a specific bit of the pattern.
Example:
.. code-block::
patmask 0x0101
patsetbit 0x0001
Patmask configures bit 0 and 8 of the pattern to be set to their value in patsetbit. These bits will be ignored during pattern execution and will always be 0 (bit 8) and 1 (bit 0).
The mappings of bit positions in the pattern word to signals/pads of the FPGA are listed below for the three detector types where patterns are used. In the case of the two CTB's, connections of the signals to actual pads of a chip depend on the layout of the used detector adapter board. Therefore, each type of detector adapter board adds an additional mapping layer.
**CTB Pattern Bit Mapping**
.. table::
+----+---+------+----+----------+-------------------+----------------+
| 63 | 62| 61-57| 56 | 55-48 | 47-32 | 31-0 |
+----+---+------+----+----------+-------------------+----------------+
| A | D| --- | T | EXTIO | DO, stream source | DIO |
+----+---+------+----+----------+-------------------+----------------+
DIO: Driving the 32 FPGA pins corresponding to the lowest 32 bits of the patioctrl command. If bits in patioctrl are 0, the same bit positions in DIO will switch to input pins and connect to dbit sampling. Additionally, some of these 32 bits have an automatic override by detector-specific statemachines which is active whenever one of these statemachines is running (currently bits 7,8,11,14 and 20).
DO: Directly connected to 16 FPGA pins. Output only. Not influenced by patioctrl. Also connected to bit 47-32 in all Ctb dbit samples. All of them can be used as dbit sample trigger. In addition, every bit of DO can be selected as trigger for sending out a udp packet with samples to the receiver.
EXTIO: Similar to DIO, but not used as input to the fpga. With the corresponding patioctrl bits set to 0 these pins will switch to a high impedance mode and be ignored by the firmware.
T: trigger output
D: enable signal for digital sampling
A: adc enable
**Xilinx_CTB Pattern Bit Mapping**
.. table::
+-------+----------------+
| 63-32 | 31-0 |
+-------+----------------+
| --- | DIO |
+-------+----------------+
DIO: Driving the 32 FPGA pins corresponding to the lowest 32 bits of the patioctrl command. If bits in patioctrl are 0, the same bit positions in DIO will switch to input pins and connect to dbit sampling. Additionally, some of these 32 bits have an automatic override by detector-specific statemachines which is active whenever these sm's are running (currently bits 7,8,11,14 and 20).
**Mythen3 Pattern Bit Mapping**
.. table::
+-------+--------+-------+--------+------------+----------+----------+-----+-----+
| 63-33 | 32 | 31-25 | 24 | 23 | 22 | 21 | 20 | 19 |
+-------+--------+-------+--------+------------+----------+----------+-----+-----+
| --- | signARD| --- | CHSclk | cnt_rst | sto_rst | STATLOAD | STO | SIN |
+-------+--------+-------+--------+------------+----------+----------+-----+-----+
.. table::
+---------+-----+-------+-------+----+-------+---------+--------+
| 18 | 17 | 16-14 | 13 | 12 | 11 | 10 | 9-0 |
+---------+-----+-------+-------+----+-------+---------+--------+
| SR_MODE | clk | EN | PULSE | RD | CHSIN | ANAMode | TBLOAD |
+---------+-----+-------+-------+----+-------+---------+--------+
For Mythen3 the pattern word only connects to output pins of the FPGA when the pattern is running. Afterwards the signals will switch back to other logic in the FPGA. Both CTB's hold the last executed pattern word until a new pattern is started.

View File

@ -0,0 +1,34 @@
PatternGenerator
=====================================================
Python class to generate patterns for the Chip Test Board.
.. code-block:: python
from slsdet import PatternGenerator
p = PatternGenerator()
p.SB(5)
p.PW()
p.SB(8,9)
p.PW()
p.CB(5)
Created a pattern like this:
.. code-block:: bash
patword 0x0000 0x0000000000000020
patword 0x0001 0x0000000000000320
patword 0x0002 0x0000000000000300
patioctrl 0x0000000000000000
patlimits 0x0000 0x0002
...
.. py:currentmodule:: slsdet
.. autoclass:: PatternGenerator
:members:
:undoc-members:
:show-inheritance:
:inherited-members:

View File

@ -128,6 +128,10 @@ For Multiple Modules
# set file path
fpath /tmp
.. note ::
The **hostname** and **detsize** command in a multi module system can affect the row and column values in the udp/zmq header. The modules are stacked row by row until they reach the y-axis limit set by detsize (if specified). Then, stacking continues in the next column and so on.
Gui
----

View File

@ -89,18 +89,3 @@ DACS
:widths: 35, 35
:header-rows: 1
Gotthard
-------------
.. csv-table:: Default values
:file: gotthard.csv
:widths: 35, 35
:header-rows: 1
DACS
^^^^^^^^^^^^^
.. csv-table:: Gotthard DACS
:file: gotthard-dacs.csv
:widths: 35, 35
:header-rows: 1

View File

@ -27,25 +27,24 @@ Arguments
-p, --port <port> : TCP communication port with client.
-g, --nomodule : [Mythen3][Gotthard2]
Generic or No Module mode. Skips detector type checks.
-f, --phaseshift <value> : [Gotthard] only. Sets phase shift.
-d, --devel : Developer mode. Skips firmware checks.
-u, --update : Update mode. Skips firmware checks and initial detector setup.
-i, --ignore-config : [Eiger][Jungfrau][Gotthard][Gotthard2][Moench]
-i, --ignore-config : [Eiger][Jungfrau][Gotthard2][Moench]
Ignore config file.
-m, --master <master> : [Eiger][Mythen3][Gotthard][Gotthard2]
-m, --master <master> : [Eiger][Mythen3][Gotthard2]
Set Master to 0 or 1. Precedence over config file. Only for virtual servers except Eiger.
-t, --top <top> : [Eiger] Set Top to 0 or 1. Precedence over config file.
-s, --stopserver : Stop server. Do not use as it is created by control server
.. _Automatic start servers:
Automatic start
------------------
One can start the on-board detector server automatically upon powering on the board.
#. Create a soft link to the binary on board
:
#. Create a soft link to the binary on board:
.. code-block:: bash
ln -sf someDetectorServervx.x.x someDetectorServer
@ -65,7 +64,7 @@ One can start the on-board detector server automatically upon powering on the bo
/home/root/executables/eigerDetectorServer &> /dev/null &
exit 0
Jungfrau | Moench | CTB | Gotthard I
Jungfrau | Moench | CTB
.. code-block:: bash
# Edit inittab on board
@ -87,8 +86,7 @@ One can start the on-board detector server automatically upon powering on the bo
/root/xxxDetectorServer >> /dev/null &
#. Sync, reboot and verify
:
#. Sync, reboot and verify:
.. code-block:: bash
sync

View File

@ -385,14 +385,6 @@ Cannot get data without a module attached
You cannot get data without a module attached as a specific pin is floating. Attach module to get data.
Gotthard
----------
Missing first frame or next frame after a delay
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Connect the data link from the Module directly to receiver pc or to a private network.
Mythen3
--------

View File

@ -73,9 +73,9 @@ Description
* **modId**: module ID picked up from det_id_[detector type].txt on the detector cpu.
* **row**: row position of the module in the detector system. It is calculated by the order of the module in hostname command, as well as the detsize command.
* **row**: row position of the module in the detector system. It is calculated by the order of the module in hostname command, as well as the detsize command. The modules are stacked row by row until they reach the y-axis limit set by detsize (if specified). Then, stacking continues in the next column and so on.
* **column**: column position of the module in the detector system. It is calculated by the order of the module in hostname command, as well as the detsize command.
* **column**: column position of the module in the detector system. It is calculated by the order of the module in hostname command, as well as the detsize command. The modules are stacked row by row until they reach the y-axis limit set by detsize (if specified). Then, stacking continues in the next column and so on.
* **detType**: detector type from enum of detectorType in the package.
@ -92,14 +92,14 @@ Detector Enum
================ ========
GENERIC 0
EIGER 1
GOTTHARD 2
GOTTHARD* 2
JUNGFRAU 3
CHIPTESTBOARD 4
MOENCH 5
MYTHEN3 6
GOTTHARD2 7
================ ========
* deprecated since v10.0.0
Previous Versions

View File

@ -22,7 +22,6 @@ Binaries
eigerDetectorServer_virtual
jungfrauDetectorServer_virtual
gotthardDetectorServer_virtual
gotthard2DetectorServer_virtual
mythen3DetectorServer_virtual
moenchDetectorServer_virtual

View File

@ -1,12 +0,0 @@
# detector hostname
hostname bchip007
# receiver pc hostname of 1Gb IP of the machine
rx_hostname my_receiver_hostname
# output directory
fpath /bigRAID/datadir_gotthard/rec_test_data
# high voltage
highvoltage 120

View File

@ -0,0 +1,18 @@
diff --git a/CMakeLists.txt b/CMakeLists.txt
index dd3d8eb9..c0187747 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,11 +1,8 @@
# CMake build script for ZeroMQ
project(ZeroMQ)
-if(${CMAKE_SYSTEM_NAME} STREQUAL Darwin)
- cmake_minimum_required(VERSION 3.0.2)
-else()
- cmake_minimum_required(VERSION 2.8.12)
-endif()
+cmake_minimum_required(VERSION 3.15)
+message(STATUS "Patched cmake version")
include(CheckIncludeFiles)
include(CheckCCompilerFlag)

View File

@ -12,5 +12,6 @@ slsDetectorPackage/8.0.1_rh8 stable cmake/3.15.5 Qt/5.12.10
slsDetectorPackage/8.0.2_rh7 stable cmake/3.15.5 Qt/5.12.10
slsDetectorPackage/8.0.2_rh8 stable cmake/3.15.5 Qt/5.12.10
slsDetectorPackage/9.0.0_rh8 stable cmake/3.15.5 Qt/5.12.10
slsDetectorPackage/9.1.0_rh8 stable cmake/3.15.5 Qt/5.12.10

View File

@ -10,6 +10,7 @@ from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as Navigatio
from pyctbgui.utils.defines import Defines
from pyctbgui.utils.plotPattern import PlotPattern
from slsdet import DurationWrapper
class PatternTab(QtWidgets.QWidget):
@ -61,7 +62,10 @@ class PatternTab(QtWidgets.QWidget):
getattr(self.view, f"lineEditLoop{i}Wait").editingFinished.connect(partial(self.setPatLoopWaitAddress, i))
getattr(self.view,
f"spinBoxLoop{i}Repetition").editingFinished.connect(partial(self.setPatLoopRepetition, i))
getattr(self.view, f"spinBoxLoop{i}WaitTime").editingFinished.connect(partial(self.setPatLoopWaitTime, i))
getattr(self.view, f"doubleSpinBoxLoop{i}WaitClocks").editingFinished.connect(partial(self.setPatLoopWaitClocks, i))
getattr(self.view, f"spinBoxLoop{i}WaitInterval").editingFinished.connect(partial(self.setPatLoopWaitInterval, i))
getattr(self.view, f"comboBoxLoop{i}WaitInterval").currentIndexChanged.connect(partial(self.setPatLoopWaitInterval, i))
self.view.toolButtonTogglePageWaitTime.clicked.connect(self.setTogglePageWaitTime)
self.view.pushButtonCompiler.clicked.connect(self.setCompiler)
self.view.pushButtonUncompiled.clicked.connect(self.setUncompiledPatternFile)
self.view.pushButtonPatternFile.clicked.connect(self.setPatternFile)
@ -91,7 +95,8 @@ class PatternTab(QtWidgets.QWidget):
self.getPatLoopStartStopAddress(i)
self.getPatLoopWaitAddress(i)
self.getPatLoopRepetition(i)
self.getPatLoopWaitTime(i)
self.getPatLoopWaitClocks(i)
self.getPatLoopWaitInterval(i)
# Pattern Tab functions
@ -182,17 +187,67 @@ class PatternTab(QtWidgets.QWidget):
self.det.patnloop[level] = spinBox.value()
self.getPatLoopRepetition(level)
def getPatLoopWaitTime(self, level):
def getPatLoopWaitClocks(self, level):
retval = self.det.patwaittime[level]
spinBox = getattr(self.view, f"spinBoxLoop{level}WaitTime")
spinBox = getattr(self.view, f"doubleSpinBoxLoop{level}WaitClocks")
spinBox.editingFinished.disconnect()
spinBox.setValue(retval)
spinBox.editingFinished.connect(partial(self.setPatLoopWaitTime, level))
spinBox.editingFinished.connect(partial(self.setPatLoopWaitClocks, level))
def setPatLoopWaitClocks(self, level):
spinBox = getattr(self.view, f"doubleSpinBoxLoop{level}WaitClocks")
self.det.patwaittime[level] = int(spinBox.value())
self.getPatLoopWaitClocks(level)
def getPatLoopWaitInterval(self, level):
retval = self.det.getPatternWaitInterval(level)[0].count()
spinBox = getattr(self.view, f"spinBoxLoop{level}WaitInterval")
comboBox = getattr(self.view, f"comboBoxLoop{level}WaitInterval")
spinBox.editingFinished.disconnect()
comboBox.currentIndexChanged.disconnect()
# Converting to right time unit for period
if retval >= 1e9:
comboBox.setCurrentIndex(0)
spinBox.setValue(retval / 1e9)
elif retval >= 1e6:
comboBox.setCurrentIndex(1)
spinBox.setValue(retval / 1e6)
elif retval >= 1e3:
comboBox.setCurrentIndex(2)
spinBox.setValue(retval / 1e3)
else:
comboBox.setCurrentIndex(3)
spinBox.setValue(retval)
spinBox.editingFinished.connect(partial(self.setPatLoopWaitInterval, level))
comboBox.currentIndexChanged.connect(partial(self.setPatLoopWaitInterval, level))
def setPatLoopWaitInterval(self, level):
spinBox = getattr(self.view, f"spinBoxLoop{level}WaitInterval")
comboBox = getattr(self.view, f"comboBoxLoop{level}WaitInterval")
value = spinBox.value()
if comboBox.currentIndex() == 0:
value *= 1e9
elif comboBox.currentIndex() == 1:
value *= 1e6
elif comboBox.currentIndex() == 2:
value *= 1e3
t = DurationWrapper()
t.set_count(int(value))
self.det.patwaittime[level] = t
self.getPatLoopWaitInterval(level)
def setTogglePageWaitTime(self):
if self.view.stackedWidgetWaitTime.currentIndex() == 0:
self.view.stackedWidgetWaitTime.setCurrentIndex(1)
self.view.labelWaitTime.setText("Time")
for i in range(Defines.pattern.loops_count):
self.getPatLoopWaitInterval(i)
else:
self.view.stackedWidgetWaitTime.setCurrentIndex(0)
self.view.labelWaitTime.setText("Clocks")
for i in range(Defines.pattern.loops_count):
self.getPatLoopWaitClocks(i)
def setPatLoopWaitTime(self, level):
spinBox = getattr(self.view, f"spinBoxLoop{level}WaitTime")
self.det.patwaittime[level] = spinBox.value()
self.getPatLoopWaitTime(level)
def setCompiler(self):
response = QtWidgets.QFileDialog.getOpenFileName(
@ -450,7 +505,7 @@ class PatternTab(QtWidgets.QWidget):
f"{getattr(self.view, f'lineEditLoop{i}Stop').text()}")
commands.append(f"patwait {i} {getattr(self.view, f'lineEditLoop{i}Wait').text()}")
commands.append(f"patwaittime {i} {getattr(self.view, f'spinBoxLoop{i}WaitTime').text()}")
commands.append(f"patwaittime {i} {getattr(self.view, f'doubleSpinBoxLoop{i}WaitClocks').text()}")
commands.append(f"patlimits {self.view.lineEditStartAddress.text()}, {self.view.lineEditStopAddress.text()}")
# commands.append(f"patfname {self.view.lineEditPatternFile.text()}")
return commands

View File

@ -22,6 +22,7 @@ class SignalsTab(QtWidgets.QWidget):
self.plotTab = None
self.legend: LegendItem | None = None
self.rx_dbitoffset = None
self.rx_dbitreorder = None
self.rx_dbitlist = None
def refresh(self):
@ -29,6 +30,7 @@ class SignalsTab(QtWidgets.QWidget):
self.updateDigitalBitEnable()
self.updateIOOut()
self.getDBitOffset()
self.getDBitReorder()
def connect_ui(self):
for i in range(Defines.signals.count):
@ -49,6 +51,7 @@ class SignalsTab(QtWidgets.QWidget):
partial(self.setIOOutRange, Defines.signals.half, Defines.signals.count))
self.view.lineEditPatIOCtrl.editingFinished.connect(self.setIOOutReg)
self.view.spinBoxDBitOffset.editingFinished.connect(self.setDbitOffset)
self.view.checkBoxDBitReorder.stateChanged.connect(self.setDbitReorder)
def setup_ui(self):
self.plotTab = self.mainWindow.plotTab
@ -87,60 +90,79 @@ class SignalsTab(QtWidgets.QWidget):
self.legend.addItem(plot, name)
@recordOrApplyPedestal
def _processWaveformData(self, data, aSamples, dSamples, rx_dbitlist, isPlottedArray, rx_dbitoffset, romode,
def _processWaveformData(self, data, aSamples, dSamples, rx_dbitreorder, rx_dbitlist, isPlottedArray, romode,
nADCEnabled):
"""
transform raw waveform data into a processed numpy array
@param data: raw waveform data
"""
dbitoffset = rx_dbitoffset
#transform raw waveform data into a processed numpy array
#@param data: raw waveform data
start_digital_data = 0
if romode == 2:
dbitoffset += nADCEnabled * 2 * aSamples
digital_array = np.array(np.frombuffer(data, offset=dbitoffset, dtype=np.uint8))
nbitsPerDBit = dSamples
if nbitsPerDBit % 8 != 0:
nbitsPerDBit += (8 - (dSamples % 8))
offset = 0
arr = []
for i in rx_dbitlist:
# where numbits * numsamples is not a multiple of 8
if offset % 8 != 0:
offset += (8 - (offset % 8))
if not isPlottedArray[i]:
offset += nbitsPerDBit
return None
waveform = np.zeros(dSamples)
for iSample in range(dSamples):
# all samples for digital bit together from slsReceiver
index = int(offset / 8)
iBit = offset % 8
bit = (digital_array[index] >> iBit) & 1
waveform[iSample] = bit
offset += 1
arr.append(waveform)
start_digital_data += nADCEnabled * 2 * aSamples
digital_array = np.array(np.frombuffer(data, offset=start_digital_data, dtype=np.uint8))
if rx_dbitreorder:
samples_per_bit = np.empty((len(rx_dbitlist), dSamples), dtype=np.uint8) #stored per row all the corresponding signals of all samples
nbitsPerDBit = dSamples
if nbitsPerDBit % 8 != 0:
nbitsPerDBit += (8 - (dSamples % 8))
bit_index = 0
for idx, i in enumerate(rx_dbitlist):
# where numbits * numsamples is not a multiple of 8
if bit_index % 8 != 0:
bit_index += (8 - (bit_index % 8))
if not isPlottedArray[i]:
bit_index += nbitsPerDBit
samples_per_bit[idx, :] = np.nan
continue
for iSample in range(dSamples):
# all samples for digital bit together from slsReceiver
index = int(bit_index / 8)
iBit = bit_index % 8
bit = (digital_array[index] >> iBit) & 1
samples_per_bit[idx,iSample] = bit
bit_index += 1
return samples_per_bit
else:
nbitsPerSample = len(rx_dbitlist) if len(rx_dbitlist) % 8 == 0 else len(rx_dbitlist) + (8 - (len(rx_dbitlist) % 8))
bits_per_sample = np.empty((dSamples, len(rx_dbitlist)), dtype=np.uint8) #store per row all selected bits of a sample
for iSample in range(dSamples):
bit_index = nbitsPerSample * iSample
for idx, i in enumerate(rx_dbitlist):
if not isPlottedArray[i]:
bit_index += 1
bits_per_sample[iSample, idx] = np.nan
return np.array(arr)
index = int(bit_index/8)
iBit = idx % 8
bit = (digital_array[index] >> iBit) & 1
bits_per_sample[iSample, idx] = bit
bit_index += 1
return bits_per_sample.T.copy()
def processWaveformData(self, data, aSamples, dSamples):
"""
view function
plots processed waveform data
data: raw waveform data
dsamples: digital samples
asamples: analog samples
"""
#view function
#plots processed waveform data
#data: raw waveform data
#dsamples: digital samples
#asamples: analog samples
self.refresh()
waveforms = {}
isPlottedArray = {i: getattr(self.view, f"checkBoxBIT{i}Plot").isChecked() for i in self.rx_dbitlist}
digital_array = self._processWaveformData(data, aSamples, dSamples, self.rx_dbitlist, isPlottedArray,
self.rx_dbitoffset, self.mainWindow.romode.value,
digital_array = self._processWaveformData(data, aSamples, dSamples, self.rx_dbitreorder, self.rx_dbitlist, isPlottedArray,
self.mainWindow.romode.value,
self.mainWindow.nADCEnabled)
irow = 0
for idx, i in enumerate(self.rx_dbitlist):
for idx, i in enumerate(self.rx_dbitlist):
# bits enabled but not plotting
waveform = digital_array[idx]
if waveform is None:
waveform = digital_array[idx, :]
if np.isnan(waveform[0]):
continue
self.mainWindow.digitalPlots[i].setData(waveform)
plotName = getattr(self.view, f"labelBIT{i}").text()
@ -151,8 +173,10 @@ class SignalsTab(QtWidgets.QWidget):
irow += 1
else:
self.mainWindow.digitalPlots[i].setY(0)
return waveforms
def initializeAllDigitalPlots(self):
self.mainWindow.plotDigitalWaveform = pg.plot()
self.mainWindow.plotDigitalWaveform.addLegend(colCount=Defines.colCount)
@ -360,14 +384,25 @@ class SignalsTab(QtWidgets.QWidget):
self.view.spinBoxDBitOffset.setValue(self.rx_dbitoffset)
self.view.spinBoxDBitOffset.editingFinished.connect(self.setDbitOffset)
def getDBitReorder(self):
self.view.checkBoxDBitReorder.stateChanged.disconnect()
self.rx_dbitreorder = self.det.rx_dbitreorder
self.view.checkBoxDBitReorder.setChecked(self.rx_dbitreorder)
self.view.checkBoxDBitReorder.stateChanged.connect(self.setDbitReorder)
def setDbitOffset(self):
self.det.rx_dbitoffset = self.view.spinBoxDBitOffset.value()
def setDbitReorder(self):
self.det.rx_dbitreorder = self.view.checkBoxDBitReorder.isChecked()
def saveParameters(self) -> list:
commands = []
dblist = [str(i) for i in range(Defines.signals.count) if getattr(self.view, f"checkBoxBIT{i}DB").isChecked()]
if len(dblist) > 0:
commands.append(f"rx_dbitlist {', '.join(dblist)}")
commands.append(f"rx_dbitoffset {self.view.spinBoxDBitOffset.value()}")
commands.append(f"rx_dbitreorder {self.view.checkBoxDBitReorder.isChecked()}")
commands.append(f"patioctrl {self.view.lineEditPatIOCtrl.text()}")
return commands

File diff suppressed because it is too large Load Diff

View File

@ -6074,6 +6074,33 @@
<enum>QFrame::Raised</enum>
</property>
<layout class="QGridLayout" name="gridLayout_17">
<item row="0" column="4">
<widget class="QSpinBox" name="spinBoxDBitOffset">
<property name="minimumSize">
<size>
<width>150</width>
<height>32</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>150</width>
<height>32</height>
</size>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="styleSheet">
<string notr="true">background-color: rgb(255, 255, 255);</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="0" column="3">
<widget class="QLabel" name="label_66">
<property name="font">
@ -6086,6 +6113,36 @@
</property>
</widget>
</item>
<item row="0" column="6">
<widget class="QCheckBox" name="checkBoxDBitReorder">
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="text">
<string>DBit Reorder</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_48">
<property name="minimumSize">
<size>
<width>50</width>
<height>0</height>
</size>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="text">
<string>IO Control Register:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="lineEditPatIOCtrl">
<property name="sizePolicy">
@ -6133,50 +6190,18 @@
</property>
</spacer>
</item>
<item row="0" column="4">
<widget class="QSpinBox" name="spinBoxDBitOffset">
<property name="minimumSize">
<item row="0" column="5">
<spacer name="horizontalSpacer_22">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>150</width>
<height>32</height>
<width>40</width>
<height>20</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>150</width>
<height>32</height>
</size>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="styleSheet">
<string notr="true">background-color: rgb(255, 255, 255);</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_48">
<property name="minimumSize">
<size>
<width>50</width>
<height>0</height>
</size>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="text">
<string>IO Control Register:</string>
</property>
</widget>
</spacer>
</item>
</layout>
</widget>

33
pyproject.toml Normal file
View File

@ -0,0 +1,33 @@
[tool.scikit-build.metadata.version]
provider = "scikit_build_core.metadata.regex"
input = "VERSION"
regex = '^(?P<version>\d+(?:\.\d+)*(?:[\.\+\w]+)?)$'
result = "{version}"
[build-system]
requires = [ "scikit-build-core>=0.10", "pybind11", "numpy",]
build-backend = "scikit_build_core.build"
[project]
name = "slsdet"
dynamic = ["version"]
[tool.cibuildwheel]
before-all = "uname -a"
[tool.scikit-build.build]
verbose = true
[tool.scikit-build.cmake]
build-type = "Release"
[tool.scikit-build.install]
components = [ "python",]
[tool.scikit-build.cmake.define]
SLS_USE_RECEIVER = "OFF"
SLS_USE_RECEIVER_BINARIES = "OFF"
SLS_USE_TEXTCLIENT = "OFF"
SLS_BUILD_SHARED_LIBRARIES = "OFF"
SLS_USE_PYTHON = "ON"
SLS_INSTALL_PYTHONEXT = "ON"

View File

@ -20,13 +20,14 @@ target_link_libraries(_slsdet PUBLIC
set_target_properties(_slsdet PROPERTIES
LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin
LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin/slsdet
)
#Copy Python code
set( PYTHON_FILES
slsdet/__init__.py
slsdet/adcs.py
slsdet/bits.py
slsdet/dacs.py
slsdet/powers.py
slsdet/decorators.py
@ -37,8 +38,8 @@ set( PYTHON_FILES
slsdet/enums.py
slsdet/errors.py
slsdet/gaincaps.py
slsdet/gotthard.py
slsdet/pattern.py
slsdet/PatternGenerator.py
slsdet/gotthard2.py
slsdet/moench.py
slsdet/proxy.py
@ -71,9 +72,18 @@ configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/../VERSION
if(SLS_INSTALL_PYTHONEXT)
install(TARGETS _slsdet
EXPORT "${TARGETS_EXPORT_NAME}"
LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/python
LIBRARY DESTINATION slsdet
COMPONENT python
)
install(
FILES ${PYTHON_FILES}
DESTINATION slsdet
COMPONENT python
)
install(
FILES ../VERSION
DESTINATION slsdet
COMPONENT python
)
install(FILES ${PYTHON_FILES} DESTINATION ${CMAKE_INSTALL_PREFIX}/python/slsdet)
install(FILES ../VERSION DESTINATION ${CMAKE_INSTALL_PREFIX}/python/slsdet)
endif()

View File

@ -55,7 +55,6 @@ servers = [
# "jungfrauDetectorServer",
"mythen3DetectorServer",
# "gotthard2DetectorServer",
# "gotthardDetectorServer",
# "ctbDetectorServer",
# "moenchDetectorServer",
]

View File

@ -0,0 +1,19 @@
# SPDX-License-Identifier: LGPL-3.0-or-other
# Copyright (C) 2021 Contributors to the SLS Detector Package
# Script to get combined zmq packets from frame synchronizer using pull zmq sockets
import json
import zmq
c = zmq.Context()
s = c.socket(zmq.PULL)
s.connect("tcp://127.0.0.1:5555")
while True:
m = s.recv_multipart()
for p in m:
if p.startswith(b"{"):
print(p.decode().strip())
else:
print("binary")
print("--------")

View File

@ -123,6 +123,9 @@ def visit(node):
if node.kind == cindex.CursorKind.CLASS_DECL:
if node.displayname == "Detector":
for child in node.get_children():
# Skip assignment operators
if child.kind == cindex.CursorKind.CXX_METHOD and child.spelling == "operator=":
continue
if (
child.kind == cindex.CursorKind.CXX_METHOD
and child.access_specifier == cindex.AccessSpecifier.PUBLIC

View File

@ -1,77 +0,0 @@
# SPDX-License-Identifier: LGPL-3.0-or-other
# Copyright (C) 2021 Contributors to the SLS Detector Package
"""
Setup file for slsdet
Build upon the pybind11 example found here: https://github.com/pybind/python_example
"""
import os
import sys
from setuptools import setup, find_packages
from pybind11.setup_helpers import Pybind11Extension, build_ext
def read_version():
try:
version_file = os.path.join(os.path.dirname(__file__), 'slsdet', 'VERSION')
with open(version_file, "r") as f:
return f.read().strip()
except:
raise RuntimeError("VERSION file not found in slsdet package from setup.py.")
__version__ = read_version()
def get_conda_path():
"""
Keep this a function if we need some fancier logic later
"""
print('Prefix: ', os.environ['CONDA_PREFIX'])
return os.environ['CONDA_PREFIX']
#TODO migrate to CMake build or fetch files from cmake?
ext_modules = [
Pybind11Extension(
'_slsdet',
['src/main.cpp',
'src/enums.cpp',
'src/current.cpp',
'src/detector.cpp',
'src/network.cpp',
'src/pattern.cpp',
'src/scan.cpp',
'src/duration.cpp',
'src/DurationWrapper.cpp',
'src/pedestal.cpp',
]
,
include_dirs=[
os.path.join(get_conda_path(), 'include'),
],
libraries=['SlsDetector', 'SlsSupport', 'SlsReceiver'],
library_dirs=[
os.path.join(get_conda_path(), 'lib'),
],
language='c++'
),
]
setup(
name='slsdet',
version=__version__,
author='Erik Frojdh',
author_email='erik.frojdh@psi.ch',
url='https://github.com/slsdetectorgroup/slsDetectorPackage',
description='Detector API for SLS Detector Group detectors',
long_description='',
packages=find_packages(exclude=['contrib', 'docs', 'tests']),
package_data={
'slsdet': ['VERSION'],
},
ext_modules=ext_modules,
cmdclass={"build_ext": build_ext},
zip_safe=False,
)

View File

@ -0,0 +1,226 @@
from . import Detector, Pattern
from .bits import setbit, clearbit
import textwrap
from pathlib import Path
class PatternGenerator:
"""
Class to generate a pattern for the SLS detector. Intents to as closely as possible
mimic the old pattern generation in the C code.
"""
def __init__(self):
self.pattern = Pattern()
self.iaddr = 0
def SB(self, *bits):
"""
Set one or several bits. Change will take affect with the next PW.
"""
for bit in bits:
self.pattern.word[self.iaddr] = setbit(bit, self.pattern.word[self.iaddr])
return self.pattern.word[self.iaddr]
def CB(self, *bits):
"""
Clear one or several bits. Change will take affect with the next PW.
"""
for bit in bits:
self.pattern.word[self.iaddr] = clearbit(bit, self.pattern.word[self.iaddr])
return self.pattern.word[self.iaddr]
def _pw(self, verbose = False):
if verbose:
print(f'{self.iaddr:#06x} {self.pattern.word[self.iaddr]:#018x}')
#Limits are inclusive so we need to increment the address before writing the next word
self.pattern.limits[1] = self.iaddr
self.iaddr += 1
self.pattern.word[self.iaddr] = self.pattern.word[self.iaddr-1]
def PW(self, x = 1, verbose = False):
for i in range(x):
self._pw(verbose)
# def REPEAT(self, x, verbose = False):
# for i in range(x):
# self._pw(verbose)
# def PW2(self, verbose = 0):
# self.REPEAT(2, verbose)
def CLOCKS(self, bit, times = 1, length = 1, verbose = False):
"""
clocks "bit" n "times", every half clock is long "length"
length is optional, default value is 1
"""
for i in range(0, times):
self.SB(bit); self.PW(length, verbose)
self.CB(bit); self.PW(length, verbose)
def CLOCK(self, bit, length = 1, verbose = 0):
self.CLOCKS(bit, 1, length ,verbose)
def serializer(self, value, serInBit, clkBit, nbits, msbfirst = True, length = 1):
"""serializer(value,serInBit,clkBit,nbits,msbfirst=1,length=1)
Produces the .pat file needed to serialize a word into a shift register.
value: value to be serialized
serInBit: control bit corresponding to serial in
clkBit: control bit corresponding to the clock
nbits: number of bits of the target register to load
msbfirst: if 1 pushes in the MSB first (default),
if 0 pushes in the LSB first
length: length of all the PWs in the pattern
It produces no output because it modifies directly the members of the class pat via SB and CB"""
c = value
self.CB(serInBit, clkBit)
self.PW(length) #generate initial line with clk and serIn to 0
start = 0
stop = nbits
step = 1
if msbfirst:
start = nbits - 1
stop = -1
step =- 1 #reverts loop if msb has to be pushed in first
for i in range(start, stop, step):
if c & (1<<i):
self.SB(serInBit)
self.PW(length)
else:
self.CB(serInBit)
self.PW(length)
self.SB(clkBit)
self.PW(length)
self.CB(clkBit)
self.PW(length)
self.CB(serInBit, clkBit)
self.PW(length) #generate final line with clk and serIn to 0
#NOT IMPLEMENTED YET
#TODO! What should setstop do? Or can we remove it?
#def setstop():
#
def setoutput(self, bit):
self.pattern.ioctrl = setbit(bit, self.pattern.ioctrl)
def setinput(self, bit):
self.pattern.ioctrl= clearbit(bit, self.pattern.ioctrl)
#TODO! What should setclk do? Or can we remove it?
# def setclk(bit):
# self.clkctrl=self.setbit(bit,self.clkctrl)
def setinputs(self, *args):
for i in args:
self.setinput(i)
def setoutputs(self, *args):
for i in args:
self.setoutput(i)
#def setclks(self, *args):
# for i in args:
# self.setclk(i)
def setnloop(self, i, reps):
self.pattern.nloop[i] = reps
def setstartloop(self, i):
"""
Set startloop[i] to the current address.
"""
self.pattern.startloop[i] = self.iaddr
def setstoploop(self, i):
"""
Set stoploop[i] to the current address.
"""
self.pattern.stoploop[i] = self.iaddr
def setstart(self):
"""
Set start of pattern to the current address.
"""
self.pattern.limits[0]=self.iaddr
def setstop(self,l):
"""
Set stop of pattern to the current address.
"""
self.pattern.limits[1] = self.iaddr
def setwaitpoint(self, i):
"""
Set wait[i] to the current address.
"""
self.pattern.wait[i] = self.iaddr
def setwaittime(self, i, t):
"""
Set waittime[i] to t.
"""
self.pattern.waittime[i] = t
def setwait(self, i, t):
"""
Set wait[i] to the current address and waittime[i] to t.
"""
self.setwait(i)
self.setwaittime(i, t)
def __repr__(self):
return textwrap.dedent(f"""\
PatternBuilder:
patlimits: {self.pattern.limits}
startloop: {self.pattern.startloop}
stoploop: {self.pattern.stoploop}
nloop: {self.pattern.nloop}
wait: {self.pattern.wait}
waittime: {self.pattern.waittime}""")
def __str__(self):
return self.pattern.str()
def print(self):
print(self)
def save(self, fname):
"""Save pattern to text file"""
fname = str(fname) #Accept also Path objects, but C++ code needs a string
self.pattern.save(fname)
def load(self, fname):
"""Load pattern from text file"""
fname = str(fname) #Accept also Path objects, but C++ code needs a string
n = self.pattern.load(fname)
#If the firs and only word is 0 we assume an empty pattern
if n == 1 and self.pattern.word[0] == 0:
n = 0
self.iaddr = n
#To make PW work as expected we need to set 1+last word to the last word
if n > 0:
self.pattern.word[n] = self.pattern.word[n-1]
def send_to_detector(self, det):
"""
Load the pattern into the detector.
"""
det.setPattern(self.pattern)

View File

@ -9,12 +9,12 @@ from .detector import Detector
from .jungfrau import Jungfrau
from .mythen3 import Mythen3
from .gotthard2 import Gotthard2
from .gotthard import Gotthard
from .moench import Moench
from .pattern import Pattern, patternParameters
from .gaincaps import Mythen3GainCapsWrapper
from .PatternGenerator import PatternGenerator
import _slsdet
from . import _slsdet
xy = _slsdet.xy
defs = _slsdet.slsDetectorDefs
@ -41,3 +41,6 @@ def read_version():
__version__ = read_version()

31
python/slsdet/bits.py Normal file
View File

@ -0,0 +1,31 @@
import numpy as np
def setbit(bit, word):
if isinstance(word, np.generic):
mask = word.dtype.type(1)
mask = mask << bit
else:
mask = 1 << bit
return word | mask
def setbit_arr(bit, arr):
arr |= arr.dtype.type(1 << bit)
def clearbit(bit, word):
"""
Clear the bit at position bit in word.
Two paths to avoid converting the types.
"""
if isinstance(word, np.generic):
mask = word.dtype.type(1)
mask = ~(mask << bit)
else:
mask = ~(1 << bit)
return word & mask
def clearbit_arr(bit, arr):
arr &= arr.dtype.type(~(1 << bit))

View File

@ -4,7 +4,7 @@ from .detector import Detector, freeze
from .utils import element_if_equal
from .dacs import DetectorDacs, NamedDacs
from .powers import DetectorPowers, NamedPowers
import _slsdet
from . import _slsdet
dacIndex = _slsdet.slsDetectorDefs.dacIndex
from .detector_property import DetectorProperty

View File

@ -3,7 +3,7 @@
from .detector_property import DetectorProperty
from functools import partial
import numpy as np
import _slsdet
from . import _slsdet
from .detector import freeze
dacIndex = _slsdet.slsDetectorDefs.dacIndex
class Dac(DetectorProperty):

View File

@ -1,8 +1,8 @@
# SPDX-License-Identifier: LGPL-3.0-or-other
# Copyright (C) 2021 Contributors to the SLS Detector Package
from _slsdet import CppDetectorApi
from _slsdet import slsDetectorDefs
from _slsdet import IpAddr, MacAddr
from ._slsdet import CppDetectorApi
from ._slsdet import slsDetectorDefs
from ._slsdet import IpAddr, MacAddr
runStatus = slsDetectorDefs.runStatus
timingMode = slsDetectorDefs.timingMode
@ -15,7 +15,7 @@ defs = slsDetectorDefs
from .utils import element_if_equal, all_equal, get_set_bits, list_to_bitmask
from .utils import Geometry, to_geo, element, reduce_time, is_iterable, hostname_list
from _slsdet import xy
from ._slsdet import xy
from .gaincaps import Mythen3GainCapsWrapper
from . import utils as ut
from .proxy import JsonProxy, SlowAdcProxy, ClkDivProxy, MaxPhaseProxy, ClkFreqProxy, PatLoopProxy, PatNLoopProxy, PatWaitProxy, PatWaitTimeProxy
@ -151,6 +151,10 @@ class Detector(CppDetectorApi):
def hostname(self):
"""Frees shared memory and sets hostname (or IP address) of all modules concatenated by +
Virtual servers can already use the port in hostname separated by ':' and ports incremented by 2 to accomodate the stop server as well.
Note
-----
The row and column values in the udp/zmq header are affected by the order in this command and the detsize command. The modules are stacked row by row until they reach the y-axis limit set by detsize (if specified). Then, stacking continues in the next column and so on. This only affects row and column in udp/zmq header.
Example
-------
@ -270,7 +274,7 @@ class Detector(CppDetectorApi):
@property
@element
def serialnumber(self):
"""Jungfrau][Gotthard][Mythen3][Gotthard2][CTB][Moench] Serial number of detector """
"""Jungfrau][Mythen3][Gotthard2][CTB][Moench] Serial number of detector """
return ut.lhex(self.getSerialNumber())
@property
@ -308,7 +312,7 @@ class Detector(CppDetectorApi):
-----
[Eiger] Options: 4, 8, 12, 16, 32. If set to 32, also sets clkdivider to 2 (quarter speed), else to 0 (full speed)\n
[Mythen3] Options: 8, 16, 32 \n
[Jungfrau][Moench][Gotthard][Ctb][Mythen3][Gotthard2][Xilinx Ctb] 16
[Jungfrau][Moench][Ctb][Mythen3][Gotthard2][Xilinx Ctb] 16
"""
return self.getDynamicRange()
@ -368,7 +372,6 @@ class Detector(CppDetectorApi):
[Eiger] Use threshold command to load settings
[Jungfrau] GAIN0, HIGHGAIN0 \n
[Gotthard] DYNAMICGAIN, HIGHGAIN, LOWGAIN, MEDIUMGAIN, VERYHIGHGAIN \n
[Gotthard2] DYNAMICGAIN, FIXGAIN1, FIXGAIN2 \n
[Eiger] settings loaded from file found in settingspath
[Moench] G1_HIGHGAIN, G1_LOWGAIN, G2_HIGHCAP_HIGHGAIN, G2_HIGHCAP_LOWGAIN, G2_LOWCAP_HIGHGAIN, G2_LOWCAP_LOWGAIN, G4_HIGHGAIN, G4_LOWGAIN
@ -400,7 +403,7 @@ class Detector(CppDetectorApi):
@element
def framesl(self):
"""
[Gotthard][Jungfrau][Moench][Mythen3][Gotthard2][CTB][Xilinx CTB] Number of frames left in acquisition.\n
[Jungfrau][Moench][Mythen3][Gotthard2][CTB][Xilinx CTB] Number of frames left in acquisition.\n
Note
----
@ -631,7 +634,7 @@ class Detector(CppDetectorApi):
@element
def periodl(self):
"""
[Gotthard][Jungfrau][Moench][CTB][Mythen3][Gotthard2][Xilinx Ctb] Period left for current frame.
[Jungfrau][Moench][CTB][Mythen3][Gotthard2][Xilinx Ctb] Period left for current frame.
Note
-----
@ -653,7 +656,7 @@ class Detector(CppDetectorApi):
@element
def delay(self):
"""
[Gotthard][Jungfrau][Moench][CTB][Mythen3][Gotthard2][Xilinx Ctb] Delay after trigger, accepts either a value in seconds, DurationWrapper or datetime.timedelta
[Jungfrau][Moench][CTB][Mythen3][Gotthard2][Xilinx Ctb] Delay after trigger, accepts either a value in seconds, DurationWrapper or datetime.timedelta
:getter: always returns in seconds. To get in DurationWrapper, use getDelayAfterTrigger
@ -695,7 +698,7 @@ class Detector(CppDetectorApi):
@element
def delayl(self):
"""
[Gotthard][Jungfrau][Moench][CTB][Mythen3][Gotthard2][Xilinx Ctb] Delay left after trigger during acquisition, accepts either a value in seconds, datetime.timedelta or DurationWrapper
[Jungfrau][Moench][CTB][Mythen3][Gotthard2][Xilinx Ctb] Delay left after trigger during acquisition, accepts either a value in seconds, datetime.timedelta or DurationWrapper
Note
-----
@ -1440,8 +1443,6 @@ class Detector(CppDetectorApi):
@udp_srcip.setter
def udp_srcip(self, ip):
if ip == "auto":
if self.type == detectorType.GOTTHARD:
raise NotImplementedError('Auto for udp_srcip cannot be used for GotthardI')
ip = socket.gethostbyname(self.hostname[0])
ip = ut.make_ip(ip)
ut.set_using_dict(self.setSourceUDPIP, ip)
@ -1522,7 +1523,6 @@ class Detector(CppDetectorApi):
Note
-----
[Gotthard] 0, 90, 110, 120, 150, 180, 200 \n
[Eiger][Mythen3][Gotthard2] 0 - 200 \n
[Jungfrau][Moench][Ctb] 0, 60 - 200
"""
@ -1650,7 +1650,7 @@ class Detector(CppDetectorApi):
def master(self):
"""
[Eiger][Gotthard2][Jungfrau][Moench] Sets (half) module to master and other(s) to slaves.\n
[Gotthard][Gotthard2][Mythen3][Eiger][Jungfrau][Moench] Gets if the current (half) module is master.
[Gotthard2][Mythen3][Eiger][Jungfrau][Moench] Gets if the current (half) module is master.
"""
return self.getMaster()
@ -1908,7 +1908,7 @@ class Detector(CppDetectorApi):
@property
def adcreg(self):
"""[Jungfrau][Moench][Ctb][Gotthard] Writes to an adc register
"""[Jungfrau][Moench][Ctb] Writes to an adc register
Note
-----
@ -1937,7 +1937,7 @@ class Detector(CppDetectorApi):
@element
def triggersl(self):
"""
[Gotthard][Jungfrau][Moench][Mythen3][Gotthard2][CTB][Xilinx CTB] Number of triggers left in acquisition.\n
[Jungfrau][Moench][Mythen3][Gotthard2][CTB][Xilinx CTB] Number of triggers left in acquisition.\n
Note
----
@ -2202,7 +2202,7 @@ class Detector(CppDetectorApi):
Note
-----
Default: AUTO_TIMING \n
[Jungfrau][Moench][Gotthard][Ctb][Gotthard2][Xilinx Ctb] AUTO_TIMING, TRIGGER_EXPOSURE \n
[Jungfrau][Moench][Ctb][Gotthard2][Xilinx Ctb] AUTO_TIMING, TRIGGER_EXPOSURE \n
[Mythen3] AUTO_TIMING, TRIGGER_EXPOSURE, GATED, TRIGGER_GATED \n
[Eiger] AUTO_TIMING, TRIGGER_EXPOSURE, GATED, BURST_TRIGGER
"""
@ -2259,7 +2259,7 @@ class Detector(CppDetectorApi):
def type(self):
""" Returns detector type.
Enum: detectorType
[EIGER, JUNGFRAU, GOTTHARD, MOENCH, MYTHEN3, GOTTHARD2, CHIPTESTBOARD]
[EIGER, JUNGFRAU, MOENCH, MYTHEN3, GOTTHARD2, CHIPTESTBOARD]
:setter: Not implemented
"""
@ -2566,7 +2566,7 @@ class Detector(CppDetectorApi):
Note
-----
By default, the on-chip gain switching is active during the entire exposure. This mode disables the on-chip gain switching comparator automatically after 93.75% of exposure time (only for longer than 100us). The % is only for chipv1.0, the duration can be set for chipv1.1.\n
By default, the on-chip gain switching is active during the entire exposure. This mode disables the on-chip gain switching comparator automatically and the duration is set using compdisabletime.\n
Default is 0 or this mode disabled (comparator enabled throughout). 1 enables mode. 0 disables mode.
"""
return self.getAutoComparatorDisable()
@ -2580,10 +2580,6 @@ class Detector(CppDetectorApi):
def compdisabletime(self):
"""[Jungfrau] Time before end of exposure when comparator is disabled.
Note
-----
It is only possible for chipv1.1.
:getter: always returns in seconds. To get in DurationWrapper, use getComparatorDisableTime
Example
@ -2913,7 +2909,7 @@ class Detector(CppDetectorApi):
@property
@element
def timing_info_decoder(self):
"""[Jungfrau] [Jungfrau] Advanced Command and only for SWISSFEL and SHINE. Sets the bunch id or timing info decoder. Default is SWISSFEL.
"""[Jungfrau] [Jungfrau] Advanced Command and only for SWISSFEL and SHINE. Sets the bunch id or timing info decoder. Default is SWISSFEL. Only allowed for pcbv2.0.
Enum: timingInfoDecoder
"""
return self.getTimingInfoDecoder()
@ -3467,6 +3463,16 @@ class Detector(CppDetectorApi):
def rx_dbitoffset(self, value):
ut.set_using_dict(self.setRxDbitOffset, value)
@property
@element
def rx_dbitreorder(self):
"""[Ctb] Reorder digital data to group together all samples per signal. Default is 1. Setting to 0 means 'do not reorder' and to keep what the board spits out, which is that all signals in a sample are grouped together."""
return self.getRxDbitReorder()
@rx_dbitreorder.setter
def rx_dbitreorder(self, value):
ut.set_using_dict(self.setRxDbitReorder, value)
@property
@element
def maxadcphaseshift(self):
@ -3479,15 +3485,13 @@ class Detector(CppDetectorApi):
@property
@element
def adcphase(self):
"""[Gotthard][Jungfrau][Moench][CTB] Sets phase shift of ADC clock.
"""[Jungfrau][Moench][CTB] Sets phase shift of ADC clock.
Note
-----
[Jungfrau][Moench] Absolute phase shift. Changing Speed also resets adcphase to recommended defaults.\n
[Ctb] Absolute phase shift. Changing adcclk also resets adcphase and sets it to previous values.\n
[Gotthard] Relative phase shift.
[Ctb] Absolute phase shift. Changing adcclk also resets adcphase and sets it to previous values.
:getter: Not implemented for Gotthard
"""
return self.getADCPhase()
@ -3702,7 +3706,13 @@ class Detector(CppDetectorApi):
@property
def patwaittime(self):
"""
[Ctb][Mythen3][Xilinx Ctb] Wait time in clock cycles of loop level provided.
[Ctb][Mythen3][Xilinx Ctb] Wait time in clock cycles of loop level provided.
Info
----
:getter: Always return in clock cycles. To get in DurationWrapper, use getPatternWaitInterval
:setter: Accepts either a value in clock cycles or a time unit (timedelta, DurationWrapper)
Example
-------
@ -3713,41 +3723,85 @@ class Detector(CppDetectorApi):
0: 5
1: 20
2: 30
>>> # using timedelta (up to microseconds precision)
>>> from datetime import timedelta
>>> d.patwaittime[0] = timedelta(seconds=1, microseconds=3)
>>>
>>> # using DurationWrapper to set in seconds
>>> from slsdet import DurationWrapper
>>> d.patwaittime[0] = DurationWrapper(1.2)
>>>
>>> # using DurationWrapper to set in ns
>>> t = DurationWrapper()
>>> t.set_count(500)
>>> d.patwaittime = t
>>>
>>> # to get in clock cycles
>>> d.patwaittime
1000
>>>
>>> d.getPatternWaitInterval(0)
sls::DurationWrapper(total_seconds: 1.23 count: 1230000000)
"""
return PatWaitTimeProxy(self)
@property
@element
def patwaittime0(self):
"""[Ctb][Mythen3][Xilinx Ctb] Wait 0 time in clock cycles."""
return self.getPatternWaitTime(0)
@patwaittime0.setter
def patwaittime0(self, nclk):
nclk = ut.merge_args(0, nclk)
ut.set_using_dict(self.setPatternWaitTime, *nclk)
def create_patwaittime_property(level):
docstring_template ="""
Deprecated command. Use patwaittime instead.
[Ctb][Mythen3][Xilinx Ctb] Wait time in clock cycles of loop level {level} provided.
@property
@element
def patwaittime1(self):
"""[Ctb][Mythen3][Xilinx Ctb] Wait 1 time in clock cycles."""
return self.getPatternWaitTime(1)
Info
----
@patwaittime1.setter
def patwaittime1(self, nclk):
nclk = ut.merge_args(1, nclk)
ut.set_using_dict(self.setPatternWaitTime, *nclk)
:getter: Always return in clock cycles. To get in DurationWrapper, use getPatternWaitInterval
:setter: Accepts either a value in clock cycles or a time unit (timedelta, DurationWrapper)
Example
-------
>>> d.patwaittime{level} = 5
>>> d.patwaittime{level}
5
>>> # using timedelta (up to microseconds precision)
>>> from datetime import timedelta
>>> d.patwaittime{level} = timedelta(seconds=1, microseconds=3)
>>>
>>> # using DurationWrapper to set in seconds
>>> from slsdet import DurationWrapper
>>> d.patwaittime{level} = DurationWrapper(1.2)
>>>
>>> # using DurationWrapper to set in ns
>>> t = DurationWrapper()
>>> t.set_count(500)
>>> d.patwaittime{level} = t
>>>
>>> # to get in clock cycles
>>> d.patwaittime{level}
1000
>>>
>>> d.getPatternWaitInterval(level)
sls::DurationWrapper(total_seconds: 1.23 count: 1230000000)
"""
@property
@element
def patwaittime(self):
return self.getPatternWaitClocks(level)
@property
@element
def patwaittime2(self):
"""[Ctb][Mythen3][Xilinx Ctb] Wait 2 time in clock cycles."""
return self.getPatternWaitTime(2)
@patwaittime.setter
def patwaittime(self, value):
if isinstance(value, (int, float)) and not isinstance(value, bool):
nclk = ut.merge_args(level, value)
ut.set_using_dict(self.setPatternWaitClocks, level, *nclk)
else:
ut.set_time_using_dict(self.setPatternWaitInterval, level, value)
patwaittime.__doc__ = docstring_template.format(level=level)
@patwaittime2.setter
def patwaittime2(self, nclk):
nclk = ut.merge_args(2, nclk)
ut.set_using_dict(self.setPatternWaitTime, *nclk)
return patwaittime
patwaittime0 = create_patwaittime_property(0)
patwaittime1 = create_patwaittime_property(1)
patwaittime2 = create_patwaittime_property(2)
@property
@ -4038,27 +4092,6 @@ class Detector(CppDetectorApi):
return ClkDivProxy(self)
"""
---------------------------<<<Gotthard specific>>>---------------------------
"""
@property
def exptimel(self):
"""[Gotthard] Exposure time left for current frame.
:getter: always returns in seconds. To get in DurationWrapper, use getExptimeLeft
:setter: Not Implemented
Example
-----------
>>> d.exptimel
181.23
>>> d.getExptimeLeft()
[sls::DurationWrapper(total_seconds: 181.23 count: 181230000000)]
"""
t = self.getExptimeLeft()
return reduce_time(t)
"""
---------------------------<<<Mythen3 specific>>>---------------------------

View File

@ -11,7 +11,7 @@ Created on Wed Dec 6 11:51:18 2017
from .detector import Detector
from .temperature import Temperature, DetectorTemperature
from .dacs import DetectorDacs
import _slsdet
from . import _slsdet
dacIndex = _slsdet.slsDetectorDefs.dacIndex
from .detector_property import DetectorProperty

View File

@ -15,8 +15,8 @@ if dt === detectorType.EIGER:
"""
import _slsdet
from . import _slsdet
for name, cls in _slsdet.slsDetectorDefs.__dict__.items():
if isinstance(cls, type):
exec(f'{name} = {cls.__module__}.{cls.__qualname__}')
exec(f'{name} = _slsdet.{cls.__qualname__}')

View File

@ -1,6 +1,6 @@
import _slsdet
from . import _slsdet
gc = _slsdet.slsDetectorDefs.M3_GainCaps

View File

@ -1,53 +0,0 @@
# SPDX-License-Identifier: LGPL-3.0-or-other
# Copyright (C) 2021 Contributors to the SLS Detector Package
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
This file contains the specialization for the Moench detector
"""
from .detector import Detector, freeze
from .dacs import DetectorDacs
import _slsdet
dacIndex = _slsdet.slsDetectorDefs.dacIndex
from .detector_property import DetectorProperty
# @freeze
# vref_ds, vcascn_pb, vcascp_pb, vout_cm, vcasc_out, vin_cm, vref_comp, ib_test_c
class GotthardDacs(DetectorDacs):
_dacs = [('vref_ds', dacIndex.VREF_DS, 0, 4000, 660),
('vcascn_pb', dacIndex.VCASCN_PB, 0, 4000, 650),
('vcascp_pb,', dacIndex.VCASCP_PB, 0, 4000, 1480),
('vout_cm', dacIndex.VOUT_CM, 0, 4000, 1520),
('vcasc_out', dacIndex.VCASC_OUT, 0, 4000, 1320),
('vin_cm', dacIndex.VIN_CM, 0, 4000, 1350),
('vref_comp', dacIndex.VREF_COMP, 0, 4000, 350),
('ib_test_c', dacIndex.IB_TESTC, 0, 4000, 2001),
]
_dacnames = [_d[0] for _d in _dacs]
#vthreshold??
@freeze
class Gotthard(Detector):
"""
Subclassing Detector to set up correct dacs and detector specific
functions.
"""
_detector_dynamic_range = [16]
_settings = ['standard', 'highgain', 'lowgain', 'veryhighgain', 'verylowgain']
"""available settings for Eiger, note almost always standard"""
def __init__(self, id=0):
super().__init__(id)
self._frozen = False
self._dacs = GotthardDacs(self)
@property
def dacs(self):
return self._dacs

View File

@ -11,7 +11,7 @@ from .detector import Detector, freeze
# from .adcs import Adc, DetectorAdcs
from .dacs import DetectorDacs
import _slsdet
from . import _slsdet
dacIndex = _slsdet.slsDetectorDefs.dacIndex
from .detector_property import DetectorProperty

View File

@ -11,7 +11,7 @@ from .detector import Detector, freeze
# from .adcs import Adc, DetectorAdcs
from .dacs import DetectorDacs
import _slsdet
from . import _slsdet
dacIndex = _slsdet.slsDetectorDefs.dacIndex
from .detector_property import DetectorProperty

View File

@ -9,7 +9,7 @@ This file contains the specialization for the Moench detector
from .detector import Detector, freeze
from .dacs import DetectorDacs
import _slsdet
from . import _slsdet
dacIndex = _slsdet.slsDetectorDefs.dacIndex
from .detector_property import DetectorProperty

View File

@ -11,7 +11,7 @@ from .detector import Detector, freeze
# from .adcs import Adc, DetectorAdcs
from .dacs import DetectorDacs
import _slsdet
from . import _slsdet
dacIndex = _slsdet.slsDetectorDefs.dacIndex
gc_enums = _slsdet.slsDetectorDefs.M3_GainCaps
from .detector_property import DetectorProperty

View File

@ -1,8 +1,8 @@
# SPDX-License-Identifier: LGPL-3.0-or-other
# Copyright (C) 2021 Contributors to the SLS Detector Package
import _slsdet
from . import _slsdet
from _slsdet import Pattern
from ._slsdet import Pattern
class patternParameters(_slsdet.patternParameters):

View File

@ -3,7 +3,7 @@
from .detector_property import DetectorProperty
from functools import partial
import numpy as np
import _slsdet
from . import _slsdet
from .detector import freeze
dacIndex = _slsdet.slsDetectorDefs.dacIndex
class Power(DetectorProperty):

View File

@ -3,7 +3,7 @@
from .utils import element_if_equal
from .enums import dacIndex
from .defines import M3_MAX_PATTERN_LEVELS, MAX_PATTERN_LEVELS
from _slsdet import slsDetectorDefs
from ._slsdet import slsDetectorDefs
detectorType = slsDetectorDefs.detectorType
@ -275,10 +275,13 @@ class PatWaitTimeProxy:
self.det = det
def __getitem__(self, key):
return element_if_equal(self.det.getPatternWaitTime(key))
return element_if_equal(self.det.getPatternWaitClocks(key))
def __setitem__(self, key, value):
set_proxy_using_dict(self.det.setPatternWaitTime, key, value)
if isinstance(value, (int, float)) and not isinstance(value, bool):
set_proxy_using_dict(self.det.setPatternWaitClocks, key, value)
else:
set_proxy_using_dict(self.det.setPatternWaitInterval, key, value)
def __repr__(self):
max_levels = MAX_PATTERN_LEVELS

View File

@ -6,7 +6,7 @@ but not directly used in controlling the detector
"""
from collections import namedtuple
import _slsdet #C++ lib
from . import _slsdet #C++ lib
import functools
import datetime as dt
import pathlib

View File

@ -1293,20 +1293,6 @@ void init_det(py::module &m) {
(void (Detector::*)(defs::collectionMode, sls::Positions)) &
Detector::setCollectionMode,
py::arg(), py::arg() = Positions{});
CppDetectorApi.def("getROI",
(Result<defs::ROI>(Detector::*)(sls::Positions) const) &
Detector::getROI,
py::arg() = Positions{});
CppDetectorApi.def("setROI",
(void (Detector::*)(defs::ROI, int)) & Detector::setROI,
py::arg(), py::arg());
CppDetectorApi.def(
"clearROI", (void (Detector::*)(sls::Positions)) & Detector::clearROI,
py::arg() = Positions{});
CppDetectorApi.def("getExptimeLeft",
(Result<sls::ns>(Detector::*)(sls::Positions) const) &
Detector::getExptimeLeft,
py::arg() = Positions{});
CppDetectorApi.def("getNumberOfBursts",
(Result<int64_t>(Detector::*)(sls::Positions) const) &
Detector::getNumberOfBursts,
@ -1678,6 +1664,14 @@ void init_det(py::module &m) {
(void (Detector::*)(int, sls::Positions)) &
Detector::setRxDbitOffset,
py::arg(), py::arg() = Positions{});
CppDetectorApi.def("getRxDbitReorder",
(Result<bool>(Detector::*)(sls::Positions) const) &
Detector::getRxDbitReorder,
py::arg() = Positions{});
CppDetectorApi.def("setRxDbitReorder",
(void (Detector::*)(bool, sls::Positions)) &
Detector::setRxDbitReorder,
py::arg(), py::arg() = Positions{});
CppDetectorApi.def("setDigitalIODelay",
(void (Detector::*)(uint64_t, int, sls::Positions)) &
Detector::setDigitalIODelay,
@ -1861,13 +1855,22 @@ void init_det(py::module &m) {
Detector::setPatternWaitAddr,
py::arg(), py::arg(), py::arg() = Positions{});
CppDetectorApi.def(
"getPatternWaitTime",
"getPatternWaitClocks",
(Result<uint64_t>(Detector::*)(int, sls::Positions) const) &
Detector::getPatternWaitTime,
Detector::getPatternWaitClocks,
py::arg(), py::arg() = Positions{});
CppDetectorApi.def("setPatternWaitTime",
CppDetectorApi.def("setPatternWaitClocks",
(void (Detector::*)(int, uint64_t, sls::Positions)) &
Detector::setPatternWaitTime,
Detector::setPatternWaitClocks,
py::arg(), py::arg(), py::arg() = Positions{});
CppDetectorApi.def(
"getPatternWaitInterval",
(Result<sls::ns>(Detector::*)(int, sls::Positions) const) &
Detector::getPatternWaitInterval,
py::arg(), py::arg() = Positions{});
CppDetectorApi.def("setPatternWaitInterval",
(void (Detector::*)(int, sls::ns, sls::Positions)) &
Detector::setPatternWaitInterval,
py::arg(), py::arg(), py::arg() = Positions{});
CppDetectorApi.def("getPatternMask",
(Result<uint64_t>(Detector::*)(sls::Positions)) &

View File

@ -1,3 +1,4 @@
#include <chrono>
#include "py_headers.h"
#include "DurationWrapper.h"
@ -19,4 +20,25 @@ void init_duration(py::module &m) {
<< " count: " << self.count() << ")";
return ss.str();
});
m.def(
"test_return_DurationWrapper",
[]() {
DurationWrapper t(1.3);
return t;
},
R"(
Test function to return a DurationWrapper object. Ensures that the automatic conversion in typecaster.h works.
)");
m.def(
"test_duration_to_ns",
[](const std::chrono::nanoseconds t) {
//Duration wrapper is used to be able to convert from time in python to chrono::nanoseconds
//return count to have something to test
return t.count();
},
R"(
Test function convert DurationWrapper or number to chrono::ns. Ensures that the automatic conversion in typecaster.h works.
)"); // default value to test the default constructor
}

View File

@ -121,13 +121,9 @@ void init_enums(py::module &m) {
.value("VTHRESHOLD", slsDetectorDefs::dacIndex::VTHRESHOLD)
.value("IO_DELAY", slsDetectorDefs::dacIndex::IO_DELAY)
.value("VREF_DS", slsDetectorDefs::dacIndex::VREF_DS)
.value("VCASCN_PB", slsDetectorDefs::dacIndex::VCASCN_PB)
.value("VCASCP_PB", slsDetectorDefs::dacIndex::VCASCP_PB)
.value("VOUT_CM", slsDetectorDefs::dacIndex::VOUT_CM)
.value("VCASC_OUT", slsDetectorDefs::dacIndex::VCASC_OUT)
.value("VIN_CM", slsDetectorDefs::dacIndex::VIN_CM)
.value("VREF_COMP", slsDetectorDefs::dacIndex::VREF_COMP)
.value("IB_TESTC", slsDetectorDefs::dacIndex::IB_TESTC)
.value("VB_COMP", slsDetectorDefs::dacIndex::VB_COMP)
.value("VDD_PROT", slsDetectorDefs::dacIndex::VDD_PROT)
.value("VIN_COM", slsDetectorDefs::dacIndex::VIN_COM)

View File

@ -20,8 +20,10 @@ void init_pattern(py::module &m) {
});
py::class_<sls::Pattern> Pattern(m, "Pattern");
Pattern.def(py::init());
Pattern.def("load", &sls::Pattern::load);
Pattern.def("data", (pat * (sls::Pattern::*)()) & sls::Pattern::data,
Pattern.def(py::init())
.def("load", &sls::Pattern::load)
.def("save", &sls::Pattern::save)
.def("str", &sls::Pattern::str)
.def("data", (pat * (sls::Pattern::*)()) & sls::Pattern::data,
py::return_value_policy::reference);
}

View File

@ -54,11 +54,16 @@ template <> struct type_caster<std::chrono::nanoseconds> {
value = duration_cast<nanoseconds>(duration<double>(PyFloat_AsDouble(src.ptr())));
return true;
}
// If invoked with an int we assume it is nanoseconds and convert, same as in chrono.h
if (PyLong_Check(src.ptr())) {
value = duration_cast<nanoseconds>(duration<int64_t>(PyLong_AsLongLong(src.ptr())));
return true;
}
// Lastly if we were actually called with a DurationWrapper object we get
// the number of nanoseconds and create a std::chrono::nanoseconds from it
py::object py_cls = py::module::import("_slsdet").attr("DurationWrapper");
py::object py_cls = py::module::import("slsdet._slsdet").attr("DurationWrapper");
if (py::isinstance(src, py_cls)){
sls::DurationWrapper *cls = src.cast<sls::DurationWrapper *>();
value = nanoseconds(cls->count());
@ -77,7 +82,7 @@ template <> struct type_caster<std::chrono::nanoseconds> {
* set the count from chrono::nanoseconds and return
*/
static handle cast(std::chrono::nanoseconds src, return_value_policy /* policy */, handle /* parent */) {
py::object py_cls = py::module::import("_slsdet").attr("DurationWrapper");
py::object py_cls = py::module::import("slsdet._slsdet").attr("DurationWrapper");
py::object* obj = new py::object;
*obj = py_cls();
sls::DurationWrapper *dur = obj->cast<sls::DurationWrapper *>();

View File

@ -0,0 +1,58 @@
import pytest
from slsdet import DurationWrapper
#import the compiled extension to use test functions for the automatic conversion
from slsdet import _slsdet
def test_default_construction_of_DurationWrapper():
"""Test default construction of DurationWrapper"""
t = DurationWrapper()
assert t.count() == 0
assert t.total_seconds() == 0
def test_construction_of_DurationWrapper():
"""Test construction of DurationWrapper with total_seconds"""
t = DurationWrapper(5)
assert t.count() == 5e9
assert t.total_seconds() == 5
def test_set_count_on_DurationWrapper():
"""Test set_count on DurationWrapper"""
t = DurationWrapper()
t.set_count(10)
assert t.count() == 10
assert t.total_seconds() == 10e-9
t.set_count(0)
assert t.count() == 0
assert t.total_seconds() == 0
def test_return_a_DurationWrapper_from_cpp():
"""Test returning a DurationWrapper from C++"""
t = _slsdet.test_return_DurationWrapper()
assert t.count() == 1.3e9
assert t.total_seconds() == 1.3
def test_call_a_cpp_function_with_a_duration_wrapper():
"""C++ functions can accept a DurationWrapper"""
t = DurationWrapper(5)
assert _slsdet.test_duration_to_ns(t) == 5e9
def test_call_a_cpp_function_converting_number_to_DurationWrapper():
"""int and float can be converted to std::chrono::nanoseconds"""
assert _slsdet.test_duration_to_ns(0) == 0
assert _slsdet.test_duration_to_ns(3) == 3e9
assert _slsdet.test_duration_to_ns(1.3) == 1.3e9
assert _slsdet.test_duration_to_ns(10e-9) == 10
def test_call_a_cpp_function_with_datetime_timedelta():
"""datetime.timedelta can be converted to std::chrono::nanoseconds"""
import datetime
t = datetime.timedelta(seconds=5)
assert _slsdet.test_duration_to_ns(t) == 5e9
t = datetime.timedelta(seconds=0)
assert _slsdet.test_duration_to_ns(t) == 0
t = datetime.timedelta(seconds=1.3)
assert _slsdet.test_duration_to_ns(t) == 1.3e9

View File

@ -0,0 +1,152 @@
import pytest
from slsdet import PatternGenerator
def apply_detconf(p):
"""
Hacky workaround to apply detConf_mh02 to a pattern
"""
DACMON = 0
cnt_en_3 = 1
pulse_counter_en = 2
cnt_en_1 = 3
par_load = 4
pulse_mux_ctr = 5
reset_cnt = 6
reset_periphery = 7
config_load = 8
cnt_en_0 = 9
tbl = 10
clk_ext = 11
trimbit_load_reg = 12
store = 13
data_in = 14
en_pll_clk = 15
cnt_en_2 = 16
DACINT = 17
data_out_slow = 18 #IN
COMP2_MON = 19 #IN
start_read = 20
dac_store = 21
CNT3_MON = 22 #IN
EN_PIX_DIG_MON = 23
clk_sel = 24
BUSY = 25 #IN
COMP3_MON = 26 #IN
CNT2_MON = 27 #IN
dbit_ena=62 #FIFO LATCH
adc_ena=63 #ADC ENABLE
#FPGA input/ouutputs
p.setoutput(DACMON)
p.setoutput(cnt_en_3)
p.setoutput(pulse_counter_en)
p.setoutput(cnt_en_1)
p.setoutput(par_load)
p.setoutput(pulse_mux_ctr)
p.setoutput(reset_cnt)
p.setoutput(reset_periphery)
p.setoutput(cnt_en_0)
p.setoutput(tbl)
p.setoutput(clk_ext)
p.setoutput(config_load)
p.setoutput(trimbit_load_reg)
p.setoutput(store)
p.setoutput(data_in)
p.setoutput(en_pll_clk)
p.setoutput(cnt_en_2)
p.setoutput(DACINT)
p.setinput(data_out_slow)
p.setinput(COMP2_MON)
p.setoutput(start_read)
p.setoutput(dac_store)
p.setinput(CNT3_MON)
p.setoutput(EN_PIX_DIG_MON)
p.setoutput(clk_sel)
p.setinput(BUSY)
p.setinput(COMP3_MON)
p.setinput(CNT2_MON)
#system signals
p.setoutput(adc_ena)
# FIFO LATCH
p.setoutput(dbit_ena)
return p
def test_first_two_PW():
p = PatternGenerator()
#The pattern is created with a single empty word
assert p.pattern.limits[0] == 0
assert p.pattern.limits[1] == 0
p.SB(8)
p.PW()
#When doing the first PW the empty word is overwritten
assert p.pattern.limits[0] == 0
assert p.pattern.limits[1] == 0
assert p.pattern.word[0] == 256
p.SB(9)
p.PW()
#When doing the second PW we add a new word
assert p.pattern.limits[0] == 0
assert p.pattern.limits[1] == 1
assert p.pattern.word[0] == 256
assert p.pattern.word[1] == 768
def test_simple_pattern():
"""
Using enable pll pattern for MH02
"""
en_pll_clk = 15
p = PatternGenerator()
p = apply_detconf(p)
p.SB(en_pll_clk)
p.PW()
p.PW()
lines = str(p).split("\n")
enable_pll_pattern = [
"patword 0x0000 0x0000000000008000",
"patword 0x0001 0x0000000000008000",
"patioctrl 0xc000000001b3ffff",
"patlimits 0x0000 0x0001",
"patloop 0 0x1fff 0x1fff",
"patnloop 0 0",
"patloop 1 0x1fff 0x1fff",
"patnloop 1 0",
"patloop 2 0x1fff 0x1fff",
"patnloop 2 0",
"patloop 3 0x1fff 0x1fff",
"patnloop 3 0",
"patloop 4 0x1fff 0x1fff",
"patnloop 4 0",
"patloop 5 0x1fff 0x1fff",
"patnloop 5 0",
"patwait 0 0x1fff",
"patwaittime 0 0",
"patwait 1 0x1fff",
"patwaittime 1 0",
"patwait 2 0x1fff",
"patwaittime 2 0",
"patwait 3 0x1fff",
"patwaittime 3 0",
"patwait 4 0x1fff",
"patwaittime 4 0",
"patwait 5 0x1fff",
"patwaittime 5 0",
]
assert len(lines) == len(enable_pll_pattern)
for i, line in enumerate(lines):
assert line == enable_pll_pattern[i]

50
python/tests/test_bits.py Normal file
View File

@ -0,0 +1,50 @@
from slsdet.bits import clearbit, clearbit_arr, setbit, setbit_arr
import numpy as np
def test_clearbit_on_python_int():
val = 5 # 0b101
r = clearbit(0, val)
assert r == 4
assert val == 5
def test_setbit_on_python_int():
val = 5 # 0b101
r = setbit(1, val)
assert r == 7
assert val == 5
def test_setbit_doesnt_change_type():
word = np.int32(5)
ret = setbit(0, word)
assert isinstance(ret, np.int32)
def test_clearbit_doesnt_change_type():
word = np.uint8(5)
ret = clearbit(0, word)
assert isinstance(ret, np.uint8)
def test_setbit_on_array_element():
arr = np.zeros(10, dtype=np.uint64)
arr[5] = setbit(0, arr[5])
arr[5] = setbit(1, arr[5])
arr[5] = setbit(4, arr[5])
assert arr[4] == 0
assert arr[5] == 19 # 0b10011
assert arr[6] == 0
def test_setbit_arr():
arr = np.zeros(10, dtype=np.int32)
setbit_arr(3, arr[3:9])
assert all(arr == np.array((0, 0, 0, 8, 8, 8, 8, 8, 8, 0), dtype=np.int32))
def test_clearbit_arr():
arr = np.array((5, 5, 5), dtype=np.int8)
clearbit_arr(0, arr)
assert all(arr == (4, 4, 4))

View File

@ -0,0 +1 @@
../slsDetectorServers/xilinx_ctbDetectorServer/bin/xilinx_ctbDetectorServer_developer

View File

@ -3,14 +3,14 @@
#ifndef ANALOGDETECTOR_H
#define ANALOGDETECTOR_H
//#include <mutex>
#include <mutex>
#include "commonModeSubtractionNew.h"
#include "ghostSummation.h"
#include "pedestalSubtraction.h"
#include "sls/tiffIO.h"
#include "slsDetectorData.h"
#include "slsInterpolation.h"
#include "sls/tiffIO.h"
#include <pthread.h>
#ifdef ROOTSPECTRUM
@ -90,6 +90,8 @@ template <class dataType> class analogDetector {
ymin = 0;
ymax = ny;
fMode = ePedestal;
dMode = eInterpolating;
// std::cout << "dMode " << dMode << std::endl;
thr = 0;
myFile = NULL;
#ifdef ROOTSPECTRUM
@ -111,15 +113,20 @@ template <class dataType> class analogDetector {
destructor. Deletes the pdestalSubtraction array and the image
*/
virtual ~analogDetector() {
// std::cout << "#### Debug: Destructing analogDetector! ####"
// << std::endl;
for (int i = 0; i < ny; i++) {
delete[] stat[i];
if (stat[i]) { delete[] stat[i]; stat[i] = nullptr; }
// delete[] stat[i];
/* delete [] pedMean[i]; */
/* delete [] pedVariance[i]; */
};
}
/* delete [] pedMean; */
/* delete [] pedVariance; */
delete[] stat;
delete[] image;
// delete[] stat;
// delete[] image;
if (stat) { delete[] stat; stat = nullptr; }
if (image) { delete[] image; image = nullptr; }
#ifdef ROOTSPECTRUM
delete hs;
#ifdef ROOTCLUST
@ -137,6 +144,8 @@ template <class dataType> class analogDetector {
*/
analogDetector(analogDetector *orig) {
/* copy construction from orig*/
// std::cout << "#### Debug: Calling analogDetector cloning method! ####"
// << std::endl;
det = orig->det;
nx = orig->nx;
ny = orig->ny;
@ -152,6 +161,8 @@ template <class dataType> class analogDetector {
thr = orig->thr;
// nSigma=orig->nSigma;
fMode = orig->fMode;
dMode = orig->dMode;
// std::cout << "dMode " << dMode << std::endl;
myFile = orig->myFile;
stat = new pedestalSubtraction *[ny];
@ -216,12 +227,75 @@ template <class dataType> class analogDetector {
ghSum = NULL;
}
/**
constructor creating a deep copy of another analog detector
\param other analog Detector structure to be copied
*/
analogDetector(const analogDetector &other)
: det(other.det), nx(other.nx), ny(other.ny), dataSign(other.dataSign),
iframe(other.iframe), gmap(other.gmap), id(other.id),
xmin(other.xmin), xmax(other.xmax), ymin(other.ymin),
ymax(other.ymax), thr(other.thr), fMode(other.fMode),
dMode(other.dMode), myFile(NULL) {
// std::cout << "#### Debug: Calling analogDetector copy constructor! ####"
// << std::endl;
// Deep copy the stat array
stat = new pedestalSubtraction *[ny];
for (int i = 0; i < ny; i++) {
stat[i] = new pedestalSubtraction[nx];
std::copy(other.stat[i], other.stat[i] + nx, stat[i]);
}
// Deep copy image array
image = new int[nx * ny];
std::copy(other.image, other.image + (nx * ny), image);
// Copy common-mode subtraction object (if it exists)
if (other.cmSub) {
cmSub = other.cmSub->Clone();
std::cout << "Copying cmSub" << std::endl;
} else {
cmSub = nullptr;
}
// Copy ghost summation object (if it exists)
if (other.ghSum) {
ghSum = other.ghSum->Clone();
std::cout << "Copying ghSum" << std::endl;
} else {
ghSum = nullptr;
}
// Ensure pedestal values are copied properly
int nped = other.GetNPedestals(0, 0);
for (int iy = 0; iy < ny; ++iy) {
for (int ix = 0; ix < nx; ++ix) {
stat[iy][ix].SetNPedestals(nped);
setPedestal(ix, iy, other.getPedestal(ix, iy),
other.getPedestalRMS(ix, iy),
other.GetNPedestals(ix, iy));
}
}
}
/**
clone. Must be virtual!
\returns a clone of the original analog detector
*/
virtual analogDetector *Clone() = 0;
/**
Deep copy. Must be virtual!
This is a new addition because of multithreaded storage cell data (where
each sc has its own mutex). If the pure virtual function exists here,
EVERY derived class has to overwrite it! That means a Copy() function
must also be implemented in any derived class. \returns a deep copy of
the original analog detector
*/
virtual analogDetector *Copy() = 0;
/**
Gives an id to the structure. For debugging purposes in case of
multithreading. \param i is to be set \returns current id
@ -548,6 +622,11 @@ template <class dataType> class analogDetector {
return ped;
};
// Const version for use in the copy constructor
virtual double getPedestal(int ix, int iy, int cm = 0) const {
return stat[iy][ix].getPedestal();
};
/**
gets pedestal rms (i.e. noise)
\param ix pixel x coordinate
@ -566,6 +645,11 @@ template <class dataType> class analogDetector {
return ped;
};
// Const version for use in the copy constructor
virtual double getPedestalRMS(int ix, int iy) const {
return stat[iy][ix].getPedestalRMS();
};
/**
sets pedestal
\param ix pixel x coordinate
@ -1226,7 +1310,7 @@ template <class dataType> class analogDetector {
/** gets number of samples for moving average pedestal calculation
\returns actual number of samples
*/
int GetNPedestals(int ix, int iy) {
int GetNPedestals(int ix, int iy) const {
if (ix >= 0 && ix < nx && iy >= 0 && iy < ny)
return stat[iy][ix].GetNPedestals();
else

View File

@ -50,6 +50,8 @@ template <typename Element> class CircularFifo {
mutable sem_t data_mutex;
mutable sem_t free_mutex;
unsigned int increment(unsigned int idx_) const;
int id_;
int thread_id_;
};
template <typename Element> int CircularFifo<Element>::getDataValue() const {
@ -74,14 +76,18 @@ template <typename Element> int CircularFifo<Element>::getFreeValue() const {
template <typename Element>
bool CircularFifo<Element>::push(Element *&item_, bool no_block) {
// check for fifo full
if (no_block && isFull())
return false;
if (no_block && isFull()) {
//std::cout << "Full Fifo at push. Returning." << std::endl;
return false; // No space, return immediately
}
sem_wait(&free_mutex);
array[tail] = item_;
tail = increment(tail);
sem_post(&data_mutex);
return true;
//std::cout << "Thread " << thread_id_ <<" Push Fifo " << id_ << " item " << static_cast<void*>(item_) << std::endl;
sem_wait(&free_mutex); // Wait for space
array[tail] = item_; // Add item to the buffer
tail = increment(tail); // Move the tail pointer
sem_post(&data_mutex); // Signal that there is new data
return true; // Success
}
/** Consumer only: Removes and returns item from the queue
@ -94,14 +100,18 @@ bool CircularFifo<Element>::push(Element *&item_, bool no_block) {
template <typename Element>
bool CircularFifo<Element>::pop(Element *&item_, bool no_block) {
// check for fifo empty
if (no_block && isEmpty())
return false;
if (no_block && isEmpty()) {
//std::cout << "Empty Fifo at pop. Returning." << std::endl;
return false; // No data in fifo, return immediately
}
sem_wait(&data_mutex);
item_ = array[head];
head = increment(head);
sem_post(&free_mutex);
return true;
//std::cout << "Thread " << thread_id_ << " Pop Fifo " << id_ << " item " << static_cast<void*>(item_) << std::endl;
sem_wait(&data_mutex); // Wait for data
item_ = array[head]; // Retreive item from the current head of the buffer
head = increment(head); // Move the head pointer (to point to the next item)
sem_post(&free_mutex); // Signal that there is new free space available
return true; //Success
}
/** Useful for testinng and Consumer check of status

View File

@ -0,0 +1,482 @@
#include "HDF5File.h"
#include "ansi.h"
#include <algorithm>
#include <fmt/ranges.h>
/*
* No class member helper functions
*/
std::string vectorToString(std::vector<hsize_t> const& v) {
return fmt::format("({})", fmt::join(v, ", "));
}
/*
* increment frame offset (if s dimension exists, loop through all before incrementing z)
* should also work if file_dims[1] is not s but x (in that case we ignore it)
*/
void conditionalIncrement(std::vector<hsize_t>& vec, hsize_t max_value) {
if (vec.size() < 3) {
throw std::invalid_argument("Vector must have at least 3 elements.");
}
// If vector has 4 elements, increment vec[1] first
if (vec.size() == 4) {
if (++vec[1] >= max_value) { //max_value is never reached!
vec[1] = 0; // Reset and increment vec[0]
++vec[0];
}
}
// If vector has 3 elements, increment vec[0] directly
else if (vec.size() == 3) {
++vec[0];
}
}
void printDatatypeSize(hid_t dataset) {
hid_t datatype = H5Dget_type(dataset);
H5T_class_t class_id = H5Tget_class(datatype);
size_t type_size = H5Tget_size(datatype);
H5Tclose(datatype);
std::cout << " dataset type class: " << class_id
<< ", size: " << type_size << " bytes\n";
//Ensure the read datatype matches a system native type correctly
//hid_t read_type = (type_size == 8) ? H5T_NATIVE_LLONG : H5T_NATIVE_UINT;
//return read_type;
}
/* **********************
* Class member functions
* **********************
*/
// Default constructor
/*
HDF5File::HDF5File () {
//InitializeParameters(); //old
}
*/
/*
HDF5File::~HDF5File () {
if(current_image)
delete [] current_image;
}
*/
void HDF5File::SetImageDataPath (std::string const& name) {
std::cout << "Image dataset path set to " << name << std::endl;
data_datasetname = name;
}
void HDF5File::SetFrameIndexPath (std::string const& name) {
std::cout << "Frame index dataset path set to " << name << std::endl;
index_datasetname = name;
}
void HDF5File::InitializeDimensions () {
rank = H5Sget_simple_extent_ndims(dataspace);
file_dims.resize(rank);
H5Sget_simple_extent_dims(dataspace, file_dims.data(), nullptr);
std::cout << "Dataset dimensions: " << vectorToString(file_dims) << "\n";
}
std::vector<hsize_t> HDF5File::GetDatasetDimensions() {
return file_dims;
}
std::vector<hsize_t> HDF5File::GetChunkDimensions() {
return chunk_dims;
}
hsize_t HDF5File::GetRank() {
return rank;
}
bool HDF5File::ValidateDimensions () {
// validate rank
if(rank != RANK) {
cprintf(RED,"rank found %llu. Expected %d\n", rank, RANK);
std::cerr << "Error: Rank could not be validated\n";
return false;
}
// validate file dimensions of x and y (assuming those are the last two dimensions of the dataset)
if ( (file_dims[file_dims.size()-2] != DEFAULT_X_DIMS) || (file_dims[file_dims.size()-1] != DEFAULT_Y_DIMS) ) {
cprintf(RED,"file dimensions of x found %llu. Expected %d\n", file_dims[file_dims.size()-2], DEFAULT_X_DIMS);
cprintf(RED,"file dimensions of y found %llu. Expected %d\n", file_dims[file_dims.size()-1], DEFAULT_Y_DIMS);
std::cerr << "Error: Dataset dimensions could not be validated\n";
return false;
}
cprintf(GREEN, "File rank & dimensions validated.");
return true;
}
bool HDF5File::ReadChunkDimensions () {
// Get layout
hid_t plist_id = H5Dget_create_plist(dataset);
if (H5Pget_layout (plist_id) != H5D_CHUNKED) {
cprintf(RED,"NOTE: Dataset is not chunked!\n");
std::cerr << "Error: Dataset is not chunked\n";
return false;
}
// Get Chunk Dimensions
int rank_chunk = H5Pget_chunk (plist_id, 0, nullptr);
chunk_dims.resize(rank_chunk);
H5Pget_chunk (plist_id, rank_chunk, chunk_dims.data());
std::cout << "Chunk dimensions: " << vectorToString(chunk_dims) << "\n";
H5Pclose (plist_id);
return true;
}
bool HDF5File::ValidateChunkDimensions () {
// validate rank
if(chunk_dims.size() != rank) {
cprintf(RED,"Chunk rank does not match dataset rank! Found %lu. Expected %llu\n", chunk_dims.size(), rank);
std::cerr << "Error: Chunk rank does not match dataset rank\n";
return false;
}
// validate chunk dimensions of x and y (assuming those are the last two dimensions of the dataset)
if ( (chunk_dims[chunk_dims.size()-2] != DEFAULT_CHUNK_X_DIMS) || (chunk_dims[chunk_dims.size()-1] != DEFAULT_CHUNK_Y_DIMS) ) {
cprintf(RED,"file dimensions of x found %llu. Expected %d\n", chunk_dims[chunk_dims.size()-2], DEFAULT_CHUNK_X_DIMS);
cprintf(RED,"file dimensions of y found %llu. Expected %d\n", chunk_dims[chunk_dims.size()-1], DEFAULT_CHUNK_Y_DIMS);
std::cerr << "Error: Chunk dimensions could not be validated\n";
return false;
}
cprintf(GREEN, "Chunk rank & dimensions validated.");
return true;
}
bool HDF5File::OpenFrameIndexDataset() {
// Get all the frame numbers
// Open frame index dataset
hid_t fi_dataset = H5Dopen2 (file, index_datasetname.c_str(), H5P_DEFAULT);
if (fi_dataset < 0){
cprintf (RED,"Could not open frame index dataset %s\n", index_datasetname.c_str());
std::cerr << "Error: Could not open frame index dataset\n";
return false;
}
hid_t fi_dataspace = H5Dget_space (fi_dataset);
int fi_rank = H5Sget_simple_extent_ndims(fi_dataspace);
std::vector<hsize_t> fi_dims(fi_rank);
H5Sget_simple_extent_dims (fi_dataspace, fi_dims.data(), nullptr);
std::cout << "Frame index dataset dimensions: " << vectorToString(fi_dims) << "\n";
// validate size
if (fi_dims[0] != file_dims[0]) {
cprintf (RED,"Frame index dimensions of z found %llu. Expected %llu\n", fi_dims[0], file_dims[0]);
std::cerr << "Error: Z dimension of frame index dataset does not align with z dimension of image dataset\n";
H5Sclose (fi_dataspace);
H5Dclose (fi_dataset);
return false;
}
// allocate frame index memory
frame_index_list.resize(fi_dims[0]); //file_dims
// read and print datatype size of dataset
std::cout << "Frame index";
printDatatypeSize(fi_dataset);
// make sure we only read the first column of the frame index dataset (not all storage cells)
//NOTE: For XFEL datasets, this may mean that some frame numbers are skipped
//(because they assign a unique frame number to every storage cell)
//Possibly, there is a cleaner fix for this...
std::vector<hsize_t> start(fi_rank,0);
std::vector<hsize_t> count(fi_rank,1);
count[0] = fi_dims[0];
hid_t memspace = H5Screate_simple (fi_rank, count.data(), nullptr);
if (memspace < 0) {
std::cerr << "Error: Failed to create memory space for HDF5 read operation\n";
H5Sclose(memspace);
H5Sclose(fi_dataspace);
H5Dclose(fi_dataset);
return false;
}
// Create hyperslab selection
if (H5Sselect_hyperslab(fi_dataspace, H5S_SELECT_SET, start.data(), nullptr, count.data(), nullptr) < 0 ) {
cprintf (RED,"Could not create hyperslab for %s\n", vectorToString(start).c_str());
std::cerr << "Error: Hyperslab creation failed for " << vectorToString(start) << "\n";
H5Sclose(memspace);
H5Sclose (fi_dataspace);
H5Dclose(fi_dataset);
return false;
}
//read frame index values
//Is u32 correct? I would think not. But I get a segmentation fault if I use u64.
if (H5Dread (fi_dataset, H5T_STD_U64LE, memspace, fi_dataspace, H5P_DEFAULT, frame_index_list.data()) < 0) {
cprintf (RED,"Could not read frame index dataset %s\n", index_datasetname.c_str());
std::cerr << "Error: Could not read frame index dataset\n";
H5Sclose(memspace);
H5Sclose (fi_dataspace);
H5Dclose (fi_dataset);
return false;
}
H5Sclose(memspace);
H5Sclose (fi_dataspace);
H5Dclose(fi_dataset);
return true;
}
int HDF5File::OpenResources (char const*const fname, bool validate) {
std::cout << "Debug HDF5File.cpp: Attempting to open file " << fname << std::endl;
// Open File
file = H5Fopen (fname, H5F_ACC_RDONLY, H5P_DEFAULT);
if (file < 0) {
cprintf(RED,"Could not open hdf5 file\n");
std::cerr << "Error: H5Fopen failed\n";
return 0;
}
cprintf(BLUE, "Opened File: %s\n", fname);
// Open Dataset
dataset = H5Dopen2 (file, data_datasetname.c_str(), H5P_DEFAULT);
if (dataset < 0){
cprintf(RED,"Could not open dataset\n");
std::cerr << "Error: H5Dopen2 failed\n";
CloseResources ();
return 0;
}
cprintf(BLUE, "Opened Dataset: %s\n", data_datasetname.c_str());
// print datatype size of dataset
std::cout << "Image";
printDatatypeSize(dataset);
// Create Dataspace
dataspace = H5Dget_space (dataset);
if (dataspace < 0){
cprintf(RED,"Could not open dataspace\n");
std::cerr << "Error: H5Dget_space failed\n";
CloseResources ();
return 0;
}
// Get Dimensions
InitializeDimensions();
// Get chunk dimensions
if (!ReadChunkDimensions()) {
CloseResources();
return 0;
}
// validate file dimensions
if (validate) {
if ( !ValidateDimensions() || !ValidateChunkDimensions() ) {
CloseResources();
return 0;
}
}
//Read frame indices
if (!OpenFrameIndexDataset()) {
CloseResources();
return 0;
}
return 1;
}
void HDF5File::CloseResources () {
if (dataspace >=0 ) {
H5Sclose(dataspace);
dataspace = -1;
}
if (dataset >=0 ) {
H5Dclose(dataset);
dataset = -1;
}
if (file >=0 ) {
H5Fclose(file);
file = -1;
}
}
/*
* Function takes uint16_t* argument to make explicit that the caller has to handle memory allocation and deallocation.
* This is legacy caused by the structure with which the slsDetectorCalibration cluster finder is written.
* (Best practice for modern C++ would be using smart pointers.)
*
* Originially, this function took uint16_t** which may lead to memory management issues since image gets redirected
* to point to current_image, which is owned by HDF5File.
* (Good practice in classic C-style. HDF5File needs to clean up the resource at destruction.)
*
* \param image pointer to uint16_t, buffer which the image is read into. (Memory handled by caller!)
* \param offset contains iFrame at [0] and storage cell number at [1],
* depending on dimensionality of the dataset, the storage cell number may not be included.
* Note that frame number (as read from file) may (likely) differ from frame index (in the dataset)!
*/
int HDF5File::ReadImage (uint16_t* image, std::vector<hsize_t>& offset ) {
// Validate input arguments
if (!image) {
std::cerr << "Error: image buffer is null.\n";
return -99;
}
if ( offset.size() != rank-2 ) {
cprintf ( RED,"Offset vector must have size %llu. Found %lu\n", rank-2, offset.size() );
std::cerr << "Error: Wrong offset vector size\n";
CloseResources ();
return -99;
}
// Initialize frame_offset
if (frame_offset.empty())
frame_offset.resize(rank,0);
// Check if we reached the end of file
// Compares that the offsets of frame and storage cell (Z and S) have reached the end of file
// Excludes X and Y indices (of the image dataset) from the comparison
// As it is now, this never triggers, because frame_offset[1] is never equals file_dims[1]=16
/*
if( std::equal( frame_offset.cbegin(), frame_offset.cend()-2, file_dims.cbegin() ) ) {
printf("End of file reached\n");
return -1;
}
*/
if (frame_offset[0] == file_dims[0]) {
printf("End of file reached\n");
return -1;
}
/* //old
if (frame_offset[0] == file_dims[0]-1) {
printf("end of file\n");
return -1;
}
*/
// Validate frame_offset index
if (frame_offset[0] >= frame_index_list.size()) {
std::cerr << "Error: frame_offset[0] = " << frame_offset[0] << " of bounds.\n";
return -99;
}
// Check if images exist at the current frame offset
if (frame_index_list[frame_offset[0]] == 0) {
cprintf (RED,"No images at this frame offset %llu\n", frame_offset[0]);
std::cerr << "Error: Framenumber 0 at this frame offset\n";
CloseResources ();
return -99;
}
// Optional: Ensure dataset and dataspace are valid
if (dataset < 0) {
std::cerr << "Error: Invalid dataset ID.\n";
return -99;
}
if (dataspace < 0) {
std::cerr << "Error: Invalid dataspace.\n";
return -99;
}
// Define the size of the hyperslab to read
std::vector<hsize_t> frame_size(rank, 1);
std::copy(file_dims.begin() + rank-2, file_dims.end(), frame_size.begin() + rank-2);
/*
for ( int d=0; d < rank; ++d ) {
if (d < rank-2)
frame_size[d] = 1;
if ( d >= rank-2 )
frame_size[d] = file_dims[d];
}
*/
// Define memory space
hid_t memspace = H5Screate_simple (rank, frame_size.data(), nullptr);
if (memspace < 0) {
std::cerr << "Error: Failed to create memory space for HDF5 read operation\n";
CloseResources();
return -99;
}
// Create hyperslab selection
// This aligns dataspace such that we read the correct frame
if (H5Sselect_hyperslab(dataspace, H5S_SELECT_SET, frame_offset.data(), nullptr, frame_size.data(), nullptr) < 0 ) {
cprintf (RED,"Could not create hyperslab for frame offset %s\n", vectorToString(frame_offset).c_str());
std::cerr << "Error: Hyperslab creation failed for frame offset " << vectorToString(frame_offset) << "\n";
CloseResources();
H5Sclose(memspace);
return -99;
}
// Read dataset into image buffer (previously read to current_image owned by HDF5File)
if (H5Dread(dataset, H5T_STD_U16LE, memspace, dataspace, H5P_DEFAULT, image) < 0 ) {
cprintf (RED,"Could not read dataset for frame offset %s\n", vectorToString(frame_offset).c_str());
std::cerr << "Error: Reading of dataset failed for given start frame offset " << vectorToString(frame_offset) << "\n";
CloseResources ();
H5Sclose(memspace);
return -99;
}
// Clean up memory space
H5Sclose(memspace);
//*image = current_image; //if uint16_t** is passed, HDF5File owns the resource image points to, which is potentially dangerous
// Return frame number
unsigned int retval = frame_index_list[frame_offset[0]];
// Pass updated frame offset value(s) via offset parameter vector
std::copy_n(frame_offset.begin(), offset.size(), offset.begin());
/*
std::transform( offset.begin(), offset.end(), offset.begin(),
[&, i = 0](size_t) mutable { return frame_offset[i++]; } );
*/
// Increment frame offset correctly
conditionalIncrement(frame_offset, file_dims[1]);
//++frame_offset[0]; //old
return retval;
}
void HDF5File::PrintCurrentImage (uint16_t* image) {
printf("\n");
printf("Frame %llu, Image: %llu\n", frame_offset[0]-1, frame_index_list[frame_offset[0]-1]);
hsize_t size = file_dims[rank-1] * file_dims[rank-2];
for (hsize_t i = 0; i < size; ++i){
printf("%u ", image[i]);
if (!((i+1) % file_dims[rank-2] ))
printf("\n\n");
}
printf("\n\n\n\n");
}

View File

@ -0,0 +1,159 @@
#pragma once
/************************************************
* @file HDF5Fle.h
* @short functions to open/close/read HDF5 File
* Adapted for generalization, accepts rank 3 and 4
* Supports control over storage cells
***********************************************/
/**
*@short functions to open/close/read HDF5 File
*/
#include <iostream>
#include <vector>
#include <string>
#include "hdf5.h"
#include "hdf5_hl.h"
//#define MAX_STR_LENGTH 1000
#define RANK 4 // Dimension of the image dataset, only for validation
#define DEFAULT_Z_DIMS 10000 // only for validation
#define DEFAULT_Y_DIMS 1024 // only for validation
#define DEFAULT_X_DIMS 512 // only for validation
//#define DEFAULT_S_DIMS 1 // Storage cells
#define DEFAULT_CHUNK_Z_DIMS 1 // only for validation
#define DEFAULT_CHUNK_Y_DIMS 1024 // only for validation
#define DEFAULT_CHUNK_X_DIMS 512 // only for validation
//#define DEFAULT_CHUNK_S_DIMS 1
#define DATA_DATASETNAME "/data/JF18T01V01/data" //Furka JF
#define INDEX_DATASETNAME "/data/JF18T01V01/frame_index"
//enum{Z,S,X,Y}; //S is the storage cell //enum is not used
class HDF5File {
public:
/**
* Constructor
*/
//HDF5File () = default; //No need to declare if it is default
/**
* Destructor
*/
//~HDF5File () = default; //Since the destructor is default (and copy and move are default too)
std::vector<hsize_t> GetDatasetDimensions ();
std::vector<hsize_t> GetChunkDimensions ();
hsize_t GetRank ();
void SetImageDataPath (std::string const& name);
void SetFrameIndexPath (std::string const& name);
/**
* Open HDF5 file and dataset,
* reads frame index dataset to array
* @param fname file name
* @param validate true if one must validate if file is
* chunked with dims [? x 128 x 512] and chunk dims [1 x 128 x 512]
* @returns 1 if successful, else 0 if fail
*/
int OpenResources (const char* const fname, bool validate);
/**
* Close Open resources
*/
void CloseResources ();
/**
* Read an image into current_image,
* increment Z-offset (frame) and (if rank==4) storage cell
* @returns frame number read from file,
*/
int ReadImage (uint16_t* image, std::vector<hsize_t>& offset);
/**
* Print current image in memory
*/
void PrintCurrentImage (uint16_t* image);
private:
/**
* Initialize dimensions of image dataset for each new file
*/
void InitializeDimensions ();
bool ReadChunkDimensions ();
bool ValidateDimensions ();
bool ValidateChunkDimensions ();
/**
* Open dataset containing the frame numbers
*/
bool OpenFrameIndexDataset ();
/** file name */
std::string file_name{};
/** dataset name for image data */
std::string data_datasetname = DATA_DATASETNAME;
/** dataset name for frame index data */
std::string index_datasetname = INDEX_DATASETNAME;
/** file handle */
hid_t file{};
/** dataspace handle */
hid_t dataspace{};
/** memory space handle */
//hid_t memspace; //old
/** dataset handle */
hid_t dataset{};
/** file dimensions */
std::vector<hsize_t> file_dims{};
//hsize_t file_dims[RANK]{}; //static array (dimensions are known)
/** chunk dimensions
** not necessarily required
** useful for optimization or validation */
std::vector<hsize_t> chunk_dims{};
//hsize_t chunk_dims[RANK]{};
/** Rank of the image dataset */
hsize_t rank{};
/** number of frames */
unsigned int number_of_frames{};
/** frame index list */
std::vector<hsize_t> frame_index_list{};
/** Current image
** dynamic array
** uint16_t pointer format is chosen to support use with slsDetectorCalibration cluster finder */
//uint16_t* current_image{nullptr};
//uint16_t current_chunk[DEFAULT_CHUNK_Z_DIMS][DEFAULT_CHUNK_Y_DIMS][DEFAULT_CHUNK_X_DIMS];
/** Current frame offset
** (Z-offset, S-offset, 0, 0) or (Z-offset, 0, 0), increments automatically with ReadImage */
std::vector<hsize_t> frame_offset{};
//hsize_t frame_offset[RANK]{};
};

View File

@ -1,155 +0,0 @@
// SPDX-License-Identifier: LGPL-3.0-or-other
// Copyright (C) 2021 Contributors to the SLS Detector Package
#ifndef GOTTHARD2MODULEDATANEW_H
#define GOTTHARD2MODULEDATANEW_H
#include "gotthardModuleDataNew.h"
class gotthardDoubleModuleDataNew : public slsDetectorData<uint16_t> {
private:
const int nModules;
const int offset;
int iframe;
public:
/**
Implements the slsReceiverData structure for the gotthard read out by a module
i.e. using the slsReceiver (1x1280 pixels, 2 packets 1286 large etc.) \param c
crosstalk parameter for the output buffer
*/
gotthardDoubleModuleDataNew(int off = 24 * 2, int nmod = 2)
: slsDetectorData<uint16_t>(1280 * nmod, 1, nmod * (1280 * 2 + off)),
nModules(nmod), offset(off), iframe(0) {
#ifdef BCHIP074_BCHIP075
cout << "This is a bchip074-bchip075 system " << endl;
#endif
uint16_t **dMask;
int **dMap;
int ix, iy;
int ypixels = 1;
int xpixels = 1280 * nmod;
int imod, ipix;
dMask = new uint16_t *[1];
dMap = new int *[1];
dMap[0] = new int[1280 * nmod];
dMask[0] = new uint16_t[1280 * nmod];
for (int ix = 0; ix < xpixels; ix++) {
imod = ix % 2;
if (imod == 0)
ipix = ix / 2;
else
ipix = 1280 - 1 - ix / 2;
if (imod == 0)
dMap[0][ix] = ipix * 2 + offset;
else
dMap[0][ix] = 1280 * 2 + 2 * offset +
ipix * 2; // dataSize-2-ix;//+2*offset;
// dMap[0][ix] = 2*ipix+offset*(imod+1)+1280*2*imod;
dMask[0][ix] = 0x0;
#ifdef BCHIP074_BCHIP075
int ibad = ix / 2 + 1280 * imod;
if ((ibad >= 128 * 4 && ibad < 128 * 5) ||
(ibad >= 9 * 128 && ibad < 10 * 128) ||
(ibad >= (1280 + 128 * 4) && ibad < ibad >= (1280 + 128 * 6)))
dataROIMask[0][ix] = 0;
#endif
}
setDataMap(dMap);
setDataMask(dMask);
};
/**
Returns the frame number for the given dataset.
\param buff pointer to the dataset
\returns frame number
*/
int getFrameNumber(char *buff) {
if (offset >= sizeof(sls_detector_header))
return ((sls_detector_header *)buff)->frameNumber;
return iframe;
}; //*((int*)(buff+5))&0xffffff;};
/**
gets the packets number (last packet is labelled with 0 and is replaced with
40) \param buff pointer to the memory \returns packet number
*/
int getPacketNumber(char *buff) {
if (offset >= sizeof(sls_detector_header))
return ((sls_detector_header *)buff)->packetNumber;
};
/**
Loops over a memory slot until a complete frame is found (i.e. all
packets 0 to nPackets, same frame number). purely virtual func \param
data pointer to the memory to be analyzed \param ndata reference to the
amount of data found for the frame, in case the frame is incomplete at
the end of the memory slot \param dsize size of the memory slot to be
analyzed \returns pointer to the beginning of the last good frame (might
be incomplete if ndata smaller than dataSize), or NULL if no frame is
found
*/
virtual char *findNextFrame(char *data, int &ndata, int dsize) {
if (dsize < dataSize)
ndata = dsize;
else
ndata = dataSize;
return data;
}
virtual char *readNextFrame(ifstream &filebin) {
int ff = -1, np = -1;
return readNextFrame(filebin, ff, np);
};
virtual char *readNextFrame(ifstream &filebin, int &ff) {
int np = -1;
return readNextFrame(filebin, ff, np);
};
virtual char *readNextFrame(ifstream &filebin, int &ff, int &np) {
char *data = new char[dataSize];
char *d = readNextFrame(filebin, ff, np, data);
if (d == NULL) {
delete[] data;
data = NULL;
}
return data;
}
virtual char *readNextFrame(ifstream &filebin, int &ff, int &np,
char *data) {
char *retval = 0;
int nd;
int fnum = -1;
np = 0;
int pn;
// cout << dataSize << endl;
if (ff >= 0)
fnum = ff;
if (filebin.is_open()) {
if (filebin.read(data, dataSize)) {
ff = getFrameNumber(data);
np = getPacketNumber(data);
return data;
}
}
return NULL;
};
};
#endif

View File

@ -1,105 +0,0 @@
// SPDX-License-Identifier: LGPL-3.0-or-other
// Copyright (C) 2021 Contributors to the SLS Detector Package
#ifndef GOTTHARDSHORTMODULEDATA_H
#define GOTTHARDSHORTMODULEDATA_H
#include "slsReceiverData.h"
class gotthardShortModuleData : public slsReceiverData<uint16_t> {
public:
/**
Implements the slsReceiverData structure for the gotthard short read out by a
module i.e. using the slsReceiver (1x256 pixels, 1 packet 256 large etc.)
\param c crosstalk parameter for the output buffer
*/
gotthardShortModuleData(double c = 0)
: slsReceiverData<uint16_t>(xpixels, ypixels, npackets, buffersize),
xtalk(c) {
uint16_t **dMask;
int **dMap;
int ix, iy;
int offset = 2;
dMask = new uint16_t *[ypixels];
dMap = new int *[ypixels];
for (int i = 0; i < ypixels; i++) {
dMap[i] = new int[xpixels];
dMask[i] = new uint16_t[xpixels];
}
for (ix = 0; ix < ypixels; ++ix)
for (iy = 0; iy < xpixels; ++iy)
dMask[ix][iy] = 0x0;
for (ix = 0; ix < ypixels; ++ix)
for (iy = 0; iy < xpixels; ++iy) {
dMap[ix][iy] = offset;
offset++;
}
setDataMap(dMap);
setDataMask(dMask);
};
/**
Returns the frame number for the given dataset.
\param buff pointer to the dataset
\returns frame number
*/
int getFrameNumber(char *buff) { return (*(int *)buff); };
/**
gets the packets number (last packet is labelled with 0 and is replaced with
40) \param buff pointer to the memory \returns packet number
*/
int getPacketNumber(char *buff) { return 1; };
/**
returns the pixel value as double correcting for the output buffer crosstalk
\param data pointer to the memory
\param ix coordinate in the x direction
\param iy coordinate in the y direction
\returns channel value as double
*/
double getValue(char *data, int ix, int iy = 0) {
// check how it is for gotthard
if (xtalk == 0)
return slsDetectorData<uint16_t>::getValue(data, ix, iy);
else
return slsDetectorData<uint16_t>::getValue(data, ix, iy) -
xtalk *
slsDetectorData<uint16_t>::getValue(data, ix - 1, iy);
};
/** sets the output buffer crosstalk correction parameter
\param c output buffer crosstalk correction parameter to be set
\returns current value for the output buffer crosstalk correction parameter
*/
double setXTalk(double c) {
xtalk = c;
return xtalk;
}
/** gets the output buffer crosstalk parameter
\returns current value for the output buffer crosstalk correction parameter
*/
double getXTalk() { return xtalk; }
private:
double xtalk; /**<output buffer crosstalk correction parameter */
const static int xpixels = 256;
const static int ypixels = 1;
const static int npackets = 1;
const static int buffersize = 518;
};
#endif

View File

@ -0,0 +1,422 @@
// SPDX-License-Identifier: LGPL-3.0-or-other
// Copyright (C) 2021 Contributors to the SLS Detector Package
#ifndef JUNGFRAULGADSTRIXELSDATAQUAD_H
#define JUNGFRAULGADSTRIXELSDATAQUAD_H
#ifdef CINT
#include "sls/sls_detector_defs_CINT.h"
#else
#include "sls/sls_detector_defs.h"
#endif
#include "slsDetectorData.h"
// #define VERSION_V2
/**
@short structure for a Detector Packet or Image Header
@li frameNumber is the frame number
@li expLength is the subframe number (32 bit eiger) or real time exposure
time in 100ns (others)
@li packetNumber is the packet number
@li bunchId is the bunch id from beamline
@li timestamp is the time stamp with 10 MHz clock
@li modId is the unique module id (unique even for left, right, top, bottom)
@li xCoord is the x coordinate in the complete detector system
@li yCoord is the y coordinate in the complete detector system
@li zCoord is the z coordinate in the complete detector system
@li debug is for debugging purposes
@li roundRNumber is the round robin set number
@li detType is the detector type see :: detectorType
@li version is the version number of this structure format
*/
#include <algorithm>
#include <numeric>
#include <tuple>
namespace strixelQuad {
constexpr int nc_rawimg = 1024; // for full images //256;
constexpr int nc_quad = 512;
constexpr int nr_rawimg = 512;
constexpr int nr_chip = 256;
constexpr int gr = 9;
//shift due to extra pixels
constexpr int shift_x = 2; //left
constexpr int nc_strixel = ( nc_quad - shift_x - 2*gr ) / 3; //164
constexpr int nr_strixel = ( nr_chip - 1 - gr ) * 3; //one half (-1 because double sided pixel) //738
constexpr int nr_center = 12; //double sided pixels to be skipped
// boundaries in ASIC coordinates (pixels at both bounds are included)
constexpr int xstart = 256 + gr; // 265
constexpr int xend = 255 + nc_quad - gr; // 758
constexpr int bottom_ystart = gr; // 9
constexpr int bottom_yend = nr_chip - 2; // 254
constexpr int top_ystart = nr_chip + 1; // 257
constexpr int top_yend = nr_chip*2 - gr - 1; // 502
// x shift because of 2-pixel strixels on one side
constexpr int shift = 2;
} // namespace strixelQuad
//to account for module rotation
enum rotation {
NORMAL = 0,
INVERSE = 1
};
const int rota = NORMAL;
typedef struct {
uint64_t bunchNumber; /**< is the frame number */
uint64_t pre; /**< something */
} jf_header; // Aldo's header
using namespace strixelQuad;
class jungfrauLGADStrixelsDataQuad : public slsDetectorData<uint16_t> {
private:
int iframe;
int x0, y0, x1, y1, shifty;
struct {
uint16_t xmin;
uint16_t xmax;
uint16_t ymin;
uint16_t ymax;
int nc;
} globalROI;
//to account for the inverted routing of the two different quad halfs
enum location {
BOTTOM = 0,
TOP = 1
};
int multiplicator = 3;
std::vector<int> mods{ 0, 1, 2 };
void reverseVector( std::vector<int>& v ) {
std::reverse( v.begin(), v.end() );
std::cout << "mods reversed ";
for ( auto i : v )
std::cout << i << " ";
std::cout << '\n';
}
void setMappingShifts( const int rot, const int half ) {
x0 = xstart;
x1 = xend;
if (rot==NORMAL) {
x0 += shift;
} else {
x1-=shift;
reverseVector(mods);
}
if (half==BOTTOM) {
y0 = bottom_ystart;
y1 = bottom_yend;
shifty = 0;
} else {
y0 = top_ystart;
y1 = top_yend;
reverseVector(mods);
shifty = nr_strixel + nr_center; //double-sided pixels in the center have to be jumped
}
}
void remap( int xmin=0, int xmax=0, int ymin=0, int ymax=0 ) {
int ix, iy = 0;
// remapping loop
for (int ipy = y0; ipy <= y1; ipy++) {
for (int ipx = x0; ipx <= x1; ipx++) {
ix = int((ipx - x0) / multiplicator);
for (int m = 0; m < multiplicator; ++m) {
if ((ipx - x0) % multiplicator == m)
iy = (ipy - y0) * multiplicator + mods[m] + shifty;
}
// if (iy< 40) cout << iy << " " << ix <<endl;
if (xmin < xmax && ymin < ymax) {
if ( ipx>=xmin && ipx<=xmax && ipy>=ymin && ipy <=ymax )
dataMap[iy][ix] =
sizeof(header) + (globalROI.nc * (ipy - globalROI.ymin) + (ipx - globalROI.xmin)) * 2;
} else {
dataMap[iy][ix] = sizeof(header) + (nc_rawimg * ipy + ipx) * 2;
}
}
}
}
void remapQuad(const int rot) {
setMappingShifts( rot, BOTTOM );
remap();
setMappingShifts( rot, TOP );
remap();
}
std::tuple< uint16_t, uint16_t, uint16_t, uint16_t > adjustROItoLimits(uint16_t xmin,
uint16_t xmax,
uint16_t ymin,
uint16_t ymax,
uint16_t lim_roi_xmin,
uint16_t lim_roi_xmax,
uint16_t lim_roi_ymin,
uint16_t lim_roi_ymax) {
uint16_t xmin_roi, xmax_roi, ymin_roi, ymax_roi;
if ( xmin < lim_roi_xmin)
xmin_roi = lim_roi_xmin;
else
xmin_roi = xmin;
if ( xmax > lim_roi_xmax )
xmax_roi = lim_roi_xmax;
else
xmax_roi = xmax;
if ( ymin < lim_roi_ymin )
ymin_roi = lim_roi_ymin;
else
ymin_roi = ymin;
if ( ymax > lim_roi_ymax )
ymax_roi = lim_roi_ymax;
else
ymax_roi = ymax;
return std::make_tuple(xmin_roi, xmax_roi, ymin_roi, ymax_roi);
}
std::vector < std::tuple< int, uint16_t, uint16_t, uint16_t, uint16_t > > mapSubROIs(uint16_t xmin,
uint16_t xmax,
uint16_t ymin,
uint16_t ymax) {
bool bottom = false;
bool top = false;
for ( int x=xmin; x!=xmax+1; ++x ) {
for ( int y=ymin; y!=ymax; ++y ) {
if ( xstart<=x && x<=xend && bottom_ystart<=y && y<=bottom_yend )
bottom = true;
if ( xstart<=x && x<=xend && top_ystart<=y && y<=top_yend )
top = true;
}
}
uint16_t xmin_roi{}, xmax_roi{}, ymin_roi{}, ymax_roi{};
std::vector < std::tuple< int, uint16_t, uint16_t, uint16_t, uint16_t > > rois{};
if (bottom) {
std::tie( xmin_roi, xmax_roi, ymin_roi, ymax_roi ) =
adjustROItoLimits( xmin, xmax, ymin, ymax,
xstart, xend, bottom_ystart, bottom_yend );
rois.push_back( std::make_tuple( BOTTOM, xmin_roi, xmax_roi, ymin_roi, ymax_roi ) );
}
if (top) {
std::tie( xmin_roi, xmax_roi, ymin_roi, ymax_roi ) =
adjustROItoLimits( xmin, xmax, ymin, ymax,
xstart, xend, top_ystart, top_yend );
rois.push_back( std::make_tuple( TOP, xmin_roi, xmax_roi, ymin_roi, ymax_roi ) );
}
return rois;
}
void remapROI(std::tuple< int, uint16_t, uint16_t, uint16_t, uint16_t > roi, const int rot ) {
int half, xmin, xmax, ymin, ymax;
std::tie( half, xmin, xmax, ymin, ymax ) = roi;
setMappingShifts(rot, half);
std::cout << "remapping roi: "
<< ", x0: " << x0 << ", x1: " << x1 << ", y0: " << y0
<< ", y1: " << y1 << std::endl;
std::cout << "Adjusted roi: [" << xmin << ", " << xmax << ", " << ymin << ", " << ymax << "]" << std::endl;
remap( xmin, xmax, ymin, ymax );
}
public:
using header = sls::defs::sls_receiver_header;
jungfrauLGADStrixelsDataQuad(uint16_t xmin = 0, uint16_t xmax = 0,
uint16_t ymin = 0, uint16_t ymax = 0)
: slsDetectorData<uint16_t>(
nc_strixel,
nr_strixel * 2 + nr_center,
nc_strixel * ( nr_strixel * 2 + nr_center ) * 2 + sizeof(header)) {
std::cout << "Jungfrau strixels quad with full module data "
<< std::endl;
// Fill all strixels with dummy values
for (int ix = 0; ix != nc_strixel; ++ix) {
for (int iy = 0; iy != nr_strixel * 2 + nr_center; ++iy) {
dataMap[iy][ix] = sizeof(header);
}
}
globalROI.xmin = xmin;
globalROI.xmax = xmax;
globalROI.ymin = ymin;
globalROI.ymax = ymax;
std::cout << "sizeofheader = " << sizeof(header) << std::endl;
std::cout << "Jungfrau strixels quad with full module data "
<< std::endl;
if (xmin < xmax && ymin < ymax) {
// get ROI raw image number of columns
globalROI.nc = xmax - xmin + 1;
std::cout << "nc_roi = " << globalROI.nc << std::endl;
dataSize =
(xmax - xmin + 1) * (ymax - ymin + 1) * 2 + sizeof(header);
std::cout << "datasize " << dataSize << std::endl;
auto rois = mapSubROIs(xmin, xmax, ymin, ymax);
//function to fill vector of rois from globalROI
for ( auto roi : rois )
remapROI(roi, rota);
} else {
remapQuad( rota );
}
iframe = 0;
std::cout << "data struct created" << std::endl;
};
/**
Returns the value of the selected channel for the given dataset as
double. \param data pointer to the dataset (including headers etc) \param
ix pixel number in the x direction \param iy pixel number in the y
direction \returns data for the selected channel, with inversion if
required as double
*/
virtual double getValue(char *data, int ix, int iy = 0) {
uint16_t val = getChannel(data, ix, iy) & 0x3fff;
return val;
};
/**
Returns the frame number for the given dataset. Purely virtual func.
\param buff pointer to the dataset
\returns frame number
*/
int getFrameNumber(char *buff) {
#ifdef ALDO // VH
return ((jf_header *)buff)->bunchNumber; // VH
#endif // VH
return ((header *)buff)->detHeader.frameNumber;
};
/**
Returns the packet number for the given dataset. purely virtual func
\param buff pointer to the dataset
\returns packet number number
*/
int getPacketNumber(char *buff) {
#ifdef ALDO // VH
// uint32_t fakePacketNumber = 1000;
// return fakePacketNumber; //VH //TODO: Keep in mind in case of bugs!
// //This is definitely bad!
return 1000;
#endif // VH
return ((header *)buff)->detHeader.packetNumber;
};
char *readNextFrame(std::ifstream &filebin) {
int ff = -1, np = -1;
return readNextFrame(filebin, ff, np);
};
char *readNextFrame(std::ifstream &filebin, int &ff) {
int np = -1;
return readNextFrame(filebin, ff, np);
};
char *readNextFrame(std::ifstream &filebin, int &ff, int &np) {
char *data = new char[dataSize];
char *d = readNextFrame(filebin, ff, np, data);
if (d == NULL) {
delete[] data;
data = NULL;
}
return data;
};
char *readNextFrame(std::ifstream &filebin, int &ff, int &np, char *data) {
//char *retval = 0;
//int nd;
//int fnum = -1;
np = 0;
//int pn;
//std::cout << dataSize << std::endl;
//if (ff >= 0) {
// fnum = ff; }
if (filebin.is_open()) {
if (filebin.read(data, dataSize)) {
std::cout << "*";
ff = getFrameNumber(data);
np = getPacketNumber(data);
return data;
}
std::cout << "#";
} else {
std::cout << "File not open" << std::endl;
}
return NULL;
};
/* Loops over a memory slot until a complete frame is found (i.e. all */
/* packets 0 to nPackets, same frame number). purely virtual func \param
*/
/* data pointer to the memory to be analyzed \param ndata reference to
* the */
/* amount of data found for the frame, in case the frame is incomplete at
*/
/* the end of the memory slot \param dsize size of the memory slot to be
*/
/* analyzed \returns pointer to the beginning of the last good frame
* (might */
/* be incomplete if ndata smaller than dataSize), or NULL if no frame is
*/
/* found */
/* *\/ */
virtual char *findNextFrame(char *data, int &ndata, int dsize) {
if (dsize < dataSize)
ndata = dsize;
else
ndata = dataSize;
return data;
};
// int getPacketNumber(int x, int y) {return dataMap[y][x]/packetSize;};
};
#endif

View File

@ -0,0 +1,432 @@
// SPDX-License-Identifier: LGPL-3.0-or-other
// Copyright (C) 2021 Contributors to the SLS Detector Package
#ifndef JUNGFRAULGADSTRIXELSDATAQUADH5_H
#define JUNGFRAULGADSTRIXELSDATAQUADH5_H
#ifdef CINT
#include "sls/sls_detector_defs_CINT.h"
#else
#include "sls/sls_detector_defs.h"
#endif
#include "slsDetectorData.h"
// This needs to be linked correctly
#include "HDF5File.cpp"
#include "HDF5File.h" //this includes hdf5.h and hdf5_hl.h
// #define VERSION_V2
/**
@short structure for a Detector Packet or Image Header
@li frameNumber is the frame number
@li expLength is the subframe number (32 bit eiger) or real time exposure
time in 100ns (others)
@li packetNumber is the packet number
@li bunchId is the bunch id from beamline
@li timestamp is the time stamp with 10 MHz clock
@li modId is the unique module id (unique even for left, right, top, bottom)
@li xCoord is the x coordinate in the complete detector system
@li yCoord is the y coordinate in the complete detector system
@li zCoord is the z coordinate in the complete detector system
@li debug is for debugging purposes
@li roundRNumber is the round robin set number
@li detType is the detector type see :: detectorType
@li version is the version number of this structure format
*/
// #include <algorithm>
#include <numeric>
#include <tuple>
namespace strixelQuad {
constexpr int nc_rawimg = 1024; // for full images //256;
constexpr int nc_quad = 512;
constexpr int nr_rawimg = 512;
constexpr int nr_chip = 256;
constexpr int gr = 9;
// shift due to extra pixels
constexpr int shift_x = 2; // left
constexpr int nc_strixel = (nc_quad - shift_x - 2 * gr) / 3; // 164
constexpr int nr_strixel =
(nr_chip - 1 - gr) * 3; // one half (-1 because double sided pixel) //738
constexpr int nr_center = 12; // double sided pixels to be skipped
// boundaries in ASIC coordinates (pixels at both bounds are included)
constexpr int xstart = 256 + gr; // 265
constexpr int xend = 255 + nc_quad - gr; // 758
constexpr int bottom_ystart = gr; // 9
constexpr int bottom_yend = nr_chip - 2; // 254
constexpr int top_ystart = nr_chip + 1; // 257
constexpr int top_yend = nr_chip * 2 - gr - 1; // 502
// x shift because of 2-pixel strixels on one side
constexpr int shift = 2;
} // namespace strixelQuad
// to account for module rotation
enum rotation { NORMAL = 0, INVERSE = 1 };
const int rota = NORMAL;
typedef struct {
uint64_t bunchNumber; /**< is the frame number */
uint64_t pre; /**< something */
} jf_header; // Aldo's header
using namespace strixelQuad;
class jungfrauLGADStrixelsDataQuadH5 : public slsDetectorData<uint16_t> {
private:
int iframe;
int x0, y0, x1, y1, shifty;
struct {
uint16_t xmin;
uint16_t xmax;
uint16_t ymin;
uint16_t ymax;
int nc;
} globalROI;
// to account for the inverted routing of the two different quad halfs
enum location { BOTTOM = 0, TOP = 1 };
int multiplicator = 3;
std::vector<int> mods{0, 1, 2};
void reverseVector(std::vector<int> &v) {
std::reverse(v.begin(), v.end());
std::cout << "mods reversed ";
for (auto i : v)
std::cout << i << " ";
std::cout << '\n';
}
void setMappingShifts(const int rot, const int half) {
x0 = xstart;
x1 = xend;
if (rot == NORMAL) {
x0 += shift;
} else {
x1 -= shift;
reverseVector(mods);
}
if (half == BOTTOM) {
y0 = bottom_ystart;
y1 = bottom_yend;
shifty = 0;
} else {
y0 = top_ystart;
y1 = top_yend;
reverseVector(mods);
shifty = nr_strixel + nr_center; // double-sided pixels in the
// center have to be jumped
}
}
void remap(int xmin = 0, int xmax = 0, int ymin = 0, int ymax = 0) {
int ix, iy = 0;
// remapping loop
for (int ipy = y0; ipy <= y1; ++ipy) {
for (int ipx = x0; ipx <= x1; ++ipx) {
ix = int((ipx - x0) / multiplicator);
for (int m = 0; m < multiplicator; ++m) {
if ((ipx - x0) % multiplicator == m)
iy = (ipy - y0) * multiplicator + mods[m] + shifty;
}
// if (iy< 40) cout << iy << " " << ix <<endl;
if (xmin < xmax && ymin < ymax) { // if ROI
if (ipx >= xmin && ipx <= xmax && ipy >= ymin &&
ipy <= ymax)
dataMap[iy][ix] =
(globalROI.nc * (ipy - globalROI.ymin) +
(ipx - globalROI.xmin)) *
2;
} else { // if full Quad
dataMap[iy][ix] = (nc_rawimg * ipy + ipx) * 2;
}
}
}
}
void remapQuad(const int rot) {
setMappingShifts(rot, BOTTOM);
remap();
setMappingShifts(rot, TOP);
remap();
}
std::tuple<uint16_t, uint16_t, uint16_t, uint16_t>
adjustROItoLimits(uint16_t xmin, uint16_t xmax, uint16_t ymin,
uint16_t ymax, uint16_t lim_roi_xmin,
uint16_t lim_roi_xmax, uint16_t lim_roi_ymin,
uint16_t lim_roi_ymax) {
uint16_t xmin_roi, xmax_roi, ymin_roi, ymax_roi;
if (xmin < lim_roi_xmin)
xmin_roi = lim_roi_xmin;
else
xmin_roi = xmin;
if (xmax > lim_roi_xmax)
xmax_roi = lim_roi_xmax;
else
xmax_roi = xmax;
if (ymin < lim_roi_ymin)
ymin_roi = lim_roi_ymin;
else
ymin_roi = ymin;
if (ymax > lim_roi_ymax)
ymax_roi = lim_roi_ymax;
else
ymax_roi = ymax;
return std::make_tuple(xmin_roi, xmax_roi, ymin_roi, ymax_roi);
}
// The strixel Quad has a mirrored symmetry from the center axis
// So we need to distinguish between bottom and top half for remapping
std::vector<std::tuple<int, uint16_t, uint16_t, uint16_t, uint16_t>>
mapSubROIs(uint16_t xmin, uint16_t xmax, uint16_t ymin, uint16_t ymax) {
bool bottom = false;
bool top = false;
for (int x = xmin; x != xmax + 1; ++x) {
for (int y = ymin; y != ymax; ++y) {
if (xstart <= x && x <= xend && bottom_ystart <= y &&
y <= bottom_yend)
bottom = true;
if (xstart <= x && x <= xend && top_ystart <= y &&
y <= top_yend)
top = true;
}
}
uint16_t xmin_roi{}, xmax_roi{}, ymin_roi{}, ymax_roi{};
std::vector<std::tuple<int, uint16_t, uint16_t, uint16_t, uint16_t>>
rois{};
if (bottom) {
std::tie(xmin_roi, xmax_roi, ymin_roi, ymax_roi) =
adjustROItoLimits(xmin, xmax, ymin, ymax, xstart, xend,
bottom_ystart, bottom_yend);
rois.push_back(std::make_tuple(BOTTOM, xmin_roi, xmax_roi, ymin_roi,
ymax_roi));
}
if (top) {
std::tie(xmin_roi, xmax_roi, ymin_roi, ymax_roi) =
adjustROItoLimits(xmin, xmax, ymin, ymax, xstart, xend,
top_ystart, top_yend);
rois.push_back(
std::make_tuple(TOP, xmin_roi, xmax_roi, ymin_roi, ymax_roi));
}
return rois;
}
void remapROI(std::tuple<int, uint16_t, uint16_t, uint16_t, uint16_t> roi,
const int rot) {
int half, xmin, xmax, ymin, ymax;
std::tie(half, xmin, xmax, ymin, ymax) = roi;
setMappingShifts(rot, half);
std::cout << "remapping roi: "
<< ", x0: " << x0 << ", x1: " << x1 << ", y0: " << y0
<< ", y1: " << y1 << std::endl;
std::cout << "Adjusted roi: [" << xmin << ", " << xmax << ", " << ymin
<< ", " << ymax << "]" << std::endl;
remap(xmin, xmax, ymin, ymax);
}
// The following functions are pure virtual in the base class. But I don't
// want them to be accessible here! Implement the functions as private (to
// satisfy the linker) int getFrameNumber(char* buff){return 0;} //This is
// actually needed because the cluster finder writes the framenumber
int getPacketNumber(char *buff) { return 0; } // Not provided
// Mark overwritten functions as override final
char *readNextFrame(std::ifstream &filebin) override final {
return nullptr;
}
public:
using header = sls::defs::sls_receiver_header;
jungfrauLGADStrixelsDataQuadH5(uint16_t xmin = 0, uint16_t xmax = 0,
uint16_t ymin = 0, uint16_t ymax = 0)
: slsDetectorData<uint16_t>(
// nc_strixel,
// nr_strixel * 2 + nr_center,
// nc_strixel * ( nr_strixel * 2 + nr_center ) * 2
512 / 2, 1024 * 2, 512 * 1024 * 2) {
std::cout << "Jungfrau strixels quad with full module data "
<< std::endl;
// Fill all strixels with dummy values
// for (int ix = 0; ix != nc_strixel; ++ix) {
// for (int iy = 0; iy != nr_strixel * 2 + nr_center; ++iy) {
for (int ix = 0; ix != 512 / 2; ++ix) {
for (int iy = 0; iy != 1024 * 2; ++iy) {
// Set everything to dummy value
dataMap[iy][ix] = sizeof(header);
}
}
globalROI.xmin = xmin;
globalROI.xmax = xmax;
globalROI.ymin = ymin;
globalROI.ymax = ymax;
// std::cout << "sizeofheader = " << sizeof(header) << std::endl;
std::cout << "Jungfrau strixels quad with full module data "
<< std::endl;
if (xmin < xmax && ymin < ymax) {
// get ROI raw image number of columns
globalROI.nc = xmax - xmin + 1;
std::cout << "nc_roi = " << globalROI.nc << std::endl;
dataSize = (xmax - xmin + 1) * (ymax - ymin + 1) * 2;
std::cout << "datasize " << dataSize << std::endl;
auto rois = mapSubROIs(xmin, xmax, ymin, ymax);
// function to fill vector of rois from globalROI
for (auto roi : rois)
remapROI(roi, rota);
} else {
remapQuad(rota);
}
iframe = 0;
std::cout << "data struct created" << std::endl;
};
/**
Returns the value of the selected channel for the given dataset as
double. \param data pointer to the dataset (including headers etc) \param
ix pixel number in the x direction \param iy pixel number in the y
direction \returns data for the selected channel, with inversion if
required as double
*/
virtual double getValue(char *data, int ix, int iy = 0) {
uint16_t val = getChannel(data, ix, iy) & 0x3fff;
return val;
};
char *readNextFrame(HDF5File &hfile) {
int fn = 0;
std::vector<hsize_t> h5offset(1);
return readNextFrame(hfile, fn, h5offset);
};
char *readNextFrame(HDF5File &hfile, int &fn) {
std::vector<hsize_t> h5offset(1);
return readNextFrame(hfile, fn, h5offset);
};
char *readNextFrame(HDF5File &hfile, int &fn,
std::vector<hsize_t> &h5offset) {
// Ensure dataSize is a valid size for allocation
if (dataSize <= 0) {
// Handle error case appropriately, e.g., log an error message
return nullptr;
}
char *data = new char[dataSize];
char *readResult = readNextFrame(hfile, fn, h5offset, data);
// Check if reading failed
if (readResult == nullptr) {
delete[] data; // Free allocated memory
data = nullptr; // Set to nullptr to avoid dangling pointer
}
return data; // returning data is equivalent to returning
// reinterpret_cast<char*>(data_ptr) as they both point to
// the same memory
};
/*
* This is the most recent function. This is used in the cluster finder!
* The overloads are legacy!
* Note that caller has to allocate and deallocate memory for data!
* \param hfile object of type HDF5File (reader class)
* \param framenumber frame number as read from the HDF5 file
* \param h5offset vector defining offset parameters for HDF5 hyperslab
* selection (dimensions Z and S), incremented automatially
* \param data pointer to image buffer (converted to hold uint16_t by
* definition of HDF5File)
*/
char *readNextFrame(HDF5File &hfile, int &framenumber,
std::vector<hsize_t> &h5offset, char *data) {
if (framenumber >= 0) {
if (h5offset[0] % 10 == 0)
std::cout << "*";
// Storing the reinterpret_cast in the variable data_ptr ensures
// that I can pass it to a function that expects at uint16_t*
uint16_t *data_ptr = reinterpret_cast<uint16_t *>(
data); // now data_ptr points where data points (thus modifies
// the same memory)
framenumber = hfile.ReadImage(data_ptr, h5offset);
iframe = h5offset[0]; // iframe is a class member!
return data; // return reinterpret_cast<char*>(data_ptr); //
// Equivalent
}
std::cout << "#";
return nullptr;
};
int getFrameNumber(char *buff) {
return iframe;
} // Provided via public method readNextFrame
// It is debatable if one might not instead want to provide the "real" frame
// number as read from the file here For now, this is the frame offset
// counter (that always has to start at 0 for each new file)
/* Loops over a memory slot until a complete frame is found (i.e. all */
/* packets 0 to nPackets, same frame number). purely virtual func \param
*/
/* data pointer to the memory to be analyzed \param ndata reference to
* the */
/* amount of data found for the frame, in case the frame is incomplete at
*/
/* the end of the memory slot \param dsize size of the memory slot to be
*/
/* analyzed \returns pointer to the beginning of the last good frame
* (might */
/* be incomplete if ndata smaller than dataSize), or NULL if no frame is
*/
/* found */
/* *\/ */
virtual char *findNextFrame(char *data, int &ndata, int dsize) {
if (dsize < dataSize)
ndata = dsize;
else
ndata = dataSize;
return data;
};
// int getPacketNumber(int x, int y) {return dataMap[y][x]/packetSize;};
};
#endif

View File

@ -28,6 +28,10 @@
@li version is the version number of this structure format
*/
#include <algorithm>
#include <numeric>
#include <tuple>
namespace strixelSingleChip {
constexpr int nc_rawimg = 1024; // for full images //256;
constexpr int nr_rawimg = 512;
@ -77,7 +81,7 @@ constexpr int c6g1_ystart = c6g2_yend + 1; // 448
constexpr int c6g1_yend = c6g2_yend + 64 - gr; // 502
// y shift due to faulty bonding (relevant for M408)
constexpr int bond_shift_y = 1; // CHANGE IF YOU CHANGE MODULE!
constexpr int bond_shift_y = 0; // CHANGE IF YOU CHANGE MODULE!
} // namespace strixelSingleChip
@ -97,6 +101,13 @@ class jungfrauLGADStrixelsData : public slsDetectorData<uint16_t> {
int chip_x0;
int chip_y0;
int x0, y0, x1, y1, shifty;
struct {
uint16_t xmin;
uint16_t xmax;
uint16_t ymin;
uint16_t ymax;
int nc;
} globalROI;
int getMultiplicator(const int group) {
int multiplicator;
@ -215,9 +226,113 @@ class jungfrauLGADStrixelsData : public slsDetectorData<uint16_t> {
}
}
void remapROI(uint16_t xmin, uint16_t xmax, uint16_t ymin, uint16_t ymax) {
std::tuple< uint16_t, uint16_t, uint16_t, uint16_t > adjustROItoLimits(uint16_t xmin,
uint16_t xmax,
uint16_t ymin,
uint16_t ymax,
uint16_t lim_roi_xmin,
uint16_t lim_roi_xmax,
uint16_t lim_roi_ymin,
uint16_t lim_roi_ymax) {
uint16_t xmin_roi, xmax_roi, ymin_roi, ymax_roi;
if ( xmin < lim_roi_xmin)
xmin_roi = lim_roi_xmin;
else
xmin_roi = xmin;
if ( xmax > lim_roi_xmax )
xmax_roi = lim_roi_xmax;
else
xmax_roi = xmax;
if ( ymin < lim_roi_ymin )
ymin_roi = lim_roi_ymin;
else
ymin_roi = ymin;
if ( ymax > lim_roi_ymax )
ymax_roi = lim_roi_ymax;
else
ymax_roi = ymax;
return std::make_tuple(xmin_roi, xmax_roi, ymin_roi, ymax_roi);
}
std::vector < std::tuple< int, int, uint16_t, uint16_t, uint16_t, uint16_t > > mapSubROIs(uint16_t xmin,
uint16_t xmax,
uint16_t ymin,
uint16_t ymax) {
bool chip_1_1 = false;
bool chip_1_2 = false;
bool chip_1_3 = false;
bool chip_6_1 = false;
bool chip_6_2 = false;
bool chip_6_3 = false;
for ( int x=xmin; x!=xmax+1; ++x ) {
for ( int y=ymin; y!=ymax; ++y ) {
if ( c1g1_xstart<=x && x<=c1_xend && (c1g1_ystart+bond_shift_y)<=y && y<=(c1g1_yend+bond_shift_y) )
chip_1_1 = true;
if ( c1g2_xstart<=x && x<=c1_xend && (c1g2_ystart+bond_shift_y)<=y && y<=(c1g2_yend+bond_shift_y) )
chip_1_2 = true;
if ( c1g3_xstart<=x && x<=c1_xend && (c1g3_ystart+bond_shift_y)<=y && y<=(c1g3_yend+bond_shift_y) )
chip_1_3 = true;
if ( c6_xstart<=x && x<=c6g1_xend && (c6g1_ystart-bond_shift_y)<=y && y<=(c6g1_yend-bond_shift_y) )
chip_6_1 = true;
if ( c6_xstart<=x && x<=c6g2_xend && (c6g2_ystart-bond_shift_y)<=y && y<=(c6g2_yend-bond_shift_y) )
chip_6_2 = true;
if ( c6_xstart<=x && x<=c6g3_xend && (c6g3_ystart-bond_shift_y)<=y && y<=(c6g3_yend-bond_shift_y) )
chip_6_3 = true;
}
}
uint16_t xmin_roi{}, xmax_roi{}, ymin_roi{}, ymax_roi{};
//[ chip, group, xmin, xmax, ymin, ymax ]
std::vector < std::tuple< int, int, uint16_t, uint16_t, uint16_t, uint16_t > > rois{};
if (chip_1_1) {
std::tie( xmin_roi, xmax_roi, ymin_roi, ymax_roi ) =
adjustROItoLimits( xmin, xmax, ymin, ymax,
c1g1_xstart, c1_xend, 0, c1g1_yend+bond_shift_y );
rois.push_back( std::make_tuple( 1, 1, xmin_roi, xmax_roi, ymin_roi, ymax_roi ) );
}
if (chip_1_2) {
std::tie( xmin_roi, xmax_roi, ymin_roi, ymax_roi ) =
adjustROItoLimits( xmin, xmax, ymin, ymax,
c1g2_xstart, c1_xend, c1g2_ystart+bond_shift_y, c1g2_yend+bond_shift_y );
rois.push_back( std::make_tuple( 1, 2, xmin_roi, xmax_roi, ymin_roi, ymax_roi ) );
}
if (chip_1_3) {
std::tie( xmin_roi, xmax_roi, ymin_roi, ymax_roi ) =
adjustROItoLimits( xmin, xmax, ymin, ymax,
c1g3_xstart, c1_xend, c1g3_ystart+bond_shift_y, c1g3_yend+bond_shift_y );
rois.push_back( std::make_tuple( 1, 3, xmin_roi, xmax_roi, ymin_roi, ymax_roi ) );
}
if (chip_6_3) {
std::tie( xmin_roi, xmax_roi, ymin_roi, ymax_roi ) =
adjustROItoLimits( xmin, xmax, ymin, ymax,
c6_xstart, c6g3_xend, c6g3_ystart-bond_shift_y, c6g3_yend-bond_shift_y );
rois.push_back( std::make_tuple( 6, 3, xmin_roi, xmax_roi, ymin_roi, ymax_roi ) );
}
if (chip_6_2) {
std::tie( xmin_roi, xmax_roi, ymin_roi, ymax_roi ) =
adjustROItoLimits( xmin, xmax, ymin, ymax,
c6_xstart, c6g2_xend, c6g2_ystart-bond_shift_y, c6g2_yend-bond_shift_y );
rois.push_back( std::make_tuple( 6, 2, xmin_roi, xmax_roi, ymin_roi, ymax_roi ) );
}
if (chip_6_1) {
std::tie( xmin_roi, xmax_roi, ymin_roi, ymax_roi ) =
adjustROItoLimits( xmin, xmax, ymin, ymax,
c6_xstart, c6g1_xend, c6g1_ystart-bond_shift_y, 511 );
rois.push_back( std::make_tuple( 6, 1, xmin_roi, xmax_roi, ymin_roi, ymax_roi ) );
}
return rois;
}
void remapROI(std::tuple< int, int, uint16_t, uint16_t, uint16_t, uint16_t > roi) {
// determine group and chip selected by ROI
int group;
int group, xmin, xmax, ymin, ymax;
std::tie( mchip, group, xmin, xmax, ymin, ymax ) = roi;
/*
if (ymax <= c1g1_yend + bond_shift_y) {
group = 1;
mchip = 1;
@ -243,18 +358,17 @@ class jungfrauLGADStrixelsData : public slsDetectorData<uint16_t> {
group = -1;
mchip = -1;
}
int multiplicator = getMultiplicator(group);
setMappingShifts(group);
std::cout << "chip: " << mchip << ", group: " << group << ", m: " << multiplicator
<< ", x0: " << x0 << ", x1: " << x1 << ", y0: " << y0
<< ", y1: " << y1 << std::endl;
// get ROI raw image number of columns
int nc_roi = xmax - xmin + 1;
std::cout << "nc_roi = " << nc_roi << std::endl;
*/
int multiplicator = getMultiplicator(group);
setMappingShifts(group);
std::cout << "remapping chip: " << mchip << ", group: " << group << ", m: " << multiplicator
<< ", x0: " << x0 << ", x1: " << x1 << ", y0: " << y0
<< ", y1: " << y1 << std::endl;
std::cout << "Adjusted roi: [" << xmin << ", " << xmax << ", " << ymin << ", " << ymax << "]" << std::endl;
// make sure loop bounds are correct
/*
if (y0 < ymin)
std::cout << "Error ymin" << std::endl;
if (y1 > ymax)
@ -264,24 +378,27 @@ class jungfrauLGADStrixelsData : public slsDetectorData<uint16_t> {
std::cout << "Error xmin" << std::endl;
if (x1 > xmax)
std::cout << "Error xmax" << std::endl;
*/
// remapping loop
int ix, iy = 0;
for (int ipy = y0; ipy <= y1; ++ipy) {
for (int ipx = x0; ipx <= x1; ++ipx) {
int ix, iy = 0;
for (int ipy = y0; ipy <= y1; ++ipy) {
for (int ipx = x0; ipx <= x1; ++ipx) {
ix = int((ipx - x0 /*-xmin*/) / multiplicator);
for (int m = 0; m < multiplicator; m++) {
if ((ipx - x0 /*-xmin*/) % multiplicator == m)
iy = (ipy - y0 /*-ymin*/) * multiplicator + m + shifty;
}
// if (iy< 40) cout << iy << " " << ix <<endl;
dataMap[iy][ix] =
sizeof(header) + (nc_roi * (ipy - ymin) + (ipx - xmin)) * 2;
groupmap[iy][ix] = group - 1;
}
}
ix = int((ipx - x0 /*-xmin*/) / multiplicator);
for (int m = 0; m < multiplicator; m++) {
if ((ipx - x0 /*-xmin*/) % multiplicator == m)
iy = (ipy - y0 /*-ymin*/) * multiplicator + m + shifty;
}
// if (iy< 40) cout << iy << " " << ix <<endl;
if ( ipx>=xmin && ipx<=xmax && ipy>=ymin && ipy <=ymax )
dataMap[iy][ix] =
sizeof(header) + (globalROI.nc * (ipy - globalROI.ymin) + (ipx - globalROI.xmin)) * 2;
else dataMap[iy][ix] = sizeof(header);
groupmap[iy][ix] = group - 1;
}
}
}
public:
@ -307,16 +424,31 @@ class jungfrauLGADStrixelsData : public slsDetectorData<uint16_t> {
}
}
globalROI.xmin = xmin;
globalROI.xmax = xmax;
globalROI.ymin = ymin;
globalROI.ymax = ymax;
std::cout << "sizeofheader = " << sizeof(header) << std::endl;
std::cout << "Jungfrau strixels 2X single chip with full module data "
<< std::endl;
if (xmin < xmax && ymin < ymax) {
dataSize =
(xmax - xmin + 1) * (ymax - ymin + 1) * 2 + sizeof(header);
std::cout << "datasize " << dataSize << std::endl;
remapROI(xmin, xmax, ymin, ymax);
// get ROI raw image number of columns
globalROI.nc = xmax - xmin + 1;
std::cout << "nc_roi = " << globalROI.nc << std::endl;
dataSize =
(xmax - xmin + 1) * (ymax - ymin + 1) * 2 + sizeof(header);
std::cout << "datasize " << dataSize << std::endl;
//[ chip, group, xmin, xmax, ymin, ymax ]
auto rois = mapSubROIs(xmin, xmax, ymin, ymax);
//function to fill vector of rois from globalROI
for ( auto roi : rois )
remapROI(roi);
} else {

View File

@ -3,6 +3,7 @@
#ifndef JUNGFRAUMODULEDATA_H
#define JUNGFRAUMODULEDATA_H
#include <cstdint>
#include "sls/sls_detector_defs.h"
#include "slsDetectorData.h"
//#define VERSION_V2
@ -27,7 +28,7 @@ typedef struct {
uint64_t bunchNumber; /**< is the frame number */
uint64_t pre; /**< something */
} jf_header;
} jf_header; //Aldo's header!
using namespace std;
class jungfrauModuleData : public slsDetectorData<uint16_t> {
@ -42,20 +43,56 @@ class jungfrauModuleData : public slsDetectorData<uint16_t> {
1286 large etc.) \param c crosstalk parameter for the output buffer
*/
#ifdef ALDO
using header = jf_header;
#else
using header = sls::defs::sls_receiver_header;
#endif
#ifndef ZMQ
#define off sizeof(jf_header)
#define off sizeof(header)
#endif
#ifdef ZMQ
#define off 0
#endif
jungfrauModuleData()
jungfrauModuleData(uint16_t xmin=0, uint16_t xmax=0,
uint16_t ymin=0, uint16_t ymax=0)
: slsDetectorData<uint16_t>(1024, 512,
1024* 512 * 2 + off) {
for (int ix = 0; ix < 1024; ix++) {
for (int iy = 0; iy < 512; iy++) {
for (int ix = 0; ix != 1024; ++ix) {
for (int iy = 0; iy != 512; ++iy) {
dataMap[iy][ix] = off;
}
}
if (xmin < xmax && ymin < ymax) {
int nc_roi = xmax - xmin + 1;
int nr_roi = ymax - ymin + 1;
std::cout << "nc_roi = " << nc_roi << std::endl;
std::cout << "nr_roi = " << nr_roi << std::endl;
dataSize =
(xmax - xmin + 1) * (ymax - ymin + 1) * 2 + off;
std::cout << "datasize " << dataSize << std::endl;
for (int ix = xmin; ix < xmax+1; ++ix) {
for (int iy = ymin; iy < ymax+1; ++iy) {
dataMap[iy][ix] = off + (nc_roi * iy + ix) * 2;
#ifdef HIGHZ
dataMask[iy][ix] = 0x3fff;
#endif
}
}
} else {
for (int ix = 0; ix < 1024; ++ix) {
for (int iy = 0; iy < 512; ++iy) {
dataMap[iy][ix] = off + (1024 * iy + ix) * 2;
#ifdef HIGHZ
dataMask[iy][ix] = 0x3fff;
@ -63,7 +100,7 @@ class jungfrauModuleData : public slsDetectorData<uint16_t> {
}
}
}
iframe = 0;
@ -150,7 +187,7 @@ class jungfrauModuleData : public slsDetectorData<uint16_t> {
//int pn;
// cout << dataSize << endl;
if (ff >= 0)
//if (ff >= 0)
//fnum = ff;
if (filebin.is_open()) {

View File

@ -0,0 +1,226 @@
// SPDX-License-Identifier: LGPL-3.0-or-other
// Copyright (C) 2021 Contributors to the SLS Detector Package
#ifndef JUNGFRAUSINGLECHIPDATA_H
#define JUNGFRAUSINGLECHIPDATA_H
#include <cstdint>
#include "sls/sls_detector_defs.h"
#include "slsDetectorData.h"
//#define VERSION_V2
/**
@short structure for a Detector Packet or Image Header
@li frameNumber is the frame number
@li expLength is the subframe number (32 bit eiger) or real time exposure
time in 100ns (others)
@li packetNumber is the packet number
@li bunchId is the bunch id from beamline
@li timestamp is the time stamp with 10 MHz clock
@li modId is the unique module id (unique even for left, right, top, bottom)
@li xCoord is the x coordinate in the complete detector system
@li yCoord is the y coordinate in the complete detector system
@li zCoord is the z coordinate in the complete detector system
@li debug is for debugging purposes
@li roundRNumber is the round robin set number
@li detType is the detector type see :: detectorType
@li version is the version number of this structure format
*/
typedef struct {
uint64_t bunchNumber; /**< is the frame number */
uint64_t pre; /**< something */
} jf_header; //Aldo's header!
using namespace std;
class jungfrauSingleChipData : public slsDetectorData<uint16_t> {
private:
int iframe;
public:
/**
Implements the slsReceiverData structure for the moench02 prototype read
out by a module i.e. using the slsReceiver (160x160 pixels, 40 packets
1286 large etc.) \param c crosstalk parameter for the output buffer
*/
#ifdef ALDO
using header = jf_header;
#else
using header = sls::defs::sls_receiver_header;
#endif
#ifndef ZMQ
#define off sizeof(header)
#endif
#ifdef ZMQ
#define off 0
#endif
jungfrauSingleChipData(uint16_t xmin=0, uint16_t xmax=0,
uint16_t ymin=0, uint16_t ymax=0)
: slsDetectorData<uint16_t>(256, 256,
256* 256 * 2 + off) {
for (int ix = 0; ix != 256; ++ix) {
for (int iy = 0; iy != 256; ++iy) {
dataMap[iy][ix] = off;
}
}
if (xmin < xmax && ymin < ymax) {
int nc_roi = xmax - xmin + 1;
int nr_roi = ymax - ymin + 1;
std::cout << "nc_roi = " << nc_roi << std::endl;
std::cout << "nr_roi = " << nr_roi << std::endl;
dataSize =
(xmax - xmin + 1) * (ymax - ymin + 1) * 2 + off;
std::cout << "datasize " << dataSize << std::endl;
for (int ix = xmin; ix < xmax+1; ++ix) {
for (int iy = ymin; iy < ymax+1; ++iy) {
dataMap[iy][ix] = off + (nc_roi * iy + ix) * 2;
#ifdef HIGHZ
dataMask[iy][ix] = 0x3fff;
#endif
}
}
} else {
for (int ix = 0; ix < 256; ++ix) {
for (int iy = 0; iy < 256; ++iy) {
dataMap[iy][ix] = off + (256 * iy + ix) * 2;
#ifdef HIGHZ
dataMask[iy][ix] = 0x3fff;
#endif
}
}
}
iframe = 0;
// cout << "data struct created" << endl;
};
/**
Returns the value of the selected channel for the given dataset as
double. \param data pointer to the dataset (including headers etc) \param
ix pixel number in the x direction \param iy pixel number in the y
direction \returns data for the selected channel, with inversion if
required as double
*/
virtual double getValue(char *data, int ix, int iy = 0) {
uint16_t val = getChannel(data, ix, iy) & 0x3fff;
/* if (ix==0 && iy==0) */
/* cout << val << endl; */
return val;
};
/**
Returns the frame number for the given dataset. Purely virtual func.
\param buff pointer to the dataset
\returns frame number
*/
/* class jfrau_packet_header_t { */
/* public: */
/* unsigned char reserved[4]; */
/* unsigned char packetNumber[1]; */
/* unsigned char frameNumber[3]; */
/* unsigned char bunchid[8]; */
/* }; */
int getFrameNumber(char *buff) {
return ((jf_header *)buff)->bunchNumber;
};
/**
Returns the packet number for the given dataset. purely virtual func
\param buff pointer to the dataset
\returns packet number number
*/
int getPacketNumber(char *buff) {
return 0;
};
char *readNextFrame(ifstream &filebin) {
int ff = -1, np = -1;
return readNextFrame(filebin, ff, np);
};
char *readNextFrame(ifstream &filebin, int &ff) {
int np = -1;
return readNextFrame(filebin, ff, np);
};
char *readNextFrame(ifstream &filebin, int &ff, int &np) {
char *data = new char[dataSize];
char *d = readNextFrame(filebin, ff, np, data);
if (d == NULL) {
delete[] data;
data = NULL;
}
return data;
};
char *readNextFrame(ifstream &filebin, int &ff, int &np,char *data) {
//char *retval = 0;
//int nd;
//int fnum = -1;
np = 0;
//int pn;
// cout << dataSize << endl;
//if (ff >= 0)
//fnum = ff;
if (filebin.is_open()) {
if (filebin.read(data, dataSize)) {
ff = getFrameNumber(data);
np = getPacketNumber(data);
return data;
}
}
return NULL;
};
/**
Loops over a memory slot until a complete frame is found (i.e. all
packets 0 to nPackets, same frame number). purely virtual func \param
data pointer to the memory to be analyzed \param ndata reference to the
amount of data found for the frame, in case the frame is incomplete at
the end of the memory slot \param dsize size of the memory slot to be
analyzed \returns pointer to the beginning of the last good frame (might
be incomplete if ndata smaller than dataSize), or NULL if no frame is
found
*/
virtual char *findNextFrame(char *data, int &ndata, int dsize) {
if (dsize < dataSize)
ndata = dsize;
else
ndata = dataSize;
return data;
};
// int getPacketNumber(int x, int y) {return dataMap[y][x]/packetSize;};
};
#endif

View File

@ -54,6 +54,10 @@ class interpolatingDetector : public singlePhotonDetector {
pthread_mutex_init(fi, NULL);
};
/**
pointer-based copy constructor (cloner)
\param orig detector to be copied
*/
interpolatingDetector(interpolatingDetector *orig)
: singlePhotonDetector(orig) {
// if (orig->interp)
@ -66,10 +70,28 @@ class interpolatingDetector : public singlePhotonDetector {
fi = orig->fi;
}
/**
* copy constructor (deep copy)
* stricly, TODO: Implement Rule of Five!
* (copy op=, move ctor, and move op= would need to be defined)
*/
interpolatingDetector(interpolatingDetector const& other)
: singlePhotonDetector(other) {
interp = other.interp;
id = other.id;
fi = other.fi;
}
virtual interpolatingDetector *Clone() {
return new interpolatingDetector(this);
}
virtual interpolatingDetector *Copy() {
return new interpolatingDetector(*this);
}
virtual int setId(int i) {
id = i;
// interp->setId(id);

View File

@ -6,18 +6,86 @@
set(JUNGFRAU_EXECUTABLES)
find_package(fmt REQUIRED)
#find_package(fmt REQUIRED)
#nlohmann_json
#If the library was INSTALLED (i.e. sudo dnf install nlohmann-json3-dev), do stuff described in the repo under CMake -> External
#find_package(nlohmann_json 3.2.0 REQUIRED)
#find_package(nlohmann_json 3.11.2 REQUIRED)
#find_package(nlohmann_json 3.11.3 REQUIRED)
#
#If the library was not installed but just cloned from https://github.com/nlohmann/json.git (possible, because it is a header-only library),
# do stuff described in the repo under CMake -> Embedded
#set(JSON_BuildTests OFF CACHE INTERNAL "")
# If you only include this third party in PRIVATE source files, you do not
# need to install it when your main project gets installed.
# set(JSON_Install OFF CACHE INTERNAL "")
#add_subdirectory(nlohmann_json) #Put the actual path to json
#
#Alternative: Fetch and install it from remote
include(FetchContent)
FetchContent_Declare(
json
GIT_REPOSITORY https://github.com/nlohmann/json.git
GIT_TAG v3.11.3 # Replace with the version you need
)
#FetchContent_Declare(json URL https://github.com/nlohmann/json/releases/download/v3.11.3/json.tar.xz) #Alternative (from the repo documentation)
FetchContent_MakeAvailable(json)
FetchContent_Declare(
fmt
GIT_REPOSITORY https://github.com/fmtlib/fmt.git
GIT_TAG master
)
FetchContent_MakeAvailable(fmt)
# HDF5 file writing
if (SLS_USE_HDF5)
find_package(HDF5 1.10 COMPONENTS CXX REQUIRED)
list (APPEND SOURCES
HDF5File.cpp
)
endif (SLS_USE_HDF5)
# jungfrauRawDataProcess
add_executable(jungfrauRawDataProcess jungfrauRawDataProcess.cpp)
target_compile_definitions(jungfrauRawDataProcess PRIVATE MODULE)
list(APPEND JUNGFRAU_EXECUTABLES jungfrauRawDataProcess)
# jungfrauRawDataProcessChipAldo
add_executable(jungfrauRawDataProcessChipAldo jungfrauRawDataProcess_filetxt.cpp)
target_compile_definitions(jungfrauRawDataProcessChipAldo PRIVATE CHIP ALDO)
list(APPEND JUNGFRAU_EXECUTABLES jungfrauRawDataProcessChipAldo)
# jungfrauRawDataProcessStrx
add_executable(jungfrauRawDataProcessStrx jungfrauRawDataProcess_filetxt.cpp)
target_compile_definitions(jungfrauRawDataProcessStrx PRIVATE JFSTRX)
list(APPEND JUNGFRAU_EXECUTABLES jungfrauRawDataProcessStrx)
# jungfrauRawDataProcessStrxQuad
add_executable(jungfrauRawDataProcessStrxQuad jungfrauRawDataProcess_filetxt.cpp)
target_compile_definitions(jungfrauRawDataProcessStrxQuad PRIVATE JFSTRXQ)
list(APPEND JUNGFRAU_EXECUTABLES jungfrauRawDataProcessStrxQuad)
# jungfrauRawDataProcessStrxQuadH5
# HDF5
if (SLS_USE_HDF5)
if (HDF5_FOUND)
add_executable(jungfrauRawDataProcessStrxQuadH5 jungfrauRawDataProcess_filetxtH5.cpp)
#target_compile_definitions(jungfrauRawDataProcessStrxQuadH5 PRIVATE JFSTRXQH5)
list(APPEND JUNGFRAU_EXECUTABLES jungfrauRawDataProcessStrxQuadH5)
target_include_directories(jungfrauRawDataProcessStrxQuadH5 PRIVATE ${HDF5_INCLUDE_DIRS} ${CMAKE_INSTALL_PREFIX}/include)
target_link_libraries(jungfrauRawDataProcessStrxQuadH5 PRIVATE ${HDF5_LIBRARIES})
add_executable(jungfrauRawDataProcessStrxQuadH5SC jungfrauRawDataProcess_filetxtH5_SC.cpp)
#target_compile_definitions(jungfrauRawDataProcessStrxQuadH5 PRIVATE JFSTRXQH5)
list(APPEND JUNGFRAU_EXECUTABLES jungfrauRawDataProcessStrxQuadH5SC)
target_include_directories(jungfrauRawDataProcessStrxQuadH5SC PRIVATE ${HDF5_INCLUDE_DIRS} ${CMAKE_INSTALL_PREFIX}/include)
target_link_libraries(jungfrauRawDataProcessStrxQuadH5SC PRIVATE ${HDF5_LIBRARIES})
endif ()
endif (SLS_USE_HDF5)
# jungfrauRawDataProcessStrxChip1
add_executable(jungfrauRawDataProcessStrxChip1 jungfrauRawDataProcess.cpp)
@ -58,6 +126,22 @@ list(APPEND JUNGFRAU_EXECUTABLES jungfrauRawDataProcessStrxOldAldo)
# others to be added if needed (might already be there in Makefile.cluster_finder TO BE CHECKED)
if (SLS_USE_HDF5)
if (HDF5_FOUND)
target_include_directories(jungfrauRawDataProcessStrxQuadH5 PRIVATE
${HDF5_INCLUDE_DIRS}
${CMAKE_INSTALL_PREFIX}/include
)
target_link_libraries(jungfrauRawDataProcessStrxQuadH5 PRIVATE ${HDF5_LIBRARIES})
target_include_directories(jungfrauRawDataProcessStrxQuadH5SC PRIVATE
${HDF5_INCLUDE_DIRS}
${CMAKE_INSTALL_PREFIX}/include
)
target_link_libraries(jungfrauRawDataProcessStrxQuadH5SC PRIVATE ${HDF5_LIBRARIES})
endif ()
endif (SLS_USE_HDF5)
foreach(exe ${JUNGFRAU_EXECUTABLES})
@ -67,11 +151,16 @@ foreach(exe ${JUNGFRAU_EXECUTABLES})
../interpolations
../dataStructures
../interpolations/etaVEL
../../slsSupportLib/include/
../../slsSupportLib/include/sls/
../../slsReceiverSoftware/include/
../tiffio/include
${fmt_INCLUDE_DIRS}
)
# if (SLS_USE_HDF5)
# if (HDF5_FOUND)
# target_include_directories(${exe} PRIVATE ${HDF5_INCLUDE_DIRS} ${CMAKE_INSTALL_PREFIX}/include)
# endif ()
# endif (SLS_USE_HDF5)
target_link_libraries(${exe}
PUBLIC
@ -79,6 +168,7 @@ foreach(exe ${JUNGFRAU_EXECUTABLES})
pthread
tiffio
fmt::fmt
nlohmann_json::nlohmann_json
#-L/usr/lib64/
#-lm -lstdc++ -lrt
@ -86,7 +176,11 @@ foreach(exe ${JUNGFRAU_EXECUTABLES})
slsProjectWarnings
slsProjectOptions
)
# if (SLS_USE_HDF5)
# if (HDF5_FOUND)
# target_link_libraries(${exe} PRIVATE ${HDF5_LIBRARIES})
# endif ()
# endif (SLS_USE_HDF5)
set_target_properties(${exe} PROPERTIES
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin
@ -98,4 +192,8 @@ foreach(exe ${JUNGFRAU_EXECUTABLES})
endforeach(exe ${JUNGFRAU_EXECUTABLES})
install(TARGETS ${JUNGFRAU_EXECUTABLES} DESTINATION bin)
install(TARGETS ${JUNGFRAU_EXECUTABLES} DESTINATION bin)

View File

@ -11,7 +11,7 @@
#define RAWDATA
#if !defined JFSTRX && !defined JFSTRXOLD && !defined JFSTRXCHIP1 && \
!defined JFSTRXCHIP6
!defined JFSTRXCHIP6 && !defined CHIP
#ifndef MODULE
#include "jungfrauHighZSingleChipData.h"
#endif
@ -20,6 +20,10 @@
#endif
#endif
#ifdef CHIP
#include "jungfrauSingleChipData.h"
#endif
#ifdef JFSTRX
#include "jungfrauLGADStrixelsData_new.h"
#endif
@ -41,6 +45,9 @@
#include <ctime>
#include <fmt/core.h>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
std::string getRootString( const std::string& filepath ) {
size_t pos1 = filepath.find_last_of("/");
@ -61,20 +68,24 @@ std::string getRootString( const std::string& filepath ) {
std::string createFileName( const std::string& dir, const std::string& fprefix="run", const std::string& fsuffix="", const std::string& fext="raw", int aindex=0, int mindex=0, int findex=0, int outfilecounter=-1 ) {
if (outfilecounter >= 0)
return fmt::format("{:s}/{:s}_d{:d}_f{:d}_{:d}_f{:05d}.{:s}", dir, fprefix, mindex, findex, aindex, outfilecounter, fext);
else if (fsuffix.length()!=0)
return fmt::format("{:s}/{:s}_{:s}.{:s}", dir, fprefix, fsuffix, fext);
else if (fsuffix.length()!=0) {
if (fsuffix == "master")
return fmt::format("{:s}/{:s}_master_{:d}.{:s}", dir, fprefix, aindex, fext);
else
return fmt::format("{:s}/{:s}_{:s}.{:s}", dir, fprefix, fsuffix, fext);
}
else
return fmt::format("{:s}/{:s}_d{:d}_f{:d}_{:d}.{:s}", dir, fprefix, mindex, findex, aindex, fext);
}
int main(int argc, char *argv[]) {
if (argc < 5) {
if (argc < 6) {
std::cout
<< "Usage is " << argv[0]
<< "indir outdir fprefix(excluding slsDetector standard suffixes and extension) fextension "
"[runmin] [runmax] [pedfile (raw or tiff)] [threshold] "
"[nframes] [xmin xmax ymin ymax] [gainmap]"
<< "indir outdir [fprefix(excluding slsDetector standard suffixes and extension)] [fextension] "
"[fmin] [fmax] [runmin] [runmax] [pedfile (raw or tiff)] [threshold] "
"[nframes] [xmin xmax ymin ymax] [optional: bool read rxroi from data file header] [gainmap]"
<< std::endl;
std::cout
<< "threshold <0 means analog; threshold=0 means cluster finder; "
@ -106,94 +117,131 @@ int main(int argc, char *argv[]) {
std::string outdir(argv[2]);
std::string fprefix(argv[3]);
std::string fext(argv[4]);
int fmin = 0;
if (argc >= 6)
fmin = atoi(argv[5]);
int fmax = fmin;
if (argc >= 7)
fmax = atoi(argv[6]);
int runmin = 0;
// cout << "argc is " << argc << endl;
if (argc >= 6) {
runmin = atoi(argv[5]);
if (argc >= 8) {
runmin = atoi(argv[7]);
}
int runmax = runmin;
if (argc >= 7) {
runmax = atoi(argv[6]);
if (argc >= 9) {
runmax = atoi(argv[8]);
}
std::string pedfilename{};
if (argc >= 8) {
pedfilename = argv[7];
if (argc >= 10) {
pedfilename = argv[9];
}
double thr = 0;
double thr1 = 1;
if (argc >= 9) {
thr = atof(argv[8]);
if (argc >= 11) {
thr = atof(argv[10]);
}
int nframes = 0;
if (argc >= 10) {
nframes = atoi(argv[9]);
if (argc >= 12) {
nframes = atoi(argv[11]);
}
bool readrxroifromdatafile = false;
if (argc >= 17)
readrxroifromdatafile = atoi(argv[16]);
// Receiver ROI
uint16_t rxroi_xmin = 0;
uint16_t rxroi_xmax = 0;
uint16_t rxroi_ymin = 0;
uint16_t rxroi_ymax = 0;
{ //protective scope so ifstream gets destroyed properly
auto jsonmastername = createFileName( indir, fprefix, "master", "json", runmin );
std::cout << "json master file " << jsonmastername << std::endl;
std::ifstream masterfile(jsonmastername); //, ios::in | ios::binary);
if (masterfile.is_open()) {
json j;
masterfile >> j;
rxroi_xmin = j["Receiver Roi"]["xmin"];
rxroi_xmax = j["Receiver Roi"]["xmax"];
rxroi_ymin = j["Receiver Roi"]["ymin"];
rxroi_ymax = j["Receiver Roi"]["ymax"];
masterfile.close();
std::cout << "Read Receiver ROI [" << rxroi_xmin << ", " << rxroi_xmax << ", "
<< rxroi_ymin << ", " << rxroi_ymax << "] from json master file" << std::endl;
} else
std::cout << "Could not open master file " << jsonmastername << std::endl;
}
// Define decoders...
#if !defined JFSTRX && !defined JFSTRXOLD && !defined JFSTRXCHIP1 && \
!defined JFSTRXCHIP6
!defined JFSTRXCHIP6 && !defined CHIP
#ifndef MODULE
jungfrauHighZSingleChipData *decoder = new jungfrauHighZSingleChipData();
int nx = 256, ny = 256;
#endif
#ifdef MODULE
jungfrauModuleData *decoder = new jungfrauModuleData();
jungfrauModuleData *decoder = new jungfrauModuleData(rxroi_xmin, rxroi_xmax, rxroi_ymin, rxroi_ymax);
int nx = 1024, ny = 512;
#endif
#endif
#ifdef CHIP
std::cout << "Jungfrau pixel module single chip readout" << std::endl;
jungfrauSingleChipData *decoder = new jungfrauSingleChipData();
int nx = 256, ny = 256;
#endif
#ifdef JFSTRX
cout << "Jungfrau strixel full module readout" << endl;
// ROI
uint16_t xxmin = 0;
uint16_t xxmax = 0;
uint16_t yymin = 0;
uint16_t yymax = 0;
std::cout << "Jungfrau strixel full module readout" << std::endl;
#ifndef ALDO
{ //THIS SCOPE IS IMPORTANT! (To ensure proper destruction of ifstream)
using header = sls::defs::sls_receiver_header;
// check if there is a roi in the header
typedef struct {
uint16_t xmin;
uint16_t xmax;
uint16_t ymin;
uint16_t ymax;
} receiverRoi_compact;
receiverRoi_compact croi;
std::string fsuffix{};
auto filename = createFileName( indir, fprefix, fsuffix, fext, runmin );
std::cout << "Reading header of file " << filename << " to check for ROI "
<< std::endl;
ifstream firstfile(filename, ios::in | ios::binary);
if (firstfile.is_open()) {
header hbuffer;
std::cout << "sizeof(header) = " << sizeof(header) << std::endl;
if (firstfile.read((char *)&hbuffer, sizeof(header))) {
memcpy(&croi, &hbuffer.detHeader.detSpec1, 8);
std::cout << "Read ROI [" << croi.xmin << ", " << croi.xmax << ", "
<< croi.ymin << ", " << croi.ymax << "]" << std::endl;
xxmin = croi.xmin;
xxmax = croi.xmax;
yymin = croi.ymin;
yymax = croi.ymax;
} else
std::cout << "reading error" << std::endl;
firstfile.close();
} else
std::cout << "Could not open " << filename << " for reading " << std::endl;
} //end of protective scope
if (readrxroifromdatafile)
{ //THIS SCOPE IS IMPORTANT! (To ensure proper destruction of ifstream)
using header = sls::defs::sls_receiver_header;
// check if there is a roi in the header
typedef struct {
uint16_t xmin;
uint16_t xmax;
uint16_t ymin;
uint16_t ymax;
} receiverRoi_compact;
receiverRoi_compact croi;
std::string fsuffix{};
auto filename = createFileName( indir, fprefix, fsuffix, fext, runmin );
std::cout << "Reading header of file " << filename << " to check for ROI "
<< std::endl;
ifstream firstfile(filename, ios::in | ios::binary);
if (firstfile.is_open()) {
header hbuffer;
std::cout << "sizeof(header) = " << sizeof(header) << std::endl;
if (firstfile.read((char *)&hbuffer, sizeof(header))) {
memcpy(&croi, &hbuffer.detHeader.detSpec1, 8);
std::cout << "Read ROI [" << croi.xmin << ", " << croi.xmax << ", "
<< croi.ymin << ", " << croi.ymax << "]" << std::endl;
rxroi_xmin = croi.xmin;
rxroi_xmax = croi.xmax;
rxroi_ymin = croi.ymin;
rxroi_ymax = croi.ymax;
} else
std::cout << "reading error" << std::endl;
firstfile.close();
} else
std::cout << "Could not open " << filename << " for reading " << std::endl;
} //end of protective scope
#endif
jungfrauLGADStrixelsData *decoder =
new jungfrauLGADStrixelsData(xxmin, xxmax, yymin, yymax);
new jungfrauLGADStrixelsData(rxroi_xmin, rxroi_xmax, rxroi_ymin, rxroi_ymax);
int nx = 1024 / 3, ny = 512 * 5;
#endif
#ifdef JFSTRXCHIP1
@ -218,19 +266,20 @@ int main(int argc, char *argv[]) {
decoder->getDetectorSize(nx, ny);
std::cout << "Detector size is " << nx << " " << ny << std::endl;
int xmin = 0, xmax = nx, ymin = 0, ymax = ny;
if (argc >= 14) {
xmin = atoi(argv[10]);
xmax = atoi(argv[11]);
ymin = atoi(argv[12]);
ymax = atoi(argv[13]);
//Cluster finder ROI
int xmin = 0, xmax = nx-1, ymin = 0, ymax = ny-1;
if (argc >= 16) {
xmin = atoi(argv[12]);
xmax = atoi(argv[13]);
ymin = atoi(argv[14]);
ymax = atoi(argv[15]);
}
std::cout << xmin << " " << xmax << " " << ymin << " " << ymax << " "
std::cout << "Cluster finder ROI: [" << xmin << ", " << xmax << ", " << ymin << ", " << ymax << "]"
<< std::endl;
char *gainfname = NULL;
if (argc > 14) {
gainfname = argv[14];
if (argc > 17) {
gainfname = argv[17];
std::cout << "Gain map file name is: " << gainfname << std::endl;
}
@ -239,6 +288,8 @@ int main(int argc, char *argv[]) {
std::cout << "input directory is " << indir << std::endl;
std::cout << "output directory is " << outdir << std::endl;
std::cout << "input file prefix is " << fprefix << std::endl;
std::cout << "fmin is " << fmin << std::endl;
std::cout << "fmax is " << fmax << std::endl;
std::cout << "runmin is " << runmin << std::endl;
std::cout << "runmax is " << runmax << std::endl;
if (pedfilename.length()!=0)
@ -319,7 +370,7 @@ int main(int argc, char *argv[]) {
mt->setFrameMode(ePedestal);
ifstream pedefile(fname, ios::in | ios::binary);
std::ifstream pedefile(fname, ios::in | ios::binary);
// //open file
if (pedefile.is_open()) {
std::cout << "bbbb " << std::ctime(&end_time) << std::endl;
@ -380,26 +431,27 @@ int main(int argc, char *argv[]) {
}
ifr = 0;
int ifile = 0;
int ioutfile = 0;
mt->setFrameMode(eFrame);
FILE *of = NULL;
for (int irun = runmin; irun <= runmax; irun++) {
for (int irun = runmin; irun <= runmax; ++irun) {
for (int ifile = fmin; ifile <= fmax; ++ifile) {
std::cout << "DATA ";
std::string fsuffix{};
auto fname = createFileName( indir, fprefix, fsuffix, fext, irun );
auto imgfname = createFileName( outdir, fprefix, fsuffix, "tiff", irun );
auto cfname = createFileName( outdir, fprefix, fsuffix, "clust", irun );
auto fname = createFileName( indir, fprefix, fsuffix, fext, irun, 0, ifile );
auto imgfname = createFileName( outdir, fprefix, fsuffix, "tiff", irun, 0, ifile );
auto cfname = createFileName( outdir, fprefix, fsuffix, "clust", irun, 0, ifile );
std::cout << fname << " ";
std::cout << imgfname << std::endl;
std::time(&end_time);
std::cout << std::ctime(&end_time) << std::endl;
// std::cout << fname << " " << outfname << " " << imgfname << std::endl;
ifstream filebin(fname, ios::in | ios::binary);
std::ifstream filebin(fname, ios::in | ios::binary);
// //open file
ifile = 0;
ioutfile = 0;
if (filebin.is_open()) {
if (thr <= 0 && cf != 0) { // cluster finder
if (of == NULL) {
@ -436,10 +488,10 @@ int main(int argc, char *argv[]) {
std::cout << " " << ifr << " " << ff << std::endl;
if (nframes > 0) {
if (ifr % nframes == 0) {
imgfname = createFileName( outdir, fprefix, fsuffix, "tiff", irun, 0, 0, ifile );
imgfname = createFileName( outdir, fprefix, fsuffix, "tiff", irun, 0, 0, ioutfile );
mt->writeImage(imgfname.c_str(), thr1);
mt->clearImage();
ifile++;
ioutfile++;
}
}
// } else
@ -453,7 +505,7 @@ int main(int argc, char *argv[]) {
}
if (nframes >= 0) {
if (nframes > 0)
imgfname = createFileName( outdir, fprefix, fsuffix, "tiff", irun, 0, 0, ifile );
imgfname = createFileName( outdir, fprefix, fsuffix, "tiff", irun, 0, 0, ioutfile );
std::cout << "Writing tiff to " << imgfname << " " << thr1
<< std::endl;
mt->writeImage(imgfname.c_str(), thr1);
@ -469,9 +521,10 @@ int main(int argc, char *argv[]) {
} else
std::cout << "Could not open " << fname << " for reading "
<< std::endl;
}
}
if (nframes < 0) {
auto imgfname = createFileName( outdir, fprefix, "sum", "tiff", -1, 0, 0, -1 );
auto imgfname = createFileName( outdir, fprefix, "sum", "tiff", runmin, 0, fmin, -1 );
std::cout << "Writing tiff to " << imgfname << " " << thr1 << std::endl;
mt->writeImage(imgfname.c_str(), thr1);
}

View File

@ -10,8 +10,8 @@
#define RAWDATA
#if !defined JFSTRX && !defined JFSTRXOLD && !defined JFSTRXCHIP1 && \
!defined JFSTRXCHIP6
#if !defined JFSTRX && !defined JFSTRXQ && !defined JFSTRXOLD && !defined JFSTRXCHIP1 && \
!defined JFSTRXCHIP6 && !defined CHIP
#ifndef MODULE
#include "jungfrauHighZSingleChipData.h"
#endif
@ -20,9 +20,16 @@
#endif
#endif
#ifdef CHIP
#include "jungfrauSingleChipData.h"
#endif
#ifdef JFSTRX
#include "jungfrauLGADStrixelsData_new.h"
#endif
#ifdef JFSTRXQ
#include "jungfrauLGADStrixelsDataQuad.h"
#endif
#if defined JFSTRXCHIP1 || defined JFSTRXCHIP6
#include "jungfrauLGADStrixelsDataSingleChip.h"
#endif
@ -41,6 +48,9 @@
#include <ctime>
#include <fmt/core.h>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
std::string getRootString( const std::string& filepath ) {
size_t pos1;
@ -71,11 +81,11 @@ std::string createFileName( const std::string& dir, const std::string& fprefix="
//NOTE THAT THE DATA FILES HAVE TO BE IN THE RIGHT ORDER SO THAT PEDESTAL TRACKING WORKS!
int main(int argc, char *argv[]) {
if (argc < 10) {
if (argc < 11) {
std::cout
<< "Usage is " << argv[0]
<< " filestxt outdir [pedfile (raw or tiff)] [xmin xmax ymin ymax] "
"[threshold] [nframes] "
<< " filestxt outdir [json master] [pedfile (raw or tiff)] [xmin xmax ymin ymax] "
"[threshold] [nframes] [optional: bool read rxroi from data file header]"
"NOTE THAT THE DATA FILES HAVE TO BE IN THE RIGHT ORDER SO THAT PEDESTAL TRACKING WORKS! "
<< std::endl;
std::cout
@ -105,19 +115,19 @@ int main(int argc, char *argv[]) {
const std::string txtfilename(argv[1]);
const std::string outdir(argv[2]);
const std::string pedfilename(argv[3]);
int xmin = atoi(argv[4]);
int xmax = atoi(argv[5]);
int ymin = atoi(argv[6]);
int ymax = atoi(argv[7]);
const std::string jsonmastername(argv[3]);
const std::string pedfilename(argv[4]);
double thr = 0;
double thr1 = 1;
thr = atof(argv[8]);
thr = atof(argv[9]);
int nframes = 0;
nframes = atoi(argv[9]);
nframes = atoi(argv[10]);
bool readrxroifromdatafile = false;
if (argc > 11)
readrxroifromdatafile = atoi(argv[11]);
//Get vector of filenames from input txt-file
std::vector<std::string> filenames{};
@ -146,10 +156,34 @@ int main(int argc, char *argv[]) {
}
std::cout << "###############" << std::endl;
// Receiver ROI
uint16_t rxroi_xmin = 0;
uint16_t rxroi_xmax = 0;
uint16_t rxroi_ymin = 0;
uint16_t rxroi_ymax = 0;
{ //protective scope so ifstream gets destroyed properly
std::ifstream masterfile(jsonmastername); //, ios::in | ios::binary);
if (masterfile.is_open()) {
json j;
masterfile >> j;
rxroi_xmin = j["Receiver Roi"]["xmin"];
rxroi_xmax = j["Receiver Roi"]["xmax"];
rxroi_ymin = j["Receiver Roi"]["ymin"];
rxroi_ymax = j["Receiver Roi"]["ymax"];
masterfile.close();
std::cout << "Read rxROI [" << rxroi_xmin << ", " << rxroi_xmax << ", "
<< rxroi_ymin << ", " << rxroi_ymax << "]" << std::endl;
} else
std::cout << "Could not open master file " << jsonmastername << std::endl;
}
// Define decoders...
#if !defined JFSTRX && !defined JFSTRXOLD && !defined JFSTRXCHIP1 && \
!defined JFSTRXCHIP6
#if !defined JFSTRX && !defined JFSTRXQ && !defined JFSTRXOLD && !defined JFSTRXCHIP1 && \
!defined JFSTRXCHIP6 && !defined CHIP
#ifndef MODULE
jungfrauHighZSingleChipData *decoder = new jungfrauHighZSingleChipData();
int nx = 256, ny = 256;
@ -160,52 +194,60 @@ int main(int argc, char *argv[]) {
#endif
#endif
#ifdef CHIP
std::cout << "Jungfrau pixel module single chip readout" << std::endl;
jungfrauSingleChipData *decoder = new jungfrauSingleChipData();
int nx = 256, ny = 256;
#endif
#ifdef JFSTRX
cout << "Jungfrau strixel full module readout" << endl;
// ROI
uint16_t xxmin = 0;
uint16_t xxmax = 0;
uint16_t yymin = 0;
uint16_t yymax = 0;
std::cout << "Jungfrau strixel full module readout" << std::endl;
#ifndef ALDO
{ //THIS SCOPE IS IMPORTANT! (To ensure proper destruction of ifstream)
using header = sls::defs::sls_receiver_header;
// check if there is a roi in the header
typedef struct {
uint16_t xmin;
uint16_t xmax;
uint16_t ymin;
uint16_t ymax;
} receiverRoi_compact;
receiverRoi_compact croi;
//std::string filepath(argv[9]); //This is a problem if the input files have different ROIs!
std::cout << "Reading header of file " << filenames[0] << " to check for ROI "
<< std::endl;
ifstream firstfile(filenames[0], ios::in | ios::binary);
if (firstfile.is_open()) {
header hbuffer;
std::cout << "sizeof(header) = " << sizeof(header) << std::endl;
if (firstfile.read((char *)&hbuffer, sizeof(header))) {
memcpy(&croi, &hbuffer.detHeader.detSpec1, 8);
std::cout << "Read ROI [" << croi.xmin << ", " << croi.xmax << ", "
<< croi.ymin << ", " << croi.ymax << "]" << std::endl;
xxmin = croi.xmin;
xxmax = croi.xmax;
yymin = croi.ymin;
yymax = croi.ymax;
} else
std::cout << "reading error" << std::endl;
firstfile.close();
} else
std::cout << "Could not open " << filenames[0] << " for reading " << std::endl;
} //end of protective scope
if (readrxroifromdatafile)
{ //THIS SCOPE IS IMPORTANT! (To ensure proper destruction of ifstream)
using header = sls::defs::sls_receiver_header;
// check if there is a roi in the header
typedef struct {
uint16_t xmin;
uint16_t xmax;
uint16_t ymin;
uint16_t ymax;
} receiverRoi_compact;
receiverRoi_compact croi;
//std::string filepath(argv[9]); //This is a problem if the input files have different ROIs!
std::cout << "Reading header of file " << filenames[0] << " to check for ROI "
<< std::endl;
std::ifstream firstfile( filenames[0], ios::in | ios::binary);
if (firstfile.is_open()) {
header hbuffer;
std::cout << "sizeof(header) = " << sizeof(header) << std::endl;
if (firstfile.read((char *)&hbuffer, sizeof(header))) {
memcpy(&croi, &hbuffer.detHeader.detSpec1, 8);
std::cout << "Read ROI [" << croi.xmin << ", " << croi.xmax << ", "
<< croi.ymin << ", " << croi.ymax << "]" << std::endl;
rxroi_xmin = croi.xmin;
rxroi_xmax = croi.xmax;
rxroi_ymin = croi.ymin;
rxroi_ymax = croi.ymax;
} else
std::cout << "reading error" << std::endl;
firstfile.close();
} else
std::cout << "Could not open " << filenames[0] << " for reading " << std::endl;
} //end of protective scope
#endif
jungfrauLGADStrixelsData *decoder =
new jungfrauLGADStrixelsData(xxmin, xxmax, yymin, yymax);
new jungfrauLGADStrixelsData(rxroi_xmin, rxroi_xmax, rxroi_ymin, rxroi_ymax);
int nx = 1024 / 3, ny = 512 * 5;
#endif
#ifdef JFSTRXQ
std::cout << "Jungfrau strixel quad" << std::endl;
jungfrauLGADStrixelsDataQuad *decoder =
new jungfrauLGADStrixelsDataQuad(rxroi_xmin, rxroi_xmax, rxroi_ymin, rxroi_ymax);
int nx = 1024 / 3, ny = 512 * 3;
#endif
#ifdef JFSTRXCHIP1
std::cout << "Jungfrau strixel LGAD single chip 1" << std::endl;
jungfrauLGADStrixelsDataSingleChip *decoder =
@ -228,7 +270,16 @@ int main(int argc, char *argv[]) {
decoder->getDetectorSize(nx, ny);
std::cout << "Detector size is " << nx << " " << ny << std::endl;
//Cluster finder ROI
int xmin = 0, xmax = nx-1, ymin = 0, ymax = ny-1;
xmin = atoi(argv[5]);
xmax = atoi(argv[6]);
ymin = atoi(argv[7]);
ymax = atoi(argv[8]);
std::cout << "Cluster finder ROI: [" << xmin << ", " << xmax << ", " << ymin << ", " << ymax << "]"
<< std::endl;
/* old
if ( xmin == xmax ) {
xmin = 0;
xmax = nx;
@ -239,6 +290,7 @@ int main(int argc, char *argv[]) {
}
std::cout << xmin << " " << xmax << " " << ymin << " " << ymax << " "
<< std::endl;
*/
/*
char *gainfname = NULL;
@ -410,7 +462,7 @@ int main(int argc, char *argv[]) {
std::time(&end_time);
std::cout << std::ctime(&end_time) << std::endl;
ifstream filebin(filenames[ifile], ios::in | ios::binary);
std::ifstream filebin(filenames[ifile], ios::in | ios::binary);
// //open file
ioutfile = 0;
if (filebin.is_open()) {

View File

@ -0,0 +1,457 @@
// SPDX-License-Identifier: LGPL-3.0-or-other
// Copyright (C) 2021 Contributors to the SLS Detector Package
// #include "sls/ansi.h"
//#include <iostream>
#undef CORR
#define C_GHOST 0.0004
#define CM_ROWS 50
#define RAWDATA
#include "jungfrauLGADStrixelsDataQuadH5.h"
#include "multiThreadedCountingDetector.h"
#include "singlePhotonDetector.h"
#include <fstream>
#include <map>
#include <memory>
#include <stdio.h>
#include <sys/stat.h>
#include <ctime>
#include <fmt/core.h>
/*
#include <nlohmann/json.hpp>
using json = nlohmann::json;
*/
/*Dataset paths according to different beamlines*/
std::string const data_datasetname_furka("/data/JF18T01V01/data");
std::string const index_datasetname_furka("/data/JF18T01V01/frame_index");
std::string const data_datasetname_xfelSCS("/INSTRUMENT/SCS_HRIXS_JUNGF/DET/JNGFR01:daqOutput/data/adc");
std::string const index_datasetname_xfelSCS("/INSTRUMENT/SCS_HRIXS_JUNGF/DET/JNGFR01:daqOutput/data/frameNumber");
std::string getRootString( std::string const& filepath ) {
size_t pos1;
if (filepath.find("/") == std::string::npos )
pos1 = 0;
else
pos1 = filepath.find_last_of("/")+1;
size_t pos2 = filepath.find_last_of(".");
//std::cout << "pos1 " << pos1 << " pos2 " << pos2 << " size " << filepath.length() << std::endl;
return filepath.substr( pos1, pos2-pos1 );
}
//Create file name string
// dir: directory
// fprefix: fileprefix (without extension)
// fsuffix: filesuffix (for output files, e.g. "ped")
// fext: file extension (e.g. "raw")
std::string createFileName( std::string const& dir, std::string const& fprefix="run",
std::string const& fsuffix="", std::string const& fext="raw", int const outfilecounter=-1 ) {
std::string return_string;
if (outfilecounter >= 0)
return_string = fmt::format("{:s}/{:s}_{:s}_f{:05d}", dir, fprefix, fsuffix, outfilecounter);
else if (fsuffix.length()!=0)
return_string = fmt::format("{:s}/{:s}_{:s}", dir, fprefix, fsuffix);
else
return_string = fmt::format("{:s}/{:s}", dir, fprefix);
if (fext.length()!=0)
return_string += "." + fext;
return return_string;
}
//NOTE THAT THE DATA FILES HAVE TO BE IN THE RIGHT ORDER SO THAT PEDESTAL TRACKING WORKS!
int main(int argc, char *argv[]) {
if (argc < 4) {
std::cout
<< "Usage is " << argv[0]
<< " filestxt outdir [pedfile (h5)] "
" optional: [int dataset path; 0 means Furka, 1 means XFEL; overwrites default given in HDF5File.h] "
" [bool validate h5 rank] "
" [xmin xmax ymin ymax] [threshold] [nframes] "
" NOTE THAT THE DATA FILES HAVE TO BE IN THE RIGHT ORDER SO THAT PEDESTAL TRACKING WORKS! "
<< std::endl;
std::cout
<< "threshold <0 means analog; threshold=0 means cluster finder; "
"threshold>0 means photon counting"
<< std::endl;
std::cout
<< "nframes <0 means sum everything; nframes=0 means one file per "
"run; nframes>0 means one file every nframes"
<< std::endl;
return 1;
}
int const fifosize = 100; //1000;
int const nthreads = 10;
int const csize = 3; // 3
int const nsigma = 5;
int const nped = 10000;
int cf = 0;
std::string const txtfilename(argv[1]);
std::string const outdir(argv[2]);
std::string const pedfilename(argv[3]);
std::string datasetpath{};
std::string frameindexpath{};
if (argc > 4) {
switch (atoi(argv[4])) {
case 0:
datasetpath = data_datasetname_furka;
frameindexpath = index_datasetname_furka;
break;
case 1:
datasetpath = data_datasetname_xfelSCS;
frameindexpath = index_datasetname_xfelSCS;
break;
default:
break;
}
}
bool validate_rank=true;
if (argc > 5)
validate_rank = atoi(argv[5]);
double thr = 0;
double thr1 = 1;
if (argc > 9)
thr = atof(argv[9]);
int nframes = 0;
if (argc > 10)
nframes = atoi(argv[10]);
//Get vector of filenames from input txt-file
std::vector<std::string> filenames{};
//filenames.reserve(512);
{ //Safety scope for ifstream
ifstream inputs( txtfilename, std::ios::in );
if (inputs.is_open()) {
std::cout << "Reading imput filenames from txt-file ..." << std::endl;
std::string line{};
while (!inputs.eof()) {
std::getline(inputs, line);
if(line.find(".h5") != std::string::npos) {
filenames.emplace_back(line);
std::cout << line << std::endl; //" line.max_size() " << line.max_size() << " filenames.capacity() " << filenames.capacity() << '\n';
}
}
inputs.close();
std::cout << "---- Reached end of txt-file. ----" << std::endl;
} else
std::cout << "Could not open " << txtfilename << std::endl;
if (filenames.size()>0) {
std::cout << filenames.size() << " filenames found in " << txtfilename << std::endl;
std::cout << "The files will be processed in the same order as found in the txt-file." << std::endl;
} else {
std::cout << "No files found in txt-file!" << std::endl;
return 1;
}
}
std::cout << "###############" << std::endl;
// Define decoder
std::cout << "Jungfrau strixel quad h5" << std::endl;
jungfrauLGADStrixelsDataQuadH5* decoder = new jungfrauLGADStrixelsDataQuadH5();
//auto decoder = std::make_unique<jungfrauLGADStrixelsDataQuadH5>();
int nx = 1024 / 3, ny = 512 * 3;
//Cluster finder ROI
int xmin = 0, xmax = nx-1, ymin = 0, ymax = ny-1;
if (argc > 9) {
xmin = atoi(argv[6]);
xmax = atoi(argv[7]);
ymin = atoi(argv[8]);
ymax = atoi(argv[9]);
}
std::cout << "Cluster finder ROI: [" << xmin << ", " << xmax << ", " << ymin << ", " << ymax << "]"
<< std::endl;
decoder->getDetectorSize(nx, ny);
std::cout << "Detector size is " << nx << " " << ny << std::endl;
std::time_t end_time;
std::cout << "output directory is " << outdir << std::endl;
if (pedfilename.length()!=0)
std::cout << "pedestal file is " << pedfilename << std::endl;
if (thr > 0)
std::cout << "threshold is " << thr << std::endl;
std::cout << "Nframes is " << nframes << std::endl;
uint32_t nnx, nny;
singlePhotonDetector* filter =
new singlePhotonDetector(decoder, 3, nsigma, 1, NULL, nped, 200, -1, -1, NULL, NULL);
//auto filter = std::make_unique<singlePhotonDetector>(decoder.get(), 3, nsigma, 1, nullptr, nped, 200, -1, -1, nullptr, nullptr);
thr = 0.15 * thr;
//filter->newDataSet(); //This only initializes the dataset for the first thread (the other threads are created via cloning)
// int dsize = decoder->getDataSize();
if (thr > 0) {
std::cout << "threshold is " << thr << std::endl;
filter->setThreshold(thr);
cf = 0;
} else
cf = 1;
filter->setROI(xmin, xmax, ymin, ymax);
std::time(&end_time);
std::cout << std::ctime(&end_time) << std::endl;
char* buff;
multiThreadedCountingDetector* mt =
new multiThreadedCountingDetector(filter, nthreads, fifosize);
//auto mt = std::make_unique<multiThreadedCountingDetector>(filter.get(), nthreads, fifosize);
mt->setClusterSize(csize, csize);
mt->newDataSet(); //Initialize new dataset for each thread
#ifndef ANALOG
mt->setDetectorMode(ePhotonCounting);
std::cout << "Counting!" << std::endl;
if (thr > 0) {
cf = 0;
}
#endif
//{
#ifdef ANALOG
mt->setDetectorMode(eAnalog);
std::cout << "Analog!" << std::endl;
cf = 0;
// thr1=thr;
#endif
// }
mt->StartThreads();
mt->popFree(buff);
int ifr = 0; //frame counter of while loop
int framenumber = 0; //framenumber as read from file (detector)
std::vector<hsize_t> h5offset(1,0); //frame counter internal to HDF5File::ReadImage (provided for sanity check/debugging)
if (pedfilename.length()>1) {
std::cout << "PEDESTAL " << std::endl;
if (pedfilename.find(".tif") == std::string::npos) { //not a tiff file
std::string const fname(pedfilename);
std::cout << fname << std::endl;
std::time(&end_time);
std::cout << "aaa " << std::ctime(&end_time) << std::endl;
mt->setFrameMode(ePedestal);
//HDF5File pedefile;
auto pedefile = std::make_unique<HDF5File>();
pedefile->SetFrameIndexPath(frameindexpath);
pedefile->SetImageDataPath(datasetpath);
// //open file
if ( pedefile->OpenResources(fname.c_str(),validate_rank) ) {
std::cout << "bbbb " << std::ctime(&end_time) << std::endl;
framenumber = 0;
while ( decoder->readNextFrame(*pedefile, framenumber, h5offset, buff) ) {
if ((ifr + 1) % 100 == 0) {
std::cout
<< " ****"
<< decoder->getValue(buff, 20, 20); // << std::endl;
}
mt->pushData(buff);
mt->nextThread();
mt->popFree(buff);
++ifr;
if (ifr % 100 == 0) {
std::cout << " ****" << ifr << " " << framenumber << " " << h5offset[0]
<< std::endl;
} // else
if (ifr >= 1000)
break;
//framenumber = 0;
}
pedefile->CloseResources();
while (mt->isBusy()) {
;
}
std::cout << "Writing pedestal to " << getRootString(pedfilename) << "_ped.tiff" << std::endl;
auto imgfname = createFileName( outdir, getRootString(pedfilename), "ped", "");
mt->writePedestal(imgfname.c_str());
std::cout << "Writing pedestal rms to " << getRootString(pedfilename) << "_rms.tiff" << std::endl;
imgfname = createFileName( outdir, getRootString(pedfilename), "rms", "");
mt->writePedestalRMS(imgfname.c_str());
} else
std::cout << "Could not open pedestal file " << fname
<< " for reading " << std::endl;
} else { //is a tiff file
std::vector<double> ped(nx * ny);
float* pp = ReadFromTiff(pedfilename.c_str(), nny, nnx);
if (pp && (int)nnx == nx && (int)nny == ny) {
for (int i = 0; i < nx * ny; i++) {
ped[i] = pp[i];
}
delete[] pp;
mt->setPedestal(ped.data());
std::cout << "Pedestal set from tiff file " << pedfilename
<< std::endl;
} else {
std::cout << "Could not open pedestal tiff file " << pedfilename
<< " for reading " << std::endl;
}
}
std::time(&end_time);
std::cout << std::ctime(&end_time) << std::endl;
}
ifr = 0;
int ioutfile = 0;
mt->setFrameMode(eFrame);
FILE* of = nullptr;
//NOTE THAT THE DATA FILES HAVE TO BE IN THE RIGHT ORDER SO THAT PEDESTAL TRACKING WORKS!
for (unsigned int ifile = 0; ifile != filenames.size(); ++ifile) {
std::cout << "DATA " << filenames[ifile] << " " << std::endl;
auto imgfname( createFileName( outdir, getRootString(filenames[ifile]), "", "" ) );
std::string const cfname( createFileName( outdir, getRootString(filenames[ifile]), "", "clust" ) );
std::time(&end_time);
std::cout << std::ctime(&end_time) << std::endl;
//HDF5File fileh5;
auto fileh5 = std::make_unique<HDF5File>();
fileh5->SetFrameIndexPath(frameindexpath);
fileh5->SetImageDataPath(datasetpath);
// //open file
ioutfile = 0;
if ( fileh5->OpenResources(filenames[ifile].c_str(), validate_rank) ) {
if (thr <= 0 && cf != 0) { // cluster finder
if (of == nullptr) {
of = fopen(cfname.c_str(), "w");
if (of) {
if (mt) {
mt->setFilePointer(of);
std::cout << "file pointer set " << std::endl;
} else {
std::cerr << "Error: mt is null." << std::endl;
return 1;
}
//mt->setFilePointer(of);
//std::cout << "file pointer set " << std::endl;
//std::cout << "Here! " << framenumber << " ";
} else {
std::cout << "Could not open " << cfname
<< " for writing " << std::endl;
mt->setFilePointer(nullptr);
return 1;
}
}
}
// //while read frame
framenumber = 0;
h5offset[0] = 0;
ifr = 0;
//std::cout << "Here! " << framenumber << " ";
while ( decoder->readNextFrame(*fileh5, framenumber, h5offset, buff) ) {
//std::cout << "Here! " << framenumber << " ";
// //push
if ((ifr + 1) % 1000 == 0) {
std::cout << " ****"
<< decoder->getValue(buff, 20, 20); // << std::endl;
}
mt->pushData(buff);
// // //pop
mt->nextThread();
mt->popFree(buff); /* In the last execution of the loop,
* this leaves buff outside of the Fifo!
* Free explicitely at the end! */
++ifr;
if (ifr % 1000 == 0)
std::cout << " " << ifr << " " << framenumber << " " << h5offset[0]
<< std::endl;
if (nframes > 0) {
if (ifr % nframes == 0) {
imgfname = createFileName( outdir, getRootString(filenames[ifile]), "", "", ioutfile );
mt->writeImage(imgfname.c_str(), thr1);
mt->clearImage();
++ioutfile;
}
}
//framenumber = 0;
}
//std::cout << "aa --" << std::endl;
fileh5->CloseResources();
//std::cout << "bb --" << std::endl;
while (mt->isBusy()) {
;
}
//std::cout << "cc --" << std::endl;
if (nframes >= 0) {
if (nframes > 0)
imgfname = createFileName( outdir, getRootString(filenames[ifile]), "", "", ioutfile );
std::cout << "Writing tiff to " << imgfname << " " << thr1 << ".tiff"
<< std::endl;
mt->writeImage(imgfname.c_str(), thr1);
mt->clearImage();
if (of) {
fclose(of);
of = nullptr;
mt->setFilePointer(nullptr);
}
}
std::time(&end_time);
std::cout << std::ctime(&end_time) << std::endl;
} else
std::cout << "Could not open " << filenames[ifile] << " for reading "
<< std::endl;
}
if (nframes < 0) {
//std::string fprefix( getRootString(filenames[0]) ); //Possibly, non-ideal name choice for file
auto imgfname( createFileName( outdir, getRootString(filenames[0]), "sum", "" ) );
std::cout << "Writing tiff to " << imgfname << " " << thr1 << ".tiff" << std::endl;
mt->writeImage(imgfname.c_str(), thr1);
}
//std::cout << "Calling delete..." << std::endl;
/* Info: Previously, 'delete mt' caused crash
(double calls of StopThread() in both destructors of
multiThreadedAnalogDetector and threadedAnalogDetector)
Now fixed! */
delete mt; // triggers cleanup of all threads and singlePhotonDetector instances (delete filter is obsolete)
delete decoder;
free(buff); // Free explicitly as it gets popped out of the Fifo at termination of while(readNextFrame)
return 0;
}

View File

@ -0,0 +1,468 @@
// SPDX-License-Identifier: LGPL-3.0-or-other
// Copyright (C) 2021 Contributors to the SLS Detector Package
// #include "sls/ansi.h"
//#include <iostream>
#undef CORR
#define RAWDATA
#include "jungfrauLGADStrixelsDataQuadH5.h"
#include "multiThreadedCountingDetector.h"
#include "singlePhotonDetector.h"
#include <fstream>
#include <csignal>
#include <map>
#include <memory>
#include <stdio.h>
#include <sys/stat.h>
#include <ctime>
#include <fmt/core.h>
/*Dataset paths according to different beamlines*/
std::string const data_datasetname_furka("/data/JF18T01V01/data");
std::string const index_datasetname_furka("/data/JF18T01V01/frame_index");
std::string const data_datasetname_xfelSCS("/INSTRUMENT/SCS_HRIXS_JUNGF/DET/JNGFR01:daqOutput/data/adc");
std::string const index_datasetname_xfelSCS("/INSTRUMENT/SCS_HRIXS_JUNGF/DET/JNGFR01:daqOutput/data/frameNumber");
std::string getRootString( std::string const& filepath ) {
size_t pos1;
if (filepath.find("/") == std::string::npos )
pos1 = 0;
else
pos1 = filepath.find_last_of("/")+1;
size_t pos2 = filepath.find_last_of(".");
//std::cout << "pos1 " << pos1 << " pos2 " << pos2 << " size " << filepath.length() << std::endl;
return filepath.substr( pos1, pos2-pos1 );
}
/* Create file name string
* \param dir directory
* \param fprefix fileprefix (without extension)
* \param fsuffix filesuffix (for output files, e.g. "ped")
* \param fext file extension (e.g. "raw")
*/
std::string createFileName( std::string const& dir, std::string const& fprefix="run",
std::string const& fsuffix="", std::string const& fext="raw", int const outfilecounter=-1 ) {
std::string return_string;
if (outfilecounter >= 0)
return_string = fmt::format("{:s}/{:s}_{:s}_f{:05d}", dir, fprefix, fsuffix, outfilecounter);
else if (fsuffix.length()!=0)
return_string = fmt::format("{:s}/{:s}_{:s}", dir, fprefix, fsuffix);
else
return_string = fmt::format("{:s}/{:s}", dir, fprefix);
if (fext.length()!=0)
return_string += "." + fext;
return return_string;
}
/* Adjusts number of threads to be a multiple of number of storage cells
* \param requestedThreads number of threads requested by the user
* \param nSC number of storage cells
*/
int adjustThreads(int requestedThreads, int nSC) {
if (nSC <= 0) {
std::cerr << "Error: Number of S values must be greater than zero!" << std::endl;
return requestedThreads; // Return the original value as a fallback
}
// Calculate the remainder
int remainder = requestedThreads % nSC;
// If remainder is non-zero, round up by adding the difference
int adjustedThreads = (remainder == 0) ? requestedThreads : requestedThreads + (nSC - remainder);
// Ensure at least `nSC` threads are used
if (adjustedThreads < nSC) {
adjustedThreads = nSC;
}
std::cout << "Adjusted thread count (rounded up): " << adjustedThreads << " (nearest multiple of "
<< nSC << ")" << std::endl;
return adjustedThreads;
}
// Signal handler for segmentation faults
void signal_handler(int signum) {
std::cerr << "Caught signal " << signum << ": Segmentation fault (core dump)" << std::endl;
// Handle the error (e.g., clean up, abort, etc.)
exit(signum); // Exit program with the signal code
}
int main(int argc, char *argv[]) {
if (argc < 4) {
std::cout
<< "Usage is " << argv[0]
<< " filestxt outdir [pedfile (h5)] "
" optional: [int dataset path; 0 means Furka, 1 means XFEL; overwrites default given in HDF5File.h] "
" [bool validate h5 rank] "
" [xmin xmax ymin ymax] [nframes] "
" NOTE THAT THE DATA FILES HAVE TO BE IN THE RIGHT ORDER SO THAT PEDESTAL TRACKING WORKS! "
<< std::endl;
return 1;
}
// Set up the signal handler for segmentation faults
signal(SIGSEGV, signal_handler);
int const fifosize = 100; //1000;
int const nthreads = 10;
int const csize = 3; // 3
int const nsigma = 5;
int const nped = 10000;
//int cf = 0;
std::string const txtfilename(argv[1]);
std::string const outdir(argv[2]);
std::string const pedfilename(argv[3]);
std::string datasetpath{};
std::string frameindexpath{};
if (argc > 4) {
switch (atoi(argv[4])) {
case 0:
datasetpath = data_datasetname_furka;
frameindexpath = index_datasetname_furka;
break;
case 1:
datasetpath = data_datasetname_xfelSCS;
frameindexpath = index_datasetname_xfelSCS;
break;
default:
break;
}
}
bool validate_rank=true;
if (argc > 5)
validate_rank = atoi(argv[5]);
//Get vector of filenames from input txt-file
std::vector<std::string> filenames{};
{ //Safety scope for ifstream
ifstream inputs( txtfilename, std::ios::in );
if (inputs.is_open()) {
std::cout << "Reading imput filenames from txt-file ..." << std::endl;
std::string line{};
while (!inputs.eof()) {
std::getline(inputs, line);
if(line.find(".h5") != std::string::npos) {
filenames.emplace_back(line);
std::cout << line << std::endl;
}
}
inputs.close();
std::cout << "---- Reached end of txt-file. ----" << std::endl;
} else
std::cout << "Could not open " << txtfilename << std::endl;
if (filenames.size()>0) {
std::cout << filenames.size() << " filenames found in " << txtfilename << std::endl;
std::cout << "The files will be processed in the same order as found in the txt-file." << std::endl;
} else {
std::cout << "No files found in txt-file!" << std::endl;
return 1;
}
}
std::cout << "###############" << std::endl;
// Define decoder
std::cout << "Jungfrau strixel quad h5" << std::endl;
jungfrauLGADStrixelsDataQuadH5* decoder = new jungfrauLGADStrixelsDataQuadH5();
//auto decoder = std::make_unique<jungfrauLGADStrixelsDataQuadH5>();
int nx = 1024 / 3, ny = 512 * 3;
//Cluster finder ROI
int xmin = 0, xmax = nx-1, ymin = 0, ymax = ny-1;
if (argc > 9) {
xmin = atoi(argv[6]);
xmax = atoi(argv[7]);
ymin = atoi(argv[8]);
ymax = atoi(argv[9]);
}
std::cout << "Cluster finder ROI: [" << xmin << ", " << xmax << ", " << ymin << ", " << ymax << "]"
<< std::endl;
decoder->getDetectorSize(nx, ny);
std::cout << "Detector size is " << nx << " " << ny << std::endl;
std::time_t end_time;
std::cout << "Output directory is " << outdir << std::endl;
if (pedfilename.length()!=0)
std::cout << "Pedestal file is " << pedfilename << std::endl;
//uint32_t nnx, nny;
singlePhotonDetector* filter =
new singlePhotonDetector(decoder, 3, nsigma, 1, NULL, nped, 200, -1, -1, NULL, NULL);
//auto filter = std::make_unique<singlePhotonDetector>(decoder.get(), 3, nsigma, 1, nullptr, nped, 200, -1, -1, nullptr, nullptr);
filter->setROI(xmin, xmax, ymin, ymax);
std::time(&end_time);
std::cout << std::ctime(&end_time) << std::endl;
// Validate number of threads for number of storage cells (if applicable)
int nThreads = nthreads;
int nSC = 1;
// Determine the dimensions of the dataset from the first datafile
auto firstfileh5 = std::make_unique<HDF5File>();
firstfileh5->SetFrameIndexPath(frameindexpath);
firstfileh5->SetImageDataPath(datasetpath);
//std::cout << "Debug: Attempting to open file " << filenames[0].c_str() << std::endl;
if ( firstfileh5->OpenResources(filenames[0].c_str(), validate_rank) ) {
// Validate number of threads
if( firstfileh5->GetRank() == 4 ) {
auto h5dims = firstfileh5->GetDatasetDimensions();
nSC = h5dims[1];
nThreads = adjustThreads(nthreads,nSC);
}
firstfileh5->CloseResources();
} else {
std::cerr << "Error: Could not open data file " << filenames[0]
<< " for validating rank " << std::endl;
}
multiThreadedCountingDetector* mt =
new multiThreadedCountingDetector(filter, nThreads, fifosize, nSC);
//auto mt = std::make_unique<multiThreadedCountingDetector>(filter.get(), nthreads, fifosize);
mt->setClusterSize(csize, csize);
mt->newDataSet(); //Initialize new dataset for each thread
mt->setDetectorMode(ePhotonCounting);
char* buff;
mt->StartThreads();
mt->popFree(buff); // Get the first pointer to write image to
size_t ifr = 0; //frame counter of while loop
int framenumber = 0; //framenumber as read from file (detector)
std::vector<hsize_t> h5offset; //hyperslab offset internal to HDF5File::ReadImage
hsize_t h5rank;
if (pedfilename.length()>1) {
std::string froot = getRootString(pedfilename);
std::cout << "PEDESTAL " << pedfilename << std::endl;
std::time(&end_time);
std::cout << "aaa " << std::ctime(&end_time) << std::endl;
mt->setFrameMode(ePedestal);
//HDF5File pedefile;
auto pedefile = std::make_unique<HDF5File>();
pedefile->SetFrameIndexPath(frameindexpath);
pedefile->SetImageDataPath(datasetpath);
// //open file
if ( pedefile->OpenResources(pedfilename.c_str(),validate_rank) ) {
// Initialize offset vector to 0
h5rank = pedefile->GetRank();
h5offset.resize(h5rank-2, 0);
framenumber = 0;
while ( decoder->readNextFrame(*pedefile, framenumber, h5offset, buff) ) {
if ((ifr + 1) % 100 == 0) {
std::cout
<< " ****"
<< decoder->getValue(buff, 20, 20); // << std::endl;
}
int storageCell = 0;
hsize_t n_storageCells = 1;
if (h5rank == 4) {
storageCell = h5offset[1];
n_storageCells = pedefile->GetDatasetDimensions()[1];
}
// push buff into fifoData for a thread corresponding to the active storage cell
mt->pushData(buff, storageCell);
// increment (round-robin) the internal thread counter for that storage cell
mt->nextThread(storageCell);
// get a free memory address from fifoFree of the active storage cell for the next read operation
mt->popFree(buff, storageCell);
/* NOTE: the buff that was popped free from the current thread, will be (likely) pushed into
* the fifoData of a different thread in the next iteration of the loop! */
++ifr;
if (ifr % 100 == 0) {
std::cout << " ****" << ifr << " " << framenumber << " " << h5offset[0];
if (n_storageCells>1)
std::cout << " sc " << storageCell;
std::cout << "\n";
} // else
if (ifr >= 1000*n_storageCells)
break;
//framenumber = 0;
}
pedefile->CloseResources();
while (mt->isBusy()) {
;
}
std::cout << "Writing pedestal to " << getRootString(pedfilename) << "_ped_SCxx.tiff" << std::endl;
auto imgfname = createFileName( outdir, getRootString(pedfilename), "ped", "" );
mt->writePedestal(imgfname.c_str());
std::cout << "Writing pedestal rms to " << getRootString(pedfilename) << "_rms_SCxx.tiff" << std::endl;
imgfname = createFileName( outdir, getRootString(pedfilename), "rms", "");
mt->writePedestalRMS(imgfname.c_str());
} else {
std::cerr << "Error: Could not open pedestal file " << pedfilename
<< " for reading " << std::endl;
}
std::time(&end_time);
std::cout << std::ctime(&end_time) << std::endl;
}
ifr = 0;
//int ioutfile = 0;
mt->setFrameMode(eFrame);
std::vector<FILE*> of(nSC, nullptr);
for (unsigned int ifile = 0; ifile != filenames.size(); ++ifile) {
std::cout << "DATA " << filenames[ifile] << " " << std::endl;
std::time(&end_time);
std::cout << std::ctime(&end_time) << std::endl;
//HDF5File fileh5;
auto fileh5 = std::make_unique<HDF5File>();
fileh5->SetFrameIndexPath(frameindexpath);
fileh5->SetImageDataPath(datasetpath);
// Open HDF5 file
if ( fileh5->OpenResources(filenames[ifile].c_str(), validate_rank) ) {
std::vector<std::string> cfnames(nSC);
for ( int s = 0; s < nSC; ++s ) {
std::string fsuffix = "SC" + std::to_string(s);
cfnames[s] = createFileName( outdir, getRootString(filenames[ifile]), fsuffix, "clust" );
}
//Open output files and set file pointers according to storage cells
for ( size_t f = 0; f < of.size(); ++f ) {
if (!of[f]) {
of[f] = fopen(cfnames[f].c_str(), "w");
if (of[f])
{
if (mt) {
mt->setFilePointer(of[f],f); // assumes f == sc
std::cout << "File pointer set for storage cell " << f << std::endl;
} else {
std::cerr << "Error: mt is null." << std::endl;
return 1;
}
} else {
std::cerr << "Error: could not open " << cfnames[f]
<< " for writing " << std::endl;
mt->setFilePointer(nullptr,f);
return 1;
}
}
}
// Read frames
framenumber = 0;
std::fill(h5offset.begin(), h5offset.end(), 0);
ifr = 0;
while ( decoder->readNextFrame(*fileh5, framenumber, h5offset, buff) ) {
if ((ifr + 1) % 1000 == 0) {
std::cout << " ****"
<< decoder->getValue(buff, 20, 20); // << std::endl;
}
int storageCell = 0;
hsize_t n_storageCells = 1;
if (h5rank == 4) {
storageCell = h5offset[1];
n_storageCells = fileh5->GetDatasetDimensions()[1];
}
// push buff into fifoData for a thread corresponding to the active storage cell
mt->pushData(buff, storageCell);
// increment (round-robin) the internal thread counter for that storage cell
mt->nextThread(storageCell);
// get a free memory address from fifoFree of the active storage cell for the next read operation
mt->popFree(buff, storageCell); /* In the last execution of the loop,
* this leaves buff outside of the Fifo!
* Free explicitely at the end! */
++ifr;
if (ifr % 1000 == 0) {
std::cout << " " << ifr << " " << framenumber << " " << h5offset[0];
if (n_storageCells>1) std::cout << " sc " << storageCell;
std::cout << "\n";
}
//framenumber = 0;
}
//std::cout << "aa --" << std::endl;
fileh5->CloseResources();
//std::cout << "bb --" << std::endl;
while (mt->isBusy()) {
;
}
//std::cout << "cc --" << std::endl;
auto imgfname = createFileName( outdir, getRootString(filenames[ifile]), "", "" );
std::cout << "Writing tiff to " << imgfname << "_SCxx.tiff" << std::endl;
mt->writeImage(imgfname.c_str());
mt->clearImage();
// Close output files
for ( size_t f = 0; f < of.size(); ++f ) {
if (of[f]) {
fclose(of[f]);
mt->setFilePointer(nullptr,f);
of[f] = nullptr;
}
}
std::time(&end_time);
std::cout << std::ctime(&end_time) << std::endl;
} else {
std::cerr << "Error: Could not open " << filenames[ifile] << " for reading "
<< std::endl;
}
}
//std::cout << "Calling delete..." << std::endl;
/* Info: Previously, 'delete mt' caused crash
(double calls of StopThread() in both destructors of
multiThreadedAnalogDetector and threadedAnalogDetector)
Now fixed! */
delete mt; // triggers cleanup of all threads and singlePhotonDetector instances (delete filter is obsolete)
delete decoder;
free(buff); // Free explicitly as it gets popped out of the Fifo at termination of while(readNextFrame)
return 0;
}

View File

@ -336,7 +336,7 @@ int main(int argc, char *argv[]) {
string fname;
// int length;
int *detimage = NULL;
int *detimage = nullptr;
int nnx, nny, nnsx, nnsy;
// uint32_t imageSize = 0, nPixelsX = 0, nPixelsY = 0,
// uint32_t dynamicRange = 0;

View File

@ -23,7 +23,9 @@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
//#include <mutex>
#include <mutex>
#include <algorithm>
#include<unordered_map>
using namespace std;
@ -47,12 +49,20 @@ class threadedAnalogDetector {
if (mm) {
// memset(mm,0, det->getDataSize());
/*
if (i == 0) { // debug
first_mm = mm;
}
*/
fifoFree->push(mm);
//std::cout << "Allocated memory at: " << static_cast<void*>(mm) << " (fifoslot " << i << ")" << std::endl;
} else
break;
}
if (i < fs)
cout << "Could allocate only " << i << " frames";
std::cout << "Could allocate only " << i << " frames";
busy = 0;
stop = 1;
@ -102,24 +112,50 @@ class threadedAnalogDetector {
};
virtual ~threadedAnalogDetector() {
// std::cout << "#### Debug: Destructing threadedAnalogDetector! ####" << std::endl;
StopThread();
delete fifoFree;
delete fifoData;
if (fifoFree) { delete fifoFree; fifoFree = nullptr; }
if (fifoData) { delete fifoData; fifoData = nullptr; }
if (det) {
delete det; // Call destructor for singlePhotonDetector
det = nullptr;
}
}
/** Returns true if the thread was successfully started, false if there was
* an error starting the thread */
virtual bool StartThread() {
stop = 0;
cout << "Detector number " << det->getId() << endl;
cout << "common mode is " << det->getCommonModeSubtraction() << endl;
cout << "ghos summation is " << det->getGhostSummation() << endl;
std::cout << "Detector number " << det->getId() << std::endl;
std::cout << "common mode is " << det->getCommonModeSubtraction() << std::endl;
std::cout << "ghos summation is " << det->getGhostSummation() << std::endl;
return (pthread_create(&_thread, NULL, processData, this) == 0);
}
virtual void StopThread() {
stop = 1;
(void)pthread_join(_thread, NULL);
//std::cout << "Attempting to stop thread..." << std::endl;
// Free all remaining allocated memory in fifoFree
char *mm = nullptr;
while (fifoFree->pop(mm, true)) { // Use no_block to avoid waiting
//std::cout << "fifo Free: Freeing memory at: " << static_cast<void*>(mm) << std::endl;
free(mm); // Free the allocated memory
}
if (_thread) {
//(void)pthread_join(_thread, NULL);
//std::cout << "Calling pthread_join for thread: " << det->getId() << std::endl;
pthread_join(_thread, NULL);
_thread = 0;
std::cout << "Thread " << det->getId() << " stopped and joined." << std::endl;
} else {
std::cout << "No thread to join." << std::endl;
}
}
virtual bool pushData(char *&ptr) { return fifoData->push(ptr); }
@ -266,6 +302,8 @@ class threadedAnalogDetector {
char *data;
int *ff;
//char* first_mm = nullptr; // For debug; to track first allocated block
static void *processData(void *ptr) {
threadedAnalogDetector *This = ((threadedAnalogDetector *)ptr);
return This->processData();
@ -278,55 +316,118 @@ class threadedAnalogDetector {
usleep(100);
if (fifoData->isEmpty()) {
busy = 0;
} else
} else {
busy = 1;
} else
}
} else {
busy = 1;
}
if (busy == 1) {
// Check stop flag before making a blocking call
//if (stop) {
// break;
//}
// Blocking call
fifoData->pop(data); // blocking!
// Process data if not stopping
//if (!stop) {
det->processData(data);
fifoFree->push(data);
//}
// busy=0;
}
}
return NULL;
}
};
class multiThreadedAnalogDetector {
public:
multiThreadedAnalogDetector(analogDetector<uint16_t> *d, int n,
int fs = 1000)
: stop(0), nThreads(n), ithread(0) {
multiThreadedAnalogDetector(analogDetector<uint16_t> *d, int num_threads,
int fs = 1000, int num_sc = 1)
: stop(0), nThreads(num_threads), nSC(num_sc) {
/*
dd[0] = d;
if (nThreads == 1)
dd[0]->setId(100);
else
dd[0]->setId(0);
for (int i = 1; i < nThreads; i++) {
dd[i] = d->Clone();
dd[i]->setId(i);
*/
// Create separate detectorObjects for each SC (each owns its mutex)
std::vector< analogDetector<uint16_t>* > sc_detectors(nSC, nullptr);
sc_detectors[0] = d; // First storage cell uses the given detector
// std::cout << "#### Debug: Copied analogDetector object for storage cell 0! ####" << std::endl;
for (int sc = 1; sc < nSC; ++sc) {
sc_detectors[sc] = d->Copy(); // Ensure unique mutex for each SC
// std::cout << "#### Debug: Copied analogDetector object for storage cell " << sc << "! ####" << std::endl;
}
// Distribute threads among storage cells
int threads_per_sc = nThreads / nSC;
int remaining_threads = nThreads % nSC; // additional safety measure if nThreads is not divisible by nSC
sc_to_threads.clear();
for (int s = 0, thread_idx = 0; s < nSC; ++s) {
// Remaining threads (if any) are assigned to the first storage cells
int current_sc_threads = threads_per_sc + (s < remaining_threads ? 1 : 0);
for (int t = 0; t < current_sc_threads; ++t, ++thread_idx) {
if (t == 0) {
dd[thread_idx] = sc_detectors[s]; // First thread gets main SC detector
} else {
dd[thread_idx] = sc_detectors[s]->Clone(); // Other threads get clones
}
std::cout << "Assigned thread " << thread_idx << " to storage cell " << s << std::endl;
dd[thread_idx]->setId(thread_idx);
// Store which threads belong to which SC
sc_to_threads[s].push_back(thread_idx);
}
}
if (nSC == 1 && nThreads == 1) {
dd[0]->setId(100);
}
// Initialize threadedAnalogDetector objects
for (int i = 0; i < nThreads; i++) {
cout << "**" << i << endl;
dets[i] = new threadedAnalogDetector(dd[i], fs);
}
image = NULL;
// Set all thread counters to zero for each storage cell
thread_counters_by_sc.resize(nSC,0);
image = nullptr;
ff = NULL;
ped = NULL;
cout << "Ithread is " << ithread << endl;
//std::cout << "Ithread is " << ithread << std::endl;
}
virtual ~multiThreadedAnalogDetector() {
StopThreads();
for (int i = 0; i < nThreads; i++)
delete dets[i];
/* for (int i=1; i<nThreads; i++) */
/* delete dd[i]; */
// delete [] image;
// std::cout << "#### Debug: Destructing multiThreadedAnalogDetector! ####" << std::endl;
//StopThreads(); // Superfluous, leads to double delete
/* Reverse loop for destruction.
* Deletes clones first, then root object, which owns the mutex
* (ensure shared mutex is deleted last).
* Optional solution: reference counting (safer but more complex) */
for (int i = nThreads - 1; i >= 0; --i) {
delete dets[i]; //StopThread() called by each ~threadedAnalogDetector()
dets[i] = nullptr;
}
}
virtual int setFrameMode(int fm) {
@ -359,36 +460,52 @@ class multiThreadedAnalogDetector {
dets[i]->newDataSet();
};
virtual int *getImage(int &nnx, int &nny, int &ns, int &nsy) {
int *img;
// Storage cell sensitive
virtual int *getImage(int &nnx, int &nny, int &ns, int &nsy, int sc = 0) {
//int *img;
// int nnx, nny, ns;
// int nnx, nny, ns;
int nn = dets[0]->getImageSize(nnx, nny, ns, nsy);
if (image) {
delete[] image;
image = NULL;
if (sc_images[sc]) {
delete[] sc_images[sc];
sc_images[sc] = nullptr;
}
image = new int[nn];
// Allocate memory for image and zero-initialize
sc_images[sc] = new int[nn]();
// int nn=dets[0]->getImageSize(nnx, nny, ns);
// for (i=0; i<nn; i++) image[i]=0;
for (int ii = 0; ii < nThreads; ii++) {
// cout << ii << " " << nn << " " << nnx << " " << nny << " " << ns
// << endl;
img = dets[ii]->getImage();
// Get the threads assigned to this storage cell
auto const& assigned_threads = sc_to_threads[sc];
// Only iterate over threads assigned to this storage cell
for (int thread_id : assigned_threads) {
int* tmp_img = dets[thread_id]->getImage();
if (!tmp_img) continue; // Skip if null
/* std::cout << "## Thread " << ii
<< " # image size " << nn
<< " # nnx " << nnx
<< " # nny " << nny
<< " # ns " << ns; */
// Sum images across threads
for (int i = 0; i < nn; i++) {
if (ii == 0)
// if (img[i]>0)
image[i] = img[i];
// else
// image[i]=0;
else // if (img[i]>0)
image[i] += img[i];
/* std::cout << " # pixel " << i
<< " # value " << tmp_img[i]
<< " ## " << std::endl; */
sc_images[sc][i] += tmp_img[i];
// if (img[i]) cout << "det " << ii << " pix " << i << " val
// " << img[i] << " " << image[i] << endl;
}
}
return image;
return sc_images[sc];
}
virtual void clearImage() {
@ -398,7 +515,7 @@ class multiThreadedAnalogDetector {
}
}
virtual void *writeImage(const char *imgname, double t = 1) {
virtual void *writeImage(char const* base_imgname, double t = 1) {
/* #ifdef SAVE_ALL */
/* for (int ii=0; ii<nThreads; ii++) { */
/* char tit[10000];cout << "m" <<endl; */
@ -407,11 +524,30 @@ class multiThreadedAnalogDetector {
/* } */
/* #endif */
int nnx, nny, ns, nsy;
getImage(nnx, nny, ns, nsy);
// int nnx, nny, ns;
int nn = dets[0]->getImageSize(nnx, nny, ns, nsy);
float *gm = new float[nn];
if (gm) {
// Allocate teporary float buffer and zero-initialize
std::vector<float> gm(nn);
// Lambda for pixel conversion
auto convert_pixel = [t](int pixel) -> float {
return (t > 0) ? static_cast<float>(std::max(0, pixel)) / static_cast<float>(t) : pixel;
}; // t ... threshold
// Loop over each storage cell
for (auto const& [sc, _] : sc_to_threads) { // structured bindings [sc, _] only available with -std=c++17
std::string imgname(base_imgname);
if (nSC > 1) imgname += "_SC" + std::to_string(sc);
imgname += ".tiff";
//Retrieve the image for this storage cell
int *image = getImage(nnx, nny, ns, nsy, sc);
if (!image) continue; // Skip if null
// Convert image data to float
std::transform(image, image + nn, gm.begin(), convert_pixel);
/* old loop implementing same logic as convert_pixel
for (int ix = 0; ix < nn; ix++) {
if (t) {
if (image[ix] < 0)
@ -424,11 +560,18 @@ class multiThreadedAnalogDetector {
// if (image[ix]>0 && ix/nnx<350) cout << ix/nnx << " " <<
// ix%nnx << " " << image[ix]<< " " << gm[ix] << endl;
}
// cout << "image " << nnx << " " << nny << endl;
WriteToTiff(gm, imgname, nnx, nny);
delete[] gm;
} else
cout << "Could not allocate float image " << endl;
*/
WriteToTiff(gm.data(), imgname.c_str(), nnx, nny);
// Clean up memory for this storage cell
if (sc_images[sc]) {
delete[] sc_images[sc];
sc_images[sc] = nullptr;
}
}
return NULL;
}
@ -439,6 +582,7 @@ class multiThreadedAnalogDetector {
}
virtual void StopThreads() {
std::cout << "Stopping all threads ..." << std::endl;
for (int i = 0; i < nThreads; i++)
dets[i]->StopThread();
}
@ -453,76 +597,133 @@ class multiThreadedAnalogDetector {
return ret;
}
virtual bool pushData(char *&ptr) { return dets[ithread]->pushData(ptr); }
/*
virtual std::vector<int> getThreadsForSc(int sc) {
return sc_to_threads[sc];
}
*/
virtual bool pushData(char *&ptr, int sc=0) {
//Additional logic implemented to accommodate storage cells
virtual bool popFree(char *&ptr) {
// cout << ithread << endl;
return dets[ithread]->popFree(ptr);
std::unique_lock<std::mutex> lock(map_mutex);
// Get assigned threads for this storage cell
auto& assigned_threads = sc_to_threads[sc];
auto& counter = thread_counters_by_sc[sc];
// Distribute workload among threads using round-robin
int selected_thread = assigned_threads[counter % assigned_threads.size()];
return dets[selected_thread]->pushData(ptr);
}
virtual int nextThread() {
ithread++;
if (ithread == nThreads)
ithread = 0;
return ithread;
virtual bool popFree(char *&ptr, int sc=0) {
//Additional logic implemented to accommodate storage cells
std::unique_lock<std::mutex> lock(map_mutex);
// Get assigned threads for this storage cell
auto& assigned_threads = sc_to_threads[sc];
auto& counter = thread_counters_by_sc[sc];
// Distribute workload among threads using round-robin
int selected_thread = assigned_threads[counter % assigned_threads.size()];
return dets[selected_thread]->popFree(ptr);
}
virtual double *getPedestal() {
virtual int nextThread(int sc=0) {
//Additional logic implemented to accommodate storage cells
auto& counter = thread_counters_by_sc[sc];
//counter++;
if (++counter == nThreads/nSC)
counter = 0;
return counter;
}
// Storage cell sensitive
virtual double *getPedestal(int sc = 0) {
int nx, ny;
dets[0]->getDetectorSize(nx, ny);
if (ped)
delete[] ped;
ped = new double[nx * ny];
if (sc_pedestals.count(sc) && sc_pedestals[sc]) {
delete[] sc_pedestals[sc];
sc_pedestals[sc] = nullptr;
}
// allocate memory and initialize all values to zero
sc_pedestals[sc] = new double[nx * ny](); // parentheses initialize elements to zero
//std::fill(sc_pedestals[sc], sc_pedestals[sc] + (nx * ny), 0.0); // explicit zero initialization
double *p0 = new double[nx * ny];
for (int i = 0; i < nThreads; i++) {
// Get the threads assigned to this storage cell
auto const& assigned_threads = sc_to_threads[sc];
int num_threads = assigned_threads.size();
// Only iterate over threads assigned to this storage cell
for ( int thread_id : assigned_threads ) {
// inte=(slsInterpolation*)dets[i]->getInterpolation(nb,emi,ema);
// cout << i << endl;
p0 = dets[i]->getPedestal(p0);
if (p0) {
if (i == 0) {
//p0 = dets[thread_id]->getPedestal(p0);
dets[thread_id]->getPedestal(p0);
if (p0) { /*
if (i == 0) {
// If first thread, initialize ped with first thread's values
for (int ib = 0; ib < nx * ny; ib++) {
ped[ib] = p0[ib] / ((double)nThreads);
// cout << p0[ib] << " ";
}
} else {
for (int ib = 0; ib < nx * ny; ib++) {
ped[ib] += p0[ib] / ((double)nThreads);
// cout << p0[ib] << " ";
}
*/
// For subsequent threads, accumulate pedestal values
// if ( i == 0 ) becomes superfluous if we zero-initialize earlier
for (int ib = 0; ib < nx * ny; ib++) {
sc_pedestals[sc][ib] += p0[ib] / ((double)num_threads);
// cout << p0[ib] << " ";
}
//}
}
}
delete[] p0;
return ped;
return sc_pedestals[sc];
};
virtual double *getPedestalRMS() {
// Storage cell sensitive
virtual double *getPedestalRMS(int sc = 0) {
int nx, ny;
dets[0]->getDetectorSize(nx, ny);
// if (ped) delete [] ped;
double *rms = new double[nx * ny];
if (sc_pedestals_rms.count(sc) && sc_pedestals_rms[sc]) {
delete[] sc_pedestals_rms[sc];
sc_pedestals_rms[sc] = nullptr;
}
// allocate memory and initialize all values to zero
sc_pedestals_rms[sc] = new double[nx * ny](); // Zero-initialize
//std::fill(sc_pedestals_rms[sc], sc_pedestals_rms[sc] + (nx * ny), 0.0); // explicit zero initialization
//double *rms = sc_pedestals_rms[sc];
double *p0 = new double[nx * ny];
for (int i = 0; i < nThreads; i++) {
// Get the threads assigned to this storage cell
auto const& assigned_threads = sc_to_threads[sc];
int num_threads = assigned_threads.size();
// Only iterate over threads assigned to this storage cell
for (int thread_id : assigned_threads) {
// inte=(slsInterpolation*)dets[i]->getInterpolation(nb,emi,ema);
// cout << i << endl;
p0 = dets[i]->getPedestalRMS(p0);
if (p0) {
if (i == 0) {
//p0 = dets[thread_id]->getPedestalRMS(p0);
dets[thread_id]->getPedestalRMS(p0);
for (int ib = 0; ib < nx * ny; ib++) {
rms[ib] = p0[ib] * p0[ib] / ((double)nThreads);
if (p0) {
for (int ib = 0; ib < nx * ny; ib++) {
sc_pedestals_rms[sc][ib] += (p0[ib] * p0[ib]) / ((double)num_threads);
// cout << p0[ib] << " ";
}
} else {
for (int ib = 0; ib < nx * ny; ib++) {
rms[ib] += p0[ib] * p0[ib] / ((double)nThreads);
// cout << p0[ib] << " ";
}
}
}
}
delete[] p0;
@ -533,64 +734,117 @@ class multiThreadedAnalogDetector {
/* rms[ib]=0; */
/* } */
return rms;
return sc_pedestals_rms[sc];
};
virtual double *setPedestal(double *h = NULL) {
/**
* Sets pedestal for given storage cell
* \param h pedestal
* \param sc storage cell
* \returns NULL
*/
virtual double *setPedestal(double *h = NULL, int sc = 0) {
// int nb=0;
int nx, ny;
dets[0]->getDetectorSize(nx, ny);
if (h == NULL)
h = ped;
for (int i = 0; i < nThreads; i++) {
for (auto i : sc_to_threads[sc]) {
dets[i]->setPedestal(h);
}
return NULL;
};
virtual void *writePedestal(const char *imgname) {
// Storage cell sensitive
virtual void *writePedestal(char const* base_imgname) {
int nx, ny;
dets[0]->getDetectorSize(nx, ny);
getPedestal();
float *gm = new float[nx * ny];
if (gm) {
//float *gm = new float[nx * ny];
// Loop over each storage cell
for ( auto const& entry : sc_to_threads ) {
int sc = entry.first;
std::string imgname = std::string(base_imgname);
if (nSC>1)
imgname += "_SC" + std::to_string(sc);
imgname += ".tiff";
getPedestal(sc); // Compute pedestal for this storage cell
std::vector<float> gm(nx * ny);
// Copy pedestal data into the float array
/*
for (int ix = 0; ix < nx * ny; ix++) {
gm[ix] = ped[ix];
gm[ix] = sc_pedestals[sc][ix];
}
WriteToTiff(gm, imgname, nx, ny);
delete[] gm;
} else
cout << "Could not allocate float image " << endl;
*/
std::copy(sc_pedestals[sc], sc_pedestals[sc] + (nx * ny), gm.data());
WriteToTiff(gm.data(), imgname.c_str(), nx, ny);
// Clean up memory
if(sc_pedestals[sc]) {
delete[] sc_pedestals[sc];
sc_pedestals[sc] = nullptr;
}
}
return NULL;
};
virtual void *writePedestalRMS(const char *imgname) {
// Storage cell sensitive
virtual void *writePedestalRMS(char const* base_imgname) {
int nx, ny;
dets[0]->getDetectorSize(nx, ny);
double *rms = getPedestalRMS();
float *gm = new float[nx * ny];
if (gm) {
//float *gm = new float[nx * ny];
// Loop over each stoarge cell
for ( auto const& entry : sc_to_threads ) {
int sc = entry.first;
std::string imgname = std::string(base_imgname);
if (nSC>1)
imgname += "_SC" + std::to_string(sc);
imgname += ".tiff";
double *rms = getPedestalRMS(sc); // Compute pedestal RMS for this storage cell
std::vector<float> gm(nx * ny);
// Copy rms data into the float array
/*
for (int ix = 0; ix < nx * ny; ix++) {
gm[ix] = rms[ix];
gm[ix] = rms[ix];
}
WriteToTiff(gm, imgname, nx, ny);
delete[] gm;
delete[] rms;
} else
cout << "Could not allocate float image " << endl;
*/
std::copy(rms, rms + (nx * ny), gm.data());
WriteToTiff(gm.data(), imgname.c_str(), nx, ny);
// Clean up memory
//delete[] rms; //This would cause double-free since the pointer is already handled by sc_pedestals_rms[sc]
if(sc_pedestals_rms[sc]) {
delete[] sc_pedestals_rms[sc];
sc_pedestals_rms[sc] = nullptr;
}
}
return NULL;
};
/**
* Reads pedestal and sets it for given storage cell
* \param imgname name of pedestal file
* \param nb obsolete
* \param emin obsolete
* \param emax obsolete
* \param sc storage cell
* \returns setPedestal(NULL, sc)
* */
virtual void *readPedestal(const char *imgname, int nb = -1,
double emin = 1, double emax = 0) {
double emin = 1, double emax = 0, int sc = 0) {
int nx, ny;
dets[0]->getDetectorSize(nx, ny);
@ -610,36 +864,68 @@ class multiThreadedAnalogDetector {
}
delete[] gm;
return setPedestal();
return setPedestal(NULL, sc);
};
/** sets file pointer where to write the clusters to
/** Sets file pointer where to write the clusters to
\param f file pointer
\returns current file pointer
\param sc storage cell index
\returns current file pointer, or nullptr if invalid
*/
virtual FILE *setFilePointer(FILE *f) {
for (int i = 0; i < nThreads; i++) {
dets[i]->setFilePointer(f);
// dets[i]->setMutex(&fmutex);
virtual FILE *setFilePointer(FILE *f, int sc = 0) {
// Check if the storage cell exists
if (sc_to_threads.find(sc) == sc_to_threads.end() || sc_to_threads[sc].empty()) {
std::cerr << "Error: Invalid storage cell index " << sc << std::endl;
return nullptr;
}
return dets[0]->getFilePointer();
// Assign file pointer to all threads belonging to this storage cell
for (auto i : sc_to_threads[sc]) {
if(dets[i]) {
dets[i]->setFilePointer(f);
// dets[i]->setMutex(&fmutex);
} else {
std::cerr << "Warning: dets[" << i << "] is null, skipping file pointer set." << std::endl;
}
}
// Return file pointer of the first thread in this storage cell
return dets[sc_to_threads[sc][0]] ? dets[sc_to_threads[sc][0]]->getFilePointer() : nullptr;
};
/** gets file pointer where to write the clusters to
\returns current file pointer
/** Gets file pointer where to write the clusters to
\param sc storage cell index
\returns current file pointer, or nullptr if invalid
*/
virtual FILE *getFilePointer() { return dets[0]->getFilePointer(); };
virtual FILE *getFilePointer(int sc = 0) {
// Ensure storage cell index is valid
if (sc_to_threads.find(sc) == sc_to_threads.end() || sc_to_threads[sc].empty()) {
std::cerr << "Error: Invalid storage cell index " << sc << std::endl;
return nullptr;
}
// Return file pointer of the first thread in this storage cell
return dets[sc_to_threads[sc][0]] ? dets[sc_to_threads[sc][0]]->getFilePointer() : nullptr;
};
protected:
bool stop;
const int nThreads;
threadedAnalogDetector *dets[MAXTHREADS];
analogDetector<uint16_t> *dd[MAXTHREADS];
int ithread;
int *image;
int *ff;
double *ped;
pthread_mutex_t fmutex;
//int ithread{0}; // Thread index
std::vector<int> thread_counters_by_sc{}; // Counters for threads for each storage cell
int* image;
int* ff;
double* ped;
//pthread_mutex_t fmutex; //unused
std::unordered_map<int,std::vector<int>> sc_to_threads; // Maps storage cell -> vector of assigned thread ids
std::mutex map_mutex; // Ensure thread-safe access to the map
int nSC{1}; // Number of storage cells
std::unordered_map<int,int*> sc_images; // Store images per storage cell
std::unordered_map<int,double*> sc_pedestals; // Store pedestal arrays per storage cell
std::unordered_map<int,double*> sc_pedestals_rms; // Store pedestal RMS arrays per storage cell
// at the moment, these maps could be avoided, but this implementation is more robust in allowing future changes
};
#endif

View File

@ -19,8 +19,8 @@ using namespace std;
class multiThreadedCountingDetector : public multiThreadedAnalogDetector {
public:
multiThreadedCountingDetector(singlePhotonDetector *d, int n, int fs = 1000)
: multiThreadedAnalogDetector(d, n, fs){};
multiThreadedCountingDetector(singlePhotonDetector *d, int num_threads, int fs = 1000, int num_sc = 1)
: multiThreadedAnalogDetector(d, num_threads, fs, num_sc){};
// virtual
// ~multiThreadedCountingDetector{multiThreadedAnalogDetector::~multiThreadedAnalogDetector();};
virtual double setNSigma(double n) {

View File

@ -70,7 +70,6 @@ class multiThreadedInterpolatingDetector
if (getInterpolation() == NULL)
return multiThreadedAnalogDetector::getImage(nnx, nny, nsx, nsy);
// if one interpolates, the whole image is stored in detector 0;
int *img;
// int nnx, nny, ns;
// int nnx, nny, ns;
int nn = dets[0]->getImageSize(nnx, nny, nsx, nsy);
@ -79,9 +78,9 @@ class multiThreadedInterpolatingDetector
image = NULL;
}
image = new int[nn];
img = dets[0]->getImage();
int* tmp_img = dets[0]->getImage();
for (int i = 0; i < nn; i++) {
image[i] = img[i];
image[i] = tmp_img[i];
}
return image;
};

View File

@ -51,7 +51,6 @@ class singlePhotonDetector : public analogDetector<uint16_t> {
*/
singlePhotonDetector(slsDetectorData<uint16_t> *d, int csize = 3,
double nsigma = 5, int sign = 1,
commonModeSubtraction *cm = NULL, int nped = 1000,
@ -60,10 +59,11 @@ class singlePhotonDetector : public analogDetector<uint16_t> {
: analogDetector<uint16_t>(d, sign, cm, nped, nnx, nny, gm, gs),
nDark(nd), eventMask(NULL), nSigma(nsigma), eMin(-1), eMax(-1),
clusterSize(csize), clusterSizeY(csize), c2(1), c3(1), clusters(NULL),
quad(UNDEFINED_QUADRANT), tot(0), quadTot(0) {
quad(UNDEFINED_QUADRANT), tot(0), quadTot(0), ownsMutex(true) { // The original object owns the mutex {
fm = new pthread_mutex_t;
pthread_mutex_init(fm, NULL);
//fm = new pthread_mutex_t;
//pthread_mutex_init(fm, NULL);
fm = new std::mutex();
eventMask = new eventType *[ny];
// val=new double*[ny];
@ -86,24 +86,31 @@ class singlePhotonDetector : public analogDetector<uint16_t> {
nphFrame = 0;
};
/**
destructor. Deletes the cluster structure, the pdestalSubtraction and the
image array
Destructor. Deletes the cluster structure, event mask, and destroys the mutex.
*/
virtual ~singlePhotonDetector() {
delete[] clusters;
for (int i = 0; i < ny; i++)
delete[] eventMask[i];
delete[] eventMask;
// std::cout << "#### Debug: Destructing singlePhotonDetector! ####" << std::endl;
if (clusters) { delete[] clusters; clusters = nullptr; }
for (int i = 0; i < ny; i++) {
if (eventMask[i]) { delete[] eventMask[i]; eventMask[i] = nullptr; }
}
if (eventMask) { delete[] eventMask; eventMask = nullptr; }
if (ownsMutex) {
if (fm) {
//pthread_mutex_destroy(fm); // Destroy the mutex (not necessary with std::mutex)
delete fm; // Free the memory allocated for the mutex
fm = nullptr; // Set the pointer to nullptr to avoid dangling pointer
}
}
};
/**
copy constructor
pointer-based copy constructor (cloner)
\param orig detector to be copied
*/
singlePhotonDetector(singlePhotonDetector *orig)
: analogDetector<uint16_t>(orig) {
: analogDetector<uint16_t>(orig), fm(orig->fm), ownsMutex(false) {
nDark = orig->nDark;
myFile = orig->myFile;
@ -126,11 +133,12 @@ class singlePhotonDetector : public analogDetector<uint16_t> {
c3 = sqrt(clusterSizeY * clusterSize);
clusters = new single_photon_hit[nx * ny];
//std::copy(orig->clusters, orig->clusters + (nx * ny), clusters); //possibly superfluous
// cluster=clusters;
setClusterSize(clusterSize);
fm = orig->fm;
//fm = orig->fm;
quad = UNDEFINED_QUADRANT;
tot = 0;
@ -143,13 +151,67 @@ class singlePhotonDetector : public analogDetector<uint16_t> {
}
/**
duplicates the detector structure
\returns new single photon detector with same parameters
* copy constructor (deep copy), creates a new mutex
* stricly, TODO: Implement Rule of Five!
* (copy op=, move ctor, and move op= would need to be defined)
*/
virtual singlePhotonDetector *Clone() {
singlePhotonDetector(singlePhotonDetector const& other)
: analogDetector<uint16_t>(other), ownsMutex(true) {
//fm = new pthread_mutex_t; // create a new mutex
//pthread_mutex_init(fm, NULL);
fm = new std::mutex(); // New unique mutex per copy
nDark = other.nDark;
myFile = other.myFile;
eventMask = new eventType *[ny];
for (int i = 0; i < ny; i++) {
eventMask[i] = new eventType[nx];
//std::copy(other.eventMask[i], other.eventMask[i] + nx, eventMask[i]);
}
eMin = other.eMin;
eMax = other.eMax;
nSigma = other.nSigma;
clusterSize = other.clusterSize;
clusterSizeY = other.clusterSizeY;
c2 = sqrt((clusterSizeY + 1) / 2 * (clusterSize + 1) / 2);
c3 = sqrt(clusterSizeY * clusterSize);
clusters = new single_photon_hit[nx * ny];
//std::copy(other.clusters, other.clusters + (nx * ny), clusters);
setClusterSize(clusterSize);
quad = other.quad;
tot = other.tot;
quadTot = other.quadTot;
gmap = other.gmap;
nphTot = other.nphTot;
nphFrame = other.nphFrame;
}
/**
Clones the detector structure
\returns new single photon detector with same parameters
that shares the mutex of the original
*/
virtual singlePhotonDetector* Clone() {
return new singlePhotonDetector(this);
}
/**
Copies the detector structure
\returns new single photon detector with same parameters
that owns a new mutex
*/
virtual singlePhotonDetector* Copy() {
return new singlePhotonDetector(*this); // Calls the copy constructor
}
/** sets/gets number of rms threshold to detect photons
\param n number of sigma to be set (0 or negative gets)
\returns actual number of sigma parameter
@ -381,7 +443,7 @@ class singlePhotonDetector : public analogDetector<uint16_t> {
// int ir, ic;
eventType ee;
double max = 0, tl = 0, tr = 0, bl = 0, br = 0, *v;
double max = 0, tl = 0, tr = 0, bl = 0, br = 0, v = 0;//, *v;
int cm = 0;
int good = 1;
int ir, ic;
@ -403,7 +465,8 @@ class singlePhotonDetector : public analogDetector<uint16_t> {
cm = 1;
}
double *val = new double[ny * nx];
//double *val = new double[ny * nx];
std::vector<double> val( ny * nx );
for (int iy = ymin; iy < ymax; ++iy) {
for (int ix = xmin; ix < xmax; ++ix) {
@ -435,26 +498,34 @@ class singlePhotonDetector : public analogDetector<uint16_t> {
(ix + ic) >= 0 && (ix + ic) < nx) {
if ((iy + ir) > iy && (ix + ic) > ix ) {
if ((iy + ir) > iy && (ix + ic) > ix ) {
val[(iy + ir) * nx + ix + ic] =
subtractPedestal(data, ix + ic, iy + ir, cm);
val[(iy + ir) * nx + ix + ic] =
subtractPedestal(data, ix + ic, iy + ir, cm);
}
v = &(val[(iy + ir) * nx + ix + ic]);
tot += *v;
if (ir <= 0 && ic <= 0)
bl += *v;
if (ir <= 0 && ic >= 0)
br += *v;
if (ir >= 0 && ic <= 0)
tl += *v;
if (ir >= 0 && ic >= 0)
tr += *v;
if (*v > max) //{
max = *v;
//}
}
//v = &(val[(iy + ir) * nx + ix + ic]);
v = val[(iy + ir) * nx + ix + ic];
//tot += *v;
tot += v;
if (ir <= 0 && ic <= 0)
bl += v;
//bl += *v;
if (ir <= 0 && ic >= 0)
br += v;
//br += *v;
if (ir >= 0 && ic <= 0)
tl += v;
//tl += *v;
if (ir >= 0 && ic >= 0)
tr += v;
//tr += *v;
//if (*v > max) //{
//max = *v;
if (v > max)
max = v;
//}
}
}
}
@ -503,6 +574,7 @@ class singlePhotonDetector : public analogDetector<uint16_t> {
}
#endif
if (ee == PHOTON && val[iy * nx + ix] == max) {
good = 1;
ee = PHOTON_MAX;
// cout << "**" <<id<< " " << iframe << " " << nDark << " "
// << ix << " " << iy << " " << rms << " " << max << " " <<
@ -523,20 +595,19 @@ class singlePhotonDetector : public analogDetector<uint16_t> {
ic < (clusterSize / 2) + 1; ic++) {
if ((iy + ir) >= 0 && (iy + ir) < ny &&
(ix + ic) >= 0 && (ix + ic) < nx) {
(clusters + nph)
(clusters + nph)
->set_data(val[(iy + ir) * nx + ix + ic],
ic, ir);
if (val[(iy + ir) * nx + ix + ic]>max)
good=0;
}
if (val[(iy + ir) * nx + ix + ic]>max)
good=0;
}
}
}
if (good==0) {
(clusters + nph)->print();
cout << max << " " << val[iy * nx + ix] << endl;
}
//else (clusters + nph)->print();
good = 1;
if (good==0) {
(clusters + nph)->print();
cout << max << " " << val[iy * nx + ix] << endl;
}
//else (clusters + nph)->print();
if (eMin > 0 && tot < eMin)
good = 0;
if (eMax > 0 && tot > eMax)
@ -561,7 +632,7 @@ class singlePhotonDetector : public analogDetector<uint16_t> {
// cout <<id << " **********************************"<< iframe << " " <<
// det->getFrameNumber(data) << " " << nphFrame << endl;
writeClusters(det->getFrameNumber(data));
delete[] val;
//delete[] val;
return image;
};
@ -667,13 +738,14 @@ class singlePhotonDetector : public analogDetector<uint16_t> {
void writeClusters(int fn) {
if (myFile) {
// cout << "++" << endl;
pthread_mutex_lock(fm);
//pthread_mutex_lock(fm); // This is dangerous! What if writeClusters() throws? Then the mutex is never unlocked!
std::lock_guard<std::mutex> lock(*fm); // safer, RAII-based locking
// cout <<"**********************************"<< fn << " " <<
// nphFrame << endl;
writeClusters(myFile, clusters, nphFrame, fn);
// for (int i=0; i<nphFrame; i++)
// (clusters+i)->write(myFile);
pthread_mutex_unlock(fm);
//pthread_mutex_unlock(fm); // unsafe
// cout << "--" << endl;
}
};
@ -711,7 +783,14 @@ class singlePhotonDetector : public analogDetector<uint16_t> {
ema = eMax;
};
void setMutex(pthread_mutex_t *m) { fm = m; };
//void setMutex(pthread_mutex_t *m) { fm = m; };
void setMutex(std::mutex* m) {
if (ownsMutex && fm) {
delete fm; // Cleanup old mutex
}
fm = m;
ownsMutex = false;
};
protected:
int nDark; /**< number of frames to be used at the beginning of the dataset
@ -733,7 +812,9 @@ class singlePhotonDetector : public analogDetector<uint16_t> {
int nphFrame;
// double **val;
pthread_mutex_t *fm;
//pthread_mutex_t* fm; // Pointer to the shared mutex
std::mutex* fm; // Pointer to the shared (or unique) mutex (safer version)
bool ownsMutex; // Flag to indicate ownership
};
#endif

Some files were not shown because too many files have changed in this diff Show More