Skip to content

Instrument management

Use the InstrumentManager() to start experiments and control your PalmSens instrument.

The most high-level way to start a measurement is to use the measure() function:

>>> import pypalmsens as ps

>>> method = ps.CyclicVoltammetry()
>>> ps.measure(method)

You can also manage the connection yourself, using connect(), for example:

>>> with ps.connect() as manager:
...    method = ps.ChronoAmperometry()
...    measurement = manager.measure(method)

Or using InstrumentManager() directly as a context manager:

>>> instruments = discover()

>>> with ps.InstrumentManager(instruments[0]) as manager:
...    measurement = manager.measure(method)

Or managing the instrument connection yourself:

>>> instruments = discover()

>>> manager = ps.InstrumentManager(instruments[0])
>>> manager.connect()
>>> # ...
>>> manager.disconnect()

For more information, see the measurement documentation.

Functions:

Classes:

pypalmsens.connect

connect(instrument: None | Instrument = None) -> InstrumentManager

Connect to instrument and return InstrumentManager.

Connects to any plugged-in PalmSens USB device. Error if multiple devices are plugged-in.

Parameters:

  • instrument

    (Instrument, default: None ) –

    Connect to a specific instrument. Use pypalmsens.discover() to discover instruments.

Returns:

  • manager ( InstrumentManager ) –

    Return instance of InstrumentManager connected to the given instrument.

Source code in src/pypalmsens/_instruments/instrument_manager.py
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
def connect(
    instrument: None | Instrument = None,
) -> InstrumentManager:
    """Connect to instrument and return InstrumentManager.

    Connects to any plugged-in PalmSens USB device.
    Error if multiple devices are plugged-in.

    Parameters
    ----------
    instrument : Instrument, optional
        Connect to a specific instrument.
        Use `pypalmsens.discover()` to discover instruments.

    Returns
    -------
    manager : InstrumentManager
        Return instance of `InstrumentManager` connected to the given instrument.
    """
    if not instrument:
        available_instruments = discover(ignore_errors=True)

        if not available_instruments:
            raise ConnectionError('No instruments were discovered.')

        if len(available_instruments) > 1:
            raise ConnectionError('More than one device discovered.')

        instrument = available_instruments[0]

    manager = InstrumentManager(instrument)
    manager.connect()
    return manager

pypalmsens.discover

discover(ftdi: bool = True, usbcdc: bool = True, winusb: bool = True, bluetooth: bool = False, serial: bool = True, ignore_errors: bool = False) -> list[Instrument]

Discover instruments.

For a list of device interfaces, see: https://dev.palmsens.com/python/latest/_attachments/installation/index.html#compatibility

Parameters:

  • ftdi

    (bool, default: True ) –

    If True, discover ftdi devices

  • usbcdc

    (bool, default: True ) –

    If True, discover usbcdc devices (Windows only)

  • winusb

    (bool, default: True ) –

    If True, discover winusb devices (Windows only)

  • bluetooth

    (bool, default: False ) –

    If True, discover bluetooth devices (Windows only)

  • serial

    (bool, default: True ) –

    If True, discover serial devices

  • ignore_errors

    (False, default: False ) –

    Ignores errors in device discovery

Returns:

  • discovered ( list[Instrument] ) –

    List of dataclasses with discovered instruments.

Source code in src/pypalmsens/_instruments/instrument.py
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
def discover(
    ftdi: bool = True,
    usbcdc: bool = True,
    winusb: bool = True,
    bluetooth: bool = False,
    serial: bool = True,
    ignore_errors: bool = False,
) -> list[Instrument]:
    """Discover instruments.

    For a list of device interfaces, see:
        https://dev.palmsens.com/python/latest/_attachments/installation/index.html#compatibility

    Parameters
    ----------
    ftdi : bool
        If True, discover ftdi devices
    usbcdc : bool
        If True, discover usbcdc devices (Windows only)
    winusb : bool
        If True, discover winusb devices (Windows only)
    bluetooth : bool
        If True, discover bluetooth devices (Windows only)
    serial : bool
        If True, discover serial devices
    ignore_errors : False
        Ignores errors in device discovery

    Returns
    -------
    discovered : list[Instrument]
        List of dataclasses with discovered instruments.
    """
    return asyncio.run(
        discover_async(
            ftdi=ftdi,
            usbcdc=usbcdc,
            winusb=winusb,
            bluetooth=bluetooth,
            serial=serial,
            ignore_errors=ignore_errors,
        )
    )

pypalmsens.measure

measure(method: MethodTypeCompatible, instrument: None | Instrument = None, callback: Callback | CallbackEIS | None = None, stream: str | Path | None = None) -> Measurement

Run measurement.

Executes the given method on any plugged-in PalmSens USB device. Error if multiple devices are plugged-in.

Parameters:

  • instrument

    (Instrument, default: None ) –

    Connect to and meassure on a specific instrument. Use pypalmsens.discover() to discover instruments.

  • callback

    (Callback | CallbackEIS | None, default: None ) –

    If specified, call this function on every new set of data points. New data points are batched, and contain all points since the last time it was called. Each point is an instance of ps.data.CallbackData for non-impedimetric or ps.data.CallbackDataEIS for impedimetric measurments.

  • stream

    (str | Path | None, default: None ) –

    If defined, stream data directly to this file in JSON Lines text format (https://jsonlines.org). This option is useful for long-term measurements. In case of a PC crash or power outage, the most recent measurement data will still be available.

Returns:

  • measurement ( Measurement ) –

    Finished measurement.

Source code in src/pypalmsens/_instruments/instrument_manager.py
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
def measure(
    method: MethodTypeCompatible,
    instrument: None | Instrument = None,
    callback: Callback | CallbackEIS | None = None,
    stream: str | Path | None = None,
) -> Measurement:
    """Run measurement.

    Executes the given method on any plugged-in PalmSens USB device.
    Error if multiple devices are plugged-in.

    Parameters
    ----------
    instrument : Instrument, optional
        Connect to and meassure on a specific instrument.
        Use `pypalmsens.discover()` to discover instruments.
    callback: Callback | CallbackEIS, optional
        If specified, call this function on every new set of data points.
        New data points are batched, and contain all points since the last
        time it was called. Each point is an instance of `ps.data.CallbackData`
        for non-impedimetric or `ps.data.CallbackDataEIS`
        for impedimetric measurments.
    stream: Path | str | None
        If defined, stream data directly to this file in JSON Lines text format
        (https://jsonlines.org). This option is useful for long-term measurements.
        In case of a PC crash or power outage, the most recent measurement data will
        still be available.

    Returns
    -------
    measurement : Measurement
        Finished measurement.
    """
    with connect(instrument=instrument) as manager:
        measurement = manager.measure(method, callback=callback, stream=stream)

    assert measurement

    return measurement

pypalmsens.Instrument dataclass

Instrument(id: str, interface: str, device: Device)

Dataclass holding instrument info.

Methods:

  • from_ip

    Create TCP instrument class.

  • from_port

    Create serial port instrument class.

Attributes:

  • baudrate (int) –

    Baud rate.

  • channel (int) –

    Channel index if part of a multichannel device.

  • device (Device) –

    Device connection class.

  • id (str) –

    Device ID of the instrument.

  • interface (str) –

    Type of the connection.

  • name (str) –

    Name of the instrument.

baudrate property

baudrate: int

Baud rate.

channel class-attribute instance-attribute

channel: int = field(init=False, default=-1)

Channel index if part of a multichannel device.

Returns -1 if instrument is not part of a multichannel device.

device class-attribute instance-attribute

device: Device = field(repr=False)

Device connection class.

id class-attribute instance-attribute

id: str = field(repr=False)

Device ID of the instrument.

interface instance-attribute

interface: str

Type of the connection.

name class-attribute instance-attribute

name: str = field(init=False)

Name of the instrument.

from_ip classmethod

from_ip(hostname: str, port: int = 49152) -> Instrument

Create TCP instrument class.

Use this method to connect to a device that is connected to the network, like a Nexus.

Parameters:

  • hostname

    (str) –

    Hostname or IP to connect to.

  • port

    (str, default: 49152 ) –

    Port to connect to (default: Nexus default port).

Returns:

  • instrument ( Instrument ) –

    Instrument dataclass

Source code in src/pypalmsens/_instruments/instrument.py
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
@classmethod
def from_ip(cls, hostname: str, port: int = 49152) -> Instrument:
    """Create TCP instrument class.

    Use this method to connect to a device that is connected to the network,
    like a Nexus.

    Parameters
    ----------
    hostname : str
        Hostname or IP to connect to.
    port : str
        Port to connect to (default: Nexus default port).

    Returns
    -------
    instrument : Instrument
        Instrument dataclass
    """
    device = PSDevices.TCPDevice(hostname, port)
    return cls._from_device(device)

from_port classmethod

from_port(port: str, *, baudrate: int | None = None) -> Instrument

Create serial port instrument class.

Parameters:

  • port

    (str) –

    Name of the port to connect to.

  • baudrate

    (int, default: None ) –

    Set the baudrate. If None, use the default baudrate.

Returns:

  • instrument ( Instrument ) –

    Instrument dataclass

Source code in src/pypalmsens/_instruments/instrument.py
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
@classmethod
def from_port(cls, port: str, *, baudrate: int | None = None) -> Instrument:
    """Create serial port instrument class.

    Parameters
    ----------
    port : str
        Name of the port to connect to.
    baudrate : int, optional
        Set the baudrate. If None, use the default baudrate.

    Returns
    -------
    instrument : Instrument
        Instrument dataclass
    """
    if baudrate is None:
        device = PSDevices.SerialPortDevice(port)
    else:
        device = PSDevices.SerialPortDevice(port, baudrate=baudrate)

    return cls._from_device(device)

pypalmsens.InstrumentManager

InstrumentManager(instrument: Instrument)

Instrument manager for PalmSens instruments.

Parameters:

  • instrument

    (Instrument) –

    Instrument to connect to, use discover() to find connected instruments.

Methods:

Attributes:

Source code in src/pypalmsens/_instruments/instrument_manager.py
121
122
123
124
125
126
127
128
129
def __init__(self, instrument: Instrument):
    self.instrument: Instrument = instrument
    """Instrument being managed by this class."""

    self.events: MeasurementEvents = MeasurementEvents()
    """Register functions to event hooks."""

    self._receive_message_callback: Callable[[str], None]
    self._comm: CommManager

capabilities property

capabilities: Capabilities

Retrieve device capabilities and device info as a dataclass.

Returns:

  • capabilities ( Capabilities ) –

    Device capabilities and device info.

events instance-attribute

Register functions to event hooks.

instrument instance-attribute

instrument: Instrument = instrument

Instrument being managed by this class.

abort

abort() -> None

Abort measurement.

Source code in src/pypalmsens/_instruments/instrument_manager.py
401
402
403
404
def abort(self) -> None:
    """Abort measurement."""
    with self._lock():
        self._comm.Abort()

connect

connect() -> None

Connect to instrument.

Source code in src/pypalmsens/_instruments/instrument_manager.py
177
178
179
180
181
182
183
184
185
186
187
188
def connect(self) -> None:
    """Connect to instrument."""
    if self.is_connected():
        return

    # The comm manager needs to open async, because the measurement is handled async.
    # Opening the comm manager in async sets some handlers in ClientConnection
    # that are sync or async specific. This affects the measurement,
    # receive status, and device state change events.
    self._comm = asyncio.run(self.instrument._connect_async())

    firmware_warning(self._comm.Capabilities)

disconnect

disconnect()

Disconnect from the instrument.

Source code in src/pypalmsens/_instruments/instrument_manager.py
496
497
498
499
500
501
502
503
def disconnect(self):
    """Disconnect from the instrument."""
    if not self.is_connected():
        return

    self._comm.Disconnect()

    del self._comm

ensure_connection

ensure_connection()

Raises connection error if the instrument is not connected.

Source code in src/pypalmsens/_instruments/instrument_manager.py
172
173
174
175
def ensure_connection(self):
    """Raises connection error if the instrument is not connected."""
    if not self.is_connected():
        raise ConnectionError('Not connected to an instrument')

get_current_range

get_current_range() -> AllowedCurrentRanges

Get the current range for the cell.

Returns:

Source code in src/pypalmsens/_instruments/instrument_manager.py
233
234
235
236
237
238
239
240
241
def get_current_range(self) -> AllowedCurrentRanges:
    """Get the current range for the cell.

    Returns
    -------
    current_range: AllowedCurrentRanges
    """
    with self._lock():
        return cr_enum_to_string(self._comm.CurrentRange)

get_estimated_duration

get_estimated_duration(method: Method | MethodTypeCompatible) -> float

Get the estimated duration for this method.

Parameters:

  • method

    (MethodType) –

    The method to get the estimated duration for.

Returns:

  • float

    Estimated duration in seconds.

Source code in src/pypalmsens/_instruments/instrument_manager_async.py
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
def get_estimated_duration(
    self: HasCommProtocol,
    method: PalmSens.Method | MethodTypeCompatible,
) -> float:
    """Get the estimated duration for this method.

    Parameters
    -----------
    method : MethodType
        The method to get the estimated duration for.

    Returns
    -------
    float
        Estimated duration in seconds.
    """
    self.ensure_connection()

    if not isinstance(method, PalmSens.Method):
        method = method._to_psmethod()

    capabilities = self._comm.Capabilities

    return method.GetMinimumEstimatedMeasurementDuration(capabilities)

get_instrument_serial

get_instrument_serial() -> str

Return instrument serial number.

Returns:

  • serial ( str ) –

    Instrument serial.

Source code in src/pypalmsens/_instruments/instrument_manager.py
302
303
304
305
306
307
308
309
310
311
312
313
def get_instrument_serial(self) -> str:
    """Return instrument serial number.

    Returns
    -------
    serial : str
        Instrument serial.
    """
    with self._lock():
        serial = self._comm.DeviceSerial.ToString()

    return serial

get_potential_range

get_potential_range() -> AllowedPotentialRanges

Get the potential range for the cell.

Returns:

Source code in src/pypalmsens/_instruments/instrument_manager.py
280
281
282
283
284
285
286
287
288
def get_potential_range(self) -> AllowedPotentialRanges:
    """Get the potential range for the cell.

    Returns
    -------
    potential_range: AllowedPotentialRanges
    """
    with self._lock():
        return pr_enum_to_string(self._comm.PotentialRange)

initialize_multiplexer

initialize_multiplexer(mux_model: int) -> int

Initialize the multiplexer.

Parameters:

  • mux_model

    (int) –

    The model of the multiplexer. - 0 = 8 channel - 1 = 16 channel - 2 = 32 channel

Returns:

  • channels ( int ) –

    Number of available multiplexes channels

Source code in src/pypalmsens/_instruments/instrument_manager.py
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
def initialize_multiplexer(self, mux_model: int) -> int:
    """Initialize the multiplexer.

    Parameters
    ----------
    mux_model: int
        The model of the multiplexer.
        - 0 = 8 channel
        - 1 = 16 channel
        - 2 = 32 channel

    Returns
    -------
    channels : int
        Number of available multiplexes channels
    """
    with self._lock():
        model = PalmSens.MuxModel(mux_model)

        if model == PalmSens.MuxModel.MUX8R2 and (
            self._comm.ClientConnection.GetType().Equals(
                clr.GetClrType(PalmSens.Comm.ClientConnectionPS4)
            )
            or self._comm.ClientConnection.GetType().Equals(
                clr.GetClrType(PalmSens.Comm.ClientConnectionMS)
            )
        ):
            self._comm.ClientConnection.ReadMuxInfo()

        self._comm.Capabilities.MuxModel = model

        if self._comm.Capabilities.MuxModel == PalmSens.MuxModel.MUX8:
            self._comm.Capabilities.NumMuxChannels = 8
        elif self._comm.Capabilities.MuxModel == PalmSens.MuxModel.MUX16:
            self._comm.Capabilities.NumMuxChannels = 16
        elif self._comm.Capabilities.MuxModel == PalmSens.MuxModel.MUX8R2:
            self._comm.ClientConnection.ReadMuxInfo()

    channels = self._comm.Capabilities.NumMuxChannels
    return channels

is_cell_on

is_cell_on() -> bool

Get cell status.

Returns:

  • cell_on ( bool ) –

    Return true if the cell is on

Source code in src/pypalmsens/_instruments/instrument_manager.py
209
210
211
212
213
214
215
216
217
218
def is_cell_on(self) -> bool:
    """Get cell status.

    Returns
    -------
    cell_on : bool
        Return true if the cell is on
    """
    with self._lock():
        return self._comm.CellOn

is_connected

is_connected() -> bool

Return True if an instrument connection exists.

Source code in src/pypalmsens/_instruments/instrument_manager.py
163
164
165
166
167
168
169
170
def is_connected(self) -> bool:
    """Return True if an instrument connection exists."""
    try:
        self._comm
    except AttributeError:
        return False
    else:
        return True

is_measuring

is_measuring() -> bool

Return True if device is measuring.

Source code in src/pypalmsens/_instruments/instrument_manager.py
143
144
145
def is_measuring(self) -> bool:
    """Return True if device is measuring."""
    return int(self._comm.State) == CommManager.DeviceState.Measurement

measure

measure(method: MethodTypeCompatible, *, callback: Callback | CallbackEIS | None = None, stream: Path | str | None = None) -> Measurement

Start measurement using given method parameters.

Parameters:

  • method

    (MethodTypeCompatible) –

    Method parameters for measurement

  • callback

    (Callback | CallbackEIS | None, default: None ) –

    If specified, call this function on every new set of data points. New data points are batched, and contain all points since the last time it was called. Each point is an instance of ps.data.CallbackData for non-impedimetric or ps.data.CallbackDataEIS for impedimetric measurments.

    For more advanced use cases, use InstrumentManager.events to register callbacks to various events.

  • stream

    (Path | str | None, default: None ) –

    If defined, stream data directly to this file in JSON Lines text format (https://jsonlines.org). This option is useful for long-term measurements. In case of a PC crash or power outage, the most recent measurement data will still be available.

Returns:

  • measurement ( Measurement ) –

    Finished measurement.

Source code in src/pypalmsens/_instruments/instrument_manager.py
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
def measure(
    self,
    method: MethodTypeCompatible,
    *,
    callback: Callback | CallbackEIS | None = None,
    stream: Path | str | None = None,
) -> Measurement:
    """Start measurement using given method parameters.

    Parameters
    ----------
    method: MethodType
        Method parameters for measurement
    callback: Callback, optional
        If specified, call this function on every new set of data points.
        New data points are batched, and contain all points since the last
        time it was called. Each point is an instance of `ps.data.CallbackData`
        for non-impedimetric or  `ps.data.CallbackDataEIS`
        for impedimetric measurments.

        For more advanced use cases, use `InstrumentManager.events`
        to register callbacks to various events.
    stream: Path | str | None
        If defined, stream data directly to this file in JSON Lines text format
        (https://jsonlines.org). This option is useful for long-term measurements.
        In case of a PC crash or power outage, the most recent measurement data will
        still be available.

    Returns
    -------
    measurement : Measurement
        Finished measurement.
    """
    self.ensure_connection()
    self.validate_method(method)

    # note that the comm manager must be opened async so it sets the
    # correct async event handlers
    measurement_manager = MeasurementManagerAsync(comm=self._comm)

    return asyncio.run(
        measurement_manager.measure(
            method,
            callback=callback,
            stream=stream,
            events=self.events,
        )
    )

read_current

read_current() -> float

Read the current in µA.

Returns:

  • current ( float ) –

    Current in µA.

Source code in src/pypalmsens/_instruments/instrument_manager.py
220
221
222
223
224
225
226
227
228
229
230
231
def read_current(self) -> float:
    """Read the current in µA.

    Returns
    -------
    current : float
        Current in µA.
    """
    with self._lock():
        current = self._comm.Current

    return current

read_potential

read_potential() -> float

Read the potential in V.

Returns:

  • potential ( float ) –

    Potential in V.

Source code in src/pypalmsens/_instruments/instrument_manager.py
255
256
257
258
259
260
261
262
263
264
265
266
267
def read_potential(self) -> float:
    """Read the potential in V.

    Returns
    -------
    potential : float
        Potential in V.
    """

    with self._lock():
        potential = self._comm.Potential

    return potential

register_receive_message_callback

register_receive_message_callback(callback: Callable[[str], None])

Register callback when a message is received.

The callback is triggered, for example, when a method is started, or when send_string is called in MethodSCRIPT.

Parameters:

  • callback

    (Callable[[str], None]) –

    The function to call when triggered

Source code in src/pypalmsens/_instruments/instrument_manager.py
315
316
317
318
319
320
321
322
323
324
325
326
327
def register_receive_message_callback(self, callback: Callable[[str], None], /):
    """Register callback when a message is received.

    The callback is triggered, for example, when a method is started,
    or when `send_string` is called in MethodSCRIPT.

    Parameters
    ----------
    callback: Callable[[str], None]
        The function to call when triggered
    """
    self._receive_message_callback = callback
    self._comm.ClientConnection.ReceiveMessage += self._receive_message_handler

set_cell

set_cell(cell_on: bool)

Turn the cell on or off.

Parameters:

  • cell_on

    (bool) –

    If true, turn on the cell

Source code in src/pypalmsens/_instruments/instrument_manager.py
198
199
200
201
202
203
204
205
206
207
def set_cell(self, cell_on: bool):
    """Turn the cell on or off.

    Parameters
    ----------
    cell_on : bool
        If true, turn on the cell
    """
    with self._lock():
        self._comm.CellOn = cell_on

set_current_range

set_current_range(current_range: AllowedCurrentRanges)

Set the current range for the cell.

Parameters:

  • current_range

    (AllowedCurrentRanges) –

    Set the current range as a string. See pypalmsens.settings.AllowedCurrentRanges for options.

Source code in src/pypalmsens/_instruments/instrument_manager.py
243
244
245
246
247
248
249
250
251
252
253
def set_current_range(self, current_range: AllowedCurrentRanges):
    """Set the current range for the cell.

    Parameters
    ----------
    current_range: AllowedCurrentRanges
        Set the current range as a string.
        See `pypalmsens.settings.AllowedCurrentRanges` for options.
    """
    with self._lock():
        self._comm.CurrentRange = cr_string_to_enum(current_range)

set_multiplexer_channel

set_multiplexer_channel(channel: int)

Sets the multiplexer channel.

Parameters:

  • channel

    (int) –

    Index of the channel to set.

Source code in src/pypalmsens/_instruments/instrument_manager.py
485
486
487
488
489
490
491
492
493
494
def set_multiplexer_channel(self, channel: int):
    """Sets the multiplexer channel.

    Parameters
    ----------
    channel : int
        Index of the channel to set.
    """
    with self._lock():
        self._comm.ClientConnection.SetMuxChannel(channel)

set_mux8r2_settings

Set the settings for the Mux8R2 multiplexer.

Parameters:

  • connect_sense_to_working_electrode

    (bool, default: False ) –

    Connect the sense electrode to the working electrode. Default is False.

  • combine_reference_and_counter_electrodes

    (bool, default: False ) –

    Combine the reference and counter electrodes. Default is False.

  • use_channel_1_reference_and_counter_electrodes

    (bool, default: False ) –

    Use channel 1 reference and counter electrodes for all working electrodes. Default is False.

  • set_unselected_channel_working_electrode

    (int, default: 0 ) –

    Set the unselected channel working electrode to disconnected/floating (0), ground (1), or standby potential (2). Default is 0.

Source code in src/pypalmsens/_instruments/instrument_manager.py
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
def set_mux8r2_settings(
    self,
    connect_sense_to_working_electrode: bool = False,
    combine_reference_and_counter_electrodes: bool = False,
    use_channel_1_reference_and_counter_electrodes: bool = False,
    set_unselected_channel_working_electrode: int = 0,
):
    """Set the settings for the Mux8R2 multiplexer.

    Parameters
    ---------
    connect_sense_to_working_electrode: float
        Connect the sense electrode to the working electrode. Default is False.
    combine_reference_and_counter_electrodes: float
        Combine the reference and counter electrodes. Default is False.
    use_channel_1_reference_and_counter_electrodes: float
        Use channel 1 reference and counter electrodes for all working electrodes. Default is False.
    set_unselected_channel_working_electrode: float
        Set the unselected channel working electrode to disconnected/floating (0), ground (1), or standby potential (2). Default is 0.
    """
    self.ensure_connection()

    if self._comm.Capabilities.MuxModel != PalmSens.MuxModel.MUX8R2:
        raise ValueError(
            f"Incompatible mux model: {self._comm.Capabilities.MuxModel}, expected 'MUXR2'."
        )

    mux_settings = PalmSens.Method.MuxSettings(False)
    mux_settings.ConnSEWE = connect_sense_to_working_electrode
    mux_settings.ConnectCERE = combine_reference_and_counter_electrodes
    mux_settings.CommonCERE = use_channel_1_reference_and_counter_electrodes
    mux_settings.UnselWE = PalmSens.Method.MuxSettings.UnselWESetting(
        set_unselected_channel_working_electrode
    )

    with self._lock():
        self._comm.ClientConnection.SetMuxSettings(MuxType(1), mux_settings)

set_potential

set_potential(potential: float)

Set the potential of the cell.

Parameters:

  • potential

    (float) –

    Potential in V

Source code in src/pypalmsens/_instruments/instrument_manager.py
269
270
271
272
273
274
275
276
277
278
def set_potential(self, potential: float):
    """Set the potential of the cell.

    Parameters
    ----------
    potential : float
        Potential in V
    """
    with self._lock():
        self._comm.Potential = potential

set_potential_range

Set the potential range for the cell.

Parameters:

  • potential_range

    (AllowedPotentialRanges) –

    Set the potential range as a string. See pypalmsens.settings.AllowedPotentialRanges for options.

Source code in src/pypalmsens/_instruments/instrument_manager.py
290
291
292
293
294
295
296
297
298
299
300
def set_potential_range(self, potential_range: AllowedPotentialRanges):
    """Set the potential range for the cell.

    Parameters
    ----------
    potential_range: AllowedPotentialRanges
        Set the potential range as a string.
        See `pypalmsens.settings.AllowedPotentialRanges` for options.
    """
    with self._lock():
        self._comm.PotentialRange = pr_string_to_enum(potential_range)

status

status() -> Status

Get status.

Source code in src/pypalmsens/_instruments/instrument_manager.py
190
191
192
193
194
195
196
def status(self) -> Status:
    """Get status."""
    self.ensure_connection()
    return Status(
        self._comm.get_Status(),
        device_state=str(self._comm.get_State()),  # type:ignore
    )

supported_applied_current_ranges

supported_applied_current_ranges() -> list[AllowedCurrentRanges]

List applied current ranges supported by this device.

Returns:

Source code in src/pypalmsens/_instruments/instrument_manager_async.py
158
159
160
161
162
163
164
165
166
167
def supported_applied_current_ranges(self: HasCommProtocol) -> list[AllowedCurrentRanges]:
    """List applied current ranges supported by this device.

    Returns
    -------
    current_ranges: list[AllowedCurrentRanges]
        List of supported current ranges.
    """
    self.ensure_connection()
    return CapabilitiesInterface(comm=self._comm).supported_applied_current_ranges

supported_bipot_current_ranges

supported_bipot_current_ranges() -> list[AllowedCurrentRanges]

List bipot current ranges supported by this device.

Returns:

Source code in src/pypalmsens/_instruments/instrument_manager_async.py
169
170
171
172
173
174
175
176
177
178
def supported_bipot_current_ranges(self: HasCommProtocol) -> list[AllowedCurrentRanges]:
    """List bipot current ranges supported by this device.

    Returns
    -------
    current_ranges: list[AllowedCurrentRanges]
        List of supported current ranges.
    """
    self.ensure_connection()
    return CapabilitiesInterface(comm=self._comm).supported_bipot_current_ranges

supported_current_ranges

supported_current_ranges() -> list[AllowedCurrentRanges]

List current ranges supported by this device.

Returns:

Source code in src/pypalmsens/_instruments/instrument_manager_async.py
147
148
149
150
151
152
153
154
155
156
def supported_current_ranges(self: HasCommProtocol) -> list[AllowedCurrentRanges]:
    """List current ranges supported by this device.

    Returns
    -------
    current_ranges: list[AllowedCurrentRanges]
        List of supported current ranges.
    """
    self.ensure_connection()
    return CapabilitiesInterface(comm=self._comm).supported_current_ranges

supported_methods

supported_methods() -> list[AllowedMethods]

List methods supported by this device.

Returns:

Source code in src/pypalmsens/_instruments/instrument_manager_async.py
136
137
138
139
140
141
142
143
144
145
def supported_methods(self: HasCommProtocol) -> list[AllowedMethods]:
    """List methods supported by this device.

    Returns
    -------
    methods: list[AllowedMethods]
        List of supported methods.
    """
    self.ensure_connection()
    return CapabilitiesInterface(comm=self._comm).supported_methods

supported_potential_ranges

supported_potential_ranges() -> list[AllowedPotentialRanges]

List applied potential ranges supported by this device.

Returns:

Source code in src/pypalmsens/_instruments/instrument_manager_async.py
180
181
182
183
184
185
186
187
188
189
def supported_potential_ranges(self: HasCommProtocol) -> list[AllowedPotentialRanges]:
    """List applied potential ranges supported by this device.

    Returns
    -------
    potential_ranges: list[AllowedPotentialRanges]
        List of supported potential ranges.
    """
    self.ensure_connection()
    return CapabilitiesInterface(comm=self._comm).supported_potential_ranges

unregister_receive_message_callback

unregister_receive_message_callback()

Unregister callback from message events.

Source code in src/pypalmsens/_instruments/instrument_manager.py
329
330
331
332
def unregister_receive_message_callback(self):
    """Unregister callback from message events."""
    self._comm.ClientConnection.ReceiveMessage -= self._receive_message_handler
    del self._receive_message_callback

validate_method

validate_method(method: MethodTypeCompatible)

Validate method.

Raise ValueError if the method cannot be validated.

Parameters:

Source code in src/pypalmsens/_instruments/instrument_manager_async.py
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
def validate_method(
    self: HasCommProtocol,
    method: MethodTypeCompatible,
):
    """Validate method.

    Raise ValueError if the method cannot be validated.

    Parameters
    -----------
    method: MethodType
        The method to validate.
    """
    self.ensure_connection()

    capabilities = self._comm.Capabilities

    psmethod = method._to_psmethod()
    errors = psmethod.Validate(capabilities)

    if any(error.IsFatal for error in errors):
        message = '\n'.join([error.Message for error in errors])
        raise MethodIncompatibleError(f'Method not compatible:\n{message}')

wait_digital_trigger

wait_digital_trigger(wait_for_high: bool)

Wait for digital trigger.

Parameters:

  • wait_for_high

    (bool) –

    Wait for digital line high before starting

Source code in src/pypalmsens/_instruments/instrument_manager.py
387
388
389
390
391
392
393
394
395
396
397
398
399
def wait_digital_trigger(self, wait_for_high: bool):
    """Wait for digital trigger.

    Parameters
    ----------
    wait_for_high: bool
        Wait for digital line high before starting
    """
    with self._lock():
        while True:
            if self._comm.DigitalLineD0 == wait_for_high:
                break
            sleep(0.05)

pypalmsens.InstrumentPool

Manages a set of instrument.

Most calls are run asynchronously in the background, which means that measurements are running in parallel.

This is a thin wrapper around the InstrumentPoolAsync class.

Parameters:

Methods:

  • add

    Open and add manager to the pool.

  • connect

    Connect all instrument managers in the pool.

  • disconnect

    Disconnect all instrument managers in the pool.

  • is_connected

    Return true if all managers in the pool are connected.

  • is_disconnected

    Return true if all managers in the pool are disconnected.

  • measure

    Concurrently run measurement on all managers in the pool.

  • remove

    Close and remove manager from pool.

  • status

    Return status for all managers in pool.

Attributes:

Source code in src/pypalmsens/_instruments/instrument_pool.py
30
31
32
33
34
35
36
37
38
def __init__(
    self,
    instruments_or_managers: Sequence[Instrument | InstrumentManagerAsync],
):
    self._async: InstrumentPoolAsync = InstrumentPoolAsync(instruments_or_managers)
    self._loop = asyncio.new_event_loop()

    self.managers: list[InstrumentManagerAsync] = self._async.managers
    """List of instruments managers in the pool."""

managers instance-attribute

managers: list[InstrumentManagerAsync] = self._async.managers

List of instruments managers in the pool.

add

Open and add manager to the pool.

Parameters:

Source code in src/pypalmsens/_instruments/instrument_pool.py
 96
 97
 98
 99
100
101
102
103
104
def add(self, manager: InstrumentManagerAsync) -> None:
    """Open and add manager to the pool.

    Parameters
    ----------
    manager : InstrumentManagerAsync
        Instance of an instrument manager.
    """
    self._loop.run_until_complete(self._async.add(manager))

connect

connect(attempts: int = 1) -> None

Connect all instrument managers in the pool.

Parameters:

  • attempts

    (int, default: 1 ) –

    Number of attempts to establish connection. Use this if you experience connection issues via USB.

Source code in src/pypalmsens/_instruments/instrument_pool.py
63
64
65
66
67
68
69
70
71
72
def connect(self, attempts: int = 1) -> None:
    """Connect all instrument managers in the pool.

    Parameters
    ----------
    attempts: int, optional
        Number of attempts to establish connection.
        Use this if you experience connection issues via USB.
    """
    self._loop.run_until_complete(self._async.connect(attempts=attempts))

disconnect

disconnect() -> None

Disconnect all instrument managers in the pool.

Source code in src/pypalmsens/_instruments/instrument_pool.py
74
75
76
def disconnect(self) -> None:
    """Disconnect all instrument managers in the pool."""
    self._loop.run_until_complete(self._async.disconnect())

is_connected

is_connected() -> bool

Return true if all managers in the pool are connected.

Source code in src/pypalmsens/_instruments/instrument_pool.py
78
79
80
def is_connected(self) -> bool:
    """Return true if all managers in the pool are connected."""
    return self._async.is_connected()

is_disconnected

is_disconnected() -> bool

Return true if all managers in the pool are disconnected.

Source code in src/pypalmsens/_instruments/instrument_pool.py
82
83
84
def is_disconnected(self) -> bool:
    """Return true if all managers in the pool are disconnected."""
    return self._async.is_disconnected()

measure

measure(method: MethodType, callback: Sequence[Callback | CallbackEIS] | Callback | CallbackEIS | None = None, **kwargs) -> list[Measurement]

Concurrently run measurement on all managers in the pool.

For hardware synchronization, set .general.use_hardware_sync on the method. For MethodSCRIPT, use 'set_channel_sync 1'.

In addition, the pool must contain: - channels from a single multichannel instrument only - the first channel of the multichannel instrument - at least two channels

All instruments are prepared and put in a waiting state. The measurements are started via a hardware sync trigger on channel 1.

Parameters:

  • method

    (MethodType) –

    Method parameters for measurement.

  • callback

    (list[Callback] | Callback | CallbackEIS | None, default: None ) –

    If specified, call these functions/this function on every new set of data points. New data points are batched, and contain all points since the last time it was called.

    Specify a sequence of callbacks to set a different function for every channel. The number of callbacks must match the number of channels.

    Specify a single callback to set the same function to all channels.

  • **kwargs

    These keyword parameters are passed to the measure function.

Source code in src/pypalmsens/_instruments/instrument_pool.py
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
def measure(
    self,
    method: MethodType,
    callback: Sequence[Callback | CallbackEIS] | Callback | CallbackEIS | None = None,
    **kwargs,
) -> list[Measurement]:
    """Concurrently run measurement on all managers in the pool.

    For hardware synchronization, set `.general.use_hardware_sync` on the method.
    For MethodSCRIPT, use 'set_channel_sync 1'.

    In addition, the pool must contain:
    - channels from a single multichannel instrument only
    - the first channel of the multichannel instrument
    - at least two channels

    All instruments are prepared and put in a waiting state.
    The measurements are started via a hardware sync trigger on channel 1.

    Parameters
    ----------
    method : MethodType
        Method parameters for measurement.
    callback : list[Callback] | Callback | CallbackEIS | None
        If specified, call these functions/this function on every new set of data points.
        New data points are batched, and contain all points since the last
        time it was called.

        Specify a sequence of callbacks to set a different function for every channel.
        The number of callbacks must match the number of channels.

        Specify a single callback to set the same function to all channels.
    **kwargs
        These keyword parameters are passed to the measure function.
    """
    return self._loop.run_until_complete(
        self._async.measure(method=method, callback=callback, **kwargs)
    )

remove

Close and remove manager from pool.

Parameters:

Source code in src/pypalmsens/_instruments/instrument_pool.py
86
87
88
89
90
91
92
93
94
def remove(self, manager: InstrumentManagerAsync) -> None:
    """Close and remove manager from pool.

    Parameters
    ----------
    manager : InstrumentManagerAsync
        Instance of an instrument manager.
    """
    self._loop.run_until_complete(self._async.remove(manager))

status

status() -> list[Status]

Return status for all managers in pool.

Returns:

Source code in src/pypalmsens/_instruments/instrument_pool.py
106
107
108
109
110
111
112
113
114
def status(self) -> list[Status]:
    """Return status for all managers in pool.

    Returns
    -------
    list[Status]
        List of status objects.
    """
    return [manager.status() for manager in self]

pypalmsens.MeasurementEvents

Register callbacks to measurement events.

For non-impedimetric measurements, use:

  • on_curve_begin
  • on_curve_new_data
  • on_curve_end

For impedimetric measurements, use:

  • on_eis_data_begin
  • on_eis_new_data
  • on_eis_data_end

Attributes:

on_curve_begin class-attribute instance-attribute

on_curve_begin: Callable[[Curve], None] | None = None

Called at the start of a new curve (for EIS use on_eis_data_start).

on_curve_end class-attribute instance-attribute

on_curve_end: Callable[[Curve], None] | None = None

Called at the end of a curve (for EIS use on_eis_data_end).

on_curve_new_data class-attribute instance-attribute

on_curve_new_data: Callable[[CallbackData], None] | None = None

Called when new data are received (for EIS use on_eis_new_data).

Note that the data are batched depending on available resources.

on_eis_data_begin class-attribute instance-attribute

on_eis_data_begin: Callable[[EISData], None] | None = None

Called at the start of a new EIS data set.

on_eis_data_end class-attribute instance-attribute

on_eis_data_end: Callable[[], None] | None = None

Called at the end of an EIS data set.

on_eis_new_data class-attribute instance-attribute

on_eis_new_data: Callable[[CallbackDataEIS], None] | None = None

Called when new eis data are received.

Data points are batched depending on available resources.

on_error class-attribute instance-attribute

on_error: Callable[[], None] | None = None

Called when a connection or communication error occurs.

on_measurement_begin class-attribute instance-attribute

on_measurement_begin: Callable[[Measurement], None] | None = None

Called at the start of a measurement.

on_measurement_end class-attribute instance-attribute

on_measurement_end: Callable[[], None] | None = None

Called at the end of a measurement.

setup class-attribute instance-attribute

setup: Callable[[], None] | None = None

Called before the measurement starts.

Use this to set up file resources, database connections, etc.

teardown class-attribute instance-attribute

teardown: Callable[[], None] | None = None

Called after the measurement has ended, either succesfully or after an error occurs.

Use this to close files or clean up resources.