mirror of
https://github.com/eddyem/small_tel.git
synced 2026-09-30 13:50:32 +03:00
fix some bugs
This commit is contained in:
@@ -19,7 +19,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
// commands
|
// commands
|
||||||
#define CMD_SHUTDOWN ":shutdown#" // TODO!
|
#define CMD_SHUTDOWN ":shutdown#"
|
||||||
#define CMD_TRKSTART ":AP#"
|
#define CMD_TRKSTART ":AP#"
|
||||||
#define CMD_TRKSTOP ":AL#"
|
#define CMD_TRKSTOP ":AL#"
|
||||||
#define CMD_GOTOAZ ":MA#"
|
#define CMD_GOTOAZ ":MA#"
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ set(PROJ mountdaemon_10micron)
|
|||||||
project(${PROJ})
|
project(${PROJ})
|
||||||
|
|
||||||
set(MAJOR_VERSION "0")
|
set(MAJOR_VERSION "0")
|
||||||
set(MID_VERSION "1")
|
set(MID_VERSION "2")
|
||||||
set(MINOR_VERSION "1")
|
set(MINOR_VERSION "1")
|
||||||
set(VERSION "${MAJOR_VERSION}.${MID_VERSION}.${MINOR_VERSION}")
|
set(VERSION "${MAJOR_VERSION}.${MID_VERSION}.${MINOR_VERSION}")
|
||||||
|
|
||||||
@@ -36,7 +36,7 @@ aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR} SOURCES)
|
|||||||
|
|
||||||
###### pkgconfig ######
|
###### pkgconfig ######
|
||||||
find_package(PkgConfig REQUIRED)
|
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
|
# change wrong behaviour with install prefix
|
||||||
if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT AND CMAKE_INSTALL_PREFIX MATCHES "/usr/local")
|
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
|
# -D
|
||||||
add_definitions(-D_XOPEN_SOURCE=1234 -D_DEFAULT_SOURCE -D_GNU_SOURCE
|
add_definitions(-D_XOPEN_SOURCE=1234 -D_DEFAULT_SOURCE -D_GNU_SOURCE
|
||||||
-DPACKAGE_VERSION=\"${VERSION}\" -DMINOR_VERSION=\"${MINOR_VERSION}\"
|
-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
|
# Installation of the program
|
||||||
|
|||||||
496
Daemons/10micron_stellarium/Readme.md
Normal file
496
Daemons/10micron_stellarium/Readme.md
Normal file
@@ -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 `<prefix>/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
|
||||||
|
<file>`. 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<year>` | 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=<key>`.
|
||||||
|
2. Within 5 minutes, a client must send `shutdown = <key>`. 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...
|
||||||
|
|
||||||
@@ -53,27 +53,26 @@ void getDUT(almDut_t *D){
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief setPlaceData - correct place data to given values
|
* @brief setPlaceData - correct place data to given values
|
||||||
* @param longitude - degrees, (-180, 180], minus to west
|
* @param longitude - radians, (-180, 180], minus to west
|
||||||
* @param latitude - degrees, [-90, 90], minus to south
|
* @param latitude - radians, [-90, 90], minus to south
|
||||||
* @param altitude - meters
|
* @param altitude - meters
|
||||||
* @return false if some of data wrong
|
* @return false if some of data wrong
|
||||||
*/
|
*/
|
||||||
bool setPlaceData(double longitude, double latitude, double altitude){
|
bool setPlaceData(placeData_t *pd){
|
||||||
if(longitude <= -180. || longitude > 180.){
|
if(!pd) return false;
|
||||||
WARNX("setPlaceData(): bad longitude (%g)", longitude);
|
if(pd->slong <= -M_PI || pd->slong > M_PI){
|
||||||
|
WARNX("setPlaceData(): bad longitude (%g)", pd->slong);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if(latitude < -90. || latitude > 90.){
|
if(pd->slat < -M_PI_2 || pd->slat > M_PI_2){
|
||||||
WARNX("setPlaceData(): bad latitude (%g)", latitude);
|
WARNX("setPlaceData(): bad latitude (%g)", pd->slat);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if(altitude < -500. || altitude > 9000.){
|
if(pd->salt < -500. || pd->salt > 9000.){
|
||||||
WARNX("setPlaceData(): bad altitude (%g)", altitude);
|
WARNX("setPlaceData(): bad altitude (%g)", pd->salt);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
place.salt = altitude;
|
place = *pd;
|
||||||
place.slat = DEG2RAD(latitude);
|
|
||||||
place.slong = DEG2RAD(longitude);
|
|
||||||
DBG("Change place data: alt=%gm, lat=%gdeg, long=%gdeg", place.salt, RAD2DEG(place.slat), RAD2DEG(place.slong));
|
DBG("Change place data: alt=%gm, lat=%gdeg, long=%gdeg", place.salt, RAD2DEG(place.slat), RAD2DEG(place.slong));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -94,7 +93,7 @@ char *ra2str(double ra, char buf[RADEC_STR_MAXLEN]){
|
|||||||
ra -= h; ra *= 60.;
|
ra -= h; ra *= 60.;
|
||||||
int m = (int)ra;
|
int m = (int)ra;
|
||||||
ra -= m; ra *= 60.;
|
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;
|
buf[RADEC_STR_MAXLEN-1] = 0;
|
||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
@@ -114,7 +113,7 @@ char *dec2str(double dec, char buf[RADEC_STR_MAXLEN]){
|
|||||||
dec -= d; dec *= 60.;
|
dec -= d; dec *= 60.;
|
||||||
int dm = (int)dec;
|
int dm = (int)dec;
|
||||||
dec -= dm; dec *= 60.;
|
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;
|
buf[RADEC_STR_MAXLEN-1] = 0;
|
||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
@@ -128,12 +127,12 @@ bool normAZ(double *a, double *z){
|
|||||||
}
|
}
|
||||||
if(z){
|
if(z){
|
||||||
norm_angle180(z);
|
norm_angle180(z);
|
||||||
if(*z > 90. || *z < -90.) return false;
|
if(*z > 90. || *z < 0.) return false;
|
||||||
if(*z < 0.){
|
/*if(*z < 0.){
|
||||||
*z += 90.;
|
*z += 90.;
|
||||||
*a += 180.;
|
*a += 180.;
|
||||||
if(*a >= 360.) *a -= 360.;
|
if(*a >= 360.) *a -= 360.;
|
||||||
}
|
}*/
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -287,11 +286,14 @@ bool str2coord(const char *str, double *val){
|
|||||||
++str;
|
++str;
|
||||||
}
|
}
|
||||||
int n = sscanf(str, "%d:%d:%f#", &d, &m, &s);
|
int n = sscanf(str, "%d:%d:%f#", &d, &m, &s);
|
||||||
|
double ang = 0.;
|
||||||
if(n != 3){
|
if(n != 3){
|
||||||
DBG("sscanf('%s')=%d", str, 3);
|
DBG("sscanf('%s')=%d; try to read double", str, 3);
|
||||||
return false;
|
if(!sl_str2d(&ang, str)){
|
||||||
}
|
WARNX("Wrong number %s", str);
|
||||||
double ang = d + ((double)m)/60. + s/3600.;
|
return false;
|
||||||
|
}
|
||||||
|
}else ang = d + ((double)m)/60. + s/3600.;
|
||||||
if(sign == -1) *val = -ang;
|
if(sign == -1) *val = -ang;
|
||||||
else *val = ang;
|
else *val = ang;
|
||||||
return true;
|
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 JnowtoJ2000(const polarCrds_t *in, polarCrds_t *out){
|
||||||
bool ret = false;
|
bool ret = false;
|
||||||
DBG("appRa: %gdegr, appDecl: %gdegr", RAD2DEG(in->ra), RAD2DEG(in->dec));
|
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;
|
sMJD_t MJD;
|
||||||
if(!get_MJDt(NULL, &MJD)) return false;
|
if(!get_MJDt(NULL, &MJD)) return false;
|
||||||
double ri = 0., di = 0., eo = 0.;
|
double ri = 0., di = 0., eo = 0.;
|
||||||
eraAtic13(in->ra, in->dec, MJD.tt1, MJD.tt2, &ri, &di, &eo);
|
eraAtic13(in->ra, in->dec, MJD.tt1, MJD.tt2, &ri, &di, &eo);
|
||||||
if(out){
|
if(out){
|
||||||
out->ha = 0.; out->eo = 0.;
|
out->ha = 0.; out->eo = 0.;
|
||||||
out->ra = eraAnp(ri + eo);
|
//out->ra = eraAnp(ri - eo);
|
||||||
|
out->ra = ri;
|
||||||
out->dec = di;
|
out->dec = di;
|
||||||
DBG("JnowtoJ2000: ra=%gdeg, dec=%gdeg", RAD2DEG(out->ra), RAD2DEG(out->dec));
|
DBG("JnowtoJ2000: ra=%gdeg, dec=%gdeg", RAD2DEG(out->ra), RAD2DEG(out->dec));
|
||||||
}
|
}
|
||||||
ret = true;
|
ret = true;
|
||||||
#undef ERFA
|
//#undef ERFA
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief J2000toJnow - convert ra/dec between epochs
|
* @brief JXtoJnow - convert ra/dec between epochs
|
||||||
* @param in - J2000 (degrees)
|
* @param in - coordinates for Jx (degrees)
|
||||||
* @param out - Jnow (degrees)
|
* @param out - Jnow (degrees)
|
||||||
|
* @param Jx - Epoch for `in`
|
||||||
* @return false if failed
|
* @return false if failed
|
||||||
*/
|
*/
|
||||||
bool J2000toJnow(const polarCrds_t *in, polarCrds_t *out){
|
bool JXtoJnow(const polarCrds_t *in, polarCrds_t *out, double Jx){
|
||||||
DBG("J200RA: %gdegr, J200Dec: %gdegr", RAD2DEG(in->ra), RAD2DEG(in->dec));
|
DBG("JXRA: %ghrs, JXDec: %gdegr; epoch X: %g", RAD2HRS(in->ra), RAD2DEG(in->dec), Jx);
|
||||||
sMJD_t MJD;
|
|
||||||
if(!in) return false;
|
if(!in) return false;
|
||||||
if(!get_MJDt(NULL, &MJD)) return false;
|
|
||||||
// these values could be changed (e.g. by user's input)
|
// these values could be changed (e.g. by user's input)
|
||||||
double pr = 0.0; // RA proper motion (radians/year)
|
double pr = 0.0; // RA proper motion (radians/year)
|
||||||
double pd = 0.0; // Dec proper motion (radians/year)
|
double pd = 0.0; // Dec proper motion (radians/year)
|
||||||
double px = 0.0; // parallax (arcsec)
|
double px = 0.0; // parallax (arcsec)
|
||||||
double rv = 0.0; // radial velocity (km/s, positive if receding)
|
double rv = 0.0; // radial velocity (km/s, positive if receding)
|
||||||
double ri, di, eo;
|
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){
|
if(out){
|
||||||
out->ra = eraAnp(ri - eo);
|
out->ha = 0.; out->eo = 0.;
|
||||||
|
//out->ra = eraAnp(ri - eo);
|
||||||
|
out->ra = ri;
|
||||||
out->dec = di;
|
out->dec = di;
|
||||||
}
|
}
|
||||||
return true;
|
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
|
|
||||||
|
|||||||
@@ -80,9 +80,7 @@ void eq2hor(polarCrds_t *pc, horizCrds_t *h, double sidTime);
|
|||||||
void r2sHMS(double radians, char *hms, int len);
|
void r2sHMS(double radians, char *hms, int len);
|
||||||
void r2sDMS(double radians, char *hms, int len);
|
void r2sDMS(double radians, char *hms, int len);
|
||||||
void d2sDMS(double degrees, char *dms, 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_MJDt(struct timeval *tval, sMJD_t *MJD);
|
||||||
bool get_LST(sMJD_t *mjd, double *LST);
|
bool get_LST(sMJD_t *mjd, double *LST);
|
||||||
bool get_ObsPlace(struct timeval *tval, polarCrds_t *p2000, polarCrds_t *pnow, horizCrds_t *hnow);
|
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);
|
bool setDUT(almDut_t *D);
|
||||||
void getDUT(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);
|
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);
|
||||||
|
|||||||
@@ -50,20 +50,28 @@ static parameters_t Conf = {0};
|
|||||||
#define STR(x) STR_HELPER(x)
|
#define STR(x) STR_HELPER(x)
|
||||||
|
|
||||||
#define OPTIONS(Stor) \
|
#define OPTIONS(Stor) \
|
||||||
{"device", NEED_ARG, NULL, 'd', arg_string, APTR(&Stor.device), "serial device name"}, \
|
{"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"}, \
|
{"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"}, \
|
{"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 ")"}, \
|
{"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 ")"}, \
|
{"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 ")"}, \
|
{"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 ")"}, \
|
{"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)"}, \
|
{"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"}, \
|
{"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)"}, \
|
{"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) ")"}, \
|
{"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) ")"}, \
|
{"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 ")"}, \
|
{"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)"}, \
|
{"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[] = {
|
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){
|
parameters_t *parse_cmdline(int *argc, char ***argv){
|
||||||
sl_parseargs(argc, argv, cmdlnopts);
|
sl_parseargs(argc, argv, cmdlnopts);
|
||||||
if(help) sl_showhelp(-1, cmdlnopts);
|
if(help) sl_showhelp(-1, cmdlnopts);
|
||||||
//if(!conffile) return &G;
|
|
||||||
// fix for conffile
|
// fix for conffile
|
||||||
if(conffile){
|
if(conffile){
|
||||||
if(!sl_conf_readopts(conffile, confopts)){ // show conf help
|
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);
|
DBLCHKD(sertmout, DEFAULT_SERTMOUT);
|
||||||
INTCHKD(serspeed, DEFAULT_SERSPEED);
|
INTCHKD(serspeed, DEFAULT_SERSPEED);
|
||||||
INTCHKD(maxclients, DEFAULT_MAXCLIENTS);
|
INTCHKD(maxclients, DEFAULT_MAXCLIENTS);
|
||||||
|
STRCHK(parkA);
|
||||||
|
STRCHK(parkZ);
|
||||||
|
STRCHK(longitude);
|
||||||
|
STRCHK(latitude);
|
||||||
|
STRCHK(altitude);
|
||||||
|
DBLCHK(DUT1);
|
||||||
|
DBLCHK(polarx);
|
||||||
|
DBLCHK(polary);
|
||||||
return &G;
|
return &G;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,11 @@ typedef struct{
|
|||||||
char *logfile; // logging to this file
|
char *logfile; // logging to this file
|
||||||
char *crdsfile; // file where FITS-header should be written
|
char *crdsfile; // file where FITS-header should be written
|
||||||
char *mountname; // set mount name for FITS-header
|
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 emulation; // run in emulation mode
|
||||||
int verbose; // verbose level
|
int verbose; // verbose level
|
||||||
int sleept; // server's sleeping in main cycle time
|
int sleept; // server's sleeping in main cycle time
|
||||||
@@ -33,6 +38,8 @@ typedef struct{
|
|||||||
int serspeed; // serial speed, baud
|
int serspeed; // serial speed, baud
|
||||||
int maxclients; // max amount of clients connected to one socket
|
int maxclients; // max amount of clients connected to one socket
|
||||||
double sertmout; // serial timeout, s
|
double sertmout; // serial timeout, s
|
||||||
|
double DUT1; // DUT1 value
|
||||||
|
double polarx, polary; // polar coordinates, m
|
||||||
} parameters_t;
|
} parameters_t;
|
||||||
|
|
||||||
parameters_t *parse_cmdline(int *argc, char ***argv);
|
parameters_t *parse_cmdline(int *argc, char ***argv);
|
||||||
|
|||||||
@@ -215,7 +215,9 @@ void emulation_stop(){
|
|||||||
void emul_start_tracking(){
|
void emul_start_tracking(){
|
||||||
if(MNT_S_TRACKING == emulation_status()) return;
|
if(MNT_S_TRACKING == emulation_status()) return;
|
||||||
atomic_store(&emul_status, MNT_S_TRACKING);
|
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(){
|
mount_status_t emulation_status(){
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <erfa.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
@@ -26,10 +27,6 @@
|
|||||||
|
|
||||||
static char *hdname = NULL;
|
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
|
* @brief set_header_name - check ability of witing into file and set given name
|
||||||
* @param name - header filename
|
* @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){
|
void wrhdr(fitsheader_t *HDR){
|
||||||
if(!HDR) return;
|
if(!HDR) return;
|
||||||
static time_t lastcorr = 0; // last time of time/weather corrections sent to mount
|
static time_t lastcorr = 0; // last time of time/weather corrections sent to mount
|
||||||
time_t curtime = time(NULL);
|
double curtime = sl_dtime();
|
||||||
bool haveweather = false;
|
//bool haveweather = false;
|
||||||
bool poweredON = (HDR->status == MNT_S_ERROR || HDR->status == MNT_S_OFF) ? false : true;
|
bool poweredON = (HDR->status == MNT_S_ERROR || HDR->status == MNT_S_OFF) ? false : true;
|
||||||
// weather block
|
// weather block
|
||||||
if(HDR->weather.last_update < (int)(1 + MOUNT_CHECK_T * 2)){ // weather data is good
|
if(time(NULL) - HDR->weather.last_update < (int)(1. + MOUNT_CHECK_T * 10.)){ // weather data is good
|
||||||
if(time(NULL) - lastcorr > CORRECTIONS_TIMEDIFF){ // make correction once per hour
|
if(poweredON && time(NULL) - lastcorr > CORRECTIONS_TIMEDIFF){ // make correction once per hour
|
||||||
if(mount_corrdata(&HDR->weather)) lastcorr = time(NULL);
|
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){
|
if(!hdname){
|
||||||
DBG("hdname not given!");
|
DBG("hdname not given!");
|
||||||
return;
|
return;
|
||||||
@@ -135,32 +132,41 @@ void wrhdr(fitsheader_t *HDR){
|
|||||||
if(pt > ht){ // horcrds is older -> show polar
|
if(pt > ht){ // horcrds is older -> show polar
|
||||||
if(curtime - pt < COORDS_OLD_T){
|
if(curtime - pt < COORDS_OLD_T){
|
||||||
snprintf(val, 22, "%.10f", RAD2DEG(polar.ra));
|
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));
|
snprintf(val, 22, "%.10f", RAD2DEG(polar.dec));
|
||||||
WRHDR("TAGDEC", val, "Target DEC (J2000), degrees");
|
WRHDR("INPDEC", val, "Input DEC, degrees");
|
||||||
}
|
}
|
||||||
}else{ // show horiz
|
}else{ // show horiz
|
||||||
if(curtime - ht < COORDS_OLD_T){
|
if(curtime - ht < COORDS_OLD_T){
|
||||||
snprintf(val, 22, "%.10f", RAD2DEG(horiz.az));
|
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));
|
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){
|
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");
|
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));
|
snprintf(val, 22, "%.10f", RAD2DEG(HDR->polar.dec));
|
||||||
WRHDR("DEC", val, "Telescope declination, current epoch, deg");
|
WRHDR("DEC", val, "Telescope declination, current epoch, deg");
|
||||||
snprintf(val, 22, "%.10f", RAD2DEG(HDR->altaz.az));
|
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));
|
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");
|
WRHDR("TELSTAT", mount_status_str(HDR->status), "Telescope mount status");
|
||||||
double mjd;
|
double mjd;
|
||||||
mount_getInpMJD(&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");
|
WRHDR("INPEQUIN", val, "Equinox of input celestial coordinate system");
|
||||||
if(poweredON){
|
if(poweredON){
|
||||||
snprintf(val, 22, "%.10f", 2000.+(HDR->MJD.MJD - ERFA_DJM00)/365.25); // telescope coordinates: JNOW
|
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)");
|
WRHDR("LONGITUD", val, "Geo longitude of site (east negative)");
|
||||||
snprintf(val, 22, "%.10f", RAD2DEG(HDR->place.slat));
|
snprintf(val, 22, "%.10f", RAD2DEG(HDR->place.slat));
|
||||||
WRHDR("LATITUDE", val, "Geo latitude of site (south negative)");
|
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");
|
WRHDR("LSTEND", val, "Local sidereal time of file creation");
|
||||||
|
/* Weather data is written into separate header-file by weather daemon
|
||||||
if(haveweather){
|
if(haveweather){
|
||||||
snprintf(val, 22, "%.1f", HDR->weather.humidity);
|
snprintf(val, 22, "%.1f", HDR->weather.humidity);
|
||||||
WRHDR("HUMIDITY", val, "Relative humidity, %%");
|
WRHDR("HUMIDITY", val, "Relative humidity, %%");
|
||||||
@@ -192,9 +199,9 @@ void wrhdr(fitsheader_t *HDR){
|
|||||||
WRHDR("WINDSPD", val, "Wind speed (m/s)");
|
WRHDR("WINDSPD", val, "Wind speed (m/s)");
|
||||||
snprintf(val, 22, "%.1f", HDR->weather.windmax);
|
snprintf(val, 22, "%.1f", HDR->weather.windmax);
|
||||||
WRHDR("WINDMAX", val, "Last hour maximal wind speed (m/s)");
|
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("WEATTIME", val, "Unix time of weather measurements");
|
||||||
}
|
}*/
|
||||||
// WRHDR("", , "");
|
// WRHDR("", , "");
|
||||||
#undef WRHDR
|
#undef WRHDR
|
||||||
returning:
|
returning:
|
||||||
|
|||||||
@@ -27,6 +27,10 @@
|
|||||||
// status checking interval and writing FITS-header, seconds
|
// status checking interval and writing FITS-header, seconds
|
||||||
#define MOUNT_CHECK_T 0.5
|
#define MOUNT_CHECK_T 0.5
|
||||||
|
|
||||||
|
#ifndef FLT_EPSILON
|
||||||
|
#define FLT_EPSILON 1e-6
|
||||||
|
#endif
|
||||||
|
|
||||||
typedef struct{
|
typedef struct{
|
||||||
weather_data_t weather;
|
weather_data_t weather;
|
||||||
mount_status_t status;
|
mount_status_t status;
|
||||||
|
|||||||
@@ -28,6 +28,7 @@
|
|||||||
#include "fitshdr.h"
|
#include "fitshdr.h"
|
||||||
#include "server.h"
|
#include "server.h"
|
||||||
#include "mount.h"
|
#include "mount.h"
|
||||||
|
#include "stellarium.h"
|
||||||
|
|
||||||
static pid_t childpid = -1; // PID of child process
|
static pid_t childpid = -1; // PID of child process
|
||||||
static parameters_t *G = NULL;
|
static parameters_t *G = NULL;
|
||||||
@@ -43,9 +44,11 @@ void signals(int sig){
|
|||||||
}
|
}
|
||||||
if(childpid > 0){ // parent process
|
if(childpid > 0){ // parent process
|
||||||
LOGERR("PID %d exit with status %d", getpid(), sig);
|
LOGERR("PID %d exit with status %d", getpid(), sig);
|
||||||
|
isrunning = false;
|
||||||
}else{
|
}else{
|
||||||
LOGWARN("Child %d died with %d", getpid(), sig);
|
LOGWARN("Child %d died with %d", getpid(), sig);
|
||||||
server_stop();
|
server_stop();
|
||||||
|
stellarium_stop();
|
||||||
}
|
}
|
||||||
DBG("EXIT");
|
DBG("EXIT");
|
||||||
}
|
}
|
||||||
@@ -75,11 +78,33 @@ int main(int argc, char **argv){
|
|||||||
WARNX("Can't set mount name to %s", G->mountname);
|
WARNX("Can't set mount name to %s", G->mountname);
|
||||||
return 5;
|
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(SIGTERM, signals);
|
||||||
signal(SIGINT, signals);
|
signal(SIGINT, signals);
|
||||||
signal(SIGQUIT, signals);
|
signal(SIGQUIT, signals);
|
||||||
signal(SIGTSTP, SIG_IGN);
|
signal(SIGTSTP, SIG_IGN);
|
||||||
signal(SIGHUP, signals);
|
signal(SIGHUP, SIG_IGN);
|
||||||
|
//signal(SIGHUP, signals); // re-read config file
|
||||||
sl_check4running((char*)__progname, G->pidfile);
|
sl_check4running((char*)__progname, G->pidfile);
|
||||||
if(G->logfile) OPENLOG(G->logfile, lvl, 1);
|
if(G->logfile) OPENLOG(G->logfile, lvl, 1);
|
||||||
DBG("Started");
|
DBG("Started");
|
||||||
@@ -108,6 +133,7 @@ int main(int argc, char **argv){
|
|||||||
break; // go out to normal functional
|
break; // go out to normal functional
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if(!isrunning) return 0;
|
||||||
#endif
|
#endif
|
||||||
server_sock_t sockt = {
|
server_sock_t sockt = {
|
||||||
.cmd_isunix = G->isunix,
|
.cmd_isunix = G->isunix,
|
||||||
|
|||||||
@@ -42,9 +42,12 @@ static sl_tty_t *mount_dev = NULL;
|
|||||||
static pthread_mutex_t mntdev_mutex = PTHREAD_MUTEX_INITIALIZER;
|
static pthread_mutex_t mntdev_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||||
// status
|
// status
|
||||||
static atomic_int mountstatus = MNT_S_ERROR;
|
static atomic_int mountstatus = MNT_S_ERROR;
|
||||||
|
|
||||||
// parking coordinates
|
// 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
|
// input and current target coordinates
|
||||||
static polarCrds_t InpCoords = {0}, // input as user give (for epoch InpMJD)
|
static polarCrds_t InpCoords = {0}, // input as user give (for epoch InpMJD)
|
||||||
TagCoords = {0}; // target for Jnow after command "point to input"
|
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)
|
* @return false if `ha` isn't in [0,24)
|
||||||
*/
|
*/
|
||||||
bool mount_setInpHA(double ha){
|
bool mount_setInpHA(double ha){
|
||||||
|
if(ha < 0) ha += 24.;
|
||||||
if(ha < 0. || ha >= 24.) return false;
|
if(ha < 0. || ha >= 24.) return false;
|
||||||
InpCoords.ha = HRS2RAD(ha);
|
InpCoords.ha = HRS2RAD(ha);
|
||||||
InpCTime = sl_dtime();
|
InpCTime = sl_dtime();
|
||||||
@@ -228,7 +232,7 @@ static bool send_cmd_resp(const char *cmd, char *resp, size_t resplen){
|
|||||||
size_t pos = 0;
|
size_t pos = 0;
|
||||||
double t0 = sl_dtime();
|
double t0 = sl_dtime();
|
||||||
bool gotEOM = false;
|
bool gotEOM = false;
|
||||||
while(sl_dtime() - t0 < sertmout){
|
while(sl_dtime() - t0 < sertmout && !gotEOM){
|
||||||
int got = sl_tty_read(mount_dev);
|
int got = sl_tty_read(mount_dev);
|
||||||
if(got < 0){
|
if(got < 0){
|
||||||
WARN("sl_tty_read()");
|
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 == '\r' || c == '\n') continue; // WTF?
|
||||||
if(c == '#'){ // end of message
|
if(c == '#'){ // end of message
|
||||||
gotEOM = true;
|
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;
|
break;
|
||||||
}
|
}
|
||||||
if(resp && pos + 1 < resplen) resp[pos++] = c;
|
if(resp && pos + 1 < resplen) resp[pos++] = c;
|
||||||
@@ -340,7 +341,7 @@ static bool guess_speed(){
|
|||||||
if(!mount_dev) return false;
|
if(!mount_dev) return false;
|
||||||
close(mount_dev->comfd);
|
close(mount_dev->comfd);
|
||||||
#define SPDBUFSZ 7
|
#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;
|
int idx = 0;
|
||||||
for(; idx < SPDBUFSZ && isrunning; ++idx){
|
for(; idx < SPDBUFSZ && isrunning; ++idx){
|
||||||
DBG("try speed %d", speeds[idx]);
|
DBG("try speed %d", speeds[idx]);
|
||||||
@@ -382,8 +383,10 @@ bool mount_connect(){
|
|||||||
snprintf(buf, 63, CMD_SETMINALT, 10);
|
snprintf(buf, 63, CMD_SETMINALT, 10);
|
||||||
if(!write_cmd(buf, true)) ret = false; // set minimum altitude to 10 degrees
|
if(!write_cmd(buf, true)) ret = false; // set minimum altitude to 10 degrees
|
||||||
pthread_mutex_unlock(&mntdev_mutex);
|
pthread_mutex_unlock(&mntdev_mutex);
|
||||||
if(ret) LOGMSG("Connected to %s@115200", mount_dev->portname);
|
if(ret){
|
||||||
else LOGERR("Can't write commands to mount");
|
LOGMSG("Connected to %s@115200", mount_dev->portname);
|
||||||
|
atomic_store(&mountstatus, MNT_S_STOPPED);
|
||||||
|
}else LOGERR("Can't write commands to mount");
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -392,7 +395,9 @@ void mount_disconnect(){
|
|||||||
emulation_stop();
|
emulation_stop();
|
||||||
return;
|
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);
|
if(mount_dev) close(mount_dev->comfd);
|
||||||
pthread_mutex_unlock(&mntdev_mutex);
|
pthread_mutex_unlock(&mntdev_mutex);
|
||||||
}
|
}
|
||||||
@@ -427,7 +432,7 @@ bool mount_point(double ra, double dec){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
ret = true;
|
ret = true;
|
||||||
TagCoords.ra = DEG2RAD(ra);
|
TagCoords.ra = HRS2RAD(ra);
|
||||||
TagCoords.dec = DEG2RAD(dec);
|
TagCoords.dec = DEG2RAD(dec);
|
||||||
TagTime = sl_dtime();
|
TagTime = sl_dtime();
|
||||||
retn:
|
retn:
|
||||||
@@ -461,7 +466,7 @@ bool mount_pointAZ(double A, double Z){
|
|||||||
if(!write_cmd(cmd, true)) goto retn;
|
if(!write_cmd(cmd, true)) goto retn;
|
||||||
snprintf(cmd, 127, CMD_SETZD, azstr);
|
snprintf(cmd, 127, CMD_SETZD, azstr);
|
||||||
if(!write_cmd(cmd, true)) goto retn;
|
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'){
|
if(*cmd != '0'){
|
||||||
WARNX("Goto error, answer: %s", cmd);
|
WARNX("Goto error, answer: %s", cmd);
|
||||||
LOGWARN("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(!ra || !dec) return false;
|
||||||
if(isemulated){
|
if(isemulated){
|
||||||
get_emul_coords(ra, dec);
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
if(!mount_dev) return false;
|
if(!mount_dev) return false;
|
||||||
@@ -509,7 +516,7 @@ bool mount_getaz(double *a, double *z){
|
|||||||
double LST;
|
double LST;
|
||||||
get_LST(NULL, &LST);
|
get_LST(NULL, &LST);
|
||||||
eq2hor(&P, &H, LST);
|
eq2hor(&P, &H, LST);
|
||||||
*a = H.az; *z = H.zd;
|
*a = RAD2DEG(H.az); *z = RAD2DEG(H.zd);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if(!mount_dev) return false;
|
if(!mount_dev) return false;
|
||||||
@@ -582,7 +589,9 @@ bool mount_tracking_start(){
|
|||||||
*/
|
*/
|
||||||
bool mount_park(){
|
bool mount_park(){
|
||||||
if(isemulated) return pointAZ_emulation(ParkCoords.az, ParkCoords.zd);
|
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));
|
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);
|
pthread_mutex_unlock(&mntdev_mutex);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief mount_shutdown - turn off mount
|
||||||
|
* @return false if failed
|
||||||
|
*/
|
||||||
|
bool mount_shutdown(){
|
||||||
|
return write_cmd(CMD_SHUTDOWN, true);
|
||||||
|
}
|
||||||
|
|||||||
@@ -88,3 +88,5 @@ bool mount_park();
|
|||||||
bool mount_setParkAz(double az);
|
bool mount_setParkAz(double az);
|
||||||
bool mount_setParkZD(double zd);
|
bool mount_setParkZD(double zd);
|
||||||
void mount_getPark(horizCrds_t *c);
|
void mount_getPark(horizCrds_t *c);
|
||||||
|
|
||||||
|
bool mount_shutdown();
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!DOCTYPE QtCreatorProject>
|
<!DOCTYPE QtCreatorProject>
|
||||||
<!-- Written by QtCreator 20.0.1, 2026-09-22T14:24:31. -->
|
<!-- Written by QtCreator 20.0.1, 2026-09-28T09:01:11. -->
|
||||||
<qtcreator>
|
<qtcreator>
|
||||||
<data>
|
<data>
|
||||||
<variable>EnvironmentId</variable>
|
<variable>EnvironmentId</variable>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
10micron_commands.h
|
10micron_commands.h
|
||||||
|
Readme.md
|
||||||
angles.c
|
angles.c
|
||||||
angles.h
|
angles.h
|
||||||
args.c
|
args.c
|
||||||
|
|||||||
@@ -36,26 +36,34 @@
|
|||||||
#define STATUS_MAX_AGE (30.)
|
#define STATUS_MAX_AGE (30.)
|
||||||
|
|
||||||
// commands
|
// commands
|
||||||
#define CMD_UNIXT "unixt"
|
#define CMD_DUT1 "dut1"
|
||||||
#define CMD_STATUS "status"
|
#define CMD_GOTOAZ "gotoaz"
|
||||||
#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_GOTORD "gotord"
|
#define CMD_GOTORD "gotord"
|
||||||
#define CMD_GOTORH "gotorh"
|
#define CMD_GOTORH "gotorh"
|
||||||
#define CMD_GOTOAZ "gotoaz"
|
#define CMD_LST "lst"
|
||||||
#define CMD_STOPTRK "stoptrk"
|
|
||||||
#define CMD_TRACK "track"
|
|
||||||
#define CMD_PARK "park"
|
|
||||||
#define CMD_PARKAZ "parkaz"
|
#define CMD_PARKAZ "parkaz"
|
||||||
|
#define CMD_PARK "park"
|
||||||
#define CMD_PARKZD "parkzd"
|
#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
|
// main command socket
|
||||||
@@ -65,7 +73,7 @@ static int stellarium_sockfd = -1;
|
|||||||
// sleep time (us)
|
// sleep time (us)
|
||||||
static unsigned int sleept = DEFAULT_SLEEP_T;
|
static unsigned int sleept = DEFAULT_SLEEP_T;
|
||||||
// running flag
|
// running flag
|
||||||
volatile bool isrunning = false;
|
volatile bool isrunning = true;
|
||||||
// lost weather flag
|
// lost weather flag
|
||||||
static bool weatherlost = false;
|
static bool weatherlost = false;
|
||||||
|
|
||||||
@@ -102,9 +110,7 @@ static int parse_key_value(const char *req, double *val){
|
|||||||
if(!req) return 0; // is getter
|
if(!req) return 0; // is getter
|
||||||
DBG("parsing of %s", req);
|
DBG("parsing of %s", req);
|
||||||
double d;
|
double d;
|
||||||
if(strchr(req, ':')){ // DD:MM:SS or HH:MM:SS
|
if(!str2coord(req, &d)) return -1;
|
||||||
if(!str2coord(req, &d)) return -1;
|
|
||||||
}else if(sscanf(req, "%lf", &d) != 1) return -1; // error
|
|
||||||
if(val) *val = d;
|
if(val) *val = d;
|
||||||
return 1; // is setter
|
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);
|
int res = parse_key_value(req, &val);
|
||||||
if(res < 0) return RESULT_BADVAL;
|
if(res < 0) return RESULT_BADVAL;
|
||||||
if(res > 0){ // setter
|
if(res > 0){ // setter
|
||||||
|
LOGDBG("Client %d set tagra: %g", c->fd, val);
|
||||||
if(mount_setInpRA(val)) return RESULT_OK;
|
if(mount_setInpRA(val)) return RESULT_OK;
|
||||||
return RESULT_BADVAL;
|
return RESULT_BADVAL;
|
||||||
}else{ // getter
|
}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);
|
int res = parse_key_value(req, &val);
|
||||||
if(res < 0) return RESULT_BADVAL;
|
if(res < 0) return RESULT_BADVAL;
|
||||||
if(res > 0){
|
if(res > 0){
|
||||||
|
LOGDBG("Client %d set tagdec: %g", c->fd, val);
|
||||||
if(mount_setInpDec(val)) return RESULT_OK;
|
if(mount_setInpDec(val)) return RESULT_OK;
|
||||||
return RESULT_BADVAL;
|
return RESULT_BADVAL;
|
||||||
}else{
|
}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);
|
int res = parse_key_value(req, &val);
|
||||||
if(res < 0) return RESULT_BADVAL;
|
if(res < 0) return RESULT_BADVAL;
|
||||||
if(res > 0){
|
if(res > 0){
|
||||||
|
LOGDBG("Client %d set tagha: %g", c->fd, val);
|
||||||
if(mount_setInpHA(val)) return RESULT_OK;
|
if(mount_setInpHA(val)) return RESULT_OK;
|
||||||
return RESULT_BADVAL;
|
return RESULT_BADVAL;
|
||||||
}else{
|
}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);
|
int res = parse_key_value(req, &val);
|
||||||
if(res < 0) return RESULT_BADVAL;
|
if(res < 0) return RESULT_BADVAL;
|
||||||
if(res > 0){
|
if(res > 0){
|
||||||
|
LOGDBG("Client %d set tagaz: %g", c->fd, val);
|
||||||
if(mount_setInpA(val)) return RESULT_OK;
|
if(mount_setInpA(val)) return RESULT_OK;
|
||||||
return RESULT_BADVAL;
|
return RESULT_BADVAL;
|
||||||
}else{
|
}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);
|
int res = parse_key_value(req, &val);
|
||||||
if(res < 0) return RESULT_BADVAL;
|
if(res < 0) return RESULT_BADVAL;
|
||||||
if(res > 0){
|
if(res > 0){
|
||||||
|
LOGDBG("Client %d set tagzd: %g", c->fd, val);
|
||||||
if(mount_setInpZ(val)) return RESULT_OK;
|
if(mount_setInpZ(val)) return RESULT_OK;
|
||||||
return RESULT_BADVAL;
|
return RESULT_BADVAL;
|
||||||
}else{
|
}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;
|
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){
|
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;
|
if(weatherlost || HDR.status == MNT_S_ERROR) return RESULT_FAIL;
|
||||||
char buf[64];
|
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;
|
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){
|
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;
|
if(weatherlost || HDR.status == MNT_S_ERROR) return RESULT_FAIL;
|
||||||
char buf[64];
|
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){
|
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;
|
if(weatherlost || HDR.status == MNT_S_ERROR) return RESULT_FAIL;
|
||||||
polarCrds_t p;
|
polarCrds_t px, pnow;
|
||||||
mount_getInpCoords(&p);
|
mount_getInpCoords(&px);
|
||||||
double ra_h = RAD2HRS(p.ra);
|
double Epoch;
|
||||||
double dec_d = RAD2DEG(p.dec);
|
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;
|
if(!mount_point(ra_h, dec_d)) return RESULT_FAIL;
|
||||||
return RESULT_OK;
|
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_rad = eraAnp(LST - p.ha);
|
||||||
double ra_h = RAD2HRS(RA_rad);
|
double ra_h = RAD2HRS(RA_rad);
|
||||||
double dec_d = RAD2DEG(p.dec);
|
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;
|
if(!mount_point(ra_h, dec_d)) return RESULT_FAIL;
|
||||||
return RESULT_OK;
|
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;
|
if(weatherlost || HDR.status == MNT_S_ERROR) return RESULT_FAIL;
|
||||||
horizCrds_t h;
|
horizCrds_t h;
|
||||||
mount_getInpHor(&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;
|
if(mount_pointAZ(RAD2DEG(h.az), RAD2DEG(h.zd))) return RESULT_OK;
|
||||||
return RESULT_FAIL;
|
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){
|
||||||
|
LOGDBG("Client %d asks to stop", c->fd);
|
||||||
if(mount_stop()) return RESULT_OK;
|
if(mount_stop()) return RESULT_OK;
|
||||||
return RESULT_FAIL;
|
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){
|
||||||
|
LOGDBG("Client %d asks to stop tracking", c->fd);
|
||||||
if(mount_tracking_stop()) return RESULT_OK;
|
if(mount_tracking_stop()) return RESULT_OK;
|
||||||
return RESULT_FAIL;
|
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
|
// 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(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;
|
if(mount_tracking_start()) return RESULT_OK;
|
||||||
return RESULT_FAIL;
|
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){
|
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;
|
if(mount_park()) return RESULT_OK;
|
||||||
return RESULT_FAIL;
|
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);
|
int res = parse_key_value(req, &val);
|
||||||
if(res < 0) return RESULT_BADVAL;
|
if(res < 0) return RESULT_BADVAL;
|
||||||
if(res > 0){
|
if(res > 0){
|
||||||
|
LOGDBG("Client %d set parking Az to %g", c->fd, val);
|
||||||
if(mount_setParkAz(val)) return RESULT_OK;
|
if(mount_setParkAz(val)) return RESULT_OK;
|
||||||
return RESULT_BADVAL;
|
return RESULT_BADVAL;
|
||||||
}else{
|
}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);
|
int res = parse_key_value(req, &val);
|
||||||
if(res < 0) return RESULT_BADVAL;
|
if(res < 0) return RESULT_BADVAL;
|
||||||
if(res > 0){
|
if(res > 0){
|
||||||
|
LOGDBG("Client %d set parking ZD to %g", c->fd, val);
|
||||||
if(mount_setParkZD(val)) return RESULT_OK;
|
if(mount_setParkZD(val)) return RESULT_OK;
|
||||||
return RESULT_BADVAL;
|
return RESULT_BADVAL;
|
||||||
}else{
|
}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;
|
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
|
// and all handlers collection
|
||||||
static sl_sock_hitem_t handlers[] = {
|
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_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_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_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_park, CMD_PARK, "park telescope", NULL},
|
||||||
{cmd_parkaz, CMD_PARKAZ, "set parking azimuth (Deg: 0 - north, 90 - east)", 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_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},
|
{status, CMD_STATUS, "get mount status", NULL},
|
||||||
{cmd_stop, CMD_STOP, "stop telescope", 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_tagaz, CMD_TAGAZ, "get/set target azimuth (Deg)", NULL},
|
||||||
{cmd_tagdec, CMD_TAGDEC, "get/set target declination (Deg)", NULL},
|
{cmd_tagdec, CMD_TAGDEC, "get/set target declination (Deg)", NULL},
|
||||||
{cmd_tagha, CMD_TAGHA, "get/set target hour angle (Hrs)", 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_tagra, CMD_TAGRA, "get/set target right acsention (Hrs)", NULL},
|
||||||
{cmd_tagzd, CMD_TAGZD, "get/set target zenith distance (Deg)", NULL},
|
{cmd_tagzd, CMD_TAGZD, "get/set target zenith distance (Deg)", NULL},
|
||||||
{cmd_teldec, CMD_TELDEC, "get current telescope declination (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_telra, CMD_TELRA, "get current telescope right acsention (Hrs)", NULL},
|
||||||
{cmd_telaz, CMD_TELAZ, "get current telescope azimuth (Deg)", NULL},
|
{cmd_telaz, CMD_TELAZ, "get current telescope azimuth (Deg)", NULL},
|
||||||
{cmd_telzd, CMD_TELZD, "get current telescope zenith distance (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},
|
{cmd_track, CMD_TRACK, "start tracking from current position", NULL},
|
||||||
{dtimeh, CMD_UNIXT, "get server's UNIX time", NULL},
|
{dtimeh, CMD_UNIXT, "get server's UNIX time", NULL},
|
||||||
{NULL, NULL, NULL, NULL}
|
{NULL, NULL, NULL, NULL}
|
||||||
@@ -415,10 +578,6 @@ bool server_check(server_sock_t *sockt){
|
|||||||
}
|
}
|
||||||
|
|
||||||
void server_run(){
|
void server_run(){
|
||||||
if(isrunning){
|
|
||||||
LOGERR("server_run(): still running!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if(stellarium_sockfd == -1 || !cmd_socket){
|
if(stellarium_sockfd == -1 || !cmd_socket){
|
||||||
LOGERR("server_run(): not initialized");
|
LOGERR("server_run(): not initialized");
|
||||||
if(cmd_socket) sl_sock_delete(&cmd_socket);
|
if(cmd_socket) sl_sock_delete(&cmd_socket);
|
||||||
@@ -455,26 +614,29 @@ void server_run(){
|
|||||||
}
|
}
|
||||||
// collect data for other fields
|
// collect data for other fields
|
||||||
HDR.status = mount_status();
|
HDR.status = mount_status();
|
||||||
get_MJDt(NULL, &HDR.MJD);
|
|
||||||
get_LST(&HDR.MJD, &HDR.sidtime);
|
|
||||||
if(HDR.status != MNT_S_OFF){
|
if(HDR.status != MNT_S_OFF){
|
||||||
double deg1, deg2;
|
double deg1, deg2;
|
||||||
if(mount_getcoords(°1, °2)){
|
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);
|
HDR.polar.dec = DEG2RAD(deg2);
|
||||||
}
|
}
|
||||||
if(mount_getaz(°1, °2)){
|
if(mount_getaz(°1, °2)){
|
||||||
HDR.altaz.az = DEG2RAD(deg1);
|
HDR.altaz.az = DEG2RAD(deg1);
|
||||||
HDR.altaz.zd = DEG2RAD(deg2);
|
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);
|
getDUT(&HDR.dut);
|
||||||
if(HDR.status == MNT_S_OFF || !mount_getpierside(HDR.pierside, MAX_HDR_STRLEN)) *HDR.pierside = 0;
|
if(HDR.status == MNT_S_OFF || !mount_getpierside(HDR.pierside, MAX_HDR_STRLEN)) *HDR.pierside = 0;
|
||||||
// and write FITS-header
|
// and write FITS-header
|
||||||
|
|||||||
@@ -32,6 +32,9 @@
|
|||||||
// max weather lost time
|
// max weather lost time
|
||||||
#define MOUNT_WEATHER_ALRM 900.
|
#define MOUNT_WEATHER_ALRM 900.
|
||||||
|
|
||||||
|
// shutdown key refresh period (5min)
|
||||||
|
#define SHTDWN_KEY_TMOUT 300
|
||||||
|
|
||||||
typedef struct{
|
typedef struct{
|
||||||
int cmd_isunix; // UNIX-socket instead of INET for `cmdnode`
|
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
|
const char *stellport; // port of stellarium server; could be "localhost:port" for local-only work
|
||||||
|
|||||||
@@ -32,7 +32,7 @@
|
|||||||
#define BUFLEN 256
|
#define BUFLEN 256
|
||||||
|
|
||||||
// running flag
|
// running flag
|
||||||
static volatile bool isrunning = false;
|
extern volatile bool isrunning;
|
||||||
static pthread_t mainthread;
|
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
|
//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
|
* @param sockfd - socket fd for sending data
|
||||||
* @return false if client disconnected
|
* @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);
|
ssize_t sent = send(sockfd, data, dlen, MSG_NOSIGNAL);
|
||||||
if(sent != (ssize_t)dlen){
|
if(sent != (ssize_t)dlen){
|
||||||
if(sent == -1 && errno != EINTR){
|
if(sent == -1 && errno != EINTR){
|
||||||
@@ -168,10 +168,19 @@ static void *handle_socket(void *sockd){
|
|||||||
sleep(1);
|
sleep(1);
|
||||||
continue;
|
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);
|
//DBG("got : %g/%g", RA, Decl);
|
||||||
dout.ra = htole32(RAD2RA(RA));
|
dout.ra = htole32(RAD2RA(J2000.ra));
|
||||||
dout.dec = (int32_t)htole32(RAD2DEC(Decl));
|
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(!send_data((uint8_t*)&dout, sizeof(outdata), sock)) break;
|
||||||
if(!sl_canread(sock)){
|
if(!sl_canread(sock)){
|
||||||
sleep(1);
|
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
|
// Main loop thread: wait connections over socket and create one thread for each
|
||||||
static void* start(void *F){
|
static void* start(void *F){
|
||||||
if(isrunning){
|
|
||||||
WARNX("already running");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
if(!F){
|
if(!F){
|
||||||
WARNX("start(): No arg");
|
WARNX("start(): No arg");
|
||||||
return NULL;
|
return NULL;
|
||||||
@@ -213,7 +218,6 @@ static void* start(void *F){
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
DBG("Start main loop, sockfd=%d", sockfd);
|
DBG("Start main loop, sockfd=%d", sockfd);
|
||||||
isrunning = true;
|
|
||||||
// Main loop
|
// Main loop
|
||||||
while(isrunning){
|
while(isrunning){
|
||||||
socklen_t size = sizeof(struct sockaddr_in);
|
socklen_t size = sizeof(struct sockaddr_in);
|
||||||
@@ -271,11 +275,13 @@ bool stellarium_start(int sockfd){
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
DBG("Stellarium server started");
|
DBG("Stellarium server started");
|
||||||
|
LOGDBG("Stellarium server started");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void stellarium_stop(){
|
void stellarium_stop(){
|
||||||
if(!isrunning) return;
|
if(isrunning) isrunning = false;
|
||||||
isrunning = false;
|
/*DBG("Wait for stellarium thread");
|
||||||
pthread_join(mainthread, NULL);
|
pthread_join(mainthread, NULL);
|
||||||
|
LOGDBG("Stellarium thread died");*/
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user