From 36de4fcf677bd7d8843571ca1d035f77d1b1b913 Mon Sep 17 00:00:00 2001
From: unknown <asoalexandros@gmail.com>
Date: Fri, 21 Mar 2025 12:40:27 +0100
Subject: [PATCH] Stress and Sampling modes added!

---
 hp4155/Custom_SMU/lib/help.py          |  10 +
 hp4155/Custom_SMU/main.py              | 366 ++++++++++++++++++++++---
 hp4155/Custom_SMU/test_interface.ipynb |  31 ++-
 3 files changed, 359 insertions(+), 48 deletions(-)

diff --git a/hp4155/Custom_SMU/lib/help.py b/hp4155/Custom_SMU/lib/help.py
index 2b96c4c..ac3295b 100644
--- a/hp4155/Custom_SMU/lib/help.py
+++ b/hp4155/Custom_SMU/lib/help.py
@@ -163,6 +163,16 @@ def create_file(filename):
     root.destroy()
     return file
 
+# sampling help functions
+
+def check_cons_smu_samp(smus:list):
+    cons_smu_numbers = []
+    for i,smu in enumerate(smus):
+        if smu['mode']!='COMM': # Non Grounded SMUs
+            cons_smu_numbers.append(i+1) # for the actual number of smus
+
+    return cons_smu_numbers
+    
 
     
     
\ No newline at end of file
diff --git a/hp4155/Custom_SMU/main.py b/hp4155/Custom_SMU/main.py
index d27315e..c93e5bc 100644
--- a/hp4155/Custom_SMU/main.py
+++ b/hp4155/Custom_SMU/main.py
@@ -3,6 +3,7 @@ sys.path.insert(0, './lib')
 sys.path.insert(0, '..') #append parent directory
 import os
 import configparser
+import warnings
 
 from interface import *
 from help import *
@@ -31,6 +32,7 @@ device = hp4155a.HP4155a('GPIB0::17::INSTR')
 
 def on_start_clicked(b):
     with output:
+        clear_output()
         change_state(first_page,second_page,third_page,fourth_page,fifth_page)
 
         # Reset the device
@@ -52,7 +54,43 @@ def on_start_clicked(b):
         cons_smus = []
         for j in range(1,5):
             cons_smus.append(create_dict(value = third_page[21,j].value,comp=third_page[22,j].value))
-        
+
+        # 2nd page
+        user_functions = []
+        #iterate over the rows
+        for i in range(1,7):
+            if second_page[i,0]!="": # do not save user functions without names
+                user_functions.append(
+                    create_dict(
+                        name = second_page[i,0].value,
+                        unit=second_page[i,1].value,
+                        expression = second_page[i,2].value
+                    )
+                )
+
+         # Page 4
+        axes = [] # 0 is x-axis , 1 is y1-axis and 2 is y2-axis
+        for j in range(1,4): #iterate over the column
+            axes.append(
+                create_dict(
+                    name = fourth_page[1,j].value,
+                    scale = fourth_page[2,j].value,
+                    min =fourth_page[3,j].value,
+                    max = fourth_page[4,j].value
+                )
+            )
+
+        # Page 5
+        variables = []
+        for i in range(8):
+            if fifth_page[6+i,0].value!="": # do not save empty rows
+                variables.append(
+                    create_dict(
+                        name =  fifth_page[6+i,0].value,
+                        unit =fifth_page[6+i,1].value
+                    )
+                )
+    
 
         # first sweep
         if measurement_mode == 'SWEEP':
@@ -71,21 +109,8 @@ def on_start_clicked(b):
                 )
             
            
-            # 2nd page
-            user_functions = []
-            #iterate over the rows
-            for i in range(1,7):
-                if second_page[i,0]!="": # do not save user functions without names
-                    user_functions.append(
-                        create_dict(
-                            name = second_page[i,0].value,
-                            unit=second_page[i,1].value,
-                            expression = second_page[i,2].value
-                        )
-                    )
-
+            
             # Page 3
-    
             #match hysteris checkbox with the command forwarded to the tool
             if third_page[9,0].value == True:
                 hyst = 'DOUB'
@@ -119,30 +144,6 @@ def on_start_clicked(b):
                 width = third_page[5,3].value,
                 base = third_page[6,3].value
             )
-               
-    
-            # Page 4
-            axes = [] # 0 is x-axis , 1 is y1-axis and 2 is y2-axis
-            for j in range(1,4): #iterate over the column
-                axes.append(
-                    create_dict(
-                        name = fourth_page[1,j].value,
-                        scale = fourth_page[2,j].value,
-                        min =fourth_page[3,j].value,
-                        max = fourth_page[4,j].value
-                    )
-                )
-    
-            # Page 5
-            variables = []
-            for i in range(8):
-                if fifth_page[6+i,0].value!="": # do not save empty rows
-                    variables.append(
-                        create_dict(
-                            name =  fifth_page[6+i,0].value,
-                            unit =fifth_page[6+i,1].value
-                        )
-                    )
 
             # Now execute measurement
 
@@ -223,20 +224,20 @@ def on_start_clicked(b):
             # Save results
             df = pd.Dataframe(values)
 
-            filename = f"{sample_series}_{field}_{dut}_{measurement_name}_sweep.txt"
+            filename = f"{sample_series}_{field}_{dut}_{measurement_name}.txt"
             txt_file = create_file(filename)
             ini_file = os.path.splitext(txt_file)[0]+'.ini'
 
             with open(txt_file,'w') as f:
                 date = str(datetime.today().replace(microsecond=0))
-                f.write(f"{measurement_name} at {date}:"+"\n")
+                f.write(f"{measurement_name} (sweep) at {date}:"+"\n")
                 f.write("Sample Information\n")
                 f.write(f"Processing Number:{proceccing_nr}"+"\n")
                 f.write(f"Sample Series:{sample_series}"+"\n")
                 f.write(f"Field:{field}"+"\n")
                 f.write(f"DUT:{dut}"+"\n")
                 f.write("\n")
-                f.write(f"Measurement arameters can be found at: {ini_file}"+"\n")
+                f.write(f"Measurement parameters can be found at: {ini_file}"+"\n")
                 f.write("\nResults\n")
             
             df.to_csv(txt_file,sep=" ",mode='a')
@@ -313,11 +314,290 @@ def on_start_clicked(b):
                 interval=third_page[13,0].value,
                 hold=third_page[16,0].value,
                 points=third_page[14,0].value,
-                filter=third_page[17,0].value
+                filter=int(third_page[17,0].value)
             )
             duration = third_page[13,0].value
 
+            # Set the smus all constant
+            #page 1
+            smus = []
+            #iterate over the rows and set the function to constant
+            for i in range(1,5):
+                smus.append(
+                    create_dict(
+                        vname = first_page[i,1].value,
+                        iname = first_page[i,2].value,
+                        mode = first_page[i,3].value,
+                        func = 'CONS',
+                        disabled = first_page[i,5].value
+                    )
+                )
+            
+            # Now start the measurement
+            device.measurement_mode('SAMP')
+
+            #disable all irrelevant units
+            device.disable_not_smu()
+
+            # First Setup Smus
+            for i,smu in enumerate(smus):
+                if smu['disabled'] == False:
+                    device.setup_smu(i+1,smu)
+                else: #disabled
+                    device.smu_disable(i+1)
+                
+            
+            # Setup User Functions
+            for user_function in user_functions:
+                device.user_function(user_function['name'],user_function['unit'],user_function['expression'])     
+
+            # Set the integration time
+            device.integration_time(integration)
+
+            # Set the sampling parameters
+            device.setup_sampling(parameters)
+
+            # Set the total sampling time
+            if duration<= 0:
+                warnings.warn("Non positive measurement duration. Auto Sampling time will be set")
+                device.auto_sampling_time(1)
+            else:
+                device.total_sampling_time(duration)
+
+            # Setup the constant SMUs
+            cons_smu_numbers = check_cons_smu_sampling(smus)
+            for i, cons_smu in enumerate(smus):
+                if i+1 in cons_smu_numbers: # check if the constant smu was found in the first page func
+                    device.setup_smu_sampling(i+1,cons_smu)
+
+            # Set integration time
+            device.intergration_time(integration_time)
         
+
+
+            # Now set the axes
+            setup_axes(axes,device)
+
+            # Set the variables to be saved (This function has an exemption)
+            try:
+                save_variables(axes,variables,device)
+            except Exception as e:
+                error_box(e)
+                change_state(first_page,second_page,third_page,fourth_page,fifth_page)
+                return
+                
+
+            # Start the measurement
+            device.single_measurement()
+            while device.operation_completed()==False:
+                pass
+    
+            device.autoscaling()
+
+            # List all errors occured
+            counter,message = device.list_all_errors()
+            if counter>1:
+                error_box(message)
+                change_state(first_page,second_page,third_page,fourth_page,fifth_page)
+                return
+            
+
+            # Get the data from the device 
+            # These are correcly defined and include the plotted ones
+            values = {}
+            for variable in variables:
+                key = f"{variable['name']} ({variable['unit']})"
+                values[key] = device.return_data(variable['name'])
+
+            plot_results(values,device)
+
+            # Save results
+            df = pd.Dataframe(values)
+
+            filename = f"{sample_series}_{field}_{dut}_{measurement_name}_sweep.txt"
+            txt_file = create_file(filename)
+            ini_file = os.path.splitext(txt_file)[0]+'.ini'
+
+            with open(txt_file,'w') as f:
+                date = str(datetime.today().replace(microsecond=0))
+                f.write(f"{measurement_name} (sampling) at {date}:"+"\n")
+                f.write("Sample Information\n")
+                f.write(f"Processing Number:{proceccing_nr}"+"\n")
+                f.write(f"Sample Series:{sample_series}"+"\n")
+                f.write(f"Field:{field}"+"\n")
+                f.write(f"DUT:{dut}"+"\n")
+                f.write("\n")
+                f.write(f"Measurement parameters can be found at: {ini_file}"+"\n")
+                f.write("\nResults\n")
+            
+            df.to_csv(txt_file,sep=" ",mode='a')
+
+            # export interface to ini file
+            # First the Smus
+            config = configparser.ConfigParser()
+            with open(ini_file,'w') as configfile:
+                config.add_section("THESE ARE THE PARAMETERS OF THE CORRESPONDING MEASUREMENT")
+                config.add_section("THE WHOLE INTERFACE IS SAVED SO FIND WHAT YOU NEED")
+                config.add_section("DO NOT MODIFY THIS FILE")
+                config.add_section(f"MEASUREMENT MODE: {measurement_mode}")
+                config.add_section(f"INTEGRATION TIME: {integration}")
+                
+                # First the smus
+                config.add_section("SMUS")
+                for i, smu in enumerate(smus):
+                    config.add_section(f"SMU{i+1}")
+                    for key,value in smu.items():
+                        config.set(f"SMU{i+1}",key,value)
+
+                # Secondly the user functions
+                config.add_section("USER FUNCTIONS")
+                for i, user_function in enumerate(user_functions):
+                    config.add_section(f"USER FUNCTION {i+1}")
+                    for key,value in user_function.items():
+                        config.set(f"USER FUNCTION {i+1}",key,value)
+
+                # Then the 3rd page
+                config.add_section('SAMPLING PARAMETERS')
+                for key,value in parameters.items():
+                    config.set('SAMPLING PARAMETERS',key,value)
+
+                # Now the constant smus
+                config.add_section('CONSTANT SMUS')
+                for i, cons_smu in enumerate(cons_smus):
+                    config.add_section(f"CONSTANT SMU{i+1}")
+                    for key, value in cons_smu.items():
+                        config.set(f"CONSTANT SMU{i+1}",key,value)
+                
+
+                # Page 4 The axes
+                config.add_section('AXES')
+                for i,axis in enumerate(axes):
+                    config_add_section(f"AXIS {i+1}")
+                    for key,value in axis.items():
+                        config.set(f"AXIS {i+1}",key,value)
+                
+                # Page 5 The varibles
+                config.add_section("SAVED VARIABLES")
+                for i, variable in enumerate(variables):
+                    config.add_section(f"VARIABLE {i+1}")
+                    for key,value in variable.items():
+                        config.set(f"VARIABLE {i+1}",key,value)
+
+                config.write(configfile)
+        else: # Stress
+            #page 1
+            smus = []
+            #iterate over the rows and set the function to constant
+            for i in range(1,5):
+                # Set SYNC non SYNC mode
+                mode= first_page[i,3].value
+                if mode != 'COMM':
+                    func = 'SYNC'
+                else:
+                    func = 'NSYNC'
+                smus.append(
+                    create_dict(
+                        name = first_page[i,1].value,
+                        mode = mode,
+                        func = func,
+                        disabled = first_page[i,5].value
+                    )
+                )
+
+            # Now define the parameters
+            duration = third_page[13,0].value
+            if duration <= 0:
+                error_box("Stress Time should be positive!")
+                change_state(first_page,second_page,third_page,fourth_page,fifth_page)
+                return
+            hold_time = third_page[16,0].value
+            filter = int(third_page[17,0].value)
+
+            # Now start the measurement
+            device.stress_page()
+            device.stress_disable_not_smu()
+
+            # First Setup Smus
+            for i,smu in enumerate(smus):
+                if smu['disabled'] == False:
+                    device.smu_stress(i+1,smu)
+                else: #disabled
+                    device.smu_stress_disable(i+1)
+            
+            # Now set the Parameters
+            device.stress_filter(filter)
+            device.hold_time(hold_time)
+            device.stress_time(duration)
+
+            cons_smu_numbers = check_cons_smu_sampling(smus) # works also for sampling
+            for i, cons_smu in enumerate(smus):
+                if i+1 in cons_smu_numbers: # check if the constant smu was found in the first page func
+                    device.setup_smu_stress(i+1,cons_smu)
+        
+            # Now start the measurement
+            device.start_stress()
+            while device.operation_completed() == False:
+                pass
+            
+            # List all errors occured
+            counter,message = device.list_all_errors()
+            if counter>1:
+                error_box(message)
+                change_state(first_page,second_page,third_page,fourth_page,fifth_page)
+                return
+
+            filename = f"{sample_series}_{field}_{dut}_{measurement_name}_sweep.txt"
+            txt_file = create_file(filename)
+            ini_file = os.path.splitext(txt_file)[0]+'.ini'
+
+            with open(txt_file,'w') as f:
+                date = str(datetime.today().replace(microsecond=0))
+                f.write(f"{measurement_name} (Stress) at {date}:"+"\n")
+                f.write("Sample Information\n")
+                f.write(f"Processing Number:{proceccing_nr}"+"\n")
+                f.write(f"Sample Series:{sample_series}"+"\n")
+                f.write(f"Field:{field}"+"\n")
+                f.write(f"DUT:{dut}"+"\n")
+                f.write("\n")
+                f.write(f"Measurement parameters can be found at: {ini_file}"+"\n")
+                f.write("\nNo Results Available\n")
+            
+            #export the interface to ini file
+            config = configparser.ConfigParser()
+            with open(ini_file,'w') as configfile:
+                config.add_section("THESE ARE THE PARAMETERS OF THE CORRESPONDING MEASUREMENT")
+                config.add_section("THE WHOLE INTERFACE IS SAVED SO FIND WHAT YOU NEED")
+                config.add_section("DO NOT MODIFY THIS FILE")
+                config.add_section(f"MEASUREMENT MODE: {measurement_mode}")
+                
+                # First the smus
+                config.add_section("SMUS")
+                for i, smu in enumerate(smus):
+                    config.add_section(f"SMU{i+1}")
+                    for key,value in smu.items():
+                        config.set(f"SMU{i+1}",key,value)
+
+                config.add_section('PARAMETERS')
+                config.set('PARAMETERS', "STRESS TIME",duration)
+                config.set('PARAMETERS', "HOLD TIME",hold_time)
+                config.set('PARAMETERS', "FILTER",filter)
+        
+        
+                # Now the constant smus
+                config.add_section('CONSTANT SMUS')
+                for i, cons_smu in enumerate(cons_smus):
+                    config.add_section(f"CONSTANT SMU{i+1}")
+                    for key, value in cons_smu.items():
+                        config.set(f"CONSTANT SMU{i+1}",key,value)
+                config.write(configfile)
+
+        # End of fuction
+        information_box("Measurement finished!")
+        change_state(first_page,second_page,third_page,fourth_page,fifth_page)
+        return # just to be sure
+
+            
+
 start.on_click(on_start_clicked)
 
 
diff --git a/hp4155/Custom_SMU/test_interface.ipynb b/hp4155/Custom_SMU/test_interface.ipynb
index 34839db..e660df2 100644
--- a/hp4155/Custom_SMU/test_interface.ipynb
+++ b/hp4155/Custom_SMU/test_interface.ipynb
@@ -2,14 +2,14 @@
  "cells": [
   {
    "cell_type": "code",
-   "execution_count": 2,
+   "execution_count": 1,
    "id": "8c823003-1b29-4ece-a0cb-e693a1cd0402",
    "metadata": {},
    "outputs": [
     {
      "data": {
       "application/vnd.jupyter.widget-view+json": {
-       "model_id": "6427bccee9fd48e58312921affdeb9b3",
+       "model_id": "8d3ae4e04b93409d9e2c2ae411d086e3",
        "version_major": 2,
        "version_minor": 0
       },
@@ -23,7 +23,7 @@
     {
      "data": {
       "application/vnd.jupyter.widget-view+json": {
-       "model_id": "a34bc4bbbc7f44eb8a20a124af8cc604",
+       "model_id": "13e68e30196f47168d03fe727bf4c898",
        "version_major": 2,
        "version_minor": 0
       },
@@ -37,7 +37,7 @@
     {
      "data": {
       "application/vnd.jupyter.widget-view+json": {
-       "model_id": "06a413304448487fad402f1b345badb1",
+       "model_id": "73798f7b542a425f99b55e1c38154150",
        "version_major": 2,
        "version_minor": 0
       },
@@ -79,9 +79,30 @@
   },
   {
    "cell_type": "code",
-   "execution_count": null,
+   "execution_count": 4,
    "id": "aae3eef8-c2d2-424a-8b10-230f7d8cd67a",
    "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "1"
+      ]
+     },
+     "execution_count": 4,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "int(True)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "e8b4ed5c-3f37-4d87-b77a-a4003b9b271d",
+   "metadata": {},
    "outputs": [],
    "source": []
   }
-- 
GitLab