Let us now discuss the object-oriented workflow of the VIBES Toolbox. Object-oriented programming (OOP) is a popular paradigm in software development, which is perfectly suited for sound & vibration analysis in MATLAB. It is not necessary to be an expert in the programming part; instead, it is essential to know how to use the pre-designed classes of the VIBES Toolbox for your purpose.
To start, we will have a look into the vibes.TimeSeries class. Objects from this class can be seen as a representation of a stream of time data, which has multiple channels with (measurement) quantities, a time vector with length and sampling rate, and perhaps some descriptive properties. In standard MATLAB use, this probably means having several variables in your workspace, each representing some part of the abovementioned information. Concerning data representation, the vibes.TimeSeries object can be seen as a struct that contains all this data under one variable.
vibes.TimeSeries
struct
% Create a TimeSeries object TS = vibes.TimeSeries();&amp;lt;/code&amp;gt;</pre> % Create a time vector for 0-8 sec @ 1000 Hz fs = 1000; dt = 1/fs; t = 0:dt:8; % Create two sine waves with random noise y = sin(2*pi*[45; 250]*t) + 0.1*rand(2,numel(t)); % Assign the data to the TimeSeries object TS.Time = t; TS.Data = y; % Set a name to the dataset TS.Name = 'Sample data'; % Clear all variables except TS clearvars -except TS
We have now created some data and put it into the TS variable, which is of class &lt;code&gt;vibes.TimeSeries&lt;/code&gt;, and cleared all variables that we used in the meantime (just for the sake of illustration). You can display the object and see a summary of the object structure.
% Display the object in the command window disp(TS)
The object properties are shown organized in categories so that one can quickly read the important information from the display. Some properties, such as the Time vector, are not shown in full: by clicking on All properties these are revealed too.
All properties
While constructing the vibes.TimeSeries object, two vibes.Channel objects have been created too. These fields contain descriptive information on the rows of the data array, which are the (measurement) channels. Let us inspect these too:
vibes.Channel
TS.Channels
It is good practice to provide complete channel information, including quantities and units, which can be done by setting the properties of the channels. We will use different approaches to do that:
% Get the channels as a separate variable ch = TS.Channels; % Set properties of each channel individually ch(1).Description = 'Sine wave at 45 Hz'; ch(2).Description = 'Sine wave at 250 Hz'; % Assign a single value to both channels ch.set('Quantity','Acceleration'); % Disperse different values over the two channels ch.setArray('NodeNumber',[1 2]); % Also set the position and directions ch.setArray('Position',{[1 0 -0.4];[1 0 -0.4]},'Direction',{'+X';'-Z'}); % Display the effect for the channels in the TimeSeries object TS.listChannels
The fact that these changes are seen in TS may come as a surprise, as the channels were not re-assigned into the TS variable. This reveals the very nature of the object-oriented way of working: the variable ch is actually a reference to the channels that are hosted in the object TS, meaning that the ones in the object and the separate are the same. This is checked by comparing their so-called handles:
ch
disp(TS.Channels == ch)
Most classes in the VIBES Toolbox are indeed handle classes, which all follow this behavior. Obvious exceptions are all simple types such as double, char and struct, which are value-typed by nature. Some implications are shown here:
handle
double
char
% Assignment to a new variable > still the same object TS2 = TS; disp(TS2 == TS) % Copy to an actual new instance > creates a new object TS3 = TS.copy(); disp(TS3 == TS) % The channels are handle classes, so these are still the same "references" disp(TS3.Channels == TS.Channels) % If really needed (often not), a deep copy also creates copies for the % direct handle classes TS4 = TS.deepCopy(); disp(TS4.Channels == TS.Channels)
Often-used methods such as vibes.TimeSeries/crop will create a new instance if an output variable is provided; otherwise, the operation is performed on the original object. For subset selection vibes.TimeSeries/subs is a synonym for vibes.TimeSeries/crop, but it creates a separate instance regardless of output.
vibes.TimeSeries/crop
vibes.TimeSeries/subs
% Crop the original object to the interval of 3-5.5 seconds TS.crop([],[3 5.5]); % Get a subset for only channel 2 TS_ch2 = TS.subs(2);
Slowly, we have moved into using a second strong suit of objects, which is their class-specific operations known as “methods”. Methods are just functions, but they operate on objects and can be called by the syntax out = obj.methodname(arg1,arg2,...). Let us review the methods available for the vibes.TimeSeries class:
out = obj.methodname(arg1,arg2,...)
methods(TS)
Most of the methods have their own documentation, which is best found by typing vibes.doc and pressing TAB to activate auto-completion, for instance:
vibes.doc
vibes.doc('vibes.TimeSeries.plot') % More concise help is available in the command window using the familiar % help command: help vibes.TimeSeries.plotSumLevel
Let us now try some methods on the object. The following lines should be rather self-explaining:
% Create a "named" figure and clear vibes.figure2d('Time Series'); clf % Plot channel 1 TS.plot(1); % Also plot a sum-level of channel 1 (with default settings) hold on [h,Opts] = TS.plotSumLevel(1); legend show % Play-back the audio over the left and right audio channel TS.play([1 2]) % Create a summed variant over the channel (first) direction TSsum = sum(TS,1); % Merge into the original set TS = cat([TS; TSsum]);
Example of a time series plot
You can now see how one does not need to provide “boiler-plate” code such as xlabel, ylabel, title nor any for loops over matrix dimensions: the VIBES Toolbox does this for you based on the data and meta-data set in the object.
xlabel
ylabel
title
Many classes in the VIBES Toolbox can convert to each other using methods that start with from* and to*. Here is a typical example to get to the frequency domain in two steps:
from*
to*
% Slice into time blocks of 0.5 sec > creates a TimeBlocks object TB = TS.toTimeBlocks([],0.5); % Compute FFT with a hann window and for 0 to 500 Hz > creates a FreqBlocks object FB = TB.toFreqBlocks('hann',0:500); % Review result disp(FB)
Of course, the resulting vibes.FreqBlocks object has its own methods for plotting. Although the syntax is quite similar, the plots are very different:
vibes.FreqBlocks
% Plot the second channel with sine at 250Hz vibes.figure2d('Spectra'); clf % Plot a 3D waterfall diagram of the summed (3rd) channel subplot(2,1,1) FB.plot3d(3) % Plot a 2D spectrum of the first block subplot(2,1,2) FB.plot(3,1,'label','') % Also add an averaged spectrum over all blocks and a sum level hold on FB.plot(3,[],'averaging','mean','label',': ') FB.plot(3,[],'plottype','sum','averaging','mean','label',': ') ylim([1e-4 1e1])
Example of a frequency blocks plot
VIBES objects can be loaded and saved from and to custom locations using load and save methods. A repository system can be used to locate files on relative paths for easy project management and sharing. Have a look at the following functions and methods:
vibes.browse
vibes.load
vibes.fullfile
vibes.TimeSeries/save
VIBES objects can be exported for further post-processing in SOURCE. However, SOURCE cannot interpret the object data format that VIBES objects are saved in the Toolbox. So, they need to be saved in the MATLAB format.
To export your objects to SOURCE, add the '-native','-v6' extension to the save command:
'-native','-v6'
YA.save('VIBES,Examples,Export','YA','-native','-v6');
Contact us for more VIBES
Contact our support team or call us on +31 85 822 50 49
Subscribe to our newsletter
Privacy statement. By submitting this form you agree to having your details registered in our database. At your request, we'll remove your details from our database immediately *