fix some bugs

This commit is contained in:
2026-09-28 17:50:25 +03:00
parent 30b7c9bc9e
commit 06dd7d7059
18 changed files with 899 additions and 170 deletions

View File

@@ -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#"

View File

@@ -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

View 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...

View File

@@ -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

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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(){

View File

@@ -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:

View File

@@ -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;

View File

@@ -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,

View File

@@ -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);
}

View File

@@ -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();

View File

@@ -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>

View File

@@ -1,4 +1,5 @@
10micron_commands.h
Readme.md
angles.c
angles.h
args.c

View File

@@ -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(&deg1, &deg2)){
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(&deg1, &deg2)){
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

View File

@@ -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

View File

@@ -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");*/
}