Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added functions to convert between time_point and secNsec #150

Merged
merged 18 commits into from
Sep 3, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 94 additions & 4 deletions include/ignition/math/Helpers.hh
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,18 @@
#ifndef IGNITION_MATH_FUNCTIONS_HH_
#define IGNITION_MATH_FUNCTIONS_HH_

#include <algorithm>
#include <chrono>
#include <cmath>
#include <algorithm>
#include <cstdint>
#include <iomanip>
#include <iostream>
#include <limits>
#include <sstream>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While we're in here, can we alphabetize includes?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done :) f9ef912

#include <string>
#include <iostream>
#include <vector>
#include <tuple>
#include <utility>
#include <cstdint>
#include <vector>

#include <ignition/math/config.hh>
#include "ignition/math/Export.hh"
Expand Down Expand Up @@ -727,8 +729,46 @@ namespace ignition
}
}

/// \brief Convert a std::chrono::steady_clock::time_point to a seconds and
ahcorde marked this conversation as resolved.
Show resolved Hide resolved
/// nanoseconds pair.
// and on macOS, microsecond precision.
/// \param[in] _time The time point to convert.
/// \return A pair where the first element is the number of seconds and
/// the second is the number of nanoseconds.
inline std::pair<int64_t, int64_t> timePointToSecNsec(
const std::chrono::steady_clock::time_point &_time)
{
auto now_ns = std::chrono::duration_cast<std::chrono::nanoseconds>(
_time.time_since_epoch());
auto now_s = std::chrono::duration_cast<std::chrono::seconds>(
_time.time_since_epoch());
int64_t seconds = now_s.count();
int64_t nanoseconds = std::chrono::duration_cast
<std::chrono::nanoseconds>(now_ns - now_s).count();
return {seconds, nanoseconds};
}

/// \brief Convert seconds and nanoseconds to
/// std::chrono::steady_clock::time_point.
ahcorde marked this conversation as resolved.
Show resolved Hide resolved
// and on macOS, microsecond precision.
/// \param[in] _sec The seconds to convert.
/// \param[in] _nanosec The nanoseconds to convert.
/// \return A std::chrono::steady_clock::time_point based on the number of
/// seconds and the number of nanoseconds.
inline std::chrono::steady_clock::time_point secNsecToTimePoint(
const uint64_t &_sec, const uint64_t &_nanosec)
{
auto duration = std::chrono::seconds(_sec) + std::chrono::nanoseconds(
_nanosec);
std::chrono::steady_clock::time_point result;
using std::chrono::duration_cast;
result += duration_cast<std::chrono::steady_clock::duration>(duration);
return result;
}

/// \brief Convert a std::chrono::steady_clock::duration to a seconds and
/// nanoseconds pair.
// and on macOS, microsecond precision.
/// \param[in] _dur The duration to convert.
/// \return A pair where the first element is the number of seconds and
/// the second is the number of nanoseconds.
Expand All @@ -740,6 +780,56 @@ namespace ignition
return {s.count(), ns.count()};
}

// TODO(anyone): Replace this with std::chrono::days.
/// This will exist in C++-20
typedef std::chrono::duration<uint64_t, std::ratio<86400>> days;

/// \brief break down durations
/// NOTE: the template arguments must be properly ordered according
/// to magnitude and there can be no duplicates.
/// This function uses the braces initializer to split all the templated
/// duration. The initializer will be called recursievely due the `...`
/// \param[in] d Duration to break down
/// \return A tuple based on the durations specified
template<class...Durations, class DurationIn>
std::tuple<Durations...> breakDownDurations(DurationIn d) {
std::tuple<Durations...> retval;
using discard = int[];
(void)discard{0, (void((
(std::get<Durations>(retval) =
std::chrono::duration_cast<Durations>(d)),
(d -= std::chrono::duration_cast<DurationIn>(
std::get<Durations>(retval))))), 0)...};
return retval;
}

/// \brief Convert a std::chrono::steady_clock::time_point to a string
/// \param[in] _point The std::chrono::steady_clock::time_point to convert.
/// \return A string formatted with the time_point
inline std::string timePointToString(
const std::chrono::steady_clock::time_point &_point)
{
auto duration = _point - secNsecToTimePoint(0, 0);
auto cleanDuration = breakDownDurations<days,
std::chrono::hours,
std::chrono::minutes,
std::chrono::seconds,
std::chrono::milliseconds>(
duration);
std::ostringstream output_string;
output_string << std::setw(2) << std::setfill('0')
<< std::get<0>(cleanDuration).count() << " "
<< std::setw(2) << std::setfill('0')
<< std::get<1>(cleanDuration).count() << ":"
<< std::setw(2) << std::setfill('0')
<< std::get<2>(cleanDuration).count() << ":"
<< std::setfill('0') << std::setw(6)
<< std::fixed << std::setprecision(3)
<< std::get<3>(cleanDuration).count() +
std::get<4>(cleanDuration).count()/1000.0;
return output_string.str();
}

// Degrade precision on Windows, which cannot handle 'long double'
// values properly. See the implementation of Unpair.
// 32 bit ARM processors also define 'long double' to be the same
Expand Down
68 changes: 68 additions & 0 deletions src/Helpers_TEST.cc
Original file line number Diff line number Diff line change
Expand Up @@ -518,6 +518,74 @@ TEST(HelpersTest, Pair)
}
}

/////////////////////////////////////////////////
TEST(HelpersTest, timePointToSecNsec)
{
std::pair<int64_t, int64_t> parts = math::timePointToSecNsec(
math::secNsecToTimePoint(0, 0));
EXPECT_EQ(parts.first, 0);
EXPECT_EQ(parts.second, 0);

std::chrono::steady_clock::time_point point;
point += std::chrono::nanoseconds(1000);
parts = math::timePointToSecNsec(point);

EXPECT_EQ(parts.first, 0);
EXPECT_EQ(parts.second, 1000);

point = math::secNsecToTimePoint(0, 0);
point += std::chrono::seconds(60);
point += std::chrono::nanoseconds(57989);
parts = math::timePointToSecNsec(point);

EXPECT_EQ(parts.first, 60);
EXPECT_EQ(parts.second, 57989);
}

/////////////////////////////////////////////////
TEST(HelpersTest, secNsecToTimePoint)
{
using std::chrono::duration_cast;
using std::chrono::nanoseconds;
using std::chrono::steady_clock;

std::chrono::steady_clock::time_point point =
math::secNsecToTimePoint(0, 0);
point += std::chrono::hours(24);

std::chrono::steady_clock::time_point s =
math::secNsecToTimePoint(24*60*60, 0);
EXPECT_EQ(s, point);

point = math::secNsecToTimePoint(0, 0);
point += std::chrono::nanoseconds(1000);
s = math::secNsecToTimePoint(0, 1000);
EXPECT_EQ(s, point);
}

/////////////////////////////////////////////////
TEST(HelpersTest, timePointToString)
{
std::chrono::steady_clock::time_point time_clock =
math::secNsecToTimePoint(0, 0);
std::string s = math::timePointToString(time_clock);

EXPECT_STREQ(s.c_str(), std::string("00 00:00:00.000").c_str());

std::chrono::steady_clock::time_point point;
point += std::chrono::hours(24);

s = math::timePointToString(point);
EXPECT_STREQ(s.c_str(), std::string("01 00:00:00.000").c_str());

point = math::secNsecToTimePoint(0, 0);
point += std::chrono::minutes(1);
point += std::chrono::seconds(23);
point += std::chrono::milliseconds(125);
s = math::timePointToString(point);
EXPECT_STREQ(s.c_str(), std::string("00 00:01:23.125").c_str());
}

/////////////////////////////////////////////////
TEST(HelpersTest, durationToSecNsec)
{
Expand Down