added Hikrobot support, fixed some troubles

This commit is contained in:
Edward Emelianov 2025-04-08 17:30:48 +03:00
parent 7a0acd14f5
commit 3090f2c85e
14 changed files with 566 additions and 44 deletions

View File

@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.0) cmake_minimum_required(VERSION 3.20)
set(PROJ loccorr) set(PROJ loccorr)
set(MINOR_VERSION "1") set(MINOR_VERSION "1")
set(MID_VERSION "0") set(MID_VERSION "0")
@ -7,9 +7,6 @@ set(VERSION "${MAJOR_VERSION}.${MID_VERSION}.${MINOR_VERSION}")
project(${PROJ} VERSION ${VERSION} LANGUAGES C) project(${PROJ} VERSION ${VERSION} LANGUAGES C)
# default flags
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99")
set(CMAKE_COLOR_MAKEFILE ON) set(CMAKE_COLOR_MAKEFILE ON)
# here is one of two variants: all .c in directory or .c files in list # 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 # list of options
option(DEBUG "Compile in debug mode" OFF) 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 # 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) set(CMAKE_COLOR_MAKEFILE ON)
# cmake -DDEBUG=yes -> debugging # cmake -DDEBUG=yes -> debugging
@ -45,9 +45,25 @@ set(MODULES usefull_macros cfitsio improc)
# find packages: # find packages:
SET(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}) SET(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR})
find_package(PkgConfig REQUIRED) find_package(PkgConfig REQUIRED)
find_package(FLYCAP REQUIRED) if(GRASSHOPPER)
find_package(BASLER REQUIRED) find_package(FLYCAP REQUIRED)
pkg_check_modules(${PROJ} REQUIRED ${MODULES}) 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) include(FindOpenMP)
if(OPENMP_FOUND) if(OPENMP_FOUND)
@ -73,9 +89,10 @@ message("Install dir prefix: ${CMAKE_INSTALL_PREFIX}")
# exe file # exe file
add_executable(${PROJ} ${SOURCES}) add_executable(${PROJ} ${SOURCES})
# -I # -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 # -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 # -D
add_definitions(${CFLAGS} -DLOCALEDIR=\"${LOCALEDIR}\" add_definitions(${CFLAGS} -DLOCALEDIR=\"${LOCALEDIR}\"
-DPACKAGE_VERSION=\"${VERSION}\" -DGETTEXT_PACKAGE=\"${PROJ}\" -DPACKAGE_VERSION=\"${VERSION}\" -DGETTEXT_PACKAGE=\"${PROJ}\"
@ -83,7 +100,7 @@ add_definitions(${CFLAGS} -DLOCALEDIR=\"${LOCALEDIR}\"
-DMAJOR_VERSION=\"${MAJOR_VERSION}\" -DTHREAD_NUMBER=${PROCESSOR_COUNT}) -DMAJOR_VERSION=\"${MAJOR_VERSION}\" -DTHREAD_NUMBER=${PROCESSOR_COUNT})
# -l # -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 # Installation of the program
INSTALL(TARGETS ${PROJ} DESTINATION "bin") INSTALL(TARGETS ${PROJ} DESTINATION "bin")

View File

@ -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. 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 corrector controller - `multistepper`.
Used CMOS - Basler or Grasshopper. Used CMOS - Basler, Grasshopper or Hikrobot.

View File

@ -19,9 +19,13 @@
#ifndef BASLER_H__ #ifndef BASLER_H__
#define BASLER_H__ #define BASLER_H__
#ifdef BASLER_FOUND
#include "cameracapture.h" // `camera` #include "cameracapture.h" // `camera`
#define BASLER_CAPT_NAME "basler" #define BASLER_CAPT_NAME "basler"
extern camera Basler; extern camera Basler;
#endif
#endif // BASLER_H__ #endif // BASLER_H__

View File

@ -22,11 +22,10 @@
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include "cameracapture.h"
#include "cmdlnopts.h" #include "cmdlnopts.h"
#include "config.h" #include "config.h"
#include "debug.h" #include "debug.h"
#include "fits.h"
#include "grasshopper.h"
#include "imagefile.h" #include "imagefile.h"
#include "improc.h" #include "improc.h"
#include "median.h" #include "median.h"
@ -103,21 +102,38 @@ void camdisconnect(){
static void calcexpgain(float newexp){ static void calcexpgain(float newexp){
DBG("recalculate exposition: oldexp=%g, oldgain=%g, newexp=%g", exptime, gain, 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 while(newexp*1.25 > theconf.minexp){ // increase gain first
if(gain < gainmax - 0.9999){ if(newgain < gainmax - 0.9999){
gain += 1.; newgain += 1.;
newexp /= 1.25; newexp /= 1.25;
}else break; }else break;
} }
while(newexp < theconf.minexp){ while(newexp < theconf.minexp){
if(1.25*newexp < theconf.maxexp && gain > 0.9999){ if(1.25*newexp < theconf.maxexp && newgain > 0.9999){
gain -= 1.; newgain -= 1.;
newexp *= 1.25; newexp *= 1.25;
}else break; }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; if(newexp < theconf.minexp) newexp = theconf.minexp;
else if(newexp > theconf.maxexp) newexp = theconf.maxexp; 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; exptime = newexp;
gain = newgain;
DBG("New values: exp=%g, gain=%g", exptime, gain); DBG("New values: exp=%g, gain=%g", exptime, gain);
} }
@ -129,15 +145,18 @@ static void recalcexp(Image *I){
// check if user changed exposition values // check if user changed exposition values
if(exptime < theconf.minexp){ if(exptime < theconf.minexp){
exptime = theconf.minexp; exptime = theconf.minexp;
LOGDBG("recalcexp(): minimal exptime");
return; return;
} }
else if(exptime > theconf.maxexp){ else if(exptime > theconf.maxexp){
exptime = theconf.maxexp; exptime = theconf.maxexp;
LOGDBG("recalcexp(): maximal exptime");
return; return;
} }
size_t histogram[HISTOSZ]; size_t histogram[HISTOSZ];
if(!get_histogram(I, histogram)){ if(!get_histogram(I, histogram)){
WARNX("Can't calculate histogram"); WARNX("Can't calculate histogram");
LOGWARN("recalcexp(): can't calculate histogram");
return; return;
} }
int idx100; int idx100;
@ -153,14 +172,14 @@ static void recalcexp(Image *I){
} }
if(idx100 > 253){ // exposure too long if(idx100 > 253){ // exposure too long
DBG("Exp too long"); DBG("Exp too long");
calcexpgain(0.7*exptime); calcexpgain(exptime * 0.3f);
}else{ // exposure too short }else{ // exposure too short
if(idx100 > 5){ if(idx100 > 5){
DBG("Exp too short"); DBG("Exp too short");
calcexpgain(exptime * 230. / (float)idx100); calcexpgain(exptime * 230.f / (float)idx100);
}else{ }else{
DBG("divide exp by 2"); DBG("increase exptime 50 times");
calcexpgain(exptime * 50.); 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 avg = I->avg_intensity;
float dx = fabsf(curr_x - last_centroid_x); float dx = fabsf(curr_x - last_centroid_x);
float dy = fabsf(curr_y - last_centroid_y); 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 // 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 || if(fabsf(avg - last_avg_intensity) > 0.1f * last_avg_intensity ||
dx > 20.f || dy > 20.f){ dx > 20.f || dy > 20.f){
DBG("avg_cur=%g, avg_last=%g, dx=%g, dy=%g", avg, last_avg_intensity, dx, dy); 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_avg_intensity = avg;
last_centroid_x = curr_x; last_centroid_x = curr_x;
last_centroid_y = curr_y; last_centroid_y = curr_y;
LOGDBG("Need adj: changed conditions");
return TRUE; return TRUE;
} }
return FALSE; return FALSE;

View File

@ -70,7 +70,7 @@ static myoption cmdlnopts[] = {
{"logfile", NEED_ARG, NULL, 'l', arg_string, APTR(&G.logfile), _("file to save logs (default: none)")}, {"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 ")")}, {"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)")}, {"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")}, {"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.)")}, // {"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")}, {"equalize", NO_ARGS, NULL, 'e', arg_int, APTR(&G.equalize), _("make historam equalization of saved jpeg")},

View File

@ -19,10 +19,12 @@
#ifndef GRASSHOPPER_H__ #ifndef GRASSHOPPER_H__
#define GRASSHOPPER_H__ #define GRASSHOPPER_H__
#include "cameracapture.h" // `camera`
#ifdef FLYCAP_FOUND
#include "cameracapture.h" // `camera`
#define GRASSHOPPER_CAPT_NAME "grasshopper" #define GRASSHOPPER_CAPT_NAME "grasshopper"
extern camera GrassHopper; extern camera GrassHopper;
#endif
#endif // GRASSHOPPER_H__ #endif // GRASSHOPPER_H__

404
LocCorr_new/hikrobot.c Normal file
View File

@ -0,0 +1,404 @@
/*
* This file is part of the loccorr project.
* Copyright 2025 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 <MvCameraControl.h>
#include <math.h>
#include <stdio.h>
#include <string.h>
#include <usefull_macros.h>
#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,
};

27
LocCorr_new/hikrobot.h Normal file
View File

@ -0,0 +1,27 @@
/*
* This file is part of the loccorr project.
* Copyright 2025 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/>.
*/
#pragma once
#ifdef MVS_FOUND
#include "cameracapture.h" // `camera`
#define HIKROBOT_CAPT_NAME "hikrobot"
extern camera Hikrobot;
#endif

View File

@ -34,6 +34,7 @@
#include "draw.h" #include "draw.h"
#include "fits.h" #include "fits.h"
#include "grasshopper.h" #include "grasshopper.h"
#include "hikrobot.h"
#include "imagefile.h" #include "imagefile.h"
#include "median.h" #include "median.h"
@ -103,8 +104,15 @@ static InputType imtype(FILE *f){
*/ */
InputType chkinput(const char *name){ InputType chkinput(const char *name){
DBG("input name: %s", name); DBG("input name: %s", name);
#ifdef FLYCAP_FOUND
if(0 == strcmp(name, GRASSHOPPER_CAPT_NAME)) return T_CAPT_GRASSHOPPER; 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; struct stat fd_stat;
stat(name, &fd_stat); stat(name, &fd_stat);
if(S_ISDIR(fd_stat.st_mode)){ if(S_ISDIR(fd_stat.st_mode)){

View File

@ -53,7 +53,8 @@ typedef enum{
T_JPEG, T_JPEG,
T_PNG, T_PNG,
T_CAPT_GRASSHOPPER, // capture grasshopper T_CAPT_GRASSHOPPER, // capture grasshopper
T_CAPT_BASLER T_CAPT_BASLER,
T_CAPT_HIKROBOT
} InputType; } InputType;
void Image_minmax(Image *I); void Image_minmax(Image *I);

View File

@ -30,10 +30,9 @@
#include "debug.h" #include "debug.h"
#include "draw.h" #include "draw.h"
#include "grasshopper.h" #include "grasshopper.h"
#include "fits.h" #include "hikrobot.h"
#include "improc.h" #include "improc.h"
#include "inotify.h" #include "inotify.h"
#include "median.h"
#include "steppers.h" #include "steppers.h"
volatile atomic_ullong ImNumber = 0; // GLOBAL: counter of processed images volatile atomic_ullong ImNumber = 0; // GLOBAL: counter of processed images
@ -133,7 +132,9 @@ static void getDeviation(object *curobj){
averflag = 1; averflag = 1;
if(fXYlog) fprintf(fXYlog, "%.1f\t%.1f\t%.1f\t%.1f", xx, yy, Sx, Sy); if(fXYlog) fprintf(fXYlog, "%.1f\t%.1f\t%.1f\t%.1f", xx, yy, Sx, Sy);
process_corrections: process_corrections:
LOGDBG("here");
if(theSteppers){ if(theSteppers){
DBG("Process corrections");
if(theSteppers->proc_corr && averflag){ if(theSteppers->proc_corr && averflag){
if(Sx > XY_TOLERANCE || Sy > XY_TOLERANCE){ if(Sx > XY_TOLERANCE || Sy > XY_TOLERANCE){
LOGDBG("Bad value - not process"); // don't run processing for bad data LOGDBG("Bad value - not process"); // don't run processing for bad data
@ -142,8 +143,9 @@ process_corrections:
} }
}else{ }else{
LOGERR("Lost connection with stepper server"); LOGERR("Lost connection with stepper server");
ERRX("Lost connection with stepper server"); WARNX("Lost connection with stepper server");
} }
LOGDBG("And there");
XYnewline(); XYnewline();
} }
@ -410,9 +412,26 @@ int process_input(InputType tp, char *name){
if(tp == T_DIRECTORY){ if(tp == T_DIRECTORY){
imagedata = watchdr; imagedata = watchdr;
return watch_directory(name, process_file); return watch_directory(name, process_file);
}else if(tp == T_CAPT_GRASSHOPPER || tp == T_CAPT_BASLER){ }else if(tp == T_CAPT_GRASSHOPPER || tp == T_CAPT_BASLER || tp == T_CAPT_HIKROBOT){
camera *cam = &GrassHopper; camera *cam = NULL;
if(tp == T_CAPT_BASLER) cam = &Basler; 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)){ if(!setCamera(cam)){
WARNX("The camera disconnected"); WARNX("The camera disconnected");
LOGWARN("The camera disconnected"); LOGWARN("The camera disconnected");

View File

@ -1,14 +1,14 @@
Kxu = 45.155 Kxu = -0.034
Kxv = 43.771 Kxv = -0.706
Kyu = 68.838 Kyu = 1.311
Kyv = -70.428 Kyv = -0.040
brightness = 0.000 brightness = 0.000
eqthrowpart = 0.900 eqthrowpart = 0.900
equalize = 0 equalize = 0
expmethod = 0 expmethod = 1
fbglevel = 100 fbglevel = 100
fixedbg = 0 fixedbg = 0
fixedexp = 1000.000 fixedexp = 60.000
focmax = 4000 focmax = 4000
focmin = 0 focmin = 0
gain = 36.000 gain = 36.000
@ -40,5 +40,5 @@ vmin = 100
width = 700 width = 700
xoffset = 408 xoffset = 408
xtarget = 740.000 xtarget = 740.000
yoffset = 300 yoffset = 250
ytarget = 730.000 ytarget = 730.000

View File

@ -104,8 +104,11 @@ static InputType chk_inp(const char *name){
case T_CAPT_BASLER: case T_CAPT_BASLER:
printf("capture basler camera"); printf("capture basler camera");
break; break;
case T_CAPT_HIKROBOT:
printf("hikrobot camera capture");
break;
default: default:
printf("Unsupported type\n"); printf("unsupported type\n");
return T_WRONG; return T_WRONG;
} }
printf("\n"); printf("\n");
@ -215,7 +218,7 @@ int main(int argc, char *argv[]){
} }
if(!(theSteppers = steppers_connect())){ if(!(theSteppers = steppers_connect())){
LOGERR("Steppers server unavailable, can't run"); 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); if(GP->logXYname) openXYlog(GP->logXYname);
LOGMSG("Start application..."); LOGMSG("Start application...");

View File

@ -203,7 +203,7 @@ static void stp_disconnect(){
} }
state = STP_DISCONN; state = STP_DISCONN;
sockfd = -1; sockfd = -1;
LOGWARN("Canserver disconnected"); LOGWARN("Stepper server disconnected");
} }
// check if nmot is U/V/F and return FALSE if not // 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(!stp_connect_server()) return NULL;
if(pthread_create(&processingthread, NULL, stp_process_states, NULL)){ if(pthread_create(&processingthread, NULL, stp_process_states, NULL)){
LOGERR("pthread_create() for steppers server failed"); LOGERR("pthread_create() for steppers server failed");
ERR("pthread_create()"); WARNX("pthread_create()");
return NULL;
} }
return &steppers; return &steppers;
} }