change
This commit is contained in:
@@ -54,7 +54,7 @@ You always stay at the **repo root** for Git operations and use `--project` to p
|
||||
INSTRUMENT-CONTROL/
|
||||
├── .gitignore
|
||||
├── README.md
|
||||
├── automated-iv-curves/
|
||||
├── your-project-folder/
|
||||
│ ├── pyproject.toml ← declares your dependencies (commit this)
|
||||
│ ├── uv.lock ← locks exact versions (commit this)
|
||||
│ ├── .python-version ← pins the Python version (commit this)
|
||||
@@ -83,10 +83,10 @@ Pick one version and stick to it across all projects unless you have a specific
|
||||
From the **repo root**, run:
|
||||
|
||||
```bash
|
||||
uv init --python 3.12 automated-iv-curves
|
||||
uv init --python 3.12 your-project-folder
|
||||
```
|
||||
|
||||
This creates the following inside `automated-iv-curves/`:
|
||||
This creates the following inside `your-project-folder/`:
|
||||
- `pyproject.toml` — the project's metadata and dependency list
|
||||
- `uv.lock` — a locked snapshot of the full dependency tree (including sub-dependencies)
|
||||
- `.python-version` — a one-line file that just says `3.12`
|
||||
@@ -98,7 +98,7 @@ The `.venv/` folder doesn't exist yet — it gets created the first time you add
|
||||
## Adding packages
|
||||
|
||||
```bash
|
||||
uv --project automated-iv-curves add numpy matplotlib scipy
|
||||
uv --project your-project-folder add numpy matplotlib scipy
|
||||
```
|
||||
|
||||
This does three things at once: updates `pyproject.toml` with the new dependencies, resolves the full dependency tree into `uv.lock`, and installs everything into `.venv/`. You never need to manually `pip install` anything.
|
||||
@@ -108,7 +108,7 @@ This does three things at once: updates `pyproject.toml` with the new dependenci
|
||||
## Running a script
|
||||
|
||||
```bash
|
||||
uv --project automated-iv-curves run python automated-iv-curves/measure.py
|
||||
uv --project your-project-folder run python your-project-folder/measure.py
|
||||
```
|
||||
|
||||
`uv run` automatically uses the project's environment — no need to activate anything first for one-off script runs.
|
||||
@@ -119,18 +119,18 @@ uv --project automated-iv-curves run python automated-iv-curves/measure.py
|
||||
|
||||
First add Jupyter to the project:
|
||||
```bash
|
||||
uv --project automated-iv-curves add jupyter
|
||||
uv --project your-project-folder add jupyter
|
||||
```
|
||||
|
||||
Then activate the environment and launch:
|
||||
```bash
|
||||
source automated-iv-curves/.venv/bin/activate
|
||||
source your-project-folder/.venv/bin/activate
|
||||
jupyter lab
|
||||
```
|
||||
|
||||
You need to activate here (rather than use `uv run`) because Jupyter is an interactive session, not a one-off command. After activating, your terminal prompt will change to show `(.venv)` — this means all `python`, `jupyter`, and other commands will use the project's environment until you run `deactivate`.
|
||||
|
||||
Create or open any `.ipynb` file inside `automated-iv-curves/` and it will have access to all the project's packages. Before committing notebooks, clear the outputs (`Kernel → Restart & Clear Output`) to keep your Git diffs clean.
|
||||
Create or open any `.ipynb` file inside `your-project-folder/` and it will have access to all the project's packages. Before committing notebooks, clear the outputs (`Kernel → Restart & Clear Output`) to keep your Git diffs clean.
|
||||
|
||||
---
|
||||
|
||||
@@ -138,15 +138,15 @@ Create or open any `.ipynb` file inside `automated-iv-curves/` and it will have
|
||||
|
||||
To confirm which Python a project is using:
|
||||
```bash
|
||||
uv --project automated-iv-curves run python --version
|
||||
uv --project your-project-folder run python --version
|
||||
|
||||
# or just read the pin file directly:
|
||||
cat automated-iv-curves/.python-version
|
||||
cat your-project-folder/.python-version
|
||||
```
|
||||
|
||||
To change it:
|
||||
```bash
|
||||
uv --project automated-iv-curves python pin 3.12
|
||||
uv --project your-project-folder python pin 3.12
|
||||
```
|
||||
|
||||
---
|
||||
@@ -158,13 +158,13 @@ uv --project automated-iv-curves python pin 3.12
|
||||
git pull
|
||||
|
||||
# Add a new package
|
||||
uv --project automated-iv-curves add pyvisa
|
||||
uv --project your-project-folder add pyvisa
|
||||
|
||||
# Run your script
|
||||
uv --project automated-iv-curves run python automated-iv-curves/measure.py
|
||||
uv --project your-project-folder run python your-project-folder/measure.py
|
||||
|
||||
# Commit the updated dependency files
|
||||
git add automated-iv-curves/pyproject.toml automated-iv-curves/uv.lock
|
||||
git add your-project-folder/pyproject.toml your-project-folder/uv.lock
|
||||
git commit -m "add pyvisa dependency"
|
||||
git push
|
||||
```
|
||||
@@ -176,7 +176,7 @@ git push
|
||||
The `.venv/` folder is gitignored, so it doesn't exist on a fresh clone. `uv sync` recreates it from `uv.lock`:
|
||||
|
||||
```bash
|
||||
uv --project automated-iv-curves sync
|
||||
uv --project your-project-folder sync
|
||||
```
|
||||
|
||||
You need this in two situations:
|
||||
@@ -284,7 +284,7 @@ It's not sensitive or machine-specific — committing it means any collaborator
|
||||
If you set up the `.gitignore` after Git already started tracking `.venv/`, the file won't be automatically un-tracked — `.gitignore` only prevents *new* files from being added. To fix this:
|
||||
|
||||
```bash
|
||||
git rm -r --cached automated-iv-curves/.venv/
|
||||
git rm -r --cached your-project-folder/.venv/
|
||||
git commit -m "untrack .venv"
|
||||
```
|
||||
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
# Experiment parameters
|
||||
V_start = -1.0
|
||||
V_end = 1.0
|
||||
step_size = 0.05
|
||||
time_average = 0.1
|
||||
sample_name = "test"
|
||||
contact_1 = "test1"
|
||||
contact_2 = "test1"
|
||||
light = "MIR"
|
||||
temp = 300
|
||||
amplifier = "SR570"
|
||||
gain = 1e7
|
||||
number_of_loops = 0
|
||||
|
||||
|
||||
# Instrument addresses: typically do NOT need to be changed
|
||||
Keithley_address = 'TCPIP0::Prologix-00-21-69-01-4e-fb.psi.ch::1234::SOCKET'
|
||||
SIM900_address = 'TCPIP0::ir-moxa01.psi.ch::3001::SOCKET'#'ASRL1::INSTR'
|
||||
SIM928_port = 1
|
||||
@@ -15,13 +15,9 @@ import SIM900_commands as SIM900_comm
|
||||
import SIM928_commands as SIM928_comm
|
||||
import Keithley196_commands as Keithley_comm
|
||||
|
||||
Keithley_address = 'TCPIP0::Prologix-00-21-69-01-4e-fb.psi.ch::1234::SOCKET'
|
||||
# Keithley_address = 'GPIB0::6::INSTR'
|
||||
SIM900_address = 'TCPIP0::ir-moxa01.psi.ch::3001::SOCKET'#'ASRL1::INSTR'
|
||||
|
||||
Keithley = rm.open_resource(Keithley_address)
|
||||
SIM900 = rm.open_resource(SIM900_address)
|
||||
SIM928_port = 1
|
||||
|
||||
# Initialise Keithley
|
||||
Keithley.clear()
|
||||
@@ -30,9 +26,194 @@ Keithley.read_termination = '\n'
|
||||
Keithley.write("T5X\n") # Ensures the Keithley reads when it is triggered by "X" write
|
||||
Keithley_comm.Set_Mode(Keithley, "DC V")
|
||||
Keithley_comm.Set_Range(Keithley, "Auto")
|
||||
Keithley.read() # Throwaway
|
||||
Keithley.read() # Throwaway
|
||||
|
||||
# Initialise SIM900
|
||||
SIM900_comm.flush_main(SIM900)
|
||||
SIM900_comm.reset(SIM900)
|
||||
SIM900_comm.flush_port(SIM900, SIM928_port)
|
||||
SIM900_comm.flush_port(SIM900, SIM928_port)
|
||||
|
||||
# Function to read the voltage set by a voltage source
|
||||
def read_source_V():
|
||||
count = 0
|
||||
voltage = SIM900_comm.send_query(SIM900, SIM928_port, SIM928_comm.read_voltage(), 128) # get voltage
|
||||
print("returned data: "+voltage)
|
||||
while (count<10) and (type(voltage)!= float):
|
||||
try:
|
||||
voltage = float(voltage)
|
||||
except:
|
||||
print('no float')
|
||||
voltage = SIM900_comm.send_query(SIM900, SIM928_port, SIM928_comm.read_voltage(), 128) # get voltage
|
||||
print("returned data: "+voltage)
|
||||
count = count + 1
|
||||
return voltage
|
||||
|
||||
#print(read_source_V())
|
||||
|
||||
# Function to set voltage of SIM928
|
||||
def set_voltage(V_value=1e-3):
|
||||
if V_value > 20:
|
||||
print ('Voltage setting too high (>10V), it is set to 10V')
|
||||
V_value = 20
|
||||
elif V_value < -20:
|
||||
print ('Voltage setting too high (<-10V), it is set to -10V')
|
||||
V_value = -20
|
||||
SIM900_comm.send_command(SIM900, SIM928_port, SIM928_comm.set_voltage(V_value)) # get voltage
|
||||
time.sleep(1)
|
||||
# count = 0
|
||||
# while (count<10) and (read_source_V()!= V_value):
|
||||
# print('Setting voltage again')
|
||||
# time.sleep(1) #wait 1 second
|
||||
# SIM900_comm.send_command(SIM900, SIM928_port, SIM928_comm.set_voltage(V_value)) # get voltage
|
||||
# count = count + 1
|
||||
SIM900_comm.send_command(SIM900, SIM928_port, SIM928_comm.V_source_state("on"))
|
||||
print('Voltage set to ' + str(V_value))
|
||||
# return read_source_V()
|
||||
|
||||
# Function to measure the current from the Keithley voltmeter
|
||||
# time_average is the averaging time, time_interval is the time between points
|
||||
# Could also use "Filter (P)" command to set the Keithley's internal averaging time
|
||||
def measure_V(time_average=10, time_interval=0.2):
|
||||
N_points = time_average/time_interval + 1
|
||||
V_list = []
|
||||
i = 1
|
||||
Keithley_comm.Set_Range(Keithley, "Auto") #set auto range
|
||||
time.sleep(time_interval)
|
||||
while i < N_points:
|
||||
V_list.append(Keithley_comm.Voltage(Keithley)) #read the voltage
|
||||
time.sleep(time_interval)
|
||||
i = i+1
|
||||
V_avg_ = np.mean(V_list, dtype=np.float32)
|
||||
V_std_ = np.std(V_list, dtype=np.float32)
|
||||
V_err_ = V_std_/(N_points)**(1/2)
|
||||
return [V_list, V_avg_, V_err_]
|
||||
|
||||
|
||||
# Function to do I-V measurements
|
||||
# Currently doesn't read the voltage that was set, due to issues with querying through mainframe
|
||||
# Errors caluclated incorrectly
|
||||
def V_scan(V_start=1e-3, step_size=1e-3, V_end=2e-3, time_average=10, sample_name='no_name', contact_1 = '', contact_2 = '', light = 'Dark', temp=777, amplifier='SR570', gain=10**3, number_of_loops=0):
|
||||
Keithley.clear()
|
||||
print('Measurement started: %s'\
|
||||
%(datetime.datetime.now().strftime("%Hh%Mm%Ss")))
|
||||
V_scan_list = np.linspace(V_start, V_end, int(abs(V_start-V_end)/step_size)+1)
|
||||
for n_l in range(number_of_loops):
|
||||
V_scan_list = np.append(V_scan_list,np.linspace(V_end, V_start, (abs(V_start-V_end)/step_size)+1))
|
||||
print(V_scan_list)
|
||||
SIM900_comm.send_command(SIM900, SIM928_port, SIM928_comm.V_source_state("off")) #set source off
|
||||
# open a file to write data to
|
||||
date_today = datetime.date.today() #date for making a directory
|
||||
file_path = Path(__file__).parent / "data" / str(date_today)
|
||||
if not os.path.exists(file_path):#make the directory if it is not there
|
||||
os.mkdir(file_path)
|
||||
time_start = datetime.datetime.now().strftime("%Hh%Mm%Ss")
|
||||
filename_root = "%s_%s_%s_%s_%s_scan_at_%sK_%s" % \
|
||||
(time_start, sample_name, contact_1, contact_2, light, temp, amplifier)
|
||||
filename = filename_root + "_averaging%ss.txt" % (time_average)
|
||||
plotname = filename_root + "_averaging%ss.png" % (time_average)
|
||||
filename2 = filename_root + "_raw.txt"
|
||||
full_name = os.path.join(file_path, filename)
|
||||
full_plotname = os.path.join(file_path, plotname)
|
||||
full_name2 = os.path.join(file_path, filename2)
|
||||
#data_file=open(str(full_name),'w')
|
||||
with open(str(full_name),'w') as data_file, open(str(full_name2),'w') as raw_V_file:
|
||||
data_file.write("Time\tVoltage_source[V]\tVoltage_meas[V]\tVoltage_meas_err[V]\tGain[V/A]\n")
|
||||
raw_V_file.write("Voltage_source[V]\tVoltage_meas[V]\n")
|
||||
# just for ploting here
|
||||
V_plot_list = []
|
||||
V_err_plot_list = []
|
||||
V_scan_plot_list = []
|
||||
# A loop over all currents
|
||||
|
||||
i=0
|
||||
# set_voltage(V_scan_list[0])
|
||||
# time.sleep(5) #wait 3 sec to avoid large noise on first measurement
|
||||
|
||||
for V_scan_item in V_scan_list:
|
||||
|
||||
time_start_iteration= datetime.datetime.now()
|
||||
i = i+1
|
||||
set_voltage(V_scan_item)
|
||||
time.sleep(2) #wait 5 sec for signal to settle
|
||||
# measure+read+write the data
|
||||
V_source = V_scan_item
|
||||
# V_source = read_source_V()
|
||||
[V_list, V_avg, V_err] = measure_V(time_average=time_average, time_interval=0.2)
|
||||
time_now = datetime.datetime.now()
|
||||
data_string = "%s\t %s\t %.5e\t %.5e\t %s\n"\
|
||||
%(time_now, V_source, V_avg, V_err, gain)
|
||||
data_file.write(str(data_string))
|
||||
raw_V_file.write("%s\t %s\n" %(V_source, V_list))
|
||||
V_plot_list.append(V_avg)
|
||||
V_err_plot_list.append(V_err)
|
||||
V_scan_plot_list.append(V_source)
|
||||
|
||||
#make plot
|
||||
plt.close()
|
||||
plt.plot(V_scan_plot_list, V_plot_list,'+')
|
||||
plt.title(str(sample_name))
|
||||
plt.xlabel('Source Voltage / V')
|
||||
plt.ylabel('Measured voltage / V')
|
||||
plt.draw()
|
||||
plt.pause(0.1)
|
||||
plt.show(block=False)
|
||||
iteration_time = datetime.datetime.now()-time_start_iteration
|
||||
remaining_time = iteration_time.total_seconds()*(len(V_scan_list)-i)
|
||||
print(time.strftime('remaining time: %H:%M:%S',\
|
||||
time.gmtime(remaining_time)))
|
||||
|
||||
I_list = [V_plot_list[i]/gain for i in range(len(V_plot_list))]
|
||||
I_err_list = [V_err_plot_list[i]/gain for i in range(len(V_plot_list))]
|
||||
plt.close()
|
||||
plt.figure()
|
||||
plt.errorbar(V_scan_plot_list, I_list, yerr=I_err_list, fmt='+')
|
||||
plt.title(str(sample_name))
|
||||
plt.xlabel('Source Voltage / V')
|
||||
plt.ylabel('I / A')
|
||||
plt.tight_layout()
|
||||
plt.grid()
|
||||
|
||||
plt.savefig(full_plotname)
|
||||
plt.draw()
|
||||
plt.pause(0.001)
|
||||
plt.show(block=False)
|
||||
plt.close()
|
||||
|
||||
data_file.close()
|
||||
raw_V_file.close()
|
||||
set_voltage(0)
|
||||
SIM900_comm.send_command(SIM900, SIM928_port, SIM928_comm.V_source_state("off")) #set source off
|
||||
print('Source turned off')
|
||||
print('Measurement finished: %s'\
|
||||
%(datetime.datetime.now().strftime("%Hh%Mm%Ss")))
|
||||
return
|
||||
|
||||
# V_start, step size, V_end, time average, sample name, contact 1, contact 2, light, temperature, amplifier, amplifier gain
|
||||
V_scan(-1.0, 0.05, 1.0, .1, "test", "test1", "test1", "MIR", 300, "SR570", 10**(7)) #amplifier sensitivity = 1/gain
|
||||
#V_scan(-1.0, 0.1, 1.0, 1.0, "SHADWELL", "posContactC", "negContactF", "MIR", 18, "SR570", 10**(8)) #amplifier sensitivity = 1/gain
|
||||
#V_scan(0.0, 0.1, 1.0, 1.0, "Sun", "posContactB", "negContactH", "MIR", 18, "SR570", 10**(9)) #amplifier sensitivity = 1/gain
|
||||
#V_scan(0.0, 0.2, -2.0, 1.0, "Sun", "posContactB", "negContactH", "MIR", 18, "SR570", 10**(9)) #amplifier sensitivity = 1/gain
|
||||
#V_scan(0.0, 0.2, 2.0, 1.0, "Sun", "posContactB", "negContactH", "MIR", 18, "SR570", 10**(9)) #amplifier sensitivity = 1/gain
|
||||
#V_scan(0.0, 0.3, -3.0, 1.0, "Sun", "posContactB", "negContactH", "MIR", 18, "SR570", 10**(9)) #amplifier sensitivity = 1/gain
|
||||
#V_scan(0.0, 0.3, 3.0, 1.0, "Sun", "posContactB", "negContactH", "MIR", 18, "SR570", 10**(9)) #amplifier sensitivity = 1/gain
|
||||
#V_scan(0.0, 0.5, -5.0, 1.0, "Sun", "posContactB", "negContactH", "MIR", 18, "SR570", 10**(9)) #amplifier sensitivity = 1/gain
|
||||
#V_scan(0.0, 0.5, 5.0, 1.0, "Sun", "posContactB", "negContactH", "MIR", 18, "SR570", 10**(9)) #amplifier sensitivity = 1/gain
|
||||
|
||||
"""
|
||||
# Set up the plot
|
||||
plt.ion() # Enable interactive mode
|
||||
figure, ax = plt.subplots(figsize=(10, 6)) # Create a figure and an axes
|
||||
ax.set_title("Live Number Update")
|
||||
|
||||
# Loop to update the number and plot every second
|
||||
for _ in range(9999): # Limiting to 10 iterations for demonstration
|
||||
number = Keithley_comm.Voltage(Keithley)
|
||||
# Create a new plot for the current number
|
||||
figure, ax = plt.subplots(figsize=(10, 6))
|
||||
ax.text(0.5, 0.5, str(number), fontsize=100, ha='center') # Display the number in the center
|
||||
ax.set_title(f"Number: {number}")
|
||||
ax.axis('off') # Turn off the axis
|
||||
|
||||
plt.show() # Show the plot
|
||||
time.sleep(2) # Pause for a second
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user