Add Apex Shared Loop rotator

https://github.com/Hamlib/Hamlib/issues/1159
pull/1215/head
Mike Black W9MDB 2022-12-25 17:07:26 -06:00
rodzic c59b5383e9
commit bc6c14e430
11 zmienionych plików z 347 dodań i 3 usunięć

1
NEWS
Wyświetl plik

@ -14,6 +14,7 @@ Version 5.x -- future
Version 4.6 Version 4.6
* 2023-11-XX -- Planned for Nov 2023 * 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 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) * Add --set-conf=tuner_control_pathname=hamlib_tuner_control (default)
If file exists then it will be called with 0/1 (Off/On) argument If file exists then it will be called with 0/1 (Off/On) argument

Wyświetl plik

@ -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 rotor definitions, e.g. "dummy". Optional backends will not be listed
dnl here but will be added later, e.g. "winradio". 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" 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 # Amplifiers are all in the amplifiers directory
AMP_BACKEND_LIST="amplifiers/elecraft amplifiers/gemini" AMP_BACKEND_LIST="amplifiers/elecraft amplifiers/gemini"
@ -843,6 +843,7 @@ bindings/Makefile
doc/Makefile doc/Makefile
doc/hamlib.cfg doc/hamlib.cfg
rotators/amsat/Makefile rotators/amsat/Makefile
rotators/apex/Makefile
rotators/ars/Makefile rotators/ars/Makefile
rotators/celestron/Makefile rotators/celestron/Makefile
rotators/cnctrk/Makefile rotators/cnctrk/Makefile

Wyświetl plik

@ -666,6 +666,20 @@
//! @endcond //! @endcond
#define ROT_MODEL_FLIR ROT_MAKE_MODEL(ROT_FLIR, 1) #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. * \brief Convenience type definition for a rotator model.
* *

Wyświetl plik

@ -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)

Wyświetl plik

@ -0,0 +1,5 @@
noinst_LTLIBRARIES = libhamlib-apex.la
libhamlib_apex_la_SOURCES = apex.c sharedloop.c
EXTRA_DIST = Android.mk

Wyświetl plik

@ -0,0 +1,136 @@
#include <hamlib/rotator.h>
#include <pthread.h>
#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
// <VER> 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, "<VER>"))
{
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;
}

Wyświetl plik

@ -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 */

Wyświetl plik

@ -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]
<VER> response with string

Wyświetl plik

@ -0,0 +1,99 @@
/* Apex Shared Loop Controller */
#include <math.h>
#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,
};

Wyświetl plik

@ -1337,7 +1337,6 @@ static int read_string_generic(hamlib_port_t *p,
* read 1 character from the rig, (check if in stop set) * read 1 character from the rig, (check if in stop set)
* The file descriptor must have been set up non blocking. * The file descriptor must have been set up non blocking.
*/ */
do
{ {
#if 0 #if 0
#ifndef __MINGW32__ #ifndef __MINGW32__
@ -1349,7 +1348,7 @@ static int read_string_generic(hamlib_port_t *p,
#endif #endif
rd_count = port_read_generic(p, &rxbuffer[total_count], rd_count = port_read_generic(p, &rxbuffer[total_count],
expected_len == 1 ? 1 : minlen, direct); 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; minlen -= rd_count;
if (errno == EAGAIN) if (errno == EAGAIN)
@ -1380,6 +1379,8 @@ static int read_string_generic(hamlib_port_t *p,
total_count += (int) rd_count; total_count += (int) rd_count;
if (total_count == rxmax) break;
if (stopset && memchr(stopset, rxbuffer[total_count - 1], stopset_len)) if (stopset && memchr(stopset, rxbuffer[total_count - 1], stopset_len))
{ {
if (minlen == 1) { minlen = total_count; } if (minlen == 1) { minlen = total_count; }

Wyświetl plik

@ -91,6 +91,7 @@ DEFINE_INITROT_BACKEND(androidsensor);
#endif #endif
DEFINE_INITROT_BACKEND(grbltrk); DEFINE_INITROT_BACKEND(grbltrk);
DEFINE_INITROT_BACKEND(flir); DEFINE_INITROT_BACKEND(flir);
DEFINE_INITROT_BACKEND(apex);
//! @endcond //! @endcond
/** /**
@ -140,6 +141,7 @@ static struct
#endif #endif
{ ROT_GRBLTRK, ROT_BACKEND_GRBLTRK, ROT_FUNCNAMA(grbltrk) }, { ROT_GRBLTRK, ROT_BACKEND_GRBLTRK, ROT_FUNCNAMA(grbltrk) },
{ ROT_FLIR, ROT_BACKEND_FLIR, ROT_FUNCNAMA(flir) }, { ROT_FLIR, ROT_BACKEND_FLIR, ROT_FUNCNAMA(flir) },
{ ROT_APEX, ROT_BACKEND_APEX, ROT_FUNCNAMA(apex) },
{ 0, NULL }, /* end */ { 0, NULL }, /* end */
}; };