Skip to content
Snippets Groups Projects
Commit 00aeed38 authored by Joan Vallvé Navarro's avatar Joan Vallvé Navarro
Browse files

removed _ptr

parent e325ba60
No related branches found
No related tags found
No related merge requests found
......@@ -38,10 +38,10 @@ struct ProcessorParamsGnssFix : public ProcessorParamsBase
class ProcessorGnssFix : public ProcessorBase
{
protected:
SensorGnssPtr sensor_gnss_ptr_;
SensorGnssPtr sensor_gnss_;
ProcessorParamsGnssFixPtr params_gnss_;
CaptureGnssFixPtr first_capture_ptr_, last_capture_ptr_;
FrameBasePtr last_gnss_fix_KF_;
CaptureGnssFixPtr first_capture_, incoming_capture_;
FrameBasePtr last_KF_;
public:
ProcessorGnssFix(ProcessorParamsGnssFixPtr _params, SensorGnssPtr _sensor_gnss_ptr);
......
......@@ -38,12 +38,13 @@ struct ProcessorParamsGnssSingleDiff : public ProcessorParamsBase
class ProcessorGnssSingleDiff : public ProcessorBase
{
protected:
SensorGnssPtr sensor_gnss_ptr_;
SensorGnssPtr sensor_gnss_;
ProcessorParamsGnssSingleDiffPtr params_gnss_;
CaptureGnssSingleDiffPtr last_capture_ptr_, incoming_capture_ptr_;
CaptureGnssSingleDiffPtr incoming_capture_;
FrameBasePtr last_KF_;
public:
ProcessorGnssSingleDiff(ProcessorParamsGnssSingleDiffPtr _params_gnss, SensorGnssPtr _sensor_gnss_ptr);
ProcessorGnssSingleDiff(ProcessorParamsGnssSingleDiffPtr _params_gnss, SensorGnssPtr _sensor_gnss);
virtual ~ProcessorGnssSingleDiff();
virtual void configure(SensorBasePtr _sensor);
......@@ -59,7 +60,7 @@ class ProcessorGnssSingleDiff : public ProcessorBase
*
* The ProcessorTracker only processes incoming captures (it is not called).
*/
virtual void processKeyFrame(FrameBasePtr _keyframe_ptr, const Scalar& _time_tolerance) override {};
virtual void processKeyFrame(FrameBasePtr _keyframe, const Scalar& _time_tolerance) override {};
/** \brief trigger in capture
*
......@@ -71,16 +72,16 @@ class ProcessorGnssSingleDiff : public ProcessorBase
*
* The ProcessorTracker only processes incoming captures, then it returns false.
*/
virtual bool triggerInKeyFrame(FrameBasePtr _keyframe_ptr, const Scalar& _time_tolerance) override {return false;}
virtual bool triggerInKeyFrame(FrameBasePtr _keyframe, const Scalar& _time_tolerance) override {return false;}
virtual bool voteForKeyFrame();
private:
FactorBasePtr emplaceFactor(FeatureBasePtr& ftr_ptr);
FactorBasePtr emplaceFactor(FeatureBasePtr& ftr);
public:
static ProcessorBasePtr create(const std::string& _unique_name, const ProcessorParamsBasePtr _params, const SensorBasePtr sensor_ptr);
static ProcessorBasePtr create(const std::string& _unique_name, const ProcessorParamsBasePtr _params, const SensorBasePtr sensor);
};
......
......@@ -7,11 +7,11 @@
namespace wolf
{
ProcessorGnssFix::ProcessorGnssFix(ProcessorParamsGnssFixPtr _params_gnss, SensorGnssPtr _sensor_gnss_ptr) :
ProcessorGnssFix::ProcessorGnssFix(ProcessorParamsGnssFixPtr _params_gnss, SensorGnssPtr _sensor_gnss) :
ProcessorBase("GNSS FIX", _params_gnss),
sensor_gnss_ptr_(_sensor_gnss_ptr),
sensor_gnss_(_sensor_gnss),
params_gnss_(_params_gnss),
first_capture_ptr_(nullptr)
first_capture_(nullptr)
{
//
}
......@@ -21,63 +21,63 @@ ProcessorGnssFix::~ProcessorGnssFix()
//
}
void ProcessorGnssFix::processCapture(CaptureBasePtr _capture_ptr)
void ProcessorGnssFix::processCapture(CaptureBasePtr _capture)
{
// TODO: keep captures in a buffer and deal with KFpacks
//WOLF_DEBUG("ProcessorGnssFix::process()");
last_capture_ptr_ = std::static_pointer_cast<CaptureGnssFix>(_capture_ptr);
incoming_capture_ = std::static_pointer_cast<CaptureGnssFix>(_capture);
FrameBasePtr new_frame_ptr = nullptr;
FrameBasePtr new_frame = nullptr;
FactorBasePtr new_fac = nullptr;
// ALREADY CREATED KF
PackKeyFramePtr KF_pack = buffer_pack_kf_.selectPack( _capture_ptr, params_gnss_->time_tolerance);
if (KF_pack && KF_pack->key_frame != last_gnss_fix_KF_)
PackKeyFramePtr KF_pack = buffer_pack_kf_.selectPack( incoming_capture_, params_gnss_->time_tolerance);
if (KF_pack && KF_pack->key_frame != last_KF_)
{
WOLF_DEBUG( "PR ", getName()," - capture ", _capture_ptr->id(), " appended to existing KF " , KF_pack->key_frame->id() , " TS: ", KF_pack->key_frame->getTimeStamp());
new_frame_ptr = KF_pack->key_frame;
WOLF_DEBUG( "PR ", getName()," - capture ", incoming_capture_->id(), " appended to existing KF " , KF_pack->key_frame->id() , " TS: ", KF_pack->key_frame->getTimeStamp());
new_frame = KF_pack->key_frame;
}
// MAKE KF
else if (voteForKeyFrame() && permittedKeyFrame())
{
new_frame_ptr = getProblem()->emplaceFrame(KEY, _capture_ptr->getTimeStamp());
getProblem()->keyFrameCallback(new_frame_ptr, shared_from_this(), params_gnss_->time_tolerance);
WOLF_DEBUG( "PR ", getName()," - capture ", _capture_ptr->id(), " appended to new KF " , new_frame_ptr->id() , " TS: ", new_frame_ptr->getTimeStamp());
new_frame = getProblem()->emplaceFrame(KEY, incoming_capture_->getTimeStamp());
getProblem()->keyFrameCallback(new_frame, shared_from_this(), params_gnss_->time_tolerance);
WOLF_DEBUG( "PR ", getName()," - capture ", incoming_capture_->id(), " appended to new KF " , new_frame->id() , " TS: ", new_frame->getTimeStamp());
}
// ESTABLISH FACTOR
if (new_frame_ptr)
if (new_frame)
{
// LINK CAPTURE
_capture_ptr->link(new_frame_ptr); // Add incoming Capture to the new Frame
incoming_capture_->link(new_frame); // Add incoming Capture to the new Frame
// EMPLACE FEATURES
WOLF_DEBUG( "PR ", getName()," - emplacing the feature...");
auto ftr_ptr = FeatureBase::emplace<FeatureGnssFix>(last_capture_ptr_, last_capture_ptr_->getData(),last_capture_ptr_->getDataCovariance());
auto ftr = FeatureBase::emplace<FeatureGnssFix>(incoming_capture_, incoming_capture_->getData(),incoming_capture_->getDataCovariance());
// EMPLACE FACTOR
new_fac = emplaceFactor(ftr_ptr);
new_fac = emplaceFactor(ftr);
// outlier rejection
if (sensor_gnss_ptr_->isEnuDefined() && sensor_gnss_ptr_->isEnuMapInitialized())
if (sensor_gnss_->isEnuDefined() && sensor_gnss_->isEnuMapInitialized())
if (rejectOutlier(new_fac))
new_fac = nullptr;
// store last KF
if (new_fac)
last_gnss_fix_KF_= new_frame_ptr;
last_KF_= new_frame;
}
// SET ECEF_ENU IF:
// 1 - not initialized
// 2 - factor established
if (!sensor_gnss_ptr_->isEnuDefined() && new_fac != nullptr)
if (!sensor_gnss_->isEnuDefined() && new_fac != nullptr)
{
WOLF_DEBUG("setting ecef enu");
sensor_gnss_ptr_->setEcefEnu(last_capture_ptr_->getData());
sensor_gnss_->setEcefEnu(incoming_capture_->getData());
// Store the first capture that established a factor
first_capture_ptr_ = last_capture_ptr_;
first_capture_ = incoming_capture_;
}
// INITIALIZE ENU_MAP IF 4 NECESSARY CONDITIONS:
......@@ -85,55 +85,55 @@ void ProcessorGnssFix::processCapture(CaptureBasePtr _capture_ptr)
// 2 - not initialized
// 3 - current capture in key-frame with factor
// 4 - two factors established (first and current) in frames separated enough ( > enu_map_init_dist_min)
if ( sensor_gnss_ptr_->isEnuDefined() &&
!sensor_gnss_ptr_->isEnuMapInitialized() &&
if ( sensor_gnss_->isEnuDefined() &&
!sensor_gnss_->isEnuMapInitialized() &&
new_fac != nullptr &&
first_capture_ptr_ != nullptr && first_capture_ptr_->getFrame() != nullptr && last_capture_ptr_->getFrame() != nullptr &&
(first_capture_ptr_->getFrame()->getState()-last_capture_ptr_->getFrame()->getState()).norm() > params_gnss_->enu_map_init_dist_min)
first_capture_ != nullptr && first_capture_->getFrame() != nullptr && incoming_capture_->getFrame() != nullptr &&
(first_capture_->getFrame()->getState()-incoming_capture_->getFrame()->getState()).norm() > params_gnss_->enu_map_init_dist_min)
{
WOLF_DEBUG("initializing enu map");
sensor_gnss_ptr_->initializeEnuMap(first_capture_ptr_->getFrame()->getState(), first_capture_ptr_->getData(),
last_capture_ptr_->getFrame()->getState(), last_capture_ptr_->getData());
sensor_gnss_->initializeEnuMap(first_capture_->getFrame()->getState(), first_capture_->getData(),
incoming_capture_->getFrame()->getState(), incoming_capture_->getData());
}
}
FactorBasePtr ProcessorGnssFix::emplaceFactor(FeatureBasePtr& ftr_ptr)
FactorBasePtr ProcessorGnssFix::emplaceFactor(FeatureBasePtr& ftr)
{
// CREATE CONSTRAINT --------------------
//WOLF_DEBUG("creating the factor...");
// 2D
if (getProblem()->getDim() == 2)
return FactorBase::emplace<FactorGnssFix2D>(ftr_ptr, ftr_ptr, sensor_gnss_ptr_, shared_from_this(), false, FAC_ACTIVE);
return FactorBase::emplace<FactorGnssFix2D>(ftr, ftr, sensor_gnss_, shared_from_this(), false, FAC_ACTIVE);
// 3D
else
return FactorBase::emplace<FactorGnssFix3D>(ftr_ptr, ftr_ptr, sensor_gnss_ptr_, shared_from_this(), false, FAC_ACTIVE);
return FactorBase::emplace<FactorGnssFix3D>(ftr, ftr, sensor_gnss_, shared_from_this(), false, FAC_ACTIVE);
}
bool ProcessorGnssFix::rejectOutlier(FactorBasePtr fac_ptr)
bool ProcessorGnssFix::rejectOutlier(FactorBasePtr fac)
{
// Cast feature
auto gnss_ftr_ptr = std::static_pointer_cast<FeatureGnssFix>(fac_ptr->getFeature());
auto gnss_ftr = std::static_pointer_cast<FeatureGnssFix>(fac->getFeature());
// copy states
Eigen::VectorXs x(gnss_ftr_ptr->getCapture()->getFrame()->getP()->getState());
Eigen::VectorXs o(gnss_ftr_ptr->getCapture()->getFrame()->getP()->getState());
Eigen::VectorXs x_antena(sensor_gnss_ptr_->getStateBlock(0)->getState());
Eigen::VectorXs t_ENU_map(sensor_gnss_ptr_->getEnuMapTranslation()->getState());
Eigen::VectorXs roll_ENU_map(sensor_gnss_ptr_->getEnuMapRoll()->getState());
Eigen::VectorXs pitch_ENU_map(sensor_gnss_ptr_->getEnuMapPitch()->getState());
Eigen::VectorXs yaw_ENU_map(sensor_gnss_ptr_->getEnuMapYaw()->getState());
Eigen::VectorXs x(gnss_ftr->getCapture()->getFrame()->getP()->getState());
Eigen::VectorXs o(gnss_ftr->getCapture()->getFrame()->getP()->getState());
Eigen::VectorXs x_antena(sensor_gnss_->getStateBlock(0)->getState());
Eigen::VectorXs t_ENU_map(sensor_gnss_->getEnuMapTranslation()->getState());
Eigen::VectorXs roll_ENU_map(sensor_gnss_->getEnuMapRoll()->getState());
Eigen::VectorXs pitch_ENU_map(sensor_gnss_->getEnuMapPitch()->getState());
Eigen::VectorXs yaw_ENU_map(sensor_gnss_->getEnuMapYaw()->getState());
// create Scalar* array of a copy of the state
Scalar* parameters[7] = {x.data(), o.data(), x_antena.data(), t_ENU_map.data(), roll_ENU_map.data(),
pitch_ENU_map.data(), yaw_ENU_map.data()};
// create residuals pointer
Eigen::VectorXs residuals(3);
// evaluate the factor in this state
fac_ptr->evaluate(parameters, residuals.data(), nullptr);
fac->evaluate(parameters, residuals.data(), nullptr);
// discard if residual too high evaluated at the current estimation
if (residuals.norm() > 1e3)
{
WOLF_WARN("Discarding GNSS FIX Factor, considered OUTLIER");
WOLF_TRACE("Feature: ", fac_ptr->getMeasurement().transpose(),"\nError: ",(fac_ptr->getMeasurementSquareRootInformationUpper().inverse()*residuals).transpose());
fac_ptr->remove();
WOLF_TRACE("Feature: ", fac->getMeasurement().transpose(),"\nError: ",(fac->getMeasurementSquareRootInformationUpper().inverse()*residuals).transpose());
fac->remove();
return true;
}
return false;
......@@ -142,7 +142,7 @@ bool ProcessorGnssFix::rejectOutlier(FactorBasePtr fac_ptr)
bool ProcessorGnssFix::voteForKeyFrame()
{
// Depending on time since the last KF with gnssfix capture
if (!last_gnss_fix_KF_ || (last_capture_ptr_->getTimeStamp() - last_gnss_fix_KF_->getTimeStamp()) > params_gnss_->time_th)
if (!last_KF_ || (incoming_capture_->getTimeStamp() - last_KF_->getTimeStamp()) > params_gnss_->time_th)
return true;
// TODO: more alternatives?
......@@ -153,15 +153,15 @@ bool ProcessorGnssFix::voteForKeyFrame()
void ProcessorGnssFix::configure(SensorBasePtr _sensor)
{
sensor_gnss_ptr_ = std::static_pointer_cast<SensorGnss>(_sensor);
sensor_gnss_ = std::static_pointer_cast<SensorGnss>(_sensor);
};
ProcessorBasePtr ProcessorGnssFix::create(const std::string& _unique_name, const ProcessorParamsBasePtr _params, const SensorBasePtr sensor_ptr)
ProcessorBasePtr ProcessorGnssFix::create(const std::string& _unique_name, const ProcessorParamsBasePtr _params, const SensorBasePtr sensor)
{
auto prc_ptr = std::make_shared<ProcessorGnssFix>(std::static_pointer_cast<ProcessorParamsGnssFix>(_params),
std::static_pointer_cast<SensorGnss>(sensor_ptr));
prc_ptr->setName(_unique_name);
return prc_ptr;
auto prc = std::make_shared<ProcessorGnssFix>(std::static_pointer_cast<ProcessorParamsGnssFix>(_params),
std::static_pointer_cast<SensorGnss>(sensor));
prc->setName(_unique_name);
return prc;
}
......
......@@ -6,9 +6,9 @@
namespace wolf
{
ProcessorGnssSingleDiff::ProcessorGnssSingleDiff(ProcessorParamsGnssSingleDiffPtr _params_gnss, SensorGnssPtr _sensor_gnss_ptr) :
ProcessorGnssSingleDiff::ProcessorGnssSingleDiff(ProcessorParamsGnssSingleDiffPtr _params_gnss, SensorGnssPtr _sensor_gnss) :
ProcessorBase("GNSS SINGLE DIFFERENCES", _params_gnss),
sensor_gnss_ptr_(_sensor_gnss_ptr),
sensor_gnss_(_sensor_gnss),
params_gnss_(_params_gnss)
{
//
......@@ -19,45 +19,48 @@ ProcessorGnssSingleDiff::~ProcessorGnssSingleDiff()
//
}
void ProcessorGnssSingleDiff::processCapture(CaptureBasePtr _capture_ptr)
void ProcessorGnssSingleDiff::processCapture(CaptureBasePtr _capture)
{
// TODO: keep captures in a buffer and deal with KFpacks
//WOLF_DEBUG("ProcessorGnssSingleDiff::process()");
incoming_capture_ptr_ = std::static_pointer_cast<CaptureGnssSingleDiff>(_capture_ptr);
incoming_capture_ = std::static_pointer_cast<CaptureGnssSingleDiff>(_capture);
// discard capture with null or non-key origin frame
if (incoming_capture_ptr_->getOriginFrame() == nullptr || !incoming_capture_ptr_->getOriginFrame()->isKey())
if (incoming_capture_->getOriginFrame() == nullptr || !incoming_capture_->getOriginFrame()->isKey())
return;
FrameBasePtr new_frame_ptr = nullptr;
FrameBasePtr new_frame = nullptr;
// ALREADY CREATED KF
PackKeyFramePtr KF_pack = buffer_pack_kf_.selectPack( _capture_ptr, params_->time_tolerance);
if (KF_pack && KF_pack->key_frame != incoming_capture_ptr_->getOriginFrame())
PackKeyFramePtr KF_pack = buffer_pack_kf_.selectPack( _capture, params_->time_tolerance);
if (KF_pack && KF_pack->key_frame != incoming_capture_->getOriginFrame())
{
new_frame_ptr = KF_pack->key_frame;
WOLF_DEBUG( "PR ",getName()," - capture ", _capture_ptr->id(), " appended to existing KF " , KF_pack->key_frame->id() , " TS: ", KF_pack->key_frame->getTimeStamp());
new_frame = KF_pack->key_frame;
WOLF_DEBUG( "PR ",getName()," - capture ", _capture->id(), " appended to existing KF " , KF_pack->key_frame->id() , " TS: ", KF_pack->key_frame->getTimeStamp());
}
// MAKE KF
else if (voteForKeyFrame() && permittedKeyFrame())
{
new_frame_ptr = getProblem()->emplaceFrame(KEY, _capture_ptr->getTimeStamp());
getProblem()->keyFrameCallback(new_frame_ptr, shared_from_this(), params_->time_tolerance);
WOLF_DEBUG( "PR ",getName()," - capture ", _capture_ptr->id(), " appended to new KF " , new_frame_ptr->id() , " TS: ", new_frame_ptr->getTimeStamp());
new_frame = getProblem()->emplaceFrame(KEY, _capture->getTimeStamp());
getProblem()->keyFrameCallback(new_frame, shared_from_this(), params_->time_tolerance);
WOLF_DEBUG( "PR ",getName()," - capture ", _capture->id(), " appended to new KF " , new_frame->id() , " TS: ", new_frame->getTimeStamp());
}
// ESTABLISH FACTOR
if (new_frame_ptr)
if (new_frame)
{
// LINK CAPTURE
_capture_ptr->link(new_frame_ptr); // Add incoming Capture to the new Frame
_capture->link(new_frame); // Add incoming Capture to the new Frame
// EMPLACE FEATURE
auto ftr_ptr = FeatureBase::emplace<FeatureGnssSingleDiff>(incoming_capture_ptr_, incoming_capture_ptr_->getData(),incoming_capture_ptr_->getDataCovariance());
auto ftr = FeatureBase::emplace<FeatureGnssSingleDiff>(incoming_capture_, incoming_capture_->getData(),incoming_capture_->getDataCovariance());
// ADD CONSTRAINT
emplaceFactor(ftr_ptr);
emplaceFactor(ftr);
// store last KF
last_KF_ = new_frame;
}
// INITIALIZE ENU_MAP IF 4 NECESSARY CONDITIONS:
......@@ -65,29 +68,26 @@ void ProcessorGnssSingleDiff::processCapture(CaptureBasePtr _capture_ptr)
// 2 - not initialized
// 3 - current capture in key-frame with factor
// 4 - frames constained by the factor separated enough ( > enu_map_init_dist_min)
if ( sensor_gnss_ptr_->isEnuDefined() &&
!sensor_gnss_ptr_->isEnuMapInitialized() &&
new_frame_ptr != nullptr &&
incoming_capture_ptr_->getFrame() != nullptr && incoming_capture_ptr_->getFrame()->isKey() &&
incoming_capture_ptr_->getData().norm() > params_gnss_->enu_map_init_dist_min)
if ( sensor_gnss_->isEnuDefined() &&
!sensor_gnss_->isEnuMapInitialized() &&
new_frame != nullptr &&
incoming_capture_->getFrame() != nullptr && incoming_capture_->getFrame()->isKey() &&
incoming_capture_->getData().norm() > params_gnss_->enu_map_init_dist_min)
{
WOLF_DEBUG("initializing enu map");
sensor_gnss_ptr_->initializeEnuMapYaw(incoming_capture_ptr_->getOriginFrame()->getState(),
incoming_capture_ptr_->getFrame()->getState(),
incoming_capture_ptr_->getData());
sensor_gnss_->initializeEnuMapYaw(incoming_capture_->getOriginFrame()->getState(),
incoming_capture_->getFrame()->getState(),
incoming_capture_->getData());
}
last_capture_ptr_ = incoming_capture_ptr_;
incoming_capture_ptr_ = nullptr;
}
FactorBasePtr ProcessorGnssSingleDiff::emplaceFactor(FeatureBasePtr& ftr_ptr)
FactorBasePtr ProcessorGnssSingleDiff::emplaceFactor(FeatureBasePtr& ftr)
{
// CREATE CONSTRAINT --------------------
//WOLF_DEBUG("creating the factor...");
// 2D
if (getProblem()->getDim() == 2)
return FactorBase::emplace<FactorGnssSingleDiff2D>(ftr_ptr, ftr_ptr, incoming_capture_ptr_->getOriginFrame(), sensor_gnss_ptr_, shared_from_this());
return FactorBase::emplace<FactorGnssSingleDiff2D>(ftr, ftr, incoming_capture_->getOriginFrame(), sensor_gnss_, shared_from_this());
// 3D TODO
else
std::runtime_error("Single Differences in 3D not implemented yet.");
......@@ -98,7 +98,7 @@ FactorBasePtr ProcessorGnssSingleDiff::emplaceFactor(FeatureBasePtr& ftr_ptr)
bool ProcessorGnssSingleDiff::voteForKeyFrame()
{
// Depending on time since the last KF with gnssfix capture
if (last_capture_ptr_==nullptr || (last_capture_ptr_->getTimeStamp() - incoming_capture_ptr_->getTimeStamp()) > params_gnss_->time_th)
if (last_KF_==nullptr || (last_KF_->getTimeStamp() - incoming_capture_->getTimeStamp()) > params_gnss_->time_th)
return true;
// TODO: more alternatives?
......@@ -109,14 +109,14 @@ bool ProcessorGnssSingleDiff::voteForKeyFrame()
void ProcessorGnssSingleDiff::configure(SensorBasePtr _sensor)
{
sensor_gnss_ptr_ = std::static_pointer_cast<SensorGnss>(_sensor);
sensor_gnss_ = std::static_pointer_cast<SensorGnss>(_sensor);
};
ProcessorBasePtr ProcessorGnssSingleDiff::create(const std::string& _unique_name, const ProcessorParamsBasePtr _params, const SensorBasePtr sensor_ptr)
ProcessorBasePtr ProcessorGnssSingleDiff::create(const std::string& _unique_name, const ProcessorParamsBasePtr _params, const SensorBasePtr sensor)
{
ProcessorGnssSingleDiffPtr prc_ptr = std::make_shared<ProcessorGnssSingleDiff>(std::static_pointer_cast<ProcessorParamsGnssSingleDiff>(_params), std::static_pointer_cast<SensorGnss>(sensor_ptr));
prc_ptr->setName(_unique_name);
return prc_ptr;
ProcessorGnssSingleDiffPtr prc = std::make_shared<ProcessorGnssSingleDiff>(std::static_pointer_cast<ProcessorParamsGnssSingleDiff>(_params), std::static_pointer_cast<SensorGnss>(sensor));
prc->setName(_unique_name);
return prc;
}
} // namespace wolf
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment