Skip to content
Open
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
33 changes: 33 additions & 0 deletions core/analogSignalArray.m
Original file line number Diff line number Diff line change
Expand Up @@ -218,5 +218,38 @@ function disp(self)
varargin{:});
end

function self = downsample(self, new_fs)
% Downsample analogSignalArray using MATLAB's resample()
%
% Usage:
% asa.downsample(10)

if isempty(self.data)
warning('analogSignalArray is empty');
return;
end

old_fs = self.sampling_rate;

if new_fs >= old_fs

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

resample can upsample or downsample. It may be better not to restrict downsample to just downsampling. You could call this function resample instead of downsample and then allow it to up or downsample

warning('new_fs must be less than old_fs, downsampling not performed')
return;
end

% Compute rational resampling ratio
[p, q] = rat(new_fs / old_fs);

% --- Resample data ---
% resample operates column-wise (perfect for [samples x signals])
self.data = resample(self.data, p, q);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make sure input to resample is what resample expects: single | double.


% --- Recompute timestamps ---
n_samples_new = size(self.data, 1);
self.timestamps = self.timestamps(1) + (0:n_samples_new-1)' / new_fs;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will need to compute timestamps from the original for cases where the original signal is already sliced.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like resample can return timestamps. This may be the solution

[y,ty] = resample(x,tx,___) returns in ty the instants that correspond to the resampled signal.


% Update sampling rate
self.sampling_rate = new_fs;
end

end
end
34 changes: 34 additions & 0 deletions test/test_analogSignalArray.m
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,37 @@ function testIssorted(testCase)

verifyEqual(testCase, asa.issorted(), true);
end

function testDownsample(testCase)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add another case where the analogSignalArray has been sliced before downsampling

% Create synthetic signal
fs = 100; % original sampling rate
t = (0:1/fs:5)'; % 5 seconds
data = [sin(2*pi*5*t), cos(2*pi*2*t)]; % 2-channel signal

asa = analogSignalArray( ...
'data', data, ...
'timestamps', t, ...
'sampling_rate', fs);

% Downsample
new_fs = 10;
asa.downsample(new_fs);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This shows the change happens "in place". Is this true? Does it always modify the original or can you return to a new variable: downsampled_asa = asa.downsample(new_fs)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be better if, by default, it didn't modify the original, and "in_place" was an option


% Check sampling rate updated
verifyEqual(testCase, asa.sampling_rate, new_fs);

% Check timestamps spacing
dt = diff(asa.timestamps);
verifyEqual(testCase, mean(dt), 1/new_fs, 'AbsTol', 1e-10);

% Check number of channels preserved
verifyEqual(testCase, size(asa.data, 2), 2);

% Check number of samples roughly correct
expected_n = floor(length(t) * (new_fs / fs));
verifyLessThanOrEqual(testCase, abs(size(asa.data,1) - expected_n), 1);

% Check timestamps start correctly
verifyEqual(testCase, asa.timestamps(1), t(1));
end

Loading