[{"id":17486,"web_url":"https://patchwork.libcamera.org/comment/17486/","msgid":"<YL/ZshZB17FUU2lm@pendragon.ideasonboard.com>","date":"2021-06-08T20:57:22","subject":"Re: [libcamera-devel] [PATCH v6 1/4] libcamera: utils: Add helper\n\tclass for std::chrono::duration","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"Hi Naush,\n\nThank you for the patch.\n\nOn Tue, Jun 08, 2021 at 12:03:32PM +0100, Naushir Patuck wrote:\n> A new utils::Duration class is defined to represent a std::chrono::duration type\n> with double precision nanosecond timebase. Using a double minimises the loss of\n> precision when converting timebases. This helper class may be used by IPAs to\n> represent variables such as frame durations and exposure times.\n> \n> An operator << overload is defined to help with displaying utils::Duration value\n> in stream objects. Currently, this will display the duration value in\n> microseconds.\n> \n> Signed-off-by: Naushir Patuck <naush@raspberrypi.com>\n> Reviewed-by: David Plowman <david.plowman@raspberrypi.com>\n> Reviewed-by: Jacopo Mondi <jacopo@jmondi.org>\n> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> ---\n>  include/libcamera/internal/utils.h | 32 +++++++++++++++++\n>  src/libcamera/utils.cpp            | 58 ++++++++++++++++++++++++++++++\n>  test/utils.cpp                     | 45 +++++++++++++++++++++++\n>  3 files changed, 135 insertions(+)\n> \n> diff --git a/include/libcamera/internal/utils.h b/include/libcamera/internal/utils.h\n> index 83dada7cc16c..15beb0f44172 100644\n> --- a/include/libcamera/internal/utils.h\n> +++ b/include/libcamera/internal/utils.h\n> @@ -316,8 +316,40 @@ auto enumerate(T (&iterable)[N]) -> details::enumerate_adapter<T *>\n>  }\n>  #endif\n>  \n> +class Duration : public std::chrono::duration<double, std::nano>\n> +{\n> +\tusing BaseDuration = std::chrono::duration<double, std::nano>;\n> +\n> +public:\n> +\tDuration() = default;\n> +\n> +\ttemplate<typename Rep, typename Period>\n> +\tconstexpr Duration(const std::chrono::duration<Rep, Period> &d)\n> +\t\t: BaseDuration(d)\n> +\t{\n> +\t}\n> +\n> +\ttemplate<typename Period>\n> +\tdouble get() const\n> +\t{\n> +\t\tauto const c = std::chrono::duration_cast<std::chrono::duration<double, Period>>(*this);\n> +\t\treturn c.count();\n> +\t}\n> +\n> +\texplicit constexpr operator bool() const\n> +\t{\n> +\t\treturn *this != BaseDuration::zero();\n> +\t}\n> +};\n> +\n>  } /* namespace utils */\n>  \n> +#ifndef __DOXYGEN__\n> +template<class CharT, class Traits>\n> +std::basic_ostream<CharT, Traits> &operator<<(std::basic_ostream<CharT, Traits> &os,\n> +\t\t\t\t\t      const utils::Duration &d);\n> +#endif\n> +\n>  } /* namespace libcamera */\n>  \n>  #endif /* __LIBCAMERA_INTERNAL_UTILS_H__ */\n> diff --git a/src/libcamera/utils.cpp b/src/libcamera/utils.cpp\n> index 826144d3c837..2e7d35fb1173 100644\n> --- a/src/libcamera/utils.cpp\n> +++ b/src/libcamera/utils.cpp\n> @@ -506,6 +506,64 @@ std::string libcameraSourcePath()\n>   * loop, iterates over an indexed view of the \\a iterable\n>   */\n>  \n> +/**\n> + * \\class Duration\n> + * \\brief Helper class from std::chrono::duration that represents a time\n> + * duration in nanoseconds with double precision\n> + */\n> +\n> +/**\n> + * \\fn Duration::Duration(const std::chrono::duration<Rep, Period> &d)\n> + * \\brief Construct a Duration by converting an arbitrary std::chrono::duration\n> + * \\param[in] d The std::chrono::duration object to convert from\n> + *\n> + * The constructed \\a Duration object is internally represented in double\n> + * precision with nanoseconds ticks.\n> + */\n> +\n> +/**\n> + * \\fn Duration::get<Period>()\n> + * \\brief Retrieve the tick count, converted to the timebase provided by the\n> + * template argument Period of type \\a std::ratio\n> + *\n> + * A typical usage example is given below:\n> + *\n> + * \\code{.cpp}\n> + * utils::Duration d = 5s;\n> + * double d_in_ms = d.get<std::milli>();\n> + * \\endcode\n> + *\n> + * \\return The tick count of the Duration expressed in \\a Period\n> + */\n> +\n> +/**\n> + * \\fn Duration::operator bool()\n> + * \\brief Boolean operator to test if a \\a Duration holds a non-zero time value\n> + *\n> + * \\return True if \\a Duration is a non-zero time value, False otherwise\n> + */\n> +\n>  } /* namespace utils */\n>  \n> +#ifndef __DOXYGEN__\n> +template<class CharT, class Traits>\n> +std::basic_ostream<CharT, Traits> &operator<<(std::basic_ostream<CharT, Traits> &os,\n> +\t\t\t\t\t      const utils::Duration &d)\n> +{\n> +\tstd::basic_ostringstream<CharT, Traits> s;\n> +\n> +\ts.flags(os.flags());\n> +\ts.imbue(os.getloc());\n> +\ts.setf(std::ios_base::fixed, std::ios_base::floatfield);\n> +\ts.precision(2);\n> +\ts << d.get<std::micro>() << \"us\";\n> +\treturn os << s.str();\n> +}\n> +\n> +template\n> +std::basic_ostream<char, std::char_traits<char>> &\n> +operator<< <char, std::char_traits<char>>(std::basic_ostream<char, std::char_traits<char>> &os,\n> +\t\t\t\t\t  const utils::Duration &d);\n> +#endif\n> +\n>  } /* namespace libcamera */\n> diff --git a/test/utils.cpp b/test/utils.cpp\n> index 7e24c71e4775..f170ae4c2f35 100644\n> --- a/test/utils.cpp\n> +++ b/test/utils.cpp\n> @@ -20,6 +20,7 @@\n>  \n>  using namespace std;\n>  using namespace libcamera;\n> +using namespace std::literals::chrono_literals;\n>  \n>  class UtilsTest : public Test\n>  {\n> @@ -128,6 +129,46 @@ protected:\n>  \t\treturn TestPass;\n>  \t}\n>  \n> +\tint testDuration()\n> +\t{\n> +\t\tstd::ostringstream os;\n> +\t\tutils::Duration exposure;\n> +\t\tdouble ratio;\n> +\n> +\t\texposure = 25ms + 25ms;\n> +\t\tif (exposure.get<std::micro>() != 50000.0) {\n> +\t\t\tcerr << \"utils::Duration failed to return microsecond count\";\n> +\t\t\treturn TestFail;\n> +\t\t}\n> +\n> +\t\texposure = 1.0s / 4;\n> +\t\tif (exposure != 250ms) {\n> +\t\t\tcerr << \"utils::Duration failed scalar divide test\";\n> +\t\t\treturn TestFail;\n> +\t\t}\n> +\n> +\t\texposure = 5000.5us;\n> +\t\tif (!exposure) {\n> +\t\t\tcerr << \"utils::Duration failed boolean test\";\n> +\t\t\treturn TestFail;\n> +\t\t}\n> +\n> +\t\tos << exposure;\n> +\t\tif (os.str() != \"5000.50us\") {\n> +\t\t\tcerr << \"utils::Duration operator << failed\";\n\nI'll s/operator <</operator<</ and push the series after the tests\ncomplete.\n\n> +\t\t\treturn TestFail;\n> +\t\t}\n> +\n> +\t\texposure = 100ms;\n> +\t\tratio = exposure / 25ms;\n> +\t\tif (ratio != 4.0) {\n> +\t\t\tcerr << \"utils::Duration failed ratio test\";\n> +\t\t\treturn TestFail;\n> +\t\t}\n> +\n> +\t\treturn TestPass;\n> +\t}\n> +\n>  \tint run()\n>  \t{\n>  \t\t/* utils::hex() test. */\n> @@ -236,6 +277,10 @@ protected:\n>  \t\tif (testEnumerate() != TestPass)\n>  \t\t\treturn TestFail;\n>  \n> +\t\t/* utils::Duration test. */\n> +\t\tif (testDuration() != TestPass)\n> +\t\t\treturn TestFail;\n> +\n>  \t\treturn TestPass;\n>  \t}\n>  };","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id AADE2BD22E\n\tfor <parsemail@patchwork.libcamera.org>;\n\tTue,  8 Jun 2021 20:57:39 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 2F5D66892C;\n\tTue,  8 Jun 2021 22:57:39 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id D52AC602A7\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue,  8 Jun 2021 22:57:37 +0200 (CEST)","from pendragon.ideasonboard.com (62-78-145-57.bb.dnainternet.fi\n\t[62.78.145.57])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 5625B3E6;\n\tTue,  8 Jun 2021 22:57:37 +0200 (CEST)"],"Authentication-Results":"lancelot.ideasonboard.com;\n\tdkim=fail reason=\"signature verification failed\" (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"GQheJFYd\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1623185857;\n\tbh=5NF6xFNzIjPJcBjibLrsE4/QHDp+z1i6aWJa2T7K6pA=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=GQheJFYdUo/M2lbCffN90Ecx8gDa8QQdxlhZECpfuQuIuUgADyTU+D0fl4PobS3D6\n\tIlyHfh8yxpAm3RmRZTmgjIbHJ6c/0aKfhbi0pWq7fR1Y9csmSjq/f3+hNbVO0jzi2Q\n\t6WUhv75llZBD+CkU6kEsCGBGPNAA1+Pb1GWfgMk8=","Date":"Tue, 8 Jun 2021 23:57:22 +0300","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"Naushir Patuck <naush@raspberrypi.com>","Message-ID":"<YL/ZshZB17FUU2lm@pendragon.ideasonboard.com>","References":"<20210608110335.4078551-1-naush@raspberrypi.com>\n\t<20210608110335.4078551-2-naush@raspberrypi.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","In-Reply-To":"<20210608110335.4078551-2-naush@raspberrypi.com>","Subject":"Re: [libcamera-devel] [PATCH v6 1/4] libcamera: utils: Add helper\n\tclass for std::chrono::duration","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Cc":"libcamera-devel@lists.libcamera.org","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}}]