Files
small_tel/Daemons/10micron_stellarium/stellarium.c

285 lines
8.9 KiB
C

/*
* This file is part of the mountdaemon_10micron project.
* Copyright 2026 Edward V. Emelianov <edward.emelianoff@gmail.com>.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
// base functions for sending/receiving data in stellarium protocol
#include <stdint.h>
#include <usefull_macros.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "angles.h"
#include "mount.h"
#include "stellarium.h"
#define BUFLEN 256
// running flag
static volatile bool isrunning = false;
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
// command: goto 16h29 24.45 -26d25 55.62
/*
LITTLE-ENDIAN!!!
from client:
LENGTH (2 bytes, integer): length of the message
TYPE (2 bytes, integer): 0
TIME (8 bytes, integer): current time on the server computer in microseconds
since 1970.01.01 UT. Currently unused.
RA (4 bytes, unsigned integer): right ascension of the telescope (J2000)
a value of 0x100000000 = 0x0 means 24h=0h,
a value of 0x80000000 means 12h
DEC (4 bytes, signed integer): declination of the telescope (J2000)
a value of -0x40000000 means -90degrees,
a value of 0x0 means 0degrees,
a value of 0x40000000 means 90degrees
to client:
LENGTH (2 bytes, integer): length of the message
TYPE (2 bytes, integer): 0
TIME (8 bytes, integer): current time on the server computer in microseconds
since 1970.01.01 UT. Currently unused.
RA (4 bytes, unsigned integer): right ascension of the telescope (J2000)
a value of 0x100000000 = 0x0 means 24h=0h,
a value of 0x80000000 means 12h
DEC (4 bytes, signed integer): declination of the telescope (J2000)
a value of -0x40000000 means -90degrees,
a value of 0x0 means 0degrees,
a value of 0x40000000 means 90degrees
STATUS (4 bytes, signed integer): status of the telescope, currently unused.
status=0 means ok, status<0 means some error
*/
#define DEG2DEC(degr) ((int32_t)(degr / 90. * ((double)0x40000000)))
#define DEG2RA(degr) ((uint32_t)(degr / 180. * ((double)0x80000000)))
#define HRS2RA(degr) ((uint32_t)(degr / 12. * ((double)0x80000000)))
#define DEC2DEG(i32) (((double)i32)*90./((double)0x40000000))
#define RA2DEG(u32) (((double)u32)*180. /((double)0x80000000))
#define RA2HRS(u32) (((double)u32)*12. /((double)0x80000000))
typedef struct __attribute__((__packed__)){
uint16_t len;
uint16_t type;
uint64_t time;
uint32_t ra;
int32_t dec;
} indata;
typedef struct __attribute__((__packed__)){
uint16_t len;
uint16_t type;
uint64_t time;
uint32_t ra;
int32_t dec;
int32_t status;
} outdata;
/**
* @brief proc_data - process data received from Stellarium
* @param data - raw data
* @param len - its length
* @return true if all OK
*/
static bool proc_data(uint8_t *data, ssize_t len){
FNAME();
if(len != sizeof(indata)){
WARNX("Bad data size: got %zd instead of %zd!", len, sizeof(indata));
return false;
}
indata *dat = (indata*)data;
uint16_t L, T;
uint32_t ra;
int32_t dec;
#if __BYTE_ORDER__ != __ORDER_LITTLE_ENDIAN__
L = le16toh(dat->len); T = le16toh(dat->type);
ra = le32toh(dat->ra);
dec = (int32_t)le32toh((uint32_t)dat->dec);
#else
L = dat->len; T = dat->type;
ra = dat->ra; dec = dat->dec;
#endif
DBG("got message with len %u & type %u", L, T);
if(L != len){
WARNX("Length of message != msg->len");
return false;
}
if(T){
WARNX("Wrong message type");
return false;
}
// convert RA/DEC to degrees
double tagRA = RA2DEG(ra), tagDec = DEC2DEG(dec);
DBG("RA: %u (%g degr), DEC: %d (%g degr)", ra, tagRA, dec, tagDec);
LOGMSG("(stellarium) RA: %u (%g degr), DEC: %d (%g degr)", ra, tagRA, dec, tagDec);
// check RA/DEC
horizCrds_t hnow; // without refraction
polarCrds_t p2000, pnow;
p2000.ra = DEG2RAD(tagRA);
p2000.dec = DEG2RAD(tagDec);
// now J2000 obs Jnow
if(!get_ObsPlace(NULL, &p2000, &pnow, &hnow)){
WARNX("Can't convert coordinates to Jnow");
return false;
}
tagRA = RAD2DEG(pnow.ra - pnow.eo);
tagDec = RAD2DEG(pnow.dec);
return (mount_setInpRA(tagRA) && mount_setInpDec(tagDec));
}
/**
* Send data to user
* @param data - data to send
* @param dlen - data length
* @param sockfd - socket fd for sending data
* @return false if client disconnected
*/
float 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){
WARN("Disconnected?");
return false;
}
WARN("write()");
}
return true;
}
/**
* main socket service procedure
*/
static void *handle_socket(void *sockd){
FNAME();
if(!isrunning) return NULL;
outdata dout;
int sock = *(int*)sockd;
dout.len = htole16(sizeof(outdata));
dout.type = 0;
while(isrunning){
// get coordinates
double RA = 0., Decl = 0.;
if((dout.status = mount_getcoords(&RA, &Decl)) == MNT_S_ERROR){
WARNX("Error: can't get coordinates");
sleep(1);
continue;
}
//DBG("got : %g/%g", RA, Decl);
dout.ra = htole32(HRS2RA(RA));
dout.dec = (int32_t)htole32(DEG2DEC(Decl));
if(!send_data((uint8_t*)&dout, sizeof(outdata), sock)) break;
//DBG("sent ra = %g, dec = %g", RA2HRS(dout.ra), DEC2DEG(dout.dec));
if(!sl_canread(sock)) continue;
// fill incoming buffer
uint8_t buff[BUFLEN];
ssize_t rd = read(sock, buff, BUFLEN-1);
if(rd <= 0){ // error or disconnect
DBG("Nothing to read from fd %d (ret: %zd)", sock, rd);
WARNX("Client disconnected?");
break;
}
buff[rd] = 0;
DBG("read %zd (%s)", rd, buff);
if(!proc_data(buff, rd)) dout.status = -1;
else dout.status = 0;
}
close(sock);
return NULL;
}
// 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;
}
int sockfd = *((int*)F);
if(sockfd < 0){
WARNX("start(): sockfd=%d", sockfd);
return NULL;
}
DBG("Start main loop, sockfd=%d", sockfd);
isrunning = true;
// Main loop
while(isrunning){
socklen_t size = sizeof(struct sockaddr_in);
struct sockaddr_in myaddr;
int newsock;
newsock = accept(sockfd, (struct sockaddr*)&myaddr, &size);
if(newsock <= 0){
if(errno == EAGAIN) continue; // nothing available
WARN("accept()");
LOGWARN("Stellarium socket error in accept()");
sleep(1);
continue;
}
struct sockaddr_in peer;
socklen_t peer_len = sizeof(peer);
if(getpeername(newsock, (struct sockaddr*)&peer, &peer_len) == -1){
LOGWARN("Stellarium socket error in getpeername()");
WARN("getpeername()");
close(newsock);
continue;
}
int sockport = -1;
if(getsockname(newsock, (struct sockaddr*)&peer, &peer_len) == 0){
sockport = ntohs(peer.sin_port);
}
char *peerIP = inet_ntoa(peer.sin_addr);
LOGMSG("Got connection from %s @ %d", peerIP, sockport);
DBG("Peer's IP address is: %s (@port %d)\n", peerIP, sockport);
/*if(strcmp(peerIP, ACCEPT_IP) && strcmp(peerIP, "127.0.0.1")){
WARNX("Wrong IP");
close(newsock);
continue;
}*/
pthread_t rthrd;
if(pthread_create(&rthrd, NULL, handle_socket, (void*)&newsock)){
WARN("Can't create socket thread");
LOGERR("Stellarium socket: error creating listen thread");
}else{
DBG("Thread created, detouch");
pthread_detach(rthrd); // don't care about thread state
}
}
close(sockfd);
return NULL;
}
bool stellarium_start(int sockfd){
int fd = sockfd;
if(pthread_create(&mainthread, NULL, start, (void*)&fd)){
WARN("pthread_create()");
return false;
}
DBG("Stellarium server started");
return true;
}
void stellarium_stop(){
if(!isrunning) return;
isrunning = false;
pthread_join(mainthread, NULL);
}