Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
57 changes: 50 additions & 7 deletions +ndr/+reader/intan_rhd.m
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@
newchannel.name = intan_rhd_obj.intanname2mfdaqname(...
intan_rhd_obj,...
channel_type_entry,...
channel(p).native_channel_name);
channel(p));
channels(end+1) = newchannel;
end
end
Expand Down Expand Up @@ -453,19 +453,62 @@
end;
end; % ndr.reader.intan_rhd.intanchanneltype2mfdaqchanneltype()

function [channame] = intanname2mfdaqname(intan_rhd_obj, type, name)
function [channame] = intanname2mfdaqname(intan_rhd_obj, type, name_or_struct)

Check warning

Code scanning / Code Analyzer

Input argument might be unused. Consider replacing the argument with ~ instead. Warning

Input argument might be unused. Consider replacing the argument with ~ instead.
% INTANNAME2MFDAQNAME - Converts a channel name from Intan native format to ndr.ndr.reader.mfdaq format
%
% [CHANNAME] = INTANNAME2MFDAQNAME(NDR_NDRREADER_INTANREADER_OBJ, TYPE, NAME)
% [CHANNAME] = INTANNAME2MFDAQNAME(NDR_NDRREADER_INTANREADER_OBJ, TYPE, NAME_OR_STRUCT)
%
% Given an Intan native channel name (e.g., 'A-000') in NAME and an
% ndr.ndr.reader.mfdaq channel type string (see NDI_DEVICE_MFDAQ), this function
% produces an ndr.ndr.reader.mfdaq channel name (e.g., 'ai1').
%
% NAME_OR_STRUCT can also be the Intan channel structure (with fields 'native_channel_name', 'chip_channel', etc).
%

if isstruct(name_or_struct),

Check notice

Code scanning / Code Analyzer

Extra comma is unnecessary. Note

Extra comma is unnecessary.
name = name_or_struct.native_channel_name;
if isfield(name_or_struct, 'chip_channel'),

Check notice

Code scanning / Code Analyzer

Extra comma is unnecessary. Note

Extra comma is unnecessary.
chip_channel = name_or_struct.chip_channel;
else
chip_channel = [];
end
else,

Check notice

Code scanning / Code Analyzer

Extra comma is unnecessary. Note

Extra comma is unnecessary.
name = name_or_struct;
chip_channel = [];
end;

Check notice

Code scanning / Code Analyzer

Extra semicolon is unnecessary. Note

Extra semicolon is unnecessary.

sep = find(name=='-');
chan_intan = str2num(name(sep+1:end));
chan = chan_intan + 1; % Intan numbers from 0
channame = [ndr.reader.base.mfdaq_prefix(type) int2str(chan)];
if ~isempty(sep),

Check notice

Code scanning / Code Analyzer

Extra comma is unnecessary. Note

Extra comma is unnecessary.
chan_intan = str2num(name(sep(end)+1:end));

Check notice

Code scanning / Code Analyzer

If you are operating on scalar values, consider using 'str2double' for faster performance. Note

If you are operating on scalar values, consider using 'str2double' for faster performance.
chan = chan_intan + 1; % Intan numbers from 0
else,

Check notice

Code scanning / Code Analyzer

Extra comma is unnecessary. Note

Extra comma is unnecessary.
% try to find a number at the end
[s,e] = regexp(name,'\d+$');
if ~isempty(s),

Check notice

Code scanning / Code Analyzer

Extra comma is unnecessary. Note

Extra comma is unnecessary.
chan_intan = str2num(name(s:e));

Check notice

Code scanning / Code Analyzer

If you are operating on scalar values, consider using 'str2double' for faster performance. Note

If you are operating on scalar values, consider using 'str2double' for faster performance.
if strncmpi(type,'aux',3) | strncmpi(type,'ax',2),

Check notice

Code scanning / Code Analyzer

When both arguments are numeric scalars, consider replacing | with || for performance. Note

When both arguments are numeric scalars, consider replacing | with || for performance.

Check notice

Code scanning / Code Analyzer

Extra comma is unnecessary. Note

Extra comma is unnecessary.
% Intan aux channels are named AUX1, AUX2, etc, so they are 1-based
chan = chan_intan;
else,

Check notice

Code scanning / Code Analyzer

Extra comma is unnecessary. Note

Extra comma is unnecessary.
chan = chan_intan + 1; % assume 0-based
end;

Check notice

Code scanning / Code Analyzer

Extra semicolon is unnecessary. Note

Extra semicolon is unnecessary.
else,

Check notice

Code scanning / Code Analyzer

Extra comma is unnecessary. Note

Extra comma is unnecessary.
chan = [];
end;

Check notice

Code scanning / Code Analyzer

Extra semicolon is unnecessary. Note

Extra semicolon is unnecessary.

Check notice

Code scanning / Code Analyzer

Extra semicolon is unnecessary. Note

Extra semicolon is unnecessary.
end;

Check notice

Code scanning / Code Analyzer

Extra semicolon is unnecessary. Note

Extra semicolon is unnecessary.

Check notice

Code scanning / Code Analyzer

Extra semicolon is unnecessary. Note

Extra semicolon is unnecessary.

if isempty(chan),

Check notice

Code scanning / Code Analyzer

Extra comma is unnecessary. Note

Extra comma is unnecessary.

Check notice

Code scanning / Code Analyzer

Extra comma is unnecessary. Note

Extra comma is unnecessary.
% if we couldn't parse the name, try to use the chip_channel if available
if ~isempty(chip_channel),

Check notice

Code scanning / Code Analyzer

Extra comma is unnecessary. Note

Extra comma is unnecessary.
chan = chip_channel + 1; % assume 0-based chip channel
end;
end;

if isempty(chan),
channame = [ndr.reader.base.mfdaq_prefix(type)]; % fallback
else,

Check notice

Code scanning / Code Analyzer

Extra comma is unnecessary. Note

Extra comma is unnecessary.
channame = [ndr.reader.base.mfdaq_prefix(type) int2str(chan)];
end;

Check notice

Code scanning / Code Analyzer

Extra semicolon is unnecessary. Note

Extra semicolon is unnecessary.
end; % ndr.reader.intan_rhd.intanname2mfdaqname

function headername = mfdaqchanneltype2intanfreqheader(channeltype)
Expand All @@ -482,7 +525,7 @@
headername = 'board_dig_in_sample_rate';
case {'time','timestamp'},
headername = 'amplifier_sample_rate';
case{'auxiliary','aux'},
case{'auxiliary','aux','auxiliary_in'},

Check notice

Code scanning / Code Analyzer

Extra comma is unnecessary. Note

Extra comma is unnecessary.
headername = 'aux_input_sample_rate';
otherwise,
error(['Do not know frequency header for channel type ' channeltype '.']);
Expand Down
2 changes: 1 addition & 1 deletion .github/badges/code_issues.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion .github/badges/tests.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added example_data/example.abf
Binary file not shown.
4 changes: 2 additions & 2 deletions lib/abfload/abfload2.m
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@
% ************************
% abf version >= 2.0
% ************************
otherwise
otherwise
error(['unknown or incompatible file signature: ' fFileSignature]);
end

Expand Down Expand Up @@ -428,7 +428,7 @@
% -------------------------------------------------------------------------
if h.lActualAcqLength<h.nADCNumChannels
fclose(fid);
error('less data points than sampled channels in file');
error(['less data points than sampled channels in file: ' fn]);
end
% the numerical value of all recorded channels (numbers 0..15)
recChIdx=h.nADCSamplingSeq(1:h.nADCNumChannels);
Expand Down
58 changes: 58 additions & 0 deletions tools/tests/+ndr/+unittest/+reader/TestIntanRhd.m
Original file line number Diff line number Diff line change
Expand Up @@ -82,5 +82,63 @@ function testReadChannels(testCase)
header = ndr.format.intan.read_Intan_RHD2000_header(rhd_file);
testCase.verifyNotEmpty(header);
end

function testChannelNameParsing(testCase)
% Test parsing of Intan channel names to MFDAQ names

% Valid cases
names = {'A-000', 'B-005', 'AUX1', 'AUX2', 'DIN-00'};
types = {'analog_in', 'analog_in', 'auxiliary_in', 'auxiliary_in', 'digital_in'};
expected = {'ai1', 'ai6', 'ax1', 'ax2', 'di1'};

for i = 1:length(names)
result = ndr.reader.intan_rhd.intanname2mfdaqname([], types{i}, names{i});
testCase.verifyEqual(result, expected{i}, ...
['Failed to convert ' names{i} ' (' types{i} ') to ' expected{i}]);
end
end

function testChannelStructParsing(testCase)
% Test parsing using channel struct and chip_channel fallback

% Case 1: Name parsing fails, use chip_channel
% Assume 'AUX' name but chip_channel 0 -> 'ax1'
channel_struct.native_channel_name = 'AUX';
channel_struct.chip_channel = 0;
type = 'auxiliary_in';

result = ndr.reader.intan_rhd.intanname2mfdaqname([], type, channel_struct);
testCase.verifyEqual(result, 'ax1', 'Failed fallback to chip_channel for AUX');

% Case 2: Name parsing works, ignore chip_channel (or ensure consistency)
channel_struct.native_channel_name = 'AUX2';
channel_struct.chip_channel = 99; % Should be ignored if name parses?
% My logic uses name first.

result = ndr.reader.intan_rhd.intanname2mfdaqname([], type, channel_struct);
testCase.verifyEqual(result, 'ax2', 'Should use name if parseable');

% Case 3: Struct without chip_channel
channel_struct2.native_channel_name = 'A-000';
% no chip_channel field
result = ndr.reader.intan_rhd.intanname2mfdaqname([], 'analog_in', channel_struct2);
testCase.verifyEqual(result, 'ai1', 'Should work without chip_channel field');
end

function testAuxSampleRate(testCase)
% Test that samplerate works for auxiliary_in
reader = ndr.reader.intan_rhd();
ndr_path = ndr.fun.ndrpath();
rhd_file = fullfile(ndr_path, 'example_data', 'example.rhd');
epochstreams = {rhd_file};
epoch_select = 1;

% This should not error now
sr = reader.samplerate(epochstreams, epoch_select, 'auxiliary_in', 1);

% Check if it returns a valid number (header frequency_parameters are usually populated)
testCase.verifyNotEmpty(sr);
testCase.verifyTrue(isnumeric(sr));
end
end
end
Loading