diff --git a/NEWS b/NEWS index 3ee554af3..c63400142 100644 --- a/NEWS +++ b/NEWS @@ -14,6 +14,7 @@ Version 5.x -- future Version 4.6 * 2023-11-XX -- Planned for Nov 2023 + * Add Apex Shared Loop rotator -- unidirectional only so far * Add client_version to rigctld so client can report it's version for future use/compatility/alternatives * Add --set-conf=tuner_control_pathname=hamlib_tuner_control (default) If file exists then it will be called with 0/1 (Off/On) argument diff --git a/configure.ac b/configure.ac index d0938071b..fcaffc794 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". RIG_BACKEND_LIST="rigs/adat rigs/alinco rigs/aor rigs/barrett rigs/codan rigs/dorji rigs/drake rigs/dummy rigs/elad rigs/flexradio rigs/icom rigs/icmarine rigs/jrc rigs/kachina rigs/kenwood rigs/kit rigs/lowe rigs/pcr rigs/prm80 rigs/racal rigs/rft rigs/rs rigs/skanti rigs/tapr rigs/tentec rigs/tuner rigs/uniden rigs/winradio rigs/wj rigs/yaesu rigs/gomspace rigs/mds" -ROT_BACKEND_LIST="rotators/amsat rotators/ars rotators/celestron rotators/cnctrk rotators/grbltrk rotators/easycomm rotators/ether6 rotators/flir rotators/fodtrack rotators/gs232a rotators/heathkit rotators/m2 rotators/meade rotators/rotorez rotators/sartek rotators/spid rotators/ts7400 rotators/prosistel rotators/ioptron rotators/satel rotators/radant" +ROT_BACKEND_LIST="rotators/amsat rotators/apex rotators/ars rotators/celestron rotators/cnctrk rotators/grbltrk rotators/easycomm rotators/ether6 rotators/flir rotators/fodtrack rotators/gs232a rotators/heathkit rotators/m2 rotators/meade rotators/rotorez rotators/sartek rotators/spid rotators/ts7400 rotators/prosistel rotators/ioptron rotators/satel rotators/radant" # Amplifiers are all in the amplifiers directory AMP_BACKEND_LIST="amplifiers/elecraft amplifiers/gemini" @@ -843,6 +843,7 @@ bindings/Makefile doc/Makefile doc/hamlib.cfg rotators/amsat/Makefile +rotators/apex/Makefile rotators/ars/Makefile rotators/celestron/Makefile rotators/cnctrk/Makefile diff --git a/include/hamlib/rotlist.h b/include/hamlib/rotlist.h index 2565d56aa..11c981bd7 100644 --- a/include/hamlib/rotlist.h +++ b/include/hamlib/rotlist.h @@ -666,6 +666,20 @@ //! @endcond #define ROT_MODEL_FLIR ROT_MAKE_MODEL(ROT_FLIR, 1) +/** + * \brief A macro that returns the model number of the APEX backend. + * + * \def ROT_MODEL_APEX + * + * The APEX backend can be used with APEX * rotators. + */ +//! @cond Doxygen_Suppress +#define ROT_APEX 26 +#define ROT_BACKEND_APEX "apex" +//! @endcond +#define ROT_MODEL_APEX_SHARED_LOOP ROT_MAKE_MODEL(ROT_APEX, 1) + + /** * \brief Convenience type definition for a rotator model. * diff --git a/rotators/apex/Android.mk b/rotators/apex/Android.mk new file mode 100644 index 000000000..99e7cb2d8 --- /dev/null +++ b/rotators/apex/Android.mk @@ -0,0 +1,12 @@ +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := apex.c sharedloop.c +LOCAL_MODULE := apex + +LOCAL_CFLAGS := +LOCAL_C_INCLUDES := android include src +LOCAL_LDLIBS := -lhamlib -Lobj/local/$(TARGET_ARCH_ABI) + +include $(BUILD_STATIC_LIBRARY) diff --git a/rotators/apex/Makefile.am b/rotators/apex/Makefile.am new file mode 100644 index 000000000..d575974e5 --- /dev/null +++ b/rotators/apex/Makefile.am @@ -0,0 +1,5 @@ + +noinst_LTLIBRARIES = libhamlib-apex.la +libhamlib_apex_la_SOURCES = apex.c sharedloop.c + +EXTRA_DIST = Android.mk diff --git a/rotators/apex/apex.c b/rotators/apex/apex.c new file mode 100644 index 000000000..698b32674 --- /dev/null +++ b/rotators/apex/apex.c @@ -0,0 +1,136 @@ +#include +#include +#include "iofunc.h" +#include "register.h" +#include "apex.h" + +float apex_azimuth; + +char apex_info[64]; + +static pthread_t apex_read_thread; + +// We only have two strings to get +// one is 18 chars and the other may be variable +// [T4BRFA99H00M010#] Unidirectional +// xxxxxxxxxx +// So we'll read 5 chars and use the to determine how to read the rest + +static int apex_get_string(ROT *rot, char *s, int maxlen) +{ + int retval = 0; + struct rot_state *rs = &rot->state; + char buf[64]; + + memset(s, 0, maxlen); + + retval = read_string(&rs->rotport, (unsigned char *)buf, + sizeof(buf), + "\n", strlen("\n"), sizeof(buf), 1); + strncpy(s, buf, maxlen); + strtok(s, "\r\n"); // truncate any CR/LF + rig_debug(RIG_DEBUG_VERBOSE, "%s: %d bytes '%s'\n", __func__, retval, s); + + if (retval <= 0) { return -RIG_EPROTO; } + + return RIG_OK; +} + + +// Expecting # from 0-7 +// [T4BRFA99H00M010#] Unidirectional +// Or +// [T4BRFA99H00M020#] Bidirectional +static void *apex_read(void *arg) +{ + ROT *rot = arg; + int retval = 0; + char data[64]; + int expected_return_length = 63; + + while (1) + { + apex_get_string(rot, data, expected_return_length); + + if (strstr(data, "")) + { + strncpy(apex_info, data, sizeof(apex_info)); + rig_debug(RIG_DEBUG_TRACE, "%s: apex_info=%s\n", __func__, apex_info); + continue; + } + + if (retval != 0 || strstr(data, "[T4BRFA99") == NULL) + { + rig_debug(RIG_DEBUG_ERR, "%s: unknown apex status message=%s\n", __func__, + data); + continue; + } + + rig_debug(RIG_DEBUG_VERBOSE, "%s: data='%s'\n", __func__, data); + + if (retval == 0) + { + switch (data[16]) + { + case '0': apex_azimuth = 45; break; + + case '1': apex_azimuth = 90; break; + + case '2': apex_azimuth = 135; break; + + case '3': apex_azimuth = 180; break; + + case '4': apex_azimuth = 225; break; + + case '5': apex_azimuth = 270; break; + + case '6': apex_azimuth = 315; break; + + case '7': apex_azimuth = 0; break; + } + } + +// printf("az=%f\n", apex_azimuth); + } + + return NULL; +} + + +int apex_open(ROT *rot) +{ + int retval; + char *cmdstr = "[GETVER]\r"; // does this work on all Apex controllers? + struct rot_state *rs = &rot->state; + + rig_debug(RIG_DEBUG_VERBOSE, "%s: entered\n", __func__); + + apex_azimuth = -1; // we check to see if we've seen azimuth at least one time + rig_flush(&rs->rotport); + retval = write_block(&rs->rotport, (unsigned char *) cmdstr, strlen(cmdstr)); + + if (retval != RIG_OK) + { + rig_debug(RIG_DEBUG_ERR, "%s: write_block failed - %s\n", __func__, + rigerror(retval)); + return retval; + } + + pthread_create(&apex_read_thread, NULL, apex_read, rot); + return RIG_OK; +} + +const char *apex_get_info(ROT *rot) +{ + return apex_info; +} + +DECLARE_INITROT_BACKEND(apex) +{ + rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); + + rot_register(&apex_shared_loop_rot_caps); + + return RIG_OK; +} + diff --git a/rotators/apex/apex.h b/rotators/apex/apex.h new file mode 100644 index 000000000..30b1178d3 --- /dev/null +++ b/rotators/apex/apex.h @@ -0,0 +1,31 @@ +/* + * Hamlib Rotator backend - Apex rotators + * + * + * 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 _ROT_APEX_H +#define _ROT_APEX_H 1 + +extern const struct rot_caps apex_shared_loop_rot_caps; + +extern float apex_azimuth; + +extern int apex_open(ROT *rot); +extern const char *apex_get_info(ROT *rot); + +#endif /* _ROT_APEX_H */ diff --git a/rotators/apex/apex.txt b/rotators/apex/apex.txt new file mode 100644 index 000000000..cfa53e8ed --- /dev/null +++ b/rotators/apex/apex.txt @@ -0,0 +1,42 @@ +[T4BRFA99H00M0107] -- 0 degrees +[T4BRFA99H00M0100] -- 45 degrees +[T4BRFA99H00M0101] -- 90 degrees +[T4BRFA99H00M0102] -- 135 degrees +[T4BRFA99H00M0103] -- 180 degrees +[T4BRFA99H00M0104] -- 225 degrees +[T4BRFA99H00M0105] -- 270 degrees +[T4BRFA99H00M0106] -- 315 degrees + +These are the bidirectional answers +[T4BRFA99H00M0200] -- 45 +[T4BRFA99H00M0201] -- 90 +[T4BRFA99H00M0202] -- 135 +[T4BRFA99H00M0203] -- 180 Bidirectional N/S +[T4BRFA99H00M0204] -- 225 +[T4BRFA99H00M0205] -- 270 +[T4BRFA99H00M0206] -- 315 +[T4BRFA99H00M0207] -- 0 + +All commands need CR added +The 98 should be replaced by whatever the rig returns -- in this case it was 99. +Startup: [LINK] returns [LINKOK] +LEFT:[R98T4AH01] [R99T4AH01] counter +RIGHT:[R98T4AH02] +UNI:[R98T4AH03] [R99T4AH03] +BI: [R98T4AH04] [R99T4AH04] +FLIP: [R98T4AH05] [R99T4AH05] + +Direct switching +Seems the "98" model does not recognized the direct commands +[R99T4AM10] 0 +[R99T4AM11] 45 +[R99T4AM12] 90 +[R99T4AM13] 135 +[R99T4AM14] 180 +[R99T4AM15] 225 +[R99T4AM16] 270 +[R99T4AM17] 315 + +Misc Commands +[GETVER] + response with string diff --git a/rotators/apex/sharedloop.c b/rotators/apex/sharedloop.c new file mode 100644 index 000000000..8981c9bfa --- /dev/null +++ b/rotators/apex/sharedloop.c @@ -0,0 +1,99 @@ +/* Apex Shared Loop Controller */ + +#include +#include "hamlib/rotator.h" +#include "iofunc.h" +#include "apex.h" + +int apex_shared_loop_get_position(ROT *rot, float *az, float *el) +{ + int loop = 10; + + while (--loop > 0 && apex_azimuth < 0) + { + hl_usleep(250 * 1000); + }; + + *az = apex_azimuth; + + *el = 0; + + return RIG_OK; +} + +int apex_shared_loop_set_position(ROT *rot, float az, float dummy) +{ + char cmdstr[16]; + int retval; + struct rot_state *rs = &rot->state; + int remainder = lround(az + 22.5) % 45; + int apex_az = lround(az + 22.5) - remainder; + + // default to 0 degrees + snprintf(cmdstr, sizeof(cmdstr), "[R99T4AM10]\r\n"); + + switch (apex_az) + { + case 45: cmdstr[9] = '1'; break; + + case 90: cmdstr[9] = '2'; break; + + case 135: cmdstr[9] = '3'; break; + + case 180: cmdstr[9] = '4'; break; + + case 225: cmdstr[9] = '5'; break; + + case 270: cmdstr[9] = '6'; break; + + case 315: cmdstr[9] = '7'; break; + + default: + rig_debug(RIG_DEBUG_ERR, "%s: unknown az=%d\n", __func__, apex_az); + return -RIG_EINTERNAL; + } + + rig_flush(&rs->rotport); + apex_azimuth = -1; + retval = write_block(&rs->rotport, (unsigned char *) cmdstr, strlen(cmdstr)); + + if (retval != RIG_OK) + { + rig_debug(RIG_DEBUG_ERR, "%s: write_block error - %s\n", __func__, + rigerror(retval)); + return retval; + } + + return RIG_OK; +} + +const struct rot_caps apex_shared_loop_rot_caps = +{ + ROT_MODEL(ROT_MODEL_APEX_SHARED_LOOP), + .model_name = "Shared Loop", + .mfg_name = "Apex", + .version = "20221224.0", + .copyright = "LGPL", + .status = RIG_STATUS_STABLE, + .rot_type = ROT_TYPE_AZIMUTH, + .port_type = RIG_PORT_SERIAL, + .serial_rate_min = 57600, + .serial_rate_max = 57600, + .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 = 4000, + .retry = 2, + + .min_az = 0.0, + .max_az = 360.0, + + .rot_open = apex_open, + .get_info = apex_get_info, + .get_position = apex_shared_loop_get_position, + .set_position = apex_shared_loop_set_position, +}; + diff --git a/src/iofunc.c b/src/iofunc.c index dc8979624..880ce02d6 100644 --- a/src/iofunc.c +++ b/src/iofunc.c @@ -1337,7 +1337,6 @@ static int read_string_generic(hamlib_port_t *p, * read 1 character from the rig, (check if in stop set) * The file descriptor must have been set up non blocking. */ - do { #if 0 #ifndef __MINGW32__ @@ -1349,7 +1348,7 @@ static int read_string_generic(hamlib_port_t *p, #endif rd_count = port_read_generic(p, &rxbuffer[total_count], expected_len == 1 ? 1 : minlen, direct); -// rig_debug(RIG_DEBUG_VERBOSE, "%s: read %d bytes\n", __func__, (int)rd_count); +// rig_debug(RIG_DEBUG_VERBOSE, "%s: read %d bytes tot=%d\n", __func__, (int)rd_count, total_count); minlen -= rd_count; if (errno == EAGAIN) @@ -1380,6 +1379,8 @@ static int read_string_generic(hamlib_port_t *p, total_count += (int) rd_count; + if (total_count == rxmax) break; + if (stopset && memchr(stopset, rxbuffer[total_count - 1], stopset_len)) { if (minlen == 1) { minlen = total_count; } diff --git a/src/rot_reg.c b/src/rot_reg.c index b476dd4c2..26161b54d 100644 --- a/src/rot_reg.c +++ b/src/rot_reg.c @@ -91,6 +91,7 @@ DEFINE_INITROT_BACKEND(androidsensor); #endif DEFINE_INITROT_BACKEND(grbltrk); DEFINE_INITROT_BACKEND(flir); +DEFINE_INITROT_BACKEND(apex); //! @endcond /** @@ -140,6 +141,7 @@ static struct #endif { ROT_GRBLTRK, ROT_BACKEND_GRBLTRK, ROT_FUNCNAMA(grbltrk) }, { ROT_FLIR, ROT_BACKEND_FLIR, ROT_FUNCNAMA(flir) }, + { ROT_APEX, ROT_BACKEND_APEX, ROT_FUNCNAMA(apex) }, { 0, NULL }, /* end */ };