From patchwork Tue Jun 8 11:03:32 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Naushir Patuck X-Patchwork-Id: 12514 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id DC17DC320B for ; Tue, 8 Jun 2021 11:03:42 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 943FF602A7; Tue, 8 Jun 2021 13:03:41 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=raspberrypi.com header.i=@raspberrypi.com header.b="Qn+rLU5w"; dkim-atps=neutral Received: from mail-wm1-x32f.google.com (mail-wm1-x32f.google.com [IPv6:2a00:1450:4864:20::32f]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 7377F602A0 for ; Tue, 8 Jun 2021 13:03:40 +0200 (CEST) Received: by mail-wm1-x32f.google.com with SMTP id f16-20020a05600c1550b02901b00c1be4abso1647893wmg.2 for ; Tue, 08 Jun 2021 04:03:40 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=raspberrypi.com; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=AP35MsAf3IUqIZd38LNp8BO/7DuGvRroW2gGEQZ1KD8=; b=Qn+rLU5wJyzEt8TpEAlDYBrJwQDt+g2cIOa1QKFSP4KVdkOBSxqyFwCg3LGTKkqhHz NBmUvyJZjjVcInP4kTH9xfWyxJ61+dU+iF0E6QAbBioLH895q2SCUCiyR7tOn4DQAXLd jprG4atOsbTRcg7bZ22h1L4rbZm+4nk7h3mRtWaMXa4iuzmau2VM3VixrnXgBtH3c0sl Sv7mZ4Ayxdghb36/m88ei00fuRSv2dtzLGWFUhGxtvxiOG9grW2axKMApb5ZSI/yyOI3 vAxoHeQO9l37HMElCW+dLTt9EHfZKXCL1ZL13b56FgCTF2wCiyWLu0tmgcOeCzHSKpcd f1xw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=AP35MsAf3IUqIZd38LNp8BO/7DuGvRroW2gGEQZ1KD8=; b=bPbOU67gJQRE1LHNvLoTBVK60q4xNo6LhQxPDKzRxEOn4WNQghlwFR7tqzqrtOyTJ3 vwDVFUwRJebcAYWZUX2yNJcJJWeCLWnM5alUXU34FkPCPlVa+HUcf5hzaDBCMOG0E251 uoUNrQqGXzym1x7hYBkXfTxTZ+H2thINxdtdEgxcIO8dIxQHLC8VmnLcci1EznoYQIPs PqN33rRx26m7HE7i1JIsSJSnWjtMoKBZYuXStYw6ZPoZ4Q3CQxyHRoMJQ3XR05tUFLsM CQQlm68vvHu2kiJxfdRbd0YsIVmFYeZBFprRVqBreoqYgoIYzS/y6IKjWT5DEqS3V+Sq vWIg== X-Gm-Message-State: AOAM532cw+wDRlHxwfgu0IQkuIlcklZEKK8dP+z6uvQYG45sCVU34WJW zic6aHsQFvqwlUdKeS5Th3a8SdUrT6fM9g== X-Google-Smtp-Source: ABdhPJzcsXxyssbz9BDJraJVWUS+DvcyVNEPbktYtosS+lTio7Xww8thnCAdgdUTogcB1hGH+lXXLw== X-Received: by 2002:a1c:b783:: with SMTP id h125mr3654115wmf.182.1623150219885; Tue, 08 Jun 2021 04:03:39 -0700 (PDT) Received: from naush-laptop.pitowers.org ([2a00:1098:3142:14:5aef:f665:51b0:d8c0]) by smtp.gmail.com with ESMTPSA id o129sm2639350wmo.22.2021.06.08.04.03.39 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 08 Jun 2021 04:03:39 -0700 (PDT) From: Naushir Patuck To: libcamera-devel@lists.libcamera.org Date: Tue, 8 Jun 2021 12:03:32 +0100 Message-Id: <20210608110335.4078551-2-naush@raspberrypi.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20210608110335.4078551-1-naush@raspberrypi.com> References: <20210608110335.4078551-1-naush@raspberrypi.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v6 1/4] libcamera: utils: Add helper class for std::chrono::duration X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" 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 defined to help with displaying utils::Duration value in stream objects. Currently, this will display the duration value in microseconds. Signed-off-by: Naushir Patuck Reviewed-by: David Plowman Reviewed-by: Jacopo Mondi Reviewed-by: Laurent Pinchart --- include/libcamera/internal/utils.h | 32 +++++++++++++++++ src/libcamera/utils.cpp | 58 ++++++++++++++++++++++++++++++ test/utils.cpp | 45 +++++++++++++++++++++++ 3 files changed, 135 insertions(+) diff --git a/include/libcamera/internal/utils.h b/include/libcamera/internal/utils.h index 83dada7cc16c..15beb0f44172 100644 --- a/include/libcamera/internal/utils.h +++ b/include/libcamera/internal/utils.h @@ -316,8 +316,40 @@ auto enumerate(T (&iterable)[N]) -> details::enumerate_adapter } #endif +class Duration : public std::chrono::duration +{ + using BaseDuration = std::chrono::duration; + +public: + Duration() = default; + + template + constexpr Duration(const std::chrono::duration &d) + : BaseDuration(d) + { + } + + template + double get() const + { + auto const c = std::chrono::duration_cast>(*this); + return c.count(); + } + + explicit constexpr operator bool() const + { + return *this != BaseDuration::zero(); + } +}; + } /* namespace utils */ +#ifndef __DOXYGEN__ +template +std::basic_ostream &operator<<(std::basic_ostream &os, + const utils::Duration &d); +#endif + } /* namespace libcamera */ #endif /* __LIBCAMERA_INTERNAL_UTILS_H__ */ diff --git a/src/libcamera/utils.cpp b/src/libcamera/utils.cpp index 826144d3c837..2e7d35fb1173 100644 --- a/src/libcamera/utils.cpp +++ b/src/libcamera/utils.cpp @@ -506,6 +506,64 @@ std::string libcameraSourcePath() * loop, iterates over an indexed view of the \a iterable */ +/** + * \class Duration + * \brief Helper class from std::chrono::duration that represents a time + * duration in nanoseconds with double precision + */ + +/** + * \fn Duration::Duration(const std::chrono::duration &d) + * \brief Construct a Duration by converting an arbitrary std::chrono::duration + * \param[in] d The std::chrono::duration object to convert from + * + * The constructed \a Duration object is internally represented in double + * precision with nanoseconds ticks. + */ + +/** + * \fn Duration::get() + * \brief Retrieve the tick count, converted 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(); + * \endcode + * + * \return 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 + */ + } /* namespace utils */ +#ifndef __DOXYGEN__ +template +std::basic_ostream &operator<<(std::basic_ostream &os, + const utils::Duration &d) +{ + std::basic_ostringstream 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() << "us"; + return os << s.str(); +} + +template +std::basic_ostream> & +operator<< >(std::basic_ostream> &os, + const utils::Duration &d); +#endif + } /* namespace libcamera */ diff --git a/test/utils.cpp b/test/utils.cpp index 7e24c71e4775..f170ae4c2f35 100644 --- a/test/utils.cpp +++ b/test/utils.cpp @@ -20,6 +20,7 @@ using namespace std; using namespace libcamera; +using namespace std::literals::chrono_literals; class UtilsTest : public Test { @@ -128,6 +129,46 @@ protected: return TestPass; } + int testDuration() + { + std::ostringstream os; + utils::Duration exposure; + double ratio; + + exposure = 25ms + 25ms; + if (exposure.get() != 50000.0) { + cerr << "utils::Duration failed to return microsecond count"; + return TestFail; + } + + exposure = 1.0s / 4; + if (exposure != 250ms) { + cerr << "utils::Duration failed scalar divide test"; + return TestFail; + } + + exposure = 5000.5us; + if (!exposure) { + cerr << "utils::Duration failed boolean test"; + return TestFail; + } + + os << exposure; + if (os.str() != "5000.50us") { + cerr << "utils::Duration operator << failed"; + return TestFail; + } + + exposure = 100ms; + ratio = exposure / 25ms; + if (ratio != 4.0) { + cerr << "utils::Duration failed ratio test"; + return TestFail; + } + + return TestPass; + } + int run() { /* utils::hex() test. */ @@ -236,6 +277,10 @@ protected: if (testEnumerate() != TestPass) return TestFail; + /* utils::Duration test. */ + if (testDuration() != TestPass) + return TestFail; + return TestPass; } }; From patchwork Tue Jun 8 11:03:33 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Naushir Patuck X-Patchwork-Id: 12516 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 97380BD22E for ; Tue, 8 Jun 2021 11:03:46 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 599F96892E; Tue, 8 Jun 2021 13:03:46 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=raspberrypi.com header.i=@raspberrypi.com header.b="CeE0ZA1F"; dkim-atps=neutral Received: from mail-wr1-x433.google.com (mail-wr1-x433.google.com [IPv6:2a00:1450:4864:20::433]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id C584168930 for ; Tue, 8 Jun 2021 13:03:41 +0200 (CEST) Received: by mail-wr1-x433.google.com with SMTP id r9so4394908wrz.10 for ; Tue, 08 Jun 2021 04:03:41 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=raspberrypi.com; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=yME5FCrCg19JnvRxSTvNgwfjfmnjYTDgYXpLunm2vv8=; b=CeE0ZA1FguQ6qXS0F/V3odVMFn1icFYvUccLM/s45oupHN+aLljVdIGGqwc/TPkmgd C4IRvtvUU8oB94kvNHQso1KzJirz8SPM/yQgH/NxKw4pMzQ0vIHS5CE3wrLZ/DSFEMVz oMlzQ1rctOzdIBRlSMBAHi2Ag0KU9acMy+rjSBTKooDNwjou4kXFSAdq8WY5BrmplZFl 2GQZIWbIC24yWMKKmFoh8bEhvTox99cL/knYHjAadL2d7w40lAIlCufQKG7UbjipYqqz bPp/3qrc7RanLonW16r1Rf2zqDFhcgIhmuwDULp5HGGfA7b2q1IadllKrNvSZwMaLssB E17g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=yME5FCrCg19JnvRxSTvNgwfjfmnjYTDgYXpLunm2vv8=; b=Qw0FgCi/CRB0R6EOBeEoXlvcKZo1QUWRydBEKnOQE02YBqfiADMeCpyU0qZSxGUGIU cbX8LcOHL1Hid6dtuekVCvxBtmvsPLJZwnWWEM5B29QPB/wqeQKHMK8wNm+1e1sj0O7I IQ1b5meev7cSWNQ6Wgc+7bStFku9rKnZeAsH4Tmjxjeq0U2LaptunXRVhBK15diSHj5v 0Rbxt+CNMEqjRj431QPkt/z8/G2bpHflCns4oHsapQ3xY2iCtWNT1SD3lUHZysSDiUfV hBaROYnNfVXj5wt+mh2H9FGKca2HCzmJk/8UXgeDG12asR47LdaxuTP0n9BNJNKaiPis vOcg== X-Gm-Message-State: AOAM533gJh1LPhMa5v7gAB3ttrmEZyeP/jsJeQwV6WifhXcTPAGbH8A9 I0STtRUaOKN8wQMOOnULPCda2AdtkaGPLg== X-Google-Smtp-Source: ABdhPJyA5WPC4XhgUz2IvCdTjIYWAfB2aYGe5yuR/r8v/db8YHADkLrY4OL39w6Y7JszIWIIOR1jrw== X-Received: by 2002:a05:6000:551:: with SMTP id b17mr21855722wrf.32.1623150220749; Tue, 08 Jun 2021 04:03:40 -0700 (PDT) Received: from naush-laptop.pitowers.org ([2a00:1098:3142:14:5aef:f665:51b0:d8c0]) by smtp.gmail.com with ESMTPSA id o129sm2639350wmo.22.2021.06.08.04.03.39 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 08 Jun 2021 04:03:40 -0700 (PDT) From: Naushir Patuck To: libcamera-devel@lists.libcamera.org Date: Tue, 8 Jun 2021 12:03:33 +0100 Message-Id: <20210608110335.4078551-3-naush@raspberrypi.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20210608110335.4078551-1-naush@raspberrypi.com> References: <20210608110335.4078551-1-naush@raspberrypi.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v6 2/4] ipa: raspberrypi: Switch ipa and cam_helper to use utils::Duration X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Switch the ipa and cam_helper code to use libcamera::utils::Duration for all time based variables. This improves code readability and avoids possible errors when converting between time bases. Signed-off-by: Naushir Patuck Reviewed-by: David Plowman Reviewed-by: Laurent Pinchart Reviewed-by: Jacopo Mondi --- src/ipa/raspberrypi/cam_helper.cpp | 20 ++++--- src/ipa/raspberrypi/cam_helper.hpp | 10 ++-- src/ipa/raspberrypi/controller/camera_mode.h | 6 +- src/ipa/raspberrypi/raspberrypi.cpp | 62 ++++++++++---------- 4 files changed, 53 insertions(+), 45 deletions(-) diff --git a/src/ipa/raspberrypi/cam_helper.cpp b/src/ipa/raspberrypi/cam_helper.cpp index 09917f3cc079..92a38007f038 100644 --- a/src/ipa/raspberrypi/cam_helper.cpp +++ b/src/ipa/raspberrypi/cam_helper.cpp @@ -18,6 +18,7 @@ using namespace RPiController; using namespace libcamera; +using libcamera::utils::Duration; namespace libcamera { LOG_DECLARE_CATEGORY(IPARPI) @@ -61,20 +62,21 @@ void CamHelper::Process([[maybe_unused]] StatisticsPtr &stats, { } -uint32_t CamHelper::ExposureLines(double exposure_us) const +uint32_t CamHelper::ExposureLines(const Duration exposure) const { assert(initialized_); - return exposure_us * 1000.0 / mode_.line_length; + return exposure / mode_.line_length; } -double CamHelper::Exposure(uint32_t exposure_lines) const +Duration CamHelper::Exposure(uint32_t exposure_lines) const { assert(initialized_); - return exposure_lines * mode_.line_length / 1000.0; + return exposure_lines * mode_.line_length; } -uint32_t CamHelper::GetVBlanking(double &exposure, double minFrameDuration, - double maxFrameDuration) const +uint32_t CamHelper::GetVBlanking(Duration &exposure, + Duration minFrameDuration, + Duration maxFrameDuration) const { uint32_t frameLengthMin, frameLengthMax, vblank; uint32_t exposureLines = ExposureLines(exposure); @@ -85,8 +87,8 @@ uint32_t CamHelper::GetVBlanking(double &exposure, double minFrameDuration, * minFrameDuration and maxFrameDuration are clamped by the caller * based on the limits for the active sensor mode. */ - frameLengthMin = 1e3 * minFrameDuration / mode_.line_length; - frameLengthMax = 1e3 * maxFrameDuration / mode_.line_length; + frameLengthMin = minFrameDuration / mode_.line_length; + frameLengthMax = maxFrameDuration / mode_.line_length; /* * Limit the exposure to the maximum frame duration requested, and @@ -182,7 +184,7 @@ void CamHelper::parseEmbeddedData(Span buffer, return; } - deviceStatus.shutter_speed = Exposure(exposureLines); + deviceStatus.shutter_speed = Exposure(exposureLines).get(); deviceStatus.analogue_gain = Gain(gainCode); LOG(IPARPI, Debug) << "Metadata updated - Exposure : " diff --git a/src/ipa/raspberrypi/cam_helper.hpp b/src/ipa/raspberrypi/cam_helper.hpp index a52f3f0b583c..f53f5c39b01c 100644 --- a/src/ipa/raspberrypi/cam_helper.hpp +++ b/src/ipa/raspberrypi/cam_helper.hpp @@ -15,6 +15,7 @@ #include "controller/metadata.hpp" #include "md_parser.hpp" +#include "libcamera/internal/utils.h" #include "libcamera/internal/v4l2_videodevice.h" namespace RPiController { @@ -72,10 +73,11 @@ public: virtual void Prepare(libcamera::Span buffer, Metadata &metadata); virtual void Process(StatisticsPtr &stats, Metadata &metadata); - uint32_t ExposureLines(double exposure_us) const; - double Exposure(uint32_t exposure_lines) const; // in us - virtual uint32_t GetVBlanking(double &exposure_us, double minFrameDuration, - double maxFrameDuration) const; + uint32_t ExposureLines(libcamera::utils::Duration exposure) const; + libcamera::utils::Duration Exposure(uint32_t exposure_lines) const; + virtual uint32_t GetVBlanking(libcamera::utils::Duration &exposure, + libcamera::utils::Duration minFrameDuration, + libcamera::utils::Duration maxFrameDuration) const; virtual uint32_t GainCode(double gain) const = 0; virtual double Gain(uint32_t gain_code) const = 0; virtual void GetDelays(int &exposure_delay, int &gain_delay, diff --git a/src/ipa/raspberrypi/controller/camera_mode.h b/src/ipa/raspberrypi/controller/camera_mode.h index 256438c931d9..2aa2335dcf90 100644 --- a/src/ipa/raspberrypi/controller/camera_mode.h +++ b/src/ipa/raspberrypi/controller/camera_mode.h @@ -8,6 +8,8 @@ #include +#include "libcamera/internal/utils.h" + // Description of a "camera mode", holding enough information for control // algorithms to adapt their behaviour to the different modes of the camera, // including binning, scaling, cropping etc. @@ -33,8 +35,8 @@ struct CameraMode { double scale_x, scale_y; // scaling of the noise compared to the native sensor mode double noise_factor; - // line time in nanoseconds - double line_length; + // line time + libcamera::utils::Duration line_length; // any camera transform *not* reflected already in the camera tuning libcamera::Transform transform; // minimum and maximum fame lengths in units of lines diff --git a/src/ipa/raspberrypi/raspberrypi.cpp b/src/ipa/raspberrypi/raspberrypi.cpp index 0c4752ecffb6..33b316b90d16 100644 --- a/src/ipa/raspberrypi/raspberrypi.cpp +++ b/src/ipa/raspberrypi/raspberrypi.cpp @@ -54,19 +54,22 @@ namespace libcamera { +using namespace std::literals::chrono_literals; +using utils::Duration; + /* Configure the sensor with these values initially. */ constexpr double DefaultAnalogueGain = 1.0; -constexpr unsigned int DefaultExposureTime = 20000; -constexpr double defaultMinFrameDuration = 1e6 / 30.0; -constexpr double defaultMaxFrameDuration = 1e6 / 0.01; +constexpr Duration DefaultExposureTime = 20.0ms; +constexpr Duration defaultMinFrameDuration = 1.0s / 30.0; +constexpr Duration defaultMaxFrameDuration = 100.0s; /* - * Determine the minimum allowable inter-frame duration (in us) to run the - * controller algorithms. If the pipeline handler provider frames at a rate - * higher than this, we rate-limit the controller Prepare() and Process() calls - * to lower than or equal to this rate. + * Determine the minimum allowable inter-frame duration to run the controller + * algorithms. If the pipeline handler provider frames at a rate higher than this, + * we rate-limit the controller Prepare() and Process() calls to lower than or + * equal to this rate. */ -constexpr double controllerMinFrameDuration = 1e6 / 60.0; +constexpr Duration controllerMinFrameDuration = 1.0s / 60.0; LOG_DEFINE_CATEGORY(IPARPI) @@ -110,7 +113,7 @@ private: void reportMetadata(); void fillDeviceStatus(const ControlList &sensorControls); void processStats(unsigned int bufferId); - void applyFrameDurations(double minFrameDuration, double maxFrameDuration); + void applyFrameDurations(Duration minFrameDuration, Duration maxFrameDuration); void applyAGC(const struct AgcStatus *agcStatus, ControlList &ctrls); void applyAWB(const struct AwbStatus *awbStatus, ControlList &ctrls); void applyDG(const struct AgcStatus *dgStatus, ControlList &ctrls); @@ -166,9 +169,9 @@ private: /* Distinguish the first camera start from others. */ bool firstStart_; - /* Frame duration (1/fps) limits, given in microseconds. */ - double minFrameDuration_; - double maxFrameDuration_; + /* Frame duration (1/fps) limits. */ + Duration minFrameDuration_; + Duration maxFrameDuration_; }; int IPARPi::init(const IPASettings &settings, ipa::RPi::SensorConfig *sensorConfig) @@ -310,10 +313,10 @@ void IPARPi::setMode(const IPACameraSensorInfo &sensorInfo) mode_.noise_factor = sqrt(mode_.bin_x * mode_.bin_y); /* - * Calculate the line length in nanoseconds as the ratio between - * the line length in pixels and the pixel rate. + * Calculate the line length as the ratio between the line length in + * pixels and the pixel rate. */ - mode_.line_length = 1e9 * sensorInfo.lineLength / sensorInfo.pixelRate; + mode_.line_length = sensorInfo.lineLength * (1.0s / sensorInfo.pixelRate); /* * Set the frame length limits for the mode to ensure exposure and @@ -386,7 +389,7 @@ int IPARPi::configure(const IPACameraSensorInfo &sensorInfo, /* Supply initial values for gain and exposure. */ ControlList ctrls(sensorCtrls_); AgcStatus agcStatus; - agcStatus.shutter_time = DefaultExposureTime; + agcStatus.shutter_time = DefaultExposureTime.get(); agcStatus.analogue_gain = DefaultAnalogueGain; applyAGC(&agcStatus, ctrls); @@ -861,7 +864,7 @@ void IPARPi::queueRequest(const ControlList &controls) case controls::FRAME_DURATION_LIMITS: { auto frameDurations = ctrl.second.get>(); - applyFrameDurations(frameDurations[0], frameDurations[1]); + applyFrameDurations(frameDurations[0] * 1.0us, frameDurations[1] * 1.0us); break; } @@ -936,9 +939,9 @@ void IPARPi::prepareISP(const ipa::RPi::ISPConfig &data) returnEmbeddedBuffer(data.embeddedBufferId); /* Allow a 10% margin on the comparison below. */ - constexpr double eps = controllerMinFrameDuration * 1e3 * 0.1; + Duration delta = (frameTimestamp - lastRunTimestamp_) * 1.0ns; if (lastRunTimestamp_ && frameCount_ > dropFrameCount_ && - frameTimestamp - lastRunTimestamp_ + eps < controllerMinFrameDuration * 1e3) { + delta < controllerMinFrameDuration * 0.9) { /* * Ensure we merge the previous frame's metadata with the current * frame. This will not overwrite exposure/gain values for the @@ -1011,7 +1014,7 @@ void IPARPi::fillDeviceStatus(const ControlList &sensorControls) int32_t exposureLines = sensorControls.get(V4L2_CID_EXPOSURE).get(); int32_t gainCode = sensorControls.get(V4L2_CID_ANALOGUE_GAIN).get(); - deviceStatus.shutter_speed = helper_->Exposure(exposureLines); + deviceStatus.shutter_speed = helper_->Exposure(exposureLines).get(); deviceStatus.analogue_gain = helper_->Gain(gainCode); LOG(IPARPI, Debug) << "Metadata - Exposure : " @@ -1056,10 +1059,10 @@ void IPARPi::applyAWB(const struct AwbStatus *awbStatus, ControlList &ctrls) static_cast(awbStatus->gain_b * 1000)); } -void IPARPi::applyFrameDurations(double minFrameDuration, double maxFrameDuration) +void IPARPi::applyFrameDurations(Duration minFrameDuration, Duration maxFrameDuration) { - const double minSensorFrameDuration = 1e-3 * mode_.min_frame_length * mode_.line_length; - const double maxSensorFrameDuration = 1e-3 * mode_.max_frame_length * mode_.line_length; + const Duration minSensorFrameDuration = mode_.min_frame_length * mode_.line_length; + const Duration maxSensorFrameDuration = mode_.max_frame_length * mode_.line_length; /* * This will only be applied once AGC recalculations occur. @@ -1075,20 +1078,20 @@ void IPARPi::applyFrameDurations(double minFrameDuration, double maxFrameDuratio /* Return the validated limits via metadata. */ libcameraMetadata_.set(controls::FrameDurationLimits, - { static_cast(minFrameDuration_), - static_cast(maxFrameDuration_) }); + { static_cast(minFrameDuration_.get()), + static_cast(maxFrameDuration_.get()) }); /* * Calculate the maximum exposure time possible for the AGC to use. * GetVBlanking() will update maxShutter with the largest exposure * value possible. */ - double maxShutter = std::numeric_limits::max(); + Duration maxShutter = Duration::max(); helper_->GetVBlanking(maxShutter, minFrameDuration_, maxFrameDuration_); RPiController::AgcAlgorithm *agc = dynamic_cast( controller_.GetAlgorithm("agc")); - agc->SetMaxShutter(maxShutter); + agc->SetMaxShutter(maxShutter.get()); } void IPARPi::applyAGC(const struct AgcStatus *agcStatus, ControlList &ctrls) @@ -1096,9 +1099,8 @@ void IPARPi::applyAGC(const struct AgcStatus *agcStatus, ControlList &ctrls) int32_t gainCode = helper_->GainCode(agcStatus->analogue_gain); /* GetVBlanking might clip exposure time to the fps limits. */ - double exposure = agcStatus->shutter_time; - int32_t vblanking = helper_->GetVBlanking(exposure, minFrameDuration_, - maxFrameDuration_); + Duration exposure = agcStatus->shutter_time * 1.0us; + int32_t vblanking = helper_->GetVBlanking(exposure, minFrameDuration_, maxFrameDuration_); int32_t exposureLines = helper_->ExposureLines(exposure); LOG(IPARPI, Debug) << "Applying AGC Exposure: " << exposure From patchwork Tue Jun 8 11:03:34 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Naushir Patuck X-Patchwork-Id: 12515 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 45F3BBD22E for ; Tue, 8 Jun 2021 11:03:44 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id F2EEB68930; Tue, 8 Jun 2021 13:03:43 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=raspberrypi.com header.i=@raspberrypi.com header.b="pK0SKqnl"; dkim-atps=neutral Received: from mail-wm1-x32e.google.com (mail-wm1-x32e.google.com [IPv6:2a00:1450:4864:20::32e]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 0C7586892E for ; Tue, 8 Jun 2021 13:03:42 +0200 (CEST) Received: by mail-wm1-x32e.google.com with SMTP id l11-20020a05600c4f0bb029017a7cd488f5so1591063wmq.0 for ; Tue, 08 Jun 2021 04:03:42 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=raspberrypi.com; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=fdrC8KEhavjfv2hqPb4hbHb8dTYssUbrYPASaoaD9ks=; b=pK0SKqnlMaXubRXJ/EvUJnbYN39g4pHcc2nkBOrMYLz3anynuYd5wznnAUZUKHnGlr Ej8eWogJTGCZr/fDKPetH10hZQ126XpgiD9Cfq2uvNy5Bwj9G5QCpkdpl4yzZC4yxCg4 f8HjiKCnNAxRZSyWi241yfDcrrd+vyu0tW1abyqcII8qO80l4BgemqBUXI1GuYFYubp/ W4ejXllNA0rtMae5CKCzIoW/mhX8x06868+yng5FzK+N9/gHmB0QUIxq5MTaTYBpoVvN qSFHODUGsI6/vqwJUd7FPjDPFsVgjnvWj6wBiKNykE4ry3Nq8Skh2yt8HUBkbeUlLqQa RczQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=fdrC8KEhavjfv2hqPb4hbHb8dTYssUbrYPASaoaD9ks=; b=S1L1Ic7WFv1df6WDZIPtbhw81RK9JwuKaWOsrJslsYehtc2i3WHqybxsMZcfczxGHC eEMEGyohvPoJkC5e1gNBpQMXDX7MejWkA9rHc9y+2PvnGjIMV1OaPRLlQJLyw/WXOAmX 4Twt5D3WBU370+qjsIHe74Cvolk0C2q/OtpkGEepa/UTd/oOUq/AD1k2rjofJQ7N+w44 jgGblSTaQlyd4mrJ69S8jSq0WiYlAW5GXyIAjboQKZvzzfIwk2o08Y8MEjb2ylKdM0Hv 9qyUhU/tbb70zuU9O7evFLgFde/dOdrfWW41oZC8+jFLTbdgN7Z4vqz9GtfDAegFd1Am aV9g== X-Gm-Message-State: AOAM531CFz4CAe/o+59DhLKsuRfS9Pe61HuANPWwruB059J3GJFzwJXG JFmD8IVm7PkyZ7qkrRmi1VGuqDRGXZGwOw== X-Google-Smtp-Source: ABdhPJxw2yF6qFBpgMzw3YRvW/O5Ix+IBEk5trWaExYMoAAxY1Dhk7aWt7RgNap2khdbTWF9/j3iuw== X-Received: by 2002:a7b:c414:: with SMTP id k20mr21717777wmi.90.1623150221378; Tue, 08 Jun 2021 04:03:41 -0700 (PDT) Received: from naush-laptop.pitowers.org ([2a00:1098:3142:14:5aef:f665:51b0:d8c0]) by smtp.gmail.com with ESMTPSA id o129sm2639350wmo.22.2021.06.08.04.03.40 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 08 Jun 2021 04:03:41 -0700 (PDT) From: Naushir Patuck To: libcamera-devel@lists.libcamera.org Date: Tue, 8 Jun 2021 12:03:34 +0100 Message-Id: <20210608110335.4078551-4-naush@raspberrypi.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20210608110335.4078551-1-naush@raspberrypi.com> References: <20210608110335.4078551-1-naush@raspberrypi.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v6 3/4] ipa: raspberrypi: Switch AgcAlgorithm API to use utils::Duration X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Switch the AgcAlgorithm API functions to use utils::Duration for all time based variables. Signed-off-by: Naushir Patuck Reviewed-by: David Plowman Reviewed-by: Jacopo Mondi Reviewed-by: Laurent Pinchart --- src/ipa/raspberrypi/controller/agc_algorithm.hpp | 7 ++++--- src/ipa/raspberrypi/controller/rpi/agc.cpp | 13 +++++++------ src/ipa/raspberrypi/controller/rpi/agc.hpp | 6 +++--- src/ipa/raspberrypi/raspberrypi.cpp | 6 +++--- 4 files changed, 17 insertions(+), 15 deletions(-) diff --git a/src/ipa/raspberrypi/controller/agc_algorithm.hpp b/src/ipa/raspberrypi/controller/agc_algorithm.hpp index f97deb0fca59..134bbcda50ce 100644 --- a/src/ipa/raspberrypi/controller/agc_algorithm.hpp +++ b/src/ipa/raspberrypi/controller/agc_algorithm.hpp @@ -6,6 +6,7 @@ */ #pragma once +#include "libcamera/internal/utils.h" #include "algorithm.hpp" namespace RPiController { @@ -17,9 +18,9 @@ public: // An AGC algorithm must provide the following: virtual unsigned int GetConvergenceFrames() const = 0; virtual void SetEv(double ev) = 0; - virtual void SetFlickerPeriod(double flicker_period) = 0; - virtual void SetFixedShutter(double fixed_shutter) = 0; // microseconds - virtual void SetMaxShutter(double max_shutter) = 0; // microseconds + virtual void SetFlickerPeriod(libcamera::utils::Duration flicker_period) = 0; + virtual void SetFixedShutter(libcamera::utils::Duration fixed_shutter) = 0; + virtual void SetMaxShutter(libcamera::utils::Duration max_shutter) = 0; virtual void SetFixedAnalogueGain(double fixed_analogue_gain) = 0; virtual void SetMeteringMode(std::string const &metering_mode_name) = 0; virtual void SetExposureMode(std::string const &exposure_mode_name) = 0; diff --git a/src/ipa/raspberrypi/controller/rpi/agc.cpp b/src/ipa/raspberrypi/controller/rpi/agc.cpp index f4cd5d26fb4e..fd3359a77958 100644 --- a/src/ipa/raspberrypi/controller/rpi/agc.cpp +++ b/src/ipa/raspberrypi/controller/rpi/agc.cpp @@ -21,6 +21,7 @@ using namespace RPiController; using namespace libcamera; +using libcamera::utils::Duration; LOG_DEFINE_CATEGORY(RPiAgc) @@ -222,19 +223,19 @@ void Agc::SetEv(double ev) ev_ = ev; } -void Agc::SetFlickerPeriod(double flicker_period) +void Agc::SetFlickerPeriod(Duration flicker_period) { - flicker_period_ = flicker_period; + flicker_period_ = flicker_period.get(); } -void Agc::SetMaxShutter(double max_shutter) +void Agc::SetMaxShutter(Duration max_shutter) { - max_shutter_ = max_shutter; + max_shutter_ = max_shutter.get(); } -void Agc::SetFixedShutter(double fixed_shutter) +void Agc::SetFixedShutter(Duration fixed_shutter) { - fixed_shutter_ = fixed_shutter; + fixed_shutter_ = fixed_shutter.get(); // Set this in case someone calls Pause() straight after. status_.shutter_time = clipShutter(fixed_shutter_); } diff --git a/src/ipa/raspberrypi/controller/rpi/agc.hpp b/src/ipa/raspberrypi/controller/rpi/agc.hpp index 0427fb59ec1b..b52aaa23d51c 100644 --- a/src/ipa/raspberrypi/controller/rpi/agc.hpp +++ b/src/ipa/raspberrypi/controller/rpi/agc.hpp @@ -77,9 +77,9 @@ public: void Resume() override; unsigned int GetConvergenceFrames() const override; void SetEv(double ev) override; - void SetFlickerPeriod(double flicker_period) override; - void SetMaxShutter(double max_shutter) override; // microseconds - void SetFixedShutter(double fixed_shutter) override; // microseconds + void SetFlickerPeriod(libcamera::utils::Duration flicker_period) override; + void SetMaxShutter(libcamera::utils::Duration max_shutter) override; + void SetFixedShutter(libcamera::utils::Duration fixed_shutter) override; void SetFixedAnalogueGain(double fixed_analogue_gain) override; void SetMeteringMode(std::string const &metering_mode_name) override; void SetExposureMode(std::string const &exposure_mode_name) override; diff --git a/src/ipa/raspberrypi/raspberrypi.cpp b/src/ipa/raspberrypi/raspberrypi.cpp index 33b316b90d16..4aa8ccaa985a 100644 --- a/src/ipa/raspberrypi/raspberrypi.cpp +++ b/src/ipa/raspberrypi/raspberrypi.cpp @@ -638,8 +638,8 @@ void IPARPi::queueRequest(const ControlList &controls) break; } - /* This expects units of micro-seconds. */ - agc->SetFixedShutter(ctrl.second.get()); + /* The control provides units of microseconds. */ + agc->SetFixedShutter(ctrl.second.get() * 1.0us); libcameraMetadata_.set(controls::ExposureTime, ctrl.second.get()); break; @@ -1091,7 +1091,7 @@ void IPARPi::applyFrameDurations(Duration minFrameDuration, Duration maxFrameDur RPiController::AgcAlgorithm *agc = dynamic_cast( controller_.GetAlgorithm("agc")); - agc->SetMaxShutter(maxShutter.get()); + agc->SetMaxShutter(maxShutter); } void IPARPi::applyAGC(const struct AgcStatus *agcStatus, ControlList &ctrls) From patchwork Tue Jun 8 11:03:35 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Naushir Patuck X-Patchwork-Id: 12517 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 15646C320B for ; Tue, 8 Jun 2021 11:03:47 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id BD933602A7; Tue, 8 Jun 2021 13:03:46 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=raspberrypi.com header.i=@raspberrypi.com header.b="Hcd5BWqO"; dkim-atps=neutral Received: from mail-wr1-x435.google.com (mail-wr1-x435.google.com [IPv6:2a00:1450:4864:20::435]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 05554602A1 for ; Tue, 8 Jun 2021 13:03:43 +0200 (CEST) Received: by mail-wr1-x435.google.com with SMTP id o3so2707753wri.8 for ; Tue, 08 Jun 2021 04:03:43 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=raspberrypi.com; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=P+wTgJIbLFrWSOSfwmz0uzLj+lv+deYhjU5AKmwm3M8=; b=Hcd5BWqOjylP2MNtXePGcTnuTaOv1In3XQvRIwQ3kftlz2l5CF3zpLnEleSrEF/uLa dqNsXXs1dCXsKTlvV7lPGo3hSH1BoFBvuhDNcztmS+f34VPzZBU+ZljXaZI4iU6DUCPd sv/x/ppfCkCaQZUyICwhtLuySI0tmAz9kAkZqcTgSAwYHOwvUgWXY7jCoEX885Q8GE53 sUj7aQusMV6i0ExYs1VV5+b6QHhISCVOp9dLjwjKNyb3NH1nc8sI3+E1oJxUTjmX/gTa 6FcxCFQ34eYwntcgzJ14Otep/VPFTZiFC2oaFJLgzrjnJ70u0kGNsDonVixgsXxLli1/ IrsA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=P+wTgJIbLFrWSOSfwmz0uzLj+lv+deYhjU5AKmwm3M8=; b=cOB5OcWkx87nYAzR3LrNGiQKlor0nnyjLUFWGxxCeuZV+FspyVCTo6IYynFvVDU6cm 5sk4r9+EgBQnyT/IfCDl4CZtjot+LI75O/Ct97hUy5vx5Ck8CaHmbqrXg1HBXULJK1b5 z27KfmiE6kTlFfrNJZ61y7v5Sco+1pPEV0aiVfcBc5yAX9akKdf316ob9fVGQIDDSVIX X7ukwW9dP13ixuKocLXVoKN3CblKoaFzM2Gt3S+1MHpxplSxPkFsbuhQVEiZCPiA2eMn dc8uG3XobzKybN0Wmnbrrauxh3VtwFIXhAYHhLHa7PD4yvHhJOOTL3/HGFOSL6sDocXX SXWw== X-Gm-Message-State: AOAM531kaeOrQdKMKpp3EOrFDoc3jZAyDVBHoP1+OePE0D91SopLOzQ4 yKVccYwpWPzyia11S5LDHeQXksbaWbNGtQ== X-Google-Smtp-Source: ABdhPJz4tbycgOA8QTg734yYABOSdsA5MGKYcARxY6fgPHv01Nnwrce2NUk5Wzz8MLc+pWZaZaVBRg== X-Received: by 2002:a05:6000:110e:: with SMTP id z14mr22476434wrw.235.1623150222228; Tue, 08 Jun 2021 04:03:42 -0700 (PDT) Received: from naush-laptop.pitowers.org ([2a00:1098:3142:14:5aef:f665:51b0:d8c0]) by smtp.gmail.com with ESMTPSA id o129sm2639350wmo.22.2021.06.08.04.03.41 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 08 Jun 2021 04:03:41 -0700 (PDT) From: Naushir Patuck To: libcamera-devel@lists.libcamera.org Date: Tue, 8 Jun 2021 12:03:35 +0100 Message-Id: <20210608110335.4078551-5-naush@raspberrypi.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20210608110335.4078551-1-naush@raspberrypi.com> References: <20210608110335.4078551-1-naush@raspberrypi.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v6 4/4] ipa: raspberrypi: Switch the AGC/Lux code to use utils::Duration X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Convert the core AGC and Lux controller code to use utils::Duration for all exposure time related variables and calculations. Convert the exposure/shutter time fields in AgcStatus and DeviceStatus to use utils::Duration. Signed-off-by: Naushir Patuck Reviewed-by: David Plowman Reviewed-by: Jacopo Mondi --- src/ipa/raspberrypi/cam_helper.cpp | 2 +- src/ipa/raspberrypi/controller/agc_status.h | 12 +-- .../raspberrypi/controller/device_status.h | 6 +- src/ipa/raspberrypi/controller/rpi/agc.cpp | 82 ++++++++++--------- src/ipa/raspberrypi/controller/rpi/agc.hpp | 28 ++++--- src/ipa/raspberrypi/controller/rpi/lux.cpp | 19 +++-- src/ipa/raspberrypi/controller/rpi/lux.hpp | 4 +- src/ipa/raspberrypi/raspberrypi.cpp | 13 +-- 8 files changed, 93 insertions(+), 73 deletions(-) diff --git a/src/ipa/raspberrypi/cam_helper.cpp b/src/ipa/raspberrypi/cam_helper.cpp index 92a38007f038..062e94c4fef3 100644 --- a/src/ipa/raspberrypi/cam_helper.cpp +++ b/src/ipa/raspberrypi/cam_helper.cpp @@ -184,7 +184,7 @@ void CamHelper::parseEmbeddedData(Span buffer, return; } - deviceStatus.shutter_speed = Exposure(exposureLines).get(); + deviceStatus.shutter_speed = Exposure(exposureLines); deviceStatus.analogue_gain = Gain(gainCode); LOG(IPARPI, Debug) << "Metadata updated - Exposure : " diff --git a/src/ipa/raspberrypi/controller/agc_status.h b/src/ipa/raspberrypi/controller/agc_status.h index 10381c90a313..5d50e177f0dc 100644 --- a/src/ipa/raspberrypi/controller/agc_status.h +++ b/src/ipa/raspberrypi/controller/agc_status.h @@ -6,6 +6,8 @@ */ #pragma once +#include "libcamera/internal/utils.h" + // The AGC algorithm should post the following structure into the image's // "agc.status" metadata. @@ -18,17 +20,17 @@ extern "C" { // ignored until then. struct AgcStatus { - double total_exposure_value; // value for all exposure and gain for this image - double target_exposure_value; // (unfiltered) target total exposure AGC is aiming for - double shutter_time; + libcamera::utils::Duration total_exposure_value; // value for all exposure and gain for this image + libcamera::utils::Duration target_exposure_value; // (unfiltered) target total exposure AGC is aiming for + libcamera::utils::Duration shutter_time; double analogue_gain; char exposure_mode[32]; char constraint_mode[32]; char metering_mode[32]; double ev; - double flicker_period; + libcamera::utils::Duration flicker_period; int floating_region_enable; - double fixed_shutter; + libcamera::utils::Duration fixed_shutter; double fixed_analogue_gain; double digital_gain; int locked; diff --git a/src/ipa/raspberrypi/controller/device_status.h b/src/ipa/raspberrypi/controller/device_status.h index aa08608b5d40..131b4cd344ee 100644 --- a/src/ipa/raspberrypi/controller/device_status.h +++ b/src/ipa/raspberrypi/controller/device_status.h @@ -6,6 +6,8 @@ */ #pragma once +#include "libcamera/internal/utils.h" + // Definition of "device metadata" which stores things like shutter time and // analogue gain that downstream control algorithms will want to know. @@ -14,8 +16,8 @@ extern "C" { #endif struct DeviceStatus { - // time shutter is open, in microseconds - double shutter_speed; + // time shutter is open + libcamera::utils::Duration shutter_speed; double analogue_gain; // 1.0/distance-in-metres, or 0 if unknown double lens_position; diff --git a/src/ipa/raspberrypi/controller/rpi/agc.cpp b/src/ipa/raspberrypi/controller/rpi/agc.cpp index fd3359a77958..55e80ac74ae1 100644 --- a/src/ipa/raspberrypi/controller/rpi/agc.cpp +++ b/src/ipa/raspberrypi/controller/rpi/agc.cpp @@ -56,19 +56,26 @@ read_metering_modes(std::map &metering_modes, return first; } -static int read_double_list(std::vector &list, - boost::property_tree::ptree const ¶ms) +static int read_list(std::vector &list, + boost::property_tree::ptree const ¶ms) { for (auto &p : params) list.push_back(p.second.get_value()); return list.size(); } +static int read_list(std::vector &list, + boost::property_tree::ptree const ¶ms) +{ + for (auto &p : params) + list.push_back(p.second.get_value() * 1us); + return list.size(); +} + void AgcExposureMode::Read(boost::property_tree::ptree const ¶ms) { - int num_shutters = - read_double_list(shutter, params.get_child("shutter")); - int num_ags = read_double_list(gain, params.get_child("gain")); + int num_shutters = read_list(shutter, params.get_child("shutter")); + int num_ags = read_list(gain, params.get_child("gain")); if (num_shutters < 2 || num_ags < 2) throw std::runtime_error( "AgcConfig: must have at least two entries in exposure profile"); @@ -148,7 +155,7 @@ void AgcConfig::Read(boost::property_tree::ptree const ¶ms) params.get("fast_reduce_threshold", 0.4); base_ev = params.get("base_ev", 1.0); // Start with quite a low value as ramping up is easier than ramping down. - default_exposure_time = params.get("default_exposure_time", 1000); + default_exposure_time = params.get("default_exposure_time", 1000) * 1us; default_analogue_gain = params.get("default_analogue_gain", 1.0); } @@ -156,9 +163,9 @@ Agc::Agc(Controller *controller) : AgcAlgorithm(controller), metering_mode_(nullptr), exposure_mode_(nullptr), constraint_mode_(nullptr), frame_count_(0), lock_count_(0), - last_target_exposure_(0.0), - ev_(1.0), flicker_period_(0.0), - max_shutter_(0), fixed_shutter_(0), fixed_analogue_gain_(0.0) + last_target_exposure_(0s), + ev_(1.0), flicker_period_(0s), + max_shutter_(0s), fixed_shutter_(0s), fixed_analogue_gain_(0.0) { memset(&awb_, 0, sizeof(awb_)); // Setting status_.total_exposure_value_ to zero initially tells us @@ -204,7 +211,7 @@ void Agc::Pause() void Agc::Resume() { - fixed_shutter_ = 0; + fixed_shutter_ = 0s; fixed_analogue_gain_ = 0; } @@ -225,17 +232,17 @@ void Agc::SetEv(double ev) void Agc::SetFlickerPeriod(Duration flicker_period) { - flicker_period_ = flicker_period.get(); + flicker_period_ = flicker_period; } void Agc::SetMaxShutter(Duration max_shutter) { - max_shutter_ = max_shutter.get(); + max_shutter_ = max_shutter; } void Agc::SetFixedShutter(Duration fixed_shutter) { - fixed_shutter_ = fixed_shutter.get(); + fixed_shutter_ = fixed_shutter; // Set this in case someone calls Pause() straight after. status_.shutter_time = clipShutter(fixed_shutter_); } @@ -267,8 +274,8 @@ void Agc::SwitchMode([[maybe_unused]] CameraMode const &camera_mode, { housekeepConfig(); - double fixed_shutter = clipShutter(fixed_shutter_); - if (fixed_shutter != 0.0 && fixed_analogue_gain_ != 0.0) { + Duration fixed_shutter = clipShutter(fixed_shutter_); + if (fixed_shutter && fixed_analogue_gain_) { // We're going to reset the algorithm here with these fixed values. fetchAwbStatus(metadata); @@ -313,8 +320,8 @@ void Agc::Prepare(Metadata *image_metadata) // Process has run, so we have meaningful values. DeviceStatus device_status; if (image_metadata->Get("device.status", device_status) == 0) { - double actual_exposure = device_status.shutter_speed * - device_status.analogue_gain; + Duration actual_exposure = device_status.shutter_speed * + device_status.analogue_gain; if (actual_exposure) { status_.digital_gain = status_.total_exposure_value / @@ -327,7 +334,8 @@ void Agc::Prepare(Metadata *image_metadata) std::min(status_.digital_gain, 4.0)); LOG(RPiAgc, Debug) << "Actual exposure " << actual_exposure; LOG(RPiAgc, Debug) << "Use digital_gain " << status_.digital_gain; - LOG(RPiAgc, Debug) << "Effective exposure " << actual_exposure * status_.digital_gain; + LOG(RPiAgc, Debug) << "Effective exposure " + << actual_exposure * status_.digital_gain; // Decide whether AEC/AGC has converged. updateLockStatus(device_status); } @@ -371,9 +379,9 @@ void Agc::updateLockStatus(DeviceStatus const &device_status) const double RESET_MARGIN = 1.5; // Add 200us to the exposure time error to allow for line quantisation. - double exposure_error = last_device_status_.shutter_speed * ERROR_FACTOR + 200; + Duration exposure_error = last_device_status_.shutter_speed * ERROR_FACTOR + 200us; double gain_error = last_device_status_.analogue_gain * ERROR_FACTOR; - double target_error = last_target_exposure_ * ERROR_FACTOR; + Duration target_error = last_target_exposure_ * ERROR_FACTOR; // Note that we don't know the exposure/gain limits of the sensor, so // the values we keep requesting may be unachievable. For this reason @@ -463,7 +471,7 @@ void Agc::fetchCurrentExposure(Metadata *image_metadata) current_.analogue_gain = device_status->analogue_gain; AgcStatus *agc_status = image_metadata->GetLocked("agc.status"); - current_.total_exposure = agc_status ? agc_status->total_exposure_value : 0; + current_.total_exposure = agc_status ? agc_status->total_exposure_value : 0s; current_.total_exposure_no_dg = current_.shutter * current_.analogue_gain; } @@ -574,7 +582,7 @@ void Agc::computeGain(bcm2835_isp_stats *statistics, Metadata *image_metadata, void Agc::computeTargetExposure(double gain) { - if (status_.fixed_shutter != 0.0 && status_.fixed_analogue_gain != 0.0) { + if (status_.fixed_shutter && status_.fixed_analogue_gain) { // When ag and shutter are both fixed, we need to drive the // total exposure so that we end up with a digital gain of at least // 1/min_colour_gain. Otherwise we'd desaturate channels causing @@ -589,11 +597,11 @@ void Agc::computeTargetExposure(double gain) target_.total_exposure = current_.total_exposure_no_dg * gain; // The final target exposure is also limited to what the exposure // mode allows. - double max_shutter = status_.fixed_shutter != 0.0 + Duration max_shutter = status_.fixed_shutter ? status_.fixed_shutter : exposure_mode_->shutter.back(); max_shutter = clipShutter(max_shutter); - double max_total_exposure = + Duration max_total_exposure = max_shutter * (status_.fixed_analogue_gain != 0.0 ? status_.fixed_analogue_gain @@ -638,7 +646,7 @@ void Agc::filterExposure(bool desaturate) if ((status_.fixed_shutter && status_.fixed_analogue_gain) || frame_count_ <= config_.startup_frames) speed = 1.0; - if (filtered_.total_exposure == 0.0) { + if (!filtered_.total_exposure) { filtered_.total_exposure = target_.total_exposure; filtered_.total_exposure_no_dg = target_.total_exposure_no_dg; } else { @@ -675,9 +683,10 @@ void Agc::divideUpExposure() // Sending the fixed shutter/gain cases through the same code may seem // unnecessary, but it will make more sense when extend this to cover // variable aperture. - double exposure_value = filtered_.total_exposure_no_dg; - double shutter_time, analogue_gain; - shutter_time = status_.fixed_shutter != 0.0 + Duration exposure_value = filtered_.total_exposure_no_dg; + Duration shutter_time; + double analogue_gain; + shutter_time = status_.fixed_shutter ? status_.fixed_shutter : exposure_mode_->shutter[0]; shutter_time = clipShutter(shutter_time); @@ -687,8 +696,8 @@ void Agc::divideUpExposure() if (shutter_time * analogue_gain < exposure_value) { for (unsigned int stage = 1; stage < exposure_mode_->gain.size(); stage++) { - if (status_.fixed_shutter == 0.0) { - double stage_shutter = + if (!status_.fixed_shutter) { + Duration stage_shutter = clipShutter(exposure_mode_->shutter[stage]); if (stage_shutter * analogue_gain >= exposure_value) { @@ -714,12 +723,11 @@ void Agc::divideUpExposure() << analogue_gain; // Finally adjust shutter time for flicker avoidance (require both // shutter and gain not to be fixed). - if (status_.fixed_shutter == 0.0 && - status_.fixed_analogue_gain == 0.0 && - status_.flicker_period != 0.0) { + if (!status_.fixed_shutter && !status_.fixed_analogue_gain && + status_.flicker_period) { int flicker_periods = shutter_time / status_.flicker_period; - if (flicker_periods > 0) { - double new_shutter_time = flicker_periods * status_.flicker_period; + if (flicker_periods) { + Duration new_shutter_time = flicker_periods * status_.flicker_period; analogue_gain *= shutter_time / new_shutter_time; // We should still not allow the ag to go over the // largest value in the exposure mode. Note that this @@ -739,7 +747,7 @@ void Agc::divideUpExposure() void Agc::writeAndFinish(Metadata *image_metadata, bool desaturate) { status_.total_exposure_value = filtered_.total_exposure; - status_.target_exposure_value = desaturate ? 0 : target_.total_exposure_no_dg; + status_.target_exposure_value = desaturate ? 0s : target_.total_exposure_no_dg; status_.shutter_time = filtered_.shutter; status_.analogue_gain = filtered_.analogue_gain; // Write to metadata as well, in case anyone wants to update the camera @@ -751,7 +759,7 @@ void Agc::writeAndFinish(Metadata *image_metadata, bool desaturate) << " analogue gain " << filtered_.analogue_gain; } -double Agc::clipShutter(double shutter) +Duration Agc::clipShutter(Duration shutter) { if (max_shutter_) shutter = std::min(shutter, max_shutter_); diff --git a/src/ipa/raspberrypi/controller/rpi/agc.hpp b/src/ipa/raspberrypi/controller/rpi/agc.hpp index b52aaa23d51c..750789482b49 100644 --- a/src/ipa/raspberrypi/controller/rpi/agc.hpp +++ b/src/ipa/raspberrypi/controller/rpi/agc.hpp @@ -9,6 +9,8 @@ #include #include +#include "libcamera/internal/utils.h" + #include "../agc_algorithm.hpp" #include "../agc_status.h" #include "../pwl.hpp" @@ -22,13 +24,15 @@ namespace RPiController { +using namespace std::literals::chrono_literals; + struct AgcMeteringMode { double weights[AGC_STATS_SIZE]; void Read(boost::property_tree::ptree const ¶ms); }; struct AgcExposureMode { - std::vector shutter; + std::vector shutter; std::vector gain; void Read(boost::property_tree::ptree const ¶ms); }; @@ -61,7 +65,7 @@ struct AgcConfig { std::string default_exposure_mode; std::string default_constraint_mode; double base_ev; - double default_exposure_time; + libcamera::utils::Duration default_exposure_time; double default_analogue_gain; }; @@ -101,19 +105,19 @@ private: void filterExposure(bool desaturate); void divideUpExposure(); void writeAndFinish(Metadata *image_metadata, bool desaturate); - double clipShutter(double shutter); + libcamera::utils::Duration clipShutter(libcamera::utils::Duration shutter); AgcMeteringMode *metering_mode_; AgcExposureMode *exposure_mode_; AgcConstraintMode *constraint_mode_; uint64_t frame_count_; AwbStatus awb_; struct ExposureValues { - ExposureValues() : shutter(0), analogue_gain(0), - total_exposure(0), total_exposure_no_dg(0) {} - double shutter; + ExposureValues() : shutter(0s), analogue_gain(0), + total_exposure(0s), total_exposure_no_dg(0s) {} + libcamera::utils::Duration shutter; double analogue_gain; - double total_exposure; - double total_exposure_no_dg; // without digital gain + libcamera::utils::Duration total_exposure; + libcamera::utils::Duration total_exposure_no_dg; // without digital gain }; ExposureValues current_; // values for the current frame ExposureValues target_; // calculate the values we want here @@ -121,15 +125,15 @@ private: AgcStatus status_; int lock_count_; DeviceStatus last_device_status_; - double last_target_exposure_; + libcamera::utils::Duration last_target_exposure_; // Below here the "settings" that applications can change. std::string metering_mode_name_; std::string exposure_mode_name_; std::string constraint_mode_name_; double ev_; - double flicker_period_; - double max_shutter_; - double fixed_shutter_; + libcamera::utils::Duration flicker_period_; + libcamera::utils::Duration max_shutter_; + libcamera::utils::Duration fixed_shutter_; double fixed_analogue_gain_; }; diff --git a/src/ipa/raspberrypi/controller/rpi/lux.cpp b/src/ipa/raspberrypi/controller/rpi/lux.cpp index f74381cab2b4..258e44f4dcce 100644 --- a/src/ipa/raspberrypi/controller/rpi/lux.cpp +++ b/src/ipa/raspberrypi/controller/rpi/lux.cpp @@ -16,6 +16,7 @@ using namespace RPiController; using namespace libcamera; +using namespace std::literals::chrono_literals; LOG_DEFINE_CATEGORY(RPiLux) @@ -38,7 +39,7 @@ char const *Lux::Name() const void Lux::Read(boost::property_tree::ptree const ¶ms) { reference_shutter_speed_ = - params.get("reference_shutter_speed"); + params.get("reference_shutter_speed") * 1.0us; reference_gain_ = params.get("reference_gain"); reference_aperture_ = params.get("reference_aperture", 1.0); reference_Y_ = params.get("reference_Y"); @@ -60,15 +61,15 @@ void Lux::Prepare(Metadata *image_metadata) void Lux::Process(StatisticsPtr &stats, Metadata *image_metadata) { // set some initial values to shut the compiler up - DeviceStatus device_status = - { .shutter_speed = 1.0, - .analogue_gain = 1.0, - .lens_position = 0.0, - .aperture = 0.0, - .flash_intensity = 0.0 }; + DeviceStatus device_status = { + .shutter_speed = 1.0ms, + .analogue_gain = 1.0, + .lens_position = 0.0, + .aperture = 0.0, + .flash_intensity = 0.0 + }; if (image_metadata->Get("device.status", device_status) == 0) { double current_gain = device_status.analogue_gain; - double current_shutter_speed = device_status.shutter_speed; double current_aperture = device_status.aperture; if (current_aperture == 0) current_aperture = current_aperture_; @@ -83,7 +84,7 @@ void Lux::Process(StatisticsPtr &stats, Metadata *image_metadata) double current_Y = sum / (double)num + .5; double gain_ratio = reference_gain_ / current_gain; double shutter_speed_ratio = - reference_shutter_speed_ / current_shutter_speed; + reference_shutter_speed_ / device_status.shutter_speed; double aperture_ratio = reference_aperture_ / current_aperture; double Y_ratio = current_Y * (65536 / num_bins) / reference_Y_; double estimated_lux = shutter_speed_ratio * gain_ratio * diff --git a/src/ipa/raspberrypi/controller/rpi/lux.hpp b/src/ipa/raspberrypi/controller/rpi/lux.hpp index f9090484a136..45c844393e62 100644 --- a/src/ipa/raspberrypi/controller/rpi/lux.hpp +++ b/src/ipa/raspberrypi/controller/rpi/lux.hpp @@ -8,6 +8,8 @@ #include +#include "libcamera/internal/utils.h" + #include "../lux_status.h" #include "../algorithm.hpp" @@ -28,7 +30,7 @@ public: private: // These values define the conditions of the reference image, against // which we compare the new image. - double reference_shutter_speed_; // in micro-seconds + libcamera::utils::Duration reference_shutter_speed_; double reference_gain_; double reference_aperture_; // units of 1/f double reference_Y_; // out of 65536 diff --git a/src/ipa/raspberrypi/raspberrypi.cpp b/src/ipa/raspberrypi/raspberrypi.cpp index 4aa8ccaa985a..1c1e802a2cdc 100644 --- a/src/ipa/raspberrypi/raspberrypi.cpp +++ b/src/ipa/raspberrypi/raspberrypi.cpp @@ -222,11 +222,11 @@ void IPARPi::start(const ControlList &controls, ipa::RPi::StartConfig *startConf /* SwitchMode may supply updated exposure/gain values to use. */ AgcStatus agcStatus; - agcStatus.shutter_time = 0.0; + agcStatus.shutter_time = 0.0s; agcStatus.analogue_gain = 0.0; metadata.Get("agc.status", agcStatus); - if (agcStatus.shutter_time != 0.0 && agcStatus.analogue_gain != 0.0) { + if (agcStatus.shutter_time && agcStatus.analogue_gain) { ControlList ctrls(sensorCtrls_); applyAGC(&agcStatus, ctrls); startConfig->controls = std::move(ctrls); @@ -389,7 +389,7 @@ int IPARPi::configure(const IPACameraSensorInfo &sensorInfo, /* Supply initial values for gain and exposure. */ ControlList ctrls(sensorCtrls_); AgcStatus agcStatus; - agcStatus.shutter_time = DefaultExposureTime.get(); + agcStatus.shutter_time = DefaultExposureTime; agcStatus.analogue_gain = DefaultAnalogueGain; applyAGC(&agcStatus, ctrls); @@ -461,7 +461,8 @@ void IPARPi::reportMetadata() */ DeviceStatus *deviceStatus = rpiMetadata_.GetLocked("device.status"); if (deviceStatus) { - libcameraMetadata_.set(controls::ExposureTime, deviceStatus->shutter_speed); + libcameraMetadata_.set(controls::ExposureTime, + deviceStatus->shutter_speed.get()); libcameraMetadata_.set(controls::AnalogueGain, deviceStatus->analogue_gain); } @@ -1014,7 +1015,7 @@ void IPARPi::fillDeviceStatus(const ControlList &sensorControls) int32_t exposureLines = sensorControls.get(V4L2_CID_EXPOSURE).get(); int32_t gainCode = sensorControls.get(V4L2_CID_ANALOGUE_GAIN).get(); - deviceStatus.shutter_speed = helper_->Exposure(exposureLines).get(); + deviceStatus.shutter_speed = helper_->Exposure(exposureLines); deviceStatus.analogue_gain = helper_->Gain(gainCode); LOG(IPARPI, Debug) << "Metadata - Exposure : " @@ -1099,7 +1100,7 @@ void IPARPi::applyAGC(const struct AgcStatus *agcStatus, ControlList &ctrls) int32_t gainCode = helper_->GainCode(agcStatus->analogue_gain); /* GetVBlanking might clip exposure time to the fps limits. */ - Duration exposure = agcStatus->shutter_time * 1.0us; + Duration exposure = agcStatus->shutter_time; int32_t vblanking = helper_->GetVBlanking(exposure, minFrameDuration_, maxFrameDuration_); int32_t exposureLines = helper_->ExposureLines(exposure);