필터 지우기
필터 지우기

Passing C++ Handles in Simulink

조회 수: 6 (최근 30일)
Taylor
Taylor 2023년 1월 15일
답변: Taylor 2023년 1월 16일
Hi All,
I need a method to generate random numbers from within a Simulink foreach block for a set of simuated sensors. It is extremely cumbersome to seed each noise block individually, so I'm looking for a way to create a persistent random engine and pass a pointer to the user blocks. I originally created an s-function with the following class, registered he "get()" and "getWhite()" functions, and was calling them with the function caller block, but this method fails when I try to run the simulation in a compiled accelerator mode.
class RandomGenerator {
private:
unsigned long rand_seed;
std::default_random_engine engine;
std::uniform_real_distribution<double> distribution;
public:
double get();
double getWhite(double mu, double sigma);
RandomGenerator(unsigned long n);
};
Is there a clean s-function method that would allow me to pass a pointer to the get or getWhite function and subsequently call the function?
Any suggestions would be extremely helpful.
Thanks,
Taylor

채택된 답변

Taylor
Taylor 2023년 1월 16일
Playing around with it a bit, I came up with a fairly messy way to imlement this by passing a pointer to the pwork vector in the model outputs function. It seems like there must be a safer solution in Simulink though...
#define S_FUNCTION_NAME sfun_RandomStream
#define S_FUNCTION_LEVEL 2
#include "simstruc.h"
#include <random>
#include "RandomNumberStream.h"
enum params {SEED_idx, NPARAMS};
enum outputs {STREAMPTR_idx, NOUT};
static int tmp = 0;
static void mdlInitializeSizes(SimStruct *S)
{
int_T status;
DTypeId id;
tmp = 0;
ssSetNumSFcnParams(S, params::NPARAMS); //seed
if (ssGetNumSFcnParams(S) != ssGetSFcnParamsCount(S)) {
return; /* Parameter mismatch will be reported by Simulink */
}
if (!ssSetNumInputPorts(S, 0)) return;
if (!ssSetNumOutputPorts(S,outputs::NOUT)) return;
ssSetOutputPortWidth(S, outputs::STREAMPTR_idx, 1);
id = ssRegisterDataType(S, "voidPtr");
if(id == INVALID_DTYPE_ID) return;
status = ssSetDataTypeSize(S, id, sizeof(voidPtr));
if(status == 0) return;
ssSetOutputPortDataType(S, outputs::STREAMPTR_idx, id);
ssSetNumSampleTimes(S, 1);
ssSetNumPWork(S,1);
ssSetOperatingPointCompliance(S, USE_DEFAULT_OPERATING_POINT);
ssSetOptions(S,
SS_OPTION_WORKS_WITH_CODE_REUSE |
SS_OPTION_EXCEPTION_FREE_CODE);
}
#define MDL_START
static void mdlStart(SimStruct *S)
{
double seed = (double) mxGetScalar(ssGetSFcnParam(S, SEED_idx));
//printf("SEED: %i\n", seed);
RandomGenerator *generator = new RandomGenerator(seed);
(ssGetPWork(S))[0] = (void *)(generator);
}
static void mdlInitializeSampleTimes(SimStruct *S)
{
ssSetSampleTime(S, 0, INHERITED_SAMPLE_TIME);
ssSetOffsetTime(S, 0, 0.0);
ssSetModelReferenceSampleTimeDefaultInheritance(S);
}
static void mdlOutputs(SimStruct *S, int_T tid)
{
voidPtr pwork = ssGetPWork(S)[0];
voidPtr *out = (voidPtr *)ssGetOutputPortSignal(S, 0);
//void *tmp = (void *) out[0];
out[0] = pwork;
// printf("randStream mdlOutputs address: %p\n", out[0]);
}
static void mdlTerminate(SimStruct *S)
{
delete ( RandomGenerator *)ssGetPWorkValue(S,0);
}
#ifdef MATLAB_MEX_FILE /* Is this file being compiled as a MEX-file? */
#include "simulink.c" /* MEX-file interface mechanism */
#else
#include "cg_sfun.h" /* Code generation registration function */
#endif
%implements "sfun_RandomStream" "C"
%function BlockTypeSetup(block, system) void
%% The Target Language must be C
%if ::GenCPP!=1 && !IsModelReferenceSimTarget()
%<LibReportFatalError("Random Number Generator must be implemented in c++.")>
%endif
%<LibAddToCommonIncludes("RandomNumberStream.h")>
%endfunction %% BlockTypeSetup
%function Start(block, system) Output
/* %<Type> Block: %<Name> */
%assign seed = LibBlockParameterValue(block.Parameter[0], 0)
RandomGenerator *stream = new RandomGenerator((double) %<seed>);
%<LibBlockPWork(0, "", "", 0)> = stream;
%endfunction
%function Outputs(block, system) Output
/* %<Type> Block: %<Name> */
%assign pwork = LibBlockPWork(0, "", "", 0)
%assign out = LibBlockOutputSignal(0, "", "", 0)
%<out> = %<pwork>;
%endfunction
%function Terminate(block, system) Output
%switch SFunctionType
%case "TLC"
/* %<Type> Block: %<Name> */
%assign u = LibBlockPWork(0, "", "", 0)
RandomGenerator *stream = (RandomGenerator *)%<u>;
delete stream;
%break
%endswitch
%endfunction
#define S_FUNCTION_NAME sfun_NormalDist
#define S_FUNCTION_LEVEL 2
#include "simstruc.h"
#include <random>
#include "RandomNumberStream.h"
enum params {TS_idx, SIGMA_idx, MU_idx, NPARAMS};
enum outputs {VAL_idx, NOUT};
enum inputs {STREAM_idx, NINP};
static void mdlInitializeSizes(SimStruct *S)
{
int_T status;
DTypeId id;
ssSetNumSFcnParams(S, params::NPARAMS); /* Number of expected parameters */
if (ssGetNumSFcnParams(S) != ssGetSFcnParamsCount(S)) {return; }
ssSetNumContStates(S, 0);
ssSetNumDiscStates(S, 0);
if (!ssSetNumInputPorts(S, inputs::NINP)) return;
ssSetInputPortWidth(S, inputs::STREAM_idx, 1);
//DTypeId id = ssGetDataTypeId(S, "voidPtr");
id = ssRegisterDataType(S, "voidPtr");
if(id == INVALID_DTYPE_ID) return;
status = ssSetDataTypeSize(S, id, sizeof(voidPtr));
if(status == 0) return;
if(id != ssSetInputPortDataType(S, inputs::STREAM_idx, id)) return;
ssSetInputPortDirectFeedThrough(S, inputs::STREAM_idx, 1);
if (!ssSetNumOutputPorts(S, outputs::NOUT)) return;
ssSetOutputPortWidth(S, outputs::VAL_idx, 1);
ssSetNumSampleTimes(S, 1);
ssSetNumRWork(S, 0);
ssSetNumIWork(S, 0);
ssSetNumPWork(S, 0);
ssSetNumModes(S, 0);
ssSetNumNonsampledZCs(S, 0);
ssSetOperatingPointCompliance(S, USE_DEFAULT_OPERATING_POINT);
//ssSetRuntimeThreadSafetyCompliance(S, RUNTIME_THREAD_SAFETY_COMPLIANCE_TRUE);
//ssSetOptions(S, SS_OPTION_EXCEPTION_FREE_CODE);
ssSupportsMultipleExecInstances(S, true);
}
static void mdlInitializeSampleTimes(SimStruct *S)
{
double ts = (double) mxGetScalar(ssGetSFcnParam(S, TS_idx));
ssSetSampleTime(S, 0, ts);
ssSetOffsetTime(S, 0, 0.0);
}
static void mdlOutputs(SimStruct *S, int_T tid)
{
voidPtr **inPtr = ((voidPtr**) ssGetInputPortSignal(S, inputs::STREAM_idx));
if(inPtr == NULL)
return;
voidPtr in = inPtr[0][0];
RandomGenerator *engine = static_cast<RandomGenerator*>(in);
if(engine == NULL) //deal with sample time issues...
return;
double sigma = (double) mxGetScalar(ssGetSFcnParam(S, SIGMA_idx));
double mu = (double) mxGetScalar(ssGetSFcnParam(S, MU_idx));
std::normal_distribution<double> distribution(mu, sigma);
double val = distribution(engine->getEngine());
double *out = (double*) ssGetOutputPortSignal(S, outputs::VAL_idx);
out[0] = val;
}
static void mdlTerminate(SimStruct *S)
{
}
/*=============================*
* Required S-function trailer *
*=============================*/
#ifdef MATLAB_MEX_FILE /* Is this file being compiled as a MEX-file? */
#include "simulink.c" /* MEX-file interface mechanism */
#else
#include "cg_sfun.h" /* Code generation registration function */
#endif
%implements "sfun_NormalDist" "C"
%function BlockTypeSetup(block, system) void
%% The Target Language must be C
%if ::GenCPP!=1 && !IsModelReferenceSimTarget()
%<LibReportFatalError("Random Number Generator must be implemented in c++.")>
%endif
%<LibAddToCommonIncludes("RandomNumberStream.h")>
%endfunction %% BlockTypeSetup
%function Outputs(block, system) Output
/* %<Type> Block: %<Name> */
%assign pu = LibBlockInputSignalAddr(0, "", "", 0)
%assign y = LibBlockOutputSignal(0, "", "", 0)
RandomGenerator *engine = (RandomGenerator *)(*%<pu>);
%assign sigma = LibBlockParameterValue(block.Parameter[1], 0)
%assign mu = LibBlockParameterValue(block.Parameter[2], 0)
std::normal_distribution<double> distribution((double) %<mu>, (double) %<sigma>);
double val = distribution(engine->getEngine());
%<y> = val;
%endfunction

추가 답변 (0개)

카테고리

Help CenterFile Exchange에서 Simulink Coder에 대해 자세히 알아보기

제품


릴리스

R2021b

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!

Translated by