mirror of
https://github.com/eddyem/CCD_Capture.git
synced 2025-12-06 02:35:13 +03:00
Add support of Basler cameras
This commit is contained in:
parent
e9a22b0b14
commit
5aed1d5955
15
BASLER_cameras/CMakeLists.txt
Normal file
15
BASLER_cameras/CMakeLists.txt
Normal 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})
|
||||
36
BASLER_cameras/FindBASLER.cmake
Normal file
36
BASLER_cameras/FindBASLER.cmake
Normal 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
556
BASLER_cameras/basler.c
Normal 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,
|
||||
};
|
||||
@ -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
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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++;
|
||||
}
|
||||
|
||||
36
Readme.md
36
Readme.md
@ -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).
|
||||
|
||||
|
||||
17
ccdfunc.c
17
ccdfunc.c
@ -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");
|
||||
|
||||
4
events.c
4
events.c
@ -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){
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user