diff --git a/include/hamlib/rotlist.h b/include/hamlib/rotlist.h index a389fb932..e55842cfb 100644 --- a/include/hamlib/rotlist.h +++ b/include/hamlib/rotlist.h @@ -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. diff --git a/spid/spid.c b/spid/spid.c index a41385a9d..a0ac0838b 100644 --- a/spid/spid.c +++ b/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; } diff --git a/spid/spid.h b/spid/spid.h index d6f4e569f..6883f7050 100644 --- a/spid/spid.h +++ b/spid/spid.h @@ -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 */ diff --git a/spid/spid.txt b/spid/spid.txt index 925567ea8..dadc1aa02 100644 --- a/spid/spid.txt +++ b/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