30037

РАЗРАБОТКА ТЕХНОЛОГИИ ИСПОЛЬЗОВАНИЯ СРЕДСТВ ПАКЕТА MATLAB В ВИЗУАЛЬНОЙ СРЕДЕ ПРОГРАММИРОВАНИЯ C/C++

Дипломная

Информатика, кибернетика и программирование

Палухин Павел Николаевич РАЗРАБОТКА ТЕХНОЛОГИИ ИСПОЛЬЗОВАНИЯ СРЕДСТВ ПАКЕТА MATLAB В ВИЗУАЛЬНОЙ СРЕДЕ ПРОГРАММИРОВАНИЯ C C Дипломная работа Научный руководитель: доктор технических наук профессор _____ В. ЯЗЫК MATLAB АВТОНОМНЫЕ ПРИЛОЖЕНИЯ НА C C MATLAB C MATH LIBRARY ВИЗУАЛЬНАЯ СРЕДА ПРОГРАММИРОВАНИЯ МАТЕМАТИЧЕСКОЕ МОДЕЛИРОВАНИЕ НЕЛИНЕЙНЫЕ ДИНАМИЧЕСКИЕ СТОХАСТИЧЕСКИЕ СИСТЕМЫ НЕЛИНЕЙНАЯ ФИЛЬТРАЦИЯ МЕТОД ИНВАРИАНТНОГО ПОГРУЖЕНИЯ. Объект исследования – технология использования средств MATLAB в визуальной среде...

Русский

2013-08-22

1.92 MB

28 чел.

Министерство образования Российской Федерации

ТОМСКИЙ ГОСУДАРСТВЕННЫЙ УНИВЕРСИТЕТ

Факультет информатики

Кафедра прикладной информатики

ДОПУСТИТЬ К ЗАЩИТЕ В ГАК

Зав. каф. прикладной информатики, д.т.н., профессор

                   ______ _   С.П. Сущенко

«       »  июня  2002 г.

Палухин Павел Николаевич

РАЗРАБОТКА ТЕХНОЛОГИИ ИСПОЛЬЗОВАНИЯ

СРЕДСТВ ПАКЕТА MATLAB В ВИЗУАЛЬНОЙ СРЕДЕ ПРОГРАММИРОВАНИЯ C/C++

Дипломная работа

Научный руководитель:

доктор технических наук, профессор

                     _____ В.В. Поддубный

Автор работы

                      _____П.Н. Палухин

Томск 2002


РЕФЕРАТ

Дипломная работа, 97 с., 25 рис., 6 табл., библ. 8 назв.

ЯЗЫК MATLAB, АВТОНОМНЫЕ ПРИЛОЖЕНИЯ НА C/C++, MATLAB C MATH LIBRARY, ВИЗУАЛЬНАЯ СРЕДА ПРОГРАММИРОВАНИЯ, МАТЕМАТИЧЕСКОЕ МОДЕЛИРОВАНИЕ, НЕЛИНЕЙНЫЕ ДИНАМИЧЕСКИЕ СТОХАСТИЧЕСКИЕ СИСТЕМЫ, НЕЛИНЕЙНАЯ ФИЛЬТРАЦИЯ, МЕТОД ИНВАРИАНТНОГО ПОГРУЖЕНИЯ.

  1.  Объект исследования –  технология использования средств MATLAB в визуальной среде разработки автономных приложений на языке C/C++ на примере задач моделирования и фильтрации состояний нелинейных динамических стохастических систем.
  2.  Цель работы – разработка технологии использования возможностей MATLAB в визуальной среде программирования на языке C/C++, применение данной технологии для создания автономного приложения на примере задачи нелинейной фильтрации.
  3.  Метод исследования –  теоретический и экспериментальный (на ЭВМ).
  4.  Основные результаты – разработана технология создания автономных приложений в среде визуального программирования Borland C++ Builder v5.0-6.0 с использованием средств пакета MATLAB v6.0-6.1 (алгоритмического языка MATLAB, MATLAB Compiler, MATLAB C Math Library,  MATLAB C Graphics Library); на основе этой технологии создано приложение, реализующее решение задачи нелинейной фильтрации.


Оглавление


Введение

Пользователям визуальных средств разработки Windows-приложений приходится сталкиваться со следующей ситуацией: разработав интерфейс пользователя, им необходимо реализовать обработчики кнопок, элементов меню и т.д., содержанием которых должно являться выполнение нетривиальных математических вычислений. Несмотря на то, что эти системы имеют удобные базовые средства по организации пользовательского интерфейса, позволяют просто работать с базами данных, имеют широкие возможности по созданию клиент-серверных приложений, приложений для Интернет и т.д., реализуемый ими стандартный язык программирования имеет очень ограниченный набор математических функций.

При возникновении такой ситуации пользователи идут разными путями. Либо спешно ищут (например, в Internet) соответствующую математическую библиотеку, совместимую с данной средой программирования. При этом часто неизвестно, корректно ли работает данная библиотека, каковы правила ее использования и обладает ли она достаточными возможностями. Либо пишут программный код необходимых математических процедур сами, что грамотно осуществить бывает довольно сложно и, более того, невозможно. Конечно, они могут прибегнуть к помощи мощных математических пакетов, в которых профессионально реализуется широкий спектр математических алгоритмов, накопленный за многие годы использования вычислительной техники, в них также делается упор на точность и математическую правильность работы соответствующих алгоритмов. Но каждая такая система имеет свой собственный язык программирования и интерпретатор, реализующий его возможности.

С другой стороны, специалистам, работающим в математических пакетах, хотелось бы создать приложение, которое помимо вычислительных возможностей обладало бы еще, как минимум, удобным пользовательским интерфейсом. Увы, соответствующие системы не обладают средствами разработки интерфейса пользователя, сравнимыми по возможностям с визуальными средами программирования. Такая проблема становится особенно актуальной тогда, когда уже имеется алгоритм, написанный на языке математического пакета, который бы хотелось использовать при создании подобного приложения, причем работающего независимо от многофункциональной и поэтому громоздкой и не столь быстрой родительской среды.

Но существует решение, которое должно удовлетворить как пользователей визуальных сред, так и пользователей математических пакетов – применить в процессе разработки автономного приложения в визуальной среде программирования уникальные в своем роде, специальные средства, предоставляемые математическим пакетом MATLAB. Последний имеет в своем составе мощную математическую библиотеку MATLAB C/C++ Math Library, включающую большой объем математически грамотно и, в то же время, алгоритмически эффективно написанных на C/C++ специализированных функций и процедур. Также эти средства включают C/C++-библиотеку графических функций, аналогичных графическим функциям MATLAB и MATLAB Compiler, специально разработанную утилиту, позволяющую осуществить, правда, с небольшими ограничениями однозначный перевод программного кода на языке MATLAB в код на C/C++ с использованием функций математической библиотеки MATLAB. Однако при использовании данных средств совместно с визуальной средой программирования возникают проблемы, связанные с сопряжением программных продуктов различных фирм разработчиков.

Таким образом, данная работа посвящена разработке технологии использования предоставляемых MATLAB средств для создания автономных приложений на C/C++ в визуальной среде программирования, в частности, в Borland C++ Builder v5.0-6.0. Т.е. последовательным системным образом описывается процесс разработки, начиная от создания «переводимого» на Си кода на языке MATLAB и заканчивая компоновкой приложения (какие, как и где подключить библиотеки). На каждом технологическом этапе описывается обязательный набор действий, которые должен осуществить пользователь, возникающие проблемы, способы их решения. В соответствующих местах рассказываются имеющиеся возможности средств разработки, которые можно применить на данном этапе. Каждый технологический этап поясняется на небольших, но конструктивных примерах.

Примером использования созданной технологии стала разработка автономного приложения, реализующего задачи нелинейной фильтрации. Комплекс задач нелинейной фильтрации имеет непростое вычислительное решение, так как включает в себя целый ряд подспудных вычислительных проблем. На первом этапе было необходимо построить адекватную стохастическую модель динамической системы в непрерывном времени. Так как непрерывность в данном случае является искусственной, то важным моментом стало моделирование случайного непрерывного процесса дискретным. Что, в свою очередь, потребовало моделирования возмущений в системе и наблюдений. Для этого случайная составляющая была заменена кусочно-постоянной функцией, меандром, уменьшая шаг разбиения которого можно получить шум, уже реально приближенный к непрерывному процессу. Для моделирования состояний и наблюдений приходится решать дифференциальное уравнение, что осуществляется с помощью одного из численных методов. В вышеозначенном контексте алгоритмы инвариантного погружения применительно к вопросу нелинейной фильтрации использовались для компьютерной реализации численного алгоритма нелинейной фильтрации, осуществленного на языке MATLAB. Именно эта реализация была взята за основу при создании автономного приложения.

  1.  


  1.  Технология перевода программ из среды MATLAB в C-код с созданием исполняемого модуля консольного приложения

  1.  Знакомство с MATLAB Compiler

  1.  Описание MATLAB Compiler

Компилятор MATLAB (MATLAB Compiler) – это программный продукт фирмы "MathWorks", выполненный в виде консольной утилиты, на вход которой поступают m-файлы1, а в качестве выхода генерируется исходный код на C/C++. Компилятор MATLAB может формировать следующие виды кода:

  •  Код на C/C++, комбинируемый с другими модулями, необходимый для создания автономных прикладных систем (независимых приложений). Эти приложения не требуют MATLAB во время выполнения; они могут исполняться, даже если MATLAB не установлен в системе. Но чтобы создавать исполняемые модули,  MATLAB Compiler требует наличия MATLAB C/C++ Math Library (математической библиотеки MATLAB), которая включает в себя базовые математические и аналитические средства MATLAB. Компилятор также требует MATLAB C/C++ Graphics Library, чтобы создавать независимые приложения, использующие вызов графических функций MATLAB.
  •  Разделяемые C-библиотеки (динамически подключаемые библиотеки, или DLL для Microsoft Windows 95/98/NT/2000) и статические C++-библиотеки. Они могут использоваться без MATLAB, но при наличии в системе MATLAB C/C++ Math Library.

Зачем необходимо компилировать m-файлы?

Имеется три основных причины, чтобы компилировать m-файлы:

  •  Создание автономных приложений или разделяемых C-библиотек (DLL в Windows) или статических C++-библиотек
  •  Сокрытие исходного кода
  •  Повышение скорости исполнения

  1.  Автономные приложения и разделяемые библиотеки

В последних версиях системы (MATLAB v6.0 и v6.1) появилась возможность создавать приложения MATLAB, что дает возможность использовать  преимущество математических функций MATLAB, при этом не требуется, чтобы пользователь являлся обладателем MATLAB. Автономные приложения являются очень удобным способом «упаковывать» вычислительную мощь MATLAB, а также распространять приложения частного характера его пользователям.

Используя MATLAB Compiler, стало возможным разработанный в MATLAB алгоритм для специализированных вычислений поместить в разделяемую библиотеку, написанную на Си, а также в статическую C++-библиотеку. Таким образом, появилась возможность интегрировать данный алгоритм в приложение на языках C/C++. После компиляции кода специализированные вычисления могут производиться прямо из приложения при помощи алгоритма, написанного на MATLAB.

Сокрытие исходного кода

M-файлы MATLAB – это текстовые ASCII-файлы, содержимое которых каждый может просматривать и модифицировать, в свою очередь, EXE-файл является двоичным файлом. Поэтому использование исполняемых модулей взамен m-файлов скрывает имеющийся алгоритм, возможно являющийся интеллектуальной собственностью, а также предотвращает модификацию кода.

Более быстрое выполнение

Скомпилированный код на C/C++ обычно исполняется быстрее, чем их m-файловый эквивалент, потому, что

  •  скомпилированный код обычно выполняется быстрее, чем интерпретируемый,
  •  C/C++ могут избежать накладных расходов, связанных с излишним распределением памяти, которые выполняет интерпретатор.

Случаи, когда производительность не повышается.

Компиляция не применима для ускорения m-кода2 тогда,

  •  когда он перегружен векторными операциями,
  •  когда основное время работы алгоритма занимают встроенные в MATLAB индексирующие математические функции или функции графики.

Случаи, когда производительность повышается.

Компиляция применима для ускорения выполнения m-кода, когда в нем содержатся циклы.

  1.  Создание автономных приложений на C/C++

Компилятор MATLAB, вызываемый с макро-опцией "–m", переводит входные m-файлы в исходный код на Си, который может быть использован в любом из поддерживаемых исполняемых типов. Компилятор также порождает требуемый файл-упаковщик, который понадобится для создания автономного приложения. Затем ANSI C компилятор компилирует С-файлы исходного кода, и полученные в результате объектные файлы компонуются совместно с MATLAB C/C++ Math Library, наличие которой обязательно, если требуется создать исполняемый модуль.

Для преобразования m-файлов в исходный код на C++ нужно использовать макрос "– p".

Следует заметить, что если не установлена MATLAB C/C++ Graphics Library (libsgl), и приложение вызывает функции графики MATLAB, то их вызов приведет к ошибке времени исполнения.

  1.  Принципы разработки автономных приложений

  1.  Структура процесса разработки

Предположим, необходимо создать приложение, которое производит какие-либо нетривиальные математические вычисления. Одним из путей создания такого приложения является написание его полностью на Си или C++, однако это потребует написания своих собственных математических функций, что составит более сложную или даже непосильную задачу.

Более простым путем создания соответствующего приложения станет написание одного или нескольких m-файлов. Схема на рис. 1.1 описывает структуру процесса разработки.

  1.  Ограничения MATLAB Compiler

Последняя версия компилятора MATLAB поддерживает практически всю функциональность MATLAB. Однако существует несколько ограничений, на которые стоит обратить внимание. Данная версия компилятора (MATLAB Compiler v2.2) не может компилировать следующее:

  •  m-файлы сценарного типа,
  •  m-файлы, которые используют объекты,
  •  m-файлы, использующие функции eval и input для манипулирования переменными рабочего пространства MATLAB.

Компилятор не может компилировать встроенные в MATLAB функции (например, функция eig не имеет своего m-файла, таким образом, она не может быть скомпилирована). Заметьте, однако, что большинство этих функций могут использоваться, потому что они находятся в MATLAB Math Built-in Library (в частности, функция eig).

Также имеется перечень функций, которые компилятор не может переводить для использования в автономном приложении (см. табл. 1.1).

Табл. 1.1. Перечень функций, не поддерживаемых в режиме автономного приложения

add_block

add_line

applescript

assignin

callstats

close_system

cputime

dbclear

dbcont

dbdown

dbquit

dbstack

dbstatus

dbstep

dbstop

dbtype

dbup

delete_block

delete_line

diary

echo

edt

errorstat

errortrap

evalin

fields

fschange

functionscalled

get_param

hcreate

help

home

hregister

inferiorto

inmem

isglobal

isjava

isruntime

java

javaArray

javaMethod

javaObject

keyboard

linmod

lookfor

macprint

mactools

methods

mislocked

mlock

more

munlock

new_system

open_system

pack

pfile

rehash

runtime

set_param

sim

simget

simset

sldebug

str2func

superiorto

system_dependent

trmginput

type

vms

what

which

who

whos

  1.  Преобразование m-файлов сценарного типа (Script m-files) в m-файлы функционального типа (Function m-files)

MATLAB предоставляет две возможности размещения последовательности команд:

  •  m-файлы функционального типа,
  •  m-файлы сценарного типа.

Эти две категории m-файлов различаются в двух важных аспектах:

  •  имеется возможность передавать аргументы m-файлам функционального типа, но не сценарного
  •  переменные, используемые внутри m-файлов функционального типа локальны по отношению к этой функции; отсутствует возможность доступа к этим переменным из рабочего пространства интерпретатора MATLAB до тех пор, пока они не будут возвращены функцией. Напротив, переменные, используемые m-файлами сценарного типа, являются разделяемыми для операторов рабочего пространства; имеется возможность вызова этих переменных из командной строки интерпретатора MATLAB.

MATLAB Compiler не может компилировать m-файлы сценарного типа, так же как и m-файлы функционального типа, которые вызывают script.

Преобразование script в функцию осуществляется обычно довольно просто. Необходимо просто добавить ключевое слово function в верхней строке m-файла.

Для примера, пусть m-файлом сценарного типа является "example.m", тогда необходимо провести следующее преобразование:

function example

%

% Содержимое "example.m"

%

Если же необходимо вернуть какие-либо значения, то следует записать:

function [par1,par2,…] = example

%

% Содержимое "example.m"

% par1, par2, ... - переменные, используемые в "example.m"

%

Следует заметить, что в случае, когда возвращаемых значений очень много, то их можно объявить в главном m-файле и в вызываемой из него функции глобальными

global par1, par2, ...

  1.  Создание автономных приложений

  1.  Опции компилятора MATLAB

Табл. 1.2. Опции утилиты mcc

Макро-

опции

Пакетный файл

Что образуется

 Эквивалентные опции

 Перевод m-файла на C/C++

 |   Файл упаковщика

 |    |         Язык трансляции

 |    |              |         Стадия вывода

 |    |              |           |        Вспомогательн.  функции                 

 |    |              |           |                       |  Библ. m-файлов

 |    |              |           |                       |   |

-m

macro_option_m

Автономное

приложение на Си

-t -W main -L C   -T link:exe –h libmmfile.mlib

-p

macro_option_p

Автономное

приложение на C++

-t -W main -L Cpp -T link:exe –h libmmfile.mlib

-B sgl

sgl

Автономное

приложение на Си c использован. графики

-t -W mainhg                     libmwsglm.mlib

-B sglcpp

sglcpp

Автономное

приложение на C++ с использован. графики

-t -W mainhg                     libmwsglm.mlib

-g

macro_option_g

Разрешена отладка

-G -A debugline:on –O none

Утилита фирмы "MathWorks" mbuild позволяет настраивать конфигурацию и процесс компоновки. Утилита также предоставляет простой способ изменять следующие характеристики:

  •  устанавливать параметры компилятора и компоновщика,
  •  изменять параметры компилятора и компоновщика,
  •  переключаться между разработкой на Си или C++,
  •  компоновать приложения.

MATLAB Compiler (утилита компилятора называется mcc) автоматически вызывает mbuild с соответствующими установками. Опции компилятора можно посмотреть в табл. 1.2.

Расшифровка макро-опций.

Опция "-m" указывает компилятору создать автономное приложение на Си. Макрос "-m" эквивалентен следующей последовательности опций:

-t -W main -L C -T link:exe -h libmmfile.mlib

Следующая табл. 1.3. раскрывает пять опций, которые составляют макрос "-m", и информацию, которую они предоставляют компилятору.

Табл. 1.3. Описание макроса "-m"

Опция

Функция

-t

Переводит m-код в код на C/C++

-W main

Создает файл-упаковщик, необходимый

при создании автономного приложения

-L C

Генерирует C-код

-T link:exe

В итоге создается исполняемый модуль

-h

Автоматически находит и компилирует

вспомогательные функции, включаемые

в исходный m-файл

libmmfile.mlib

Производит компоновку с разделяемой

библиотекой, если есть необходимость

  1.  Создание пользовательских C-библиотек

Смысл упаковщика C-библиотеки состоит в том, чтобы позволить включить произвольный набор m-файлов в статическую или разделяемую библиотеку. Заголовочный файл содержит все точки входов для всех компилируемых m-функций. Список экспорта содержит набор идентификаторов, экспортируемых из разделяемой C-библиотеки.

Другим достоинством создания библиотек является то, что можно компилировать общий набор функций единожды. Впоследствии появляется возможность компилировать другие m-функции, зависящие от этого набора, без перекомпиляции исходных функций. Для этого можно использовать mlib-файлы, которые автоматически генерируются, когда создается библиотека.

Пример создания библиотеки

Функции для примера взяты из "%MATLAB%\toolbox\matlab\timefun". Библиотечные файлы можно сгенерировать из командной строки следующим образом:

mcc -W lib:libtimefun -L C weekday date tic calendar toc

Будут созданы следующие файлы:

libtimefun.c –  C-файл упаковщика

libtimefun.h –  заголовочный C-файл

libtimefun.exports –  список экспорта Cи

libtimefun.mlib –  библиотека m-файлов

C-файл упаковщика содержит инициализирующую (libtimefunInitialize) и завершающую (libtimefunTerminate) функции. Необходимо вызвать libtimefunInitialize прежде, чем будет вызываться созданный компилятором код. Функция инициализирует режим использования сформированных компилятором функций, следовательно, эти функции могут быть вызваны из C-кода, не генерировавшегося компилятором MATLAB.

Также MATLAB позволяет создать разделяемую C-библиотеку:

mcc -W lib:libtimefun -L C -t -T link:lib -h weekday date tic calendar toc

где опции имеют следующий смысл:

  •  опция "-t" указывает компилятору создать C-код из каждого перечисленного m-файла,
  •  опция "-T link:lib" указывает компилятору компилировать и компоновать разделяемую библиотеку,
  •  опция "-h" указывает компилятору включать в приложение любые другие m-функции, вызываемые из m-файлов, перечисленных в командной строке с mcc, поэтому их можно назвать вспомогательными.

Будут созданы следующие файлы: "libtimefun.c", "libtimefun.h", "libtimefun.exports", "libtimefun.mlib", "libtimefun.dll" (разделяемая библиотека на PC).

Пример использования mlib-файлов

Создадим простую функцию timer, которая использует библиотечные функции tic и toc.

function timer

tic

x = fft(1:1000);

toc

Прежде она бы компилировалась так:

mcc -m timer

Обе функции tic и toc перекомпилировались, так как макрос "-m" содержит опцию "-h". Используя mlib-файл, можно записать:

mcc -m timer libtimefun.mlib

Во время компиляции определения функций tic и toc будут размещаться в "libtimefun.mlib", по которым все будущие обращения к tic и toc должны исходить от корреспондирующей с mlib-файлом разделяемой библиотеки. Созданный исполняемый модуль будет скомпонован с соответствующей разделяемой библиотекой.

Преимущество использования разделяемых библиотек состоит в том, что генерируемый код станет меньше, так как часть кода теперь будет размещена в разделяемой библиотеке.

Некоторые ограничения

  •  Нельзя переименовывать файл библиотеки.
  •  Во время компиляции mlib-файл и разделяемая библиотека должны быть в одной директории.
  •  Во время исполнения путь к разделяемой библиотеке должен быть прописан в системном PATH.

Нет необходимости в присутствии mlib-файла, присоединенного к DLL, во время исполнения.

  1.  Подготовка к компиляции

Настройка компилятора MATLAB на один из ANSI C компиляторов

Первоначально необходимо настроить mbuild.

Для того чтобы можно было использовать утилиту mbuild или компилятор mcc в системах Microsoft Windows 95/98/ME, необходимо обязательно прописать в "config.sys"

shell = c:\command.com /e:32768 /p

или в "autoexec.bat" указать следующую переменную окружения

set comspec = c:\command.com /e:32768 /p

Это необходимо сделать, так как утилита использует несколько большее пространство переменного окружения, чем установлено в Windows по умолчанию.

Опция mbuild "-setup", позволяет выбрать, какой ANSI C компилятор будет использоваться при компиляции. Выбранный компилятор становится компилятором по умолчанию, и затем нет необходимости выполнять повторную установку.

Утилита может сама определить установленные в системе компиляторы (компилятор Borland C++ Builder v6.0 утилита обнаружить не может, так он был выпущен после последнего релиза MATLAB, однако, указав путь вручную, можно прекрасно компилировать m-файл в виду полной совместимости с Borland C++ Builder v5.0)  или предоставить выбор компилятора из списка. Утилитой mbuild поддерживаются следующие компиляторы для построения автономных приложений:

  •  Borland C++ Builder version 5.0 (можно использовать Borland C++Builder v6.0, см. выше)
  •  Borland C++ Builder version 4.0
  •  Borland C++ Builder version 3.0
  •  Borland C/C++ version 5.02
  •  Borland C/C++ version 5.0
  •  Borland C/C++ (free command line tools) version 5.5
  •  Lcc C version 2.4
  •  Microsoft Visual C/C++ version 6.0
  •  Microsoft Visual C/C++ version 5.0
  •  Microsoft Visual C/C++ version 4.2

Следует заметить, что компилятор Lcc C version 2.4 поставляется вместе с MATLAB и всегда наличествует в системе.

Использование IDE

Интегрированную среду разработки во многих случаях гораздо удобнее использовать во время создания реальных приложений. По этому случаю, создатели MATLAB предусмотрели возможность интеграции с такой средой, как Microsoft Visual C/C++, а также возможность использование Borland C++ Builder для создания приложений (настройку Borland C++ Builder на совместное использование библиотек MATLAB см. в разделе  на стр. 36).

  1.  Распространение автономных приложений

Казалось бы, что понятие stand-alone application говорит само за себя: исполняемый файл можно спокойно переносить на другой компьютер, имеющий Microsoft Windows. Но в реальности это обстоит несколько иначе. Дело в том, что при компиляции, как минимум, использовались математические (и, возможно, графические) разделяемые библиотеки MATLAB. При установке MATLAB путь к ним был автоматически прописан в системе. Там же, где MATLAB не был установлен, этих библиотек, естественно, нет.

Таким образом, при использовании на другой машине приложения, созданного с помощью MATLAB Compiler,  необходимо включить в набор дистрибутива, помимо исполняемого модуля, следующие дополнительные файлы:

  •  Содержимое, если, конечно, имеется, директории с именем "bin", созданной mbuild в той же самой директории, где находится исполняемый модуль.
  •  Используемые данным приложением MEX-файлы.
  •  Все математические библиотеки MATLAB, относящиеся ко времени исполнения.

MATLAB C Math Library имеет уже заранее собранные в единый пакет все библиотеки MATLAB времени исполнения, требуемые автономным приложением, в один файл самораскрывающегося архива, называющегося MATLAB Math and Graphics Run-Time Library Installer. Вместо того чтобы включать в дистрибутив приложения библиотеки времени исполнения по отдельности, достаточно использовать дополнительно лишь один архивный файл. Этот файл находится по следующему пути:

"$MATLAB\extern\lib\win32\mglinstaller.exe"

где "$MATLAB"  – директория установки MATLAB в системе.

После того как пользователь раскроет архив в директорию по его желанию, например  "mgl_runtime_dir", он должен добавить  "mgl_runtime_dir\bin\win32" к переменной окружения PATH.

  1.  Оптимизация исполнения

Возможно, что этот раздел не столь уж важен для изложения концепции перевода m-кода в C-код, все-таки довольно интересно проследить за теми дополнительными возможностями, которые предоставляет MATLAB Compiler.

Компилятор версии 2.1 (2.2) предоставляет ряд оптимизаций, которые могут помочь ускорению исполнения скомпилированного кода. Оптимизация повышает производительность, когда:

  •  код манипулирует скалярными и нескалярными массивами,
  •  код содержит простое одно- или двумерное индексирование массивов,
  •  код содержит циклические конструкции с целочисленным начальным значением и целочисленным инкрементом
  •  код содержит условные выражения, где оба операнда являются целыми

Оптимизация массивов

Когда включена оптимизация для скалярных массивов, то все константы, операции с массивами скалярных значений «свертываются» во время компиляции и сохраняются в пуле констант, который создается однажды во время инициализации программы. Свертывание уменьшает число вычислений, которые происходят во время исполнения, что повышает производительность времени исполнения программы.

Оптимизация нескалярных массивов схожа с оптимизацией скалярных; она свертывает переменные типа mxArray в массивы времени компиляции, которые инициализируются во время запуска программы. Это может оказать большое влияние на производительность, если создавались массивы, использовавшиеся с [ ] и {} внутри циклов. Тем не менее, данная оптимизация делает код плохо читаемым.

Оптимизация циклов

Без оптимизации простого одно- или двумерного индексирования массивов все индексации массивов используют полную основную функцию индексирования, которая не оптимизирована для таких простых массивов. С включенной данной оптимизацией используется более быстрая процедуру, которая уже оптимизирована для такого простого индексирования.

Оптимизация упрощения циклов определяет, когда начальное значение цикла и инкремент являются целыми числами. В этом случае происходит замена данного цикл на более простой, который, в свою очередь, использует переменные Си целого типа взамен переменных массивного типа. Улучшение производительности с использованием данной оптимизации может быть впечатляющим.

Оптимизация условных выражений

Использование данного вида оптимизации заменяет условные операторы MATLAB на условные скалярные операторы Си, когда оба операнда являются целочисленными скалярами. Компилятор «знает», что nargin, nargout3 и контрольная переменная цикла for являются целыми скалярами.

  1.  Альтернативный способ компиляции m-файлов

Помимо компиляции с помощью макроса "–m", возможна раздельная компиляция группы m-файлов.

Для примера, если имеются следующие файлы "file1.m", "file2.m" и некоторый главный файл "main.m", то раздельную компиляцию делать необходимо следующим образом:

mcc -t main.m (будет создан файл "main.c")

mcc -t file1.m (будет создан файл "file1.c")

mcc -t file2.m (будет создан файл "file2.c")

mcc -W main main file1 file2 (будет создан файл "main_main.c")

Далее необходимо скомпилировать и скомпоновать получившийся исходный С-код в автономное приложение:

mbuild main_main.c file1.c file2.c

Материал, использованный в данных главах, был преимущественно заимствован из технической документации [5] и [6]. Данные источники прошли творческую переработку вследствие того, что охватывают более обширную предметную область, чем цели данной главы. Но если читатель заинтересуется данной темой, то он может почерпнуть больше информации из Help, особенно если ему понадобится создавать MEX-файлы. Следует заметить, что изучения данного материала должно быть достаточно для самостоятельного создания автономных приложений и разделяемых библиотек с помощью системы MATLAB.

Далее на примере будет описано создание исполняемого модуля и будет проведен расчет изменения производительности полученного кода.

  1.  Перевод алгоритмов нелинейной фильтрации

  1.  Математическое представление алгоритмов нелинейной фильтрации

 

Математические алгоритмы нелинейного фильтра, модель системы, начальные условия представлены в табл. 4.1. Как видно из таблицы, программная реализация была построена на большом числе элементарных матричных операций, производится обращение матриц, решается два дифференциальных уравнения, а также применяется большое число циклов. Таким образом, ожидается ускорение исполнения скомпилированного m-кода.

  1.  Процесс создания исполняемого модуля

Однако прежде чем проводить компиляцию кода, необходимо произвести хотя бы некоторую «косметическую» модификацию исходного m-кода. Проведенные операции, скорее всего, станут общими для трансляции любого кода на Си.

В первую очередь необходимо преобразовать m-файлы сценарного типа в m-файлы функционального типа. Как это делается, описано в разделе .

Далее следует преобразовать операции с массивами следующего типа таким образом (см. рис. 1.2).

for  i = 1 : N, for  i = 1 : N,

  ............    ............

  M = [M; <значение>];    M[i] = <значение>;

  ............    ............

end end

                                       Рис. 1.2. Преобразование операций с массивами

Данное обозначение очень условно, но достаточно информативно. Такую же операцию необходимо применить и к многомерным массивам. Размер кода во втором случае, возможно, немного и увеличится, но скорость выполнения цикла может увеличиться во много раз.

Также необходимо обязательно зарезервировать память для массива M следующим образом:

M = zeros(n, m);

где n и m – размерности массива.

Для того, чтобы доказать эффективность такой замены, был сконструирован простейший тест. Было создано два файла-функции, содержащие вышеописанные циклы, внутри которых еще находилось условие для вывода прогресса счета, на выходе строился график зависимости времени выполнения программ от числа итераций цикла (см. рис. 1.3). Хорошо видно, что преобразование очень эффективно даже для таких примитивных операций. Здесь рост времени преобразуемой операции имеет экспоненциальный характер в силу постоянного динамического увеличения памяти, отводимой под массив, когда как время работы второго цикла мизерно (для распространенного числа отсчетов в 10000 имеем следующее соотношение по времени тип1/тип2 как 0,02сек/6,72сек в тестируемой конфигурации, см. несколько ниже).

На следующем шаге рекомендуется удалить все комментарии MATLAB 

% комментарий

на русском языке, так как  некоторые из них вызывают ошибку компиляции. Полностью данная проблема не выяснена, однако известно, что русская буква "я", встречающаяся как в комментариях, так и в функциях текстового вывода и т.п., воспринимается в качестве переноса строки. «Разорванная» строка, в свою очередь, вызывает ошибку компиляции. Данную проблему можно разрешить уже после перевода m-кода на Си (см. раздел ).

Также рекомендуется, во избежание ошибок, привести к единому регистру имена файлов, название функций и наименование функций-параметров.

Не используйте переменную с именем "j" в алгоритмах, которые собираетесь переводить на C++, так как у класса MathWorks есть метод MathWorks::j(), в противном случае возникнет ошибка компиляции.

После проделанных манипуляций m-файлы готовы к компиляции.

Для компиляции использовались следующий перечень файлов алгоритмов нелинейной фильтрации: "main72.m", "geta.m", "getx.m", "getz.m", "ht.m", "htnoise.m", "meandr.m", "smooth.m", "solvesys.m".

Исходно в алгоритм входили функции графики MATLAB и компилировать необходимо было бы со следующими опциями: "mcc -m -B sgl", но для исследования скорости выполнения эти функции были исключены (см. раздел ). Поэтому использовался следующий синтаксис из командной строки:

mcc -m main72.m htnoise.m solvesys.m

Легко заметить, что в командной строке вызывается всего три файла, а не девять. Здесь было использовано преимущество макроса "-m", включающего в себя опцию "-h", которая автоматически подключает к приложению остальные, являющиеся вспомогательными, m-файлы. Два последних файла-функции вызываются по имени из ode45  и не являются вспомогательными, поэтому указаны дополнительно в командной строке.

Необходимо сделать еще одно замечание: бывают случаи, когда компилятор не может подключить некоторые служебные функции MATLAB, которые, в свою очередь, тоже являются вспомогательными (например, title и grid), так как имеют свой m-файл, но располагаются в дереве директорий MATLAB. Встретив такую функцию, компилятор выдает ошибку. При возникновении такой ситуации следует просто скопировать m-файл, соответствующий данной функции, в директорию к вызывающему ее m-файлу функционального типа.

Используемые файлы находились в одной директории, откуда и был вызван mcc. Получившийся исполняемый модуль будет находиться в этой же директории и будет иметь имя "main72.exe", т.е. имя первого m-файла, использованного для компиляции.

  1.  Сравнение скоростей исполнения созданного кода

Сравнение скорости проводилось применительно к алгоритмам нелинейной фильтрации, построение исполняемого модуля которых проводилось в предыдущем разделе.

Сравнение осуществлялось на одном компьютере под управлением двух различных операционных систем. При компиляции использовались режимы с включенной оптимизацией и без нее, различные языки трансляции (C/C++), а также при использовании языка Си тестировались различные ANSI C компиляторы.

Конфигурация оборудования:

  •  Celeron 400 MHz (4 x 100) без кеша L2
  •  Acorp 6BX67 (Intel 443BX)
  •  ОЗУ 192 Mb PC133
  •  HDD 30.7 Gb IDE IBM (DTLA 305030) UDMA100

Используемы OC:

  •  Microsoft Windows Millennium Edition
  •  Microsoft Windows XP Home Edition

Режимы оптимизации:

  •  "mcc -O all" – включает все возможные режимы оптимизации
  •  "mcc -O none"отключает оптимизацию вообще

Языковые режимы:

  •  "mcc -m"  формируется исполняемый модуль из кода на Си (оптимизация включена в данном режиме)
  •  "mcc -p"  формируется исполняемый модуль из кода на C++  (оптимизация включена в данном режиме)

Используемые ANSI C компиляторы и интерпретаторы:

  •  Lcc C version 2.4 (поставляется вместе с MATLAB)
  •  Borland C++Builder version 5.0 (содержит компилятор Borland C/C++ version 5.5)
  •  Borland C++Builder version 5.0 для компиляции с C++
  •  Интерпретатор MATLAB

Результаты тестирования приведены в табл. 1.4.

Табл.1.4. Сравнение времени исполнения тестируемого приложения в различных системах

Язык трансляции

ANSI C компилятор

Операционная система

Оптимизация

среднее время выполнения, c

1.

Си

Borland C/C++ v5.5

Windows ME

да

18,82

2.

Си

Lcc C v2.4

Windows ME

да

19,46

3.

Си

Borland C/C++ v5.5

Windows ME

нет

20,34

4.

Си

Lcc C v2.4

Windows ME

нет

21,00

5.

Си

Borland C/C++ v5.5

Windows XP

да

18,10

6.

Си

Lcc C v2.4

Windows XP

да

18,70

7.

Си

Borland C/C++ v5.5

Windows XP

нет

20,04

8.

Си

Lcc C v2.4

Windows XP

нет

20,38

9.

C++

Borland C/C++ v5.5

Windows ME

да

27,04

10.

С++

Borland C/C++ v5.5

Windows XP

да

26,28

11.

C++

Borland C/C++ v5.5

Windows ME

нет

38,87

12.

С++

Borland C/C++ v5.5

Windows XP

нет

37,74

13.

Интерпретатор MATLAB

Windows ME

39,75

14.

Интерпретатор MATLAB

Windows XP

37,81

   Табл. 1.5. Сравнение размеров исполняемых модулей для разных компиляторов

Язык трансляции

ANSI C компилятор

Оптимизация

Размер EXE-файла, Кб

1.

Си

Borland C/C++ v5.5

да

84,00

2.

Си

Lcc C v2.4

да

60,02

3.

Си

Borland C/C++ v5.5

нет

83,00

4.

Си

Lcc C v2.4

нет

56,73

5.

C++

Borland C/C++ v5.5

да

504,5

6.

C++

Borland C/C++ v5.5

нет

549,0

Суммарный размер m-файлов составляет 3296 байт (9 файлов).

  1.  Итоги исследования

На основании полученных таблиц можно сделать следующие выводы:

  •  Никогда не следует отключать оптимизацию.
  •  Если это возможно, то использовать компилятор с Си.
  •  По возможности использовать операционную систему на базе MS Windows NT.
  •  При компиляции на язык C++ ни в коем случае нельзя отключать оптимизацию.

Дополнительно рекомендуется использовать компилятор C++ в исключительных случаях, если требуется написать приложение, содержащее обращение к объектам C++ вперемешку с математическим кодом. В частности, возможно «смешивание» кода на Си и C++, где код на этих языках разграничен модулями, а взаимное обращение осуществляется с помощью функций имеющих соответствующие модификаторы (см. раздел ), в этом случае размер исполняемого модуля будет гораздо меньше, а время выполнения заметно уменьшится.


  1.  Технология использования C-кода, полученного трансляцией из m-кода, в визуальной среде программирования

  1.  Идея и краткое описание технологии

При необходимости решить сложную математическую задачу, требующую использования математических процедур, не входящих в набор стандартных средств программирования, можно пойти разными путями. Либо спешно искать (например, в Internet) соответствующую математическую библиотеку, совместимую с данной средой программирования (при этом часто неизвестно, корректно ли она работает, каковы правила ее использования и обладает ли она достаточными возможностями). Либо писать программный код соответствующих математических процедур самому. Либо воспользоваться средствами, предоставляемыми каким-нибудь математическим пакетом, например, пакетом MATLAB. Последний имеет в своем составе мощную математическую библиотеку MATLAB C/C++ Math Library. Вдобавок, MATLAB является довольно распространенным средством среди специалистов, и алгоритм, который необходимо реализовать в качестве программного комплекса уже имеется в виде m-файлов, в этом случае можно просто перевести m-код на С-код, которым затем воспользоваться при создании «реального» приложения.

Проделав все описанные в предыдущей главе манипуляции над m-кодом, в итоге можно получить лишь исполняемый файл автономного консольного приложения, что является пригодным для использования, когда приложению не требуется хорошо организованного ввода и вывода данных. Использование такого подхода не отвечает требованиям профессионального программирования, использующего широкую гамму возможностей, предоставляемых современными средствами создания приложений. Использование же эффективного математического кода MATLAB совместно с возможностями мощных визуальных сред разработки позволит программисту решить практически любую прикладную задачу. Исчезают такие ограничения MATLAB, сковывающие разработчиков, как создание грамотного Windows-интерфейса, простого использование баз данных и т.д. При этом пользователи таких сред как Microsoft Visual C/C++ и Borland C++ Builder станут обладателями математической мощи MATLAB, которая грамотно создавалась в течение многих лет (и продолжает развиваться) трудом большого коллектива математиков-программистов фирмы "MathWorks".

В этой связи, данная главе будет содержать описание процесса использования m-кода в приложении на Си, выполненное в виде некоторой технологии, кратко описать которую можно следующими этапами:

Создание исходного m-кода.

Перевод исходного m-кода в C-код с помощью MATLAB Compiler.

Внедрение созданного компилятором MATLAB C-кода в приложение, разрабатываемое в визуальной среде программирования.

Внесение в приложение новых функциональных возможностей.

Основные элементы технологии были изложены выше, далее же проиллюстрируем на небольшом примере практическую составляющую технологии. Пример был выбран с тем расчетом, чтобы как можно проще пояснить технологические этапы, задействовав при этом графические возможности MATLAB. Также здесь будет делаться  упор на минимальное изменение кода, генерированного компилятором MATLAB. Использование элементов графики несколько усложняет изложение, однако визуальное представление результата работы программы является зачастую неотъемлемой частью создаваемого программного комплекса.

  1.  Этап 1. Создание исходного m-кода

Наличие данного этапа может показаться необязательным, так как исходный код можно писать сразу на Си, используя функции математической библиотеки MATLAB. Стоит сразу оговорить, что данный путь не оптимален по нескольким причинам. Во-первых, создавая приложение таким образом, пользователь теряет возможность контекстной отладки кода, во-вторых, построенный код может получиться неустойчивым в плане исполнения, в-третьих, даже если создается чисто C-приложение, содержащее небольшие участки сложных математических вычислений, изначально проще их предварительно написать в MATLAB в виде функций, затем перевести полученный m-код в С-код с помощью компилятора MATLAB, полученный код впоследствии легко подключить в виде модуля к приложению на C\C++.

Итак, создадим следующий m-файл функционального типа:

% Файл "plt.m"

function plt

 tic

 x = 1:10;

 y = 1:10;

 plot(x, y);

 set(gca, 'FontName', 'FixedWidth', ...

          'XGrid',    'on', ...

          'YGrid',    'on');

 title('Привет');

 legend('Это пример');

 time = toc;

 disp(time);

Данный код создает два вектора по 9 элементов, содержащих числа от 1 до 10 с шагом 1, затем с помощью функции plot строится прямая, соответствующая векторам. К графику задается заголовок с помощью функции title, а также надпись для отрезка при помощи legend. Также посредством функции set устанавливаются свойства текущего окна (gca). Свойство FontName, установленное в значение FixedWidth (т. е. Courier – по умолчанию) или шрифт с символами фиксированной ширины, позволяет выводить на рисунок надписи на русском языке, в противном случае надписи будут не читаемыми. Такого же эффекта можно добиться, если установить свойство FontName всех объектов MATLAB типа Axes и text по умолчанию в FixedWidth. Для этого необходимо в начале программы поместить следующие две строчки кода:

set(0, 'DefaulttextFontName', 'FixedWidth'),

set(0, 'DefaultAxesFontName', 'FixedWidth'),

которые устанавливают FontName по умолчанию для глобального объекта Root, являющегося родителем для всех создаваемых объектов.

Свойства XGrid и YGrid, установленное в значение on, включают отображение горизонтальной и вертикальной сетки на рисунке. И в конце выводится время работы программы в секундах с помощью функции disp, подсчитываемое парой tic и toc.

После создания исходного m-кода можно перейти к следующему этапу.

  1.  Этап 2. Перевод m-кода в C-код с помощью MATLAB Compiler

  1.  Замечания по переводу

Данный процесс подробнейшим образом описан в предыдущей главе, где также указаны рекомендации по созданию «переводимого» m-кода.

Однако компилятор MATLAB выдаст ошибку во время преобразования на функции title. Это связано с тем, что данная функция не входит ни в одну из используемых компилятором библиотек, а присутствует в качестве дополнительного инструментария в директории "%MATLAB%\toolbox\matlab\graph2d\title.m". Чтобы можно было воспользоваться данной функцией в коде программы, необходимо вставить содержимое этого файла в конец файл "plt.m".

%Файл "plt.m"

function plt

  < .. >4  

function hh = title(string, varargin)

 ax = gca; h = get(ax, 'title');

 if nargin > 1 & (nargin-1)/2-fix((nargin-1)/2),

   error('Incorrect number of input arguments')

 end

 set(h, 'FontAngle',  get(ax, 'FontAngle'), ...

        'FontName',   get(ax, 'FontName'), ...

        'FontSize',   get(ax, 'FontSize'), ...

        'FontWeight', get(ax, 'FontWeight'), ...

        'Rotation',   0, ...

        'string',     string, varargin{:});

 if nargout > 0

   hh = h;

 end,

Аналогичная ситуация с функцией grid (on/off) (в версии компилятора 2.2 из MATLAB 6.1 данная функция транслируется без проблем), ее также можно подключить в виде дополнительной функции к основе, но функция очень громоздка и рассчитана на различное употребление и, в конце концов, лишь установит значения соответствующих свойств. Вместо использования функции title, как и в случае с grid (on/off), можно установит свойство String у свойства Title текущего axes следующим образом:

set(get(gca, 'Title'), 'String', 'Привет'),

хотя копирование содержимого файла "title.m" не является более сложной процедурой, к тому же решает проблему совместимости конвертируемого кода.

Следует сделать еще одно замечание по трансляции. Довольно удобно, но совершенно не обязательно, если весь m-код будет сосредоточен в одном файле. Все используемые подфункции вполне комфортно могут сосуществовать в одном программном модуле, и даже те, которые используются в других функциях по имени как строка (# ode45, feval и др.). Например, в программе имеется строка

[C, XX] = ode45(’htnoise’, TP, x0, options);

где ‘htnoise’ – имя m-файла функционального типа "htnoise.m", который должен находится в той же директории, что и вызывающий файл. Если же содержимое этих файлов объединить, то вызов нужно производить с помощью function handle следующим образом

[C, XX] = ode45(@htnoise, TP, x0, options);

  1.  Изучение кода, генерируемого MATLAB Compiler

Подробнее рассмотрим генерируемый компилятором MATLAB С-код:

/* Файл "plt.c" */

/* Заголовочный файл, содержащий прототипы функций с префиксом extern, которые

* вызываются из других модулей. */

#include "plt.h"

/* Заголовочные файлы, содержащие прототипы библиотечных функций. */

#include "libmatlbm.h"

#include "libmmfile.h"

#include "libmwsglm.h"

/* Группа служебных переменных, содержащих сообщения об ошибках, которые

* возникают в случае, если пользователь вызовет "функцию-переходник" с

* префиксом "mlx" с большим, чем необходимо, числом входных и выходных

 * параметров. */

static mxChar _array1_[124] = { 'R', 'u', 'n', '-', 't', 'i', 'm', 'e', ' ',

5

/* Данный блок содержит описание рабочих строковых и массивных констант,  

* которыми инициализируются рабочие переменные. */

static double _array7_[10] = { 1.0, 2.0, 3.0, 4.0, 5.0,

                              6.0, 7.0, 8.0, 9.0, 10.0 };

static mxArray * _mxarray6_;

static mxChar _array9_[8] = { 'F', 'o', 'n', 't', 'N', 'a', 'm', 'e' };

/* Функция, инициализирующая рабочие переменные. Использование данного способа

* инициализации является внутренним элементом оптимизации трансляции. */

void InitializeModule_plt(void) {

   _mxarray0_ = mclInitializeString(124, _array1_);

   _mxarray6_ = mclInitializeDoubleVector(1, 10, _array7_);

 

}

/* Функция, уничтожающая ранее инициализированные рабочие переменные. */

void TerminateModule_plt(void) {

 

   mxDestroyArray(_mxarray0_);

}

/* Заголовки нижеописанных функций. */

static mxArray * mlfNPlt_title(int nargout, mxArray * string, ...);

static void mlxPlt_title(int nlhs,

                        mxArray * plhs[],

                        int nrhs,

                        mxArray * prhs[]);

static void Mplt(void);

static mxArray * Mplt_title(int nargout_, mxArray * string, mxArray * varargin);

/* Описание элементов локальной таблицы функций, которые могут быть вызваны по

* имени через feval-интерфейс. Для каждой функции создается отдельная запись,

* содержащая, соответственно, имя функции, имя «функции-переходника», число  

* входных и выходных параметров, а также адрес локальной для этой функции

 * таблицы. */

static mexFunctionTableEntry local_function_table_[1]

 = { { "title", mlxPlt_title, -2, 1, NULL } };

/* Описание самой локальной для этого файла таблицы функций, содержащей число

* записей и имя таблицы элементов. Это описание будет использовано в качестве

* элемента глобальной таблицы функций в файле "plt_mainhg". */

_mexLocalFunctionTable _local_function_table_plt = { 1, local_function_table_ };

/* Функция "mlfPlt" содержит стандартный интерфейс для m-функции "plt" из файла

 * "E:\Diplom\graph\plt.m" (строки 1-9). Эта функция обрабатывает все входные

* параметры и передает их в реализующую версию "Mplt". */

void mlfPlt(void) {

   mlfEnterNewContext(0, 0);

   Mplt();

   mlfRestorePreviousContext(0, 0);

}

/* Функция "mlxPlt" содержит дополнительный интерфейс, использующийся функцией

* feval, для m-функции "plt" из файла "E:\Diplom\graph\plt.m" (строки 1-9).

* Эта функция обрабатывает все входные параметры и передает их в

* реализующую версию. */

void mlxPlt(int nlhs, mxArray * plhs[], int nrhs, mxArray * prhs[]) {

   /* Здесь осуществляется проверка наличия необходимого числа входных и

    * выходных параметров функции, если их больше нужного количества, то

    * выводится сообщение об ошибке и работа программы прерывается. */

   if (nlhs > 0) {

       mlfError(_mxarray0_);

   }

   if (nrhs > 0) {

       mlfError(_mxarray2_);

   }

   /* Далее реализуется автоматическое управление памятью MATLAB C Math Lib. */

   mlfEnterNewContext(0, 0);

   Mplt();

   mlfRestorePreviousContext(0, 0);

}

/* Функция "mlfNPlt_title" содержит специальный nargout-интерфейс для m-функции

 * "plt/title" из файла "E:\Diplom\graph\plt.m" (строки 9-29). Данный интерфейс

* порождается лишь в том случае, когда m-функция использует специальную

* переменную "nargout". Данный интерфейс позволяет описать через параметр

* nargout число требуемых выходов в противоположность стандартному интерфейсу,

* который динамически вычисляет число выходов, основываясь на числе

* непустых получаемых входов. Эта функция обрабатывает все входные параметры и

* передает их в реализующую версию. */

static mxArray * mlfNPlt_title(int nargout, mxArray * string, ...) {

   mxArray * varargin = NULL;

   mxArray * hh = mclGetUninitializedArray();

   mlfVarargin(&varargin, string, 0);

   mlfEnterNewContext(0, -2, string, varargin);

   hh = Mplt_title(nargout, string, varargin);

   mlfRestorePreviousContext(0, 1, string);

   mxDestroyArray(varargin);

   return mlfReturnValue(hh);

}

/* Функция "mlxPlt_title" содержит дополнительный интерфейс, использующийся

* функцией feval, для m-функции "plt" из файла "E:\Diplom\graph\plt.m" (строки

* 9-29). Эта функция обрабатывает все входные параметры и передает их в

 * реализующую версию. */

static void mlxPlt_title(int nlhs, mxArray * plhs[], int nrhs,

                        mxArray * prhs[]) {

 

}

/* Функция "Mplt" является реализующей версией m-функции "plt" из файла

 * "E:\Diplom\graph\plt.m" (строки 1-9). Она содержит фактически

* скомпилированный код для данной m-функции. Данная функция является

* статической и может быть вызвана лишь из интерфейсной функции. */

/* function plt */

static void Mplt(void) {

   mexLocalFunctionTable save_local_function_table_=

                    mclSetCurrentLocalFunctionTable(&_local_function_table_plt)

   mxArray * ans = mclGetUninitializedArray();

   mxArray * y = mclGetUninitializedArray();

   mxArray * x = mclGetUninitializedArray();

   /* tic */     mlfTic();

   /* x=1:10; */ mlfAssign(&x, _mxarray6_);

   /* y=1:10; */ mlfAssign(&y, _mxarray6_);

   /* plot(x,y); */

   mclAssignAns(&ans, mlfNPlot(0, mclVv(x, "x"), mclVv(y, "y"), NULL));

   /* set(gca,'FontName','FixedWidth','XGrid','on','YGrid','on'); */

   mclAssignAns(&ans,

     mlfNSet(0, mclVe(mlfGca(NULL)), _mxarray8_, _mxarray10_, _mxarray12_,

       _mxarray14_, _mxarray16_, _mxarray14_, NULL));

   /* title('Привет'); */

   mclAssignAns(&ans, mlfNPlt_title(0, _mxarray18_, NULL));

   /* legend('Это пример'); */

   mclAssignAns(&ans, mlfNLegend(0, NULL, _mxarray20_, NULL));

   /* time=toc; */

   mlfAssign(&time, mlfNToc(1));

   /* disp(time); */

   mlfDisp(mclVv(time, "time"));

   mxDestroyArray(x);

   mxDestroyArray(y);

   mxDestroyArray(ans);

   mclSetCurrentLocalFunctionTable(save_local_function_table_);

}

/* Функция "Mplt_title" является реализующей версией m-функции "plt/title" из

 * файла "E:\Diplom\graph\plt.m" (строки 9-29). Она содержит фактически

* скомпилированный код для данной m-функции. Данная функция является

* статической и может быть вызвана лишь из интерфейсной функции. */

/* function hh = title(string,varargin) */

static mxArray * Mplt_title(int nargout_,

                           mxArray * string,

                           mxArray * varargin) {

 

 return hh;

}

/* Файл "plt_mainhg.c" – «файл-оболочка» автономного консольного приложения. */

#include "libmatlb.h"

#include "libsgl.h"

#include "plt.h"

#include "libmmfile.h"

#include "libmwsglm.h"

/* Здесь могут находиться объявления глобальных переменных. */

/* Здесь может находиться описание элементов таблицы глобальных переменных. */

 

/* Описание элементов глобальной таблицы файлов-функций, которые соответствуют

* отдельным модулям, используемым при сборке исполняемого файла. Модули же

* генерируются из соответствующих m-файлов с кодом m-функций. Для каждой

* функции создается отдельная запись (см. ниже описание таблицы). */

static mexFunctionTableEntry function_table[1]  =

{ {  

   "plt", /* Имя файла-функции (тип const char *). */

   mlxPlt, /* Имя «функции-переходника»(тип mxFunctionPtr). */

   0, /* Число входных параметров (тип int). */

   0, /* Число выходных параметров (тип int). */

   &_local_function_table_plt  /* Адрес локальной таблицы функций (тип struct

                                *_mexLocalFunctionTable).*/

   } };

/* Описание элементов таблицы парных функций, инициализирующих и завершающих

* использование специализированных библиотек, а также рабочих переменных

* соответствующих программных модулей. */

static _mexInitTermTableEntry init_term_table[3]

 = { { libmmfileInitialize, libmmfileTerminate },

     { libmwsglmInitialize, libmwsglmTerminate },

     { InitializeModule_plt, TerminateModule_plt } };

/* Структура, содержащая всю глобальную информацию о модулях приложения

* (расшифровку содержимого см. ниже). */

static _mex_information _main_info  =

{

   1, /* Версия типа данной структуры (int). */

   1, /* Размер таблицы файлов-функций (int). */

   function_table, /* Таблица файлов-функций (mexFunctionTable). */

   0, /* Размер таблицы глобальных переменных (int). */

   NULL, /* Таблица глобальных переменных (mexGlobalTable). */

   0, /* (Только для UNIX) Число элементов списка директорий (int). */

   NULL, /* (Только для UNIX) Список директорий для поиска библиотек.  */

   3, /* Размер таблицы инициализации и завершения (int). */

   init_term_table/*Таблица инициализации и завершения(mexInitTermTableEntry)*/

};

/* Функция main, содержащая вызов функции mclMainhg, которая, в свою очередь,

* инициализирует описанные выше таблицы с помощью _main_info, графику и

* запускает приложение через feval-интерфейс. */

int main(int argc, const char * * argv) {

   return mclMainhg(argc, argv, mlxPlt, 0, &_main_info);

}

Одновременно с кодом можно создать исполняемый модуль, который будет прекрасно работать как Console Application. Но целью представляемой технологии является использование данного   кода   совместно с элементами визуального программирования. Поэтому необходимо перейти к следующему технологическому этапу.

  1.  Этап 3. Присоединение созданного компилятором MATLAB C-кода к проекту, разрабатываемому в визуальной среде программирования

В качестве визуальной среды программирования был выбран Borland C++ Builder version 5.0 (6.0), помимо удобного пользовательского интерфейса предоставляющий мощный компилятор кода на C/C++.

Итак, создадим проект в этой среде, содержащий форму, и поместим кнопку и метку на эту форму. Но прежде, чем присоединять полученный на предыдущем этапе код, необходимо настроить данную среду на  совместное использование с библиотеками MATLAB C Math Library и MATLAB C Graphics Library.

  1.  Использование Borland C++ Builder совместно с библиотеками MATLAB

Чтобы стала возможной компиляция проектов в последних версиях Builder (v. 4.0 или 5.0, 6.0) достаточно проделать следующие шаги:

  •  На панели элемента меню Project -> Options имеется закладка   Directories/Conditionals. Модифицируйте пути Include и Library так, чтобы они указывали на местоположение библиотек MATLAB  (см. рис. 2.1. и рис. 2.2).

На рис. "C:\Program Files\MATLAB\" является конкретным местоположением MATLAB на диске и может быть любым другим.

Стоит расшифровать в текстовой форме:

Include. $(MATLAB)\extern\include\cpp

 $(MATLAB)\extern\include

Library. $(MATLAB)\lib\win32

 $(MATLAB)\lib\win32\borland\bc54

Данная нотация соответствует Borland C++ Builder version 6.0, где "MATLAB" является переменной системного окружения, установить которую можно на закладке Environment Variables элемента главного меню Tools -> Environment Options. В предыдущей версии такой возможности не имеется.

  •  Затем следует подключить к проектному ".cpp"-файлу следующие необходимые для компоновки  библиотеки:

$(MATLAB)\extern\lib\win32\libmatpb55.lib,

$(MATLAB)\extern\lib\win32\borland\bc54\sgl.lib,

$(MATLAB)\extern\lib\win32\borland\bc54\libmat.lib,

$(MATLAB)\extern\lib\win32\borland\bc54\libmatlb.lib

$(MATLAB)\extern\lib\win32\borland\bc54\libmmfile.lib,

$(MATLAB)\extern\lib\win32\borland\bc54\libmwsglm.lib,

$(MATLAB)\extern\lib\win32\borland\bc54\libmx.lib,

добавив их в проект через панель Project -> Add to Project… Для других версий Builder (v.3.0 и v4.0) используйте библиотеку "libmatpb5x.lib", где x – номер версии Builder.

Если в исходном m-коде не производится вызов графических функций MATLAB, то библиотеки "sgl.lib" и "libmwsglm.lib" можно не подключать к проекту.

  •  Теперь можно компоновать проект.

  1.  Добавление C-кода в проект

На данном этапе постараемся произвести минимальную модификацию созданного MATLAB Compiler C-кода, который, если вспомнить, состоит из трех файлов: "plt.c", "plt.h", "plt_mainhg.c". Поскольку файл "plt_mainhg.c" отвечает за создание автономного консольного приложения, то он более не потребуется, однако содержащийся в нем код, за исключением функции main, нужно перенести в создаваемый проект, для удобства в ".h"-файл, дополнительно подключаемый к  ".cpp"-файлу, реализующему методы формы (см. несколько ниже).

Теперь подключим к проекту файл  "plt.c", произведя следующую небольшую модификацию кода.

// Файл "plt.c" -------------------------------------------------------------

#include "mhelper.h" // Содержит (пока) лишь прототип функции WinFlush()

< .. >

static void Mplt(void) {

 < .. >

 mlfDisp(mclVv(time, "time"));

 WinFlush();

 mxDestroyArray(ans);

 < .. >

}

< .. >

Хорошо видно, что изменения кода минимальны. Был лишь подключен файл, содержащий объявление функции Winflush, использовавшейся после mlfDisp для одновременного вывода времени работы программы на форму, полученное от Print Handler.

  1.  Что такое Print Handler?

В прошлом, когда существовали одни лишь символьные терминалы, ввод и вывод осуществлялся довольно просто; программы использовали scanf для ввода и printf для вывода. Графический пользовательский интерфейс (GUI) использует более сложные процедуры ввода-вывода. MATLAB C Math Library создана таким образом, что может работать как с символьным терминалом, так и с графической оконной средой. Использование printf или подобного рода процедур актуально для текстового вывода, но не удовлетворительно для вывода в графическую среду.

Сама библиотека производит мало вывода; в частности, выводит сообщения об ошибках и предупреждения, при этом, не осуществляя никакого ввода. Для поддержки программирования в графических средах библиотека позволяет определить пользователю, каким образом она должна осуществлять вывод.

Требования к выводу математической библиотеки MATLAB очень просты. Библиотека самостоятельно формирует на выход строку символов, а затем вызывает функцию, которая печатает эту строку. Если пользователь хочет изменить «когда и каким образом»  библиотека должна осуществлять вывод, то ему необходимо предоставить свой обработчик печати (Print Handler).

Взамен прямого вызова printf, библиотека MATLAB вызывает обработчик печати, когда ей необходимо вывести сообщение об ошибке или предупреждение. Print Handler по умолчанию, используемый библиотекой, имеет единственный параметр – const char * (выводимое сообщение) и возвращает void. Print Handler по умолчанию имеет следующий вид:

static void DefaultPrintHandler(const char *s)

{

   printf("%s",s);

}

Процедура направляет вывод на stdout Си, используя функцию printf.

Если необходимо производить вывод другим способом, то нужно написать свой Print Handler и зарегистрировать для библиотеки. Любой создаваемый обработчик печати должен соответствовать прототипу обработчика по умолчанию (см. выше).

Чтобы зарегистрировать пользовательскую функцию и настроить библиотеку на использование нового обработчика, необходимо вызвать функцию mlfSetPrintHandler, которая имеет единственный аргумент –  имя функции-обработчика.

  1.  Реализация пользовательского Print Handler

Далее рассмотрим вариант пользовательского обработчика печати, который применялся в описываемом примере. Он сконструирован таким образом, что его удобно использовать при выводе матриц и других составных объектов печати, но для полноценной работы ему требуется функция, которая осуществляла бы вывод накопленного текста на форму и, желательно, чтобы она вызывалась следом за выводящей библиотечной функцией (например, mlfPrintMatix, mlfDisp, mlfPrintf и т.д.).

//--------------------------- Print Handler ---------------------------------

static int totalcnt  = 0;

static int upperlim  = 0;

static int firsttime = 1;

char *OutputBuffer;

void WinPrint( char *text)

{        int cnt;

       /* Размещается буфер для вывода */

       if (firsttime) {

               OutputBuffer = (char *)mxCalloc(1028, 1);

               upperlim += 1028;

               firsttime = 0;

       }

       /* Производится проверка, что хватает текущего размера буфера */

       cnt = strlen(text);

       while (totalcnt + cnt >= upperlim) {

               char *TmpOut;

               upperlim += 1028;

               TmpOut = (char *)mxRealloc(OutputBuffer, upperlim);

               if (TmpOut != NULL)

                   OutputBuffer = TmpOut;

       }

       /* Присоединяется следующая строка текста */

       strncat(OutputBuffer, text, cnt);

       /* Обновляется число символов, содержащихся в буфере */

       totalcnt += cnt;

}

// Функция, очищающая текущий буфер.

void FreeOutput(void)

{

 mxFree(OutputBuffer);

 firsttime = 1;

}

void WinFlush(void)

{ //Простейший вывод текста, полученного через Print Handler

 Form1->Label1->Caption = OutputBuffer;

 FreeOutput();

}

  1.  Доработка исходного C-кода

Уже было сказано, что функция main, содержащая, в свою очередь, функцию mclMainhg, удалена (проект должен содержать либо main для консольных приложений, либо WinMain для Windows-приложений). Но вспомогательная функция компилятора MATLAB mclMainhg несла большую смысловую нагрузку, которую как раз можно компенсировать вызовом функций компилятора MATLAB из модуля "Unit1.h" (см. ниже). Функция mclMainhg делает следующее:

  •  инициализирует рабочие таблицы: таблицу глобальных переменных, глобальную и локальные таблицы файлов-функций, рабочие переменные модулей и специализированные библиотеки. Также удаляет из памяти рабочие переменные и завершает работу специализированных библиотек. Данное действие компенсируется «ручным» вызовом функции

mclLibInitCommon(&_main_info);

при нажатии кнопки "Run", где &_main_info – адрес структуры, содержащей все вышеперечисленные таблицы.

  •  выполняет инициализацию графической библиотеки MATLAB. Данное действие можно осуществить самостоятельно, единожды вызвав библиотечную функцию mlfHGInitialize, что и было сделано при создании формы приложения.
  •  вызывает через feval-интерфейс рабочий код (через функцию mlxPlt). Аналогичное действие можно выполнить при нажатии "Run" только через стандартный интерфейс (функцию mlfPlt).
  •  завершает работу графической библиотеки MATLAB. Соответствующее действие с помощью функции mlfHGTerminate было осуществлено в файле "Unit1.cpp" в момент закрытия приложения.

// Файл "cpphelper.c" -------------------------------------------------------

< .. > // Содержимое "Plt_mainhg.c", за исключением функции main.

//-------------------------------- Print Handler ----------------------------

// Объявление функции WinFlush(), чтобы ее можно было вызвать из C-модуля.

extern "C" void WinFlush(void);

// --------------------------- Print Handler --------------------------------

< .. > // Вспомогательные переменные WinPrint.

void WinPrint(const char *text)

{

 < .. >

}

void FreeOutput(void)

{

 < .. >

}

// Файл "Unit1.cpp" ---------------------------------------------------------

#include "cpphelper.h" // Дополнительно подключаемый файл (см. выше).

TForm1 *Form1;

__fastcall TForm1::TForm1(TComponent* Owner): TForm(Owner)

{

 const char *argv = *_argv;

 mlfHGInitialize(&_argc, &argv);// Инициализируем MATLAB C Graphics Library

}

void __fastcall TForm1::Button1Click(TObject *Sender)

{

 mlfSetPrintHandler(WinPrint);// Регистрируем обработчик печати

 mclLibInitCommon(&_main_info );// Осуществляем инициализацию таблиц компил-ра

 mlfPlt();//Имя исходной m-функции

}

void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action)

{

 mlfHGTerminate();// Прерываем использование графической библиотеки

}

void WinFlush(void)

{

 < .. >

}

Здесь по нажатию кнопки на форме производится запуск транслированного кода с помощью функций, полностью подменяющих функцию компилятора MATLAB mclMainhg. Следует заметить, что замена mclMainhg необходимое, но необязательное действие. Вызов

mclMainhg(&_argc,&argv, mlxPlt, 0, &_main_info);

произойдет вполне успешно, однако при повторном нажатии на кнопку приложение выдает ошибку. Это связано с тем, что работа данной функции рассчитана на одиночный запуск из консольного приложения. Также пользователь теряет столь удобную возможность отладки подключенного кода, что довольно неприятно. Поэтому настоятельно рекомендуется использовать описанный способ запуска C-кода.

Необходимо сделать еще одно важное замечание. В случае если нет надобности использовать в приложении графику MATLAB, то часть кода, касающуюся инициализации графической библиотеки можно удалить из проекта.

  1.  Этап 4. Внесение в приложение новых функциональных возможностей 

Название данного этапа довольно абстрактно, так как включает большое число технических приемов, касающихся взаимодействия между рабочими средами. В данный этап можно поместить также непосредственную разработку интерфейса пользователя, который уже естественно привязан к назначению создаваемого приложения.

Задачам этого этапа будет посвящена следующая глава, которая, в свою очередь, тоже будет состоять из описания любопытного комплексного примера.

В контексте данного этапа можно произвести расширение и переработку рассматриваемого примера, чтобы «пробросить мостик» между созданием иллюстративного примера и разработкой реального приложения.

Введем в исходный m-код «долгий» цикл, внутри которого будем выводить на экран процент  от общего времени вычислений в алгоритме. Расширив код подобным образом и проделав заново все выше перечисленные технологические этапы, получится следующее: пока будут производиться вычисления, форма примера просто-напросто «умрет». Она не станет отвечать ни на какие внешние или внутренние воздействия и лишь по истечению работы цикла опять «оживет» и, будет выведено, что вычисления произведены на 100%.

Причиной данного явления является то, что математические вычисления осуществляются в контексте главного потока VCL, а не независимо, как-то должно быть. Данное явление не критично, если вычисления не занимают много времени, в противном случае необходимо знать прогресс вычислений и, возможно, досрочно их завершить.

Обратите особое внимание, что при создании нижеописанного примера использовалась следующая комбинация средств – MATLAB C Math/C Graphics Library v2.1, MATLAB Compiler v2.1, Borland C++ Builder v5.0 (содержит компилятор Borland C++ v5.5).

Добавим на форму еще одну кнопку "Stop" и панель статуса (Status Bar), на которую будем отображать процент прогресса и общее время работы «вычислений». По нажатию на кнопку "Run" будет создаваться новый поток класса TNewThread, реализующий в отдельном модуле абстрактный класс TThread, внутри которого уже будут производиться заданные вычисления. При использовании данного стандартного приема программирования возникает две особенности, связанные с использованием функций математической библиотеки MATLAB:

Инициализация графической библиотеки MATLAB и глобальной таблицы функций должна осуществляться в модуле главной формы, в противном случае возникает ошибка. Вследствие этого использование графических функций MATLAB необходимо осуществлять в контексте основного потока VCL. Чтобы реализовать данное действие как можно удобнее, нужно сосредоточить весь графический вывод в одном m-файле  функционального типа, например, "graph.m" (если это, конечно, возможно, иначе в нескольких); переменные, использующиеся в графических функциях, нужно объявить глобальными в главном и создаваемом m-файлах. Затем функцию mlfGraph(), получившуюся после трансляции на язык Си, вызвать в контексте главного потока VCL, а не напрямую, как это делается в главном C-модуле. Вывод текста также нужно производить в контексте основного потока VCL.

  1.   Вызов методов созданного потока нельзя осуществлять напрямую из C-модуля, так как объекты можно использовать лишь в C++. Однако вызов методов потока можно произвести с помощью дополнительных C-функций, которые объявляются с модификатором – extern "C" – в модуле, описывающем методы потока, где также и реализуются. Данная дополнительная функция вызывает метод потока, который, в свою очередь, вызывает с помощью метода Synchronize еще один метод, содержащий защищаемый код (пример см. ниже в файле "Unit2.cpp").

Теперь рассмотрим ключевые фрагменты кода, реализующие вышеописанные изменения  и использованные технические приемы.

% Файл "plt.m"

function plt

global x y,

tic;

x = 1:10; y = 1:10;

endmax = 500000; oldpers = 0;

for i = 1:endmax,

 pers = floor((i / endmax) * 100);

 if pers > oldpers,

   fprintf(1, 'Выполенено %4.0f%%', pers);

   oldpers = pers;

 end

end

graph;

time = toc; fprintf(1, '%3.1f cекунды', time);

% Файл "graph.m"

function graph

global x y,

set(0, 'DefaulttextFontName', 'FixedWidth', ...

      'DefaultAxesFontName', 'FixedWidth');

plot(x, y);

set(gca, 'XGrid', 'on', ...

        'YGrid', 'on');

set(get(gca, 'Title'), 'String', 'Привет');

legend('Это пример');

Далее рассмотрим С-код, реализующий изложенный выше m-код.

// Файл "cpph.h" -----------------------------------------------------------

mxArray * x = NULL;

mxArray * y = NULL;

static _mex_information _main_info

 = { 1, 2, function_table, 2, global_table, 0, NULL, 4, init_term_table };

// Файл "Unit1.cpp" --------------------------------------------------------

#include "Unit1.h" // Содержит описание класса TForm1 и внешнее объявление Form1

#include "Unit2.h" // Содержит описание класса TNewThread и внешнее объявление

                  // MyThread – объекта, инкапсулирующего поток WinAPI

#include "cpph.h"  // Содержит объявление _main_info

int MyExit; // Переменная-флаг, указывающая процессу на досрочное завершение

TNewThread *MyThread;

TForm1 *Form1;

__fastcall TForm1::TForm1(TComponent* Owner): TForm(Owner)

{

 const char *argv = *_argv;

 // При создании формы инициализируем графическую библиотеку MATLAB

 mlfHGInitialize(&_argc, &argv);

}

void __fastcall TForm1::Button1Click(TObject *Sender)

{

 MyExit = 0;

 mclLibInitCommon(&_main_info ); // Инициализируем таблицу _main_info

 Button1->Enabled = false;

 // Создаем новый поток и сразу запускаем его

 MyThread = new TNewThread(false);

}

void __fastcall TForm1::Button2Click(TObject *Sender)

{

 MyExit = 1; Button1->Enabled = true;

}

void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action)

{  // Во время завершения работы приложения выгружаем графическую библиотеку

 mlfHGTerminate();

}

// Файл "Unit2.cpp"----------------------------------------------------------

#include "plt.h" // Содержит внешнее объявление mlfPlt

#include "graph.h" // Содержит внешнее объявление mlfGraph

extern "C" {

 void SyncVCL(int mode); // Функции, объявленные таким образом, что их можно   

 void FinishThread();    // напрямую использовать из C-модуля

}

__fastcall TNewThread::TNewThread(bool CreateSuspended)

       : TThread(CreateSuspended)

{ // Устанавливаем, чтобы поток автоматически уничтожился после завершения

 FreeOnTerminate = true;

}

void __fastcall TNewThread::Execute()

{ // «Тело» потока, в котором выполняем математический код

 mlfPlt(); Form1->Button1->Enabled = true;

}

void __fastcall TNewThread::UpdateMethod(int mode)

{ // Выполняем обновление строки статуса (1) и вызов графики (2) в контексте

 // главного потока VCL с помощью метода Synchronize

 switch (mode)

 {

   case 1: Synchronize(UpdateForm); break;

   case 2: Synchronize(ShowGraph);

 }

}

void __fastcall TNewThread::ShowGraph()

{  mlfGraph();

}

void __fastcall TNewThread::UpdateForm()

{

 Form1->StatusBar1->SimpleText = OutputBuffer;

 FreeOutput();

}

// Дополнительная функция, с помощью которой запускаем методы объекта-потока

void SyncVCL(int mode)

{

 MyThread->UpdateMethod(mode);

}

// Дополнительная функция, досрочно завершающая работу потока

void FinishThread()

{ // Системная функция Borland C++, вызывающая функцию WinAPI ExitThread

 EndThread(0);

}

/* Файл "plt.c" */

/* Содержит объявление дополнительных C-функций синхронизации с потоком VCL и

 * тело Print Handler */

#include "mhelper.h"

extern mxArray * x;

extern mxArray * y;

void mlfPlt(void) {

   mlfEnterNewContext(0, 0);

   Mplt();

   mlfRestorePreviousContext(0, 0);

}

/* function plt */

static void Mplt(void) {

   

   /* Регистрируем для обработчик печати MATLAB C Math Library */

   mlfSetPrintHandler(WinPrint);

   /* global x y, */

    /* x=1:10; */ mlfAssign(mclPrepareGlobal(&x), _mxarray4_);

   /* y=1:10; */ mlfAssign(mclPrepareGlobal(&y), _mxarray4_);

           

           for (; ; ) {

               /* Производим досрочное завершение потока (если необходимо) */

               if (MyExit) {

                 WinPrint("Процесс остановлен!!!"); SyncVCL(1);

                 FinishThread();

               }

           

           }

   /* graph; */

   SyncVCL(2); // Осуществляем вывод графики

   WinPrint("Время выполнения составило ");

   /* fprintf(1, '%3.1f секунды', time); */

   mclAssignAns(&ans,

          mlfNFprintf(0, _mxarray10_, _mxarray13_, mclVv(time, "time"), NULL));

   SyncVCL(1); // Осуществляем вывод текста на форму

   mxDestroyArray(ans);

}

Здесь необходимо сделать небольшое замечание по поводу вывода текста на форму. Как видно выше, фраза – "Время выполнения составило " – была выведена напрямую через Print Handler, а не содержится в строке форматного вывода функции  mlfNFprintf. Причина этого тривиальна – нельзя в m-коде использовать русскую букву "я" в кодировке Windows, она воспринимается (почему-то!) компилятором MATLAB как "перенос строки". Но данную проблему легко разрешить, если вызвать «вручную» функцию Print Handler c текстом, который нужно вывести на форму.

/* Файл "graph.c" */

extern mxArray * x; extern mxArray * y;

void mlfGraph(void) {

   mlfEnterNewContext(0, 0);

   Mgraph();

   mlfRestorePreviousContext(0, 0); }

static void Mgraph(void) { /* Производится необходимый графический вывод */

    /* plot(x, y);*/

   mclAssignAns(&ans, mlfNPlot(0, mclVg(&x, "x"), mclVg(&y, "y"), NULL));

      

}


  1.  Применение технологий использования m-файлов для создания автономных приложений в визуальной среде программирования

  1.  Пример создания более сложного приложения

Приложение, создававшееся в предыдущей главе, полностью основывалось на коде, который выдал компилятор MATLAB. Преимущество построения приложения таким образом очевидно – если вдруг понадобилось изменить m-код, то достаточно заново создать C-модуль и подключить его к проекту. К тому же всегда есть возможность наладить взаимодействие между интерфейсом и исходным C-кодом без ущерба исходному коду.

Однако чистое использование данной технологии не является  гибким для написания реальных приложений. Идеальным будет использование гибридной технологии, сочетающей «вручную» созданный код и генерируемый компилятором MATLAB код. Использование генерируемого кода просто облегчает работу по созданию приложения вместо того, чтобы писать идентичный m-коду C-код программисту самостоятельно. Код, генерируемый компилятором, хоть и оптимальный по надежности, но не оптимален по объему и локальной необходимости (наличие различных таблиц и сопутствующих им функций далеко не всегда оправдано). Поэтому разбираемое далее приложение создавалось в описываемом контексте, также на этом примере рассмотрим внутреннее взаимодействие математического и рабочего кода (внешнее – через Print Handler – было рассмотрено в предыдущей главе), создание пользовательской таблицы функций, которые могут быть вызваны по имени через feval-интерфейс, а также посмотрим обработку исключительных ситуаций при использовании функций библиотеки MATLAB.

  1.  Описание создаваемого приложения

Визуальная часть. Форма примера (см. рис. 3.1.) содержит

  •  три компонента StringGrid, объекта-контейнера символьных строк, отображающие данные в строках и столбцах; два из этих компонентов служат для ввода данных, а один для вывода.
  •  компоненты Label («символьная метка») и Memo (контейнер списка символьных строк), выводящие содержимое исходной матрицы.
  •  четыре группы управляющих элементов (Button, BitBtn, UpDown, Edit, CheckBox), имеющих непосредственную логическую связь с перечисленными выше компонентами.

  1.  Функциональность визуальных компонентов

StringGrid3 ("Parameters Matrix") – "матрица параметров", первым получает фокус, позволяет ввести значение для параметров (возможно от 1 до 9)  в поле "Value or Formula" в виде сложного  математического выражения, основанного на синтаксисе MATLAB, которое может содержать любые арифметические и логические действия, а также любые функции с одним параметром.  Проще говоря, любую, с точки зрения MATLAB, «правильную» строку, которая затем будет подвержена интерпретации. В строке могут участвовать имена параметров ("p" + номер параметра), при этом нельзя использовать имена функций, содержащие "p" (например, exp). Поле "Result" содержит числовой эквивалент значения параметра или сообщение об ошибке, если интерпретируемое выражение было построено неправильно. Числовое значение можно получить, нажав кнопку "Compute", расположенную ниже матрицы параметров; там же можно изменить количество используемых параметров.

StingGrid1 – исходная, подвергаемая математической обработке матрица. Эта таблица строк является редактируемой квадратной матрицей, синтаксис строковых элементов которой полностью совпадает с синтаксисом поля формул матрицы параметров. Размерность (от 1 до 10) матрицы можно изменить кнопками, расположенными над матрицей. Данный компонент имеет логическую связь с группой кнопок, находящихся справа. Кнопка "Fill Matrix" заполняет матрицу числами от 1 до n×n, где n – размерность матрицы. При нажатии кнопки "Compute" вычисляется содержимое исходной матрицы, и если одно из строковых выражений ошибочно, то на его месте выводится сообщение об ошибке. Прежде чем обсчитать исходную матрицу, «на всякий случай» вычисляются значения параметров. По нажатию кнопки "Run" сначала пересчитывается исходная матрица (в случае обнаружения ошибки действие этой процедуры приостанавливается), а затем запускается порция вычислительного кода, преобразующего исходную матрицу.

StringGrid2 ("Result Matrix") – "результирующая матрица", выводит преобразованную исходную матрицу. В качестве действия над исходной матрицей было выбрано получение обратной матрицы к исходной, причем к исходной матрице была прибавлена единичная (матрица, состоящая из одних единиц). Данная операция нетривиальна, как если бы ее пришлось писать программисту самостоятельно (в противоположность тому, что, используя описываемую технологию, достаточно написать две строчки кода).

Label & Memo – два компонента, выполняющих одну и туже функцию – вывод исходной матрицы в текстовом виде на форму. Непосредственную связь с ними имеет последняя группа элементов управления "Number Format", с помощью которой можно установить, сколько знаков до и после запятой будет у выводимых элементов исходной матрицы, либо будет использоваться формат вывода, предусмотренный математической библиотекой MATLAB.

  1.  Реализация визуальной части примера

Теперь посмотрим, какой код скрывается под описаниями, изложенными в предыдущем разделе. И стоит начать рассмотрение с файла, реализующего события формы и ее компонентов, где как раз сосредоточен весь код, не связанный с использованием функций библиотеки MATLAB. Предварительно рассмотрим дополнительно используемый заголовочный файл.

// Файл "cpphelper.h" -------------------------------------------------------

int CurrentSize; // Текущий размер исходной матрицы.

int CheckStatus; // Состояние CheckBox, тестируемое в модуле "Mod1.c".

// Указатель, использующийся для динамического распределения памяти.

double *preal_data2;

double real_data1[100]; // Численное содержимое исходной матрицы, используется

                       // для взаимодействия с функциями библиотеки MATLAB.

// Строка формата, используемого при текстовом выводе исходной матрицы.

char sstr[100];

// Функция, выводящая в виде текста содержимое исходной матрицы в Memo и Label.

extern "C" void WinFlush1(void);

// Функция, выводящая сообщение об ошибке в StringGrid через Print Handler.

extern "C" void WinFlush2(void);

// Необходимое объявление составляющих Print Handler.

extern "C" void FreeOutput(void);

extern char *OutputBuffer;

// Функция получения обратной матрицы к исходной.

extern "C" int run(void);

// Функция, интерпретирующая с помощью библиотеки MATLAB строку SText.

extern "C" int runeval(const char *SText);

// Файл "Unit1.cpp" ---------------------------------------------------------

#include <vcl.h>

#include <stdlib.h>

#pragma hdrstop

#include "Unit1.h"

#pragma package(smart_init)

#pragma resource "*.dfm"

//---------------------------------------------------------------------------

// Дополнительно подключаемый файл (см. выше).

#include "cpphelper.h"

//---------------------------------------------------------------------------

// Переменная, содержащая сообщение об ошибке, получаемое через Print Handler.

AnsiString ErrorString;

// Вспомогательные функции, см. описание ниже.

void UpdateGrid1(void);

void FillGrid1(void);

void UpdateGrid3(void);

// Процедура, обсчитывающая изменяемый StringGrid.

// Возвращает 0, если возникла ошибка при вычислении матрицы, иначе - 1.

int ComputeGrid(TStringGrid *ChangedGrid);

// Ключевая процедура, обсчитывающая столбец текущего StringGrid.

// Возвращает 0, если возникла ошибка в выражении, иначе - 1.

int ComputeCol(

 TStringGrid *CurGrid, // Текущий обрабатываемый StringGrid. 

 int UseParsRow, // Указывает, использовать ли столбец параметров. Позволяет

                 // определить является ли StringGrid матрицей параметров

                 // 1 – матрица параметров, 0 – исходная или другая матрица. 

 int Out, // Номер столбца, куда будет выводиться результат вычислений.

 int J // Номер обрабатываемого столбца текущего StringGrid.

);

//---------------------------------------------------------------------------

TForm1 *Form1;

//---------------------------------------------------------------------------

__fastcall TForm1::TForm1(TComponent* Owner): TForm(Owner)

{

}

// Устанавливаются начальные параметры до появления формы на экране.

void __fastcall TForm1::FormActivate(TObject *Sender)

{

 CurrentSize = 3;

 CheckStatus = 0;

 StringGrid3->Cells[1][0] = "Formula";

 StringGrid3->Cells[2][0] = "Result";

 UpdateGrid3();

 FillGrid1();

}

// Запуск процедуры "Run".

void __fastcall TForm1::Button1Click(TObject *Sender)

{ char hstr[10] = "";

 int I, J;

 // Проверяем, не возникла ли ошибка при вычислении матрицы.

 if (!ComputeGrid(StringGrid1)) return;

 // Формируем строку формата для вывода исходной матрицы через Print Handler в  

 // следующем виде "%5.0f%5.0f%5.0f...\n" в зависимости от размера матрицы.

 strcat(hstr, "%");

 strcat(hstr, Edit1->Text.c_str());

 strcat(hstr, ".");

 strcat(hstr, Edit2->Text.c_str());

 strcat(hstr, "f ");

 for (I = 0; I < CurrentSize; I++)

   strcat(sstr, hstr);

 strcat(sstr, "\n");

 // Динамически распределяем память, куда процедура run из модуля "Mod1.c"

 // поместит созданную там обратную матрицу – линейный массив длины n×n.

 preal_data2 = (double *)malloc(sizeof(double) * CurrentSize * CurrentSize);

 run(); // Непосредственный запуск вычисляющего кода. 

 strcpy(sstr, "\0");

 // Вывод в StringGrid2 полученной обратной матрицы и очистка памяти.

 for (I = 1; I < Form1->StringGrid2->ColCount; I++)

   for (J = 1; J < Form1->StringGrid2->ColCount; J++)

      Form1->StringGrid2->Cells[J][I] = FloatToStr(preal_data2[(I - 1) *

CurrentSize + (J - 1)]);

 free(preal_data2);

}

// Увеличение размера исходной матрицы

void __fastcall TForm1::BitBtn1Click(TObject *Sender)

{

 CurrentSize++;

 

}

// Уменьшение размера исходной матрицы

void __fastcall TForm1::BitBtn2Click(TObject *Sender)

{

 CurrentSize--;

 

}

// Заполняет исходную матрицу числами от 1 до n×n, где n – размерность матрицы.

void FillGrid1(void)

{

 

}

void __fastcall TForm1::Button2Click(TObject *Sender)

{

 FillGrid1();

}

// Обновляет StringGrid1, копируя содержимое массива real_data1 (численный

// эквивалент StringGrid1->Cells[][]).

void UpdateGrid1(void)

{

 

 for (I = 1; I < Form1->StringGrid1->ColCount; I++)

   for (J = 1; J < Form1->StringGrid1->ColCount; J++)

     Form1->StringGrid1->Cells[J][I] = FloatToStr(real_data1[(I - 1) *

CurrentSize + (J - 1)]);

}

void WinFlush1(void)

{

  Form1->Label4->Caption = OutputBuffer;

  Form1->Memo1->Lines->Text = AnsiString(OutputBuffer);

  FreeOutput();

}

void WinFlush2(void)

{

  ErrorString = AnsiString(OutputBuffer);

  FreeOutput();

}

void __fastcall TForm1::CheckBox1Click(TObject *Sender)

{

 

}

// Запускает процедуру "Compute" для матрицы параметров.

void __fastcall TForm1::Button3Click(TObject *Sender)

{

 ComputeCol(StringGrid3, 1, 2, 1);

}

// Обновляет StringGrid3.

void UpdateGrid3(void)

{

 

}

void __fastcall TForm1::UpDown3Click(TObject *Sender, TUDBtnType Button)

{

 

}

int ComputeGrid(TStringGrid *ChangedGrid)

{ int J;

 ComputeCol(Form1->StringGrid3, 1, 2, 1);

 for (J = 1; J < ChangedGrid->ColCount; J++)

   if (!ComputeCol(ChangedGrid, 0, J, J)) return(0);

 return(1);

}

// Запускает процедуру "Compute" для исходной матрицы.

void __fastcall TForm1::Button4Click(TObject *Sender)

{

 ComputeGrid(StringGrid1);

}

// Вспомогательная функция, преобразующая разделитель "," в ".", так как MATLAB

// воспринимает только точку в качестве разделителя дробной и целой частей.

AnsiString CommaConv(AnsiString Str)

{ int Pos;

 while (Pos = Str.Pos(AnsiString(","))) Str[Pos] = '.';

 return Str;

}

int ComputeCol(TStringGrid *CurGrid, int UseParsRow, int Out, int J)

{  int SPos,// Позиция символа "p" в обрабатываемой символьной строке.

      NPos,// Номер заменяемого параметра.

 ParsNumber,// Общее число используемых параметров.

         I,// Номер ячейки обрабатываемого столбца J.

ParsRow = 0;// Номер строки параметра, использующийся для предотвращения

           // рекурсивных ссылок между параметрами (исп., если UseParsRow = 1).

  AnsiString StringValue;// Обрабатываемая строка – элемент обрабатываемого 

                         // столбца J, подлежащий интерпретации.

  ParsNumber = Form1->StringGrid3->RowCount - 1;

  // Выделяем память для численного результата интерпретации.

  preal_data2 = (double *)malloc(sizeof(double));

  for (I = 1; I < CurGrid->RowCount; I++)

  {

     *preal_data2 = 0;

     StringValue = CommaConv(CurGrid->Cells[J][I]);

     // Ищем вхождение символа "p".

     while (SPos = StringValue.Pos(AnsiString("p")))

     try

     { // Если символ, идущий следом за "p", - не цифра, то возникает ошибка

       // преобразования строки к целому числу (EConvertError).

       NPos = StrToInt(StringValue.SubString(SPos + 1, 1));

       if (UseParsRow) ParsRow = I;

       if (NPos > ParsNumber || NPos == ParsRow)

         // Если номер параметра больше их общего числа или совпадает с номером

         // строки обрабатываемого столбца, то возбуждается аналогичное  

         // исключение, переключающее на строку обработчика исключений (catch).

         throw EConvertError("Параметр вне диапазона");

       StringValue = StringValue.Delete(SPos, 2);

       // Если преобразование завершилось успешно, то

       if (UseParsRow)

         // заменяем текущий параметр на его "формулу" – для матрицы параметров

         StringValue = StringValue.Insert(CommaConv(Form1->StringGrid3->

Cells[1][NPos], SPos);

       else

       //в противном случае заменяем текущий параметр на его числовое значение.

         StringValue = StringValue.Insert(CommaConv(Form1->StringGrid3->

Cells[2][NPos]), SPos);

     }

     catch (EConvertError &E)

     { // В случае возникновения ошибки преобразования, выводим в ячейку

       // [Out][I] сообщение об ошибке и возвращаем 0.

       CurGrid->Cells[Out][I] = AnsiString("Ошибка!!!");

       free(preal_data2);

       return(0);

     }

     // Передаем текущую строку интерпретатору из другого модуля.

     runeval(StringValue.c_str());

     // В итоге возвратиться

     if (ErrorString == "")

       // результирующее число, которое необходимо вывести на форму, либо

       CurGrid->Cells[Out][I] = FloatToStr(*preal_data2);

     else

     { // сообщение об ошибке в выражении, которое также следует вывести на

       // форму, и вся работа функции должна завершиться 0.

       CurGrid->Cells[Out][I] = ErrorString;

       ErrorString = ""; free(preal_data2);

       return(0);

     }

     if (!UseParsRow)

       // Если используется исходная матрица, то необходимо сохранить

       // полученное значение в массиве real_data1.

       real_data1[(I - 1) * CurrentSize + (J - 1)] = *preal_data2;

  }

  free(preal_data2);

  return(1);

}

  1.  Реализация вычислительной части примера

Теперь подробно рассмотрим модуль, в котором сосредоточен код специализированных вычислений, предварительно разобрав используемые возможности MATLAB C Math Library.

  1.  Массивы MATLAB

Математическая библиотека MATLAB содержит большое число процедур, с помощью которых можно создавать различные типы массивов: скаляры, векторы, матрицы, многомерные массивы и другие, часто употребляемые массивы, также имеются символьные массивы.

Создание скаляров. Для того чтобы создать скаляр, следует использовать функцию mlfScalar(). Когда ей передается числовое значение, то создается числовой массив размера 1×1, содержащий это значение, хранящееся в формате double. Всякий раз, когда требуется передать числовое значение в библиотечную функцию, нужно использовать mlfScalar().

Создание двумерных массивов (матриц). Так как двумерные массивы со значениями двойной точности часто используются в MATLAB, то библиотека включает функцию mlfDoubleMatrix(), которая позволяет создавать матрицы и инициализировать ее данными соответствующего типа.

/* Фрагмент используемого примера */

double real_data1[100];

mxArray * mat0 = NULL;

int CurrentSize;

mlfAssign(&mat0, mlfDoubleMatrix(CurrentSize, /* Число строк */

                                CurrentSize, /* Число столбцов */

                                real_data1,

                                NULL)); /* Мнимая часть отсутствует */

mxDestroyArray(mat0);

Необходимо заметить, что MATLAB хранит массивы в порядке, ориентированном на столбцы, тогда как массивы в Си имеют строкоориентированное расположение в памяти. Также можно использовать функцию MATLAB API mxCreateDoubleMatrix().

Создание многомерных числовых массивов. Чтобы создать многомерный массив, нужно воспользоваться функцией MATLAB API mxCreateNumericArray(), которая имеет следующие параметры:

  •  количество измерений массива
  •  размер каждого измерения
  •  тип данных, которые будет содержать массив
  •  данные вещественные либо комплексные

В качестве примера, следующий фрагмент кода создает трехмерный массив размерности 3×3×2.

int ndim    = 3;         /* Число измерений */

int dims[3] = { 3,3,2 }; /* Размер измерения */

mxArray *A = NULL;      

mlfAssign(&A, mxCreateNumericArray( ndim,  

                                   dims,

                                   mxDOUBLE_CLASS,

                                   mxREAL));

mxDestroyArray(A);

Создание часто употребляемых массивов. MATLAB C Math Library содержит несколько функций, с помощью которых можно создавать многомерные часто употребляемые массивы:

  •  Массив единиц, mlfOnes()
  •  Массив нулей, mlfZeros()
  •  Единичная матрица mlfEye()
  •  Равномерно и нормально распределенные случайные числа, mlfRand() и mlfRandn()

/* Фрагмент используемого примера */

mxArray *mat1 = NULL;

int CurrentSize;

mlfAssign(&mat1, mlfOnes(

                     mlfScalar(CurrentSize), /*Необходимо привести к скаляру */

                     mlfScalar(CurrentSize), /*Необходимо привести к скаляру */

                     NULL));

mxDestroyArray(mat1);

Создание символьных массивов. Всякий раз, когда функции MATLAB передается строка символов, то она должна быть представлена в виде массива символов, а не символьной строкой, заканчивающейся нулем (формат Си).

Здесь следует использовать функцию MATLAB API mxCreateString(), которая в качестве аргумента должна иметь стандартную для Си символьную строку, заключенную в двойные кавычки или переменную «строкового» типа.

/* Фрагмент используемого примера */

mclAssignAns(

   &ans,

   mlfEval(mclAnsVarargout(),

   mxCreateString(SText),

   mxCreateString("catchfcn")));

  1.  Создание пользовательской таблицы функций, которые могут быть вызваны по имени через feval-интерфейс

Математическая библиотека MATLAB имеет встроенную локальную таблицу функций, которые могут быть вызваны по имени через feval-интерфейс (см. предыдущую главу). Функция mlfFeval() обращается к этой таблице, чтобы определить указатель на функцию, связанную с конкретным именем. Неявное обращение к mlfFeval() происходит в функциях mlfNOde45(), mlfEval() и т.д., если, конечно, не используется локальный указатель, созданный с помощью функции

mclCreateSimpleFunctionHandle(_thunk_fnc_, /* Имя «функции-переходника» */

                             "name") /* Имя вызываемой функции – строка */

но данная функция является вспомогательным средством компилятора, и поэтому ее «ручное» использование нежелательно.

Таблица должна быть представлена в следующем виде:

/* Фрагмент используемого примера */

static mlfFuncTabEnt MFuncTab[] =

{

    /* Отдельная запись таблицы */

    {"catchfcn", /* Строка, представляющая имя функции. */

    (mlfFuncp)MCatchfcn,/* Указатель на эту функцию. */

    _catchfcn_thunk_fcn_/* Указатель на «функцию-переходник», в которой */

                        /* описано, как выполнить исходную функцию. */                        

   },

   { 0, 0, 0} /* Таблица должна завершаться нулевой записью. */

};

Регистрируется данная таблица для библиотеки с помощью функции mlfFevalTableSetup(), параметром которой выступает указатель на вышеописанную таблицу.

Написание «функции-переходника» (thunk function) желательно производить по шаблону, описанному ниже, во избежание неустойчивости в работе приложения.

/* Фрагмент используемого примера */

static int _catchfcn_thunk_fcn_(mlfFuncp pFunc, int nlhs, /* 1 */

                               mxArray **lhs, int nrhs,

                               mxArray **rhs )

{   typedef void (*PFCN_0_0)(void);                       /* 2 */

   mxArray *Out;

   if (nlhs > 0 || nrhs > 0)                             /* 3 */

   {

     return(0);

   }

   Out = (*((PFCN_0_0)pFunc))();                         /* 4 */

   if (nlhs > 0)                                         /* 5 */

       lhs[0] = Out;

   return(1);                                            /* 6 */

}

Нумерованным элементам следующего списка соответствуют нумерованные комментарии:

Описание «функции-переходника», которая вызывает функцию MCatchfcn. «Функция-переходник» выступает в качестве преобразователя между интерфейсом используемой функции и интерфейсом, необходимым MATLAB C Math Library.  Эта функция имеет пять аргументов – описывающие любую функцию – без входных и выходных параметров (в этом примере функция – всегда MCatchfcn()): указатель (mlfFuncp) на MCatchfcn(), целое (nlhs), указывающее количество выходных параметров MCatchfcn(), массив типа mxArray (lhs –  “left-hand side”), где сохраняется результат работы MCatchfcn(), аналогично для входных параметров (nrhs и rhs – “right-hand side”).

Описание указателя на функцию MCatchfcn, который передается в «функцию-переходник» как тип mlfFuncp – обобщенный тип, применимый к любой функции. Указатель на функцию, описываемый здесь, должен очень точно специфицировать тип возвращаемого значения и тип аргументов, необходимых MCatchfcn. Программа приводит pFunc к описанному здесь типу. Имя PFCN_0_0 используется, чтобы было легче определить, что функция не имеет никаких аргументов. Например, если встречается следующее описание "typedef mxArray *(*PFCN_1_2)(mxArray *, mxArray *);", то это обозначает, что функция имеет одно выходное значение и два входных параметра.

Проверка, что было передано ожидаемое число входных и выходных аргументов. Осуществляется выход, если было передано большее число аргументов.

Вызов MCatchfcn, приведенной к pFunc. Возвращаемое значение сохраняется во временной переменной Out. В обязательном порядке нужно привести указатель на MCatchfcn к типу, определенному в «переходнике».

Присваивание возвращаемого значения в соответствующую позицию в массиве выходных параметров (первым является lhs[0]).

Возвращается 1 в качестве успешного завершения.

  1.  Обработка исключительных ситуаций при использовании функций математической библиотеки MATLAB

В любом грамотно написанном приложении должны обрабатываться возможные исключительные ситуации, поэтому разработчики MATLAB предусмотрели несколько способов обработки исключительных ситуаций, возникающих при работе c MATLAB C Math Library:

  •  Создание функции-обработчика, используемой некоторыми функциями для локальной обработки ошибок
  •  Обработка с помощью макросов mlfTry и mlfCatch
  •  Создание пользовательского обработчика ошибок (Error Handler)

Создание функции-обработчика, используемой некоторыми функциями для локальной обработки ошибок.

Функция mlfEval, вычисляющая любое правильно сформированное строковое выражение на языке MATLAB (однако, не содержащее переменных), может иметь в качестве параметра имя функции (представленное виде строки), которая вызывается, если возникла ошибка при вычислении выражения. Если отсутствует данная функция-обработчик, то возникающие исключительные ситуации необходимо обрабатывать другими методами. Пример использования локального обработчика см. ниже.

Обработка с помощью макросов mlfTry и mlfCatch.

Чтобы вернуть управление коду приложения после того, как библиотечная функция столкнется с ошибочными условиями, необходимо заключить «опасный» участок кода между макросами mlfTry и mlfCatch.

Определение try-блока не затрагивает обработку на уровне предупреждений. Функции выводят предупредительные сообщения и возвращают контроль назад в приложение. Предотвратить вывод предупреждений можно следующим образом:

mlfNWarning(0, NULL, mxCreateString("off"));

После того как был определен try-блок, библиотечные функции уже не выводят пользователю сообщения об ошибке и завершают работу приложения. Вместо этого, функции переводят управление в catch-блок, в котором уже осуществляется обработка ошибок. Если же необходимо вывести пользователю сообщение об ошибке, то сделать это можно из catch-блока (получить текстовое сообщение, связанное с последней возникшей ошибкой, возможно при помощи функции mlfLasterr()).

Try-блок – это группа, состоящая из одно или более операторов, взятая в фигурные скобки, начинающаяся с макроса mlfTry. Аналогично и для catch-блока, только начинаться он должен с макроса mlfCatch, а завершаться – mlfEndCatch. Макросы mlfTry, mlfCatch и mlfEndCatch не требуют после себя скобок; это не вызов процедуры. Catch-блок, в свою очередь, должен содержать код, обрабатывающий ошибки. Например, сюда можно поместить код очистки, который бы освобождал распределенную до этого память перед завершением.

/* Фрагмент C-кода, реализующего алгоритм нелинейной фильтрации (см. далее) */

/* В коде, расположенном выше try-блока, решается дифференциальное уравнение с

* с помощью функции ode45, которая в качестве результата возвращает массив

* XOPT. Если же параметры вычислений заданы «плохо», то длина этого массива

* будет меньше, чем требуется алгоритмом проверки значений массива. Если здесь

* не использовать try-блок, то приложение выдаст ошибку и будет аварийно

* завершено. */

   mlfTry {

           

           for (; ; ) {

               

               /* Проверяем на отрицательность первую компоненту XOPT */

               /* if XOPT(1,i)<=0, XOPT(1,i)=x1eps; end, */

               if (mclLeBool(

                     mclVe(mclIntArrayRef2(mclVsv(XOPT, "XOPT"), 1, v_)),

                     _mxarray4_)) {

                   mclIntArrayAssign2(&XOPT, mclVg(&x1eps, "x1eps"), 1, v_);

               }

               

   }

   mlfCatch {

       /* disp('Ошибка!!!'); */  mlfDisp(_mxarray92_); WinFlush();

   }

   mlfEndCatch

Создание пользовательского обработчика ошибок (Error Handler).

Обработка ошибок в режиме использования функций математической библиотеки MATLAB по умолчанию осуществляется  библиотечной функцией обработчика ошибок (Error Handler). Впоследствии можно создать пользовательский обработчик, заменив им библиотечный.

Создаваемый пользовательский обработчик должен соответствовать прототипу библиотечной функции Error Handler:

void MyErrorHandler( const char *msg, bool isError  )

{

   /* Код пользователя */

}

Здесь:

  •  функция обработчика не должна ничего возвращать (возвращает void),
  •  функция должна иметь два входных параметра: строка-константа (const char *) и логическое значение (bool). Строка содержит текст сообщения об ошибке. Когда булева величина имеет значение TRUE, то поступает сообщение об ошибке, в противном случае – предупреждение.

После написания обработчика его необходимо зарегистрировать для математической библиотеки MATLAB, чтобы библиотечные функции могли вызывать его во время исполнения при столкновении с ошибочными условиями. Регистрацию можно произвести следующим образом:

mlfSetErrorHandler(ErrorPrint);

Теперь рассмотрим пример использования пользовательского Error Handler.

/* Фрагмент C-кода, реализующего алгоритм нелинейной фильтрации (см. далее)  */

/* ---------------------------- Error Handler ------------------------------ */

void ErrorPrint(const char *msg, bool isError  )

{

 if(isError)

 {

      WinPrint(msg);

      SyncVCL(2);

 }

   else

 {

      WinPrint("Error: "); WinPrint(msg);WinPrint("\n");

      WinPrint("\n Возникла ошибка использования функций библиотеки

                MATLAB!!!\n\n");

      SyncVCL(2);

      FinishThread();

 }

}

Здесь сообщение об ошибке или предупреждение перенаправляется в Print Handler, а затем выводится на форму в компоненту Memo1, уведомляя пользователя о возникших ошибках использования библиотечных функций. В случае возникновения ошибки, выполнение потока вычислений досрочно завершается.

Вообще обработка исключений ведет себя странно при различных способах ее использования и их комбинаций. До конца этот вопрос не исследован, но были выяснены следующие критические ситуации (относящиеся к используемой комбинации средств).

Если математический код запускается в контексте основного потока VCL, то реализация Error Handler должна находится в C-модуле, где используется «try/catch-механизм». Если же разместить Error Handler в другом модуле (".c" или ".cpp"), то «try/catch-механизм» работает (почему-то!) некорректно. Однако использование Error Handler и «try/catch-механизма» обычно является взаимоисключающим, поэтому данное ограничение не критично.

Если же код запускается внутри дополнительно создаваемого потока, то местоположение и наличие обработчика ошибок не имеет никакого значения, просто внутри try/catch ну уж очень медленно выполняется операция простого индексирования массивов (при реализации алгоритмов фильтрации в данный блок заключалась проверка элементов массива на отрицательность, которая выполнялась в два раза дольше, чем другие операции). При использовании лишь одного Error Handler никаких отклонений замечено не было.

  1.  Реализационный C-код обращения матрицы и «интерпретатора» формул

/* Файл "mhelper.h" */

/* Описание переменных из модуля "Unit1.cpp". */

extern int CurrentSize;

extern int CheckStatus;

extern double *preal_data2;

extern double real_data1[100];

extern char sstr[100];

/* Прототипы компонентов Print Handler. */

void WinFlush1(void);

void WinFlush2(void);

void WinPrint(const char *text);

void FreeOutput(void);

/* Файл "Mod1.c" */

#include <stdio.h>

#include <stdlib.h>   /* содержит EXIT_SUCCESS */

#include <string.h>

#include <math.h>     /* содержит прототип C-функции pow() */

#include "matlab.h"   /* содержит прототипы всех функций библиотеки */

#include "mhelper.h"  /* дополнительно подключаемый файл (см. выше) */

/* Реализующая версия обработчика исключений в mlfEval(). */

static void MCatchfcn(void);

/* «Функция-переходник» обработчика исключений. */

static int _catchfcn_thunk_fcn_(mlfFuncp pFunc, int nlhs,

                               mxArray **lhs, int nrhs,

                               mxArray **rhs );

static mlfFuncTabEnt MFuncTab[] =

{

   {"catchfcn", (mlfFuncp)MCatchfcn, _catchfcn_thunk_fcn_ },

   { 0, 0, 0}

};

/* Функция, преобразующая исходную матрицу. */

int run(void){

   int i;

   mxArray * mat0 = NULL;

   mxArray * mat1 = NULL;

   mxArray * mat2 = NULL;

   mlfSetPrintHandler(WinPrint);

   mlfEnterNewContext(0, 0);

   mlfAssign(&mat0, mlfDoubleMatrix(CurrentSize, CurrentSize, real_data1,

NULL));

   mlfAssign(&mat1, mlfOnes(mlfScalar(CurrentSize), mlfScalar(CurrentSize),

NULL));

   /* Получение обратной матрицы к исходной, просуммированной с единичной. */

   mlfAssign(&mat2, mlfInv(mlfPlus(mat0, mat1)));

   /* Получившуюся обратную матрицу для последующей обработки можно

    * преобразовать в одномерный массив вещественных чисел, скопировав

    * содержимое памяти, на которую ссылается указатель, возвращаемый функцией

    * MATLAB API mxGetPr(). Функция mxGetN() возвращает число столбцов матрицы-

    * параметра. */

   memcpy(preal_data2, mxGetPr(mat2), pow(mxGetN(mat2), 2) * sizeof(double));

   /* Выбор способа печати получившейся матрицы – встроенный или по формату. */

   if (CheckStatus)

     mlfPrintMatrix(mat0);

   else

     mlfFprintf(mlfScalar(1),         /* Вывод на stdout */

                mxCreateString(sstr), /* Строка формата вывода */

                mat0,                 /* Массив данных   */

                NULL);

   WinFlush1();

   mxDestroyArray(mat0);

   mxDestroyArray(mat1);

   mxDestroyArray(mat2);

   mlfRestorePreviousContext(0, 0);

   return(EXIT_SUCCESS);

}

/* Функция, «интерпретирующая входную строку. */

int runeval(const char *SText){

   mxArray * ans = NULL;

   mlfSetPrintHandler(WinPrint);

   mlfEnterNewContext(0, 0);

   /* Предотвращаем вывод предупреждений. */

   mlfNWarning(0, NULL, mxCreateString("off"));

   mlfFevalTableSetup(MFuncTab);

   /* «Интерпретируем» входную строку с помощью функции mlfEval, выходной

    * переменной которой устанавливается ans (специализированная переменная

    * MATLAB) с помощью функции компилятора mclAnsVarargout(). mlfEval()

    * обязана иметь параметр, и если он отличается от ans, то воспринимается

    * Mcatchfcn() также как выходной, которая не должна иметь никаких

    * аргументов. */

   mclAssignAns(

       &ans, mlfEval(mclAnsVarargout(), mxCreateString(SText),

mxCreateString("catchfcn")));

   /* Проверяем, не возникло ли ошибки при «интерпретации».

   if (ans != NULL)

   {   /* В положительном случае сохраняем полученное значение. */

       memcpy(preal_data2, mxGetPr(ans), sizeof(double));

       FreeOutput();

   }

   mxDestroyArray(ans);

   mlfRestorePreviousContext(0, 0);

   return(EXIT_SUCCESS);

}

static int _catchfcn_thunk_fcn_(mlfFuncp pFunc, int nlhs, <..>)

{

   

}

static void MCatchfcn(void)

{   /* Простейший обработчик, возвращающий лишь сообщение об ошибке. */

   mlfDisp(mxCreateString("Ошибка в выражении!!!"));

   WinFlush2();

}

При создании излагаемых примеров приходилось активно пользоваться [8], материал описательного характера частично был взят из [6], где довольно широко изложена концепция программирования с помощью функций математической библиотеки MATLAB, и если понадобится глубже изучить излагаемые возможности библиотеки, то следует обязательно обратиться к [6].

  1.  Создание «неизменяемого» m-кода и разработка автономного приложения на его основе

  1.  «Неизменяемый» m-код

«Неизменяемым» можно назвать такой m-код, который после трансляции на C/C++ нет необходимости редактировать и дополнять вызовом каких-либо C-функций. Определенный, стандартизованный набор C-функций просто напрямую вызывается из m-кода. Использование данной концепции, на самом деле, должно превалировать при создании автономных приложений в визуальной среде программирования.

Это связано с тем, что исходный m-код, зачастую, подвергается небольшой переработке на третьем технологическом этапе (где производится  доработка C-кода, полученного трансляцией из m-кода). И после повторного прохождения этапов технологии необходимо заново осуществить доработку C-кода, что из довольно интересного процесса в первый момент превращается в неприятную, рутинную и, возможно, для кого-то нетривиальную операцию.

Разработчики MATLAB предусмотрели возможность сопряжения m-кода с C/C++-кодом. Начиная с версии 2.1, компилятор MATLAB поддерживает вызов произвольной C/C++-функции из m-кода. Достаточно просто предоставить «суррогатную» m-функцию, определяющую каким образом данный код будет работать в m-коде, а затем реализовать тело функции в C/C++.

С другой стороны, связь между вычислительным кодом и кодом, реализующим пользовательскую часть приложения, совершенно необходима, причем она выражается, как правило, определенным, в какой-то мере стандартным набором функциональных особенностей:

  •  присвоение значения конкретной C-переменной, конкретного поля объекта и т.п. переменной, используемой функциями библиотеки MATLAB,
  •  вывод текста (скорее всего «накопленного» обработчиком печати) на рабочую форму,
  •  досрочное прекращение вычислений.

Это лишь не полный перечень действий, который можно взять за основу, и осуществлять вызов соответствующих этим действиям C-функций прямо из m-кода, первоначально не думая об их реализации, которая уже непосредственно привязана к разработке пользовательской части, хотя здесь возможно использование каких-либо шаблонов, облегчающих создание приложения для неискушенных пользователей данной технологии.

Чтобы не быть голословным, рассмотрим конкретный пример создания m-файла на основе описанного выше рецепта. За основу был взят тот же пример, описанный на четвертом технологическом этапе.

% Файл "func.m"

function main

tic;

% Блок инициализации

mShowGraph = AssignValue(0); % Отображать ли график

mMyExit = 0;

x = 1:10; y = 1:10; endmax = 500000; oldpers = 0; oldtime = 0;

% Блок вычислений

for i = 1:endmax;

 mMyExit = AssignValue(1); % Осуществить ли досрочное завершение вычислений

 if (mMyExit)

   CloseThread; % Здесь работа потока прерывается    

 end;

 pers = floor((i / endmax) * 100); % Вычисляем процент прогресса вычислений

 if (pers > oldpers)

   fprintf(1, 'Выполнено %4.0f%%', pers); oldpers = pers;

   SyncVCL(0); % Осуществить вывод на форму

 end

end

% Блок вывода

set(0,'DefaulttextFontName', 'FixedWidth', 'DefaultAxesFontName', 'FixedWidth');

if (mShowGraph) % Если 1 – выводим график, 0 – пропускаем вывод

 plot(x, y); set(gca, 'XGrid', 'on', 'YGrid', 'on');

 set(get(gca, 'Title'), 'String', 'Привет'); legend('Это пример');

end,

time = toc; fprintf(1, '%3.1f секунд', time);

SyncVCL(0); % Осуществить вывод на форму в то же место

% Блок описания внешних C-функций

function CloseThread; % Завершить выполнение потока

%#external

function SyncVCL(MethodNumber); % Синхронизировать с VCL вывод текста

% Здесь MethodNumber – определенный способ вывода текста

%#external

function OutVar = AssignValue(VarNumber); % Присвоить конкретное значение

% Здесь VarNumber – определенный способ присвоения значения

% Здесь OutNumber - выходное значение, приведенное к библиотечному типу mxArray

%#external

Вышеприведенный код, вообще, рекомендуется в качестве шаблона, применяемого для создания «быстро переводимых» m-файлов.

Все m-файлы будущего приложения следует объединить в один единственный, который можно назвать, скажем, "func.m", а основную часть, из которой вызываются вспомогательные функции, назвать, например, main.

Затем выделить в программе «блок инициализации», в котором необходимо присвоить первоначальные значения используемым переменным. Переменные, значения которых должен ввести пользователь, инициализировать с помощью функции AssignValue(), «заранее знающей» каким образом осуществить означенное присвоение.

В «блоке вычислений» следует произвести требуемые манипуляции, где, используя функцию SyncVCL(), можно выводить на форму текущие значения переменных, либо при помощи функции CloseThread досрочно прекратить процесс вычислений.

Завершать программу должен «блок вывода» результатов проделанных вычислений в графической или текстовой форме, где можно протестировать специальные переменные на предмет необходимости вывода конкретных составляющих.

И уже в самом конце нужно разместить описание внешних C-функций, внутри которых должна быть обязательно указана директива

%#external

указывающая компилятору использовать внешнюю реализующую версию соответствующей m-функции.

  1.  Автономные приложения на базе «неизменяемого» m-кода 

Теперь необходимо проиллюстрировать создание автономного приложения с использованием C-кода, полученного из «неизменяемого» m-кода.

Уже на втором технологическом этапе, т.е. на этапе трансляции возникают некоторые отличия, связанные с присутствием внешних нереализованных C-функций. Во-первых, компилятор создаст дополнительный заголовочный файл (будет называться "func_external.h"), где разместит прототипы реализующих версий внешних m-функций, а интерфейсная часть будет содержаться в главном файле ("func.c"). Во-вторых, компилятор не сможет создать исполняемый файл в виду отсутствия реализующей части, что, в общем-то, и не нужно, так как «файл-оболочка» ("func_mainhg.c") все-таки будет создан. Однако не будет создана директория "bin" с содержимым, но это тоже не помеха – ее можно взять у любого другого приложения, либо создать вручную, скопировав в нее файлы "FigureMenuBar.fig" и "FigureToolBar.fig", находящиеся в директории "$(MATLAB)\extern\include". К тому же, данная директория нужна, если вызываются графические функции MATLAB, но и даже без нее программа будет нормально работать, просто не будет функционировать панель и меню графического окна MATLAB , а также библиотека будет выдавать предупреждение об отсутствии файлов ".fig".

Обратите особое внимание, что описываемый ниже проект создавался и компоновался с использованием следующей комбинации средств MATLAB C Math/C Graphics Library v2.2, MATLAB Compiler v2.2, Borland C++ Builder v6.0 (содержит компилятор Borland C++ v5.6). При переходе на эти продукты исчезают ограничения из раздела , касающиеся инициализации таблиц компилятора MATLAB, инициализации и выполнения графических функций MATLAB в контексте дополнительного потока.

Также к форме раннее разбираемого примера добавим CheckBox, регулирующий вывод графика после вычислений. Учитывая вышеописанные изменения, рассмотрим соответствующие изменения в проектных файлах.

// Файл "Unit1.cpp" ---------------------------------------------------------

// Глобальные переменные, управляющие выводом графика и досрочным завершением

double MyCheck = 1, MyExit; // дополнительного процесса

//---------------------------------------------------------------------------

void __fastcall TForm1::Button1Click(TObject *Sender)

{

 MyExit = 0;

 Button1->Enabled = false; CheckBox1->Enabled = false;

 MyThread = new TNewThread(false);

}

void __fastcall TForm1::Button2Click(TObject *Sender)

{

 MyExit = 1;

}

void __fastcall TForm1::CheckBox1Click(TObject *Sender)

{

 MyCheck = CheckBox1->Checked;

}

// Файл "Unit1.cpp" ---------------------------------------------------------

#include "cpph.h" // Включает содержимое "func_mainhg.h" до функции main и

                 // реализацию функции PrintHandler

#include "func_external.h" // Включает прототипы специальных C-функций

extern double MyCheck, MyExit;

//---------------------------------------------------------------------------

__fastcall TNewThread::TNewThread(bool CreateSuspended)

       : TThread(CreateSuspended)

{

 const char *argv = *_argv; // Инициализируем графическую библиотеку MATLAB

 mlfHGInitialize(&_argc, &argv); // при создании дополнительно потока

 FreeOnTerminate = true;

}

void __fastcall TNewThread::Execute() // «Тело» потока

{

 mclLibInitCommon(&_main_info ); // Инициализируем таблицы компилятора

 mlfSetPrintHandler(WinPrint); // Регистрируем обработчик печати

 mlfFunc(); // Вызываем основную функцию, находящуюся в "func.c"

 // Ожидаем, пока пользователь не закроет все графические окна, это является

 // необходимым действием, а иначе завершающийся поток их закроет сам. Функция

 // pause не помогает в данной ситуации

 mlfHGWaitForFiguresToDie();

 // Разрешаем использование ранее отключенных кнопки "Run" и CheckBox

 UpdateMethod(1);

}

void __fastcall TNewThread::UpdateMethod(int mode)

{ // Вызов методов потока, содержащих защищаемый от конфликтов доступа к

 // компонентам VCL код (thread-safe code)

 switch (mode)

 {

   case 0: Synchronize(UpdateForm); break;

   case 1: Synchronize(EnableButton);

 }

}

void __fastcall TNewThread::UpdateForm()

{ // Выводим содержимое буфера Print Handler в строку статуса

 Form1->StatusBar1->SimpleText = OutputBuffer;

 FreeOutput();

}

void __fastcall TNewThread::EnableButton()

{ // Разрешаем использование кнопки и CheckBox

 Form1->Button1->Enabled = true;

 Form1->CheckBox1->Enabled = true;

}

void Mfunc_CloseThread(void)

{ // «Тело» реализующей версии m-функции CloseThread

 MyThread->UpdateMethod(1);

 EndThread(0);

}

void Mfunc_SyncVCL(mxArray * MethodNumber)

{ // «Тело» реализующей версии m-функции SyncVCL()

 MyThread->UpdateMethod(*mxGetPr(MethodNumber));

}

extern mxArray * Mfunc_AssignValue(int nargout_, mxArray * VarNumber)

{// «Тело» реализующей версии m-функции AssignValue

  mxArray *ReturnNumber = NULL;

  int VarNum;

  VarNum = (int)*mxGetPr(VarNumber);// Получаем номер способа присваивания

  switch (VarNum)

  {

    case 0:  ReturnNumber = mlfScalar(MyCheck); break;

    case 1:  ReturnNumber = mlfScalar(MyExit);

  }// Возвращаем значение типа mxArray, «понятного» функциям MATLAB C Math Lib.

  return ReturnNumber;

}

Файл "func.c" просто подключаем к проекту, ни коим образом не дополняя и не изменяя. Затем, если вдруг понадобилось внести какие-либо изменения в m-коде, достаточно лишь заново  произвести трансляцию на Си и заменить старый "func.c" на его обновленную версию.

Однако, несмотря на очевидные удобства данного способа программирования, есть один, в какой-то мере, очевидный отрицательный момент: вызов интерфейсной функции (например, присваивания) будет осуществляться куда медленнее, чем операция в Си. Так из-за постоянного внешнего тестирования переменной MyExit, цикл работает в полтора-два раза дольше в используемой конфигурации системы. При возникновении подобной ситуации можно на этапе выпуска финального релиза осуществить замену на C-эквивалент операции, если это возможно.


  1.  Оптимальная фильтрация состояний нелинейных динамических системы на основе метода инвариантного погружения

  1.  Задача фильтрации для нелинейных стохастических систем 

  1.  Формулировка задачи

На рис. 4.1 изображена структурная схема, описывающая исходную задачу. Здесь наблюдаемый вектор состояния системы z(t), где  , содержащий исходный вектор состояний x(t), подверженный помехе измерений v(t), поступает на вход фильтра. Алгоритм соответствующего фильтра должен быть построен таким образом, что минимизирует квадрат разности получаемой с помощью процесса фильтрации оценкой вектора состояния  и самим вектором состояния x(t) исследуемого объекта или системы. Для получения соответствующего алгоритма рассмотрим понятие потери или ошибки, связанные с недостижимостью абсолютно точной фильтрации. К примеру, если истинное значение подлежащего определению состояния равно , а полученная оценка равна , подходящей функцией потерь будет служить . Собственно говоря, истинное значение параметра  никогда точно неизвестно; именно это и является основной причиной задачи фильтрации. Следовательно, более разумным является использование статистических характеристик отклонения  от .  В общем виде в случае векторного параметра  эта характеристика может

быть записана таким образом

 

Здесь  означает цену ошибки. Ошибка определяется формулой

 

где  – истинное значение состояния и  – оценка состояния, основанная на некотором наблюдении . Формула  представляет собой условное математическое ожидание потери в оценке состояния и получается непосредственным применением основной теоремы о средних значениях. Наиболее распространенная функция потерь является квадратичная

  

где — неотрицательно определенная симметричная матрица, и ступенчатая

                                         

Минимизируя  оптимальным выбором  при квадратичной цене ошибки вида , легко прийти к выводу, что наилучшей оценкой  является условное математическое ожидание

 

Критерий  часто рассматривается на достаточно малом , так что эквивалентной  становится функция потерь

 

Подставляя  в  и переходя в полученном выражении к пределу при , стремящемся к нулю, или непосредственно подставляя  в , получим функцию потерь максимума апостериорной вероятности

 

когда необходимо выбором  минимизировать . Такая оценка  называется оценкой максимальной априорной вероятности (МАВ), поскольку эта оценка получается максимизацией условной плотности вероятности  и обычно находится из уравнения

 

  1.  Модель системы

Сначала рассмотрим дискретную модель формирования сигнала и наблюдений. Данная модель задается  уравнениями

 

 

где  – n-мерный вектор состояния;  – n-мерная функция, множества значений которой охватывает все возможные входные сигналы,  – nm –матрица,   – m-вектор входного шума,   – p-мерный вектор наблюдений,  – p-мерная функция вектор-функция,  –  p-мерный вектор помехи измерений.

Через  обозначен обобщенный вектор состояния в k-й точке фиксации . В дискретных моделях оценивания  и  предполагаются независимыми марковскими последовательностями независимых случайных гауссовских величин, имеющих нулевые средние, и такими, что

 

 

где  – символ Кронекера, а Q и R  – симметричные неотрицательно определенные ковариационные матрицы размерности mm  и pp соответственно.

Непрерывную модель часто выводят с помощью не вполне строгого предельного перехода, так что она получается непосредственно из дискретной модели по мере сгущения точек фиксации, т.е. когда  (шаг фиксации) стремится к нулю, . Эта непрерывная модель оценивания задается уравнениями

  

 

в которых  и  считаются белым гауссовским шумом с нулевым математическим ожиданием, так что

 

 

  1.  Фильтрация по критерию максимума апостериорной вероятности

 

Будем обозначать последовательности  и  соответственно через  и . Аналогично непрерывные реализации и  на отрезке  обозначаются через  и . Через  и  обозначим условные плотности вероятности X относительно результатов измерения Z. В дальнейшем предполагается, что плотности  и  известны и являются нормальными со средним и ковариационной матрицей .

Наилучшая оценка обобщенного вектора состояния x на рассматриваемом интервале времени зависит, вообще говоря, от критерия, используемого для определения наилучшей оценки. В данном случае под «наилучшей оценкой» понимается оценка, определяемая путем максимизации по X условной плотности  на всем интервале наблюдений. Получающаяся оценка известна под названием байесовской максимально правдоподобной или оценкой максимума апостериорной вероятности. В дальнейшем все выкладки будут проводиться для дискретного случая, а для непрерывного случая будет сформулирован лишь окончательный результат.

Применяя формулу Байеса к , получим

 

Из  ясно, что при известном плотность  является гауссовской, поскольку  – гауссовская величина. Поэтому при данном

 

Используя определение условной вероятности

 

можно записать

 

Так как  – гауссовская марковская последовательность, то последовательность  также является марковской и

 

Следовательно,  образована из гауссовских компонент

 

(где  – гауссовские плотности и, согласно , имеет среднее значение  и ковариационную матрицу

не зависит от , и  является известной величиной в процессе максимизации, который необходимо провести. Поэтому  можно рассматривать по отношению к этой максимизации как нормировочную константу. После простых преобразований  в обозначениях  и  можно переписать как

  

где предполагается, что A не зависит от  и

 

Отсюда ясно, что максимизация  относительно  эквивалентна минимизации

 

Аналогично максимизация  эквивалентна минимизации

 

при ограничении, задаваемом дифференциальным уравнением . Соотношение  задает функцию потерь метода наименьших квадратов, которая при правильном выборе априорных дисперсий и выполнении предположений о гауссовости ,  и  эквивалентна функции потерь максимума апостериорной вероятности.

Форма уравнения  такова, что напрашивается применение дискретного принципа или дискретных уравнений Эйлера-Лагранжа. Гамильтониан задается формулой

  

в которой

 

Каноническое уравнение и граничные условия имеют вид

 

 

 

 

Этими каноническими уравнениями и соответствующими граничными условиями определяется нелинейная двухточечная краевая задача (ДТКЗ), решением которой является искомая оценка при фиксированном интервале сглаживания.

Довольно трудоемкие вычисления позволяют получить следующую развернутую запись канонических уравнений:

 

  

где обозначено:  

Слагаемые  в формулах для  и  содержат квадратичные относительно  выражения. При использовании процедуры инвариантного погружения члены степени выше первой по  исчезают. Поэтому при решении методом инвариантного погружения можно пользоваться эквивалентным выражением

 

Эти уравнения необходимо решить при двухточечных граничных условиях

 

Двухточечную краевую задачу для непрерывного случая можно получить, устремляя к бесконечности плотность точек фиксации в уравнениях  и  или применяя непрерывный вариант принципа максимума для минимизации функции штрафа  при ограничении, задаваемом дифференциальным уравнением . При использовании последнего подхода вводится гамильтониан

 

и выписываются канонические уравнения

  

Проводя необходимые преобразования, получим двухточечную краевую задачу

 

 

с начальными и конечными условиями

 

Переменная состояния в этих формулах может и должна записываться в виде , чтобы подчеркнуть, что если найдено решение задачи оценивания x по наблюдениям до момента .

  1.  Метод инвариантного погружения для решения ДТКЗ

  1.  Замечания о методе

В данной главе излагается метод инвариантного погружения, использование которого обозначено в названии главы. Существуют иные методы квазилинеаризации, но их использование сопряжено с нехваткой такого важного ресурса как время, недостаток которого всегда испытывается в задачах прилагательного характера. Он предназначен для обработки состояний динамической системы в реальном времени, по сути, время тратится лишь на вычисление пары не очень громоздких формул, в которых требуется подсчитать две основополагающие матрицы. Использование этого метода сопряжено с возможной неэффективностью и неточностью его применения для получения оценок состояний динамической системы. Поэтому в разделе  (см стр. 86) производится статистический анализ данного факта, целью которого должно стать подтверждение или опровержения вопроса неэффективности.

  1.  Получение алгоритмов нелинейной фильтрации для непрерывных систем методом инвариантного погружения

В целях упрощения обозначений и увеличения общности результатов вывод будет базироваться на общей постановке двухточечной краевой задачи. Необходимо найти решение ДТКЗ:

 

 

с условиями на концах траектории

 

Эта формулировка охватывает двухточечную краевую задачу из предыдущего раздела.

Основная идея инвариантного погружения состоит во включении частной задачи в обобщающую задачу. Если можно решить общую задачу, то частная задача решается автоматически. Удивительно, что часто легче решить общую задачу.

Давайте обобщим ДТКЗ, т.е. осуществим инвариантное погружение задачи  – , допустив, что условие на конце траектории при  равно не 0, а c. Другими словами, вместо  будем писать . Кроме того, будем предполагать, что как величина c, так и момент  переменны. В частности, будут рассматриваться «соседние» траектории, одна из которых удовлетворяет условию , а другая – условию .

Относительно первой траектории допустим, что значение состояния системы на конце траектории имеет вид

 

Другими словами, функция  отражает связь  между условием на конце  и финальным значением  при . Если эта функция известна, то ДТКЗ легко можно было бы решить, интегрируя  и  от конца траектории при условиях  и . Но функция  неизвестна, таким образом, необходимо иметь метод ее определения.

Предположим, что имеется траектория, для которой при ,  и . Изменим слегка конечное значение  на :

 

и соответственно

 

где и  имеют порядок . Но, с другой стороны,

 

Приравнивая правые части  и , получим

 

Если разложить правую часть уравнения  в ряд Тейлора относительно c и , получим

 

Обращаясь к уравнениям  и , видим что

 

и

 

Так что уравнение  принимает вид

 

Поделив  на  и устремляя  к нулю, получим уравнение в частных производных, которому должна удовлетворять функция :

 

К сожалению, неизвестно, как решать это уравнение в общем виде. Однако часто можно аппроксимировать решение линейной функцией

 

Здесь  – решение для случая , которое является решением исходной ДТКЗ при . Другими словами, предполагается, что  является оптимальным значением  (все еще неизвестным), если , плюс линейная комбинация отклонений от 0, характеризуемых вектором c. Такая аппроксимация будет хорошей, если , т.е. когда рассматривается траектория вблизи истинного оптимального значения. В дальнейшем будем предполагать, что c мало, пренебрегая членами порядка и выше.

Если подставить  в , получим

 

Теперь разложим  и  в ряд Тейлора относительно x, c и , ограничившись членами порядка c. Уравнение  преобразуется к виду

 

Для того чтобы продолжить вывод, необходимо вместо  и  подставить функции, которые соответствуют ДТКЗ для фильтрации системы.   Ради  простоты воспользуемся ДТКЗ вида  – . Из  и  функции  и  определяются как

 

 

Отметим, что последнее слагаемое в выражении для  можно опустить, так как оно второго порядка малости по c. Теперь, если подставить  и  в , получим следующее выражение, не содержащее членов более высокого порядка малости, чем c:

 

Так как эта формула должна быть справедлива для всех достаточно малых c, можно отдельно приравнять коэффициенты при членах нулевого и первого порядков малости по c. В результате получим

 

 

Этот результат можно представить в несколько более знакомой форме, подставив . К тому же вспомним, что финальный момент  переменный, и обозначим его просто . С этими изменениями уравнения  и  перепишутся в виде

 

 

Начальные условия для уравнений ,  можно получить из условия , а именно из условия

 

Если решить это уравнение относительно , получим

 

Простое сравнение уравнений  и  обнаруживает, что следует использовать начальные условия

 

 

Теперь можно проинтегрировать уравнения  и  в прямом времени с начальными условиями  и , чтобы получить последовательную оценку «состояния» . В случае постоянных параметров, которые представляют наибольший интерес при идентификации систем, значение, получаемое в момент , представляет собой выход фильтра и сглаженную оценку. Для удобства результаты сведены в табл. 4.1.

Уравнение  очень похоже на уравнение фильтра Калмана для дисперсии ошибок. Если f, g и h –  линейные функции x, то  есть просто фильтр Калмана.

Необходимо заметить, что авторы книги [4] на стр. 209 указывают, что уравнение  представляет собой первое приближение оценки условной дисперсии ошибки

 

если начальное условие  и входные шумы  и  –  гауссовские процессы.

Данный теоретический материал был взят из монографии Сейджа и Мелсы [4, с. 51-52, 58-65, 204-209] и в несколько переработанном, упрощенном и обобщенном виде был помещен на страницы данной работы. Табличная систематизация была позаимствована из сборника «Фильтрация и …» [3]. Целью воспроизведения данной порции теории стала необходимость изложить теоретические вопросы нелинейной фильтрации, метода инвариантного погружения,

Табл. 4.1. Непрерывные алгоритмы инвариантного погружения

Характеристика

Уравнение

Модель системы

Модель наблюдений

Начальные условия

Статистические характеристики

Алгоритм фильтрации

Уравнение для дисперсии ошибки

                                           

                                                        

       

так как для понимания практической части необходимо некоторое ознакомление с теорией по данному вопросу. Для более полного ознакомления с теоретическими вопросами фильтрации и идентификации систем следует познакомиться с [4], где формулы фильтрации приведены с полными обоснованными доказательствами, а также имеются примеры, поясняющие суть материала

  1.  Моделирование динамики нелинейной системы, результатов наблюдений и оценок

  1.  Моделирование входных возмущений (меандровая модель)

Рассмотрим

,

где x(t0) = x0,  –  меандр, или

Это выражение обозначает белую кусочно-постоянную гауссовскую последовательность.

В пределе при  и интервал Dt0, т.е. частота переключения становится очень большой. Меандр дает в пределе нам функцию гауссовского белого шума, которую можно рассматривать как функцию времени (что и было сделано в практической реализации), состоящую из суперпозиции произвольно большого числа независимых импульсов очень малой длительности с нормально распределенными амплитудами. В действительности белый гауссовский шум представляет собой просто полезную идеализацию многих физических явлений, таких как случайные порывы ветра, электронные и атмосферные шумы, ошибки измерения датчиков и т.п.

Термин белый гауссовский шум используется потому, что этим процессом обычно моделируют излишние и нежелательные эффекты в системах. 

Хорошо пронаблюдать «реальные» меандровые возмущения на графиках (см. рис. 4.2, 4.3, 4.4), где прекрасно видно, как поэтапно происходит «уточнение» значений меандра, что постепенно превращает его в белый гауссовский шум. Более подробный случай получается за счет усреднения соседних точек исходно заданной базовой последовательности случайных точек, на основе которой стоятся меандры с различной степенью «сглаженности».

При случайном меандровом воздействии на систему, если не производить определенной корректировки значений меандра в каждой точке, будем получать различную дисперсию исходного стохастического процесса, что совершенно неправильно, так как, меняя шаг меандра, мы при моделировании должны получить процесс с теми же характеристиками, но в более «уточненном» варианте.

Таким образом, необходимо провести небольшое математическое исследование с целью установить связь между дисперсией состояний системы моделируемого процесса и шагом разбиения меандра. Если возможно установить связь такого рода, то тогда на практике можно будет «скомпенсировать» значения меандра так, что дисперсия исходного процесса останется неизменной при различных значениях шага меандра

Очевидно, что меандровая модель может быть применена как в линейном, так и нелинейном случае, так как универсальна применительно к вопросу моделирования. Поэтому исследование для простоты можно провести на линейной модели, используя результаты при моделировании динамики нелинейных систем.

Метод вариаций и постоянных приводит к решению задачи Коши в виде:

Здесь  – фундаментальная матрица.

При условии, что шаг h является постоянным. Получаем выражение

,

где Ji можно рассматривать как сумму двух следующих слагаемых:

N – целая часть выражения t/N. Поэтому N есть функция от t, то есть N(t), и, следовательно, Ji также зависит от t, Ji(t).

Теперь найдем генеральные моменты случайной функции x(t), стохастичный характер которой объясняется наличием случайных компонент {i}.

при условии, что =0 по определению, так как .

Найдем отклонение от среднего:

.

Теперь определим корреляционную матрицу  в момент времени t:

где M = eAt  матрица nn,

im-вектор, в общем случае mn,

Ji – матрица nn, тогда

K– матрица nn, где ,

D – дисперсионная (точнее корреляционная) матрица самих векторов i размером nn.

Требуется   выбрать  D   таким  образом,   чтобы K(t) не зависела  от h  асимптотически (при ).

Рассмотрим

Тогда, с учетом того, что матрицы eAt, A-1 и (I  e-Ah) коммутируют, получим:

Перепишем K(t) в асимптотическом приближении для большей ясности:

Требуется определить S.

Рассмотрим скалярный случай, t0=0, когда  A = - – скаляр.

Корреляционная матрица запишется в виде:

при

Чтобы это выражение не зависело от h, необходимо чтобы.

Окончательно получаем, , где Q=const.

Теперь попробуем получить схожий результат в векторном виде:

где T –  матрица векторов-столбцов – собственных векторов A,

–  матрица векторов-столбцов – собственных векторов AT,

– диагональная матрица собственных значений A и AT.

Рассмотрим k, l – элемент матрицы S:

где – сумма членов геометрической прогрессии, причем первый член: , знаменатель:

При  для устойчивости системы необходимо, чтобы {i}<0

Остается:

Для простоты посчитаем, что , где – некоторая величина, независящая от h. Рекурсивно возвращаясь назад, получаем, что , где , в свою очередь, также не зависит от h.

Окончательно получаем, что

K(t) = h2 S + D2 = h+ …,

а значит, нужно выбрать , чтобы дисперсия случайного процесса x(t) не зависела от изменения шага h , что полностью согласуется со скалярным случаем.

  1.  Моделирование состояний и наблюдений нелинейной стохастической динамической системы при меандровых воздействиях

Состояния нелинейной системы на практике формируются решением исходного дифференциального уравнения. Решение производилось при помощи одного из численных методов. Моделирование состояний на практике можно пронаблюдать на рис. 4.5 (темные кривые). В идеальном случае графики должны быть гладкими, но при воздействии случайной составляющей каждая кривая эволюции популяции имеет «зазубрины», означающие случайные воздействия  со стороны окружающей среды. Для сравнения приведен график в линейном случае (см. рис. 4.6).

Нетрудно заметить (см. рис. 4.5), что значения наблюдений,   становящиеся отрицательными, попросту «усекаются». Это сделано потому, что рассматриваемая система характеризует модель поведения популяции, а это значит, популяция просто не может быть отрицательной. Такое введение штрафной функции использовано во всех дополнительных процедурах фильтрации. Для того чтобы не возникло проблемы деления на ноль, применяется некоторая малая величина . Таким образом, в случае отрицательности популяции, в нее вносится какое-то минимальное число особей, что можно позволить из-за стохастичности системы, чтобы возобновить функционирование популяции зайцев либо волков.

  1.  Моделирование фильтрационных оценок при меандровых возмущениях

Как видно из рис. 4.7., нелинейный фильтр практически ликвидирует случайное внешнее воздействие на наблюдателя, что говорит о качественных возможностях фильтра

В процессе моделирования вычислительная процедура содержала интересный вычислительный прием, надобности в котором не было в процедурах линейной фильтрации. Здесь матричное уравнение Риккати решалось совместно с получением фильтрационных оценок состояний системы, так как эти оценки являются параметрами уравнения Риккати. Это уравнение решалось стандартными численными методами. Процедура решения совмещенной системы находится в приложении 3.

Интересно пронаблюдать в сравнении вычислительные аспекты фильтрации как в нелинейном, так и в линейном случае.

Теоретический материал, посвященный фильтрации линейных систем в дискретном и непрерывном времени, вычислительным аспектам фильтра Калмана и т.п., можно найти в монографии Медича [1]. Здесь формулы линейной фильтрации приведены с полными последовательными доказательствами, а также имеются примеры, поясняющие суть материала

  1.  Проверка эффективности применения метода инвариантного погружения

Если посмотреть на пары рисунков 4.5 – 4.12, то они выглядят очень похожими, но резко бросается в глаза существенные различия графиков 4.11 и 4.12. С чем это связано?

Изначально в линейном фильтре матрица P является корреляционной матрицей ошибки фильтрации. Это хорошо видно из рис. 4.12 – диагональные элементы этой матрицы очень быстро стабилизируются, что говорит о функциональной особенности фильтра быстро приспосабливаться к имеющейся ошибке и довольно точно оценивать исходные состояния системы.

Колебательный характер диагональных элементов матрицы P в нелинейном случае связан как раз с нелинейностью самой системы. Причем напрямую этот характер обусловлен применением процедуры инвариантного погружения, которая производит некоторую квазилинеаризацию системы, что вообще может привести к потере точности вычислений.

Если хотя бы статистически удалось показать, что диагональные элементы матрицы характеризуют дисперсию ошибки фильтрации, то это докажет правильную необходимость использования данного метода в процессе нелинейной фильтрации. Для этих целей был проведен статистический эксперимент по методу статистических испытаний (Монте-Карло), который на практике позволил проверить исходные собственные предположения и предположения других авторов.

Проведем следующий эксперимент. Осуществим запуск исходной программы определенное число раз (было использовано 100 итераций). Что позволит набрать статистику о значениях диагональных элементов матрицы P и посчитать фактическую ошибку фильтрации на протяжении числа этих итераций. Одновременно можно вычислить итеративно среднее и дисперсию ошибки фильтрации по следующим формулам

 

Данные вычисления довольно трудоемки по времени, но имеют меньшие затраты по памяти. Доверительный интервал для фактической дисперсии можно определить следующим образом

 

где  – объем выборки,  – оценка дисперсии, а  с  степенями свободы. На рис. 2.13 можно наглядно пронаблюдать, как соотносятся фактическая дисперсия ошибки фильтрации с расчетной в момент сотой итерации. Результат довольно обнадеживающий, так как обе кривые лежат на одной оси, в общем, имеют схожий характер поведения с небольшим отклонением.

Теперь, чтобы окончательно убедится в точности предположений. Попробуем другой способ удостовериться, что расчетная дисперсия описывает фактическую дисперсию. Для этого на основе полученных статистических данных вычислим среднее значение расчетной дисперсии (той дисперсии, которую вычисляем в процессе фильтрации) в каждый момент времени , а также СКО  данного параметра, на основании которого можно построить доверительный интервал . Далее совместим эти два графика (см. рис. 4.14). Хорошо прослеживается тот факт, что фактическая дисперсия полностью поместилась в построенный интервал, а также подробно описывает среднее значение расчетной дисперсии, правда с несколько большей амплитудой.

Для более полного обоснования полученного факта, возможно, потребуется провести некоторые дополнительные исследования и тесты, выходящие за рамки данной работы,  но уже сейчас уже можно определенно сказать, что путь исследования является верным и должен привести к удовлетворительным результатам.


Заключение

Цели, поставленные в данной работе, были успешно достигнуты. Была показана технология перевода файлов  языка MATLAB на C/C++ с практическими рекомендациями и пояснениями, которые, возможно, облегчат использование данной технологии пользователями. Одна из глав работы была составлена как подробное руководство по созданию автономных приложений из файлов языка MATLAB, изучение которой позволит быстро и без особых проблем создать исполняемый модуль.

В связи с тем, что генерируемого MATLAB исполняемого файла автономного консольного приложения не достаточно для разработки современного программного продукта, было осуществлено создание технологии использования кода на языке MATLAB (предварительно переведенного на язык Си с применением функций MATLAB C Math Library) в визуальной среде программирования, последовательное описание которой приводится в нескольких главах работы. В качестве важного дополнения был показан способ создания программ на языке MATLAB таким образом, чтобы прикладывать наименьшие усилия по их использования для создания автономного приложения на C/C++.

В теоретической части, касающейся нелинейной фильтрации, было проведено исследование, связанное с моделированием шумового воздействия в виде меандра, и какое влияние оно оказывает на моделирование состояний, наблюдений и фильтрационных оценок. Дополнительно осуществлялась проверка того, описывает ли расчетная дисперсия ошибки нелинейной фильтрации фактическую дисперсию, и если «да», то каким образом. Это исследование послужило для проверки точности свойств полученного нелинейного фильтра, дало новые данные, касающиеся эффективности использования метода инвариантного погружения при решении задач нелинейной фильтрации.

И как итог, данная технология была использована для проектирования приложения нелинейной фильтрации модели популяции в среде Borland C++ Builder v6.0.

Необходимо отметить, что описанная технология требует серьезного развития и расширения в силу возможного эффективного применения для быстрого создания приложений, использующих большой объем нетривиальных математических вычислений. Необходимо также реабилитировать употребление математической C++-библиотеки MATLAB для создания автономных приложений. Данная технология создала базу для разработки специального программного средства, которое может позволить быстро и удобно создавать приложения прямо в Borland C++ Builder, используя исходный код на языке MATLAB.


Список использованной литературы

1. Медич Дж. Статистически оптимальные линейные оценки и управление. – М.: Энергия, 1973. – 440 с.

2. Параев Ю. И. Введение в статистическую динамику процессов управления и фильтрации (Библиотека технической кибернетики). – М.: Советское Радио, 1976. – 184 с.

3. Фильтрация и стохастическое управление в динамических системах / Под ред. К. Т. Леондеса. – М.: Мир, 1980. – 408 с.

4. Сейдж Э. П., Мелса Дж. Л. Идентификация систем управления. – М.: Наука, 1977. – 248 с.

5. MATLAB Compiler User’s Guide. Fifth printing. Revised for Version 2.1 (Release 12) – 2000. – 264 с.

6. MATLAB C Math Library User’s Guide. Revised for Version 2.1 (Release 12) – 2000. – 332 с.

7. MATLAB C/C++ Graphics Library User’s Guide. Fifth printing. Revised for Version 2.1 (Release 12) – 2000. – 52 с.

8. MATLAB C Math Library Reference. Revised for Version 2.1 (Release 12) – 2000. – 429 с.


Приложение 1. Руководство пользователя

Часть 1. Приложение нелинейной фильтрации динамической системы модели популяции

Дистрибутив приложения включает исходный m-код, полученный трансляцией C-код, проектные файлы Borland C++ Builder и исполняемый модуль в двух вариантах:

  •  BPL_RTL_MainProject.exe, компонованный совместно с библиотеками Borland
  •  ALONE_MainProject.exe, автономное приложение.

Однако, для работы с приложением еще необходимо, чтобы в системы были установлены библиотеки MATLAB времени исполнения (см. раздел ).

На рисунке 5.1 представлена рабочая область приложения фильтрации.

Область состоит из четырех частей. Вверху расположено главное меню и панель управления, полностью соответствующая элементам меню:

Процесс. В данном подменю можно начать работу приложения с помощью элемента "Новый" (соответствует кнопке панели управления), при этом приложение будет настроено на установки по умолчанию, очистится рабочее окно, и будет позволено сразу начать процесс вычислений с помощью "Запустить" (кнопка на панели). Также здесь располагается элемент "Завершить" (кнопка ), активный лишь в момент вычислений, позволяющий при необходимости досрочно завершить процесс фильтрации. С помощью "Выход" можно завершить работу приложения.

Параметры. Здесь присутствуют элементы, осуществляющие работу со структурой параметров. Первоначально пользователю разрешается загрузить параметры из файла, записанные до этого через "Сохранить в файл" (кнопка ). Это довольно удобно, так как параметров не так уж мало, и вводить их заново достаточно неприятное занятие. Изменить параметры можно посредством элемента "Редактировать" (последняя кнопка ).

Помощь. Здесь выдается симпатичная информация о создателе, написанная с помощью MATLAB в качестве демонстрации.

Теперь подробно рассмотрим форму параметров, выполненную на основе перелистывающихся закладок. Также на форме (см. рис. 5.2) имеется  три кнопки, позволяющие установить параметры по умолчанию, принять новые параметры и отменить текущие изменения. Все параметры разбиты на четыре смысловые группы:

Параметры системы. Данная группа (см. рис. 5.2.) позволяет изменить параметры уравнения Холлинга-Тэннера, задающего модель популяции. Здесь же устанавливается начальная точка вычислений, которая может выражаться в абсолютных координатах, либо выставляется относительно точки покоя системы, в свою очередь, зависящую от параметров уравнения. Также можно выставить значение минимального числа особей, вносимых в первую компоненту системы, если их число будет приближаться к нулю (что можно допустить в силу стохастичности системы), дабы предотвратить деление на ноль при вычислении уравнений фильтрации.

Параметры шума. Группа параметров, располагающихся на этой закладке (см. рис. 5.3.), дает возможность управлять шумом в системе, наблюдений и априорными знаниями о популяции. Управление осуществляется установкой амплитуды шумовых компонент и коэффициента корреляции между ними. Ковариационную матрицу, образуемую установленными параметрами можно посмотреть, нажав кнопку "Обновить".

Здесь же можно установить значения переходных матриц возмущений и измерений, участвующих в уравнениях фильтрации, которые задают полноту получаемой информации о наблюдениях и оценках.

Параметры времени. На этой закладке (рис. 5.4) позволяется установить временной интервал развития популяции, а также отрегулировать датчики случайных чисел, выставив соответствующее число отсчетов вычислений. Также возможно зафиксировать датчик, если необходимо пронаблюдать одну реализацию процесса при различных параметрах.

Параметры графики. Данные параметры (рис. 5.5) управляют выводом заранее предусмотренных графиков, показывающих «под различным углом» результаты работы программы.

Главная форма (см. рис. 5.1) также содержит область текстового вывода текущей информации о работе программы, сюда же перенаправляются предупреждения и ошибки, возникающие при использовании функций математической библиотеки MATLAB, а также общее время вычислений.

Для удобства работы с программой в строке статуса выводится процент прогресса текущих вычислений, позволяющий отследить состояние процесса вычислений.

Часть 2. Алгоритм нелинейной фильтрации

Реализация алгоритмов нелинейной фильтрации включает следующий перечень файлов:

"Main.m" - главная вычислительная процедура,

  1.   "SolveSystem.m", "htnoise.m" - функции-параметры, использующиеся математическими функциями MATLAB,
  2.   "GetA.m", "GetZ.m" - подфункции, необходимые для получения промежуточных значений в процессе вычислений,
  3.   "Meandr.m", "Smooth.m", "ht.m" - служебные функции, вынесенные вне основной процедуры.

В Приложении 3 приведен листинг всех вышеперечисленных процедур в соответствии со списком, подробнейшим образом закомментированный для большей ясности.

Все программы проверены на работоспособность и синтаксическую правильность в среде MATLAB и могут быть использованы при необходимости для вычислений.

Основным файлом является "Main.m", который и следует запускать в MATLAB. Чтобы пронаблюдать приведенные иллюстрации в более крупном виде, или чтобы провести свои собственные эксперименты достаточно установить временные параметры nt – количество отсчетов меандра, np – число отчетов наблюдений. А также, если это необходимо, можно установить параметры шума.

Служебные функции и подфункции должны размещаться в той же директории, что и функция Main. Если путь к  программной директории не установлен, сделайте это в меню Файл окна управления MATLAB или с помощью команды cd.


Приложение 2. Руководство программиста

В данном разделе рассмотрим итоговое приложение нелинейной фильтрации, создание которого проводилось на базе выше описанного материала. При построении были задействованы практически все нюансы описанных технологий. Но, все-таки, полезно еще раз постепенно пройти все технологические, чтобы посмотреть технология не на отдельных пояснительных примерах, а на реальном комплексном приложении.

Этап 1. Создание исходного m-код.

Исходный m-код был разработан раннее и находится в Приложении 3. Но если внимательно его изучить, то видно, что он делался с расчетом на среду MATLAB. Пользовательский интерфейс отсутствует полностью, все параметры выставляются вручную прямо в m-коде, не выводится никакого прогресса вычислений, нет досрочного прекращения вычислений. Поэтому данная программа была переработана, и все вышеперечисленные недостатки были исправлены, причем код из Приложения 3 был превращен в «неизменяемый». Это было сделано для того, чтобы удобнее отлаживать имеющейся код, потому что, добавив хотя бы вывод одного сообщения, придется опять повторять третий этап.

Ниже можно посмотреть внесенные корректировки.

% Файл "func.m"

function main

global Q R H G XX PP T0 T np nt n dtp RR r s j w k d xp x1eps max1 pers oldpers

tic

max1 = 38; % Средний процент от общего времени вычислений решения 1-го диф. Ур-я

pers = 0; oldpers = 0; %Вспомогательные переменные для вычисления тек. % вычисл.

% Блок инициализации

eq_p = AssignValue(1); % Получаем параметры уравнения, введенные пользователем

r = eq_p(1); j = eq_p(2); w = eq_p(3); K = eq_p(4); D = eq_p(5); s = eq_p(6);

 % Подобным образом считываем другие параметры

rn_f = AssignValue(10); % Получаем значение флага фиксации датчика случ. чисел

if (rn_f),

 rnd1=[362436069;209195];randn('state',rnd1);

 rnd2=[362436069;521288629];randn('state',rnd2);

end

 % Блок вычислений – полностью соответствует «старому» коду

time = toc;

fprintf(1, 'Продолжительность вычислений по времени составила %5.2f сек. \n',

       time);

SyncVCL(2); % Выводим на форму (2 – в Memo) сообщение о времени вычислений

% Вывод графики

% Получаем параметры вывода графиков – общие (ot_g_t) и по каждому в отдельности

ot_g_t = AssignValue(11); gp = AssignValue(12);

if (~(ot_g_t==2)),

 if (~ot_g_t | gp(5)), figure;                                                                    

 % Осуществляем проверку необходимости выводить график (для каждого из 5) и

   % и водим при наличии необходимости  

end;

 % Вспомогательные функции (сохраняются без изменений, за исключением ...)

function z=htnoise(tt,x) % Функция-параметр 1-го дифференциального уравнения,

global G max1 T pers oldpers, % где можно вычислять и выводить прогресс вычисл.

% Тестируем C-переменную на необходимость досрочного завершения вычислений

MyExitThread = AssignValue(0);

if (MyExitThread),

 fprintf(1, 'Выполнение процесса завершено досрочно \n\n');

 SyncVCL(2); % Выводим вышеозначенное сообщение на форму (2 – в Memo)

 CloseThread; % Досрочно завершаем вычисления

end

pers = floor((tt/T)*max1); % Вычисляем текущий процент, исходя из того, что

% вычисление 1-го диф. ур-я занимает ~38% от общего объема вычислений

if (pers > oldpers), % Выводим процент на форму (1 – StatusBar)

 fprintf(1, '%5.0f %%', pers); SyncVCL(1);

 oldpers = pers;

end

z=ht(tt,x)+G*meandr(tt);

function xp1=solvesystem(tt,XP); % Функция-параметр второго диф. ур-я

 % Аналогично как и выше, только вычисления занимают (100-max1)%

function grid(arg1, arg2);

%GRID   Grid lines.

function hh = title(string,varargin)

%TITLE  Graph title.

% Вызов внешних C-функций

function CloseThread;

%#external

function SyncVCL(MethodNumber);

%#external

function OutVar = AssignValue(VarNumber);

%#external

Так как разработка интерфейса относится лишь к четвертому этапу, то не должно еще быть известно каким образом осуществляется вывод, поэтому данная информация указана в скобках.

Этап 2. Перевод исходного m-кода в C-код с помощью MATLAB Compiler.

Перевод m-кода в C-код осуществлялся из командной строки в директории, где располагались подлежащий переводу файл "func.m", включающий основную функцию main и все другие подфункции и даже те, которые вызываются по имени (см. раздел ).

mcc –m –B sgl func.m

После выполнения данной команды будут созданы следующие файлы: "func.c", "func.h", "func_external.h", "func_mainhg.c",  и будет выведено сообщение о том, что компилятор Borland C++ не смог провести компоновку EXE-файла, так как не найдены три нереализованные функции. Можно провести альтернативный перевод без предупреждений, правда немного больше необходимо выполнить действий:

mcc -t -h libmmfile.mlib libmwsglm.mlib func.m

будут созданы func.c", "func.h", "func_external.m" и

mcc -W mainhg libmmfile.mlib libmwsglm.mlib func.m

будет создан  «файл-оболочка» "func_mainhg.c".

Этап 3. Присоединение созданного компилятором MATLAB C-кода к приложению, разрабатываемому в визуальной среде программирования.

Следующим этапом стало создание проекта в Borland C++ Builder, основой которому послужил пример, описанный в разделе . Пример был создан таким образом, что его удобно использовать в качестве шаблона. Модуль, реализующий методы формы, содержит лишь создание вычислительного потока, внутри которого уже происходит инициализация графической библиотеки и таблиц компилятора. Рассмотрим модуль, реализующий методы вычислительного потока.

// Файл "cpph.h" ---------------------------------------------------------

 // Содержимое "func_mainhg.c" до функции main

#include "func_external.h" //Содержит объявл. реализ. версий суррогатных функций

 // Стандартный PrintHandler

//------------------------- ErrorHandler ------------------------------------void ErrorPrint(const char *msg, bool isError  )

{

 if(!isError)

 {

      WinPrint(msg);

      MyThread->UpdateMethod(2); // Выводим сообщение, синхронизируясь с VCL

 }

   else

 {

      WinPrint("Error: ");WinPrint(msg);WinPrint("\n");

      WinPrint("\nВозникла ошибка использования функций библиотеки           

                MATLAB!!!\n\n");

      MyThread->UpdateMethod(2);

      Mfunc_CloseThread(); // Досрочно прекращаем вычисления

 }

}

// Файл "Unit3.cpp" ---------------------------------------------------------

#include "Unit1.h" // Содержит объявление главной формы приложения Form1

#include "Unit3.h" // Содержит объявление класса потока

#include "cpph.h"

#include "pars.h" // Содержит объявление структуры и переменной текущих парам-в

extern int MyExit; // Переменная-флаг, говорящая о необходимости закрыть поток

//---------------------------------------------------------------------------

__fastcall TNewThread::TNewThread(bool CreateSuspended)

       : TThread(CreateSuspended)

{

 const char *argv = *_argv;

 mlfHGInitialize(&_argc, &argv);

 FreeOnTerminate = true;

}

void __fastcall TNewThread::Execute()

{

 // Регистрируем Error & Print Handlers

 mclLibInitCommon(&_main_info );

 mlfSetPrintHandler(WinPrint); mlfSetErrorHandler(ErrorPrint);

 mlfFunc(); mlfHGWaitForFiguresToDie();

 WinPrint(" 100%");

 UpdateMethod(1); // Обновляем строку статуса

 UpdateMethod(3); // Разрешаем использовать элементы меню и кнопки

}

void __fastcall TNewThread::UpdateMethod(int mode)

{

 switch (mode)

 { // Безопасный вызов методов потока для обновления главной формы

   case 1: Synchronize(UpdateStatusBar); break; // строки статуса

   case 2: Synchronize(UpdateMemo); break; // текстового окна

   case 3: Synchronize(UpdateMenu); // элементов меню и кнопок панели

 }

}

void __fastcall TNewThread::UpdateMemo()

{

 AnsiString s = Form1->Memo1->Lines->Text;

 AppendStr(s, AnsiString(OutputBuffer));

 Form1->Memo1->Lines->Text = s; FreeOutput();

}

void __fastcall TNewThread::UpdateStatusBar()

{

 Form1->StatusBar1->Panels->Items[1]->Text = OutputBuffer;

 FreeOutput();

}

void __fastcall TNewThread::UpdateMenu()

{

 int BT[] = {1, 1, 0, 1, 1, 1}; // Массив, указывающий обновляемые элементы

 Form1->UpdateMenuButtons(BT); // у меню и панели

}

void Mfunc_CloseThread(void)

{

 MyThread->UpdateMethod(3);//Обновляем меню и кнопки на главной форме

 EndThread(0);

}

void Mfunc_SyncVCL(mxArray * MethodNumber)

{

 MyThread->UpdateMethod(*mxGetPr(MethodNumber));// Выводим текст на форму

}

extern mxArray * Mfunc_AssignValue(int nargout_, mxArray * VarNumber)

{

  mxArray *ReturnNumber = NULL;

  int VarNum;

  VarNum = (int)*mxGetPr(VarNumber);

  switch (VarNum)

  {

    case 0: ReturnNumber = mlfScalar(MyExit); break;

    case 1: ReturnNumber = mlfDoubleMatrix(1, 6, CurPars->equation_pars,

                                           NULL);break;

    case 2: ReturnNumber = mlfScalar(CurPars->x1eps); break;

    case 3: ReturnNumber = mlfScalar(CurPars->begin_point_type); break;

    case 4: ReturnNumber = mlfScalar(CurPars->factor); break;

    case 5: ReturnNumber = mlfDoubleMatrix(2, 1, CurPars->bpxy, NULL);break;

    case 6: ReturnNumber = mlfDoubleMatrix(1, 9, CurPars->noise_pars,

                                           NULL);break;

    case 7: ReturnNumber = mlfDoubleMatrix(2, 2, CurPars->G, NULL);break;

    case 8: ReturnNumber = mlfDoubleMatrix(2, 2, CurPars->H, NULL);break;

    case 9: ReturnNumber = mlfDoubleMatrix(1, 5, CurPars->time_pars,  

                                           NULL);break;

    case 10: ReturnNumber = mlfScalar(CurPars->random_fix); break;

    case 11: ReturnNumber = mlfScalar(CurPars->output_graph_type); break;

    case 12: ReturnNumber = mlfDoubleMatrix(1, 5, CurPars->time_pars, NULL);

  }

  return ReturnNumber;

}

Этап 4. Внесение новых функциональных возможностей.

Здесь было организовано две формы – основная форма и форма параметров, вызываемая модально из главной (за описанием функций компонентов и математических возможностей приложения обратитесь к Руководству пользователя). Также была осуществлена реализация суррогатных функций.

Для обмена между формой параметров и модулем потока использовалась описанная ниже структура, которую к тому же удобно сохранять в отдельный файл, а затем при необходимости  загрузить. Данная структура полностью соответствует форме параметров и удовлетворяет переменным-параметрам, использующимся в математическом коде.

// Файл "pars.h" ---------------------------------------------------------

typedef struct Parameters pars;

struct Parameters

{

 char Str[18];

 double CRC;

 // Установка 6 параметров уравнения в следующем порядке:

 // r, j, w, K, D, s

 double equation_pars[6], x1eps;

 double begin_point_type;

 double factor, bpxy[2];

 // Установка 3 матриц, управляющих шумом, в следующем порядке:

 // Q - система [s1Q, s2Q, rQ],

 // R - наблюдения [s1R, s2R, rR],

 // P - начального значения [s1P, s2P, rP].  

 double noise_pars[9];

 double G[4], H[4];

 // Установка 5 параметров времени в сдующем порядке:

 // T0, T, n, np, nt

 double time_pars[5], random_fix;

 double output_graph_type;

 double graphics_pars[5];

};

extern pars *CurPars, *NewPars, *DefaultPars;

Изначально создается запись, содержащая параметры по умолчанию. В начале работы с программой текущей записи присваиваются значения структуры по умолчанию. Далее значения текущей записи можно изменить с помощью формы параметров, причем типы и содержимое параметров строго отслеживаются на этапе запоминания текущей структуры.


Приложение 3. Программная реализация в MATLAB нелинейного фильтра для случая непрерывных наблюдений

(1) Основная часть

% Файл "Main.m"

% Процедура моделирования непрерывного стохастического процесса с дальнейшей

% фильтрацией шумового воздействия с помощью алгоритмов инвариантного погружения

global A Q R H G XX PP T0 T np nt n dtp RR r s j w k d xp x1eps,

% Параметры системы (уравнения Холлинга-Тэннера)

r = 1; s = 0.3; j = 0.5; w = 0.8571; K = 350; D = 50; x1eps = 0.001;

ot1 = 0.5 * (K – D – K * w / (j * r) + sqrt((K – D – K * w / (j * r))^2 ...

     + 4 * D * K));

k = K / ot1; d = D / ot1;

w = j * r * (k - 1) * (d + 1) / k;

s1 = r * (k – d - 2) / (k * (d + 1)); s = s1 * 1.1;

xp = [1; 1 / j]; % Точка покоя

x0 = 3 * xp; % Начальная точка

% Параметры шума

% Q - шум в системе

% R - шум наблюдений

% s1X, s2X - амплитуды шумового воздействия на компоненты системы

% rX - коэффициент корреляции шумовых компонент

% s1P, s2P - амплитуды шумового воздействия на начальное значение

% rP - коэффициент корреляции шумовых компонент начального значения

s1Q = 0.1; s2Q = 0.1; rQ = 0;

s1R = 0.5; s2R = 0.5; rR = 0;

s1P = 0.01; s2P = 0.01; rP = 0;

Q = [s1Q^2, s1Q * s2Q * rQ; s1Q * s2Q * rQ, s2Q^2];

R = [s1R^2, s1R * s2R * rR; s1R * s2R * rR, s2R^2];

% Матрицы преобразования компонент шума (воздействие может осуществляться

% не на все составляющие системы)

H = [1 0; 0 1]; G = [1 0; 0 1];

MM = []; ZZ = []; XX = []; W = []; V = []; PP = []; XX = []; XOPT = [];

% Параметры времени системы и датчиков

% T0 - начальный момент времени

% T  - время, в течение которого производятся наблюдения

% nt - число точек, в которых вычисляется значения меандра

% dt - шаг меандра

T0 = 0; T = 60; Tfin = T0 + T;

nt = 400; dt = T / nt; TT = [T0 : dt : Tfin - dt];

% Параметры времени для процесса вычисления

% np - число точек, в которых осуществляются вычисления

% dtp - шаг процесса вычисления

np = 800; dtp = T / np; TP = [T0 : dtp : Tfin - dtp];

% n - число случайных точек, из которых происходит выбор значений меандра

n = 1000;

MQ = [s1Q * sqrt(1 - rQ^2), s1Q * rQ; 0, s2Q]; MQ = sqrt(MQ);

MR = [s1R * sqrt(1 - rR^2), s1R * rQ; 0, s2R]; MR=sqrt(MR);

% Датчики

% W - исходный шум в системе

% rnd1 = [362436069; 209195]; randn('state', rnd1);

% Необходимо удалить знак комментария выше,

% если требуется одна реализация процесса

W1 = randn(2, n); W2 = smooth(W1);

W = MQ / sqrt(dt) * W2;

% V - исходный шум наблюдений

% rnd2 = [362436069; 521288629]; randn('state', rnd2);

% Необходимо удалить знак комментария выше,

% если требуется одна реализация процесса

V1 = randn(2, n); V2 = smooth(V1);

V = MR / sqrt(dt) * V2;

% options - параметры точности вычисления функции ode45

options = odeset('RelTol', 1e-4, 'AbsTol', 1e-4);

RR = W; for I = 1:np, MM = [MM, meandr(TP(i))]; end,

plot(TP, MM(1, :), 'g', TP , MM(2, :), 'r'), grid on,

title('Меандровое возмущение в системе (обе компоненты)');pause;

[C, XX] = ode45('htnoise', TP , x0, options); XX = XX';

XX = real(XX);

% Здесь происходит «урезание» отрицательных значений решений системы

% (при исследовании другой системы можно удалить)

for i = 1:np,

 if XX(1, i) <= 0, XX(1, i) = x1eps; end,

 if XX(2, i) <= 0, XX(2, i) = 0; end,

end,

% XX0 - случайное начальное значение реализации процесса

XX0 = x0 + [s1P, rP; rP, s2P] * randn(2, 1);

if XX0(1) <= 0, XX0(1) = x1eps; end,

if XX0(2) <= 0, XX0(2) = 0; end,

% xp0 - начальное значение, необходимое для решения уравнения Риккати,

% совмещенного с решением уравнения фильтрации

xp0 = [s1P; rP; s2P; XX0(1); XX0(2)];

RR = V; [CC, XPOPT] = ode45('SolveSystem', TP, xp0, options);

XPOPT = XPOPT'; CC = CC';

PP = XPOPT(1:3, :); XOPT = XPOPT(4:5, :);

% XPOPT - решение совмещенной системы

% PP - решение матричного уравнения Риккати

% XOPT - решение уравнения фильтрации или оптимальная оценка

% решения системы по наблюдениям

% ZZ - наблюдения системы с шумом

for i = 1:np,

 ZZ = [ZZ, getZ(TP(i))];

 if XOPT(1, i) <= 0, XOPT(1, i) = x1eps; end,

 if XOPT(2, i) <= 0, XOPT(2, i) = 0; end,

end,

plot(CC, ZZ(1, :), 'g', CC, ZZ(2, :), 'c', C, XX(1, :), 'b', C, XX(2, :), 'm'),

grid on,

title('График исходных решений и наблюдений системы');

legend('Зайцы - Наблюдени', 'Волки - Наблюдение', 'Зайцы - Решение', ...

      'Волки - Решение'); pause;

plot(CC, ZZ(1, :), 'g', CC, XX(1, :), 'b', CC, XOPT(1, :), 'r'), grid on,

title('Сравнение исходных наблюдений с оценкой по одной компоненте');

legend('Наблюдение','Решение','Оценки'); pause,

plot(XX(1, :), XX(2, :), 'b', XOPT(1, :), XOPT(2, :), 'r'), grid on;

title('Фазовый портрет решений и оценок');

legend('Решение', 'Оценка'); pause;

plot(CC, sqrt(PP(1, :)), 'g', CC, sqrt(PP(3, :)), 'r'), grid on,

title('Изменение диагональных элементов матрицы ошибки фильтрации');

legend('Зайцы', 'Волки'); pause;

close

(2)  Функции-параметры

% Файл "SolveSystem.m"

% Функция, совмещающая решение матричного уравнения Риккати и

% уравнений нелинейной фильтрации

function xp1 = SolveSystem(tt, XP);

global Q R G H x1eps;

% Здесь происходит замена отрицательных значений параметров на

% минимальные (при необходимости можно удалить)

if XP(1) <= 0, XP(1) = 0; end,

if XP(3) <= 0, XP(3) = 0; end,

if XP(4) <= 0, XP(4) = x1eps; end,

if XP(5) <= 0, XP(5) = 0; end,

% Расчленение вектора для дальнейшего использования

P(1, 1) = XP(1); P(1, 2) = XP(2); P(2, 1) = XP(2); P(2, 2) = XP(3);

xx = [XP(4); XP(5)];

A = GetA(tt, xx); Z = getZ(tt);

P1 = G * Q * G' + P * A' + A * P – P * H' * inv(R) * H * P;

x1 = ht(tt, xx) + P * H' * inv(R) * (Z – H * xx);

% Сборка вектора решения

xp1 = [P1(1, 1); P1(1, 2); P1(2, 2); x1(1); x1(2)];

% Файл "htnoise.m"

% Уравнение Холлинга-Тэннера с шумовым воздействием

function z = htnoise(tt, x)

global G,

z = ht(tt, x) + G * meandr(tt);

(3)  Подфункции

% Файл "GetA.m"

% Функция, возвращающая Якобиан уравнения Холлинга-Тэннера

function A = GetA(tt, x);

global r s j w k d x1eps,

if x(1) <= 0, x(1) = x1eps; end,

if x(2) <= 0, x(2) = 0; end,

df1x1 = r * (1 – 2 * x(1) / k) – d * w * x(2) / (d + x(1))^2;

df1x2 =-w * x(1) / (d + x(1));

df2x1 = s * j * x(2)^2 / x(1)^2;

df2x2 = s * (1 – 2 * j * x(2) / x(1));

A = [df1x1, df1x2; df2x1, df2x2];

% Файл "GetZ.m"

% Функция получения наблюдения в момент времени tt

function Z = getZ(tt)

global H,

Z = H * getX(tt) + meandr(tt);% Данные берутся из массива V

if z1(1) <= 0, z1(1) = x1eps; end,

if z1(2) <= 0, z1(2) = 0; end,

% Файл "meandr.m"

% Функция меандра, выбирающего определенные

% случайные значения из входного массива RR

function z = meandr(tt, type)

global T0 T nt RR

num = fix(1 + (tt - T0) /T * (nt - 1));

z = RR(:, num);

% Файл "Smooth.m"

% Функция предварительного «сглаживания» шумовых составляющих

% путем усреднения группы соседних значений

function ZZ = smooth(UW)

global n nt,

GG = []; num = n / nt;

count0 = 1; count1 = fix(num); rest = num - fix(num);

while count1 <= n   

 ss1 = 0; ss2 = 0;   

 for I = count0:count1,

   ss1 = ss1 + UW(I * 2 - 1); ss2 = ss2 + UW(I * 2);

 end,

 GG = [GG, [ss1 / (count1 - count0 + 1); ss2 / (count1 - count0 + 1)]];   

 count0 = count1; count1 = count0 + fix(num + rest);   

 rest = num + rest - fix(num + rest);   

end   

ZZ = GG;

% Файл "ht.m"

% Уравнение Холлинга-Тэннера

function z = ht(t, x)

global r s j w k d x1eps,

if x(1) <= 0, x(1) = x1eps; end,

if x(2) <= 0, x(2) = 0; end,

z = [r * (1 - x(1) / k) * x(1) – w * x(1) * x(2) / (d + x(1)); ...

    s * (1 – j * x(2) / x(1)) * x(2)];


mcc -m

      версия           С-файл

 m-кода на Си  упаковщика  

m-файл

функционального типа

компилятор с Си

Объектные файлы

Компоновщик

Автономное

приложение на Си

математическая библиотека

MATLAB m-file

математическая библиотека встроенных функций MATLAB

ANSI C библиотека

библиотека обслуживающих функций MATLAB

MATLAB C/C++ графическая библиотека

Эту часть выполняет mbuild

библиотека MATLAB API

Рис. 1.1. Разработка типичного автономного приложения

Рис. 1.3. Зависимость времени выполнения

теста от числа итераций цикла

ис. 2.1. Пути для подключения заголовочных файлов  

Рис. 2.2. Пути для подключения

библиотек

Рис. 3.1. Форма рабочего примера.

Исследуемый объект

или система

  

Входной шум

Устройство наблюдения

x(t)

Помеха измерений

z(t)

Вектор состояния

Наблюдаемый вектор состояния

Рис. 4.1. Модель исследования

v(t)

x(t)

Оценка

вектора состояния

Нелинейный фильтр

w(t)

Рис. 4.3. Меандровое возмущение в модели популяции при большом  шаге меандра   и            малом шаге наблюдений (nt = 200, np = 900)

Рис. 4.4. Меандровое возмущение в модели популяции при большом  шаге меандра   и          несколько большим шагом наблюдений (nt = 200, np = 300)

Рис. 4.2. Меандровое возмущение в модели популяции (зеленым зайцы, красным –            волки, так всегда далее) при малом   шаге  меандра   и с малым  шагом            наблюдений (nt = 700, np = 900, см. практическую сторону вопроса)

Рис. 4.5. Моделируемые состояния и наблюдения популяции в нелинейном случае при меандровых воздействиях на систему

Рис. 4.6. Моделируемые состояния и наблюдения   популяции в линейном случае при меандровых воздействиях на систему

Рис. 4.7. Результат процесса фильтрации (наглядное представление оценки) в случае нелинейной системы

Рис. 4.8. Результат процесса фильтрации (наглядное представление оценки) в случае линейной системы

.

Рис. 2.9. Фазовый портрет исходных и фильтрованных данных (нелинейный вариант).

Рис. 4.10. Фазовый портрет исходных и фильтрованных данных (линейный вариант).

Рис. 4.11. Поведение диагональных элементов матрицы P в каждый момент времени t в нелинейном случае

Рис. 4.12. Дисперсия ошибки фильтрации  в каждый момент времени t в линейном случае (диагональные элементы корреляционной матрицы ошибки фильтрации P)  

Рис. 4.13. Расчетная дисперсия ошибки фильтрации на фоне фактической с доверительным интервалом

Рис. 4.14. Фактическая дисперсия ошибки фильтрации на фоне усредненной расчетной с доверительным интервалом (P-,P+)

Рис. 5.1. Рабочая область приложения

Рис. 5.2. Закладка параметров системы

Рис. 5.3. Закладка параметров шума

Рис. 5.4. Закладка установок времени

Рис. 5.5. Закладка, управляющая выводом графики

1 Это текстовые файлы, содержащие программу или функцию, написанную на языке MATLAB. Обычно имеют расширение ".m".

2 Программа на языке, встроенном в систему MATLAB.

3 Данные переменные характеризуют число входных и выходных параметров.

4 Здесь и далее этим символом будет обозначаться пропущенный код, идентичный вышеизложенному коду этого же файла.

5 Здесь и далее данный символ будет означать удаленный участок кода, необходимость в котором для понимания технологии минимальна.


 

А также другие работы, которые могут Вас заинтересовать

85048. Аварии на радиационно опасных объектах и их возможные последствия. Обеспечение радиационной безопасности населения 32.01 KB
  Аварии на радиационно опасных объектах и их возможные последствия. Обеспечение радиационной безопасности населения. Познакомить учащихся с понятиями радиационно опасный объект ионизирующее излучение радиоактивное загрязнение окружающей среды. Дать общее представление о последствиях аварий на радиационно опасных объектах и о влиянии ионизирующего излучения на организм человека.
85049. Аварии на химически опасных объектах Тамбовской области и их возможные последствия. Обеспечение химической защиты населения 33.32 KB
  Обеспечение химической защиты населения. Познакомить учащихся с общими мероприятиями провод