mirror of
https://github.com/eddyem/CCD_Capture.git
synced 2025-12-06 02:35:13 +03:00
554 lines
15 KiB
C
554 lines
15 KiB
C
/*
|
|
* This file is part of the CCD_Capture project.
|
|
* Copyright 2022 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <fcntl.h>
|
|
#include <limits.h>
|
|
#include <math.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <usefull_macros.h>
|
|
#include <ASICamera2.h>
|
|
|
|
#include "basestructs.h"
|
|
|
|
extern Camera camera;
|
|
extern Focuser focuser;
|
|
extern Wheel wheel;
|
|
|
|
// remove all these after removing stubs!
|
|
// VVV
|
|
static const int filtermax = 5;
|
|
static const float focmaxpos = 10.;
|
|
static int filterpos = 0;
|
|
static float focuserpos = 1.;
|
|
// AAA
|
|
|
|
static int curbin = 1;
|
|
static ASI_BOOL isdark = ASI_FALSE;
|
|
static struct{
|
|
float maxgain;
|
|
float mingain;
|
|
float maxbright;
|
|
float minbright;
|
|
int maxbin;
|
|
} extrvalues = {0}; // extremal values
|
|
|
|
static double starttime = 0.; // time when exposure started
|
|
static float exptime = 0.; // exposition time
|
|
|
|
static ASI_CAMERA_INFO caminfo = {0};
|
|
|
|
// setters and getters of some parameters
|
|
static int zwo_setfloat(float f, ASI_CONTROL_TYPE t){
|
|
DBG("Try to set float %f, type %d", f, t);
|
|
long val = (long) f;
|
|
if(ASI_SUCCESS != ASISetControlValue(caminfo.CameraID, t, val, ASI_FALSE)){
|
|
DBG("FAILED");
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
static int zwo_getfloat(float *f, ASI_CONTROL_TYPE t){
|
|
if(!f) return FALSE;
|
|
long val; ASI_BOOL aut = ASI_FALSE;
|
|
if(ASI_SUCCESS != ASIGetControlValue(caminfo.CameraID, t, &val, &aut))
|
|
return FALSE;
|
|
if(aut) DBG("VALUE IS AUTO!!!");
|
|
*f = (float) val;
|
|
return TRUE;
|
|
}
|
|
|
|
static int asi_checkcam(){
|
|
camera.Ndevices = ASIGetNumOfConnectedCameras();
|
|
DBG("found %d ZWO's", camera.Ndevices);
|
|
if(camera.Ndevices) return TRUE;
|
|
return FALSE;
|
|
}
|
|
|
|
static int campoll(capture_status *st, float *remain){
|
|
if(!st) return FALSE;
|
|
ASI_EXPOSURE_STATUS s;
|
|
if(ASI_SUCCESS != ASIGetExpStatus(caminfo.CameraID, &s)){
|
|
DBG("Can't get exp status");
|
|
return FALSE;
|
|
}
|
|
switch(s){
|
|
case ASI_EXP_IDLE:
|
|
*st = CAPTURE_NO;
|
|
break;
|
|
case ASI_EXP_WORKING:
|
|
*st = CAPTURE_PROCESS;
|
|
break;
|
|
case ASI_EXP_SUCCESS:
|
|
*st = CAPTURE_READY;
|
|
break;
|
|
default: // failed
|
|
DBG("Failed: %d", s);
|
|
*st = CAPTURE_ABORTED;
|
|
}
|
|
if(remain){
|
|
float diff = exptime - (dtime() - starttime);
|
|
if(diff < 0.) diff = 0.;
|
|
*remain = diff;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
static int camcapt(IMG *ima){
|
|
if(!ima || !ima->data) return FALSE;
|
|
unsigned char *d = (unsigned char *)ima->data;
|
|
long image_size = ima->h * ima->w * 2;
|
|
if(ASI_SUCCESS != ASIGetDataAfterExp(caminfo.CameraID, d, image_size)){
|
|
WARNX("Couldn't read exposure data\n");
|
|
return FALSE;
|
|
}
|
|
ima->bitpix = 16;
|
|
return TRUE;
|
|
}
|
|
|
|
static void camcancel(){
|
|
ASI_EXPOSURE_STATUS s;
|
|
if(ASI_SUCCESS == ASIGetExpStatus(caminfo.CameraID, &s) && s == ASI_EXP_WORKING){
|
|
ASIStopExposure(caminfo.CameraID);
|
|
}
|
|
}
|
|
|
|
static int setframetype(int l){
|
|
if(l) isdark = ASI_FALSE;
|
|
else isdark = ASI_TRUE;
|
|
return TRUE;
|
|
}
|
|
|
|
static int startcapt(){
|
|
camcancel();
|
|
//red("ISDARK = %s\n", isdark ? "true" : "false");
|
|
if(ASI_SUCCESS == ASIStartExposure(caminfo.CameraID, isdark)){
|
|
starttime = dtime();
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
static void asi_closecam(){
|
|
FNAME();
|
|
if(caminfo.CameraID){
|
|
camcancel();
|
|
ASICloseCamera(caminfo.CameraID);
|
|
caminfo.CameraID = 0;
|
|
}
|
|
}
|
|
|
|
static int setdevno(int n){
|
|
if(n > camera.Ndevices - 1 || n < 0) return FALSE;
|
|
asi_closecam();
|
|
if(ASI_SUCCESS != ASIGetCameraProperty(&caminfo, n)) return FALSE;
|
|
DBG("Camera #%d, name: %s, ID: %d", n, caminfo.Name, caminfo.CameraID);
|
|
DBG("WxH: %ldx%ld, %s", caminfo.MaxWidth, caminfo.MaxHeight, caminfo.IsColorCam == ASI_TRUE ? "color" : "monochrome");
|
|
DBG("Pixel size: %1.1f mkm; gain: %1.2f e/ADU", caminfo.PixelSize, caminfo.ElecPerADU);
|
|
int *sup = caminfo.SupportedBins;
|
|
while(*sup){
|
|
extrvalues.maxbin = *sup++;
|
|
}
|
|
camera.pixX = camera.pixY = (float)caminfo.PixelSize / 1e6; // um -> m
|
|
camera.array = (frameformat){.w = caminfo.MaxWidth, .h = caminfo.MaxHeight, .xoff = 0, .yoff = 0};
|
|
camera.field = camera.array; // initial setup (will update later)
|
|
if(ASI_SUCCESS != ASIOpenCamera(caminfo.CameraID)){
|
|
WARNX("Can't open device for camera %s", caminfo.Name);
|
|
return FALSE;
|
|
}
|
|
if(ASI_SUCCESS != ASIInitCamera(caminfo.CameraID)){
|
|
WARNX("Can't init device for camera %s", caminfo.Name);
|
|
asi_closecam();
|
|
return FALSE;
|
|
}
|
|
// get binning
|
|
int imtype;
|
|
if(ASI_SUCCESS == ASIGetROIFormat(caminfo.CameraID, &camera.field.w, &camera.field.h, &curbin, &imtype)){
|
|
ASIGetStartPos(caminfo.CameraID, &camera.field.xoff, &camera.field.yoff);
|
|
DBG("FIELD: %dx%d, offset %dx%d, binning %d", camera.field.w, camera.field.h, camera.field.xoff, camera.field.yoff, curbin);
|
|
}
|
|
int ncontrols = 0;
|
|
if(ASI_SUCCESS == ASIGetNumOfControls(caminfo.CameraID, &ncontrols)){
|
|
ASI_CONTROL_CAPS asicon;
|
|
for(int i = 0; i < ncontrols; ++i){
|
|
if(ASI_SUCCESS == ASIGetControlCaps(caminfo.CameraID, i, &asicon)){
|
|
#ifdef EBUG
|
|
green("Control #%d: '%s' ", i, asicon.Name);
|
|
#endif
|
|
long val;
|
|
ASI_BOOL aut;
|
|
if(ASI_SUCCESS == ASIGetControlValue(caminfo.CameraID, asicon.ControlType, &val, &aut)){
|
|
#ifdef EBUG
|
|
printf("curval: %ld%s, ", val, aut ? " (auto)": "");
|
|
#endif
|
|
switch(asicon.ControlType){ // get extremal values of brightness and gain
|
|
case ASI_GAIN:
|
|
extrvalues.maxgain = (float) asicon.MaxValue;
|
|
extrvalues.mingain = (float) asicon.MinValue;
|
|
break;
|
|
case ASI_BRIGHTNESS:
|
|
extrvalues.maxbright = (float) asicon.MaxValue;
|
|
extrvalues.minbright = (float) asicon.MinValue;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
#ifdef EBUG
|
|
printf("min/max: %ld/%ld; def: %ld, writeable: %d, descr: %s\n",
|
|
asicon.MinValue, asicon.MaxValue, asicon.DefaultValue, asicon.IsWritable,
|
|
asicon.Description);
|
|
#endif
|
|
}
|
|
}
|
|
//red("Bit: %d\n", caminfo.BitDepth);
|
|
//caminfo.BitDepth = 10;
|
|
//red("Bit: %d\n", caminfo.BitDepth);
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
static int camsetbrig(float b){
|
|
if(b < extrvalues.minbright || b > extrvalues.maxbright){
|
|
WARNX(_("Brightness should be from %g to %g"), extrvalues.minbright, extrvalues.maxbright);
|
|
return FALSE;
|
|
}
|
|
return zwo_setfloat(b, ASI_BRIGHTNESS);
|
|
}
|
|
|
|
static int camgetbrig(float *b){
|
|
if(!b) return FALSE;
|
|
return zwo_getfloat(b, ASI_BRIGHTNESS);
|
|
}
|
|
|
|
static int camsetexp(float t){
|
|
if(!zwo_setfloat(t*1e6, ASI_EXPOSURE)) return FALSE;
|
|
exptime = t;
|
|
return TRUE;
|
|
}
|
|
|
|
static int camsetgain(float g){
|
|
if(g < extrvalues.mingain || g > extrvalues.maxgain){
|
|
WARNX(_("Gain should be from %g to %g"), extrvalues.mingain, extrvalues.maxgain);
|
|
return FALSE;
|
|
}
|
|
return zwo_setfloat(g, ASI_GAIN);
|
|
}
|
|
|
|
static int camgetgain(float *g){
|
|
if(!g) return FALSE;
|
|
return zwo_getfloat(g, ASI_GAIN);
|
|
}
|
|
|
|
static int camsett(float t){
|
|
if(caminfo.IsCoolerCam == ASI_FALSE){
|
|
DBG("Cooling unsupported");
|
|
return FALSE;
|
|
}
|
|
if(!zwo_setfloat(1., ASI_FAN_ON)){
|
|
DBG("Can't set fan on");
|
|
return FALSE;
|
|
}
|
|
float f;
|
|
if(zwo_getfloat(&f, ASI_FAN_ON)){
|
|
DBG("FAN: %g", f);
|
|
}
|
|
if(!zwo_setfloat(1., ASI_COOLER_ON)){
|
|
DBG("Can't set cooler on");
|
|
return FALSE;
|
|
}
|
|
if(!zwo_setfloat(t, ASI_TARGET_TEMP)){
|
|
DBG("Can't set target temperature");
|
|
return FALSE;
|
|
}
|
|
if(zwo_getfloat(&f, ASI_TARGET_TEMP)){
|
|
DBG("Ttarg = %g", f);
|
|
}
|
|
if(!zwo_getfloat(&f, ASI_COOLER_ON)) return FALSE;
|
|
DBG("COOLERON = %g", f);
|
|
/*
|
|
#ifdef EBUG
|
|
double t0 = dtime();
|
|
float c, p, tn;
|
|
while(dtime() - t0 < 1200.){
|
|
usleep(100000); // without this first ASI_FAN_ON will show false data
|
|
green("%.1f ", dtime()-t0);
|
|
zwo_getfloat(&f, ASI_FAN_ON);
|
|
zwo_getfloat(&t, ASI_TARGET_TEMP);
|
|
zwo_getfloat(&c, ASI_COOLER_ON);
|
|
zwo_getfloat(&tn, ASI_TEMPERATURE);
|
|
zwo_getfloat(&p, ASI_COOLER_POWER_PERC);
|
|
printf("fan: %g, t: %g, cooler: %g, perc: %g, tnow: %g\n", f, t, c, p, tn/10.);
|
|
}
|
|
#endif
|
|
*/
|
|
return TRUE;
|
|
}
|
|
|
|
static int camgett(float *t){
|
|
if(!t) return FALSE;
|
|
float curt;
|
|
if(!zwo_getfloat(&curt, ASI_TEMPERATURE)) return FALSE;
|
|
*t = curt / 10.;
|
|
return TRUE;
|
|
}
|
|
// get Tbody & Thot unsupported
|
|
static int gett(_U_ float *t){
|
|
return FALSE;
|
|
}
|
|
|
|
static int camsetbin(int h, int v){
|
|
DBG("set bin %dx%d", h, v);
|
|
if(h != v){
|
|
WARNX(_("BinX and BinY should be equal, take h"));
|
|
//return FALSE;
|
|
}
|
|
if(h > extrvalues.maxbin){
|
|
WARNX(_("Maximal binning value is %d"), extrvalues.maxbin);
|
|
}
|
|
if(zwo_setfloat(1., ASI_HARDWARE_BIN)){
|
|
curbin = h;
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
// unsupported, but return TRUE if have mechanical shutter
|
|
static int camshutter(_U_ shutter_op s){
|
|
if(!caminfo.MechanicalShutter) return FALSE;
|
|
return TRUE;
|
|
}
|
|
|
|
// set ROI
|
|
static int camsetgeom(frameformat *f){ // w,h, xoff, yoff
|
|
if(!f) return FALSE;
|
|
int imtype;
|
|
DBG("w: %g, h: %g, bin: %d", (double)f->w / curbin, (double)f->h / curbin, curbin);
|
|
if(ASI_SUCCESS != ASISetROIFormat(caminfo.CameraID, f->w/curbin, f->h/curbin, curbin, ASI_IMG_RAW16)){
|
|
DBG(_("Can't set geometry"));
|
|
return FALSE;
|
|
}
|
|
if(ASI_SUCCESS != ASIGetROIFormat(caminfo.CameraID, &f->w, &f->h, &curbin, &imtype)){
|
|
DBG(_("Can't get geometry"));
|
|
return FALSE;
|
|
}
|
|
DBG("curformat: w=%d, h=%d, bin=%d", f->w, f->h, curbin);
|
|
DBG("w=%d, h=%d, bin=%d", f->w, f->h, curbin);
|
|
if(ASI_SUCCESS != ASISetStartPos(caminfo.CameraID, f->xoff/curbin, f->yoff/curbin)){
|
|
DBG("Can't set start pos");
|
|
return FALSE;
|
|
}
|
|
if(ASI_SUCCESS != ASIGetStartPos(caminfo.CameraID, &f->xoff, &f->yoff)){
|
|
DBG("Can't get start pos");
|
|
return FALSE;
|
|
}
|
|
DBG("curstartpos: x=%d, y=%d", f->xoff, f->yoff);
|
|
camera.geometry = *f;
|
|
return TRUE;
|
|
}
|
|
|
|
static int camgetnam(char *n, int l){
|
|
strncpy(n, caminfo.Name, l);
|
|
return TRUE;
|
|
}
|
|
|
|
static int camgmg(float *mg){ // get max gain
|
|
if(mg) *mg = extrvalues.maxgain;
|
|
return TRUE;
|
|
}
|
|
|
|
static int camggl(frameformat *max, frameformat *step){ // get geometry limits
|
|
DBG("array: %dx%d, off: %dx%d", camera.array.w, camera.array.h, camera.array.xoff, camera.array.yoff);
|
|
if(max) *max = camera.array;
|
|
if(step) *step = (frameformat){1,1,1,1};
|
|
return TRUE;
|
|
}
|
|
|
|
static int camgetbin(int *binh, int *binv){
|
|
if(binh) *binh = curbin;
|
|
if(binv) *binv = curbin;
|
|
return TRUE;
|
|
}
|
|
|
|
static int setfspd(int spd){ // set fast speed (0..3): 0 - 40% bandwidthovrl, 3 - 100%
|
|
float bw = 40.;
|
|
if(spd > 2) bw = 100.;
|
|
else if(spd > 0) bw += 20. * spd;
|
|
DBG("set BANDWIDTH to %g", bw);
|
|
if(ASI_SUCCESS != zwo_setfloat(bw, ASI_BANDWIDTHOVERLOAD)){
|
|
DBG("Can't set");
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
static int camgetio(_U_ int *io){ // not supported
|
|
return FALSE;
|
|
}
|
|
|
|
static int camfan(_U_ fan_speed spd){ // not supported, just turn it on/off
|
|
switch(spd){
|
|
case FAN_OFF:
|
|
if(!zwo_setfloat(0., ASI_FAN_ON)){
|
|
DBG("Can't set fan off");
|
|
return FALSE;
|
|
}
|
|
break;
|
|
default: // turn ON
|
|
if(!zwo_setfloat(1., ASI_FAN_ON)){
|
|
DBG("Can't set fan on");
|
|
return FALSE;
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/* --------------- FOCUSER --------------- */
|
|
static int focsetpos(_U_ int a, float n){
|
|
if(n < 0. || n > focmaxpos) return FALSE;
|
|
focuserpos = n;
|
|
return TRUE;
|
|
}
|
|
|
|
static int fochome(_U_ int a){
|
|
focuserpos = 0.;
|
|
return TRUE;
|
|
}
|
|
|
|
static int focgetnam(char *n, int l){
|
|
strncpy(n, "Dummy focuser", l);
|
|
return TRUE;
|
|
}
|
|
|
|
static int focpos(float *p){
|
|
if(p) *p = focuserpos;
|
|
return TRUE;
|
|
}
|
|
|
|
static int focMp(float *p){
|
|
if(p) *p = focmaxpos;
|
|
return TRUE;
|
|
}
|
|
|
|
static int focmp(float *p){
|
|
if(p) *p = 0.;
|
|
return TRUE;
|
|
}
|
|
|
|
/* --------------- WHEEL --------------- */
|
|
static int whlsetpos(int n){
|
|
if(n > filtermax || n < 0) return FALSE;
|
|
filterpos = n;
|
|
return TRUE;
|
|
}
|
|
|
|
static int whlgetpos(int *n){
|
|
if(n) *n = filterpos;
|
|
return TRUE;
|
|
}
|
|
|
|
static int whlgmp(int *n){
|
|
if(n) *n = filtermax;
|
|
return TRUE;
|
|
}
|
|
|
|
static int whlgetnam(char *n, int l){
|
|
strncpy(n, "Dummy filter wheel", l);
|
|
return TRUE;
|
|
}
|
|
|
|
static int stub(){
|
|
return FALSE;
|
|
}
|
|
static void vstub(){
|
|
FNAME();
|
|
return;
|
|
}
|
|
static int istub(_U_ int N){
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* Global objects: camera, focuser and wheel
|
|
*/
|
|
__attribute__ ((visibility("default"))) Camera camera = {
|
|
.check = asi_checkcam,
|
|
.close = asi_closecam,
|
|
.pollcapture = campoll,
|
|
.capture = camcapt,
|
|
.cancel = camcancel,
|
|
.startexposition = startcapt,
|
|
// setters:
|
|
.setDevNo = setdevno,
|
|
.setbrightness = camsetbrig,
|
|
.setexp = camsetexp,
|
|
.setgain = camsetgain,
|
|
.setT = camsett,
|
|
.setbin = camsetbin,
|
|
.setnflushes = istub,
|
|
.shuttercmd = camshutter,
|
|
.confio = istub,
|
|
.setio = istub,
|
|
.setframetype = setframetype,
|
|
.setbitdepth = istub, // there's no ways in documentation to SET bit depth
|
|
.setfastspeed = setfspd,
|
|
.setgeometry = camsetgeom,
|
|
.setfanspeed = camfan,
|
|
// getters:
|
|
.getbrightness = camgetbrig,
|
|
.getModelName = camgetnam,
|
|
.getgain = camgetgain,
|
|
.getmaxgain = camgmg,
|
|
.getgeomlimits = camggl,
|
|
.getTcold = camgett,
|
|
.getThot = gett,
|
|
.getTbody = gett,
|
|
.getbin = camgetbin,
|
|
.getio = camgetio,
|
|
};
|
|
|
|
__attribute__ ((visibility("default"))) Focuser focuser = {
|
|
.check = stub,
|
|
.close = vstub,
|
|
// setters:
|
|
.setDevNo = setdevno,
|
|
.setAbsPos = focsetpos,
|
|
.home = fochome,
|
|
// getters:
|
|
.getModelName = focgetnam,
|
|
.getTbody = gett,
|
|
.getPos = focpos,
|
|
.getMaxPos = focMp,
|
|
.getMinPos = focmp,
|
|
};
|
|
|
|
__attribute__ ((visibility("default"))) Wheel wheel = {
|
|
.check = stub,
|
|
.close = vstub,
|
|
// setters
|
|
.setDevNo = setdevno,
|
|
.setPos = whlsetpos,
|
|
// getters
|
|
.getModelName = whlgetnam,
|
|
.getTbody = gett,
|
|
.getPos = whlgetpos,
|
|
.getMaxPos = whlgmp,
|
|
};
|