Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
41de256
Make full use of the .prm file in kilosort import
Sep 19, 2020
fa6e381
adds trial file and PSTH parameters during bootstrap if file is present
Sep 19, 2020
32c08fe
temp solution for saveRes
Oct 24, 2020
92422ec
added bootstrapNDI, ndiRecording object, both seem to work well
stevevanhooser Oct 28, 2020
1ba0024
detection, bootstrapping working, partially tested
stevevanhooser Oct 29, 2020
f61a62a
ndi update almost done
stevevanhooser Nov 5, 2020
ada6b30
path to parameter file
Nov 5, 2020
5e12780
export to NDI finished, undergoing testing
stevevanhooser Nov 6, 2020
96600d9
ndi updates, also adding neuron_extracellular doc
stevevanhooser Nov 13, 2020
6402ccb
ndi export added, bootstrap docs added
stevevanhooser Nov 13, 2020
dd1f530
update README.md
Feb 26, 2021
ff9556c
merged kilosort changes that use dataTypeRaw and dataTypeExtracted
stevevanhooser Jul 12, 2021
a0453fd
added unit test for ndi bootstrapping, detection
stevevanhooser Jul 13, 2021
b6944b5
fixed bug in adding multiple directories in ndi
stevevanhooser Feb 13, 2022
67873dd
updates to NDI export
stevevanhooser Apr 12, 2022
a991f24
added message that cells without notes will be skipped
stevevanhooser Apr 15, 2022
e6bd01c
fixed ignoring a directory
stevevanhooser Sep 20, 2022
05e6bf4
update for ndi2
stevevanhooser Apr 26, 2023
18ed885
fix to add session id to neurons
stevevanhooser Sep 11, 2023
41fcc9f
updates for allowing configuration parameters to influence data reading
stevevanhooser May 1, 2024
12dc18d
update for new document specification
stevevanhooser Apr 15, 2025
aae0ccc
update for new document specification
stevevanhooser Apr 15, 2025
b16dc6a
update small problem in ndiRecording
stevevanhooser Jul 10, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions +jrclust/+detect/@DetectController/detectOneRecording.m
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
end

% load raw samples
iSamplesRaw = hRec.readRawROI(obj.hCfg.siteMap, 1+loadOffset:loadOffset+nSamples);
iSamplesRaw = hRec.readRawROI(obj.hCfg.siteMap, 1+loadOffset:loadOffset+nSamples,obj.hCfg);

% convert samples to int16
iSamplesRaw = samplesToInt16(iSamplesRaw, obj.hCfg);
Expand All @@ -52,7 +52,7 @@
if iLoad < nLoads && obj.hCfg.nSamplesPad > 0
leftBound = loadOffset + nSamples + 1;
rightBound = leftBound + obj.hCfg.nSamplesPad - 1;
samplesPost = hRec.readRawROI(obj.hCfg.siteMap, leftBound:rightBound);
samplesPost = hRec.readRawROI(obj.hCfg.siteMap, leftBound:rightBound,obj.hCfg);
samplesPost = samplesToInt16(samplesPost, obj.hCfg);
else
samplesPost = [];
Expand Down Expand Up @@ -135,7 +135,7 @@
% if in import mode, only process loads where there are imported spikes.
if isempty(impTimes) || any(inInterval)
% load raw samples
iSamplesRaw = hRec.readRawROI(obj.hCfg.siteMap, 1+loadOffset:loadOffset+nSamples);
iSamplesRaw = hRec.readRawROI(obj.hCfg.siteMap, 1+loadOffset:loadOffset+nSamples,obj.hCfg);

% convert samples to int16
iSamplesRaw = samplesToInt16(iSamplesRaw, obj.hCfg);
Expand All @@ -144,7 +144,7 @@
if iLoad < nLoads && obj.hCfg.nSamplesPad > 0
leftBound = loadOffset + nSamples + 1;
rightBound = leftBound + obj.hCfg.nSamplesPad - 1;
samplesPost = hRec.readRawROI(obj.hCfg.siteMap, leftBound:rightBound);
samplesPost = hRec.readRawROI(obj.hCfg.siteMap, leftBound:rightBound,obj.hCfg);
samplesPost = samplesToInt16(samplesPost, obj.hCfg);
else
samplesPost = [];
Expand Down
4 changes: 2 additions & 2 deletions +jrclust/+detect/@DetectController/findSecondaryPeaks.m
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
function [spikeSites2, spikeSites3] = findSecondaryPeaks(obj, spikeWindows, spikeSites)
%FINDSECONDARYPEAKS Find secondary peaks
% spikeWindows: nSamples x nSites x nSpikes
evtNeighbors = 2:obj.hCfg.nSitesEvt; % sites within merge radius, excluding ref sites
evtNeighbors = 2:obj.hCfg.nSitesEvt; % sites within merge radius, excluding ref sites
siteNeighbors2 = obj.hCfg.siteNeighbors(evtNeighbors, spikeSites);

spikeWindows2 = spikeWindows(:, evtNeighbors, :);
Expand All @@ -22,4 +22,4 @@
else
spikeSites3 = [];
end
end
end
105 changes: 105 additions & 0 deletions +jrclust/+detect/ndiRecording.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
classdef ndiRecording < jrclust.interfaces.RawRecording
%NDIRECORDING Model of an element in NDI
%% NDI-SPECIFIC PROPERTIES

properties (GetAccess=public,SetAccess=private)
S; % ndi.session.dir that holds the experimental session data
E; % the ndi.element object that holds the data
epoch_id; % the epoch ids that are included
nsamples; % the number of samples per epoch
t0_t1; % start and end times, in local device time, of each epoch
end;

%% LIFECYCLE
methods
function obj = ndiRecording(epochname, hCfg)
%NDIRECORDING Construct an instance of this class
% set object data type
obj = obj@jrclust.interfaces.RawRecording(epochname, hCfg);
% file not found error is not an error for ndi
obj.errMsg = '';
obj.isError = 0;
obj.S = ndi.session.dir(hCfg.ndiPath);
E = getelements(obj.S,'element.name',hCfg.ndiElementName,'element.reference',hCfg.ndiElementReference);
if iscell(E) & numel(E)==1,
obj.E = E{1};
else,
obj.isError = 1;
obj.errMsg = 'No such element found.';
return;
end;

try
obj.hCfg = hCfg;
catch ME
obj.errMsg = ME.message;
obj.isError = 1;
return;
end

et = epochtable(obj.E);

[parentdir,epoch_id] = fileparts(epochname);
match = find(strcmp(epoch_id,{et.epoch_id}));
if isempty(match),
obj.isError = 1;
obj.errMsg = ['No such epoch ' epoch_id '.'];
return;
end;
obj.epoch_id = et(match).epoch_id;
% find clock that is dev local time
foundMatch = 0;
for i=1:numel(et(match).epoch_clock),
if strcmp(et(match).epoch_clock{i}.type,'dev_local_time'),
foundMatch = i;
break;
end;
end;
if foundMatch,
obj.t0_t1 = et(match).t0_t1{foundMatch};
else,
error(['No clock type ''dev_local_time''.']);
end;
obj.nsamples = 1+diff(times2samples(obj.E,epoch_id,obj.t0_t1));
obj.dshape = [hCfg.nChans, obj.nsamples];
mksqlite('close');
end % ndiRecording()

function openRaw(obj)
%OPENRAW Open the raw recording file for reading - does nothing for ndiRecording
obj.rawIsOpen = 1;
end
function closeRaw(obj)
%CLOSERAW Close the raw recording file for reading - does nothing for ndiRecording
obj.rawIsOpen = 0;
end

function roi = readRawROI(obj, rows, cols,hCfg)
%READRAWROI Get a region of interest by rows/cols from the raw file
t0t1 = samples2times(obj.E, obj.epoch_id, cols([1 end]));
roi = hCfg.ndiScale*readtimeseries(obj.E, obj.epoch_id, t0t1(1), t0t1(2));
roi = roi'; % switch to column-based samples
roi = single(roi(rows,:)); % if only a subset requested, return only the subset
end % readRawROI()


end % methods

%% GETTERS/SETTERS
methods
function set.S(obj, val)
if ~isa(val,'ndi.session.dir'),
error(['S must be of type ndi.session.dir']);
end;
obj.S = val;
end
function set.E(obj,val)
if ~isa(val,'ndi.element') & ~isa(val,'ndi.time.timeseries'),
error(['E must be an ndi.element and an ndi.time.timeseries']);
end;
obj.E = val;
end;

end
end

4 changes: 2 additions & 2 deletions +jrclust/+detect/newRecording.m
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
switch hCfg.recordingFormat
case 'Intan'
hRec = jrclust.detect.IntanRecording(rawPath, hCfg);

case 'ndi',
hRec = jrclust.detect.ndiRecording(rawPath, hCfg);
otherwise % SpikeGLX .bin/.dat
hRec = jrclust.detect.Recording(rawPath, hCfg);

end
end

Loading