From 089370d276a6880b6b223a8822074b5d6bea1669 Mon Sep 17 00:00:00 2001 From: Edward Emelianov Date: Fri, 3 Feb 2023 09:57:35 +0300 Subject: [PATCH] Add PointGrey Grasshopper support; fixed DATAMAX for 8- and 12-bit images --- BASLER_cameras/basler.c | 1073 ++++++++++++++--------------- CMakeLists.txt | 4 + Dummy_cameras/dummyfunc.c | 1 + FLI_cameras/flifunc.c | 3 + GRH_cameras/CMakeLists.txt | 15 + GRH_cameras/FindFLYCAP.cmake | 40 ++ GRH_cameras/grasshopper.c | 476 +++++++++++++ HIKROBOT_cameras/mvsfunc.c | 1234 +++++++++++++++++----------------- ZWO_cameras/zwofunc.c | 1 + basestructs.h | 1 + ccdfunc.c | 3 +- locale/ru/messages.po | 131 ++-- locale/ru/ru.po | 125 ++-- 13 files changed, 1810 insertions(+), 1297 deletions(-) create mode 100644 GRH_cameras/CMakeLists.txt create mode 100644 GRH_cameras/FindFLYCAP.cmake create mode 100644 GRH_cameras/grasshopper.c diff --git a/BASLER_cameras/basler.c b/BASLER_cameras/basler.c index 3adb76a..45e4b8d 100644 --- a/BASLER_cameras/basler.c +++ b/BASLER_cameras/basler.c @@ -1,556 +1,517 @@ -/* - * This file is part of the CCD_Capture project. - * Copyright 2023 Edward V. Emelianov . - * - * 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 . - */ - -#include -#include -#include -#include -#include - -#include "basestructs.h" -#include "omp.h" - -extern Camera camera; - -static PYLON_DEVICE_HANDLE hDev; -static int isopened = FALSE, is16bit = FALSE; -static char camname[BUFSIZ] = {0}; -static size_t payloadsize = 0; // size of imgBuf -static unsigned char *imgBuf = NULL; -static uint32_t expostime = 0.; // current exposition time (in microseconds) -static PYLON_DEVICECALLBACK_HANDLE hCb; -static int curhbin = 1, curvbin = 1; - -#if 0 -static void *handle = NULL; -static char camname[BUFSIZ] = {0}; -//static long cam_err, tmpl; -static capture_status capStatus = CAPTURE_NO; -static int curhbin = 1, curvbin = 1; -static double starttime = 0.; // time when exposure started -static float exptime = 0.; // exposition time (in seconds) -static uint8_t *pdata = NULL; -static int pdatasz = 0; - -static struct{ - float maxgain; - float mingain; - float maxbright; - float minbright; - float minexp; - float maxexp; - int maxbin; -} extrvalues = {0}; // extremal values -#endif - -typedef struct{ - int64_t min; - int64_t max; - int64_t incr; - int64_t val; -} int64_values; - -typedef struct{ - double min; - double max; - double val; -} float_values; - -static char* describeError(GENAPIC_RESULT reserr){ - static char buf[1024]; - char* errMsg, *bptr = buf; - size_t length, l = 1023; - GenApiGetLastErrorMessage(NULL, &length); - errMsg = MALLOC(char, length); - GenApiGetLastErrorMessage(errMsg, &length); - size_t ll = snprintf(bptr, l, "%s (%d); ", errMsg, (int)reserr); - if(ll > 0){l -= ll; bptr += ll;} - FREE(errMsg); - GenApiGetLastErrorDetail(NULL, &length); - errMsg = MALLOC(char, length); - GenApiGetLastErrorDetail(errMsg, &length); - snprintf(bptr, l, "%s", errMsg); - FREE(errMsg); - return buf; -} - -#define PYLONFN(fn, ...) do{register GENAPIC_RESULT reserr; if(GENAPI_E_OK != (reserr=fn(__VA_ARGS__))){ \ - WARNX(#fn "(): %s", describeError(reserr)); return FALSE;}}while(0) - -static void disconnect(){ - FNAME(); - if(!isopened) return; - FREE(imgBuf); - PylonDeviceDeregisterRemovalCallback(hDev, hCb); - PylonDeviceClose(hDev); - PylonDestroyDevice(hDev); - PylonTerminate(); - isopened = FALSE; -} - -/** - * @brief chkNode - get node & check it for read/write - * @param phNode (io) - pointer to node - * @param nodeType - type of node - * @param wr - ==TRUE if need to check for writeable - * @return TRUE if node found & checked - */ -static int chkNode(NODE_HANDLE *phNode, const char *featureName, EGenApiNodeType nodeType, int wr){ - if(!isopened || !phNode || !featureName) return FALSE; - NODEMAP_HANDLE hNodeMap; - EGenApiNodeType nt; - _Bool bv; - PYLONFN(PylonDeviceGetNodeMap, hDev, &hNodeMap); - PYLONFN(GenApiNodeMapGetNode, hNodeMap, featureName, phNode); - if(*phNode == GENAPIC_INVALID_HANDLE) return FALSE; - PYLONFN(GenApiNodeGetType, *phNode, &nt); - if(nodeType != nt) return FALSE; - PYLONFN(GenApiNodeIsReadable, *phNode, &bv); - if(!bv) return FALSE; // not readable - if(wr){ - PYLONFN(GenApiNodeIsWritable, *phNode, &bv); - if(!bv) return FALSE; // not writeable - } - return TRUE; -} - -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wunused-function" - -// getters of different types of data -static int getBoolean(const char *featureName, _Bool *val){ - if(!isopened || !featureName) return FALSE; - NODE_HANDLE hNode; - if(!chkNode(&hNode, featureName, BooleanNode, FALSE)) return FALSE; - if(!val) return TRUE; - PYLONFN(GenApiBooleanGetValue, hNode, val); - //DBG("Get boolean: %s = %s", featureName, val ? "true" : "false"); - return TRUE; -} -static int getInt(char *featureName, int64_values *val){ - if(!isopened || !featureName) return FALSE; - NODE_HANDLE hNode; - if(!chkNode(&hNode, featureName, IntegerNode, FALSE)) return FALSE; - if(!val) return TRUE; - PYLONFN(GenApiIntegerGetMin, hNode, &val->min); - PYLONFN(GenApiIntegerGetMax, hNode, &val->max); - PYLONFN(GenApiIntegerGetInc, hNode, &val->incr); - PYLONFN(GenApiIntegerGetValue, hNode, &val->val); - //DBG("Get integer %s = %ld: min = %ld, max = %ld, incr = %ld", featureName, val->val, val->min, val->max, val->incr); - return TRUE; -} -static int getFloat(char *featureName, float_values *val){ - if(!isopened || !featureName) return FALSE; - NODE_HANDLE hNode; - if(!chkNode(&hNode, featureName, FloatNode, FALSE)) return FALSE; - if(!val) return TRUE; - PYLONFN(GenApiFloatGetMin, hNode, &val->min); - PYLONFN(GenApiFloatGetMax, hNode, &val->max); - PYLONFN(GenApiFloatGetValue, hNode, &val->val); - //DBG("Get float %s = %g: min = %g, max = %g", featureName, val->val, val->min, val->max); - return TRUE; -} - -// setters of different types of data -static int setBoolean(char *featureName, _Bool val){ - if(!isopened || !featureName) return FALSE; - NODE_HANDLE hNode; - if(!chkNode(&hNode, featureName, BooleanNode, TRUE)) return FALSE; - PYLONFN(GenApiBooleanSetValue, hNode, val); - return TRUE; -} -static int setInt(char *featureName, int64_t val){ - if(!isopened || !featureName) return FALSE; - NODE_HANDLE hNode; - if(!chkNode(&hNode, featureName, IntegerNode, TRUE)) return FALSE; - PYLONFN(GenApiIntegerSetValue, hNode, val); - return TRUE; -} -static int setFloat(char *featureName, float val){ - if(!isopened || !featureName) return FALSE; - NODE_HANDLE hNode; - if(!chkNode(&hNode, featureName, FloatNode, TRUE)) return FALSE; - PYLONFN(GenApiFloatSetValue, hNode, val); - return TRUE; -} -#pragma GCC diagnostic pop - -static void disableauto(){ - if(!isopened) return; - const char *features[] = {"EnumEntry_TriggerSelector_AcquisitionStart", - "EnumEntry_TriggerSelector_FrameBurstStart", - "EnumEntry_TriggerSelector_FrameStart"}; - const char *triggers[] = {"AcquisitionStart", "FrameBurstStart", "FrameStart"}; - for(int i = 0; i < 3; ++i){ - if(PylonDeviceFeatureIsAvailable(hDev, features[i])){ - PylonDeviceFeatureFromString(hDev, "TriggerSelector", triggers[i]); - PylonDeviceFeatureFromString(hDev, "TriggerMode", "Off"); - } - } - PylonDeviceFeatureFromString(hDev, "GainAuto", "Off"); - PylonDeviceFeatureFromString(hDev, "ExposureAuto", "Off"); - PylonDeviceFeatureFromString(hDev, "ExposureMode", "Timed"); - PylonDeviceFeatureFromString(hDev, "SequencerMode", "Off"); -} - -static void GENAPIC_CC removalCallbackFunction(_U_ PYLON_DEVICE_HANDLE hDevice){ - disconnect(); -} - -static int connect(){ - FNAME(); - size_t numDevices; - disconnect(); - PylonInitialize(); - PYLONFN(PylonEnumerateDevices, &numDevices); - if(!numDevices){ - WARNX("No cameras found"); - return FALSE; - } - camera.Ndevices = numDevices; - return TRUE; -} - -static int getbin(int *binh, int *binv){ - if(!PylonDeviceFeatureIsAvailable(hDev, "BinningVertical") || - !PylonDeviceFeatureIsAvailable(hDev, "BinningHorizontal")) return FALSE; - int64_values v; - if(!getInt("BinningVertical", &v)){ - DBG("Can't get Vbin"); - return FALSE; - } - curvbin = v.val; - if(!getInt("BinningHorizontal", &v)){ - DBG("Can't get Hbin"); - return FALSE; - } - curhbin = v.val; - DBG("binning: %d x %d", curhbin, curvbin); - if(binv) *binv = curvbin; - if(binh) *binh = curhbin; - return TRUE; -} - -static int getgeom(){ - FNAME(); - if(!isopened) return FALSE; - int64_values i; - if(!getInt("Width", &i)) return FALSE; - camera.field.w = i.val; - camera.array.w = i.max; - if(!getInt("Height", &i)) return FALSE; - camera.field.h = i.val; - camera.array.h = i.max; - if(!getInt("OffsetX", &i)) return FALSE; - camera.field.xoff = i.val; - camera.array.xoff = i.val; - camera.array.w -= i.val; - if(!getInt("OffsetY", &i)) return FALSE; - camera.field.yoff = i.val; - camera.array.yoff = i.val; - camera.array.h -= i.val; - camera.geometry = camera.field; - return TRUE; -} - -static int geometrylimits(frameformat *max, frameformat *step){ - FNAME(); - if(!isopened || !max || !step) return FALSE; - int64_values i; - if(!getInt("Width", &i)) return FALSE; - max->w = i.max; step->w = i.incr; - if(!getInt("Height", &i)) return FALSE; - max->h = i.max; step->h = i.incr; - if(!getInt("OffsetX", &i)) return FALSE; - max->xoff = i.max; step->xoff = i.incr; - if(!getInt("OffsetY", &i)) return FALSE; - max->yoff = i.max; step->yoff = i.incr; - return TRUE; -} - -static int setdevno(int N){ - if(N > camera.Ndevices - 1) return FALSE; - PYLONFN(PylonCreateDeviceByIndex, 0, &hDev); - isopened = TRUE; - PYLONFN(PylonDeviceOpen, hDev, PYLONC_ACCESS_MODE_CONTROL | PYLONC_ACCESS_MODE_STREAM | PYLONC_ACCESS_MODE_EXCLUSIVE); - disableauto(); - PYLONFN(PylonDeviceFeatureFromString, hDev, "CameraOperationMode", "LongExposure"); - PYLONFN(PylonDeviceFeatureFromString, hDev, "UserSetSelector", "HighGain"); // set high gain selector - PYLONFN(PylonDeviceFeatureFromString, hDev, "AcquisitionMode", "SingleFrame"); - PYLONFN(PylonDeviceExecuteCommandFeature, hDev, "UserSetLoad"); // load high gain mode - if(PylonDeviceFeatureIsReadable(hDev, "DeviceModelName")){ - size_t l = BUFSIZ-1; - PYLONFN(PylonDeviceFeatureToString, hDev, "DeviceModelName", camname, &l); - DBG("Using camera %s\n", camname); - }else strcpy(camname, "Unknown camera"); - if(!getbin(NULL, NULL)) WARNX("Can't get current binning"); - if(!getgeom()) WARNX("Can't get current frame format"); - - PylonDeviceRegisterRemovalCallback(hDev, removalCallbackFunction, &hCb); - PYLON_STREAMGRABBER_HANDLE hGrabber; - PYLONFN(PylonDeviceGetStreamGrabber, hDev, 0, &hGrabber); - PYLONFN(PylonStreamGrabberOpen, hGrabber); - PYLONFN(PylonStreamGrabberGetPayloadSize, hDev, hGrabber, &payloadsize); - DBG("payload sz %zd", payloadsize); - PylonStreamGrabberClose(hGrabber); - FREE(imgBuf); - imgBuf = MALLOC(unsigned char, payloadsize); - return TRUE; -} - -static int setbitdepth(int i){ - if(i == 0){ // 8 bit - if(!PylonDeviceFeatureIsAvailable( hDev, "EnumEntry_PixelFormat_Mono8" )) return FALSE; - PYLONFN(PylonDeviceFeatureFromString, hDev, "PixelFormat", "Mono8"); - is16bit = FALSE; - DBG("8 bit"); - }else{ // 16 bit - if(!PylonDeviceFeatureIsAvailable( hDev, "EnumEntry_PixelFormat_Mono16" )) return FALSE; - PYLONFN(PylonDeviceFeatureFromString, hDev, "PixelFormat", "Mono16"); - is16bit = TRUE; - DBG("16 bit"); - } - PYLON_STREAMGRABBER_HANDLE hGrabber; - PYLONFN(PylonDeviceGetStreamGrabber, hDev, 0, &hGrabber); - PYLONFN(PylonStreamGrabberOpen, hGrabber); - PYLONFN(PylonStreamGrabberGetPayloadSize, hDev, hGrabber, &payloadsize); - DBG("payload sz %zd", payloadsize); - PylonStreamGrabberClose(hGrabber); - FREE(imgBuf); - imgBuf = MALLOC(unsigned char, payloadsize); - return TRUE; -} - -// stub function: the capture process is blocking -static int pollcapt(capture_status *st, float *remain){ - if(st) *st = CAPTURE_READY; - if(remain) *remain = 0.f; - return TRUE; -} - -static int capture(IMG *ima){ - FNAME(); - if(!ima || !ima->data || !imgBuf || !isopened) return FALSE; - static int toohot = FALSE; - float_values f; - if(!getFloat("DeviceTemperature", &f)) WARNX("Can't get temperature"); - else{ - DBG("Temperature: %.1f", f.val); - if(f.val > 80.){ - WARNX("Device too hot"); - toohot = TRUE; - }else if(toohot && f.val < 75.){ - DBG("Device temperature is normal"); - toohot = FALSE; - } - } - PylonGrabResult_t grabResult; - _Bool bufferReady; - GENAPIC_RESULT res = PylonDeviceGrabSingleFrame(hDev, 0, imgBuf, payloadsize, - &grabResult, &bufferReady, 500 + (uint32_t)expostime); - if(res != GENAPI_E_OK || !bufferReady){ - WARNX("res != GENAPI_E_OK || !bufferReady"); - return FALSE; - } - if(grabResult.Status != Grabbed){ - WARNX("grabResult.Status != Grabbed"); - return FALSE; - } - int width = grabResult.SizeX, height = grabResult.SizeY, stride = grabResult.SizeX + grabResult.PaddingX; - if(is16bit){ - int s2 = stride<<1, w2 = width<<1; - OMP_FOR() - for(int y = 0; y < height; ++y){ - uint16_t *Out = &ima->data[y*width]; - const uint8_t *In = &imgBuf[y*s2]; - memcpy(Out, In, w2); - } - }else{ - OMP_FOR() - for(int y = 0; y < height; ++y){ - uint16_t *Out = &ima->data[y*width]; - const uint8_t *In = &imgBuf[y*stride]; - for(int x = 0; x < width; ++x){ - *Out++ = *In++; - } - } - } - return TRUE; -} - -// Basler have no "brightness" parameter -static int setbrightness(_U_ float b){ - FNAME(); - return FALSE; -} - -static int setexp(float e){ - FNAME(); - if(!isopened) return FALSE; - e *= 1e6f; - if(!setFloat("ExposureTime", e)){ - LOGWARN("Can't set expose time %g", e); - WARNX("Can't set expose time %g", e); - return FALSE; - } - float_values f; - if(!getFloat("ExposureTime", &f)) return FALSE; - expostime = (uint32_t)(f.val); - DBG("EXPOSURE time: need %f, real %f", e, f.val); - return TRUE; -} - -static int setgain(float e){ - FNAME(); - if(!isopened) return FALSE; - if(!setFloat("Gain", e)){ - LOGWARN("Can't set gain %g", e); - WARNX("Can't set gain %g", e); - return FALSE; - } - DBG("GAIN -> %f", e); - return TRUE; -} - -static int changeformat(frameformat *fmt){ - FNAME(); - if(!isopened) return FALSE; - if(!getbin(NULL, NULL)){curhbin = 1; curvbin = 1;} - fmt->h /= curvbin; fmt->yoff /= curvbin; - fmt->w /= curhbin; fmt->xoff /= curhbin; - DBG("set geom %dx%d (off: %dx%d)", fmt->w, fmt->h, fmt->xoff, fmt->yoff); - setInt("Width", fmt->w); - setInt("Height", fmt->h); - setInt("OffsetX", fmt->xoff); - setInt("OffsetY", fmt->yoff); - int64_values i; - if(getInt("Width", &i)) camera.geometry.w = fmt->w = i.val * curhbin; - if(getInt("Height", &i)) camera.geometry.h = fmt->h = i.val * curvbin; - if(getInt("OffsetX", &i)) camera.geometry.xoff = fmt->xoff = i.val * curhbin; - if(getInt("OffsetY", &i)) camera.geometry.yoff = fmt->yoff = i.val * curvbin; - return TRUE; -} - -static int getgain(float *g){ - FNAME(); - float_values v; - if(!getFloat("Gain", &v)) return FALSE; - if(g) *g = (float)v.val; - return TRUE; -} - -static int gainmax(float *g){ - FNAME(); - float_values v; - if(!getFloat("Gain", &v)) return FALSE; - if(g) *g = (float)v.max; - return TRUE; -} - -static int modelname(char *buf, int bufsz){ - strncpy(buf, camname, bufsz); - return TRUE; -} - -static int setbin(int binh, int binv){ - if(!PylonDeviceFeatureIsAvailable(hDev, "BinningVertical") || - !PylonDeviceFeatureIsAvailable(hDev, "BinningHorizontal")){ - DBG("No Vbin / Hbin"); - return FALSE; - } - if(setInt("BinningVertical", (int64_t)binv) && - setInt("BinningHorizontal", (int64_t)binh)) return TRUE; - DBG("Can't set v or h"); - return FALSE; -} - -static int gett(float *t){ - if(!t) return FALSE; - float_values fv; - if(!getFloat("DeviceTemperature", &fv)) return FALSE; - *t = fv.val; - return TRUE; -} - -static int setfanspd(_U_ fan_speed s){ - return FALSE; -} -static int shutter(_U_ shutter_op cmd){ - return FALSE; -} - -static int ffalse(_U_ float f){ return FALSE; } -static int fpfalse(_U_ float *f){ return FALSE; } -static int ifalse(_U_ int i){ return FALSE; } -static int vtrue(){ return TRUE; } -static int ipfalse(_U_ int *i){ return FALSE; } -static void vstub(){ return ;} - -#if 0 -// exported object -camera Basler = { - .disconnect = disconnect, - .connect = connect, - .capture = capture, - .setbrightness = setbrightness, - .setexp = setexp, - .setgain = setgain, - .setgeometry = changeformat, - .getgeomlimits = geometrylimits, - .getmaxgain = gainmax, -}; -#endif - -/* - * Global objects: camera, focuser and wheel - */ -Camera camera = { - .check = connect, - .close = disconnect, - .pollcapture = pollcapt, - .capture = capture, - .cancel = vstub, - .startexposition = vtrue, - // setters: - .setDevNo = setdevno, - .setbrightness = setbrightness, - .setexp = setexp, - .setgain = setgain, - .setT = ffalse, - .setbin = setbin, - .setnflushes = ifalse, - .shuttercmd = shutter, - .confio = ifalse, - .setio = ifalse, - .setframetype = ifalse, // set DARK or NORMAL: no shutter -> no darks - .setbitdepth = setbitdepth, - .setfastspeed = ifalse, - .setgeometry = changeformat, - .setfanspeed = setfanspd, - // getters: - .getbrightness = fpfalse, - .getModelName = modelname, - .getgain = getgain, - .getmaxgain = gainmax, - .getgeomlimits = geometrylimits, - .getTcold = fpfalse, - .getThot = fpfalse, - .getTbody = gett, - .getbin = getbin, - .getio = ipfalse, -}; +/* + * This file is part of the CCD_Capture project. + * Copyright 2023 Edward V. Emelianov . + * + * 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 . + */ + +#include +#include +#include +#include +#include + +#include "basestructs.h" +#include "omp.h" + +extern Camera camera; + +static PYLON_DEVICE_HANDLE hDev; +static int isopened = FALSE, is16bit = FALSE; +static char camname[BUFSIZ] = {0}; +static size_t payloadsize = 0; // size of imgBuf +static unsigned char *imgBuf = NULL; +static uint32_t expostime = 0.; // current exposition time (in microseconds) +static PYLON_DEVICECALLBACK_HANDLE hCb; +static int curhbin = 1, curvbin = 1; + +typedef struct{ + int64_t min; + int64_t max; + int64_t incr; + int64_t val; +} int64_values; + +typedef struct{ + double min; + double max; + double val; +} float_values; + +static char* describeError(GENAPIC_RESULT reserr){ + static char buf[1024]; + char* errMsg, *bptr = buf; + size_t length, l = 1023; + GenApiGetLastErrorMessage(NULL, &length); + errMsg = MALLOC(char, length); + GenApiGetLastErrorMessage(errMsg, &length); + size_t ll = snprintf(bptr, l, "%s (%d); ", errMsg, (int)reserr); + if(ll > 0){l -= ll; bptr += ll;} + FREE(errMsg); + GenApiGetLastErrorDetail(NULL, &length); + errMsg = MALLOC(char, length); + GenApiGetLastErrorDetail(errMsg, &length); + snprintf(bptr, l, "%s", errMsg); + FREE(errMsg); + return buf; +} + +#define PYLONFN(fn, ...) do{register GENAPIC_RESULT reserr; if(GENAPI_E_OK != (reserr=fn(__VA_ARGS__))){ \ + WARNX(#fn "(): %s", describeError(reserr)); return FALSE;}}while(0) + +static void disconnect(){ + FNAME(); + if(!isopened) return; + FREE(imgBuf); + PylonDeviceDeregisterRemovalCallback(hDev, hCb); + PylonDeviceClose(hDev); + PylonDestroyDevice(hDev); + PylonTerminate(); + isopened = FALSE; +} + +/** + * @brief chkNode - get node & check it for read/write + * @param phNode (io) - pointer to node + * @param nodeType - type of node + * @param wr - ==TRUE if need to check for writeable + * @return TRUE if node found & checked + */ +static int chkNode(NODE_HANDLE *phNode, const char *featureName, EGenApiNodeType nodeType, int wr){ + if(!isopened || !phNode || !featureName) return FALSE; + NODEMAP_HANDLE hNodeMap; + EGenApiNodeType nt; + _Bool bv; + PYLONFN(PylonDeviceGetNodeMap, hDev, &hNodeMap); + PYLONFN(GenApiNodeMapGetNode, hNodeMap, featureName, phNode); + if(*phNode == GENAPIC_INVALID_HANDLE) return FALSE; + PYLONFN(GenApiNodeGetType, *phNode, &nt); + if(nodeType != nt) return FALSE; + PYLONFN(GenApiNodeIsReadable, *phNode, &bv); + if(!bv) return FALSE; // not readable + if(wr){ + PYLONFN(GenApiNodeIsWritable, *phNode, &bv); + if(!bv) return FALSE; // not writeable + } + return TRUE; +} + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-function" + +// getters of different types of data +static int getBoolean(const char *featureName, _Bool *val){ + if(!isopened || !featureName) return FALSE; + NODE_HANDLE hNode; + if(!chkNode(&hNode, featureName, BooleanNode, FALSE)) return FALSE; + if(!val) return TRUE; + PYLONFN(GenApiBooleanGetValue, hNode, val); + //DBG("Get boolean: %s = %s", featureName, val ? "true" : "false"); + return TRUE; +} +static int getInt(char *featureName, int64_values *val){ + if(!isopened || !featureName) return FALSE; + NODE_HANDLE hNode; + if(!chkNode(&hNode, featureName, IntegerNode, FALSE)) return FALSE; + if(!val) return TRUE; + PYLONFN(GenApiIntegerGetMin, hNode, &val->min); + PYLONFN(GenApiIntegerGetMax, hNode, &val->max); + PYLONFN(GenApiIntegerGetInc, hNode, &val->incr); + PYLONFN(GenApiIntegerGetValue, hNode, &val->val); + //DBG("Get integer %s = %ld: min = %ld, max = %ld, incr = %ld", featureName, val->val, val->min, val->max, val->incr); + return TRUE; +} +static int getFloat(char *featureName, float_values *val){ + if(!isopened || !featureName) return FALSE; + NODE_HANDLE hNode; + if(!chkNode(&hNode, featureName, FloatNode, FALSE)) return FALSE; + if(!val) return TRUE; + PYLONFN(GenApiFloatGetMin, hNode, &val->min); + PYLONFN(GenApiFloatGetMax, hNode, &val->max); + PYLONFN(GenApiFloatGetValue, hNode, &val->val); + //DBG("Get float %s = %g: min = %g, max = %g", featureName, val->val, val->min, val->max); + return TRUE; +} + +// setters of different types of data +static int setBoolean(char *featureName, _Bool val){ + if(!isopened || !featureName) return FALSE; + NODE_HANDLE hNode; + if(!chkNode(&hNode, featureName, BooleanNode, TRUE)) return FALSE; + PYLONFN(GenApiBooleanSetValue, hNode, val); + return TRUE; +} +static int setInt(char *featureName, int64_t val){ + if(!isopened || !featureName) return FALSE; + NODE_HANDLE hNode; + if(!chkNode(&hNode, featureName, IntegerNode, TRUE)) return FALSE; + PYLONFN(GenApiIntegerSetValue, hNode, val); + return TRUE; +} +static int setFloat(char *featureName, float val){ + if(!isopened || !featureName) return FALSE; + NODE_HANDLE hNode; + if(!chkNode(&hNode, featureName, FloatNode, TRUE)) return FALSE; + PYLONFN(GenApiFloatSetValue, hNode, val); + return TRUE; +} +#pragma GCC diagnostic pop + +static void disableauto(){ + if(!isopened) return; + const char *features[] = {"EnumEntry_TriggerSelector_AcquisitionStart", + "EnumEntry_TriggerSelector_FrameBurstStart", + "EnumEntry_TriggerSelector_FrameStart"}; + const char *triggers[] = {"AcquisitionStart", "FrameBurstStart", "FrameStart"}; + for(int i = 0; i < 3; ++i){ + if(PylonDeviceFeatureIsAvailable(hDev, features[i])){ + PylonDeviceFeatureFromString(hDev, "TriggerSelector", triggers[i]); + PylonDeviceFeatureFromString(hDev, "TriggerMode", "Off"); + } + } + PylonDeviceFeatureFromString(hDev, "GainAuto", "Off"); + PylonDeviceFeatureFromString(hDev, "ExposureAuto", "Off"); + PylonDeviceFeatureFromString(hDev, "ExposureMode", "Timed"); + PylonDeviceFeatureFromString(hDev, "SequencerMode", "Off"); +} + +static void GENAPIC_CC removalCallbackFunction(_U_ PYLON_DEVICE_HANDLE hDevice){ + disconnect(); +} + +static int connect(){ + FNAME(); + size_t numDevices; + disconnect(); + PylonInitialize(); + PYLONFN(PylonEnumerateDevices, &numDevices); + if(!numDevices){ + WARNX("No cameras found"); + return FALSE; + } + camera.Ndevices = numDevices; + return TRUE; +} + +static int getbin(int *binh, int *binv){ + if(!PylonDeviceFeatureIsAvailable(hDev, "BinningVertical") || + !PylonDeviceFeatureIsAvailable(hDev, "BinningHorizontal")) return FALSE; + int64_values v; + if(!getInt("BinningVertical", &v)){ + DBG("Can't get Vbin"); + return FALSE; + } + curvbin = v.val; + if(!getInt("BinningHorizontal", &v)){ + DBG("Can't get Hbin"); + return FALSE; + } + curhbin = v.val; + DBG("binning: %d x %d", curhbin, curvbin); + if(binv) *binv = curvbin; + if(binh) *binh = curhbin; + return TRUE; +} + +static int getgeom(){ + FNAME(); + if(!isopened) return FALSE; + int64_values i; + if(!getInt("Width", &i)) return FALSE; + camera.field.w = i.val; + camera.array.w = i.max; + if(!getInt("Height", &i)) return FALSE; + camera.field.h = i.val; + camera.array.h = i.max; + if(!getInt("OffsetX", &i)) return FALSE; + camera.field.xoff = i.val; + camera.array.xoff = i.val; + camera.array.w -= i.val; + if(!getInt("OffsetY", &i)) return FALSE; + camera.field.yoff = i.val; + camera.array.yoff = i.val; + camera.array.h -= i.val; + camera.geometry = camera.field; + return TRUE; +} + +static int geometrylimits(frameformat *max, frameformat *step){ + FNAME(); + if(!isopened || !max || !step) return FALSE; + int64_values i; + if(!getInt("Width", &i)) return FALSE; + max->w = i.max; step->w = i.incr; + if(!getInt("Height", &i)) return FALSE; + max->h = i.max; step->h = i.incr; + if(!getInt("OffsetX", &i)) return FALSE; + max->xoff = i.max; step->xoff = i.incr; + if(!getInt("OffsetY", &i)) return FALSE; + max->yoff = i.max; step->yoff = i.incr; + return TRUE; +} + +static int setdevno(int N){ + if(N > camera.Ndevices - 1) return FALSE; + PYLONFN(PylonCreateDeviceByIndex, 0, &hDev); + isopened = TRUE; + PYLONFN(PylonDeviceOpen, hDev, PYLONC_ACCESS_MODE_CONTROL | PYLONC_ACCESS_MODE_STREAM | PYLONC_ACCESS_MODE_EXCLUSIVE); + disableauto(); + PYLONFN(PylonDeviceFeatureFromString, hDev, "CameraOperationMode", "LongExposure"); + PYLONFN(PylonDeviceFeatureFromString, hDev, "UserSetSelector", "HighGain"); // set high gain selector + PYLONFN(PylonDeviceFeatureFromString, hDev, "AcquisitionMode", "SingleFrame"); + PYLONFN(PylonDeviceExecuteCommandFeature, hDev, "UserSetLoad"); // load high gain mode + if(PylonDeviceFeatureIsReadable(hDev, "DeviceModelName")){ + size_t l = BUFSIZ-1; + PYLONFN(PylonDeviceFeatureToString, hDev, "DeviceModelName", camname, &l); + DBG("Using camera %s\n", camname); + }else strcpy(camname, "Unknown camera"); + if(!getgeom()) WARNX("Can't get current frame format"); + + PylonDeviceRegisterRemovalCallback(hDev, removalCallbackFunction, &hCb); + PYLON_STREAMGRABBER_HANDLE hGrabber; + PYLONFN(PylonDeviceGetStreamGrabber, hDev, 0, &hGrabber); + PYLONFN(PylonStreamGrabberOpen, hGrabber); + PYLONFN(PylonStreamGrabberGetPayloadSize, hDev, hGrabber, &payloadsize); + DBG("payload sz %zd", payloadsize); + PylonStreamGrabberClose(hGrabber); + FREE(imgBuf); + imgBuf = MALLOC(unsigned char, payloadsize); + return TRUE; +} + +static int setbitdepth(int i){ + if(i == 0){ // 8 bit + if(!PylonDeviceFeatureIsAvailable( hDev, "EnumEntry_PixelFormat_Mono8" )) return FALSE; + PYLONFN(PylonDeviceFeatureFromString, hDev, "PixelFormat", "Mono8"); + is16bit = FALSE; + DBG("8 bit"); + }else{ // 16 bit + if(!PylonDeviceFeatureIsAvailable( hDev, "EnumEntry_PixelFormat_Mono16" )) return FALSE; + PYLONFN(PylonDeviceFeatureFromString, hDev, "PixelFormat", "Mono16"); + is16bit = TRUE; + DBG("16 bit"); + } + PYLON_STREAMGRABBER_HANDLE hGrabber; + PYLONFN(PylonDeviceGetStreamGrabber, hDev, 0, &hGrabber); + PYLONFN(PylonStreamGrabberOpen, hGrabber); + PYLONFN(PylonStreamGrabberGetPayloadSize, hDev, hGrabber, &payloadsize); + DBG("payload sz %zd", payloadsize); + PylonStreamGrabberClose(hGrabber); + FREE(imgBuf); + imgBuf = MALLOC(unsigned char, payloadsize); + return TRUE; +} + +// stub function: the capture process is blocking +static int pollcapt(capture_status *st, float *remain){ + if(st) *st = CAPTURE_READY; + if(remain) *remain = 0.f; + return TRUE; +} + +static int capture(IMG *ima){ + FNAME(); + if(!ima || !ima->data || !imgBuf || !isopened) return FALSE; + static int toohot = FALSE; + float_values f; + if(!getFloat("DeviceTemperature", &f)) WARNX("Can't get temperature"); + else{ + DBG("Temperature: %.1f", f.val); + if(f.val > 80.){ + WARNX("Device too hot"); + toohot = TRUE; + }else if(toohot && f.val < 75.){ + DBG("Device temperature is normal"); + toohot = FALSE; + } + } + PylonGrabResult_t grabResult; + _Bool bufferReady; + GENAPIC_RESULT res = PylonDeviceGrabSingleFrame(hDev, 0, imgBuf, payloadsize, + &grabResult, &bufferReady, 500 + (uint32_t)expostime); + if(res != GENAPI_E_OK || !bufferReady){ + WARNX("res != GENAPI_E_OK || !bufferReady"); + return FALSE; + } + if(grabResult.Status != Grabbed){ + WARNX("grabResult.Status != Grabbed"); + return FALSE; + } + int width = grabResult.SizeX, height = grabResult.SizeY, stride = grabResult.SizeX + grabResult.PaddingX; + if(is16bit){ + int s2 = stride<<1, w2 = width<<1; + OMP_FOR() + for(int y = 0; y < height; ++y){ + uint16_t *Out = &ima->data[y*width]; + const uint8_t *In = &imgBuf[y*s2]; + memcpy(Out, In, w2); + } + }else{ + OMP_FOR() + for(int y = 0; y < height; ++y){ + uint16_t *Out = &ima->data[y*width]; + const uint8_t *In = &imgBuf[y*stride]; + for(int x = 0; x < width; ++x){ + *Out++ = *In++; + } + } + } + ima->bitpix = (is16bit) ? 16 : 8; + return TRUE; +} + +// Basler have no "brightness" parameter +static int setbrightness(_U_ float b){ + FNAME(); + return FALSE; +} + +static int setexp(float e){ + FNAME(); + if(!isopened) return FALSE; + e *= 1e6f; + if(!setFloat("ExposureTime", e)){ + WARNX("Can't set expose time %g", e); + return FALSE; + } + float_values f; + if(!getFloat("ExposureTime", &f)) return FALSE; + expostime = (uint32_t)(f.val); + DBG("EXPOSURE time: need %f, real %f", e, f.val); + return TRUE; +} + +static int setgain(float e){ + FNAME(); + if(!isopened) return FALSE; + if(!setFloat("Gain", e)){ + WARNX("Can't set gain %g", e); + return FALSE; + } + DBG("GAIN -> %f", e); + return TRUE; +} + +static int changeformat(frameformat *fmt){ + FNAME(); + if(!isopened) return FALSE; + if(!getbin(NULL, NULL)){curhbin = 1; curvbin = 1;} + fmt->h /= curvbin; fmt->yoff /= curvbin; + fmt->w /= curhbin; fmt->xoff /= curhbin; + DBG("set geom %dx%d (off: %dx%d)", fmt->w, fmt->h, fmt->xoff, fmt->yoff); + setInt("Width", fmt->w); + setInt("Height", fmt->h); + setInt("OffsetX", fmt->xoff); + setInt("OffsetY", fmt->yoff); + int64_values i; + if(getInt("Width", &i)) camera.geometry.w = fmt->w = i.val * curhbin; + if(getInt("Height", &i)) camera.geometry.h = fmt->h = i.val * curvbin; + if(getInt("OffsetX", &i)) camera.geometry.xoff = fmt->xoff = i.val * curhbin; + if(getInt("OffsetY", &i)) camera.geometry.yoff = fmt->yoff = i.val * curvbin; + return TRUE; +} + +static int getgain(float *g){ + FNAME(); + float_values v; + if(!getFloat("Gain", &v)) return FALSE; + if(g) *g = (float)v.val; + return TRUE; +} + +static int gainmax(float *g){ + FNAME(); + float_values v; + if(!getFloat("Gain", &v)) return FALSE; + if(g) *g = (float)v.max; + return TRUE; +} + +static int modelname(char *buf, int bufsz){ + strncpy(buf, camname, bufsz); + return TRUE; +} + +static int setbin(int binh, int binv){ + if(!PylonDeviceFeatureIsAvailable(hDev, "BinningVertical") || + !PylonDeviceFeatureIsAvailable(hDev, "BinningHorizontal")){ + DBG("No Vbin / Hbin"); + return FALSE; + } + if(setInt("BinningVertical", (int64_t)binv) && + setInt("BinningHorizontal", (int64_t)binh)) return TRUE; + DBG("Can't set v or h"); + return FALSE; +} + +static int gett(float *t){ + if(!t) return FALSE; + float_values fv; + if(!getFloat("DeviceTemperature", &fv)) return FALSE; + *t = fv.val; + return TRUE; +} + +static int setfanspd(_U_ fan_speed s){ + return FALSE; +} +static int shutter(_U_ shutter_op cmd){ + return FALSE; +} + +static int ffalse(_U_ float f){ return FALSE; } +static int fpfalse(_U_ float *f){ return FALSE; } +static int ifalse(_U_ int i){ return FALSE; } +static int vtrue(){ return TRUE; } +static int ipfalse(_U_ int *i){ return FALSE; } +static void vstub(){ return ;} + +/* + * Global objects: camera, focuser and wheel + */ +Camera camera = { + .check = connect, + .close = disconnect, + .pollcapture = pollcapt, + .capture = capture, + .cancel = vstub, + .startexposition = vtrue, + // setters: + .setDevNo = setdevno, + .setbrightness = setbrightness, + .setexp = setexp, + .setgain = setgain, + .setT = ffalse, + .setbin = setbin, + .setnflushes = ifalse, + .shuttercmd = shutter, + .confio = ifalse, + .setio = ifalse, + .setframetype = ifalse, // set DARK or NORMAL: no shutter -> no darks + .setbitdepth = setbitdepth, + .setfastspeed = ifalse, + .setgeometry = changeformat, + .setfanspeed = setfanspd, + // getters: + .getbrightness = fpfalse, + .getModelName = modelname, + .getgain = getgain, + .getmaxgain = gainmax, + .getgeomlimits = geometrylimits, + .getTcold = fpfalse, + .getThot = fpfalse, + .getTbody = gett, + .getbin = getbin, + .getio = ipfalse, +}; diff --git a/CMakeLists.txt b/CMakeLists.txt index 97ae53f..a9497a9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,6 +16,7 @@ option(ZWO "Add support of ZWO cameras" OFF) option(FLI "Add support of FLI cameras" OFF) option(BASLER "Add support of BASLER cameras" OFF) option(HIKROBOT "Add support of HIKROBOT cameras" OFF) +option(FLYCAP "Add support of Grasshopper FlyCap cameras" OFF) # default flags set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -W -Wextra -std=gnu99") @@ -92,6 +93,9 @@ endif() if(BASLER) add_subdirectory(BASLER_cameras) endif() +if(FLYCAP) + add_subdirectory(GRH_cameras) +endif() # directory should contain dir locale/ru for gettext translations diff --git a/Dummy_cameras/dummyfunc.c b/Dummy_cameras/dummyfunc.c index 0573734..575b314 100644 --- a/Dummy_cameras/dummyfunc.c +++ b/Dummy_cameras/dummyfunc.c @@ -74,6 +74,7 @@ static int camcapt(IMG *ima){ *d++ = (uint16_t)((1 + sin((n+x) * M_PI/50)*sin((n+y) * M_PI/100))*32767.); } ++n; + ima->bitpix = 16; return TRUE; } diff --git a/FLI_cameras/flifunc.c b/FLI_cameras/flifunc.c index ccdb9ca..b58291f 100644 --- a/FLI_cameras/flifunc.c +++ b/FLI_cameras/flifunc.c @@ -67,6 +67,7 @@ static capture_status capStatus = CAPTURE_NO; static int curhbin = 1, curvbin = 1; static long filterpos = -1, filtermax = -1; // filter position static long focuserpos = -1, focmaxpos = -1; // focuser position +static int is16bit = TRUE; static int fli_init(){ char libver[LIBVERSIZ]; // FLI library version @@ -465,6 +466,7 @@ static int fli_capt(IMG *ima){ TRYFUNC(FLIGrabRow, camdev, &ima->data[row * ima->w], ima->w); if(fli_err) return FALSE; } + ima->bitpix = is16bit ? 16 : 8; return TRUE; } @@ -619,6 +621,7 @@ static int fli_setbitdepth(int i){ flibitdepth_t depth = i ? FLI_MODE_16BIT : FLI_MODE_8BIT; TRYFUNC(FLISetBitDepth, camdev, depth); if(fli_err) return FALSE; + is16bit = (i) ? TRUE : FALSE; return TRUE; } diff --git a/GRH_cameras/CMakeLists.txt b/GRH_cameras/CMakeLists.txt new file mode 100644 index 0000000..3d26ec3 --- /dev/null +++ b/GRH_cameras/CMakeLists.txt @@ -0,0 +1,15 @@ +cmake_minimum_required(VERSION 3.20) +set(CCDLIB devflycap) + +SET(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}) +find_package(PkgConfig REQUIRED) +find_package(FLYCAP REQUIRED) +pkg_check_modules(${CCDLIB} REQUIRED usefull_macros) + +aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR} SRC) +include_directories(${${CCDLIB}_INCLUDE_DIRS} ${FLYCAP_INCLUDE_DIRS} ..) +link_directories(${${CCDLIB}_LIBRARY_DIRS} ${FLYCAP_LIBRARY_DIRS}) + +add_library(${CCDLIB} SHARED ${SRC}) +target_link_libraries(${CCDLIB} ${${CCDLIB}_LIBRARIES} ${FLYCAP_LIBRARY} ${FLYCAP_LIBRARYC} -fPIC) +install(TARGETS ${CCDLIB} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}) diff --git a/GRH_cameras/FindFLYCAP.cmake b/GRH_cameras/FindFLYCAP.cmake new file mode 100644 index 0000000..db436f9 --- /dev/null +++ b/GRH_cameras/FindFLYCAP.cmake @@ -0,0 +1,40 @@ +# - Try to find libflycapture +# Once done this will define +# +# FLYCAP_FOUND - system has libflycapture +# FLYCAP_INCLUDE_DIR - include directory +# FLYCAP_LIBRARIES - Link these to use libflycapture + +# Copyright (c) 2021, Edward V. Emelianov +# +# Redistribution and use is allowed according to the terms of the GPLv2/GPLv3. + +include(GNUInstallDirs) + +find_path(FLYCAP_INCLUDE_DIR FlyCapture2.h + PATH_SUFFIXES libflycapture flycapture + PATHS /usr/include /usr/local/include /opt/include /opt/local/include +) +find_path(FLYCAP_LIBRARY_DIR libflycapture.so + PATHS /lib /lib64 /usr/lib /usr/lib64 /opt/lib /opt/lib64 /usr/local/lib /usr/local/lib64 +) +find_library(FLYCAP_LIBRARY NAMES flycapture + PATHS /lib /lib64 /usr/lib /usr/lib64 /opt/lib /opt/lib64 /usr/local/lib /usr/local/lib64 +) +find_library(FLYCAP_LIBRARYC NAMES flycapture-c + PATHS /lib /lib64 /usr/lib /usr/lib64 /opt/lib /opt/lib64 /usr/local/lib /usr/local/lib64 +) + +find_package_handle_standard_args(FLYCAP DEFAULT_MSG FLYCAP_INCLUDE_DIR FLYCAP_LIBRARY FLYCAP_LIBRARYC FLYCAP_LIBRARY_DIR) + +if(FLYCAP_FOUND) + set(FLYCAP_INCLUDE_DIRS ${FLYCAP_INCLUDE_DIR}) + set(FLYCAP_LIBRARIES ${FLYCAP_LIBRARY} ${FLYCAP_LIBRARYC}) + set(FLYCAP_LIBRARY_DIRS ${FLYCAP_LIBRARY_DIR}) +# message("FLYCAP include dir = ${FLYCAP_INCLUDE_DIRS}") +# message("FLYCAP lib = ${FLYCAP_LIBRARIES}") +# message("FLYCAP libdir = ${FLYCAP_LIBRARY_DIRS}") + mark_as_advanced(FLYCAP_INCLUDE_DIRS FLYCAP_LIBRARIES FLYCAP_LIBRARY_DIRS) +else() + message("FLYCAP not found") +endif(FLYCAP_FOUND) diff --git a/GRH_cameras/grasshopper.c b/GRH_cameras/grasshopper.c new file mode 100644 index 0000000..8fb5248 --- /dev/null +++ b/GRH_cameras/grasshopper.c @@ -0,0 +1,476 @@ +/* + * This file is part of the CCD_Capture project. + * Copyright 2023 Edward V. Emelianov . + * + * 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 . + */ + +#include +#include +#include +#include +#include +#include + +#include "basestructs.h" +#include "omp.h" + +extern Camera camera; + +static fc2Context context; +static fc2PGRGuid guid; +static fc2Error err = FC2_ERROR_OK; + +static int isopened = FALSE, is16bit = FALSE; +static char camname[BUFSIZ] = {0}; + +#ifndef Stringify +#define Stringify(x) #x +#endif +#define FC2FN(fn, ...) do{err = FC2_ERROR_OK; if(FC2_ERROR_OK != (err=fn(context __VA_OPT__(,) __VA_ARGS__))){ \ + WARNX(Stringify(fn) "(): %s", fc2ErrorToDescription(err)); return FALSE;}}while(0) + +static void disconnect(){ + FNAME(); + if(!isopened) return; + fc2DestroyContext(context); + isopened = FALSE; +} + +static int getfloat(fc2PropertyType t, float *f){ + fc2Property prop = {0}; + prop.type = t; + FC2FN(fc2GetProperty, &prop); + if(!prop.present){ + DBG("No property %d", t); + return FALSE; + } + DBG("property %d: abs=%f, vala=%u, valb=%u", t, + prop.absValue, prop.valueA, prop.valueB); + if(f) *f = prop.absValue; + return TRUE; +} + +/** + * @brief setfloat - set absolute property value (float) + * @param t - type of property + * @param f - new value + * @return 1 if all OK + */ +static int setfloat(fc2PropertyType t, float f){ + fc2Property prop = {0}; + prop.type = t; + fc2PropertyInfo i = {0}; + i.type = t; + FC2FN(fc2GetProperty, &prop); + FC2FN(fc2GetPropertyInfo, &i); + if(!prop.present || !i.present) return 0; + if(prop.autoManualMode){ + if(!i.manualSupported){ + WARNX("Can't set auto-only property"); + return FALSE; + } + prop.autoManualMode = false; + } + if(!prop.absControl){ + if(!i.absValSupported){ + WARNX("Can't set non-absolute property to absolute value"); + return FALSE; + } + prop.absControl = true; + } + if(!prop.onOff){ + if(!i.onOffSupported){ + WARNX("Can't set property ON"); + return FALSE; + } + prop.onOff = true; + } + if(prop.onePush && i.onePushSupported) prop.onePush = false; + prop.valueA = prop.valueB = 0; + prop.absValue = f; + FC2FN(fc2SetProperty, &prop); + // now check + FC2FN(fc2GetProperty, &prop); + if(fabsf(prop.absValue - f) > 0.02f){ + WARNX("Can't set property! Got %g instead of %g.", prop.absValue, f); + return FALSE; + } + return TRUE; +} + +static int propOnOff(fc2PropertyType t, BOOL onOff){ + fc2Property prop = {0}; + prop.type = t; + fc2PropertyInfo i = {0}; + i.type = t; + FC2FN(fc2GetPropertyInfo, &i); + FC2FN(fc2GetProperty, &prop); + if(!prop.present || !i.present) return 0; + if(prop.onOff == onOff) return 0; + if(!i.onOffSupported){ + WARNX("Property doesn't support state OFF"); + return 0; + } + prop.onOff = onOff; + FC2FN(fc2SetProperty, &prop); + FC2FN(fc2GetProperty, &prop); + if(prop.onOff != onOff){ + WARNX("Can't change property OnOff state"); + return 0; + } + return 1; +} + +static void disableauto(){ + if(!isopened) return; + propOnOff(FC2_AUTO_EXPOSURE, false); + propOnOff(FC2_WHITE_BALANCE, false); + propOnOff(FC2_GAMMA, false); + propOnOff(FC2_TRIGGER_MODE, false); + propOnOff(FC2_TRIGGER_DELAY, false); + propOnOff(FC2_FRAME_RATE, false); +} + +static int connect(){ + FNAME(); + unsigned int numDevices; + disconnect(); + DBG("fc2CreateContext"); + if(FC2_ERROR_OK != (err = fc2CreateContext(&context))){ + WARNX("fc2CreateContext(): %s", fc2ErrorToDescription(err)); + return FALSE; + } + DBG("fc2GetNumOfCameras"); + FC2FN(fc2GetNumOfCameras, &numDevices); + DBG("test"); + if(numDevices == 0){ + WARNX("No cameras detected!"); + fc2DestroyContext(context); + return FALSE; + } + camera.Ndevices = numDevices; + DBG("Found %d camera[s]", numDevices); + return TRUE; +} + +static int getbin(int *binh, int *binv){ + //unsigned int h, v; + //FC2FN(fc2GetGigEImageBinningSettings, &h, &v); + //green("got: %u x %u", h, v); + if(binh) *binh = 1; + if(binv) *binv = 1; + return TRUE; +} + +static int getformat(frameformat *fmt){ + if(!fmt) return FALSE; + unsigned int packsz; float pc; + fc2Format7ImageSettings f7; + FC2FN(fc2GetFormat7Configuration, &f7, &packsz, &pc); + fmt->h = f7.height; fmt->w = f7.width; + fmt->xoff = f7.offsetX; fmt->yoff = f7.offsetY; + if(f7.pixelFormat != FC2_PIXEL_FORMAT_MONO16){ + is16bit = FALSE; + DBG("8 bit"); + }else{ + is16bit = TRUE; + DBG("16 bit"); + } + return TRUE; +} + +static int getgeom(){ + FNAME(); + if(!isopened) return FALSE; + fc2Format7Info f = {.mode = FC2_MODE_0}; + BOOL b; + FC2FN(fc2GetFormat7Info, &f, &b); + + if(!b) return FALSE; + camera.array.h = f.maxHeight; + camera.array.w = f.maxWidth; + camera.array.xoff = camera.array.yoff = 0; + camera.field = camera.array; + getformat(&camera.geometry); + return TRUE; +} + +static int geometrylimits(frameformat *max, frameformat *step){ + FNAME(); + if(!isopened || !max || !step) return FALSE; + fc2Format7Info f = {.mode = FC2_MODE_0}; + BOOL b; + fc2Format7Info i = {0}; + FC2FN(fc2GetFormat7Info, &i, &b); + if(!b) return FALSE; + max->h = f.maxHeight; max->w = f.maxWidth; + max->xoff = f.maxWidth - f.offsetHStepSize; + max->yoff = f.maxHeight - f.offsetVStepSize; + step->w = f.imageHStepSize; + step->h = f.imageVStepSize; + step->xoff = f.offsetHStepSize; + step->yoff = f.offsetVStepSize; + return TRUE; +} + +static int setdevno(int N){ + if(N > camera.Ndevices - 1) return FALSE; + FC2FN(fc2GetCameraFromIndex, 0, &guid); + FC2FN(fc2Connect, &guid); + isopened = TRUE; + disableauto(); + fc2CameraInfo caminfo; + FC2FN(fc2GetCameraInfo, &caminfo); + if(err == FC2_ERROR_OK){ + strncpy(camname, caminfo.modelName, BUFSIZ-1); + DBG("Using camera %s\n", camname); + }else strcpy(camname, "Unknown camera"); + + if(!getbin(NULL, NULL)) WARNX("Can't get current binning"); + if(!getgeom()) WARNX("Can't get current frame format"); + return TRUE; +} + +// stub function: the capture process is blocking +static int pollcapt(capture_status *st, float *remain){ + if(st) *st = CAPTURE_READY; + if(remain) *remain = 0.f; + return TRUE; +} + +static int GrabImage(fc2Image *convertedImage){ + if(!convertedImage) return FALSE; + int ret = FALSE; + fc2Image rawImage; + // start capture + FC2FN(fc2StartCapture); + err = fc2CreateImage(&rawImage); + if(err != FC2_ERROR_OK){ + WARNX("Error in fc2CreateImage: %s", fc2ErrorToDescription(err)); + fc2StopCapture(context); + return FALSE; + } + // Retrieve the image + err = fc2RetrieveBuffer(context, &rawImage); + if(err != FC2_ERROR_OK){ + WARNX("Error in fc2RetrieveBuffer: %s", fc2ErrorToDescription(err)); + goto rtn; + } + // Convert image to gray (we need to convert RAW into same bitpix + fc2PixelFormat fmt = (is16bit) ? FC2_PIXEL_FORMAT_MONO16 : FC2_PIXEL_FORMAT_MONO8; + err = fc2ConvertImageTo(fmt, &rawImage, convertedImage); + if(err != FC2_ERROR_OK){ + WARNX("Error in fc2ConvertImageTo: %s", fc2ErrorToDescription(err)); + goto rtn; + } + ret = TRUE; + DBG("raw: ds=%u, rds=%u, str=%u; conv: ds=%u, rds=%u, str=%u", rawImage.dataSize, rawImage.receivedDataSize, rawImage.stride, + convertedImage->dataSize, convertedImage->receivedDataSize, convertedImage->stride); +rtn: + fc2StopCapture(context); + fc2DestroyImage(&rawImage); + return ret; +} + +static int capture(IMG *ima){ + FNAME(); + if(!ima || !ima->data || !isopened) return FALSE; + static int toohot = FALSE; + float f; + if(getfloat(FC2_TEMPERATURE, &f)){ + DBG("Temperature: %.1f", f); + if(f > 80.){ + WARNX("Device is too hot"); + toohot = TRUE; + }else if(toohot && f < 75.){ + DBG("Device temperature is normal"); + toohot = FALSE; + } + } + + fc2Image convertedImage; + err = fc2CreateImage(&convertedImage); + if(err != FC2_ERROR_OK){ + WARNX("capture_grasshopper(): can't create image, %s", fc2ErrorToDescription(err)); + return FALSE; + } + if(!GrabImage(&convertedImage)){ + WARNX("Can't grab image"); + fc2DestroyImage(&convertedImage); + return FALSE; + } + int width = convertedImage.cols, height = convertedImage.rows, stride = convertedImage.stride; + DBG("w=%d, h=%d, s=%d", width, height, stride); + if(is16bit){ + int w2 = width<<1; + OMP_FOR() + for(int y = 0; y < height; ++y){ + uint16_t *Out = &ima->data[y*width]; + const uint8_t *In = &convertedImage.pData[y*stride]; + memcpy(Out, In, w2); + } + }else{ + OMP_FOR() + for(int y = 0; y < height; ++y){ + uint16_t *Out = &ima->data[y*width]; + const uint8_t *In = &convertedImage.pData[y*stride]; + for(int x = 0; x < width; ++x){ + *Out++ = *In++; + } + } + } + ima->bitpix = is16bit ? 16 : 8; + fc2DestroyImage(&convertedImage); + return TRUE; +} + +static int setbrightness(float b){ + return setfloat(FC2_BRIGHTNESS, b); +} + +static int setexp(float e){ + FNAME(); + if(!isopened) return FALSE; + e *= 1e3f; + if(!setfloat(FC2_SHUTTER, e)){ + WARNX("Can't set expose time %g", e); + return FALSE; + } + return TRUE; +} + +static int setgain(float e){ + FNAME(); + if(!isopened) return FALSE; + if(!setfloat(FC2_GAIN, e)){ + WARNX("Can't set gain %g", e); + return FALSE; + } + DBG("GAIN -> %f", e); + return TRUE; +} + +static int changeformat(frameformat *fmt){ + FNAME(); + if(!isopened) return FALSE; + DBG("set geom %dx%d (off: %dx%d)", fmt->w, fmt->h, fmt->xoff, fmt->yoff); + BOOL b; + fc2Format7ImageSettings f7; + f7.mode = FC2_MODE_0; + f7.offsetX = fmt->xoff; + f7.offsetY = fmt->yoff; + f7.width = fmt->w; + f7.height = fmt->h; + DBG("offx=%d, offy=%d, w=%d, h=%d ", f7.offsetX, f7.offsetY, f7.width, f7.height); + f7.pixelFormat = (is16bit) ? FC2_PIXEL_FORMAT_MONO16 : FC2_PIXEL_FORMAT_MONO8; + fc2Format7PacketInfo f7p; + FC2FN(fc2ValidateFormat7Settings, &f7, &b, &f7p); + if(!b) return FALSE; // invalid + FC2FN(fc2SetFormat7Configuration, &f7, f7p.recommendedBytesPerPacket); + getformat(&camera.geometry); + return TRUE; +} + +static int setbitdepth(int i){ + frameformat fmt; + getformat(&fmt); + int o16bit = is16bit; + if(i == 0) is16bit = FALSE; // 8 bit + else is16bit = TRUE; + if(!changeformat(&fmt)){ + is16bit = o16bit; + return FALSE; + } + return TRUE; +} + +static int getgain(float *g){ + return getfloat(FC2_GAIN, g); +} + +static int gainmax(float *g){ + if(g) *g = 32.f; + return TRUE; +} + +static int modelname(char *buf, int bufsz){ + strncpy(buf, camname, bufsz); + return TRUE; +} + +// can't do binning +static int setbin(int binh, int binv){ + //FC2FN(fc2SetGigEImageBinningSettings, binh, binv); + //getbin(&binh, &binv); + if(binh != 1 || binv != 1) return FALSE; + return TRUE; +} + +static int gett(float *t){ + return getfloat(FC2_TEMPERATURE, t); +} + +static int setfanspd(_U_ fan_speed s){ + return FALSE; +} +static int shutter(_U_ shutter_op cmd){ + return FALSE; +} + +static int ffalse(_U_ float f){ return FALSE; } +static int fpfalse(_U_ float *f){ return FALSE; } +static int ifalse(_U_ int i){ return FALSE; } +static int vtrue(){ return TRUE; } +static int ipfalse(_U_ int *i){ return FALSE; } +static void vstub(){ return ;} + +/* + * Global objects: camera, focuser and wheel + */ +Camera camera = { + .check = connect, + .close = disconnect, + .pollcapture = pollcapt, + .capture = capture, + .cancel = vstub, + .startexposition = vtrue, + // setters: + .setDevNo = setdevno, + .setbrightness = setbrightness, + .setexp = setexp, + .setgain = setgain, + .setT = ffalse, + .setbin = setbin, + .setnflushes = ifalse, + .shuttercmd = shutter, + .confio = ifalse, + .setio = ifalse, + .setframetype = ifalse, // set DARK or NORMAL: no shutter -> no darks + .setbitdepth = setbitdepth, + .setfastspeed = ifalse, + .setgeometry = changeformat, + .setfanspeed = setfanspd, + // getters: + .getbrightness = fpfalse, + .getModelName = modelname, + .getgain = getgain, + .getmaxgain = gainmax, + .getgeomlimits = geometrylimits, + .getTcold = fpfalse, + .getThot = fpfalse, + .getTbody = gett, + .getbin = getbin, + .getio = ipfalse, +}; diff --git a/HIKROBOT_cameras/mvsfunc.c b/HIKROBOT_cameras/mvsfunc.c index 1f6d63d..83353dc 100644 --- a/HIKROBOT_cameras/mvsfunc.c +++ b/HIKROBOT_cameras/mvsfunc.c @@ -1,616 +1,618 @@ -/* - * This file is part of the CCD_Capture project. - * Copyright 2022 Edward V. Emelianov . - * - * 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 . - */ - -#include -#include -#include -#include -#include - -#include "basestructs.h" -#include "omp.h" - -#ifndef FLT_EPSILON -#define FLT_EPSILON 1.19209290E-07F -#endif - -extern Camera camera; - -static MV_CC_DEVICE_INFO_LIST stDeviceList; -static void *handle = NULL; -static char camname[BUFSIZ] = {0}; -//static long cam_err, tmpl; -static capture_status capStatus = CAPTURE_NO; -static int curhbin = 1, curvbin = 1; -static double starttime = 0.; // time when exposure started -static float exptime = 0.; // exposition time (in seconds) -static MV_FRAME_OUT_INFO_EX stImageInfo = {0}; // last image info -static uint8_t *pdata = NULL; -static int pdatasz = 0; -static int lastecode = MV_OK; - -static struct{ - float maxgain; - float mingain; - float maxbright; - float minbright; - float minexp; - float maxexp; - int maxbin; -} extrvalues = {0}; // extremal values - -static void printErr(){ - const char *errcode = "unknown error"; - switch(lastecode){ - case MV_E_HANDLE: errcode = "Error or invalid handle "; break; - case MV_E_SUPPORT: errcode = "Not supported function "; break; - case MV_E_BUFOVER: errcode = "Cache is full "; break; - case MV_E_CALLORDER: errcode = "Function calling order error "; break; - case MV_E_PARAMETER: errcode = "Incorrect parameter "; break; - case MV_E_RESOURCE: errcode = "Applying resource failed "; break; - case MV_E_NODATA: errcode = "No data "; break; - case MV_E_PRECONDITION: errcode = "Precondition error, or running environment changed "; break; - case MV_E_VERSION: errcode = "Version mismatches "; break; - case MV_E_NOENOUGH_BUF: errcode = "Insufficient memory "; break; - case MV_E_ABNORMAL_IMAGE: errcode = "Abnormal image, maybe incomplete image because of lost packet "; break; - case MV_E_UNKNOW: errcode = "Unknown error "; break; - case MV_E_GC_GENERIC: errcode = "General error "; break; - case MV_E_GC_ACCESS: errcode = "Node accessing condition error "; break; - case MV_E_ACCESS_DENIED: errcode = "No permission "; break; - case MV_E_BUSY: errcode = "Device is busy, or network disconnected "; break; - case MV_E_NETER: errcode = "Network error "; - } - WARNX("CMOS error: %s", errcode); -} - -#define TRY(fn, ...) do{lastecode = MV_CC_ ## fn(handle __VA_OPT__(,) __VA_ARGS__); if(lastecode != MV_OK) printErr(); }while(0) -#define ONERR() if(MV_OK != lastecode) -#define ONOK() if(MV_OK == lastecode) - -static int changeenum(const char *key, uint32_t val){ - if(!handle) return FALSE; - MVCC_ENUMVALUE e; - TRY(GetEnumValue, key, &e); - ONERR(){ - WARNX("Enum '%s' is absent", key); - return FALSE; - } - DBG("Try to change '%s' to %u, cur=%u", key, val, e.nCurValue); - if(e.nCurValue == val) return TRUE; - TRY(SetEnumValue, key, val); - ONERR(){ - WARNX("Cant change %s to %d, supported values are:", key, val); - for(int i = 0; i < (int)e.nSupportedNum; ++i){ - fprintf(stderr, "%s%u", i ? ", " : "", e.nSupportValue[i]); - } - fprintf(stderr, "\n"); - return FALSE; - } - TRY(GetEnumValue, key, &e); - ONERR() return FALSE; - if(e.nCurValue == val) return TRUE; - WARNX("New value of '%s' changed to %d, not to %d", key, e.nCurValue, val); - return FALSE; -} - -static int changeint(const char *key, uint32_t val){ - if(!handle) return FALSE; - MVCC_INTVALUE i; - TRY(GetIntValue, key, &i); - ONERR(){ - WARNX("Int '%s' is absent", key); - return FALSE; - } - if(i.nCurValue == val) return TRUE; - TRY(SetIntValue, key, val); - ONERR(){ - WARNX("Cant change %s to %u; available range is %u..%u", key, val, i.nMin, i.nMax); - return FALSE; - } - TRY(GetIntValue, key, &i); - ONERR() return FALSE; - if(i.nCurValue == val) return TRUE; - WARNX("New value of '%s' changed to %d, not to %d", key, i.nCurValue, val); - return FALSE; -} - -static int changefloat(const char *key, float val){ - if(!handle) return FALSE; - MVCC_FLOATVALUE f; - TRY(GetFloatValue, key, &f); - ONERR(){ - WARNX("Float '%s' is absent", key); - return FALSE; - } - if(f.fCurValue == val) return TRUE; - TRY(SetFloatValue, key, val); - ONERR(){ - WARNX("Cant change %s to %g; available range is %g..%g", key, val, f.fMin, f.fMax); - return FALSE; - } - TRY(GetFloatValue, key, &f); - ONERR() return FALSE; - if(fabs(f.fCurValue - val) < FLT_EPSILON) return TRUE; - WARNX("New value of '%s' changed to %g, not to %g", key, f.fCurValue, val); - return FALSE; -} - -static int cam_setbin(int binh, int binv){ - FNAME(); - if(!handle) return FALSE; - if(!changeenum("BinningHorizontal", binh)) return FALSE; - if(!changeenum("BinningVertical", binv)) return FALSE; - curhbin = binh; - curvbin = binv; - return TRUE; -} - -static int cam_getbin(int *h, int *v){ - MVCC_ENUMVALUE e; - TRY(GetEnumValue, "BinningHorizontal", &e); - ONERR() return FALSE; - curhbin = e.nCurValue; - //printf("Hbin supported = %d", e.nSupportedNum); - //for(int i = 0; i < (int)e.nSupportedNum; ++i) printf("\t%d", e.nSupportValue[i]); - TRY(GetEnumValue, "BinningVertical", &e); - ONERR() return FALSE; - curvbin = e.nCurValue; - //printf("Vbin supported = %d", e.nSupportedNum); - //for(int i = 0; i < (int)e.nSupportedNum; ++i) printf("\t%d", e.nSupportValue[i]); - if(h) *h = curhbin; - if(v) *v = curvbin; - return TRUE; -} - -static int cam_getgain(float *g){ - if(!handle) return FALSE; - MVCC_FLOATVALUE gain; - TRY(GetFloatValue, "Gain", &gain); - ONERR() return FALSE; - if(g) *g = gain.fCurValue; - extrvalues.maxgain = gain.fMax; - extrvalues.mingain = gain.fMin; - DBG("Gain: cur=%g, min=%g, max=%g", gain.fCurValue, gain.fMin, gain.fMax); - return TRUE; -} - -static int cam_getmaxgain(float *g){ - if(!handle) return FALSE; - if(g) *g = extrvalues.maxgain; - return TRUE; -} - -static int cam_setgain(float g){ - if(!handle) return FALSE; - return changefloat("Gain", g); -} - -static int cam_getbright(float *b){ - if(!handle) return FALSE; - DBG("Get brightness"); - MVCC_INTVALUE bright; - TRY(GetIntValue, "Brightness", &bright); - ONERR() return FALSE; - if(b) *b = bright.nCurValue; - extrvalues.maxgain = bright.nMax; - extrvalues.mingain = bright.nMin; - DBG("Brightness: cur=%d, min=%d, max=%d", bright.nCurValue, bright.nMin, bright.nMax); - return TRUE; -} - -static int cam_setbright(float b){ - if(!handle) return FALSE; - return changeint("Brightness", (uint32_t)b); -} - -static void PrintDeviceInfo(MV_CC_DEVICE_INFO* pstMVDevInfo){ - if(!pstMVDevInfo) return; - if(pstMVDevInfo->nTLayerType == MV_GIGE_DEVICE){ - int nIp1 = ((pstMVDevInfo->SpecialInfo.stGigEInfo.nCurrentIp & 0xff000000) >> 24); - int nIp2 = ((pstMVDevInfo->SpecialInfo.stGigEInfo.nCurrentIp & 0x00ff0000) >> 16); - int nIp3 = ((pstMVDevInfo->SpecialInfo.stGigEInfo.nCurrentIp & 0x0000ff00) >> 8); - int nIp4 = (pstMVDevInfo->SpecialInfo.stGigEInfo.nCurrentIp & 0x000000ff); - printf("Device Model Name: %s\n", pstMVDevInfo->SpecialInfo.stGigEInfo.chModelName); - strncpy(camname, (char*)pstMVDevInfo->SpecialInfo.stGigEInfo.chModelName, BUFSIZ-1); - printf("CurrentIp: %d.%d.%d.%d\n" , nIp1, nIp2, nIp3, nIp4); - printf("UserDefinedName: %s\n\n" , pstMVDevInfo->SpecialInfo.stGigEInfo.chUserDefinedName); - }else if (pstMVDevInfo->nTLayerType == MV_USB_DEVICE){ - printf("Device Model Name: %s\n", pstMVDevInfo->SpecialInfo.stUsb3VInfo.chModelName); - printf("UserDefinedName: %s\n\n", pstMVDevInfo->SpecialInfo.stUsb3VInfo.chUserDefinedName); - strncpy(camname, (char*)pstMVDevInfo->SpecialInfo.stUsb3VInfo.chModelName, BUFSIZ-1); - }else { - printf("Not support.\n"); - } -} - -static void cam_closecam(){ - DBG("CAMERA CLOSE"); - if(handle){ - MV_CC_StopGrabbing(handle); - TRY(CloseDevice); - ONERR() WARNX("Can't close opened camera"); - TRY(DestroyHandle); - ONERR() WARNX("Can't destroy camera handle"); - handle = NULL; - } - FREE(pdata); - pdatasz = 0; -} - -static int cam_findCCD(){ - DBG("Try to find HIKROBOT cameras .. "); - memset(&stDeviceList, 0, sizeof(MV_CC_DEVICE_INFO_LIST)); - if(MV_OK != MV_CC_EnumDevices(MV_GIGE_DEVICE | MV_USB_DEVICE, &stDeviceList)){ - WARNX("No HIKROBOT cameras found"); - return FALSE; - } - camera.Ndevices = stDeviceList.nDeviceNum; - if(stDeviceList.nDeviceNum > 0){ - for(uint32_t i = 0; i < stDeviceList.nDeviceNum; ++i){ - DBG("[device %d]:\n", i); - MV_CC_DEVICE_INFO* pDeviceInfo = stDeviceList.pDeviceInfo[i]; - if(!pDeviceInfo) continue; - PrintDeviceInfo(pDeviceInfo); - } - } else{ - WARNX("No HIKROBOT cameras found"); - return FALSE; - } - return TRUE; -} - -static int cam_setActiceCam(int n){ - DBG("SET ACTIVE #%d", n); - if(!camera.Ndevices && !cam_findCCD()) return FALSE; - if(n >= camera.Ndevices){ - return FALSE; - } - cam_closecam(); - lastecode = MV_CC_CreateHandleWithoutLog(&handle, stDeviceList.pDeviceInfo[n]); - ONERR(){ - WARNX("Can't create camera handle"); - printErr(); - return FALSE; - } - TRY(OpenDevice, MV_ACCESS_Exclusive, 0); - ONERR(){ - WARNX("Can't open camera file"); - return FALSE; - } - if(stDeviceList.pDeviceInfo[n]->nTLayerType == MV_GIGE_DEVICE){ - int nPacketSize = MV_CC_GetOptimalPacketSize(handle); - if(nPacketSize > 0){ - if(!changeint("GevSCPSPacketSize", nPacketSize)){ - WARNX("Can't set optimal packet size"); - } - } else{ - WARNX("Can't get optimal packet size"); - } - } - // set software trigger -/* MVCC_ENUMVALUE enumval; - MV_CC_GetEnumValue(handle, "TriggerMode", &enumval); - DBG("TRmode: %d", enumval.nSupportedNum); - for(uint16_t i = 0; i < enumval.nSupportedNum; ++i) fprintf(stderr, "\t%d: %u\n", i, enumval.nSupportValue[i]); - */ - if(!changeenum("TriggerMode", MV_TRIGGER_MODE_OFF)){ - WARNX("Can't turn off triggered mode"); - return FALSE; - } - if(!changeenum("AcquisitionMode", MV_ACQ_MODE_SINGLE)){ - WARNX("Can't set acquisition mode to single"); - return FALSE; - } - if(!changeenum("ExposureMode", MV_EXPOSURE_MODE_TIMED)){ - WARNX("Can't change exposure mode to timed"); - return FALSE; - } - if(!changeenum("ExposureAuto", MV_EXPOSURE_AUTO_MODE_OFF)){ - WARNX("Can't turn off auto exposure mode"); - return FALSE; - } - if(!changeenum("GainAuto", 0)){ - WARNX("Can't turn off auto gain"); - return FALSE; - } - MVCC_ENUMVALUE EnumValue; - TRY(GetEnumValue, "PixelFormat", &EnumValue); - ONOK(){ - DBG("PixelFormat=%x", EnumValue.nCurValue); -#ifdef EBUG - for(int i = 0; i < (int)EnumValue.nSupportedNum; ++i) fprintf(stderr, "\t\t%x\n", EnumValue.nSupportValue[i]); -#endif - } - TRY(GetEnumValue, "PixelSize", &EnumValue); - ONOK(){ - DBG("PixelSize=%d", EnumValue.nCurValue); -#ifdef EBUG - for(int i = 0; i < (int)EnumValue.nSupportedNum; ++i) fprintf(stderr, "\t\t%d\n", EnumValue.nSupportValue[i]); -#endif - } - cam_getgain(NULL); // get extremal gain values - cam_getbright(NULL); // get extremal brightness values - cam_getbin(NULL, NULL); // get current binning - MVCC_FLOATVALUE FloatValue; - // get extremal exptime values - TRY(GetFloatValue, "ExposureTime", &FloatValue); - ONOK(){ - extrvalues.maxexp = FloatValue.fMax / 1e6; - extrvalues.minexp = FloatValue.fMin / 1e6; - exptime = FloatValue.fCurValue / 1e6; - printf("Min exp: %g s, max exp: %g s\n", extrvalues.minexp, extrvalues.maxexp); - } - camera.pixX = camera.pixY = 0.; // unknown - MVCC_INTVALUE IntValue; - camera.array.xoff = camera.array.yoff = 0; - int *values[6] = {&camera.array.w, &camera.array.h, &camera.geometry.w, &camera.geometry.h, &camera.geometry.xoff, &camera.geometry.yoff}; - const char *names[2] = {"WidthMax", "HeightMax"};//, "Width", "Height", "OffsetX", "OffsetY"}; - for(int i = 0; i < 2; ++i){ - TRY(GetIntValue, names[i], &IntValue); - ONERR(){ - WARNX("Can't get %s", names[i]); return FALSE; - } - *values[i] = IntValue.nCurValue; - DBG("%s = %d", names[i], *values[i]); - } - camera.array.h *= curvbin; - camera.array.w *= curhbin; - camera.geometry = camera.array; - camera.field = camera.array; - pdatasz = camera.array.h * camera.array.w * 2; - DBG("\t\t2*w*h = %d", pdatasz); -#ifdef EBUG - MVCC_INTVALUE stParam = {0}; - TRY(GetIntValue, "PayloadSize", &stParam); - ONOK(){DBG("PAYLOAD: %u", stParam.nCurValue);} -#endif - pdata = MALLOC(uint8_t, pdatasz); // allocate max available buffer - return TRUE; -} - -static int cam_geomlimits(frameformat *l, frameformat *s){ - if(l) *l = camera.array; - if(s) *s = (frameformat){.w = 1, .h = 1, .xoff = 1, .yoff = 1}; - return TRUE; -} - -static int cam_startexp(){ - if(!handle || !pdata) return FALSE; - DBG("Start exposition"); - MV_CC_StopGrabbing(handle); - TRY(StartGrabbing); - ONERR() return FALSE; - starttime = dtime(); - capStatus = CAPTURE_PROCESS; - return TRUE; -} - -static int cam_pollcapt(capture_status *st, float *remain){ - if(!handle || !pdata) return FALSE; - DBG("capStatus = %d", capStatus); - if(capStatus == CAPTURE_READY){ - DBG("Capture ends"); - goto retn; - } - if(capStatus == CAPTURE_NO){ // start capture - goto retn; - } - if(capStatus == CAPTURE_PROCESS){ - DBG("PDATASZ=%d", pdatasz); - TRY(GetOneFrameTimeout, pdata, pdatasz, &stImageInfo, 50); - ONOK(){ - DBG("OK, ready"); - if(remain) *remain = 0.f; - if(st) *st = CAPTURE_READY; - capStatus = CAPTURE_NO; - return TRUE; - } - DBG("not ready"); - if(remain){ - float diff = exptime - (dtime() - starttime); - DBG("diff = %g", diff); - if(diff < -5.0){ - capStatus = CAPTURE_NO; - if(st) *st = CAPTURE_ABORTED; - return FALSE; - } - if(diff < 0.f) diff = 0.f; - *remain = diff; - } - }else{ // some error - if(st) *st = CAPTURE_ABORTED; - capStatus = CAPTURE_NO; - return FALSE; - } -retn: - if(st) *st = capStatus; - return TRUE; -} - -static int cam_capt(IMG *ima){ - if(!handle || !pdata) return FALSE; - if(!ima || !ima->data) return FALSE; - ; - int bytes = ima->h*ima->w * 2, stbytes = stImageInfo.nWidth * stImageInfo.nHeight * 2; - if(bytes > pdatasz) bytes = pdatasz; - if(bytes != stbytes) WARNX("Different sizes of image buffer & grabbed image"); - if(stbytes > bytes) bytes = stbytes; - DBG("Copy %d bytes (stbytes=%d)", bytes, stbytes); - MVCC_ENUMVALUE EnumValue; - TRY(GetEnumValue, "PixelSize", &EnumValue); - DBG("PixelSize = %u", EnumValue.nCurValue); - ONOK(){ -//green("pixsize=%d\n", EnumValue.nCurValue); - if(EnumValue.nCurValue == 16){ - memcpy(ima->data, pdata, bytes); - return TRUE; - }else if(EnumValue.nCurValue != 8){ - WARNX("Unsupported pixel size"); - return FALSE; - } - } - // transform 8bits to 16 - DBG("TRANSFORM 8 bit to 16"); - bytes /= 2; - uint8_t *ptr = (uint8_t*) pdata; - OMP_FOR() - for(int i = 0; i < bytes; ++i){ - ima->data[i] = (uint16_t) *ptr++; - } - return TRUE; -} - -static int cam_modelname(char *buf, int bufsz){ - strncpy(buf, camname, bufsz); - return TRUE; -} - -static int cam_setgeometry(frameformat *f){ - FNAME(); - if(!f || !handle) return FALSE; - DBG("getbin"); - if(!cam_getbin(NULL, NULL)) return FALSE; - DBG("set geom %dx%d (off: %dx%d)", f->w, f->h, f->xoff, f->yoff); - if(!changeint("Width", f->w * curhbin)) return FALSE; - if(!changeint("Height", f->h * curvbin)) return FALSE; - if(!changeint("OffsetX", f->xoff * curhbin)) return FALSE; - if(!changeint("OffsetY", f->yoff * curvbin)) return FALSE; - DBG("Success!"); - return TRUE; -} - -static int cam_settemp(float t){ - if(!handle) return FALSE; - if(!changeenum("DeviceTemperatureSelector", 0)) return FALSE; - if(!changeenum("DeviceTemperature", t)) return FALSE; - return TRUE; -} - -static int cam_gettemp(float *t){ - MVCC_FLOATVALUE fl; - TRY(GetFloatValue, "DeviceTemperature", &fl); - ONERR() return FALSE; - if(t) *t = fl.fCurValue; - return TRUE; -} - -static int cam_gettchip(float *t){ - if(!handle) return FALSE; - changeenum("DeviceTemperatureSelector", 0); // there's can be camera without this enume - return cam_gettemp(t); -} - -static int cam_gettbody(_U_ float *t){ - if(!handle) return FALSE; - if(!changeenum("DeviceTemperatureSelector", 1)) return FALSE; - return cam_gettemp(t); -} - -static void cam_cancel(){ - if(!handle) return; - TRY(StopGrabbing); -} - -static int cam_shutter(_U_ shutter_op cmd){ - return FALSE; -} - -/* -static int cam_confio(int io){ - if(!handle) return FALSE; - MVCC_ENUMVALUE e; - TRY(GetEnumValue, "LineSelector", &e); - ONERR() return FALSE; - int bit = 1; - for(int i = 0; i < (int)e.nSupportedNum; ++i, bit <<= 1){ - green("line %d: %d\n", e.nSupportValue[i]); - if(io & bit) printf("bit %d\n", i); - } - return TRUE; -} -*/ - -static int cam_setexp(float t){ // t is in seconds!! - if(!handle) return FALSE; - if(!changefloat("ExposureTime", t*1e6)) return FALSE; - exptime = t; - return TRUE; -} - -static int cam_setbitdepth(int i){ - if(!handle) return FALSE; - int d = i ? PixelType_Gvsp_Mono12 : PixelType_Gvsp_Mono8; - if(!changeenum("PixelFormat", d)){ - WARNX("pixformat"); - return FALSE; - } - d = i ? 16 : 8; - if(!changeenum("PixelSize", d)){ - WARNX("pixsz"); - return FALSE; - } - return TRUE; -} - -static int cam_setfanspd(_U_ fan_speed s){ - return FALSE; -} - -//static int cam_ffalse(_U_ float f){ return FALSE; } -static int cam_fpfalse(_U_ float *f){ return FALSE; } -static int cam_ifalse(_U_ int i){ return FALSE; } -static int cam_ipfalse(_U_ int *i){ return FALSE; } - -/* - * Global objects: camera, focuser and wheel - */ -Camera camera = { - .check = cam_findCCD, - .close = cam_closecam, - .pollcapture = cam_pollcapt, - .capture = cam_capt, - .cancel = cam_cancel, - .startexposition = cam_startexp, - // setters: - .setDevNo = cam_setActiceCam, - .setbrightness = cam_setbright, - .setexp = cam_setexp, - .setgain = cam_setgain, - .setT = cam_settemp, - .setbin = cam_setbin, - .setnflushes = cam_ifalse, - .shuttercmd = cam_shutter, - .confio = cam_ifalse, - .setio = cam_ifalse, - .setframetype = cam_ifalse, // set DARK or NORMAL: no shutter -> no darks - .setbitdepth = cam_setbitdepth, - .setfastspeed = cam_ifalse, - .setgeometry = cam_setgeometry, - .setfanspeed = cam_setfanspd, - // getters: - .getbrightness = cam_getbright, - .getModelName = cam_modelname, - .getgain = cam_getgain, - .getmaxgain = cam_getmaxgain, - .getgeomlimits = cam_geomlimits, - .getTcold = cam_gettchip, - .getThot = cam_gettbody, - .getTbody = cam_fpfalse, - .getbin = cam_getbin, - .getio = cam_ipfalse, -}; +/* + * This file is part of the CCD_Capture project. + * Copyright 2022 Edward V. Emelianov . + * + * 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 . + */ + +#include +#include +#include +#include +#include + +#include "basestructs.h" +#include "omp.h" + +#ifndef FLT_EPSILON +#define FLT_EPSILON 1.19209290E-07F +#endif + +extern Camera camera; + +static MV_CC_DEVICE_INFO_LIST stDeviceList; +static void *handle = NULL; +static char camname[BUFSIZ] = {0}; +//static long cam_err, tmpl; +static capture_status capStatus = CAPTURE_NO; +static int curhbin = 1, curvbin = 1; +static double starttime = 0.; // time when exposure started +static float exptime = 0.; // exposition time (in seconds) +static MV_FRAME_OUT_INFO_EX stImageInfo = {0}; // last image info +static uint8_t *pdata = NULL; +static int pdatasz = 0; +static int lastecode = MV_OK; + +static struct{ + float maxgain; + float mingain; + float maxbright; + float minbright; + float minexp; + float maxexp; + int maxbin; +} extrvalues = {0}; // extremal values + +static void printErr(){ + const char *errcode = "unknown error"; + switch(lastecode){ + case MV_E_HANDLE: errcode = "Error or invalid handle "; break; + case MV_E_SUPPORT: errcode = "Not supported function "; break; + case MV_E_BUFOVER: errcode = "Cache is full "; break; + case MV_E_CALLORDER: errcode = "Function calling order error "; break; + case MV_E_PARAMETER: errcode = "Incorrect parameter "; break; + case MV_E_RESOURCE: errcode = "Applying resource failed "; break; + case MV_E_NODATA: errcode = "No data "; break; + case MV_E_PRECONDITION: errcode = "Precondition error, or running environment changed "; break; + case MV_E_VERSION: errcode = "Version mismatches "; break; + case MV_E_NOENOUGH_BUF: errcode = "Insufficient memory "; break; + case MV_E_ABNORMAL_IMAGE: errcode = "Abnormal image, maybe incomplete image because of lost packet "; break; + case MV_E_UNKNOW: errcode = "Unknown error "; break; + case MV_E_GC_GENERIC: errcode = "General error "; break; + case MV_E_GC_ACCESS: errcode = "Node accessing condition error "; break; + case MV_E_ACCESS_DENIED: errcode = "No permission "; break; + case MV_E_BUSY: errcode = "Device is busy, or network disconnected "; break; + case MV_E_NETER: errcode = "Network error "; + } + WARNX("CMOS error: %s", errcode); +} + +#define TRY(fn, ...) do{lastecode = MV_CC_ ## fn(handle __VA_OPT__(,) __VA_ARGS__); if(lastecode != MV_OK) printErr(); }while(0) +#define ONERR() if(MV_OK != lastecode) +#define ONOK() if(MV_OK == lastecode) + +static int changeenum(const char *key, uint32_t val){ + if(!handle) return FALSE; + MVCC_ENUMVALUE e; + TRY(GetEnumValue, key, &e); + ONERR(){ + WARNX("Enum '%s' is absent", key); + return FALSE; + } + DBG("Try to change '%s' to %u, cur=%u", key, val, e.nCurValue); + if(e.nCurValue == val) return TRUE; + TRY(SetEnumValue, key, val); + ONERR(){ + WARNX("Cant change %s to %d, supported values are:", key, val); + for(int i = 0; i < (int)e.nSupportedNum; ++i){ + fprintf(stderr, "%s%u", i ? ", " : "", e.nSupportValue[i]); + } + fprintf(stderr, "\n"); + return FALSE; + } + TRY(GetEnumValue, key, &e); + ONERR() return FALSE; + if(e.nCurValue == val) return TRUE; + WARNX("New value of '%s' changed to %d, not to %d", key, e.nCurValue, val); + return FALSE; +} + +static int changeint(const char *key, uint32_t val){ + if(!handle) return FALSE; + MVCC_INTVALUE i; + TRY(GetIntValue, key, &i); + ONERR(){ + WARNX("Int '%s' is absent", key); + return FALSE; + } + if(i.nCurValue == val) return TRUE; + TRY(SetIntValue, key, val); + ONERR(){ + WARNX("Cant change %s to %u; available range is %u..%u", key, val, i.nMin, i.nMax); + return FALSE; + } + TRY(GetIntValue, key, &i); + ONERR() return FALSE; + if(i.nCurValue == val) return TRUE; + WARNX("New value of '%s' changed to %d, not to %d", key, i.nCurValue, val); + return FALSE; +} + +static int changefloat(const char *key, float val){ + if(!handle) return FALSE; + MVCC_FLOATVALUE f; + TRY(GetFloatValue, key, &f); + ONERR(){ + WARNX("Float '%s' is absent", key); + return FALSE; + } + if(f.fCurValue == val) return TRUE; + TRY(SetFloatValue, key, val); + ONERR(){ + WARNX("Cant change %s to %g; available range is %g..%g", key, val, f.fMin, f.fMax); + return FALSE; + } + TRY(GetFloatValue, key, &f); + ONERR() return FALSE; + if(fabs(f.fCurValue - val) < FLT_EPSILON) return TRUE; + WARNX("New value of '%s' changed to %g, not to %g", key, f.fCurValue, val); + return FALSE; +} + +static int cam_setbin(int binh, int binv){ + FNAME(); + if(!handle) return FALSE; + if(!changeenum("BinningHorizontal", binh)) return FALSE; + if(!changeenum("BinningVertical", binv)) return FALSE; + curhbin = binh; + curvbin = binv; + return TRUE; +} + +static int cam_getbin(int *h, int *v){ + MVCC_ENUMVALUE e; + TRY(GetEnumValue, "BinningHorizontal", &e); + ONERR() return FALSE; + curhbin = e.nCurValue; + //printf("Hbin supported = %d", e.nSupportedNum); + //for(int i = 0; i < (int)e.nSupportedNum; ++i) printf("\t%d", e.nSupportValue[i]); + TRY(GetEnumValue, "BinningVertical", &e); + ONERR() return FALSE; + curvbin = e.nCurValue; + //printf("Vbin supported = %d", e.nSupportedNum); + //for(int i = 0; i < (int)e.nSupportedNum; ++i) printf("\t%d", e.nSupportValue[i]); + if(h) *h = curhbin; + if(v) *v = curvbin; + return TRUE; +} + +static int cam_getgain(float *g){ + if(!handle) return FALSE; + MVCC_FLOATVALUE gain; + TRY(GetFloatValue, "Gain", &gain); + ONERR() return FALSE; + if(g) *g = gain.fCurValue; + extrvalues.maxgain = gain.fMax; + extrvalues.mingain = gain.fMin; + DBG("Gain: cur=%g, min=%g, max=%g", gain.fCurValue, gain.fMin, gain.fMax); + return TRUE; +} + +static int cam_getmaxgain(float *g){ + if(!handle) return FALSE; + if(g) *g = extrvalues.maxgain; + return TRUE; +} + +static int cam_setgain(float g){ + if(!handle) return FALSE; + return changefloat("Gain", g); +} + +static int cam_getbright(float *b){ + if(!handle) return FALSE; + DBG("Get brightness"); + MVCC_INTVALUE bright; + TRY(GetIntValue, "Brightness", &bright); + ONERR() return FALSE; + if(b) *b = bright.nCurValue; + extrvalues.maxgain = bright.nMax; + extrvalues.mingain = bright.nMin; + DBG("Brightness: cur=%d, min=%d, max=%d", bright.nCurValue, bright.nMin, bright.nMax); + return TRUE; +} + +static int cam_setbright(float b){ + if(!handle) return FALSE; + return changeint("Brightness", (uint32_t)b); +} + +static void PrintDeviceInfo(MV_CC_DEVICE_INFO* pstMVDevInfo){ + if(!pstMVDevInfo) return; + if(pstMVDevInfo->nTLayerType == MV_GIGE_DEVICE){ + int nIp1 = ((pstMVDevInfo->SpecialInfo.stGigEInfo.nCurrentIp & 0xff000000) >> 24); + int nIp2 = ((pstMVDevInfo->SpecialInfo.stGigEInfo.nCurrentIp & 0x00ff0000) >> 16); + int nIp3 = ((pstMVDevInfo->SpecialInfo.stGigEInfo.nCurrentIp & 0x0000ff00) >> 8); + int nIp4 = (pstMVDevInfo->SpecialInfo.stGigEInfo.nCurrentIp & 0x000000ff); + printf("Device Model Name: %s\n", pstMVDevInfo->SpecialInfo.stGigEInfo.chModelName); + strncpy(camname, (char*)pstMVDevInfo->SpecialInfo.stGigEInfo.chModelName, BUFSIZ-1); + printf("CurrentIp: %d.%d.%d.%d\n" , nIp1, nIp2, nIp3, nIp4); + printf("UserDefinedName: %s\n\n" , pstMVDevInfo->SpecialInfo.stGigEInfo.chUserDefinedName); + }else if (pstMVDevInfo->nTLayerType == MV_USB_DEVICE){ + printf("Device Model Name: %s\n", pstMVDevInfo->SpecialInfo.stUsb3VInfo.chModelName); + printf("UserDefinedName: %s\n\n", pstMVDevInfo->SpecialInfo.stUsb3VInfo.chUserDefinedName); + strncpy(camname, (char*)pstMVDevInfo->SpecialInfo.stUsb3VInfo.chModelName, BUFSIZ-1); + }else { + printf("Not support.\n"); + } +} + +static void cam_closecam(){ + DBG("CAMERA CLOSE"); + if(handle){ + MV_CC_StopGrabbing(handle); + TRY(CloseDevice); + ONERR() WARNX("Can't close opened camera"); + TRY(DestroyHandle); + ONERR() WARNX("Can't destroy camera handle"); + handle = NULL; + } + FREE(pdata); + pdatasz = 0; +} + +static int cam_findCCD(){ + DBG("Try to find HIKROBOT cameras .. "); + memset(&stDeviceList, 0, sizeof(MV_CC_DEVICE_INFO_LIST)); + if(MV_OK != MV_CC_EnumDevices(MV_GIGE_DEVICE | MV_USB_DEVICE, &stDeviceList)){ + WARNX("No HIKROBOT cameras found"); + return FALSE; + } + camera.Ndevices = stDeviceList.nDeviceNum; + if(stDeviceList.nDeviceNum > 0){ + for(uint32_t i = 0; i < stDeviceList.nDeviceNum; ++i){ + DBG("[device %d]:\n", i); + MV_CC_DEVICE_INFO* pDeviceInfo = stDeviceList.pDeviceInfo[i]; + if(!pDeviceInfo) continue; + PrintDeviceInfo(pDeviceInfo); + } + } else{ + WARNX("No HIKROBOT cameras found"); + return FALSE; + } + return TRUE; +} + +static int cam_setActiceCam(int n){ + DBG("SET ACTIVE #%d", n); + if(!camera.Ndevices && !cam_findCCD()) return FALSE; + if(n >= camera.Ndevices){ + return FALSE; + } + cam_closecam(); + lastecode = MV_CC_CreateHandleWithoutLog(&handle, stDeviceList.pDeviceInfo[n]); + ONERR(){ + WARNX("Can't create camera handle"); + printErr(); + return FALSE; + } + TRY(OpenDevice, MV_ACCESS_Exclusive, 0); + ONERR(){ + WARNX("Can't open camera file"); + return FALSE; + } + if(stDeviceList.pDeviceInfo[n]->nTLayerType == MV_GIGE_DEVICE){ + int nPacketSize = MV_CC_GetOptimalPacketSize(handle); + if(nPacketSize > 0){ + if(!changeint("GevSCPSPacketSize", nPacketSize)){ + WARNX("Can't set optimal packet size"); + } + } else{ + WARNX("Can't get optimal packet size"); + } + } + // set software trigger +/* MVCC_ENUMVALUE enumval; + MV_CC_GetEnumValue(handle, "TriggerMode", &enumval); + DBG("TRmode: %d", enumval.nSupportedNum); + for(uint16_t i = 0; i < enumval.nSupportedNum; ++i) fprintf(stderr, "\t%d: %u\n", i, enumval.nSupportValue[i]); + */ + if(!changeenum("TriggerMode", MV_TRIGGER_MODE_OFF)){ + WARNX("Can't turn off triggered mode"); + return FALSE; + } + if(!changeenum("AcquisitionMode", MV_ACQ_MODE_SINGLE)){ + WARNX("Can't set acquisition mode to single"); + return FALSE; + } + if(!changeenum("ExposureMode", MV_EXPOSURE_MODE_TIMED)){ + WARNX("Can't change exposure mode to timed"); + return FALSE; + } + if(!changeenum("ExposureAuto", MV_EXPOSURE_AUTO_MODE_OFF)){ + WARNX("Can't turn off auto exposure mode"); + return FALSE; + } + if(!changeenum("GainAuto", 0)){ + WARNX("Can't turn off auto gain"); + return FALSE; + } + MVCC_ENUMVALUE EnumValue; + TRY(GetEnumValue, "PixelFormat", &EnumValue); + ONOK(){ + DBG("PixelFormat=%x", EnumValue.nCurValue); +#ifdef EBUG + for(int i = 0; i < (int)EnumValue.nSupportedNum; ++i) fprintf(stderr, "\t\t%x\n", EnumValue.nSupportValue[i]); +#endif + } + TRY(GetEnumValue, "PixelSize", &EnumValue); + ONOK(){ + DBG("PixelSize=%d", EnumValue.nCurValue); +#ifdef EBUG + for(int i = 0; i < (int)EnumValue.nSupportedNum; ++i) fprintf(stderr, "\t\t%d\n", EnumValue.nSupportValue[i]); +#endif + } + cam_getgain(NULL); // get extremal gain values + cam_getbright(NULL); // get extremal brightness values + cam_getbin(NULL, NULL); // get current binning + MVCC_FLOATVALUE FloatValue; + // get extremal exptime values + TRY(GetFloatValue, "ExposureTime", &FloatValue); + ONOK(){ + extrvalues.maxexp = FloatValue.fMax / 1e6; + extrvalues.minexp = FloatValue.fMin / 1e6; + exptime = FloatValue.fCurValue / 1e6; + printf("Min exp: %g s, max exp: %g s\n", extrvalues.minexp, extrvalues.maxexp); + } + camera.pixX = camera.pixY = 0.; // unknown + MVCC_INTVALUE IntValue; + camera.array.xoff = camera.array.yoff = 0; + int *values[6] = {&camera.array.w, &camera.array.h, &camera.geometry.w, &camera.geometry.h, &camera.geometry.xoff, &camera.geometry.yoff}; + const char *names[2] = {"WidthMax", "HeightMax"};//, "Width", "Height", "OffsetX", "OffsetY"}; + for(int i = 0; i < 2; ++i){ + TRY(GetIntValue, names[i], &IntValue); + ONERR(){ + WARNX("Can't get %s", names[i]); return FALSE; + } + *values[i] = IntValue.nCurValue; + DBG("%s = %d", names[i], *values[i]); + } + camera.array.h *= curvbin; + camera.array.w *= curhbin; + camera.geometry = camera.array; + camera.field = camera.array; + pdatasz = camera.array.h * camera.array.w * 2; + DBG("\t\t2*w*h = %d", pdatasz); +#ifdef EBUG + MVCC_INTVALUE stParam = {0}; + TRY(GetIntValue, "PayloadSize", &stParam); + ONOK(){DBG("PAYLOAD: %u", stParam.nCurValue);} +#endif + pdata = MALLOC(uint8_t, pdatasz); // allocate max available buffer + return TRUE; +} + +static int cam_geomlimits(frameformat *l, frameformat *s){ + if(l) *l = camera.array; + if(s) *s = (frameformat){.w = 1, .h = 1, .xoff = 1, .yoff = 1}; + return TRUE; +} + +static int cam_startexp(){ + if(!handle || !pdata) return FALSE; + DBG("Start exposition"); + MV_CC_StopGrabbing(handle); + TRY(StartGrabbing); + ONERR() return FALSE; + starttime = dtime(); + capStatus = CAPTURE_PROCESS; + return TRUE; +} + +static int cam_pollcapt(capture_status *st, float *remain){ + if(!handle || !pdata) return FALSE; + DBG("capStatus = %d", capStatus); + if(capStatus == CAPTURE_READY){ + DBG("Capture ends"); + goto retn; + } + if(capStatus == CAPTURE_NO){ // start capture + goto retn; + } + if(capStatus == CAPTURE_PROCESS){ + DBG("PDATASZ=%d", pdatasz); + TRY(GetOneFrameTimeout, pdata, pdatasz, &stImageInfo, 50); + ONOK(){ + DBG("OK, ready"); + if(remain) *remain = 0.f; + if(st) *st = CAPTURE_READY; + capStatus = CAPTURE_NO; + return TRUE; + } + DBG("not ready"); + if(remain){ + float diff = exptime - (dtime() - starttime); + DBG("diff = %g", diff); + if(diff < -5.0){ + capStatus = CAPTURE_NO; + if(st) *st = CAPTURE_ABORTED; + return FALSE; + } + if(diff < 0.f) diff = 0.f; + *remain = diff; + } + }else{ // some error + if(st) *st = CAPTURE_ABORTED; + capStatus = CAPTURE_NO; + return FALSE; + } +retn: + if(st) *st = capStatus; + return TRUE; +} + +static int cam_capt(IMG *ima){ + if(!handle || !pdata) return FALSE; + if(!ima || !ima->data) return FALSE; + ; + int bytes = ima->h*ima->w * 2, stbytes = stImageInfo.nWidth * stImageInfo.nHeight * 2; + if(bytes > pdatasz) bytes = pdatasz; + if(bytes != stbytes) WARNX("Different sizes of image buffer & grabbed image"); + if(stbytes > bytes) bytes = stbytes; + DBG("Copy %d bytes (stbytes=%d)", bytes, stbytes); + MVCC_ENUMVALUE EnumValue; + TRY(GetEnumValue, "PixelSize", &EnumValue); + DBG("PixelSize = %u", EnumValue.nCurValue); + ONOK(){ +//green("pixsize=%d\n", EnumValue.nCurValue); + if(EnumValue.nCurValue == 16){ + memcpy(ima->data, pdata, bytes); + ima->bitpix = 12; + return TRUE; + }else if(EnumValue.nCurValue != 8){ + WARNX("Unsupported pixel size"); + return FALSE; + } + } + // transform 8bits to 16 + DBG("TRANSFORM 8 bit to 16"); + bytes /= 2; + uint8_t *ptr = (uint8_t*) pdata; + OMP_FOR() + for(int i = 0; i < bytes; ++i){ + ima->data[i] = (uint16_t) *ptr++; + } + ima->bitpix = 8; + return TRUE; +} + +static int cam_modelname(char *buf, int bufsz){ + strncpy(buf, camname, bufsz); + return TRUE; +} + +static int cam_setgeometry(frameformat *f){ + FNAME(); + if(!f || !handle) return FALSE; + DBG("getbin"); + if(!cam_getbin(NULL, NULL)) return FALSE; + DBG("set geom %dx%d (off: %dx%d)", f->w, f->h, f->xoff, f->yoff); + if(!changeint("Width", f->w * curhbin)) return FALSE; + if(!changeint("Height", f->h * curvbin)) return FALSE; + if(!changeint("OffsetX", f->xoff * curhbin)) return FALSE; + if(!changeint("OffsetY", f->yoff * curvbin)) return FALSE; + DBG("Success!"); + return TRUE; +} + +static int cam_settemp(float t){ + if(!handle) return FALSE; + if(!changeenum("DeviceTemperatureSelector", 0)) return FALSE; + if(!changeenum("DeviceTemperature", t)) return FALSE; + return TRUE; +} + +static int cam_gettemp(float *t){ + MVCC_FLOATVALUE fl; + TRY(GetFloatValue, "DeviceTemperature", &fl); + ONERR() return FALSE; + if(t) *t = fl.fCurValue; + return TRUE; +} + +static int cam_gettchip(float *t){ + if(!handle) return FALSE; + changeenum("DeviceTemperatureSelector", 0); // there's can be camera without this enume + return cam_gettemp(t); +} + +static int cam_gettbody(_U_ float *t){ + if(!handle) return FALSE; + if(!changeenum("DeviceTemperatureSelector", 1)) return FALSE; + return cam_gettemp(t); +} + +static void cam_cancel(){ + if(!handle) return; + TRY(StopGrabbing); +} + +static int cam_shutter(_U_ shutter_op cmd){ + return FALSE; +} + +/* +static int cam_confio(int io){ + if(!handle) return FALSE; + MVCC_ENUMVALUE e; + TRY(GetEnumValue, "LineSelector", &e); + ONERR() return FALSE; + int bit = 1; + for(int i = 0; i < (int)e.nSupportedNum; ++i, bit <<= 1){ + green("line %d: %d\n", e.nSupportValue[i]); + if(io & bit) printf("bit %d\n", i); + } + return TRUE; +} +*/ + +static int cam_setexp(float t){ // t is in seconds!! + if(!handle) return FALSE; + if(!changefloat("ExposureTime", t*1e6)) return FALSE; + exptime = t; + return TRUE; +} + +static int cam_setbitdepth(int i){ + if(!handle) return FALSE; + int d = i ? PixelType_Gvsp_Mono12 : PixelType_Gvsp_Mono8; + if(!changeenum("PixelFormat", d)){ + WARNX("pixformat"); + return FALSE; + } + d = i ? 16 : 8; + if(!changeenum("PixelSize", d)){ + WARNX("pixsz"); + return FALSE; + } + return TRUE; +} + +static int cam_setfanspd(_U_ fan_speed s){ + return FALSE; +} + +//static int cam_ffalse(_U_ float f){ return FALSE; } +static int cam_fpfalse(_U_ float *f){ return FALSE; } +static int cam_ifalse(_U_ int i){ return FALSE; } +static int cam_ipfalse(_U_ int *i){ return FALSE; } + +/* + * Global objects: camera, focuser and wheel + */ +Camera camera = { + .check = cam_findCCD, + .close = cam_closecam, + .pollcapture = cam_pollcapt, + .capture = cam_capt, + .cancel = cam_cancel, + .startexposition = cam_startexp, + // setters: + .setDevNo = cam_setActiceCam, + .setbrightness = cam_setbright, + .setexp = cam_setexp, + .setgain = cam_setgain, + .setT = cam_settemp, + .setbin = cam_setbin, + .setnflushes = cam_ifalse, + .shuttercmd = cam_shutter, + .confio = cam_ifalse, + .setio = cam_ifalse, + .setframetype = cam_ifalse, // set DARK or NORMAL: no shutter -> no darks + .setbitdepth = cam_setbitdepth, + .setfastspeed = cam_ifalse, + .setgeometry = cam_setgeometry, + .setfanspeed = cam_setfanspd, + // getters: + .getbrightness = cam_getbright, + .getModelName = cam_modelname, + .getgain = cam_getgain, + .getmaxgain = cam_getmaxgain, + .getgeomlimits = cam_geomlimits, + .getTcold = cam_gettchip, + .getThot = cam_gettbody, + .getTbody = cam_fpfalse, + .getbin = cam_getbin, + .getio = cam_ipfalse, +}; diff --git a/ZWO_cameras/zwofunc.c b/ZWO_cameras/zwofunc.c index 921b72d..a4916df 100644 --- a/ZWO_cameras/zwofunc.c +++ b/ZWO_cameras/zwofunc.c @@ -119,6 +119,7 @@ static int camcapt(IMG *ima){ WARNX("Couldn't read exposure data\n"); return FALSE; } + ima->bitpix = 16; return TRUE; } diff --git a/basestructs.h b/basestructs.h index f75f632..ea8b633 100644 --- a/basestructs.h +++ b/basestructs.h @@ -24,6 +24,7 @@ typedef struct{ uint16_t *data; // image data + uint8_t bitpix; // bits per pixel (8 or 16) int w, h; // image size uint16_t max, min; // min/max values float avr, std; // statistics diff --git a/ccdfunc.c b/ccdfunc.c index b7b8cc0..84d4e45 100644 --- a/ccdfunc.c +++ b/ccdfunc.c @@ -248,8 +248,7 @@ int saveFITS(IMG *img, char **outp){ // DATAMAX, DATAMIN / Max, min pixel value tmpi = 0; WRITEKEY(fp, TINT, "DATAMIN", &tmpi, "Min pixel value"); - //itmp = GP->fast ? 255 : 65535; - tmpi = 65535; + tmpi = (1<bitpix) - 1; WRITEKEY(fp, TINT, "DATAMAX", &tmpi, "Max pixel value"); tmpi = img->min; WRITEKEY(fp, TUSHORT, "STATMIN", &tmpi, "Min data value"); diff --git a/locale/ru/messages.po b/locale/ru/messages.po index a6c348b..f97493d 100644 --- a/locale/ru/messages.po +++ b/locale/ru/messages.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2022-12-22 11:40+0300\n" +"POT-Creation-Date: 2023-02-03 09:26+0300\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -279,273 +279,278 @@ msgstr "" msgid "Can't save file with prefix %s" msgstr "" -#: ccdfunc.c:357 +#: ccdfunc.c:356 #, c-format msgid "File saved as '%s'" msgstr "" -#: ccdfunc.c:366 +#: ccdfunc.c:365 msgid "Error saving file" msgstr "" -#: ccdfunc.c:406 +#: ccdfunc.c:405 #, c-format msgid "Image stat:\n" msgstr "" -#: ccdfunc.c:414 +#: ccdfunc.c:413 msgid "Focuser device not pointed" msgstr "" -#: ccdfunc.c:421 +#: ccdfunc.c:420 msgid "No focusers found" msgstr "" -#: ccdfunc.c:453 +#: ccdfunc.c:452 #, c-format msgid "Found %d focusers, you point number %d" msgstr "" -#: ccdfunc.c:457 +#: ccdfunc.c:456 msgid "Can't set active focuser number" msgstr "" -#: ccdfunc.c:471 +#: ccdfunc.c:470 msgid "Can't get focuser limit positions" msgstr "" -#: ccdfunc.c:478 +#: ccdfunc.c:477 msgid "Can't get current focuser position" msgstr "" -#: ccdfunc.c:492 +#: ccdfunc.c:491 #, c-format msgid "Can't set position %g: out of limits [%g, %g]" msgstr "" -#: ccdfunc.c:496 +#: ccdfunc.c:495 msgid "Can't home focuser" msgstr "" -#: ccdfunc.c:498 +#: ccdfunc.c:497 #, c-format msgid "Can't set position %g" msgstr "" -#: ccdfunc.c:506 +#: ccdfunc.c:505 msgid "Wheel device not pointed" msgstr "" -#: ccdfunc.c:513 +#: ccdfunc.c:512 msgid "No wheels found" msgstr "" -#: ccdfunc.c:545 +#: ccdfunc.c:544 #, c-format msgid "Found %d wheels, you point number %d" msgstr "" -#: ccdfunc.c:549 +#: ccdfunc.c:548 msgid "Can't set active wheel number" msgstr "" -#: ccdfunc.c:565 +#: ccdfunc.c:564 msgid "Can't get max wheel position" msgstr "" -#: ccdfunc.c:572 +#: ccdfunc.c:571 #, c-format msgid "Wheel position should be from 0 to %d" msgstr "" -#: ccdfunc.c:576 +#: ccdfunc.c:575 #, c-format msgid "Can't set wheel position %d" msgstr "" -#: ccdfunc.c:593 +#: ccdfunc.c:592 #, c-format msgid "%.1f seconds till exposition ends" msgstr "" -#: ccdfunc.c:607 +#: ccdfunc.c:606 msgid "Camera device not pointed" msgstr "" -#: ccdfunc.c:614 ccdfunc.c:615 +#: ccdfunc.c:613 ccdfunc.c:614 msgid "No cameras found" msgstr "" -#: ccdfunc.c:650 +#: ccdfunc.c:649 #, c-format msgid "Found %d cameras, you point number %d" msgstr "" -#: ccdfunc.c:654 +#: ccdfunc.c:653 msgid "Can't set active camera number" msgstr "" -#: ccdfunc.c:660 +#: ccdfunc.c:659 msgid "Can't set fan speed" msgstr "" -#: ccdfunc.c:661 +#: ccdfunc.c:660 #, c-format msgid "Set fan speed to %d" msgstr "" -#: ccdfunc.c:666 +#: ccdfunc.c:665 #, c-format msgid "Camera model: %s" msgstr "" -#: ccdfunc.c:667 +#: ccdfunc.c:666 #, c-format msgid "Pixel size: %g x %g" msgstr "" -#: ccdfunc.c:673 +#: ccdfunc.c:672 #, c-format msgid "Full array: %s" msgstr "" -#: ccdfunc.c:676 +#: ccdfunc.c:675 #, c-format msgid "Field of view: %s" msgstr "" -#: ccdfunc.c:679 +#: ccdfunc.c:678 +#, c-format +msgid "Current format: %s" +msgstr "" + +#: ccdfunc.c:681 #, c-format msgid "Can't set T to %g degC" msgstr "" -#: ccdfunc.c:686 +#: ccdfunc.c:688 #, c-format msgid "Shutter command: %s\n" msgstr "" -#: ccdfunc.c:688 +#: ccdfunc.c:690 #, c-format msgid "Can't run shutter command %s (unsupported?)" msgstr "" #. "Попытка сконфигурировать порт I/O как %d\n" -#: ccdfunc.c:692 +#: ccdfunc.c:694 #, c-format msgid "Try to configure I/O port as %d" msgstr "" -#: ccdfunc.c:694 +#: ccdfunc.c:696 msgid "Can't configure (unsupported?)" msgstr "" -#: ccdfunc.c:700 +#: ccdfunc.c:702 msgid "Can't get IOport state (unsupported?)" msgstr "" #. "Попытка записи %d в порт I/O\n" -#: ccdfunc.c:704 +#: ccdfunc.c:706 #, c-format msgid "Try to write %d to I/O port" msgstr "" -#: ccdfunc.c:706 +#: ccdfunc.c:708 msgid "Can't set IOport" msgstr "" -#: ccdfunc.c:713 +#: ccdfunc.c:715 #, c-format msgid "Set gain to %g" msgstr "" -#: ccdfunc.c:714 +#: ccdfunc.c:716 #, c-format msgid "Can't set gain to %g" msgstr "" -#: ccdfunc.c:719 +#: ccdfunc.c:721 #, c-format msgid "Set brightness to %g" msgstr "" -#: ccdfunc.c:720 +#: ccdfunc.c:722 #, c-format msgid "Can't set brightness to %g" msgstr "" -#: ccdfunc.c:728 server.c:223 +#: ccdfunc.c:730 server.c:223 #, c-format msgid "Can't set binning %dx%d" msgstr "" -#: ccdfunc.c:738 server.c:224 +#: ccdfunc.c:742 server.c:224 msgid "Can't set given geometry" msgstr "" -#: ccdfunc.c:742 -#, c-format -msgid "Can't set %d flushes" -msgstr "" - #: ccdfunc.c:746 #, c-format +msgid "Can't set %d flushes" +msgstr "" + +#: ccdfunc.c:750 +#, c-format msgid "Can't set exposure time to %f seconds" msgstr "" -#: ccdfunc.c:749 +#: ccdfunc.c:753 msgid "Can't change frame type" msgstr "" -#: ccdfunc.c:752 +#: ccdfunc.c:756 msgid "Can't set bit depth" msgstr "" -#: ccdfunc.c:754 +#: ccdfunc.c:758 msgid "Can't set readout speed" msgstr "" -#: ccdfunc.c:755 +#: ccdfunc.c:759 #, c-format msgid "Readout mode: %s" msgstr "" -#: ccdfunc.c:756 +#: ccdfunc.c:760 msgid "Only show statistics" msgstr "" #. GET binning should be AFTER setgeometry! -#: ccdfunc.c:758 +#: ccdfunc.c:762 msgid "Can't get current binning" msgstr "" -#: ccdfunc.c:772 +#: ccdfunc.c:776 msgid "Can't open OpenGL window, image preview will be inaccessible" msgstr "" #. Захват кадра %d\n -#: ccdfunc.c:780 +#: ccdfunc.c:784 #, c-format msgid "Capture frame %d" msgstr "" -#: ccdfunc.c:782 server.c:121 +#: ccdfunc.c:786 server.c:121 msgid "Can't start exposition" msgstr "" -#: ccdfunc.c:786 ccdfunc.c:808 ccdfunc.c:849 +#: ccdfunc.c:790 ccdfunc.c:816 ccdfunc.c:857 msgid "Can't capture image" msgstr "" -#: ccdfunc.c:789 +#: ccdfunc.c:793 msgid "Read grabbed image" msgstr "" -#: ccdfunc.c:792 ccdfunc.c:812 ccdfunc.c:853 +#: ccdfunc.c:796 ccdfunc.c:820 ccdfunc.c:861 msgid "Can't grab image" msgstr "" #. %d секунд до окончания паузы\n -#: ccdfunc.c:829 client.c:260 +#: ccdfunc.c:837 client.c:260 #, c-format msgid "%d seconds till pause ends\n" msgstr "" diff --git a/locale/ru/ru.po b/locale/ru/ru.po index b1f2dd0..ed7df68 100644 --- a/locale/ru/ru.po +++ b/locale/ru/ru.po @@ -7,7 +7,7 @@ msgid "" msgstr "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" - "POT-Creation-Date: 2022-12-20 15:47+0300\n" + "POT-Creation-Date: 2023-02-03 09:26+0300\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -16,13 +16,13 @@ msgstr "Project-Id-Version: PACKAGE VERSION\n" "Content-Type: text/plain; charset=koi8-r\n" "Content-Transfer-Encoding: 8bit\n" -#: ccdfunc.c:593 +#: ccdfunc.c:592 #, c-format msgid "%.1f seconds till exposition ends" msgstr "%.1f " #. %d секунд до окончания паузы\n -#: ccdfunc.c:829 client.c:260 +#: ccdfunc.c:837 client.c:260 #, c-format msgid "%d seconds till pause ends\n" msgstr "%d \n" @@ -39,7 +39,7 @@ msgstr " msgid "CMOS gain level" msgstr " Gain CMOS" -#: ccdfunc.c:607 +#: ccdfunc.c:606 msgid "Camera device not pointed" msgstr " " @@ -47,20 +47,20 @@ msgstr " msgid "Camera device unknown" msgstr " " -#: ccdfunc.c:666 +#: ccdfunc.c:665 #, c-format msgid "Camera model: %s" msgstr " : %s" -#: ccdfunc.c:786 ccdfunc.c:808 ccdfunc.c:849 +#: ccdfunc.c:790 ccdfunc.c:816 ccdfunc.c:857 msgid "Can't capture image" msgstr " " -#: ccdfunc.c:749 +#: ccdfunc.c:753 msgid "Can't change frame type" msgstr " " -#: ccdfunc.c:694 +#: ccdfunc.c:696 msgid "Can't configure (unsupported?)" msgstr " ( ?)" @@ -84,32 +84,32 @@ msgstr " msgid "Can't find wheel in plugin %s: %s" msgstr " %s: %s" -#: ccdfunc.c:700 +#: ccdfunc.c:702 msgid "Can't get IOport state (unsupported?)" msgstr " I/O ( ?)" #. GET binning should be AFTER setgeometry! -#: ccdfunc.c:758 +#: ccdfunc.c:762 msgid "Can't get current binning" msgstr " " -#: ccdfunc.c:478 +#: ccdfunc.c:477 msgid "Can't get current focuser position" msgstr " " -#: ccdfunc.c:471 +#: ccdfunc.c:470 msgid "Can't get focuser limit positions" msgstr " " -#: ccdfunc.c:565 +#: ccdfunc.c:564 msgid "Can't get max wheel position" msgstr " " -#: ccdfunc.c:792 ccdfunc.c:812 ccdfunc.c:853 +#: ccdfunc.c:796 ccdfunc.c:820 ccdfunc.c:861 msgid "Can't grab image" msgstr " " -#: ccdfunc.c:496 +#: ccdfunc.c:495 msgid "Can't home focuser" msgstr " " @@ -121,11 +121,11 @@ msgstr " msgid "Can't make exposition" msgstr " " -#: ccdfunc.c:772 +#: ccdfunc.c:776 msgid "Can't open OpenGL window, image preview will be inaccessible" msgstr " OpenGL, " -#: ccdfunc.c:688 +#: ccdfunc.c:690 #, c-format msgid "Can't run shutter command %s (unsupported?)" msgstr " %s ( ?)" @@ -136,93 +136,98 @@ msgstr " msgid "Can't save file with prefix %s" msgstr " %s" -#: ccdfunc.c:742 +#: ccdfunc.c:746 #, c-format msgid "Can't set %d flushes" msgstr " %d " -#: ccdfunc.c:706 +#: ccdfunc.c:708 msgid "Can't set IOport" msgstr " I/O" -#: ccdfunc.c:679 +#: ccdfunc.c:681 #, c-format msgid "Can't set T to %g degC" msgstr " %g " -#: ccdfunc.c:654 +#: ccdfunc.c:653 msgid "Can't set active camera number" msgstr " " -#: ccdfunc.c:457 +#: ccdfunc.c:456 msgid "Can't set active focuser number" msgstr " " -#: ccdfunc.c:549 +#: ccdfunc.c:548 msgid "Can't set active wheel number" msgstr " " -#: ccdfunc.c:728 server.c:223 +#: ccdfunc.c:730 server.c:223 #, c-format msgid "Can't set binning %dx%d" msgstr " %dx%d" -#: ccdfunc.c:752 +#: ccdfunc.c:756 msgid "Can't set bit depth" msgstr " " -#: ccdfunc.c:720 +#: ccdfunc.c:722 #, c-format msgid "Can't set brightness to %g" msgstr " %g" -#: ccdfunc.c:746 +#: ccdfunc.c:750 #, c-format msgid "Can't set exposure time to %f seconds" msgstr " %f " -#: ccdfunc.c:660 +#: ccdfunc.c:659 msgid "Can't set fan speed" msgstr " " -#: ccdfunc.c:714 +#: ccdfunc.c:716 #, c-format msgid "Can't set gain to %g" msgstr " Gain %g" -#: ccdfunc.c:738 server.c:224 +#: ccdfunc.c:742 server.c:224 msgid "Can't set given geometry" msgstr " " -#: ccdfunc.c:498 +#: ccdfunc.c:497 #, c-format msgid "Can't set position %g" msgstr " %g" -#: ccdfunc.c:492 +#: ccdfunc.c:491 #, c-format msgid "Can't set position %g: out of limits [%g, %g]" msgstr " %g: [%g, %g]" -#: ccdfunc.c:754 +#: ccdfunc.c:758 msgid "Can't set readout speed" msgstr " " -#: ccdfunc.c:576 +#: ccdfunc.c:575 #, c-format msgid "Can't set wheel position %d" msgstr " %d" -#: ccdfunc.c:782 server.c:121 +#: ccdfunc.c:786 server.c:121 msgid "Can't start exposition" msgstr " " #. Захват кадра %d\n -#: ccdfunc.c:780 +#: ccdfunc.c:784 #, c-format msgid "Capture frame %d" msgstr " %d" +#: ccdfunc.c:678 +#, c-format +msgid "Current format: %s" +msgstr "" + #: cmdlnopts.c:108 msgid "Display image in OpenGL window" msgstr " OpenGL" @@ -232,40 +237,40 @@ msgstr " msgid "Equalization of histogram: %s" msgstr " : %s" -#: ccdfunc.c:366 +#: ccdfunc.c:365 msgid "Error saving file" msgstr " " -#: ccdfunc.c:676 +#: ccdfunc.c:675 #, c-format msgid "Field of view: %s" msgstr " : %s" -#: ccdfunc.c:357 +#: ccdfunc.c:356 #, c-format msgid "File saved as '%s'" msgstr " '%s'" -#: ccdfunc.c:414 +#: ccdfunc.c:413 msgid "Focuser device not pointed" msgstr " " -#: ccdfunc.c:650 +#: ccdfunc.c:649 #, c-format msgid "Found %d cameras, you point number %d" msgstr " %d , %d" -#: ccdfunc.c:453 +#: ccdfunc.c:452 #, c-format msgid "Found %d focusers, you point number %d" msgstr " %d , %d" -#: ccdfunc.c:545 +#: ccdfunc.c:544 #, c-format msgid "Found %d wheels, you point number %d" msgstr " %d , %d" -#: ccdfunc.c:673 +#: ccdfunc.c:672 #, c-format msgid "Full array: %s" msgstr " : %s" @@ -275,7 +280,7 @@ msgstr " msgid "Histogram conversion: %s" msgstr " : %s" -#: ccdfunc.c:406 +#: ccdfunc.c:405 #, c-format msgid "Image stat:\n" msgstr " : \n" @@ -292,19 +297,19 @@ msgstr " msgid "No camera device" msgstr " " -#: ccdfunc.c:614 ccdfunc.c:615 +#: ccdfunc.c:613 ccdfunc.c:614 msgid "No cameras found" msgstr " " -#: ccdfunc.c:421 +#: ccdfunc.c:420 msgid "No focusers found" msgstr " " -#: ccdfunc.c:513 +#: ccdfunc.c:512 msgid "No wheels found" msgstr " " -#: ccdfunc.c:756 +#: ccdfunc.c:760 msgid "Only show statistics" msgstr " " @@ -312,16 +317,16 @@ msgstr " msgid "PID file (default: " msgstr "PID- ( : " -#: ccdfunc.c:667 +#: ccdfunc.c:666 #, c-format msgid "Pixel size: %g x %g" msgstr " : %g x %g" -#: ccdfunc.c:789 +#: ccdfunc.c:793 msgid "Read grabbed image" msgstr " " -#: ccdfunc.c:755 +#: ccdfunc.c:759 #, c-format msgid "Readout mode: %s" msgstr " : %s" @@ -330,34 +335,34 @@ msgstr " msgid "Server timeout" msgstr " " -#: ccdfunc.c:719 +#: ccdfunc.c:721 #, c-format msgid "Set brightness to %g" msgstr " %g" -#: ccdfunc.c:661 +#: ccdfunc.c:660 #, c-format msgid "Set fan speed to %d" msgstr " %d" -#: ccdfunc.c:713 +#: ccdfunc.c:715 #, c-format msgid "Set gain to %g" msgstr " Gain %g" -#: ccdfunc.c:686 +#: ccdfunc.c:688 #, c-format msgid "Shutter command: %s\n" msgstr " : %s\n" #. "Попытка сконфигурировать порт I/O как %d\n" -#: ccdfunc.c:692 +#: ccdfunc.c:694 #, c-format msgid "Try to configure I/O port as %d" msgstr " I/O %d" #. "Попытка записи %d в порт I/O\n" -#: ccdfunc.c:704 +#: ccdfunc.c:706 #, c-format msgid "Try to write %d to I/O port" msgstr " %d I/O" @@ -366,11 +371,11 @@ msgstr " msgid "UNIX socket name" msgstr " UNIX-" -#: ccdfunc.c:506 +#: ccdfunc.c:505 msgid "Wheel device not pointed" msgstr " " -#: ccdfunc.c:572 +#: ccdfunc.c:571 #, c-format msgid "Wheel position should be from 0 to %d" msgstr " 0 %d"