# -*- coding: utf-8 -*-
from typing import List
from stg._wrapper.dll import (
System,
CURRENT,
VOLTAGE,
available,
select,
DeviceInfo,
StreamingInterface,
bitmap,
STGX,
)
[docs]class STG4000(STGX):
"""
This class implements the interface to download, start and stop stimulation.
At this point, you should pay attention to two specific details. First, indexing starts at 0. That means, the first channel is channel 0. Second, there is a difference between channels and triggers for the STG. Triggers are mapped to channels according to a channelmap. That means a single trigger can start stimulation of a whole set of channels. During initialization of the STG, we give this a sensible default. That means, all triggers are mapped to the respective channels following diagonal identity, i.e. trigger 0 maps to channel 0. Use :meth:`~STG4000.diagonalize_triggermap` to repeat this normalization.
Example
-------
.. code-block:: python
import time
from stg.api import STG4000
stg = STG4000()
stg.download(0,[1,-1, 0], [0.1, 0.1, 49.8])
while True:
time.sleep(0.5)
a.trigger()
stg.start_stimulation([0])
.. note::
* Indexing starts at zero
* Differentiate triggers and channels
"""
[docs] def stop_stimulation(self, triggerIndex: List[int] = []):
"""stops all trigger inputs or a selection based on a list
args
----
triggerIndex:List[int]
defaults to [], which stops stimulation at all channels. Give it a list of integers to start a specific subset of triggers, e.g. [0,1].
"""
if triggerIndex == []:
triggerIndex = [c for c in range(self.channel_count)]
with self.interface() as interface:
interface.SendStop(System.UInt32(bitmap(triggerIndex)))
[docs] def start_stimulation(self, triggerIndex: List[int] = []):
"""starts all trigger inputs or a selection based on a list
args
----
triggerIndex:List[int]
defaults to [], which starts stimulation at all channels. Give it a list of integers to start a specific subset of triggers, e.g. [0,1].
"""
if triggerIndex == []:
triggerIndex = [c for c in range(self.channel_count)]
with self.interface() as interface:
interface.SendStart(System.UInt32(bitmap(triggerIndex)))
[docs] def set_mode(self, channel_index: List[int] = [], mode: str = "current") -> int:
"""set a single or all channels to voltage or current mode
args
----
channel_index: list
defaults to [], which sets the mode at all channels. Give it a list of integers to set the mode only for a specific subset of channels, e.g. [0,1].
mode: str ("current", "voltage")
defaults to current
.. warning::
Because we primarily use current-mode, voltage mode is relatively untested. Additionally, i so far have not tested the behavior when different channels are in different modes. Be safe, and just set all channels to current-mode with :code:`stg.set_mode("current")`
"""
if mode == "current":
self._set_current_mode(channel_index)
return CURRENT
elif mode == "voltage":
self._set_voltage_mode(channel_index)
return VOLTAGE
else: # pragma no cover
raise ValueError(
f"Unknow mode {mode}. select either 'current' or ' 'voltage'"
)
[docs] def diagonalize_triggermap(self):
"""Give each trigger a sensible channel
Use this function to normalize the mapping of trigger to channel to a diagonal identity, i.e. trigger 0 maps to channel 0, so on.
+----------+---+---+---+---+---+---+---+---+
| Trigger | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
+----------+---+---+---+---+---+---+---+---+
| Channel | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
+----------+---+---+---+---+---+---+---+---+
"""
channelmap = []
syncoutmap = []
repeat = []
for chan_idx in range(0, self.channel_count):
repeat.append(1) # every trigger only once
syncoutmap.append(1 << chan_idx) # diagonal triggerout
channelmap.append(1 << chan_idx) # diagonal triggerin
with self.interface() as interface:
interface.SetupTrigger(0, channelmap, syncoutmap, repeat)
[docs] def download(
self,
channel_index: int = 0,
amplitudes_in_mA: List[float,] = [0],
durations_in_ms: List[float,] = [0],
mode="current",
):
"""Download a stimulation signal
.. Warning::
Any previous data sent to that channel is erased. Other channels stay untouched.
The signal is compressed as amplitudes and their respective durations
args
----
channel_index: int
The index of the channel for which to download the signal. Indexing
starts at 0
amplitudes_in_mA: List[float]
a list of amplitudes in mA/mV delivered for the corresponding duration
durations_in_ms: List[float]
a list of durations in ms determing how long each corresponding
amplitude is delivered
mode: str
defaults to current
Example
-------
.. code-block:: python
stg.download(channel_index = 0,
amplitudes_in_mA = [1, -1, 0],
durations_in_ms = [.1, .1, .488])
"""
if len(amplitudes_in_mA) != len(durations_in_ms):
raise ValueError("Every amplitude needs a duration and vice versa!")
amplitudes = [System.Int32(a * 1000_000) for a in amplitudes_in_mA]
durations = [System.UInt64(s * 1000) for s in durations_in_ms]
MODE = self.set_mode([channel_index], mode)
with self.interface() as interface:
interface.PrepareAndSendData(
System.UInt32(channel_index), amplitudes, durations, MODE
)