How do I iterate through a struct in an Embedded Matlab Function in Simulink? - matlab

I have hit a roadblock where I am trying to iterate through a structure formed in the MATLAB workspace inside an EML (Embedded Matlab) function block in SIMULINK. Here is some example code:
% Matlab code to create workspace structure variables
% Create the Elements
MyElements = struct;
MyElements.Element1 = struct;
MyElements.Element1.var1 = 1;
MyElements.Element1.type = 1;
MyElements.Element2 = struct;
MyElements.Element2.var2 = 2;
MyElements.Element2.type = 2;
MyElements.Element3 = struct;
MyElements.Element3.var3 = 3;
MyElements.Element3.type = 3;
% Get the number of root Elements
numElements = length(fieldnames(MyElements));
MyElements is a Bus type Parameter for the MATLAB Function Block (EML) in SIMULINK. Below is the area I am running into trouble with. I know the number of elements inside my struct and I know the names, but the number of elements can change with any configuration. So I cannot hardcode based on the Element names. I have to iterate through the struct inside the EML block.
function output = fcn(MyElements, numElements)
%#codegen
persistent p_Elements;
% Assign the variable and make persistent on first run
if isempty(p_Elements)
p_Elements = MyElements;
end
% Prepare the output to hold the vars found for the number of Elements that exist
output= zeros(numElements,1);
% Go through each Element and get its data
for i=1:numElements
element = p_Elements.['Element' num2str(i)]; % This doesn't work in SIMULINK
if (element.type == 1)
output(i) = element.var1;
else if (element.type == 2)
output(i) = element.var2;
else if (element.type == 3)
output(i) = element.var3;
else
output(i) = -1;
end
end
Any thoughts on how I can iterate through a struct type in SIMULINK? Also, I cannot use any extrinsic functions like num2str because this is to be compiled on a target system.

I believe you are trying to use dynamic field names for structures. The correct syntax should be:
element = p_Elements.( sprintf('Element%d',i) );
type = element.type;
%# ...

Related

For loop iteration through .txt files

I have a function that is part of an object that I have name TestData. Here are the properties and its constructor:
classdef TestData
properties
metaData = []; % stores meta data in Nx2 array
data = []; % stores data in PxQ array
colLabels = []; % labels columns
colUnits = []; % provides units
temp = []; % temporary to bypass setters while structuring data
end
methods
%% Constructor
function this = TestData(varargin) % Will take in a variable number of inputs
if (nargin == 0)
return;
end
if (nargin == 1 && ischar(varargin{1})) % Case of the input being a file (.txt, .s*p, etc..)
this = readDataFromFile(this, varargin{1});
return;
elseif (nargin == 4) % Case of the input being raw data. Note that the order matters.
this.metaData = varargin{1};
this.colLabels = varargin{2};
this.colUnits = varargin{3};
this.data = varargin{4};
return
else
error('Not enough input arguments.');
end
end
Now, let us say that I have 42 text files that I want to pass into the object. I have created a function for this called extractData that sifts through the data and finds a user input x-axis and y-axis. It does so by first calling a function readDataFromFile, which sets the property arrays that are outlined in the code above. It is a fairly long function and I won't include it for the time being, but I will say that I do know it is working.
The problem: I make an object of the class called myTestData and perform this operation in the CW, myTestData=myTestData.extractData('Tip Type','Tip S/N'). This will look for the Tip Type and Tip S/N. As you can see in the code, the type and S/N are in column 3 of the matrix. If the string is found, it takes the row it was found on, accesses column 3 of that row, and places that value into the temp array. In my case, the type is passive and the S/N is AM66191. It will write this to the temp array, but then on the second iteration of the for loop it will write something else over the first row and the passive and AM66191 will be displayed on the second row. It will do this all the way until the end, where on row 42 I will see the type and S/N, but all the other rows are just garbage. I have attached an image of what the temp array looks like after the second iteration of the for loop. Here is the code for the extractData function:
function this = extractData(this, xAxis, yAxis)
s = dir('*.txt'); % Gather all text files
disp(length(s))
for i=1:length(s) % Loop through and gather data until last element of strcuct
j = 1;
fid = s(i).name; % Open file in read-only mode
this = this.readDataFromFile(fid);
disp(fid)
x = this.metaData(find(contains(this.metaData(:,1), xAxis)),3);
this.temp(i,j) = x;
disp(this.temp(i,j))
j = j+1;
y = this.metaData(find(contains(this.metaData, yAxis)),3); %#ok<*FNDSB>
this.temp(i,j) = y;
disp(this.temp(i,j))
end %for
end %extractData

Matlab Data Import from a text file

How do i import data of the following format.
1.0,X
.,-1.0
The size of the table may differ. As in the next file can also be a valid input file
.,.,.,1.0
.,X,.,-1.0
.,.,.,.
I have tried doing this using readtable to read the data and then use strcmp to assign the values to a different matrix but it doesnt work. What am i doing wrong.
Code:
foo = readtable(environment_file)
bar = zeros(size(foo,1),size(foo,2));
for i = 1:size(foo,1)
for j = 1:size(foo,2)
if strcmp(foo(i,j),'.')
bar(i,j) = 3;
elseif strcmp(foo(i,j),'X')
bar(i,j) = -10;
elseif strcmp(foo(i,j),'1.0')
bar(i,j) = 1;
elseif strcmp(foo(i,j),'-1.0')
bar(i,j) = -1;
end
end
end
bar
There are two problems in your code. One is that readtable reads the file as a table and takes the first row in the file as the names of the columns. So therefore, you should have the names at the top of the file.
Secondly, to access the contents of the table you need to use {} and not (). Also, there is no need to use for loops as you can use logical indexing to assign values to your matrix bar_b. Therefore, the changed code is as follows:
dataArray = readtable('environment_file.txt');
[n, m] = size(dataArray);
bar_b = zeros(n, m);
bar_b(strcmp(dataArray{:, :}, '.')) = 3;
bar_b(strcmp(dataArray{:, :}, 'X')) = -10;
bar_b(strcmp(dataArray{:, :}, '1.0')) = 1;
bar_b(strcmp(dataArray{:, :}, '-1.0')) = -1;

Define Matlab Class with Attributes of Another Class

I'm trying to construct a class framework for a neural network (ANN) in Matlab by defining a Node class:
function NodeObject = Node(Input, Weights, Activation)
Features.Input = [];
Features.Weights = [];
Features.Activation = [];
Features.Input = Input;
Features.Weights = Weights;
Features.Activation = Activation;
NodeObject = class(Features, 'Node');
Where here the input is an integer (expected number of inputs), Weights is a vector of length Features.Input, and Features.Activation is a string that references an activation function stored in the methods.
What I want to do next is construct a cell array of nodes and define a Network class based on this array:
function Architecture = Network(NodeArray)
ANN.Layers = []; % Number of layers in architecture
ANN.LayerWidths = []; % Vector of length ANN.Layers containing width of each layer
ANN.NodeArray = []; % Original input is cell array with layers in ascending order (input at top, output at bottom) with nodes in each row.
ANN.InputSizes = [];
% Find number of layers
ANN.Layers = length(NodeArray(:,1));
% Find width of each layer
Widths = zeros(ANN.Layers,1);
for i = 1:length(Widths)
Widths(i) = length(NodeArray(:,i));
end
ANN.LayerWidths = Widths;
% Pass NodeArray to Network class
ANN.NodeArray = NodeArray;
% Construct cell of input sizes
InputSizes = [];
for i = 1:ANN.Layers
for j = 1:Widths(i)
InputSizes(i,j) = NodeArray{i,j}.Inputs;
end
end
ANN.InputSizes = InputSizes;
Architecture = class(ANN, 'Network');
The attribute ANN.InputSizes tries to extract the attributes from a Node object, but my code doesn't allow me to do this. How do I amend this problem, or do you recommend a different architecture to this problem all together? Currently my classes Node and Network are contained in two separate directories, but I have a feeling that there is something else I'm not seeing. For reference, I have absolutely no prior experience in OOP, and from what I've gathered it seems Matlab is not the best environment in which to implement these structures. At the moment though I don't have enough experience to implement this type of framework in another language.
Your InputSizes isn't a cell. You initialize it to a double array ([]) and then fill it as such. If you want to define it as a cell you should do something like
InputSizes = cell();
for i = 1:ANN.Layers
for j = 1:Widths(i)
InputSizes{i,j} = NodeArray{i,j}.Inputs;
end
end
All of that, you should really look into defining your classes using a classdef file, as it is much more straightforward.
Node.m
classdef Node < handle
properties
Inputs
Weights
Activation
end
methods
function obj = Node(inputs, weights, activation)
obj.Inputs = inputs;
obj.Weights = weights;
obj.Activation = activation;
end
end
end
Network.m
classdef Network < handle
properties
NodeArray
end
properties (Dependent)
Layers
LayerWidths
InputSizes
end
methods
function obj = Network(nodes)
obj.NodeArray = nodes;
end
function result = get.Layers(obj)
result = size(obj.NodeArray, 1);
end
function result = get.LayerWidths(obj)
result = size(obj.NodeArray, 2);
end
function result = get.InputSizes(obj)
result = arrayfun(#(x)x.Inputs, obj.NodeArray, 'uniformoutput', 0);
end
end
end
As far as suggesting a better layout, that's subject to the opinion of the individual developer.

How to adjust the size of block.outputport.data in Matlab?

I have tried to generate a square pulsed clock. But it gives error. I tried this:
function pll( block)
setup(block);
function setup(block)
% Register number of ports
block.NumInputPorts = 1;
block.NumOutputPorts = 1;
% Override input port properties
block.InputPort(1).Dimensions = 1;
block.InputPort(1).DatatypeID = 8; % boolean
block.InputPort(1).Complexity = 'Real';
block.InputPort(1).DirectFeedthrough = false;
% Override output port properties
block.OutputPort(1).Dimensions = 1;
block.OutputPort(1).DatatypeID = 0; %double
block.OutputPort(1).Complexity = 'Real';
block.NumDialogPrms = 1;
block.DialogPrmsTunable = 0;
ts = 1/24000000'; %'
block.sample times= [ts 0];
block.SimStateCompliance = 'DefaultSimState'
function Outputs(block)
t = [0:1/(24000000):0.000001];
l = 0.1*exp(-6);
c = 220*exp(-9) + 60*exp(-9);
f = 1/(2*pi*sqrt(l*c));
block.OutputPort(1).Data = square(2*pi*f*t);
function Terminate(block)
But it gives me the error
"Error evaluating registered method 'Outputs' of M-S-Function 'pll' in
'untitled/Level-2 M-file S-Function'. Invalid assignment in
'untitled/Level-2 M-file S-Function': attempt to assign a vector of
width 24001 to a vector of width 1."
the error indicates on the line
block.OutputPort(1).Data = square(2*pi*f*t);
so what can be done to overcome this error?
It seems from your example that you're not really familiar with the way Simulink works. At each time step, each block in a Simulink model outputs a value (i.e the block's output value) corresponding to the current simulation time. In your case, within the block.Output function you are trying to output all time points at every simulation time step.
It appears that what you really want is to replace
t = [0:1/(24000000):0.000001];
with
t = block.CurrentTime;
And replace
block.OutputPort(1).Data = square(2*pi*f*t);
with
block.OutputPort(1).Data = sign(sin(2*pi*f*t));
Also, some other things to consider:
you don't seem to be registering the block's output method using:
block.RegBlockMethod('Outputs',#Output);
Why have you defined the block to have an input when it doesn't seem to require one?
Why are you doing this in an S-Function when a From Workspace block (or one of the many other ways to get data into a model) would seem to make more sense?

Read multiple files with for loop

My code is below. In the code, I am evaluating only the data in the 'fb2010' file. I want to add other files" 'fb2020', 'fb2030', and 'fb2040' and evaluate their data by the same code. My question is how to apply a for loop and include the other data files. I tried, but I got confused by the for loop.
load('fb2010'); % loading the data
x = fb2010(3:1:1502,:);
% y_filt = filter(b,a,x); % filtering the received signal
y_filt= filter(b,a,x,[],2);
%%%%%%% fourier transform
nfft = length(y_filt);
res = fft(y_filt,nfft,2)/nfft;
res2 = res(:,1:nfft/2+1); %%%% taking single sided spectrum
res3 = fft(res2,[],2);
for i = 3:1:1500 %%%% dividing each row by first row.
resd(i,:) = res3(i,:)./res3(1,:);
end
I'm assuming that your files are MAT-files, not ASCII. You can do this by having load return a struct and using dynamic field referencing:
n = 4;
for i = 1:n
vname = ['fb20' int2str(i) '0']; % Create file/variable name based on index
s = load(vname); % Load data as struct (overwriting previous s)
x = s.(vname)(3:1:1502,:); % Access struct with dynamic field reference
% Rest of your code
...
end
If you're using a plain ASCII file, load won't produce a struct. However, such files are much simpler (see documentation for load/save). The following code would probably work:
n = 4;
for i = 1:n
vname = ['fb20' int2str(i) '0']; % Create file/variable name based on index
s = load(vname); % Load data as matrix (overwriting previous s)
x = s(3:1:1502,:); % Directly index matrix
% Rest of your code
...
end
It would be a good idea to add the file extension to your load command to make your code more readable.