2015-01-20 15:30:25 -04:00
// -*- tab-width: 4; Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*-
2014-07-28 19:21:07 -03:00
/*
Inspired by work done here https : //github.com/PX4/Firmware/tree/master/src/drivers/frsky_telemetry from Stefan Rado <px4@sradonia.net>
This program 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 .
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 .
You should have received a copy of the GNU General Public License
along with this program . If not , see < http : //www.gnu.org/licenses/>.
*/
/*
FRSKY Telemetry library
*/
2015-08-11 03:28:43 -03:00
# include "AP_Frsky_Telem.h"
2014-07-28 19:21:07 -03:00
extern const AP_HAL : : HAL & hal ;
2015-01-22 03:13:11 -04:00
//constructor
2016-05-03 14:43:16 -03:00
AP_Frsky_Telem : : AP_Frsky_Telem ( AP_AHRS & ahrs , const AP_BattMonitor & battery ) :
2015-01-22 03:13:11 -04:00
_ahrs ( ahrs ) ,
_battery ( battery ) ,
_port ( NULL ) ,
2016-05-03 14:43:16 -03:00
_protocol ( ) ,
2015-01-22 03:13:11 -04:00
_crc ( 0 ) ,
2016-05-03 14:43:16 -03:00
_control_mode ( ) ,
_gps ( )
2015-01-22 03:13:11 -04:00
{ }
2016-05-03 14:43:16 -03:00
/*
* init - perform required initialisation
*/
void AP_Frsky_Telem : : init ( const AP_SerialManager & serial_manager , uint8_t * control_mode )
2014-07-28 19:21:07 -03:00
{
2016-05-03 14:43:16 -03:00
// check for protocol configured for a serial port - only the first serial port with one of these protocols will then run (cannot have FrSky on multiple serial ports)
if ( ( _port = serial_manager . find_serial ( AP_SerialManager : : SerialProtocol_FrSky_D , 0 ) ) ) {
_protocol = AP_SerialManager : : SerialProtocol_FrSky_D ; // FrSky D protocol (D-receivers)
} else if ( ( _port = serial_manager . find_serial ( AP_SerialManager : : SerialProtocol_FrSky_SPort , 0 ) ) ) {
_protocol = AP_SerialManager : : SerialProtocol_FrSky_SPort ; // FrSky SPort protocol (X-receivers)
2015-01-19 09:37:01 -04:00
}
2015-01-29 00:53:41 -04:00
2016-05-03 14:43:16 -03:00
_control_mode = control_mode ;
2015-01-29 00:53:41 -04:00
if ( _port ! = NULL ) {
2016-05-03 14:43:16 -03:00
hal . scheduler - > register_io_process ( FUNCTOR_BIND_MEMBER ( & AP_Frsky_Telem : : tick , void ) ) ;
2015-01-29 00:53:41 -04:00
// we don't want flow control for either protocol
_port - > set_flow_control ( AP_HAL : : UARTDriver : : FLOW_CONTROL_DISABLE ) ;
}
2015-01-20 15:30:25 -04:00
}
2015-01-27 07:35:46 -04:00
/*
2016-05-03 19:03:49 -03:00
* send telemetry data
2016-05-03 14:43:16 -03:00
* for FrSky SPort protocol ( X - receivers )
*/
2016-05-03 19:03:49 -03:00
void AP_Frsky_Telem : : send_SPort ( void )
2016-05-03 14:43:16 -03:00
{
// variables keeping track of which data to send
static uint8_t fas_call = 1 ;
static uint8_t gps_call = 1 ;
static uint8_t vario_call = 1 ;
static uint8_t various_call = 1 ;
while ( _port - > available ( ) ) {
uint8_t readbyte = _port - > read ( ) ;
2016-05-03 19:03:49 -03:00
if ( readbyte = = START_STOP_SPORT ) { // byte 0x7E is the header of each poll request
2016-05-03 14:43:16 -03:00
readbyte = _port - > read ( ) ;
switch ( readbyte ) {
case SENSOR_ID_FAS :
switch ( fas_call ) {
case 1 :
2016-05-03 19:03:49 -03:00
send_data ( DATA_ID_FUEL , ( uint16_t ) roundf ( _battery . capacity_remaining_pct ( ) ) ) ; // send battery remaining
2016-05-03 14:43:16 -03:00
break ;
case 2 :
2016-05-03 19:03:49 -03:00
send_data ( DATA_ID_VFAS , ( uint16_t ) roundf ( _battery . voltage ( ) * 10.0f ) ) ; // send battery voltage
2016-05-03 14:43:16 -03:00
break ;
case 3 :
2016-05-03 19:03:49 -03:00
send_data ( DATA_ID_CURRENT , ( uint16_t ) roundf ( _battery . current_amps ( ) * 10.0f ) ) ; // send current consumption
2016-05-03 14:43:16 -03:00
break ;
}
if ( fas_call + + > 3 ) fas_call = 0 ;
break ;
case SENSOR_ID_GPS :
switch ( gps_call ) {
case 1 :
calc_gps_position ( ) ; // gps data is not recalculated until all of it has been sent
2016-05-03 19:03:49 -03:00
send_data ( DATA_ID_GPS_LAT_BP , _gps . latdddmm ) ; // send gps lattitude degree and minute integer part
2016-05-03 14:43:16 -03:00
break ;
case 2 :
2016-05-03 19:03:49 -03:00
send_data ( DATA_ID_GPS_LAT_AP , _gps . latmmmm ) ; // send gps lattitude minutes decimal part
2016-05-03 14:43:16 -03:00
break ;
case 3 :
2016-05-03 19:03:49 -03:00
send_data ( DATA_ID_GPS_LAT_NS , _gps . lat_ns ) ; // send gps North / South information
2016-05-03 14:43:16 -03:00
break ;
case 4 :
2016-05-03 19:03:49 -03:00
send_data ( DATA_ID_GPS_LONG_BP , _gps . londddmm ) ; // send gps longitude degree and minute integer part
2016-05-03 14:43:16 -03:00
break ;
case 5 :
2016-05-03 19:03:49 -03:00
send_data ( DATA_ID_GPS_LONG_AP , _gps . lonmmmm ) ; // send gps longitude minutes decimal part
2016-05-03 14:43:16 -03:00
break ;
case 6 :
2016-05-03 19:03:49 -03:00
send_data ( DATA_ID_GPS_LONG_EW , _gps . lon_ew ) ; // send gps East / West information
2016-05-03 14:43:16 -03:00
break ;
case 7 :
2016-05-03 19:03:49 -03:00
send_data ( DATA_ID_GPS_SPEED_BP , _gps . speed_in_meter ) ; // send gps speed integer part
2016-05-03 14:43:16 -03:00
break ;
case 8 :
2016-05-03 19:03:49 -03:00
send_data ( DATA_ID_GPS_SPEED_AP , _gps . speed_in_centimeter ) ; // send gps speed decimal part
2016-05-03 14:43:16 -03:00
break ;
case 9 :
2016-05-03 19:03:49 -03:00
send_data ( DATA_ID_GPS_ALT_BP , _gps . alt_gps_meters ) ; // send gps altitude integer part
2016-05-03 14:43:16 -03:00
break ;
case 10 :
2016-05-03 19:03:49 -03:00
send_data ( DATA_ID_GPS_ALT_AP , _gps . alt_gps_cm ) ; // send gps altitude decimals
2016-05-03 14:43:16 -03:00
break ;
case 11 :
2016-05-03 19:03:49 -03:00
send_data ( DATA_ID_GPS_COURS_BP , ( uint16_t ) ( ( _ahrs . yaw_sensor / 100 ) % 360 ) ) ; // send heading in degree based on AHRS and not GPS
2016-05-03 14:43:16 -03:00
break ;
}
if ( gps_call + + > 11 ) gps_call = 1 ;
break ;
case SENSOR_ID_VARIO :
switch ( vario_call ) {
case 1 :
calc_nav_alt ( ) ; // nav altitude is not recalculated until all of it has been sent
2016-05-03 19:03:49 -03:00
send_data ( DATA_ID_BARO_ALT_BP , _gps . alt_nav_meters ) ; // send altitude integer part
2016-05-03 14:43:16 -03:00
break ;
case 2 :
2016-05-03 19:03:49 -03:00
send_data ( DATA_ID_BARO_ALT_AP , _gps . alt_nav_cm ) ; // send altitude decimal part
2016-05-03 14:43:16 -03:00
break ;
}
if ( vario_call + + > 2 ) vario_call = 1 ;
break ;
case SENSOR_ID_SP2UR :
switch ( various_call ) {
case 1 :
2016-05-03 19:03:49 -03:00
send_data ( DATA_ID_TEMP2 , ( uint16_t ) ( _ahrs . get_gps ( ) . num_sats ( ) * 10 + _ahrs . get_gps ( ) . status ( ) ) ) ; // send GPS status and number of satellites as num_sats*10 + status (to fit into a uint8_t)
2016-05-03 14:43:16 -03:00
break ;
case 2 :
2016-05-03 19:03:49 -03:00
send_data ( DATA_ID_TEMP1 , ( uint16_t ) * _control_mode ) ; // send flight mode
2016-05-03 14:43:16 -03:00
break ;
}
if ( various_call + + > 2 ) various_call = 1 ;
break ;
}
2015-01-27 07:35:46 -04:00
}
}
}
2015-01-27 07:52:17 -04:00
/*
2016-05-03 19:03:49 -03:00
* send frame1 and frame2 telemetry data
2016-05-03 14:43:16 -03:00
* one frame ( frame1 ) is sent every 200 ms with baro alt , nb sats , batt volts and amp , control_mode
* a second frame ( frame2 ) is sent every second ( 1000 ms ) with gps position data , and ahrs . yaw_sensor heading ( instead of GPS heading )
* for FrSky D protocol ( D - receivers )
2015-01-27 07:52:17 -04:00
*/
2016-05-03 19:03:49 -03:00
void AP_Frsky_Telem : : send_D ( void )
2015-01-27 07:52:17 -04:00
{
2016-05-03 14:43:16 -03:00
static uint32_t last_200ms_frame = 0 ;
static uint32_t last_1000ms_frame = 0 ;
2015-01-27 07:52:17 -04:00
2015-11-19 23:10:09 -04:00
uint32_t now = AP_HAL : : millis ( ) ;
2015-01-27 07:35:46 -04:00
// send frame1 every 200ms
2016-05-03 14:43:16 -03:00
if ( now - last_200ms_frame > 200 ) {
last_200ms_frame = now ;
2016-05-03 19:03:49 -03:00
send_data ( DATA_ID_TEMP2 , ( uint16_t ) ( _ahrs . get_gps ( ) . num_sats ( ) * 10 + _ahrs . get_gps ( ) . status ( ) ) ) ; // send GPS status and number of satellites as num_sats*10 + status (to fit into a uint8_t)
send_data ( DATA_ID_TEMP1 , ( uint16_t ) * _control_mode ) ; // send flight mode
send_data ( DATA_ID_FUEL , ( uint16_t ) roundf ( _battery . capacity_remaining_pct ( ) ) ) ; // send battery remaining
send_data ( DATA_ID_VFAS , ( uint16_t ) roundf ( _battery . voltage ( ) * 10.0f ) ) ; // send battery voltage
send_data ( DATA_ID_CURRENT , ( uint16_t ) roundf ( _battery . current_amps ( ) * 10.0f ) ) ; // send current consumption
2016-05-03 14:43:16 -03:00
calc_nav_alt ( ) ;
2016-05-03 19:03:49 -03:00
send_data ( DATA_ID_BARO_ALT_BP , _gps . alt_nav_meters ) ; // send nav altitude integer part
send_data ( DATA_ID_BARO_ALT_AP , _gps . alt_nav_cm ) ; // send nav altitude decimal part
2015-01-27 07:35:46 -04:00
}
// send frame2 every second
2016-05-03 14:43:16 -03:00
if ( now - last_1000ms_frame > 1000 ) {
last_1000ms_frame = now ;
2016-05-03 19:03:49 -03:00
send_data ( DATA_ID_GPS_COURS_BP , ( uint16_t ) ( ( _ahrs . yaw_sensor / 100 ) % 360 ) ) ; // send heading in degree based on AHRS and not GPS
2015-01-27 07:35:46 -04:00
calc_gps_position ( ) ;
2016-05-03 14:43:16 -03:00
if ( _ahrs . get_gps ( ) . status ( ) > = 3 ) {
2016-05-03 19:03:49 -03:00
send_data ( DATA_ID_GPS_LAT_BP , _gps . latdddmm ) ; // send gps lattitude degree and minute integer part
send_data ( DATA_ID_GPS_LAT_AP , _gps . latmmmm ) ; // send gps lattitude minutes decimal part
send_data ( DATA_ID_GPS_LAT_NS , _gps . lat_ns ) ; // send gps North / South information
send_data ( DATA_ID_GPS_LONG_BP , _gps . londddmm ) ; // send gps longitude degree and minute integer part
send_data ( DATA_ID_GPS_LONG_AP , _gps . lonmmmm ) ; // send gps longitude minutes decimal part
send_data ( DATA_ID_GPS_LONG_EW , _gps . lon_ew ) ; // send gps East / West information
send_data ( DATA_ID_GPS_SPEED_BP , _gps . speed_in_meter ) ; // send gps speed integer part
send_data ( DATA_ID_GPS_SPEED_AP , _gps . speed_in_centimeter ) ; // send gps speed decimal part
send_data ( DATA_ID_GPS_ALT_BP , _gps . alt_gps_meters ) ; // send gps altitude integer part
send_data ( DATA_ID_GPS_ALT_AP , _gps . alt_gps_cm ) ; // send gps altitude decimal part
2015-01-27 07:35:46 -04:00
}
}
}
/*
2016-05-03 14:43:16 -03:00
* tick - main call to send data to the receiver ( called by scheduler at 1 kHz )
*/
void AP_Frsky_Telem : : tick ( void )
2015-01-27 07:35:46 -04:00
{
2016-05-03 14:43:16 -03:00
static bool initialised_uart = false ; // true when we have detected the protocol and UART has been initialised
2015-01-27 07:52:17 -04:00
// check UART has been initialised
2016-05-03 14:43:16 -03:00
if ( ! initialised_uart ) {
// initialise uart (this must be called from within tick b/c the UART begin must be called from the same thread as it is used from)
2016-05-03 19:03:49 -03:00
if ( _protocol = = AP_SerialManager : : SerialProtocol_FrSky_D ) { // FrSky D protocol (D-receivers)
2016-05-03 14:43:16 -03:00
_port - > begin ( AP_SERIALMANAGER_FRSKY_D_BAUD , AP_SERIALMANAGER_FRSKY_BUFSIZE_RX , AP_SERIALMANAGER_FRSKY_BUFSIZE_TX ) ;
2016-05-03 19:03:49 -03:00
} else { // FrSky SPort protocol (X-receivers)
2016-05-03 14:43:16 -03:00
_port - > begin ( AP_SERIALMANAGER_FRSKY_SPORT_BAUD , AP_SERIALMANAGER_FRSKY_BUFSIZE_RX , AP_SERIALMANAGER_FRSKY_BUFSIZE_TX ) ;
}
initialised_uart = true ;
2015-01-27 07:35:46 -04:00
}
2016-05-03 19:03:49 -03:00
if ( _protocol = = AP_SerialManager : : SerialProtocol_FrSky_D ) { // FrSky D protocol (D-receivers)
send_D ( ) ;
} else if ( _protocol = = AP_SerialManager : : SerialProtocol_FrSky_SPort ) { // FrSky SPort protocol (X-receivers)
send_SPort ( ) ;
2015-01-27 07:35:46 -04:00
}
}
2015-01-20 15:30:25 -04:00
/*
2016-05-03 14:43:16 -03:00
* build up the frame ' s crc
* for FrSky SPort protocol ( X - receivers )
*/
2016-05-03 19:03:49 -03:00
void AP_Frsky_Telem : : calc_crc ( uint8_t byte )
2015-01-22 01:43:49 -04:00
{
2015-01-20 15:30:25 -04:00
_crc + = byte ; //0-1FF
_crc + = _crc > > 8 ; //0-100
2016-05-03 14:43:16 -03:00
_crc & = 0xFF ;
2015-01-20 15:30:25 -04:00
}
/*
2016-05-03 14:43:16 -03:00
* send the frame ' s crc at the end of the frame
* for FrSky SPort protocol ( X - receivers )
2015-01-20 15:30:25 -04:00
*/
2016-05-03 19:03:49 -03:00
void AP_Frsky_Telem : : send_crc ( void )
2015-01-22 01:43:49 -04:00
{
2016-05-03 19:03:49 -03:00
send_byte ( 0xFF - _crc ) ;
2015-01-20 15:30:25 -04:00
_crc = 0 ;
2014-07-28 19:21:07 -03:00
}
2015-01-20 15:30:25 -04:00
/*
2016-05-03 14:43:16 -03:00
send 1 byte and do byte stuffing
2015-01-20 15:30:25 -04:00
*/
2016-05-03 19:03:49 -03:00
void AP_Frsky_Telem : : send_byte ( uint8_t byte )
2016-05-03 14:43:16 -03:00
{
if ( _protocol = = AP_SerialManager : : SerialProtocol_FrSky_D ) { // FrSky D protocol (D-receivers)
2016-05-03 19:03:49 -03:00
if ( byte = = START_STOP_D ) {
2016-05-03 14:43:16 -03:00
_port - > write ( 0x5D ) ;
_port - > write ( 0x3E ) ;
2016-05-03 19:03:49 -03:00
} else if ( byte = = BYTESTUFF_D ) {
2016-05-03 14:43:16 -03:00
_port - > write ( 0x5D ) ;
_port - > write ( 0x3D ) ;
} else {
_port - > write ( byte ) ;
2015-01-22 01:43:49 -04:00
}
2016-05-03 14:43:16 -03:00
} else { // FrSky SPort protocol (X-receivers)
2016-05-03 19:03:49 -03:00
if ( byte = = START_STOP_SPORT ) {
2016-05-03 14:43:16 -03:00
_port - > write ( 0x7D ) ;
_port - > write ( 0x5E ) ;
2016-05-03 19:03:49 -03:00
} else if ( byte = = BYTESTUFF_SPORT ) {
2016-05-03 14:43:16 -03:00
_port - > write ( 0x7D ) ;
_port - > write ( 0x5D ) ;
} else {
_port - > write ( byte ) ;
2015-01-22 01:43:49 -04:00
}
2016-05-03 19:03:49 -03:00
calc_crc ( byte ) ;
2014-07-28 19:21:07 -03:00
}
}
2015-01-20 15:30:25 -04:00
/*
2016-05-03 14:43:16 -03:00
* send one frame of FrSky data
2014-07-28 19:21:07 -03:00
*/
2016-05-03 19:03:49 -03:00
void AP_Frsky_Telem : : send_data ( uint16_t id , uint32_t data )
2016-05-03 14:43:16 -03:00
{
if ( _protocol = = AP_SerialManager : : SerialProtocol_FrSky_D ) { // FrSky D protocol (D-receivers)
2016-05-03 19:03:49 -03:00
_port - > write ( START_STOP_D ) ; // send a 0x5E start byte
2016-05-03 14:43:16 -03:00
uint8_t * bytes = ( uint8_t * ) & id ;
2016-05-03 19:03:49 -03:00
send_byte ( bytes [ 0 ] ) ;
2016-05-03 14:43:16 -03:00
bytes = ( uint8_t * ) & data ;
2016-05-03 19:03:49 -03:00
send_byte ( bytes [ 0 ] ) ; // LSB
send_byte ( bytes [ 1 ] ) ; // MSB
2016-05-03 14:43:16 -03:00
} else { // FrSky SPort protocol (X-receivers)
2016-05-03 19:03:49 -03:00
send_byte ( 0x10 ) ; // DATA_FRAME
2016-05-03 14:43:16 -03:00
uint8_t * bytes = ( uint8_t * ) & id ;
2016-05-03 19:03:49 -03:00
send_byte ( bytes [ 0 ] ) ; // LSB
send_byte ( bytes [ 1 ] ) ; // MSB
2016-05-03 14:43:16 -03:00
bytes = ( uint8_t * ) & data ;
2016-05-03 19:03:49 -03:00
send_byte ( bytes [ 0 ] ) ; // LSB
send_byte ( bytes [ 1 ] ) ;
send_byte ( bytes [ 2 ] ) ;
send_byte ( bytes [ 3 ] ) ; // MSB
send_crc ( ) ;
2015-01-20 15:30:25 -04:00
}
2014-07-28 19:21:07 -03:00
}
2015-01-20 15:30:25 -04:00
/*
2016-05-03 14:43:16 -03:00
* prepare altitude between vehicle and home location data
* for FrSky D and SPort protocols
2014-07-28 19:21:07 -03:00
*/
2016-05-03 14:43:16 -03:00
void AP_Frsky_Telem : : calc_nav_alt ( void )
2015-01-22 01:43:49 -04:00
{
2016-05-03 14:43:16 -03:00
Location current_loc ;
float current_height ; // in centimeters above home
if ( _ahrs . get_position ( current_loc ) ) {
if ( current_loc . flags . relative_alt ) {
current_height = current_loc . alt * 0.01f ;
} else {
current_height = ( current_loc . alt - _ahrs . get_home ( ) . alt ) * 0.01f ;
2015-01-22 01:43:49 -04:00
}
2016-05-03 14:43:16 -03:00
} else {
current_height = 0 ;
2014-07-28 19:21:07 -03:00
}
2016-05-03 14:43:16 -03:00
_gps . alt_nav_meters = ( int16_t ) current_height ;
_gps . alt_nav_cm = ( current_height - _gps . alt_nav_meters ) * 100 ;
}
2014-07-28 19:21:07 -03:00
2016-05-03 14:43:16 -03:00
/*
* format the decimal latitude / longitude to the required degrees / minutes
* for FrSky D and SPort protocols
2014-07-28 19:21:07 -03:00
*/
2016-05-03 14:43:16 -03:00
float AP_Frsky_Telem : : format_gps ( float dec )
2014-07-28 19:21:07 -03:00
{
uint8_t dm_deg = ( uint8_t ) dec ;
return ( dm_deg * 100.0f ) + ( dec - dm_deg ) * 60 ;
}
2015-01-20 15:30:25 -04:00
/*
2016-05-03 14:43:16 -03:00
* prepare gps data
* for FrSky D and SPort protocols
2014-07-28 19:21:07 -03:00
*/
2016-05-03 14:43:16 -03:00
void AP_Frsky_Telem : : calc_gps_position ( void )
2015-01-22 01:43:49 -04:00
{
2015-01-20 15:30:25 -04:00
float lat ;
2016-05-03 14:43:16 -03:00
float lon ;
float alt ;
2015-01-20 15:30:25 -04:00
float speed ;
2016-05-03 14:43:16 -03:00
if ( _ahrs . get_gps ( ) . status ( ) > = 3 ) {
Location loc = _ahrs . get_gps ( ) . location ( ) ; //get gps instance 0
lat = format_gps ( fabsf ( loc . lat / 10000000.0f ) ) ;
_gps . latdddmm = lat ;
_gps . latmmmm = ( lat - _gps . latdddmm ) * 10000 ;
_gps . lat_ns = ( loc . lat < 0 ) ? ' S ' : ' N ' ;
2015-01-20 15:30:25 -04:00
2016-05-03 14:43:16 -03:00
lon = format_gps ( fabsf ( loc . lng / 10000000.0f ) ) ;
_gps . londddmm = lon ;
_gps . lonmmmm = ( lon - _gps . londddmm ) * 10000 ;
_gps . lon_ew = ( loc . lng < 0 ) ? ' W ' : ' E ' ;
2015-01-20 15:30:25 -04:00
2016-05-03 14:43:16 -03:00
alt = loc . alt * 0.01f ;
_gps . alt_gps_meters = ( int16_t ) alt ;
_gps . alt_gps_cm = ( alt - _gps . alt_gps_meters ) * 100 ;
2015-01-20 15:30:25 -04:00
2016-05-03 14:43:16 -03:00
speed = _ahrs . get_gps ( ) . ground_speed ( ) ;
_gps . speed_in_meter = speed ;
_gps . speed_in_centimeter = ( speed - _gps . speed_in_meter ) * 100 ;
} else {
_gps . latdddmm = 0 ;
_gps . latmmmm = 0 ;
_gps . lat_ns = 0 ;
_gps . londddmm = 0 ;
_gps . lonmmmm = 0 ;
_gps . alt_gps_meters = 0 ;
_gps . alt_gps_cm = 0 ;
_gps . speed_in_meter = 0 ;
_gps . speed_in_centimeter = 0 ;
}
2015-01-20 15:30:25 -04:00
}