puda-drivers 0.0.18__tar.gz → 0.0.20__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {puda_drivers-0.0.18 → puda_drivers-0.0.20}/PKG-INFO +2 -1
- {puda_drivers-0.0.18 → puda_drivers-0.0.20}/pyproject.toml +2 -1
- puda_drivers-0.0.20/src/puda_drivers/labware/MEA_cell_MTP.json +56 -0
- {puda_drivers-0.0.18 → puda_drivers-0.0.20}/src/puda_drivers/labware/labware.py +44 -18
- {puda_drivers-0.0.18 → puda_drivers-0.0.20}/src/puda_drivers/labware/polyelectric_8_wellplate_30000ul.json +2 -1
- {puda_drivers-0.0.18 → puda_drivers-0.0.20}/src/puda_drivers/labware/trash_bin.json +2 -1
- puda_drivers-0.0.20/src/puda_drivers/machines/__init__.py +4 -0
- puda_drivers-0.0.20/src/puda_drivers/machines/biologic.py +357 -0
- {puda_drivers-0.0.18 → puda_drivers-0.0.20}/src/puda_drivers/machines/first.py +230 -165
- {puda_drivers-0.0.18 → puda_drivers-0.0.20}/src/puda_drivers/move/gcode.py +14 -23
- puda_drivers-0.0.20/src/puda_drivers/py.typed +0 -0
- {puda_drivers-0.0.18 → puda_drivers-0.0.20}/tests/first.py +3 -0
- puda_drivers-0.0.18/src/puda_drivers/machines/__init__.py +0 -3
- {puda_drivers-0.0.18 → puda_drivers-0.0.20}/.gitignore +0 -0
- {puda_drivers-0.0.18 → puda_drivers-0.0.20}/LICENSE +0 -0
- {puda_drivers-0.0.18 → puda_drivers-0.0.20}/README.md +0 -0
- {puda_drivers-0.0.18 → puda_drivers-0.0.20}/src/puda_drivers/__init__.py +0 -0
- {puda_drivers-0.0.18 → puda_drivers-0.0.20}/src/puda_drivers/core/__init__.py +0 -0
- {puda_drivers-0.0.18 → puda_drivers-0.0.20}/src/puda_drivers/core/logging.py +0 -0
- {puda_drivers-0.0.18 → puda_drivers-0.0.20}/src/puda_drivers/core/position.py +0 -0
- {puda_drivers-0.0.18 → puda_drivers-0.0.20}/src/puda_drivers/core/serialcontroller.py +0 -0
- {puda_drivers-0.0.18 → puda_drivers-0.0.20}/src/puda_drivers/cv/__init__.py +0 -0
- {puda_drivers-0.0.18 → puda_drivers-0.0.20}/src/puda_drivers/cv/camera.py +0 -0
- {puda_drivers-0.0.18 → puda_drivers-0.0.20}/src/puda_drivers/labware/__init__.py +0 -0
- {puda_drivers-0.0.18 → puda_drivers-0.0.20}/src/puda_drivers/labware/opentrons_96_tiprack_300ul.json +0 -0
- /puda_drivers-0.0.18/src/puda_drivers/py.typed → /puda_drivers-0.0.20/src/puda_drivers/measure/electrode.py +0 -0
- {puda_drivers-0.0.18 → puda_drivers-0.0.20}/src/puda_drivers/move/__init__.py +0 -0
- {puda_drivers-0.0.18 → puda_drivers-0.0.20}/src/puda_drivers/move/deck.py +0 -0
- {puda_drivers-0.0.18 → puda_drivers-0.0.20}/src/puda_drivers/move/grbl/__init__.py +0 -0
- {puda_drivers-0.0.18 → puda_drivers-0.0.20}/src/puda_drivers/move/grbl/api.py +0 -0
- {puda_drivers-0.0.18 → puda_drivers-0.0.20}/src/puda_drivers/move/grbl/constants.py +0 -0
- {puda_drivers-0.0.18 → puda_drivers-0.0.20}/src/puda_drivers/transfer/liquid/sartorius/__init__.py +0 -0
- {puda_drivers-0.0.18 → puda_drivers-0.0.20}/src/puda_drivers/transfer/liquid/sartorius/api.py +0 -0
- {puda_drivers-0.0.18 → puda_drivers-0.0.20}/src/puda_drivers/transfer/liquid/sartorius/constants.py +0 -0
- {puda_drivers-0.0.18 → puda_drivers-0.0.20}/src/puda_drivers/transfer/liquid/sartorius/rLine.py +0 -0
- {puda_drivers-0.0.18 → puda_drivers-0.0.20}/tests/example.py +0 -0
- {puda_drivers-0.0.18 → puda_drivers-0.0.20}/tests/pipette.py +0 -0
- {puda_drivers-0.0.18 → puda_drivers-0.0.20}/tests/poll_position.py +0 -0
- {puda_drivers-0.0.18 → puda_drivers-0.0.20}/tests/qubot.py +0 -0
- {puda_drivers-0.0.18 → puda_drivers-0.0.20}/tests/test_deck.py +0 -0
- {puda_drivers-0.0.18 → puda_drivers-0.0.20}/tests/test_position_polling.py +0 -0
- {puda_drivers-0.0.18 → puda_drivers-0.0.20}/tests/webcam.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: puda-drivers
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.20
|
|
4
4
|
Summary: Hardware drivers for the PUDA platform.
|
|
5
5
|
Project-URL: Homepage, https://github.com/PUDAP/puda
|
|
6
6
|
Project-URL: Issues, https://github.com/PUDAP/puda/issues
|
|
@@ -16,6 +16,7 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
|
16
16
|
Classifier: Topic :: System :: Hardware
|
|
17
17
|
Requires-Python: >=3.10
|
|
18
18
|
Requires-Dist: nats-py>=2.12.0
|
|
19
|
+
Requires-Dist: numpy==2.4.2
|
|
19
20
|
Requires-Dist: opencv-python>=4.12.0.88
|
|
20
21
|
Requires-Dist: pyserial~=3.5
|
|
21
22
|
Description-Content-Type: text/markdown
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "puda-drivers"
|
|
3
|
-
version = "0.0.
|
|
3
|
+
version = "0.0.20"
|
|
4
4
|
description = "Hardware drivers for the PUDA platform."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
authors = [
|
|
@@ -21,6 +21,7 @@ license-files = ["LICEN[CS]E*"]
|
|
|
21
21
|
|
|
22
22
|
dependencies = [
|
|
23
23
|
"nats-py>=2.12.0",
|
|
24
|
+
"numpy==2.4.2", # Explicit numpy to ensure proper Windows wheels (avoids MINGW-W64 builds)
|
|
24
25
|
"opencv-python>=4.12.0.88",
|
|
25
26
|
"pyserial~=3.5",
|
|
26
27
|
]
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
|
|
3
|
+
"ordering": [
|
|
4
|
+
[
|
|
5
|
+
"A1"
|
|
6
|
+
]
|
|
7
|
+
|
|
8
|
+
],
|
|
9
|
+
"brand": {
|
|
10
|
+
"brand": "Custom",
|
|
11
|
+
"brandId": []
|
|
12
|
+
},
|
|
13
|
+
"metadata": {
|
|
14
|
+
"displayName": "MEA cell MTP",
|
|
15
|
+
"displayCategory": "functionalLabware",
|
|
16
|
+
"tags": []
|
|
17
|
+
},
|
|
18
|
+
"dimensions": {
|
|
19
|
+
"xDimension": 127,
|
|
20
|
+
"yDimension": 85,
|
|
21
|
+
"zDimension": 28
|
|
22
|
+
},
|
|
23
|
+
"wells": {
|
|
24
|
+
"A1": {
|
|
25
|
+
"shape": "square",
|
|
26
|
+
"x": 63.5,
|
|
27
|
+
"y": 42.5,
|
|
28
|
+
"z": 25
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"groups": [
|
|
32
|
+
{
|
|
33
|
+
"metadata": {
|
|
34
|
+
"wellBottomShape": "flat"
|
|
35
|
+
},
|
|
36
|
+
"wells": [
|
|
37
|
+
"A1"
|
|
38
|
+
]
|
|
39
|
+
}
|
|
40
|
+
],
|
|
41
|
+
"parameters": {
|
|
42
|
+
"format": "irregular",
|
|
43
|
+
"quirks": [],
|
|
44
|
+
"isTiprack": false,
|
|
45
|
+
"isMagneticModuleCompatible": false,
|
|
46
|
+
"loadName": "MEA_cell_MTP"
|
|
47
|
+
},
|
|
48
|
+
"namespace": "custom_beta",
|
|
49
|
+
"version": 1,
|
|
50
|
+
"schemaVersion": 2,
|
|
51
|
+
"cornerOffsetFromSlot": {
|
|
52
|
+
"x": 0,
|
|
53
|
+
"y": 0,
|
|
54
|
+
"z": 0
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -3,14 +3,14 @@
|
|
|
3
3
|
import json
|
|
4
4
|
import inspect
|
|
5
5
|
from pathlib import Path
|
|
6
|
-
from typing import Dict, Any, List
|
|
6
|
+
from typing import Dict, Any, List, Optional
|
|
7
7
|
from abc import ABC
|
|
8
8
|
from puda_drivers.core import Position
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
class StandardLabware(ABC):
|
|
12
12
|
"""
|
|
13
|
-
Generic Parent Class for all Labware on a microplate
|
|
13
|
+
Generic Parent Class for all Labware on a microplate
|
|
14
14
|
"""
|
|
15
15
|
def __init__(self, labware_name: str):
|
|
16
16
|
"""
|
|
@@ -63,7 +63,7 @@ class StandardLabware(ABC):
|
|
|
63
63
|
|
|
64
64
|
with open(definition_path, "r", encoding="utf-8") as f:
|
|
65
65
|
return json.load(f)
|
|
66
|
-
|
|
66
|
+
|
|
67
67
|
def __str__(self):
|
|
68
68
|
"""
|
|
69
69
|
Return a string representation of the labware.
|
|
@@ -92,7 +92,7 @@ class StandardLabware(ABC):
|
|
|
92
92
|
List of well identifiers (e.g., ["A1", "A2", "B1", ...])
|
|
93
93
|
"""
|
|
94
94
|
return list(self._wells.keys())
|
|
95
|
-
|
|
95
|
+
|
|
96
96
|
def get_well_position(self, well_id: str) -> Position:
|
|
97
97
|
"""
|
|
98
98
|
Get the position of a well from definition.json.
|
|
@@ -110,39 +110,65 @@ class StandardLabware(ABC):
|
|
|
110
110
|
well_id_upper = well_id.upper()
|
|
111
111
|
if well_id_upper not in self._wells:
|
|
112
112
|
raise KeyError(f"Well '{well_id}' not found in tip rack definition")
|
|
113
|
-
|
|
113
|
+
|
|
114
114
|
# Get the well data from the definition
|
|
115
115
|
well_data = self._wells.get(well_id_upper, {})
|
|
116
|
-
|
|
116
|
+
|
|
117
117
|
# Return position of the well (x, y are already center coordinates)
|
|
118
118
|
return Position(
|
|
119
119
|
x=well_data.get("x", 0.0),
|
|
120
120
|
y=well_data.get("y", 0.0),
|
|
121
|
-
z=well_data.get("z", 0.0),
|
|
121
|
+
z=well_data.get("z", 0.0),
|
|
122
122
|
)
|
|
123
|
-
|
|
124
|
-
def get_height(self) -> float:
|
|
123
|
+
|
|
124
|
+
def get_height(self, well_name: Optional[str] = None) -> float:
|
|
125
125
|
"""
|
|
126
126
|
Get the height of the labware.
|
|
127
|
-
|
|
127
|
+
|
|
128
|
+
Args:
|
|
129
|
+
well_name: Optional well name within the labware (e.g., "A1" for a well in a tiprack)
|
|
130
|
+
If not provided, the height of the labware is returned (zDimension).
|
|
128
131
|
Returns:
|
|
129
|
-
Height of the labware (zDimension)
|
|
132
|
+
Height of the labware (zDimension) or the height of the well (z)
|
|
130
133
|
|
|
131
134
|
Raises:
|
|
132
|
-
KeyError: If dimensions or zDimension is not found in the definition
|
|
135
|
+
KeyError: If dimensions or zDimension is not found in the definition, or if well_name is not found in the labware
|
|
133
136
|
"""
|
|
134
137
|
dimensions = self._definition.get("dimensions")
|
|
135
138
|
if dimensions is None:
|
|
136
139
|
raise KeyError("'dimensions' not found in labware definition")
|
|
137
140
|
|
|
138
|
-
if
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
141
|
+
if well_name:
|
|
142
|
+
well_data = self._wells.get(well_name.upper(), {})
|
|
143
|
+
if well_data is None:
|
|
144
|
+
raise KeyError(f"Well '{well_name}' not found in labware definition")
|
|
145
|
+
return well_data.get("z")
|
|
146
|
+
else:
|
|
147
|
+
if "zDimension" not in dimensions:
|
|
148
|
+
raise KeyError("'zDimension' not found in labware dimensions")
|
|
149
|
+
return dimensions["zDimension"]
|
|
150
|
+
|
|
143
151
|
def get_insert_depth(self) -> float:
|
|
144
152
|
"""
|
|
145
|
-
Get the insert depth of the labware.
|
|
153
|
+
Get the insert depth of the labware. This should be added to all labware definitions.
|
|
154
|
+
|
|
155
|
+
insert_depth represents the maximum internal Z-axis clearance of the
|
|
156
|
+
labware. It is calculated as the absolute difference between the Top
|
|
157
|
+
Plane (the highest point of the well opening) and the Internal Floor
|
|
158
|
+
(the physical bottom of the well).
|
|
159
|
+
|
|
160
|
+
Top of Labware (Z = 0 reference)
|
|
161
|
+
│ │
|
|
162
|
+
┌─────┴──────┴─────┐ <─── Opening
|
|
163
|
+
│ │
|
|
164
|
+
│ SPACE │ }
|
|
165
|
+
│ AVAILABLE │ }
|
|
166
|
+
│ FOR │ } insert_depth
|
|
167
|
+
│ INSERTION │ } (e.g., 40mm)
|
|
168
|
+
│ │ }
|
|
169
|
+
└──────┬───────────┘
|
|
170
|
+
│
|
|
171
|
+
Bottom of Well (Limit)
|
|
146
172
|
|
|
147
173
|
Returns:
|
|
148
174
|
Insert depth of the labware
|
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
"""
|
|
2
|
+
BiologicMachine class for handling Biologic device commands.
|
|
3
|
+
|
|
4
|
+
This module provides a wrapper around easy_biologic that enables dynamic command
|
|
5
|
+
handling via getattr, allowing command-based execution patterns for Biologic
|
|
6
|
+
electrochemical testing devices.
|
|
7
|
+
"""
|
|
8
|
+
import logging
|
|
9
|
+
import sys
|
|
10
|
+
from typing import Dict, Any, Type
|
|
11
|
+
|
|
12
|
+
logger = logging.getLogger(__name__)
|
|
13
|
+
|
|
14
|
+
# Only import easy_biologic on Windows (allows class inspection on Linux)
|
|
15
|
+
if sys.platform == "win32":
|
|
16
|
+
import easy_biologic as ebl
|
|
17
|
+
import easy_biologic.base_programs as blp
|
|
18
|
+
else:
|
|
19
|
+
ebl = None
|
|
20
|
+
blp = None
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class Biologic:
|
|
24
|
+
"""
|
|
25
|
+
Wrapper around easy_biologic that provides command handlers for Biologic devices.
|
|
26
|
+
|
|
27
|
+
This class wraps easy_biologic.base_programs to allow for dynamic method lookup
|
|
28
|
+
via getattr, enabling command-based execution patterns. Each method (OCV, CA, PEIS, etc.)
|
|
29
|
+
wraps the corresponding base program class and provides a consistent interface.
|
|
30
|
+
|
|
31
|
+
Example:
|
|
32
|
+
machine = Biologic("192.168.1.2")
|
|
33
|
+
handler = getattr(machine, "OCV", None) # Get handler dynamically
|
|
34
|
+
result = handler(params={"time": 60}, channels=[1, 2])
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
def __init__(self, device_ip: str):
|
|
38
|
+
"""
|
|
39
|
+
Initialize the Biologic machine.
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
device_ip: IP address of the Biologic device
|
|
43
|
+
"""
|
|
44
|
+
self.device_ip = device_ip
|
|
45
|
+
self.device = None
|
|
46
|
+
logger.info("BiologicMachine initialized with IP: %s (not connected yet)", device_ip)
|
|
47
|
+
|
|
48
|
+
def startup(self):
|
|
49
|
+
"""
|
|
50
|
+
Connect to the Biologic device.
|
|
51
|
+
|
|
52
|
+
This method should be called before executing any commands to establish
|
|
53
|
+
the connection to the device.
|
|
54
|
+
|
|
55
|
+
Raises:
|
|
56
|
+
OSError: If easy_biologic cannot be used (e.g., on non-Windows systems)
|
|
57
|
+
"""
|
|
58
|
+
if ebl is None:
|
|
59
|
+
raise OSError("easy_biologic can only be used on Windows.")
|
|
60
|
+
if self.device is None:
|
|
61
|
+
self.device = ebl.BiologicDevice(self.device_ip)
|
|
62
|
+
logger.info("BiologicDevice connected with IP: %s", self.device_ip)
|
|
63
|
+
else:
|
|
64
|
+
logger.warning("BiologicDevice already connected")
|
|
65
|
+
|
|
66
|
+
def _run_base_program(
|
|
67
|
+
self,
|
|
68
|
+
program_class: Type[blp.BiologicProgram],
|
|
69
|
+
params: dict[str, Any],
|
|
70
|
+
**kwargs
|
|
71
|
+
) -> Dict[str, Any]:
|
|
72
|
+
"""
|
|
73
|
+
Generic helper method to run any base program.
|
|
74
|
+
|
|
75
|
+
This method handles the common pattern:
|
|
76
|
+
1. Create the program instance with params and kwargs
|
|
77
|
+
2. Run the program (with appropriate signature)
|
|
78
|
+
3. Return the data
|
|
79
|
+
|
|
80
|
+
Args:
|
|
81
|
+
program_class: The base program class to instantiate (e.g., blp.OCV, blp.CA)
|
|
82
|
+
params: Dictionary of parameters for the program
|
|
83
|
+
**kwargs: Additional keyword arguments:
|
|
84
|
+
- For standard programs: retrieve_data [Default: True]
|
|
85
|
+
- For MPP/MPP_Cycles: data, by_channel, cv
|
|
86
|
+
- channels: Optional list of channel numbers (for constructor)
|
|
87
|
+
|
|
88
|
+
Returns:
|
|
89
|
+
Dictionary containing the program data
|
|
90
|
+
|
|
91
|
+
Raises:
|
|
92
|
+
RuntimeError: If startup() has not been called yet
|
|
93
|
+
"""
|
|
94
|
+
if self.device is None:
|
|
95
|
+
raise RuntimeError("Device not connected. Call startup() before executing commands.")
|
|
96
|
+
|
|
97
|
+
# Check program class type to determine run signature
|
|
98
|
+
# MPP and MPP_Cycles use: data, by_channel, cv
|
|
99
|
+
# MPP_Tracking uses: folder, by_channel
|
|
100
|
+
# Standard programs use: retrieve_data
|
|
101
|
+
if issubclass(program_class, blp.MPP):
|
|
102
|
+
# MPP/MPP_Cycles style: extract run parameters
|
|
103
|
+
data = kwargs.pop('data', 'data')
|
|
104
|
+
by_channel = kwargs.pop('by_channel', False)
|
|
105
|
+
cv_params = kwargs.pop('cv', {})
|
|
106
|
+
run_kwargs = {'data': data, 'by_channel': by_channel, 'cv': cv_params}
|
|
107
|
+
elif program_class == blp.MPP_Tracking:
|
|
108
|
+
# MPP_Tracking style: extract run parameters
|
|
109
|
+
folder = kwargs.pop('folder', None)
|
|
110
|
+
by_channel = kwargs.pop('by_channel', False)
|
|
111
|
+
run_kwargs = {'folder': folder, 'by_channel': by_channel}
|
|
112
|
+
else:
|
|
113
|
+
# Standard program style: extract retrieve_data
|
|
114
|
+
retrieve_data = kwargs.pop('retrieve_data', True)
|
|
115
|
+
run_kwargs = {'retrieve_data': retrieve_data}
|
|
116
|
+
|
|
117
|
+
# Create program instance - pass all remaining kwargs directly to constructor
|
|
118
|
+
program = program_class(
|
|
119
|
+
device=self.device,
|
|
120
|
+
params=params,
|
|
121
|
+
**kwargs
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
# Run the program with appropriate signature
|
|
125
|
+
program.run(**run_kwargs)
|
|
126
|
+
|
|
127
|
+
return program.data
|
|
128
|
+
|
|
129
|
+
def OCV(
|
|
130
|
+
self,
|
|
131
|
+
params: dict[str, Any],
|
|
132
|
+
**kwargs
|
|
133
|
+
) -> Dict[str, Any]:
|
|
134
|
+
"""
|
|
135
|
+
Run OCV (Open Circuit Voltage) test.
|
|
136
|
+
|
|
137
|
+
Args:
|
|
138
|
+
params: Dictionary containing:
|
|
139
|
+
- time: Test duration in seconds
|
|
140
|
+
- time_interval: Maximum time between readings. [Default: 1]
|
|
141
|
+
- voltage_interval: Maximum interval between voltage readings. [Default: 0.01]
|
|
142
|
+
**kwargs: Additional keyword arguments passed to program constructor:
|
|
143
|
+
- channels: Optional list of channel numbers
|
|
144
|
+
- retrieve_data: Whether to automatically retrieve data after running [Default: True]
|
|
145
|
+
|
|
146
|
+
Returns:
|
|
147
|
+
Dictionary containing the OCV data (keyed by channel)
|
|
148
|
+
"""
|
|
149
|
+
logger.info("Running OCV test: params=%s", params)
|
|
150
|
+
return self._run_base_program(blp.OCV, params, **kwargs)
|
|
151
|
+
|
|
152
|
+
def CA(
|
|
153
|
+
self,
|
|
154
|
+
params: dict[str, Any],
|
|
155
|
+
**kwargs
|
|
156
|
+
) -> Dict[str, Any]:
|
|
157
|
+
"""
|
|
158
|
+
Run CA (Chronoamperometry) test.
|
|
159
|
+
|
|
160
|
+
Args:
|
|
161
|
+
params: Dictionary containing:
|
|
162
|
+
- voltages: List of voltages.
|
|
163
|
+
- durations: List of times in seconds.
|
|
164
|
+
- vs_initial: If step is vs. initial or previous. [Default: False]
|
|
165
|
+
- time_interval: Maximum time interval between points. [Default: 1]
|
|
166
|
+
- current_interval: Maximum current change between points. [Default: 0.001]
|
|
167
|
+
- current_range: Current range. Use ec_lib.IRange. [Default: IRange.m10]
|
|
168
|
+
**kwargs: Additional keyword arguments passed to program constructor:
|
|
169
|
+
- channels: Optional list of channel numbers
|
|
170
|
+
- retrieve_data: Whether to automatically retrieve data after running [Default: True]
|
|
171
|
+
|
|
172
|
+
Returns:
|
|
173
|
+
Dictionary containing the CA data (keyed by channel)
|
|
174
|
+
"""
|
|
175
|
+
logger.info("Running CA test: params=%s", params)
|
|
176
|
+
return self._run_base_program(blp.CA, params, **kwargs)
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def PEIS(
|
|
180
|
+
self,
|
|
181
|
+
params: dict[str, Any],
|
|
182
|
+
**kwargs
|
|
183
|
+
) -> Dict[str, Any]:
|
|
184
|
+
"""
|
|
185
|
+
Run PEIS (Potentiostatic Electrochemical Impedance Spectroscopy) test.
|
|
186
|
+
|
|
187
|
+
Args:
|
|
188
|
+
params: Dictionary containing:
|
|
189
|
+
- voltage: Initial potential in Volts.
|
|
190
|
+
- amplitude_voltage: Sinus amplitude in Volts.
|
|
191
|
+
- initial_frequency: Initial frequency in Hertz.
|
|
192
|
+
- final_frequency: Final frequency in Hertz.
|
|
193
|
+
- frequency_number: Number of frequencies.
|
|
194
|
+
- duration: Overall duration in seconds.
|
|
195
|
+
- vs_initial: If step is vs. initial or previous. [Default: False]
|
|
196
|
+
- time_interval: Maximum time interval between points in seconds. [Default: 1]
|
|
197
|
+
- current_interval: Maximum time interval between points in Amps. [Default: 0.001]
|
|
198
|
+
- sweep: Defines whether the spacing between frequencies is logarithmic ('log') or linear ('lin'). [Default: 'log']
|
|
199
|
+
- repeat: Number of times to repeat the measurement and average the values for each frequency. [Default: 1]
|
|
200
|
+
- correction: Drift correction. [Default: False]
|
|
201
|
+
- wait: Adds a delay before the measurement at each frequency. The delay is expressed as a fraction of the period. [Default: 0]
|
|
202
|
+
**kwargs: Additional keyword arguments passed to program constructor:
|
|
203
|
+
- channels: Optional list of channel numbers
|
|
204
|
+
- retrieve_data: Whether to automatically retrieve data after running [Default: True]
|
|
205
|
+
|
|
206
|
+
Returns:
|
|
207
|
+
Dictionary containing the PEIS data (keyed by channel)
|
|
208
|
+
"""
|
|
209
|
+
logger.info("Running PEIS test: params=%s", params)
|
|
210
|
+
return self._run_base_program(blp.PEIS, params, **kwargs)
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def GEIS(
|
|
214
|
+
self,
|
|
215
|
+
params: dict[str, Any],
|
|
216
|
+
**kwargs
|
|
217
|
+
) -> Dict[str, Any]:
|
|
218
|
+
"""
|
|
219
|
+
Run GEIS (Galvanostatic Electrochemical Impedance Spectroscopy) test.
|
|
220
|
+
|
|
221
|
+
Args:
|
|
222
|
+
params: Dictionary containing:
|
|
223
|
+
- current: Initial current in Ampere.
|
|
224
|
+
- amplitude_current: Sinus amplitude in Ampere.
|
|
225
|
+
- initial_frequency: Initial frequency in Hertz.
|
|
226
|
+
- final_frequency: Final frequency in Hertz.
|
|
227
|
+
- frequency_number: Number of frequencies.
|
|
228
|
+
- duration: Overall duration in seconds.
|
|
229
|
+
- vs_initial: If step is vs. initial or previous. [Default: False]
|
|
230
|
+
- time_interval: Maximum time interval between points in seconds. [Default: 1]
|
|
231
|
+
- potential_interval: Maximum interval between points in Volts. [Default: 0.001]
|
|
232
|
+
- sweep: Defines whether the spacing between frequencies is logarithmic ('log') or linear ('lin'). [Default: 'log']
|
|
233
|
+
- repeat: Number of times to repeat the measurement and average the values for each frequency. [Default: 1]
|
|
234
|
+
- correction: Drift correction. [Default: False]
|
|
235
|
+
- wait: Adds a delay before the measurement at each frequency. The delay is expressed as a fraction of the period. [Default: 0]
|
|
236
|
+
**kwargs: Additional keyword arguments passed to program constructor:
|
|
237
|
+
- channels: Optional list of channel numbers
|
|
238
|
+
- retrieve_data: Whether to automatically retrieve data after running [Default: True]
|
|
239
|
+
|
|
240
|
+
Returns:
|
|
241
|
+
Dictionary containing the GEIS data (keyed by channel)
|
|
242
|
+
"""
|
|
243
|
+
logger.info("Running GEIS test: params=%s", params)
|
|
244
|
+
return self._run_base_program(blp.GEIS, params, **kwargs)
|
|
245
|
+
|
|
246
|
+
def CV(
|
|
247
|
+
self,
|
|
248
|
+
params: dict[str, Any],
|
|
249
|
+
**kwargs
|
|
250
|
+
) -> Dict[str, Any]:
|
|
251
|
+
"""
|
|
252
|
+
Run CV (Cyclic Voltammetry) test.
|
|
253
|
+
|
|
254
|
+
Args:
|
|
255
|
+
params: Dictionary containing:
|
|
256
|
+
- start: Start voltage. [Default: 0]
|
|
257
|
+
- end: End voltage. Boundary voltage in forward scan. [Default: 0.5]
|
|
258
|
+
- E2: Boundary voltage in backward scan. [Default: 0]
|
|
259
|
+
- Ef: End voltage in the final cycle scan [Default: 0]
|
|
260
|
+
- step: Voltage step. dEN/1000 [Default: 0.01]
|
|
261
|
+
- rate: Scan rate in V/s. [Default: 0.01]
|
|
262
|
+
- average: Average over points. [Default: False]
|
|
263
|
+
- N_Cycles: Number of cycles. [Default: 0]
|
|
264
|
+
**kwargs: Additional keyword arguments passed to program constructor:
|
|
265
|
+
- channels: Optional list of channel numbers
|
|
266
|
+
- retrieve_data: Whether to automatically retrieve data after running [Default: True]
|
|
267
|
+
|
|
268
|
+
Returns:
|
|
269
|
+
Dictionary containing the CV data (keyed by channel)
|
|
270
|
+
"""
|
|
271
|
+
logger.info("Running CV test: params=%s", params)
|
|
272
|
+
return self._run_base_program(blp.CV, params, **kwargs)
|
|
273
|
+
|
|
274
|
+
def MPP_Tracking(
|
|
275
|
+
self,
|
|
276
|
+
params: dict[str, Any],
|
|
277
|
+
**kwargs
|
|
278
|
+
) -> Dict[str, Any]:
|
|
279
|
+
"""
|
|
280
|
+
Run MPP_Tracking (Maximum Power Point Tracking) test.
|
|
281
|
+
|
|
282
|
+
Args:
|
|
283
|
+
params: Dictionary containing:
|
|
284
|
+
- run_time: Run time in seconds.
|
|
285
|
+
- init_vmpp: Initial v_mpp.
|
|
286
|
+
- probe_step: Voltage step for probe. [Default: 0.005 V]
|
|
287
|
+
- probe_points: Number of data points to collect for probe. [Default: 5]
|
|
288
|
+
- probe_interval: How often to probe in seconds. [Default: 2]
|
|
289
|
+
- record_interval: How often to record a data point in seconds. [Default: 1]
|
|
290
|
+
**kwargs: Additional keyword arguments:
|
|
291
|
+
- channels: Optional list of channel numbers
|
|
292
|
+
- folder: Folder or file for saving data [Default: None]
|
|
293
|
+
- by_channel: Save data by channel [Default: False]
|
|
294
|
+
|
|
295
|
+
Returns:
|
|
296
|
+
Dictionary containing the MPP_Tracking data (keyed by channel)
|
|
297
|
+
"""
|
|
298
|
+
logger.info("Running MPP_Tracking test: params=%s", params)
|
|
299
|
+
return self._run_base_program(blp.MPP_Tracking, params, **kwargs)
|
|
300
|
+
|
|
301
|
+
def MPP(
|
|
302
|
+
self,
|
|
303
|
+
params: dict[str, Any],
|
|
304
|
+
**kwargs
|
|
305
|
+
) -> Dict[str, Any]:
|
|
306
|
+
"""
|
|
307
|
+
Run MPP (Maximum Power Point) test.
|
|
308
|
+
|
|
309
|
+
Makes a CV scan and Voc scan and runs MPP tracking.
|
|
310
|
+
|
|
311
|
+
Args:
|
|
312
|
+
params: Dictionary containing:
|
|
313
|
+
- run_time: Run time in seconds.
|
|
314
|
+
- probe_step: Voltage step for probe. [Default: 0.005 V]
|
|
315
|
+
- probe_points: Number of data points to collect for probe. [Default: 5]
|
|
316
|
+
- probe_interval: How often to probe in seconds. [Default: 2]
|
|
317
|
+
- record_interval: How often to record a data point in seconds. [Default: 1]
|
|
318
|
+
**kwargs: Additional keyword arguments:
|
|
319
|
+
- channels: Optional list of channel numbers
|
|
320
|
+
- data: Data folder path. [Default: 'data']
|
|
321
|
+
- by_channel: Save data by channel. [Default: False]
|
|
322
|
+
- cv: Parameters passed to CV to find initial MPP, or {} for default. [Default: {}]
|
|
323
|
+
|
|
324
|
+
Returns:
|
|
325
|
+
Dictionary containing the MPP data (keyed by channel)
|
|
326
|
+
"""
|
|
327
|
+
logger.info("Running MPP test: params=%s", params)
|
|
328
|
+
return self._run_base_program(blp.MPP, params, **kwargs)
|
|
329
|
+
|
|
330
|
+
def MPP_Cycles(
|
|
331
|
+
self,
|
|
332
|
+
params: dict[str, Any],
|
|
333
|
+
**kwargs
|
|
334
|
+
) -> Dict[str, Any]:
|
|
335
|
+
"""
|
|
336
|
+
Run MPP_Cycles (MPP tracking with periodic CV scans) test.
|
|
337
|
+
|
|
338
|
+
Args:
|
|
339
|
+
params: Dictionary containing:
|
|
340
|
+
- run_time: Cycle run time in seconds.
|
|
341
|
+
- cycles: Number of cycles to perform.
|
|
342
|
+
- probe_step: Voltage step for probe. [Default: 0.01 V]
|
|
343
|
+
- probe_points: Number of data points to collect for probe. [Default: 5]
|
|
344
|
+
- probe_interval: How often to probe in seconds. [Default: 2]
|
|
345
|
+
- record_interval: How often to record a data point in seconds. [Default: 1]
|
|
346
|
+
**kwargs: Additional keyword arguments:
|
|
347
|
+
- channels: Optional list of channel numbers
|
|
348
|
+
- data: Data folder path. [Default: 'data']
|
|
349
|
+
- by_channel: Save data by channel. [Default: False]
|
|
350
|
+
- cv: Parameters for the CV. [Default: {}]
|
|
351
|
+
|
|
352
|
+
Returns:
|
|
353
|
+
Dictionary containing the MPP_Cycles data (keyed by channel)
|
|
354
|
+
"""
|
|
355
|
+
logger.info("Running MPP_Cycles test: params=%s", params)
|
|
356
|
+
return self._run_base_program(blp.MPP_Cycles, params, **kwargs)
|
|
357
|
+
|