function varargout = dspblksfw2(action, varargin)
% DSPBLKSFW2 DSP Blockset Signal From Workspace block helper function.

% Copyright 1995-2000 The MathWorks, Inc. 
% $Revision: 1.18 $ $Date: 2000/06/07 20:27:16 $

if nargin==0, action = 'dynamic'; end

fullblk       = getfullname(gcb);
FromWorks_blk = [fullblk ['/From Workspace']];
ExtendOutput  = get_param(fullblk, 'OutputAfterFinalValue');

ExtendOutput(find(isspace(ExtendOutput)))=[];  % Simulink does not use spaces

switch action
case 'init'
   % Setting Simulink FromWorkspace parameter
   if ~strcmp(get_param(FromWorks_blk, 'OutputAfterFinalValue'), ExtendOutput)
       set_param(FromWorks_blk,'OutputAfterFinalValue', ExtendOutput);
   end

   varargout = initBlock(fullblk, FromWorks_blk, ExtendOutput, varargin);   
   break;
end  % end switch/case



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function argsOut = initBlock(fullblk, FromWorks_blk, ExtendOutput, argsIn)

   X      = argsIn{1};
   Ts     = argsIn{2};
   nSamps = argsIn{3};
   
   % Set Frame Status Conversion block
   setOutputFrameStatus(fullblk, X, nSamps);
   
   if isempty(X) | isempty(Ts) | isempty(nSamps), 
       % If any of the fields are empty then assign defaults
       % This let's us hit the apply button with parameters
       % yet to be defined in the MATLAB workspace.
       s.time               = [];
       s.signals.values     = 0;
       s.signals.dimensions = [1 1];   
       
       newTs  = Ts;  % Sample time of Simulink FromWorkspace block
       
       % If you are about to run, the all params must be defined.
       if(strcmp(get_param(gcs,'SimulationStatus'),'initializing'))
       		if isempty(X),      error('Workspace variable must not be empty'); end
       		if isempty(Ts),     error('Sample time must not be empty');        end
       		if isempty(nSamps), error('Samples per frame must not be empty');  end
       end       
       
   else
       if nSamps <= 0,
           error('Samples per frame must be > 0.');
       end
       
       if Ts <= 0,
           error('Samples time must be > 0.');
       end

       % Prepare data and add buffer block if needed
       s = PrepareDataForFromWorkspace(fullblk, X, nSamps, ExtendOutput);
              
       if(exist_block(fullblk, 'Buffer'))
           newTs = Ts;             % Don't change sample time
       else
           newTs = Ts * nSamps;    % Update sample time for output:
       end
   end
   
   argsOut(1:2) = {s, newTs};

   
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function  s = PrepareDataForFromWorkspace(fullblk, X, nSamps, ExtendOutput)
   
if ndims(X)==3
    % 3D inputs can be passed directly to the underlying
    % Simulink FromWorkspace block.
    if nSamps ~= 1,
        error('Samples per frame must be 1 for 3 dimensional inputs.');
    end
    
    [outRows,outCols,steps] = size(X);
    UU = X;         
else      
    % 1 or 2D inputs needs to be reshaped to be passed
    % to the underlying Simulink FromWorkspace block.
    % The variable to the Simulink FromWorkspace block (UU) is
    % made to be 3D, so that each frame is output as a 2D page.
    [UU, outCols] = reshape2D(X,nSamps);
    outRows = nSamps;
    
    [xRows, xCols] = size(X);
    
    % Row is a single Channel (Convenience rule)
    if(xRows==1)
        xRows = xCols;
        X = X(:);
    end
    
    switch ExtendOutput
    case 'SettingToZero'
        % Nothing extra needed for this case
        remove_Buffer(fullblk);
        
    case 'CyclicRepetition'
    	% If using Cyclic Repetion, the we need the workspace  variable 
    	% to have the it's number of rows to be an even multiple of the 
    	% framesize.  Because the current implementation would erroneously 
    	% zero pad the output and repeat the last zero padded frame.

       if(xRows > nSamps)
            R = mod(xRows, nSamps);
        else
            R = nSamps - xRows;
        end
        
        if( (R == 0) | nSamps == 1)
            % Samplebased or signals with length that are multiple
            % of the framesize do not need the buffer block.
            remove_Buffer(fullblk);
            
        else
            % Framebased signals that have lengths which are not 
            % multiples of the frameszie need the buffer block.
            % We'll pass the variable to the FromWorkspace block
            % which will output samples one row at a time to the buffer.
            
            [m,n,p] = size(UU);

            if(xRows > nSamps)
                IC = UU(:,:,1)';    % First frame to go into IC
                
                % Stuff rows into the 3rd dimension
                % The buffer block will construct the frame
                % from these samplebased rows.
                for j = 1:p
                	for i = 1:m
                   		UUU(:,:,i+(j-1)*m) = UU(i,:,j);
                	end
            	end
                UU = UUU;
                
                UU(:,:,end-nSamps+R+1:end) = [];                      % Remove padded zeros
                UU = cat(3, UU(:,:,nSamps+1:end), UU(:,:,1:nSamps));  % Shift first frame to end

            else
                % Input does not fill a full frame
                rep = ceil(nSamps / xRows);              % Time to duplicate input in frame
                UU  = repmat(UU(1:xRows,:),rep, 1);      % Copy data into frame, but not zero pad
                IC  = UU(1:nSamps,:)';                   % IC equals first frame
                UU  = [UU(nSamps+1:end,:); UU(1:R,:)];   % Circular shift data to beginning

                % Suff rows into 3rd dimension
                for j = 1:p
                	for i = 1:xRows
                   		UUU(:,:,i+(j-1)*xRows) = UU(i,:,j);
                	end
            	end
                UU = UUU;
               
            end

            outRows = 1;  % We are feeding samplebased rows to the buffer block
            outCols = n;  % Number of channels stays the same
            
            insert_Buffer(fullblk, IC);
            
        end
    
    case 'HoldingFinalValue'	
    	% We have more work to do if HOLD FINAL VALUE is selected
    	% samples per frame is greater than one.  Fill out the remaining
    	% frame with the last value and then build an additioal frame
    	% full of final values to be repeated.

        remove_Buffer(fullblk);

        lastRow = X(end,:); % This is the values to hold
        
        if(xRows > nSamps)
            % Input has one full frame and a partially full frame
            R = mod(xRows, nSamps);
            if(R > 0)
                % Fill last part of frame with last value
                pad = ones(nSamps-R,1) * lastRow;
                UU(R+1:end,:,end) = pad;
            end
            % Create a full frame of last values
            lastFrame = ones(nSamps,1) * lastRow;
            UU(:,:,end+1) = lastFrame;
            
        else
            % There will always be a remainder of the frame
            % that must be filled because the signal length
            % is less than the frame length in this case.
            R = nSamps-xRows;
            if(R > 0)                   
                % Fill last part of frame with last value
                pad = ones(R,1) * lastRow;
                UU(end-R+1:end,:) = pad;
            end
            % Create a full frame of last values
            lastFrame = ones(nSamps,1) * lastRow;
            UU = cat(3, UU, lastFrame);
        end
    end  % End of Switch/case
    
end

% Form structure input for Simulink From Workspace block
s.time               = [];
s.signals.values     = UU;
s.signals.dimensions = [outRows outCols];

   
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function [UU, nChans] = reshape2D(X,nSamps)

% Reshape data
[m,n] = size(X);
if(m==1 | n==1),
   U = X(:);  % Input is a vector, force it to be a column (single channel)
else 
   U = X;     % Assume multiple columns = multiple channels
end

nChans = size(U,2);

if (nSamps == 1)
   % Single sample per channel 
   % Move  rows into 3D matrix
   for j=1:size(U,1)
      UU(:,:,j) = U(j,:);
   end
   
else
   
   if (m==1 | n==1)
      % VECTOR input to create new 3D output:
      for i=1:nChans,
         V(:,:,i) = buffer(U(:,i),nSamps);
      end
      V = num2cell(V,[1 2]);
      U = cat(1,V{:}).';  % one time step per row
      
      for j=1:size(U,1)
         UU(:,:,j)=U(j,:).';
      end 
   else
      % MATRIX input
      U = reshape(X.',m*n,1);       % Force into column
      V = buffer(U,nChans*nSamps);  % Buffer U
      
      % Reshape and put into 3D array
      for i = 1:size(V,2)
         UU(:,:,i) = reshape(V(:,i),nChans,nSamps).';
      end
   end
end


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function setOutputFrameStatus(fullblk, X, nSamps)

   frame_conv_blk  = [fullblk '/Frame Status'];
   frameStr        = get_param(frame_conv_blk, 'outframe');

   % Decide if data is to be output as framebased or not
   if (nSamps > 1) & (ndims(X) ~= 3),
      if ~strcmp(frameStr,'Frame-based')
         set_param(frame_conv_blk, 'outframe', 'Frame-based');      
      end
   else
      if ~strcmp(frameStr,'Sample-based')
         set_param(frame_conv_blk, 'outframe', 'Sample-based');
      end
   end

   
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function insert_Buffer(fullblk, IC)
% If the Buffer block does not exist, then insert it

buffer_blk  = [fullblk '/Buffer'];

if ~exist_block(fullblk, 'Buffer')
   delete_line(fullblk,'From Workspace/1','Frame Status/1');
   load_system('dspbuff3');  % Library must be loaded to add_block
   add_block('dspbuff3/Buffer',buffer_blk);

   set_param(buffer_blk,'Position',[140    20   190    70]);
   
   add_line(fullblk,'From Workspace/1','Buffer/1');
   add_line(fullblk,'Buffer/1','Frame Status/1');
  
end

% Setting the parameters of the Buffer block
set_param(buffer_blk, 'ic', mat2str(IC'));
set_param(buffer_blk, 'N', mat2str(size(IC,2)));


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function remove_Buffer(fullblk)
%If the blocks exist, then remove them

if exist_block(fullblk, 'Buffer')
   delete_line(fullblk,'From Workspace/1','Buffer/1');
   delete_line(fullblk,'Buffer/1','Frame Status/1');
   delete_block([fullblk '/Buffer']);
   add_line(fullblk,'From Workspace/1','Frame Status/1')
end
   
   
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function present = exist_block(sys, name)

present = ~isempty(find_system(sys,'searchdepth',1,...
         'followlinks','on','lookundermasks','on','name',name));

     
% [EOF] dspblksfw2.m
