Add support of Basler cameras

This commit is contained in:
Edward Emelianov 2023-02-02 14:31:54 +03:00
parent e9a22b0b14
commit 5aed1d5955
9 changed files with 677 additions and 26 deletions

View File

@ -0,0 +1,15 @@
cmake_minimum_required(VERSION 3.20)
set(CCDLIB devbasler)
SET(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR})
find_package(PkgConfig REQUIRED)
find_package(BASLER REQUIRED)
pkg_check_modules(${CCDLIB} REQUIRED usefull_macros)
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR} SRC)
include_directories(${${CCDLIB}_INCLUDE_DIRS} ${BASLER_INCLUDE_DIRS} ..)
link_directories(${${CCDLIB}_LIBRARY_DIRS} ${BASLER_LIBRARY_DIRS})
add_library(${CCDLIB} SHARED ${SRC})
target_link_libraries(${CCDLIB} ${${CCDLIB}_LIBRARIES} ${BASLER_LIBRARY} -fPIC)
install(TARGETS ${CCDLIB} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})

View File

@ -0,0 +1,36 @@
# - Try to find libpylonc.so
# Once done this will define
#
# BASLER_FOUND - system has lib
# BASLER_INCLUDE_DIR - include directory
# BASLER_LIBRARIES - Link these to use lib
# Copyright (c) 2021, Edward V. Emelianov <edward.emelianoff@gmail.com>
#
# Redistribution and use is allowed according to the terms of the GPLv2/GPLv3.
include(GNUInstallDirs)
find_path(BASLER_INCLUDE_DIR pylonc/PylonC.h
PATHS /usr/pylon/include /opt/pylon/include /usr/local/pylon/include /usr/include /usr/local/include /opt/include /opt/local/include
)
find_path(BASLER_LIBRARY_DIR libpylonc.so
PATHS /usr/pylon/lib /opt/pylon/lib /usr/local/pylon/lib /lib /lib64 /usr/lib /usr/lib64 /opt/lib /opt/lib64 /usr/local/lib /usr/local/lib64
)
find_library(BASLER_LIBRARY NAMES pylonc
PATHS /opt/pylon/lib /usr/pylon/lib /usr/local/pylon/lib /lib /lib64 /usr/lib /usr/lib64 /opt/lib /opt/lib64 /usr/local/lib /usr/local/lib64
)
find_package_handle_standard_args(BASLER DEFAULT_MSG BASLER_INCLUDE_DIR BASLER_LIBRARY BASLER_LIBRARY_DIR)
if(BASLER_FOUND)
set(BASLER_INCLUDE_DIRS ${BASLER_INCLUDE_DIR})
set(BASLER_LIBRARIES ${BASLER_LIBRARY})
set(BASLER_LIBRARY_DIRS ${BASLER_LIBRARY_DIR})
# message("BASLER include dir = ${BASLER_INCLUDE_DIRS}")
# message("BASLER lib = ${BASLER_LIBRARIES}")
# message("BASLER libdir = ${BASLER_LIBRARY_DIRS}")
mark_as_advanced(BASLER_INCLUDE_DIRS BASLER_LIBRARIES BASLER_LIBRARY_DIRS)
else()
message("BASLER not found")
endif(BASLER_FOUND)

556
BASLER_cameras/basler.c Normal file
View File

@ -0,0 +1,556 @@
/*
* This file is part of the CCD_Capture project.
* Copyright 2023 Edward V. Emelianov <edward.emelianoff@gmail.com>.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <pylonc/PylonC.h>
#include <math.h>
#include <stdio.h>
#include <string.h>
#include <usefull_macros.h>
#include "basestructs.h"
#include "omp.h"
extern Camera camera;
static PYLON_DEVICE_HANDLE hDev;
static int isopened = FALSE, is16bit = FALSE;
static char camname[BUFSIZ] = {0};
static size_t payloadsize = 0; // size of imgBuf
static unsigned char *imgBuf = NULL;
static uint32_t expostime = 0.; // current exposition time (in microseconds)
static PYLON_DEVICECALLBACK_HANDLE hCb;
static int curhbin = 1, curvbin = 1;
#if 0
static void *handle = NULL;
static char camname[BUFSIZ] = {0};
//static long cam_err, tmpl;
static capture_status capStatus = CAPTURE_NO;
static int curhbin = 1, curvbin = 1;
static double starttime = 0.; // time when exposure started
static float exptime = 0.; // exposition time (in seconds)
static uint8_t *pdata = NULL;
static int pdatasz = 0;
static struct{
float maxgain;
float mingain;
float maxbright;
float minbright;
float minexp;
float maxexp;
int maxbin;
} extrvalues = {0}; // extremal values
#endif
typedef struct{
int64_t min;
int64_t max;
int64_t incr;
int64_t val;
} int64_values;
typedef struct{
double min;
double max;
double val;
} float_values;
static char* describeError(GENAPIC_RESULT reserr){
static char buf[1024];
char* errMsg, *bptr = buf;
size_t length, l = 1023;
GenApiGetLastErrorMessage(NULL, &length);
errMsg = MALLOC(char, length);
GenApiGetLastErrorMessage(errMsg, &length);
size_t ll = snprintf(bptr, l, "%s (%d); ", errMsg, (int)reserr);
if(ll > 0){l -= ll; bptr += ll;}
FREE(errMsg);
GenApiGetLastErrorDetail(NULL, &length);
errMsg = MALLOC(char, length);
GenApiGetLastErrorDetail(errMsg, &length);
snprintf(bptr, l, "%s", errMsg);
FREE(errMsg);
return buf;
}
#define PYLONFN(fn, ...) do{register GENAPIC_RESULT reserr; if(GENAPI_E_OK != (reserr=fn(__VA_ARGS__))){ \
WARNX(#fn "(): %s", describeError(reserr)); return FALSE;}}while(0)
static void disconnect(){
FNAME();
if(!isopened) return;
FREE(imgBuf);
PylonDeviceDeregisterRemovalCallback(hDev, hCb);
PylonDeviceClose(hDev);
PylonDestroyDevice(hDev);
PylonTerminate();
isopened = FALSE;
}
/**
* @brief chkNode - get node & check it for read/write
* @param phNode (io) - pointer to node
* @param nodeType - type of node
* @param wr - ==TRUE if need to check for writeable
* @return TRUE if node found & checked
*/
static int chkNode(NODE_HANDLE *phNode, const char *featureName, EGenApiNodeType nodeType, int wr){
if(!isopened || !phNode || !featureName) return FALSE;
NODEMAP_HANDLE hNodeMap;
EGenApiNodeType nt;
_Bool bv;
PYLONFN(PylonDeviceGetNodeMap, hDev, &hNodeMap);
PYLONFN(GenApiNodeMapGetNode, hNodeMap, featureName, phNode);
if(*phNode == GENAPIC_INVALID_HANDLE) return FALSE;
PYLONFN(GenApiNodeGetType, *phNode, &nt);
if(nodeType != nt) return FALSE;
PYLONFN(GenApiNodeIsReadable, *phNode, &bv);
if(!bv) return FALSE; // not readable
if(wr){
PYLONFN(GenApiNodeIsWritable, *phNode, &bv);
if(!bv) return FALSE; // not writeable
}
return TRUE;
}
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-function"
// getters of different types of data
static int getBoolean(const char *featureName, _Bool *val){
if(!isopened || !featureName) return FALSE;
NODE_HANDLE hNode;
if(!chkNode(&hNode, featureName, BooleanNode, FALSE)) return FALSE;
if(!val) return TRUE;
PYLONFN(GenApiBooleanGetValue, hNode, val);
//DBG("Get boolean: %s = %s", featureName, val ? "true" : "false");
return TRUE;
}
static int getInt(char *featureName, int64_values *val){
if(!isopened || !featureName) return FALSE;
NODE_HANDLE hNode;
if(!chkNode(&hNode, featureName, IntegerNode, FALSE)) return FALSE;
if(!val) return TRUE;
PYLONFN(GenApiIntegerGetMin, hNode, &val->min);
PYLONFN(GenApiIntegerGetMax, hNode, &val->max);
PYLONFN(GenApiIntegerGetInc, hNode, &val->incr);
PYLONFN(GenApiIntegerGetValue, hNode, &val->val);
//DBG("Get integer %s = %ld: min = %ld, max = %ld, incr = %ld", featureName, val->val, val->min, val->max, val->incr);
return TRUE;
}
static int getFloat(char *featureName, float_values *val){
if(!isopened || !featureName) return FALSE;
NODE_HANDLE hNode;
if(!chkNode(&hNode, featureName, FloatNode, FALSE)) return FALSE;
if(!val) return TRUE;
PYLONFN(GenApiFloatGetMin, hNode, &val->min);
PYLONFN(GenApiFloatGetMax, hNode, &val->max);
PYLONFN(GenApiFloatGetValue, hNode, &val->val);
//DBG("Get float %s = %g: min = %g, max = %g", featureName, val->val, val->min, val->max);
return TRUE;
}
// setters of different types of data
static int setBoolean(char *featureName, _Bool val){
if(!isopened || !featureName) return FALSE;
NODE_HANDLE hNode;
if(!chkNode(&hNode, featureName, BooleanNode, TRUE)) return FALSE;
PYLONFN(GenApiBooleanSetValue, hNode, val);
return TRUE;
}
static int setInt(char *featureName, int64_t val){
if(!isopened || !featureName) return FALSE;
NODE_HANDLE hNode;
if(!chkNode(&hNode, featureName, IntegerNode, TRUE)) return FALSE;
PYLONFN(GenApiIntegerSetValue, hNode, val);
return TRUE;
}
static int setFloat(char *featureName, float val){
if(!isopened || !featureName) return FALSE;
NODE_HANDLE hNode;
if(!chkNode(&hNode, featureName, FloatNode, TRUE)) return FALSE;
PYLONFN(GenApiFloatSetValue, hNode, val);
return TRUE;
}
#pragma GCC diagnostic pop
static void disableauto(){
if(!isopened) return;
const char *features[] = {"EnumEntry_TriggerSelector_AcquisitionStart",
"EnumEntry_TriggerSelector_FrameBurstStart",
"EnumEntry_TriggerSelector_FrameStart"};
const char *triggers[] = {"AcquisitionStart", "FrameBurstStart", "FrameStart"};
for(int i = 0; i < 3; ++i){
if(PylonDeviceFeatureIsAvailable(hDev, features[i])){
PylonDeviceFeatureFromString(hDev, "TriggerSelector", triggers[i]);
PylonDeviceFeatureFromString(hDev, "TriggerMode", "Off");
}
}
PylonDeviceFeatureFromString(hDev, "GainAuto", "Off");
PylonDeviceFeatureFromString(hDev, "ExposureAuto", "Off");
PylonDeviceFeatureFromString(hDev, "ExposureMode", "Timed");
PylonDeviceFeatureFromString(hDev, "SequencerMode", "Off");
}
static void GENAPIC_CC removalCallbackFunction(_U_ PYLON_DEVICE_HANDLE hDevice){
disconnect();
}
static int connect(){
FNAME();
size_t numDevices;
disconnect();
PylonInitialize();
PYLONFN(PylonEnumerateDevices, &numDevices);
if(!numDevices){
WARNX("No cameras found");
return FALSE;
}
camera.Ndevices = numDevices;
return TRUE;
}
static int getbin(int *binh, int *binv){
if(!PylonDeviceFeatureIsAvailable(hDev, "BinningVertical") ||
!PylonDeviceFeatureIsAvailable(hDev, "BinningHorizontal")) return FALSE;
int64_values v;
if(!getInt("BinningVertical", &v)){
DBG("Can't get Vbin");
return FALSE;
}
curvbin = v.val;
if(!getInt("BinningHorizontal", &v)){
DBG("Can't get Hbin");
return FALSE;
}
curhbin = v.val;
DBG("binning: %d x %d", curhbin, curvbin);
if(binv) *binv = curvbin;
if(binh) *binh = curhbin;
return TRUE;
}
static int getgeom(){
FNAME();
if(!isopened) return FALSE;
int64_values i;
if(!getInt("Width", &i)) return FALSE;
camera.field.w = i.val;
camera.array.w = i.max;
if(!getInt("Height", &i)) return FALSE;
camera.field.h = i.val;
camera.array.h = i.max;
if(!getInt("OffsetX", &i)) return FALSE;
camera.field.xoff = i.val;
camera.array.xoff = i.val;
camera.array.w -= i.val;
if(!getInt("OffsetY", &i)) return FALSE;
camera.field.yoff = i.val;
camera.array.yoff = i.val;
camera.array.h -= i.val;
camera.geometry = camera.field;
return TRUE;
}
static int geometrylimits(frameformat *max, frameformat *step){
FNAME();
if(!isopened || !max || !step) return FALSE;
int64_values i;
if(!getInt("Width", &i)) return FALSE;
max->w = i.max; step->w = i.incr;
if(!getInt("Height", &i)) return FALSE;
max->h = i.max; step->h = i.incr;
if(!getInt("OffsetX", &i)) return FALSE;
max->xoff = i.max; step->xoff = i.incr;
if(!getInt("OffsetY", &i)) return FALSE;
max->yoff = i.max; step->yoff = i.incr;
return TRUE;
}
static int setdevno(int N){
if(N > camera.Ndevices - 1) return FALSE;
PYLONFN(PylonCreateDeviceByIndex, 0, &hDev);
isopened = TRUE;
PYLONFN(PylonDeviceOpen, hDev, PYLONC_ACCESS_MODE_CONTROL | PYLONC_ACCESS_MODE_STREAM | PYLONC_ACCESS_MODE_EXCLUSIVE);
disableauto();
PYLONFN(PylonDeviceFeatureFromString, hDev, "CameraOperationMode", "LongExposure");
PYLONFN(PylonDeviceFeatureFromString, hDev, "UserSetSelector", "HighGain"); // set high gain selector
PYLONFN(PylonDeviceFeatureFromString, hDev, "AcquisitionMode", "SingleFrame");
PYLONFN(PylonDeviceExecuteCommandFeature, hDev, "UserSetLoad"); // load high gain mode
if(PylonDeviceFeatureIsReadable(hDev, "DeviceModelName")){
size_t l = BUFSIZ-1;
PYLONFN(PylonDeviceFeatureToString, hDev, "DeviceModelName", camname, &l);
DBG("Using camera %s\n", camname);
}else strcpy(camname, "Unknown camera");
if(!getbin(NULL, NULL)) WARNX("Can't get current binning");
if(!getgeom()) WARNX("Can't get current frame format");
PylonDeviceRegisterRemovalCallback(hDev, removalCallbackFunction, &hCb);
PYLON_STREAMGRABBER_HANDLE hGrabber;
PYLONFN(PylonDeviceGetStreamGrabber, hDev, 0, &hGrabber);
PYLONFN(PylonStreamGrabberOpen, hGrabber);
PYLONFN(PylonStreamGrabberGetPayloadSize, hDev, hGrabber, &payloadsize);
DBG("payload sz %zd", payloadsize);
PylonStreamGrabberClose(hGrabber);
FREE(imgBuf);
imgBuf = MALLOC(unsigned char, payloadsize);
return TRUE;
}
static int setbitdepth(int i){
if(i == 0){ // 8 bit
if(!PylonDeviceFeatureIsAvailable( hDev, "EnumEntry_PixelFormat_Mono8" )) return FALSE;
PYLONFN(PylonDeviceFeatureFromString, hDev, "PixelFormat", "Mono8");
is16bit = FALSE;
DBG("8 bit");
}else{ // 16 bit
if(!PylonDeviceFeatureIsAvailable( hDev, "EnumEntry_PixelFormat_Mono16" )) return FALSE;
PYLONFN(PylonDeviceFeatureFromString, hDev, "PixelFormat", "Mono16");
is16bit = TRUE;
DBG("16 bit");
}
PYLON_STREAMGRABBER_HANDLE hGrabber;
PYLONFN(PylonDeviceGetStreamGrabber, hDev, 0, &hGrabber);
PYLONFN(PylonStreamGrabberOpen, hGrabber);
PYLONFN(PylonStreamGrabberGetPayloadSize, hDev, hGrabber, &payloadsize);
DBG("payload sz %zd", payloadsize);
PylonStreamGrabberClose(hGrabber);
FREE(imgBuf);
imgBuf = MALLOC(unsigned char, payloadsize);
return TRUE;
}
// stub function: the capture process is blocking
static int pollcapt(capture_status *st, float *remain){
if(st) *st = CAPTURE_READY;
if(remain) *remain = 0.f;
return TRUE;
}
static int capture(IMG *ima){
FNAME();
if(!ima || !ima->data || !imgBuf || !isopened) return FALSE;
static int toohot = FALSE;
float_values f;
if(!getFloat("DeviceTemperature", &f)) WARNX("Can't get temperature");
else{
DBG("Temperature: %.1f", f.val);
if(f.val > 80.){
WARNX("Device too hot");
toohot = TRUE;
}else if(toohot && f.val < 75.){
DBG("Device temperature is normal");
toohot = FALSE;
}
}
PylonGrabResult_t grabResult;
_Bool bufferReady;
GENAPIC_RESULT res = PylonDeviceGrabSingleFrame(hDev, 0, imgBuf, payloadsize,
&grabResult, &bufferReady, 500 + (uint32_t)expostime);
if(res != GENAPI_E_OK || !bufferReady){
WARNX("res != GENAPI_E_OK || !bufferReady");
return FALSE;
}
if(grabResult.Status != Grabbed){
WARNX("grabResult.Status != Grabbed");
return FALSE;
}
int width = grabResult.SizeX, height = grabResult.SizeY, stride = grabResult.SizeX + grabResult.PaddingX;
if(is16bit){
int s2 = stride<<1, w2 = width<<1;
OMP_FOR()
for(int y = 0; y < height; ++y){
uint16_t *Out = &ima->data[y*width];
const uint8_t *In = &imgBuf[y*s2];
memcpy(Out, In, w2);
}
}else{
OMP_FOR()
for(int y = 0; y < height; ++y){
uint16_t *Out = &ima->data[y*width];
const uint8_t *In = &imgBuf[y*stride];
for(int x = 0; x < width; ++x){
*Out++ = *In++;
}
}
}
return TRUE;
}
// Basler have no "brightness" parameter
static int setbrightness(_U_ float b){
FNAME();
return FALSE;
}
static int setexp(float e){
FNAME();
if(!isopened) return FALSE;
e *= 1e6f;
if(!setFloat("ExposureTime", e)){
LOGWARN("Can't set expose time %g", e);
WARNX("Can't set expose time %g", e);
return FALSE;
}
float_values f;
if(!getFloat("ExposureTime", &f)) return FALSE;
expostime = (uint32_t)(f.val);
DBG("EXPOSURE time: need %f, real %f", e, f.val);
return TRUE;
}
static int setgain(float e){
FNAME();
if(!isopened) return FALSE;
if(!setFloat("Gain", e)){
LOGWARN("Can't set gain %g", e);
WARNX("Can't set gain %g", e);
return FALSE;
}
DBG("GAIN -> %f", e);
return TRUE;
}
static int changeformat(frameformat *fmt){
FNAME();
if(!isopened) return FALSE;
if(!getbin(NULL, NULL)){curhbin = 1; curvbin = 1;}
fmt->h /= curvbin; fmt->yoff /= curvbin;
fmt->w /= curhbin; fmt->xoff /= curhbin;
DBG("set geom %dx%d (off: %dx%d)", fmt->w, fmt->h, fmt->xoff, fmt->yoff);
setInt("Width", fmt->w);
setInt("Height", fmt->h);
setInt("OffsetX", fmt->xoff);
setInt("OffsetY", fmt->yoff);
int64_values i;
if(getInt("Width", &i)) camera.geometry.w = fmt->w = i.val * curhbin;
if(getInt("Height", &i)) camera.geometry.h = fmt->h = i.val * curvbin;
if(getInt("OffsetX", &i)) camera.geometry.xoff = fmt->xoff = i.val * curhbin;
if(getInt("OffsetY", &i)) camera.geometry.yoff = fmt->yoff = i.val * curvbin;
return TRUE;
}
static int getgain(float *g){
FNAME();
float_values v;
if(!getFloat("Gain", &v)) return FALSE;
if(g) *g = (float)v.val;
return TRUE;
}
static int gainmax(float *g){
FNAME();
float_values v;
if(!getFloat("Gain", &v)) return FALSE;
if(g) *g = (float)v.max;
return TRUE;
}
static int modelname(char *buf, int bufsz){
strncpy(buf, camname, bufsz);
return TRUE;
}
static int setbin(int binh, int binv){
if(!PylonDeviceFeatureIsAvailable(hDev, "BinningVertical") ||
!PylonDeviceFeatureIsAvailable(hDev, "BinningHorizontal")){
DBG("No Vbin / Hbin");
return FALSE;
}
if(setInt("BinningVertical", (int64_t)binv) &&
setInt("BinningHorizontal", (int64_t)binh)) return TRUE;
DBG("Can't set v or h");
return FALSE;
}
static int gett(float *t){
if(!t) return FALSE;
float_values fv;
if(!getFloat("DeviceTemperature", &fv)) return FALSE;
*t = fv.val;
return TRUE;
}
static int setfanspd(_U_ fan_speed s){
return FALSE;
}
static int shutter(_U_ shutter_op cmd){
return FALSE;
}
static int ffalse(_U_ float f){ return FALSE; }
static int fpfalse(_U_ float *f){ return FALSE; }
static int ifalse(_U_ int i){ return FALSE; }
static int vtrue(){ return TRUE; }
static int ipfalse(_U_ int *i){ return FALSE; }
static void vstub(){ return ;}
#if 0
// exported object
camera Basler = {
.disconnect = disconnect,
.connect = connect,
.capture = capture,
.setbrightness = setbrightness,
.setexp = setexp,
.setgain = setgain,
.setgeometry = changeformat,
.getgeomlimits = geometrylimits,
.getmaxgain = gainmax,
};
#endif
/*
* Global objects: camera, focuser and wheel
*/
Camera camera = {
.check = connect,
.close = disconnect,
.pollcapture = pollcapt,
.capture = capture,
.cancel = vstub,
.startexposition = vtrue,
// setters:
.setDevNo = setdevno,
.setbrightness = setbrightness,
.setexp = setexp,
.setgain = setgain,
.setT = ffalse,
.setbin = setbin,
.setnflushes = ifalse,
.shuttercmd = shutter,
.confio = ifalse,
.setio = ifalse,
.setframetype = ifalse, // set DARK or NORMAL: no shutter -> no darks
.setbitdepth = setbitdepth,
.setfastspeed = ifalse,
.setgeometry = changeformat,
.setfanspeed = setfanspd,
// getters:
.getbrightness = fpfalse,
.getModelName = modelname,
.getgain = getgain,
.getmaxgain = gainmax,
.getgeomlimits = geometrylimits,
.getTcold = fpfalse,
.getThot = fpfalse,
.getTbody = gett,
.getbin = getbin,
.getio = ipfalse,
};

View File

@ -1,7 +1,7 @@
cmake_minimum_required(VERSION 3.20)
set(PROJ ccd_capture)
set(MINOR_VERSION "0")
set(MID_VERSION "0")
set(MID_VERSION "1")
set(MAJOR_VERSION "1")
set(VERSION "${MAJOR_VERSION}.${MID_VERSION}.${MINOR_VERSION}")
@ -9,6 +9,14 @@ project(${PROJ} VERSION ${VERSION} LANGUAGES C)
message("VER: ${VERSION}")
# list of options
option(DEBUG "Compile in debug mode" OFF)
option(IMAGEVIEW "Build with imageview module" OFF)
option(ZWO "Add support of ZWO cameras" OFF)
option(FLI "Add support of FLI cameras" OFF)
option(BASLER "Add support of BASLER cameras" OFF)
option(HIKROBOT "Add support of HIKROBOT cameras" OFF)
# default flags
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -W -Wextra -std=gnu99")
@ -36,14 +44,14 @@ set(CMAKE_COLOR_MAKEFILE ON)
set(SOURCES main.c cmdlnopts.c ccdfunc.c socket.c server.c client.c)
# cmake -DDEBUG=yes -> debugging
if(DEFINED DEBUG AND DEBUG STREQUAL "yes")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Og -ggdb -fno-builtin-strlen -Werror")
if(DEBUG)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Og -g3 -ggdb -fno-builtin-strlen -Werror")
add_definitions(-DEBUG)
set(CMAKE_BUILD_TYPE DEBUG)
set(CMAKE_VERBOSE_MAKEFILE "ON")
else()
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O3 -march=native -fdata-sections -ffunction-sections")
SET(CMAKE_EXE_LINKER_FLAGS "-Wl,--gc-sections")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O3 -march=native -fdata-sections -ffunction-sections -flto")
SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections -flto")
set(CMAKE_BUILD_TYPE RELEASE)
endif()
@ -59,7 +67,7 @@ if(OPENMP_FOUND)
add_definitions(-DOMP_FOUND)
endif()
if(DEFINED IMAGEVIEW AND IMAGEVIEW STREQUAL "yes")
if(IMAGEVIEW)
list(APPEND SOURCES events.c imageview.c)
find_package(OpenGL REQUIRED)
find_package(GLUT REQUIRED)
@ -72,17 +80,18 @@ endif()
add_subdirectory(Dummy_cameras)
# additional modules with CCD/CMOS support
if(DEFINED ZWO AND ZWO STREQUAL "yes")
if(ZWO)
add_subdirectory(ZWO_cameras)
endif()
if(DEFINED FLI AND FLI STREQUAL "yes")
if(FLI)
add_subdirectory(FLI_cameras)
endif()
if(DEFINED HIKROBOT AND HIKROBOT STREQUAL "yes")
if(HIKROBOT)
add_subdirectory(HIKROBOT_cameras)
endif()
if(BASLER)
add_subdirectory(BASLER_cameras)
endif()
# directory should contain dir locale/ru for gettext translations

View File

@ -663,7 +663,7 @@ static int fli_fpfalse(_U_ float *f){ return FALSE; }
/*
* Global objects: camera, focuser and wheel
*/
Camera camera = {
__attribute__ ((visibility("default"))) Camera camera = {
.check = fli_findCCD,
.close = fli_closecam,
.pollcapture = fli_pollcapt,
@ -699,7 +699,7 @@ Camera camera = {
.getio = fli_getio,
};
Focuser focuser = {
__attribute__ ((visibility("default"))) Focuser focuser = {
.check = fli_findFocuser,
.setDevNo = fli_setActiceFocuser,
.close = fli_closefocuser,
@ -712,7 +712,7 @@ Focuser focuser = {
.setAbsPos = fli_fgoto,
};
Wheel wheel = {
__attribute__ ((visibility("default"))) Wheel wheel = {
.check = fli_findWheel,
.setDevNo = fli_setActiceWheel,
.close = fli_closewheel,

View File

@ -23,6 +23,7 @@
#include <usefull_macros.h>
#include "basestructs.h"
#include "omp.h"
#ifndef FLT_EPSILON
#define FLT_EPSILON 1.19209290E-07F
@ -467,6 +468,7 @@ static int cam_capt(IMG *ima){
DBG("TRANSFORM 8 bit to 16");
bytes /= 2;
uint8_t *ptr = (uint8_t*) pdata;
OMP_FOR()
for(int i = 0; i < bytes; ++i){
ima->data[i] = (uint16_t) *ptr++;
}

View File

@ -1,7 +1,7 @@
CCD/CMOS imaging server
=======================
Supports FLI cameras/focusers/wheels and ZWO cameras.
Supports FLI cameras/focusers/wheels and cameras: ZWO, Basler, HikRobot.
Allows to run as standalone application or imaging server/client.
To restart server (e.g. if hardware was off) kill it with SIGUSR1
@ -10,11 +10,12 @@ To restart server (e.g. if hardware was off) kill it with SIGUSR1
cmake options:
- `-DDEBUG=yes` -- make with a lot debugging info
- `-DIMAGEVIEW=yes -- compile with image viewer support (only for standalone) (OpenGL!!!)
- `-DZWO=yes` -- compile ZWO support plugin
- `-DFLI=yes` -- compile FLI support 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
- `-DHIKROBOT=ON` - compile HikRobot support plugin
- `-DZWO=ON` - compile ZWO support plugin
```
@ -79,3 +80,26 @@ Usage: ccd_capture [args] [output file prefix]
--wheeldevno=arg filter wheel device number (if many: 0, 1, 2 etc)
```
## Image viewer
In image view mode you can display menu by clicking of right mouse key or use shortcuts:
- '0' - restore zoom;
- 'c' - capture new image in pause mode (works only with `-n` flag);
- 'e' - switch on/off histogram equalization;
- 'l' - flip image right-left;
- 'p' - pause or resume capturing (works only with `-n` flag);
- 'u' - flip image up-down;
- 'Z' - zoom+;
- 'z' - zoom-;
- 'Ctrl+r' - roll histogram conversion function (LOG, SQRT, POW, LINEAR);
- 'Ctrl+s' - save displayed image (works only if you pointed output file name or prefix);
- 'Ctrl+q' - exit.
Mouse functions:
- Left button - center selected point.
- Middle button - move image.
- Right button - show menu.
- Wheel up - scroll up, or scroll left (with Shift), or zoom+ (with Ctrl).
- Wheel down - scroll down, or scroll right (with Shift), or zoom- (with Ctrl).

View File

@ -674,6 +674,9 @@ void ccds(){
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);
snprintf(buf, BUFSIZ, "(%d, %d)(%d, %d)", camera->geometry.xoff, camera->geometry.yoff,
camera->geometry.xoff + camera->geometry.w, camera->geometry.yoff + camera->geometry.h);
verbose(2, _("Current format: %s"), buf);
if(!isnan(GP->temperature)){
if(!camera->setT((float)GP->temperature))
WARNX(_("Can't set T to %g degC"), GP->temperature);
@ -724,16 +727,18 @@ void ccds(){
camera->cancel();
if(GP->hbin < 1) GP->hbin = 1;
if(GP->vbin < 1) GP->vbin = 1;
if(!camera->setbin(GP->hbin, GP->vbin))
if(!camera->setbin(GP->hbin, GP->vbin)){
WARNX(_("Can't set binning %dx%d"), GP->hbin, GP->vbin);
camera->getbin(&GP->hbin, &GP->vbin);
}
if(GP->X0 < 0) GP->X0 = x0; // default values
else if(GP->X0 > x1-1) GP->X0 = x1-1;
if(GP->Y0 < 0) GP->Y0 = y0;
else if(GP->Y0 > y1-1) GP->Y0 = y1-1;
if(GP->X1 < GP->X0+1 || GP->X1 > x1) GP->X1 = x1;
if(GP->Y1 < GP->Y0+1 || GP->Y1 > y1) GP->Y1 = y1;
DBG("x1/x0: %d/%d", GP->X1, GP->X0);
frameformat fmt = {.w = GP->X1 - GP->X0, .h = GP->Y1 - GP->Y0, .xoff = GP->X0, .yoff = GP->Y0};
int raw_width = fmt.w / GP->hbin, raw_height = fmt.h / GP->vbin;
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);
@ -757,7 +762,7 @@ void ccds(){
if(!camera->getbin(&GP->hbin, &GP->vbin)) // GET binning should be AFTER setgeometry!
WARNX(_("Can't get current binning"));
verbose(2, "Binning: %d x %d", GP->hbin, GP->vbin);
int raw_width = fmt.w / GP->hbin, raw_height = fmt.h / GP->vbin;
uint16_t *img = MALLOC(uint16_t, raw_width * raw_height);
DBG("\n\nAllocated image 2x%dx%d=%d", raw_width, raw_height, 2 * raw_width * raw_height);
@ -793,8 +798,12 @@ void ccds(){
break;
}
calculate_stat(&ima);
saveFITS(&ima, NULL);
#ifdef IMAGEVIEW
if(!GP->showimage){ // don't save all FITS files in imagev view mode
#endif
saveFITS(&ima, NULL);
#ifdef IMAGEVIEW
}
if(GP->showimage){ // display image
if((mainwin = getWin())){
DBG("change image");

View File

@ -84,7 +84,7 @@ static void processKeybrd(unsigned char key, int mod, _U_ int x, _U_ int y){
* Process keyboard
*/
void keyPressed(unsigned char key, int x, int y){
int mod = glutGetModifiers();
int mod = glutGetModifiers() & 7;
//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);
@ -101,7 +101,7 @@ 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();
int mod = glutGetModifiers() & 7;
windowData *win = getWin();
if(!win) return;
if(state == GLUT_DOWN){