main.cpp 62 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582
  1. //////////////////////////////////////////////////////////////////////////////
  2. //
  3. // (c) Copyright 2012 Blackrock Microsystems
  4. //
  5. // $Workfile: main.c $
  6. // $Archive: /n2h5/main.c $
  7. // $Revision: 1 $
  8. // $Date: 11/1/12 1:00p $
  9. // $Author: Ehsan $
  10. //
  11. // $NoKeywords: $
  12. //
  13. //////////////////////////////////////////////////////////////////////////////
  14. //
  15. // PURPOSE:
  16. //
  17. // Convert nsx and nev files to hdf5 format
  18. //
  19. // Multiple data groups can be merged in one file
  20. // For example to combine different experiments.
  21. // Main data group populates the root itself,
  22. // the next roots at "/group00001" and so forth
  23. // The field GroupCount in the root attributes contains total data groups count
  24. // While first level group names are part of the spec (/channel, /comment, /group00001, ...)
  25. // the sub-group names should not be relied upon, instead all children should be iterated
  26. // and attributes consulted to find the information about each sub-group
  27. // To merge channels of the same experiment, it should be added to the same group
  28. // for example the raw recording of /channel/channel00001 can go to /channel/channel00001_1
  29. //
  30. //////////////////////////////////////////////////////////////////////////////
  31. #include "stdafx.h"
  32. #include <algorithm>
  33. #include "n2h5.h"
  34. #include "NevNsx.h"
  35. #ifndef WIN32
  36. #include <sys/types.h>
  37. #include <sys/stat.h>
  38. #include <unistd.h>
  39. #endif
  40. #ifdef WIN32 // Windows needs the different spelling
  41. #define ftello _ftelli64
  42. #define fseeko _fseeki64
  43. #endif
  44. // TODO: optimize these numbers
  45. #define CHUNK_SIZE_CONTINUOUS (1024)
  46. #define CHUNK_SIZE_EVENT (1024)
  47. uint16_t g_nCombine = 0; // subchannel combine level
  48. bool g_bAppend = false;
  49. bool g_bNoSpikes = false;
  50. bool g_bSkipEmpty = false;
  51. // Keep last synch packet
  52. BmiSynch_t g_synch = {0};
  53. // Author & Date: Ehsan Azar Jan 16, 2013
  54. // Purpose: Add created header to the hdf file
  55. // Inputs:
  56. // file - the destination file
  57. // header - Root header
  58. // Outputs:
  59. // Returns 0 on success, error code otherwise
  60. int AddRoot(hid_t file, BmiRootAttr_t & header)
  61. {
  62. herr_t ret;
  63. // Add file header as attribute of the root group
  64. {
  65. std::string strAttr = "BmiRoot";
  66. hsize_t dims[1] = {1};
  67. hid_t space = H5Screate_simple(1, dims, NULL);
  68. hid_t gid_root = H5Gopen(file, "/", H5P_DEFAULT);
  69. if(!H5Aexists(gid_root, strAttr.c_str()))
  70. {
  71. hid_t tid_root_attr = CreateRootAttrType(file);
  72. hid_t aid_root = H5Acreate(gid_root, strAttr.c_str(), tid_root_attr, space, H5P_DEFAULT, H5P_DEFAULT);
  73. ret = H5Awrite(aid_root, tid_root_attr, &header);
  74. ret = H5Aclose(aid_root);
  75. H5Tclose(tid_root_attr);
  76. }
  77. ret = H5Gclose(gid_root);
  78. ret = H5Sclose(space);
  79. }
  80. return 0;
  81. }
  82. // Author & Date: Ehsan Azar Nov 17, 2012
  83. // Purpose: Create and add root
  84. // Inputs:
  85. // pFile - the source file
  86. // file - the destination file
  87. // isHdr - NEV header
  88. // Outputs:
  89. // Returns 0 on success, error code otherwise
  90. int AddRoot(FILE * pFile, hid_t file, NevHdr & isHdr)
  91. {
  92. BmiRootAttr_t header;
  93. memset(&header, 0, sizeof(header));
  94. header.nMajorVersion = 1;
  95. strncpy(header.szApplication, isHdr.szApplication, 32);
  96. strncpy(header.szComment, isHdr.szComment, 256);
  97. header.nGroupCount = 1;
  98. {
  99. TIMSTM ts;
  100. memset(&ts, 0, sizeof(ts));
  101. SYSTEMTIME & st = isHdr.isAcqTime;
  102. sprintf((char *)&ts, "%04hd-%02hd-%02hd %02hd:%02hd:%02hd.%06d",
  103. st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute,
  104. st.wSecond, st.wMilliseconds * 1000);
  105. ts.chNull = '\0';
  106. strncpy(header.szDate, (char *)&ts, sizeof(ts));
  107. }
  108. return AddRoot(file, header);
  109. }
  110. // Author & Date: Ehsan Azar Nov 17, 2012
  111. // Purpose: Create and add root
  112. // Inputs:
  113. // szSrcFile - source file name
  114. // pFile - the source file
  115. // file - the destination file
  116. // isHdr - Nsx 2.1 header
  117. // Outputs:
  118. // Returns 0 on success, error code otherwise
  119. int AddRoot(const char * szSrcFile, FILE * pFile, hid_t file, Nsx21Hdr & isHdr)
  120. {
  121. BmiRootAttr_t header;
  122. memset(&header, 0, sizeof(header));
  123. header.nMajorVersion = 1;
  124. strncpy(header.szApplication, isHdr.szGroup, 16);
  125. char szComment[] = ""; // Old format does not have a comment
  126. strncpy(header.szComment, szComment, 1024);
  127. header.nGroupCount = 1;
  128. TIMSTM ts;
  129. memset(&ts, 0, sizeof(ts));
  130. #ifdef WIN32
  131. WIN32_FILE_ATTRIBUTE_DATA fattr;
  132. if (!GetFileAttributesEx(szSrcFile, GetFileExInfoStandard, &fattr))
  133. {
  134. printf("Cannot get file attributes\n");
  135. return 1;
  136. }
  137. SYSTEMTIME st;
  138. FileTimeToSystemTime(&fattr.ftCreationTime, &st);
  139. sprintf((char *)&ts, "%04hd-%02hd-%02hd %02hd:%02hd:%02hd.%06d",
  140. st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute,
  141. st.wSecond, st.wMilliseconds * 1000);
  142. ts.chNull = '\0';
  143. header.szDate = (char *)&ts;
  144. #else
  145. struct stat st;
  146. if (stat(szSrcFile, &st))
  147. {
  148. printf("Cannot get file attributes\n");
  149. return 1;
  150. }
  151. //unused time_t t = st.st_mtime;
  152. // TODO: use strftime to convert to st
  153. #endif
  154. return AddRoot(file, header);
  155. }
  156. // Author & Date: Ehsan Azar Nov 17, 2012
  157. // Purpose: Create and add root
  158. // Inputs:
  159. // pFile - the source file
  160. // file - the destination file
  161. // isHdr - NSx 2.2 header
  162. // Outputs:
  163. // Returns 0 on success, error code otherwise
  164. int AddRoot(FILE * pFile, hid_t file, Nsx22Hdr & isHdr)
  165. {
  166. BmiRootAttr_t header;
  167. memset(&header, 0, sizeof(header));
  168. header.nMajorVersion = 1;
  169. strncpy(header.szApplication, isHdr.szGroup, 16);
  170. strncpy(header.szComment, isHdr.szComment, 256);
  171. header.nGroupCount = 1;
  172. {
  173. TIMSTM ts;
  174. memset(&ts, 0, sizeof(ts));
  175. SYSTEMTIME & st = isHdr.isAcqTime;
  176. sprintf((char *)&ts, "%04hd-%02hd-%02hd %02hd:%02hd:%02hd.%06d",
  177. st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute,
  178. st.wSecond, st.wMilliseconds * 1000);
  179. ts.chNull = '\0';
  180. strncpy(header.szDate, (char *)&ts, sizeof(ts));
  181. }
  182. return AddRoot(file, header);
  183. }
  184. // Author & Date: Ehsan Azar Nov 13, 2012
  185. // Purpose: Convert NEV
  186. // Inputs:
  187. // pFile - the source file
  188. // file - the destination file
  189. // Outputs:
  190. // Returns 0 on success, error code otherwise
  191. int ConvertNev(FILE * pFile, hid_t file)
  192. {
  193. herr_t ret;
  194. NevHdr isHdr;
  195. fseeko(pFile, 0, SEEK_SET); // read header from beginning of file
  196. if (fread(&isHdr, sizeof(isHdr), 1, pFile) != 1)
  197. {
  198. printf("Cannot read source file header\n");
  199. return 1;
  200. }
  201. // Add root attribute
  202. if (AddRoot(pFile, file, isHdr))
  203. return 1;
  204. uint16_t nSpikeLength = 48;
  205. hid_t tid_spike = -1;
  206. hid_t tid_dig = -1;
  207. hid_t tid_comment = -1;
  208. hid_t tid_tracking[cbMAXTRACKOBJ];
  209. int size_tracking[cbMAXTRACKOBJ] = {0};
  210. hid_t tid_synch = -1;
  211. hid_t tid_sampling_attr = -1;
  212. hid_t tid_filt_attr = -1;
  213. // 21, 22, 23 flat file verion number
  214. uint32_t nVer = isHdr.byFileRevMajor * 10 + isHdr.byFileRevMinor;
  215. nSpikeLength = (isHdr.dwBytesPerPacket - 8) / 2;
  216. char * szMapFile = NULL;
  217. BmiTrackingAttr_t trackingAttr[cbMAXTRACKOBJ];
  218. memset(trackingAttr, 0, sizeof(trackingAttr));
  219. BmiSynchAttr_t synchAttr;
  220. memset(&synchAttr, 0, sizeof(synchAttr));
  221. BmiSamplingAttr_t samplingAttr[cbNUM_ANALOG_CHANS];
  222. memset(samplingAttr, 0, sizeof(samplingAttr));
  223. BmiChanAttr_t chanAttr[cbMAXCHANS];
  224. memset(chanAttr, 0, sizeof(chanAttr));
  225. BmiChanExtAttr_t chanExtAttr[cbNUM_ANALOG_CHANS];
  226. memset(chanExtAttr, 0, sizeof(chanExtAttr));
  227. BmiFiltAttr_t filtAttr[cbNUM_ANALOG_CHANS];
  228. memset(filtAttr, 0, sizeof(filtAttr));
  229. // NEV provides Ext1 additional header
  230. BmiChanExt1Attr_t chanExt1Attr[cbNUM_ANALOG_CHANS];
  231. memset(chanExt1Attr, 0, sizeof(chanExt1Attr));
  232. // Read the header to fill channel attributes
  233. for (uint32_t i = 0; i < isHdr.dwNumOfExtendedHeaders; ++i)
  234. {
  235. NevExtHdr isExtHdr;
  236. if (fread(&isExtHdr, sizeof(isExtHdr), 1, pFile) != 1)
  237. {
  238. printf("Invalid source file header\n");
  239. return 1;
  240. }
  241. if (0 == strncmp(isExtHdr.achPacketID, "NEUEVWAV", sizeof(isExtHdr.achPacketID)))
  242. {
  243. if (isExtHdr.neuwav.wave_samples != 0)
  244. nSpikeLength = isExtHdr.neuwav.wave_samples;
  245. int id = isExtHdr.id;
  246. if (id == 0 || id > cbNUM_ANALOG_CHANS)
  247. {
  248. printf("Invalid channel ID in source file header\n");
  249. return 1;
  250. }
  251. id--; // make it zero-based
  252. chanAttr[id].id = isExtHdr.id;
  253. // Currently all spikes are sampled at clock rate
  254. samplingAttr[id].fClock = float(isHdr.dwTimeStampResolutionHz);
  255. samplingAttr[id].fSampleRate = float(isHdr.dwSampleResolutionHz);
  256. samplingAttr[id].nSampleBits = isExtHdr.neuwav.wave_bytes * 8;
  257. chanExtAttr[id].phys_connector = isExtHdr.neuwav.phys_connector;
  258. chanExtAttr[id].connector_pin = isExtHdr.neuwav.connector_pin;
  259. chanExtAttr[id].dFactor = isExtHdr.neuwav.digital_factor;
  260. chanExt1Attr[id].energy_thresh = isExtHdr.neuwav.energy_thresh;
  261. chanExt1Attr[id].high_thresh = isExtHdr.neuwav.high_thresh;
  262. chanExt1Attr[id].low_thresh = isExtHdr.neuwav.low_thresh;
  263. chanExt1Attr[id].sortCount = isExtHdr.neuwav.sorted_count;
  264. }
  265. else if (0 == strncmp(isExtHdr.achPacketID, "NEUEVLBL", sizeof(isExtHdr.achPacketID)))
  266. {
  267. int id = isExtHdr.id;
  268. if (id == 0 || id > cbNUM_ANALOG_CHANS)
  269. {
  270. printf("Invalid channel ID in source file header\n");
  271. return 1;
  272. }
  273. id--;
  274. strncpy(chanAttr[id].szLabel, isExtHdr.neulabel.label, 16);
  275. }
  276. else if (0 == strncmp(isExtHdr.achPacketID, "NEUEVFLT", sizeof(isExtHdr.achPacketID)))
  277. {
  278. int id = isExtHdr.id;
  279. if (id == 0 || id > cbNUM_ANALOG_CHANS)
  280. {
  281. printf("Invalid channel ID in source file header\n");
  282. return 1;
  283. }
  284. id--;
  285. filtAttr[id].hpfreq = isExtHdr.neuflt.hpfreq;
  286. filtAttr[id].hporder = isExtHdr.neuflt.hporder;
  287. filtAttr[id].hptype = isExtHdr.neuflt.hptype;
  288. filtAttr[id].lpfreq = isExtHdr.neuflt.lpfreq;
  289. filtAttr[id].lporder = isExtHdr.neuflt.lporder;
  290. filtAttr[id].lptype = isExtHdr.neuflt.lptype;
  291. }
  292. else if (0 == strncmp(isExtHdr.achPacketID, "VIDEOSYN", sizeof(isExtHdr.achPacketID)))
  293. {
  294. synchAttr.id = isExtHdr.id;
  295. synchAttr.fFps = isExtHdr.videosyn.fFps;
  296. strncpy(synchAttr.szLabel, isExtHdr.videosyn.label, 16);
  297. }
  298. else if (0 == strncmp(isExtHdr.achPacketID, "TRACKOBJ", sizeof(isExtHdr.achPacketID)))
  299. {
  300. int id = isExtHdr.trackobj.trackID; // 1-based
  301. if (id == 0 || id > cbMAXTRACKOBJ)
  302. {
  303. printf("Invalid trackable ID in source file header\n");
  304. return 1;
  305. }
  306. id--;
  307. trackingAttr[id].type = isExtHdr.id; // 0-based type
  308. trackingAttr[id].trackID = isExtHdr.trackobj.trackID;
  309. trackingAttr[id].maxPoints = isExtHdr.trackobj.maxPoints;
  310. strncpy(trackingAttr[id].szLabel, isExtHdr.trackobj.label, 16);
  311. }
  312. else if (0 == strncmp(isExtHdr.achPacketID, "MAPFILE", sizeof(isExtHdr.achPacketID)))
  313. {
  314. szMapFile = _strdup(isExtHdr.mapfile.label);
  315. }
  316. else if (0 == strncmp(isExtHdr.achPacketID, "DIGLABEL", sizeof(isExtHdr.achPacketID)))
  317. {
  318. int id = 0;
  319. if (isExtHdr.diglabel.mode == 0)
  320. {
  321. id = cbFIRST_SERIAL_CHAN; // Serial
  322. chanAttr[id].id = cbFIRST_SERIAL_CHAN + 1;
  323. }
  324. else if (isExtHdr.diglabel.mode == 1)
  325. {
  326. id = cbFIRST_DIGIN_CHAN; // Digital
  327. chanAttr[id].id = cbFIRST_DIGIN_CHAN + 1;
  328. } else {
  329. printf("Invalid digital input mode in source file header\n");
  330. return 1;
  331. }
  332. strncpy(chanAttr[id].szLabel, isExtHdr.diglabel.label, 16);
  333. } else {
  334. printf("Unknown header (%7s) in the source file\n", isExtHdr.achPacketID);
  335. }
  336. } // end for (uint32_t i = 0
  337. uint32_t nChannelOffset = 0;
  338. uint32_t nDigChannelOffset = 0;
  339. uint32_t nSerChannelOffset = 0;
  340. hsize_t dims[1] = {1};
  341. hid_t space_attr = H5Screate_simple(1, dims, NULL);
  342. // Add channel group
  343. {
  344. hid_t gid_channel = -1;
  345. if (H5Lexists(file, "channel", H5P_DEFAULT))
  346. gid_channel = H5Gopen(file, "channel", H5P_DEFAULT);
  347. else
  348. gid_channel = H5Gcreate(file, "channel", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
  349. if (szMapFile != NULL)
  350. {
  351. hid_t tid_attr_map_str = H5Tcopy(H5T_C_S1);
  352. ret = H5Tset_size(tid_attr_map_str, 1024);
  353. hid_t aid = H5Acreate(gid_channel, "MapFile", tid_attr_map_str, space_attr, H5P_DEFAULT, H5P_DEFAULT);
  354. char szMapFileRecord[1024] = {0};
  355. strcpy(szMapFileRecord, szMapFile);
  356. ret = H5Awrite(aid, tid_attr_map_str, szMapFileRecord);
  357. ret = H5Aclose(aid);
  358. H5Tclose(tid_attr_map_str);
  359. }
  360. if (g_bAppend)
  361. {
  362. bool bExists = false;
  363. // Find the last place to append to
  364. do {
  365. nChannelOffset++;
  366. std::string strLabel = "channel";
  367. char szNum[7];
  368. sprintf(szNum, "%05u", nChannelOffset);
  369. strLabel += szNum;
  370. bExists = (H5Lexists(gid_channel, strLabel.c_str(), H5P_DEFAULT) != 0);
  371. } while(bExists);
  372. nChannelOffset--;
  373. do {
  374. nDigChannelOffset++;
  375. std::string strLabel = "digital";
  376. char szNum[7];
  377. sprintf(szNum, "%05u", nDigChannelOffset);
  378. strLabel += szNum;
  379. bExists = (H5Lexists(gid_channel, strLabel.c_str(), H5P_DEFAULT) != 0);
  380. } while(bExists);
  381. nDigChannelOffset--;
  382. do {
  383. nSerChannelOffset++;
  384. std::string strLabel = "serial";
  385. char szNum[7];
  386. sprintf(szNum, "%05u", nSerChannelOffset);
  387. strLabel += szNum;
  388. bExists = (H5Lexists(gid_channel, strLabel.c_str(), H5P_DEFAULT) != 0);
  389. } while(bExists);
  390. nSerChannelOffset--;
  391. }
  392. tid_spike = CreateSpike16Type(gid_channel, nSpikeLength);
  393. hid_t tid_chan_attr = CreateChanAttrType(gid_channel);
  394. hid_t tid_chanext_attr = CreateChanExtAttrType(gid_channel);
  395. hid_t tid_chanext1_attr = CreateChanExt1AttrType(gid_channel);
  396. tid_sampling_attr = CreateSamplingAttrType(gid_channel);
  397. tid_filt_attr = CreateFiltAttrType(gid_channel);
  398. for (int i = 0; i < cbNUM_ANALOG_CHANS; ++i)
  399. {
  400. if (chanAttr[i].id == 0)
  401. continue;
  402. char szNum[7];
  403. std::string strLabel = "channel";
  404. sprintf(szNum, "%05u", chanAttr[i].id + nChannelOffset);
  405. strLabel += szNum;
  406. hid_t gid = -1;
  407. if (H5Lexists(gid_channel, strLabel.c_str(), H5P_DEFAULT))
  408. gid = H5Gopen(gid_channel, strLabel.c_str(), H5P_DEFAULT);
  409. else
  410. gid = H5Gcreate(gid_channel, strLabel.c_str(), H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
  411. // Basic channel attributes
  412. if (!H5Aexists(gid, "BmiChan"))
  413. {
  414. hid_t aid = H5Acreate(gid, "BmiChan", tid_chan_attr, space_attr, H5P_DEFAULT, H5P_DEFAULT);
  415. ret = H5Awrite(aid, tid_chan_attr, &chanAttr[i]);
  416. ret = H5Aclose(aid);
  417. }
  418. // Extra channel attributes
  419. if (!H5Aexists(gid, "BmiChanExt"))
  420. {
  421. hid_t aid = H5Acreate(gid, "BmiChanExt", tid_chanext_attr, space_attr, H5P_DEFAULT, H5P_DEFAULT);
  422. ret = H5Awrite(aid, tid_chanext_attr, &chanExtAttr[i]);
  423. ret = H5Aclose(aid);
  424. }
  425. // Additional extra channel attributes
  426. if (!H5Aexists(gid, "BmiChanExt1"))
  427. {
  428. hid_t aid = H5Acreate(gid, "BmiChanExt1", tid_chanext1_attr, space_attr, H5P_DEFAULT, H5P_DEFAULT);
  429. ret = H5Awrite(aid, tid_chanext1_attr, &chanExt1Attr[i]);
  430. ret = H5Aclose(aid);
  431. }
  432. ret = H5Gclose(gid);
  433. }
  434. ret = H5Tclose(tid_chanext1_attr);
  435. ret = H5Tclose(tid_chanext_attr);
  436. // Add digital and serial channel and their attributes
  437. {
  438. uint16_t id = 1 + 0;
  439. char szNum[7];
  440. std::string strLabel = "digital";
  441. sprintf(szNum, "%05u", id + nDigChannelOffset);
  442. strLabel += szNum;
  443. tid_dig = CreateDig16Type(gid_channel);
  444. hid_t gid = -1;
  445. if (H5Lexists(gid_channel, strLabel.c_str(), H5P_DEFAULT))
  446. gid = H5Gopen(gid_channel, strLabel.c_str(), H5P_DEFAULT);
  447. else
  448. gid = H5Gcreate(gid_channel, strLabel.c_str(), H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
  449. if (chanAttr[cbFIRST_DIGIN_CHAN].id)
  450. {
  451. if (!H5Aexists(gid, "BmiChan"))
  452. {
  453. hid_t aid = H5Acreate(gid, "BmiChan", tid_chan_attr, space_attr, H5P_DEFAULT, H5P_DEFAULT);
  454. ret = H5Awrite(aid, tid_chan_attr, &chanAttr[cbFIRST_DIGIN_CHAN]);
  455. ret = H5Aclose(aid);
  456. }
  457. }
  458. ret = H5Gclose(gid);
  459. }
  460. // Add digital and serial channel and their attributes
  461. {
  462. uint16_t id = 1 + 0;
  463. char szNum[7];
  464. std::string strLabel = "serial";
  465. sprintf(szNum, "%05u", id + nSerChannelOffset);
  466. strLabel += szNum;
  467. hid_t gid = -1;
  468. if (H5Lexists(gid_channel, strLabel.c_str(), H5P_DEFAULT))
  469. gid = H5Gopen(gid_channel, strLabel.c_str(), H5P_DEFAULT);
  470. else
  471. gid = H5Gcreate(gid_channel, strLabel.c_str(), H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
  472. if (chanAttr[cbFIRST_SERIAL_CHAN].id)
  473. {
  474. if (!H5Aexists(gid, "BmiChan"))
  475. {
  476. hid_t aid = H5Acreate(gid, "BmiChan", tid_chan_attr, space_attr, H5P_DEFAULT, H5P_DEFAULT);
  477. ret = H5Awrite(aid, tid_chan_attr, &chanAttr[cbFIRST_SERIAL_CHAN]);
  478. ret = H5Aclose(aid);
  479. }
  480. }
  481. ret = H5Gclose(gid);
  482. }
  483. ret = H5Tclose(tid_chan_attr);
  484. ret = H5Gclose(gid_channel);
  485. }
  486. bool bHasVideo = false;
  487. // Add video group
  488. if (nVer >= 23)
  489. {
  490. hid_t gid_video = -1;
  491. if (H5Lexists(file, "video", H5P_DEFAULT))
  492. gid_video = H5Gopen(file, "video", H5P_DEFAULT);
  493. else
  494. gid_video = H5Gcreate(file, "video", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
  495. hid_t tid_tracking_attr = CreateTrackingAttrType(gid_video);
  496. hid_t tid_synch_attr = CreateSynchAttrType(gid_video);
  497. tid_synch = CreateSynchType(gid_video);
  498. // Add synchronization groups
  499. if (synchAttr.szLabel != NULL) // Warning: Always true!
  500. {
  501. bHasVideo = true;
  502. char szNum[7];
  503. std::string strLabel = "synch";
  504. sprintf(szNum, "%05u", synchAttr.id);
  505. strLabel += szNum;
  506. hid_t gid = -1;
  507. if (H5Lexists(gid_video, strLabel.c_str(), H5P_DEFAULT))
  508. gid = H5Gopen(gid_video, strLabel.c_str(), H5P_DEFAULT);
  509. else
  510. gid = H5Gcreate(gid_video, strLabel.c_str(), H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
  511. if (!H5Aexists(gid, "BmiSynch"))
  512. {
  513. hid_t aid = H5Acreate(gid, "BmiSynch", tid_synch_attr, space_attr, H5P_DEFAULT, H5P_DEFAULT);
  514. ret = H5Awrite(aid, tid_synch_attr, &synchAttr);
  515. ret = H5Aclose(aid);
  516. ret = H5Gclose(gid);
  517. }
  518. }
  519. // Add tracking groups
  520. for (int i = 0; i < cbMAXTRACKOBJ; ++i)
  521. {
  522. tid_tracking[i] = -1;
  523. if (trackingAttr[i].szLabel != NULL) // Warning: Always true!
  524. {
  525. bHasVideo = true;
  526. int dim = 2, width = 2;
  527. switch (trackingAttr[i].type)
  528. {
  529. case cbTRACKOBJ_TYPE_2DMARKERS:
  530. case cbTRACKOBJ_TYPE_2DBLOB:
  531. case cbTRACKOBJ_TYPE_2DBOUNDARY:
  532. dim = 2;
  533. width = 2;
  534. break;
  535. case cbTRACKOBJ_TYPE_3DMARKERS:
  536. dim = 3;
  537. width = 2;
  538. break;
  539. case cbTRACKOBJ_TYPE_1DSIZE:
  540. dim = 1;
  541. width = 4;
  542. break;
  543. default:
  544. // The defualt is already set
  545. break;
  546. }
  547. // The only fixed length now is the case for single point tracking
  548. tid_tracking[i] = CreateTrackingType(gid_video, dim, width);
  549. size_tracking[i] = dim * width; // Size of each tracking element in bytes
  550. char szNum[7];
  551. std::string strLabel = "tracking";
  552. sprintf(szNum, "%05u", trackingAttr[i].trackID);
  553. strLabel += szNum;
  554. hid_t gid = -1;
  555. if (H5Lexists(gid_video, strLabel.c_str(), H5P_DEFAULT))
  556. gid = H5Gopen(gid_video, strLabel.c_str(), H5P_DEFAULT);
  557. else
  558. gid = H5Gcreate(gid_video, strLabel.c_str(), H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
  559. if (!H5Aexists(gid, "BmiTracking"))
  560. {
  561. hid_t aid = H5Acreate(gid, "BmiTracking", tid_tracking_attr, space_attr, H5P_DEFAULT, H5P_DEFAULT);
  562. ret = H5Awrite(aid, tid_tracking_attr, &trackingAttr[i]);
  563. ret = H5Aclose(aid);
  564. ret = H5Gclose(gid);
  565. }
  566. } // end if (trackingAttr[i].szLabel
  567. } // end for (int i = 0
  568. ret = H5Tclose(tid_tracking_attr);
  569. ret = H5Tclose(tid_synch_attr);
  570. ret = H5Gclose(gid_video);
  571. }
  572. // Comment group
  573. if (nVer >= 23)
  574. {
  575. hid_t gid_comment = H5Gcreate(file, "comment", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
  576. tid_comment = CreateCommentType(gid_comment);
  577. {
  578. // NeuroMotive charset is fixed
  579. hid_t aid = H5Acreate(gid_comment, "NeuroMotiveCharset", H5T_NATIVE_UINT8, space_attr, H5P_DEFAULT, H5P_DEFAULT);
  580. uint8_t charset = 255;
  581. ret = H5Awrite(aid, H5T_NATIVE_UINT8, &charset);
  582. ret = H5Aclose(aid);
  583. hid_t gid;
  584. if (bHasVideo)
  585. {
  586. gid = H5Gcreate(gid_comment, "comment00256", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
  587. aid = H5Acreate(gid, "Charset", H5T_NATIVE_UINT8, space_attr, H5P_DEFAULT, H5P_DEFAULT);
  588. ret = H5Awrite(aid, H5T_NATIVE_UINT8, &charset);
  589. ret = H5Aclose(aid);
  590. ret = H5Gclose(gid);
  591. }
  592. charset = 0; // Add group for normal comments right here
  593. gid = H5Gcreate(gid_comment, "comment00001", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
  594. aid = H5Acreate(gid, "Charset", H5T_NATIVE_UINT8, space_attr, H5P_DEFAULT, H5P_DEFAULT);
  595. ret = H5Awrite(aid, H5T_NATIVE_UINT8, &charset);
  596. ret = H5Aclose(aid);
  597. ret = H5Gclose(gid);
  598. }
  599. ret = H5Gclose(gid_comment);
  600. }
  601. ret = H5Sclose(space_attr);
  602. // ---------------------------------------------------------------------------------------------
  603. // Done with reading headers
  604. // Add packet tables
  605. {
  606. size_t chunk_size = CHUNK_SIZE_EVENT;
  607. int compression = -1; // TODO: use options to add compression
  608. hid_t ptid_spike[cbNUM_ANALOG_CHANS];
  609. for (int i = 0; i < cbNUM_ANALOG_CHANS; ++i)
  610. ptid_spike[i] = -1;
  611. hid_t ptid_serial = -1, ptid_digital = -1, ptid_synch = -1;
  612. hid_t ptid_comment[256];
  613. for (int i = 0; i < 256; ++i)
  614. ptid_comment[i] = -1;
  615. hid_t ptid_tracking[cbMAXTRACKOBJ];
  616. for (int i = 0; i < cbMAXTRACKOBJ; ++i)
  617. ptid_tracking[i] = -1;
  618. NevData nevData;
  619. fseeko(pFile, isHdr.dwStartOfData, SEEK_SET);
  620. size_t nGot = fread(&nevData, isHdr.dwBytesPerPacket, 1, pFile);
  621. if (nGot != 1)
  622. {
  623. perror("Source file is empty or invalid\n");
  624. return 1;
  625. }
  626. do {
  627. if (nevData.wPacketID >= 1 && nevData.wPacketID <= cbNUM_ANALOG_CHANS) // found spike data
  628. {
  629. if (!g_bNoSpikes)
  630. {
  631. int id = nevData.wPacketID; // 1-based
  632. if (ptid_spike[id - 1] < 0)
  633. {
  634. char szNum[7];
  635. std::string strLabel = "/channel/channel";
  636. sprintf(szNum, "%05u", id + nChannelOffset);
  637. strLabel += szNum;
  638. hid_t gid;
  639. if(H5Lexists(file, strLabel.c_str(), H5P_DEFAULT))
  640. {
  641. gid = H5Gopen(file, strLabel.c_str(), H5P_DEFAULT);
  642. } else {
  643. printf("Creating %s without attributes\n", strLabel.c_str());
  644. gid = H5Gcreate(file, strLabel.c_str(), H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
  645. }
  646. ptid_spike[id - 1] = H5PTcreate_fl(gid, "spike_set", tid_spike, chunk_size, compression);
  647. hid_t dsid = H5Dopen(gid, "spike_set", H5P_DEFAULT);
  648. ret = H5Gclose(gid);
  649. hid_t space_attr = H5Screate_simple(1, dims, NULL);
  650. // Add data sampling attribute
  651. hid_t aid = H5Acreate(dsid, "Sampling", tid_sampling_attr, space_attr, H5P_DEFAULT, H5P_DEFAULT);
  652. ret = H5Awrite(aid, tid_sampling_attr, &samplingAttr[id - 1]);
  653. ret = H5Aclose(aid);
  654. // Add data filtering attribute
  655. aid = H5Acreate(dsid, "Filter", tid_filt_attr, space_attr, H5P_DEFAULT, H5P_DEFAULT);
  656. ret = H5Awrite(aid, tid_filt_attr, &filtAttr[id - 1]);
  657. ret = H5Aclose(aid);
  658. ret = H5Sclose(space_attr);
  659. }
  660. BmiSpike16_t spk;
  661. spk.dwTimestamp = nevData.dwTimestamp;
  662. spk.res = nevData.spike.res;
  663. spk.unit = nevData.spike.unit;
  664. for (int i = 0; i < nSpikeLength; ++i)
  665. spk.wave[i] = nevData.spike.wave[i];
  666. ret = H5PTappend(ptid_spike[id - 1], 1, &spk);
  667. } // end if (!g_bNoSpikes
  668. } else {
  669. switch (nevData.wPacketID)
  670. {
  671. case 0: // found a digital or serial event
  672. if (!(nevData.digital.byInsertionReason & 1))
  673. {
  674. // Other digital events are not implemented in NSP yet
  675. printf("Unkown digital event (%u) dropped\n", nevData.digital.byInsertionReason);
  676. break;
  677. }
  678. if (nevData.digital.byInsertionReason & 128) // If bit 7 is set it is serial
  679. {
  680. if (ptid_serial < 0)
  681. {
  682. uint16_t id = 1 + 0;
  683. char szNum[7];
  684. std::string strLabel = "/channel/serial";
  685. sprintf(szNum, "%05u", id + nSerChannelOffset);
  686. strLabel += szNum;
  687. hid_t gid = H5Gopen(file, strLabel.c_str(), H5P_DEFAULT);
  688. ptid_serial = H5PTcreate_fl(gid, "serial_set", tid_dig, chunk_size, compression);
  689. H5Gclose(gid);
  690. }
  691. BmiDig16_t dig;
  692. dig.dwTimestamp = nevData.dwTimestamp;
  693. dig.value = nevData.digital.wDigitalValue;
  694. ret = H5PTappend(ptid_serial, 1, &dig);
  695. } else {
  696. if (ptid_digital < 0)
  697. {
  698. uint16_t id = 1 + 0;
  699. char szNum[7];
  700. std::string strLabel = "/channel/digital";
  701. sprintf(szNum, "%05u", id + nDigChannelOffset);
  702. strLabel += szNum;
  703. hid_t gid = H5Gopen(file, strLabel.c_str(), H5P_DEFAULT);
  704. ptid_digital = H5PTcreate_fl(gid, "digital_set", tid_dig, chunk_size, compression);
  705. H5Gclose(gid);
  706. }
  707. BmiDig16_t dig;
  708. dig.dwTimestamp = nevData.dwTimestamp;
  709. dig.value = nevData.digital.wDigitalValue;
  710. ret = H5PTappend(ptid_digital, 1, &dig);
  711. }
  712. break;
  713. case 0xFFFF: // found a comment event
  714. {
  715. int id = nevData.comment.charset; // 0-based
  716. if (ptid_comment[id] < 0)
  717. {
  718. char szNum[7];
  719. std::string strLabel = "/comment/comment";
  720. sprintf(szNum, "%05u", id + 1);
  721. strLabel += szNum;
  722. hid_t gid;
  723. if(H5Lexists(file, strLabel.c_str(), H5P_DEFAULT))
  724. {
  725. gid = H5Gopen(file, strLabel.c_str(), H5P_DEFAULT);
  726. } else {
  727. gid = H5Gcreate(file, strLabel.c_str(), H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
  728. {
  729. hsize_t dims[1] = {1};
  730. hid_t space_attr = H5Screate_simple(1, dims, NULL);
  731. hid_t aid = H5Acreate(gid, "Charset", H5T_NATIVE_UINT8, space_attr, H5P_DEFAULT, H5P_DEFAULT);
  732. uint8_t charset = nevData.comment.charset;
  733. ret = H5Awrite(aid, H5T_NATIVE_UINT8, &charset);
  734. ret = H5Aclose(aid);
  735. ret = H5Sclose(space_attr);
  736. }
  737. }
  738. ptid_comment[id] = H5PTcreate_fl(gid, "comment_set", tid_comment, chunk_size, compression);
  739. H5Gclose(gid);
  740. }
  741. BmiComment_t cmt;
  742. cmt.dwTimestamp = nevData.dwTimestamp;
  743. cmt.data = nevData.comment.data;
  744. cmt.flags = nevData.comment.flags;
  745. strncpy(cmt.szComment, nevData.comment.comment, std::min((std::size_t)BMI_COMMENT_LEN, sizeof(nevData.comment.comment)));
  746. ret = H5PTappend(ptid_comment[id], 1, &cmt);
  747. }
  748. break;
  749. case 0xFFFE: // found a synchronization event
  750. {
  751. int id = nevData.synch.id; // 0-based
  752. if (id != 0)
  753. {
  754. printf("Unsupported synchronization source dropped\n");
  755. break;
  756. }
  757. if (ptid_synch < 0)
  758. {
  759. char szNum[7];
  760. std::string strLabel = "/video/synch";
  761. sprintf(szNum, "%05u", id + 1);
  762. strLabel += szNum;
  763. hid_t gid;
  764. if(H5Lexists(file, strLabel.c_str(), H5P_DEFAULT))
  765. {
  766. gid = H5Gopen(file, strLabel.c_str(), H5P_DEFAULT);
  767. } else {
  768. printf("Creating %s without attributes\n", strLabel.c_str());
  769. gid = H5Gcreate(file, strLabel.c_str(), H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
  770. }
  771. ptid_synch = H5PTcreate_fl(gid, "synch_set", tid_synch, chunk_size, compression);
  772. H5Gclose(gid);
  773. }
  774. g_synch.dwTimestamp = nevData.dwTimestamp;
  775. g_synch.etime = nevData.synch.etime;
  776. g_synch.frame = nevData.synch.frame;
  777. g_synch.split = nevData.synch.split;
  778. ret = H5PTappend(ptid_synch, 1, &g_synch);
  779. }
  780. break;
  781. case 0xFFFD: // found a video tracking event
  782. {
  783. int id = nevData.track.nodeID; // 0-based
  784. if (id >= cbMAXTRACKOBJ)
  785. {
  786. printf("Invalid tracking packet dropped\n");
  787. break;
  788. }
  789. if (ptid_tracking[id] < 0)
  790. {
  791. char szNum[7];
  792. std::string strLabel = "/video/tracking";
  793. sprintf(szNum, "%05u", id + 1);
  794. strLabel += szNum;
  795. hid_t gid;
  796. if(H5Lexists(file, strLabel.c_str(), H5P_DEFAULT))
  797. {
  798. gid = H5Gopen(file, strLabel.c_str(), H5P_DEFAULT);
  799. } else {
  800. printf("Creating %s without attributes\n", strLabel.c_str());
  801. gid = H5Gcreate(file, strLabel.c_str(), H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
  802. }
  803. if (tid_tracking[id] < 0)
  804. {
  805. printf("Creating tracking set with undefined type\n");
  806. hid_t gid_video = H5Gopen(file, "/video", H5P_DEFAULT);
  807. tid_tracking[id] = CreateTrackingType(gid_video, 2, 2);
  808. ret = H5Gclose(gid_video);
  809. }
  810. ptid_tracking[id] = H5PTcreate_fl(gid, "tracking_set", tid_tracking[id], chunk_size, compression);
  811. H5Gclose(gid);
  812. }
  813. // Flatten tracking array
  814. BmiTracking_fl_t tr;
  815. tr.dwTimestamp = nevData.dwTimestamp;
  816. tr.nodeCount = nevData.track.nodeCount;
  817. tr.parentID = nevData.track.parentID;
  818. // Use last synch packet to augment the data
  819. tr.etime = g_synch.etime;
  820. if (size_tracking[id] > 0)
  821. {
  822. for (int i = 0; i < nevData.track.coordsLength; ++i)
  823. {
  824. memcpy(tr.coords, (char *)&nevData.track.coords[0] + i * size_tracking[id], size_tracking[id]);
  825. ret = H5PTappend(ptid_tracking[id], 1, &tr);
  826. }
  827. }
  828. }
  829. break;
  830. default:
  831. if (nevData.wPacketID <= 2048)
  832. printf("Unexpected spike channel (%u) dropped\n", nevData.wPacketID);
  833. else
  834. printf("Unknown packet type (%u) dropped\n", nevData.wPacketID);
  835. break;
  836. }
  837. }
  838. // Read more packets
  839. nGot = fread(&nevData, isHdr.dwBytesPerPacket, 1, pFile);
  840. } while (nGot == 1);
  841. // H5Close SIGSEVs if I do not close the PT manually!
  842. for (int i = 0; i < cbNUM_ANALOG_CHANS; ++i) {
  843. if (ptid_spike[i] >= 0) {
  844. H5PTclose(ptid_spike[i]);
  845. }
  846. }
  847. }
  848. //
  849. // We are going to call H5Close so no need to close what is open at this stage
  850. //
  851. return 0;
  852. }
  853. // Author & Date: Ehsan Azar Nov 17, 2012
  854. // Purpose: Convert NSx2.1
  855. // Inputs:
  856. // szSrcFile - source file name
  857. // pFile - the source file
  858. // file - the destination file
  859. // Outputs:
  860. // Returns 0 on success, error code otherwise
  861. int ConvertNSx21(const char * szSrcFile, FILE * pFile, hid_t file)
  862. {
  863. herr_t ret;
  864. Nsx21Hdr isHdr;
  865. // Read the header
  866. fseeko(pFile, 0, SEEK_SET); // read header from beginning of file
  867. fread(&isHdr, sizeof(isHdr), 1, pFile);
  868. if (isHdr.cnChannels > cbNUM_ANALOG_CHANS)
  869. {
  870. printf("Invalid number of channels in source file header\n");
  871. return 1;
  872. }
  873. BmiChanAttr_t chanAttr[cbNUM_ANALOG_CHANS];
  874. memset(chanAttr, 0, sizeof(chanAttr));
  875. BmiSamplingAttr_t samplingAttr[cbNUM_ANALOG_CHANS];
  876. memset(samplingAttr, 0, sizeof(samplingAttr));
  877. // Add root attribute
  878. if (AddRoot(szSrcFile, pFile, file, isHdr))
  879. return 1;
  880. hid_t ptid_chan[cbNUM_ANALOG_CHANS];
  881. {
  882. size_t chunk_size = CHUNK_SIZE_CONTINUOUS;
  883. int compression = -1; // TODO: use options to add compression
  884. hsize_t dims[1] = {1};
  885. hid_t space_attr = H5Screate_simple(1, dims, NULL);
  886. hid_t gid_channel = -1;
  887. if (H5Lexists(file, "channel", H5P_DEFAULT))
  888. gid_channel = H5Gopen(file, "channel", H5P_DEFAULT);
  889. else
  890. gid_channel = H5Gcreate(file, "channel", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
  891. hid_t tid_chan_attr = CreateChanAttrType(gid_channel);
  892. hid_t tid_sampling_attr = CreateSamplingAttrType(gid_channel);
  893. for (uint32_t i = 0; i < isHdr.cnChannels; ++i)
  894. {
  895. char szNum[7];
  896. uint32_t id; // 1-based
  897. if (fread(&id, sizeof(uint32_t), 1, pFile) != 1)
  898. {
  899. printf("Invalid header in source file\n");
  900. return 1;
  901. }
  902. chanAttr[i].id = id;
  903. std::string strLabel = "chan";
  904. sprintf(szNum, "%u", id);
  905. strLabel += szNum;
  906. strncpy(chanAttr[i].szLabel, strLabel.c_str(), 64);
  907. samplingAttr[i].fClock = 30000;
  908. // FIXME: This might be incorrect for really old file recordings
  909. // TODO: search the file to see if 14 is more accurate
  910. samplingAttr[i].nSampleBits = 16;
  911. samplingAttr[i].fSampleRate = float(30000.0) / isHdr.nPeriod;
  912. uint32_t nChannelOffset = 0;
  913. if (g_bAppend)
  914. {
  915. bool bExists = false;
  916. // Find the last place to append to
  917. do {
  918. nChannelOffset++;
  919. std::string strLabel = "channel";
  920. char szNum[7];
  921. sprintf(szNum, "%05u", nChannelOffset);
  922. strLabel += szNum;
  923. bExists = (H5Lexists(gid_channel, strLabel.c_str(), H5P_DEFAULT) != 0);
  924. } while(bExists);
  925. nChannelOffset--;
  926. }
  927. strLabel = "channel";
  928. sprintf(szNum, "%05u", id + nChannelOffset);
  929. strLabel += szNum;
  930. hid_t gid = -1;
  931. if (H5Lexists(gid_channel, strLabel.c_str(), H5P_DEFAULT))
  932. gid = H5Gopen(gid_channel, strLabel.c_str(), H5P_DEFAULT);
  933. else
  934. gid = H5Gcreate(gid_channel, strLabel.c_str(), H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
  935. // Basic channel attributes
  936. hid_t aid = H5Acreate(gid, "BmiChan", tid_chan_attr, space_attr, H5P_DEFAULT, H5P_DEFAULT);
  937. ret = H5Awrite(aid, tid_chan_attr, &chanAttr[i]);
  938. ret = H5Aclose(aid);
  939. // If need to go one level deeper
  940. if (g_nCombine > 0)
  941. {
  942. hid_t gidParent = gid;
  943. sprintf(szNum, "%05u", g_nCombine);
  944. strLabel = szNum;
  945. if (H5Lexists(gidParent, strLabel.c_str(), H5P_DEFAULT))
  946. gid = H5Gopen(gidParent, strLabel.c_str(), H5P_DEFAULT);
  947. else
  948. gid = H5Gcreate(gidParent, strLabel.c_str(), H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
  949. ret = H5Gclose(gidParent);
  950. }
  951. ptid_chan[i] = H5PTcreate_fl(gid, "continuous_set", H5T_NATIVE_INT16, chunk_size, compression);
  952. hid_t dsid = H5Dopen(gid, "continuous_set", H5P_DEFAULT);
  953. ret = H5Gclose(gid);
  954. // Add data start clock attribute
  955. aid = H5Acreate(dsid, "StartClock", H5T_NATIVE_UINT32, space_attr, H5P_DEFAULT, H5P_DEFAULT);
  956. uint32_t nStartTime = 0; // 2.1 does not have paused headers
  957. ret = H5Awrite(aid, H5T_NATIVE_UINT32, &nStartTime);
  958. ret = H5Aclose(aid);
  959. // Add data sampling attribute
  960. aid = H5Acreate(dsid, "Sampling", tid_sampling_attr, space_attr, H5P_DEFAULT, H5P_DEFAULT);
  961. ret = H5Awrite(aid, tid_sampling_attr, &samplingAttr[i]);
  962. ret = H5Aclose(aid);
  963. ret = H5Dclose(dsid);
  964. } // end for (uint32_t i = 0
  965. ret = H5Tclose(tid_sampling_attr);
  966. ret = H5Tclose(tid_chan_attr);
  967. ret = H5Gclose(gid_channel);
  968. ret = H5Sclose(space_attr);
  969. }
  970. int count = 0;
  971. int16_t anDataBufferCache[cbNUM_ANALOG_CHANS][CHUNK_SIZE_CONTINUOUS];
  972. int16_t anDataBuffer[cbNUM_ANALOG_CHANS];
  973. size_t nGot = fread(anDataBuffer, sizeof(int16_t), isHdr.cnChannels, pFile);
  974. if (nGot != isHdr.cnChannels)
  975. {
  976. perror("Source file is empty or invalid\n");
  977. return 1;
  978. }
  979. do {
  980. for (uint32_t i = 0; i < isHdr.cnChannels; ++i)
  981. {
  982. anDataBufferCache[i][count] = anDataBuffer[i];
  983. }
  984. count++;
  985. if (count == CHUNK_SIZE_CONTINUOUS)
  986. {
  987. for (uint32_t i = 0; i < isHdr.cnChannels; ++i)
  988. {
  989. ret = H5PTappend(ptid_chan[i], count, &anDataBufferCache[i][0]);
  990. }
  991. count = 0;
  992. }
  993. nGot = fread(anDataBuffer, sizeof(int16_t), isHdr.cnChannels, pFile);
  994. } while (nGot == isHdr.cnChannels);
  995. // Write out the remaining chunk
  996. if (count > 0)
  997. {
  998. for (uint32_t i = 0; i < isHdr.cnChannels; ++i)
  999. {
  1000. ret = H5PTappend(ptid_chan[i], count, &anDataBufferCache[i][0]);
  1001. }
  1002. }
  1003. //
  1004. // We are going to call H5Close so no need to close what is open at this stage
  1005. //
  1006. return 0;
  1007. }
  1008. // Author & Date: Ehsan Azar Nov 17, 2012
  1009. // Purpose: Convert NSx2.2
  1010. // Inputs:
  1011. // pFile - the source file
  1012. // file - the destination file
  1013. // Outputs:
  1014. // Returns 0 on success, error code otherwise
  1015. int ConvertNSx22(FILE * pFile, hid_t file)
  1016. {
  1017. herr_t ret;
  1018. Nsx22Hdr isHdr;
  1019. // Read the header
  1020. fseeko(pFile, 0, SEEK_SET); // read header from beginning of file
  1021. fread(&isHdr, sizeof(isHdr), 1, pFile);
  1022. //UINT64 dataStart = isHdr.nBytesInHdrs + sizeof(Nsx22DataHdr);
  1023. if (isHdr.cnChannels > cbNUM_ANALOG_CHANS)
  1024. {
  1025. printf("Invalid number of channels in source file header\n");
  1026. return 1;
  1027. }
  1028. BmiFiltAttr_t filtAttr[cbNUM_ANALOG_CHANS];
  1029. memset(filtAttr, 0, sizeof(filtAttr));
  1030. BmiSamplingAttr_t samplingAttr[cbNUM_ANALOG_CHANS];
  1031. memset(samplingAttr, 0, sizeof(samplingAttr));
  1032. BmiChanAttr_t chanAttr[cbNUM_ANALOG_CHANS];
  1033. memset(chanAttr, 0, sizeof(chanAttr));
  1034. BmiChanExtAttr_t chanExtAttr[cbNUM_ANALOG_CHANS];
  1035. memset(chanExtAttr, 0, sizeof(chanExtAttr));
  1036. BmiChanExt2Attr_t chanExt2Attr[cbNUM_ANALOG_CHANS];
  1037. memset(chanExt2Attr, 0, sizeof(chanExt2Attr));
  1038. // Add root attribute
  1039. if (AddRoot(pFile, file, isHdr))
  1040. return 1;
  1041. // Read extra headers
  1042. for (uint32_t i = 0; i < isHdr.cnChannels; ++i)
  1043. {
  1044. Nsx22ExtHdr isExtHdr;
  1045. if (fread(&isExtHdr, sizeof(isExtHdr), 1, pFile) != 1)
  1046. {
  1047. printf("Invalid source file header\n");
  1048. return 1;
  1049. }
  1050. if (0 != strncmp(isExtHdr.achExtHdrID, "CC", sizeof(isExtHdr.achExtHdrID)))
  1051. {
  1052. printf("Invalid source file extended header\n");
  1053. return 1;
  1054. }
  1055. int id = isExtHdr.id;
  1056. if (id == 0 || id > cbNUM_ANALOG_CHANS)
  1057. {
  1058. printf("Invalid channel ID in source file header\n");
  1059. return 1;
  1060. }
  1061. chanAttr[i].id = isExtHdr.id;
  1062. samplingAttr[i].fClock = float(isHdr.nResolution);
  1063. samplingAttr[i].fSampleRate = float(isHdr.nResolution) / float(isHdr.nPeriod);
  1064. samplingAttr[i].nSampleBits = 16;
  1065. chanExtAttr[i].phys_connector = isExtHdr.phys_connector;
  1066. chanExtAttr[i].connector_pin = isExtHdr.connector_pin;
  1067. uint64_t anarange = int64_t(isExtHdr.anamax) - int64_t(isExtHdr.anamin);
  1068. uint64_t digrange = int64_t(isExtHdr.digmax) - int64_t(isExtHdr.digmin);
  1069. if (strncmp(isExtHdr.anaunit, "uV", 2) == 0)
  1070. {
  1071. chanExtAttr[i].dFactor = uint32_t((anarange * int64_t(1E3)) / digrange);
  1072. }
  1073. else if (strncmp(isExtHdr.anaunit, "mV", 2) == 0)
  1074. {
  1075. chanExtAttr[i].dFactor = uint32_t((anarange * int64_t(1E6)) / digrange);
  1076. }
  1077. else if (strncmp(isExtHdr.anaunit, "V", 2) == 0)
  1078. {
  1079. chanExtAttr[i].dFactor = uint32_t((anarange * int64_t(1E9)) / digrange);
  1080. } else {
  1081. printf("Unknown analog unit for channel %u, uV used\n", isExtHdr.id);
  1082. chanExtAttr[i].dFactor = uint32_t((anarange * int64_t(1E3)) / digrange);
  1083. }
  1084. filtAttr[i].hpfreq = isExtHdr.hpfreq;
  1085. filtAttr[i].hporder = isExtHdr.hporder;
  1086. filtAttr[i].hptype = isExtHdr.hptype;
  1087. filtAttr[i].lpfreq = isExtHdr.lpfreq;
  1088. filtAttr[i].lporder = isExtHdr.lporder;
  1089. filtAttr[i].lptype = isExtHdr.lptype;
  1090. strncpy(chanAttr[i].szLabel, isExtHdr.label, 16);
  1091. chanExt2Attr[i].anamax = isExtHdr.anamax;
  1092. chanExt2Attr[i].anamin = isExtHdr.anamin;
  1093. chanExt2Attr[i].digmax = isExtHdr.digmax;
  1094. chanExt2Attr[i].digmin = isExtHdr.digmin;
  1095. strncpy(chanExt2Attr[i].anaunit, isExtHdr.anaunit, 16);
  1096. }
  1097. hsize_t dims[1] = {1};
  1098. hid_t space_attr = H5Screate_simple(1, dims, NULL);
  1099. hid_t tid_sampling_attr = -1;
  1100. hid_t tid_filt_attr = -1;
  1101. uint32_t nChannelOffset = 0;
  1102. hid_t ptid_chan[cbNUM_ANALOG_CHANS];
  1103. {
  1104. hid_t gid_channel = -1;
  1105. if (H5Lexists(file, "channel", H5P_DEFAULT))
  1106. gid_channel = H5Gopen(file, "channel", H5P_DEFAULT);
  1107. else
  1108. gid_channel = H5Gcreate(file, "channel", H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
  1109. if (g_bAppend)
  1110. {
  1111. bool bExists = false;
  1112. // Find the last place to append to
  1113. do {
  1114. nChannelOffset++;
  1115. std::string strLabel = "channel";
  1116. char szNum[7];
  1117. sprintf(szNum, "%05u", nChannelOffset);
  1118. strLabel += szNum;
  1119. bExists = (H5Lexists(gid_channel, strLabel.c_str(), H5P_DEFAULT) != 0);
  1120. } while(bExists);
  1121. nChannelOffset--;
  1122. }
  1123. tid_sampling_attr = CreateSamplingAttrType(gid_channel);
  1124. tid_filt_attr = CreateFiltAttrType(gid_channel);
  1125. hid_t tid_chan_attr = CreateChanAttrType(gid_channel);
  1126. hid_t tid_chanext_attr = CreateChanExtAttrType(gid_channel);
  1127. hid_t tid_chanext2_attr = CreateChanExt2AttrType(gid_channel);
  1128. for (uint32_t i = 0; i < isHdr.cnChannels; ++i)
  1129. {
  1130. std::string strLabel = "channel";
  1131. {
  1132. uint32_t id = chanAttr[i].id + nChannelOffset;
  1133. char szNum[7];
  1134. sprintf(szNum, "%05u", id);
  1135. strLabel += szNum;
  1136. }
  1137. hid_t gid = -1;
  1138. if (H5Lexists(gid_channel, strLabel.c_str(), H5P_DEFAULT))
  1139. gid = H5Gopen(gid_channel, strLabel.c_str(), H5P_DEFAULT);
  1140. else
  1141. gid = H5Gcreate(gid_channel, strLabel.c_str(), H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
  1142. // Basic channel attributes
  1143. if (!H5Aexists(gid, "BmiChan"))
  1144. {
  1145. hid_t aid = H5Acreate(gid, "BmiChan", tid_chan_attr, space_attr, H5P_DEFAULT, H5P_DEFAULT);
  1146. ret = H5Awrite(aid, tid_chan_attr, &chanAttr[i]);
  1147. ret = H5Aclose(aid);
  1148. }
  1149. // Extra header attribute
  1150. if (!H5Aexists(gid, "BmiChanExt"))
  1151. {
  1152. hid_t aid = H5Acreate(gid, "BmiChanExt", tid_chanext_attr, space_attr, H5P_DEFAULT, H5P_DEFAULT);
  1153. ret = H5Awrite(aid, tid_chanext_attr, &chanExtAttr[i]);
  1154. ret = H5Aclose(aid);
  1155. }
  1156. // Additional extra channel attributes
  1157. if (!H5Aexists(gid, "BmiChanExt2"))
  1158. {
  1159. hid_t aid = H5Acreate(gid, "BmiChanExt2", tid_chanext2_attr, space_attr, H5P_DEFAULT, H5P_DEFAULT);
  1160. ret = H5Awrite(aid, tid_chanext2_attr, &chanExt2Attr[i]);
  1161. ret = H5Aclose(aid);
  1162. }
  1163. ret = H5Gclose(gid);
  1164. } // end for (uint32_t i = 0
  1165. ret = H5Tclose(tid_chanext2_attr);
  1166. ret = H5Tclose(tid_chanext_attr);
  1167. ret = H5Tclose(tid_chan_attr);
  1168. ret = H5Gclose(gid_channel);
  1169. }
  1170. // Now read data
  1171. fseeko(pFile, isHdr.nBytesInHdrs, SEEK_SET);
  1172. Nsx22DataHdr isDataHdr;
  1173. size_t nGot = fread(&isDataHdr, sizeof(Nsx22DataHdr), 1, pFile);
  1174. int setCount = 0;
  1175. if (nGot != 1)
  1176. {
  1177. printf("Invalid source file (cannot read data header)\n");
  1178. return 1;
  1179. }
  1180. do {
  1181. if (isDataHdr.nHdr != 1)
  1182. {
  1183. printf("Invalid data header in source file\n");
  1184. break;
  1185. }
  1186. if (isDataHdr.nNumDatapoints == 0)
  1187. {
  1188. printf("Data section %d with zero points detected!\n", setCount);
  1189. if (g_bSkipEmpty)
  1190. {
  1191. printf(" Skip this section and assume next in file is new data header\n");
  1192. nGot = fread(&isDataHdr, sizeof(Nsx22DataHdr), 1, pFile);
  1193. if (nGot != 1)
  1194. break;
  1195. continue;
  1196. } else {
  1197. printf(" Retrieve the rest of the file as one chunk\n"
  1198. " Last section may have unaligned trailing data points\n"
  1199. " Use --skipempty if instead you want to skip empty headers\n");
  1200. }
  1201. }
  1202. for (uint32_t i = 0; i < isHdr.cnChannels; ++i)
  1203. {
  1204. size_t chunk_size = CHUNK_SIZE_CONTINUOUS;
  1205. int compression = -1; // TODO: use options to add compression
  1206. char szNum[7];
  1207. std::string strLabel = "/channel/channel";
  1208. sprintf(szNum, "%05u", chanAttr[i].id + nChannelOffset);
  1209. strLabel += szNum;
  1210. hid_t gid = H5Gopen(file, strLabel.c_str(), H5P_DEFAULT);
  1211. // If need to go one level deeper
  1212. if (g_nCombine > 0)
  1213. {
  1214. hid_t gidParent = gid;
  1215. sprintf(szNum, "%05u", g_nCombine);
  1216. strLabel = szNum;
  1217. if (H5Lexists(gidParent, strLabel.c_str(), H5P_DEFAULT))
  1218. gid = H5Gopen(gidParent, strLabel.c_str(), H5P_DEFAULT);
  1219. else
  1220. gid = H5Gcreate(gidParent, strLabel.c_str(), H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
  1221. ret = H5Gclose(gidParent);
  1222. }
  1223. strLabel = "continuous_set";
  1224. if (setCount > 0)
  1225. {
  1226. sprintf(szNum, "%05u", setCount);
  1227. strLabel += szNum;
  1228. }
  1229. // We want to keep all data sets
  1230. while (H5Lexists(gid, strLabel.c_str(), H5P_DEFAULT))
  1231. {
  1232. setCount++;
  1233. strLabel = "continuous_set";
  1234. sprintf(szNum, "%05u", setCount);
  1235. strLabel += szNum;
  1236. }
  1237. ptid_chan[i] = H5PTcreate_fl(gid, strLabel.c_str(), H5T_NATIVE_INT16, chunk_size, compression);
  1238. hid_t dsid = H5Dopen(gid, strLabel.c_str(), H5P_DEFAULT);
  1239. ret = H5Gclose(gid);
  1240. // Add data start clock attribute
  1241. hid_t aid = H5Acreate(dsid, "StartClock", H5T_NATIVE_UINT32, space_attr, H5P_DEFAULT, H5P_DEFAULT);
  1242. uint32_t nStartTime = isDataHdr.nTimestamp;
  1243. ret = H5Awrite(aid, H5T_NATIVE_UINT32, &nStartTime);
  1244. ret = H5Aclose(aid);
  1245. // Add data sampling attribute
  1246. aid = H5Acreate(dsid, "Sampling", tid_sampling_attr, space_attr, H5P_DEFAULT, H5P_DEFAULT);
  1247. ret = H5Awrite(aid, tid_sampling_attr, &samplingAttr[i]);
  1248. ret = H5Aclose(aid);
  1249. // Add data filtering attribute
  1250. aid = H5Acreate(dsid, "Filter", tid_filt_attr, space_attr, H5P_DEFAULT, H5P_DEFAULT);
  1251. ret = H5Awrite(aid, tid_filt_attr, &filtAttr[i]);
  1252. ret = H5Aclose(aid);
  1253. ret = H5Dclose(dsid);
  1254. }
  1255. int count = 0;
  1256. int16_t anDataBufferCache[cbNUM_ANALOG_CHANS][CHUNK_SIZE_CONTINUOUS];
  1257. for (uint32_t i = 0; i < isDataHdr.nNumDatapoints || isDataHdr.nNumDatapoints == 0; ++i)
  1258. {
  1259. int16_t anDataBuffer[cbNUM_ANALOG_CHANS];
  1260. size_t nGot = fread(anDataBuffer, sizeof(int16_t), isHdr.cnChannels, pFile);
  1261. if (nGot != isHdr.cnChannels)
  1262. {
  1263. if (isDataHdr.nNumDatapoints == 0)
  1264. printf("Data section %d may be unaligned\n", setCount);
  1265. else
  1266. printf("Fewer data points (%u) than specified in data header (%u) at the source file!\n", i + 1, isDataHdr.nNumDatapoints);
  1267. break;
  1268. }
  1269. for (uint32_t j = 0; j < isHdr.cnChannels; ++j)
  1270. {
  1271. anDataBufferCache[j][count] = anDataBuffer[j];
  1272. }
  1273. count++;
  1274. if (count == CHUNK_SIZE_CONTINUOUS)
  1275. {
  1276. for (uint32_t j = 0; j < isHdr.cnChannels; ++j)
  1277. {
  1278. ret = H5PTappend(ptid_chan[j], count, &anDataBufferCache[j][0]);
  1279. }
  1280. count = 0;
  1281. }
  1282. } // end for (uint32_t i = 0
  1283. // Write out the remaining chunk
  1284. if (count > 0)
  1285. {
  1286. for (uint32_t i = 0; i < isHdr.cnChannels; ++i)
  1287. {
  1288. ret = H5PTappend(ptid_chan[i], count, &anDataBufferCache[i][0]);
  1289. }
  1290. }
  1291. // Close packet tables as we may open them again for paused files
  1292. for (uint32_t i = 0; i < isHdr.cnChannels; ++i)
  1293. {
  1294. ret = H5PTclose(ptid_chan[i]);
  1295. }
  1296. // Read possiblly more data streams
  1297. nGot = fread(&isDataHdr, sizeof(Nsx22DataHdr), 1, pFile);
  1298. setCount++;
  1299. } while (nGot == 1);
  1300. //
  1301. // We are going to call H5Close so no need to close what is open at this stage
  1302. //
  1303. return 0;
  1304. }
  1305. /////////////////////////////////////////////////////////////////////////////////////////////////
  1306. // Function main()
  1307. /////////////////////////////////////////////////////////////////////////////////////////////////
  1308. int main(int argc, char * const argv[])
  1309. {
  1310. herr_t ret;
  1311. int idxSrcFile = 1;
  1312. bool bForce = false;
  1313. bool bCache = true;
  1314. bool bCombine = false;
  1315. for (int i = 1; i < argc; ++i)
  1316. {
  1317. if (strcmp(argv[i], "--force") == 0)
  1318. {
  1319. bForce = true;
  1320. idxSrcFile++;
  1321. }
  1322. else if (strcmp(argv[i], "--nocache") == 0)
  1323. {
  1324. bCache = false;
  1325. idxSrcFile++;
  1326. }
  1327. else if (strcmp(argv[i], "--nospikes") == 0)
  1328. {
  1329. g_bNoSpikes = true;
  1330. idxSrcFile++;
  1331. }
  1332. else if (strcmp(argv[i], "--skipempty") == 0)
  1333. {
  1334. g_bSkipEmpty = true;
  1335. idxSrcFile++;
  1336. }
  1337. else if (strcmp(argv[i], "--append") == 0)
  1338. {
  1339. g_bAppend = true;
  1340. idxSrcFile++;
  1341. }
  1342. else if (strcmp(argv[i], "--combine") == 0)
  1343. {
  1344. if (i + 1 >= argc || !isdigit(argv[i + 1][0]))
  1345. {
  1346. printf("Combine level not specified or is invalid\n");
  1347. idxSrcFile = argc; // Just to show the usage
  1348. break;
  1349. }
  1350. g_nCombine = atoi(argv[i + 1]);
  1351. bCombine = true;
  1352. idxSrcFile += 2;
  1353. i++;
  1354. }
  1355. else if (strcmp(argv[i], "--group") == 0)
  1356. {
  1357. // TODO: implement
  1358. printf("Group addition is not implemented in this version!\n");
  1359. return 0;
  1360. }
  1361. }
  1362. if (idxSrcFile >= argc)
  1363. {
  1364. printf("Blackrock file conversion utility (version 1.0)\n"
  1365. "Usage: n2h5 [options] <srcfile> [<destfile>]\n"
  1366. "Purpose: Converts srcfile to destfile\n"
  1367. "Inputs:\n"
  1368. "<srcfile> - the file to convert from (nev or nsx format)\n"
  1369. "<destfile> - the converted file to create (hdf5 format)\n"
  1370. " default is <srcfile>.bh5\n"
  1371. "Options:\n"
  1372. " --force : overwrites the destination if it exists, create if not\n"
  1373. " --nocache : slower but results in smaller file size\n"
  1374. " --nospikes : ignore spikes\n"
  1375. " --skipempty: skip 0-sized headers (instead of ignoring them)\n"
  1376. " --combine <level> : combine to the existing channels at given subchannel level (level 0 means no subchannel)\n"
  1377. " same experiment, same channels, different data sets (e.g. different sampling rates or filters)\n"
  1378. " --append : append channels to the end of current channels\n"
  1379. " same experiment, different channel (e.g. sync systems recording)\n"
  1380. " --group : add as new group\n"
  1381. " different experiments\n");
  1382. return 0;
  1383. }
  1384. // TODO: implement --append for video and comment data
  1385. // TODO: implement --group
  1386. const char * szSrcFile = argv[idxSrcFile];
  1387. std::string strDest;
  1388. if ((idxSrcFile + 1) >= argc)
  1389. {
  1390. strDest = szSrcFile;
  1391. strDest += ".bh5";
  1392. } else {
  1393. strDest = argv[idxSrcFile + 1];
  1394. }
  1395. const char * szDstFile = strDest.c_str();
  1396. char achFileID[8];
  1397. FILE * pFile = fopen(szSrcFile, "rb");
  1398. if (pFile == NULL)
  1399. {
  1400. perror("Unable to open source file for reading");
  1401. return 0;
  1402. }
  1403. if (H5open())
  1404. {
  1405. fclose(pFile);
  1406. printf("cannot open hdf5 library\n");
  1407. return 0;
  1408. }
  1409. hid_t file;
  1410. hid_t facpl = H5P_DEFAULT;
  1411. if (g_bAppend || bCombine)
  1412. {
  1413. // Open read-only just to validate destination file
  1414. file = H5Fopen(szDstFile, H5F_ACC_RDONLY, H5P_DEFAULT);
  1415. if (file < 0 && !bForce)
  1416. {
  1417. printf("Cannot append to the destination file or destiantion file does not exist\n"
  1418. "Use --force to to ignore this error\n");
  1419. goto ErrHandle;
  1420. }
  1421. H5Fclose(file);
  1422. }
  1423. if (bCache)
  1424. {
  1425. double rdcc_w0 = 1; // We only write so this should work
  1426. facpl = H5Pcreate(H5P_FILE_ACCESS);
  1427. // Useful primes: 401 4049 404819
  1428. ret = H5Pset_cache(facpl, 0, 404819, 4 * 1024 * CHUNK_SIZE_CONTINUOUS, rdcc_w0);
  1429. }
  1430. if (g_bAppend || bCombine)
  1431. {
  1432. file = H5Fopen(szDstFile, H5F_ACC_RDWR, H5P_DEFAULT);
  1433. } else {
  1434. file = H5Fcreate(szDstFile, bForce ? H5F_ACC_TRUNC : H5F_ACC_EXCL, H5P_DEFAULT, facpl);
  1435. }
  1436. if (facpl != H5P_DEFAULT)
  1437. ret = H5Pclose(facpl);
  1438. if (file < 0)
  1439. {
  1440. if (g_bAppend || bCombine)
  1441. printf("Cannot open the destination file or destination file does not exist\n"
  1442. "Use --force to create new file\n");
  1443. else
  1444. printf("Cannot create destination file or destiantion file exists\n"
  1445. "Use --force to overwite the file\n");
  1446. goto ErrHandle;
  1447. }
  1448. fread(&achFileID, sizeof(achFileID), 1, pFile);
  1449. // NEV file
  1450. if (0 == strncmp(achFileID, "NEURALEV", sizeof(achFileID)))
  1451. {
  1452. if (ConvertNev(pFile, file))
  1453. {
  1454. printf("Error in ConvertNev()\n");
  1455. goto ErrHandle;
  1456. }
  1457. }
  1458. // 2.1 filespec
  1459. else if (0 == strncmp(achFileID, "NEURALSG", sizeof(achFileID)))
  1460. {
  1461. if (ConvertNSx21(szSrcFile, pFile, file))
  1462. {
  1463. printf("Error in ConvertNSx21()\n");
  1464. goto ErrHandle;
  1465. }
  1466. }
  1467. // 2.2 filespec
  1468. else if (0 == strncmp(achFileID, "NEURALCD", sizeof(achFileID)))
  1469. {
  1470. if (ConvertNSx22(pFile, file))
  1471. {
  1472. printf("Error in ConvertNSx22()\n");
  1473. goto ErrHandle;
  1474. }
  1475. } else {
  1476. printf("Invalid source file format\n");
  1477. }
  1478. ErrHandle:
  1479. if (pFile)
  1480. fclose(pFile);
  1481. if (file > 0)
  1482. H5Fclose(file);
  1483. H5close();
  1484. return 0;
  1485. }