From 64a2ec11a05bb6e324dd4f09d224ad8406cce53d Mon Sep 17 00:00:00 2001 From: Edward Emelianov Date: Mon, 6 Mar 2023 17:36:41 +0300 Subject: [PATCH] Added Apogee support --- APOGEE_cameras/CMakeLists.txt | 14 ++ APOGEE_cameras/apogee.c | 338 ++++++++++++++++++++++++++++++++++ CMakeLists.txt | 5 +- Readme.md | 4 +- locale/ru/messages.po | 10 +- locale/ru/ru.po | 10 +- server.c | 2 +- socket.c | 1 + 8 files changed, 371 insertions(+), 13 deletions(-) create mode 100644 APOGEE_cameras/CMakeLists.txt create mode 100644 APOGEE_cameras/apogee.c diff --git a/APOGEE_cameras/CMakeLists.txt b/APOGEE_cameras/CMakeLists.txt new file mode 100644 index 0000000..6727542 --- /dev/null +++ b/APOGEE_cameras/CMakeLists.txt @@ -0,0 +1,14 @@ +cmake_minimum_required(VERSION 3.20) +set(CCDLIB devapogee) + +SET(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}) +find_package(PkgConfig REQUIRED) +pkg_check_modules(${CCDLIB} REQUIRED usefull_macros apogeec>=1.71 libusb) + +aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR} SRC) +include_directories(${${CCDLIB}_INCLUDE_DIRS} ..) +link_directories(${${CCDLIB}_LIBRARY_DIRS}) + +add_library(${CCDLIB} SHARED ${SRC}) +target_link_libraries(${CCDLIB} ${${CCDLIB}_LIBRARIES} -fPIC) +install(TARGETS ${CCDLIB} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}) diff --git a/APOGEE_cameras/apogee.c b/APOGEE_cameras/apogee.c new file mode 100644 index 0000000..8fe1d51 --- /dev/null +++ b/APOGEE_cameras/apogee.c @@ -0,0 +1,338 @@ +/* + * This file is part of the CCD_Capture project. + * Copyright 2023 Edward V. Emelianov . + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include "basestructs.h" +//#include "omp.h" + + +extern Camera camera; +static int ncameras = 0; +static int isopened = FALSE; +static int osw = 0; // overscan width +static int hbin = 1, vbin = 1; +static int is16bit = 1; +static int isobject = 0; +static int maxbinv = 0, maxbinh = 0; // max binning +static char camname[BUFSIZ] = {0}; +static double expt[2] = {0.}; // min/max exposition time +static double exptime = 0.; // actual exposition time +static double tstart = 0.; // exposure start time +static char whynot[BUFSIZ]; // temporary buffer for error messages +static int imW = 0, imH = 0; // size of output image +static int pid = -1, vid = -1; +static int isexposuring = 0; + +static void disconnect(){ + FNAME(); + if(!isopened) return; + ApnGlueExpAbort(); + ApnGlueClose(); + isopened = FALSE; +} + +static void cancel(){ + //if(!isexposuring) return; + FNAME(); + ApnGlueReset(); + //ApnGlueExpAbort(); + //ApnGlueStopExposure(); + DBG("OK"); +} + +static int ndev(){ + ncameras = 1; + if(ApnGlueOpen(ncameras)) ncameras = 0; + else ApnGlueClose(); + DBG("Found %d cameras", ncameras); + camera.Ndevices = ncameras; + return ncameras; +} +/* +static void reset_usb_port(){ + if(vid < 0 || pid < 0) return; + int fd, rc; + char buf[FILENAME_MAX*3], *d = NULL, *f = NULL; + struct usb_bus *bus; + struct usb_device *dev; + int found = 0; + usb_init(); + usb_find_busses(); + usb_find_devices(); + for(bus = usb_busses; bus && !found; bus = bus->next) { + for(dev = bus->devices; dev && !found; dev = dev->next) { + if (dev->descriptor.idVendor == vid && dev->descriptor.idProduct == pid){ + found = 1; + d = bus->dirname; + f = dev->filename; + } + } + } + if(!found){ + ERR(_("Device not found")); + return; + } + DBG("found camera device, reseting"); + snprintf(buf, sizeof(buf), "/dev/bus/usb/%s/%s", d,f); + fd = open(buf, O_WRONLY); + if(fd < 0){ + ERR("Can't open device file %s: %s", buf, strerror(errno)); + return; + } + WARNX("Resetting USB device %s", buf); + rc = ioctl(fd, USBDEVFS_RESET, 0); + if(rc < 0){ + perror("Error in ioctl"); + return; + } + close(fd); +}*/ + +static int setdevno(int n){ + FNAME(); + if(n > ncameras - 1) return FALSE; + if(ApnGlueOpen(n)) return FALSE; + ApnGlueExpAbort(); + ApnGluePowerResume(); + ApnGlueReset(); + char *msg = ApnGlueGetInfo(&pid, &vid); + DBG("CAMERA msg:\n%s\n", msg); + char *f = strstr(msg, "Model: "); + if(f){ + f += strlen("Model: "); + char *e = strchr(f, '\n'); + size_t l = (e) ? (size_t)(e-f) : strlen(f); + if(l >= BUFSIZ) l = BUFSIZ - 1; + snprintf(camname, l, "%s", f); + } + ApnGlueGetMaxValues(expt, &camera.array.w, &camera.array.h, &osw, NULL, &maxbinh, &maxbinv, NULL, NULL); + DBG("MAX format: W/H: %d/%d; osw=%d, binh/v=%d/%d", camera.array.w, camera.array.h, osw, maxbinh, maxbinv); + double x, y; + ApnGlueGetGeom(&x, &y); + camera.pixX = x, camera.pixY = y; + camera.field.w = camera.array.w - osw; + camera.field.h = camera.array.h; + DBG("Pixel size W/H: %g/%g; field w/h: %d/%d", x, y, camera.field.w, camera.field.h); + ; + return TRUE; +} + +static int modelname(char *buf, int bufsz){ + strncpy(buf, camname, bufsz); + return TRUE; +} + +static int shutter(shutter_op cmd){ + int op = (cmd == SHUTTER_OPEN) ? 1 : 0; + ApnGlueOpenShutter(op); + return TRUE; +} + +static int geometrylimits(frameformat *l, frameformat *s){ + if(l) *l = camera.array; + if(s) *s = (frameformat){.w = 1, .h = 1, .xoff = 1, .yoff = 1}; + return TRUE; +} + +static int sett(float t){ + ApnGlueSetTemp(t); + return TRUE; +} + +static int setfanspd(fan_speed s){ + ApnGlueSetFan((int) s); + return TRUE; +} + +static int preflash(int n){ + if(n > 0) n = 1; else n = 0; + ApnGluePreFlash(n); + return TRUE; +} + +// 0 - 12 bit, 1 - 16bit +static int setbitdepth(int i){ + DBG("set bit depth %d", i); + Apn_Resolution res = (i) ? Apn_Resolution_SixteenBit : Apn_Resolution_TwelveBit; + ApnGlueSetDatabits(res); + is16bit = i; + return TRUE; +} + +static int setfastspeed(int fast){ + DBG("set fast speed %d", fast); + unsigned short spd = (fast) ? AdcSpeed_Fast : AdcSpeed_Normal; + ApnGlueSetSpeed(spd); + return TRUE; +} + +static int setgeometry(frameformat *f){ + if(!f) return FALSE; + int ow = (f->w > camera.field.w) ? camera.array.w - f->w : f->w; + if(ow < 0) ow = 0; + if(ApnGlueSetExpGeom(f->w * hbin, f->h * vbin, ow, 0, hbin, vbin, + f->xoff, f->yoff, &imW, &imH, whynot)){ + WARNX("Can't set geometry: %s", whynot); + }else{ + camera.geometry = *f; + } + return TRUE; +} + +static int setbin(int binh, int binv){ + DBG("set bin v/h: %d/%d", binv, binh); + if(binh > maxbinh || binv > maxbinv) return FALSE; + hbin = binh; vbin = binv; + return TRUE; +} + +static int tcold(float *t){ + if(!t) return FALSE; + double dt; + ApnGlueGetTemp(&dt); + *t = dt; + return TRUE; +} + +static int thot(float *t){ + if(t) *t = ApnGlueGetHotTemp(); + return TRUE; +} + +static int startexp(){ + tstart = dtime(); + DBG("Start exposition"); + CCDerr r = ApnGlueStartExp(&exptime, isobject); + if(ALTA_OK != r){ + /*reset_usb_port(); + r = ApnGlueStartExp(&exptime, isobject); + if(ALTA_OK != r){*/ + ApnGlueReset(); + DBG("Error starting exp: %d", (int)r); + return FALSE; + //} + } + isexposuring = 1; + return TRUE; +} + +static int frametype(int islight){ + DBG("set frame type %d", islight); + isobject = islight; + return TRUE; +} + +static int setexp(float t){ + DBG("start exp %g, min: %g, max: %g", t, expt[0], expt[1]); + if(t < expt[0] || t > expt[1]) return FALSE; // too big or too small exptime + exptime = t; + return TRUE; +} + +static int getbin(int *h, int *v){ + DBG("get bin v/h: %d/%d", vbin, hbin); + if(h) *h = hbin; + if(v) *v = vbin; + return TRUE; +} + +static int pollcapt(capture_status *st, float *remain){ + DBG("Poll capture, tremain=%g", dtime() - tstart); + if(dtime() - tstart > 5.){ // capture error? + ApnGlueExpAbort(); + if(*st) *st = CAPTURE_ABORTED; + return FALSE; + } + if(remain) *remain = dtime() - tstart; + if(st) *st = CAPTURE_PROCESS; + if(ApnGlueExpDone()){ + if(st) *st = CAPTURE_READY; + isexposuring = 0; + DBG("Capture ready"); + } + return TRUE; +} + +static int capture(IMG *ima){ + FNAME(); + if(!ima || !ima->data) return FALSE; + if(ApnGlueReadPixels((uint16_t*)ima->data, imW * imH, whynot)){ + WARNX("Can't read image: %s", whynot); + return FALSE; + } + ima->bitpix = is16bit ? 16 : 12; + return TRUE; +} + + +static int ffalse(_U_ float f){ return FALSE; } +static int fpfalse(_U_ float *f){ return FALSE; } +static int ifalse(_U_ int i){ return FALSE; } +//static int vtrue(){ return TRUE; } +static int ipfalse(_U_ int *i){ return FALSE; } + +/* + * Global objects: camera, focuser and wheel + */ +Camera camera = { + .check = ndev, + .close = disconnect, + .pollcapture = pollcapt, + .capture = capture, + .cancel = cancel, + .startexposition = startexp, + // setters: + .setDevNo = setdevno, + .setbrightness = ffalse, + .setexp = setexp, + .setgain = ffalse, + .setT = sett, + .setbin = setbin, + .setnflushes = preflash, + .shuttercmd = shutter, + .confio = ifalse, + .setio = ifalse, + .setframetype = frametype, // set DARK or NORMAL: no shutter -> no darks + .setbitdepth = setbitdepth, + .setfastspeed = setfastspeed, + .setgeometry = setgeometry, + .setfanspeed = setfanspd, + // getters: + .getbrightness = fpfalse, + .getModelName = modelname, + .getgain = fpfalse, + .getmaxgain = fpfalse, + .getgeomlimits = geometrylimits, + .getTcold = tcold, + .getThot = thot, + .getTbody = fpfalse, + .getbin = getbin, + .getio = ipfalse, +}; diff --git a/CMakeLists.txt b/CMakeLists.txt index a9497a9..71bef5f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,6 +17,7 @@ option(FLI "Add support of FLI cameras" OFF) option(BASLER "Add support of BASLER cameras" OFF) option(HIKROBOT "Add support of HIKROBOT cameras" OFF) option(FLYCAP "Add support of Grasshopper FlyCap cameras" OFF) +option(APOGEE "Add support of Apogee cameras" OFF) # default flags set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -W -Wextra -std=gnu99") @@ -96,7 +97,9 @@ endif() if(FLYCAP) add_subdirectory(GRH_cameras) endif() - +if(APOGEE) + add_subdirectory(APOGEE_cameras) +endif() # directory should contain dir locale/ru for gettext translations set(LCPATH ${CMAKE_SOURCE_DIR}/locale/ru) diff --git a/Readme.md b/Readme.md index 989353f..a60cd64 100644 --- a/Readme.md +++ b/Readme.md @@ -1,7 +1,7 @@ CCD/CMOS imaging server ======================= -Supports FLI cameras/focusers/wheels and cameras: ZWO, Basler, HikRobot. +Supports FLI cameras/focusers/wheels and cameras: ZWO, Basler, HikRobot, PointGrey, Apogee. Allows to run as standalone application or imaging server/client. To restart server (e.g. if hardware was off) kill it with SIGUSR1 @@ -10,10 +10,12 @@ To restart server (e.g. if hardware was off) kill it with SIGUSR1 cmake options: +- `-DAPOGEE=ON` - compile Apogee plugin - `-DDEBUG=ON` - make with a lot debugging info - `-DIMAGEVIEW=ON` - compile with image viewer support (only for standalone) (OpenGL!!!) - `-DBASLER=ON` - compile Basler support plugin - `-DFLI=ON` - compile FLI support plugin +- `-DFLYCAPT=ON` - compile GrassHopper PointGrey plugin - `-DHIKROBOT=ON` - compile HikRobot support plugin - `-DZWO=ON` - compile ZWO support plugin diff --git a/locale/ru/messages.po b/locale/ru/messages.po index 0065998..fc447c1 100644 --- a/locale/ru/messages.po +++ b/locale/ru/messages.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-02-28 17:05+0300\n" +"POT-Creation-Date: 2023-03-06 17:25+0300\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -474,12 +474,12 @@ msgstr "" msgid "Can't set brightness to %g" msgstr "" -#: ccdfunc.c:725 server.c:230 +#: ccdfunc.c:725 server.c:229 #, c-format msgid "Can't set binning %dx%d" msgstr "" -#: ccdfunc.c:737 server.c:231 +#: ccdfunc.c:737 server.c:230 msgid "Can't set given geometry" msgstr "" @@ -525,7 +525,7 @@ msgstr "" msgid "Capture frame %d" msgstr "" -#: ccdfunc.c:781 ccdfunc.c:831 server.c:125 +#: ccdfunc.c:781 ccdfunc.c:831 server.c:124 msgid "Can't start exposition" msgstr "" @@ -547,7 +547,7 @@ msgstr "" msgid "%d seconds till pause ends\n" msgstr "" -#: server.c:168 +#: server.c:167 msgid "No camera device" msgstr "" diff --git a/locale/ru/ru.po b/locale/ru/ru.po index dbc5704..99fea0c 100644 --- a/locale/ru/ru.po +++ b/locale/ru/ru.po @@ -7,7 +7,7 @@ msgid "" msgstr "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" - "POT-Creation-Date: 2023-02-28 17:05+0300\n" + "POT-Creation-Date: 2023-03-01 08:54+0300\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -162,7 +162,7 @@ msgstr " msgid "Can't set active wheel number" msgstr "Не могу установить номер активного колеса" -#: ccdfunc.c:725 server.c:230 +#: ccdfunc.c:725 server.c:229 #, c-format msgid "Can't set binning %dx%d" msgstr "Не могу установить биннинг %dx%d" @@ -190,7 +190,7 @@ msgstr " msgid "Can't set gain to %g" msgstr "Не могу установить Gain в %g" -#: ccdfunc.c:737 server.c:231 +#: ccdfunc.c:737 server.c:230 msgid "Can't set given geometry" msgstr "Не могу установить геометрию" @@ -213,7 +213,7 @@ msgstr " msgid "Can't set wheel position %d" msgstr "Не могу установить положение колеса %d" -#: ccdfunc.c:781 ccdfunc.c:831 server.c:125 +#: ccdfunc.c:781 ccdfunc.c:831 server.c:124 msgid "Can't start exposition" msgstr "Не могу начать экспозицию" @@ -289,7 +289,7 @@ msgstr " msgid "N flushes before exposing (default: 1)" msgstr "N засвечиваний перед экспозицией (по умолчанию: 1)" -#: server.c:168 +#: server.c:167 msgid "No camera device" msgstr "Не указано устройство камеры" diff --git a/server.c b/server.c index 1e2643c..852c0a5 100644 --- a/server.c +++ b/server.c @@ -885,7 +885,7 @@ static hresult imsendhandler(int fd, _U_ const char *key, _U_ const char *val){ if(!ima.data || !ima.h || !ima.w) return RESULT_FAIL; // send image as raw data w*h*2 if(!sendimage(fd, ima.data, 2*ima.h*ima.w)) return RESULT_DISCONNECTED; - return RESULT_OK; + return RESULT_SILENCE; } static hresult imsizehandler(int fd, const char *key, _U_ const char *val){ diff --git a/socket.c b/socket.c index 63eea06..10dafe8 100644 --- a/socket.c +++ b/socket.c @@ -132,6 +132,7 @@ int start_socket(int isserver, char *path, int isnet){ return 0; } +// send image data to client int sendimage(int fd, uint16_t *data, int l){ if(fd < 1 || !data || l < 1) return TRUE; // empty message DBG("send new image (size=%d) to fd %d", l, fd);