kopia lustrzana https://github.com/Hamlib/Hamlib
791 wiersze
21 KiB
C
791 wiersze
21 KiB
C
/*
|
|
* Hamlib Rotator backend - ARS parallel port backend
|
|
* Copyright (c) 2010 by Stephane Fillod
|
|
* This code is inspired by work from Pablo GARCIA - EA4TX
|
|
*
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h> /* String function definitions */
|
|
#include <unistd.h> /* UNIX standard function definitions */
|
|
#ifdef HAVE_SYS_IOCTL_H
|
|
#include <sys/ioctl.h>
|
|
#endif
|
|
|
|
#ifdef HAVE_PTHREAD
|
|
#include <pthread.h>
|
|
#endif
|
|
|
|
#include "hamlib/rotator.h"
|
|
#include "parallel.h"
|
|
#include "misc.h"
|
|
#include "register.h"
|
|
|
|
#include "ars.h"
|
|
|
|
#define CTL_PIN_CS PARPORT_CONTROL_AUTOFD /* pin14: Control Linefeed */
|
|
#define CTL_PIN_CLK PARPORT_CONTROL_STROBE /* pin01: Control /Strobe */
|
|
#define STA_PIN_D0 PARPORT_STATUS_BUSY /* pin11: Status Busy */
|
|
#define STA_PIN_D1 PARPORT_STATUS_ERROR /* pin15: Status /Error */
|
|
|
|
#define DTA_PIN02 0x01 /* Data0 */
|
|
#define DTA_PIN03 0x02 /* Data1 */
|
|
#define DTA_PIN04 0x04 /* Data2 */
|
|
#define DTA_PIN05 0x08 /* Data3 */
|
|
#define DTA_PIN06 0x10 /* Data4 */
|
|
#define DTA_PIN07 0x20 /* Data5 */
|
|
#define DTA_PIN08 0x40 /* Data6 */
|
|
#define DTA_PIN09 0x80 /* Data7 */
|
|
|
|
#define CTL_PIN16 PARPORT_CONTROL_INIT
|
|
#define CTL_PIN17 PARPORT_CONTROL_SELECT
|
|
|
|
#define ARS_BRAKE_DELAY (100*1000) /* usecs */
|
|
#define ARS_SETTLE_DELAY (500*1000) /* usecs */
|
|
#define PP_IO_PERIOD (25) /* usecs */
|
|
#define NUM_SAMPLES 3
|
|
|
|
/* TODO: take into account ADC res (8 bits -> 1.4 deg at 360 deg)
|
|
* Rem: at 360 deg/mn, that's 6 deg/sec.
|
|
*/
|
|
#define AZ_RANGE 3.
|
|
#define EL_RANGE 2.
|
|
|
|
/* Check return value, with appropriate unlocking upon error.
|
|
* Assumes "rot" variable is current ROT pointer.
|
|
*/
|
|
#define CHKPPRET(a) \
|
|
do { int _retval = a; if (_retval != RIG_OK) \
|
|
{par_unlock (&rot->state.rotport);return _retval; }} while(0)
|
|
|
|
static int ars_init(ROT *rot);
|
|
static int ars_cleanup(ROT *rot);
|
|
static int ars_open(ROT *rot);
|
|
static int ars_close(ROT *rot);
|
|
static int ars_stop(ROT *rot);
|
|
static int ars_move(ROT *rot, int direction, int speed);
|
|
static int ars_set_position_sync(ROT *rot, azimuth_t az, elevation_t el);
|
|
static int ars_set_position(ROT *rot, azimuth_t az, elevation_t el);
|
|
static int ars_get_position(ROT *rot, azimuth_t *az, elevation_t *el);
|
|
|
|
#ifdef HAVE_PTHREAD
|
|
static void *handle_set_position(void *);
|
|
#endif
|
|
|
|
/* ************************************************************************* */
|
|
|
|
static int ars_clear_ctrl_pin(ROT *rot, unsigned char pin)
|
|
{
|
|
hamlib_port_t *pport = &rot->state.rotport;
|
|
struct ars_priv_data *priv = (struct ars_priv_data *)rot->state.priv;
|
|
|
|
priv->pp_control &= ~pin;
|
|
|
|
return par_write_control(pport, priv->pp_control);
|
|
}
|
|
|
|
static int ars_set_ctrl_pin(ROT *rot, unsigned char pin)
|
|
{
|
|
hamlib_port_t *pport = &rot->state.rotport;
|
|
struct ars_priv_data *priv = (struct ars_priv_data *)rot->state.priv;
|
|
|
|
priv->pp_control |= pin;
|
|
|
|
return par_write_control(pport, priv->pp_control);
|
|
}
|
|
|
|
static int ars_clear_data_pin(ROT *rot, unsigned char pin)
|
|
{
|
|
hamlib_port_t *pport = &rot->state.rotport;
|
|
struct ars_priv_data *priv = (struct ars_priv_data *)rot->state.priv;
|
|
|
|
priv->pp_data &= ~pin;
|
|
|
|
return par_write_data(pport, priv->pp_data);
|
|
}
|
|
|
|
static int ars_set_data_pin(ROT *rot, unsigned char pin)
|
|
{
|
|
hamlib_port_t *pport = &rot->state.rotport;
|
|
struct ars_priv_data *priv = (struct ars_priv_data *)rot->state.priv;
|
|
|
|
priv->pp_data |= pin;
|
|
|
|
return par_write_data(pport, priv->pp_data);
|
|
}
|
|
|
|
static int ars_has_el(const ROT *rot)
|
|
{
|
|
return (rot->caps->rot_type & ROT_FLAG_ELEVATION);
|
|
}
|
|
|
|
/* ************************************************************************* */
|
|
|
|
int
|
|
ars_init(ROT *rot)
|
|
{
|
|
struct ars_priv_data *priv;
|
|
|
|
if (!rot)
|
|
{
|
|
return -RIG_EINVAL;
|
|
}
|
|
|
|
rot->state.priv = (struct ars_priv_data *)malloc(sizeof(struct ars_priv_data));
|
|
|
|
if (!rot->state.priv)
|
|
{
|
|
/* whoops! memory shortage! */
|
|
return -RIG_ENOMEM;
|
|
}
|
|
|
|
priv = rot->state.priv;
|
|
|
|
priv->pp_control = 0;
|
|
priv->pp_data = 0;
|
|
|
|
/* Always use 10 bit resolution, which supports also 8 bits
|
|
* at the cost of 2 potentialy wrong lsb */
|
|
priv->adc_res = 10; /* 8 bits vs. 10 bits ADC */
|
|
priv->brake_off = 0; /* i.e. brake is ON */
|
|
priv->curr_move = 0;
|
|
|
|
return RIG_OK;
|
|
}
|
|
|
|
int
|
|
ars_cleanup(ROT *rot)
|
|
{
|
|
if (!rot)
|
|
{
|
|
return -RIG_EINVAL;
|
|
}
|
|
|
|
if (rot->state.priv)
|
|
{
|
|
free(rot->state.priv);
|
|
rot->state.priv = NULL;
|
|
}
|
|
|
|
return RIG_OK;
|
|
}
|
|
|
|
int
|
|
ars_open(ROT *rot)
|
|
{
|
|
/* make it idle, and known state */
|
|
ars_stop(rot);
|
|
|
|
#ifdef HAVE_PTHREAD
|
|
{
|
|
struct ars_priv_data *priv = (struct ars_priv_data *)rot->state.priv;
|
|
pthread_attr_t attr;
|
|
int retcode;
|
|
|
|
pthread_attr_init(&attr);
|
|
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
|
|
|
|
/* create behind-the-scene thread doing the ars_set_position_sync() */
|
|
retcode = pthread_create(&priv->thread, &attr, handle_set_position, rot);
|
|
|
|
if (retcode != RIG_OK)
|
|
{
|
|
rig_debug(RIG_DEBUG_ERR, "%s: pthread_create: %s\n", __func__,
|
|
strerror(retcode));
|
|
return -RIG_ENOMEM;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return RIG_OK;
|
|
}
|
|
|
|
int
|
|
ars_close(ROT *rot)
|
|
{
|
|
#ifdef HAVE_PTHREAD
|
|
struct ars_priv_data *priv = (struct ars_priv_data *)rot->state.priv;
|
|
|
|
pthread_cancel(priv->thread);
|
|
#endif
|
|
|
|
/* leave it in safe state */
|
|
ars_stop(rot);
|
|
|
|
return RIG_OK;
|
|
}
|
|
|
|
int
|
|
ars_stop(ROT *rot)
|
|
{
|
|
struct ars_priv_data *priv = (struct ars_priv_data *)rot->state.priv;
|
|
hamlib_port_t *pport = &rot->state.rotport;
|
|
|
|
rig_debug(RIG_DEBUG_TRACE, "%s called, brake was %s\n", __func__,
|
|
priv->brake_off ? "OFF" : "ON");
|
|
|
|
#ifdef HAVE_PTHREAD
|
|
priv->set_pos_active = 0;
|
|
#endif
|
|
|
|
par_lock(pport);
|
|
|
|
priv->brake_off = 0;
|
|
priv->curr_move = 0;
|
|
|
|
// Relay AUX -> Off
|
|
CHKPPRET(ars_clear_data_pin(rot, DTA_PIN02 | DTA_PIN04 | DTA_PIN08));
|
|
CHKPPRET(ars_clear_ctrl_pin(rot, CTL_PIN17 | CTL_PIN16));
|
|
|
|
// Elevation Relays -> Off
|
|
CHKPPRET(ars_clear_data_pin(rot, DTA_PIN03 | DTA_PIN07));
|
|
|
|
par_unlock(pport);
|
|
|
|
return RIG_OK;
|
|
}
|
|
|
|
int
|
|
ars_move(ROT *rot, int direction, int speed)
|
|
{
|
|
struct ars_priv_data *priv = (struct ars_priv_data *)rot->state.priv;
|
|
hamlib_port_t *pport = &rot->state.rotport;
|
|
int need_settle_delay = 0;
|
|
|
|
rig_debug(RIG_DEBUG_TRACE, "%s called%s%s%s%s%s\n", __func__,
|
|
(direction & ROT_MOVE_LEFT) ? " LEFT" : "",
|
|
(direction & ROT_MOVE_RIGHT) ? " RIGHT" : "",
|
|
(direction & ROT_MOVE_UP) ? " UP" : "",
|
|
(direction & ROT_MOVE_DOWN) ? " DOWN" : "",
|
|
(direction == 0) ? " STOP" : "");
|
|
|
|
par_lock(pport);
|
|
|
|
/* Allow the rotator to settle down when changing direction */
|
|
if (((priv->curr_move & ROT_MOVE_LEFT) && (direction & ROT_MOVE_RIGHT)) ||
|
|
((priv->curr_move & ROT_MOVE_RIGHT) && (direction & ROT_MOVE_LEFT)))
|
|
{
|
|
|
|
// Relay AUX -> Off
|
|
CHKPPRET(ars_clear_data_pin(rot, DTA_PIN02 | DTA_PIN04 | DTA_PIN08));
|
|
CHKPPRET(ars_clear_ctrl_pin(rot, CTL_PIN17 | CTL_PIN16));
|
|
need_settle_delay = 1;
|
|
priv->brake_off = 0;
|
|
}
|
|
|
|
if (ars_has_el(rot) &&
|
|
(((priv->curr_move & ROT_MOVE_UP) && (direction & ROT_MOVE_DOWN)) ||
|
|
((priv->curr_move & ROT_MOVE_DOWN) && (direction & ROT_MOVE_UP))))
|
|
{
|
|
|
|
// Elevation Relays -> Off
|
|
CHKPPRET(ars_clear_data_pin(rot, DTA_PIN03 | DTA_PIN07));
|
|
need_settle_delay = 1;
|
|
}
|
|
|
|
if (need_settle_delay)
|
|
{
|
|
rig_debug(RIG_DEBUG_TRACE, "%s need settling delay\n", __func__);
|
|
|
|
hl_usleep(ARS_SETTLE_DELAY);
|
|
}
|
|
|
|
priv->curr_move = direction;
|
|
|
|
|
|
/* Brake handling, only for AZ */
|
|
if (!priv->brake_off && (direction & (ROT_MOVE_LEFT | ROT_MOVE_RIGHT)))
|
|
{
|
|
/* release the brake */
|
|
if (ars_has_el(rot))
|
|
{
|
|
// RCI Model Azim & Elev
|
|
// Desactivated CCW/CW relays
|
|
CHKPPRET(ars_clear_ctrl_pin(rot, CTL_PIN17 | CTL_PIN16));
|
|
// Relay Aux
|
|
CHKPPRET(ars_set_data_pin(rot, DTA_PIN02 | DTA_PIN04 | DTA_PIN06 | DTA_PIN08));
|
|
CHKPPRET(ars_clear_data_pin(rot, DTA_PIN09));
|
|
}
|
|
else
|
|
{
|
|
// RCI Model Azimuth only
|
|
// Desactivated CCW/CW relays
|
|
CHKPPRET(ars_clear_ctrl_pin(rot, CTL_PIN17 | CTL_PIN16));
|
|
// Relay Aux
|
|
CHKPPRET(ars_set_data_pin(rot,
|
|
DTA_PIN02 | DTA_PIN04 | DTA_PIN06 | DTA_PIN07 | DTA_PIN08 | DTA_PIN09));
|
|
CHKPPRET(ars_clear_data_pin(rot, DTA_PIN03 | DTA_PIN05));
|
|
}
|
|
|
|
priv->brake_off = 1;
|
|
hl_usleep(ARS_BRAKE_DELAY);
|
|
}
|
|
|
|
|
|
if (ars_has_el(rot))
|
|
{
|
|
if (direction & ROT_MOVE_UP)
|
|
{
|
|
/* UP */
|
|
CHKPPRET(ars_set_data_pin(rot, DTA_PIN03 | DTA_PIN06 | DTA_PIN07));
|
|
CHKPPRET(ars_clear_data_pin(rot, DTA_PIN05 | DTA_PIN09));
|
|
}
|
|
else if (direction & ROT_MOVE_DOWN)
|
|
{
|
|
CHKPPRET(ars_set_data_pin(rot, DTA_PIN03 | DTA_PIN05 | DTA_PIN06 | DTA_PIN07));
|
|
CHKPPRET(ars_clear_data_pin(rot, DTA_PIN09));
|
|
}
|
|
else
|
|
{
|
|
// Elevation Relays -> Off
|
|
CHKPPRET(ars_clear_data_pin(rot, DTA_PIN03 | DTA_PIN07));
|
|
}
|
|
}
|
|
|
|
if (direction & ROT_MOVE_LEFT)
|
|
{
|
|
// Relay Left
|
|
if (ars_has_el(rot))
|
|
{
|
|
// RCI Model Azim & Elev
|
|
CHKPPRET(ars_set_data_pin(rot, DTA_PIN02 | DTA_PIN04 | DTA_PIN06));
|
|
CHKPPRET(ars_set_ctrl_pin(rot, CTL_PIN16));
|
|
CHKPPRET(ars_clear_data_pin(rot, DTA_PIN09));
|
|
CHKPPRET(ars_clear_ctrl_pin(rot, CTL_PIN17));
|
|
}
|
|
else
|
|
{
|
|
// RCI Model Azimuth only
|
|
CHKPPRET(ars_set_data_pin(rot,
|
|
DTA_PIN02 | DTA_PIN04 | DTA_PIN06 | DTA_PIN07 | DTA_PIN08));
|
|
CHKPPRET(ars_set_ctrl_pin(rot, CTL_PIN16));
|
|
CHKPPRET(ars_clear_data_pin(rot, DTA_PIN03 | DTA_PIN05));
|
|
CHKPPRET(ars_clear_ctrl_pin(rot, CTL_PIN17));
|
|
}
|
|
}
|
|
else if (direction & ROT_MOVE_RIGHT)
|
|
{
|
|
// Relay Right
|
|
if (ars_has_el(rot))
|
|
{
|
|
// RCI Model Azim & Elev
|
|
CHKPPRET(ars_set_data_pin(rot, DTA_PIN02 | DTA_PIN04 | DTA_PIN06));
|
|
CHKPPRET(ars_set_ctrl_pin(rot, CTL_PIN17));
|
|
CHKPPRET(ars_clear_data_pin(rot, DTA_PIN09));
|
|
CHKPPRET(ars_clear_ctrl_pin(rot, CTL_PIN16));
|
|
}
|
|
else
|
|
{
|
|
// RCI Model Azimuth only
|
|
CHKPPRET(ars_set_data_pin(rot,
|
|
DTA_PIN02 | DTA_PIN04 | DTA_PIN06 | DTA_PIN07 | DTA_PIN08));
|
|
CHKPPRET(ars_set_ctrl_pin(rot, CTL_PIN17));
|
|
CHKPPRET(ars_clear_data_pin(rot, DTA_PIN03 | DTA_PIN05));
|
|
CHKPPRET(ars_clear_ctrl_pin(rot, CTL_PIN16));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Relay AUX -> Off
|
|
CHKPPRET(ars_clear_data_pin(rot, DTA_PIN02 | DTA_PIN04 | DTA_PIN08));
|
|
CHKPPRET(ars_clear_ctrl_pin(rot, CTL_PIN17 | CTL_PIN16));
|
|
// AZ stop
|
|
}
|
|
|
|
par_unlock(pport);
|
|
|
|
return RIG_OK;
|
|
}
|
|
|
|
static int angle_in_range(float angle, float angle_base, float range)
|
|
{
|
|
return (angle >= angle_base - range && angle <= angle_base + range);
|
|
}
|
|
|
|
/*
|
|
* Thread handler
|
|
*/
|
|
#ifdef HAVE_PTHREAD
|
|
static void *handle_set_position(void *arg)
|
|
{
|
|
ROT *rot = (ROT *) arg;
|
|
struct ars_priv_data *priv = (struct ars_priv_data *)rot->state.priv;
|
|
int retcode;
|
|
|
|
while (1)
|
|
{
|
|
|
|
if (!priv->set_pos_active)
|
|
{
|
|
/* TODO: replace polling period by cond var */
|
|
hl_usleep(100 * 1000 - 1);
|
|
continue;
|
|
}
|
|
|
|
retcode = ars_set_position_sync(rot, priv->target_az, priv->target_el);
|
|
priv->set_pos_active = 0;
|
|
|
|
if (retcode != RIG_OK)
|
|
{
|
|
rig_debug(RIG_DEBUG_WARN, "%s: ars_set_position_sync() failed: %s\n",
|
|
__func__, rigerror(retcode));
|
|
hl_usleep(1000 * 1000);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* ars_set_position_sync() is synchronous.
|
|
* See handle_set_position_async() for asynchronous thread handler,
|
|
* with wait loop in background
|
|
*/
|
|
int
|
|
ars_set_position_sync(ROT *rot, azimuth_t az, elevation_t el)
|
|
{
|
|
azimuth_t curr_az, prev_az;
|
|
elevation_t curr_el, prev_el;
|
|
int retval;
|
|
int az_move, el_move;
|
|
struct timeval last_pos_az_tv, last_pos_el_tv;
|
|
|
|
rig_debug(RIG_DEBUG_TRACE, "%s called: %.1f %.1f\n", __func__, az, el);
|
|
|
|
ars_stop(rot);
|
|
|
|
retval = ars_get_position(rot, &curr_az, &curr_el);
|
|
|
|
if (retval != RIG_OK)
|
|
{
|
|
return retval;
|
|
}
|
|
|
|
/* watchdog init */
|
|
prev_az = curr_az;
|
|
prev_el = curr_el;
|
|
gettimeofday(&last_pos_az_tv, NULL);
|
|
last_pos_el_tv = last_pos_az_tv;
|
|
|
|
while (!angle_in_range(curr_az, az, AZ_RANGE) ||
|
|
(ars_has_el(rot) && !angle_in_range(curr_el, el, EL_RANGE))
|
|
)
|
|
{
|
|
|
|
if (curr_az < (az - AZ_RANGE))
|
|
{
|
|
az_move = ROT_MOVE_RIGHT;
|
|
}
|
|
else if (curr_az > (az + AZ_RANGE))
|
|
{
|
|
az_move = ROT_MOVE_LEFT;
|
|
}
|
|
else
|
|
{
|
|
az_move = 0;
|
|
}
|
|
|
|
if (ars_has_el(rot))
|
|
{
|
|
if (curr_el < (el - EL_RANGE))
|
|
{
|
|
el_move = ROT_MOVE_UP;
|
|
}
|
|
else if (curr_el > (el + EL_RANGE))
|
|
{
|
|
el_move = ROT_MOVE_DOWN;
|
|
}
|
|
else
|
|
{
|
|
el_move = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
el_move = 0;
|
|
}
|
|
|
|
retval = ars_move(rot, az_move | el_move, 0);
|
|
|
|
if (retval != RIG_OK)
|
|
{
|
|
ars_stop(rot);
|
|
return retval;
|
|
}
|
|
|
|
/* wait a little */
|
|
hl_usleep(10 * 1000);
|
|
|
|
retval = ars_get_position(rot, &curr_az, &curr_el);
|
|
|
|
if (retval != RIG_OK)
|
|
{
|
|
ars_stop(rot);
|
|
return retval;
|
|
}
|
|
|
|
/* Watchdog detecting when rotor is blocked unexpectedly */
|
|
#define AZ_WATCHDOG 5000 /* ms */
|
|
#define EL_WATCHDOG 5000 /* ms */
|
|
|
|
if (az_move != 0 && angle_in_range(curr_az, prev_az, AZ_RANGE))
|
|
{
|
|
if (rig_check_cache_timeout(&last_pos_az_tv, AZ_WATCHDOG))
|
|
{
|
|
ars_stop(rot);
|
|
return -RIG_ETIMEOUT;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
prev_az = curr_az;
|
|
gettimeofday(&last_pos_az_tv, NULL);
|
|
}
|
|
|
|
if (el_move != 0 && ars_has_el(rot) &&
|
|
angle_in_range(curr_el, prev_el, EL_RANGE))
|
|
{
|
|
if (rig_check_cache_timeout(&last_pos_el_tv, EL_WATCHDOG))
|
|
{
|
|
ars_stop(rot);
|
|
return -RIG_ETIMEOUT;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
prev_el = curr_el;
|
|
gettimeofday(&last_pos_el_tv, NULL);
|
|
}
|
|
}
|
|
|
|
return ars_stop(rot);
|
|
}
|
|
|
|
int
|
|
ars_set_position(ROT *rot, azimuth_t az, elevation_t el)
|
|
{
|
|
#ifdef HAVE_PTHREAD
|
|
struct ars_priv_data *priv = (struct ars_priv_data *)rot->state.priv;
|
|
|
|
/* will be picked by handle_set_position() next polling tick */
|
|
priv->target_az = az;
|
|
priv->target_el = el;
|
|
priv->set_pos_active = 1;
|
|
|
|
return RIG_OK;
|
|
#else
|
|
return ars_set_position_sync(rot, az, el);
|
|
#endif
|
|
}
|
|
|
|
static int comparunsigned(const void *a, const void *b)
|
|
{
|
|
const unsigned ua = *(const unsigned *)a, ub = *(const unsigned *)b;
|
|
|
|
return ua == ub ? 0 : ua < ub ? -1 : 1;
|
|
}
|
|
|
|
int
|
|
ars_get_position(ROT *rot, azimuth_t *az, elevation_t *el)
|
|
{
|
|
struct ars_priv_data *priv = (struct ars_priv_data *)rot->state.priv;
|
|
struct rot_state *rs = &rot->state;
|
|
hamlib_port_t *pport = &rs->rotport;
|
|
int i, num_sample;
|
|
unsigned az_samples[NUM_SAMPLES], az_value;
|
|
unsigned el_samples[NUM_SAMPLES], el_value;
|
|
unsigned char status;
|
|
|
|
par_lock(pport);
|
|
|
|
/* flush last sampled value, with a dummy read */
|
|
CHKPPRET(ars_clear_ctrl_pin(rot, CTL_PIN_CLK));
|
|
hl_usleep(PP_IO_PERIOD);
|
|
|
|
CHKPPRET(ars_clear_ctrl_pin(rot, CTL_PIN_CS));
|
|
hl_usleep(PP_IO_PERIOD);
|
|
|
|
for (i = 0; i < priv->adc_res; i++)
|
|
{
|
|
CHKPPRET(ars_set_ctrl_pin(rot, CTL_PIN_CLK));
|
|
hl_usleep(PP_IO_PERIOD);
|
|
|
|
CHKPPRET(ars_clear_ctrl_pin(rot, CTL_PIN_CLK));
|
|
hl_usleep(PP_IO_PERIOD);
|
|
}
|
|
|
|
CHKPPRET(ars_clear_ctrl_pin(rot, CTL_PIN_CLK));
|
|
hl_usleep(PP_IO_PERIOD);
|
|
|
|
CHKPPRET(ars_set_ctrl_pin(rot, CTL_PIN_CS));
|
|
/* end of dummy read */
|
|
|
|
for (num_sample = 0; num_sample < NUM_SAMPLES; num_sample++)
|
|
{
|
|
|
|
/* read ADC value TLC(1)549 (8/10 bits), by SPI bitbanging */
|
|
|
|
hl_usleep(PP_IO_PERIOD);
|
|
CHKPPRET(ars_clear_ctrl_pin(rot, CTL_PIN_CLK));
|
|
hl_usleep(PP_IO_PERIOD);
|
|
|
|
CHKPPRET(ars_clear_ctrl_pin(rot, CTL_PIN_CS));
|
|
hl_usleep(PP_IO_PERIOD);
|
|
|
|
az_samples[num_sample] = 0;
|
|
el_samples[num_sample] = 0;
|
|
|
|
for (i = 0; i < priv->adc_res; i++)
|
|
{
|
|
CHKPPRET(ars_set_ctrl_pin(rot, CTL_PIN_CLK));
|
|
hl_usleep(PP_IO_PERIOD);
|
|
|
|
CHKPPRET(par_read_status(pport, &status));
|
|
|
|
az_samples[num_sample] <<= 1;
|
|
az_samples[num_sample] |= (status & STA_PIN_D0) ? 1 : 0;
|
|
|
|
el_samples[num_sample] <<= 1;
|
|
el_samples[num_sample] |= (status & STA_PIN_D1) ? 1 : 0;
|
|
|
|
CHKPPRET(ars_clear_ctrl_pin(rot, CTL_PIN_CLK));
|
|
hl_usleep(PP_IO_PERIOD);
|
|
}
|
|
|
|
CHKPPRET(ars_set_ctrl_pin(rot, CTL_PIN_CS));
|
|
|
|
rig_debug(RIG_DEBUG_TRACE, "%s: raw samples: az %u, el %u\n",
|
|
__func__, az_samples[num_sample], el_samples[num_sample]);
|
|
|
|
hl_usleep(PP_IO_PERIOD);
|
|
}
|
|
|
|
par_unlock(pport);
|
|
|
|
qsort(az_samples, NUM_SAMPLES, sizeof(unsigned), comparunsigned);
|
|
qsort(el_samples, NUM_SAMPLES, sizeof(unsigned), comparunsigned);
|
|
|
|
/* select median value in order to rule out glitches */
|
|
az_value = az_samples[NUM_SAMPLES / 2];
|
|
el_value = el_samples[NUM_SAMPLES / 2];
|
|
|
|
*az = rs->min_az + ((float)az_value * (rs->max_az - rs->min_az)) / ((
|
|
1 << priv->adc_res) - 1);
|
|
*el = rs->min_el + ((float)el_value * (rs->max_el - rs->min_el)) / ((
|
|
1 << priv->adc_res) - 1);
|
|
|
|
rig_debug(RIG_DEBUG_TRACE, "%s: az=%.1f el=%.1f\n", __func__, *az, *el);
|
|
|
|
return RIG_OK;
|
|
}
|
|
|
|
|
|
/* ************************************************************************* */
|
|
/*
|
|
* ARS rotator capabilities.
|
|
*/
|
|
|
|
/*
|
|
* RCI/RCI-SE, with Elevation daugtherboard/unit.
|
|
*/
|
|
const struct rot_caps rci_azel_rot_caps =
|
|
{
|
|
ROT_MODEL(ROT_MODEL_RCI_AZEL),
|
|
.model_name = "ARS RCI AZ&EL",
|
|
.mfg_name = "EA4TX",
|
|
.version = "20200112.0",
|
|
.copyright = "LGPL",
|
|
.status = RIG_STATUS_BETA,
|
|
.rot_type = ROT_TYPE_AZEL, /* AZ&EL units */
|
|
.port_type = RIG_PORT_PARALLEL,
|
|
.write_delay = 0,
|
|
.post_write_delay = 10,
|
|
.timeout = 0,
|
|
.retry = 3,
|
|
|
|
.min_az = 0,
|
|
.max_az = 360,
|
|
.min_el = 0,
|
|
.max_el = 180, /* TBC */
|
|
|
|
.rot_init = ars_init,
|
|
.rot_cleanup = ars_cleanup,
|
|
.rot_open = ars_open,
|
|
.rot_close = ars_close,
|
|
.set_position = ars_set_position,
|
|
.get_position = ars_get_position,
|
|
.stop = ars_stop,
|
|
.move = ars_move,
|
|
};
|
|
|
|
/*
|
|
* RCI/RCI-SE, without Elevation daugtherboard/unit.
|
|
* Azimuth only
|
|
*/
|
|
const struct rot_caps rci_az_rot_caps =
|
|
{
|
|
ROT_MODEL(ROT_MODEL_RCI_AZ),
|
|
.model_name = "ARS RCI AZ",
|
|
.mfg_name = "EA4TX",
|
|
.version = "20200112.0",
|
|
.copyright = "LGPL",
|
|
.status = RIG_STATUS_BETA,
|
|
.rot_type = ROT_TYPE_AZIMUTH, /* AZ-only unit */
|
|
.port_type = RIG_PORT_PARALLEL,
|
|
.write_delay = 0,
|
|
.post_write_delay = 10,
|
|
.timeout = 0,
|
|
.retry = 3,
|
|
|
|
.min_az = 0,
|
|
.max_az = 360,
|
|
.min_el = 0,
|
|
.max_el = 180, /* TBC */
|
|
|
|
.rot_init = ars_init,
|
|
.rot_cleanup = ars_cleanup,
|
|
.rot_open = ars_open,
|
|
.rot_close = ars_close,
|
|
.set_position = ars_set_position,
|
|
.get_position = ars_get_position,
|
|
.stop = ars_stop,
|
|
.move = ars_move,
|
|
};
|
|
|
|
|
|
/* ************************************************************************* */
|
|
|
|
DECLARE_INITROT_BACKEND(ars)
|
|
{
|
|
rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__);
|
|
|
|
rot_register(&rci_az_rot_caps);
|
|
rot_register(&rci_azel_rot_caps);
|
|
|
|
return RIG_OK;
|
|
}
|
|
|
|
/* ************************************************************************* */
|
|
/* end of file */
|