This commit is contained in:
root
2025-08-12 11:36:07 +02:00
parent e680052592
commit 2520718f2c
61 changed files with 926 additions and 270 deletions

View File

@@ -16,9 +16,14 @@
"rotate": 0,
"roi": null,
"image_background": null,
"source_type": "bsread",
"source_type": "stddaq",
"name": "S10BC02-DSRM310",
"__source_type": "bsread",
"source": "S10BC02-DSRM310",
"url": "sf-daq-6.psi.ch:6379",
"_source": "tcp://sf-daq-6.psi.ch:30032",
"_source_type": "array10",
"mode": "SUB",
"group": [
"Electrons"
],
@@ -26,6 +31,7 @@
"connections": 2,
"_buffer_size": 40,
"threaded": true,
"debug": true,
"_forwarder_port": 9015
"debug": false,
"_forwarder_port": 9015,
"no_client_timeout": 0
}

View File

@@ -1,10 +1,10 @@
{
"camera_calibration": {
"reference_marker": [
642,
497,
644,
499
788,
450,
795,
459
],
"reference_marker_width": 16.256,
"reference_marker_height": 16.256,

View File

@@ -1,13 +1,13 @@
{
"camera_calibration": {
"reference_marker": [
1100,
1230,
1102,
1232
1207,
1207,
1209,
1209
],
"reference_marker_width": 20.0,
"reference_marker_height": 12.356435643564357,
"reference_marker_height": 20.0,
"angle_horizontal": 0.0,
"angle_vertical": 0.0
},
@@ -24,5 +24,7 @@
"Photonics",
"Bernina"
],
"alias": []
"alias": [
"THC_INLINE (SARES20-CAMS142-M2)"
]
}

View File

@@ -1,10 +1,10 @@
{
"camera_calibration": {
"reference_marker": [
1492,
705,
1494,
707
1496,
704,
1498,
706
],
"reference_marker_width": 19.333333333333332,
"reference_marker_height": 22.4,

View File

@@ -20,6 +20,7 @@
"name": "SARES20-PROF141-M1",
"protocol": "tcp",
"source": "SARES20-PROF141-M1",
"forwarder_port": 9013,
"_debug": true,
"alias": [
"PROF_KB (SARES20-PROF141-M1) (SARES20-PROF141-M1)"

View File

@@ -24,6 +24,7 @@
"Photonics",
"Bernina"
],
"forwarder_port": 9044,
"alias": [
"PROF_DSD (SARES20-PROF146-M1)(SARES20-PROF146-M1)"
]

View File

@@ -1,9 +1,9 @@
{
"camera_calibration": {
"reference_marker": [
955,
1195,
585,
1291,
1531,
1476
],
"reference_marker_width": 6000.0,

View File

@@ -1,13 +1,13 @@
{
"camera_calibration": {
"reference_marker": [
219,
281,
316,
382
177,
95,
419,
337
],
"reference_marker_width": 5000.0,
"reference_marker_height": 5000.0,
"reference_marker_width": 10000.0,
"reference_marker_height": 10000.0,
"angle_horizontal": 0.0,
"angle_vertical": 0.0
},

View File

@@ -1,13 +1,13 @@
{
"camera_calibration": {
"reference_marker": [
736,
100,
1828,
1175
904,
147,
1155,
411
],
"reference_marker_width": 5000.0,
"reference_marker_height": 5000.0,
"reference_marker_width": 1000.0,
"reference_marker_height": 1000.0,
"angle_horizontal": 0.0,
"angle_vertical": 0.0
},

View File

@@ -18,6 +18,7 @@
"image_background": null,
"source_type": "bsread",
"protocol": "tcp",
"forwarder_port": 9009,
"source": "SATES30-CAMS182-GIGE6",
"name": "SATES30-CAMS182-GIGE6",
"prefix": "SATES30-CAMS182-GIGE6",

View File

@@ -0,0 +1,13 @@
{
"camera_calibration": null,
"mirror_x": false,
"mirror_y": false,
"rotate": 0,
"roi": null,
"image_background": null,
"source_type": "bsread",
"name": "SATES31-CAMS187-PCO2",
"source": "SATES31-CAMS187-PCO2",
"prefix": "SATES31-CAMS187-PCO2",
"forwarder_port": 9013
}

View File

@@ -27,10 +27,10 @@
"hardware_configuration": {
"EXPOSURE": 1.0,
"HSSPEED": 0.0,
"REGIONX_START": 8.0,
"REGIONY_START": 810.0,
"REGIONX_END": 4104.0,
"REGIONY_END": 1200.0,
"REGIONX_START": 1420,
"REGIONY_START": 790.0,
"REGIONX_END": 3210,
"REGIONY_END": 1130,
"TRIGGER": 1,
"TRIGGERSOURCE": 0,
"DELAY": 0.0,

View File

@@ -12,7 +12,7 @@
"angle_vertical": 0.0
},
"mirror_x": false,
"mirror_y": false,
"mirror_y": true,
"rotate": 0,
"roi": null,
"image_background": null,

View File

@@ -17,7 +17,8 @@
"roi": null,
"image_background": null,
"source_type": "epics",
"source": "SATUN04-DSCR050:RF",
"_source": "SATUN04-DSCR050:RF",
"source": "SATUN04-DSCR050",
"name": "SATUN04-DSCR050",
"group": [
"Electrons"

View File

@@ -0,0 +1,27 @@
{
"camera_calibration": {
"reference_marker": [
1,
1,
2,
2
],
"reference_marker_width": 5.3,
"reference_marker_height": 5.3,
"angle_horizontal": 0.0,
"angle_vertical": 0.0
},
"mirror_x": false,
"mirror_y": false,
"rotate": 0,
"roi": null,
"image_background": null,
"source_type": "bsread",
"name": "SLAAR02-LPMO05-C301",
"source": "SLAAR02-LPMO05-C301",
"group": [
"Laser",
"Bernina"
],
"alias": []
}

View File

@@ -0,0 +1,27 @@
{
"camera_calibration": {
"reference_marker": [
122,
130,
133,
139
],
"reference_marker_width": 58.0,
"reference_marker_height": 48.4,
"angle_horizontal": 0.0,
"angle_vertical": 0.0
},
"mirror_x": false,
"mirror_y": false,
"rotate": 0,
"roi": null,
"image_background": null,
"source_type": "bsread",
"name": "SLAAR02-LPMO06-C302",
"source": "SLAAR02-LPMO06-C302",
"group": [
"Laser",
"Bernina"
],
"alias": []
}

View File

@@ -1,13 +1,13 @@
{
"camera_calibration": {
"reference_marker": [
98,
85,
109,
96
122,
130,
133,
139
],
"reference_marker_width": 58.0,
"reference_marker_height": 58.4,
"reference_marker_height": 48.4,
"angle_horizontal": 0.0,
"angle_vertical": 0.0
},

View File

@@ -1,5 +1,4 @@
{
"#S10BC02-DSRM310": "S10BC02-DSRM310",
"#SAROP21-PPRM133": "SAROP21-PPRM133",
"#SATES21-ADTEST1-CAM1": "SATES21-ADTEST1-CAM1",
"#SATES21-ADTEST1-CAM2": "SATES21-ADTEST1-CAM2",
@@ -7,6 +6,7 @@
"#SATES21-ADTEST1-CAM4": "SATES21-ADTEST1-CAM4",
"#SATES21-ADTEST1-CAM5": "SATES21-ADTEST1-CAM5",
"#SINBC02-DSRM310": "SINBC02-DSRM310",
"S10BC02-DSRM310": "S10BC02-DSRM310",
"S10BD01-DSCR030": "S10BD01-DSCR030",
"S10DI01-DSCR020": "S10DI01-DSCR020",
"SARCL01-DSCR170": "SARCL01-DSCR170",
@@ -43,6 +43,7 @@
"SATES24-CAMS161-M1": "SATES24-CAMS161-M1",
"SATES30-CAMS182-GIGE1": "SATES30-CAMS182-GIGE1",
"SATES30-CAMS182-GIGE2": "SATES30-CAMS182-GIGE2",
"SATES30-CAMS182-GIGE6": "SATES30-CAMS182-GIGE6",
"SATES30-RIXS-CAM01": "SATES30-RIXS-CAM01",
"SATES31-CAMS187-RIXS1": "SATES31-CAMS187-RIXS1",
"SATFE10-PSRD066": "SATFE10-PSRD066",
@@ -55,5 +56,7 @@
"SINDI02-DSCR075": "SINDI02-DSCR075",
"SLAAR02-LPMO01-C321": "SLAAR02-LPMO01-C321",
"SLAAR02-LPMO02-C322": "SLAAR02-LPMO02-C322",
"SLAAR02-LPMO05-C301": "SLAAR02-LPMO05-C301",
"SLAAR02-LPMO06-C302": "SLAAR02-LPMO06-C302",
"SLAAR12-LSPC-SPEC1": "SLAAR12-LSPC-SPEC1"
}

View File

@@ -44,6 +44,9 @@
"S10DI01-DSCR020",
"SATBD02-DSCR050",
"SARCL01-DSCR170",
"SARBD02-DSCR051",
"test_bschannel",
"test_stddaq",
"SARCL02-DSCR280"
]
},
@@ -104,6 +107,8 @@
"enabled": true,
"expanding": false,
"instances": [
"SLAAR02-LPMO05-C301",
"SLAAR02-LPMO06-C302",
"SARES20-CAMS142-M1",
"SARES20-CAMS142-M2",
"SARES20-CAMS142-M3",
@@ -135,7 +140,9 @@
"SATES30-CAMS182-GIGE3",
"SATES30-CAMS182-GIGE4",
"SATES30-CAMS182-GIGE5",
"SATES30-CAMS182-GIGE6",
"SATES30-RIXS-CAM01",
"SATES31-CAMS187-PCO2",
"furka_jungfrau"
]
},

View File

@@ -0,0 +1,10 @@
{
"name": "test_bschannel",
"mirror_x": false,
"mirror_y": false,
"rotate": 0,
"source_type": "bschannel",
"source": "tcp://sf-daq-6.psi.ch:30031",
"channel": "S10BC02-DSRM310:FPICTURE",
"mode": "SUB"
}

View File

@@ -0,0 +1,10 @@
{
"name": "test_stddaq",
"mirror_x": false,
"mirror_y": false,
"rotate": 0,
"source_type": "stddaq",
"source": "S10BC02-DSRM310",
"url": "sf-daq-6.psi.ch:6379",
"mode": "SUB"
}

View File

@@ -0,0 +1,20 @@
{
"image_background_enable": false,
"image_background": null,
"image_threshold": null,
"image_region_of_interest": null,
"image_good_region": {
"threshold": 0.5,
"gfscale": 3.0
},
"image_slices": null,
"pipeline_type": "processing",
"camera_name": "SLAAR02-LPMO05-C301",
"name": "AGalvotest1",
"function": "pprm_simple.py",
"mode": "PUSH",
"allow_type_changes": false,
"no_client_timeout": 0,
"port": "9015",
"reload": true
}

View File

@@ -0,0 +1,20 @@
{
"image_background_enable": false,
"image_background": null,
"image_threshold": null,
"image_region_of_interest": null,
"image_good_region": {
"threshold": 0.5,
"gfscale": 3.0
},
"image_slices": null,
"pipeline_type": "processing",
"camera_name": "SLAAR02-LPMO06-C302",
"name": "AGalvotest2",
"function": "pprm_simple.py",
"mode": "PUSH",
"allow_type_changes": false,
"no_client_timeout": 0,
"port": "9015",
"reload": true
}

View File

@@ -1,12 +1,12 @@
{
"image_background_enable": true,
"image_background_enable": false,
"image_background": "SARBD02-DSCR050_20250305_182528_541069",
"image_threshold": null,
"image_region_of_interest": [
1171,
448,
461,
1364
1012,
519,
446,
1492
],
"image_good_region": null,
"image_slices": null,

View File

@@ -1,6 +1,6 @@
{
"image_background_enable": "passive",
"image_background": "SARES11-SPEC125-M1_20250501_183259_735224",
"image_background": "SARES11-SPEC125-M1_20250619_162742_527140",
"image_threshold": null,
"image_region_of_interest": null,
"image_good_region": null,

View File

@@ -12,10 +12,10 @@
"mode": "PUSH",
"allow_type_changes": false,
"roi_signal": [
1000,
1200,
700,
1100,
1700
0,
2000
],
"roi_background": [
0,

View File

@@ -26,10 +26,10 @@
"down": "SARES21-PBPS141:Lnk9Ch0-PP_VAL_PD2",
"right": "SARES21-PBPS141:Lnk9Ch0-PP_VAL_PD3",
"left": "SARES21-PBPS141:Lnk9Ch0-PP_VAL_PD0",
"up_calib": 9.086696828684651e-06,
"down_calib": 8.479198152755808e-06,
"left_calib": 2.402225113860666e-05,
"right_calib": 1.253692752001019e-05,
"up_calib": 0.0034218450588557347,
"down_calib": 0.0032857555923560184,
"left_calib": 0.008527766407422569,
"right_calib": 0.005182206376186726,
"horiz_calib": -5.93887,
"vert_calib": -9.0057,
"uJ_calib": 941.943984588351,
@@ -94,5 +94,5 @@
0.11052246554408188,
0.11072063526778356
],
"calib_datetime": "2025-05-06 11:19:16"
"calib_datetime": "2025-07-18 08:38:03"
}

View File

@@ -26,12 +26,12 @@
"down": "SARFE10-PBPS053:Lnk9Ch0-PP_VAL_PD2",
"right": "SARFE10-PBPS053:Lnk9Ch0-PP_VAL_PD3",
"left": "SARFE10-PBPS053:Lnk9Ch0-PP_VAL_PD0",
"up_calib": 5.3603874230730805e-05,
"down_calib": 6.429273872665403e-05,
"left_calib": 0.0001873753953620842,
"right_calib": 0.00011135083709105292,
"horiz_calib": -3.5966302878741434,
"vert_calib": -5.704870102644,
"up_calib": 8.633479112505974e-05,
"down_calib": 0.0001042203401418397,
"left_calib": 0.0003061204498256338,
"right_calib": 0.00017606576126609594,
"horiz_calib": -3.393096838515036,
"vert_calib": -5.192750296774873,
"uJ_calib": 834.5191797495979,
"threshold": 0,
"queue_length": 5000,
@@ -69,9 +69,9 @@
0.3
],
"calib_x_norm": [
0.08373324791431114,
0.0002666207178174507,
-0.08308957010589782
0.08811721513032335,
-4.067443571301668e-05,
-0.08871239762620074
],
"calib_y_range": [
-0.3,
@@ -79,20 +79,20 @@
0.3
],
"calib_y_norm": [
0.05334340044159048,
6.423190047678002e-05,
-0.051829896970023125
0.05874380605733328,
2.452356570837876e-05,
-0.056801900111640324
],
"calib_time": "2022-11-28 16:19:37",
"calib_datetime": "2025-05-12 19:38:08",
"calib_datetime": "2025-06-30 21:22:50",
"calib_x_norm_std": [
0.03694393985351428,
0.036381119656154114,
0.037504981101666966
0.061973364995272784,
0.05926920770976579,
0.06478227269887708
],
"calib_y_norm_std": [
0.032515850998287014,
0.031406268858134787,
0.03357316145483791
0.06312747470291118,
0.058446505041557885,
0.061107151961064754
]
}

View File

@@ -8,7 +8,7 @@
"pipeline_type": "processing",
"camera_name": "SARFE10-PSSS059-LB",
"name": "SARFE10-PSSS059-LB_psss",
"function": "psss_2.py",
"function": "swissfel_spectral_processing.py",
"mode": "PUSH",
"allow_type_changes": false,
"no_client_timeout": 0,

View File

@@ -8,7 +8,7 @@
"pipeline_type": "processing",
"camera_name": "SARFE10-PSSS059",
"name": "SARFE10-PSSS059_psss",
"function": "swissfel_spectral_processing_fast.py",
"function": "swissfel_spectral_processing.py",
"mode": "PUSH",
"allow_type_changes": false,
"no_client_timeout": 0,

View File

@@ -26,12 +26,12 @@
"down": "SAROP11-PBPS110:Lnk9Ch0-PP_VAL_PD2",
"right": "SAROP11-PBPS110:Lnk9Ch0-PP_VAL_PD3",
"left": "SAROP11-PBPS110:Lnk9Ch0-PP_VAL_PD0",
"up_calib": 1.778951980962652e-05,
"down_calib": 1.647636031137421e-05,
"left_calib": 1.343967521143297e-05,
"right_calib": 1.4712267477026206e-05,
"horiz_calib": -4.084489404678124,
"vert_calib": -4.152037498266164,
"up_calib": 0.0001022569332245865,
"down_calib": 9.511294471959562e-05,
"left_calib": 8.063480882131907e-05,
"right_calib": 8.028682306967998e-05,
"horiz_calib": -4.224711312085033,
"vert_calib": -4.13819794609348,
"uJ_calib": 605.4608924473305,
"threshold": 0,
"queue_length": 1000,
@@ -69,9 +69,9 @@
0.3
],
"calib_x_norm": [
0.07355089424549768,
0.0005918260320667978,
-0.07334629180492924
0.07025056065580759,
5.415676993147808e-05,
-0.07177097778249114
],
"calib_y_range": [
-0.3,
@@ -79,19 +79,19 @@
0.3
],
"calib_y_norm": [
0.07059159363843578,
-0.0010492828771324209,
-0.07391577178168675
0.07125245801920767,
-7.646332998672576e-05,
-0.07373818955636538
],
"calib_datetime": "2025-05-08 19:40:32",
"calib_datetime": "2025-07-17 21:44:14",
"calib_x_norm_std": [
0.03951572779577543,
0.036895048152752644,
0.04082834764914548
0.04567411017192871,
0.04489322077121599,
0.045452729857613255
],
"calib_y_norm_std": [
0.042522854070151855,
0.04157478982211735,
0.03874863852211236
0.045872146420338974,
0.04796098280035745,
0.044361688908978424
]
}

View File

@@ -27,12 +27,12 @@
"down": "SAROP11-PBPS122:Lnk9Ch0-PP_VAL_PD2",
"right": "SAROP11-PBPS122:Lnk9Ch0-PP_VAL_PD3",
"left": "SAROP11-PBPS122:Lnk9Ch0-PP_VAL_PD0",
"up_calib": 3.136666045188568e-05,
"down_calib": 4.287710153537755e-05,
"left_calib": 1.9240676699217273e-05,
"right_calib": 1.9033978392323185e-05,
"horiz_calib": -4.800851116441316,
"vert_calib": -4.325389727023896,
"up_calib": 7.351388736143368e-05,
"down_calib": 0.00010575994016525625,
"left_calib": 6.948416622613705e-05,
"right_calib": 6.608864655210245e-05,
"horiz_calib": -4.356338212003961,
"vert_calib": -4.095658834474431,
"uJ_calib": 605.9512700123181,
"threshold": 0,
"queue_length": 1000,
@@ -70,14 +70,14 @@
0.3
],
"calib_x_norm": [
0.06260165947402832,
0.0005469889807531498,
-0.06237617996473284
0.06890356395488378,
-0.0004675885333441556,
-0.06882678910785368
],
"calib_x_norm_std": [
0.037489193261730905,
0.04049565430616364,
0.036369883937793064
0.0464095778335109,
0.04783392945726656,
0.044714614533193406
],
"calib_y_range": [
-0.3,
@@ -85,14 +85,14 @@
0.3
],
"calib_y_norm": [
0.0680948696951371,
-0.001074835754827735,
-0.07062095428052871
0.07370253499550317,
0.00026569111039769416,
-0.07279404205081079
],
"calib_y_norm_std": [
0.036781724002055614,
0.03612254480714764,
0.039495450086968996
0.042861938803805864,
0.046628964274299044,
0.044222723318716746
],
"calib_datetime": "2025-05-08 19:20:54"
"calib_datetime": "2025-07-17 21:43:10"
}

View File

@@ -29,12 +29,12 @@
"reload": true,
"dark_buffer_length": 3,
"calibration": [
-9.73024084e-19,
3.28988958e-16,
1.0206364e-12
-7.63784366e-19,
-2.36924701e-16,
1.4943272e-12
],
"roi": [
600,
800,
1600
],
"dpx_poly": 50,
@@ -46,6 +46,6 @@
"is_fel_dark": 0,
"event_code_laser_delayed": 25,
"event_code_laser_dark": 25,
"event_code_fel_dark": 201,
"event_code_fel_dark": 64,
"port": "9003"
}

View File

@@ -10,19 +10,19 @@
"check_timestamp": true,
"stream_timeout": 20,
"queue_length": 5000,
"down_calib": 1.3438944081736304e-05,
"down_calib": 1.5289933529766168e-06,
"xpos_odd_w_pvname": "SAROP21-PBPS103:XPOS-ODD-HIST-W",
"ypos_all_y_pvname": "SAROP21-PBPS103:YPOS-ALL-HIST-Y",
"ypos_all_w_pvname": "SAROP21-PBPS103:YPOS-ALL-HIST-W",
"name": "SAROP21-PBPS103_proc",
"vert_calib": -3.9358348895956135,
"vert_calib": -3.933091298181025,
"bsread_address": "",
"right": "SAROP21-PBPS103:Lnk9Ch0-PP_VAL_PD3",
"ypos_dif_w_pvname": "SAROP21-PBPS103:YPOS-DIF-HIST-W",
"ypos_odd_x_pvname": "SAROP21-PBPS103:YPOS-ODD-HIST-X",
"function": "pbps_full",
"port": "9009",
"left_calib": 8.747777364727171e-06,
"left_calib": 8.51985418426329e-07,
"down": "SAROP21-PBPS103:Lnk9Ch0-PP_VAL_PD2",
"ypos_odd_w_pvname": "SAROP21-PBPS103:YPOS-ODD-HIST-W",
"xpos_odd_y_pvname": "SAROP21-PBPS103:XPOS-ODD-HIST-Y",
@@ -32,7 +32,7 @@
"ypos_evn_x_pvname": "SAROP21-PBPS103:YPOS-EVN-HIST-X",
"uJ_calib": 605.9512700123181,
"xpos_evn_m_pvname": "SAROP21-PBPS103:XPOS-EVN-HIST-M",
"horiz_calib": -4.106805000082229,
"horiz_calib": -4.3381383543483505,
"ypos_all_m_pvname": "SAROP21-PBPS103:YPOS-ALL-HIST-M",
"ypos_dif_m_pvname": "SAROP21-PBPS103:YPOS-DIF-HIST-M",
"bsread_channels": [
@@ -45,7 +45,7 @@
"ypos_evn_w_pvname": "SAROP21-PBPS103:YPOS-EVN-HIST-W",
"pipeline_type": "stream",
"ypos_all_x_pvname": "SAROP21-PBPS103:YPOS-ALL-HIST-X",
"right_calib": 9.684568190671639e-06,
"right_calib": 9.523986071299896e-07,
"xpos_all_m_pvname": "SAROP21-PBPS103:XPOS-ALL-HIST-M",
"xpos_odd_m_pvname": "SAROP21-PBPS103:XPOS-ODD-HIST-M",
"left": "SAROP21-PBPS103:Lnk9Ch0-PP_VAL_PD0",
@@ -58,7 +58,7 @@
"ypos_evn_y_pvname": "SAROP21-PBPS103:YPOS-EVN-HIST-Y",
"xpos_odd_x_pvname": "SAROP21-PBPS103:XPOS-ODD-HIST-X",
"threshold": 0,
"up_calib": 1.2578887233395516e-05,
"up_calib": 1.4557228389427614e-06,
"ypos_odd_m_pvname": "SAROP21-PBPS103:YPOS-ODD-HIST-M",
"xpos_all_x_pvname": "SAROP21-PBPS103:XPOS-ALL-HIST-X",
"up": "SAROP21-PBPS103:Lnk9Ch0-PP_VAL_PD1",
@@ -69,9 +69,9 @@
0.3
],
"calib_x_norm": [
0.07264113800909158,
-0.00027682928694833323,
-0.07345783673842782
0.0688426139078096,
-0.00017495557137281405,
-0.06946556139475711
],
"calib_y_range": [
-0.3,
@@ -79,19 +79,19 @@
0.3
],
"calib_y_norm": [
0.07681455660145628,
-0.0005021565266583652,
-0.07563086268838766
0.08127191573286321,
0.000395220890146269,
-0.07127984432353003
],
"calib_x_norm_std": [
0.3239146627568919,
0.3360879764341496,
0.31769366304131247
0.05618368921948914,
0.031260276512354725,
0.03343277006478486
],
"calib_y_norm_std": [
0.3375259185485036,
0.3494180218450826,
0.34646009364333663
0.03983144338005543,
0.04203066731355165,
0.03945612564739388
],
"calib_datetime": "2025-05-12 18:46:13"
"calib_datetime": "2025-07-18 08:12:36"
}

View File

@@ -27,12 +27,12 @@
"down": "SAROP21-PBPS133:Lnk9Ch0-PP_VAL_PD2",
"right": "SAROP21-PBPS133:Lnk9Ch0-PP_VAL_PD3",
"left": "SAROP21-PBPS133:Lnk9Ch0-PP_VAL_PD0",
"up_calib": 1.1994417318403323e-05,
"down_calib": 1.1777156969811e-05,
"left_calib": 8.546152470880694e-06,
"right_calib": 8.292371780859814e-06,
"horiz_calib": -4.030808070907288,
"vert_calib": -3.9995027435812665,
"up_calib": 1.4747247514734858e-06,
"down_calib": 1.42535974283956e-06,
"left_calib": 9.034139608852398e-07,
"right_calib": 8.990738532348965e-07,
"horiz_calib": -4.318186025090137,
"vert_calib": -4.353176127757587,
"uJ_calib": 605.4608924473305,
"threshold": 0,
"queue_length": 3000,
@@ -70,14 +70,14 @@
0.3
],
"calib_x_norm": [
0.07558987766893042,
0.00010780904235330975,
-0.07326364982362192
0.07026520386341667,
-0.0001555796103222294,
-0.06868202919087854
],
"calib_x_norm_std": [
0.37640119241355796,
0.3766165188476263,
0.38834395117940335
0.03593882830801818,
0.031721088035632165,
0.03381114098823549
],
"calib_y_range": [
-0.3,
@@ -85,14 +85,14 @@
0.3
],
"calib_y_norm": [
0.07489741542673554,
0.0007133487847056664,
-0.07512123400735467
0.06929021697237708,
0.0005273168268902044,
-0.06854018142895585
],
"calib_y_norm_std": [
0.4039920352608262,
0.4026890943163395,
0.3902031448932646
0.04223293863234544,
0.04921758401755543,
0.04487262986719493
],
"calib_datetime": "2025-05-10 10:06:36"
"calib_datetime": "2025-07-18 08:13:56"
}

View File

@@ -26,12 +26,12 @@
"down": "SAROP31-PBPS113:Lnk9Ch0-PP_VAL_PD2",
"right": "SAROP31-PBPS113:Lnk9Ch0-PP_VAL_PD3",
"left": "SAROP31-PBPS113:Lnk9Ch0-PP_VAL_PD0",
"up_calib": 3.861793515770638e-05,
"down_calib": 4.4900118787754264e-05,
"left_calib": 9.126519109105711e-05,
"right_calib": 0.00011276977914264295,
"horiz_calib": -3.8084771908604007,
"vert_calib": -6.247648389472207,
"up_calib": 1.5555950635219156e-06,
"down_calib": 1.5863259817149662e-06,
"left_calib": 4.522479527187428e-06,
"right_calib": 5.042093412646135e-06,
"horiz_calib": -10.217744421858216,
"vert_calib": 64.64999936036992,
"uJ_calib": 941.943984588351,
"threshold": 0,
"queue_length": 300,
@@ -70,14 +70,14 @@
0.3
],
"calib_x_norm": [
0.0786096188018742,
0.00035821990918893194,
-0.0789336642299587
0.027476180760177122,
0.0007477943868970838,
-0.031245194058756053
],
"calib_x_norm_std": [
0.040490407392167534,
0.04028139405139821,
0.03862030068928889
0.548319590284712,
0.5516448585709409,
0.5458879023627853
],
"calib_y_range": [
-0.3,
@@ -85,14 +85,14 @@
0.3
],
"calib_y_norm": [
0.04775193765978428,
-0.00032249451531026196,
-0.04828419667374328
-0.012427589505407299,
-7.386532969491803e-05,
-0.0031468469541893235
],
"calib_y_norm_std": [
0.0376808182312105,
0.0399368891843527,
0.037962528852795725
0.5359666485603565,
0.5471586459810386,
0.5649269726881252
],
"calib_datetime": "2025-05-12 19:40:38"
"calib_datetime": "2025-07-11 13:29:23"
}

View File

@@ -27,12 +27,12 @@
"down": "SAROP31-PBPS149:Lnk9Ch0-PP_VAL_PD2",
"right": "SAROP31-PBPS149:Lnk9Ch0-PP_VAL_PD3",
"left": "SAROP31-PBPS149:Lnk9Ch0-PP_VAL_PD0",
"up_calib": 8.923922065175471e-06,
"down_calib": 9.805425067137745e-06,
"left_calib": 5.870948933687726e-06,
"right_calib": 8.378280574406873e-06,
"horiz_calib": -3.775717899566187,
"vert_calib": -3.8593992845314324,
"up_calib": 1.7257138767496805e-06,
"down_calib": 1.6633104167303162e-06,
"left_calib": 4.841112180846291e-06,
"right_calib": 5.88459407152098e-06,
"horiz_calib": -8.521995715357336,
"vert_calib": -19.803743284465746,
"uJ_calib": 605.9512700123181,
"threshold": 0.0,
"queue_length": 300,
@@ -71,14 +71,14 @@
0.3
],
"calib_x_norm": [
0.08010117214752004,
0.00011095255532506727,
-0.07880900492607362
0.034869462299519136,
5.445654136857332e-05,
-0.03553658107817827
],
"calib_x_norm_std": [
0.03546822052236209,
0.03566075428198375,
0.038689695811515164
0.5521538969221853,
0.5807997036780183,
0.560907907220158
],
"calib_y_range": [
-0.3,
@@ -86,14 +86,14 @@
0.3
],
"calib_y_norm": [
0.07814310330557633,
-0.0003988992296753892,
-0.07732150550150274
0.01521561218564969,
-5.73608923202534e-05,
-0.015081690267803441
],
"calib_y_norm_std": [
0.07331681331314215,
0.03660437215717908,
0.036695466427208896
0.5696086871998763,
0.5656565843542883,
0.5606055691837639
],
"calib_datetime": "2025-05-12 19:42:03"
"calib_datetime": "2025-07-11 13:30:43"
}

View File

@@ -2,7 +2,12 @@
"image_background_enable": false,
"image_background": "SATBD02-DSCR050_20250217_170917_808358",
"image_threshold": null,
"image_region_of_interest": null,
"image_region_of_interest": [
180,
2342,
81,
1919
],
"image_good_region": null,
"image_slices": null,
"pipeline_type": "processing",

View File

@@ -1,6 +1,6 @@
{
"image_background_enable": "passive",
"image_background": "SATES21-CAMS-PATT1_20250511_094953_959660",
"image_background": "SATES21-CAMS-PATT1_20250624_174643_619689",
"image_threshold": null,
"image_region_of_interest": null,
"image_good_region": null,
@@ -19,17 +19,11 @@
"port": "9003",
"project_axis": 0,
"reload": true,
"roi_background": [
9,
444,
10,
2501
],
"roi_signal": [
9,
444,
10,
2501
82,
414,
375,
2505
],
"enforce_pid": true,
"enforce_timestamp": true,

View File

@@ -1,12 +1,12 @@
{
"image_background_enable": "passive",
"image_background": "SATES21-CAMS154-M1_20250430_112650_821925",
"image_background": "SATES21-CAMS154-M1_20250708_105621_406300",
"image_threshold": null,
"image_region_of_interest": null,
"image_good_region": null,
"image_slices": null,
"pipeline_type": "processing",
"function": "maloja_spectrometer.py",
"function": "spectrometer.py",
"camera_name": "SATES21-CAMS154-M1",
"name": "SATES21-CAMS154-M1_spec_db",
"mode": "PUSH",
@@ -20,15 +20,9 @@
"threshold": 10,
"project_axis": 0,
"roi_signal": [
1,
847,
1,
1635
],
"roi_background": [
1,
847,
1,
1635
190,
761,
405,
1322
]
}

View File

@@ -0,0 +1,11 @@
{
"image_background_enable": false,
"image_background": null,
"image_threshold": null,
"image_region_of_interest": null,
"image_good_region": null,
"image_slices": null,
"pipeline_type": "processing",
"camera_name": "SATES22-ADTEST1-CAM4",
"name": "SATES22-ADTEST1-CAM4_sp"
}

View File

@@ -0,0 +1,11 @@
{
"image_background_enable": false,
"image_background": null,
"image_threshold": null,
"image_region_of_interest": null,
"image_good_region": null,
"image_slices": null,
"pipeline_type": "processing",
"camera_name": "SATES22-ADTEST1-CAM7",
"name": "SATES22-ADTEST1-CAM7_sp"
}

View File

@@ -1,17 +1,34 @@
{
"image_background_enable": false,
"image_background": null,
"image_background_enable": "passive",
"image_background": "SATES24-CAMS161-M1_20250712_122200_955534",
"image_threshold": null,
"image_region_of_interest": null,
"image_good_region": null,
"image_slices": null,
"pipeline_type": "processing",
"function": "Laser_Quick_Int.py",
"function": "spectrometer.py",
"camera_name": "SATES24-CAMS161-M1",
"name": "SATES24-CAMS161-M1_spec_db",
"mode": "PUSH",
"allow_type_changes": false,
"processing_threads": 4,
"thread_buffer_size": 30,
"reload": true
"reload": true,
"threshold": 10,
"enforce_pid": true,
"enforce_timestamp": true,
"check_timestamp": true,
"roi_background": [
40,
110,
5,
1648
],
"roi_signal": [
110,
180,
5,
1648
],
"project_axis": 0
}

View File

@@ -1,6 +1,6 @@
{
"image_background_enable": true,
"image_background": "SATES30-RIXS-CAM01_20250325_105110_535588",
"image_background": "SATES30-RIXS-CAM01_20250716_200949_694408",
"image_threshold": null,
"image_region_of_interest": null,
"image_good_region": null,

View File

@@ -0,0 +1,16 @@
{
"image_background_enable": false,
"image_background": null,
"image_threshold": null,
"image_region_of_interest": null,
"image_good_region": null,
"image_slices": null,
"pipeline_type": "processing",
"camera_name": "SATES31-CAMS187-PCO2",
"name": "SATES31-CAMS187-PCO2_proc",
"function": "profiles.py",
"mode": "PUSH",
"allow_type_changes": false,
"block": false,
"no_client_timeout": 0
}

View File

@@ -0,0 +1,12 @@
{
"image_background_enable": false,
"image_background": null,
"image_threshold": null,
"image_region_of_interest": null,
"image_good_region": null,
"image_slices": null,
"pipeline_type": "processing",
"camera_name": "SATES31-CAMS187-PCO2",
"name": "SATES31-CAMS187-PCO2_sp",
"max_frame_rate": 5.1
}

View File

@@ -1,6 +1,6 @@
{
"image_background_enable": true,
"image_background": "SATES31-CAMS187-RIXS1_20250513_091423_890325",
"image_background_enable": false,
"image_background": "SATES31-CAMS187-RIXS1_20250515_224135_992441",
"image_threshold": null,
"image_region_of_interest": null,
"image_good_region": null,

View File

@@ -46,9 +46,9 @@
"global_stage": "SLAAT31-LMOT-M816:MOT",
"feedback_output": "SLAAT01-LSCP-DRS1FNS:CH2:BSTART",
"feedback_buffer": 200,
"feedback_deadband": 50,
"feedback_target": 1450,
"feedback_step": 0.01
"feedback_deadband": 100,
"feedback_target": 1420,
"feedback_step": 0.02
},
"filter_window": 101,
"filter": true

View File

@@ -1,6 +1,6 @@
{
"image_background_enable": true,
"image_background": "SATOP31-PMOS132-2D_20250311_183617_282213",
"image_background": "SATOP31-PMOS132-2D_20250707_152837_645169",
"image_threshold": null,
"image_region_of_interest": null,
"image_good_region": null,

View File

@@ -1,6 +1,6 @@
{
"image_background_enable": false,
"image_background": "SATOP31-PSRD132_20250216_120304_546817",
"image_background_enable": true,
"image_background": "SATOP31-PSRD132_20250705_102222_768278",
"image_threshold": null,
"image_region_of_interest": null,
"image_good_region": null,

View File

@@ -0,0 +1,11 @@
{
"image_background_enable": false,
"image_background": null,
"image_threshold": null,
"image_region_of_interest": null,
"image_good_region": null,
"image_slices": null,
"pipeline_type": "processing",
"camera_name": "SATUN04-DSCR05",
"name": "SATUN04-DSCR05_sp"
}

View File

@@ -0,0 +1,11 @@
{
"image_background_enable": false,
"image_background": null,
"image_threshold": null,
"image_region_of_interest": null,
"image_good_region": null,
"image_slices": null,
"pipeline_type": "processing",
"camera_name": "SLAAR02-LPMO05-C301",
"name": "SLAAR02-LPMO05-C301_sp"
}

View File

@@ -0,0 +1,11 @@
{
"image_background_enable": false,
"image_background": null,
"image_threshold": null,
"image_region_of_interest": null,
"image_good_region": null,
"image_slices": null,
"pipeline_type": "processing",
"camera_name": "SLAAR02-LPMO06-C302",
"name": "SLAAR02-LPMO06-C302_sp"
}

View File

@@ -53,10 +53,13 @@
"#test_stream": "test_stream",
"#test_tadej": "test_tadej",
"#testdb_ib": "testdb_ib",
"AGalvotest1": "AGalvotest1",
"AGalvotest2": "AGalvotest2",
"Bernina_mid_IR_CEP_analysis": "Bernina_mid_IR_CEP_analysis",
"Bernina_mid_IR_CEP_populate_pvs": "Bernina_mid_IR_CEP_populate_pvs",
"Bernina_mid_IR_CEP_projection": "Bernina_mid_IR_CEP_projection",
"Bernina_tt_kb_populate_pvs": "Bernina_tt_kb_populate_pvs",
"S10BC02-DSRM310_sp": "S10BC02-DSRM310_sp",
"S10BD01-DSCR030_profiles": "S10BD01-DSCR030_profiles",
"SARBD01-DSCR110_sp1": "SARBD01-DSCR110_sp_rep",
"SARBD02-DSCR050_pref": "SARBD02-DSCR050_pref",
@@ -120,6 +123,7 @@
"SATES30-RIXS-CAM01_fit": "SATES30-RIXS-CAM01_fit",
"SATES30-RIXS-CAM01_proc": "SATES30-RIXS-CAM01_proc",
"SATES30-RIXS-CAM01_repeater": "SATES30-RIXS-CAM01_repeater",
"SATES31-CAMS187-PCO2_proc": "SATES31-CAMS187-PCO2_proc",
"SATES31-CAMS187-RIXS1_proc": "SATES31-CAMS187-RIXS1_proc",
"SATES31-CAMS187-RIXS1_proc_db_proxy": "SATES31-CAMS187-RIXS1_proc_db_proxy",
"SATES31-CAMS187-RIXS1_sp": "SATES31-CAMS187-RIXS1_sp",

View File

@@ -27,6 +27,8 @@
"enabled": true,
"expanding": false,
"instances": [
"AGalvotest1:9040",
"AGalvotest2:9041",
"SARFE10-PBPS053_proc:9004",
"SAROP21-ATT01_proc:9003",
"Bernina_tt_kb_populate_pvs:9030",
@@ -97,7 +99,10 @@
"SARBD01-DSCR050",
"SARBD02-DSCR050",
"SARBD02-DSCR051",
"S10DI01-DSCR020"
"S10DI01-DSCR020",
"S10BC02-DSRM310",
"test_bschannel",
"test_stddaq"
],
"enabled": true,
"expanding": false,
@@ -156,7 +161,8 @@
"SATES21-CAMS154-GIGE6",
"SATES21-CAMS154-GIGE7",
"SATES21-CAMS154-GIGE7b",
"SATES21-CAMS154-GIGE8"
"SATES21-CAMS154-GIGE8",
"SATOP21-PMOS127-1D"
],
"enabled": true,
"expanding": false,
@@ -281,6 +287,8 @@
"SATES30-CAMS182-GIGE5",
"SATES30-CAMS182-GIGE6",
"SATES30-RIXS-CAM01",
"SATOP31-PMOS132-1D",
"SATES31-CAMS187-PCO2",
"furka_jungfrau"
],
"enabled": true,
@@ -292,6 +300,7 @@
"SATES30-RIXS-CAM01_proc:9100",
"SATES30-RIXS-CAM01_repeater:9006",
"SATES30-RIXS-CAM01_fit:9010",
"SATES31-CAMS187-PCO2_proc:9012",
"test_load_file",
"SATES30-CAMS182-GIGE1_sp",
"SATES31-CAMS187-RIXS1_proc_db_proxy:9011",

View File

@@ -1,7 +1,7 @@
{
"image_background_enable": false,
"image_background": "simulation_20241114_103340_976351",
"image_threshold": 0.0,
"image_threshold": 20.0,
"image_region_of_interest": [
463,
342,
@@ -9,8 +9,8 @@
168
],
"image_good_region": {
"threshold": 0.5,
"gfscale": 6.0
"threshold": 0.05,
"gfscale": 2.0
},
"image_slices": null,
"pipeline_type": "processing",
@@ -18,7 +18,7 @@
"name": "simulation_sp",
"reload": true,
"_function": "bunch_length_op",
"fw_threshold": 0.1,
"fw_threshold": 0.2,
"no_client_timeout": 0,
"image_background_subtraction": true,
"slice_orientation": "horizontal",
@@ -28,5 +28,7 @@
"mode": "0.0",
"angle": 0.0,
"order": 1
}
},
"function": "bunch_length_op",
"averaging": 4
}

View File

@@ -0,0 +1,12 @@
{
"image_background_enable": false,
"image_background": null,
"image_threshold": null,
"image_region_of_interest": null,
"image_good_region": null,
"image_slices": null,
"pipeline_type": "processing",
"camera_name": "test_bschannel",
"name": "test_bschannel_sp",
"max_frame_rate": 5.1
}

View File

@@ -0,0 +1,12 @@
{
"image_background_enable": false,
"image_background": null,
"image_threshold": null,
"image_region_of_interest": null,
"image_good_region": null,
"image_slices": null,
"pipeline_type": "processing",
"camera_name": "test_stddaq",
"name": "test_stddaq_sp",
"max_frame_rate": 5.1
}

View File

@@ -42,9 +42,7 @@ def get_spectrum(image, background):
profile[j] += image[i, j] - background[i, j]
return profile
def update_PVs(buffer, *pv_names):
"""Continuously read from buffer and write to EPICS PVs."""
pvs = create_thread_pvs(list(pv_names))
while True:
time.sleep(0.1)
@@ -52,12 +50,13 @@ def update_PVs(buffer, *pv_names):
rec = buffer.popleft()
except IndexError:
continue
try:
for pv, val in zip(pvs, rec):
if pv and pv.connected and (val is not None):
for pv, val in zip(pvs, rec):
if pv and pv.connected and (val is not None):
try:
pv.put(val)
except Exception:
_logger.exception("Error updating channels")
except Exception as e:
_logger.error(f"Error writing to {pv.pvname}: {e}")
def initialize(params):
@@ -118,6 +117,16 @@ def initialize(params):
f"{camera}:AVG-SPECTRUM_Y",
f"{camera}:AVG-FIT-SPECTRUM_Y"
]
global ARRAY_PVS
ARRAY_PVS = set([
f"{camera}:SPECTRUM_Y",
f"{camera}:FIT-SPECTRUM_Y",
f"{camera}:AVG-SPECTRUM_Y",
f"{camera}:AVG-FIT-SPECTRUM_Y",
f"{camera}:FIT-SPECTRUM_Y-RAVG"
# add any others here
])
# All PVs (original + running average + N-shot average)
all_pv_names = base_pv_names + ravg_pv_names + avg_pv_names
@@ -126,6 +135,35 @@ def initialize(params):
thread = Thread(target=update_PVs, args=(buffer, *all_pv_names), daemon=True)
thread.start()
def _pv_safe(val, pvname):
if pvname in ARRAY_PVS:
# array PV: must always send a 1D numpy array
if isinstance(val, np.ndarray):
if val.ndim == 1:
return val.astype(np.float64)
elif val.ndim == 0:
# Scalar array, wrap as 1-element
return np.array([val.item()], dtype=np.float64)
else:
raise ValueError(f"{pvname}: expected 1D array, got shape {val.shape}")
elif isinstance(val, (list, tuple)):
return np.array(val, dtype=np.float64)
elif isinstance(val, (float, int, np.generic)):
return np.array([val], dtype=np.float64)
else:
raise TypeError(f"{pvname}: expected array, got {type(val)}")
else:
# scalar PV: must always send float
if isinstance(val, np.ndarray):
if val.size == 1:
return float(val.item())
else:
raise ValueError(f"{pvname}: expected scalar, got array of size {val.size}")
elif isinstance(val, (np.generic, float, int)):
return float(val)
else:
raise TypeError(f"{pvname}: expected scalar, got {type(val)}")
def process_image(image, pulse_id, timestamp, x_axis, y_axis, parameters, bsdata=None, background=None):
"""
@@ -180,6 +218,7 @@ def process_image(image, pulse_id, timestamp, x_axis, y_axis, parameters, bsdata
)
# Reconstruct fitted curve
fit_spectrum = offset + amp_fit * np.exp(-((axis - center)**2) / (2 * sigma**2))
#fit_spectrum = np.abs(fit_spectrum)
# Moments
sm_norm = smoothed / np.sum(smoothed)
@@ -198,7 +237,7 @@ def process_image(image, pulse_id, timestamp, x_axis, y_axis, parameters, bsdata
f"{camera}:FIT-FWHM": np.float64(2.355 * sigma),
f"{camera}:FIT-RMS": np.float64(sigma),
f"{camera}:FIT-RES": np.float64(2.355 * sigma / center * 1000),
f"{camera}:FIT-SPECTRUM_Y": fit_spectrum,
f"{camera}:FIT-SPECTRUM_Y": np.float64(fit_spectrum),
f"{camera}:SPECT-COM": spect_com,
f"{camera}:SPECT-RMS": spect_std,
f"{camera}:SPECT-SKEW": spect_skew,
@@ -218,6 +257,7 @@ def process_image(image, pulse_id, timestamp, x_axis, y_axis, parameters, bsdata
# N-shot average and average fitted spectrum
avg_buffer.append(spectrum)
avg_spectrum = np.mean(np.stack(avg_buffer), axis=0)
avg_spectrum = np.abs(avg_spectrum)
fit_avg = offset + amp_fit * np.exp(-((axis - center)**2) / (2 * sigma**2)) # using avg fit params below
sm_avg = scipy.signal.savgol_filter(avg_spectrum, 51, 3)
min_a, max_a = sm_avg.min(), sm_avg.max()
@@ -226,6 +266,8 @@ def process_image(image, pulse_id, timestamp, x_axis, y_axis, parameters, bsdata
sm_avg[::2], axis[::2], offset=min_a, amplitude=amp_a, skip=skip_a, maxfev=10
)
fit_avg_spectrum = offs_a + amp_fit_a * np.exp(-((axis - center_a)**2) / (2 * sigma_a**2))
fit_avg_spectrum = np.abs(fit_avg_spectrum)
# Average moments
sm_norm_a = sm_avg / np.sum(sm_avg)
spect_com_a = np.sum(axis * sm_norm_a)
@@ -236,11 +278,11 @@ def process_image(image, pulse_id, timestamp, x_axis, y_axis, parameters, bsdata
spect_res_a = spect_iqr_a / spect_com_a * 1000
avg_results = {
f"{camera}:AVG-FIT-COM": np.float64(center_a),
f"{camera}:AVG-FIT-COM": np.float64(center),
f"{camera}:AVG-FIT-FWHM": np.float64(2.355 * sigma_a),
f"{camera}:AVG-FIT-RMS": np.float64(sigma_a),
f"{camera}:AVG-FIT-RES": np.float64(2.355 * sigma_a / center_a * 1000),
f"{camera}:AVG-SPECT-COM": spect_com_a,
f"{camera}:AVG-SPECT-COM": spect_com,
f"{camera}:AVG-SPECT-RMS": spect_std_a,
f"{camera}:AVG-SPECT-SKEW": spect_skew_a,
f"{camera}:AVG-SPECT-IQR": spect_iqr_a,
@@ -255,7 +297,8 @@ def process_image(image, pulse_id, timestamp, x_axis, y_axis, parameters, bsdata
try:
if pulse_id > sent_pid:
sent_pid = pulse_id
entry = tuple(full_results.get(pv) for pv in all_pv_names)
#entry = tuple(full_results.get(pv) for pv in all_pv_names)
entry = tuple(_pv_safe(full_results.get(pv), pv) for pv in all_pv_names)
buffer.append(entry)
finally:
epics_lock.release()

View File

@@ -0,0 +1,310 @@
from logging import getLogger
from cam_server.pipeline.data_processing import functions
from cam_server.utils import create_thread_pvs, epics_lock
from collections import deque
import json
import numpy as np
import scipy.signal
import numba
import time
import sys
from threading import Thread
# Configure Numba to use multiple threads
numba.set_num_threads(4)
_logger = getLogger(__name__)
# Shared state globals
global_roi = [0, 0]
initialized = False
sent_pid = -1
buffer = deque(maxlen=5)
channel_pv_names = None
base_pv_names = []
all_pv_names = []
global_ravg_length = 100
ravg_buffers = {}
# Configuration for rolling-average of statistics
# Configuration for N-shot average spectrum
global_avg_length = 100
avg_buffer = None
avg_pv_names = []
@numba.njit(parallel=False)
def get_spectrum(image, background):
"""Compute background-subtracted spectrum via row-wise summation."""
y, x = image.shape
profile = np.zeros(x, dtype=np.float64)
for i in numba.prange(y):
for j in range(x):
profile[j] += image[i, j] - background[i, j]
return profile
def update_PVs(buffer, *pv_names):
"""Continuously read from buffer and write to EPICS PVs."""
pvs = create_thread_pvs(list(pv_names))
while True:
time.sleep(0.1)
try:
rec = buffer.popleft()
except IndexError:
continue
try:
for pv, val in zip(pvs, rec):
if pv and pv.connected and (val is not None):
pv.put(val)
except Exception:
_logger.exception("Error updating channels")
def initialize(params):
"""Initialize PV names, running-average settings, N-shot average, and launch update thread."""
global channel_pv_names, base_pv_names, all_pv_names, global_ravg_length
global global_avg_length, avg_buffer, avg_pv_names
camera = params["camera_name"]
e_int = params["e_int_name"]
e_axis = params["e_axis_name"]
# Fit/result PV names
center_pv = f"{camera}:FIT-COM"
fwhm_pv = f"{camera}:FIT-FWHM"
fit_rms_pv = f"{camera}:FIT-RMS"
fit_res_pv = f"{camera}:FIT-RES"
fit_spec_pv = f"{camera}:FIT-SPECTRUM_Y"
# ROI PVs for dynamic read
ymin_pv = f"{camera}:SPC_ROI_YMIN"
ymax_pv = f"{camera}:SPC_ROI_YMAX"
axis_pv = e_axis
channel_pv_names = [ymin_pv, ymax_pv, axis_pv]
# Spectrum statistical PV names
com_pv = f"{camera}:SPECT-COM"
std_pv = f"{camera}:SPECT-RMS"
skew_pv = f"{camera}:SPECT-SKEW"
iqr_pv = f"{camera}:SPECT-IQR"
res_pv = f"{camera}:SPECT-RES"
# Base PVs for update thread (order matters)
base_pv_names = [
e_int, center_pv, fwhm_pv, fit_rms_pv,
fit_res_pv, fit_spec_pv, com_pv, std_pv, skew_pv, iqr_pv, res_pv
]
# Running-average configuration
global_ravg_length = params.get('RAVG_length', global_ravg_length)
exclude = {e_int, e_axis, f"{camera}:processing_parameters"}
ravg_base = [pv for pv in base_pv_names if pv not in exclude]
ravg_pv_names = [pv + '-RAVG' for pv in ravg_base]
# N-shot average configuration
global_avg_length = params.get('avg_nshots', global_avg_length)
avg_buffer = deque(maxlen=global_avg_length)
# Define PVs for N-shot average statistics and spectra
avg_pv_names = [
f"{camera}:AVG-FIT-COM",
f"{camera}:AVG-FIT-FWHM",
f"{camera}:AVG-FIT-RMS",
f"{camera}:AVG-FIT-RES",
f"{camera}:AVG-SPECT-COM",
f"{camera}:AVG-SPECT-RMS",
f"{camera}:AVG-SPECT-SKEW",
f"{camera}:AVG-SPECT-IQR",
f"{camera}:AVG-SPECT-RES",
f"{camera}:AVG-SPECTRUM_Y",
f"{camera}:AVG-FIT-SPECTRUM_Y"
]
global ARRAY_PVS
ARRAY_PVS = set([
f"{camera}:SPECTRUM_Y",
f"{camera}:FIT-SPECTRUM_Y",
f"{camera}:AVG-SPECTRUM_Y",
f"{camera}:AVG-FIT-SPECTRUM_Y"
# add any others here
])
# All PVs (original + running average + N-shot average)
all_pv_names = base_pv_names + ravg_pv_names + avg_pv_names
# Start background thread for PV updates
thread = Thread(target=update_PVs, args=(buffer, *all_pv_names), daemon=True)
thread.start()
def _pv_safe(val, pvname):
if pvname in ARRAY_PVS:
# array PV: must always send a 1D numpy array
if isinstance(val, np.ndarray):
if val.ndim == 1:
return val.astype(np.float64)
elif val.ndim == 0:
# Scalar array, wrap as 1-element
return np.array([val.item()], dtype=np.float64)
else:
raise ValueError(f"{pvname}: expected 1D array, got shape {val.shape}")
elif isinstance(val, (list, tuple)):
return np.array(val, dtype=np.float64)
elif isinstance(val, (float, int, np.generic)):
return np.array([val], dtype=np.float64)
else:
raise TypeError(f"{pvname}: expected array, got {type(val)}")
else:
# scalar PV: must always send float
if isinstance(val, np.ndarray):
if val.size == 1:
return float(val.item())
else:
raise ValueError(f"{pvname}: expected scalar, got array of size {val.size}")
elif isinstance(val, (np.generic, float, int)):
return float(val)
else:
raise TypeError(f"{pvname}: expected scalar, got {type(val)}")
def process_image(image, pulse_id, timestamp, x_axis, y_axis, parameters, bsdata=None, background=None):
"""
Main entrypoint: subtract background, crop ROI, smooth, fit Gaussian,
compute metrics, N-shot average, queue PV updates (with running averages).
Returns a dict of processed PV values (original and average channels).
"""
global initialized, sent_pid, channel_pv_names
global global_ravg_length, ravg_buffers, avg_buffer
try:
if not initialized:
initialize(parameters)
initialized = True
camera = parameters["camera_name"]
# Dynamic ROI and axis PV read
ymin_pv, ymax_pv, axis_pv = create_thread_pvs(channel_pv_names)
if ymin_pv and ymin_pv.connected:
global_roi[0] = ymin_pv.value
if ymax_pv and ymax_pv.connected:
global_roi[1] = ymax_pv.value
if not (axis_pv and axis_pv.connected):
_logger.warning("Energy axis not connected")
return None
axis = axis_pv.value[:image.shape[1]]
# Preprocess image
proc_img = image.astype(np.float32) - np.float32(parameters.get("pixel_bkg", 0))
nrows, _ = proc_img.shape
# Background image
bg_img = parameters.pop('background_data', None)
bg_img = bg_img.astype(np.float32) if isinstance(bg_img, np.ndarray) and bg_img.shape == proc_img.shape else None
# Crop ROI
ymin, ymax = map(int, global_roi)
if 0 <= ymin < ymax <= nrows:
proc_img = proc_img[ymin:ymax, :]
if bg_img is not None:
bg_img = bg_img[ymin:ymax, :]
# Extract spectrum and fitted spectrum
spectrum = get_spectrum(proc_img, bg_img) if bg_img is not None else np.sum(proc_img, axis=0)
smoothed = scipy.signal.savgol_filter(spectrum, 51, 3)
minimum, maximum = smoothed.min(), smoothed.max()
amplitude = maximum - minimum
skip = amplitude <= nrows * 1.5
offset, amp_fit, center, sigma = functions.gauss_fit_psss(
smoothed[::2], axis[::2], offset=minimum, amplitude=amplitude, skip=skip, maxfev=10
)
# Reconstruct fitted curve
fit_spectrum = offset + amp_fit * np.exp(-((axis - center)**2) / (2 * sigma**2))
#fit_spectrum = np.abs(fit_spectrum)
# Moments
sm_norm = smoothed / np.sum(smoothed)
spect_com = np.sum(axis * sm_norm)
spect_std = np.sqrt(np.sum((axis - spect_com)**2 * sm_norm))
spect_skew = np.sum((axis - spect_com)**3 * sm_norm) / (spect_std**3)
cum = np.cumsum(sm_norm); e25 = np.interp(0.25, cum, axis); e75 = np.interp(0.75, cum, axis)
spect_iqr = e75 - e25; spect_sum = np.sum(spectrum)
# Original result
result = {
parameters["e_int_name"]: spectrum,
parameters["e_axis_name"]: axis,
f"{camera}:SPECTRUM_Y_SUM": spect_sum,
f"{camera}:FIT-COM": np.float64(center),
f"{camera}:FIT-FWHM": np.float64(2.355 * sigma),
f"{camera}:FIT-RMS": np.float64(sigma),
f"{camera}:FIT-RES": np.float64(2.355 * sigma / center * 1000),
f"{camera}:FIT-SPECTRUM_Y": np.float64(fit_spectrum),
f"{camera}:SPECT-COM": spect_com,
f"{camera}:SPECT-RMS": spect_std,
f"{camera}:SPECT-SKEW": spect_skew,
f"{camera}:SPECT-IQR": spect_iqr,
f"{camera}:SPECT-RES": np.float64(spect_iqr / spect_com * 1000),
f"{camera}:processing_parameters": json.dumps({"roi": global_roi})
}
# Rolling averages
exclude = {parameters["e_int_name"], parameters["e_axis_name"], f"{camera}:processing_parameters"}
ravg_results = {}
for base_pv in (pv for pv in base_pv_names if pv not in exclude):
buf = ravg_buffers.setdefault(base_pv, deque(maxlen=global_ravg_length))
buf.append(result[base_pv])
ravg_results[f"{base_pv}-RAVG"] = np.mean(buf)
# N-shot average and average fitted spectrum
avg_buffer.append(spectrum)
avg_spectrum = np.mean(np.stack(avg_buffer), axis=0)
avg_spectrum = np.abs(avg_spectrum)
fit_avg = offset + amp_fit * np.exp(-((axis - center)**2) / (2 * sigma**2)) # using avg fit params below
sm_avg = scipy.signal.savgol_filter(avg_spectrum, 51, 3)
min_a, max_a = sm_avg.min(), sm_avg.max()
amp_a = max_a - min_a; skip_a = amp_a <= nrows * 1.5
offs_a, amp_fit_a, center_a, sigma_a = functions.gauss_fit_psss(
sm_avg[::2], axis[::2], offset=min_a, amplitude=amp_a, skip=skip_a, maxfev=10
)
fit_avg_spectrum = offs_a + amp_fit_a * np.exp(-((axis - center_a)**2) / (2 * sigma_a**2))
fit_avg_spectrum = np.abs(fit_avg_spectrum)
# Average moments
sm_norm_a = sm_avg / np.sum(sm_avg)
spect_com_a = np.sum(axis * sm_norm_a)
spect_std_a = np.sqrt(np.sum((axis - spect_com_a)**2 * sm_norm_a))
spect_skew_a = np.sum((axis - spect_com_a)**3 * sm_norm_a) / (spect_std_a**3)
cum_a = np.cumsum(sm_norm_a); e25_a = np.interp(0.25, cum_a, axis); e75_a = np.interp(0.75, cum_a, axis)
spect_iqr_a = e75_a - e25_a
spect_res_a = spect_iqr_a / spect_com_a * 1000
avg_results = {
f"{camera}:AVG-FIT-COM": np.float64(center),
f"{camera}:AVG-FIT-FWHM": np.float64(2.355 * sigma_a),
f"{camera}:AVG-FIT-RMS": np.float64(sigma_a),
f"{camera}:AVG-FIT-RES": np.float64(2.355 * sigma_a / center_a * 1000),
f"{camera}:AVG-SPECT-COM": spect_com,
f"{camera}:AVG-SPECT-RMS": spect_std_a,
f"{camera}:AVG-SPECT-SKEW": spect_skew_a,
f"{camera}:AVG-SPECT-IQR": spect_iqr_a,
f"{camera}:AVG-SPECT-RES": np.float64(spect_res_a),
f"{camera}:AVG-SPECTRUM_Y": avg_spectrum,
f"{camera}:AVG-FIT-SPECTRUM_Y": fit_avg_spectrum
}
# Merge and queue
full_results = {**result, **ravg_results, **avg_results}
if epics_lock.acquire(False):
try:
if pulse_id > sent_pid:
sent_pid = pulse_id
#entry = tuple(full_results.get(pv) for pv in all_pv_names)
entry = tuple(_pv_safe(full_results.get(pv), pv) for pv in all_pv_names)
buffer.append(entry)
finally:
epics_lock.release()
return full_results
except Exception as ex:
_logger.warning("Processing error: %s", ex)
return {}

View File

@@ -164,35 +164,45 @@ def process_image(image, pulse_id, timestamp, x_axis, y_axis, parameters, bsdata
if 0 <= ymin_i < ymax_i <= nrows:
proc_img = proc_img[ymin_i:ymax_i, :]
# Trim x_axis to match the cropped image width
axis = x_axis[:proc_img.shape[1]]
# Spectrum via vector sum
spectrum = np.sum(proc_img, axis=0)
# Smooth
smoothed = scipy.signal.savgol_filter(spectrum, 51, 3)
# Fit Gaussian
minimum, maximum = smoothed.min(), smoothed.max()
amplitude = maximum - minimum
skip = amplitude <= nrows * 1.5
offset, amp_fit, center, sigma = functions.gauss_fit_psss(
smoothed[::2], x_axis[:len(smoothed)][::2], offset=minimum,
amplitude=amplitude, skip=skip, maxfev=10
smoothed[::2],
axis[:len(smoothed)][::2],
offset=minimum,
amplitude=amplitude,
skip=skip,
maxfev=10
)
fit_spectrum = offset + amp_fit * np.exp(
-((axis[:len(smoothed)] - center)**2) / (2 * sigma**2)
)
fit_spectrum = offset + amp_fit * np.exp(-((x_axis[:len(smoothed)] - center)**2) / (2 * sigma**2))
# Compute stats
sm_norm = smoothed / np.sum(smoothed)
spect_com = np.dot(x_axis[:len(sm_norm)], sm_norm)
spect_std = np.sqrt(np.dot((x_axis[:len(sm_norm)] - spect_com)**2, sm_norm))
spect_skew = np.dot((x_axis[:len(sm_norm)] - spect_com)**3, sm_norm) / (spect_std**3)
spect_com = np.dot(axis[:len(sm_norm)], sm_norm)
spect_std = np.sqrt(np.dot((axis[:len(sm_norm)] - spect_com)**2, sm_norm))
spect_skew = np.dot((axis[:len(sm_norm)] - spect_com)**3, sm_norm) / (spect_std**3)
cum = np.cumsum(sm_norm)
e25 = np.interp(0.25, cum, x_axis[:len(cum)])
e75 = np.interp(0.75, cum, x_axis[:len(cum)])
e25 = np.interp(0.25, cum, axis[:len(cum)])
e75 = np.interp(0.75, cum, axis[:len(cum)])
spect_iqr = e75 - e25
spect_sum = spectrum.sum()
# Original result
result = {
parameters["e_int_name"]: spectrum,
parameters["e_axis_name"]: x_axis[:len(spectrum)],
parameters["e_axis_name"]: axis,
f"{camera}:SPECTRUM_Y_SUM": spect_sum,
f"{camera}:FIT-COM": np.float64(center),
f"{camera}:FIT-FWHM": np.float64(2.355 * sigma),
@@ -212,7 +222,11 @@ def process_image(image, pulse_id, timestamp, x_axis, y_axis, parameters, bsdata
# Running averages (thread-safe, incremental sum)
ravg_results = {}
exclude = {parameters["e_int_name"], parameters["e_axis_name"], f"{camera}:processing_parameters"}
exclude = {
parameters["e_int_name"],
parameters["e_axis_name"],
f"{camera}:processing_parameters"
}
with ravg_lock:
for pv in base_pv_names:
if pv not in exclude:
@@ -236,23 +250,30 @@ def process_image(image, pulse_id, timestamp, x_axis, y_axis, parameters, bsdata
avg_buffer.append(spectrum)
avg_sum += spectrum
avg_spectrum = avg_sum / len(avg_buffer)
# Fit and stats on avg_spectrum
sm_avg = scipy.signal.savgol_filter(avg_spectrum, 51, 3)
min_a, max_a = sm_avg.min(), sm_avg.max()
amp_a = max_a - min_a
skip_a = amp_a <= nrows * 1.5
offs_a, amp_fit_a, center_a, sigma_a = functions.gauss_fit_psss(
sm_avg[::2], x_axis[:len(sm_avg)][::2], offset=min_a,
amplitude=amp_a, skip=skip_a, maxfev=10
sm_avg[::2],
axis[:len(sm_avg)][::2],
offset=min_a,
amplitude=amp_a,
skip=skip_a,
maxfev=10
)
fit_avg_spectrum = offs_a + amp_fit_a * np.exp(
-((axis[:len(sm_avg)] - center_a)**2) / (2 * sigma_a**2)
)
fit_avg_spectrum = offs_a + amp_fit_a * np.exp(-((x_axis[:len(sm_avg)] - center_a)**2) / (2 * sigma_a**2))
sm_norm_a = sm_avg / np.sum(sm_avg)
spect_com_a = np.dot(x_axis[:len(sm_norm_a)], sm_norm_a)
spect_std_a = np.sqrt(np.dot((x_axis[:len(sm_norm_a)] - spect_com_a)**2, sm_norm_a))
spect_skew_a = np.dot((x_axis[:len(sm_norm_a)] - spect_com_a)**3, sm_norm_a) / (spect_std_a**3)
spect_com_a = np.dot(axis[:len(sm_norm_a)], sm_norm_a)
spect_std_a = np.sqrt(np.dot((axis[:len(sm_norm_a)] - spect_com_a)**2, sm_norm_a))
spect_skew_a = np.dot((axis[:len(sm_norm_a)] - spect_com_a)**3, sm_norm_a) / (spect_std_a**3)
cum_a = np.cumsum(sm_norm_a)
e25_a = np.interp(0.25, cum_a, x_axis[:len(cum_a)])
e75_a = np.interp(0.75, cum_a, x_axis[:len(cum_a)])
e25_a = np.interp(0.25, cum_a, axis[:len(cum_a)])
e75_a = np.interp(0.75, cum_a, axis[:len(cum_a)])
spect_iqr_a = e75_a - e25_a
spect_res_a = spect_iqr_a / spect_com_a * 1000
@@ -281,3 +302,4 @@ def process_image(image, pulse_id, timestamp, x_axis, y_axis, parameters, bsdata
epics_lock.release()
return full