/*  XNECVIEW - a program for visualizing NEC2 input and output data
 *
 *  Copyright (C) 1998-2020, Pieter-Tjerk de Boer -- pa3fwm@amsat.org
 *
 *  Distributed on the conditions of version 2 of the GPL: see the files
 *  README and COPYING, which accompany this source file.
 */

/* ------------------------------------------------------------------------ */


#include "config.h"        /* file defining things like default colours etc. */


#define VERSION "1.37"


typedef struct {     /* structure to describe a point in 3D space */ 
   float x,y,z;
} Point;

typedef struct {     /* structure to describe a straight wire (segment) */
   Point p0,p1;       /* begin and end point */
   double rad;        /* radius */
   int itg;           /* tag number */
   int ns;            /* number of segments for multi-segment wire; -segmentnumber for single-segment wire that's part of a larger wire (GA and GH cards)  */
   int abs;           /* absolute segment number of the first segment of this wire */
   int dispnr;        /* flag: true (nonzero) on any multi-segment wire, and on (a) middle segment for a wire that was split into multiple single-segment wires (GA and GH) */
} Wire;

typedef struct {     /* structure to describe a surface patch */
   int type;          /* see below */
   Point p0,p1,p2,p3; /* corners */
   Point pc;          /* center */
   Point pa;          /* tip of outward normal arrow */
} Surface;
#define SU_rect 1     /* rectangular shape (p2,p3 ignored) */
#define SU_arb 2      /* "arbitrary" shape, shown as a set of 8 crossing lines (p2,p3 ignored) */
#define SU_tri 3      /* triangle (p3 ignored) */
#define SU_quad 4     /* quadrilateral shape */

typedef struct {    /* structure to describe an excitation (only voltage sources, EX card) */
   Wire *wire;        /* pointer to Wire data */
   int seg;           /* segment number within wire */
} Exci;

typedef struct {    /* structure to describe a load */
   Wire *wire;        /* pointer to Wire data */
   int firstseg;      /* number of first loaded segment within wire */
   int lastseg;       /* number of last loaded segment within wire */
} Load;               /* note: if an LD card specifies a loading which extends over several wires, it is represented here by one Load struct per wire */

typedef struct {    /* structure to describe a "network" in the antenna structure; a transmission line is a special case of this */
   Wire *wire1;       /* pointer to Wire data of one end of the network / transmission line */
   int seg1;          /* segment number within wire */
   Wire *wire2;       /* same for the other end of the network / transmission line */
   int seg2;
   int type;          /* type: 0 = generic network; 1 = transmission line without phase reversal; -1 = transmission line with phase reversal */
} Netw;


extern double extr_str;	/* size of the biggest dimension of the structure data to be shown; used for (initial) scaling */
extern double extr;	/* size of the biggest dimension of all data to be shown; used for (initial) scaling */

extern int quick;         /* flag to indicate that drawing should be rough but quick */
extern int redraw;        /* flag which signifies need for redrawing */
extern int dragging;      /* flag to indicate that user is dragging the structure */

                            /* these define the projection: */
extern double phi,theta;      /* angles defining direction of eye */
extern double zoom;           /* zoom factor */
extern double trx,try;        /* 2D-translation, as a fraction of winsize */
extern int winsizex,winsizey; /* size of window in pixels */
extern int winsize;          /* min(winsizex,winsizey) */

extern int gainscale;	     /* type of scaling of gain: */
enum { GSlinpower,GSlinvolt,GSarrl,GSlog,numGS };
extern char *GSnames[];      /* array with names of gainscales */
extern char GSnumbers[][32]; /* array with descriptions of levels of scale lines in plot */

extern int gainplot;         /* type of radiation plot: */
enum { GPnone,GPslice,GPframe,GPopaque,GPnearfield,numGP };

extern int structplot;       /* type of structure plot: */
enum { SPnone,SPstruct,SPtags,SPcurrents,SPanim,numSP };

extern int scaleplot;        /* flag: plot gain scale? */

extern int polarization;     /* polarization for gain and currents displays */
enum { POLnone,POLhor,POLvert,POLlhcp,POLrhcp,POLcolour };   /* note: if this order changes, find_fb() may also need to be changed */

extern int phasedirlock;     /* flag: is the direction used for calculation phases locked? */
extern double phasephi,phasetheta;   /* if so, this is that direction; otherwise, it is identical to phi and theta */

extern double phaseoffset;   /* phase offset for currents display */
extern int distcor;          /* flag: correct phase for distance to observer? */
extern int maxthickness;     /* line thickness used for maximum current in currents display */

extern double animphase;     /* phase (in rad) of the "animation" of currents and E and H fields */
extern double animfreq;      /* frequency of the animation: number of displayed cycles per second */
extern double anim_update_interval;  /* time (in milliseconds) between updates of the animation display */

extern double escale;        /* scale factor for the E field */
extern double hscale;        /* scale factor for the H field */
extern int show_p;           /* flag: display Poynting vector? */
extern double qscale;        /* scale factor for the currents */
extern double iscale;        /* scale factor for the charges */


extern int win2sizex,win2sizey; /* size of freqplot window in pixels */
extern int plot2_swr, plot2_z, plot2_z2, plot2_maxgain, plot2_dir, plot2_vgain; /* flags for the each of the frequency plots in window 2 */

extern int numwires;
extern int maxwires;
extern Wire *wires;
extern int numsurfaces;
extern int maxsurfaces;
extern Surface *surfaces;
extern int numexcis;
extern int maxexcis;
extern Exci *excis;
extern int numloads;
extern int maxloads;
extern Load *loads;
extern int numnetws;
extern int maxnetws;
extern Netw *netws;


typedef struct {     /* structure to contain, for one direction, the radiated power and polarization information */
   float total;   /* total radiated power, dBi */
   float axial;   /* axial ratio, with sign indicating the polarity: + left-hand circular, - right-hand */
   float tilt;    /* tilt angle of major axis, radians; defined as in NEC output: 0 = vertical, >0 topleft/bottomright */
} Gain;

typedef struct RAdpattern {    /* structure to contain (far field) radiation pattern for one frequency */
   double *gtheta;  /* pointer to array of theta values */
   double *gphi;    /* pointer to array of phi values */
   Gain **gain;     /* pointer to an array of pointers (one for every phi) to an array of Gain structs (one for every theta at that phi) */
   Point **gpo;     /* pointer to an array of pointers (one for every phi) to an array of Points (one for every theta at that phi) */
   int numtheta;    /* number of theta values */
   int numphi;      /* number of phi values */
   struct RAdpattern *next;    /* pointer to the next such structure, used if more than one set of radiation pattern data is available for one frequency */
   double *sin_gtheta;   /* pointer to an array containing the sines of the theta values */
   double *sin_gphi;     /* ... etc. for phi */
   double *cos_gtheta;   /* ... and cosines */
   double *cos_gphi;
} Radpattern;


typedef struct {   /* structure to contain the current (phase, magnitude, location, direction) information of one segment */
   /* note: part of the below information is in principle also available in the
      arrays describing the antenna's structure as read from the NEC input file;
      however, it seems more consistent to read this structure information now from the
      NEC output file, so we're sure we have the right data even if the input and output
      files are (accidentally) from different models.
   */
   /* note2: the current in surface patches is also represented by the below structure;
      basically, a surface patch current is transformed into two equivalent segments,
      although in many practical cases one of them carries almost no current and can
      therefore be omitted.
   */
   /* note3: for the animations, it is more convenient to have real and imaginary
      vectors. For segment currents, these obviously point in the same (or opposite)
      directions, namely along the segment. For surface currents, they are non-
      trivial; however, we don't need a second segment here (in contrast to the
      other representation), so only the first 'numanimseg' (see below, Currents
      structure) segments contain valid data in re[] and im[].
   */
   /* note4: although the name suggests otherwise, this structure can also contain
      charge information.
   */
   Point p0,p1;  /* coordinates of segment endpoints */
   Point c;      /* coordinate of segment center point */
   float a;     /* amplitude of the current */
   float phase; /* phase of the current (degrees) */
   float dist;  /* distance of center of segment from the plane through the origin, perpendicular to the viewing direction; positive if in front of this plane, negative otherwise */
   float area;  /* area of a surface patch */
   float re[3],im[3]; /* real and imaginary vector */
   float q0[3],q1[3]; /* two unity vectors, orthogonal to each other and to the direction of the segment; used for drawing charges */
   float qre,qim;  /* real and imaginary value of the charge */
} Segcur;

typedef struct {   /* structure to contain the current distribution for all segments at one frequency */
   Segcur *s;
   int maxseg;       /* current size of s[] */
   int numseg;       /* number of valid entries in s[] */
   int numanimseg;   /* number of those that are needed for the animations; i.e., excluding the extra segments caused by the decomposition of the surface patch currents */
   int numrealseg;   /* number of those that are actually segments; so s[numrealseg...numseg-1] are surface patches */
   double maxI;      /* highest segment current */
   double maxQ;      /* highest segment charge */
} Currents;


typedef struct NF { /* structure to contain the electric and magnetic field at some point (near the antenna) */
   Point p;           /* location of the point */
   int evalid,hvalid; /* flags to indicate presence of E and H data */
   float ere[3];     /* real component of E vector */
   float eim[3];     /* imaginary component of E vector */
   float hre[3];     /* real component of H vector */
   float him[3];     /* imaginary component of H vector */
   float poy[3];     /* time-averaged Poynting vector */
   struct NF *next;   /* pointer to next datapoint */
} Nearfield;



/* indices in the element 'd' of the NECoutput structure defined below: */
enum {
   neco_zr, neco_zi,       /* index of real and imaginary part of impedance */
   neco_zphi, neco_zabs,   /* phi and absolute value of impedance */
   neco_swr,               /* index of SWR */
   neco_maxgain, neco_fb,  /* index of maximum gain and corresponding front/back ratio */
   neco_phi, neco_theta,   /* index direction of maximum gain */
   neco_vgain, neco_vfb,   /* index of gain in viewing direction and corresponding f/b ratio */
   neco_vgain2, neco_vfb2, /* same, but taking polarization into account; fb/fb2 as fb1/fb2 below */
   neco_maxgain_hor, neco_fb_hor1, neco_fb_hor2, neco_phi_hor, neco_theta_hor,
   neco_maxgain_vert, neco_fb_vert1, neco_fb_vert2, neco_phi_vert, neco_theta_vert,
   neco_maxgain_lhcp, neco_fb_lhcp1, neco_fb_lhcp2, neco_phi_lhcp, neco_theta_lhcp,
   neco_maxgain_rhcp, neco_fb_rhcp1, neco_fb_rhcp2, neco_phi_rhcp, neco_theta_rhcp,
   neco_numdat             /* last element of the enum: number of elements in the array */
};
/* since (maximum) gain and corresponding quantities are not available in all NECoutput records,
   the following macro is needed to tell whether a quantity is available always, or only if
   radiation pattern data is available in the record: */
#define ONLY_IF_RP(i) ((i)>=neco_maxgain)
/* a constant for more systematic access to the polarization-specific fields: */
#define Neco_gsize 5     /* group size */
#define Neco_polgain (neco_maxgain_hor-Neco_gsize)
#define Neco_polfb1 (neco_fb_hor1-Neco_gsize)
#define Neco_polfb2 (neco_fb_hor2-Neco_gsize)
#define Neco_polphi (neco_phi_hor-Neco_gsize)
#define Neco_poltheta (neco_theta_hor-Neco_gsize)
/* now, we can write say  neco_phi_lhcp  as  Neco_gsize*POLlhcp+Neco_polphi  */
/* note: fb...1 is f/b ratio for this polarization; fb...2 is same but for back total power is used */

typedef struct {    /* structure to contain all output data that NEC produces for one frequency */
   double f;          /* frequency in MHz */
   Radpattern *rp;    /* pointer to radiation pattern data, if available; NULL otherwise */
   double d[neco_numdat];
   int rpgpovalid;    /* flag: !=0 if the gpo field of *rp already contains valid data for the present settings */
   Currents *cu;      /* pointer to currents data, if available; NULL otherwise */
   Nearfield *nf;     /* pointer to nearfield data, if available; NULL otherwise */
   double maxe,maxh;  /* largest values of E and H in nearfield data */
} NECoutput;

extern NECoutput *neco;
extern int numneco;
extern int maxfreqs;

extern double globalmaxdb; /* maximum gain, global over all frequencies and all output data files */

extern int rp_index;       /* index of the entry in neco[] whose radiation pattern is being shown in the 3D plot */

extern double r0;          /* reference impedance for SWR calculations */

extern int window1open;
extern int window2open;


extern char *inputfilename;

extern int fontheight;

#ifndef GdkColor
   #include <gdk/gdk.h>
#endif
#define c_bg    col[0]
#define c_axis  col[1]
#define c_wire  col[2]
#define c_surf  col[3]
#define c_back  col[4]
#define c_gain  col[5]
#define c_scale col[6]
#define c_exci  col[7]
#define c_load  col[8]
#define c_netw  col[9]
#define c_inactive col[10]
#define c_efield   col[11]
#define c_hfield   col[12]
#define c_poynting col[13]
#define c_qpos     col[14]
#define c_qneg     col[15]
#define c_gain_lin   col[16]
#define c_gain_lhcp  col[17]
#define c_gain_rhcp  col[18]
#define c_currents (col+19)
#define NC NC_phase+19
extern GdkColor col[NC];



typedef struct {
   void (*SetLineAttributes)(unsigned int,int,int,int);
   void (*DrawLine)(double,double,double,double);
   void (*SetForeground)(GdkColor *);
   void (*ClearWindow)();
   void (*DrawString)(double,double,char*,double,double);
   void (*Complete)();
   void (*SetClipRectangle)(double,double,double,double);
   void (*ClearRectangle)(double,double,double,double);
} Outdev;

extern Outdev *out;   /* pointer to Outdev struct, determines destination of drawing commands issued through the below macros: */

#define SetLineAttributes(a,b,c,d) out->SetLineAttributes((a),(b),(c),(d))
#define DrawLine(a,b,c,d) out->DrawLine((a),(b),(c),(d))
#define SetForeground(a) out->SetForeground(a)
#define ClearWindow() out->ClearWindow()
#define DrawStringLL(a,b,c) DrawString(a,b,c,0,0)     /* draw text specifying lower left corner */
#define DrawStringUL(a,b,c) DrawString(a,b,c,0,1)     /* draw text specifying upper left corner */
#define DrawString(a,b,c,d,e) out->DrawString(a,b,c,d,e) /* draw text specifying a corner defined by d and e:
                                                            d = 0...1 for left...right; e = 0...1 for lower...upper  */
#define SetClipRectangle(a,b,c,d) out->SetClipRectangle(a,b,c,d)  /* set clip rectangle to (a,b)..(c,d) */
#define ClearRectangle(a,b,c,d) out->ClearRectangle(a,b,c,d)  /* clear part of window */


void removecommas(char *p);                 /* change commas in string into spaces */

void updateextremes(Point *p);              /* update the value of extr_str with information about this Point */

void read_nec_input(FILE *f);               /* read the input file, and update the arrays of wires and surfaces accordingly */

int read_nec_output(FILE *f);               /* tries to read NEC output data from f, updates gain distribution arrays; returns 1 if not succesful */

void process_nec_output(NECoutput *ne);     /* transform the gain data array into an array of points in 3D space */

void calc_vgain(void);                      /* update the vgain records in accordance with the viewing direction */

void mark_gpo_invalid(void);                /* mark gpo fields as invalid; must be called after every change of gain scale */

void calcswr(NECoutput *ne);                /* calculate the SWR from zr and zi */

void calcphiabs(NECoutput *ne);             /* calculate phi and abs of impedance from zr and zi */

void initX(int *argcp,char **argv);         /* initialize the X-window stuff */

void mainX(int really);                     /* main loop (wait for event, process event, redisplay) */

int Pending(void);                          /* test whether events are pending; returns !=0 if present (drawing) action should be interrupted */

void reread(void);                          /* reread the input file(s) */

void process_optionstring(char *s);         /* process a set of options, contained in the string s */

void draw_all(int ie);                      /* draw antenna structure and/or gain pattern; ie=interupt_enable */

void calcproj(void);                        /* calculate the projection matrix etc. */

int write_png(int which,const char *filename);    /* write window 1 or 2 to an PNG file; return !=0 in case of error */

int write_postscript(const char *filename,void (*drawfn)(int),int xsize, int ysize);
                                            /* write postscript to file; return !=0 in case of error */

void draw_all2(int dummy);                  /* draw plot(s) vs. frequency */

double xfreq(int x);                        /* return frequency corresponding to X coordinate in frequency plot */
int freqx(double f);                        /* return X coordinate corresponding to frequency */
int freqindex(double f);                    /* return index in neco[] of data closest to frequency f */

void *mymalloc(size_t n);                   /* like malloc(), but exits with error message if memory not available; implementation in parse_output.c */
void *myrealloc(void *,size_t n);           /* like realloc(), but exits with error message if memory not available; implementation in parse_output.c */

void draw_opaque(Radpattern *);             /* draw radiation pattern as an opaque surface */

void setpolcolour(double axial);            /* set the colour corresponding to this axial ratio */

char *opaque_impossible(Radpattern *rp);    /* returns NULL if opaque drawing of this Radpattern is possible; otherwise, pointer to string with explanation */


/* a macro for conveniently expanding array sizes: */
#define EXPAND_IF_NECESSARY(num,max,pointer)                \
   if (num>=max) {                                          \
      while (max<num+1) max=max*2;                          \
      pointer=myrealloc(pointer,max*(sizeof(*pointer)));    \
   }

/* if your system doesn't have the function drem() available, uncomment
   the following line and recompile (after deleting *.o)
*/
/*  #define drem(x,y) ( (x) - (y)*rint((x)/(y)) )   */

