Skip to content

Commit e6856d0

Browse files
feat: Add Keithley 2400 Lakeshore 350 V vs T Active V1.py
1 parent c0387bc commit e6856d0

1 file changed

Lines changed: 56 additions & 24 deletions

File tree

Keithley_2400/Keithley_2400_Lakeshore_350_V_vs_T_Active_V1.py

Lines changed: 56 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -63,22 +63,17 @@ def configure_instruments(self, current_ma, compliance_v):
6363
self.k2400.measure_voltage()
6464
self.k2400.enable_source()
6565

66-
def stabilize_at_start(self, start_temp, stability_log_callback):
67-
while True:
68-
current_temp = float(self.lakeshore.query('KRDG? A').strip())
69-
if current_temp > start_temp + 0.2:
70-
stability_log_callback(f"Cooling... Current: {current_temp:.4f} K > Target: {start_temp} K")
71-
self.lakeshore.write('RANGE 1,0') # Heater Off
72-
else:
73-
stability_log_callback(f"Heating... Current: {current_temp:.4f} K <= Target: {start_temp} K")
74-
self.lakeshore.write('RANGE 1,4') # Heater Medium
75-
self.lakeshore.write(f'SETP 1,{start_temp}')
76-
77-
if abs(current_temp - start_temp) < 0.1:
78-
stability_log_callback(f"Stabilized at {current_temp:.4f} K. Waiting 5s before ramp.")
79-
time.sleep(5)
80-
return
81-
time.sleep(2)
66+
def get_temperature(self):
67+
if not self.lakeshore: return 0.0
68+
return float(self.lakeshore.query('KRDG? A').strip())
69+
70+
def set_heater_range(self, output, heater_range):
71+
range_map = {'off': 0, 'low': 2, 'medium': 4, 'high': 5}
72+
range_code = range_map.get(heater_range.lower())
73+
if range_code is None: raise ValueError("Invalid heater range.")
74+
self.lakeshore.write(f'RANGE {output},{range_code}')
75+
def set_setpoint(self, output, temperature_k):
76+
self.lakeshore.write(f'SETP {output},{temperature_k}')
8277

8378
def start_ramp(self, end_temp, rate_k_min):
8479
self.lakeshore.write(f'SETP 1,{end_temp}')
@@ -138,9 +133,13 @@ def create_widgets(self):
138133
header = tk.Frame(self.root, bg=self.CLR_HEADER); header.pack(side='top', fill='x')
139134
ttk.Label(header, text=f"Active R-T Sweep (K2400) v{self.PROGRAM_VERSION}", style='Header.TLabel', font=self.FONT_TITLE).pack(side='left', padx=20, pady=10)
140135
main_pane = ttk.PanedWindow(self.root, orient='horizontal'); main_pane.pack(fill='both', expand=True, padx=10, pady=10)
141-
left_panel = self._create_left_panel(main_pane); main_pane.add(left_panel, weight=2)
142-
right_panel = self._create_right_panel(main_pane); main_pane.add(right_panel, weight=3)
143-
136+
137+
# --- FIX: Create both panels first, then add them to the PanedWindow ---
138+
left_panel = self._create_left_panel(main_pane)
139+
right_panel = self._create_right_panel(main_pane)
140+
main_pane.add(left_panel, weight=2)
141+
main_pane.add(right_panel, weight=3)
142+
144143
def _create_left_panel(self, parent):
145144
panel = ttk.Frame(parent, padding=5); panel.grid_columnconfigure(0, weight=1); panel.grid_rowconfigure(3, weight=1)
146145
self._create_info_panel(panel, 0)
@@ -235,16 +234,44 @@ def stop_experiment(self, reason=""):
235234
self.ax_main.set_title("Experiment stopped."); self.canvas.draw()
236235
if reason: messagebox.showinfo("Experiment Finished", f"Reason: {reason}")
237236

237+
# --- NON-BLOCKING HEATER LOGIC (from 6517B scripts) ---
238+
def _stabilization_loop(self):
239+
if self.experiment_state != 'stabilizing': return
240+
try:
241+
current_temp = self.backend.get_temperature()
242+
start_temp = self.params['start_temp']
243+
244+
if current_temp > start_temp + 0.2:
245+
self.log(f"Cooling... Current: {current_temp:.4f} K > Target: {start_temp} K")
246+
self.backend.set_heater_range(1, 'off')
247+
else:
248+
self.log(f"Heating... Current: {current_temp:.4f} K <= Target: {start_temp} K")
249+
self.backend.set_heater_range(1, 'medium')
250+
self.backend.set_setpoint(1, start_temp)
251+
252+
if abs(current_temp - start_temp) < 0.1:
253+
self.log(f"Stabilized at {current_temp:.4f} K. Waiting 5s before starting ramp...")
254+
self.experiment_state = 'ramping_setup'
255+
self.root.after(5000, self._experiment_loop) # Transition to next state
256+
else:
257+
self.root.after(2000, self._stabilization_loop) # Continue stabilizing
258+
except Exception as e:
259+
self.log(f"ERROR during stabilization: {e}"); self.stop_experiment("Stabilization Error")
260+
238261
def _experiment_loop(self):
239262
if self.experiment_state == 'idle': return
240263
try:
241264
if self.experiment_state == 'stabilizing':
242-
self.backend.stabilize_at_start(self.params['start_temp'], self.log)
265+
self._stabilization_loop()
266+
return # Let the after() calls manage the flow
267+
268+
elif self.experiment_state == 'ramping_setup':
243269
self.backend.start_ramp(self.params['end_temp'], self.params['rate'])
244270
self.log(f"Ramp started towards {self.params['end_temp']} K.")
245271
self.experiment_state = 'ramping'; self.start_time = time.time()
246-
self.root.after(100, self._experiment_loop)
247-
272+
self.root.after(100, self._experiment_loop) # Transition to measurement
273+
return
274+
248275
elif self.experiment_state == 'ramping':
249276
temp, voltage = self.backend.get_measurement()
250277
resistance = voltage / (self.params['current_ma'] * 1e-3) if self.params['current_ma'] != 0 else float('inf')
@@ -256,9 +283,11 @@ def _experiment_loop(self):
256283
self.line_main.set_data(self.data_storage['temperature'], self.data_storage['resistance'])
257284
self.ax_main.relim(); self.ax_main.autoscale_view(); self.figure.tight_layout(); self.canvas.draw()
258285

286+
# Check end conditions
259287
if temp >= self.params['cutoff']:
260288
self.stop_experiment(f"Safety cutoff reached at {temp:.2f} K.")
261-
elif temp >= self.params['end_temp']:
289+
elif (self.params['rate'] > 0 and temp >= self.params['end_temp']) or \
290+
(self.params['rate'] < 0 and temp <= self.params['end_temp']):
262291
self.stop_experiment("End temperature reached.")
263292
else:
264293
self.root.after(int(self.params['delay_s'] * 1000), self._experiment_loop)
@@ -275,7 +304,10 @@ def _validate_and_get_params(self):
275304
'compliance_v': float(self.entries["Compliance (V)"].get()), 'delay_s': float(self.entries["Logging Delay (s)"].get()),
276305
'k2400_visa': self.k2400_cb.get()}
277306
if not all([p for k, p in params.items() if k not in ['rate', 'cutoff']]): raise ValueError("A required field is empty.")
278-
if not (params['start_temp'] < params['end_temp'] < params['cutoff']): raise ValueError("Temperatures must be in order: start < end < cutoff.")
307+
if params['rate'] > 0 and not (params['start_temp'] < params['end_temp'] < params['cutoff']):
308+
raise ValueError("For heating, temperatures must be in order: start < end < cutoff.")
309+
if params['rate'] < 0 and not (params['start_temp'] > params['end_temp'] > params['cutoff']):
310+
raise ValueError("For cooling, temperatures must be in order: start > end > cutoff.")
279311
return params
280312
except Exception as e: raise ValueError(f"Invalid parameter input: {e}")
281313

0 commit comments

Comments
 (0)