Знайомство з GNSS на прикладі uBlox. Частина перша - огляд

Зміст

Замість вступу

У цій статті описано базовий принцип роботи GNSS, розглянуто можливості приймача uBlox ZED F9P а також пропрієтарного протоколу uBlox - UBX. Посилання на документацію uBlox (Integration manual та Interface description) на які я буду посилатися у цій статті, можна знайти тут.

Кілька слів про GNSS

GNSS — Global Navigation Satellite System, або Глобальна Навігаційна Супутникова Система. Не слід плутати з GPS, хоча в не предметній розмові ці поняття часто вживаються як тотожні. GNSS - загальне позначення усіх супутників позиціонування. В свою чергу супутники відносяться до різних сузір’їв - як GPS (США), Galileo (Європа), ГЛОНАСС (московія), BeiDou (Китай), QZSS (Японія) та NAVIC (Індія).

Дуже коротко про принцип роботи

Супутник GNSS випромінює сигнал в сторону Землі, який приймається та обробляється GNSS приймачем. Приймач може вирахувати свої координати, якщо бачить сигнали одночасно від мінімум 4ох різних супутників.

Сигнал може випромінюватися на різних частотах, один супутник може працювати одразу на декількох частотах. GNSS використовує частоти L-діапазону - частоти від 1 ГГц до 2 ГГц.
Частоти, які використовують супутники GNSS, можна знайти за цим посиланням.


Приймач uBlox ZED F9P

uBlox ZED F9P — дводіапазонний GNSS приймач професійного рівня.

ZED-F9P module | u-blox

Може надавати координати сантиметрової точності, може працювати в режимі RTK (виходить за рамки цієї статті). З інтерфейсів має 2 UART і по одному SPI, I2C та USB. Працює з протоколами NMEA (увімкнено за замовчуванням), UBX, RTCM та SPARTN (в останніх версіях софту). У цій статті ми розглянемо протокол UBX.

Кілька слів про протокол NMEA

NMEA — National Marine Electronics Association. Це стандарт, який визначає характеристики протоколу обміну даними між різними пристроями морського електронного обладнання, і який став дуже популярним серед GNSS приймачів. Це ASCII протокол, тому його дуже легко аналізувати просто дивлячись у термінал, але використовувати його для отримання даних від приймача в програмі, наприклад під мікроконтролер - не найкраща ідея.

Протокол UBX

Бінарний пропрієтарний протокол, розвитком якого займається компанія uBlox. Повідомлення передаються у вигляді потоку байтів, а тому очіма в текстовому вигляді в терміналі без попередньої обробки сприймати його неможливо.
Повідомлення складається з заголовку, корисних даних та чексуми.
Заголовок повідомлення містить преамбулу (0xb5 0x62), айді класу та повідомлення (class ID, message ID) та довжину корисної інформації.

Interface Description PDF, розділ 3.2

Такий заголовок можна описати у вигляді структури на мові С наступним чином

typedef struct {
  uint8_t   sync1;
  uint8_t   sync2;
  uint8_t   class_id;
  uint8_t   msg_id;
  uint16_t  payload_len;
} __attribute__((packed)) ubx_header_t;

Зверніть увагу на __attribute__((packed)) - структура має бути упакованою, щоб компілятор не зробив додаткових відступів між полями.

Кілька слів про Class ID та Message ID

Типи повідомлень протоколу UBX поділені на класи (Class ID), які в свою чергу поділені на підкласи (Message ID).
Наприклад, клас навігаційних повідомлень NAV - всі повідомлення, які мають class ID 0x01. Клас NAV має різні типи повідомлень - про статус приймача, про координати, про координати з високою точністю, про азімут, ітд. Це все - навігаційні дані, але різного типу. Вони відрізняються між собою по Message ID.

Висновок - Message ID це не ідентифікатор кожного окремого отриманного повідомлення. Це ідентифікатор підтипу повідомлення в середині загального класу.

Кілька прикладів Class ID - CFG (налаштування), NAV (навігація), MON (моніторинг), LOG (логування).

Чексума

Розрахунок чексуми описано в файлі Interface Description в розділі 3.4. Чексума розраховується від поля Class включно, до останнього байта пейлоада включно.
Наведено наступний псевдо-код, де N - довжина корисних даних в байтах (Length або payload_len), а Buffer - масив цих даних.

CK_A = 0, CK_B = 0
For (I = 0; I < N; I++)
{
  CK_A = CK_A + Buffer[I]
  CK_B = CK_B + CK_A
}

Отримання координат

Працюючи з протоколом UBX є два способи отримувати поточні координати від модуля - у відповіді на запит відповідного меседжа (class ID + message ID) або слухати інтерфейс на наявність регулярних повідомлень (налаштовується додатково).

меседж UBX-NAV-PVT — Interface Description PDF, розділ 3.15.13

Особисто я використовую UBX-NAV-PVT як основний меседж для отримання координат, оскільки він містить не лише координати, а також інформацію про час, статус модуля (чи отримано стабільний сигнал, for ex.), вектор руху, швидкість, інформацію про точність.
Повний опис пейлоаду можна знайти у документі Interface Description.pdf

Запит потрібного меседжа

Якщо треба запитати якусь інформацію у модуля, треба відправити йому повідомлення із заголовком UBX, в якому вказані Class та ID, але довжина вказана 0 (нуль), після чого додана чексума.
У відповідь прийде меседж з такими ж Class та ID, але не нульовою довжиною та наявним пейлоадом.

Приклад запиту UBX-NAV-PVT

Уявімо, що у нас є Raspberry Pi 5, до UART якої підключено UART1 модуля uBlox ZED F9P. Тоді для відправки запиту і отримання відповіді потрібен приблизно наступний код.

typedef struct {
  uint8_t   sync1;
  uint8_t   sync2;
  uint8_t   class_id;
  uint8_t   msg_id;
  uint16_t  payload_len;
} __attribute__((packed)) ubx_header_t;

typedef struct {
  /* попередні поля */
  int32_t lon;  /* longitude 1e^-7 */
  int32_t lat;  /* latitude 1e^-7 */
  /* наступні поля */
} __attribute__((packed)) ubx_nav_pvt_t;

#define CHECKSUM_LEN  2

/* заповнена структура ubx_header_t + чексума, щоб не розтягувати приклад */
uint8_t request[] = {0xb5, 0x62, 0x01, 0x07, 0x00, };
uint8_t response[128] = {0};
ubx_nav_pvt_t *pvt = (ubx_nav_pvt_t *)(response + sizeof(ubx_header_t));
int fd = open("/dev/ttyS0");  /* відкриваємо інтерфейс, до якого підключено модуль GNSS */

/* відравляємо запит в УАРТ */
write(fd, request, sizeof(request));
/* читаємо відповідь з УАРТу */
read(fd, response, sizeof(ubx_header_t) + sizeof(ubx_nav_pvt_t) + CHECKSUM_LEN  );

close(fd);  /* закриваємо інтерфейс */

/* виводимо довготу та широту в термінал */
printf("lon %.7f lat %.7f\r\n", 
        (float)pvt->lon * 1e-7, 
        (float)pvt->lat * 1e-7
      );

Регулярні повідомлення

У версіях протоколу, старіших за 23.01 можна було використати меседж UBX-CFG-MSG для налаштування регулярності відправки потрібного повідомлення, вказавши Class, ID та частоту.

UBX-CFG-MSG

У версіях протоколу від 23.01 і новіше треба використовувати процедуру налаштування модуля UBX-CFG. Ця процедура буде детально розглянута у наступній статті.

Список джерел
  1. u-blox F9 high precision GNSS receiver Interface description
Поділись своїми ідеями в новій публікації.
Ми чекаємо саме на твій довгочит!
Oleksandr Valentirov
Oleksandr Valentirov@aleks0010

Програміст-самоучка

10Прочитань
0Автори
0Читачі
На Друкарні з 11 квітня

Вам також сподобається

  • Що таке патерни проєктування?

    Патерни проєктування подібні до стандартних конструкцій у будівництві. Наприклад, "патерн Фасад" відповідає фасаду будинку, який приховує деталі і надає простий інтерфейс. "Патерн Одинак" подібний до створення лише одного ключа для доступу до чогось цінного.

    Теми цього довгочиту:

    Програмування
  • Поширені помилки у дизайні REST API

    У довгочиті розглядаються поширені помилки при проектуванні REST API та способи їх уникнення: версіонування, використання DTO, підхід CQRS, робота з мікросервісами, та інші практики для підвищення продуктивності, безпеки й зручності API

    Теми цього довгочиту:

    Java

Коментарі (0)

Підтримайте автора першим.
Напишіть коментар!

Вам також сподобається

  • Що таке патерни проєктування?

    Патерни проєктування подібні до стандартних конструкцій у будівництві. Наприклад, "патерн Фасад" відповідає фасаду будинку, який приховує деталі і надає простий інтерфейс. "Патерн Одинак" подібний до створення лише одного ключа для доступу до чогось цінного.

    Теми цього довгочиту:

    Програмування
  • Поширені помилки у дизайні REST API

    У довгочиті розглядаються поширені помилки при проектуванні REST API та способи їх уникнення: версіонування, використання DTO, підхід CQRS, робота з мікросервісами, та інші практики для підвищення продуктивності, безпеки й зручності API

    Теми цього довгочиту:

    Java