function varargout = trimmod (action, varargin)
%TRIMMOD Find the trim point of a Simulink system
%
% TRIMMOD
% finds the trim point (equilibrium) of a Simulink system.
% When invoked without left-hand arguments,
% trimmod opens a new figure with a graphical user interface.
% The user can load a Simulink system (.mdl),
% define certain trim point requirements and
% ask trimmod to calculate the corresponding trim point variables
% that are necessary to satisfy the requirements.
% This trim point is then automatically transferred to the
% "Simulation Parameters Workspace I/O" dialog box
% ("Load Input from Workspace" and "Load Initial States")
% of the corresponding Simulink system.
%
% H = TRIMMOD
% When invoked with a left-hand argument, H = TRIMMOD
% opens the gui and additionally returns the handle H of the figure.
% Copyright 2000-2004, J. J. Buchholz, Hochschule Bremen, buchholz@hs-bremen.de
% Version 1.2 26.05.2000
%
% The names of inputs, ... outputs for the error messages
% are now transferred via the parameter list.
%
% The precompilation and the release of the system is now done in jj_trim.
% Action taken depends on the number of input arguments.
% A user usually calls trimmod without input arguments;
% TrimMod calls itself recursively with varying input arguments.
switch nargin
% I N I T I A L I Z A T I O N
% If the number of input arguments is zero
case 0
% Display the graphical user interface and save its handle
fig_han = trimmod_fig;
% If trimmod has been invoked with a left-hand argument,
if nargout > 0
% return the handle of the gui
varargout{1} = fig_han;
end
% Extract handles and strings of all tooltips and save them in the userdata property
% of the tooltip menu switch.
% This is necessary only once during the initialization phase and might be used later
% if the tooltips are switched off and on again.
% Find the handle of the "Show Tooltips" menu entry
tooltip_menu_handle = findobj (gcf, 'type', 'uimenu', 'tag', 'show_tooltips');
% Find all uicontrols in the current figure
all_ui_controls = findall (gcf, 'type','uicontrol');
% Find all uicontrols that have *no* tooltip.
% Necessary, because it is not (yet) possible in Matlab to find all uicontrols
% that have a tooltip in one step
no_tooltip_handles = findall (all_ui_controls, 'flat', 'tooltipstring', '');
% Find and save all uicontrols that *have* a tooltip.
tooltip.handles = all_ui_controls(~ismember (all_ui_controls, no_tooltip_handles));
% Save the tooltip strings
tooltip.strings = get (tooltip.handles, 'tooltipstring');
% Save tooltip handles and tooltip strings in the userdata property
% of the tooltip menu switch
set (tooltip_menu_handle, 'userdata', tooltip);
% Create a new figure for the parameters and save the handle of that figure
parameters_handle = parameters_fig;
% Initialize the userdata property of the edit texts
set (findobj (parameters_handle, 'style', 'edit', 'tag', 'max_iterations'), ...
'userdata', 42);
set (findobj (parameters_handle, 'style', 'edit', 'tag', 'cost_value'), ...
'userdata', 1e-9);
% Make the figure invisible.
set (parameters_handle, 'visible', 'off')
% N O R M A L O P E R A T I O N
% If the number of input arguments is one or two
case {1 , 2}
% Initialization of normal operation.
% Variables defined here can be used in every callback case
% Define a cell array holding the names of all frames.
% Will be used by more than one callback case
frames = {'input', 'derivative', 'state', 'output'};
% Define a cell array holding the names of all edit texts.
% Will be used by more than one callback case
edits = {'_value', '_trim', '_lin'};
% Always read the userdata property of the current figure first
fud = get (gcbf, 'userdata');
% Start of the switch grave.
% The first callback argument defines the action to be taken
switch action
% If the menu entry "Open Model" has been selected
case 'open_model'
% Open a file select box and save the selection
[file_name, file_path] = uigetfile ('*.mdl', 'Open Model');
% If the user has pressed the cancel button,
if ~file_name
% do nothing at all
return
end
% Extract the model name (without the file extension)
% and save it in the figure user data structure
fud.model_name = strtok (lower (file_name), '.');
% Extract the model path and save it without the trailing backslash
fud.model_path = lower (file_path (1:(end - 1)));
% Insert the model name in the heading of the gui
set (gcbf, 'name', ['Trim Model ', fud.model_name]);
% Set the working directory to the model path
cd (fud.model_path);
% Call model and save model parameters.
% Since Release 11 (Version 5.3) the state names vector does include the newline characters.
[sizes, initial_conditions, state_names_with_nl] = eval (fud.model_name);
% Save number of states, inputs and outputs in figure user data
fud.n_states = sizes (1);
fud.n_outputs = sizes (3);
fud.n_inputs = sizes (4);
set (gcbf, 'userdata', fud);
% Find the block types of all states.
% Valid types are 'Integrator', 'StateSpace', 'TransferFcn', 'ZeroPole', and 'S-Function'
state_types = get_param (state_names_with_nl, 'blocktype');
% Save the state names cell array (with newline characters) in the userdata property
% of the state and derivative listboxes of the gui
set (findobj (gcbf, 'style', 'listbox', 'tag', 'state'), 'userdata', state_names_with_nl);
set (findobj (gcbf, 'style', 'listbox', 'tag', 'derivative'), 'userdata', state_names_with_nl);
% Save the state types in the userdata property of the corresponding gui objects
set (findobj (gcbf, 'style', 'text', 'tag', 'state'), 'userdata', state_types);
set (findobj (gcbf, 'style', 'text', 'tag', 'derivative'), 'userdata', state_types);
% Substitute the newline characters in the state names by blanks for better readability
state_names = kill_nl (state_names_with_nl);
% Loop over all the states
for i_state = 1 : fud.n_states
% Strip the model name from the state names
% in order to make the names easier to read in the listbox
state_names{i_state} = state_names{i_state}(length (fud.model_name) + 2 : end);
end
% Uniquefy(?) multiple state names
% Loop over all the states
for i_state = 1 : fud.n_states
% Find multiple state names and number of multiple state names
matches = strmatch (state_names(i_state), state_names, 'exact');
n_matches = length (matches);
% If multiple state names have been found
if n_matches > 1
% S-Functions have to be treated differently:
% They can return a list (cell array) of their internal state names,
% if the user has defined the "str"-output argument
% of the initialization (zero-flag) call:
% [dummy, dummy, str, dummy] = s_function_name ([], [], [], 0)
% An example can be found in the function "mdlInitializeSizes"
% in the file "second_order.m".
% If the state belongs to an S-Function
if strmatch (state_types{i_state}, 'S-Function')
% Find the block of that state
block_name = state_names_with_nl(i_state);
% Get the name of the S-Function that is called by that block
function_name = get_param (block_name, 'FunctionName');
% Get the state names of that S-Function
[dummy, dummy, s_function_state_names, dummy] = ...
feval (function_name{1}, [], [], [], 0);
% If the user has defined the S-Function state names as a string cell array
if ~isempty (s_function_state_names)
% Loop over all copies of a multiple state name
for i_match = 1 : n_matches
% Append the internal S-Function state name to every copy of a multiple state name
state_names{matches(i_match)} = ...
[state_names{matches(i_match)}, ' ', s_function_state_names{i_match}];
end
end
% If the state does not belong to an S-Function
else
% Loop over all copies of a multiple state name
for i_match = 1 : n_matches
% Append "___1"," ___2", ... to every copy of a multiple state name
state_names{matches(i_match)} = ...
[state_names{matches(i_match)}, '___', num2str(i_match)];
end
end
end
% Derivatives get the words "Deriv. of" in front of their names
% to make sure the user understands that these are the derivatives of the states
derivative_names{i_state} = ['Deriv. of ', state_names{i_state}];
end
% Save the modified state names in the string property
% of the state and derivative listboxes of the gui
set (findobj (gcbf, 'style', 'listbox', 'tag', 'state'), 'String', state_names);
set (findobj (gcbf, 'style', 'listbox', 'tag', 'derivative'), 'String', derivative_names);
% Save the initial conditions of the block diagram integrators
% in the userdata property of the state edit text
set (findobj (gcbf, 'style', 'edit', 'tag', 'state_value'), 'userdata', ...
initial_conditions);
% Initialize all the other edit texts with zeros
set (findobj (gcbf, 'style', 'edit', 'tag', 'input_value'), 'userdata', ...
zeros (fud.n_inputs, 1));
set (findobj (gcbf, 'style', 'edit', 'tag', 'input_trim'), 'userdata', ...
zeros (fud.n_inputs, 1));
set (findobj (gcbf, 'style', 'edit', 'tag', 'input_lin'), 'userdata', ...
zeros (fud.n_inputs, 1));
set (findobj (gcbf, 'style', 'edit', 'tag', 'state_trim'), 'userdata', ...
zeros (fud.n_states, 1));
set (findobj (gcbf, 'style', 'edit', 'tag', 'state_lin'), 'userdata', ...
zeros (fud.n_states, 1));
set (findobj (gcbf, 'style', 'edit', 'tag', 'derivative_value'), 'userdata', ...
zeros (fud.n_states, 1));
set (findobj (gcbf, 'style', 'edit', 'tag', 'output_value'), 'userdata', ...
zeros (fud.n_outputs, 1));
% Initialize all checkboxes to "not checked"
set (findobj (gcbf, 'style', 'checkbox', 'tag', 'state'), 'userdata', ...
zeros (fud.n_states, 1));
set (findobj (gcbf, 'style', 'checkbox', 'tag', 'input'), 'userdata', ...
zeros (fud.n_inputs, 1));
set (findobj (gcbf, 'style', 'checkbox', 'tag', 'derivative'), 'userdata', ...
zeros (fud.n_states, 1));
set (findobj (gcbf, 'style', 'checkbox', 'tag', 'output'), 'userdata', ...
zeros (fud.n_outputs, 1));
% Define a cell array holding the two strings "In" and "Out"
% and an array holding the numbers of inputs and outputs
in_out = {'In'; 'Out'};
n_in_out = [fud.n_inputs; fud.n_outputs];
% Repeat twice (once for the inputs and once for the outputs)
for i_num = 1 : 2
% Select the correct substring ("In" or "Out")
in_out_str = in_out{i_num};
% Concatenate the corresponding port ("Inport" or "Outport")
port_str = [in_out_str, 'port'];
% Concatenate the corresponding inputs/outputs ("Input" or "Output")
put_str = [lower(in_out_str), 'put'];
% Find all ports (even in libraries: 'FollowLinks')
port_names = find_system (fud.model_name, 'FollowLinks', 'on', 'searchdepth', 1, 'Blocktype', port_str);
% Save port names in the userdata property of the corresponding listbox
set (findobj (gcbf, 'style', 'listbox', 'tag', put_str), 'UserData', port_names);
% Save the number of ports
n_ports = length (port_names);
% Loop over all ports
for i_port = 1 : n_ports
% Strip the model name from the port names
% in order to make the names easier to read in the listbox
port_names{i_port} = port_names{i_port}(length (fud.model_name) + 2 : end);
end
% If there are more inputs/outputs than ports, some of the ports are vector ports.
if n_ports < n_in_out(i_num)
% Display a warning
l1 = ['TrimMod cannot determine the widths of vector ', port_str, 's.'];
l2 = ['In this case TrimMod uses generic ', port_str, ' names:'];
l3 = ['Single element of vector ', port_str, '___1'];
l4 = ['Single element of vector ', port_str, '___2'];
l5 = ['...'];
l6 = ['Use scalar ', port_str, 's if you want to see their specific names.'];
box_title = ['Vector ', port_str, '(s)'];
warndlg ({l1; l2; l3; l4; l5; l6}, box_title);
% Loop over all inputs/outputs
for i_put = 1 : n_in_out(i_num)
% Forget the original names and consecutively number the ports
port_names{i_put} = ...
['Single element of vector ', port_str, '___', num2str(i_put)];
end
% Save the portnames in the user data property of the corresponding listbox
set (findobj (gcbf, 'style', 'listbox', 'tag', put_str), 'UserData', port_names);
end
% Remove the newline characters and
% save the portnames in the user string property of the corresponding listbox
set (findobj (gcbf, 'style', 'listbox', 'tag', put_str), 'String', kill_nl (port_names));
end
% Loop over all frames
for i_frame = frames
% Reset current listbox value.
% Otherwise there were a problem, if the current listbox value of the old model
% was greater than the number of the listbox items in the new model
set (findobj (gcbf, 'style', 'listbox', 'tag', i_frame{1}), 'value', 1);
end
% Update all edit, checkbox, and text fields
trimmod ('update');
% Enable all menu entries,
set (findobj (gcbf, 'type', 'uimenu'), 'enable', 'on');
% except the "Untrim" menu entry
set (findobj (gcbf, 'type', 'uimenu', 'tag', 'untrim'), 'enable', 'off');
% Append model name to menu entry "Save Trim Point in ..."
set (findobj (gcbf, 'type', 'uimenu', 'tag', 'save_trim_point'), ...
'label', ['&Save Trim Point in ', fud.model_name, '.mat']);
% Enable all listboxes, edit texts, checkboxes, and pushbuttons;
set (findobj (gcbf, 'style', 'listbox'), 'enable', 'on');
set (findobj (gcbf, 'style', 'edit'), 'enable', 'on');
set (findobj (gcbf, 'style', 'checkbox'), 'enable', 'on');
set (findobj (gcbf, 'style', 'pushbutton'), 'enable', 'on');
% If the menu entry "Save Trim Point" has been selected
case 'save_trim_point'
% Save model name in save structure
save_struct = struct ('model_name', fud.model_name);
% Loop over all frames (in order to save all listboxes)
for i_frame = frames
% Get all the listbox strings
string_data = get (findobj (gcbf, 'style', 'listbox', 'tag', i_frame{1}), 'string');
% Assemble listbox field name ('listbox.input', 'listbox.derivative', ...)
field_name = ['listbox.', i_frame{1}];
% Append listbox strings into new field of save structure
save_struct = setfield (save_struct, field_name, string_data);
end
% Loop over all frames (in order to save all checkboxes)
for i_frame = frames
% Get all checkbox userdata properties
userdata = get (findobj (gcbf, 'style', 'checkbox', 'tag', i_frame{1}), 'userdata');
% Assemble field name ('checkbox.input', 'checkbox.derivative', ...)
field_name = ['checkbox.', i_frame{1}];
% Append checkbox userdata into new field of save structure
save_struct = setfield (save_struct, field_name, userdata);
end
% Loop over all frames (in order to save all edit texts)
for i_frame = frames
% Loop over all (three) edit texts
for i_edit = edits
% Construct the tag name of the edit text (e.g. "input_value", "input_trim", ...)
tag_name = [i_frame{1}, i_edit{1}];
% Get the currrent edit text (with all its elements)
userdata = get (findobj (gcbf, 'style', 'edit', 'tag', tag_name), 'userdata');
% Some combinations are not valid (e.g. "derivative_trim", ...)
if ~isempty (userdata)
% Assemble field name ('edit.input_value', 'edit.input_trim', ...)
field_name = ['edit.', tag_name];
% Append edit text userdata into new field of save structure
save_struct = setfield (save_struct, field_name, userdata);
end
end
end
% If trimmod has been called with the input argument 'save_trim_point' only,
if nargin == 1
% assemble the file name, into which the trim point will be saved,
% directly from the model name
file_name = [fud.model_name, '.mat'];
% If trimmod has been called with an additional input argument,
else
% take this additional input argument as the file name
file_name = varargin{1};
end
% Save the save structure
save (file_name, 'save_struct');
% If the menu entry "Save Trim Point As" has been selected
case 'save_trim_point_as'
% Open a file select box and let the user decide where to save the trim point
[file_name, file_path] = uiputfile ([fud.model_name, '.mat'], 'Save Trim Point');
% If the user has pressed the cancel button,
if ~file_name
% do nothing at all
return
end
% Recursively call TrimMod with the file name as the second input argument
trimmod ('save_trim_point', [file_path, file_name]);
% If the menu entry "Load Trim Point" has been selected
case 'load_trim_point'
% Open a file select box and let the user decide which trim point to load
[file_name, file_path] = uigetfile ('*.mat', 'Load Trim Point');
% If the user has pressed the cancel button,
if ~file_name
% do nothing at all
return
end
% Load new trim point
load ([file_path, file_name]);
% Loop over all frames
for i_frame = frames
% Extract the listbox strings from the preloaded trim point
listbox_string_save = getfield (save_struct.listbox, i_frame{1});
% Get the listbox strings from the current model
listbox_string = ...
get (findobj (gcbf, 'style', 'listbox', 'tag', i_frame{1}), 'string');
% Find those strings that are identical in the preloaded trim point listbox
% and in the current model listbox.
% Only these trim point definitions will be updated
[dummy, rows_save, rows] = intersect (listbox_string_save, listbox_string);
% Extract the checkbox strings from the preloaded trim point
checkbox_userdata_save = getfield (save_struct.checkbox, i_frame{1});
% Get the checkbox strings from the current model
checkbox_userdata = ...
get (findobj (gcbf, 'style', 'checkbox', 'tag', i_frame{1}), 'userdata');
% Copy relevant checkbox trim point definitions from preloaded trim point to buffer,
checkbox_userdata(rows) = checkbox_userdata_save(rows_save);
% and update current model with buffer
set (findobj (gcbf, 'style', 'checkbox', 'tag', i_frame{1}), ...
'userdata', checkbox_userdata);
% Loop over all (three) edit texts
for i_edit = edits
% Construct the tag name of the edit text (e.g. "input_value", "input_trim", ...)
tag_name = [i_frame{1}, i_edit{1}];
% Get the edit text strings from the current model
edit_userdata = ...
get (findobj (gcbf, 'style', 'edit', 'tag', tag_name), 'userdata');
% Some combinations are not valid (e.g. "derivative_trim", ...)
if ~isempty (edit_userdata)
% Extract the edit text strings from the preloaded trim point
edit_userdata_save = getfield (save_struct.edit, tag_name);
% Copy relevant edit text trim point definitions from preloaded trim point to buffer,
edit_userdata(rows) = edit_userdata_save(rows_save);
% and update current model with buffer
set (findobj (gcbf, 'style', 'edit', 'tag', tag_name), 'userdata', edit_userdata);
end
end
end
% Update all edit, checkbox, and text fields
trimmod ('update');
% If the menu entry "Exit TrimMod" has been selected
case 'exit_trimmod'
% Ask user if (s)he wants to save the trim point
button = questdlg ('Save trim point before closing?', ...
'Save trim point', ...
'Yes', 'No', 'Cancel', 'Yes');
% Action taken depends on button pressed
switch button
% If the "yes"-button has been pressed,
case 'Yes'
% recursively call TrimMod with the "save_trim_point_as" action flag
trimmod save_trim_point_as;
% Close the gui (the current call back figure)
delete (gcbf);
% Find the handle of the "Additional Parameters" figure
parameters_handle = findobj ('tag', 'parameters_fig');
% Close the "Additional Parameters" figure
delete (parameters_handle);
% If the "No"-button has been pressed,
case 'No'
% simply close the gui (the current call back figure)
delete (gcbf);
% Find the handle of the "Additional Parameters" figure
parameters_handle = findobj ('tag', 'parameters_fig');
% Close the "Additional Parameters" figure
delete (parameters_handle);
% If the "Cancel"-button has been pressed,
case 'Cancel'
% do nothing at all
return;
end
% If the "Show Blockdiagram"-button in the gui has been pressed
case 'blockdiagram'
% Find out in which frame the button has been pressed
tag_name = get (gcbo, 'tag');
% Get all the names from the current listbox
listbox_names = get (findobj (gcbf, 'style', 'listbox', 'tag', tag_name), 'userdata');
% Get the value of the current element of the listbox
listbox_value = get (findobj (gcbf, 'style', 'listbox', 'tag', tag_name), 'value');
% Find the name of the current element of the listbox
block_to_open = listbox_names{listbox_value};
% If the current element is part of a vector input or output,
if strmatch ('Single element of vector ', block_to_open)
% the corresponding block cannot be found.
% Only the main system blockdiagram is opened
open_system (fud.model_name);
% If the current element is *not* part of a vector input or output,
else
% Find the indices of all slashes in the name of the blockdiagram to be opened
slashes = findstr ('/', block_to_open);
% Find the index of the last slash
last_slash = slashes (end);
% The system to be opened is the whole path to the block to be opened
% up to the last slash
system_to_open = block_to_open(1 : last_slash - 1);
% A subsystem can only be opened if the main system is open.
% Find all open blockdiagrams (even in libraries: 'FollowLinks')
all_open_systems = find_system ('FollowLinks', 'on', 'type', 'block_diagram');
% If the main system blockdiagram is not open,
if isempty (strmatch (fud.model_name, all_open_systems, 'exact'))
% open the main system blockdiagram internally
% This dummy-evaluation opens the main system internally,
% without bringing the main system blockdiagram to front
[dummy, dummy, dummy] = eval (fud.model_name);
end
% Open blockdiagram of current listbox element
open_system (system_to_open);
% Save the background color of the current block
old_backgroundcolor = get_param (block_to_open, 'backgroundcolor');
% Blink three times
for i_blink = 1 : 3
% Cycle between red and white
for i_color = {'red', 'white'}
% Switch color
set_param (block_to_open, 'backgroundcolor', i_color{1})
% Wait half a second
pause (0.5)
end
end
% Restore the original background color
set_param (block_to_open, 'backgroundcolor', old_backgroundcolor)
end
% If the current listbox element has changed
case 'list'
% Get the listbox frame name
tag_name = get (gcbo, 'tag');
% Update frame
trimmod ('update', tag_name);
% If a checkbox has been checked
case 'checkbox'
% Get the checkbox frame name
tag_name = get (gcbo, 'tag');
% Get the value of the current listbox element in the current frame
listbox_value = get (findobj (gcbf, 'style', 'listbox', 'tag', tag_name), 'value');
% Get the value of the current checkbox (i.e. the state of the checkbox)
checkbox_value = get (findobj (gcbf, 'style', 'checkbox', 'tag', tag_name), 'value');
% Get the userdata of the current checkbox
% (i.e. the states of all the elements in the current frame)
checkbox_userdata = get (findobj (gcbf, 'style', 'checkbox', 'tag', tag_name), 'userdata');
% Update the state of the current checkbox
checkbox_userdata(listbox_value) = checkbox_value;
% Save the updated checkbox state in the userdata property of the current checkbox
set (findobj (gcbf, 'style', 'checkbox', 'tag', tag_name), 'userdata', checkbox_userdata);
% Update frame
trimmod ('update', tag_name);
% If an edit text has been changed
case 'edit'
% Get the edit text tag name
tag_name = get (gcbo, 'tag');
% The first part of the tag name is the frame name ("input", "state", ...)
frame_name = tag_name(1:findstr(tag_name, '_')-1);
% Get the value of the current listbox element in the current frame
listbox_value = get (findobj (gcbf, 'style', 'listbox', 'tag', frame_name), 'value');
% Get the string of the current edit text (i.e. the current user input)
edit_string = get (findobj (gcbf, 'style', 'edit', 'tag', tag_name), 'string');
% Get the userdata of the current edit text
% (i.e. all the numbers behind the edit text strings in the current frame)
edit_userdata = get (findobj (gcbf, 'style', 'edit', 'tag', tag_name), 'userdata');
% Try to convert the current edit string to a number
edit_num = str2num (edit_string);
% If the edit string is not a scalar number,
if isempty (edit_num) | length (edit_num) > 1
% inform the user about the error,
errordlg ([edit_string, ' is not a number']);
% change the color of the edit string to red (as a warning),
set (findobj (gcbf, 'style', 'edit', 'tag', tag_name), 'foregroundcolor', [1 0 0]);
% disable the current listbox (as a warning), and
set (findobj (gcbf, 'style', 'listbox', 'tag', frame_name), 'enable', 'off');
% disable the trim menu entry
set (findobj (gcbf, 'type', 'uimenu', 'tag', 'trim'), 'enable', 'off')
% If the edit string is a scalar number,
else
% (re)set the color of the edit string back to black,
set (findobj (gcbf, 'style', 'edit', 'tag', tag_name), 'foregroundcolor', [0 0 0]);
% (re)enable the current listbox,
set (findobj (gcbf, 'style', 'listbox', 'tag', frame_name), 'enable', 'on');
% (re)enable the trim menu entry,
set (findobj (gcbf, 'type', 'uimenu', 'tag', 'trim'), 'enable', 'on')
% update the current edit text value, and
edit_userdata(listbox_value) = edit_num;
% save the updated edit text in the userdata property of the current edit text
set (findobj (gcbf, 'style', 'edit', 'tag', tag_name), 'userdata', edit_userdata);
end
% If the menu entry "Overview" has been selected
case 'overview'
% Create a new figure for the overview and save the handle of that figure
overview_handle = overview_fig;
% The overview figure is modal, i.e. it has to be closed before the program continues
set (overview_handle, 'WindowStyle', 'modal');
% Find the handle of the listbox in the overview figure
listbox_handle = findobj (overview_handle, 'style', 'listbox');
% Create an empty cell array
listbox_string = cell (0);
% Wieland wanted a special order of the variable frames.
% Trim variables first; then the trim requirements
overview_frames = {'input', 'state', 'derivative', 'output'};
% Recall the pre-trim structure from the userdata property of the "Untrim" menu entry
pre_trim = get (findobj (gcbf, 'type', 'uimenu', 'tag', 'untrim'), 'userdata');
% Loop over all frames
for i_frame = overview_frames
% Get the string of the current listbox element (the names of the variables)
frame_listbox_string = ...
get (findobj (gcbf, 'style', 'listbox', 'tag', i_frame{1}), 'string');
% Get the userdata of the current "value" edit text element
frame_value_data = ...
get (findobj (gcbf, 'style', 'edit', 'tag', [i_frame{1},'_value']), 'userdata');
% Get the userdata of the current "trim" edit text element
frame_trim_data = ...
get (findobj (gcbf, 'style', 'edit', 'tag', [i_frame{1},'_trim']), 'userdata');
% Get the userdata of the current "lin" edit text element
frame_lin_data = ...
get (findobj (gcbf, 'style', 'edit', 'tag', [i_frame{1},'_lin']), 'userdata');
% Get the userdata of the current checkbox element (the states of the variables)
frame_checkbox_data = ...
get (findobj (gcbf, 'style', 'checkbox', 'tag', i_frame{1}), 'userdata');
% If the pre-trim structure has not been initialized,
if ~isstruct (pre_trim)
% copy the current data (new) to the pre-trim vector (old)
pre_trim_data = frame_value_data;
% If the pre-trim structure has been initialized,
else
% extract the current pre-trim vector from the pre-trim structure
pre_trim_data = getfield (pre_trim, i_frame{1});
end
% If the model has been modified,
if length (frame_value_data) ~= length (pre_trim_data)
% update the pre-trim vector
pre_trim_data = frame_value_data;
end
% Loop over all variables in the current listbox
for i_string = 1 : length (frame_listbox_string)
% Fill the frame buffer with the pre- and post-trim values
% and the name of the current element
frame_listbox_string {i_string} = ...
[sprintf('%15.6e', pre_trim_data(i_string)), ...
sprintf('%15.6e', frame_value_data(i_string)), ...
' ', frame_listbox_string{i_string}];
% If the frame is "input" or "state",
if ((strcmp ('input', i_frame)) | (strcmp ('state', i_frame)))
% add the "trim" and "lin" data to the frame buffer
frame_listbox_string {i_string} = ...
[sprintf('%15.6e', frame_trim_data(i_string)), ...
sprintf('%15.6e', frame_lin_data(i_string)), ...
frame_listbox_string{i_string}];
end
% If the current element is a trim variable or a trim requirement,
if frame_checkbox_data(i_string)
% and if the current element is a trim variable,
if ((strcmp ('input', i_frame)) | (strcmp ('state', i_frame)))
% fill the frame buffer with a "TV" in the first column
frame_listbox_string {i_string} = ['TV', frame_listbox_string{i_string}];
% If the current element is a trim requirement,
else
% fill the frame buffer with a "TR" in the first column
frame_listbox_string {i_string} = ['TR', frame_listbox_string{i_string}];
end
% If the current element is neither a trim variable nor a trim requirement,
else
% leave the first column blank
frame_listbox_string {i_string} = [' ', frame_listbox_string{i_string}];
end
end
% Add some separation lines to the overview buffer
listbox_string = [listbox_string; {''}];
listbox_string = [listbox_string; {'******************************************'}];
% Move the frame name a bit to the right
frame_name = [' ', upper(i_frame{1})];
% Add the current frame name to the overview buffer
listbox_string = [listbox_string; {frame_name}];
% Add a separation line to the overview buffer
listbox_string = [listbox_string; {'******************************************'}];
% If the frame is "input" or "state",
if ((strcmp ('input', i_frame)) | (strcmp ('state', i_frame)))
% add the long heading to the overview buffer
listbox_string = [listbox_string; ...
{' MAX. TRIM LIN. STEP PRE-TRIM POST-TRIM NAME'}];
% If the frame is "derivative" or "output",
else
% add the short heading to the overview buffer
listbox_string = [listbox_string; {' PRE-TRIM POST-TRIM NAME'}];
end
% Add the frame buffer to the overview buffer
listbox_string = [listbox_string; frame_listbox_string];
end
% Display the overview buffer
set (listbox_handle, 'string', listbox_string);
% If the menu entry "Trim" has been selected
case 'trim'
% Copy states, inputs, ... to variables x, u, ...
x = get (findobj (gcbf, 'style', 'edit', 'tag', 'state_value'), 'userdata');
u = get (findobj (gcbf, 'style', 'edit', 'tag', 'input_value'), 'userdata');
d = get (findobj (gcbf, 'style', 'edit', 'tag', 'derivative_value'), 'userdata');
y = get (findobj (gcbf, 'style', 'edit', 'tag', 'output_value'), 'userdata');
% Find the indices of all trim variables and trim requirements
i_x = find (get (findobj (gcbf, 'style', 'checkbox', 'tag', 'state'), 'userdata'));
i_u = find (get (findobj (gcbf, 'style', 'checkbox', 'tag', 'input'), 'userdata'));
i_d = find (get (findobj (gcbf, 'style', 'checkbox', 'tag', 'derivative'), 'userdata'));
i_y = find (get (findobj (gcbf, 'style', 'checkbox', 'tag', 'output'), 'userdata'));
% Find the names of inputs, states, derivatives and outputs
x_nam = get (findobj (gcbf, 'style', 'listbox', 'tag', 'state'), 'string');
u_nam = get (findobj (gcbf, 'style', 'listbox', 'tag', 'input'), 'string');
d_nam = get (findobj (gcbf, 'style', 'listbox', 'tag', 'derivative'), 'string');
y_nam = get (findobj (gcbf, 'style', 'listbox', 'tag', 'output'), 'string');
% Find the handle of the "Additional Parameters" figure
parameters_handle = findobj ('tag', 'parameters_fig');
% Fill the options vector with the corresponding values
% from the "Additional Parameters" figure
options(1) = get (findobj ...
(parameters_handle, 'style', 'edit', 'tag', 'max_iterations'), 'userdata');
options(2) = get (findobj ...
(parameters_handle, 'style', 'edit', 'tag', 'cost_value'), 'userdata');
% Get the current values for "Max. Trim Step" and "Linear Step"
del_x_max = get (findobj (gcbf, 'style', 'edit', 'tag', 'state_trim'), 'userdata');
del_u_max = get (findobj (gcbf, 'style', 'edit', 'tag', 'input_trim'), 'userdata');
del_x_lin = get (findobj (gcbf, 'style', 'edit', 'tag', 'state_lin'), 'userdata');
del_u_lin = get (findobj (gcbf, 'style', 'edit', 'tag', 'input_lin'), 'userdata');
% Replace every zero-element in "Max. Trim Step" with the default value (1e42)
del_x_max(find (~del_x_max)) = 1e42;
del_u_max(find (~del_u_max)) = 1e42;
% Replace every zero-element in "Linear Step" with the default value (1e-6*(1+abs(...)))
del_x_lin(find (~del_x_lin)) = 1e-6*(1 + abs(x(find (~del_x_lin))));
del_u_lin(find (~del_u_lin)) = 1e-6*(1 + abs(u(find (~del_u_lin))));
% Enable the "Untrim" menu entry
set (findobj (gcbf, 'type', 'uimenu', 'tag', 'untrim'), 'enable', 'on');
% Save the "old" (pre-trim) variables in a structure
pre_trim.state = x;
pre_trim.input = u;
pre_trim.derivative = d;
pre_trim.output = y;
% Save the pre-trim structure in the userdata property of the "Untrim" menu entry
set (findobj (gcbf, 'type', 'uimenu', 'tag', 'untrim'), 'userdata', pre_trim);
% Call the actual trim algorithm
[x_tr, u_tr, d_tr, y_tr] = jj_trim ( ...
fud.model_name, ...
x, u, d, y, ...
i_x, i_u, i_d, i_y, ...
x_nam, u_nam, d_nam, y_nam, ...
del_x_max, del_u_max, del_x_lin, del_u_lin, ...
options);
% Save the trim vectors in the userdata properties of the edit texts
set (findobj (gcbf, 'style', 'edit', 'tag', 'state_value'), 'userdata', x_tr);
set (findobj (gcbf, 'style', 'edit', 'tag', 'input_value'), 'userdata', u_tr);
set (findobj (gcbf, 'style', 'edit', 'tag', 'derivative_value'), 'userdata', d_tr);
set (findobj (gcbf, 'style', 'edit', 'tag', 'output_value'), 'userdata', y_tr);
% Update all frames
trimmod ('update');
% Convert the state trim vector to a string, with maximum precision
% A postulated precision of 42 seems to give more significant(?) digits
% than the recommended version without any postulated precision
x_tr_string = mat2str (x_tr, 42);
% Convert the input trim vector to a string
% Add a zero as the "time span"
u_tr_string = mat2str ([0, u_tr'], 42);
% Check (Enable) the "Load Initial States" checkbox
% in the "Simulation Parameters" menu entry of the current model
set_param (fud.model_name, 'LoadInitialState', 'on');
% Transfer the state trim vector to the "Load Initial States" edit text
% in the "Simulation Parameters" menu entry of the current model.
% This seems to be the only way to automatically set the trim point
% in the current model
set_param (fud.model_name, 'InitialState', x_tr_string);
% Check (Enable) the "Load Input from Workspace" checkbox
% in the "Simulation Parameters" menu entry of the current model
set_param (fud.model_name, 'LoadExternalInput', 'on');
% Transfer the input trim vector to the "Load Input from Workspace" edit text
% in the "Simulation Parameters" menu entry of the current model.
set_param (fud.model_name, 'ExternalInput', u_tr_string);
% If the menu entry "Untrim" has been selected
case 'untrim'
% Disable the "Untrim" menu entry.
% Only one untrim is possible.
set (findobj (gcbf, 'type', 'uimenu', 'tag', 'untrim'), 'enable', 'off');
% Recall the pre-trim structure from the userdata property of the "Untrim" menu entry
pre_trim = get (findobj (gcbf, 'type', 'uimenu', 'tag', 'untrim'), 'userdata');
% Loop over all frames
for i_frame = frames
% Extract the pre-trim vector from the pre-trim structure
field_value = getfield (pre_trim, i_frame{1});
% Save the pre-trim vector in the userdata property of the edit text.
set (findobj (gcbf, 'style', 'edit', 'tag', [i_frame{1},'_value']), 'userdata', field_value);
end
% Update all frames
trimmod ('update');
% If the menu entry "Show Tooltips" has been selected
case 'show_tooltips'
% Find the handle of the "Show Tooltips" menu entry
tooltip_menu_handle = findobj (gcbf, 'type', 'uimenu', 'tag', 'show_tooltips');
% Get all the tooltips
tooltip = get (tooltip_menu_handle, 'userdata');
% Get the state of the "Show Tooltips" menu entry
checked = get (tooltip_menu_handle, 'checked');
% If the "Show Tooltips" menu entry is checked,
if strcmp (checked, 'on')
% toggle the check state to "off" and
checked = 'off';
% clear all tooltips
set (tooltip.handles, 'tooltipstring', '');
% If the "Show Tooltips" menu entry is not checked,
else
% toggle the check state to "on" and
checked = 'on';
% reset all tooltips to their default values
set (tooltip.handles, {'tooltipstring'}, tooltip.strings);
end
% Update the visible checkstate in the menu entry
set (findobj (gcbf, 'type', 'uimenu', 'tag', 'show_tooltips'), 'checked', checked);
% If the menu entry "Additional Parameters" has been selected
case 'additional_parameters'
% Find the handle of the "Additional Parameters" figure
parameters_handle = findobj ('tag', 'parameters_fig');
% make it visible
set (parameters_handle, 'visible', 'on');
% If an edit text in the "Additional Parameters" figure has been changed
case 'parameter_edit'
% Get the edit text tag name
tag_name = get (gcbo, 'tag');
% Get the string of the current edit text (i.e. the current user input)
edit_string = get (gcbo, 'string');
% Try to convert the current edit string to a number
edit_num = str2num (edit_string);
% If the edit string is not a scalar number,
if isempty (edit_num) | length (edit_num) > 1
% inform the user about the error,
errordlg ([edit_string, ' is not a number']);
% change the color of the edit string to red (as a warning),
set (gcbo, 'foregroundcolor', [1 0 0]);
% and disable the OK button
set (findobj (gcbf, 'style', 'pushbutton'), 'enable', 'off');
% If the edit string is a scalar number,
else
% (re)set the color of the edit string back to black,
set (gcbo, 'foregroundcolor', [0 0 0]);
% (re)enable the OK button,
set (findobj (gcbf, 'style', 'pushbutton'), 'enable', 'on');
% and save the updated edit text in the userdata property of the current edit text
set (gcbo, 'userdata', edit_num);
end
% If the OK button of the "Additional Parameters" figure has been pushed
case 'exit_parameters'
% Make the figure invisible.
% Do not close the figure, because the data would be lost.
set (gcbf, 'visible', 'off')
% If the menu entry "About TrimMod" has been selected
case 'about_trimmod'
% Display the standard "About" dialog
helpdlg ({'TrimMod'; ...
'Release 1.2'; ...
'Copyright 2000 - 2004'; ...
'Joerg J. Buchholz'; ...
'Hochschule Bremen'; ...
'Germany'; ...
'buchholz@hs-bremen.de'}, ...
'About TrimMod');
% If the menu entry "Help on Trim Point" has been selected
case 'help_on_trimmod'
% Call the default browser and display the HTML help text (trimmod.html)
% This only works if trimmod.html has been copied to
% ...\MATLAB\help\toolbox\trimmod\trimmod.html
doc trimmod/trimmod
% If TrimMod is supposed to update one or more frames
case 'update'
% If "update" has been called without an extra argument,
if nargin == 1
% all frames have to be updated
update_frames = frames;
% If "update" has been called with an extra argument,
else
% the extra argument is taken as the only frame to be updated
update_frames = varargin(1);
end
% Loop over all frames
% (could also be only one frame)
for i_frame = update_frames
% Get the value of the current listbox element in the current frame
listbox_value = get (findobj (gcbf, 'style', 'listbox', 'tag', i_frame{1}), 'value');
% If the current frame is a state frame or a derivative frame,
if strcmp (i_frame{1}, 'state') | strcmp (i_frame{1}, 'derivative')
% get all the state types and
text_userdata = get (findobj (gcbf, 'style', 'text', 'tag', i_frame{1}), 'userdata');
% update the corresponding state type text
set (findobj (gcbf, 'style', 'text', 'tag', i_frame{1}), ...
'string', text_userdata{listbox_value});
end
% Loop over all (three) edit texts
for i_edit = edits
% Construct the tag name of the edit text (e.g. "input_value", "input_trim", ...)
tag_name = [i_frame{1}, i_edit{1}];
% Get the currrent edit text (with all its elements)
edit_userdata = get (findobj (gcbf, 'style', 'edit', 'tag', tag_name), 'userdata');
% Some combinations are not valid (e.g. "derivative_trim", ...)
if ~isempty (edit_userdata)
% Update the current edit text
set (findobj (gcbf, 'style', 'edit', 'tag', tag_name), ...
'string', edit_userdata(listbox_value));
end
end
% Get all checkbox states
checkbox_userdata = get (findobj (gcbf, 'style', 'checkbox', 'tag', i_frame{1}), 'userdata');
% Update the current checkbox
set (findobj (gcbf, 'style', 'checkbox', 'tag', i_frame{1}), ...
'value', checkbox_userdata(listbox_value));
end
% Check if the number of trim varibales equals the number of trim requirements
% Get the userdata of all checkboxes (the checked items)
input_userdata = get (findobj (gcbf, 'style', 'checkbox', 'tag', 'input'), 'userdata');
derivative_userdata = get (findobj (gcbf, 'style', 'checkbox', 'tag', 'derivative'), 'userdata');
state_userdata = get (findobj (gcbf, 'style', 'checkbox', 'tag', 'state'), 'userdata');
output_userdata = get (findobj (gcbf, 'style', 'checkbox', 'tag', 'output'), 'userdata');
% Calculate the number of trim variables and trim requirements
n_trim_variables = sum (input_userdata) + sum (state_userdata);
n_trim_requirements = sum (derivative_userdata) + sum (output_userdata);
% Write these numbers on the screen
set (findobj (gcbf, 'style', 'text', 'tag', 'number of trim variables'), ...
'string', n_trim_variables);
set (findobj (gcbf, 'style', 'text', 'tag', 'number of trim requirements'), ...
'string', n_trim_requirements);
% If the number of of trim variables equals the number of trim requirements,
if n_trim_variables == n_trim_requirements
% write this information on the screen (in green)
set (findobj (gcbf, 'style', 'text', 'tag', 'relation'), ...
'string', 'equals', ...
'foregroundcolor', [0 1 0]);
% and enable the "Trim" menu entry
set (findobj (gcbf, 'type', 'uimenu', 'tag', 'trim'), 'enable', 'on')
% If the number of of trim variables does not equal the number of trim requirements,
else
% write this information on the screen (in red)
set (findobj (gcbf, 'style', 'text', 'tag', 'relation'), ...
'string', 'does not equal', ...
'foregroundcolor', [1 0 0]);
% and disable the "Trim" menu entry
set (findobj (gcbf, 'type', 'uimenu', 'tag', 'trim'), 'enable', 'off')
end
% If TrimMod has been called with an unknown type of argument,
otherwise
% write an error message on the screen
error (['Unknown type of callback argument: ', action]);
end
% If TrimMod has been called with more than 2 input arguments
otherwise
% write an error message on the screen
error (['Invalid number of input arguments: ', int2str(nargin)]);
end