[{"id":38305,"web_url":"https://patchwork.libcamera.org/comment/38305/","msgid":"<85cy1r1vsp.fsf@mzamazal-thinkpadp1gen7.tpbc.csb>","date":"2026-02-26T13:25:26","subject":"Re: [PATCH 1/2] ipa: simple: agc: Replace bang-bang controller with\n\tproportional","submitter":{"id":177,"url":"https://patchwork.libcamera.org/api/people/177/","name":"Milan Zamazal","email":"mzamazal@redhat.com"},"content":"Hi Javier,\n\nthank you for the patch.\n\nJavier Tia <floss@jetm.me> writes:\n\n> The AGC's updateExposure() uses a fixed ~10% step per frame regardless\n> of how far the current exposure is from optimal. With a hysteresis dead\n> band of only +/-4%, the controller overshoots when the correct value\n> falls within one step, causing visible brightness oscillation (flicker).\n>\n> Replace the fixed-step bang-bang controller with a proportional one\n> where the correction factor scales linearly with the MSV error:\n>\n>   factor = 1.0 + clamp(error * 0.04, -0.15, +0.15)\n>\n> At maximum error (~2.5), this gives the same ~10% step as before. Near\n> the target, steps shrink to <1%, eliminating overshoot. The existing\n> hysteresis (kExposureSatisfactory) still prevents hunting on noise.\n>\n> Tested on OV2740 behind Intel IPU6 ISYS (ThinkPad X1 Carbon Gen 10)\n> where the old controller produced continuous brightness flicker. The\n> proportional controller converges in ~3 seconds from cold start with\n> no visible oscillation.\n\nNice change.  The patch looks good to me, let me test it in my\nenvironment (later).\n\n> Signed-off-by: Javier Tia <floss@jetm.me>\n> ---\n>  src/ipa/simple/algorithms/agc.cpp | 73 +++++++++++++++++++++----------\n>  1 file changed, 49 insertions(+), 24 deletions(-)\n>\n> diff --git a/src/ipa/simple/algorithms/agc.cpp b/src/ipa/simple/algorithms/agc.cpp\n> index 2f7e040c..a13a7552 100644\n> --- a/src/ipa/simple/algorithms/agc.cpp\n> +++ b/src/ipa/simple/algorithms/agc.cpp\n> @@ -7,6 +7,8 @@\n>  \n>  #include \"agc.h\"\n>  \n> +#include <algorithm>\n> +#include <cmath>\n>  #include <stdint.h>\n>  \n>  #include <libcamera/base/log.h>\n> @@ -37,52 +39,74 @@ static constexpr float kExposureOptimal = kExposureBinsCount / 2.0;\n>   */\n>  static constexpr float kExposureSatisfactory = 0.2;\n>  \n> +/*\n> + * Proportional gain for exposure/gain adjustment. Maps the MSV error to a\n> + * multiplicative correction factor:\n> + *\n> + *   factor = 1.0 + kExpProportionalGain * error\n> + *\n> + * With kExpProportionalGain = 0.04:\n> + *   - max error ~2.5 -> factor 1.10 (~10% step, same as before)\n> + *   - error 1.0      -> factor 1.04 (~4% step)\n> + *   - error 0.3      -> factor 1.012 (~1.2% step)\n> + *\n> + * This replaces the fixed 10% bang-bang step with a proportional correction\n> + * that converges smoothly and avoids overshooting near the target.\n> + */\n> +static constexpr float kExpProportionalGain = 0.04;\n> +\n> +/*\n> + * Maximum multiplicative step per frame, to bound the correction when the\n> + * scene changes dramatically.\n> + */\n> +static constexpr float kExpMaxStep = 0.15;\n> +\n>  Agc::Agc()\n>  {\n>  }\n>  \n>  void Agc::updateExposure(IPAContext &context, IPAFrameContext &frameContext, double exposureMSV)\n>  {\n> -\t/*\n> -\t * kExpDenominator of 10 gives ~10% increment/decrement;\n> -\t * kExpDenominator of 5 - about ~20%\n> -\t */\n> -\tstatic constexpr uint8_t kExpDenominator = 10;\n> -\tstatic constexpr uint8_t kExpNumeratorUp = kExpDenominator + 1;\n> -\tstatic constexpr uint8_t kExpNumeratorDown = kExpDenominator - 1;\n> -\n>  \tint32_t &exposure = frameContext.sensor.exposure;\n>  \tdouble &again = frameContext.sensor.gain;\n>  \n> -\tif (exposureMSV < kExposureOptimal - kExposureSatisfactory) {\n> +\tdouble error = kExposureOptimal - exposureMSV;\n> +\n> +\tif (std::abs(error) <= kExposureSatisfactory)\n> +\t\treturn;\n> +\n> +\t/*\n> +\t * Compute a proportional correction factor. The sign of the error\n> +\t * determines the direction: positive error means too dark (increase),\n> +\t * negative means too bright (decrease).\n> +\t */\n> +\tfloat step = std::clamp(static_cast<float>(error) * kExpProportionalGain,\n> +\t\t\t\t-kExpMaxStep, kExpMaxStep);\n> +\tfloat factor = 1.0f + step;\n> +\n> +\tif (factor > 1.0f) {\n> +\t\t/* Scene too dark: increase exposure first, then gain. */\n>  \t\tif (exposure < context.configuration.agc.exposureMax) {\n> -\t\t\tint32_t next = exposure * kExpNumeratorUp / kExpDenominator;\n> -\t\t\tif (next - exposure < 1)\n> -\t\t\t\texposure += 1;\n> -\t\t\telse\n> -\t\t\t\texposure = next;\n> +\t\t\tint32_t next = static_cast<int32_t>(exposure * factor);\n> +\t\t\texposure = std::max(next, exposure + 1);\n>  \t\t} else {\n> -\t\t\tdouble next = again * kExpNumeratorUp / kExpDenominator;\n> +\t\t\tdouble next = again * factor;\n>  \t\t\tif (next - again < context.configuration.agc.againMinStep)\n>  \t\t\t\tagain += context.configuration.agc.againMinStep;\n>  \t\t\telse\n>  \t\t\t\tagain = next;\n>  \t\t}\n> -\t}\n> -\n> -\tif (exposureMSV > kExposureOptimal + kExposureSatisfactory) {\n> +\t} else {\n> +\t\t/* Scene too bright: decrease gain first, then exposure. */\n>  \t\tif (again > context.configuration.agc.again10) {\n> -\t\t\tdouble next = again * kExpNumeratorDown / kExpDenominator;\n> +\t\t\tdouble next = again * factor;\n>  \t\t\tif (again - next < context.configuration.agc.againMinStep)\n>  \t\t\t\tagain -= context.configuration.agc.againMinStep;\n>  \t\t\telse\n>  \t\t\t\tagain = next;\n>  \t\t} else {\n> -\t\t\tint32_t next = exposure * kExpNumeratorDown / kExpDenominator;\n> -\t\t\tif (exposure - next < 1)\n> -\t\t\t\texposure -= 1;\n> -\t\t\telse\n> -\t\t\t\texposure = next;\n> +\t\t\tint32_t next = static_cast<int32_t>(exposure * factor);\n> +\t\t\texposure = std::min(next, exposure - 1);\n>  \t\t}\n>  \t}\n>  \n> @@ -96,6 +120,7 @@ void Agc::updateExposure(IPAContext &context, IPAFrameContext &frameContext, dou\n>  \n>  \tLOG(IPASoftExposure, Debug)\n>  \t\t<< \"exposureMSV \" << exposureMSV\n> +\t\t<< \" error \" << error << \" factor \" << factor\n>  \t\t<< \" exp \" << exposure << \" again \" << again;\n>  }","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id 75B5EC0DA4\n\tfor <parsemail@patchwork.libcamera.org>;\n\tThu, 26 Feb 2026 13:25:35 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 5B142622EB;\n\tThu, 26 Feb 2026 14:25:34 +0100 (CET)","from us-smtp-delivery-124.mimecast.com\n\t(us-smtp-delivery-124.mimecast.com [170.10.129.124])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 84C87620FA\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu, 26 Feb 2026 14:25:32 +0100 (CET)","from mail-wr1-f72.google.com (mail-wr1-f72.google.com\n\t[209.85.221.72]) by relay.mimecast.com with ESMTP with STARTTLS\n\t(version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id\n\tus-mta-299-xClivATEPka-pBXOydpMGg-1; Thu, 26 Feb 2026 08:25:30 -0500","by mail-wr1-f72.google.com with SMTP id\n\tffacd0b85a97d-4368060a5e5so974992f8f.3\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu, 26 Feb 2026 05:25:29 -0800 (PST)","from mzamazal-thinkpadp1gen7.tpbc.csb ([213.175.37.14])\n\tby smtp.gmail.com with ESMTPSA id\n\tffacd0b85a97d-43995c4b726sm7588295f8f.5.2026.02.26.05.25.26\n\t(version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n\tThu, 26 Feb 2026 05:25:27 -0800 (PST)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=redhat.com header.i=@redhat.com\n\theader.b=\"IbX1x9hb\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com;\n\ts=mimecast20190719; t=1772112331;\n\th=from:from:reply-to:subject:subject:date:date:message-id:message-id:\n\tto:to:cc:cc:mime-version:mime-version:content-type:content-type:\n\tin-reply-to:in-reply-to:references:references;\n\tbh=mxW3bvDTHFyxAmHxGqExCJaag1fSa9fd97xU3+F6Jk4=;\n\tb=IbX1x9hbPOIksit+5k8mikYsOnt2BxJKoSpLDY6hNu1My8AJWGCRY1rdpcKqqQ1zLoiMP8\n\t25Ohhec5j9uI+Qe76isb1oQ0wD3Hjpp7ksmah8C6odDpA+YcRuHPQm5I7103uetdtUUxs8\n\tArzB8wF1Gj0ViLiyvs1K9gjR+LxIiKQ=","X-MC-Unique":"xClivATEPka-pBXOydpMGg-1","X-Mimecast-MFC-AGG-ID":"xClivATEPka-pBXOydpMGg_1772112329","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20230601; t=1772112328; x=1772717128;\n\th=mime-version:user-agent:message-id:date:references:in-reply-to\n\t:subject:cc:to:from:x-gm-gg:x-gm-message-state:from:to:cc:subject\n\t:date:message-id:reply-to;\n\tbh=mxW3bvDTHFyxAmHxGqExCJaag1fSa9fd97xU3+F6Jk4=;\n\tb=kL858+uzmvmICzUulxxtwS9n92iijUgKNrqlWmsdbDNSPAldE0FOBHecx9gBO0SQwf\n\ts2jt0t4jcSS/xp908NgVU3zKCbxTTqzv1wVUPXeJp1MH7H4SPbw4r9Q83r/9oGn7F3Dy\n\tbMmEU/1ZceiX88haEjB5mTWZYBekHMxDYlbNXiTouYN6cauyGiFttr9GYvuRC2ddvjdd\n\taNDwmtF3B7ztYyYWDbRY8WIHB+gxlNHPwqxmTxSEkBlUEcz+O/uRLllo+LynYeNOazZD\n\tD2N53Krhg7fEbWvmUzVaGcDsQoE539jfQsd4nBts2hAaQpiPFebhPezB1YqwRasfPbQk\n\tCZ0Q==","X-Gm-Message-State":"AOJu0Yzsc2JWymo4KZSMSkNSTuWqktGkgz7jtbf26CR2BTIuM9F4Miys\n\t2TKWdVXFhgudN4uLbLhpwC6JTDigjK7pgpHhAzKHHwuPVbe4Bhh8mQGafElCwWdWY/CYeSjzFu8\n\tgyvFdUVpvWwf9nKFUTk9OeMLZ5V7niZBU079I1i8W9tmlgXelB05X49Q6lInwT+yDFsRPYYWL2t\n\tnxlmcvigPzPKkIvvcf+GAwwDmfD4yDLRGnwYZW+yUNKfTYcIhx6mUtcDHPsEw=","X-Gm-Gg":"ATEYQzyERCQpzomZWNDm1iD+rY3SDlgkG4yfbro8sDn0pLAUVJc2TescSfKk3/tzdw+\n\t2X2lwcR377dqy6OEJccgUtGuzrpPQ1gUtpbyhrfYskVMqozwzoBL7dbpfqGB6xMlUzZwO7TVzmn\n\t4/CSpOaylcCPiVIB4P7FOArtRddsm8IwSC5v8IAzgpBegQ4p+JcZ5t8n/Ami746WzSip/b7VPEr\n\tlS0iVjtktRvAMI26Jrjkb+P1icdhaf8FMEnvbQPyZn1HpokYTLzc3BKhFEgmlj1pGleeJ57kGe7\n\tWuzS/8iPHC+nmZ4xUWPWohyIeHe6oQgpbYEHtdIqtBPj06P0ut/NgqeJP+6Em4fjsSszScKCqt4\n\tdN/yGqjj+h8tHIoHscGoHcsjY+g97MX9f+MJ0Gg==","X-Received":["by 2002:a05:6000:228a:b0:430:ff0c:35f9 with SMTP id\n\tffacd0b85a97d-439942f5a4bmr8043610f8f.48.1772112328350; \n\tThu, 26 Feb 2026 05:25:28 -0800 (PST)","by 2002:a05:6000:228a:b0:430:ff0c:35f9 with SMTP id\n\tffacd0b85a97d-439942f5a4bmr8043524f8f.48.1772112327700; \n\tThu, 26 Feb 2026 05:25:27 -0800 (PST)"],"From":"Milan Zamazal <mzamazal@redhat.com>","To":"Javier Tia <floss@jetm.me>","Cc":"libcamera-devel@lists.libcamera.org","Subject":"Re: [PATCH 1/2] ipa: simple: agc: Replace bang-bang controller with\n\tproportional","In-Reply-To":"<20260225221859.600869-2-floss@jetm.me> (Javier Tia's message of\n\t\"Wed, 25 Feb 2026 16:18:58 -0600\")","References":"<20260225221859.600869-1-floss@jetm.me>\n\t<20260225221859.600869-2-floss@jetm.me>","Date":"Thu, 26 Feb 2026 14:25:26 +0100","Message-ID":"<85cy1r1vsp.fsf@mzamazal-thinkpadp1gen7.tpbc.csb>","User-Agent":"Gnus/5.13 (Gnus v5.13)","MIME-Version":"1.0","X-Mimecast-Spam-Score":"0","X-Mimecast-MFC-PROC-ID":"0sSbTDUR-JkVXWY8bqqUa_ID5675T-1JIG0KqUNp7KM_1772112329","X-Mimecast-Originator":"redhat.com","Content-Type":"text/plain","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":38308,"web_url":"https://patchwork.libcamera.org/comment/38308/","msgid":"<85o6laurrv.fsf@mzamazal-thinkpadp1gen7.tpbc.csb>","date":"2026-02-27T15:31:48","subject":"Re: [PATCH 1/2] ipa: simple: agc: Replace bang-bang controller with\n\tproportional","submitter":{"id":177,"url":"https://patchwork.libcamera.org/api/people/177/","name":"Milan Zamazal","email":"mzamazal@redhat.com"},"content":"Milan Zamazal <mzamazal@redhat.com> writes:\n\n> Hi Javier,\n>\n> thank you for the patch.\n>\n> Javier Tia <floss@jetm.me> writes:\n>\n>> The AGC's updateExposure() uses a fixed ~10% step per frame regardless\n>> of how far the current exposure is from optimal. With a hysteresis dead\n>> band of only +/-4%, the controller overshoots when the correct value\n>> falls within one step, causing visible brightness oscillation (flicker).\n>>\n>> Replace the fixed-step bang-bang controller with a proportional one\n>> where the correction factor scales linearly with the MSV error:\n>>\n>>   factor = 1.0 + clamp(error * 0.04, -0.15, +0.15)\n>>\n>> At maximum error (~2.5), this gives the same ~10% step as before. Near\n>> the target, steps shrink to <1%, eliminating overshoot. The existing\n>> hysteresis (kExposureSatisfactory) still prevents hunting on noise.\n>>\n>> Tested on OV2740 behind Intel IPU6 ISYS (ThinkPad X1 Carbon Gen 10)\n>> where the old controller produced continuous brightness flicker. The\n>> proportional controller converges in ~3 seconds from cold start with\n>> no visible oscillation.\n>\n> Nice change.  The patch looks good to me, let me test it in my\n> environment (later).\n\nIt works fine for me.  I wouldn't mind increasing kExpProportionalGain\nto react to exposure changes faster, but it's easy to change the\nconstant later.\n\nReviewed-by: Milan Zamazal <mzamazal@redhat.com>\n\n>> Signed-off-by: Javier Tia <floss@jetm.me>\n>> ---\n>>  src/ipa/simple/algorithms/agc.cpp | 73 +++++++++++++++++++++----------\n>>  1 file changed, 49 insertions(+), 24 deletions(-)\n>>\n>> diff --git a/src/ipa/simple/algorithms/agc.cpp b/src/ipa/simple/algorithms/agc.cpp\n>> index 2f7e040c..a13a7552 100644\n>> --- a/src/ipa/simple/algorithms/agc.cpp\n>> +++ b/src/ipa/simple/algorithms/agc.cpp\n>> @@ -7,6 +7,8 @@\n>>  \n>>  #include \"agc.h\"\n>>  \n>> +#include <algorithm>\n>> +#include <cmath>\n>>  #include <stdint.h>\n>>  \n>>  #include <libcamera/base/log.h>\n>> @@ -37,52 +39,74 @@ static constexpr float kExposureOptimal = kExposureBinsCount / 2.0;\n>>   */\n>>  static constexpr float kExposureSatisfactory = 0.2;\n>>  \n>> +/*\n>> + * Proportional gain for exposure/gain adjustment. Maps the MSV error to a\n>> + * multiplicative correction factor:\n>> + *\n>> + *   factor = 1.0 + kExpProportionalGain * error\n>> + *\n>> + * With kExpProportionalGain = 0.04:\n>> + *   - max error ~2.5 -> factor 1.10 (~10% step, same as before)\n>> + *   - error 1.0      -> factor 1.04 (~4% step)\n>> + *   - error 0.3      -> factor 1.012 (~1.2% step)\n>> + *\n>> + * This replaces the fixed 10% bang-bang step with a proportional correction\n>> + * that converges smoothly and avoids overshooting near the target.\n>> + */\n>> +static constexpr float kExpProportionalGain = 0.04;\n>> +\n>> +/*\n>> + * Maximum multiplicative step per frame, to bound the correction when the\n>> + * scene changes dramatically.\n>> + */\n>> +static constexpr float kExpMaxStep = 0.15;\n>> +\n>>  Agc::Agc()\n>>  {\n>>  }\n>>  \n>>  void Agc::updateExposure(IPAContext &context, IPAFrameContext &frameContext, double exposureMSV)\n>>  {\n>> -\t/*\n>> -\t * kExpDenominator of 10 gives ~10% increment/decrement;\n>> -\t * kExpDenominator of 5 - about ~20%\n>> -\t */\n>> -\tstatic constexpr uint8_t kExpDenominator = 10;\n>> -\tstatic constexpr uint8_t kExpNumeratorUp = kExpDenominator + 1;\n>> -\tstatic constexpr uint8_t kExpNumeratorDown = kExpDenominator - 1;\n>> -\n>>  \tint32_t &exposure = frameContext.sensor.exposure;\n>>  \tdouble &again = frameContext.sensor.gain;\n>>  \n>> -\tif (exposureMSV < kExposureOptimal - kExposureSatisfactory) {\n>> +\tdouble error = kExposureOptimal - exposureMSV;\n>> +\n>> +\tif (std::abs(error) <= kExposureSatisfactory)\n>> +\t\treturn;\n>> +\n>> +\t/*\n>> +\t * Compute a proportional correction factor. The sign of the error\n>> +\t * determines the direction: positive error means too dark (increase),\n>> +\t * negative means too bright (decrease).\n>> +\t */\n>> +\tfloat step = std::clamp(static_cast<float>(error) * kExpProportionalGain,\n>> +\t\t\t\t-kExpMaxStep, kExpMaxStep);\n>> +\tfloat factor = 1.0f + step;\n>> +\n>> +\tif (factor > 1.0f) {\n>> +\t\t/* Scene too dark: increase exposure first, then gain. */\n>>  \t\tif (exposure < context.configuration.agc.exposureMax) {\n>> -\t\t\tint32_t next = exposure * kExpNumeratorUp / kExpDenominator;\n>> -\t\t\tif (next - exposure < 1)\n>> -\t\t\t\texposure += 1;\n>> -\t\t\telse\n>> -\t\t\t\texposure = next;\n>> +\t\t\tint32_t next = static_cast<int32_t>(exposure * factor);\n>> +\t\t\texposure = std::max(next, exposure + 1);\n>>  \t\t} else {\n>> -\t\t\tdouble next = again * kExpNumeratorUp / kExpDenominator;\n>> +\t\t\tdouble next = again * factor;\n>>  \t\t\tif (next - again < context.configuration.agc.againMinStep)\n>>  \t\t\t\tagain += context.configuration.agc.againMinStep;\n>>  \t\t\telse\n>>  \t\t\t\tagain = next;\n>>  \t\t}\n>> -\t}\n>> -\n>> -\tif (exposureMSV > kExposureOptimal + kExposureSatisfactory) {\n>> +\t} else {\n>> +\t\t/* Scene too bright: decrease gain first, then exposure. */\n>>  \t\tif (again > context.configuration.agc.again10) {\n>> -\t\t\tdouble next = again * kExpNumeratorDown / kExpDenominator;\n>> +\t\t\tdouble next = again * factor;\n>>  \t\t\tif (again - next < context.configuration.agc.againMinStep)\n>>  \t\t\t\tagain -= context.configuration.agc.againMinStep;\n>>  \t\t\telse\n>>  \t\t\t\tagain = next;\n>>  \t\t} else {\n>> -\t\t\tint32_t next = exposure * kExpNumeratorDown / kExpDenominator;\n>> -\t\t\tif (exposure - next < 1)\n>> -\t\t\t\texposure -= 1;\n>> -\t\t\telse\n>> -\t\t\t\texposure = next;\n>> +\t\t\tint32_t next = static_cast<int32_t>(exposure * factor);\n>> +\t\t\texposure = std::min(next, exposure - 1);\n>>  \t\t}\n>>  \t}\n>>  \n>> @@ -96,6 +120,7 @@ void Agc::updateExposure(IPAContext &context, IPAFrameContext &frameContext, dou\n>>  \n>>  \tLOG(IPASoftExposure, Debug)\n>>  \t\t<< \"exposureMSV \" << exposureMSV\n>> +\t\t<< \" error \" << error << \" factor \" << factor\n>>  \t\t<< \" exp \" << exposure << \" again \" << again;\n>>  }","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id CA733C0DA4\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri, 27 Feb 2026 15:31:57 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 036E362369;\n\tFri, 27 Feb 2026 16:31:56 +0100 (CET)","from us-smtp-delivery-124.mimecast.com\n\t(us-smtp-delivery-124.mimecast.com [170.10.129.124])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 752A1622AD\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 27 Feb 2026 16:31:55 +0100 (CET)","from mail-wr1-f71.google.com (mail-wr1-f71.google.com\n\t[209.85.221.71]) by relay.mimecast.com with ESMTP with STARTTLS\n\t(version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id\n\tus-mta-339-pF_DTxk9NBK1wWBvbkklNg-1; Fri, 27 Feb 2026 10:31:52 -0500","by mail-wr1-f71.google.com with SMTP id\n\tffacd0b85a97d-4398e534783so1088721f8f.2\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 27 Feb 2026 07:31:52 -0800 (PST)","from mzamazal-thinkpadp1gen7.tpbc.csb\n\t(ip-77-48-47-2.net.vodafone.cz. [77.48.47.2])\n\tby smtp.gmail.com with ESMTPSA id\n\tffacd0b85a97d-4399c71ad07sm7658938f8f.16.2026.02.27.07.31.48\n\t(version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n\tFri, 27 Feb 2026 07:31:49 -0800 (PST)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=redhat.com header.i=@redhat.com\n\theader.b=\"gIs3IeAj\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com;\n\ts=mimecast20190719; t=1772206314;\n\th=from:from:reply-to:subject:subject:date:date:message-id:message-id:\n\tto:to:cc:cc:mime-version:mime-version:content-type:content-type:\n\tin-reply-to:in-reply-to:references:references;\n\tbh=5Tsq0qE2sfmIlrEupnGHCPOVsLhHRM/kHGaX42w+OxE=;\n\tb=gIs3IeAjBaCrRPueRQyJhcEsDXgxwLcfS4RJvMjpYF6SCDnkt38jQt0cB1B58R0qRj6qmE\n\tjyn98uUIjPaBmYg3sl8y9zhyAyK4cpVqIcJhvuO+vRIFUMYoIIiljU40Dlpyvo0+yH5tgT\n\tYHhWXxwCA+enyhTaXZYSCAzgTTSrBUk=","X-MC-Unique":"pF_DTxk9NBK1wWBvbkklNg-1","X-Mimecast-MFC-AGG-ID":"pF_DTxk9NBK1wWBvbkklNg_1772206311","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20230601; t=1772206311; x=1772811111;\n\th=mime-version:user-agent:message-id:date:references:in-reply-to\n\t:subject:cc:to:from:x-gm-gg:x-gm-message-state:from:to:cc:subject\n\t:date:message-id:reply-to;\n\tbh=5Tsq0qE2sfmIlrEupnGHCPOVsLhHRM/kHGaX42w+OxE=;\n\tb=I/y3l2zDCkmhWatcyI9sCJvWB6t+jtvspSzNhThKb8UddybdtIwXKKCuMCLTfatoIP\n\t0aFDOl9tlKc/W8+AWJkkMERpAa2pE9quDWPjwv7Jlm9tKDGbwblqyLOKE8kZuAodjjxF\n\t6B4R+srCA0uaxhus9kkHT8JW/+tMTygXeIYUBEVXPLMQaEzTxDz9gR5B5YmNhmRp5dNt\n\t9a1/RZEpCPCNZ2oZekrk6quLJrnVlJfsfin/ockZmmrUTuRFBt8gTHN8YJI7oF+UZlHY\n\tXEe+EMJuAR3vor6Cm68dzvn7IgtRuiFU1EbcYiWMdx5cCFjakgy4GeoqN45ZNAJuxWrK\n\tDE1g==","X-Gm-Message-State":"AOJu0YxCGKj3+KULpbkPb81xULI6RhvhhVW1AmrfoXzcR9vvjAWlq/8b\n\tk2HgTOX7mOGqzv2H+8PdpgkLcGE+uFgl2lvGq7qG0oxkz91jto6QMDtYpRDwvuh3hReexyxcZ3f\n\tg8agfyW4ENL+24l+xHKIt4RtWptVu3RDMdwF1RfR3mEAl5QOR9R4TesEi/u/9wH1ClgLEFJ5z7A\n\tNYGTp/sGfwRpLf6QjwbkX2RqgzpvfcaGgIgZucrJU5bpb5+pt6ESVnmEffpxc=","X-Gm-Gg":"ATEYQzxNKHIbC4jN5UOnzwY5p8IhmKwKmO3zAgea4BhTzbZmIeRk7MT62xjvuMUP+6t\n\tZUt6hDbczfTM67840BVV17F0awuk8oN8WCT+ZJ6DV42A9YDLZ1+BASdCR5QLOfN8uJAS5Q4DPRu\n\t8MVirKR1l8lbGndBXcJsQwbvPcg2psS25n65gQDRGYqSEEObsQLarf6TBIuvDBmDOmosh9rOHKo\n\tKuF/vmrMu2PWEDFzWMtb4sMYpKJiHDVPyz2tV+Kw8RRzdxNAqeDrVeh5c1NDX+qq5Z07M249tBm\n\tYfQjPfREQfAzkPsU6FPpvSH1YzYbDYzVQtIq7YIZxC+OiypVHk+NBJCjI3g2dcd6Y1ShGS8FoJx\n\tm0KcphhdMkp0eGQSntMgITNgI8b+33+qI6yubYgwDmGk4OGeJzjaxm3zY2rrxgj1H7u53knsmfK\n\tg=","X-Received":["by 2002:a05:6000:1a8b:b0:437:8fc0:87de with SMTP id\n\tffacd0b85a97d-4399de2891dmr5756248f8f.28.1772206310588; \n\tFri, 27 Feb 2026 07:31:50 -0800 (PST)","by 2002:a05:6000:1a8b:b0:437:8fc0:87de with SMTP id\n\tffacd0b85a97d-4399de2891dmr5756168f8f.28.1772206309883; \n\tFri, 27 Feb 2026 07:31:49 -0800 (PST)"],"From":"Milan Zamazal <mzamazal@redhat.com>","To":"Javier Tia <floss@jetm.me>","Cc":"libcamera-devel@lists.libcamera.org","Subject":"Re: [PATCH 1/2] ipa: simple: agc: Replace bang-bang controller with\n\tproportional","In-Reply-To":"<85cy1r1vsp.fsf@mzamazal-thinkpadp1gen7.tpbc.csb> (Milan\n\tZamazal's message of \"Thu, 26 Feb 2026 14:25:26 +0100\")","References":"<20260225221859.600869-1-floss@jetm.me>\n\t<20260225221859.600869-2-floss@jetm.me>\n\t<85cy1r1vsp.fsf@mzamazal-thinkpadp1gen7.tpbc.csb>","Date":"Fri, 27 Feb 2026 16:31:48 +0100","Message-ID":"<85o6laurrv.fsf@mzamazal-thinkpadp1gen7.tpbc.csb>","User-Agent":"Gnus/5.13 (Gnus v5.13)","MIME-Version":"1.0","X-Mimecast-Spam-Score":"0","X-Mimecast-MFC-PROC-ID":"JHtCUeqjbfRb0dgHHE22ZnrwmxxQRWkEEBwHKfCBrgY_1772206311","X-Mimecast-Originator":"redhat.com","Content-Type":"text/plain","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}}]