Dec 2025, before change EPICS writer function

This commit is contained in:
root
2025-12-03 10:41:31 +01:00
parent 850628cc56
commit ff7008dfb8
12 changed files with 383 additions and 18 deletions

View File

@@ -1,8 +1,8 @@
{
"camera_calibration": {
"reference_marker": [
100,
100,
44,
44,
1999,
1999
],

View File

@@ -0,0 +1,18 @@
{
"image_background_enable": false,
"image_background": null,
"image_threshold": null,
"image_region_of_interest": null,
"image_good_region": {
"threshold": 0.4,
"gfscale": 3.0
},
"image_slices": null,
"pipeline_type": "processing",
"camera_name": "ABODI-SRM-6",
"function": "epics_forwarder_gr.py",
"name": "ABODI-SRM-6_proc",
"max_frame_rate": 10.1,
"throw_epics_errors": true,
"reload": true
}

View File

@@ -2,11 +2,17 @@
"image_background_enable": false,
"image_background": null,
"image_threshold": null,
"image_region_of_interest": null,
"image_region_of_interest": [
1807,
1251,
1506,
1077
],
"image_good_region": {
"threshold": 0.4,
"gfscale": 3.0
},
"averaging": 15,
"image_slices": null,
"pipeline_type": "processing",
"camera_name": "X01DD-FE-CAM1",

View File

@@ -1,6 +1,6 @@
{
"image_background_enable": false,
"image_background": null,
"image_background": "X01DD-FE-CAM1_20251202_165201_161057",
"image_threshold": null,
"image_region_of_interest": null,
"image_good_region": {
@@ -11,5 +11,11 @@
"pipeline_type": "processing",
"camera_name": "X01DD-FE-CAM1",
"name": "X01DD-FE-CAM1_sp",
"max_frame_rate": 10.1
"max_frame_rate": 100.1,
"reload": true,
"function": "epics_handler.py",
"max_output_rate": 10.1,
"single_optics": true,
"PSF": 3.4,
"throw_epics_errors": true
}

View File

@@ -3,6 +3,7 @@
"image_background": null,
"image_threshold": null,
"image_region_of_interest": null,
"averaging": 15,
"image_good_region": {
"threshold": 0.4,
"gfscale": 3.0

View File

@@ -11,5 +11,10 @@
"pipeline_type": "processing",
"camera_name": "X08DB-FE-CAM1",
"name": "X08DB-FE-CAM1_sp",
"max_frame_rate": 10.1
"function": "epics_handler.py",
"max_frame_rate": 100.1,
"max_output_rate": 10.1,
"single_optics": true,
"PSF": 3.4,
"throw_epics_errors": true
}

View File

@@ -1,11 +1,11 @@
{
"#ALBDI-SM-2_emittance": "ALBDI-SM-2_emittance",
"#ALIDI-SM-5_proc": "ALIDI-SM-5_proc",
"#X01DD-FE-CAM1_sp": "X01DD-FE-CAM1_sp",
"#simulation_proc_sp": "simulation_proc_sp",
"#simulation_sp1": "simulation_sp",
"ABODI-SM-1S1_proc": "ABODI-SM-1S1_proc",
"ABODI-SM-1S2_proc": "ABODI-SM-1S2_proc",
"ABODI-SRM-6_proc": "ABODI-SRM-6_proc",
"ABRTL-DSCR-0440_proc": "ABRTL-DSCR-0440_proc",
"ABRTL-DSCR-0680_proc": "ABRTL-DSCR-0680_proc",
"ABRTL-DSCR-0870_proc": "ABRTL-DSCR-0870_proc",
@@ -25,8 +25,8 @@
"ARS05-RCAM-0310_proc2": "ARS05-RCAM-0310_proc2",
"X01DD-ES-CAM1_emittance": "X01DD-ES-CAM1_emittance",
"X01DD-ES-CAM1_sp_proc": "X01DD-ES-CAM1_sp_proc",
"X01DD-FE-CAM1_proc": "X01DD-FE-CAM1_proc",
"X01DD-FE-CAM1_sp": "X01DD-FE-CAM1_sp",
"X01DD-SC-CAM1_proc": "X01DD-SC-CAM1_proc",
"X08DB-FE-CAM1_proc": "X08DB-FE-CAM1_proc",
"X08DB-FE-CAM1_sp": "X08DB-FE-CAM1_sp",
"emittance_pipolar": "emittance_pipolar"
}

View File

@@ -38,7 +38,7 @@
"instances": [
"ALIDI-SM-5_sp_proc",
"ALBDI-SM-2_sp_proc",
"#X01DD-FE-CAM1_sp",
"X01DD-FE-CAM1_sp",
"ALBDI-SM-2_emittance:9001",
"X01DD-ES-CAM1_emittance",
"X01DD-FE-CAM1_proc",
@@ -58,6 +58,8 @@
],
"enabled": true,
"expanding": false,
"instances": []
"instances": [
"ABODI-SRM-6_proc"
]
}
}

View File

@@ -16,7 +16,7 @@ mean_x_name, mean_y_name, sig_x_name, sig_y_name = None, None, None, None
ampl_x_pv, ampl_y_pv, integ_x_pv, integ_y_pv = None, None, None, None
ampl_x_name, ampl_y_name, integ_x_name, integ_y_name = None, None, None, None
sent_pid = -1
PSF = 2.91 # um
PSF = 3.4# 3.4um updated 11/11/25 at 11:16hrs
initialized = False
@@ -36,7 +36,10 @@ def initialize(parameters):
initialized = True
def hypot_diff(a, b):
return math.sqrt(a**2 - b**2)
try:
return math.sqrt(a**2 - b**2)
except:
return 0.0
def process_image(image, pulse_id, timestamp, x_axis, y_axis, parameters, bsdata):
global initialized, sent_pid

View File

@@ -0,0 +1,111 @@
from cam_server.pipeline.data_processing import processor
from cam_server.utils import create_thread_pvs, epics_lock
from logging import getLogger
import math
_logger = getLogger(__name__)
#Relevant parameters
# max_frame_rate #max processing frame rate (EPICS)
# max_output_rate #max displaying frame rate (ScreenPanel)
# single_optics #default: False
# PSF #defauit 2.91um
# throw_epics_errors #in case of permanent pipelines can recreate EPICS channels after failure, but does stream
#Output PVs
x_pos_pv, y_pos_pv, x_sig_pv, y_sig_pv = None, None, None, None
x_amp_pv, y_amp_pv, x_int_pv, y_int_pv = None, None, None, None
#Channel names
x_pos_name, y_pos_name, x_sig_name, y_sig_name = None, None, None, None
x_amp_name, y_amp_name, x_int_name, y_int_name = None, None, None, None
sent_pid = -1
initialized = False
run_once = False
GAUSS_INT_FACTOR = math.sqrt(2 * math.pi)
def initialize(parameters):
global initialized
global x_pos_name, y_pos_name, x_sig_name, y_sig_name, x_amp_name, y_amp_name, x_int_name, y_int_name
prefix = parameters["camera_name"]
x_pos_name = prefix + ":POS-X"
y_pos_name = prefix + ":POS-Y"
x_sig_name = prefix + ":SIG-X"
y_sig_name = prefix + ":SIG-Y"
x_amp_name = prefix + ":AMPL-X"
y_amp_name = prefix + ":AMPL-Y"
x_int_name = prefix + ":INTEG-X"
y_int_name = prefix + ":INTEG-Y"
initialized = True
def process_image(image, pulse_id, timestamp, x_axis, y_axis, parameters, bsdata):
global initialized, sent_pid, run_once
global x_pos_pv, y_pos_pv, x_sig_pv, y_sig_pv, x_amp_pv, y_amp_pv, x_int_pv, y_int_pv
global x_pos_name, y_pos_name, x_sig_name, y_sig_name, x_amp_name, y_amp_name, x_int_name, y_int_name
if not initialized:
initialize(parameters)
initialized = True
throw_epics_errors = parameters.get("throw_epics_errors", True)
single_optics = parameters.get("single_optics", False)
PSF = parameters.get("PSF", 2.91) # um
good_region = parameters["image_good_region"]
detached = parameters.get("detached_instance", False)
ret = processor.process_image(image, pulse_id, timestamp, x_axis, y_axis, parameters, bsdata)
if good_region:
x_pos_key, y_pos_key = "gr_x_fit_mean", "gr_y_fit_mean"
x_sig_key, y_sig_key = "gr_x_fit_standard_deviation", "gr_y_fit_standard_deviation"
x_amp_key, y_amp_key = "gr_x_fit_amplitude", "gr_y_fit_amplitude"
else:
x_pos_key, y_pos_key = "x_fit_mean", "y_fit_mean"
x_sig_key, y_sig_key = "x_fit_standard_deviation", "y_fit_standard_deviation"
x_amp_key, y_amp_key = "x_fit_amplitude", "y_fit_amplitude"
x_pos, y_pos = ret[x_pos_key], ret[y_pos_key]
x_sig, y_sig = ret[x_sig_key], ret[y_sig_key]
x_amp, y_amp = ret[x_amp_key], ret[y_amp_key]
x_int, y_int = x_amp * x_sig * GAUSS_INT_FACTOR, y_amp * y_sig * GAUSS_INT_FACTOR
if single_optics:
x_sig, y_sig = math.hypot(x_sig, PSF), math.hypot(y_sig, PSF)
ret[x_sig_key], ret[y_sig_key] = x_sig, y_sig
x_pos_pv, y_pos_pv, x_sig_pv, y_sig_pv, x_amp_pv, y_amp_pv, x_int_pv, y_int_pv = create_thread_pvs(
[x_pos_name, y_pos_name, x_sig_name, y_sig_name, x_amp_name, y_amp_name, x_int_name, y_int_name])
if not detached: #Only write to EPICS in the "main" instance
epics_error = None
if pulse_id > sent_pid:
sent_pid = pulse_id
if epics_lock.acquire(False):
try:
for (pv, value) in [(x_pos_pv, x_pos), (y_pos_pv, y_pos), (x_sig_pv, x_sig), (y_sig_pv, y_sig),
(x_amp_pv, x_amp), (y_amp_pv, y_amp), (x_int_pv, x_int), (y_int_pv, y_int)]:
if pv and pv.connected:
pv.put(value)
else:
if not run_once:
_logger.warning("PV not connected: %s" % (str(pv),))
except Exception as e:
epics_error = "Error writing PVs: %s" % (str(e),)
finally:
epics_lock.release()
else:
_logger.warning("Cannot aquire EPICS lock")
if epics_error:
_logger.warning(epics_error)
if throw_epics_errors:
raise Exception(epics_error)
run_once = True
return ret
else:
_logger.warning("Invalid PID: %s last:%s" % (str(pulse_id), str(sent_pid),))
sent_pid = 0 # It should be single threaded, assumes the camera instance restarted

View File

@@ -213,7 +213,7 @@ def calculate_emittance(image, fit_pars):
ratio_corrected = 2 * (valley_fitted_value) / (abs(peak_value[0] + peak_value[1]))
idx = 0 if abs(peak_value[0] ) > abs(peak_value[1] ) else 1
ratio_max = 0.33 # abs(peak_value[idx]) #valley_fitted_value /
ratio_max = valley_fitted_value /abs(peak_value[idx])
emittance = ppol.get_emittance(ratio_corrected)
emittance2 = ppol.get_emittance(ratio_max)

View File

@@ -1,12 +1,225 @@
import sys
import os
from emittance import calculate_emittance
from cam_server.pipeline.data_processing import functions, processor
import numpy as np
from scipy import interpolate
from scipy.signal import find_peaks
import math
from logging import getLogger
_logger = getLogger(__name__)
class PpolPeakValley():
def __init__(self):
self.x, self.y = self.ppol_interpol()
def ppol_interpol(self, plot_flag=False):
ipv = [0.0, 32.976, 20.848, 15.306, 12.121, 10.050, 8.595, 7.518, 6.687, 6.028, 5.491, 5.047, 4.673, 4.353, 4.078, 3.837, 3.439, 3.122, 2.865, 2.652, 2.473, 2.320]
sig = [0.0, 3.659, 5.175, 6.338, 7.318, 8.182, 8.963, 9.681, 10.350, 10.978, 11.572, 12.136, 12.676, 13.194, 13.692, 14.172, 15.087, 15.950, 16.769, 17.549, 18.296, 19.014]
#ipv = [0.0, 43.237,26.505,19.129,14.978,12.316,10.466,9.104,8.061,7.237,6.569,6.017,5.553,5.159,4.819,4.523,4.033,3.645,3.330,3.070,2.851,2.666]
#sig = [0.0, 3.659, 5.175, 6.338, 7.318, 8.182, 8.963, 9.681, 10.350, 10.978, 11.572, 12.136, 12.676, 13.194, 13.692, 14.172, 15.087, 15.950, 16.769, 17.549, 18.296, 19.014]
sig2 = [element * element for element in sig]
x = np.linspace(0, 999999, 999999)
x = [(1+val)/1000000*max(ipv) for val in x]
interpfunc = interpolate.interp1d(ipv, sig2, kind='quadratic')
inter_x = interpfunc(x)
sqrt_val = [math.sqrt(val) for val in inter_x]
y = [0]
y.extend(sqrt_val)
y.append(max(sig))
xf = [0]
xf.extend(x)
xf.append(max(ipv))
return xf, y
def get_emittance(self, ratio):
emittance = 0
for i in range(0, len(self.x)):
if self.x[i] > ratio:
self.y[i-1]
return self.y[i-1]
#For peak search - delta(h) to max peak value
DELTA_HEIGHT = 60000
BG_XRANGE_LOW = [0, 200]
BG_XRANGE_HIGH = [1921, 2112]
PEAK_SEARCH_REL_RANGE = [-2, 3]
VALLEY_SEARCH_REL_RANGE = [-2, 3]
ppol = PpolPeakValley()
# abreviations
BX1 = BG_XRANGE_LOW[0]
BX2 = BG_XRANGE_LOW[1]
BX3 = BG_XRANGE_HIGH[0]
BX4 = BG_XRANGE_HIGH[1]
plr = PEAK_SEARCH_REL_RANGE
vlr = VALLEY_SEARCH_REL_RANGE
ydata = []
def calculate_emittance(image, fit_pars):
global ydata # array of indexes
DELTA_HEIGHT = fit_pars['delta_height']
BG_XRANGE_LOW = fit_pars['bg_range_low']
BG_XRANGE_HIGH = fit_pars['bg_range_high']
PEAK_SEARCH_REL_RANGE = fit_pars['peak_search_rel_range']
VALLEY_SEARCH_REL_RANGE = fit_pars['valley_search_rel_range']
w, h = len(image[0]), len(image)
if len(ydata) != h:
H = []
for i in range(0, h):
H.append(i)
ydata = H[:int(h)]
peak_array = [None] * 2
proj_peak_array = [None] * 2
peak_value = [None] * 2
peak_bg = [None] * 2
image -= image.min()
projy = np.sum(image, axis=1)
projx = np.sum(image, axis=0)
# find peaks
max_element = np.amax(projy)
max_indices = np.where(projy == max_element)
peaks, _ = find_peaks(projy, height=(max_element - DELTA_HEIGHT))
_logger.debug("max indices /peaks " + str(max_indices) + " " + str(peaks))
if len(peaks) != 2:
mess = "Too few peaks found! " if len(peaks) < 2 else \
"Too many peaks found "
_logger.debug(mess + str(peaks))
peaks_buffer = []
for val in peaks:
### COMMENTED BY ALEX
# if val > 567 and val < 590:
peaks_buffer.append(val)
# if len(peaks_buffer) ==3:
# peaks = [None] * 2
# peaks[0] = peaks_buffer[0]
# peaks[1] = peaks_buffer[2]
if len(peaks_buffer) != 2:
return (-1.0, -2.0)
else:
peaks = peaks_buffer
if (peaks[1] - peaks[0]) < 6:
_logger.debug("Peaks are too close: " + str(peaks[1] - peaks[0]))
raise Exception("Peaks are too close")
# peaks =[569, 577]
# Distance to minimum
min_element = np.amin(projy[peaks[0]:peaks[1]])
min_indices = np.where(projy == min_element)
min_idx_value = 0 # min_indices[0][0]
for val in min_indices[0]:
if val > peaks[0] and val < peaks[1]:
min_idx_value = val
break
if min_idx_value == 0:
raise Exception("min_idx_value == 0")
for i in range(0, len(peak_array)):
peak_array[i] = ydata[peaks[i] + plr[0]: peaks[i] + plr[1]]
valley_array = ydata[min_idx_value + vlr[0]: min_idx_value + vlr[1]]
# print("peaks", peaks, flush=True)
# print(np.subtract(peaks, h)*(-1))
# print("projections peak, valley", projy[(peaks[0]-1):(peaks[1]+2)], projy[valley_array])
# print(peak_array, valley_array, flush=True)
# x_bg_center = min_indices[0][0]
# background
bg_y1 = projy[BX1: BX2]
bg_y2 = projy[BX3: BX4]
bg_yS = np.concatenate((bg_y1, bg_y2))
bg_xS = list(range(BX1, BX2)) + list(range(BX3, BX4))
bg_x = []
bg_y = []
for x, y in zip(bg_xS, bg_yS):
### COMMENTED BY ALEX
# if y > 800 and y < 1000:
bg_x.append(x)
bg_y.append(y)
# print(bg_x, flush=True)
# print(bg_y, flush=True)
# fit
poly_bg = np.polyfit(bg_x, bg_y, deg=1)
array_bg = np.linspace(0, h, 10400)
val_bg = np.polyval(poly_bg, array_bg)
for i in range(0, len(proj_peak_array)):
proj_peak_array[i] = projy[peaks[i] + plr[0]: peaks[i] + plr[1]]
# proj_peak_array[1] = projy[peaks[1]-1 : peaks[1]+2]
proj_valley = projy[min_idx_value + vlr[0]: min_idx_value + vlr[1]]
# peaks
for i in range(0, 2):
poly = np.polyfit(peak_array[i], proj_peak_array[i], deg=2)
idx = -poly[1] / 2 / poly[0]
peak_value[i] = np.polyval(poly, idx)
peak_bg[i] = val_bg[int(idx)]
# valley
poly2 = np.polyfit(valley_array, proj_valley, deg=2)
# Only works for deg=2
minv_idx = -poly2[1] / 2 / poly2[0]
poly = np.polyfit(valley_array, proj_valley, deg=4)
valley_subarray = np.linspace(valley_array[0], valley_array[-1], 800)
poly_array = np.polyval(poly, valley_subarray)
valley_fitted_value = min(poly_array)
# print("peak value", peak_value, "valley_fitted value", valley_fitted_value, flush=True)
valley_bg = val_bg[int(minv_idx)]
# print("valley background", valley_bg, "peak background", peak_bg[0], peak_bg[1], flush=True)
### COMMENTED BY ALEX
# if valley_fitted_value < valley_bg:
# valley_fitted_value = min( projy[valley_array])
# if valley_fitted_value < valley_bg:
# raise Exception("valley_fitted_value < valley_bg")
# #return
#ratio_corrected = abs((peak_value[0] - peak_bg[0]) + (peak_value[1] - peak_bg[1])) / (2 * (valley_fitted_value - valley_bg))
#idx = 0 if abs(peak_value[0] - peak_bg[0]) > abs(peak_value[1] - peak_bg[1]) else 1
#ratio_max = abs(peak_value[idx] - peak_bg[idx]) / (valley_fitted_value - valley_bg)
ratio_corrected = (abs(peak_value[0] + peak_value[1])) / (2 * valley_fitted_value)
idx = 0 if abs(peak_value[0] ) > abs(peak_value[1] ) else 1
ratio_max = abs(peak_value[idx]) / valley_fitted_value
emittance = ppol.get_emittance(ratio_corrected)
emittance2 = ppol.get_emittance(ratio_max)
_logger.debug("ratio=%f emittance %f ratio2=%f emittance2 %f" % (ratio_corrected, emittance, ratio_max, emittance2))
return (emittance, emittance2)
def process_image(image, pulse_id, timestamp, x_axis, y_axis, parameters, bsdata=None):
ret = processor.process_image(image, pulse_id, timestamp, x_axis, y_axis, parameters, bsdata)
camera = parameters["camera_name"]
camera = parameters["camera_name"]
emittance = emittance2 = float("NaN")
status = "Ok"
try:
@@ -17,11 +230,11 @@ def process_image(image, pulse_id, timestamp, x_axis, y_axis, parameters, bsdata
except Exception as e:
status="Error: "
exc_type, exc_obj, exc_tb = sys.exc_info()
while exc_tb is not None:
while exc_tb is not None:
fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
status = status + fname + " line " + str(exc_tb.tb_lineno) + ": " + str(e) + " | "
exc_tb = exc_tb.tb_next
ret["emittance"] = emittance
ret["emittance2"] = emittance2
ret["emittance_status"] = str(status)