void	Mock1Test::testMock1() {
	debug(LOG_DEBUG, DEBUG_LOG, 0, "Mock1Test begin");
	ModulePtr	module = repository->getModule("mock1");
	debug(LOG_DEBUG, DEBUG_LOG, 0, "got module");
	module->open();
	debug(LOG_DEBUG, DEBUG_LOG, 0, "module open");
	DeviceLocatorPtr	cl = module->getDeviceLocator();
	debug(LOG_DEBUG, DEBUG_LOG, 0, "get DeviceLocator");
	std::vector<std::string>	cameras = cl->getDevicelist();
	debug(LOG_DEBUG, DEBUG_LOG, 0, "get %d devices", cameras.size());
	CPPUNIT_ASSERT(cameras.size() == 10);
	CameraPtr	camera = cl->getCamera("camera:mock1/5");
	// for every CCD, take an image
	for (unsigned int i = 0; i < camera->nCcds(); i++) {
		CcdPtr	ccd = camera->getCcd(i);
		Exposure	exposure;
		ImageRectangle	frame(ImagePoint(1,1),
			ImageSize(ccd->getSize().width() - 2,
			ccd->getSize().height() - 2));
		exposure.frame(frame);
		ccd->startExposure(exposure);
		while (ccd->exposureStatus() == Exposure::exposing) {
			sleep(1);
		}
		if (ccd->exposureStatus() == Exposure::exposed) {
			ImagePtr	image = ccd->getImage();
			debug(LOG_DEBUG, DEBUG_LOG, 0,
				"result image size: %d x %d",
				image->size().width(), image->size().height());
		}
	}
	debug(LOG_DEBUG, DEBUG_LOG, 0, "Mock1Test end");
}
/**
 * \brief Symmetrize the exposure
 *
 * The M26C has an interlaced CCD which reads the different fields and
 * colors from different sides of the chip. This only works for symmetric
 * exposures, symmetric with respect to the center of the CCD chip. This
 * method computes a symmetrized exposure object.
 */
Exposure	SxCcdM26C::symmetrize(const Exposure& exp) const {
	debug(LOG_DEBUG, DEBUG_LOG, 0, "symmetrizing exposure %s",
		exp.toString().c_str());
	Exposure	symexp = exp;
	int	x[4], y[4];
	x[0] = exp.x();
	y[0] = exp.y();
	x[1] = M26C_WIDTH - x[0];
	y[1] = M26C_HEIGHT - y[0];
	x[2] = exp.x() + exp.width();
	y[2] = exp.y() + exp.height();
	x[3] = M26C_WIDTH - x[2];
	y[3] = M26C_HEIGHT - y[2];
	debug(LOG_DEBUG, DEBUG_LOG, 0, "x[] = %d %d %d %d",
		x[0], x[1], x[2], x[3]);
	debug(LOG_DEBUG, DEBUG_LOG, 0, "y[] = %d %d %d %d",
		y[0], y[1], y[2], y[3]);

	// symmetrized origin
	ImagePoint	origin(min(x, 4), min(y, 4));
	
	// symmetrize size
	unsigned int	sizex = max(x, 4) - symexp.x();
	if (sizex > 3900) {
		sizex = 3900;
	}
	unsigned int	sizey = max(y, 4) - symexp.y();
	ImageSize	size(sizex, sizey);
	ImageRectangle	frame(origin, size);
	symexp.frame(frame);

	debug(LOG_DEBUG, DEBUG_LOG, 0, "symmetrized exposure: %s",
		symexp.toString().c_str());
	return symexp;
}
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();
}
/**
 * \brief Symmetrize the exposure
 *
 * The M26C has an interlaced CCD which reads the different fields and
 * colors from different sides of the chip. This only works for symmetric
 * exposures, symmetric with respect to the center of the CCD chip. This
 * method computes a symmetrized exposure object.
 */
Exposure	SxCcdM26C::symmetrize(const Exposure& exp) const {
	debug(LOG_DEBUG, DEBUG_LOG, 0, "symmetrizing exposure %s",
		exp.toString().c_str());
	Exposure	symexp = exp;

	int	x[4], y[4];
	// compute all the possible corner coordinates
	x[0] = exp.x();			y[0] = exp.y();
	x[1] = M26C_WIDTH - x[0]; 	y[1] = M26C_HEIGHT - y[0];
	x[2] = exp.x() + exp.width(); 	y[2] = exp.y() + exp.height();
	x[3] = M26C_WIDTH - x[2]; 	y[3] = M26C_HEIGHT - y[2];
	debug(LOG_DEBUG, DEBUG_LOG, 0, "x[] = %d %d %d %d",
		x[0], x[1], x[2], x[3]);
	debug(LOG_DEBUG, DEBUG_LOG, 0, "y[] = %d %d %d %d",
		y[0], y[1], y[2], y[3]);

	// make the origin with the right divisibility
	int	xmin = ((min(x, 4) >> 2) << 2) - 4;
	if (xmin < 0) { xmin = 0; }
	int	xmax = M26C_WIDTH - xmin;
	int	ymin = ((min(y, 4) >> 2) << 2) - 4;
	if (ymin < 0) { ymin = 0; }
	int	ymax = M26C_HEIGHT - ymin;

	// symmetrized origin
	ImagePoint	origin(xmin, ymin);

	// symmetrize size
	unsigned int	sizex = xmax - xmin;
	if (sizex > 3900) {
		sizex = 3900;
	}
	unsigned int	sizey = ymax - ymin;
	ImageSize	size(sizex, sizey);
	ImageRectangle	frame(origin, size);
	symexp.frame(frame);

	debug(LOG_DEBUG, DEBUG_LOG, 0, "symmetrized exposure: %s",
		symexp.toString().c_str());
	return symexp;
}
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;
}