From 3724ce87ee3953d54cd83d993320320e2fdfe10b Mon Sep 17 00:00:00 2001 From: Robert Benedict Date: Mon, 10 Dec 2018 14:18:15 -0500 Subject: [PATCH] New iOptron rotator backend I have an potential initial release of a backend for iOptron alt-az mounts. I have tested it and it appears to work well both from the command line and driven by Gpredict with my iEQ45Pro. I ran the source through astyle and I think that it's OK. Compiling and testing was done under Unbuntu 18. Bob KD8CGH --- NEWS | 1 + configure.ac | 6 +- include/hamlib/rotlist.h | 12 ++ ioptron/Android.mk | 12 ++ ioptron/Makefile.am | 6 + ioptron/README_ioptron | 10 ++ ioptron/rot_ioptron.c | 350 +++++++++++++++++++++++++++++++++++++++ ioptron/rot_ioptron.h | 31 ++++ src/rot_reg.c | 2 + 9 files changed, 428 insertions(+), 2 deletions(-) create mode 100644 ioptron/Android.mk create mode 100644 ioptron/Makefile.am create mode 100644 ioptron/README_ioptron create mode 100644 ioptron/rot_ioptron.c create mode 100644 ioptron/rot_ioptron.h diff --git a/NEWS b/NEWS index 139ebcfc2..873204a3f 100644 --- a/NEWS +++ b/NEWS @@ -10,6 +10,7 @@ Version 4.0 * API/ABI changes, advance ABI to 4 0 0. * Add GPIO and GPION options for DCD. Jeroen Vreeken * New backend: ELAD FDM DUO. Giovanni, HB9EIK. + * New rotator backend: iOptron. Bob, KD8CGH Version 3.3 2018-08-12 diff --git a/configure.ac b/configure.ac index 7f0231809..3d4df381f 100644 --- a/configure.ac +++ b/configure.ac @@ -50,7 +50,7 @@ dnl Beware of duplication should a backend directory include both rig and dnl rotor definitions, e.g. "dummy". Optional backends will not be listed dnl here but will be added later, e.g. "winradio". BACKEND_LIST="adat alinco aor barrett dorji drake dummy elad flexradio icom icmarine jrc kachina kenwood kit lowe pcr prm80 racal rft rs skanti tapr tentec tuner uniden wj yaesu" -ROT_BACKEND_LIST="amsat ars celestron cnctrk easycomm ether6 fodtrack gs232a heathkit m2 meade rotorez sartek spid ts7400 prosistel" +ROT_BACKEND_LIST="amsat ars celestron cnctrk easycomm ether6 fodtrack gs232a heathkit m2 meade rotorez sartek spid ts7400 prosistel ioptron" dnl See README.release on setting these values # Values given to -version-info when linking. See libtool documentation. @@ -737,6 +737,7 @@ macros/Makefile include/Makefile lib/Makefile dummy/Makefile +elad/Makefile yaesu/Makefile icom/Makefile icmarine/Makefile @@ -788,8 +789,9 @@ prosistel/Makefile dorji/Makefile barrett/Makefile meade/Makefile +ioptron/Makefile hamlib.pc -elad/Makefile]) +]) AC_OUTPUT diff --git a/include/hamlib/rotlist.h b/include/hamlib/rotlist.h index 9afdf0ac8..a40268730 100644 --- a/include/hamlib/rotlist.h +++ b/include/hamlib/rotlist.h @@ -361,6 +361,18 @@ #define ROT_BACKEND_MEADE "meade" #define ROT_MODEL_MEADE ROT_MAKE_MODEL(ROT_MEADE, 1) +/** + * \def ROT_MODEL_IOPTRON + * \brief A macro that returns the model number of the IOPTRON backend. + * + * The IOPTRON backen can be used with IOPTRON telescope mounts + */ +#define ROT_IOPTRON 19 +#define ROT_BACKEND_IOPTRON "ioptron" +#define ROT_MODEL_IOPTRON ROT_MAKE_MODEL(ROT_IOPTRON, 1) + + + /** * \typedef typedef int rot_model_t diff --git a/ioptron/Android.mk b/ioptron/Android.mk new file mode 100644 index 000000000..439c0b482 --- /dev/null +++ b/ioptron/Android.mk @@ -0,0 +1,12 @@ +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := rot_ioptron.c +LOCAL_MODULE := rot_ioptron + +LOCAL_CFLAGS := -DHAVE_CONFIG_H +LOCAL_C_INCLUDES := android include src +LOCAL_LDLIBS := -lhamlib -Lobj/local/armeabi + +include $(BUILD_STATIC_LIBRARY) diff --git a/ioptron/Makefile.am b/ioptron/Makefile.am new file mode 100644 index 000000000..220f11da4 --- /dev/null +++ b/ioptron/Makefile.am @@ -0,0 +1,6 @@ +IOPTRONSRC = rot_ioptron.c rot_ioptron.h + +noinst_LTLIBRARIES = libhamlib-ioptron.la +libhamlib_ioptron_la_SOURCES = $(IOPTRONSRC) + +EXTRA_DIST = Android.mk diff --git a/ioptron/README_ioptron b/ioptron/README_ioptron new file mode 100644 index 000000000..d001c0431 --- /dev/null +++ b/ioptron/README_ioptron @@ -0,0 +1,10 @@ +This is a small backend for iOptron alt-az mounts. It implements get position, goto position, stop and get info. It has been tested from the command line and with Gpredict with an iEQ45 Pro in alt-az mode. It should work for other iOptron alt-az mounts such as the AZ Mount Pro, Cube-II, CubePro and iEQ30 Pro in alt-az mode as iOptron claims that they all use the same command language. + +A warning on iOptron command language. iOptron is in the process of moving from V2 to V3 of the command lamguage and presently (December 2018) uses a mix of the two. This back end should work with mounts with current firmware, but new firmware may change commands and break it. + +Minor annoyance - the mount will automatically start siderial tracking after a slew. Adding a stop tracking command at the end of the goto set did not solve the problem. + +Bigger annoyance - Be careful with the GEM mounts (iEQ30, iEQ45) in alt-az mode. The dec/el motor cord easily catches on the ra/az clutch knobs when slewing in az. I attached a small standoff to keep the cord out of the way. + +Reccomendation - since the hand controller remains plugged, in I suggest that you use it for calibration, manual slews and setting and going to the zero position. + diff --git a/ioptron/rot_ioptron.c b/ioptron/rot_ioptron.c new file mode 100644 index 000000000..d8671d6c5 --- /dev/null +++ b/ioptron/rot_ioptron.c @@ -0,0 +1,350 @@ +/* + * Hamlib Rotator backend - Celestron + * Copyright (c) 2011 by Stephane Fillod + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hamlib/rotator.h" +#include "serial.h" +#include "misc.h" +#include "register.h" + +#include "rot_ioptron.h" + +#define ACK "#" +#define ACK1 '1' + +#define BUFSZ 128 + +/** + * ioptron_transaction + * + * cmdstr - Command to be sent to the rig. + * data - Buffer for reply string. Can be NULL, indicating that no reply is + * is needed, but answer will still be read. + * data_len - in: Size of buffer. It is the caller's responsibily to provide + * a large enough buffer for all possible replies for a command. + * + * COMMANDS note: as of 12/2018 a mixture of V2 and V3 + * | TTTTTTTT(T) .01 arc seconds + * | alt- sign with 8 digits, az - 9 digits | + * | Command | Atribute | Return value | Description | + * -------------------------------------------------------------------| + * | :GAC# | .01 arcsec | sTTTTTTTTTTTTTTTTT# | gets alt(s8), az(9) | + * | :SzTTTTTTTTT# | .01 arcsec | '1' == OK | Set Target azimuth | + * | :SasTTTTTTTT# |.01 arcsec | '1' == OK | Set Target elevation | + * | :Q# | - | '1' == OK | Halt all slewing | + * | :ST0# | - | '1' == OK | Halt tracking | + * | :MS# | - | '1' == OK | GoTo Target | + * | + * returns: + * RIG_OK - if no error occured. + * RIG_EIO - if an I/O error occured while sending/receiving data. + * RIG_ETIMEOUT - if timeout expires without any characters received. + */ + +static int +ioptron_transaction(ROT *rot, const char *cmdstr, + char *data, size_t data_len) +{ + struct rot_state *rs; + int retval; + int retry_read = 0; + char replybuf[BUFSZ]; + + rs = &rot->state; + +transaction_write: + + serial_flush(&rs->rotport); + + if (cmdstr) + { + retval = write_block(&rs->rotport, cmdstr, strlen(cmdstr)); + + if (retval != RIG_OK) + { + goto transaction_quit; + } + } + + /** Always read the reply to know whether the cmd went OK */ + if (!data) + { + data = replybuf; + } + + if (!data_len) + { + data_len = BUFSZ; + } + + /** the answer */ + memset(data, 0, data_len); + retval = read_string(&rs->rotport, data, data_len, ACK, strlen(ACK)); + + if (retval < 0) + { + if (retry_read++ < rot->state.rotport.retry) + { + goto transaction_write; + } + + goto transaction_quit; + } + + /** check for acknowledge */ + if (retval < 1) + { + rig_debug(RIG_DEBUG_ERR, "%s: unexpected response, len %d: '%s'\n", __func__, + retval, data); + return -RIG_EPROTO; + } + + retval = RIG_OK; +transaction_quit: + return retval; +} + +/** + * Opens the Port and sets all needed parametes for operation + * as of 12/2018 initiates mount with V3 :MountInfo# + */ +static int ioptron_open(ROT *rot) +{ + rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); + + return ioptron_transaction(rot, ":Mountinfo#", NULL, 0); +} + +/** sets mount position, requires 4 steps + * set azmiuth + * set altitude + * goto set + * stop tracking - mount starts tracking after goto + */ +static int +ioptron_set_position(ROT *rot, azimuth_t az, elevation_t el) +{ + char cmdstr[32]; + char retbuf[10]; + int retval; + float faz, fel; + + rig_debug(RIG_DEBUG_TRACE, "%s called: %f %f\n", __func__, az, el); + +/* units .01 arc sec */ + faz = az * 360000; + fel = el * 360000; +/* set azmiuth, returns '1" if OK */ + sprintf(cmdstr, ":Sz%09.0f#", faz); + retval = ioptron_transaction(rot, cmdstr, retbuf, sizeof(retbuf)); + + if (retval != RIG_OK || retbuf[0] != ACK1) + { + return -RIG_EPROTO; + } + +/* set altitude, returns '1" if OK */ + sprintf(cmdstr, ":Sa+%08.0f#", fel); + retval = ioptron_transaction(rot, cmdstr, retbuf, sizeof(retbuf)); + + if (retval != RIG_OK || retbuf[0] != ACK1) + { + return -RIG_EPROTO; + } + +/* move to set target, V2 command, returns '1" if OK */ + sprintf(cmdstr, ":MS#"); // + retval = ioptron_transaction(rot, cmdstr, retbuf, sizeof(retbuf)); + + if (retval != RIG_OK || retbuf[0] != ACK1) + { + return -RIG_EPROTO; + } + +/* stop tracking, V2 command, returns '1" if OK */ + sprintf(cmdstr, ":ST0#"); + retval = ioptron_transaction(rot, cmdstr, retbuf, sizeof(retbuf)); + + if (retval != RIG_OK || retbuf[0] != ACK1) + { + return -RIG_EPROTO; + } + + return retval; +} + +/** gets current position */ +static int +ioptron_get_position(ROT *rot, azimuth_t *az, elevation_t *el) +{ + char posbuf[32]; + int retval; + float w; + + rig_debug(RIG_DEBUG_TRACE, "%s called\n", __func__); + + /** Get Az-Alt */ + retval = ioptron_transaction(rot, ":GAC#", posbuf, sizeof(posbuf)); + + if (retval != RIG_OK || strlen(posbuf) < 18) + { + return retval < 0 ? retval : -RIG_EPROTO; + } + + if (sscanf(posbuf, "%9f", &w) != 1) + { + return -RIG_EPROTO; + } + + /** convert from .01 arc sec to degrees */ + *el = ((elevation_t)w / 360000.); + + if (sscanf(posbuf + 9, "%9f", &w) != 1) + { + return -RIG_EPROTO; + } + + *az = ((azimuth_t)w / 360000.); + + rig_debug(RIG_DEBUG_TRACE, "%s: (az, el) = (%.1f, %.1f)\n", + __func__, *az, *el); + + return RIG_OK; +} + +/** stop everything **/ +static int +ioptron_stop(ROT *rot) +{ + int retval; + char retbuf[10]; + + rig_debug(RIG_DEBUG_TRACE, "%s called\n", __func__); + + /** stop slew, returns "1" if OK */ + retval = ioptron_transaction(rot, ":Q#", retbuf, 10); + + if (retval != RIG_OK || retbuf[0] != ACK1) + { + return -RIG_EPROTO; + } + + /** stops tracking returns "1" if OK */ + retval = ioptron_transaction(rot, ":ST0#", retbuf, 10); + + if (retval != RIG_OK || retbuf[0] != ACK1) + { + return -RIG_EPROTO; + } + + return retval; +} + +/** get mount type code, initializes mount */ +static const char * +ioptron_get_info(ROT *rot) +{ + static char info[16]; + char str[6]; + + rig_debug(RIG_DEBUG_TRACE, "%s called\n", __func__); + int retval; + + retval = ioptron_transaction(rot, ":MountInfo#", str, sizeof(str)); + + rig_debug(RIG_DEBUG_TRACE, "retval, RIG_OK str %d %d %str\n", retval, RIG_OK, + str); + + sprintf(info, "MountInfo %s", str); + + return info; +} + + + +/** ************************************************************************* + * + * ioptron mount capabilities. + * + * Protocol documentation: + * from ioptron: + * RS232-Command_Language pdf + * note that iOptron is currently (12/2018) using a mix of V2 and V3 commands :( + */ + +const struct rot_caps ioptron_rot_caps = +{ + .rot_model = ROT_MODEL_IOPTRON, + .model_name = "iOptron", + .mfg_name = "iOptron", + .version = "0.1", + .copyright = "LGPL", + .status = RIG_STATUS_ALPHA, + .rot_type = ROT_TYPE_AZEL, + .port_type = RIG_PORT_SERIAL, + .serial_rate_min = 9600, + .serial_rate_max = 9600, + .serial_data_bits = 8, + .serial_stop_bits = 1, + .serial_parity = RIG_PARITY_NONE, + .serial_handshake = RIG_HANDSHAKE_NONE, + .write_delay = 0, + .post_write_delay = 0, + .timeout = 1000, /* worst case scenario 3500 */ + .retry = 1, + + .min_az = 0.0, + .max_az = 360.0, + .min_el = 0.0, + .max_el = 180.0, + + .rot_open = ioptron_open, + .get_position = ioptron_get_position, + .set_position = ioptron_set_position, + .stop = ioptron_stop, + .get_info = ioptron_get_info, +}; + +/* ****************************************************************** */ + +DECLARE_INITROT_BACKEND(ioptron) +{ + rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __FUNCTION__); + + rot_register(&ioptron_rot_caps); + + return RIG_OK; +} + +/* ****************************************************************** */ +/* end of file */ + diff --git a/ioptron/rot_ioptron.h b/ioptron/rot_ioptron.h new file mode 100644 index 000000000..9918c4258 --- /dev/null +++ b/ioptron/rot_ioptron.h @@ -0,0 +1,31 @@ +/* + * Hamlib Meade telescope rotor backend - main header + * Copyright (c) 2018 by Andreas Mueller (DC1MIL) + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef _IOPTRON_H +#define _IOPTRON_H 1 + +#define BUFSIZE 128 +#define CR "\r" +#define LF "\x0a" + +extern const struct rot_caps ioptron_caps; + +#endif /* _IOPTRON_H */ diff --git a/src/rot_reg.c b/src/rot_reg.c index 7d04acf7b..7c7f6da50 100644 --- a/src/rot_reg.c +++ b/src/rot_reg.c @@ -82,6 +82,7 @@ DEFINE_INITROT_BACKEND(ether6); DEFINE_INITROT_BACKEND(cnctrk); DEFINE_INITROT_BACKEND(prosistel); DEFINE_INITROT_BACKEND(meade); +DEFINE_INITROT_BACKEND(ioptron); /** * \def ROT_BACKEND_LIST @@ -119,6 +120,7 @@ static struct { ROT_CNCTRK, ROT_BACKEND_CNCTRK, ROT_FUNCNAMA(cnctrk) }, { ROT_PROSISTEL, ROT_BACKEND_PROSISTEL, ROT_FUNCNAMA(prosistel) }, { ROT_MEADE, ROT_BACKEND_MEADE, ROT_FUNCNAMA(meade) }, + { ROT_IOPTRON, ROT_BACKEND_IOPTRON, ROT_FUNCNAMA(ioptron) }, { 0, NULL }, /* end */ };