diff --git a/LocCorr/cameracapture.c b/LocCorr/cameracapture.c index b545486..f74febb 100644 --- a/LocCorr/cameracapture.c +++ b/LocCorr/cameracapture.c @@ -175,6 +175,7 @@ int camcapture(void (*process)(Image*)){ DBG("Disconnected"); connected = theCam->connect(); sleep(1); + changeformat(); continue; } if(fabsf(oldbrightness - brightness) > FLT_EPSILON){ // new brightness diff --git a/LocCorr/doc/LCdoc.pdf b/LocCorr/doc/LCdoc.pdf new file mode 100644 index 0000000..664ebfa Binary files /dev/null and b/LocCorr/doc/LCdoc.pdf differ diff --git a/LocCorr/doc/LCdoc.tex b/LocCorr/doc/LCdoc.tex new file mode 100644 index 0000000..08140bd --- /dev/null +++ b/LocCorr/doc/LCdoc.tex @@ -0,0 +1,721 @@ +\documentclass[a4paper,12pt]{extarticle} +\usepackage{/home/eddy/ed} + +\def\t#1{\texttt{#1}} +\def\look#1{(см.\,стр.\,\pageref{#1}, п.\,\ref{#1})} +\nocolon + +\title{Система управления оптоволоконным спектрографом 1-м телескопа} +\begin{document} +\maketitle +\section{Описание} +Основой системы управления прибором является мини-компьютер под управлением ОС Gentoo Linux, расположенного +на подвесной части. В подвесной части расположены три шаговых двигателя (управляются по CAN-шине): управление +позиционированием волокна и подфокусировкой, а также камера подсмотра волокна. В стационарной части размещены +система калибровочной засветки (может управляться как отдельно по USB, так и с общего интерфейса по CAN-шине), +управление кросс-дисперсором и фокусировкой камеры. + +Система управления состоит из трех демонов: \t{canserver}~-- работа с устройствами на CAN-шине через USB-CAN +переходник, \t{loccorr}~-- анализ изображений и управление процессом коррекции положения объекта на волокне и +\t{spec\_server}~-- сервер веб-интерфейса системы управления. + +\section{Базовые демоны системы управления} +\subsection{Демон \t{loccorr}} +\subsubsection{Описание} +Данный демон\footnote{\url{https://github.com/eddyem/astrovideoguide_v3/tree/main/LocCorr}} предназначен для анализа +изображений, определения координат центроидов и выдачи управляющих сигналов на корректирующую аппаратуру. + +В качестве входных изображений могут быть файлы формата FITS, JPEG или PNG (посредством \t{inotify} производится +мониторинг обновления одного файла либо появления новых файлов в указанной директории), CMOS-светоприемники +Basler или Grasshopper. Возможно добавление других источников, для этого необходимо в директории проекта создать +заголовочный файл и файл исходных текстов наподобие \t{basler.h} и \t{basler.c}, где будут реализованы обработчики +функций структуры \t{camera} (файл \t{cameracapture.c}): +\begin{description} + \item[disconnect] отключить камеру; + \item[connect] подключить камеру и произвести базовые настройки; + \item[capture] захватить изображение (возвращается указатель на структуру \t{Image}, выделенную в этой функции); + \item[setbrightness] установить настройку <<яркость>> (если таковая имеется); + \item[setext] установить экспозицию (в миллисекундах); + \item[setgain] установить усиление (в дБ, если таковое имеется); + \item[getmaxgain] получить предельное значение усиления (если таковое имеется); + \item[setgeometry] установить геометрию кадра (ширина, высота, смещение по X и Y); + \item[getgeomlimits] получить предельные значения геометрии и шаг изменения. +\end{description} + +Алгоритм обработки изображений следующий. После считывания очередного изображения опционально выполняется +его медианная фильтрация. Далее строится гистограмма, по которой (в точке перегиба после первого максимума) +определяется уровень фона. Изображение бинаризуется по найденному фону. Производится заданное количество +эрозий и дилатаций для очистки шума. Находятся и нумеруются 4-связные области. Каждая область проверяется на +соотношение сторон описывающего прямоугольника и занимаемую площадь (они должны лежать в заданных рамках). +Если после этого все еще обнаружено больше одного объекта, объекты сортируются по яркости либо близости к +координатам оптического волокна. После обработки определенного количества изображений выполняется вычисление +средних координат центроида звезды. Если среднеквадратичное отклонение не превышает пяти пикселей, определен +модуль коррекции и его состояние позволяет выполнять коррекцию, посылаются команды коррекции положения. + +Работа модуля коррекции (на основе контроллеров шаговых двигателей от фирмы PusiRobo) описана в \t{pusirobo.c}. +Принимаемые команды передаются через открытый сокет демону \t{canserver}~\look{canserverdaemon}. +Возможно подключение любого другого модуля коррекции, для этого необходимо реализовать описанные в \t{improc.h} +поля структуры \t{steppersproc}: +\begin{description} + \item[proc\_corr] выполнить коррекцию положения звезды на заданное количество пикселей; + \item[stepstatus] получить состояние шаговых двигателей; + \item[setstepstatus] установить новое состояние; + \item[movefocus] переместить фокусер на заданное количество шагов; + \item[moveByU] переместить корректор на заданное количество шагов по оси U; + \item[moveByV] переместить корректор на заданное количество шагов по оси V; + \item[relay] выполнить команду на стационарной части (включить\slash выключить реле, установить значение ШИМ, + считать состояние кнопок и т.п.); + \item[stepdisconnect] отключиться от устройства коррекции. +\end{description} + +\subsubsection{Аргументы командной строки демона} +\begin{description} + \item[-A, --maxarea=arg] максимальная площадь (в пикселях) объекта (по умолчанию~150000); + \item[-C, --canport=arg] порт локального сервера \t{canserver} (по умолчанию~4444); + \item[-D, --ndilat=arg] количество дилатаций при обработке изображения (по умолчанию~2); + \item[-E, --neros=arg] количество эрозий при обработке изображения (по умолчанию~2); + \item[-H, --height=arg] высота рабочего участка изображения; + \item[-I, --minarea=arg] минимальная площадь (в пикселях) объекта (по умолчанию~400); + \item[-L, --logXY=arg] файл для логгирования вычисленных координат центроидов; + \item[-N, --naverage=arg] количество изображений для вычисления средних координат центроида (от~1 до~25); + \item[-P, --pidfile=arg] файл с PID сервера (по умолчанию \t{/tmp/loccorr.pid}); + \item[-T, --intthres=arg] порог яркости изображений при сортировке ($(I_1-I_2)/(I_1+I_2)$, по умолчанию~0.01) + \item[-W, --width=arg] ширина рабочего участка изображения; + \item[-X, --xtarget=arg] координата~$X$ цели (оптоволокна, щели); + \item[-Y, --ytarget=arg] координата~$Y$ цели; + \item[-b, --blackp=arg] доля пикселей с низкой интенсивностью, которая будет отброшена при эквализации гистограммы + (если включена эквализация обработанного кадра); + \item[-c, --confname=arg] имя файла конфигурации (по умолчанию \t{loccorr.conf}); + \item[-e, --equalize] выполнять эквализацию обработанного кадра; + \item[-h, --help] вызвать данную справку; + \item[-i, --input=arg] название объекта для мониторинга новых изображений (имя файла или директории, + <> или <> при захвате с КМОП-камеры); + \item[-j, --jpegout=arg] название файла, куда будет записано обработанное изображение (по умолчанию + \t{./outpWcrosses.jpg}); + \item[-l, --logfile=arg] файл, в который будет вестись логгирование; + \item[-p, --proc=arg] имя модуля процессинга коррекций (<>); + \item[-v, --verbose] повысить уровень информативности логгирования (каждый~\t{-v} повышает на~1); + \item[-x, --xoff=arg] сдвиг по оси~$X$ рабочего участка изображения; + \item[-y, --yoff=arg] сдвиг по оси~$Y$ рабочего участка изображения; + \item[--ioport=arg] номер порта сокета для подключения управления (по умолчанию 12345); + \item[--maxexp=arg] максимальная экспозиция (в миллисекундах, по умолчанию 500); + \item[--minexp=arg] минимальная экспозиция (в миллисекундах, по умолчанию 0.001). +\end{description} + +\subsubsection{Конфигурационные параметры} +В файле конфигурации хранятся базовые параметры настроек демона. В случае, если в аргументах командной строки +введены другие значения, нежели в настройках, преимущество будет за первыми. В случае отсутствия и +конфигурационного файла, и аргументов, будут использоваться значения по умолчанию. При завершении работы в +конфигурационный файл сохраняются текущие значения (в т.ч. полученные в результате указания пользователем во +время работы). + +Конфигурационный файл может иметь следующую структуру (после поля \t{\#} указан комментарий): +\begin{verbatim} +maxarea = 10000 # максимальная площадь объекта +minarea = 100 # минимальная площадь объекта +minwh = 0.800 # минимальное отношение ширины к высоте объекта +maxwh = 1.300 # максимальное отношение ширины к высоте объекта +ndilat = 3 # количество дилатаций +neros = 3 # количество эрозий +xoffset = 528 # горизонтальное смещение изображения +yoffset = 0 # вертикальное смещение изображения +width = 1000 # ширина изображения +height = 800 # высота изображения +equalize = 1 # эквализация результата +expmethod = 0 # метод вычисления экспозиции (0-авто, 1-вручную) +naverage = 1 # количество усреднений +umax = 24000 # максимальное абсолютное значение отсчетов по U +vmax = 24000 # максимальное абсолютное значение отсчетов по U +focmax = 32000 # макимальное значение отсчетов F +focmin = -32000 # минимальное значение отсчетов F +stpservport = 4444 # порт сервера корректора +Kxu = 71.987 # коэффициенты перевода +Kyu = 54.506 # координат изображения +Kxv = 22.750 # в шаги моторов +Kyv = -90.607 # по осям U и V +xtarget = 1170.900 # координаты центра +ytarget = 480.300 # цели +eqthrowpart = 0.900 # доля отброшенных пикселей при эквализации +minexp = 50.000 # минимальная экспозиция +maxexp = 1000.000 # максимальная экспозиция +fixedexp = 1000.000 # экспозиция, введенная вручную +intensthres = 0.010 # порог интенсивностей при сортировке +gain = 36.000 # усиление, введенное вручную +brightness = 0.000 # яркость +starssort = 0 # метод сортировки звезд (0-по расстоянию до цели, + 1-по интенсивности) +medfilt = 0 # медианная фильтрация +medseed = 3 # радиус медианной фильтрации +fixedbg = 0 # 1-не считать фон автоматически +fbglevel = 100 # установленный вручную уровень фона +\end{verbatim} + +\subsubsection{Сетевой протокол} +В целях безопасности сетевое соединение для подключения клиентов открывается исключительно на локальном +компьютере. Для осуществления безопасных удаленных подключений можно выполнить проброс портов посредством +ssh. + +<<Общение>> клиента с сервером выполняется по текстовому протоколу. В простейшем случае можно подключиться к +указанному в параметрах командной строки порту при помощи утилиты \t{nc} и вручную передавать команды. Полный +список команд с пояснениями появляется в ответ на запрос \t{help}: +{\small +\begin{verbatim} +maxarea=newval - maximal area (in square pixels) of recognized star image + (from 4 to 2.5e+06) +minarea=newval - minimal area (in square pixels) of recognized star image + (from 4 to 2.5e+06) +minwh=newval - minimal value of W/H roundness parameter (from 0.3 to 1) +maxwh=newval - maximal value of W/H roundness parameter (from 1 to 3) +ndilat=newval - amount of dilations on binarized image (from 1 to 100) +neros=newval - amount of erosions after dilations (from 1 to 100) +xoffset=newval - X offset of subimage (from 0 to 10000) +yoffset=newval - Y offset of subimage (from 0 to 10000) +width=newval - subimage width (from 0 to 10000) +height=newval - subimage height (from 0 to 10000) +equalize=newval - make histogram equalization (from 0 to 1) +expmethod=newval - exposition method: 0 - auto, 1 - fixed (from 0 to 1) +naverage=newval - calculate mean position by N images (from 1 to 25) +umax=newval - maximal value of steps on U semi-axe (from 100 to 50000) +vmax=newval - maximal value of steps on V semi-axe (from 100 to 50000) +focmax=newval - maximal focus position in microsteps (from 2.22045e-16 to 64000) +focmin=newval - minimal focus position in microsteps (from -64000 to -2.22045e-16) +stpservport=newval - port number of steppers' server (from 0 to 65536) +Kxu=newval - dU = Kxu*dX + Kyu*dY (from -5000 to 5000) +Kyu=newval - dU = Kxu*dX + Kyu*dY (from -5000 to 5000) +Kxv=newval - dV = Kxv*dX + Kyv*dY (from -5000 to 5000) +Kyv=newval - dV = Kxv*dX + Kyv*dY (from -5000 to 5000) +xtarget=newval - X coordinate of target position (from 1 to 10000) +ytarget=newval - Y coordinate of target position (from 1 to 10000) +eqthrowpart=newval - a part of low intensity pixels to throw away when histogram equalized + (from 0 to 0.9) +minexp=newval - minimal exposition time (from 0 to 4001) +maxexp=newval - maximal exposition time (from 0 to 4001) +fixedexp=newval - fixed (in manual mode) exposition time (from 0.1 to 4001) +intensthres=newval - threshold by total object intensity when sorting = |I1-I2|/(I1+I2) + (from 4.44089e-16 to 1) +gain=newval - gain value in manual mode (from 0 to 100) +brightness=newval - brightness value (from 0 to 10) +starssort=newval - stars sorting algorithm: by distance from target (0) or by intensity (1) + (from 0 to 1) +medfilt=newval - use median filter (from 0 to 1) +medseed=newval - median filter radius (from 1 to 7) +fixedbg=newval - don't calculate background, use fixed value instead (from 0 to 1) +fbglevel=newval - fixed background level (from 0 to 250) +help - List avaiable commands +settings - List current configuration +canbus - Get status of CAN bus server +imdata - Get image data (status, path, FPS, counter) +stpstate=newval - Set given steppers' server state +focus=newval - Move focus to given value +moveU=newval - Relative moving by U axe +moveV=newval - Relative moving by V axe +relay=newval - Send relay commands (Rx=0/1, PWMX=0..255) +\end{verbatim}} + +Часть команд является сеттерами различных параметров конфигурации. Формат сеттеров: <<параметр=значение>>. +В случае, если команда--сеттер принята успешно, возвращается ответ~\t{OK} (однако, это не гарантирует ее +выполнения). Если же команда--сеттер имеет неправильное (например, выходящие за допустимые диапазоны) значение +или же неизвестна, возвращается~\t{FAILED}. + +Полностью весь список конфигурационных параметров в формате JSON можно получить по запросу \t{settings}. +Ответом будет строка вида +\begin{verbatim} +{ "messageid": "settings", "maxarea": 10000, "minarea": 100, "minwh": 0.800, +"maxwh": 1.300, "ndilat": 3, "neros": 3, "xoffset": 528, "yoffset": 0, "width": +1000, "height": 800, "equalize": 1, "expmethod": 0, "naverage": 1, "umax": +24000, "vmax": 24000, "focmax": 32000, "focmin": -32000, "stpservport": 4444, +"Kxu": 71.987, "Kyu": 54.506, "Kxv": 22.750, "Kyv": -90.607, "xtarget": +1043.600, "ytarget": 417.100, "eqthrowpart": 0.900, "minexp": 50.000, "maxexp": +1000.000, "fixedexp": 2000.000, "intensthres": 0.010, "gain": 36.000, +"brightness": 0.000, "starssort": 0, "medfilt": 0, "medseed": 3, "fixedbg": 0, +"fbglevel": 100 } +\end{verbatim} + +Общим для каждой JSON-строки является параметр \t{messageid}, характеризующий содержащуюся в строке +информацию. + +Запрос \t{canbus} возвращает JSON-строку вида: +\begin{verbatim} +{ "messageid": "canbus", "status": "ready", "Umotor": { "status": "stopping", +"position": 0 }, "Vmotor": { "status": "stopping", "position": 0 }, "Fmotor": +{ "status": "stopping", "position": 0 }, "relay": 0, "PWM0": 0, "PWM1": 0, "PWM2": +0, "button0": 0, "button1": 0, "button2": 0, "button3": 0 } +\end{verbatim} +Здесь отображается состояние устройств (\t{status}): \t{disconnected}~-- соединение с сервером отсутствует, \t{ready}~-- +пассивное состояние, \t{setup}~-- юстировка осей~$U$ и~$V$, \t{gotomiddle}~-- перемещение подвижки и фокусера в +изначальное положение, \t{findtarget}~-- определение центра волокна (при обратной засветке), \t{fixing}~-- корректор +работает, \t{fixoutofrange}~-- корректор не может работать, т.к. объект вышел за допустимые диапазоны его +перемещения. У состояний \t{setup} и \t{gotomiddle} есть дополнительный параметр~--- текущее действие (например, +\t{waituv0}~-- ожидание перемещения подвижек в крайнее отрицательное положение до упора). +Каждому мотору соответствует свое поле значений: \t{Umotor}, \t{Vmotor} и \t{Fmotor}. Значения этих полей: \t{status}~-- +\t{stopping} или \t{moving}; \t{position}~-- текущее положение в шагах. Параметр \t{relay} описывает состояние обоих реле: +0~-- оба отключены, 1~-- включено первое, 2~-- включено второе, 3~-- включены оба. +Параметр \t{PWMx} характеризует заполнение ШИМ канала \t{x} ($x=0\div3$): от~0 (нет сигнала) до~255 (полный сигнал). +Параметры \t{buttonx} ($x=0\div3$) характеризуют состояние соответствующей кнопки: 0~-- события отсутствуют, 1~-- +кнопка была нажата в течение более 9~мс, 2~-- кнопка была нажата в течение более 199~мс, 3~-- кнопка была отпущена. + +Запрос \t{imdata} возвращает состояние граббера: +\begin{verbatim} +{ "messageid": "imdata", "camstatus": "disconnected", "impath": +"/dev/shm/image.jpg", "imctr": 0, "fps": 0.000, "expmethod": "auto", +"exposition": 100, "gain": 0, "brightness": 0, "xcenter": -1.0, "ycenter": -1.0 } +\end{verbatim} +\t{camstatus} может принимать значения \t{disconnected} (камера отключена), \t{watch directory} или \t{watch file} +(мониторинг файловой системы), \t{connected} (камера подключена). +\t{impath} содержит полный путь к сохраняемому обработанному файлу. +\t{imctr}~-- счетчик отснятых изображений. +\t{fps}~-- среднее количество обработанных кадров в секунду. +\t{expmethod}~-- метод вычисления экспозиции (\t{auto} или \t{manual}). +\t{exposition}~-- длительность последней экспозиции (в миллисекундах). +\t{gain}~-- текущее значение усиления (в дБ). +\t{brightness}~-- текущий уровень яркости. +\t{xcenter} и \t{ycenter}~-- координаты последнего вычисленного центроида (или $-1.0$, если центроид вычислить не +удалось). + +Сеттер \t{stpstate} позволяет задать требуемый режим работы: \t{relax} или \t{disconnect}~-- выйти в режим бездействия, +\t{middle}~-- вывести подвижки в среднее положение, \t{setup}~-- начать процесс калибровки, \t{fix}~-- включить режим +коррекции. + +Сеттеры \t{focus}, \t{moveU} и \t{moveV} дают возможность перемещать подвижки фокуса и корректора вдоль +соответствующей координаты. Между ними есть разница: \t{focus} принимает \ж абсолютное\н значение (в шагах), а +\t{moveU} и \t{moveV}~--- относительные значения. Если требуемое значение за диапазоном перемещения, будет +возвращено \t{FAILED}. + + \subsubsection{Калибровка корректора положения звезды} +Калибровка выполняется автоматически в режиме \t{stpstate=setup}. Для начала ее проведения необходимо установить +все подвижки в среднее положение, навести телескоп на относительно яркую звезду, сфокусироваться телескопом, +установить звезду как можно более близко к метке рабочего оптоволокна и включить процедуру калибровки. +Процедура заключается в следующем: +\begin{enumerate} + \item обе координаты выставляются в среднее положение; + \item\label{V0} подвижка по оси~$U$ выставляется в крайнее <<отрицательное>> положение и запоминаются + усредненные координаты звезды; + \item\label{Vmax} подвижка по оси~$U$ выставляется в крайнее <<положительное>> положение и запоминаются + усредненные координаты звезды; + \item аналогично пунктам~\ref{V0} и~\ref{Vmax} определяются крайние координаты по оси~$V$; + \item после измерения координат звезды во всех крайних положениях подвижек коррекции вычисляются + коэффициенты преобразования координат. +\end{enumerate} + +\subsubsection{Коэффициенты преобразования координат} +\def\D#1{\Delta{}#1{}} +\def\Vv#1,#2\relax{\begin{pmatrix}#1\\#2\end{pmatrix}} +\def\V#1{\Vv#1\relax} +Система координат корректора, $(U, V)$ априори считается неортогональной. Кроме того, по каждой из осей может быть +свой масштабирующий коэффициент. Для преобразования смещений в координатном пространстве изображения, +$(\D{x}, \D{y})$, в шаги корректора положения, $(\D{u}, \D{v})$, необходимо найти коэффициенты матрицы: +$$\V{\D{u},\D{v}}= +\begin{pmatrix} +K_{xu} & K_{yu}\\ +K_{xv} & K_{yv} +\end{pmatrix} \V{\D{x},\D{y}}. +$$ + +Непосредственно измерить эти коэффициенты нельзя, т.к. при калибровке в процессе экрана получаются другие +коэффициенты: +$$\V{\D{x},\D{y}}= +\begin{pmatrix} + K_{ux} & K_{vx}\\ + K_{uy} & K_{vy} +\end{pmatrix} \V{\D{u},\D{v}}. +$$ + +Искомые коэффициенты связаны с данными обратным преобразованием: +$$ +\begin{pmatrix} + K_{xu} & K_{yu}\\ + K_{xv} & K_{yv} +\end{pmatrix} = +\begin{pmatrix} + K_{ux} & K_{vx}\\ + K_{uy} & K_{vy} +\end{pmatrix}^{-1}. +$$ + +Введем коэффициенты пропорциональности: $K_U=\frc{\D{u}}{\D{r}}$, $K_V=\frc{\D_{v}}{\D{r}}$, где +$\D{r}=\sqrt{\D{x}^2+\D{y}^2}$. Пусть ось~$U$ наклонена по отношению к оси~$X$ под углом~$\alpha$, а ось~$V$~--- под +углом~$\beta$. В этом случае получим: +$$ +\V{\D{x},\D{y}}= +\begin{pmatrix} + \cos\alpha & \cos\beta\\ + \sin\alpha & \sin\beta +\end{pmatrix} \V{\frc{\D{u}}{K_U},\frc{\D{v}}{K_V}}= +\begin{pmatrix} + \frc{\cos\alpha}{K_U} & \frc{\cos\beta}{K_V} \\ + \frc{\sin\alpha}{K_U} & \frc{\sin\beta}{K_V} +\end{pmatrix} \V{\D{u},\D{v}}. +$$ +Отсюда, обратная матрица +\begin{equation} +\begin{pmatrix} + K_{xu} & K_{yu}\\ + K_{xv} & K_{yv} +\end{pmatrix} = +\begin{pmatrix} + \frc{\cos\alpha}{K_U} & \frc{\cos\beta}{K_V} \\ +\frc{\sin\alpha}{K_U} & \frc{\sin\beta}{K_V} +\end{pmatrix}^{-1}= +K +\begin{pmatrix} + K_U\sin\beta & -K_U\cos\beta \\ + -K_V\sin\alpha & K_V\cos\alpha +\end{pmatrix}, +\label{maineq} +\end{equation} +где +$$ +K = \frac{1}{\cos\alpha\sin\beta - \sin\alpha\cos\beta}. +$$ + +Для калибровки выставим корректор в центральное положение, а затем поочередно передвинем его в крайние +положения по обеим осям. Таким образом, смещению по оси~$U$ на $N_U$~шагов будут соответствовать изменения +координат~$\D{y_u}$ и~$\D{x_u}$, а смещению по оси~$V$ на $N_V$~шагов~---~$\D{x_v}$ и~$\D{y_v}$. +Коэффициенты пропорциональности определим как +$$K_U=\frac{N_U}{\D{r_u}}, K_V=\frac{N_V}{\D{r_v}}, +\quad\text{где}\quad \D{r_u}=\sqrt{\D{x_u}^2+\D{y_u}^2}, \D{r_v} = \sqrt{\D{x_v}^2+\D{y_v}^2}. +$$ +А тригонометрические функции направляющих углов~--- как +$$ +\cos\alpha=\frac{\D{x_u}}{\D{r_u}},\quad \sin\alpha=\frac{\D{y_u}}{\D{r_u}},\qquad +\cos\beta=\frac{\D{x_v}}{\D{r_v}},\quad \sin\beta=\frac{\D{y_v}}{\D{r_v}}. +$$ +В результате вычислим искомые коэффициенты по уравнению~\eqref{maineq}. + +\subsubsection{Коррекция телескопом} +В случае, если запрос \t{canbus} возвращает состояние \t{fixoutofrange}, необходимо сделать коррекцию телескопом для +возвращения объекта в допустимую область. В данный момент автоматическая коррекция не реализована. Для работы +данного модуля необходимо выполнить процедуру калибровки, аналогичную калибровке локального корректора +положения звезды, чтобы вычислить коэффициенты перехода от экранных координат к экваториальным. + +\subsection{Демон \t{canserver}}\label{canserverdaemon} +Обеспечивает работу\footnote{\url{https://github.com/eddyem/pusirobot/tree/master/canserver}} с устройствами, +подключенными по CAN-шине к преобразователю USB--CAN~\look{canusb}. +Устройство преобразователя можно определить по его PID/VID либо файлу устройства. Демон обеспечивает +резервирование: можно подключить два одинаковых устройства преобразователя к общей CAN-шине, вставив их в +разные порты USB. В случае, если базовое устройство перестанет корректно работать, автоматически произойдет +переподключение к резервному. + +\subsubsection{Аргументы командной строки} + \begin{description} + \item[-P, --pid=arg] PID устройства преобразователя; + \item[-V, --vid=arg] VID устройства; + \item[-e, --echo] включить опцию <<эха>> введенных пользователем команд; + \item[-i, --device=arg] название файла устройства преобразователя; + \item[-h, --help] отобразить данную справку; + \item[-l, --logfile=arg] сохранять логи в указанный файл; + \item[-p, --port=arg] использовать для подключения указанный сетевой порт; + \item[-s, --speed=arg] скорость соединения по CAN-шине (в бодах); + \item[-v, --verbose] повысить уровень подробностей логгирования (каждая \t{-v} повышает уровень на 1); + \item[--pidfile=arg] имя PID-файла процесса (по умолчанию: \t{/tmp/canserver.pid}). + \end{description} + +\subsubsection{Сетевой протокол} +В целях безопасности сетевое соединение для подключения клиентов открывается исключительно на локальном +aкомпьютере. Для осуществления безопасных удаленных подключений можно выполнить проброс портов +посредством~ssh. +Протокол~--- текстовый. Для получения базовой справки можно выполнить запрос \t{help}: +\begin{verbatim} +help> help - show help +help> list - list all threads +help> mesg NAME MESG - send message `MESG` to thread `NAME` +help> register NAME ID ROLE - register new thread with `NAME`, raw receiving `ID` + running thread `ROLE` +help> threads - list all possible threads with their message format +help> unregister NAME - kill thread `NAME` +\end{verbatim} + +Формат передаваемых сообщений: первым словом идет имя потока или определенное ключевое слово (от имени потока +отличается наличием знака ">" в конце, поэтому не стоит именовать потоки подобным образом). Далее следует +непосредственно команда \t{параметр=значение}. В случае неправильно введенной команды последует ответ сервера: +\t{Wrong command}, если же команда принята, ответом будет~\t{OK} с возможными дальнейшими сообщениями. + +Чтобы обеспечить гибкость работы с разнообразными устройствами, подключенными к CAN-шине, каждым устройством +управляет один поток. Новый поток нужно <<зарегистрировать>> командой \t{register}. Ее параметры: имя потока +(должно быть уникальным, иначе в ответ получим ошибку), идентификатор потока (классический CANbus ID, который +поток будет слушать, скажем, если нам нужно подключиться к CANopen устройству с NodeID=10, то ID=0x58A) и роль +потока. + +Например, зарегистрируем в системе два двигателя с именами <> и <>: +\begin{verbatim} +register x 0x58a stepper +OK +x maxspeed=OK +register y 0x58b stepper +OK +y maxspeed=OK +\end{verbatim} + +После регистрации устройства все принимаемые пакеты с указанным для него идентификатором будут перенаправлены +в поток, обслуживающий это устройство. Соответственно, в зависимости от <<роли>> устройства, поступающие данные +будут обрабатываться и сообщаться клиенту. + +Командой \t{list} можно посмотреть, какие потоки запущены: +\begin{verbatim} +list +thread> name='x' role='stepper' ID=0x58A +thread> name='y' role='stepper' ID=0x58B +thread> Send message 'help' to threads marked with (args) to get commands list +\end{verbatim} + +Список всех возможных <<ролей>> можно получить при помощи команды \t{threads}: +\begin{verbatim} +role> canopen NodeID index subindex [data] - raw CANOpen commands with `index` +and `subindex` to `NodeID` +role> emulation (args) - stepper emulation +role> raw ID [DATA] - raw CANbus commands to raw `ID` with `DATA` +role> stepper (args) - simple stepper motor: no limit switches, only goto +\end{verbatim} + +<<Роль>> \t{canopen} дает возможность работать с CAN-шиной в режиме CANopen, посылая туда пакеты и читая +ответные данные. <<Роль>> \t{raw} дает доступ к <<чистой>> CAN-шине. В случае указания нулевого идентификатора, +будут приниматься все команды, передающиеся по CAN-шине, поэтому удобно использовать \t{raw} с нулевым +идентификатором для полного мониторинга сети. + +Команда \t{mesg} позволяет отправлять сообщения указанным потокам. Чтобы посмотреть, какие команды принимает +конкретный поток, посылаем ему сообщение \t{help}: +\begin{verbatim} +mesg x help +OK +x> COMMAND NARGS MEANING +x> stop 0 stop motor and clear errors +x> status 0 get current position and status +x> relmove 1 relative move +x> absmove 1 absolute move to position arg +x> enable 1 enable (!0) or disable (0) motor +x> setzero 0 set current position as zero +x> maxspeed 1 set/get maxspeed (get: arg==0) +x> info 0 get motor information +\end{verbatim} + +Например, для получения полной информации о драйвере~<>, отправим команду~\t{info}: +\begin{verbatim} +mesg x info +OK +x errstatus=0 +x devstatus=0 +x curpos=0 +x enable=1 +x microsteps=32 +x extenable=0 +x maxspeed=3200 +x maxcurnt=600 +x gpioval=8191 +x rotdir=1 +x relsteps=0 +x abssteps=0 +\end{verbatim} + +Здесь перечислены: флаги ошибок, состояние устройства, текущее положение (в микрошагах), активность обмоток +двигателя, количество микрошагов в одном шаге, останов двигателя по внешнему концевику, максимальная скорость (в +микрошагах в секунду), максимальный ток (в микроамперах), текущее значение на пинах GPIO, направление вращения, +последнее заданное относительное положение (в микрошагах), последнее заданное абсолютное положение (в +микрошагах). + +Для перемещения в заданное абсолютное положение дадим команду~\t{absmove}: +\begin{verbatim} +mesg x absmove 3200 +OK +x abssteps=OK +mesg x status +OK +x devstatus=0 +x curpos=3200 +x errstatus=0 +\end{verbatim} + +В случае ненулевого значения~\r{errstatus}, сбросить флаги ошибок можно командой~\t{stop}. + +Если во время движения попробовать дать новую команду движения без команды~\t{stop}, придет сообщение об ошибке: +\begin{verbatim} +mesg x relmove 1000 +OK +x abortcode='0x8000022' error='Data cannot be transferred or stored to the + application because of the present device state' +x abortcode='0x8000022' error='Data cannot be transferred or stored to the +application because of the present device state' +\end{verbatim} +Т.о., следует проверять все сообщения, т.к. ответ~\t{OK} лишь подтверждает правильность введенной команды и +возможность ее отправки соответствующему устройству. В конкретном случае сообщений об ошибке два, +т.к.~\t{relmove} сначала пытается задать направление движения, а затем~--- указать, сколько шагов надо проехать. + +Ответы~\t{OK} на запросы видит лишь тот клиент, который отправлял данные запросы. Ответы от сервера отправляются +всем подключенным клиентам. + + +%\subsection{Демон \t{spec\_server}} +%ТИМУР? + +\section{Электронные компоненты} +\subsection{Преобразователь USB--CAN}\label{canusb} +Данный преобразователь\footnote{\url{https://github.com/eddyem/stm32samples/tree/master/F0-nolib/usbcan}} +разработан на основе микроконтроллера STM32F042C6T6. При подключении к ПК эмулирует преобразователь +USB--UART PL2303, так что нет необходимости настраивать нестандартное устройство. Кроме того, такой подход +позволяет после конфигурации устройства (например, при помощи \t{stty}) работать с ним напрямую из bash-скриптов +без утилит вроде screen и т.п. +\subsubsection{Протокол последовательного интерфейса} +Используется текстовый протокол с односимвольными командами. При вводе неправильной команды (например, \t{?}), +пользователь получает справку: +\begin{verbatim} +'a' - add ID to ignore list (max 10 IDs) +'b' - reinit CAN with given baudrate +'d' - delete ignore list +'f' - add/delete filter, format: bank# FIFO# mode(M/I) num0 [num1 [num2 [num3]]] +'F' - send/clear flood message: F ID byte0 ... byteN +'I' - reinit CAN +'l' - list all active filters +'o' - turn LEDs OFF +'O' - turn LEDs ON +'p' - print ignore buffer +'P' - pause/resume in packets displaying +'R' - software reset +'s/S' - send data over CAN: s ID byte0 .. byteN +'T' - get time from start (ms) +\end{verbatim} + +По умолчанию устройство работает на скорости 100\,кбод. Если необходима другая скорость CAN, следует сразу после +включения задать ее командой~\t{b}. + +Численные данные выводятся в шестнадцатеричном формате, а ввод позволяют делать в двоичном (например, +\t{0b110011}), восьмеричном (например, \t{0123}), десятичном или шестнадцатеричном (например, \t{0xdeadbeef}). + +Реализован программный <<черный список>>, позволяющий игнорировать до десяти идентификаторов, а также +аппаратные фильтры STM32. Командой~\t{l} можно получить информацию о всех активных фильтрах. По умолчанию их +два: +\begin{verbatim} +Filter 0, FIFO0 in MASK mode: ID=0x01, MASK=0x01 +Filter 1, FIFO1 in MASK mode: ID=0x00, MASK=0x01 +\end{verbatim} +Один фильтр размещает пакеты с нечетными идентификаторами в FIFO0, другой размещает четные в FIFO1. Удалив +оба этих фильтра (\t{f 0}, \t{f 1}), можно установить необходимые фильтры (на конкретный список идентификаторов или +же по маске). Например, \t{f 0 0 M 0 0x3fc} настраивает фильтр номер~0 на FIFO0 для идентификаторов с~0 по~7 (т.е. +младшие 3 бита), а \t{f 1 1 I 11 15 20 30} настраивает фильтр~1 на FIFO1 для списка идентификаторов. + +Команды~\t{o} и~\t{O} позволяют отключать или включать диагностические светодиоды (один светодиод, LED1, +постоянно горит при стабильной связи; второй, LED0, вспыхивает в момент получения сообщений, прошедших +фильтрацию). + +При помощи команды~\t{s} можно отправлять данные в шину. После команды указывается идентификатор сообщения за +которым следуют от нуля до восьми байт данных. В случае ошибки выводятся соответствующие сообщения, например, +если на шине нет ни одного устройства, либо настроена неправильная скорость, получим: +\begin{verbatim} +s 123 0b1010 +Message parsed OK + +Too much errors, restarting CAN! +Receive error counter: 0 +Transmit error counter: 96 +Last error code: Ack error +Error counter limit +\end{verbatim} + +Прошедшие фильтрацию принятые сообщения отображаются в формате \t{\# ID [data]}: после символа решетки следует +идентификатор сообщения, за которым перечисляются данные тела сообщения (если длина данных больше нуля). + +Приостановить\slash возобновить отображение пришедших сообщений можно при помощи команды~\t{P}. + +Команда~\t{F} позволяет отправлять в сеть каждые 5\,мс заданное сообщение. + +\subsection{Модуль управления нагрузкой} +Модуль\footnote{\url{https://github.com/eddyem/stm32samples/tree/master/F0-nolib/usbcan_relay}} +разработан на основе микроконтроллера STM32F042C6T6 и по сути является <<наследником>> преобразователя +USB--CAN. Помимо приема команд по USB или CAN для работы с нагрузкой, модуль может выступать и в роли +преобразователя USB--CAN. + +CAN--идентификатор устройства задается переключателями на плате (возможно задать ID от~0 до~255). Скорость +интерфейса конфигурируется USB-командой~\t{C}, по умолчанию составляет 250~кбод. + +\subsubsection{Протокол последовательного интерфейса} +\begin{verbatim} +'0' - turn relay0 on(1) or off(0) +'1' - turn relay1 on(1) or off(0) +'a' - add ID to ignore list (max 10 IDs) +'A' - get ADC values @ all 4 channels +'b' - get buttons' state +'C' - reinit CAN with given baudrate +'d' - delete ignore list +'f' - add/delete filter, format: bank# FIFO# mode(M/I) num0 [num1 [num2 [num3]]] +'F' - send/clear flood message: F ID byte0 ... byteN +'I' - read CAN ID +'l' - list all active filters +'m' - get MCU temp & Vdd +'o' - turn nth LED OFF +'O' - turn nth LED ON +'p' - print ignore buffer +'P' - pause/resume in packets displaying +'R' - software reset +'s/S' - send data over CAN: s ID byte0 .. byteN +'T' - get time from start (ms) +'w' - get PWM settings +'W' - set PWM @nth channel (ch: 0..2, PWM: 0..255) +\end{verbatim} +Команды включения\slash выключения реле: состоят из номера реле и аргумента (0/1), например, \t{0 1} включит реле +номер~0. + +Считать информацию со всех четырех каналов (два внешних, температура МК и Vdd) можно при помощи команды~\t{A}. + +Команда~\t{b} позволяет получить информацию о состоянии кнопок, ответ выводится в виде человекочитаемой строки, +например: \t{The key 2 is released at 145623} (т.е. в условное время145623\,мс кнопка номер~2 была отпущена). +Нумерация начинается с нуля. + +Команда~\t{I} отображает установленный переключателями идентификатор CAN. + +При помощи команды~\t{o x} можно отключить питание с внешнего светодиода номер~\t{x} (от~0 до~2). Командой~\t{O x} +можно подать на него питание. + +Команда~\t{w} позволяет получить данные ШИМ со всех трех каналов (от~0 до~2), а команда~\t{W x val}~--- установить +на канале с номером~\t{x} значение заполнения ШИМ~\t{val} (от 0~-- сигнал отсутствует до 255~-- максимальный сигнал). + +В отличие от преобразователя USB--CAN, данное устройство не позволяет изменять фильтр номер~0: в момент старта +он настраивается на приме сообщений с идентификатором, соответствующим выставленному при помощи +переключателей на плате модуля. + +\subsubsection{Протокол интерфейса CAN} +Если устройство получает на свой идентификатор пустое сообщение, оно отправляет его обратно (<>). У +сообщения с ненулевой длиной анализируется байт 0 пришедших данных (это~--- команда). Команда может быть одна +из: +\begin{verbatim} +typedef enum{ + CAN_CMD_PING, // ping (without setter) + CAN_CMD_RELAY, // relay get/set + CAN_CMD_PWM, // PWM get/set + CAN_CMD_ADC, // get ADC (without setter) + CAN_CMD_MCU, // MCU T and Vdd + CAN_CMD_LED, // LEDs get/set + CAN_CMD_BTNS, // get Buttons state (without setter) + CAN_CMD_TMS, // get time from start (in ms) + CAN_CMD_ERRCMD, // wrong command + CAN_CMD_SETFLAG = 0x80 // command is setter +} CAN_commands; +\end{verbatim} + +Старший бит команды (\t{CAN\_CMD\_SETFLAG}) является маркером того, что команда~--- сеттер. Без этого маркера +команда рассматривается как геттер. Т.е. номер команды определяется выражением \t{byte[0]\&0x7f}. + +Начиная с номера~8 (\t{CAN\_CMD\_ERRCMD}) команда считается ошибочной, в этом случае в сеть отправляется пакет +с длиной, равной единице, где нулевой байт данных содержит код~\t{CAN\_CMD\_ERRCMD}. + +Идентификатор ответного сообщения совпадает с идентификатором устройства. + +Большинство команд передает данные в формате little endian, кроме состояния АЦП. +\begin{description} +\item[CAN\_CMD\_ADC]~--- получить значение всех каналов АЦП. Поле данных в байтах $1\div6$ содержит смешанные +по 12\,бит показания АЦП (каналы 5\,В, 12\,В и внутренние: температура МК и Vdd). +\item[CAN\_CMD\_BTNS]~--- получить параметры кнопок. В байте \verb'data[1]' указан номер кнопки, \verb'data[2]'~-- ее +состояние (0~-- не нажата, 1~-- была нажата больше 9\,мс и меньше 200\,мс, 2~-- была нажата больше 200\,мс, 3~-- была +отпущена), \verb'data[4..7]' (для выравнивания)~-- время наступления событий в условных мс от запуска МК или +переполнения счетчика времени (\t{uint32\_t}, little endian). +\item[CAN\_CMD\_LED]~--- установить или получить состояние светодиодов. В случае сеттера \verb'data[1]' входящего +потока задает состояние соответствующего светодиода (если n-й бит установлен, светодиод включается, нет~-- гаснет), +в ответе \verb'data[1]' указывает на текущее состояние светодиодов (аналогично сеттеру). +\item[CAN\_CMD\_MCU]~--- температура МК и значение Vdd: в \verb'data[2,3]' содержится температура МК +($T\cdot10\degr{}C$, little endian \t{int16\_t}), в полях \verb'data[4,5]'~--- значение Vdd ($V\cdot100$, little endian \t{uint16\_t}). +\item[CAN\_CMD\_PWM] позволяет задавать и читать заполнение соответствующих ШИМ каналов, поля у сеттера и +геттера аналогичны: \verb'data[1..3]'~--- соответствующее значение заполнения ($0\div255$) ШИМ для каналов $0\div2$. +\item[CAN\_CMD\_RELAY] управляет реле, поля сеттеров и геттеров аналогичны: биты~0 и~1 в \verb'data[1]' отражают +состояние соответствующего реле (1~-- включено, 0~-- выключено). +\item[CAN\_CMD\_TMS] содержит в полях \verb'data[4..7]' время Tms~--- условное время в миллисекундах с момента +запуска микроконтроллера (little endian \t{uint32\_t}). +\end{description} + +\end{document}