diff --git a/utils/raspberrypi/ctt/ctt.py b/utils/raspberrypi/ctt/ctt.py
index 522933bd..96f1b5e6 100755
--- a/utils/raspberrypi/ctt/ctt.py
+++ b/utils/raspberrypi/ctt/ctt.py
@@ -269,7 +269,7 @@ class Camera:
     colour channel seperately, and then partially corrects for vignetting.
     The extent of the correction depends on the 'luminance_strength' parameter.
     """
-    def alsc_cal(self, luminance_strength, do_alsc_colour, grid_size):
+    def alsc_cal(self, luminance_strength, do_alsc_colour, grid_size, max_gain=8.0):
         if 'rpi.alsc' in self.disable:
             return 1
         print('\nStarting ALSC calibration')
@@ -292,7 +292,7 @@ class Camera:
         call calibration function
         """
         plot = "rpi.alsc" in self.plot
-        alsc_out = alsc_all(self, do_alsc_colour, plot, grid_size)
+        alsc_out = alsc_all(self, do_alsc_colour, plot, grid_size, max_gain=max_gain)
         cal_cr_list, cal_cb_list, luminance_lut, av_corn = alsc_out
         """
         write output to json and finish if not do_alsc_colour
@@ -705,11 +705,13 @@ def run_ctt(json_output, directory, config, log_output, json_template, grid_size
     alsc_d = get_config(configs, "alsc", {}, 'dict')
     do_alsc_colour = get_config(alsc_d, "do_alsc_colour", 1, 'bool')
     luminance_strength = get_config(alsc_d, "luminance_strength", 0.8, 'num')
+    lsc_max_gain = get_config(alsc_d, "max_gain", 8.0, 'num')
     blacklevel = get_config(configs, "blacklevel", -1, 'num')
     macbeth_d = get_config(configs, "macbeth", {}, 'dict')
     mac_small = get_config(macbeth_d, "small", 0, 'bool')
     mac_show = get_config(macbeth_d, "show", 0, 'bool')
     mac_config = (mac_small, mac_show)
+    print("Read lsc_max_gain", lsc_max_gain)
 
     if blacklevel < -1 or blacklevel >= 2**16:
         print('\nInvalid blacklevel, defaulted to 64')
@@ -750,7 +752,7 @@ def run_ctt(json_output, directory, config, log_output, json_template, grid_size
             Cam.json['rpi.black_level']['black_level'] = Cam.blacklevel_16
         Cam.json_remove(disable)
         print('\nSTARTING CALIBRATIONS')
-        Cam.alsc_cal(luminance_strength, do_alsc_colour, grid_size)
+        Cam.alsc_cal(luminance_strength, do_alsc_colour, grid_size, max_gain=lsc_max_gain)
         Cam.geq_cal()
         Cam.lux_cal()
         Cam.noise_cal()
diff --git a/utils/raspberrypi/ctt/ctt_alsc.py b/utils/raspberrypi/ctt/ctt_alsc.py
index 66ce8c14..1d94dfa5 100644
--- a/utils/raspberrypi/ctt/ctt_alsc.py
+++ b/utils/raspberrypi/ctt/ctt_alsc.py
@@ -13,7 +13,7 @@ from mpl_toolkits.mplot3d import Axes3D
 """
 preform alsc calibration on a set of images
 """
-def alsc_all(Cam, do_alsc_colour, plot, grid_size=(16, 12)):
+def alsc_all(Cam, do_alsc_colour, plot, grid_size=(16, 12), max_gain=8.0):
     imgs_alsc = Cam.imgs_alsc
     grid_w, grid_h = grid_size
     """
@@ -24,7 +24,7 @@ def alsc_all(Cam, do_alsc_colour, plot, grid_size=(16, 12)):
     list_cb = []
     list_cg = []
     for Img in imgs_alsc:
-        col, cr, cb, cg, size = alsc(Cam, Img, do_alsc_colour, plot, grid_size=grid_size)
+        col, cr, cb, cg, size = alsc(Cam, Img, do_alsc_colour, plot, grid_size=grid_size, max_gain=max_gain)
         list_col.append(col)
         list_cr.append(cr)
         list_cb.append(cb)
@@ -118,7 +118,7 @@ def alsc_all(Cam, do_alsc_colour, plot, grid_size=(16, 12)):
 """
 calculate g/r and g/b for 32x32 points arranged in a grid for a single image
 """
-def alsc(Cam, Img, do_alsc_colour, plot=False, grid_size=(16, 12)):
+def alsc(Cam, Img, do_alsc_colour, plot=False, grid_size=(16, 12), max_gain=8.0):
     Cam.log += '\nProcessing image: ' + Img.name
     grid_w, grid_h = grid_size
     """
@@ -153,9 +153,12 @@ def alsc(Cam, Img, do_alsc_colour, plot=False, grid_size=(16, 12)):
         median blur to remove peaks and save as float 64
         """
         cr = cv2.medianBlur(cr, 3).astype('float64')
+        cr = cr/np.min(cr)  # gain tables are easier for humans to read if the minimum is 1.0
         cb = cv2.medianBlur(cb, 3).astype('float64')
+        cb = cb/np.min(cb)
         cg = cv2.medianBlur(cg, 3).astype('float64')
         cg = cg/np.min(cg)
+        cg = [min(v, max_gain) for v in cg.flatten()]  # never exceed the max luminance gain
 
         """
         debugging code showing 2D surface plot of vignetting. Quite useful for
@@ -179,7 +182,7 @@ def alsc(Cam, Img, do_alsc_colour, plot=False, grid_size=(16, 12)):
             # print(Img.str)
             plt.show()
 
-        return Img.col, cr.flatten(), cb.flatten(), cg.flatten(), (w, h, dx, dy)
+        return Img.col, cr.flatten(), cb.flatten(), cg, (w, h, dx, dy)
 
     else:
         """
@@ -189,6 +192,7 @@ def alsc(Cam, Img, do_alsc_colour, plot=False, grid_size=(16, 12)):
         cg = np.reshape(1/g, (grid_h, grid_w)).astype('float32')
         cg = cv2.medianBlur(cg, 3).astype('float64')
         cg = cg/np.min(cg)
+        cg = [min(v, max_gain) for v in cg.flatten()]  # never exceed the max luminance gain
 
         if plot:
             hf = plt.figure(figssize=(8, 8))
diff --git a/utils/raspberrypi/ctt/ctt_config_example.json b/utils/raspberrypi/ctt/ctt_config_example.json
index c7f90761..1105862c 100644
--- a/utils/raspberrypi/ctt/ctt_config_example.json
+++ b/utils/raspberrypi/ctt/ctt_config_example.json
@@ -3,7 +3,8 @@
   "plot": [],
   "alsc": {
     "do_alsc_colour": 1,
-    "luminance_strength": 0.5
+    "luminance_strength": 0.8,
+    "max_gain": 8.0
   },
   "awb": {
     "greyworld": 0
@@ -13,4 +14,4 @@
     "small": 0,
     "show": 0
   }
-}
\ No newline at end of file
+}
