Long term measurement
This commit is contained in:
parent
c0158e2478
commit
c73928046f
@ -0,0 +1 @@
|
|||||||
|
from cpdctrl.voltage_measurement_device.base import VoltageMeasurementDevice
|
@ -40,10 +40,14 @@ if __name__ == "__main__":
|
|||||||
parser.add_argument("-c", "--config", action="store", help="alternate path to config file")
|
parser.add_argument("-c", "--config", action="store", help="alternate path to config file")
|
||||||
args = vars(parser.parse_args())
|
args = vars(parser.parse_args())
|
||||||
|
|
||||||
from .voltage_measurement.base import VoltageMeasurementDevice as VDev
|
|
||||||
from .voltage_measurement.impl import keithley2700 as _volt
|
|
||||||
from .led_control.impl import thorlabs_ledd1b as _led
|
|
||||||
|
|
||||||
|
from .voltage_measurement_device.base import VoltageMeasurementDevice
|
||||||
|
from .voltage_measurement_device.impl import keithley2700 as _volt
|
||||||
|
from .led_control_device.base import LedControlDevice
|
||||||
|
from .led_control_device.impl import thorlabs_ledd1b as _led
|
||||||
|
from .led_script import LedScript
|
||||||
|
|
||||||
|
from .measurement import Measurement
|
||||||
from .utility.data import DataCollector
|
from .utility.data import DataCollector
|
||||||
|
|
||||||
|
|
||||||
@ -69,8 +73,9 @@ settings = {
|
|||||||
test = False
|
test = False
|
||||||
|
|
||||||
# global variable for the instrument/client returned by pyvisa/bleak
|
# global variable for the instrument/client returned by pyvisa/bleak
|
||||||
dev:VDev|None = None
|
dev: VoltageMeasurementDevice|None = None
|
||||||
data = DataCollector(settings["datadir"], settings["name"])
|
led: LedControlDevice|None = None
|
||||||
|
# data = DataCollector(settings["datadir"], settings["name"])
|
||||||
t0 = 0
|
t0 = 0
|
||||||
|
|
||||||
def monitor(interval=None, max_measurements=None, max_points_shown=160):
|
def monitor(interval=None, max_measurements=None, max_points_shown=160):
|
||||||
@ -239,7 +244,7 @@ Functions:
|
|||||||
|
|
||||||
|
|
||||||
def init():
|
def init():
|
||||||
global dev, settings, config_path
|
global dev, led, settings, config_path
|
||||||
print(r""" .___ __ .__
|
print(r""" .___ __ .__
|
||||||
____ ______ __| _/_____/ |________| |
|
____ ______ __| _/_____/ |________| |
|
||||||
_/ ___\\____ \ / __ |/ ___\ __\_ __ \ |
|
_/ ___\\____ \ / __ |/ ___\ __\_ __ \ |
|
||||||
@ -271,7 +276,7 @@ Enter 'help()' for a list of commands""")
|
|||||||
makedirs(settings["datadir"])
|
makedirs(settings["datadir"])
|
||||||
|
|
||||||
try:
|
try:
|
||||||
dev = _volt.init()
|
dev = _volt.init("GPIB0::22::INSTR")
|
||||||
led = _led.LEDD1B()
|
led = _led.LEDD1B()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(e)
|
print(e)
|
||||||
|
@ -1,2 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
1
cpdctrl/led_control_device/__init__.py
Normal file
1
cpdctrl/led_control_device/__init__.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
from cpdctrl.led_control_device.base import LedControlDevice
|
60
cpdctrl/led_script.py
Normal file
60
cpdctrl/led_script.py
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
Created on Fri Jan 24 16:46:06 2025
|
||||||
|
|
||||||
|
@author: CPD
|
||||||
|
"""
|
||||||
|
import time
|
||||||
|
|
||||||
|
class LedScript:
|
||||||
|
def __init__(self):
|
||||||
|
self.t_start = 0
|
||||||
|
|
||||||
|
def start(self) -> int:
|
||||||
|
"""
|
||||||
|
Start the script and return the initial LED state
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
int
|
||||||
|
LED Intensity [0,100]
|
||||||
|
"""
|
||||||
|
self.t_start = time.time()
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def get_state(self, t: None|float=None) -> int:
|
||||||
|
"""
|
||||||
|
Get the LED statefrom an absolute time (relative to when `start` was called)
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
t : None|float, optional
|
||||||
|
Absolute time since epoch or None.
|
||||||
|
If None, the current time will be used (using python `time.time()`
|
||||||
|
The default is None.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
int
|
||||||
|
LED Intensity [0,100]
|
||||||
|
"""
|
||||||
|
if t is None:
|
||||||
|
t = time.time()
|
||||||
|
dt = t - self.t_start
|
||||||
|
return self.get_relative_state(dt)
|
||||||
|
|
||||||
|
def get_relative_state(self, dt: float) -> int:
|
||||||
|
"""
|
||||||
|
Get the LED state from a script-relative time
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
dt : float
|
||||||
|
Number of seconds from the starting point of the script
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
int
|
||||||
|
LED Intensity [0,100]
|
||||||
|
"""
|
||||||
|
return 0
|
58
cpdctrl/measurement.py
Normal file
58
cpdctrl/measurement.py
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
Created on Fri Jan 24 15:18:31 2025
|
||||||
|
|
||||||
|
@author: Matthias Quintern
|
||||||
|
"""
|
||||||
|
from cpdctrl.voltage_measurement_device.base import VoltageMeasurementDevice
|
||||||
|
from cpdctrl.led_control_device.base import LedControlDevice
|
||||||
|
from cpdctrl.led_script import LedScript
|
||||||
|
from cpdctrl.utility.data import DataCollector
|
||||||
|
|
||||||
|
import time
|
||||||
|
import datetime
|
||||||
|
|
||||||
|
class Measurement:
|
||||||
|
def __init__(self,
|
||||||
|
vm_dev: VoltageMeasurementDevice,
|
||||||
|
led_dev: LedControlDevice,
|
||||||
|
led_script: LedScript,
|
||||||
|
data: DataCollector,
|
||||||
|
|
||||||
|
):
|
||||||
|
self.vm_dev = vm_dev
|
||||||
|
self.led_dev = led_dev
|
||||||
|
self.led_script = led_script
|
||||||
|
self.data = data
|
||||||
|
|
||||||
|
def measure_finite(self, delta_t: 0.1, flush_after:int|None=None, max_measurements=None, verbose=False):
|
||||||
|
# if no "time" in header, set the current local time in ISO 8601 format
|
||||||
|
# and without microseconds
|
||||||
|
if not "time" in self.data.header:
|
||||||
|
self.data.header["time"] = datetime.datetime.now().replace(microsecond=0).isoformat()
|
||||||
|
self.vm_dev.reset(True)
|
||||||
|
try:
|
||||||
|
i = 0
|
||||||
|
led_val = self.led_script.start()
|
||||||
|
t_iter_start = time.time()
|
||||||
|
while max_measurements is None or i < max_measurements:
|
||||||
|
tval, vval = self.vm_dev.read_value()
|
||||||
|
if i == 0:
|
||||||
|
t0 = tval
|
||||||
|
tval -= t0
|
||||||
|
self.data.add_data(i, tval, vval, led_val)
|
||||||
|
print(f"n = {i:6d}, t = {tval: .2f} s, U = {vval: .5f} V, LED = {led_val:03}%" + " "*10, end='\r')
|
||||||
|
if flush_after is not None and (i+1) % flush_after == 0:
|
||||||
|
self.data.flush(verbose=verbose)
|
||||||
|
# substract the execution time from the sleep time for a more
|
||||||
|
# acurate frequency
|
||||||
|
dt_sleep = delta_t - (t_iter_start - time.time())
|
||||||
|
if dt_sleep > 0:
|
||||||
|
time.sleep(dt_sleep)
|
||||||
|
t_iter_start = time.time()
|
||||||
|
led_val = self.led_script.get_state()
|
||||||
|
i += 1
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
pass
|
||||||
|
self.data.flush(verbose=verbose)
|
||||||
|
print("Measurement stopped" + " "*50)
|
@ -1,25 +1,65 @@
|
|||||||
import pandas as pd
|
import pandas as pd
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from os import path
|
import os
|
||||||
import matplotlib.pyplot as plt
|
import matplotlib.pyplot as plt
|
||||||
|
import datetime
|
||||||
|
|
||||||
|
from cpdctrl.utility.file_io import get_next_filename, sanitize_filename
|
||||||
|
|
||||||
class DataCollector:
|
class DataCollector:
|
||||||
def __init__(self, data_name, data_path):
|
def __init__(self,
|
||||||
|
data_name: str,
|
||||||
|
data_path: str,
|
||||||
|
header: dict[str, str]={},
|
||||||
|
dirname: str|None=None,
|
||||||
|
dir_exists_is_ok=False,
|
||||||
|
):
|
||||||
self.data = []
|
self.data = []
|
||||||
self.name = data_name
|
self.name = data_name
|
||||||
self.path = data_path
|
self.header = header
|
||||||
|
self.path = os.path.abspath(os.path.expanduser(data_path))
|
||||||
|
if dirname is None:
|
||||||
|
self.dirname = sanitize_filename(datetime.datetime.now(datetime.UTC).strftime("%Y-%m-%d_%H-%M") + "_" + self.name)
|
||||||
|
else:
|
||||||
|
self.dirname = sanitize_filename(dirname)
|
||||||
|
self.dirpath = os.path.join(self.path, self.dirname)
|
||||||
|
|
||||||
|
if os.path.exists(self.dirpath):
|
||||||
|
if not dir_exists_is_ok:
|
||||||
|
raise Exception(f"Directory '{self.dirname}' already exists. Provide a different directory or pass `dir_exists_is_ok=True` to ignore this")
|
||||||
|
else:
|
||||||
|
os.makedirs(self.dirpath)
|
||||||
|
self.flushed = False
|
||||||
|
|
||||||
|
def _get_filename(self):
|
||||||
|
return sanitize_filename(get_next_filename(self.name, self.dirpath, digits=5))
|
||||||
|
|
||||||
|
def flush(self, verbose=False):
|
||||||
|
# TODO check if dir still exists
|
||||||
|
filename = self._get_filename() + ".csv"
|
||||||
|
filepath = os.path.join(self.dirpath, filename)
|
||||||
|
if verbose: print(f"Flushing data to {filepath}")
|
||||||
|
self.to_dataframe().to_csv(filepath, sep=",", index=False, header=True)
|
||||||
|
self.data = []
|
||||||
|
self.flushed = True
|
||||||
|
|
||||||
|
def finalize(self):
|
||||||
|
if self.flushed:
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
def clear(self):
|
def clear(self):
|
||||||
self.data = []
|
self.data = []
|
||||||
|
|
||||||
def add_data(self, data):
|
def add_data(self, i, t, v, l):
|
||||||
self.data.append(data)
|
self.data.append((i, t, v, l))
|
||||||
|
|
||||||
def to_dataframe(self):
|
def to_dataframe(self):
|
||||||
return pd.DataFrame(self.data)
|
df = pd.DataFrame(self.data, columns=["idx", "t [s]", "V [V]", "LED [%]"])
|
||||||
|
df.meta = str(self.header)
|
||||||
|
return df
|
||||||
|
|
||||||
def save_csv(self):
|
def save_csv(self):
|
||||||
self.to_dataframe().to_csv(path.join(self.path, self.name + ".csv"), index=False, header=True)
|
self.to_dataframe().to_csv(os.path.join(self.path, self.name + ".csv"), index=False, header=True)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -28,7 +68,7 @@ class DataCollector:
|
|||||||
# df = pd.DataFrame(buffer)
|
# df = pd.DataFrame(buffer)
|
||||||
# df.colums = ["Time [s]", "Voltage [V]"]
|
# df.colums = ["Time [s]", "Voltage [V]"]
|
||||||
# return df
|
# return df
|
||||||
|
# OLD STUFF
|
||||||
def buffers2dataframe(ibuffer, vbuffer):
|
def buffers2dataframe(ibuffer, vbuffer):
|
||||||
"""
|
"""
|
||||||
@param ibuffer : 2d - array: timestamps, current
|
@param ibuffer : 2d - array: timestamps, current
|
||||||
@ -44,7 +84,7 @@ def load_dataframe(p:str):
|
|||||||
Load a dataframe from file.
|
Load a dataframe from file.
|
||||||
@param p : path of the file. If it has 'csv' extension, pandas.read_csv is used, pandas.read_pickle otherwise
|
@param p : path of the file. If it has 'csv' extension, pandas.read_csv is used, pandas.read_pickle otherwise
|
||||||
"""
|
"""
|
||||||
if not path.isfile(p):
|
if not os.path.isfile(p):
|
||||||
print(f"ERROR: load_dataframe: File does not exist: {p}")
|
print(f"ERROR: load_dataframe: File does not exist: {p}")
|
||||||
return None
|
return None
|
||||||
if p.endswith(".csv"):
|
if p.endswith(".csv"):
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
from os import listdir, path
|
from os import listdir, path
|
||||||
|
import re
|
||||||
|
|
||||||
def add_zeros(v: int, digits=3):
|
def add_zeros(v: int, digits=3):
|
||||||
"""
|
"""
|
||||||
@ -31,3 +32,6 @@ def get_next_filename(basename, directory=".", digits=3):
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
return basename + add_zeros(lowest_number+1)
|
return basename + add_zeros(lowest_number+1)
|
||||||
|
|
||||||
|
def sanitize_filename(filename):
|
||||||
|
return re.sub(r'[\\/*?:"<>|]',"", filename)
|
@ -19,6 +19,7 @@ scripts = {
|
|||||||
"instrument_reset": "~/cpd-dev/cpdctrl/cpdctrl/keithley_scripts/reset.scpi",
|
"instrument_reset": "~/cpd-dev/cpdctrl/cpdctrl/keithley_scripts/reset.scpi",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def select_visa_device(visa_backend=""):
|
def select_visa_device(visa_backend=""):
|
||||||
rm = pyvisa.ResourceManager(visa_backend)
|
rm = pyvisa.ResourceManager(visa_backend)
|
||||||
resources = rm.list_resources()
|
resources = rm.list_resources()
|
||||||
@ -202,7 +203,11 @@ class Keithley2700(VoltageMeasurementDevice):
|
|||||||
pass
|
pass
|
||||||
print("Measurement stopped" + " "*50)
|
print("Measurement stopped" + " "*50)
|
||||||
|
|
||||||
def init():
|
def init(name=None, visa_backend=""):
|
||||||
instr = select_visa_device()
|
if name:
|
||||||
|
rm = pyvisa.ResourceManager(visa_backend)
|
||||||
|
instr = rm.open_resource(name)
|
||||||
|
else:
|
||||||
|
instr = select_visa_device(name=name)
|
||||||
return Keithley2700(instr)
|
return Keithley2700(instr)
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user