GPSProvider wrapper library for STMicroelectronics' X-NUCLEO-GNSS1A1 Expansion Board.

Dependents:   TeseoLocation

X_NUCLEO_GNSS1A1 Library

GPS_Provider wrapper library for STMicroelectronics' X-NUCLEO-GNSS1A1 component.

Overview

This library includes drivers for ST’s Teseo-LIV3F Global Navigation Satellite System (GNSS) device and middleware for the NMEA protocol support. This firmware package implements the port of the GPS_Provider to STMicroelectronics' X-NUCLEO-GNSS1A1 GNSS Expansion Board.

The key features of the library are:

  • Complete software to build applications using Teseo-LIV3F GNSS device
  • Middleware for the NMEA protocol support

Furthermore the library provides the following advanced features:

  • Geofencing - allows the Teseo-LIV3F receiver to raise a NMEA message when the resolved GNSS position is close to or entering or exiting from a specific circle
  • Odometer - provides information on the traveled distance using only the resolved GNSS position
  • Data Logging - allows the Teseo-LIV3F receiver to save locally on the flash the resolved GNSS position to be retrieved on demand from the Host

Hardware description

The X-NUCLEO-GNSS1A1 is a Global Navigation Satellite System Expansion Board usable with the STM32 Nucleo system and other Arduino compatible platforms. It is designed around the STMicroelectronics Teseo-LIV3F GNSS receiver IC working on multiple constellations (GPS/Galileo/Glonass/BeiDou/QZSS).

The Teseo-LIV3F module is designed for top performance in a minimal space. Within its 10x10mm compact size, Teseo-LIV3F offers superior accuracy thanks to the on board 26MHz Temperature Compensated Crystal Oscillator (TCXO) and a reduced Time To First Fix (TTFF) relying to its dedicated 32KHz Real Time Clock (RTC) oscillator.

The X-NUCLEO-GNSS1A1, hosting the Teseo-LIV3F, is compatible with Arduino UNO R3 connector layout and interfaces with the MCU via the UART channel. To connect by serial port the GNSS receiver and the host the following parameters must be used:

  • 8 data bits
  • No parity
  • 1 stop bit
  • 9600 bauds

A GPS/GLONASS/Beidou antenna, distributed along with the X-NUCLEO-GNSS1A1 Expansion Board, must be connected to the antenna connector present on the Expansion Board. For the X-NUCLEO-GNSS1A1 proper operations, the following jumper settings must be used:

  • J2 open
  • J3 closed
  • J4 closed
  • J5 open
  • J6 closed
  • J7 closed
  • J8 open
  • J9 closed
  • J10 open
  • J11 closed
  • J12 closed
  • J13 closed
  • J14 closed
  • J15 closed

Tested platforms

This firmware has been tested on STM32 NUCLEO-F401RE

Example Application

To run GNSS example applications using X-NUCLEO-GNSS1A1 Expansion Board based on mbed OS, please refer to TeseoLocation page.

createTeseoGNSS.cpp

Committer:
apalmieri
Date:
2019-02-14
Revision:
4:9d0addf682f0
Parent:
0:a77f1f1f8318
Child:
5:1fe1ba1f0013

File content as of revision 4:9d0addf682f0:

/**
 ******************************************************************************
 * @file    createTeseoGNSS.cpp
 * @author  AST/CL
 * @version V1.1.0
 * @date    May, 2017
 * @brief   .
 ******************************************************************************
 * @attention
 *
 * <h2><center>&copy; COPYRIGHT(c) 2017 STMicroelectronics</center></h2>
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *   1. Redistributions of source code must retain the above copyright notice,
 *      this list of conditions and the following disclaimer.
 *   2. Redistributions in binary form must reproduce the above copyright notice,
 *      this list of conditions and the following disclaimer in the documentation
 *      and/or other materials provided with the distribution.
 *   3. Neither the name of STMicroelectronics nor the names of its contributors
 *      may be used to endorse or promote products derived from this software
 *      without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 ******************************************************************************
 */

#include "mbed.h"
#include "Teseo.h"
#include "GPSProviderImplBase.h"

#include "TeseoConfig.h"

static void _AppOutputCallback(uint32_t msgId, uint32_t msgType, tTeseoData *pData);
static void _AppEventCallback(eTeseoLocEventType event, uint32_t data);
static char msg[256];

static char *geofenceCirclePosition[] = {
  "Unknown",
  "Outside",
  "Boundary",
  "Inside"
};

extern Serial serialDebug;
#define TESEO_APP_LOG_INFO(...) serialDebug.printf(__VA_ARGS__)

GPSProviderImplBase *
createGPSProviderInstance(void)
{
    static Teseo gnss(TESEO_PIN_RESET,
                      TESEO_PIN_WAKEUP,
                      TESEO_PIN_PPS,
                      TESEO_PIN_TX,
                      TESEO_PIN_RX,
                      &serialDebug);

    /* Register output callback and event callback functions */
    gnss.TeseoLocRegOutput(_AppOutputCallback, _AppEventCallback);

    return &gnss;
}

/** 
 * @brief  This function prints on the console the info about Fix data for single or combined satellite navigation system
 * @param  pData
 * @retval None
 */
static void
GetGNSMsgInfos(tTeseoData *pData)
{
//  char msg[256];
  
  TESEO_APP_LOG_INFO("\r\n");
  
  sprintf(msg, "Constellation:\t\t[ %s ]\t",
          pData->gns_data.constellation);
  TESEO_APP_LOG_INFO(msg);
  
  if (strcmp(pData->gns_data.constellation, "$GPGSV") == 0) {
    TESEO_APP_LOG_INFO("-- only GPS constellation is enabled\n\r");  
  }
  else if (strcmp(pData->gns_data.constellation, "$GLGSV") == 0) {
    TESEO_APP_LOG_INFO("-- only GLONASS constellation is enabled\n\r");
  }
  else if (strcmp(pData->gns_data.constellation, "$GAGSV") == 0) {
    TESEO_APP_LOG_INFO("-- only GALILEO constellation is enabled\n\r");
  }
  else if (strcmp(pData->gns_data.constellation, "$BDGSV") == 0) {
    TESEO_APP_LOG_INFO("-- only BEIDOU constellation is enabled\n\r");    
  }
  else if (strcmp(pData->gns_data.constellation, "$QZGSV") == 0) {
    TESEO_APP_LOG_INFO("-- only QZSS constellation is enabled\n\r");
  }
  else if (strcmp(pData->gns_data.constellation, "$GNGSV") == 0) {
    TESEO_APP_LOG_INFO("-- message to report all satellites for all enabled constellations\n\r");   
  }
  
  sprintf(msg, "UTC:\t\t\t[ %02d:%02d:%02d ]\n\r",
          pData->gns_data.utc.hh, 
          pData->gns_data.utc.mm, 
          pData->gns_data.utc.ss);
  TESEO_APP_LOG_INFO(msg);
  
  sprintf(msg,"Latitude:\t\t[ %.0f' %d'' %c ]\n\r",
          (pData->gns_data.xyz.lat - ((int)pData->gns_data.xyz.lat % 100)) / 100, 
          ((int)pData->gns_data.xyz.lat % 100), 
          pData->gns_data.xyz.ns);          
  TESEO_APP_LOG_INFO(msg);
  
  sprintf(msg,"Longitude:\t\t[ %.0f' %d'' %c ]\n\r",
          (pData->gns_data.xyz.lon - ((int)pData->gns_data.xyz.lon % 100)) / 100, 
          ((int)pData->gns_data.xyz.lon % 100),
          pData->gns_data.xyz.ew);
  TESEO_APP_LOG_INFO(msg);
  
  sprintf(msg,"Satellites locked:\t[ %d ]\n\r",
          pData->gns_data.sats);
  TESEO_APP_LOG_INFO(msg);
  
  sprintf(msg,"HDOP:\t\t\t[ %.01f ]\n\r",
          pData->gns_data.hdop);
  TESEO_APP_LOG_INFO(msg);
  
  sprintf(msg,"Altitude:\t\t[ %.01f ]\n\r",
          pData->gns_data.xyz.alt);
  TESEO_APP_LOG_INFO(msg);
  
  sprintf(msg,"Geoid infos:\t\t[ %.01f ]\n\r",
          pData->gns_data.geo_sep);
  TESEO_APP_LOG_INFO(msg);
  
  //  sprintf(msg,"ID - Checksum:\t\t[ *%x ]\n\r",
  //          (pData->gns_data.checksum);
  //  TESEO_APP_LOG_INFO(msg);
  
  TESEO_APP_LOG_INFO("\n\n\r");
  
  return;
}

/** 
 * @brief  This function prints on the console the info about GPS Pseudorange Noise Statistics
 * @param  pData
 * @retval None
 */
static void
GetGPGSTInfos(tTeseoData *pData)
{
//  char msg[256];
  
  TESEO_APP_LOG_INFO("\r\n");
    
  sprintf(msg, "UTC:\t\t\t[ %02d:%02d:%02d ]\n\r",
          pData->gpgst_data.utc.hh, 
          pData->gpgst_data.utc.mm, 
          pData->gpgst_data.utc.ss);
  TESEO_APP_LOG_INFO(msg);
    
  sprintf(msg,"EHPE:\t\t[ %.01f ]\n\r",
          pData->gpgst_data.EHPE);
  TESEO_APP_LOG_INFO(msg);
    
  sprintf(msg,"Semi-major Dev:\t\t[ %.01f ]\n\r",
          pData->gpgst_data.semi_major_dev);
  TESEO_APP_LOG_INFO(msg);
    
  sprintf(msg,"Semi-minor Dev:\t\t[ %.01f ]\n\r",
          pData->gpgst_data.semi_minor_dev);
  TESEO_APP_LOG_INFO(msg);
  
  sprintf(msg,"Semi-maior Angle:\t\t[ %.01f ]\n\r",
          pData->gpgst_data.semi_major_angle);
  TESEO_APP_LOG_INFO(msg);
      
  sprintf(msg,"Lat Err Dev:\t\t[ %.01f ]\n\r",
          pData->gpgst_data.lat_err_dev);
  TESEO_APP_LOG_INFO(msg);
  
  sprintf(msg,"Lon Err Dev:\t\t[ %.01f ]\n\r",
          pData->gpgst_data.lon_err_dev);
  TESEO_APP_LOG_INFO(msg);
  
  sprintf(msg,"Alt Err Dev:\t\t[ %.01f ]\n\r",
          pData->gpgst_data.alt_err_dev);
  TESEO_APP_LOG_INFO(msg);
    
//  sprintf(msg,"ID - Checksum:\t\t[ *%x ]\n\r",
//          pData->gpgst_data.checksum);
//  TESEO_APP_LOG_INFO(msg);
    
  TESEO_APP_LOG_INFO("\n\n\r");

  return;
}

/** 
 * @brief  This function prints on the console the info about Recommended Minimum Specific GPS/Transit data got by the most recent reception process
 * @param  pData
 * @retval None
 */
static void
GetGPRMCInfos(tTeseoData *pData)
{
//  char msg[256];
  
  TESEO_APP_LOG_INFO("\r\n");
    
  sprintf(msg, "UTC:\t\t\t\t[ %02d:%02d:%02d ]\n\r",
          pData->gprmc_data.utc.hh, 
          pData->gprmc_data.utc.mm, 
          pData->gprmc_data.utc.ss);
  TESEO_APP_LOG_INFO(msg);
    
  sprintf(msg,"Status:\t\t\t\t[ %c ]\t\t",
          pData->gprmc_data.status);
  TESEO_APP_LOG_INFO(msg);
  if (pData->gprmc_data.status == 'A') {
    TESEO_APP_LOG_INFO("-- Valid (reported in 2D and 3D fix conditions)\n\r");
  }
  else if (pData->gprmc_data.status == 'V') {
    TESEO_APP_LOG_INFO("-- Warning (reported in NO FIX conditions)\n\r");
  }
  else {
    TESEO_APP_LOG_INFO("-- Unknown status\n\r");
  }
    
  sprintf(msg,"Latitude:\t\t\t[ %.0f' %02d'' %c ]\n\r",
          ((pData->gprmc_data.xyz.lat - ((int)pData->gprmc_data.xyz.lat % 100))) / 100, 
          ((int)pData->gprmc_data.xyz.lat % 100), 
          pData->gprmc_data.xyz.ns);          
  TESEO_APP_LOG_INFO(msg);
  
  sprintf(msg,"Longitude:\t\t\t[ %.0f' %02d'' %c ]\n\r",
          ((pData->gprmc_data.xyz.lon - ((int)pData->gprmc_data.xyz.lon % 100))) / 100, 
          ((int)pData->gprmc_data.xyz.lon % 100),
          pData->gprmc_data.xyz.ew);
  TESEO_APP_LOG_INFO(msg);
    
  sprintf(msg,"Speed over ground (knots):\t[ %.01f ]\n\r",
          pData->gprmc_data.speed);
  TESEO_APP_LOG_INFO(msg);
  
  sprintf(msg,"Trackgood:\t\t\t[ %.01f ]\n\r",
          pData->gprmc_data.trackgood);
  TESEO_APP_LOG_INFO(msg);
  
  sprintf(msg,"Date (ddmmyy):\t\t\t[ %d ]\n\r",
          pData->gprmc_data.date);
  TESEO_APP_LOG_INFO(msg);
    
  sprintf(msg,"Magnetic Variation:\t\t[ %.01f ]\n\r",
          pData->gprmc_data.mag_var);
  TESEO_APP_LOG_INFO(msg);
  
  if (pData->gprmc_data.mag_var_dir != 'E' &&
      pData->gprmc_data.mag_var_dir != 'W') {
    sprintf(msg,"Magnetic Var. Direction:\t[ - ]\n\r");
  }
  else {
    sprintf(msg,"Magnetic Var. Direction:\t[ %c ]\n\r",
          pData->gprmc_data.mag_var_dir);
  }
  TESEO_APP_LOG_INFO(msg);
  
//  sprintf(msg,"Checksum:\t\t[ *%x ]\n\r",
//          pData->gprmc_data.checksum);
//  TESEO_APP_LOG_INFO(msg);
    
  TESEO_APP_LOG_INFO("\n\n\r");

  return;
}

/** 
 * @brief  This function prints on the console the info about GNSS satellites got by the most recent reception process
 * @param  pData
 * @retval None
 */
static void
GetGSAMsgInfos(tTeseoData *pData)
{
//  char msg[256];
  
  TESEO_APP_LOG_INFO("\r\n");
  
  sprintf(msg, "Constellation:\t\t[ %s ]\t",
          pData->gsa_data.constellation);
  TESEO_APP_LOG_INFO(msg);
  
  if (strcmp(pData->gsa_data.constellation, "$GPGSA") == 0) {
    TESEO_APP_LOG_INFO("-- only GPS constellation is enabled\n\r");    
  }
  else if (strcmp(pData->gsa_data.constellation, "$GLGSA") == 0) {
    TESEO_APP_LOG_INFO("-- only GLONASS constellation is enabled\n\r");
  }
  else if (strcmp(pData->gsa_data.constellation, "$GAGSA") == 0) {
    TESEO_APP_LOG_INFO("-- only GALILEO constellation is enabled\n\r");
  }
  else if (strcmp(pData->gsa_data.constellation, "$BDGSA") == 0) {
    TESEO_APP_LOG_INFO("-- only BEIDOU constellation is enabled\n\r");
  }
  else if (strcmp(pData->gsa_data.constellation, "$GNGSA") == 0) {
    TESEO_APP_LOG_INFO("-- more than one constellation is enabled\n\r");   
  }
  
  sprintf(msg, "Operating Mode:\t\t[ %c ]\t\t",
          pData->gsa_data.operating_mode);
  TESEO_APP_LOG_INFO(msg);
  if (pData->gsa_data.operating_mode == 'A') {
    TESEO_APP_LOG_INFO("-- Auto (2D/3D)\n\r");
  }
  else if (pData->gsa_data.operating_mode == 'M') {
    TESEO_APP_LOG_INFO("-- Manual\n\r");
  }
  
  sprintf(msg,"Current Mode:\t\t[ %d ]\t\t",
          pData->gsa_data.current_mode);          
  TESEO_APP_LOG_INFO(msg);
  if (pData->gsa_data.current_mode == 1) {
    TESEO_APP_LOG_INFO("-- no fix available\n\r");
  }
  else if (pData->gsa_data.current_mode == 2) {
    TESEO_APP_LOG_INFO("-- 2D\n\r");
  }
  else if (pData->gsa_data.current_mode == 3) {
    TESEO_APP_LOG_INFO("-- 3D\n\r");
  }
  
  for (uint8_t i=0; i<12; i++) {  
    sprintf(msg,"SatPRN%02d:\t\t[ %d ]\n\r", i+1,
            pData->gsa_data.sat_prn[i]);
    TESEO_APP_LOG_INFO(msg);
  }
  
  sprintf(msg,"PDOP:\t\t\t[ %.01f ]\n\r",
          pData->gsa_data.pdop);
  TESEO_APP_LOG_INFO(msg);
  
  sprintf(msg,"HDOP:\t\t\t[ %.01f ]\n\r",
          pData->gsa_data.hdop);
  TESEO_APP_LOG_INFO(msg);
  
  sprintf(msg,"VDOP:\t\t\t[ %.01f ]\n\r",
          pData->gsa_data.vdop);
  TESEO_APP_LOG_INFO(msg);
  
//  sprintf(msg,"ID - Checksum:\t\t[ *%x ]\n\r",
//          pData->gsa_data.checksum);
//  TESEO_APP_LOG_INFO(msg);
  
  TESEO_APP_LOG_INFO("\n\n\r");
  
}
            
/** 
 * @brief  This function prints on the console the info about GNSS satellites got by the most recent reception process
 * @param  pData
 * @retval None
 */
static void
GetGSVMsgInfos(tTeseoData *pData)
{
  uint8_t i;
  uint8_t tot_sats = pData->gsv_data.tot_sats;
//  char msg[256];
  
  char degree_sym = 248;
  
  TESEO_APP_LOG_INFO("\r\n");
  
  sprintf(msg, "Constellation:\t\t[ %s ]\t",
          pData->gsv_data.constellation);
  TESEO_APP_LOG_INFO(msg);
  
  if (strcmp(pData->gsv_data.constellation, "$GPGSV") == 0) {
    TESEO_APP_LOG_INFO("-- message to report all GPS satellites\n\r");    
  }
  else if (strcmp(pData->gsv_data.constellation, "$GLGSV") == 0) {
    TESEO_APP_LOG_INFO("-- message to report all GLONASS satellites\n\r");
  }
  else if (strcmp(pData->gsv_data.constellation, "$GAGSV") == 0) {
    TESEO_APP_LOG_INFO("-- message to report all GALILEO satellites\n\r");
  }
  else if (strcmp(pData->gsv_data.constellation, "$BDGSV") == 0) {
    TESEO_APP_LOG_INFO("-- message to report all BEIDOU satellites\n\r");
  }
  else if (strcmp(pData->gsv_data.constellation, "$QZGSV") == 0) {
    TESEO_APP_LOG_INFO("-- message to report all QZSS satellites\n\r");
  }
  else if (strcmp(pData->gsv_data.constellation, "$GNGSV") == 0) {
    TESEO_APP_LOG_INFO("-- message to report all satellites for all enabled constellations\n\r");   
  }
  
  /* debug */
//  sprintf(msg,"Tot Messages:\t\t[ %d ]\n\r",
//          ((tTeseoData *)(handle->pData))->gsv_data.amount);
//  TESEO_APP_LOG_INFO(msg);
//  
//  sprintf(msg,"Message Num:\t\t[ %d ]\n\r",
//          ((tTeseoData *)(handle->pData))->gsv_data.number);
//  TESEO_APP_LOG_INFO(msg);
  
  sprintf(msg,"Num of Satellites:\t[ %d ]\n\r", tot_sats);
  TESEO_APP_LOG_INFO(msg);
  
  TESEO_APP_LOG_INFO("\n\r");
  
  for (i=0; i<tot_sats; i++) {
    sprintf(msg,"Sat%02dPRN:\t\t[ %03d ]\n\r", i+1, 
            pData->gsv_data.gsv_sat_i[i].prn);
    TESEO_APP_LOG_INFO(msg);
    
    sprintf(msg,"Sat%02dElev (%c):\t\t[ %03d ]\n\r", i+1, degree_sym,
            pData->gsv_data.gsv_sat_i[i].elev);
    TESEO_APP_LOG_INFO(msg);
    
    sprintf(msg,"Sat%02dAzim (%c):\t\t[ %03d ]\n\r", i+1, degree_sym,
            pData->gsv_data.gsv_sat_i[i].azim);
    TESEO_APP_LOG_INFO(msg);
    
    sprintf(msg,"Sat%02dCN0 (dB):\t\t[ %03d ]\n\r", i+1, 
            pData->gsv_data.gsv_sat_i[i].cn0);
    TESEO_APP_LOG_INFO(msg);  
    
    TESEO_APP_LOG_INFO("\n\r");
  }
  
  TESEO_APP_LOG_INFO("\r\n");
  
}

/** 
 * @brief  This function prints on the console the info Geofence
 * @param  pData
 * @retval None
 */
static void
GetGeofenceInfos(tTeseoData *pData)
{
//  char msg[256];
  
  TESEO_APP_LOG_INFO("\r\n");

  if(pData->geofence_data.op == GNSS_FEATURE_EN_MSG) {
    sprintf(msg, "Geofence Enabling:\t\t[ %s ]\t",
            pData->geofence_data.result ? "ERROR" : "OK");
    TESEO_APP_LOG_INFO(msg);
  }
  if(pData->geofence_data.op == GNSS_GEOFENCE_CFG_MSG) {
    sprintf(msg, "Geofence Configuration:\t\t[ %s ]\t",
          pData->geofence_data.result ? "ERROR" : "OK");
    TESEO_APP_LOG_INFO(msg);
  }
  if(pData->geofence_data.op == GNSS_GEOFENCE_STATUS_MSG) {
    sprintf(msg, "Geofence Status:\t\t[ %s ]\t",
          pData->geofence_data.result ? "ERROR" : "OK");
    TESEO_APP_LOG_INFO(msg);
    if(pData->geofence_data.result == 0) {
      TESEO_APP_LOG_INFO("\r\n");
      sprintf(msg, "Time/Date:\t\t%02d:%02d:%02d %02d/%02d/%04d\n",
          pData->geofence_data.timestamp.hh,
          pData->geofence_data.timestamp.mm,
          pData->geofence_data.timestamp.ss,
          pData->geofence_data.timestamp.day,
          pData->geofence_data.timestamp.month,
          pData->geofence_data.timestamp.year);
      TESEO_APP_LOG_INFO(msg);
      
      for(uint8_t i = 0; i<MAX_GEOFENCES_NUM; i++) {
        sprintf(msg, "Position circle[%d]:\t%s\n",
          i, geofenceCirclePosition[pData->geofence_data.status[i]]);
        TESEO_APP_LOG_INFO(msg);
      }
    }
  }
  if(pData->geofence_data.op == GNSS_GEOFENCE_ALARM_MSG) {
    sprintf(msg, "Geofence Alarm:\t\t[ %s ]\t",
          pData->geofence_data.result ? "ERROR" : "OK");
    TESEO_APP_LOG_INFO(msg);
    if(pData->geofence_data.result == 0) {
      TESEO_APP_LOG_INFO("\r\n");
      sprintf(msg, "Time:\t\t%02d:%02d:%02d\n",
          pData->geofence_data.timestamp.hh,
          pData->geofence_data.timestamp.mm,
          pData->geofence_data.timestamp.ss);
      TESEO_APP_LOG_INFO(msg);
      int i = pData->geofence_data.idAlarm;
      sprintf(msg, "Position circle[%d]:\t%s\n",
              i, geofenceCirclePosition[pData->geofence_data.status[i]]);
      TESEO_APP_LOG_INFO(msg);
    }
  }  
  TESEO_APP_LOG_INFO("\r\n");
  
}

/** 
 * @brief  This function prints on the console the info about Odometer
 * @param  pData
 * @retval None
 */
static void
GetOdometerInfos(tTeseoData *pData)
{
//  char msg[256];
  
  TESEO_APP_LOG_INFO("\r\n");
  
  if(pData->odo_data.op == GNSS_FEATURE_EN_MSG) {
    sprintf(msg, "Odometer Enabling:\t\t[ %s ]\t",
            pData->odo_data.result ? "ERROR" : "OK");
    TESEO_APP_LOG_INFO(msg);
  }
  if((pData->odo_data.op == GNSS_ODO_START_MSG) ||
     (pData->odo_data.op == GNSS_ODO_STOP_MSG)) {
    sprintf(msg, "Odometer Operation:\t\t[ %s ]\t",
          pData->odo_data.result ? "ERROR" : "OK");
    TESEO_APP_LOG_INFO(msg);
  }  
  TESEO_APP_LOG_INFO("\r\n");
  
}

/** 
 * @brief  This function prints on the console the info about Datalog
 * @param  pData
 * @retval None
 */
static void
GetDatalogInfos(tTeseoData *pData)
{
//  char msg[256];
  
  TESEO_APP_LOG_INFO("\r\n");
  
  if(pData->datalog_data.op == GNSS_FEATURE_EN_MSG) {
    sprintf(msg, "Datalog Enabling:\t\t[ %s ]\t",
            pData->datalog_data.result ? "ERROR" : "OK");
    TESEO_APP_LOG_INFO(msg);
  }
  if(pData->datalog_data.op == GNSS_DATALOG_CFG_MSG) {
    sprintf(msg, "Datalog Configuring:\t\t[ %s ]\t",
            pData->datalog_data.result ? "ERROR" : "OK");
    TESEO_APP_LOG_INFO(msg);
  }
  if(pData->datalog_data.op == GNSS_DATALOG_START_MSG) {
    sprintf(msg, "Datalog Start:\t\t[ %s ]\t",
            pData->datalog_data.result ? "ERROR" : "OK");
    TESEO_APP_LOG_INFO(msg);
  }
  if(pData->datalog_data.op == GNSS_DATALOG_STOP_MSG) {
    sprintf(msg, "Datalog Stop:\t\t[ %s ]\t",
            pData->datalog_data.result ? "ERROR" : "OK");
    TESEO_APP_LOG_INFO(msg);
  }
  if(pData->datalog_data.op == GNSS_DATALOG_ERASE_MSG) {
    sprintf(msg, "Datalog Erase:\t\t[ %s ]\t",
            pData->datalog_data.result ? "ERROR" : "OK");
    TESEO_APP_LOG_INFO(msg);
  }
  TESEO_APP_LOG_INFO("\r\n");
  
}

/** 
 * @brief  This function prints on the console the ack about Message List cfg
 * @param  pData
 * @retval None
 */
static void
GetMsgListAck(tTeseoData *pData)
{
//  char msg[256];
  
  TESEO_APP_LOG_INFO("\r\n");
  
  sprintf(msg, "Msg List config:\t\t[ %s ]\t",
          pData->ack ? "ERROR" : "OK");
  TESEO_APP_LOG_INFO(msg);
    
  TESEO_APP_LOG_INFO("\r\n");
}

/** 
 * @brief  This function prints on the console the ack about Message List cfg
 * @param  pData
 * @retval None
 */
static void
GetAck(tTeseoData *pData)
{
//  char msg[256];
  
  TESEO_APP_LOG_INFO("\r\n");
  
  sprintf(msg, "Params configuration:\t\t[ %s ]\t",
          pData->ack ? "ERROR" : "OK");
  TESEO_APP_LOG_INFO(msg);
    
  TESEO_APP_LOG_INFO("\r\n");
}

void
_AppOutputCallback(uint32_t msgId, uint32_t msgType, tTeseoData *pData)
{
  switch (msgId) {
  case LOC_OUTPUT_LOCATION: {
    // Output last location
    TESEO_APP_LOG_INFO("Loc: lat=%lf, lon=%lf, alt=%f\r\n", pData->gpgga_data.xyz.lat, pData->gpgga_data.xyz.lon, pData->gpgga_data.xyz.alt);
    break;
  }
  case LOC_OUTPUT_NMEA: {
    //return;
    Teseo::eMsg msg = (Teseo::eMsg)msgType;
    switch(msg) {
    case Teseo::GNS:
      // GET Fix data for single or combined Satellite navigation system
      GetGNSMsgInfos(pData);
      break;
      
    case Teseo::GPGST:
      // GET GPS Pseudorange Noise Statistics
      GetGPGSTInfos(pData);
      break;
      
    case Teseo::GPRMC:
      // GET Recommended Minimum Specific GPS/Transit data
      GetGPRMCInfos(pData);
      break;
      
    case Teseo::GSA:
      // GET GPS DOP and Active Satellites
      GetGSAMsgInfos(pData);
      break;
      
    case Teseo::GSV:
      // GET GPS Satellites in View
      GetGSVMsgInfos(pData);
      break;
      
    default:
      break;
    }
    break;
  }
  case LOC_OUTPUT_PSTM: {
    Teseo::ePSTMsg msg = (Teseo::ePSTMsg)msgType;
    switch(msg) {
    case Teseo::PSTMGEOFENCE:
      // GET Geofence info
      GetGeofenceInfos(pData);
      break;
    case Teseo::PSTMODO:
      // GET Geofence info
      GetOdometerInfos(pData);
      break;
    case Teseo::PSTMDATALOG:
      // GET Datalog info
      GetDatalogInfos(pData);
      break;
    case Teseo::PSTMSGL:
      // GET Message List ack
      GetMsgListAck(pData);
      break;
    case Teseo::PSTMSAVEPAR:
      // GET SAVE PAR ack
      GetAck(pData);
      break;
    default:
      break;
    }
    break;
  }
  
  default:
    break;
  }
}


void
_AppEventCallback(eTeseoLocEventType event, uint32_t data)
{
    switch (event) {
        case TESEO_LOC_EVENT_START_RESULT:
            if (data != 0) {
                TESEO_APP_LOG_INFO("start failed.\r\n");
            } else {
                TESEO_APP_LOG_INFO("start OK.\r\n");
            }
            break;
        case TESEO_LOC_EVENT_STOP_RESULT:
            if (data != 0) {
                TESEO_APP_LOG_INFO("stop failed.\r\n");
            } else {
                TESEO_APP_LOG_INFO("stop OK.\r\n");
            }
            break;
        default:
            break;
    }
}