Commit f09418e6 authored by Guillaume Gonnet's avatar Guillaume Gonnet
Browse files

Make GPS working (some times at least).

parent edb85a0c
......@@ -39,4 +39,6 @@ This array has 10 elements so this parameter is coded on **4 bits**.
## Temperature
TODO.
Temerature is encoded on **9 bits**.
To get the real temperature, use the following formula.
> `Temperature = (value / 4) - 55`
......@@ -13,7 +13,8 @@ This project is under the MIT license
*/
require; // For NodeJS typings.
// Unused import for NodeJS typings.
const util = require("util");
// Parse command line.
......@@ -34,9 +35,7 @@ if (payload.length != 10) {
// Value used for the conversion of the position from DMS to decimal.
const MaxNorthPosition = 8388607; // 2^23 - 1
const MaxSouthPosition = 8388608; // -2^23
const MaxEastPosition = 8388607; // 2^23 - 1
const MaxWestPosition = 8388608; // -2^23
// Extract LoRa settings.
......@@ -50,5 +49,41 @@ Power = [2, 5, 8, 11, 14, 15, 16, 17, 18, 19][Power];
// Extract temperature.
let Temperature = payload.readUInt16LE(1) & 0x1FF;
Temperature = Temperature * 0.25 - 55;
let Temperature = (payload.readUInt16BE(1) >> 7) & 0x1FF;
Temperature = Temperature / 4 - 55;
// Extract latitude.
let Latitude = (payload.readUInt32BE(2) >> 8) & 0x7FFFFF;
Latitude = Latitude * 90 / MaxNorthPosition;
Latitude = Math.round(Latitude * 1000000) / 1000000;
// Extract longitude.
let Longitude = (payload.readInt32BE(5) >> 8);
Longitude = Longitude * 180 / MaxEastPosition;
Longitude = Math.round(Longitude * 1000000) / 1000000;
// Extract altitude.
const Altitude = payload.readUInt16BE(8);
// Log extracted values.
console.log("\n" + `
LoRa parameters:
DataRate = DR_${DataRate}
CodingRate = ${["4/5", "4/8"][CodingRate]}
TX Power = ${Power} dBm
Location:
Latitude = ${Latitude}
Longitude = ${Longitude}
Altitude = ${Altitude} m
Other information:
Temperature = ${Temperature} °C
Links:
https://www.google.fr/maps/place/${Latitude},${Longitude}
`.trim() + "\n");
......@@ -18,14 +18,16 @@ This project is under the MIT license
#define PANIC(msg) core_panic(PANIC_GENERAL_ERROR, msg)
// Update UART line every .. ms
#define UART_UPDATE_MS 500
// Store information given by UART.
typedef struct {
mutex_t mutex_gps;
mutex_t mutex_cmd;
mutex_t mutex;
bool is_parsing;
uint8_t line_length;
char line[128];
} uart_isr_info_t;
} uart_info_t;
// Unique instance of UART info structure.
extern uart_isr_info_t uart_isr_info;
extern uart_info_t uart_info;
......@@ -35,10 +35,10 @@ static uint16_t bm_get_temperature(void)
int16_t temp;
ds75_read_temperature(&temp);
temp_cache = temp;
temp_cache = ((uint16_t)(temp + (55 << 8)) >> 6) & 0x1FF;
temp_last_usec = now;
return (uint16_t)(temp + (55 << 8)) & 0x1FF;
return temp_cache;
}
......@@ -48,12 +48,10 @@ void bm_encode_payload(uint8_t power_i, uint8_t dr_i, uint8_t cr_i)
// Retreive GPS data.
int32_t lat, lon;
int16_t alt;
//gps_get_binary(&lat, &lon, &alt);
lat = lon = alt = 0;
gps_get_binary(&lat, &lon, &alt);
// Retreive temperature.
(void)bm_get_temperature;
uint16_t temp = 0; // bm_get_temperature();
uint16_t temp = bm_get_temperature();
// Encode LoRa settings (on 8 bits).
......
......@@ -38,8 +38,10 @@ static const uint8_t resoltions[] = {
// Initialize DS75LX module.
uint8_t ds75_init(void)
{
uint8_t config = resoltions[DS75_DEFAULT_RES];
i2c_acquire(DS75_I2C_DEV);
int res = i2c_write_reg(DS75_I2C_DEV, DS75_I2C_ADDRESS, CONFIG_REG, DS75_RES9, 0);
int res = i2c_write_reg(DS75_I2C_DEV, DS75_I2C_ADDRESS, CONFIG_REG, config, 0);
i2c_release(DS75_I2C_DEV);
DS75_DEBUG("initialize error", res);
......@@ -59,7 +61,7 @@ uint8_t ds75_set_resolution(uint8_t resol)
config &= ~MASK_RESOLUTION;
config |= resoltions[resol];
res = i2c_write_reg(DS75_I2C_DEV, DS75_I2C_ADDRESS, CONFIG_REG, DS75_RES9, 0);
res = i2c_write_reg(DS75_I2C_DEV, DS75_I2C_ADDRESS, CONFIG_REG, config, 0);
i2c_release(DS75_I2C_DEV);
DS75_DEBUG("set resolution error", res);
......
......@@ -30,6 +30,9 @@ This project is under the MIT license
#define DS75_RES11 2
#define DS75_RES12 3
// Default resolution to use.
#define DS75_DEFAULT_RES DS75_RES10
/**
* @brief Initialize DS75LX module.
......
/*
Parse GPS data.
Copyright (C) 2019, ENSIMAG students
This file is based on LoRaMAC-node from Semtech, under Revised BSD License.
*/
#include "gps.h"
#include <string.h>
#include <stdlib.h>
// Various type of NMEA data we can receive with the GPS.
static const char NmeaDataTypeGPGGA[] = "GPGGA";
static const char NmeaDataTypeGPRMC[] = "GPRMC";
// Value used for the conversion of the position from DMS to decimal.
static const int32_t MaxNorthPosition = 8388607; // 2^23 - 1
static const int32_t MaxSouthPosition = 8388608; // -2^23
static const int32_t MaxEastPosition = 8388607; // 2^23 - 1
static const int32_t MaxWestPosition = 8388608; // -2^23
// GPS data in ASCII and numrical formats.
gps_nmea_t gps_nmea;
gps_data_t gps_data;
// Convert a nibble to hex char.
static int8_t nibble_to_hex(uint8_t a)
{
if (a < 10)
return '0' + a;
else if (a < 16)
return 'A' + (a - 10);
else
return '?';
}
// Convert GPS positions from double to binary values.
static void positions_to_binary(void)
{
long double temp;
if (gps_data.latitude >= 0) { // North
temp = gps_data.latitude * MaxNorthPosition;
gps_data.latitude_bin = temp / 90;
} else { // South
temp = gps_data.latitude * MaxSouthPosition;
gps_data.latitude_bin = temp / 90;
}
if (gps_data.longitude >= 0) { // East
temp = gps_data.longitude * MaxEastPosition;
gps_data.longitude_bin = temp / 180;
} else { // West
temp = gps_data.longitude * MaxWestPosition;
gps_data.longitude_bin = temp / 180;
}
}
// Convert GPS positions from ASCII to double values.
static void positions_to_double(void)
{
double valueTmp1, valueTmp2, valueTmp3, valueTmp4;
// Convert the latitude from ASCII to uint8_t values.
for (int i = 0 ; i < 10 ; i++ )
gps_nmea.latitude[i] = gps_nmea.latitude[i] & 0xF;
// Convert latitude from degree/minute/second (DMS) format into decimal.
valueTmp1 = (double)gps_nmea.latitude[0] * 10.0 + (double)gps_nmea.latitude[1];
valueTmp2 = (double)gps_nmea.latitude[2] * 10.0 + (double)gps_nmea.latitude[3];
valueTmp3 = (double)gps_nmea.latitude[5] * 1000.0 + (double)gps_nmea.latitude[6] * 100.0 +
(double)gps_nmea.latitude[7] * 10.0 + (double)gps_nmea.latitude[8];
gps_data.latitude = valueTmp1 + ((valueTmp2 + (valueTmp3 * 0.0001)) / 60.0);
if (gps_nmea.latitude_pole[0] == 'S')
gps_data.latitude *= -1;
// Convert the longitude from ASCII to uint8_t values.
for (int i = 0 ; i < 10 ; i++)
gps_nmea.longitude[i] = gps_nmea.longitude[i] & 0xF;
// Convert longitude from degree/minute/second (DMS) format into decimal.
valueTmp1 = (double)gps_nmea.longitude[0] * 100.0 + (double)gps_nmea.longitude[1] * 10.0 + (double)gps_nmea.longitude[2];
valueTmp2 = (double)gps_nmea.longitude[3] * 10.0 + (double)gps_nmea.longitude[4];
valueTmp3 = (double)gps_nmea.longitude[6] * 1000.0 + (double)gps_nmea.longitude[7] * 100;
valueTmp4 = (double)gps_nmea.longitude[8] * 10.0 + (double)gps_nmea.longitude[9];
gps_data.longitude = valueTmp1 + (valueTmp2 / 60.0) + (((valueTmp3 + valueTmp4) * 0.0001) / 60.0);
if (gps_nmea.longitude_pole[0] == 'W')
gps_data.longitude *= -1;
}
// Calculates the checksum for a NMEA sentence (and return the position of the
// checksum in the sentence).
static int32_t nmea_checksum(int8_t *nmeaStr, int32_t nmeaStrSize, int8_t *checksum)
{
int i = 0;
uint8_t checkNum = 0;
if ((nmeaStr == NULL) || (checksum == NULL) || (nmeaStrSize <= 1))
return -1;
if (nmeaStr[i] == '$')
i += 1; // Skip the first '$' if necessary.
while (nmeaStr[i] != '*') {
checkNum ^= nmeaStr[i]; // XOR until '*' or max length is reached.
i += 1;
if (i >= nmeaStrSize)
return -1;
}
// Convert checksum value to 2 hexadecimal characters.
checksum[0] = nibble_to_hex(checkNum / 16); // upper nibble
checksum[1] = nibble_to_hex(checkNum % 16); // lower nibble
return i + 1;
}
// Calculate the checksum of a NMEA frame and compare it to the checksum that
// is present at the end of it (return true if it matches).
static bool nmea_validate_checksum(int8_t *serialBuff, int32_t buffSize)
{
int32_t checksumIndex;
int8_t checksum[2]; // 2 characters to calculate NMEA checksum.
checksumIndex = nmea_checksum(serialBuff, buffSize, checksum);
if (checksumIndex < 0)
return false;
if (checksumIndex >= (buffSize - 2))
return false; // Not enough char in the serial buffer to read checksum.
return // Check the checksum.
(serialBuff[checksumIndex + 0] == checksum[0]) &&
(serialBuff[checksumIndex + 1] == checksum[1]);
}
// Format GPS data.
static void format_gps_data(void)
{
positions_to_double();
positions_to_binary();
if (gps_data.has_fix)
gps_data.altitude = atoi(gps_nmea.altitude);
}
// Read a field from RX buffer.
#define READ_FIELD(field, i, rxBuffer, maxSize) \
{ \
uint8_t __fs = 0; \
while ((rxBuffer)[(i) + __fs++] != ',') \
if (__fs > (maxSize)) return GPS_FAIL; \
for (uint8_t __j = 0; __j < __fs; __j++, (i)++) \
(field)[__j] = (rxBuffer)[i]; \
}
// Read a field from RX buffer.
#define SKIP_FIELD(i, rxBuffer, maxSize) \
{ \
uint8_t __fs = 0; \
while ((rxBuffer)[(i) + __fs++] != ',') \
if (__fs > (maxSize)) return GPS_FAIL; \
(i) += __fs; \
}
// Parse a GPGGA message.
static uint8_t parse_GPGGA(uint8_t i, int8_t *rxBuffer)
{
// NmeaUtcTime.
SKIP_FIELD(i, rxBuffer, 11);
// NmeaLatitude.
READ_FIELD(gps_nmea.latitude, i, rxBuffer, 10);
// NmeaLatitudePole.
READ_FIELD(gps_nmea.latitude_pole, i, rxBuffer, 2);
// NmeaLongitude.
READ_FIELD(gps_nmea.longitude, i, rxBuffer, 11);
// NmeaLongitudePole.
READ_FIELD(gps_nmea.longitude_pole, i, rxBuffer, 2);
// NmeaFixQuality.
READ_FIELD(gps_nmea.fix_quality, i, rxBuffer, 2);
// NmeaSatelliteTracked.
SKIP_FIELD(i, rxBuffer, 3);
// NmeaHorizontalDilution.
SKIP_FIELD(i, rxBuffer, 6);
// NmeaAltitude.
READ_FIELD(gps_nmea.altitude, i, rxBuffer, 8);
// NmeaAltitudeUnit.
SKIP_FIELD(i, rxBuffer, 2);
// NmeaHeightGeoid.
SKIP_FIELD(i, rxBuffer, 8);
// NmeaHeightGeoidUnit.
SKIP_FIELD(i, rxBuffer, 2);
gps_data.has_fix = (gps_nmea.fix_quality[0] > 0x30);
format_gps_data();
return GPS_SUCCESS;
}
// Parse a GPRMC message.
static uint8_t parse_GPRMC(uint8_t i, int8_t *rxBuffer)
{
// NmeaUtcTime.
SKIP_FIELD(i, rxBuffer, 11);
// NmeaDataStatus.
READ_FIELD(gps_nmea.fix_quality, i, rxBuffer, 2);
// NmeaLatitude.
READ_FIELD(gps_nmea.latitude, i, rxBuffer, 10);
// NmeaLatitudePole.
READ_FIELD(gps_nmea.latitude_pole, i, rxBuffer, 2);
// NmeaLongitude.
READ_FIELD(gps_nmea.longitude, i, rxBuffer, 11);
// NmeaLongitudePole.
READ_FIELD(gps_nmea.longitude_pole, i, rxBuffer, 2);
// NmeaSpeed.
SKIP_FIELD(i, rxBuffer, 8);
// NmeaDetectionAngle.
SKIP_FIELD(i, rxBuffer, 8);
// NmeaDate.
SKIP_FIELD(i, rxBuffer, 8);
gps_data.has_fix = (gps_nmea.fix_quality[0] == 0x41);
format_gps_data();
return GPS_SUCCESS;
}
// Parse GPS data.
uint8_t gps_parse_data(int8_t *rxBuffer, int32_t rxBufferSize)
{
if (!nmea_validate_checksum(rxBuffer, rxBufferSize))
return GPS_FAIL;
uint8_t i = 1;
READ_FIELD(gps_nmea.data_type, i, rxBuffer, 6);
if (strncmp(gps_nmea.data_type, NmeaDataTypeGPGGA, 5) == 0)
return parse_GPGGA(i, rxBuffer);
else if (strncmp(gps_nmea.data_type, NmeaDataTypeGPRMC, 5) == 0)
return parse_GPRMC(i, rxBuffer);
else
return GPS_FAIL;
}
// Reset GPS data.
void gps_reset_data(void)
{
gps_data.has_fix = false;
gps_data.altitude = 0xFFFF;
gps_data.latitude = 0;
gps_data.longitude = 0;
gps_data.latitude_bin = 0;
gps_data.longitude_bin = 0;
}
/*
Decode GPS data.
Parse GPS data.
Copyright (C) 2019, ENSIMAG students
This file is based on LoRaMAC-node from Semtech, under Revised BSD License.
......@@ -8,30 +8,262 @@ This file is based on LoRaMAC-node from Semtech, under Revised BSD License.
*/
#include "gps.h"
#include "app.h"
#include <string.h>
#include <stdlib.h>
// Mutex that protects GPS data.
static mutex_t gps_mutex = MUTEX_INIT;
// GPS parsed data.
extern gps_data_t gps_data;
// Various type of NMEA data we can receive with the GPS.
static const char NmeaDataTypeGPGGA[] = "GPGGA";
static const char NmeaDataTypeGPRMC[] = "GPRMC";
// Value used for the conversion of the position from DMS to decimal.
static const int32_t MaxNorthPosition = 8388607; // 2^23 - 1
static const int32_t MaxSouthPosition = 8388608; // -2^23
static const int32_t MaxEastPosition = 8388607; // 2^23 - 1
static const int32_t MaxWestPosition = 8388608; // -2^23
uint8_t gps_parse_data2(void)
// GPS data in ASCII and numrical formats.
gps_nmea_t gps_nmea;
gps_data_t gps_data;
// Convert a nibble to hex char.
static int8_t nibble_to_hex(uint8_t a)
{
if (a < 10)
return '0' + a;
else if (a < 16)
return 'A' + (a - 10);
else
return '?';
}
// Convert GPS positions from double to binary values.
static void positions_to_binary(void)
{
long double temp;
if (gps_data.latitude >= 0) { // North
temp = gps_data.latitude * MaxNorthPosition;
gps_data.latitude_bin = temp / 90;
} else { // South
temp = gps_data.latitude * MaxSouthPosition;
gps_data.latitude_bin = temp / 90;
}
if (gps_data.longitude >= 0) { // East
temp = gps_data.longitude * MaxEastPosition;
gps_data.longitude_bin = temp / 180;
} else { // West
temp = gps_data.longitude * MaxWestPosition;
gps_data.longitude_bin = temp / 180;
}
}
// Convert GPS positions from ASCII to double values.
static void positions_to_double(void)
{
// TODO: limit update.
double valueTmp1, valueTmp2, valueTmp3, valueTmp4;
// Convert the latitude from ASCII to uint8_t values.
for (int i = 0 ; i < 10 ; i++ )
gps_nmea.latitude[i] = gps_nmea.latitude[i] & 0xF;
// Convert latitude from degree/minute/second (DMS) format into decimal.
valueTmp1 = (double)gps_nmea.latitude[0] * 10.0 + (double)gps_nmea.latitude[1];
valueTmp2 = (double)gps_nmea.latitude[2] * 10.0 + (double)gps_nmea.latitude[3];
valueTmp3 = (double)gps_nmea.latitude[5] * 1000.0 + (double)gps_nmea.latitude[6] * 100.0 +
(double)gps_nmea.latitude[7] * 10.0 + (double)gps_nmea.latitude[8];
gps_data.latitude = valueTmp1 + ((valueTmp2 + (valueTmp3 * 0.0001)) / 60.0);
if (gps_nmea.latitude_pole[0] == 'S')
gps_data.latitude *= -1;
// Convert the longitude from ASCII to uint8_t values.
for (int i = 0 ; i < 10 ; i++)
gps_nmea.longitude[i] = gps_nmea.longitude[i] & 0xF;
// Convert longitude from degree/minute/second (DMS) format into decimal.
valueTmp1 = (double)gps_nmea.longitude[0] * 100.0 + (double)gps_nmea.longitude[1] * 10.0 + (double)gps_nmea.longitude[2];
valueTmp2 = (double)gps_nmea.longitude[3] * 10.0 + (double)gps_nmea.longitude[4];
valueTmp3 = (double)gps_nmea.longitude[6] * 1000.0 + (double)gps_nmea.longitude[7] * 100;
valueTmp4 = (double)gps_nmea.longitude[8] * 10.0 + (double)gps_nmea.longitude[9];
return 0;
gps_data.longitude = valueTmp1 + (valueTmp2 / 60.0) + (((valueTmp3 + valueTmp4) * 0.0001) / 60.0);
if (gps_nmea.longitude_pole[0] == 'W')
gps_data.longitude *= -1;
}
// Calculates the checksum for a NMEA sentence (and return the position of the
// checksum in the sentence).
static int32_t nmea_checksum(int8_t *nmeaStr, int32_t nmeaStrSize, int8_t *checksum)
{
int i = 0;
uint8_t checkNum = 0;
if ((nmeaStr == NULL) || (checksum == NULL) || (nmeaStrSize <= 1))
return -1;
if (nmeaStr[i] == '$')
i += 1; // Skip the first '$' if necessary.
while (nmeaStr[i] != '*') {
checkNum ^= nmeaStr[i]; // XOR until '*' or max length is reached.
i += 1;
if (i >= nmeaStrSize)
return -1;
}
// Convert checksum value to 2 hexadecimal characters.
checksum[0] = nibble_to_hex(checkNum / 16); // upper nibble
checksum[1] = nibble_to_hex(checkNum % 16); // lower nibble
return i + 1;
}
// Calculate the checksum of a NMEA frame and compare it to the checksum that
// is present at the end of it (return true if it matches).
static bool nmea_validate_checksum(int8_t *serialBuff, int32_t buffSize)
{
int32_t checksumIndex;
int8_t checksum[2]; // 2 characters to calculate NMEA checksum.
checksumIndex = nmea_checksum(serialBuff, buffSize, checksum);
if (checksumIndex < 0)
return false;
if (checksumIndex >= (buffSize - 2))
return false; // Not enough char in the serial buffer to read checksum.
return // Check the checksum.
(serialBuff[checksumIndex + 0] == checksum[0]) &&
(serialBuff[checksumIndex + 1] == checksum[1]);
}
// Format GPS data.
static void format_gps_data(void)
{
positions_to_double();
positions_to_binary();
if (gps_data.has_fix)
gps_data.altitude = atoi(gps_nmea.altitude);
}
// Read a field from RX buffer.
#define READ_FIELD(field, i, rxBuffer, maxSize) \
{ \
uint8_t __fs = 0; \
while ((rxBuffer)[(i) + __fs++] != ',') \
if (__fs > (maxSize)) return GPS_FAIL; \
for (uint8_t __j = 0; __j < __fs; __j++, (i)++) \
(field)[__j] = (rxBuffer)[i]; \
}
// Read a field from RX buffer.