<div dir="ltr"><div dir="ltr"><br></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Fri, 21 May 2021 at 09:05, Naushir Patuck <<a href="mailto:naush@raspberrypi.com">naush@raspberrypi.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">Convert the core AGC and Lux controller code to use<br>
utils::Duration for all exposure time related variables and<br>
calculations.<br>
<br>
Convert the exposure/shutter time fields in AgcStatus and DeviceStatus<br>
to use utils::Duration.<br>
<br>
Signed-off-by: Naushir Patuck <<a href="mailto:naush@raspberrypi.com" target="_blank">naush@raspberrypi.com</a>><br>
---<br>
 src/ipa/raspberrypi/cam_helper.cpp            |  2 +-<br>
 src/ipa/raspberrypi/controller/agc_status.h   | 14 +--<br>
 .../raspberrypi/controller/device_status.h    |  6 +-<br>
 src/ipa/raspberrypi/controller/rpi/agc.cpp    | 89 +++++++++++--------<br>
 src/ipa/raspberrypi/controller/rpi/agc.hpp    | 28 +++---<br>
 src/ipa/raspberrypi/controller/rpi/lux.cpp    | 17 ++--<br>
 src/ipa/raspberrypi/controller/rpi/lux.hpp    |  4 +-<br>
 src/ipa/raspberrypi/raspberrypi.cpp           | 13 +--<br>
 src/libcamera/utils.cpp                       |  9 +-<br>
 9 files changed, 105 insertions(+), 77 deletions(-)<br>
<br>
diff --git a/src/ipa/raspberrypi/cam_helper.cpp b/src/ipa/raspberrypi/cam_helper.cpp<br>
index feb02d1806b2..968fae13bd5e 100644<br>
--- a/src/ipa/raspberrypi/cam_helper.cpp<br>
+++ b/src/ipa/raspberrypi/cam_helper.cpp<br>
@@ -183,7 +183,7 @@ void CamHelper::parseEmbeddedData(Span<const uint8_t> buffer,<br>
                return;<br>
        }<br>
<br>
-       deviceStatus.shutter_speed = Exposure(exposureLines).get<std::micro>();<br>
+       deviceStatus.shutter_speed = Exposure(exposureLines);<br>
        deviceStatus.analogue_gain = Gain(gainCode);<br>
<br>
        LOG(IPARPI, Debug) << "Metadata updated - Exposure : "<br>
diff --git a/src/ipa/raspberrypi/controller/agc_status.h b/src/ipa/raspberrypi/controller/agc_status.h<br>
index 10381c90a313..c893715af588 100644<br>
--- a/src/ipa/raspberrypi/controller/agc_status.h<br>
+++ b/src/ipa/raspberrypi/controller/agc_status.h<br>
@@ -6,6 +6,8 @@<br>
  */<br>
 #pragma once<br>
<br>
+#include "libcamera/internal/utils.h"<br>
+<br>
 // The AGC algorithm should post the following structure into the image's<br>
 // "agc.status" metadata.<br>
<br>
@@ -17,18 +19,20 @@ extern "C" {<br>
 // seen statistics and calculated meaningful values. The contents should be<br>
 // ignored until then.<br>
<br>
+using libcamera::utils::Duration;<br>
+<br>
 struct AgcStatus {<br>
-       double total_exposure_value; // value for all exposure and gain for this image<br>
-       double target_exposure_value; // (unfiltered) target total exposure AGC is aiming for<br>
-       double shutter_time;<br>
+       Duration total_exposure_value; // value for all exposure and gain for this image<br>
+       Duration target_exposure_value; // (unfiltered) target total exposure AGC is aiming for<br>
+       Duration shutter_time;<br>
        double analogue_gain;<br>
        char exposure_mode[32];<br>
        char constraint_mode[32];<br>
        char metering_mode[32];<br>
        double ev;<br>
-       double flicker_period;<br>
+       Duration flicker_period;<br>
        int floating_region_enable;<br>
-       double fixed_shutter;<br>
+       Duration fixed_shutter;<br>
        double fixed_analogue_gain;<br>
        double digital_gain;<br>
        int locked;<br>
diff --git a/src/ipa/raspberrypi/controller/device_status.h b/src/ipa/raspberrypi/controller/device_status.h<br>
index aa08608b5d40..131b4cd344ee 100644<br>
--- a/src/ipa/raspberrypi/controller/device_status.h<br>
+++ b/src/ipa/raspberrypi/controller/device_status.h<br>
@@ -6,6 +6,8 @@<br>
  */<br>
 #pragma once<br>
<br>
+#include "libcamera/internal/utils.h"<br>
+<br>
 // Definition of "device metadata" which stores things like shutter time and<br>
 // analogue gain that downstream control algorithms will want to know.<br>
<br>
@@ -14,8 +16,8 @@ extern "C" {<br>
 #endif<br>
<br>
 struct DeviceStatus {<br>
-       // time shutter is open, in microseconds<br>
-       double shutter_speed;<br>
+       // time shutter is open<br>
+       libcamera::utils::Duration shutter_speed;<br>
        double analogue_gain;<br>
        // 1.0/distance-in-metres, or 0 if unknown<br>
        double lens_position;<br>
diff --git a/src/ipa/raspberrypi/controller/rpi/agc.cpp b/src/ipa/raspberrypi/controller/rpi/agc.cpp<br>
index 482eb3ef962d..46742d845034 100644<br>
--- a/src/ipa/raspberrypi/controller/rpi/agc.cpp<br>
+++ b/src/ipa/raspberrypi/controller/rpi/agc.cpp<br>
@@ -21,6 +21,7 @@<br>
<br>
 using namespace RPiController;<br>
 using namespace libcamera;<br>
+using utils::operator<<;<br>
<br>
 LOG_DEFINE_CATEGORY(RPiAgc)<br>
<br>
@@ -55,19 +56,27 @@ read_metering_modes(std::map<std::string, AgcMeteringMode> &metering_modes,<br>
        return first;<br>
 }<br>
<br>
-static int read_double_list(std::vector<double> &list,<br>
-                           boost::property_tree::ptree const &params)<br>
+static int read_list(std::vector<double> &list,<br>
+                    boost::property_tree::ptree const &params)<br>
 {<br>
        for (auto &p : params)<br>
                list.push_back(p.second.get_value<double>());<br>
        return list.size();<br>
 }<br>
<br>
+static int read_list(std::vector<Duration> &list,<br>
+                    boost::property_tree::ptree const &params)<br>
+{<br>
+       for (auto &p : params)<br>
+               list.push_back(p.second.get_value<double>() * 1us);<br>
+       return list.size();<br>
+}<br>
+<br>
 void AgcExposureMode::Read(boost::property_tree::ptree const &params)<br>
 {<br>
        int num_shutters =<br>
-               read_double_list(shutter, params.get_child("shutter"));<br>
-       int num_ags = read_double_list(gain, params.get_child("gain"));<br>
+               read_list(shutter, params.get_child("shutter"));<br>
+       int num_ags = read_list(gain, params.get_child("gain"));<br>
        if (num_shutters < 2 || num_ags < 2)<br>
                throw std::runtime_error(<br>
                        "AgcConfig: must have at least two entries in exposure profile");<br>
@@ -147,7 +156,7 @@ void AgcConfig::Read(boost::property_tree::ptree const &params)<br>
                params.get<double>("fast_reduce_threshold", 0.4);<br>
        base_ev = params.get<double>("base_ev", 1.0);<br>
        // Start with quite a low value as ramping up is easier than ramping down.<br>
-       default_exposure_time = params.get<double>("default_exposure_time", 1000);<br>
+       default_exposure_time = params.get<double>("default_exposure_time", 1000) * 1us;<br>
        default_analogue_gain = params.get<double>("default_analogue_gain", 1.0);<br>
 }<br>
<br>
@@ -155,9 +164,9 @@ Agc::Agc(Controller *controller)<br>
        : AgcAlgorithm(controller), metering_mode_(nullptr),<br>
          exposure_mode_(nullptr), constraint_mode_(nullptr),<br>
          frame_count_(0), lock_count_(0),<br>
-         last_target_exposure_(0.0),<br>
-         ev_(1.0), flicker_period_(0.0),<br>
-         max_shutter_(0), fixed_shutter_(0), fixed_analogue_gain_(0.0)<br>
+         last_target_exposure_(0s),<br>
+         ev_(1.0), flicker_period_(0s),<br>
+         max_shutter_(0s), fixed_shutter_(0s), fixed_analogue_gain_(0.0)<br>
 {<br>
        memset(&awb_, 0, sizeof(awb_));<br>
        // Setting status_.total_exposure_value_ to zero initially tells us<br>
@@ -203,7 +212,7 @@ void Agc::Pause()<br>
<br>
 void Agc::Resume()<br>
 {<br>
-       fixed_shutter_ = 0;<br>
+       fixed_shutter_ = 0s;<br>
        fixed_analogue_gain_ = 0;<br>
 }<br>
<br>
@@ -224,17 +233,17 @@ void Agc::SetEv(double ev)<br>
<br>
 void Agc::SetFlickerPeriod(const Duration &flicker_period)<br>
 {<br>
-       flicker_period_ = flicker_period.get<std::micro>();<br>
+       flicker_period_ = flicker_period;<br>
 }<br>
<br>
 void Agc::SetMaxShutter(const Duration &max_shutter)<br>
 {<br>
-       max_shutter_ = max_shutter.get<std::micro>();<br>
+       max_shutter_ = max_shutter;<br>
 }<br>
<br>
 void Agc::SetFixedShutter(const Duration &fixed_shutter)<br>
 {<br>
-       fixed_shutter_ = fixed_shutter.get<std::micro>();<br>
+       fixed_shutter_ = fixed_shutter;<br>
        // Set this in case someone calls Pause() straight after.<br>
        status_.shutter_time = clipShutter(fixed_shutter_);<br>
 }<br>
@@ -266,8 +275,8 @@ void Agc::SwitchMode([[maybe_unused]] CameraMode const &camera_mode,<br>
 {<br>
        housekeepConfig();<br>
<br>
-       double fixed_shutter = clipShutter(fixed_shutter_);<br>
-       if (fixed_shutter != 0.0 && fixed_analogue_gain_ != 0.0) {<br>
+       Duration fixed_shutter = clipShutter(fixed_shutter_);<br>
+       if (fixed_shutter && fixed_analogue_gain_) {<br>
                // We're going to reset the algorithm here with these fixed values.<br>
<br>
                fetchAwbStatus(metadata);<br>
@@ -312,8 +321,8 @@ void Agc::Prepare(Metadata *image_metadata)<br>
                // Process has run, so we have meaningful values.<br>
                DeviceStatus device_status;<br>
                if (image_metadata->Get("device.status", device_status) == 0) {<br>
-                       double actual_exposure = device_status.shutter_speed *<br>
-                                                device_status.analogue_gain;<br>
+                       Duration actual_exposure = device_status.shutter_speed *<br>
+                                                  device_status.analogue_gain;<br>
                        if (actual_exposure) {<br>
                                status_.digital_gain =<br>
                                        status_.total_exposure_value /<br>
@@ -326,7 +335,8 @@ void Agc::Prepare(Metadata *image_metadata)<br>
                                        std::min(status_.digital_gain, 4.0));<br>
                                LOG(RPiAgc, Debug) << "Actual exposure " << actual_exposure;<br>
                                LOG(RPiAgc, Debug) << "Use digital_gain " << status_.digital_gain;<br>
-                               LOG(RPiAgc, Debug) << "Effective exposure " << actual_exposure * status_.digital_gain;<br>
+                               LOG(RPiAgc, Debug) << "Effective exposure "<br>
+                                                  << actual_exposure * status_.digital_gain;<br>
                                // Decide whether AEC/AGC has converged.<br>
                                updateLockStatus(device_status);<br>
                        }<br>
@@ -370,9 +380,9 @@ void Agc::updateLockStatus(DeviceStatus const &device_status)<br>
        const double RESET_MARGIN = 1.5;<br>
<br>
        // Add 200us to the exposure time error to allow for line quantisation.<br>
-       double exposure_error = last_device_status_.shutter_speed * ERROR_FACTOR + 200;<br>
+       Duration exposure_error = last_device_status_.shutter_speed * ERROR_FACTOR + 200us;<br>
        double gain_error = last_device_status_.analogue_gain * ERROR_FACTOR;<br>
-       double target_error = last_target_exposure_ * ERROR_FACTOR;<br>
+       Duration target_error = last_target_exposure_ * ERROR_FACTOR;<br>
<br>
        // Note that we don't know the exposure/gain limits of the sensor, so<br>
        // the values we keep requesting may be unachievable. For this reason<br>
@@ -462,7 +472,7 @@ void Agc::fetchCurrentExposure(Metadata *image_metadata)<br>
        current_.analogue_gain = device_status->analogue_gain;<br>
        AgcStatus *agc_status =<br>
                image_metadata->GetLocked<AgcStatus>("agc.status");<br>
-       current_.total_exposure = agc_status ? agc_status->total_exposure_value : 0;<br>
+       current_.total_exposure = agc_status ? agc_status->total_exposure_value : 0s;<br>
        current_.total_exposure_no_dg = current_.shutter * current_.analogue_gain;<br>
 }<br>
<br>
@@ -573,7 +583,7 @@ void Agc::computeGain(bcm2835_isp_stats *statistics, Metadata *image_metadata,<br>
<br>
 void Agc::computeTargetExposure(double gain)<br>
 {<br>
-       if (status_.fixed_shutter != 0.0 && status_.fixed_analogue_gain != 0.0) {<br>
+       if (status_.fixed_shutter && status_.fixed_analogue_gain) {<br>
                // When ag and shutter are both fixed, we need to drive the<br>
                // total exposure so that we end up with a digital gain of at least<br>
                // 1/min_colour_gain. Otherwise we'd desaturate channels causing<br>
@@ -588,11 +598,11 @@ void Agc::computeTargetExposure(double gain)<br>
                target_.total_exposure = current_.total_exposure_no_dg * gain;<br>
                // The final target exposure is also limited to what the exposure<br>
                // mode allows.<br>
-               double max_shutter = status_.fixed_shutter != 0.0<br>
+               Duration max_shutter = status_.fixed_shutter<br>
                                   ? status_.fixed_shutter<br>
                                   : exposure_mode_->shutter.back();<br>
                max_shutter = clipShutter(max_shutter);<br>
-               double max_total_exposure =<br>
+               Duration max_total_exposure =<br>
                        max_shutter *<br>
                        (status_.fixed_analogue_gain != 0.0<br>
                                 ? status_.fixed_analogue_gain<br>
@@ -637,7 +647,7 @@ void Agc::filterExposure(bool desaturate)<br>
        if ((status_.fixed_shutter && status_.fixed_analogue_gain) ||<br>
            frame_count_ <= config_.startup_frames)<br>
                speed = 1.0;<br>
-       if (filtered_.total_exposure == 0.0) {<br>
+       if (!filtered_.total_exposure) {<br>
                filtered_.total_exposure = target_.total_exposure;<br>
                filtered_.total_exposure_no_dg = target_.total_exposure_no_dg;<br>
        } else {<br>
@@ -674,9 +684,10 @@ void Agc::divideUpExposure()<br>
        // Sending the fixed shutter/gain cases through the same code may seem<br>
        // unnecessary, but it will make more sense when extend this to cover<br>
        // variable aperture.<br>
-       double exposure_value = filtered_.total_exposure_no_dg;<br>
-       double shutter_time, analogue_gain;<br>
-       shutter_time = status_.fixed_shutter != 0.0<br>
+       Duration exposure_value = filtered_.total_exposure_no_dg;<br>
+       Duration shutter_time;<br>
+       double analogue_gain;<br>
+       shutter_time = status_.fixed_shutter<br>
                               ? status_.fixed_shutter<br>
                               : exposure_mode_->shutter[0];<br>
        shutter_time = clipShutter(shutter_time);<br>
@@ -686,8 +697,8 @@ void Agc::divideUpExposure()<br>
        if (shutter_time * analogue_gain < exposure_value) {<br>
                for (unsigned int stage = 1;<br>
                     stage < exposure_mode_->gain.size(); stage++) {<br>
-                       if (status_.fixed_shutter == 0.0) {<br>
-                               double stage_shutter =<br>
+                       if (!status_.fixed_shutter) {<br>
+                               Duration stage_shutter =<br>
                                        clipShutter(exposure_mode_->shutter[stage]);<br>
                                if (stage_shutter * analogue_gain >=<br>
                                    exposure_value) {<br>
@@ -713,12 +724,11 @@ void Agc::divideUpExposure()<br>
                           << analogue_gain;<br>
        // Finally adjust shutter time for flicker avoidance (require both<br>
        // shutter and gain not to be fixed).<br>
-       if (status_.fixed_shutter == 0.0 &&<br>
-           status_.fixed_analogue_gain == 0.0 &&<br>
-           status_.flicker_period != 0.0) {<br>
-               int flicker_periods = shutter_time / status_.flicker_period;<br>
-               if (flicker_periods > 0) {<br>
-                       double new_shutter_time = flicker_periods * status_.flicker_period;<br>
+       if (!status_.fixed_shutter && !status_.fixed_analogue_gain &&<br>
+           status_.flicker_period) {<br>
+               double flicker_periods = shutter_time / status_.flicker_period;<br>
+               if (flicker_periods > 0.0) {<br>
+                       Duration new_shutter_time = flicker_periods * status_.flicker_period;<br>
                        analogue_gain *= shutter_time / new_shutter_time;<br>
                        // We should still not allow the ag to go over the<br>
                        // largest value in the exposure mode. Note that this<br>
@@ -738,7 +748,7 @@ void Agc::divideUpExposure()<br>
 void Agc::writeAndFinish(Metadata *image_metadata, bool desaturate)<br>
 {<br>
        status_.total_exposure_value = filtered_.total_exposure;<br>
-       status_.target_exposure_value = desaturate ? 0 : target_.total_exposure_no_dg;<br>
+       status_.target_exposure_value = desaturate ? 0s : target_.total_exposure_no_dg;<br>
        status_.shutter_time = filtered_.shutter;<br>
        status_.analogue_gain = filtered_.analogue_gain;<br>
        // Write to metadata as well, in case anyone wants to update the camera<br>
@@ -750,11 +760,12 @@ void Agc::writeAndFinish(Metadata *image_metadata, bool desaturate)<br>
                           << " analogue gain " << filtered_.analogue_gain;<br>
 }<br>
<br>
-double Agc::clipShutter(double shutter)<br>
+Duration Agc::clipShutter(const Duration &shutter)<br>
 {<br>
+       Duration clip_shutter = shutter;<br>
        if (max_shutter_)<br>
-               shutter = std::min(shutter, max_shutter_);<br>
-       return shutter;<br>
+               clip_shutter = std::min(clip_shutter, max_shutter_);<br>
+       return clip_shutter;<br>
 }<br>
<br>
 // Register algorithm with the system.<br>
diff --git a/src/ipa/raspberrypi/controller/rpi/agc.hpp b/src/ipa/raspberrypi/controller/rpi/agc.hpp<br>
index 16a65959d90e..24a6610096c2 100644<br>
--- a/src/ipa/raspberrypi/controller/rpi/agc.hpp<br>
+++ b/src/ipa/raspberrypi/controller/rpi/agc.hpp<br>
@@ -9,6 +9,8 @@<br>
 #include <vector><br>
 #include <mutex><br>
<br>
+#include "libcamera/internal/utils.h"<br>
+<br>
 #include "../agc_algorithm.hpp"<br>
 #include "../agc_status.h"<br>
 #include "../pwl.hpp"<br>
@@ -22,13 +24,15 @@<br>
<br>
 namespace RPiController {<br>
<br>
+using namespace std::literals::chrono_literals;<br>
+<br>
 struct AgcMeteringMode {<br>
        double weights[AGC_STATS_SIZE];<br>
        void Read(boost::property_tree::ptree const &params);<br>
 };<br>
<br>
 struct AgcExposureMode {<br>
-       std::vector<double> shutter;<br>
+       std::vector<Duration> shutter;<br>
        std::vector<double> gain;<br>
        void Read(boost::property_tree::ptree const &params);<br>
 };<br>
@@ -61,7 +65,7 @@ struct AgcConfig {<br>
        std::string default_exposure_mode;<br>
        std::string default_constraint_mode;<br>
        double base_ev;<br>
-       double default_exposure_time;<br>
+       Duration default_exposure_time;<br>
        double default_analogue_gain;<br>
 };<br>
<br>
@@ -101,19 +105,19 @@ private:<br>
        void filterExposure(bool desaturate);<br>
        void divideUpExposure();<br>
        void writeAndFinish(Metadata *image_metadata, bool desaturate);<br>
-       double clipShutter(double shutter);<br>
+       Duration clipShutter(const Duration &shutter);<br>
        AgcMeteringMode *metering_mode_;<br>
        AgcExposureMode *exposure_mode_;<br>
        AgcConstraintMode *constraint_mode_;<br>
        uint64_t frame_count_;<br>
        AwbStatus awb_;<br>
        struct ExposureValues {<br>
-               ExposureValues() : shutter(0), analogue_gain(0),<br>
-                                  total_exposure(0), total_exposure_no_dg(0) {}<br>
-               double shutter;<br>
+               ExposureValues() : shutter(0s), analogue_gain(0),<br>
+                                  total_exposure(0s), total_exposure_no_dg(0s) {}<br>
+               Duration shutter;<br>
                double analogue_gain;<br>
-               double total_exposure;<br>
-               double total_exposure_no_dg; // without digital gain<br>
+               Duration total_exposure;<br>
+               Duration total_exposure_no_dg; // without digital gain<br>
        };<br>
        ExposureValues current_;  // values for the current frame<br>
        ExposureValues target_;   // calculate the values we want here<br>
@@ -121,15 +125,15 @@ private:<br>
        AgcStatus status_;<br>
        int lock_count_;<br>
        DeviceStatus last_device_status_;<br>
-       double last_target_exposure_;<br>
+       Duration last_target_exposure_;<br>
        // Below here the "settings" that applications can change.<br>
        std::string metering_mode_name_;<br>
        std::string exposure_mode_name_;<br>
        std::string constraint_mode_name_;<br>
        double ev_;<br>
-       double flicker_period_;<br>
-       double max_shutter_;<br>
-       double fixed_shutter_;<br>
+       Duration flicker_period_;<br>
+       Duration max_shutter_;<br>
+       Duration fixed_shutter_;<br>
        double fixed_analogue_gain_;<br>
 };<br>
<br>
diff --git a/src/ipa/raspberrypi/controller/rpi/lux.cpp b/src/ipa/raspberrypi/controller/rpi/lux.cpp<br>
index f74381cab2b4..0fa6457c1376 100644<br>
--- a/src/ipa/raspberrypi/controller/rpi/lux.cpp<br>
+++ b/src/ipa/raspberrypi/controller/rpi/lux.cpp<br>
@@ -16,6 +16,7 @@<br>
<br>
 using namespace RPiController;<br>
 using namespace libcamera;<br>
+using namespace std::literals::chrono_literals;<br>
<br>
 LOG_DEFINE_CATEGORY(RPiLux)<br>
<br>
@@ -38,7 +39,7 @@ char const *Lux::Name() const<br>
 void Lux::Read(boost::property_tree::ptree const &params)<br>
 {<br>
        reference_shutter_speed_ =<br>
-               params.get<double>("reference_shutter_speed");<br>
+               params.get<double>("reference_shutter_speed") * 1.0us;<br>
        reference_gain_ = params.get<double>("reference_gain");<br>
        reference_aperture_ = params.get<double>("reference_aperture", 1.0);<br>
        reference_Y_ = params.get<double>("reference_Y");<br>
@@ -60,15 +61,13 @@ void Lux::Prepare(Metadata *image_metadata)<br>
 void Lux::Process(StatisticsPtr &stats, Metadata *image_metadata)<br>
 {<br>
        // set some initial values to shut the compiler up<br>
-       DeviceStatus device_status =<br>
-               { .shutter_speed = 1.0,<br>
-                 .analogue_gain = 1.0,<br>
-                 .lens_position = 0.0,<br>
-                 .aperture = 0.0,<br>
-                 .flash_intensity = 0.0 };<br>
+       DeviceStatus device_status = { .shutter_speed = 1.0ms,<br>
+                                      .analogue_gain = 1.0,<br>
+                                      .lens_position = 0.0,<br>
+                                      .aperture = 0.0,<br>
+                                      .flash_intensity = 0.0 };<br>
        if (image_metadata->Get("device.status", device_status) == 0) {<br>
                double current_gain = device_status.analogue_gain;<br>
-               double current_shutter_speed = device_status.shutter_speed;<br>
                double current_aperture = device_status.aperture;<br>
                if (current_aperture == 0)<br>
                        current_aperture = current_aperture_;<br>
@@ -83,7 +82,7 @@ void Lux::Process(StatisticsPtr &stats, Metadata *image_metadata)<br>
                double current_Y = sum / (double)num + .5;<br>
                double gain_ratio = reference_gain_ / current_gain;<br>
                double shutter_speed_ratio =<br>
-                       reference_shutter_speed_ / current_shutter_speed;<br>
+                       reference_shutter_speed_ / device_status.shutter_speed;<br>
                double aperture_ratio = reference_aperture_ / current_aperture;<br>
                double Y_ratio = current_Y * (65536 / num_bins) / reference_Y_;<br>
                double estimated_lux = shutter_speed_ratio * gain_ratio *<br>
diff --git a/src/ipa/raspberrypi/controller/rpi/lux.hpp b/src/ipa/raspberrypi/controller/rpi/lux.hpp<br>
index f9090484a136..45c844393e62 100644<br>
--- a/src/ipa/raspberrypi/controller/rpi/lux.hpp<br>
+++ b/src/ipa/raspberrypi/controller/rpi/lux.hpp<br>
@@ -8,6 +8,8 @@<br>
<br>
 #include <mutex><br>
<br>
+#include "libcamera/internal/utils.h"<br>
+<br>
 #include "../lux_status.h"<br>
 #include "../algorithm.hpp"<br>
<br>
@@ -28,7 +30,7 @@ public:<br>
 private:<br>
        // These values define the conditions of the reference image, against<br>
        // which we compare the new image.<br>
-       double reference_shutter_speed_; // in micro-seconds<br>
+       libcamera::utils::Duration reference_shutter_speed_;<br>
        double reference_gain_;<br>
        double reference_aperture_; // units of 1/f<br>
        double reference_Y_; // out of 65536<br>
diff --git a/src/ipa/raspberrypi/raspberrypi.cpp b/src/ipa/raspberrypi/raspberrypi.cpp<br>
index f8f36420b076..b77230ded591 100644<br>
--- a/src/ipa/raspberrypi/raspberrypi.cpp<br>
+++ b/src/ipa/raspberrypi/raspberrypi.cpp<br>
@@ -225,11 +225,11 @@ void IPARPi::start(const ControlList &controls, ipa::RPi::StartConfig *startConf<br>
<br>
        /* SwitchMode may supply updated exposure/gain values to use. */<br>
        AgcStatus agcStatus;<br>
-       agcStatus.shutter_time = 0.0;<br>
+       agcStatus.shutter_time = 0.0s;<br>
        agcStatus.analogue_gain = 0.0;<br>
<br>
        metadata.Get("agc.status", agcStatus);<br>
-       if (agcStatus.shutter_time != 0.0 && agcStatus.analogue_gain != 0.0) {<br>
+       if (agcStatus.shutter_time && agcStatus.analogue_gain) {<br>
                ControlList ctrls(sensorCtrls_);<br>
                applyAGC(&agcStatus, ctrls);<br>
                startConfig->controls = std::move(ctrls);<br>
@@ -392,7 +392,7 @@ int IPARPi::configure(const CameraSensorInfo &sensorInfo,<br>
                /* Supply initial values for gain and exposure. */<br>
                ControlList ctrls(sensorCtrls_);<br>
                AgcStatus agcStatus;<br>
-               agcStatus.shutter_time = DefaultExposureTime.get<std::micro>();<br>
+               agcStatus.shutter_time = DefaultExposureTime;<br>
                agcStatus.analogue_gain = DefaultAnalogueGain;<br>
                applyAGC(&agcStatus, ctrls);<br>
<br>
@@ -464,7 +464,8 @@ void IPARPi::reportMetadata()<br>
         */<br>
        DeviceStatus *deviceStatus = rpiMetadata_.GetLocked<DeviceStatus>("device.status");<br>
        if (deviceStatus) {<br>
-               libcameraMetadata_.set(controls::ExposureTime, deviceStatus->shutter_speed);<br>
+               libcameraMetadata_.set(controls::ExposureTime,<br>
+                                      deviceStatus->shutter_speed.get<std::micro>());<br>
                libcameraMetadata_.set(controls::AnalogueGain, deviceStatus->analogue_gain);<br>
        }<br>
<br>
@@ -1017,7 +1018,7 @@ void IPARPi::fillDeviceStatus(const ControlList &sensorControls)<br>
        int32_t exposureLines = sensorControls.get(V4L2_CID_EXPOSURE).get<int32_t>();<br>
        int32_t gainCode = sensorControls.get(V4L2_CID_ANALOGUE_GAIN).get<int32_t>();<br>
<br>
-       deviceStatus.shutter_speed = helper_->Exposure(exposureLines).get<std::micro>();<br>
+       deviceStatus.shutter_speed = helper_->Exposure(exposureLines);<br>
        deviceStatus.analogue_gain = helper_->Gain(gainCode);<br>
<br>
        LOG(IPARPI, Debug) << "Metadata - Exposure : "<br>
@@ -1103,7 +1104,7 @@ void IPARPi::applyAGC(const struct AgcStatus *agcStatus, ControlList &ctrls)<br>
        int32_t gainCode = helper_->GainCode(agcStatus->analogue_gain);<br>
<br>
        /* GetVBlanking might clip exposure time to the fps limits. */<br>
-       Duration exposure = agcStatus->shutter_time * 1.0us;<br>
+       Duration exposure = agcStatus->shutter_time;<br>
        int32_t vblanking = helper_->GetVBlanking(exposure, minFrameDuration_, maxFrameDuration_);<br>
        int32_t exposureLines = helper_->ExposureLines(exposure);<br>
<br>
diff --git a/src/libcamera/utils.cpp b/src/libcamera/utils.cpp<br>
index 3131aa2d6666..5fa6f8366f3d 100644<br>
--- a/src/libcamera/utils.cpp<br>
+++ b/src/libcamera/utils.cpp<br>
@@ -506,9 +506,14 @@ std::string libcameraSourcePath()<br>
  * loop, iterates over an indexed view of the \a iterable<br>
  */<br>
<br>
+/**<br>
+ * \typedef BaseDuration<br>
+ * \brief Base struct for the \a Duration helper class.<br>
+ */<br>
+<br>
 /**<br>
  * \class Duration<br>
- * \brief Helper for std::chrono::duration types. Derived from \a BaseDuration<br>
+ * \brief Helper class for std::chrono::duration types.<br>
  */<br>
<br>
 /**<br>
@@ -517,7 +522,7 @@ std::string libcameraSourcePath()<br>
  * \param[in] d The std::chrono::duration object to convert from.<br>
  *<br>
  * Constructs a \a Duration object from a \a std::chrono::duration object,<br>
- * converting the representation to \a BaseDuration type.<br>
+ * internally converting the representation to \a BaseDuration type.<br>
  */<br></blockquote><div><br></div><div>Hmmm, this does not belong in patch 4/4.  It should be in 1/4 instead.</div><div>Will fix in the next revision.  Sorry about that!</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
<br>
 /**<br>
-- <br>
2.25.1<br>
<br>
</blockquote></div></div>