Support for the SPID Rot2Prog rotator controller, by Norvald H. Ryeng, LA6YKA

git-svn-id: https://hamlib.svn.sourceforge.net/svnroot/hamlib/trunk@2699 7ae35d74-ebe9-4afe-98af-79ac388436b8
Hamlib-1.2.10
Stéphane Fillod, F8CFE 2009-06-01 10:28:55 +00:00
rodzic 38b3425646
commit 4776b181f6
8 zmienionych plików z 555 dodań i 2 usunięć

Wyświetl plik

@ -192,6 +192,9 @@ M: Rob Frohne, KL7NA
[rotorez]
M: Nate Bargmann, N0NB
[spid]
M: Norvald H. Ryeng, LA6YKA
[Frontend]
M: Stephane Fillod, F8CFE
@ -260,3 +263,4 @@ Steve Conklin, AI4QR, <steve (at) conklinhouse.com>
Martin Ewing, AA6E, <aa6e (at) arrl.net>
Terry Embry, KJ4EED, <mrtembry (at) users.sourceforge.net>
Alessandro Zummo, IZ1PRB <alessandro.zummo (at) towertech.it>
Norvald H. Ryeng, LA6YKA <norvald (at) ryeng.name>

Wyświetl plik

@ -18,7 +18,7 @@ DIST_SUBDIRS = macros include lib libltdl src c++ bindings tests doc \
icom kenwood aor yaesu dummy pcr alinco uniden tentec kachina jrc \
rpcrig winradio easycomm fodtrack rpcrot drake rotorez \
flexradio sartek lowe rft rs tapr kit skanti wj racal tuner \
gs232a heathkit
gs232a heathkit spid
rpm: Makefile
make dist

Wyświetl plik

@ -229,7 +229,7 @@ AC_SUBST(RIGMATRIX)
BACKEND_LIST="icom kenwood aor yaesu dummy pcr alinco uniden tentec kachina jrc drake lowe rft rs kit skanti tapr flexradio wj racal tuner"
ROT_BACKEND_LIST="dummy easycomm fodtrack gs232a heathkit kit rotorez sartek"
ROT_BACKEND_LIST="dummy easycomm fodtrack gs232a heathkit kit rotorez sartek spid"
BINDINGS=""
BINDING_ALL=""
BINDING_CHECK=""
@ -496,6 +496,7 @@ easycomm/Makefile
fodtrack/Makefile
gs232a/Makefile
heathkit/Makefile
spid/Makefile
sartek/Makefile
rpcrig/Makefile
rpcrot/Makefile

Wyświetl plik

@ -166,6 +166,16 @@
#define ROT_BACKEND_HEATHKIT "heathkit"
#define ROT_MODEL_HD1780 ROT_MAKE_MODEL(ROT_HEATHKIT, 1)
/*! \def ROT_MODEL_SPID
* \brief A macro that returns the model number of the SPID backend.
*
* The SPID backend can be used with rotators that support the SPID
* protocol.
*/
#define ROT_SPID 9
#define ROT_BACKEND_SPID "spid"
#define ROT_MODEL_SPID ROT_MAKE_MODEL(ROT_SPID, 1)
/*! \typedef typedef int rot_model_t
\brief Convenience type definition for rotator model.
*/
@ -188,6 +198,7 @@ typedef int rot_model_t;
{ ROT_GS232A, ROT_BACKEND_GS232A }, \
{ ROT_KIT, ROT_BACKEND_KIT }, \
{ ROT_HEATHKIT, ROT_BACKEND_HEATHKIT }, \
{ ROT_SPID, ROT_BACKEND_SPID }, \
{ 0, NULL }, /* end */ \
}

7
spid/Makefile.am 100644
Wyświetl plik

@ -0,0 +1,7 @@
lib_LTLIBRARIES = hamlib-spid.la
hamlib_spid_la_SOURCES = spid.c
hamlib_spid_la_LDFLAGS = -no-undefined -module -avoid-version
hamlib_spid_la_LIBADD = $(top_builddir)/src/libhamlib.la
noinst_HEADERS = spid.h

294
spid/spid.c 100644
Wyświetl plik

@ -0,0 +1,294 @@
/*
* Hamlib Rotator backend - SPID Rot2Prog
* Copyright (c) 2009 by Norvald H. Ryeng, LA6YKA
*
* $Id$
*
* 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <math.h>
#include "hamlib/rotator.h"
#include "serial.h"
#include "misc.h"
#include "register.h"
#include "spid.h"
#define TOK_AZRES 1
#define TOK_ELRES 2
struct spid_priv_data {
int az_resolution;
int el_resolution;
};
static int spid_rot_init(ROT *rot)
{
struct spid_priv_data *priv;
rig_debug(RIG_DEBUG_TRACE, "%s called\n", __FUNCTION__);
if (!rot || !rot->caps)
return -RIG_EINVAL;
priv = (struct spid_priv_data*)malloc(sizeof(struct spid_priv_data));
if (!priv) {
return -RIG_ENOMEM;
}
rot->state.priv = (void*)priv;
priv->az_resolution = 0;
priv->el_resolution = 0;
return RIG_OK;
}
static int spid_rot_cleanup(ROT *rot)
{
rig_debug(RIG_DEBUG_TRACE, "%s called\n", __FUNCTION__);
if (!rot)
return -RIG_EINVAL;
if (rot->state.priv)
free(rot->state.priv);
rot->state.priv = NULL;
return RIG_OK;
}
static int spid_get_conf(ROT *rot, token_t token, char *val)
{
struct spid_priv_data *priv = (struct spid_priv_data*)rot->state.priv;
rig_debug(RIG_DEBUG_TRACE, "%s called %d\n", __FUNCTION__, token);
switch(token) {
case TOK_AZRES:
sprintf(val, "%d", priv->az_resolution);
break;
case TOK_ELRES:
sprintf(val, "%d", priv->el_resolution);
break;
default:
return -RIG_EINVAL;
}
return RIG_OK;
}
static int spid_set_conf(ROT *rot, token_t token, const char *val)
{
struct spid_priv_data *priv = (struct spid_priv_data*)rot->state.priv;
rig_debug(RIG_DEBUG_TRACE, "%s called %d %s\n", __FUNCTION__, token, val);
switch(token) {
case TOK_AZRES:
priv->az_resolution = atoi(val);
break;
case TOK_ELRES:
priv->el_resolution = atoi(val);
break;
default:
return -RIG_EINVAL;
}
return RIG_OK;
}
static int spid_rot_set_position(ROT *rot, azimuth_t az, elevation_t el)
{
struct rot_state *rs;
rs = &rot->state;
struct spid_priv_data *priv = (struct spid_priv_data*)rs->priv;
int retval;
int retry_read = 0;
char cmdstr[13];
unsigned int u_az, u_el;
rig_debug(RIG_DEBUG_TRACE, "%s called: %f %f\n", __FUNCTION__, az, el);
if (!priv->az_resolution || !priv->el_resolution) {
do {
retval = write_block(&rs->rotport, "\x57\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1F\x20", 13);
if (retval != RIG_OK) {
return retval;
}
memset(cmdstr, 0, 12);
retval = read_block(&rs->rotport, cmdstr, 12);
} while (retval < 0 && retry_read++ < rot->state.rotport.retry);
if (retval < 0)
return retval;
} else {
cmdstr[5] = priv->az_resolution; /* PH */
cmdstr[10] = priv->el_resolution; /* PV */
}
u_az = cmdstr[5]*(360 + az);
u_el = cmdstr[10]*(360 + el);
cmdstr[0] = 0x57; /* S */
cmdstr[1] = 0x30 + u_az/1000; /* H1 */
cmdstr[2] = 0x30 + (u_az % 1000) / 100; /* H2 */
cmdstr[3] = 0x30 + (u_az % 100) / 10; /* H3 */
cmdstr[4] = 0x30 + (u_az % 10); /* H4 */
cmdstr[6] = 0x30 + u_el / 1000; /* V1 */
cmdstr[7] = 0x30 + (u_el % 1000) / 100; /* V2 */
cmdstr[8] = 0x30 + (u_el % 100) / 10; /* V3 */
cmdstr[9] = 0x30 + (u_el % 10); /* V4 */
cmdstr[11] = 0x2F; /* K */
cmdstr[12] = 0x20; /* END */
retval = write_block(&rs->rotport, cmdstr, 13);
if (retval != RIG_OK) {
return retval;
}
return RIG_OK;
}
static int spid_rot_get_position(ROT *rot, azimuth_t *az, elevation_t *el)
{
struct rot_state *rs;
rs = &rot->state;
int retval;
int retry_read = 0;
char posbuf[12];
rig_debug(RIG_DEBUG_TRACE, "%s called\n", __FUNCTION__);
do {
retval = write_block(&rs->rotport, "\x57\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1F\x20", 13);
if (retval != RIG_OK) {
return retval;
}
memset(posbuf, 0, 12);
retval = read_block(&rs->rotport, posbuf, 12);
} while (retval < 0 && retry_read++ < rot->state.rotport.retry);
if (retval < 0)
return retval;
*az = posbuf[1] * 100;
*az += posbuf[2] * 10;
*az += posbuf[3];
*az += posbuf[4] / 10.0;
*az -= 360;
*el = posbuf[6] * 100;
*el += posbuf[7] * 10;
*el += posbuf[8];
*el += posbuf[9] / 10.0;
*el -= 360;
rig_debug(RIG_DEBUG_TRACE, "%s: (az, el) = (%.1f, %.1f)\n",
__FUNCTION__, *az, *el);
return RIG_OK;
}
static int spid_rot_stop(ROT *rot)
{
struct rot_state *rs;
int retval;
rs = &rot->state;
int retry_read = 0;
char posbuf[12];
rig_debug(RIG_DEBUG_TRACE, "%s called\n", __FUNCTION__);
do {
retval = write_block(&rs->rotport, "\x57\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0F\x20", 13);
if (retval != RIG_OK) {
return retval;
}
memset(posbuf, 0, 12);
retval = read_block(&rs->rotport, posbuf, 12);
} while (retval < 0 && retry_read++ < rot->state.rotport.retry);
if (retval < 0)
return retval;
return RIG_OK;
}
const struct confparams spid_cfg_params[] = {
{ TOK_AZRES, "az_resolution", "Azimuth resolution", "Number of pulses per degree, 0 = auto sense",
"0", RIG_CONF_NUMERIC, { .n = { 0, 0xff, 1 } }
},
{ TOK_ELRES, "el_resolution", "Eleveation resolution", "Number of pulses per degree, 0 = auto sense",
"0", RIG_CONF_NUMERIC, { .n = { 0, 0xff, 1 } }
},
{ RIG_CONF_END, NULL, }
};
const struct rot_caps spid_rot_caps = {
.rot_model = ROT_MODEL_SPID,
.model_name = "Rot2Prog",
.mfg_name = "SPID",
.version = "0.1",
.copyright = "LGPL",
.status = RIG_STATUS_UNTESTED,
.rot_type = ROT_TYPE_OTHER,
.port_type = RIG_PORT_SERIAL,
.serial_rate_min = 600,
.serial_rate_max = 1200,
.serial_data_bits = 8,
.serial_stop_bits = 1,
.serial_parity = RIG_PARITY_NONE,
.serial_handshake = RIG_HANDSHAKE_NONE,
.write_delay = 0,
.post_write_delay = 0,
.timeout = 400,
.retry = 3,
.min_az = -180.0,
.max_az = 540.0,
.min_el = -20.0,
.max_el = 210.0,
.cfgparams = spid_cfg_params,
.get_conf = spid_get_conf,
.set_conf = spid_set_conf,
.rot_init = spid_rot_init,
.rot_cleanup = spid_rot_cleanup,
.get_position = spid_rot_get_position,
.set_position = spid_rot_set_position,
.stop = spid_rot_stop,
};
DECLARE_INITROT_BACKEND(spid)
{
rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __FUNCTION__);
rot_register(&spid_rot_caps);
return RIG_OK;
}

28
spid/spid.h 100644
Wyświetl plik

@ -0,0 +1,28 @@
/*
* Hamlib Rotator backend - SPID Rot2Prog
* Copyright (c) 2009 by Norvald H. Ryeng, LA6YKA
*
* $Id$
*
* 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_SPID_H
#define _ROT_SPID_H 1
extern const struct rot_caps spid_rot_caps;
#endif /* _ROT_SPID_H */

208
spid/spid.txt 100644
Wyświetl plik

@ -0,0 +1,208 @@
SPID Rot2Prog Protocol
======================
This is an attempt at documenting the protocol of the Rot2Prog rotator
controller from SPID Elektronik (spid@alpha.pl). The protocol is
supposed to be similar on Rot1Prog and Rot2Prog, but the specification
in this document has only been tested on Rot2Prog.
GENERAL INFO
------------
The SPID protocol supports 3 commands: stop, status and set. The stop
command stops the rotator in its current position. The status command
returns the current position of the rotator, and the set command tells
the rotator to rotate to a given position.
The rotator controller communicates with the PC using a serial
port. Communication parameters are 600 bps, 8 bits, no parity and 1
stop bit. (Rot1Prog uses 1200 bps.)
All commands are issued as 13 byte packets, and responses are received
as 12 byte packets.
COMMAND PACKETS
---------------
Command packets are 13 byte long.
Byte: 0 1 2 3 4 5 6 7 8 9 10 11 12
-----------------------------------------------------------------
Field: | S | H1 | H2 | H3 | H4 | PH | V1 | V2 | V3 | V4 | PV | K | END |
-----------------------------------------------------------------
Value: 57 3x 3x 3x 3x 0x 3x 3x 3x 3x 0x xF 20 (hex)
S: Start byte. This is always 0x57 ('W')
H1-H4: Azimuth as ASCII characters 0-9
PH: Azimuth resolution in pulses per degree (ignored!)
V1-V4: Elevation as ASCII characters 0-9
PV: Elevation resolution in pulses per degree (ignored!)
K: Command (0x0F=stop, 0x1F=status, 0x2F=set)
END: End byte. This is always 0x20 (space)
Positions are encoded as number of pulses in ASCII numbers
'0000'-'9999' (see set command for formula).
RESPONSE PACKETS
----------------
Response packets are 12 byte long.
Byte: 0 1 2 3 4 5 6 7 8 9 10 11
-------------------------------------------------------------
Field: | S | H1 | H2 | H3 | H4 | PH | V1 | V2 | V3 | V4 | PV | END |
-------------------------------------------------------------
Value: 57 3x 3x 3x 3x 0x 3x 3x 3x 3x 0x 20 (hex)
S: Start byte. This is always 0x57 ('W')
H1-H4: Azimuth as byte values
PH: Azimuth resolution in pulses per degree (controller setting)
V1-V4: Elevation as byte values
PV: Elevation resolution in pulses per degree (controller setting)
END: End byte. This is always 0x20 (space)
Positions are decoded using the following formulas:
az = H1 * 100 + H2 * 10 + H3 + H4 / 10 - 360
el = V1 * 100 + V2 * 10 + V3 + V4 / 10 - 360
The PH and PV values in the response packet reflect the settings of
the rotator controller. The Rot2Prog supports the following
resolutions (always the same for azimuth and elevation):
1 pulse/deg: PH=0x01, PV=0x01
0.5 pulse/deg: PH=0x02, PV=0x02
0.25 pulse/deg: PH=0x04, PV=0x04
STOP COMMAND
------------
The stop command stops the rotator immediately in the current
position and returns the current position. (The position returned does
not seem to be entirely correct, often off by a degree or two.)
The H1-H4, PH, V1-V4 and PV fields are ignored, so only the S, K and
END fields are used. E.g.:
Command:
-----------------------------------------------------------------
| S | H1 | H2 | H3 | H4 | PH | V1 | V2 | V3 | V4 | PV | K | END |
-----------------------------------------------------------------
| 57| 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 0F| 20 | (hex)
-----------------------------------------------------------------
Rotator stops
Response:
-------------------------------------------------------------
| S | H1 | H2 | H3 | H4 | PH | V1 | V2 | V3 | V4 | PV | END |
-------------------------------------------------------------
| 57| 03 | 07 | 02 | 05 | 02 | 03 | 09 | 04 | 00 | 02 | 20 | (hex)
-------------------------------------------------------------
az=372.5-360=12.5, el=394.0-360=34.0
PH=PV=0x02 (pulse for each 0.5 deg)
STATUS COMMAND
--------------
The status command returns the current position of the antenna.
The H1-H4, PH, V1-V4 and PV fields are ignored, so only the S, K and
END fields are used. E.g.:
Command:
-----------------------------------------------------------------
| S | H1 | H2 | H3 | H4 | PH | V1 | V2 | V3 | V4 | PV | K | END |
-----------------------------------------------------------------
| 57| 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 1F| 20 | (hex)
-----------------------------------------------------------------
Response:
-------------------------------------------------------------
| S | H1 | H2 | H3 | H4 | PH | V1 | V2 | V3 | V4 | PV | END |
-------------------------------------------------------------
| 57| 03 | 07 | 02 | 05 | 02 | 03 | 09 | 04 | 00 | 02 | 20 | (hex)
-------------------------------------------------------------
az=372.5-360=12.5, el=394.0-360=34.0
PH=PV=0x02 (pulse for each 0.5 deg)
Status commands can be issued while the rotator is moving and will
always return the current position.
SET COMMAND
-----------
The set command tells the rotator to turn to a specific
position. The controller does not send a response to this command.
Azimuth and elevation is calculated as number of pulses, with a +360
degree offset (so that negative position can be encoded with positive
numbers).
H = PH * (360 + az)
V = PV * (360 + el)
H1-H4 and V1-V4 are these numbers encoded as ASCII (0x30-0x39,
i.e. '0'-'9').
E.g., when pointing to azimuth 123.5, elevation 77.0 when the rotator
sends one pulse per 0.5 degree (PH=PV=2):
H = 2 * (360 + 123.5) = 967
V = 2 * (360 + 77.0) = 874
-----------------------------------------------------------------
| S | H1 | H2 | H3 | H4 | PH | V1 | V2 | V3 | V4 | PV | K | END |
-----------------------------------------------------------------
| 57| 30 | 39 | 36 | 37 | 02 | 30 | 38 | 37 | 34 | 02 | 2F| 20 | (hex)
-----------------------------------------------------------------
The PH and PV values sent are ignored. The values used by the rotator
control unit are set by choosing resolution in the setup
menu. Luckily, these values can be read using the status command.
SEE ALSO
--------
http://alfaradio.ca/downloads/program_info/
AUTHOR
------
Norvald H. Ryeng, LA6YKA
norvald@ryeng.name
2009-05-21