Matlab - using a function mulitple times in the same workspace, to add values and fields to a structure - matlab

I have a structure such as:
specimen.trial1 = 1
I now want to add another trial to the specimen, so that
specimen.trial1 = 1
specimen.trial2 = 2
I can do this without a problem within the workspace and command window. But, if I'm using a function to calculate the numbers for each trial (with dynamic fields), the new field and value erases the previous one. Eg:
function [specimen] = dummy(trial,value)
specimen.(trial) = value
end
run the function:
[specimen] = dummy('trial1',1)
then run the function again with different inputs, but keeping the structure intact in the workspace
[specimen] = dummy('trial2',2)
Instead of getting a structure with 2 fields, I get just one with Trial2 being the only field. Does that make any sense? What would like is to use the outputs of a function to progressively add to a structure.
Thank you,
Chris

Yes it makes sense, because you're creating a new struct specimen within your function.
Solution: pass the the previous specimen to the function as well.
function [specimen] = dummy(specimen,trial,value)
specimen.(trial) = value
end
and call:
[specimen] = dummy(specimen,'trial1',1)
or alternativly leave out the assignment at all and use the following
function [output] = dummy(value)
output = value
end
and call:
[specimen.trail1] = dummy(1)
which really depends on what you actually want to do. Put passing a name to a function which uses this name to define a struct is a little pointless unless you "use" that name otherwise. Also if you want to have input-dependent dynamic names you'd also go with the first alternative

Related

Creating a structure within a structure with a dynamic name

I have large data sets which i want to work with in matlab.
I have a struct called Trail containing serveral structures called trail1, trail2 ...
which then contain several matrices. I now want to add another point to for instance trail1
I can do that with Trail.trail1.a2rotated(i,:) = rotpoint'; the problem is that i have to do it in a loop where the trail number as well as the a2rotated changes to e.g. a3rot...
I tired to do it like that
name ="trail"+num2str(z)+".a2rotated"+"("+i+",:)";
name = convertStringsToChars(name);
Trail.(name) = rotpoint'
But that gives me the error: Invalid field name: 'trail1.a2rotated(1,:)'.
Does someone have a solution?
The name in between brackets after the dot must be the name of a field of the struct. Other indexing operations must be done separately:
Trail.("trail"+z).a2rotated(i,:)
But you might be better off making trail(z) an array instead of separate fields with a number in the name.

Create a mask in Simulink with edit field as enumeration

I want to create a mask on a subsystem, like the mask of the Enumerated Constant (shown below). As you see in the blue circle, the Value can be edited using a drop down list of possibilities. The Enumerated Constant
If creating a mask it is indeed possible to make a Popup instead of an Edit but the problem with this is that the list of possible selections needs to be manually created inside the mask. What I want is that this Edit value only shows the possible selections of a certain enumeration that I want to specify only ones. The Enumerated Constant mask does this with an Edit type but even then it works.
Of course I tried to reverse engineer this from this block but I can not find how Matlab does this.
I am not sure if my initial question was quite clear if I read it now. The enumerals should be taken from an enumerated type from a Data Dictionary. So based on the answers of Julian and DrBuck I could answer my own question.
I first get the enumerated type from the data dictionary.
% set name of DD and type
DDName = 'types.sldd';
EventType = 'Dem_EventIdType';
% Get DD entry
myDictionaryObj = Simulink.data.dictionary.open(DDName);
dDataSectObj = getSection(myDictionaryObj,'Design Data');
entryObj = getEntry(dDataSectObj,EventType,'DataSource',DDName);
entryValue = getValue(entryObj);
Then I get the enumerals and add the event type to it. This will be used to fill the pop-up options.
% Get enumerals
NoOfEvents = length(entryValue.Enumerals);
for i = 1:NoOfEvents
EventIDs{i,1} = [EventType '.' entryValue.Enumerals(i).Name];
end
After this I used the above proposed code to manipulate the created pop-up menu with the enumerals from the Data Dictionary.
maskObj= Simulink.Mask.get(gcb);
par_name = 'Value_eventID'; % name
par_idx = find(strcmp({maskObj.Parameters.Name},par_name)); % index of parameter
maskObj.Parameters(par_idx).TypeOptions = EventIDs; % enum options
I do this not in the 'Initialization Commands' but in the callback function of the 'Refresh events' button. When the block is added from the library it only contains the INVALID_EVENT. After a refresh it does a fresh look-up and adds the current enumerals to the list.
My end result:
I couldn't quite figure out how to do this but hopefully this answer should get you on the right track.
Create two popups on the mask, eg 'popup1' and 'popup2'. Hardcode your type options to popup1, and leave popup2 blank and disabled (because it's dependent on what you select with popup1). You can then use a callback on popup1 to populate popup2 when the first one is clicked. Your callback would look something like this:
% Grab the value selected from popup1
value = get_param(gcb, 'popup_1');
% Do some sort of check/switch statement to set your options
if value == 1
% Enable popup2
set_param(gcb, 'MaskEnables', {'on', 'on'});
% Set the type options for popup2
set_param(gcb, 'MaskStyleString', 'popup(1|2|3|4|5),popup(my|new|options)');
end if
Have a look at this and this in the Matlab/Simulink documentation. get_param and set_param are useful if a bit unintuitive. It looks like in later versions of Simulink you can use maskObj = Simulink.Mask.create(gcb); methods to do this sort of stuff, which might be easier.
as DrBuck sugessted, you should use maskObj = Simulink.Mask.create(gcb) to create a mask for the currently selected block (gcb) or select an already existing mask by maskObj=Simulink.Mask.get(gcb). Then, add parameters to it:
par_name = 'foo'; % name
par_prompt = 'This is my enum constant'; %prompt
maskObj.addParameter('Prompt',par_prompt,'Name',par_name); % add parameter
The field you are looking for is called TypeOptions, but first you must find the correct parameter number by e.g.
par_idx = find(strcmp({maskObj.Parameters.Name},par_name)); % index of parameter
Set the mask parameter to 'popup' and create your enum values:
maskObj.Parameters(par_idx).Type = 'popup'; % type
maskObj.Parameters(par_idx).TypeOptions = {'Option1','Option2'}; % enum options
There you are ;)
HTH Julian

Calculations in table based on variable names in matlab

I am trying to find a better solution to calculation using data stored in table. I have a large table with many variables (100+) from which I select smaller sub-table with only two observations and their difference for smaller selection of variables. Thus, the resulting table looks for example similarly to this:
air bbs bri
_________ ________ _________
test1 12.451 0.549 3.6987
test2 10.2 0.47 3.99
diff 2.251 0.078999 -0.29132
Now, I need to multiply the ‘diff’ row with various coefficients that differ between variables. I can get the same result with the following code:
T(4,:) = array2table([T.air(3)*0.2*0.25, T.bbs(3)*0.1*0.25, T.bri(3)*0.7*0.6/2]);
However, I need more flexible solution since the selection of variables will differ between applications. I was thinking that better solution might be using either varfun or rowfun and speficic function that would assign correct coefficients/equations based on variable names:
T(4,:) = varfun(#func, T(3,:), 'InputVariables', {'air' 'bbs' 'bri'});
or
T(4,:) = rowfun(#func, T(3,:), 'OutputVariableNames', T.Properties.VariableNames);
However, the current solution I have is similarly inflexible as the basic calculation above:
function [air_out, bbs_out, bri_out] = func(air, bbs, bri)
air_out = air*0.2*0.25;
bbs_out = bbs*0.1*0.25;
bri_out = bri*0.7*0.6/2;
since I need to define every input/output variable. What I need is to assign in the function coefficients/equations for every variable and the ability of the function to apply it only to the variables that are present in the specific sub-table.
Any suggestions?

Auto generate a name when saving file in Matlab?

I am working on a GUI and I have a file named 'work.mid'. The user can make some modifications to it and when they click the save button I want it to be saved as 'work1.mid' to 'c:\saved_datas\'. When they click that button second time, it should save it as 'work2.mid', on the third time 'work3.mid' and so on. Here's the code I have so far:
nmat = readmidi_java('work.mid');
Name = fullfile('c:\saved_datas\', '?????');
writemidi_java(nmat, Name);
Figuring out what should go at ????? is where I'm stuck.
The following code would work if you have no prior work*.mid or if you have any number of sequential work*.mid files inside c:\saved_datas\. Now, if the files are not in sequence, this code could be tweaked for that, just let me know if you would like to handle that case too.
Code listed here -
%// Parameters
org_filename = 'work.mid';
main_dir = 'c:\saved_datas\'; %//'
%// Your code
nmat = readmidi_java(org_filename);
%// Added code
[~,filename_noext,ext] = fileparts(org_filename)
filenames = ls(strcat(main_dir,filename_noext,'*',ext))
new_filename = strcat(filename_noext,num2str(size(filenames,1)+1),ext)
Name = fullfile(main_dir,new_filename)
%// Your code
writemidi_java(nmat, Name);
For achieving uniqueness of filenames, some also use timestamps. This could be implemented like this -
org_filename = 'work.mid'; %//'
main_dir = 'c:\saved_datas\'; %//'
[~,filename_noext,ext] = fileparts(org_filename)
new_filename = strcat('filename_noext','-',datestr(clock,'yyyy-mm-dd-hh-MM-SS'),ext)
Name = fullfile(main_dir,new_filename);
This could be done a couple of ways depending on how you have structured your GUI. You need to keep track of how many times the button has been pressed. In the callback for the button you could use a persistent variable ('count') and increment it by one at the start of the function. Then construct the filename with filename = ['work' num2str(count) '.mid']. Alternatively you could increment a class member variable if you have implemented your GUI using OOP.
To save the file use the 'save()' function with the previously constructed file name and a reference to the variable.
Check out the documentation for persistent variables, save, fullfile and uiputfile for extra info.

Matlab: dynamic name for structure

I want to create a structure with a variable name in a matlab script. The idea is to extract a part of an input string filled by the user and to create a structure with this name. For example:
CompleteCaseName = input('s');
USER WRITES '2013-06-12_test001_blabla';
CompleteCaseName = '2013-06-12_test001_blabla'
casename(12:18) = struct('x','y','z');
In this example, casename(12:18) gives me the result test001.
I would like to do this to allow me to compare easily two cases by importing the results of each case successively. So I could write, for instance :
plot(test001.x,test001.y,test002.x,test002.y);
The problem is that the line casename(12:18) = struct('x','y','z'); is invalid for Matlab because it makes me change a string to a struct. All the examples I find with struct are based on a definition like
S = struct('x','y','z');
And I can't find a way to make a dynamical name for S based on a string.
I hope someone understood what I write :) I checked on the FAQ and with Google but I wasn't able to find the same problem.
Use a structure with a dynamic field name.
For example,
mydata.(casename(12:18)) = struct;
will give you a struct mydata with a field test001.
You can then later add your x, y, z fields to this.
You can use the fields later either by mydata.test001.x, or by mydata.(casename(12:18)).x.
If at all possible, try to stay away from using eval, as another answer suggests. It makes things very difficult to debug, and the example given there, which directly evals user input:
eval('%s = struct(''x'',''y'',''z'');',casename(12:18));
is even a security risk - what happens if the user types in a string where the selected characters are system(''rm -r /''); a? Something bad, that's what.
As I already commented, the best case scenario is when all your x and y vectors have same length. In this case you can store all data from the different files into 2 matrices and call plot(x,y) to plot each column as a series.
Alternatively, you can use a cell array such that:
c = cell(2,nufiles);
for ii = 1:numfiles
c{1,ii} = import x data from file ii
c{2,ii} = import y data from file ii
end
plot(c{:})
A structure, on the other hand
s.('test001').x = ...
s.('test001').y = ...
Use eval:
eval(sprintf('%s = struct(''x'',''y'',''z'');',casename(12:18)));
Edit: apologies, forgot the sprintf.