Конечно, ты можешь купить GPS-приемник в Китае за несколько долларов. А можешь и не покупать — все равно он есть у тебя в телефоне, в навигаторе в машине… Но если ты хочешь быть настоящим хакером-инженером и разобраться в технологии GPS на низком уровне, то добро пожаловать в эту статью. Разберемся так, что мало не покажется!

История разработки глобальной системы позиционирования (Global Positioning System, GPS) уходит корнями в 50-е годы прошлого века, а первый спутник был запущен в 1974 году. Первоначально система использовалась лишь военными, но после трагедии с самолетом авиакомпании Korean Airlines, который был сбит над территорией СССР, гражданские службы также получили возможность работы с GPS. В 1993-м окончательно было решено предоставить GPS для использования гражданскими службами на безвозмездной основе, а после отключения намеренного округления положения точность возросла со ста метров до двадцати. В наши дни точность продолжает увеличиваться, а стоимость приемников — снижаться. Поэтому сделать GPS-логгер, ну или навигатор, может любой, кто родился с паяльником вместо…ладно-ладно, это шутка была. Одно другого не исключает :).

 

Матчасть

Итак, что же должно делать наше устройство?

  1. Получить информацию о текущем положении от GPS-приемника.
  2. Разобрать ее.
  3. Показать на экране текущее положение приемника, а также видимые спутники.

Для этого придется узнать, что таится за терминами GPS, NMEA-0183 и алгоритм Брезенхема.

 

GPS

Конечно, детально разбираться в нюансах работы GPS не требуется, поскольку всю работу по вычислению координат, скорости, курса и других параметров за нас возьмет на себя GPS-приемник. Но базу знать надо.

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

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

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

Существуют способы, которые позволяют уменьшить время старта: AGPS (получение альманаха альтернативными способами — через интернет или Почтой России), DGPS (исключение искажения сигнала атмосферой) и другие. Но я их рассматривать не буду, поскольку для выполнения поставленной задачи этого не нужно.

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

 

NMEA-0183

NMEA — National Marine Electronics Association, а NMEA-0183 (согласно Википедии) — текстовый протокол связи морского (как правило, навигационного) оборудования (или оборудования, используемого в поездах) между собой. Вот строки, приходящие от моего приемника.

(...)
$GPRMC,174214.00,A,5541.23512,N,03749.12634,E,3.845,178.09,150914,,,A*6F
$GPVTG,178.09,T,,M,3.845,N,7.121,K,A*35
$GPGGA,174214.00,5541.23512,N,03749.12634,E,1,04,4.98,178.2,M,13.1,M,,*56
$GPGSA,A,3,20,14,04,17,,,,,,,,,8.85,4.98,7.31*02
$GPGSV,4,1,16,01,67,242,,02,,,15,03,,,15,04,53,284,35*74
$GPGSV,4,2,16,05,,,23,07,,,19,08,,,21,10,,,24*7F
$GPGSV,4,3,16,11,,,14,12,,,12,14,35,058,37,17,24,311,34*72
$GPGSV,4,4,16,20,40,275,29,22,08,097,,37,25,199,27,39,25,195,32*73
$GPGLL,5541.23512,N,03749.12634,E,174214.00,A,A*6D
(...)

Сначала определим похожие части каждой строки. Легко видеть, что все они начинаются одинаково и более-менее одинаково заканчиваются. $GP — информация идет от приемника GPS (ты ведь понимаешь, что на корабле куча других датчиков: если бы у нас был аварийный маяк, то строка начиналась бы с $EP, а если эхолот, то с $SD, ну и так далее). Каждая строка обязательно заканчивается контрольной XOR-суммой всех байтов в строке начиная от $ и заканчивая * — это как раз те два символа в конце строки. И не забываем про символы <CR> и <LF> после контрольной суммы. Разберем каждую из строк подробнее.

$GPRMC,hhmmss.ss,A,aaaa.aaaa,N,bbbb.bbbb,E,c.c,d.d,DDMMYY,z1,z2,e*ff

  • GPRMC — GPS Recommended Minimum Navigation Information sentence C — рекомендуемый минимум навигационной информации, строка типа С.
  • hhmmss.ss — время по всемирному координированному времени UTC, когда была произведена фиксация положения.
  • A — флаг достоверности информации. Если V, то информации верить нельзя.
  • aaaa.aaaaa — величина широты. Первые две цифры — градусы, вторые две — целое значение количества угловых минут, после точки — дробная часть количества угловых минут (переменной длины).
  • N — северная широта. Если S, то южная.
  • bbbb.bbbbb — величина долготы. Первые две цифры — градусы, вторые две — целое значение количества угловых минут, после точки — дробная часть количества угловых минут (переменной длины).
  • E — восточная долгота. Если W, то западная.
  • c.c — горизонтальная скорость в узлах (умножить на 1,852 для получения скорости в километрах в час), целая и дробная части имеют переменную длину.
  • d.d — направление скорости (путевой угол, курс) в градусах, целая и дробная части имеют переменную длину.
  • DDMMYY — текущая дата.
  • z1 — отсутствующая у нас величина направления магнитного склонения.
  • z2 — также отсутствующее у нас направление магнитного склонения.
  • e — индикатор режима.
  • ff — контрольная сумма.

$GPVTG,a.a,T,b.b,M,c.c,N,d.d,K,A*e

  • $GPVTG — GPS Track Made Good and Ground Speed — строка с информацией о курсе и скорости.
  • a.a — курс в градусах.
  • T — True, флаг достоверности информации.
  • b.b — направление магнитного склонения (у нас его нет).
  • M — Magnetic, да, действительно магнитное.
  • c.c — горизонтальная скорость в узлах (умножить на 1,852 для получения скорости в километрах в час).
  • N — kNots, узлы.
  • d.d — горизонтальная скорость в километрах в час (и умножать ничего не надо).
  • K — километры в час.
  • ee — контрольная сумма.

$GPGGA,hhmmss.ss,a.a,N,b.b,E,c,d,e.e,f.f,M,g.g,M,h.h,*i

  • $GPGGA — Global Positioning System Fix Data — строка с информацией о текущем местоположении.
  • hhmmss.ss — время по всемирному координированному времени UTC, когда была произведена фиксация положения.
  • a.a — величина широты.
  • N — северная широта. Если S, то южная.
  • b.b — величина долготы.
  • E — восточная долгота. Если W, то западная.
  • c — флаг качества сигнала GPS.
  • d — количество используемых спутников.
  • e.e — фактор снижения точности (DOP, Dilution of precision).
  • f.f — высота расположения приемника над уровнем моря.
  • M — высота дается в метрах.
  • g.g — различие между геоидом (истинной формой нашей планеты) и эллипсоидом по WGS84 (трехмерная система координат для позиционирования).
  • M — различие дается в метрах.
  • h.h — номер станции, передающей поправки DGPS.
  • i — контрольная сумма.

$GPGSA,A,x,y1,y2,y3,y4,y5,y6,y7,y8,y9,y10,y11,y12,z1,z2,z3*i

  • $GPGSA — GPS DOP and Active satellites — строка с информацией о спутниках, использованных для определения местоположения и о факторах снижения точности.
  • A — автоматический режим выбора работы в 2D или 3D, M — ручной режим, когда жестко выбран, например, 2D.
  • x — режим работы приемника: 0 — координаты не определены, 1 — режим 2D, 2 — режим 3D.
  • y1..y12 — номера спутников, используемых для определения местоположения приемника.
  • z1..z2 — PDOP, HDOP, VDOP (факторы снижения точности по положению, в горизонтальной плоскости и в вертикальной плоскости соответственно).
  • i — контрольная сумма.

$GPGSV,a,b,c1,d1,e1,f1,c2,d2,e2,f2,c3,d3,e3,f3,c4,d4,e4,f4*i

  • GPGSV — GPS Satellites in View — строка содержит в себе информацию о номере, азимуте, высоте над горизонтом и соотношением сигнал/шум спутника. В строке максимально может быть четыре спутника.
  • a — общее количество строк GPGSV.
  • b — номер текущей строки.
  • c1..c4 — номер спутника.
  • d1..d4 — высота над горизонтом в градусах (0..90).
  • e1..e4 — азимут спутника в градусах (0..359).
  • f1..f4 — соотношение сигнал/шум в дБ (0..99).

$GPGLL,5541.23512,N,03749.12634,E,174214.00,A,A*6D — на этой строке нет смысла останавливаться подробно, поскольку она содержит в себе координаты и время, а это мы уже имеем в строках GPRMC и GPGGA.

Разумеется, производителям GPS-приемников не запрещается добавлять собственные строки. У моего приемника можно при запуске увидеть такие:

$GPTXT,01,01,02,u-blox ag - www.u-blox.com*50
$GPTXT,01,01,02,HW  UBX-G60xx  00040007 FF7FFFFFp*53
$GPTXT,01,01,02,ROM CORE 7.03 (45969) Mar 17 2011 16:18:34*59
$GPTXT,01,01,02,ANTSUPERV=AC SD PDoS SR*20
$GPTXT,01,01,02,ANTSTATUS=DONTKNOW*33
$GPTXT,01,01,02,ANTSTATUS=INIT*25
$GPTXT,01,01,02,ANTSTATUS=OK*3B
 

Алгоритм Брезенхема

Этот алгоритм является одним из самых старых алгоритмов компьютерной графики — он был разработан Джеком Брезенхемом (IBM) аж в 1962 году. С его помощью происходит растеризация графического примитива, другими словами, этот алгоритм определяет координаты пикселей, которые необходимо зажечь на экране, чтобы полученный рисунок примитива совпадал с оригиналом.

Представим, что мы рисуем линию, идущую из точки (0; 0) в (100, 32), как ты помнишь, у нашего экрана разрешение 128 x 64 точки. Несложно посчитать, что угол между этой прямой и осью Х составляет менее 45 градусов. Работа алгоритма заключается в последовательном переборе всех координат по оси Х в диапазоне от 0 до 100 и расчете соответствующей координаты Y. Логично, что в большинстве случаев значение координаты Y будет дробным, а это значит, что надо каким-то образом выбрать целочисленное значение координаты. Это делается путем выбора ближайшего пикселя. Для других углов наклона прямой, а также для окружностей, эллипсов и прочего алгоритм имеет аналогичный вид (подробнее о нем можно почитать в Википедии).

Алгоритм Брезенхема использует только операции сложения и вычитания целых чисел: обычно использование арифметики дробных чисел замедляет работу контроллера. Обычно, но не в нашем случае, поскольку внутри контроллера STM32F303VC находится ядро ARM Cortex-M4 с FPU. FPU (Floating Point Unit) — устройство, ускоряющее работу с дробными числами (математический сопроцессор), поэтому нас ничто не ограничивает и мы можем использовать алгоритм DDA-линии. Интересную демонстрацию ускорения работы МК при рисовании фракталов можно посмотреть на YouTube.

 

Железо

Что же мы используем в проекте?

  • Отладочную плату STM32F3-Discovery;
  • модуль UART GPS NEO-6M от WaveShare на базе приемника u-blox NEO-6M;
  • ЖК-матрицу МТ-12864А.

Про ЖК-экран я рассказывал в сентябрьском номере (№ 188), кратко лишь скажу, что сделан он на базе контроллеров KS0108, а схема подключения к STM32F3-Discovery не изменилась: разъем для подключения экрана содержит в себе 20 пинов, описание представлено в списке по следующей маске: <номер> — <название из даташита> — <описание из даташита> — <куда подключается>.

  • 1 — Ucc — питание — к 5V на Discovery.
  • 2 — GND — земля — к GND на Discovery.
  • 3 — Uo — вход питания ЖК-панели для управления контрастностью — к подстроечному резистору.
  • 4..11 — DB0..DB7 — шина данных — к PD0..PD7 на Discovery.
  • 12, 13 — E1, E2 — выбор контроллера — к PD8,PD9 на Discovery.
  • 14 — RES — сброс — к PD10 на Discovery.
  • 15 — R/W — выбор: чтение/запись — к PD11 на Discovery.
  • 16 — A0 — выбор: команда/данные — к PD12 на Discovery.
  • 17 — E — стробирование данных — к PD13 на Discovery.
  • 18 — Uee — выход DC-DC преобразователя — к подстроечному резистору.
 

NEO-6M

Данный приемник производится швейцарской компанией u-blox, основанной в 1997 году. Линейка модулей Neo-6 представлена разновидностями G, Q, M, P, V и T, каждый из которых обладает своими характерными возможностями: например, Neo-6P имеет возможность очень точного (с ошибкой <1 м) определения положения за счет метода Precise Point Positioning (PPP).

Приемник Neo-6M обладает следующими свойствами:

  • время холодного или теплого старта — 27 с;
  • время горячего старта — 21 с;
  • максимальная частота выдачи информации — 1 Гц;
  • диапазон частот импульсов на пин PPS — 0,25 Гц — 1 кГц;
  • максимальная точность определения положения — 2,5 м;
  • максимальная точность определения скорости — 0,1 м/с;
  • максимальная точность определения курса — 0,5 градуса.

Neo-6M умеет использовать SBAS (Satellite Based Augmentation System) — спутниковые системы дифференциальной коррекции, что увеличивает точность определения положения до 2 м, а также AGPS (Assisted GPS) для снижения времени холодного старта. Получение данных AGPS происходит с сайта u-blox с помощью сервисов AssistNow Online и AssistNow Offline (долгосрочный альманах). Модуль обладает поддержкой протоколов NMEA, UBX и RTCM. UBX — проприетарный протокол от u-blox, а RTCM — протокол для передачи модулю данных о дифференциальной коррекции DGPS. Также для связи доступны интерфейсы UART, I2C, SPI и USB.

Для работы с приемниками существует оригинальная утилита u-center, имеющая на момент написания статьи версию 8.11 (рис. 1).

Рис. 1. Общий вид u-center
Рис. 1. Общий вид u-center

Видно, что Neo-6M обладает огромным потенциалом, но детально описать все его возможности не хватит места, поэтому ограничимся предлагаемыми из коробки: только UART на скорости 9600, только NMEA, частота импульсов — 1 Гц.

В плане подключения все предельно просто: линии VCC, GND, RX и TX на приемнике подключаем к +3.3V, GND, PA9 и PA10 на Discovery соответственно.

 

Программа

Она должна отображать текущее положение приемника, скорость, направление движения, факторы снижения точности, время, дату, а еще показывать в полярной системе координат используемые спутники. Вот примерно так, как это делает u-center на рис. 2.

Рис. 2. Ожидание того, что будет показано на экранчике 128 х 64
Рис. 2. Ожидание того, что будет показано на экранчике 128 х 64

Как только строка line от Neo-6M принимается контроллером, происходит ее разбитие на токены (массив charTokens) — на подстроки, которые в исходной строке разделены запятыми.

char *token = malloc(strlen(line) + 1);
char *token2 = malloc(strlen(line) + 1);
int currentTokenNumber = 0;
int currentCharInTokenNumber = 0;
strcpy(token, line);
char *delimeter = ",";
while (token != NULL) {
  token2 = strpbrk(token + 1, delimeter);
  if (token2 == NULL) {
    // В конце строки меняем разделитель на "*"
    delimeter = "*";
    token2 = strpbrk(token + 1, "*");
  }
  /* Копируем часть строки между разделителями */
  currentCharInTokenNumber = 0;
  /* Очищаем значение токена */
  memset(charTokens[currentTokenNumber], '', MAX_TOKEN_LENGTH);
  for (char *ch = token + 1; ch < token2; *(ch++)) {
    charTokens[currentTokenNumber][currentCharInTokenNumber] = *ch;
    currentCharInTokenNumber++;
  }
  currentTokenNumber++;
  if (delimeter[0] == '*') {
    token = NULL;
  } else {
    token = token2;
  }
}

Казалось бы, вполне логично использовать функцию strtok, но я этого не делаю. Причину покажу на примере. Пусть имеется строка a,b,,,c. Результат разбития ее на токены с помощью strtok будет таким: 'a', 'b', 'c'. Для разбора NMEA это недопустимо, поскольку в этом протоколе значения токенов зависят от положения в строке. Результат работы описанного выше метода включает в себя пустые токены — 'a', 'b', '0', '0' 'c'.

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

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

struct _minimumNavigationInfo {
  float latitude;
  char latModificator; // East or West
  float longitude;
  char lonModificator; // North or South
  float groundSpeed;
  float speedAngle;
  float height;
  char heightModificator; // Metres or smth else
  char time[9]; // "hh:mm:ss"
  char date[9]; // "DD.MM.YY"
  char isValid;
};

Структура точности определения координат:

struct _fixInfo {
  double PDOP;
  double HDOP;
  double VDOP;
};

Структура о номере спутника, его положении и качестве сигнала:

struct _satelliteInfo {
  int satelliteId;
  float height;
  float azimuth;
  float SNR; //signal-to-noise ratio -- соотношение сигнал/шум
  int isFull; 
};

Если информация о спутнике не полная, например есть информация о высоте и азимуте, но нет о соотношении сигнал/шум, то в поле isFull записывается нулевое значение. Такие спутники при выводе на «радар» будут игнорироваться.

Заполнение структуры на основе массива токенов происходит очень просто: после разбора строки GPGSA в массиве charTokens значения факторов снижения точности *DOP находятся в элементах за номерами 15, 16 и 17.

fixInfo->PDOP = atof(charTokens[15]);
fixInfo->HDOP = atof(charTokens[16]);
fixInfo->VDOP = atof(charTokens[17]);

Теперь можно разобранную информацию смело выводить на экран (рис. 3).

3333
Рис. 3. А вот и реальность!

 

 

FIN

Теперь ты знаешь, что в GPS тоже нет ничего сложного (если не лезть в дебри), а если тебе хочется понять суть спутниковой навигации, то добро пожаловать на курс от Стэнфорда GPS: An Introduction to Satellite Navigation, with an interactive Worldwide Laboratory using Smartphones или от Университета Миннесоты From GPS and Google Maps to Spatial Computing на Coursera.

А в качестве домашнего задания я поставлю перед тобой три задачи: 1. Добавить возможность записи трека. 2. Заменить монохромный экран на цветной. 3. Вместе с WizFi220 (из номера 188) снабдить устройство возможностью получения A-GPS. Если возникли какие-нибудь вопросы, пиши мне на email, который можно найти в начале статьи. Удачи!

SRC

Весь код ты можешь найти на https://github.com/argrento. Просто скопируй с заменой файлы в папку Template из Standard Peripheral Library.

Не забывай заземляться! Помни, что разряд статического электричества может убить и модуль GPS, и антенну, и экран, и контроллер!

Теги:

3 комментария

  1. 24.01.2015 at 01:47

    Парень отважный «железяка», Nikitozz такое не мог.

  2. 24.01.2015 at 14:13

    «разобраться в технологии GPS на низком уровне» — Серьезно? NMEA-0183 — это низкий уровень? Дети такое на Ардуине собирают.

  3. 04.02.2015 at 23:31

    Слышь, товарищч, не лез бы ты в туда, где ни черта не смыслишь! Во-первых, не «радар», а РЛС по-русски. Ибо слово RADAR — это есть сокращение равнозначное отечественному «РЛС». Родной язык как-то надо немножко уважать. Во-вторых, где ж ты там собственно «радар»-то увидел? Матчасть-то учим, не? В-третьих, такими статьями уже весь Инет завален. А вот действительно именно РЛС с внешней подсветкой, в качестве коей используется сигнал ГНСС — это сделать можно. Слабо?!

Оставить мнение

Check Also

WTF is APT? Продвинутые атаки, хитрости и методы защиты

Наверняка ты уже читал о масштабных сетевых атаках, от которых пострадали банки, крупные п…