Algorithm testing via simulation in Scilab/Xcos
Started by 4 years ago●11 replies●latest reply 4 years ago●563 viewsHello,
I have been developing a library of control blocks (PID controller, various filters etc.) in C programming language. My goal is to have basic building stones for control software development. Before implementing those building stones I would like to verify the algorithms via a simulation. It seems to me that appropriate way how to do that is the S-function block in Matlab/Simulink. Unfortunately I don't have Matlab. So I have started to look for some alternative and I have found the Scilab/Xcos - open source alternative for Matlab/Simulink. The Xcos toolbox (alternative for Simulink) offers the so called C_BLOCK which enables to insert C code into the Xcos simulation. I haven't found any example regarding usage of this block. So I have started to experiment with it and I have developed a Ramp filter block:
#include
#include
void Ramp(flag,nevprt,t,xd,x,nx,z,nz,tvec,
ntvec,rpar,nrpar,ipar,nipar
,u1,nu1,y1,ny1)
double *t,xd[],x[],z[],tvec[];
int *flag,*nevprt,*nx,*nz,*ntvec,*nrpar,ipar[],*nipar,*nu1,*ny1;
double rpar[],u1[],y1[];
/* modify below this line */
{
static double target = 0;
static double inputDelta = 0;
static double out = 0;
if(u1[0] != target)
{
target = u1[0];
if(target - y1[0] < 0)
{
inputDelta = y1[0] - target;
}
else
{
inputDelta = target - y1[0];
}
}
if(target > y1[0])
{
out += inputDelta*rpar[2]/rpar[0];
if(out > target)
{
out = target;
}
}
else if(target < y1[0])
{
out -= inputDelta*rpar[2]/rpar[1];
if(out < target)
{
out = target;
}
}
y1[0] = out;
}
This C_BLOCK works but I have hit to a problem how to create multiple instances of a given C_BLOCK (each one with different set of parameters) in one simulation. I have done a naive experiment with a copy of a given C_BLOCK (so I have two instances of the Ramp filter and each one has different set of parameters) but this resulted in strange behavior when both instances produce same ouput but this output does not correspond to any set of parameters.
Does anybody know how to have several instances of one Scilab/Xcos C_BLOCK in one simulation?
#Scilab, #c, #Matlab
I have no spcific knowledge about using C blocks in xcos, but it seems to me the problem must lie with using static variables within your block to save system states.
A second instance of the block would read and store target, inputDelta and out to the identical memory location.
There must be some other way to store system states - probably one of the arrays or pointers handed to the function should be used...
Hello dudelsound,
thank you for your reply. You are probably correct.
I have found that the Scilab/Xcos offers several variants of the "C block". One of them is called CBLOCK4. I have read the available documentation and it seems to me that the void **work item of the scicos_block structure is intended to be used for storage of the address of the dynamically allocated memory which shall be used for system state. I have attempted to implement the Ramp filter with CBLOCK4 usage:
#include "scicos_block4.h"
#define U ((double *)GetRealInPortPtrs(block, 1))
#define Y ((double *)GetRealOutPortPtrs(block, 1))
// parameters
#define Tu (GetRparPtrs(block)[0])
#define Td (GetRparPtrs(block)[1])
#define T (GetRparPtrs(block)[2])
void Ramp(scicos_block *block, int flag)
{
double *target;
double *inputDelta;
double *out;
if(flag == 4)
{
/* init */
*(block->work) = (double *)scicos_malloc(sizeof(double)*3);
target = (double*)(*(block->work));
inputDelta = (double*)(*(block->work + 1));
out = (double*)(*(block->work + 2));
*target = 0;
*inputDelta = 0;
*out = 0;
}
else if(flag == 1)
{
/* output computation */
if(U[0] != *target)
{
*target = U[0];
if(*target - Y[0] < 0)
{
*inputDelta = Y[0] - *target;
}
else
{
*inputDelta = *target - Y[0];
}
}
if(*target > Y[0])
{
*out += *inputDelta*T/Tu;
if(*out > *target)
{
*out = *target;
}
}
else if(*target < Y[0])
{
*out -= *inputDelta*T/Td;
if(*out < *target)
{
*out = *target;
}
}
Y[0] = *out;
}
else if (flag == 5)
{
/* ending */
scicos_free(*(block->work));
}
}
The
compilation process was successfull but as soon as I had run the
simulation the simulation crashed. I know that you don't have experience
with C blocks in xcos but do you see at first glance any problem in the
C code above?
yes, the lines
inputDelta = (double*)(*(block->work + 1));
out = (double*)(*(block->work + 2));
increment the pointer block->work by the size of the type of block->work, not by the size of a double (=8 bytes)
Thanks. I have attempted to fix it in below given manner:
inputDelta = ((double*)(*block->work) + 1);
out = ((double*)(*block->work) + 2);
Do you think it is correct i.e. that the inputDelta variable contains address of the second double allocated and similar for the out variable?
yes, I think it does - but I might err
I have fixed the bug and run the simulation again. Unfortunately it still crashes.
*(block->work) = (double *)scicos_malloc(sizeof(double)*3);
should probably be
block->work = (double *)scicos_malloc(sizeof(double)*3);
I have tested what you have suggested above but without success.
on a closer look, the entire code has pointer/value problems. For instance:
target = (double*)(*(block->work));
inputDelta = (double*)(*(block->work + 1));
out = (double*)(*(block->work + 2));
you dereference the pointer block->work (you take the value stored at that location) and reinterpret it as a pointer to a double value. I think what you are trying to do is use the double value stored at block->work. Your code assumes, you stored an address to some value at block->work. also target is a pointer in your code - it shouldn't be - it should be a value:
double target;
...
target = *((double*)block->work);
this code inits a variable named 'target'. Then it assigns a double value stored at address block->work to that variable.
You should really evaluate in every line, if you are using a pointer or a value and if that is correct...
The intended idea behind my code is that I allocate a memory for three double data types and then pass addresses of those three memory locations into pointers (target, inputDelta, out). Then in the rest of the code I work with the content at the addresses in individual pointers.
I have got up and running that with below given modification of the c code:
#include "scicos_block4.h"
#define U ((double *)GetRealInPortPtrs(block, 1))
#define Y ((double *)GetRealOutPortPtrs(block, 1))
// parameters
#define Tu (GetRparPtrs(block)[0])
#define Td (GetRparPtrs(block)[1])
#define T (GetRparPtrs(block)[2])
typedef struct
{
double target;
double inputDelta;
double out;
}Ramp_work;
void Ramp(scicos_block *block, int flag)
{
Ramp_work *work;
if(flag == 4)
{
/* init */
if((*(block->work) = (Ramp_work*)scicos_malloc(sizeof(Ramp_work))) == NULL)
{
set_block_error(-16);
return;
}
work = *(block->work);
work->target = 0;
work->inputDelta = 0;
work->out = 0;
}
else if(flag == 1)
{
work = *(block->work);
/* output computation */
if(U[0] != work->target)
{
work->target = U[0];
if(work->target - Y[0] < 0)
{
work->inputDelta = Y[0] - work->target;
}
else
{
work->inputDelta = work->target - Y[0];
}
}
if(work->target > Y[0])
{
work->out += work->inputDelta*T/Tu;
if(work->out > work->target)
{
work->out = work->target;
}
}
else if(work->target < Y[0])
{
work->out -= work->inputDelta*T/Td;
if(work->out < work->target)
{
work->out = work->target;
}
}
Y[0] = work->out;
}
else if (flag == 5)
{
/* ending */
scicos_free(*(block->work));
}
}