diff --git a/LocCorr_new/CMakeLists.txt b/LocCorr_new/CMakeLists.txt index 859912f..eff40e0 100644 --- a/LocCorr_new/CMakeLists.txt +++ b/LocCorr_new/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.0) +cmake_minimum_required(VERSION 3.20) set(PROJ loccorr) set(MINOR_VERSION "1") set(MID_VERSION "0") @@ -7,9 +7,6 @@ set(VERSION "${MAJOR_VERSION}.${MID_VERSION}.${MINOR_VERSION}") project(${PROJ} VERSION ${VERSION} LANGUAGES C) -# default flags -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99") - set(CMAKE_COLOR_MAKEFILE ON) # here is one of two variants: all .c in directory or .c files in list @@ -19,9 +16,12 @@ set(CMAKE_VERBOSE_MAKEFILE "ON") # list of options option(DEBUG "Compile in debug mode" OFF) +option(BASLER "Add Basler cameras support" OFF) +option(GRASSHOPPER "Add GrassHopper cameras support" OFF) +option(HIKROBOT "Add HikRobot cameras support" OFF) # default flags -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -W -Wextra -std=gnu99") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -W -Wextra") set(CMAKE_COLOR_MAKEFILE ON) # cmake -DDEBUG=yes -> debugging @@ -45,9 +45,25 @@ set(MODULES usefull_macros cfitsio improc) # find packages: SET(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}) find_package(PkgConfig REQUIRED) -find_package(FLYCAP REQUIRED) -find_package(BASLER REQUIRED) -pkg_check_modules(${PROJ} REQUIRED ${MODULES}) +if(GRASSHOPPER) + find_package(FLYCAP REQUIRED) + add_definitions("-DFLYCAP_FOUND=1") +else() + list(REMOVE_ITEM SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/grasshopper.c") +endif() +if(BASLER) + find_package(BASLER REQUIRED) + add_definitions("-DBASLER_FOUND=1") +else() + list(REMOVE_ITEM SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/basler.c") +endif() +if(HIKROBOT) + pkg_check_modules(MVS REQUIRED mvs>=2.1) + add_definitions("-DMVS_FOUND=1") +else() + list(REMOVE_ITEM SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/hikrobot.c") +endif() +pkg_check_modules(MODULES REQUIRED ${MODULES}) include(FindOpenMP) if(OPENMP_FOUND) @@ -73,9 +89,10 @@ message("Install dir prefix: ${CMAKE_INSTALL_PREFIX}") # exe file add_executable(${PROJ} ${SOURCES}) # -I -include_directories(${${PROJ}_INCLUDE_DIRS} ${FLYCAP_INCLUDE_DIRS} ${BASLER_INCLUDE_DIRS}) +target_include_directories(${PROJ} PUBLIC ${MODULES_INCLUDE_DIRS} ${FLYCAP_INCLUDE_DIRS} ${BASLER_INCLUDE_DIRS} ${MVS_INCLUDE_DIRS}) # -L -link_directories(${${PROJ}_LIBRARY_DIRS} ${FLYCAP_LIBRARY_DIRS} ${BASLER_LIBRARY_DIRS}) +target_link_directories(${PROJ} PUBLIC ${MODULES_LIBRARY_DIRS} ${FLYCAP_LIBRARY_DIRS} ${BASLER_LIBRARY_DIRS} ${MVS_LIBRARY_DIRS}) +message("MOD: ${MODULES_LIBRARY_DIRS}, FC: ${FLYCAP_LIBRARY_DIRS}, MVS: ${MVS_LIBRARY_DIRS}") # -D add_definitions(${CFLAGS} -DLOCALEDIR=\"${LOCALEDIR}\" -DPACKAGE_VERSION=\"${VERSION}\" -DGETTEXT_PACKAGE=\"${PROJ}\" @@ -83,7 +100,7 @@ add_definitions(${CFLAGS} -DLOCALEDIR=\"${LOCALEDIR}\" -DMAJOR_VERSION=\"${MAJOR_VERSION}\" -DTHREAD_NUMBER=${PROCESSOR_COUNT}) # -l -target_link_libraries(${PROJ} ${${PROJ}_LIBRARIES} ${FLYCAP_LIBRARIES} ${BASLER_LIBRARIES} -lm) +target_link_libraries(${PROJ} ${MODULES_LIBRARIES} ${FLYCAP_LIBRARIES} ${BASLER_LIBRARIES} ${MVS_LIBRARIES} -lm) # Installation of the program INSTALL(TARGETS ${PROJ} DESTINATION "bin") diff --git a/LocCorr_new/Readme.md b/LocCorr_new/Readme.md index 4c4cc21..f5e260d 100644 --- a/LocCorr_new/Readme.md +++ b/LocCorr_new/Readme.md @@ -3,4 +3,4 @@ Takes fresh images from CMOS, single file or directory using inotify, find objects, calculate their centroids and send messages to corrector over serial-proxy. Used corrector controller - `multistepper`. -Used CMOS - Basler or Grasshopper. \ No newline at end of file +Used CMOS - Basler, Grasshopper or Hikrobot. diff --git a/LocCorr_new/basler.h b/LocCorr_new/basler.h index 42e2f4d..f1e109c 100644 --- a/LocCorr_new/basler.h +++ b/LocCorr_new/basler.h @@ -19,9 +19,13 @@ #ifndef BASLER_H__ #define BASLER_H__ + +#ifdef BASLER_FOUND #include "cameracapture.h" // `camera` + #define BASLER_CAPT_NAME "basler" extern camera Basler; +#endif #endif // BASLER_H__ diff --git a/LocCorr_new/cameracapture.c b/LocCorr_new/cameracapture.c index 41edfe1..289c9d2 100644 --- a/LocCorr_new/cameracapture.c +++ b/LocCorr_new/cameracapture.c @@ -22,11 +22,10 @@ #include #include +#include "cameracapture.h" #include "cmdlnopts.h" #include "config.h" #include "debug.h" -#include "fits.h" -#include "grasshopper.h" #include "imagefile.h" #include "improc.h" #include "median.h" @@ -103,21 +102,38 @@ void camdisconnect(){ static void calcexpgain(float newexp){ DBG("recalculate exposition: oldexp=%g, oldgain=%g, newexp=%g", exptime, gain, newexp); + float newgain = gain; +#if 0 while(newexp*1.25 > theconf.minexp){ // increase gain first - if(gain < gainmax - 0.9999){ - gain += 1.; + if(newgain < gainmax - 0.9999){ + newgain += 1.; newexp /= 1.25; }else break; } while(newexp < theconf.minexp){ - if(1.25*newexp < theconf.maxexp && gain > 0.9999){ - gain -= 1.; + if(1.25*newexp < theconf.maxexp && newgain > 0.9999){ + newgain -= 1.; newexp *= 1.25; }else break; } +#endif + if(newexp > exptime){ // need to increase exptime - try to increase gain first + if(newgain < gainmax - 0.9999f){ + newgain += 1.f; + newexp = exptime; // leave exptime unchanged + }else if(newgain < gainmax) newgain = gainmax; + }else{ // decrease -> decrease gain if exptime too small + if(newexp < theconf.minexp){ + if(newgain > 1.f) newgain -= 1.f; + else newgain = 0.f; + } + } + if(newexp < theconf.minexp) newexp = theconf.minexp; else if(newexp > theconf.maxexp) newexp = theconf.maxexp; + LOGDBG("recalc exp from %g to %g; gain from %g to %g", exptime, newexp, gain, newgain); exptime = newexp; + gain = newgain; DBG("New values: exp=%g, gain=%g", exptime, gain); } @@ -129,15 +145,18 @@ static void recalcexp(Image *I){ // check if user changed exposition values if(exptime < theconf.minexp){ exptime = theconf.minexp; + LOGDBG("recalcexp(): minimal exptime"); return; } else if(exptime > theconf.maxexp){ exptime = theconf.maxexp; + LOGDBG("recalcexp(): maximal exptime"); return; } size_t histogram[HISTOSZ]; if(!get_histogram(I, histogram)){ WARNX("Can't calculate histogram"); + LOGWARN("recalcexp(): can't calculate histogram"); return; } int idx100; @@ -153,14 +172,14 @@ static void recalcexp(Image *I){ } if(idx100 > 253){ // exposure too long DBG("Exp too long"); - calcexpgain(0.7*exptime); + calcexpgain(exptime * 0.3f); }else{ // exposure too short if(idx100 > 5){ DBG("Exp too short"); - calcexpgain(exptime * 230. / (float)idx100); + calcexpgain(exptime * 230.f / (float)idx100); }else{ - DBG("divide exp by 2"); - calcexpgain(exptime * 50.); + DBG("increase exptime 50 times"); + calcexpgain(exptime * 50.f); } } } @@ -171,14 +190,31 @@ static int needs_exposure_adjustment(const Image *I, float curr_x, float curr_y) float avg = I->avg_intensity; float dx = fabsf(curr_x - last_centroid_x); float dy = fabsf(curr_y - last_centroid_y); + LOGDBG("avg: %g, curr_x: %g, curr_y: %g", avg, curr_x, curr_y); + // don't change brightness if average value in 5..50 + if(avg > 5.f && avg < 50.f){ + last_avg_intensity = avg; + return FALSE; + } // Adjust if intensity changes >10% or centroid moves >20px or no x/y centroids - if(curr_x < 0.f || curr_y < 0.f) return TRUE; + if(curr_x < 0.f || curr_y < 0.f){ // star wasn't detected + int ret = FALSE; + if(fabsf(avg - last_avg_intensity) > 0.1f * last_avg_intensity || + avg < 0.001f || avg > 200.f){ + LOGDBG("Need adj: image too bad"); + ret = TRUE; + } + last_avg_intensity = avg; + return ret; + } if(fabsf(avg - last_avg_intensity) > 0.1f * last_avg_intensity || dx > 20.f || dy > 20.f){ DBG("avg_cur=%g, avg_last=%g, dx=%g, dy=%g", avg, last_avg_intensity, dx, dy); + LOGDBG("avg_cur=%g, avg_last=%g, dx=%g, dy=%g", avg, last_avg_intensity, dx, dy); last_avg_intensity = avg; last_centroid_x = curr_x; last_centroid_y = curr_y; + LOGDBG("Need adj: changed conditions"); return TRUE; } return FALSE; diff --git a/LocCorr_new/cmdlnopts.c b/LocCorr_new/cmdlnopts.c index e9414e3..057a53b 100644 --- a/LocCorr_new/cmdlnopts.c +++ b/LocCorr_new/cmdlnopts.c @@ -70,7 +70,7 @@ static myoption cmdlnopts[] = { {"logfile", NEED_ARG, NULL, 'l', arg_string, APTR(&G.logfile), _("file to save logs (default: none)")}, {"pidfile", NEED_ARG, NULL, 'P', arg_string, APTR(&G.pidfile), _("pidfile (default: " DEFAULT_PIDFILE ")")}, {"verbose", NO_ARGS, NULL, 'v', arg_none, APTR(&G.verb), _("increase verbosity level of log file (each -v increased by 1)")}, - {"input", NEED_ARG, NULL, 'i', arg_string, APTR(&G.inputname), _("file or directory name for monitoring (or grasshopper/basler for capturing)")}, + {"input", NEED_ARG, NULL, 'i', arg_string, APTR(&G.inputname), _("file or directory name for monitoring (or grasshopper/basler/hikrobot for capturing)")}, {"blackp", NEED_ARG, NULL, 'b', arg_double, APTR(&G.throwpart), _("fraction of black pixels to throw away when make histogram eq")}, // {"radius", NEED_ARG, NULL, 'r', arg_int, APTR(&G.medradius), _("radius of median filter (r=1 -> 3x3, r=2 -> 5x5 etc.)")}, {"equalize", NO_ARGS, NULL, 'e', arg_int, APTR(&G.equalize), _("make historam equalization of saved jpeg")}, diff --git a/LocCorr_new/grasshopper.h b/LocCorr_new/grasshopper.h index 30edfb4..1db6cde 100644 --- a/LocCorr_new/grasshopper.h +++ b/LocCorr_new/grasshopper.h @@ -19,10 +19,12 @@ #ifndef GRASSHOPPER_H__ #define GRASSHOPPER_H__ -#include "cameracapture.h" // `camera` +#ifdef FLYCAP_FOUND +#include "cameracapture.h" // `camera` #define GRASSHOPPER_CAPT_NAME "grasshopper" extern camera GrassHopper; +#endif #endif // GRASSHOPPER_H__ diff --git a/LocCorr_new/hikrobot.c b/LocCorr_new/hikrobot.c new file mode 100644 index 0000000..3e23ab5 --- /dev/null +++ b/LocCorr_new/hikrobot.c @@ -0,0 +1,404 @@ +/* + * This file is part of the loccorr project. + * Copyright 2025 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 "hikrobot.h" + +static struct{ + float maxgain; + float mingain; + float maxbright; + float minbright; + float minexp; + float maxexp; + int maxbin; +} extrvalues = {0}; // extremal values + +static MV_CC_DEVICE_INFO_LIST stDeviceList; +static void *handle = NULL; +static char camname[BUFSIZ] = {0}; +static float exptime = 0.; // exposition time (in seconds) +static uint8_t *pdata = NULL; +static int pdatasz = 0; +static int lastecode = MV_OK; +static frameformat array; // max geometry + +static void printErr(){ + const char *errcode = "unknown error"; + switch(lastecode){ + case MV_E_HANDLE: errcode = "Error or invalid handle "; break; + case MV_E_SUPPORT: errcode = "Not supported function "; break; + case MV_E_BUFOVER: errcode = "Cache is full "; break; + case MV_E_CALLORDER: errcode = "Function calling order error "; break; + case MV_E_PARAMETER: errcode = "Incorrect parameter "; break; + case MV_E_RESOURCE: errcode = "Applying resource failed "; break; + case MV_E_NODATA: errcode = "No data "; break; + case MV_E_PRECONDITION: errcode = "Precondition error, or running environment changed "; break; + case MV_E_VERSION: errcode = "Version mismatches "; break; + case MV_E_NOENOUGH_BUF: errcode = "Insufficient memory "; break; + case MV_E_ABNORMAL_IMAGE: errcode = "Abnormal image, maybe incomplete image because of lost packet "; break; + case MV_E_UNKNOW: errcode = "Unknown error "; break; + case MV_E_GC_GENERIC: errcode = "General error "; break; + case MV_E_GC_ACCESS: errcode = "Node accessing condition error "; break; + case MV_E_ACCESS_DENIED: errcode = "No permission "; break; + case MV_E_BUSY: errcode = "Device is busy, or network disconnected "; break; + case MV_E_NETER: errcode = "Network error "; + } + WARNX("CMOS error: %s", errcode); +} + +#define TRYERR(fn, ...) do{lastecode = MV_CC_ ## fn(handle __VA_OPT__(,) __VA_ARGS__); if(lastecode != MV_OK) printErr(); }while(0) +#define TRY(fn, ...) do{lastecode = MV_CC_ ## fn(handle __VA_OPT__(,) __VA_ARGS__); }while(0) +#define ONERR() if(MV_OK != lastecode) +#define ONOK() if(MV_OK == lastecode) + +static int changeenum(const char *key, uint32_t val){ + if(!handle) return FALSE; + MVCC_ENUMVALUE e; + TRY(GetEnumValue, key, &e); + ONERR(){ + WARNX("Enum '%s' is absent", key); + return FALSE; + } + DBG("Try to change '%s' to %u, cur=%u", key, val, e.nCurValue); + if(e.nCurValue == val) return TRUE; + TRYERR(SetEnumValue, key, val); + ONERR(){ + WARNX("Cant change %s to %d, supported values are:", key, val); + for(int i = 0; i < (int)e.nSupportedNum; ++i){ + fprintf(stderr, "%s%u", i ? ", " : "", e.nSupportValue[i]); + } + fprintf(stderr, "\n"); + return FALSE; + } + TRY(GetEnumValue, key, &e); + ONERR() return FALSE; + if(e.nCurValue == val) return TRUE; + WARNX("New value of '%s' changed to %d, not to %d", key, e.nCurValue, val); + return FALSE; +} + +static int changeint(const char *key, uint32_t val){ + if(!handle) return FALSE; + MVCC_INTVALUE i; + TRY(GetIntValue, key, &i); + ONERR(){ + WARNX("Int '%s' is absent", key); + return FALSE; + } + if(i.nCurValue == val) return TRUE; + TRYERR(SetIntValue, key, val); + ONERR(){ + WARNX("Cant change %s to %u; available range is %u..%u", key, val, i.nMin, i.nMax); + return FALSE; + } + TRY(GetIntValue, key, &i); + ONERR() return FALSE; + if(i.nCurValue == val) return TRUE; + WARNX("New value of '%s' changed to %d, not to %d", key, i.nCurValue, val); + return FALSE; +} + +static int changefloat(const char *key, float val){ + if(!handle) return FALSE; + MVCC_FLOATVALUE f; + TRY(GetFloatValue, key, &f); + ONERR(){ + WARNX("Float '%s' is absent", key); + return FALSE; + } + if(f.fCurValue == val) return TRUE; + TRYERR(SetFloatValue, key, val); + ONERR(){ + WARNX("Cant change %s to %g; available range is %g..%g", key, val, f.fMin, f.fMax); + return FALSE; + } + TRY(GetFloatValue, key, &f); + ONERR() return FALSE; + if(fabs(f.fCurValue - val) < __FLT_EPSILON__) return TRUE; + WARNX("New value of '%s' changed to %g, not to %g", key, f.fCurValue, val); + return FALSE; +} + +static int cam_getgain(float *g){ + if(!handle) return FALSE; + MVCC_FLOATVALUE gain; + TRYERR(GetFloatValue, "Gain", &gain); + ONERR() return FALSE; + if(g) *g = gain.fCurValue; + extrvalues.maxgain = gain.fMax; + extrvalues.mingain = gain.fMin; + DBG("Gain: cur=%g, min=%g, max=%g", gain.fCurValue, gain.fMin, gain.fMax); + return TRUE; +} + +static float maxgain(){ + if(!handle) return 0.; + return extrvalues.maxgain; +} + +static int cam_setgain(float g){ + if(!handle) return FALSE; + return changefloat("Gain", g); +} + +static int cam_getbright(float *b){ + if(!handle) return FALSE; + DBG("Get brightness"); + MVCC_INTVALUE bright; + TRY(GetIntValue, "Brightness", &bright); + ONERR() return FALSE; + if(b) *b = bright.nCurValue; + extrvalues.maxgain = bright.nMax; + extrvalues.mingain = bright.nMin; + DBG("Brightness: cur=%d, min=%d, max=%d", bright.nCurValue, bright.nMin, bright.nMax); + return TRUE; +} + +static int cam_setbright(float b){ + if(!handle) return FALSE; + return changeint("Brightness", (uint32_t)b); +} + +static void cam_closecam(){ + DBG("CAMERA CLOSE"); + if(handle){ + MV_CC_StopGrabbing(handle); + TRY(CloseDevice); + ONERR() WARNX("Can't close opened camera"); + TRY(DestroyHandle); + ONERR() WARNX("Can't destroy camera handle"); + handle = NULL; + } + FREE(pdata); + pdatasz = 0; +} + +static void PrintDeviceInfo(MV_CC_DEVICE_INFO* pstMVDevInfo){ + if(!pstMVDevInfo) return; + if(pstMVDevInfo->nTLayerType == MV_GIGE_DEVICE){ + int nIp1 = ((pstMVDevInfo->SpecialInfo.stGigEInfo.nCurrentIp & 0xff000000) >> 24); + int nIp2 = ((pstMVDevInfo->SpecialInfo.stGigEInfo.nCurrentIp & 0x00ff0000) >> 16); + int nIp3 = ((pstMVDevInfo->SpecialInfo.stGigEInfo.nCurrentIp & 0x0000ff00) >> 8); + int nIp4 = (pstMVDevInfo->SpecialInfo.stGigEInfo.nCurrentIp & 0x000000ff); + printf("Device Model Name: %s\n", pstMVDevInfo->SpecialInfo.stGigEInfo.chModelName); + strncpy(camname, (char*)pstMVDevInfo->SpecialInfo.stGigEInfo.chModelName, BUFSIZ-1); + printf("CurrentIp: %d.%d.%d.%d\n" , nIp1, nIp2, nIp3, nIp4); + printf("UserDefinedName: %s\n\n" , pstMVDevInfo->SpecialInfo.stGigEInfo.chUserDefinedName); + }else if (pstMVDevInfo->nTLayerType == MV_USB_DEVICE){ + printf("Device Model Name: %s\n", pstMVDevInfo->SpecialInfo.stUsb3VInfo.chModelName); + printf("UserDefinedName: %s\n\n", pstMVDevInfo->SpecialInfo.stUsb3VInfo.chUserDefinedName); + strncpy(camname, (char*)pstMVDevInfo->SpecialInfo.stUsb3VInfo.chModelName, BUFSIZ-1); + }else { + printf("Not support.\n"); + } +} + +static int cam_findCCD(){ + DBG("Try to find HIKROBOT cameras .. "); + memset(&stDeviceList, 0, sizeof(MV_CC_DEVICE_INFO_LIST)); + if(MV_OK != MV_CC_EnumDevices(MV_GIGE_DEVICE | MV_USB_DEVICE, &stDeviceList)){ + WARNX("No HIKROBOT cameras found"); + return FALSE; + } + if(stDeviceList.nDeviceNum > 0){ + for(uint32_t i = 0; i < stDeviceList.nDeviceNum; ++i){ + DBG("[device %d]:\n", i); + MV_CC_DEVICE_INFO* pDeviceInfo = stDeviceList.pDeviceInfo[i]; + if(!pDeviceInfo) continue; + PrintDeviceInfo(pDeviceInfo); + } + }else{ + WARNX("No HIKROBOT cameras found"); + return FALSE; + } + return TRUE; +} + +static int connect(){ + if(!cam_findCCD()) return FALSE; + cam_closecam(); + lastecode = MV_CC_CreateHandleWithoutLog(&handle, stDeviceList.pDeviceInfo[0]); + ONERR(){ + WARNX("Can't create camera handle"); + printErr(); + return FALSE; + } + TRYERR(OpenDevice, MV_ACCESS_Exclusive, 0); + ONERR(){ + WARNX("Can't open camera file"); + return FALSE; + } + if(stDeviceList.pDeviceInfo[0]->nTLayerType == MV_GIGE_DEVICE){ + int nPacketSize = MV_CC_GetOptimalPacketSize(handle); + if(nPacketSize > 0){ + if(!changeint("GevSCPSPacketSize", nPacketSize)){ + WARNX("Can't set optimal packet size"); + } + } else{ + WARNX("Can't get optimal packet size"); + } + } + if(!changeenum("BinningHorizontal", 1)){ + WARNX("Can't clear soft H binning"); + return FALSE; + } + if(!changeenum("BinningVertical", 1)){ + WARNX("Can't clear soft V binning"); + return FALSE; + } + if(!changeenum("TriggerMode", MV_TRIGGER_MODE_OFF)){ + WARNX("Can't turn off triggered mode"); + return FALSE; + } + if(!changeenum("AcquisitionMode", MV_ACQ_MODE_SINGLE)){ + WARNX("Can't set acquisition mode to single"); + return FALSE; + } + if(!changeenum("ExposureMode", MV_EXPOSURE_MODE_TIMED)){ + WARNX("Can't change exposure mode to timed"); + return FALSE; + } + if(!changeenum("ExposureAuto", MV_EXPOSURE_AUTO_MODE_OFF)){ + WARNX("Can't turn off auto exposure mode"); + return FALSE; + } + if(!changeenum("GainAuto", 0)){ + WARNX("Can't turn off auto gain"); + return FALSE; + } + if(!changeenum("PixelFormat", PixelType_Gvsp_Mono8) || !changeenum("PixelSize", 8)){ + WARNX("Can't change format to 8 pix"); + return FALSE; + } + cam_getgain(NULL); // get extremal gain values + cam_getbright(NULL); // get extremal brightness values + MVCC_FLOATVALUE FloatValue; + // get extremal exptime values + TRY(GetFloatValue, "ExposureTime", &FloatValue); + ONOK(){ + extrvalues.maxexp = FloatValue.fMax / 1e6; + extrvalues.minexp = FloatValue.fMin / 1e6; + exptime = FloatValue.fCurValue / 1e6; + printf("Min exp: %g s, max exp: %g s\n", extrvalues.minexp, extrvalues.maxexp); + } + MVCC_INTVALUE IntValue; + array.xoff = array.yoff = 0; + int *values[2] = {&array.w, &array.h}; + const char *names[2] = {"WidthMax", "HeightMax"};//, "Width", "Height", "OffsetX", "OffsetY"}; + for(int i = 0; i < 2; ++i){ + TRYERR(GetIntValue, names[i], &IntValue); + ONERR(){ + WARNX("Can't get %s", names[i]); return FALSE; + } + *values[i] = IntValue.nCurValue; + DBG("%s = %d", names[i], *values[i]); + } + pdatasz = array.h * array.w; + DBG("2*w*h = %d", pdatasz); + if(changeenum("DeviceTemperatureSelector", 0)){ + if(!changefloat("DeviceTemperature", -20.)) WARNX("Can't set camtemp to -20"); + } +/* +#ifdef EBUG + MVCC_INTVALUE stParam = {0}; + TRY(GetIntValue, "PayloadSize", &stParam); + ONOK(){DBG("PAYLOAD: %u", stParam.nCurValue);} +#endif + */ + pdata = MALLOC(uint8_t, pdatasz); // allocate max available buffer + return TRUE; +} + +static int geometrylimits(frameformat *max, frameformat *step){ + if(max) *max = array; + if(step) *step = (frameformat){.w = 1, .h = 1, .xoff = 1, .yoff = 1}; + return TRUE; +} + +static int changeformat(frameformat *f){ + if(!f || !handle) return FALSE; + DBG("set geom %dx%d (off: %dx%d)", f->w, f->h, f->xoff, f->yoff); + if(!changeint("Width", f->w)) return FALSE; + + if(!changeint("Height", f->h)) return FALSE; + if(!changeint("OffsetX", f->xoff)) return FALSE; + if(!changeint("OffsetY", f->yoff)) return FALSE; + DBG("Success!"); + return TRUE; +} + +// exptime - in milliseconds! +static int setexp(float e){ + if(!handle) return FALSE; + float eS = e / 1e3; + if(eS > extrvalues.maxexp || eS < extrvalues.minexp){ + WARNX("Wrong exposure time: %fs (should be [%fs..%fs])", eS, + extrvalues.minexp, extrvalues.maxexp); + return FALSE; + } + if(!changefloat("ExposureTime", e * 1e3)) return FALSE; + exptime = eS; + return TRUE; +} + +static int cam_startexp(){ + if(!handle || !pdata) return FALSE; + DBG("Start exposition"); + MV_CC_StopGrabbing(handle); + TRY(StartGrabbing); + ONERR() return FALSE; + return TRUE; +} + +static Image* capture(){ + if(!cam_startexp()) return NULL; + MV_FRAME_OUT_INFO_EX stImageInfo = {0}; // last image info + double starttime = dtime(); + do{ + usleep(100); + double diff = exptime - (dtime() - starttime); + if(diff > 0.) continue; // wait until exposure ends + DBG("diff = %g", diff); + if(diff < -5.0){ // wait much longer than exp lasts + MV_CC_StopGrabbing(handle); + return NULL; + } + TRY(GetOneFrameTimeout, pdata, pdatasz, &stImageInfo, 10); + ONOK() break; + }while(1); + Image *captIma = u8toImage(pdata, stImageInfo.nWidth, stImageInfo.nHeight, stImageInfo.nWidth); + return captIma; +} + +camera Hikrobot = { + .disconnect = cam_closecam, + .connect = connect, + .capture = capture, + .setbrightness = cam_setbright, + .setexp = setexp, + .setgain = cam_setgain, + .setgeometry = changeformat, + .getgeomlimits = geometrylimits, + .getmaxgain = maxgain, +}; diff --git a/LocCorr_new/hikrobot.h b/LocCorr_new/hikrobot.h new file mode 100644 index 0000000..5ba30f4 --- /dev/null +++ b/LocCorr_new/hikrobot.h @@ -0,0 +1,27 @@ +/* + * This file is part of the loccorr project. + * Copyright 2025 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 + +#ifdef MVS_FOUND +#include "cameracapture.h" // `camera` + +#define HIKROBOT_CAPT_NAME "hikrobot" + +extern camera Hikrobot; +#endif diff --git a/LocCorr_new/imagefile.c b/LocCorr_new/imagefile.c index bd0cde6..3b1d3b5 100644 --- a/LocCorr_new/imagefile.c +++ b/LocCorr_new/imagefile.c @@ -34,6 +34,7 @@ #include "draw.h" #include "fits.h" #include "grasshopper.h" +#include "hikrobot.h" #include "imagefile.h" #include "median.h" @@ -103,8 +104,15 @@ static InputType imtype(FILE *f){ */ InputType chkinput(const char *name){ DBG("input name: %s", name); +#ifdef FLYCAP_FOUND if(0 == strcmp(name, GRASSHOPPER_CAPT_NAME)) return T_CAPT_GRASSHOPPER; - else if(0 == strcmp(name, BASLER_CAPT_NAME)) return T_CAPT_BASLER; +#endif +#ifdef BASLER_FOUND + if(0 == strcmp(name, BASLER_CAPT_NAME)) return T_CAPT_BASLER; +#endif +#ifdef MVS_FOUND + if(0 == strcmp(name, HIKROBOT_CAPT_NAME)) return T_CAPT_HIKROBOT; +#endif struct stat fd_stat; stat(name, &fd_stat); if(S_ISDIR(fd_stat.st_mode)){ diff --git a/LocCorr_new/imagefile.h b/LocCorr_new/imagefile.h index aaa06fb..d7c37fa 100644 --- a/LocCorr_new/imagefile.h +++ b/LocCorr_new/imagefile.h @@ -53,7 +53,8 @@ typedef enum{ T_JPEG, T_PNG, T_CAPT_GRASSHOPPER, // capture grasshopper - T_CAPT_BASLER + T_CAPT_BASLER, + T_CAPT_HIKROBOT } InputType; void Image_minmax(Image *I); diff --git a/LocCorr_new/improc.c b/LocCorr_new/improc.c index ebb5887..3d8db63 100644 --- a/LocCorr_new/improc.c +++ b/LocCorr_new/improc.c @@ -30,10 +30,9 @@ #include "debug.h" #include "draw.h" #include "grasshopper.h" -#include "fits.h" +#include "hikrobot.h" #include "improc.h" #include "inotify.h" -#include "median.h" #include "steppers.h" volatile atomic_ullong ImNumber = 0; // GLOBAL: counter of processed images @@ -133,7 +132,9 @@ static void getDeviation(object *curobj){ averflag = 1; if(fXYlog) fprintf(fXYlog, "%.1f\t%.1f\t%.1f\t%.1f", xx, yy, Sx, Sy); process_corrections: + LOGDBG("here"); if(theSteppers){ + DBG("Process corrections"); if(theSteppers->proc_corr && averflag){ if(Sx > XY_TOLERANCE || Sy > XY_TOLERANCE){ LOGDBG("Bad value - not process"); // don't run processing for bad data @@ -142,8 +143,9 @@ process_corrections: } }else{ LOGERR("Lost connection with stepper server"); - ERRX("Lost connection with stepper server"); + WARNX("Lost connection with stepper server"); } + LOGDBG("And there"); XYnewline(); } @@ -410,9 +412,26 @@ int process_input(InputType tp, char *name){ if(tp == T_DIRECTORY){ imagedata = watchdr; return watch_directory(name, process_file); - }else if(tp == T_CAPT_GRASSHOPPER || tp == T_CAPT_BASLER){ - camera *cam = &GrassHopper; - if(tp == T_CAPT_BASLER) cam = &Basler; + }else if(tp == T_CAPT_GRASSHOPPER || tp == T_CAPT_BASLER || tp == T_CAPT_HIKROBOT){ + camera *cam = NULL; + switch(tp){ + case T_CAPT_GRASSHOPPER: +#ifdef FLYCAP_FOUND + cam = &GrassHopper; +#endif + break; + case T_CAPT_BASLER: +#ifdef BASLER_FOUND + cam = &Basler; +#endif + break; + case T_CAPT_HIKROBOT: +#ifdef MVS_FOUND + cam = &Hikrobot; +#endif + break; + default: return FALSE; + } if(!setCamera(cam)){ WARNX("The camera disconnected"); LOGWARN("The camera disconnected"); diff --git a/LocCorr_new/loccorr.conf b/LocCorr_new/loccorr.conf index 210b532..ead90cb 100644 --- a/LocCorr_new/loccorr.conf +++ b/LocCorr_new/loccorr.conf @@ -1,14 +1,14 @@ -Kxu = 45.155 -Kxv = 43.771 -Kyu = 68.838 -Kyv = -70.428 +Kxu = -0.034 +Kxv = -0.706 +Kyu = 1.311 +Kyv = -0.040 brightness = 0.000 eqthrowpart = 0.900 equalize = 0 -expmethod = 0 +expmethod = 1 fbglevel = 100 fixedbg = 0 -fixedexp = 1000.000 +fixedexp = 60.000 focmax = 4000 focmin = 0 gain = 36.000 @@ -40,5 +40,5 @@ vmin = 100 width = 700 xoffset = 408 xtarget = 740.000 -yoffset = 300 +yoffset = 250 ytarget = 730.000 diff --git a/LocCorr_new/main.c b/LocCorr_new/main.c index 2d6e73e..0e3329a 100644 --- a/LocCorr_new/main.c +++ b/LocCorr_new/main.c @@ -104,8 +104,11 @@ static InputType chk_inp(const char *name){ case T_CAPT_BASLER: printf("capture basler camera"); break; + case T_CAPT_HIKROBOT: + printf("hikrobot camera capture"); + break; default: - printf("Unsupported type\n"); + printf("unsupported type\n"); return T_WRONG; } printf("\n"); @@ -215,7 +218,7 @@ int main(int argc, char *argv[]){ } if(!(theSteppers = steppers_connect())){ LOGERR("Steppers server unavailable, can't run"); - ERRX("Steppers server unavailable, can't run"); + WARNX("Steppers server unavailable, can't run"); } if(GP->logXYname) openXYlog(GP->logXYname); LOGMSG("Start application..."); diff --git a/LocCorr_new/steppers.c b/LocCorr_new/steppers.c index 1095bc8..838cdba 100644 --- a/LocCorr_new/steppers.c +++ b/LocCorr_new/steppers.c @@ -203,7 +203,7 @@ static void stp_disconnect(){ } state = STP_DISCONN; sockfd = -1; - LOGWARN("Canserver disconnected"); + LOGWARN("Stepper server disconnected"); } // check if nmot is U/V/F and return FALSE if not @@ -1129,7 +1129,8 @@ steppersproc* steppers_connect(){ if(!stp_connect_server()) return NULL; if(pthread_create(&processingthread, NULL, stp_process_states, NULL)){ LOGERR("pthread_create() for steppers server failed"); - ERR("pthread_create()"); + WARNX("pthread_create()"); + return NULL; } return &steppers; }