From 8ba011eaf7dbcd9ae9b7f36d4d36cbd3b9b94627 Mon Sep 17 00:00:00 2001 From: Nonoo Date: Sun, 15 Nov 2020 12:46:35 +0100 Subject: [PATCH] Add INDI rotator backend This backend lets Hamlib control an astronomical (telescope) rotator through an INDI (https://indilib.org/) server. --- AUTHORS | 5 + INSTALL | 1 + configure.ac | 42 +++ include/hamlib/rotlist.h | 14 + macros/Makefile.am | 2 + macros/ax_lib_indi.m4 | 23 ++ macros/ax_lib_nova.m4 | 23 ++ rotators/indi/Android.mk | 12 + rotators/indi/Makefile.am | 6 + rotators/indi/README.indi.md | 8 + rotators/indi/indi.c | 70 ++++ rotators/indi/indi_wrapper.cpp | 621 +++++++++++++++++++++++++++++++++ rotators/indi/indi_wrapper.h | 36 ++ rotators/indi/indi_wrapper.hpp | 71 ++++ src/Makefile.am | 2 +- src/rot_reg.c | 6 + 16 files changed, 941 insertions(+), 1 deletion(-) create mode 100644 macros/ax_lib_indi.m4 create mode 100644 macros/ax_lib_nova.m4 create mode 100644 rotators/indi/Android.mk create mode 100644 rotators/indi/Makefile.am create mode 100644 rotators/indi/README.indi.md create mode 100644 rotators/indi/indi.c create mode 100644 rotators/indi/indi_wrapper.cpp create mode 100644 rotators/indi/indi_wrapper.h create mode 100644 rotators/indi/indi_wrapper.hpp diff --git a/AUTHORS b/AUTHORS index 7f48f4783..36aa525a1 100644 --- a/AUTHORS +++ b/AUTHORS @@ -265,6 +265,10 @@ C: Chris Bryant, G3WIE M: Øystein Hårberg, LA7LKA +[indi] +M: Norbert Varga, HA2NON + + [Frontend] M: Stephane Fillod, F8CFE @@ -362,3 +366,4 @@ Alexander Sack Nirgal Vourgère Andrew Errington Kārlis Millers YL3ALK +Norbert Varga HA2NON diff --git a/INSTALL b/INSTALL index 81b451bf3..23034b25c 100644 --- a/INSTALL +++ b/INSTALL @@ -287,6 +287,7 @@ are: --with-tcl-binding build Tcl binding and demo [default=no] --with-tcl=PATH directory containing tcl configuration (tclConfig.sh) --with-lua-binding build lua binding and demo [default=no] + --without-indi disable INDI rotator support [default=no] Optional features that may require specialized hardware are: diff --git a/configure.ac b/configure.ac index 5ac3056cf..ab2e38af1 100644 --- a/configure.ac +++ b/configure.ac @@ -363,6 +363,46 @@ AS_IF([test x"$ax_cv_lib_readline" = "xno"], [ cf_with_readline_support=no ]) +dnl Check if INDI support in rigctl/rotctl is wanted +AC_MSG_CHECKING([whether to use INDI in rigctl/rotctl]) +AC_ARG_WITH([indi], + [AS_HELP_STRING([--without-indi], + [disable INDI in rigctl/rotctl @<:@default=yes@:>@])], + [cf_with_indi_support=no], + [cf_with_indi_support=yes] + ) + +AS_IF([test x"$cf_with_indi_support" != "xno"], [ + # INDI support needs a C++ compiler, tested for presence above. + AS_IF([test x"${cf_with_cxx}" != "xyes"], [ + AC_MSG_WARN([INDI support needs a C++ compiler.]) + cf_with_indi_support=no + ]) +]) + +AS_IF([test x"$cf_with_indi_support" != "xno"], [ + # macros/ax_lib_nova.m4 + AX_LIB_NOVA + + AS_IF([test x"$ax_cv_lib_nova" = "xno"], [ + AC_MSG_WARN([libnova support not found, required by INDI.]) + cf_with_indi_support=no + ]) + + AS_IF([test x"$ax_cv_lib_nova" != "xno"], [ + # macros/ax_lib_indi.m4 + AX_LIB_INDI + + AS_IF([test x"$ax_cv_lib_indi" = "xno"], [ + AC_MSG_WARN([INDI support not found.]) + cf_with_indi_support=no + ]) + + AS_IF([test x"$cf_with_indi_support" != "xno"], [ + ROT_BACKEND_LIST="$ROT_BACKEND_LIST rotators/indi" + ]) + ]) +]) dnl Check if libgd-dev is installed, so we can enable rigmatrix AC_MSG_CHECKING([whether to build HTML rig feature matrix]) @@ -801,6 +841,7 @@ rotators/rotorez/Makefile rotators/sartek/Makefile rotators/spid/Makefile rotators/ts7400/Makefile +rotators/indi/Makefile rigs/adat/Makefile rigs/alinco/Makefile rigs/aor/Makefile @@ -857,6 +898,7 @@ echo \ With Lua binding ${cf_with_lua_binding} With rigmem XML support ${cf_with_xml_support} With Readline support ${cf_with_readline_support} + With INDI support ${cf_with_indi_support} Enable HTML rig feature matrix ${cf_enable_html_matrix} Enable WinRadio ${cf_with_winradio} diff --git a/include/hamlib/rotlist.h b/include/hamlib/rotlist.h index 4481c22f6..53706e2ad 100644 --- a/include/hamlib/rotlist.h +++ b/include/hamlib/rotlist.h @@ -455,6 +455,20 @@ #define ROT_MODEL_IOPTRON ROT_MAKE_MODEL(ROT_IOPTRON, 1) +/** ++ * \def ROT_MODEL_INDI ++ * \brief A macro that returns the model number of the INDI backend. ++ * ++ * The INDI backend can be used with rotators that support, among other, the ++ * INDI interface. ++ */ +//! @cond Doxygen_Suppress +#define ROT_INDI 20 +#define ROT_BACKEND_INDI "indi" +//! @endcond +#define ROT_MODEL_INDI ROT_MAKE_MODEL(ROT_INDI, 1) + + /** diff --git a/macros/Makefile.am b/macros/Makefile.am index c058a6063..3507bbcfa 100644 --- a/macros/Makefile.am +++ b/macros/Makefile.am @@ -6,6 +6,8 @@ MACROS = \ ax_pkg_swig.m4 \ ax_pthread.m4 \ ax_python_devel.m4 \ + ax_lib_indi.m4 \ + ax_lib_nova.m4 \ gr_doxygen.m4 \ gr_pwin32.m4 \ hl_getaddrinfo.m4 \ diff --git a/macros/ax_lib_indi.m4 b/macros/ax_lib_indi.m4 new file mode 100644 index 000000000..d74538e5c --- /dev/null +++ b/macros/ax_lib_indi.m4 @@ -0,0 +1,23 @@ +AU_ALIAS([VL_LIB_INDI], [AX_LIB_INDI]) +AC_DEFUN([AX_LIB_INDI], [ + AC_CACHE_CHECK([for INDI library], + ax_cv_lib_indi, [ + AC_LANG_PUSH(C++) + AC_CHECK_HEADER(libindi/baseclient.h, ax_cv_lib_indi="-lindiclient -lstdc++ -lz") + AC_LANG_POP() + + if test -z "$ax_cv_lib_indi"; then + ax_cv_lib_indi="no" + fi + ]) + + if test "$ax_cv_lib_indi" != "no"; then + ORIG_LIBS="$LIBS" + LIBS="$LIBS $ax_cv_lib_indi" + AC_DEFINE(HAVE_LIBINDI, 1, + [Define if you have an INDI compatible library]) + LIBS="$ORIG_LIBS" + INDI_LIBS="$ax_cv_lib_indi" + AC_SUBST([INDI_LIBS]) + fi +]) diff --git a/macros/ax_lib_nova.m4 b/macros/ax_lib_nova.m4 new file mode 100644 index 000000000..6eb0f0ebf --- /dev/null +++ b/macros/ax_lib_nova.m4 @@ -0,0 +1,23 @@ +AU_ALIAS([VL_LIB_NOVA], [AX_LIB_NOVA]) +AC_DEFUN([AX_LIB_NOVA], [ + AC_CACHE_CHECK([for nova library], + ax_cv_lib_nova, [ + AC_LANG_PUSH(C++) + AC_CHECK_HEADER(libnova/libnova.h, ax_cv_lib_nova="-lnova -lstdc++ -lz") + AC_LANG_POP() + + if test -z "$ax_cv_lib_nova"; then + ax_cv_lib_nova="no" + fi + ]) + + if test "$ax_cv_lib_nova" != "no"; then + ORIG_LIBS="$LIBS" + LIBS="$LIBS $ax_cv_lib_nova" + AC_DEFINE(HAVE_LIBNOVA, 1, + [Define if you have a nova compatible library]) + LIBS="$ORIG_LIBS" + NOVA_LIBS="$ax_cv_lib_nova" + AC_SUBST([NOVA_LIBS]) + fi +]) diff --git a/rotators/indi/Android.mk b/rotators/indi/Android.mk new file mode 100644 index 000000000..4152c67a0 --- /dev/null +++ b/rotators/indi/Android.mk @@ -0,0 +1,12 @@ +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := indi.c indi_wrapper.cpp +LOCAL_MODULE := indi + +LOCAL_CFLAGS := -DHAVE_CONFIG_H +LOCAL_C_INCLUDES := android include src +LOCAL_LDLIBS := -lhamlib -Lobj/local/armeabi + +include $(BUILD_STATIC_LIBRARY) diff --git a/rotators/indi/Makefile.am b/rotators/indi/Makefile.am new file mode 100644 index 000000000..20b7bcd05 --- /dev/null +++ b/rotators/indi/Makefile.am @@ -0,0 +1,6 @@ + +noinst_LTLIBRARIES = libhamlib-indi.la +libhamlib_indi_la_SOURCES = indi.c indi_wrapper.cpp indi_wrapper.hpp indi_wrapper.h +libhamlib_indi_la_LDFLAGS = $(INDI_LIBS) + +EXTRA_DIST = Android.mk diff --git a/rotators/indi/README.indi.md b/rotators/indi/README.indi.md new file mode 100644 index 000000000..1fbb2712c --- /dev/null +++ b/rotators/indi/README.indi.md @@ -0,0 +1,8 @@ +INDI rotator backend +==================== + +This backend lets Hamlib control an astronomical (telescope) rotator through an +[INDI](https://indilib.org/) server. + +The easiest way to set up an INDI server is to use [EKOS](https://www.indilib.org/about/ekos.html), +which is available in [KStars](https://edu.kde.org/kstars/). diff --git a/rotators/indi/indi.c b/rotators/indi/indi.c new file mode 100644 index 000000000..07996fbb8 --- /dev/null +++ b/rotators/indi/indi.c @@ -0,0 +1,70 @@ +/* + * Hamlib Rotator backend - INDI integration + * Copyright (c) 2020 by Norbert Varga HA2NON + * + * + * 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 "indi_wrapper.h" + +#include +#include + +const struct rot_caps indi_rot_caps = +{ + ROT_MODEL(ROT_MODEL_INDI), + .model_name = "INDI", + .mfg_name = "INDI", + .version = "0.1", + .copyright = "LGPL", + .status = RIG_STATUS_ALPHA, + .rot_type = ROT_TYPE_OTHER, + .port_type = RIG_PORT_NONE, + .write_delay = 0, + .post_write_delay = 0, + .timeout = 200, + .retry = 3, + + .min_az = 0, + .max_az = 360, + .min_el = -90, + .max_el = 90, + + .set_position = indi_wrapper_set_position, + .get_position = indi_wrapper_get_position, + .stop = indi_wrapper_stop, + .park = indi_wrapper_park, + .move = indi_wrapper_move, + .get_info = indi_wrapper_get_info, + .rot_open = indi_wrapper_open, + .rot_close = indi_wrapper_close, +}; + +/* ************************************************************************* */ + +DECLARE_INITROT_BACKEND(indi) +{ + rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); + + rot_register(&indi_rot_caps); + + return RIG_OK; +} diff --git a/rotators/indi/indi_wrapper.cpp b/rotators/indi/indi_wrapper.cpp new file mode 100644 index 000000000..df6169999 --- /dev/null +++ b/rotators/indi/indi_wrapper.cpp @@ -0,0 +1,621 @@ +/* + * Hamlib Rotator backend - INDI integration + * Copyright (c) 2020 by Norbert Varga HA2NON + * + * + * 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 + * + */ + +#include "indi_wrapper.hpp" + +#include +#include + +#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d)) + +static std::unique_ptr indi_wrapper_client(new RotINDIClient()); + +int RotINDIClient::setSpeed(int speedPercent) +{ + if (!mTelescope || !mTelescope->isConnected()) + { + rig_debug(RIG_DEBUG_ERR, "indi: telescope not connected\n"); + return -RIG_EIO; + } + + ISwitchVectorProperty *switchVector = + mTelescope->getSwitch("TELESCOPE_SLEW_RATE"); + + if (!switchVector) + { + rig_debug(RIG_DEBUG_ERR, + "indi: unable to find telescope or TELESCOPE_SLEW_RATE switch\n"); + return -RIG_EPROTO; + } + + if (speedPercent < 0) + { + speedPercent = 0; + } + + if (speedPercent > 100) + { + speedPercent = 100; + } + + int speed = DIV_ROUND_UP(speedPercent, 10); + + for (int i = 1; i <= 10; i++) + { + char switchName[4]; + snprintf(switchName, sizeof(switchName), "%ux", i); + + ISwitch *slewrate = IUFindSwitch(switchVector, switchName); + + if (slewrate) + { + if (speed == i) + { + rig_debug(RIG_DEBUG_VERBOSE, "indi: setting speed %s\n", switchName); + slewrate->s = ISS_ON; + } + else + { + slewrate->s = ISS_OFF; + } + } + else + { + rig_debug(RIG_DEBUG_ERR, "indi: unable to find switch member %s\n", switchName); + return -RIG_EPROTO; + } + } + + sendNewSwitch(switchVector); + + return RIG_OK; +} + +int RotINDIClient::move(int direction, int speedPercent) +{ + if (!mTelescope || !mTelescope->isConnected()) + { + rig_debug(RIG_DEBUG_ERR, "indi: telescope not connected\n"); + return -RIG_EIO; + } + + int err = setSpeed(speedPercent); + + if (err != RIG_OK) + { + return err; + } + + ISwitchVectorProperty *switchVector = + mTelescope->getSwitch("TELESCOPE_MOTION_NS"); + + if (!switchVector) + { + rig_debug(RIG_DEBUG_ERR, + "indi: unable to find telescope or TELESCOPE_MOTION_NS switch\n"); + return -RIG_EPROTO; + } + + ISwitch *motion_north = IUFindSwitch(switchVector, "MOTION_NORTH"); + + if (!motion_north) + { + rig_debug(RIG_DEBUG_ERR, "indi: unable to find switch member MOTION_NORTH\n"); + return -RIG_EPROTO; + } + + if (direction & ROT_MOVE_UP) + { + rig_debug(RIG_DEBUG_VERBOSE, "indi: moving up\n"); + motion_north->s = ISS_ON; + } + else + { + motion_north->s = ISS_OFF; + } + + ISwitch *motion_south = IUFindSwitch(switchVector, "MOTION_SOUTH"); + + if (!motion_south) + { + rig_debug(RIG_DEBUG_ERR, "indi: unable to find switch member MOTION_SOUTH\n"); + return -RIG_EPROTO; + } + + if (direction & ROT_MOVE_DOWN) + { + rig_debug(RIG_DEBUG_VERBOSE, "indi: moving down\n"); + motion_south->s = ISS_ON; + } + else + { + motion_south->s = ISS_OFF; + } + + sendNewSwitch(switchVector); + + switchVector = mTelescope->getSwitch("TELESCOPE_MOTION_WE"); + + if (!switchVector) + { + rig_debug(RIG_DEBUG_ERR, + "indi: unable to find telescope or TELESCOPE_MOTION_WE switch\n"); + return -RIG_EPROTO; + } + + ISwitch *motion_west = IUFindSwitch(switchVector, "MOTION_WEST"); + + if (!motion_west) + { + rig_debug(RIG_DEBUG_ERR, "indi: unable to find switch member MOTION_WEST\n"); + return -RIG_EPROTO; + } + + if (direction & ROT_MOVE_LEFT) + { + rig_debug(RIG_DEBUG_VERBOSE, "indi: moving left\n"); + motion_west->s = ISS_ON; + } + else + { + motion_west->s = ISS_OFF; + } + + ISwitch *motion_east = IUFindSwitch(switchVector, "MOTION_EAST"); + + if (!motion_east) + { + rig_debug(RIG_DEBUG_ERR, "indi: unable to find switch member MOTION_RIGHT\n"); + return -RIG_EPROTO; + } + + if (direction & ROT_MOVE_RIGHT) + { + rig_debug(RIG_DEBUG_VERBOSE, "indi: moving right\n"); + motion_east->s = ISS_ON; + } + else + { + motion_east->s = ISS_OFF; + } + + sendNewSwitch(switchVector); + + return RIG_OK; +} + +int RotINDIClient::stop() +{ + if (!mTelescope || !mTelescope->isConnected()) + { + rig_debug(RIG_DEBUG_ERR, "indi: telescope not connected\n"); + return -RIG_EIO; + } + + ISwitchVectorProperty *switchVector = + mTelescope->getSwitch("TELESCOPE_ABORT_MOTION"); + + if (!switchVector) + { + rig_debug(RIG_DEBUG_ERR, + "indi: unable to find telescope or TELESCOPE_ABORT_MOTION switch\n"); + return -RIG_EPROTO; + } + + ISwitch *sw = IUFindSwitch(switchVector, "ABORT"); + + if (!sw) + { + rig_debug(RIG_DEBUG_ERR, "indi: unable to find switch member ABORT_MOTION\n"); + return -RIG_EPROTO; + } + + sw->s = ISS_ON; + sendNewSwitch(switchVector); + + return RIG_OK; +} + +int RotINDIClient::park() +{ + if (!mTelescope) + { + return -RIG_EIO; + } + + if (!mTelescope->isConnected()) + { + rig_debug(RIG_DEBUG_ERR, "indi: telescope not connected\n"); + return -RIG_EIO; + } + + ISwitchVectorProperty *switchVector = mTelescope->getSwitch("TELESCOPE_PARK"); + + if (!switchVector) + { + rig_debug(RIG_DEBUG_ERR, + "indi: unable to find telescope or TELESCOPE_PARK switch\n"); + return -RIG_EPROTO; + } + + ISwitch *unpark = IUFindSwitch(switchVector, "UNPARK"); + + if (!unpark) + { + rig_debug(RIG_DEBUG_ERR, "indi: unable to find switch member UNPARK\n"); + return -RIG_EPROTO; + } + + unpark->s = ISS_OFF; + + ISwitch *park = IUFindSwitch(switchVector, "PARK"); + + if (!park) + { + rig_debug(RIG_DEBUG_ERR, "indi: unable to find switch member PARK\n"); + return -RIG_EPROTO; + } + + park->s = ISS_ON; + + sendNewSwitch(switchVector); + + return RIG_OK; +} + +int RotINDIClient::unPark() +{ + if (!mTelescope || !mTelescope->isConnected()) + { + rig_debug(RIG_DEBUG_ERR, "indi: telescope not connected\n"); + return -RIG_EIO; + } + + ISwitchVectorProperty *switchVector = mTelescope->getSwitch("TELESCOPE_PARK"); + + if (!switchVector) + { + rig_debug(RIG_DEBUG_ERR, + "indi: unable to find telescope or TELESCOPE_PARK switch\n"); + return -RIG_EPROTO; + } + + ISwitch *park = IUFindSwitch(switchVector, "PARK"); + + if (!park) + { + rig_debug(RIG_DEBUG_ERR, "indi: unable to find switch member PARK\n"); + return -RIG_EPROTO; + } + + park->s = ISS_OFF; + + ISwitch *unpark = IUFindSwitch(switchVector, "UNPARK"); + + if (!unpark) + { + rig_debug(RIG_DEBUG_ERR, "indi: unable to find switch member UNPARK\n"); + return -RIG_EPROTO; + } + + unpark->s = ISS_ON; + + sendNewSwitch(switchVector); + + return RIG_OK; +} + +void RotINDIClient::position(azimuth_t *az, elevation_t *el) +{ + *az = mAz; + *el = mEl; +} + +double RotINDIClient::getPositionDiffBetween(double deg1, double deg2) +{ + return fabs(deg1 - deg2); +} + +double RotINDIClient::getPositionDiffOutside(double deg1, double deg2, + double minDeg, double maxDeg) +{ + if (deg1 < deg2) + { + return getPositionDiffBetween(minDeg, deg1) + getPositionDiffBetween(deg2, + maxDeg); + } + + return getPositionDiffBetween(minDeg, deg2) + getPositionDiffBetween(deg1, + maxDeg); +} + +double RotINDIClient::getPositionDiff(double deg1, double deg2, double minDeg, + double maxDeg) +{ + double between = getPositionDiffBetween(deg1, deg2); + double outside = getPositionDiffOutside(deg1, deg2, minDeg, maxDeg); + + if (between < outside) + { + return between; + } + + return outside; +} + +int RotINDIClient::setPosition(azimuth_t az, elevation_t el) +{ + if (!mTelescope || !mTelescope->isConnected()) + { + rig_debug(RIG_DEBUG_ERR, "indi: telescope not connected\n"); + return -RIG_EIO; + } + + if (fabs(mDstAz - az) < 0.001 && fabs(mDstEl - el) < 0.001) + { + rig_debug(RIG_DEBUG_VERBOSE, + "indi: ignoring new position, already approaching\n"); + return RIG_OK; + } + + // Each coordinate set command stops and restarts the rotator, so we have to avoid unnecessary + // set calls by defining a range of distance from the new az/el coords which we ignore until + // we are closer to the new az/el coords than this value. + const int quickJumpRangeDegrees = 10; + + double currDstNewDstAzDiff = getPositionDiff(mDstAz, az, 0, 360); + double currDstNewDstElDiff = getPositionDiff(mDstEl, el, -90, 90); + double currDstNewDstDistance = sqrt(pow(currDstNewDstAzDiff, + 2) + pow(currDstNewDstElDiff, 2)); + double currPosNewDstAzDiff = getPositionDiff(mAz, az, 0, 360); + double currPosNewDstElDiff = getPositionDiff(mEl, el, -90, 90); + double currPosNewDstDistance = sqrt(pow(currPosNewDstAzDiff, + 2) + pow(currPosNewDstElDiff, 2)); + + if (currDstNewDstDistance < quickJumpRangeDegrees + && currPosNewDstDistance > quickJumpRangeDegrees) + { + rig_debug(RIG_DEBUG_VERBOSE, + "indi: ignoring new position, approaching quickly, newDst/currDst distance: %f newDst/currPos distance: %f\n", + currDstNewDstDistance, currPosNewDstDistance); + return RIG_OK; + } + + rig_debug(RIG_DEBUG_VERBOSE, "indi: setting position to az: %f el: %f\n", az, + el); + + mDstAz = az; + mDstEl = el; + + ISwitchVectorProperty *switchVector = mTelescope->getSwitch("ON_COORD_SET"); + + if (!switchVector) + { + rig_debug(RIG_DEBUG_ERR, + "indi: unable to find telescope or ON_COORD_SET switch\n"); + return -RIG_EPROTO; + } + + ISwitch *slew = IUFindSwitch(switchVector, "SLEW"); + + if (!slew) + { + rig_debug(RIG_DEBUG_ERR, "indi: unable to find switch member SLEW\n"); + return -RIG_EPROTO; + } + + slew->s = ISS_OFF; + + ISwitch *track = IUFindSwitch(switchVector, "TRACK"); + + if (!track) + { + rig_debug(RIG_DEBUG_ERR, "indi: unable to find switch member TRACK\n"); + return -RIG_EPROTO; + } + + track->s = ISS_ON; + + ISwitch *sync = IUFindSwitch(switchVector, "SYNC"); + + if (!sync) + { + rig_debug(RIG_DEBUG_ERR, "indi: unable to find switch member SYNC\n"); + return -RIG_EPROTO; + } + + sync->s = ISS_OFF; + + sendNewSwitch(switchVector); + + INumberVectorProperty *property = mTelescope->getNumber("HORIZONTAL_COORD"); + + if (!property) + { + rig_debug(RIG_DEBUG_ERR, + "indi: unable to find telescope or HORIZONTAL_COORD property\n"); + return -RIG_EPROTO; + } + + property->np[0].value = az; + property->np[1].value = el; + sendNewNumber(property); + + return RIG_OK; +} + +void RotINDIClient::removeDevice(INDI::BaseDevice *dp) {} +void RotINDIClient::newProperty(INDI::Property *property) +{ + std::string name(property->getName()); + + if (!mTelescope && name == "TELESCOPE_INFO") + { + mTelescope = property->getBaseDevice(); + rig_debug(RIG_DEBUG_VERBOSE, "indi: using device %s\n", + mTelescope->getDeviceName()); + watchDevice(mTelescope->getDeviceName()); + + if (!mTelescope->isConnected()) + { + connectDevice(mTelescope->getDeviceName()); + } + + mDstAz = INT_MAX; + mDstEl = INT_MAX; + } + + if (name == "HORIZONTAL_COORD") + { + mAz = property->getNumber()->np[0].value; + mEl = property->getNumber()->np[1].value; + } +} +void RotINDIClient::removeProperty(INDI::Property *property) {} +void RotINDIClient::newBLOB(IBLOB *bp) {} +void RotINDIClient::newSwitch(ISwitchVectorProperty *svp) {} +void RotINDIClient::newNumber(INumberVectorProperty *nvp) +{ + std::string name(nvp->name); + + if (name == "HORIZONTAL_COORD") + { + mAz = nvp->np[0].value; + mEl = nvp->np[1].value; + } +} +void RotINDIClient::newMessage(INDI::BaseDevice *dp, int messageID) {} +void RotINDIClient::newText(ITextVectorProperty *tvp) {} +void RotINDIClient::newLight(ILightVectorProperty *lvp) {} +void RotINDIClient::newDevice(INDI::BaseDevice *dp) {} + +void RotINDIClient::serverConnected() +{ + rig_debug(RIG_DEBUG_VERBOSE, "indi: server connected\n"); +} + +void RotINDIClient::serverDisconnected(int exit_code) +{ + rig_debug(RIG_DEBUG_VERBOSE, "indi: server disconnected\n"); +} + +const char *RotINDIClient::getInfo(void) +{ + static char info[128]; + + if (mTelescope && mTelescope->isConnected()) + { + snprintf(info, sizeof(info), "using INDI device %s", + mTelescope->getDeviceName()); + return info; + } + + return "no INDI device connected"; +} + +void RotINDIClient::close(void) +{ + if (mTelescope && mTelescope->isConnected()) + { + disconnectDevice(mTelescope->getDeviceName()); + } + + disconnectServer(); +} + +extern "C" int indi_wrapper_set_position(ROT *rot, azimuth_t az, + elevation_t el) +{ + int res; + + rig_debug(RIG_DEBUG_TRACE, "%s called: az=%f el=%f\n", __func__, az, el); + + res = indi_wrapper_client->unPark(); + + if (res != RIG_OK) + { + return res; + } + + return indi_wrapper_client->setPosition(az, el); +} + +extern "C" int indi_wrapper_get_position(ROT *rot, azimuth_t *az, + elevation_t *el) +{ + rig_debug(RIG_DEBUG_TRACE, "%s called\n", __func__); + + indi_wrapper_client->position(az, el); + + return RIG_OK; +} + +extern "C" int indi_wrapper_stop(ROT *rot) +{ + rig_debug(RIG_DEBUG_TRACE, "%s called\n", __func__); + + return indi_wrapper_client->stop(); +} + +extern "C" int indi_wrapper_park(ROT *rot) +{ + rig_debug(RIG_DEBUG_TRACE, "%s called\n", __func__); + + return indi_wrapper_client->park(); +} + +extern "C" int indi_wrapper_move(ROT *rot, int direction, int speed) +{ + rig_debug(RIG_DEBUG_TRACE, "%s called\n", __func__); + + return indi_wrapper_client->move(direction, speed); +} + +extern "C" const char *indi_wrapper_get_info(ROT *rot) +{ + rig_debug(RIG_DEBUG_TRACE, "%s called\n", __func__); + + return indi_wrapper_client->getInfo(); +} + +extern "C" int indi_wrapper_open(ROT *rot) +{ + rig_debug(RIG_DEBUG_TRACE, "%s called\n", __func__); + + indi_wrapper_client->setServer("localhost", 7624); + + if (!indi_wrapper_client->connectServer()) + { + rig_debug(RIG_DEBUG_ERR, "indi: server refused connection\n"); + return -RIG_EPROTO; + } + + return RIG_OK; +} + +extern "C" int indi_wrapper_close(ROT *rot) +{ + rig_debug(RIG_DEBUG_TRACE, "%s called\n", __func__); + + indi_wrapper_client->close(); + + return RIG_OK; +} diff --git a/rotators/indi/indi_wrapper.h b/rotators/indi/indi_wrapper.h new file mode 100644 index 000000000..cfd83682a --- /dev/null +++ b/rotators/indi/indi_wrapper.h @@ -0,0 +1,36 @@ +/* + * Hamlib Rotator backend - INDI integration + * Copyright (c) 2020 by Norbert Varga HA2NON + * + * + * 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 _INDI_WRAPPER_H +#define _INDI_WRAPPER_H 1 + +#include + +int indi_wrapper_set_position(ROT *rot, azimuth_t az, elevation_t el); +int indi_wrapper_get_position(ROT *rot, azimuth_t *az, elevation_t *el); +int indi_wrapper_stop(ROT *rot); +int indi_wrapper_park(ROT *rot); +int indi_wrapper_move(ROT *rot, int direction, int speed); +const char *indi_wrapper_get_info(ROT *rot); +int indi_wrapper_open(ROT *rot); +int indi_wrapper_close(ROT *rot); + +#endif // _INDI_WRAPPER_H diff --git a/rotators/indi/indi_wrapper.hpp b/rotators/indi/indi_wrapper.hpp new file mode 100644 index 000000000..3f5efef0b --- /dev/null +++ b/rotators/indi/indi_wrapper.hpp @@ -0,0 +1,71 @@ +/* + * Hamlib Rotator backend - INDI integration + * Copyright (c) 2020 by Norbert Varga HA2NON + * + * + * 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 _INDI_WRAPPER_HPP +#define _INDI_WRAPPER_HPP 1 + +#include +#include + +#include + +class RotINDIClient : public INDI::BaseClient +{ +public: + int setSpeed(int speedPercent); + int move(int direction, int speedPercent); + int stop(); + int park(); + int unPark(); + void position(azimuth_t *az, elevation_t *el); + int setPosition(azimuth_t az, elevation_t el); + const char *getInfo(); + void close(void); + +protected: + virtual void newDevice(INDI::BaseDevice *dp); + virtual void removeDevice(INDI::BaseDevice *dp); + virtual void newProperty(INDI::Property *property); + virtual void removeProperty(INDI::Property *property); + virtual void newBLOB(IBLOB *bp); + virtual void newSwitch(ISwitchVectorProperty *svp); + virtual void newNumber(INumberVectorProperty *nvp); + virtual void newMessage(INDI::BaseDevice *dp, int messageID); + virtual void newText(ITextVectorProperty *tvp); + virtual void newLight(ILightVectorProperty *lvp); + virtual void serverConnected(); + virtual void serverDisconnected(int exit_code); + +private: + double getPositionDiffBetween(double deg1, double deg2); + double getPositionDiffOutside(double deg1, double deg2, double minDeg, double maxDeg); + double getPositionDiff(double deg1, double deg2, double minDeg, double maxDeg); + + INDI::BaseDevice *mTelescope; + + azimuth_t mDstAz; + elevation_t mDstEl; + + azimuth_t mAz; + elevation_t mEl; +}; + +#endif // _INDI_WRAPPER_HPP diff --git a/src/Makefile.am b/src/Makefile.am index b53926a4f..f3a9a22c1 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -12,7 +12,7 @@ libhamlib_la_SOURCES = $(RIGSRC) libhamlib_la_LDFLAGS = $(WINLDFLAGS) $(OSXLDFLAGS) -no-undefined -version-info $(ABI_VERSION):$(ABI_REVISION):$(ABI_AGE) libhamlib_la_LIBADD = $(top_builddir)/lib/libmisc.la \ - $(BACKENDEPS) $(RIG_BACKENDEPS) $(ROT_BACKENDEPS) $(AMP_BACKENDEPS) $(NET_LIBS) $(MATH_LIBS) $(LIBUSB_LIBS) + $(BACKENDEPS) $(RIG_BACKENDEPS) $(ROT_BACKENDEPS) $(AMP_BACKENDEPS) $(NET_LIBS) $(MATH_LIBS) $(LIBUSB_LIBS) $(INDI_LIBS) libhamlib_la_DEPENDENCIES = $(top_builddir)/lib/libmisc.la $(BACKENDEPS) $(RIG_BACKENDEPS) $(ROT_BACKENDEPS) $(AMP_BACKENDEPS) diff --git a/src/rot_reg.c b/src/rot_reg.c index ab77990a0..3353e7bc8 100644 --- a/src/rot_reg.c +++ b/src/rot_reg.c @@ -84,6 +84,9 @@ DEFINE_INITROT_BACKEND(cnctrk); DEFINE_INITROT_BACKEND(prosistel); DEFINE_INITROT_BACKEND(meade); DEFINE_INITROT_BACKEND(ioptron); +#if HAVE_LIBINDI +DEFINE_INITROT_BACKEND(indi); +#endif //! @endcond /** @@ -123,6 +126,9 @@ static struct { ROT_PROSISTEL, ROT_BACKEND_PROSISTEL, ROT_FUNCNAMA(prosistel) }, { ROT_MEADE, ROT_BACKEND_MEADE, ROT_FUNCNAMA(meade) }, { ROT_IOPTRON, ROT_BACKEND_IOPTRON, ROT_FUNCNAMA(ioptron) }, +#if HAVE_LIBINDI + { ROT_INDI, ROT_BACKEND_INDI, ROT_FUNCNAMA(indi) }, +#endif { 0, NULL }, /* end */ };