1
0
Fork 0
mirror of https://github.com/betaflight/betaflight.git synced 2025-07-24 16:55:36 +03:00

committed (untested) GPS support by sbaron;

fix for channel map cli stuff by simonk.
reindented some code, so changes are large.

git-svn-id: https://afrodevices.googlecode.com/svn/trunk/baseflight@127 7c89a4a9-59b9-e629-4cfe-3a2d53b20e61
This commit is contained in:
timecop 2012-03-24 09:27:40 +00:00
parent 0534444b2d
commit 007e033364
10 changed files with 3046 additions and 2475 deletions

249
src/gps.c Normal file
View file

@ -0,0 +1,249 @@
#include "board.h"
#include "mw.h"
#ifndef PI
#define PI 3.14159265358979323846
#endif
#ifndef sq
#define sq(x) ((x)*(x))
#endif
static void GPS_NewData(uint16_t c);
static bool GPS_newFrame(char c);
static void GPS_distance(int32_t lat1, int32_t lon1, int32_t lat2, int32_t lon2, uint16_t * dist, int16_t * bearing);
/*-----------------------------------------------------------
*
* GPS low level routines
*
*-----------------------------------------------------------*/
void USART2_IRQHandler(void)
{
if (USART_GetITStatus(USART2, USART_IT_RXNE) == SET) {
GPS_NewData(USART_ReceiveData(USART2));
}
}
static void uart2Init(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
USART_InitStructure.USART_BaudRate = 9600;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_Mode = USART_Mode_Rx;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_Init(USART2, &USART_InitStructure);
USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);
USART_Cmd(USART2, ENABLE);
}
void gpsInit(void)
{
uart2Init();
sensorsSet(SENSOR_GPS);
}
/*-----------------------------------------------------------
*
* Multiwii GPS code
*
*-----------------------------------------------------------*/
static void GPS_NewData(uint16_t c)
{
if (GPS_newFrame(c)) {
if (GPS_update == 1)
GPS_update = 0;
else
GPS_update = 1;
if (GPS_fix == 1 && GPS_numSat > 3) {
if (GPS_fix_home == 0) {
GPS_fix_home = 1;
GPS_latitude_home = GPS_latitude;
GPS_longitude_home = GPS_longitude;
}
if (GPSModeHold == 1)
GPS_distance(GPS_latitude_hold, GPS_longitude_hold, GPS_latitude, GPS_longitude, &GPS_distanceToHold, &GPS_directionToHold);
else
GPS_distance(GPS_latitude_home, GPS_longitude_home, GPS_latitude, GPS_longitude, &GPS_distanceToHome, &GPS_directionToHome);
}
}
}
/* this is an equirectangular approximation to calculate distance and bearing between 2 GPS points (lat/long)
it's much more faster than an exact calculation
the error is neglectible for few kilometers assuming a constant R for earth
input: lat1/long1 <-> lat2/long2 unit: 1/100000 degree
output: distance in meters, bearing in degrees
*/
static void GPS_distance(int32_t lat1, int32_t lon1, int32_t lat2, int32_t lon2, uint16_t * dist, int16_t * bearing)
{
float dLat = (lat2 - lat1); // difference of latitude in 1/100000 degrees
float dLon = (lon2 - lon1) * cos(lat1 * (PI / 180 / 100000.0)); // difference of longitude in 1/100000 degrees
*dist = 6372795 / 100000.0 * PI / 180 * (sqrt(sq(dLat) + sq(dLon)));
*bearing = 180 / PI * (atan2(dLon, dLat));
}
/* The latitude or longitude is coded this way in NMEA frames
dm.m coded as degrees + minutes + minute decimal
Where:
- d can be 1 or more char long. generally: 2 char long for latitude, 3 char long for longitude
- m is always 2 char long
- m can be 1 or more char long
This function converts this format in a unique unsigned long where 1 degree = 100 000
*/
static uint32_t GPS_coord_to_degrees(char *s)
{
char *p, *d = s;
uint32_t sec, m = 1000;
uint16_t min, dec = 0;
if (!*s)
return 0;
for (p = s; *p != 0; p++) {
if (d != s) {
*p -= '0';
dec += *p * m;
m /= 10;
}
if (*p == '.')
d = p;
}
m = 10000;
min = *--d - '0';
min += (*--d - '0') * 10;
sec = (m * min + dec) / 6;
while (d != s) {
m *= 10;
*--d -= '0';
sec += *d * m;
}
return sec;
}
// helper functions
static uint16_t grab_fields(char *src, uint8_t mult)
{ // convert string to uint16
uint8_t i;
uint16_t tmp = 0;
for (i = 0; src[i] != 0; i++) {
if (src[i] == '.') {
i++;
if (mult == 0)
break;
else
src[i + mult] = 0;
}
tmp *= 10;
if (src[i] >= '0' && src[i] <= '9')
tmp += src[i] - '0';
}
return tmp;
}
static uint8_t hex_c(uint8_t n)
{ // convert '0'..'9','A'..'F' to 0..15
n -= '0';
if (n > 9)
n -= 7;
n &= 0x0F;
return n;
}
/* This is a light implementation of a GPS frame decoding
This should work with most of modern GPS devices configured to output NMEA frames.
It assumes there are some NMEA GGA frames to decode on the serial bus
Here we use only the following data :
- latitude
- longitude
- GPS fix is/is not ok
- GPS num sat (4 is enough to be +/- reliable)
// added by Mis
- GPS altitude (for OSD displaying)
- GPS speed (for OSD displaying)
*/
#define FRAME_GGA 1
#define FRAME_RMC 2
static bool GPS_newFrame(char c)
{
uint8_t frameOK = 0;
static uint8_t param = 0, offset = 0, parity = 0;
static char string[15];
static uint8_t checksum_param, frame = 0;
if (c == '$') {
param = 0;
offset = 0;
parity = 0;
} else if (c == ',' || c == '*') {
string[offset] = 0;
if (param == 0) { //frame identification
frame = 0;
if (string[0] == 'G' && string[1] == 'P' && string[2] == 'G' && string[3] == 'G' && string[4] == 'A')
frame = FRAME_GGA;
if (string[0] == 'G' && string[1] == 'P' && string[2] == 'R' && string[3] == 'M' && string[4] == 'C')
frame = FRAME_RMC;
} else if (frame == FRAME_GGA) {
if (param == 2) {
GPS_latitude = GPS_coord_to_degrees(string);
} else if (param == 3 && string[0] == 'S')
GPS_latitude = -GPS_latitude;
else if (param == 4) {
GPS_longitude = GPS_coord_to_degrees(string);
} else if (param == 5 && string[0] == 'W')
GPS_longitude = -GPS_longitude;
else if (param == 6) {
GPS_fix = string[0] > '0';
} else if (param == 7) {
GPS_numSat = grab_fields(string, 0);
} else if (param == 9) {
GPS_altitude = grab_fields(string, 0);
} // altitude in meters added by Mis
} else if (frame == FRAME_RMC) {
if (param == 7) {
GPS_speed = ((uint32_t) grab_fields(string, 1) * 514444L) / 100000L;
} // speed in cm/s added by Mis
}
param++;
offset = 0;
if (c == '*')
checksum_param = 1;
else
parity ^= c;
} else if (c == '\r' || c == '\n') {
if (checksum_param) { //parity checksum
uint8_t checksum = hex_c(string[0]);
checksum <<= 4;
checksum += hex_c(string[1]);
if (checksum == parity)
frameOK = 1;
}
checksum_param = 0;
} else {
if (offset < 15)
string[offset++] = c;
if (!checksum_param)
parity ^= c;
}
return frameOK && (frame == FRAME_GGA);
}