diff --git a/scripts/camera_control.py b/scripts/camera_control.py index 20930e3..df8582e 100644 --- a/scripts/camera_control.py +++ b/scripts/camera_control.py @@ -13,6 +13,8 @@ Usage: uv run scripts/camera_control.py set-exposure 10 # Set exposure to 10ms uv run scripts/camera_control.py get-framerate # Get current framerate uv run scripts/camera_control.py set-framerate 30 # Set framerate to 30fps + uv run scripts/camera_control.py get-gain # Get current gain + uv run scripts/camera_control.py set-gain 50 # Set gain to 50 uv run scripts/camera_control.py status # Get pipeline status This script provides both individual control commands and full test suite functionality @@ -178,6 +180,8 @@ Examples: %(prog)s set-exposure 10 # Set exposure to 10ms %(prog)s get-framerate # Get current framerate %(prog)s set-framerate 30 # Set framerate to 30fps + %(prog)s get-gain # Get current gain + %(prog)s set-gain 50 # Set gain to 50 %(prog)s status # Get pipeline status """, formatter_class=argparse.RawDescriptionHelpFormatter @@ -185,7 +189,8 @@ Examples: parser.add_argument('command', nargs='?', - choices=['test', 'get-exposure', 'set-exposure', 'get-framerate', 'set-framerate', 'status'], + choices=['test', 'get-exposure', 'set-exposure', 'get-framerate', 'set-framerate', + 'get-gain', 'set-gain', 'status'], help='Command to execute') parser.add_argument('value', @@ -239,6 +244,21 @@ Examples: sys.exit(1) success = simple_command(f"SET_FRAMERATE {args.value}", f"Setting framerate to {args.value}fps") + elif args.command == 'get-gain': + success = simple_command("GET_GAIN", "Getting current gain") + + elif args.command == 'set-gain': + if args.value is None: + print("Error: set-gain requires a value (0-100, 0 for auto)") + parser.print_help() + sys.exit(1) + # Convert to int for gain + gain_value = int(args.value) + if gain_value < 0 or gain_value > 100: + print("Error: Gain must be between 0 and 100 (0 for auto)") + sys.exit(1) + success = simple_command(f"SET_GAIN {gain_value}", f"Setting gain to {gain_value}") + elif args.command == 'status': success = simple_command("STATUS", "Getting pipeline status") diff --git a/scripts/launch-ids.py b/scripts/launch-ids.py index 48dfa49..7fd91f7 100644 --- a/scripts/launch-ids.py +++ b/scripts/launch-ids.py @@ -42,6 +42,8 @@ # 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, 0 for auto) +# GET_GAIN - Get current gain value # STATUS - Get pipeline status and current settings # # Example Control Usage: @@ -131,7 +133,8 @@ class ControlServer: print(f"Control server listening on UDP port {self.port}") print(" Commands: SET_EXPOSURE , GET_EXPOSURE, SET_FRAMERATE , GET_FRAMERATE,") - print(" SET_CAMERA_ID , GET_CAMERA_ID, SET_DEVICE_ID , GET_DEVICE_ID, STATUS") + print(" SET_CAMERA_ID , GET_CAMERA_ID, SET_DEVICE_ID , GET_DEVICE_ID,") + print(" SET_GAIN , GET_GAIN, STATUS") while self.running: try: @@ -185,6 +188,10 @@ class ControlServer: return self.handle_set_device_id(parts) elif cmd == "GET_DEVICE_ID": return self.handle_get_device_id() + elif cmd == "SET_GAIN": + return self.handle_set_gain(parts) + elif cmd == "GET_GAIN": + return self.handle_get_gain() elif cmd == "STATUS": return self.handle_status() else: @@ -302,6 +309,33 @@ class ControlServer: except Exception as e: return f"ERROR: {str(e)}" + def handle_set_gain(self, parts): + """Handle SET_GAIN command""" + if len(parts) != 2: + return "ERROR INVALID_SYNTAX: Usage: SET_GAIN " + + try: + value = int(parts[1]) + if value < 0 or value > 100: + return "ERROR OUT_OF_RANGE: Gain must be 0-100 (0 for auto)" + + self.src.set_property("gain", value) + actual = self.src.get_property("gain") + return f"OK {actual}" + + except ValueError: + return "ERROR INVALID_SYNTAX: Gain must be an integer" + except Exception as e: + return f"ERROR: {str(e)}" + + def handle_get_gain(self): + """Handle GET_GAIN command""" + try: + value = self.src.get_property("gain") + return f"OK {value}" + except Exception as e: + return f"ERROR: {str(e)}" + def handle_status(self): """Handle STATUS command""" try: @@ -309,6 +343,7 @@ class ControlServer: framerate = self.src.get_property("framerate") camera_id = self.src.get_property("camera-id") device_id = self.src.get_property("device-id") + gain = self.src.get_property("gain") # Get pipeline state state = "UNKNOWN" @@ -316,7 +351,7 @@ class ControlServer: _, current_state, _ = self.pipeline.get_state(0) state = current_state.value_nick.upper() - return f"OK exposure={exposure} framerate={framerate} camera_id={camera_id} device_id={device_id} state={state}" + return f"OK exposure={exposure} framerate={framerate} camera_id={camera_id} device_id={device_id} gain={gain} state={state}" except Exception as e: return f"ERROR: {str(e)}" @@ -382,6 +417,13 @@ Examples: metavar='ID', help='Device ID (system enumeration, 0 is first, 0-254)' ) + camera_group.add_argument( + '--gain', '-g', + type=int, + default=0, + metavar='GAIN', + help='Master gain (0-100, 0 for auto/default)' + ) # Video processing video_group = parser.add_argument_group('Video Processing') @@ -470,6 +512,9 @@ Examples: if args.device_id < 0 or args.device_id > 254: parser.error(f"Device ID must be between 0 and 254, got {args.device_id}") + if args.gain < 0 or args.gain > 100: + parser.error(f"Gain must be between 0 and 100, got {args.gain}") + if args.port < 1 or args.port > 65535: parser.error(f"UDP port must be between 1 and 65535, got {args.port}") @@ -528,6 +573,9 @@ src.set_property("framerate", args.framerate) src.set_property("camera-id", args.camera_id) src.set_property("device-id", args.device_id) +# Gain (0 for auto/default) +src.set_property("gain", args.gain) + # Video crop to remove bottom pixels (if enabled) elements_to_link = [src] if args.crop_bottom > 0: @@ -671,6 +719,7 @@ if not args.quiet: print(f"Device ID: {args.device_id}") print(f"Exposure: {args.exposure} ms") print(f"Framerate: {args.framerate} Hz") + print(f"Gain: {args.gain} (0=auto)") if args.crop_bottom > 0: print(f"Crop bottom: {args.crop_bottom} pixels") else: diff --git a/sys/idsueye/gstidsueyesrc.c b/sys/idsueye/gstidsueyesrc.c index 44158bd..d6dceec 100644 --- a/sys/idsueye/gstidsueyesrc.c +++ b/sys/idsueye/gstidsueyesrc.c @@ -73,7 +73,8 @@ enum PROP_NUM_CAPTURE_BUFFERS, PROP_TIMEOUT, PROP_EXPOSURE, - PROP_FRAMERATE + PROP_FRAMERATE, + PROP_GAIN }; #define DEFAULT_PROP_CAMERA_ID 0 @@ -83,6 +84,7 @@ enum #define DEFAULT_PROP_TIMEOUT 1000 #define DEFAULT_PROP_EXPOSURE 0 #define DEFAULT_PROP_FRAMERATE 0 +#define DEFAULT_PROP_GAIN 0 /* pad templates */ @@ -162,6 +164,11 @@ gst_idsueyesrc_class_init (GstIdsueyeSrcClass * klass) "Framerate in frames per second", 0, G_MAXDOUBLE, DEFAULT_PROP_FRAMERATE, (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); + g_object_class_install_property (gobject_class, PROP_GAIN, + g_param_spec_int ("gain", "Master Gain", + "Master gain (0-100, 0 for auto)", 0, 100, + DEFAULT_PROP_GAIN, + (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); } static void @@ -196,6 +203,7 @@ gst_idsueyesrc_init (GstIdsueyeSrc * src) src->timeout = DEFAULT_PROP_TIMEOUT; src->exposure = DEFAULT_PROP_EXPOSURE; src->framerate = DEFAULT_PROP_FRAMERATE; + src->gain = DEFAULT_PROP_GAIN; src->stop_requested = FALSE; src->caps = NULL; @@ -240,6 +248,17 @@ gst_idsueyesrc_set_property (GObject * object, guint property_id, gst_idsueyesrc_set_framerate_exposure (src); } break; + case PROP_GAIN: + src->gain = g_value_get_int (value); + if (src->is_started) { + INT ret = is_SetHardwareGain (src->hCam, src->gain, IS_IGNORE_PARAMETER, + IS_IGNORE_PARAMETER, IS_IGNORE_PARAMETER); + if (ret != IS_SUCCESS) { + GST_WARNING_OBJECT (src, "Failed to set gain to %d (error %d)", + src->gain, ret); + } + } + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; @@ -277,6 +296,9 @@ gst_idsueyesrc_get_property (GObject * object, guint property_id, case PROP_FRAMERATE: g_value_set_double (value, src->framerate); break; + case PROP_GAIN: + g_value_set_int (value, src->gain); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; @@ -911,6 +933,17 @@ gst_idsueyesrc_set_framerate_exposure (GstIdsueyeSrc * src) success = FALSE; } + /* Set gain if specified (0 means use default/auto) */ + if (src->gain > 0) { + ret = is_SetHardwareGain (src->hCam, src->gain, IS_IGNORE_PARAMETER, + IS_IGNORE_PARAMETER, IS_IGNORE_PARAMETER); + if (ret != IS_SUCCESS) { + GST_WARNING_OBJECT (src, "Failed to set gain to %d (error %d)", + src->gain, ret); + success = FALSE; + } + } + return success; } diff --git a/sys/idsueye/gstidsueyesrc.h b/sys/idsueye/gstidsueyesrc.h index 0870703..79b092d 100644 --- a/sys/idsueye/gstidsueyesrc.h +++ b/sys/idsueye/gstidsueyesrc.h @@ -57,6 +57,7 @@ struct _GstIdsueyeSrc gint timeout; gdouble exposure; gdouble framerate; + gint gain; GstClockTime acq_start_time; guint32 last_frame_count;