diff --git a/Daemons/10micron_stellarium/10micron_commands.h b/Daemons/10micron_stellarium/10micron_commands.h index 1d00641..04c35b6 100644 --- a/Daemons/10micron_stellarium/10micron_commands.h +++ b/Daemons/10micron_stellarium/10micron_commands.h @@ -25,6 +25,8 @@ #define CMD_GOTOAZ ":MA#" #define CMD_SLEWRADEC ":MS#" #define CMD_BAUDRATE ":SB0#" +#define CMD_DUALTRK ":Sdat1#" +#define CMD_REFCORR_ON ":SREF1#" #define CMD_STOP ":STOP#" #define CMD_HIGHPREC ":U2#" @@ -34,10 +36,14 @@ #define CMD_GETDEC ":GD#" #define CMD_GETSTAT ":Gstat#" #define CMD_GETRA ":GR#" +#define CMD_GETPS ":pS#" // setters #define CMD_SETALT ":Sa%s#" #define CMD_SETDEC ":Sd%s#" -#define CMD_SETRA ":Sr%s#" +#define CMD_SETTIME ":SLDT%04d-%02d-%02d,%02d:%02d:%02d.%02ld#" #define CMD_SETMINALT ":So%d#" +#define CMD_SETRA ":Sr%s#" +#define CMD_SETPRESSURE ":SRPRS%.1f#" +#define CMD_SETTEMPER ":SRTMP%.1f#" #define CMD_SETZD ":Sz%s#" diff --git a/Daemons/10micron_stellarium/angles.c b/Daemons/10micron_stellarium/angles.c index 44dc720..1e514b4 100644 --- a/Daemons/10micron_stellarium/angles.c +++ b/Daemons/10micron_stellarium/angles.c @@ -227,9 +227,9 @@ void eq2horH(polarCrds_t *pc, horizCrds_t *h){ /** * @brief eq2hor - convert polar coordinates to horizontal - * @param pc (i) - polar coordinates (only RA used) - * @param h (o) - horizontal coordinates - * @param sidTime - sidereal time + * @param pc (i) - polar coordinates (RA, EO, DEC used) (radians) + * @param h (o) - horizontal coordinates (radians) + * @param sidTime - sidereal time (radians) */ void eq2hor(polarCrds_t *pc, horizCrds_t *h, double sidTime){ if(!h || !pc) return; @@ -262,7 +262,7 @@ void r2sDMS(double radians, char *dms, int len){ char pm; int i[4]; eraA2af(1, radians, &pm, i); - snprintf(dms, len, "'%c%02d:%02d:%02d.%d'", pm, i[0],i[1],i[2],i[3]); + snprintf(dms, len, "%c%02d:%02d:%02d.%d", pm, i[0],i[1],i[2],i[3]); } // the same, but argument in degrees void d2sDMS(double degrees, char *dms, int len){ diff --git a/Daemons/10micron_stellarium/fitshdr.c b/Daemons/10micron_stellarium/fitshdr.c new file mode 100644 index 0000000..6337f1d --- /dev/null +++ b/Daemons/10micron_stellarium/fitshdr.c @@ -0,0 +1,203 @@ +/* + * This file is part of the mountdaemon_10micron project. + * Copyright 2026 Edward V. Emelianov . + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include + +#include "fitshdr.h" +#include "mount.h" + +static char *hdname = NULL; + +#ifndef FLT_EPSILON +#define FLT_EPSILON 1e-6 +#endif + +/** + * @brief set_header_name - check ability of witing into file and set given name + * @param name - header filename + * @return false if can't write into `name` + */ +bool set_header_name(const char *name){ + if(!name) return false; + int fd; + if((fd = open(name, O_WRONLY | O_TRUNC | O_CREAT, 0644)) < 0){ // test FITS-header file for writing + WARN("Can't open %s for writing", name); + return false; + } + close(fd); + FREE(hdname); + hdname = strdup(name); + return true; +} + +/** + * @brief printhdr - write FITS record into output file + * @param fd - fd to write + * @param key - key + * @param val - value + * @param cmnt - comment + * @return 0 if all OK + */ +static int printhdr(int fd, const char *key, const char *val, const char *cmnt){ + char tmp[81]; + char tk[9]; + if(strlen(key) > 8){ + snprintf(tk, 8, "%s", key); + tk[8] = 0; + key = tk; + } + size_t L = 0; + if(cmnt){ + L = snprintf(tmp, 81, "%-8s= %-21s / %s", key, val, cmnt); + }else{ + L = snprintf(tmp, 81, "%-8s= %s", key, val); + } + if(L > 80){ + tmp[80] = 0; + L = 80; + } + tmp[L++] = '\n'; + if(write(fd, tmp, L) != (ssize_t)L){ + WARN("write()"); + return 1; + } + return 0; +} + +/** + * @brief wrhdr - try to write into header file + */ +void wrhdr(fitsheader_t *HDR){ + if(!HDR) return; + static time_t lastcorr = 0; // last time of time/weather corrections sent to mount + time_t curtime = time(NULL); + bool haveweather = false; + bool poweredON = (HDR->status == MNT_S_ERROR || HDR->status == MNT_S_OFF) ? false : true; + // weather block + if(HDR->weather.last_update < (int)(1 + MOUNT_CHECK_T * 2)){ // weather data is good + if(time(NULL) - lastcorr > CORRECTIONS_TIMEDIFF){ // make correction once per hour + if(mount_corrdata(&HDR->weather)) lastcorr = time(NULL); + } + haveweather = true; + } + if(!hdname){ + DBG("hdname not given!"); + return; + } + char aname[PATH_MAX]; + size_t L = snprintf(aname, PATH_MAX, "%sXXXXXX", hdname); + if(L == PATH_MAX) aname[PATH_MAX-1] = 0; + int fd = mkstemp(aname); + if(fd < 0){ + WARN("Can't write header file: mkstemp()"); + return; + } + fchmod(fd, 0644); + char val[23]; + val[22] = 0; +#define WRHDR(k, v, c) do{if(printhdr(fd, k, v, c)){goto returning;}}while(0) + WRHDR("TIMESYS", "'UTC'", "Time system"); + WRHDR("ORIGIN", "'SAO RAS'", "Organization responsible for the data"); + WRHDR("MOUNTNAM", HDR->mountname, "Mount name"); + if(fabs(HDR->dut.px) > FLT_EPSILON){ + snprintf(val, 22, "%.10f", HDR->dut.px); + WRHDR("POLARX", val, "IERS pole X coordinate, arcsec"); + } + if(fabs(HDR->dut.py) > FLT_EPSILON){ + snprintf(val, 22, "%.10f", HDR->dut.py); + WRHDR("POLARY", val, "IERS pole Y coordinate, arcsec"); + } + if(fabs(HDR->dut.DUT1) > FLT_EPSILON){ + snprintf(val, 22, "%.10f", HDR->dut.DUT1); + WRHDR("DUT1", val, "IERS `UT1-UTC`, sec"); + } + polarCrds_t polar; + horizCrds_t horiz; + double pt = mount_getInpCoords(&polar), ht = mount_getInpHor(&horiz); + if(pt > ht){ // horcrds is older -> show polar + if(curtime - pt < COORDS_OLD_T){ + snprintf(val, 22, "%.10f", RAD2DEG(polar.ra)); + WRHDR("TAGRA", val, "Target RA (J2000), degrees"); + snprintf(val, 22, "%.10f", RAD2DEG(polar.dec)); + WRHDR("TAGDEC", val, "Target DEC (J2000), degrees"); + } + }else{ // show horiz + if(curtime - ht < COORDS_OLD_T){ + snprintf(val, 22, "%.10f", RAD2DEG(horiz.az)); + WRHDR("TAGAZ", val, "Target Azimuth, degrees"); + snprintf(val, 22, "%.10f", RAD2DEG(horiz.zd)); + WRHDR("TAGZD", val, "Target Zenith dist., degrees"); + } + } + if(poweredON){ + snprintf(val, 22, "%.10f", RAD2DEG(HDR->polar.ra)); // convert RA to degrees + WRHDR("RA", val, "Telescope right ascension, current epoch, deg"); + snprintf(val, 22, "%.10f", RAD2DEG(HDR->polar.dec)); + WRHDR("DEC", val, "Telescope declination, current epoch, deg"); + snprintf(val, 22, "%.10f", RAD2DEG(HDR->altaz.az)); + WRHDR("AZ", val, "Telescope azimuth, current epoch, deg"); + snprintf(val, 22, "%.10f", RAD2DEG(HDR->altaz.zd)); + WRHDR("ZD", val, "Telescope zenith distance, current epoch, deg"); + } + WRHDR("TELSTAT", mount_status_str(HDR->status), "Telescope mount status"); + double mjd; + mount_getInpMJD(&mjd); + snprintf(val, 22, "%.10f", 2000.+(mjd - ERFA_DJM00)/365.25); // calculate EPOCH/EQUINOX + WRHDR("INPEQUIN", val, "Equinox of input celestial coordinate system"); + if(poweredON){ + snprintf(val, 22, "%.10f", 2000.+(HDR->MJD.MJD - ERFA_DJM00)/365.25); // telescope coordinates: JNOW + WRHDR("EQUINOX", val, "Equinox of telescope celestial coordinate sys."); + snprintf(val, 22, "%.10f", HDR->MJD.MJD); + WRHDR("MJD", val, "Modified Julian date of file creation"); + if(*HDR->pierside) WRHDR("PIERSIDE", HDR->pierside, "Pier side of telescope mount"); + } + snprintf(val, 22, "%.1f", HDR->place.salt); + WRHDR("ELEVAT", val, "Elevation of site over the sea level"); + snprintf(val, 22, "%.10f", RAD2DEG(HDR->place.slong)); + WRHDR("LONGITUD", val, "Geo longitude of site (east negative)"); + snprintf(val, 22, "%.10f", RAD2DEG(HDR->place.slat)); + WRHDR("LATITUDE", val, "Geo latitude of site (south negative)"); + snprintf(val, 22, "%.4f", RAD2HRS(HDR->sidtime)); + WRHDR("LSTEND", val, "Local sidereal time of file creation"); + if(haveweather){ + snprintf(val, 22, "%.1f", HDR->weather.humidity); + WRHDR("HUMIDITY", val, "Relative humidity, %%"); + snprintf(val, 22, "%.1f", HDR->weather.pressure); + WRHDR("PRESSURE", val, "Atmospheric pressure, mmHg"); + snprintf(val, 22, "%.1f", HDR->weather.exttemp); + WRHDR("EXTTEMP", val, "External temperature, degrC"); + snprintf(val, 22, "%d", HDR->weather.rain); + WRHDR("RAIN", val, "Rain conditions"); + snprintf(val, 22, "%.1f", HDR->weather.clouds); + WRHDR("SKYQUAL", val, "Sky quality (0 - wery bad, >2500 - good)"); + snprintf(val, 22, "%.1f", HDR->weather.wind); + WRHDR("WINDSPD", val, "Wind speed (m/s)"); + snprintf(val, 22, "%.1f", HDR->weather.windmax); + WRHDR("WINDMAX", val, "Last hour maximal wind speed (m/s)"); + snprintf(val, 22, "%zd", HDR->weather.last_update); + WRHDR("WEATTIME", val, "Unix time of weather measurements"); + } + // WRHDR("", , ""); +#undef WRHDR +returning: + close(fd); + rename(aname, hdname); +} diff --git a/Daemons/10micron_stellarium/fitshdr.h b/Daemons/10micron_stellarium/fitshdr.h new file mode 100644 index 0000000..a8b4a85 --- /dev/null +++ b/Daemons/10micron_stellarium/fitshdr.h @@ -0,0 +1,44 @@ +/* + * This file is part of the mountdaemon_10micron project. + * Copyright 2026 Edward V. Emelianov . + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include + +#include "angles.h" +#include "mount.h" + +// maximal string length (including terminating zero) +#define MAX_HDR_STRLEN 32 + +// status checking interval and writing FITS-header, seconds +#define MOUNT_CHECK_T 0.5 + +typedef struct{ + weather_data_t weather; + mount_status_t status; + almDut_t dut; + placeData_t place; + polarCrds_t polar; + horizCrds_t altaz; + sMJD_t MJD; + double sidtime; + char pierside[MAX_HDR_STRLEN]; + char mountname[MAX_HDR_STRLEN]; +} fitsheader_t; + +bool set_header_name(const char *name); +void wrhdr(fitsheader_t *HDR); diff --git a/Daemons/10micron_stellarium/main.c b/Daemons/10micron_stellarium/main.c index 271dd1b..0ccd7e0 100644 --- a/Daemons/10micron_stellarium/main.c +++ b/Daemons/10micron_stellarium/main.c @@ -25,14 +25,12 @@ #include #include "args.h" +#include "fitshdr.h" #include "server.h" #include "mount.h" static pid_t childpid = -1; // PID of child process static parameters_t *G = NULL; -#ifndef EBUG -static bool isrunning = false; -#endif void signals(int sig){ if(childpid){ // single or parent process @@ -49,9 +47,6 @@ void signals(int sig){ LOGWARN("Child %d died with %d", getpid(), sig); server_stop(); } -#ifndef EBUG - isrunning = true; -#endif DBG("EXIT"); } @@ -62,12 +57,7 @@ int main(int argc, char **argv){ sl_loglevel_e lvl = G->verbose + LOGLEVEL_ERR; if(lvl >= LOGLEVEL_AMOUNT) lvl = LOGLEVEL_AMOUNT - 1; DBG("verb: %d, level: %d", G->verbose, lvl); - int fd; - if((fd = open(G->crdsfile, O_WRONLY | O_TRUNC | O_CREAT, 0644)) < 0){ // test FITS-header file for writing - WARN(_("Can't open %s for writing"), G->crdsfile); - return 1; - } - close(fd); + if(!set_header_name(G->crdsfile)) return 1; if(G->sleept < 1){ WARNX("Sleeping time sould be positive value (%d)", G->sleept); return 2; diff --git a/Daemons/10micron_stellarium/mount.c b/Daemons/10micron_stellarium/mount.c index e89c339..dbfc7a5 100644 --- a/Daemons/10micron_stellarium/mount.c +++ b/Daemons/10micron_stellarium/mount.c @@ -20,21 +20,23 @@ #include #include #include +#include #include #include "10micron_commands.h" #include "emulation.h" #include "mount.h" +#include "server.h" // isrunning -#define MNAME_LEN 31 +#define MNAME_LEN 32 static bool isemulated = false; // ==true for emulation mode // default serial timeout, seconds static double sertmout = 1.; -static char mount_name[MNAME_LEN+3] = "'noname'"; +static char mount_name[MNAME_LEN] = "noname"; static sl_tty_t *mount_dev = NULL; // device mutex, blocking only in non-local functions static pthread_mutex_t mntdev_mutex = PTHREAD_MUTEX_INITIALIZER; @@ -47,7 +49,7 @@ static horizCrds_t ParkCoords = {.az = 0., .zd = DEG2RAD(80.)}; static polarCrds_t InpCoords = {0}, // input as user give (for epoch InpMJD) TagCoords = {0}; // target for Jnow after command "point to input" static horizCrds_t InpHoriz = {0}; -// input MJD (Modified Julian Date: started from ERFA_DJM0==2400000.5 +// input MJD (Modified Julian Date: started from ERFA_DJM0==2400000.5) static double InpMJD = ERFA_DJM00; // J2000 // times of coords/mjd change static double InpCTime = 0., TagTime = 0., InpHTime = 0., InpMTime = 0.; @@ -141,11 +143,20 @@ bool mount_setInpMJD(double m){ bool mount_set_name(const char *name){ if(!name || !*name) return false; int l = strlen(name); - if(l > MNAME_LEN) return false; - sprintf(mount_name, "'%s'", name); + if(l > MNAME_LEN-1) return false; + strcpy(mount_name, name); return true; } +void mount_get_name(char *nm, size_t l){ + if(!nm) return; + size_t L = snprintf(nm, l, "'%s'", mount_name); + if(L == l){ + nm[l-1] = 0; + nm[l-2] = '\''; + } +} + /** * @brief mount_set_dev - open mount device without checking that mount is alive * @param dev - path to device @@ -164,9 +175,6 @@ bool mount_set_dev(char *dev, int speed, double timeout){ return false; } DBG("Mount device inited and opened"); - // set terminal timeout to `timeout / 10` or 100ms - int usecs = (timeout < 1.) ? (int)(timeout * 1e5) : 100000; - sl_tty_tmout(usecs); sertmout = timeout; DBG("set timeout of answer waiting to %gs", sertmout); pthread_mutex_unlock(&mntdev_mutex); @@ -319,7 +327,7 @@ static bool chkconn(){ } write_cmd("#", false); // clear cmd buffer bool ret = false; - for(int i = 0; i < 5; ++i){ + for(int i = 0; i < 3 && isrunning; ++i){ DBG("Try %d", i+1); ret = write_cmd(CMD_BAUDRATE, true); if(ret) break; @@ -334,19 +342,24 @@ static bool guess_speed(){ #define SPDBUFSZ 7 const int speeds[SPDBUFSZ] = {57600, 38400, 19200, 9600, 4800, 2400, 1200}; int idx = 0; - for(; idx < SPDBUFSZ; ++idx){ + for(; idx < SPDBUFSZ && isrunning; ++idx){ DBG("try speed %d", speeds[idx]); mount_dev->speed = speeds[idx]; sl_tty_t *trydev = sl_tty_open(mount_dev, 1); if(!trydev) continue; - if(chkconn()) break; + if(chkconn()){ + close(mount_dev->comfd); + break; + } close(mount_dev->comfd); } - if(idx == SPDBUFSZ) return false; // device not responding - close(mount_dev->comfd); mount_dev->speed = 115200; - if(!sl_tty_open(mount_dev, 1)) return false; - DBG("OK, opened @ 115200"); + if(!sl_tty_open(mount_dev, 1)){ + WARN("Can't open serial @115200"); + return false; + } + DBG("Opened @ 115200"); + if(idx == SPDBUFSZ) return false; // device not responding #undef SPDBUFSZ return true; } @@ -362,7 +375,9 @@ bool mount_connect(){ } bool ret = true; if(!write_cmd(CMD_STOP, false)) ret = false; // stop tracking after poweron + usleep(100000); if(!write_cmd(CMD_HIGHPREC, false)) ret = false; // set high precision + usleep(100000); char buf[64]; snprintf(buf, 63, CMD_SETMINALT, 10); if(!write_cmd(buf, true)) ret = false; // set minimum altitude to 10 degrees @@ -412,6 +427,9 @@ bool mount_point(double ra, double dec){ } } ret = true; + TagCoords.ra = DEG2RAD(ra); + TagCoords.dec = DEG2RAD(dec); + TagTime = sl_dtime(); retn: pthread_mutex_unlock(&mntdev_mutex); return ret; @@ -460,30 +478,29 @@ void set_emulation_mode(){ isemulated = true; } -mount_status_t mount_getcoords(double *ra, double *dec){ - if(!ra || !dec) return MNT_S_ERROR; +bool mount_getcoords(double *ra, double *dec){ + if(!ra || !dec) return false; if(isemulated){ get_emul_coords(ra, dec); DBG("Emulated coordinates: %gh, %gdeg", RAD2HRS(*ra), RAD2DEG(*dec)); - return emulation_status(); + return true; } - if(!mount_dev) return MNT_S_ERROR; + if(!mount_dev) return false; char resp[64]; - mount_status_t st = MNT_S_ERROR; + bool ret = false; pthread_mutex_lock(&mntdev_mutex); if(!send_cmd_resp(CMD_GETRA, resp, 64)) goto retn; if(!str2coord(resp, ra)) goto retn; if(!send_cmd_resp(CMD_GETDEC, resp, 64)) goto retn; if(!str2coord(resp, dec)) goto retn; - st = MNT_S_STATAMOUNT; + ret = true; retn: pthread_mutex_unlock(&mntdev_mutex); - if(st == MNT_S_STATAMOUNT) st = mount_status(); - return st; + return ret; } -mount_status_t mount_getaz(double *a, double *z){ - if(!a || !z) return MNT_S_ERROR; +bool mount_getaz(double *a, double *z){ + if(!a || !z) return false; if(isemulated){ double ra, dec; get_emul_coords(&ra, &dec); @@ -493,11 +510,11 @@ mount_status_t mount_getaz(double *a, double *z){ get_LST(NULL, &LST); eq2hor(&P, &H, LST); *a = H.az; *z = H.zd; - return emulation_status(); + return true; } - if(!mount_dev) return MNT_S_ERROR; + if(!mount_dev) return false; char resp[64]; - mount_status_t st = MNT_S_ERROR; + bool ret = false; pthread_mutex_lock(&mntdev_mutex); if(!send_cmd_resp(CMD_GETAZIM, resp, 64)) goto retn; if(!str2coord(resp, a)) goto retn; @@ -505,11 +522,10 @@ mount_status_t mount_getaz(double *a, double *z){ double alt; if(!str2coord(resp, &alt)) goto retn; *z = 90. - alt; - st = MNT_S_STATAMOUNT; + ret = true; retn: pthread_mutex_unlock(&mntdev_mutex); - if(st == MNT_S_STATAMOUNT) st = mount_status(); - return st; + return ret; } /** @@ -600,3 +616,59 @@ bool mount_setParkZD(double zd){ void mount_getPark(horizCrds_t *c){ if(c) *c = ParkCoords; } + +/** + * @brief mount_getpierside - get pier-side of telescope + * @param Ps (o) - side + * @param len - length of `Ps` + * @return false if failed + */ +bool mount_getpierside(char *Ps, size_t len){ + pthread_mutex_lock(&mntdev_mutex); + bool ret = send_cmd_resp(CMD_GETPS, Ps, len); + pthread_mutex_unlock(&mntdev_mutex); + return ret; +} + +/** + * @brief mount_corrdata - correct mount time & weather data + * @param w - new data + * @return false if failed to connect + */ +bool mount_corrdata(weather_data_t *w){ + if(!w) return false; + bool ret = true; // returning value + mount_status_t curst = mount_status(); + if(curst == MNT_S_SLEWING || curst == MNT_S_TRACKING) return true; + DBG("Refresh datetime"); + pthread_mutex_lock(&mntdev_mutex); + char buf[128]; + buf[127] = 0; + time_t t = time(NULL); + struct tm *stm = localtime(&t); + struct timeval tv; + gettimeofday(&tv, NULL); + snprintf(buf, 127, CMD_SETTIME, 1900+stm->tm_year, stm->tm_mon+1, stm->tm_mday, + stm->tm_hour, stm->tm_min, stm->tm_sec, tv.tv_usec/10000); + DBG("write: %s", buf); + if(!write_cmd(buf, true)){ + WARNX("Can't write current date/time"); + LOGWARN("Can't set system time"); + ret = false; + }else LOGMSG("Set system time by command %s", buf); + // set refraction model data + snprintf(buf, 127, CMD_SETPRESSURE, w->pressure*1013./760.); + if(!write_cmd(buf, true)){ + LOGWARN("Can't set pressure data of refraction model"); + ret = false; + }else LOGMSG("Correct pressure to %gmmHg", w->pressure); + snprintf(buf, 64, CMD_SETTEMPER, w->exttemp); + if(!write_cmd(buf, true)){ + LOGWARN("Can't set temperature data of refraction model"); + ret = false; + }else LOGMSG("Correct temperature to %g", w->exttemp); + if(!write_cmd(CMD_REFCORR_ON, true)) ret = false; + if(!write_cmd(CMD_DUALTRK, true)) ret = false; + pthread_mutex_unlock(&mntdev_mutex); + return ret; +} diff --git a/Daemons/10micron_stellarium/mount.h b/Daemons/10micron_stellarium/mount.h index bb0e29c..4a42900 100644 --- a/Daemons/10micron_stellarium/mount.h +++ b/Daemons/10micron_stellarium/mount.h @@ -18,8 +18,16 @@ #pragma once +#include + #include "angles.h" +// input coordinates lifetime - 24hrs +#define COORDS_OLD_T (86400) + +// make datetime/pressure/temperature corrections each CORRECTIONS_TIMEDIFF seconds +#define CORRECTIONS_TIMEDIFF (3600) + // mount statuses typedef enum{ MNT_S_TRACKING = 0, @@ -55,21 +63,28 @@ double mount_getInpHor(horizCrds_t *c); bool mount_set_name(const char *name); +void mount_get_name(char *nm, size_t l); bool mount_set_dev(char *dev, int speed, double timeout); const char* mount_status_str(mount_status_t st); mount_status_t mount_status(); bool mount_connect(); void mount_disconnect(); -mount_status_t mount_getcoords(double *ra, double *dec); -mount_status_t mount_getaz(double *a, double *z); +bool mount_getcoords(double *ra, double *dec); +bool mount_getaz(double *a, double *z); + +bool mount_corrdata(weather_data_t *w); + +bool mount_getpierside(char *Ps, size_t len); + bool mount_point(double ra, double dec); bool mount_pointAZ(double A, double Z); + bool mount_stop(); bool mount_tracking_stop(); bool mount_tracking_start(); -bool mount_park(); +bool mount_park(); bool mount_setParkAz(double az); bool mount_setParkZD(double zd); void mount_getPark(horizCrds_t *c); diff --git a/Daemons/10micron_stellarium/mountdaemon_10micron.files b/Daemons/10micron_stellarium/mountdaemon_10micron.files index a30736b..16f6aa1 100644 --- a/Daemons/10micron_stellarium/mountdaemon_10micron.files +++ b/Daemons/10micron_stellarium/mountdaemon_10micron.files @@ -8,6 +8,8 @@ dome.c dome.h emulation.c emulation.h +fitshdr.c +fitshdr.h main.c server.c server.h diff --git a/Daemons/10micron_stellarium/server.c b/Daemons/10micron_stellarium/server.c index 60d4d4d..8bd0cd7 100644 --- a/Daemons/10micron_stellarium/server.c +++ b/Daemons/10micron_stellarium/server.c @@ -27,6 +27,7 @@ #include #include +#include "fitshdr.h" #include "mount.h" #include "server.h" #include "stellarium.h" @@ -64,7 +65,12 @@ static int stellarium_sockfd = -1; // sleep time (us) static unsigned int sleept = DEFAULT_SLEEP_T; // running flag -static volatile bool isrunning = false; +volatile bool isrunning = false; +// lost weather flag +static bool weatherlost = false; + +// data for FITS-header (also to send user) +static fitsheader_t HDR = {0}; unsigned int server_getsleept(){ return sleept; } bool server_setsleept(unsigned int t){ @@ -86,8 +92,7 @@ static sl_sock_hresult_e dtimeh(sl_sock_t *c, _U_ sl_sock_hitem_t *item, _U_ con // statust - text format status static sl_sock_hresult_e status(sl_sock_t *c, sl_sock_hitem_t *item, _U_ const char *req){ char buf[BUFSIZ]; - mount_status_t st = mount_status(); - snprintf(buf, BUFSIZ-1, "%s=%s\n", item->key, mount_status_str(st)); + snprintf(buf, BUFSIZ-1, "%s=%s\n", item->key, mount_status_str(HDR.status)); LOGDBG("Client %d asks status: %s", c->fd, buf); sl_sock_sendstrmessage(c, buf); return RESULT_SILENCE; @@ -123,7 +128,7 @@ static sl_sock_hresult_e cmd_tagra(sl_sock_t *c, sl_sock_hitem_t *item, const ch return RESULT_SILENCE; } -static sl_sock_hresult_e cmd_tagdec(sl_sock_t *c, sl_sock_hitem_t *item, const char *req) { +static sl_sock_hresult_e cmd_tagdec(sl_sock_t *c, sl_sock_hitem_t *item, const char *req){ double val; int res = parse_key_value(req, &val); if(res < 0) return RESULT_BADVAL; @@ -141,7 +146,7 @@ static sl_sock_hresult_e cmd_tagdec(sl_sock_t *c, sl_sock_hitem_t *item, const c return RESULT_SILENCE; } -static sl_sock_hresult_e cmd_tagha(sl_sock_t *c, sl_sock_hitem_t *item, const char *req) { +static sl_sock_hresult_e cmd_tagha(sl_sock_t *c, sl_sock_hitem_t *item, const char *req){ double val; int res = parse_key_value(req, &val); if(res < 0) return RESULT_BADVAL; @@ -159,7 +164,7 @@ static sl_sock_hresult_e cmd_tagha(sl_sock_t *c, sl_sock_hitem_t *item, const ch return RESULT_SILENCE; } -static sl_sock_hresult_e cmd_tagaz(sl_sock_t *c, sl_sock_hitem_t *item, const char *req) { +static sl_sock_hresult_e cmd_tagaz(sl_sock_t *c, sl_sock_hitem_t *item, const char *req){ double val; int res = parse_key_value(req, &val); if(res < 0) return RESULT_BADVAL; @@ -177,7 +182,7 @@ static sl_sock_hresult_e cmd_tagaz(sl_sock_t *c, sl_sock_hitem_t *item, const ch return RESULT_SILENCE; } -static sl_sock_hresult_e cmd_tagzd(sl_sock_t *c, sl_sock_hitem_t *item, const char *req) { +static sl_sock_hresult_e cmd_tagzd(sl_sock_t *c, sl_sock_hitem_t *item, const char *req){ double val; int res = parse_key_value(req, &val); if(res < 0) return RESULT_BADVAL; @@ -195,43 +200,40 @@ static sl_sock_hresult_e cmd_tagzd(sl_sock_t *c, sl_sock_hitem_t *item, const ch return RESULT_SILENCE; } -static sl_sock_hresult_e cmd_telra(sl_sock_t *c, sl_sock_hitem_t *item, _U_ const char *req) { - double ra, dec; - if(mount_getcoords(&ra, &dec) == MNT_S_ERROR) return RESULT_FAIL; +static sl_sock_hresult_e cmd_telra(sl_sock_t *c, sl_sock_hitem_t *item, _U_ const char *req){ + if(weatherlost || HDR.status == MNT_S_ERROR) return RESULT_FAIL; char buf[64]; - snprintf(buf, 63, "%s=%.6f\n", item->key, ra); + snprintf(buf, 63, "%s=%.6f\n", item->key, RAD2HRS(HDR.polar.ra)); sl_sock_sendstrmessage(c, buf); return RESULT_SILENCE; } -static sl_sock_hresult_e cmd_teldec(sl_sock_t *c, sl_sock_hitem_t *item, _U_ const char *req) { - double ra, dec; - if(mount_getcoords(&ra, &dec) == MNT_S_ERROR) return RESULT_FAIL; +static sl_sock_hresult_e cmd_teldec(sl_sock_t *c, sl_sock_hitem_t *item, _U_ const char *req){ + if(weatherlost || HDR.status == MNT_S_ERROR) return RESULT_FAIL; char buf[64]; - snprintf(buf, 63, "%s=%.6f\n", item->key, dec); + snprintf(buf, 63, "%s=%.6f\n", item->key, RAD2DEG(HDR.polar.dec)); sl_sock_sendstrmessage(c, buf); return RESULT_SILENCE; } -static sl_sock_hresult_e cmd_telaz(sl_sock_t *c, sl_sock_hitem_t *item, _U_ const char *req) { - double a, z; - if(mount_getaz(&a, &z) == MNT_S_ERROR) return RESULT_FAIL; +static sl_sock_hresult_e cmd_telaz(sl_sock_t *c, sl_sock_hitem_t *item, _U_ const char *req){ + if(weatherlost || HDR.status == MNT_S_ERROR) return RESULT_FAIL; char buf[64]; - snprintf(buf, 63, "%s=%.6f\n", item->key, a); + snprintf(buf, 63, "%s=%.6f\n", item->key, RAD2DEG(HDR.altaz.az)); sl_sock_sendstrmessage(c, buf); return RESULT_SILENCE; } -static sl_sock_hresult_e cmd_telzd(sl_sock_t *c, sl_sock_hitem_t *item, _U_ const char *req) { - double a, z; - if(mount_getaz(&a, &z) == MNT_S_ERROR) return RESULT_FAIL; +static sl_sock_hresult_e cmd_telzd(sl_sock_t *c, sl_sock_hitem_t *item, _U_ const char *req){ + if(weatherlost || HDR.status == MNT_S_ERROR) return RESULT_FAIL; char buf[64]; - snprintf(buf, 63, "%s=%.6f\n", item->key, z); + snprintf(buf, 63, "%s=%.6f\n", item->key, RAD2DEG(HDR.altaz.zd)); sl_sock_sendstrmessage(c, buf); return RESULT_SILENCE; } -static sl_sock_hresult_e cmd_gotord(_U_ sl_sock_t *c, _U_ sl_sock_hitem_t *item, _U_ const char *req) { +static sl_sock_hresult_e cmd_gotord(_U_ sl_sock_t *c, _U_ sl_sock_hitem_t *item, _U_ const char *req){ + if(weatherlost || HDR.status == MNT_S_ERROR) return RESULT_FAIL; polarCrds_t p; mount_getInpCoords(&p); double ra_h = RAD2HRS(p.ra); @@ -240,7 +242,8 @@ static sl_sock_hresult_e cmd_gotord(_U_ sl_sock_t *c, _U_ sl_sock_hitem_t *item, return RESULT_OK; } -static sl_sock_hresult_e cmd_gotorh(_U_ sl_sock_t *c, _U_ sl_sock_hitem_t *item, _U_ const char *req) { +static sl_sock_hresult_e cmd_gotorh(_U_ sl_sock_t *c, _U_ sl_sock_hitem_t *item, _U_ const char *req){ + if(weatherlost || HDR.status == MNT_S_ERROR) return RESULT_FAIL; polarCrds_t p; mount_getInpCoords(&p); sMJD_t mjd; @@ -254,25 +257,27 @@ static sl_sock_hresult_e cmd_gotorh(_U_ sl_sock_t *c, _U_ sl_sock_hitem_t *item, return RESULT_OK; } -static sl_sock_hresult_e cmd_gotoaz(_U_ sl_sock_t *c, _U_ sl_sock_hitem_t *item, _U_ const char *req) { +static sl_sock_hresult_e cmd_gotoaz(_U_ sl_sock_t *c, _U_ sl_sock_hitem_t *item, _U_ const char *req){ + if(weatherlost || HDR.status == MNT_S_ERROR) return RESULT_FAIL; horizCrds_t h; mount_getInpHor(&h); if(mount_pointAZ(RAD2DEG(h.az), RAD2DEG(h.zd))) return RESULT_OK; return RESULT_FAIL; } -static sl_sock_hresult_e cmd_stop(_U_ sl_sock_t *c, _U_ sl_sock_hitem_t *item, _U_ const char *req) { +static sl_sock_hresult_e cmd_stop(_U_ sl_sock_t *c, _U_ sl_sock_hitem_t *item, _U_ const char *req){ if(mount_stop()) return RESULT_OK; return RESULT_FAIL; } -static sl_sock_hresult_e cmd_stoptrk(_U_ sl_sock_t *c, _U_ sl_sock_hitem_t *item, _U_ const char *req) { +static sl_sock_hresult_e cmd_stoptrk(_U_ sl_sock_t *c, _U_ sl_sock_hitem_t *item, _U_ const char *req){ if(mount_tracking_stop()) return RESULT_OK; return RESULT_FAIL; } // run tracking from current position -static sl_sock_hresult_e cmd_track(_U_ sl_sock_t *c, _U_ sl_sock_hitem_t *item, _U_ const char *req) { +static sl_sock_hresult_e cmd_track(_U_ sl_sock_t *c, _U_ sl_sock_hitem_t *item, _U_ const char *req){ + if(weatherlost || HDR.status == MNT_S_ERROR) return RESULT_FAIL; if(mount_tracking_start()) return RESULT_OK; return RESULT_FAIL; } @@ -423,12 +428,15 @@ void server_run(){ LOGERR("Can't start stellarium server"); return; } + isrunning = true; if(!mount_connect()){ LOGWARN("Can't connect to mount, will try to reconnect later"); } - isrunning = true; DBG("While"); - double tcheck = 0.; + double tcheck = 0., treconnect = sl_dtime(); + time_t lastweathertime = 0; + getPlaceData(&HDR.place); + mount_get_name(HDR.mountname, MAX_HDR_STRLEN); bool notlogged = true; while(isrunning && cmd_socket && cmd_socket->connected){ usleep(sleept); @@ -440,22 +448,59 @@ void server_run(){ double tnow = sl_dtime(); if(tnow - tcheck >= MOUNT_CHECK_T){ tcheck = tnow; - mount_status_t curst = mount_status(); - if(curst == MNT_S_OFF){ // mount is off -> try to reconnect + if(get_weather_data(&HDR.weather) == 0){ + lastweathertime = HDR.weather.last_update; + }else{ + WARNX("Can't get weather data"); + } + // collect data for other fields + HDR.status = mount_status(); + get_MJDt(NULL, &HDR.MJD); + get_LST(&HDR.MJD, &HDR.sidtime); + if(HDR.status != MNT_S_OFF){ + double deg1, deg2; + if(mount_getcoords(°1, °2)){ + HDR.polar.ra = DEG2RAD(deg1); + HDR.polar.dec = DEG2RAD(deg2); + } + if(mount_getaz(°1, °2)){ + HDR.altaz.az = DEG2RAD(deg1); + HDR.altaz.zd = DEG2RAD(deg2); + } + } + if(tnow - lastweathertime > MOUNT_WEATHER_ALRM){ + weatherlost = true; + if(HDR.status == MNT_S_TRACKING || HDR.status == MNT_S_SLEWING){ + LOGERR("Lost meteo connection -> park"); + if(mount_park()) lastweathertime = tnow; + } + } else weatherlost = false; + getDUT(&HDR.dut); + if(HDR.status == MNT_S_OFF || !mount_getpierside(HDR.pierside, MAX_HDR_STRLEN)) *HDR.pierside = 0; + // and write FITS-header + wrhdr(&HDR); + // check need of reconnection + if(HDR.status == MNT_S_OFF){ // mount is off -> try to reconnect if(notlogged){ WARNX("Mount is OFF"); LOGWARN("Mount is OFF"); notlogged = false; } - DBG("Try to [re]connect"); - if(mount_connect()){ - notlogged = true; - WARNX("Mount is ON"); + if(tnow - treconnect >= MOUNT_RECONNECT_T){ + treconnect = tnow; + DBG("Try to [re]connect"); + if(mount_connect()){ + notlogged = true; + WARNX("Mount is ON"); + } } } - DBG("Current status: %s", mount_status_str(curst)); + DBG("Current status: %s", mount_status_str(HDR.status)); } } + DBG("Stop mount"); + double t0 = sl_dtime(); + while(sl_dtime() - t0 < 3. && !mount_stop()); DBG("Stop command socket"); sl_sock_delete(&cmd_socket); WARNX("Server is dead"); diff --git a/Daemons/10micron_stellarium/server.h b/Daemons/10micron_stellarium/server.h index 5cf4e3c..32c734d 100644 --- a/Daemons/10micron_stellarium/server.h +++ b/Daemons/10micron_stellarium/server.h @@ -27,8 +27,10 @@ // maximal amount of connected clients #define DEFAULT_MAXCLIENTS 5 -// status checking interval, seconds -#define MOUNT_CHECK_T 10. +// try to reconnect each RECONN seconds +#define MOUNT_RECONNECT_T 60. +// max weather lost time +#define MOUNT_WEATHER_ALRM 900. typedef struct{ int cmd_isunix; // UNIX-socket instead of INET for `cmdnode` @@ -37,6 +39,8 @@ typedef struct{ int maxclients; // maximal amount of clients connected } server_sock_t; +extern volatile bool isrunning; + bool server_check(server_sock_t *sockt); void server_run(); void server_stop(); diff --git a/Daemons/10micron_stellarium/stellarium.c b/Daemons/10micron_stellarium/stellarium.c index 0db4b57..1693d3a 100644 --- a/Daemons/10micron_stellarium/stellarium.c +++ b/Daemons/10micron_stellarium/stellarium.c @@ -162,12 +162,13 @@ static void *handle_socket(void *sockd){ dout.type = 0; while(isrunning){ // get coordinates - double RA = 0., Decl = 0.; - if((dout.status = mount_getcoords(&RA, &Decl)) == MNT_S_ERROR){ + double Rhrs = 0., Ddeg = 0.; + if((dout.status = mount_getcoords(&Rhrs, &Ddeg)) == false){ WARNX("Error: can't get coordinates"); sleep(1); continue; } + double RA = HRS2RAD(Rhrs), Decl = DEG2RAD(Ddeg); //DBG("got : %g/%g", RA, Decl); dout.ra = htole32(RAD2RA(RA)); dout.dec = (int32_t)htole32(RAD2DEC(Decl));