diff --git a/3rdparty/CMakeLists.txt b/3rdparty/CMakeLists.txt index 1753dd00e9..73c80eb241 100644 --- a/3rdparty/CMakeLists.txt +++ b/3rdparty/CMakeLists.txt @@ -87,6 +87,7 @@ option(WITH_RADIOSIM "Install RadioSim Receiver" On) option(WITH_GPSNMEA "Install GPS NMEA Driver" On) option(WITH_ARMADILLO "Install Armadillo & Platypus Driver" On) option(WITH_FXLOAD "Install FX3 compatible fxload tool" Off) +option(WITH_NIGHTSCAPE "Install Nightscape 8300 Driver" On) find_package(FFmpeg) @@ -307,6 +308,10 @@ if (WITH_WEBCAM) add_subdirectory(indi-webcam) endif() +if (WITH_NIGHTSCAPE) +add_subdirectory(indi-nightscape) +endif(WITH_NIGHTSCAPE) + # Check if libraries are found. If not, we must build them, install them, THEN run CMake again to build and instal the drivers. If all the libraraies are installed, then we build and install the drivers only now. if (LIBRARIES_FOUND) message(STATUS "############################################################################") diff --git a/3rdparty/indi-nightscape/CMakeLists.txt b/3rdparty/indi-nightscape/CMakeLists.txt new file mode 100644 index 0000000000..79c3e0f2fe --- /dev/null +++ b/3rdparty/indi-nightscape/CMakeLists.txt @@ -0,0 +1,98 @@ +cmake_minimum_required(VERSION 3.0) +PROJECT(indi_nightscape CXX C) + +LIST(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake_modules/") +LIST(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../cmake_modules/") +include(GNUInstallDirs) + +find_package(CFITSIO REQUIRED) +find_package(INDI REQUIRED) +find_package(Threads REQUIRED) +FIND_PACKAGE(D2XX) +FIND_PACKAGE(USB1 REQUIRED) +FIND_PACKAGE(FTDI1 REQUIRED) + +IF (D2XX_FOUND) +set(HAVE_D2XX 1) +SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DHAVE_D2XX") +SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DHAVE_D2XX") + +ENDIF() + +IF (APPLE) +SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_DARWIN_C_SOURCE") +SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_DARWIN_C_SOURCE") +ENDIF() + + +set(INDI_NIGHTSCAPE_VERSION_MAJOR 0) +set(INDI_NIGHTSCAPE_VERSION_MINOR 1) + +#set (HAVE_SERIAL 1) + +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/indi_nightscape.xml.cmake ${CMAKE_CURRENT_BINARY_DIR}/indi_nightscape.xml ) + + +include_directories( ${CMAKE_CURRENT_BINARY_DIR}) +include_directories( ${CMAKE_CURRENT_SOURCE_DIR}) +include_directories( ${INDI_INCLUDE_DIR}) +include_directories( ${CFITSIO_INCLUDE_DIR}) +include_directories( ${USB1_INCLUDE_DIRS}) +include_directories( ${FTDI1_INCLUDE_DIRS}) + +include(CMakeCommon) + + +SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-error") +SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wno-error") + +SET(indinightscape_CORE + ${CMAKE_CURRENT_SOURCE_DIR}/nschannel.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/nschannel-u.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/nsmsg.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/nsdownload.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/nsstatus.cpp) + +IF(HAVE_D2XX) + SET(indinightscape_CORE + ${indinightscape_CORE} + ${CMAKE_CURRENT_SOURCE_DIR}/nschannel-ftd.cpp) +ENDIF() + +IF(HAVE_SERIAL) + SET(indinightscape_CORE + ${indinightscape_CORE} + ${CMAKE_CURRENT_SOURCE_DIR}/nschannel-ser.cpp) + + SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DHAVE_SERIAL") + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DHAVE_SERIAL") + +ENDIF() + +SET(indinightscape_SRCS + ${indinightscape_CORE} + ${CMAKE_CURRENT_SOURCE_DIR}/nightscape.cpp) + +SET(nstest_SRCS + ${indinightscape_CORE} + ${CMAKE_CURRENT_SOURCE_DIR}/nstest-main.cpp) + + +add_executable(indi_nightscape_ccd ${indinightscape_SRCS}) + +add_executable(nstest ${nstest_SRCS}) + +IF(HAVE_D2XX) + target_link_libraries(indi_nightscape_ccd ${INDI_LIBRARIES} ${CFITSIO_LIBRARIES} ${D2XX_LIBRARIES} ${FTDI1_LIBRARIES} ${USB1_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) + + target_link_libraries(nstest ${D2XX_LIBRARIES} ${FTDI1_LIBRARIES} ${USB1_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) +ELSE() + target_link_libraries(indi_nightscape_ccd ${INDI_LIBRARIES} ${CFITSIO_LIBRARIES} ${FTDI1_LIBRARIES} ${USB1_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) + + target_link_libraries(nstest ${FTDI1_LIBRARIES} ${USB1_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) +ENDIF() + +install(TARGETS indi_nightscape_ccd RUNTIME DESTINATION bin ) + +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/indi_nightscape.xml DESTINATION ${INDI_DATA_DIR}) + diff --git a/3rdparty/indi-nightscape/indi_nightscape.xml.cmake b/3rdparty/indi-nightscape/indi_nightscape.xml.cmake new file mode 100644 index 0000000000..3249d6af5d --- /dev/null +++ b/3rdparty/indi-nightscape/indi_nightscape.xml.cmake @@ -0,0 +1,9 @@ + + + + + indi_nighscape_ccd + @INDI_NIGHTSCAPE_VERSION_MAJOR@.@INDI_NIGHTSCAPE_VERSION_MINOR@ + + + diff --git a/3rdparty/indi-nightscape/kaf_constants.h b/3rdparty/indi-nightscape/kaf_constants.h new file mode 100644 index 0000000000..5c7bba6996 --- /dev/null +++ b/3rdparty/indi-nightscape/kaf_constants.h @@ -0,0 +1,22 @@ +#ifndef KAF_CONSTANTS_H +#define KAF_CONSTANTS_H + +#define KAF8300_MAX_X 3448 +#define KAF8300_MAX_Y 2574 +//Trailing 2 dummy 1 CTE 3 dummy 6 dark dummy 39 dark 8 dar dummy 4 blue 16 active buf +#define KAF8300_PREAMBLE (2 + 1 + 3 + 6 + 39 +8 + 4+ + 16) +//Leading 16 active 4 blue 5 dark dummy 5 dummy 1 active 8 dummy 4 virtual dummy +#define KAF8300_POSTAMBLE (4 + 8+ 1 + 5 + 5 + 4 + 16) +#define KAF8300_ACTIVE_X 3326 + +//trailing 6 dark dummy 12 dark 8 dark dumy 4 blue 16 active buf +#define KAF8300_Y_PREAMBLE (6 + 12 + 8 + 4 + 16) +//Leading 1 active cte 3 dark dummy 4 blue 16 active +#define KAF8300_Y_POSTAMBLE (1+3+4+16) + +#define IMG_Y 2506 +#define IMG_Y_HALF 1254 + +#define CMD_SIZE 16 + +#endif \ No newline at end of file diff --git a/3rdparty/indi-nightscape/nightscape.cpp b/3rdparty/indi-nightscape/nightscape.cpp new file mode 100644 index 0000000000..194537a4a4 --- /dev/null +++ b/3rdparty/indi-nightscape/nightscape.cpp @@ -0,0 +1,687 @@ +/* + Based on INDI Developers Manual Tutorial #3 + + "Nighscape 8300 CCD Driver" + + Refer to README, which contains instruction on how to build this driver, and use it + with an INDI-compatible client. + +*/ + +/** \file NightscapeCCD.cpp + \brief Construct a basic INDI CCD device It also generates an image and uploads it as a FITS file. + \author Dirk Niggemann + + \example NightscapeCCD.cpp + A simple CCD device that can capture images and control temperature. It returns a FITS image to the client. To build drivers for complex CCDs, please + refer to the INDI Generic CCD driver template in INDI SVN (under 3rdparty). +*/ + + +#ifdef _DARWIN_C_SOURCE +#define ___secure_getenv getenv +#else +#define ___secure_getenv secure_getenv +#endif + +#include "nightscape.h" + +#include +#include +#include +#include +#include +#include + +/* Macro shortcut to CCD temperature value */ +#define currentCCDTemperature TemperatureN[0].value + +std::unique_ptr nightscapeCCD(new NightscapeCCD()); + + +static int drop_root_privileges(void) { // returns 0 on success and -1 on failure + gid_t gid; + uid_t uid; + DO_DBG("%s\n", "privilege drop"); + // no need to "drop" the privileges that you don't have in the first place! + if (getuid() != 0) { + return 0; + } + + // when your program is invoked with sudo, getuid() will return 0 and you + // won't be able to drop your privileges + if ((uid = getuid()) == 0) { + const char *sudo_uid = ___secure_getenv("SUDO_UID"); + if (sudo_uid == NULL) { + DO_ERR("%s\n", "environment variable `SUDO_UID` not found"); + return -1; + } + errno = 0; + uid = (uid_t) strtoll(sudo_uid, NULL, 10); + if (errno != 0) { + DO_ERR("under-/over-flow in converting `SUDO_UID` to integer %s", strerror(errno)); + return -1; + } + } + + // again, in case your program is invoked using sudo + if ((gid = getgid()) == 0) { + const char *sudo_gid = ___secure_getenv("SUDO_GID"); + if (sudo_gid == NULL) { + DO_ERR("%s\n", "environment variable `SUDO_GID` not found"); + return -1; + } + errno = 0; + gid = (gid_t) strtoll(sudo_gid, NULL, 10); + if (errno != 0) { + DO_ERR("under-/over-flow in converting `SUDO_GID` to integer %s", strerror(errno)); + return -1; + } + } + + if (setgid(gid) != 0) { + DO_ERR("setgid %s", strerror(errno)); + return -1; + } + if (setuid(uid) != 0) { + DO_ERR("setuid %s", strerror(errno)); + return -1; + } + + // change your directory to somewhere else, just in case if you are in a + // root-owned one (e.g. /root) + if (chdir("/") != 0) { + DO_ERR("chdir %s", strerror(errno)); + return -1; + } + + // check if we successfully dropped the root privileges + if (setuid(0) == 0 || seteuid(0) == 0) { + DO_ERR("%s\n", "could not drop root privileges!"); + return -1; + } + + return 0; +} + + +void ISGetProperties(const char *dev) +{ + nightscapeCCD->ISGetProperties(dev); +} + +void ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) +{ + nightscapeCCD->ISNewSwitch(dev, name, states, names, n); +} + +void ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n) +{ + nightscapeCCD->ISNewText(dev, name, texts, names, n); +} + +void ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) +{ + nightscapeCCD->ISNewNumber(dev, name, values, names, n); +} + +void ISNewBLOB(const char *dev, const char *name, int sizes[], int blobsizes[], char *blobs[], char *formats[], + char *names[], int n) +{ + INDI_UNUSED(dev); + INDI_UNUSED(name); + INDI_UNUSED(sizes); + INDI_UNUSED(blobsizes); + INDI_UNUSED(blobs); + INDI_UNUSED(formats); + INDI_UNUSED(names); + INDI_UNUSED(n); +} + +void ISSnoopDevice(XMLEle *root) +{ + nightscapeCCD->ISSnoopDevice(root); +} + +/************************************************************************************** +** Client is asking us to establish connection to the device +***************************************************************************************/ +bool NightscapeCCD::Connect() +{ +#ifdef HAVE_D2XX + if (useD2xx == 1) { cn = new NsChannelFTD(camnum); + } + else + #endif + if (useD2xx == 0) + { + cn = new NsChannelU(camnum); + } +#ifdef HAVE_SERIAL + else + { + cn = new NsChannelSER(camnum); + } +#endif + if (cn->open() < 0) { + IDMessage(getDeviceName(), "open failed!"); + delete cn; + return false; + } else { + IDMessage(getDeviceName(), "opened successfully!"); + } + + m = new Nsmsg(cn); + dn = new NsDownload(cn); + st = new NsStatus(m, dn); + if(!m->inquiry()) { + IDMessage(getDeviceName(), "inquiry failed!"); + delete cn; + delete dn; + delete st; + return false; + + } else { + IDMessage(getDeviceName(), "Firmware ver %s", m->getFirmwareVer()); + } + dn->setFrameYBinning(1); + dn->setFrameXBinning(1); + + dn->setIncrement(1); + dn->setFbase(""); + dn->setNumExp(99999); + dn->setImgWrite(false); + if (useD2xx == 0) { + dn->setZeroReads(100); + } + if (useD2xx == 2) { + //dn->setZeroReads(100); + } + dn->startThread(); + st->startThread(); + // Let's set a timer that checks teleCCDs status every POLLMS milliseconds. + SetTimer(POLLMS); + return true; +} + +/************************************************************************************** +** Client is asking us to terminate connection to the device +***************************************************************************************/ +bool NightscapeCCD::Disconnect() +{ + IDMessage(getDeviceName(), "Nightscape CCD disconnected successfully!"); + m->abort(); + + dn->stopThread(); + st->stopThread(); + //m->sendfan(deffanspeed); + cn->close(); + delete m; + + delete cn; + delete dn; + m = nullptr; + dn = nullptr; + cn = nullptr; + + return true; +} + +/************************************************************************************** +** INDI is asking us for our default device name +***************************************************************************************/ +const char *NightscapeCCD::getDefaultName() +{ + return "Nightscape 8300"; +} + +/************************************************************************************** +** INDI is asking us to init our properties. +***************************************************************************************/ +bool NightscapeCCD::initProperties() +{ + setpriority(PRIO_PROCESS, getpid(), -20); + drop_root_privileges(); + + // Must init parent properties first! + INDI::CCD::initProperties(); + IUFillSwitch(&CoolerS[0], "COOLER_ON", "ON", cooler ? ISS_ON : ISS_OFF); + IUFillSwitch(&CoolerS[1], "COOLER_OFF", "OFF", cooler ? ISS_OFF : ISS_ON ); + + IUFillSwitchVector(&CoolerSP, CoolerS, 2, getDeviceName(), "CCD_COOLER", "Cooler", + MAIN_CONTROL_TAB, IP_WO, ISR_1OFMANY, 0, IPS_IDLE); + + IUFillSwitch(&FanS[0], "FANOFF", "Off", fanspeed == 1 ? ISS_ON: ISS_OFF); + IUFillSwitch(&FanS[1], "FANQUIET", "Quiet", fanspeed == 2 ? ISS_ON: ISS_OFF); + IUFillSwitch(&FanS[2], "FANFULL", "Full", fanspeed == 3 ? ISS_ON: ISS_OFF); + IUFillSwitchVector(&FanSP, FanS, 3, getDeviceName(), "CCD_FAN", "Fan", + MAIN_CONTROL_TAB, IP_RW, ISR_1OFMANY, 60, IPS_IDLE); + + + IUFillNumber(&CamNumN[0], "CAMNUM", "Camera Number", "%4.0f", 1.0, 4.0, 1.0, 1.0); + IUFillNumberVector(&CamNumNP, CamNumN, 1, getDeviceName(), "CAMNUM", "Camera Number", + MAIN_CONTROL_TAB, IP_RW, 60, IPS_IDLE); + + defineNumber(&CamNumNP); + + IUFillSwitch(&D2xxS[0], "USEFTDI", "libftdi", useD2xx == 0 ? ISS_ON: ISS_OFF); +#ifdef HAVE_D2XX + IUFillSwitch(&D2xxS[1], "USED2XX", "libd2xx", useD2xx== 1 ? ISS_ON: ISS_OFF); +#ifdef HAVE_SERIAL + IUFillSwitch(&D2xxS[2], "USESERIAL", "Serial", useD2xx == 2? ISS_ON: ISS_OFF); +#endif +#else +#ifdef HAVE_SERIAL + IUFillSwitch(&D2xxS[1], "USESERIAL", "Serial", useD2xx == 2? ISS_ON: ISS_OFF); +#endif +#endif + +#ifdef HAVE_D2XX +#ifdef HAVE_SERIAL + IUFillSwitchVector(&D2xxSP, D2xxS, 3, getDeviceName(), "CCD_LIBRARY", "USB Library", + MAIN_CONTROL_TAB, IP_WO, ISR_1OFMANY, 60, IPS_IDLE); +#else + IUFillSwitchVector(&D2xxSP, D2xxS, 2, getDeviceName(), "CCD_LIBRARY", "USB Library", + MAIN_CONTROL_TAB, IP_WO, ISR_1OFMANY, 60, IPS_IDLE); +#endif +#else +#ifdef HAVE_SERIAL/ + IUFillSwitchVector(&D2xxSP, D2xxS, 2, getDeviceName(), "CCD_LIBRARY", "USB Library", + MAIN_CONTROL_TAB, IP_WO, ISR_1OFMANY, 60, IPS_IDLE); +#else + IUFillSwitchVector(&D2xxSP, D2xxS, 1, getDeviceName(), "CCD_LIBRARY", "USB Library", + MAIN_CONTROL_TAB, IP_WO, ISR_1OFMANY, 60, IPS_IDLE); +#endif +#endif + defineSwitch(&D2xxSP); + + // We set the CCD capabilities + uint32_t cap = CCD_CAN_ABORT | CCD_CAN_BIN | CCD_CAN_SUBFRAME | CCD_HAS_COOLER | CCD_HAS_SHUTTER ; + if (bayer) { + cap |= CCD_HAS_BAYER; + IUSaveText(&BayerT[0], "0"); + IUSaveText(&BayerT[1], "1"); + IUSaveText(&BayerT[2], "RGGB"); + + } + SetCCDCapability(cap); + + // Add Debug, Simulator, and Configuration controls + addAuxControls(); + + setDefaultPollingPeriod(500); + + return true; +} + +/******************************************************************************************** +** INDI is asking us to update the properties because there is a change in CONNECTION status +** This fucntion is called whenever the device is connected or disconnected. +*********************************************************************************************/ +bool NightscapeCCD::updateProperties() +{ + // Call parent update properties first + INDI::CCD::updateProperties(); + + if (isConnected()) + { + // Let's get parameters now from CCD + setupParams(); + defineSwitch(&CoolerSP); + defineSwitch(&FanSP); + + // Start the timer + SetTimer(POLLMS); + } + else + { + deleteProperty(FanSP.name); + deleteProperty(CoolerSP.name); + } + + return true; +} + +/************************************************************************************** +** Setting up CCD parameters +***************************************************************************************/ +void NightscapeCCD::setupParams() +{ + // Our CCD is an 16 bit CCD, 1280x1024 resolution, with 5.4um square pixels. + SetCCDParams(KAF8300_ACTIVE_X, IMG_Y, 16, 5.4, 5.4); + PrimaryCCD.setMinMaxStep("CCD_EXPOSURE", "CCD_EXPOSURE_VALUE", 0.001, 3600, 1, false); + + // Let's calculate how much memory we need for the primary CCD buffer + int nbuf; + nbuf = PrimaryCCD.getXRes() * PrimaryCCD.getYRes() * PrimaryCCD.getBPP() / 8; + nbuf += 512; // leave a little extra at the end + PrimaryCCD.setFrameBufferSize(nbuf); + //IDLog("fbuf size %d\n",nbuf); + + IUResetSwitch(&FanSP); + FanS[fanspeed - 1].s = ISS_ON; + defineSwitch(&FanSP); + IUResetSwitch(&CoolerSP); + CoolerS[!cooler].s = ISS_ON; + defineSwitch(&CoolerSP); + + +} + +/************************************************************************************** +** Client is asking us to start an exposure +***************************************************************************************/ +bool NightscapeCCD::StartExposure(float duration) +{ + ExposureRequest = duration; + + // Since we have only have one CCD with one chip, we set the exposure duration of the primary CCD + PrimaryCCD.setExposureDuration(duration); + + gettimeofday(&ExpStart, nullptr); + //int zonestart = (IMG_Y - PrimaryCCD.getSubH()) /2; + int zonestart = PrimaryCCD.getSubY(); + int zonelen = PrimaryCCD.getSubH(); + int framediv = PrimaryCCD.getBinY(); + PrimaryCCD.setPixelSize(5.4*PrimaryCCD.getBinX(), 5.4*PrimaryCCD.getBinY()); + dn->setImgSize(m->getRawImgSize(zonestart,zonelen,framediv)); + dn->setFrameYBinning(framediv); + dn->setFrameXBinning(PrimaryCCD.getBinX()); + m->sendzone(zonestart , zonelen, framediv); + INDI::CCDChip::CCD_FRAME ft = PrimaryCCD.getFrameType(); + if (ft == INDI::CCDChip::DARK_FRAME || ft == INDI::CCDChip::BIAS_FRAME) dark = true; + else dark = false; + m->senddur(duration, framediv, dark); + InExposure = true; + // We're done + return true; +} + +/************************************************************************************** +** Client is asking us to abort an exposure +***************************************************************************************/ +bool NightscapeCCD::AbortExposure() +{ + InExposure = false; + m->abort(); + return true; +} + +/************************************************************************************** +** Client is asking us to set a new temperature +***************************************************************************************/ +int NightscapeCCD::SetTemperature(double temperature) +{ + setTemp = TemperatureRequest = temperature; + m->sendtemp(setTemp, cooler); + dn->setSetTemp(setTemp); + ntemps = 0; + backoffs = 1; + // 0 means it will take a while to change the temperature + return 0; +} + +/************************************************************************************** +** How much longer until exposure is done? +***************************************************************************************/ +float NightscapeCCD::CalcTimeLeft() +{ + double timesince; + double timeleft; + struct timeval now { 0, 0 }; + gettimeofday(&now, nullptr); + + timesince = (double)(now.tv_sec * 1000.0 + now.tv_usec / 1000) - + (double)(ExpStart.tv_sec * 1000.0 + ExpStart.tv_usec / 1000); + timesince = timesince / 1000; + + timeleft = ExposureRequest - timesince; + return timeleft; +} + +/************************************************************************************** +** Main device loop. We check for exposure and temperature progress here +***************************************************************************************/ +void NightscapeCCD::TimerHit() +{ + long timeleft; + + if (!isConnected()) + return; // No need to reset timer if we are not connected anymore + + if (InExposure) + { + timeleft = CalcTimeLeft(); + + // Less than a 0.1 second away from exposure completion + // This is an over simplified timing method, check CCDSimulator and simpleCCD for better timing checks + if (timeleft < 0.1) + { + /* We're done exposing */ + IDMessage(getDeviceName(), "Exposure done, starting readout..."); + + // Set exposure left to zero + PrimaryCCD.setExposureLeft(0); + + // We're no longer exposing... + InExposure = false; + InReadout = true; + /* grab and save image */ + st->doStatus(); + + + } + else + { // Just update time left in client + PrimaryCCD.setExposureLeft(timeleft); + } + } + if (InReadout) { + stat = st->getStatus(); + if(oldstat == 2 && stat == 0) { + IDMessage(getDeviceName(), "Starting download..."); + InReadout = false; + InDownload = true; + } + oldstat = stat; + + } + if (InDownload && !dn->inDownload()) { + IDMessage(getDeviceName(), "download done..."); + InDownload = false; + grabImage(); + } + + + // TemperatureNP is defined in INDI::CCD + switch (TemperatureNP.s) + { + case IPS_IDLE: + case IPS_OK: + break; + + case IPS_BUSY: + if (InDownload || InReadout || InExposure) break; + if(ntemps % backoffs == 0) { + currentCCDTemperature = m->rcvtemp(); + backoffs *= 2; + if(backoffs > 32) backoffs = 32; + } + ntemps++; + dn->setActTemp(currentCCDTemperature); + + /* If target temperature is higher, then increase current CCD temperature */ + if (fabs(currentCCDTemperature - TemperatureRequest) < 0.1) + + { + TemperatureNP.s = IPS_OK; + IDSetNumber(&TemperatureNP, "Target temperature reached."); + + break; + } + + IDSetNumber(&TemperatureNP, nullptr); + + break; + + case IPS_ALERT: + break; + } + if (!InReadout && !InDownload) { + int stat = m->rcvstat(); + if (oldstat != stat) { + DO_DBG("Status change %d\n", stat); + } + oldstat = stat; +// if(oldstat == 2 && stat == 0) { +// IDMessage(getDeviceName(), "Starting download..."); +// dn->doDownload(); +// +// InReadout = false; +// InDownload = true; +// } else { +// oldstat = stat; +// } + } + SetTimer(POLLMS); +} + +/************************************************************************************** +** copy image from internal raw buffer +***************************************************************************************/ +void NightscapeCCD::grabImage() +{ + // Let's get a pointer to the frame buffer + uint8_t *image = PrimaryCCD.getFrameBuffer(); + uint8_t * downbuf = dn->getBuf(); + size_t downsz = dn->getBufImageSize(); + IDLog("image size %ld buf %p\n", downsz, downbuf); + // Get width and height + //int width = PrimaryCCD.getSubW() / PrimaryCCD.getBinX() * PrimaryCCD.getBPP() / 8; + //int height = PrimaryCCD.getSubH() / PrimaryCCD.getBinY(); + memset(image, 0, PrimaryCCD.getFrameBufferSize()); + dn->copydownload(image, PrimaryCCD.getSubX(), PrimaryCCD.getSubW(), PrimaryCCD.getBinX(), 1, 1); + IDLog("copied..\n"); + + // Fill buffer with random pattern + //for (int i = 0; i < height; i++) + // for (int j = 0; j < width; j++) + // image[i * width + j] = rand() % 255; + dn->freeBuf(); + IDMessage(getDeviceName(), "Download %d lines complete.", dn->getActWriteLines()); + + // Let INDI::CCD know we're done filling the image buffer + ExposureComplete(&PrimaryCCD); +} + +bool NightscapeCCD::ISNewSwitch (const char *dev, const char *name, ISState *states, char *names[], int n) +{ +// Make sure the call is for our device + if(!strcmp(dev,getDeviceName())) + { + if (!strcmp(name, FanSP.name)) { + const char *actionName = IUFindOnSwitchName(states, names, n); + if (!strcmp(actionName, FanS[fanspeed -1].name)) + { + DEBUGF(INDI::Logger::DBG_SESSION, "Fan is already %s", FanS[fanspeed -1].label); + FanSP.s = IPS_IDLE; + IDSetSwitch(&FanSP, NULL); + return true; + } + IUUpdateSwitch(&FanSP, states, names, n); + fanspeed = IUFindOnSwitchIndex(&FanSP) +1; + DEBUGF(INDI::Logger::DBG_SESSION, "Fan is now %s", FanS[fanspeed -1].label); + FanSP.s = IPS_OK; + IDSetSwitch(&FanSP, NULL); + m->sendfan(fanspeed); + return true; + } else if (!strcmp(name, CoolerSP.name)) { + const char *actionName = IUFindOnSwitchName(states, names, n); + if (!strcmp(actionName, CoolerS[!cooler].name)) + { + DEBUGF(INDI::Logger::DBG_SESSION, "Cooler is already %s", CoolerS[!cooler].label); + CoolerSP.s = IPS_IDLE; + IDSetSwitch(&CoolerSP, NULL); + return true; + } + IUUpdateSwitch(&CoolerSP, states, names, n); + cooler = !IUFindOnSwitchIndex(&CoolerSP); + DEBUGF(INDI::Logger::DBG_SESSION, "Cooler is now %s", CoolerS[!cooler].label); + CoolerSP.s = IPS_OK; + IDSetSwitch(&CoolerSP, NULL); + m->sendtemp(setTemp, cooler); + dn->setActTemp(currentCCDTemperature); + return true; + + } else if (!strcmp(name, D2xxSP.name)) { + const char *actionName = IUFindOnSwitchName(states, names, n); + if (!strcmp(actionName, D2xxS[useD2xx].name)) + { + DEBUGF(INDI::Logger::DBG_SESSION, "Library is already %s", D2xxS[useD2xx].label); + D2xxSP.s = IPS_IDLE; + IDSetSwitch(&D2xxSP, NULL); + return true; + } + IUUpdateSwitch(&D2xxSP, states, names, n); +#ifdef HAVE_D2XX + useD2xx = IUFindOnSwitchIndex(&D2xxSP); + DEBUGF(INDI::Logger::DBG_SESSION, "Library is now %s", D2xxS[useD2xx].label); +#else + useD2xx = IUFindOnSwitchIndex(&D2xxSP); + DEBUGF(INDI::Logger::DBG_SESSION, "Library is now %s", D2xxS[useD2xx].label); + if(useD2xx == 1) useD2xx = 2; +#endif + D2xxSP.s = IPS_OK; + IDSetSwitch(&D2xxSP, NULL); + return true; + + } + } + + return INDI::CCD::ISNewSwitch(dev, name, states, names, n); + +} + +bool NightscapeCCD::ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) +{ + if (!strcmp(name, CamNumNP.name)) { + if (values[0] < CamNumN[0].min || values[0] > CamNumN[0].max) + { + CamNumNP.s = IPS_ALERT; + DEBUGF(INDI::Logger::DBG_ERROR, "Error: Bad camera number value! Range is [%.1f, %.1f].", + CamNumN[0].min, CamNumN[0].max); + IDSetNumber(&CamNumNP, nullptr); + return false; + } + + camnum = values[0]; + CamNumN[0].value = camnum; + //if (rc == 0) + // TemperatureNP.s = IPS_BUSY; + //else if (rc == 1) + // TemperatureNP.s = IPS_OK; + //else + // TemperatureNP.s = IPS_ALERT; + + IDSetNumber(&CamNumNP, nullptr); + return true; + } + return INDI::CCD::ISNewNumber(dev, name, values, names, n); + +} + +bool NightscapeCCD::saveConfigItems(FILE *fp) { + currentCCDTemperature = setTemp; + IUSaveConfigSwitch(fp, &FanSP); + IUSaveConfigSwitch(fp, &CoolerSP); + IUSaveConfigNumber(fp, &CamNumNP); + IUSaveConfigSwitch(fp, &D2xxSP); + float tTemp = currentCCDTemperature; + + IUSaveConfigNumber(fp, &TemperatureNP); + + currentCCDTemperature = tTemp; + + return INDI::CCD::saveConfigItems(fp); +}; + diff --git a/3rdparty/indi-nightscape/nightscape.h b/3rdparty/indi-nightscape/nightscape.h new file mode 100644 index 0000000000..239757140b --- /dev/null +++ b/3rdparty/indi-nightscape/nightscape.h @@ -0,0 +1,104 @@ +/* + Based on INDI Developers Manual Tutorial #3 + + "Simple CCD Driver" + + We develop a simple CCD driver. + + Refer to README, which contains instruction on how to build this driver, and use it + with an INDI-compatible client. + +*/ + +/** \file nightscape.h + \brief Construct a basic INDI CCD device that supports exposure & temperature settings. + It also gcollects an image and uploads it as a FITS file. + \author Dirk Niggemann + + \example nightscape.h + A simple CCD device that can capture images and control temperature. It returns a FITS image to the client. To build drivers for complex CCDs, please + refer to the INDI Generic CCD driver template in INDI SVN (under 3rdparty). +*/ + +#pragma once + +#include "indiccd.h" +#include "nsmsg.h" +#include "nschannel.h" +#include "nsdownload.h" +#include "nsstatus.h" +#include "nschannel-u.h" +#ifdef HAVE_D2XX +#include "nschannel-ftd.h" +#endif +#ifdef HAVE_SERIAL +#include "nschannel-ser.h" +#endif + +class NightscapeCCD : public INDI::CCD +{ + public: + NightscapeCCD() = default; + virtual bool ISNewSwitch (const char *dev, const char *name, ISState *states, char *names[], int n) override; + virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override; + + protected: + // General device functions + bool Connect() override; + bool Disconnect() override; + const char *getDefaultName() override; + bool initProperties() override; + bool updateProperties() override; + + // CCD specific functions + bool StartExposure(float duration) override; + bool AbortExposure() override; + int SetTemperature(double temperature) override; + void TimerHit() override; + bool saveConfigItems(FILE *fp) override; + + + ISwitch CoolerS [2]; + ISwitchVectorProperty CoolerSP; + + ISwitch FanS[3]; + ISwitchVectorProperty FanSP; + + INumber CamNumN[1]; + INumberVectorProperty CamNumNP; + + ISwitch D2xxS [3]; + ISwitchVectorProperty D2xxSP; + + private: + // Utility functions + float CalcTimeLeft(); + void setupParams(); + void grabImage(); + + // Are we exposing? + bool InExposure { false }; + bool InReadout { false }; + bool InDownload { false }; + int stat {0}; + int oldstat { 0}; + // Struct to keep timing + struct timeval ExpStart { 0, 0 }; + + float ExposureRequest { 0 }; + float TemperatureRequest { 0 }; + Nsmsg * m; + NsChannel * cn; + NsDownload * dn; + NsStatus * st; + int fanspeed {3 }; + int camnum { 1}; + + bool cooler { true }; + float setTemp; + int useD2xx { 1 }; + bool bayer { true }; + bool dark { false }; + int ntemps {0}; + int backoffs {1}; +}; diff --git a/3rdparty/indi-nightscape/nschannel-ftd.cpp b/3rdparty/indi-nightscape/nschannel-ftd.cpp new file mode 100644 index 0000000000..208b6d8290 --- /dev/null +++ b/3rdparty/indi-nightscape/nschannel-ftd.cpp @@ -0,0 +1,273 @@ +#include +#include "nschannel-ftd.h" +#include "nsdebug.h" + + +static const char* status_string(FT_STATUS res) +{ + static const char *table[] = { + "FT_OK", + "FT_INVALID_HANDLE", + "FT_DEVICE_NOT_FOUND", + "FT_DEVICE_NOT_OPENED", + "FT_IO_ERROR", + "FT_INSUFFICIENT_RESOURCES", + "FT_INVALID_PARAMETER", + "FT_INVALID_BAUD_RATE", + "FT_DEVICE_NOT_OPENED_FOR_ERASE", + "FT_DEVICE_NOT_OPENED_FOR_WRITE", + "FT_FAILED_TO_WRITE_DEVICE", + "FT_EEPROM_READ_FAILED", + "FT_EEPROM_WRITE_FAILED", + "FT_EEPROM_ERASE_FAILED", + "FT_EEPROM_NOT_PRESENT", + "FT_EEPROM_NOT_PROGRAMMED", + "FT_INVALID_ARGS", + "FT_NOT_SUPPORTED", + "FT_OTHER_ERROR", + "FT_DEVICE_LIST_NOT_READY" + }; + return table[res]; +} + + +int NsChannelFTD::close() +{ + FT_Close(ftdic); + FT_Close(ftdid); + opened = 0; + return 0; +} + +int NsChannelFTD::closecontrol() +{ + FT_Close(ftdic); + return 0; +} + + +int NsChannelFTD::resetcontrol() +{ + closecontrol(); + return opencontrol(); +} + +int NsChannelFTD::opendownload(void) +{ + + FT_STATUS rc2 ; + //unsigned chunksize = DEFAULT_CHUNK_SIZE; //DEFAULT_CHUNK_SIZE; + unsigned chunksize = 65536L; + //chunksize = chunksize - ((chunksize / 64)*2); + rc2 = FT_Open(thedev+1, &ftdid); + if (rc2 != FT_OK) + { + DO_ERR( "unable to open ftdi data device: %d (%s)\n", rc2, status_string(rc2)); + return(-1); + } + rc2 = FT_ResetDevice(ftdid); + if (rc2 != FT_OK) + { + DO_ERR( "unable to reset ftdi data device: %d (%s)\n", rc2, status_string(rc2)); + return(-1); + } + rc2 = FT_Purge(ftdid, FT_PURGE_RX | FT_PURGE_TX); + if (rc2 != FT_OK) + { + DO_ERR( "unable to purge ftdi data device: %d (%s)\n", rc2, status_string(rc2)); + return(-1); + } + rc2=FT_SetTimeouts(ftdid, 500, 250); + if (rc2 != FT_OK) + { + DO_ERR( "unable to set timeouts data device: %d (%s)\n", rc2, status_string(rc2)); + return(-1); + } + DO_INFO("test read chunksize %d, max xfer %d\n", chunksize, maxxfer); + + //rc2 = FT_SetUSBParameters(ftdid, chunksize/2, chunksize); + rc2 = FT_SetUSBParameters(ftdid, chunksize, chunksize); + + if (rc2 != FT_OK) + { + DO_ERR( "unable to set USB Parameters: %d (%s)\n", rc2, status_string(rc2)); + return(-1); + } + // maxxfer = chunksize - ((chunksize / 64)*2); + maxxfer = chunksize - ((chunksize / 512)*2); + + //maxxfer = (chunksize/4) - (((chunksize/4) / 64)*2); + //maxxfer = (chunksize/4) - (((chunksize/4) / 512)*2); + //maxxfer = chunksize; + //maxxfer = DEFAULT_OLD_CHUNK_SIZE; + DO_INFO("actual read chunksize %d, max xfer %d\n", chunksize, maxxfer); + rc2 = FT_SetFlowControl(ftdid, FT_FLOW_RTS_CTS, 0, 0); + if (rc2 != FT_OK) + { + DO_ERR( "unable to set flow control: %d (%s)\n", rc2, status_string(rc2)); + return(-1); + } + rc2=FT_SetRts (ftdid); + if (rc2 != FT_OK) + { + DO_ERR( "unable to set rts on data channel: %d (%s)\n", rc2, status_string(rc2)); + return (-1); + } + return maxxfer; +} + + +int NsChannelFTD::opencontrol (void) +{ + FT_STATUS rc; + + // Open device + rc = FT_Open(thedev, &ftdic); + if (rc != FT_OK) + { + DO_ERR( "unable to open ftdi device: %d (%s)\n", rc, status_string(rc)); + return(-1); + } + rc= FT_ResetDevice(ftdic); + if (rc != FT_OK) { + DO_ERR( "unable to reset: %d (%s)\n", rc, status_string(rc)); + return (-1); + } + rc= FT_Purge(ftdic, FT_PURGE_RX | FT_PURGE_TX); + if (rc != FT_OK) { + DO_ERR( "unable to purge: %d (%s)\n", rc, status_string(rc)); + return (-1); + } + rc=FT_SetLatencyTimer(ftdic, 2); + if (rc != FT_OK) { + DO_ERR( "unable to set latency: %d (%s)\n", rc, status_string(rc)); + return (-1); + } + rc=FT_SetTimeouts(ftdic, 500, 250); + if (rc != FT_OK) + { + DO_ERR( "unable to set timeouts: %d (%s)\n", rc, status_string(rc)); + return (-1); + } + rc = FT_SetFlowControl(ftdic, FT_FLOW_RTS_CTS, 0, 0); + if (rc != FT_OK) + { + DO_ERR( "unable to set control flow control: %d (%s)\n", rc, status_string(rc)); + return(-1); + } + rc=FT_SetRts (ftdic); + if (rc != FT_OK) + { + DO_ERR( "unable to set rts on control channel: %d (%s)\n", rc, status_string(rc)); + return (-1); + } + return 0; +} + +int NsChannelFTD::scan(void) { + FT_STATUS rc; + thedev = -1; + rc = FT_SetVIDPID(vid, pid); + if (rc != FT_OK) { + DO_ERR("unable to set vip/pid: %d(%s)\n", rc, status_string(rc)); + return (-1); + } + rc = FT_CreateDeviceInfoList(&ndevs); + if (rc != FT_OK) { + DO_ERR("unable to get device info: %d(%s)\n", rc, status_string(rc)); + return (-1); + } + DO_INFO("Found %d devices\n", ndevs); + FT_DEVICE_LIST_INFO_NODE * dev = NULL; + if (ndevs > 0) { + dev = (FT_DEVICE_LIST_INFO_NODE *)malloc (sizeof(FT_DEVICE_LIST_INFO_NODE) *ndevs); + rc = FT_GetDeviceInfoList (dev, &ndevs); + if (rc != FT_OK) { + DO_ERR("unable to get device info list: %d(%s)\n", rc, status_string(rc)); + return(-1); + } + for (unsigned c = 0; c < ndevs; c++) { + DO_INFO("Dev %d:\n",c); + DO_INFO(" Flags=0x%x\n",dev[c].Flags); + DO_INFO(" Type=0x%x\n",dev[c].Type); + DO_INFO(" ID=0x%x\n",dev[c].ID); + DO_INFO(" LocId=0x%x\n",dev[c].LocId); + + DO_INFO(" SerialNumber=%s\n",dev[c].SerialNumber); + DO_INFO(" Description=%s\n",dev[c].Description); + DO_INFO(" ftHandle=%p\n",dev[c].ftHandle); + if (c == (camnum-1)*2) thedev = c; + } + } + if (thedev == -1) { + DO_ERR("Can't find camera number %d\n", camnum); + return(-1); + } + // Read out FTDIChip-ID of R type chips + if (dev[thedev].Type != FT_DEVICE_2232H) { + DO_ERR("incorrect ftdi type: %d\n", dev[thedev].Type); + return(-1); + }; + free(dev); + return ndevs; +} + +int NsChannelFTD::readCommand(unsigned char *buf, size_t size) { + FT_STATUS rc; + unsigned nbytes = 0; + rc = FT_Read(ftdic, buf, size, &nbytes); + if (rc != FT_OK) { + DO_ERR( "unable to read command: %d (%s)\n", rc,status_string(rc) ); + return -1; + } else { + return nbytes; + } +} + + +int NsChannelFTD::writeCommand(const unsigned char *buf, size_t size) { + FT_STATUS rc; + unsigned nbytes = 0; + rc=FT_Write(ftdic, (void *)buf, size, &nbytes); + + if (rc != FT_OK) { + DO_ERR( "unable to write command: %d (%s)\n", rc, status_string(rc)); + return -1; + } else { + return nbytes; + } +} + + +int NsChannelFTD::readData(unsigned char *buf, size_t size) { + FT_STATUS rc2; + unsigned nbytes= 0; + rc2 = FT_Read(ftdid, buf, size, &nbytes); + if (rc2 != FT_OK) { + DO_ERR( "unable to read data: %d (%s)\n", rc2, status_string(rc2)); + return -1; + } else { + return nbytes; + } +} + +int NsChannelFTD::purgeData(void) { + FT_STATUS rc2; + rc2= FT_Purge(ftdid, FT_PURGE_RX | FT_PURGE_TX); + if (rc2 != FT_OK) { + DO_ERR( "unable to purge: %d (%s)\n", rc2, status_string(rc2)); + return (-1); + } + return 0; +} + +int NsChannelFTD::setDataRts(void) { + FT_STATUS rc2; + rc2=FT_SetRts (ftdid); + if (rc2 != FT_OK) { + DO_ERR("unable to set rts on data channel: %d (%s)\n", rc2, status_string(rc2)); + return (-1); + } + return 0; +} + diff --git a/3rdparty/indi-nightscape/nschannel-ftd.h b/3rdparty/indi-nightscape/nschannel-ftd.h new file mode 100644 index 0000000000..772e5693d8 --- /dev/null +++ b/3rdparty/indi-nightscape/nschannel-ftd.h @@ -0,0 +1,46 @@ +#ifndef __NS_CHANNEL_FTD_H__ +#define __NS_CHANNEL_FTD_H__ +#include "nschannel.h" +#include +#include +class NsChannelFTD : public NsChannel { + public: + NsChannelFTD() { + devs = NULL; + maxxfer = 0; + opened = 0; + camnum = 0; + thedev = -1; + } + NsChannelFTD(int cam) { + devs = NULL; + camnum = cam; + maxxfer = 0; + opened = 0; + thedev = -1; + } + + int open(); + int close(); + int getMaxXfer(); + int readCommand(unsigned char * buf, size_t n); + int writeCommand(const unsigned char * buf, size_t n); + int readData(unsigned char * buf, size_t n); + int purgeData(void); + int setDataRts(void); + int resetcontrol (void); + + protected: + int opencontrol (void); + int closecontrol(void); + int opendownload(void); + int scan(void); + private: + FT_HANDLE ftdic, ftdid; + struct ftdi_device_list * devs; + int thedev; + + +}; + +#endif diff --git a/3rdparty/indi-nightscape/nschannel-ser.cpp b/3rdparty/indi-nightscape/nschannel-ser.cpp new file mode 100644 index 0000000000..edb6a80279 --- /dev/null +++ b/3rdparty/indi-nightscape/nschannel-ser.cpp @@ -0,0 +1,209 @@ +#include +#include "nschannel-ser.h" +#include "nsdebug.h" + +#include +#include +#include +#include +#include +#include + +static int +set_interface_attribs (int fd, int speed) +{ + struct termios tty; + memset (&tty, 0, sizeof tty); + if (tcgetattr (fd, &tty) != 0) + { + DO_ERR ("error %d from tcgetattr %s", errno, strerror(errno)); + return -1; + } + + cfsetospeed (&tty, speed); + cfsetispeed (&tty, speed); + + tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8; // 8-bit chars + // disable IGNBRK for mismatched speed tests; otherwise receive break + // as \000 chars + tty.c_iflag &= ~IGNBRK; // disable break processing + tty.c_lflag = 0; // no signaling chars, no echo, + // no canonical processing + tty.c_oflag = 0; // no remapping, no delays + tty.c_cc[VMIN] = 0; // read doesn't block + tty.c_cc[VTIME] = 5; // 0.5 seconds read timeout + + tty.c_iflag &= ~(IXON | IXOFF | IXANY); // shut off xon/xoff ctrl + + tty.c_cflag |= (CLOCAL | CREAD);// ignore modem controls, + // enable reading + tty.c_cflag &= ~(PARENB | PARODD); // shut off parity + //tty.c_cflag |= parity; + tty.c_cflag &= ~CSTOPB; + tty.c_cflag &= ~CRTSCTS; + tty.c_cflag |= CRTSCTS; + + + if (tcsetattr (fd, TCSANOW, &tty) != 0) + { + DO_ERR ("error %d from tcsetattr %s", errno, strerror(errno)); + return -1; + } + return 0; +} + +static void +set_blocking (int fd, int should_block) +{ + struct termios tty; + memset (&tty, 0, sizeof tty); + if (tcgetattr (fd, &tty) != 0) + { + DO_ERR ("error %d from tggetattr %s", errno, strerror(errno)); + return; + } + + tty.c_cc[VMIN] = should_block ? 1 : 0; + tty.c_cc[VTIME] = 5; // 0.5 seconds read timeout + + if (tcsetattr (fd, TCSANOW, &tty) != 0) + DO_ERR ("error %d setting term attributes %s", errno, strerror(errno)); +} + + + +int NsChannelSER::close() +{ + ::close(ftdic); + ::close(ftdid); + delete cportname; + delete dportname; + opened = 0; + return 0; +} + + +int NsChannelSER::closecontrol() +{ + ::close(ftdic); + return 0; +} + +int NsChannelSER::resetcontrol() +{ + closecontrol(); + return opencontrol(); +} +int NsChannelSER::opendownload(void) +{ + unsigned chunksize = 4095; //DEFAULT_CHUNK_SIZE; + + ftdid = ::open (dportname, O_RDWR | O_NOCTTY | O_SYNC); + + if (ftdid < 0) + { + DO_ERR ("error %d opening data: %s", errno, strerror (errno)); + return -1; + } + set_interface_attribs (ftdid, B115200); // set speed to 115,200 bps, 8n1 (no parity) + set_blocking (ftdid, 0); // set no blocking + //maxxfer = chunksize - ((chunksize / 64)*2); + + //maxxfer = (chunksize/4) - (((chunksize/4) / 64)*2); + maxxfer = (chunksize) - (((chunksize) / 512)*2); + //maxxfer = chunksize; + //maxxfer = DEFAULT_OLD_CHUNK_SIZE; + setDataRts(); + return maxxfer; +} + + +int NsChannelSER::opencontrol (void) +{ + + // Open device + ftdic = ::open (cportname, O_RDWR | O_NOCTTY | O_SYNC); + + if (ftdic < 0) + { + DO_ERR ("error %d opening control %s", errno, strerror (errno)); + return -1; + } + set_interface_attribs (ftdic, B115200); // set speed to 115,200 bps, 8n1 (no parity) + set_blocking (ftdic, 0); // set no blocking + + return 0; +} + +int NsChannelSER::scan(void) { + //int rc; + if( access( cportname, R_OK| W_OK ) != -1 && access( dportname, R_OK| W_OK ) != -1) { + return 1; + } else { + return 0; + } +} + +int NsChannelSER::readCommand(unsigned char *buf, size_t size) { + int rc; + //unsigned nbytes = 0; + rc = ::read(ftdic, buf, size); + if (rc < 0) { + DO_ERR( "unable to read command: %d (%s)\n", rc,strerror(errno) ); + return -1; + } else { + return rc; + } +} + + +int NsChannelSER::writeCommand(const unsigned char *buf, size_t size) { + int rc; + //unsigned nbytes = 0; + rc=::write(ftdic, (void *)buf, size); + + if (rc < 0) { + DO_ERR( "unable to write command: %d (%s)\n", rc, strerror(errno)); + return -1; + } else { + return rc; + } +} + + +int NsChannelSER::readData(unsigned char *buf, size_t size) { + int rc2; + //unsigned nbytes= 0; + rc2 = ::read(ftdid, buf, size); + if (rc2 < 0) { + DO_ERR( "unable to read data: %d (%s)\n", rc2, strerror(errno)); + return -1; + } else { + return rc2; + } +} + +int NsChannelSER::purgeData(void) { + int rc2; + rc2= tcflush(ftdid,TCIOFLUSH); + + if (rc2 < 0) { + DO_ERR( "unable to purge: %d (%s)\n", rc2, strerror(errno)); + return (-1); + } + return 0; +} + +int NsChannelSER::setDataRts(void) { + int rc2; + int RTS_flag; + RTS_flag = TIOCM_RTS; + + rc2 = ioctl(ftdid,TIOCMBIS,&RTS_flag);//Set RTS pin + if (rc2 < 0) { + DO_ERR( "unable to set rts: %d: (%s)\n", rc2, strerror(errno)); + return (-1); + } + return 0; +} + diff --git a/3rdparty/indi-nightscape/nschannel-ser.h b/3rdparty/indi-nightscape/nschannel-ser.h new file mode 100644 index 0000000000..4558880e0b --- /dev/null +++ b/3rdparty/indi-nightscape/nschannel-ser.h @@ -0,0 +1,58 @@ +#ifndef __NS_CHANNEL_SER_H__ +#define __NS_CHANNEL_SER_H__ +#include "nschannel.h" +#include +#include +#include + +class NsChannelSER : public NsChannel { + public: + NsChannelSER() { + maxxfer = 0; + opened = 0; + camnum = 1; + thedev = -1; + char * name = new char [64]; + snprintf(name, 64, "/dev/ttyUSB%d", camnum-1); + cportname = name; + name = new char [64]; + snprintf(name, 64, "/dev/ttyUSB%d", camnum); + dportname = name; + + } + NsChannelSER(int cam) { + camnum = cam; + maxxfer = 0; + opened = 0; + thedev = -1; + char * name = new char [64]; + snprintf(name, 64, "/dev/ttyUSB%d", camnum-1); + cportname = name; + name = new char [64]; + snprintf(name, 64, "/dev/ttyUSB%d", camnum); + dportname = name; + } + + int open(); + int close(); + int getMaxXfer(); + int readCommand(unsigned char * buf, size_t n); + int writeCommand(const unsigned char * buf, size_t n); + int readData(unsigned char * buf, size_t n); + int purgeData(void); + int setDataRts(void); + int resetcontrol (void); + + protected: + int opencontrol (void); + int closecontrol (void); + int opendownload(void); + int scan(void); + private: + int ftdic, ftdid; + int thedev; + const char * cportname; + const char * dportname; +}; + +#endif diff --git a/3rdparty/indi-nightscape/nschannel-u.cpp b/3rdparty/indi-nightscape/nschannel-u.cpp new file mode 100644 index 0000000000..bb41e7c55d --- /dev/null +++ b/3rdparty/indi-nightscape/nschannel-u.cpp @@ -0,0 +1,288 @@ +#include + +#include "nschannel-u.h" +#include "nsdebug.h" + +struct ftdi_context * NsChannelU::getDataChannel(void) { + return &data_channel; +} +struct ftdi_context * NsChannelU::getCommandChannel(void) { + return &command_channel; +} + +int NsChannelU::close() +{ + ftdi_usb_close(&data_channel); + ftdi_usb_close(&command_channel); + ftdi_usb_close(&scan_channel); + + ftdi_deinit(&command_channel); + ftdi_deinit(&data_channel); + ftdi_deinit(&scan_channel); + ftdi_list_free(&devs); + opened = 0; + return 0; +} + +int NsChannelU::closecontrol() +{ + ftdi_usb_close(&command_channel); + + ftdi_deinit(&command_channel); + return 0; +} + +int NsChannelU::resetcontrol() +{ + closecontrol(); + return opencontrol(); +} + +int NsChannelU::opendownload(void) +{ + struct libusb_device * dev = camdev; + struct ftdi_context * ftdid = &data_channel; + int rc2; + unsigned chunksize = DEFAULT_CHUNK_SIZE; //DEFAULT_CHUNK_SIZE; + //chunksize = chunksize - ((chunksize / 64)*2); + rc2 = ftdi_init(ftdid); + if(rc2 < 0) { + + DO_ERR( "unable to init ftdi data device: %d (%s)\n", rc2, ftdi_get_error_string(ftdid)); + return -1; + } + + // Select second interface + ftdi_set_interface(ftdid, INTERFACE_B); + + + // Open device + rc2 = ftdi_usb_open_dev(ftdid, dev); + if (rc2 < 0) + { + DO_ERR( "unable to open ftdi data device: %d (%s)\n", rc2, ftdi_get_error_string(ftdid)); + return -1; + } + rc2 = ftdi_usb_reset(ftdid); + if (rc2 < 0) + { + DO_ERR( "unable to reset ftdi data device: %d (%s)\n", rc2, ftdi_get_error_string(ftdid)); + return -1; + } + rc2 = ftdi_usb_purge_buffers(ftdid); + if (rc2 < 0) + { + DO_ERR( "unable to purge ftdi data device: %d (%s)\n", rc2, ftdi_get_error_string(ftdid)); + return -1; + } + rc2 = ftdi_set_bitmode(ftdid, 0x0, BITMODE_RESET); + if (rc2 < 0) + { + DO_ERR( "unable to set bitmode data device: %d (%s)\n", rc2, ftdi_get_error_string(ftdid)); + return -1; + } + + ftdid->usb_read_timeout = 20000; + ftdid->usb_write_timeout = 250; + rc2 = ftdi_write_data_set_chunksize(ftdid, chunksize); + if (rc2 < 0) + { + DO_ERR( "unable to set write chunksize: %d (%s)\n", rc2, ftdi_get_error_string(ftdid)); + return -1; + } + rc2 = ftdi_read_data_set_chunksize(ftdid, chunksize); + if (rc2 < 0) + { + DO_ERR( "unable to set read chunksize: %d (%s)\n", rc2, ftdi_get_error_string(ftdid)); + return -1; + } + rc2 = ftdi_read_data_get_chunksize (ftdid, &chunksize); + if (rc2 < 0) + { + DO_ERR( "unable to get read chunksize: %d (%s)\n", rc2, ftdi_get_error_string(ftdid)); + return -1; + } + //rc2 = ftdi_set_latency_timer (&ftdid, 255); + rc2 = ftdi_set_latency_timer (ftdid, 2); + + if (rc2 < 0) + { + DO_ERR( "unable to set latency timer: %d (%s)\n", rc2, ftdi_get_error_string(ftdid)); + return -1; + } + //maxxfer = chunksize - ((chunksize / 64)*2); + maxxfer = chunksize - ((chunksize / 512)*2); + //maxxfer = imgsz; + DO_INFO("actual read chunksize %d, max xfer %d\n", chunksize, maxxfer); + rc2 = ftdi_setflowctrl(ftdid, SIO_RTS_CTS_HS); + if (rc2 < 0) + { + DO_ERR( "unable to set flow control: %d (%s)\n", rc2, ftdi_get_error_string(ftdid)); + return -1; + } + rc2=ftdi_setrts(ftdid, 1); + if (rc2 < 0) { + DO_ERR( "unable to set rts on data channel: %d (%s)\n", rc2, ftdi_get_error_string(ftdid)); + return -1; + } + return maxxfer; +} + + +int NsChannelU::opencontrol (void) +{ + struct libusb_device * dev = camdev; + + struct ftdi_context * ftdic = &command_channel; + int rc; + rc = ftdi_init(ftdic); + if(rc < 0) { + DO_ERR( "unable to init ftdi control device: %d (%s)\n", rc, ftdi_get_error_string(ftdic)); + return -1; + } + // Select first interface + ftdi_set_interface(ftdic, INTERFACE_A); + + // Open device + rc = ftdi_usb_open_dev(ftdic, dev); + if (rc < 0) + { + DO_ERR( "unable to open ftdi device: %d (%s)\n", rc, ftdi_get_error_string(ftdic)); + return -1; + } +// Read out FTDIChip-ID of R type chips + if (ftdic->type == TYPE_2232H) + { + unsigned int chipid; + rc = ftdi_read_chipid(ftdic, &chipid); + if (rc < 0) { + DO_ERR( "unable read ftdi chipid: %d (%s)\n", rc, ftdi_get_error_string(ftdic)); + return -1; + } + DO_INFO("FTDI chipid: %X\n", chipid); + } else { + DO_ERR("incorrect ftdi type: %d\n", ftdic->type); + return -1; + }; + rc= ftdi_usb_reset(ftdic); + if (rc < 0) { + DO_ERR( "unable to reset: %d (%s)\n", rc, ftdi_get_error_string(ftdic)); + return -1; + } + rc= ftdi_usb_purge_buffers(ftdic); + if (rc < 0) { + DO_ERR( "unable to purge: %d (%s)\n", rc, ftdi_get_error_string(ftdic)); + return -1; + } + rc=ftdi_set_baudrate(ftdic, 460800*2); + if (rc < 0) { + DO_ERR( "unable to set baudrate: %d (%s)\n", rc, ftdi_get_error_string(ftdic)); + return -1; + } + rc=ftdi_set_latency_timer(ftdic, 2); + if (rc < 0) { + DO_ERR( "unable to set latency: %d (%s)\n", rc, ftdi_get_error_string(ftdic)); + return -1; + } + ftdic->usb_read_timeout = 500; + ftdic->usb_write_timeout = 250; + return 0; +} + +int NsChannelU::scan(void) { + int rc; + struct ftdi_context * ftdis = &scan_channel; + rc = ftdi_init(ftdis); + if(rc < 0) { + DO_ERR( "unable to init ftdi scan device: %d (%s)\n", rc, ftdi_get_error_string(ftdis)); + return -1; + } + ndevs = ftdi_usb_find_all (ftdis, &devs, vid, pid); + DO_INFO("Found %d devices\n", ndevs); + struct ftdi_device_list * dev = devs; + camdev = NULL; + for (unsigned c = 0; c < ndevs; c++) { + char manf[64]; + char desc[64]; + //char ser[64]; + //rc = ftdi_usb_get_strings (ftdis, dev->dev, manf, 64, desc, 64, ser, 64); + rc = ftdi_usb_get_strings (ftdis, dev->dev, manf, 64, desc, 64, NULL, 0); + if (rc !=0 ) { + DO_ERR( "unable to get strings: %d (%s)\n", rc, ftdi_get_error_string(ftdis)); + return -1; + } + DO_INFO("Camera %d, Man: %s, Desc: %s\n", c+1, manf, desc); + //DO_INFO("Man: %s, Desc: %s, Ser: %s\n", manf, desc, ser); + if (c == (camnum -1)) camdev = dev->dev; + dev = dev->next; + } + + if (camdev == NULL) { + DO_ERR( "Can't find camera number %d\n", camnum); + return -1; + } + return ndevs; +} + +int NsChannelU::readCommand(unsigned char *buf, size_t size) { + int rc; + struct ftdi_context * ftdic = &command_channel; + + rc = ftdi_read_data(ftdic, buf, size); + if (rc < 0) { + DO_ERR( "unable to read command: %d (%s)\n", rc, ftdi_get_error_string(ftdic)); + return -1; + } else { + return rc; + } +} + + +int NsChannelU::writeCommand(const unsigned char *buf, size_t size) { + int rc; + struct ftdi_context * ftdic = &command_channel; + + rc = ftdi_write_data(ftdic, buf, size); + if (rc < 0) { + DO_ERR( "unable to write command: %d (%s)\n", rc, ftdi_get_error_string(ftdic)); + return -1; + } else { + return rc; + } +} + + +int NsChannelU::readData(unsigned char *buf, size_t size) { + int rc; + struct ftdi_context * ftdid = &data_channel; + + rc = ftdi_read_data(ftdid, buf, size); + if (rc < 0) { + DO_ERR( "unable to read data: %d (%s)\n", rc, ftdi_get_error_string(ftdid)); + return -1; + } else { + return rc; + } +} + +int NsChannelU::purgeData(void) { + struct ftdi_context * ftdid = &data_channel; + int rc2; + rc2 = ftdi_usb_purge_buffers(ftdid); + if (rc2 < 0 ) { + DO_ERR( "unable to purge: %d (%s)\n", rc2, ftdi_get_error_string(ftdid)); + return (-1); + } + return 0; +} + +int NsChannelU::setDataRts(void) { + struct ftdi_context * ftdid = &data_channel; + int rc2=ftdi_setrts(ftdid, 1); + if (rc2 < 0) { + DO_ERR( "unable to set rts on data channel: %d (%s)\n", rc2, ftdi_get_error_string(ftdid)); + return (-1); + } + return 0; +} + \ No newline at end of file diff --git a/3rdparty/indi-nightscape/nschannel-u.h b/3rdparty/indi-nightscape/nschannel-u.h new file mode 100644 index 0000000000..c20d6aa6b4 --- /dev/null +++ b/3rdparty/indi-nightscape/nschannel-u.h @@ -0,0 +1,49 @@ +#ifndef __NS_CHANNEL_U_H__ +#define __NS_CHANNEL_U_H__ +#include "nschannel.h" +#include +#include + +class NsChannelU : public NsChannel { + public: + NsChannelU() { + devs = NULL; + maxxfer = 0; + opened = 0; + camnum = 0; + } + NsChannelU(int cam) { + devs = NULL; + camnum = cam; + maxxfer = 0; + opened = 0; + } + struct ftdi_context * getCommandChannel(); + struct ftdi_context * getDataChannel(); + int open(); + int close(); + int getMaxXfer(); + int readCommand(unsigned char * buf, size_t n); + int writeCommand(const unsigned char * buf, size_t n); + int readData(unsigned char * buf, size_t n); + int purgeData(void); + int setDataRts(void); + int resetcontrol (void); + + protected: + int opencontrol (void); + int closecontrol (void); + + int opendownload(void); + int scan(void); + private: + struct ftdi_context scan_channel; + struct ftdi_context command_channel; + struct ftdi_context data_channel; + struct ftdi_device_list * devs; + struct libusb_device * camdev; + + +}; + +#endif diff --git a/3rdparty/indi-nightscape/nschannel.cpp b/3rdparty/indi-nightscape/nschannel.cpp new file mode 100644 index 0000000000..091501a926 --- /dev/null +++ b/3rdparty/indi-nightscape/nschannel.cpp @@ -0,0 +1,21 @@ +#include +#include "nschannel.h" +#include "nsdebug.h" + +int NsChannel::open() { + int rc = 0; + if ((rc = scan()) < 0) return rc; + if((rc = opencontrol()) < 0) return rc; + if((rc = opendownload()) < 0) return rc; + opened = 1; + return 0; +} + +int NsChannel::close() { + opened = 0; + return 0; +} + +int NsChannel::getMaxXfer() { + return maxxfer; +} diff --git a/3rdparty/indi-nightscape/nschannel.h b/3rdparty/indi-nightscape/nschannel.h new file mode 100644 index 0000000000..c3363615b4 --- /dev/null +++ b/3rdparty/indi-nightscape/nschannel.h @@ -0,0 +1,49 @@ +#ifndef __NS_CHANNEL_H__ +#define __NS_CHANNEL_H__ +#include +#include +#define DEFAULT_OLD_CHUNK_SIZE 63448 +#define DEFAULT_CHUNK_SIZE 65536 + +class NsChannel { + public: + NsChannel() { + maxxfer = 0; + opened = 0; + camnum = 0; + } + NsChannel(int cam) { + camnum = cam; + maxxfer = 0; + opened = 0; + } + virtual ~NsChannel() { if (opened) close(); }; + int open(); + int getMaxXfer(); + + virtual int close(); + virtual int readCommand(unsigned char * buf, size_t n) = 0; + virtual int writeCommand(const unsigned char * buf, size_t n) = 0; + virtual int readData(unsigned char * buf, size_t n)= 0; + virtual int purgeData(void)= 0; + virtual int setDataRts(void)= 0; + virtual int resetcontrol (void)= 0; + + protected: + virtual int opencontrol (void)= 0; + + virtual int opendownload(void)= 0; + virtual int scan(void)= 0; + static const int vid = 0x19b4; + static const int pid = 0x0065; + unsigned camnum; + int maxxfer; + unsigned ndevs; + bool opened; + int thedev; + + + +}; + +#endif diff --git a/3rdparty/indi-nightscape/nsdebug.h b/3rdparty/indi-nightscape/nsdebug.h new file mode 100644 index 0000000000..9663f1c6c0 --- /dev/null +++ b/3rdparty/indi-nightscape/nsdebug.h @@ -0,0 +1,10 @@ +#ifndef __NS_DEBUG__H__ +#define __NS_DEBUG__H__ +#include + + +#define DO_INFO(a, ...) IDLog(a, __VA_ARGS__) +#define DO_DBG(a, ...) IDLog(a, __VA_ARGS__) +#define DO_ERR(a, ...) IDLog( a, __VA_ARGS__) + +#endif \ No newline at end of file diff --git a/3rdparty/indi-nightscape/nsdownload.cpp b/3rdparty/indi-nightscape/nsdownload.cpp new file mode 100644 index 0000000000..2fc37aac71 --- /dev/null +++ b/3rdparty/indi-nightscape/nsdownload.cpp @@ -0,0 +1,541 @@ +#include "nsdownload.h" +#include "kaf_constants.h" +#include +#include +#include +#include +#include +#include "nsdebug.h" +#include + +void NsDownload::setFrameYBinning(int binning) { + ctx->imgp->ybinning = binning; + +} + +void NsDownload::setFrameXBinning(int binning) { + ctx->imgp->xbinning = binning; + +} +void NsDownload::setImgSize(int siz) { + rd->imgsz = siz; +} + +void NsDownload::setNumExp(int n){ + ctx->nexp = n; +} + +void NsDownload::nextImage() { + ctx->imgseq++; +} +int NsDownload::getImgSeq(void) { + return ctx->imgseq; + +} +void NsDownload::setSetTemp (float temp) { + ctx->imgp->settemp = temp; + +} + +void NsDownload::setActTemp(float temp) { + ctx->imgp->acttemp = temp; + +} + +void NsDownload::setExpDur(float exp){ + ctx->imgp->exp = exp; + +} + +void NsDownload::setIncrement(int inc) { + ctx->increment = inc; +} + +void NsDownload::setFbase(const char * name) { + strncpy(ctx->fbase,name, 64) ; +} + +void NsDownload::doDownload() { + + ctx->imgp->expdate = time(NULL); + std::unique_lock ulock(mutx); + + do_download = 1; + go_download.notify_all(); +} + +bool NsDownload::inDownload() { + return in_download||do_download; +} + + +bool NsDownload::getDoDownload() { + return do_download; +} + +unsigned char * NsDownload::getBuf() { + if (retrBuf == NULL) return NULL; + return retrBuf->buffer; +} + + +size_t NsDownload::getBufImageSize() { + if (retrBuf == NULL) return 0; + + return retrBuf->imgsz; +} + + +void NsDownload::setImgWrite(bool w) { + write_it = w; +} + +void NsDownload::freeBuf() { + if (!retrBuf) return; + if (retrBuf->buffer) free(retrBuf->buffer); + retrBuf->buffer = NULL; + retrBuf = NULL; +} + +void NsDownload::setInterrupted(){ + std::unique_lock ulock(mutx); + interrupted = 1; + go_download.notify_all(); +} + +void NsDownload::setZeroReads(int zeroes){ + zero_reads = zeroes; +} + int NsDownload::getActWriteLines(){ + return writelines; +} + +int NsDownload::downloader() +{ + //struct ftdi_context * ftdid = cn->->getDataChannel(); + //struct ftdi_transfer_control * ctl; + + int rc2; + int hardloop = 20; + int sleepage = 1000; + + int download =1; + if (rd->nread > rd->bufsiz) { + DO_ERR("image too large %d\n", rd->nread); + return (-1); + } + while((rc2 = cn->readData(rd->buffer+rd->nread, cn->getMaxXfer())) == 0 && hardloop > 0) { + if (hardloop % 5 == 0) DO_INFO("W%d\n",hardloop); + usleep(sleepage); + sleepage *= 2; + if (sleepage > 100000) sleepage = 100000; + hardloop--; + } + /* ctl = ftdi_read_data_submit(ftdid, rd->buffer+rd->nread, maxxfer); + if (ctl == NULL) { + DO_ERR( "unable to submit read: %d (%s)\n", rc2, ftdi_get_error_string(ftdid)); + return (-1); + } + DO_INFO( "."); + rc2 = ftdi_transfer_data_done(ctl); + */ + if (rc2 < 0 ) { + DO_ERR("unable to read download data: %d\n", rc2); + return (-1); + } + rd->nread += rc2; + if (rc2 != cn->getMaxXfer()) { + DO_INFO("short! %d %d\n", rd->nblks, rc2); + } + + if (rc2 == 0) { + readdone = 1; + } else { rd->nblks ++; } + if (rd->nread >= rd->imgsz) { + readdone = 1; + } + if (readdone) { + download=0; + lastread = rc2; + + rb = rdd; + retrBuf = &rb; + rd->buffer = NULL; + } + return download; +} + + +/* + +SIMPLE = T +BITPIX = 16 /8 unsigned int, 16 & 32 int, -32 & -64 real +NAXIS = 2 /number of axes NAXIS1 = 3326 /fastest changing axis +NAXIS2 = 2504 /next to fastest changing axis BSCALE = 1.0000000000000000 /physical = BZERO + BSCALE*array_value +BZERO = 32768.000000000000 /physical = BZERO + BSCALE*array_value +DATE-OBS= '2016-03-18T23:22:47' /YYYY-MM-DDThh:mm:ss observation start, UT +EXPTIME = 1.0000000000000000 /Exposure time in seconds EXPOSURE= 1.0000000000000000 /Exposure time in seconds +SET-TEMP= -7.0000000000000000 /CCD temperature setpoint in C +CCD-TEMP= -4.1500000000000004 /CCD temperature at start of exposure in C +XPIXSZ = 5.4000000000000004 /Pixel Width in microns (after binning) +YPIXSZ = 5.4000000000000004 /Pixel Height in microns (after binning) +XBINNING= 1 /Binning factor in width +YBINNING= 1 /Binning factor in height +XORGSUBF= 0 /Subframe X position in binned pixels +YORGSUBF= 0 /Subframe Y position in binned pixels +READOUTM= 'Raw ' / Readout mode of image +IMAGETYP= 'LIGHT ' / Type of image +SWCREATE= 'Celestron AstroFX V1.06' /Name of software that created the image +COLORTYP= 2 +XBAYROFF= 0 +YBAYROFF= 0 +OBJECT = 'test3 ' +INSTRUME= 'Celestron Nightscape 8300C' /instrument or camera used +SWOWNER = 'Dirk ' / Licensed owner of software +END +*/ + +void NsDownload::fitsheader(int x, int y, char * fbase, struct img_params * ip) +{ + FILE * f = img; + char datebuf[25] = "''"; + char fbase2[64]; + struct tm t; + gmtime_r(&ip->expdate, &t); + strncpy (fbase2, fbase, 12); + fbase2[12] = 0; + snprintf(datebuf, 25, "'%4d-%02d-%02dT%02d:%02d:%02d'", t.tm_year+1900, t.tm_mon+1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec); + int nl = 0; + nl++; fprintf(f, "%-8s=%21s %-49s", "SIMPLE", "T", ""); + nl++; fprintf(f, "%-8s=%21s %-49s", "BITPIX", "16", "/8 unsigned int, 16 & 32 int, -32 & -64 real"); + nl++; fprintf(f, "%-8s=%21s %-49s", "NAXIS", "2", "/number of axes"); + nl++; fprintf(f, "%-8s=%21d %-49s", "NAXIS1", x, "/fastest changing axis"); + nl++; fprintf(f, "%-8s=%21d %-49s", "NAXIS2", y, "/next to fastest changing axis"); + nl++; fprintf(f, "%-8s=%22s %-48s", "DATE-OBS", datebuf, "/YYYY-MM-DDThh:mm:ss observation start, UT"); + nl++; fprintf(f, "%-8s=%21.12f %-49s", "BZERO", 32768.0, "/physical = BZERO + BSCALE*array_value"); + nl++; fprintf(f, "%-8s=%21.16f %-49s", "EXPTIME", ip->exp, "/Exposure time in seconds"); + nl++; fprintf(f, "%-8s=%21.16f %-49s", "SET-TEMP", ip->settemp, "/CCD temperature setpoint in C"); + nl++; fprintf(f, "%-8s=%21.16f %-49s", "CCD-TEMP", ip->acttemp, "/CCD temperature at start of exposure in C"); + nl++; fprintf(f, "%-8s=%21.16f %-49s", "XPIXSZ", (float)ip->xbinning*5.40, "/Pixel Width in microns (after binning) "); + nl++; fprintf(f, "%-8s=%21.16f %-49s", "YPIXSZ", (float)ip->ybinning*5.40, "/Pixel Height in microns (after binning) "); + nl++; fprintf(f, "%-8s=%21d %-49s", "XBINNING", ip->xbinning, "/Binning factor in width"); + nl++; fprintf(f, "%-8s=%21d %-49s", "YBINNING", ip->ybinning, "/Binning factor in height"); + nl++; fprintf(f, "%-8s=%21s %-49s", "XORGSUBF", "0", "/Subframe X position in binned pixels"); + nl++; fprintf(f, "%-8s=%21s %-49s", "YORGSUBF", "0", "/Subframe Y position in binned pixels"); + nl++; fprintf(f, "%-8s= '%-8s' %-59s", "READOUTM", "Raw", "/ Readout mode of image"); + nl++; fprintf(f, "%-8s= '%-8s' %-59s", "IMAGETYP", "LIGHT", "/ Type of image"); + nl++; fprintf(f, "%-8s= '%-13s' %-54s", "SWCREATE", "nstest-u 0.90", "/Name of software that created the image"); + nl++; fprintf(f, "%-8s=%21s %-49s", "COLORTYP", "2", ""); + nl++; fprintf(f, "%-8s= '%-4s' %-63s", "BAYERPAT", "BGGR", "/ Baye pattern"); + nl++; fprintf(f, "%-8s=%21s %-49s", "XBAYROFF", "0", ""); + nl++; fprintf(f, "%-8s=%21s %-49s", "YBAYROFF", "0", ""); + nl++; fprintf(f, "%-8s= '%-12s' %-55s", "OBJECT", fbase, ""); + nl++; fprintf(f, "%-8s= '%-26s' %-41s", "INSTRUME", "Celestron Nightscape 8300C", "/instrument or camera used"); + nl++; fprintf(f, "%-80s", "END"); + for (int x = nl; x < 36; x++) fprintf(f, "%-80s", ""); + +} + + +void NsDownload::writedownload(int pad, int cooked) +{ + char fname[64]; + char fnab[64]; + unsigned char linebuf[KAF8300_MAX_X*2]; + const char * extn; + int procid = getpid(); + int nwrite = 0; + if (cooked) { + extn = ".fts"; + } else { + extn = ".bin"; + } + if (retrBuf == NULL) { + DO_DBG("%s", "no image"); + return; + } + DO_INFO("done! blks %d totl %d last %d\n", retrBuf->nblks, retrBuf->nread,lastread); + if (strlen(ctx->fbase) == 0) { + snprintf(ctx->fbase, 64, "img_%d", procid); + } + if (ctx->increment) { + snprintf(fname, 64, "%s_%d%s", ctx->fbase, ctx->imgseq, extn); + snprintf(fnab, 64, "%s_%d", ctx->fbase, ctx->imgseq); + + } else { + snprintf(fname, 64, "%s%s",ctx->fbase, extn); + strcpy(fnab, ctx->fbase); + } + printf("%s\n",fname); + + img = fopen(fname, "wb"); + if (img == NULL) { + DO_ERR( "cannot create file %s, error %s\n", fname, strerror(errno)); + } + if (pad) { + nwrite = retrBuf->imgsz; + } else { + nwrite = retrBuf->nread; + } + if (!cooked) { + + if (img) fwrite (retrBuf->buffer, sizeof(char), nwrite , img); + } else if (img) { + int actlines = nwrite / (KAF8300_MAX_X*2); + //int rem = nwrite % (KAF8300_MAX_X*2); + + fitsheader(KAF8300_ACTIVE_X, actlines, fnab, ctx->imgp); + int nwriteleft = nwrite; + unsigned char * bufp = retrBuf->buffer; + writelines = 0; + while (nwriteleft >= (KAF8300_MAX_X*2)) { + swab (bufp + (KAF8300_POSTAMBLE*2), linebuf, KAF8300_ACTIVE_X*2 ); + fwrite (linebuf, sizeof(char), KAF8300_ACTIVE_X*2 , img); + bufp += KAF8300_MAX_X*2; + nwriteleft -= KAF8300_MAX_X*2; + writelines++; + } + DO_INFO( "wrote %d lines\n", writelines); + } + if (img) fclose(img); +} + + + +void NsDownload::copydownload(unsigned char *buf, int xstart, int xlen, int xbin, int pad, int cooked) +{ + unsigned char linebuf[KAF8300_MAX_X*2]; + bool forwards = true; + bool rms = false; + int binning = xbin; + uint8_t * dbufp = buf; + uint8_t * bufp; + int nwrite = 0; + + if (retrBuf == NULL) { + DO_DBG("%s", "no image"); + return; + } + DO_INFO("done! blks %d totl %d last %d\n", retrBuf->nblks, retrBuf->nread,lastread); + + if (!cooked) { + if (pad) { + nwrite = retrBuf->imgsz; + } else { + nwrite = retrBuf->nread; + } + memcpy (dbufp, retrBuf->buffer, nwrite); + } else { + //int actlines = nwrite / (KAF8300_MAX_X*2); + //int rem = nwrite % (KAF8300_MAX_X*2); + nwrite = retrBuf->nread; + int nwriteleft = nwrite; + if (forwards) { + bufp = retrBuf->buffer; + //dbufp = (dbufp +(KAF8300_ACTIVE_X*2*IMG_Y)) - (KAF8300_ACTIVE_X*2); + } else { + bufp = (retrBuf->buffer + nwrite) - (KAF8300_MAX_X*2); + dbufp = (dbufp +(KAF8300_ACTIVE_X*2*IMG_Y)) - (KAF8300_ACTIVE_X*2); + } + writelines = 0; + while (nwriteleft >= (KAF8300_MAX_X*2)) { + //swab (bufp + (KAF8300_POSTAMBLE*2), linebuf, KAF8300_ACTIVE_X*2 ); + //memcpy (dbufp, linebuf, KAF8300_ACTIVE_X*2); + if (binning > 1) { + uint8_t * lbufp = bufp + (KAF8300_POSTAMBLE*2) + xstart*2; + int len = xlen * 2; + int linelen = 0; + while (len > 0) { + short px[4]; + long long pxsq =0; + long pxav =0; + short pxa; + memcpy(px, lbufp,binning * 2); + for (int a = 0; a < binning; a++) { + pxav += px[a]; + pxsq += px[a]*px[a]; + } + pxav /= binning; + pxsq /= binning; + if(rms) { + pxa = round(sqrt((double) pxsq)); + + } else { + pxa = pxav; + } + memcpy (linebuf + linelen, &pxa, 2); + linelen += 2; + lbufp += 2*binning; + len -= 2* binning; + } + memcpy (dbufp, linebuf, (xlen*2)/binning); + + } else { + memcpy (dbufp, bufp + (KAF8300_POSTAMBLE*2) + xstart*2, xlen * 2 ); //KAF8300_ACTIVE_X*2 ); + } + if (forwards) { + bufp += KAF8300_MAX_X*2; + dbufp +=(xlen*2)/binning; + } else { + bufp -= KAF8300_MAX_X*2; + dbufp-=(xlen*2)/binning; + } + nwriteleft -= KAF8300_MAX_X*2; + writelines++; + } + DO_INFO( "wrote %d lines\n", writelines); + } +} + +int NsDownload::purgedownload() +{ + int rc2; + rc2 = cn->readData(rd->buffer, rd->bufsiz); + if (rc2 < 0 ) { + DO_ERR( "purge: unable to read: %d \n", rc2); + return (-1); + } + if (rc2 > 0) { + DO_ERR("purge: spare read %d\n", rc2); + rc2 = cn->purgeData(); + if (rc2 < 0 ) { + DO_ERR( "unable to purge: %d\n", rc2); + return (-1); + } + } + return 0; +} + + +int NsDownload::fulldownload() +{ + int rc2; + + rc2 = cn->readData(rd->buffer+rd->nread, rd->bufsiz - rd->nread); + if (rc2 < 0 ) { + DO_ERR( "unable to read: %d\n", rc2); + return (-1); + } + if (rc2 > 0) { + DO_INFO("read %d\n", rc2); + rd->nread += rc2; + rd->nblks += rc2/65536; + DO_INFO("read %d tot %d\n", rc2, rd->nread); + + } + return rc2; +} + + +void NsDownload::initdownload() +{ + long imgszmax = KAF8300_MAX_X*0x9ca*2 + DEFAULT_CHUNK_SIZE; + readdone = 0; + rd->nread = 0; + if(!rd->buffer) { + rd->buffer = (unsigned char *)malloc(imgszmax); + } + memset(rd->buffer, 0, imgszmax); + + rd->bufsiz = imgszmax; + rd->nblks = 0; +} + + +// static void download_thread(NsDownload * d) { +// d->trun(); +//} + + +void NsDownload::trun() +{ + + int maxxfer; + int zeroes = 0; + //struct ftdi_context * ftdid = cn->getDataChannel(); + maxxfer = cn->getMaxXfer(); + + do { + DO_INFO("%s\n", "initdownload"); + std::unique_lock ulock(mutx); + + while(!do_download && !interrupted) go_download.wait(ulock); + if(interrupted) break; + initdownload(); + + + if (do_download && !in_download) { + //ftdi_usb_close(ftdid); + //maxxfer = opendownload(ftdid, ctx->dev); + in_download = 1; + ctx->imgseq++; + zeroes = 0; + } + while (in_download && !interrupted) { + //int rc2= cn->setDataRts();; + //if (rc2 < 0) { + // DO_ERR( "unable to set rts: %d\n", rc2); + //} + int down = 0; + if (zero_reads > 1) + down = fulldownload(); + else + down = downloader(); + if (down < 0) { + DO_ERR( "unable to read download: %d\n", down); + do_download = 0; + in_download = 0; + continue; + } + if (rd->nread < rd->imgsz) { + if (down == 0 && rd->nread > 0) { + zeroes++; + } + if (zeroes < zero_reads) continue; + } + int pad = 0; + if (rd->nread != rd->imgsz) { + int actlines = rd->nread / (KAF8300_MAX_X*2); + int rem = rd->nread % (KAF8300_MAX_X*2); + DO_INFO( "siz %d read %d act lines %d rem %d\n", rd->imgsz,rd->nread, actlines, rem); + if (rd->imgsz - rd->nread < KAF8300_MAX_X * 5) { + pad = 1; + } + } + lastread = down; + // IDLog("foop\n"); + + if (zero_reads > 1) { + rb = rdd; + retrBuf = &rb; + rd->buffer = NULL; + } + //IDLog("retr %p buf %p \n", retrBuf, rb.buffer); + if(write_it) writedownload(pad, 0); + + do_download = 0; + in_download = 0; + } + if (!in_download && !do_download) { + initdownload(); + purgedownload (); + } + } while(ctx->nexp >= ctx->imgseq && !interrupted); + DO_DBG("%s\n", "thread done"); + +} + + +void NsDownload::startThread(void) { + interrupted = 0; + sched_param sch_params; + sch_params.sched_priority = 3; + downthread = new std::thread (&NsDownload::trun, this); //(&NsDownload::trun, this); + pthread_setschedparam(downthread->native_handle(), SCHED_FIFO, &sch_params); + +} + +void NsDownload::stopThread(void) { + setInterrupted(); + downthread->join(); +} diff --git a/3rdparty/indi-nightscape/nsdownload.h b/3rdparty/indi-nightscape/nsdownload.h new file mode 100644 index 0000000000..9b5d217786 --- /dev/null +++ b/3rdparty/indi-nightscape/nsdownload.h @@ -0,0 +1,140 @@ +#ifndef __NS_DOWNLOAD_H__ +#define __NS_DOWNLOAD_H__ +#include "nschannel.h" +#include +#include +#include +#include // std::thread +#include + +typedef struct ns_readdata { + int nread; + int bufsiz; + unsigned char * buffer; + int nblks; + int imgsz; + +} ns_readdata_t; + + +struct img_params { + float exp; + float settemp; + float acttemp; + time_t expdate; + int ybinning; + int xbinning; +}; + +struct download_params { + int increment; + int imgseq; + int nexp; + char fbase[64]; + struct img_params * imgp; +}; + + +class NsDownload { + public: + NsDownload() { + + ctx = &dp; + ctx->imgp = &ip; + rd = &rdd; + retrBuf = &rb; + ctx->increment = 0; + ctx->nexp = 0; + rd->imgsz =0; + ctx->imgseq = 1; + + //strcpy(ctx->fbase, ""); + rd->buffer = NULL; + in_download = 0; + do_download = 0; + write_it = 0; + + readdone = 0; + retrBuf = NULL; + + } + NsDownload(NsChannel * chn) { + ctx = &dp; + ctx->imgp = &ip; + rd = &rdd; + retrBuf = &rb; + ctx->increment = 0; + ctx->nexp = 0; + rd->imgsz =0; + ctx->imgseq = 1; + + //strcpy(ctx->fbase, ""); + rd->buffer = NULL; + cn = chn; + in_download = 0; + do_download = 0; + write_it = 0; + readdone = 0; + retrBuf = NULL; + } + void setFrameYBinning(int binning); + void setFrameXBinning(int binning); + + void setSetTemp (float temp); + void setActTemp(float temp); + void setImgSize(int siz); + void setIncrement(int inc); + void setFbase(const char * name); + void nextImage(void); + void setNumExp(int n); + int getImgSeq(void); + void setExpDur(float exp); + void doDownload(); + void startThread(); + void stopThread(); + bool inDownload(); + int getActWriteLines(); + void trun(); + void initdownload(); + int downloader(); + int purgedownload(); + unsigned char * getBuf(); + size_t getBufImageSize(); + void setImgWrite(bool w); + void freeBuf(); + void setInterrupted(); + void copydownload(unsigned char *buf, int xstart, int xlen, int xbin, int pad, int cooked); + void writedownload(int pad, int cooked); + void setZeroReads(int zeroes); + private: + + void fitsheader(int x, int y, char * fbase, struct img_params * ip); + int fulldownload(); + bool getDoDownload(); + struct download_params dp; + struct img_params ip; + ns_readdata_t rdd; + + struct download_params * ctx; + ns_readdata_t *rd; + FILE * img; + volatile int readdone; + volatile int do_download; + volatile int in_download; + volatile int interrupted; + + NsChannel * cn; + int write_it; + int lastread; + std::thread * downthread; + std::condition_variable go_download; + std::mutex mutx; + + //static void download_thread(int x); + ns_readdata_t rb; + + ns_readdata_t * retrBuf; + int zero_reads { 1 }; + int writelines{0}; +}; +#endif \ No newline at end of file diff --git a/3rdparty/indi-nightscape/nsmsg.cpp b/3rdparty/indi-nightscape/nsmsg.cpp new file mode 100644 index 0000000000..7c23a1be9a --- /dev/null +++ b/3rdparty/indi-nightscape/nsmsg.cpp @@ -0,0 +1,316 @@ +#include "nsmsg.h" +#include +#include +#include +#include +#include "nsdebug.h" + const unsigned char Nsmsg::inq [CMD_SIZE] = {0xa5, 0x1, 0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + const unsigned char Nsmsg::sts [CMD_SIZE] = {0xa5, 0x2, 0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + const unsigned char Nsmsg::abt [CMD_SIZE] = {0xa5, 0x4, 0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + const unsigned char Nsmsg::stp [CMD_SIZE] = {0xa5, 0x8, 0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + const unsigned char Nsmsg::gtp [CMD_SIZE] = {0xa5, 0x9, 0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + const unsigned char Nsmsg::fan [CMD_SIZE] = {0xa5, 0xa, 0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + const unsigned char Nsmsg::inqr_8600 [CMD_SIZE] = {0xA5,1,1,0x80,0,0x4F,0xD8,1,0x26,0,0,0,0,0,0,0}; + const unsigned char Nsmsg::dur[CMD_SIZE] = {0xa5, 0x3, 0,0,0,0,0xb,0,0,0,0,0,0,0,0,0}; + const unsigned char Nsmsg::zon[CMD_SIZE] = {0xa5, 0x7, 0,0,0,0, 0,0,0,0, 0x1a,0x82,0,0,7,0}; + + +int Nsmsg::getRawImgSize(int start_y_offset, int num_lines, int framediv) { + cmdmutex.lock(); + calczone(start_y_offset, num_lines, framediv); + + cmdmutex.unlock(); + + return imgsz; +} + +int Nsmsg::getRawImgSize() { + return imgsz; +} + +int Nsmsg::sendtemp(float temp, bool cooler_on) { + int rc; + cmdmutex.lock(); + + settemp(temp, cooler_on); + rc = sendcmd("settemp"); + cmdmutex.unlock(); + + return rc; +} + + +int Nsmsg::senddur(float expo, int framediv, bool dark) { + int rc; + cmdmutex.lock(); + setdur(expo, framediv, dark); + rc = sendcmd("setdur"); + cmdmutex.unlock(); + + return rc; + +} + +int Nsmsg::sendzone(int start_y_offset, int num_lines, int framediv){ + int rc; + cmdmutex.lock(); + setzone(start_y_offset, num_lines, framediv); + rc = sendcmd("setzone"); + cmdmutex.unlock(); + + return rc; +} + +int Nsmsg::sendfan(int speed){ + int rc; + cmdmutex.lock(); + setfan(speed); + rc = sendcmd("setfan"); + cmdmutex.unlock(); + return rc; +} + +int Nsmsg::rcvstat(void){ + cmdmutex.lock(); + memcpy(cmd, sts, CMD_SIZE); + if (sendcmd("status") < 0) { + cmdmutex.unlock(); + return -1; + } + curr_status = resp[2]; + cmdmutex.unlock(); + return curr_status; +} + +float Nsmsg::rcvtemp(void){ + cmdmutex.lock(); + memcpy(cmd, gtp, CMD_SIZE); + if (sendcmd("gettemp") < 0) { + cmdmutex.unlock(); + return 99999.9; + } + resp_4 = resp[4]; + temp_act = gettemp(); + cmdmutex.unlock(); + return temp_act; +} + +int Nsmsg::getResp4() { + return resp_4; +} + +bool Nsmsg::inquiry (void) { + cmdmutex.lock(); + memcpy(cmd, inq, CMD_SIZE); + if(sendcmd("inquiry")< 0) return false; + if (memcmp(resp, inqr_8600, 5) != 0) { + DO_ERR("%s", "not an 8600\n"); + hexdump("<", resp, sizeof(inqr_8600)); + hexdump(">", inqr_8600, sizeof(inqr_8600)); + cmdmutex.unlock(); + + return false; + } else{ + snprintf(firmware_ver,25, "%c.%d.%d.%d\n", resp[5], resp[6], resp[7], resp[8]); // )hexdump("<", resp +5,4); + + DO_INFO("%c.%d.%d.%d\n", resp[5], resp[6], resp[7], resp[8]); // )hexdump("<", resp +5,4); + DO_INFO("%c.%d.%d.%d\n", inqr_8600[5], inqr_8600[6], inqr_8600[7], inqr_8600[8] );//hexdump(">", inqr_8600 +5,4); + } + hexdump("<", resp, sizeof(inqr_8600)); + + cmdmutex.unlock(); + return true; +} + +const char * Nsmsg::getFirmwareVer() { + return firmware_ver; +} + +int Nsmsg::abort(void) { + int rc; + cmdmutex.lock(); + memcpy(cmd, abt, CMD_SIZE); + rc = sendcmd("abort"); + cmdmutex.unlock(); + + return rc; +} + +void Nsmsg::settemp(float temp, bool cooler_on) { + short temp2; + memcpy(cmd, stp, CMD_SIZE); + temp2 = (short)(temp*100); + cmd [2] = cooler_on; + if (!cooler_on) temp2 = 10000; + cmd[3] = temp2>>8; + cmd[4] = temp2 & 0xff; + DO_DBG( "temp %02x %02x\n", cmd[3], cmd[4]); +} + +void Nsmsg::setdur(float expo, int framediv, bool dark) { + int exp2; + memcpy(cmd, dur, CMD_SIZE); + + exp2 = expo*1000; + if (framediv == 2) { + cmd[6] = 0x2b; + } else if (framediv == 4) { + cmd[6] = 0x4b; + } else { + cmd[6] = 0xb; + }; + + if (dark) { + cmd[6] &= ~0x8; + } + cmd[2] = exp2>>24; + cmd[3] = exp2>>16; + cmd[4] = exp2>>8; + cmd[5] = exp2 & 0xff; + DO_DBG( " exp %02x %02x %02x %02x %02x\n", cmd[2], cmd[3], cmd[4], cmd[5], cmd[6]); +} + + void Nsmsg::calczone(int start_y_offset, int num_lines, int framediv) { + + start_y =start_y_offset; + lines = num_lines; + const int min_y = 0x24; + const int max_y = 0x09ca; + const int max_half = 0x04e6; + const int max_quarter = 0x274; + int max; + if (framediv == 2) { + max = max_half; + } else if (framediv == 4) { + max = max_quarter; + } else { + max = max_y; + } + if (start_y < min_y) start_y = min_y; + if (start_y > max_y) start_y = max_y; + if (lines < 1 || lines > max) lines = max; + if ((lines + start_y) > (min_y+max)) lines = max -start_y; + if(lines < 1) lines = 1; + imgsz = KAF8300_MAX_X*lines*2; + +} + +void Nsmsg::setzone(int start_y, int num_lines, int framediv) { + memcpy(cmd, zon, CMD_SIZE); + calczone(start_y, num_lines, framediv); + cmd[2] = start_y>>8; + cmd[3] = start_y & 0xff; + cmd[4] = lines>>8; + cmd[5] = lines & 0xff; + //cmd[6] = dark * 128; + DO_DBG( "zone %02x %02x %02x %02x \n", zon[2], zon[3], zon[4], zon[5]); +} + +void Nsmsg::setfan(int speed) { + memcpy(cmd, fan, CMD_SIZE); + + const unsigned char spds [3] = {0x96, 0xc8, 0xff}; + cmd[2] = spds[speed -1]; +} + +float Nsmsg::gettemp(void) { + float temp = 0.0; + short temp2; + //DO_DBG( "gtemp %02x %02x\n", resp[2], resp[3]); + temp2 = (short)((resp[2]<<8)|(resp[3]&0xff)); + //DO_DBG( "gtemp %d %02x %02x\n", temp2, resp[2], resp[3]); + temp = temp2/100.0; + return temp; +} + + +int Nsmsg::sendcmd (const char * name) { + int rc; + int hardloop; + rc=chan->writeCommand(cmd, CMD_SIZE); + if (rc != CMD_SIZE) { + DO_ERR( "unable to write(%s): %d\n", name, rc); + chan->resetcontrol(); + return -1; + } + usleep(1000); + hardloop = 7; + int usec = 1000; + while((rc = chan->readCommand(resp, sizeof(resp))) == 0 && hardloop > 0) { + if (hardloop % 2 == 0) DO_INFO("CW%d\n",hardloop); + usleep(usec); + usec*= 2; + //if (usec > 100000) usec = 100000; + hardloop--; + } + if (rc != sizeof(resp)) { + DO_ERR("unable to read(%s) rc %d\n", name, rc); + chan->resetcontrol(); + + return -1; + } + if (resp[0] != cmd[0] || resp[1] != cmd[1]) { + DO_ERR( "not a %s %02x %02x\n", name, resp[0], resp[1]); + hexdump("<", resp, rc); + return -1; + } + //hexdump("<", resp, rc); + + return 0; +} + + void Nsmsg::hexdump(const char *pre, const void *data, int size) +{ + /* dumps size bytes of *data to stdout. Looks like: + * [0000] 75 6E 6B 6E 6F 77 6E 20 + * 30 FF 00 00 00 00 39 00 unknown 0.....9. + * (in a single line of course) + */ + + unsigned char *p = (unsigned char *)data; + unsigned char c; + int n; + char bytestr[4] = {0}; + char addrstr[10] = {0}; + char hexstr[ 16*3 + 5] = {0}; + char charstr[16*1 + 5] = {0}; + + for(n=1;n<=size;n++) { + if (n%16 == 1) { + /* store address for this line */ + snprintf(addrstr, sizeof(addrstr), "%.4lx", + ((unsigned long)p-(unsigned long)data) ); + } + + c = *p; + if (isalnum(c) == 0) { + c = '.'; + } + + /* store hex str (for left side) */ + snprintf(bytestr, sizeof(bytestr), "%02X ", *p); + strncat(hexstr, bytestr, sizeof(hexstr)-strlen(hexstr)-1); + + /* store char str (for right side) */ + snprintf(bytestr, sizeof(bytestr), "%c", c); + strncat(charstr, bytestr, sizeof(charstr)-strlen(charstr)-1); + + if(n%16 == 0) { + /* line completed */ + DO_DBG("%s [%4.4s] %-50.50s %s\n", pre, addrstr, hexstr, charstr); + hexstr[0] = 0; + charstr[0] = 0; + } else if(n%8 == 0) { + /* half line: add whitespaces */ + strncat(hexstr, " ", sizeof(hexstr)-strlen(hexstr)-1); + strncat(charstr, " ", sizeof(charstr)-strlen(charstr)-1); + } + p++; /* next byte */ + } + + if (strlen(hexstr) > 0) { + /* print rest of buffer if not empty */ + DO_DBG("%s [%4.4s] %-50.50s %s\n", pre, addrstr, hexstr, charstr); + } +} + \ No newline at end of file diff --git a/3rdparty/indi-nightscape/nsmsg.h b/3rdparty/indi-nightscape/nsmsg.h new file mode 100644 index 0000000000..b245c3b67a --- /dev/null +++ b/3rdparty/indi-nightscape/nsmsg.h @@ -0,0 +1,65 @@ +#ifndef __NS_MSG_H__ +#define __NS_MSG_H__ + +#define CMD_SIZE 16 +#include +#include "kaf_constants.h" +#include"nschannel.h" +#include +class Nsmsg +{ + public: + Nsmsg(); + Nsmsg(NsChannel * channel) { + this->chan = channel; + }; + int sendtemp(float temp, bool cooler_on); + int senddur(float expo, int framediv, bool dark); + int sendzone(int start_y, int lines, int framediv); + int sendfan(int speed); + int rcvstat(void); + float rcvtemp(void); + int getRawImgSize(); + int getRawImgSize(int start_y, int lines, int framediv); + bool inquiry (void); + int abort(void); + int getResp4(); + const char * getFirmwareVer(); + private: + static const unsigned char inq [CMD_SIZE]; + static const unsigned char sts [CMD_SIZE]; + static const unsigned char abt [CMD_SIZE]; + static const unsigned char stp [CMD_SIZE]; + static const unsigned char gtp [CMD_SIZE]; + static const unsigned char fan [CMD_SIZE]; + static const unsigned char inqr_8600 [CMD_SIZE]; + static const unsigned char dur[CMD_SIZE]; + static const unsigned char zon[CMD_SIZE]; + + short start_y; + short lines; + int imgsz; + float temp_set; + float temp_act; + int curr_status; + int resp_4; //purpose? + char firmware_ver [25]; + unsigned char cmd [CMD_SIZE]; + + unsigned char resp [CMD_SIZE + 1]; + + NsChannel * chan; + + void calczone(int start_y_offset, int num_lines, int framediv); + + void settemp(float temp, bool cooler_on); + void setdur(float expo, int framediv, bool dark); + void setzone(int start_y, int lines, int framediv); + void setfan(int speed); + float gettemp(void); + int sendcmd (const char * name); + void hexdump(const char *pre, const void *data, int size); + std::mutex cmdmutex; + +}; +#endif \ No newline at end of file diff --git a/3rdparty/indi-nightscape/nsstatus.cpp b/3rdparty/indi-nightscape/nsstatus.cpp new file mode 100644 index 0000000000..d5fc8c77e0 --- /dev/null +++ b/3rdparty/indi-nightscape/nsstatus.cpp @@ -0,0 +1,87 @@ +#include "nsstatus.h" +#include + + +static long long millis() +{ + struct timeval now; + gettimeofday(&now, NULL); + long long t2 = (long long)now.tv_usec + (long long)(now.tv_sec*1000000); + return t2 / 1000; +} + +int NsStatus::getStatus() { + return status; +} + +void NsStatus::startThread() { + sched_param sch_params; + sch_params.sched_priority = 3; + statThread = new std::thread (&NsStatus::trun, this); //(&NsDownload::trun, this); + pthread_setschedparam(statThread->native_handle(), SCHED_FIFO, &sch_params); + +} + +void NsStatus::stopThread() { + setInterrupted(); + statThread->join(); +} + + +void NsStatus::setInterrupted(){ + std::unique_lock ulock(stmut); + interrupted = 1; + go_status.notify_all(); +} + +void NsStatus::trun(){ + + do { + DO_INFO("%s\n", "status thread"); + std::unique_lock ulock(stmut); + + while(!do_status && !interrupted) go_status.wait(ulock); + DO_DBG ("%s\n", "status thread wakeup"); + + bool download = false; + bool done = false; + while (!done&&!interrupted) { + status = m->rcvstat(); + if (status < 0) { + done = true; + DO_ERR("%s\n", "status read failed.."); + + } + if (old_status == 1 && status == 2) { + stattime = millis(); + } + if ((old_status == 2 && status == 0) || + (status == 2 && ( millis() - stattime >= 9500)) ) { + if (!download) { + d->doDownload(); + DO_DBG("%s\n", "download start.."); + } + download = true; + do_status = 0; + if(status == 0) done = true; + + } + if (old_status != status) { + DO_DBG("status change %d\n", status); + } + old_status = status; + usleep(33000); + + + } + } while (!interrupted); + DO_DBG ("%s", "status thread terminated"); +} + +void NsStatus::doStatus() { + + std::unique_lock ulock(stmut); + + do_status = 1; + go_status.notify_all(); +} \ No newline at end of file diff --git a/3rdparty/indi-nightscape/nsstatus.h b/3rdparty/indi-nightscape/nsstatus.h new file mode 100644 index 0000000000..dd15aaf7da --- /dev/null +++ b/3rdparty/indi-nightscape/nsstatus.h @@ -0,0 +1,53 @@ +#ifndef _NS_STATUS_H__ +#define _NS_STATUS_H__ +#include +#include // std::thread +#include "nsdebug.h" + +#include +#include "nsmsg.h" +#include "nschannel.h" +#include "nsdownload.h" + +class NsStatus { + public: + NsStatus () { + m = NULL; + status = 0; + old_status = 0; + do_status = 0; + interrupted = 0; + + } + NsStatus (Nsmsg * ms, NsDownload *dn) { + m = ms; + d = dn; + status = 0; + old_status = 0; + do_status = 0; + interrupted = 0; + + } + int getStatus(); + void doStatus(); + void startThread(); + void stopThread(); + + private: + long long stattime {0}; + void setInterrupted(); + void trun(); + volatile int status; + int old_status; + volatile int do_status { 0 }; + volatile int interrupted; + + std::thread * statThread; + std::condition_variable go_status; + std::mutex stmut; + + Nsmsg * m; + NsDownload * d; +}; + +#endif \ No newline at end of file diff --git a/3rdparty/indi-nightscape/nstest-main.cpp b/3rdparty/indi-nightscape/nstest-main.cpp new file mode 100644 index 0000000000..e7e3376feb --- /dev/null +++ b/3rdparty/indi-nightscape/nstest-main.cpp @@ -0,0 +1,282 @@ +/* serial_read.c + + Read data via serial I/O + + This program is distributed under the GPL, version 2 +*/ +#include +#include +#include +#include +#include +#include +//#include +#include + +#include +#include +#include +#include +#include +#include "kaf_constants.h" +#include "nsmsg.h" +#include "nsdownload.h" +#include "nsdebug.h" +#include "nschannel-u.h" +#ifdef HAVE_D2XX +#include "nschannel-ftd.h" +#endif +#include + +static volatile int interrupted = 0; +//static volatile int readdone = 0; + + +void siginthandler(int s) { + interrupted = 1; +} + + +void IDLog (const char * fmt, ...) +{ + va_list args; + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); +} + +long long millis() +{ + struct timeval now; + gettimeofday(&now, NULL); + long long t2 = (long long)now.tv_usec + (long long)(now.tv_sec*1000000); + return t2 / 1000; +} + + + +void usage(char * prog) +{ + fprintf(stderr, "usage: %s [-c camera] [-f fanspeed=1-3] [-n num exp] [-t temp(c)] [ -d tdiff(c)] [-e exposure(s)] [-b binning=1|2] [-z start,lines] increment [-i] dark [-k]\n", prog); + exit(-1); +} + + +int main(int argc, char **argv) +{ + int ftd = 1; + //char eeprom [128]; + int i; + //int baudrate = 115200; + //int interface = INTERFACE_ANY; + int fanspeed = 1; + int deffanspeed = 1; + int curfanspeed = -1; + float temp = -7.00; + float tempdif = 0.5; + float expdur = 1.00; + long long start, last, now, lastfan, lasttemp; + int camnum = 0; + int binning = 1; + //long imgszmax = 3448*2574*2 + DEFAULT_CHUNK_SIZE; + int zonestart = 0; + int zoneend =0; + int lcount = 0; + int scount = 0; + int in_exp = 0; + int old_busy_flag =0; + int download = 0; + //int imgseq = 1; + int done_first = 0; + int nexp = 0; + //unsigned char buf[chunksize]; + int threaded = 1; + int increment = 0; + char fbase [64]; + int laststat = 0; + bool dark = false; + //char fbase[64] = ""; + + //bigbuf = malloc(3358*2536*2); + signal(SIGINT, siginthandler); + while ((i = getopt(argc, argv, "t:f:c:n:e:b:z:d:o:ik")) != -1) + { + switch (i) + { + case 't': // 0=ANY, 1=A, 2=B, 3=C, 4=D + temp = strtof(optarg, NULL); + break; + case 'f': + deffanspeed = strtoul(optarg, NULL,0); + break; + case 'c': + camnum = strtoul(optarg, NULL, 0); + break; + case 'n': + nexp = strtoul(optarg, NULL, 0); + break; + case 'e': + expdur = strtof(optarg, NULL); + break; + case 'b': + binning = strtoul(optarg, NULL, 0); + break; + case 'z': + if (strstr(optarg, ",") == NULL) usage(argv[0]); + zonestart = strtoul(strtok(optarg, ","), NULL, 0); + zoneend = strtoul(strtok(NULL, ","), NULL, 0); + break; + case 'd': + tempdif = strtof(optarg, NULL); + break; + case 'i': + increment = 1; + break; + case 'o': + strncpy(fbase, optarg, 64); + break; + case 'k': + dark = true; + break; + default: + usage(argv[0]); + break; + } + } + + NsChannel * cn; +#ifdef HAVE_D2XX + if (ftd) { + cn = new NsChannelFTD(camnum); + } else { + cn = new NsChannelU(camnum); + } +#else + ftd = 0; + cn = new NsChannelU(camnum); +#endif + if (cn->open() < 0) exit (-1); + + Nsmsg * m = new Nsmsg(cn); + + NsDownload * d = new NsDownload(cn); + + d->setFrameXBinning(binning); + d->setFrameYBinning(binning); + + d->setSetTemp(temp); + + d->setImgSize(m->getRawImgSize(zonestart,zoneend,binning)); + + d->setExpDur(expdur); + d->setIncrement(increment); + d->setFbase(fbase); + d->setNumExp(nexp); + d->setImgWrite(true); + + if(!m->inquiry()) exit(-1); + + start = millis(); + last = start; + now = start; + lastfan = start; + lasttemp = start; + long long sdiff, tdiff, fandiff; + if (threaded) { + d->startThread(); + } + + while (!interrupted) { + int busy_flag =0; + if (!download) { + now = millis(); + sdiff = now - last; + fandiff = now - lastfan; + tdiff = now - lasttemp; + } + if ((sdiff > 50) && !download) { + busy_flag = m->rcvstat(); + if (old_busy_flag != busy_flag) { + fprintf(stderr,"status change %d millis %lld\n", busy_flag, now - laststat); + laststat = now; + } + if (in_exp && old_busy_flag == 2 && busy_flag == 0) { + download = 1; + if(threaded) d->doDownload(); + + fprintf(stderr,"downloading image..\n"); + if (!threaded) { + d->nextImage(); + //d->initdownload(); + } + } + old_busy_flag = busy_flag; + scount++; + last = now; + sdiff = 0; + } + if (download) { + if (!threaded) { + download = d->downloader(); + } + else download = d->inDownload(); + if (!download) { + in_exp = 0; + // + if (!threaded) { + d->writedownload(0,1); + d->freeBuf(); + } + if (d->getImgSeq() > nexp) interrupted = 1; + } + } + + if (download) { + if (threaded) sleep(1); + } else { + usleep(2000); + } + lcount++; + if (!interrupted && scount == 1 && !done_first) { + fprintf(stderr,"settemp %f\n", temp); + m->sendtemp(temp, 1); + } + if (!interrupted && ((scount == 1 && !done_first) || (tdiff > (10 * 1000))) && !in_exp) { + done_first = 1; + + float acttemp = m->rcvtemp(); + d->setActTemp(acttemp); + fprintf(stderr, "setpoint %f temp %f %d\n", temp,acttemp, m->getResp4()); + if (fabs(acttemp - temp) < tempdif) { + fanspeed = 1; + } else { + fanspeed = deffanspeed; + } + if (fanspeed != curfanspeed) { + fprintf(stderr,"fan speed %d\n", fanspeed); + lastfan = now; + fandiff = 0; + m->sendfan(fanspeed); + curfanspeed = fanspeed; + } + lasttemp = now; + tdiff = 0; + } + if (!interrupted && fandiff > (10 * 1000) &&!in_exp && fanspeed == 1) { + fprintf(stderr,"now do exp\n"); + if (!threaded) { + d->initdownload(); + d->purgedownload(); + } + m->sendzone(zonestart,zoneend,binning); + m->senddur(expdur, binning, dark); + in_exp = 1; + //if(threaded) d->doDownload(); + } + } + d->setInterrupted(); + fprintf(stderr,"exiting\n"); + m->abort(); + m->sendfan(deffanspeed); + cn->close(); +} diff --git a/cmake_modules/FindD2XX.cmake b/cmake_modules/FindD2XX.cmake new file mode 100644 index 0000000000..7a4648efd4 --- /dev/null +++ b/cmake_modules/FindD2XX.cmake @@ -0,0 +1,56 @@ +# - Try to find D2XX +# Once done this will define +# +# D2XX_FOUND - system has FTDI +# D2XX_INCLUDE_DIR - the FTDI include directory +# D2XX_LIBRARIES - Link these to use FTDI +# +# N.B. You must include the file as following: +# +#include +# +# Redistribution and use is allowed according to the terms of the BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. + +if (D2XX_INCLUDE_DIR AND D2XX_LIBRARIES) + + # in cache already + set(D2XX_FOUND TRUE) + message(STATUS "Found libfd2xx: ${D2XXX_LIBRARIES}") + +else (D2XX_INCLUDE_DIR AND D2XX_LIBRARIES) + + find_path(D2XX_INCLUDE_DIR ftd2xx.h + #PATH_SUFFIXES libD2XX + ${_obIncDir} + ${GNUWIN32_DIR}/include + /usr/local/include + ) + + find_library(D2XX_LIBRARIES NAMES ftd2xx + PATHS + ${_obLinkDir} + ${GNUWIN32_DIR}/lib + /usr/local/lib + ) + + if(D2XX_INCLUDE_DIR AND D2XX_LIBRARIES) + set(D2XX_FOUND TRUE) + else (D2XX_INCLUDE_DIR AND D2XX_LIBRARIES) + set(D2XX_FOUND FALSE) + endif(D2XX_INCLUDE_DIR AND D2XX_LIBRARIES) + + + if (D2XX_FOUND) + if (NOT D2XX_FIND_QUIETLY) + message(STATUS "Found D2XX: ${D2XX_LIBRARIES}") + endif (NOT D2XX_FIND_QUIETLY) + else (D2XX_FOUND) + if (D2XX_FIND_REQUIRED) + message(FATAL_ERROR "D2XX not found. Please install libd2xx") + endif (D2XX_FIND_REQUIRED) + endif (D2XX_FOUND) + + mark_as_advanced(D2XX_INCLUDE_DIR D2XX_LIBRARIES) + +endif (D2XX_INCLUDE_DIR AND D2XX_LIBRARIES) diff --git a/libindi/drivers/filter_wheel/ifwoptec.cpp b/libindi/drivers/filter_wheel/ifwoptec.cpp index a98435a0c5..ff82b8b1b2 100644 --- a/libindi/drivers/filter_wheel/ifwoptec.cpp +++ b/libindi/drivers/filter_wheel/ifwoptec.cpp @@ -20,6 +20,7 @@ #include "indicom.h" #include "indicontroller.h" +//#include "connectionplugins/connectioninterface.h" #include "connectionplugins/connectionserial.h" #include @@ -114,7 +115,8 @@ FilterIFW::FilterIFW() strncpy(filterSim, filterSim5, sizeof(filterSim)); // For simulation mode // Set communication to serail only and avoid driver crash at starting up - setFilterConnection(CONNECTION_SERIAL); + // setFilterConnection(CONNECTION_SERIAL); + setFilterConnection(CONNECTION_SERIAL | CONNECTION_TCP); // We add an additional debug level so we can log verbose member function starting // DBG_TAG is used by macro DEBUGTAG() define in ifwoptec.h @@ -166,9 +168,9 @@ bool FilterIFW::initProperties() IUFillText(&FirmwareT[0], "FIRMWARE", "Firmware", "Unknown"); IUFillTextVector(&FirmwareTP, FirmwareT, 1, getDeviceName(), "FIRMWARE_ID", "IFW", FILTER_TAB, IP_RO, 60, IPS_IDLE); - serialConnection->setDefaultBaudRate(Connection::Serial::B_19200); addAuxControls(); + serialConnection->setDefaultBaudRate(Connection::Serial::B_19200); return true; } @@ -178,7 +180,8 @@ bool FilterIFW::initProperties() ************************************************************************************/ bool FilterIFW::updateProperties() { - if (isConnected()) + INDI::FilterWheel::updateProperties(); + if (isConnected()) { defineSwitch(&HomeSP); defineText(&FirmwareTP); @@ -255,7 +258,7 @@ bool FilterIFW::ReadTTY(char *resp, char *simulation, int timeout) if ((errcode = tty_read_section(PortFD, response, 0xd, timeout, &nbytes_read)) != TTY_OK) { tty_error_msg(errcode, errmsg, MAXRBUF); - LOGF_ERROR("%s() TTY error: %s", __FUNCTION__, "errmsg"); + LOGF_ERROR("%s() TTY error: %s", __FUNCTION__, errmsg); return false; } } @@ -269,7 +272,7 @@ bool FilterIFW::ReadTTY(char *resp, char *simulation, int timeout) response[nbytes_read - 2] = '\0'; //Remove control char from string (\n\r) LOGF_DEBUG("RES (%s)", response); - strncpy(resp, response, OPTEC_MAXLEN_RESP + 1); + strncpy(resp, response, /* sizeof(response)*/ OPTEC_MAXLEN_RESP +1); return true; } @@ -280,7 +283,6 @@ bool FilterIFW::Handshake() { char response[OPTEC_MAXLEN_RESP + 1]; memset(response, 0, sizeof(response)); - if (!WriteTTY((char *)"WSMODE")) { LOGF_ERROR("(Function %s()) failed to write to TTY", __FUNCTION__);