Commit 0fa47c82 authored by Adam Fuksa's avatar Adam Fuksa

#34469 add matlab toolbox

parent d7e8ba29
Pipeline #5206 passed with stages
in 1 minute and 3 seconds
Poniższe działa na MATLAB R2017a, wersja trial bez żadnych dodatków.
1. Wygenerowanie pakietu (toolboxa) matlabowego z plików:
Trzeba wyklikać:
https://www.mathworks.com/help/matlab/matlab_prog/create-and-share-custom-matlab-toolboxes.html#bugxfu9
(wystarczy dodać pliki z katalogu src, wpisać nazwę toolboxa(obci_readmanager) i kliknąć package.
Pojawi się plik obci_readmanager.mltbx.
2. Instalacja toolboxa:
Wpisujemy w matlabie: matlab.addons.toolbox.installToolbox('obci_readmanager.mltbx')
3. Przykładowe użycie:
Wpisujemy w matlabie:
TestTags (w okienku Workspace pojawiają się różne obiekty)
albo wpisujemy:
x = ReadManager('test_data.obci.info', 'test_data.obci.dat', 'test_data.obci.tags')
x.get_samples
x.get_start_timestamp
x.get_params
classdef DataSource < handle
%DATASOURCE Provides sample data
% retuns sample in matrix. Rows are channels, columns are samples
properties
end
properties (SetAccess=private, GetAccess=private)
iter_pos
iter_limit
end
methods
function self=DataSource()
end
function samples=get_samples(self,p_from,p_len)
%GET_SAMPLES(p_from=0,p_len=inf) retuns samples
% when p_len is inf, maximum number of samples is returned
% samples are returned in a
% matrix(nr_of_channels,nr_of_samples)
% Raises Exceptions when p_len is not inf and is to big
default('p_len',inf);
default('p_from',0);
self.seek_samples(p_from);
samples=self.read_samples(p_len);
end
function iter_samples(self,p_from,p_len)
%ITER_SAMPLES(p_from=0,p_len=inf)
% starts iterator for getting samples
default('p_from',0)
default('p_len',inf)
self.iter_limit=p_len;
self.seek_samples(p_from);
end
function samples=next_samples(self)
%NEXT_SAMPLES() - gets one column of samples, all channels
% Raises exception in the end
self.iter_pos=self.iter_pos+1;
if self.iter_pos>self.iter_limit
samples=[];
throw(MException('SignalExceptions:NoNextValue','There is no next value in the iterator'))
end
samples=self.read_samples(1);
end
function save_to_file(self,p_file_name,sample_type)
%SAVE_TO_FILE(p_filename) - dumps all samples to the file
default('sample_type','double')
file_id=fopen(p_file_name,'w');
fwrite(file_id,self.get_samples(),lower(sample_type),0,'l');
fclose(file_id);
end
function seek_samples(self,p_from)
end
function samples=read_samples(self,p_len)
throw(MException('SignalExceptions:NoNextValue','There is no more samples'))
end
end
end
classdef FileDataSource < DataSource
%FileDataSource Reads data from give file
properties
file_name
end
properties (SetAccess=private,GetAccess=private)
channel_count
fileID
sample_size=8;
sample_type= 'double'
end
methods
function self=FileDataSource(p_file_name,p_nr_channels,sample_type)
default('sample_type','double')
self.file_name=p_file_name;
self.channel_count=p_nr_channels;
[self.fileID,msg]=fopen(p_file_name,'r');
if self.fileID<0
throw(MException('SignalExceptions:FileOpenError',[msg ,':',p_file_name]));
end
self.sample_type=lower(sample_type);
if strcmp(self.sample_type, 'float')
self.sample_size=4;
end
end
function seek_samples(self,p_from)
fseek(self.fileID,p_from*self.channel_count*self.sample_size,-1);
end
function samples=read_samples(self,p_len)
[samples,len]=fread(self.fileID,[self.channel_count,p_len],self.sample_type,0,'l');
if p_len~=inf && len<p_len
throw(MException('SignalExceptions:NoNextValue','Not enough samples!'))
end
end
function delete(self)
fclose(self.fileID);
end
end
end
class FileInfoSource
properties
filename
end
methods
function obj = FileInfoSource(filename)
obj.filename=filename;
end
end
\ No newline at end of file
classdef InfoSource < handle
%INFOSOURCE Class for reading and writing data params
% Detailed explanation goes here
properties
file_name
end
properties (SetAccess=private, GetAccess=private)
params
dom
tag_definitions=struct('channels_names',{'channelLabels', 'label'} ,...
'channels_numbers',{'channelNumbers', 'number'} ,...
'channels_gains',{'calibrationGain', 'calibrationParam'} ,...
'channels_offsets',{'calibrationOffset', 'calibrationParam'} ,...
'number_of_samples',{'sampleCount',[]} ,...
'number_of_channels',{'channelCount',[]} ,...
'sampling_frequency',{'samplingFrequency',[]} ,...
'first_sample_timestamp',{'firstSampleTimestamp',[]} ,...
'file',{'sourceFileName',[]} ,...
'file_format',{'sourceFileFormat', 'rawSignalInfo'} ,...
'calibration',{'calibration',[]} ,...
'sample_type',{'sampleType',[]} ,...
'byte_order',{'byteOrder',[]} ,...
'page_size',{'pageSize',[]} ,...
'blocks_per_page',{'blocksPerPage',[]} ,...
'export_file_name',{'exportFileName',[]} ,...
'export_date',{'exportDate',[]});
end
methods
function self=InfoSource(p_file_name)
%InfoSource(p_filename=[]) Reads params from xml p_filename
self.params=struct();
if nargin==1
self.file_name=p_file_name;
self.dom=xmlread(p_file_name);
self.params=self.get_dom_params();
end
end
function params=get_params(self)
%GET_PARAMS returns struct where each field name is param name,
% and field value is param value- string or array of string
params=self.params;
end
function param=get_param(self,p_param_name)
%GET_PARAM(p_param_name) get single param with name
%p_param_name. Raises an exception when there is no such param
try
param=self.params.(p_param_name);
catch
throw(MException('SignalExceptions:NoParameter','There is no parameter like "%s"',p_param_name));
end
end
function set_params(self,p_params)
%SET_PARAMS(p_params) Replaces current params with params from
%struct p_params.
% Only params in p_params are replaced, the rest
% stays the same. Use reset_params() to clear all the params.
% Raises exception when param names are invalid.
fn=fieldnames(p_params);
for i=1:length(fn)
self.set_param(fn{i},p_params.(fn{i}));
end
end
function set_param(self,p_param_name,p_value)
%SET_PARAM(p_param_name,p_value) Sets single param
% Raises exception when param name is invalid.
try
[~]=self.tag_definitions.(p_param_name);
catch Ex
throw(MException('SignalExceptions:NoParameter','There is no parameter like "%s"',p_param_name))
end
self.params.(p_param_name)=p_value;
end
function reset_params(self)
%RESET_PARAMS clears all of the params
self.params=struct();
end
function save_to_file(self,p_file_name)
%SAVE_TO_FILE(p_file_name) - saves all the params to xml file
docNode=com.mathworks.xml.XMLUtils.createDocument('rs:rawSignal');
docRootNode=docNode.getDocumentElement;
docRootNode.setAttribute('xmlns:rs','http://signalml.org/rawsignal');
fn=fieldnames(self.params);
for i=1:length(fn)
field=fn{i};
[name,subname]=self.get_param_names(field);
child=docNode.createElement(name);
if isempty(subname)
child.appendChild(docNode.createTextNode(num2str(self.params.(field))));
else
elems=self.params.(field);
for elem=elems
subchild=docNode.createElement(subname);
subchild.appendChild(docNode.createTextNode(elem) );
child.appendChild(subchild);
end
end
docRootNode.appendChild(child);
end
xmlwrite(p_file_name,docNode);
end
end
methods (Static, Access=private)
function param=get_param_name(p_param)
param=strcat('rs:',p_param);
end
end
methods (Access=private)
function params=get_param_list(self,p_name,p_subname)
try
list=self.dom.getElementsByTagName(p_name).item(0).getElementsByTagName(p_subname);
params=cell(1,list.getLength);
for i=0:list.getLength-1
try
params(i+1)={char(list.item(i).getFirstChild.getData)};
catch Ex
params(i+1)={''};
end
end
catch ex
params=[];
end
end
function [name,subname]=get_param_names(self,p_name)
subname=[];
name=self.get_param_name(p_name);
try
names={self.tag_definitions.(p_name)};
name=self.get_param_name(names{1});
if ischar(names{2})
subname=self.get_param_name(names{2});
end
catch ex
end
end
function param=get_dom_param(self,p_param_name)
[name,subname]=self.get_param_names(p_param_name);
param=[];
if ischar(subname)
param=self.get_param_list(name,subname);
return
end
try
child=self.dom.getElementsByTagName(name).item(0).getFirstChild;
try
t_param=char(child.getData);
catch
t_param='';
end
[param,status]=str2num(t_param);
if ~status
param=t_param;
end
catch ex
throw(MException('SignalExceptions:NoParameter','There is no parameter like "%s"',name))
end
end
function params=get_dom_params(self)
params=struct();
fn=fieldnames(self.tag_definitions);
for i=1:length(fn)
try
params.(fn{i})=self.get_dom_param(fn{i});
catch ex
end
end
try
params.sample_type;
catch ex
params.sample_type='DOUBLE';
end
end
end
end
classdef MemoryDataSource < DataSource
%UNTITLED12 Summary of this class goes here
% Detailed explanation goes here
properties (SetAccess=private,GetAccess=private)
samples
samp_pos=0
end
methods
function self=MemoryDataSource(p_samples)
default('p_samples',[])
self.set_samples(p_samples)
end
function set_samples(self,samples)
self.samples=samples;
self.samp_pos=0;
end
function seek_samples(self,p_from)
self.samp_pos=max(p_from-1,0);
end
function samples=read_samples(self,p_len)
if p_len==inf
p_len=length(self.samples)-self.samp_pos;
end
to=self.samp_pos+p_len;
if to>length(self.samples)
throw(MException('SignalExceptions:NoNextValue','Not enough samples!'))
else
samples=self.samples(:,self.samp_pos+1:to);
end
self.samp_pos=to;
end
end
end
classdef ReadManager < handle
%READMANAGER Class for reading and writing channel data, tags and data
%params
properties
info_source
data_source
tags_source
end
methods
function self=ReadManager(p_info_source,p_data_source,p_tags_source)
%READMANAGER(p_info_source,p_data_source,p_tags_source=[])
% p_info_source - file_name or InfoSource
% p_data_source - file_name or DataSource
% p_tags_source - optional,file_name or TagsSource
if ~isa(p_info_source,'InfoSource')
p_info_source=InfoSource(p_info_source);
end
self.info_source=p_info_source;
if ~isa(p_data_source,'DataSource')
p_data_source=FileDataSource(p_data_source,self.info_source.get_param('number_of_channels'), self.info_source.get_param('sample_type'));
end
self.data_source=p_data_source;
if nargin==3 && ~isempty(p_tags_source)
if ~isa(p_tags_source,'TagsSource')
p_tags_source=TagsSource(p_tags_source);
end
self.tags_source = p_tags_source;
else
self.tags_source=TagsSource();
end
end
function param=get_param(self,p_param_name)
%GET_PARAM get one param. see help InfoSource.get_param
param=self.info_source.get_param(p_param_name);
end
function set_param(self,p_param_name, p_param_value)
%SET_PARAM set one param. see help InfoSource.set_param
self.info_source.set_param(p_param_name, p_param_value);
end
function start_ts=get_start_timestamp(self)
%GET_START_TIMESTAMP gets start timestamp of this stream
start_ts=0;
end
function params=get_params(self)
%GET_PARAMS get all params. see help InfoSource.get_params
params=self.info_source.get_params();
end
function set_params(self,p_params)
%SET_PARAMS(p_params)
% p_params= struct
% Set params. see help InfoSource.set_params
self.info_source.set_params(p_params)
end
function samples=get_samples(self,p_from,p_len)
%GET_SAMPLES(p_from,p_len) get samples.
% see help DataSource.get_samples
% when invoked without arguments, all samples are cached in
% memory, so it is faster in the future.
if nargin<3; p_len=inf; end
if nargin<2; p_from=0; end
if p_from==0 && p_len==inf
samples=self.data_source.get_samples();
if ~isa(self.data_source,'MemoryDataSource')
self.data_source=MemoryDataSource(samples);
end
else
samples=self.data_source.get_samples(p_from,p_len);
end
end
function iter_samples(self,p_from,p_len)
%ITER_SAMPLES iter through samples
% see help DataSource.iter_samples
if nargin<3; p_len=inf; end
if nargin<2; p_from=0; end
self.data_source.iter_samples(p_from,p_len)
end
function samples=next_samples(self)
%NEXT_SAMPLES iter through samples
% see help DataSource.next_samples
samples=self.data_source.next_samples();
end
function set_samples(self,p_samples,p_channel_names)
%SET_SAMPLES(p_samples,p_channel_names)
% p_samples - matrix(num_channels,num_samples)
% p_channel_names - array of strings of length of
% num_channels
%Usage:
% set_samples([1,2,3;4,5,6],{'A','B'})
s=size(p_samples);
if s(1)>0 && (~exist('p_channel_names','var')||length(p_channel_names)~=s(1))
throw(MException('ReadManager:WrongChannelNames','Wrong number of channel names'));
end
self.data_source=MemoryDataSource();
self.data_source.set_samples(p_samples);
self.info_source.set_param('channels_names',p_channel_names);
self.info_source.set_param('number_of_channels',length(p_channel_names));
self.info_source.set_param('number_of_samples',s(2)) ;
end
function ch_samples=get_channel_samples(self,p_ch_name,p_from,p_len)
%GET_CHANNEL_SAMPLES(p_ch_name, p_from=[],p_len=inf) - gets all
%samples fron given channel
% p_ch_name - channel index or channel name as string
% p_from - from which sample to start
% p_len - how many samples
if nargin<4; p_len=inf; end
if nargin<3; p_from=0; end
samples=self.get_samples(p_from,p_len);
if isinteger(p_ch_name)
ch_ind=p_ch_name;
else
ch_ind=find(ismember(self.get_param('channels_names'),p_ch_name)==1);
end
ch_samples=samples(ch_ind,:);
end
function tags=get_tags(self,p_tag_type,p_from,p_len,p_func)
%GET_TAGS(p_tag_type,p_from,p_len,p_func) get filtered tags.
% see help TagsSource.get_tags
if nargin<5; p_func=@(x) true;end
if nargin<4; p_len=inf;end
if nargin<3; p_from=0;end
if nargin<2; p_tag_type=[];end
tags = self.tags_source.get_tags(p_tag_type,p_from,p_len,p_func);
end
function iter_tags(self,p_tag_type,p_from,p_len,p_func)
%ITER_TAGS(p_tag_type,p_from,p_len,p_func) get filtered tags.
% see help TagsSource.iter_tags
if nargin<4; p_func=@(x) true;end
if nargin<3; p_len=inf;end
if nargin<2; p_from=0;end
if nargin<1; p_tag_type=[];end
self.tags_source.iter_tags(p_tag_type,p_from,p_len,p_func);
end
function tag=next_tag(self)
%NEXT_TAG() get filtered tags.
% see help TagsSource.next_tag
tag=self.tags_source.next_tag();
end
function set_tags(self,p_tags,p_first_ts)
%SET_TAGS(p_tags,p_first_timestamp) set tags.
% see help TagsSource.set_tags
default('p_first_ts',0)
self.tags_source.set_tags(p_tags,p_first_ts)
end
function save_to_file(self,p_dir,p_name)
%SAVE_TO_FILE(p_dir,p_name) save date from this ReadManager to
% files
% p_dir - directory path
% p_name - prefix for file names .obci.dat, .obci.info,
% .obci.tags
if ~isa(self.data_source,'MemoryDataSource')
self.get_samples();
end
path=[p_dir '/' p_name];
self.data_source.save_to_file([path '.obci.dat'],self.info_source.get_param('sample_type'));
self.info_source.save_to_file([path '.obci.info']);
self.tags_source.save_to_file([path '.obci.tags']);
end
end
end
classdef SmartTag < ReadManager
%SmartTag It is ReadManager with some context.
% It can by used to get some parts of the signal.
properties
start_tag
tag_def
end
properties (SetAccess=protected)
is_initialised
end
methods
function self=SmartTag(p_tag_def,p_start_tag)
%SmartTag(p_tag_def,p_start_tag)
% p_tag_def = SmartTagDefinition
% p_start_tag = starting tag of this SmartTag
self=self@ReadManager(InfoSource(),MemoryDataSource(),TagsSource());
self.is_initialised=false;
if nargin==0
return
end
self.start_tag=p_start_tag;
self.tag_def=p_tag_def;
end
function start_ts=get_start_timestamp(self)
start_ts=self.tag_def.start_param_func(self.start_tag)+self.tag_def.start_offset;
end
function end_ts=get_end_timestamp(self)
end_ts=self.get_start_timestamp();
end
function initialize(self,read_manager)
%INITIALIZE - initializes itself with part of the data from read_manager
%part is defined by this SmartTag
start_ts=max(self.get_start_timestamp(),0);
first_ts=read_manager.get_start_timestamp();
sampling_freq=read_manager.get_param('sampling_frequency');
samples_to_start=int32((start_ts-first_ts)*sampling_freq);
end_ts=self.get_end_timestamp();
samples_to_end=int32((end_ts-first_ts)*sampling_freq);
sample_count=read_manager.get_param('number_of_samples');
samples_to_start=min(samples_to_start,sample_count);
samples_to_end=min(samples_to_end,sample_count);
chan_names=read_manager.get_param('channels_names');
self.info_source.set_params(read_manager.get_params());
try
first_sample_ts=read_manager.get_param('first_sample_timestamp');
self.info_source.set_param('first_sample_timestamp',first_sample_ts+start_ts);
end
samples=read_manager.get_samples(samples_to_start,samples_to_end-samples_to_start);
self.set_samples(samples,chan_names);
tags=read_manager.get_tags([],start_ts,(end_ts-start_ts));
self.tags_source.set_tags(tags,0.0);
self.is_initialised=true;
end
end
end
classdef SmartTagDefinition
%SmartTagDefinition is used to create SmartTags, with given parameters
properties
start_tag_name
start_offset=0.0
end_offset=0.0
start_param_func=@(t) t.start_timestamp
end_param_func=@(t) t.start_timestamp
end
methods
function self=SmartTagDefinition(start_tag_name,start_offset,end_offset,start_param_func,end_param_func)
%SMARTTAGDEFINITION(start_tag_name,start_offset=9,
% end_offset=0,start_param_func=@(t) t.start_timestamp,
% end_param_func=@(t) t.start_timestamp)
% start_tag_name - tag_name from which smart tag will start
% start_offset - value added to first_tag.start_time, data is
% taken from extended range
% end_offset - same as start_offset but with the end
% start_param_Func - function used for getting tags
% start_time
% end_param_func - function used for getting end_timestamp
default('end_param_func',@eval,'@(t) t.start_timestamp')
default('start_param_func',@eval,'@(t) t.start_timestamp')
default('end_offset',0.0)
default('start_offset',0.0)
self.start_tag_name=start_tag_name;
self.start_offset=start_offset;
self.end_offset=end_offset;
self.start_param_func=start_param_func;
self.end_param_func=end_param_func;
end
function ans=is_type(self,p_type)
ans=0;
end
function smart_tags=get_smart_tags(self,p_read_manager)
smart_tags=[];
end
end
end
classdef SmartTagDuration < SmartTag
%SmartTagDuration created by SmartTagDurationDefinition
% see help SmartTagDurationDefinition
properties
end
methods
function self=SmartTagDuration(varargin)
self=self@SmartTag(varargin{:});
end
function end_ts=get_end_timestamp(self)
end_ts=self.tag_def.start_param_func(self.start_tag)+self.tag_def.duration+self.tag_def.end_offset;
end
end
end
classdef SmartTagDurationDefinition < SmartTagDefinition
%SmartTagDurationDefinition Get Smart tags with given duration
% The class is to be used for following requirement:
% 'We want to extract bunches of samples starting from some particular
% tag type and lasting x miliseconds.
% It is a constructor parameter for SmartTagsManager.
% Constructor`s parameters and (at the same time) public slots:
% - start_tag_name - string
% - start_offset - float (default 0)
% - end_offset - float (default 0)
% - duration - float
%
% x = SmartTagDuration( duration=100.0
% start_tag_name='ugm_config',
% start_offset=-10.0,
% end_offset=20.0,
% )
%
% Consider samples file f, and tag scattered on the timeline like that:
% ---100ms------------------300ms-----------400ms---------500ms-------------
% ugm_config ugm_config ugm_break ugm_config
%
% Using x definition means:
% Generate following samples bunches:
% - 90ms;220ms
% - 290ms;420ms
% - 490ms;620ms
properties
duration
end
methods
function self=SmartTagDurationDefinition(duration,varargin)
self=self@SmartTagDefinition(varargin{:});
self.duration=duration;
end
function smart_tags=get_smart_tags(self,read_manager)
tags=read_manager.get_tags(self.start_tag_name);
if isempty(tags)
smart_tags=[];
return
end
smart_tags(1,length(tags))=SmartTagDuration();
for i=1:length(tags)
smart_tags(i)=SmartTagDuration(self,tags(i));
smart_tags(i).initialize(read_manager)
end
end
end
end
classdef SmartTagEndTag < SmartTag
%SmartTagEndTag created by SmartTagEndTagDefinition
% see help SmartTagEndTagDefinition
properties %(SetAccess=protected,GetAccess=protected)
end_tag
end
methods
function self=SmartTagEndTag(p_tag_def,p_start_tag)
self=self@SmartTag(p_tag_def,p_start_tag);
end
function set_end_tag(self,p_end_tag)
self.end_tag=p_end_tag;
end
function end_ts=get_end_timestamp(self)
end_ts=self.tag_def.end_param_func(self.end_tag)+self.tag_def.end_offset;