void	QsiCcd::startExposure(const Exposure& exposure) {
	std::unique_lock<std::recursive_mutex>	lock(_camera.mutex);
	Ccd::startExposure(exposure);

	debug(LOG_DEBUG, DEBUG_LOG, 0, "start QSI exposure");
	try {
		// set the binning mode
		_camera.camera().put_BinX(exposure.mode().x());
		_camera.camera().put_BinY(exposure.mode().y());

		// compute the frame size in binned pixels, as this is what
		// the QSI camera expects
		ImagePoint origin = exposure.frame().origin() / exposure.mode();
		ImageSize  size = exposure.frame().size() / exposure.mode();
		ImageRectangle	frame(origin, size);
		debug(LOG_DEBUG, DEBUG_LOG, 0, "requesting %s image",
			frame.toString().c_str());

		// set the subframe
		_camera.camera().put_NumX(size.width());
		_camera.camera().put_NumY(size.height());
		_camera.camera().put_StartX(origin.x());
		_camera.camera().put_StartY(origin.y());

		// turn off the led
		debug(LOG_DEBUG, DEBUG_LOG, 0, "turn LED off");
		_camera.camera().put_LEDEnabled(false);

		// get shutter info
		bool	light = (exposure.shutter() == Shutter::OPEN);
		_camera.camera().StartExposure(exposure.exposuretime(), light);
		debug(LOG_DEBUG, DEBUG_LOG, 0, "%fsec %s exposure started",
			exposure.exposuretime(), (light) ? "light" : "dark");
	} catch (const std::exception& x) {
		debug(LOG_ERR, DEBUG_LOG, 0, "bad exposure parameters: %s",
			x.what());
		cancelExposure();
		throw BadParameter(x.what());
	}

	// check the current state of the camera
	exposureStatus();
}
int	main(int argc, char *argv[]) {
	debugthreads = 1;
	Exposure	exposure;
	unsigned int	nImages = 1;
	std::string	reponame;
	std::string	filtername;
	int	c;
	int	longindex;
	double	temperature = std::numeric_limits<double>::quiet_NaN();
	while (EOF != (c = getopt_long(argc, argv, "b:c:de:f:hn:p:r:t:?",
		longopts, &longindex)))
		switch (c) {
		case 'b':
			exposure.mode(Binning(optarg));
			break;
		case 'c':
			Configuration::set_default(optarg);
			break;
		case 'd':
			debuglevel = LOG_DEBUG;
			break;
		case 'e':
			exposure.exposuretime(atof(optarg));
			break;
		case 'f':
			filtername = std::string(optarg);
			break;
		case 'h':
		case '?':
			usage(argv[0]);
			return EXIT_SUCCESS;
		case 'n':
			nImages = atoi(optarg);
			break;
		case 'p':
			exposure.purpose(Exposure::string2purpose(optarg));
			break;
		case 'r':
			reponame = std::string(optarg);
			break;
		case 't':
			temperature = atof(optarg);
			break;
		case 1:
			exposure.frame(ImageRectangle(optarg));
			break;
		default:
			throw std::runtime_error("unknown option");
		}

	// next argument must be instrument name or help
	if (optind >= argc) {
		std::cerr << "missing instrument name" << std::endl;
		return EXIT_FAILURE;
	}
	std::string	instrumentname(argv[optind++]);

	// get the configuration
	ConfigurationPtr	config = Configuration::get();

	// backend for instruments
	InstrumentBackend	instrumentbackend(config->database());
        InstrumentPtr   instrument = instrumentbackend.get(instrumentname);

	// get the image repository
	ImageRepoPtr	repo(NULL);
	if (reponame.size() > 0) {
		ImageRepoConfigurationPtr	imagerepos
			= ImageRepoConfiguration::get(config);
		repo = imagerepos->repo(reponame);
	}

	// prepare a repository from which we can extract the devices
	Repository	repository;
	Devices		devices(repository);

	// get the devices
	CameraPtr	camera = devices.getCamera(instrument->getCamera(0).deviceurl());
	CcdPtr		ccd = devices.getCcd(instrument->getCcd(0).deviceurl());

	// If temperature is set, and a cooler is present, initialize the
	// cooler and wait until temperature is reached
	CoolerPtr	cooler(NULL);
	if ((temperature == temperature)
		&& (instrument->hasCooler())) {
		double	absolute = 273.15 + temperature;
		if (absolute < 0) {
			std::string	msg
				= stringprintf("illegal temperature: %f",
					temperature);
			debug(LOG_ERR, DEBUG_LOG, 0, "%s", msg.c_str());
			throw std::runtime_error(msg);
		}
		cooler = devices.getCooler(instrument->getCooler(0).deviceurl());
		cooler->setTemperature(absolute);
		cooler->setOn(true);

		double	delta;
		do {
			double	actual = cooler->getActualTemperature();
			delta = fabs(absolute - actual);
			debug(LOG_DEBUG, DEBUG_LOG, 0,
				"set: %.1f, actual: %.1f, delta: %.1f",
				absolute, actual, delta);
		} while (delta > 1);
	}

	// if the instrument has a filter wheel, we get a pointer to it
	// and try 
	FilterWheelPtr	filterwheel(NULL);
	if (instrument->hasFilterWheel()) {
		filterwheel = devices.getFilterWheel(
			instrument->getFilterWheel(0).deviceurl());
		filterwheel->wait(20);
		if (filtername.size() > 0) {
			filterwheel->select(filtername);
			filterwheel->wait(20);
		}
	}

	// start the stream
	unsigned int	imagesRetrieved = 0;
	ccd->startStream(exposure);
	while (imagesRetrieved < nImages) {
		ImagePtr	image = ccd->getEntry(true).image;
		debug(LOG_DEBUG, DEBUG_LOG, 0, "got image[%u] %s",
			++imagesRetrieved,
			image->size().toString().c_str());
		if (!image->hasMetadata(std::string("INSTRUME"))) {
			image->setMetadata(FITSKeywords::meta(
				std::string("INSTRUME"), instrument->name()));
		}
		// do something about the image
		if (repo) {
			repo->save(image);
		}
	}
	// stop the stream
	ccd->stopStream();

	// find out how many images were dropped
	if (ccd->dropped() > 0) {
		std::cerr << "images dropped: " << ccd->dropped() << std::endl;
	}

	// turn off the cooler
	if (cooler) {
		cooler->setOn(false);
	}
	
	return EXIT_SUCCESS;
}