Back to the main page.

Bug 2964 - add support for blackrock data

Status CLOSED FIXED
Reported 2015-09-21 14:32:00 +0200
Modified 2021-09-06 11:11:02 +0200
Product: FieldTrip
Component: external
Version: unspecified
Hardware: Macintosh
Operating System: Mac OS
Importance: P5 normal
Assigned to: Jan-Mathijs Schoffelen
URL: https://www.fieldtriptoolbox.org/tag/blackrock/
Tags:
Depends on:
Blocks:
See also:

Jan-Mathijs Schoffelen - 2015-09-21 14:32:27 +0200

blackrock microsystems is the follow-up of cyberkinetics. FieldTrip seem to have some native support for (some of) this fileformat, but it appears windows (i.e. platform) specific. Nowadays, blackrock has a version-controlled matlab-based toolbox (hosted on git) that seems platform independent. JM is going to implement support for this using the external code. He has been in touch with the blackrock people, and they have pointed him to some example data, which JM will put on /home/common/matlab/fieldtrip for future reference and testing. @Saskia: the cyberkinetics support seems to have been implemented by you, but the code mentions here and there that it is still work in progress. Are you still working on this? @Robert: you may have some other example data around, which you promised to locate.


Robert Oostenveld - 2015-09-22 10:27:12 +0200

I came across http://www.fieldtriptoolbox.org/getting_started/cyberkinetics and http://www.fieldtriptoolbox.org/development/read_neuroshare Could you keep it up to date when you make changes? PS neuroshare is a dead end, so the development project can be closed


Jan-Mathijs Schoffelen - 2015-09-22 14:03:27 +0200

Sure!


Jan-Mathijs Schoffelen - 2015-09-24 14:11:30 +0200

progress report: -created page getting_started/blackrock -unlinked getting_started/cyberkinetics and linked to blackrock in case users end up there anyway -added support for reading header info from LFP files in ft_read_header.m (+updated ft_filetype and ft_hastoolbox).


liang wang - 2015-11-26 14:34:34 +0100

Hi Fieldtrip folks I am wondering if Fieldtrip has updated codes for converting Blackrock data to a compatible format for analysis. Currently I met a problem using Neuroshare library (particular for mexprog.dll). If you have one (even a beta version), could you please send me a OSX 64bit version. Thanks a lot. Best, Liang


- 2016-02-11 00:13:16 +0100

Hey Fieldtrippers, hey JM, I guess we got this to work. It needs some edits in ft_read_data and ft_read_event. I worked on Windows 64 with Matlab 64 and fieldtrip-20160128. I only concentrated on the LFP data (blackrock_nsx) and used the Spike data (blackrock_nev) only to get the event time stamps. Here are my edits: 1) ft_read_data This is almost fully implemented for the 'blackrock_nsx'. The existing versions already makes use of the low-level functions of the NPMK toolbox. From line 1280: orig = openNSx(filename, 'duration', [begsample endsample]); and then simply insert: dat = double(orig.Data); This works great for segmented data (ft_definetrial first). For the entire continuous recording I ran into "Out of Memory" issues. This can be circumvented by reading groups of channels in a for-loop but still takes pretty long. % chan_groups=ceil(linspace(1,hdr.nChans+1,10)); % for iter=1:numel(chan_groups)-1 % %tmp=openNSx(filename,'channels',chan_groups(iter):chan_groups(iter+1)-1, 'duration', [begsample endsample]); % dat(chan_groups(iter):chan_groups(iter+1)-1,:)=double(tmp.Data); % end 2) ft_read_header I think this was already up to date. From line 2121 it should read: case 'blackrock_nsx' ft_hastoolbox('NPMK', 1); % ensure that the filename contains a full path specification, % otherwise the low-level function fails [p,f,e] = fileparts(filename); if ~isempty(p) % this is OK elseif isempty(p) filename = which(filename); end orig = openNSx(filename, 'noread'); hdr = []; hdr.orig = orig; hdr.Fs = orig.MetaTags.SamplingFreq; hdr.nChans = orig.MetaTags.ChannelCount; hdr.nSamples = orig.MetaTags.DataPoints; hdr.nSamplesPre = 0; hdr.nTrials = 1; hdr.label = deblank({orig.ElectrodesInfo.Label})'; hdr.chanunit = deblank({hdr.orig.ElectrodesInfo.AnalogUnits})'; 3) ft_read_event I inserted a case to read the event markers from the .nev file: case {'blackrock_nev', 'blackrock_nsx'} % use the NPMK toolbox for the file reading ft_hastoolbox('NPMK', 1); % this prevents read of spike waveform & automatic save of .nev file to .mat file orig = openNEV(filename, 'noread', 'nosave'); if orig.MetaTags.SampleRes ~= 30000 error('sampling rate is different from 30 kHz') end % here one has to account for different sampling frequencies eventCodeTimes = double((orig.Data.SerialDigitalIO.TimeStamp)./(orig.MetaTags.SampleRes/1000)); eventCodes = double(orig.Data.SerialDigitalIO.UnparsedData); % probably not necessary for all but we often have pins up eventCodes2= eventCodes-min(eventCodes)+1; for k=1:numel(eventCodes2) event(k).type = 'trigger'; event(k).sample = eventCodeTimes(k); event(k).value = eventCodes2(k); event(k).duration = 1; event(k).offset = []; end 4) I wrote my own trial function because header and triggers come from different files (header: .nsx file, triggers from .nev file). The rest is standard: cfg=[]; cfg.dataset='file.nsx'; cfg.eventfile='file.nev'; function trl=blackrock_to_ft_trialfun(cfg) hdr=ft_read_header(cfg.dataset); event=ft_read_event(cfg.eventfile); trl=[]; for i=1:length(event) if strcmp(event(i).type, cfg.trialdef.eventtype) % it is a trigger, see whether it has the right value if ismember(event(i).value, cfg.trialdef.eventvalue) % add this to the trl definition begsample = event(i).sample - cfg.trialdef.prestim*hdr.Fs; endsample = event(i).sample + cfg.trialdef.poststim*hdr.Fs - 1; offset = -cfg.trialdef.prestim*hdr.Fs; trigger = event(i).value; % remember the trigger (=condition) for each trial trl(end+1, :) = [round([begsample endsample offset]) trigger]; end end end I believe there is a more convenient solution to this. An important additional edit would take care of the different sampling frequencies for the different .nsx-files. I worked with .ns2 which samples with 1kHz. I hope this helps! Andi


Jan-Mathijs Schoffelen - 2016-02-12 10:03:47 +0100

(In reply to anwutz from comment #5) Hi Andi, I have created a blackrock branch on my fork of the fieldtrip repository on Github :o). (github.com/schoffelen/fieldtrip) I don't know the extent to which you are familiar with git (I am not much familiar with it, we have just moved over from svn to git, so I am still on the steep end of the learning curve). Anyway, it should be possible for you to clone this particular branch so that you can contribute directly. I believe the best way to go about it would be that suggested changes by you can be put in my branch by a 'pull request', and once we are both happy with how it works, we can push it to fieldtrip. This being said, I have looked at your suggestions, and I am happy with the change to ft_read_data (which I have included (in a slightly revised version) in the git-branch. Bottome line: I suggest to leave the looping over channels to a higher level, i.e. based on the decision of the user, who can specify a subset of channels to be read in cfg.channel, prior to calling ft_preprocessing. With respect to ft_read_event, I think we need to try and make the code more generic. I was playing around with some testdata I got from the blackrock people, and in combination with the documentation about the file format, I don't understand why you have to correct the offset (and create the variable eventCodes2). The way I see it (in the testdata I used), is that there are specific events with value 65535 65534 etc that have a particular meaning, referring to comments, tracking info etc (whatever that means). Also, I guess that if spike data has been recorded, the spiking events are also represented in the 'orig' data structure. The bottom line is, that for ft_read_event I would like to see some intelligence in distinguishing between the different things: -spike timings -> these should NOT go into the event structure -special events 65535 65534 etc, these might go into the event structure, but this can probably be accompanied with some more information (e.g. the text of a comment or the type of the tracking event) -experimental triggers and button presses, these should go into the event structure, and may need to be offset corrected. What are your thoughts on this?


Robert Oostenveld - 2016-02-15 09:30:31 +0100

(In reply to Jan-Mathijs Schoffelen from comment #6) Hi JM and Andi, Different sampling rates are annoying, as they don't fit well into the original design philosophy. I don't know the blackrock details, but want to share two considerations: 1) Use ft_read_header/data and ft_preprocessing on each file separately, i.e. don't treat a collection of nix files as a "dataset", but simply treat one file at a time and use ft_appenddata. The disadvantage is that trl/samples in ft_definetrial don't index clearly into the combined data. 2) look at the EDF implementation. EDF consists of a single file that can contain channels with different sampling rates. Regarding looping over channels instead over trials, that is supported in ft_preprocessing with the cfg.method option.


- 2016-02-16 00:27:56 +0100

Dear JM and others in the list, I have looked into your git-branch and in particular into ft_read_event using our data here. Here are some general specifics of the blackrock system with regard to sampling frequencies. Further below, I'll try to provide some more details to your suggestions/ questions about spikes/ events. A) Sampling frequencies Blackrock data comes in two different file formats a) .nev (sampled at 30 kHz) contains the events and spikes (more on this below) b) .nsx - contains the LFP and are analog filtered by the Amplifier (cut-off: 0.3 - 7.5 kHz). These files can come in 6 different formats depending on the sampling frequency 1) .ns1 - 500 Hz 2) .ns2 - 1 kHz 3) .ns3 - 2 kHz 4) .ns4 - 10 kHz 5) .ns5 - 30 kHz all of these can also be digitally filtered. We checked with the guys from blackrock about this. They apply a Butterworth filter only in the forward direction (I guess this means phase distortions). This leaves us with 6) .ns6 - 30 kHz raw data (no digital filter) Bottom line: The different sampling frequencies can be dissociated based on the file format (.ns1 - .ns6). Usually, only one of these different file formats will be analysed and one would often start off with the .ns6 files anyways to avoid the phase shift. 2) Spikes/ Events a) Correcting for the offset If in your example data the events that you send are exactly those that your record, then never mind about correcting the offset. I just joined the group here but I noticed that the trigger codes that should be sent are offset by some pins being up at the time of recording. In other words, my proposed correction would make sure that the sent/recorded codes match (sent code "1" = recorded code "1"). B) Specific events In my data, I don't have these specific events referring to comments, tracking info, etc. So it's hard for me to implement an efficient method that dissociates these codes. However, I do want to point to the following distinction: After: orig = openNEV(filename, 'noread', 'nosave'); orig.Data has the following fields: SerialDigitalIO: [1x1 struct] Spikes: [1x1 struct] Comments: [1x1 struct] VideoSync: [1x1 struct] Tracking: [] TrackingEvents: [1x1 struct] PatientTrigger: [1x1 struct] Reconfig: [1x1 struct] The experimental codes are stored in "SerialDigitalIO", the Spikes are stored in "Spikes" and so on. So, I guess the different event reasons (sent triggers, Spikes, Comments, etc.) are already dissociated in the orig data structure. Referring only to SerialDigitalIO will hence only give us the experimental event codes we want. Am I missing something here? In my opinion, it would be valuable to include the Spike-TimeStamps in the Event structure (maybe in a separate field?) for Spike-triggered analysis. The issue here is that Spike-Timing and Spike-Electrode are stored in two separate fields: orig.Data.Spikes.TimeStamp orig.Data.Spikes.Electrode Maybe a No_Electrode x No_Samples - Boolean with Spike events (1) and else (0) was a good thing to have here? C) One question about one of your changes: fs = orig.MetaTags.SampleRes; eventCodeTimes = double(timestamps)./double(fs); % express in seconds which was before expressed in samples with 1000 Hz frequency: double((orig.Data.SerialDigitalIO.TimeStamp)./(orig.MetaTags.SampleRes/1000)); I did this in order to account for the different sampling frequencies between .nev (30 kHz) and .ns2 file (1 kHz). How do I account for consistency between files with different sampling frequencies if we express the timestamps in seconds (not samples)? On a similar note, the expression of TimeStamps in Seconds (not Samples) is already present in the orig data structure under: orig.Data.SerialDigitalIO.TimeStampSec I can try to implement more generic code in future. In my view, accounting for Spikes and different sampling frequencies are the most urgent modifications. Best, Andi


Jan-Mathijs Schoffelen - 2016-12-23 09:39:27 +0100

Hi Andi, I would like to see this carried forward, but at the moment don't have much time to spend on it myself. Have there been some new insights, new code on your end which we could try and incorporate into FieldTrip? We could work of the blackrock branch on my FT fork (but let me know before you start working on this, so that I can make an update pull with respect to FT-master (otherwise we may end up with a bunch of conflicts in the end, which I'd rather solve now, than later).


Jan-Mathijs Schoffelen - 2016-12-23 09:40:18 +0100

As a general comment: I addressed my previous comment to Andi, but if other people on the CC-list of this bug have code/insights to share, feel free to do so.


- 2017-07-01 16:29:29 +0200

(In reply to Jan-Mathijs Schoffelen from comment #10) Hi Jan-Mathijs, hi all, first of all many apologies for my very, very long response time for this one. Unfortunately the bugzilla was still linked to an old email address that I only check once in a while. Going through my ~1600 unread emails, I found your comment. On the other hand, the timing is not so bad, because workload seems a little lighter on my side and I will likely find some time in the next 1-2 weeks to look over this again. I will see in which terms my current code corresponds with what we already have. The reading-in is working for me but you mentioned you wanted to have this implemented in a more generic way. I will likely need some guidance from your side. Best, Andi


Jan-Mathijs Schoffelen - 2017-07-04 22:31:00 +0200

(In reply to awutz from comment #11) OK, I suggest that you make an inventory of what might not be working for you, or what you think might be relevant functionality that people might need. I kind of lost track of this myself, and there's no priority for me to work on this now. I'd be happy to assist though. One possible outcome could be, that we verify that the current state of the code has some basic functionality, that we document what is not yet there, and leave it at that. PS: we have moved our code repository over to github: github.com/fieldtrip/fieldtrip.git The idea behind this is that it would make collaborations easier (e.g. we could work on code in a separate branch).


- 2017-07-06 20:01:44 +0200

Hi Jan-Mathijs, hi all, I am not so familiar with GitHub, so I would need a word (or two) explaining how to work there. Also, I can only provide edits for reading in LFP data. I have not looked into Spikes yet, maybe I can contribute here later. Blackrock data comes with two files: .NEV and .NSx .NEV contain the trigger time stamps at 30K Hz sampling .NSx contain the data with different sampling frequencies (depending on file type between 500 Hz-30k Hz) In my view, edits would have to be made for 1) ft_read_data (reads in data from .NSx files; was already approved (see above) but I am not sure if it is implemented yet) 2) ft_read_event (reads trigger/event info from .NEV file) I edited ft_read_event to output time stamps in seconds. The mapping to the data with different sampling frequencies is taken care of later when calling ft_definetrial with a blackrock_to_ft_trialfun. I also try to add some functionality to dissociate different trigger reasons (Comments, Spikes, etc.). There is a field called InsertionReason. I am not sure yet that this is what we need. In my data, there is only the value '1'. But I also have only triggers in my data (no spikes, comments). I will check back with other people in my lab and will update in case. Anyways, this field is copied into the event output as event.type. 3) blackrock_to_ft_trialfun I can contribute this trial function.


Jan-Mathijs Schoffelen - 2017-07-07 09:50:55 +0200

(In reply to awutz from comment #13) Hi Andi, Here's a link that should get you started. http://www.fieldtriptoolbox.org/development/git I am also still learning, but once you get the hang of it, git is a powerful tool, which keeps the development clean. It took me some time this morning, but I had an old (sleeping) branch on my own fieldtrip fork, called blackrock, which contained some of the changes you mentioned in your previous comment. Particularly, it has some code in ft_read_data that should support the NsX files, and there's some stuff in ft_read_event. In particular I am not sure about the correctness of the latter. I think it would be great if you could have a look at all this. The recipe to follow would be: -create an account on github.com (if you don't have one already) -fork the fieldtrip repository from github.com/fieldtrip/fieldtrip.git -clone your fork to a computer that you use to work on -pull the blackrock branch from my fieldtrip fork (i.e. github.com/schoffelen/fieldtrip.git) -start playing.


- 2017-07-09 18:26:11 +0200

Hi Jan-Mathijs, I followed your instructions for git until the pull-request. Is there an approval from your side needed at this point? I can't get any further (i.e. I cannot pull the blackrock branch). I am using SourceTree to interface with git.


Jan-Mathijs Schoffelen - 2018-03-10 22:06:54 +0100

Anybody on the CC-list to this bug. What's the status, and what needs to be done in order to finalize this?


Jan-Mathijs Schoffelen - 2018-03-15 10:50:35 +0100

Closing for now, due to perceived lack of interest and priority by people on the CC list. Feel free to constructively reopen.


Jan-Mathijs Schoffelen - 2018-11-12 10:55:00 +0100

closing time. Feel free to reopen once needed


Zhu - 2021-09-06 09:44:21 +0200

Created attachment 886 ft_read cann't read Blackrock NEV file. mexprog didn't defined. Hi,I'm a new comer on matlab and data analysis. Nowadays, I found one paper said FieldTrip is compatible for Blackrock data. So I tried. I used NEV data, and wrote as the website (https://www.fieldtriptoolbox.org/getting_started/blackrock/). But it failed. There was a hint "mexprog hasn't defined". And it didn't successfully call the NPMK toolbox,the NEV file hadn't been read.(NPMK toolbox added to path) If I loaded Mat file,there were NEV in workspace.But failed either, and "mexprog hasn't defined". So,how to solve this? I skipped some important process or the data has to transform to Neuroshare format or else? (script as following) clear; clc; close all; addpath D:\Action\Analysis\Toolbox\fieldtrip-20210904\fieldtrip-20210904\ ft_defaults filename = 'D:\Action\Analysis\cell\sorted\m06cat003.nev'; hdr = ft_read_header(filename, 'headerformat', 'neuroshare'); spike = ft_read_spike(filename, 'spikeformat', 'neuroshare');


Robert Oostenveld - 2021-09-06 10:07:01 +0200

Dear Zhu Blackrock has multiple file formats with different versions as detailed on https://github.com/BlackrockNeurotech/NPMK/blob/master/NPMK/Users%20Guide.pdf Neuroshare (see http://neuroshare.sourceforge.net) has not been updated or maintained since about 10 years and I don't expect it to work on recent (64 bit) computers and/or MATLAB versions. I will update the online documentation and remove neuroshare as to not confuse people with outdated instructions. David Kluger from Blackrock wrote us by email some time ago: > Thanks for following up. I read the thread. As we are also unaware of who the end user will be and what they intend to do with FieldTrip/Blackrock products, we are uncertain of the development on our end that will be needed. If a user comes to you asking for further Blackrock support, perhaps we can delegate some development time based on this request. Feel free to notify us if our services could be useful, preferably through an email to support@blackrockmicro.com. To move forward with this, I suggest you contact support@blackrockmicro.com and ask them to help you.


Robert Oostenveld - 2021-09-06 10:32:24 +0200

I have updated the website, see https://github.com/fieldtrip/website/commit/453ce21efe480b42bab6ba36bad6f67ebb3d633d and the two relevant pages on the website on https://www.fieldtriptoolbox.org/tag/blackrock/


Robert Oostenveld - 2021-09-06 10:55:51 +0200

I had a quick look at the .nev file format and the NPMK toolbox. The openNEV function returns the data in a format that strongly resembles the FieldTrip spike representation, so I added support for it in https://github.com/fieldtrip/fieldtrip/commit/249cf2105e7d983fc72a5cebdb45e509fb375e6e it will be included in the next release, but you can already try it out from the master branch.


Robert Oostenveld - 2021-09-06 11:11:02 +0200

It turned out that the blackrock_nsx format was already supported in the corresponding code. I have now added both spikes and LFPs to the documentation overview on https://www.fieldtriptoolbox.org/getting_started/blackrock/