[libcamera-devel,v3,1/4] libcamera: utils: Add helper class for std::chrono::duration
diff mbox series

Message ID 20210521080530.37591-2-naush@raspberrypi.com
State Superseded
Headers show
Series
  • Switch RaspberryPi IPA to use std::chrono::duration
Related show

Commit Message

Naushir Patuck May 21, 2021, 8:05 a.m. UTC
A new utils::Duration class is defined to represent a
std::chrono::duration type with double precision nanosecond timebase.
Using a double minimises the loss of precision when converting timebases.
This helper class may be used by IPAs to represent variables such as frame
durations and exposure times.

An operator << overload is define to help with displaying
utils::Duration value in stream objects. Currently, this will display
the duration value in microseconds.

Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
---
 include/libcamera/internal/utils.h | 42 ++++++++++++++++++++++++++++++
 src/libcamera/utils.cpp            | 36 +++++++++++++++++++++++++
 2 files changed, 78 insertions(+)

Comments

David Plowman May 21, 2021, 10:20 a.m. UTC | #1
Hi Naush

Thanks for the updated patch!

On Fri, 21 May 2021 at 09:05, Naushir Patuck <naush@raspberrypi.com> wrote:
>
> A new utils::Duration class is defined to represent a
> std::chrono::duration type with double precision nanosecond timebase.
> Using a double minimises the loss of precision when converting timebases.
> This helper class may be used by IPAs to represent variables such as frame
> durations and exposure times.
>
> An operator << overload is define to help with displaying
> utils::Duration value in stream objects. Currently, this will display
> the duration value in microseconds.
>
> Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
> ---
>  include/libcamera/internal/utils.h | 42 ++++++++++++++++++++++++++++++
>  src/libcamera/utils.cpp            | 36 +++++++++++++++++++++++++
>  2 files changed, 78 insertions(+)
>
> diff --git a/include/libcamera/internal/utils.h b/include/libcamera/internal/utils.h
> index 83dada7cc16c..a377d4ea07ab 100644
> --- a/include/libcamera/internal/utils.h
> +++ b/include/libcamera/internal/utils.h
> @@ -316,6 +316,48 @@ auto enumerate(T (&iterable)[N]) -> details::enumerate_adapter<T *>
>  }
>  #endif
>
> +using BaseDuration = std::chrono::duration<double, std::nano>;
> +
> +class Duration : public BaseDuration
> +{
> +public:
> +       Duration() = default;
> +
> +       template<typename Rep, typename Period>
> +       constexpr Duration(const std::chrono::duration<Rep, Period> &d)
> +               : BaseDuration(d)
> +       {
> +       }
> +
> +       template<typename Period>
> +       double get() const
> +       {
> +               auto const c = std::chrono::duration_cast<std::chrono::duration<double, Period>>(*this);
> +               return c.count();
> +       }
> +
> +       explicit constexpr operator bool() const
> +       {
> +               return *this != BaseDuration::zero();
> +       }
> +};
> +
> +#ifndef __DOXYGEN__
> +template<class CharT, class Traits>
> +static inline std::basic_ostream<CharT, Traits> &operator<<(std::basic_ostream<CharT, Traits> &os,
> +                                                           const Duration &d)
> +{
> +       std::basic_ostringstream<CharT, Traits> s;
> +
> +       s.flags(os.flags());
> +       s.imbue(os.getloc());
> +       s.setf(std::ios_base::fixed, std::ios_base::floatfield);
> +       s.precision(2);
> +       s << d.get<std::micro>() << "us";
> +       return os << s.str();
> +}
> +#endif

One small thing I wonder about is that many classes have a toString
method. Do we want one here? It could even take an optional argument
to allow you to format the output differently... but I think that's
overkill and best left for now!

> +
>  } /* namespace utils */
>
>  } /* namespace libcamera */
> diff --git a/src/libcamera/utils.cpp b/src/libcamera/utils.cpp
> index 826144d3c837..3131aa2d6666 100644
> --- a/src/libcamera/utils.cpp
> +++ b/src/libcamera/utils.cpp
> @@ -506,6 +506,42 @@ std::string libcameraSourcePath()
>   * loop, iterates over an indexed view of the \a iterable
>   */
>
> +/**
> + * \class Duration
> + * \brief Helper for std::chrono::duration types. Derived from \a BaseDuration
> + */
> +
> +/**
> + * \fn Duration::Duration(const std::chrono::duration<Rep, Period> &d)
> + * \brief Copy constructor from a \a std::chrono::duration type.
> + * \param[in] d The std::chrono::duration object to convert from.
> + *
> + * Constructs a \a Duration object from a \a std::chrono::duration object,
> + * converting the representation to \a BaseDuration type.
> + */
> +
> +/**
> + * \fn Duration::get<Period>()
> + * \brief Retrieve the tick count, coverted to the timebase provided by the

s/coverted/converted/

With this tiny fix:

Reviewed-by: David Plowman <david.plowman@raspberrypi.com>

Thanks!
David

> + * template argument Period of type \a std::ratio.
> + *
> + * A typical usage example is given below:
> + *
> + * \code{.cpp}
> + * utils::Duration d = 5s;
> + * double d_in_ms = d.get<std::milli>();
> + * \endcode
> + *
> + * \return Tick count
> + */
> +
> +/**
> + * \fn Duration::operator bool()
> + * \brief Boolean operator to test if a \a Duration holds a non-zero time value.
> + *
> + * \return True if \a Duration is a non-zero time value, False otherwise.
> + */
> +
>  } /* namespace utils */
>
>  } /* namespace libcamera */
> --
> 2.25.1
>
Naushir Patuck May 21, 2021, 10:53 a.m. UTC | #2
Hi David,

Thank you for your feedback.

On Fri, 21 May 2021 at 11:20, David Plowman <david.plowman@raspberrypi.com>
wrote:

> Hi Naush
>
> Thanks for the updated patch!
>
> On Fri, 21 May 2021 at 09:05, Naushir Patuck <naush@raspberrypi.com>
> wrote:
> >
> > A new utils::Duration class is defined to represent a
> > std::chrono::duration type with double precision nanosecond timebase.
> > Using a double minimises the loss of precision when converting timebases.
> > This helper class may be used by IPAs to represent variables such as
> frame
> > durations and exposure times.
> >
> > An operator << overload is define to help with displaying
> > utils::Duration value in stream objects. Currently, this will display
> > the duration value in microseconds.
> >
> > Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
> > ---
> >  include/libcamera/internal/utils.h | 42 ++++++++++++++++++++++++++++++
> >  src/libcamera/utils.cpp            | 36 +++++++++++++++++++++++++
> >  2 files changed, 78 insertions(+)
> >
> > diff --git a/include/libcamera/internal/utils.h
> b/include/libcamera/internal/utils.h
> > index 83dada7cc16c..a377d4ea07ab 100644
> > --- a/include/libcamera/internal/utils.h
> > +++ b/include/libcamera/internal/utils.h
> > @@ -316,6 +316,48 @@ auto enumerate(T (&iterable)[N]) ->
> details::enumerate_adapter<T *>
> >  }
> >  #endif
> >
> > +using BaseDuration = std::chrono::duration<double, std::nano>;
> > +
> > +class Duration : public BaseDuration
> > +{
> > +public:
> > +       Duration() = default;
> > +
> > +       template<typename Rep, typename Period>
> > +       constexpr Duration(const std::chrono::duration<Rep, Period> &d)
> > +               : BaseDuration(d)
> > +       {
> > +       }
> > +
> > +       template<typename Period>
> > +       double get() const
> > +       {
> > +               auto const c =
> std::chrono::duration_cast<std::chrono::duration<double, Period>>(*this);
> > +               return c.count();
> > +       }
> > +
> > +       explicit constexpr operator bool() const
> > +       {
> > +               return *this != BaseDuration::zero();
> > +       }
> > +};
> > +
> > +#ifndef __DOXYGEN__
> > +template<class CharT, class Traits>
> > +static inline std::basic_ostream<CharT, Traits>
> &operator<<(std::basic_ostream<CharT, Traits> &os,
> > +                                                           const
> Duration &d)
> > +{
> > +       std::basic_ostringstream<CharT, Traits> s;
> > +
> > +       s.flags(os.flags());
> > +       s.imbue(os.getloc());
> > +       s.setf(std::ios_base::fixed, std::ios_base::floatfield);
> > +       s.precision(2);
> > +       s << d.get<std::micro>() << "us";
> > +       return os << s.str();
> > +}
> > +#endif
>
> One small thing I wonder about is that many classes have a toString
> method. Do we want one here? It could even take an optional argument
> to allow you to format the output differently... but I think that's
> overkill and best left for now!
>

Did you mean that we should do something like:

LOG(IPA, Debug) << "Exposure time is " << exposure.toString();

over

LOG(IPA, Debug) << "Exposure time is " << exposure;

I chose the later way with the overload <<() operator as that is more
similar
to what C++20 will eventually allow with std::chrono::duration formatting.
What do others think?

Regards,
Naush



>
> > +
> >  } /* namespace utils */
> >
> >  } /* namespace libcamera */
> > diff --git a/src/libcamera/utils.cpp b/src/libcamera/utils.cpp
> > index 826144d3c837..3131aa2d6666 100644
> > --- a/src/libcamera/utils.cpp
> > +++ b/src/libcamera/utils.cpp
> > @@ -506,6 +506,42 @@ std::string libcameraSourcePath()
> >   * loop, iterates over an indexed view of the \a iterable
> >   */
> >
> > +/**
> > + * \class Duration
> > + * \brief Helper for std::chrono::duration types. Derived from \a
> BaseDuration
> > + */
> > +
> > +/**
> > + * \fn Duration::Duration(const std::chrono::duration<Rep, Period> &d)
> > + * \brief Copy constructor from a \a std::chrono::duration type.
> > + * \param[in] d The std::chrono::duration object to convert from.
> > + *
> > + * Constructs a \a Duration object from a \a std::chrono::duration
> object,
> > + * converting the representation to \a BaseDuration type.
> > + */
> > +
> > +/**
> > + * \fn Duration::get<Period>()
> > + * \brief Retrieve the tick count, coverted to the timebase provided by
> the
>
> s/coverted/converted/
>
> With this tiny fix:
>
> Reviewed-by: David Plowman <david.plowman@raspberrypi.com>
>
> Thanks!
> David
>
> > + * template argument Period of type \a std::ratio.
> > + *
> > + * A typical usage example is given below:
> > + *
> > + * \code{.cpp}
> > + * utils::Duration d = 5s;
> > + * double d_in_ms = d.get<std::milli>();
> > + * \endcode
> > + *
> > + * \return Tick count
> > + */
> > +
> > +/**
> > + * \fn Duration::operator bool()
> > + * \brief Boolean operator to test if a \a Duration holds a non-zero
> time value.
> > + *
> > + * \return True if \a Duration is a non-zero time value, False
> otherwise.
> > + */
> > +
> >  } /* namespace utils */
> >
> >  } /* namespace libcamera */
> > --
> > 2.25.1
> >
>
Kieran Bingham May 21, 2021, 11:14 a.m. UTC | #3
Hi Naush, David,

On 21/05/2021 11:53, Naushir Patuck wrote:
> Hi David,
> 
> Thank you for your feedback.
> 
> On Fri, 21 May 2021 at 11:20, David Plowman
> <david.plowman@raspberrypi.com <mailto:david.plowman@raspberrypi.com>>
> wrote:
> 
>     Hi Naush
> 
>     Thanks for the updated patch!
> 
>     On Fri, 21 May 2021 at 09:05, Naushir Patuck <naush@raspberrypi.com
>     <mailto:naush@raspberrypi.com>> wrote:
>     >
>     > A new utils::Duration class is defined to represent a
>     > std::chrono::duration type with double precision nanosecond timebase.
>     > Using a double minimises the loss of precision when converting
>     timebases.
>     > This helper class may be used by IPAs to represent variables such
>     as frame
>     > durations and exposure times.
>     >
>     > An operator << overload is define to help with displaying
>     > utils::Duration value in stream objects. Currently, this will display
>     > the duration value in microseconds.
>     >
>     > Signed-off-by: Naushir Patuck <naush@raspberrypi.com
>     <mailto:naush@raspberrypi.com>>
>     > ---
>     >  include/libcamera/internal/utils.h | 42
>     ++++++++++++++++++++++++++++++
>     >  src/libcamera/utils.cpp            | 36 +++++++++++++++++++++++++
>     >  2 files changed, 78 insertions(+)
>     >
>     > diff --git a/include/libcamera/internal/utils.h
>     b/include/libcamera/internal/utils.h
>     > index 83dada7cc16c..a377d4ea07ab 100644
>     > --- a/include/libcamera/internal/utils.h
>     > +++ b/include/libcamera/internal/utils.h
>     > @@ -316,6 +316,48 @@ auto enumerate(T (&iterable)[N]) ->
>     details::enumerate_adapter<T *>
>     >  }
>     >  #endif
>     >
>     > +using BaseDuration = std::chrono::duration<double, std::nano>;
>     > +
>     > +class Duration : public BaseDuration
>     > +{
>     > +public:
>     > +       Duration() = default;
>     > +
>     > +       template<typename Rep, typename Period>
>     > +       constexpr Duration(const std::chrono::duration<Rep,
>     Period> &d)
>     > +               : BaseDuration(d)
>     > +       {
>     > +       }
>     > +
>     > +       template<typename Period>
>     > +       double get() const
>     > +       {
>     > +               auto const c =
>     std::chrono::duration_cast<std::chrono::duration<double,
>     Period>>(*this);
>     > +               return c.count();
>     > +       }
>     > +
>     > +       explicit constexpr operator bool() const
>     > +       {
>     > +               return *this != BaseDuration::zero();
>     > +       }
>     > +};
>     > +
>     > +#ifndef __DOXYGEN__
>     > +template<class CharT, class Traits>
>     > +static inline std::basic_ostream<CharT, Traits>
>     &operator<<(std::basic_ostream<CharT, Traits> &os,
>     > +                                                           const
>     Duration &d)
>     > +{
>     > +       std::basic_ostringstream<CharT, Traits> s;
>     > +
>     > +       s.flags(os.flags());
>     > +       s.imbue(os.getloc());
>     > +       s.setf(std::ios_base::fixed, std::ios_base::floatfield);
>     > +       s.precision(2);
>     > +       s << d.get<std::micro>() << "us";
>     > +       return os << s.str();
>     > +}
>     > +#endif
> 
>     One small thing I wonder about is that many classes have a toString
>     method. Do we want one here? It could even take an optional argument
>     to allow you to format the output differently... but I think that's
>     overkill and best left for now!
> 
> 
> Did you mean that we should do something like:
> 
> LOG(IPA, Debug) << "Exposure time is " << exposure.toString();
> 
> over
> 
> LOG(IPA, Debug) << "Exposure time is " << exposure;
> 
> I chose the later way with the overload <<() operator as that is more
> similar
> to what C++20 will eventually allow with std::chrono::duration formatting.
> What do others think?

I think the main reason we've used toString() is so that we don't have
to override operator<< outside of the libcamera namespace, so we've been
more explicit with the toString().

I don't mind the override or the explicit, but I think that's the reasoning.

Would it be cleaner to implement a toString() which doesn't have to
track the outputstream state, using a local stringstream perhaps and as
that returns a string, the override operator<< can simply return that
pre-prepared string?



> 
> Regards,
> Naush
> 
>  
> 
> 
>     > +
>     >  } /* namespace utils */
>     >
>     >  } /* namespace libcamera */
>     > diff --git a/src/libcamera/utils.cpp b/src/libcamera/utils.cpp
>     > index 826144d3c837..3131aa2d6666 100644
>     > --- a/src/libcamera/utils.cpp
>     > +++ b/src/libcamera/utils.cpp
>     > @@ -506,6 +506,42 @@ std::string libcameraSourcePath()
>     >   * loop, iterates over an indexed view of the \a iterable
>     >   */
>     >
>     > +/**
>     > + * \class Duration
>     > + * \brief Helper for std::chrono::duration types. Derived from \a
>     BaseDuration
>     > + */
>     > +
>     > +/**
>     > + * \fn Duration::Duration(const std::chrono::duration<Rep,
>     Period> &d)
>     > + * \brief Copy constructor from a \a std::chrono::duration type.
>     > + * \param[in] d The std::chrono::duration object to convert from.
>     > + *
>     > + * Constructs a \a Duration object from a \a
>     std::chrono::duration object,
>     > + * converting the representation to \a BaseDuration type.
>     > + */
>     > +
>     > +/**
>     > + * \fn Duration::get<Period>()
>     > + * \brief Retrieve the tick count, coverted to the timebase
>     provided by the
> 
>     s/coverted/converted/
> 
>     With this tiny fix:
> 
>     Reviewed-by: David Plowman <david.plowman@raspberrypi.com
>     <mailto:david.plowman@raspberrypi.com>>
> 
>     Thanks!
>     David
> 
>     > + * template argument Period of type \a std::ratio.
>     > + *
>     > + * A typical usage example is given below:
>     > + *
>     > + * \code{.cpp}
>     > + * utils::Duration d = 5s;
>     > + * double d_in_ms = d.get<std::milli>();
>     > + * \endcode
>     > + *
>     > + * \return Tick count
>     > + */
>     > +
>     > +/**
>     > + * \fn Duration::operator bool()
>     > + * \brief Boolean operator to test if a \a Duration holds a
>     non-zero time value.
>     > + *
>     > + * \return True if \a Duration is a non-zero time value, False
>     otherwise.
>     > + */
>     > +
>     >  } /* namespace utils */
>     >
>     >  } /* namespace libcamera */
>     > --
>     > 2.25.1
>     >
>
Naushir Patuck May 21, 2021, 11:44 a.m. UTC | #4
Hi Kieran,

On Fri, 21 May 2021 at 12:14, Kieran Bingham <
kieran.bingham@ideasonboard.com> wrote:

> Hi Naush, David,
>
> On 21/05/2021 11:53, Naushir Patuck wrote:
> > Hi David,
> >
> > Thank you for your feedback.
> >
> > On Fri, 21 May 2021 at 11:20, David Plowman
> > <david.plowman@raspberrypi.com <mailto:david.plowman@raspberrypi.com>>
> > wrote:
> >
> >     Hi Naush
> >
> >     Thanks for the updated patch!
> >
> >     On Fri, 21 May 2021 at 09:05, Naushir Patuck <naush@raspberrypi.com
> >     <mailto:naush@raspberrypi.com>> wrote:
> >     >
> >     > A new utils::Duration class is defined to represent a
> >     > std::chrono::duration type with double precision nanosecond
> timebase.
> >     > Using a double minimises the loss of precision when converting
> >     timebases.
> >     > This helper class may be used by IPAs to represent variables such
> >     as frame
> >     > durations and exposure times.
> >     >
> >     > An operator << overload is define to help with displaying
> >     > utils::Duration value in stream objects. Currently, this will
> display
> >     > the duration value in microseconds.
> >     >
> >     > Signed-off-by: Naushir Patuck <naush@raspberrypi.com
> >     <mailto:naush@raspberrypi.com>>
> >     > ---
> >     >  include/libcamera/internal/utils.h | 42
> >     ++++++++++++++++++++++++++++++
> >     >  src/libcamera/utils.cpp            | 36 +++++++++++++++++++++++++
> >     >  2 files changed, 78 insertions(+)
> >     >
> >     > diff --git a/include/libcamera/internal/utils.h
> >     b/include/libcamera/internal/utils.h
> >     > index 83dada7cc16c..a377d4ea07ab 100644
> >     > --- a/include/libcamera/internal/utils.h
> >     > +++ b/include/libcamera/internal/utils.h
> >     > @@ -316,6 +316,48 @@ auto enumerate(T (&iterable)[N]) ->
> >     details::enumerate_adapter<T *>
> >     >  }
> >     >  #endif
> >     >
> >     > +using BaseDuration = std::chrono::duration<double, std::nano>;
> >     > +
> >     > +class Duration : public BaseDuration
> >     > +{
> >     > +public:
> >     > +       Duration() = default;
> >     > +
> >     > +       template<typename Rep, typename Period>
> >     > +       constexpr Duration(const std::chrono::duration<Rep,
> >     Period> &d)
> >     > +               : BaseDuration(d)
> >     > +       {
> >     > +       }
> >     > +
> >     > +       template<typename Period>
> >     > +       double get() const
> >     > +       {
> >     > +               auto const c =
> >     std::chrono::duration_cast<std::chrono::duration<double,
> >     Period>>(*this);
> >     > +               return c.count();
> >     > +       }
> >     > +
> >     > +       explicit constexpr operator bool() const
> >     > +       {
> >     > +               return *this != BaseDuration::zero();
> >     > +       }
> >     > +};
> >     > +
> >     > +#ifndef __DOXYGEN__
> >     > +template<class CharT, class Traits>
> >     > +static inline std::basic_ostream<CharT, Traits>
> >     &operator<<(std::basic_ostream<CharT, Traits> &os,
> >     > +                                                           const
> >     Duration &d)
> >     > +{
> >     > +       std::basic_ostringstream<CharT, Traits> s;
> >     > +
> >     > +       s.flags(os.flags());
> >     > +       s.imbue(os.getloc());
> >     > +       s.setf(std::ios_base::fixed, std::ios_base::floatfield);
> >     > +       s.precision(2);
> >     > +       s << d.get<std::micro>() << "us";
> >     > +       return os << s.str();
> >     > +}
> >     > +#endif
> >
> >     One small thing I wonder about is that many classes have a toString
> >     method. Do we want one here? It could even take an optional argument
> >     to allow you to format the output differently... but I think that's
> >     overkill and best left for now!
> >
> >
> > Did you mean that we should do something like:
> >
> > LOG(IPA, Debug) << "Exposure time is " << exposure.toString();
> >
> > over
> >
> > LOG(IPA, Debug) << "Exposure time is " << exposure;
> >
> > I chose the later way with the overload <<() operator as that is more
> > similar
> > to what C++20 will eventually allow with std::chrono::duration
> formatting.
> > What do others think?
>
> I think the main reason we've used toString() is so that we don't have
> to override operator<< outside of the libcamera namespace, so we've been
> more explicit with the toString().
>

> I don't mind the override or the explicit, but I think that's the
> reasoning.
>
> Would it be cleaner to implement a toString() which doesn't have to
> track the outputstream state, using a local stringstream perhaps and as
> that returns a string, the override operator<< can simply return that
> pre-prepared string?
>

I don't think it makes much of a difference in the implementation, as this
is exactly
what the operator<< overload does right now.  There is an awkwardness in
having
to remember to do a "using libcamera::utils::operator<<" in every source
file
that wants to use the overload.  Te toString() usage could eliminate that,
but then
it means typing more characters in your logging statement :-)

I do like the overload usage (apart from the using.. directive) in that it
sort of mirrors
what C++20 will allow.

Naush


>
>
> >
> > Regards,
> > Naush
> >
> >
> >
> >
> >     > +
> >     >  } /* namespace utils */
> >     >
> >     >  } /* namespace libcamera */
> >     > diff --git a/src/libcamera/utils.cpp b/src/libcamera/utils.cpp
> >     > index 826144d3c837..3131aa2d6666 100644
> >     > --- a/src/libcamera/utils.cpp
> >     > +++ b/src/libcamera/utils.cpp
> >     > @@ -506,6 +506,42 @@ std::string libcameraSourcePath()
> >     >   * loop, iterates over an indexed view of the \a iterable
> >     >   */
> >     >
> >     > +/**
> >     > + * \class Duration
> >     > + * \brief Helper for std::chrono::duration types. Derived from \a
> >     BaseDuration
> >     > + */
> >     > +
> >     > +/**
> >     > + * \fn Duration::Duration(const std::chrono::duration<Rep,
> >     Period> &d)
> >     > + * \brief Copy constructor from a \a std::chrono::duration type.
> >     > + * \param[in] d The std::chrono::duration object to convert from.
> >     > + *
> >     > + * Constructs a \a Duration object from a \a
> >     std::chrono::duration object,
> >     > + * converting the representation to \a BaseDuration type.
> >     > + */
> >     > +
> >     > +/**
> >     > + * \fn Duration::get<Period>()
> >     > + * \brief Retrieve the tick count, coverted to the timebase
> >     provided by the
> >
> >     s/coverted/converted/
> >
> >     With this tiny fix:
> >
> >     Reviewed-by: David Plowman <david.plowman@raspberrypi.com
> >     <mailto:david.plowman@raspberrypi.com>>
> >
> >     Thanks!
> >     David
> >
> >     > + * template argument Period of type \a std::ratio.
> >     > + *
> >     > + * A typical usage example is given below:
> >     > + *
> >     > + * \code{.cpp}
> >     > + * utils::Duration d = 5s;
> >     > + * double d_in_ms = d.get<std::milli>();
> >     > + * \endcode
> >     > + *
> >     > + * \return Tick count
> >     > + */
> >     > +
> >     > +/**
> >     > + * \fn Duration::operator bool()
> >     > + * \brief Boolean operator to test if a \a Duration holds a
> >     non-zero time value.
> >     > + *
> >     > + * \return True if \a Duration is a non-zero time value, False
> >     otherwise.
> >     > + */
> >     > +
> >     >  } /* namespace utils */
> >     >
> >     >  } /* namespace libcamera */
> >     > --
> >     > 2.25.1
> >     >
> >
>
> --
> Regards
> --
> Kieran
>
Jacopo Mondi May 21, 2021, 12:27 p.m. UTC | #5
Hi Naush,

On Fri, May 21, 2021 at 09:05:27AM +0100, Naushir Patuck wrote:
> A new utils::Duration class is defined to represent a
> std::chrono::duration type with double precision nanosecond timebase.
> Using a double minimises the loss of precision when converting timebases.
> This helper class may be used by IPAs to represent variables such as frame
> durations and exposure times.
>
> An operator << overload is define to help with displaying
> utils::Duration value in stream objects. Currently, this will display
> the duration value in microseconds.
>
> Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
> ---
>  include/libcamera/internal/utils.h | 42 ++++++++++++++++++++++++++++++
>  src/libcamera/utils.cpp            | 36 +++++++++++++++++++++++++
>  2 files changed, 78 insertions(+)
>
> diff --git a/include/libcamera/internal/utils.h b/include/libcamera/internal/utils.h
> index 83dada7cc16c..a377d4ea07ab 100644
> --- a/include/libcamera/internal/utils.h
> +++ b/include/libcamera/internal/utils.h
> @@ -316,6 +316,48 @@ auto enumerate(T (&iterable)[N]) -> details::enumerate_adapter<T *>
>  }
>  #endif
>
> +using BaseDuration = std::chrono::duration<double, std::nano>;

I was a bit puzzled by the use of double, but reading the standard I
see constructor 4)

template< class Rep2, class Period2 >
        constexpr duration( const duration<Rep2,Period2>& d );

which partecipates in overload resolution if
        computation of the conversion factor (by
        std::ratio_divide<Period2, Period>) does not overflow and:
                - std::chrono::treat_as_floating_point<rep>::value == true
        or both:
                - std::ratio_divide<Period2, period>::den == 1, and
                - std::chrono::treat_as_floating_point<Rep2>::value == false.

        (that is, either the duration uses floating-point ticks, or
        Period2 is exactly divisible by period)

So I guess we need double to maximize the number of template
specializations that can be used to construct a Duration ?

Could we use std::chrono:nanoseconds ? Or it gets complicated to
specify the additional <double> template argument ? Just out of
curiosity...

Also, I would not expose BaseDuration outside. Can this be done as

        class Duration : public std::chrono::duration:...
        {
                using BaseDuration = ....

        };

Or do you plan to expose BaseDuration to the rest of the library ?

> +
> +class Duration : public BaseDuration
> +{
> +public:
> +	Duration() = default;
> +
> +	template<typename Rep, typename Period>
> +	constexpr Duration(const std::chrono::duration<Rep, Period> &d)
> +		: BaseDuration(d)
> +	{
> +	}
> +
> +	template<typename Period>
> +	double get() const
> +	{
> +		auto const c = std::chrono::duration_cast<std::chrono::duration<double, Period>>(*this);
> +		return c.count();
> +	}
> +
> +	explicit constexpr operator bool() const
> +	{
> +		return *this != BaseDuration::zero();
> +	}
> +};
> +
> +#ifndef __DOXYGEN__
> +template<class CharT, class Traits>
> +static inline std::basic_ostream<CharT, Traits> &operator<<(std::basic_ostream<CharT, Traits> &os,
> +							    const Duration &d)

Nit: alignement to (

> +{
> +	std::basic_ostringstream<CharT, Traits> s;
> +
> +	s.flags(os.flags());
> +	s.imbue(os.getloc());
> +	s.setf(std::ios_base::fixed, std::ios_base::floatfield);
> +	s.precision(2);

Why micro and not nano ?

> +	s << d.get<std::micro>() << "us";
> +	return os << s.str();
> +}
> +#endif
> +
>  } /* namespace utils */
>
>  } /* namespace libcamera */
> diff --git a/src/libcamera/utils.cpp b/src/libcamera/utils.cpp
> index 826144d3c837..3131aa2d6666 100644
> --- a/src/libcamera/utils.cpp
> +++ b/src/libcamera/utils.cpp
> @@ -506,6 +506,42 @@ std::string libcameraSourcePath()
>   * loop, iterates over an indexed view of the \a iterable
>   */
>
> +/**
> + * \class Duration
> + * \brief Helper for std::chrono::duration types. Derived from \a BaseDuration

Where's BaseDuration documented ?

> + */
> +
> +/**
> + * \fn Duration::Duration(const std::chrono::duration<Rep, Period> &d)
> + * \brief Copy constructor from a \a std::chrono::duration type.
> + * \param[in] d The std::chrono::duration object to convert from.

we don't usually end with a . in documentation single sentences

> + *
> + * Constructs a \a Duration object from a \a std::chrono::duration object,
> + * converting the representation to \a BaseDuration type.
> + */
> +
> +/**
> + * \fn Duration::get<Period>()
> + * \brief Retrieve the tick count, coverted to the timebase provided by the
> + * template argument Period of type \a std::ratio.
> + *
> + * A typical usage example is given below:
> + *
> + * \code{.cpp}
> + * utils::Duration d = 5s;
> + * double d_in_ms = d.get<std::milli>();

Nice!

> + * \endcode
> + *
> + * \return Tick count

The tick count of the Duration expressed in \a Period ?
> + */
> +
> +/**
> + * \fn Duration::operator bool()
> + * \brief Boolean operator to test if a \a Duration holds a non-zero time value.
> + *
> + * \return True if \a Duration is a non-zero time value, False otherwise.
> + */

I'm a bit skeptical of operator overloading like this one, even if
it's understandable that
        Duration d = ...
        if (!d) {

        }
checks for d being 0 or not.

Not sure Duration::zero() is not better, nor is Duration::expired()...

All minors, the patch looks mostly good, maybe with BaseDuration made
private...

Thanks
  j


> +
>  } /* namespace utils */
>
>  } /* namespace libcamera */
> --
> 2.25.1
>
Naushir Patuck May 21, 2021, 12:42 p.m. UTC | #6
Hi Jacopo,

Thank you for your review feedback.

On Fri, 21 May 2021 at 13:27, Jacopo Mondi <jacopo@jmondi.org> wrote:

> Hi Naush,
>
> On Fri, May 21, 2021 at 09:05:27AM +0100, Naushir Patuck wrote:
> > A new utils::Duration class is defined to represent a
> > std::chrono::duration type with double precision nanosecond timebase.
> > Using a double minimises the loss of precision when converting timebases.
> > This helper class may be used by IPAs to represent variables such as
> frame
> > durations and exposure times.
> >
> > An operator << overload is define to help with displaying
> > utils::Duration value in stream objects. Currently, this will display
> > the duration value in microseconds.
> >
> > Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
> > ---
> >  include/libcamera/internal/utils.h | 42 ++++++++++++++++++++++++++++++
> >  src/libcamera/utils.cpp            | 36 +++++++++++++++++++++++++
> >  2 files changed, 78 insertions(+)
> >
> > diff --git a/include/libcamera/internal/utils.h
> b/include/libcamera/internal/utils.h
> > index 83dada7cc16c..a377d4ea07ab 100644
> > --- a/include/libcamera/internal/utils.h
> > +++ b/include/libcamera/internal/utils.h
> > @@ -316,6 +316,48 @@ auto enumerate(T (&iterable)[N]) ->
> details::enumerate_adapter<T *>
> >  }
> >  #endif
> >
> > +using BaseDuration = std::chrono::duration<double, std::nano>;
>
> I was a bit puzzled by the use of double, but reading the standard I
> see constructor 4)
>
> template< class Rep2, class Period2 >
>         constexpr duration( const duration<Rep2,Period2>& d );
>
> which partecipates in overload resolution if
>         computation of the conversion factor (by
>         std::ratio_divide<Period2, Period>) does not overflow and:
>                 - std::chrono::treat_as_floating_point<rep>::value == true
>         or both:
>                 - std::ratio_divide<Period2, period>::den == 1, and
>                 - std::chrono::treat_as_floating_point<Rep2>::value ==
> false.
>
>         (that is, either the duration uses floating-point ticks, or
>         Period2 is exactly divisible by period)
>
> So I guess we need double to maximize the number of template
> specializations that can be used to construct a Duration ?
>

That's correct!  Also I used double to preserve precision during possible
base conversions.


>
> Could we use std::chrono:nanoseconds ? Or it gets complicated to
> specify the additional <double> template argument ? Just out of
> curiosity...
>

I don't think we can, std::chrono::nanoseconds is a typedef for
std::chrono::duration</*signed integer*/, std::nano>, so we would
lose the double.


>
> Also, I would not expose BaseDuration outside. Can this be done as
>
>         class Duration : public std::chrono::duration:...
>         {
>                 using BaseDuration = ....
>
>         };
>
> Or do you plan to expose BaseDuration to the rest of the library ?
>

I don't see BaseDuration being used outside this library, so I will put it
into the class definition as you suggested.


>
> > +
> > +class Duration : public BaseDuration
> > +{
> > +public:
> > +     Duration() = default;
> > +
> > +     template<typename Rep, typename Period>
> > +     constexpr Duration(const std::chrono::duration<Rep, Period> &d)
> > +             : BaseDuration(d)
> > +     {
> > +     }
> > +
> > +     template<typename Period>
> > +     double get() const
> > +     {
> > +             auto const c =
> std::chrono::duration_cast<std::chrono::duration<double, Period>>(*this);
> > +             return c.count();
> > +     }
> > +
> > +     explicit constexpr operator bool() const
> > +     {
> > +             return *this != BaseDuration::zero();
> > +     }
> > +};
> > +
> > +#ifndef __DOXYGEN__
> > +template<class CharT, class Traits>
> > +static inline std::basic_ostream<CharT, Traits>
> &operator<<(std::basic_ostream<CharT, Traits> &os,
> > +                                                         const Duration
> &d)
>
> Nit: alignement to (
>

Ack.


>
> > +{
> > +     std::basic_ostringstream<CharT, Traits> s;
> > +
> > +     s.flags(os.flags());
> > +     s.imbue(os.getloc());
> > +     s.setf(std::ios_base::fixed, std::ios_base::floatfield);
> > +     s.precision(2);
>
> Why micro and not nano ?
>

Purely personal preference :-)
Laurent did also suggest using seconds.  I don't have strong opinions on
this one,
so I will go with what the majority would like.


> > +     s << d.get<std::micro>() << "us";
> > +     return os << s.str();
> > +}
> > +#endif
> > +
> >  } /* namespace utils */
> >
> >  } /* namespace libcamera */
> > diff --git a/src/libcamera/utils.cpp b/src/libcamera/utils.cpp
> > index 826144d3c837..3131aa2d6666 100644
> > --- a/src/libcamera/utils.cpp
> > +++ b/src/libcamera/utils.cpp
> > @@ -506,6 +506,42 @@ std::string libcameraSourcePath()
> >   * loop, iterates over an indexed view of the \a iterable
> >   */
> >
> > +/**
> > + * \class Duration
> > + * \brief Helper for std::chrono::duration types. Derived from \a
> BaseDuration
>
> Where's BaseDuration documented ?
>

Sorry, this is my bad.  It somehow ended up in patch 4/4, don't ask how :-)
I'll move it back to this patch in the next revision.


>
> > + */
> > +
> > +/**
> > + * \fn Duration::Duration(const std::chrono::duration<Rep, Period> &d)
> > + * \brief Copy constructor from a \a std::chrono::duration type.
> > + * \param[in] d The std::chrono::duration object to convert from.
>
> we don't usually end with a . in documentation single sentences
>

Ack.


>
> > + *
> > + * Constructs a \a Duration object from a \a std::chrono::duration
> object,
> > + * converting the representation to \a BaseDuration type.
> > + */
> > +
> > +/**
> > + * \fn Duration::get<Period>()
> > + * \brief Retrieve the tick count, coverted to the timebase provided by
> the
> > + * template argument Period of type \a std::ratio.
> > + *
> > + * A typical usage example is given below:
> > + *
> > + * \code{.cpp}
> > + * utils::Duration d = 5s;
> > + * double d_in_ms = d.get<std::milli>();
>
> Nice!
>
> > + * \endcode
> > + *
> > + * \return Tick count
>
> The tick count of the Duration expressed in \a Period ?
>

Ack.


> > + */
> > +
> > +/**
> > + * \fn Duration::operator bool()
> > + * \brief Boolean operator to test if a \a Duration holds a non-zero
> time value.
> > + *
> > + * \return True if \a Duration is a non-zero time value, False
> otherwise.
> > + */
>
> I'm a bit skeptical of operator overloading like this one, even if
> it's understandable that
>         Duration d = ...
>         if (!d) {
>
>         }
> checks for d being 0 or not.
>
> Not sure Duration::zero() is not better, nor is Duration::expired()...
>

I do like that we can use Duration as a scaler equivalent with the overload,
e.g.

Duration manualShutter = 5ms;

If (manualShutter) {
    /* do something */
}

But again, I am happy to create a Duration::zero() if you prefer.

Regards,
Naush


>
> All minors, the patch looks mostly good, maybe with BaseDuration made
> private...
>
> Thanks
>   j
>
>
> > +
> >  } /* namespace utils */
> >
> >  } /* namespace libcamera */
> > --
> > 2.25.1
> >
>
Jacopo Mondi May 21, 2021, 1:05 p.m. UTC | #7
Hi Naush,

On Fri, May 21, 2021 at 01:42:43PM +0100, Naushir Patuck wrote:
> Hi Jacopo,
>
> Thank you for your review feedback.
>
> On Fri, 21 May 2021 at 13:27, Jacopo Mondi <jacopo@jmondi.org> wrote:
>
> > Hi Naush,
> >
> > On Fri, May 21, 2021 at 09:05:27AM +0100, Naushir Patuck wrote:
> > > A new utils::Duration class is defined to represent a
> > > std::chrono::duration type with double precision nanosecond timebase.
> > > Using a double minimises the loss of precision when converting timebases.
> > > This helper class may be used by IPAs to represent variables such as
> > frame
> > > durations and exposure times.
> > >
> > > An operator << overload is define to help with displaying
> > > utils::Duration value in stream objects. Currently, this will display
> > > the duration value in microseconds.
> > >
> > > Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
> > > ---
> > >  include/libcamera/internal/utils.h | 42 ++++++++++++++++++++++++++++++
> > >  src/libcamera/utils.cpp            | 36 +++++++++++++++++++++++++
> > >  2 files changed, 78 insertions(+)
> > >
> > > diff --git a/include/libcamera/internal/utils.h
> > b/include/libcamera/internal/utils.h
> > > index 83dada7cc16c..a377d4ea07ab 100644
> > > --- a/include/libcamera/internal/utils.h
> > > +++ b/include/libcamera/internal/utils.h
> > > @@ -316,6 +316,48 @@ auto enumerate(T (&iterable)[N]) ->
> > details::enumerate_adapter<T *>
> > >  }
> > >  #endif
> > >
> > > +using BaseDuration = std::chrono::duration<double, std::nano>;
> >
> > I was a bit puzzled by the use of double, but reading the standard I
> > see constructor 4)
> >
> > template< class Rep2, class Period2 >
> >         constexpr duration( const duration<Rep2,Period2>& d );
> >
> > which partecipates in overload resolution if
> >         computation of the conversion factor (by
> >         std::ratio_divide<Period2, Period>) does not overflow and:
> >                 - std::chrono::treat_as_floating_point<rep>::value == true
> >         or both:
> >                 - std::ratio_divide<Period2, period>::den == 1, and
> >                 - std::chrono::treat_as_floating_point<Rep2>::value ==
> > false.
> >
> >         (that is, either the duration uses floating-point ticks, or
> >         Period2 is exactly divisible by period)
> >
> > So I guess we need double to maximize the number of template
> > specializations that can be used to construct a Duration ?
> >
>
> That's correct!  Also I used double to preserve precision during possible
> base conversions.
>
>
> >
> > Could we use std::chrono:nanoseconds ? Or it gets complicated to
> > specify the additional <double> template argument ? Just out of
> > curiosity...
> >
>
> I don't think we can, std::chrono::nanoseconds is a typedef for
> std::chrono::duration</*signed integer*/, std::nano>, so we would
> lose the double.
>
>
> >
> > Also, I would not expose BaseDuration outside. Can this be done as
> >
> >         class Duration : public std::chrono::duration:...
> >         {
> >                 using BaseDuration = ....
> >
> >         };
> >
> > Or do you plan to expose BaseDuration to the rest of the library ?
> >
>
> I don't see BaseDuration being used outside this library, so I will put it
> into the class definition as you suggested.
>

Thanks!

>
> >
> > > +
> > > +class Duration : public BaseDuration
> > > +{
> > > +public:
> > > +     Duration() = default;
> > > +
> > > +     template<typename Rep, typename Period>
> > > +     constexpr Duration(const std::chrono::duration<Rep, Period> &d)
> > > +             : BaseDuration(d)
> > > +     {
> > > +     }
> > > +
> > > +     template<typename Period>
> > > +     double get() const
> > > +     {
> > > +             auto const c =
> > std::chrono::duration_cast<std::chrono::duration<double, Period>>(*this);
> > > +             return c.count();
> > > +     }
> > > +
> > > +     explicit constexpr operator bool() const
> > > +     {
> > > +             return *this != BaseDuration::zero();
> > > +     }
> > > +};
> > > +
> > > +#ifndef __DOXYGEN__
> > > +template<class CharT, class Traits>
> > > +static inline std::basic_ostream<CharT, Traits>
> > &operator<<(std::basic_ostream<CharT, Traits> &os,
> > > +                                                         const Duration
> > &d)
> >
> > Nit: alignement to (
> >
>
> Ack.
>
>
> >
> > > +{
> > > +     std::basic_ostringstream<CharT, Traits> s;
> > > +
> > > +     s.flags(os.flags());
> > > +     s.imbue(os.getloc());
> > > +     s.setf(std::ios_base::fixed, std::ios_base::floatfield);
> > > +     s.precision(2);
> >
> > Why micro and not nano ?
> >
>
> Purely personal preference :-)
> Laurent did also suggest using seconds.  I don't have strong opinions on
> this one,
> so I will go with what the majority would like.

I see. Seconds for the kind of durations we deal with it probably too
much ? We have failed to standardize on a time measurement unit
unforuntately. We have a mjority of controls that work in microseconds
and one nanosecond (SensorTimestamp, my bad). Keep micro might be
better to align with most of the time measurement units. Although
stabilizing on micro might require some rational values to represent
shorter events such as a line duration. Anyway, not strictly related
to this one, feel free to keep micro if you like it most.

>
>
> > > +     s << d.get<std::micro>() << "us";
> > > +     return os << s.str();
> > > +}
> > > +#endif
> > > +
> > >  } /* namespace utils */
> > >
> > >  } /* namespace libcamera */
> > > diff --git a/src/libcamera/utils.cpp b/src/libcamera/utils.cpp
> > > index 826144d3c837..3131aa2d6666 100644
> > > --- a/src/libcamera/utils.cpp
> > > +++ b/src/libcamera/utils.cpp
> > > @@ -506,6 +506,42 @@ std::string libcameraSourcePath()
> > >   * loop, iterates over an indexed view of the \a iterable
> > >   */
> > >
> > > +/**
> > > + * \class Duration
> > > + * \brief Helper for std::chrono::duration types. Derived from \a
> > BaseDuration
> >
> > Where's BaseDuration documented ?
> >
>
> Sorry, this is my bad.  It somehow ended up in patch 4/4, don't ask how :-)
> I'll move it back to this patch in the next revision.
>
>
> >
> > > + */
> > > +
> > > +/**
> > > + * \fn Duration::Duration(const std::chrono::duration<Rep, Period> &d)
> > > + * \brief Copy constructor from a \a std::chrono::duration type.
> > > + * \param[in] d The std::chrono::duration object to convert from.
> >
> > we don't usually end with a . in documentation single sentences
> >
>
> Ack.
>
>
> >
> > > + *
> > > + * Constructs a \a Duration object from a \a std::chrono::duration
> > object,
> > > + * converting the representation to \a BaseDuration type.
> > > + */
> > > +
> > > +/**
> > > + * \fn Duration::get<Period>()
> > > + * \brief Retrieve the tick count, coverted to the timebase provided by
> > the
> > > + * template argument Period of type \a std::ratio.
> > > + *
> > > + * A typical usage example is given below:
> > > + *
> > > + * \code{.cpp}
> > > + * utils::Duration d = 5s;
> > > + * double d_in_ms = d.get<std::milli>();
> >
> > Nice!
> >
> > > + * \endcode
> > > + *
> > > + * \return Tick count
> >
> > The tick count of the Duration expressed in \a Period ?
> >
>
> Ack.
>
>
> > > + */
> > > +
> > > +/**
> > > + * \fn Duration::operator bool()
> > > + * \brief Boolean operator to test if a \a Duration holds a non-zero
> > time value.
> > > + *
> > > + * \return True if \a Duration is a non-zero time value, False
> > otherwise.
> > > + */
> >
> > I'm a bit skeptical of operator overloading like this one, even if
> > it's understandable that
> >         Duration d = ...
> >         if (!d) {
> >
> >         }
> > checks for d being 0 or not.
> >
> > Not sure Duration::zero() is not better, nor is Duration::expired()...
> >
>
> I do like that we can use Duration as a scaler equivalent with the overload,
> e.g.
>
> Duration manualShutter = 5ms;
>
> If (manualShutter) {
>     /* do something */
> }
>
> But again, I am happy to create a Duration::zero() if you prefer.

I'm not pushing for it, as I think the behaviour of this bool operator
is predicatable enough, so feel free to keep it!

Thanks
  j
>
> Regards,
> Naush
>
>
> >
> > All minors, the patch looks mostly good, maybe with BaseDuration made
> > private...
> >
> > Thanks
> >   j
> >
> >
> > > +
> > >  } /* namespace utils */
> > >
> > >  } /* namespace libcamera */
> > > --
> > > 2.25.1
> > >
> >

Patch
diff mbox series

diff --git a/include/libcamera/internal/utils.h b/include/libcamera/internal/utils.h
index 83dada7cc16c..a377d4ea07ab 100644
--- a/include/libcamera/internal/utils.h
+++ b/include/libcamera/internal/utils.h
@@ -316,6 +316,48 @@  auto enumerate(T (&iterable)[N]) -> details::enumerate_adapter<T *>
 }
 #endif
 
+using BaseDuration = std::chrono::duration<double, std::nano>;
+
+class Duration : public BaseDuration
+{
+public:
+	Duration() = default;
+
+	template<typename Rep, typename Period>
+	constexpr Duration(const std::chrono::duration<Rep, Period> &d)
+		: BaseDuration(d)
+	{
+	}
+
+	template<typename Period>
+	double get() const
+	{
+		auto const c = std::chrono::duration_cast<std::chrono::duration<double, Period>>(*this);
+		return c.count();
+	}
+
+	explicit constexpr operator bool() const
+	{
+		return *this != BaseDuration::zero();
+	}
+};
+
+#ifndef __DOXYGEN__
+template<class CharT, class Traits>
+static inline std::basic_ostream<CharT, Traits> &operator<<(std::basic_ostream<CharT, Traits> &os,
+							    const Duration &d)
+{
+	std::basic_ostringstream<CharT, Traits> s;
+
+	s.flags(os.flags());
+	s.imbue(os.getloc());
+	s.setf(std::ios_base::fixed, std::ios_base::floatfield);
+	s.precision(2);
+	s << d.get<std::micro>() << "us";
+	return os << s.str();
+}
+#endif
+
 } /* namespace utils */
 
 } /* namespace libcamera */
diff --git a/src/libcamera/utils.cpp b/src/libcamera/utils.cpp
index 826144d3c837..3131aa2d6666 100644
--- a/src/libcamera/utils.cpp
+++ b/src/libcamera/utils.cpp
@@ -506,6 +506,42 @@  std::string libcameraSourcePath()
  * loop, iterates over an indexed view of the \a iterable
  */
 
+/**
+ * \class Duration
+ * \brief Helper for std::chrono::duration types. Derived from \a BaseDuration
+ */
+
+/**
+ * \fn Duration::Duration(const std::chrono::duration<Rep, Period> &d)
+ * \brief Copy constructor from a \a std::chrono::duration type.
+ * \param[in] d The std::chrono::duration object to convert from.
+ *
+ * Constructs a \a Duration object from a \a std::chrono::duration object,
+ * converting the representation to \a BaseDuration type.
+ */
+
+/**
+ * \fn Duration::get<Period>()
+ * \brief Retrieve the tick count, coverted to the timebase provided by the
+ * template argument Period of type \a std::ratio.
+ *
+ * A typical usage example is given below:
+ *
+ * \code{.cpp}
+ * utils::Duration d = 5s;
+ * double d_in_ms = d.get<std::milli>();
+ * \endcode
+ *
+ * \return Tick count
+ */
+
+/**
+ * \fn Duration::operator bool()
+ * \brief Boolean operator to test if a \a Duration holds a non-zero time value.
+ *
+ * \return True if \a Duration is a non-zero time value, False otherwise.
+ */
+
 } /* namespace utils */
 
 } /* namespace libcamera */