kopia lustrzana https://github.com/Hamlib/Hamlib
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-79ac388436b8Hamlib-1.2.13
rodzic
53dad4d957
commit
dd7e4a17ae
|
@ -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.
|
||||
|
|
154
spid/spid.c
154
spid/spid.c
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
|
108
spid/spid.txt
108
spid/spid.txt
|
@ -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
|
||||
|
|
Ładowanie…
Reference in New Issue