From 838c9567c316b75f353b8a544e5bc4d53d2f96c0 Mon Sep 17 00:00:00 2001 From: Edward Emelianov Date: Thu, 13 Jan 2022 12:32:11 +0300 Subject: [PATCH] pre-alpha (works only with FLI) --- .gitignore | 46 +- CMakeLists.txt | 129 +++++ FLI_cameras/CMakeLists.txt | 24 + FLI_cameras/flifunc.c | 712 +++++++++++++++++++++++++++ FLI_cameras/flifunc.h | 29 ++ FindCFITSIO.cmake | 67 +++ ZWO_cameras/CMakeLists.txt | 23 + ZWO_cameras/zwofunc.c | 23 + ZWO_cameras/zwofunc.h | 29 ++ ccdfunc.c | 657 ++++++++++++++++++++++++ ccdfunc.h | 140 ++++++ cmdlnopts.c | 167 +++++++ cmdlnopts.h | 81 +++ events.c | 199 ++++++++ events.h | 42 ++ imageview.c | 463 +++++++++++++++++ imageview.h | 91 ++++ locale/ru/LC_MESSAGES/ccd_capture.mo | Bin 0 -> 338 bytes locale/ru/messages.po | 450 +++++++++++++++++ locale/ru/ru.po | 448 +++++++++++++++++ main.c | 58 +++ 21 files changed, 3843 insertions(+), 35 deletions(-) create mode 100644 CMakeLists.txt create mode 100644 FLI_cameras/CMakeLists.txt create mode 100644 FLI_cameras/flifunc.c create mode 100644 FLI_cameras/flifunc.h create mode 100644 FindCFITSIO.cmake create mode 100644 ZWO_cameras/CMakeLists.txt create mode 100644 ZWO_cameras/zwofunc.c create mode 100644 ZWO_cameras/zwofunc.h create mode 100644 ccdfunc.c create mode 100644 ccdfunc.h create mode 100644 cmdlnopts.c create mode 100644 cmdlnopts.h create mode 100644 events.c create mode 100644 events.h create mode 100644 imageview.c create mode 100644 imageview.h create mode 100644 locale/ru/LC_MESSAGES/ccd_capture.mo create mode 100644 locale/ru/messages.po create mode 100644 locale/ru/ru.po create mode 100644 main.c 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 0000000000000000000000000000000000000000..1a0f90bc3bd6b71004d9bc78f07d5888f22994f8 GIT binary patch literal 338 zcmYL@u};G<5Qd9j%E-jP;DHUUz!pqFHH~Q_?4(N4O0b#41VgYZ$3^fUydKZOWud?H z>4y7H_x&Co{ERSr$T4z)>?3E$kQh0_QCi$i6iV+-=()Do07Q%%RYqX1IYw1x?8#X9dU{5=Is|mFekIp7BViK0xJ_ z?K>5mr{sPm1j(4>WWps&#grvU!OQX%tG3;$Rzs!L2PJ}K^VMK75n|ezvGeV&g9b8{ ze5ZXmzKCYd25s@(_Xd4}{tD-tP8oYm>R07^9d6#8iNWuRgV&4pz-{dsV_Qn%XA`2) EA0#SVegFUf literal 0 HcmV?d00001 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; +} +