Skip to content

File service.cpp

File List > pose_lookup > service.cpp

Go to the documentation of this file

#include "service.hpp"

#include "illixr/iterators/csv_iterator.hpp"
#include "utils.hpp"

#include 
#include 

using namespace ILLIXR;
using namespace ILLIXR::data_format;

inline std::map<ullong, pose::head_pose_type> read_data(std::ifstream& gt_file, const std::string& file_name) {
    (void) file_name;
    std::map<ullong, pose::head_pose_type> data;

    for (csv_iterator row{gt_file, 1}; row != csv_iterator{}; ++row) {
        ullong             t = std::stoull(row[0]);
        Eigen::Vector3f    av{std::stof(row[1]), std::stof(row[2]), std::stof(row[3])};
        Eigen::Quaternionf la{std::stof(row[4]), std::stof(row[5]), std::stof(row[6]), std::stof(row[7])};
        data[t] = {{}, av, la};
    }
    return data;
}

pose_lookup_impl::pose_lookup_impl(const phonebook* const pb)
    : switchboard_{pb->lookup_impl<switchboard>()}
    , clock_{pb->lookup_impl<relative_clock>()}
    , sensor_data_{load_data<pose::head_pose_type>("state_groundtruth_estimate0", "pose_lookup", &read_data, switchboard_)}
    , sensor_data_it_{sensor_data_.cbegin()}
    , dataset_first_time_{sensor_data_it_->first}
    , vsync_estimate_{switchboard_->get_reader<switchboard::event_wrapper<time_point>>("vsync_estimate")}
    , enable_alignment_{switchboard_->get_env_bool("ILLIXR_ALIGNMENT_ENABLE", "False")}
    , init_pos_offset_{Eigen::Vector3f::Zero()}
    , align_rot_{Eigen::Matrix3f::Zero()}
    , align_trans_{Eigen::Vector3f::Zero()}
    , align_quat_{Eigen::Vector4f::Zero()}
    , align_scale_{0.0} {
    if (enable_alignment_) {
        std::string path_to_alignment(switchboard_->get_env("ILLIXR_ALIGNMENT_FILE", "./metrics/alignMatrix.txt"));
        load_align_parameters(path_to_alignment, align_rot_, align_trans_, align_quat_, align_scale_);
    }
    // Read position data of the first frame
    init_pos_offset_ = sensor_data_.cbegin()->second.position;

    auto newoffset = correct_pose(sensor_data_.begin()->second).orientation;
    set_offset(newoffset);
}

pose::fast_head_pose_type pose_lookup_impl::get_fast_pose() const {
    const switchboard::ptr<const switchboard::event_wrapper<time_point>> estimated_vsync = vsync_estimate_.get_ro_nullable();
    if (estimated_vsync == nullptr) {
        spdlog::get("illixr")->trace("[pose_lookup] Vsync estimation not valid yet, returning fast_pose for now()");
        return get_fast_pose(clock_->now());
    } else {
        return get_fast_pose(**estimated_vsync);
    }
}

pose::head_pose_type pose_lookup_impl::get_true_pose() const {
    throw std::logic_error{"Not Implemented"};
}

bool pose_lookup_impl::fast_pose_reliable() const {
    return true;
}

bool pose_lookup_impl::true_pose_reliable() const {
    return false;
}

Eigen::Quaternionf pose_lookup_impl::get_offset() {
    return offset_;
}

pose::head_pose_type pose_lookup_impl::correct_pose(const pose::head_pose_type& pose) const {
    pose::head_pose_type swapped_pose;

    // Step 1: Compensate starting point to (0, 0, 0), pos only
    auto input_pose = pose::head_pose_type{pose.sensor_time,
                                           Eigen::Vector3f{
                                               pose.position(0) - init_pos_offset_(0),
                                               pose.position(1) - init_pos_offset_(1),
                                               pose.position(2) - init_pos_offset_(2),
                                           },
                                           pose.orientation};

    if (enable_alignment_) {
        // Step 2: Apply estimated alignment parameters
        // Step 2.1: Position alignment
        input_pose.position = align_scale_ * align_rot_ * input_pose.position + align_trans_;

        // Step 2.2: Orientation alignment
        Eigen::Vector4f quat_in    = {pose.orientation.x(), pose.orientation.y(), pose.orientation.z(), pose.orientation.w()};
        Eigen::Vector4f quat_out   = ori_multiply(quat_in, ori_inv(align_quat_));
        input_pose.orientation.x() = quat_out(0);
        input_pose.orientation.y() = quat_out(1);
        input_pose.orientation.z() = quat_out(2);
        input_pose.orientation.w() = quat_out(3);
    }

    // Step 3: Swap axis for both position and orientation
    // Step 3.1: Swap for position
    // This uses the OpenVINS standard output coordinate system.
    // This is a mapping between the OV coordinate system and the OpenGL system.
    swapped_pose.position.x() = -input_pose.position.y();
    swapped_pose.position.y() = input_pose.position.z();
    swapped_pose.position.z() = -input_pose.position.x();

    // Step 3.2: Swap for orientation
    // There is a slight issue with the orientations: basically,
    // the output orientation acts as though the "top of the head" is the
    // forward direction, and the "eye direction" is the up direction.
    Eigen::Quaternionf raw_o(input_pose.orientation.w(), -input_pose.orientation.y(), input_pose.orientation.z(),
                             -input_pose.orientation.x());

    swapped_pose.orientation = apply_offset(raw_o);

    return swapped_pose;
}

void pose_lookup_impl::set_offset(const Eigen::Quaternionf& raw_o_times_offset) {
    std::unique_lock   lock{offset_mutex_};
    Eigen::Quaternionf raw_o = raw_o_times_offset * offset_.inverse();
    // std::cout << "pose_prediction: set_offset" << std::endl;
    offset_ = raw_o.inverse();
}

Eigen::Quaternionf pose_lookup_impl::apply_offset(const Eigen::Quaternionf& orientation) const {
    std::shared_lock lock{offset_mutex_};
    return orientation * offset_;
}

pose::fast_head_pose_type pose_lookup_impl::get_fast_pose(time_point time) const {
    ullong lookup_time = time.time_since_epoch().count() + dataset_first_time_;

    auto nearest_row = sensor_data_.upper_bound(lookup_time);

    if (nearest_row == sensor_data_.cend()) {
#ifndef NDEBUG
        spdlog::get("illixr")->debug("[pose_lookup] Time {} ({} + {}) after last datum {}", lookup_time,
                                     std::chrono::nanoseconds(time.time_since_epoch()).count(), dataset_first_time_,
                                     sensor_data_.rbegin()->first);
#endif
        nearest_row--;
    } else if (nearest_row == sensor_data_.cbegin()) {
#ifndef NDEBUG
        spdlog::get("illixr")->debug("[pose_lookup] Time {} ({} + {}) before first datum {}", lookup_time,
                                     std::chrono::nanoseconds(time.time_since_epoch()).count(), dataset_first_time_,
                                     sensor_data_.cbegin()->first);
#endif
    } else {
        // "std::map::upper_bound" returns an iterator to the first pair whose key is GREATER than the argument.
        // I already know we aren't at the begin()
        // So I will decrement nearest_row here.
        nearest_row--;
    }

    auto looked_up_pose        = nearest_row->second;
    looked_up_pose.sensor_time = time_point{std::chrono::nanoseconds{nearest_row->first - dataset_first_time_}};
    return pose::fast_head_pose_type{correct_pose(looked_up_pose), clock_->now(), time};
}

class pose_lookup_plugin : public plugin {
public:
    [[maybe_unused]] pose_lookup_plugin(const std::string& name, phonebook* pb)
        : plugin{name, pb} {
        pb->register_impl<pose_prediction>(std::static_pointer_cast<pose_prediction>(std::make_shared<pose_lookup_impl>(pb)));
    }
};

PLUGIN_MAIN(pose_lookup_plugin)