From c6566c318de2510d4ea79534880390e94098c0f2 Mon Sep 17 00:00:00 2001
From: Nicolas Roisin <nicolas.roisin@uclouvain.be>
Date: Fri, 28 Mar 2025 14:05:20 +0100
Subject: [PATCH] data analysis

---
 data_analysis/data_processing.py              | 138 +++++++++++++--
 data_analysis/file_handling.py                |  32 +++-
 data_analysis/raman.py                        | 167 ++++++++++++++++--
 data_analysis/semiconductor.py                |   9 +
 .../__pycache__/sh242.cpython-311.pyc         | Bin 0 -> 9335 bytes
 .../example/single_equipment/sh242_script.py  |  14 +-
 equipment_control/sh242.py                    |   6 +-
 7 files changed, 320 insertions(+), 46 deletions(-)
 create mode 100644 data_analysis/semiconductor.py
 create mode 100644 equipment_control/__pycache__/sh242.cpython-311.pyc

diff --git a/data_analysis/data_processing.py b/data_analysis/data_processing.py
index 9c454dc..985019d 100644
--- a/data_analysis/data_processing.py
+++ b/data_analysis/data_processing.py
@@ -4,16 +4,55 @@ import scipy.signal as signal
 from scipy.optimize import curve_fit
 
 def moving_median(x,window_length=3):
+    """ Function to perform a median filter on a array
+    
+        args:
+            - x (array_like) : the input array.
+            - window_length (scalar) : size of the median filter window.    
+        return:
+            - an array the same size as input containing the median filtered result.
+    """
     return signal.medfilt(x,window_length)
 
 def smooth(x, window_length=10, polyorder=2):
+    """ Function to smooth an array by applying a Savitzky-Golay filter
+    
+        args:
+            - x (array_like) : the data to be filtered.
+            - window_length (scalar) : the length of the filter window .    
+            - polyorder (scalar) : the order of the polynomial used to fit the samples. polyorder must be less than window_length.   
+        return:
+            - an array the same size as input containing the filtered result.
+    """    
     return signal.savgol_filter(x,window_length,polyorder)
 
+
 def interpolate(x,y,x_interp,kind="cubic"):
+    """ Function to interpolate an 1-D array
+    
+        args:
+            - x (array_like) : a 1-D array of real values.
+            - y (array_like) : a 1-D array of real values of the same dimension of x.
+            - x_interp (array_like) : a 1-D array of real values of the any dimension but with all values include between the max and min value of x.
+            - kind (string or integer) : Specifies the kind of interpolation as a string specifying the order of the spline interpolator to use. The string has to be one of ‘linear’, ‘nearest’, ‘nearest-up’, ‘zero’, ‘slinear’, ‘quadratic’, ‘cubic’, ‘previous’, or ‘next’. 
+        return:
+            - an array the same size as x_interp containing the interpolated result.
+    """    
     f=interp1d(x, y,kind=kind)    
     return f(x_interp)
 
 def remove_baseline(x,y,xmin_baseline,xmax_baseline,polyorder=2):
+    """ Function to remove the baseline a 1-D array
+    
+        args:
+            - x (array_like) : a 1D input array.
+            - y (array_like) : a 1D  input array with the same dimension of x.
+            - xmin_baseline (scalar or array) : a scalar or array of values to set the minimum value of the data ranges on which the baseline is calculated. Several windows can be specified by specifying an array of values
+            - xmax_baseline (scalar or array) : a scalar or array of values to set the maximum value of the data ranges on which the baseline is calculated. Several windows can be specified by specifying an array of values.
+            - polyorder (scalar) : the order of the polynome to calculate the baseline    
+        return:
+            - a tuple (corrected_data, baseline) with the corrected data and the baseline calculated.
+    """       
     index=[False]*len(x)
     for i in range(len(xmin_baseline)):
         index = index or ((x>=xmin_baseline[i]) & (x<=xmax_baseline[i]) )
@@ -21,45 +60,118 @@ def remove_baseline(x,y,xmin_baseline,xmax_baseline,polyorder=2):
     baseline=np.polyval(p,x)
     return y-baseline, baseline
 
+def lorentzian(x,x0,A,W):
+    """ Lorentzian function 
+    
+        args:
+            - x (array_like) : a 1D input array.
+            - x0 (scalar) : the position of the Lorentzian function
+            - A (scalar) : the amplitude of the Lorentzian function
+            - W (scalar) : the full width at half maximum (FWHM) of the Lorentzian function
+        return:
+            - an array of the same dimension as x
+    """ 
+    return A/(1+((x-x0)/(W/2))**2)
+
+def lorentzian2(x,x0,x01,A,A1,W,W1):
+    """ Function with the sum of two Lorentzian 
+    
+        args:
+            - x (array_like) : a 1D input array.
+            - x01 (scalar) : the position of the first Lorentzian function
+            - A1 (scalar) : the amplitude of the first Lorentzian function
+            - W1 (scalar) : the full width at half maximum (FWHM) of the first Lorentzian function
+            - x02 (scalar) : the position of the second Lorentzian function
+            - A2 (scalar) : the amplitude of the second Lorentzian function
+            - W2 (scalar) : the full width at half maximum (FWHM) of the second Lorentzian function
+        return:
+            - an array of the same dimension as x
+    """ 
+    return A/(1+((x-x0)/(W/2))**2)+A1/(1+((x-x01)/(W1/2))**2)
+
 def fit_lorentzian(x,y,xmin=None,xmax=None,x0=520.7,A0=1,W0=3):
+    """ Function to fit the data with a Lorentzian function 
+    
+        args:
+            - x (array_like) : a 1D input array.
+            - y (array_like) : a 1D  input array with the same dimension of x.
+            - xmin (scalar) : a scalar or array of values to set the minimum value of the data range on which the Lorentzian is calculated. 
+            - xmax (scalar) : a scalar or array of values to set the maximum value of the data range on which the Lorentzian is calculated. .
+            - x0 (scalar) : the starting position of the Lorentzian function for the fit
+            - A (scalar) : the starting amplitude of the Lorentzian function for the fit
+            - W (scalar) : the starting full width at half maximum (FWHM) of the Lorentzian function for the fit
+        return:
+            - a tuple (parameter_dictionary, data_lorentzian) with the a dictionary containing the fitting parameters ("position", "amplitude" and "width") and the corresponding Lorentzian function evaluated in x.
+    """
     if (xmin is None) and (xmax is None):
         x_fit=x
         y_fit=y
     else:
-        index=(x>xmin) & (x<xmax) 
+        index=(x>=xmin) & (x<=xmax) 
         x_fit=x[index]
         y_fit=y[index]
     p_lorentzian=curve_fit(lorentzian, x_fit, y_fit, p0=[x0,A0,W0])[0]
     return {"position":p_lorentzian[0],"amplitude":p_lorentzian[1],"width":p_lorentzian[2]}, lorentzian(x,*p_lorentzian)
 
 def fit_lorentzian_2peaks(x,y,xmin=None,xmax=None,x0=(520,520.7),A0=(1,1),W0=(3,3)):
+    """ Function to fit the data with a Lorentzian function 
+    
+        args:
+            - x (array_like) : a 1D input array.
+            - y (array_like) : a 1D  input array with the same dimension of x.
+            - xmin (scalar) : a scalar or array of values to set the minimum value of the data range on which the Lorentzian is calculated. 
+            - xmax (scalar) : a scalar or array of values to set the maximum value of the data range on which the Lorentzian is calculated. .
+            - x0 (tuple) : the two starting position of the double Lorentzian function for the fit
+            - A (tuple) : the two starting amplitude of the double Lorentzian function for the fit
+            - W (tuple) : the two starting full width at half maximum (FWHM) of the double Lorentzian function for the fit
+        return:
+            - a tuple (parameter_dictionary, data_lorentzian) with the a dictionary containing the fitting parameters ("position", "amplitude" and "width") and the corresponding double Lorentzian function evaluated in x.
+        """
     if (xmin is None) and (xmax is None):
         x_fit=x
         y_fit=y
     else:
-        index=(x>xmin) & (x<xmax) 
+        index=(x>=xmin) & (x<=xmax) 
         x_fit=x[index]
         y_fit=y[index]
     p_lorentzian=curve_fit(lorentzian2, x_fit, y_fit, p0=np.reshape([x0,A0,W0],newshape=6))[0]
     return {"position":p_lorentzian[:2],"amplitude":p_lorentzian[2:4],"width":p_lorentzian[4:]}, lorentzian(x,*p_lorentzian)
 
-def lorentzian(x,x0,A,W):
-    return A/(1+((x-x0)/(W/2))**2)
-
-def lorentzian2(x,x0,x01,A,A1,W,W1):
-    return A/(1+((x-x0)/(W/2))**2)+A1/(1+((x-x01)/(W1/2))**2)
 
-def gaussian(x, x0, w, a):
-    return a*np.exp(-np.power(x - x0, 2.) / (2 * np.power(w, 2.))) / (w * np.sqrt(2*np.pi))
+def gaussian(x, x0, A, W):
+    """ Faussian function 
+    
+        args:
+            - x (array_like) : a 1D input array.
+            - x0 (scalar) : the position of the Gaussian function
+            - A (scalar) : the amplitude of the Lorentzian function
+            - W (scalar) : the full width at half maximum (FWHM) of the Lorentzian function
+        return:
+            - an array of the same dimension od x
+    """ 
+    return A*np.exp(-np.power(x - x0, 2.) / (2 * np.power(W, 2.))) / (W * np.sqrt(2*np.pi))
 
-def fit_gaussian(x,y,xmin=None,xmax=None,x0=0,w=1,a=1):
+def fit_gaussian(x,y,xmin=None,xmax=None,x0=0,A=1,W=1):
+    """ Function to fit the data with a Lorentzian function 
+    
+        args:
+            - x (array_like) : a 1D input array.
+            - y (array_like) : a 1D  input array with the same dimension of x.
+            - xmin (scalar) : a scalar or array of values to set the minimum value of the data range on which the Gaussian is calculated. 
+            - xmax (scalar) : a scalar or array of values to set the maximum value of the data range on which the Gaussian is calculated. .
+            - x0 (scalar) : the starting position of the Gaussian function for the fit
+            - A (scalar) : the starting amplitude of the Gaussian function for the fit
+            - W (scalar) : the starting full width at half maximum (FWHM) of the Gaussian function for the fit
+        return:
+            - a tuple (parameter_dictionary, data_gaussian) with the a dictionary containing the fitting parameters ("position", "amplitude" and "width") and the corresponding Gaussian function evaluated in x.
+    """
     if (xmin is None) and (xmax is None):
         x_fit=x
         y_fit=y
     else:
-        index=(x>xmin) & (x<xmax) 
+        index=(x>=xmin) & (x<=xmax) 
         x_fit=x[index]
         y_fit=y[index]
 
-    p_lorentzian=curve_fit(gaussian, x_fit, y_fit, p0=[x0,w,a])[0]
-    return {"position":p_lorentzian[0],"width":p_lorentzian[1],"amplitude":p_lorentzian[2]}, gaussian(x,*p_lorentzian)
+    p_gaussian=curve_fit(gaussian, x_fit, y_fit, p0=[x0,A,W])[0]
+    return {"position":p_gaussian[0],"width":p_gaussian[1],"amplitude":p_gaussian[2]}, gaussian(x,*p_gaussian)
diff --git a/data_analysis/file_handling.py b/data_analysis/file_handling.py
index a41aaf0..b417041 100644
--- a/data_analysis/file_handling.py
+++ b/data_analysis/file_handling.py
@@ -1,22 +1,38 @@
-
-import sys, os
 import numpy as np
 import datetime   
 
-def __init__(self,workdir=None):
-    if workdir==None:
-        self.workdir=os.path.dirname(sys.argv[0])
+comments="#"
+delimiter=","
+skip_header=0
+skip_footer=0
+max_rows=None
+usecols=None
+deletechars=" !#$%&'()*+, /:;<=>?@[\\]^{|}~"
 
-def read_file(self,file_path,comments=comments,delimiter=delimiter, skip_header=skip_header,skip_footer=skip_footer,
+def read_file(file_path,comments=comments,delimiter=delimiter, skip_header=skip_header,skip_footer=skip_footer,
                     max_rows=max_rows,usecols=usecols, deletechars=deletechars):
-      
+    """ Function to read data from a file
+    
+        args:
+            - file_path (string) : file, filename, list, or generator to read.
+            - comments (char) : the character used to indicate the start of a comment.
+            - delimiter (char) : the string used to separate values.
+            - skip_header (integer or sequence) : the number of lines to skip at the beginning of the file.
+            - skip_footer (integer or sequence) : the number of lines to skip at the end of the file.
+            - max_rows (integer) : the maximum number of rows to read. Must not be used with skip_footer at the same time.
+            - usecols (sequence) : which columns to read, with 0 being the first. For example, usecols = (1, 4, 5) will extract the 2nd, 5th and 6th columns.
+            - deletechars (string) : a string combining invalid characters that must be deleted from the names.
+    
+        return:
+            - data (numpy array): numpy array of the data read from the file
+    """          
     data=np.genfromtxt(file_path,comments=comments,delimiter=delimiter,
                         skip_header=skip_header,skip_footer=skip_footer,
                         max_rows=max_rows,usecols=usecols, deletechars=deletechars)
     
     return data
     
-def write_in_file(self,file_path,data,delimiter=",",overwrite=False,header=None,date=True, comment="#"):
+def write_in_file(file_path,data,delimiter=",",overwrite=False,header=None,date=True, comment="#"):
     """ Function to write data in a file
     
         args:
diff --git a/data_analysis/raman.py b/data_analysis/raman.py
index e3afb7b..aca71de 100644
--- a/data_analysis/raman.py
+++ b/data_analysis/raman.py
@@ -1,20 +1,157 @@
+import numpy as np
+import matplotlib.pyplot as plt
+import scipy.signal
+import file_handling
 
-
-class raman:
-    peak_dic={"Si":[520.7],"VO2":[140, 192, 223, 260, 308, 338, 387, 395, 440, 482, 497, 588, 613, 661, 823]}
-
-    def find_peaks(self,x,y):
-
-    def find_peaks_from_file(self,file_path):
+def phonon_deformation_silicon(strain_matrix,p,q,r,sort=False,w0=None):
+    """ Function to calculate the Raman shift from an arbitrary strain tensor using Phonon Deformation Potential (PDP) theory
+    
+        args:
+            - strain_matrix (3x3 matrix) : the strain tensor
+            - p (scalar) : the first phonon deformation potential    
+            - q (scalar) : the second phonon deformation potential.    
+            - r (scalar) : the third phonon deformation potential  
+            - sort (boolean) : if true, sort the phonon modes from lowest to highest energy
+            - w0 (scalar) : reference energy at zero strain, 520.7 cm-1 for silicon
+        return:
+            - an array of three elements with the energy of the three phonon modes (LO, TO1, and TO2).
+    """
+    if w0==None:
+        w0=520.7 # in cm-1
         
-    def plot_with_peaks(self,x,y,ax=None,material=None):
-        if material not in peak_dic.keys():
-            print("This material is not available in the raman peaks database.\nAvailable material are: %s"%peak_dic.keys())
+    p=p*w0**2
+    q=q*w0**2
+    r=r*w0**2
+    exx=strain_matrix[0,0]
+    eyy=strain_matrix[1,1]
+    ezz=strain_matrix[2,2]
+    eyz=strain_matrix[1,2]
+    exz=strain_matrix[0,2]
+    exy=strain_matrix[0,1]
+    ezy=strain_matrix[2,1]
+    ezx=strain_matrix[2,0]
+    eyx=strain_matrix[1,0]
+    P=np.array([[p*exx+q*(eyy+ezz),2*r*exy,2*r*exz],
+                [2*r*eyx,p*eyy+q*(ezz+exx),2*r*eyz],
+                [2*r*ezx,2*r*ezy,p*ezz+q*(eyy+exx)]])
+    det=np.linalg.det(P)
+    tr=np.trace(P)
+    tr2=np.trace(P.dot(P))
+    
+    if sort : 
+        lamb=np.sort(np.roots([1,-tr,-0.5*(tr2-tr**2),-det]))
         
+    else :
+        lamb=np.roots([1,-tr,-0.5*(tr2-tr**2),-det])
+    return np.sqrt(lamb+w0**2)
 
-    def plot_from_file(self,file_path,ax=None):
-
-    def plot_from_multiple_files(self,file_paths,ax=None):
-
+def find_peaks(x,y,height=None, threshold=None, distance=None, width=None):
+    """ Function to find peaks in the Raman spectrum
+    
+        args:
+            - x (array) : an array 
+            - y (array) : the signal with peaks with the same dimension as x
+            - height (None, scalar or 2-element sequence) : required height of peaks. Either a number, None, an array matching x or a 2-element sequence of the former. The first element is always interpreted as the minimal and the second, if supplied, as the maximal required height.
+            - threshold (None, scalar or 2-element sequence) : required threshold of peaks, the vertical distance to its neighboring samples. Either a number, None, an array matching x or a 2-element sequence of the former. The first element is always interpreted as the minimal and the second, if supplied, as the maximal required threshold.
+            - distance (scalar) : Required minimal horizontal distance in samples between neighbouring peaks. The distance should be higher than the step between two adjacents points from x. Smaller peaks are removed first until the condition is fulfilled for all remaining peaks.
+            - width (None, scalar or 2-element sequence) : required width of peaks in samples. Either a number, None, an array matching x or a 2-element sequence of the former. The first element is always interpreted as the minimal and the second, if supplied, as the maximal required width.
+        
+        return:
+            - (positions, heights, widths) : an array of three elements with the positions, the heights and the widths of the peaks
+    """
+    dx=(np.max(x)-np.max(y))/len(x)
+    distance_index=int(np.round(distance/dx))
+    
+    peaks_index,properties=scipy.signal.find_peaks(y,height=height, threshold=threshold, distance=distance_index, width=width)
+    return x[peaks_index],properties["peak_heights"],properties["widths"]
+    
+def plot_with_peaks(x,y,ax=None,height=None, threshold=None, distance=None, width=None,*ax_kwargs,**plot_kwargs):
+    """ Function to plot the Raman spectrum with the peaks indicated
+    
+        args:
+            - x (array) : an array for the energy of the Raman spectrum
+            - y (array) : the Raman signal with peaks with the same dimension as x
+            - ax (Axes) : the axes of the subplot in which plotting the Raman signal and peaks
+            - height (None, scalar or 2-element sequence) : required height of peaks. Either a number, None, an array matching x or a 2-element sequence of the former. The first element is always interpreted as the minimal and the second, if supplied, as the maximal required height.
+            - threshold (None, scalar or 2-element sequence) : required threshold of peaks, the vertical distance to its neighboring samples. Either a number, None, an array matching x or a 2-element sequence of the former. The first element is always interpreted as the minimal and the second, if supplied, as the maximal required threshold.
+            - distance (scalar) : Required minimal horizontal distance in samples between neighbouring peaks. The distance should be higher than the step between two adjacents points from x. Smaller peaks are removed first until the condition is fulfilled for all remaining peaks.
+            - width (None, scalar or 2-element sequence) : required width of peaks in samples. Either a number, None, an array matching x or a 2-element sequence of the former. The first element is always interpreted as the minimal and the second, if supplied, as the maximal required width.
+            - *ax_kwargs : this method also takes any keyword argument for the Figure.add_sublot() function such as xlabel, ylabel, xticks, yticks, xlim, ylim, xscale, yscale, ...
+            - **plot_kwargs : this method also takes any keyword argument for the Axes.plot() function such as linewidth, linestyle, marker, markersize, color, markeredgecolor, zorder, alpha, label, clip_on, ...
+            
+        return:
+            - fig, ax : the Figure and Axes object created if None is specified for ax
+    """
     
-            
\ No newline at end of file
+    if ax==None:
+        fig=plt.figure()
+        ax=fig.add_subplot(*ax_kwargs)
+    
+    ax.plot(x,y,**plot_kwargs)
+    position, heights, width=find_peaks(x,y,height=height, threshold=threshold, distance=distance, width=width)
+    ax.plot(position,height,marker="o",ls="",color="k")
+    for i in range(len(height)):
+        ax.plot([position[i],position[i]],[0,height[i]],marker="",ls="--",color="k")
+        ax.text(position[i],1.2*height[i],"%.2f cm$^{-1}$"%position[i],ha="center",va="bottom",rotation="vertical")
+    if ax==None:
+        return fig, ax
+def plot_from_file(file_path,ax=None,with_peaks=False,height=None, threshold=None, distance=None, width=None,*ax_kwargs,**plot_kwargs):
+    """ Function to plot the Raman spectrum with the peaks indicated
+    
+        args:
+            - file_path (string) : the file to read
+            - ax (Axes) : the axes of the subplot in which plotting the Raman signal and peaks
+            - height (None, scalar or 2-element sequence) : required height of peaks. Either a number, None, an array matching x or a 2-element sequence of the former. The first element is always interpreted as the minimal and the second, if supplied, as the maximal required height.
+            - threshold (None, scalar or 2-element sequence) : required threshold of peaks, the vertical distance to its neighboring samples. Either a number, None, an array matching x or a 2-element sequence of the former. The first element is always interpreted as the minimal and the second, if supplied, as the maximal required threshold.
+            - distance (scalar) : Required minimal horizontal distance in samples between neighbouring peaks. The distance should be higher than the step between two adjacents points from x. Smaller peaks are removed first until the condition is fulfilled for all remaining peaks.
+            - width (None, scalar or 2-element sequence) : required width of peaks in samples. Either a number, None, an array matching x or a 2-element sequence of the former. The first element is always interpreted as the minimal and the second, if supplied, as the maximal required width.
+            - *ax_kwargs : this method also takes any keyword argument for the Figure.add_sublot() function such as xlabel, ylabel, xticks, yticks, xlim, ylim, xscale, yscale, ...
+            - **plot_kwargs : this method also takes any keyword argument for the Axes.plot() function such as linewidth, linestyle, marker, markersize, color, markeredgecolor, zorder, alpha, label, clip_on, ...
+            
+        return:
+            - fig, ax : the Figure and Axes object created if None is specified for ax
+    """
+    data=file_handling.read_file(file_path,comments="#",delimiter=None)
+    x=data[:,0]
+    y=data[:,0]
+    
+    if ax==None:
+        fig=plt.figure()
+        ax=fig.add_subplot(*ax_kwargs)
+    
+    if with_peaks:
+        plot_with_peaks(x,y,ax=ax,height=height, threshold=threshold, distance=distance, width=width,**plot_kwargs)
+    else:
+        ax.plot(x,y,*ax_kwargs)
+        
+    if ax==None:
+        return fig, ax
+    
+def plot_from_multiple_files(file_paths,with_peaks=False,height=None, threshold=None, distance=None, width=None,*ax_kwargs,**plot_kwargs):
+    
+    """ Function to plot the Raman spectrum with the peaks indicated
+    
+        args:
+            - file_paths (list of string) : lis of the names for the files to read
+            - ax (Axes) : the axes of the subplot in which plotting the Raman signal and peaks
+            - height (None, scalar or 2-element sequence) : required height of peaks. Either a number, None, an array matching x or a 2-element sequence of the former. The first element is always interpreted as the minimal and the second, if supplied, as the maximal required height.
+            - threshold (None, scalar or 2-element sequence) : required threshold of peaks, the vertical distance to its neighboring samples. Either a number, None, an array matching x or a 2-element sequence of the former. The first element is always interpreted as the minimal and the second, if supplied, as the maximal required threshold.
+            - distance (scalar) : Required minimal horizontal distance in samples between neighbouring peaks. The distance should be higher than the step between two adjacents points from x. Smaller peaks are removed first until the condition is fulfilled for all remaining peaks.
+            - width (None, scalar or 2-element sequence) : required width of peaks in samples. Either a number, None, an array matching x or a 2-element sequence of the former. The first element is always interpreted as the minimal and the second, if supplied, as the maximal required width.
+            - *ax_kwargs : this method also takes any keyword argument for the Figure.add_sublot() function such as xlabel, ylabel, xticks, yticks, xlim, ylim, xscale, yscale, ...
+            - **plot_kwargs : this method also takes any keyword argument for the Axes.plot() function such as linewidth, linestyle, marker, markersize, color, markeredgecolor, zorder, alpha, label, clip_on, ...
+            
+        return:
+            - fig, ax : the Figure and a list with the axes object created
+    """
+    
+    n_plot=len(file_paths)
+    fig,ax=plt.subplots(n_plot,1,sharex=True,figsize=(5,n_plot*2))
+    
+    for i in range(n_plot):
+        ax[i].spines.right.set_visible(False)
+        ax[i].spines.top.set_visible(False)
+        ax[i].spines.bottom.set_visible(False)
+        plot_from_file(file_paths[i],ax=ax[i],with_peaks=with_peaks,height=height, threshold=threshold, distance=distance, width=width,*ax_kwargs,**plot_kwargs)
+        
+    return fig, ax
\ No newline at end of file
diff --git a/data_analysis/semiconductor.py b/data_analysis/semiconductor.py
new file mode 100644
index 0000000..db73e45
--- /dev/null
+++ b/data_analysis/semiconductor.py
@@ -0,0 +1,9 @@
+import numpy as np
+import matplotlib.pyplot as plt
+
+def mobility_extraction:
+    
+def transistor_current:
+    
+def mobility_strain_silicon(strain_tensor, pi11, pi12, pi44):
+    
\ No newline at end of file
diff --git a/equipment_control/__pycache__/sh242.cpython-311.pyc b/equipment_control/__pycache__/sh242.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..e9411676ca7dcea379714dff50028f355ee80256
GIT binary patch
literal 9335
zcmeGiTWs6bm3rB<Oxd!f+HR7iv^__bB3VjeudCZM>D;(>{YY9nb)%&d7+pG2B2x-U
zC9q%v{^*AT<Rb$LbnE(IU|W|W8{p3l+kj$U`y@3276=3|FkprM++`m_e!6o>krMT&
zjlPHViaNaa@ZNLJJ@@cDuJk*P$4x-_)35${ed7lN@ekZEimP0CJO!0^2}B@UhFG!1
zZ8p7bkJ~kzBkr^luMo)oHh~<h?S{=D@PUIM9>P~IaTl^J5JBf2Zq6smSnbwYZaj`d
z<z0d$;x<G8bz9tl>~SY@z*uLz1-VcQYDFY!L+<w-s2x$rgKNu9)bX}0-ikWkCgLRW
z0&GKl$Okw#dK&e>y&d(!+(%TGkUBkjx)7MiFoHmfSvr~JL_V9L7p4GDCo}04MocH^
zWQtk2#`3C5!>g_f3zsfT6hbLc%n9SsC@bXH<VZ5R66Kg^j?bceQWT;qS;S_dLMj2I
z7AN=CVKM2yfA-mDdw!?|9jYsz%VC<C>>ao{1XkQb@PxMm*n1X0(hxb+i5B=`;XQs4
zYVQ(j#1=H_3$nq-jvVhhJ^;Z#G=*=25tXgj!gJIW<g6Vd8e0K<Z#azs$W`0VLc5x4
zV+ip9)E=6xLvhnmJ4+L8T5J1#0d8#kr<PHT<wLGHP}|eyDhjX@wR1P&@tcvD8~AJg
z4)aF=f1YoPKkk)7>~P07xSw|ZUx(nRbItjk);TZm_g&VxF5vH5(>df@IE>xx{^qm}
za}@M55ioG{WS&ck=`4pGUz$scX(p2{u(X(Bb^qt4%f+nVHp5>R#w{3(jnE>y0`80v
z^E^urU(04QEW-uqaXP(BFY<XdOfxXqZAR25sP0{Mx(#Ti@+)bS7FQ2J(CpfrTGgBh
z1YUg@0A4@CuVO~aE!?E<q{Wmb2+c>+!vorVAV@PDqBVyNIPH#c8HQico?y|HB}s!n
zr*qjfCqhrCL%-!LuhFdH<8#*ht+HrH)8wyU_qZ1!Xz9Acr$sg)viwS#gV-p`sa~FC
zkOe~uEQb>CEwo*_xOCz5Fnw`;4~HU!&T@O=;{3wmOY@6+INmI@FJ73t6sBi~MousD
zZB>{l4TJkUDfd~>FzvXHTOYM>adNWIK6QC+x=g`nXoH`WpJ?L>ZoL~GG9PSFkHNkq
zKwmgk(}P5g;h7Z{)`n%TcIH-Zrv)a#vqCn{Ct1~{tzC7B=@n=Q8J5kdPQ3V_OLYot
zW?5~o+FF$~gs65_p*7N!@d&pB95CT`h<-Bm^7z%)vuPpCMPJS)^D8U|abz-;&9TB&
z_EtWf!x+pl4pOgb!RttFRdrY1-hx-hRxlo*F8mqZ_YGp-PqcY<$Y7BSeso*9l8|m@
zrBDtonY^Wtw<Pk`E=eimu^n=tNDgd0FOyM)j7ntm5lP*1-#>NNbI-HsDV6*57s-B^
z98}0bi5xVLc%U>mDwAUhIVO=~`pDCFJMMLCb{L&3BX`?-_6bkhaEbCN6um>8C{ic3
z*0&QMFDYYla$ruT<`rsQqUIk_-g~d!zjb%+-rVM#(R-*!4arnMp#l;WFpys*iOb5^
zj2xJesab`Zm8jW+xd29XtJkt7Q=<wsDp8{ckKJz}D1ReyT>UtpjJ+xcUX`gCg_@D5
znS+Vdh~q0EF{F%5$$=@EnpUW3iJCr`3lPVbk%&J0F(@&SG_x6K;9N+{hu(n-&WCQm
zT`ybJ{R||et94$sDpR?EX=w%7Yx`Mfn^}<2AK$In&AM$3r?!s5uPrVXTZ&ryPiL1k
zRBfhd+Fi4P?E}w(cjXRrzCsdvB<#|v?2b8;p30hZpSZ9YJ@<*eoZWLDy7*qG^L-fL
zSe-wD!BGG~JAVw<;bediaE1gN^~25d0A>JC=+UiE*)y2<b9m%W0Dvv(09#bCHP6cA
zNrgNqktcPFaprF4z0S?flD|*!5AXORMSn#0M-_i`v%S=LL?VyygFxngP9xrHtPzGg
zFgK~@;KAH2v-0`sVA30|=z@teRIW}DuyZ`ep8&b?0RYB!ABVbSyFJ>r8uSXjs=YRl
zGkDBP0AQo3Lv3`|Q%cv+PFJYd6_UHcN>_N3EOqv7c53_l$$P)V<&WohAJ9xf-fb3q
zLZR7#anQ)a*?~1%!_56_vIaInY)E;n-p)AqkC0=%1v%GS*IJO{CVs~Ve%oSy8r!zI
z^M%H9qWUux97>hT4O6aSr-37^mh}+UZ;Pyqt<+G>DGhQ~&H=3U7VIyq`=PdWJbs-N
z@uru{8VS1x0ikuwn)9aiocFG^B6|Ts9*|(`nyV~92O`&7IJ;G@+rLL`3xx3vy7LpD
zMwZtTNANhJZ)8Qk<O^C&vg#cbA-RJ16Wl~qGS%dL#TEdU2z`fv+>N`Qz;P*EO%1ox
z9AqL8F(U1J6L+Prv5<+dG_wqmE<W1iuEXfMmFm2V(9_E_V@zSDFoMWjOGCD(WdL+q
z0M`WJDbJ#?MyJdXt#OoM;GB~a%F~WmOju<B3E(MVCEGBI#2d0~B(B<W#1y0NQ~8vZ
zPB8*)Py$)vTn?l#V4yUf6Cxz2Ji{f~0Xlmd<WTdN%i{yz0Xn&w%&-Es%VUNd8!Vs+
zY0G9{MKb2ZyYkoViSmiFkVe2jGfmUYG@M0e1(s&HZ2mfArdhpv19dr&JAGyJFl44U
zL?}05f#szF?Q1Nk1)IEy6;V4AZ_J@Z%;vm2KHlcL0ZeK~+58Yx_pk|kVhb<Mf9P%+
zU#NC2r@HuTo<pi@Ig@2X$g*;ow5U3mYeKNC5`Cytwa%+OxK*z}YHyQ$QT^5jx(w8w
zs<+NmMHZ<(Ywsj5D5~D-&e$OeIB~`LtQ9>l>ck0VRmZ|py05H3FTqIRIk4>I6s-~#
zo!vev556FiXBG0SM4ml357T?V2wEX&iKKVi`*AukQW_kVsDTpQFHz6zb`L)oDt1Su
z?r6#DSG+&m@rH`t(1T}X?-|8=M)IC{<n_Pf{>7=Ed*1Qf_dMz!{7v5<hPItb?B$)<
zbTKw9$7Yn+%uejJV(c~P^~-YX4JG!5wDhJDyCR3;N+>S<^s3zdmeT*0<fR|^dZgaK
zgLb+1jO-g#e52CXY|%F>`DRQ0z=JN?ACmlmQg28KpD*^FmwL~aqGzPO5x9245vlLw
zzK`$)4Ss=Txc1v%McT6VNx~Ncj20#6p$pc$?;`{df~C~|X7hg<A#^m15bTGE5SVbY
z2*C(@zs~sGj1B50s|n-{6ZBOE2IidKM+IMRR4`QMaZS-%<G{}k2#i6dSFR|9)tP;{
zXuz0;ZW0PS1vaQU6bRw=2nJCAi}QR8ZiZvWc_#lO4DgTN{Esns4uI-2rm1qu{PXyU
z$9&Sv15?=58odhL758i4ZUY@*w6X8)lLw!Z$#I1om&oxi?0Z8cdI-F(;d_U+^2P43
z)Ez#Q?>$bxpZab7!J-m*X(uvSj7-XrDJ3$s6PYhY=Km(hkp(5PAT3^2B5%lnB_*&V
zy?I4G9#@XX!EyU~?yqmXDfdQX--zNHk)o4D-=yT5Z02~wG4Qwl*&PmoN5*dVguyCs
z3m0~~A?$WT*zHCDqq*Jrpi>L<CiyVlGMucL80|0td*mRcl8FQd$LooN>P{r!&k^|y
zu2YG`tvr({KWR-QP&Nr@m-g=owH5wUkz=@3)sg2j`~apujKN6^&SHQM89pR*|KE=h
zI7QU}4mpLt0N5};yUtUR^Hj;*wQ-?D`8HlDk)s<EaOOG`+#Z8KM*0~erEW;mH{seR
zhza|o9c~xxm+)=Leg)qOws_?ha}DnkR)B17*U^2#1buC;zJ0<3eaBo7xmdxA$6O%R
zCO}^qUKIRbakTD7fDsd;wwYD!+)j1DyPaF*PZ{#yr*Ul%11zW~<O}*HyvWZ%6W#zq
wSOKu_u-R-SA}U$064Ab)eM&^PWL*2sIlB#bZwxo#jQE*PwTJ(1(nVwEzukg~@c;k-

literal 0
HcmV?d00001

diff --git a/equipment_control/example/single_equipment/sh242_script.py b/equipment_control/example/single_equipment/sh242_script.py
index b865627..9788f56 100644
--- a/equipment_control/example/single_equipment/sh242_script.py
+++ b/equipment_control/example/single_equipment/sh242_script.py
@@ -19,14 +19,14 @@ print("Available connections: %s"%str(list_connections))
 # =============================================================================
 # 3. Connection to the equipments
 # =============================================================================
-mysh242=sh242.sh242('USB0::0x05E6::0x6500::04529651::INSTR',timeout=1e3)
+mysh242=sh242.sh242('TCPIP0::192.168.100.11::57732::SOCKET',timeout=30e3)
 
 # =============================================================================
 # 4. Measurement parameters
 # =============================================================================
-temperature=30
-humidity=50
-wait_for_stabilization=True # if True, wait for the temperature and humidity to be stabilized to continue the script
+temperature=25
+humidity=70
+wait_for_stabilization=False # if True, wait for the temperature and humidity to be stabilized to continue the script
 
 # =============================================================================
 # 5. Initialization of the equipment
@@ -36,9 +36,9 @@ mysh242.initialize(temperature=True, humidity=True,temperature_dic={"upper":125,
 # =============================================================================
 # 6. Read temperature and humidity
 # =============================================================================
-data_temperature=mysh242.read_temperature()
-data_humidity=mysh242.read_humidity()
-print("\n- Temperature: %.2f °C\n- Humidity: %.2f %%"%(data_temperature,data_humidity))
+data_temperature=mysh242.read_temperature().split(",")
+data_humidity=mysh242.read_humidity().split(",")
+print("\n- Temperature: %.2f °C\n- Humidity: %.2f %%"%(float(data_temperature[0]),float(data_humidity[0])))
 
 # =============================================================================
 # 7. Set temperature and humidity
diff --git a/equipment_control/sh242.py b/equipment_control/sh242.py
index dff6c7b..31ee407 100644
--- a/equipment_control/sh242.py
+++ b/equipment_control/sh242.py
@@ -20,7 +20,7 @@ class sh242(equipment.equipment):
                 - humidity_dic (dictionary) : dictionnary with the upper ("upper") and lower ("lower") alarm and the initial set point ("set") for the humidity
         """
 
-        self.set_connection_parameter_dic({"write_termination":';',"read_termination":';',"send_end":True})
+        self.set_connection_parameter_dic({"write_termination":'\r\n',"read_termination":'\r\n',"send_end":True})
 
         self.pyvisa_resource.write('POWER, ON')
         time.sleep(1)
@@ -142,8 +142,8 @@ class sh242(equipment.equipment):
                     stabilization_count+=1
                     if stabilization_count==stabilization_number:
                         humidity_stabilized=True
-                    else:
-                        stabilization_count=0
+                else:
+                    stabilization_count=0
 
 
 
-- 
GitLab