kopia lustrzana https://github.com/Hamlib/Hamlib
Implement and document Extended Response Protocol for rotctld
New test program, testrotctld.pl for rotctld Implemented locator.c functions in rotctl and rotctld git-svn-id: https://hamlib.svn.sourceforge.net/svnroot/hamlib/trunk@2831 7ae35d74-ebe9-4afe-98af-79ac388436b8Hamlib-1.2.11
rodzic
786e53ce50
commit
121f4a996b
|
@ -5,15 +5,15 @@ DEJATOOL = testfreq testbcd testloc rigctl
|
|||
DISTCLEANFILES = rigctl.log rigctl.sum testbcd.log testbcd.sum
|
||||
|
||||
bin_PROGRAMS = rigctl rigmem rigswr rigsmtr rotctl rigctld rotctld
|
||||
man_MANS = rigctl.1 rigmem.1 rigswr.1 rigsmtr.1 rotctl.1 rigctld.8 rotctld.8
|
||||
man_MANS = rigctl.1 rigmem.1 rigswr.1 rigsmtr.1 rotctl.1 rigctld.8 rotctld.8
|
||||
|
||||
check_PROGRAMS = dumpmem testrig testtrn testbcd testfreq listrigs \
|
||||
testloc rig_bench @RIGMATRIX@
|
||||
|
||||
rigctl_SOURCES = rigctl.c rigctl_parse.c dumpcaps.c sprintflst.c
|
||||
rigctld_SOURCES = rigctld.c rigctl_parse.c dumpcaps.c sprintflst.c
|
||||
rotctl_SOURCES = rotctl.c rotctl_parse.c
|
||||
rotctld_SOURCES = rotctld.c rotctl_parse.c
|
||||
rotctl_SOURCES = rotctl.c rotctl_parse.c dumpcaps_rot.c
|
||||
rotctld_SOURCES = rotctld.c rotctl_parse.c dumpcaps_rot.c
|
||||
rigswr_SOURCES = rigswr.c
|
||||
rigsmtr_SOURCES = rigsmtr.c
|
||||
rigmem_SOURCES = rigmem.c memsave.c memload.c memcsv.c sprintflst.c
|
||||
|
@ -40,11 +40,11 @@ rigmem_LDFLAGS = @BACKENDLNK@ @LIBXML2_LIBS@
|
|||
rotctl_LDFLAGS = @ROT_BACKENDLNK@
|
||||
rigctld_LDFLAGS = @BACKENDLNK@ @PTHREAD_LIBS@ @NET_LIBS@
|
||||
rotctld_LDFLAGS = @ROT_BACKENDLNK@ @PTHREAD_LIBS@ @NET_LIBS@
|
||||
|
||||
|
||||
# temporary hack
|
||||
testbcd_LDFLAGS = -dlpreopen self
|
||||
testloc_LDFLAGS = -dlpreopen self
|
||||
|
||||
|
||||
# rigmatrix needs also libgd
|
||||
rigmatrix_LDFLAGS = -lgd -lz @BACKENDLNK@
|
||||
|
||||
|
@ -72,5 +72,5 @@ rigmatrix.html: rigmatrix_head.html rigmatrix listrigs
|
|||
for f in `./listrigs | tail -n +2 | cut -f1` ; do ( ./rigctl -m $$f -u > sup-info/support/model$$f.txt || exit 0 ) ; done
|
||||
./rigctl -l |sort -n | $(srcdir)/rig_split_lst.awk -v lst_dir="sup-info"
|
||||
|
||||
EXTRA_DIST = rigmatrix_head.html rig_split_lst.awk $(man_MANS) testctld.pl
|
||||
EXTRA_DIST = rigmatrix_head.html rig_split_lst.awk $(man_MANS) testctld.pl testrotctld.pl
|
||||
|
||||
|
|
|
@ -2,26 +2,26 @@
|
|||
* rotctl.c - (C) Stephane Fillod 2000-2009
|
||||
*
|
||||
* This program test/control a rotator using Hamlib.
|
||||
* It takes commands in interactive mode as well as
|
||||
* It takes commands in interactive mode as well as
|
||||
* from command line options.
|
||||
*
|
||||
* $Id: rotctl.c,v 1.14 2009-02-17 08:03:22 fillods Exp $
|
||||
* $Id: rotctl.c,v 1.14 2009-02-17 08:03:22 fillods Exp $
|
||||
*
|
||||
*
|
||||
* 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 2
|
||||
* 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, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
|
@ -41,13 +41,13 @@
|
|||
|
||||
#include "rotctl_parse.h"
|
||||
|
||||
/*
|
||||
/*
|
||||
* Prototypes
|
||||
*/
|
||||
void usage();
|
||||
|
||||
/*
|
||||
* Reminder: when adding long options,
|
||||
* Reminder: when adding long options,
|
||||
* keep up to date SHORT_OPTIONS, usage()'s output and man page. thanks.
|
||||
* NB: do NOT use -W since it's reserved by POSIX.
|
||||
* TODO: add an option to read from a file
|
||||
|
@ -77,7 +77,7 @@ int opt_end= 0 ; /* only used by rotctld */
|
|||
char send_cmd_term = '\r'; /* send_cmd termination char */
|
||||
|
||||
int main (int argc, char *argv[])
|
||||
{
|
||||
{
|
||||
ROT *my_rot; /* handle to rot (instance) */
|
||||
rot_model_t my_model = ROT_MODEL_DUMMY;
|
||||
|
||||
|
@ -167,7 +167,7 @@ int main (int argc, char *argv[])
|
|||
"<hamlib-developer@lists.sourceforge.net>\n\n");
|
||||
|
||||
/*
|
||||
* at least one command on command line,
|
||||
* at least one command on command line,
|
||||
* disable interactive mode
|
||||
*/
|
||||
if (optind < argc)
|
||||
|
@ -176,7 +176,7 @@ int main (int argc, char *argv[])
|
|||
my_rot = rot_init(my_model);
|
||||
|
||||
if (!my_rot) {
|
||||
fprintf(stderr, "Unknown rot num %d, or initialization error.\n",
|
||||
fprintf(stderr, "Unknown rot num %d, or initialization error.\n",
|
||||
my_model);
|
||||
fprintf(stderr, "Please check with --list option.\n");
|
||||
exit(2);
|
||||
|
|
|
@ -2,26 +2,26 @@
|
|||
* rotctl.c - (C) Stephane Fillod 2000-2009
|
||||
*
|
||||
* This program test/control a rotator using Hamlib.
|
||||
* It takes commands in interactive mode as well as
|
||||
* It takes commands in interactive mode as well as
|
||||
* from command line options.
|
||||
*
|
||||
* $Id: rotctl_parse.c,v 1.5 2009-01-04 14:49:17 fillods Exp $
|
||||
* $Id: rotctl_parse.c,v 1.5 2009-01-04 14:49:17 fillods Exp $
|
||||
*
|
||||
*
|
||||
* 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 2
|
||||
* 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, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
|
@ -70,17 +70,20 @@ static pthread_mutex_t rot_mutex = PTHREAD_MUTEX_INITIALIZER;
|
|||
struct test_table {
|
||||
unsigned char cmd;
|
||||
const char *name;
|
||||
int (*rot_routine)(ROT*, FILE*, int, const struct test_table*, const char*,
|
||||
const char*, const char*);
|
||||
int (*rot_routine)(ROT*, FILE*, int, const struct test_table*, const char*,
|
||||
const char*, const char*, const char*, const char*, const char*);
|
||||
int flags;
|
||||
const char *arg1;
|
||||
const char *arg2;
|
||||
const char *arg3;
|
||||
const char *arg4;
|
||||
const char *arg5;
|
||||
const char *arg6;
|
||||
};
|
||||
|
||||
#define declare_proto_rot(f) static int (f)(ROT *rot, FILE *fout, int interactive, \
|
||||
const struct test_table *cmd, const char *arg1, \
|
||||
const char *arg2, const char *arg3)
|
||||
const struct test_table *cmd, const char *arg1, const char *arg2, \
|
||||
const char *arg3, const char *arg4, const char *arg5, const char *arg6)
|
||||
|
||||
declare_proto_rot(set_position);
|
||||
declare_proto_rot(get_position);
|
||||
|
@ -92,6 +95,17 @@ declare_proto_rot(get_info);
|
|||
declare_proto_rot(inter_set_conf); /* interactive mode set_conf */
|
||||
declare_proto_rot(send_cmd);
|
||||
declare_proto_rot(dump_state);
|
||||
declare_proto_rot(dump_caps);
|
||||
/* Follows are functions from locator.c */
|
||||
declare_proto_rot(loc2lonlat);
|
||||
declare_proto_rot(lonlat2loc);
|
||||
declare_proto_rot(d_m_s2dec);
|
||||
declare_proto_rot(dec2d_m_s);
|
||||
declare_proto_rot(d_mm2dec);
|
||||
declare_proto_rot(dec2d_mm);
|
||||
declare_proto_rot(coord2qrb);
|
||||
declare_proto_rot(az_sp2az_lp);
|
||||
declare_proto_rot(dist_sp2dist_lp);
|
||||
|
||||
/*
|
||||
* convention: upper case cmd is set, lowercase is get
|
||||
|
@ -99,24 +113,34 @@ declare_proto_rot(dump_state);
|
|||
* NB: 'q' 'Q' '?' are reserved by interactive mode interface
|
||||
*/
|
||||
struct test_table test_list[] = {
|
||||
{ 'P', "set_pos", set_position, ARG_IN, "Azimuth", "Elevation" },
|
||||
{ 'p', "get_pos", get_position, ARG_OUT, "Azimuth", "Elevation" },
|
||||
{ 'K', "park", park, ARG_NONE, },
|
||||
{ 'S', "stop", stop, ARG_NONE, },
|
||||
{ 'R', "reset", reset, ARG_IN, "Reset" },
|
||||
{ 'M', "move", move, ARG_IN, "Direction", "Speed" },
|
||||
{ 'C', "set_conf", inter_set_conf, ARG_IN, "Token", "Value" },
|
||||
{ '_', "get_info", get_info, ARG_OUT, "Info" },
|
||||
{ 'w', "send_cmd", send_cmd, ARG_IN1|ARG_IN_LINE|ARG_OUT2, "Cmd", "Reply" },
|
||||
{ 0x8f,"dump_state", dump_state, ARG_OUT },
|
||||
{ 0x00, "", NULL },
|
||||
{ 'P', "set_pos", set_position, ARG_IN, "Azimuth", "Elevation" },
|
||||
{ 'p', "get_pos", get_position, ARG_OUT, "Azimuth", "Elevation" },
|
||||
{ 'K', "park", park, ARG_NONE, },
|
||||
{ 'S', "stop", stop, ARG_NONE, },
|
||||
{ 'R', "reset", reset, ARG_IN, "Reset" },
|
||||
{ 'M', "move", move, ARG_IN, "Direction", "Speed" },
|
||||
{ 'C', "set_conf", inter_set_conf, ARG_IN, "Token", "Value" },
|
||||
{ '_', "get_info", get_info, ARG_OUT, "Info" },
|
||||
{ 'w', "send_cmd", send_cmd, ARG_IN1|ARG_IN_LINE|ARG_OUT2, "Cmd", "Reply" },
|
||||
{ '1', "dump_caps", dump_caps, },
|
||||
{ 0x8f,"dump_state",dump_state, ARG_OUT },
|
||||
{ 'L', "lonlat2loc",lonlat2loc, ARG_IN1|ARG_IN2|ARG_IN3|ARG_OUT1, "Longitude", "Latitude", "Loc Len [2-12]", "Locator" },
|
||||
{ 'l', "loc2lonlat",loc2lonlat, ARG_IN1|ARG_OUT1|ARG_OUT2, "Locator", "Longitude", "Latitude" },
|
||||
{ 'D', "dms2dec", d_m_s2dec, ARG_IN1|ARG_IN2|ARG_IN3|ARG_IN4|ARG_OUT1, "Degrees", "Minutes", "Seconds", "S/W", "Dec Degrees" },
|
||||
{ 'd', "dec2dms", dec2d_m_s, ARG_IN1|ARG_OUT1|ARG_OUT2|ARG_OUT3|ARG_OUT4, "Dec Degrees", "Degrees", "Minutes", "Seconds", "S/W" },
|
||||
{ 'E', "dmmm2dec", d_mm2dec, ARG_IN1|ARG_IN2|ARG_IN3|ARG_OUT1, "Degrees", "Dec Minutes", "S/W", "Dec Deg" },
|
||||
{ 'e', "dec2dmmm", dec2d_mm, ARG_IN1|ARG_OUT1|ARG_OUT2|ARG_OUT3, "Dec Deg", "Degrees", "Dec Minutes", "S/W" },
|
||||
{ 'B', "qrb", coord2qrb, ARG_IN1|ARG_IN2|ARG_IN3|ARG_IN4|ARG_OUT1|ARG_OUT2, "Lon 1", "Lat 1", "Lon 2", "Lat 2", "QRB Distance", "QRB Azimuth" },
|
||||
{ 'A', "a_sp2a_lp", az_sp2az_lp, ARG_IN1|ARG_OUT1, "Short Path Deg", "Long Path Deg" },
|
||||
{ 'a', "d_sp2d_lp", dist_sp2dist_lp,ARG_IN1|ARG_OUT1, "Short Path km", "Long Path km" },
|
||||
{ 0x00, "", NULL },
|
||||
|
||||
};
|
||||
|
||||
struct test_table *find_cmd_entry(int cmd)
|
||||
{
|
||||
int i;
|
||||
for (i=0; i<MAXNBOPT && test_list[i].cmd != 0x00; i++)
|
||||
for (i = 0; i < MAXNBOPT && test_list[i].cmd != 0x00; i++)
|
||||
if (test_list[i].cmd == cmd)
|
||||
break;
|
||||
|
||||
|
@ -131,13 +155,13 @@ struct test_table *find_cmd_entry(int cmd)
|
|||
char parse_arg(const char *arg)
|
||||
{
|
||||
int i;
|
||||
for (i=0; i<MAXNBOPT && test_list[i].cmd != 0; i++)
|
||||
for (i = 0; i < MAXNBOPT && test_list[i].cmd != 0; i++)
|
||||
if (!strncmp(arg, test_list[i].name, MAXNAMSIZ))
|
||||
return test_list[i].cmd;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
* This scanf works even in presence of signals (timer, SIGIO, ..)
|
||||
*/
|
||||
static int scanfc(FILE *fin, const char *format, void *p)
|
||||
|
@ -161,16 +185,20 @@ extern int interactive;
|
|||
extern int prompt;
|
||||
extern int opt_end;
|
||||
extern char send_cmd_term;
|
||||
int ext_resp = 0;
|
||||
unsigned char resp_sep = '\n'; /* Default response separator */
|
||||
|
||||
int rotctl_parse(ROT *my_rot, FILE *fin, FILE *fout, char *argv[], int argc)
|
||||
{
|
||||
{
|
||||
int retcode; /* generic return code from functions */
|
||||
unsigned char cmd;
|
||||
struct test_table *cmd_entry;
|
||||
|
||||
char arg1[MAXARGSZ+1], *p1;
|
||||
char arg2[MAXARGSZ+1], *p2;
|
||||
char arg3[MAXARGSZ+1], *p3;
|
||||
char arg1[MAXARGSZ + 1], *p1;
|
||||
char arg2[MAXARGSZ + 1], *p2;
|
||||
char arg3[MAXARGSZ + 1], *p3;
|
||||
char arg4[MAXARGSZ + 1], *p4;
|
||||
char *p5, *p6;
|
||||
static int last_was_ret = 1;
|
||||
|
||||
if (interactive) {
|
||||
|
@ -181,11 +209,31 @@ int rotctl_parse(ROT *my_rot, FILE *fin, FILE *fout, char *argv[], int argc)
|
|||
if (scanfc(fin, "%c", &cmd) < 0)
|
||||
return -1;
|
||||
|
||||
/* Extended response protocol requested with leading '+' on command
|
||||
* string -- rotctld only! */
|
||||
if (cmd == '+' && !prompt) {
|
||||
ext_resp = 1;
|
||||
if (scanfc(fin, "%c", &cmd) < 0)
|
||||
return -1;
|
||||
} else if (cmd == '+' && prompt) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (cmd != '\\' && cmd != '_' && ispunct(cmd) && !prompt) {
|
||||
ext_resp = 1;
|
||||
resp_sep = cmd;
|
||||
if (scanfc(fin, "%c", &cmd) < 0)
|
||||
return -1;
|
||||
continue;
|
||||
} else if (cmd != '\\' && cmd != '?' && cmd != '_' && ispunct(cmd) && prompt) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* command by name */
|
||||
if (cmd == '\\') {
|
||||
unsigned char cmd_name[MAXNAMSIZ], *pcmd = cmd_name;
|
||||
int c_len = MAXNAMSIZ;
|
||||
|
||||
|
||||
if (scanfc(fin, "%c", pcmd) < 0)
|
||||
return -1;
|
||||
|
||||
|
@ -202,7 +250,6 @@ int rotctl_parse(ROT *my_rot, FILE *fin, FILE *fout, char *argv[], int argc)
|
|||
if (last_was_ret) {
|
||||
if (prompt) {
|
||||
fprintf(fout, "? for help, q to quit.\n");
|
||||
fprintf(fout, "\nRotator command: ");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -243,9 +290,9 @@ int rotctl_parse(ROT *my_rot, FILE *fin, FILE *fout, char *argv[], int argc)
|
|||
return 0;
|
||||
}
|
||||
|
||||
p1 = p2 = p3 = NULL;
|
||||
p1 = p2 = p3 = p4 = p5 = p6 = NULL;
|
||||
|
||||
if ((cmd_entry->flags & ARG_IN_LINE) &&
|
||||
if ((cmd_entry->flags & ARG_IN_LINE) &&
|
||||
(cmd_entry->flags & ARG_IN1) && cmd_entry->arg1) {
|
||||
if (interactive) {
|
||||
char *nl;
|
||||
|
@ -259,7 +306,7 @@ int rotctl_parse(ROT *my_rot, FILE *fin, FILE *fout, char *argv[], int argc)
|
|||
p1 = arg1[0]==' '?arg1+1:arg1;
|
||||
} else {
|
||||
if (!argv[optind]) {
|
||||
fprintf(stderr, "Invalid arg for command '%s'\n",
|
||||
fprintf(stderr, "Invalid arg for command '%s'\n",
|
||||
cmd_entry->name);
|
||||
exit(2);
|
||||
}
|
||||
|
@ -274,7 +321,7 @@ int rotctl_parse(ROT *my_rot, FILE *fin, FILE *fout, char *argv[], int argc)
|
|||
p1 = arg1;
|
||||
} else {
|
||||
if (!argv[optind]) {
|
||||
fprintf(stderr, "Invalid arg for command '%s'\n",
|
||||
fprintf(stderr, "Invalid arg for command '%s'\n",
|
||||
cmd_entry->name);
|
||||
exit(2);
|
||||
}
|
||||
|
@ -290,7 +337,7 @@ int rotctl_parse(ROT *my_rot, FILE *fin, FILE *fout, char *argv[], int argc)
|
|||
p2 = arg2;
|
||||
} else {
|
||||
if (!argv[optind]) {
|
||||
fprintf(stderr, "Invalid arg for command '%s'\n",
|
||||
fprintf(stderr, "Invalid arg for command '%s'\n",
|
||||
cmd_entry->name);
|
||||
exit(2);
|
||||
}
|
||||
|
@ -306,7 +353,7 @@ int rotctl_parse(ROT *my_rot, FILE *fin, FILE *fout, char *argv[], int argc)
|
|||
p3 = arg3;
|
||||
} else {
|
||||
if (!argv[optind]) {
|
||||
fprintf(stderr, "Invalid arg for command '%s'\n",
|
||||
fprintf(stderr, "Invalid arg for command '%s'\n",
|
||||
cmd_entry->name);
|
||||
exit(2);
|
||||
}
|
||||
|
@ -314,6 +361,23 @@ int rotctl_parse(ROT *my_rot, FILE *fin, FILE *fout, char *argv[], int argc)
|
|||
}
|
||||
}
|
||||
|
||||
if (p1 && p1[0]!='?' && (cmd_entry->flags & ARG_IN4) && cmd_entry->arg4) {
|
||||
if (interactive) {
|
||||
if (prompt)
|
||||
fprintf(fout, "%s: ", cmd_entry->arg4);
|
||||
if (scanfc(fin, "%s", arg4) < 0)
|
||||
return -1;
|
||||
p4 = arg4;
|
||||
} else {
|
||||
if (!argv[optind]) {
|
||||
fprintf(stderr, "Invalid arg for command '%s'\n",
|
||||
cmd_entry->name);
|
||||
exit(2);
|
||||
}
|
||||
p4 = argv[optind++];
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* mutex locking needed because rigctld is multithreaded
|
||||
* and hamlib is not MT-safe
|
||||
|
@ -323,28 +387,60 @@ int rotctl_parse(ROT *my_rot, FILE *fin, FILE *fout, char *argv[], int argc)
|
|||
#endif
|
||||
|
||||
if (!prompt)
|
||||
rig_debug(RIG_DEBUG_TRACE, "rotctl: %c '%s' '%s' '%s'\n",
|
||||
cmd, p1, p2, p3);
|
||||
rig_debug(RIG_DEBUG_TRACE, "rotctl(d): %c '%s' '%s' '%s' '%s'\n",
|
||||
cmd, p1, p2, p3, p4);
|
||||
|
||||
retcode = (*cmd_entry->rot_routine)(my_rot, fout, interactive,
|
||||
cmd_entry, p1, p2, p3);
|
||||
/*
|
||||
* Extended Response protocol: output received command name and arguments
|
||||
* response.
|
||||
*/
|
||||
if (interactive && ext_resp && !prompt) {
|
||||
char a1[MAXARGSZ + 1];
|
||||
char a2[MAXARGSZ + 1];
|
||||
char a3[MAXARGSZ + 1];
|
||||
char a4[MAXARGSZ + 1];
|
||||
|
||||
p1 == NULL ? a1[0] = '\0' : snprintf(a1, sizeof(a1), " %s", p1);
|
||||
p2 == NULL ? a2[0] = '\0' : snprintf(a2, sizeof(a2), " %s", p2);
|
||||
p3 == NULL ? a3[0] = '\0' : snprintf(a3, sizeof(a3), " %s", p3);
|
||||
p4 == NULL ? a4[0] = '\0' : snprintf(a4, sizeof(a4), " %s", p4);
|
||||
|
||||
fprintf(fout, "%s:%s%s%s%s%c", cmd_entry->name, a1, a2, a3, a4, resp_sep);
|
||||
}
|
||||
|
||||
retcode = (*cmd_entry->rot_routine)(my_rot, fout, interactive,
|
||||
cmd_entry, p1, p2, p3, p4, p5, p6);
|
||||
|
||||
#ifdef HAVE_PTHREAD
|
||||
pthread_mutex_unlock(&rot_mutex);
|
||||
#endif
|
||||
|
||||
if (retcode != RIG_OK) {
|
||||
if (interactive && !prompt)
|
||||
fprintf(fout, NETROTCTL_RET "%d\n", retcode); /* only for rotctld */
|
||||
/* only for rotctld */
|
||||
if (interactive && !prompt) {
|
||||
fprintf(fout, NETROTCTL_RET "%d\n", retcode);
|
||||
ext_resp = 0;
|
||||
resp_sep = '\n';
|
||||
}
|
||||
else
|
||||
fprintf(fout, "%s: error = %s\n", cmd_entry->name, rigerror(retcode));
|
||||
fprintf(fout, "%s: error = %s\n", cmd_entry->name, rigerror(retcode));
|
||||
} else {
|
||||
if (interactive && !prompt) { /* only for rotctld */
|
||||
if (!(cmd_entry->flags & ARG_OUT) && !opt_end) /* netrotctl RIG_OK */
|
||||
/* only for rotctld */
|
||||
if (interactive && !prompt) {
|
||||
/* netrotctl RIG_OK */
|
||||
if (!(cmd_entry->flags & ARG_OUT)
|
||||
&& !opt_end && !ext_resp && cmd != 0xf0)
|
||||
fprintf(fout, NETROTCTL_RET "0\n");
|
||||
else if ((cmd_entry->flags & ARG_OUT) && opt_end) /* Nate's protocol */
|
||||
/* block marker protocol */
|
||||
else if (ext_resp && cmd != 0xf0) {
|
||||
fprintf(fout, NETROTCTL_RET "0\n");
|
||||
ext_resp = 0;
|
||||
resp_sep = '\n';
|
||||
}
|
||||
/* Nate's protocol (obsolete) */
|
||||
else if ((cmd_entry->flags & ARG_OUT) && opt_end)
|
||||
fprintf(fout, "END\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fflush(fout);
|
||||
|
@ -360,26 +456,31 @@ void version()
|
|||
printf("%s\n", hamlib_copyright);
|
||||
}
|
||||
|
||||
|
||||
void usage_rot(FILE *fout)
|
||||
{
|
||||
int i;
|
||||
int i, nbspaces;
|
||||
|
||||
fprintf(fout, "Commands (may not be available for this rotator):\n");
|
||||
for (i=0; test_list[i].cmd != 0; i++) {
|
||||
fprintf(fout, "%c: %-16s(", test_list[i].cmd, test_list[i].name);
|
||||
if (test_list[i].arg1)
|
||||
fprintf(fout, "%s", test_list[i].arg1);
|
||||
if (test_list[i].arg2)
|
||||
fprintf(fout, ",%s", test_list[i].arg2);
|
||||
if (test_list[i].arg3)
|
||||
fprintf(fout, ",%s", test_list[i].arg3);
|
||||
fprintf(fout, ") \t");
|
||||
fprintf(fout, "Commands (some may not be available for this rig):\n");
|
||||
for (i = 0; test_list[i].cmd != 0; i++) {
|
||||
fprintf(fout, "%c: %-12s(", isprint(test_list[i].cmd) ?
|
||||
test_list[i].cmd : '?', test_list[i].name);
|
||||
|
||||
if (i%2)
|
||||
fprintf(fout, "\n");
|
||||
nbspaces = 16;
|
||||
if (test_list[i].arg1 && (test_list[i].flags&ARG_IN1))
|
||||
nbspaces -= fprintf(fout, "%s", test_list[i].arg1);
|
||||
if (test_list[i].arg2 && (test_list[i].flags&ARG_IN2))
|
||||
nbspaces -= fprintf(fout, ", %s", test_list[i].arg2);
|
||||
if (test_list[i].arg3 && (test_list[i].flags&ARG_IN3))
|
||||
nbspaces -= fprintf(fout, ", %s", test_list[i].arg3);
|
||||
if (test_list[i].arg4 && (test_list[i].flags&ARG_IN4))
|
||||
nbspaces -= fprintf(fout, ", %s", test_list[i].arg4);
|
||||
|
||||
fprintf(fout, ")\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int print_conf_list(const struct confparams *cfp, rig_ptr_t data)
|
||||
{
|
||||
ROT *rot = (ROT*) data;
|
||||
|
@ -387,33 +488,33 @@ int print_conf_list(const struct confparams *cfp, rig_ptr_t data)
|
|||
char buf[128] = "";
|
||||
|
||||
rot_get_conf(rot, cfp->token, buf);
|
||||
printf("%s: \"%s\"\n" "\tDefault: %s, Value: %s\n",
|
||||
cfp->name, cfp->tooltip,
|
||||
printf("%s: \"%s\"\n" "\tDefault: %s, Value: %s\n",
|
||||
cfp->name, cfp->tooltip,
|
||||
cfp->dflt, buf );
|
||||
|
||||
switch (cfp->type) {
|
||||
case RIG_CONF_NUMERIC:
|
||||
printf("\tRange: %.1f..%.1f, step %.1f\n",
|
||||
printf("\tRange: %.1f..%.1f, step %.1f\n",
|
||||
cfp->u.n.min, cfp->u.n.max, cfp->u.n.step);
|
||||
break;
|
||||
case RIG_CONF_COMBO:
|
||||
if (!cfp->u.c.combostr)
|
||||
break;
|
||||
break;
|
||||
printf("\tCombo: %s", cfp->u.c.combostr[0]);
|
||||
for (i=1 ; i<RIG_COMBO_MAX && cfp->u.c.combostr[i]; i++)
|
||||
printf(", %s", cfp->u.c.combostr[i]);
|
||||
for (i = 1 ; i < RIG_COMBO_MAX && cfp->u.c.combostr[i]; i++)
|
||||
printf(", %s", cfp->u.c.combostr[i]);
|
||||
printf("\n");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 1; /* !=0, we want them all ! */
|
||||
return 1; /* != 0, we want them all ! */
|
||||
}
|
||||
|
||||
static int print_model_list(const struct rot_caps *caps, void *data)
|
||||
{
|
||||
printf("%d\t%-14s%-16s%-8s%s\n", caps->rot_model, caps->mfg_name,
|
||||
printf("%-8d%-16s%-25s%-10s%s\n", caps->rot_model, caps->mfg_name,
|
||||
caps->model_name, caps->version, rig_strstatus(caps->status));
|
||||
return 1; /* !=0, we want them all ! */
|
||||
}
|
||||
|
@ -424,7 +525,7 @@ void list_models()
|
|||
|
||||
rot_load_all_backends();
|
||||
|
||||
printf("Rot#\tMfg Model Vers.\n");
|
||||
printf("Rot# Mfg Model Vers.\n");
|
||||
status = rot_list_foreach(print_model_list, NULL);
|
||||
if (status != RIG_OK ) {
|
||||
printf("rot_list_foreach: error = %s \n", rigerror(status));
|
||||
|
@ -459,6 +560,7 @@ int set_conf(ROT *my_rot, char *conf_parms)
|
|||
* static int (f)(ROT *rot, int interactive, const void *arg1, const void *arg2, const void *arg3, const void *arg4)
|
||||
*/
|
||||
|
||||
/* 'P' */
|
||||
declare_proto_rot(set_position)
|
||||
{
|
||||
azimuth_t az;
|
||||
|
@ -469,6 +571,7 @@ declare_proto_rot(set_position)
|
|||
return rot_set_position(rot, az, el);
|
||||
}
|
||||
|
||||
/* 'p' */
|
||||
declare_proto_rot(get_position)
|
||||
{
|
||||
int status;
|
||||
|
@ -477,28 +580,30 @@ declare_proto_rot(get_position)
|
|||
|
||||
status = rot_get_position(rot, &az, &el);
|
||||
if (status != RIG_OK)
|
||||
return status;
|
||||
if (interactive && prompt)
|
||||
return status;
|
||||
if ((interactive && prompt) || (interactive && !prompt && ext_resp))
|
||||
fprintf(fout, "%s: ", cmd->arg1);
|
||||
fprintf(fout, "%f\n", az);
|
||||
if (interactive && prompt)
|
||||
fprintf(fout, "%f%c", az, resp_sep);
|
||||
if ((interactive && prompt) || (interactive && !prompt && ext_resp))
|
||||
fprintf(fout, "%s: ", cmd->arg2);
|
||||
fprintf(fout, "%f\n", el);
|
||||
fprintf(fout, "%f%c", el, resp_sep);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/* 'S' */
|
||||
declare_proto_rot(stop)
|
||||
{
|
||||
return rot_stop(rot);
|
||||
}
|
||||
|
||||
/* 'K' */
|
||||
declare_proto_rot(park)
|
||||
{
|
||||
return rot_park(rot);
|
||||
}
|
||||
|
||||
|
||||
/* 'R' */
|
||||
declare_proto_rot(reset)
|
||||
{
|
||||
rot_reset_t reset;
|
||||
|
@ -507,28 +612,31 @@ declare_proto_rot(reset)
|
|||
return rot_reset(rot, reset);
|
||||
}
|
||||
|
||||
/* '_' */
|
||||
declare_proto_rot(get_info)
|
||||
{
|
||||
const char *s;
|
||||
|
||||
s = rot_get_info(rot);
|
||||
if (interactive && prompt)
|
||||
if ((interactive && prompt) || (interactive && !prompt && ext_resp))
|
||||
fprintf(fout, "%s: ", cmd->arg1);
|
||||
fprintf(fout, "%s\n", s ? s : "None");
|
||||
fprintf(fout, "%s%c", s ? s : "None", resp_sep);
|
||||
|
||||
return RIG_OK;
|
||||
}
|
||||
|
||||
/* 'M' */
|
||||
declare_proto_rot(move)
|
||||
{
|
||||
int direction;
|
||||
int speed;
|
||||
int direction;
|
||||
int speed;
|
||||
|
||||
sscanf(arg1, "%d", &direction);
|
||||
sscanf(arg1, "%d", &direction);
|
||||
sscanf(arg2, "%d", &speed);
|
||||
return rot_move(rot, direction, speed);
|
||||
}
|
||||
|
||||
/* 'C' */
|
||||
declare_proto_rot(inter_set_conf)
|
||||
{
|
||||
token_t token;
|
||||
|
@ -539,7 +647,19 @@ declare_proto_rot(inter_set_conf)
|
|||
return rot_set_conf(rot, token, val);
|
||||
}
|
||||
|
||||
/* For rotctld internal use */
|
||||
|
||||
/* '1' */
|
||||
declare_proto_rot(dump_caps)
|
||||
{
|
||||
dumpcaps_rot(rot, fout);
|
||||
|
||||
return RIG_OK;
|
||||
}
|
||||
|
||||
|
||||
/* For rotctld internal use
|
||||
* '0x8f'
|
||||
*/
|
||||
declare_proto_rot(dump_state)
|
||||
{
|
||||
struct rot_state *rs = &rot->state;
|
||||
|
@ -548,13 +668,29 @@ declare_proto_rot(dump_state)
|
|||
* - Protocol version
|
||||
*/
|
||||
#define ROTCTLD_PROT_VER 0
|
||||
fprintf(fout, "%d\n", ROTCTLD_PROT_VER);
|
||||
fprintf(fout, "%d\n", rot->caps->rot_model);
|
||||
if ((interactive && prompt) || (interactive && !prompt && ext_resp))
|
||||
fprintf(fout, "rotctld Protocol Ver: ");
|
||||
fprintf(fout, "%d%c", ROTCTLD_PROT_VER, resp_sep);
|
||||
|
||||
fprintf(fout, "%lf\n", rs->min_az);
|
||||
fprintf(fout, "%lf\n", rs->max_az);
|
||||
fprintf(fout, "%lf\n", rs->min_el);
|
||||
fprintf(fout, "%lf\n", rs->max_el);
|
||||
if ((interactive && prompt) || (interactive && !prompt && ext_resp))
|
||||
fprintf(fout, "Rotor Model: ");
|
||||
fprintf(fout, "%d%c", rot->caps->rot_model, resp_sep);
|
||||
|
||||
if ((interactive && prompt) || (interactive && !prompt && ext_resp))
|
||||
fprintf(fout, "Minimum Azimuth: ");
|
||||
fprintf(fout, "%lf%c", rs->min_az, resp_sep);
|
||||
|
||||
if ((interactive && prompt) || (interactive && !prompt && ext_resp))
|
||||
fprintf(fout, "Maximum Azimuth: ");
|
||||
fprintf(fout, "%lf%c", rs->max_az, resp_sep);
|
||||
|
||||
if ((interactive && prompt) || (interactive && !prompt && ext_resp))
|
||||
fprintf(fout, "Minimum Elevation: ");
|
||||
fprintf(fout, "%lf%c", rs->min_el, resp_sep);
|
||||
|
||||
if ((interactive && prompt) || (interactive && !prompt && ext_resp))
|
||||
fprintf(fout, "Maximum Elevation: ");
|
||||
fprintf(fout, "%lf%c", rs->max_el, resp_sep);
|
||||
|
||||
return RIG_OK;
|
||||
}
|
||||
|
@ -584,21 +720,21 @@ declare_proto_rot(send_cmd)
|
|||
if (send_cmd_term == -1 || backend_num == -1) {
|
||||
const char *p = arg1, *pp = NULL;
|
||||
int i;
|
||||
for (i=0; i < BUFSZ-1 && p != pp; i++) {
|
||||
pp = p+1;
|
||||
bufcmd[i] = strtol(p+1, (char **) &p, 0);
|
||||
for (i = 0; i < BUFSZ - 1 && p != pp; i++) {
|
||||
pp = p + 1;
|
||||
bufcmd[i] = strtol(p + 1, (char **) &p, 0);
|
||||
}
|
||||
/* must save length to allow 0x00 to be sent as part of a command
|
||||
*/
|
||||
cmd_len = i-1;
|
||||
cmd_len = i - 1;
|
||||
|
||||
/* no End Of Message chars */
|
||||
eom_buf[0] = '\0';
|
||||
} else {
|
||||
/* text protocol */
|
||||
|
||||
strncpy(bufcmd,arg1,BUFSZ);
|
||||
bufcmd[BUFSZ-2] = '\0';
|
||||
strncpy(bufcmd,arg1, BUFSZ);
|
||||
bufcmd[BUFSZ - 2] = '\0';
|
||||
|
||||
cmd_len = strlen(bufcmd);
|
||||
|
||||
|
@ -627,12 +763,12 @@ declare_proto_rot(send_cmd)
|
|||
retval = read_string(&rs->rotport, buf, BUFSZ, eom_buf, strlen(eom_buf));
|
||||
if (retval < 0)
|
||||
break;
|
||||
|
||||
|
||||
if (retval < BUFSZ)
|
||||
buf[retval] = '\0';
|
||||
else
|
||||
buf[BUFSZ-1] = '\0';
|
||||
|
||||
|
||||
fprintf(fout, "%s\n", buf);
|
||||
|
||||
} while (retval > 0);
|
||||
|
@ -643,3 +779,207 @@ declare_proto_rot(send_cmd)
|
|||
return retval;
|
||||
}
|
||||
|
||||
/* 'L' */
|
||||
declare_proto_rot(lonlat2loc)
|
||||
{
|
||||
unsigned char loc[MAXARGSZ + 1];
|
||||
double lat, lon;
|
||||
int err, pair;
|
||||
|
||||
sscanf(arg1, "%lf", &lon);
|
||||
sscanf(arg2, "%lf", &lat);
|
||||
sscanf(arg3, "%d", &pair);
|
||||
|
||||
pair /= 2;
|
||||
|
||||
err = longlat2locator(lon, lat, (char *)&loc, pair);
|
||||
|
||||
if (err != RIG_OK)
|
||||
return err;
|
||||
|
||||
if ((interactive && prompt) || (interactive && !prompt && ext_resp))
|
||||
fprintf(fout, "%s: ", cmd->arg4);
|
||||
fprintf(fout, "%s%c", loc, resp_sep);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/* 'l' */
|
||||
declare_proto_rot(loc2lonlat)
|
||||
{
|
||||
unsigned char loc[MAXARGSZ + 1];
|
||||
double lat, lon;
|
||||
int status;
|
||||
|
||||
sscanf(arg1, "%s", (char *)&loc);
|
||||
|
||||
status = locator2longlat(&lon, &lat, (const char *)loc);
|
||||
|
||||
if (status != RIG_OK)
|
||||
return status;
|
||||
|
||||
if ((interactive && prompt) || (interactive && !prompt && ext_resp))
|
||||
fprintf(fout, "%s: ", cmd->arg2);
|
||||
fprintf(fout, "%f%c", lon, resp_sep);
|
||||
if ((interactive && prompt) || (interactive && !prompt && ext_resp))
|
||||
fprintf(fout, "%s: ", cmd->arg3);
|
||||
fprintf(fout, "%f%c", lat, resp_sep);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/* 'D' */
|
||||
declare_proto_rot(d_m_s2dec)
|
||||
{
|
||||
int deg, min, sw;
|
||||
double sec, dec_deg;
|
||||
|
||||
sscanf(arg1, "%d", °);
|
||||
sscanf(arg2, "%d", &min);
|
||||
sscanf(arg3, "%lf", &sec);
|
||||
sscanf(arg4, "%d", &sw);
|
||||
|
||||
dec_deg = dms2dec(deg, min, sec, sw);
|
||||
|
||||
if ((interactive && prompt) || (interactive && !prompt && ext_resp))
|
||||
fprintf(fout, "%s: ", cmd->arg5);
|
||||
fprintf(fout, "%lf%c", dec_deg, resp_sep);
|
||||
|
||||
return RIG_OK;
|
||||
}
|
||||
|
||||
/* 'd' */
|
||||
declare_proto_rot(dec2d_m_s)
|
||||
{
|
||||
int deg, min, sw, err;
|
||||
double sec, dec_deg;
|
||||
|
||||
sscanf(arg1, "%lf", &dec_deg);
|
||||
|
||||
err = dec2dms(dec_deg, °, &min, &sec, &sw);
|
||||
|
||||
if (err != RIG_OK)
|
||||
return err;
|
||||
|
||||
if ((interactive && prompt) || (interactive && !prompt && ext_resp))
|
||||
fprintf(fout, "%s: ", cmd->arg2);
|
||||
fprintf(fout, "%d%c", deg, resp_sep);
|
||||
if ((interactive && prompt) || (interactive && !prompt && ext_resp))
|
||||
fprintf(fout, "%s: ", cmd->arg3);
|
||||
fprintf(fout, "%d%c", min, resp_sep);
|
||||
if ((interactive && prompt) || (interactive && !prompt && ext_resp))
|
||||
fprintf(fout, "%s: ", cmd->arg4);
|
||||
fprintf(fout, "%lf%c", sec, resp_sep);
|
||||
if ((interactive && prompt) || (interactive && !prompt && ext_resp))
|
||||
fprintf(fout, "%s: ", cmd->arg5);
|
||||
fprintf(fout, "%d%c", sw, resp_sep);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/* 'E' */
|
||||
declare_proto_rot(d_mm2dec)
|
||||
{
|
||||
int deg, sw;
|
||||
double dec_deg, min;
|
||||
|
||||
sscanf(arg1, "%d", °);
|
||||
sscanf(arg2, "%lf", &min);
|
||||
sscanf(arg3, "%d", &sw);
|
||||
|
||||
dec_deg = dmmm2dec(deg, min, sw);
|
||||
|
||||
if ((interactive && prompt) || (interactive && !prompt && ext_resp))
|
||||
fprintf(fout, "%s: ", cmd->arg4);
|
||||
fprintf(fout, "%lf%c", dec_deg, resp_sep);
|
||||
|
||||
return RIG_OK;
|
||||
}
|
||||
|
||||
/* 'e' */
|
||||
declare_proto_rot(dec2d_mm)
|
||||
{
|
||||
int deg, sw, err;
|
||||
double min, dec_deg;
|
||||
|
||||
sscanf(arg1, "%lf", &dec_deg);
|
||||
|
||||
err = dec2dmmm(dec_deg, °, &min, &sw);
|
||||
|
||||
if (err != RIG_OK)
|
||||
return err;
|
||||
|
||||
if ((interactive && prompt) || (interactive && !prompt && ext_resp))
|
||||
fprintf(fout, "%s: ", cmd->arg2);
|
||||
fprintf(fout, "%d%c", deg, resp_sep);
|
||||
if ((interactive && prompt) || (interactive && !prompt && ext_resp))
|
||||
fprintf(fout, "%s: ", cmd->arg3);
|
||||
fprintf(fout, "%lf%c", min, resp_sep);
|
||||
if ((interactive && prompt) || (interactive && !prompt && ext_resp))
|
||||
fprintf(fout, "%s: ", cmd->arg4);
|
||||
fprintf(fout, "%d%c", sw, resp_sep);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/* 'B' */
|
||||
declare_proto_rot(coord2qrb)
|
||||
{
|
||||
double lon1, lat1, lon2, lat2, dist, az;
|
||||
int err;
|
||||
|
||||
sscanf(arg1, "%lf", &lon1);
|
||||
sscanf(arg2, "%lf", &lat1);
|
||||
sscanf(arg3, "%lf", &lon2);
|
||||
sscanf(arg4, "%lf", &lat2);
|
||||
|
||||
err = qrb(lon1, lat1, lon2, lat2, &dist, &az);
|
||||
|
||||
if (err != RIG_OK)
|
||||
return err;
|
||||
|
||||
if ((interactive && prompt) || (interactive && !prompt && ext_resp))
|
||||
fprintf(fout, "%s: ", cmd->arg5);
|
||||
fprintf(fout, "%lf%c", dist, resp_sep);
|
||||
if ((interactive && prompt) || (interactive && !prompt && ext_resp))
|
||||
fprintf(fout, "%s: ", cmd->arg6);
|
||||
fprintf(fout, "%lf%c", az, resp_sep);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/* 'A' */
|
||||
declare_proto_rot(az_sp2az_lp)
|
||||
{
|
||||
double az_sp, az_lp;
|
||||
|
||||
sscanf(arg1, "%lf", &az_sp);
|
||||
|
||||
az_lp = azimuth_long_path(az_sp);
|
||||
|
||||
if (az_lp < 0)
|
||||
return -RIG_EINVAL;
|
||||
|
||||
if ((interactive && prompt) || (interactive && !prompt && ext_resp))
|
||||
fprintf(fout, "%s: ", cmd->arg2);
|
||||
fprintf(fout, "%lf%c", az_lp, resp_sep);
|
||||
|
||||
return RIG_OK;
|
||||
}
|
||||
|
||||
/* 'a' */
|
||||
declare_proto_rot(dist_sp2dist_lp)
|
||||
{
|
||||
double dist_sp, dist_lp;
|
||||
|
||||
sscanf(arg1, "%lf", &dist_sp);
|
||||
|
||||
dist_lp = distance_long_path(dist_sp);
|
||||
|
||||
if ((interactive && prompt) || (interactive && !prompt && ext_resp))
|
||||
fprintf(fout, "%s: ", cmd->arg2);
|
||||
fprintf(fout, "%lf%c", dist_lp, resp_sep);
|
||||
|
||||
return RIG_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -2,26 +2,26 @@
|
|||
* rotctl_parse.h - (C) Stephane Fillod 2000-2008
|
||||
*
|
||||
* This program test/control a radio using Hamlib.
|
||||
* It takes commands in interactive mode as well as
|
||||
* It takes commands in interactive mode as well as
|
||||
* from command line options.
|
||||
*
|
||||
* $Id: rotctl_parse.h,v 1.1 2008-09-12 22:55:09 fillods Exp $
|
||||
* $Id: rotctl_parse.h,v 1.1 2008-09-12 22:55:09 fillods Exp $
|
||||
*
|
||||
*
|
||||
* 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 2
|
||||
* 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, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ROTCTL_PARSE_H
|
||||
|
@ -30,7 +30,14 @@
|
|||
#include <stdio.h>
|
||||
#include <hamlib/rotator.h>
|
||||
|
||||
/*
|
||||
/*
|
||||
* external prototype
|
||||
*/
|
||||
|
||||
int dumpcaps_rot (ROT *, FILE *);
|
||||
|
||||
|
||||
/*
|
||||
* Prototypes
|
||||
*/
|
||||
void usage_rot(FILE *);
|
||||
|
|
353
tests/rotctld.8
353
tests/rotctld.8
|
@ -2,7 +2,7 @@
|
|||
.\" First parameter, NAME, should be all caps
|
||||
.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection
|
||||
.\" other parameters are allowed: see man(7), man(1)
|
||||
.TH ROTCTLD "8" "January 14, 2009" "Hamlib" "Rotator Control Daemon"
|
||||
.TH ROTCTLD "8" "February 14, 2010" "Hamlib" "Rotator Control Daemon"
|
||||
.\" Please adjust this date whenever revising the manpage.
|
||||
.\"
|
||||
.\" Some roff macros, for reference:
|
||||
|
@ -16,19 +16,21 @@
|
|||
.\" .sp <n> insert n+1 empty lines
|
||||
.\" for manpage-specific macros, see man(7)
|
||||
.SH NAME
|
||||
rotctld \- Hamlib rotator control daemon
|
||||
rotctld \- Hamlib TCP rotator control daemon
|
||||
.SH SYNOPSIS
|
||||
.B rotctld
|
||||
[\fIOPTION\fR]...
|
||||
.SH DESCRIPTION
|
||||
The \fBrotctld\fP program is an EXPERIMENTAL \fBHamlib\fP rotator daemon that
|
||||
handles TCP client requests. This allows multiple user programs to share one
|
||||
rotator. Multiple rotators can be controlled on different TCP ports. The syntax
|
||||
of the commands are the same as \fBrotctl\fP. It is hoped that \fBrotctld\fP
|
||||
will be especially useful for languages such as Perl, Python, and others.
|
||||
The \fBrotctld\fP program is an NEW \fBHamlib\fP rotator control daemon ready for
|
||||
testing that handles client requests via TCP sockets. This allows multiple user
|
||||
programs to share one rotator (this needs testing). Multiple rotators can be
|
||||
controlled on different TCP ports by use of multiple \fBrotctld\fP processes. The
|
||||
syntax of the commands are the same as \fBrotctl\fP. It is hoped that \fBrotctld\fP
|
||||
will be especially useful for client authors using languages such as Perl, Python,
|
||||
PHP, and others.
|
||||
.PP
|
||||
.\" TeX users may be more comfortable with the \fB<whatever>\fP and
|
||||
.\" \fI<whatever>\fP escape sequences to invoke bold face and italics,
|
||||
.\" \fI<whatever>\fP escape sequences to invoke bold face and italics,
|
||||
.\" respectively.
|
||||
\fBrotctld\fP communicates to a client through a TCP socket using text
|
||||
commands shared with \fBrotctl\fP. The protocol is simple, commands are sent
|
||||
|
@ -37,20 +39,28 @@ the requested values, one per line, when successful, otherwise, it responds
|
|||
with one line "RPTR x", where x is a negative number indicating the error code.
|
||||
Commands that do not return values respond with the line "RPTR x", where x
|
||||
is zero when successful, otherwise is a regative number indicating the error code.
|
||||
Each line is terminated with a newline '\\n' character.
|
||||
Each line is terminated with a newline '\\n' character. This protocol is primarily
|
||||
for use by the \fINET rotctl\fP (rot model 2) backend.
|
||||
.PP
|
||||
Keep in mind that \fBHamlib\fP is BETA level software.
|
||||
A separate \fBExtended Response\fP protocol extends the above
|
||||
behavior by echoing the received command string as a header, any returned values
|
||||
as a key: value pair, and the "RPTR x" string as the end of response marker which
|
||||
includes the \fBHamlib\fP success or failure value. See the \fIPROTOCOL\fP
|
||||
section for details. Consider using this protocol for clients that will interact
|
||||
with \fBrotctld\fP directly through a TCP socket.
|
||||
.PP
|
||||
Keep in mind that \fBHamlib\fP is BETA level software.
|
||||
While a lot of backend libraries lack complete rotator support, the basic functions
|
||||
are usually well supported. The API may change without publicized notice,
|
||||
are usually well supported. The API may change without publicized notice,
|
||||
while an advancement of the minor version (e.g. 1.1.x to 1.2.x) indicates such
|
||||
a change.
|
||||
.PP
|
||||
Please report bugs and provide feedback at the e-mail address given in the
|
||||
Please report bugs and provide feedback at the e-mail address given in the
|
||||
REPORTING BUGS section. Patches and code enhancements are also welcome.
|
||||
.SH OPTIONS
|
||||
This program follows the usual GNU command line syntax, with long
|
||||
options starting with two dashes ('-').
|
||||
|
||||
.PP
|
||||
Here is a summary of the supported options:
|
||||
.TP
|
||||
.B \-m, --model=id
|
||||
|
@ -58,13 +68,19 @@ Select rotator model number. See -l, "list" option below.
|
|||
.TP
|
||||
.B \-r, --rot-file=device
|
||||
Use \fIdevice\fP as the file name of the port the rotator is connected.
|
||||
Often a serial port, but could be a USB to serial adapter. Typically
|
||||
/dev/ttyS0, /dev/ttyS1, /dev/ttyUSB0, etc.
|
||||
Often a serial port, but could be a USB to serial adapter or USB port device.
|
||||
Typically /dev/ttyS0, /dev/ttyS1, /dev/ttyUSB0, etc.
|
||||
.TP
|
||||
.B \-s, --serial-speed=baud
|
||||
Set serial speed to \fIbaud\fP rate. Uses maximum serial speed from rotor
|
||||
backend capabilities (set by -m above) as the default.
|
||||
.TP
|
||||
.B \-T, --listen-addr=IPADDR
|
||||
Use \fIIPADDR\fP as the listening IP address. The default is ANY.
|
||||
.TP
|
||||
.B \-t, --port=number
|
||||
Use \fInumber\fP as the TCP listening port. The default is 4533.
|
||||
.TP
|
||||
.B \-L, --show-conf
|
||||
List all config parameters for the rotator defined with -m above.
|
||||
.TP
|
||||
|
@ -73,18 +89,18 @@ Set config parameter. e.g. --set-conf=stop_bits=2
|
|||
.br
|
||||
Use -L option for a list.
|
||||
.TP
|
||||
.B \-e, --end-marker
|
||||
Use END marker in rotctld protocol.
|
||||
.TP
|
||||
.B \-t, --port=number
|
||||
Use \fInumber\fP as the TCP listening port. The default is 4533.
|
||||
.TP
|
||||
.B \-T, --listen-addr=IPADDR
|
||||
Use \fIIPADDR\fP as the listening IP address. The default is ANY.
|
||||
.TP
|
||||
.B \-l, --list
|
||||
List all model numbers defined in \fBHamlib\fP and exit.
|
||||
.TP
|
||||
.B \-u, --dump-caps
|
||||
Dump capabilities for the radio defined with -m above and exit.
|
||||
.TP
|
||||
.B \-e, --end-marker
|
||||
Use END marker in rotctld protocol.
|
||||
.br
|
||||
N.B.: This option can be considered obsolete. Please consider using the block
|
||||
protocol instead (see \fIPROTOCOL\fP below).
|
||||
.TP
|
||||
.B \-v, --verbose
|
||||
Set verbose mode, cumulative (see DIAGNOSTICS below).
|
||||
.TP
|
||||
|
@ -95,109 +111,302 @@ Show a summary of these options and exit.
|
|||
Show the version of \fBrotctld\fP and exit.
|
||||
.PP
|
||||
\fBN.B.\fP Some options may not be implemented by a given backend and will
|
||||
return an error. This is most likely to occur with the \fI\-\-set-conf\fP
|
||||
return an error. This is most likely to occur with the \fI\-\-set-conf\fP
|
||||
and \fI\-\-show-conf\fP options.
|
||||
.PP
|
||||
Please note that the backend for the rotator to be controlled,
|
||||
or the rotator itself may not support some commands. In that case,
|
||||
Please note that the backend for the rotator to be controlled,
|
||||
or the rotator itself may not support some commands. In that case,
|
||||
the operation will fail with a \fBHamlib\fP error code.
|
||||
.SH COMMANDS
|
||||
Commands can be sent over the TCP socket either as a single char, or as a
|
||||
long command name plus the value(s) on one '\\n' terminated line. See
|
||||
PROTOCOL.
|
||||
Commands can be sent over the TCP socket either as a single char, or as a
|
||||
long command name plus the value(s) space separated on one '\\n' terminated
|
||||
line. See \fIPROTOCOL\fP.
|
||||
.PP
|
||||
Since most of the \fBHamlib\fP operations have a \fIset\fP and a \fIget\fP method,
|
||||
an upper case letter will be used for \fIset\fP method whereas the
|
||||
an upper case letter will be used for \fIset\fP methods whereas the
|
||||
corresponding lower case letter refers to the \fIget\fP method. Each operation
|
||||
also has a long name, prepend a backslash to send a long command name.
|
||||
also has a long name; prepend a backslash to send a long command name.
|
||||
.PP
|
||||
Please note that the backend for the rotator to be controlled,
|
||||
or the rotator itself may not support some commands. In that case,
|
||||
the operation will fail with a \fBHamlib\fP error message.
|
||||
Example (Perl): `print $socket "\\\\dump_caps\\n";' to see what the rotor's
|
||||
backend can do (NOTE: In Perl and many other languages a '\\' will need to be
|
||||
escaped with a preceding '\\' so that even though two backslash characters
|
||||
appear in the code, only one will be passed to \fBrotctld\fP. This is a
|
||||
possible bug!).
|
||||
.PP
|
||||
Here is a summary of the supported commands:
|
||||
Please note that the backend for the rotator to be controlled, or the rotator
|
||||
itself may not support some commands. In that case, the operation will fail
|
||||
with a \fBHamlib\fP error message.
|
||||
.PP
|
||||
Here is a summary of the supported commands (In the case of "set" commands the
|
||||
quoted string is replaced by the value in the description. In the case of "get"
|
||||
commands the quoted string is the key name of the value returned.):
|
||||
.TP
|
||||
.B P, set_pos
|
||||
Set position: azimuth and elevation.
|
||||
.B P, set_pos 'Azimuth' 'Elevation'
|
||||
Set position: Azimuth and Elevation as double precision floating point values.
|
||||
.TP
|
||||
.B p, get_pos
|
||||
Get position: azimuth and elevation.
|
||||
Get position: 'Azimuth' and 'Elevation' as double precision floating point
|
||||
values.
|
||||
.TP
|
||||
.B K, park
|
||||
Park the antenna.
|
||||
.TP
|
||||
.B M, move 'Direction' 'Speed'
|
||||
Move the rotator in a specific direction at the given rate.
|
||||
.sp
|
||||
Values are integers where Direction is defined as 2 = Up, 4 = Down, 8 = Left,
|
||||
and 16 = Right. Speed is an integer between 1 and 100. Not all backends that
|
||||
implement the move command use the Speed value. At this time only the gs232a
|
||||
utilizes the Speed parameter.
|
||||
.TP
|
||||
.B S, stop
|
||||
Stop the rotator.
|
||||
.B K, park
|
||||
Park the antenna.
|
||||
.TP
|
||||
.B R, reset
|
||||
.B C, set_conf 'Token' 'Value'
|
||||
Set Token to Value.
|
||||
.sp
|
||||
Backend dependent. Needs testing.
|
||||
.TP
|
||||
.B R, reset 'Reset'
|
||||
Reset the rotator.
|
||||
.TP
|
||||
.B M, move
|
||||
Move the rotator in a specific direction.
|
||||
.sp
|
||||
Integer value of '1' for Reset All.
|
||||
.TP
|
||||
.B _, get_info
|
||||
Get misc information about the rotator.
|
||||
.sp
|
||||
At the moment returns 'Model Name'.
|
||||
.TP
|
||||
.B w, send_cmd
|
||||
Send raw command string to rotator.
|
||||
.br
|
||||
For binary protocols enter values as \\0xAA\\0xBB
|
||||
|
||||
.B w, send_cmd 'Cmd'
|
||||
Send raw command string to rotator.
|
||||
.sp
|
||||
For binary protocols enter values as \\0xAA\\0xBB. Expect a 'Reply' from the
|
||||
rotator which will likely be a binary block or an ASCII string.
|
||||
.PP
|
||||
\fBLocator Commands\fP
|
||||
.PP
|
||||
These commands offer conversions of Degrees Minutes Seconds to other formats,
|
||||
Maidenhead square locator conversions and distance and azimuth conversions.
|
||||
.TP
|
||||
.B L, lonlat2loc 'Longitude' 'Latitude' 'Loc Len [2-12]'
|
||||
Returns the Maidenhead locator for the given 'Longitude' and 'Latitude'.
|
||||
.sp
|
||||
Both are floating point values. The precision of the returned square is
|
||||
controlled by 'Loc Len' which should be an even numbered integer value between
|
||||
2 and 12.
|
||||
.sp
|
||||
For example, "+L -170.000000 -85.000000 12\\n" returns "Locator: AA55AA00AA00\\n".
|
||||
.TP
|
||||
.B l, loc2lonlat 'Locator'
|
||||
Returns 'Longitude' and 'Latitude' in decimal degrees at the approximate
|
||||
center of the requested grid square (despite the use of double precision
|
||||
variables internally, some rounding error occurs). West longitude is
|
||||
expressed as a negative value. South latitude is expressed as a negative
|
||||
value. Locator can be from 2 to 12 characters in length.
|
||||
.sp
|
||||
For example, "+l AA55AA00AA00\\n" returns "Longitude: -169.999983\\nLatitude:
|
||||
-84.999991\\n".
|
||||
.TP
|
||||
.B D, dms2dec 'Degrees' 'Minutes' 'Seconds' 'S/W'
|
||||
Returns 'Dec Degrees', a signed floating point value.
|
||||
.sp
|
||||
Degrees and Minutes are
|
||||
integer values and Seconds is a floating point value. S/W is a flag with '1'
|
||||
indicating South latitude or West longitude and '0' North or East (the flag is
|
||||
needed as computers don't recognize a signed zero even though only the Degrees
|
||||
value only is typically signed in DMS notation).
|
||||
.TP
|
||||
.B d, dec2dms 'Dec Degrees'
|
||||
Returns 'Degrees' 'Minutes' 'Seconds' 'S/W'.
|
||||
.sp
|
||||
Values are as in dms2dec above.
|
||||
.TP
|
||||
.B E, dmmm2dec 'Degrees' 'Dec Minutes' 'S/W'
|
||||
Returns 'Dec Degrees', a signed floating point value.
|
||||
.sp
|
||||
Degrees is an integer
|
||||
value and Minutes is a floating point value. S/W is a flag with '1'
|
||||
indicating South latitude or West longitude and '0' North or East (the flag is
|
||||
needed as computers don't recognize a signed zero even though only the Degrees
|
||||
value only is typically signed in DMS notation).
|
||||
.TP
|
||||
.B e, dec2dmmm 'Dec Deg'
|
||||
Returns 'Degrees' 'Minutes' 'S/W'.
|
||||
.sp
|
||||
Values are as in dmmm2dec above.
|
||||
.TP
|
||||
.B B, qrb 'Lon 1' 'Lat 1' 'Lon 2' 'Lat 2'
|
||||
Returns 'Distance' 'Azimuth' where Distance is in km and Azimuth is in degrees.
|
||||
.sp
|
||||
All Lon/Lat values are signed floating point numbers.
|
||||
.TP
|
||||
.B A, a_sp2a_lp 'Short Path Deg'
|
||||
Returns 'Long Path Deg' or -RIG_EINVAL upon input error..
|
||||
.sp
|
||||
Both are floating point values within the range 0.00 to 360.00.
|
||||
.TP
|
||||
.B a, d_sp2d_lp 'Short Path km'
|
||||
Returns 'Long Path km'.
|
||||
.sp
|
||||
Both are floating point values.
|
||||
.SH PROTOCOL
|
||||
\fBDefault Protocol\fP
|
||||
.PP
|
||||
The \fBrotctld\fP protocol is intentionally simple. Commands are entered on
|
||||
a single line with any needed values. In Perl, reliable results are obtained
|
||||
by terminating each command string with a newline character, '\\n'.
|
||||
.PP
|
||||
.sp
|
||||
Example \fIset\fP (Perl code):
|
||||
|
||||
.sp
|
||||
print $socket "P 135 10\\n";
|
||||
.br
|
||||
.sp
|
||||
print $socket "\\\\set_pos 135 10\\n"; # escape leading '\\'
|
||||
.PP
|
||||
Responses from \fBrotctld\fP are text values and match the same tokens used
|
||||
in the \fIset\fP commands. Each value is returned on its own line. To
|
||||
signal the end of a response the "END\\n" string is sent when the '-e' option
|
||||
is passed.
|
||||
A one line response will be sent to \fIset\fP commands, "RPTR \fIx\fP\\n"
|
||||
where \fIx\fP is the Hamlib error code with '0' indicating success of the
|
||||
command.
|
||||
.PP
|
||||
Responses from \fBrotctld\fP \fIget\fP commands are text values and match the
|
||||
same tokens used in the \fIset\fP commands. Each value is returned on its own
|
||||
line. On error the string "RPTR \fIx\fP\\n" where \fIx\fP is the Hamlib error
|
||||
code.
|
||||
.sp
|
||||
Example \fIget\fP (Perl code):
|
||||
|
||||
.sp
|
||||
print $socket "p\\n";
|
||||
|
||||
.sp
|
||||
"135"
|
||||
.br
|
||||
"10"
|
||||
.br
|
||||
"END"
|
||||
.PP
|
||||
Most \fIget\fP functions return one to three values.
|
||||
Future work will focus on making this output compatible with assignment to a
|
||||
hash, dictionary, or other key:value variable.
|
||||
Most \fIget\fP functions return one to three values. A notable exception is
|
||||
the \fI\\dump_caps\fP function which returns many lines of key:value pairs.
|
||||
.PP
|
||||
This protocol is primarily used by the \fINET rotctl\fP (rotctl model 2) backend
|
||||
which allows applications already written for Hamlib to take advantage of
|
||||
\fBrotctld\fP without the need of rewriting application code. An application
|
||||
user can select rotor model 2 ("NET rotctl") and then set rot_pathname to
|
||||
"localhost:4533" or other network host:port.
|
||||
.PP
|
||||
\fBExtended Response Protocol\fP
|
||||
.PP
|
||||
An \fIEXPERIMENTAL\fP Extended Response protocol has been introduced into
|
||||
\fBrotctld\fP as of February 10, 2010. This protocol adds several rules to the
|
||||
strings returned by \fBrotctld\fP.
|
||||
.PP
|
||||
1. The command received by \fBrotctld\fP is echoed with its long command name
|
||||
followed by the value(s) (if any) received from the client terminated by the
|
||||
specified response separator as the first record of the block.
|
||||
.PP
|
||||
2. The last record of each block is the string "RPTR \fIx\fP\\n" where \fIx\fP is
|
||||
the numeric return value of the Hamlib backend function that was called by the
|
||||
command.
|
||||
.PP
|
||||
3. Any records consisting of data values returned by the rotor backend are
|
||||
prepended by a string immediately followed by a colon then a space and then the
|
||||
value terminated by the response separator. e.g. "Azimuth: 90.000000\\n" when the
|
||||
command was prepended by '+'.
|
||||
.PP
|
||||
4. All commands received will be acknowledged by \fBrotctld\fP with records from
|
||||
rules 1 and 2. Records from rule 3 are only returned when data values must be
|
||||
returned to the client.
|
||||
.PP
|
||||
An example response to a \fI+P\fP command (note the prepended '+'):
|
||||
.br
|
||||
set_pos: 90 45
|
||||
.br
|
||||
RPRT 0
|
||||
.PP
|
||||
In this case the long command name and values are returned on the first line and
|
||||
the second line contains the end of block marker and the numeric rig backend
|
||||
return value indicating success.
|
||||
.PP
|
||||
An example response to a \fI+\\get_pos\fP query:
|
||||
.br
|
||||
get_pos:
|
||||
.br
|
||||
Azimuth: 90.000000
|
||||
.br
|
||||
Elevation: 45.000000
|
||||
.br
|
||||
RPRT 0
|
||||
.PP
|
||||
In this case, as no value is passed to \fBrotctld\fP, the first line consists
|
||||
only of the long command name. The final line shows that the command was
|
||||
processed successfully by the rotor backend.
|
||||
.PP
|
||||
Invoking the Extended Response protocol requires prepending a command with a
|
||||
punctuation character. As shown in the examples above, prepending a '+'
|
||||
character to the command results in the responses being separated by a newline
|
||||
character ('\\n'). Any other punctuation character recognized by the C
|
||||
\fIispunct()\fP function except '\\', '?', or '_' will cause that character to
|
||||
become the response separator and the entire response will be on one line.
|
||||
.PP
|
||||
For example, invoking a \fI;\\get_pos\fP query with a leading ';' returns:
|
||||
.sp
|
||||
get_pos:;Azimuth: 90.000000;Elevation: 45.000000;RPRT 0
|
||||
.sp
|
||||
Or, using the pipe character '|' returns:
|
||||
.sp
|
||||
get_pos:|Azimuth: 90.000000|Elevation: 45.000000|RPRT 0
|
||||
.sp
|
||||
And a \\set_pos command prepended with a '|' returns:
|
||||
.sp
|
||||
set_pos: 135 22.5|RPRT 0
|
||||
.PP
|
||||
Such a format will allow reading a response as a single event using a prefered
|
||||
response separator. Other punctuation characters have not been tested!
|
||||
.PP
|
||||
All commands with the exception of \fI\\set_conf\fP have been tested with the
|
||||
Extended Response protocol and the included \fBtestrotctld.pl\fP script.
|
||||
.PP
|
||||
.SH EXAMPLES
|
||||
Start \fBrotctld\fP for a Ham IV rotor with the RotorEZ installed using a
|
||||
USB-to-serial adapter and backgrounding:
|
||||
.sp
|
||||
$ rotctld -m 401 -r /dev/ttyUSB1 &
|
||||
.sp
|
||||
Connect to the already running \fBrotctld\fP, and set position to
|
||||
135.0 degrees azimuth and 30.0 degrees elevation with a 1 second read timeout:
|
||||
.sp
|
||||
$ echo "\\set_pos 135.0 30.0" | nc -w 1 localhost 4533
|
||||
.sp
|
||||
Connect to a running \fBrotctld\fP with \fBrotctl\fP on the local host:
|
||||
.sp
|
||||
$ rotctl -m2
|
||||
.SH DIAGNOSTICS
|
||||
The \fB-v\fP, \fB--version\fP option allows different levels of diagnostics
|
||||
to be output to \fBstderr\fP and correspond to -v for BUG, -vv for ERR,
|
||||
-vvv for WARN, -vvvv for VERBOSE, or -vvvvv for TRACE.
|
||||
to be output to \fBstderr\fP and correspond to -v for BUG, -vv for ERR,
|
||||
-vvv for WARN, -vvvv for VERBOSE, or -vvvvv for TRACE.
|
||||
.PP
|
||||
A given verbose level is useful for providing needed debugging information to
|
||||
the email address below. For example, TRACE output shows all of the values
|
||||
sent to and received from the rotator which is very useful for rotator backend
|
||||
library development and may be requested by the developers.
|
||||
library development and may be requested by the developers. See the
|
||||
\fBREADME.betatester\fP and \fBREADME.developer\fP files for more information.
|
||||
.SH SECURITY
|
||||
No authentication whatsoever; DO NOT leave this TCP port open wide to the Internet.
|
||||
Please ask if stronger security is needed.
|
||||
No authentication whatsoever; DO NOT leave this TCP port open wide to the
|
||||
Internet. Please ask if stronger security is needed or consider using an
|
||||
SSH tunnel.
|
||||
.SH BUGS
|
||||
The daemon is not detaching and backgrounding itself.
|
||||
|
||||
.PP
|
||||
Much testing needs to be done.
|
||||
.SH REPORTING BUGS
|
||||
Report bugs to <hamlib-developer@lists.sourceforge.net>.
|
||||
.br
|
||||
.PP
|
||||
We are already aware of the bugs in the previous section :-)
|
||||
.SH AUTHORS
|
||||
Written by Stephane Fillod and the Hamlib Group
|
||||
Written by Stephane Fillod, Nate Bargmann, and the Hamlib Group
|
||||
.br
|
||||
<http://www.hamlib.org>.
|
||||
.SH COPYRIGHT
|
||||
Copyright \(co 2000-2009 Stephane Fillod and the Hamlib Group.
|
||||
Copyright \(co 2000-2009 Stephane Fillod
|
||||
.br
|
||||
Copyright \(co 2010 Nate Bargmann
|
||||
.br
|
||||
Copyright \(co 2000-2009 the Hamlib Group.
|
||||
.PP
|
||||
This is free software; see the source for copying conditions.
|
||||
There is NO warranty; not even for MERCHANTABILITY
|
||||
|
|
193
tests/rotctld.c
193
tests/rotctld.c
|
@ -4,23 +4,23 @@
|
|||
* This program test/control a rotator using Hamlib.
|
||||
* It takes commands from network connection.
|
||||
*
|
||||
* $Id: rotctld.c,v 1.7 2009-01-04 14:49:17 fillods Exp $
|
||||
* $Id: rotctld.c,v 1.7 2009-01-04 14:49:17 fillods Exp $
|
||||
*
|
||||
*
|
||||
* 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 2
|
||||
* 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, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
|
@ -71,26 +71,27 @@ void * handle_socket(void * arg);
|
|||
void usage();
|
||||
|
||||
/*
|
||||
* Reminder: when adding long options,
|
||||
* Reminder: when adding long options,
|
||||
* keep up to date SHORT_OPTIONS, usage()'s output and man page. thanks.
|
||||
* NB: do NOT use -W since it's reserved by POSIX.
|
||||
* TODO: add an option to read from a file
|
||||
*/
|
||||
#define SHORT_OPTIONS "m:r:s:C:t:T:LevhVl"
|
||||
#define SHORT_OPTIONS "m:r:s:C:t:T:LuevhVl"
|
||||
static struct option long_options[] =
|
||||
{
|
||||
{"model", 1, 0, 'm'},
|
||||
{"rot-file", 1, 0, 'r'},
|
||||
{"serial-speed", 1, 0, 's'},
|
||||
{"port", 1, 0, 't'},
|
||||
{"listen-addr", 1, 0, 'T'},
|
||||
{"list", 0, 0, 'l'},
|
||||
{"set-conf", 1, 0, 'C'},
|
||||
{"show-conf",0, 0, 'L'},
|
||||
{"end-marker", 0, 0, 'e'},
|
||||
{"verbose", 0, 0, 'v'},
|
||||
{"help", 0, 0, 'h'},
|
||||
{"version", 0, 0, 'V'},
|
||||
{"model", 1, 0, 'm'},
|
||||
{"rot-file", 1, 0, 'r'},
|
||||
{"serial-speed",1, 0, 's'},
|
||||
{"port", 1, 0, 't'},
|
||||
{"listen-addr", 1, 0, 'T'},
|
||||
{"list", 0, 0, 'l'},
|
||||
{"set-conf", 1, 0, 'C'},
|
||||
{"show-conf", 0, 0, 'L'},
|
||||
{"dump-caps", 0, 0, 'u'},
|
||||
{"end-marker", 0, 0, 'e'},
|
||||
{"verbose", 0, 0, 'v'},
|
||||
{"help", 0, 0, 'h'},
|
||||
{"version", 0, 0, 'V'},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
|
||||
|
@ -107,7 +108,7 @@ char send_cmd_term = '\r'; /* send_cmd termination char */
|
|||
|
||||
|
||||
int main (int argc, char *argv[])
|
||||
{
|
||||
{
|
||||
ROT *my_rot; /* handle to rot (instance) */
|
||||
rot_model_t my_model = ROT_MODEL_DUMMY;
|
||||
|
||||
|
@ -115,6 +116,7 @@ int main (int argc, char *argv[])
|
|||
|
||||
int verbose = 0;
|
||||
int show_conf = 0;
|
||||
int dump_caps_opt = 0;
|
||||
const char *rot_file=NULL;
|
||||
int serial_rate = 0;
|
||||
char conf_parms[MAXCONFLEN] = "";
|
||||
|
@ -135,79 +137,81 @@ int main (int argc, char *argv[])
|
|||
|
||||
switch(c) {
|
||||
case 'h':
|
||||
usage();
|
||||
exit(0);
|
||||
usage();
|
||||
exit(0);
|
||||
case 'V':
|
||||
version();
|
||||
exit(0);
|
||||
version();
|
||||
exit(0);
|
||||
case 'm':
|
||||
if (!optarg) {
|
||||
usage(); /* wrong arg count */
|
||||
exit(1);
|
||||
}
|
||||
my_model = atoi(optarg);
|
||||
break;
|
||||
case 'r':
|
||||
if (!optarg) {
|
||||
usage(); /* wrong arg count */
|
||||
exit(1);
|
||||
}
|
||||
rot_file = optarg;
|
||||
break;
|
||||
case 's':
|
||||
if (!optarg) {
|
||||
usage(); /* wrong arg count */
|
||||
exit(1);
|
||||
}
|
||||
serial_rate = atoi(optarg);
|
||||
break;
|
||||
case 'C':
|
||||
if (!optarg) {
|
||||
usage(); /* wrong arg count */
|
||||
exit(1);
|
||||
}
|
||||
if (*conf_parms != '\0')
|
||||
strcat(conf_parms, ",");
|
||||
strncat(conf_parms, optarg, MAXCONFLEN-strlen(conf_parms));
|
||||
break;
|
||||
case 't':
|
||||
if (!optarg) {
|
||||
usage(); /* wrong arg count */
|
||||
exit(1);
|
||||
}
|
||||
portno = atoi(optarg);
|
||||
break;
|
||||
case 'T':
|
||||
if (!optarg) {
|
||||
usage(); /* wrong arg count */
|
||||
exit(1);
|
||||
}
|
||||
if (4 != sscanf(optarg, "%d.%d.%d.%d", &a0,&a1,&a2,&a3)) {
|
||||
usage(); /* wrong arg count */
|
||||
exit(1);
|
||||
}
|
||||
src_addr = (a0<<24)|(a1<<16)|(a2<<8)|a3;
|
||||
break;
|
||||
case 'v':
|
||||
verbose++;
|
||||
break;
|
||||
case 'L':
|
||||
show_conf++;
|
||||
break;
|
||||
case 'l':
|
||||
list_models();
|
||||
exit(0);
|
||||
case 'e':
|
||||
opt_end = 1;
|
||||
break;
|
||||
default:
|
||||
usage(); /* unknown option? */
|
||||
if (!optarg) {
|
||||
usage(); /* wrong arg count */
|
||||
exit(1);
|
||||
}
|
||||
my_model = atoi(optarg);
|
||||
break;
|
||||
case 'r':
|
||||
if (!optarg) {
|
||||
usage(); /* wrong arg count */
|
||||
exit(1);
|
||||
}
|
||||
rot_file = optarg;
|
||||
break;
|
||||
case 's':
|
||||
if (!optarg) {
|
||||
usage(); /* wrong arg count */
|
||||
exit(1);
|
||||
}
|
||||
serial_rate = atoi(optarg);
|
||||
break;
|
||||
case 'C':
|
||||
if (!optarg) {
|
||||
usage(); /* wrong arg count */
|
||||
exit(1);
|
||||
}
|
||||
if (*conf_parms != '\0')
|
||||
strcat(conf_parms, ",");
|
||||
strncat(conf_parms, optarg, MAXCONFLEN-strlen(conf_parms));
|
||||
break;
|
||||
case 't':
|
||||
if (!optarg) {
|
||||
usage(); /* wrong arg count */
|
||||
exit(1);
|
||||
}
|
||||
portno = atoi(optarg);
|
||||
break;
|
||||
case 'T':
|
||||
if (!optarg) {
|
||||
usage(); /* wrong arg count */
|
||||
exit(1);
|
||||
}
|
||||
if (4 != sscanf(optarg, "%d.%d.%d.%d", &a0,&a1,&a2,&a3)) {
|
||||
usage(); /* wrong arg count */
|
||||
exit(1);
|
||||
}
|
||||
src_addr = (a0<<24)|(a1<<16)|(a2<<8)|a3;
|
||||
break;
|
||||
case 'v':
|
||||
verbose++;
|
||||
break;
|
||||
case 'L':
|
||||
show_conf++;
|
||||
break;
|
||||
case 'l':
|
||||
list_models();
|
||||
exit(0);
|
||||
case 'u':
|
||||
dump_caps_opt++;
|
||||
break;
|
||||
case 'e':
|
||||
opt_end = 1;
|
||||
fprintf(stderr, "-e|--end-marker option is deprecated!\nPlease consider using the Extended Response protocol instead.\n");
|
||||
break;
|
||||
default:
|
||||
usage(); /* unknown option? */
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
rig_set_debug(verbose<2 ? RIG_DEBUG_WARN: verbose);
|
||||
|
||||
rig_debug(RIG_DEBUG_VERBOSE, "rotctld, %s\n", hamlib_version);
|
||||
rig_debug(RIG_DEBUG_VERBOSE, "Report bugs to "
|
||||
"<hamlib-developer@lists.sourceforge.net>\n\n");
|
||||
|
@ -215,7 +219,7 @@ int main (int argc, char *argv[])
|
|||
my_rot = rot_init(my_model);
|
||||
|
||||
if (!my_rot) {
|
||||
fprintf(stderr, "Unknown rot num %d, or initialization error.\n",
|
||||
fprintf(stderr, "Unknown rot num %d, or initialization error.\n",
|
||||
my_model);
|
||||
fprintf(stderr, "Please check with --list option.\n");
|
||||
exit(2);
|
||||
|
@ -241,6 +245,16 @@ int main (int argc, char *argv[])
|
|||
rot_token_foreach(my_rot, print_conf_list, (rig_ptr_t)my_rot);
|
||||
}
|
||||
|
||||
/*
|
||||
* print out conf parameters, and exits immediately
|
||||
* We may be interested only in only caps, and rig_open may fail.
|
||||
*/
|
||||
if (dump_caps_opt) {
|
||||
dumpcaps_rot(my_rot, stdout);
|
||||
rig_cleanup(my_rot); /* if you care about memory */
|
||||
exit(0);
|
||||
}
|
||||
|
||||
retcode = rot_open(my_rot);
|
||||
if (retcode != RIG_OK) {
|
||||
fprintf(stderr,"rot_open: error = %s \n", rigerror(retcode));
|
||||
|
@ -257,7 +271,7 @@ int main (int argc, char *argv[])
|
|||
/*
|
||||
* Prepare listening socket
|
||||
*/
|
||||
sock_listen = socket(AF_INET, SOCK_STREAM, 0);
|
||||
sock_listen = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (sock_listen < 0) {
|
||||
perror("ERROR opening socket");
|
||||
exit(1);
|
||||
|
@ -403,7 +417,8 @@ void usage()
|
|||
" -C, --set-conf=PARM=VAL set config parameters\n"
|
||||
" -L, --show-conf list all config parameters\n"
|
||||
" -l, --list list all model numbers and exit\n"
|
||||
" -e, --end-marker use END marker in rotctld protocol\n"
|
||||
" -u, --dump-caps dump capabilities and exit\n"
|
||||
" -e, --end-marker use END marker in rotctld protocol (obsolete)\n"
|
||||
" -v, --verbose set verbose mode, cumulative\n"
|
||||
" -h, --help display this help and exit\n"
|
||||
" -V, --version output version information and exit\n\n",
|
||||
|
|
|
@ -0,0 +1,678 @@
|
|||
#! /usr/bin/perl
|
||||
|
||||
# testrotctld.pl - (C) 2008,2010 Nate Bargmann, n0nb@arrl.net
|
||||
# A Perl test script for the rotctld program.
|
||||
#
|
||||
# $Id$
|
||||
#
|
||||
# It connects to the rotctld TCP port (default 4533) and queries the daemon
|
||||
# for some common rot information and sets some values. It also aims to
|
||||
# provide a bit of example code for Perl scripting.
|
||||
#
|
||||
# This program utilizes the Extended Response protocol of rotctld in line
|
||||
# response mode. See the rotctld(8) man page for details.
|
||||
|
||||
#############################################################################
|
||||
# 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 2
|
||||
# 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, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
#
|
||||
# See the file 'COPYING' in the main Hamlib distribution directory for the
|
||||
# complete text of the GNU Public License version 2.
|
||||
#
|
||||
#############################################################################
|
||||
|
||||
|
||||
# Perl modules this script uses
|
||||
use warnings;
|
||||
use strict;
|
||||
use IO::Socket;
|
||||
use Getopt::Long;
|
||||
use Pod::Usage;
|
||||
|
||||
# Global variables
|
||||
my $socket;
|
||||
my $host = 'localhost';
|
||||
my $port = 4533;
|
||||
my $vfo = '';
|
||||
my %rot_state = (); # State of the rotor--position, etc.
|
||||
my %rot_caps = (); # Rotor capabilities from \dump_caps
|
||||
|
||||
my $man = 0;
|
||||
my $help = 0;
|
||||
my $debug = 0;
|
||||
my $user_in;
|
||||
my $ret_val;
|
||||
|
||||
# Error values returned from rotctld by Hamlib name
|
||||
my %errstr = (
|
||||
RIG_OK => "0", # No error, operation completed sucessfully
|
||||
RIG_EINVAL => "-1", # invalid parameter
|
||||
RIG_ECONF => "-2", # invalid configuration (serial,..)
|
||||
RIG_ENOMEM => "-3", # memory shortage
|
||||
RIG_ENIMPL => "-4", # function not implemented, but will be
|
||||
RIG_ETIMEOUT => "-5", # communication timed out
|
||||
RIG_EIO => "-6", # IO error, including open failed
|
||||
RIG_EINTERNAL => "-7", # Internal Hamlib error, huh?!
|
||||
RIG_EPROTO => "-8", # Protocol error
|
||||
RIG_ERJCTED => "-9", # Command rejected by the rot
|
||||
RIG_ETRUNC => "-10", # Command performed, but arg truncated
|
||||
RIG_ENAVAIL => "-11", # function not available
|
||||
RIG_ENTARGET => "-12", # VFO not targetable
|
||||
RIG_BUSERROR => "-13", # Error talking on the bus
|
||||
RIG_BUSBUSY => "-14", # Collision on the bus
|
||||
RIG_EARG => "-15", # NULL RIG handle or any invalid pointer parameter in get arg
|
||||
RIG_EVFO => "-16", # Invalid VFO
|
||||
RIG_EDOM => "-17", # Argument out of domain of func
|
||||
# testctld specific error values from -100 onward
|
||||
CTLD_OK => "-100", # testrotctld -- No error
|
||||
CTLD_ENIMPL => "-103", # testrotctld -- %rot_caps reports backend function not implemented
|
||||
CTLD_EPROTO => "-108", # testrotctld -- Echoed command mismatch or other error
|
||||
);
|
||||
|
||||
# Error values returned from rotctld by Hamlib value
|
||||
my %errval = reverse %errstr;
|
||||
|
||||
|
||||
# Rotor '\move' command token values
|
||||
my %direct = (
|
||||
UP => '2',
|
||||
DOWN => '4',
|
||||
LEFT => '8',
|
||||
CCW => '8', # Synonym for LEFT
|
||||
RIGHT => '16',
|
||||
CW => '16', # Synonym for RIGHT
|
||||
);
|
||||
|
||||
|
||||
#############################################################################
|
||||
# Main program
|
||||
#
|
||||
#############################################################################
|
||||
|
||||
# Parse command line options
|
||||
argv_opts();
|
||||
|
||||
# Create the new socket.
|
||||
# 'localhost' may be replaced by any hostname or IP address where a
|
||||
# rotctld instance is running.
|
||||
# Timeout is set to 5 seconds.
|
||||
$socket = new IO::Socket::INET (PeerAddr => $host,
|
||||
PeerPort => $port,
|
||||
Proto => 'tcp',
|
||||
Type => SOCK_STREAM,
|
||||
Timeout => 5 )
|
||||
or die $@;
|
||||
|
||||
|
||||
print "Welcome to testrotctld.pl a program to test `rotctld'\n";
|
||||
print "Type '?' or 'help' for commands help.\n\n";
|
||||
|
||||
|
||||
# Populate %rot_caps from \dump_caps
|
||||
$ret_val = dump_caps();
|
||||
|
||||
# Tell user what rotor rotctld is working with
|
||||
if ($ret_val eq $errstr{'RIG_OK'}) {
|
||||
print "Hamlib Model: " . $rot_caps{'Caps dump for model'} . "\t";
|
||||
print "Common Name: " . $rot_caps{'Mfg name'} . ' ' . $rot_caps{'Model name'} . "\n\n\n";
|
||||
} else {
|
||||
errmsg ($ret_val);
|
||||
}
|
||||
|
||||
|
||||
# Interactive loop
|
||||
do {
|
||||
|
||||
print "rotctld command: ";
|
||||
chomp($user_in = <>);
|
||||
|
||||
# P, \set_pos
|
||||
if ($user_in =~ /^(P|\\set_pos)\s+([-+]?([0-9]*\.)?[0-9]+)\s+([-+]?([0-9]*\.)?[0-9]+)\b$/) {
|
||||
if ($rot_caps{'Can set Position'} eq 'Y') {
|
||||
# Get the entered az and el values
|
||||
print "Az = $2, El = $4\n" if $debug;
|
||||
$ret_val = rot_cmd('set_pos', $2, $4);
|
||||
|
||||
unless ($ret_val eq $errstr{'RIG_OK'}) {
|
||||
errmsg ($ret_val);
|
||||
}
|
||||
} else {
|
||||
errmsg($errstr{'CTLD_ENIMPL'});
|
||||
}
|
||||
}
|
||||
|
||||
# p, \get_pos
|
||||
elsif ($user_in =~ /^(p|\\get_pos)\b$/) {
|
||||
if ($rot_caps{'Can get Position'} eq 'Y') {
|
||||
# Query rot and process result
|
||||
$ret_val = rot_cmd('get_pos');
|
||||
|
||||
if ($ret_val eq $errstr{'RIG_OK'}) {
|
||||
print "Azimuth: " . $rot_state{Azimuth} . "\n";
|
||||
print "Elevation: " . $rot_state{Elevation} . "\n\n";
|
||||
} else {
|
||||
errmsg ($ret_val);
|
||||
}
|
||||
} else {
|
||||
errmsg($errstr{'CTLD_ENIMPL'});
|
||||
}
|
||||
}
|
||||
|
||||
# M, \move
|
||||
elsif ($user_in =~ /^(M|\\move)\s+([A-Z]+)\s+(\d+)\b$/) {
|
||||
if ($rot_caps{'Can Move'} eq 'Y') {
|
||||
# Get the entered mode and passband values
|
||||
print "Move = $direct{$2}, Speed = $3\n" if $debug;
|
||||
$ret_val = rot_cmd('move', $direct{$2}, $3);
|
||||
|
||||
unless ($ret_val eq $errstr{'RIG_OK'}) {
|
||||
errmsg ($ret_val);
|
||||
}
|
||||
} else {
|
||||
errmsg($errstr{'CTLD_ENIMPL'});
|
||||
}
|
||||
}
|
||||
|
||||
# S, \stop
|
||||
elsif ($user_in =~ /^(S|\\stop)\b$/) {
|
||||
if ($rot_caps{'Can Stop'} eq 'Y') {
|
||||
print "Stop\n" if $debug;
|
||||
$ret_val = rot_cmd('stop'); # $vfo not used!
|
||||
|
||||
unless ($ret_val eq $errstr{'RIG_OK'}) {
|
||||
errmsg ($ret_val);
|
||||
}
|
||||
} else {
|
||||
errmsg($errstr{'CTLD_ENIMPL'});
|
||||
}
|
||||
}
|
||||
|
||||
# K, \park
|
||||
elsif ($user_in =~ /^(K|\\park)\b$/) {
|
||||
if ($rot_caps{'Can Park'} eq 'Y') {
|
||||
print "Park\n" if $debug;
|
||||
$ret_val = rot_cmd('park');
|
||||
|
||||
unless ($ret_val eq $errstr{'RIG_OK'}) {
|
||||
errmsg ($ret_val);
|
||||
}
|
||||
} else {
|
||||
errmsg($errstr{'CTLD_ENIMPL'});
|
||||
}
|
||||
}
|
||||
|
||||
# R, \reset
|
||||
elsif ($user_in =~ /^(R|\\reset)\s+(\d)\b$/) {
|
||||
if ($rot_caps{'Can Reset'} eq 'Y') {
|
||||
print "Reset\n" if $debug;
|
||||
$ret_val = rot_cmd('reset', $2);
|
||||
|
||||
unless ($ret_val eq $errstr{'RIG_OK'}) {
|
||||
errmsg ($ret_val);
|
||||
}
|
||||
} else {
|
||||
errmsg($errstr{'CTLD_ENIMPL'});
|
||||
}
|
||||
}
|
||||
|
||||
# _, \get_info
|
||||
elsif ($user_in =~ /^(_|\\get_info)\b$/) {
|
||||
if ($rot_caps{'Can get Info'} eq 'Y') {
|
||||
print "Get info\n" if $debug;
|
||||
$ret_val = rot_cmd('get_info');
|
||||
|
||||
if ($ret_val eq $errstr{'RIG_OK'}) {
|
||||
print "Info: " . $rot_state{Info} . "\n\n";
|
||||
} else {
|
||||
errmsg ($ret_val);
|
||||
}
|
||||
} else {
|
||||
errmsg($errstr{'CTLD_ENIMPL'});
|
||||
}
|
||||
}
|
||||
|
||||
# L, \lonlat2loc
|
||||
elsif ($user_in =~ /^(L|\\lonlat2loc)\s+([-+]?([0-9]*\.)?[0-9]+)\s+([-+]?([0-9]*\.)?[0-9]+)\s+(\d+)\b$/) {
|
||||
print "Longitude = $2, Latitude = $4, Length = $6\n" if $debug;
|
||||
$ret_val = rot_cmd('lonlat2loc', $2, $4, $6);
|
||||
|
||||
if ($ret_val eq $errstr{'RIG_OK'}) {
|
||||
print "Locator: " . $rot_state{Locator} . "\n\n";
|
||||
} else {
|
||||
errmsg ($ret_val);
|
||||
}
|
||||
}
|
||||
|
||||
# l, \loc2lonlat
|
||||
elsif ($user_in =~ /^(l|\\loc2lonlat)\s+([A-Za-z0-9]+)\b$/) {
|
||||
print "Locator = $2\n" if $debug;
|
||||
$ret_val = rot_cmd('loc2lonlat', $2);
|
||||
|
||||
if ($ret_val eq $errstr{'RIG_OK'}) {
|
||||
print "Longitude: " . $rot_state{Longitude} . "\n";
|
||||
print "Latitude: " . $rot_state{Latitude} . "\n\n";
|
||||
} else {
|
||||
errmsg ($ret_val);
|
||||
}
|
||||
}
|
||||
|
||||
# D, \dms2dec
|
||||
elsif ($user_in =~ /^(D|\\dms2dec)\s+[+-]?(\d+)\s+(\d+)\s+(([0-9]*\.)?[0-9]+)\s+(\d)\b$/) {
|
||||
print "Degrees = $2, Minutes = $3, Seconds = $4, S/W = $6\n" if $debug;
|
||||
$ret_val = rot_cmd('dms2dec', $2, $3, $4, $6);
|
||||
|
||||
if ($ret_val eq $errstr{'RIG_OK'}) {
|
||||
print "Decimal Degrees: " . $rot_state{'Dec Degrees'} . "\n\n";
|
||||
} else {
|
||||
errmsg ($ret_val);
|
||||
}
|
||||
}
|
||||
|
||||
# d, \dec2dms
|
||||
elsif ($user_in =~ /^(d|\\dec2dms)\s+([-+]?([0-9]*\.)?[0-9]+)\b$/) {
|
||||
print "Decimal Degrees = $2\n" if $debug;
|
||||
$ret_val = rot_cmd('dec2dms', $2);
|
||||
|
||||
if ($ret_val eq $errstr{'RIG_OK'}) {
|
||||
print "Degrees: " . $rot_state{Degrees} . "\n";
|
||||
print "Minutes: " . $rot_state{Minutes} . "\n";
|
||||
print "Seconds: " . $rot_state{Seconds} . "\n";
|
||||
print "South/West: " . $rot_state{'S/W'} . "\n\n";
|
||||
} else {
|
||||
errmsg ($ret_val);
|
||||
}
|
||||
}
|
||||
|
||||
# E, \dmmm2dec
|
||||
elsif ($user_in =~ /^(E|\\dmmm2dec)\s+[+-]?(\d+)\s+(([0-9]*\.)?[0-9]+)\s+(\d)\b$/) {
|
||||
print "Degrees = $2, Minutes = $3, S/W = $5\n" if $debug;
|
||||
$ret_val = rot_cmd('dmmm2dec', $2, $3, $5);
|
||||
|
||||
if ($ret_val eq $errstr{'RIG_OK'}) {
|
||||
print "Decimal Degrees: " . $rot_state{'Dec Deg'} . "\n\n";
|
||||
} else {
|
||||
errmsg ($ret_val);
|
||||
}
|
||||
}
|
||||
|
||||
# e, \dec2dmmm
|
||||
elsif ($user_in =~ /^(e|\\dec2dmmm)\s+([-+]?([0-9]*\.)?[0-9]+)\b$/) {
|
||||
print "Decimal Degrees = $2\n" if $debug;
|
||||
$ret_val = rot_cmd('dec2dmmm', $2);
|
||||
|
||||
if ($ret_val eq $errstr{'RIG_OK'}) {
|
||||
print "Degrees: " . $rot_state{Degrees} . "\n";
|
||||
print "Decimal Minutes: " . $rot_state{'Dec Minutes'} . "\n";
|
||||
print "South/West: " . $rot_state{'S/W'} . "\n\n";
|
||||
} else {
|
||||
errmsg ($ret_val);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# B, \qrb
|
||||
elsif ($user_in =~ /^(B|\\qrb)\s+([-+]?([0-9]*\.)?[0-9]+)\s+([-+]?([0-9]*\.)?[0-9]+)\s+([-+]?([0-9]*\.)?[0-9]+)\s+([-+]?([0-9]*\.)?[0-9]+)\b$/) {
|
||||
print "Lon 1 = $2, Lat 1 = $4, Lon 2 = $6, Lat 2 = $8\n" if $debug;
|
||||
$ret_val = rot_cmd('qrb', $2, $4, $6, $8);
|
||||
|
||||
if ($ret_val eq $errstr{'RIG_OK'}) {
|
||||
print "Distance (km): " . $rot_state{'QRB Distance'} . "\n";
|
||||
print "Azimuth (Deg): " . $rot_state{'QRB Azimuth'} . "\n\n";
|
||||
} else {
|
||||
errmsg ($ret_val);
|
||||
}
|
||||
}
|
||||
|
||||
# A, \a_sp2a_lp
|
||||
elsif ($user_in =~ /^(A|\\a_sp2a_lp)\s+([-+]?([0-9]*\.)?[0-9]+)\b$/) {
|
||||
print "Short Path Degrees = $2\n" if $debug;
|
||||
$ret_val = rot_cmd('a_sp2a_lp', $2);
|
||||
|
||||
if ($ret_val eq $errstr{'RIG_OK'}) {
|
||||
print "Long Path Degrees: " . $rot_state{'Long Path Deg'} . "\n\n";
|
||||
} else {
|
||||
errmsg ($ret_val);
|
||||
}
|
||||
}
|
||||
|
||||
# a, \d_sp2d_lp
|
||||
elsif ($user_in =~ /^(a|\\d_sp2d_lp)\s+([-+]?([0-9]*\.)?[0-9]+)\b$/) {
|
||||
print "Short Path km = $2\n" if $debug;
|
||||
$ret_val = rot_cmd('d_sp2d_lp', $2);
|
||||
|
||||
if ($ret_val eq $errstr{'RIG_OK'}) {
|
||||
print "Long Path km: " . $rot_state{'Long Path km'} . "\n\n";
|
||||
} else {
|
||||
errmsg ($ret_val);
|
||||
}
|
||||
}
|
||||
|
||||
# 1, \dump_caps
|
||||
elsif ($user_in =~ /^(1|\\dump_caps)\b$/) {
|
||||
$ret_val = dump_caps();
|
||||
|
||||
if ($ret_val eq $errstr{'RIG_OK'}) {
|
||||
print "Model: " . $rot_caps{'Caps dump for model'} . "\n";
|
||||
print "Manufacturer: " . $rot_caps{'Mfg name'} . "\n";
|
||||
print "Name: " . $rot_caps{'Model name'} . "\n\n";
|
||||
} else {
|
||||
errmsg ($ret_val);
|
||||
}
|
||||
}
|
||||
|
||||
# ?, help
|
||||
elsif ($user_in =~ /^\?|^help\b$/) {
|
||||
print <<EOF;
|
||||
|
||||
Commands are entered in the same format as described in the rotctld(8)
|
||||
man page. e.g. generally lower case letters call \\get commands and upper
|
||||
case letters call \\set commands or long command names may be used. An
|
||||
exception are the locator commands where paired conversions are arbitrarily
|
||||
assigned upper and lower case commands.
|
||||
|
||||
Values passed to set commands are separated by a single space:
|
||||
|
||||
P 150.75 22.5
|
||||
|
||||
\\get_pos
|
||||
|
||||
See `man rotctld' for complete command descriptions.
|
||||
|
||||
Type 'q' or 'exit' to exit $0.
|
||||
|
||||
EOF
|
||||
|
||||
}
|
||||
|
||||
elsif ($user_in !~ /^(exit|q)\b$/i) {
|
||||
print "Unrecognized command. Type '?' or 'help' for command help.\n"
|
||||
}
|
||||
|
||||
} while ($user_in !~ /^(exit|q)\b$/i);
|
||||
|
||||
|
||||
# Close the connection before we exit.
|
||||
close($socket);
|
||||
|
||||
|
||||
#############################################################################
|
||||
# Subroutines for interacting with rotctld
|
||||
#
|
||||
#############################################################################
|
||||
|
||||
|
||||
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# rot_cmd -- Build command string, check returned data, and populate %rot_state
|
||||
#
|
||||
# Passed parameters:
|
||||
# $cmd = rotctld command
|
||||
# $p1 - $p4 = \set command parameters
|
||||
#
|
||||
# Returns error value from rotctld or local error value if echoed command mismatch
|
||||
#
|
||||
sub rot_cmd {
|
||||
my ($errno, @answer);
|
||||
my $cmd = shift @_;
|
||||
my $p1 = shift @_;
|
||||
my $p2 = shift @_;
|
||||
my $p3 = shift @_;
|
||||
my $p4 = shift @_;
|
||||
|
||||
# Add a space to the beginning of the $p? arguments
|
||||
if (defined $p1) {
|
||||
# "Stringify" parameter value then add a space to the beginning of the string
|
||||
$p1 .= '';
|
||||
$p1 = sprintf("%*s", 1 + length $p1, $p1);
|
||||
} else { $p1 = ''; }
|
||||
|
||||
if (defined $p2) {
|
||||
$p2 .= '';
|
||||
$p2 = sprintf("%*s", 1 + length $p2, $p2);
|
||||
} else { $p2 = ''; }
|
||||
|
||||
if (defined $p3) {
|
||||
$p3 .= '';
|
||||
$p3 = sprintf("%*s", 1 + length $p3, $p3);
|
||||
} else { $p3 = ''; }
|
||||
|
||||
if (defined $p4) {
|
||||
$p4 .= '';
|
||||
$p4 = sprintf("%*s", 1 + length $p4, $p4);
|
||||
} else { $p4 = ''; }
|
||||
|
||||
print 'Command: +\\' . $cmd . $p1 . $p2 . $p3 . $p4 . "\n\n" if $debug;
|
||||
|
||||
# N.B. Terminate query commands with a newline, e.g. "\n" character.
|
||||
# N.B. Preceding '+' char to request block or extended response protocol
|
||||
print $socket '+\\' . $cmd . $p1 . $p2 . $p3 . $p4 . "\n";
|
||||
|
||||
# rotctld echoes the command plus value(s) on "get" along with
|
||||
# separate lines for the queried value(s) and the Hamlib return value.
|
||||
@answer = get_rotctl($socket);
|
||||
|
||||
if ((shift @answer) =~ /^$cmd:/) {
|
||||
$errno = get_errno(pop @answer);
|
||||
|
||||
if ($errno eq $errstr{'RIG_OK'}) {
|
||||
# At this point the first line of @answer which is the command string echo
|
||||
# and the last line which is the ending block marker and the Hamlib rot
|
||||
# function return value have been removed from the array. What is left
|
||||
# over will be stored in the %state hash as a key: value pair for each
|
||||
# returned line.
|
||||
|
||||
if (@answer) { get_state(@answer) } # Empty array on \set commands
|
||||
}
|
||||
return $errno;
|
||||
|
||||
} else {
|
||||
return $errstr{'CTLD_EPROTO'};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# Get the rot capabilities from Hamlib and store in the %rot_caps hash.
|
||||
sub dump_caps {
|
||||
my ($cmd, $errno, @answer);
|
||||
|
||||
print $socket "+\\dump_caps\n";
|
||||
|
||||
@answer = get_rotctl($socket);
|
||||
$cmd = shift @answer;
|
||||
|
||||
if ($cmd =~ /^dump_caps:/) {
|
||||
$errno = get_errno(pop @answer);
|
||||
|
||||
if ($errno eq $errstr{'RIG_OK'}) {
|
||||
get_caps(@answer);
|
||||
}
|
||||
return $errno;
|
||||
} else {
|
||||
return $errstr{'RIG_EPROTO'};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#############################################################################
|
||||
# testrotctld.pl helper functions
|
||||
#
|
||||
#############################################################################
|
||||
|
||||
# Thanks to Uri Guttman on comp.lang.perl.misc for this function.
|
||||
# 'RPRT' is the token returned by rotctld to mark the end of the reply block.
|
||||
sub get_rotctl {
|
||||
my $sock = shift @_;
|
||||
my @lines;
|
||||
|
||||
while (<$sock>) {
|
||||
# rotctld terminates each line with '\n'
|
||||
chomp;
|
||||
push @lines, $_;
|
||||
return @lines if $_ =~ /^RPRT/;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# Builds the %rot_state hash from the lines returned by rotctld which are of the
|
||||
# form "Azimuth: 90.000000, elevation: 45.000000", etc.
|
||||
sub get_state {
|
||||
my ($key, $val);
|
||||
|
||||
foreach (@_) {
|
||||
($key, $val) = split(/: /, $_);
|
||||
$rot_state{$key} = $val;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# Parse the (large) \dump_caps command response into %rot_caps.
|
||||
# TODO: process all lines of output
|
||||
sub get_caps {
|
||||
my ($key, $val);
|
||||
|
||||
foreach (@_) {
|
||||
if (($_ =~ /^Caps .*:/) or
|
||||
($_ =~ /^Model .*:/) or
|
||||
($_ =~ /^Mfg .*:/) or
|
||||
($_ =~ /^Can .*:/)
|
||||
) {
|
||||
($key, $val) = split(/:\s+/, $_);
|
||||
$rot_caps{$key} = $val;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# Extract the Hamlib error value returned with the last line from rotctld
|
||||
sub get_errno {
|
||||
|
||||
chomp @_;
|
||||
my @errno = split(/ /, $_[0]);
|
||||
|
||||
return $errno[1];
|
||||
}
|
||||
|
||||
|
||||
# FIXME: Better argument handling
|
||||
sub errmsg {
|
||||
|
||||
unless (($_[0] eq $errstr{'CTLD_EPROTO'}) or ($_[0] eq $errstr{'CTLD_ENIMPL'})) {
|
||||
print "rotctld returned Hamlib $errval{$_[0]}\n\n";
|
||||
}
|
||||
elsif ($_[0] eq $errstr{'CTLD_EPROTO'}) {
|
||||
print "Echoed command mismatch\n\n";
|
||||
}
|
||||
elsif ($_[0] eq $errstr{'CTLD_ENIMPL'}) {
|
||||
print "Function not yet implemented in Hamlib rot backend\n\n";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# Parse the command line for supported options. Print help text as needed.
|
||||
sub argv_opts {
|
||||
|
||||
# Parse options and print usage if there is a syntax error,
|
||||
# or if usage was explicitly requested.
|
||||
GetOptions('help|?' => \$help,
|
||||
man => \$man,
|
||||
"port=i" => \$port,
|
||||
"host=s" => \$host,
|
||||
debug => \$debug
|
||||
) or pod2usage(2);
|
||||
pod2usage(1) if $help;
|
||||
pod2usage(-verbose => 2) if $man;
|
||||
|
||||
}
|
||||
|
||||
|
||||
# POD for pod2usage
|
||||
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
testctld.pl - A test and example program for `rotctld' written in Perl.
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
testctld.pl [options]
|
||||
|
||||
Options:
|
||||
--host Hostname or IP address of target `rotctld' process
|
||||
--port TCP Port of target `rotctld' process
|
||||
--help Brief help message
|
||||
--man Full documentation
|
||||
--debug Enable debugging output
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
B<testcld.pl> provides a set of functions to interactively test the Hamlib
|
||||
I<rotctld> TCP/IP network daemon. It also aims to be an example of programming
|
||||
code to control a rotor via TCP/IP in Hamlib.
|
||||
|
||||
=head1 OPTIONS
|
||||
|
||||
=over 8
|
||||
|
||||
=item B<--host>
|
||||
|
||||
Hostname or IP address of the target I<rotctld> process. Default is I<localhost>
|
||||
which should resolve to 127.0.0.1 if I</etc/hosts> is configured correctly.
|
||||
|
||||
=item B<--port>
|
||||
|
||||
TCP port of the target I<rotctld> process. Default is 4533. Mutliple instances
|
||||
of I<rotctld> will require unique port numbers.
|
||||
|
||||
=item B<--help>
|
||||
|
||||
Prints a brief help message and exits.
|
||||
|
||||
=item B<--man>
|
||||
|
||||
Prints this manual page and exits.
|
||||
|
||||
=item B<--debug>
|
||||
|
||||
Enables debugging output to the console.
|
||||
|
||||
=back
|
||||
|
||||
=head1 COMMANDS
|
||||
|
||||
Commands are the same as described in the rotctld(8) man page. This is only
|
||||
a brief summary.
|
||||
|
||||
F, \set_freq Set frequency in Hz
|
||||
f, \get_freq Get frequency in Hz
|
||||
M, \set_mode Set mode including passband in Hz
|
||||
m, \get_mode Get mode including passband in Hz
|
||||
V, \set_vfo Set VFO (VFOA, VFOB, etc.)
|
||||
v, \get_vfo Get VFO (VFOA, VFOB, etc.)
|
||||
J, \set_rit Set RIT in +/-Hz, '0' to clear
|
||||
j, \get_rit Get RIT in +/-Hz, '0' indicates Off
|
||||
Z, \set_xit Set XIT in +/-Hz, '0' to clear
|
||||
z, \get_rit Get XIT in +/-Hz, '0' indicates Off
|
||||
T, \set_ptt Set PTT, '1' On, '0' Off
|
||||
t, \get_ptt Get PTT, '1' indicates On, '0' indicates Off
|
||||
S, \set_split_vfo Set rot into "split" VFO mode, '1' On, '0' Off
|
||||
s, \get_split_vfo Get status of :split" VFO mode, '1' On, '0' Off
|
||||
I, \set_split_freq Set TX VFO frequency in Hz
|
||||
i, \get_split_freq Get TX VFO frequency in Hz
|
||||
X, \set_split_mode Set TX VFO mode including passband in Hz
|
||||
x, \get_split_mode Get TX VFO mode including passband in Hz
|
||||
2, \power2mW Translate a power value [0.0..1.0] to milliWatts
|
||||
4, \mW2power Translate milliWatts to a power value [0.0..1.0]
|
||||
1, \dump_caps Get the rot capabilities and display select values.
|
||||
|
||||
=cut
|
Ładowanie…
Reference in New Issue