ToFile from SImulink read into C++ standalone program
Show older comments
Looking for sample C or C++ code that will successfully extract MATFILE data created by Simulink's "To File" block. It is time series data, apparently. The sample reader program, matdgns.c, does identify that a single variable, called "ans", is in the MATFILE but I am unable to get the values out into an ordinary C array. "mxGetNumberOfDimensions" indicates "2" which suggests that data is present. Also, the file is 193K bytes, which also indicates that there is data present. However, mxIsEmpty(arr) is returning TRUE.
Answers (4)
James Tursa
on 9 Aug 2018
Edited: James Tursa
on 10 Aug 2018
Time series objects are classdef OOP objects. To get at the underlying data in them, you will need to use the mxGetProperty API function. This, unfortunately, will produce a deep data copy. If your data is large you might want to explore using the mxGetPropertyPtr function from the FEX. E.g.,
/* Echo timeseries Time and Data values to the screen */
#include "mex.h"
#include <string.h>
int mxIsTimeSeries(const mxArray *mx)
{
int result = 0;
if( mx ) {
result = strcmp(mxGetClassName(mx),"timeseries") == 0;
}
return result;
}
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
mxArray *Time, *Data;
double *t, *d;
int i, n;
if( nrhs && mxIsTimeSeries(prhs[0]) ) {
Time = mxGetProperty(prhs[0],0,"Time"); /* makes deep data copy */
if( Time ) {
t = mxGetPr(Time);
n = mxGetNumberOfElements(Time);
for( i=0; i<n; i++ ) {
mexPrintf("t[%d] = %f\n",i,t[i]);
}
mxDestroyArray(Time);
}
Data = mxGetProperty(prhs[0],0,"Data"); /* makes deep data copy */
if( Data ) {
d = mxGetPr(Data);
n = mxGetNumberOfElements(Data);
for( i=0; i<n; i++ ) {
mexPrintf("d[%d] = %f\n",i,d[i]);
}
mxDestroyArray(Data);
}
}
}
* EDIT *
Try this for standalone code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "matrix.h"
#include "mat.h"
int mxIsTimeSeries(const mxArray *mx)
{
int result = 0;
if( mx ) {
result = strcmp(mxGetClassName(mx),"timeseries") == 0;
}
return result;
}
int main(int argc, char **argv)
{
MATFile *pmat;
int i, n;
mxArray *pa, *Time, *Data;
double *t, *d;
/* If no input argument, do nothing */
if (argc == 0 ) return 0;
/* Open the mat file */
pmat = matOpen(argv[0], "r");
if (pmat == NULL) {
printf("Error opening file %s\n", argv[0]);
return 1;
}
/* Read the desired variable */
pa = matGetVariable(pmat,"ans");
if (pa == NULL) {
printf("Error reading variable 'ans'\n");
matClose(pmat);
return 1;
}
/* Check to see if it is a timseries object */
if( !mxIsTimeSeries(pa) ) {
printf("Error variable 'ans' is not a timeseries object\n");
mxDestroyArray(pa);
matClose(pmat);
return 1;
}
/* Process the data */
Time = mxGetProperty(pa,0,"Time");
if( Time ) {
t = mxGetPr(Time);
n = mxGetNumberOfElements(Time);
for( i=0; i<n; i++ ) {
printf("t[%d] = %f\n",i,t[i]);
}
mxDestroyArray(Time);
} else {
printf("Error variable 'ans' does not have a 'Time' property\n");
}
Data = mxGetProperty(pa,0,"Data");
if( Data ) {
d = mxGetPr(Data);
n = mxGetNumberOfElements(Data);
for( i=0; i<n; i++ ) {
printf("d[%d] = %f\n",i,d[i]);
}
mxDestroyArray(Data);
} else {
printf("Error variable 'ans' does not have a 'Data' property\n");
}
/* Done */
mxDestroyArray(pa);
if (matClose(pmat) != 0) {
printf("Error closing file %s\n",argv[0]);
return 1;
}
printf("Done\n");
return 0;
}
7 Comments
Elliott Rachlin
on 9 Aug 2018
James Tursa
on 9 Aug 2018
Edited: James Tursa
on 9 Aug 2018
My example code is for a mex function. For the matdgns.c example, that is a standalone program with main() function so no you would not be using the mexFunction interface. You would use the body of my mexFunction but you would use pa instead of prhs[0]. Unfortunately, for standalone code you cannot use the mxGetPropertyPtr function from the FEX since that only works for mex functions ... you may be stuck with the mxGetProperty API function.
Elliott Rachlin
on 9 Aug 2018
Elliott Rachlin
on 9 Aug 2018
James Tursa
on 9 Aug 2018
A small data file would help, yes.
James Tursa
on 10 Aug 2018
I think the simplest thing for you to do is to use the matdgns.c file as the starting point for your code. Use everything in the code except the "Diagnose array pa" section. Replace that code with this:
mxArray *Time, *Data;
double *t, *d;
int n;
:
/*
* Diagnose array pa
*/
Time = mxGetProperty(pa,0,"Time");
t = mxGetPr(Time);
Data = mxGetProperty(pa,0,"Data");
d = mxGetPr(Data);
n = mxGetNumberOfElements(Time);
:
/* insert code here to use t and d vector data */
:
mxDestroyArray(Data);
mxDestroyArray(Time);
mxDestroyArray(pa);
Elliott Rachlin
on 10 Aug 2018
Elliott Rachlin
on 9 Aug 2018
0 votes
1 Comment
James Tursa
on 9 Aug 2018
Use the paper clip icon to attach a file.
Elliott Rachlin
on 10 Aug 2018
0 votes
2 Comments
Elliott Rachlin
on 10 Aug 2018
James Tursa
on 10 Aug 2018
I loaded the mat file into MATLAB and saw data there, but have not put together a C program to read it. I can do that tomorrow.
Elliott Rachlin
on 10 Aug 2018
0 votes
21 Comments
Elliott Rachlin
on 13 Aug 2018
James Tursa
on 14 Aug 2018
Edited: James Tursa
on 14 Aug 2018
"With the new stand-alone code example, both "Time" and "Data" are still coming back NULL."
Strange. I used this exact code (albeit called from a mex routine) and got the data printed to the screen using your provided mat file.
"... Also, the two references to argv[0] should both be argv[1] …"
My bad … again the result of me calling it directly from a mex routine (calling main) and passing in the filename as the first argv without thinking.
Lemme post the mex routine tomorrow to see if that works for you and then we can go from there to figure out how to get the standalone code to work. The mex code is a very simple wrapper that has a hard-coded filename that it passes to main(), so pretty trivial to write.
Elliott Rachlin
on 14 Aug 2018
James Tursa
on 14 Aug 2018
Here is the mex routine I used to read your mat file, and following that is a sample run I made. Everything seems to work OK with this setup. See if it works for you. The frontend mexFunction is just a very simple wrapper to call main().
/* bare bones limited checking */
#include "mex.h"
int main(int argc, char **argv);
void mexFunction( int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
char *matname[2] = {"mexFunction","COM2A_To_File_In.mat"};
main(2,matname);
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "matrix.h"
#include "mat.h"
int mxIsTimeSeries(const mxArray *mx)
{
int result = 0;
if( mx ) {
result = strcmp(mxGetClassName(mx),"timeseries") == 0;
}
return result;
}
int main(int argc, char **argv)
{
MATFile *pmat;
int i, n;
mxArray *pa, *Time, *Data;
double *t, *d;
/* If no filename input argument, do nothing */
if (argc < 2 ) return 0;
/* Open the mat file */
pmat = matOpen(argv[1], "r");
if (pmat == NULL) {
printf("Error opening file %s\n", argv[1]);
return 1;
}
/* Read the desired variable */
pa = matGetVariable(pmat,"ans");
if (pa == NULL) {
printf("Error reading variable 'ans'\n");
matClose(pmat);
return 1;
}
/* Check to see if it is a timseries object */
if( !mxIsTimeSeries(pa) ) {
printf("Error variable 'ans' is not a timeseries object\n");
mxDestroyArray(pa);
matClose(pmat);
return 1;
}
/* Process the data */
Time = mxGetProperty(pa,0,"Time");
if( Time ) {
t = mxGetPr(Time);
n = mxGetNumberOfElements(Time);
for( i=0; i<n; i++ ) {
printf("t[%d] = %f\n",i,t[i]);
}
mxDestroyArray(Time);
} else {
printf("Error variable 'ans' does not have a 'Time' property\n");
}
Data = mxGetProperty(pa,0,"Data");
if( Data ) {
d = mxGetPr(Data);
n = mxGetNumberOfElements(Data);
for( i=0; i<n; i++ ) {
printf("d[%d] = %f\n",i,d[i]);
}
mxDestroyArray(Data);
} else {
printf("Error variable 'ans' does not have a 'Data' property\n");
}
/* Done */
mxDestroyArray(pa);
if (matClose(pmat) != 0) {
printf("Error closing file %s\n",argv[0]);
return 1;
}
printf("Done\n");
return 0;
}
And the sample run:
>> mex read_timeseries_main.c
>> read_timeseries_main
t[0] = 0.000000
t[1] = 1.000000
t[2] = 1.000000
t[3] = 2.000000
t[4] = 2.000000
t[5] = 3.000000
t[6] = 3.000000
t[7] = 4.000000
t[8] = 4.000000
t[9] = 5.000000
t[10] = 5.000000
t[11] = 6.000000
t[12] = 6.000000
t[13] = 7.000000
t[14] = 7.000000
t[15] = 8.000000
t[16] = 8.000000
t[17] = 9.000000
t[18] = 9.000000
t[19] = 10.000000
t[20] = 10.000000
d[0] = 1.000000
d[1] = 2.000000
d[2] = 1.000000
d[3] = 2.000000
d[4] = 1.000000
d[5] = 2.000000
d[6] = 1.000000
d[7] = 2.000000
d[8] = 1.000000
d[9] = 2.000000
d[10] = 1.000000
d[11] = 2.000000
d[12] = 1.000000
d[13] = 2.000000
d[14] = 1.000000
d[15] = 2.000000
d[16] = 1.000000
d[17] = 2.000000
d[18] = 1.000000
d[19] = 2.000000
d[20] = 1.000000
Done
>> ts = load('COM2A_To_File_In');
>> ts.ans.Time
ans =
0
1
1
2
2
3
3
4
4
5
5
6
6
7
7
8
8
9
9
10
10
>> ts.ans.Data
ans =
1
2
1
2
1
2
1
2
1
2
1
2
1
2
1
2
1
2
1
2
1
Elliott Rachlin
on 14 Aug 2018
James Tursa
on 14 Aug 2018
Edited: James Tursa
on 14 Aug 2018
This is a mex function, not a standalone program. The entry point to the mex function is the function mexFunction ... i.e. that is what MATLAB will call upon entry to this dll. In this setup, main() is just another arbitrary function ... it is not the entry point. We are first just trying to easily get code to read your mat file, so compile this as a mex routine and call it from the MATLAB command line. I would like to see this work first before we move on to your standalone program. So do this at the MATLAB command prompt and see what happens:
mex read_timeseries_main.c
read_timeseries_main
Elliott Rachlin
on 14 Aug 2018
James Tursa
on 14 Aug 2018
But when you pull the wrapper out and just do the main() stuff as a standalone it doesn't read the file properly? How do you build the standalone?
Elliott Rachlin
on 14 Aug 2018
Elliott Rachlin
on 14 Aug 2018
Elliott Rachlin
on 15 Aug 2018
James Tursa
on 15 Aug 2018
Edited: James Tursa
on 15 Aug 2018
Nobody should be calling mexFunction in your standalone because that code shouldn't even be there in your standalone! Everything above #include stdio.h in my posted code should be deleted from your standalone source code. In particular, you should not be including mex.h in your standalone and you shouldn't need some hacked up version of it ... mex.h is only for mex routines that have mexFunction etc for calling from MATLAB directly. You also shouldn't be linking in libmex.lib.
Elliott Rachlin
on 15 Aug 2018
James Tursa
on 15 Aug 2018
Edited: James Tursa
on 15 Aug 2018
You are compiling only the stuff from #include stdio.h and down, and only linking in libmx.lib and libmat.lib? And you are trying to read the exact mat file you posted here? File opens and 'ans' passes the timeseries class test, but no Time or Data properties present?
Elliott Rachlin
on 15 Aug 2018
Elliott Rachlin
on 15 Aug 2018
Elliott Rachlin
on 15 Aug 2018
Elliott Rachlin
on 15 Aug 2018
James Tursa
on 15 Aug 2018
I don't understand either of your 1. and 2. comments above. Mex routines certainly do call mexFunction since that is the entry point for all mex routines called from MATLAB. And standalone programs should not need mex.h included since that stuff is only for the functions that call back into MATLAB which standalone programs don't do.
I need to think about this overnight ...
Elliott Rachlin
on 15 Aug 2018
Elliott Rachlin
on 20 Aug 2018
Categories
Find more on Write C Functions Callable from MATLAB (MEX Files) in Help Center and File Exchange
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!