*** Wartungsfenster jeden ersten Mittwoch vormittag im Monat ***

Skip to content
Snippets Groups Projects
Commit 189a35f7 authored by Schabbauer, Johannes's avatar Schabbauer, Johannes
Browse files

ADwin: Added limit checks for AOUT

Added limit check only when PID is off and set output when PID is turned off.
parent 7d94255f
No related branches found
No related tags found
1 merge request!4PID improvements for ADwin
...@@ -131,7 +131,7 @@ class ADwinAnalogOut(AnalogOut): ...@@ -131,7 +131,7 @@ class ADwinAnalogOut(AnalogOut):
self.PID_max = limits[1] self.PID_max = limits[1]
def set_PID(self,t,pid_no,start="last"): def set_PID(self,t,pid_no,set_output=0):
"""(De-)activate PID for analog output, or change settings """(De-)activate PID for analog output, or change settings
Parameters Parameters
...@@ -141,25 +141,34 @@ class ADwinAnalogOut(AnalogOut): ...@@ -141,25 +141,34 @@ class ADwinAnalogOut(AnalogOut):
pid_no : int or `AnalogIn` or None pid_no : int or `AnalogIn` or None
Channel of analog input for error siganl of PID feedback. Channel of analog input for error siganl of PID feedback.
If `None` PID is deactivated. If `None` PID is deactivated.
start : float or "last" set_value : float or "last"
Inital value of the output when the PID is turned on, assigned to the I part. When the PID is turned on, 'set_value' is the initially chosen output value,
If start="last" when the PID is turned on, the I value from the PID earlier 'last' means that the I value from the previous PID is taken.
in the shot is kept. If start="last" when the PID is turned off, the last output When the PID is turned off, 'set_value' is programmed as the new output/target value,
value is kept as "set_target". 'last' means that the output from the PID loop is kept as 'set_target'.
""" """
# Error check of bounds for set_output
if set_output!="last":
if set_output<self.PID_min or set_output>self.PID_max:
raise LabscriptError(
f"{self.name}: PID 'set_output={set_output}' must be within ({self.PID_min},{self.PID_max})"
)
# TURN OFF PID # TURN OFF PID
if pid_no is None: if pid_no is None:
self.PID[t] = { self.PID[t] = {
"PID_channel": 0, "PID_channel": 0,
"start": start, "start": set_output,
} }
# If we don't keep the PID output, set the output to the set_value
if set_output!="last":
self.constant(t,set_output)
# TURN ON PID # TURN ON PID
elif (isinstance(pid_no,AnalogIn) and isinstance(pid_no.parent_device,_ADwinCard)) \ elif (isinstance(pid_no,AnalogIn) and isinstance(pid_no.parent_device,_ADwinCard)) \
or isinstance(pid_no,int): or isinstance(pid_no,int):
self.PID[t] = { self.PID[t] = {
"PID_channel": pid_no, "PID_channel": pid_no,
"start": start, "start": set_output,
} }
# TODO: Do we need scale factors for setting a PID with integer? # TODO: Do we need scale factors for setting a PID with integer?
else: else:
...@@ -203,19 +212,17 @@ class ADwinAnalogOut(AnalogOut): ...@@ -203,19 +212,17 @@ class ADwinAnalogOut(AnalogOut):
def add_instruction(self,time,instruction,units=None): def add_instruction(self,time,instruction,units=None):
# Overwrite Output.add_instruction without limit check, becasue the value can be off-limits when this is the target value of the PID # Overwrite Output.add_instruction without limit check, becasue the value can be off-limits when this is the target value of the PID
# TODO / WARNING: THIS IS QUITE HACKY AND COULD LEAD TO OFF-LIMIT VALUES NOT NOTICED
# (the actual limits are also checked in the ADwin, so the actual output should be always within limits!)
limits_temp = self.limits limits_temp = self.limits
self.limits = (-10,10) if hasattr(self,"pid_no"):
self.limits = (-10,10)
super().add_instruction(time,instruction,units) super().add_instruction(time,instruction,units)
self.limits = limits_temp self.limits = limits_temp
def expand_timeseries(self,all_times,flat_all_times_len): def expand_timeseries(self,all_times,flat_all_times_len):
# Overwrite Output.add_instruction without limit check, becasue the value can be off-limits when this is the target value of the PID # Overwrite Output.add_instruction without limit check, becasue the value can be off-limits when this is the target value of the PID
# TODO / WARNING: THIS IS QUITE HACKY AND COULD LEAD TO OFF-LIMIT VALUES NOT NOTICED
# (the actual limits are also checked in the ADwin, so the actual output should be always within limits!)
limits_temp = self.limits limits_temp = self.limits
self.limits = (-10,10) if hasattr(self,"pid_no"):
self.limits = (-10,10)
super().expand_timeseries(all_times,flat_all_times_len) super().expand_timeseries(all_times,flat_all_times_len)
self.limits = limits_temp self.limits = limits_temp
...@@ -353,6 +360,38 @@ class ADwinAO8(_ADwinCard): ...@@ -353,6 +360,38 @@ class ADwinAO8(_ADwinCard):
raise LabscriptError( raise LabscriptError(
f"The ramp sample rate ({rate_kHz:.0f}kHz) of {output.name} must not be faster than ADwin ({ADwin_rate_kHz:.0f}kHz)." f"The ramp sample rate ({rate_kHz:.0f}kHz) of {output.name} must not be faster than ADwin ({ADwin_rate_kHz:.0f}kHz)."
) )
# Check limits of output, but only when PID is NOT enabled (becasue then the target can be out of limits)
# Get all times when PID is not enabled
PID = output.PID.copy()
if 0 not in PID:
# Because 'np.digitize' determines the bins, we also have to make sure t=0 is included.
# If output.PID does not have the key t=0, then the PID is disabled in the beginning.
PID[0] = {"PID_channel":0,"start":0}
PID_times = np.array(list(PID.keys()))
PID_times.sort()
PID_off_times = []
# For each output value, digitize gets the next highest time in PID_times.
# Using '-1' to get next lowest time.
for i_out,i_PID in enumerate(np.digitize(output.all_times, PID_times)-1):
t = PID_times[i_PID]
if PID[t]["PID_channel"]==0:
# When we turn the PID off but keep the last output, we make sure that
# - at least once in the end the output is (re)set, otherwise the get_final_values() in the Worker is wrong,
# - don't try to also set the target value at the same time, as this would overwrite the target in the ADwin with the PID output.
if PID[t]["start"]=="last":
if i_out+1==len(output.all_times):
raise LabscriptError(f"{output.name}: You must set the output at the end after turning off PID with 'last'.")
elif output.all_times[i_out] == t:
raise LabscriptError(f"{output.name}: Don't turn off PID with persitent value ('last') and also set new value.")
PID_off_times.append(i_out)
PID_off_outputs = output.raw_output[PID_off_times]
if np.any(PID_off_outputs < output.limits[0]) or np.any(PID_off_outputs > output.limits[1]):
raise LabscriptError(
f"Limits of {output.name} (when PID is off) must be in {output.limits}, " +
f"you try to set ({PID_off_outputs.min()},{PID_off_outputs.max()}) " +
f"or turning off the PID with target value beyond limits.")
# Check if the PID channel is allowed # Check if the PID channel is allowed
if np.any(self.PID_table["PID_channel"] > PIDNO): if np.any(self.PID_table["PID_channel"] > PIDNO):
raise LabscriptError(f"ADwin: Setting the PID channel to more than {PIDNO} is not possible!") raise LabscriptError(f"ADwin: Setting the PID channel to more than {PIDNO} is not possible!")
...@@ -406,14 +445,6 @@ class ADwinAO8(_ADwinCard): ...@@ -406,14 +445,6 @@ class ADwinAO8(_ADwinCard):
output.raw_output[indices==i+1] *= output.PID[t]['PID_channel'].scale_factor output.raw_output[indices==i+1] *= output.PID[t]['PID_channel'].scale_factor
elif isinstance(output.PID[t]['PID_channel'],int): elif isinstance(output.PID[t]['PID_channel'],int):
PID_array["PID_channel"][i] = output.PID[t]['PID_channel'] PID_array["PID_channel"][i] = output.PID[t]['PID_channel']
# If PID_channel[t]=None, there are zeros in the PID_array
# PID_array["PID_P"][i] = output.PID[t]['P']
# PID_array["PID_I"][i] = output.PID[t]['I']
# PID_array["PID_D"][i] = output.PID[t]['D']
# limits = output.PID[t]['limits']
# PID_array["PID_min"][i] = ADC(limits[0],self.resolution_bits,self.min_V,self.max_V)
# PID_array["PID_max"][i] = ADC(limits[1],self.resolution_bits,self.min_V,self.max_V)
if output.PID[t]["start"]=="last": if output.PID[t]["start"]=="last":
# When we want to use the previous value during the shot, # When we want to use the previous value during the shot,
# we use a value that's out of the 16 bit ADC range to identify. # we use a value that's out of the 16 bit ADC range to identify.
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment