470 lines
12 KiB
C
470 lines
12 KiB
C
/*
|
|
* This program tries various ISOs for best ISO with a shutterspeed limit
|
|
*
|
|
* compile with: gcc -Wall -o best-iso best-iso.c -lgphoto2 -lgphoto2_port
|
|
*
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#include <stdio.h>
|
|
#include <stdarg.h>
|
|
#include <string.h>
|
|
#include <gphoto2/gphoto2.h>
|
|
|
|
#if !defined (O_BINARY)
|
|
/*To have portable binary open() on *nix and on Windows */
|
|
#define O_BINARY 0
|
|
#endif
|
|
|
|
#define DEBUG
|
|
|
|
static int aperture;
|
|
static float shutterspeed;
|
|
|
|
static void errordumper(GPLogLevel level, const char *domain, const char *str,
|
|
void *data) {
|
|
fprintf(stdout, "%s\n", str);
|
|
}
|
|
|
|
static void
|
|
ctx_error_func (GPContext *context, const char *str, void *data)
|
|
{
|
|
fprintf (stderr, "\n*** Contexterror *** \n%s\n",str);
|
|
fflush (stderr);
|
|
}
|
|
|
|
static void
|
|
ctx_status_func (GPContext *context, const char *str, void *data)
|
|
{
|
|
fprintf (stderr, "%s\n", str);
|
|
fflush (stderr);
|
|
}
|
|
|
|
static
|
|
GPContext* sample_create_context() {
|
|
GPContext *context;
|
|
|
|
/* This is the mandatory part */
|
|
context = gp_context_new();
|
|
|
|
/* All the parts below are optional! */
|
|
gp_context_set_error_func (context, ctx_error_func, NULL);
|
|
gp_context_set_status_func (context, ctx_status_func, NULL);
|
|
|
|
/* also:
|
|
gp_context_set_cancel_func (p->context, ctx_cancel_func, p);
|
|
gp_context_set_message_func (p->context, ctx_message_func, p);
|
|
if (isatty (STDOUT_FILENO))
|
|
gp_context_set_progress_funcs (p->context,
|
|
ctx_progress_start_func, ctx_progress_update_func,
|
|
ctx_progress_stop_func, p);
|
|
*/
|
|
return context;
|
|
}
|
|
/*
|
|
* This function looks up a label or key entry of
|
|
* a configuration widget.
|
|
* The functions descend recursively, so you can just
|
|
* specify the last component.
|
|
*/
|
|
|
|
static int
|
|
_lookup_widget(CameraWidget*widget, const char *key, CameraWidget **child) {
|
|
int ret;
|
|
ret = gp_widget_get_child_by_name (widget, key, child);
|
|
if (ret < GP_OK)
|
|
ret = gp_widget_get_child_by_label (widget, key, child);
|
|
return ret;
|
|
}
|
|
|
|
/* Gets a string configuration value.
|
|
* This can be:
|
|
* - A Text widget
|
|
* - The current selection of a Radio Button choice
|
|
* - The current selection of a Menu choice
|
|
*
|
|
* Sample (for Canons eg):
|
|
* get_config_value_string (camera, "owner", &ownerstr, context);
|
|
*/
|
|
static int
|
|
get_config_value_string (Camera *camera, const char *key, char **str, GPContext *context) {
|
|
CameraWidget *widget = NULL, *child = NULL;
|
|
CameraWidgetType type;
|
|
int ret;
|
|
char *val;
|
|
|
|
ret = gp_camera_get_config (camera, &widget, context);
|
|
if (ret < GP_OK) {
|
|
fprintf (stderr, "camera_get_config failed: %d\n", ret);
|
|
return ret;
|
|
}
|
|
ret = _lookup_widget (widget, key, &child);
|
|
if (ret < GP_OK) {
|
|
fprintf (stderr, "lookup widget failed: %d\n", ret);
|
|
goto out;
|
|
}
|
|
|
|
/* This type check is optional, if you know what type the label
|
|
* has already. If you are not sure, better check. */
|
|
ret = gp_widget_get_type (child, &type);
|
|
if (ret < GP_OK) {
|
|
fprintf (stderr, "widget get type failed: %d\n", ret);
|
|
goto out;
|
|
}
|
|
switch (type) {
|
|
case GP_WIDGET_MENU:
|
|
case GP_WIDGET_RADIO:
|
|
case GP_WIDGET_TEXT:
|
|
break;
|
|
default:
|
|
fprintf (stderr, "widget has bad type %d\n", type);
|
|
ret = GP_ERROR_BAD_PARAMETERS;
|
|
goto out;
|
|
}
|
|
|
|
/* This is the actual query call. Note that we just
|
|
* a pointer reference to the string, not a copy... */
|
|
ret = gp_widget_get_value (child, &val);
|
|
if (ret < GP_OK) {
|
|
fprintf (stderr, "could not query widget value: %d\n", ret);
|
|
goto out;
|
|
}
|
|
/* Create a new copy for our caller. */
|
|
*str = strdup (val);
|
|
out:
|
|
gp_widget_free (widget);
|
|
return ret;
|
|
}
|
|
|
|
|
|
/* Sets a string configuration value.
|
|
* This can set for:
|
|
* - A Text widget
|
|
* - The current selection of a Radio Button choice
|
|
* - The current selection of a Menu choice
|
|
*
|
|
* Sample (for Canons eg):
|
|
* get_config_value_string (camera, "owner", &ownerstr, context);
|
|
*/
|
|
static int
|
|
set_config_value_string (Camera *camera, const char *key, const char *val, GPContext *context) {
|
|
CameraWidget *widget = NULL, *child = NULL;
|
|
CameraWidgetType type;
|
|
int ret;
|
|
|
|
ret = gp_camera_get_config (camera, &widget, context);
|
|
if (ret < GP_OK) {
|
|
fprintf (stderr, "camera_get_config failed: %d\n", ret);
|
|
return ret;
|
|
}
|
|
ret = _lookup_widget (widget, key, &child);
|
|
if (ret < GP_OK) {
|
|
fprintf (stderr, "lookup widget failed: %d\n", ret);
|
|
goto out;
|
|
}
|
|
|
|
/* This type check is optional, if you know what type the label
|
|
* has already. If you are not sure, better check. */
|
|
ret = gp_widget_get_type (child, &type);
|
|
if (ret < GP_OK) {
|
|
fprintf (stderr, "widget get type failed: %d\n", ret);
|
|
goto out;
|
|
}
|
|
switch (type) {
|
|
case GP_WIDGET_MENU:
|
|
case GP_WIDGET_RADIO:
|
|
case GP_WIDGET_TEXT:
|
|
/* This is the actual set call. Note that we keep
|
|
* ownership of the string and have to free it if necessary.
|
|
*/
|
|
ret = gp_widget_set_value (child, val);
|
|
if (ret < GP_OK) {
|
|
fprintf (stderr, "could not set widget value: %d\n", ret);
|
|
goto out;
|
|
}
|
|
break;
|
|
case GP_WIDGET_TOGGLE: {
|
|
int ival;
|
|
|
|
sscanf(val,"%d",&ival);
|
|
ret = gp_widget_set_value (child, &ival);
|
|
if (ret < GP_OK) {
|
|
fprintf (stderr, "could not set widget value: %d\n", ret);
|
|
goto out;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
fprintf (stderr, "widget has bad type %d\n", type);
|
|
ret = GP_ERROR_BAD_PARAMETERS;
|
|
goto out;
|
|
}
|
|
|
|
/* This stores it on the camera again */
|
|
ret = gp_camera_set_config (camera, widget, context);
|
|
if (ret < GP_OK) {
|
|
fprintf (stderr, "camera_set_config failed: %d\n", ret);
|
|
return ret;
|
|
}
|
|
out:
|
|
gp_widget_free (widget);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int
|
|
camera_tether(Camera *camera, GPContext *context) {
|
|
int fd, retval;
|
|
CameraFile *file;
|
|
CameraEventType evttype;
|
|
CameraFilePath *path;
|
|
void *evtdata;
|
|
|
|
printf("Tethering...\n");
|
|
|
|
while (1) {
|
|
retval = gp_camera_wait_for_event (camera, 2000, &evttype, &evtdata, context);
|
|
if (retval != GP_OK)
|
|
return retval;
|
|
switch (evttype) {
|
|
case GP_EVENT_FILE_ADDED:
|
|
path = (CameraFilePath*)evtdata;
|
|
printf("File added on the camera: %s/%s\n", path->folder, path->name);
|
|
|
|
fd = open(path->name, O_CREAT | O_WRONLY | O_BINARY, 0644);
|
|
retval = gp_file_new_from_fd(&file, fd);
|
|
printf(" Downloading %s...\n", path->name);
|
|
retval = gp_camera_file_get(camera, path->folder, path->name,
|
|
GP_FILE_TYPE_NORMAL, file, context);
|
|
|
|
printf(" Deleting %s on camera...\n", path->name);
|
|
retval = gp_camera_file_delete(camera, path->folder, path->name, context);
|
|
gp_file_free(file);
|
|
break;
|
|
case GP_EVENT_FOLDER_ADDED:
|
|
path = (CameraFilePath*)evtdata;
|
|
printf("Folder added on camera: %s / %s\n", path->folder, path->name);
|
|
break;
|
|
case GP_EVENT_CAPTURE_COMPLETE:
|
|
printf("Capture Complete.\n");
|
|
break;
|
|
case GP_EVENT_TIMEOUT:
|
|
printf("Timeout.\n");
|
|
return GP_OK;
|
|
case GP_EVENT_UNKNOWN:
|
|
if (evtdata) {
|
|
char *val;
|
|
int ret;
|
|
|
|
/* printf("Unknown event: %s.\n", (char*)evtdata); */
|
|
|
|
/* 3 eos properties hardcoded */
|
|
if (strstr(evtdata,"d103")) {
|
|
ret = get_config_value_string(camera,"iso",&val,context);
|
|
if (ret == GP_OK) {
|
|
printf("ISO is %s\n", val);
|
|
free(val);
|
|
}
|
|
}
|
|
if (strstr(evtdata,"d102")) {
|
|
ret = get_config_value_string(camera,"shutterspeed",&val,context);
|
|
if (ret == GP_OK) {
|
|
if (strchr(val,'/')) {
|
|
int zaehler,nenner;
|
|
sscanf(val,"%d/%d",&zaehler,&nenner);
|
|
shutterspeed = 1.0*zaehler/nenner;
|
|
} else {
|
|
if (!sscanf(val,"%g",&shutterspeed))
|
|
shutterspeed = 0.0;
|
|
}
|
|
printf("Shutterspeed is %s (%g)\n", val, shutterspeed);
|
|
free(val);
|
|
}
|
|
}
|
|
if (strstr(evtdata,"d101")) {
|
|
ret = get_config_value_string(camera,"aperture",&val,context);
|
|
if (ret == GP_OK) {
|
|
float ap;
|
|
|
|
sscanf(val,"%g",&ap);
|
|
aperture = 10*ap;
|
|
printf("Aperture is %s (%d)\n", val, aperture);
|
|
free (val);
|
|
}
|
|
}
|
|
} else {
|
|
printf("Unknown event.\n");
|
|
}
|
|
break;
|
|
default:
|
|
printf("Type %d?\n", evttype);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
int
|
|
main(int argc, char **argv) {
|
|
Camera *camera;
|
|
int retval, iso, tries;
|
|
char buf[20];
|
|
GPContext *context = sample_create_context();
|
|
int fd;
|
|
CameraFile *file;
|
|
CameraFilePath camera_file_path;
|
|
|
|
gp_log_add_func(GP_LOG_ERROR, errordumper, NULL);
|
|
gp_camera_new(&camera);
|
|
|
|
retval = gp_camera_init(camera, context);
|
|
if (retval != GP_OK) {
|
|
printf(" camera failed to initialize: %d\n", retval);
|
|
exit (1);
|
|
}
|
|
|
|
retval = camera_tether(camera, context);
|
|
if (retval != GP_OK) {
|
|
printf("Tether error: %d\n", retval);
|
|
exit (1);
|
|
}
|
|
|
|
iso = 100;
|
|
|
|
shutterspeed = 0.0;
|
|
aperture = 0;
|
|
|
|
#if 1
|
|
gp_file_new (&file);
|
|
retval = gp_camera_capture_preview (camera, file, context);
|
|
if (retval != GP_OK) {
|
|
printf("Preview capture error: %d\n", retval);
|
|
exit (1);
|
|
}
|
|
#endif
|
|
|
|
while (1) {
|
|
sprintf(buf,"%d",iso);
|
|
printf("Setting ISO to %d\n",iso);
|
|
|
|
retval = set_config_value_string(camera, "iso", buf, context);
|
|
if (retval != GP_OK) {
|
|
printf(" failed setting ISO to %d: %d\n", iso, retval);
|
|
|
|
/* usually also happens if too dark */
|
|
exit (1);
|
|
}
|
|
|
|
printf("eosremoterelease half press\n");
|
|
retval = set_config_value_string(camera,"eosremoterelease", "Press Half", context);
|
|
if (retval != GP_OK) {
|
|
printf(" failed pressing shutter to half: %d\n", retval);
|
|
exit (1);
|
|
}
|
|
|
|
retval = camera_tether(camera, context);
|
|
if (retval != GP_OK) {
|
|
printf("Tether error: %d\n", retval);
|
|
exit (1);
|
|
}
|
|
|
|
if (shutterspeed < 0.000000001) {
|
|
printf("Camera did not report shutterspeed?\n");
|
|
}
|
|
|
|
printf("eosremoterelease release\n");
|
|
retval = set_config_value_string(camera,"eosremoterelease", "Release Half", context);
|
|
if (retval != GP_OK) {
|
|
printf(" failed releasing shutter button from half: %d\n", retval);
|
|
exit (1);
|
|
}
|
|
|
|
if (shutterspeed < 30.0) {
|
|
|
|
printf("ISO %d gets Aperture %g and Shutterspeed %g\n", iso, aperture/10.0, shutterspeed);
|
|
|
|
break;
|
|
}
|
|
/* ISO always goes up in 2 multiplicator steps */
|
|
iso = iso*2;
|
|
}
|
|
|
|
/* we can take the picture now */
|
|
|
|
#if 0
|
|
|
|
printf("Capturing.\n");
|
|
|
|
retval = gp_camera_capture(camera, GP_CAPTURE_IMAGE, &camera_file_path, context);
|
|
if (retval != GP_OK) {
|
|
printf(" capture failed: %d\n", retval);
|
|
exit(1);
|
|
}
|
|
|
|
printf("Pathname on the camera: %s/%s\n", camera_file_path.folder, camera_file_path.name);
|
|
|
|
fd = open("capture.jpg", O_CREAT | O_WRONLY | O_BINARY, 0644);
|
|
|
|
retval = gp_file_new_from_fd(&file, fd);
|
|
if (retval != GP_OK) printf(" gp_file_new failed: %d\n", retval);
|
|
|
|
retval = gp_camera_file_get(camera, camera_file_path.folder, camera_file_path.name,
|
|
GP_FILE_TYPE_NORMAL, file, context);
|
|
if (retval != GP_OK) printf(" file_get failed: %d\n", retval);
|
|
|
|
printf("Deleting downloaded image.\n");
|
|
retval = gp_camera_file_delete(camera, camera_file_path.folder, camera_file_path.name,
|
|
context);
|
|
if (retval != GP_OK) printf(" Retval: %d\n", retval);
|
|
|
|
gp_file_free(file);
|
|
|
|
#endif
|
|
#if 1
|
|
/* SAMPLE Capture code with eosremoterelease ... normal gp_camera_capture_image also works. */
|
|
printf("eosremoterelease release\n");
|
|
retval = set_config_value_string(camera,"eosremoterelease", "Immediate", context);
|
|
|
|
if (retval != GP_OK) {
|
|
printf(" failed pressing shutter button full: %d\n", retval);
|
|
exit (1);
|
|
}
|
|
|
|
retval = set_config_value_string(camera,"eosremoterelease", "Release Full", context);
|
|
|
|
if (retval != GP_OK) {
|
|
printf(" failed releasing shutter button full: %d\n", retval);
|
|
exit (1);
|
|
}
|
|
|
|
|
|
retval = camera_tether(camera, context);
|
|
if (retval != GP_OK) {
|
|
printf("Tether error: %d\n", retval);
|
|
exit (1);
|
|
}
|
|
#endif
|
|
|
|
tries = 30; /* seconds at most, discounting events. */
|
|
while (tries--) {
|
|
retval = camera_tether(camera, context);
|
|
if (retval != GP_OK) {
|
|
printf("Tether error: %d\n", retval);
|
|
exit (1);
|
|
}
|
|
}
|
|
|
|
#if 1
|
|
retval = set_config_value_string(camera,"eosviewfinder", "0", context);
|
|
if (retval != GP_OK) {
|
|
printf("setting eosviewfinder off error: %d\n", retval);
|
|
exit (1);
|
|
}
|
|
#endif
|
|
|
|
gp_camera_exit(camera, context);
|
|
return 0;
|
|
}
|