pstutils 0.1.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.
- pstutils-0.1.0/MANIFEST.in +2 -0
- pstutils-0.1.0/PKG-INFO +56 -0
- pstutils-0.1.0/README.md +42 -0
- pstutils-0.1.0/pyproject.toml +34 -0
- pstutils-0.1.0/setup.cfg +4 -0
- pstutils-0.1.0/src/pstutils/__init__.py +13 -0
- pstutils-0.1.0/src/pstutils/maps/1S256.mat +0 -0
- pstutils-0.1.0/src/pstutils/maps/1S512-N.mat +0 -0
- pstutils-0.1.0/src/pstutils/maps/1S512.mat +0 -0
- pstutils-0.1.0/src/pstutils/maps/4S1024.mat +0 -0
- pstutils-0.1.0/src/pstutils/maps/4S1024_mod.mat +0 -0
- pstutils-0.1.0/src/pstutils/maps/8S1024.mat +0 -0
- pstutils-0.1.0/src/pstutils/maps/8S1024_mod.mat +0 -0
- pstutils-0.1.0/src/pstutils/nex/NexFileData.py +278 -0
- pstutils-0.1.0/src/pstutils/nex/NexFileHeaders.py +416 -0
- pstutils-0.1.0/src/pstutils/nex/NexFileReaders.py +240 -0
- pstutils-0.1.0/src/pstutils/nex/NexFileWriters.py +297 -0
- pstutils-0.1.0/src/pstutils/nex/__init__.py +0 -0
- pstutils-0.1.0/src/pstutils/pstutils.py +946 -0
- pstutils-0.1.0/src/pstutils.egg-info/PKG-INFO +56 -0
- pstutils-0.1.0/src/pstutils.egg-info/SOURCES.txt +23 -0
- pstutils-0.1.0/src/pstutils.egg-info/dependency_links.txt +1 -0
- pstutils-0.1.0/src/pstutils.egg-info/requires.txt +6 -0
- pstutils-0.1.0/src/pstutils.egg-info/top_level.txt +1 -0
- pstutils-0.1.0/tests/tests.py +43 -0
pstutils-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pstutils
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: This package is internal to Plexon. It includes functions to be used in conjunction with the SiNAPS Data Pipeline.
|
|
5
|
+
Author-email: Nikhil Chandra <nikhil@plexon.com>, Chris Heydrick <chris@plexon.com>
|
|
6
|
+
Requires-Python: >3.9
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
Requires-Dist: phylib==2.4.3
|
|
9
|
+
Requires-Dist: numpy==1.26.4
|
|
10
|
+
Requires-Dist: pypl2
|
|
11
|
+
Requires-Dist: pypixelmapdump
|
|
12
|
+
Requires-Dist: debugpy
|
|
13
|
+
Requires-Dist: wheel
|
|
14
|
+
|
|
15
|
+
# Introduction
|
|
16
|
+
The pstutils Python package manages an API that exposes functions written for various applications that form a part of the SiNAPS Data Pipeline (e.g., PL2STASH, NPY2NEX)
|
|
17
|
+
|
|
18
|
+
# Clone repository
|
|
19
|
+
```bash
|
|
20
|
+
mkdir pstutils
|
|
21
|
+
cd
|
|
22
|
+
git clone git@192.168.37.103:nikhilchandra/pstutils.git .
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
# Package
|
|
26
|
+
There are a few different options for packaging this project into a .tar.gz file.
|
|
27
|
+
|
|
28
|
+
## Option 1: Python Virtual Environment
|
|
29
|
+
```bash
|
|
30
|
+
python -m venv venv
|
|
31
|
+
./venv/scripts/activate
|
|
32
|
+
python -m pip install setuptools build
|
|
33
|
+
python -m build
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Option 2: Conda Environment
|
|
37
|
+
```bash
|
|
38
|
+
conda create -n <env_name> python=3.<version #> --yes
|
|
39
|
+
conda activate <env_name>
|
|
40
|
+
python -m pip install build
|
|
41
|
+
python -m build
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
# Install package to virtual environment
|
|
45
|
+
|
|
46
|
+
## From .tar.gz
|
|
47
|
+
You can install the package from the .tar.gz file generated in the previous step.
|
|
48
|
+
```bash
|
|
49
|
+
python -m pip install ./dist/<name-of-tar.gz-file>
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## From Plexon's private GitLab server
|
|
53
|
+
Alternatively, you can install the package directly from the repository stored on Plexon's GitLab server.
|
|
54
|
+
```bash
|
|
55
|
+
pip install git+http://192.168.37.103/nikhilchandra/pstutils
|
|
56
|
+
```
|
pstutils-0.1.0/README.md
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Introduction
|
|
2
|
+
The pstutils Python package manages an API that exposes functions written for various applications that form a part of the SiNAPS Data Pipeline (e.g., PL2STASH, NPY2NEX)
|
|
3
|
+
|
|
4
|
+
# Clone repository
|
|
5
|
+
```bash
|
|
6
|
+
mkdir pstutils
|
|
7
|
+
cd
|
|
8
|
+
git clone git@192.168.37.103:nikhilchandra/pstutils.git .
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
# Package
|
|
12
|
+
There are a few different options for packaging this project into a .tar.gz file.
|
|
13
|
+
|
|
14
|
+
## Option 1: Python Virtual Environment
|
|
15
|
+
```bash
|
|
16
|
+
python -m venv venv
|
|
17
|
+
./venv/scripts/activate
|
|
18
|
+
python -m pip install setuptools build
|
|
19
|
+
python -m build
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Option 2: Conda Environment
|
|
23
|
+
```bash
|
|
24
|
+
conda create -n <env_name> python=3.<version #> --yes
|
|
25
|
+
conda activate <env_name>
|
|
26
|
+
python -m pip install build
|
|
27
|
+
python -m build
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
# Install package to virtual environment
|
|
31
|
+
|
|
32
|
+
## From .tar.gz
|
|
33
|
+
You can install the package from the .tar.gz file generated in the previous step.
|
|
34
|
+
```bash
|
|
35
|
+
python -m pip install ./dist/<name-of-tar.gz-file>
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## From Plexon's private GitLab server
|
|
39
|
+
Alternatively, you can install the package directly from the repository stored on Plexon's GitLab server.
|
|
40
|
+
```bash
|
|
41
|
+
pip install git+http://192.168.37.103/nikhilchandra/pstutils
|
|
42
|
+
```
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "pstutils"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
authors = [
|
|
5
|
+
{ name="Nikhil Chandra", email="nikhil@plexon.com" },
|
|
6
|
+
{ name="Chris Heydrick", email="chris@plexon.com" },
|
|
7
|
+
]
|
|
8
|
+
description="This package is internal to Plexon. It includes functions to be used in conjunction with the SiNAPS Data Pipeline."
|
|
9
|
+
readme="README.md"
|
|
10
|
+
requires-python=">3.9"
|
|
11
|
+
dependencies = [
|
|
12
|
+
"phylib==2.4.3",
|
|
13
|
+
"numpy==1.26.4",
|
|
14
|
+
"pypl2",
|
|
15
|
+
"pypixelmapdump",
|
|
16
|
+
"debugpy",
|
|
17
|
+
"wheel",
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
[build-system]
|
|
21
|
+
requires = ["setuptools"]
|
|
22
|
+
build-backend = "setuptools.build_meta"
|
|
23
|
+
|
|
24
|
+
[tool.setuptools]
|
|
25
|
+
include-package-data = true
|
|
26
|
+
package-dir = {"" = "src"}
|
|
27
|
+
|
|
28
|
+
[tool.setuptools.package-data]
|
|
29
|
+
"pstutils.maps" = ["*.mat"]
|
|
30
|
+
"*" = ["*.pyd", "pyarmor_runtime_*/*"]
|
|
31
|
+
|
|
32
|
+
[tool.setuptools.packages.find]
|
|
33
|
+
where = ["src"]
|
|
34
|
+
include = ["*"]
|
pstutils-0.1.0/setup.cfg
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from .pstutils import DEBUG
|
|
2
|
+
from .pstutils import extract_wb_data_to_bin
|
|
3
|
+
from .pstutils import extract_pl2_data_to_nex5_stash
|
|
4
|
+
from .pstutils import robocopy
|
|
5
|
+
from .pstutils import archive
|
|
6
|
+
from .pstutils import get_disk_usage
|
|
7
|
+
from .pstutils import build_final_nex5_from_npy_and_pl2
|
|
8
|
+
from .pstutils import build_final_nex5_from_npy_and_stash
|
|
9
|
+
from .pstutils import extract_pl2_headers_to_json
|
|
10
|
+
# from .pstutils import _get_channel_out_of_tune_durations as get_oot_durs
|
|
11
|
+
from .pstutils import get_bad_channels
|
|
12
|
+
from .pstutils import get_probe_names
|
|
13
|
+
from .pstutils import get_unit_id_to_neuron_name_mapping
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Data classes for .nex and .nex5 files.
|
|
3
|
+
"""
|
|
4
|
+
import numpy as np
|
|
5
|
+
from typing import List
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def CalcScaleFloatsToShorts(numbers: 'np.ndarray[np.float32]') -> float:
|
|
9
|
+
"""Calculates coefficient that can be used to convert float values to shorts (16-bit integers).
|
|
10
|
+
|
|
11
|
+
Args:
|
|
12
|
+
numbers (np.ndarray[np.float32]): array of float values
|
|
13
|
+
|
|
14
|
+
Returns:
|
|
15
|
+
float: coefficient such that float_value*coefficient is within 16-bit
|
|
16
|
+
integer range for all values in array.
|
|
17
|
+
"""
|
|
18
|
+
theMax = np.amax(numbers)
|
|
19
|
+
theMin = np.amin(numbers)
|
|
20
|
+
absMax = max(abs(theMin), abs(theMax))
|
|
21
|
+
if absMax == 0.0:
|
|
22
|
+
return 1.0
|
|
23
|
+
else:
|
|
24
|
+
return 32767.0 / absMax
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class Variable:
|
|
28
|
+
"""Base variable class: name only."""
|
|
29
|
+
|
|
30
|
+
def __init__(self, name: str = "") -> None:
|
|
31
|
+
self.Name: str = name
|
|
32
|
+
"""Variable name."""
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class Event(Variable):
|
|
36
|
+
"""Event class: name and array of timestamps in seconds."""
|
|
37
|
+
|
|
38
|
+
def __init__(self, name: str = "", timestamps: List[float] = []) -> None:
|
|
39
|
+
self.Name: str = name
|
|
40
|
+
"""Variable name."""
|
|
41
|
+
|
|
42
|
+
self.Timestamps: 'np.ndarray[np.float64]' = np.asarray(timestamps).astype(np.float64)
|
|
43
|
+
"""Timestamps in seconds."""
|
|
44
|
+
|
|
45
|
+
def MaxTimestamp(self) -> float:
|
|
46
|
+
"""Returns maximum timestamp in seconds."""
|
|
47
|
+
if self.Timestamps is None or len(self.Timestamps) == 0:
|
|
48
|
+
return 0
|
|
49
|
+
return self.Timestamps[-1]
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class Neuron(Event):
|
|
53
|
+
"""
|
|
54
|
+
Neuron class: event data plus wire, unit and position data.
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
def __init__(self, name: str = "", timestamps: List[float] = []) -> None:
|
|
58
|
+
self.Name: str = name
|
|
59
|
+
"""Variable name."""
|
|
60
|
+
|
|
61
|
+
self.Timestamps: 'np.ndarray[np.float64]' = np.asarray(timestamps).astype(np.float64)
|
|
62
|
+
"""Timestamps in seconds."""
|
|
63
|
+
|
|
64
|
+
self.WireNumber: int = 0
|
|
65
|
+
"""Wire (electrode) number."""
|
|
66
|
+
|
|
67
|
+
self.UnitNumber: int = 0
|
|
68
|
+
"""Unit or cluster number."""
|
|
69
|
+
|
|
70
|
+
self.XPos: float = 0
|
|
71
|
+
"""X axis electrode position in (0,100) range, used in 3D display."""
|
|
72
|
+
|
|
73
|
+
self.YPos: float = 0
|
|
74
|
+
"""Y axis electrode position in (0,100) range, used in 3D display."""
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class Marker(Event):
|
|
78
|
+
"""Markers are events with associated strings or integers."""
|
|
79
|
+
|
|
80
|
+
def __init__(self, name: str = "", timestamps: List[float] = [],
|
|
81
|
+
fieldNames: List[str] = [], markerValues: List[List[str]] = []) -> None:
|
|
82
|
+
self.Name: str = name
|
|
83
|
+
"""Variable name."""
|
|
84
|
+
|
|
85
|
+
self.Timestamps: 'np.ndarray[np.float64]' = np.asarray(timestamps).astype(np.float64)
|
|
86
|
+
"""Timestamps in seconds."""
|
|
87
|
+
|
|
88
|
+
self.FieldNames: List[str] = fieldNames
|
|
89
|
+
"""Each timestamp can have several strings or integers associated with it (several fields). These are filed names."""
|
|
90
|
+
|
|
91
|
+
self.MarkerValues: List[List[str]] = markerValues
|
|
92
|
+
"""Each timestamp can have several strings or integers associated with it (several fields).
|
|
93
|
+
Each sublist contains string values for a specific marker field."""
|
|
94
|
+
|
|
95
|
+
self.MarkerValuesAsUnsignedIntegers = [[]]
|
|
96
|
+
"""List of np arrays. Each list element contains numeric values for a specific marker field."""
|
|
97
|
+
|
|
98
|
+
def MaxMarkerLength(self):
|
|
99
|
+
"""Returns maximum length of all string marker values."""
|
|
100
|
+
maxLen = 0
|
|
101
|
+
if len(self.MarkerValues) > 0:
|
|
102
|
+
for fieldValues in self.MarkerValues:
|
|
103
|
+
for m in fieldValues:
|
|
104
|
+
maxLen = max(maxLen, len(m))
|
|
105
|
+
return maxLen
|
|
106
|
+
else:
|
|
107
|
+
for fieldValues in self.MarkerValuesAsUnsignedIntegers:
|
|
108
|
+
for m in fieldValues:
|
|
109
|
+
maxLen = max(maxLen, len(str(m)))
|
|
110
|
+
return maxLen
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
class Interval(Variable):
|
|
114
|
+
"""Interval class. Has two arrays: interval starts and interval ends in seconds."""
|
|
115
|
+
|
|
116
|
+
def __init__(self, name: str = "", starts: List[float] = [], ends: List[float] = []) -> None:
|
|
117
|
+
self.Name: str = name
|
|
118
|
+
"""Variable name."""
|
|
119
|
+
|
|
120
|
+
self.IntervalStarts: 'np.ndarray[np.float64]' = np.asarray(starts).astype(np.float64)
|
|
121
|
+
"""Interval start times in seconds."""
|
|
122
|
+
|
|
123
|
+
self.IntervalEnds: 'np.ndarray[np.float64]' = np.asarray(ends).astype(np.float64)
|
|
124
|
+
"""Interval end times in seconds."""
|
|
125
|
+
|
|
126
|
+
def MaxTimestamp(self) -> float:
|
|
127
|
+
"""Returns maximum timestamp in seconds."""
|
|
128
|
+
if self.IntervalEnds is None or len(self.IntervalEnds) == 0:
|
|
129
|
+
return 0
|
|
130
|
+
return self.IntervalEnds[-1]
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
class Continuous(Variable):
|
|
134
|
+
""" Continuous class.
|
|
135
|
+
Contains: (1) Sampling rate in Hz. (2) Array of timestamps in seconds (each timestamp is for the beginning of the fragment).
|
|
136
|
+
(3) Array of indexes (each index is the position of the first data point of the fragment in the a/d array).
|
|
137
|
+
(4) Array of all a/d values in milliVolts.
|
|
138
|
+
"""
|
|
139
|
+
|
|
140
|
+
def __init__(self, name: str = "", samplingRate: float = 0, fragmentStarts: List[float] = [],
|
|
141
|
+
fragmentStartIndexes: List[int] = [],
|
|
142
|
+
values: List[float] = []) -> None:
|
|
143
|
+
self.Name: str = name
|
|
144
|
+
"""Variable name."""
|
|
145
|
+
|
|
146
|
+
self.SamplingRate: float = samplingRate
|
|
147
|
+
"""Sampling rate in Hz."""
|
|
148
|
+
|
|
149
|
+
self.FragmentTimestamps: 'np.ndarray[np.float64]' = np.asarray(fragmentStarts).astype(np.float64)
|
|
150
|
+
"""Array of timestamps in seconds (each timestamp is for the beginning of the fragment)."""
|
|
151
|
+
|
|
152
|
+
self.FragmentStartIndexes: 'np.ndarray[np.uint32]' = np.asarray(fragmentStartIndexes).astype(np.uint32)
|
|
153
|
+
"""Array of indexes (each index is the position of the first data point of the fragment in the a/d array)."""
|
|
154
|
+
|
|
155
|
+
self.Values: 'np.ndarray[np.float32]' = np.asarray(values).astype(np.float32)
|
|
156
|
+
"""Array of all a/d values in milliVolts."""
|
|
157
|
+
|
|
158
|
+
self.CalculatedScaleFloatsToShorts = 1
|
|
159
|
+
"""When saving data to .nex file, this field contains coefficient to convert floats to shorts."""
|
|
160
|
+
|
|
161
|
+
def MaxTimestamp(self) -> float:
|
|
162
|
+
"""Returns maximum timestamp in seconds (the last timestamp of all continuous values)."""
|
|
163
|
+
if self.FragmentTimestamps is None or len(self.FragmentTimestamps) == 0 or self.SamplingRate <= 0:
|
|
164
|
+
return 0
|
|
165
|
+
if self.FragmentStartIndexes is None or len(self.FragmentStartIndexes) == 0 or self.Values is None or len(self.Values) == 0:
|
|
166
|
+
return 0
|
|
167
|
+
step = 1.0/self.SamplingRate
|
|
168
|
+
return self.FragmentTimestamps[-1] + step * (len(self.Values) - self.FragmentStartIndexes[-1] - 1.0)
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
class Waveform(Event):
|
|
172
|
+
""" Waveform class.
|
|
173
|
+
Contains: Sampling rate in Hz
|
|
174
|
+
Number of points in each wave
|
|
175
|
+
Array of timestamps in seconds (each timestamp is for the beginning of the waveform).
|
|
176
|
+
Array of all waveform a/d values in milliVolts.
|
|
177
|
+
"""
|
|
178
|
+
|
|
179
|
+
def __init__(self, name: str = "", samplingRate: float = 0, timestamps: List[float] = [],
|
|
180
|
+
numPointsWave: int = 0, values: List[float] = []):
|
|
181
|
+
self.Name: str = name
|
|
182
|
+
"""Variable name."""
|
|
183
|
+
|
|
184
|
+
self.SamplingRate: float = samplingRate
|
|
185
|
+
"""Sampling rate in Hz."""
|
|
186
|
+
|
|
187
|
+
self.Timestamps: 'np.ndarray[np.float64]' = np.asarray(timestamps).astype(np.float64)
|
|
188
|
+
"""Timestamps in seconds."""
|
|
189
|
+
|
|
190
|
+
self.WireNumber: int = 0
|
|
191
|
+
"""Wire (electrode) number."""
|
|
192
|
+
|
|
193
|
+
self.UnitNumber: int = 0
|
|
194
|
+
"""Unit or cluster number."""
|
|
195
|
+
|
|
196
|
+
self.NumPointsWave: int = numPointsWave
|
|
197
|
+
"""Number of data point in each wave."""
|
|
198
|
+
|
|
199
|
+
self.Values: 'np.ndarray[np.float32]' = np.asarray(values).astype(np.float32)
|
|
200
|
+
"""Waveform values in milliVolts."""
|
|
201
|
+
|
|
202
|
+
self.Values = self.Values.reshape((len(self.Timestamps), self.NumPointsWave))
|
|
203
|
+
|
|
204
|
+
self.CalculatedScaleFloatsToShorts = 1
|
|
205
|
+
"""When saving data to .nex file, this field contains coefficient to convert floats to shorts."""
|
|
206
|
+
|
|
207
|
+
def MaxTimestamp(self) -> float:
|
|
208
|
+
"""Returns maximum timestamp in seconds (the last timestamp of the last waveform)."""
|
|
209
|
+
if self.Timestamps is None or len(self.Timestamps) == 0 or self.SamplingRate <= 0 or self.NumPointsWave == 0:
|
|
210
|
+
return 0
|
|
211
|
+
step = 1.0/self.SamplingRate
|
|
212
|
+
return self.Timestamps[-1] + step * (self.NumPointsWave - 1.0)
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
class FileData:
|
|
216
|
+
"""
|
|
217
|
+
FileData object. Contains arrays of data variables.
|
|
218
|
+
"""
|
|
219
|
+
|
|
220
|
+
def __init__(self) -> None:
|
|
221
|
+
self.Comment: str = ""
|
|
222
|
+
"""File comment."""
|
|
223
|
+
|
|
224
|
+
self.TimestampFrequency: float = 0
|
|
225
|
+
"""Timestamps frequency, in Hertz. Timestamp values in .net and .nex5 files are stored in ticks, where tick = 1/TimestampFrequency."""
|
|
226
|
+
|
|
227
|
+
self.StartTimeSeconds: float = 0
|
|
228
|
+
"""Start time in seconds."""
|
|
229
|
+
|
|
230
|
+
self.EndTimeSeconds: float = 0
|
|
231
|
+
"""End time in seconds."""
|
|
232
|
+
|
|
233
|
+
self.Events: List[Event] = []
|
|
234
|
+
"""List of Events."""
|
|
235
|
+
|
|
236
|
+
self.Neurons: List[Neuron] = []
|
|
237
|
+
"""List of Neurons."""
|
|
238
|
+
|
|
239
|
+
self.Intervals: List[Interval] = []
|
|
240
|
+
"""List of intervals."""
|
|
241
|
+
|
|
242
|
+
self.Markers: List[Marker] = []
|
|
243
|
+
"""List of Markers."""
|
|
244
|
+
|
|
245
|
+
self.Continuous: List[Continuous] = []
|
|
246
|
+
"""List of Continuous variables."""
|
|
247
|
+
|
|
248
|
+
self.Waveforms: List[Waveform] = []
|
|
249
|
+
"""List of Waveform variables."""
|
|
250
|
+
|
|
251
|
+
def NumberOfVariables(self) -> int:
|
|
252
|
+
"""Returns number of all variables"""
|
|
253
|
+
return len(self.Neurons) + len(self.Events) + len(self.Intervals) + len(self.Markers) + len(self.Continuous) + len(self.Waveforms)
|
|
254
|
+
|
|
255
|
+
def MaxTimestamp(self) -> float:
|
|
256
|
+
"""Returns maximum timestamp of all variables in seconds."""
|
|
257
|
+
maxTs = 0
|
|
258
|
+
for v in self.Neurons:
|
|
259
|
+
maxTs = max(maxTs, v.MaxTimestamp())
|
|
260
|
+
for v in self.Events:
|
|
261
|
+
maxTs = max(maxTs, v.MaxTimestamp())
|
|
262
|
+
for v in self.Intervals:
|
|
263
|
+
maxTs = max(maxTs, v.MaxTimestamp())
|
|
264
|
+
for v in self.Markers:
|
|
265
|
+
maxTs = max(maxTs, v.MaxTimestamp())
|
|
266
|
+
for v in self.Continuous:
|
|
267
|
+
maxTs = max(maxTs, v.MaxTimestamp())
|
|
268
|
+
for v in self.Waveforms:
|
|
269
|
+
maxTs = max(maxTs, v.MaxTimestamp())
|
|
270
|
+
return maxTs
|
|
271
|
+
|
|
272
|
+
def TicksToSeconds(self, ticks: int) -> float:
|
|
273
|
+
"""Converts time in thicks to time in seconds."""
|
|
274
|
+
return ticks/self.TimestampFrequency
|
|
275
|
+
|
|
276
|
+
def SecondsToTicks(self, seconds: float) -> int:
|
|
277
|
+
"""Converts time in seconds to time in ticks."""
|
|
278
|
+
return round(seconds * self.TimestampFrequency)
|