2014-06-18 09:04:26 +00:00
/*!
* \ file galileo_e1_ls_pvt . cc
* \ brief Implementation of a Least Squares Position , Velocity , and Time
* ( PVT ) solver , based on K . Borre ' s Matlab receiver .
* \ author Javier Arribas , 2011. jarribas ( at ) cttc . es
*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*
2015-01-08 18:49:59 +00:00
* Copyright ( C ) 2010 - 2015 ( see AUTHORS file for a list of contributors )
2014-06-18 09:04:26 +00:00
*
* GNSS - SDR is a software defined Global Navigation
* Satellite Systems receiver
*
* This file is part of GNSS - SDR .
*
* GNSS - SDR 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
2015-01-08 18:49:59 +00:00
* ( at your option ) any later version .
2014-06-18 09:04:26 +00:00
*
* GNSS - SDR 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 GNSS - SDR . If not , see < http : //www.gnu.org/licenses/>.
*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
# include "hybrid_ls_pvt.h"
# include <glog/logging.h>
# include "Galileo_E1.h"
using google : : LogMessage ;
2015-11-14 13:17:02 +00:00
hybrid_ls_pvt : : hybrid_ls_pvt ( int nchannels , std : : string dump_filename , bool flag_dump_to_file ) : Ls_Pvt ( )
2014-06-18 09:04:26 +00:00
{
// init empty ephemeris for all the available GNSS channels
d_nchannels = nchannels ;
2014-06-19 15:07:27 +00:00
d_Gal_ephemeris = new Galileo_Navigation_Message [ nchannels ] ;
d_GPS_ephemeris = new Gps_Navigation_Message [ nchannels ] ;
2014-06-18 09:04:26 +00:00
d_dump_filename = dump_filename ;
d_flag_dump_enabled = flag_dump_to_file ;
d_galileo_current_time = 0 ;
2015-05-21 18:27:07 +00:00
d_valid_GPS_obs = 0 ;
d_valid_GAL_obs = 0 ;
count_valid_position = 0 ;
2015-12-02 22:44:07 +00:00
d_flag_averaging = false ;
2014-06-18 09:04:26 +00:00
// ############# ENABLE DATA FILE LOG #################
if ( d_flag_dump_enabled = = true )
{
if ( d_dump_file . is_open ( ) = = false )
{
try
{
d_dump_file . exceptions ( std : : ifstream : : failbit | std : : ifstream : : badbit ) ;
d_dump_file . open ( d_dump_filename . c_str ( ) , std : : ios : : out | std : : ios : : binary ) ;
LOG ( INFO ) < < " PVT lib dump enabled Log file: " < < d_dump_filename . c_str ( ) ;
}
2016-03-29 16:40:00 +00:00
catch ( const std : : ifstream : : failure & e )
2014-06-18 09:04:26 +00:00
{
LOG ( WARNING ) < < " Exception opening PVT lib dump file " < < e . what ( ) ;
}
}
}
}
hybrid_ls_pvt : : ~ hybrid_ls_pvt ( )
{
d_dump_file . close ( ) ;
2014-06-19 15:07:27 +00:00
delete [ ] d_Gal_ephemeris ;
delete [ ] d_GPS_ephemeris ;
2014-06-18 09:04:26 +00:00
}
2014-06-19 15:07:27 +00:00
bool hybrid_ls_pvt : : get_PVT ( std : : map < int , Gnss_Synchro > gnss_pseudoranges_map , double hybrid_current_time , bool flag_averaging )
2014-06-18 09:04:26 +00:00
{
std : : map < int , Gnss_Synchro > : : iterator gnss_pseudoranges_iter ;
std : : map < int , Galileo_Ephemeris > : : iterator galileo_ephemeris_iter ;
2014-06-19 15:07:27 +00:00
std : : map < int , Gps_Ephemeris > : : iterator gps_ephemeris_iter ;
2014-06-18 09:04:26 +00:00
int valid_pseudoranges = gnss_pseudoranges_map . size ( ) ;
2014-09-05 10:44:51 +00:00
arma : : mat W = arma : : eye ( valid_pseudoranges , valid_pseudoranges ) ; // channels weights matrix
2014-06-18 09:04:26 +00:00
arma : : vec obs = arma : : zeros ( valid_pseudoranges ) ; // pseudoranges observation vector
2014-09-05 10:44:51 +00:00
arma : : mat satpos = arma : : zeros ( 3 , valid_pseudoranges ) ; // satellite positions matrix
2014-06-18 09:04:26 +00:00
int Galileo_week_number = 0 ;
2015-12-08 19:24:36 +00:00
int GPS_week = 0 ;
double utc = 0.0 ;
double GST = 0.0 ;
2016-03-29 16:40:00 +00:00
//double utc_tx_corrected = 0.0; //utc computed at tx_time_corrected, added for Galileo constellation (in GPS utc is directly computed at TX_time_corrected_s)
double TX_time_corrected_s = 0.0 ;
2015-12-08 19:24:36 +00:00
double SV_clock_bias_s = 0.0 ;
2014-06-18 09:04:26 +00:00
d_flag_averaging = flag_averaging ;
// ********************************************************************************
// ****** PREPARE THE LEAST SQUARES DATA (SV POSITIONS MATRIX AND OBS VECTORS) ****
// ********************************************************************************
int valid_obs = 0 ; //valid observations counter
int obs_counter = 0 ;
2014-07-22 07:30:27 +00:00
int valid_obs_GPS_counter = 0 ;
int valid_obs_GALILEO_counter = 0 ;
2014-06-18 09:04:26 +00:00
for ( gnss_pseudoranges_iter = gnss_pseudoranges_map . begin ( ) ;
gnss_pseudoranges_iter ! = gnss_pseudoranges_map . end ( ) ;
gnss_pseudoranges_iter + + )
{
2016-05-10 19:19:09 +00:00
if ( gnss_pseudoranges_iter - > second . System = = ' E ' )
2014-09-05 10:44:51 +00:00
{
// 1 Gal - find the ephemeris for the current GALILEO SV observation. The SV PRN ID is the map key
2016-05-10 09:18:41 +00:00
galileo_ephemeris_iter = galileo_ephemeris_map . find ( gnss_pseudoranges_iter - > second . PRN ) ;
2014-09-05 10:44:51 +00:00
if ( galileo_ephemeris_iter ! = galileo_ephemeris_map . end ( ) )
{
/*!
* \ todo Place here the satellite CN0 ( power level , or weight factor )
*/
W ( obs_counter , obs_counter ) = 1 ;
2015-12-08 19:24:36 +00:00
// COMMON RX TIME PVT ALGORITHM
2014-09-05 10:44:51 +00:00
double Rx_time = hybrid_current_time ;
2016-05-10 19:19:09 +00:00
double Tx_time = Rx_time - gnss_pseudoranges_iter - > second . Pseudorange_m / GALILEO_C_m_s ;
2014-09-05 10:44:51 +00:00
// 2- compute the clock drift using the clock model (broadcast) for this SV
SV_clock_bias_s = galileo_ephemeris_iter - > second . sv_clock_drift ( Tx_time ) ;
// 3- compute the current ECEF position for this SV using corrected TX time
TX_time_corrected_s = Tx_time - SV_clock_bias_s ;
galileo_ephemeris_iter - > second . satellitePosition ( TX_time_corrected_s ) ;
satpos ( 0 , obs_counter ) = galileo_ephemeris_iter - > second . d_satpos_X ;
satpos ( 1 , obs_counter ) = galileo_ephemeris_iter - > second . d_satpos_Y ;
satpos ( 2 , obs_counter ) = galileo_ephemeris_iter - > second . d_satpos_Z ;
// 5- fill the observations vector with the corrected pseudoranges
2016-05-10 19:19:09 +00:00
obs ( obs_counter ) = gnss_pseudoranges_iter - > second . Pseudorange_m + SV_clock_bias_s * GALILEO_C_m_s ;
2014-09-05 10:44:51 +00:00
d_visible_satellites_IDs [ valid_obs ] = galileo_ephemeris_iter - > second . i_satellite_PRN ;
d_visible_satellites_CN0_dB [ valid_obs ] = gnss_pseudoranges_iter - > second . CN0_dB_hz ;
valid_obs + + ;
valid_obs_GALILEO_counter + + ;
2015-12-08 19:24:36 +00:00
Galileo_week_number = galileo_ephemeris_iter - > second . WN_5 ; //for GST
GST = galileo_ephemeris_iter - > second . Galileo_System_Time ( Galileo_week_number , hybrid_current_time ) ;
2014-09-05 10:44:51 +00:00
// SV ECEF DEBUG OUTPUT
2015-05-04 20:16:26 +00:00
DLOG ( INFO ) < < " ECEF satellite SV ID= " < < galileo_ephemeris_iter - > second . i_satellite_PRN
2015-11-14 19:41:28 +00:00
< < " X= " < < galileo_ephemeris_iter - > second . d_satpos_X
< < " [m] Y= " < < galileo_ephemeris_iter - > second . d_satpos_Y
< < " [m] Z= " < < galileo_ephemeris_iter - > second . d_satpos_Z
< < " [m] PR_obs= " < < obs ( obs_counter ) < < " [m] " ;
2014-09-05 10:44:51 +00:00
}
else // the ephemeris are not available for this SV
{
// no valid pseudorange for the current SV
W ( obs_counter , obs_counter ) = 0 ; // SV de-activated
obs ( obs_counter ) = 1 ; // to avoid algorithm problems (divide by zero)
2016-05-10 09:18:41 +00:00
DLOG ( INFO ) < < " No ephemeris data for SV " < < gnss_pseudoranges_iter - > second . PRN ;
2014-09-05 10:44:51 +00:00
}
}
2014-06-19 15:07:27 +00:00
2016-05-10 19:19:09 +00:00
else if ( gnss_pseudoranges_iter - > second . System = = ' G ' )
2014-09-05 10:44:51 +00:00
{
//std::cout << "Satellite System: " << gnss_pseudoranges_iter->second.System <<std::endl;
// 1 GPS - find the ephemeris for the current GPS SV observation. The SV PRN ID is the map key
2016-05-10 09:18:41 +00:00
gps_ephemeris_iter = gps_ephemeris_map . find ( gnss_pseudoranges_iter - > second . PRN ) ;
2014-09-05 10:44:51 +00:00
if ( gps_ephemeris_iter ! = gps_ephemeris_map . end ( ) )
{
/*!
* \ todo Place here the satellite CN0 ( power level , or weight factor )
*/
W ( obs_counter , obs_counter ) = 1 ;
// COMMON RX TIME PVT ALGORITHM MODIFICATION (Like RINEX files)
// first estimate of transmit time
double Rx_time = hybrid_current_time ;
2016-05-10 19:19:09 +00:00
double Tx_time = Rx_time - gnss_pseudoranges_iter - > second . Pseudorange_m / GPS_C_m_s ;
2014-09-05 10:44:51 +00:00
// 2- compute the clock drift using the clock model (broadcast) for this SV
SV_clock_bias_s = gps_ephemeris_iter - > second . sv_clock_drift ( Tx_time ) ;
// 3- compute the current ECEF position for this SV using corrected TX time
TX_time_corrected_s = Tx_time - SV_clock_bias_s ;
gps_ephemeris_iter - > second . satellitePosition ( TX_time_corrected_s ) ;
satpos ( 0 , obs_counter ) = gps_ephemeris_iter - > second . d_satpos_X ;
satpos ( 1 , obs_counter ) = gps_ephemeris_iter - > second . d_satpos_Y ;
satpos ( 2 , obs_counter ) = gps_ephemeris_iter - > second . d_satpos_Z ;
// 5- fill the observations vector with the corrected pseudorranges
2016-05-10 19:19:09 +00:00
obs ( obs_counter ) = gnss_pseudoranges_iter - > second . Pseudorange_m + SV_clock_bias_s * GPS_C_m_s ;
2014-09-05 10:44:51 +00:00
d_visible_satellites_IDs [ valid_obs ] = gps_ephemeris_iter - > second . i_satellite_PRN ;
d_visible_satellites_CN0_dB [ valid_obs ] = gnss_pseudoranges_iter - > second . CN0_dB_hz ;
valid_obs + + ;
valid_obs_GPS_counter + + ;
2015-12-08 19:24:36 +00:00
GPS_week = gps_ephemeris_iter - > second . i_GPS_week ;
2014-09-05 10:44:51 +00:00
// SV ECEF DEBUG OUTPUT
2015-05-04 20:16:26 +00:00
DLOG ( INFO ) < < " (new)ECEF satellite SV ID= " < < gps_ephemeris_iter - > second . i_satellite_PRN
2014-09-05 10:44:51 +00:00
< < " X= " < < gps_ephemeris_iter - > second . d_satpos_X
< < " [m] Y= " < < gps_ephemeris_iter - > second . d_satpos_Y
< < " [m] Z= " < < gps_ephemeris_iter - > second . d_satpos_Z
< < " [m] PR_obs= " < < obs ( obs_counter ) < < " [m] " ;
}
else // the ephemeris are not available for this SV
{
// no valid pseudorange for the current SV
W ( obs_counter , obs_counter ) = 0 ; // SV de-activated
obs ( obs_counter ) = 1 ; // to avoid algorithm problems (divide by zero)
2016-05-10 09:18:41 +00:00
DLOG ( INFO ) < < " No ephemeris data for SV " < < gnss_pseudoranges_iter - > second . PRN ;
2014-09-05 10:44:51 +00:00
}
}
obs_counter + + ;
2014-06-18 09:04:26 +00:00
}
2014-09-05 10:44:51 +00:00
2014-06-18 09:04:26 +00:00
// ********************************************************************************
// ****** SOLVE LEAST SQUARES******************************************************
// ********************************************************************************
d_valid_observations = valid_obs ;
2014-07-22 07:30:27 +00:00
d_valid_GPS_obs = valid_obs_GPS_counter ;
d_valid_GAL_obs = valid_obs_GALILEO_counter ;
2014-06-30 15:48:01 +00:00
LOG ( INFO ) < < " HYBRID PVT: valid observations= " < < valid_obs ;
2014-06-18 09:04:26 +00:00
2016-05-10 19:19:09 +00:00
if ( valid_obs > = 4 )
2014-06-18 09:04:26 +00:00
{
2014-06-30 15:48:01 +00:00
arma : : vec mypos ;
DLOG ( INFO ) < < " satpos= " < < satpos ;
2016-05-10 19:19:09 +00:00
DLOG ( INFO ) < < " obs= " < < obs ;
2014-06-30 15:48:01 +00:00
DLOG ( INFO ) < < " W= " < < W ;
2015-11-15 22:31:27 +00:00
2016-03-29 16:12:59 +00:00
mypos = leastSquarePos ( satpos , obs , W ) ;
d_rx_dt_m = mypos ( 3 ) / GPS_C_m_s ; // Convert RX time offset from meters to seconds
2016-05-10 19:19:09 +00:00
double secondsperweek = 604800.0 ;
2014-06-30 15:48:01 +00:00
// Compute GST and Gregorian time
2015-12-08 19:24:36 +00:00
if ( GST ! = 0.0 )
{
utc = galileo_utc_model . GST_to_UTC_time ( GST , Galileo_week_number ) ;
}
else
{
2016-05-10 19:19:09 +00:00
utc = gps_utc_model . utc_time ( TX_time_corrected_s , GPS_week ) + secondsperweek * static_cast < double > ( GPS_week ) ;
2015-12-08 19:24:36 +00:00
}
2016-05-10 19:19:09 +00:00
2014-06-30 15:48:01 +00:00
// get time string Gregorian calendar
2016-05-10 19:19:09 +00:00
boost : : posix_time : : time_duration t = boost : : posix_time : : seconds ( utc ) ;
2014-06-30 15:48:01 +00:00
// 22 August 1999 00:00 last Galileo start GST epoch (ICD sec 5.1.2)
boost : : posix_time : : ptime p_time ( boost : : gregorian : : date ( 1999 , 8 , 22 ) , t ) ;
d_position_UTC_time = p_time ;
2016-05-10 19:19:09 +00:00
2015-05-04 20:16:26 +00:00
DLOG ( INFO ) < < " HYBRID Position at TOW= " < < hybrid_current_time < < " in ECEF (X,Y,Z) = " < < mypos ;
2014-06-30 15:48:01 +00:00
2016-03-29 16:12:59 +00:00
cart2geo ( static_cast < double > ( mypos ( 0 ) ) , static_cast < double > ( mypos ( 1 ) ) , static_cast < double > ( mypos ( 2 ) ) , 4 ) ;
2014-06-30 15:48:01 +00:00
//ToDo: Find an Observables/PVT random bug with some satellite configurations that gives an erratic PVT solution (i.e. height>50 km)
if ( d_height_m > 50000 )
{
b_valid_position = false ;
LOG ( INFO ) < < " Hybrid Position at " < < boost : : posix_time : : to_simple_string ( p_time )
2015-11-14 19:41:28 +00:00
< < " is Lat = " < < d_latitude_d < < " [deg], Long = " < < d_longitude_d
2016-03-29 16:12:59 +00:00
< < " [deg], Height= " < < d_height_m < < " [m] " < < " RX time offset= " < < mypos ( 3 ) < < " [s] " ;
2014-06-30 15:48:01 +00:00
return false ;
}
LOG ( INFO ) < < " Hybrid Position at " < < boost : : posix_time : : to_simple_string ( p_time )
2015-11-14 19:41:28 +00:00
< < " is Lat = " < < d_latitude_d < < " [deg], Long = " < < d_longitude_d
2016-03-29 16:12:59 +00:00
< < " [deg], Height= " < < d_height_m < < " [m] " < < " RX time offset= " < < d_rx_dt_m < < " [s] " ;
2014-07-22 07:30:27 +00:00
2014-06-30 15:48:01 +00:00
// ###### Compute DOPs ########
2015-11-15 22:31:27 +00:00
hybrid_ls_pvt : : compute_DOP ( ) ;
2014-06-30 15:48:01 +00:00
// ######## LOG FILE #########
if ( d_flag_dump_enabled = = true )
{
// MULTIPLEXED FILE RECORDING - Record results to file
try
{
double tmp_double ;
// PVT GPS time
tmp_double = hybrid_current_time ;
d_dump_file . write ( ( char * ) & tmp_double , sizeof ( double ) ) ;
// ECEF User Position East [m]
tmp_double = mypos ( 0 ) ;
d_dump_file . write ( ( char * ) & tmp_double , sizeof ( double ) ) ;
// ECEF User Position North [m]
tmp_double = mypos ( 1 ) ;
d_dump_file . write ( ( char * ) & tmp_double , sizeof ( double ) ) ;
// ECEF User Position Up [m]
tmp_double = mypos ( 2 ) ;
d_dump_file . write ( ( char * ) & tmp_double , sizeof ( double ) ) ;
// User clock offset [s]
tmp_double = mypos ( 3 ) ;
d_dump_file . write ( ( char * ) & tmp_double , sizeof ( double ) ) ;
// GEO user position Latitude [deg]
tmp_double = d_latitude_d ;
d_dump_file . write ( ( char * ) & tmp_double , sizeof ( double ) ) ;
// GEO user position Longitude [deg]
tmp_double = d_longitude_d ;
d_dump_file . write ( ( char * ) & tmp_double , sizeof ( double ) ) ;
// GEO user position Height [m]
tmp_double = d_height_m ;
d_dump_file . write ( ( char * ) & tmp_double , sizeof ( double ) ) ;
}
catch ( const std : : ifstream : : failure & e )
{
2016-05-10 19:19:09 +00:00
LOG ( WARNING ) < < " Exception writing PVT LS dump file " < < e . what ( ) ;
2014-06-30 15:48:01 +00:00
}
}
2014-06-18 09:04:26 +00:00
2014-06-30 15:48:01 +00:00
// MOVING AVERAGE PVT
2016-03-29 16:12:59 +00:00
pos_averaging ( flag_averaging ) ;
2014-09-05 10:44:51 +00:00
}
2014-06-18 09:04:26 +00:00
else
{
b_valid_position = false ;
}
2015-11-14 19:41:28 +00:00
return b_valid_position ;
2014-06-18 09:04:26 +00:00
}