/*  rc2k.c  **  1.20.91  ** (formerly sabust1.c  **  5.31.91  **  Revisions
include support for the new SA_MISC_CMND, SA_AZ_CMND modified to and renamed 
SA_JOG_CMND, and more baud rate options.)  This program has been designed to 
give the program developer a tool to use to exercise the rc 1000.  The user 
can send commands to the positioner, see the actual bytes which are 
transmitted, and view the reply received back from the positioner. 

The RC 2000 uses the SA Bus protocol to communicate with a personal computer.  
The SA Bus protocol was developed by Scientific Atlanta for control of
communications equipment.  The protocol defines the physical, electrical, and 
logical operation of the interface.

When execution begins, the user is prompted to specify the baud rate and the 
comm port.  The operation of this program is pretty self explanatory.

The user is presented with three windows, a menu window - where the user is 
prompted to enter commands to the rc 2000, a transmit window - which displays 
all bytes sent to the positioner in both hex and ascii, if possible, and a 
receive window - which displays all bytes received from the rc 2000 in a 
format identical to that of the transmit window.

This program uses the C ASYNCH MANAGER library (version 2.0) of serial 
routines available from BLAISE COMPUTING INC., 2560 Ninth Street, Suite 316, 
Berkeley, CA. 94710, (415) 540-5441.  This program was compiled with the 
BORLAND TURBO C compiler, version 2.00.  The project file is rc2k.prj. */

#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <ctype.h>
#include <dos.h>
#include <asynch_2.h>

/* Here are the global literals. */

#define MENU_TOP_ROW         2    /* the window starting and ending rows */
#define MENU_BOTTOM_ROW      9
#define XMIT_TOP_ROW         11
#define XMIT_BOTTOM_ROW      15
#define RCV_TOP_ROW          17
#define RCV_BOTTOM_ROW       25

/* The following literals define the SA bus command codes. */

#define SA_QUERY_ID_CMND     0x30
#define SA_QUERY_CMND        0x31
#define SA_QUERY_NAME_CMND   0x35
#define SA_AUTO_CMND         0x32
#define SA_JOG_CMND          0x33
#define SA_POL_CMND          0x34
#define SA_MISC_CMND         0x36

#define STX                  0x2
#define ETX                  0x3
#define SAT_NAME_LNGTH       10

/* Here are the global variables. */

    int com_port, baud_rate;      /* These 2 variables hold the selected comm 
         port (1 or 2), and the baud rate. */

    struct text_info xmit_window_info, rcv_window_info;    /* These two 
         structures are used to hold the current status of the receive and 
         transmit windows.  This allows those windows to be restored when 
         there is data to be written to those windows. */

    int rcv_etx_cnt, rcv_etx_found = 0;     /* These variables implement a 
         system which generates a newline in the receive window after an ETX 
         character is received.  The idea is to generate a newline when the 
         second character after the ETX is received.  When an ETX character 
         is received, 'found is set to one and 'cnt is reset to zero.  As 
         succeeding characters are received, 'found is still 1 and 'cnt is 
         incremented.  When 'cnt equals 2 a newline performed and 'found is
         reset to 0. */ 

/* The following routine restores a window based on a text_info data
structure.  The text_info structure is returned by the gettextinfo function.
*/

void window_restore(struct text_info *window_info) {

    /* Restore the window. */

    window( window_info -> winleft,
            window_info -> wintop,
            window_info -> winright,
            window_info -> winbottom);

    /* Restore the curser position. */
    gotoxy( window_info -> curx, window_info -> cury);

    return;
}   /* end - window_restore */

/* The following routine swaps windows.  The curr_window text_info structure
will receive the current window status, and the new_window text_info
structure holds the parameters of the new window which will be created. */

void window_swap_curr_new( struct text_info *curr_window,
                           struct text_info *new_window ) {

    gettextinfo( curr_window );
    window_restore( new_window );

    return;
}   /* end - window_swap_curr_new */

/* The following routine checks to see if the cursor is on the last row of
the currently active window.  If the cursor is on the bottom line, a 1 is
returned, otherwise 0 is returned. */

int window_cursor_on_bottom_row() {

    struct text_info win;

    gettextinfo( &win );

    if( (win.wintop + wherey()) > win.winbottom) return 1;
    else return 0;
}   /* end - window_cursor_on_bottom_row */

/* The following routine scrolls the current window upward, and leaves the
cursor on the bottom row in column 1.  The top row scrolls off of the top of
screen. */

void window_scroll_up() {

    struct text_info win;

    /* Get the current window parameters. */
    gettextinfo(&win);

    /* Move the text up one line. */
    movetext( 1, win.wintop+1, 80, win.winbottom, 1, win.wintop);

    /* Position the cursor on the bottom row of the window and clear that 
    row. */

    gotoxy(1, win.winbottom - win.wintop + 1);
    clreol();
    return;
}

/* The following routine generates a newline in a turbo c window enviroment.  
If the cursor is not at the bottom row of the window, a printf("\n") is 
performed, otherwise a window_scroll_up is performed. */

void window_newline() {

    struct text_info win;

    /* Get the current window parameters. */
    gettextinfo(&win);

    if (1 == window_cursor_on_bottom_row()) window_scroll_up();
    else printf("\n");

    return;
}   /* end - window_newline */

/* The following routine clears the screen from the current cursor position
to the end of the current window.  The cursor position is changed and then
returned to the original position. */

void clreos() {

    struct text_info info;
    unsigned char row;

    /* Get the existing window parameters. */
    gettextinfo( &info );

    /* Clear to the end of the line that line which contains the cursor. */
    clreol();

    /* Move the cursor down the window, clearing each line along the way. */

    for (row = info.wintop + info.cury; row <= info.winbottom ;row++) {
         gotoxy( 1, wherey() + 1);
         clreol();
    }

    /* Restore the cursor. */
    gotoxy( (int) info.curx, (int) info.cury );

    return;

}   /* end - clreos */

/* This routine displays a rc 2000 command string.  The data is displayed in
a hexadecimal format along with the ascii representation (if applicable).
The input parameters are a pointer to the text_info structure of the window
that will display the characters, a pointer to the string to be displayed and
the length of the string.  On exit, the window which was active on entry is
restored. */

void display_xmit_data( struct text_info *xmit_window,
                        unsigned char *str, unsigned int lngth) {
    unsigned int u;
    struct text_info prev_window;

    window_swap_curr_new( &prev_window, xmit_window);

    /* Display the characters. */

    for (u=0; u < lngth ;u++) {

         /* See if a new line is needed. */

         if (((u == 0) && (wherex() != 1)) || (wherex() > 70))
          window_newline();

         if (0 != isprint( (int) str[u]))
          printf("%02x '%c'  ", str[u], (char) str[u] );

         else printf("%02x ...  ", str[u] );
    }

    /* Restore the previous window. */
    window_swap_curr_new( xmit_window, &prev_window);

    return;
}   /* end - display_xmit_data */

/* The following routine is called to check for the presence of and display 
data received by the serial port.  This routine assumes that the receive 
window is not active when it is called.  */

void display_rcv_data() {

    char rcv_char;      /* holds the received character */
    int size;      /* holds the number of characters remaining in the input 
              queue */
    unsigned int status;     /* When a call is made to read a character from 
              the input queue, bits in the status word can be set to indicate 
              that some kind of an error has occurred.  Here's the bits that 
              can be set and their meanings....

              0x0001 - input lost - receive characters lost because the queue 
                   was not read quickly enough.
              0x0002 - internal error - the serial port interrupt service 
                   routine has lost its way.
              0x0010 - parity error
              0x0020 - overrun error - the uart detected an overrun, a 
                   character was lost, something maybe disabling the uart
                   interrupt for long periods.
              0x0040 - framing error - the stop bits were not received as 
                   expected.
              0x0080 - break - the line was held in the spacing condition for
                   at least the amount of time required to send one 
                   character.     */

    int window_swapped = 0;  /* This variable will be set to 1 when the 
              receive window becomes current (in response to a valid 
              character received).  On exit, if this variable is set, the 
              window which was active on entry must be restored. */

    struct text_info prev_window;      /* used to store the window which was 
              active on entry */

    int err_code;  /* holds the return value of the functions that read the
              serial port.  0 means that characters were read without error
              and 10 means no data was available. */

    /* Stay in this loop as long receive data (or status errors) are
    available. */

    for (;;) {

         /* See if any characters are ready. */
         err_code = rdch_a2( com_port, &rcv_char, &size, &status);

         /* Exit loop if there are no characters ready and no status errors 
         have occurred. */ 

         if ((err_code == 10) && (0 == (status & STATUS_ERR))) break;

         /* See if the window needs to be swapped. */

         if (window_swapped == 0) {
              window_swap_curr_new( &prev_window, &rcv_window_info);
              window_swapped=1;
         }

         /* See if a status error has occurred. */

         if (status & STATUS_ERR) {
              window_newline();
              printf("Status Error: %04x  ");
         }

         /* See if a character is ready. */

         else if (err_code == 0) {

              /* See if a newline is needed because of the reception of a
              previous ETX. */

              if (rcv_etx_found != 0) {
                   rcv_etx_cnt++;
                   if (rcv_etx_cnt == 2) {
                        rcv_etx_found = 0;
                        window_newline();
                   }
              }

              /* See if a newline is needed because the current line is full. 
              */

              if (wherex() > 70) window_newline();

              /* Display the characters. */

              if (0 != isprint( (int) rcv_char))
               printf("%02x '%c'  ", (unsigned char) rcv_char, rcv_char );

              else printf("%02x ...  ", (unsigned char) rcv_char );

              /* See if an ETX was received. */
              if (rcv_char == ETX) { rcv_etx_found = 1; rcv_etx_cnt = 0; }
         }
    }    /* end - for */

    /* If the window was swapped, restore the window which was active when 
    this routine was called. */

    if (window_swapped != 0)
     window_swap_curr_new( &rcv_window_info, &prev_window);

    return;
}   /* end - display_rcv_data */

/* The following routine prompts the user to enter an alphabetic value and
displays receive data as it arrives in the receive window.  The input
parameters are a string to prompt the user with and a character that sets the 
limit for the 'greatest' alphabetic value which will be accepted.  If the 
limit is 'G' then A thru G will be accepted.  The return value is the 
character which was entered.  Lower case letters are accepted, but are 
converted to upper case on return. */ 

char prompt_alpha_display_rcv( char *prompt_str, char limit) {

    char ch;

    printf("%s", prompt_str);

    for (;;) {

         /* See if a character has been hit. */

         if (0 != kbhit()) {

              /* A character is ready, read the character. */
              ch = (char) getch();

              /* See if the character is alphabetic. */
              if (0 != isalpha(ch)) {

                   /* The character is alphabetic, if it is lower case
                   convert it to upper case. */

                   if (0 != islower(ch)) ch = (char) toupper(ch);

                   /* See if the character is within the limits specified.
                   If so, display the character and return. */

                   if ((ch >= 'A') && (ch <= limit)) {
                        printf( "%c\n", ch);
                        return ch;
                   }
              }
         }

         /* Call a routine to display any receive data. */
         display_rcv_data();

    }    /* end - for */
}   /* end - prompt_alpha_display_rcv */

/* The following routine prompts the user to enter an alphabetic value.  The 
input parameters are a string to prompt the user with and a character that 
sets the limit for the 'greatest' alphabetic value which will be accepted.  
If the limit is 'G' then A thru G will be accepted.  The return value is the
character which was entered.  Lower case letters are accepted, but are 
converted to upper case on return. */ 

char prompt_alpha( char *prompt_str, char limit) {

    char ch;

    printf("%s", prompt_str);

    for (;;) {

         /* See if a character has been hit. */

         if (0 != kbhit()) {

              /* A character is ready, read the character. */
              ch = (char) getch();

              /* See if the character is alphabetic. */
              if (0 != isalpha(ch)) {

                   /* The character is alphabetic, if it is lower case
                   convert it to upper case. */

                   if (0 != islower(ch)) ch = (char) toupper(ch);

                   /* See if the character is within the limits specified.
                   If so, display the character and return. */

                   if ((ch >= 'A') && (ch <= limit)) {
                        printf( "%c\n", ch);
                        return ch;
                   }
              }
         }
    }    /* end - for */
}   /* end - prompt_alpha */

/* The following routine prompts the user for an unsigned integer.  The
input parameter is a string which will be used to prompt the user and the
return value is the numeric value entered by the user. */

unsigned int prompt_uint( char *prompt_str) {

    unsigned int u;

    printf( "%s", prompt_str);
    scanf( "%u", &u);
    return u;
}   /* end - prompt_uint */

/* The following routine prompts the user for a character.  The input
parameter is the string which will prompt the user and the return value is
the character entered by the user.  If the user enters an alphabetic value
convert it to upper case. */

char prompt_char( char *prompt_str) {

    char ch;

    printf( "%s", prompt_str);
    ch = (char) getche();

    /* If an alphabetic character was entered, convert it to upper case. */
    if (0 != isalpha( ch )) ch = (char) toupper( ch );

    if (ch != '\n') printf("\n");
    return ch;
}   /* end - char_prompt */

/* The following routine calculates an exclusive-or checksum over a character
array.  The checksum is calculated on a byte by byte basis. */

unsigned char calc_xor_checksum( char *str, unsigned int lngth) {

    unsigned char sum;
    unsigned int u;

    if (lngth == 0) sum = 0;

    else {

         sum = str[0];
         for (u=1; u<lngth ;u++) sum = sum ^ (unsigned char) str[u];
    }

    return sum;
}   /* end - calc_xor_chsum */

/* The following routines format commands for the rc 2000 antenna positioner.
Certains aspects of these routines are similar.  Those will be discussed
here.  All of the routines accept the device address (49 - 111 decimal, 31 -
6f hex), a pointer to a string which will receive the command (the string
must already be allocated when this routine is called), and a pointer to a
variable which will receive the length of the formatted command.

The commands have similar formats.  The commands will be all begin with the
following header bytes: (STX  Address  Command code).  Each command will end
with the following bytes: (ETX  Checksum).  The checksum is an exclusive-or
sum over all bytes in the message, STX thru ETX inclusive.  */

/* The following function formats the query_id command.  In response to this
command the rc 2000 should return a string containing its model number and
software version. */

void format_query_id_cmnd( unsigned int addr,
                           unsigned char *str, unsigned int *lngth) {

    /* Initialize the length. */
    *lngth = 0;

    /* Format the command header. */

    str[ (*lngth)++ ] = STX;
    str[ (*lngth)++ ] = (unsigned char) addr;
    str[ (*lngth)++ ] = SA_QUERY_ID_CMND;

    /* Append the trailing bytes onto the command. */

    str[ (*lngth)++ ] = ETX;
    str[ *lngth ] = calc_xor_checksum( str, (*lngth));
    (*lngth)++;

    return;
}   /* end - format_query_id_cmnd */

/* The following function formats the query command.  This commands instructs 
the rc 2000 to send a message containing its current status back to the host. 
*/

void format_query_cmnd( unsigned int addr, 
                        unsigned char *str, unsigned int *lngth) {

    /* Initialize the length. */
    *lngth = 0;

    /* Format the command header. */

    str[ (*lngth)++ ] = STX;
    str[ (*lngth)++ ] = (unsigned char) addr;
    str[ (*lngth)++ ] = SA_QUERY_CMND;

    /* Append the trailing bytes onto the command. */

    str[ (*lngth)++ ] = ETX;
    str[ *lngth ] = calc_xor_checksum( str, (*lngth));
    (*lngth)++;

    return;
}   /* end - format_query_cmnd */

/* This function formats the query_name command.  This command instructs the
rc 2000 to upload an entry in it's internal satellite data list stored in
non-volatile RAM.  This lets the host find out which satellites are available
for use with the auto move command.  The sat_index input parameter specifies
which entry in the list to upload.  Sat_index equal to one specifies the
first entry in the list.  If an entry does not exists, the rci 2000 will
respond with the NAK reply. */

void format_query_name_cmnd( unsigned int addr, unsigned int sat_index,
                             unsigned char *str, unsigned int *lngth) {

    /* Initialize the length. */
    *lngth = 0;

    /* Format the command header. */

    str[ (*lngth)++ ] = STX;
    str[ (*lngth)++ ] = (unsigned char) addr;
    str[ (*lngth)++ ] = SA_QUERY_NAME_CMND;

    /* Append the satellite data index onto the message. */
    sprintf( &str[*lngth], "%02u", sat_index );
    *lngth += 2;

    /* Append the trailing bytes onto the command. */

    str[ (*lngth)++ ] = ETX;
    str[ *lngth ] = calc_xor_checksum( str, (*lngth));
    (*lngth)++;

    return;
}   /* end - format_query_name_cmnd */

/* The following function formats the rc 2000 auto command.  This command
specifies the satellite and polarization that the rc 2000 should move to.
The valid polarization codes are 'V', 'H', or ' ' (for no polarization
adjustment). */

void format_auto_cmnd( unsigned int addr, char pol, char *sat_name,
                       unsigned char *str, unsigned int *lngth) {

    int i;

    /* Initialize the length. */
    *lngth = 0;

    /* Format the command header. */

    str[ (*lngth)++ ] = STX;
    str[ (*lngth)++ ] = (unsigned char) addr;
    str[ (*lngth)++ ] = SA_AUTO_CMND;

    /* Append the polarization character to the command. */
    str[ (*lngth)++ ] = (unsigned char) pol;

    /* Append the satellite name onto the command.  Truncate or pad the
    string to be the desired length.  Convert to upper case. */

    for (i=0; i < SAT_NAME_LNGTH ;i++) {

         if (i < strlen( sat_name )) {

              /* Convert the string to upper case. */

              if (0 != isalpha( sat_name[i] ))
               sat_name[i] = (char) toupper(sat_name[i]);

              str[ (*lngth)++ ] = (unsigned char) sat_name[i];
         }

         else str[ (*lngth)++ ] = (unsigned char) ' ';
    }

    /* Append the trailing bytes onto the command. */

    str[ (*lngth)++ ] = ETX;
    str[ *lngth ] = calc_xor_checksum( str, (*lngth));
    (*lngth)++;

    return;
}   /* end - format_auto_cmnd */

/* The following command specifies an azimuth or elevation jog.  The input
parameters specify the direction, 'E', 'W', 'D' or 'U', the speed, 'F' or 
'S', and the duration of the jog, 0-9999. */ 

void format_jog_cmnd(unsigned int addr, char direction, char speed, 
                     unsigned int duration,
                     unsigned char *str, unsigned int *lngth) {

    /* Initialize the length. */
    *lngth = 0;

    /* Format the command header. */

    str[ (*lngth)++ ] = STX;
    str[ (*lngth)++ ] = (unsigned char) addr;
    str[ (*lngth)++ ] = SA_JOG_CMND;

    /* Append the polarization and speed onto the command. */

    str[ (*lngth)++ ] = (unsigned char) direction;
    str[ (*lngth)++ ] = (unsigned char) speed;

    /* Append the duration onto the command. */
    sprintf( &str[*lngth], "%04u", duration);
    *lngth += 4;

    /* Append the trailing bytes onto the command. */

    str[ (*lngth)++ ] = ETX;
    str[ *lngth ] = calc_xor_checksum( str, (*lngth));
    (*lngth)++;

    return;
}   /* end - format_jog_cmnd */

/* The following function formats the polarization command.  The pol input 
parameter specifies a polarization command.  If 'H' or 'V' is specified the 
rc 2000 will position the polarotor to the preset H or V polarization of the 
satellite closest to the rc 2000's present azimuth position.  If pol is 'C' a 
clockwise polarization skew is initiated and if pol is 'W' a counter 
clockwise skew is initiated.  For the rc 2000, the two skew commands are self 
timed,  meaning that once initiated, the commands will time out if another 
command is not received.  When first begun, the polarization skew speed will 
be slow.  As skew commands continue to be received and polarization movement
continues in an uninterrupted fashion, the speed will increase. */

void format_pol_cmnd( unsigned int addr, char pol,
                      unsigned char *str, unsigned int *lngth) {

    /* Initialize the length. */
    *lngth = 0;

    /* Format the command header. */

    str[ (*lngth)++ ] = STX;
    str[ (*lngth)++ ] = (unsigned char) addr;
    str[ (*lngth)++ ] = SA_POL_CMND;

    /* Append the polarization code onto the command. */
    str[ (*lngth)++ ] = (unsigned char) pol;

    /* Append the trailing bytes onto the command. */

    str[ (*lngth)++ ] = ETX;
    str[ *lngth ] = calc_xor_checksum( str, (*lngth));
    (*lngth)++;

    return;
}   /* end - format_pol_cmnd */

/* The following routine formats the SA_MISC_CMND.  This command has two 
functions.  It can specify that the azimuth/elevation drive be reset or it 
can disable/enable the autopol function.  The input parameters param1 and 
param2 specify the command.

    param1    param2              meaning

      R         A                 reset azimuth drive
      R         E                 reset elevation drive
      P         N                 autopol enable
      P         F                 autopol disable   */

void format_misc_cmnd(unsigned int addr, char param1, char param2,
                      unsigned char *str, unsigned int *lngth) {

    /* Initialize the length. */
    *lngth = 0;

    /* Format the command header. */

    str[ (*lngth)++ ] = STX;
    str[ (*lngth)++ ] = (unsigned char) addr;
    str[ (*lngth)++ ] = SA_MISC_CMND;

    /* Append the polarization code onto the command. */

    str[ (*lngth)++ ] = (unsigned char) param1;
    str[ (*lngth)++ ] = (unsigned char) param2;

    /* Append the trailing bytes onto the command. */

    str[ (*lngth)++ ] = ETX;
    str[ *lngth ] = calc_xor_checksum( str, (*lngth));
    (*lngth)++;

    return;
}   /* end - format_misc_cmnd */

/* Here is the main line.  This is where things begin. */

void main() {

    char speed_code,    /* This character holds the speed code which will be
              used for azimuth jog operations.  'F' for fast and 'S' for 
              slow. */ 

         select,   /* This variable holds the users menu selection. */

         ch,       /* This variable is used as a general purpose character 
              buffer, used for clearing the keyboard buffer and for selecting 
              the speed code. */

         pol;      /* This variable holds the users selected polarization 
              when an auto command is issued. */

    unsigned int duration,   /* This variable will hold the duration (in 
              milliseconds) for azimuth jog commands.  The user can set the 
              value of this variable via a menu selection. */

         address,  /* This variable holds the device address which will be 
              the target for all commands.  This variable can be set via a 
              menu selection. */

         uint,     /* This variable receives the users entry of duration and 
              address data. */

         lngth;    /* This variable holds the length of formatted commands.
              */

    unsigned char str[100],  /* This string holds formatted commands. */

         sat_name[20];  /* This string holds the satellite name entered by 
              the user when the auto command is selected. */

    int err_code,  /* This variable is initialized to the return value of the
         functions which open and set the parameters of the serial port.  The 
         return values indicate if any errors occurred while configuring the 
         serial system.  See the documentation preceding these calls for the 
         meaning of these error codes. */

         cnt;      /* This variable holds the number of bytes written to the 
              serial port output queue when a command is sent.  This 
              information is not used. */

    POPT option_rec;    /* This structure is used to contain the serial port 
         setup parameters.  This structure is defined in the asynch header 
         file. */

    /* Clear the screen and prompt the user to enter the baud rate and the 
    comm port. */

    clrscr();
    printf("A.1200  B.2400  C.4800  D.9600\n");
    switch (prompt_alpha(
            "Select baud rate - enter selection: ", 'D')) {

         case 'A':
              /* Select 1200 baud. */

              baud_rate = 1200;
              break;

         case 'B':
              /* Select 2400 baud. */

              baud_rate = 2400;
              break;

         case 'C':
              /* Select 4800 baud. */

              baud_rate = 4800;
              break;

         case 'D':
              /* Select 9600 baud. */

              baud_rate = 9600;
              break;
    }

    switch (prompt_alpha(
             "Select the comm port, A.COM1  B.COM2, enter selection: ",
              'B')) {

         case 'A':
              /* Select COM1. */

              com_port = 1;
              break;

         case 'B':
              /* Select COM2. */

              com_port = 2;
              break;
    }

    clrscr();

    /* Open up the comm port, exit on error.  Here are the possible error 
    codes... 0 - port successfully opened, 2 - invalid port number, 4 - 
    invalid parameter, 6 - no serial port found, 9 - port already open, 11 - 
    memory allocation error for the input and output queues. */ 

    if (0 != (err_code=open_a2(com_port,2000,128,0,0))) {
         printf("Open Error %d\n",err_code);
         exit(0);
    }

    /* Set up the comm options. */

    option_rec.baud_rate          = baud_rate;
    option_rec.parity             = EVEN;
    option_rec.data_bits          = 7;
    option_rec.stop_bits          = 1;
    option_rec.remote_flow_ctrl   = OFF;
    option_rec.local_flow_ctrl    = OFF;
    option_rec.bit_trimming       = OFF;
    option_rec.bit_forcing        = OFF;
    option_rec.require_cts        = OFF;
    option_rec.break_time         = 255;

    /* Set the communication port options.  Here are the possible error 
    codes... 0 - no error, 2 - invalid port, 3 - port not open, 14 - invalid 
    baud rate, 24 - invalid parity, 34 - invalid data bit count, 44 - invalid 
    number of stop bits. */

    if (0 != (err_code=setop_a2(com_port,&option_rec))) {
         printf("Set options error %d\n",err_code);
         close_a2(com_port);
         exit(0);
    }

    /* Display the communication parameters, leave it up a while. */

    printf("COMMUNICATIONS PARAMETERS\n\nData bits: 7  Parity: Even"
           "  Stop Bits: 1\nBaud rate: %d  Comm port: %d\n",
           baud_rate,com_port);

    delay(5000);

    /* Clear any pending keystrokes. */
    while (0 != kbhit()) ch = (char) getch();

    /* Clear the screen, and display the headings for each window. */

    clrscr();
    gotoxy( 1, MENU_TOP_ROW - 1);
    printf("MENU");
    gotoxy( 1, XMIT_TOP_ROW - 1);
    printf("TRANSMIT");
    gotoxy( 1, RCV_TOP_ROW - 1);
    printf("RECEIVE");

    /* Setup the windows.  The menu window will be the main window, meaning
    that it is the current window except when the receive or transmit window
    is activated for data display.  After the data is displayed the menu
    window will be restored. */

    window( 1, XMIT_TOP_ROW, 80, XMIT_BOTTOM_ROW);
    gettextinfo( &xmit_window_info);

    window( 1, RCV_TOP_ROW, 80, RCV_BOTTOM_ROW);
    gettextinfo( &rcv_window_info);

    window( 1, MENU_TOP_ROW, 80, MENU_BOTTOM_ROW);

    /* Display the menu. */
         /* 123456789012345678901234567890123456789012345 */
    printf("A.Speed:   B.Duration:      C.Address:     D.Query_Id  ");
    printf("Baud: %d  COM%d\n", baud_rate, com_port);
    printf("E.Query  F.Query_Name  G.Auto  H.Jog_East I.Jog_West  ");
    printf("J.Jog_Down K.Jog_Up\n");
    printf("L.Go_To_Hpol M.Go_To_Vpol  N.Skew_Cw O.Skew_Ccw  ");
    printf("P.Reset_Az Q.Reset_El\n");
    printf("R.Autopol_Enable  S.Autopol_Disable  T.Az_El_Stop  U.Exit\n");

    /* Set up the default values for speed_code, duration, and address. */
    speed_code='F'; duration=2000; address=49;

    /* Display the default values. */

    gotoxy(9, 1); printf("%c",speed_code);
    gotoxy(23,1); printf("%04u",duration);
    gotoxy(39,1); printf("%03u",address);

    /* Here's the big loop.  */

    for (;;) {

         /* Position the cursor to prompt the user for a menu selection,
         clear the window from the cursor on, and prompt the user. */

         gotoxy(1,6); clreos(0);
         select = prompt_alpha_display_rcv("Enter selection (A-U): ", 'U');

         if (select == 'U') break;

         /* Process the user's selection. */
         switch (select) {

              case 'A':
                   /* Prompt the user for the azimuth jog speed. */

                   ch = prompt_char("Enter the speed, F or S: ");

                   if ((ch == 'F') || (ch == 'S')) {
                        speed_code = ch;

                        /* A valid speed code was entered.  Display the new
                        value. */

                        gotoxy(9,1); printf("%c", speed_code);
                   }
                   break;

              case 'B':
                   /* Prompt the user for a duration. */

                   uint = prompt_uint
                    ("Enter the azimuth jog duration (0 - 9999): ");

                    /* See if a valid value was entered. */
                    if (uint <= 9999) {

                        duration = uint;
                        gotoxy( 23, 1); printf("%04u", duration);
                    }
                    break;

              case 'C':
                   /* Prompt the user for an address. */

                   uint = prompt_uint
                    ("Enter the device address (49 - 111): ");

                   /* See if the value is valid, if so, display it. */

                   if ((uint >= 49) && (uint <= 111)) {
                        address = uint;
                        gotoxy( 39, 1); printf("%03u", address);
                   }

                   fflush(stdin);

                   break;

              case 'D':
                   /* Send the query_id command. */

                   format_query_id_cmnd( address, &str[0], &lngth);
                   display_xmit_data( &xmit_window_info, &str[0], lngth);

                   /* Transmit the data. */
                   wrtst_a1( com_port, lngth, str, &cnt);

                   break;

              case 'E':
                   /* Send the query command. */

                   format_query_cmnd( address, &str[0], &lngth);
                   display_xmit_data( &xmit_window_info, &str[0], lngth);

                   /* Transmit the data. */
                   wrtst_a1( com_port, lngth, str, &cnt);

                   break;

              case 'F':
                   /* Send the query_name command. */

                   format_query_name_cmnd( address,
                    prompt_uint("Enter the sat_index: "), &str[0], &lngth);

                   display_xmit_data( &xmit_window_info, &str[0], lngth);

                   /* Transmit the data. */
                   wrtst_a1( com_port, lngth, str, &cnt);

                   break;

              case 'G':
                   /* Send the auto command. */

                   /* Prompt for the polarization code. */

                   switch (prompt_alpha_display_rcv(
                    "Enter polarization code, A.H_pol  B.V_pol  C.None: ",
                    'C')) {

                        case 'A':
                             pol = 'H';
                             break;

                        case 'B':
                             pol = 'V';
                             break;

                        case 'C':
                             pol = ' ';
                             break;
                   }

                   fflush(stdin);

                   /* Prompt for the satellite name. */

                   printf("Enter the satellite name: ");
                   gets( sat_name );

                   format_auto_cmnd( address, pol, sat_name,
                                     &str[0], &lngth);

                   display_xmit_data( &xmit_window_info, &str[0], lngth);

                   /* Transmit the data. */
                   wrtst_a1( com_port, lngth, str, &cnt);

                   break;

              case 'H':
                   /* Send the az jog east command. */

                   format_jog_cmnd( address, 'E', speed_code, duration,
                                    &str[0], &lngth);

                   display_xmit_data( &xmit_window_info, &str[0], lngth);

                   /* Transmit the data. */
                   wrtst_a1( com_port, lngth, str, &cnt);

                   break;

              case 'I':
                   /* Send the az jog west command. */

                   format_jog_cmnd( address, 'W', speed_code, duration,
                                    &str[0], &lngth);

                   display_xmit_data( &xmit_window_info, &str[0], lngth);

                   /* Transmit the data. */
                   wrtst_a1( com_port, lngth, str, &cnt);

                   break;

              case 'J':
                   /* Send the elev jog down command. */

                   format_jog_cmnd( address, 'D', speed_code, duration,
                                    &str[0], &lngth);

                   display_xmit_data( &xmit_window_info, &str[0], lngth);

                   /* Transmit the data. */
                   wrtst_a1( com_port, lngth, str, &cnt);

                   break;

              case 'K':
                   /* Send the elev jog up command. */

                   format_jog_cmnd( address, 'U', speed_code, duration,
                                    &str[0], &lngth);

                   display_xmit_data( &xmit_window_info, &str[0], lngth);

                   /* Transmit the data. */
                   wrtst_a1( com_port, lngth, str, &cnt);

                   break;

              case 'L':
                   /* Send the goto hpol command. */

                   format_pol_cmnd( address, 'H', &str[0], &lngth);
                   display_xmit_data( &xmit_window_info, &str[0], lngth);

                   /* Transmit the data. */
                   wrtst_a1( com_port, lngth, str, &cnt);

                   break;

              case 'M':
                   /* Send the goto vpol command. */

                   format_pol_cmnd( address, 'V', &str[0], &lngth);
                   display_xmit_data( &xmit_window_info, &str[0], lngth);

                   /* Transmit the data. */
                   wrtst_a1( com_port, lngth, str, &cnt);

                   break;

              case 'N':
                   /* Send the skew clockwise command. */

                   format_pol_cmnd( address, 'C', &str[0], &lngth);
                   display_xmit_data( &xmit_window_info, &str[0], lngth);

                   /* Transmit the data. */
                   wrtst_a1( com_port, lngth, str, &cnt);

                   break;

              case 'O':
                   /* Send the skew counter clockwise command. */

                   format_pol_cmnd( address, 'W', &str[0], &lngth);
                   display_xmit_data( &xmit_window_info, &str[0], lngth);

                   /* Transmit the data. */
                   wrtst_a1( com_port, lngth, str, &cnt);

                   break;

              case 'P':
                   /* Send reset az miscellaneous command. */

                   format_misc_cmnd( address, 'R', 'A', &str[0], &lngth);
                   display_xmit_data( &xmit_window_info, &str[0], lngth);

                   /* Transmit the data. */
                   wrtst_a1( com_port, lngth, str, &cnt);

                   break;

              case 'Q':
                   /* Send reset el miscellaneous command. */

                   format_misc_cmnd( address, 'R', 'E', &str[0], &lngth);
                   display_xmit_data( &xmit_window_info, &str[0], lngth);

                   /* Transmit the data. */
                   wrtst_a1( com_port, lngth, str, &cnt);

                   break;

              case 'R':
                   /* Send autopol enable miscellaneous command. */

                   format_misc_cmnd( address, 'P', 'N', &str[0], &lngth);
                   display_xmit_data( &xmit_window_info, &str[0], lngth);

                   /* Transmit the data. */
                   wrtst_a1( com_port, lngth, str, &cnt);

                   break;

              case 'S':
                   /* Send autopol disable miscellaneous command. */

                   format_misc_cmnd( address, 'P', 'F', &str[0], &lngth);
                   display_xmit_data( &xmit_window_info, &str[0], lngth);

                   /* Transmit the data. */
                   wrtst_a1( com_port, lngth, str, &cnt);

                   break;

              case 'T':
                   /* Send the az/el stop command. */

                   format_jog_cmnd( address, 'X', speed_code, duration,
                                    &str[0], &lngth);

                   display_xmit_data( &xmit_window_info, &str[0], lngth);

                   /* Transmit the data. */
                   wrtst_a1( com_port, lngth, str, &cnt);

                   break;

         }    /* end - switch */

    }    /* end - for */

    /* Close the comm port. */
    close_a2( com_port );

    /* Clear the screen. */

    window(1,1,80,25);
    clrscr();

}   /* end - main */
