mirror of
https://github.com/eddyem/small_tel.git
synced 2025-12-06 10:45:16 +03:00
449 lines
14 KiB
C
449 lines
14 KiB
C
/*
|
|
* This file is part of the SSII project.
|
|
* Copyright 2022 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/>.
|
|
*/
|
|
|
|
#include <ctype.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <math.h>
|
|
|
|
#include "emulator.h"
|
|
#include "motlog.h"
|
|
#include "sidservo.h"
|
|
|
|
static TTY_descr *dev = NULL; // shoul be global to restore if die
|
|
static uint8_t buff[BUFLEN];
|
|
static int buflen = 0;
|
|
|
|
static uint16_t calcChecksum(uint8_t initval, uint8_t *buf, int len){
|
|
uint16_t checksum = initval; // I don't know from where does this "magick"
|
|
for(int i = 0; i < len; i++)
|
|
checksum += *buf++;
|
|
checksum ^= 0xFF00; // invert high byte
|
|
DBG("Checksum of %d bytes: 0x%04x", len, checksum);
|
|
return checksum;
|
|
}
|
|
|
|
// encoder's settings per rev
|
|
static struct {
|
|
uint32_t hamotperrev; // motors' encoders ticks per revolution
|
|
uint32_t decmotperrev;
|
|
uint32_t haencperrev; // axis' encoders ticks per revolution
|
|
uint32_t decencperrev;
|
|
int32_t hamotzero; // motors' encoders ticks @ zero position
|
|
int32_t decmotzero;
|
|
} encsettings;
|
|
|
|
/**
|
|
* @brief SSinit - open serial device and get initial info
|
|
* @return TRUE if all OK
|
|
*/
|
|
int SSinit(char *devpath, int speed){
|
|
LOGDBG("Try to open serial %s @%d", devpath, speed);
|
|
int64_t i;
|
|
dev = new_tty(devpath, speed, BUFLEN);
|
|
if(dev) dev = tty_open(dev, 1); // open exclusively
|
|
if(!dev){
|
|
LOGERR("Can't open %s with speed %d. Exit.", devpath, speed);
|
|
signals(-1);
|
|
}
|
|
for(int ntries = 0; ntries < 10; ++ntries){ // try at most 10 times
|
|
SSstat s;
|
|
DBG("Try for %dth time... ", ntries);
|
|
SSwritecmd(CMD_MOTHA);
|
|
SSwritecmd(CMD_MOTDEC);
|
|
if(!SSgetPartialstat(&s)) continue;
|
|
if(!SSgetInt(CMD_GETDECMEPR, &i)) continue;
|
|
encsettings.decmotperrev = (uint32_t) i;
|
|
LOGDBG("decmotperrev = %u", encsettings.decmotperrev);
|
|
if(!SSgetInt(CMD_GETHAMEPR, &i)) continue;
|
|
encsettings.hamotperrev = (uint32_t) i;
|
|
LOGDBG("hamotperrev = %u", encsettings.hamotperrev);
|
|
if(!SSgetInt(CMD_GETDECAEPR, &i)) continue;
|
|
encsettings.decencperrev = (uint32_t) i;
|
|
LOGDBG("decencperrev = %u", encsettings.decencperrev);
|
|
if(!SSgetInt(CMD_GETHAAEPR, &i)) continue;
|
|
encsettings.haencperrev = (uint32_t) i;
|
|
LOGDBG("haencperrev = %u", encsettings.haencperrev);
|
|
double d = (double)(s.HAenc - HA_ENC_ZEROPOS);
|
|
d /= encsettings.haencperrev;
|
|
d *= encsettings.hamotperrev;
|
|
encsettings.hamotzero = s.HAmot - (int32_t)d;
|
|
LOGDBG("hamotzero = %d", encsettings.hamotzero);
|
|
d = (double)(s.DECenc - DEC_ENC_ZEROPOS);
|
|
d /= encsettings.decencperrev;
|
|
d *= encsettings.decmotperrev;
|
|
encsettings.decmotzero = s.DECmot - (int32_t)d;
|
|
LOGDBG("decmotzero = %u", encsettings.decmotzero);
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* @brief SSclose - stop telescope and close serial device
|
|
*/
|
|
void SSclose(){
|
|
if(dev) close_tty(&dev);
|
|
}
|
|
|
|
/**
|
|
* @brief SSwrite - write command and wait for answer
|
|
* @param buf - buffer with text or binary data
|
|
* @param len - its length
|
|
* @param flags - >0: wait for binary data (until timeout), ==0: wait for '\r' or timeout, <0: don't wait at all
|
|
* @return amount of bytes read, if got answer; 0 without answer, -1 if device disconnected, -2 if can't write
|
|
*/
|
|
int SSwrite(const uint8_t *buf, int len, int flags){
|
|
DBG("try to write %d bytes", len);
|
|
#ifdef EBUG
|
|
double t00 = dtime();
|
|
for(int i = 0; i < len; ++i) if(buf[i] > 32) printf("%c", buf[i]); else printf(".");
|
|
printf("\n");
|
|
for(int i = 0; i < len; ++i) printf("0x%02x ", buf[i]);
|
|
printf("\n");
|
|
#endif
|
|
read_tty(dev); // clear incoming buffer
|
|
if(write_tty(dev->comfd, (const char*)buf, (size_t) len)){
|
|
LOGERR("Can't write data to port");
|
|
DBG("Can't write, time=%g", dtime()-t00);
|
|
return -2;
|
|
}
|
|
write_tty(dev->comfd, "\r", 1); // add EOL
|
|
if(flags < 0){
|
|
DBG("Don't wait, time=%g", dtime()-t00);
|
|
return 0;
|
|
}
|
|
buflen = 0;
|
|
double t0 = dtime();
|
|
while(dtime() - t0 < READTIMEOUT && buflen < BUFLEN){
|
|
int r = read_tty(dev);
|
|
if(r == -1){
|
|
LOGERR("Seems like tty device disconnected");
|
|
return -1;
|
|
}
|
|
if(r == 0) continue;
|
|
int rest = BUFLEN - buflen;
|
|
int out = 0;
|
|
DBG("buf[%d]=0x%02x", r-1, dev->buf[r-1]);
|
|
if(flags == 0 && dev->buf[r-1] == '\n') out = 1; // end of message
|
|
if((int)dev->buflen > rest){
|
|
dev->buflen = (size_t) rest;
|
|
out = 1;
|
|
}
|
|
memcpy(&buff[buflen], dev->buf, dev->buflen);
|
|
buflen += dev->buflen;
|
|
DBG("get %ld bytes, out=%d", dev->buflen, out);
|
|
if(out) break;
|
|
t0 = dtime();
|
|
}
|
|
DBG("got buflen=%d, time=%g", buflen, dtime()-t00);
|
|
#ifdef EBUG
|
|
for(int i = 0; i < buflen; ++i){
|
|
printf("%02x (%c) ", buff[i], (buff[i] > 31) ? buff[i] : ' ');
|
|
}
|
|
printf("\n");
|
|
#endif
|
|
return buflen;
|
|
}
|
|
|
|
// write simple command with string answer
|
|
int SSwritecmd(const uint8_t *cmd){
|
|
return SSwrite(cmd, (int)strlen((const char*)cmd), 0);
|
|
}
|
|
|
|
// write command and get answer
|
|
int SSgetInt(const uint8_t *cmd, int64_t *ans){
|
|
int r = SSwritecmd(cmd);
|
|
if(r > 0 && ans){
|
|
int l = 0;
|
|
char *ptr = (char*) SSread(&l);
|
|
ptr[l] = 0;
|
|
while(l){
|
|
if(isdigit(*ptr)) break;
|
|
++ptr; ++l;
|
|
}
|
|
if(l == 0) return 0; // no integer found
|
|
*ans = atol(ptr);
|
|
DBG("from %s get answer: %ld", buff, *ans);
|
|
}
|
|
return r;
|
|
}
|
|
|
|
/**
|
|
* @brief SSread - return buff and buflen
|
|
* @param l (o) - length of data
|
|
* @return buff or NULL if buflen == 0
|
|
*/
|
|
uint8_t *SSread(int *l){
|
|
if(!buflen) return NULL;
|
|
if(l) *l = buflen;
|
|
return buff;
|
|
}
|
|
|
|
// fill some fields of `s` by stupid string commands
|
|
int SSgetPartialstat(SSstat *s){
|
|
SSstat st = {0};
|
|
int64_t par;
|
|
int r = 0;
|
|
#define GET(cmd, field) do{if(SSgetInt(cmd, &par)){st.field = (int32_t)par; ++r;}}while(0)
|
|
GET(CMD_MOTDEC, DECmot);
|
|
GET(CMD_MOTHA, HAmot);
|
|
GET(CMD_ENCDEC, DECenc);
|
|
GET(CMD_ENCHA, HAenc);
|
|
#undef GET
|
|
if(s) *s = st;
|
|
#ifdef EBUG
|
|
if(s){
|
|
green("\nGet data:\n");
|
|
printf("DECmot=%d (0x%08x)\n", s->DECmot, (uint32_t)s->DECmot);
|
|
printf("DECenc=%d (0x%08x)\n", s->DECenc, (uint32_t)s->DECenc);
|
|
printf("RAmot=%d (0x%08x)\n", s->HAmot, (uint32_t)s->HAmot);
|
|
printf("RAenc=%d (0x%08x)\n", s->HAenc, (uint32_t)s->HAenc);
|
|
}
|
|
#endif
|
|
return r; // amount of fields with good data
|
|
}
|
|
|
|
static void parsestat(SSstat *s){
|
|
green("\nGet data:\n");
|
|
printf("DECmot=%d (0x%08x)\n", s->DECmot, (uint32_t)s->DECmot);
|
|
printf("DECenc=%d (0x%08x)\n", s->DECenc, (uint32_t)s->DECenc);
|
|
printf("DECLast=%d (0x%08x)\n", s->DecLast, (uint32_t)s->DecLast);
|
|
printf("RAmot=%d (0x%08x)\n", s->HAmot, (uint32_t)s->HAmot);
|
|
printf("RAenc=%d (0x%08x)\n", s->HAenc, (uint32_t)s->HAenc);
|
|
printf("RALast=%d (0x%08x)\n", s->HALast, (uint32_t)s->HALast);
|
|
printf("keypad=0x%02x\n", s->keypad);
|
|
printf("XBits=0x%02x\n", s->XBits);
|
|
printf("YBits=0x%02x\n", s->YBits);
|
|
printf("EBits=0x%02x\n", s->ExtraBits);
|
|
printf("ain0=%u\n", s->ain0);
|
|
printf("ain1=%u\n", s->ain1);
|
|
printf("millis=%u\n", s->millis);
|
|
printf("tF=%d\n", s->tF);
|
|
printf("V=%f\n", ((float)s->voltage)/10.f);
|
|
printf("checksum=0x%04x\n\n", s->checksum);
|
|
}
|
|
|
|
/**
|
|
* @brief SSgetstat - get struct with status & check its crc
|
|
* @param s - pointer to allocated struct (or NULL just to check)
|
|
* @return 1 if OK
|
|
*/
|
|
int SSgetstat(SSstat *s){
|
|
int r = SSwrite(CMD_GETSTAT, 3, 1);
|
|
SSstat *n;
|
|
switch(r){
|
|
case sizeof(SSstat):
|
|
n = (SSstat*) buff;
|
|
if(calcChecksum(0, buff, sizeof(SSstat)-2) != n->checksum) return FALSE;
|
|
if(s) memcpy(s, buff, sizeof(SSstat));
|
|
break;
|
|
default: return FALSE; // wrong answer size
|
|
}
|
|
#ifdef EBUG
|
|
if(s){
|
|
parsestat(s);
|
|
}
|
|
#endif
|
|
return TRUE;
|
|
}
|
|
|
|
// send short/long binary command
|
|
static int bincmd(void *cmd, int len){
|
|
//uint8_t *buf = MALLOC(uint8_t, len + 3);
|
|
if(len == sizeof(SSscmd)){
|
|
((SSscmd*)cmd)->checksum = calcChecksum(0, cmd, len-2);
|
|
SSwrite(CMD_SHORTCMD, 3, -1);
|
|
//memcpy(buf, CMD_SHORTCMD, 3);
|
|
//memcpy(buf+3, cmd, len);
|
|
}else if(len == sizeof(SSlcmd)){
|
|
((SSlcmd*)cmd)->checksum = calcChecksum(0, cmd, len-2);
|
|
SSwrite(CMD_LONGCMD, 3, -1);
|
|
//memcpy(buf, CMD_LONGCMD, 3);
|
|
//memcpy(buf+3, cmd, len);
|
|
}else{
|
|
//free(buf);
|
|
return -1;
|
|
}
|
|
//len += 3;
|
|
DBG("Write %d bytes", len);
|
|
int r = SSwrite(cmd, len, 1);
|
|
#ifdef EBUG
|
|
if(r == sizeof(SSscmd)) parsestat((SSstat*)buff);
|
|
#endif
|
|
return r;
|
|
}
|
|
int SScmds(SSscmd *cmd){
|
|
int r = bincmd(cmd, sizeof(SSscmd));
|
|
if(r < 0) return -1;
|
|
DBG("Got %d bytes answer", r);
|
|
return r;
|
|
}
|
|
int SScmdl(SSlcmd *cmd){
|
|
int r = bincmd(cmd, sizeof(SSlcmd));
|
|
if(r < 0) return -1;
|
|
DBG("Got %d bytes answer", r);
|
|
return r;
|
|
}
|
|
|
|
// return d in 0..360 for isdec==1 and -180..180 for isdec==0
|
|
static double normangle(double d, int isdec){
|
|
if(d < -360.) d = fmod((fmod(d, 360.) + 360.), 360.);
|
|
else d = fmod(d + 360., 360.);
|
|
if(isdec) return d;
|
|
if(d < 180.) return d;
|
|
return (d - 180.);
|
|
}
|
|
|
|
/**
|
|
* @brief SSticks2deg - convert motor ticks to degrees
|
|
* @param ticks - motor ticks
|
|
* @param isdec == 1 if it is DEC axe
|
|
* @return degrees
|
|
*/
|
|
double SSticks2deg(int32_t ticks, int isdec){
|
|
double denom = (isdec) ? encsettings.decmotperrev : encsettings.hamotperrev;
|
|
int32_t zp = (isdec) ? encsettings.decmotzero : encsettings.hamotzero;
|
|
return normangle(360. * (ticks - zp) / denom, isdec);
|
|
}
|
|
/**
|
|
* @brief SSenc2deg - convert encoder ticks to degrees
|
|
* @param ticks - motor ticks
|
|
* @param isdec == 1 if it is DEC axe
|
|
* @return degrees
|
|
*/
|
|
double SSenc2deg(int32_t ticks, int isdec){
|
|
double denom = (isdec) ? encsettings.decencperrev : encsettings.haencperrev;
|
|
int32_t zp = (isdec) ? DEC_ENC_ZEROPOS : HA_ENC_ZEROPOS;
|
|
return normangle(360. * (ticks - zp) / denom, isdec);
|
|
}
|
|
|
|
/**
|
|
* @brief SSdeg2ticks - convert degrees to motor ticks
|
|
* @param d - angle
|
|
* @param isdec == 1 if it is DEC axe
|
|
* @return ticks
|
|
*/
|
|
int32_t SSdeg2ticks(double d, int isdec){
|
|
double k = (isdec) ? encsettings.decmotperrev : encsettings.hamotperrev;
|
|
int32_t zp = (isdec) ? encsettings.decmotzero : encsettings.hamotzero;
|
|
return (int32_t)(k * normangle(d, isdec) / 360.) + zp;
|
|
|
|
}
|
|
/**
|
|
* @brief SSdeg2enc - convert degrees to encoder ticks
|
|
* @param d - angle
|
|
* @param isdec == 1 if it is DEC axe
|
|
* @return ticks
|
|
*/
|
|
int32_t SSdeg2enc(double d, int isdec){
|
|
double k = (isdec) ? encsettings.decencperrev : encsettings.haencperrev;
|
|
int32_t zp = (isdec) ? DEC_ENC_ZEROPOS : HA_ENC_ZEROPOS;
|
|
return (int32_t)(k * normangle(d, isdec) / 360.) + zp;
|
|
}
|
|
|
|
// convert speed `dps` in degrees per second into ticks for `XS`/`YS` commands
|
|
int32_t SSdeg2spd(double dps, int isdec){
|
|
double k = (isdec) ? encsettings.decmotperrev : encsettings.hamotperrev;
|
|
return (int32_t)(k * dps * 65536./1953./360.);
|
|
}
|
|
|
|
// convert ticks for `XS`/`YS` into degrees per second
|
|
double SSspd2deg(int32_t ticks, int isdec){
|
|
;
|
|
}
|
|
|
|
/**
|
|
* @brief SSgoto - move telescope to given angle
|
|
* @param ha - Hour Angle (degrees)
|
|
* @param dec - Declination (degrees)
|
|
* @return TRUE if OK
|
|
*/
|
|
int SSgoto(double ha, double dec){
|
|
char buf[32];
|
|
if(ha < 0. || ha > 360.) return FALSE;
|
|
if(dec < -90. || dec > 90.) return FALSE;
|
|
int32_t raticks = SSdeg2ticks(ha, 0);
|
|
int32_t decticks = SSdeg2ticks(dec, 1);
|
|
snprintf(buf, 31, "%s%u", CMD_MOTDEC, decticks);
|
|
SSwritecmd((uint8_t*)buf);
|
|
snprintf(buf, 31, "%s%u", CMD_MOTHA, raticks);
|
|
SSwritecmd((uint8_t*)buf);
|
|
return TRUE;
|
|
}
|
|
|
|
// wait till moving ends
|
|
void SSwaitmoving(){
|
|
SSstat s = {0};
|
|
double t0 = dtime();
|
|
int32_t oldRAm = 0, oldDm = 0;
|
|
int first = 1, ctr = 0;
|
|
do{
|
|
if(!SSlog_motor_data(&s, &t0)) continue;
|
|
DBG("Moving: HA=%g, DEC=%g", SSenc2deg(s.HAenc, 0), SSenc2deg(s.DECenc, 1));
|
|
if(first) first = 0;
|
|
else if(s.HAmot == oldRAm && s.DECmot == oldDm){
|
|
if(++ctr > 2) return;
|
|
}else ctr = 0;
|
|
oldRAm = s.HAmot;
|
|
oldDm = s.DECmot;
|
|
}while(dtime() - t0 < 3.); // simplest timeout
|
|
DBG("Moving ends (or timeout ends)");
|
|
}
|
|
|
|
int SScatchtarg(double ra, double dec){
|
|
;
|
|
}
|
|
|
|
#if 0
|
|
The controller loops 1953 times per second. Fields that reference a speed/velocity/rate (e.g. the XS command) express this speed as a 32-bit number representing the number of ticks the motor encoder should advance per loop, multiplied by 2^16 (= 65,536) to express fractional values.
|
|
|
|
For example, if you want to advance the motor 1,000 counts per second, this corresponds to:
|
|
|
|
1,000 / 1953 ~= 0.512 counts per loop
|
|
|
|
which is expressed as the integer value:
|
|
|
|
round(0.512 * 65,536) = 33,555
|
|
|
|
So you would send the command ?XS33555\r? to set a max speed of 1,000 counts per second.
|
|
|
|
Note that 65,536 / 1,953 = 33.55657962109575, and 1,953 / 65,536 = 0.0298004150390625, so you can simply do:
|
|
|
|
CountsPerSecToSpeedValue(cps): return round(cps * 33.55657962109575)
|
|
|
|
SpeedValueToCountsPerSec(speed): return round(speed * 0.0298004150390625)
|
|
|
|
The SiTech Operations Manual describes the following ?C++? functions (converted to something like Python here) to do similar conversions:
|
|
|
|
def DegsPerSec2MotorSpeed(dps, ticksPerRev):
|
|
|
|
return round(ticksPerRev * dps * 0.09321272116971)
|
|
|
|
def MotorSpeed2DegsPerSec(speed, ticksPerRev):
|
|
|
|
return round(speed / ticksPerRev * 10.7281494140625)
|
|
|
|
These coefficients can be derived as follows:
|
|
|
|
10.7281494140625 = 0.0298004150390625 * 360 degs/rev
|
|
|
|
0.09321272116971 = 33.55657962109575 / 360 degs/rev
|
|
|
|
#endif
|