|  |  |  | @ -15,6 +15,7 @@ const double ACCEL_SANITY_CHECK = 100.0;  // m/s^2 | 
			
		
	
		
			
				
					|  |  |  |  | const double ROTATION_SANITY_CHECK = 10.0;  // rad/s
 | 
			
		
	
		
			
				
					|  |  |  |  | const double TRANS_SANITY_CHECK = 200.0;  // m/s
 | 
			
		
	
		
			
				
					|  |  |  |  | const double CALIB_RPY_SANITY_CHECK = 0.5; // rad (+- 30 deg)
 | 
			
		
	
		
			
				
					|  |  |  |  | const double ALTITUDE_SANITY_CHECK = 10000; // m
 | 
			
		
	
		
			
				
					|  |  |  |  | const double MIN_STD_SANITY_CHECK = 1e-5; // m or rad
 | 
			
		
	
		
			
				
					|  |  |  |  | const double VALID_TIME_SINCE_RESET = 1.0; // s
 | 
			
		
	
		
			
				
					|  |  |  |  | const double VALID_POS_STD = 50.0; // m
 | 
			
		
	
	
		
			
				
					|  |  |  | @ -24,11 +25,15 @@ const double INPUT_INVALID_THRESHOLD = 5.0; // same as reset tracker | 
			
		
	
		
			
				
					|  |  |  |  | const double DECAY = 0.99995; // same as reset tracker
 | 
			
		
	
		
			
				
					|  |  |  |  | const double MAX_FILTER_REWIND_TIME = 0.8; // s
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | // TODO: GPS sensor time offsets are empirically calculated
 | 
			
		
	
		
			
				
					|  |  |  |  | // They should be replaced with synced time from a real clock
 | 
			
		
	
		
			
				
					|  |  |  |  | const double GPS_QUECTEL_SENSOR_TIME_OFFSET = 0.630; // s
 | 
			
		
	
		
			
				
					|  |  |  |  | const double GPS_UBLOX_SENSOR_TIME_OFFSET = 0.095; // s
 | 
			
		
	
		
			
				
					|  |  |  |  | const float  GPS_POS_STD_THRESHOLD = 50.0; | 
			
		
	
		
			
				
					|  |  |  |  | const float  GPS_VEL_STD_THRESHOLD = 5.0; | 
			
		
	
		
			
				
					|  |  |  |  | const float  GPS_POS_ERROR_RESET_THRESHOLD = 300.0; | 
			
		
	
		
			
				
					|  |  |  |  | const float  GPS_POS_STD_RESET_THRESHOLD = 10.0; | 
			
		
	
		
			
				
					|  |  |  |  | const float  GPS_VEL_STD_RESET_THRESHOLD = 1.0; | 
			
		
	
		
			
				
					|  |  |  |  | const float  GPS_POS_STD_RESET_THRESHOLD = 2.0; | 
			
		
	
		
			
				
					|  |  |  |  | const float  GPS_VEL_STD_RESET_THRESHOLD = 0.5; | 
			
		
	
		
			
				
					|  |  |  |  | const float  GPS_ORIENTATION_ERROR_RESET_THRESHOLD = 1.0; | 
			
		
	
		
			
				
					|  |  |  |  | const int    GPS_ORIENTATION_ERROR_RESET_CNT = 3; | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
	
		
			
				
					|  |  |  | @ -67,7 +72,7 @@ static VectorXd rotate_std(const MatrixXdr& rot_matrix, const VectorXd& std_in) | 
			
		
	
		
			
				
					|  |  |  |  |   return rotate_cov(rot_matrix, std_in.array().square().matrix().asDiagonal()).diagonal().array().sqrt(); | 
			
		
	
		
			
				
					|  |  |  |  | } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | Localizer::Localizer() { | 
			
		
	
		
			
				
					|  |  |  |  | Localizer::Localizer(LocalizerGnssSource gnss_source) { | 
			
		
	
		
			
				
					|  |  |  |  |   this->kf = std::make_unique<LiveKalman>(); | 
			
		
	
		
			
				
					|  |  |  |  |   this->reset_kalman(); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
	
		
			
				
					|  |  |  | @ -81,6 +86,7 @@ Localizer::Localizer() { | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   VectorXd ecef_pos = this->kf->get_x().segment<STATE_ECEF_POS_LEN>(STATE_ECEF_POS_START); | 
			
		
	
		
			
				
					|  |  |  |  |   this->converter = std::make_unique<LocalCoord>((ECEF) { .x = ecef_pos[0], .y = ecef_pos[1], .z = ecef_pos[2] }); | 
			
		
	
		
			
				
					|  |  |  |  |   this->configure_gnss_source(gnss_source); | 
			
		
	
		
			
				
					|  |  |  |  | } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | void Localizer::build_live_location(cereal::LiveLocationKalman::Builder& fix) { | 
			
		
	
	
		
			
				
					|  |  |  | @ -293,6 +299,64 @@ void Localizer::input_fake_gps_observations(double current_time) { | 
			
		
	
		
			
				
					|  |  |  |  |   this->kf->predict_and_observe(current_time, OBSERVATION_ECEF_VEL, { ecef_vel }, { ecef_vel_R }); | 
			
		
	
		
			
				
					|  |  |  |  | } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | void Localizer::handle_gps(double current_time, const cereal::GpsLocationData::Reader& log, const double sensor_time_offset) { | 
			
		
	
		
			
				
					|  |  |  |  |   // ignore the message if the fix is invalid
 | 
			
		
	
		
			
				
					|  |  |  |  |   bool gps_invalid_flag = (log.getFlags() % 2 == 0); | 
			
		
	
		
			
				
					|  |  |  |  |   bool gps_unreasonable = (Vector2d(log.getAccuracy(), log.getVerticalAccuracy()).norm() >= SANE_GPS_UNCERTAINTY); | 
			
		
	
		
			
				
					|  |  |  |  |   bool gps_accuracy_insane = ((log.getVerticalAccuracy() <= 0) || (log.getSpeedAccuracy() <= 0) || (log.getBearingAccuracyDeg() <= 0)); | 
			
		
	
		
			
				
					|  |  |  |  |   bool gps_lat_lng_alt_insane = ((std::abs(log.getLatitude()) > 90) || (std::abs(log.getLongitude()) > 180) || (std::abs(log.getAltitude()) > ALTITUDE_SANITY_CHECK)); | 
			
		
	
		
			
				
					|  |  |  |  |   bool gps_vel_insane = (floatlist2vector(log.getVNED()).norm() > TRANS_SANITY_CHECK); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   if (gps_invalid_flag || gps_unreasonable || gps_accuracy_insane || gps_lat_lng_alt_insane || gps_vel_insane) { | 
			
		
	
		
			
				
					|  |  |  |  |     //this->gps_valid = false;
 | 
			
		
	
		
			
				
					|  |  |  |  |     this->determine_gps_mode(current_time); | 
			
		
	
		
			
				
					|  |  |  |  |     return; | 
			
		
	
		
			
				
					|  |  |  |  |   } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   double sensor_time = current_time - sensor_time_offset; | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   // Process message
 | 
			
		
	
		
			
				
					|  |  |  |  |   //this->gps_valid = true;
 | 
			
		
	
		
			
				
					|  |  |  |  |   this->gps_mode = true; | 
			
		
	
		
			
				
					|  |  |  |  |   Geodetic geodetic = { log.getLatitude(), log.getLongitude(), log.getAltitude() }; | 
			
		
	
		
			
				
					|  |  |  |  |   this->converter = std::make_unique<LocalCoord>(geodetic); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   VectorXd ecef_pos = this->converter->ned2ecef({ 0.0, 0.0, 0.0 }).to_vector(); | 
			
		
	
		
			
				
					|  |  |  |  |   VectorXd ecef_vel = this->converter->ned2ecef({ log.getVNED()[0], log.getVNED()[1], log.getVNED()[2] }).to_vector() - ecef_pos; | 
			
		
	
		
			
				
					|  |  |  |  |   float ecef_pos_std = std::sqrt(this->gps_variance_factor * std::pow(log.getAccuracy(), 2) + this->gps_vertical_variance_factor * std::pow(log.getVerticalAccuracy(), 2)); | 
			
		
	
		
			
				
					|  |  |  |  |   MatrixXdr ecef_pos_R = Vector3d::Constant(std::pow(this->gps_std_factor * ecef_pos_std, 2)).asDiagonal(); | 
			
		
	
		
			
				
					|  |  |  |  |   MatrixXdr ecef_vel_R = Vector3d::Constant(std::pow(this->gps_std_factor * log.getSpeedAccuracy(), 2)).asDiagonal(); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   this->unix_timestamp_millis = log.getUnixTimestampMillis(); | 
			
		
	
		
			
				
					|  |  |  |  |   double gps_est_error = (this->kf->get_x().segment<STATE_ECEF_POS_LEN>(STATE_ECEF_POS_START) - ecef_pos).norm(); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   VectorXd orientation_ecef = quat2euler(vector2quat(this->kf->get_x().segment<STATE_ECEF_ORIENTATION_LEN>(STATE_ECEF_ORIENTATION_START))); | 
			
		
	
		
			
				
					|  |  |  |  |   VectorXd orientation_ned = ned_euler_from_ecef({ ecef_pos(0), ecef_pos(1), ecef_pos(2) }, orientation_ecef); | 
			
		
	
		
			
				
					|  |  |  |  |   VectorXd orientation_ned_gps = Vector3d(0.0, 0.0, DEG2RAD(log.getBearingDeg())); | 
			
		
	
		
			
				
					|  |  |  |  |   VectorXd orientation_error = (orientation_ned - orientation_ned_gps).array() - M_PI; | 
			
		
	
		
			
				
					|  |  |  |  |   for (int i = 0; i < orientation_error.size(); i++) { | 
			
		
	
		
			
				
					|  |  |  |  |     orientation_error(i) = std::fmod(orientation_error(i), 2.0 * M_PI); | 
			
		
	
		
			
				
					|  |  |  |  |     if (orientation_error(i) < 0.0) { | 
			
		
	
		
			
				
					|  |  |  |  |       orientation_error(i) += 2.0 * M_PI; | 
			
		
	
		
			
				
					|  |  |  |  |     } | 
			
		
	
		
			
				
					|  |  |  |  |     orientation_error(i) -= M_PI; | 
			
		
	
		
			
				
					|  |  |  |  |   } | 
			
		
	
		
			
				
					|  |  |  |  |   VectorXd initial_pose_ecef_quat = quat2vector(euler2quat(ecef_euler_from_ned({ ecef_pos(0), ecef_pos(1), ecef_pos(2) }, orientation_ned_gps))); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   if (ecef_vel.norm() > 5.0 && orientation_error.norm() > 1.0) { | 
			
		
	
		
			
				
					|  |  |  |  |     LOGE("Locationd vs ubloxLocation orientation difference too large, kalman reset"); | 
			
		
	
		
			
				
					|  |  |  |  |     this->reset_kalman(NAN, initial_pose_ecef_quat, ecef_pos, ecef_vel, ecef_pos_R, ecef_vel_R); | 
			
		
	
		
			
				
					|  |  |  |  |     this->kf->predict_and_observe(sensor_time, OBSERVATION_ECEF_ORIENTATION_FROM_GPS, { initial_pose_ecef_quat }); | 
			
		
	
		
			
				
					|  |  |  |  |   } else if (gps_est_error > 100.0) { | 
			
		
	
		
			
				
					|  |  |  |  |     LOGE("Locationd vs ubloxLocation position difference too large, kalman reset"); | 
			
		
	
		
			
				
					|  |  |  |  |     this->reset_kalman(NAN, initial_pose_ecef_quat, ecef_pos, ecef_vel, ecef_pos_R, ecef_vel_R); | 
			
		
	
		
			
				
					|  |  |  |  |   } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   this->last_gps_msg = sensor_time; | 
			
		
	
		
			
				
					|  |  |  |  |   this->kf->predict_and_observe(sensor_time, OBSERVATION_ECEF_POS, { ecef_pos }, { ecef_pos_R }); | 
			
		
	
		
			
				
					|  |  |  |  |   this->kf->predict_and_observe(sensor_time, OBSERVATION_ECEF_VEL, { ecef_vel }, { ecef_vel_R }); | 
			
		
	
		
			
				
					|  |  |  |  | } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | void Localizer::handle_gnss(double current_time, const cereal::GnssMeasurements::Reader& log) { | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   if (!log.getPositionECEF().getValid() || !log.getVelocityECEF().getValid()) { | 
			
		
	
	
		
			
				
					|  |  |  | @ -301,22 +365,21 @@ void Localizer::handle_gnss(double current_time, const cereal::GnssMeasurements: | 
			
		
	
		
			
				
					|  |  |  |  |   } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   double sensor_time = log.getMeasTime() * 1e-9; | 
			
		
	
		
			
				
					|  |  |  |  |   sensor_time -= this->gps_time_offset; | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   auto ecef_pos_v = log.getPositionECEF().getValue(); | 
			
		
	
		
			
				
					|  |  |  |  |   VectorXd ecef_pos = Vector3d(ecef_pos_v[0], ecef_pos_v[1], ecef_pos_v[2]); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   auto ecef_pos_std = log.getPositionECEF().getStd(); | 
			
		
	
		
			
				
					|  |  |  |  |   VectorXd ecef_pos_var = Vector3d(ecef_pos_std[0], ecef_pos_std[1], ecef_pos_std[2]).array().square().matrix(); | 
			
		
	
		
			
				
					|  |  |  |  |   double ecef_pos_std_norm = std::sqrt(ecef_pos_var.sum()); | 
			
		
	
		
			
				
					|  |  |  |  |   MatrixXdr ecef_pos_R = ecef_pos_var.asDiagonal(); | 
			
		
	
		
			
				
					|  |  |  |  |   // indexed at 0 cause all std values are the same MAE
 | 
			
		
	
		
			
				
					|  |  |  |  |   auto ecef_pos_std = log.getPositionECEF().getStd()[0]; | 
			
		
	
		
			
				
					|  |  |  |  |   MatrixXdr ecef_pos_R = Vector3d::Constant(pow(this->gps_std_factor*ecef_pos_std, 2)).asDiagonal(); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   auto ecef_vel_v = log.getVelocityECEF().getValue(); | 
			
		
	
		
			
				
					|  |  |  |  |   VectorXd ecef_vel = Vector3d(ecef_vel_v[0], ecef_vel_v[1], ecef_vel_v[2]); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   auto ecef_vel_std = log.getVelocityECEF().getStd(); | 
			
		
	
		
			
				
					|  |  |  |  |   VectorXd ecef_vel_var = Vector3d(ecef_vel_std[0], ecef_vel_std[1], ecef_vel_std[2]).array().square().matrix(); | 
			
		
	
		
			
				
					|  |  |  |  |   double ecef_vel_std_norm = std::sqrt(ecef_vel_var.sum()); | 
			
		
	
		
			
				
					|  |  |  |  |   MatrixXdr ecef_vel_R = ecef_vel_var.asDiagonal(); | 
			
		
	
		
			
				
					|  |  |  |  |   // indexed at 0 cause all std values are the same MAE
 | 
			
		
	
		
			
				
					|  |  |  |  |   auto ecef_vel_std = log.getVelocityECEF().getStd()[0]; | 
			
		
	
		
			
				
					|  |  |  |  |   MatrixXdr ecef_vel_R = Vector3d::Constant(pow(this->gps_std_factor*ecef_vel_std, 2)).asDiagonal(); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   double gps_est_error = (this->kf->get_x().segment<STATE_ECEF_POS_LEN>(STATE_ECEF_POS_START) - ecef_pos).norm(); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
	
		
			
				
					|  |  |  | @ -339,13 +402,13 @@ void Localizer::handle_gnss(double current_time, const cereal::GnssMeasurements: | 
			
		
	
		
			
				
					|  |  |  |  |   } | 
			
		
	
		
			
				
					|  |  |  |  |   VectorXd initial_pose_ecef_quat = quat2vector(euler2quat(ecef_euler_from_ned({ ecef_pos(0), ecef_pos(1), ecef_pos(2) }, orientation_ned_gps))); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   if (ecef_pos_std_norm > GPS_POS_STD_THRESHOLD || ecef_vel_std_norm > GPS_VEL_STD_THRESHOLD) { | 
			
		
	
		
			
				
					|  |  |  |  |   if (ecef_pos_std > GPS_POS_STD_THRESHOLD || ecef_vel_std > GPS_VEL_STD_THRESHOLD) { | 
			
		
	
		
			
				
					|  |  |  |  |     this->determine_gps_mode(current_time); | 
			
		
	
		
			
				
					|  |  |  |  |     return; | 
			
		
	
		
			
				
					|  |  |  |  |   } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   // prevent jumping gnss measurements (covered lots, standstill...)
 | 
			
		
	
		
			
				
					|  |  |  |  |   bool orientation_reset = ecef_vel_std_norm < GPS_VEL_STD_RESET_THRESHOLD; | 
			
		
	
		
			
				
					|  |  |  |  |   bool orientation_reset = ecef_vel_std < GPS_VEL_STD_RESET_THRESHOLD; | 
			
		
	
		
			
				
					|  |  |  |  |   orientation_reset &= orientation_error.norm() > GPS_ORIENTATION_ERROR_RESET_THRESHOLD; | 
			
		
	
		
			
				
					|  |  |  |  |   orientation_reset &= !this->standstill; | 
			
		
	
		
			
				
					|  |  |  |  |   if (orientation_reset) { | 
			
		
	
	
		
			
				
					|  |  |  | @ -354,7 +417,7 @@ void Localizer::handle_gnss(double current_time, const cereal::GnssMeasurements: | 
			
		
	
		
			
				
					|  |  |  |  |     this->orientation_reset_count = 0; | 
			
		
	
		
			
				
					|  |  |  |  |   } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   if ((gps_est_error > GPS_POS_ERROR_RESET_THRESHOLD && ecef_pos_std_norm < GPS_POS_STD_RESET_THRESHOLD) || this->last_gps_msg == 0) { | 
			
		
	
		
			
				
					|  |  |  |  |   if ((gps_est_error > GPS_POS_ERROR_RESET_THRESHOLD && ecef_pos_std < GPS_POS_STD_RESET_THRESHOLD) || this->last_gps_msg == 0) { | 
			
		
	
		
			
				
					|  |  |  |  |     // always reset on first gps message and if the location is off but the accuracy is high
 | 
			
		
	
		
			
				
					|  |  |  |  |     LOGE("Locationd vs gnssMeasurement position difference too large, kalman reset"); | 
			
		
	
		
			
				
					|  |  |  |  |     this->reset_kalman(NAN, initial_pose_ecef_quat, ecef_pos, ecef_vel, ecef_pos_R, ecef_vel_R); | 
			
		
	
	
		
			
				
					|  |  |  | @ -524,8 +587,12 @@ void Localizer::handle_msg(const cereal::Event::Reader& log) { | 
			
		
	
		
			
				
					|  |  |  |  |     this->handle_sensor(t, log.getAccelerometer()); | 
			
		
	
		
			
				
					|  |  |  |  |   } else if (log.isGyroscope()) { | 
			
		
	
		
			
				
					|  |  |  |  |     this->handle_sensor(t, log.getGyroscope()); | 
			
		
	
		
			
				
					|  |  |  |  |   } else if (log.isGnssMeasurements()) { | 
			
		
	
		
			
				
					|  |  |  |  |     this->handle_gnss(t, log.getGnssMeasurements()); | 
			
		
	
		
			
				
					|  |  |  |  |   } else if (log.isGpsLocation()) { | 
			
		
	
		
			
				
					|  |  |  |  |     this->handle_gps(t, log.getGpsLocation(), GPS_QUECTEL_SENSOR_TIME_OFFSET); | 
			
		
	
		
			
				
					|  |  |  |  |   } else if (log.isGpsLocationExternal()) { | 
			
		
	
		
			
				
					|  |  |  |  |     this->handle_gps(t, log.getGpsLocationExternal(), GPS_UBLOX_SENSOR_TIME_OFFSET); | 
			
		
	
		
			
				
					|  |  |  |  |   //} else if (log.isGnssMeasurements()) {
 | 
			
		
	
		
			
				
					|  |  |  |  |   //  this->handle_gnss(t, log.getGnssMeasurements());
 | 
			
		
	
		
			
				
					|  |  |  |  |   } else if (log.isCarState()) { | 
			
		
	
		
			
				
					|  |  |  |  |     this->handle_car_state(t, log.getCarState()); | 
			
		
	
		
			
				
					|  |  |  |  |   } else if (log.isCameraOdometry()) { | 
			
		
	
	
		
			
				
					|  |  |  | @ -586,12 +653,38 @@ void Localizer::determine_gps_mode(double current_time) { | 
			
		
	
		
			
				
					|  |  |  |  |   } | 
			
		
	
		
			
				
					|  |  |  |  | } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | void Localizer::configure_gnss_source(const LocalizerGnssSource &source) { | 
			
		
	
		
			
				
					|  |  |  |  |   this->gnss_source = source; | 
			
		
	
		
			
				
					|  |  |  |  |   if (source == LocalizerGnssSource::UBLOX) { | 
			
		
	
		
			
				
					|  |  |  |  |     this->gps_std_factor = 10.0; | 
			
		
	
		
			
				
					|  |  |  |  |     this->gps_variance_factor = 1.0; | 
			
		
	
		
			
				
					|  |  |  |  |     this->gps_vertical_variance_factor = 1.0; | 
			
		
	
		
			
				
					|  |  |  |  |     this->gps_time_offset = GPS_UBLOX_SENSOR_TIME_OFFSET; | 
			
		
	
		
			
				
					|  |  |  |  |   } else { | 
			
		
	
		
			
				
					|  |  |  |  |     this->gps_std_factor = 2.0; | 
			
		
	
		
			
				
					|  |  |  |  |     this->gps_variance_factor = 0.0; | 
			
		
	
		
			
				
					|  |  |  |  |     this->gps_vertical_variance_factor = 3.0; | 
			
		
	
		
			
				
					|  |  |  |  |     this->gps_time_offset = GPS_QUECTEL_SENSOR_TIME_OFFSET; | 
			
		
	
		
			
				
					|  |  |  |  |   } | 
			
		
	
		
			
				
					|  |  |  |  | } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | int Localizer::locationd_thread() { | 
			
		
	
		
			
				
					|  |  |  |  |   const std::initializer_list<const char *> service_list = {"gnssMeasurements", "cameraOdometry", "liveCalibration", | 
			
		
	
		
			
				
					|  |  |  |  |   LocalizerGnssSource source; | 
			
		
	
		
			
				
					|  |  |  |  |   const char* gps_location_socket; | 
			
		
	
		
			
				
					|  |  |  |  |   if (Params().getBool("UbloxAvailable")) { | 
			
		
	
		
			
				
					|  |  |  |  |     source = LocalizerGnssSource::UBLOX; | 
			
		
	
		
			
				
					|  |  |  |  |     gps_location_socket = "gpsLocationExternal"; | 
			
		
	
		
			
				
					|  |  |  |  |   } else { | 
			
		
	
		
			
				
					|  |  |  |  |     source = LocalizerGnssSource::QCOM; | 
			
		
	
		
			
				
					|  |  |  |  |     gps_location_socket = "gpsLocation"; | 
			
		
	
		
			
				
					|  |  |  |  |   } | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   this->configure_gnss_source(source); | 
			
		
	
		
			
				
					|  |  |  |  |   const std::initializer_list<const char *> service_list = {gps_location_socket, "cameraOdometry", "liveCalibration", | 
			
		
	
		
			
				
					|  |  |  |  |                                                           "carState", "carParams", "accelerometer", "gyroscope"}; | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   // TODO: remove carParams once we're always sending at 100Hz
 | 
			
		
	
		
			
				
					|  |  |  |  |   SubMaster sm(service_list, {}, nullptr, {"gnssMeasurements", "carParams"}); | 
			
		
	
		
			
				
					|  |  |  |  |   SubMaster sm(service_list, {}, nullptr, {gps_location_socket, "carParams"}); | 
			
		
	
		
			
				
					|  |  |  |  |   PubMaster pm({"liveLocationKalman"}); | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   uint64_t cnt = 0; | 
			
		
	
	
		
			
				
					|  |  |  | 
 |