%% Create Moving Average System object
% This example shows how to create a System object™ that implements a moving average filter.
%% Introduction
% System objects are MATLAB classes that derive from matlab.System. As a result, System objects all inherit a common public interface, which includes standard methods:
% setup — Initialize the object, typically at the beginning of a simulation
% reset — Clear the internal state of the object, bringing it back to its default post-initialization status
% release —  Release any resources (memory, hardware, or OS-specific resources) used internally by the object
% When you create new kinds of System objects, you provide specific implementations for all the preceding methods to determine its behavior.
% In this example, you create and use the movingAverageFilter System object. movingAverageFilter is a System object that computes the unweighted mean of the previous WindowLength input samples. WindowLength is the length of the moving average window. movingAverageFilter accepts single-precision and double-precision 2-D input matrices. Each column of the input matrix is treated as an independent (1-D) channel. The first dimension of the input defines the length of the channel (or the input frame size). movingAverageFilter independently computes the moving average of each input channel over time.
%% Create the System object File
% In the MATLAB Home tab, create a new System object class by selecting New > System Object > Basic. The basic template for a System object opens in the MATLAB editor to guide you as you create the movingAverageFilter System object.
% Rename the class movingAverageFilter and save the file as movingAverageFilter.m. To ensure you can use the System object, make sure you save the System object in a folder that is on the MATLAB path.
% For your convenience, a complete movingAverageFilter System object file is available with this example. To open the completed class, enter:

edit movingAverageFilter.m

%% Add Properties
% This System object needs four properties. First, add a public property WindowLength to control the length of the moving average window. Because the algorithm depends on this value being constant once data processing begins, the property is defined as nontunable. Additionally, the property only accepts real, positive integers. To ensure input satisfies these conditions, add property validators (see docid:matlab_oop#bu3l65b). Set the default value of this property to 5.
% properties(Nontunable)
%     WindowLength (1,1){mustBeInteger,mustBePositive} = 5
% end
% Second, add two properties called State and pNumChannels.  Users should not access either property, so use the Access = private attribute.  State saves the state of the moving average filter. pNumChannels stores the number of channels in your input.  The value of this property is determined from the number of columns in the input.
% properties(Access = private)
%     State;
%     pNumChannels = -1;
% end
% Finally, you need a property to store the FIR numerator coefficients. Add a property called pCoefficients. Because the coefficients do not change during data processing and you do not want users of the System object to access the coefficients, set the property attributes as Nontunable, Access = private.
% properties(Access = private, Nontunable)
%     pCoefficients
% end
%% Add Constructor for Easy Creation
% The System object constructor is a method that has the same name as the class (movingAverageFilter in this example). You implement a System object constructor to allow name-value pair inputs to the System object with the setProperties method. For example, with the constructor, users can use this syntax to create an instance of the System object: filter = movingAverageFilter('WindowLength',10). Do not use the constructor for anything else. All other setup tasks should be written in the setupImpl method.
% methods
%     function obj = movingAverageFilter(varargin)
%         % Support name-value pair arguments when constructing object
%         setProperties(obj,nargin,varargin{:})
%     end
% end
%% Set Up and Initialization in setupImpl
% The setupImpl method sets up the object and implements one-time initialization tasks. For this System object, modify the default setupImpl method to calculate the filter coefficients, the state, and the number of channels. The filter coefficients are computed based on the specified WindowLength. The filter state is initialized to zero. (Note that there are WindowLength-1 states per input channel.) Finally, the number of channels is determined from the number of columns in the input.
% For setupImpl and all Impl methods, you must set the method attribute Access = protected because users of the System object do not call these methods directly. Instead the back-end of the System object calls these methods through other user-facing functions.
% function setupImpl(obj,x)
%     % Perform one-time calculations, such as computing constants
%     obj.pNumChannels = size(x,2);
%     obj.pCoefficients = ones(1,obj.WindowLength)/obj.WindowLength;
%     obj.State = zeros(obj.WindowLength-1,obj.pNumChannels,'like',x);
% end
%% Define the Algorithm in stepImpl
% The object's algorithm is defined in the stepImpl method. The algorithm in stepImpl is executed when the user of the System object calls the object at the command line. In this example, modify stepImpl to calculate the output and update the object's state values using the filter function.
% function y = stepImpl(obj,u)
%     [y,obj.State] = filter(obj.pCoefficients,1,u,obj.State);
% end
%% Reset and Release
% The state reset equations are defined in the resetImpl method. In this example, reset states to zero. Additionally, you need to add a releaseImpl method. From the Editor toolstrip, select Insert Method > Release resources. The releaseImpl method is added to your System object. Modify releaseImpl to set the number of channels to -1, which allows new input to be used with the filter.
% function resetImpl(obj)
%     % Initialize / reset discrete-state properties
%     obj.State(:) = 0;
% end
% function releaseImpl(obj)
%     obj.pNumChannels = -1;
% end
%% Validate Input
% To validate inputs to your System object, you must implement a validateInputsImpl method. This method validates inputs at initialization and at each subsequent call where the input attributes (such as dimensions, data type, or complexity) change. From the toolstrip, select Insert Method > Validate inputs. In the newly inserted validateInputsImpl method, call validateattributes to ensure that the input is a 2-D matrix with floating-point data.
% function validateInputsImpl(~, u)
%     validateattributes(u,{'double','single'}, {'2d',...
%         'nonsparse'},'','input');
% end
%% Object Saving and Loading
% When you save an instance of a System object, saveObjectImpl defines what property and state values are saved in a MAT-file. If you do not define a saveObjectImpl method for your System object class, only public properties and properties with the DiscreteState attribute are saved. Select Insert Method > Save in MAT-file. Modify saveObjectImpl so that if the object is locked, the coefficients and number of channels is saved.
% function s = saveObjectImpl(obj)
%     s = saveObjectImpl@matlab.System(obj);
%     if isLocked(obj)
%         s.pCoefficients = obj.pCoefficients;
%         s.pNumChannels = obj.pNumChannels;
%     end
% end
% loadObjectImpl is the companion to saveObjectImpl because it defines how a saved object loads. When you load the saved object, the object loads in the same locked state. Select Insert Method > Load from MAT-file. Modify loadObjectImpl so that if the object is locked, the coefficients and number of channels are loaded.
% function loadObjectImpl(obj,s,wasLocked)
%     if wasLocked
%         obj.pCoefficients = s.pCoefficients;
%         obj.pNumChannels = s.pNumChannels;
%     end
%     loadObjectImpl@matlab.System(obj,s,wasLocked);
% end
%
%% Use movingAverageFilter in MATLAB
% Now that you have defined the System object, you can use the object in MATLAB. For example, use movingAverageFilter to remove noise from a noisy pulse sequence.

movingAverageFilter = movingAverageFilter('WindowLength',10);

t = (1:250)';
signal = randn(250,1);
smoothedSignal = movingAverageFilter(signal);

plot(t,signal,t,smoothedSignal);
legend(["Input","Smoothed input"])

%
%% Extend Your System object for Simulink
% To use your System object in Simulink, see docid:simulink_ug#mw_161d6e4e-97f5-44c5-b8c1-a9f6519d4fc3.
% Copyright 2019 The MathWorks, Inc.