diff --git a/Daemons/10micron_stellarium/10micron_commands.h b/Daemons/10micron_stellarium/10micron_commands.h index 04c35b6..a644d25 100644 --- a/Daemons/10micron_stellarium/10micron_commands.h +++ b/Daemons/10micron_stellarium/10micron_commands.h @@ -19,7 +19,7 @@ #pragma once // commands -#define CMD_SHUTDOWN ":shutdown#" // TODO! +#define CMD_SHUTDOWN ":shutdown#" #define CMD_TRKSTART ":AP#" #define CMD_TRKSTOP ":AL#" #define CMD_GOTOAZ ":MA#" diff --git a/Daemons/10micron_stellarium/CMakeLists.txt b/Daemons/10micron_stellarium/CMakeLists.txt index 45b7019..598fc37 100644 --- a/Daemons/10micron_stellarium/CMakeLists.txt +++ b/Daemons/10micron_stellarium/CMakeLists.txt @@ -4,7 +4,7 @@ set(PROJ mountdaemon_10micron) project(${PROJ}) set(MAJOR_VERSION "0") -set(MID_VERSION "1") +set(MID_VERSION "2") set(MINOR_VERSION "1") set(VERSION "${MAJOR_VERSION}.${MID_VERSION}.${MINOR_VERSION}") @@ -36,7 +36,7 @@ aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR} SOURCES) ###### pkgconfig ###### find_package(PkgConfig REQUIRED) -pkg_check_modules(MODULES REQUIRED usefull_macros>=0.3.2 erfa>=2.0.0) +pkg_check_modules(MODULES REQUIRED usefull_macros>=0.3.5 erfa>=2.0.0) # change wrong behaviour with install prefix if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT AND CMAKE_INSTALL_PREFIX MATCHES "/usr/local") @@ -57,7 +57,7 @@ target_link_libraries(${PROJ} ${MODULES_LIBRARIES} -lweather -lm) # -D add_definitions(-D_XOPEN_SOURCE=1234 -D_DEFAULT_SOURCE -D_GNU_SOURCE -DPACKAGE_VERSION=\"${VERSION}\" -DMINOR_VERSION=\"${MINOR_VERSION}\" - -DMID_VERSION=\"${MID_VERSION}\" -DMAJOR_VERSION=\"${MAJOR_VESION}\" + -DMID_VERSION=\"${MID_VERSION}\" -DMAJOR_VERSION=\"${MAJOR_VERSION}\" ) # Installation of the program diff --git a/Daemons/10micron_stellarium/Readme.md b/Daemons/10micron_stellarium/Readme.md new file mode 100644 index 0000000..011f166 --- /dev/null +++ b/Daemons/10micron_stellarium/Readme.md @@ -0,0 +1,496 @@ +# mountdaemon_10micron + +A daemon for controlling 10Micron equatorial mounts (e.g. GM4000HPS) over a serial port. It exposes +a text command interface for local clients, a binary Stellarium-protocol interface for planetarium +software, and periodically publishes the current telescope state into a FITS-header file for +downstream acquisition software. + +The daemon is written in C (C23), uses [ERFA](https://github.com/liberfa/erfa) for all astronomical +computations, [usefull_macros](https://github.com/eddyem/snippets_library) for utility routines, +and optionally [weather +proxy](https://github.com/eddyem/small_tel/tree/master/Daemons/weather_proxy) for getting meteo data. + +--- + +## Table of contents + +1. [Overview](#overview) +2. [Building and installation](#building-and-installation) +3. [Running the daemon](#running-the-daemon) +4. [Command-line options and configuration file](#command-line-options-and-configuration-file) +5. [Astronomical parameters](#astronomical-parameters) +6. [Coordinate systems](#coordinate-systems) +7. [Network interfaces](#network-interfaces) + - [Command socket](#command-socket) + - [Stellarium socket](#stellarium-socket) +8. [Command reference](#command-reference) +9. [FITS-header file](#fits-header-file) +10. [Emulation mode](#emulation-mode) +11. [Signals and process management](#signals-and-process-management) +12. [Architecture](#architecture) +13. [Known limitations](#known-limitations) + +--- + +## Overview + +`mountdaemon_10micron` connects to a 10Micron mount over a serial line (default `/dev/ttyUSB0` at +115200 baud) and performs three independent jobs: + +1. **Serves a local text command interface** — clients send newline-terminated commands (`tagra`, +`gotord`, `park`, …) over a TCP or UNIX socket and receive plain `key=value` replies. This is the +primary interface for an observatory control system. +2. **Serves a Stellarium-compatible binary protocol** — a planetarium program (Stellarium, Cartes +du Ciel, etc.) can connect to a second TCP socket, read the current telescope position, and issue +goto commands. This allows visual pointing with the planetarium. +3. **Writes a FITS-header file** — at least every `MOUNT_CHECK_T` seconds the daemon writes a fresh +FITS-header block with the current telescope position, sidereal time, MJD and place data. +The file is atomically replaced so a downstream acquisition program can read it at any moment. + +Additionally, the daemon: +- corrects the mount's internal clock and refraction model once per hour from local weather data; +- parks the mount automatically if the weather feed is lost while tracking; +- reconnects to the mount if the serial link drops; +- supports an **emulation mode**, in which the mount is simulated locally — useful for testing +clients without hardware. + +The daemon is built around the ERFA library, so coordinates are rigorously converted between ICRS +(J2000), CIRS, observed, and horizontal systems, and refraction is accounted for using the current +pressure and temperature. + +--- + +## Building and installation + +### Dependencies + +- a C23-capable compiler (GCC 13+ or Clang 16+); +- CMake ≥ 4.0; +- **pkg-config**; +- **liberfa** ≥ 2.0.0; +- **usefull_macros** ≥ 0.3.5; +- **libweather** (linked with `-lweather`); +- `libm`. + +### Build + +```sh +git clone --depth=1 https://github.com/eddyem/small_tel +cd Daemons/10micron_stellarium +mkdir build && cd build +cmake .. +make +``` + +Options recognised by CMake: + +| Option | Default | Meaning | +|----------|---------|---------------------------------------------------| +| `DEBUG` | `OFF` | Build in debug mode (`-Og -g3 -Werror`, no fork). | + +### Install + +```sh +su -c "make install" +``` + +The binary is installed to `/bin/mountdaemon_10micron`. The default prefix is `/usr/local`. + +--- + +## Running the daemon + +```sh +mountdaemon_10micron -d /dev/ttyS0 -S 9600 \ + --latitude 43.65:39:00 --longitude 41.44:06:00 --altitude 2070 \ + --dut1 0.1 -p :10000 -P localhost:10001 -v +``` + +The daemon forks a supervisor process that re-spawns the working process if it crashes. It writes a +PID-file (`/tmp/mountdaemon_10micron.pid` by default), which is removed on clean exit. + +Stopping the daemon: send `SIGTERM` (or `SIGINT`, `SIGQUIT`) to the supervisor PID from +the PID-file. The supervisor kills its child and exits. + +--- + +## Command-line options and configuration file + +All options may be given either on the command line or in a configuration file passed with `-c +`. Command-line values always take precedence over the configuration file. The config file +uses `key = value` lines — key names are the long option names below without the leading `--`. + +| Long option | Short | Argument | Default | Description | +|--------------------|-------|----------|----------------------------------|------------------------------------------------------------------------| +| `--device` | `-d` | string | `/dev/ttyUSB0` | Serial device connected to the mount. | +| `--emulation` | `-e` | — | `0` | Run in emulation mode (no hardware needed). | +| `--logfile` | `-l` | string | no | Redirect log output to this file. | +| `--hdrfile` | `-o` | string | `/tmp/10micron.fitsheader` | File to write the FITS-header into. | +| `--pidfile` | | string | `/tmp/mountdaemon_10micron.pid` | PID-file path. | +| `--port` | `-p` | string | `:10000` | Port (or `host:port`) for the Stellarium server. | +| `--cmdport` | `-P` | string | `localhost:10001` | Port (or UNIX-socket path) for the command console. | +| `--sleept` | `-t` | int | `100` | Main-loop sleep, µs. | +| `--isunix` | `-U` | — | `0` | Use a UNIX-domain socket for `cmdport`. | +| `--sertmout` | `-T` | double | `1.0` | Serial answer timeout, seconds. | +| `--serspeed` | `-S` | int | `115200` | Serial speed. | +| `--maxclients` | | int | `5` | Max clients per socket. | +| `--mountname` | | string | `"10Micron GM4000HPS"` | Mount name for the FITS header. | +| `--verbose` | `-v` | — | `0` | Increase verbosity (each `-v` adds a level). | +| `--parka` | `-A` | string | built-in | Parking azimuth, degrees or `DD:MM:SS`. | +| `--parkz` | `-Z` | string | built-in | Parking zenith distance. | +| `--dut1` | | double | `0` | UT1 − UTC, seconds. | +| `--polarx` | | double | `0` | IERS polar-motion X, arcsec. | +| `--polary` | | double | `0` | IERS polar-motion Y, arcsec. | +| `--latitude` | | string | built-in | Site latitude (degrees, `DD:MM:SS`, or decimal). | +| `--longitude` | | string | built-in | Site longitude (degrees, positive east). | +| `--altitude` | | string | built-in | Site altitude, metres. | +| `--help` | `-h` | — | — | Show help. | +| `--config` | `-c` | string | — | Read options from this file. | + +### Example configuration file + +```ini +device = /dev/serial/by-id/usb-FTDI_USB-RS232-if00-port0 +serspeed = 115200 +sertmout = 1.5 +port = :10000 +cmdport = /tmp/mountdaemon.sock +isunix = 1 +sleept = 200 +maxclients = 10 +latitude = 43.65:39:00 +longitude = 41.44:06:00 +altitude = 2070 +dut1 = 0.1 +polarx = 0.05 +polary = 0.12 +mountname = "10Micron GM4000HPS" +logfile = /var/log/mountdaemon.log +``` + +--- + +## Astronomical parameters + +The following parameters influence pointing accuracy and FITS-header content. They may be set at +start-up via CLI/config, and (except place data) also at runtime via network commands. + +| Parameter | Runtime command | Units | Typical range | Notes | +|-----------------|-----------------|----------|--------------------|-----------------------------------------------------------| +| Site latitude | — | degrees | −90 … +90 | Set at start-up only. | +| Site longitude | — | degrees | −180 … +180 (E+) | Set at start-up only. | +| Site altitude | — | metres | −500 … +9000 | Set at start-up only. | +| DUT1 | `dut1` | seconds | −1 … +1 | UT1 − UTC. Affects LST by up to ±15″, affecting pointing. | +| Polar motion X | `polarx` | arcsec | ±1000 | IERS X coordinate of celestial pole. | +| Polar motion Y | `polary` | arcsec | ±1000 | IERS Y coordinate of celestial pole. | + +If not set, the daemon uses internal defaults (site: SAO RAS, altitude 2070 m). DUT1 and polar +motion default to 0. + +--- + +## Coordinate systems + +The daemon distinguishes the following frames: + +| Frame | Notation | Units | Notes | +|-----------------------------|------------|----------|----------------------------------------------------------| +| Catalog (ICRS / J2000) | `J2000` | RA: hours, Dec: degrees | Coordinates entered by the user via `tagra/tagdec`. | +| Epoch of date (mean equinox)| `Jnow` | RA: hours, Dec: degrees | Used internally for pointing. | +| CIRS (geocentric apparent) | — | radians | Result of `eraAtci13`. | +| Observed (with refraction) | — | radians | Result of `eraAtco13`. | +| Horizontal | `Az/Zd` | degrees | Azimuth, clockwise from North. ZD = 90° − altitude. | + +- **Target epoch** is stored as an MJD. By default it is J2000 (`ERFA_DJM00` = 51544.5). It can be +set to any epoch via the `tagmjd` command. +- The command `gotord` converts the input coordinates from the target epoch to *Jnow* using +`JXtoJnow()` before sending to the mount. +- The command `gotorh` interprets the input as an **hour angle** and converts to RA using the +current LST. +- The current telescope position returned by the mount is in *Jnow*, and the FITS-header records it +under `RA`, `HA`, `DEC`, `AZ`, `ZD`. + +--- + +## Network interfaces + +### Command socket + +Default address: `localhost:10001` (TCP) or a UNIX socket if `--isunix` is set. + +Protocol: **line-oriented text**. A client sends one command per line, optionally with a value: + +``` +tagra = 12.345 +tagdec = +45:30:00 +gotord +``` + +The daemon replies with `key=value` (newline-terminated) for getters, or nothing (only implicit +success/failure) for setters/actions. Commands may be issued one at a time or in a stream. All +sockets are multiplexed by a thread-per-client model inside `usefull_macros`'s `sl_sock` +infrastructure. + +Up to `--maxclients` simultaneous clients are accepted. If more try to connect, they receive `Try +later: too much clients connected` and the connection is closed. + +Values may be given either as a decimal number (`12.345`, `-26.5`) or as `DD:MM:SS` / `HH:MM:SS` +(sexagesimal). Angles are interpreted as degrees unless the field name (RA, HA, LST) explicitly +implies hours. + +### Stellarium socket + +Default address: `:10000` (all interfaces, port 10000). Protocol: **binary little-endian**, +according to Stellarium's "Telescope Control" plugin specification. + +Incoming (client → server) message, 20 bytes: + +| Field | Type | Meaning | +|--------|-----------|--------------------------------------------------------| +| `len` | `uint16` | Total message length (`20`). | +| `type` | `uint16` | `0`. | +| `time` | `uint64` | µs since epoch (unused). | +| `RA` | `uint32` | Target RA. `0` = 0h, `0x80000000` = 12h, `0x100000000` = 24h. | +| `DEC` | `int32` | Target Dec. `-0x40000000` = −90°, `0x40000000` = +90°. | + +Outgoing (server → client) message, 24 bytes: same fields plus a trailing `int32 status` (0 = ok, +other = error). + +The daemon treats incoming messages as `tagra`/`tagdec` in the current epoch (which for Stellarium +is normally J2000). It does **not** automatically start a slew — the client must send `gotord` on +the command socket. + +--- + +## Command reference + +All commands are case-sensitive. Getter command names are typically used without an argument; +setter commands require an argument of the indicated type. + +### Target coordinates (input) + +| Command | Type | Units | Description | +|-----------|--------|--------------------|------------------------------------------------------------------| +| `tagra` | get/set| hours (0…24) | Target right ascension. | +| `tagdec` | get/set| degrees (−90…+90) | Target declination. | +| `tagha` | get/set| hours | Target hour angle (alternative to RA). | +| `tagaz` | get/set| degrees (0…360) | Target azimuth (clock from North). | +| `tagzd` | get/set| degrees (0…90) | Target zenith distance. | +| `tagmjd` | get/set| MJD or `J` | Epoch of the input coordinates. `J2050` = 2050.0, `51544.5` = J2000. | + +### Pointing + +| Command | Description | +|-----------|---------------------------------------------------------------------------------------------| +| `gotord` | Slew to the input RA/Dec (converted from target epoch to Jnow) and start tracking. | +| `gotorh` | Slew using the stored hour angle: RA = LST − HA, then track. | +| `gotoaz` | Slew to the input Az/ZD and stop. | +| `stop` | Stop any motion (emergency stop). | +| `stoptrk` | Stop tracking but leave the mount in place. | +| `track` | Start tracking from the current position. | +| `park` | Slew to the parking position (see `parkaz`/`parkzd`). | +| `shutdown`| Power off the mount. Requires a numeric key to confirm (see below). | + +### State queries + +| Command | Units | Description | +|-----------|----------|-----------------------------------------------------------------| +| `status` | — | Human-readable mount status. | +| `telra` | hours | Current telescope RA (Jnow). | +| `telha` | hours | Current telescope hour angle. | +| `teldec` | degrees | Current telescope declination (Jnow). | +| `telaz` | degrees | Current telescope azimuth. | +| `telzd` | degrees | Current telescope zenith distance. | +| `lst` | hours | Local sidereal time. | +| `unixt` | seconds | Server UNIX time. | + +`tel*` commands return `RESULT_FAIL` if the weather feed is lost or the mount is in `Error` state — +this prevents a client from acting on stale coordinates. + +### Place and almanac data + +| Command | Type | Description | +|-----------|--------|-----------------------------------------------------------------| +| `place` | get | Site latitude, longitude, altitude. | +| `dut1` | get/set| UT1 − UTC, seconds. | +| `polarx` | get/set| IERS X pole coordinate, arcsec. | +| `polary` | get/set| IERS Y pole coordinate, arcsec. | + +Place data can only be set at start-up (via CLI/config). + +### Parking + +| Command | Type | Description | +|-----------|--------|--------------------------------------| +| `parkaz` | get/set| Parking azimuth, degrees. | +| `parkzd` | get/set| Parking zenith distance, degrees. | + +### Shutdown + +The `shutdown` command implements a simple confirmation handshake: + +1. A client sends `shutdown` **without** an argument. The daemon generates a random key, stores it +with a timestamp, and returns `shutdown=`. +2. Within 5 minutes, a client must send `shutdown = `. If the key matches, the mount is powered +off. + +This protects against accidental shutdown commands. + +--- + +## FITS-header file + +Every `MOUNT_CHECK_T` (0.5 s) the daemon collects the current state and writes a complete +FITS-header block into the file given by `--hdrfile`. The write is atomic: a temporary file is +created with `mkstemp()` and `rename()`d over the destination, so a reader never sees a partial +block. + +The following keywords are written (only if meaningful): + +| Keyword | Comment | +|------------------|----------------------------------------------------------------------| +| `TIMESYS` | `'UTC'`. | +| `ORIGIN` | `'SAO RAS'`. | +| `MOUNTNAM` | Mount name (from `--mountname`). | +| `POLARX` | X pole coordinate, arcsec (if non-zero). | +| `POLARY` | Y pole coordinate, arcsec (if non-zero). | +| `DUT1` | UT1 − UTC, seconds (if non-zero). | +| `INPRA`,`INPDEC` | Input target RA/Dec, if the last input was celestial. | +| `INPAZ`,`INPZD` | Input target Az/ZD, if the last input was horizontal. | +| `TAGRA`,`TAGDEC` | Last **slewed-to** target, always celestial. | +| `RA`,`HA`,`DEC` | Current telescope position in *Jnow*. | +| `AZ`,`ZD` | Current telescope position in the horizontal frame. | +| `TELSTAT` | Human-readable mount status. | +| `INPEQUIN` | Epoch (year) of the input coordinates. | +| `EQUINOX` | Epoch (year) of the current telescope coordinates. | +| `MJD` | MJD of the header. | +| `PIERSIDE` | Pier side of the mount (`'E'`/`'W'`). | +| `ELEVAT` | Site altitude, m. | +| `LONGITUD` | Site longitude, degrees east. | +| `LATITUDE` | Site latitude, degrees north. | +| `LSTEND` | Local sidereal time, hours. | + +Weather keywords (`HUMIDITY`, `PRESSURE`, `EXTTEMP`, `RAIN`, `SKYQUAL`, `WINDSPD`, `WINDMAX`, +`WEATTIME`) are intentionally **not** written by this daemon — the weather data is expected to be +merged by the weather daemon's own header writer. + +--- + +## Emulation mode + +Started with `-e`/`--emulation`, the daemon does not open the serial port and instead simulates a +mount with the following behaviour: + +- Start-up position: Az = 180°, ZD = 80°. +- Slew rates: 5°/s in RA, 8°/s in Dec. +- Slew completes when the angular distance is below 1″ or when the modelled time has elapsed. +- `ZD_LIMIT = 80°` — slews beyond this limit are refused or stopped. +- **Meridian flip** is simulated: if reaching the target from the "flipped" side is faster, the +mount will go through a flipped state (Dec > 90°, RA += 12h). +- Statuses `Stopped`, `Slewing`, `Tracking` are fully modelled; the others are not. + +This is useful for developing clients (control-system, planetarium) without access to real +hardware. All command-socket and Stellarium-socket behaviour is identical to the real-mount mode. + +--- + +## Signals and process management + +The daemon runs as a supervisor + worker pair: + +- The **supervisor** (`main()` before `fork()`) watches the child, re-spawns it after a crash, and +holds the PID-file. +- The **worker** is the actual server: it opens the serial port, listens on sockets, and processes +commands. + +Signals handled: + +| Signal | Action | +|-------------|-----------------------------------------------------------------------| +| `SIGTERM` | Remove PID-file, kill worker, exit. | +| `SIGINT` | Same as `SIGTERM`. | +| `SIGQUIT` | Same as `SIGTERM`. | +| `SIGHUP` | Ignored (may be used later for config-file re-reading) | +| `SIGTSTP` | Ignored (so the daemon can survive `Ctrl-Z` from a shell session). | + +If the worker dies with a non-zero exit status, the supervisor logs the event. If the worker dies +within 10 minutes of the previous restart, this is treated as a crash-loop and logged with a +warning. + +--- + +## Architecture + +``` + ┌─────────────────────────┐ + │ supervisor (fork) │ + └────────────┬────────────┘ + │ fork + waitpid + ┌────────────▼────────────┐ + cmd clients ──►│ worker process │◄── Stellarium clients + │ │ + │ ┌───────────────────┐ │ + │ │ cmd_socket (TCP │ │ + │ │ or UNIX, thread │ │ + │ │ per client) │ │ + │ └───────────────────┘ │ + │ ┌───────────────────┐ │ + │ │ stellarium_sock │ │ + │ │ (TCP, thread │ │ + │ │ per client) │ │ + │ └───────────────────┘ │ + │ ┌───────────────────┐ │ + │ │ main loop │ │ + │ │ (0.5 s tick) │ │ + │ └────────┬──────────┘ │ + │ │ │ + └───────────┼─────────────┘ + │ serial (mutex-protected) + ▼ + ┌──────────────┐ + │ 10Micron │ + │ mount │ + └──────────────┘ +``` + +Threading model: + +- The **main thread** runs the state-collection loop: every `MOUNT_CHECK_T` seconds it queries +mount status, coordinates, azimuth, pier side, weather, and updates the FITS header. +- The **command socket** is served by `sl_sock` which spawns one thread per connected client. All +command handlers run in those threads; access to the serial device is serialised by `mntdev_mutex`. +- The **Stellarium socket** spawns one thread per connected client, with each thread performing a +bidirectional exchange with the client. +- Shared state (`HDR`, mount status, input coordinates) is protected implicitly — all updates go +through accessor functions, and the serial port is guarded by `mntdev_mutex`. + +Synchronisation primitives: + +- `pthread_mutex_t mntdev_mutex` — serialises access to the mount device. +- `atomic_int mountstatus` — cached mount status visible from any thread. +- `atomic_int emul_status` — emulation-mode status. + +--- + +## Known limitations + +- The daemon assumes a well-behaved serial link; there is no protocol-level retry for corrupted +responses beyond 3 immediate retries on `mount_status()`. +- Weather data is read via `libweather`; the format is expected to be stable. If `libweather` is +unavailable at build time, the code will not build. +- Emulation mode does not model all mount statuses and does not implement true dual-axis motion; it +is a kinematic approximation suitable for client development. +- `mount_corrdata()` uses **local** time for the `:SLDT` command, which matches the 10Micron +firmware convention; this is intentional and differs from the rest of the daemon, which works in +UTC. +- The Stellarium protocol implementation uses JNow coordinates (the DAEMON does not perform the +JNow→J2000 conversion that some clients expect). Planetarium programs that assume J2000 output may +show a small offset; this is acceptable for visual pointing but should be considered if used for +astrometric work. + +--- + +## License + +All source files are licensed under **GNU General Public License v3.0** unless stated otherwise... + diff --git a/Daemons/10micron_stellarium/angles.c b/Daemons/10micron_stellarium/angles.c index 1e514b4..b819658 100644 --- a/Daemons/10micron_stellarium/angles.c +++ b/Daemons/10micron_stellarium/angles.c @@ -53,27 +53,26 @@ void getDUT(almDut_t *D){ /** * @brief setPlaceData - correct place data to given values - * @param longitude - degrees, (-180, 180], minus to west - * @param latitude - degrees, [-90, 90], minus to south + * @param longitude - radians, (-180, 180], minus to west + * @param latitude - radians, [-90, 90], minus to south * @param altitude - meters * @return false if some of data wrong */ -bool setPlaceData(double longitude, double latitude, double altitude){ - if(longitude <= -180. || longitude > 180.){ - WARNX("setPlaceData(): bad longitude (%g)", longitude); +bool setPlaceData(placeData_t *pd){ + if(!pd) return false; + if(pd->slong <= -M_PI || pd->slong > M_PI){ + WARNX("setPlaceData(): bad longitude (%g)", pd->slong); return false; } - if(latitude < -90. || latitude > 90.){ - WARNX("setPlaceData(): bad latitude (%g)", latitude); + if(pd->slat < -M_PI_2 || pd->slat > M_PI_2){ + WARNX("setPlaceData(): bad latitude (%g)", pd->slat); return false; } - if(altitude < -500. || altitude > 9000.){ - WARNX("setPlaceData(): bad altitude (%g)", altitude); + if(pd->salt < -500. || pd->salt > 9000.){ + WARNX("setPlaceData(): bad altitude (%g)", pd->salt); return false; } - place.salt = altitude; - place.slat = DEG2RAD(latitude); - place.slong = DEG2RAD(longitude); + place = *pd; DBG("Change place data: alt=%gm, lat=%gdeg, long=%gdeg", place.salt, RAD2DEG(place.slat), RAD2DEG(place.slong)); return true; } @@ -94,7 +93,7 @@ char *ra2str(double ra, char buf[RADEC_STR_MAXLEN]){ ra -= h; ra *= 60.; int m = (int)ra; ra -= m; ra *= 60.; - snprintf(buf, RADEC_STR_MAXLEN, "%d:%d:%.2f", h,m,ra); + snprintf(buf, RADEC_STR_MAXLEN, "%d:%02d:%05.2f", h,m,ra); buf[RADEC_STR_MAXLEN-1] = 0; return buf; } @@ -114,7 +113,7 @@ char *dec2str(double dec, char buf[RADEC_STR_MAXLEN]){ dec -= d; dec *= 60.; int dm = (int)dec; dec -= dm; dec *= 60.; - snprintf(buf, RADEC_STR_MAXLEN, "%c%d:%d:%.1f",sign,d,dm,dec); + snprintf(buf, RADEC_STR_MAXLEN, "%c%d:%02d:%04.1f",sign,d,dm,dec); buf[RADEC_STR_MAXLEN-1] = 0; return buf; } @@ -128,12 +127,12 @@ bool normAZ(double *a, double *z){ } if(z){ norm_angle180(z); - if(*z > 90. || *z < -90.) return false; - if(*z < 0.){ + if(*z > 90. || *z < 0.) return false; + /*if(*z < 0.){ *z += 90.; *a += 180.; if(*a >= 360.) *a -= 360.; - } + }*/ } return true; } @@ -287,11 +286,14 @@ bool str2coord(const char *str, double *val){ ++str; } int n = sscanf(str, "%d:%d:%f#", &d, &m, &s); + double ang = 0.; if(n != 3){ - DBG("sscanf('%s')=%d", str, 3); - return false; - } - double ang = d + ((double)m)/60. + s/3600.; + DBG("sscanf('%s')=%d; try to read double", str, 3); + if(!sl_str2d(&ang, str)){ + WARNX("Wrong number %s", str); + return false; + } + }else ang = d + ((double)m)/60. + s/3600.; if(sign == -1) *val = -ang; else *val = ang; return true; @@ -443,69 +445,47 @@ bool get_ObsPlace(struct timeval *tval, polarCrds_t *p2000, polarCrds_t *pnow, h bool JnowtoJ2000(const polarCrds_t *in, polarCrds_t *out){ bool ret = false; DBG("appRa: %gdegr, appDecl: %gdegr", RAD2DEG(in->ra), RAD2DEG(in->dec)); -#define ERFA(f, ...) do{if(f(__VA_ARGS__)){WARNX("Error in " #f); goto rtn;}}while(0) +//#define ERFA(f, ...) do{if(f(__VA_ARGS__)){WARNX("Error in " #f); goto rtn;}}while(0) sMJD_t MJD; if(!get_MJDt(NULL, &MJD)) return false; double ri = 0., di = 0., eo = 0.; eraAtic13(in->ra, in->dec, MJD.tt1, MJD.tt2, &ri, &di, &eo); if(out){ out->ha = 0.; out->eo = 0.; - out->ra = eraAnp(ri + eo); + //out->ra = eraAnp(ri - eo); + out->ra = ri; out->dec = di; DBG("JnowtoJ2000: ra=%gdeg, dec=%gdeg", RAD2DEG(out->ra), RAD2DEG(out->dec)); } ret = true; -#undef ERFA +//#undef ERFA return ret; } /** - * @brief J2000toJnow - convert ra/dec between epochs - * @param in - J2000 (degrees) + * @brief JXtoJnow - convert ra/dec between epochs + * @param in - coordinates for Jx (degrees) * @param out - Jnow (degrees) + * @param Jx - Epoch for `in` * @return false if failed */ -bool J2000toJnow(const polarCrds_t *in, polarCrds_t *out){ - DBG("J200RA: %gdegr, J200Dec: %gdegr", RAD2DEG(in->ra), RAD2DEG(in->dec)); - sMJD_t MJD; +bool JXtoJnow(const polarCrds_t *in, polarCrds_t *out, double Jx){ + DBG("JXRA: %ghrs, JXDec: %gdegr; epoch X: %g", RAD2HRS(in->ra), RAD2DEG(in->dec), Jx); if(!in) return false; - if(!get_MJDt(NULL, &MJD)) return false; // these values could be changed (e.g. by user's input) double pr = 0.0; // RA proper motion (radians/year) double pd = 0.0; // Dec proper motion (radians/year) double px = 0.0; // parallax (arcsec) double rv = 0.0; // radial velocity (km/s, positive if receding) double ri, di, eo; - eraAtci13(in->ra, in->dec, pr, pd, px, rv, MJD.tt1, MJD.tt2, &ri, &di, &eo); + eraAtci13(in->ra, in->dec, pr, pd, px, rv, ERFA_DJM0, Jx, &ri, &di, &eo); + LOGDBG("ri: %ghrs, eo: %ghrs, di: %gdeg", RAD2HRS(ri), RAD2HRS(eo), RAD2DEG(di)); if(out){ - out->ra = eraAnp(ri - eo); + out->ha = 0.; out->eo = 0.; + //out->ra = eraAnp(ri - eo); + out->ra = ri; out->dec = di; } return true; } - - -#if 0 -typedef enum { - WEATHER_GOOD = 0, // may start observations - WEATHER_BAD = 1, // cannot start but can continue if want - WEATHER_TERRIBLE = 2, // close & park: wind, precipitation, humidity etc. - WEATHER_PROHIBITED = 3, // force closing & parking; power off equipment, ready to power off computer -} weather_condition_t; - -typedef struct { - weather_condition_t weather; // conditions: field "WEATHER" - float windmax; // maximal wind for last hour, m/s: "WINDMAX1" - float wind; // current wind speed, m/s: "WIND" - float clouds; // sky "quality" (>2500 - OK): "CLOUDS" - float exttemp; // external temperature, degC: "EXTTEMP" - float pressure; // atm. pressure, mmHg: "PRESSURE" - float humidity; // humidity, percents: "HUMIDITY" - int rain; // ==1 when rainy: "PRECIP" - int forceoff; // force power off (AC power lost or lightning) - time_t last_update; // value of "TMEAS" -} weather_data_t; - -int get_weather_data(weather_data_t *data); -#endif diff --git a/Daemons/10micron_stellarium/angles.h b/Daemons/10micron_stellarium/angles.h index 3f33954..a440c7d 100644 --- a/Daemons/10micron_stellarium/angles.h +++ b/Daemons/10micron_stellarium/angles.h @@ -80,9 +80,7 @@ void eq2hor(polarCrds_t *pc, horizCrds_t *h, double sidTime); void r2sHMS(double radians, char *hms, int len); void r2sDMS(double radians, char *hms, int len); void d2sDMS(double degrees, char *dms, int len); -void hor2eq(horizCrds_t *h, polarCrds_t *pc, double sidTime); -void eq2horH(polarCrds_t *pc, horizCrds_t *h); -void eq2hor(polarCrds_t *pc, horizCrds_t *h, double sidTime); + bool get_MJDt(struct timeval *tval, sMJD_t *MJD); bool get_LST(sMJD_t *mjd, double *LST); bool get_ObsPlace(struct timeval *tval, polarCrds_t *p2000, polarCrds_t *pnow, horizCrds_t *hnow); @@ -90,5 +88,8 @@ bool get_ObsPlace(struct timeval *tval, polarCrds_t *p2000, polarCrds_t *pnow, h bool setDUT(almDut_t *D); void getDUT(almDut_t *D); -bool setPlaceData(double longitude, double latitude, double altitude); +bool setPlaceData(placeData_t *pd); void getPlaceData(placeData_t *pd); + +bool JnowtoJ2000(const polarCrds_t *in, polarCrds_t *out); +bool JXtoJnow(const polarCrds_t *in, polarCrds_t *out, double Jx); diff --git a/Daemons/10micron_stellarium/args.c b/Daemons/10micron_stellarium/args.c index c5cb666..5727cc0 100644 --- a/Daemons/10micron_stellarium/args.c +++ b/Daemons/10micron_stellarium/args.c @@ -50,20 +50,28 @@ static parameters_t Conf = {0}; #define STR(x) STR_HELPER(x) #define OPTIONS(Stor) \ - {"device", NEED_ARG, NULL, 'd', arg_string, APTR(&Stor.device), "serial device name"}, \ - {"emulation",NO_ARGS, NULL, 'e', arg_int, APTR(&Stor.emulation), "run in emulation mode"}, \ - {"logfile", NEED_ARG, NULL, 'l', arg_string, APTR(&Stor.logfile), "file to save logs"}, \ - {"hdrfile", NEED_ARG, NULL, 'o', arg_string, APTR(&Stor.crdsfile), "file to save FITS-header with coordinates and time (default: " DEFAULT_FITSHDR ")"}, \ - {"pidfile", NEED_ARG, NULL, 0, arg_string, APTR(&Stor.pidfile), "pidfile (default: " DEFAULT_PIDFILE ")"}, \ - {"port", NEED_ARG, NULL, 'p', arg_string, APTR(&Stor.port), "port for stellaruim's connect (default: " DEFAULT_PORT ")"}, \ - {"cmdport", NEED_ARG, NULL, 'P', arg_string, APTR(&Stor.cmdnode), "port or UNIX-socket path to connect for command console (default: " DEFAULT_CMDNODE ")"}, \ - {"sleept", NEED_ARG, NULL, 't', arg_int, APTR(&Stor.sleept), "time of servers' sleeping in main cycle, us (default: " STR(DEFAULT_SLEEP_T) " us)"}, \ - {"isunix", NO_ARGS, NULL, 'U', arg_int, APTR(&Stor.isunix), "use UNIX-socket for cmdport"}, \ - {"sertmout",NEED_ARG, NULL, 'T', arg_double, APTR(&Stor.sertmout), "serial timeout, seconds (default: " STR(DEFAULT_SERTMOUT) " s)"}, \ - {"serspeed",NEED_ARG, NULL, 'S', arg_int, APTR(&Stor.serspeed), "serial speed (default: " STR(DEFAULT_SERSPEED) ")"}, \ - {"maxclients",NEED_ARG, NULL, 0, arg_int, APTR(&Stor.maxclients),"max amount of clients connected to one socket (default: " STR(DEFAULT_MAXCLIENTS) ")"}, \ - {"mountname",NEED_ARG, NULL, 0, arg_string, APTR(&Stor.mountname), "mount name (default: " DEFAULT_MOUNT_NAME ")"}, \ - {"verbose", NO_ARGS, NULL, 'v', arg_none, APTR(&Stor.verbose), "verbose level (each -v increases it)"}, \ + {"device", NEED_ARG, NULL, 'd', arg_string, APTR(&Stor.device), "serial device name"}, \ + {"emulation",NO_ARGS, NULL, 'e', arg_int, APTR(&Stor.emulation), "run in emulation mode"}, \ + {"logfile", NEED_ARG, NULL, 'l', arg_string, APTR(&Stor.logfile), "file to save logs"}, \ + {"hdrfile", NEED_ARG, NULL, 'o', arg_string, APTR(&Stor.crdsfile), "file to save FITS-header with coordinates and time (default: " DEFAULT_FITSHDR ")"}, \ + {"pidfile", NEED_ARG, NULL, 0, arg_string, APTR(&Stor.pidfile), "pidfile (default: " DEFAULT_PIDFILE ")"}, \ + {"port", NEED_ARG, NULL, 'p', arg_string, APTR(&Stor.port), "port for stellaruim's connect (default: " DEFAULT_PORT ")"}, \ + {"cmdport", NEED_ARG, NULL, 'P', arg_string, APTR(&Stor.cmdnode), "port or UNIX-socket path to connect for command console (default: " DEFAULT_CMDNODE ")"}, \ + {"sleept", NEED_ARG, NULL, 't', arg_int, APTR(&Stor.sleept), "time of servers' sleeping in main cycle, us (default: " STR(DEFAULT_SLEEP_T) " us)"}, \ + {"isunix", NO_ARGS, NULL, 'U', arg_int, APTR(&Stor.isunix), "use UNIX-socket for cmdport"}, \ + {"sertmout",NEED_ARG, NULL, 'T', arg_double, APTR(&Stor.sertmout), "serial timeout, seconds (default: " STR(DEFAULT_SERTMOUT) " s)"}, \ + {"serspeed",NEED_ARG, NULL, 'S', arg_int, APTR(&Stor.serspeed), "serial speed (default: " STR(DEFAULT_SERSPEED) ")"}, \ + {"maxclients",NEED_ARG, NULL, 0, arg_int, APTR(&Stor.maxclients), "max amount of clients connected to one socket (default: " STR(DEFAULT_MAXCLIENTS) ")"}, \ + {"mountname",NEED_ARG, NULL, 0, arg_string, APTR(&Stor.mountname), "mount name (default: " DEFAULT_MOUNT_NAME ")"}, \ + {"verbose", NO_ARGS, NULL, 'v', arg_none, APTR(&Stor.verbose), "verbose level (each -v increases it)"}, \ + {"parka", NEED_ARG, NULL, 'A', arg_string, APTR(&Stor.parkA), "azimuth of parking point"}, \ + {"parkz", NEED_ARG, NULL, 'Z', arg_string, APTR(&Stor.parkZ), "zenith distance of parking point"}, \ + {"dut1", NEED_ARG, NULL, 0, arg_double, APTR(&Stor.DUT1), "set DUT1 value (s)"}, \ + {"polarx", NEED_ARG, NULL, 0, arg_double, APTR(&Stor.polarx), "set polar X value (m)"}, \ + {"polary", NEED_ARG, NULL, 0, arg_double, APTR(&Stor.polary), "set polar Y value (m)"}, \ + {"latitude",NEED_ARG, NULL, 0, arg_string, APTR(&Stor.latitude), "latitude of mount"}, \ + {"altitude",NEED_ARG, NULL, 0, arg_string, APTR(&Stor.altitude), "altitude of mount"}, \ + {"longitude",NEED_ARG, NULL, 0, arg_string, APTR(&Stor.longitude), "longitude of mount"}, \ static sl_option_t cmdlnopts[] = { @@ -126,7 +134,6 @@ static void chkdbl(double *conf, double *cmd, double dflt){ parameters_t *parse_cmdline(int *argc, char ***argv){ sl_parseargs(argc, argv, cmdlnopts); if(help) sl_showhelp(-1, cmdlnopts); - //if(!conffile) return &G; // fix for conffile if(conffile){ if(!sl_conf_readopts(conffile, confopts)){ // show conf help @@ -150,5 +157,13 @@ parameters_t *parse_cmdline(int *argc, char ***argv){ DBLCHKD(sertmout, DEFAULT_SERTMOUT); INTCHKD(serspeed, DEFAULT_SERSPEED); INTCHKD(maxclients, DEFAULT_MAXCLIENTS); + STRCHK(parkA); + STRCHK(parkZ); + STRCHK(longitude); + STRCHK(latitude); + STRCHK(altitude); + DBLCHK(DUT1); + DBLCHK(polarx); + DBLCHK(polary); return &G; } diff --git a/Daemons/10micron_stellarium/args.h b/Daemons/10micron_stellarium/args.h index f52c9d7..207961f 100644 --- a/Daemons/10micron_stellarium/args.h +++ b/Daemons/10micron_stellarium/args.h @@ -26,6 +26,11 @@ typedef struct{ char *logfile; // logging to this file char *crdsfile; // file where FITS-header should be written char *mountname; // set mount name for FITS-header + char *parkA; // parking coordinates (degrees or "DD:MM:SS") + char *parkZ; + char *latitude; // place coordinates + char *longitude; + char *altitude; // place altitude int emulation; // run in emulation mode int verbose; // verbose level int sleept; // server's sleeping in main cycle time @@ -33,6 +38,8 @@ typedef struct{ int serspeed; // serial speed, baud int maxclients; // max amount of clients connected to one socket double sertmout; // serial timeout, s + double DUT1; // DUT1 value + double polarx, polary; // polar coordinates, m } parameters_t; parameters_t *parse_cmdline(int *argc, char ***argv); diff --git a/Daemons/10micron_stellarium/emulation.c b/Daemons/10micron_stellarium/emulation.c index af5934f..1494dfc 100644 --- a/Daemons/10micron_stellarium/emulation.c +++ b/Daemons/10micron_stellarium/emulation.c @@ -215,7 +215,9 @@ void emulation_stop(){ void emul_start_tracking(){ if(MNT_S_TRACKING == emulation_status()) return; atomic_store(&emul_status, MNT_S_TRACKING); - // TODO: convert current A/Z into RA/Dec and set zero speeds by both axes + double LST; + if(!get_LST(NULL, &LST)) return; + hor2eq(&CurAZ, &CurRD, LST); } mount_status_t emulation_status(){ diff --git a/Daemons/10micron_stellarium/fitshdr.c b/Daemons/10micron_stellarium/fitshdr.c index 6337f1d..9eed681 100644 --- a/Daemons/10micron_stellarium/fitshdr.c +++ b/Daemons/10micron_stellarium/fitshdr.c @@ -16,6 +16,7 @@ * along with this program. If not, see . */ +#include #include #include #include @@ -26,10 +27,6 @@ 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 @@ -88,16 +85,16 @@ static int printhdr(int fd, const char *key, const char *val, const char *cmnt){ 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; + double curtime = sl_dtime(); + //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(time(NULL) - HDR->weather.last_update < (int)(1. + MOUNT_CHECK_T * 10.)){ // weather data is good + if(poweredON && time(NULL) - lastcorr > CORRECTIONS_TIMEDIFF){ // make correction once per hour if(mount_corrdata(&HDR->weather)) lastcorr = time(NULL); } - haveweather = true; - } + //haveweather = true; + }else WARNX("bad weather; curtime - HDR->weather.last_update = %g", curtime - HDR->weather.last_update); if(!hdname){ DBG("hdname not given!"); return; @@ -135,32 +132,41 @@ void wrhdr(fitsheader_t *HDR){ 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"); + WRHDR("INPRA", val, "Input RA, degrees"); snprintf(val, 22, "%.10f", RAD2DEG(polar.dec)); - WRHDR("TAGDEC", val, "Target DEC (J2000), degrees"); + WRHDR("INPDEC", val, "Input DEC, degrees"); } }else{ // show horiz if(curtime - ht < COORDS_OLD_T){ snprintf(val, 22, "%.10f", RAD2DEG(horiz.az)); - WRHDR("TAGAZ", val, "Target Azimuth, degrees"); + WRHDR("INPAZ", val, "Input Azimuth, degrees"); snprintf(val, 22, "%.10f", RAD2DEG(horiz.zd)); - WRHDR("TAGZD", val, "Target Zenith dist., degrees"); + WRHDR("INPZD", val, "Input Zenith dist., degrees"); } } + if(curtime - mount_getTagCoords(&polar) < COORDS_OLD_T){ + snprintf(val, 22, "%.10f", RAD2DEG(polar.ra)); + WRHDR("TAGRA", val, "Target RA, degrees"); + snprintf(val, 22, "%.10f", RAD2DEG(polar.dec)); + WRHDR("TAGDEC", val, "Target DEC, degrees"); + } if(poweredON){ - snprintf(val, 22, "%.10f", RAD2DEG(HDR->polar.ra)); // convert RA to degrees + snprintf(val, 22, "%.10f", RAD2HRS(HDR->polar.ra)); // convert RA to degrees WRHDR("RA", val, "Telescope right ascension, current epoch, deg"); + snprintf(val, 22, "%.10f", RAD2HRS(eraAnp(HDR->sidtime - HDR->polar.ra))); // hour angle + WRHDR("HA", val, "Telescope hour angle, 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"); + WRHDR("AZ", val, "Telescope azimuth, deg"); snprintf(val, 22, "%.10f", RAD2DEG(HDR->altaz.zd)); - WRHDR("ZD", val, "Telescope zenith distance, current epoch, deg"); + WRHDR("ZD", val, "Telescope zenith distance, 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 + if(mjd > 0.) snprintf(val, 22, "%.10f", 2000.+(mjd - ERFA_DJM00)/365.25); // calculate EPOCH/EQUINOX + else snprintf(val, 22, "%.10f", 2000.+(HDR->MJD.MJD - ERFA_DJM00)/365.25); 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 @@ -175,8 +181,9 @@ void wrhdr(fitsheader_t *HDR){ 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)); + snprintf(val, 22, "%.6f", RAD2HRS(HDR->sidtime)); WRHDR("LSTEND", val, "Local sidereal time of file creation"); + /* Weather data is written into separate header-file by weather daemon if(haveweather){ snprintf(val, 22, "%.1f", HDR->weather.humidity); WRHDR("HUMIDITY", val, "Relative humidity, %%"); @@ -192,9 +199,9 @@ void wrhdr(fitsheader_t *HDR){ 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); + snprintf(val, 22, "%lld", (long long)HDR->weather.last_update); WRHDR("WEATTIME", val, "Unix time of weather measurements"); - } + }*/ // WRHDR("", , ""); #undef WRHDR returning: diff --git a/Daemons/10micron_stellarium/fitshdr.h b/Daemons/10micron_stellarium/fitshdr.h index a8b4a85..31f4258 100644 --- a/Daemons/10micron_stellarium/fitshdr.h +++ b/Daemons/10micron_stellarium/fitshdr.h @@ -27,6 +27,10 @@ // status checking interval and writing FITS-header, seconds #define MOUNT_CHECK_T 0.5 +#ifndef FLT_EPSILON +#define FLT_EPSILON 1e-6 +#endif + typedef struct{ weather_data_t weather; mount_status_t status; diff --git a/Daemons/10micron_stellarium/main.c b/Daemons/10micron_stellarium/main.c index 0ccd7e0..45132bf 100644 --- a/Daemons/10micron_stellarium/main.c +++ b/Daemons/10micron_stellarium/main.c @@ -28,6 +28,7 @@ #include "fitshdr.h" #include "server.h" #include "mount.h" +#include "stellarium.h" static pid_t childpid = -1; // PID of child process static parameters_t *G = NULL; @@ -43,9 +44,11 @@ void signals(int sig){ } if(childpid > 0){ // parent process LOGERR("PID %d exit with status %d", getpid(), sig); + isrunning = false; }else{ LOGWARN("Child %d died with %d", getpid(), sig); server_stop(); + stellarium_stop(); } DBG("EXIT"); } @@ -75,11 +78,33 @@ int main(int argc, char **argv){ WARNX("Can't set mount name to %s", G->mountname); return 5; } + if(G->parkA){ + double A = 0.; + if(!str2coord(G->parkA, &A) || !mount_setParkAz(A)) WARNX("Can't set parking azimuth to %g", A); + } + if(G->parkZ){ + double Z = 0.; + if(!str2coord(G->parkZ, &Z) || !mount_setParkZD(Z)) WARNX("Can't set parking zenith distance to %g", Z); + } + if(fabs(G->DUT1) > FLT_EPSILON || fabs(G->polarx) > FLT_EPSILON || fabs(G->polary) > FLT_EPSILON){ + almDut_t par = {.DUT1 = G->DUT1, .px = G->polarx, .py = G->polary}; + setDUT(&par); + } + if(G->altitude || G->longitude || G->latitude){ + placeData_t place; + double val; + getPlaceData(&place); + if(G->altitude && sl_str2d(&val, G->altitude)) place.salt = val; + if(G->longitude && str2coord(G->longitude, &val)) place.slong = DEG2RAD(val); + if(G->latitude && str2coord(G->latitude, &val)) place.slat = DEG2RAD(val); + setPlaceData(&place); + } signal(SIGTERM, signals); signal(SIGINT, signals); signal(SIGQUIT, signals); signal(SIGTSTP, SIG_IGN); - signal(SIGHUP, signals); + signal(SIGHUP, SIG_IGN); + //signal(SIGHUP, signals); // re-read config file sl_check4running((char*)__progname, G->pidfile); if(G->logfile) OPENLOG(G->logfile, lvl, 1); DBG("Started"); @@ -108,6 +133,7 @@ int main(int argc, char **argv){ break; // go out to normal functional } } + if(!isrunning) return 0; #endif server_sock_t sockt = { .cmd_isunix = G->isunix, diff --git a/Daemons/10micron_stellarium/mount.c b/Daemons/10micron_stellarium/mount.c index dbfc7a5..ddae71d 100644 --- a/Daemons/10micron_stellarium/mount.c +++ b/Daemons/10micron_stellarium/mount.c @@ -42,9 +42,12 @@ static sl_tty_t *mount_dev = NULL; static pthread_mutex_t mntdev_mutex = PTHREAD_MUTEX_INITIALIZER; // status static atomic_int mountstatus = MNT_S_ERROR; - // parking coordinates -static horizCrds_t ParkCoords = {.az = 0., .zd = DEG2RAD(80.)}; +/* +A="01:48:38" -> A=1.8105556 +H="01:13:29" -> Z=88.7752778 +*/ +static horizCrds_t ParkCoords = {.az = DEG2RAD(1.8105556), .zd = DEG2RAD(88.7752778)}; // input and current target coordinates static polarCrds_t InpCoords = {0}, // input as user give (for epoch InpMJD) TagCoords = {0}; // target for Jnow after command "point to input" @@ -79,6 +82,7 @@ double mount_getInpHor(horizCrds_t *c){ * @return false if `ha` isn't in [0,24) */ bool mount_setInpHA(double ha){ + if(ha < 0) ha += 24.; if(ha < 0. || ha >= 24.) return false; InpCoords.ha = HRS2RAD(ha); InpCTime = sl_dtime(); @@ -228,7 +232,7 @@ static bool send_cmd_resp(const char *cmd, char *resp, size_t resplen){ size_t pos = 0; double t0 = sl_dtime(); bool gotEOM = false; - while(sl_dtime() - t0 < sertmout){ + while(sl_dtime() - t0 < sertmout && !gotEOM){ int got = sl_tty_read(mount_dev); if(got < 0){ WARN("sl_tty_read()"); @@ -241,9 +245,6 @@ static bool send_cmd_resp(const char *cmd, char *resp, size_t resplen){ if(c == '\r' || c == '\n') continue; // WTF? if(c == '#'){ // end of message gotEOM = true; - //if(resp && resplen) resp[pos < resplen ? pos : resplen-1] = 0; - //DBG("Got end of message: '%s' (waited for %gs)", resp, ); - //return true; break; } if(resp && pos + 1 < resplen) resp[pos++] = c; @@ -340,7 +341,7 @@ static bool guess_speed(){ if(!mount_dev) return false; close(mount_dev->comfd); #define SPDBUFSZ 7 - const int speeds[SPDBUFSZ] = {57600, 38400, 19200, 9600, 4800, 2400, 1200}; + const int speeds[SPDBUFSZ] = {9600, 57600, 38400, 19200, 4800, 2400, 1200}; int idx = 0; for(; idx < SPDBUFSZ && isrunning; ++idx){ DBG("try speed %d", speeds[idx]); @@ -382,8 +383,10 @@ bool mount_connect(){ snprintf(buf, 63, CMD_SETMINALT, 10); if(!write_cmd(buf, true)) ret = false; // set minimum altitude to 10 degrees pthread_mutex_unlock(&mntdev_mutex); - if(ret) LOGMSG("Connected to %s@115200", mount_dev->portname); - else LOGERR("Can't write commands to mount"); + if(ret){ + LOGMSG("Connected to %s@115200", mount_dev->portname); + atomic_store(&mountstatus, MNT_S_STOPPED); + }else LOGERR("Can't write commands to mount"); return ret; } @@ -392,7 +395,9 @@ void mount_disconnect(){ emulation_stop(); return; } - pthread_mutex_trylock(&mntdev_mutex); // at least, try + double t0 = sl_dtime(); + while(sl_dtime() - t0 < 120. && pthread_mutex_trylock(&mntdev_mutex)) + usleep(100000); if(mount_dev) close(mount_dev->comfd); pthread_mutex_unlock(&mntdev_mutex); } @@ -427,7 +432,7 @@ bool mount_point(double ra, double dec){ } } ret = true; - TagCoords.ra = DEG2RAD(ra); + TagCoords.ra = HRS2RAD(ra); TagCoords.dec = DEG2RAD(dec); TagTime = sl_dtime(); retn: @@ -461,7 +466,7 @@ bool mount_pointAZ(double A, double Z){ if(!write_cmd(cmd, true)) goto retn; snprintf(cmd, 127, CMD_SETZD, azstr); if(!write_cmd(cmd, true)) goto retn; - if(send_cmd_resp(CMD_GOTOAZ, cmd, 127)){ // returned not '0' + if(send_cmd_resp(CMD_GOTOAZ, cmd, 128)){ // returned not '0' if(*cmd != '0'){ WARNX("Goto error, answer: %s", cmd); LOGWARN("Goto error, answer: %s", cmd); @@ -482,7 +487,9 @@ 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)); + *ra = RAD2HRS(*ra); + *dec = RAD2DEG(*dec); + DBG("Emulated coordinates: %gh, %gdeg", *ra, *dec); return true; } if(!mount_dev) return false; @@ -509,7 +516,7 @@ bool mount_getaz(double *a, double *z){ double LST; get_LST(NULL, &LST); eq2hor(&P, &H, LST); - *a = H.az; *z = H.zd; + *a = RAD2DEG(H.az); *z = RAD2DEG(H.zd); return true; } if(!mount_dev) return false; @@ -582,7 +589,9 @@ bool mount_tracking_start(){ */ bool mount_park(){ if(isemulated) return pointAZ_emulation(ParkCoords.az, ParkCoords.zd); - // TODO: set lower limit to 0deg + char buf[64]; + snprintf(buf, 63, CMD_SETMINALT, 0); + if(!write_cmd(buf, true)) return false; return mount_pointAZ(RAD2DEG(ParkCoords.az), RAD2DEG(ParkCoords.zd)); } @@ -672,3 +681,11 @@ bool mount_corrdata(weather_data_t *w){ pthread_mutex_unlock(&mntdev_mutex); return ret; } + +/** + * @brief mount_shutdown - turn off mount + * @return false if failed + */ +bool mount_shutdown(){ + return write_cmd(CMD_SHUTDOWN, true); +} diff --git a/Daemons/10micron_stellarium/mount.h b/Daemons/10micron_stellarium/mount.h index 4a42900..4d9a422 100644 --- a/Daemons/10micron_stellarium/mount.h +++ b/Daemons/10micron_stellarium/mount.h @@ -88,3 +88,5 @@ bool mount_park(); bool mount_setParkAz(double az); bool mount_setParkZD(double zd); void mount_getPark(horizCrds_t *c); + +bool mount_shutdown(); diff --git a/Daemons/10micron_stellarium/mountdaemon_10micron.creator.user b/Daemons/10micron_stellarium/mountdaemon_10micron.creator.user index f10b7c7..fba2ccf 100644 --- a/Daemons/10micron_stellarium/mountdaemon_10micron.creator.user +++ b/Daemons/10micron_stellarium/mountdaemon_10micron.creator.user @@ -1,6 +1,6 @@ - + EnvironmentId diff --git a/Daemons/10micron_stellarium/mountdaemon_10micron.files b/Daemons/10micron_stellarium/mountdaemon_10micron.files index 16f6aa1..a5a955b 100644 --- a/Daemons/10micron_stellarium/mountdaemon_10micron.files +++ b/Daemons/10micron_stellarium/mountdaemon_10micron.files @@ -1,4 +1,5 @@ 10micron_commands.h +Readme.md angles.c angles.h args.c diff --git a/Daemons/10micron_stellarium/server.c b/Daemons/10micron_stellarium/server.c index 8bd0cd7..3e9ae5b 100644 --- a/Daemons/10micron_stellarium/server.c +++ b/Daemons/10micron_stellarium/server.c @@ -36,26 +36,34 @@ #define STATUS_MAX_AGE (30.) // commands -#define CMD_UNIXT "unixt" -#define CMD_STATUS "status" -#define CMD_STOP "stop" -#define CMD_TAGRA "tagra" -#define CMD_TAGDEC "tagdec" -#define CMD_TAGHA "tagha" -#define CMD_TAGAZ "tagaz" -#define CMD_TAGZD "tagzd" -#define CMD_TELRA "telra" -#define CMD_TELDEC "teldec" -#define CMD_TELAZ "telaz" -#define CMD_TELZD "telzd" +#define CMD_DUT1 "dut1" +#define CMD_GOTOAZ "gotoaz" #define CMD_GOTORD "gotord" #define CMD_GOTORH "gotorh" -#define CMD_GOTOAZ "gotoaz" -#define CMD_STOPTRK "stoptrk" -#define CMD_TRACK "track" -#define CMD_PARK "park" +#define CMD_LST "lst" #define CMD_PARKAZ "parkaz" +#define CMD_PARK "park" #define CMD_PARKZD "parkzd" +#define CMD_PLACE "place" +#define CMD_POLARX "polarx" +#define CMD_POLARY "polary" +#define CMD_SHUTDOWN "shutdown" +#define CMD_STATUS "status" +#define CMD_STOP "stop" +#define CMD_STOPTRK "stoptrk" +#define CMD_TAGAZ "tagaz" +#define CMD_TAGDEC "tagdec" +#define CMD_TAGHA "tagha" +#define CMD_TAGMJD "tagmjd" +#define CMD_TAGRA "tagra" +#define CMD_TAGZD "tagzd" +#define CMD_TELAZ "telaz" +#define CMD_TELDEC "teldec" +#define CMD_TELHA "telha" +#define CMD_TELRA "telra" +#define CMD_TELZD "telzd" +#define CMD_TRACK "track" +#define CMD_UNIXT "unixt" // main command socket @@ -65,7 +73,7 @@ static int stellarium_sockfd = -1; // sleep time (us) static unsigned int sleept = DEFAULT_SLEEP_T; // running flag -volatile bool isrunning = false; +volatile bool isrunning = true; // lost weather flag static bool weatherlost = false; @@ -102,9 +110,7 @@ static int parse_key_value(const char *req, double *val){ if(!req) return 0; // is getter DBG("parsing of %s", req); double d; - if(strchr(req, ':')){ // DD:MM:SS or HH:MM:SS - if(!str2coord(req, &d)) return -1; - }else if(sscanf(req, "%lf", &d) != 1) return -1; // error + if(!str2coord(req, &d)) return -1; if(val) *val = d; return 1; // is setter } @@ -115,6 +121,7 @@ static sl_sock_hresult_e cmd_tagra(sl_sock_t *c, sl_sock_hitem_t *item, const ch int res = parse_key_value(req, &val); if(res < 0) return RESULT_BADVAL; if(res > 0){ // setter + LOGDBG("Client %d set tagra: %g", c->fd, val); if(mount_setInpRA(val)) return RESULT_OK; return RESULT_BADVAL; }else{ // getter @@ -133,6 +140,7 @@ static sl_sock_hresult_e cmd_tagdec(sl_sock_t *c, sl_sock_hitem_t *item, const c int res = parse_key_value(req, &val); if(res < 0) return RESULT_BADVAL; if(res > 0){ + LOGDBG("Client %d set tagdec: %g", c->fd, val); if(mount_setInpDec(val)) return RESULT_OK; return RESULT_BADVAL; }else{ @@ -151,6 +159,7 @@ static sl_sock_hresult_e cmd_tagha(sl_sock_t *c, sl_sock_hitem_t *item, const ch int res = parse_key_value(req, &val); if(res < 0) return RESULT_BADVAL; if(res > 0){ + LOGDBG("Client %d set tagha: %g", c->fd, val); if(mount_setInpHA(val)) return RESULT_OK; return RESULT_BADVAL; }else{ @@ -169,6 +178,7 @@ static sl_sock_hresult_e cmd_tagaz(sl_sock_t *c, sl_sock_hitem_t *item, const ch int res = parse_key_value(req, &val); if(res < 0) return RESULT_BADVAL; if(res > 0){ + LOGDBG("Client %d set tagaz: %g", c->fd, val); if(mount_setInpA(val)) return RESULT_OK; return RESULT_BADVAL; }else{ @@ -187,6 +197,7 @@ static sl_sock_hresult_e cmd_tagzd(sl_sock_t *c, sl_sock_hitem_t *item, const ch int res = parse_key_value(req, &val); if(res < 0) return RESULT_BADVAL; if(res > 0){ + LOGDBG("Client %d set tagzd: %g", c->fd, val); if(mount_setInpZ(val)) return RESULT_OK; return RESULT_BADVAL; }else{ @@ -200,6 +211,13 @@ 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_lst(sl_sock_t *c, sl_sock_hitem_t *item, _U_ const char *req){ + char buf[64]; + snprintf(buf, 63, "%s=%.6f\n", item->key, RAD2HRS(HDR.sidtime)); + sl_sock_sendstrmessage(c, buf); + return RESULT_SILENCE; +} + 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]; @@ -208,6 +226,15 @@ static sl_sock_hresult_e cmd_telra(sl_sock_t *c, sl_sock_hitem_t *item, _U_ cons return RESULT_SILENCE; } +static sl_sock_hresult_e cmd_telha(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]; + double ha = eraAnp(HDR.sidtime - HDR.polar.ra); + snprintf(buf, 63, "%s=%.6f\n", item->key, RAD2HRS(ha)); + 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){ if(weatherlost || HDR.status == MNT_S_ERROR) return RESULT_FAIL; char buf[64]; @@ -234,10 +261,16 @@ static sl_sock_hresult_e cmd_telzd(sl_sock_t *c, sl_sock_hitem_t *item, _U_ cons 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); - double dec_d = RAD2DEG(p.dec); + polarCrds_t px, pnow; + mount_getInpCoords(&px); + double Epoch; + mount_getInpMJD(&Epoch); + if(Epoch > 0.){ + if(!JXtoJnow(&px, &pnow, Epoch)) return RESULT_FAIL; + }else pnow = px; + double ra_h = RAD2HRS(pnow.ra); + double dec_d = RAD2DEG(pnow.dec); + LOGDBG("Client %d asks to point ra=%ghrs, dec=%gdeg", c->fd, ra_h, dec_d); if(!mount_point(ra_h, dec_d)) return RESULT_FAIL; return RESULT_OK; } @@ -253,6 +286,7 @@ static sl_sock_hresult_e cmd_gotorh(_U_ sl_sock_t *c, _U_ sl_sock_hitem_t *item, double RA_rad = eraAnp(LST - p.ha); double ra_h = RAD2HRS(RA_rad); double dec_d = RAD2DEG(p.dec); + LOGDBG("Client %d asks to point to HA/Dec", c->fd); if(!mount_point(ra_h, dec_d)) return RESULT_FAIL; return RESULT_OK; } @@ -261,16 +295,19 @@ static sl_sock_hresult_e cmd_gotoaz(_U_ sl_sock_t *c, _U_ sl_sock_hitem_t *item, if(weatherlost || HDR.status == MNT_S_ERROR) return RESULT_FAIL; horizCrds_t h; mount_getInpHor(&h); + LOGDBG("Client %d asks to point to A/Z", c->fd); 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){ + LOGDBG("Client %d asks to stop", c->fd); 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){ + LOGDBG("Client %d asks to stop tracking", c->fd); if(mount_tracking_stop()) return RESULT_OK; return RESULT_FAIL; } @@ -278,11 +315,13 @@ static sl_sock_hresult_e cmd_stoptrk(_U_ sl_sock_t *c, _U_ sl_sock_hitem_t *item // 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){ if(weatherlost || HDR.status == MNT_S_ERROR) return RESULT_FAIL; + LOGDBG("Client %d asks to start tracking", c->fd); if(mount_tracking_start()) return RESULT_OK; return RESULT_FAIL; } static sl_sock_hresult_e cmd_park(_U_ sl_sock_t *c, _U_ sl_sock_hitem_t *item, _U_ const char *req){ + LOGDBG("Client %d asks to park", c->fd); if(mount_park()) return RESULT_OK; return RESULT_FAIL; } @@ -293,6 +332,7 @@ static sl_sock_hresult_e cmd_parkaz(sl_sock_t *c, sl_sock_hitem_t *item, const c int res = parse_key_value(req, &val); if(res < 0) return RESULT_BADVAL; if(res > 0){ + LOGDBG("Client %d set parking Az to %g", c->fd, val); if(mount_setParkAz(val)) return RESULT_OK; return RESULT_BADVAL; }else{ @@ -309,6 +349,7 @@ static sl_sock_hresult_e cmd_parkzd(sl_sock_t *c, sl_sock_hitem_t *item, const c int res = parse_key_value(req, &val); if(res < 0) return RESULT_BADVAL; if(res > 0){ + LOGDBG("Client %d set parking ZD to %g", c->fd, val); if(mount_setParkZD(val)) return RESULT_OK; return RESULT_BADVAL; }else{ @@ -321,28 +362,150 @@ static sl_sock_hresult_e cmd_parkzd(sl_sock_t *c, sl_sock_hitem_t *item, const c return RESULT_SILENCE; } +static sl_sock_hresult_e cmd_tagmjd(sl_sock_t *c, sl_sock_hitem_t *item, const char *req){ + double val; + bool isyear = false; + if(req && *req == 'J'){ // e.g. J2050. + ++req; + isyear = true; + } + int res = parse_key_value(req, &val); + if(res < 0) return RESULT_BADVAL; + if(res > 0){ + if(isyear) val = (val - 2000.)*365.25 + ERFA_DJM00; + LOGDBG("Client %d set target epoch %g", c->fd, val); + if(mount_setInpMJD(val)) return RESULT_OK; + return RESULT_BADVAL; + } + mount_getInpMJD(&val); + char buf[64]; + snprintf(buf, 63, "%s=%.1f\n", item->key, val); + sl_sock_sendstrmessage(c, buf); + return RESULT_SILENCE; +} + +static sl_sock_hresult_e cmd_dut1(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; + almDut_t curval; + getDUT(&curval); + if(res > 0){ + LOGDBG("Client %d set dut1 %g", c->fd, val); + curval.DUT1 = val; + if(setDUT(&curval)) return RESULT_OK; + return RESULT_BADVAL; + } + char buf[64]; + snprintf(buf, 63, "%s=%.3f\n", item->key, curval.DUT1); + sl_sock_sendstrmessage(c, buf); + return RESULT_SILENCE; +} + +static sl_sock_hresult_e cmd_polarx(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; + almDut_t curval; + getDUT(&curval); + if(res > 0){ + LOGDBG("Client %d set polarx %g", c->fd, val); + curval.px = val; + if(setDUT(&curval)) return RESULT_OK; + return RESULT_BADVAL; + } + char buf[64]; + snprintf(buf, 63, "%s=%.3f\n", item->key, curval.px); + sl_sock_sendstrmessage(c, buf); + return RESULT_SILENCE; +} + +static sl_sock_hresult_e cmd_polary(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; + almDut_t curval; + getDUT(&curval); + if(res > 0){ + LOGDBG("Client %d set polary %g", c->fd, val); + curval.py = val; + if(setDUT(&curval)) return RESULT_OK; + return RESULT_BADVAL; + } + char buf[64]; + snprintf(buf, 63, "%s=%.3f\n", item->key, curval.py); + sl_sock_sendstrmessage(c, buf); + return RESULT_SILENCE; +} + +static sl_sock_hresult_e cmd_place(sl_sock_t *c, sl_sock_hitem_t _U_ *item, const char _U_ *req){ + char buf[128]; + placeData_t place; + getPlaceData(&place); + snprintf(buf, 127, "latitude=%.7f\nlongitude=%.7f\naltitude=%.7f\n", + RAD2DEG(place.slat), RAD2DEG(place.slong), place.salt); + sl_sock_sendstrmessage(c, buf); + return RESULT_SILENCE; +} + +static sl_sock_hresult_e cmd_shutdown(sl_sock_t *c, sl_sock_hitem_t *item, const char *req){ + double val; + static int shtdwnkey = -1; + static time_t keytime = 0; + int res = parse_key_value(req, &val); + if(res < 0) return RESULT_BADVAL; + time_t tnow = time(NULL); + if(tnow - keytime > SHTDWN_KEY_TMOUT) keytime = 0; + if(res == 0){ // generate new shutdown key or show old - depending on keytime + if(!keytime){ + keytime = tnow; + srand(tnow); + shtdwnkey = rand(); + LOGDBG("Generated new shutdown key: %d", shtdwnkey); + } + char buf[64]; + snprintf(buf, 63, "%s=%d\n", item->key, shtdwnkey); + sl_sock_sendstrmessage(c, buf); + return RESULT_SILENCE; + } + if((int)val != shtdwnkey){ + LOGWARN("User tryes to run shutdown with wrong key"); + return RESULT_BADVAL; + } + LOGWARN("User give command \"shutdown\""); + if(!mount_shutdown()) return RESULT_FAIL; + return RESULT_OK; +} // and all handlers collection static sl_sock_hitem_t handlers[] = { + {cmd_dut1, CMD_DUT1, "set DUT1 (s)", NULL}, {cmd_gotoaz, CMD_GOTOAZ, "point telescope by input Az/ZD and stop", NULL}, {cmd_gotord, CMD_GOTORD, "point telescope by input RA/Dec and start tracking", NULL}, {cmd_gotorh, CMD_GOTORH, "point telescope by input RA/HA and start tracking", NULL}, + {cmd_lst, CMD_LST, "get current local sidereal time", NULL}, {cmd_park, CMD_PARK, "park telescope", NULL}, {cmd_parkaz, CMD_PARKAZ, "set parking azimuth (Deg: 0 - north, 90 - east)", NULL}, {cmd_parkzd, CMD_PARKZD, "set parking zenith distance (Deg)", NULL}, + {cmd_place, CMD_PLACE, "get place data", NULL}, + {cmd_polarx, CMD_POLARX, "set polar X value (m)", NULL}, + {cmd_polary, CMD_POLARY, "set polar Y value (m)", NULL}, + {cmd_shutdown, CMD_SHUTDOWN, "shut down mount power", NULL}, {status, CMD_STATUS, "get mount status", NULL}, {cmd_stop, CMD_STOP, "stop telescope", NULL}, + {cmd_stoptrk, CMD_STOPTRK, "stop tracking", NULL}, {cmd_tagaz, CMD_TAGAZ, "get/set target azimuth (Deg)", NULL}, {cmd_tagdec, CMD_TAGDEC, "get/set target declination (Deg)", NULL}, {cmd_tagha, CMD_TAGHA, "get/set target hour angle (Hrs)", NULL}, + {cmd_tagmjd, CMD_TAGMJD, "get/set target MJD epoch (-1 for Jnow) or Jxxx for year, like J2050.", NULL}, {cmd_tagra, CMD_TAGRA, "get/set target right acsention (Hrs)", NULL}, {cmd_tagzd, CMD_TAGZD, "get/set target zenith distance (Deg)", NULL}, {cmd_teldec, CMD_TELDEC, "get current telescope declination (Deg)", NULL}, + {cmd_telha, CMD_TELHA, "get current telescope hour angle (Hrs)", NULL}, {cmd_telra, CMD_TELRA, "get current telescope right acsention (Hrs)", NULL}, {cmd_telaz, CMD_TELAZ, "get current telescope azimuth (Deg)", NULL}, {cmd_telzd, CMD_TELZD, "get current telescope zenith distance (Deg)", NULL}, - {cmd_stoptrk, CMD_STOPTRK, "stop tracking", NULL}, {cmd_track, CMD_TRACK, "start tracking from current position", NULL}, {dtimeh, CMD_UNIXT, "get server's UNIX time", NULL}, {NULL, NULL, NULL, NULL} @@ -415,10 +578,6 @@ bool server_check(server_sock_t *sockt){ } void server_run(){ - if(isrunning){ - LOGERR("server_run(): still running!"); - return; - } if(stellarium_sockfd == -1 || !cmd_socket){ LOGERR("server_run(): not initialized"); if(cmd_socket) sl_sock_delete(&cmd_socket); @@ -455,26 +614,29 @@ void server_run(){ } // 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); + get_MJDt(NULL, &HDR.MJD); + get_LST(&HDR.MJD, &HDR.sidtime); + HDR.polar.ra = HRS2RAD(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; + }else{ + get_MJDt(NULL, &HDR.MJD); + get_LST(&HDR.MJD, &HDR.sidtime); } - 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 diff --git a/Daemons/10micron_stellarium/server.h b/Daemons/10micron_stellarium/server.h index 32c734d..0bf2638 100644 --- a/Daemons/10micron_stellarium/server.h +++ b/Daemons/10micron_stellarium/server.h @@ -32,6 +32,9 @@ // max weather lost time #define MOUNT_WEATHER_ALRM 900. +// shutdown key refresh period (5min) +#define SHTDWN_KEY_TMOUT 300 + typedef struct{ int cmd_isunix; // UNIX-socket instead of INET for `cmdnode` const char *stellport; // port of stellarium server; could be "localhost:port" for local-only work diff --git a/Daemons/10micron_stellarium/stellarium.c b/Daemons/10micron_stellarium/stellarium.c index 1693d3a..8f50e67 100644 --- a/Daemons/10micron_stellarium/stellarium.c +++ b/Daemons/10micron_stellarium/stellarium.c @@ -32,7 +32,7 @@ #define BUFLEN 256 // running flag -static volatile bool isrunning = false; +extern volatile bool isrunning; static pthread_t mainthread; //read: 0x14 0x0 0x0 0x0 0x5b 0x5a 0x2e 0xc6 0x8c 0x23 0x5 0x0 0x23 0x9 0xe5 0xaf 0x23 0x2e 0x34 0xed @@ -138,7 +138,7 @@ static bool proc_data(uint8_t *data, ssize_t len){ * @param sockfd - socket fd for sending data * @return false if client disconnected */ -float send_data(uint8_t *data, size_t dlen, int sockfd){ +static bool send_data(uint8_t *data, size_t dlen, int sockfd){ ssize_t sent = send(sockfd, data, dlen, MSG_NOSIGNAL); if(sent != (ssize_t)dlen){ if(sent == -1 && errno != EINTR){ @@ -168,10 +168,19 @@ static void *handle_socket(void *sockd){ sleep(1); continue; } - double RA = HRS2RAD(Rhrs), Decl = DEG2RAD(Ddeg); + /* + // convert from Jnow to J2000 + polarCrds_t Jn = {0}, J2000 = {0}; + Jn.ra = HRS2RAD(Rhrs); + Jn.dec = DEG2RAD(Ddeg); + if(!JnowtoJ2000(&Jn, &J2000)) break; //DBG("got : %g/%g", RA, Decl); - dout.ra = htole32(RAD2RA(RA)); - dout.dec = (int32_t)htole32(RAD2DEC(Decl)); + dout.ra = htole32(RAD2RA(J2000.ra)); + dout.dec = (int32_t)htole32(RAD2DEC(J2000.dec)); + */ + dout.ra = htole32(RAD2RA(HRS2RAD(Rhrs))); + dout.dec = (int32_t)htole32(RAD2DEC(DEG2RAD(Ddeg))); + /**/ if(!send_data((uint8_t*)&dout, sizeof(outdata), sock)) break; if(!sl_canread(sock)){ sleep(1); @@ -199,10 +208,6 @@ static void *handle_socket(void *sockd){ // Main loop thread: wait connections over socket and create one thread for each static void* start(void *F){ - if(isrunning){ - WARNX("already running"); - return NULL; - } if(!F){ WARNX("start(): No arg"); return NULL; @@ -213,7 +218,6 @@ static void* start(void *F){ return NULL; } DBG("Start main loop, sockfd=%d", sockfd); - isrunning = true; // Main loop while(isrunning){ socklen_t size = sizeof(struct sockaddr_in); @@ -271,11 +275,13 @@ bool stellarium_start(int sockfd){ return false; } DBG("Stellarium server started"); + LOGDBG("Stellarium server started"); return true; } void stellarium_stop(){ - if(!isrunning) return; - isrunning = false; + if(isrunning) isrunning = false; + /*DBG("Wait for stellarium thread"); pthread_join(mainthread, NULL); + LOGDBG("Stellarium thread died");*/ }