diff --git a/.gitignore b/.gitignore index c6127b3..e961961 100644 --- a/.gitignore +++ b/.gitignore @@ -1,52 +1,28 @@ +# Temporary files +*~ + # Prerequisites *.d # Object files *.o -*.ko -*.obj -*.elf # Linker output *.ilk *.map *.exp -# Precompiled Headers -*.gch -*.pch - # Libraries *.lib *.a *.la *.lo -# Shared objects (inc. Windows DLLs) -*.dll -*.so -*.so.* -*.dylib - -# Executables -*.exe -*.out -*.app -*.i*86 -*.x86_64 -*.hex - -# Debug files -*.dSYM/ -*.su -*.idb -*.pdb - -# Kernel Module Compile Results -*.mod* -*.cmd -.tmp_versions/ -modules.order -Module.symvers -Mkfile.old -dkms.conf +# QT-creator files +*.cflags +*.config +*.creator +*.user +*.cxxflags +*.files +*.includes diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..37d0627 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,129 @@ +cmake_minimum_required(VERSION 3.20) +set(PROJ ccd_capture) +project(${PROJ}) +set(MINOR_VERSION "0") +set(MID_VERSION "0") +set(MAJOR_VERSION "1") +set(VERSION "${MAJOR_VERSION}.${MID_VERSION}.${MINOR_VERSION}") + +message("VER: ${VERSION}") + +# default flags +set(CFLAGS ${CFLAGS} -Wall -W -Wextra -O3 -std=gnu99) +add_definitions(-D_XOPEN_SOURCE=1234 -D_DEFAULT_SOURCE -D_GNU_SOURCE) + +set(CMAKE_COLOR_MAKEFILE ON) + +set(SOURCES main.c cmdlnopts.c ccdfunc.c) + +# cmake -DDEBUG=yes -> debugging +if(DEFINED DEBUG AND DEBUG STREQUAL "yes") + set(CFLAGS ${CFLAGS} -Werror) + add_definitions(-DEBUG) + set(CMAKE_VERBOSE_MAKEFILE "ON") +endif() + +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}") + +find_package(CFITSIO REQUIRED) +find_package(PkgConfig REQUIRED) +pkg_check_modules(${PROJ} REQUIRED usefull_macros) + +if(DEFINED IMAGEVIEW AND IMAGEVIEW STREQUAL "yes") + list(APPEND SOURCES events.c imageview.c) + find_package(OpenGL REQUIRED) + find_package(GLUT REQUIRED) + find_package(X11 REQUIRED) + find_package(Threads REQUIRED) + list(APPEND ${PROJ}_LIBRARIES ${CMAKE_THREAD_LIBS_INIT}) + add_definitions(-DIMAGEVIEW) +endif() + +# additional modules with CCD/CMOS support +if(DEFINED ZWO AND ZWO STREQUAL "yes") + add_subdirectory(ZWO_cameras) + list(APPEND ${PROJ}_INCLUDE_DIRS ZWO_cameras) + add_definitions(-DUSEZWO) + list(APPEND ${PROJ}_LIBRARIES ${ZWOLIB}) + include_directories(ZWO_cameras) +endif() + +# additional modules with CCD/CMOS support +if(DEFINED FLI AND FLI STREQUAL "yes") + add_subdirectory(FLI_cameras) + list(APPEND ${PROJ}_INCLUDE_DIRS FLI_cameras) + add_definitions(-DUSEFLI) + list(APPEND ${PROJ}_LIBRARIES ${FLILIB}) + include_directories(FLI_cameras) +endif() + + +# change wrong behaviour with install prefix +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT AND CMAKE_INSTALL_PREFIX MATCHES "/usr/local") + message("Change default install path to /usr") + set(CMAKE_INSTALL_PREFIX "/usr") +endif() +message("Install dir prefix: ${CMAKE_INSTALL_PREFIX}") + +# directory should contain dir locale/ru for gettext translations +set(LCPATH ${CMAKE_SOURCE_DIR}/locale/ru) + +if(NOT DEFINED LOCALEDIR) + if(DEFINED DEBUG AND DEBUG STREQUAL "yes") + set(LOCALEDIR ${CMAKE_CURRENT_SOURCE_DIR}/locale) + else() + set(LOCALEDIR ${CMAKE_INSTALL_PREFIX}/share/locale) + endif() +endif() + +# gettext files +set(PO_FILE ${LCPATH}/messages.po) +set(MO_FILE ${LCPATH}/LC_MESSAGES/${PROJ}.mo) +set(RU_FILE ${LCPATH}/ru.po) + +# exe file +add_executable(${PROJ} ${SOURCES} ${PO_FILE} ${MO_FILE}) +target_link_libraries(${PROJ} ${CFITSIO_LIBRARIES} ${X11_LIBRARIES} ${OPENGL_LIBRARIES} ${GLUT_LIBRARIES} ${${PROJ}_LIBRARIES} -lm) +include_directories(${${PROJ}_INCLUDE_DIRS} .) +link_directories(${${PROJ}_LIBRARY_DIRS} ) +add_definitions(${CFLAGS} -DLOCALEDIR=\"${LOCALEDIR}\" + -DPACKAGE_VERSION=\"${VERSION}\" -DGETTEXT_PACKAGE=\"${PROJ}\" + -DMINOR_VERSION=\"${MINOR_VERSION}\" -DMID_VERSION=\"${MID_VERSION}\" + -DMAJOR_VERSION=\"${MAJOR_VESION}\") + +# Installation of the program +INSTALL(FILES ${MO_FILE} DESTINATION "share/locale/ru/LC_MESSAGES") +INSTALL(TARGETS ${PROJ} DESTINATION "bin") + +find_package(Gettext REQUIRED) +find_program(GETTEXT_XGETTEXT_EXECUTABLE xgettext) +if(NOT GETTEXT_XGETTEXT_EXECUTABLE OR NOT GETTEXT_MSGFMT_EXECUTABLE) + message(FATAL_ERROR "xgettext not found") +endif() +file(MAKE_DIRECTORY ${LCPATH}) +file(MAKE_DIRECTORY ${LCPATH}/LC_MESSAGES) + +add_custom_command( + OUTPUT ${PO_FILE} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + COMMAND ${GETTEXT_XGETTEXT_EXECUTABLE} --from-code=koi8-r ${SOURCES} -c -k_ -kN_ -o ${PO_FILE} + COMMAND sed -i 's/charset=.*\\\\n/charset=koi8-r\\\\n/' ${PO_FILE} +# COMMAND enconv ${PO_FILE} + DEPENDS ${SOURCES} +) +# we need this to prewent ru.po & .mo from deleting by make clean +add_custom_command( + OUTPUT ${MO_FILE} + COMMAND ${GETTEXT_MSGFMT_EXECUTABLE} ${RU_FILE} -o ${MO_FILE} + DEPENDS ${RU_FILE} ru_file_updated +) + +add_custom_command( + OUTPUT ru_file_updated + COMMAND [ -f ${RU_FILE} ] && ${GETTEXT_MSGMERGE_EXECUTABLE} -Uis ${RU_FILE} ${PO_FILE} || cp ${PO_FILE} ${RU_FILE} + COMMAND ${CMAKE_COMMAND} -E touch ru_file_updated + BYPRODUCTS ${RU_FILE} + DEPENDS ${PO_FILE} +) + +add_custom_target(MO_FILE ALL DEPENDS ${MO_FILE}) diff --git a/FLI_cameras/CMakeLists.txt b/FLI_cameras/CMakeLists.txt new file mode 100644 index 0000000..25e9af8 --- /dev/null +++ b/FLI_cameras/CMakeLists.txt @@ -0,0 +1,24 @@ +cmake_minimum_required(VERSION 3.20) +set(CCDLIB fli_module) +set(FLILIB ${CCDLIB} PARENT_SCOPE) + +find_package(PkgConfig REQUIRED) +pkg_check_modules(${CCDLIB} REQUIRED fli>=1.71 usefull_macros) + +set(CFLAGS -O3 -Wextra -Wall -W -std=gnu99) +set(CMAKE_COLOR_MAKEFILE ON) + +if(DEFINED DEBUG AND DEBUG STREQUAL "yes") + set(CFLAGS ${CFLAGS} -Werror) + message ("DBG = ${DEBUG}, CFLAGS=${CFLAGS}") + add_definitions(-DEBUG) + set(CMAKE_VERBOSE_MAKEFILE "ON") +endif() + +add_definitions(${CFLAGS}) + +aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR} SRC) +add_library(${CCDLIB} ${SRC}) +target_link_libraries(${CCDLIB} ${${CCDLIB}_LIBRARIES}) +include_directories(${${CCDLIB}_INCLUDE_DIRS} ..) +link_directories(${${CCDLIB}_LIBRARY_DIRS}) diff --git a/FLI_cameras/flifunc.c b/FLI_cameras/flifunc.c new file mode 100644 index 0000000..c375109 --- /dev/null +++ b/FLI_cameras/flifunc.c @@ -0,0 +1,712 @@ +/* + * This file is part of the FLI_control project. + * Copyright 2020 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 +#include +#include +#include +#include +#include +#include +#include + +#include "flifunc.h" + +#define LIBVERSIZ 1024 + +#ifndef FLIUSB_VENDORID +#define FLIUSB_VENDORID 0xf18 +#endif +#ifndef FLIUSB_PROLINE_ID +#define FLIUSB_PROLINE_ID 0x0a +#endif +#ifndef FLIUSB_FILTER_ID +#define FLIUSB_FILTER_ID 0x07 +#endif +#ifndef FLIUSB_FOCUSER_ID +#define FLIUSB_FOCUSER_ID 0x06 +#endif + +// wheel position in steps = WHEEL_POS0STPS + WHEEL_STEPPOS*N +#define WHEEL_POS0STPS (239) +#define WHEEL_STEPPOS (48) +// 1mm == FOCSCALE steps of focuser +#define FOCSCALE (10000.) + +#define TRYFUNC(f, ...) \ +do{ if((fli_err = f(__VA_ARGS__))) \ + WARNX(#f "() failed"); \ +}while(0) + +#define TRYFITS(f, ...) \ +do{ int status = 0; \ + f(__VA_ARGS__, &status); \ + if (status){ \ + fits_report_error(stderr, status); \ + return -1;} \ +}while(0) +#define WRITEKEY(...) \ +do{ int status = 0; \ + fits_write_key(__VA_ARGS__, &status); \ + if(status) fits_report_error(stderr, status);\ +}while(0) + +typedef struct{ + flidomain_t domain; + char *dname; + char *name; +}cam_t; + +static char camname[BUFSIZ] = {0}, whlname[BUFSIZ], focname[BUFSIZ]; +static long fli_err, tmpl; +static cam_t *camz = NULL, *whlz = NULL, *focz = NULL; +static flidev_t camdev, whldev, focdev; +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 fli_init(){ + char libver[LIBVERSIZ]; // FLI library version + TRYFUNC(FLISetDebugLevel, NULL /* "NO HOST" */, FLIDEBUG_NONE); + if(fli_err) return FALSE; + TRYFUNC(FLIGetLibVersion, libver, LIBVERSIZ); + if(!fli_err) DBG("Library version '%s'", libver); + return TRUE; +} + +static int findcams(flidomain_t domain, cam_t **cam){ + char **tmplist; + int numcams = 0; + TRYFUNC(FLIList, domain, &tmplist); + if(tmplist && tmplist[0]){ + int i, cams = 0; + for(i = 0; tmplist[i]; i++) cams++; + if((*cam = realloc(*cam, (numcams + cams) * sizeof(cam_t))) == NULL) + ERR("realloc() failed"); + for(i = 0; tmplist[i]; i++){ + int j; + cam_t *tmpcam = *cam + i; + for (j = 0; tmplist[i][j] != '\0'; j++) + if (tmplist[i][j] == ';'){ + tmplist[i][j] = '\0'; + break; + } + tmpcam->domain = domain; + tmpcam->name = strdup(tmplist[i]); + switch (domain & 0xff){ + case FLIDOMAIN_PARALLEL_PORT: + tmpcam->dname = "parallel port"; + break; + case FLIDOMAIN_USB: + tmpcam->dname = "USB"; + break; + case FLIDOMAIN_SERIAL: + tmpcam->dname = "serial"; + break; + case FLIDOMAIN_INET: + tmpcam->dname = "inet"; + break; + default: + tmpcam->dname = "Unknown domain"; + break; + } + DBG("found: %s @ %s", tmpcam->name, tmpcam->dname); + } + numcams += cams; + } + TRYFUNC(FLIFreeList, tmplist); + return numcams; +} + +static int fli_findCCD(){ + DBG("Try to find FLI cameras .. "); + if(!fli_init()){ + DBG("FLI not found"); + return FALSE; + } + if(!camz){ // build cameras list + FLIcam.Ndevices = findcams(FLIDOMAIN_USB | FLIDEVICE_CAMERA, &camz); + if(!FLIcam.Ndevices){ + DBG("No cameras"); + return FALSE; + } + for(int i = 0; i < FLIcam.Ndevices; i++){ + DBG("Camera '%s', domain %s", camz[i].name, camz[i].dname); + } + } + return TRUE; +} +static int fli_setActiceCam(int n){ + if(!camz && !fli_findCCD()) return FALSE; + if(n >= FLIcam.Ndevices){ + return FALSE; + } + FLIClose(camdev); + TRYFUNC(FLIOpen, &camdev, camz[n].name, camz[n].domain); + if(fli_err){ + return FALSE; + } + TRYFUNC(FLIGetModel, camdev, camname, BUFSIZ); +#ifdef EBUG + if(!fli_err) DBG("Model: %s", camname); + TRYFUNC(FLIGetHWRevision, camdev, &tmpl); + if(!fli_err) DBG("HW revision: %ld", tmpl); + TRYFUNC(FLIGetFWRevision, camdev, &tmpl); + if(!fli_err) DBG("SW revision: %ld", tmpl); +#endif + double x,y; + TRYFUNC(FLIGetPixelSize, camdev, &x, &y); + if(!fli_err){ + DBG("Pixel size: %g x %g", x,y); + FLIcam.pixX = (float)x; + FLIcam.pixY = (float)y; + } + long x0, x1, y0, y1; + TRYFUNC(FLIGetVisibleArea, camdev, &x0, &y0, &x1, &y1); + if(!fli_err){ + DBG("Field of view: (%ld, %ld)(%ld, %ld)", x0, y0, x1, y1); + FLIcam.field = (frameformat){.w = x1 - x0, .h = y1 - y0, .xoff = x0, .yoff = y0}; + } + TRYFUNC(FLIGetArrayArea, camdev, &x0, &y0, &x1, &y1); + if(!fli_err){ + DBG("Array field: (%ld, %ld)(%ld, %ld)", x0, y0, x1, y1); + FLIcam.array = (frameformat){.w = x1 - x0, .h = y1 - y0, .xoff = x0, .yoff = y0}; + } + return TRUE; +} + +static int fli_geomlimits(frameformat *l, frameformat *s){ + if(l) *l = FLIcam.array; + if(s) *s = (frameformat){.w = 1, .h = 1, .xoff = 1, .yoff = 1}; + return TRUE; +} + +static int fli_findFocuser(){ + DBG("Try to find FLI focusers .. "); + if(!fli_init()){ + DBG("FLI not found"); + return FALSE; + } + if(!focz){ + FLIfocus.Ndevices = findcams(FLIDOMAIN_USB | FLIDEVICE_FOCUSER, &focz); + if(!FLIfocus.Ndevices){ + DBG("No focusers"); + return FALSE; + } + for(int i = 0; i < FLIfocus.Ndevices; i++){ + DBG("Focuser '%s', domain %s", focz[i].name, focz[i].dname); + } + } + return TRUE; +} +static int fli_setActiceFocuser(int n){ + if(!focz && !fli_findFocuser()) return FALSE; + if(n >= FLIfocus.Ndevices) return FALSE; + FLIClose(focdev); + int OK = FALSE; + for(int i = 0; i < FLIfocus.Ndevices; ++i){ + DBG("Try %s", focz[i].name); + TRYFUNC(FLIOpen, &focdev, focz[i].name, focz[i].domain); + if(fli_err) continue; + TRYFUNC(FLIGetModel, focdev, focname, BUFSIZ); + DBG("MODEL '%s'", focname); + if(fli_err) continue; + if(!strcasestr(focname, "focuser")){ // not a focuser? + DBG("Not a focuser"); + TRYFUNC(FLIClose, focdev); + continue; + } + if(n-- == 0){ + OK = TRUE; break; + } + } + if(!OK){ + DBG("Not found"); + return FALSE; + } + DBG("Focuser: %s", focname); +#ifdef EBUG + TRYFUNC(FLIGetHWRevision, focdev, &tmpl); + if(!fli_err) DBG("HW revision: %ld", tmpl); + TRYFUNC(FLIGetFWRevision, focdev, &tmpl); + if(!fli_err) DBG("SW revision: %ld", tmpl); +#endif + TRYFUNC(FLIGetStepperPosition, focdev, &focuserpos); + TRYFUNC(FLIGetFocuserExtent, focdev, &focmaxpos); + DBG("Curpos: %ld, maxpos: %ld", focuserpos, focmaxpos); + return TRUE; +} + +static int fli_fgetmodel(char *model, int l){ + strncpy(model, focname, l); + return TRUE; +} + +static int fli_fgett(float *t){ + if(!t) return FALSE; + double d; + if(FLIReadTemperature(focdev, FLI_TEMPERATURE_INTERNAL, &d)) return FALSE; + *t = (float) d; + return TRUE; +} + +static int fli_fgetpos(float *p){ + if(!p) return FALSE; + TRYFUNC(FLIGetStepperPosition, focdev, &focuserpos); + if(fli_err) return FALSE; + *p = focuserpos / FOCSCALE; + return TRUE; +} + +static int fli_fgetmaxpos(float *p){ + if(!p) return FALSE; + *p = focmaxpos / FOCSCALE; + return TRUE; +} + +static int fli_fgetminpos(float *p){ + if(!p) return FALSE; + *p = 0.; + return TRUE; +} + +static int fli_fhome(int async){ + if(async) TRYFUNC(FLIHomeDevice, focdev); + else TRYFUNC(FLIHomeFocuser, focdev); + if(fli_err) return FALSE; + return TRUE; +} + +static int fli_fgoto(int async, float pos){ + long tagpos = pos * FOCSCALE; + if(tagpos > focmaxpos) return FALSE; + DBG("tagpos: %ld, focuserpos: %ld", tagpos, focuserpos); + tagpos -= focuserpos; + if(labs(tagpos) < 2) return TRUE; + DBG("tagpos: %ld", tagpos); + if(async) TRYFUNC(FLIStepMotorAsync, focdev, tagpos); + else TRYFUNC(FLIStepMotor, focdev, tagpos); + return TRUE; +} + +static int fli_findWheel(){ + if(whlz) return TRUE; + DBG("Try to find FLI filter wheels .. "); + if(!fli_init()){ + DBG("FLI not found"); + return FALSE; + } + FLIwheel.Ndevices = findcams(FLIDOMAIN_USB | FLIDEVICE_FILTERWHEEL, &whlz); + if(!FLIwheel.Ndevices){ + DBG("No wheels"); + return FALSE; + } + for(int i = 0; i < FLIwheel.Ndevices; i++){ + DBG("Wheel '%s', domain %s", whlz[i].name, whlz[i].dname); + } + return TRUE; +} + +static int fli_wgetpos(int *p); + +static int fli_setActiceWheel(int n){ + if(!whlz && !fli_findWheel()) return FALSE; + if(n >= FLIwheel.Ndevices) return FALSE; + FLIClose(whldev); + int OK = FALSE; + for(int i = 0; i < FLIfocus.Ndevices; ++i){ + DBG("Try %s", whlz[i].name); + TRYFUNC(FLIOpen, &whldev, whlz[i].name, whlz[i].domain); + if(fli_err) continue; + TRYFUNC(FLIGetFilterCount, whldev, &filtermax); + if(fli_err || filtermax < 2){ // not a wheel + DBG("Not a wheel"); + TRYFUNC(FLIClose, whldev); + continue; + } + if(n-- == 0){ + OK = TRUE; break; + } + } + if(!OK){ + DBG("Not found"); + return FALSE; + } + TRYFUNC(FLIGetModel, whldev, whlname, BUFSIZ); + DBG("Wheel: %s", whlname); +#ifdef EBUG + TRYFUNC(FLIGetHWRevision, whldev, &tmpl); + if(!fli_err) DBG("HW revision: %ld", tmpl); + TRYFUNC(FLIGetFWRevision, whldev, &tmpl); + if(!fli_err) DBG("SW revision: %ld", tmpl); +#endif + --filtermax; // max position number + int tmp; + fli_wgetpos(&tmp); + DBG("Cur position: %ld, max position: %ld", filterpos, filtermax); + return TRUE; +} + +static int fli_wgetname(char *x, int n){ + strncpy(x, whlname, n); + return TRUE; +} + +static int fli_wgetmaxpos(int *p){ + if(!p) return FALSE; + *p = filtermax; + return TRUE; +} + +static int fli_wgetpos(int *p){ + if(!p) return FALSE; + //TRYFUNC(FLIGetFilterPos, whldev, &filterpos); - wrong position! + //if(fli_err){ + // DBG("FLIGetFilterPos - ERROR!"); + TRYFUNC(FLIGetStepperPosition, whldev, &tmpl); + if(fli_err) return FALSE; + if(tmpl < 0) tmpl = -tmpl; + int pos = (tmpl - WHEEL_POS0STPS+WHEEL_STEPPOS/2)/WHEEL_STEPPOS; + DBG("pos = %d", pos); + if(pos > -1 && pos <= filtermax) filterpos = pos; + else return FALSE; + //} + *p = filterpos; + return TRUE; +} + +static int fli_wsetpos(int p){ + if(p == filterpos) return TRUE; + if(p > filtermax || p < 0) return FALSE; + TRYFUNC(FLISetFilterPos, whldev, (long)p); + if(fli_err) return FALSE; + return TRUE; +} + +static int fli_wgett(float *t){ + if(!t) return FALSE; + double d; + if(FLIReadTemperature(whldev, FLI_TEMPERATURE_INTERNAL, &d)) return FALSE; + *t = (float) d; + return TRUE; +} + +static int fli_pollcapt(capture_status *st, float *remain){ + static int errctr = 0; + if(capStatus == CAPTURE_READY){ + DBG("Capture ends"); + goto retn; + } + if(capStatus == CAPTURE_NO){ // start capture + errctr = 0; + DBG("Start exposition"); + TRYFUNC(FLIExposeFrame, camdev); + if(fli_err){ + TRYFUNC(FLICancelExposure, camdev); + if(st) *st = CAPTURE_CANTSTART; + return FALSE; + } + capStatus = CAPTURE_PROCESS; + } + if(capStatus == CAPTURE_PROCESS){ + TRYFUNC(FLIGetExposureStatus, camdev, &tmpl); + if(fli_err){ + if(++errctr > 3){ + if(st) *st = CAPTURE_ABORTED; + TRYFUNC(FLICancelExposure, camdev); + capStatus = CAPTURE_NO; + return FALSE; + } + goto retn; + } + if(remain) *remain = tmpl/1000.; + DBG("remained: %g", tmpl/1000.); + if(tmpl == 0){ + if(st) *st = CAPTURE_READY; + capStatus = CAPTURE_NO; + return TRUE; + } + }else{ // some error + if(st) *st = CAPTURE_ABORTED; + capStatus = CAPTURE_NO; + } +retn: + if(st) *st = capStatus; + return TRUE; +} + +static int fli_capt(IMG *ima){ + if(!ima || !ima->data) return FALSE; + for(int row = 0; row < ima->h; row++){ + TRYFUNC(FLIGrabRow, camdev, &ima->data[row * ima->w], ima->w); + if(fli_err) return FALSE; + } + return TRUE; +} + +static int fli_modelname(char *buf, int bufsz){ + strncpy(buf, camname, bufsz); + return TRUE; +} + +static int fli_setbin(int binh, int binv){ + TRYFUNC(FLISetHBin, camdev, binh); + if(fli_err) return FALSE; + curhbin = binh; + TRYFUNC(FLISetVBin, camdev, binv); + if(fli_err) return FALSE; + curvbin = binv; + return TRUE; +} + +static int fli_getbin(int *h, int *v){ + if(h) *h = curhbin; + if(v) *v = curvbin; + return TRUE; +} + +static int fli_setgeometry(frameformat *f){ + if(!f) return FALSE; + TRYFUNC(FLISetImageArea, camdev, f->xoff, f->yoff, + f->xoff + f->w/curhbin, f->yoff + f->h/curvbin); + if(fli_err) return FALSE; + FLIcam.geometry = *f; + return TRUE; +} + +static int fli_setnflushes(int n){ + if(n < 0) return FALSE; + if(n){ + TRYFUNC(FLIControlBackgroundFlush, camdev, FLI_BGFLUSH_START); + TRYFUNC(FLISetNFlushes, camdev, n); + } + else TRYFUNC(FLIControlBackgroundFlush, camdev, FLI_BGFLUSH_STOP); + if(fli_err) return FALSE; + return TRUE; +} + +static int fli_settemp(float t){ + TRYFUNC(FLISetTemperature, camdev, t); + if(fli_err) return FALSE; + return TRUE; +} + +typedef enum{ + T_COLD, + T_BODY, + T_HOT +} temptype; + +static int fli_gettemp(temptype type, float *t){ + double d; + switch(type){ + case T_COLD: + TRYFUNC(FLIGetTemperature, camdev, &d); + break; + case T_BODY: + TRYFUNC(FLIReadTemperature, camdev, FLI_TEMPERATURE_EXTERNAL, &d); + break; + default: + TRYFUNC(FLIReadTemperature, camdev, FLI_TEMPERATURE_INTERNAL, &d); + } + if(fli_err) return FALSE; + *t = d; + return TRUE; +} + +static int fli_getTcold(float *t){ + return fli_gettemp(T_COLD, t); +} +static int fli_getTbody(float *t){ + return fli_gettemp(T_BODY, t); +} +static int fli_getThot(float *t){ + return fli_gettemp(T_HOT, t); +} + +static void fli_cancel(){ + TRYFUNC(FLICancelExposure, camdev); + TRYFUNC(FLIEndExposure, camdev); +} + +static int fli_shutter(shutter_op cmd){ + flishutter_t shtr = FLI_SHUTTER_CLOSE; + switch(cmd){ + case SHUTTER_OPEN: + shtr = FLI_SHUTTER_OPEN; + break; + case SHUTTER_CLOSE: + break; + case SHUTTER_OPENATHIGH: + shtr = FLI_SHUTTER_EXTERNAL_EXPOSURE_CONTROL|FLI_SHUTTER_EXTERNAL_TRIGGER_HIGH; + break; + case SHUTTER_OPENATLOW: + shtr = FLI_SHUTTER_EXTERNAL_EXPOSURE_CONTROL|FLI_SHUTTER_EXTERNAL_TRIGGER_HIGH; + break; + default: + return FALSE; + } + TRYFUNC(FLIControlShutter, camdev, shtr); + if(fli_err) return FALSE; + return TRUE; +} + +static int fli_confio(int io){ + TRYFUNC(FLIConfigureIOPort, camdev, io); + if(fli_err) return FALSE; + return TRUE; +} + +static int fli_getio(int *io){ + if(!io) return FALSE; + long lio = (long)*io; + TRYFUNC(FLIReadIOPort, camdev, &lio); + if(fli_err) return FALSE; + return TRUE; +} + +static int fli_setio(int io){ + TRYFUNC(FLIWriteIOPort, camdev, (long)io); + if(fli_err) return FALSE; + return TRUE; +} + +static int fli_setexp(float t){ + long e = (long)(t*1000.); + TRYFUNC(FLISetExposureTime, camdev, e); + if(fli_err) return FALSE; + return TRUE; +} + +static int fli_setframetype(int t){ + fliframe_t frametype = t ? FLI_FRAME_TYPE_NORMAL : FLI_FRAME_TYPE_DARK; + TRYFUNC(FLISetFrameType, camdev, frametype); + if(fli_err) return FALSE; + return TRUE; +} + +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; + return TRUE; +} + +static int fli_setfastspeed(int fast){ + flimode_t mode = fast ? 0 : 1; + TRYFUNC(FLISetCameraMode, camdev, mode); + if(fli_err) return FALSE; + return TRUE; +} + +static int fli_setfanspd(fan_speed s){ + long sp = (s == FAN_OFF) ? FLI_FAN_SPEED_OFF : FLI_FAN_SPEED_ON; + TRYFUNC(FLISetFanSpeed, camdev, sp); + if(fli_err) return FALSE; + return TRUE; +} + +static void camt_free(cam_t **c, int n, flidev_t dev){ + if(!c || !*c) return; + TRYFUNC(FLIClose, dev); + for(int i = 0; i < n; ++i) + FREE((*c)[i].name); + FREE(*c); +} + +static void fli_closecam(){ + DBG("CAMERA CLOSE"); + camt_free(&camz, FLIcam.Ndevices, camdev); +} +static void fli_closefocuser(){ + DBG("FOCUSER CLOSE"); + camt_free(&focz, FLIfocus.Ndevices, focdev); +} +static void fli_closewheel(){ + DBG("WHEEL CLOSE"); + camt_free(&whlz, FLIwheel.Ndevices, whldev); +} + +static int fli_ffalse(_U_ float f){ return FALSE; } +static int fli_fpfalse(_U_ float *f){ return FALSE; } + +/* + * Global objects: camera, focuser and wheel + */ +Camera FLIcam = { + .check = fli_findCCD, + .setDevNo = fli_setActiceCam, + .close = fli_closecam, + .pollcapture = fli_pollcapt, + .capture = fli_capt, + .cancel = fli_cancel, + + .setbin = fli_setbin, + .setgeometry = fli_setgeometry, + .setnflushes = fli_setnflushes, + .setT = fli_settemp, + .setio = fli_setio, + .setexp = fli_setexp, + .setframetype = fli_setframetype, + .setbitdepth = fli_setbitdepth, + .setfastspeed = fli_setfastspeed, + .setfanspeed = fli_setfanspd, + .shuttercmd = fli_shutter, + .confio = fli_confio, + + .getModelName = fli_modelname, + .getbin = fli_getbin, + .getTcold = fli_getTcold, + .getThot = fli_getThot, + .getTbody = fli_getTbody, + .getio = fli_getio, + .getgeomlimits = fli_geomlimits, + + .setbrightness = fli_ffalse, + .setgain = fli_ffalse, + .getmaxgain = fli_fpfalse, +}; +Focuser FLIfocus = { + .check = fli_findFocuser, + .setDevNo = fli_setActiceFocuser, + .close = fli_closefocuser, + .getModelName = fli_fgetmodel, + .getTbody = fli_fgett, + .getPos = fli_fgetpos, + .getMaxPos = fli_fgetmaxpos, + .getMinPos = fli_fgetminpos, + .home = fli_fhome, + .setAbsPos = fli_fgoto, +}; +Wheel FLIwheel = { + .check = fli_findWheel, + .setDevNo = fli_setActiceWheel, + .close = fli_closewheel, + .getModelName = fli_wgetname, + .getMaxPos = fli_wgetmaxpos, + .getPos = fli_wgetpos, + .setPos = fli_wsetpos, + .getTbody = fli_wgett, +}; diff --git a/FLI_cameras/flifunc.h b/FLI_cameras/flifunc.h new file mode 100644 index 0000000..9875700 --- /dev/null +++ b/FLI_cameras/flifunc.h @@ -0,0 +1,29 @@ +/* + * 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 . + */ + +#pragma once +#ifndef FLIFUNC_H__ +#define FLIFUNC_H__ + +#include "ccdfunc.h" + +extern Camera FLIcam; +extern Focuser FLIfocus; +extern Wheel FLIwheel; + +#endif // FLIFUNC_H__ diff --git a/FindCFITSIO.cmake b/FindCFITSIO.cmake new file mode 100644 index 0000000..01dd612 --- /dev/null +++ b/FindCFITSIO.cmake @@ -0,0 +1,67 @@ +# - Try to find CFITSIO +# Once done this will define +# +# CFITSIO_FOUND - system has CFITSIO +# CFITSIO_INCLUDE_DIR - the CFITSIO include directory +# CFITSIO_LIBRARIES - Link these to use CFITSIO +# CFITSIO_VERSION_STRING - Human readable version number of cfitsio +# CFITSIO_VERSION_MAJOR - Major version number of cfitsio +# CFITSIO_VERSION_MINOR - Minor version number of cfitsio + +# Copyright (c) 2006, Jasem Mutlaq +# Based on FindLibfacile by Carsten Niehaus, +# +# Redistribution and use is allowed according to the terms of the BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. + +if (CFITSIO_INCLUDE_DIR AND CFITSIO_LIBRARIES) + + # in cache already, be quiet + set(CFITSIO_FIND_QUIETLY TRUE) + +else (CFITSIO_INCLUDE_DIR AND CFITSIO_LIBRARIES) + + # JM: Packages from different distributions have different suffixes + find_path(CFITSIO_INCLUDE_DIR fitsio.h + PATH_SUFFIXES libcfitsio3 libcfitsio0 cfitsio + PATHS + $ENV{CFITSIO} + ${_obIncDir} + ${GNUWIN32_DIR}/include + ) + + find_library(CFITSIO_LIBRARIES NAMES cfitsio + PATHS + $ENV{CFITSIO} + ${_obLinkDir} + ${GNUWIN32_DIR}/lib + ) + + if(CFITSIO_INCLUDE_DIR AND CFITSIO_LIBRARIES) + set(CFITSIO_FOUND TRUE) + else (CFITSIO_INCLUDE_DIR AND CFITSIO_LIBRARIES) + set(CFITSIO_FOUND FALSE) + endif(CFITSIO_INCLUDE_DIR AND CFITSIO_LIBRARIES) + + + if (CFITSIO_FOUND) + + # Find the version of the cfitsio header + FILE(READ "${CFITSIO_INCLUDE_DIR}/fitsio.h" FITSIO_H) + STRING(REGEX REPLACE ".*#define CFITSIO_VERSION[^0-9]*([0-9]+)\\.([0-9]+).*" "\\1.\\2" CFITSIO_VERSION_STRING "${FITSIO_H}") + STRING(REGEX REPLACE "^([0-9]+)[.]([0-9]+)" "\\1" CFITSIO_VERSION_MAJOR ${CFITSIO_VERSION_STRING}) + STRING(REGEX REPLACE "^([0-9]+)[.]([0-9]+)" "\\2" CFITSIO_VERSION_MINOR ${CFITSIO_VERSION_STRING}) + message(STATUS "found version string ${CFITSIO_VERSION_STRING}") + + if (NOT CFITSIO_FIND_QUIETLY) + message(STATUS "Found CFITSIO ${CFITSIO_VERSION_MAJOR}.${CFITSIO_VERSION_MINOR}: ${CFITSIO_LIBRARIES}") + endif (NOT CFITSIO_FIND_QUIETLY) + else (CFITSIO_FOUND) + if (CFITSIO_FIND_REQUIRED) + message(STATUS "CFITSIO not found.") + endif (CFITSIO_FIND_REQUIRED) + endif (CFITSIO_FOUND) + + mark_as_advanced(CFITSIO_INCLUDE_DIR CFITSIO_LIBRARIES) + +endif (CFITSIO_INCLUDE_DIR AND CFITSIO_LIBRARIES) diff --git a/ZWO_cameras/CMakeLists.txt b/ZWO_cameras/CMakeLists.txt new file mode 100644 index 0000000..c88bbe3 --- /dev/null +++ b/ZWO_cameras/CMakeLists.txt @@ -0,0 +1,23 @@ +cmake_minimum_required(VERSION 3.20) +set(CCDLIB zwo_module) +set(ZWOLIB ${CCDLIB} PARENT_SCOPE) + +find_package(PkgConfig REQUIRED) +pkg_check_modules(${CCDLIB} REQUIRED usefull_macros) + +set(CFLAGS -O3 -Wextra -Wall -W -std=gnu99) +set(CMAKE_COLOR_MAKEFILE ON) + +if(DEFINED DEBUG AND DEBUG STREQUAL "yes") + set(CFLAGS ${CFLAGS} -Werror) + add_definitions(-DEBUG) + set(CMAKE_VERBOSE_MAKEFILE "ON") +endif() + +add_definitions(${CFLAGS}) + +aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR} SRC) +add_library(${CCDLIB} ${SRC}) +target_link_libraries(${CCDLIB} ${${CCDLIB}_LIBRARIES} -lASICamera2) +include_directories(${${CCDLIB}_INCLUDE_DIRS} ..) +link_directories(${${CCDLIB}_LIBRARY_DIRS}) diff --git a/ZWO_cameras/zwofunc.c b/ZWO_cameras/zwofunc.c new file mode 100644 index 0000000..78344c0 --- /dev/null +++ b/ZWO_cameras/zwofunc.c @@ -0,0 +1,23 @@ +/* + * 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 "zwofunc.h" + +Camera ZWOcam; +Focuser ZWOfocus; +Wheel ZWOwheel; diff --git a/ZWO_cameras/zwofunc.h b/ZWO_cameras/zwofunc.h new file mode 100644 index 0000000..9f99c23 --- /dev/null +++ b/ZWO_cameras/zwofunc.h @@ -0,0 +1,29 @@ +/* + * 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 . + */ + +#pragma once +#ifndef ZWOFUNC_H__ +#define ZWOFUNC_H__ + +#include "ccdfunc.h" + +extern Camera ZWOcam; +extern Focuser ZWOfocus; +extern Wheel ZWOwheel; + +#endif // ZWOFUNC_H__ diff --git a/ccdfunc.c b/ccdfunc.c new file mode 100644 index 0000000..6b05d4f --- /dev/null +++ b/ccdfunc.c @@ -0,0 +1,657 @@ +/* + * This file is part of the FLI_control project. + * Copyright 2020 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 +#include +#include +#include +#include + +#include "ccdfunc.h" +#include "cmdlnopts.h" +#ifdef USEFLI +#include "flifunc.h" +#endif +#ifdef USEZWO +#include "zwofunc.h" +#endif +#ifdef IMAGEVIEW +#include "imageview.h" +#endif + +Camera *camera = NULL; +Focuser *focuser = NULL; +Wheel *wheel = NULL; + +static int fitserror = 0; + +#define TRYFITS(f, ...) \ +do{ int status = 0; \ + f(__VA_ARGS__, &status); \ + if(status){ \ + fits_report_error(stderr, status); \ + fitserror = status;} \ +}while(0) +#define WRITEKEY(...) \ +do{ int status = 0; \ + fits_write_key(__VA_ARGS__, &status); \ + if(status) fits_report_error(stderr, status);\ +}while(0) + +#define TMBUFSIZ 40 + +/* +static size_t curtime(char *s_time){ // current date/time + time_t tm = time(NULL); + return strftime(s_time, TMBUFSIZ, "%d/%m/%Y,%H:%M:%S", localtime(&tm)); +}*/ + +static int check_filename(char *buff, char *outfile, char *ext){ + struct stat filestat; + int num; + for(num = 1; num < 10000; num++){ + if(snprintf(buff, PATH_MAX, "%s_%04d.%s", outfile, num, ext) < 1) + return 0; + if(stat(buff, &filestat)) // no such file or can't stat() + return 1; + } + return 0; +} + +/** + * @brief addrec - add FITS records from file + * @param f (i) - FITS filename + * @param filename (i) - name of file + */ +static void addrec(fitsfile *f, char *filename){ + FILE *fp = fopen(filename, "r"); + if(!fp) return; + char buf[2*FLEN_CARD]; + while(fgets(buf, 2*FLEN_CARD, fp)){ + //DBG("check record _%s_", buf); + int keytype, status = 0; + char newcard[FLEN_CARD], keyname[FLEN_CARD]; + fits_parse_template(buf, newcard, &keytype, &status); + if(status){ + fits_report_error(stderr, status); + continue; + } + //DBG("reformatted to _%s_", newcard); + strncpy(keyname, newcard, FLEN_CARD); + char *eq = strchr(keyname, '='); if(eq) *eq = 0; + eq = strchr(keyname, ' '); if(eq) *eq = 0; + //DBG("keyname: %s", keyname); + fits_update_card(f, keyname, newcard, &status); + } +} + +void saveFITS(IMG *img, char *filename){ + char buff[PATH_MAX]; + if(filename == NULL) return; + fitserror = 0; + if(!check_filename(buff, filename, "fits") && !GP->rewrite){ + // + WARNX(_("Can't save file")); + }else{ + if(GP->rewrite){ + DBG("REW"); + snprintf(buff, PATH_MAX, "!%s.fits", filename); + } + } + int width = img->w, height = img->h; + void *data = (void*) img->data; + long naxes[2] = {width, height}, tmpl; + double tmpd = 0.0; + float tmpf = 0.0; + int tmpi = 0; + struct tm *tm_time; + char bufc[FLEN_CARD]; + time_t savetime = time(NULL); + fitsfile *fp; + TRYFITS(fits_create_file, &fp, buff); + if(fitserror) goto cloerr; + TRYFITS(fits_create_img, fp, USHORT_IMG, 2, naxes); + if(fitserror) goto cloerr; + // FILE / Input file original name + WRITEKEY(fp, TSTRING, "FILE", filename, "Input file original name"); + // ORIGIN / organization responsible for the data + WRITEKEY(fp, TSTRING, "ORIGIN", "SAO RAS", "organization responsible for the data"); + // OBSERVAT / Observatory name + WRITEKEY(fp, TSTRING, "OBSERVAT", "Special Astrophysical Observatory, Russia", "Observatory name"); + // DETECTOR / detector + if(camera->getModelName(buff, PATH_MAX)){ + WRITEKEY(fp, TSTRING, "DETECTOR", buff, "Detector model"); + } + // INSTRUME / Instrument + if(GP->instrument){ + WRITEKEY(fp, TSTRING, "INSTRUME", GP->instrument, "Instrument"); + }else + WRITEKEY(fp, TSTRING, "INSTRUME", "direct imaging", "Instrument"); + snprintf(bufc, FLEN_VALUE, "%.g x %.g", camera->pixX, camera->pixY); + // PXSIZE / pixel size + WRITEKEY(fp, TSTRING, "PXSIZE", bufc, "Pixel size in m"); + snprintf(bufc, FLEN_VALUE, "(%d, %d)(%d, %d)", camera->field.xoff, camera->field.yoff, + camera->field.xoff + camera->field.w, camera->field.yoff + camera->field.h); + WRITEKEY(fp, TSTRING, "VIEWFLD", bufc, "Camera field of view"); + snprintf(bufc, FLEN_VALUE, "(%d, %d)(%d, %d)", camera->array.xoff, camera->array.yoff, + camera->array.xoff + camera->array.w, camera->array.yoff + camera->array.h); + WRITEKEY(fp, TSTRING, "ARRAYFLD", bufc, "Camera full array size"); + // CRVAL1, CRVAL2 / Offset in X, Y + if(GP->X0 > -1) WRITEKEY(fp, TINT, "X0", &GP->X0, "Subframe left border"); + if(GP->Y0 > -1) WRITEKEY(fp, TINT, "Y0", &GP->Y0, "Subframe upper border"); + if(GP->objtype) strncpy(bufc, GP->objtype, FLEN_CARD-1); + else sprintf(bufc, "object"); + // IMAGETYP / object, flat, dark, bias, scan, eta, neon, push + WRITEKEY(fp, TSTRING, "IMAGETYP", bufc, "Image type"); + // DATAMAX, DATAMIN / Max, min pixel value + tmpi = 0; + WRITEKEY(fp, TINT, "DATAMIN", &tmpi, "Min pixel value"); + //itmp = GP->fast ? 255 : 65535; + tmpi = 65535; + WRITEKEY(fp, TINT, "DATAMAX", &tmpi, "Max pixel value"); + tmpi = img->min; + WRITEKEY(fp, TUSHORT, "STATMIN", &tmpi, "Min data value"); + tmpi = img->max; + WRITEKEY(fp, TUSHORT, "STATMAX", &tmpi, "Max data value"); + tmpf = img->avr; + WRITEKEY(fp, TFLOAT, "STATAVR", &tmpf, "Average data value"); + tmpf = img->std; + WRITEKEY(fp, TFLOAT, "STATSTD", &tmpf, "Std. of data value"); + WRITEKEY(fp, TFLOAT, "CAMTEMP0", &GP->temperature, "Camera temperature at exp. start, degr C"); + if(camera->getTcold(&tmpf)) + WRITEKEY(fp, TFLOAT, "CAMTEMP", &tmpf, "Camera temperature at exp. end, degr C"); + if(camera->getTbody(&tmpf)) + WRITEKEY(fp, TFLOAT, "BODYTEMP", &tmpf, "Camera body temperature at exp. end, degr C"); + if(camera->getThot(&tmpf)) + WRITEKEY(fp, TFLOAT, "HOTTEMP", &tmpf, "Camera peltier hot side temperature at exp. end, degr C"); + // EXPTIME / actual exposition time (sec) + tmpd = GP->exptime; + WRITEKEY(fp, TDOUBLE, "EXPTIME", &tmpd, "Actual exposition time (sec)"); + // DATE / Creation date (YYYY-MM-DDThh:mm:ss, UTC) + strftime(bufc, FLEN_VALUE, "%Y-%m-%dT%H:%M:%S", gmtime(&savetime)); + WRITEKEY(fp, TSTRING, "DATE", bufc, "Creation date (YYYY-MM-DDThh:mm:ss, UTC)"); + tmpl = (long) savetime; + tm_time = localtime(&savetime); + strftime(bufc, FLEN_VALUE, "File creation time (UNIX)", tm_time); + WRITEKEY(fp, TLONG, "UNIXTIME", &tmpl, bufc); + strftime(bufc, 80, "%Y/%m/%d", tm_time); + // DATE-OBS / DATE (YYYY/MM/DD) OF OBS. + WRITEKEY(fp, TSTRING, "DATE-OBS", bufc, "DATE OF OBS. (YYYY/MM/DD, local)"); + strftime(bufc, 80, "%H:%M:%S", tm_time); + WRITEKEY(fp, TSTRING, "TIME", bufc, "Creation time (hh:mm:ss, local)"); + // OBJECT / Object name + if(GP->objname){ + WRITEKEY(fp, TSTRING, "OBJECT", GP->objname, "Object name"); + } + // BINNING / Binning + if(GP->hbin != 1 || GP->vbin != 1){ + snprintf(bufc, 80, "%d x %d", GP->hbin, GP->vbin); + WRITEKEY(fp, TSTRING, "BINNING", bufc, "Binning (hbin x vbin)"); + } + // OBSERVER / Observers + if(GP->observers){ + WRITEKEY(fp, TSTRING, "OBSERVER", GP->observers, "Observers"); + } + // PROG-ID / Observation program identifier + if(GP->prog_id){ + WRITEKEY(fp, TSTRING, "PROG-ID", GP->prog_id, "Observation program identifier"); + } + // AUTHOR / Author of the program + if(GP->author){ + WRITEKEY(fp, TSTRING, "AUTHOR", GP->author, "Author of the program"); + } + if(focuser){ // there is a focuser device - add info + if(focuser->getModelName(buff, PATH_MAX)) + WRITEKEY(fp, TSTRING, "FOCUSER", buff, "Focuser model"); + if(focuser->getPos(&tmpf)) + WRITEKEY(fp, TFLOAT, "FOCUS", &tmpf, "Current focuser position, mm"); + if(focuser->getMinPos(&tmpf)) + WRITEKEY(fp, TFLOAT, "FOCMIN", &tmpf, "Minimal focuser position, mm"); + if(focuser->getMaxPos(&tmpf)) + WRITEKEY(fp, TFLOAT, "FOCMAX", &tmpf, "Maximal focuser position, mm"); + if(focuser->getTbody(&tmpf)) + WRITEKEY(fp, TFLOAT, "FOCTEMP", &tmpf, "Focuser body temperature, degr C"); + } + if(wheel){ // there is a filter wheel device - add info + if(wheel->getModelName(buff, PATH_MAX)) + WRITEKEY(fp, TSTRING, "WHEEL", buff, "Filter wheel model"); + if(wheel->getPos(&tmpi)) + WRITEKEY(fp, TINT, "FILTER", &tmpi, "Current filter number"); + if(wheel->getMaxPos(&tmpi)) + WRITEKEY(fp, TINT, "FILTMAX", &tmpi, "Amount of filter positions"); + if(wheel->getTbody(&tmpf)) + WRITEKEY(fp, TFLOAT, "FILTTEMP", &tmpf, "Filter wheel body temperature, degr C"); + } + if(GP->addhdr){ // add records from files + char **nxtfile = GP->addhdr; + while(*nxtfile){ + addrec(fp, *nxtfile++); + } + } + TRYFITS(fits_write_img, fp, TUSHORT, 1, width * height, data); + if(fitserror) goto cloerr; + TRYFITS(fits_close_file, fp); +cloerr: + if(fitserror == 0){ + verbose(1, _("File saved as '%s'"), buff); + }else{ + WARNX(_("Error saving file")); + fitserror = 0; + } +} + +static void print_stat(IMG *image){ + long i, Noverld = 0L, size = image->h*image->w; + float pv, sum=0., sum2=0., sz = (float)size; + uint16_t *ptr = image->data, val; + uint16_t max = 0, min = 65535; + for(i = 0; i < size; i++, ptr++){ + val = *ptr; + pv = (float) val; + sum += pv; + sum2 += (pv * pv); + if(max < val) max = val; + if(min > val) min = val; + if(val >= 65530) Noverld++; + } + // :\n + printf(_("Image stat:\n")); + float avr = sum/sz; + printf("avr = %.1f, std = %.1f, Noverload = %ld\n", image->avr = avr, + image->std = sqrt(fabs(sum2/sz - avr*avr)), Noverld); + printf("max = %u, min = %u, size = %ld\n", max, min, size); +} + +/* + * Find focusers and work with each of them + */ +void focusers(){ + if(!GP->focuserdev){ + verbose(3, _("Focuser device not pointed, try to guess")); +#ifdef USEFLI + if(FLIfocus.check()) focuser = &FLIfocus; +#endif +#ifdef USEZWO + if(ZWOfocus.check()) focuser = &ZWOfocus; +#endif + }else{ +#ifdef USEFLI + if(strcasecmp(GP->focuserdev, "fli") == 0) focuser = &FLIfocus; +#endif +#ifdef USEZWO + if(strcasecmp(GP->focuserdev, "zwo") == 0) focuser = &ZWOfocus; +#endif + } + if(!focuser){ + WARNX(_("Focuser not found")); + return; + } + int num = GP->focdevno; + if(num > focuser->Ndevices - 1){ + WARNX(_("Found %d focusers, you point number %d"), focuser->Ndevices, num); + goto retn; + } + if(!focuser->setDevNo(num)){ + WARNX(_("Can't set active focuser number")); + goto retn; + } + char buf[BUFSIZ]; + if(focuser->getModelName(buf, BUFSIZ)){ + verbose(2, "Focuser model: %s", buf); + } + float t; + if(focuser->getTbody(&t)){ + verbose(1, "FOCTEMP=%.1f", t); + DBG("FOCTEMP=%.1f", t); + } + float minpos, maxpos, curpos; + if(!focuser->getMinPos(&minpos) || !focuser->getMaxPos(&maxpos)){ + WARNX(_("Can't get focuser limit positions")); + goto retn; + } + verbose(1, "FOCMINPOS=%g", minpos); + verbose(1, "FOCMAXPOS=%g", maxpos); + DBG("FOCMINPOS=%g, FOCMAXPOS=%g", minpos, maxpos); + if(!focuser->getPos(&curpos)){ + WARNX(_("Can't get current focuser position")); + goto retn; + } + verbose(1, "FOCPOS=%g", curpos); + DBG("Curpos = %g", curpos); + if(isnan(GP->gotopos) && isnan(GP->addsteps)) goto retn; // no focuser commands + float tagpos = 0.; + if(!isnan(GP->gotopos)){ // set absolute position + tagpos = GP->gotopos; + }else{ // relative + tagpos = curpos + GP->addsteps; + } + DBG("tagpos: %g", tagpos); + if(tagpos < minpos || tagpos > maxpos){ + WARNX(_("Can't set position %g: out of limits [%g, %g]"), tagpos, minpos, maxpos); + goto retn; + } + if(tagpos - minpos < __FLT_EPSILON__){ + if(!focuser->home(GP->async)) WARNX(_("Can't home focuser")); + }else{ + if(!focuser->setAbsPos(GP->async, tagpos)) WARNX(_("Can't set position %g"), tagpos); + } +retn: + focuser->close(); + focuser = NULL; +} + +/* + * Find wheels and work with each of them + */ +void wheels(){ + if(!GP->wheeldev){ + verbose(3, _("Wheel device not pointed, try to guess")); +#ifdef USEFLI + if(FLIwheel.check()) wheel = &FLIwheel; +#endif +#ifdef USEZWO + if(ZWOwheel.check()) wheel = &ZWOwheel; +#endif + }else{ +#ifdef USEFLI + if(strcasecmp(GP->wheeldev, "fli") == 0) wheel = &FLIwheel; +#endif +#ifdef USEZWO + if(strcasecmp(GP->wheeldev, "zwo") == 0) wheel = &ZWOwheel; +#endif + } + if(!wheel){ + WARNX(_("Wheel not found")); + return; + } + int num = GP->whldevno; + if(num > wheel->Ndevices - 1){ + WARNX(_("Found %d wheels, you point number %d"), wheel->Ndevices, num); + goto retn; + } + if(!wheel->setDevNo(num)){ + WARNX(_("Can't set active wheel number")); + goto retn; + } + float t; + if(wheel->getTbody(&t)){ + verbose(1, "WHEELTEMP=%.1f", t); + } + int pos, maxpos; + if(wheel->getPos(&pos)){ + verbose(1, "WHEELPOS=%d", pos); + }else WARNX("Can't get current wheel position"); + if(!wheel->getMaxPos(&maxpos)){ + WARNX(_("Can't get max wheel position")); + goto retn; + } + verbose(1, "WHEELMAXPOS=%d", maxpos); + pos = GP->setwheel; + if(pos == -1) goto retn; // no wheel commands + if(pos < 0 || pos > maxpos){ + WARNX(_("Wheel position should be from 0 to %d"), maxpos); + goto retn; + } + if(!wheel->setPos(pos)) + WARNX(_("Can't set wheel position %d"), pos); +retn: + wheel->close(); + wheel = NULL; +} + +static void closeall(){ + if(camera){camera->close(); camera = NULL;} + if(focuser){focuser->close(); focuser = NULL;} + if(wheel){wheel->close(); wheel = NULL;} +} + +/* + * Find CCDs and work with each of them + */ +void ccds(){ + float tmpf; + int tmpi; + if(!GP->cameradev){ + verbose(3, _("Camera device not pointed, try to guess")); +#ifdef USEFLI + if(FLIcam.check()) camera = &FLIcam; +#endif +#ifdef USEZWO + if(ZWOcam.check()) camera = &ZWOcam; +#endif + }else{ +#ifdef USEFLI + if(strcasecmp(GP->cameradev, "fli") == 0) camera = &FLIcam; +#endif +#ifdef USEZWO + if(strcasecmp(GP->cameradev, "zwo") == 0) camera = &ZWOcam; +#endif + } + if(!camera){ + WARNX(_("Camera device not found")); + goto retn; + } + int num = GP->camdevno; + if(num > camera->Ndevices - 1){ + WARNX(_("Found %d cameras, you point number %d"), camera->Ndevices, num); + goto retn; + } + if(!camera->setDevNo(num)){ + WARNX(_("Can't set active camera number")); + goto retn; + } + if(GP->fanspeed > -1){ + if(GP->fanspeed > FAN_HIGH) GP->fanspeed = FAN_HIGH; + if(!camera->setfanspeed((fan_speed)GP->fanspeed)) + WARNX(_("Can't set fan speed")); + } + int x0,x1, y0,y1; + char buf[BUFSIZ]; + if(camera->getModelName(buf, BUFSIZ)) + verbose(2, _("Camera model: %s"), buf); + verbose(2, _("Pixel size: %g x %g"), camera->pixX, camera->pixY); + x0 = camera->array.xoff; + y0 = camera->array.yoff; + x1 = camera->array.xoff + camera->array.w; + y1 = camera->array.yoff + camera->array.h; + snprintf(buf, BUFSIZ, "(%d, %d)(%d, %d)", x0, y0, x1, y1); + verbose(2, _("Full array: %s"), buf); + snprintf(buf, BUFSIZ, "(%d, %d)(%d, %d)", camera->field.xoff, camera->field.yoff, + camera->field.xoff + camera->field.w, camera->field.yoff + camera->field.h); + verbose(2, _("Field of view: %s"), buf); + if(GP->temperature < 40.){ + if(!camera->setT((float)GP->temperature)) + WARNX(_("Can't set T to %g degC"), GP->temperature); + verbose(3, "SetT=%.1f", GP->temperature); + } + if(camera->getTcold(&tmpf)) verbose(1, "CCDTEMP=%.1f", tmpf); + if(camera->getTbody(&tmpf)) verbose(1, "BODYTEMP=%.1f", tmpf); + if(GP->shtr_cmd > -1 && GP->shtr_cmd < SHUTTER_AMOUNT){ + const char *str[] = {"open", "close", "expose @high", "expose @low"}; + verbose(1, _("Shutter command: %s\n"), str[GP->shtr_cmd]); + if(!camera->shuttercmd((shutter_op)GP->shtr_cmd)) + WARNX(_("Can't run shutter command %s (unsupported?)"), str[GP->shtr_cmd]); + } + if(GP->confio > -1){ + // " I/O %d\n" + verbose(1, _("Try to convfigure I/O port as %d"), GP->confio); + if(!camera->confio(GP->confio)) + WARNX(_("Can't configure (unsupported?)")); + } + if(GP->getio){ + if(camera->getio(&tmpi)) + verbose(1, "CCDIOPORT=9x%02x\n", tmpi); + else + WARNX(_("Can't get IOport state (unsupported?)")); + } + if(GP->setio > -1){ + // " %d I/O\n" + verbose(1, _("Try to write %d to I/O port"), GP->setio); + if(!camera->setio(GP->setio)) + WARNX(_("Can't set IOport")); + } + if(GP->exptime < 0.) goto retn; + /*********************** expose control ***********************/ +#ifdef IMAGEVIEW + windowData *mainwin = NULL; + if(GP->showimage) imageview_init(); +#endif + // cancel previous exp + camera->cancel(); + int binh = 1, binv = 1; + if(!camera->setbin(GP->hbin, GP->vbin)) + WARNX(_("Can't set binning %dx%d"), GP->hbin, GP->vbin); + if(!camera->getbin(&binh, &binv)) + WARNX(_("Can't get current binning")); + verbose(2, "Binning: %d x %d", binh, binv); + if(GP->fullframe){ + DBG("FULLFRAME"); + GP->X0 = x0; GP->Y0 = y0; GP->X1 = x1; GP->Y1 = y1; + } + if(GP->X0 == -1) GP->X0 = x0; // default values + if(GP->Y0 == -1) GP->Y0 = y0; + if(GP->X1 == -1) GP->X1 = x1; + else if(GP->X1 > x1) GP->X1 = x1; + if(GP->Y1 == -1) GP->Y1 = y1; + else if(GP->Y1 > y1) GP->Y1 = y1; + frameformat fmt = {.w = GP->X1 - GP->X0, .h = GP->Y1 - GP->Y0, .xoff = GP->X0, .yoff = GP->Y0}; + int raw_width = fmt.w / binh, raw_height = fmt.h / binv; + if(!camera->setgeometry(&fmt)) + WARNX(_("Can't set given geometry")); + verbose(3, "Geometry: off=%d/%d, wh=%d/%d", fmt.xoff, fmt.yoff, fmt.w, fmt.h); + if(!camera->setnflushes(GP->nflushes)) + WARNX(_("Can't set %d flushes"), GP->nflushes); + verbose(3, "Nflushes=%d", GP->nflushes); + if(!camera->setexp(GP->exptime)) + WARNX(_("Can't set exposure time to %f seconds"), GP->exptime); + tmpi = (GP->dark) ? 0 : 1; + if(!camera->setframetype(tmpi)) + WARNX(_("Can't change frame type")); + tmpi = (GP->_8bit) ? 0 : 1; + if(!camera->setbitdepth(tmpi)) + WARNX(_("Can't set bit depth")); + tmpi = (GP->fast) ? 1 : 0; + if(!camera->setfastspeed(tmpi)) + WARNX(_("Can't set readout speed")); + else if(GP->fast) verbose(1, _("Fast readout mode")); + if(!GP->outfile) verbose(1, _("Only show statistics")); + + uint16_t *img = MALLOC(uint16_t, raw_width * raw_height); + IMG ima = {.data = img, .w = raw_width, .h = raw_height}; + for(int j = 0; j < GP->nframes; ++j){ + verbose(1, "\n\n"); + // %d\n + verbose(1, _("Capture frame %d\n"), j); + capture_status cs; + float tleave = 1.; + while(camera->pollcapture(&cs, &tleave)){ + if(cs != CAPTURE_PROCESS) break; + verbose(2, _("%.1f seconds till exposition ends"), tleave); + if(camera->getTcold(&tmpf)) verbose(1, "CCDTEMP=%.1f", tmpf); + if(camera->getTbody(&tmpf)) verbose(1, "BODYTEMP=%.1f", tmpf); + if(tleave > 6.) sleep(5); + else if(tleave > 0.9) sleep((int)(tleave+0.99)); + else usleep((int)(1e6*tleave) + 99999); + } + if(cs != CAPTURE_READY){ + WARNX(_("Can't capture image")); + break; + } + verbose(2, _("Read grabbed image")); + if(!camera->capture(&ima)){ + WARNX(_("Can't grab image")); + break; + } + print_stat(&ima); + saveFITS(&ima, GP->outfile); +#ifdef IMAGEVIEW + if(GP->showimage){ // display image + if(!(mainwin = getWin())){ + DBG("Create new win"); + mainwin = createGLwin("Sample window", raw_width, raw_height, NULL); + if(!mainwin){ + WARNX(_("Can't open OpenGL window, image preview will be inaccessible")); + }else + pthread_create(&mainwin->thread, NULL, &image_thread, (void*)&ima); + } + if((mainwin = getWin())){ + DBG("change image"); + change_displayed_image(mainwin, &ima); + while((mainwin = getWin())){ // test paused state & grabbing custom frames + if((mainwin->winevt & WINEVT_PAUSE) == 0) break; + if(mainwin->winevt & WINEVT_GETIMAGE){ + mainwin->winevt &= ~WINEVT_GETIMAGE; + if(!camera->capture(&ima)){ + WARNX(_("Can't grab image")); + } + change_displayed_image(mainwin, &ima); + } + usleep(10000); + } + } + } +#endif + if(GP->pause_len && j != (GP->nframes - 1)){ + double delta, time1 = dtime() + GP->pause_len; + while((delta = time1 - dtime()) > 0.){ + // %d \n + verbose(1, _("%d seconds till pause ends\n"), (int)delta); + if(camera->getTcold(&tmpf)) verbose(1, "CCDTEMP=%.1f\n", tmpf); + if(camera->getTbody(&tmpf)) verbose(1, "BODYTEMP=%.1f\n", tmpf); + if(delta > 10) sleep(10); + else usleep((int)(delta*1e6 + 1)); + } + } + } +#ifdef IMAGEVIEW + if(GP->showimage){ + mainwin->winevt |= WINEVT_PAUSE; + DBG("Waiting"); + while((mainwin = getWin())){ + if(mainwin->killthread) break; + if(mainwin->winevt & WINEVT_GETIMAGE){ + DBG("GRAB"); + mainwin->winevt &= ~WINEVT_GETIMAGE; + if(!camera->capture(&ima)){ + WARNX(_("Can't grab image")); + } + change_displayed_image(mainwin, &ima); + } + } + DBG("Close window"); + usleep(10000); + } +#endif + FREE(img); +retn: + camera->close(); + camera = NULL; +} + +void cancel(){ + if(camera){ + camera->cancel(); + } + closeall(); +} diff --git a/ccdfunc.h b/ccdfunc.h new file mode 100644 index 0000000..bb2b4eb --- /dev/null +++ b/ccdfunc.h @@ -0,0 +1,140 @@ +/* + * 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 . + */ + +#pragma once +#ifndef CCDFUNC_H__ +#define CCDFUNC_H__ + +#include + +typedef struct{ + uint16_t *data; // image data + int w, h; // image size + uint16_t max, min; // min/max values + float avr, std; // statistics +} IMG; + +// format of single frame +typedef struct{ + int w; int h; // width & height + int xoff; int yoff; // X and Y offset +} frameformat; + +typedef enum{ + SHUTTER_OPEN, // open shutter now + SHUTTER_CLOSE, // close shutter now + SHUTTER_OPENATLOW, // ext. expose control @low + SHUTTER_OPENATHIGH, // -//- @high + SHUTTER_AMOUNT, // amount of entries +} shutter_op; + +typedef enum{ + CAPTURE_NO, // no capture initiated + CAPTURE_PROCESS, // in progress + CAPTURE_CANTSTART, // can't start + CAPTURE_ABORTED, // some error - aborted + CAPTURE_READY, // ready - user can read image +} capture_status; + +typedef enum{ + FAN_OFF, + FAN_LOW, + FAN_HIGH, +} fan_speed; + +// all setters and getters of Camera, Focuser and Wheel should return TRUE if success or FALSE if failed or unsupported +// camera +typedef struct{ + int (*check)(); // check if the device is available, connect and init + int Ndevices; // amount of devices found + void (*close)(); // disconnect & close device + int (*pollcapture)(capture_status *st, float *remain);// start or poll capture process, `remain` - time remain (s) + int (*capture)(IMG *ima); // capture an image, struct `ima` should be prepared before + void (*cancel)(); // cancel exposition + // setters: + int (*setDevNo)(int n); // set active device number + int (*setbrightness)(float b); + int (*setexp)(float e); + int (*setgain)(float g); + int (*setT)(float t); + int (*setbin)(int binh, int binv); // binning + int (*setnflushes)(int N); // flushes amount + int (*shuttercmd)(shutter_op s); // work with shutter + int (*confio)(int s); // configure IO-port + int (*setio)(int s); // set IO-port to given state + int (*setframetype)(int l); // set frametype: 1 - light, 0 - dark + int (*setbitdepth)(int h); // set bit depth: 1 - high, 0 - low + int (*setfastspeed)(int s); // set readout speed: 1 - fast, 0 - low + // geometry (if TRUE, all args are changed to suitable values) + int (*setgeometry)(frameformat *fmt); // set geometry in UNBINNED coordinates + int (*setfanspeed)(fan_speed spd); // set fan speed + // getters: + int (*getModelName)(char *n, int l);// string with model name (l - length of n in bytes) + int (*getmaxgain)(float *g);// get max available gain value + // get limits of geometry: maximal values and steps + int (*getgeomlimits)(frameformat *max, frameformat *step); + int (*getTcold)(float *t); // cold-side T + int (*getThot)(float *t); // hot-side T + int (*getTbody)(float *t); // body T + int (*getbin)(int *binh, int *binv); + int (*getio)(int *s); // get IO-port state + float pixX, pixY; // pixel size in um + frameformat field; // max field of view + frameformat array; // array format + frameformat geometry; // current geometry settings (as in setgeometry) +} Camera; + +// focuser +typedef struct{ + int (*check)(); // check if the device is available + int Ndevices; + void (*close)(); + // setters: + int (*setDevNo)(int n); // set active device number + int (*setAbsPos)(int async, float n);// set absolute position (in millimeters!!!) + int (*home)(int async); // home device + // getters: + int (*getModelName)(char *n, int l);// string with model name (l - length of n in bytes) + int (*getTbody)(float *t); // body T + int (*getPos)(float *p); // current position number (starting from zero) + int (*getMaxPos)(float *p); // max position + int (*getMinPos)(float *p); // min position +} Focuser; + +// wheel +typedef struct{ + int (*check)(); // check if the device is available + int Ndevices; + void (*close)(); + // setters: + int (*setDevNo)(int n); // set active device number + int (*setPos)(int n); // set absolute position (starting from 0) + // getters: + int (*getModelName)(char *n, int l);// string with model name (l - length of n in bytes) + int (*getTbody)(float *t); // body T + int (*getPos)(int *p); // current position number (starting from zero) + int (*getMaxPos)(int *p); // amount of positions +} Wheel; + +void saveFITS(IMG *img, char *filename); // for imageview module +void focusers(); +void wheels(); +void ccds(); +void cancel(); + +#endif // CCDFUNC_H__ diff --git a/cmdlnopts.c b/cmdlnopts.c new file mode 100644 index 0000000..907445e --- /dev/null +++ b/cmdlnopts.c @@ -0,0 +1,167 @@ +/* + * cmdlnopts.c - the only function that parse cmdln args and returns glob parameters + * + * Copyright 2013 Edward V. Emelianoff + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cmdlnopts.h" +#include "usefull_macros.h" + +#define RAD 57.2957795130823 +#define D2R(x) ((x) / RAD) +#define R2D(x) ((x) * RAD) + +/* + * here are global parameters initialisation + */ +static int help; +static glob_pars G; +glob_pars *GP = NULL; + +// DEFAULTS +// default global parameters +glob_pars const Gdefault = { + .objtype = "object", + .instrument = "direct imaging", + .exptime = -1, + .nframes = 1, + .hbin = 1, .vbin = 1, + .X0 = -1, .Y0 = -1, + .X1 = -1, .Y1 = -1, + .temperature = 1e6, + .shtr_cmd = -1, + .confio = -1, .setio = -1, + .gotopos = NAN, .addsteps = NAN, + .setwheel = -1, + .fanspeed = -1, + .nflushes = 1 +}; + +/* + * Define command line options by filling structure: + * name has_arg flag val type argptr help +*/ +myoption cmdlnopts[] = { + {"cameradev", NEED_ARG, NULL, 'C', arg_string, APTR(&G.cameradev), N_("camera device type (fli/zwo/etc)")}, + {"focuserdev", NEED_ARG,NULL, 'F', arg_string, APTR(&G.focuserdev),N_("focuser device type (fli/zwo/etc)")}, + {"wheeldev", NEED_ARG, NULL, 'W', arg_string, APTR(&G.wheeldev), N_("wheel device type (fli/zwo/etc)")}, + {"camdevno",NEED_ARG, NULL, 0, arg_int, APTR(&G.camdevno), N_("camera device number (if many: 0, 1, 2 etc)")}, + {"wheeldevno",NEED_ARG, NULL, 0, arg_int, APTR(&G.whldevno), N_("filter wheel device number (if many: 0, 1, 2 etc)")}, + {"focdevno",NEED_ARG, NULL, 0, arg_int, APTR(&G.focdevno), N_("focuser device number (if many: 0, 1, 2 etc)")}, + {"help", NO_ARGS, &help, 1, arg_none, NULL, N_("show this help")}, + {"rewrite", NO_ARGS, &G.rewrite,1, arg_none, NULL, N_("rewrite output file if exists")}, + {"verbose", NO_ARGS, NULL, 'V', arg_none, APTR(&G.verbose), N_("verbose level (each -v increase it)")}, + {"dark", NO_ARGS, NULL, 'd', arg_int, APTR(&G.dark), N_("not open shutter, when exposing (\"dark frames\")")}, + {"8bit", NO_ARGS, NULL, '8', arg_int, APTR(&G._8bit), N_("run in 8-bit mode")}, + {"fast", NO_ARGS, NULL, 'f', arg_int, APTR(&G.fast), N_("fast (8MHz) readout mode")}, + {"set-temp",NEED_ARG, NULL, 't', arg_double, APTR(&G.temperature),N_("set CCD temperature to given value (degr C)")}, + {"set-fan", NEED_ARG, NULL, 0, arg_int, APTR(&G.fanspeed), N_("set fan speed (0 - off, 1 - low, 2 - high)")}, + + {"author", NEED_ARG, NULL, 'A', arg_string, APTR(&G.author), N_("program author")}, + {"objtype", NEED_ARG, NULL, 'Y', arg_string, APTR(&G.objtype), N_("object type (neon, object, flat etc)")}, + {"instrument",NEED_ARG, NULL, 'I', arg_string, APTR(&G.instrument),N_("instrument name")}, + {"object", NEED_ARG, NULL, 'O', arg_string, APTR(&G.objname), N_("object name")}, + {"obsname", NEED_ARG, NULL, 'N', arg_string, APTR(&G.observers), N_("observers' names")}, + {"prog-id", NEED_ARG, NULL, 'P', arg_string, APTR(&G.prog_id), N_("observing program name")}, + {"addrec", MULT_PAR, NULL, 'r', arg_string, APTR(&G.addhdr), N_("add records to header from given file[s]")}, + + {"nflushes",NEED_ARG, NULL, 'l', arg_int, APTR(&G.nflushes), N_("N flushes before exposing (default: 1)")}, + {"hbin", NEED_ARG, NULL, 'h', arg_int, APTR(&G.hbin), N_("horizontal binning to N pixels")}, + {"vbin", NEED_ARG, NULL, 'v', arg_int, APTR(&G.vbin), N_("vertical binning to N pixels")}, + {"nframes", NEED_ARG, NULL, 'n', arg_int, APTR(&G.nframes), N_("make series of N frames")}, + {"pause", NEED_ARG, NULL, 'p', arg_int, APTR(&G.pause_len), N_("make pause for N seconds between expositions")}, + {"exptime", NEED_ARG, NULL, 'x', arg_float, APTR(&G.exptime), N_("set exposure time to given value (ms)")}, + {"X0", NEED_ARG, NULL, 0, arg_int, APTR(&G.X0), N_("frame X0 coordinate (-1 - all with overscan)")}, + {"Y0", NEED_ARG, NULL, 0, arg_int, APTR(&G.Y0), N_("frame Y0 coordinate (-1 - all with overscan)")}, + {"X1", NEED_ARG, NULL, 0, arg_int, APTR(&G.X1), N_("frame X1 coordinate (-1 - all with overscan)")}, + {"Y1", NEED_ARG, NULL, 0, arg_int, APTR(&G.Y1), N_("frame Y1 coordinate (-1 - all with overscan)")}, + {"fullframe",NO_ARGS, NULL, 0, arg_int, APTR(&G.fullframe), N_("grab full frame (with overscans)")}, + + {"open-shutter",NO_ARGS,&G.shtr_cmd, SHUTTER_OPEN,arg_none,NULL, N_("open shutter")}, + {"close-shutter",NO_ARGS,&G.shtr_cmd, SHUTTER_CLOSE,arg_none,NULL, N_("close shutter")}, + {"shutter-on-low",NO_ARGS,&G.shtr_cmd, SHUTTER_OPENATLOW,arg_none,NULL, N_("run exposition on LOW @ pin5 I/O port")}, + {"shutter-on-high",NO_ARGS,&G.shtr_cmd,SHUTTER_OPENATHIGH,arg_none,NULL,N_("run exposition on HIGH @ pin5 I/O port")}, + {"get-ioport",NO_ARGS, NULL, 'i', arg_int, APTR(&G.getio), N_("get value of I/O port pins")}, + {"async", NO_ARGS, &G.async,1, arg_none, NULL, N_("move stepper motor asynchronous")}, + + {"set-ioport",NEED_ARG, NULL, 's', arg_int, APTR(&G.setio), N_("set I/O port pins to given value (decimal number, pin1 is LSB)")}, + {"conf-ioport",NEED_ARG,NULL, 'c', arg_int, APTR(&G.confio), N_("configure I/O port pins to given value (decimal number, pin1 is LSB, 1 == output, 0 == input)")}, + + {"goto", NEED_ARG, NULL, 'g', arg_double, APTR(&G.gotopos), N_("move focuser to absolute position, mm")}, + {"addsteps",NEED_ARG, NULL, 'a', arg_double, APTR(&G.addsteps), N_("move focuser to relative position, mm")}, + + {"wheel-set",NEED_ARG, NULL, 'w', arg_int, APTR(&G.setwheel), N_("set wheel position")}, + +#ifdef IMAGEVIEW + {"display", NO_ARGS, NULL, 'D', arg_int, APTR(&G.showimage), N_("Display image in OpenGL window")}, +#endif + //{"", NEED_ARG, NULL, '', arg_double, APTR(&G.), N_("")}, + + end_option +}; + + +/** + * Parse command line options and return dynamically allocated structure + * to global parameters + * @param argc - copy of argc from main + * @param argv - copy of argv from main + * @return allocated structure with global parameters + */ +glob_pars *parse_args(int argc, char **argv){ + void *ptr; + ptr = memcpy(&G, &Gdefault, sizeof(G)); assert(ptr); + // format of help: "Usage: progname [args]\n" + change_helpstring("Usage: %s [args] \n\n\tWhere args are:\n"); + // parse arguments + parseargs(&argc, &argv, cmdlnopts); + if(help) showhelp(-1, cmdlnopts); + if(argc > 0){ + G.outfile = strdup(argv[0]); + if(argc > 1){ + WARNX("%d unused parameters:\n", argc - 1); + for(int i = 1; i < argc; ++i) + printf("\t%4d: %s\n", i, argv[i]); + } + } + GP = &G; + return GP; +} + +/** + * @brief verbose - print additional messages depending of G.verbose + * @param levl - message level + * @param fmt - message + */ +void verbose(int levl, const char *fmt, ...){ + va_list ar; + if(levl > G.verbose) return; + printf("%s: ", __progname); + va_start(ar, fmt); + vprintf(fmt, ar); + va_end(ar); + printf("\n"); +} diff --git a/cmdlnopts.h b/cmdlnopts.h new file mode 100644 index 0000000..71e3e03 --- /dev/null +++ b/cmdlnopts.h @@ -0,0 +1,81 @@ +/* + * cmdlnopts.h - comand line options for parceargs + * + * Copyright 2013 Edward V. Emelianoff + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#pragma once +#ifndef __CMDLNOPTS_H__ +#define __CMDLNOPTS_H__ + +#include "ccdfunc.h" +#include + +/* + * here are some typedef's for global data + */ +typedef struct{ + char *cameradev; // camera device ("fli", "zwo" etc) + char *focuserdev; // focuser ... + char *wheeldev; // wheel ... + char *objname; // object's name + char *outfile; // output filename prefix + char *objtype; // type of object (dark/obj/bias) + char *instrument; // instrument's name + char *observers; // observers' names + char *prog_id; // programm identificator + char *author; // programm author + int fanspeed; // fan speed: 0-2 + int noflush; // turn off bg flushing + int camdevno; // camera number (0, 1, 2 etc) + int focdevno; + int whldevno; + int dark; // dark frame + int nframes; // amount of frames to take + int hbin; int vbin; // binning + int X0; int Y0; // top left corner coordinate (-1 - all, including overscan) + int X1; int Y1; // bottom right corner coordinate + int fullframe; // grab full frame (with overscans) + int nflushes; // amount of flushes + int pause_len; // pause (in seconds) between expositions + int shtr_cmd; // shutter command (flishutter_t) + int _8bit; // 8bit mode + int fast; // fast (8MHz) readout mode + int getio; // get value of ioport + int setio; // set value of ioport + int confio; // configure ioport + float exptime; // time of exposition in seconds + double temperature; // temperature of CCD + double gotopos; // move stepper motor of focuser to absolute position + double addsteps; // move stepper motor of focuser to relative position + int setwheel; // set wheel position + int async; // asynchronous moving + int verbose; // each '-V' increases it + int rewrite; // rewrite file + int showimage; // show image preview + char **addhdr; // list of files from which to add header records +} glob_pars; + + +// default & global parameters +extern glob_pars const Gdefault; +extern glob_pars *GP; + +glob_pars *parse_args(int argc, char **argv); +void verbose(int levl, const char *fmt, ...); +#endif // __CMDLNOPTS_H__ diff --git a/events.c b/events.c new file mode 100644 index 0000000..2d413dc --- /dev/null +++ b/events.c @@ -0,0 +1,199 @@ +/* + * events.c + * + * Copyright 2015 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#include + +#include "events.h" +#include "imageview.h" + +/** + * manage pressed keys & menu items + */ +static void processKeybrd(unsigned char key, int mod, _U_ int x, _U_ int y){ + windowData *win = getWin(); + if(!win) return; + DBG("key=%d (%c), mod=%d", key, key, mod); + if(mod == GLUT_ACTIVE_CTRL){ // 'a' == 1, 'b' == 2... + key += 'a'-1; + DBG("CTRL+%c", key); + switch(key){ + case 'r': // roll colorfun + win->winevt |= WINEVT_ROLLCOLORFUN; + break; + case 's': // save image + win->winevt |= WINEVT_SAVEIMAGE; + break; + case 'q': // exit case 17: + win->killthread = 1; + break; + } + }else if(mod == GLUT_ACTIVE_ALT){ + ; // ALT + }else switch(key){ + case '0': // return zoom to 1 & image to 0 + win->zoom = 1; + win->x = 0; win->y = 0; + break; + case 27: // esc - kill + win->killthread = 1; + break; + case 'c': // capture in pause mode + DBG("winevt = %d", win->winevt); + if(win->winevt & WINEVT_PAUSE) + win->winevt |= WINEVT_GETIMAGE; + break; + case 'l': // flip left-right + win->flip ^= WIN_FLIP_LR; + break; + case 'p': // pause capturing + win->winevt ^= WINEVT_PAUSE; + break; + case 'u': // flip up-down + win->flip ^= WIN_FLIP_UD; + break; + case 'Z': // zoom+ + win->zoom *= 1.1f; + calc_win_props(NULL, NULL); + break; + case 'z': // zoom- + win->zoom /= 1.1f; + calc_win_props(NULL, NULL); + break; + } +} + +/* + * Process keyboard + */ +void keyPressed(unsigned char key, int x, int y){ + int mod = glutGetModifiers(); + //mod: GLUT_ACTIVE_SHIFT, GLUT_ACTIVE_CTRL, GLUT_ACTIVE_ALT; result is their sum + DBG("Key pressed. mod=%d, keycode=%d (%c), point=(%d,%d)\n", mod, key, key, x,y); + processKeybrd(key, mod, x, y); +} +/* +void keySpPressed(_U_ int key, _U_ int x, _U_ int y){ +// int mod = glutGetModifiers(); + DBG("Sp. key pressed. mod=%d, keycode=%d, point=(%d,%d)\n", glutGetModifiers(), key, x,y); +}*/ + +static int oldx, oldy; // coordinates when mouse was pressed +static int movingwin = 0; // ==1 when user moves image by middle button + +void mousePressed(int key, int state, int x, int y){ +// key: GLUT_LEFT_BUTTON, GLUT_MIDDLE_BUTTON, GLUT_RIGHT_BUTTON +// state: GLUT_UP, GLUT_DOWN + int mod = glutGetModifiers(); + windowData *win = getWin(); + if(!win) return; + if(state == GLUT_DOWN){ + oldx = x; oldy = y; + float X,Y, Zoom = win->zoom; + conv_mouse_to_image_coords(x,y,&X,&Y,win); + DBG("press in (%d, %d) == (%f, %f) on image; mod == %d", x,y,X,Y, mod); + if(key == GLUT_LEFT_BUTTON){ + DBG("win->x=%g, win->y=%g", win->x, win->y); + win->x += Zoom * (win->w/2.f - (float)x); + win->y -= Zoom * (win->h/2.f - (float)y); + }else if(key == GLUT_MIDDLE_BUTTON) movingwin = 1; + else if(key == 3){ // wheel UP + if(mod == 0) win->y += 10.f*win->zoom; // nothing pressed - scroll up + else if(mod == GLUT_ACTIVE_SHIFT) win->x -= 10.f*Zoom; // shift pressed - scroll left + else if(mod == GLUT_ACTIVE_CTRL) win->zoom *= 1.1f; // ctrl+wheel up == zoom+ + }else if(key == 4){ // wheel DOWN + if(mod == 0) win->y -= 10.f*win->zoom; // nothing pressed - scroll down + else if(mod == GLUT_ACTIVE_SHIFT) win->x += 10.f*Zoom; // shift pressed - scroll right + else if(mod == GLUT_ACTIVE_CTRL) win->zoom /= 1.1f; // ctrl+wheel down == zoom- + } + calc_win_props(NULL, NULL); + }else{ + movingwin = 0; + } +} + +void mouseMove(int x, int y){ + windowData *win = getWin(); + if(!win) return; + if(movingwin){ + float X, Y, nx, ny, w2, h2; + float a = win->Daspect; + X = (x - oldx) * a; Y = (y - oldy) * a; + nx = win->x + X; + ny = win->y - Y; + w2 = win->image->w / 2.f * win->zoom; + h2 = win->image->h / 2.f * win->zoom; + if(nx < w2 && nx > -w2) + win->x = nx; + if(ny < h2 && ny > -h2) + win->y = ny; + oldx = x; + oldy = y; + calc_win_props(NULL, NULL); + } +} + +void menuEvents(int opt){ + DBG("opt: %d, key: %d (%c), mod: %d", opt, opt&0xff, opt&0xff, opt>>8); + // just work as shortcut pressed + processKeybrd((unsigned char)(opt&0xff), opt>>8, 0, 0); +} // GLUT_ACTIVE_CTRL + +typedef struct{ + char *name; // menu entry name + int symbol; // shortcut symbol + rolled modifier +} menuentry; + +#define CTRL_K(key) ((key-'a'+1) | (GLUT_ACTIVE_CTRL<<8)) +#define SHIFT_K(key) (key | (GLUT_ACTIVE_SHIFT<<8)) +#define ALT_K(key) (key | (GLUT_ACTIVE_ALT<<8)) +static const menuentry entries[] = { + {"Capture in pause mode (c)", 'c'}, + {"Flip image LR (l)", 'l'}, + {"Flip image UD (u)", 'u'}, + {"Make a pause/continue (p)", 'p'}, + {"Restore zoom (0)", '0'}, + {"Roll colorfun (ctrl+r)", CTRL_K('r')}, + {"Save image (ctrl+s)", CTRL_K('s')}, + {"Close this window (ESC)", 27}, + {"Quit (ctrl+q)", CTRL_K('q')}, + {NULL, 0} +}; +#undef CTRL_K +#undef SHIFT_K +#undef ALT_K + +void createMenu(){ + FNAME(); + windowData *win = getWin(); + if(!win) return; + DBG("menu for win ID %d", win->ID); + glutSetWindow(win->ID); + if(win->menu) glutDestroyMenu(win->menu); + win->menu = glutCreateMenu(menuEvents); + const menuentry *ptr = entries; + while(ptr->name){ + glutAddMenuEntry(ptr->name, ptr->symbol); + ++ptr; + } + DBG("created menu %d\n", win->menu); + glutAttachMenu(GLUT_RIGHT_BUTTON); +} + diff --git a/events.h b/events.h new file mode 100644 index 0000000..c5a4c72 --- /dev/null +++ b/events.h @@ -0,0 +1,42 @@ +/* + * events.h + * + * Copyright 2015 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#pragma once +#ifndef __EVENTS_H__ +#define __EVENTS_H__ + +#include +#include +#include +#include +#include + +extern float Z; // Z (zoom) + +void keyPressed(unsigned char key, int x, int y); +//void keySpPressed(int key, int x, int y); +void mousePressed(int key, int state, int x, int y); +void mouseMove(int x, int y); +void createMenu(); +void menuEvents(int opt); +//void mouseWheel(int button, int dir, int x, int y); + +#endif // __EVENTS_H__ diff --git a/imageview.c b/imageview.c new file mode 100644 index 0000000..ddcea33 --- /dev/null +++ b/imageview.c @@ -0,0 +1,463 @@ +/* + * This file is part of the FLI_control project. + * Copyright 2020 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 // XInitThreads(); +#include // roundf(), log(), sqrt() +#include +#include +#include + +#include "cmdlnopts.h" +#include "imageview.h" + +static windowData *win = NULL; // main window +static pthread_t GLUTthread = 0; // main GLUT thread + +static int initialized = 0; // ==1 if GLUT is initialized; ==0 after clear_GL_context + +static void *Redraw(_U_ void *p); +static void createWindow(); +static void RedrawWindow(); +static void Resize(int width, int height); + +/** + * calculate window properties on creating & resizing + */ +void calc_win_props(GLfloat *Wortho, GLfloat *Hortho){ + if(!win || ! win->image) return; + float a, A, w, h, W, H; + float Zoom = win->zoom; + w = (float)win->image->w / 2.f; + h = (float)win->image->h / 2.f; + W = (float)win->w; + H =(float) win->h; + A = W / H; + a = w / h; + if(A > a){ // now W & H are parameters for glOrtho + win->Daspect = (float)h / H * 2.f; + W = h * A; H = h; + }else{ + win->Daspect = (float)w / W * 2.f; + H = w / A; W = w; + } + if(Wortho) *Wortho = W; + if(Hortho) *Hortho = H; + // calculate coordinates of center + win->x0 = W/Zoom - w + win->x / Zoom; + win->y0 = H/Zoom + h - win->y / Zoom; +} + +/** + * create window & run main loop + */ +static void createWindow(){ + DBG("ini=%d, win %s", initialized, win ? "yes" : "no"); + if(!initialized) return; + if(!win) return; + int w = win->w, h = win->h; + DBG("create window with title %s", win->title); + glutInitWindowSize(w, h); + win->ID = glutCreateWindow(win->title); + DBG("created GL_ID=%d", win->ID); + glutReshapeFunc(Resize); + glutDisplayFunc(RedrawWindow); + glutKeyboardFunc(keyPressed); + //glutSpecialFunc(keySpPressed); + //glutMouseWheelFunc(mouseWheel); + glutMouseFunc(mousePressed); + glutMotionFunc(mouseMove); + //glutIdleFunc(glutPostRedisplay); + glutIdleFunc(RedrawWindow); + DBG("init textures"); + glGenTextures(1, &(win->Tex)); + calc_win_props(NULL, NULL); + win->zoom = 1. / win->Daspect; + glEnable(GL_TEXTURE_2D); + // the hext 4 lines need to unaligned storage + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0); + glPixelStorei(GL_UNPACK_SKIP_ROWS, 0); + glBindTexture(GL_TEXTURE_2D, win->Tex); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, win->image->w, win->image->h, 0, + GL_RGB, GL_UNSIGNED_BYTE, win->image->rawdata); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST); + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + glDisable(GL_TEXTURE_2D); + createMenu(); + DBG("Window opened"); +} + +int killwindow(){ + if(!win) return 0; + if(!win->killthread){ + // say threads to die + win->killthread = 1; + } + pthread_mutex_lock(&win->mutex); + //pthread_join(win->thread, NULL); // wait while thread dies + if(win->menu) glutDestroyMenu(win->menu); + glutDestroyWindow(win->ID); + DBG("destroy menu, wundow & texture %d", win->Tex); + glDeleteTextures(1, &(win->Tex)); + glutLeaveMainLoop(); + DBG("Cancel"); + windowData *old = win; + win = NULL; + DBG("free(rawdata)"); + FREE(old->image->rawdata); + DBG("free(image)"); + FREE(old->image); + pthread_mutex_unlock(&old->mutex); + DBG("free(win)"); + FREE(old); + DBG("return"); + return 1; +} + +void renderBitmapString(float x, float y, void *font, char *string, GLubyte *color){ + if(!initialized) return; + char *c; + int x1=x, W=0; + for(c = string; *c; c++){ + W += glutBitmapWidth(font,*c);// + 1; + } + x1 -= W/2; + glColor3ubv(color); + glLoadIdentity(); + glTranslatef(0.,0., -150); + //glTranslatef(x,y, -4000.); + for (c = string; *c != '\0'; c++){ + glColor3ubv(color); + glRasterPos2f(x1,y); + glutBitmapCharacter(font, *c); + //glutStrokeCharacter(GLUT_STROKE_ROMAN, *c); + x1 = x1 + glutBitmapWidth(font,*c);// + 1; + } +} + +static void RedrawWindow(){ + //DBG("ini=%d, win=%s", initialized, win ? "yes" : "no"); + if(!initialized || !win || win->killthread) return; + if(pthread_mutex_trylock(&win->mutex)) return; + GLfloat w = win->image->w, h = win->image->h; + glutSetWindow(win->ID); + glClearColor(0.0, 0.0, 0.5, 1.0); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glLoadIdentity(); + glTranslatef(win->x, win->y, 0.); + glScalef(-win->zoom, -win->zoom, 1.); + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, win->Tex); + if(win->image->changed){ + DBG("Image changed!"); + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, win->image->w, win->image->h, 0, + GL_RGB, GL_UNSIGNED_BYTE, win->image->rawdata); + /* glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, win->image->w, win->image->h, + GL_RGB, GL_UNSIGNED_BYTE, win->image->rawdata);*/ + win->image->changed = 0; + } + w /= 2.f; h /= 2.f; + float lr = 1., ud = 1.; // flipping coefficients + if(win->flip & WIN_FLIP_LR) lr = -1.; + if(win->flip & WIN_FLIP_UD) ud = -1.; + glBegin(GL_QUADS); + glTexCoord2f(1.0f, 1.0f); glVertex2f( -1.f*lr*w, ud*h ); // top right + glTexCoord2f(1.0f, 0.0f); glVertex2f( -1.f*lr*w, -1.f*ud*h ); // bottom right + glTexCoord2f(0.0f, 0.0f); glVertex2f(lr*w, -1.f*ud*h ); // bottom left + glTexCoord2f(0.0f, 1.0f); glVertex2f(lr*w, ud*h ); // top left + glEnd(); + glDisable(GL_TEXTURE_2D); + glFinish(); + glutSwapBuffers(); + pthread_mutex_unlock(&win->mutex); + usleep(10000); +} + +/** + * main freeGLUT loop + * waits for global signals to create windows & make other actions + */ +static void *Redraw(_U_ void *arg){ + FNAME(); + createWindow(); + glutMainLoop(); + return NULL; +} + +static void Resize(int width, int height){ + FNAME(); + if(!initialized || !win || win->killthread) return; + glutReshapeWindow(width, height); + win->w = width; + win->h = height; + glViewport(0, 0, width, height); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + GLfloat W, H; + calc_win_props(&W, &H); + glOrtho(-W,W, -H,H, -1., 1.); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); +} + +/** + * create new window, run thread & return pointer to its structure or NULL + * asynchroneous call from outside + * wait for window creating & return its data + * @param title - header (copyed inside this function) + * @param w,h - image size + * @param rawdata - NULL (then the memory will be allocated here with size w x h) + * or allocated outside data + */ +windowData *createGLwin(char *title, int w, int h, rawimage *rawdata){ + FNAME(); + if(!initialized) imageview_init(); + if(win) killwindow(); + win = MALLOC(windowData, 1); + rawimage *raw; + if(rawdata){ + raw = rawdata; + }else{ + raw = MALLOC(rawimage, 1); + if(raw){ + raw->rawdata = MALLOC(GLubyte, w*h*3); + raw->w = w; + raw->h = h; + raw->changed = 1; + // raw->protected is zero automatically + } + } + if(!raw || !raw->rawdata){ + free(raw); + return NULL; + } + win->title = strdup(title); + win->image = raw; + if(pthread_mutex_init(&win->mutex, NULL)){ + WARN(_("Can't init mutex!")); + killwindow(); + return NULL; + } + win->w = w; + win->h = h; + pthread_create(&GLUTthread, NULL, &Redraw, NULL); + return win; +} + +/** + * Init freeGLUT + */ +void imageview_init(){ + FNAME(); + char *v[] = {"Image view window", NULL}; + int c = 1; + if(initialized){ + WARNX(_("Already initialized!")); + return; + } + XInitThreads(); // we need it for threaded windows + glutInit(&c, v); + glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH); + glutSetOption(GLUT_ACTION_ON_WINDOW_CLOSE, GLUT_ACTION_CONTINUE_EXECUTION); + initialized = 1; +} + +/** + * Close all opened windows and terminate main GLUT thread + */ +void clear_GL_context(){ + FNAME(); + if(!initialized) return; + initialized = 0; + DBG("kill"); + killwindow(); + DBG("join"); + if(GLUTthread) pthread_join(GLUTthread, NULL); // wait while main thread exits + DBG("main GL thread cancelled"); +} + + +/* + * Coordinates transformation from CS of drawingArea into CS of picture + * x,y - pointer coordinates + * X,Y - coordinates of appropriate point at picture + */ +void conv_mouse_to_image_coords(int x, int y, + float *X, float *Y, + windowData *window){ + float a = window->Daspect / window->zoom; + *X = (float)x * a - window->x0; + *Y = window->y0 - (float)y * a; +} + +void conv_image_to_mouse_coords(float X, float Y, + int *x, int *y, + windowData *window){ + float a = window->zoom / window->Daspect; + *x = (int)roundf((X + window->x0) * a); + *y = (int)roundf((window->y0 - Y) * a); +} + + +windowData *getWin(){ + return win; +} + +/** + * Convert gray (unsigned short) into RGB components (GLubyte) + * @argument L - gray level (0..1) + * @argument rgb - rgb array (GLubyte [3]) + */ +static void gray2rgb(double gray, GLubyte *rgb){ + int i = gray * 4.; + double x = (gray - (double)i * .25) * 4.; + GLubyte r = 0, g = 0, b = 0; + //r = g = b = (gray < 1) ? gray * 256 : 255; + switch(i){ + case 0: + g = (GLubyte)(255. * x); + b = 255; + break; + case 1: + g = 255; + b = (GLubyte)(255. * (1. - x)); + break; + case 2: + r = (GLubyte)(255. * x); + g = 255; + break; + case 3: + r = 255; + g = (GLubyte)(255. * (1. - x)); + break; + default: + r = 255; + } + *rgb++ = r; + *rgb++ = g; + *rgb = b; +} + +typedef enum{ + COLORFN_LINEAR, // linear + COLORFN_LOG, // ln + COLORFN_SQRT, // sqrt + COLORFN_MAX // end of list +} colorfn_type; + +static colorfn_type ft = COLORFN_LINEAR; + +// all colorfun's should get argument in [0, 1] and return in [0, 1] +static double linfun(double arg){ return arg; } // bung for PREVIEW_LINEAR +static double logfun(double arg){ return log(1.+arg) / 0.6931472; } // for PREVIEW_LOG [log_2(x+1)] +static double (*colorfun)(double) = linfun; // default function to convert color + +static void change_colorfun(colorfn_type f){ + DBG("New colorfn: %d", f); + switch (f){ + case COLORFN_LOG: + colorfun = logfun; + ft = COLORFN_LOG; + break; + case COLORFN_SQRT: + colorfun = sqrt; + ft = COLORFN_SQRT; + break; + default: // linear + colorfun = linfun; + ft = COLORFN_LINEAR; + } +} + +// cycle switch between palettes +static void roll_colorfun(){ + colorfn_type t = ++ft; + if(t == COLORFN_MAX) t = COLORFN_LINEAR; + change_colorfun(t); +} + +/** + * @brief equalize - hystogram equalization + * @param ori (io) - input/output data + * @param w,h - image width and height + * @return data allocated here + */ +static uint8_t *equalize(uint16_t *ori, int w, int h){ + uint8_t *retn = MALLOC(uint8_t, w*h); + double orig_hysto[0x10000] = {0.}; // original hystogram + uint8_t eq_levls[0x10000] = {0}; // levels to convert: newpix = eq_levls[oldpix] + int s = w*h; + for(int i = 0; i < s; ++i) ++orig_hysto[ori[i]]; + double part = (double)(s + 1) / 0x100, N = 0.; + for(int i = 0; i <= 0xffff; ++i){ + N += orig_hysto[i]; + eq_levls[i] = (uint8_t)(N/part); + } + + for(int i = 0; i < s; ++i){ + retn[i] = eq_levls[ori[i]]; + } + return retn; +} + +void change_displayed_image(windowData *win, IMG *img){ + if(!win || !win->image) return; + rawimage *im = win->image; + DBG("imh=%d, imw=%d, ch=%u, cw=%u", im->h, im->w, img->w, img->h); + pthread_mutex_lock(&win->mutex); + int w = img->w, h = img->h, s = w*h; + uint8_t *newima = equalize(img->data, w, h); + GLubyte *dst = im->rawdata; + for(int i = 0; i < s; ++i, dst += 3){ + gray2rgb(colorfun(newima[i] / 256.), dst); + } + FREE(newima); + win->image->changed = 1; + pthread_mutex_unlock(&win->mutex); +} + +void* image_thread(_U_ void *data){ + FNAME(); + IMG *img = (IMG*) data; + while(1){ + windowData *win = getWin(); + if(!win || win->killthread){ + DBG("got killthread"); + clear_GL_context(); + pthread_exit(NULL); + } + if(win && win->winevt){ + if(win->winevt & WINEVT_SAVEIMAGE){ // save image + verbose(2, "Make screenshot\n"); + saveFITS(img, "ScreenShot"); + win->winevt &= ~WINEVT_SAVEIMAGE; + } + if(win->winevt & WINEVT_ROLLCOLORFUN){ + roll_colorfun(); + win->winevt &= ~WINEVT_ROLLCOLORFUN; + change_displayed_image(win, img); + } + } + usleep(10000); + } +} diff --git a/imageview.h b/imageview.h new file mode 100644 index 0000000..e0deedf --- /dev/null +++ b/imageview.h @@ -0,0 +1,91 @@ +/* + * imageview.h + * + * Copyright 2015 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ +#pragma once +#ifndef __BMPVIEW_H__ +#define __BMPVIEW_H__ + +#include +#include +#include +#include + +#include "ccdfunc.h" +#include "events.h" + +typedef struct{ + GLubyte *rawdata; // raw image data + int w; // size of image + int h; + int changed; // == 1 if data was changed outside (to redraw) +} rawimage; + +// events from menu: +// temporaly stop capture of regular sequence +#define WINEVT_PAUSE (1<<0) +// capture one image in pause mode +#define WINEVT_GETIMAGE (1<<1) +// save current image +#define WINEVT_SAVEIMAGE (1<<2) +// change color palette function +#define WINEVT_ROLLCOLORFUN (1<<3) + +// flip image +#define WIN_FLIP_LR (1<<0) +#define WIN_FLIP_UD (1<<1) + +typedef struct{ + int ID; // identificator of OpenGL window + char *title; // title of window + GLuint Tex; // texture for image inside window + rawimage *image; // raw image data + int w; int h; // window size + float x; float y; // image offset coordinates + float x0; float y0; // center of window for coords conversion + float zoom; // zoom aspect + float Daspect; // aspect ratio between image & window sizes + int menu; // window menu identifier + uint32_t winevt; // window menu events + uint8_t flip; // flipping settings + pthread_t thread; // identificator of thread that changes window data + pthread_mutex_t mutex;// mutex for operations with image + int killthread; // flag for killing data changing thread & also signal that there's no threads +} windowData; + +typedef enum{ + INNER, + OPENGL +} winIdType; + +void imageview_init(); +windowData *createGLwin(char *title, int w, int h, rawimage *rawdata); +windowData *getWin(); +int killwindow(); +void renderBitmapString(float x, float y, void *font, char *string, GLubyte *color); +void clear_GL_context(); + +void calc_win_props(GLfloat *Wortho, GLfloat *Hortho); + +void conv_mouse_to_image_coords(int x, int y, float *X, float *Y, windowData *window); +void conv_image_to_mouse_coords(float X, float Y, int *x, int *y, windowData *window); + +void* image_thread(void *data); +void change_displayed_image(windowData *win, IMG *img); +#endif // __BMPVIEW_H__ diff --git a/locale/ru/LC_MESSAGES/ccd_capture.mo b/locale/ru/LC_MESSAGES/ccd_capture.mo new file mode 100644 index 0000000..1a0f90b Binary files /dev/null and b/locale/ru/LC_MESSAGES/ccd_capture.mo differ diff --git a/locale/ru/messages.po b/locale/ru/messages.po new file mode 100644 index 0000000..5349d3d --- /dev/null +++ b/locale/ru/messages.po @@ -0,0 +1,450 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2022-01-13 12:16+0300\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=koi8-r\n" +"Content-Transfer-Encoding: 8bit\n" + +#: cmdlnopts.c:68 +msgid "camera device type (fli/zwo/etc)" +msgstr "" + +#: cmdlnopts.c:69 +msgid "focuser device type (fli/zwo/etc)" +msgstr "" + +#: cmdlnopts.c:70 +msgid "wheel device type (fli/zwo/etc)" +msgstr "" + +#: cmdlnopts.c:71 +msgid "camera device number (if many: 0, 1, 2 etc)" +msgstr "" + +#: cmdlnopts.c:72 +msgid "filter wheel device number (if many: 0, 1, 2 etc)" +msgstr "" + +#: cmdlnopts.c:73 +msgid "focuser device number (if many: 0, 1, 2 etc)" +msgstr "" + +#: cmdlnopts.c:74 +msgid "show this help" +msgstr "" + +#: cmdlnopts.c:75 +msgid "rewrite output file if exists" +msgstr "" + +#: cmdlnopts.c:76 +msgid "verbose level (each -v increase it)" +msgstr "" + +#: cmdlnopts.c:77 +msgid "not open shutter, when exposing (\"dark frames\")" +msgstr "" + +#: cmdlnopts.c:78 +msgid "run in 8-bit mode" +msgstr "" + +#: cmdlnopts.c:79 +msgid "fast (8MHz) readout mode" +msgstr "" + +#: cmdlnopts.c:80 +msgid "set CCD temperature to given value (degr C)" +msgstr "" + +#: cmdlnopts.c:81 +msgid "set fan speed (0 - off, 1 - low, 2 - high)" +msgstr "" + +#: cmdlnopts.c:83 +msgid "program author" +msgstr "" + +#: cmdlnopts.c:84 +msgid "object type (neon, object, flat etc)" +msgstr "" + +#: cmdlnopts.c:85 +msgid "instrument name" +msgstr "" + +#: cmdlnopts.c:86 +msgid "object name" +msgstr "" + +#: cmdlnopts.c:87 +msgid "observers' names" +msgstr "" + +#: cmdlnopts.c:88 +msgid "observing program name" +msgstr "" + +#: cmdlnopts.c:89 +msgid "add records to header from given file[s]" +msgstr "" + +#: cmdlnopts.c:91 +msgid "N flushes before exposing (default: 1)" +msgstr "" + +#: cmdlnopts.c:92 +msgid "horizontal binning to N pixels" +msgstr "" + +#: cmdlnopts.c:93 +msgid "vertical binning to N pixels" +msgstr "" + +#: cmdlnopts.c:94 +msgid "make series of N frames" +msgstr "" + +#: cmdlnopts.c:95 +msgid "make pause for N seconds between expositions" +msgstr "" + +#: cmdlnopts.c:96 +msgid "set exposure time to given value (ms)" +msgstr "" + +#: cmdlnopts.c:97 +msgid "frame X0 coordinate (-1 - all with overscan)" +msgstr "" + +#: cmdlnopts.c:98 +msgid "frame Y0 coordinate (-1 - all with overscan)" +msgstr "" + +#: cmdlnopts.c:99 +msgid "frame X1 coordinate (-1 - all with overscan)" +msgstr "" + +#: cmdlnopts.c:100 +msgid "frame Y1 coordinate (-1 - all with overscan)" +msgstr "" + +#: cmdlnopts.c:101 +msgid "grab full frame (with overscans)" +msgstr "" + +#: cmdlnopts.c:103 +msgid "open shutter" +msgstr "" + +#: cmdlnopts.c:104 +msgid "close shutter" +msgstr "" + +#: cmdlnopts.c:105 +msgid "run exposition on LOW @ pin5 I/O port" +msgstr "" + +#: cmdlnopts.c:106 +msgid "run exposition on HIGH @ pin5 I/O port" +msgstr "" + +#: cmdlnopts.c:107 +msgid "get value of I/O port pins" +msgstr "" + +#: cmdlnopts.c:108 +msgid "move stepper motor asynchronous" +msgstr "" + +#: cmdlnopts.c:110 +msgid "set I/O port pins to given value (decimal number, pin1 is LSB)" +msgstr "" + +#: cmdlnopts.c:111 +msgid "" +"configure I/O port pins to given value (decimal number, pin1 is LSB, 1 == " +"output, 0 == input)" +msgstr "" + +#: cmdlnopts.c:113 +msgid "move focuser to absolute position, mm" +msgstr "" + +#: cmdlnopts.c:114 +msgid "move focuser to relative position, mm" +msgstr "" + +#: cmdlnopts.c:116 +msgid "set wheel position" +msgstr "" + +#: cmdlnopts.c:119 +msgid "Display image in OpenGL window" +msgstr "" + +#. Не могу сохранить файл +#: ccdfunc.c:115 +msgid "Can't save file" +msgstr "" + +#: ccdfunc.c:257 +#, c-format +msgid "File saved as '%s'" +msgstr "" + +#: ccdfunc.c:259 +msgid "Error saving file" +msgstr "" + +#. Статистика по изображению:\n +#: ccdfunc.c:279 +#, c-format +msgid "Image stat:\n" +msgstr "" + +#: ccdfunc.c:291 +msgid "Focuser device not pointed, try to guess" +msgstr "" + +#: ccdfunc.c:307 +msgid "Focuser not found" +msgstr "" + +#: ccdfunc.c:312 +#, c-format +msgid "Found %d focusers, you point number %d" +msgstr "" + +#: ccdfunc.c:316 +msgid "Can't set active focuser number" +msgstr "" + +#: ccdfunc.c:330 +msgid "Can't get focuser limit positions" +msgstr "" + +#: ccdfunc.c:337 +msgid "Can't get current focuser position" +msgstr "" + +#: ccdfunc.c:351 +#, c-format +msgid "Can't set position %g: out of limits [%g, %g]" +msgstr "" + +#: ccdfunc.c:355 +msgid "Can't home focuser" +msgstr "" + +#: ccdfunc.c:357 +#, c-format +msgid "Can't set position %g" +msgstr "" + +#: ccdfunc.c:369 +msgid "Wheel device not pointed, try to guess" +msgstr "" + +#: ccdfunc.c:385 +msgid "Wheel not found" +msgstr "" + +#: ccdfunc.c:390 +#, c-format +msgid "Found %d wheels, you point number %d" +msgstr "" + +#: ccdfunc.c:394 +msgid "Can't set active wheel number" +msgstr "" + +#: ccdfunc.c:406 +msgid "Can't get max wheel position" +msgstr "" + +#: ccdfunc.c:413 +#, c-format +msgid "Wheel position should be from 0 to %d" +msgstr "" + +#: ccdfunc.c:417 +#, c-format +msgid "Can't set wheel position %d" +msgstr "" + +#: ccdfunc.c:436 +msgid "Camera device not pointed, try to guess" +msgstr "" + +#: ccdfunc.c:452 +msgid "Camera device not found" +msgstr "" + +#: ccdfunc.c:457 +#, c-format +msgid "Found %d cameras, you point number %d" +msgstr "" + +#: ccdfunc.c:461 +msgid "Can't set active camera number" +msgstr "" + +#: ccdfunc.c:467 +msgid "Can't set fan speed" +msgstr "" + +#: ccdfunc.c:472 +#, c-format +msgid "Camera model: %s" +msgstr "" + +#: ccdfunc.c:473 +#, c-format +msgid "Pixel size: %g x %g" +msgstr "" + +#: ccdfunc.c:479 +#, c-format +msgid "Full array: %s" +msgstr "" + +#: ccdfunc.c:482 +#, c-format +msgid "Field of view: %s" +msgstr "" + +#: ccdfunc.c:485 +#, c-format +msgid "Can't set T to %g degC" +msgstr "" + +#: ccdfunc.c:492 +#, c-format +msgid "Shutter command: %s\n" +msgstr "" + +#: ccdfunc.c:494 +#, c-format +msgid "Can't run shutter command %s (unsupported?)" +msgstr "" + +#. "Попытка сконфигурировать порт I/O как %d\n" +#: ccdfunc.c:498 +#, c-format +msgid "Try to convfigure I/O port as %d" +msgstr "" + +#: ccdfunc.c:500 +msgid "Can't configure (unsupported?)" +msgstr "" + +#: ccdfunc.c:506 +msgid "Can't get IOport state (unsupported?)" +msgstr "" + +#. "Попытка записи %d в порт I/O\n" +#: ccdfunc.c:510 +#, c-format +msgid "Try to write %d to I/O port" +msgstr "" + +#: ccdfunc.c:512 +msgid "Can't set IOport" +msgstr "" + +#: ccdfunc.c:524 +#, c-format +msgid "Can't set binning %dx%d" +msgstr "" + +#: ccdfunc.c:526 +msgid "Can't get current binning" +msgstr "" + +#: ccdfunc.c:541 +msgid "Can't set given geometry" +msgstr "" + +#: ccdfunc.c:544 +#, c-format +msgid "Can't set %d flushes" +msgstr "" + +#: ccdfunc.c:547 +#, c-format +msgid "Can't set exposure time to %f seconds" +msgstr "" + +#: ccdfunc.c:550 +msgid "Can't change frame type" +msgstr "" + +#: ccdfunc.c:553 +msgid "Can't set bit depth" +msgstr "" + +#: ccdfunc.c:556 +msgid "Can't set readout speed" +msgstr "" + +#: ccdfunc.c:557 +msgid "Fast readout mode" +msgstr "" + +#: ccdfunc.c:558 +msgid "Only show statistics" +msgstr "" + +#. Захват кадра %d\n +#: ccdfunc.c:565 +#, c-format +msgid "Capture frame %d\n" +msgstr "" + +#: ccdfunc.c:570 +#, c-format +msgid "%.1f seconds till exposition ends" +msgstr "" + +#: ccdfunc.c:578 +msgid "Can't capture image" +msgstr "" + +#: ccdfunc.c:581 +msgid "Read grabbed image" +msgstr "" + +#: ccdfunc.c:583 ccdfunc.c:606 ccdfunc.c:637 +msgid "Can't grab image" +msgstr "" + +#: ccdfunc.c:594 +msgid "Can't open OpenGL window, image preview will be inaccessible" +msgstr "" + +#. %d секунд до окончания паузы\n +#: ccdfunc.c:619 +#, c-format +msgid "%d seconds till pause ends\n" +msgstr "" + +#: imageview.c:257 +msgid "Can't init mutex!" +msgstr "" + +#: imageview.c:275 +msgid "Already initialized!" +msgstr "" diff --git a/locale/ru/ru.po b/locale/ru/ru.po new file mode 100644 index 0000000..4bf3555 --- /dev/null +++ b/locale/ru/ru.po @@ -0,0 +1,448 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "Project-Id-Version: PACKAGE VERSION\n" + "Report-Msgid-Bugs-To: \n" + "POT-Creation-Date: 2022-01-13 12:15+0300\n" + "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" + "Last-Translator: FULL NAME \n" + "Language-Team: LANGUAGE \n" + "Language: \n" + "MIME-Version: 1.0\n" + "Content-Type: text/plain; charset=koi8-r\n" + "Content-Transfer-Encoding: 8bit\n" + +#: ccdfunc.c:570 +#, c-format +msgid "%.1f seconds till exposition ends" +msgstr "" + +#. %d секунд до окончания паузы\n +#: ccdfunc.c:619 +#, c-format +msgid "%d seconds till pause ends\n" +msgstr "" + +#: imageview.c:275 +msgid "Already initialized!" +msgstr "" + +#: ccdfunc.c:452 +msgid "Camera device not found" +msgstr "" + +#: ccdfunc.c:436 +msgid "Camera device not pointed, try to guess" +msgstr "" + +#: ccdfunc.c:472 +#, c-format +msgid "Camera model: %s" +msgstr "" + +#: ccdfunc.c:578 +msgid "Can't capture image" +msgstr "" + +#: ccdfunc.c:550 +msgid "Can't change frame type" +msgstr "" + +#: ccdfunc.c:500 +msgid "Can't configure (unsupported?)" +msgstr "" + +#: ccdfunc.c:506 +msgid "Can't get IOport state (unsupported?)" +msgstr "" + +#: ccdfunc.c:526 +msgid "Can't get current binning" +msgstr "" + +#: ccdfunc.c:337 +msgid "Can't get current focuser position" +msgstr "" + +#: ccdfunc.c:330 +msgid "Can't get focuser limit positions" +msgstr "" + +#: ccdfunc.c:406 +msgid "Can't get max wheel position" +msgstr "" + +#: ccdfunc.c:583 ccdfunc.c:606 ccdfunc.c:637 +msgid "Can't grab image" +msgstr "" + +#: ccdfunc.c:355 +msgid "Can't home focuser" +msgstr "" + +#: imageview.c:257 +msgid "Can't init mutex!" +msgstr "" + +#: ccdfunc.c:594 +msgid "Can't open OpenGL window, image preview will be inaccessible" +msgstr "" + +#: ccdfunc.c:494 +#, c-format +msgid "Can't run shutter command %s (unsupported?)" +msgstr "" + +#. Не могу сохранить файл +#: ccdfunc.c:115 +msgid "Can't save file" +msgstr "" + +#: ccdfunc.c:544 +#, c-format +msgid "Can't set %d flushes" +msgstr "" + +#: ccdfunc.c:512 +msgid "Can't set IOport" +msgstr "" + +#: ccdfunc.c:485 +#, c-format +msgid "Can't set T to %g degC" +msgstr "" + +#: ccdfunc.c:461 +msgid "Can't set active camera number" +msgstr "" + +#: ccdfunc.c:316 +msgid "Can't set active focuser number" +msgstr "" + +#: ccdfunc.c:394 +msgid "Can't set active wheel number" +msgstr "" + +#: ccdfunc.c:524 +#, c-format +msgid "Can't set binning %dx%d" +msgstr "" + +#: ccdfunc.c:553 +msgid "Can't set bit depth" +msgstr "" + +#: ccdfunc.c:547 +#, c-format +msgid "Can't set exposure time to %f seconds" +msgstr "" + +#: ccdfunc.c:467 +msgid "Can't set fan speed" +msgstr "" + +#: ccdfunc.c:541 +msgid "Can't set given geometry" +msgstr "" + +#: ccdfunc.c:357 +#, c-format +msgid "Can't set position %g" +msgstr "" + +#: ccdfunc.c:351 +#, c-format +msgid "Can't set position %g: out of limits [%g, %g]" +msgstr "" + +#: ccdfunc.c:556 +msgid "Can't set readout speed" +msgstr "" + +#: ccdfunc.c:417 +#, c-format +msgid "Can't set wheel position %d" +msgstr "" + +#. Захват кадра %d\n +#: ccdfunc.c:565 +#, c-format +msgid "Capture frame %d\n" +msgstr "" + +#: cmdlnopts.c:119 +msgid "Display image in OpenGL window" +msgstr "" + +#: ccdfunc.c:259 +msgid "Error saving file" +msgstr "" + +#: ccdfunc.c:557 +msgid "Fast readout mode" +msgstr "" + +#: ccdfunc.c:482 +#, c-format +msgid "Field of view: %s" +msgstr "" + +#: ccdfunc.c:257 +#, c-format +msgid "File saved as '%s'" +msgstr "" + +#: ccdfunc.c:291 +msgid "Focuser device not pointed, try to guess" +msgstr "" + +#: ccdfunc.c:307 +msgid "Focuser not found" +msgstr "" + +#: ccdfunc.c:457 +#, c-format +msgid "Found %d cameras, you point number %d" +msgstr "" + +#: ccdfunc.c:312 +#, c-format +msgid "Found %d focusers, you point number %d" +msgstr "" + +#: ccdfunc.c:390 +#, c-format +msgid "Found %d wheels, you point number %d" +msgstr "" + +#: ccdfunc.c:479 +#, c-format +msgid "Full array: %s" +msgstr "" + +#. Статистика по изображению:\n +#: ccdfunc.c:279 +#, c-format +msgid "Image stat:\n" +msgstr "" + +#: cmdlnopts.c:91 +msgid "N flushes before exposing (default: 1)" +msgstr "" + +#: ccdfunc.c:558 +msgid "Only show statistics" +msgstr "" + +#: ccdfunc.c:473 +#, c-format +msgid "Pixel size: %g x %g" +msgstr "" + +#: ccdfunc.c:581 +msgid "Read grabbed image" +msgstr "" + +#: ccdfunc.c:492 +#, c-format +msgid "Shutter command: %s\n" +msgstr "" + +#. "Попытка сконфигурировать порт I/O как %d\n" +#: ccdfunc.c:498 +#, c-format +msgid "Try to convfigure I/O port as %d" +msgstr "" + +#. "Попытка записи %d в порт I/O\n" +#: ccdfunc.c:510 +#, c-format +msgid "Try to write %d to I/O port" +msgstr "" + +#: ccdfunc.c:369 +msgid "Wheel device not pointed, try to guess" +msgstr "" + +#: ccdfunc.c:385 +msgid "Wheel not found" +msgstr "" + +#: ccdfunc.c:413 +#, c-format +msgid "Wheel position should be from 0 to %d" +msgstr "" + +#: cmdlnopts.c:89 +msgid "add records to header from given file[s]" +msgstr "" + +#: cmdlnopts.c:71 +msgid "camera device number (if many: 0, 1, 2 etc)" +msgstr "" + +#: cmdlnopts.c:68 +msgid "camera device type (fli/zwo/etc)" +msgstr "" + +#: cmdlnopts.c:104 +msgid "close shutter" +msgstr "" + +#: cmdlnopts.c:111 +msgid "configure I/O port pins to given value (decimal number, pin1 is LSB, " + "1 == output, 0 == input)" +msgstr "" + +#: cmdlnopts.c:79 +msgid "fast (8MHz) readout mode" +msgstr "" + +#: cmdlnopts.c:72 +msgid "filter wheel device number (if many: 0, 1, 2 etc)" +msgstr "" + +#: cmdlnopts.c:73 +msgid "focuser device number (if many: 0, 1, 2 etc)" +msgstr "" + +#: cmdlnopts.c:69 +msgid "focuser device type (fli/zwo/etc)" +msgstr "" + +#: cmdlnopts.c:97 +msgid "frame X0 coordinate (-1 - all with overscan)" +msgstr "" + +#: cmdlnopts.c:99 +msgid "frame X1 coordinate (-1 - all with overscan)" +msgstr "" + +#: cmdlnopts.c:98 +msgid "frame Y0 coordinate (-1 - all with overscan)" +msgstr "" + +#: cmdlnopts.c:100 +msgid "frame Y1 coordinate (-1 - all with overscan)" +msgstr "" + +#: cmdlnopts.c:107 +msgid "get value of I/O port pins" +msgstr "" + +#: cmdlnopts.c:101 +msgid "grab full frame (with overscans)" +msgstr "" + +#: cmdlnopts.c:92 +msgid "horizontal binning to N pixels" +msgstr "" + +#: cmdlnopts.c:85 +msgid "instrument name" +msgstr "" + +#: cmdlnopts.c:95 +msgid "make pause for N seconds between expositions" +msgstr "" + +#: cmdlnopts.c:94 +msgid "make series of N frames" +msgstr "" + +#: cmdlnopts.c:113 +msgid "move focuser to absolute position, mm" +msgstr "" + +#: cmdlnopts.c:114 +msgid "move focuser to relative position, mm" +msgstr "" + +#: cmdlnopts.c:108 +msgid "move stepper motor asynchronous" +msgstr "" + +#: cmdlnopts.c:77 +msgid "not open shutter, when exposing (\"dark frames\")" +msgstr "" + +#: cmdlnopts.c:86 +msgid "object name" +msgstr "" + +#: cmdlnopts.c:84 +msgid "object type (neon, object, flat etc)" +msgstr "" + +#: cmdlnopts.c:87 +msgid "observers' names" +msgstr "" + +#: cmdlnopts.c:88 +msgid "observing program name" +msgstr "" + +#: cmdlnopts.c:103 +msgid "open shutter" +msgstr "" + +#: cmdlnopts.c:83 +msgid "program author" +msgstr "" + +#: cmdlnopts.c:75 +msgid "rewrite output file if exists" +msgstr "" + +#: cmdlnopts.c:106 +msgid "run exposition on HIGH @ pin5 I/O port" +msgstr "" + +#: cmdlnopts.c:105 +msgid "run exposition on LOW @ pin5 I/O port" +msgstr "" + +#: cmdlnopts.c:78 +msgid "run in 8-bit mode" +msgstr "" + +#: cmdlnopts.c:80 +msgid "set CCD temperature to given value (degr C)" +msgstr "" + +#: cmdlnopts.c:110 +msgid "set I/O port pins to given value (decimal number, pin1 is LSB)" +msgstr "" + +#: cmdlnopts.c:96 +msgid "set exposure time to given value (ms)" +msgstr "" + +#: cmdlnopts.c:81 +msgid "set fan speed (0 - off, 1 - low, 2 - high)" +msgstr "" + +#: cmdlnopts.c:116 +msgid "set wheel position" +msgstr "" + +#: cmdlnopts.c:74 +msgid "show this help" +msgstr "" + +#: cmdlnopts.c:76 +msgid "verbose level (each -v increase it)" +msgstr "" + +#: cmdlnopts.c:93 +msgid "vertical binning to N pixels" +msgstr "" + +#: cmdlnopts.c:70 +msgid "wheel device type (fli/zwo/etc)" +msgstr "" diff --git a/main.c b/main.c new file mode 100644 index 0000000..e2f03f3 --- /dev/null +++ b/main.c @@ -0,0 +1,58 @@ +/* + * geany_encoding=koi8-r + * main.c + * + * Copyright 2017 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cmdlnopts.h" +#include "ccdfunc.h" +#ifdef IMAGEVIEW +#include "imageview.h" +#endif + +void signals(int signo){ + cancel(); + exit(signo); +} + +extern const char *__progname; + +int main(int argc, char **argv){ + initial_setup(); + parse_args(argc, argv); + signal(SIGINT, signals); + signal(SIGQUIT, signals); + signal(SIGABRT, signals); + signal(SIGTERM, signals); + focusers(); + wheels(); + ccds(); + return 0; +} +