diff --git a/include/gnss/processor/processor_gnss_fix.h b/include/gnss/processor/processor_gnss_fix.h index 1fa78ddd321ef08d9c1d557a7bd2ea7a5fac703a..a2bdf9edc92a504082fc1b5fbecabec311c10687 100644 --- a/include/gnss/processor/processor_gnss_fix.h +++ b/include/gnss/processor/processor_gnss_fix.h @@ -23,8 +23,8 @@ #define WOLF_PROCESSOR_GNSS_FIX_H // Wolf includes +#include "gnss/internal/config.h" #include "core/processor/processor_base.h" -#include "gnss/capture/capture_gnss.h" #include "gnss/sensor/sensor_gnss.h" // Std includes @@ -127,9 +127,8 @@ class ProcessorGnssFix : public ProcessorBase SensorGnssPtr sensor_gnss_; ParamsProcessorGnssFixPtr params_gnss_; CaptureBasePtr last_KF_capture_, incoming_capture_; - FeatureGnssFixPtr last_KF_feature_; + FeatureBasePtr last_KF_feature_, incoming_feature_; FrameBasePtr last_KF_; - GnssUtils::ComputePosOutput incoming_pos_out_; Eigen::Vector3d first_pos_; VectorComposite first_frame_state_; @@ -155,43 +154,42 @@ class ProcessorGnssFix : public ProcessorBase */ void processKeyFrame(FrameBasePtr _keyframe_ptr) override {}; + /** \brief trigger in capture * * Returns true if processCapture() should be called after the provided capture arrived. */ - bool triggerInCapture(CaptureBasePtr) const override; + bool triggerInCapture(CaptureBasePtr) const override {return true;}; /** \brief trigger in key-frame * * The ProcessorTracker only processes incoming captures, then it returns false. */ - bool triggerInKeyFrame(FrameBasePtr _keyframe_ptr) const override {return false;} + bool triggerInKeyFrame(FrameBasePtr _keyframe_ptr) const override {return false;}; /** \brief store key frame * * Returns true if the key frame should be stored */ - bool storeKeyFrame(FrameBasePtr) override; + bool storeKeyFrame(FrameBasePtr) override {return true;}; /** \brief store capture * * Returns true if the capture should be stored */ - bool storeCapture(CaptureBasePtr) override; + bool storeCapture(CaptureBasePtr) override {return false;}; bool voteForKeyFrame() const override; private: + void processStoredFrames(); + void handleEnuMap(FeatureBasePtr incoming_feature); + FeatureBasePtr emplaceFeature(CaptureBasePtr _capture); FactorBasePtr emplaceFactor(FeatureBasePtr _ftr_ptr); bool detectOutlier(FactorBasePtr ctr_ptr); }; -inline bool ProcessorGnssFix::triggerInCapture(CaptureBasePtr) const -{ - return true; -} - } // namespace wolf #endif //WOLF_PROCESSOR_GNSS_FIX_H diff --git a/src/processor/processor_gnss_fix.cpp b/src/processor/processor_gnss_fix.cpp index 31f95fd152bb034143384d3d96ee03b5029c6479..6066c1f0aea1e80fbfb698fda952de48cd9bf872 100644 --- a/src/processor/processor_gnss_fix.cpp +++ b/src/processor/processor_gnss_fix.cpp @@ -19,12 +19,12 @@ // along with this program. If not, see <http://www.gnu.org/licenses/>. // //--------LICENSE_END-------- +#include "gnss/processor/processor_gnss_fix.h" #include "gnss/capture/capture_gnss.h" #include "gnss/capture/capture_gnss_fix.h" #include "gnss/factor/factor_gnss_fix_2d.h" #include "gnss/factor/factor_gnss_fix_3d.h" #include "gnss/feature/feature_gnss_fix.h" -#include "gnss/processor/processor_gnss_fix.h" namespace wolf { @@ -41,59 +41,148 @@ ProcessorGnssFix::~ProcessorGnssFix() // } +void ProcessorGnssFix::processStoredFrames() +{ + // clean buffers (useless entries) + if (not buffer_frame_.empty() and not buffer_capture_.empty()) + { + buffer_frame_ .removeUpToLower(buffer_capture_.getContainer().begin()->first-params_->time_tolerance); + buffer_capture_.removeUpToLower(buffer_frame_ .getContainer().begin()->first-params_->time_tolerance); + } + + // iterate over stored frames + auto frm_it = buffer_frame_.getContainer().begin(); + while (frm_it != buffer_frame_.getContainer().end()) + { + // Search for any stored capture within time tolerance of frame + auto capture = buffer_capture_.select(frm_it->first, params_->time_tolerance); + + // Capture found + if (capture) + { + // safety check: capture stored already linked + if (capture->getFrame()) + { + WOLF_WARN("ProcessorGnssFix::processStoredFrames: any stored CaptureGnss was already linked to a frame"); + buffer_capture_.getContainer().erase(capture->getTimeStamp()); + frm_it++; + continue; + } + // link capture + capture->link(frm_it->second); + + // get feature + FeatureGnssFixPtr feat_fix; + auto feature_list = capture->getFeatureList(); + for (auto feat : feature_list) + { + feat_fix = std::dynamic_pointer_cast<FeatureGnssFix>(feat); + if (feat_fix) + break; + } + if (not feat_fix) + { + WOLF_WARN("ProcessorGnssFix::processStoredFrames: A stored CaptureGnss doesn't have any FrameGnssFix"); + buffer_capture_.getContainer().erase(capture->getTimeStamp()); + frm_it++; + continue; + } + + // emplace factor + auto fac = emplaceFactor(feat_fix); + + // outlier rejection (only can be evaluated if ENU defined and ENU-MAP well initialized: fixed) + if (params_gnss_->remove_outliers and detectOutlier(fac)) + { + feat_fix->remove(); + buffer_capture_.getContainer().erase(capture->getTimeStamp()); + frm_it++; + continue; + } + + // Handle ENU definition, ENU-MAP initialization etc. + handleEnuMap(feat_fix); + + // store last KF (if more recent) + if (not last_KF_capture_ or last_KF_capture_->getTimeStamp() < capture->getTimeStamp()) + { + last_KF_capture_ = capture; + last_KF_feature_ = feat_fix; + } + + // remove capture from buffer + buffer_capture_.getContainer().erase(capture->getTimeStamp()); + + // remove frame from buffer + frm_it = buffer_frame_.getContainer().erase(frm_it); + } + else + frm_it++; + } +} + +FeatureBasePtr ProcessorGnssFix::emplaceFeature(CaptureBasePtr _capture) +{ + GnssUtils::ComputePosOutput pos_output; + if (std::dynamic_pointer_cast<CaptureGnss>(_capture)) + { + WOLF_DEBUG("ProcessorGnssFix: creating feature from incoming raw measurement. Type:", _capture->getType()); + auto raw_capture = std::static_pointer_cast<CaptureGnss>(_capture); + pos_output = GnssUtils::computePos(*(raw_capture->getObservations()), *(raw_capture->getNavigation()), params_gnss_->compute_pos_opt); + if (!pos_output.success) // error + { + WOLF_DEBUG("computePos failed with msg: ", pos_output.msg); + return nullptr; + } + } + else if (std::dynamic_pointer_cast<CaptureGnssFix>(_capture)) + { + WOLF_DEBUG("ProcessorGnssFix: creating feature from incoming navsatfix. Type:", _capture->getType()); + auto fix_capture = std::static_pointer_cast<CaptureGnssFix>(_capture); + pos_output.time = fix_capture->getGpsTimeStamp().getSeconds(); // time_t time; + pos_output.sec = 1e-9 * fix_capture->getGpsTimeStamp().getSeconds(); // double sec; + pos_output.pos = fix_capture->getPositionEcef(); // Eigen::Vector3d pos; // position (m) + pos_output.vel = Eigen::Vector3d::Zero(); // Eigen::Vector3d vel; // velocity (m/s) + pos_output.pos_covar = fix_capture->getCovarianceEcef(); // Eigen::Matrix3d pos_covar; // position covariance (m^2) + pos_output.type = 0; // int type; // coordinates used (0:xyz-ecef,1:enu-baseline) + } + else + throw std::runtime_error("ProcessorGnssFix::emplaceFeature wrong capture type"); + + return FeatureBase::emplace<FeatureGnssFix>(_capture, pos_output); +} + void ProcessorGnssFix::processCapture(CaptureBasePtr _capture) { - // TODO: keep captures in a buffer and deal with KFpacks WOLF_DEBUG("PR ", getName()," process() capture type: ", _capture->getType()); - incoming_capture_ = _capture; - bool KF_created = false; + // Check capture type + if (not std::dynamic_pointer_cast<CaptureGnss>(_capture) and not std::dynamic_pointer_cast<CaptureGnssFix>(_capture)) + throw std::runtime_error("ProcessorGnssFix::processCapture capture of bad type. Accepted types: CaptureGnss and CaptureGnssFix"); - // Check type of capture: GNSS raw - bool israw = false; - if (std::dynamic_pointer_cast<CaptureGnss>(_capture)!=nullptr) - israw = true; - // if not raw nor Fix, not processable - else if (std::dynamic_pointer_cast<CaptureGnssFix>(_capture)==nullptr) - return; + // First process stored frames + processStoredFrames(); - // Only process raw if fix_from_raw - if (israw and !params_gnss_->fix_from_raw) + // Only process raw if fix_from_raw and fix if not fix_from_raw + bool israw = std::dynamic_pointer_cast<CaptureGnss>(_capture)!=nullptr; + if (israw and not params_gnss_->fix_from_raw) return; - - // Only process fix if not fix_from_raw - if (!israw and params_gnss_->fix_from_raw) + if (not israw and params_gnss_->fix_from_raw) return; + // Process incoming capture + incoming_capture_ = _capture; + bool KF_created = false; FrameBasePtr new_frame = nullptr; FactorBasePtr new_fac = nullptr; - // emplace features - if (israw) - { - auto raw_capture = std::static_pointer_cast<CaptureGnss>(incoming_capture_); - incoming_pos_out_ = GnssUtils::computePos(*(raw_capture->getObservations()), *(raw_capture->getNavigation()), params_gnss_->compute_pos_opt); - if (!incoming_pos_out_.success) // error - { - WOLF_DEBUG("computePos failed with msg: ", incoming_pos_out_.msg); - return; - } - } - else - { - WOLF_DEBUG("ProcessorGnssFix: creating feature from incoming navsatfix. Type:", incoming_capture_->getType()); - auto fix_capture = std::static_pointer_cast<CaptureGnssFix>(incoming_capture_); - incoming_pos_out_.time = fix_capture->getGpsTimeStamp().getSeconds(); // time_t time; - incoming_pos_out_.sec = 1e-9 * fix_capture->getGpsTimeStamp().getSeconds(); // double sec; - incoming_pos_out_.pos = fix_capture->getPositionEcef(); // Eigen::Vector3d pos; // position (m) - incoming_pos_out_.vel = Eigen::Vector3d::Zero(); // Eigen::Vector3d vel; // velocity (m/s) - incoming_pos_out_.pos_covar = fix_capture->getCovarianceEcef(); // Eigen::Matrix3d pos_covar; // position covariance (m^2) - incoming_pos_out_.type = 0; // int type; // coordinates used (0:xyz-ecef,1:enu-baseline) - } - auto incoming_feature = FeatureBase::emplace<FeatureGnssFix>(incoming_capture_, incoming_pos_out_); + // emplace feature + incoming_feature_ = emplaceFeature(incoming_capture_); + if (not incoming_feature_) + return; // ALREADY CREATED KF - FrameBasePtr keyframe = buffer_frame_.select( incoming_capture_->getTimeStamp(), params_gnss_->time_tolerance); + FrameBasePtr keyframe = buffer_frame_.select(incoming_capture_->getTimeStamp(), params_gnss_->time_tolerance); if (keyframe and last_KF_capture_ and keyframe == last_KF_capture_->getFrame()) keyframe = nullptr; if (keyframe) @@ -108,70 +197,82 @@ void ProcessorGnssFix::processCapture(CaptureBasePtr _capture) new_frame = getProblem()->emplaceFrame(incoming_capture_->getTimeStamp()); KF_created = true; } - // OTHERWISE return + // OTHERWISE store capture else + { + buffer_capture_.emplace(_capture->getTimeStamp(), _capture); return; + } - // ESTABLISH FACTOR // link capture incoming_capture_->link(new_frame); + // emplace factor - new_fac = emplaceFactor(incoming_feature); + new_fac = emplaceFactor(incoming_feature_); // outlier rejection (only can be evaluated if ENU defined and ENU-MAP well initialized: fixed) - if (params_gnss_->remove_outliers and - sensor_gnss_->isEnuDefined() and - sensor_gnss_->isEnuMapFixed() and - detectOutlier(new_fac)) + if (params_gnss_->remove_outliers and detectOutlier(new_fac)) { - new_frame->remove(); + if (KF_created) + new_frame->remove(); + else + incoming_feature_->remove(); return; } + // Handle ENU definition, ENU-MAP initialization etc. + handleEnuMap(incoming_feature_); + // store last KF last_KF_capture_ = incoming_capture_; - last_KF_feature_ = incoming_feature; + last_KF_feature_ = incoming_feature_; + + // Notify if KF created + if (KF_created) + getProblem()->keyFrameCallback(new_frame, shared_from_this()); +} +void ProcessorGnssFix::handleEnuMap(FeatureBasePtr feature) +{ // Define ENU (if not defined) if (!sensor_gnss_->isEnuDefined()) { - WOLF_DEBUG("Defining ecef enu"); - sensor_gnss_->setEcefEnu(incoming_feature->getMeasurement()); + WOLF_INFO("Defining ecef enu with first fix"); + sensor_gnss_->setEcefEnu(feature->getMeasurement()); } - // Store the first capture that established a factor (for initializing ENU-MAP) + // Store the first capture that established a factor (for later initialization ENU-MAP) if (first_frame_state_.empty() and not sensor_gnss_->isEnuMapFixed()) { - first_frame_state_ = incoming_capture_->getFrame()->getState(); - first_pos_ = incoming_feature->getMeasurement(); + first_frame_state_ = feature->getCapture()->getFrame()->getState(); + first_pos_ = feature->getMeasurement(); } + // Initialize ENU-MAP if: ENU defined and ENU-MAP not initialized (and not fixed) and far enough if (params_gnss_->init_enu_map and not first_frame_state_.empty() and sensor_gnss_->isEnuDefined() and not sensor_gnss_->isEnuMapInitialized() and not sensor_gnss_->isEnuMapFixed() and - (first_pos_-incoming_pos_out_.pos).norm() > params_gnss_->enu_map_init_dist_min) + (first_pos_-feature->getMeasurement()).norm() > params_gnss_->enu_map_init_dist_min) { - assert(first_frame_state_.count('P') and first_frame_state_.count('O') and incoming_capture_->getFrame() != nullptr); + assert(first_frame_state_.count('P') and + first_frame_state_.count('O') and + feature->getCapture()->getFrame() != nullptr); sensor_gnss_->initializeEnuMap(first_frame_state_.vector("PO"), first_pos_, - incoming_capture_->getFrame()->getState().vector("PO"), - incoming_feature->getMeasurement()); + feature->getCapture()->getFrame()->getState().vector("PO"), + feature->getMeasurement()); // Set as not-initialized if factors not separated enough ( < enu_map_init_dist_max) - if ((first_pos_ - incoming_feature->getMeasurement()).norm() < params_gnss_->enu_map_init_dist_max) + if ((first_pos_ - feature->getMeasurement()).norm() < params_gnss_->enu_map_init_dist_max) sensor_gnss_->setEnuMapInitialized(false); } // Fix ENU-MAP if separated enough from first pos ( > enu_map_fix_dist ) - if ((first_pos_ - incoming_feature->getMeasurement()).norm() > params_gnss_->enu_map_fix_dist) + if ((first_pos_ - feature->getMeasurement()).norm() > params_gnss_->enu_map_fix_dist) sensor_gnss_->fixEnuMap(); - - // Notify if KF created - if (KF_created) - getProblem()->keyFrameCallback(new_frame, shared_from_this()); } FactorBasePtr ProcessorGnssFix::emplaceFactor(FeatureBasePtr _ftr) @@ -216,8 +317,8 @@ bool ProcessorGnssFix::voteForKeyFrame() const sensor_gnss_->isEnuDefined() and not sensor_gnss_->isEnuMapInitialized() and not sensor_gnss_->isEnuMapFixed() and - (first_pos_-incoming_pos_out_.pos).norm() > params_gnss_->enu_map_init_dist_min and - (first_pos_-incoming_pos_out_.pos).norm() < params_gnss_->enu_map_init_dist_max) + (first_pos_-incoming_feature_->getMeasurement()).norm() > params_gnss_->enu_map_init_dist_min and + (first_pos_-incoming_feature_->getMeasurement()).norm() < params_gnss_->enu_map_init_dist_max) { WOLF_DEBUG("KF because of enu map not initialized"); return true; @@ -225,9 +326,9 @@ bool ProcessorGnssFix::voteForKeyFrame() const // Distance criterion (ENU defined and ENU-MAP initialized) if (last_KF_capture_ != nullptr and - (incoming_pos_out_.pos - last_KF_feature_->getMeasurement()).norm() > params_gnss_->dist_traveled) + (incoming_feature_->getMeasurement() - last_KF_feature_->getMeasurement()).norm() > params_gnss_->dist_traveled) { - WOLF_DEBUG("KF because of distance criterion: ", (incoming_pos_out_.pos - last_KF_feature_->getMeasurement()).norm()); + WOLF_DEBUG("KF because of distance criterion: ", (incoming_feature_->getMeasurement() - last_KF_feature_->getMeasurement()).norm()); return true; } @@ -239,6 +340,9 @@ bool ProcessorGnssFix::detectOutlier(FactorBasePtr fac) { WOLF_DEBUG( "PR ", getName()," rejectOutlier..."); + if (not sensor_gnss_->isEnuDefined() or not sensor_gnss_->isEnuMapFixed()) + return false; + // Cast feature auto gnss_ftr = std::static_pointer_cast<FeatureGnssFix>(fac->getFeature()); @@ -273,14 +377,6 @@ bool ProcessorGnssFix::detectOutlier(FactorBasePtr fac) return false; } -bool ProcessorGnssFix::storeKeyFrame(FrameBasePtr _frame_ptr) -{ - return true; -} -bool ProcessorGnssFix::storeCapture(CaptureBasePtr _cap_ptr) -{ - return false; -} void ProcessorGnssFix::configure(SensorBasePtr _sensor) { sensor_gnss_ = std::static_pointer_cast<SensorGnss>(_sensor); diff --git a/test/gtest_factor_gnss_fix_2d.cpp b/test/gtest_factor_gnss_fix_2d.cpp index a95688fd9c9c73b8456f3d81e2ebc036edf9eee9..a5a439cfc4d7ec232122da70552d75fecfc3a875 100644 --- a/test/gtest_factor_gnss_fix_2d.cpp +++ b/test/gtest_factor_gnss_fix_2d.cpp @@ -153,6 +153,8 @@ TEST(FactorGnssFix2dTest, configure_tree) */ TEST(FactorGnssFix2dTest, gnss_1_map_base_position) { + problem_ptr->print(); + // --------------------------- Reset states resetStates(gnss_sensor_ptr, frame_ptr, o_enu_map, t_base_antena, t_enu_map, t_map_base, o_map_base);