neurosharectypesio.py 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444
  1. # -*- coding: utf-8 -*-
  2. """
  3. NeuroshareIO is a wrap with ctypes of neuroshare DLLs.
  4. Neuroshare is a C API for reading neural data.
  5. Neuroshare also provides a Matlab and a Python API on top of that.
  6. Neuroshare is an open source API but each dll is provided directly by the vendor.
  7. The neo user have to download separtatly the dll on neurosharewebsite:
  8. http://neuroshare.sourceforge.net/
  9. For some vendors (Spike2/CED , Clampfit/Abf, ...), neo.io also provides pure Python
  10. Neo users you should prefer them of course :)
  11. Supported : Read
  12. Author: sgarcia
  13. """
  14. import sys
  15. import ctypes
  16. import os
  17. # file no longer exists in Python3
  18. try:
  19. file
  20. except NameError:
  21. import io
  22. file = io.BufferedReader
  23. import numpy as np
  24. import quantities as pq
  25. from neo.io.baseio import BaseIO
  26. from neo.core import Segment, AnalogSignal, SpikeTrain, Event
  27. ns_OK = 0 # Function successful
  28. ns_LIBERROR = -1 # Generic linked library error
  29. ns_TYPEERROR = -2 # Library unable to open file type
  30. ns_FILEERROR = -3 # File access or read error
  31. ns_BADFILE = -4 # Invalid file handle passed to function
  32. ns_BADENTITY = -5 # Invalid or inappropriate entity identifier specified
  33. ns_BADSOURCE = -6 # Invalid source identifier specified
  34. ns_BADINDEX = -7 # Invalid entity index specified
  35. class NeuroshareError(Exception):
  36. def __init__(self, lib, errno):
  37. self.lib = lib
  38. self.errno = errno
  39. pszMsgBuffer = ctypes.create_string_buffer(256)
  40. self.lib.ns_GetLastErrorMsg(pszMsgBuffer, ctypes.c_uint32(256))
  41. errstr = '{}: {}'.format(errno, pszMsgBuffer.value)
  42. Exception.__init__(self, errstr)
  43. class DllWithError():
  44. def __init__(self, lib):
  45. self.lib = lib
  46. def __getattr__(self, attr):
  47. f = getattr(self.lib, attr)
  48. return self.decorate_with_error(f)
  49. def decorate_with_error(self, f):
  50. def func_with_error(*args):
  51. errno = f(*args)
  52. if errno != ns_OK:
  53. raise NeuroshareError(self.lib, errno)
  54. return errno
  55. return func_with_error
  56. class NeurosharectypesIO(BaseIO):
  57. """
  58. Class for reading file trougth neuroshare API.
  59. The user need the DLLs in the path of the file format.
  60. Usage:
  61. >>> from neo import io
  62. >>> r = io.NeuroshareIO(filename='a_file', dllname=the_name_of_dll)
  63. >>> seg = r.read_segment(import_neuroshare_segment=True)
  64. >>> print seg.analogsignals # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE
  65. [<AnalogSignal(array([ -1.77246094e+02, -2.24707031e+02, -2.66015625e+02,
  66. ...
  67. >>> print seg.spiketrains
  68. []
  69. >>> print seg.events
  70. [<EventArray: 1@1.12890625 s, 1@2.02734375 s, 1@3.82421875 s>]
  71. Note:
  72. neuroshare.ns_ENTITY_EVENT: are converted to neo.EventArray
  73. neuroshare.ns_ENTITY_ANALOG: are converted to neo.AnalogSignal
  74. neuroshare.ns_ENTITY_NEURALEVENT: are converted to neo.SpikeTrain
  75. neuroshare.ns_ENTITY_SEGMENT: is something between serie of small AnalogSignal
  76. and Spiketrain with associated waveforms.
  77. It is arbitrarily converted as SpikeTrain.
  78. """
  79. is_readable = True
  80. is_writable = False
  81. supported_objects = [Segment, AnalogSignal, Event, SpikeTrain]
  82. readable_objects = [Segment]
  83. writeable_objects = []
  84. has_header = False
  85. is_streameable = False
  86. read_params = {Segment: []}
  87. write_params = None
  88. name = 'neuroshare'
  89. extensions = []
  90. mode = 'file'
  91. def __init__(self, filename='', dllname=''):
  92. """
  93. Arguments:
  94. filename: the file to read
  95. ddlname: the name of neuroshare dll to be used for this file
  96. """
  97. BaseIO.__init__(self)
  98. self.dllname = dllname
  99. self.filename = filename
  100. def read_segment(self, import_neuroshare_segment=True,
  101. lazy=False):
  102. """
  103. Arguments:
  104. import_neuroshare_segment: import neuroshare segment as SpikeTrain
  105. with associated waveforms or not imported at all.
  106. """
  107. assert not lazy, 'Do not support lazy'
  108. seg = Segment(file_origin=os.path.basename(self.filename), )
  109. if sys.platform.startswith('win'):
  110. neuroshare = ctypes.windll.LoadLibrary(self.dllname)
  111. elif sys.platform.startswith('linux'):
  112. neuroshare = ctypes.cdll.LoadLibrary(self.dllname)
  113. neuroshare = DllWithError(neuroshare)
  114. # elif sys.platform.startswith('darwin'):
  115. # API version
  116. info = ns_LIBRARYINFO()
  117. neuroshare.ns_GetLibraryInfo(ctypes.byref(info), ctypes.sizeof(info))
  118. seg.annotate(neuroshare_version=str(info.dwAPIVersionMaj)
  119. + '.' + str(info.dwAPIVersionMin))
  120. # open file
  121. hFile = ctypes.c_uint32(0)
  122. neuroshare.ns_OpenFile(ctypes.c_char_p(self.filename), ctypes.byref(hFile))
  123. fileinfo = ns_FILEINFO()
  124. neuroshare.ns_GetFileInfo(hFile, ctypes.byref(fileinfo), ctypes.sizeof(fileinfo))
  125. # read all entities
  126. for dwEntityID in range(fileinfo.dwEntityCount):
  127. entityInfo = ns_ENTITYINFO()
  128. neuroshare.ns_GetEntityInfo(hFile, dwEntityID, ctypes.byref(
  129. entityInfo), ctypes.sizeof(entityInfo))
  130. # EVENT
  131. if entity_types[entityInfo.dwEntityType] == 'ns_ENTITY_EVENT':
  132. pEventInfo = ns_EVENTINFO()
  133. neuroshare.ns_GetEventInfo(hFile, dwEntityID, ctypes.byref(
  134. pEventInfo), ctypes.sizeof(pEventInfo))
  135. if pEventInfo.dwEventType == 0: # TEXT
  136. pData = ctypes.create_string_buffer(pEventInfo.dwMaxDataLength)
  137. elif pEventInfo.dwEventType == 1: # CVS
  138. pData = ctypes.create_string_buffer(pEventInfo.dwMaxDataLength)
  139. elif pEventInfo.dwEventType == 2: # 8bit
  140. pData = ctypes.c_byte(0)
  141. elif pEventInfo.dwEventType == 3: # 16bit
  142. pData = ctypes.c_int16(0)
  143. elif pEventInfo.dwEventType == 4: # 32bit
  144. pData = ctypes.c_int32(0)
  145. pdTimeStamp = ctypes.c_double(0.)
  146. pdwDataRetSize = ctypes.c_uint32(0)
  147. ea = Event(name=str(entityInfo.szEntityLabel), )
  148. times = []
  149. labels = []
  150. for dwIndex in range(entityInfo.dwItemCount):
  151. neuroshare.ns_GetEventData(hFile, dwEntityID, dwIndex,
  152. ctypes.byref(pdTimeStamp), ctypes.byref(pData),
  153. ctypes.sizeof(pData), ctypes.byref(pdwDataRetSize))
  154. times.append(pdTimeStamp.value)
  155. labels.append(str(pData.value))
  156. ea.times = times * pq.s
  157. ea.labels = np.array(labels, dtype='S')
  158. seg.events.append(ea)
  159. # analog
  160. if entity_types[entityInfo.dwEntityType] == 'ns_ENTITY_ANALOG':
  161. pAnalogInfo = ns_ANALOGINFO()
  162. neuroshare.ns_GetAnalogInfo(hFile, dwEntityID, ctypes.byref(
  163. pAnalogInfo), ctypes.sizeof(pAnalogInfo))
  164. dwIndexCount = entityInfo.dwItemCount
  165. pdwContCount = ctypes.c_uint32(0)
  166. pData = np.zeros((entityInfo.dwItemCount,), dtype='float64')
  167. total_read = 0
  168. while total_read < entityInfo.dwItemCount:
  169. dwStartIndex = ctypes.c_uint32(total_read)
  170. dwStopIndex = ctypes.c_uint32(entityInfo.dwItemCount - total_read)
  171. neuroshare.ns_GetAnalogData(hFile, dwEntityID, dwStartIndex,
  172. dwStopIndex, ctypes.byref(pdwContCount),
  173. pData[total_read:].ctypes.data_as(
  174. ctypes.POINTER(ctypes.c_double)))
  175. total_read += pdwContCount.value
  176. signal = pq.Quantity(pData, units=pAnalogInfo.szUnits, copy=False)
  177. # t_start
  178. dwIndex = 0
  179. pdTime = ctypes.c_double(0)
  180. neuroshare.ns_GetTimeByIndex(hFile, dwEntityID, dwIndex, ctypes.byref(pdTime))
  181. anaSig = AnalogSignal(signal,
  182. sampling_rate=pAnalogInfo.dSampleRate * pq.Hz,
  183. t_start=pdTime.value * pq.s,
  184. name=str(entityInfo.szEntityLabel),
  185. )
  186. anaSig.annotate(probe_info=str(pAnalogInfo.szProbeInfo))
  187. seg.analogsignals.append(anaSig)
  188. # segment
  189. if entity_types[
  190. entityInfo.dwEntityType] == 'ns_ENTITY_SEGMENT' and import_neuroshare_segment:
  191. pdwSegmentInfo = ns_SEGMENTINFO()
  192. if not str(entityInfo.szEntityLabel).startswith('spks'):
  193. continue
  194. neuroshare.ns_GetSegmentInfo(hFile, dwEntityID,
  195. ctypes.byref(pdwSegmentInfo),
  196. ctypes.sizeof(pdwSegmentInfo))
  197. nsource = pdwSegmentInfo.dwSourceCount
  198. pszMsgBuffer = ctypes.create_string_buffer(" " * 256)
  199. neuroshare.ns_GetLastErrorMsg(ctypes.byref(pszMsgBuffer), 256)
  200. for dwSourceID in range(pdwSegmentInfo.dwSourceCount):
  201. pSourceInfo = ns_SEGSOURCEINFO()
  202. neuroshare.ns_GetSegmentSourceInfo(hFile, dwEntityID, dwSourceID,
  203. ctypes.byref(pSourceInfo),
  204. ctypes.sizeof(pSourceInfo))
  205. pdTimeStamp = ctypes.c_double(0.)
  206. dwDataBufferSize = pdwSegmentInfo.dwMaxSampleCount * pdwSegmentInfo.dwSourceCount
  207. pData = np.zeros((dwDataBufferSize), dtype='float64')
  208. pdwSampleCount = ctypes.c_uint32(0)
  209. pdwUnitID = ctypes.c_uint32(0)
  210. nsample = int(dwDataBufferSize)
  211. times = np.empty((entityInfo.dwItemCount), dtype='f')
  212. waveforms = np.empty((entityInfo.dwItemCount, nsource, nsample), dtype='f')
  213. for dwIndex in range(entityInfo.dwItemCount):
  214. neuroshare.ns_GetSegmentData(
  215. hFile, dwEntityID, dwIndex,
  216. ctypes.byref(pdTimeStamp),
  217. pData.ctypes.data_as(ctypes.POINTER(ctypes.c_double)),
  218. dwDataBufferSize * 8,
  219. ctypes.byref(pdwSampleCount),
  220. ctypes.byref(pdwUnitID))
  221. times[dwIndex] = pdTimeStamp.value
  222. waveforms[dwIndex, :, :] = pData[:nsample * nsource].reshape(
  223. nsample, nsource).transpose()
  224. sptr = SpikeTrain(times=pq.Quantity(times, units='s', copy=False),
  225. t_stop=times.max(),
  226. waveforms=pq.Quantity(waveforms, units=str(
  227. pdwSegmentInfo.szUnits), copy=False),
  228. left_sweep=nsample / 2.
  229. / float(pdwSegmentInfo.dSampleRate) * pq.s,
  230. sampling_rate=float(pdwSegmentInfo.dSampleRate) * pq.Hz,
  231. name=str(entityInfo.szEntityLabel),
  232. )
  233. seg.spiketrains.append(sptr)
  234. # neuralevent
  235. if entity_types[entityInfo.dwEntityType] == 'ns_ENTITY_NEURALEVENT':
  236. pNeuralInfo = ns_NEURALINFO()
  237. neuroshare.ns_GetNeuralInfo(hFile, dwEntityID,
  238. ctypes.byref(pNeuralInfo), ctypes.sizeof(pNeuralInfo))
  239. pData = np.zeros((entityInfo.dwItemCount,), dtype='float64')
  240. dwStartIndex = 0
  241. dwIndexCount = entityInfo.dwItemCount
  242. neuroshare.ns_GetNeuralData(hFile, dwEntityID, dwStartIndex,
  243. dwIndexCount,
  244. pData.ctypes.data_as(ctypes.POINTER(ctypes.c_double)))
  245. times = pData * pq.s
  246. t_stop = times.max()
  247. sptr = SpikeTrain(times, t_stop=t_stop,
  248. name=str(entityInfo.szEntityLabel), )
  249. seg.spiketrains.append(sptr)
  250. # close
  251. neuroshare.ns_CloseFile(hFile)
  252. seg.create_many_to_one_relationship()
  253. return seg
  254. # neuroshare structures
  255. class ns_FILEDESC(ctypes.Structure):
  256. _fields_ = [('szDescription', ctypes.c_char * 32),
  257. ('szExtension', ctypes.c_char * 8),
  258. ('szMacCodes', ctypes.c_char * 8),
  259. ('szMagicCode', ctypes.c_char * 16),
  260. ]
  261. class ns_LIBRARYINFO(ctypes.Structure):
  262. _fields_ = [('dwLibVersionMaj', ctypes.c_uint32),
  263. ('dwLibVersionMin', ctypes.c_uint32),
  264. ('dwAPIVersionMaj', ctypes.c_uint32),
  265. ('dwAPIVersionMin', ctypes.c_uint32),
  266. ('szDescription', ctypes.c_char * 64),
  267. ('szCreator', ctypes.c_char * 64),
  268. ('dwTime_Year', ctypes.c_uint32),
  269. ('dwTime_Month', ctypes.c_uint32),
  270. ('dwTime_Day', ctypes.c_uint32),
  271. ('dwFlags', ctypes.c_uint32),
  272. ('dwMaxFiles', ctypes.c_uint32),
  273. ('dwFileDescCount', ctypes.c_uint32),
  274. ('FileDesc', ns_FILEDESC * 16),
  275. ]
  276. class ns_FILEINFO(ctypes.Structure):
  277. _fields_ = [('szFileType', ctypes.c_char * 32),
  278. ('dwEntityCount', ctypes.c_uint32),
  279. ('dTimeStampResolution', ctypes.c_double),
  280. ('dTimeSpan', ctypes.c_double),
  281. ('szAppName', ctypes.c_char * 64),
  282. ('dwTime_Year', ctypes.c_uint32),
  283. ('dwTime_Month', ctypes.c_uint32),
  284. ('dwReserved', ctypes.c_uint32),
  285. ('dwTime_Day', ctypes.c_uint32),
  286. ('dwTime_Hour', ctypes.c_uint32),
  287. ('dwTime_Min', ctypes.c_uint32),
  288. ('dwTime_Sec', ctypes.c_uint32),
  289. ('dwTime_MilliSec', ctypes.c_uint32),
  290. ('szFileComment', ctypes.c_char * 256),
  291. ]
  292. class ns_ENTITYINFO(ctypes.Structure):
  293. _fields_ = [('szEntityLabel', ctypes.c_char * 32),
  294. ('dwEntityType', ctypes.c_uint32),
  295. ('dwItemCount', ctypes.c_uint32),
  296. ]
  297. entity_types = {0: 'ns_ENTITY_UNKNOWN',
  298. 1: 'ns_ENTITY_EVENT',
  299. 2: 'ns_ENTITY_ANALOG',
  300. 3: 'ns_ENTITY_SEGMENT',
  301. 4: 'ns_ENTITY_NEURALEVENT',
  302. }
  303. class ns_EVENTINFO(ctypes.Structure):
  304. _fields_ = [
  305. ('dwEventType', ctypes.c_uint32),
  306. ('dwMinDataLength', ctypes.c_uint32),
  307. ('dwMaxDataLength', ctypes.c_uint32),
  308. ('szCSVDesc', ctypes.c_char * 128),
  309. ]
  310. class ns_ANALOGINFO(ctypes.Structure):
  311. _fields_ = [
  312. ('dSampleRate', ctypes.c_double),
  313. ('dMinVal', ctypes.c_double),
  314. ('dMaxVal', ctypes.c_double),
  315. ('szUnits', ctypes.c_char * 16),
  316. ('dResolution', ctypes.c_double),
  317. ('dLocationX', ctypes.c_double),
  318. ('dLocationY', ctypes.c_double),
  319. ('dLocationZ', ctypes.c_double),
  320. ('dLocationUser', ctypes.c_double),
  321. ('dHighFreqCorner', ctypes.c_double),
  322. ('dwHighFreqOrder', ctypes.c_uint32),
  323. ('szHighFilterType', ctypes.c_char * 16),
  324. ('dLowFreqCorner', ctypes.c_double),
  325. ('dwLowFreqOrder', ctypes.c_uint32),
  326. ('szLowFilterType', ctypes.c_char * 16),
  327. ('szProbeInfo', ctypes.c_char * 128),
  328. ]
  329. class ns_SEGMENTINFO(ctypes.Structure):
  330. _fields_ = [
  331. ('dwSourceCount', ctypes.c_uint32),
  332. ('dwMinSampleCount', ctypes.c_uint32),
  333. ('dwMaxSampleCount', ctypes.c_uint32),
  334. ('dSampleRate', ctypes.c_double),
  335. ('szUnits', ctypes.c_char * 32),
  336. ]
  337. class ns_SEGSOURCEINFO(ctypes.Structure):
  338. _fields_ = [
  339. ('dMinVal', ctypes.c_double),
  340. ('dMaxVal', ctypes.c_double),
  341. ('dResolution', ctypes.c_double),
  342. ('dSubSampleShift', ctypes.c_double),
  343. ('dLocationX', ctypes.c_double),
  344. ('dLocationY', ctypes.c_double),
  345. ('dLocationZ', ctypes.c_double),
  346. ('dLocationUser', ctypes.c_double),
  347. ('dHighFreqCorner', ctypes.c_double),
  348. ('dwHighFreqOrder', ctypes.c_uint32),
  349. ('szHighFilterType', ctypes.c_char * 16),
  350. ('dLowFreqCorner', ctypes.c_double),
  351. ('dwLowFreqOrder', ctypes.c_uint32),
  352. ('szLowFilterType', ctypes.c_char * 16),
  353. ('szProbeInfo', ctypes.c_char * 128),
  354. ]
  355. class ns_NEURALINFO(ctypes.Structure):
  356. _fields_ = [
  357. ('dwSourceEntityID', ctypes.c_uint32),
  358. ('dwSourceUnitID', ctypes.c_uint32),
  359. ('szProbeInfo', ctypes.c_char * 128),
  360. ]