Skip to content

Commit

Permalink
correct gain for channels - spikeGLX
Browse files Browse the repository at this point in the history
  • Loading branch information
Julie-Fabre committed Nov 20, 2024
1 parent 7468ee8 commit f8ecb67
Show file tree
Hide file tree
Showing 17 changed files with 85 additions and 358 deletions.
36 changes: 20 additions & 16 deletions +bc/+dependencies/SGLX_readMeta.m
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
% Simple class of MATLAB functions deomonstrating
% how to read and manipulate SpikeGLX meta and binary files.
% When this file is included in the MATLAB path, the individual methods
% are called with SGLX_readMeta.<function name>
% are called with bc.dependencies.SGLX_readMeta.<function name>
%
% The most important method in the class is ReadMeta().
% The functions for reading binary data are not optimized for speed,
Expand Down Expand Up @@ -34,13 +34,16 @@
% utility functions below.
%
function [meta] = ReadMeta(binName, path)

if nargin < 2
full_path_meta = binName;
else
% Create the matching metafile name
[~,name,~] = fileparts(binName);
metaName = strcat(name, '.meta');

full_path_meta = fullfile(path, metaName);
end
% Parse ini file into cell entries C{1}{i} = C{2}{i}
fid = fopen(fullfile(path, metaName), 'r');
fid = fopen(full_path_meta, 'r');
% -------------------------------------------------------------
% Need 'BufSize' adjustment for MATLAB earlier than 2014
% C = textscan(fid, '%[^=] = %[^\r\n]', 'BufSize', 32768);
Expand Down Expand Up @@ -101,7 +104,7 @@
% Get channel index of requested digital word dwReq
switch meta.typeThis
case 'imec'
[AP, LF, SY] = SGLX_readMeta.ChannelCountsIM(meta);
[AP, LF, SY] = bc.dependencies.SGLX_readMeta.ChannelCountsIM(meta);
if SY == 0
fprintf('No imec sync channel saved\n');
digArray = [];
Expand All @@ -110,7 +113,7 @@
digCh = AP + LF + dwReq;
end
case 'nidq'
[MN,MA,XA,DW] = SGLX_readMeta.ChannelCountsNI(meta);
[MN,MA,XA,DW] = bc.dependencies.SGLX_readMeta.ChannelCountsNI(meta);
if dwReq > DW
fprintf('Maximum digital word in file = %d\n', DW);
digArray = [];
Expand All @@ -119,7 +122,7 @@
digCh = MN + MA + XA + dwReq;
end
case 'obx'
[XA,DW,SY] = SGLX_readMeta.ChannelCountsOBX(meta);
[XA,DW,SY] = bc.dependencies.SGLX_readMeta.ChannelCountsOBX(meta);
if dwReq > DW
fprintf('Maximum digital word in file = %d\n', DW);
digArray = [];
Expand Down Expand Up @@ -317,7 +320,7 @@
APgain = APgain + 1;
end
end
fI2V = SGLX_readMeta.Int2Volts(meta);
fI2V = bc.dependencies.SGLX_readMeta.Int2Volts(meta);
APChan0_to_uV = 1e6*fI2V/APgain(1);
if size(LFgain) > 0
LFChan0_to_uV = 1e6*fI2V/LFgain(1);
Expand All @@ -341,12 +344,12 @@
%
function dataArray = GainCorrectNI(dataArray, chanList, meta)

[MN,MA] = SGLX_readMeta.ChannelCountsNI(meta);
fI2V = SGLX_readMeta.Int2Volts(meta);
[MN,MA] = bc.dependencies.SGLX_readMeta.ChannelCountsNI(meta);
fI2V = bc.dependencies.SGLX_readMeta.Int2Volts(meta);

for i = 1:length(chanList)
j = chanList(i); % index into timepoint
conv = fI2V / SGLX_readMeta.ChanGainNI(j, MN, MA, meta);
conv = fI2V / bc.dependencies.SGLX_readMeta.ChanGainNI(j, MN, MA, meta);
dataArray(j,:) = dataArray(j,:) * conv;
end
end % GainCorrectNI
Expand All @@ -365,7 +368,7 @@
%
function dataArray = GainCorrectOBX(dataArray, chanList, meta)

fI2V = SGLX_readMeta.Int2Volts(meta);
fI2V = bc.dependencies.SGLX_readMeta.Int2Volts(meta);

for i = 1:length(chanList)
j = chanList(i); % index into timepoint
Expand All @@ -388,13 +391,13 @@
function dataArray = GainCorrectIM(dataArray, chanList, meta)

% Look up gain with acquired channel ID
chans = SGLX_readMeta.OriginalChans(meta);
[APgain,LFgain] = SGLX_readMeta.ChanGainsIM(meta);
chans = bc.dependencies.SGLX_readMeta.OriginalChans(meta);
[APgain,LFgain] = bc.dependencies.SGLX_readMeta.ChanGainsIM(meta);
nAP = length(APgain);
nNu = nAP * 2;

% Common conversion factor
fI2V = SGLX_readMeta.Int2Volts(meta);
fI2V = bc.dependencies.SGLX_readMeta.Int2Volts(meta);

for i = 1:length(chanList)
j = chanList(i); % index into timepoint
Expand All @@ -421,7 +424,7 @@
'EndOfLine', ')' );

nBank = numel(C{1}) + 1; % bank0/shank0 is at time = 0
srate = SGLX_readMeta.SampRate(meta);
srate = bc.dependencies.SGLX_readMeta.SampRate(meta);

bankTimes = zeros([nBank,4], "double");
bankTimes(2:nBank,1) = double(C{1});
Expand Down Expand Up @@ -470,6 +473,7 @@ function writeMeta(meta, newPath)
% streamStr, e.g. 'ap' or 'lf'
%
%

function [runName,gateStr,triggerStr,probeStr,streamStr] = parseFileName(fileName)

% Remove extension, if present
Expand Down
98 changes: 15 additions & 83 deletions +bc/+load/readSpikeGLXMetaFile.m
Original file line number Diff line number Diff line change
Expand Up @@ -13,98 +13,30 @@
% microvolts
%

% read meta file
filetext = fileread(metaFile);
meta = bc.dependencies.SGLX_readMeta.ReadMeta(metaFile);

% try and get probe type from meta file (imDatPrb_type or imProbeOpt fields)
expr_scaling = 'imDatPrb_type=*';
[~, startIndex] = regexp(filetext, expr_scaling);
if isempty(startIndex) % try second option: there are two different saving conventions
expr_scaling = 'imProbeOpt=*';
[~, startIndex] = regexp(filetext, expr_scaling);
end
if isempty(startIndex) % if still no probe type information is found, use the param value
if strcmp(probeType, 'NaN')
error(['no probe type found in spikeGLX meta file and no param.probeType specified. ', ...
'Edit the param.probeType value in bc_qualityParamValues.'])
end
else
endIndex = find(filetext(startIndex:end) == newline, 1, 'first') + startIndex - 2;
if isempty(endIndex)
endIndex = length(filetext);
end
probeType = filetext(startIndex+1:endIndex-1);
end

% get channel map information
expr_chanMap = 'imRoFile=';
[~, startIndexChanMap] = regexp(filetext, expr_chanMap);
expr_afterChanMap = 'imSampRate';
[~, endIndexChanMap] = regexp(filetext, expr_afterChanMap);
if isempty(startIndexChanMap) % new convention in new spike glx argh
expr_chanMap = 'imroFile=';
[~, startIndexChanMap] = regexp(filetext, expr_chanMap);
expr_afterChanMap = 'nDataDirs';
[~, endIndexChanMap] = regexp(filetext, expr_afterChanMap);
end

channelMapImro = filetext(startIndexChanMap+1:endIndexChanMap-2-length(expr_afterChanMap));
% channelMapImro
channelMapImro = meta.imRoFile;
if isempty(channelMapImro) % default was used
if strcmp(probeType, '0')
channelMapImro = 'NPtype21_bank0_ref0';
end
end

% get bits_encoding
expr_imMax = 'imMaxInt=';
[~, startIndeximMax] = regexp(filetext, expr_imMax);
if isempty(startIndeximMax) % new convention in new spike glx argh
if ismember(probeType, {'1', '3', '0', '1020', '1030', '1100', '1120', '1121', '1122', '1123', '1200', '1300', '1110'}) %NP1, NP1-like
bits_encoding = 2^10; % 10-bit analog to digital
elseif ismember(probeType, {'21', '2003', '2004', '24', '2013', '2014', '2020'}) % NP2, NP2-like
bits_encoding = 2^14; % 14-bit analog to digital
else
error('unrecognized probe type. Check the imDatPrb_type value in your meta file and create a github issue / email us to add support for this probe type')
end
else
endIndex = find(filetext(startIndeximMax:end) == newline, 1, 'first') + startIndeximMax - 2;
if isempty(endIndex)
endIndex = length(filetext);
end
bits_encoding = str2num(filetext(startIndeximMax+1:endIndex-1));
end
% probeType
probeType = meta.imDatPrb_type;

% get
expr_vMax = 'imAiRangeMax =';
[~, startIndexvMax] = regexp(filetext, expr_vMax);
if isempty(startIndexvMax) % new convention in new spike glx argh
if ismember(probeType, {'1', '3', '0', '1020', '1030', '1100', '1120', '1121', '1122', '1123', '1200', '1300', '1110'}) %NP1, NP2-like
Vrange = 1.2e6; % from -0.6 to 0.6 V = 1.2 V = 1.2 e6 microvolts
elseif ismember(probeType, {'21', '2003', '2004', '24', '2013', '2014', '2020'}) % NP2, NP2-like
Vrange = 1e6; % from -0.5 to 0.5 V = 1 V = 1 e6 microvolts
else
error('unrecognized probe type. Check the imDatPrb_type value in your meta file and create a github issue / email us to add support for this probe type')
end
else
endIndex = find(filetext(startIndexvMax:end) == newline, 1, 'first') + startIndexvMax - 2;
if isempty(endIndex)
endIndex = length(filetext);
end
Vrange = 2 * str2num(filetext(startIndexvMax+1:endIndex-1)) * 1e6;
end
%% scaling factor
% gain
gain_allChannels = bc.dependencies.SGLX_readMeta.ChanGainsIM(meta);
allChannels_index = bc.dependencies.SGLX_readMeta.OriginalChans(meta);
thisChannel = find(allChannels_index == peakChan);
thisGain = gain_allChannels(thisChannel);

% gain - QQ read out from table, modify. this could be wrong in some cases.
%
if ismember(probeType, {'1', '3', '0', '1020', '1030', '1100', '1120', '1121', '1122', '1123', '1200', '1300', '1110'}) %NP1, NP2-like
gain = 500; % 10-bit analog to digital
elseif ismember(probeType, {'21', '2003', '2004', '24'}) % NP2, NP2-like
gain = 80;
elseif ismember(probeType, {'2013', '2014', '2020'}) % NP2, NP2-like
gain = 100; % 14-bit analog to digital
else
error('unrecognized probe type. Check the imDatPrb_type value in your meta file and create a github issue / email us to add support for this probe type')
end
% Vrange / bits_encoding
fI2V = bc.dependencies.SGLX_readMeta.Int2Volts(meta);

% calculate scaling factor
scalingFactor = Vrange / bits_encoding / gain;
scalingFactor = fI2V / thisGain;

end
42 changes: 42 additions & 0 deletions +bc/+qm/getRawAmplitude.asv
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
function rawAmplitude_uV = getRawAmplitude(rawWaveforms, peakChan, metaFile, probeType, gain_to_uV)
% JF, Get the amplitude of the mean raw waveform for a unit
% ------
% Inputs
% ------
% rawWaveforms: nTimePoints × 1 double vector of the mean raw waveform
% for one unit in mV
% metaFileDir: dir structure containing the location of the raw .meta or .oebin file.
% probeType: optional. only used if you are using spikeGLX *and* the meta
% file does not contain any probetype field (imDatPrb_type or imProbeOpt)
% ------
% Outputs
% ------
% rawAmplitude: raw amplitude in microVolts of the mean raw waveform for
% this unit

% sanitize inputs
if nargin < 4 || isempty(probeType)
probeType = 'NaN';
else
probeType = num2str(probeType);
end

% get scaling factor
if strcmp(metaFile, 'NaN') == 0
if contains(metaFile, 'oebin')
% open ephys format
scalingFactor = bc.load.readOEMetaFile(metaFile); % single sclaing factor per channel for now
else
% spikeGLX format
[scalingFactors_all, ~, ~] = bc.load.readSpikeGLXMetaFile(metaFile, probeType);
scalingFactor = scalingFactors_all(peakChan);
end
else
scalingFactor = gain_to_uV;
end

% scale waveforms to get amplitude in microVolts
rawWaveforms = rawWaveforms .* scalingFactor;
rawAmplitude_uV = abs(max(rawWaveforms)) + abs(min(rawWaveforms));
%rawAmplitude_mV = rawAmplitude_uV ./1000;
end
5 changes: 3 additions & 2 deletions +bc/+qm/getRawAmplitude.m
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
function rawAmplitude_uV = getRawAmplitude(rawWaveforms, metaFile, probeType, gain_to_uV)
function rawAmplitude_uV = getRawAmplitude(rawWaveforms, peakChan, metaFile, probeType, gain_to_uV)
% JF, Get the amplitude of the mean raw waveform for a unit
% ------
% Inputs
Expand All @@ -15,7 +15,7 @@
% this unit

% sanitize inputs
if nargin < 3 || isempty(probeType)
if nargin < 4 || isempty(probeType)
probeType = 'NaN';
else
probeType = num2str(probeType);
Expand All @@ -28,6 +28,7 @@
scalingFactor = bc.load.readOEMetaFile(metaFile);
else
% spikeGLX format

[scalingFactor, ~, ~] = bc.load.readSpikeGLXMetaFile(metaFile, probeType);
end
else
Expand Down
4 changes: 2 additions & 2 deletions +bc/+qm/runAllQualityMetrics.asv
Original file line number Diff line number Diff line change
Expand Up @@ -165,8 +165,8 @@ for iUnit = 1:size(uniqueTemplates, 1)

%% amplitude
if param.extractRaw
qMetric.rawAmplitude(iUnit) = bc.qm.getRawAmplitude(rawWaveformsFull(thisUnit, rawWaveformsPeakChan(thisUnit), :), ...
param.ephysMetaFile, param.probeType, param.gain_to_uV);
qMetric.rawAmplitude(iUnit) = bc.qm.getRawAmplitude(rawWaveformsFull(thisUnit, rawWaveformsPeakChan(thisUnit), :), ...
rawWaveformsPeakChan(thisUnit), param.ephysMetaFile, param.probeType, param.gain_to_uV);
else
qMetric.rawAmplitude(iUnit) = NaN;
qMetric.signalToNoiseRatio(iUnit) = NaN;
Expand Down
4 changes: 2 additions & 2 deletions +bc/+qm/runAllQualityMetrics.m
Original file line number Diff line number Diff line change
Expand Up @@ -165,8 +165,8 @@

%% amplitude
if param.extractRaw
qMetric.rawAmplitude(iUnit) = bc.qm.getRawAmplitude(rawWaveformsFull(thisUnit, rawWaveformsPeakChan(thisUnit), :), ...
param.ephysMetaFile, param.probeType, param.gain_to_uV);
qMetric.rawAmplitude(iUnit) = bc.qm.getRawAmplitude(rawWaveformsFull(thisUnit, :, :), ...
rawWaveformsPeakChan(thisUnit), param.ephysMetaFile, param.probeType, param.gain_to_uV);
else
qMetric.rawAmplitude(iUnit) = NaN;
qMetric.signalToNoiseRatio(iUnit) = NaN;
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ Below is a flowchart of how bombcell evaluates and classifies each unit:

Bombcell extracts relevant quality metrics to categorize units into four categories: single somatic units, multi-units, noise units and non-somatic units.

Take a look at the MATLAB live script [`gettingStarted`](https://github.com/Julie-Fabre/bombcell/blob/main/gettingStarted.mlx) or [`bombcell_pipeline`](https://github.com/Julie-Fabre/bombcell/blob/main/pipelines/bombcell_pipeline.m) to see an example workflow and play around with our small toy dataset. You can also take a look at the exercise we prepared for the 2024 Neuropixels course [here](https://github.com/BombCell/Neuropixels_course_2024).
Take a look at the MATLAB live script [`gettingStarted`](https://github.com/Julie-Fabre/bombcell/blob/main/gettingStarted.mlx) to see an example workflow and play around with our small toy dataset. You can also take a look at the exercise we prepared for the 2024 Neuropixels course [here](https://github.com/BombCell/Neuropixels_course_2024).

#### Installation

Expand Down
Binary file removed demos/demo_bombcell.mlx
Binary file not shown.
Binary file removed demos/gettingStarted_NPXcourse.mlx
Binary file not shown.
Binary file removed demos/gettingStarted_SWCdemo.mlx
Binary file not shown.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Binary file modified gettingStarted.mlx
Binary file not shown.
Loading

0 comments on commit f8ecb67

Please sign in to comment.