stimgrid 0.2.0__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.
- stimgrid-0.2.0/LICENSE +21 -0
- stimgrid-0.2.0/PKG-INFO +317 -0
- stimgrid-0.2.0/README.md +287 -0
- stimgrid-0.2.0/pyproject.toml +48 -0
- stimgrid-0.2.0/setup.cfg +4 -0
- stimgrid-0.2.0/src/stimgrid/__init__.py +17 -0
- stimgrid-0.2.0/src/stimgrid/__main__.py +5 -0
- stimgrid-0.2.0/src/stimgrid/cli.py +177 -0
- stimgrid-0.2.0/src/stimgrid/devices.py +227 -0
- stimgrid-0.2.0/src/stimgrid/fit.py +421 -0
- stimgrid-0.2.0/src/stimgrid/loader.py +129 -0
- stimgrid-0.2.0/src/stimgrid.egg-info/PKG-INFO +317 -0
- stimgrid-0.2.0/src/stimgrid.egg-info/SOURCES.txt +16 -0
- stimgrid-0.2.0/src/stimgrid.egg-info/dependency_links.txt +1 -0
- stimgrid-0.2.0/src/stimgrid.egg-info/entry_points.txt +2 -0
- stimgrid-0.2.0/src/stimgrid.egg-info/top_level.txt +1 -0
- stimgrid-0.2.0/tests/test_cli.py +117 -0
- stimgrid-0.2.0/tests/test_fit.py +176 -0
stimgrid-0.2.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Sanjeev Muthusamy
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
stimgrid-0.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: stimgrid
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Fit stimulation pulses to what a stimulator can actually produce, and find the nearest exactly charge-balanced pulse.
|
|
5
|
+
Author: Sanjeev Muthusamy
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/sanjeevmuthusamy/stimgrid
|
|
8
|
+
Project-URL: Issues, https://github.com/sanjeevmuthusamy/stimgrid/issues
|
|
9
|
+
Keywords: neuroscience,electrophysiology,neurostimulation,electrical stimulation,microstimulation,charge balance,charge-balanced,biphasic pulse,asymmetric pulse,stimulation safety,multi-electrode array,MEA,microelectrode,neural interface,brain-computer interface,DAC quantization,Intan,RHS2116,Multi Channel Systems,STG4000,STG5,Tucker-Davis Technologies,IZ2,Cortical Labs
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
20
|
+
Classifier: Operating System :: OS Independent
|
|
21
|
+
Classifier: Environment :: Console
|
|
22
|
+
Classifier: Intended Audience :: Science/Research
|
|
23
|
+
Classifier: Topic :: Scientific/Engineering
|
|
24
|
+
Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
|
|
25
|
+
Classifier: Topic :: Scientific/Engineering :: Medical Science Apps.
|
|
26
|
+
Requires-Python: >=3.9
|
|
27
|
+
Description-Content-Type: text/markdown
|
|
28
|
+
License-File: LICENSE
|
|
29
|
+
Dynamic: license-file
|
|
30
|
+
|
|
31
|
+
# stimgrid
|
|
32
|
+
|
|
33
|
+
Your stimulator cannot produce every pulse you write down. It sets current in fixed steps and timing on
|
|
34
|
+
a fixed clock, so every value gets rounded. For an asymmetric pulse (for example a short strong first
|
|
35
|
+
phase and a long weak recharge phase) that rounding quietly breaks the charge balance you designed. A
|
|
36
|
+
rate of 130 Hz may also become 129.87 Hz.
|
|
37
|
+
|
|
38
|
+
stimgrid shows what your device will actually deliver, and finds the nearest pulse on the device's grid
|
|
39
|
+
whose two phases carry exactly equal charge. Pure Python, no dependencies.
|
|
40
|
+
|
|
41
|
+
For electrical neurostimulation and microstimulation on multi-electrode arrays (MEAs), microelectrodes
|
|
42
|
+
and neural interfaces, with presets for the Intan RHS2116, Multi Channel Systems STG4000 and STG5,
|
|
43
|
+
Tucker-Davis Technologies IZ2 and Cortical Labs CL, and support for any other current-controlled
|
|
44
|
+
stimulator from its current and timing steps.
|
|
45
|
+
|
|
46
|
+
**Research use only.** stimgrid works from the resolution you give it (or a preset's published
|
|
47
|
+
figures). Check the result on your own hardware and settings.
|
|
48
|
+
|
|
49
|
+
## Install
|
|
50
|
+
|
|
51
|
+
Requires Python 3.9 or newer, and nothing else.
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
pip install git+https://github.com/sanjeevmuthusamy/stimgrid
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Or from a downloaded copy, inside the `stimgrid` folder:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
pip install .
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
This gives you the `stimgrid` command. You can also run it without installing, from the folder, as
|
|
64
|
+
`PYTHONPATH=src python -m stimgrid ...`.
|
|
65
|
+
|
|
66
|
+
## Quick start
|
|
67
|
+
|
|
68
|
+
1. **Find your device.** `stimgrid devices` lists the built-in presets and where their figures come
|
|
69
|
+
from. If yours is not there, give its own steps instead (see [Devices](#devices)).
|
|
70
|
+
2. **Check your pulse.** Give the first phase, the second phase and the rate:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
stimgrid quick --device mcs-stg4000 --amplitude-ua 7.3 --width-us 117 \
|
|
74
|
+
--second-amplitude-ua 1.9 --second-width-us 450 --frequency-hz 130
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
3. **Read the result** (see [Reading the output](#reading-the-output)). Use the `balanced` pulse, or one
|
|
78
|
+
of the options, in your stimulation software.
|
|
79
|
+
4. **Save it for later or for a lab mate** as a [request file](#request-files), and rerun it with
|
|
80
|
+
`stimgrid fit my_pulse.json`.
|
|
81
|
+
|
|
82
|
+
Leave out `--second-amplitude-ua` and `--second-width-us` for a symmetric pulse; the second phase then
|
|
83
|
+
copies the first.
|
|
84
|
+
|
|
85
|
+
## Reading the output
|
|
86
|
+
|
|
87
|
+
For the quick start command above:
|
|
88
|
+
|
|
89
|
+
```
|
|
90
|
+
device: mcs-stg4000 (1.6 mA version): 20 µs time steps, 0.2 µA steps, max 1600 µA
|
|
91
|
+
plan 7.3 µA x 117 µs | gap 0 µs | 1.9 µA x 450 µs
|
|
92
|
+
rounded 7.4 µA x 120 µs | gap 0 µs | 2 µA x 460 µs
|
|
93
|
+
charge 0.888 nC (+3.97% vs plan), net -0.032 nC, imbalance 3.4783%
|
|
94
|
+
balanced 7.2 µA x 120 µs | gap 0 µs | 1.8 µA x 480 µs
|
|
95
|
+
charge 0.864 nC (+1.16% vs plan), net 0 nC, imbalance 0.0000%
|
|
96
|
+
option 1 7 µA x 120 µs | gap 0 µs | 2 µA x 420 µs
|
|
97
|
+
charge 0.84 nC (-1.65% vs plan), net 0 nC, imbalance 0.0000%
|
|
98
|
+
...
|
|
99
|
+
rate frequency_hz 130 Hz -> 129.87 Hz (385 steps per period, -0.100%)
|
|
100
|
+
|
|
101
|
+
WARNING SG201 simple rounding leaves the pulse unbalanced (imbalance 3.4783%, net -0.032 nC per pulse)
|
|
102
|
+
|
|
103
|
+
best pulse: {"amplitude_ua": 7.2, "width_us": 120, "second_amplitude_ua": 1.8, "second_width_us": 480}
|
|
104
|
+
result: OK
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
| Line | Meaning |
|
|
108
|
+
|---|---|
|
|
109
|
+
| `device` | The grid being used: time step, current step and maximum current |
|
|
110
|
+
| `plan` | The pulse you asked for: first phase, gap, second phase |
|
|
111
|
+
| `rounded` | What the device delivers if you type your plan in as it is |
|
|
112
|
+
| `balanced` | The closest pulse whose two phases carry exactly equal charge. **Use this one** |
|
|
113
|
+
| `option 1-3` | Other exactly balanced pulses, next closest first |
|
|
114
|
+
| `charge` | Charge in the first phase, and how far it moved from your plan |
|
|
115
|
+
| `net` | Charge left on the electrode by every pulse; 0 means balanced |
|
|
116
|
+
| `imbalance` | The difference between the two phases' charges, as a share of the larger one |
|
|
117
|
+
| `rate` | The rate you asked for, and the rate the device's clock really gives |
|
|
118
|
+
| `WARNING` / `ERROR` | Findings, explained in [Findings](#findings) |
|
|
119
|
+
| `best pulse` | The pulse to use, in a form you can paste into a file or program |
|
|
120
|
+
| `result` | `OK`, or `ERROR` if the pulse breaks a device limit or cannot be balanced |
|
|
121
|
+
|
|
122
|
+
Here the planned pulse would leave 0.032 nC on the electrode with every pulse; the balanced one leaves
|
|
123
|
+
none and keeps charge per phase within 1.2% of the plan.
|
|
124
|
+
|
|
125
|
+
## Another example: Intan RHS2116
|
|
126
|
+
|
|
127
|
+
An asymmetric pulse on an Intan RHS2116 with 0.1 µA steps, driven at 30,000 samples/s:
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
stimgrid quick --device intan-rhs2116 --step-ua 0.1 --sample-rate-hz 30000 \
|
|
131
|
+
--amplitude-ua 7.3 --width-us 117 --second-amplitude-ua 1.9 --second-width-us 450 --frequency-hz 40
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
```
|
|
135
|
+
device: intan-rhs2116 (step 0.1 µA, 30000 samples/s): 33.3333 µs time steps, 0.1 µA steps, max 25.5 µA
|
|
136
|
+
plan 7.3 µA x 117 µs | gap 0 µs | 1.9 µA x 450 µs
|
|
137
|
+
rounded 7.3 µA x 133.333 µs | gap 0 µs | 1.9 µA x 466.667 µs
|
|
138
|
+
charge 0.97333 nC (+13.96% vs plan), net 0.08667 nC, imbalance 8.9041%
|
|
139
|
+
balanced 7.2 µA x 100 µs | gap 0 µs | 1.8 µA x 400 µs
|
|
140
|
+
charge 0.72 nC (-15.70% vs plan), net 0 nC, imbalance 0.0000%
|
|
141
|
+
option 1 7.2 µA x 133.333 µs | gap 0 µs | 1.8 µA x 533.333 µs
|
|
142
|
+
charge 0.96 nC (+12.40% vs plan), net 0 nC, imbalance 0.0000%
|
|
143
|
+
...
|
|
144
|
+
WARNING SG201 simple rounding leaves the pulse unbalanced (imbalance 8.9041%, net 0.08667 nC per pulse)
|
|
145
|
+
WARNING SG203 the balanced pulse changes charge per phase by -15.7%
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Simple rounding would leave 0.087 nC behind on every pulse. stimgrid lists exactly balanced pulses
|
|
149
|
+
instead, closest to your plan first, and warns when the grid forces charge per phase to move.
|
|
150
|
+
|
|
151
|
+
## How it works
|
|
152
|
+
|
|
153
|
+
1. **Rounded.** Each amplitude, phase width and gap is rounded (halves up) to the device's steps and
|
|
154
|
+
clamped to its ranges. This is what you get if you type the plan in and let the device round it.
|
|
155
|
+
2. **Balanced.** stimgrid searches small changes to both phases, mostly the second phase's width and
|
|
156
|
+
amplitude, the usual balancing knobs. On a gridded device, balance is checked exactly, as whole
|
|
157
|
+
current steps times whole time steps, so "balanced" means equal to the last bit. On a device with
|
|
158
|
+
continuous amplitude it also tries the first-phase amplitude that keeps your planned charge exactly.
|
|
159
|
+
3. **Ranking.** Exactly balanced candidates are ranked by closeness to your plan, with first-phase charge
|
|
160
|
+
counted double because it sets the effect. The best and three alternatives are shown.
|
|
161
|
+
4. **Rates.** Each rate is rounded to a whole number of time steps per period, and the delivered rate is
|
|
162
|
+
reported.
|
|
163
|
+
|
|
164
|
+
## Command options
|
|
165
|
+
|
|
166
|
+
`stimgrid quick` takes the pulse as flags. Amplitudes are in µA, times in µs, rates in Hz.
|
|
167
|
+
|
|
168
|
+
| Flag | Meaning |
|
|
169
|
+
|---|---|
|
|
170
|
+
| `--device NAME` | A preset from `stimgrid devices` |
|
|
171
|
+
| `--step-ua`, `--sample-rate-hz`, `--range-ua` | Preset settings (see [Devices](#devices)) |
|
|
172
|
+
| `--time-step-us`, `--amplitude-step-ua` | Your own device's steps, without a preset (or to override one) |
|
|
173
|
+
| `--max-amplitude-ua`, `--max-charge-per-phase-nc` | Your own device's limits |
|
|
174
|
+
| `--amplitude-ua`, `--width-us` | First phase (required) |
|
|
175
|
+
| `--second-amplitude-ua`, `--second-width-us` | Second phase (defaults to the same as the first) |
|
|
176
|
+
| `--gap-us` | Gap between the phases |
|
|
177
|
+
| `--monophasic` | One phase only |
|
|
178
|
+
| `--frequency-hz`, `--intra-burst-hz`, `--burst-rate-hz` | Rates to check against the time grid |
|
|
179
|
+
| `--keep-first` | Keep the rounded first phase as it is; balance with the second phase only |
|
|
180
|
+
| `--max-imbalance 0.001` | Accept up to 0.1% imbalance as balanced (the default is exact) |
|
|
181
|
+
|
|
182
|
+
Both `quick` and `fit` also take:
|
|
183
|
+
|
|
184
|
+
| Flag | Meaning |
|
|
185
|
+
|---|---|
|
|
186
|
+
| `--format text` | Readable report (the default) |
|
|
187
|
+
| `--format json` | The full report as JSON, for scripts |
|
|
188
|
+
| `--format pulse` | Only the best pulse as JSON |
|
|
189
|
+
| `--strict` | Treat warnings as failures (exit code 1), for automated checks |
|
|
190
|
+
|
|
191
|
+
`stimgrid --help` and `stimgrid quick --help` list them too.
|
|
192
|
+
|
|
193
|
+
## Devices
|
|
194
|
+
|
|
195
|
+
| Preset | Time step | Amplitude | Source |
|
|
196
|
+
|---|---|---|---|
|
|
197
|
+
| `intan-rhs2116` | 1 / sample rate (`--sample-rate-hz`, default 30,000) | `--step-ua` of 0.01, 0.02, 0.05, 0.1, 0.2, 0.5, 1, 2, 5 or 10 µA, up to 255 steps | Intan RHS2116 datasheet |
|
|
198
|
+
| `cortical-labs-cl` | 20 µs | continuous (no step published), up to 3 µA, 3 nC per phase | Cortical Labs CL API docs (StimDesign) |
|
|
199
|
+
| `mcs-stg4000` | 20 µs, up to 25 kHz | `--range-ua 1600`: 0.2 µA steps; `--range-ua 16000`: 2 µA steps | MCS STG 4002/4004/4008 datasheets |
|
|
200
|
+
| `mcs-stg5` | 5 µs, up to 100 kHz | `--range-ua 160`: 0.1 µA; `1600`: 1 µA; `16000`: 10 µA steps | MCS STG5 brochure |
|
|
201
|
+
| `tdt-iz2` | 1 / RZ sample rate (`--sample-rate-hz`, default 24,414.0625) | continuous (no step published); `--range-ua 300` (IZ2, IZ2M) or `3000` (IZ2H, IZ2MH) | TDT IZ2 manual, IZ2M/IZ2MH spec sheet |
|
|
202
|
+
|
|
203
|
+
All presets are current mode. The MCS range is the model version you bought (STG4000) or the range you
|
|
204
|
+
select (STG5). The STG4000 160 µA version is left out because its official datasheet could not be
|
|
205
|
+
confirmed.
|
|
206
|
+
|
|
207
|
+
**Not included yet**, because the vendor does not publish a current step or a timing step:
|
|
208
|
+
|
|
209
|
+
| Device | Published | Missing |
|
|
210
|
+
|---|---|---|
|
|
211
|
+
| Axion Maestro Pro / Edge | ±250 µA, ±1.2 V, phase at least 100 µs | current step, time step |
|
|
212
|
+
| MaxWell MaxOne / MaxTwo | 32 stimulation units, ±1.0 V | current range and step, time step |
|
|
213
|
+
| Blackrock CereStim | phase 44–65,535 µs, 4 Hz–5 kHz, 100 µA steps (macro model) | micro model step, time step |
|
|
214
|
+
| A-M Systems 4100 | 100 mA with 1 µA resolution, pulses from 1 µs | time step |
|
|
215
|
+
|
|
216
|
+
If you have one of these, describe it with your own numbers (below), or send the values from your
|
|
217
|
+
manual and they can become a preset.
|
|
218
|
+
|
|
219
|
+
`stimgrid devices` prints each preset's source. The RHS2116 has no pulse timers; the host sets every
|
|
220
|
+
edge, so its timing grid depends on your host software. Any other stimulator can be described by its
|
|
221
|
+
own numbers:
|
|
222
|
+
|
|
223
|
+
```bash
|
|
224
|
+
stimgrid quick --time-step-us 10 --amplitude-step-ua 0.5 --max-amplitude-ua 100 \
|
|
225
|
+
--amplitude-ua 20 --width-us 105 --second-amplitude-ua 4 --second-width-us 520
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
Fields: `time_step_us` (required), `amplitude_step_ua` (leave out for continuous), `max_amplitude_ua`,
|
|
229
|
+
`min_phase_us`, `max_phase_us`, `max_charge_per_phase_nc`, `max_frequency_hz`.
|
|
230
|
+
|
|
231
|
+
## Request files
|
|
232
|
+
|
|
233
|
+
```json
|
|
234
|
+
{
|
|
235
|
+
"stimgrid": 1,
|
|
236
|
+
"device": {"preset": "intan-rhs2116", "step_ua": 0.1, "sample_rate_hz": 30000},
|
|
237
|
+
"pulse": {"amplitude_ua": 7.3, "width_us": 117, "second_amplitude_ua": 1.9, "second_width_us": 450},
|
|
238
|
+
"rates": {"frequency_hz": 40},
|
|
239
|
+
"max_imbalance": 0,
|
|
240
|
+
"keep_first": false
|
|
241
|
+
}
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
```bash
|
|
245
|
+
stimgrid fit examples/intan_asymmetric.json
|
|
246
|
+
stimgrid fit examples/intan_asymmetric.json --format json
|
|
247
|
+
cat request.json | stimgrid fit - --format pulse
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
A preset takes its options (`step_ua`, `sample_rate_hz` or `range_ua`, as in the table) and any device
|
|
251
|
+
field to override, for example `{"preset": "intan-rhs2116", "step_ua": 0.1, "max_amplitude_ua": 10}` or
|
|
252
|
+
`{"preset": "mcs-stg5", "range_ua": 160}`. Invalid input names the exact
|
|
253
|
+
place, for example `pulse: width_us must be a positive number`.
|
|
254
|
+
|
|
255
|
+
`--format pulse` prints only the best pulse, in the common shape (`amplitude_ua`, `width_us`,
|
|
256
|
+
`second_amplitude_ua`, `second_width_us`, `gap_us`), ready to paste into a protocol file or another tool.
|
|
257
|
+
|
|
258
|
+
## Findings
|
|
259
|
+
|
|
260
|
+
| Code | Level | Meaning |
|
|
261
|
+
|---|---|---|
|
|
262
|
+
| SG101 | error | An amplitude is above the device's maximum |
|
|
263
|
+
| SG102 | error | A phase width is outside the device's range |
|
|
264
|
+
| SG103 | error | An amplitude is below one current step |
|
|
265
|
+
| SG104 | error | Charge per phase is above the device's maximum |
|
|
266
|
+
| SG105 | warning | The interphase gap rounds to zero |
|
|
267
|
+
| SG106 | warning | Rounding moves a value by more than 10% |
|
|
268
|
+
| SG201 | warning | Simple rounding leaves the pulse unbalanced |
|
|
269
|
+
| SG202 | error | No balanced pulse was found within the search and the device's limits |
|
|
270
|
+
| SG203 | warning | The balanced pulse changes charge per phase by more than 5% |
|
|
271
|
+
| SG301 | warning | A rate changes by more than 0.1% on whole periods |
|
|
272
|
+
| SG303 | error | The pulse is longer than one period at that rate |
|
|
273
|
+
| SG304 | error | A rate is above the device's maximum |
|
|
274
|
+
|
|
275
|
+
## Exit codes
|
|
276
|
+
|
|
277
|
+
| Code | Meaning |
|
|
278
|
+
|---|---|
|
|
279
|
+
| 0 | Fitted with no errors (and no warnings, with `--strict`) |
|
|
280
|
+
| 1 | Errors (or warnings, with `--strict`) |
|
|
281
|
+
| 2 | Bad input or usage |
|
|
282
|
+
| 3 | Internal error |
|
|
283
|
+
|
|
284
|
+
## From Python
|
|
285
|
+
|
|
286
|
+
```python
|
|
287
|
+
from stimgrid import Pulse, Rates, fit, intan_rhs2116
|
|
288
|
+
|
|
289
|
+
report = fit(Pulse(7.3, 117, second_amplitude_ua=1.9, second_width_us=450),
|
|
290
|
+
intan_rhs2116(step_ua=0.1, sample_rate_hz=30000), Rates(frequency_hz=40))
|
|
291
|
+
print(report.balanced.as_pulse(), report.balanced.imbalance)
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
## Limits of this tool
|
|
295
|
+
|
|
296
|
+
- It models one pulse shape: rectangular phases with an optional gap. It does not model DAC settling,
|
|
297
|
+
electrode impedance, channel-to-channel mismatch or the RHS2116's trim settings.
|
|
298
|
+
- Rates are assumed to fall on the same time grid as phase widths.
|
|
299
|
+
- Continuous amplitude means the device's real amplitude resolution is unknown; the reported amplitude
|
|
300
|
+
may need rounding again on the hardware.
|
|
301
|
+
|
|
302
|
+
## Citing
|
|
303
|
+
|
|
304
|
+
If you use stimgrid in published work, please cite it. GitHub's "Cite this repository" button gives the
|
|
305
|
+
reference from `CITATION.cff`:
|
|
306
|
+
|
|
307
|
+
> Muthusamy S. stimgrid (version 0.2.0) [Computer software]. https://github.com/sanjeevmuthusamy/stimgrid
|
|
308
|
+
|
|
309
|
+
## Tests
|
|
310
|
+
|
|
311
|
+
```bash
|
|
312
|
+
python -m unittest discover -s tests
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
## Licence
|
|
316
|
+
|
|
317
|
+
MIT. See `LICENSE`.
|
stimgrid-0.2.0/README.md
ADDED
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
# stimgrid
|
|
2
|
+
|
|
3
|
+
Your stimulator cannot produce every pulse you write down. It sets current in fixed steps and timing on
|
|
4
|
+
a fixed clock, so every value gets rounded. For an asymmetric pulse (for example a short strong first
|
|
5
|
+
phase and a long weak recharge phase) that rounding quietly breaks the charge balance you designed. A
|
|
6
|
+
rate of 130 Hz may also become 129.87 Hz.
|
|
7
|
+
|
|
8
|
+
stimgrid shows what your device will actually deliver, and finds the nearest pulse on the device's grid
|
|
9
|
+
whose two phases carry exactly equal charge. Pure Python, no dependencies.
|
|
10
|
+
|
|
11
|
+
For electrical neurostimulation and microstimulation on multi-electrode arrays (MEAs), microelectrodes
|
|
12
|
+
and neural interfaces, with presets for the Intan RHS2116, Multi Channel Systems STG4000 and STG5,
|
|
13
|
+
Tucker-Davis Technologies IZ2 and Cortical Labs CL, and support for any other current-controlled
|
|
14
|
+
stimulator from its current and timing steps.
|
|
15
|
+
|
|
16
|
+
**Research use only.** stimgrid works from the resolution you give it (or a preset's published
|
|
17
|
+
figures). Check the result on your own hardware and settings.
|
|
18
|
+
|
|
19
|
+
## Install
|
|
20
|
+
|
|
21
|
+
Requires Python 3.9 or newer, and nothing else.
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
pip install git+https://github.com/sanjeevmuthusamy/stimgrid
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Or from a downloaded copy, inside the `stimgrid` folder:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
pip install .
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
This gives you the `stimgrid` command. You can also run it without installing, from the folder, as
|
|
34
|
+
`PYTHONPATH=src python -m stimgrid ...`.
|
|
35
|
+
|
|
36
|
+
## Quick start
|
|
37
|
+
|
|
38
|
+
1. **Find your device.** `stimgrid devices` lists the built-in presets and where their figures come
|
|
39
|
+
from. If yours is not there, give its own steps instead (see [Devices](#devices)).
|
|
40
|
+
2. **Check your pulse.** Give the first phase, the second phase and the rate:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
stimgrid quick --device mcs-stg4000 --amplitude-ua 7.3 --width-us 117 \
|
|
44
|
+
--second-amplitude-ua 1.9 --second-width-us 450 --frequency-hz 130
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
3. **Read the result** (see [Reading the output](#reading-the-output)). Use the `balanced` pulse, or one
|
|
48
|
+
of the options, in your stimulation software.
|
|
49
|
+
4. **Save it for later or for a lab mate** as a [request file](#request-files), and rerun it with
|
|
50
|
+
`stimgrid fit my_pulse.json`.
|
|
51
|
+
|
|
52
|
+
Leave out `--second-amplitude-ua` and `--second-width-us` for a symmetric pulse; the second phase then
|
|
53
|
+
copies the first.
|
|
54
|
+
|
|
55
|
+
## Reading the output
|
|
56
|
+
|
|
57
|
+
For the quick start command above:
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
device: mcs-stg4000 (1.6 mA version): 20 µs time steps, 0.2 µA steps, max 1600 µA
|
|
61
|
+
plan 7.3 µA x 117 µs | gap 0 µs | 1.9 µA x 450 µs
|
|
62
|
+
rounded 7.4 µA x 120 µs | gap 0 µs | 2 µA x 460 µs
|
|
63
|
+
charge 0.888 nC (+3.97% vs plan), net -0.032 nC, imbalance 3.4783%
|
|
64
|
+
balanced 7.2 µA x 120 µs | gap 0 µs | 1.8 µA x 480 µs
|
|
65
|
+
charge 0.864 nC (+1.16% vs plan), net 0 nC, imbalance 0.0000%
|
|
66
|
+
option 1 7 µA x 120 µs | gap 0 µs | 2 µA x 420 µs
|
|
67
|
+
charge 0.84 nC (-1.65% vs plan), net 0 nC, imbalance 0.0000%
|
|
68
|
+
...
|
|
69
|
+
rate frequency_hz 130 Hz -> 129.87 Hz (385 steps per period, -0.100%)
|
|
70
|
+
|
|
71
|
+
WARNING SG201 simple rounding leaves the pulse unbalanced (imbalance 3.4783%, net -0.032 nC per pulse)
|
|
72
|
+
|
|
73
|
+
best pulse: {"amplitude_ua": 7.2, "width_us": 120, "second_amplitude_ua": 1.8, "second_width_us": 480}
|
|
74
|
+
result: OK
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
| Line | Meaning |
|
|
78
|
+
|---|---|
|
|
79
|
+
| `device` | The grid being used: time step, current step and maximum current |
|
|
80
|
+
| `plan` | The pulse you asked for: first phase, gap, second phase |
|
|
81
|
+
| `rounded` | What the device delivers if you type your plan in as it is |
|
|
82
|
+
| `balanced` | The closest pulse whose two phases carry exactly equal charge. **Use this one** |
|
|
83
|
+
| `option 1-3` | Other exactly balanced pulses, next closest first |
|
|
84
|
+
| `charge` | Charge in the first phase, and how far it moved from your plan |
|
|
85
|
+
| `net` | Charge left on the electrode by every pulse; 0 means balanced |
|
|
86
|
+
| `imbalance` | The difference between the two phases' charges, as a share of the larger one |
|
|
87
|
+
| `rate` | The rate you asked for, and the rate the device's clock really gives |
|
|
88
|
+
| `WARNING` / `ERROR` | Findings, explained in [Findings](#findings) |
|
|
89
|
+
| `best pulse` | The pulse to use, in a form you can paste into a file or program |
|
|
90
|
+
| `result` | `OK`, or `ERROR` if the pulse breaks a device limit or cannot be balanced |
|
|
91
|
+
|
|
92
|
+
Here the planned pulse would leave 0.032 nC on the electrode with every pulse; the balanced one leaves
|
|
93
|
+
none and keeps charge per phase within 1.2% of the plan.
|
|
94
|
+
|
|
95
|
+
## Another example: Intan RHS2116
|
|
96
|
+
|
|
97
|
+
An asymmetric pulse on an Intan RHS2116 with 0.1 µA steps, driven at 30,000 samples/s:
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
stimgrid quick --device intan-rhs2116 --step-ua 0.1 --sample-rate-hz 30000 \
|
|
101
|
+
--amplitude-ua 7.3 --width-us 117 --second-amplitude-ua 1.9 --second-width-us 450 --frequency-hz 40
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
```
|
|
105
|
+
device: intan-rhs2116 (step 0.1 µA, 30000 samples/s): 33.3333 µs time steps, 0.1 µA steps, max 25.5 µA
|
|
106
|
+
plan 7.3 µA x 117 µs | gap 0 µs | 1.9 µA x 450 µs
|
|
107
|
+
rounded 7.3 µA x 133.333 µs | gap 0 µs | 1.9 µA x 466.667 µs
|
|
108
|
+
charge 0.97333 nC (+13.96% vs plan), net 0.08667 nC, imbalance 8.9041%
|
|
109
|
+
balanced 7.2 µA x 100 µs | gap 0 µs | 1.8 µA x 400 µs
|
|
110
|
+
charge 0.72 nC (-15.70% vs plan), net 0 nC, imbalance 0.0000%
|
|
111
|
+
option 1 7.2 µA x 133.333 µs | gap 0 µs | 1.8 µA x 533.333 µs
|
|
112
|
+
charge 0.96 nC (+12.40% vs plan), net 0 nC, imbalance 0.0000%
|
|
113
|
+
...
|
|
114
|
+
WARNING SG201 simple rounding leaves the pulse unbalanced (imbalance 8.9041%, net 0.08667 nC per pulse)
|
|
115
|
+
WARNING SG203 the balanced pulse changes charge per phase by -15.7%
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Simple rounding would leave 0.087 nC behind on every pulse. stimgrid lists exactly balanced pulses
|
|
119
|
+
instead, closest to your plan first, and warns when the grid forces charge per phase to move.
|
|
120
|
+
|
|
121
|
+
## How it works
|
|
122
|
+
|
|
123
|
+
1. **Rounded.** Each amplitude, phase width and gap is rounded (halves up) to the device's steps and
|
|
124
|
+
clamped to its ranges. This is what you get if you type the plan in and let the device round it.
|
|
125
|
+
2. **Balanced.** stimgrid searches small changes to both phases, mostly the second phase's width and
|
|
126
|
+
amplitude, the usual balancing knobs. On a gridded device, balance is checked exactly, as whole
|
|
127
|
+
current steps times whole time steps, so "balanced" means equal to the last bit. On a device with
|
|
128
|
+
continuous amplitude it also tries the first-phase amplitude that keeps your planned charge exactly.
|
|
129
|
+
3. **Ranking.** Exactly balanced candidates are ranked by closeness to your plan, with first-phase charge
|
|
130
|
+
counted double because it sets the effect. The best and three alternatives are shown.
|
|
131
|
+
4. **Rates.** Each rate is rounded to a whole number of time steps per period, and the delivered rate is
|
|
132
|
+
reported.
|
|
133
|
+
|
|
134
|
+
## Command options
|
|
135
|
+
|
|
136
|
+
`stimgrid quick` takes the pulse as flags. Amplitudes are in µA, times in µs, rates in Hz.
|
|
137
|
+
|
|
138
|
+
| Flag | Meaning |
|
|
139
|
+
|---|---|
|
|
140
|
+
| `--device NAME` | A preset from `stimgrid devices` |
|
|
141
|
+
| `--step-ua`, `--sample-rate-hz`, `--range-ua` | Preset settings (see [Devices](#devices)) |
|
|
142
|
+
| `--time-step-us`, `--amplitude-step-ua` | Your own device's steps, without a preset (or to override one) |
|
|
143
|
+
| `--max-amplitude-ua`, `--max-charge-per-phase-nc` | Your own device's limits |
|
|
144
|
+
| `--amplitude-ua`, `--width-us` | First phase (required) |
|
|
145
|
+
| `--second-amplitude-ua`, `--second-width-us` | Second phase (defaults to the same as the first) |
|
|
146
|
+
| `--gap-us` | Gap between the phases |
|
|
147
|
+
| `--monophasic` | One phase only |
|
|
148
|
+
| `--frequency-hz`, `--intra-burst-hz`, `--burst-rate-hz` | Rates to check against the time grid |
|
|
149
|
+
| `--keep-first` | Keep the rounded first phase as it is; balance with the second phase only |
|
|
150
|
+
| `--max-imbalance 0.001` | Accept up to 0.1% imbalance as balanced (the default is exact) |
|
|
151
|
+
|
|
152
|
+
Both `quick` and `fit` also take:
|
|
153
|
+
|
|
154
|
+
| Flag | Meaning |
|
|
155
|
+
|---|---|
|
|
156
|
+
| `--format text` | Readable report (the default) |
|
|
157
|
+
| `--format json` | The full report as JSON, for scripts |
|
|
158
|
+
| `--format pulse` | Only the best pulse as JSON |
|
|
159
|
+
| `--strict` | Treat warnings as failures (exit code 1), for automated checks |
|
|
160
|
+
|
|
161
|
+
`stimgrid --help` and `stimgrid quick --help` list them too.
|
|
162
|
+
|
|
163
|
+
## Devices
|
|
164
|
+
|
|
165
|
+
| Preset | Time step | Amplitude | Source |
|
|
166
|
+
|---|---|---|---|
|
|
167
|
+
| `intan-rhs2116` | 1 / sample rate (`--sample-rate-hz`, default 30,000) | `--step-ua` of 0.01, 0.02, 0.05, 0.1, 0.2, 0.5, 1, 2, 5 or 10 µA, up to 255 steps | Intan RHS2116 datasheet |
|
|
168
|
+
| `cortical-labs-cl` | 20 µs | continuous (no step published), up to 3 µA, 3 nC per phase | Cortical Labs CL API docs (StimDesign) |
|
|
169
|
+
| `mcs-stg4000` | 20 µs, up to 25 kHz | `--range-ua 1600`: 0.2 µA steps; `--range-ua 16000`: 2 µA steps | MCS STG 4002/4004/4008 datasheets |
|
|
170
|
+
| `mcs-stg5` | 5 µs, up to 100 kHz | `--range-ua 160`: 0.1 µA; `1600`: 1 µA; `16000`: 10 µA steps | MCS STG5 brochure |
|
|
171
|
+
| `tdt-iz2` | 1 / RZ sample rate (`--sample-rate-hz`, default 24,414.0625) | continuous (no step published); `--range-ua 300` (IZ2, IZ2M) or `3000` (IZ2H, IZ2MH) | TDT IZ2 manual, IZ2M/IZ2MH spec sheet |
|
|
172
|
+
|
|
173
|
+
All presets are current mode. The MCS range is the model version you bought (STG4000) or the range you
|
|
174
|
+
select (STG5). The STG4000 160 µA version is left out because its official datasheet could not be
|
|
175
|
+
confirmed.
|
|
176
|
+
|
|
177
|
+
**Not included yet**, because the vendor does not publish a current step or a timing step:
|
|
178
|
+
|
|
179
|
+
| Device | Published | Missing |
|
|
180
|
+
|---|---|---|
|
|
181
|
+
| Axion Maestro Pro / Edge | ±250 µA, ±1.2 V, phase at least 100 µs | current step, time step |
|
|
182
|
+
| MaxWell MaxOne / MaxTwo | 32 stimulation units, ±1.0 V | current range and step, time step |
|
|
183
|
+
| Blackrock CereStim | phase 44–65,535 µs, 4 Hz–5 kHz, 100 µA steps (macro model) | micro model step, time step |
|
|
184
|
+
| A-M Systems 4100 | 100 mA with 1 µA resolution, pulses from 1 µs | time step |
|
|
185
|
+
|
|
186
|
+
If you have one of these, describe it with your own numbers (below), or send the values from your
|
|
187
|
+
manual and they can become a preset.
|
|
188
|
+
|
|
189
|
+
`stimgrid devices` prints each preset's source. The RHS2116 has no pulse timers; the host sets every
|
|
190
|
+
edge, so its timing grid depends on your host software. Any other stimulator can be described by its
|
|
191
|
+
own numbers:
|
|
192
|
+
|
|
193
|
+
```bash
|
|
194
|
+
stimgrid quick --time-step-us 10 --amplitude-step-ua 0.5 --max-amplitude-ua 100 \
|
|
195
|
+
--amplitude-ua 20 --width-us 105 --second-amplitude-ua 4 --second-width-us 520
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Fields: `time_step_us` (required), `amplitude_step_ua` (leave out for continuous), `max_amplitude_ua`,
|
|
199
|
+
`min_phase_us`, `max_phase_us`, `max_charge_per_phase_nc`, `max_frequency_hz`.
|
|
200
|
+
|
|
201
|
+
## Request files
|
|
202
|
+
|
|
203
|
+
```json
|
|
204
|
+
{
|
|
205
|
+
"stimgrid": 1,
|
|
206
|
+
"device": {"preset": "intan-rhs2116", "step_ua": 0.1, "sample_rate_hz": 30000},
|
|
207
|
+
"pulse": {"amplitude_ua": 7.3, "width_us": 117, "second_amplitude_ua": 1.9, "second_width_us": 450},
|
|
208
|
+
"rates": {"frequency_hz": 40},
|
|
209
|
+
"max_imbalance": 0,
|
|
210
|
+
"keep_first": false
|
|
211
|
+
}
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
```bash
|
|
215
|
+
stimgrid fit examples/intan_asymmetric.json
|
|
216
|
+
stimgrid fit examples/intan_asymmetric.json --format json
|
|
217
|
+
cat request.json | stimgrid fit - --format pulse
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
A preset takes its options (`step_ua`, `sample_rate_hz` or `range_ua`, as in the table) and any device
|
|
221
|
+
field to override, for example `{"preset": "intan-rhs2116", "step_ua": 0.1, "max_amplitude_ua": 10}` or
|
|
222
|
+
`{"preset": "mcs-stg5", "range_ua": 160}`. Invalid input names the exact
|
|
223
|
+
place, for example `pulse: width_us must be a positive number`.
|
|
224
|
+
|
|
225
|
+
`--format pulse` prints only the best pulse, in the common shape (`amplitude_ua`, `width_us`,
|
|
226
|
+
`second_amplitude_ua`, `second_width_us`, `gap_us`), ready to paste into a protocol file or another tool.
|
|
227
|
+
|
|
228
|
+
## Findings
|
|
229
|
+
|
|
230
|
+
| Code | Level | Meaning |
|
|
231
|
+
|---|---|---|
|
|
232
|
+
| SG101 | error | An amplitude is above the device's maximum |
|
|
233
|
+
| SG102 | error | A phase width is outside the device's range |
|
|
234
|
+
| SG103 | error | An amplitude is below one current step |
|
|
235
|
+
| SG104 | error | Charge per phase is above the device's maximum |
|
|
236
|
+
| SG105 | warning | The interphase gap rounds to zero |
|
|
237
|
+
| SG106 | warning | Rounding moves a value by more than 10% |
|
|
238
|
+
| SG201 | warning | Simple rounding leaves the pulse unbalanced |
|
|
239
|
+
| SG202 | error | No balanced pulse was found within the search and the device's limits |
|
|
240
|
+
| SG203 | warning | The balanced pulse changes charge per phase by more than 5% |
|
|
241
|
+
| SG301 | warning | A rate changes by more than 0.1% on whole periods |
|
|
242
|
+
| SG303 | error | The pulse is longer than one period at that rate |
|
|
243
|
+
| SG304 | error | A rate is above the device's maximum |
|
|
244
|
+
|
|
245
|
+
## Exit codes
|
|
246
|
+
|
|
247
|
+
| Code | Meaning |
|
|
248
|
+
|---|---|
|
|
249
|
+
| 0 | Fitted with no errors (and no warnings, with `--strict`) |
|
|
250
|
+
| 1 | Errors (or warnings, with `--strict`) |
|
|
251
|
+
| 2 | Bad input or usage |
|
|
252
|
+
| 3 | Internal error |
|
|
253
|
+
|
|
254
|
+
## From Python
|
|
255
|
+
|
|
256
|
+
```python
|
|
257
|
+
from stimgrid import Pulse, Rates, fit, intan_rhs2116
|
|
258
|
+
|
|
259
|
+
report = fit(Pulse(7.3, 117, second_amplitude_ua=1.9, second_width_us=450),
|
|
260
|
+
intan_rhs2116(step_ua=0.1, sample_rate_hz=30000), Rates(frequency_hz=40))
|
|
261
|
+
print(report.balanced.as_pulse(), report.balanced.imbalance)
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
## Limits of this tool
|
|
265
|
+
|
|
266
|
+
- It models one pulse shape: rectangular phases with an optional gap. It does not model DAC settling,
|
|
267
|
+
electrode impedance, channel-to-channel mismatch or the RHS2116's trim settings.
|
|
268
|
+
- Rates are assumed to fall on the same time grid as phase widths.
|
|
269
|
+
- Continuous amplitude means the device's real amplitude resolution is unknown; the reported amplitude
|
|
270
|
+
may need rounding again on the hardware.
|
|
271
|
+
|
|
272
|
+
## Citing
|
|
273
|
+
|
|
274
|
+
If you use stimgrid in published work, please cite it. GitHub's "Cite this repository" button gives the
|
|
275
|
+
reference from `CITATION.cff`:
|
|
276
|
+
|
|
277
|
+
> Muthusamy S. stimgrid (version 0.2.0) [Computer software]. https://github.com/sanjeevmuthusamy/stimgrid
|
|
278
|
+
|
|
279
|
+
## Tests
|
|
280
|
+
|
|
281
|
+
```bash
|
|
282
|
+
python -m unittest discover -s tests
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
## Licence
|
|
286
|
+
|
|
287
|
+
MIT. See `LICENSE`.
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "stimgrid"
|
|
7
|
+
version = "0.2.0"
|
|
8
|
+
description = "Fit stimulation pulses to what a stimulator can actually produce, and find the nearest exactly charge-balanced pulse."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = { text = "MIT" }
|
|
11
|
+
requires-python = ">=3.9"
|
|
12
|
+
authors = [{ name = "Sanjeev Muthusamy" }]
|
|
13
|
+
dependencies = []
|
|
14
|
+
keywords = [
|
|
15
|
+
"neuroscience", "electrophysiology", "neurostimulation", "electrical stimulation", "microstimulation",
|
|
16
|
+
"charge balance", "charge-balanced", "biphasic pulse", "asymmetric pulse", "stimulation safety",
|
|
17
|
+
"multi-electrode array", "MEA", "microelectrode", "neural interface", "brain-computer interface",
|
|
18
|
+
"DAC quantization", "Intan", "RHS2116", "Multi Channel Systems", "STG4000", "STG5",
|
|
19
|
+
"Tucker-Davis Technologies", "IZ2", "Cortical Labs",
|
|
20
|
+
]
|
|
21
|
+
classifiers = [
|
|
22
|
+
"Development Status :: 3 - Alpha",
|
|
23
|
+
"License :: OSI Approved :: MIT License",
|
|
24
|
+
"Programming Language :: Python :: 3",
|
|
25
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
26
|
+
"Programming Language :: Python :: 3.9",
|
|
27
|
+
"Programming Language :: Python :: 3.10",
|
|
28
|
+
"Programming Language :: Python :: 3.11",
|
|
29
|
+
"Programming Language :: Python :: 3.12",
|
|
30
|
+
"Programming Language :: Python :: 3.13",
|
|
31
|
+
"Programming Language :: Python :: 3.14",
|
|
32
|
+
"Operating System :: OS Independent",
|
|
33
|
+
"Environment :: Console",
|
|
34
|
+
"Intended Audience :: Science/Research",
|
|
35
|
+
"Topic :: Scientific/Engineering",
|
|
36
|
+
"Topic :: Scientific/Engineering :: Bio-Informatics",
|
|
37
|
+
"Topic :: Scientific/Engineering :: Medical Science Apps.",
|
|
38
|
+
]
|
|
39
|
+
|
|
40
|
+
[project.urls]
|
|
41
|
+
Homepage = "https://github.com/sanjeevmuthusamy/stimgrid"
|
|
42
|
+
Issues = "https://github.com/sanjeevmuthusamy/stimgrid/issues"
|
|
43
|
+
|
|
44
|
+
[project.scripts]
|
|
45
|
+
stimgrid = "stimgrid.cli:main"
|
|
46
|
+
|
|
47
|
+
[tool.setuptools.packages.find]
|
|
48
|
+
where = ["src"]
|
stimgrid-0.2.0/setup.cfg
ADDED