diff --git a/src/calng/AgipdCorrection.py b/src/calng/AgipdCorrection.py
index fa8e074be01a36c4d7005450b2274d3d6c0453aa..548f00f2e9fcdaff14c57b3cdf55e0e261858f82 100644
--- a/src/calng/AgipdCorrection.py
+++ b/src/calng/AgipdCorrection.py
@@ -88,9 +88,9 @@ class AgipdCorrection(BaseCorrection):
             .defaultValue(0)
             .reconfigurable()
             .commit(),
-            NODE_ELEMENT(expected)
-            .key("corrections.badPixels.subsetToUse")
-            .displayedName("Bad pixel flags to use")
+            FLOAT_ELEMENT(expected)
+            .key("corrections.relGainXray.gGainValue")
+            .displayedName("G_gain_value")
             .description(
                 "The booleans under this node allow for selecting a subset of bad "
                 "pixel types to take into account when doing bad pixel masking. "
@@ -98,20 +98,16 @@ class AgipdCorrection(BaseCorrection):
                 "be ANDed with this selection. Therefore, if you want to toggle a "
                 "disabled flag back on, please reload constants for this to take "
                 "effect (will be triggered automatically in future version)."
+                "Modern X-ray gain correction constants are absolute. The default "
+                "G_gain_value of 1 means that output is expected to be in keV. If "
+                "this is not desired, one can here specify the mean X-ray gain value "
+                "over all modules to get ADU values out - operator must manually "
+                "find this mean value."
             )
+            .assignmentOptional()
+            .defaultValue(1)
+            .reconfigurable()
             .commit(),
-        )
-        # TODO: DRY / encapsulate
-        for field in BadPixelValues:
-            (
-                BOOL_ELEMENT(expected)
-                .key(f"corrections.badPixels.subsetToUse.{field.name}")
-                .assignmentOptional()
-                .defaultValue(True)
-                .reconfigurable()
-                .commit()
-            )
-        (
             STRING_ELEMENT(expected)
             .key("corrections.badPixels.maskingValue")
             .displayedName("Bad pixel masking value")
@@ -123,8 +119,22 @@ class AgipdCorrection(BaseCorrection):
             .assignmentOptional()
             .defaultValue("float('nan')")
             .reconfigurable()
-            .commit()
+            .commit(),
+            NODE_ELEMENT(expected)
+            .key("corrections.badPixels.subsetToUse")
+            .displayedName("Bad pixel flags to use")
+            .commit(),
         )
+        # TODO: DRY / encapsulate
+        for field in BadPixelValues:
+            (
+                BOOL_ELEMENT(expected)
+                .key(f"corrections.badPixels.subsetToUse.{field.name}")
+                .assignmentOptional()
+                .defaultValue(True)
+                .reconfigurable()
+                .commit()
+            )
 
     @property
     def input_data_shape(self):
@@ -156,6 +166,7 @@ class AgipdCorrection(BaseCorrection):
         self._gpu_runner_init_args = {
             "gain_mode": self.gain_mode,
             "bad_pixel_mask_value": self.bad_pixel_mask_value,
+            "g_gain_value": config.get("corrections.relGainXray.gGainValue"),
         }
 
         self._output_transpose = {
@@ -339,18 +350,14 @@ class AgipdCorrection(BaseCorrection):
             for path in config.getPaths()
         ):
             self._has_updated_bad_pixel_selection = False
-        if config.has("corrections.badPixels.maskingValue"):
-            self.bad_pixel_mask_value = eval(
-                config.get("corrections.badPixels.maskingValue")
-            )
-            self.gpu_runner.set_bad_pixel_mask_value(self.bad_pixel_mask_value)
 
     def postReconfigure(self):
+        # TODO: keep track of which things actually need updating
         super().postReconfigure()
 
-        if self.get("corrections.overrideMdAdditionalOffset"):
+        if self.get("corrections.relGainPc.overrideMdAdditionalOffset"):
             self._override_md_additional_offset = self.get(
-                "corrections.mdAdditionalOffset"
+                "corrections.relGainPc.mdAdditionalOffset"
             )
             self.gpu_runner.md_additional_offset_gpu.override_md_additional_offset(
                 self._override_md_additional_offset
@@ -358,6 +365,13 @@ class AgipdCorrection(BaseCorrection):
         else:
             self._override_md_additional_offset = None
 
+        self.gpu_runner.set_g_gain_value(self.get("corrections.relGainXray.gGainValue"))
+        self._gpu_runner_init_args["g_gain_value"] = self.get("corrections.relGainXray.gGainValue")
+
+        self.bad_pixel_mask_value = eval(self.get("corrections.badPixels.maskingValue"))
+        self.gpu_runner.set_bad_pixel_mask_value(self.bad_pixel_mask_value)
+        self._gpu_runner_init_args["bad_pixel_mask_value"] = self.bad_pixel_mask_value
+
         if not self._has_updated_bad_pixel_selection:
             self._update_bad_pixel_selection()
             self.gpu_runner.override_bad_pixel_flags_to_use(
diff --git a/src/calng/agipd_gpu.py b/src/calng/agipd_gpu.py
index 8526836f66141e16d70bb1b1ee275cb7031bc0b0..fd935ebade2ff0ea9794bc2018271f320f4e0a37 100644
--- a/src/calng/agipd_gpu.py
+++ b/src/calng/agipd_gpu.py
@@ -38,6 +38,7 @@ class AgipdGpuRunner(base_gpu.BaseGpuRunner):
         output_data_dtype=cupy.float32,
         bad_pixel_mask_value=cupy.nan,
         gain_mode=AgipdGainMode.ADAPTIVE_GAIN,
+        g_gain_value=1,
     ):
         self.gain_mode = gain_mode
         if self.gain_mode is AgipdGainMode.ADAPTIVE_GAIN:
@@ -71,6 +72,7 @@ class AgipdGpuRunner(base_gpu.BaseGpuRunner):
         self.rel_gain_xray_map_gpu = cupy.ones(self.map_shape, dtype=cupy.float32)
         self.bad_pixel_map_gpu = cupy.zeros(self.gm_map_shape, dtype=cupy.uint32)
         self.set_bad_pixel_mask_value(bad_pixel_mask_value)
+        self.set_g_gain_value(g_gain_value)
 
         self.update_block_size((1, 1, 64))
 
@@ -144,6 +146,7 @@ class AgipdGpuRunner(base_gpu.BaseGpuRunner):
         # constant shape: y, x, memory cell
         if slopes_ff_map.shape[2] == 2:
             # old format, is per pixel only (shape is y, x, 2)
+            # note: we should not support this in online
             slopes_ff_map = np.broadcast_to(
                 slopes_ff_map[..., 0][..., np.newaxis],
                 (self.pixels_y, self.pixels_x, self.constant_memory_cells),
@@ -154,6 +157,9 @@ class AgipdGpuRunner(base_gpu.BaseGpuRunner):
             ...
         self.rel_gain_xray_map_gpu.set(np.transpose(slopes_ff_map).astype(np.float32))
 
+    def set_g_gain_value(self, override_value):
+        self.g_gain_value = cupy.float32(override_value)
+
     def load_bad_pixels_map(self, bad_pixels_map, override_flags_to_use=None):
         print(f"Loading bad pixels with shape: {bad_pixels_map.shape}")
         # will simply OR with already loaded, does not take into account which ones
@@ -233,6 +239,7 @@ class AgipdGpuRunner(base_gpu.BaseGpuRunner):
                 self.rel_gain_pc_map_gpu,
                 self.md_additional_offset_gpu,
                 self.rel_gain_xray_map_gpu,
+                self.g_gain_value,
                 self.bad_pixel_map_gpu,
                 self.bad_pixel_mask_value,
                 self.gain_map_gpu,
diff --git a/src/calng/agipd_gpu_kernels.cpp b/src/calng/agipd_gpu_kernels.cpp
index 7c8a65fe61a04796fdeed06736feb287af5d0568..192b71930c358246386dbfecba50a05741add959 100644
--- a/src/calng/agipd_gpu_kernels.cpp
+++ b/src/calng/agipd_gpu_kernels.cpp
@@ -114,6 +114,7 @@ extern "C" {
 			} else {
 				if (corr_flags & OFFSET) {
 					corrected -= offset_map[gm_map_index];
+					// TODO: optionally reassign gain stage for this pixel based on new value
 				}
 				// TODO: baseline shift
 				if (corr_flags & REL_GAIN_PC) {
@@ -123,9 +124,7 @@ extern "C" {
 					}
 				}
 				if (corr_flags & REL_GAIN_XRAY) {
-					// TODO
-					//corrected *= rel_gain_xray_map[map_index];
-					// TODO: G_gain_value
+					corrected = (corrected / rel_gain_xray_map[map_index]) * g_gain_value;
 				}
 			}
 
diff --git a/src/calng/base_correction.py b/src/calng/base_correction.py
index 12d4a96e68333b11d2f01831215c489cdbb4a537..68573a3eb99864f66eb089a243778123ed1a6cf6 100644
--- a/src/calng/base_correction.py
+++ b/src/calng/base_correction.py
@@ -582,9 +582,6 @@ class BaseCorrection(PythonDevice):
         self._has_updated_shapes = True
 
     def _update_rate_and_state(self):
-        if not self.get("state") is State.PROCESSING:
-            return
-
         self._buffered_status_update.set("performance.rate", self._rate_tracker.get())
         self._buffered_status_update.set(
             "performance.processingDuration", self._processing_time_ema.get() * 1000