gpsdPROXY daemon
version 0.6
Весьма удобно обращаться к gpsd из веб-приложений посредством команды ?POLL; в произвольный момент времени, однако есть проблемы:
во-первых, данные AIS недоступны в команде ?POLL;
во-вторых, данные, отличные от тех, что отдаёт приёмник ГПС, могут не попасть в команду ?POLL;
Причина в том, что gpsd собирает данные в течение "эпохи" от одного получения координат приёмником ГПС до другого. Но "эпоха" для данных AIS и приборов гораздо длиннее, и эти данные не попадают в запрос ?POLL;
С деталями и дискуссией по этому поводу можно ознакомиться по следующим ссылкам (англ.):
https://lists.nongnu.org/archive/html/gpsd-users/2020-04/msg00093.html
https://lists.nongnu.org/archive/html/gpsd-users/2021-06/msg00017.html
Однако надо заметить, что gpsdPROXY довольно странная программа, потому что она, в общем, делает ровно то же самое, что и собственно gpsd: собирает данные из потока, агрегирует их и отдаёт структурированные данные по требованию. Разница во времени жизни данных. В gpsdPROXY его можно задавать явным образом для каждого типа данных.
Думаю, что такая функциональность должна быть непосредственно в gpsd. Но этого нет.
Зато можно применить gpsdPROXY для сбора данных от источников, в которых нет контроля достоверности данных. Например, от VenusOS, где нет совсем никакого контроля, и от SignalK, где есть хотя бы метка времени.
Или просто использовать gpsdPROXY как websocket интерфейс к gpsd.
Дополнительно gpsdPROXY может получать и потом раздавать клиентам информацию "Человек за бортом", а также определять возможность столкновения с целями AIS.
Возможности
Предлагаемый демон собирает данные AIS и всё то, что передаётся gpsd в секции TPV и хранит их в течение указанного пользователем времени. Получить данные можно запросом ?POLL; протокола gpsd.
Таким образом, все данные AIS и данные эхолота и анемометра (и ГПС, конечно) становятся доступными в произвольный момент времени.
Дополнительно реализован и синхронная потоковая отдача данных, аналогичная режиму ?WATCH={"enable":true,"json":true} gpsd.
Источники данных
Основным источником данных для gpsdPROXY является gpsd, запущенный на той же или другой машине. При использовании gpsd обеспечивается максимальная достоверность данных.
Однако, можно получать данные и из других источников, таких как:
VenusOS
gpsdPROXY может работать под управлением VenusOS по крайней мере начиная с версии v2.80~38, и во всяком случае на Raspberry Pi. Работа в устройствах Victron Energy не проверялась. Однако, gpsdPROXY может получать данные от любого устройства в сети, работающего под управлением VenusOS любой версии.
Для того, чтобы gpsdPROXY мог получать данные, нужно сделать следующие настройки в VenusOS:
В веб-консоли или на экране управления устройства открыть пункты меню Settings -> Services -> MQTT on LAN (SSL) и указать "Включить".
Ограничения
- в VenusOS нет данных о глубине и нет данных AIS
- данные, представляемые VenusOS не являются достаточно достоверными и должны использоваться с осторожностью
Signal K
gpsdPROXY может получать данные от сервиса Signal K, работающего на той же машине, или в локальной сети. gpsdPROXY предпримет попытку самостоятельно найти Signal K посредством zeroconf, или обращаясь на стандартный порт. Но лучше сконфигурировать.
Ограничения
На самом деле gpsdPROXY к SignalK должен обращаться только локально. Через сеть -- оно странное.
Обнаружение столкновений
gpsdPROXY определяет возможность столкновений с целями AIS, а зависимости от их и от собственной скорости и указанного времени до события.
Возможность столкновения вычисляется в соответствии с примитивной моделью движения, учитывающей, фактически, только возможные отклонения от курса. Однако ожидается, что эти вычисления не должны критически загрузить сервер даже при большом количестве целей AIS.
Объект ``{"class":"ALARM","alarms":{"collisions":[]}}` содержит список mmsi и координат потенциально опасных судов. GaladrielMap обозначает такие объекты на карте специальным значком, а у курсора собственного положения рисует стрелочки с направлением на опасный объект.
Для корректной работы надо указать характеристики своего судна в params.php.
Совместимось
Linux, PHP < 8. Кретинские решения, принятые в PHP 8 не позволяют gpsdPROXY работать под PHP 8, а я не хочу следовать этим решениям.
Настройка
См. файл params.php
Использование
$ php gpsdPROXY.php
К демону можно подключиться через BSD socket или websocket по адресу, указанному в params.php.
Управление
Демон проверяет, не запущен ли он уже, и не запускается вторично.
Параметры команд gpsd не поддерживаются, зато имеются дополнительные параметры:
- параметр "subscribe":"TPV|AIS" для команд ?POLL и ?WATCH={"enable":true,"json":true}.
Параметр указывает возвращать только данные TPV или AIS, но не то и другое. Например:
?POLL={"subscribe":"AIS"} возвращает класс "POLL" с данными "ais":[], вместо "tpv":[]. - параметр "minPeriod":"", в сек. для команды WATCH={"enable":true,"json":true}. Нормально данные отсылаются так часто, как они приходят от gpsd. Указание этого параметра заставляет демон отсылать данные не чаще указанного количества секунд. Например, команда:
WATCH={"enable":true,"json":true,"minPeriod":"2"} посылает данные каждые две секунды, или реже, по мере получения данных.
Результат
Демон возвращает данные, как описано в документации к gpsd, за исключением:
- ответ DEVICES на команду WATCH содержит только одно устройство -- сам демон. Как следствие -- не надо объединять сходные данные от разных устройств: это уже делает демон.
- массив sky в объекте POLL пуст
- объект AIS отсутствует в ответе команды WATCH, вместо этого посылается отдельный объект
- в объект POLL и ответ команды WATCH добавлен массив ais с ключами mmsi и данными в формате, описанном в AIS DUMP FORMATS, за исключением:
- скорость в м/сек
- координаты в десятичных градусах
- углы в градусах
- осадка в метрах
- длина в метрах
- ширина в метрах
- поле 'second' отсутствует, но есть поле 'timestamp' с временем unix
Пример клиентского кода
webSocket = new WebSocket("ws://"+gpsdProxyHost+":"+gpsdProxyPort);
webSocket.onopen = function(e) {
console.log("spatialWebSocket open: Connection established");
};
webSocket.onmessage = function(event) {
let data;
data = JSON.parse(event.data);
switch(data.class){
case 'VERSION':
console.log('webSocket: Handshaiking with gpsd begin: VERSION recieved. Sending WATCH');
webSocket.send('?WATCH={"enable":true,"json":true,"subscribe":"TPV,AIS,ALARM","minPeriod":"0"};');
break;
case 'DEVICES':
console.log('webSocket: Handshaiking with gpsd proceed: DEVICES recieved');
break;
case 'WATCH':
console.log('webSocket: Handshaiking with gpsd complit: WATCH recieved.');
break;
case 'POLL':
break;
case 'TPV':
realtimeTPVupdate(data);
break;
case 'AIS':
realtimeAISupdate(data);
break;
case 'ALARM':
for(const alarmType in data.alarms){
switch(alarmType){
case 'MOB':
realtimeMOBupdate(data.alarms.MOB);
break;
case 'collisions':
realtimeCollisionsUpdate(data.alarms.collisions);
break;
}
}
break;
}
};
webSocket.onclose = function(event) {
console.log('webSocket closed: connection broken with code '+event.code+' by reason ${event.reason}');
};
webSocket.onerror = function(error) {
console.log('webSocket error');
};
Поддержка
Индивидуальная платная консультация
Вы можете сделать пожертвование через ЮМани.