image thumbnail
from Pulse with jitter by Phil Goddard
Level-2 m-code S-Function that acts as a pulse generator with jitter around a nominal period.

jitterPulseSfun(block)
function jitterPulseSfun(block)
%jitterPulse
%
% This is a level-2 m-code S-Function that acts as a pulse generator with
% jitter.
%
% The user specifies
% 1) a nominal period for the pulse (i.e. the nominal time between rising
% pulses)
% 2) the time that the pulse may be (randomly) delayed or advanced at each
% time step.  The block assumes a nominal 50% duty cycle, hence this must
% be less than 1/4 of the nominal period.
% 3) whether the pulse is initially high or low.
%

% Author: Phil Goddard (phil@goddardconsulting.ca)
% Date: Q1, 2009

%%
%% The setup method is used to setup the basic attributes of the
%% S-function such as ports, parameters, etc. Do not add any other
%% calls to the main body of the function.
%%
setup(block);

%endfunction

%% Function: setup ===================================================
%% Abstract:
%%   Set up the S-function block's basic characteristics such as:
%%   - Input ports
%%   - Output ports
%%   - Dialog parameters
%%   - Options
%%
%%   Required         : Yes
%%   C-Mex counterpart: mdlInitializeSizes
%%
function setup(block)

% Register number of ports
block.NumInputPorts  = 0;
block.NumOutputPorts = 1;

% Setup port properties to be inherited or dynamic
%block.SetPreCompInpPortInfoToDynamic;
block.SetPreCompOutPortInfoToDynamic;

% Override input port properties
%block.InputPort(1).DatatypeID  = 0;  % double
%block.InputPort(1).Complexity  = 'Real';

% Override output port properties
block.OutputPort(1).DatatypeID  = 0; % double
block.OutputPort(1).Complexity  = 'Real';
block.OutputPort(1).SamplingMode  = 0; % no frames

% Register parameters
block.NumDialogPrms     = 3;
block.DialogPrmsTunable = {'Nontunable','Nontunable','Nontunable'};

% Register sample times
%  [0 offset]            : Continuous sample time
%  [positive_num offset] : Discrete sample time
%
%  [-1, 0]               : Inherited sample time
%  [-2, 0]               : Variable sample time
block.SampleTimes = [-2 0];

%% -----------------------------------------------------------------
%% Options
%% -----------------------------------------------------------------
% Specify if Accelerator should use TLC or call back into
% M-file
block.SetAccelRunOnTLC(false);

%% -----------------------------------------------------------------
%% The M-file S-function uses an internal registry for all
%% block methods. You should register all relevant methods
%% (optional and required) as illustrated below. You may choose
%% any suitable name for the methods and implement these methods
%% as local functions within the same file.
%% -----------------------------------------------------------------

block.RegBlockMethod('CheckParameters', @CheckPrms);
block.RegBlockMethod('PostPropagationSetup', @DoPostPropSetup);
block.RegBlockMethod('Start', @Start);
block.RegBlockMethod('Outputs', @Outputs);
block.RegBlockMethod('Update', @Update);

%% -------------------------------------------------------------------
%% The local functions
%% -------------------------------------------------------------------

function CheckPrms(block)

% The period must positive
period = block.DialogPrm(1).Data;
if ~strcmp(class(period), 'double') || (period <=0)
    DAStudio.error('Simulink:block:invalidParameter');
end

% The advance/delay must be < 25% of the period
window = block.DialogPrm(2).Data;
if ~strcmp(class(window), 'double') || ...
        (period <=0) || ...
        (window >= period/4)
    DAStudio.error('Simulink:block:invalidParameter');
end

% The initial value must be 0 or 1
initVal = block.DialogPrm(3).Data;
if ~strcmp(class(initVal), 'double') || ...
        ~(abs(initVal-0)<2*eps || abs(initVal-1)<2*eps)
    DAStudio.error('Simulink:block:invalidParameter');
end

function DoPostPropSetup(block)
block.NumDworks = 3;

% The first work vector stores the current state
block.Dwork(1).Name            = 'x0';
block.Dwork(1).Dimensions      = 1;
block.Dwork(1).DatatypeID      = 0;      % double
block.Dwork(1).Complexity      = 'Real'; % real
block.Dwork(1).UsedAsDiscState = true;

% The second work vector is used to store the next nominal sample time
block.Dwork(2).Name            = 'NextNomTs';
block.Dwork(2).Dimensions      = 1;
block.Dwork(2).DatatypeID      = 0;      % double
block.Dwork(2).Complexity      = 'Real'; % real
block.Dwork(2).UsedAsDiscState = true;

% The third work vector is used to store the half the nominal period
block.Dwork(3).Name            = 'HalfNomP';
block.Dwork(3).Dimensions      = 1;
block.Dwork(3).DatatypeID      = 0;      % double
block.Dwork(3).Complexity      = 'Real'; % real
block.Dwork(3).UsedAsDiscState = false;

function Start(block)

% Populate the Dwork vectors
block.Dwork(1).Data = block.DialogPrm(3).Data;
block.Dwork(2).Data = (block.DialogPrm(1).Data)/2;
block.Dwork(3).Data = (block.DialogPrm(1).Data)/2;

function Outputs(block)

% output the first state (i.e. 0 or 1)
block.OutputPort(1).Data = block.Dwork(1).Data;

% Calculate the time of the next sample hit, which is the next nominal time
% plus a random term the size of the user specified window.
block.NextTimeHit = block.Dwork(2).Data + ...
    2*(rand-0.5)*block.DialogPrm(2).Data;

function Update(block)

% toggle the first state (i.e. 0 or 1)
if abs(block.Dwork(1).Data) > 2*eps
    block.Dwork(1).Data = 0;
else
    block.Dwork(1).Data = 1;
end

% determine the next nominal sample time
block.Dwork(2).Data = block.Dwork(2).Data + block.Dwork(3).Data;


Contact us at files@mathworks.com