Скриптовые вычисления

Программа для работы с данными из комплекса автоматизации физического эксперимента позволяет быстро в одно нажатие производить стандартную обработку данных: объединение нескольких графиков, сглаживание, построение общей линейной шкалы, разделение на чётную и нечетную части. Тем не менее, хотя стандартная обработка и содержит много параметров настройки, она не может охватить все случаи из реальной практики экспериментатора. Для того чтобы придать обработке данных гибкость, сравнимую с Origin, и в то же время не потерять в скорости мгновенной обработки, экспериментатору предлагаются скриптовые вычисления. Программа обработки записывается на лаконичном и интуитивно понятном языке скриптов, запоминается и далее может быть выполнена за одну команду. Одновременно поддерживается пять автивных скриптов. При вводе текста синтаксис скриптов выделяется цветом и стилями текста. Обеспечивается контроль ошибок с позиционированием на место ошибки и сообщениями на русском языке. Мощный язык скриптов поддерживает все основные конструкции языка С# и способен справиться с самыми сложными задачами.

Какие данные бывают

Давайте перечислим все данные, с которыми работает наша программа. Их всего несколько типов:

Слоты обработки

Панель слотов данных

Панель слотов данных позволяет управлять видимостью исходных экспериментальных данных (в слоте exper) и слотов обработки s0 - s9 в двух соседних графиках в нижней части экрана. Переключение видимости происходит при нажатии на соответсвующую кнопку панели слотов, при этом сама кнопка выделяется оранжевым фоном. Цвет надписи на кнопке соответствует цвету соответствующего графика.

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

Перетаскивание на псевдослот с именем File приводит к записи данных эксперимента или обработки в текстовый файл с расширением XYZ после диалога. Перетаскивание из псевдослота File в слот exper производит импорт внешнего файла с данными.

Окна скриптов

Последовательность действий, которую программа может совержить с данными, можно записать в текстовом виде. Этот текст называется скриптом. Программа одновременно поддерживает пять активных скриптов, которые выполнятся в одно нажатие. Эти скрипты находятся в пяти текстовых окнах с закладками и именами Scr1-Scr5 в верхней правой части окна программы.

Текст скрипта можно набирать с клавиатуры как в обычном редакторе текста, поддерживается выделение цветом ключевых слов, комментариев и основных команд. Программа сохраняет изменения активных скриптов при завершении работы и восстанавливает их при старте, поэтому пять активных скриптов всегда поддерживаются готовыми к работе. Команда меню Скрипт | Восстановить восстанавливает последнее сохранённое состояние активного скрипта, если сделанные изменения были неудачными. Команда Скрипт | Сохранить или комбинация клавиш <Ctrl>+S запоминает текущие изменения активного скрипта, так что в будущем будет восстановливать именно это состояние. Кроме этого постоянного места сохранения, каждый активный скрипт можно сохранить на диске или восстановить из файла с расширением EDVscript с помощью команд меню Скрипт | Сохранить как... и Скрипт | Открыть...

Комбинация клавиш <Ctrl>+S сохраняет скрипт только во время его редактирования, то есть когда курсор находится в окне скрипта. Если фокус находится на списке экспериментов, то это сочетание клавиш приведёт к сохранению выделенного эксперимента из базы данных.

Запуск скрипта

Выполнить скрипт можно с помощью команд меню, но проще всего пользоваться сочетанием клавиш <Ctrl>+1 - <Ctrl>+5 для выполнения скриптов в окнах Scr1-Scr5 соответственно. Перед запуском программа проверяет правильность написания скрипта и при наличии ошибок курсор ставится на место возникновения ошибки и в строке статуса выводится описание ошибки на русском языке, при этом никаких других действий не производится. Пользователь может легко настроить под себя пять произвольных программ, или действий с данными и пользоваться ими в своей работе.

Можно также выполнить только часть скрипта. Для этого необходимо выделить часть скрипта и нажать клавиши <Ctrl>+R или выполнить соответствующую команду из меню Скрипт.

Основы синтаксиса скрипта

Язык скрипта поддерживает все основные конструкции языка C#, поэтому знание этого языка окажет сильную поддержку пользователю.

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

Приведём ниже основные конструкции синтаксиса языка. Скрипт состоит из последовательности операторов, каждый из которых завершается точкой с запятой ;. Расположение операторов по строкам произвольное, можно записывать несколько операторов на одной строке или один оператор на нескольких строках.

Комментарий может располагаться в любом месте скрипта начиная с двух косых и до конца текущей строки: // Это комментарий

Структура данных

Главная задача скрипта - обработка экспериментальных данных. Рассмотрим, как скрипт может управлять данными, которые есть в нашей программе. Основная единица данных - это слот данных. Всего имеется слот для неупорядоченных экспериментальных точек exper и 10 слотов обработанных данных на общей равномерной шкале: s0, s1, s2, s3, s4, s5, s6, s7, s8, s9 . Программа поддерживает обновременную обработку двух потоков данных, поэтому каждый слот состоит из двух столбцов, обозначаемых y и z. Эти столбцы отображаются в двух графических областях в нижней части окна, режим отображения управляется кнопками на панели слотов данных. Столбец абсцисс общих для слотов обработки обозначается x.

Операции можно производить над всем слотом (то есть над обоими стобцами одновременно), для этого используется его имя (например exper или s5), либо над отдельным столцбом, при этом можно использовать либо синтаксис с точкой, либо имена столцов слота (например слот s5 содержит столбцы s5.y и s5.z или то же самое - столбцы y5 и z5). Для доступа к слотам обработки можно также использовать синтаксис с индексированием s[i], где i - целая переменная или целое число от 0 до 9.

Если при записи новых данных в слот или столбец пользоваться короткой формой записи (как y1), то автоматически произойдёт пересылка данных на график и этот график становится видимым. При использовании любого другого формата записи (как s1.y или s[1].y) оператора присваивания графики автоматически не обновляются. Обновление данных соответствующего графика можно сделать командой Redraw, а показать слот на графике командой Show.

Общее правило обработки состоит в том, чтобы все слоты обработки имели единую шкалу равномерную шкалу абсцисс. Тем не менее, если пользователь в процессе обработки поменял шкалу x функцией ScaleX разные слоты могут содержать данные, принадлежащие разным шкалам. Каждый столбец в этом случае помнит "свою" шкалу абсцисс, для доступа к которой из скрипта можно воспользоваться синтаксисом с точкой y5.x или z5.x, при этом просто x будет обозначать новую общую шкалу абсцисс, принятую в настоящее время.

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

Кроме предопределённых стобцов обработки пользователь может пользоваться в скрипте временными переменными и столбцами, давая им собственные имена. Перед первым присвоением значения новой переменной следует написать ключевое слово var:

var C=3.8;
var YY=y0.Deriv(); // Без отображения
y1=YY*C; // Будет показан на графике
В приведённом скрипте определяется переменная C, которой присваивается значение 3.8. Создаётся также новый временный столбец с именем YY, которому присваивается значение производной от столбца y0. Поскольку столбец YY временный, он никогда не отображается на графике. Наконец, после домножения столбца YY на определённое ранее число результат присваивается предопределённому столбцу y1, при этом точки автоматически прорисовываются на графике.

Все предопределенные данные скрипта выделяются при написании красным цветом, ключевые слова синим, а функции выделяются жирным шрифтом. Коментарий отображается зелёным курсивом.

Сохранение данных в файл

Самый простой способ сохранить данные из слота обработки в текстовый файл - это перетащить мышкой из выбранного слота на псевдослот File. При этом создаётся трёхколоночный файл, совместимый с программой Origin, состоящий из общего столбца аргументов Х и двух столбцов Y для левого и правого графиков соответствующего слота. Для автоматического сохранения данных слота из скрипта используется функция Save:

y0.Save("FileName", "X", "Y", "Z");
y0.Save("FileName");
y0.Save("X", "Y", "Z");
y0.Save();

Пользователь может явно указать имя и путь выходного файла и заголовки столбцов, как это показано в первой строчке примера. Если имена столбцов не указаны, программа присваивает им имена по умолчанию "X", "Y" и "Z", поэтому вторая строчка примера при выполнении идентична первой. Если не указано имя выходного файла, программа спрашивает его у пользователя в диалоге, как происходит при выполнении двух последних строчек примера.

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

y0.Append("FileName");
y0.Append();

Оператор присваивания

Копирование данных между слотами или столбцами совершается оператором присваивания. Например, для копирования данных из слота s1 в слот s2 нужно написать s2=s1; или два оператора отдельно для левого и правого графиков y2=y1; z2=z1;.

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

Поскольку в слоте exper находятся неупорядоченные экспериментальные данные, то при копировании из него в другой слот, где данные находятся на равномерной шкале, происходит минимальное сглаживание. Другими словами, присваивание s5=exper; эквивалентно s5=exper.Smooth(-1);. Подобный эффект возникает и при перетаскивании со слота exper в слоты обработки на панели слотов.

При обратном присваивании exper=s5; обработанные данные из слота s5 замещают собой текущие экспериментальные точки в слоте exper, как будто бы они были загружены из базы данных, при этом старые экспериментальные точки стираются. Чтобы сохранить имеющиеся экспериментальнгые точки и добавить в конец новые можно воспользоваться оператором exper+=s5;. При этом каждая новая порция экспериментальных точек отобращается на графиках другим цветом.

Обратите внимание, что изменение порядка сложения в этом случае приводит к кардинально разным результатам, так оператор exper=(exper+s5)+s6; дописывает к экспериментальным точкам две новые кривые из слотов s5 и s6, в то время как оператор exper=exper+(s5+s6); дописывает к каждому графику одну кривую, являющуюся суммой значений из двух слотов на общей шкале Х.

Действия с данными и цепочки

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

 s2 = s2.Deriv2(); // Минус вторая производная 

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

 s2 = s2.Smooth(0); // Сглаживание результата

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

 s2 = s2.Deriv2().Smooth(0); // Компактная запись

Общая равномерная шкала X

Все обработанные данные в слотах s1-s9 имеют равномерную шкалу аргументов x. Единственный слот, в котором допускаются неупорядоченные экспериментальные данные - это слот exper. Единственная операция, допустимая для данных в слоте exper - это совмещенная операция сглаживания и построения равномерной шкалы x. То есть первое правило обработки - равномерная шкала должна быть. Есть два подхода к определению параметров шкалы.

Стандартная обработка использует параметры программы из диалога для определения равномерной шкалы. При каждом вызове стандартной обработки создаётся новая равномерная шкала. Это сделано для удобства тех пользователей, которые редко пользуются скритпами, а вместо них используют кнопки и команды меню. Параметры шкалы из диалога имеют приоритет над параметрами из скрипта, поэтому результат стандартной обработки будет всегда одним и тем же независимо от скриптов, которые при этом выполнялись.

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

ScaleX(-10.0, 10.0, 21);
Полная форма оператора ScaleX задаёт начало, конец и количество точек в равномерной шкале. Пример определяет шкалу из 21 точки с шагом 1 от Х=-10 до Х=10.

ScaleX(200);
s0 = exper.Smooth(2);
Сокращенная форма оператора ScaleX используется, когда нужно, чтобы начало и конец равномерной шкалы подстроились под фактические экспериментальные точки. При многократных запусках этого скрипта в слоте s0 будет сформированы разные шкалы из 200 точек, в зависимости от фактических границ экспериментальных данных в слоте exper.

Количество точек в общей равномерной шкале тесно связано со степенью сглаживания оператором Smooth: чем меньше количество точек, тем больше степень сглаживания при одном и том же значении параметра сглаживания. Для повышения быстродействия сглаживания рекомендуется оставлять параметр сглаживания на минимальном значении (например 0) и повышать степень сглаживания уменьшением количества точек в шкале.

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

Сокращенная форма функции ScaleX не может сразу сформировать новую равномерную шкалу, поскольку ещё не значет фактического диапазона арсцисс данных. Шкала x при этом формируется во время выполнения Smooth. Поэтому в промежутке кода скрипта между сокращенной функцией ScaleX и функцией Smooth общая шкала x останется старой.

Сглаживание на равномерной шкале

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

s1 = s0.Smooth(0); // Сглаживание всего слота
Единственный параметр функции Smooth должен лежать в диапазоне от -1 до 15 и определяет степень сглаживания от минимального до практически выпрямления в линию. В примере слот s0 сглаживается с параметром 0 и результат записывается в слот s1.

z1 = z1.Smooth(2); // Сглаживание только правого графика
Сглаживать можно отдельно левый или правый графики слота, например этот скрипт дополнительно сглаживает правую часть слота s1.

Функция Detrend выделяет плавную часть графика. Единственный параметр функции имеет тот же смысл, что и в функции сглаживания. Фактически результат выполнения Detrend - это разность между исходной и сглаженной кривой с тем же параметром сглаживания:

y1 = y0.Smooth(1) + y0.Detrend(1);
В результате выполнения приведённого скрипта график y1 будет совпадать с графиком y0.

Функцию Detrend можно объединять в цепочку с функцией Smooth для выделения осцилляций из экспериментальных данных, при этом параметр детрендинга должен быть больше, чем у сглаживания, чтобы результат не обратился в ноль. Скрипт приведённый ниже обеспечивает функциональность похожую на то, что делает программа SinFinder:

y1 = y0.Smooth(0).Detrend(2);

Функция Smooth использует регрессионный сплайн со штрафной функцией (РСШФ) - нелокальный алгоритм, позволяющий восстанавливать пропущенные отрезки данных, образуя общую плавную кривую без особенностей. Более быстрый альтернативный алгоритм использует фиттинг локальным полиномом для получения каждой точки выходной шкалы. Для его вызова из скрипта следует пользоваться функцией Sigma(sigma,power), где параметр sigma задаёт характерное расстояние по оси Х от выходной точки, на котором учитываются экспериментальные точки, а параметр power задаёт степень полинома (по умолчанию парабола, power=2). Положительные значения sigma задаются в единицах шкалы Х, а отрицательные определяют долю от всего заданного диапазона выходной шкалы Х:

ScaleX(0,100,51); // 51 точка шкалы Х от 0 до 100 с шагом 2
s0 = exper.Sigma(-0.02); // Сглаживание эксперимента параболой с полушириной 2
y1 = y0.Sigma(5.0, 1);   // Линейное сглаживание с полушириной 5

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

y0 -= y0.Smooth(0).Trend();  // Вычитание фона без ошибок на границах

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

Функция Trend() имеет необязательный целый аргумент, который определяет режим постобработки полученных точек перегиба:

Trend(0) Никакой постобработки не проводится, на выходе просто точки перегиба.

Trend(1) (Режим по умолчанию) На выходе - среднее между сплайнами по четным и нечетным точкам перегиба, причём крайние точки перегиба принудительно включаются в оба сплайна.

Trend(2) На выходе - середины отрезков, соединяющих соседние точки перегиба. В этом случае количество точек на выходе на единицу меньше.

Определение максимумов и минимумов плавной кривой

Для автоматизации анализа пиков графиков служат функции Maximums и Minimums, которые возвращают набор точек соответственно локальных максимумов или минимумов заданной кривой. Для правильной работы график необходимо предварительно сгладить и устранить плавный фон (смотри раздел выше). Полученные точки можно сразу сохранить в файл с указанием заголовков столбцов. Обратите внимание, что точки максимумов и минимумов не лежат на общей равномерной шкале Х.

Предположим, что в слоте экспериментальных данных exper находятся графики некой осциллирующей кривой и температуры. Первым действием сглаживаем обе кривые в слот s0, а из графика y0 вычитаем плавный фон. Далее в примере вычисляются максимумы осциллирующей кривой y9, для абсциссы каждого максимума y9.x[i] вычисляется интерполяция по графику температуры z0 (позиции максимумов лежат между узлов шкалы сглаженных данных) с записью температуры максимума в z9, а затем все три столбца s9 записываются в файл на диске.

s0 = exper.Smooth(0);  y0 -= y0.Trend();
y9 = y0.Maximums();    z9 = y9.iValue(i=>z0.AtX(y9.x[i]));
s9.Save("Position", "Amplitude", "Temperature"); 

Функции определения экстремумов Maximums() и Minimums(), вызванные без аргументов, вычисляют значения экстремумов в математическом смысле - как место, где значение максимально или минимально. В физике часто приходится анализировать кривые, которые можно представить как произведение осциллятора с единичной амплитудой и самой амплитуды осциллятора, зависящей от Х. В этом случае нужно строить огибающую графика, и места экстремумов - это точки, в которых эта огибающая касается исходного графика. Этот режим расчёта экстремумов реализуется при вызове функций с параметром true, например y0.Maximums(true).

Формула для значений графика

Функция iValue служит для явного задания значений аргументов в зависимости от номера строки i (по аналогии с командой Set Colunm Values из программы Origin). В формуле можно использовать скобки, действия арифметики и следующие встроенные функции:
exp(y)   экспонента
log(y)   десятичный логарифм
ln(y)   натуральный логарифм
sqrt(y)   квадратный корень
squ(y)   квадрат
y^N   y в степени N

y2 = iValue(i => sqrt(i*i + 5*5));
Значения левого графика второго слота задаётся по номеру точки i в общей равномерной шкале.

z2 = iValue(i => sqrt(squ(x[i] + 5*5)));
Значения правого графика второго слота вычисляются с помощью значений общей равномерной шкалы.

y2 = iValue(i => log(y0[i]) + y1[i] - 1003.4);
В формуле используются значения из левых графиков нулевого и первого слотов.

Хотя функция iValue удобна для задания значений графика, иногда требуется выполнить другие действия для всех точек графика. Для организации цикла по всем точкам общей равномерной шкалы можно использовать синтаксис подобный принятому в языке C#

y2 = iValue(i => i*i); // реализация y2=i^2 через функцию iValue 
for(int i=0; i<points; ++i) { y2[i]=i*i; } // реализация через цикл for 
В приведённом примере левому графика второго слота присвоивается значение квадрата номера точки двумя способами: через функцию iValue и через организацию цикла по всем точкам общей шкалы. Целая константа points определяется программой автоматически и содержит текущее количество точек в общей равномерной шкале. Вместо повторения присваивания элементов y2 в цикле for можно производить любые действия над данными любых слотов.

Редактирование абсцисс графиков

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

Как правило слоты обработки содержат данные на единой равномерной шкале x. В особых случаях функция iX позволяет заменить абсциссы столбца точно также, как функция iValue меняет его ординаты. Синтаксис функции такой же - по индексу общей шкалы определяется новое значение абсциссы. Отредактированный столбец следует присваивать слоту exper, поскольку теперь он содержит несортированные данные и возможно не на равномерной шкале.

ScaleX(0.0, 100.0, 101);
...
y1 = y0.iX(i=>x[i]+50.0);
Rescale();
В этом примере график y1 рисуется сдвинутым вправо на 50 от графика y0. Изменение масштаба осей графиков необходимо, чтобы y1 был виден на графике полностью. Использовать y1 после этого следует с осторожностью, например, функция y1.Smooth(0) будет сглаживать данные, но результат будет получен на абсциссах старой шкалы, которая не менялась, поэтому в начальной области результата сглаживания 0<X<50 будет прямая линия экстраполяции, а последняя половина точек y1 с абсциссами 100<X<150 вообще не попадёт в область результата.

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

exper.FX(x => 2*x);

Функция FX редактирует только загруженные на локальный компьютер данные, экспериментальные точки в самой базе данных всегда остаются неизменными. Для восстановления точек из базы данных загрузите их снова в слот exper.

Выделение части точек графика

Иногда перед записью данных в файл требуется выделить из графика только интересующую область. Это можно сделать с помощью функции iWhere

. Например, обрежем график y0 так, чтобы оставить в нём только 10 первых точек:
y0 = y0.iWhere(i => i<10);

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

Управление видимостью графиков

Если значение присваивается всему слоту, он автоматически становится видимым и отображается в виде графиков. Если значения присваиваются отдельно левому и/или правому графику слота, можно сделать слот видимым командой Show или спрятать командой Hide. Можно также управлять видимостью слотов вручную с помощью кнопок на панели слотов данных . Временные столцы, созданные в самом скрипте, не отображаются на графиках.

s[1]=s0; s1.Redraw(); s1.Show();
Когда необходимо показать результат присваивания на графике, применяются команды Redraw для обновления графика, связанного со слотом s1, а затем точки графика делаются видимыми командой Show. Всю эту строку можно было бы записать короче как s1=s0;, поскольку обновление графиков происходит автоматически при сокращенной форме записи в левой части оператора присваивания.

s1=s0; s1.Hide();
В этом примере команда Hide прячет данные из первого слота, которые в противном случае стали бы видимыми после оператора присваивания, потому что s1 записан в сокращенной форме. Вместо этой строки можно было бы написать s[1]=s0;

Можно управлять видимостью раздельно левого и правого графиков, например следующие две строки примера идентичны:

s1.Hide();
y1.Hide();  z1.Hide();

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

Отметка выделения снимается с кнопки соответствующего слота, если не отображается ни один график слота, например после s1.Hide(); или если в графиках нет данных.

При нажатии на кнопку слота прячутся или отображаются сразу и левый и правый графики, если они есть.

С видимостью данных из разных слотов тесно связана команда автопоиска границ отображения данных Rescale. При выполнении этой команды происходит автоматический поиск границ отображения по всем видимым точкам левого и правого графиков. Если поменять видимость данных из разных слотов, а затем повторить автопоиск, результат может отличаться. Команда может быть подана нажатием на кнопку , либо сочетанием клавиш <Ctrl>+↑, либо командой скрипта:

exper.Hide(); Rescale();
Команда автопоика границ данных подстраивает общую область видимости графиков после того, как исходные экспериментальные точки были спрятаны.

Нормировка и простые операции

Арифметические операции с отдельными графиками и числовыми константами можно записывать в скрипте напрямую без использования функции iValue и без организации цикла for:

y3 = (y2+y1)/2;
Запись в y3 cреднего от двух графиков y1 и y2.

y3 = y3 + 100.0;
Добавление 100 к графику y3. В сокращенной форме y3+=100.0;

Обратите внимание на сокращенную форму записи арифметических действий: например запись y/=5; это то же самое что присваивание y=y/5;

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

y3 /= y3.AtX(0);
График делится на его значение в нуле.

y3 -= y3[0];
Из графика вычитается значение его первой точки.

y3 /= y3.Integral[points-1];
Нормировка на единичную площадь под графиком по области определения равномерной шкалы. Функция Integral вычисляет кривую интеграла от графика, для нормировки нам нужно последнее значение на этой кривой - это последняя точка с индексом points-1. Константа points определяется программой автоматически и содержит текущее количество точек в общей равномерной шкале.

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

Функцией AtX можно пользоваться как для интерполяции внутри общей равномерной шкалы, так и для экстраполяции за её пределами, при этом экстраполяция происходит линией.

Разделение на четную и нечетную части

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

Скрипт предоставляет пользователю ещё большую гибкость при выполнении этой операции:

y1 = y0.Even(); // Выделение четной части из графика

y2 = y0.Odd(); // Выделение нечетной части из графика

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

Если вы хотите, чтобы равномерная шкала содержала точку в нулевом поле, количество точек в ней должно быть нечетным, например ScaleX(-10.0, 10.0, 201);

Интегрирование и дифференцирование

Для дифференцирования и интегрирования используются следующие функции:
Deriv()  Производная
Deriv2()  Минус вторая производная
Integral()  Интеграл

Эти функции применимы только к отдельным столбцам слотов обработки (y или z), но не к слотам целиком.

y1 = y0.Deriv();
y0 = y1.Integral();

Быстрое преобразование Фурье

Для частотного анализа сигнала из графика y0 можно провести быстрое преобразование Фурье (или FFT - Fast Fourier Transform) и получить зависимость амплитуды осцилляций от частоты на графике y4 как показано в примере ниже:

var F = y0.FFT();
y4 = F.abs; 

Результатом преобразования является массив комплексных чисел. После проведения преобразования можно получить графики следующих параметров осцилляций в зависимости от частоты:
F.abs  Амплитуда
F.phase  Фаза
F.real  Вещественная часть
F.imag  Мнимая часть

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

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

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

Поскольку искодный график для анализа спектра не является периодической функцией, программа автоматически домножает исходный график на плавную кривую с минимумом гармоник, спадающую к краям, то есть применяет окно Блэкмана. По этой причине осцилляции около границ графика (по Х) практически не заметны после FFT.

Нелинейный фиттинг

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

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

var F = new FitFunc((x, p) => p[0]+p[1]*exp(p[2]*x), 0.0, 2.0, -0.3);
F.FitData(y0);
y1 = F;
F.ShowP();

Обратите внимание, что количество начальных значений параметров должно в точности совпадать с количеством использованных параметров в определении функции и что индексация параметров начинается с 0, в нашем случае это три параметра p[0], p[1] и p[2]. После подгона числовые значения этих параметров F.p[0], F.p[1] и F.p[2] можно использовать в скрипте в дальнейшей работе. Можно также вывести на экран сообщение с подогнанными значениями параметров вызовом функции ShowP(). Ниже мы рассмотрим рекомендации, что делать, если фиттинг не сходится и как улучшить его скорость сходимости и точность.

Без выбросов

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

Ближе к ответу

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

Гладкая экспериментральная кривая

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

Определение производных

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

var F = new FitFunc((x, p) => p[0]+p[1]*exp(p[2]*x), 0.0, 2.0, -0.3);
F.gradient[0] = (x, p) => 1.0;
F.gradient[1] = (x, p) => exp(p[2]*x);
F.gradient[2] = (x, p) => x*p[1]*exp(p[2]*x);

Масштабирование задачи

Алгоритм ничего не знает о структуре аналитической функции, поэтому предполагает, что масштаб изменения всех параметров порядка единицы. Если это не так, необходимо сообщить алгоритму о фактических масштабах, передавая их в параметрах функции Scale, в противном случае сходимость может драматически упасть. Обратите внимание, что количество параметров функции Scale должно быть в точности равно количеству параметров в аналитической функции. Для нашего примера это три параметра, мы считаем, что масштаб изменения показателя экспоненты должен быть на порядок меньше остальных параметров:

F.Scale(1.0, 1.0, 0.1); 

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

Границы изменения параметров

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

F.LowLimits(-INF, -10.0, -INF);
F.HighLimits(INF, 10.0, 0.0);

Увеличение точности

По умолчанию абсолютная точность определения всех параметров принимается 0.0001. Если этой точности недостаточно, можно явно указать другую точность при проведении фиттинга, например:

F.FitData(y0, 1e-8);

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

Ограничение числа итераций

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

F.FitData(y0, 1e-4, 100);
Ограничение количества итераций бывает полезно при поиске узкого локального минимума, когда приходится вручную подбирать начальные значения параметров.

Фиттинг серии данных

Если один и тот же фиттинг нужно применять много раз для серии из похожих графиков, можно определить функцию фиттинга один раз, а потом только подгонять по этой функции разные данные. В этом случае в качестве начальных значений параметров для последующих данных используются параметры результата фиттинга от предыдущих данных. Поскольку данные отличаются не сильно, это будет хорошим приближением и скорость фиттинга возрастёт. Некоторые параметры можно возвращать перед фиттингом в общее исходное значение прямым присвоением (например F.p[0]=0.0). В примере ниже определяются предельные значения в бесконечности Limit0, Limit1 и Limit2 для трёх похожих затухающих экспонент из графиков y0, y1 и y2:

var F = new FitFunc((x, p) => p[0]+p[1]*exp(p[2]*x), 0.0, 2.0, -0.3);
F.FitData(y0);  var Limit0=F.p[0];
F.FitData(y1);  var Limit1=F.p[0];
F.FitData(y2);  var Limit2=F.p[0];

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

Примеры реального использования

Ниже приведены примеры нескольких скриптов, использовавшихся в реальной экспериментальной практике.

Высокие сопротивления на переменном токе

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

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

После просмотра результата обработка ручной записью результата в файл перетаскиванием из s9 в File.

ScaleX(-12.0, 12.0, 241);
s[0] = exper.Smooth(0);
s[9].y = (y0 / z0).Even();
y9 /= y9.AtX(0);
exper.Hide(); Rescale();
Последняя строчка скрипта нужна, чтобы показать только результат выполнения скрипта, поскольку масштаб результата сильно отличается от исходных данных. Обратите внимания на то, что сокращённая форма записи y9 в левой части оператора присваивания применяется только при записи конечного результата и приводит к показу его точек на графике. Предыдущие действия не оказывают влияния на графики.

Восстановление линейного хода развёртки

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

var H0=0.0; // начало развертки поля
var H1=6.0; // конец развертки поля
var t0=1.11; // время начала линейного роста
var t1=222.22; // время конца линейного роста
exper.FX(x=>H0+(H1-H0)*(x-t0)/(t1-t0));
Rescale();