/*
* Copyright (C) EdgeTX
*
* Based on code named
* opentx - https://github.com/opentx/opentx
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
/*
* This file is based on code from Cleanflight project
* https://github.com/cleanflight/cleanflight
*
* Cleanflight is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Cleanflight is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Cleanflight. If not, see .
*/
#include "opentx.h"
#include "gps.h"
#include "gps_nmea.h"
#include "gps_ubx.h"
#include
gpsdata_t gpsData;
static int gpsProtocol = -1;
const etx_serial_driver_t* gpsSerialDrv = nullptr;
void* gpsSerialCtx = nullptr;
#define DIGIT_TO_VAL(_x) (_x - '0')
uint32_t GPS_coord_to_degrees(const char * coordinateString)
{
const char * fieldSeparator, * remainingString;
uint8_t degrees = 0, minutes = 0;
uint16_t fractionalMinutes = 0;
uint8_t digitIndex;
// scan for decimal point or end of field
for (fieldSeparator = coordinateString; isdigit((unsigned char) *fieldSeparator); fieldSeparator++) {
if (fieldSeparator >= coordinateString + 15)
return 0; // stop potential fail
}
remainingString = coordinateString;
// convert degrees
while ((fieldSeparator - remainingString) > 2) {
if (degrees)
degrees *= 10;
degrees += DIGIT_TO_VAL(*remainingString++);
}
// convert minutes
while (fieldSeparator > remainingString) {
if (minutes)
minutes *= 10;
minutes += DIGIT_TO_VAL(*remainingString++);
}
// convert fractional minutes
// expect up to four digits, result is in
// ten-thousandths of a minute
if (*fieldSeparator == '.') {
remainingString = fieldSeparator + 1;
for (digitIndex = 0; digitIndex < 4; digitIndex++) {
fractionalMinutes *= 10;
if (isdigit((unsigned char) *remainingString))
fractionalMinutes += *remainingString++ - '0';
}
}
// TODO return degrees * 10000000UL + (minutes * 1000000UL + fractionalMinutes * 100UL) / 6;
return degrees * 1000000UL + (minutes * 100000UL + fractionalMinutes * 10UL) / 6;
}
static void changeBaudrate()
{
const int baudrates_count = 5;
const uint32_t baudrates[] = {9600, 57600, 115200, 19200, 38400};
static uint8_t current_rate = 0;
auto setBaudrate = gpsSerialDrv->setBaudrate;
if (setBaudrate == nullptr) return;
setBaudrate(gpsSerialCtx, baudrates[++current_rate % baudrates_count]);
}
static void autodetectProtocol(uint8_t c)
{
static tmr10ms_t time;
static uint8_t state = 0;
static tmr10ms_t firstPacketNMEA = 0;
switch (state) {
case 0: // Init
time = get_tmr10ms();
state = 1;
case 1: // Wait for a valid packet
if (gpsNewFrameNMEA(c)) {
if (!firstPacketNMEA) {
firstPacketNMEA = time;
} else if (time - firstPacketNMEA > 200) {
// continuous stream of NMEA packets for 2 seconds, but no UBX packets
gpsProtocol = GPS_PROTOCOL_NMEA;
}
state = 0;
return;
}
if (gpsNewFrameUBX(c, true)) {
gpsProtocol = GPS_PROTOCOL_UBX;
state = 0;
return;
}
uint32_t new_time = get_tmr10ms();
if (new_time - time > 50) {
// No message received
firstPacketNMEA = 0;
changeBaudrate();
time = new_time;
}
}
}
static void detectDisconnected(bool has_frame)
{
static tmr10ms_t time = 0;
if (has_frame) {
time = get_tmr10ms();
} else if (time > 0 && get_tmr10ms() - time > 500) {
gpsProtocol = GPS_PROTOCOL_AUTO;
time = get_tmr10ms();
}
}
void gpsNewData(uint8_t c)
{
switch (gpsProtocol) {
case GPS_PROTOCOL_NMEA:
detectDisconnected(gpsNewFrameNMEA(c));
break;
case GPS_PROTOCOL_UBX:
detectDisconnected(gpsNewFrameUBX(c, false));
break;
case GPS_PROTOCOL_AUTO:
autodetectProtocol(c);
break;
}
}
#if defined(DEBUG)
uint8_t gpsTraceEnabled = false;
#endif
void gpsSetSerialDriver(void* ctx, const etx_serial_driver_t* drv, int protocol)
{
gpsProtocol = protocol;
gpsSerialCtx = ctx;
gpsSerialDrv = drv;
}
void gpsWakeup()
{
if (!gpsSerialDrv) return;
auto _getByte = gpsSerialDrv->getByte;
if (!_getByte) return;
static tmr10ms_t time = get_tmr10ms();
uint8_t byte;
while (_getByte(gpsSerialCtx, &byte)) {
#if defined(DEBUG)
if (gpsTraceEnabled) {
dbgSerialPutc(byte);
}
#endif
gpsNewData(byte);
time = get_tmr10ms();
}
if (get_tmr10ms() - time > 20) {
changeBaudrate();
time = get_tmr10ms();
}
}
void gpsSendFrame(const char * frame)
{
switch (gpsProtocol) {
case GPS_PROTOCOL_NMEA:
gpsSendFrameNMEA(frame);
break;
}
}