neurosharectypesio.py 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450
  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(lazy=False, cascade=True, 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.eventarrays
  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, cascade=True):
  102. """
  103. Arguments:
  104. import_neuroshare_segment: import neuroshare segment as SpikeTrain with associated waveforms or not imported at all.
  105. """
  106. seg = Segment( file_origin = os.path.basename(self.filename), )
  107. if sys.platform.startswith('win'):
  108. neuroshare = ctypes.windll.LoadLibrary(self.dllname)
  109. elif sys.platform.startswith('linux'):
  110. neuroshare = ctypes.cdll.LoadLibrary(self.dllname)
  111. neuroshare = DllWithError(neuroshare)
  112. #elif sys.platform.startswith('darwin'):
  113. # API version
  114. info = ns_LIBRARYINFO()
  115. neuroshare.ns_GetLibraryInfo(ctypes.byref(info) , ctypes.sizeof(info))
  116. seg.annotate(neuroshare_version = str(info.dwAPIVersionMaj)+'.'+str(info.dwAPIVersionMin))
  117. if not cascade:
  118. return seg
  119. # open file
  120. hFile = ctypes.c_uint32(0)
  121. neuroshare.ns_OpenFile(ctypes.c_char_p(self.filename) ,ctypes.byref(hFile))
  122. fileinfo = ns_FILEINFO()
  123. neuroshare.ns_GetFileInfo(hFile, ctypes.byref(fileinfo) , ctypes.sizeof(fileinfo))
  124. # read all entities
  125. for dwEntityID in range(fileinfo.dwEntityCount):
  126. entityInfo = ns_ENTITYINFO()
  127. neuroshare.ns_GetEntityInfo( hFile, dwEntityID, ctypes.byref(entityInfo), ctypes.sizeof(entityInfo))
  128. # EVENT
  129. if entity_types[entityInfo.dwEntityType] == 'ns_ENTITY_EVENT':
  130. pEventInfo = ns_EVENTINFO()
  131. neuroshare.ns_GetEventInfo ( hFile, dwEntityID, ctypes.byref(pEventInfo), ctypes.sizeof(pEventInfo))
  132. if pEventInfo.dwEventType == 0: #TEXT
  133. pData = ctypes.create_string_buffer(pEventInfo.dwMaxDataLength)
  134. elif pEventInfo.dwEventType == 1:#CVS
  135. pData = ctypes.create_string_buffer(pEventInfo.dwMaxDataLength)
  136. elif pEventInfo.dwEventType == 2:# 8bit
  137. pData = ctypes.c_byte(0)
  138. elif pEventInfo.dwEventType == 3:# 16bit
  139. pData = ctypes.c_int16(0)
  140. elif pEventInfo.dwEventType == 4:# 32bit
  141. pData = ctypes.c_int32(0)
  142. pdTimeStamp = ctypes.c_double(0.)
  143. pdwDataRetSize = ctypes.c_uint32(0)
  144. ea = Event(name = str(entityInfo.szEntityLabel),)
  145. if not lazy:
  146. times = [ ]
  147. labels = [ ]
  148. for dwIndex in range(entityInfo.dwItemCount ):
  149. neuroshare.ns_GetEventData ( hFile, dwEntityID, dwIndex,
  150. ctypes.byref(pdTimeStamp), ctypes.byref(pData),
  151. ctypes.sizeof(pData), ctypes.byref(pdwDataRetSize) )
  152. times.append(pdTimeStamp.value)
  153. labels.append(str(pData.value))
  154. ea.times = times*pq.s
  155. ea.labels = np.array(labels, dtype ='S')
  156. else :
  157. ea.lazy_shape = entityInfo.dwItemCount
  158. seg.eventarrays.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(pAnalogInfo),ctypes.sizeof(pAnalogInfo) )
  163. dwIndexCount = entityInfo.dwItemCount
  164. if lazy:
  165. signal = [ ]*pq.Quantity(1, pAnalogInfo.szUnits)
  166. else:
  167. pdwContCount = ctypes.c_uint32(0)
  168. pData = np.zeros( (entityInfo.dwItemCount,), dtype = 'float64')
  169. total_read = 0
  170. while total_read< entityInfo.dwItemCount:
  171. dwStartIndex = ctypes.c_uint32(total_read)
  172. dwStopIndex = ctypes.c_uint32(entityInfo.dwItemCount - total_read)
  173. neuroshare.ns_GetAnalogData( hFile, dwEntityID, dwStartIndex,
  174. dwStopIndex, ctypes.byref( pdwContCount) , pData[total_read:].ctypes.data_as(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. if lazy:
  188. anaSig.lazy_shape = entityInfo.dwItemCount
  189. seg.analogsignals.append( anaSig )
  190. #segment
  191. if entity_types[entityInfo.dwEntityType] == 'ns_ENTITY_SEGMENT' and import_neuroshare_segment:
  192. pdwSegmentInfo = ns_SEGMENTINFO()
  193. if not str(entityInfo.szEntityLabel).startswith('spks'):
  194. continue
  195. neuroshare.ns_GetSegmentInfo( hFile, dwEntityID,
  196. ctypes.byref(pdwSegmentInfo), 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), ctypes.sizeof(pSourceInfo) )
  204. if lazy:
  205. sptr = SpikeTrain(times, name = str(entityInfo.szEntityLabel), t_stop = 0.*pq.s)
  206. sptr.lazy_shape = entityInfo.dwItemCount
  207. else:
  208. pdTimeStamp = ctypes.c_double(0.)
  209. dwDataBufferSize = pdwSegmentInfo.dwMaxSampleCount*pdwSegmentInfo.dwSourceCount
  210. pData = np.zeros( (dwDataBufferSize), dtype = 'float64')
  211. pdwSampleCount = ctypes.c_uint32(0)
  212. pdwUnitID= ctypes.c_uint32(0)
  213. nsample = int(dwDataBufferSize)
  214. times = np.empty( (entityInfo.dwItemCount), dtype = 'f')
  215. waveforms = np.empty( (entityInfo.dwItemCount, nsource, nsample), dtype = 'f')
  216. for dwIndex in range(entityInfo.dwItemCount ):
  217. neuroshare.ns_GetSegmentData ( hFile, dwEntityID, dwIndex,
  218. ctypes.byref(pdTimeStamp), pData.ctypes.data_as(ctypes.POINTER(ctypes.c_double)),
  219. dwDataBufferSize * 8, ctypes.byref(pdwSampleCount),
  220. ctypes.byref(pdwUnitID ) )
  221. times[dwIndex] = pdTimeStamp.value
  222. waveforms[dwIndex, :,:] = pData[:nsample*nsource].reshape(nsample ,nsource).transpose()
  223. sptr = SpikeTrain(times = pq.Quantity(times, units = 's', copy = False),
  224. t_stop = times.max(),
  225. waveforms = pq.Quantity(waveforms, units = str(pdwSegmentInfo.szUnits), copy = False ),
  226. left_sweep = nsample/2./float(pdwSegmentInfo.dSampleRate)*pq.s,
  227. sampling_rate = float(pdwSegmentInfo.dSampleRate)*pq.Hz,
  228. name = str(entityInfo.szEntityLabel),
  229. )
  230. seg.spiketrains.append(sptr)
  231. # neuralevent
  232. if entity_types[entityInfo.dwEntityType] == 'ns_ENTITY_NEURALEVENT':
  233. pNeuralInfo = ns_NEURALINFO()
  234. neuroshare.ns_GetNeuralInfo ( hFile, dwEntityID,
  235. ctypes.byref(pNeuralInfo), ctypes.sizeof(pNeuralInfo))
  236. if lazy:
  237. times = [ ]*pq.s
  238. t_stop = 0*pq.s
  239. else:
  240. pData = np.zeros( (entityInfo.dwItemCount,), dtype = 'float64')
  241. dwStartIndex = 0
  242. dwIndexCount = entityInfo.dwItemCount
  243. neuroshare.ns_GetNeuralData( hFile, dwEntityID, dwStartIndex,
  244. dwIndexCount, 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. if lazy:
  250. sptr.lazy_shape = entityInfo.dwItemCount
  251. seg.spiketrains.append(sptr)
  252. # close
  253. neuroshare.ns_CloseFile(hFile)
  254. seg.create_many_to_one_relationship()
  255. return seg
  256. # neuroshare structures
  257. class ns_FILEDESC(ctypes.Structure):
  258. _fields_ = [('szDescription', ctypes.c_char*32),
  259. ('szExtension', ctypes.c_char*8),
  260. ('szMacCodes', ctypes.c_char*8),
  261. ('szMagicCode', ctypes.c_char*16),
  262. ]
  263. class ns_LIBRARYINFO(ctypes.Structure):
  264. _fields_ = [('dwLibVersionMaj', ctypes.c_uint32),
  265. ('dwLibVersionMin', ctypes.c_uint32),
  266. ('dwAPIVersionMaj', ctypes.c_uint32),
  267. ('dwAPIVersionMin', ctypes.c_uint32),
  268. ('szDescription', ctypes.c_char*64),
  269. ('szCreator',ctypes.c_char*64),
  270. ('dwTime_Year',ctypes.c_uint32),
  271. ('dwTime_Month',ctypes.c_uint32),
  272. ('dwTime_Day',ctypes.c_uint32),
  273. ('dwFlags',ctypes.c_uint32),
  274. ('dwMaxFiles',ctypes.c_uint32),
  275. ('dwFileDescCount',ctypes.c_uint32),
  276. ('FileDesc',ns_FILEDESC*16),
  277. ]
  278. class ns_FILEINFO(ctypes.Structure):
  279. _fields_ = [('szFileType', ctypes.c_char*32),
  280. ('dwEntityCount', ctypes.c_uint32),
  281. ('dTimeStampResolution', ctypes.c_double),
  282. ('dTimeSpan', ctypes.c_double),
  283. ('szAppName', ctypes.c_char*64),
  284. ('dwTime_Year',ctypes.c_uint32),
  285. ('dwTime_Month',ctypes.c_uint32),
  286. ('dwReserved',ctypes.c_uint32),
  287. ('dwTime_Day',ctypes.c_uint32),
  288. ('dwTime_Hour',ctypes.c_uint32),
  289. ('dwTime_Min',ctypes.c_uint32),
  290. ('dwTime_Sec',ctypes.c_uint32),
  291. ('dwTime_MilliSec',ctypes.c_uint32),
  292. ('szFileComment',ctypes.c_char*256),
  293. ]
  294. class ns_ENTITYINFO(ctypes.Structure):
  295. _fields_ = [('szEntityLabel', ctypes.c_char*32),
  296. ('dwEntityType',ctypes.c_uint32),
  297. ('dwItemCount',ctypes.c_uint32),
  298. ]
  299. entity_types = { 0 : 'ns_ENTITY_UNKNOWN' ,
  300. 1 : 'ns_ENTITY_EVENT' ,
  301. 2 : 'ns_ENTITY_ANALOG' ,
  302. 3 : 'ns_ENTITY_SEGMENT' ,
  303. 4 : 'ns_ENTITY_NEURALEVENT' ,
  304. }
  305. class ns_EVENTINFO(ctypes.Structure):
  306. _fields_ = [
  307. ('dwEventType',ctypes.c_uint32),
  308. ('dwMinDataLength',ctypes.c_uint32),
  309. ('dwMaxDataLength',ctypes.c_uint32),
  310. ('szCSVDesc', ctypes.c_char*128),
  311. ]
  312. class ns_ANALOGINFO(ctypes.Structure):
  313. _fields_ = [
  314. ('dSampleRate',ctypes.c_double),
  315. ('dMinVal',ctypes.c_double),
  316. ('dMaxVal',ctypes.c_double),
  317. ('szUnits', ctypes.c_char*16),
  318. ('dResolution',ctypes.c_double),
  319. ('dLocationX',ctypes.c_double),
  320. ('dLocationY',ctypes.c_double),
  321. ('dLocationZ',ctypes.c_double),
  322. ('dLocationUser',ctypes.c_double),
  323. ('dHighFreqCorner',ctypes.c_double),
  324. ('dwHighFreqOrder',ctypes.c_uint32),
  325. ('szHighFilterType', ctypes.c_char*16),
  326. ('dLowFreqCorner',ctypes.c_double),
  327. ('dwLowFreqOrder',ctypes.c_uint32),
  328. ('szLowFilterType', ctypes.c_char*16),
  329. ('szProbeInfo', ctypes.c_char*128),
  330. ]
  331. class ns_SEGMENTINFO(ctypes.Structure):
  332. _fields_ = [
  333. ('dwSourceCount',ctypes.c_uint32),
  334. ('dwMinSampleCount',ctypes.c_uint32),
  335. ('dwMaxSampleCount',ctypes.c_uint32),
  336. ('dSampleRate',ctypes.c_double),
  337. ('szUnits', ctypes.c_char*32),
  338. ]
  339. class ns_SEGSOURCEINFO(ctypes.Structure):
  340. _fields_ = [
  341. ('dMinVal',ctypes.c_double),
  342. ('dMaxVal',ctypes.c_double),
  343. ('dResolution',ctypes.c_double),
  344. ('dSubSampleShift',ctypes.c_double),
  345. ('dLocationX',ctypes.c_double),
  346. ('dLocationY',ctypes.c_double),
  347. ('dLocationZ',ctypes.c_double),
  348. ('dLocationUser',ctypes.c_double),
  349. ('dHighFreqCorner',ctypes.c_double),
  350. ('dwHighFreqOrder',ctypes.c_uint32),
  351. ('szHighFilterType', ctypes.c_char*16),
  352. ('dLowFreqCorner',ctypes.c_double),
  353. ('dwLowFreqOrder',ctypes.c_uint32),
  354. ('szLowFilterType', ctypes.c_char*16),
  355. ('szProbeInfo', ctypes.c_char*128),
  356. ]
  357. class ns_NEURALINFO(ctypes.Structure):
  358. _fields_ = [
  359. ('dwSourceEntityID',ctypes.c_uint32),
  360. ('dwSourceUnitID',ctypes.c_uint32),
  361. ('szProbeInfo',ctypes.c_char*128),
  362. ]