// radar energy pattern
//
// compile with:      gcc -o ar06 ar05.c -lgd -lpng -lm
//
// uses the libgd library.  for documentation see the file:
//   /usr/share/doc/gd-*/index.html  
//   or the website:   http://www.libgd.org/reference
//  
// you will need truetype fonts.  if you don't have them, you can
// copy ../fonts/truetype/.. from openoffice.org to /usr/share/
// libgd origin at upper left

#define  IMAGE       1
// #undef   IMAGE

#include "gd.h"
#include "math.h"
#include "string.h"
#include <stdio.h>
#include <stdlib.h>

#define  PNGFMT      "ar06.png"

/*
#define  SCANX       1000.0       // scan width in meters
#define  LABX        200.0        // label spacing
#define  EXAG        5.0          // pixel exaggeration
#define  UNITS       "meters"
#define  USCALE      1.0  
#define  TUNITS      1000.0
#define  TSCALE      "ms"
#define  OX0         700          // first output array left
#define  OX1         2000         // first output array right

#define  SCANX       0.1          // scan width in meters
#define  LABX        0.02         // label spacing
#define  EXAG        1.0          // pixel exaggeration
#define  UNITS       "centimeters"
#define  USCALE      100.0  
#define  TUNITS      1000000.0
#define  TSCALE      "μs"
#define  OX0         700          // first output array left
#define  OX1         2000         // first output array right
*/

//          SCANX  LABX  EXAG USCALE TUNITS     OX0  OX1  UNITS       TSCALE
#define D0 "1000.0 200.0 5.0 1.0 1000.0 1200 3000 meters ms"
#define D1 "0.1 0.02 1.0 100.0 1000000.0 3200 5000 centimeters μs"

#define  XSIZE       5120         // display window in pixels
#define  OY0         100          // output array top
#define  OY1         1200         // output array bottom
#define  GGAIN       250
#define  GY          (OY1+130+GGAIN) // plot centerline
#define  GT          (GY+GGAIN)   // time text bottom
#define  YSIZE       (GT+20)

#define  EYC         1100         // earth orbit display vertical middle
#define  EX0         70           // earth orbit display left
#define  EX1         940          // earth orbit display right

#define  LNAR        3            // line pixel width
#define  LWID        5            // line pixel width

#define  OSLOPE     -0.392        // orbit slope through field

#define  EINC       -50.0         // inclination of target orbit
#define  EAOP        35.0         // argument of perigee target orbit

// ------------------------------------------------------------

#define  VORB        3000.0       // orbit velocity
#define  LAMBDA      0.005        // radio wavelength in meters
#define  TARGETA     400.0        // altitude of target
#define  TARGETN     45           // target latitude north

#define  S_WIDTH     200.0        // array width in meters
#define  S_NUM       181          // thinsats in a row, odd

#define  A_SPACE     12           // array spacing in degrees
#define  A_NUM       7            // array spacing in degrees

#define  NSAT        (S_NUM*A_NUM) // total number of thinsats

#define  RE          6378.0       // earth radius kilometers
#define  R288        12789.0      // server sky radius kilometers

#define  KM          1000.0       // kilometer

#define  FNT         "DejaVuMonoSans"
#define  CMAX        256          // gray scale

// ==========================================================================

int main() {

   FILE     *pngout           ; // file handle for png output frame
   char     labstring[80]     ; // used for labelling
   int      icolor            ; // index for computing colors
   int      i                 ; // general counter
   int      fs                ; // large font size
   double   satx[NSAT]        ; // satellite x position, east positive
   double   saty[NSAT]        ; // satellite y position, out positive 
   double   satp[NSAT]        ; // satellite phase in radians
   double   sata[NSAT]        ; // satellite amplitude

   double   arrx[A_NUM]       ; // array center position
   double   arry[A_NUM]       ; // array center position

   double   pi2 = 8.0*atan(1.0)  ; // 2*pi, 6.283
   double   d2r = pi2/360.0      ; // degrees to radians
   double   r2d = 360.0/pi2      ; // radians to degrees 

// -------------------
//  coordinates - z, north south, out of plane of drawing
//                x, orbit tangential distance from center thinsat array

   double   targetr = (RE+TARGETA)*KM     ; // target radius in meters
   double   targetn = d2r*TARGETN         ; // target latitude in radians north
   double   targetx = 0.0                 ; // target x coordinate
   double   targety = targetr*cos(targetn); // target y coordinate
   double   targetz = targetr*sin(targetn); // target z coordinate

   int    idx                             ; //
   double cscale = (double) CMAX          ; //

// -------------------
   double   k       = pi2/LAMBDA          ; // wavenumber
   double   re      = RE * KM             ; // earth radius in meters
   double   r288    = R288 * KM           ; // m288 orbit radius in meters
   int      smid    = ( S_NUM - 1 ) / 2   ;
   int      amid    = ( A_NUM - 1 ) / 2   ;
   double   s_num   = (double) S_NUM      ;
   double   a_sp    = d2r* A_SPACE        ; // array spacing in radians
   double   s_sp    = S_WIDTH /((S_NUM-1.0)*r288) ; // thinsat spacing rad.

// -------------------
// compute position and phase of each thinsat

   double asum = 0.0   ;  // normalize amplitude
   int    acnt, scnt   ;

   for( acnt=0 ; acnt < A_NUM ; acnt++ ) {
      double a_c = (acnt - amid)*a_sp    ; //
      double a_0 = a_c   - smid*s_sp    ; // array starting angle
      int   aidx = acnt*S_NUM            ;
      arrx[acnt] = r288 * sin(a_c)       ;
      arry[acnt] = r288 * cos(a_c)       ;

      for( scnt=0 ; scnt < S_NUM ; scnt++ ) {
         double s_an = a_0 + scnt * s_sp ; // orbit angle for thinsat
         int     idx = aidx + scnt       ; // array index for thinsat

         satx[idx] = r288 * sin(s_an)    ; //
         saty[idx] = r288 * cos(s_an)    ; //

         double dx = satx[idx]-targetx   ; //
         double dy = saty[idx]-targety   ; //
	 double dz = targetz             ; //
         double d  = sqrt( dx*dx + dy*dy + dz*dz ) ; // distance
         satp[idx] = k*d                 ; // phase
         sata[idx] = 1.0                 ; // uniform amplitude for now
         asum     += sata[idx]/d         ; // summed amplitude 
      }
   }

   double anorm = 1.0 / asum ;

   // set up array and define colors -----------------------------------------

   gdImagePtr im = gdImageCreateTrueColor(XSIZE, YSIZE );

   // allocate standard colors
   int black = gdImageColorAllocate(im,   0,   0,   0);
   int white = gdImageColorAllocate(im, 255, 255, 255);
   int sun1  = gdImageColorAllocate(im,  51,  51, 102);
   int red   = gdImageColorAllocate(im, 255,   0,   0);
   int green = gdImageColorAllocate(im,   0, 255,   0);
   int dgreen= gdImageColorAllocate(im,   0, 128,   0);
   int blue  = gdImageColorAllocate(im,   0,   0, 255);
   int gray  = gdImageColorAllocate(im, 128, 128, 128);
   int dgray = gdImageColorAllocate(im,  48,  48,  48);
   int trans = gdImageColorAllocate(im,   1,   1,   1);
   int gcolor[CMAX]               ; // color map numbers

   // allocate gray scale array 
   for( icolor=0 ; icolor<CMAX ; icolor++ ) {
      int col = 255-icolor ;
      gcolor[icolor] = gdImageColorAllocate(im, col, col, col);
   }

   // white background 

   gdImageFilledRectangle( im, 0, 0, XSIZE-1, YSIZE-1, white );
   gdImageSetThickness( im, LWID );

   // slide label -----------------------------------------------------------
  
   fs= 80 ;
   gdImageStringFT( im, NULL,           // imagespace, bounding box
      black , FNT, fs, 0.0,             // color, font, fontsize, angle
      fs     , fs,                      // x, y
      "Phase-synced\nmulti-beam\nradar array" );                 // text
   /*
   gdImageStringFT( im, NULL,           // imagespace, bounding box
      black , FNT, fs, 0.0,             // color, font, fontsize, angle
      fs     , 6*fs/2,                    // x, y
      "multi-beam" );                   // text

   gdImageStringFT( im, NULL,           // imagespace, bounding box
      black , FNT, fs, 0.0,             // color, font, fontsize, angle
      fs     , 9*fs/2,                  // x, y
      "radar array" );                  // text
   */
   
// draw earth and orbit segments -----------------------------------------

   double  ex0  = (double) EX0 ;
   double  ex1  = (double) EX1 ;
   double  eyc  = (double) EYC ;
   double  exc  = 0.5*(ex0+ex1) ;
   int     exci = (int) exc    ;
   double  earc = 0.5*a_sp*(double)A_NUM ;
   int     eari = (int) exc    ;
   double  ewide= 2.0*r288*sin(earc);
   double  escal= (ex1-ex0)/ewide ;

 // draw earth

   int     ew   = (int)( 2.0*re*escal );

   gdImageArc(im, exci, EYC, ew, ew, 0, 360, dgreen );

   // draw orbit fragment
   int     edeg = (int)( r2d*earc );
   int     eow  = (int)( escal*r288*2.0 );
   gdImageArc(im, exci, EYC, eow, eow, 270-edeg, 270+edeg, blue   );

   // target position
   int    etx   = exc + targetx * escal ;
   int    ety   = eyc - targety * escal ;

   // all arrays and beams
   for( i = 0 ; i < A_NUM ; i++ ) {
      int eax = (int)( exc + escal*arrx[i] );
      int eay = (int)( eyc - escal*arry[i] );
      gdImageLine( im, eax, eay, etx, ety, black ) ;
   }

   // these will be used for drawing expansion lines
   int  px0 = exci ;
   int  py0 = ety  ;

   // draw a tilted orbital ellipse representing a circular orbit

   double einc = d2r*EINC ;
   double eaop = d2r*EAOP ;
   double eang ;

   for( eang = 0.0; eang <= pi2; eang += pi2 / 2048.0 ) {
      double ex00 = targetr * sin( eang ) ;
      double ey00 = targetr * cos( eang ) ;
      double ez00 = 0.0  ;

      double ex01 = ex00 ;
      double ey01 = ey00*cos(einc) - ez00*sin(einc);
      double ez01 = ey00*sin(einc) + ez00*cos(einc);
      
      double ex02 = ex01*cos(eaop) - ey01*sin(eaop);
      double ey02 = ex01*sin(eaop) + ey01*cos(eaop);
      double ez02 = ez01 ;

      double er02 = sqrt( ex02*ex02 + ey02*ey02 );

      if( ( er02 > re ) || ( ez02 > 0.0 ) ) {
         int ex = (int)( exc +  ex02 * escal ) ;
         int ey = (int)( eyc +  ey02 * escal ) ;

         gdImageFilledEllipse( im, ex, ey, LWID, LWID, red );
      }
   }

// HUGE KLUDGE ---------------------------------------------------------
// fake loop to change what were defines
   int     define, OX0, OX1 ;
   double  SCANX, LABX, EXAG, USCALE, TUNITS ;
   char    UNITS[16], TSCALE[16];
   char    str[128] ;
   

//     SCANX  LABX  EXAG USCALE TUNITS OX0 OX1 UNITS TSCALE
for( define = 0 ; define <= 1 ; define++ ) {
   if( define == 0 ) { strcpy( str, D0 ) ; }
   if( define == 1 ) { strcpy( str, D1 ) ; }

   sscanf( str, "%lf %lf %lf %lf %lf %d %d %s %s", &SCANX,
     &LABX, &EXAG, &USCALE, &TUNITS, &OX0, &OX1, UNITS, TSCALE );

   printf( "\n%s\n", str );
   printf( "SCANX=%9.3e  ", SCANX );
   printf( "LABX=%9.3e  ",   LABX );
   printf( "EXAG=%9.3e  ",   EXAG );
   printf( "USCALE=%9.3e\n", USCALE);
   printf( "TUNITS=%9.3e  ", USCALE);
   printf( "OX0=%6d  ",   OX0 );
   printf( "OX1=%6d  ",   OX1 );
   printf( "UNITS=%s  ",   UNITS );
   printf( "TSCALE=%s\n", TSCALE );
  
// ------------------------------------------------------------------------

// plot scaling - compute x and y from pixel value

   double ox0    = (double) OX0           ; // output display left
   double ox1    = (double) OX1           ; // output display right
   double oy0    = (double) OY0           ; // output display top
   double oy1    = (double) OY1           ; // output display bottom
   int    ox, oy                          ; // output grid indices

   double scany = SCANX * (oy1-oy0)/(ox1-ox0);

   double   x_d = SCANX / (ox1-ox0) ;
   double   x_0 = targetx - ( x_d * ox0 + 0.5*SCANX ) ;
   double   y_d = -x_d ;                    // pixels increase, y gets smaller
   double   y_0 = targety - ( y_d * oy0 - 0.5*scany ) ;
   
   fs= 60 ;

   gdImageStringFT( im, NULL,           // imagespace, bounding box
      black , FNT, fs, 0.0,             // color, font, fontsize, angle
      OX0    , oy1+3*fs,                // x, y
      UNITS            );               // text

//   now compute pixel array

   double dz2 = targetz * targetz ;

#ifdef IMAGE
   for( ox=ox0 ; ox<ox1 ; ox++ ) {
      double grx = x_d*ox + x_0 ;

      for( oy=oy0 ; oy<oy1 ; oy++ ) {
         double gry = y_d*oy + y_0 ;

         // compute z distance and phase of each source to plane
         double asum = 0.0 ;

         for( idx = 0; idx < NSAT ; idx ++ ) {
            double dx = satx[idx] - grx ;
            double dy = saty[idx] - gry ;
            double dist = sqrt( dx*dx + dy*dy + dz2 );
            asum += sata[idx]* cos( k*dist - satp[idx] ) / dist ;
         }
         asum *= anorm ; // normalize

         // power/intensity computation
         int mag = (int)( EXAG*cscale*asum*asum ) ;
         if( mag > cscale ) { mag = cscale ; }

         gdImageSetPixel( im, ox, oy, gcolor[mag] ) ;
      }
   }
#endif

//  now compute path through field

   gdImageLine( im, OX0-10, GY, OX1+10, GY, black );

   double gox = -1.0 ;
   double goy = -1.0 ;

   for( ox=ox0 ; ox<ox1 ; ox++ ) {
      double grx = x_d*ox + x_0 ;
      double gry = OSLOPE*grx + targety ;
      oy  = (int)( ( gry-y_0)/ y_d ) ;

      gdImageFilledEllipse( im, ox, oy, 3, 3, red );

      // compute z distance and phase of each source to plane
      double asum = 0.0 ;

      for( idx = 0; idx < NSAT ; idx ++ ) {
         double dx = satx[idx] - grx ;
         double dy = saty[idx] - gry ;
         double dist = sqrt( dx*dx + dy*dy + dz2 );
         asum += cos( k*dist - satp[idx] ) / dist ;
      }
      asum *= anorm ; // normalize
      int  gpy = GY - (int)( ((double)GGAIN)*asum );
      if( ox == ox0 ) {
        gox = ox ;
        goy = gpy ;
      }
      gdImageLine( im, gox, goy, ox, gpy, red ) ;
      gox =  ox ;
      goy = gpy ;
   } 
   
   fs = 60;
   gdImageStringFT( im, NULL,        // imagespace, bounding
      red, FNT, fs, 0.0,             // color, font, fontsize, angle
      0, GY,                        // x, y
      "      reflected signal" );   // the text

   // draw box  ------------------------------------------------------------
   
   gdImageStringFT( im, NULL,        // imagespace, bounding
      black, FNT, fs, 0.0,           // color, font, fontsize, angle
      OX0+1.0*fs, OY0-fs/2,          // x, y
      "Standing Wave Intensity" );   // the text
 
   gdImageLine( im, OX0, OY0, OX1, OY0, black );
   gdImageLine( im, OX1, OY1, OX1, OY0, black );
   gdImageLine( im, OX1, OY1, OX0, OY1, black );
   gdImageLine( im, OX0, OY0, OX0, OY1, black );
  
   // draw ticmarks on edges ----------------------------------------------
   
   int    tx    = (int) (0.001+0.5*(SCANX/LABX))  ; // tickmark +/- count
   int    ty    = (int) (0.001+0.5*(scany/LABX))  ; // tickmark +/- count
   double ts    = (ox1-ox0)*LABX/SCANX            ; // tickmark step size

   // draw x tickmarks 

   int    oxc    = (OX0+OX1)/2                ; // center of output display
   int    oyc    = (OY0+OY1)/2                ; // center of output display

   double labcm = LABX*USCALE ;

   for( i = -tx ; i <= tx ; i++ ) {
      ox = oxc - (int) ( ts*i ) ;
      gdImageLine( im, ox, OY1, ox, OY1-5, black );
      gdImageLine( im, ox, OY0, ox, OY0+5, black );

      double  lval = -labcm * (double) i ;;
      sprintf( labstring, "%4.0f", lval );
      gdImageStringFT( im, NULL,        // imagespace, bounding
         black, FNT, fs, 0.0,           // color, font, fontsize, angle
         ox-(5*fs)/2, OY1+(3*fs)/2,     // x, y
         labstring );                   // the text
   }
   gdImageLine( im, oxc, OY1, oxc, OY1-10, black );
   gdImageLine( im, oxc, OY0, oxc, OY0+10, black );

   // draw y tickmarks

   for( i = -ty;  i <= ty ; i++ ) {
      oy = oyc - (int) ( ts*i ) ;
      gdImageLine( im, OX1, oy,  OX1-5, oy, black );
      gdImageLine( im, OX0, oy,  OX0+5, oy, black );

      double  lval = labcm * (double) i ;;
      sprintf( labstring, "%4.0f", lval );
      gdImageStringFT( im, NULL,       // imagespace, bounding
         black, FNT, fs, 0.0,          // color, font, fontsize, angle
         OX0-4*fs, oy+fs/2,            // x, y,
         labstring );                  // the text
   }

   // time estimate
 
   double  torb = TUNITS * SCANX * cos(einc) * cos(eaop) / VORB ;

   sprintf( labstring, "%5.0f %s at%5.0f m/s", torb, TSCALE, VORB );

   gdImageStringFT( im, NULL,        // imagespace, bounding
      black, FNT, fs, 0.0,           // color, font, fontsize, angle
      OX0+2*fs,  GT,                 // x, y
      labstring );                   // the text

   // draw expansion lines
   gdImageSetThickness( im, LNAR );
   gdImageLine( im, px0, py0, OX0, OY0, black );
   gdImageLine( im, px0, py0, OX0, OY1, black );
   px0 = oxc ;
   py0 = oyc ;
   gdImageSetThickness( im, LWID );
}
 
 
   // output the frame ------------------------------------------------------

   pngout = fopen( PNGFMT, "wb");
   gdImagePngEx( im, pngout, 1 );
   gdImageDestroy(im);
   fclose(pngout);
}
