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
|
@ -12,8 +12,8 @@ check_PROGRAMS = dumpmem testrig testtrn testbcd testfreq listrigs \
|
||||||
|
|
||||||
rigctl_SOURCES = rigctl.c rigctl_parse.c dumpcaps.c sprintflst.c
|
rigctl_SOURCES = rigctl.c rigctl_parse.c dumpcaps.c sprintflst.c
|
||||||
rigctld_SOURCES = rigctld.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
|
rotctl_SOURCES = rotctl.c rotctl_parse.c dumpcaps_rot.c
|
||||||
rotctld_SOURCES = rotctld.c rotctl_parse.c
|
rotctld_SOURCES = rotctld.c rotctl_parse.c dumpcaps_rot.c
|
||||||
rigswr_SOURCES = rigswr.c
|
rigswr_SOURCES = rigswr.c
|
||||||
rigsmtr_SOURCES = rigsmtr.c
|
rigsmtr_SOURCES = rigsmtr.c
|
||||||
rigmem_SOURCES = rigmem.c memsave.c memload.c memcsv.c sprintflst.c
|
rigmem_SOURCES = rigmem.c memsave.c memload.c memcsv.c sprintflst.c
|
||||||
|
@ -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
|
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"
|
./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
|
||||||
|
|
||||||
|
|
|
@ -71,16 +71,19 @@ struct test_table {
|
||||||
unsigned char cmd;
|
unsigned char cmd;
|
||||||
const char *name;
|
const char *name;
|
||||||
int (*rot_routine)(ROT*, FILE*, int, const struct test_table*, const char*,
|
int (*rot_routine)(ROT*, FILE*, int, const struct test_table*, const char*,
|
||||||
const char*, const char*);
|
const char*, const char*, const char*, const char*, const char*);
|
||||||
int flags;
|
int flags;
|
||||||
const char *arg1;
|
const char *arg1;
|
||||||
const char *arg2;
|
const char *arg2;
|
||||||
const char *arg3;
|
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, \
|
#define declare_proto_rot(f) static int (f)(ROT *rot, FILE *fout, int interactive, \
|
||||||
const struct test_table *cmd, const char *arg1, \
|
const struct test_table *cmd, const char *arg1, const char *arg2, \
|
||||||
const char *arg2, const char *arg3)
|
const char *arg3, const char *arg4, const char *arg5, const char *arg6)
|
||||||
|
|
||||||
declare_proto_rot(set_position);
|
declare_proto_rot(set_position);
|
||||||
declare_proto_rot(get_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(inter_set_conf); /* interactive mode set_conf */
|
||||||
declare_proto_rot(send_cmd);
|
declare_proto_rot(send_cmd);
|
||||||
declare_proto_rot(dump_state);
|
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
|
* convention: upper case cmd is set, lowercase is get
|
||||||
|
@ -108,7 +122,17 @@ struct test_table test_list[] = {
|
||||||
{ 'C', "set_conf", inter_set_conf, ARG_IN, "Token", "Value" },
|
{ 'C', "set_conf", inter_set_conf, ARG_IN, "Token", "Value" },
|
||||||
{ '_', "get_info", get_info, ARG_OUT, "Info" },
|
{ '_', "get_info", get_info, ARG_OUT, "Info" },
|
||||||
{ 'w', "send_cmd", send_cmd, ARG_IN1|ARG_IN_LINE|ARG_OUT2, "Cmd", "Reply" },
|
{ 'w', "send_cmd", send_cmd, ARG_IN1|ARG_IN_LINE|ARG_OUT2, "Cmd", "Reply" },
|
||||||
{ 0x8f,"dump_state", dump_state, ARG_OUT },
|
{ '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 },
|
{ 0x00, "", NULL },
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -116,7 +140,7 @@ struct test_table test_list[] = {
|
||||||
struct test_table *find_cmd_entry(int cmd)
|
struct test_table *find_cmd_entry(int cmd)
|
||||||
{
|
{
|
||||||
int i;
|
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)
|
if (test_list[i].cmd == cmd)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -131,7 +155,7 @@ struct test_table *find_cmd_entry(int cmd)
|
||||||
char parse_arg(const char *arg)
|
char parse_arg(const char *arg)
|
||||||
{
|
{
|
||||||
int i;
|
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))
|
if (!strncmp(arg, test_list[i].name, MAXNAMSIZ))
|
||||||
return test_list[i].cmd;
|
return test_list[i].cmd;
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -161,6 +185,8 @@ extern int interactive;
|
||||||
extern int prompt;
|
extern int prompt;
|
||||||
extern int opt_end;
|
extern int opt_end;
|
||||||
extern char send_cmd_term;
|
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 rotctl_parse(ROT *my_rot, FILE *fin, FILE *fout, char *argv[], int argc)
|
||||||
{
|
{
|
||||||
|
@ -168,9 +194,11 @@ int rotctl_parse(ROT *my_rot, FILE *fin, FILE *fout, char *argv[], int argc)
|
||||||
unsigned char cmd;
|
unsigned char cmd;
|
||||||
struct test_table *cmd_entry;
|
struct test_table *cmd_entry;
|
||||||
|
|
||||||
char arg1[MAXARGSZ+1], *p1;
|
char arg1[MAXARGSZ + 1], *p1;
|
||||||
char arg2[MAXARGSZ+1], *p2;
|
char arg2[MAXARGSZ + 1], *p2;
|
||||||
char arg3[MAXARGSZ+1], *p3;
|
char arg3[MAXARGSZ + 1], *p3;
|
||||||
|
char arg4[MAXARGSZ + 1], *p4;
|
||||||
|
char *p5, *p6;
|
||||||
static int last_was_ret = 1;
|
static int last_was_ret = 1;
|
||||||
|
|
||||||
if (interactive) {
|
if (interactive) {
|
||||||
|
@ -181,6 +209,26 @@ int rotctl_parse(ROT *my_rot, FILE *fin, FILE *fout, char *argv[], int argc)
|
||||||
if (scanfc(fin, "%c", &cmd) < 0)
|
if (scanfc(fin, "%c", &cmd) < 0)
|
||||||
return -1;
|
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 */
|
/* command by name */
|
||||||
if (cmd == '\\') {
|
if (cmd == '\\') {
|
||||||
unsigned char cmd_name[MAXNAMSIZ], *pcmd = cmd_name;
|
unsigned char cmd_name[MAXNAMSIZ], *pcmd = cmd_name;
|
||||||
|
@ -202,7 +250,6 @@ int rotctl_parse(ROT *my_rot, FILE *fin, FILE *fout, char *argv[], int argc)
|
||||||
if (last_was_ret) {
|
if (last_was_ret) {
|
||||||
if (prompt) {
|
if (prompt) {
|
||||||
fprintf(fout, "? for help, q to quit.\n");
|
fprintf(fout, "? for help, q to quit.\n");
|
||||||
fprintf(fout, "\nRotator command: ");
|
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -243,7 +290,7 @@ int rotctl_parse(ROT *my_rot, FILE *fin, FILE *fout, char *argv[], int argc)
|
||||||
return 0;
|
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) {
|
(cmd_entry->flags & ARG_IN1) && cmd_entry->arg1) {
|
||||||
|
@ -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
|
* mutex locking needed because rigctld is multithreaded
|
||||||
* and hamlib is not MT-safe
|
* and hamlib is not MT-safe
|
||||||
|
@ -323,26 +387,58 @@ int rotctl_parse(ROT *my_rot, FILE *fin, FILE *fout, char *argv[], int argc)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (!prompt)
|
if (!prompt)
|
||||||
rig_debug(RIG_DEBUG_TRACE, "rotctl: %c '%s' '%s' '%s'\n",
|
rig_debug(RIG_DEBUG_TRACE, "rotctl(d): %c '%s' '%s' '%s' '%s'\n",
|
||||||
cmd, p1, p2, p3);
|
cmd, p1, p2, p3, p4);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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,
|
retcode = (*cmd_entry->rot_routine)(my_rot, fout, interactive,
|
||||||
cmd_entry, p1, p2, p3);
|
cmd_entry, p1, p2, p3, p4, p5, p6);
|
||||||
|
|
||||||
#ifdef HAVE_PTHREAD
|
#ifdef HAVE_PTHREAD
|
||||||
pthread_mutex_unlock(&rot_mutex);
|
pthread_mutex_unlock(&rot_mutex);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (retcode != RIG_OK) {
|
if (retcode != RIG_OK) {
|
||||||
if (interactive && !prompt)
|
/* only for rotctld */
|
||||||
fprintf(fout, NETROTCTL_RET "%d\n", retcode); /* only for rotctld */
|
if (interactive && !prompt) {
|
||||||
|
fprintf(fout, NETROTCTL_RET "%d\n", retcode);
|
||||||
|
ext_resp = 0;
|
||||||
|
resp_sep = '\n';
|
||||||
|
}
|
||||||
else
|
else
|
||||||
fprintf(fout, "%s: error = %s\n", cmd_entry->name, rigerror(retcode));
|
fprintf(fout, "%s: error = %s\n", cmd_entry->name, rigerror(retcode));
|
||||||
} else {
|
} else {
|
||||||
if (interactive && !prompt) { /* only for rotctld */
|
/* only for rotctld */
|
||||||
if (!(cmd_entry->flags & ARG_OUT) && !opt_end) /* netrotctl RIG_OK */
|
if (interactive && !prompt) {
|
||||||
|
/* netrotctl RIG_OK */
|
||||||
|
if (!(cmd_entry->flags & ARG_OUT)
|
||||||
|
&& !opt_end && !ext_resp && cmd != 0xf0)
|
||||||
fprintf(fout, NETROTCTL_RET "0\n");
|
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");
|
fprintf(fout, "END\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -360,26 +456,31 @@ void version()
|
||||||
printf("%s\n", hamlib_copyright);
|
printf("%s\n", hamlib_copyright);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void usage_rot(FILE *fout)
|
void usage_rot(FILE *fout)
|
||||||
{
|
{
|
||||||
int i;
|
int i, nbspaces;
|
||||||
|
|
||||||
fprintf(fout, "Commands (may not be available for this rotator):\n");
|
fprintf(fout, "Commands (some may not be available for this rig):\n");
|
||||||
for (i=0; test_list[i].cmd != 0; i++) {
|
for (i = 0; test_list[i].cmd != 0; i++) {
|
||||||
fprintf(fout, "%c: %-16s(", test_list[i].cmd, test_list[i].name);
|
fprintf(fout, "%c: %-12s(", isprint(test_list[i].cmd) ?
|
||||||
if (test_list[i].arg1)
|
test_list[i].cmd : '?', test_list[i].name);
|
||||||
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");
|
|
||||||
|
|
||||||
if (i%2)
|
nbspaces = 16;
|
||||||
fprintf(fout, "\n");
|
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)
|
int print_conf_list(const struct confparams *cfp, rig_ptr_t data)
|
||||||
{
|
{
|
||||||
ROT *rot = (ROT*) data;
|
ROT *rot = (ROT*) data;
|
||||||
|
@ -400,7 +501,7 @@ int print_conf_list(const struct confparams *cfp, rig_ptr_t data)
|
||||||
if (!cfp->u.c.combostr)
|
if (!cfp->u.c.combostr)
|
||||||
break;
|
break;
|
||||||
printf("\tCombo: %s", cfp->u.c.combostr[0]);
|
printf("\tCombo: %s", cfp->u.c.combostr[0]);
|
||||||
for (i=1 ; i<RIG_COMBO_MAX && cfp->u.c.combostr[i]; i++)
|
for (i = 1 ; i < RIG_COMBO_MAX && cfp->u.c.combostr[i]; i++)
|
||||||
printf(", %s", cfp->u.c.combostr[i]);
|
printf(", %s", cfp->u.c.combostr[i]);
|
||||||
printf("\n");
|
printf("\n");
|
||||||
break;
|
break;
|
||||||
|
@ -408,12 +509,12 @@ int print_conf_list(const struct confparams *cfp, rig_ptr_t data)
|
||||||
break;
|
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)
|
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));
|
caps->model_name, caps->version, rig_strstatus(caps->status));
|
||||||
return 1; /* !=0, we want them all ! */
|
return 1; /* !=0, we want them all ! */
|
||||||
}
|
}
|
||||||
|
@ -424,7 +525,7 @@ void list_models()
|
||||||
|
|
||||||
rot_load_all_backends();
|
rot_load_all_backends();
|
||||||
|
|
||||||
printf("Rot#\tMfg Model Vers.\n");
|
printf("Rot# Mfg Model Vers.\n");
|
||||||
status = rot_list_foreach(print_model_list, NULL);
|
status = rot_list_foreach(print_model_list, NULL);
|
||||||
if (status != RIG_OK ) {
|
if (status != RIG_OK ) {
|
||||||
printf("rot_list_foreach: error = %s \n", rigerror(status));
|
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)
|
* 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)
|
declare_proto_rot(set_position)
|
||||||
{
|
{
|
||||||
azimuth_t az;
|
azimuth_t az;
|
||||||
|
@ -469,6 +571,7 @@ declare_proto_rot(set_position)
|
||||||
return rot_set_position(rot, az, el);
|
return rot_set_position(rot, az, el);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 'p' */
|
||||||
declare_proto_rot(get_position)
|
declare_proto_rot(get_position)
|
||||||
{
|
{
|
||||||
int status;
|
int status;
|
||||||
|
@ -478,27 +581,29 @@ declare_proto_rot(get_position)
|
||||||
status = rot_get_position(rot, &az, &el);
|
status = rot_get_position(rot, &az, &el);
|
||||||
if (status != RIG_OK)
|
if (status != RIG_OK)
|
||||||
return status;
|
return status;
|
||||||
if (interactive && prompt)
|
if ((interactive && prompt) || (interactive && !prompt && ext_resp))
|
||||||
fprintf(fout, "%s: ", cmd->arg1);
|
fprintf(fout, "%s: ", cmd->arg1);
|
||||||
fprintf(fout, "%f\n", az);
|
fprintf(fout, "%f%c", az, resp_sep);
|
||||||
if (interactive && prompt)
|
if ((interactive && prompt) || (interactive && !prompt && ext_resp))
|
||||||
fprintf(fout, "%s: ", cmd->arg2);
|
fprintf(fout, "%s: ", cmd->arg2);
|
||||||
fprintf(fout, "%f\n", el);
|
fprintf(fout, "%f%c", el, resp_sep);
|
||||||
|
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 'S' */
|
||||||
declare_proto_rot(stop)
|
declare_proto_rot(stop)
|
||||||
{
|
{
|
||||||
return rot_stop(rot);
|
return rot_stop(rot);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 'K' */
|
||||||
declare_proto_rot(park)
|
declare_proto_rot(park)
|
||||||
{
|
{
|
||||||
return rot_park(rot);
|
return rot_park(rot);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 'R' */
|
||||||
declare_proto_rot(reset)
|
declare_proto_rot(reset)
|
||||||
{
|
{
|
||||||
rot_reset_t reset;
|
rot_reset_t reset;
|
||||||
|
@ -507,18 +612,20 @@ declare_proto_rot(reset)
|
||||||
return rot_reset(rot, reset);
|
return rot_reset(rot, reset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* '_' */
|
||||||
declare_proto_rot(get_info)
|
declare_proto_rot(get_info)
|
||||||
{
|
{
|
||||||
const char *s;
|
const char *s;
|
||||||
|
|
||||||
s = rot_get_info(rot);
|
s = rot_get_info(rot);
|
||||||
if (interactive && prompt)
|
if ((interactive && prompt) || (interactive && !prompt && ext_resp))
|
||||||
fprintf(fout, "%s: ", cmd->arg1);
|
fprintf(fout, "%s: ", cmd->arg1);
|
||||||
fprintf(fout, "%s\n", s ? s : "None");
|
fprintf(fout, "%s%c", s ? s : "None", resp_sep);
|
||||||
|
|
||||||
return RIG_OK;
|
return RIG_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 'M' */
|
||||||
declare_proto_rot(move)
|
declare_proto_rot(move)
|
||||||
{
|
{
|
||||||
int direction;
|
int direction;
|
||||||
|
@ -529,6 +636,7 @@ declare_proto_rot(move)
|
||||||
return rot_move(rot, direction, speed);
|
return rot_move(rot, direction, speed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 'C' */
|
||||||
declare_proto_rot(inter_set_conf)
|
declare_proto_rot(inter_set_conf)
|
||||||
{
|
{
|
||||||
token_t token;
|
token_t token;
|
||||||
|
@ -539,7 +647,19 @@ declare_proto_rot(inter_set_conf)
|
||||||
return rot_set_conf(rot, token, val);
|
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)
|
declare_proto_rot(dump_state)
|
||||||
{
|
{
|
||||||
struct rot_state *rs = &rot->state;
|
struct rot_state *rs = &rot->state;
|
||||||
|
@ -548,13 +668,29 @@ declare_proto_rot(dump_state)
|
||||||
* - Protocol version
|
* - Protocol version
|
||||||
*/
|
*/
|
||||||
#define ROTCTLD_PROT_VER 0
|
#define ROTCTLD_PROT_VER 0
|
||||||
fprintf(fout, "%d\n", ROTCTLD_PROT_VER);
|
if ((interactive && prompt) || (interactive && !prompt && ext_resp))
|
||||||
fprintf(fout, "%d\n", rot->caps->rot_model);
|
fprintf(fout, "rotctld Protocol Ver: ");
|
||||||
|
fprintf(fout, "%d%c", ROTCTLD_PROT_VER, resp_sep);
|
||||||
|
|
||||||
fprintf(fout, "%lf\n", rs->min_az);
|
if ((interactive && prompt) || (interactive && !prompt && ext_resp))
|
||||||
fprintf(fout, "%lf\n", rs->max_az);
|
fprintf(fout, "Rotor Model: ");
|
||||||
fprintf(fout, "%lf\n", rs->min_el);
|
fprintf(fout, "%d%c", rot->caps->rot_model, resp_sep);
|
||||||
fprintf(fout, "%lf\n", rs->max_el);
|
|
||||||
|
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;
|
return RIG_OK;
|
||||||
}
|
}
|
||||||
|
@ -584,21 +720,21 @@ declare_proto_rot(send_cmd)
|
||||||
if (send_cmd_term == -1 || backend_num == -1) {
|
if (send_cmd_term == -1 || backend_num == -1) {
|
||||||
const char *p = arg1, *pp = NULL;
|
const char *p = arg1, *pp = NULL;
|
||||||
int i;
|
int i;
|
||||||
for (i=0; i < BUFSZ-1 && p != pp; i++) {
|
for (i = 0; i < BUFSZ - 1 && p != pp; i++) {
|
||||||
pp = p+1;
|
pp = p + 1;
|
||||||
bufcmd[i] = strtol(p+1, (char **) &p, 0);
|
bufcmd[i] = strtol(p + 1, (char **) &p, 0);
|
||||||
}
|
}
|
||||||
/* must save length to allow 0x00 to be sent as part of a command
|
/* 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 */
|
/* no End Of Message chars */
|
||||||
eom_buf[0] = '\0';
|
eom_buf[0] = '\0';
|
||||||
} else {
|
} else {
|
||||||
/* text protocol */
|
/* text protocol */
|
||||||
|
|
||||||
strncpy(bufcmd,arg1,BUFSZ);
|
strncpy(bufcmd,arg1, BUFSZ);
|
||||||
bufcmd[BUFSZ-2] = '\0';
|
bufcmd[BUFSZ - 2] = '\0';
|
||||||
|
|
||||||
cmd_len = strlen(bufcmd);
|
cmd_len = strlen(bufcmd);
|
||||||
|
|
||||||
|
@ -643,3 +779,207 @@ declare_proto_rot(send_cmd)
|
||||||
return retval;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,6 +30,13 @@
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <hamlib/rotator.h>
|
#include <hamlib/rotator.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* external prototype
|
||||||
|
*/
|
||||||
|
|
||||||
|
int dumpcaps_rot (ROT *, FILE *);
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Prototypes
|
* Prototypes
|
||||||
*/
|
*/
|
||||||
|
|
331
tests/rotctld.8
331
tests/rotctld.8
|
@ -2,7 +2,7 @@
|
||||||
.\" First parameter, NAME, should be all caps
|
.\" First parameter, NAME, should be all caps
|
||||||
.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection
|
.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection
|
||||||
.\" other parameters are allowed: see man(7), man(1)
|
.\" 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.
|
.\" Please adjust this date whenever revising the manpage.
|
||||||
.\"
|
.\"
|
||||||
.\" Some roff macros, for reference:
|
.\" Some roff macros, for reference:
|
||||||
|
@ -16,16 +16,18 @@
|
||||||
.\" .sp <n> insert n+1 empty lines
|
.\" .sp <n> insert n+1 empty lines
|
||||||
.\" for manpage-specific macros, see man(7)
|
.\" for manpage-specific macros, see man(7)
|
||||||
.SH NAME
|
.SH NAME
|
||||||
rotctld \- Hamlib rotator control daemon
|
rotctld \- Hamlib TCP rotator control daemon
|
||||||
.SH SYNOPSIS
|
.SH SYNOPSIS
|
||||||
.B rotctld
|
.B rotctld
|
||||||
[\fIOPTION\fR]...
|
[\fIOPTION\fR]...
|
||||||
.SH DESCRIPTION
|
.SH DESCRIPTION
|
||||||
The \fBrotctld\fP program is an EXPERIMENTAL \fBHamlib\fP rotator daemon that
|
The \fBrotctld\fP program is an NEW \fBHamlib\fP rotator control daemon ready for
|
||||||
handles TCP client requests. This allows multiple user programs to share one
|
testing that handles client requests via TCP sockets. This allows multiple user
|
||||||
rotator. Multiple rotators can be controlled on different TCP ports. The syntax
|
programs to share one rotator (this needs testing). Multiple rotators can be
|
||||||
of the commands are the same as \fBrotctl\fP. It is hoped that \fBrotctld\fP
|
controlled on different TCP ports by use of multiple \fBrotctld\fP processes. The
|
||||||
will be especially useful for languages such as Perl, Python, and others.
|
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
|
.PP
|
||||||
.\" TeX users may be more comfortable with the \fB<whatever>\fP and
|
.\" 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,
|
||||||
|
@ -37,7 +39,15 @@ 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.
|
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
|
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.
|
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
|
||||||
|
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
|
.PP
|
||||||
Keep in mind that \fBHamlib\fP is BETA level software.
|
Keep in mind that \fBHamlib\fP is BETA level software.
|
||||||
While a lot of backend libraries lack complete rotator support, the basic functions
|
While a lot of backend libraries lack complete rotator support, the basic functions
|
||||||
|
@ -50,7 +60,7 @@ REPORTING BUGS section. Patches and code enhancements are also welcome.
|
||||||
.SH OPTIONS
|
.SH OPTIONS
|
||||||
This program follows the usual GNU command line syntax, with long
|
This program follows the usual GNU command line syntax, with long
|
||||||
options starting with two dashes ('-').
|
options starting with two dashes ('-').
|
||||||
|
.PP
|
||||||
Here is a summary of the supported options:
|
Here is a summary of the supported options:
|
||||||
.TP
|
.TP
|
||||||
.B \-m, --model=id
|
.B \-m, --model=id
|
||||||
|
@ -58,13 +68,19 @@ Select rotator model number. See -l, "list" option below.
|
||||||
.TP
|
.TP
|
||||||
.B \-r, --rot-file=device
|
.B \-r, --rot-file=device
|
||||||
Use \fIdevice\fP as the file name of the port the rotator is connected.
|
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
|
Often a serial port, but could be a USB to serial adapter or USB port device.
|
||||||
/dev/ttyS0, /dev/ttyS1, /dev/ttyUSB0, etc.
|
Typically /dev/ttyS0, /dev/ttyS1, /dev/ttyUSB0, etc.
|
||||||
.TP
|
.TP
|
||||||
.B \-s, --serial-speed=baud
|
.B \-s, --serial-speed=baud
|
||||||
Set serial speed to \fIbaud\fP rate. Uses maximum serial speed from rotor
|
Set serial speed to \fIbaud\fP rate. Uses maximum serial speed from rotor
|
||||||
backend capabilities (set by -m above) as the default.
|
backend capabilities (set by -m above) as the default.
|
||||||
.TP
|
.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
|
.B \-L, --show-conf
|
||||||
List all config parameters for the rotator defined with -m above.
|
List all config parameters for the rotator defined with -m above.
|
||||||
.TP
|
.TP
|
||||||
|
@ -73,18 +89,18 @@ Set config parameter. e.g. --set-conf=stop_bits=2
|
||||||
.br
|
.br
|
||||||
Use -L option for a list.
|
Use -L option for a list.
|
||||||
.TP
|
.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
|
.B \-l, --list
|
||||||
List all model numbers defined in \fBHamlib\fP and exit.
|
List all model numbers defined in \fBHamlib\fP and exit.
|
||||||
.TP
|
.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
|
.B \-v, --verbose
|
||||||
Set verbose mode, cumulative (see DIAGNOSTICS below).
|
Set verbose mode, cumulative (see DIAGNOSTICS below).
|
||||||
.TP
|
.TP
|
||||||
|
@ -103,75 +119,262 @@ or the rotator itself may not support some commands. In that case,
|
||||||
the operation will fail with a \fBHamlib\fP error code.
|
the operation will fail with a \fBHamlib\fP error code.
|
||||||
.SH COMMANDS
|
.SH COMMANDS
|
||||||
Commands can be sent over the TCP socket either as a single char, or as a
|
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
|
long command name plus the value(s) space separated on one '\\n' terminated
|
||||||
PROTOCOL.
|
line. See \fIPROTOCOL\fP.
|
||||||
.PP
|
.PP
|
||||||
Since most of the \fBHamlib\fP operations have a \fIset\fP and a \fIget\fP method,
|
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
|
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
|
.PP
|
||||||
Please note that the backend for the rotator to be controlled,
|
Example (Perl): `print $socket "\\\\dump_caps\\n";' to see what the rotor's
|
||||||
or the rotator itself may not support some commands. In that case,
|
backend can do (NOTE: In Perl and many other languages a '\\' will need to be
|
||||||
the operation will fail with a \fBHamlib\fP error message.
|
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
|
.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
|
.TP
|
||||||
.B P, set_pos
|
.B P, set_pos 'Azimuth' 'Elevation'
|
||||||
Set position: azimuth and elevation.
|
Set position: Azimuth and Elevation as double precision floating point values.
|
||||||
.TP
|
.TP
|
||||||
.B p, get_pos
|
.B p, get_pos
|
||||||
Get position: azimuth and elevation.
|
Get position: 'Azimuth' and 'Elevation' as double precision floating point
|
||||||
|
values.
|
||||||
.TP
|
.TP
|
||||||
.B K, park
|
.TP
|
||||||
Park the antenna.
|
.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
|
.TP
|
||||||
.B S, stop
|
.B S, stop
|
||||||
Stop the rotator.
|
Stop the rotator.
|
||||||
|
.B K, park
|
||||||
|
Park the antenna.
|
||||||
.TP
|
.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.
|
Reset the rotator.
|
||||||
.TP
|
.sp
|
||||||
.B M, move
|
Integer value of '1' for Reset All.
|
||||||
Move the rotator in a specific direction.
|
|
||||||
.TP
|
.TP
|
||||||
.B _, get_info
|
.B _, get_info
|
||||||
Get misc information about the rotator.
|
Get misc information about the rotator.
|
||||||
|
.sp
|
||||||
|
At the moment returns 'Model Name'.
|
||||||
.TP
|
.TP
|
||||||
.B w, send_cmd
|
.B w, send_cmd 'Cmd'
|
||||||
Send raw command string to rotator.
|
Send raw command string to rotator.
|
||||||
.br
|
.sp
|
||||||
For binary protocols enter values as \\0xAA\\0xBB
|
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
|
.SH PROTOCOL
|
||||||
|
\fBDefault Protocol\fP
|
||||||
|
.PP
|
||||||
The \fBrotctld\fP protocol is intentionally simple. Commands are entered on
|
The \fBrotctld\fP protocol is intentionally simple. Commands are entered on
|
||||||
a single line with any needed values. In Perl, reliable results are obtained
|
a single line with any needed values. In Perl, reliable results are obtained
|
||||||
by terminating each command string with a newline character, '\\n'.
|
by terminating each command string with a newline character, '\\n'.
|
||||||
.PP
|
.sp
|
||||||
Example \fIset\fP (Perl code):
|
Example \fIset\fP (Perl code):
|
||||||
|
.sp
|
||||||
print $socket "P 135 10\\n";
|
print $socket "P 135 10\\n";
|
||||||
.br
|
.sp
|
||||||
print $socket "\\\\set_pos 135 10\\n"; # escape leading '\\'
|
print $socket "\\\\set_pos 135 10\\n"; # escape leading '\\'
|
||||||
.PP
|
.PP
|
||||||
Responses from \fBrotctld\fP are text values and match the same tokens used
|
A one line response will be sent to \fIset\fP commands, "RPTR \fIx\fP\\n"
|
||||||
in the \fIset\fP commands. Each value is returned on its own line. To
|
where \fIx\fP is the Hamlib error code with '0' indicating success of the
|
||||||
signal the end of a response the "END\\n" string is sent when the '-e' option
|
command.
|
||||||
is passed.
|
|
||||||
.PP
|
.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):
|
Example \fIget\fP (Perl code):
|
||||||
|
.sp
|
||||||
print $socket "p\\n";
|
print $socket "p\\n";
|
||||||
|
.sp
|
||||||
"135"
|
"135"
|
||||||
.br
|
.br
|
||||||
"10"
|
"10"
|
||||||
.br
|
.br
|
||||||
"END"
|
|
||||||
.PP
|
.PP
|
||||||
Most \fIget\fP functions return one to three values.
|
Most \fIget\fP functions return one to three values. A notable exception is
|
||||||
Future work will focus on making this output compatible with assignment to a
|
the \fI\\dump_caps\fP function which returns many lines of key:value pairs.
|
||||||
hash, dictionary, or other key:value variable.
|
.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
|
.SH DIAGNOSTICS
|
||||||
The \fB-v\fP, \fB--version\fP option allows different levels of 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,
|
to be output to \fBstderr\fP and correspond to -v for BUG, -vv for ERR,
|
||||||
|
@ -180,24 +383,30 @@ to be output to \fBstderr\fP and correspond to -v for BUG, -vv for ERR,
|
||||||
A given verbose level is useful for providing needed debugging information to
|
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
|
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
|
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
|
.SH SECURITY
|
||||||
No authentication whatsoever; DO NOT leave this TCP port open wide to the Internet.
|
No authentication whatsoever; DO NOT leave this TCP port open wide to the
|
||||||
Please ask if stronger security is needed.
|
Internet. Please ask if stronger security is needed or consider using an
|
||||||
|
SSH tunnel.
|
||||||
.SH BUGS
|
.SH BUGS
|
||||||
The daemon is not detaching and backgrounding itself.
|
The daemon is not detaching and backgrounding itself.
|
||||||
|
.PP
|
||||||
Much testing needs to be done.
|
Much testing needs to be done.
|
||||||
.SH REPORTING BUGS
|
.SH REPORTING BUGS
|
||||||
Report bugs to <hamlib-developer@lists.sourceforge.net>.
|
Report bugs to <hamlib-developer@lists.sourceforge.net>.
|
||||||
.br
|
.PP
|
||||||
We are already aware of the bugs in the previous section :-)
|
We are already aware of the bugs in the previous section :-)
|
||||||
.SH AUTHORS
|
.SH AUTHORS
|
||||||
Written by Stephane Fillod and the Hamlib Group
|
Written by Stephane Fillod, Nate Bargmann, and the Hamlib Group
|
||||||
.br
|
.br
|
||||||
<http://www.hamlib.org>.
|
<http://www.hamlib.org>.
|
||||||
.SH COPYRIGHT
|
.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
|
.PP
|
||||||
This is free software; see the source for copying conditions.
|
This is free software; see the source for copying conditions.
|
||||||
There is NO warranty; not even for MERCHANTABILITY
|
There is NO warranty; not even for MERCHANTABILITY
|
||||||
|
|
|
@ -76,17 +76,18 @@ void usage();
|
||||||
* NB: do NOT use -W since it's reserved by POSIX.
|
* NB: do NOT use -W since it's reserved by POSIX.
|
||||||
* TODO: add an option to read from a file
|
* 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[] =
|
static struct option long_options[] =
|
||||||
{
|
{
|
||||||
{"model", 1, 0, 'm'},
|
{"model", 1, 0, 'm'},
|
||||||
{"rot-file", 1, 0, 'r'},
|
{"rot-file", 1, 0, 'r'},
|
||||||
{"serial-speed", 1, 0, 's'},
|
{"serial-speed",1, 0, 's'},
|
||||||
{"port", 1, 0, 't'},
|
{"port", 1, 0, 't'},
|
||||||
{"listen-addr", 1, 0, 'T'},
|
{"listen-addr", 1, 0, 'T'},
|
||||||
{"list", 0, 0, 'l'},
|
{"list", 0, 0, 'l'},
|
||||||
{"set-conf", 1, 0, 'C'},
|
{"set-conf", 1, 0, 'C'},
|
||||||
{"show-conf",0, 0, 'L'},
|
{"show-conf", 0, 0, 'L'},
|
||||||
|
{"dump-caps", 0, 0, 'u'},
|
||||||
{"end-marker", 0, 0, 'e'},
|
{"end-marker", 0, 0, 'e'},
|
||||||
{"verbose", 0, 0, 'v'},
|
{"verbose", 0, 0, 'v'},
|
||||||
{"help", 0, 0, 'h'},
|
{"help", 0, 0, 'h'},
|
||||||
|
@ -115,6 +116,7 @@ int main (int argc, char *argv[])
|
||||||
|
|
||||||
int verbose = 0;
|
int verbose = 0;
|
||||||
int show_conf = 0;
|
int show_conf = 0;
|
||||||
|
int dump_caps_opt = 0;
|
||||||
const char *rot_file=NULL;
|
const char *rot_file=NULL;
|
||||||
int serial_rate = 0;
|
int serial_rate = 0;
|
||||||
char conf_parms[MAXCONFLEN] = "";
|
char conf_parms[MAXCONFLEN] = "";
|
||||||
|
@ -197,8 +199,12 @@ int main (int argc, char *argv[])
|
||||||
case 'l':
|
case 'l':
|
||||||
list_models();
|
list_models();
|
||||||
exit(0);
|
exit(0);
|
||||||
|
case 'u':
|
||||||
|
dump_caps_opt++;
|
||||||
|
break;
|
||||||
case 'e':
|
case 'e':
|
||||||
opt_end = 1;
|
opt_end = 1;
|
||||||
|
fprintf(stderr, "-e|--end-marker option is deprecated!\nPlease consider using the Extended Response protocol instead.\n");
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
usage(); /* unknown option? */
|
usage(); /* unknown option? */
|
||||||
|
@ -206,8 +212,6 @@ int main (int argc, char *argv[])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rig_set_debug(verbose<2 ? RIG_DEBUG_WARN: verbose);
|
|
||||||
|
|
||||||
rig_debug(RIG_DEBUG_VERBOSE, "rotctld, %s\n", hamlib_version);
|
rig_debug(RIG_DEBUG_VERBOSE, "rotctld, %s\n", hamlib_version);
|
||||||
rig_debug(RIG_DEBUG_VERBOSE, "Report bugs to "
|
rig_debug(RIG_DEBUG_VERBOSE, "Report bugs to "
|
||||||
"<hamlib-developer@lists.sourceforge.net>\n\n");
|
"<hamlib-developer@lists.sourceforge.net>\n\n");
|
||||||
|
@ -241,6 +245,16 @@ int main (int argc, char *argv[])
|
||||||
rot_token_foreach(my_rot, print_conf_list, (rig_ptr_t)my_rot);
|
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);
|
retcode = rot_open(my_rot);
|
||||||
if (retcode != RIG_OK) {
|
if (retcode != RIG_OK) {
|
||||||
fprintf(stderr,"rot_open: error = %s \n", rigerror(retcode));
|
fprintf(stderr,"rot_open: error = %s \n", rigerror(retcode));
|
||||||
|
@ -403,7 +417,8 @@ void usage()
|
||||||
" -C, --set-conf=PARM=VAL set config parameters\n"
|
" -C, --set-conf=PARM=VAL set config parameters\n"
|
||||||
" -L, --show-conf list all config parameters\n"
|
" -L, --show-conf list all config parameters\n"
|
||||||
" -l, --list list all model numbers and exit\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"
|
" -v, --verbose set verbose mode, cumulative\n"
|
||||||
" -h, --help display this help and exit\n"
|
" -h, --help display this help and exit\n"
|
||||||
" -V, --version output version information and exit\n\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