Skip to content

Commit

Permalink
Wrap up documentation for v0.3
Browse files Browse the repository at this point in the history
  • Loading branch information
agricolab committed Dec 20, 2019
1 parent 9be4e04 commit 8c8766c
Show file tree
Hide file tree
Showing 13 changed files with 261 additions and 130 deletions.
13 changes: 13 additions & 0 deletions docs/source/api.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
API
---

The api is located in `stg.api` and should be imported from there. It contains the following classes and methods:


.. toctree::
:maxdepth: 2

stg
pf


18 changes: 16 additions & 2 deletions docs/source/benchmark.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
Benchmark
---------

There are several possible ways how you can start stimulation on the STG. Depending on how you do that, different jitters and latencies apply.

All the following examples use our in-house Arduino interface to generate TTL of 1ms duration. This trigger signal is shown in the oscilloscope figures as blue trace. Above each figure, you can see the code which caused this behaviour, and each code required the following snippet for initialiazation.

.. code-block:: python
from stg.api import STG4000Streamer
Expand All @@ -12,11 +16,11 @@ Benchmark
a = Arduino()
print(a.enquire())
Trigger via TTL
***************

The fastest and most reliable by far is triggering via TTL when a stimulation has already been downloaded with :meth:`~stg._wrapper.downloadnet.STG4000.download`. In that regard, the latency from the TTL arriving at the STGs BNC to the actual stimulation occuring is aroung 65µs.

.. code-block:: python
stg.download(0,[1,-1, 0], [0.1, 0.1, 49.8])
Expand All @@ -33,6 +37,8 @@ Trigger via TTL
Trigger via USB
***************

Compared to the fastest possible, triggering via :meth:`~.STG4000.start_stimulation` is still quite good. It comes with a latency of 1 to 1.5 ms.

.. code-block:: python
stg.download(0,[1,-1, 0], [0.1, 0.1, 49.8])
Expand All @@ -49,6 +55,10 @@ Trigger via USB
Download on the fly
*******************

If we want to adapt the stimulation signal on the fly, we have to download it before starting it. Downloading takes a long time, and latency and jitter are drastically increased to around 200 to 250ms.

Additionally, although not shown here, any ongoing stimulation at this channel will be interrupted while the download is going on. This means adaptation of e.g. the amplitude of an ongoing repetitive stimulation is not possible, if that stimulation is at a frequency faster than 4 Hz.

.. code-block:: python
while True:
Expand All @@ -65,6 +75,10 @@ Download on the fly

Stream
******

Only in these cases, it makes sense to consider the streaming interface offered by :meth:`~stg._wrapper.streamingnet.STG4000Streamer.start_streaming`. It incurs a hefty testing cost and requires careful tuning if the buffer sizes, due to the non-preventable racing conditions involving multiple buffers. Yet, at around 125ms, the latency can be better than when downloading on the fly. Additonally it allows adaptation of ongoing stimulation at a frequency faster than 4 Hz.


.. code-block:: python
buffer_in_s=0.05
Expand Down
21 changes: 0 additions & 21 deletions docs/source/dll.rst

This file was deleted.

57 changes: 51 additions & 6 deletions docs/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,69 @@
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
Welcome to STG4000's documentation!
===================================

The API is located in stg.api. Import from there.

.. image:: https://img.shields.io/badge/License-MIT-blue.svg
:target: https://en.wikipedia.org/wiki/MIT_License


.. image:: https://github.com/pyreiz/ctrl-stg4000/workflows/pytest/badge.svg
:target: https://github.com/pyreiz/ctrl-stg4000/actions


.. image:: https://coveralls.io/repos/github/pyreiz/ctrl-stg4000/badge.svg?branch=develop
:target: https://coveralls.io/github/pyreiz/ctrl-stg4000?branch=develop



This documentation explains the python package wrapping the `C# .dll <https://www.multichannelsystems.com/software/mcsusbnetdll>`_ offered by
multichannelsystems to control their STG4000 range of electrical stimulators.

Installation
------------

.. code-block:: bash
git clone https://github.com/pyreiz/app-stg4000
cd app-stg4000
pip install -r requirements.txt
pip install -e .
#download and install the dll from mulitchannelsystems
python -m stg.install
Because of this, the pacakage only works on Windows. If the package is run on Linux, skip the installation of the requirements. On linux, the package automatically mocks the interface to the STG4000. This allows to run tests and build documentation, and can help when you write scripts for your experiments.

Testing
-------

Connect your oscilloscope and start the following example:

.. code-block:: python
from stg.api import PulseFile, STG4000
stg = STG4000()
stg.download(0, *PulseFile().compile())
stg.start_stimulation([0])
You can run full tests using pytest, mypy or everything with :code: `make test` from the root of the package. By default, downloading the dll is not tested, but can be turned on with :code:`pytest -m "install"`.


Documentation
-------------

.. toctree::
:maxdepth: 2
:caption: Contents:

dll
stg
api
benchmark
pf
example


Indices and tables
==================
******************

* :ref:`genindex`
* :ref:`modindex`
Expand Down
5 changes: 3 additions & 2 deletions docs/source/pf.rst
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
Signal Generation
-----------------
PulseFiles
----------



.. automodule:: stg.pulsefile
Expand Down
13 changes: 4 additions & 9 deletions docs/source/stg.rst
Original file line number Diff line number Diff line change
@@ -1,24 +1,19 @@
STG
---

Properties of the STG
*********************
STG4000
*******

.. automodule:: stg._wrapper.dll
:members: STGX


Download
********
++++++++

.. automodule:: stg._wrapper.downloadnet
:members: STG4000


Stream
******
++++++

.. automodule:: stg._wrapper.streamingnet
:members: STG4000Streamer


63 changes: 23 additions & 40 deletions readme.md
Original file line number Diff line number Diff line change
@@ -1,52 +1,35 @@
[![MIT license](https://img.shields.io/badge/License-MIT-blue.svg)](https://en.wikipedia.org/wiki/MIT_License) [![pytest-status](https://github.com/pyreiz/ctrl-stg4000/workflows/pytest/badge.svg)](https://github.com/pyreiz/ctrl-stg4000/actions) [![Coverage Status](https://coveralls.io/repos/github/pyreiz/ctrl-stg4000/badge.svg?branch=develop)](https://coveralls.io/github/pyreiz/ctrl-stg4000?branch=develop) [![Documentation Status](https://readthedocs.org/projects/ctrl-stg4000/badge/?version=latest)](https://ctrl-stg4000.readthedocs.io/en/latest/?badge=latest)

### STG4000
Welcome to STG4000's documentation!
===================================

This package wraps the C# dll ```McsUsbNet.dll``` for remote control of the multichannelsystem STG4002/4/8 in Python 3. The dll can be acquired from multichannelsystems directly at https://www.multichannelsystems.com/software/mcsusbnetdll or installed by script, i.e. by running ```python post_setup.py```.
This documentation explains the python package wrapping the [C# .dll](https://www.multichannelsystems.com/software/mcsusbnetdll) offered by
multichannelsystems to control their STG4000 range of electrical stimulators.

The dll allows to set stimulation settings in stream or download mode. Currently, this package only supports download mode.
Installation
------------

#### Installation
``` bash

```bash
git clone https://github.com/pyreiz/app-stg4000
cd app-stg4000
pip install -e .
python -m stg.install
git clone https://github.com/pyreiz/app-stg4000
cd app-stg4000
pip install -r requirements.txt
pip install -e .
#download and install the dll from mulitchannelsystems
python -m stg.install
```
The last command downloads and unzips the most recent 64-bit dll from multichannelsystems into ```./bin```
Because of this, the pacakage only works on Windows. If the package is run on Linux, skip the installation of the requirements. On linux, the package automatically mocks the interface to the STG4000. This allows to run tests and build documentation, and can help when you write scripts for your experiments.

#### Downloading a stimulation setting
Testing
-------

```python
from stg.api import STG4000
stim = STG4000()
# download a biphasic single pulse with an amplitude of +- 1mA, a pulsewidth of 1ms and a
# interstimulusinterval of 48ms to the first channel. Channel indexing starts at 0.
Connect your oscilloscope and start the following example:

stim.download(channel_index=0,
amplitude=[1, -1, 0],
duration=[1, 1, 48000])
``` python

# start the stimulation
stim.start_stimulation([0]) #trigger the first channel
# abort an ongoing stimulation
stim.start_stimulation()
from stg.api import PulseFile, STG4000
stg = STG4000()
stg.download(0, *PulseFile().compile())
stg.start_stimulation([0])
```
#### Creating and downloading repetitive pulses
```python
# for convenience, there is also a PulseFile class implemented
from stg.api import PulseFile
p = PulseFile(intensity_in_mA=1000, #in microamps, i.e. 1000 -> 1 mA
mode='biphasic', # can alternatively be monophasic
pulsewidth_in_ms=1, #in milliseconds, i.e. 1 -> 1ms
burstcount=3, # number of repetitions
isi_in_ms=48, #in milliseconds, i.e. 48 -> 48ms
)
amplitude, duration = p.compile()
stim.download(1, amplitude, duration)
stim.start_stimulation([1])
```
#### Triggering multiple channels

By default, the stg4002/4/8 uses a map to define which channels are to be triggered by which triggerinput. In that regard, ```stim.start_stimulation``` does actually not trigger a channel directly, but a trigger input, which is mapped to the respective channels. The triggermap is initialized during class instantiation to the identity diagonal, i.e. trigin 0 triggers channel 0, trigin 1 triggers channel 1. If you want to trigger multiple channels with a single trigin, you can use ```stim.start_stimulation([0,1])```.
You can run full tests using pytest, mypy or everything with :code: `make test` from the root of the package. By default, downloading the dll is not tested, but can be turned on with :code:`pytest -m "install"`.
3 changes: 1 addition & 2 deletions setup.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
from distutils.core import setup
from setuptools import find_namespace_packages

setup(
name="stg",
version="0.2.0",
version="0.3.0",
description="Control multichannelsystems STG 4002/4/8.",
long_description="Toolbox to control multichannelsystems STG 4002/4/8 via MCS.USB.DLL",
author="Robert Guggenberger",
Expand Down
14 changes: 14 additions & 0 deletions stg/_wrapper/dll.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,20 @@ def __init__(self, info: DeviceInfo, buffer_size: int = 50_000):


class STGX(ABC):
"""
The STGX is the base class for the STG4000 and wraps the basic USB interface and reads all the properties that are determined by the specific STG connected to your PC. Additionally methods for downloading or streaming are implemented by subclasses. Ideally, just use :code:`from stg.api import STG4000`, and you will get the class implementing all bells and whistles.
When initialized without arguments, i.e. :code:`stg = STG4000()`, it looks through your USB ports and connects with the first STGs it finds. If you want to use a specific STG, initialize the class with the serial number of the device, e.g. using :code:`stg = STG4000(serial=12345)`.
Immediatly after the connection is established, we eagerly read all properties from the stimulator, e.g. the number of channels or the output resolution. This read-only properties are than cached, to prevent any later overhead. This means it might take a few seconds until the :code:`stg` is initialized, but it saves you precious milliseconds when you later stimulate.
Because at any time, only one process can be connected with a specific STG the connection is implemented using a :code:`with ... as` idiom. This should therefore be relatively safe. It is still possible that the STG can get into a weird state. In that case, try turning it off and on again.
.. note::
* Properties are eagerly loaded and cached during initalization
"""

def __init__(self, serial: int = None):
if serial is None:
try:
Expand Down

0 comments on commit 8c8766c

Please sign in to comment.