From e66509d2c5dca7bd4ada38572d0466cfb2e786c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Fillod=2C=20F8CFE?= Date: Thu, 27 Dec 2001 21:46:25 +0000 Subject: [PATCH] Initial release git-svn-id: https://hamlib.svn.sourceforge.net/svnroot/hamlib/trunk@812 7ae35d74-ebe9-4afe-98af-79ac388436b8 --- src/locator.c | 268 +++++++++++++++++++++++++ src/rot_conf.c | 273 +++++++++++++++++++++++++ src/rot_conf.h | 48 +++++ src/rot_reg.c | 343 +++++++++++++++++++++++++++++++ src/rotator.c | 533 +++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 1465 insertions(+) create mode 100644 src/locator.c create mode 100644 src/rot_conf.c create mode 100644 src/rot_conf.h create mode 100644 src/rot_reg.c create mode 100644 src/rotator.c diff --git a/src/locator.c b/src/locator.c new file mode 100644 index 000000000..d14aac05c --- /dev/null +++ b/src/locator.c @@ -0,0 +1,268 @@ +/** + * \file src/locator.c + * \brief Ham Radio Control Libraries interface + * \author Stephane Fillod + * \date 2000-2001 + * + * Hamlib interface is a frontend implementing wrapper functions. + */ + +/* + * Hamlib Interface - locator and bearing conversion calls + * Copyright (c) 2001 by Stephane Fillod + * + * $Id: locator.c,v 1.1 2001-12-27 21:46:25 fillods Exp $ + * + * Code to determine bearing and range was taken from the Great Circle, + * by S. R. Sampson, N5OWK. + * Ref: "Air Navigation", Air Force Manual 51-40, 1 February 1987 + * Ref: "ARRL Satellite Experimenters Handbook", August 1990 + * + * Code to calculate distance and azimuth between two Maidenhead locators, + * taken from wwl, by IK0ZSN Mirko Caserta. + * + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library 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 Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include + + +#include + + +#define RADIAN (180.0 / M_PI) + +/* arc length for 1 degree, 60 Nautical Miles */ +#define ARC_IN_KM 111.2 + +/* + * degrees >360, minutes > 60, and seconds > 60 are allowed + */ +double dms2dec(int degrees, int minutes, int seconds) +{ + return (double)degrees + minutes/60.0 + seconds/3600.0; +} +/* + * guarantee: dec2dms will make sure 0<=degress<360, + * 0<=minutes<60, 0<=seconds<0 + */ +void dec2dms(double dec, int *degrees, int *minutes, int *seconds) +{ + if (!degrees || !minutes || !seconds) + return; + + dec = fmod(dec, 360); + *degrees = (int)floor(dec); + dec -= *degrees; + dec *= 60; + *minutes = (int)floor(dec); + dec -= *minutes; + dec *= 60; + *seconds = (int)floor(dec); +} + + +/* + * 4 characters and 6 characters are accepted + */ +int locator2longlat(double *longitude, double *latitude, const char *locator) +{ + char loc[6]; + + if (locator[4] != '\0' && locator[6] != '\0') + return -1; + + loc[0] = toupper(locator[0]); + loc[1] = toupper(locator[1]); + loc[2] = locator[2]; + loc[3] = locator[3]; + if (locator[4] != '\0') { + loc[4] = toupper(locator[4]); + loc[5] = toupper(locator[5]); + } else { + loc[4] = 'A'; + loc[5] = 'A'; + } + if (loc[0] < 'A' || loc[0] > 'Z' || + loc[1] < 'A' || loc[1] > 'Z' || + loc[2] < '0' || loc[2] > '9' || + loc[3] < '0' || loc[3] > '9' || + loc[4] < 'A' || loc[4] > 'Z' || + loc[5] < 'A' || loc[5] > 'Z' ) { + return -1; + } + + *longitude = 20.0 * (loc[0]-'A') - 180.0 + 2.0 * (loc[2]-'0') + + (loc[4]-'A')/12.0 + 1.0; + + *latitude = 10.0 * (loc[1]-'A') - 90.0 + (loc[3]-'0') + + (loc[5]-'A')/24.0 + 1.0/48.0; + + return 0; +} + +/* + * locator must be at least 6 chars long + */ +int longlat2locator(double longitude, double latitude, char *locator) +{ +#if 0 + double t,s; + + t = 20.0 * (loc[0]-'A') - 180.0 + 2.0 * (loc[2]-'0') + + (loc[4]-'A')/12.0 + 1.0; + *longitude = t; + + s = 10.0 * (loc[1]-'A') - 90.0 + (loc[3]-'0') + + (loc[5]-'A')/24.0 + 1.0/48.0; + *latitude = s; +#endif + strcpy (locator, "MM00mm"); + + return 0; +} + + +/* + * 1 towards 2 + * returns qrb in km + * and azimuth in decimal degrees + */ + +/* + * This version also takes into consideration the two points + * being close enough to be in the near-field, and the antipodal points, + * which are easily calculated. These last points were made + * in discussions with John Allison who makes the nice MAPIT program. + */ +int qrb(double lon1, double lat1, double lon2, double lat2, + double *bearing, double *azimuth) +{ + double delta_long, tmp, arc, cosaz, az; + + if (!bearing || !azimuth) + return -1; + + if ((lat1 > 90.0 || lat1 < -90.0) || (lat2 > 90.0 || lat2 < -90.0)) + return -1; + + if ((lon1 > 180.0 || lon1 < -180.0) || (lon2 > 180.0 || lon2 < -180.0)) + return -1; + + /* Prevent ACOS() Domain Error */ + + if (lat1 == 90.0) + lat1 = 89.99; + else if (lat1 == -90.0) + lat1 = -89.99; + + if (lat2 == 90.0) + lat2 = 89.99; + else if (lat2 == -90.0) + lat2 = -89.99; + + /* + * Convert variables to Radians + */ + lat1 /= RADIAN; + lon1 /= RADIAN; + lat2 /= RADIAN; + lon2 /= RADIAN; + + delta_long = lon2 - lon1; + + tmp = sin(lat1) * sin(lat2) + cos(lat1) * cos(lat2) * cos(delta_long); + + if (tmp > .999999) { + /* Station points coincide, use an Omni! */ + *bearing = 0.0; + *azimuth = 0.0; + return 0; + } + + if (tmp < -.999999) { + /* + * points are antipodal, it's straight down. + * Station is equal distance in all Azimuths. + * So take 180 Degrees of arc times 60 nm, + * and you get 10800 nm, or whatever units... + */ + + *bearing = 180.0*ARC_IN_KM; + *azimuth = 0.0; + return 0; + } + + arc = acos(tmp); + + /* + * One degree of arc is 60 Nautical miles + * at the surface of the earth, 111.2 km, or 69.1 sm + * This method is easier than the one in the handbook + */ + + /* Short Path */ + + *bearing = ARC_IN_KM * RADIAN * arc; + + /* + * Long Path + * + * distlp = (ARC_IN_KM * 360.0) - distsp; + */ + + cosaz = (sin(lat2) - (sin(lat1) * cos(arc))) / + (sin(arc) * cos(lat1)); + + if (cosaz > .999999) + az = 0.0; + else if (cosaz < -.999999) + az = 180.0; + else + az = acos(cosaz) * RADIAN; + + /* + * Handbook had the test ">= 0.0" which looks backwards?? + */ + + if (sin(delta_long) < 0.0) { + *azimuth = az; + } else { + *azimuth = 360.0 - az; + } + + return 0; +} + +double bearing_long_path(double bearing) +{ + return (ARC_IN_KM * 360.0) - bearing; +} + +double azimuth_long_path(double azimuth) +{ + return 360.0-azimuth; +} + diff --git a/src/rot_conf.c b/src/rot_conf.c new file mode 100644 index 000000000..de0e19c0f --- /dev/null +++ b/src/rot_conf.c @@ -0,0 +1,273 @@ +/* + * Hamlib Interface - configuration interface + * Copyright (c) 2000,2001 by Stephane Fillod and Frank Singleton + * + * $Id: rot_conf.c,v 1.1 2001-12-27 21:46:25 fillods Exp $ + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library 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 Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include /* Standard input/output definitions */ +#include /* String function definitions */ +#include /* UNIX standard function definitions */ + +#include + +#include "rot_conf.h" + + +/* + * Place holder for now. Here will be defined all the configuration + * options available in the rot->state struct. + */ +static const struct confparams rotfrontend_cfg_params[] = { + { TOK_ROT_PATHNAME, "rot_pathname", "Rig path name", + "Path name to the device file of the rotator", + "/dev/rotator", RIG_CONF_STRING, + }, + { TOK_WRITE_DELAY, "write_delay", "Write delay", + "Delay in ms between each byte sent out", + "0", RIG_CONF_NUMERIC, { n: { 0, 1000, 1 } } + }, + { TOK_POST_WRITE_DELAY, "post_write_delay", "Post write delay", + "Delay in ms between each command sent out", + "0", RIG_CONF_NUMERIC, { n: { 0, 1000, 1 } } + }, + { TOK_TIMEOUT, "timeout", "Timeout", "Timeout in ms", + "0", RIG_CONF_NUMERIC, { n: { 0, 10000, 1 } } + }, + { TOK_RETRY, "retry", "Retry", "Max number of retry", + "0", RIG_CONF_NUMERIC, { n: { 0, 10, 1 } } + }, + + { TOK_SERIAL_SPEED, "serial_speed", "Serial speed", + "Serial port baud rate", + "0", RIG_CONF_NUMERIC, { n: { 300, 115200, 1 } } + }, + { TOK_DATA_BITS, "data_bits", "Serial data bits", + "Serial port data bits", + "8", RIG_CONF_NUMERIC, { n: { 5, 8, 1 } } + }, + { TOK_STOP_BITS, "stop_bits", "Serial stop bits", + "Serial port stop bits", + "1", RIG_CONF_NUMERIC, { n: { 0, 3, 1 } } + }, + { TOK_PARITY, "serial_parity", "Serial parity", + "Serial port parity", + "None", RIG_CONF_COMBO, { c: {{ "None", "Odd", "Even", NULL }} } + }, + { TOK_HANDSHAKE, "serial_handshake", "Serial handshake", + "Serial port handshake", + "None", RIG_CONF_COMBO, { c: {{ "None", "XONXOFF", "Hardware", NULL }} } + }, + + { RIG_CONF_END, NULL, } +}; + +/* + * frontrot_set_conf + * assumes rot!=NULL, val!=NULL + * TODO: check format of val before doing atoi(). + */ +int frontrot_set_conf(ROT *rot, token_t token, const char *val) +{ + const struct rot_caps *caps; + struct rot_state *rs; + + caps = rot->caps; + rs = &rot->state; + + switch(token) { + case TOK_ROT_PATHNAME: + strcpy(rs->rotport.pathname, val); + break; + case TOK_WRITE_DELAY: + rs->rotport.write_delay = atoi(val); + break; + case TOK_POST_WRITE_DELAY: + rs->rotport.post_write_delay = atoi(val); + break; + case TOK_TIMEOUT: + rs->rotport.timeout = atoi(val); + break; + case TOK_RETRY: + rs->rotport.retry = atoi(val); + break; + + case TOK_SERIAL_SPEED: + rs->rotport.parm.serial.rate = atoi(val); + break; + case TOK_DATA_BITS: + rs->rotport.parm.serial.data_bits = atoi(val); + break; + case TOK_STOP_BITS: + rs->rotport.parm.serial.stop_bits = atoi(val); + break; + case TOK_PARITY: + if (!strncmp(val, "None", 8)) + rs->rotport.parm.serial.parity = RIG_PARITY_NONE; + else if (!strncmp(val, "Odd", 8)) + rs->rotport.parm.serial.parity = RIG_PARITY_ODD; + else if (!strncmp(val, "Even", 8)) + rs->rotport.parm.serial.parity = RIG_PARITY_EVEN; + else + return -RIG_EINVAL; + break; + case TOK_HANDSHAKE: + if (!strncmp(val, "None", 8)) + rs->rotport.parm.serial.handshake = RIG_HANDSHAKE_NONE; + else if (!strncmp(val, "XONXOFF", 8)) + rs->rotport.parm.serial.handshake = RIG_HANDSHAKE_XONXOFF; + else if (!strncmp(val, "Hardware", 8)) + rs->rotport.parm.serial.handshake = RIG_HANDSHAKE_HARDWARE; + else + return -RIG_EINVAL; + break; + + default: + return -RIG_EINVAL; + } + return RIG_OK; +} + +/* + * frontrot_get_conf + * assumes rot!=NULL, val!=NULL + */ +int frontrot_get_conf(ROT *rot, token_t token, char *val) +{ + const struct rot_caps *caps; + struct rot_state *rs; + const char *s; + + caps = rot->caps; + rs = &rot->state; + + switch(token) { + case TOK_ROT_PATHNAME: + strcpy(val, rs->rotport.pathname); + break; + case TOK_WRITE_DELAY: + sprintf(val, "%d", rs->rotport.write_delay); + break; + case TOK_POST_WRITE_DELAY: + sprintf(val, "%d", rs->rotport.post_write_delay); + break; + case TOK_TIMEOUT: + sprintf(val, "%d", rs->rotport.timeout); + break; + case TOK_RETRY: + sprintf(val, "%d", rs->rotport.retry); + break; + case TOK_SERIAL_SPEED: + sprintf(val, "%d", rs->rotport.parm.serial.rate); + break; + case TOK_DATA_BITS: + sprintf(val, "%d", rs->rotport.parm.serial.data_bits); + break; + case TOK_STOP_BITS: + sprintf(val, "%d", rs->rotport.parm.serial.stop_bits); + break; + case TOK_PARITY: + switch (rs->rotport.parm.serial.parity) { + case RIG_PARITY_NONE: s = "None"; break; + case RIG_PARITY_ODD: s = "Odd"; break; + case RIG_PARITY_EVEN: s = "Even"; break; + default: return -RIG_EINVAL; + } + strcpy(val, s); + break; + case TOK_HANDSHAKE: + switch (rs->rotport.parm.serial.handshake) { + case RIG_HANDSHAKE_NONE: s = "None"; break; + case RIG_HANDSHAKE_XONXOFF: s = "XONXOFF"; break; + case RIG_HANDSHAKE_HARDWARE: s = "Hardware"; break; + default: return -RIG_EINVAL; + } + strcpy(val, s); + break; + + default: + return -RIG_EINVAL; + } + + return RIG_OK; +} + +/* + * rot_token_foreach + * executes cfunc on all the elements stored in the conf table + * start first with backend conf table, then finish with frontend table + */ +int rot_token_foreach(ROT *rot, int (*cfunc)(const struct confparams *, rig_ptr_t), rig_ptr_t data) +{ + const struct confparams *cfp; + + if (!rot || !rot->caps || !cfunc) + return -RIG_EINVAL; + + for (cfp = rot->caps->cfgparams; cfp && cfp->name; cfp++) + if ((*cfunc)(cfp, data) == 0) + return RIG_OK; + for (cfp = rotfrontend_cfg_params; cfp->name; cfp++) + if ((*cfunc)(cfp, data) == 0) + return RIG_OK; + return RIG_OK; +} + + +/* + * lookup conf token by its name, return pointer to confparams struct. + * + * lookup backend config table first, then fall back to frontend. + * TODO: should use Lex to speed it up, strcmp hurts! + */ +const struct confparams *rot_confparam_lookup(ROT *rot, const char *name) +{ + const struct confparams *cfp; + + if (!rot || !rot->caps) + return NULL; + for (cfp = rot->caps->cfgparams; cfp && cfp->name; cfp++) + if (!strcmp(cfp->name, name)) + return cfp; + for (cfp = rotfrontend_cfg_params; cfp->name; cfp++) + if (!strcmp(cfp->name, name)) + return cfp; + return NULL; +} + +/* + * Simple lookup returning token id assicated with name + */ +token_t rot_token_lookup(ROT *rot, const char *name) +{ + const struct confparams *cfp; + + cfp = rot_confparam_lookup(rot, name); + if (!cfp) + return RIG_CONF_END; + + return cfp->token; +} + + diff --git a/src/rot_conf.h b/src/rot_conf.h new file mode 100644 index 000000000..4d6b913e5 --- /dev/null +++ b/src/rot_conf.h @@ -0,0 +1,48 @@ +/* + * Hamlib Interface - configuration header + * Copyright (c) 2000,2001 by Stephane Fillod and Frank Singleton + * + * $Id: rot_conf.h,v 1.1 2001-12-27 21:46:25 fillods Exp $ + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library 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 Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef _ROT_CONF_H +#define _ROT_CONF_H 1 + +#include + + +int frontrot_set_conf(ROT *rot, token_t token, const char *val); +int frontrot_get_conf(ROT *rot, token_t token, char *val); + +#define ROT_TOKEN_FRONTEND RIG_TOKEN_FRONTEND + +#define TOK_ROT_PATHNAME ROT_TOKEN_FRONTEND(10) +#define TOK_WRITE_DELAY ROT_TOKEN_FRONTEND(12) +#define TOK_POST_WRITE_DELAY ROT_TOKEN_FRONTEND(13) +#define TOK_TIMEOUT ROT_TOKEN_FRONTEND(14) +#define TOK_RETRY ROT_TOKEN_FRONTEND(15) + +#define TOK_SERIAL_SPEED ROT_TOKEN_FRONTEND(30) +#define TOK_DATA_BITS ROT_TOKEN_FRONTEND(31) +#define TOK_STOP_BITS ROT_TOKEN_FRONTEND(32) +#define TOK_PARITY ROT_TOKEN_FRONTEND(33) +#define TOK_HANDSHAKE ROT_TOKEN_FRONTEND(34) + + +#endif /* _ROT_CONF_H */ + diff --git a/src/rot_reg.c b/src/rot_reg.c new file mode 100644 index 000000000..d47614145 --- /dev/null +++ b/src/rot_reg.c @@ -0,0 +1,343 @@ +/* + * Hamlib Interface - provides registering for dynamically loadable backends. + * Copyright (c) 2000,2001 by Stephane Fillod and Frank Singleton + * + * $Id: rot_reg.c,v 1.1 2001-12-27 21:46:25 fillods Exp $ + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library 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 Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include + +/* This is libtool's dl wrapper */ +#include + +#include + + +#ifndef PATH_MAX +# define PATH_MAX 1024 +#endif + +#define ROT_BACKEND_MAX 32 + +/* + * ROT_BACKEND_LIST is defined in rotlist.h, please keep it up to data, + * ie. each time you give birth to a new backend + * Also, it should be possible to register "external" backend, + * that is backend that were not known by Hamlib at compile time. + * Maybe, rotlist.h should reserve some numbers for them? --SF + */ +static struct { + int be_num; + const char *be_name; + rot_model_t (*be_probe)(port_t *); +} rot_backend_list[ROT_BACKEND_MAX] = ROT_BACKEND_LIST; + + +/* + * This struct to keep track of known rot models. + * It is chained, and used in a hash table, see below. + */ +struct rot_list { + const struct rot_caps *caps; + lt_dlhandle handle; /* handle returned by lt_dlopen() */ + struct rot_list *next; +}; + +#define ROTLSTHASHSZ 16 +#define HASH_FUNC(a) ((a)%ROTLSTHASHSZ) + +/* + * The rot_hash_table is a hash table pointing to a list of next==NULL + * terminated caps. + */ +static struct rot_list *rot_hash_table[ROTLSTHASHSZ] = { NULL, }; + + +static int rot_lookup_backend(rot_model_t rot_model); + +/* + * Basically, this is a hash insert function that doesn't check for dup! + */ +int rot_register(const struct rot_caps *caps) +{ + int hval; + struct rot_list *p; + + if (!caps) + return -RIG_EINVAL; + + rot_debug(RIG_DEBUG_VERBOSE, "rot_register (%d)\n",caps->rot_model); + +#ifndef DONT_WANT_DUP_CHECK + if (rot_get_caps(caps->rot_model)!=NULL) + return -RIG_EINVAL; +#endif + + p = (struct rot_list*)malloc(sizeof(struct rot_list)); + if (!p) + return -RIG_ENOMEM; + + hval = HASH_FUNC(caps->rot_model); + p->caps = caps; + p->handle = NULL; + p->next = rot_hash_table[hval]; + rot_hash_table[hval] = p; + + return RIG_OK; +} + +/* + * Get rot capabilities. + * ie. rot_hash_table lookup + */ + +const struct rot_caps *rot_get_caps(rot_model_t rot_model) +{ + struct rot_list *p; + + for (p = rot_hash_table[HASH_FUNC(rot_model)]; p; p=p->next) { + if (p->caps->rot_model == rot_model) + return p->caps; + } + return NULL; /* sorry, caps not registered! */ +} + +/* + * lookup for backend index in rot_backend_list table, + * according to BACKEND_NUM + * return -1 if not found. + */ +static int rot_lookup_backend(rot_model_t rot_model) +{ + int i; + + for (i=0; inext) { + if (p->caps->rot_model == rot_model) { + if (q == NULL) + rot_hash_table[hval] = p->next; + else + q->next = p->next; + free(p); + return RIG_OK; + } + q = p; + } + return -RIG_EINVAL; /* sorry, caps not registered! */ +} + +/* + * rot_list_foreach + * executes cfunc on all the elements stored in the rot hash list + */ +int rot_list_foreach(int (*cfunc)(const struct rot_caps*, rig_ptr_t),rig_ptr_t data) +{ + struct rot_list *p; + int i; + + if (!cfunc) + return -RIG_EINVAL; + + for (i=0; inext) + if ((*cfunc)(p->caps,data) == 0) + return RIG_OK; + } + return RIG_OK; +} + +/* + * rot_probe_all + * called straight by rot_probe + */ +rot_model_t rot_probe_all(port_t *p) +{ + int i; + rot_model_t rot_model; + + for (i=0; i +#include +#include +#include +#include +#include +#include + + +#include +#include +#include "rot_conf.h" + + +#define DEFAULT_SERIAL_PORT "/dev/rotator" + +/* + * Data structure to track the opened rot (by rot_open) + */ +struct opened_rot_l { + ROT *rot; + struct opened_rot_l *next; +}; +static struct opened_rot_l *opened_rot_list = { NULL }; + +/* + * track which rot is opened (with rot_open) + * needed at least for transceive mode + */ +static int add_opened_rot(ROT *rot) +{ + struct opened_rot_l *p; + p = (struct opened_rot_l *)malloc(sizeof(struct opened_rot_l)); + if (!p) + return -RIG_ENOMEM; + p->rot = rot; + p->next = opened_rot_list; + opened_rot_list = p; + return RIG_OK; +} + +static int remove_opened_rot(ROT *rot) +{ + struct opened_rot_l *p,*q; + q = NULL; + + for (p=opened_rot_list; p; p=p->next) { + if (p->rot == rot) { + if (q == NULL) { + opened_rot_list = opened_rot_list->next; + } else { + q->next = p->next; + } + free(p); + return RIG_OK; + } + q = p; + } + return -RIG_EINVAL; /* Not found in list ! */ +} + +/** + * \brief execs cfunc() on each opened rot + * \param cfunc The function to be executed on each rot + * \param data Data pointer to be passed to cfunc() + * + * Calls cfunc() function for each opened rot. + * The contents of the opened rot table + * is processed in random order according to a function + * pointed to by \a cfunc, whic is called with two arguments, + * the first pointing to the #ROT handle, the second + * to a data pointer \a data. + * If \a data is not needed, then it can be set to NULL. + * The processing of the opened rot table is stopped + * when cfunc() returns 0. + * \internal + * + * \return always RIG_OK. + */ + +int foreach_opened_rot(int (*cfunc)(ROT *, rig_ptr_t), rig_ptr_t data) +{ + struct opened_rot_l *p; + + for (p=opened_rot_list; p; p=p->next) { + if ((*cfunc)(p->rot,data) == 0) + return RIG_OK; + } + return RIG_OK; +} + +/** + * \brief allocate a new #ROT handle + * \param rot_model The rot model for this new handle + * + * Allocates a new #ROT handle and initializes the associated data + * for \a rot_model. + * + * \return a pointer to the #ROT handle otherwise NULL if memory allocation + * failed or \a rot_model is unknown (e.g. backend autoload failed). + * + * \sa rot_cleanup(), rot_open() + */ + +ROT *rot_init(rot_model_t rot_model) +{ + ROT *rot; + const struct rot_caps *caps; + struct rot_state *rs; + int retcode; + + rot_debug(RIG_DEBUG_VERBOSE,"rot:rot_init called \n"); + + rot_check_backend(rot_model); + + caps = rot_get_caps(rot_model); + if (!caps) + return NULL; + + /* + * okay, we've found it. Allocate some memory and set it to zeros, + * and especially the initialize the callbacks + */ + rot = calloc(1, sizeof(ROT)); + if (rot == NULL) { + /* + * FIXME: how can the caller know it's a memory shortage, + * and not "rot not found" ? + */ + return NULL; + } + + rot->caps = caps; + + /* + * populate the rot->state + * TODO: read the Preferences here! + */ + + rs = &rot->state; + + rs->comm_state = 0; + rs->rotport.type.rig = caps->port_type; /* default from caps */ + strncpy(rs->rotport.pathname, DEFAULT_SERIAL_PORT, FILPATHLEN); + rs->rotport.parm.serial.rate = caps->serial_rate_max; /* fastest ! */ + rs->rotport.parm.serial.data_bits = caps->serial_data_bits; + rs->rotport.parm.serial.stop_bits = caps->serial_stop_bits; + rs->rotport.parm.serial.parity = caps->serial_parity; + rs->rotport.parm.serial.handshake = caps->serial_handshake; + rs->rotport.write_delay = caps->write_delay; + rs->rotport.post_write_delay = caps->post_write_delay; + + rs->rotport.timeout = caps->timeout; + rs->rotport.retry = caps->retry; + + rs->min_el = caps->min_el; + rs->min_el = caps->min_el; + rs->min_az = caps->min_az; + rs->min_az = caps->min_az; + + rs->rotport.fd = -1; + + /* + * let the backend a chance to setup his private data + * This must be done only once defaults are setup, + * so the backend init can override rot_state. + */ + if (caps->rot_init != NULL) { + retcode = caps->rot_init(rot); + if (retcode != RIG_OK) { + rot_debug(RIG_DEBUG_VERBOSE,"rot:backend_init failed!\n"); + /* cleanup and exit */ + free(rot); + return NULL; + } + } + + return rot; +} + +/** + * \brief open the communication to the rot + * \param rot The #ROT handle of the radio to be opened + * + * Opens communication to a radio which \a ROT handle has been passed + * by argument. + * + * \return RIG_OK if the operation has been sucessful, otherwise + * a negative value if an error occured (in which case, cause is + * set appropriately). + * + * \retval RIG_EINVAL \a rot is NULL or unconsistent. + * \retval RIG_ENIMPL port type communication is not implemented yet. + * + * \sa rot_init(), rot_close() + */ + +int rot_open(ROT *rot) +{ + const struct rot_caps *caps; + struct rot_state *rs; + int status; + azimuth_t az; + elevation_t el; + + rot_debug(RIG_DEBUG_VERBOSE,"rot:rot_open called \n"); + + if (!rot || !rot->caps) + return -RIG_EINVAL; + + caps = rot->caps; + rs = &rot->state; + + if (rs->comm_state) + return -RIG_EINVAL; + + rs->rotport.fd = -1; + + switch(rs->rotport.type.rig) { + case RIG_PORT_SERIAL: + status = serial_open(&rs->rotport); + if (status != 0) + return status; + break; + + case RIG_PORT_DEVICE: + status = open(rs->rotport.pathname, O_RDWR, 0); + if (status < 0) + return -RIG_EIO; + rs->rotport.fd = status; + break; + + case RIG_PORT_NONE: + case RIG_PORT_RPC: + break; /* ez :) */ + + case RIG_PORT_NETWORK: /* not implemented yet! */ + return -RIG_ENIMPL; + default: + return -RIG_EINVAL; + } + + + add_opened_rot(rot); + + rs->comm_state = 1; + + /* + * Maybe the backend has something to initialize + * In case of failure, just close down and report error code. + */ + if (caps->rot_open != NULL) { + status = caps->rot_open(rot); + if (status != RIG_OK) { + rot_close(rot); + return status; + } + } + + /* + * trigger state->current_az/current_el first retrieval + */ + rot_get_position(rot, &az, &el); + + return RIG_OK; +} + +/** + * \brief close the communication to the rot + * \param rot The #ROT handle of the radio to be closed + * + * Closes communication to a radio which \a ROT handle has been passed + * by argument that was previously open with rot_open(). + * + * \return RIG_OK if the operation has been sucessful, otherwise + * a negative value if an error occured (in which case, cause is + * set appropriately). + * + * \sa rot_cleanup(), rot_open() + */ + +int rot_close(ROT *rot) +{ + const struct rot_caps *caps; + struct rot_state *rs; + + rot_debug(RIG_DEBUG_VERBOSE,"rot:rot_close called \n"); + + if (!rot || !rot->caps) + return -RIG_EINVAL; + + caps = rot->caps; + rs = &rot->state; + + if (!rs->comm_state) + return -RIG_EINVAL; + + /* + * Let the backend say 73s to the rot. + * and ignore the return code. + */ + if (caps->rot_close) + caps->rot_close(rot); + + + if (rs->rotport.fd != -1) { + if (!rs->rotport.stream) + fclose(rs->rotport.stream); /* this closes also fd */ + else + close(rs->rotport.fd); + rs->rotport.fd = -1; + rs->rotport.stream = NULL; + } + + remove_opened_rot(rot); + + rs->comm_state = 0; + + return RIG_OK; +} + +/** + * \brief release a rot handle and free associated memory + * \param rot The #ROT handle of the radio to be closed + * + * Releases a rot struct which port has eventualy been closed already + * with rot_close(). + * + * \return RIG_OK if the operation has been sucessful, otherwise + * a negative value if an error occured (in which case, cause is + * set appropriately). + * + * \sa rot_init(), rot_close() + */ + +int rot_cleanup(ROT *rot) +{ + rot_debug(RIG_DEBUG_VERBOSE,"rot:rot_cleanup called \n"); + + if (!rot || !rot->caps) + return -RIG_EINVAL; + + /* + * check if they forgot to close the rot + */ + if (rot->state.comm_state) + rot_close(rot); + + /* + * basically free up the priv struct + */ + if (rot->caps->rot_cleanup) + rot->caps->rot_cleanup(rot); + + free(rot); + + return RIG_OK; +} + + +/** + * \brief set a rotator configuration parameter + * \param rot The rot handle + * \param token The parameter + * \param val The value to set the parameter to + * + * Sets a configuration parameter. + * + * \return RIG_OK if the operation has been sucessful, otherwise + * a negative value if an error occured (in which case, cause is + * set appropriately). + * + * \sa rot_get_conf() + */ +int rot_set_conf(ROT *rot, token_t token, const char *val) +{ + if (!rot || !rot->caps) + return -RIG_EINVAL; + + if (RIG_IS_TOKEN_FRONTEND(token)) + return frontrot_set_conf(rot, token, val); + + if (rot->caps->set_conf == NULL) + return -RIG_ENAVAIL; + + return rot->caps->set_conf(rot, token, val); +} + +/** + * \brief get the value of a configuration parameter + * \param rot The rot handle + * \param token The parameter + * \param val The location where to store the value of config \a token + * + * Retrieves the value of a configuration paramter associated with \a token. + * + * \return RIG_OK if the operation has been sucessful, otherwise + * a negative value if an error occured (in which case, cause is + * set appropriately). + * + * \sa rot_set_conf() + */ +int rot_get_conf(ROT *rot, token_t token, char *val) +{ + if (!rot || !rot->caps || !val) + return -RIG_EINVAL; + + if (RIG_IS_TOKEN_FRONTEND(token)) + return frontrot_get_conf(rot, token, val); + + if (rot->caps->get_conf == NULL) + return -RIG_ENAVAIL; + + return rot->caps->get_conf(rot, token, val); +} + +/** + * \brief set the azimuth and elevation of the rotator + * \param rot The rot handle + * \param azimuth The azimuth to set to + * \param elevation The elevation to set to + * + * Sets the azimuth and elevation of the rotator. + * + * \return RIG_OK if the operation has been sucessful, otherwise + * a negative value if an error occured (in which case, cause is + * set appropriately). + * + * \sa rot_get_position() + */ + +int rot_set_position (ROT *rot, azimuth_t azimuth, elevation_t elevation) +{ + const struct rot_caps *caps; + + if (!rot || !rot->caps) + return -RIG_EINVAL; + + caps = rot->caps; + + if (caps->set_position == NULL) + return -RIG_ENAVAIL; + + return caps->set_position(rot, azimuth, elevation); +} + +/** + * \brief get the azimuth and elevation of the rotator + * \param rot The rot handle + * \param azimuth The location where to store the current azimuth + * \param elevation The location where to store the current elevation + * + * Retrieves the current azimuth and elevation of the rotator. + * + * \return RIG_OK if the operation has been sucessful, otherwise + * a negative value if an error occured (in which case, cause is + * set appropriately). + * + * \sa rot_set_position() + */ + +int rot_get_position (ROT *rot, azimuth_t *azimuth, elevation_t *elevation) +{ + const struct rot_caps *caps; + + if (!rot || !rot->caps || !azimuth || !elevation) + return -RIG_EINVAL; + + caps = rot->caps; + + if (caps->get_position == NULL) + return -RIG_ENAVAIL; + + return caps->get_position(rot, azimuth, elevation); +} + +/** + * \brief get general information from the rotator + * \param rot The rot handle + * + * Retrieves some general information from the rotator. + * This can include firmware revision, exact model name, or just nothing. + * + * \return a pointer to static memory containing the ASCIIZ string + * if the operation has been sucessful, otherwise NULL if an error occured + * or get_info not part of capabilities. + */ +const char* rot_get_info(ROT *rot) +{ + if (!rot || !rot->caps) + return NULL; + + if (rot->caps->get_info == NULL) + return NULL; + + return rot->caps->get_info(rot); +} +