/******************************************************************************/
/*                                                                            */
/* Title       : xdemo.c                                                      */
/* Author      : Manfred Bester                                               */
/* Date        : 01Nov94                                                      */
/* Last change : 04Jan97                                                      */
/*                                                                            */
/* Synopsis    : SatTrack color world map demo program.                       */
/*                                                                            */
/*                                                                            */
/* SatTrack V3.1                                                              */
/*                                                                            */
/* Copyright (c) 1992-1997 Bester Tracking Systems. All Rights Reserved.      */
/*                                                                            */
/* Permission to use, copy, and distribute SatTrack V3.1 and its              */
/* documentation in its entirety for educational, research and non-profit     */
/* purposes, without fee, and without a written agreement is hereby granted,  */
/* provided that the above copyright notice and the following three           */
/* paragraphs appear in all copies. SatTrack may be modified for personal     */
/* purposes, but modified versions may NOT be distributed without prior       */
/* consent of Bester Tracking Systems.                                        */
/*                                                                            */
/* Permission to incorporate this software into commercial products may be    */
/* obtained Bester Tracking Systems at the address below.                     */
/*                                                                            */
/*                                                                            */
/*     Bester Tracking Systems                                                */
/*     P.O. Box 8899                                                          */
/*     Emeryville, CA 94662, USA                                              */
/*                                                                            */
/*     Phone: (510) 654-7824                                                  */
/*     Email: license@bester.com                                              */
/*     WWW:   www.bester.com                                                  */
/*                                                                            */
/*                                                                            */
/* Note that distributing SatTrack 'bundled' in with ANY product is           */
/* considered to be a 'commercial purpose'.                                   */
/*                                                                            */
/* IN NO EVENT SHALL BESTER TRACKING SYSTEMS, ITS OWNER OR ITS EMPLOYEES BE   */
/* LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR          */
/* CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS      */
/* DOCUMENTATION, EVEN IF BESTER TRACKING SYSTEMS HAS BEEN ADVISED OF THE     */
/* POSSIBILITY OF SUCH DAMAGE.                                                */
/*                                                                            */
/* BESTER TRACKING SYSTEMS SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,  */
/* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS  */
/* FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" */
/* BASIS, AND BESTER TRACKING SYSTEMS, ITS OWNER OR ITS EMPLOYEES HAVE NO     */
/* OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR     */
/* MODIFICATIONS FREE OF CHARGE.                                              */
/*                                                                            */
/* Federal and international laws provide severe civil and criminal           */
/* penalties for the unauthorized reproduction and distribution of            */
/* copyrighted software. Criminal copyright infringement is investigated      */
/* by the FBI and may constitute a felony with severe penalties.              */
/*                                                                            */
/******************************************************************************/

#include <stdio.h>
#include <math.h>
#include <string.h>

#include <X11/Intrinsic.h>
#include <X11/Shell.h>
#include <X11/StringDefs.h>
#include <X11/Xlib.h>

#include "satstrings.h"
#include "sattrack.h"
#include "mir.h"
#include "shuttle.h"
#include "world.xbm"

/******************************************************************************/
/*                                                                            */
/* definitions                                                                */
/*                                                                            */
/******************************************************************************/

#define MAPWIDTH      (unsigned int) world_width              /* [pixels]     */
#define MAPHEIGHT     (unsigned int) world_height             /* [pixels]     */
#define MAPTYPE       (unsigned int) world_type               /* 1 or 2       */
#define MAPWIDTHD     (double) world_widthd                   /* [deg]        */
#define MAPHEIGHTD    (double) world_heightd                  /* [deg]        */

#define GRIDSCALEX    ((double) MAPWIDTH  / MAPWIDTHD)        /* [pixels/deg] */
#define GRIDSCALEY    ((double) MAPHEIGHT / MAPHEIGHTD)       /* [pixels/deg] */

#define MARGINTOP     (unsigned int) 20
#define MARGINBOT     (unsigned int) 20
#define MARGINLFT     (unsigned int) 10
#define MARGINRGT     (unsigned int) 10

#define WINWIDTH      (unsigned int) (MAPWIDTH  + MARGINLFT + MARGINRGT)
#define WINHEIGHT     (unsigned int) (MAPHEIGHT + MARGINTOP + MARGINBOT)
#define HEADERROW     (unsigned int) (MARGINTOP - 5)
#define FOOTERROW     (unsigned int) (MARGINTOP + MAPHEIGHT + MARGINBOT - 5)

/******************************************************************************/
/*                                                                            */
/* structures                                                                 */
/*                                                                            */
/******************************************************************************/

typedef struct {
    double lng, lat;
    } groundTrackType;

groundTrackType groundTrack[NSEGSGT];

/******************************************************************************/
/*                                                                            */
/* X Window stuff and other global definitions                                */
/*                                                                            */
/******************************************************************************/

Display       *theDisplay;
Window        theWindow;
GC            theGC;
Colormap      theColormap;
XColor        color;
Drawable      drawable;
Pixmap        pixmap;
XSegment      gridZero[2], gridLinesX[NGRIDX*2], gridLinesY[NGRIDY*2];
XSegment      visibCircleSatPix[NSEGSVC], visibCircleGndPix[NSEGSVC];
XSegment      visibCircleSunPix[NSEGSVC], groundTrackPix[NSEGSGT];
XPoint        stsPoints[NSTSPOINTS], mirPoints[NMIRPOINTS];
Widget        toplevel, frame;
XtAppContext  appContext;
Arg           warg[3];

double        gndLtd, gndLng, satLtd, satLng, satHgt, sunLtd, sunLng, sunHgt, 
              ctyLtd, ctyLng, satLtdNew, satLngNew, satAzi, satEle, 
              satLngAdd, satLngFact, dLng, satLng0, phi, phiFact, inc, 
              separation, sepStep, sep, dh;

unsigned long numColors, foregroundPixel, backgroundPixel, 
              redPixel, greenPixel, lightGreenPixel,
              nightBluePixel, darkBluePixel, oceanBluePixel,
              lightBluePixel, veryLightBluePixel,
              darkYellowPixel, lightYellowPixel,
              blackPixel, darkGreyPixel, mediumGreyPixel, whitePixel,
              timeInterval;

unsigned int  w, h;

int           x, y, theScreen, secCount, secs, maxSecs, stsFlag, nPoints, 
              nFramePoints, nFramePointsTop, initFlag, 
              gndLocX, gndLocY, satLocX, satLocY, sunLocX, sunLocY, 
              ctyLocX, ctyLocY, satLocBakX, satLocBakY, 
              redrawFlag, daylightFlag, satTypeFlag, satSpot, verboseFlag;

char          str[80], appName[80], gndName[80];

/******************************************************************************/
/*                                                                            */
/* external functions                                                         */
/*                                                                            */
/******************************************************************************/

extern double absol(), reduce();
extern void   multMatVec();

extern void   CalcGrid();
extern void   CalcGroundTrack();
extern void   CalcMapLocation();
extern void   CalcVisibCircle();
extern void   CleanSegments();
extern void   CreateColors();
extern void   DrawFrame();
extern void   DrawMap();
extern void   DrawMir();
extern void   DrawSat();
extern void   DrawShuttle();
extern void   Quit();
extern void   RedrawMap();
extern void   ResizeMap();
extern void   TimeLoop();

/******************************************************************************/
/*                                                                            */
/* reduce: reduces number into specified interval (e.g. -PI, +PI)             */
/*                                                                            */
/******************************************************************************/

double reduce(value,rangeMin,rangeMax)

double value, rangeMin, rangeMax;

{
    double range, rangeFrac, fullRanges, retval;

    range     = rangeMax - rangeMin;
    rangeFrac = (rangeMax - value) / range;

    modf(rangeFrac,&fullRanges);

    retval = value + fullRanges * range;

    if (retval > rangeMax)
        retval -= range;

    return(retval);
}

/******************************************************************************/
/*                                                                            */
/* DrawShuttle: draws space shuttle                                           */
/*                                                                            */
/******************************************************************************/

void DrawShuttle()

{
    int i;

    nPoints = sizeof (stsPointsWhite) / sizeof (XPoint);

    for (i = 0; i < nPoints; i++)
    {
        stsPoints[i].x = stsPointsWhite[i].x + (short int) satLocX;
        stsPoints[i].y = stsPointsWhite[i].y + (short int) satLocY;
    }

    if (daylightFlag)
        XSetForeground(theDisplay,theGC,whitePixel);
    else
        XSetForeground(theDisplay,theGC,mediumGreyPixel);

    XDrawPoints(theDisplay,drawable,theGC,stsPoints,nPoints,CoordModeOrigin);

    nPoints = sizeof (stsPointsBlack) / sizeof (XPoint);

    for (i = 0; i < nPoints; i++)
    {
        stsPoints[i].x = stsPointsBlack[i].x + (short int) satLocX;
        stsPoints[i].y = stsPointsBlack[i].y + (short int) satLocY;
    }

    XSetForeground(theDisplay,theGC,blackPixel);
    XDrawPoints(theDisplay,drawable,theGC,stsPoints,nPoints,CoordModeOrigin);

    return;
}

/******************************************************************************/
/*                                                                            */
/* DrawMir: draws Mir space station                                           */
/*                                                                            */
/******************************************************************************/

void DrawMir()

{
    int i;

    nPoints = sizeof (mirPointsWhite) / sizeof (XPoint);

    for (i = 0; i < nPoints; i++)
    {
        mirPoints[i].x = mirPointsWhite[i].x + (short int) satLocX;
        mirPoints[i].y = mirPointsWhite[i].y + (short int) satLocY;
    }

    if (daylightFlag)
        XSetForeground(theDisplay,theGC,whitePixel);
    else
        XSetForeground(theDisplay,theGC,mediumGreyPixel);

    XDrawPoints(theDisplay,drawable,theGC,mirPoints,nPoints,CoordModeOrigin);

    nPoints = sizeof (mirPointsBlack) / sizeof (XPoint);

    for (i = 0; i < nPoints; i++)
    {
        mirPoints[i].x = mirPointsBlack[i].x + (short int) satLocX;
        mirPoints[i].y = mirPointsBlack[i].y + (short int) satLocY;
    }

    XSetForeground(theDisplay,theGC,blackPixel);
    XDrawPoints(theDisplay,drawable,theGC,mirPoints,nPoints,CoordModeOrigin);

    return;
}

/******************************************************************************/
/*                                                                            */
/* DrawSat: draws satellite                                                   */
/*                                                                            */
/*          selectFlag = TRUE    color is blue   (status = SELECT or MARK)    */
/*                     = FALSE   color is yellow                              */
/*                                                                            */
/*          dayFlag    = TRUE    color is light  (blue or yellow)             */
/*                     = FALSE   color is dark   (blue or yellow)             */
/*                                                                            */
/******************************************************************************/

void DrawSat(nStr,selectFlag,dayFlag)

int  selectFlag, dayFlag;
char *nStr;

{
    satSpot = (selectFlag) ? TRKSPOT : SATSPOT;

    if (selectFlag && dayFlag)
        XSetForeground(theDisplay,theGC,lightBluePixel);
    if (selectFlag && !dayFlag)
        XSetForeground(theDisplay,theGC,darkBluePixel);

    if (!selectFlag && dayFlag)
        XSetForeground(theDisplay,theGC,lightYellowPixel);
    if (!selectFlag && !dayFlag)
        XSetForeground(theDisplay,theGC,darkYellowPixel);

    XFillRectangle(theDisplay,drawable,theGC,
                   satLocX - (int) satSpot ,satLocY - (int) satSpot,
                   (unsigned int) (satSpot*2+1),(unsigned int) (satSpot*2+1));

    x = ((int) (MARGINLFT + MAPWIDTH) - satLocX > 100) 
                 ? 10 : -10 - ((int) (strlen(str)) * 6);

    y = (satLocY - (int) MARGINTOP < 25) ? 20 : -8;

    if (!dayFlag)
    {
        if (selectFlag)
            XSetForeground(theDisplay,theGC,lightBluePixel);
        else
            XSetForeground(theDisplay,theGC,lightYellowPixel);
    }

    XDrawString(theDisplay,drawable,theGC,satLocX+x,satLocY+y,
                nStr,(int) strlen(nStr));

    return;
}

/******************************************************************************/
/*                                                                            */
/* DrawFrame: draws the frame around world map and the text in the header and */
/*            footer line                                                     */
/*                                                                            */
/******************************************************************************/

void DrawFrame()

{
    x = 0;
    y = 0;
    w = (unsigned int) (MARGINLFT + MAPWIDTH + MARGINRGT);
    h = (unsigned int) MARGINTOP;

    XSetForeground(theDisplay,theGC,blackPixel);
    XFillRectangle(theDisplay,drawable,theGC,x,y,w,h);

    XSetForeground(theDisplay,theGC,whitePixel);

    sprintf(str,"STS-71");
    XDrawString(theDisplay,drawable,theGC,(int) (MARGINLFT+2),
                (int) HEADERROW,str,(int) strlen(str));

    sprintf(str,"Orbit: 85");
    XDrawString(theDisplay,drawable,theGC,(int) (MARGINLFT+0.17*MAPWIDTH),
                (int) HEADERROW,str,(int) strlen(str));

/*
    sprintf(str,"Azi: %3.0f",satAzi);
    XDrawString(theDisplay,drawable,theGC,(int) (MARGINLFT+0.32*MAPWIDTH),
                (int) HEADERROW,str,(int) strlen(str));

    sprintf(str,"Ele: %3.0f",satEle);
    XDrawString(theDisplay,drawable,theGC,(int) (MARGINLFT+0.42*MAPWIDTH),
                (int) HEADERROW,str,(int) strlen(str));
*/

    sprintf(str,"Lat: %2.0f %s",fabs(satLtd),(satLtd >= 0.0) ? "N" : "S");
    XDrawString(theDisplay,drawable,theGC,(int) (MARGINLFT+0.38*MAPWIDTH),
                (int) HEADERROW,str,(int) strlen(str));

    sprintf(str,"Lng: %3.0f %s",fabs(satLng),(satLng >= 0.0) ? "W" : "E");
    XDrawString(theDisplay,drawable,theGC,(int) (MARGINLFT+0.50*MAPWIDTH),
                (int) HEADERROW,str,(int) strlen(str));

    sprintf(str,"03Jun1995 17:00:%02d UTC",secs);
    XDrawString(theDisplay,drawable,theGC,(int) (MARGINLFT+0.70*MAPWIDTH),
                (int) HEADERROW,str,(int) strlen(str));

    x = 0;
    y = (int) (MARGINTOP + MAPHEIGHT);
    w = (unsigned int) (MARGINLFT + MAPWIDTH + MARGINRGT);
    h = (unsigned int) MARGINBOT;

    XSetForeground(theDisplay,theGC,blackPixel);
    XFillRectangle(theDisplay,drawable,theGC,x,y,w,h);

    XSetForeground(theDisplay,theGC,whitePixel);

    sprintf(str,"Ground Track: ");
    sprintf(str,"%s  96.0 km ESE of Hanga Roa, Easter Island (Chile)",str);
    XDrawString(theDisplay,drawable,theGC,
                (int) (MARGINLFT+2),(int) FOOTERROW,str,(int) strlen(str));

    x = 0;
    y = (int) MARGINTOP;
    w = (unsigned int) MARGINLFT;
    h = (unsigned int) MAPHEIGHT;

    XSetForeground(theDisplay,theGC,blackPixel);
    XFillRectangle(theDisplay,drawable,theGC,x,y,w,h);

    x = (int) (MARGINLFT + MAPWIDTH);
    y = (int) MARGINTOP;
    w = (unsigned int) MARGINRGT;
    h = (unsigned int) MAPHEIGHT;

    XFillRectangle(theDisplay,drawable,theGC,x,y,w,h);

    return;
}

/******************************************************************************/
/*                                                                            */
/* DrawMap: draws window with world map and other features                    */
/*                                                                            */
/******************************************************************************/

void DrawMap()

{
    int i;

    if (initFlag)
        XClearWindow(theDisplay,drawable);

    /* draw frame and header line */

    DrawFrame();

    /* draw world map */

    if (initFlag)
        pixmap = XCreateBitmapFromData(theDisplay,(Drawable) drawable,
                     (char *) world_bits,MAPWIDTH,MAPHEIGHT);

    XSetForeground(theDisplay,theGC,foregroundPixel);
    XSetBackground(theDisplay,theGC,backgroundPixel);
    XCopyPlane(theDisplay,(Drawable) pixmap,(Drawable) drawable,theGC,0,0,
               MAPWIDTH,MAPHEIGHT,(int) MARGINLFT,(int) MARGINTOP,1);

    /* draw grid */

    XSetForeground(theDisplay,theGC,mediumGreyPixel);
    XDrawSegments(theDisplay,drawable,theGC,gridZero,2);

    XSetForeground(theDisplay,theGC,darkGreyPixel);
    XDrawSegments(theDisplay,drawable,theGC,gridLinesX,NGRIDX*2);
    XDrawSegments(theDisplay,drawable,theGC,gridLinesY,NGRIDY*2);

    /* draw Sun and terminator */

    XSetForeground(theDisplay,theGC,lightYellowPixel);
    XDrawSegments(theDisplay,drawable,theGC,visibCircleSunPix,NSEGSVC);
    sprintf(str,"*");
    XDrawString(theDisplay,drawable,theGC,sunLocX-2,sunLocY+5,
                str,(int) strlen(str));

    /* draw ground station */

    XSetForeground(theDisplay,theGC,redPixel);
    XDrawSegments(theDisplay,drawable,theGC,visibCircleGndPix,NSEGSVC);

    XFillRectangle(theDisplay,drawable,theGC,
                   gndLocX - (int) GNDSPOT,gndLocY - (int) GNDSPOT,
                   (unsigned int) (GNDSPOT*2+1),(unsigned int) (GNDSPOT*2+1));

    x = ((int) (MARGINLFT + MAPWIDTH) - gndLocX > 100) 
                 ? 10 : -10 - ((int) (strlen(str)) * 6);

    y = (gndLocY - (int) MARGINTOP < 25) ? 20 : -8;

    XSetForeground(theDisplay,theGC,veryLightBluePixel);
    XDrawString(theDisplay,drawable,theGC,gndLocX+x,gndLocY+y,
                gndName,(int) strlen(gndName));

    /* draw nearest city */

    CalcMapLocation(ctyLtd,ctyLng,&ctyLocX,&ctyLocY);

    XSetForeground(theDisplay,theGC,redPixel);
    XFillRectangle(theDisplay,drawable,theGC,
                   ctyLocX - (int) CTYSPOT,ctyLocY - (int) CTYSPOT,
                   (unsigned int) (CTYSPOT*2+1),(unsigned int) (CTYSPOT*2+1));

    /* draw ground track */

    XSetForeground(theDisplay,theGC,lightBluePixel);
    if (fabs(groundTrack[NSEGS+1].lng-groundTrack[1].lng) > 90.0)
    {
        XDrawSegments(theDisplay,drawable,theGC,groundTrackPix,N2SEGSGT);
    }
    else
    {
        XDrawSegments(theDisplay,drawable,theGC,groundTrackPix,N1SEGSGT);
    }

    /* draw visibility circle */

    XSetForeground(theDisplay,theGC,veryLightBluePixel);
    XDrawSegments(theDisplay,drawable,theGC,visibCircleSatPix,NSEGSVC);

    /* draw all satellites */

    satTypeFlag = STS;

    for (i = 0; i < 2; i++)
    {
/*
        CalcMapLocation(satLat*CRD,satLong*CRD,&satLocX,&satLocY);
*/
        satTypeFlag = (satTypeFlag == MIR) ? STS : MIR;

        switch(satTypeFlag)
        {
            case BASIC:
/*
                strcpy(str,"%s",satName);
                DrawSat(str,FALSE,TRUE);
*/
                break;

            case MIR:
                CalcMapLocation(satLtd,satLng,&satLocX,&satLocY);
                DrawMir();
                break;

            case STS:
                satLngNew  = satLng - separation;
                satLngNew  = reduce(satLngNew,-180.0,180.0);
                satLtdNew  = satLtd + separation;
                satLocBakX = satLocX;
                satLocBakY = satLocY;

                CalcMapLocation(satLtdNew,satLngNew,&satLocX,&satLocY);

                sep = reduce((satLng-satLngNew),0.0,360.0);
                dh  = satHgt - satHgt;

                if ((sep < 5.0 || sep > 355.0) && fabs(dh) < 5.0)
                {
                    satLocX = satLocBakX -  8;             /* Mir-STS docking */
                    satLocY = satLocBakY + 16;
                }

                DrawShuttle();
                break;

            default:
                break;
        }
    }

    daylightFlag = (daylightFlag) ? FALSE : TRUE;

    if (daylightFlag)
    {
        gndLtd =  -7.3;
        gndLng = -72.4;
        sprintf(gndName,"%s","DGS");
    }

    else
    {
        gndLtd =  35.4;
        gndLng = 116.9;
        sprintf(gndName,"GDS");

        gndLtd =  20.7;
        gndLng = 156.3;
        sprintf(gndName,"AMOS");
    }

    /* draw other satellites in multisat list */

    strcpy(str,"HST");
    CalcMapLocation(15.0,45.0,&satLocX,&satLocY);
    DrawSat(str,FALSE,daylightFlag);

    strcpy(str,"KO-23");
    CalcMapLocation(-45.0,95.0,&satLocX,&satLocY);
    DrawSat(str,TRUE,daylightFlag);

    sprintf(str,"GRO");
    CalcMapLocation(-10.0,75.0,&satLocX,&satLocY);
    DrawSat(str,TRUE,daylightFlag);

    sprintf(str,"UARS");
    satLngNew = satLng + 90.0;
    satLngNew = reduce(satLngNew,-180.0,180.0);
    CalcMapLocation(satLtd,satLngNew,&satLocX,&satLocY);
    DrawSat(str,FALSE,daylightFlag);

    /* raise map */

    if (initFlag)
        XMapRaised(theDisplay,(Drawable) drawable);

    initFlag = FALSE;
    return;
}

/******************************************************************************/
/*                                                                            */
/* RedrawMap: redraws map                                                     */
/*                                                                            */
/******************************************************************************/

void RedrawMap()

{
    if (redrawFlag)
    {
        if (verboseFlag)
            printf("redrawing map ...\n");

        CalcGroundTrack();

        CalcMapLocation(gndLtd,gndLng,&gndLocX,&gndLocY);
        CalcMapLocation(satLtd,satLng,&satLocX,&satLocY);
        CalcMapLocation(sunLtd,sunLng,&sunLocX,&sunLocY);

        CalcVisibCircle(&visibCircleGndPix[0],gndLtd,gndLng,satHgt);
        CalcVisibCircle(&visibCircleSatPix[0],satLtd,satLng,satHgt);
        CalcVisibCircle(&visibCircleSunPix[0],sunLtd,sunLng,sunHgt);

        DrawMap();
        redrawFlag = FALSE;
    }

    return;
}

/******************************************************************************/
/*                                                                            */
/* ResizeMap: resizes map                                                     */
/*                                                                            */
/******************************************************************************/

void ResizeMap()

{
    if (verboseFlag)
        printf("--> this doesn't work yet!\n");

    return;
}

/******************************************************************************/
/*                                                                            */
/* TimeLoop: performs cyclic update of satellite position                     */
/*                                                                            */
/******************************************************************************/

void TimeLoop(i,id)

int i;
XtIntervalId *id;

{
    secCount += (int) (timeInterval / 1000);

    sunLng   += (double) timeInterval / 1000.0 / 86400.0 * 360;
    sunLng    = reduce(sunLng,-180.0,180.0);

    if (secCount > maxSecs)
        secCount = 0;

    secs   = secCount % 60;
    dLng   = satLngFact * fabs(satLngAdd);
    satLtd = -inc * sin((dLng * (double) secCount) * CDR) + ONEPPM;

    phi    = -phiFact * (double) secCount * satLngAdd / 3.0 / 360.0;
    satLng = reduce(satLng0 + ((double) secCount) * dLng + phi,-180.0,180.0);

    if (separation < -22.5)
    {
        separation = -22.5;
        sepStep    =   0.5;
    }

    if (separation > 0.0)
    {
        separation =  0.0;
        sepStep    = -0.5;
    }

    separation += sepStep;

    CalcGroundTrack();

    CalcMapLocation(gndLtd,gndLng,&gndLocX,&gndLocY);
    CalcMapLocation(satLtd,satLng,&satLocX,&satLocY);
    CalcMapLocation(sunLtd,sunLng,&sunLocX,&sunLocY);

    CalcVisibCircle(&visibCircleGndPix[0],gndLtd,gndLng,satHgt);
    CalcVisibCircle(&visibCircleSatPix[0],satLtd,satLng,satHgt);
    CalcVisibCircle(&visibCircleSunPix[0],sunLtd,sunLng,sunHgt);

    redrawFlag = TRUE;
    DrawMap();
    timeInterval = 2000L;
    XtAppAddTimeOut(appContext,timeInterval,
                    (XtTimerCallbackProc) TimeLoop,(caddr_t) NULL);

    return;
}

/******************************************************************************/
/*                                                                            */
/* QuitMain: quits main program                                               */
/*                                                                            */
/******************************************************************************/

void Quit()

{
    if (verboseFlag)
        printf("closing window ...\n");

    XFreePixmap(theDisplay,pixmap);
    XFreeGC(theDisplay,theGC);
    XDestroyWindow(theDisplay,XtWindow(toplevel));
    XCloseDisplay(theDisplay);

    if (verboseFlag)
        printf("\ndid you have a good time watching this?\n\n");

    exit(-1);
}

/******************************************************************************/
/*                                                                            */
/* CreateColors: creates all colors needed                                    */
/*                                                                            */
/******************************************************************************/

void CreateColors()

{
    theColormap = DefaultColormap(theDisplay,theScreen);
    color.flags = DoRed | DoGreen | DoBlue;

    color.red   = (unsigned short) 65535;                              /* red */
    color.green = (unsigned short) 20000;
    color.blue  = (unsigned short) 25000;

    XAllocColor(theDisplay,theColormap,&color);
    redPixel = color.pixel;

    color.red   = (unsigned short) 18000;                            /* green */
    color.green = (unsigned short) 48000;
    color.blue  = (unsigned short)  2000;

    XAllocColor(theDisplay,theColormap,&color);
    greenPixel = color.pixel;

    color.red   = (unsigned short) 18000;                      /* light green */
    color.green = (unsigned short) 52000;
    color.blue  = (unsigned short)  2000;

    XAllocColor(theDisplay,theColormap,&color);
    lightGreenPixel = color.pixel;

    color.red   = (unsigned short)  5000;                       /* night blue */
    color.green = (unsigned short)  5000;
    color.blue  = (unsigned short) 35000;

    XAllocColor(theDisplay,theColormap,&color);
    nightBluePixel = color.pixel;

    color.red   = (unsigned short) 34000;                        /* dark blue */
    color.green = (unsigned short) 34000;
    color.blue  = (unsigned short) 52000;

    XAllocColor(theDisplay,theColormap,&color);
    darkBluePixel = color.pixel;

    color.red   = (unsigned short) 12500;                       /* ocean blue */
    color.green = (unsigned short)  7500;
    color.blue  = (unsigned short) 50000;

    XAllocColor(theDisplay,theColormap,&color);
    oceanBluePixel = color.pixel;

    color.red   = (unsigned short) 40000;                       /* light blue */
    color.green = (unsigned short) 60000;
    color.blue  = (unsigned short) 65535;

    XAllocColor(theDisplay,theColormap,&color);
    lightBluePixel = color.pixel;

    color.red   = (unsigned short) 62000;                  /* very light blue */
    color.green = (unsigned short) 65535;
    color.blue  = (unsigned short) 65535;

    XAllocColor(theDisplay,theColormap,&color);
    veryLightBluePixel = color.pixel;

    color.red   = (unsigned short) 52000;                      /* dark yellow */
    color.green = (unsigned short) 52000;
    color.blue  = (unsigned short) 34000;

    XAllocColor(theDisplay,theColormap,&color);
    darkYellowPixel = color.pixel;

    color.red   = (unsigned short) 65535;                     /* light yellow */
    color.green = (unsigned short) 65535;
    color.blue  = (unsigned short) 35000;

    XAllocColor(theDisplay,theColormap,&color);
    lightYellowPixel = color.pixel;

    color.red   = (unsigned short)     0;                            /* black */
    color.green = (unsigned short)     0;
    color.blue  = (unsigned short)     0;

    XAllocColor(theDisplay,theColormap,&color);
    blackPixel = color.pixel;

    color.red   = (unsigned short) 40000;                        /* dark grey */
    color.green = (unsigned short) 40000;
    color.blue  = (unsigned short) 45000;

    XAllocColor(theDisplay,theColormap,&color);
    darkGreyPixel = color.pixel;

    color.red   = (unsigned short) 48000;                      /* medium grey */
    color.green = (unsigned short) 48000;
    color.blue  = (unsigned short) 52000;

    XAllocColor(theDisplay,theColormap,&color);
    mediumGreyPixel = color.pixel;

    color.red   = (unsigned short) 65535;                            /* white */
    color.green = (unsigned short) 65535;
    color.blue  = (unsigned short) 65535;

    XAllocColor(theDisplay,theColormap,&color);
    whitePixel = color.pixel;

    return;
}

/******************************************************************************/
/*                                                                            */
/* CalcGrid: calculates grid                                                  */
/*                                                                            */
/******************************************************************************/

void CalcGrid()

{
    double gridStepX, gridStepY;
    int    i, j, mapCenterX, mapCenterY;

    mapCenterX     = (int) (MARGINLFT + MAPWIDTH  / 2 - 1);
    mapCenterY     = (int) (MARGINTOP + MAPHEIGHT / 2 - 1);

    gridZero[0].x1 = mapCenterX;                             /* zero meridian */
    gridZero[0].x2 = mapCenterX;                 
    gridZero[0].y1 = (int) MARGINTOP;
    gridZero[0].y2 = (int) (MARGINTOP + MAPHEIGHT - 1);
    
    gridZero[1].x1 = (int) MARGINLFT;                              /* equator */
    gridZero[1].x2 = (int) (MARGINLFT + MAPWIDTH - 1);
    gridZero[1].y1 = mapCenterY;
    gridZero[1].y2 = mapCenterY;
    
    gridStepX      = GRIDSTEPX * GRIDSCALEX;
    gridStepY      = GRIDSTEPY * GRIDSCALEY;

    for (i = 0; i < NGRIDX; i++)                                  /* vertical */
    {
        j = 2 * i;

        gridLinesX[j].x1   = mapCenterX + (int) ((double) (i+1) * gridStepX);
        gridLinesX[j].x2   = mapCenterX + (int) ((double) (i+1) * gridStepX);

        gridLinesX[j+1].x1 = mapCenterX - (int) ((double) (i+1) * gridStepX);
        gridLinesX[j+1].x2 = mapCenterX - (int) ((double) (i+1) * gridStepX);

        gridLinesX[j].y1   = (int) MARGINTOP;
        gridLinesX[j].y2   = (int) (MARGINTOP + MAPHEIGHT - 1);

        gridLinesX[j+1].y1 = (int) MARGINTOP;
        gridLinesX[j+1].y2 = (int) (MARGINTOP + MAPHEIGHT - 1);
    }

    for (i = 0; i < NGRIDY; i++)                                /* horizontal */
    {
        j = 2 * i;

        gridLinesY[j].x1   = (int) MARGINLFT;
        gridLinesY[j].x2   = (int) (MARGINLFT + MAPWIDTH - 1);

        gridLinesY[j+1].x1 = (int) MARGINLFT;
        gridLinesY[j+1].x2 = (int) (MARGINLFT + MAPWIDTH - 1);

        gridLinesY[j].y1   = mapCenterY + (int) ((double) (i+1) * gridStepY);
        gridLinesY[j].y2   = mapCenterY + (int) ((double) (i+1) * gridStepY);

        gridLinesY[j+1].y1 = mapCenterY - (int) ((double) (i+1) * gridStepY);
        gridLinesY[j+1].y2 = mapCenterY - (int) ((double) (i+1) * gridStepY);
    }

    return;
}

/******************************************************************************/
/*                                                                            */
/* CalcMapLocation: calculates location of ground station, satellite and Sun  */
/*                                                                            */
/******************************************************************************/

void CalcMapLocation(mapLtd,mapLng,mapX,mapY)

double mapLtd, mapLng;
int    *mapX, *mapY;

{
    *mapX  = (int) ((MAPWIDTHD  / 2.0 - mapLng) * GRIDSCALEX);
    *mapY  = (int) ((MAPHEIGHTD / 2.0 - mapLtd) * GRIDSCALEY);

    *mapX += (int) MARGINLFT - 1;
    *mapY += (int) MARGINTOP - 1;

    return;
}

/******************************************************************************/
/*                                                                            */
/* CalcVisibCircle: calculates circle of visibility around sub-satellite      */
/*                  point, using an Euler matrix for the vector rotation      */
/*                                                                            */
/******************************************************************************/

void CalcVisibCircle(visibCirclePix,circLtd,circLng,circHgt)

XSegment *visibCirclePix;
double   circLtd, circLng, circHgt;

{
    double descMat[3][3], u[3], q[3];
    double arg, circStep, qabs;
    double theta, psi, gamma, beta, lambda, lat, lng;
    double cosTheta, sinTheta, cosPsi, sinPsi;
    double cosBeta, sinBeta, cosLambda, sinLambda;

    int    i, k, posX, posY;

    theta    = HALFPI - circLtd*CDR;
    psi      = HALFPI - circLng*CDR;

    cosTheta = cos(theta);
    sinTheta = sin(theta);
    cosPsi   = cos(psi);
    sinPsi   = sin(psi);

    descMat[0][0] =  sinPsi*cosTheta;
    descMat[0][1] = -cosPsi;
    descMat[0][2] =  sinPsi*sinTheta;
    descMat[1][0] =  cosPsi*cosTheta;
    descMat[1][1] =  sinPsi;
    descMat[1][2] =  cosPsi*sinTheta;
    descMat[2][0] = -sinTheta;
    descMat[2][1] =  0.0;
    descMat[2][2] =  cosTheta;

    arg      = EARTHRADIUS / (EARTHRADIUS + circHgt);

    if (arg >  1.0) arg =  1.0;
    if (arg < -1.0) arg = -1.0;

    gamma    = acos(arg);

    beta     = HALFPI - gamma;
    cosBeta  = cos(beta);
    sinBeta  = sin(beta);

    circStep = 360.0 / ((double) NSEGSVC);

    for (k = 0; k < NSEGSVC;  k++)
    {
        lambda    = (double) (circStep * k) * CDR;
        cosLambda = cos(lambda);
        sinLambda = sin(lambda);

        u[0] = cosLambda*cosBeta;
        u[1] = sinLambda*cosBeta;
        u[2] = sinBeta;

        multMatVec(u,q,descMat);

        qabs  = absol(q);

        for (i = 0; i <= 2; i++)
            q[i] /= qabs;

        arg   = q[2];

        if (arg >  1.0) arg =  1.0;
        if (arg < -1.0) arg = -1.0;

        lat   = asin(arg) * CRD;

        q[2]  = 0.0;
        qabs  = absol(q);

        q[0] /= qabs;
        q[1] /= qabs;

        lng   = atan2(q[1],q[0]) * CRD;
        lng   = reduce(lng,-MAPWIDTHD/2.0,MAPWIDTHD/2.0);

        posX  = (int) ((MAPWIDTHD  / 2.0 - lng) * GRIDSCALEX + MARGINLFT - 0.5);
        posY  = (int) ((MAPHEIGHTD / 2.0 - lat) * GRIDSCALEY + MARGINTOP - 0.5);

        visibCirclePix[k].x1 = (short int) posX;
        visibCirclePix[k].y1 = (short int) posY;

        if (k > 0)
        {
            visibCirclePix[k-1].x2 = visibCirclePix[k].x1;
            visibCirclePix[k-1].y2 = visibCirclePix[k].y1;
        }
    }

    visibCirclePix[NSEGSVC-1].x2 = visibCirclePix[0].x1;      /* close circle */
    visibCirclePix[NSEGSVC-1].y2 = visibCirclePix[0].y1;

    CleanSegments(&visibCirclePix[0],NSEGSVC,TRUE);

    return;
}

/******************************************************************************/
/*                                                                            */
/* CalcGroundTrack: calculates ground track                                   */
/*                                                                            */
/******************************************************************************/

void CalcGroundTrack()

{
    int    k, gndX, gndY;

    dLng = satLngFact * (MAPWIDTHD / (double) NSEGS);

    for (k = 0; k < NSEGSGT; k++)
    {
        phi = -phiFact * (double) k / (double) NSEGSGT;

        groundTrack[k].lng = reduce(satLng0+(double) k * dLng+phi,-180.0,180.0);
        groundTrack[k].lat = -inc * sin((dLng * (double) k) * CDR) + ONEPPM;
    }

    for (k = 0; k < NSEGSGT; k++)
    {
        gndX = (int) ((MAPWIDTHD/2.0  - groundTrack[k].lng) * GRIDSCALEX + 
                       MARGINLFT - 0.5);

        gndY = (int) ((MAPHEIGHTD/2.0 - groundTrack[k].lat) * GRIDSCALEY + 
                       MARGINTOP - 0.5);

        groundTrackPix[k].x1 = (short int) gndX;
        groundTrackPix[k].y1 = (short int) gndY;

        if (k > 0)
        {
            groundTrackPix[k-1].x2 = groundTrackPix[k].x1;
            groundTrackPix[k-1].y2 = groundTrackPix[k].y1;
        }
    }

    groundTrackPix[NSEGSGT-1].x2 = groundTrackPix[NSEGSGT-1].x1;
    groundTrackPix[NSEGSGT-1].y2 = groundTrackPix[NSEGSGT-1].y1;

    CleanSegments(&groundTrackPix[0],NSEGSGT,FALSE);

    return;
}

/******************************************************************************/
/*                                                                            */
/* CleanSegments: cleans up line segments (wrap around at map boundaries)     */
/*                                                                            */
/******************************************************************************/

void CleanSegments(xSegments,numSegments,visibCircleFlag)

XSegment *xSegments;
int      numSegments, visibCircleFlag;

{
    int k, k0, n, dx, dy, dX, dY;

    k0 = (visibCircleFlag) ? 0 : -1;

    for (k = 0; k < numSegments + k0; k++)
    {
        n = (visibCircleFlag && k == numSegments - 1) ? 0 : k+1;

        if (xSegments[k].x1 - xSegments[k].x2 > (short int) (MAPWIDTH / 2))
        {
            dx = (int) (MAPWIDTH + MARGINLFT) - (int) xSegments[k].x1;
            dX = (int) xSegments[k].x2 - (int) MARGINLFT + dx;

            dY = (int) (xSegments[k].y2 - xSegments[k].y1);
            dy = (int) ((double) dY / (double) dX * (double) dx);

            xSegments[n].x1 = xSegments[k].x2 - (short int) (dX - dx);
            xSegments[k].x2 = xSegments[k].x1 + (short int) (dx - 1);

            xSegments[n].y1 = xSegments[k].y2 - (short int) (dY - dy);
            xSegments[k].y2 = xSegments[k].y1 + (short int) dy;
        }

        if (xSegments[k].x2 - xSegments[k].x1 > (short int) (MAPWIDTH / 2))
        {
            dx = (int) xSegments[k].x1 - (int) MARGINLFT;
            dX = (int) (MAPWIDTH + MARGINLFT) - (int) xSegments[k].x2 + dx;

            dY = (int) (xSegments[k].y2 - xSegments[k].y1);
            dy = (int) ((double) dY / (double) dX * (double) dx);

            xSegments[n].x1 = xSegments[k].x2 + (short int) (dX - dx);
            xSegments[k].x2 = xSegments[k].x1 - (short int) (dx - 1);

            xSegments[n].y1 = xSegments[k].y2 - (short int) (dY - dy);
            xSegments[k].y2 = xSegments[k].y1 + (short int) dy;
        }
    }

    for (k = 0; k < numSegments; k++)
    {
        if (xSegments[k].y1 < (short int) MARGINTOP)
            xSegments[k].y1 = (short int) MARGINTOP;

        if (xSegments[k].y2 < (short int) MARGINTOP)
            xSegments[k].y2 = (short int) MARGINTOP;

        if (xSegments[k].y1 > (short int) (MAPHEIGHT + MARGINTOP - 1))
            xSegments[k].y1 = (short int) (MAPHEIGHT + MARGINTOP - 1);

        if (xSegments[k].y2 > (short int) (MAPHEIGHT + MARGINTOP - 1))
            xSegments[k].y2 = (short int) (MAPHEIGHT + MARGINTOP - 1);
    }

    return;
}

/******************************************************************************/
/*                                                                            */
/* main program                                                               */
/*                                                                            */
/******************************************************************************/

void main(argc,argv)

int  argc;
char *argv[];

{
    unsigned int n = 0;

    initFlag = TRUE;

    if (argc > 1)
        verboseFlag = (!strcmp(argv[1],"-v")) ? TRUE : FALSE;

    sprintf(appName,"%s %s   Graphics Demo Program",sattrName,sattrVersion);

    if (verboseFlag)
        printf("\ninitializing toolkit for 'xdemo' ...\n");

    XtToolkitInitialize();
    appContext = XtCreateApplicationContext();
    theDisplay = XtOpenDisplay(appContext,NULL,appName,appName,NULL,0,
                               &argc,argv);

    if (!theDisplay)
    {
        printf("Error: cannot open display.\n");
        exit(-1);
    }

    theScreen = DefaultScreen(theDisplay);
    numColors = XDisplayCells(theDisplay,theScreen);

    if (verboseFlag)
        printf("creating toplevel and frame ...\n");

    toplevel = XtAppCreateShell(appName,appName,
                   applicationShellWidgetClass,theDisplay,NULL,0);

    if (verboseFlag)
        printf("creating colors ...\n");

    CreateColors();

    if (MAPTYPE == 1)                                             /* fill map */
    {
        foregroundPixel = greenPixel;
        backgroundPixel = oceanBluePixel;
    }

    else                                                          /* line map */
    {
        foregroundPixel = lightGreenPixel;
        backgroundPixel = nightBluePixel;
    }

    if (verboseFlag)
        printf("map size: %dx%d pixels, %.0fx%.0f deg\n",
            MAPWIDTH,MAPHEIGHT,MAPWIDTHD,MAPHEIGHTD);

    XtSetArg(warg[n],XtNwidth,WINWIDTH); n++;
    XtSetArg(warg[n],XtNheight,WINHEIGHT); n++;
    XtSetArg(warg[n],XtNbackground,blackPixel); n++;
    frame = XtCreateManagedWidget("frame",widgetClass,toplevel,warg,n);

    /* data */

    secCount     =       0;

    gndLtd       =    -7.3;                                   /* Diego Garcia */
    gndLng       =   -72.4;
    sprintf(gndName,"%s","DGS");

    sunLtd       =    -15.0;                                  /* Sun position */
    sunLng       =     20.0;
    sunHgt       = EARTHSMA;

    ctyLtd       =   -27.0;                                  /* Easter Island */
    ctyLng       =   109.0;

    satLng0      =   147.5;                              /* ground track data */
    satLngAdd    =     1.0;
    satLngFact   =    -1.0;
    inc          =    51.0;
    phiFact      =    67.5;

    separation   =   -22.0;                   /* initial separation STS - Mir */
    sepStep      =     0.5;

    satLtd       =     0.0;                     /* initial satellite position */
    satLng       = satLng0;
    satHgt       =   410.0;

    maxSecs      = (int) (3.0 * 360.0 / satLngAdd + ONEPPM);

    if (verboseFlag)
        printf("calculating grid and ground station ...\n");

    CalcGrid();

    if (verboseFlag)
        printf("calculating ground track ...\n");

    CalcGroundTrack();

    CalcMapLocation(gndLtd,gndLng,&gndLocX,&gndLocY);
    CalcMapLocation(satLtd,satLng,&satLocX,&satLocY);
    CalcMapLocation(sunLtd,sunLng,&sunLocX,&sunLocY);

    if (verboseFlag)
        printf("calculating visibility circles ...\n");

    CalcVisibCircle(&visibCircleGndPix[0],gndLtd,gndLng,satHgt);
    CalcVisibCircle(&visibCircleSatPix[0],satLtd,satLng,satHgt);
    CalcVisibCircle(&visibCircleSunPix[0],sunLtd,sunLng,sunHgt);

    if (verboseFlag)
        printf("realizing top level ...\n");

    XtRealizeWidget(toplevel);

    if (verboseFlag)
        printf("creating graphics context ...\n");

    theWindow = XtWindow(toplevel);
    theGC     = XCreateGC(theDisplay,theWindow,0,0);
    drawable  = XtWindow(frame);

    if (verboseFlag)
        printf("programming event handlers ...\n");

    redrawFlag   = TRUE;
    daylightFlag = TRUE;

    XtAddEventHandler(frame,ButtonPressMask,FALSE,
                      (XtEventHandler) Quit,(caddr_t) NULL);
    XtAddEventHandler(frame,StructureNotifyMask,FALSE,
                      (XtEventHandler) ResizeMap,(caddr_t) NULL);
    XtAddEventHandler(frame,ExposureMask,FALSE,
                      (XtEventHandler) RedrawMap,(caddr_t) NULL);

    XMapRaised(theDisplay,(Drawable) drawable);

    if (verboseFlag)
        printf("starting timer ...\n");

    timeInterval = 100L;
    XtAppAddTimeOut(appContext,timeInterval,
                    (XtTimerCallbackProc) TimeLoop,(caddr_t) NULL);

    if (verboseFlag)
    {
        printf("entering application main loop ...\n");
        printf("\n");
        printf("to quit hit mouse button while cursor is inside graphics ");
        printf("window\n");
        printf("\n");
    }

    XtAppMainLoop(appContext);
}

/******************************************************************************/
/*                                                                            */
/* End of program xdemo.c                                                     */
/*                                                                            */
/******************************************************************************/
