mirror of
https://github.com/eddyem/small_tel.git
synced 2026-09-29 21:30:41 +03:00
fix some bugs
This commit is contained in:
@@ -19,7 +19,7 @@
|
||||
#pragma once
|
||||
|
||||
// commands
|
||||
#define CMD_SHUTDOWN ":shutdown#" // TODO!
|
||||
#define CMD_SHUTDOWN ":shutdown#"
|
||||
#define CMD_TRKSTART ":AP#"
|
||||
#define CMD_TRKSTOP ":AL#"
|
||||
#define CMD_GOTOAZ ":MA#"
|
||||
|
||||
@@ -4,7 +4,7 @@ set(PROJ mountdaemon_10micron)
|
||||
project(${PROJ})
|
||||
|
||||
set(MAJOR_VERSION "0")
|
||||
set(MID_VERSION "1")
|
||||
set(MID_VERSION "2")
|
||||
set(MINOR_VERSION "1")
|
||||
set(VERSION "${MAJOR_VERSION}.${MID_VERSION}.${MINOR_VERSION}")
|
||||
|
||||
@@ -36,7 +36,7 @@ aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR} SOURCES)
|
||||
|
||||
###### pkgconfig ######
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(MODULES REQUIRED usefull_macros>=0.3.2 erfa>=2.0.0)
|
||||
pkg_check_modules(MODULES REQUIRED usefull_macros>=0.3.5 erfa>=2.0.0)
|
||||
|
||||
# change wrong behaviour with install prefix
|
||||
if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT AND CMAKE_INSTALL_PREFIX MATCHES "/usr/local")
|
||||
@@ -57,7 +57,7 @@ target_link_libraries(${PROJ} ${MODULES_LIBRARIES} -lweather -lm)
|
||||
# -D
|
||||
add_definitions(-D_XOPEN_SOURCE=1234 -D_DEFAULT_SOURCE -D_GNU_SOURCE
|
||||
-DPACKAGE_VERSION=\"${VERSION}\" -DMINOR_VERSION=\"${MINOR_VERSION}\"
|
||||
-DMID_VERSION=\"${MID_VERSION}\" -DMAJOR_VERSION=\"${MAJOR_VESION}\"
|
||||
-DMID_VERSION=\"${MID_VERSION}\" -DMAJOR_VERSION=\"${MAJOR_VERSION}\"
|
||||
)
|
||||
|
||||
# Installation of the program
|
||||
|
||||
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
|
||||
* @param longitude - degrees, (-180, 180], minus to west
|
||||
* @param latitude - degrees, [-90, 90], minus to south
|
||||
* @param longitude - radians, (-180, 180], minus to west
|
||||
* @param latitude - radians, [-90, 90], minus to south
|
||||
* @param altitude - meters
|
||||
* @return false if some of data wrong
|
||||
*/
|
||||
bool setPlaceData(double longitude, double latitude, double altitude){
|
||||
if(longitude <= -180. || longitude > 180.){
|
||||
WARNX("setPlaceData(): bad longitude (%g)", longitude);
|
||||
bool setPlaceData(placeData_t *pd){
|
||||
if(!pd) return false;
|
||||
if(pd->slong <= -M_PI || pd->slong > M_PI){
|
||||
WARNX("setPlaceData(): bad longitude (%g)", pd->slong);
|
||||
return false;
|
||||
}
|
||||
if(latitude < -90. || latitude > 90.){
|
||||
WARNX("setPlaceData(): bad latitude (%g)", latitude);
|
||||
if(pd->slat < -M_PI_2 || pd->slat > M_PI_2){
|
||||
WARNX("setPlaceData(): bad latitude (%g)", pd->slat);
|
||||
return false;
|
||||
}
|
||||
if(altitude < -500. || altitude > 9000.){
|
||||
WARNX("setPlaceData(): bad altitude (%g)", altitude);
|
||||
if(pd->salt < -500. || pd->salt > 9000.){
|
||||
WARNX("setPlaceData(): bad altitude (%g)", pd->salt);
|
||||
return false;
|
||||
}
|
||||
place.salt = altitude;
|
||||
place.slat = DEG2RAD(latitude);
|
||||
place.slong = DEG2RAD(longitude);
|
||||
place = *pd;
|
||||
DBG("Change place data: alt=%gm, lat=%gdeg, long=%gdeg", place.salt, RAD2DEG(place.slat), RAD2DEG(place.slong));
|
||||
return true;
|
||||
}
|
||||
@@ -94,7 +93,7 @@ char *ra2str(double ra, char buf[RADEC_STR_MAXLEN]){
|
||||
ra -= h; ra *= 60.;
|
||||
int m = (int)ra;
|
||||
ra -= m; ra *= 60.;
|
||||
snprintf(buf, RADEC_STR_MAXLEN, "%d:%d:%.2f", h,m,ra);
|
||||
snprintf(buf, RADEC_STR_MAXLEN, "%d:%02d:%05.2f", h,m,ra);
|
||||
buf[RADEC_STR_MAXLEN-1] = 0;
|
||||
return buf;
|
||||
}
|
||||
@@ -114,7 +113,7 @@ char *dec2str(double dec, char buf[RADEC_STR_MAXLEN]){
|
||||
dec -= d; dec *= 60.;
|
||||
int dm = (int)dec;
|
||||
dec -= dm; dec *= 60.;
|
||||
snprintf(buf, RADEC_STR_MAXLEN, "%c%d:%d:%.1f",sign,d,dm,dec);
|
||||
snprintf(buf, RADEC_STR_MAXLEN, "%c%d:%02d:%04.1f",sign,d,dm,dec);
|
||||
buf[RADEC_STR_MAXLEN-1] = 0;
|
||||
return buf;
|
||||
}
|
||||
@@ -128,12 +127,12 @@ bool normAZ(double *a, double *z){
|
||||
}
|
||||
if(z){
|
||||
norm_angle180(z);
|
||||
if(*z > 90. || *z < -90.) return false;
|
||||
if(*z < 0.){
|
||||
if(*z > 90. || *z < 0.) return false;
|
||||
/*if(*z < 0.){
|
||||
*z += 90.;
|
||||
*a += 180.;
|
||||
if(*a >= 360.) *a -= 360.;
|
||||
}
|
||||
}*/
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -287,11 +286,14 @@ bool str2coord(const char *str, double *val){
|
||||
++str;
|
||||
}
|
||||
int n = sscanf(str, "%d:%d:%f#", &d, &m, &s);
|
||||
double ang = 0.;
|
||||
if(n != 3){
|
||||
DBG("sscanf('%s')=%d", str, 3);
|
||||
return false;
|
||||
}
|
||||
double ang = d + ((double)m)/60. + s/3600.;
|
||||
DBG("sscanf('%s')=%d; try to read double", str, 3);
|
||||
if(!sl_str2d(&ang, str)){
|
||||
WARNX("Wrong number %s", str);
|
||||
return false;
|
||||
}
|
||||
}else ang = d + ((double)m)/60. + s/3600.;
|
||||
if(sign == -1) *val = -ang;
|
||||
else *val = ang;
|
||||
return true;
|
||||
@@ -443,69 +445,47 @@ bool get_ObsPlace(struct timeval *tval, polarCrds_t *p2000, polarCrds_t *pnow, h
|
||||
bool JnowtoJ2000(const polarCrds_t *in, polarCrds_t *out){
|
||||
bool ret = false;
|
||||
DBG("appRa: %gdegr, appDecl: %gdegr", RAD2DEG(in->ra), RAD2DEG(in->dec));
|
||||
#define ERFA(f, ...) do{if(f(__VA_ARGS__)){WARNX("Error in " #f); goto rtn;}}while(0)
|
||||
//#define ERFA(f, ...) do{if(f(__VA_ARGS__)){WARNX("Error in " #f); goto rtn;}}while(0)
|
||||
sMJD_t MJD;
|
||||
if(!get_MJDt(NULL, &MJD)) return false;
|
||||
double ri = 0., di = 0., eo = 0.;
|
||||
eraAtic13(in->ra, in->dec, MJD.tt1, MJD.tt2, &ri, &di, &eo);
|
||||
if(out){
|
||||
out->ha = 0.; out->eo = 0.;
|
||||
out->ra = eraAnp(ri + eo);
|
||||
//out->ra = eraAnp(ri - eo);
|
||||
out->ra = ri;
|
||||
out->dec = di;
|
||||
DBG("JnowtoJ2000: ra=%gdeg, dec=%gdeg", RAD2DEG(out->ra), RAD2DEG(out->dec));
|
||||
}
|
||||
ret = true;
|
||||
#undef ERFA
|
||||
//#undef ERFA
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief J2000toJnow - convert ra/dec between epochs
|
||||
* @param in - J2000 (degrees)
|
||||
* @brief JXtoJnow - convert ra/dec between epochs
|
||||
* @param in - coordinates for Jx (degrees)
|
||||
* @param out - Jnow (degrees)
|
||||
* @param Jx - Epoch for `in`
|
||||
* @return false if failed
|
||||
*/
|
||||
bool J2000toJnow(const polarCrds_t *in, polarCrds_t *out){
|
||||
DBG("J200RA: %gdegr, J200Dec: %gdegr", RAD2DEG(in->ra), RAD2DEG(in->dec));
|
||||
sMJD_t MJD;
|
||||
bool JXtoJnow(const polarCrds_t *in, polarCrds_t *out, double Jx){
|
||||
DBG("JXRA: %ghrs, JXDec: %gdegr; epoch X: %g", RAD2HRS(in->ra), RAD2DEG(in->dec), Jx);
|
||||
if(!in) return false;
|
||||
if(!get_MJDt(NULL, &MJD)) return false;
|
||||
// these values could be changed (e.g. by user's input)
|
||||
double pr = 0.0; // RA proper motion (radians/year)
|
||||
double pd = 0.0; // Dec proper motion (radians/year)
|
||||
double px = 0.0; // parallax (arcsec)
|
||||
double rv = 0.0; // radial velocity (km/s, positive if receding)
|
||||
double ri, di, eo;
|
||||
eraAtci13(in->ra, in->dec, pr, pd, px, rv, MJD.tt1, MJD.tt2, &ri, &di, &eo);
|
||||
eraAtci13(in->ra, in->dec, pr, pd, px, rv, ERFA_DJM0, Jx, &ri, &di, &eo);
|
||||
LOGDBG("ri: %ghrs, eo: %ghrs, di: %gdeg", RAD2HRS(ri), RAD2HRS(eo), RAD2DEG(di));
|
||||
if(out){
|
||||
out->ra = eraAnp(ri - eo);
|
||||
out->ha = 0.; out->eo = 0.;
|
||||
//out->ra = eraAnp(ri - eo);
|
||||
out->ra = ri;
|
||||
out->dec = di;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#if 0
|
||||
typedef enum {
|
||||
WEATHER_GOOD = 0, // may start observations
|
||||
WEATHER_BAD = 1, // cannot start but can continue if want
|
||||
WEATHER_TERRIBLE = 2, // close & park: wind, precipitation, humidity etc.
|
||||
WEATHER_PROHIBITED = 3, // force closing & parking; power off equipment, ready to power off computer
|
||||
} weather_condition_t;
|
||||
|
||||
typedef struct {
|
||||
weather_condition_t weather; // conditions: field "WEATHER"
|
||||
float windmax; // maximal wind for last hour, m/s: "WINDMAX1"
|
||||
float wind; // current wind speed, m/s: "WIND"
|
||||
float clouds; // sky "quality" (>2500 - OK): "CLOUDS"
|
||||
float exttemp; // external temperature, degC: "EXTTEMP"
|
||||
float pressure; // atm. pressure, mmHg: "PRESSURE"
|
||||
float humidity; // humidity, percents: "HUMIDITY"
|
||||
int rain; // ==1 when rainy: "PRECIP"
|
||||
int forceoff; // force power off (AC power lost or lightning)
|
||||
time_t last_update; // value of "TMEAS"
|
||||
} weather_data_t;
|
||||
|
||||
int get_weather_data(weather_data_t *data);
|
||||
#endif
|
||||
|
||||
@@ -80,9 +80,7 @@ void eq2hor(polarCrds_t *pc, horizCrds_t *h, double sidTime);
|
||||
void r2sHMS(double radians, char *hms, int len);
|
||||
void r2sDMS(double radians, char *hms, int len);
|
||||
void d2sDMS(double degrees, char *dms, int len);
|
||||
void hor2eq(horizCrds_t *h, polarCrds_t *pc, double sidTime);
|
||||
void eq2horH(polarCrds_t *pc, horizCrds_t *h);
|
||||
void eq2hor(polarCrds_t *pc, horizCrds_t *h, double sidTime);
|
||||
|
||||
bool get_MJDt(struct timeval *tval, sMJD_t *MJD);
|
||||
bool get_LST(sMJD_t *mjd, double *LST);
|
||||
bool get_ObsPlace(struct timeval *tval, polarCrds_t *p2000, polarCrds_t *pnow, horizCrds_t *hnow);
|
||||
@@ -90,5 +88,8 @@ bool get_ObsPlace(struct timeval *tval, polarCrds_t *p2000, polarCrds_t *pnow, h
|
||||
bool setDUT(almDut_t *D);
|
||||
void getDUT(almDut_t *D);
|
||||
|
||||
bool setPlaceData(double longitude, double latitude, double altitude);
|
||||
bool setPlaceData(placeData_t *pd);
|
||||
void getPlaceData(placeData_t *pd);
|
||||
|
||||
bool JnowtoJ2000(const polarCrds_t *in, polarCrds_t *out);
|
||||
bool JXtoJnow(const polarCrds_t *in, polarCrds_t *out, double Jx);
|
||||
|
||||
@@ -50,20 +50,28 @@ static parameters_t Conf = {0};
|
||||
#define STR(x) STR_HELPER(x)
|
||||
|
||||
#define OPTIONS(Stor) \
|
||||
{"device", NEED_ARG, NULL, 'd', arg_string, APTR(&Stor.device), "serial device name"}, \
|
||||
{"emulation",NO_ARGS, NULL, 'e', arg_int, APTR(&Stor.emulation), "run in emulation mode"}, \
|
||||
{"logfile", NEED_ARG, NULL, 'l', arg_string, APTR(&Stor.logfile), "file to save logs"}, \
|
||||
{"hdrfile", NEED_ARG, NULL, 'o', arg_string, APTR(&Stor.crdsfile), "file to save FITS-header with coordinates and time (default: " DEFAULT_FITSHDR ")"}, \
|
||||
{"pidfile", NEED_ARG, NULL, 0, arg_string, APTR(&Stor.pidfile), "pidfile (default: " DEFAULT_PIDFILE ")"}, \
|
||||
{"port", NEED_ARG, NULL, 'p', arg_string, APTR(&Stor.port), "port for stellaruim's connect (default: " DEFAULT_PORT ")"}, \
|
||||
{"cmdport", NEED_ARG, NULL, 'P', arg_string, APTR(&Stor.cmdnode), "port or UNIX-socket path to connect for command console (default: " DEFAULT_CMDNODE ")"}, \
|
||||
{"sleept", NEED_ARG, NULL, 't', arg_int, APTR(&Stor.sleept), "time of servers' sleeping in main cycle, us (default: " STR(DEFAULT_SLEEP_T) " us)"}, \
|
||||
{"isunix", NO_ARGS, NULL, 'U', arg_int, APTR(&Stor.isunix), "use UNIX-socket for cmdport"}, \
|
||||
{"sertmout",NEED_ARG, NULL, 'T', arg_double, APTR(&Stor.sertmout), "serial timeout, seconds (default: " STR(DEFAULT_SERTMOUT) " s)"}, \
|
||||
{"serspeed",NEED_ARG, NULL, 'S', arg_int, APTR(&Stor.serspeed), "serial speed (default: " STR(DEFAULT_SERSPEED) ")"}, \
|
||||
{"maxclients",NEED_ARG, NULL, 0, arg_int, APTR(&Stor.maxclients),"max amount of clients connected to one socket (default: " STR(DEFAULT_MAXCLIENTS) ")"}, \
|
||||
{"mountname",NEED_ARG, NULL, 0, arg_string, APTR(&Stor.mountname), "mount name (default: " DEFAULT_MOUNT_NAME ")"}, \
|
||||
{"verbose", NO_ARGS, NULL, 'v', arg_none, APTR(&Stor.verbose), "verbose level (each -v increases it)"}, \
|
||||
{"device", NEED_ARG, NULL, 'd', arg_string, APTR(&Stor.device), "serial device name"}, \
|
||||
{"emulation",NO_ARGS, NULL, 'e', arg_int, APTR(&Stor.emulation), "run in emulation mode"}, \
|
||||
{"logfile", NEED_ARG, NULL, 'l', arg_string, APTR(&Stor.logfile), "file to save logs"}, \
|
||||
{"hdrfile", NEED_ARG, NULL, 'o', arg_string, APTR(&Stor.crdsfile), "file to save FITS-header with coordinates and time (default: " DEFAULT_FITSHDR ")"}, \
|
||||
{"pidfile", NEED_ARG, NULL, 0, arg_string, APTR(&Stor.pidfile), "pidfile (default: " DEFAULT_PIDFILE ")"}, \
|
||||
{"port", NEED_ARG, NULL, 'p', arg_string, APTR(&Stor.port), "port for stellaruim's connect (default: " DEFAULT_PORT ")"}, \
|
||||
{"cmdport", NEED_ARG, NULL, 'P', arg_string, APTR(&Stor.cmdnode), "port or UNIX-socket path to connect for command console (default: " DEFAULT_CMDNODE ")"}, \
|
||||
{"sleept", NEED_ARG, NULL, 't', arg_int, APTR(&Stor.sleept), "time of servers' sleeping in main cycle, us (default: " STR(DEFAULT_SLEEP_T) " us)"}, \
|
||||
{"isunix", NO_ARGS, NULL, 'U', arg_int, APTR(&Stor.isunix), "use UNIX-socket for cmdport"}, \
|
||||
{"sertmout",NEED_ARG, NULL, 'T', arg_double, APTR(&Stor.sertmout), "serial timeout, seconds (default: " STR(DEFAULT_SERTMOUT) " s)"}, \
|
||||
{"serspeed",NEED_ARG, NULL, 'S', arg_int, APTR(&Stor.serspeed), "serial speed (default: " STR(DEFAULT_SERSPEED) ")"}, \
|
||||
{"maxclients",NEED_ARG, NULL, 0, arg_int, APTR(&Stor.maxclients), "max amount of clients connected to one socket (default: " STR(DEFAULT_MAXCLIENTS) ")"}, \
|
||||
{"mountname",NEED_ARG, NULL, 0, arg_string, APTR(&Stor.mountname), "mount name (default: " DEFAULT_MOUNT_NAME ")"}, \
|
||||
{"verbose", NO_ARGS, NULL, 'v', arg_none, APTR(&Stor.verbose), "verbose level (each -v increases it)"}, \
|
||||
{"parka", NEED_ARG, NULL, 'A', arg_string, APTR(&Stor.parkA), "azimuth of parking point"}, \
|
||||
{"parkz", NEED_ARG, NULL, 'Z', arg_string, APTR(&Stor.parkZ), "zenith distance of parking point"}, \
|
||||
{"dut1", NEED_ARG, NULL, 0, arg_double, APTR(&Stor.DUT1), "set DUT1 value (s)"}, \
|
||||
{"polarx", NEED_ARG, NULL, 0, arg_double, APTR(&Stor.polarx), "set polar X value (m)"}, \
|
||||
{"polary", NEED_ARG, NULL, 0, arg_double, APTR(&Stor.polary), "set polar Y value (m)"}, \
|
||||
{"latitude",NEED_ARG, NULL, 0, arg_string, APTR(&Stor.latitude), "latitude of mount"}, \
|
||||
{"altitude",NEED_ARG, NULL, 0, arg_string, APTR(&Stor.altitude), "altitude of mount"}, \
|
||||
{"longitude",NEED_ARG, NULL, 0, arg_string, APTR(&Stor.longitude), "longitude of mount"}, \
|
||||
|
||||
|
||||
static sl_option_t cmdlnopts[] = {
|
||||
@@ -126,7 +134,6 @@ static void chkdbl(double *conf, double *cmd, double dflt){
|
||||
parameters_t *parse_cmdline(int *argc, char ***argv){
|
||||
sl_parseargs(argc, argv, cmdlnopts);
|
||||
if(help) sl_showhelp(-1, cmdlnopts);
|
||||
//if(!conffile) return &G;
|
||||
// fix for conffile
|
||||
if(conffile){
|
||||
if(!sl_conf_readopts(conffile, confopts)){ // show conf help
|
||||
@@ -150,5 +157,13 @@ parameters_t *parse_cmdline(int *argc, char ***argv){
|
||||
DBLCHKD(sertmout, DEFAULT_SERTMOUT);
|
||||
INTCHKD(serspeed, DEFAULT_SERSPEED);
|
||||
INTCHKD(maxclients, DEFAULT_MAXCLIENTS);
|
||||
STRCHK(parkA);
|
||||
STRCHK(parkZ);
|
||||
STRCHK(longitude);
|
||||
STRCHK(latitude);
|
||||
STRCHK(altitude);
|
||||
DBLCHK(DUT1);
|
||||
DBLCHK(polarx);
|
||||
DBLCHK(polary);
|
||||
return &G;
|
||||
}
|
||||
|
||||
@@ -26,6 +26,11 @@ typedef struct{
|
||||
char *logfile; // logging to this file
|
||||
char *crdsfile; // file where FITS-header should be written
|
||||
char *mountname; // set mount name for FITS-header
|
||||
char *parkA; // parking coordinates (degrees or "DD:MM:SS")
|
||||
char *parkZ;
|
||||
char *latitude; // place coordinates
|
||||
char *longitude;
|
||||
char *altitude; // place altitude
|
||||
int emulation; // run in emulation mode
|
||||
int verbose; // verbose level
|
||||
int sleept; // server's sleeping in main cycle time
|
||||
@@ -33,6 +38,8 @@ typedef struct{
|
||||
int serspeed; // serial speed, baud
|
||||
int maxclients; // max amount of clients connected to one socket
|
||||
double sertmout; // serial timeout, s
|
||||
double DUT1; // DUT1 value
|
||||
double polarx, polary; // polar coordinates, m
|
||||
} parameters_t;
|
||||
|
||||
parameters_t *parse_cmdline(int *argc, char ***argv);
|
||||
|
||||
@@ -215,7 +215,9 @@ void emulation_stop(){
|
||||
void emul_start_tracking(){
|
||||
if(MNT_S_TRACKING == emulation_status()) return;
|
||||
atomic_store(&emul_status, MNT_S_TRACKING);
|
||||
// TODO: convert current A/Z into RA/Dec and set zero speeds by both axes
|
||||
double LST;
|
||||
if(!get_LST(NULL, &LST)) return;
|
||||
hor2eq(&CurAZ, &CurRD, LST);
|
||||
}
|
||||
|
||||
mount_status_t emulation_status(){
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <erfa.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
@@ -26,10 +27,6 @@
|
||||
|
||||
static char *hdname = NULL;
|
||||
|
||||
#ifndef FLT_EPSILON
|
||||
#define FLT_EPSILON 1e-6
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief set_header_name - check ability of witing into file and set given name
|
||||
* @param name - header filename
|
||||
@@ -88,16 +85,16 @@ static int printhdr(int fd, const char *key, const char *val, const char *cmnt){
|
||||
void wrhdr(fitsheader_t *HDR){
|
||||
if(!HDR) return;
|
||||
static time_t lastcorr = 0; // last time of time/weather corrections sent to mount
|
||||
time_t curtime = time(NULL);
|
||||
bool haveweather = false;
|
||||
double curtime = sl_dtime();
|
||||
//bool haveweather = false;
|
||||
bool poweredON = (HDR->status == MNT_S_ERROR || HDR->status == MNT_S_OFF) ? false : true;
|
||||
// weather block
|
||||
if(HDR->weather.last_update < (int)(1 + MOUNT_CHECK_T * 2)){ // weather data is good
|
||||
if(time(NULL) - lastcorr > CORRECTIONS_TIMEDIFF){ // make correction once per hour
|
||||
if(time(NULL) - HDR->weather.last_update < (int)(1. + MOUNT_CHECK_T * 10.)){ // weather data is good
|
||||
if(poweredON && time(NULL) - lastcorr > CORRECTIONS_TIMEDIFF){ // make correction once per hour
|
||||
if(mount_corrdata(&HDR->weather)) lastcorr = time(NULL);
|
||||
}
|
||||
haveweather = true;
|
||||
}
|
||||
//haveweather = true;
|
||||
}else WARNX("bad weather; curtime - HDR->weather.last_update = %g", curtime - HDR->weather.last_update);
|
||||
if(!hdname){
|
||||
DBG("hdname not given!");
|
||||
return;
|
||||
@@ -135,32 +132,41 @@ void wrhdr(fitsheader_t *HDR){
|
||||
if(pt > ht){ // horcrds is older -> show polar
|
||||
if(curtime - pt < COORDS_OLD_T){
|
||||
snprintf(val, 22, "%.10f", RAD2DEG(polar.ra));
|
||||
WRHDR("TAGRA", val, "Target RA (J2000), degrees");
|
||||
WRHDR("INPRA", val, "Input RA, degrees");
|
||||
snprintf(val, 22, "%.10f", RAD2DEG(polar.dec));
|
||||
WRHDR("TAGDEC", val, "Target DEC (J2000), degrees");
|
||||
WRHDR("INPDEC", val, "Input DEC, degrees");
|
||||
}
|
||||
}else{ // show horiz
|
||||
if(curtime - ht < COORDS_OLD_T){
|
||||
snprintf(val, 22, "%.10f", RAD2DEG(horiz.az));
|
||||
WRHDR("TAGAZ", val, "Target Azimuth, degrees");
|
||||
WRHDR("INPAZ", val, "Input Azimuth, degrees");
|
||||
snprintf(val, 22, "%.10f", RAD2DEG(horiz.zd));
|
||||
WRHDR("TAGZD", val, "Target Zenith dist., degrees");
|
||||
WRHDR("INPZD", val, "Input Zenith dist., degrees");
|
||||
}
|
||||
}
|
||||
if(curtime - mount_getTagCoords(&polar) < COORDS_OLD_T){
|
||||
snprintf(val, 22, "%.10f", RAD2DEG(polar.ra));
|
||||
WRHDR("TAGRA", val, "Target RA, degrees");
|
||||
snprintf(val, 22, "%.10f", RAD2DEG(polar.dec));
|
||||
WRHDR("TAGDEC", val, "Target DEC, degrees");
|
||||
}
|
||||
if(poweredON){
|
||||
snprintf(val, 22, "%.10f", RAD2DEG(HDR->polar.ra)); // convert RA to degrees
|
||||
snprintf(val, 22, "%.10f", RAD2HRS(HDR->polar.ra)); // convert RA to degrees
|
||||
WRHDR("RA", val, "Telescope right ascension, current epoch, deg");
|
||||
snprintf(val, 22, "%.10f", RAD2HRS(eraAnp(HDR->sidtime - HDR->polar.ra))); // hour angle
|
||||
WRHDR("HA", val, "Telescope hour angle, deg");
|
||||
snprintf(val, 22, "%.10f", RAD2DEG(HDR->polar.dec));
|
||||
WRHDR("DEC", val, "Telescope declination, current epoch, deg");
|
||||
snprintf(val, 22, "%.10f", RAD2DEG(HDR->altaz.az));
|
||||
WRHDR("AZ", val, "Telescope azimuth, current epoch, deg");
|
||||
WRHDR("AZ", val, "Telescope azimuth, deg");
|
||||
snprintf(val, 22, "%.10f", RAD2DEG(HDR->altaz.zd));
|
||||
WRHDR("ZD", val, "Telescope zenith distance, current epoch, deg");
|
||||
WRHDR("ZD", val, "Telescope zenith distance, deg");
|
||||
}
|
||||
WRHDR("TELSTAT", mount_status_str(HDR->status), "Telescope mount status");
|
||||
double mjd;
|
||||
mount_getInpMJD(&mjd);
|
||||
snprintf(val, 22, "%.10f", 2000.+(mjd - ERFA_DJM00)/365.25); // calculate EPOCH/EQUINOX
|
||||
if(mjd > 0.) snprintf(val, 22, "%.10f", 2000.+(mjd - ERFA_DJM00)/365.25); // calculate EPOCH/EQUINOX
|
||||
else snprintf(val, 22, "%.10f", 2000.+(HDR->MJD.MJD - ERFA_DJM00)/365.25);
|
||||
WRHDR("INPEQUIN", val, "Equinox of input celestial coordinate system");
|
||||
if(poweredON){
|
||||
snprintf(val, 22, "%.10f", 2000.+(HDR->MJD.MJD - ERFA_DJM00)/365.25); // telescope coordinates: JNOW
|
||||
@@ -175,8 +181,9 @@ void wrhdr(fitsheader_t *HDR){
|
||||
WRHDR("LONGITUD", val, "Geo longitude of site (east negative)");
|
||||
snprintf(val, 22, "%.10f", RAD2DEG(HDR->place.slat));
|
||||
WRHDR("LATITUDE", val, "Geo latitude of site (south negative)");
|
||||
snprintf(val, 22, "%.4f", RAD2HRS(HDR->sidtime));
|
||||
snprintf(val, 22, "%.6f", RAD2HRS(HDR->sidtime));
|
||||
WRHDR("LSTEND", val, "Local sidereal time of file creation");
|
||||
/* Weather data is written into separate header-file by weather daemon
|
||||
if(haveweather){
|
||||
snprintf(val, 22, "%.1f", HDR->weather.humidity);
|
||||
WRHDR("HUMIDITY", val, "Relative humidity, %%");
|
||||
@@ -192,9 +199,9 @@ void wrhdr(fitsheader_t *HDR){
|
||||
WRHDR("WINDSPD", val, "Wind speed (m/s)");
|
||||
snprintf(val, 22, "%.1f", HDR->weather.windmax);
|
||||
WRHDR("WINDMAX", val, "Last hour maximal wind speed (m/s)");
|
||||
snprintf(val, 22, "%zd", HDR->weather.last_update);
|
||||
snprintf(val, 22, "%lld", (long long)HDR->weather.last_update);
|
||||
WRHDR("WEATTIME", val, "Unix time of weather measurements");
|
||||
}
|
||||
}*/
|
||||
// WRHDR("", , "");
|
||||
#undef WRHDR
|
||||
returning:
|
||||
|
||||
@@ -27,6 +27,10 @@
|
||||
// status checking interval and writing FITS-header, seconds
|
||||
#define MOUNT_CHECK_T 0.5
|
||||
|
||||
#ifndef FLT_EPSILON
|
||||
#define FLT_EPSILON 1e-6
|
||||
#endif
|
||||
|
||||
typedef struct{
|
||||
weather_data_t weather;
|
||||
mount_status_t status;
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include "fitshdr.h"
|
||||
#include "server.h"
|
||||
#include "mount.h"
|
||||
#include "stellarium.h"
|
||||
|
||||
static pid_t childpid = -1; // PID of child process
|
||||
static parameters_t *G = NULL;
|
||||
@@ -43,9 +44,11 @@ void signals(int sig){
|
||||
}
|
||||
if(childpid > 0){ // parent process
|
||||
LOGERR("PID %d exit with status %d", getpid(), sig);
|
||||
isrunning = false;
|
||||
}else{
|
||||
LOGWARN("Child %d died with %d", getpid(), sig);
|
||||
server_stop();
|
||||
stellarium_stop();
|
||||
}
|
||||
DBG("EXIT");
|
||||
}
|
||||
@@ -75,11 +78,33 @@ int main(int argc, char **argv){
|
||||
WARNX("Can't set mount name to %s", G->mountname);
|
||||
return 5;
|
||||
}
|
||||
if(G->parkA){
|
||||
double A = 0.;
|
||||
if(!str2coord(G->parkA, &A) || !mount_setParkAz(A)) WARNX("Can't set parking azimuth to %g", A);
|
||||
}
|
||||
if(G->parkZ){
|
||||
double Z = 0.;
|
||||
if(!str2coord(G->parkZ, &Z) || !mount_setParkZD(Z)) WARNX("Can't set parking zenith distance to %g", Z);
|
||||
}
|
||||
if(fabs(G->DUT1) > FLT_EPSILON || fabs(G->polarx) > FLT_EPSILON || fabs(G->polary) > FLT_EPSILON){
|
||||
almDut_t par = {.DUT1 = G->DUT1, .px = G->polarx, .py = G->polary};
|
||||
setDUT(&par);
|
||||
}
|
||||
if(G->altitude || G->longitude || G->latitude){
|
||||
placeData_t place;
|
||||
double val;
|
||||
getPlaceData(&place);
|
||||
if(G->altitude && sl_str2d(&val, G->altitude)) place.salt = val;
|
||||
if(G->longitude && str2coord(G->longitude, &val)) place.slong = DEG2RAD(val);
|
||||
if(G->latitude && str2coord(G->latitude, &val)) place.slat = DEG2RAD(val);
|
||||
setPlaceData(&place);
|
||||
}
|
||||
signal(SIGTERM, signals);
|
||||
signal(SIGINT, signals);
|
||||
signal(SIGQUIT, signals);
|
||||
signal(SIGTSTP, SIG_IGN);
|
||||
signal(SIGHUP, signals);
|
||||
signal(SIGHUP, SIG_IGN);
|
||||
//signal(SIGHUP, signals); // re-read config file
|
||||
sl_check4running((char*)__progname, G->pidfile);
|
||||
if(G->logfile) OPENLOG(G->logfile, lvl, 1);
|
||||
DBG("Started");
|
||||
@@ -108,6 +133,7 @@ int main(int argc, char **argv){
|
||||
break; // go out to normal functional
|
||||
}
|
||||
}
|
||||
if(!isrunning) return 0;
|
||||
#endif
|
||||
server_sock_t sockt = {
|
||||
.cmd_isunix = G->isunix,
|
||||
|
||||
@@ -42,9 +42,12 @@ static sl_tty_t *mount_dev = NULL;
|
||||
static pthread_mutex_t mntdev_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
// status
|
||||
static atomic_int mountstatus = MNT_S_ERROR;
|
||||
|
||||
// parking coordinates
|
||||
static horizCrds_t ParkCoords = {.az = 0., .zd = DEG2RAD(80.)};
|
||||
/*
|
||||
A="01:48:38" -> A=1.8105556
|
||||
H="01:13:29" -> Z=88.7752778
|
||||
*/
|
||||
static horizCrds_t ParkCoords = {.az = DEG2RAD(1.8105556), .zd = DEG2RAD(88.7752778)};
|
||||
// input and current target coordinates
|
||||
static polarCrds_t InpCoords = {0}, // input as user give (for epoch InpMJD)
|
||||
TagCoords = {0}; // target for Jnow after command "point to input"
|
||||
@@ -79,6 +82,7 @@ double mount_getInpHor(horizCrds_t *c){
|
||||
* @return false if `ha` isn't in [0,24)
|
||||
*/
|
||||
bool mount_setInpHA(double ha){
|
||||
if(ha < 0) ha += 24.;
|
||||
if(ha < 0. || ha >= 24.) return false;
|
||||
InpCoords.ha = HRS2RAD(ha);
|
||||
InpCTime = sl_dtime();
|
||||
@@ -228,7 +232,7 @@ static bool send_cmd_resp(const char *cmd, char *resp, size_t resplen){
|
||||
size_t pos = 0;
|
||||
double t0 = sl_dtime();
|
||||
bool gotEOM = false;
|
||||
while(sl_dtime() - t0 < sertmout){
|
||||
while(sl_dtime() - t0 < sertmout && !gotEOM){
|
||||
int got = sl_tty_read(mount_dev);
|
||||
if(got < 0){
|
||||
WARN("sl_tty_read()");
|
||||
@@ -241,9 +245,6 @@ static bool send_cmd_resp(const char *cmd, char *resp, size_t resplen){
|
||||
if(c == '\r' || c == '\n') continue; // WTF?
|
||||
if(c == '#'){ // end of message
|
||||
gotEOM = true;
|
||||
//if(resp && resplen) resp[pos < resplen ? pos : resplen-1] = 0;
|
||||
//DBG("Got end of message: '%s' (waited for %gs)", resp, );
|
||||
//return true;
|
||||
break;
|
||||
}
|
||||
if(resp && pos + 1 < resplen) resp[pos++] = c;
|
||||
@@ -340,7 +341,7 @@ static bool guess_speed(){
|
||||
if(!mount_dev) return false;
|
||||
close(mount_dev->comfd);
|
||||
#define SPDBUFSZ 7
|
||||
const int speeds[SPDBUFSZ] = {57600, 38400, 19200, 9600, 4800, 2400, 1200};
|
||||
const int speeds[SPDBUFSZ] = {9600, 57600, 38400, 19200, 4800, 2400, 1200};
|
||||
int idx = 0;
|
||||
for(; idx < SPDBUFSZ && isrunning; ++idx){
|
||||
DBG("try speed %d", speeds[idx]);
|
||||
@@ -382,8 +383,10 @@ bool mount_connect(){
|
||||
snprintf(buf, 63, CMD_SETMINALT, 10);
|
||||
if(!write_cmd(buf, true)) ret = false; // set minimum altitude to 10 degrees
|
||||
pthread_mutex_unlock(&mntdev_mutex);
|
||||
if(ret) LOGMSG("Connected to %s@115200", mount_dev->portname);
|
||||
else LOGERR("Can't write commands to mount");
|
||||
if(ret){
|
||||
LOGMSG("Connected to %s@115200", mount_dev->portname);
|
||||
atomic_store(&mountstatus, MNT_S_STOPPED);
|
||||
}else LOGERR("Can't write commands to mount");
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -392,7 +395,9 @@ void mount_disconnect(){
|
||||
emulation_stop();
|
||||
return;
|
||||
}
|
||||
pthread_mutex_trylock(&mntdev_mutex); // at least, try
|
||||
double t0 = sl_dtime();
|
||||
while(sl_dtime() - t0 < 120. && pthread_mutex_trylock(&mntdev_mutex))
|
||||
usleep(100000);
|
||||
if(mount_dev) close(mount_dev->comfd);
|
||||
pthread_mutex_unlock(&mntdev_mutex);
|
||||
}
|
||||
@@ -427,7 +432,7 @@ bool mount_point(double ra, double dec){
|
||||
}
|
||||
}
|
||||
ret = true;
|
||||
TagCoords.ra = DEG2RAD(ra);
|
||||
TagCoords.ra = HRS2RAD(ra);
|
||||
TagCoords.dec = DEG2RAD(dec);
|
||||
TagTime = sl_dtime();
|
||||
retn:
|
||||
@@ -461,7 +466,7 @@ bool mount_pointAZ(double A, double Z){
|
||||
if(!write_cmd(cmd, true)) goto retn;
|
||||
snprintf(cmd, 127, CMD_SETZD, azstr);
|
||||
if(!write_cmd(cmd, true)) goto retn;
|
||||
if(send_cmd_resp(CMD_GOTOAZ, cmd, 127)){ // returned not '0'
|
||||
if(send_cmd_resp(CMD_GOTOAZ, cmd, 128)){ // returned not '0'
|
||||
if(*cmd != '0'){
|
||||
WARNX("Goto error, answer: %s", cmd);
|
||||
LOGWARN("Goto error, answer: %s", cmd);
|
||||
@@ -482,7 +487,9 @@ bool mount_getcoords(double *ra, double *dec){
|
||||
if(!ra || !dec) return false;
|
||||
if(isemulated){
|
||||
get_emul_coords(ra, dec);
|
||||
DBG("Emulated coordinates: %gh, %gdeg", RAD2HRS(*ra), RAD2DEG(*dec));
|
||||
*ra = RAD2HRS(*ra);
|
||||
*dec = RAD2DEG(*dec);
|
||||
DBG("Emulated coordinates: %gh, %gdeg", *ra, *dec);
|
||||
return true;
|
||||
}
|
||||
if(!mount_dev) return false;
|
||||
@@ -509,7 +516,7 @@ bool mount_getaz(double *a, double *z){
|
||||
double LST;
|
||||
get_LST(NULL, &LST);
|
||||
eq2hor(&P, &H, LST);
|
||||
*a = H.az; *z = H.zd;
|
||||
*a = RAD2DEG(H.az); *z = RAD2DEG(H.zd);
|
||||
return true;
|
||||
}
|
||||
if(!mount_dev) return false;
|
||||
@@ -582,7 +589,9 @@ bool mount_tracking_start(){
|
||||
*/
|
||||
bool mount_park(){
|
||||
if(isemulated) return pointAZ_emulation(ParkCoords.az, ParkCoords.zd);
|
||||
// TODO: set lower limit to 0deg
|
||||
char buf[64];
|
||||
snprintf(buf, 63, CMD_SETMINALT, 0);
|
||||
if(!write_cmd(buf, true)) return false;
|
||||
return mount_pointAZ(RAD2DEG(ParkCoords.az), RAD2DEG(ParkCoords.zd));
|
||||
}
|
||||
|
||||
@@ -672,3 +681,11 @@ bool mount_corrdata(weather_data_t *w){
|
||||
pthread_mutex_unlock(&mntdev_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief mount_shutdown - turn off mount
|
||||
* @return false if failed
|
||||
*/
|
||||
bool mount_shutdown(){
|
||||
return write_cmd(CMD_SHUTDOWN, true);
|
||||
}
|
||||
|
||||
@@ -88,3 +88,5 @@ bool mount_park();
|
||||
bool mount_setParkAz(double az);
|
||||
bool mount_setParkZD(double zd);
|
||||
void mount_getPark(horizCrds_t *c);
|
||||
|
||||
bool mount_shutdown();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!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>
|
||||
<data>
|
||||
<variable>EnvironmentId</variable>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
10micron_commands.h
|
||||
Readme.md
|
||||
angles.c
|
||||
angles.h
|
||||
args.c
|
||||
|
||||
@@ -36,26 +36,34 @@
|
||||
#define STATUS_MAX_AGE (30.)
|
||||
|
||||
// commands
|
||||
#define CMD_UNIXT "unixt"
|
||||
#define CMD_STATUS "status"
|
||||
#define CMD_STOP "stop"
|
||||
#define CMD_TAGRA "tagra"
|
||||
#define CMD_TAGDEC "tagdec"
|
||||
#define CMD_TAGHA "tagha"
|
||||
#define CMD_TAGAZ "tagaz"
|
||||
#define CMD_TAGZD "tagzd"
|
||||
#define CMD_TELRA "telra"
|
||||
#define CMD_TELDEC "teldec"
|
||||
#define CMD_TELAZ "telaz"
|
||||
#define CMD_TELZD "telzd"
|
||||
#define CMD_DUT1 "dut1"
|
||||
#define CMD_GOTOAZ "gotoaz"
|
||||
#define CMD_GOTORD "gotord"
|
||||
#define CMD_GOTORH "gotorh"
|
||||
#define CMD_GOTOAZ "gotoaz"
|
||||
#define CMD_STOPTRK "stoptrk"
|
||||
#define CMD_TRACK "track"
|
||||
#define CMD_PARK "park"
|
||||
#define CMD_LST "lst"
|
||||
#define CMD_PARKAZ "parkaz"
|
||||
#define CMD_PARK "park"
|
||||
#define CMD_PARKZD "parkzd"
|
||||
#define CMD_PLACE "place"
|
||||
#define CMD_POLARX "polarx"
|
||||
#define CMD_POLARY "polary"
|
||||
#define CMD_SHUTDOWN "shutdown"
|
||||
#define CMD_STATUS "status"
|
||||
#define CMD_STOP "stop"
|
||||
#define CMD_STOPTRK "stoptrk"
|
||||
#define CMD_TAGAZ "tagaz"
|
||||
#define CMD_TAGDEC "tagdec"
|
||||
#define CMD_TAGHA "tagha"
|
||||
#define CMD_TAGMJD "tagmjd"
|
||||
#define CMD_TAGRA "tagra"
|
||||
#define CMD_TAGZD "tagzd"
|
||||
#define CMD_TELAZ "telaz"
|
||||
#define CMD_TELDEC "teldec"
|
||||
#define CMD_TELHA "telha"
|
||||
#define CMD_TELRA "telra"
|
||||
#define CMD_TELZD "telzd"
|
||||
#define CMD_TRACK "track"
|
||||
#define CMD_UNIXT "unixt"
|
||||
|
||||
|
||||
// main command socket
|
||||
@@ -65,7 +73,7 @@ static int stellarium_sockfd = -1;
|
||||
// sleep time (us)
|
||||
static unsigned int sleept = DEFAULT_SLEEP_T;
|
||||
// running flag
|
||||
volatile bool isrunning = false;
|
||||
volatile bool isrunning = true;
|
||||
// lost weather flag
|
||||
static bool weatherlost = false;
|
||||
|
||||
@@ -102,9 +110,7 @@ static int parse_key_value(const char *req, double *val){
|
||||
if(!req) return 0; // is getter
|
||||
DBG("parsing of %s", req);
|
||||
double d;
|
||||
if(strchr(req, ':')){ // DD:MM:SS or HH:MM:SS
|
||||
if(!str2coord(req, &d)) return -1;
|
||||
}else if(sscanf(req, "%lf", &d) != 1) return -1; // error
|
||||
if(!str2coord(req, &d)) return -1;
|
||||
if(val) *val = d;
|
||||
return 1; // is setter
|
||||
}
|
||||
@@ -115,6 +121,7 @@ static sl_sock_hresult_e cmd_tagra(sl_sock_t *c, sl_sock_hitem_t *item, const ch
|
||||
int res = parse_key_value(req, &val);
|
||||
if(res < 0) return RESULT_BADVAL;
|
||||
if(res > 0){ // setter
|
||||
LOGDBG("Client %d set tagra: %g", c->fd, val);
|
||||
if(mount_setInpRA(val)) return RESULT_OK;
|
||||
return RESULT_BADVAL;
|
||||
}else{ // getter
|
||||
@@ -133,6 +140,7 @@ static sl_sock_hresult_e cmd_tagdec(sl_sock_t *c, sl_sock_hitem_t *item, const c
|
||||
int res = parse_key_value(req, &val);
|
||||
if(res < 0) return RESULT_BADVAL;
|
||||
if(res > 0){
|
||||
LOGDBG("Client %d set tagdec: %g", c->fd, val);
|
||||
if(mount_setInpDec(val)) return RESULT_OK;
|
||||
return RESULT_BADVAL;
|
||||
}else{
|
||||
@@ -151,6 +159,7 @@ static sl_sock_hresult_e cmd_tagha(sl_sock_t *c, sl_sock_hitem_t *item, const ch
|
||||
int res = parse_key_value(req, &val);
|
||||
if(res < 0) return RESULT_BADVAL;
|
||||
if(res > 0){
|
||||
LOGDBG("Client %d set tagha: %g", c->fd, val);
|
||||
if(mount_setInpHA(val)) return RESULT_OK;
|
||||
return RESULT_BADVAL;
|
||||
}else{
|
||||
@@ -169,6 +178,7 @@ static sl_sock_hresult_e cmd_tagaz(sl_sock_t *c, sl_sock_hitem_t *item, const ch
|
||||
int res = parse_key_value(req, &val);
|
||||
if(res < 0) return RESULT_BADVAL;
|
||||
if(res > 0){
|
||||
LOGDBG("Client %d set tagaz: %g", c->fd, val);
|
||||
if(mount_setInpA(val)) return RESULT_OK;
|
||||
return RESULT_BADVAL;
|
||||
}else{
|
||||
@@ -187,6 +197,7 @@ static sl_sock_hresult_e cmd_tagzd(sl_sock_t *c, sl_sock_hitem_t *item, const ch
|
||||
int res = parse_key_value(req, &val);
|
||||
if(res < 0) return RESULT_BADVAL;
|
||||
if(res > 0){
|
||||
LOGDBG("Client %d set tagzd: %g", c->fd, val);
|
||||
if(mount_setInpZ(val)) return RESULT_OK;
|
||||
return RESULT_BADVAL;
|
||||
}else{
|
||||
@@ -200,6 +211,13 @@ static sl_sock_hresult_e cmd_tagzd(sl_sock_t *c, sl_sock_hitem_t *item, const ch
|
||||
return RESULT_SILENCE;
|
||||
}
|
||||
|
||||
static sl_sock_hresult_e cmd_lst(sl_sock_t *c, sl_sock_hitem_t *item, _U_ const char *req){
|
||||
char buf[64];
|
||||
snprintf(buf, 63, "%s=%.6f\n", item->key, RAD2HRS(HDR.sidtime));
|
||||
sl_sock_sendstrmessage(c, buf);
|
||||
return RESULT_SILENCE;
|
||||
}
|
||||
|
||||
static sl_sock_hresult_e cmd_telra(sl_sock_t *c, sl_sock_hitem_t *item, _U_ const char *req){
|
||||
if(weatherlost || HDR.status == MNT_S_ERROR) return RESULT_FAIL;
|
||||
char buf[64];
|
||||
@@ -208,6 +226,15 @@ static sl_sock_hresult_e cmd_telra(sl_sock_t *c, sl_sock_hitem_t *item, _U_ cons
|
||||
return RESULT_SILENCE;
|
||||
}
|
||||
|
||||
static sl_sock_hresult_e cmd_telha(sl_sock_t *c, sl_sock_hitem_t *item, _U_ const char *req){
|
||||
if(weatherlost || HDR.status == MNT_S_ERROR) return RESULT_FAIL;
|
||||
char buf[64];
|
||||
double ha = eraAnp(HDR.sidtime - HDR.polar.ra);
|
||||
snprintf(buf, 63, "%s=%.6f\n", item->key, RAD2HRS(ha));
|
||||
sl_sock_sendstrmessage(c, buf);
|
||||
return RESULT_SILENCE;
|
||||
}
|
||||
|
||||
static sl_sock_hresult_e cmd_teldec(sl_sock_t *c, sl_sock_hitem_t *item, _U_ const char *req){
|
||||
if(weatherlost || HDR.status == MNT_S_ERROR) return RESULT_FAIL;
|
||||
char buf[64];
|
||||
@@ -234,10 +261,16 @@ static sl_sock_hresult_e cmd_telzd(sl_sock_t *c, sl_sock_hitem_t *item, _U_ cons
|
||||
|
||||
static sl_sock_hresult_e cmd_gotord(_U_ sl_sock_t *c, _U_ sl_sock_hitem_t *item, _U_ const char *req){
|
||||
if(weatherlost || HDR.status == MNT_S_ERROR) return RESULT_FAIL;
|
||||
polarCrds_t p;
|
||||
mount_getInpCoords(&p);
|
||||
double ra_h = RAD2HRS(p.ra);
|
||||
double dec_d = RAD2DEG(p.dec);
|
||||
polarCrds_t px, pnow;
|
||||
mount_getInpCoords(&px);
|
||||
double Epoch;
|
||||
mount_getInpMJD(&Epoch);
|
||||
if(Epoch > 0.){
|
||||
if(!JXtoJnow(&px, &pnow, Epoch)) return RESULT_FAIL;
|
||||
}else pnow = px;
|
||||
double ra_h = RAD2HRS(pnow.ra);
|
||||
double dec_d = RAD2DEG(pnow.dec);
|
||||
LOGDBG("Client %d asks to point ra=%ghrs, dec=%gdeg", c->fd, ra_h, dec_d);
|
||||
if(!mount_point(ra_h, dec_d)) return RESULT_FAIL;
|
||||
return RESULT_OK;
|
||||
}
|
||||
@@ -253,6 +286,7 @@ static sl_sock_hresult_e cmd_gotorh(_U_ sl_sock_t *c, _U_ sl_sock_hitem_t *item,
|
||||
double RA_rad = eraAnp(LST - p.ha);
|
||||
double ra_h = RAD2HRS(RA_rad);
|
||||
double dec_d = RAD2DEG(p.dec);
|
||||
LOGDBG("Client %d asks to point to HA/Dec", c->fd);
|
||||
if(!mount_point(ra_h, dec_d)) return RESULT_FAIL;
|
||||
return RESULT_OK;
|
||||
}
|
||||
@@ -261,16 +295,19 @@ static sl_sock_hresult_e cmd_gotoaz(_U_ sl_sock_t *c, _U_ sl_sock_hitem_t *item,
|
||||
if(weatherlost || HDR.status == MNT_S_ERROR) return RESULT_FAIL;
|
||||
horizCrds_t h;
|
||||
mount_getInpHor(&h);
|
||||
LOGDBG("Client %d asks to point to A/Z", c->fd);
|
||||
if(mount_pointAZ(RAD2DEG(h.az), RAD2DEG(h.zd))) return RESULT_OK;
|
||||
return RESULT_FAIL;
|
||||
}
|
||||
|
||||
static sl_sock_hresult_e cmd_stop(_U_ sl_sock_t *c, _U_ sl_sock_hitem_t *item, _U_ const char *req){
|
||||
LOGDBG("Client %d asks to stop", c->fd);
|
||||
if(mount_stop()) return RESULT_OK;
|
||||
return RESULT_FAIL;
|
||||
}
|
||||
|
||||
static sl_sock_hresult_e cmd_stoptrk(_U_ sl_sock_t *c, _U_ sl_sock_hitem_t *item, _U_ const char *req){
|
||||
LOGDBG("Client %d asks to stop tracking", c->fd);
|
||||
if(mount_tracking_stop()) return RESULT_OK;
|
||||
return RESULT_FAIL;
|
||||
}
|
||||
@@ -278,11 +315,13 @@ static sl_sock_hresult_e cmd_stoptrk(_U_ sl_sock_t *c, _U_ sl_sock_hitem_t *item
|
||||
// run tracking from current position
|
||||
static sl_sock_hresult_e cmd_track(_U_ sl_sock_t *c, _U_ sl_sock_hitem_t *item, _U_ const char *req){
|
||||
if(weatherlost || HDR.status == MNT_S_ERROR) return RESULT_FAIL;
|
||||
LOGDBG("Client %d asks to start tracking", c->fd);
|
||||
if(mount_tracking_start()) return RESULT_OK;
|
||||
return RESULT_FAIL;
|
||||
}
|
||||
|
||||
static sl_sock_hresult_e cmd_park(_U_ sl_sock_t *c, _U_ sl_sock_hitem_t *item, _U_ const char *req){
|
||||
LOGDBG("Client %d asks to park", c->fd);
|
||||
if(mount_park()) return RESULT_OK;
|
||||
return RESULT_FAIL;
|
||||
}
|
||||
@@ -293,6 +332,7 @@ static sl_sock_hresult_e cmd_parkaz(sl_sock_t *c, sl_sock_hitem_t *item, const c
|
||||
int res = parse_key_value(req, &val);
|
||||
if(res < 0) return RESULT_BADVAL;
|
||||
if(res > 0){
|
||||
LOGDBG("Client %d set parking Az to %g", c->fd, val);
|
||||
if(mount_setParkAz(val)) return RESULT_OK;
|
||||
return RESULT_BADVAL;
|
||||
}else{
|
||||
@@ -309,6 +349,7 @@ static sl_sock_hresult_e cmd_parkzd(sl_sock_t *c, sl_sock_hitem_t *item, const c
|
||||
int res = parse_key_value(req, &val);
|
||||
if(res < 0) return RESULT_BADVAL;
|
||||
if(res > 0){
|
||||
LOGDBG("Client %d set parking ZD to %g", c->fd, val);
|
||||
if(mount_setParkZD(val)) return RESULT_OK;
|
||||
return RESULT_BADVAL;
|
||||
}else{
|
||||
@@ -321,28 +362,150 @@ static sl_sock_hresult_e cmd_parkzd(sl_sock_t *c, sl_sock_hitem_t *item, const c
|
||||
return RESULT_SILENCE;
|
||||
}
|
||||
|
||||
static sl_sock_hresult_e cmd_tagmjd(sl_sock_t *c, sl_sock_hitem_t *item, const char *req){
|
||||
double val;
|
||||
bool isyear = false;
|
||||
if(req && *req == 'J'){ // e.g. J2050.
|
||||
++req;
|
||||
isyear = true;
|
||||
}
|
||||
int res = parse_key_value(req, &val);
|
||||
if(res < 0) return RESULT_BADVAL;
|
||||
if(res > 0){
|
||||
if(isyear) val = (val - 2000.)*365.25 + ERFA_DJM00;
|
||||
LOGDBG("Client %d set target epoch %g", c->fd, val);
|
||||
if(mount_setInpMJD(val)) return RESULT_OK;
|
||||
return RESULT_BADVAL;
|
||||
}
|
||||
mount_getInpMJD(&val);
|
||||
char buf[64];
|
||||
snprintf(buf, 63, "%s=%.1f\n", item->key, val);
|
||||
sl_sock_sendstrmessage(c, buf);
|
||||
return RESULT_SILENCE;
|
||||
}
|
||||
|
||||
static sl_sock_hresult_e cmd_dut1(sl_sock_t *c, sl_sock_hitem_t *item, const char *req){
|
||||
double val;
|
||||
int res = parse_key_value(req, &val);
|
||||
if(res < 0) return RESULT_BADVAL;
|
||||
almDut_t curval;
|
||||
getDUT(&curval);
|
||||
if(res > 0){
|
||||
LOGDBG("Client %d set dut1 %g", c->fd, val);
|
||||
curval.DUT1 = val;
|
||||
if(setDUT(&curval)) return RESULT_OK;
|
||||
return RESULT_BADVAL;
|
||||
}
|
||||
char buf[64];
|
||||
snprintf(buf, 63, "%s=%.3f\n", item->key, curval.DUT1);
|
||||
sl_sock_sendstrmessage(c, buf);
|
||||
return RESULT_SILENCE;
|
||||
}
|
||||
|
||||
static sl_sock_hresult_e cmd_polarx(sl_sock_t *c, sl_sock_hitem_t *item, const char *req){
|
||||
double val;
|
||||
int res = parse_key_value(req, &val);
|
||||
if(res < 0) return RESULT_BADVAL;
|
||||
almDut_t curval;
|
||||
getDUT(&curval);
|
||||
if(res > 0){
|
||||
LOGDBG("Client %d set polarx %g", c->fd, val);
|
||||
curval.px = val;
|
||||
if(setDUT(&curval)) return RESULT_OK;
|
||||
return RESULT_BADVAL;
|
||||
}
|
||||
char buf[64];
|
||||
snprintf(buf, 63, "%s=%.3f\n", item->key, curval.px);
|
||||
sl_sock_sendstrmessage(c, buf);
|
||||
return RESULT_SILENCE;
|
||||
}
|
||||
|
||||
static sl_sock_hresult_e cmd_polary(sl_sock_t *c, sl_sock_hitem_t *item, const char *req){
|
||||
double val;
|
||||
int res = parse_key_value(req, &val);
|
||||
if(res < 0) return RESULT_BADVAL;
|
||||
almDut_t curval;
|
||||
getDUT(&curval);
|
||||
if(res > 0){
|
||||
LOGDBG("Client %d set polary %g", c->fd, val);
|
||||
curval.py = val;
|
||||
if(setDUT(&curval)) return RESULT_OK;
|
||||
return RESULT_BADVAL;
|
||||
}
|
||||
char buf[64];
|
||||
snprintf(buf, 63, "%s=%.3f\n", item->key, curval.py);
|
||||
sl_sock_sendstrmessage(c, buf);
|
||||
return RESULT_SILENCE;
|
||||
}
|
||||
|
||||
static sl_sock_hresult_e cmd_place(sl_sock_t *c, sl_sock_hitem_t _U_ *item, const char _U_ *req){
|
||||
char buf[128];
|
||||
placeData_t place;
|
||||
getPlaceData(&place);
|
||||
snprintf(buf, 127, "latitude=%.7f\nlongitude=%.7f\naltitude=%.7f\n",
|
||||
RAD2DEG(place.slat), RAD2DEG(place.slong), place.salt);
|
||||
sl_sock_sendstrmessage(c, buf);
|
||||
return RESULT_SILENCE;
|
||||
}
|
||||
|
||||
static sl_sock_hresult_e cmd_shutdown(sl_sock_t *c, sl_sock_hitem_t *item, const char *req){
|
||||
double val;
|
||||
static int shtdwnkey = -1;
|
||||
static time_t keytime = 0;
|
||||
int res = parse_key_value(req, &val);
|
||||
if(res < 0) return RESULT_BADVAL;
|
||||
time_t tnow = time(NULL);
|
||||
if(tnow - keytime > SHTDWN_KEY_TMOUT) keytime = 0;
|
||||
if(res == 0){ // generate new shutdown key or show old - depending on keytime
|
||||
if(!keytime){
|
||||
keytime = tnow;
|
||||
srand(tnow);
|
||||
shtdwnkey = rand();
|
||||
LOGDBG("Generated new shutdown key: %d", shtdwnkey);
|
||||
}
|
||||
char buf[64];
|
||||
snprintf(buf, 63, "%s=%d\n", item->key, shtdwnkey);
|
||||
sl_sock_sendstrmessage(c, buf);
|
||||
return RESULT_SILENCE;
|
||||
}
|
||||
if((int)val != shtdwnkey){
|
||||
LOGWARN("User tryes to run shutdown with wrong key");
|
||||
return RESULT_BADVAL;
|
||||
}
|
||||
LOGWARN("User give command \"shutdown\"");
|
||||
if(!mount_shutdown()) return RESULT_FAIL;
|
||||
return RESULT_OK;
|
||||
}
|
||||
|
||||
|
||||
// and all handlers collection
|
||||
static sl_sock_hitem_t handlers[] = {
|
||||
{cmd_dut1, CMD_DUT1, "set DUT1 (s)", NULL},
|
||||
{cmd_gotoaz, CMD_GOTOAZ, "point telescope by input Az/ZD and stop", NULL},
|
||||
{cmd_gotord, CMD_GOTORD, "point telescope by input RA/Dec and start tracking", NULL},
|
||||
{cmd_gotorh, CMD_GOTORH, "point telescope by input RA/HA and start tracking", NULL},
|
||||
{cmd_lst, CMD_LST, "get current local sidereal time", NULL},
|
||||
{cmd_park, CMD_PARK, "park telescope", NULL},
|
||||
{cmd_parkaz, CMD_PARKAZ, "set parking azimuth (Deg: 0 - north, 90 - east)", NULL},
|
||||
{cmd_parkzd, CMD_PARKZD, "set parking zenith distance (Deg)", NULL},
|
||||
{cmd_place, CMD_PLACE, "get place data", NULL},
|
||||
{cmd_polarx, CMD_POLARX, "set polar X value (m)", NULL},
|
||||
{cmd_polary, CMD_POLARY, "set polar Y value (m)", NULL},
|
||||
{cmd_shutdown, CMD_SHUTDOWN, "shut down mount power", NULL},
|
||||
{status, CMD_STATUS, "get mount status", NULL},
|
||||
{cmd_stop, CMD_STOP, "stop telescope", NULL},
|
||||
{cmd_stoptrk, CMD_STOPTRK, "stop tracking", NULL},
|
||||
{cmd_tagaz, CMD_TAGAZ, "get/set target azimuth (Deg)", NULL},
|
||||
{cmd_tagdec, CMD_TAGDEC, "get/set target declination (Deg)", NULL},
|
||||
{cmd_tagha, CMD_TAGHA, "get/set target hour angle (Hrs)", NULL},
|
||||
{cmd_tagmjd, CMD_TAGMJD, "get/set target MJD epoch (-1 for Jnow) or Jxxx for year, like J2050.", NULL},
|
||||
{cmd_tagra, CMD_TAGRA, "get/set target right acsention (Hrs)", NULL},
|
||||
{cmd_tagzd, CMD_TAGZD, "get/set target zenith distance (Deg)", NULL},
|
||||
{cmd_teldec, CMD_TELDEC, "get current telescope declination (Deg)", NULL},
|
||||
{cmd_telha, CMD_TELHA, "get current telescope hour angle (Hrs)", NULL},
|
||||
{cmd_telra, CMD_TELRA, "get current telescope right acsention (Hrs)", NULL},
|
||||
{cmd_telaz, CMD_TELAZ, "get current telescope azimuth (Deg)", NULL},
|
||||
{cmd_telzd, CMD_TELZD, "get current telescope zenith distance (Deg)", NULL},
|
||||
{cmd_stoptrk, CMD_STOPTRK, "stop tracking", NULL},
|
||||
{cmd_track, CMD_TRACK, "start tracking from current position", NULL},
|
||||
{dtimeh, CMD_UNIXT, "get server's UNIX time", NULL},
|
||||
{NULL, NULL, NULL, NULL}
|
||||
@@ -415,10 +578,6 @@ bool server_check(server_sock_t *sockt){
|
||||
}
|
||||
|
||||
void server_run(){
|
||||
if(isrunning){
|
||||
LOGERR("server_run(): still running!");
|
||||
return;
|
||||
}
|
||||
if(stellarium_sockfd == -1 || !cmd_socket){
|
||||
LOGERR("server_run(): not initialized");
|
||||
if(cmd_socket) sl_sock_delete(&cmd_socket);
|
||||
@@ -455,26 +614,29 @@ void server_run(){
|
||||
}
|
||||
// collect data for other fields
|
||||
HDR.status = mount_status();
|
||||
get_MJDt(NULL, &HDR.MJD);
|
||||
get_LST(&HDR.MJD, &HDR.sidtime);
|
||||
if(HDR.status != MNT_S_OFF){
|
||||
double deg1, deg2;
|
||||
if(mount_getcoords(°1, °2)){
|
||||
HDR.polar.ra = DEG2RAD(deg1);
|
||||
get_MJDt(NULL, &HDR.MJD);
|
||||
get_LST(&HDR.MJD, &HDR.sidtime);
|
||||
HDR.polar.ra = HRS2RAD(deg1);
|
||||
HDR.polar.dec = DEG2RAD(deg2);
|
||||
}
|
||||
if(mount_getaz(°1, °2)){
|
||||
HDR.altaz.az = DEG2RAD(deg1);
|
||||
HDR.altaz.zd = DEG2RAD(deg2);
|
||||
}
|
||||
if(tnow - lastweathertime > MOUNT_WEATHER_ALRM){
|
||||
weatherlost = true;
|
||||
if(HDR.status == MNT_S_TRACKING || HDR.status == MNT_S_SLEWING){
|
||||
LOGERR("Lost meteo connection -> park");
|
||||
if(mount_park()) lastweathertime = tnow;
|
||||
}
|
||||
} else weatherlost = false;
|
||||
}else{
|
||||
get_MJDt(NULL, &HDR.MJD);
|
||||
get_LST(&HDR.MJD, &HDR.sidtime);
|
||||
}
|
||||
if(tnow - lastweathertime > MOUNT_WEATHER_ALRM){
|
||||
weatherlost = true;
|
||||
if(HDR.status == MNT_S_TRACKING || HDR.status == MNT_S_SLEWING){
|
||||
LOGERR("Lost meteo connection -> park");
|
||||
if(mount_park()) lastweathertime = tnow;
|
||||
}
|
||||
} else weatherlost = false;
|
||||
getDUT(&HDR.dut);
|
||||
if(HDR.status == MNT_S_OFF || !mount_getpierside(HDR.pierside, MAX_HDR_STRLEN)) *HDR.pierside = 0;
|
||||
// and write FITS-header
|
||||
|
||||
@@ -32,6 +32,9 @@
|
||||
// max weather lost time
|
||||
#define MOUNT_WEATHER_ALRM 900.
|
||||
|
||||
// shutdown key refresh period (5min)
|
||||
#define SHTDWN_KEY_TMOUT 300
|
||||
|
||||
typedef struct{
|
||||
int cmd_isunix; // UNIX-socket instead of INET for `cmdnode`
|
||||
const char *stellport; // port of stellarium server; could be "localhost:port" for local-only work
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
#define BUFLEN 256
|
||||
|
||||
// running flag
|
||||
static volatile bool isrunning = false;
|
||||
extern volatile bool isrunning;
|
||||
static pthread_t mainthread;
|
||||
|
||||
//read: 0x14 0x0 0x0 0x0 0x5b 0x5a 0x2e 0xc6 0x8c 0x23 0x5 0x0 0x23 0x9 0xe5 0xaf 0x23 0x2e 0x34 0xed
|
||||
@@ -138,7 +138,7 @@ static bool proc_data(uint8_t *data, ssize_t len){
|
||||
* @param sockfd - socket fd for sending data
|
||||
* @return false if client disconnected
|
||||
*/
|
||||
float send_data(uint8_t *data, size_t dlen, int sockfd){
|
||||
static bool send_data(uint8_t *data, size_t dlen, int sockfd){
|
||||
ssize_t sent = send(sockfd, data, dlen, MSG_NOSIGNAL);
|
||||
if(sent != (ssize_t)dlen){
|
||||
if(sent == -1 && errno != EINTR){
|
||||
@@ -168,10 +168,19 @@ static void *handle_socket(void *sockd){
|
||||
sleep(1);
|
||||
continue;
|
||||
}
|
||||
double RA = HRS2RAD(Rhrs), Decl = DEG2RAD(Ddeg);
|
||||
/*
|
||||
// convert from Jnow to J2000
|
||||
polarCrds_t Jn = {0}, J2000 = {0};
|
||||
Jn.ra = HRS2RAD(Rhrs);
|
||||
Jn.dec = DEG2RAD(Ddeg);
|
||||
if(!JnowtoJ2000(&Jn, &J2000)) break;
|
||||
//DBG("got : %g/%g", RA, Decl);
|
||||
dout.ra = htole32(RAD2RA(RA));
|
||||
dout.dec = (int32_t)htole32(RAD2DEC(Decl));
|
||||
dout.ra = htole32(RAD2RA(J2000.ra));
|
||||
dout.dec = (int32_t)htole32(RAD2DEC(J2000.dec));
|
||||
*/
|
||||
dout.ra = htole32(RAD2RA(HRS2RAD(Rhrs)));
|
||||
dout.dec = (int32_t)htole32(RAD2DEC(DEG2RAD(Ddeg)));
|
||||
/**/
|
||||
if(!send_data((uint8_t*)&dout, sizeof(outdata), sock)) break;
|
||||
if(!sl_canread(sock)){
|
||||
sleep(1);
|
||||
@@ -199,10 +208,6 @@ static void *handle_socket(void *sockd){
|
||||
|
||||
// Main loop thread: wait connections over socket and create one thread for each
|
||||
static void* start(void *F){
|
||||
if(isrunning){
|
||||
WARNX("already running");
|
||||
return NULL;
|
||||
}
|
||||
if(!F){
|
||||
WARNX("start(): No arg");
|
||||
return NULL;
|
||||
@@ -213,7 +218,6 @@ static void* start(void *F){
|
||||
return NULL;
|
||||
}
|
||||
DBG("Start main loop, sockfd=%d", sockfd);
|
||||
isrunning = true;
|
||||
// Main loop
|
||||
while(isrunning){
|
||||
socklen_t size = sizeof(struct sockaddr_in);
|
||||
@@ -271,11 +275,13 @@ bool stellarium_start(int sockfd){
|
||||
return false;
|
||||
}
|
||||
DBG("Stellarium server started");
|
||||
LOGDBG("Stellarium server started");
|
||||
return true;
|
||||
}
|
||||
|
||||
void stellarium_stop(){
|
||||
if(!isrunning) return;
|
||||
isrunning = false;
|
||||
if(isrunning) isrunning = false;
|
||||
/*DBG("Wait for stellarium thread");
|
||||
pthread_join(mainthread, NULL);
|
||||
LOGDBG("Stellarium thread died");*/
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user