SPID driver extension from Norvald H. Ryeng

This patch extends the SPID driver with support for the Rot1Prog az
rotator controller. It also updates protocol documentation. The driver
has been tested with Rot1Prog and with Rot2Prog for regression.

Submitted by Norvald H. Ryeng <ryeng@users.sourceforge.net).  Tracker
info:  SPID Rot1Prog rotator driver - ID: 3167384

git-svn-id: https://hamlib.svn.sourceforge.net/svnroot/hamlib/trunk@3027 7ae35d74-ebe9-4afe-98af-79ac388436b8
Hamlib-1.2.13
Nate Bargmann, N0NB 2011-01-29 14:31:11 +00:00
rodzic 53dad4d957
commit dd7e4a17ae
4 zmienionych plików z 218 dodań i 51 usunięć

Wyświetl plik

@ -183,7 +183,9 @@
*/
#define ROT_SPID 9
#define ROT_BACKEND_SPID "spid"
#define ROT_MODEL_SPID ROT_MAKE_MODEL(ROT_SPID, 1)
#define ROT_MODEL_SPID_ROT1PROG ROT_MAKE_MODEL(ROT_SPID, 1)
#define ROT_MODEL_SPID_ROT2PROG ROT_MAKE_MODEL(ROT_SPID, 2)
/*! \def ROT_MODEL_M2
* \brief A macro that returns the model number of the M2 backend.

Wyświetl plik

@ -1,6 +1,6 @@
/*
* Hamlib Rotator backend - SPID Rot2Prog
* Copyright (c) 2009 by Norvald H. Ryeng, LA6YKA
* Hamlib Rotator backend - SPID Rot1Prog & Rot2Prog
* Copyright (c) 2009-2011 by Norvald H. Ryeng, LA6YKA
*
*
* This library is free software; you can redistribute it and/or modify
@ -39,30 +39,32 @@
#define TOK_AZRES 1
#define TOK_ELRES 2
struct spid_priv_data {
struct spid_rot2prog_priv_data {
int az_resolution;
int el_resolution;
};
static int spid_rot_init(ROT *rot)
{
struct spid_priv_data *priv;
struct spid_rot2prog_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;
if (rot->caps->rot_model == ROT_MODEL_SPID_ROT2PROG) {
priv = (struct spid_rot2prog_priv_data*)malloc(sizeof(struct spid_rot2prog_priv_data));
if (!priv) {
return -RIG_ENOMEM;
}
rot->state.priv = (void*)priv;
priv->az_resolution = 0;
priv->el_resolution = 0;
}
rot->state.priv = (void*)priv;
priv->az_resolution = 0;
priv->el_resolution = 0;
return RIG_OK;
}
@ -73,7 +75,7 @@ static int spid_rot_cleanup(ROT *rot)
if (!rot)
return -RIG_EINVAL;
if (rot->state.priv)
if (rot->state.priv && rot->caps->rot_model == ROT_MODEL_SPID_ROT2PROG)
free(rot->state.priv);
rot->state.priv = NULL;
@ -82,10 +84,13 @@ static int spid_rot_cleanup(ROT *rot)
static int spid_get_conf(ROT *rot, token_t token, char *val)
{
struct spid_priv_data *priv = (struct spid_priv_data*)rot->state.priv;
struct spid_rot2prog_priv_data *priv = (struct spid_rot2prog_priv_data*)rot->state.priv;
rig_debug(RIG_DEBUG_TRACE, "%s called %d\n", __FUNCTION__, token);
if (rot->caps->rot_model != ROT_MODEL_SPID_ROT2PROG)
return -RIG_EINVAL;
switch(token) {
case TOK_AZRES:
sprintf(val, "%d", priv->az_resolution);
@ -101,10 +106,13 @@ static int spid_get_conf(ROT *rot, token_t token, char *val)
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;
struct spid_rot2prog_priv_data *priv = (struct spid_rot2prog_priv_data*)rot->state.priv;
rig_debug(RIG_DEBUG_TRACE, "%s called %d %s\n", __FUNCTION__, token, val);
if (rot->caps->rot_model != ROT_MODEL_SPID_ROT2PROG)
return -RIG_EINVAL;
switch(token) {
case TOK_AZRES:
priv->az_resolution = atoi(val);
@ -118,10 +126,43 @@ static int spid_set_conf(ROT *rot, token_t token, const char *val)
return RIG_OK;
}
static int spid_rot_set_position(ROT *rot, azimuth_t az, elevation_t el)
static int spid_rot1prog_rot_set_position(ROT *rot, azimuth_t az, elevation_t el)
{
struct rot_state *rs = &rot->state;
struct spid_priv_data *priv = (struct spid_priv_data*)rs->priv;
int retval;
char cmdstr[13];
unsigned int u_az;
rig_debug(RIG_DEBUG_TRACE, "%s called: %f %f\n", __FUNCTION__, az, el);
u_az = 360 + az;
cmdstr[0] = 0x57; /* S */
cmdstr[1] = 0x30 + u_az/100; /* H1 */
cmdstr[2] = 0x30 + (u_az % 100) / 10; /* H2 */
cmdstr[3] = 0x30 + (u_az % 10); /* H3 */
cmdstr[4] = 0x30; /* H4 */
cmdstr[5] = 0x00; /* PH */
cmdstr[6] = 0x00; /* V1 */
cmdstr[7] = 0x00; /* V2 */
cmdstr[8] = 0x00; /* V3 */
cmdstr[9] = 0x00; /* V4 */
cmdstr[10] = 0x00; /* PV */
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_rot2prog_rot_set_position(ROT *rot, azimuth_t az, elevation_t el)
{
struct rot_state *rs = &rot->state;
struct spid_rot2prog_priv_data *priv = (struct spid_rot2prog_priv_data*)rs->priv;
int retval;
int retry_read = 0;
char cmdstr[13];
@ -187,7 +228,12 @@ static int spid_rot_get_position(ROT *rot, azimuth_t *az, elevation_t *el)
}
memset(posbuf, 0, 12);
retval = read_block(&rs->rotport, posbuf, 12);
if (rot->caps->rot_model == ROT_MODEL_SPID_ROT1PROG)
retval = read_block(&rs->rotport, posbuf, 5);
else if (rot->caps->rot_model == ROT_MODEL_SPID_ROT2PROG)
retval = read_block(&rs->rotport, posbuf, 12);
else
retval = -RIG_EINVAL;
} while (retval < 0 && retry_read++ < rot->state.rotport.retry);
if (retval < 0)
return retval;
@ -195,14 +241,18 @@ static int spid_rot_get_position(ROT *rot, azimuth_t *az, elevation_t *el)
*az = posbuf[1] * 100;
*az += posbuf[2] * 10;
*az += posbuf[3];
*az += posbuf[4] / 10.0;
if (rot->caps->rot_model == ROT_MODEL_SPID_ROT2PROG)
*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;
*el = 0.0;
if (rot->caps->rot_model == ROT_MODEL_SPID_ROT2PROG) {
*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);
@ -226,7 +276,10 @@ static int spid_rot_stop(ROT *rot)
}
memset(posbuf, 0, 12);
retval = read_block(&rs->rotport, posbuf, 12);
if (rot->caps->rot_model == ROT_MODEL_SPID_ROT1PROG)
retval = read_block(&rs->rotport, posbuf, 5);
else if (rot->caps->rot_model == ROT_MODEL_SPID_ROT2PROG)
retval = read_block(&rs->rotport, posbuf, 12);
} while (retval < 0 && retry_read++ < rot->state.rotport.retry);
if (retval < 0)
return retval;
@ -244,14 +297,50 @@ const struct confparams spid_cfg_params[] = {
{ RIG_CONF_END, NULL, }
};
const struct rot_caps spid_rot_caps = {
.rot_model = ROT_MODEL_SPID,
.model_name = "Rot2Prog",
const struct rot_caps spid_rot1prog_rot_caps = {
.rot_model = ROT_MODEL_SPID_ROT1PROG,
.model_name = "Rot1Prog",
.mfg_name = "SPID",
.version = "0.2",
.version = "1.0",
.copyright = "LGPL",
.status = RIG_STATUS_STABLE,
.rot_type = ROT_TYPE_OTHER,
.rot_type = ROT_TYPE_AZIMUTH,
.port_type = RIG_PORT_SERIAL,
.serial_rate_min = 1200,
.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 = 0.0,
.max_el = 0.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_rot1prog_rot_set_position,
.stop = spid_rot_stop,
};
const struct rot_caps spid_rot2prog_rot_caps = {
.rot_model = ROT_MODEL_SPID_ROT2PROG,
.model_name = "Rot2Prog",
.mfg_name = "SPID",
.version = "1.0",
.copyright = "LGPL",
.status = RIG_STATUS_STABLE,
.rot_type = ROT_TYPE_AZEL,
.port_type = RIG_PORT_SERIAL,
.serial_rate_min = 600,
.serial_rate_max = 600,
@ -276,7 +365,7 @@ const struct rot_caps spid_rot_caps = {
.rot_init = spid_rot_init,
.rot_cleanup = spid_rot_cleanup,
.get_position = spid_rot_get_position,
.set_position = spid_rot_set_position,
.set_position = spid_rot2prog_rot_set_position,
.stop = spid_rot_stop,
};
@ -284,7 +373,8 @@ DECLARE_INITROT_BACKEND(spid)
{
rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __FUNCTION__);
rot_register(&spid_rot_caps);
rot_register(&spid_rot1prog_rot_caps);
rot_register(&spid_rot2prog_rot_caps);
return RIG_OK;
}

Wyświetl plik

@ -23,6 +23,7 @@
#ifndef _ROT_SPID_H
#define _ROT_SPID_H 1
extern const struct rot_caps spid_rot_caps;
extern const struct rot_caps spid_rot1prog_rot_caps;
extern const struct rot_caps spid_rot2prog_rot_caps;
#endif /* _ROT_SPID_H */

Wyświetl plik

@ -1,10 +1,11 @@
SPID Rot2Prog Protocol
======================
SPID Rot1Prog & 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.
This is an attempt at documenting the protocol of the Rot1Prog and
Rot2Prog rotator controller from SPID Elektronik (spid@alpha.pl).
Rot1Prog controls only azimuth, while Rot2Prog controls both azimuth and
elevation.
@ -17,11 +18,11 @@ 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.)
port. Communication parameters are 1200 bps (Rot1Prog) or 600 bps
(Rot2Prog), 8 bits, no parity and 1 stop bit.
All commands are issued as 13 byte packets, and responses are received
as 12 byte packets.
as 5 byte packets (Rot1Prog) or 12 byte packets (Rot2Prog).
@ -55,18 +56,44 @@ 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).
Rot1Prog does not control elevation and does not support different
resolutions, so V1-V4, PH and PV are set to 0x00. Also, since only whole
degrees are supported, H4 is always set to 0x30 (0 tenths of degrees).
RESPONSE PACKETS
----------------
Response packets are 12 byte long.
Rot1Prog response packets are 5 bytes long.
Byte: 0 1 2 3 4
--------------------------
Field: | S | H1 | H2 | H3 | END |
--------------------------
Value: 57 0x 0x 0x 20 (hex)
S: Start byte. This is always 0x57 ('W')
H1-H3: Azimuth as byte values
END: End byte. This is always 0x20 (space)
Positions are decoded using the following formula:
az = H1 * 100 + H2 * 10 + H3 - 360
Rot2Prog response packets are 12 bytes 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)
Value: 57 0x 0x 0x 0x 0x 0x 0x 0x 0x 0x 20 (hex)
S: Start byte. This is always 0x57 ('W')
@ -117,7 +144,15 @@ END fields are used. E.g.:
-----------------------------------------------------------------
Rotator stops
Response:
Rot1Prog response:
--------------------------
| S | H1 | H2 | H3 | END |
--------------------------
| 57| 03 | 07 | 02 | 20 | (hex)
--------------------------
az=372-360=12
Rot2Prog response:
-------------------------------------------------------------
| S | H1 | H2 | H3 | H4 | PH | V1 | V2 | V3 | V4 | PV | END |
-------------------------------------------------------------
@ -143,7 +178,15 @@ END fields are used. E.g.:
| 57| 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 1F| 20 | (hex)
-----------------------------------------------------------------
Response:
Rot1Prog response:
--------------------------
| S | H1 | H2 | H3 | END |
--------------------------
| 57| 03 | 07 | 02 | 20 | (hex)
--------------------------
az=372-360=12
Rot2Prog response:
-------------------------------------------------------------
| S | H1 | H2 | H3 | H4 | PH | V1 | V2 | V3 | V4 | PV | END |
-------------------------------------------------------------
@ -168,6 +211,12 @@ Azimuth and elevation is calculated as number of pulses, with a +360
degree offset (so that negative position can be encoded with positive
numbers).
Rot1Prog supports only whole degree positions:
H = 360 + az
Rot2Prog supports different resolutions:
H = PH * (360 + az)
V = PV * (360 + el)
@ -175,9 +224,20 @@ numbers).
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):
E.g., when pointing a Rot1Prog to azimuth 123:
H = 360 + 123 = 483
-----------------------------------------------------------------
| S | H1 | H2 | H3 | H4 | PH | V1 | V2 | V3 | V4 | PV | K | END |
-----------------------------------------------------------------
| 57| 34 | 38 | 33 | 30 | 00 | 00 | 00 | 00 | 00 | 00 | 2F| 20 | (hex)
-----------------------------------------------------------------
Note that H4 is not used.
E.g., when pointing a Rot2Prog 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
@ -191,7 +251,20 @@ sends one pulse per 0.5 degree (PH=PV=2):
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.
menu. Luckily, these values can be read using the status command
(Rot2Prog only).
Note that H1-H4 is interpreted differently by Rot1Prog and Rot2Prog in
the set command:
Rot1Prog Rot2Prog
H1 *100 *1000
H2 *10 *100
H3 *1 *10
H4 *0 *1
Rot1Prog does not use H4 and uses H1 for houndres, while Rot2Prog uses
H4 for ones and H1 for thousands.
@ -200,9 +273,10 @@ SEE ALSO
http://alfaradio.ca/downloads/program_info/
AUTHOR
------
Norvald H. Ryeng, LA6YKA
norvald@ryeng.name
2009-05-21
2009-05-21, updated 2011-01-29