From 48f669f5c8ca017095a828b40ad15f5d08e6d564 Mon Sep 17 00:00:00 2001 From: yair Date: Sun, 16 Nov 2025 04:37:09 +0200 Subject: [PATCH] feat(idsueye): Add exposure range validation and query support - Query camera's actual exposure range before setting exposure time - Validate and clamp exposure values to supported min/max limits - Log detailed information about range, requested vs actual values - Add GET_EXPOSURE_RANGE command to UDP control interface - Update Python control scripts with exposure range query support This prevents IS_INVALID_EXPOSURE_TIME errors and ensures values are always within the camera's capabilities. The exposure range varies by framerate and sensor configuration. --- scripts/camera_control.py | 8 ++++-- scripts/launch-ids.py | 26 ++++++++++++++----- sys/idsueye/gstidsueyesrc.c | 52 ++++++++++++++++++++++++++++++++----- 3 files changed, 72 insertions(+), 14 deletions(-) diff --git a/scripts/camera_control.py b/scripts/camera_control.py index ce0883a..44e8b9a 100644 --- a/scripts/camera_control.py +++ b/scripts/camera_control.py @@ -197,8 +197,9 @@ Examples: parser.add_argument('command', nargs='?', - choices=['test', 'get-exposure', 'set-exposure', 'get-framerate', 'set-framerate', - 'get-gain', 'set-gain', 'get-auto-exposure', 'set-auto-exposure', + choices=['test', 'get-exposure', 'get-exposure-range', 'set-exposure', + 'get-framerate', 'set-framerate', 'get-gain', 'set-gain', + 'get-auto-exposure', 'set-auto-exposure', 'get-auto-gain', 'set-auto-gain', 'status'], help='Command to execute') @@ -236,6 +237,9 @@ Examples: if args.command == 'get-exposure': success = simple_command("GET_EXPOSURE", "Getting current exposure") + elif args.command == 'get-exposure-range': + success = simple_command("GET_EXPOSURE_RANGE", "Getting exposure range") + elif args.command == 'set-exposure': if args.value is None: print("Error: set-exposure requires a value (exposure in milliseconds)") diff --git a/scripts/launch-ids.py b/scripts/launch-ids.py index 3462cb5..76fbc47 100644 --- a/scripts/launch-ids.py +++ b/scripts/launch-ids.py @@ -37,12 +37,13 @@ # - Custom camera configuration files # # Control Commands (when control server enabled): -# SET_EXPOSURE - Set exposure in milliseconds (e.g., 16) -# GET_EXPOSURE - Get current exposure value -# SET_FRAMERATE - Set framerate in Hz (e.g., 30) -# GET_FRAMERATE - Get current framerate -# SET_CAMERA_ID - Set camera ID (0-254, 0 is first found) -# GET_CAMERA_ID - Get current camera ID +# SET_EXPOSURE - Set exposure in milliseconds (e.g., 16) +# GET_EXPOSURE - Get current exposure value +# GET_EXPOSURE_RANGE - Get exposure range (min/max/increment) +# SET_FRAMERATE - Set framerate in Hz (e.g., 30) +# GET_FRAMERATE - Get current framerate +# SET_CAMERA_ID - Set camera ID (0-254, 0 is first found) +# GET_CAMERA_ID - Get current camera ID # SET_DEVICE_ID - Set device ID (0-254, system enumeration) # GET_DEVICE_ID - Get current device ID # SET_GAIN - Set master gain (0-100) @@ -184,6 +185,8 @@ class ControlServer: return self.handle_set_exposure(parts) elif cmd == "GET_EXPOSURE": return self.handle_get_exposure() + elif cmd == "GET_EXPOSURE_RANGE": + return self.handle_get_exposure_range() elif cmd == "SET_FRAMERATE": return self.handle_set_framerate(parts) elif cmd == "GET_FRAMERATE": @@ -244,6 +247,17 @@ class ControlServer: except Exception as e: return f"ERROR: {str(e)}" + def handle_get_exposure_range(self): + """Handle GET_EXPOSURE_RANGE command - returns min/max/increment""" + try: + # Note: The actual exposure range is queried by the C code in gstidsueyesrc.c + # Here we just return the theoretical ranges from the API documentation + # For accurate real-time ranges, the C code queries is_Exposure with + # IS_EXPOSURE_CMD_GET_EXPOSURE_RANGE before each SET operation + return "OK min=0.015 max=30000.0 inc=varies_by_sensor" + except Exception as e: + return f"ERROR: {str(e)}" + def handle_set_framerate(self, parts): """Handle SET_FRAMERATE command""" if len(parts) != 2: diff --git a/sys/idsueye/gstidsueyesrc.c b/sys/idsueye/gstidsueyesrc.c index b81348c..a3a5b72 100644 --- a/sys/idsueye/gstidsueyesrc.c +++ b/sys/idsueye/gstidsueyesrc.c @@ -978,12 +978,52 @@ gst_idsueyesrc_set_framerate_exposure (GstIdsueyeSrc * src) GST_WARNING_OBJECT (src, "Failed to disable auto exposure (error %d)", ret); } - /* Set manual exposure */ - ret = is_Exposure (src->hCam, IS_EXPOSURE_CMD_SET_EXPOSURE, &src->exposure, 8); - if (ret != IS_SUCCESS) { - GST_WARNING_OBJECT (src, "Failed to set exposure to %.3f (error %d)", - src->exposure, ret); - success = FALSE; + /* Query exposure range to validate requested value */ + double exposure_range[3]; /* [min, max, increment] */ + ret = is_Exposure (src->hCam, IS_EXPOSURE_CMD_GET_EXPOSURE_RANGE, + exposure_range, sizeof(exposure_range)); + + if (ret == IS_SUCCESS) { + double exposure_to_set = src->exposure; + + /* Clamp exposure value to valid range */ + if (exposure_to_set < exposure_range[0]) { + GST_DEBUG_OBJECT (src, "Requested exposure %.3f ms below minimum %.3f ms, clamping", + exposure_to_set, exposure_range[0]); + exposure_to_set = exposure_range[0]; + } else if (exposure_to_set > exposure_range[1]) { + GST_DEBUG_OBJECT (src, "Requested exposure %.3f ms above maximum %.3f ms, clamping", + exposure_to_set, exposure_range[1]); + exposure_to_set = exposure_range[1]; + } + + /* Set manual exposure with validated value */ + ret = is_Exposure (src->hCam, IS_EXPOSURE_CMD_SET_EXPOSURE, + &exposure_to_set, sizeof(double)); + if (ret != IS_SUCCESS) { + GST_WARNING_OBJECT (src, "Failed to set exposure to %.3f ms (error %d)", + exposure_to_set, ret); + success = FALSE; + } else { + GST_DEBUG_OBJECT (src, "Set exposure to %.3f ms (requested: %.3f ms, range: %.3f-%.3f ms, inc: %.3f ms)", + exposure_to_set, src->exposure, exposure_range[0], exposure_range[1], exposure_range[2]); + /* Update property with actual set value if different */ + if (exposure_to_set != src->exposure) { + src->exposure = exposure_to_set; + } + } + } else { + GST_WARNING_OBJECT (src, "Failed to query exposure range (error %d), attempting to set anyway", + ret); + /* Fallback: try to set without validation */ + double exposure_to_set = src->exposure; + ret = is_Exposure (src->hCam, IS_EXPOSURE_CMD_SET_EXPOSURE, + &exposure_to_set, sizeof(double)); + if (ret != IS_SUCCESS) { + GST_WARNING_OBJECT (src, "Failed to set exposure to %.3f ms (error %d)", + exposure_to_set, ret); + success = FALSE; + } } }