move gbp.conf to debian/
[debian/splat] / splat.cpp
1 /****************************************************************************\
2 *          SPLAT!: An RF Signal Path Loss And Terrain Analysis Tool          *
3 ******************************************************************************
4 *            Project started in 1997 by John A. Magliacane, KD2BD            *
5 *                         Last update: 10-Apr-2009                           *
6 ******************************************************************************
7 *         Please consult the documentation for a complete list of            *
8 *            individuals who have contributed to this project.               *
9 ******************************************************************************
10 *                                                                            *
11 *  This program is free software; you can redistribute it and/or modify it   *
12 *  under the terms of the GNU General Public License as published by the     *
13 *  Free Software Foundation; either version 2 of the License or any later    *
14 *  version.                                                                  *
15 *                                                                            *
16 *  This program is distributed in the hope that it will useful, but WITHOUT  *
17 *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or     *
18 *  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License     *
19 *  for more details.                                                         *
20 *                                                                            *
21 ******************************************************************************
22 * g++ -Wall -O3 -s -lm -lbz2 -fomit-frame-pointer itm.cpp splat.cpp -o splat * 
23 \****************************************************************************/
24
25 #include <stdio.h>
26 #include <math.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <ctype.h>
30 #include <bzlib.h>
31 #include <unistd.h>
32 #include "fontdata.h"
33 #include "splat.h"
34
35 #define GAMMA 2.5
36 #define BZBUFFER 65536
37
38 #if HD_MODE==0
39         #if MAXPAGES==4
40         #define ARRAYSIZE 4950
41         #endif
42
43         #if MAXPAGES==9
44         #define ARRAYSIZE 10870
45         #endif
46
47         #if MAXPAGES==16
48         #define ARRAYSIZE 19240
49         #endif
50
51         #if MAXPAGES==25
52         #define ARRAYSIZE 30025
53         #endif
54
55         #if MAXPAGES==36
56         #define ARRAYSIZE 43217
57         #endif
58
59         #if MAXPAGES==49
60         #define ARRAYSIZE 58813
61         #endif
62
63         #if MAXPAGES==64
64         #define ARRAYSIZE 76810
65         #endif
66
67         #define IPPD 1200
68 #endif
69
70 #if HD_MODE==1
71         #if MAXPAGES==1
72         #define ARRAYSIZE 5092 
73         #endif
74
75         #if MAXPAGES==4
76         #define ARRAYSIZE 14844 
77         #endif
78
79         #if MAXPAGES==9
80         #define ARRAYSIZE 32600
81         #endif
82
83         #if MAXPAGES==16
84         #define ARRAYSIZE 57713
85         #endif
86
87         #if MAXPAGES==25
88         #define ARRAYSIZE 90072
89         #endif
90
91         #if MAXPAGES==36
92         #define ARRAYSIZE 129650
93         #endif
94
95         #if MAXPAGES==49 
96         #define ARRAYSIZE 176437
97         #endif
98
99         #if MAXPAGES==64
100         #define ARRAYSIZE 230430
101         #endif
102
103         #define IPPD 3600
104 #endif
105
106 #ifndef PI
107 #define PI 3.141592653589793
108 #endif
109
110 #ifndef TWOPI
111 #define TWOPI 6.283185307179586
112 #endif
113
114 #ifndef HALFPI
115 #define HALFPI 1.570796326794896
116 #endif
117
118 #define DEG2RAD 1.74532925199e-02
119 #define EARTHRADIUS 20902230.97
120 #define METERS_PER_MILE 1609.344
121 #define METERS_PER_FOOT 0.3048
122 #define KM_PER_MILE 1.609344
123 #define FOUR_THIRDS 1.3333333333333
124
125 char    string[255], sdf_path[255], opened=0, gpsav=0, splat_name[10],
126         splat_version[6], dashes[80];
127
128 double  earthradius, max_range=0.0, forced_erp=-1.0, dpp, ppd,
129         fzone_clearance=0.6, forced_freq, clutter;
130
131 int     min_north=90, max_north=-90, min_west=360, max_west=-1, ippd, mpi,
132         max_elevation=-32768, min_elevation=32768, bzerror, contour_threshold;
133
134 unsigned char got_elevation_pattern, got_azimuth_pattern, metric=0, dbm=0;
135
136 struct site {   double lat;
137                 double lon;
138                 float alt;
139                 char name[50];
140                 char filename[255];
141             }   site;
142
143 struct path {   double lat[ARRAYSIZE];
144                 double lon[ARRAYSIZE];
145                 double elevation[ARRAYSIZE];
146                 double distance[ARRAYSIZE];
147                 int length;
148             }   path;
149
150 struct dem {    int min_north;
151                 int max_north;
152                 int min_west;
153                 int max_west;
154                 int max_el;
155                 int min_el;
156                 short data[IPPD][IPPD];
157                 unsigned char mask[IPPD][IPPD];
158                 unsigned char signal[IPPD][IPPD];
159            }    dem[MAXPAGES];
160
161 struct LR {     double eps_dielect; 
162                 double sgm_conductivity; 
163                 double eno_ns_surfref;
164                 double frq_mhz; 
165                 double conf; 
166                 double rel;
167                 double erp;
168                 int radio_climate;  
169                 int pol;
170                 float antenna_pattern[361][1001];
171           }     LR;
172
173 struct region { unsigned char color[32][3];
174                 int level[32];
175                 int levels;
176               } region;
177
178 double elev[ARRAYSIZE+10];
179
180 void point_to_point(double elev[], double tht_m, double rht_m,
181           double eps_dielect, double sgm_conductivity, double eno_ns_surfref,
182           double frq_mhz, int radio_climate, int pol, double conf,
183           double rel, double &dbloss, char *strmode, int &errnum);
184
185 double arccos(double x, double y)
186 {
187         /* This function implements the arc cosine function,
188            returning a value between 0 and TWOPI. */
189
190         double result=0.0;
191
192         if (y>0.0)
193                 result=acos(x/y);
194
195         if (y<0.0)
196                 result=PI+acos(x/y);
197
198         return result;
199 }
200
201 int ReduceAngle(double angle)
202 {
203         /* This function normalizes the argument to
204            an integer angle between 0 and 180 degrees */
205
206         double temp;
207
208         temp=acos(cos(angle*DEG2RAD));
209
210         return (int)rint(temp/DEG2RAD);
211 }
212
213 double LonDiff(double lon1, double lon2)
214 {
215         /* This function returns the short path longitudinal
216            difference between longitude1 and longitude2 
217            as an angle between -180.0 and +180.0 degrees.
218            If lon1 is west of lon2, the result is positive.
219            If lon1 is east of lon2, the result is negative. */
220
221         double diff;
222
223         diff=lon1-lon2;
224
225         if (diff<=-180.0)
226                 diff+=360.0;
227
228         if (diff>=180.0)
229                 diff-=360.0;
230
231         return diff;
232 }
233
234 char *dec2dms(double decimal)
235 {
236         /* Converts decimal degrees to degrees, minutes, seconds,
237            (DMS) and returns the result as a character string. */
238
239         char    sign;
240         int     degrees, minutes, seconds;
241         double  a, b, c, d;
242
243         if (decimal<0.0)
244         {
245                 decimal=-decimal;
246                 sign=-1;
247         }
248
249         else
250                 sign=1;
251
252         a=floor(decimal);
253         b=60.0*(decimal-a);
254         c=floor(b);
255         d=60.0*(b-c);
256
257         degrees=(int)a;
258         minutes=(int)c;
259         seconds=(int)d;
260
261         if (seconds<0)
262                 seconds=0;
263
264         if (seconds>59)
265                 seconds=59;
266
267         string[0]=0;
268         snprintf(string,250,"%d%c %d\' %d\"", degrees*sign, 176, minutes, seconds);
269         return (string);
270 }
271
272 int PutMask(double lat, double lon, int value)
273 {
274         /* Lines, text, markings, and coverage areas are stored in a
275            mask that is combined with topology data when topographic
276            maps are generated by SPLAT!.  This function sets and resets
277            bits in the mask based on the latitude and longitude of the
278            area pointed to. */
279
280         int     x, y, indx;
281         char    found;
282
283         for (indx=0, found=0; indx<MAXPAGES && found==0;)
284         {
285                 x=(int)rint(ppd*(lat-dem[indx].min_north));
286                 y=mpi-(int)rint(ppd*(LonDiff(dem[indx].max_west,lon)));
287
288                 if (x>=0 && x<=mpi && y>=0 && y<=mpi)
289                         found=1;
290                 else
291                         indx++;
292         }
293
294         if (found)
295         {
296                 dem[indx].mask[x][y]=value;
297                 return ((int)dem[indx].mask[x][y]);
298         }
299
300         else
301                 return -1;
302 }
303
304 int OrMask(double lat, double lon, int value)
305 {
306         /* Lines, text, markings, and coverage areas are stored in a
307            mask that is combined with topology data when topographic
308            maps are generated by SPLAT!.  This function sets bits in
309            the mask based on the latitude and longitude of the area
310            pointed to. */
311
312         int     x, y, indx;
313         char    found;
314
315         for (indx=0, found=0; indx<MAXPAGES && found==0;)
316         {
317                 x=(int)rint(ppd*(lat-dem[indx].min_north));
318                 y=mpi-(int)rint(ppd*(LonDiff(dem[indx].max_west,lon)));
319
320                 if (x>=0 && x<=mpi && y>=0 && y<=mpi)
321                         found=1;
322                 else
323                         indx++;
324         }
325
326         if (found)
327         {
328                 dem[indx].mask[x][y]|=value;
329                 return ((int)dem[indx].mask[x][y]);
330         }
331
332         else
333                 return -1;
334 }
335
336 int GetMask(double lat, double lon)
337 {
338         /* This function returns the mask bits based on the latitude
339            and longitude given. */
340
341         return (OrMask(lat,lon,0));
342 }
343
344 int PutSignal(double lat, double lon, unsigned char signal)
345 {
346         /* This function writes a signal level (0-255)
347            at the specified location for later recall. */
348
349         int     x, y, indx;
350         char    found;
351
352         for (indx=0, found=0; indx<MAXPAGES && found==0;)
353         {
354                 x=(int)rint(ppd*(lat-dem[indx].min_north));
355                 y=mpi-(int)rint(ppd*(LonDiff(dem[indx].max_west,lon)));
356
357                 if (x>=0 && x<=mpi && y>=0 && y<=mpi)
358                         found=1;
359                 else
360                         indx++;
361         }
362
363         if (found)
364         {
365                 dem[indx].signal[x][y]=signal;
366                 return (dem[indx].signal[x][y]);
367         }
368
369         else
370                 return 0;
371 }
372
373 unsigned char GetSignal(double lat, double lon)
374 {
375         /* This function reads the signal level (0-255) at the
376            specified location that was previously written by the
377            complimentary PutSignal() function. */
378
379         int     x, y, indx;
380         char    found;
381
382         for (indx=0, found=0; indx<MAXPAGES && found==0;)
383         {
384                 x=(int)rint(ppd*(lat-dem[indx].min_north));
385                 y=mpi-(int)rint(ppd*(LonDiff(dem[indx].max_west,lon)));
386
387                 if (x>=0 && x<=mpi && y>=0 && y<=mpi)
388                         found=1;
389                 else
390                         indx++;
391         }
392
393         if (found)
394                 return (dem[indx].signal[x][y]);
395         else
396                 return 0;
397 }
398
399 double GetElevation(struct site location)
400 {
401         /* This function returns the elevation (in feet) of any location
402            represented by the digital elevation model data in memory.
403            Function returns -5000.0 for locations not found in memory. */
404
405         char    found;
406         int     x, y, indx;
407         double  elevation;
408
409         for (indx=0, found=0; indx<MAXPAGES && found==0;)
410         {
411                 x=(int)rint(ppd*(location.lat-dem[indx].min_north));
412                 y=mpi-(int)rint(ppd*(LonDiff(dem[indx].max_west,location.lon)));
413
414                 if (x>=0 && x<=mpi && y>=0 && y<=mpi)
415                         found=1;
416                 else
417                         indx++;
418         }
419
420         if (found)
421                 elevation=3.28084*dem[indx].data[x][y];
422         else
423                 elevation=-5000.0;
424         
425         return elevation;
426 }
427
428 int AddElevation(double lat, double lon, double height)
429 {
430         /* This function adds a user-defined terrain feature
431            (in meters AGL) to the digital elevation model data
432            in memory.  Does nothing and returns 0 for locations
433            not found in memory. */
434
435         char    found;
436         int     x, y, indx;
437
438         for (indx=0, found=0; indx<MAXPAGES && found==0;)
439         {
440                 x=(int)rint(ppd*(lat-dem[indx].min_north));
441                 y=mpi-(int)rint(ppd*(LonDiff(dem[indx].max_west,lon)));
442
443                 if (x>=0 && x<=mpi && y>=0 && y<=mpi)
444                         found=1;
445                 else
446                         indx++;
447         }
448
449         if (found)
450                 dem[indx].data[x][y]+=(short)rint(height);
451
452         return found;
453 }
454
455 double Distance(struct site site1, struct site site2)
456 {
457         /* This function returns the great circle distance
458            in miles between any two site locations. */
459
460         double  lat1, lon1, lat2, lon2, distance;
461
462         lat1=site1.lat*DEG2RAD;
463         lon1=site1.lon*DEG2RAD;
464         lat2=site2.lat*DEG2RAD;
465         lon2=site2.lon*DEG2RAD;
466
467         distance=3959.0*acos(sin(lat1)*sin(lat2)+cos(lat1)*cos(lat2)*cos((lon1)-(lon2)));
468
469         return distance;
470 }
471
472 double Azimuth(struct site source, struct site destination)
473 {
474         /* This function returns the azimuth (in degrees) to the
475            destination as seen from the location of the source. */
476
477         double  dest_lat, dest_lon, src_lat, src_lon,
478                 beta, azimuth, diff, num, den, fraction;
479
480         dest_lat=destination.lat*DEG2RAD;
481         dest_lon=destination.lon*DEG2RAD;
482
483         src_lat=source.lat*DEG2RAD;
484         src_lon=source.lon*DEG2RAD;
485                 
486         /* Calculate Surface Distance */
487
488         beta=acos(sin(src_lat)*sin(dest_lat)+cos(src_lat)*cos(dest_lat)*cos(src_lon-dest_lon));
489
490         /* Calculate Azimuth */
491
492         num=sin(dest_lat)-(sin(src_lat)*cos(beta));
493         den=cos(src_lat)*sin(beta);
494         fraction=num/den;
495
496         /* Trap potential problems in acos() due to rounding */
497
498         if (fraction>=1.0)
499                 fraction=1.0;
500
501         if (fraction<=-1.0)
502                 fraction=-1.0;
503
504         /* Calculate azimuth */
505
506         azimuth=acos(fraction);
507
508         /* Reference it to True North */
509
510         diff=dest_lon-src_lon;
511
512         if (diff<=-PI)
513                 diff+=TWOPI;
514
515         if (diff>=PI)
516                 diff-=TWOPI;
517
518         if (diff>0.0)
519                 azimuth=TWOPI-azimuth;
520
521         return (azimuth/DEG2RAD);               
522 }
523
524 double ElevationAngle(struct site source, struct site destination)
525 {
526         /* This function returns the angle of elevation (in degrees)
527            of the destination as seen from the source location.
528            A positive result represents an angle of elevation (uptilt),
529            while a negative result represents an angle of depression
530            (downtilt), as referenced to a normal to the center of
531            the earth. */
532            
533         register double a, b, dx;
534
535         a=GetElevation(destination)+destination.alt+earthradius;
536         b=GetElevation(source)+source.alt+earthradius;
537
538         dx=5280.0*Distance(source,destination);
539
540         /* Apply the Law of Cosines */
541
542         return ((180.0*(acos(((b*b)+(dx*dx)-(a*a))/(2.0*b*dx)))/PI)-90.0);
543 }
544
545 void ReadPath(struct site source, struct site destination)
546 {
547         /* This function generates a sequence of latitude and
548            longitude positions between source and destination
549            locations along a great circle path, and stores
550            elevation and distance information for points
551            along that path in the "path" structure. */
552
553         int     c;
554         double  azimuth, distance, lat1, lon1, beta, den, num,
555                 lat2, lon2, total_distance, dx, dy, path_length,
556                 miles_per_sample, samples_per_radian=68755.0;
557         struct  site tempsite;
558
559         lat1=source.lat*DEG2RAD;
560         lon1=source.lon*DEG2RAD;
561
562         lat2=destination.lat*DEG2RAD;
563         lon2=destination.lon*DEG2RAD;
564
565         if (ppd==1200.0)
566                 samples_per_radian=68755.0;
567
568         if (ppd==3600.0)
569                 samples_per_radian=206265.0;
570
571         azimuth=Azimuth(source,destination)*DEG2RAD;
572
573         total_distance=Distance(source,destination);
574
575         if (total_distance>(30.0/ppd))          /* > 0.5 pixel distance */
576         {
577                 dx=samples_per_radian*acos(cos(lon1-lon2));
578                 dy=samples_per_radian*acos(cos(lat1-lat2));
579
580                 path_length=sqrt((dx*dx)+(dy*dy));              /* Total number of samples */
581
582                 miles_per_sample=total_distance/path_length;    /* Miles per sample */
583         }
584
585         else
586         {
587                 c=0;
588                 dx=0.0;
589                 dy=0.0;
590                 path_length=0.0;
591                 miles_per_sample=0.0;
592                 total_distance=0.0;
593
594                 lat1=lat1/DEG2RAD;
595                 lon1=lon1/DEG2RAD;
596
597                 path.lat[c]=lat1;
598                 path.lon[c]=lon1;
599                 path.elevation[c]=GetElevation(source);
600                 path.distance[c]=0.0;
601         }
602
603         for (distance=0.0, c=0; (total_distance!=0.0 && distance<=total_distance && c<ARRAYSIZE); c++, distance=miles_per_sample*(double)c)
604         {
605                 beta=distance/3959.0;
606                 lat2=asin(sin(lat1)*cos(beta)+cos(azimuth)*sin(beta)*cos(lat1));
607                 num=cos(beta)-(sin(lat1)*sin(lat2));
608                 den=cos(lat1)*cos(lat2);
609
610                 if (azimuth==0.0 && (beta>HALFPI-lat1))
611                         lon2=lon1+PI;
612
613                 else if (azimuth==HALFPI && (beta>HALFPI+lat1))
614                                 lon2=lon1+PI;
615
616                 else if (fabs(num/den)>1.0)
617                                 lon2=lon1;
618
619                 else
620                 {
621                         if ((PI-azimuth)>=0.0)
622                                 lon2=lon1-arccos(num,den);
623                         else
624                                 lon2=lon1+arccos(num,den);
625                 }
626         
627                 while (lon2<0.0)
628                         lon2+=TWOPI;
629
630                 while (lon2>TWOPI)
631                         lon2-=TWOPI;
632  
633                 lat2=lat2/DEG2RAD;
634                 lon2=lon2/DEG2RAD;
635
636                 path.lat[c]=lat2;
637                 path.lon[c]=lon2;
638                 tempsite.lat=lat2;
639                 tempsite.lon=lon2;
640                 path.elevation[c]=GetElevation(tempsite);
641                 path.distance[c]=distance;
642         }
643
644         /* Make sure exact destination point is recorded at path.length-1 */
645
646         if (c<ARRAYSIZE)
647         {
648                 path.lat[c]=destination.lat;
649                 path.lon[c]=destination.lon;
650                 path.elevation[c]=GetElevation(destination);
651                 path.distance[c]=total_distance;
652                 c++;
653         }
654
655         if (c<ARRAYSIZE)
656                 path.length=c;
657         else
658                 path.length=ARRAYSIZE-1;
659 }
660
661 double ElevationAngle2(struct site source, struct site destination, double er)
662 {
663         /* This function returns the angle of elevation (in degrees)
664            of the destination as seen from the source location, UNLESS
665            the path between the sites is obstructed, in which case, the
666            elevation angle to the first obstruction is returned instead.
667            "er" represents the earth radius. */
668
669         int     x;
670         char    block=0;
671         double  source_alt, destination_alt, cos_xmtr_angle,
672                 cos_test_angle, test_alt, elevation, distance,
673                 source_alt2, first_obstruction_angle=0.0;
674         struct  path temp;
675
676         temp=path;
677
678         ReadPath(source,destination);
679
680         distance=5280.0*Distance(source,destination);
681         source_alt=er+source.alt+GetElevation(source);
682         destination_alt=er+destination.alt+GetElevation(destination);
683         source_alt2=source_alt*source_alt;
684
685         /* Calculate the cosine of the elevation angle of the
686            destination (receiver) as seen by the source (transmitter). */
687
688         cos_xmtr_angle=((source_alt2)+(distance*distance)-(destination_alt*destination_alt))/(2.0*source_alt*distance);
689
690         /* Test all points in between source and destination locations to
691            see if the angle to a topographic feature generates a higher
692            elevation angle than that produced by the destination.  Begin
693            at the source since we're interested in identifying the FIRST
694            obstruction along the path between source and destination. */
695  
696         for (x=2, block=0; x<path.length && block==0; x++)
697         {
698                 distance=5280.0*path.distance[x];
699
700                 test_alt=earthradius+(path.elevation[x]==0.0?path.elevation[x]:path.elevation[x]+clutter);
701
702                 cos_test_angle=((source_alt2)+(distance*distance)-(test_alt*test_alt))/(2.0*source_alt*distance);
703
704                 /* Compare these two angles to determine if
705                    an obstruction exists.  Since we're comparing
706                    the cosines of these angles rather than
707                    the angles themselves, the sense of the
708                    following "if" statement is reversed from
709                    what it would be if the angles themselves
710                    were compared. */
711
712                 if (cos_xmtr_angle>=cos_test_angle)
713                 {
714                         block=1;
715                         first_obstruction_angle=((acos(cos_test_angle))/DEG2RAD)-90.0;
716                 }
717         }
718
719         if (block)
720                 elevation=first_obstruction_angle;
721
722         else
723                 elevation=((acos(cos_xmtr_angle))/DEG2RAD)-90.0;
724
725         path=temp;
726
727         return elevation;
728 }
729
730 double AverageTerrain(struct site source, double azimuthx, double start_distance, double end_distance)
731 {
732         /* This function returns the average terrain calculated in
733            the direction of "azimuth" (degrees) between "start_distance"
734            and "end_distance" (miles) from the source location.  If
735            the terrain is all water (non-critical error), -5000.0 is
736            returned.  If not enough SDF data has been loaded into
737            memory to complete the survey (critical error), then
738            -9999.0 is returned. */
739  
740         int     c, samples, endpoint;
741         double  beta, lat1, lon1, lat2, lon2, num, den, azimuth, terrain=0.0;
742         struct  site destination;
743
744         lat1=source.lat*DEG2RAD;
745         lon1=source.lon*DEG2RAD;
746
747         /* Generate a path of elevations between the source
748            location and the remote location provided. */
749
750         beta=end_distance/3959.0;
751
752         azimuth=DEG2RAD*azimuthx;
753
754         lat2=asin(sin(lat1)*cos(beta)+cos(azimuth)*sin(beta)*cos(lat1));
755         num=cos(beta)-(sin(lat1)*sin(lat2));
756         den=cos(lat1)*cos(lat2);
757
758         if (azimuth==0.0 && (beta>HALFPI-lat1))
759                 lon2=lon1+PI;
760
761         else if (azimuth==HALFPI && (beta>HALFPI+lat1))
762                         lon2=lon1+PI;
763
764         else if (fabs(num/den)>1.0)
765                         lon2=lon1;
766
767         else
768         {
769                 if ((PI-azimuth)>=0.0)
770                         lon2=lon1-arccos(num,den);
771                 else
772                         lon2=lon1+arccos(num,den);
773         }
774         
775         while (lon2<0.0)
776                 lon2+=TWOPI;
777
778         while (lon2>TWOPI)
779                 lon2-=TWOPI;
780  
781         lat2=lat2/DEG2RAD;
782         lon2=lon2/DEG2RAD;
783
784         destination.lat=lat2;
785         destination.lon=lon2;
786
787         /* If SDF data is missing for the endpoint of
788            the radial, then the average terrain cannot
789            be accurately calculated.  Return -9999.0 */
790
791         if (GetElevation(destination)<-4999.0)
792                 return (-9999.0);
793         else
794         {
795                 ReadPath(source,destination);
796
797                 endpoint=path.length;
798
799                 /* Shrink the length of the radial if the
800                    outermost portion is not over U.S. land. */
801
802                 for (c=endpoint-1; c>=0 && path.elevation[c]==0.0; c--);
803
804                 endpoint=c+1;
805
806                 for (c=0, samples=0; c<endpoint; c++)
807                 {
808                         if (path.distance[c]>=start_distance)
809                         {
810                                 terrain+=(path.elevation[c]==0.0?path.elevation[c]:path.elevation[c]+clutter);
811                                 samples++;
812                         }
813                 }
814
815                 if (samples==0)
816                         terrain=-5000.0;  /* No land */
817                 else
818                         terrain=(terrain/(double)samples);
819
820                 return terrain;
821         }
822 }
823
824 double haat(struct site antenna)
825 {
826         /* This function returns the antenna's Height Above Average
827            Terrain (HAAT) based on FCC Part 73.313(d).  If a critical
828            error occurs, such as a lack of SDF data to complete the
829            survey, -5000.0 is returned. */
830
831         int     azi, c;
832         char    error=0;
833         double  terrain, avg_terrain, haat, sum=0.0;
834
835         /* Calculate the average terrain between 2 and 10 miles
836            from the antenna site at azimuths of 0, 45, 90, 135,
837            180, 225, 270, and 315 degrees. */
838
839         for (c=0, azi=0; azi<=315 && error==0; azi+=45)
840         {
841                 terrain=AverageTerrain(antenna, (double)azi, 2.0, 10.0);
842
843                 if (terrain<-9998.0)  /* SDF data is missing */
844                         error=1;
845
846                 if (terrain>-4999.0)  /* It's land, not water */
847                 {
848                         sum+=terrain;  /* Sum of averages */
849                         c++;
850                 }
851         }
852
853         if (error)
854                 return -5000.0;
855         else
856         {
857                 avg_terrain=(sum/(double)c);
858                 haat=(antenna.alt+GetElevation(antenna))-avg_terrain;
859                 return haat;
860         }
861 }
862
863 void PlaceMarker(struct site location)
864 {
865         /* This function places text and marker data in the mask array
866            for illustration on topographic maps generated by SPLAT!.
867            By default, SPLAT! centers text information BELOW the marker,
868            but may move it above, to the left, or to the right of the
869            marker depending on how much room is available on the map,
870            or depending on whether the area is already occupied by
871            another marker or label.  If no room or clear space is
872            available on the map to place the marker and its associated
873            text, then the marker and text are not written to the map. */
874  
875         int     a, b, c, byte;
876         char    ok2print, occupied;
877         double  x, y, lat, lon, textx=0.0, texty=0.0, xmin, xmax,
878                 ymin, ymax, p1, p3, p6, p8, p12, p16, p24, label_length;
879
880         xmin=(double)min_north;
881         xmax=(double)max_north;
882         ymin=(double)min_west;
883         ymax=(double)max_west;
884         lat=location.lat;
885         lon=location.lon;
886
887         if (lat<xmax && lat>=xmin && (LonDiff(lon,ymax)<=0.0) && (LonDiff(lon,ymin)>=dpp))
888         {
889                 p1=1.0/ppd;
890                 p3=3.0/ppd;
891                 p6=6.0/ppd;
892                 p8=8.0/ppd;
893                 p12=12.0/ppd;
894                 p16=16.0/ppd;
895                 p24=24.0/ppd;
896
897                 ok2print=0;
898                 occupied=0;
899
900                 /* Is Marker Position Clear Of Text Or Other Markers? */
901
902                 for (a=0, x=lat-p3; (x<=xmax && x>=xmin && a<7); x+=p1, a++)
903                         for (b=0, y=lon-p3; (LonDiff(y,ymax)<=0.0) && (LonDiff(y,ymin)>=dpp) && b<7; y+=p1, b++)
904                                 occupied|=(GetMask(x,y)&2);
905
906                 if (occupied==0)
907                 {
908                         /* Determine Where Text Can Be Positioned */
909
910                         /* label_length=length in pixels.
911                            Each character is 8 pixels wide. */
912
913                         label_length=p1*(double)(strlen(location.name)<<3);
914
915                         if ((LonDiff(lon+label_length,ymax)<=0.0) && (LonDiff(lon-label_length,ymin)>=dpp))
916                         {
917                                 /* Default: Centered Text */
918
919                                 texty=lon+label_length/2.0;
920
921                                 if ((lat-p8)>=p16)
922                                 {
923                                         /* Position Text Below The Marker */
924
925                                         textx=lat-p8;
926
927                                         x=textx;
928                                         y=texty;
929         
930                                         /* Is This Position Clear Of
931                                            Text Or Other Markers? */
932
933                                         for (a=0, occupied=0; a<16; a++)
934                                         {
935                                                 for (b=0; b<(int)strlen(location.name); b++)
936                                                         for (c=0; c<8; c++, y-=p1)
937                                                                 occupied|=(GetMask(x,y)&2);
938                                                 x-=p1;
939                                                 y=texty;
940                                         }
941
942                                         x=textx;
943                                         y=texty;
944
945                                         if (occupied==0)
946                                                 ok2print=1;
947                                 }
948
949                                 else
950                                 {
951                                         /* Position Text Above The Marker */
952
953                                         textx=lat+p24;
954
955                                         x=textx;
956                                         y=texty;
957         
958                                         /* Is This Position Clear Of
959                                            Text Or Other Markers? */
960
961                                         for (a=0, occupied=0; a<16; a++)
962                                         {
963                                                 for (b=0; b<(int)strlen(location.name); b++)
964                                                         for (c=0; c<8; c++, y-=p1)
965                                                                 occupied|=(GetMask(x,y)&2);
966                                                 x-=p1;
967                                                 y=texty;
968                                         }
969
970                                         x=textx;
971                                         y=texty;
972
973                                         if (occupied==0)
974                                                 ok2print=1;
975                                 }
976                         }
977
978                         if (ok2print==0)
979                         {
980                                 if (LonDiff(lon-label_length,ymin)>=dpp)
981                                 {
982                                         /* Position Text To The
983                                            Right Of The Marker */
984
985                                         textx=lat+p6;
986                                         texty=lon-p12;
987
988                                         x=textx;
989                                         y=texty;
990         
991                                         /* Is This Position Clear Of
992                                            Text Or Other Markers? */
993
994                                         for (a=0, occupied=0; a<16; a++)
995                                         {
996                                                 for (b=0; b<(int)strlen(location.name); b++)
997                                                         for (c=0; c<8; c++, y-=p1)
998                                                                 occupied|=(GetMask(x,y)&2);
999                                                 x-=p1;
1000                                                 y=texty;
1001                                         }
1002
1003                                         x=textx;
1004                                         y=texty;
1005
1006                                         if (occupied==0)
1007                                                 ok2print=1;
1008                                 }
1009
1010                                 else
1011                                 {
1012                                         /* Position Text To The
1013                                            Left Of The Marker */
1014
1015                                         textx=lat+p6;
1016                                         texty=lon+p8+(label_length);
1017
1018                                         x=textx;
1019                                         y=texty;
1020         
1021                                         /* Is This Position Clear Of
1022                                            Text Or Other Markers? */
1023
1024                                         for (a=0, occupied=0; a<16; a++)
1025                                         {
1026                                                 for (b=0; b<(int)strlen(location.name); b++)
1027                                                         for (c=0; c<8; c++, y-=p1)
1028                                                                 occupied|=(GetMask(x,y)&2);
1029                                                 x-=p1;
1030                                                 y=texty;
1031                                         }
1032
1033                                         x=textx;
1034                                         y=texty;
1035
1036                                         if (occupied==0)
1037                                                 ok2print=1;
1038                                 }
1039                         }
1040
1041                         /* textx and texty contain the latitude and longitude
1042                            coordinates that describe the placement of the text
1043                            on the map. */
1044         
1045                         if (ok2print)
1046                         {
1047                                 /* Draw Text */
1048
1049                                 x=textx;
1050                                 y=texty;
1051                                 
1052                                 for (a=0; a<16; a++)
1053                                 {
1054                                         for (b=0; b<(int)strlen(location.name); b++)
1055                                         {
1056                                                 byte=fontdata[16*(location.name[b])+a];
1057
1058                                                 for (c=128; c>0; c=c>>1, y-=p1)
1059                                                         if (byte&c)
1060                                                                 OrMask(x,y,2);
1061                                         }
1062
1063                                         x-=p1;
1064                                         y=texty;
1065                                 }
1066         
1067                                 /* Draw Square Marker Centered
1068                                    On Location Specified */
1069
1070                                 for (a=0, x=lat-p3; (x<=xmax && x>=xmin && a<7); x+=p1, a++)
1071                                         for (b=0, y=lon-p3; (LonDiff(y,ymax)<=0.0) && (LonDiff(y,ymin)>=dpp) && b<7; y+=p1, b++)
1072                                                 OrMask(x,y,2);
1073                         }
1074                 }
1075         }
1076 }
1077
1078 double ReadBearing(char *input)
1079 {
1080         /* This function takes numeric input in the form of a character
1081            string, and returns an equivalent bearing in degrees as a
1082            decimal number (double).  The input may either be expressed
1083            in decimal format (40.139722) or degree, minute, second
1084            format (40 08 23).  This function also safely handles
1085            extra spaces found either leading, trailing, or
1086            embedded within the numbers expressed in the
1087            input string.  Decimal seconds are permitted. */
1088  
1089         double  seconds, bearing=0.0;
1090         char    string[20];
1091         int     a, b, length, degrees, minutes;
1092
1093         /* Copy "input" to "string", and ignore any extra
1094            spaces that might be present in the process. */
1095
1096         string[0]=0;
1097         length=strlen(input);
1098
1099         for (a=0, b=0; a<length && a<18; a++)
1100         {
1101                 if ((input[a]!=32 && input[a]!='\n') || (input[a]==32 && input[a+1]!=32 && input[a+1]!='\n' && b!=0))
1102                 {
1103                         string[b]=input[a];
1104                         b++;
1105                 }        
1106         }
1107
1108         string[b]=0;
1109
1110         /* Count number of spaces in the clean string. */
1111
1112         length=strlen(string);
1113
1114         for (a=0, b=0; a<length; a++)
1115                 if (string[a]==32)
1116                         b++;
1117
1118         if (b==0)  /* Decimal Format (40.139722) */
1119                 sscanf(string,"%lf",&bearing);
1120
1121         if (b==2)  /* Degree, Minute, Second Format (40 08 23.xx) */
1122         {
1123                 sscanf(string,"%d %d %lf",&degrees, &minutes, &seconds);
1124
1125                 bearing=fabs((double)degrees);
1126                 bearing+=fabs(((double)minutes)/60.0);
1127                 bearing+=fabs(seconds/3600.0);
1128
1129                 if ((degrees<0) || (minutes<0) || (seconds<0.0))
1130                         bearing=-bearing;
1131         }
1132
1133         /* Anything else returns a 0.0 */
1134
1135         if (bearing>360.0 || bearing<-360.0)
1136                 bearing=0.0;
1137
1138         return bearing;
1139 }
1140
1141 struct site LoadQTH(char *filename)
1142 {
1143         /* This function reads SPLAT! .qth (site location) files.
1144            The latitude and longitude may be expressed either in
1145            decimal degrees, or in degree, minute, second format.
1146            Antenna height is assumed to be expressed in feet above
1147            ground level (AGL), unless followed by the letter 'M',
1148            or 'm', or by the word "meters" or "Meters", in which
1149            case meters is assumed, and is handled accordingly. */
1150
1151         int     x;
1152         char    string[50], qthfile[255], *s=NULL;
1153         struct  site tempsite;
1154         FILE    *fd=NULL;
1155
1156         x=strlen(filename);
1157         strncpy(qthfile, filename, 254);
1158
1159         if (qthfile[x-3]!='q' || qthfile[x-2]!='t' || qthfile[x-1]!='h')
1160         {
1161                 if (x>249)
1162                         qthfile[249]=0;
1163
1164                 strncat(qthfile,".qth\0",5);
1165         }
1166
1167         tempsite.lat=91.0;
1168         tempsite.lon=361.0;
1169         tempsite.alt=0.0;
1170         tempsite.name[0]=0;
1171         tempsite.filename[0]=0;
1172
1173         fd=fopen(qthfile,"r");
1174
1175         if (fd!=NULL)
1176         {
1177                 /* Site Name */
1178                 s=fgets(string,49,fd);
1179
1180                 /* Strip <CR> and/or <LF> from end of site name */
1181
1182                 for (x=0; string[x]!=13 && string[x]!=10 && string[x]!=0; tempsite.name[x]=string[x], x++);
1183
1184                 tempsite.name[x]=0;
1185
1186                 /* Site Latitude */
1187                 s=fgets(string,49,fd);
1188                 tempsite.lat=ReadBearing(string);
1189
1190                 /* Site Longitude */
1191                 s=fgets(string,49,fd);
1192                 tempsite.lon=ReadBearing(string);
1193
1194                 if (tempsite.lon<0.0)
1195                         tempsite.lon+=360.0;
1196
1197                 /* Antenna Height */
1198                 s=fgets(string,49,fd);
1199                 fclose(fd);
1200
1201                 /* Remove <CR> and/or <LF> from antenna height string */
1202                 for (x=0; string[x]!=13 && string[x]!=10 && string[x]!=0; x++);
1203
1204                 string[x]=0;
1205
1206                 /* Antenna height may either be in feet or meters.
1207                    If the letter 'M' or 'm' is discovered in
1208                    the string, then this is an indication that
1209                    the value given is expressed in meters, and
1210                    must be converted to feet before exiting. */
1211
1212                 for (x=0; string[x]!='M' && string[x]!='m' && string[x]!=0 && x<48; x++);
1213
1214                 if (string[x]=='M' || string[x]=='m')
1215                 {
1216                         string[x]=0;
1217                         sscanf(string,"%f",&tempsite.alt);
1218                         tempsite.alt*=3.28084;
1219                 }
1220
1221                 else
1222                 {
1223                         string[x]=0;
1224                         sscanf(string,"%f",&tempsite.alt);
1225                 }
1226
1227                 for (x=0; x<254 && qthfile[x]!=0; x++)
1228                         tempsite.filename[x]=qthfile[x];
1229
1230                 tempsite.filename[x]=0;
1231         }
1232
1233         return tempsite;
1234 }
1235
1236 void LoadPAT(char *filename)
1237 {
1238         /* This function reads and processes antenna pattern (.az
1239            and .el) files that correspond in name to previously
1240            loaded SPLAT! .lrp files.  */
1241
1242         int     a, b, w, x, y, z, last_index, next_index, span;
1243         char    string[255], azfile[255], elfile[255], *pointer=NULL, *s=NULL;
1244         float   az, xx, elevation, amplitude, rotation, valid1, valid2,
1245                 delta, azimuth[361], azimuth_pattern[361], el_pattern[10001],
1246                 elevation_pattern[361][1001], slant_angle[361], tilt,
1247                 mechanical_tilt=0.0, tilt_azimuth, tilt_increment, sum;
1248         FILE    *fd=NULL;
1249         unsigned char read_count[10001];
1250
1251         for (x=0; filename[x]!='.' && filename[x]!=0 && x<250; x++)
1252         {
1253                 azfile[x]=filename[x];
1254                 elfile[x]=filename[x];
1255         }
1256
1257         azfile[x]='.';
1258         azfile[x+1]='a';
1259         azfile[x+2]='z';
1260         azfile[x+3]=0;
1261
1262         elfile[x]='.';
1263         elfile[x+1]='e';
1264         elfile[x+2]='l';
1265         elfile[x+3]=0;
1266
1267         rotation=0.0;
1268
1269         got_azimuth_pattern=0;
1270         got_elevation_pattern=0;
1271
1272         /* Load .az antenna pattern file */
1273
1274         fd=fopen(azfile,"r");
1275
1276         if (fd!=NULL)
1277         {
1278                 /* Clear azimuth pattern array */
1279
1280                 for (x=0; x<=360; x++)
1281                 {
1282                         azimuth[x]=0.0;
1283                         read_count[x]=0;
1284                 }
1285
1286
1287                 /* Read azimuth pattern rotation
1288                    in degrees measured clockwise
1289                    from true North. */
1290
1291                 s=fgets(string,254,fd);
1292                 pointer=strchr(string,';');
1293
1294                 if (pointer!=NULL)
1295                         *pointer=0;
1296
1297                 sscanf(string,"%f",&rotation);
1298
1299
1300                 /* Read azimuth (degrees) and corresponding
1301                    normalized field radiation pattern amplitude
1302                    (0.0 to 1.0) until EOF is reached. */
1303
1304                 s=fgets(string,254,fd);
1305                 pointer=strchr(string,';');
1306
1307                 if (pointer!=NULL)
1308                         *pointer=0;
1309
1310                 sscanf(string,"%f %f",&az, &amplitude);
1311
1312                 do
1313                 {
1314                         x=(int)rintf(az);
1315
1316                         if (x>=0 && x<=360 && fd!=NULL)
1317                         {
1318                                 azimuth[x]+=amplitude;
1319                                 read_count[x]++;
1320                         }
1321
1322                         s=fgets(string,254,fd);
1323                         pointer=strchr(string,';');
1324
1325                         if (pointer!=NULL)
1326                                 *pointer=0;
1327
1328                         sscanf(string,"%f %f",&az, &amplitude);
1329
1330                 } while (feof(fd)==0);
1331
1332                 fclose(fd);
1333
1334
1335                 /* Handle 0=360 degree ambiguity */
1336
1337                 if ((read_count[0]==0) && (read_count[360]!=0))
1338                 {
1339                         read_count[0]=read_count[360];
1340                         azimuth[0]=azimuth[360];
1341                 }
1342
1343                 if ((read_count[0]!=0) && (read_count[360]==0))
1344                 {
1345                         read_count[360]=read_count[0];
1346                         azimuth[360]=azimuth[0];
1347                 }
1348
1349                 /* Average pattern values in case more than
1350                     one was read for each degree of azimuth. */
1351
1352                 for (x=0; x<=360; x++)
1353                 {
1354                         if (read_count[x]>1)
1355                                 azimuth[x]/=(float)read_count[x];
1356                 }
1357
1358                 /* Interpolate missing azimuths
1359                    to completely fill the array */
1360
1361                 last_index=-1;
1362                 next_index=-1;
1363
1364                 for (x=0; x<=360; x++)
1365                 {
1366                         if (read_count[x]!=0)
1367                         {
1368                                 if (last_index==-1)
1369                                         last_index=x;
1370                                 else
1371                                         next_index=x;
1372                         }
1373
1374                         if (last_index!=-1 && next_index!=-1)
1375                         {
1376                                 valid1=azimuth[last_index];
1377                                 valid2=azimuth[next_index];
1378
1379                                 span=next_index-last_index;
1380                                 delta=(valid2-valid1)/(float)span;
1381
1382                                 for (y=last_index+1; y<next_index; y++)
1383                                         azimuth[y]=azimuth[y-1]+delta;
1384
1385                                 last_index=y;
1386                                 next_index=-1;
1387                         }
1388                 }
1389
1390                 /* Perform azimuth pattern rotation
1391                    and load azimuth_pattern[361] with
1392                    azimuth pattern data in its final form. */
1393
1394                 for (x=0; x<360; x++)
1395                 {
1396                         y=x+(int)rintf(rotation);
1397
1398                         if (y>=360)
1399                                 y-=360;
1400
1401                         azimuth_pattern[y]=azimuth[x];
1402                 }
1403
1404                 azimuth_pattern[360]=azimuth_pattern[0];
1405
1406                 got_azimuth_pattern=255;
1407         }
1408
1409         /* Read and process .el file */
1410
1411         fd=fopen(elfile,"r");
1412
1413         if (fd!=NULL)
1414         {
1415                 for (x=0; x<=10000; x++)
1416                 {
1417                         el_pattern[x]=0.0;
1418                         read_count[x]=0;
1419                 }
1420
1421                 /* Read mechanical tilt (degrees) and
1422                    tilt azimuth in degrees measured
1423                    clockwise from true North. */  
1424
1425                 s=fgets(string,254,fd);
1426                 pointer=strchr(string,';');
1427
1428                 if (pointer!=NULL)
1429                         *pointer=0;
1430
1431                 sscanf(string,"%f %f",&mechanical_tilt, &tilt_azimuth);
1432
1433                 /* Read elevation (degrees) and corresponding
1434                    normalized field radiation pattern amplitude
1435                    (0.0 to 1.0) until EOF is reached. */
1436
1437                 s=fgets(string,254,fd);
1438                 pointer=strchr(string,';');
1439
1440                 if (pointer!=NULL)
1441                         *pointer=0;
1442
1443                 sscanf(string,"%f %f", &elevation, &amplitude);
1444
1445                 while (feof(fd)==0)
1446                 {
1447                         /* Read in normalized radiated field values
1448                            for every 0.01 degrees of elevation between
1449                            -10.0 and +90.0 degrees */
1450
1451                         x=(int)rintf(100.0*(elevation+10.0));
1452
1453                         if (x>=0 && x<=10000)
1454                         {
1455                                 el_pattern[x]+=amplitude;
1456                                 read_count[x]++;
1457                         }
1458
1459                         s=fgets(string,254,fd);
1460                         pointer=strchr(string,';');
1461
1462                         if (pointer!=NULL)
1463                                 *pointer=0;
1464
1465                         sscanf(string,"%f %f", &elevation, &amplitude);
1466                 }
1467
1468                 fclose(fd);
1469
1470                 /* Average the field values in case more than
1471                    one was read for each 0.01 degrees of elevation. */
1472
1473                 for (x=0; x<=10000; x++)
1474                 {
1475                         if (read_count[x]>1)
1476                                 el_pattern[x]/=(float)read_count[x];
1477                 }
1478
1479                 /* Interpolate between missing elevations (if
1480                    any) to completely fill the array and provide
1481                    radiated field values for every 0.01 degrees of
1482                    elevation. */
1483
1484                 last_index=-1;
1485                 next_index=-1;
1486
1487                 for (x=0; x<=10000; x++)
1488                 {
1489                         if (read_count[x]!=0)
1490                         {
1491                                 if (last_index==-1)
1492                                         last_index=x;
1493                                 else
1494                                         next_index=x;
1495                         }
1496
1497                         if (last_index!=-1 && next_index!=-1)
1498                         {
1499                                 valid1=el_pattern[last_index];
1500                                 valid2=el_pattern[next_index];
1501
1502                                 span=next_index-last_index;
1503                                 delta=(valid2-valid1)/(float)span;
1504
1505                                 for (y=last_index+1; y<next_index; y++)
1506                                         el_pattern[y]=el_pattern[y-1]+delta;
1507
1508                                 last_index=y;
1509                                 next_index=-1;
1510                         }
1511                 }
1512
1513                 /* Fill slant_angle[] array with offset angles based
1514                    on the antenna's mechanical beam tilt (if any)
1515                    and tilt direction (azimuth). */
1516
1517                 if (mechanical_tilt==0.0)
1518                 {
1519                         for (x=0; x<=360; x++)
1520                                 slant_angle[x]=0.0;
1521                 }
1522
1523                 else
1524                 {
1525                         tilt_increment=mechanical_tilt/90.0;
1526
1527                         for (x=0; x<=360; x++)
1528                         {
1529                                 xx=(float)x;
1530                                 y=(int)rintf(tilt_azimuth+xx);
1531
1532                                 while (y>=360)
1533                                         y-=360;
1534
1535                                 while (y<0)
1536                                         y+=360;
1537
1538                                 if (x<=180)
1539                                         slant_angle[y]=-(tilt_increment*(90.0-xx));
1540
1541                                 if (x>180)
1542                                         slant_angle[y]=-(tilt_increment*(xx-270.0));
1543                         }
1544                 }
1545
1546                 slant_angle[360]=slant_angle[0];   /* 360 degree wrap-around */
1547
1548                 for (w=0; w<=360; w++)
1549                 {
1550                         tilt=slant_angle[w];
1551
1552                         /** Convert tilt angle to
1553                             an array index offset **/
1554
1555                         y=(int)rintf(100.0*tilt);
1556
1557                         /* Copy shifted el_pattern[10001] field
1558                            values into elevation_pattern[361][1001]
1559                            at the corresponding azimuth, downsampling
1560                            (averaging) along the way in chunks of 10. */
1561
1562                         for (x=y, z=0; z<=1000; x+=10, z++)
1563                         {
1564                                 for (sum=0.0, a=0; a<10; a++)
1565                                 {
1566                                         b=a+x;
1567
1568                                         if (b>=0 && b<=10000)
1569                                                 sum+=el_pattern[b];
1570                                         if (b<0)
1571                                                 sum+=el_pattern[0];
1572                                         if (b>10000)
1573                                                 sum+=el_pattern[10000];
1574                                 }
1575
1576                                 elevation_pattern[w][z]=sum/10.0;
1577                         }
1578                 }
1579
1580                 got_elevation_pattern=255;
1581         }
1582
1583         for (x=0; x<=360; x++)
1584         {
1585                 for (y=0; y<=1000; y++)
1586                 {
1587                         if (got_elevation_pattern)
1588                                 elevation=elevation_pattern[x][y];
1589                         else
1590                                 elevation=1.0;
1591
1592                         if (got_azimuth_pattern)
1593                                 az=azimuth_pattern[x];
1594                         else
1595                                 az=1.0;
1596
1597                         LR.antenna_pattern[x][y]=az*elevation;
1598                 }
1599         }
1600 }
1601
1602 int LoadSDF_SDF(char *name)
1603 {
1604         /* This function reads uncompressed SPLAT Data Files (.sdf)
1605            containing digital elevation model data into memory.
1606            Elevation data, maximum and minimum elevations, and
1607            quadrangle limits are stored in the first available
1608            dem[] structure. */
1609
1610         int     x, y, data, indx, minlat, minlon, maxlat, maxlon;
1611         char    found, free_page=0, line[20], sdf_file[255],
1612                 path_plus_name[255], *s=NULL;
1613         FILE    *fd;
1614
1615         for (x=0; name[x]!='.' && name[x]!=0 && x<250; x++)
1616                 sdf_file[x]=name[x];
1617
1618         sdf_file[x]=0;
1619
1620         /* Parse filename for minimum latitude and longitude values */
1621
1622         sscanf(sdf_file,"%d:%d:%d:%d",&minlat,&maxlat,&minlon,&maxlon);
1623
1624         sdf_file[x]='.';
1625         sdf_file[x+1]='s';
1626         sdf_file[x+2]='d';
1627         sdf_file[x+3]='f';
1628         sdf_file[x+4]=0;
1629
1630         /* Is it already in memory? */
1631
1632         for (indx=0, found=0; indx<MAXPAGES && found==0; indx++)
1633         {
1634                 if (minlat==dem[indx].min_north && minlon==dem[indx].min_west && maxlat==dem[indx].max_north && maxlon==dem[indx].max_west)
1635                         found=1;
1636         }
1637
1638         /* Is room available to load it? */
1639
1640         if (found==0)
1641         {       
1642                 for (indx=0, free_page=0; indx<MAXPAGES && free_page==0; indx++)
1643                         if (dem[indx].max_north==-90)
1644                                 free_page=1;
1645         }
1646
1647         indx--;
1648
1649         if (free_page && found==0 && indx>=0 && indx<MAXPAGES)
1650         {
1651                 /* Search for SDF file in current working directory first */
1652
1653                 strncpy(path_plus_name,sdf_file,255);
1654
1655                 fd=fopen(path_plus_name,"rb");
1656
1657                 if (fd==NULL)
1658                 {
1659                         /* Next, try loading SDF file from path specified
1660                            in $HOME/.splat_path file or by -d argument */
1661
1662                         strncpy(path_plus_name,sdf_path,255);
1663                         strncat(path_plus_name,sdf_file,255);
1664
1665                         fd=fopen(path_plus_name,"rb");
1666                 }
1667
1668                 if (fd!=NULL)
1669                 {
1670                         fprintf(stdout,"Loading \"%s\" into page %d...",path_plus_name,indx+1);
1671                         fflush(stdout);
1672
1673                         s=fgets(line,19,fd);
1674                         sscanf(line,"%d",&dem[indx].max_west);
1675
1676                         s=fgets(line,19,fd);
1677                         sscanf(line,"%d",&dem[indx].min_north);
1678
1679                         s=fgets(line,19,fd);
1680                         sscanf(line,"%d",&dem[indx].min_west);
1681
1682                         s=fgets(line,19,fd);
1683                         sscanf(line,"%d",&dem[indx].max_north);
1684
1685                         for (x=0; x<ippd; x++)
1686                                 for (y=0; y<ippd; y++)
1687                                 {
1688                                         s=fgets(line,19,fd);
1689                                         data=atoi(line);
1690
1691                                         dem[indx].data[x][y]=data;
1692                                         dem[indx].signal[x][y]=0;
1693                                         dem[indx].mask[x][y]=0;
1694
1695                                         if (data>dem[indx].max_el)
1696                                                 dem[indx].max_el=data;
1697
1698                                         if (data<dem[indx].min_el)
1699                                                 dem[indx].min_el=data;
1700                                 }
1701
1702                         fclose(fd);
1703
1704                         if (dem[indx].min_el<min_elevation)
1705                                 min_elevation=dem[indx].min_el;
1706
1707                         if (dem[indx].max_el>max_elevation)
1708                                 max_elevation=dem[indx].max_el;
1709
1710                         if (max_north==-90)
1711                                 max_north=dem[indx].max_north;
1712
1713                         else if (dem[indx].max_north>max_north)
1714                                         max_north=dem[indx].max_north;
1715
1716                         if (min_north==90)
1717                                 min_north=dem[indx].min_north;
1718
1719                         else if (dem[indx].min_north<min_north)
1720                                         min_north=dem[indx].min_north;
1721
1722                         if (max_west==-1)
1723                                 max_west=dem[indx].max_west;
1724
1725                         else
1726                         {
1727                                 if (abs(dem[indx].max_west-max_west)<180)
1728                                 {
1729                                         if (dem[indx].max_west>max_west)
1730                                                 max_west=dem[indx].max_west;
1731                                 }
1732
1733                                 else
1734                                 {
1735                                         if (dem[indx].max_west<max_west)
1736                                                 max_west=dem[indx].max_west;
1737                                 }
1738                         }
1739
1740                         if (min_west==360)
1741                                 min_west=dem[indx].min_west;
1742
1743                         else
1744                         {
1745                                 if (fabs(dem[indx].min_west-min_west)<180.0)
1746                                 {
1747                                         if (dem[indx].min_west<min_west)
1748                                                 min_west=dem[indx].min_west;
1749                                 }
1750
1751                                 else
1752                                 {
1753                                         if (dem[indx].min_west>min_west)
1754                                                 min_west=dem[indx].min_west;
1755                                 }
1756                         }
1757
1758                         fprintf(stdout," Done!\n");
1759                         fflush(stdout);
1760
1761                         return 1;
1762                 }
1763
1764                 else
1765                         return -1;
1766         }
1767
1768         else
1769                 return 0;
1770 }
1771
1772 char *BZfgets(BZFILE *bzfd, unsigned length)
1773 {
1774         /* This function returns at most one less than 'length' number
1775            of characters from a bz2 compressed file whose file descriptor
1776            is pointed to by *bzfd.  In operation, a buffer is filled with
1777            uncompressed data (size = BZBUFFER), which is then parsed
1778            and doled out as NULL terminated character strings every time
1779            this function is invoked.  A NULL string indicates an EOF
1780            or error condition. */
1781
1782         static int x, y, nBuf;
1783         static char buffer[BZBUFFER+1], output[BZBUFFER+1];
1784         char done=0;
1785
1786         if (opened!=1 && bzerror==BZ_OK)
1787         {
1788                 /* First time through.  Initialize everything! */
1789
1790                 x=0;
1791                 y=0;
1792                 nBuf=0;
1793                 opened=1;
1794                 output[0]=0;
1795         }
1796
1797         do
1798         {
1799                 if (x==nBuf && bzerror!=BZ_STREAM_END && bzerror==BZ_OK && opened)
1800                 {
1801                         /* Uncompress data into a static buffer */
1802
1803                         nBuf=BZ2_bzRead(&bzerror, bzfd, buffer, BZBUFFER);
1804                         buffer[nBuf]=0;
1805                         x=0;
1806                 }
1807
1808                 /* Build a string from buffer contents */
1809
1810                 output[y]=buffer[x];
1811
1812                 if (output[y]=='\n' || output[y]==0 || y==(int)length-1)
1813                 {
1814                         output[y+1]=0;
1815                         done=1;
1816                         y=0;
1817                 }
1818
1819                 else
1820                         y++;
1821                 x++;
1822
1823         } while (done==0);
1824
1825         if (output[0]==0)
1826                 opened=0;
1827
1828         return (output);
1829 }
1830
1831 int LoadSDF_BZ(char *name)
1832 {
1833         /* This function reads .bz2 compressed SPLAT Data Files containing
1834            digital elevation model data into memory.  Elevation data,
1835            maximum and minimum elevations, and quadrangle limits are
1836            stored in the first available dem[] structure. */
1837
1838         int     x, y, data, indx, minlat, minlon, maxlat, maxlon;
1839         char    found, free_page=0, sdf_file[255], path_plus_name[255], *string;
1840         FILE    *fd;
1841         BZFILE  *bzfd;
1842
1843         for (x=0; name[x]!='.' && name[x]!=0 && x<247; x++)
1844                 sdf_file[x]=name[x];
1845
1846         sdf_file[x]=0;
1847
1848         /* Parse sdf_file name for minimum latitude and longitude values */
1849
1850         sscanf(sdf_file,"%d:%d:%d:%d",&minlat,&maxlat,&minlon,&maxlon);
1851
1852         sdf_file[x]='.';
1853         sdf_file[x+1]='s';
1854         sdf_file[x+2]='d';
1855         sdf_file[x+3]='f';
1856         sdf_file[x+4]='.';
1857         sdf_file[x+5]='b';
1858         sdf_file[x+6]='z';
1859         sdf_file[x+7]='2';
1860         sdf_file[x+8]=0;
1861
1862         /* Is it already in memory? */
1863
1864         for (indx=0, found=0; indx<MAXPAGES && found==0; indx++)
1865         {
1866                 if (minlat==dem[indx].min_north && minlon==dem[indx].min_west && maxlat==dem[indx].max_north && maxlon==dem[indx].max_west)
1867                         found=1;
1868         }
1869
1870         /* Is room available to load it? */
1871
1872         if (found==0)
1873         {       
1874                 for (indx=0, free_page=0; indx<MAXPAGES && free_page==0; indx++)
1875                         if (dem[indx].max_north==-90)
1876                                 free_page=1;
1877         }
1878
1879         indx--;
1880
1881         if (free_page && found==0 && indx>=0 && indx<MAXPAGES)
1882         {
1883                 /* Search for SDF file in current working directory first */
1884
1885                 strncpy(path_plus_name,sdf_file,255);
1886
1887                 fd=fopen(path_plus_name,"rb");
1888                 bzfd=BZ2_bzReadOpen(&bzerror,fd,0,0,NULL,0);
1889
1890                 if (fd==NULL || bzerror!=BZ_OK)
1891                 {
1892                         /* Next, try loading SDF file from path specified
1893                            in $HOME/.splat_path file or by -d argument */
1894
1895                         strncpy(path_plus_name,sdf_path,255);
1896                         strncat(path_plus_name,sdf_file,255);
1897
1898                         fd=fopen(path_plus_name,"rb");
1899                         bzfd=BZ2_bzReadOpen(&bzerror,fd,0,0,NULL,0);
1900                 }
1901
1902                 if (fd!=NULL && bzerror==BZ_OK)
1903                 {
1904                         fprintf(stdout,"Loading \"%s\" into page %d...",path_plus_name,indx+1);
1905                         fflush(stdout);
1906
1907                         sscanf(BZfgets(bzfd,255),"%d",&dem[indx].max_west);
1908                         sscanf(BZfgets(bzfd,255),"%d",&dem[indx].min_north);
1909                         sscanf(BZfgets(bzfd,255),"%d",&dem[indx].min_west);
1910                         sscanf(BZfgets(bzfd,255),"%d",&dem[indx].max_north);
1911         
1912                         for (x=0; x<ippd; x++)
1913                                 for (y=0; y<ippd; y++)
1914                                 {
1915                                         string=BZfgets(bzfd,20);
1916                                         data=atoi(string);
1917
1918                                         dem[indx].data[x][y]=data;
1919                                         dem[indx].signal[x][y]=0;
1920                                         dem[indx].mask[x][y]=0;
1921
1922                                         if (data>dem[indx].max_el)
1923                                                 dem[indx].max_el=data;
1924
1925                                         if (data<dem[indx].min_el)
1926                                                 dem[indx].min_el=data;
1927                                 }
1928
1929                         fclose(fd);
1930
1931                         BZ2_bzReadClose(&bzerror,bzfd);
1932
1933                         if (dem[indx].min_el<min_elevation)
1934                                 min_elevation=dem[indx].min_el;
1935         
1936                         if (dem[indx].max_el>max_elevation)
1937                                 max_elevation=dem[indx].max_el;
1938
1939                         if (max_north==-90)
1940                                 max_north=dem[indx].max_north;
1941
1942                         else if (dem[indx].max_north>max_north)
1943                                         max_north=dem[indx].max_north;
1944
1945                         if (min_north==90)
1946                                 min_north=dem[indx].min_north;
1947
1948                         else if (dem[indx].min_north<min_north)
1949                                         min_north=dem[indx].min_north;
1950
1951                         if (max_west==-1)
1952                                 max_west=dem[indx].max_west;
1953
1954                         else
1955                         {
1956                                 if (abs(dem[indx].max_west-max_west)<180)
1957                                 {
1958                                         if (dem[indx].max_west>max_west)
1959                                                 max_west=dem[indx].max_west;
1960                                 }
1961
1962                                 else
1963                                 {
1964                                         if (dem[indx].max_west<max_west)
1965                                                 max_west=dem[indx].max_west;
1966                                 }
1967                         }
1968
1969                         if (min_west==360)
1970                                 min_west=dem[indx].min_west;
1971
1972                         else
1973                         {
1974                                 if (abs(dem[indx].min_west-min_west)<180)
1975                                 {
1976                                         if (dem[indx].min_west<min_west)
1977                                                 min_west=dem[indx].min_west;
1978                                 }
1979
1980                                 else
1981                                 {
1982                                         if (dem[indx].min_west>min_west)
1983                                                 min_west=dem[indx].min_west;
1984                                 }
1985                         }
1986
1987                         fprintf(stdout," Done!\n");
1988                         fflush(stdout);
1989
1990                         return 1;
1991                 }
1992
1993                 else
1994                         return -1;
1995         }
1996
1997         else
1998                 return 0;
1999 }
2000
2001 char LoadSDF(char *name)
2002 {
2003         /* This function loads the requested SDF file from the filesystem.
2004            It first tries to invoke the LoadSDF_SDF() function to load an
2005            uncompressed SDF file (since uncompressed files load slightly
2006            faster).  If that attempt fails, then it tries to load a
2007            compressed SDF file by invoking the LoadSDF_BZ() function.
2008            If that fails, then we can assume that no elevation data
2009            exists for the region requested, and that the region
2010            requested must be entirely over water. */
2011
2012         int     x, y, indx, minlat, minlon, maxlat, maxlon;
2013         char    found, free_page=0;
2014         int     return_value=-1;
2015
2016         /* Try to load an uncompressed SDF first. */
2017
2018         return_value=LoadSDF_SDF(name);
2019
2020         /* If that fails, try loading a compressed SDF. */
2021
2022         if (return_value==0 || return_value==-1)
2023                 return_value=LoadSDF_BZ(name);
2024
2025         /* If neither format can be found, then assume the area is water. */
2026
2027         if (return_value==0 || return_value==-1)
2028         {
2029                 /* Parse SDF name for minimum latitude and longitude values */
2030
2031                 sscanf(name,"%d:%d:%d:%d",&minlat,&maxlat,&minlon,&maxlon);
2032
2033                 /* Is it already in memory? */
2034
2035                 for (indx=0, found=0; indx<MAXPAGES && found==0; indx++)
2036                 {
2037                         if (minlat==dem[indx].min_north && minlon==dem[indx].min_west && maxlat==dem[indx].max_north && maxlon==dem[indx].max_west)
2038                                 found=1;
2039                 }
2040
2041                 /* Is room available to load it? */
2042
2043                 if (found==0)
2044                 {       
2045                         for (indx=0, free_page=0; indx<MAXPAGES && free_page==0; indx++)
2046                                 if (dem[indx].max_north==-90)
2047                                         free_page=1;
2048                 }
2049
2050                 indx--;
2051
2052                 if (free_page && found==0 && indx>=0 && indx<MAXPAGES)
2053                 {
2054                         fprintf(stdout,"Region  \"%s\" assumed as sea-level into page %d...",name,indx+1);
2055                         fflush(stdout);
2056
2057                         dem[indx].max_west=maxlon;
2058                         dem[indx].min_north=minlat;
2059                         dem[indx].min_west=minlon;
2060                         dem[indx].max_north=maxlat;
2061
2062                         /* Fill DEM with sea-level topography */
2063
2064                         for (x=0; x<ippd; x++)
2065                                 for (y=0; y<ippd; y++)
2066                                 {
2067                                         dem[indx].data[x][y]=0;
2068                                         dem[indx].signal[x][y]=0;
2069                                         dem[indx].mask[x][y]=0;
2070
2071                                         if (dem[indx].min_el>0)
2072                                                 dem[indx].min_el=0;
2073                                 }
2074
2075                         if (dem[indx].min_el<min_elevation)
2076                                 min_elevation=dem[indx].min_el;
2077
2078                         if (dem[indx].max_el>max_elevation)
2079                                 max_elevation=dem[indx].max_el;
2080
2081                         if (max_north==-90)
2082                                 max_north=dem[indx].max_north;
2083
2084                         else if (dem[indx].max_north>max_north)
2085                                         max_north=dem[indx].max_north;
2086
2087                         if (min_north==90)
2088                                 min_north=dem[indx].min_north;
2089
2090                         else if (dem[indx].min_north<min_north)
2091                                         min_north=dem[indx].min_north;
2092
2093                         if (max_west==-1)
2094                                 max_west=dem[indx].max_west;
2095
2096                         else
2097                         {
2098                                 if (abs(dem[indx].max_west-max_west)<180)
2099                                 {
2100                                         if (dem[indx].max_west>max_west)
2101                                                 max_west=dem[indx].max_west;
2102                                 }
2103
2104                                 else
2105                                 {
2106                                         if (dem[indx].max_west<max_west)
2107                                                 max_west=dem[indx].max_west;
2108                                 }
2109                         }
2110
2111                         if (min_west==360)
2112                                 min_west=dem[indx].min_west;
2113
2114                         else
2115                         {
2116                                 if (abs(dem[indx].min_west-min_west)<180)
2117                                 {
2118                                         if (dem[indx].min_west<min_west)
2119                                                 min_west=dem[indx].min_west;
2120                                 }
2121
2122                                 else
2123                                 {
2124                                         if (dem[indx].min_west>min_west)
2125                                                 min_west=dem[indx].min_west;
2126                                 }
2127                         }
2128
2129                         fprintf(stdout," Done!\n");
2130                         fflush(stdout);
2131
2132                         return_value=1;
2133                 }
2134         }
2135
2136         return return_value;
2137 }
2138
2139 void LoadCities(char *filename)
2140 {
2141         /* This function reads SPLAT! city/site files, and plots
2142            the locations and names of the cities and site locations
2143            read on topographic maps generated by SPLAT! */
2144
2145         int     x, y, z;
2146         char    input[80], str[3][80], *s=NULL;
2147         struct  site city_site;
2148         FILE    *fd=NULL;
2149
2150         fd=fopen(filename,"r");
2151
2152         if (fd!=NULL)
2153         {
2154                 s=fgets(input,78,fd);
2155
2156                 fprintf(stdout,"\nReading \"%s\"... ",filename);
2157                 fflush(stdout);
2158
2159                 while (fd!=NULL && feof(fd)==0)
2160                 {
2161                         /* Parse line for name, latitude, and longitude */
2162
2163                         for (x=0, y=0, z=0; x<78 && input[x]!=0 && z<3; x++)
2164                         {
2165                                 if (input[x]!=',' && y<78)
2166                                 {
2167                                         str[z][y]=input[x];
2168                                         y++;
2169                                 }
2170
2171                                 else
2172                                 {
2173                                         str[z][y]=0;
2174                                         z++;
2175                                         y=0;
2176                                 }
2177                         }
2178
2179                         strncpy(city_site.name,str[0],49);
2180                         city_site.lat=ReadBearing(str[1]);
2181                         city_site.lon=ReadBearing(str[2]);
2182                         city_site.alt=0.0;
2183
2184                         if (city_site.lon<0.0)
2185                                 city_site.lon+=360.0;
2186
2187                         PlaceMarker(city_site);
2188
2189                         s=fgets(input,78,fd);
2190                 }
2191
2192                 fclose(fd);
2193                 fprintf(stdout,"Done!");
2194                 fflush(stdout);
2195         }
2196
2197         else
2198                 fprintf(stderr,"\n*** ERROR: \"%s\": not found!",filename);
2199 }
2200
2201 void LoadUDT(char *filename)
2202 {
2203         /* This function reads a file containing User-Defined Terrain
2204            features for their addition to the digital elevation model
2205            data used by SPLAT!.  Elevations in the UDT file are evaluated
2206            and then copied into a temporary file under /tmp.  Then the
2207            contents of the temp file are scanned, and if found to be unique,
2208            are added to the ground elevations described by the digital
2209            elevation data already loaded into memory. */
2210
2211         int     i, x, y, z, ypix, xpix, tempxpix, tempypix, fd=0, n=0;
2212         char    input[80], str[3][80], tempname[15], *pointer=NULL, *s=NULL;
2213         double  latitude, longitude, height, tempheight;
2214         FILE    *fd1=NULL, *fd2=NULL;
2215
2216         strcpy(tempname,"/tmp/XXXXXX\0");
2217
2218         fd1=fopen(filename,"r");
2219
2220         if (fd1!=NULL)
2221         {
2222                 fd=mkstemp(tempname);
2223                 fd2=fopen(tempname,"w");
2224
2225                 s=fgets(input,78,fd1);
2226
2227                 pointer=strchr(input,';');
2228
2229                 if (pointer!=NULL)
2230                         *pointer=0;
2231
2232                 fprintf(stdout,"\nReading \"%s\"... ",filename);
2233                 fflush(stdout);
2234
2235                 while (feof(fd1)==0)
2236                 {
2237                         /* Parse line for latitude, longitude, height */
2238
2239                         for (x=0, y=0, z=0; x<78 && input[x]!=0 && z<3; x++)
2240                         {
2241                                 if (input[x]!=',' && y<78)
2242                                 {
2243                                         str[z][y]=input[x];
2244                                         y++;
2245                                 }
2246
2247                                 else
2248                                 {
2249                                         str[z][y]=0;
2250                                         z++;
2251                                         y=0;
2252                                 }
2253                         }
2254
2255                         latitude=ReadBearing(str[0]);
2256                         longitude=ReadBearing(str[1]);
2257
2258                         if (longitude<0.0)
2259                                 longitude+=360.0;
2260
2261                         /* Remove <CR> and/or <LF> from antenna height string */
2262
2263                         for (i=0; str[2][i]!=13 && str[2][i]!=10 && str[2][i]!=0; i++);
2264
2265                         str[2][i]=0;
2266
2267                         /* The terrain feature may be expressed in either
2268                            feet or meters.  If the letter 'M' or 'm' is
2269                            discovered in the string, then this is an
2270                            indication that the value given is expressed
2271                            in meters.  Otherwise the height is interpreted
2272                            as being expressed in feet.  */
2273
2274                         for (i=0; str[2][i]!='M' && str[2][i]!='m' && str[2][i]!=0 && i<48; i++);
2275
2276                         if (str[2][i]=='M' || str[2][i]=='m')
2277                         {
2278                                 str[2][i]=0;
2279                                 height=rint(atof(str[2]));
2280                         }
2281
2282                         else
2283                         {
2284                                 str[2][i]=0;
2285                                 height=rint(METERS_PER_FOOT*atof(str[2]));
2286                         }
2287
2288                         if (height>0.0)
2289                                 fprintf(fd2,"%d, %d, %f\n",(int)rint(latitude/dpp), (int)rint(longitude/dpp), height);
2290
2291                         s=fgets(input,78,fd1);
2292
2293                         pointer=strchr(input,';');
2294
2295                         if (pointer!=NULL)
2296                                 *pointer=0;
2297                 }
2298
2299                 fclose(fd1);
2300                 fclose(fd2);
2301                 close(fd);
2302
2303                 fprintf(stdout,"Done!");
2304                 fflush(stdout);
2305
2306                 fd1=fopen(tempname,"r");
2307                 fd2=fopen(tempname,"r");
2308
2309                 y=0;
2310
2311                 n=fscanf(fd1,"%d, %d, %lf", &xpix, &ypix, &height);
2312
2313                 do
2314                 {
2315                         x=0;
2316                         z=0;
2317
2318                         n=fscanf(fd2,"%d, %d, %lf", &tempxpix, &tempypix, &tempheight);
2319
2320                         do
2321                         {
2322                                 if (x>y && xpix==tempxpix && ypix==tempypix)
2323                                 {
2324                                                 z=1;  /* Dupe! */
2325
2326                                                 if (tempheight>height)
2327                                                         height=tempheight;
2328                                 }
2329
2330                                 else
2331                                 {
2332                                         n=fscanf(fd2,"%d, %d, %lf", &tempxpix, &tempypix, &tempheight);
2333                                         x++;
2334                                 }
2335
2336                         } while (feof(fd2)==0 && z==0);
2337
2338                         if (z==0)  /* No duplicate found */
2339                                 AddElevation(xpix*dpp, ypix*dpp, height);
2340
2341                         n=fscanf(fd1,"%d, %d, %lf", &xpix, &ypix, &height);
2342                         y++;
2343
2344                         rewind(fd2);
2345
2346                 } while (feof(fd1)==0);
2347
2348                 fclose(fd1);
2349                 fclose(fd2);
2350                 unlink(tempname);
2351         }
2352
2353         else
2354                 fprintf(stderr,"\n*** ERROR: \"%s\": not found!",filename);
2355
2356         fprintf(stdout,"\n");
2357 }
2358
2359 void LoadBoundaries(char *filename)
2360 {
2361         /* This function reads Cartographic Boundary Files available from
2362            the U.S. Census Bureau, and plots the data contained in those
2363            files on the PPM Map generated by SPLAT!.  Such files contain
2364            the coordinates that describe the boundaries of cities,
2365            counties, and states. */
2366
2367         int     x;
2368         double  lat0, lon0, lat1, lon1;
2369         char    string[80], *s=NULL;
2370         struct  site source, destination;
2371         FILE    *fd=NULL;
2372
2373         fd=fopen(filename,"r");
2374
2375         if (fd!=NULL)
2376         {
2377                 s=fgets(string,78,fd);
2378
2379                 fprintf(stdout,"\nReading \"%s\"... ",filename);
2380                 fflush(stdout);
2381
2382                 do
2383                 {
2384                         s=fgets(string,78,fd);
2385                         sscanf(string,"%lf %lf", &lon0, &lat0);
2386                         s=fgets(string,78,fd);
2387
2388                         do
2389                         {
2390                                 sscanf(string,"%lf %lf", &lon1, &lat1);
2391
2392                                 source.lat=lat0;
2393                                 source.lon=(lon0>0.0 ? 360.0-lon0 : -lon0);
2394                                 destination.lat=lat1;
2395                                 destination.lon=(lon1>0.0 ? 360.0-lon1 : -lon1);
2396
2397                                 ReadPath(source,destination);
2398
2399                                 for (x=0; x<path.length; x++)
2400                                         OrMask(path.lat[x],path.lon[x],4);
2401
2402                                 lat0=lat1;
2403                                 lon0=lon1;
2404
2405                                 s=fgets(string,78,fd);
2406
2407                         } while (strncmp(string,"END",3)!=0 && feof(fd)==0);
2408
2409                         s=fgets(string,78,fd);
2410
2411                 } while (strncmp(string,"END",3)!=0 && feof(fd)==0);
2412
2413                 fclose(fd);
2414
2415                 fprintf(stdout,"Done!");
2416                 fflush(stdout);
2417         }
2418
2419         else
2420                 fprintf(stderr,"\n*** ERROR: \"%s\": not found!",filename);
2421 }
2422
2423 char ReadLRParm(struct site txsite, char forced_read)
2424 {
2425         /* This function reads Longley-Rice parameter data for the
2426            transmitter site.  The file name is the same as the txsite,
2427            except the filename extension is .lrp.  If the needed file
2428            is not found, then the file "splat.lrp" is read from the
2429            current working directory.  Failure to load this file under
2430            a forced_read condition will result in the default parameters
2431            hard coded into this function to be used and written to
2432            "splat.lrp". */
2433
2434         double  din;
2435         char    filename[255], string[80], *pointer=NULL, *s=NULL, return_value=0;
2436         int     iin, ok=0, x;
2437         FILE    *fd=NULL, *outfile=NULL;
2438
2439         /* Default parameters */
2440
2441         LR.eps_dielect=0.0;
2442         LR.sgm_conductivity=0.0;
2443         LR.eno_ns_surfref=0.0;
2444         LR.frq_mhz=0.0;
2445         LR.radio_climate=0;
2446         LR.pol=0;
2447         LR.conf=0.0;
2448         LR.rel=0.0;
2449         LR.erp=0.0;
2450
2451         /* Generate .lrp filename from txsite filename. */
2452
2453         for (x=0; txsite.filename[x]!='.' && txsite.filename[x]!=0 && x<250; x++)
2454                 filename[x]=txsite.filename[x];
2455
2456         filename[x]='.';
2457         filename[x+1]='l';
2458         filename[x+2]='r';
2459         filename[x+3]='p';
2460         filename[x+4]=0;
2461
2462         fd=fopen(filename,"r");
2463
2464         if (fd==NULL)
2465         {
2466                 /* Load default "splat.lrp" file */
2467
2468                 strncpy(filename,"splat.lrp\0",10);
2469                 fd=fopen(filename,"r");
2470         }
2471
2472         if (fd!=NULL)
2473         {
2474                 s=fgets(string,80,fd);
2475
2476                 pointer=strchr(string,';');
2477
2478                 if (pointer!=NULL)
2479                         *pointer=0;
2480
2481                 ok=sscanf(string,"%lf", &din);
2482
2483                 if (ok)
2484                 {
2485                         LR.eps_dielect=din;
2486
2487                         s=fgets(string,80,fd);
2488
2489                         pointer=strchr(string,';');
2490
2491                         if (pointer!=NULL)
2492                                 *pointer=0;
2493
2494                         ok=sscanf(string,"%lf", &din);
2495                 }
2496
2497                 if (ok)
2498                 {
2499                         LR.sgm_conductivity=din;
2500
2501                         s=fgets(string,80,fd);
2502
2503                         pointer=strchr(string,';');
2504
2505                         if (pointer!=NULL)
2506                                 *pointer=0;
2507
2508                         ok=sscanf(string,"%lf", &din);
2509                 }
2510
2511                 if (ok)
2512                 {
2513                         LR.eno_ns_surfref=din;
2514
2515                         s=fgets(string,80,fd);
2516
2517                         pointer=strchr(string,';');
2518
2519                         if (pointer!=NULL)
2520                                 *pointer=0;
2521
2522                         ok=sscanf(string,"%lf", &din);
2523                 }
2524
2525                 if (ok)
2526                 {
2527                         LR.frq_mhz=din;
2528
2529                         s=fgets(string,80,fd);
2530
2531                         pointer=strchr(string,';');
2532
2533                         if (pointer!=NULL)
2534                                 *pointer=0;
2535
2536                         ok=sscanf(string,"%d", &iin);
2537                 }
2538
2539                 if (ok)
2540                 {
2541                         LR.radio_climate=iin;
2542
2543                         s=fgets(string,80,fd);
2544
2545                         pointer=strchr(string,';');
2546
2547                         if (pointer!=NULL)
2548                                 *pointer=0;
2549
2550                         ok=sscanf(string,"%d", &iin);
2551                 }
2552
2553                 if (ok)
2554                 {
2555                         LR.pol=iin;
2556
2557                         s=fgets(string,80,fd);
2558
2559                         pointer=strchr(string,';');
2560
2561                         if (pointer!=NULL)
2562                                 *pointer=0;
2563
2564                         ok=sscanf(string,"%lf", &din);
2565                 }
2566
2567                 if (ok)
2568                 {
2569                         LR.conf=din;
2570
2571                         s=fgets(string,80,fd);
2572
2573                         pointer=strchr(string,';');
2574
2575                         if (pointer!=NULL)
2576                                 *pointer=0;
2577
2578                         ok=sscanf(string,"%lf", &din);
2579                 }
2580
2581                 if (ok)
2582                 {
2583                         LR.rel=din;
2584                         din=0.0;
2585                         return_value=1;
2586
2587                         if (fgets(string,80,fd)!=NULL)
2588                         {
2589                                 pointer=strchr(string,':');
2590
2591                                 if (pointer!=NULL)
2592                                         *pointer=0;
2593
2594                                 if (sscanf(string,"%lf", &din))
2595                                         LR.erp=din;
2596
2597                                 /* ERP in SPLAT! is referenced to 1 Watt
2598                                    into a dipole (0 dBd).  If ERP is
2599                                    expressed in dBm (referenced to a
2600                                    0 dBi radiator), convert dBm in EIRP
2601                                    to ERP.  */
2602
2603                                 if ((strstr(string, "dBm")!=NULL) || (strstr(string,"dbm")!=NULL))
2604                                         LR.erp=(pow(10.0,(LR.erp-32.14)/10.0));
2605                         }
2606                 }
2607
2608                 fclose(fd);
2609
2610                 if (forced_erp!=-1.0)
2611                         LR.erp=forced_erp;
2612
2613                 if (forced_freq>=20.0 && forced_freq<=20000.0)
2614                         LR.frq_mhz=forced_freq;
2615
2616                 if (ok)
2617                         LoadPAT(filename);
2618         } 
2619
2620         if (fd==NULL && forced_read)
2621         {
2622                 /* Assign some default parameters
2623                    for use in this run. */
2624
2625                 LR.eps_dielect=15.0;
2626                 LR.sgm_conductivity=0.005;
2627                 LR.eno_ns_surfref=301.0;
2628                 LR.frq_mhz=300.0;
2629                 LR.radio_climate=5;
2630                 LR.pol=0;
2631                 LR.conf=0.50;
2632                 LR.rel=0.50;
2633                 LR.erp=0.0;
2634
2635                 /* Write them to a "splat.lrp" file. */
2636
2637                 outfile=fopen("splat.lrp","w");
2638
2639                 fprintf(outfile,"%.3f\t; Earth Dielectric Constant (Relative permittivity)\n",LR.eps_dielect);
2640                 fprintf(outfile,"%.3f\t; Earth Conductivity (Siemens per meter)\n", LR.sgm_conductivity);
2641                 fprintf(outfile,"%.3f\t; Atmospheric Bending Constant (N-Units)\n",LR.eno_ns_surfref);
2642                 fprintf(outfile,"%.3f\t; Frequency in MHz (20 MHz to 20 GHz)\n", LR.frq_mhz);
2643                 fprintf(outfile,"%d\t; Radio Climate\n",LR.radio_climate);
2644                 fprintf(outfile,"%d\t; Polarization (0 = Horizontal, 1 = Vertical)\n", LR.pol);
2645                 fprintf(outfile,"%.2f\t; Fraction of situations\n",LR.conf);
2646                 fprintf(outfile,"%.2f\t; Fraction of time\n",LR.rel);
2647                 fprintf(outfile,"%.2f\t; Transmitter Effective Radiated Power in Watts or dBm (optional)\n",LR.erp);
2648                 fprintf(outfile,"\nPlease consult SPLAT! documentation for the meaning and use of this data.\n");
2649
2650                 fclose(outfile);
2651
2652                 return_value=1;
2653
2654                 fprintf(stderr,"\n\n%c*** There were problems reading your \"%s\" file! ***\nA \"splat.lrp\" file was written to your directory with default data.\n",7,filename);
2655         }
2656
2657         else if (forced_read==0)
2658                         return_value=0;
2659
2660         if (forced_read && (fd==NULL || ok==0))
2661         {
2662                 LR.eps_dielect=15.0;
2663                 LR.sgm_conductivity=0.005;
2664                 LR.eno_ns_surfref=301.0;
2665                 LR.frq_mhz=300.0;
2666                 LR.radio_climate=5;
2667                 LR.pol=0;
2668                 LR.conf=0.50;
2669                 LR.rel=0.50;
2670                 LR.erp=0.0;
2671
2672                 fprintf(stderr,"Longley-Rice default parameters have been assumed for this analysis.\n");
2673
2674                 return_value=1;
2675         }
2676
2677         return (return_value);
2678 }
2679
2680 void PlotPath(struct site source, struct site destination, char mask_value)
2681 {
2682         /* This function analyzes the path between the source and
2683            destination locations.  It determines which points along
2684            the path have line-of-sight visibility to the source.
2685            Points along with path having line-of-sight visibility
2686            to the source at an AGL altitude equal to that of the
2687            destination location are stored by setting bit 1 in the
2688            mask[][] array, which are displayed in green when PPM
2689            maps are later generated by SPLAT!. */
2690
2691         char block;
2692         int x, y;
2693         register double cos_xmtr_angle, cos_test_angle, test_alt;
2694         double distance, rx_alt, tx_alt;
2695
2696         ReadPath(source,destination);
2697
2698         for (y=0; y<path.length; y++)
2699         {
2700                 /* Test this point only if it hasn't been already
2701                    tested and found to be free of obstructions. */
2702
2703                 if ((GetMask(path.lat[y],path.lon[y])&mask_value)==0)
2704                 {
2705                         distance=5280.0*path.distance[y];
2706                         tx_alt=earthradius+source.alt+path.elevation[0];
2707                         rx_alt=earthradius+destination.alt+path.elevation[y];
2708
2709                         /* Calculate the cosine of the elevation of the
2710                            transmitter as seen at the temp rx point. */
2711
2712                         cos_xmtr_angle=((rx_alt*rx_alt)+(distance*distance)-(tx_alt*tx_alt))/(2.0*rx_alt*distance);
2713
2714                         for (x=y, block=0; x>=0 && block==0; x--)
2715                         {
2716                                 distance=5280.0*(path.distance[y]-path.distance[x]);
2717                                 test_alt=earthradius+(path.elevation[x]==0.0?path.elevation[x]:path.elevation[x]+clutter);
2718
2719                                 cos_test_angle=((rx_alt*rx_alt)+(distance*distance)-(test_alt*test_alt))/(2.0*rx_alt*distance);
2720
2721                                 /* Compare these two angles to determine if
2722                                    an obstruction exists.  Since we're comparing
2723                                    the cosines of these angles rather than
2724                                    the angles themselves, the following "if"
2725                                    statement is reversed from what it would
2726                                    be if the actual angles were compared. */
2727
2728                                 if (cos_xmtr_angle>=cos_test_angle)
2729                                         block=1;
2730                         }
2731
2732                         if (block==0)
2733                                 OrMask(path.lat[y],path.lon[y],mask_value);
2734                 }
2735         }
2736 }
2737
2738 void PlotLRPath(struct site source, struct site destination, unsigned char mask_value, FILE *fd)
2739 {
2740         /* This function plots the RF path loss between source and
2741            destination points based on the Longley-Rice propagation
2742            model, taking into account antenna pattern data, if available. */
2743
2744         int     x, y, ifs, ofs, errnum;
2745         char    block=0, strmode[100];
2746         double  loss, azimuth, pattern=0.0, 
2747                 xmtr_alt, dest_alt, xmtr_alt2, dest_alt2,
2748                 cos_rcvr_angle, cos_test_angle=0.0, test_alt,
2749                 elevation=0.0, distance=0.0, four_thirds_earth,
2750                 field_strength=0.0, rxp, dBm;
2751         struct  site temp;
2752
2753         ReadPath(source,destination);
2754
2755         four_thirds_earth=FOUR_THIRDS*EARTHRADIUS;
2756
2757         /* Copy elevations plus clutter along path into the elev[] array. */
2758
2759         for (x=1; x<path.length-1; x++)
2760                 elev[x+2]=(path.elevation[x]==0.0?path.elevation[x]*METERS_PER_FOOT:(clutter+path.elevation[x])*METERS_PER_FOOT);
2761
2762         /* Copy ending points without clutter */
2763
2764         elev[2]=path.elevation[0]*METERS_PER_FOOT;
2765         elev[path.length+1]=path.elevation[path.length-1]*METERS_PER_FOOT;
2766
2767         /* Since the only energy the Longley-Rice model considers
2768            reaching the destination is based on what is scattered
2769            or deflected from the first obstruction along the path,
2770            we first need to find the location and elevation angle
2771            of that first obstruction (if it exists).  This is done
2772            using a 4/3rds Earth radius to match the model used by
2773            Longley-Rice.  This information is required for properly
2774            integrating the antenna's elevation pattern into the
2775            calculation for overall path loss. */
2776
2777         for (y=2; (y<(path.length-1) && path.distance[y]<=max_range); y++)
2778         {
2779                 /* Process this point only if it
2780                    has not already been processed. */
2781
2782                 if ((GetMask(path.lat[y],path.lon[y])&248)!=(mask_value<<3))
2783                 {
2784                         distance=5280.0*path.distance[y];
2785                         xmtr_alt=four_thirds_earth+source.alt+path.elevation[0];
2786                         dest_alt=four_thirds_earth+destination.alt+path.elevation[y];
2787                         dest_alt2=dest_alt*dest_alt;
2788                         xmtr_alt2=xmtr_alt*xmtr_alt;
2789
2790                         /* Calculate the cosine of the elevation of
2791                            the receiver as seen by the transmitter. */
2792
2793                         cos_rcvr_angle=((xmtr_alt2)+(distance*distance)-(dest_alt2))/(2.0*xmtr_alt*distance);
2794
2795                         if (cos_rcvr_angle>1.0)
2796                                 cos_rcvr_angle=1.0;
2797
2798                         if (cos_rcvr_angle<-1.0)
2799                                 cos_rcvr_angle=-1.0;
2800
2801                         if (got_elevation_pattern || fd!=NULL)
2802                         {
2803                                 /* Determine the elevation angle to the first obstruction
2804                                    along the path IF elevation pattern data is available
2805                                    or an output (.ano) file has been designated. */
2806
2807                                 for (x=2, block=0; (x<y && block==0); x++)
2808                                 {
2809                                         distance=5280.0*path.distance[x];
2810
2811                                         test_alt=four_thirds_earth+(path.elevation[x]==0.0?path.elevation[x]:path.elevation[x]+clutter);
2812
2813                                         /* Calculate the cosine of the elevation
2814                                            angle of the terrain (test point)
2815                                            as seen by the transmitter. */
2816
2817                                         cos_test_angle=((xmtr_alt2)+(distance*distance)-(test_alt*test_alt))/(2.0*xmtr_alt*distance);
2818
2819                                         if (cos_test_angle>1.0)
2820                                                 cos_test_angle=1.0;
2821
2822                                         if (cos_test_angle<-1.0)
2823                                                 cos_test_angle=-1.0;
2824
2825                                         /* Compare these two angles to determine if
2826                                            an obstruction exists.  Since we're comparing
2827                                            the cosines of these angles rather than
2828                                            the angles themselves, the sense of the
2829                                            following "if" statement is reversed from
2830                                            what it would be if the angles themselves
2831                                            were compared. */
2832
2833                                         if (cos_rcvr_angle>=cos_test_angle)
2834                                                 block=1;
2835                                 }
2836
2837                                 if (block)
2838                                         elevation=((acos(cos_test_angle))/DEG2RAD)-90.0;
2839                                 else
2840                                         elevation=((acos(cos_rcvr_angle))/DEG2RAD)-90.0;
2841                         }
2842
2843                         /* Determine attenuation for each point along the
2844                            path using Longley-Rice's point_to_point mode
2845                            starting at y=2 (number_of_points = 1), the
2846                            shortest distance terrain can play a role in
2847                            path loss. */
2848  
2849                         elev[0]=y-1;  /* (number of points - 1) */
2850
2851                         /* Distance between elevation samples */
2852
2853                         elev[1]=METERS_PER_MILE*(path.distance[y]-path.distance[y-1]);
2854
2855                         point_to_point(elev,source.alt*METERS_PER_FOOT, 
2856                         destination.alt*METERS_PER_FOOT, LR.eps_dielect,
2857                         LR.sgm_conductivity, LR.eno_ns_surfref, LR.frq_mhz,
2858                         LR.radio_climate, LR.pol, LR.conf, LR.rel, loss,
2859                         strmode, errnum);
2860
2861                         temp.lat=path.lat[y];
2862                         temp.lon=path.lon[y];
2863
2864                         azimuth=(Azimuth(source,temp));
2865
2866                         if (fd!=NULL)
2867                                 fprintf(fd,"%.7f, %.7f, %.3f, %.3f, ",path.lat[y], path.lon[y], azimuth, elevation);
2868
2869                         /* If ERP==0, write path loss to alphanumeric
2870                            output file.  Otherwise, write field strength
2871                            or received power level (below), as appropriate. */
2872
2873                         if (fd!=NULL && LR.erp==0.0)
2874                                 fprintf(fd,"%.2f",loss);
2875
2876                         /* Integrate the antenna's radiation
2877                            pattern into the overall path loss. */
2878
2879                         x=(int)rint(10.0*(10.0-elevation));
2880
2881                         if (x>=0 && x<=1000)
2882                         {
2883                                 azimuth=rint(azimuth);
2884
2885                                 pattern=(double)LR.antenna_pattern[(int)azimuth][x];
2886
2887                                 if (pattern!=0.0)
2888                                 {
2889                                         pattern=20.0*log10(pattern);
2890                                         loss-=pattern;
2891                                 }
2892                         }
2893
2894                         if (LR.erp!=0.0)
2895                         {
2896                                 if (dbm)
2897                                 {
2898                                         /* dBm is based on EIRP (ERP + 2.14) */
2899
2900                                         rxp=LR.erp/(pow(10.0,(loss-2.14)/10.0));
2901
2902                                         dBm=10.0*(log10(rxp*1000.0));
2903
2904                                         if (fd!=NULL)
2905                                                 fprintf(fd,"%.3f",dBm);
2906
2907                                         /* Scale roughly between 0 and 255 */
2908
2909                                         ifs=200+(int)rint(dBm);
2910
2911                                         if (ifs<0)
2912                                                 ifs=0;
2913
2914                                         if (ifs>255)
2915                                                 ifs=255;
2916
2917                                         ofs=GetSignal(path.lat[y],path.lon[y]);
2918
2919                                         if (ofs>ifs)
2920                                                 ifs=ofs;
2921
2922                                         PutSignal(path.lat[y],path.lon[y],(unsigned char)ifs);
2923                                 }
2924
2925                                 else
2926                                 {
2927                                         field_strength=(139.4+(20.0*log10(LR.frq_mhz))-loss)+(10.0*log10(LR.erp/1000.0));
2928
2929                                         ifs=100+(int)rint(field_strength);
2930
2931                                         if (ifs<0)
2932                                                 ifs=0;
2933
2934                                         if (ifs>255)
2935                                                 ifs=255;
2936
2937                                         ofs=GetSignal(path.lat[y],path.lon[y]);
2938
2939                                         if (ofs>ifs)
2940                                                 ifs=ofs;
2941
2942                                         PutSignal(path.lat[y],path.lon[y],(unsigned char)ifs);
2943         
2944                                         if (fd!=NULL)
2945                                                 fprintf(fd,"%.3f",field_strength);
2946                                 }
2947                         }
2948
2949                         else
2950                         {
2951                                 if (loss>255)
2952                                         ifs=255;
2953                                 else
2954                                         ifs=(int)rint(loss);
2955
2956                                 ofs=GetSignal(path.lat[y],path.lon[y]);
2957
2958                                 if (ofs<ifs && ofs!=0)
2959                                         ifs=ofs;
2960
2961                                 PutSignal(path.lat[y],path.lon[y],(unsigned char)ifs);
2962                         }
2963
2964                         if (fd!=NULL)
2965                         {
2966                                 if (block)
2967                                         fprintf(fd," *");
2968
2969                                 fprintf(fd,"\n");
2970                         }
2971
2972                         /* Mark this point as having been analyzed */
2973
2974                         PutMask(path.lat[y],path.lon[y],(GetMask(path.lat[y],path.lon[y])&7)+(mask_value<<3));
2975                 }
2976         }
2977 }
2978
2979 void PlotLOSMap(struct site source, double altitude)
2980 {
2981         /* This function performs a 360 degree sweep around the
2982            transmitter site (source location), and plots the
2983            line-of-sight coverage of the transmitter on the SPLAT!
2984            generated topographic map based on a receiver located
2985            at the specified altitude (in feet AGL).  Results
2986            are stored in memory, and written out in the form
2987            of a topographic map when the WritePPM() function
2988            is later invoked. */
2989
2990         int y, z, count;
2991         struct site edge;
2992         unsigned char symbol[4], x;
2993         double lat, lon, minwest, maxnorth, th;
2994         static unsigned char mask_value=1;
2995
2996         symbol[0]='.';
2997         symbol[1]='o';
2998         symbol[2]='O';
2999         symbol[3]='o';
3000
3001         count=0;        
3002
3003         fprintf(stdout,"\nComputing line-of-sight coverage of \"%s\" with an RX antenna\nat %.2f %s AGL",source.name,metric?altitude*METERS_PER_FOOT:altitude,metric?"meters":"feet");
3004
3005         if (clutter>0.0)
3006                 fprintf(stdout," and %.2f %s of ground clutter",metric?clutter*METERS_PER_FOOT:clutter,metric?"meters":"feet");
3007
3008         fprintf(stdout,"...\n\n 0%c to  25%c ",37,37);
3009         fflush(stdout);
3010
3011         /* th=pixels/degree divided by 64 loops per
3012            progress indicator symbol (.oOo) printed. */
3013         
3014         th=ppd/64.0;
3015
3016         z=(int)(th*ReduceAngle(max_west-min_west));
3017
3018         minwest=dpp+(double)min_west;
3019         maxnorth=(double)max_north-dpp;
3020
3021         for (lon=minwest, x=0, y=0; (LonDiff(lon,(double)max_west)<=0.0); y++, lon=minwest+(dpp*(double)y))
3022         {
3023                 if (lon>=360.0)
3024                         lon-=360.0;
3025
3026                 edge.lat=max_north;
3027                 edge.lon=lon;
3028                 edge.alt=altitude;
3029
3030                 PlotPath(source,edge,mask_value);
3031                 count++;
3032
3033                 if (count==z) 
3034                 {
3035                         fprintf(stdout,"%c",symbol[x]);
3036                         fflush(stdout);
3037                         count=0;
3038
3039                         if (x==3)
3040                                 x=0;
3041                         else
3042                                 x++;
3043                 }
3044         }
3045
3046         count=0;
3047         fprintf(stdout,"\n25%c to  50%c ",37,37);
3048         fflush(stdout);
3049         
3050         z=(int)(th*(double)(max_north-min_north));
3051
3052         for (lat=maxnorth, x=0, y=0; lat>=(double)min_north; y++, lat=maxnorth-(dpp*(double)y))
3053         {
3054                 edge.lat=lat;
3055                 edge.lon=min_west;
3056                 edge.alt=altitude;
3057
3058                 PlotPath(source,edge,mask_value);
3059                 count++;
3060
3061                 if (count==z) 
3062                 {
3063                         fprintf(stdout,"%c",symbol[x]);
3064                         fflush(stdout);
3065                         count=0;
3066
3067                         if (x==3)
3068                                 x=0;
3069                         else
3070                                 x++;
3071                 }
3072         }
3073
3074         count=0;
3075         fprintf(stdout,"\n50%c to  75%c ",37,37);
3076         fflush(stdout);
3077
3078         z=(int)(th*ReduceAngle(max_west-min_west));
3079
3080         for (lon=minwest, x=0, y=0; (LonDiff(lon,(double)max_west)<=0.0); y++, lon=minwest+(dpp*(double)y))
3081         {
3082                 if (lon>=360.0)
3083                         lon-=360.0;
3084
3085                 edge.lat=min_north;
3086                 edge.lon=lon;
3087                 edge.alt=altitude;
3088
3089                 PlotPath(source,edge,mask_value);
3090                 count++;
3091
3092                 if (count==z)
3093                 {
3094                         fprintf(stdout,"%c",symbol[x]);
3095                         fflush(stdout);
3096                         count=0;
3097
3098                         if (x==3)
3099                                 x=0;
3100                         else
3101                                 x++;
3102                 }
3103         }
3104
3105         count=0;
3106         fprintf(stdout,"\n75%c to 100%c ",37,37);
3107         fflush(stdout);
3108         
3109         z=(int)(th*(double)(max_north-min_north));
3110
3111         for (lat=(double)min_north, x=0, y=0; lat<(double)max_north; y++, lat=(double)min_north+(dpp*(double)y))
3112         {
3113                 edge.lat=lat;
3114                 edge.lon=max_west;
3115                 edge.alt=altitude;
3116
3117                 PlotPath(source,edge,mask_value);
3118                 count++;
3119
3120                 if (count==z)
3121                 {
3122                         fprintf(stdout,"%c",symbol[x]);
3123                         fflush(stdout);
3124                         count=0;
3125
3126                         if (x==3)
3127                                 x=0;
3128                         else
3129                                 x++;
3130                 }
3131         }
3132
3133         fprintf(stdout,"\nDone!\n");
3134         fflush(stdout);
3135
3136         /* Assign next mask value */
3137
3138         switch (mask_value)
3139         {
3140                 case 1:
3141                         mask_value=8;
3142                         break;
3143
3144                 case 8:
3145                         mask_value=16;
3146                         break;
3147
3148                 case 16:
3149                         mask_value=32;
3150         }
3151 }
3152
3153 void PlotLRMap(struct site source, double altitude, char *plo_filename)
3154 {
3155         /* This function performs a 360 degree sweep around the
3156            transmitter site (source location), and plots the
3157            Longley-Rice attenuation on the SPLAT! generated
3158            topographic map based on a receiver located at
3159            the specified altitude (in feet AGL).  Results
3160            are stored in memory, and written out in the form
3161            of a topographic map when the WritePPMLR() or
3162            WritePPMSS() functions are later invoked. */
3163
3164         int y, z, count;
3165         struct site edge;
3166         double lat, lon, minwest, maxnorth, th;
3167         unsigned char x, symbol[4];
3168         static unsigned char mask_value=1;
3169         FILE *fd=NULL;
3170
3171         minwest=dpp+(double)min_west;
3172         maxnorth=(double)max_north-dpp;
3173
3174         symbol[0]='.';
3175         symbol[1]='o';
3176         symbol[2]='O';
3177         symbol[3]='o';
3178
3179         count=0;
3180
3181         fprintf(stdout,"\nComputing Longley-Rice ");
3182
3183         if (LR.erp==0.0)
3184                 fprintf(stdout,"path loss");
3185         else
3186         {
3187                 if (dbm)
3188                         fprintf(stdout,"signal power level");
3189                 else
3190                         fprintf(stdout,"field strength");
3191         }
3192  
3193         fprintf(stdout," contours of \"%s\"\nout to a radius of %.2f %s with an RX antenna at %.2f %s AGL",source.name,metric?max_range*KM_PER_MILE:max_range,metric?"kilometers":"miles",metric?altitude*METERS_PER_FOOT:altitude,metric?"meters":"feet");
3194
3195         if (clutter>0.0)
3196                 fprintf(stdout,"\nand %.2f %s of ground clutter",metric?clutter*METERS_PER_FOOT:clutter,metric?"meters":"feet");
3197
3198         fprintf(stdout,"...\n\n 0%c to  25%c ",37,37);
3199         fflush(stdout);
3200
3201         if (plo_filename[0]!=0)
3202                 fd=fopen(plo_filename,"wb");
3203
3204         if (fd!=NULL)
3205         {
3206                 /* Write header information to output file */
3207
3208                 fprintf(fd,"%d, %d\t; max_west, min_west\n%d, %d\t; max_north, min_north\n",max_west, min_west, max_north, min_north);
3209         }
3210
3211         /* th=pixels/degree divided by 64 loops per
3212            progress indicator symbol (.oOo) printed. */
3213         
3214         th=ppd/64.0;
3215
3216         z=(int)(th*ReduceAngle(max_west-min_west));
3217
3218         for (lon=minwest, x=0, y=0; (LonDiff(lon,(double)max_west)<=0.0); y++, lon=minwest+(dpp*(double)y))
3219         {
3220                 if (lon>=360.0)
3221                         lon-=360.0;
3222
3223                 edge.lat=max_north;
3224                 edge.lon=lon;
3225                 edge.alt=altitude;
3226
3227                 PlotLRPath(source,edge,mask_value,fd);
3228                 count++;
3229
3230                 if (count==z) 
3231                 {
3232                         fprintf(stdout,"%c",symbol[x]);
3233                         fflush(stdout);
3234                         count=0;
3235
3236                         if (x==3)
3237                                 x=0;
3238                         else
3239                                 x++;
3240                 }
3241         }
3242
3243         count=0;
3244         fprintf(stdout,"\n25%c to  50%c ",37,37);
3245         fflush(stdout);
3246         
3247         z=(int)(th*(double)(max_north-min_north));
3248
3249         for (lat=maxnorth, x=0, y=0; lat>=(double)min_north; y++, lat=maxnorth-(dpp*(double)y))
3250         {
3251                 edge.lat=lat;
3252                 edge.lon=min_west;
3253                 edge.alt=altitude;
3254
3255                 PlotLRPath(source,edge,mask_value,fd);
3256                 count++;
3257
3258                 if (count==z) 
3259                 {
3260                         fprintf(stdout,"%c",symbol[x]);
3261                         fflush(stdout);
3262                         count=0;
3263
3264                         if (x==3)
3265                                 x=0;
3266                         else
3267                                 x++;
3268                 }
3269         }
3270
3271         count=0;
3272         fprintf(stdout,"\n50%c to  75%c ",37,37);
3273         fflush(stdout);
3274
3275         z=(int)(th*ReduceAngle(max_west-min_west));
3276
3277         for (lon=minwest, x=0, y=0; (LonDiff(lon,(double)max_west)<=0.0); y++, lon=minwest+(dpp*(double)y))
3278         {
3279                 if (lon>=360.0)
3280                         lon-=360.0;
3281
3282                 edge.lat=min_north;
3283                 edge.lon=lon;
3284                 edge.alt=altitude;
3285
3286                 PlotLRPath(source,edge,mask_value,fd);
3287                 count++;
3288
3289                 if (count==z)
3290                 {
3291                         fprintf(stdout,"%c",symbol[x]);
3292                         fflush(stdout);
3293                         count=0;
3294
3295                         if (x==3)
3296                                 x=0;
3297                         else
3298                                 x++;
3299                 }
3300         }
3301
3302         count=0;
3303         fprintf(stdout,"\n75%c to 100%c ",37,37);
3304         fflush(stdout);
3305         
3306         z=(int)(th*(double)(max_north-min_north));
3307
3308         for (lat=(double)min_north, x=0, y=0; lat<(double)max_north; y++, lat=(double)min_north+(dpp*(double)y))
3309         {
3310                 edge.lat=lat;
3311                 edge.lon=max_west;
3312                 edge.alt=altitude;
3313
3314                 PlotLRPath(source,edge,mask_value,fd);
3315                 count++;
3316
3317                 if (count==z)
3318                 {
3319                         fprintf(stdout,"%c",symbol[x]);
3320                         fflush(stdout);
3321                         count=0;
3322
3323                         if (x==3)
3324                                 x=0;
3325                         else
3326                                 x++;
3327                 }
3328         }
3329
3330         if (fd!=NULL)
3331                 fclose(fd);
3332
3333         fprintf(stdout,"\nDone!\n");
3334         fflush(stdout);
3335
3336         if (mask_value<30)
3337                 mask_value++;
3338 }
3339
3340 void LoadSignalColors(struct site xmtr)
3341 {
3342         int x, y, ok, val[4];
3343         char filename[255], string[80], *pointer=NULL, *s=NULL;
3344         FILE *fd=NULL;
3345
3346         for (x=0; xmtr.filename[x]!='.' && xmtr.filename[x]!=0 && x<250; x++)
3347                 filename[x]=xmtr.filename[x];
3348
3349         filename[x]='.';
3350         filename[x+1]='s';
3351         filename[x+2]='c';
3352         filename[x+3]='f';
3353         filename[x+4]=0;
3354
3355         /* Default values */
3356
3357         region.level[0]=128;
3358         region.color[0][0]=255;
3359         region.color[0][1]=0;
3360         region.color[0][2]=0;
3361
3362         region.level[1]=118;
3363         region.color[1][0]=255;
3364         region.color[1][1]=165;
3365         region.color[1][2]=0;
3366
3367         region.level[2]=108;
3368         region.color[2][0]=255;
3369         region.color[2][1]=206;
3370         region.color[2][2]=0;
3371
3372         region.level[3]=98;
3373         region.color[3][0]=255;
3374         region.color[3][1]=255;
3375         region.color[3][2]=0;
3376
3377         region.level[4]=88;
3378         region.color[4][0]=184;
3379         region.color[4][1]=255;
3380         region.color[4][2]=0;
3381
3382         region.level[5]=78;
3383         region.color[5][0]=0;
3384         region.color[5][1]=255;
3385         region.color[5][2]=0;
3386
3387         region.level[6]=68;
3388         region.color[6][0]=0;
3389         region.color[6][1]=208;
3390         region.color[6][2]=0;
3391
3392         region.level[7]=58;
3393         region.color[7][0]=0;
3394         region.color[7][1]=196;
3395         region.color[7][2]=196;
3396
3397         region.level[8]=48;
3398         region.color[8][0]=0;
3399         region.color[8][1]=148;
3400         region.color[8][2]=255;
3401
3402         region.level[9]=38;
3403         region.color[9][0]=80;
3404         region.color[9][1]=80;
3405         region.color[9][2]=255;
3406
3407         region.level[10]=28;
3408         region.color[10][0]=0;
3409         region.color[10][1]=38;
3410         region.color[10][2]=255;
3411
3412         region.level[11]=18;
3413         region.color[11][0]=142;
3414         region.color[11][1]=63;
3415         region.color[11][2]=255;
3416
3417         region.level[12]=8;
3418         region.color[12][0]=140;
3419         region.color[12][1]=0;
3420         region.color[12][2]=128;
3421
3422         region.levels=13;
3423
3424         fd=fopen("splat.scf","r");
3425
3426         if (fd==NULL)
3427                 fd=fopen(filename,"r");
3428
3429         if (fd==NULL)
3430         {
3431                 fd=fopen(filename,"w");
3432
3433                 fprintf(fd,"; SPLAT! Auto-generated Signal Color Definition (\"%s\") File\n",filename);
3434                 fprintf(fd,";\n; Format for the parameters held in this file is as follows:\n;\n");
3435                 fprintf(fd,";    dBuV/m: red, green, blue\n;\n");
3436                 fprintf(fd,"; ...where \"dBuV/m\" is the signal strength (in dBuV/m) and\n");
3437                 fprintf(fd,"; \"red\", \"green\", and \"blue\" are the corresponding RGB color\n");
3438                 fprintf(fd,"; definitions ranging from 0 to 255 for the region specified.\n");
3439                 fprintf(fd,";\n; The following parameters may be edited and/or expanded\n");
3440                 fprintf(fd,"; for future runs of SPLAT!  A total of 32 contour regions\n");
3441                 fprintf(fd,"; may be defined in this file.\n;\n;\n");
3442
3443                 for (x=0; x<region.levels; x++)
3444                         fprintf(fd,"%3d: %3d, %3d, %3d\n",region.level[x], region.color[x][0], region.color[x][1], region.color[x][2]);
3445
3446                 fclose(fd);
3447         }
3448
3449         else
3450         {
3451                 x=0;
3452                 s=fgets(string,80,fd);
3453
3454                 while (x<32 && feof(fd)==0)
3455                 {
3456                         pointer=strchr(string,';');
3457
3458                         if (pointer!=NULL)
3459                                 *pointer=0;
3460
3461                         ok=sscanf(string,"%d: %d, %d, %d", &val[0], &val[1], &val[2], &val[3]);
3462
3463                         if (ok==4)
3464                         {
3465                                 for (y=0; y<4; y++)
3466                                 {
3467                                         if (val[y]>255)
3468                                                 val[y]=255;
3469
3470                                         if (val[y]<0)
3471                                                 val[y]=0;
3472                                 }
3473         
3474                                 region.level[x]=val[0];
3475                                 region.color[x][0]=val[1];
3476                                 region.color[x][1]=val[2];
3477                                 region.color[x][2]=val[3];
3478                                 x++;
3479                         }
3480
3481                         s=fgets(string,80,fd);
3482                 }
3483
3484                 fclose(fd);
3485                 region.levels=x;
3486         }
3487 }
3488
3489 void LoadLossColors(struct site xmtr)
3490 {
3491         int x, y, ok, val[4];
3492         char filename[255], string[80], *pointer=NULL, *s=NULL;
3493         FILE *fd=NULL;
3494
3495         for (x=0; xmtr.filename[x]!='.' && xmtr.filename[x]!=0 && x<250; x++)
3496                 filename[x]=xmtr.filename[x];
3497
3498         filename[x]='.';
3499         filename[x+1]='l';
3500         filename[x+2]='c';
3501         filename[x+3]='f';
3502         filename[x+4]=0;
3503
3504         /* Default values */
3505
3506         region.level[0]=80;
3507         region.color[0][0]=255;
3508         region.color[0][1]=0;
3509         region.color[0][2]=0;
3510
3511         region.level[1]=90;
3512         region.color[1][0]=255;
3513         region.color[1][1]=128;
3514         region.color[1][2]=0;
3515
3516         region.level[2]=100;
3517         region.color[2][0]=255;
3518         region.color[2][1]=165;
3519         region.color[2][2]=0;
3520
3521         region.level[3]=110;
3522         region.color[3][0]=255;
3523         region.color[3][1]=206;
3524         region.color[3][2]=0;
3525
3526         region.level[4]=120;
3527         region.color[4][0]=255;
3528         region.color[4][1]=255;
3529         region.color[4][2]=0;
3530
3531         region.level[5]=130;
3532         region.color[5][0]=184;
3533         region.color[5][1]=255;
3534         region.color[5][2]=0;
3535
3536         region.level[6]=140;
3537         region.color[6][0]=0;
3538         region.color[6][1]=255;
3539         region.color[6][2]=0;
3540
3541         region.level[7]=150;
3542         region.color[7][0]=0;
3543         region.color[7][1]=208;
3544         region.color[7][2]=0;
3545
3546         region.level[8]=160;
3547         region.color[8][0]=0;
3548         region.color[8][1]=196;
3549         region.color[8][2]=196;
3550
3551         region.level[9]=170;
3552         region.color[9][0]=0;
3553         region.color[9][1]=148;
3554         region.color[9][2]=255;
3555
3556         region.level[10]=180;
3557         region.color[10][0]=80;
3558         region.color[10][1]=80;
3559         region.color[10][2]=255;
3560
3561         region.level[11]=190;
3562         region.color[11][0]=0;
3563         region.color[11][1]=38;
3564         region.color[11][2]=255;
3565
3566         region.level[12]=200;
3567         region.color[12][0]=142;
3568         region.color[12][1]=63;
3569         region.color[12][2]=255;
3570
3571         region.level[13]=210;
3572         region.color[13][0]=196;
3573         region.color[13][1]=54;
3574         region.color[13][2]=255;
3575
3576         region.level[14]=220;
3577         region.color[14][0]=255;
3578         region.color[14][1]=0;
3579         region.color[14][2]=255;
3580
3581         region.level[15]=230;
3582         region.color[15][0]=255;
3583         region.color[15][1]=194;
3584         region.color[15][2]=204;
3585
3586         region.levels=16;
3587
3588         fd=fopen("splat.lcf","r");
3589
3590         if (fd==NULL)
3591                 fd=fopen(filename,"r");
3592
3593         if (fd==NULL)
3594         {
3595                 fd=fopen(filename,"w");
3596
3597                 fprintf(fd,"; SPLAT! Auto-generated Path-Loss Color Definition (\"%s\") File\n",filename);
3598                 fprintf(fd,";\n; Format for the parameters held in this file is as follows:\n;\n");
3599                 fprintf(fd,";    dB: red, green, blue\n;\n");
3600                 fprintf(fd,"; ...where \"dB\" is the path loss (in dB) and\n");
3601                 fprintf(fd,"; \"red\", \"green\", and \"blue\" are the corresponding RGB color\n");
3602                 fprintf(fd,"; definitions ranging from 0 to 255 for the region specified.\n");
3603                 fprintf(fd,";\n; The following parameters may be edited and/or expanded\n");
3604                 fprintf(fd,"; for future runs of SPLAT!  A total of 32 contour regions\n");
3605                 fprintf(fd,"; may be defined in this file.\n;\n;\n");
3606
3607                 for (x=0; x<region.levels; x++)
3608                         fprintf(fd,"%3d: %3d, %3d, %3d\n",region.level[x], region.color[x][0], region.color[x][1], region.color[x][2]);
3609
3610                 fclose(fd);
3611         }
3612
3613         else
3614         {
3615                 x=0;
3616                 s=fgets(string,80,fd);
3617
3618                 while (x<32 && feof(fd)==0)
3619                 {
3620                         pointer=strchr(string,';');
3621
3622                         if (pointer!=NULL)
3623                                 *pointer=0;
3624
3625                         ok=sscanf(string,"%d: %d, %d, %d", &val[0], &val[1], &val[2], &val[3]);
3626
3627                         if (ok==4)
3628                         {
3629                                 for (y=0; y<4; y++)
3630                                 {
3631                                         if (val[y]>255)
3632                                                 val[y]=255;
3633
3634                                         if (val[y]<0)
3635                                                 val[y]=0;
3636                                 }
3637         
3638                                 region.level[x]=val[0];
3639                                 region.color[x][0]=val[1];
3640                                 region.color[x][1]=val[2];
3641                                 region.color[x][2]=val[3];
3642                                 x++;
3643                         }
3644
3645                         s=fgets(string,80,fd);
3646                 }
3647
3648                 fclose(fd);
3649                 region.levels=x;
3650         }
3651 }
3652
3653 void LoadDBMColors(struct site xmtr)
3654 {
3655         int x, y, ok, val[4];
3656         char filename[255], string[80], *pointer=NULL, *s=NULL;
3657         FILE *fd=NULL;
3658
3659         for (x=0; xmtr.filename[x]!='.' && xmtr.filename[x]!=0 && x<250; x++)
3660                 filename[x]=xmtr.filename[x];
3661
3662         filename[x]='.';
3663         filename[x+1]='d';
3664         filename[x+2]='c';
3665         filename[x+3]='f';
3666         filename[x+4]=0;
3667
3668         /* Default values */
3669
3670         region.level[0]=0;
3671         region.color[0][0]=255;
3672         region.color[0][1]=0;
3673         region.color[0][2]=0;
3674
3675         region.level[1]=-10;
3676         region.color[1][0]=255;
3677         region.color[1][1]=128;
3678         region.color[1][2]=0;
3679
3680         region.level[2]=-20;
3681         region.color[2][0]=255;
3682         region.color[2][1]=165;
3683         region.color[2][2]=0;
3684
3685         region.level[3]=-30;
3686         region.color[3][0]=255;
3687         region.color[3][1]=206;
3688         region.color[3][2]=0;
3689
3690         region.level[4]=-40;
3691         region.color[4][0]=255;
3692         region.color[4][1]=255;
3693         region.color[4][2]=0;
3694
3695         region.level[5]=-50;
3696         region.color[5][0]=184;
3697         region.color[5][1]=255;
3698         region.color[5][2]=0;
3699
3700         region.level[6]=-60;
3701         region.color[6][0]=0;
3702         region.color[6][1]=255;
3703         region.color[6][2]=0;
3704
3705         region.level[7]=-70;
3706         region.color[7][0]=0;
3707         region.color[7][1]=208;
3708         region.color[7][2]=0;
3709
3710         region.level[8]=-80;
3711         region.color[8][0]=0;
3712         region.color[8][1]=196;
3713         region.color[8][2]=196;
3714
3715         region.level[9]=-90;
3716         region.color[9][0]=0;
3717         region.color[9][1]=148;
3718         region.color[9][2]=255;
3719
3720         region.level[10]=-100;
3721         region.color[10][0]=80;
3722         region.color[10][1]=80;
3723         region.color[10][2]=255;
3724
3725         region.level[11]=-110;
3726         region.color[11][0]=0;
3727         region.color[11][1]=38;
3728         region.color[11][2]=255;
3729
3730         region.level[12]=-120;
3731         region.color[12][0]=142;
3732         region.color[12][1]=63;
3733         region.color[12][2]=255;
3734
3735         region.level[13]=-130;
3736         region.color[13][0]=196;
3737         region.color[13][1]=54;
3738         region.color[13][2]=255;
3739
3740         region.level[14]=-140;
3741         region.color[14][0]=255;
3742         region.color[14][1]=0;
3743         region.color[14][2]=255;
3744
3745         region.level[15]=-150;
3746         region.color[15][0]=255;
3747         region.color[15][1]=194;
3748         region.color[15][2]=204;
3749
3750         region.levels=16;
3751
3752         fd=fopen("splat.dcf","r");
3753
3754         if (fd==NULL)
3755                 fd=fopen(filename,"r");
3756
3757         if (fd==NULL)
3758         {
3759                 fd=fopen(filename,"w");
3760
3761                 fprintf(fd,"; SPLAT! Auto-generated DBM Signal Level Color Definition (\"%s\") File\n",filename);
3762                 fprintf(fd,";\n; Format for the parameters held in this file is as follows:\n;\n");
3763                 fprintf(fd,";    dBm: red, green, blue\n;\n");
3764                 fprintf(fd,"; ...where \"dBm\" is the received signal power level between +40 dBm\n");
3765                 fprintf(fd,"; and -200 dBm, and \"red\", \"green\", and \"blue\" are the corresponding\n");
3766                 fprintf(fd,"; RGB color definitions ranging from 0 to 255 for the region specified.\n");
3767                 fprintf(fd,";\n; The following parameters may be edited and/or expanded\n");
3768                 fprintf(fd,"; for future runs of SPLAT!  A total of 32 contour regions\n");
3769                 fprintf(fd,"; may be defined in this file.\n;\n;\n");
3770
3771                 for (x=0; x<region.levels; x++)
3772                         fprintf(fd,"%+4d: %3d, %3d, %3d\n",region.level[x], region.color[x][0], region.color[x][1], region.color[x][2]);
3773
3774                 fclose(fd);
3775         }
3776
3777         else
3778         {
3779                 x=0;
3780                 s=fgets(string,80,fd);
3781
3782                 while (x<32 && feof(fd)==0)
3783                 {
3784                         pointer=strchr(string,';');
3785
3786                         if (pointer!=NULL)
3787                                 *pointer=0;
3788
3789                         ok=sscanf(string,"%d: %d, %d, %d", &val[0], &val[1], &val[2], &val[3]);
3790
3791                         if (ok==4)
3792                         {
3793                                 if (val[0]<-200)
3794                                         val[0]=-200;
3795
3796                                 if (val[0]>+40)
3797                                         val[0]=+40;
3798
3799                                 region.level[x]=val[0];
3800
3801                                 for (y=1; y<4; y++)
3802                                 {
3803                                         if (val[y]>255)
3804                                                 val[y]=255;
3805
3806                                         if (val[y]<0)
3807                                                 val[y]=0;
3808                                 }
3809         
3810                                 region.color[x][0]=val[1];
3811                                 region.color[x][1]=val[2];
3812                                 region.color[x][2]=val[3];
3813                                 x++;
3814                         }
3815
3816                         s=fgets(string,80,fd);
3817                 }
3818
3819                 fclose(fd);
3820                 region.levels=x;
3821         }
3822 }
3823
3824 void WritePPM(char *filename, unsigned char geo, unsigned char kml, unsigned char ngs, struct site *xmtr, unsigned char txsites)
3825 {
3826         /* This function generates a topographic map in Portable Pix Map
3827            (PPM) format based on logarithmically scaled topology data,
3828            as well as the content of flags held in the mask[][] array.
3829            The image created is rotated counter-clockwise 90 degrees
3830            from its representation in dem[][] so that north points
3831            up and east points right in the image generated. */
3832
3833         char mapfile[255], geofile[255], kmlfile[255];
3834         unsigned char found, mask;
3835         unsigned width, height, terrain;
3836         int indx, x, y, x0=0, y0=0;
3837         double lat, lon, conversion, one_over_gamma,
3838         north, south, east, west, minwest;
3839         FILE *fd;
3840
3841         one_over_gamma=1.0/GAMMA;
3842         conversion=255.0/pow((double)(max_elevation-min_elevation),one_over_gamma);
3843
3844         width=(unsigned)(ippd*ReduceAngle(max_west-min_west));
3845         height=(unsigned)(ippd*ReduceAngle(max_north-min_north));
3846
3847         if (filename[0]==0)
3848         {
3849                 strncpy(filename, xmtr[0].filename,254);
3850                 filename[strlen(filename)-4]=0;  /* Remove .qth */
3851         }
3852
3853         y=strlen(filename);
3854
3855         if (y>4)
3856         {
3857                 if (filename[y-1]=='m' && filename[y-2]=='p' && filename[y-3]=='p' && filename[y-4]=='.')
3858                         y-=4;
3859         }
3860
3861         for (x=0; x<y; x++)
3862         {
3863                 mapfile[x]=filename[x];
3864                 geofile[x]=filename[x];
3865                 kmlfile[x]=filename[x];
3866         }
3867
3868         mapfile[x]='.';
3869         geofile[x]='.';
3870         kmlfile[x]='.';
3871         mapfile[x+1]='p';
3872         geofile[x+1]='g';
3873         kmlfile[x+1]='k';
3874         mapfile[x+2]='p';
3875         geofile[x+2]='e';
3876         kmlfile[x+2]='m';
3877         mapfile[x+3]='m';
3878         geofile[x+3]='o';
3879         kmlfile[x+3]='l';
3880         mapfile[x+4]=0;
3881         geofile[x+4]=0;
3882         kmlfile[x+4]=0;
3883
3884         minwest=((double)min_west)+dpp;
3885
3886         if (minwest>360.0)
3887                 minwest-=360.0;
3888
3889         north=(double)max_north-dpp;
3890         south=(double)min_north;
3891         east=(minwest<180.0?-minwest:360.0-min_west);
3892         west=(double)(max_west<180?-max_west:360-max_west);
3893
3894         if (kml==0 && geo)
3895         {
3896                 fd=fopen(geofile,"wb");
3897
3898                 fprintf(fd,"FILENAME\t%s\n",mapfile);
3899                 fprintf(fd,"#\t\tX\tY\tLong\t\tLat\n");
3900                 fprintf(fd,"TIEPOINT\t0\t0\t%.3f\t\t%.3f\n",west,north);
3901                 fprintf(fd,"TIEPOINT\t%u\t%u\t%.3f\t\t%.3f\n",width-1,height-1,east,south);
3902                 fprintf(fd,"IMAGESIZE\t%u\t%u\n",width,height);
3903                 fprintf(fd,"#\n# Auto Generated by %s v%s\n#\n",splat_name,splat_version);
3904
3905                 fclose(fd);
3906         }
3907
3908         if (kml && geo==0)
3909         {
3910                 fd=fopen(kmlfile,"wb");
3911
3912                 fprintf(fd,"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
3913                 fprintf(fd,"<kml xmlns=\"http://earth.google.com/kml/2.1\">\n");
3914                 fprintf(fd,"  <Folder>\n");
3915                 fprintf(fd,"   <name>%s</name>\n",splat_name);
3916                 fprintf(fd,"     <description>Line-of-Sight Overlay</description>\n");
3917                 fprintf(fd,"       <GroundOverlay>\n");
3918                 fprintf(fd,"         <name>%s Line-of-Sight Overlay</name>\n",splat_name);
3919                 fprintf(fd,"           <description>SPLAT! Coverage</description>\n");
3920                 fprintf(fd,"            <Icon>\n");
3921                 fprintf(fd,"              <href>%s</href>\n",mapfile);
3922                 fprintf(fd,"            </Icon>\n");
3923                 fprintf(fd,"            <opacity>128</opacity>\n");
3924                 fprintf(fd,"            <LatLonBox>\n");
3925                 fprintf(fd,"               <north>%.5f</north>\n",north);
3926                 fprintf(fd,"               <south>%.5f</south>\n",south);
3927                 fprintf(fd,"               <east>%.5f</east>\n",east);
3928                 fprintf(fd,"               <west>%.5f</west>\n",west);
3929                 fprintf(fd,"               <rotation>0.0</rotation>\n");
3930                 fprintf(fd,"            </LatLonBox>\n");
3931                 fprintf(fd,"       </GroundOverlay>\n");
3932
3933                 for (x=0; x<txsites; x++)
3934                 {
3935                         fprintf(fd,"     <Placemark>\n");
3936                         fprintf(fd,"       <name>%s</name>\n",xmtr[x].name);
3937                         fprintf(fd,"       <visibility>1</visibility>\n");
3938                         fprintf(fd,"       <Style>\n");
3939                         fprintf(fd,"       <IconStyle>\n");
3940                         fprintf(fd,"        <Icon>\n");
3941                         fprintf(fd,"          <href>root://icons/palette-5.png</href>\n");
3942                         fprintf(fd,"          <x>224</x>\n");
3943                         fprintf(fd,"          <y>224</y>\n");
3944                         fprintf(fd,"          <w>32</w>\n");
3945                         fprintf(fd,"          <h>32</h>\n");
3946                         fprintf(fd,"        </Icon>\n");
3947                         fprintf(fd,"       </IconStyle>\n");
3948                         fprintf(fd,"       </Style>\n");
3949                         fprintf(fd,"      <Point>\n");
3950                         fprintf(fd,"        <extrude>1</extrude>\n");
3951                         fprintf(fd,"        <altitudeMode>relativeToGround</altitudeMode>\n");
3952                         fprintf(fd,"        <coordinates>%f,%f,%f</coordinates>\n",(xmtr[x].lon<180.0?-xmtr[x].lon:360.0-xmtr[x].lon), xmtr[x].lat, xmtr[x].alt);
3953                         fprintf(fd,"      </Point>\n");
3954                         fprintf(fd,"     </Placemark>\n");
3955                 }
3956
3957
3958
3959                 fprintf(fd,"  </Folder>\n");
3960                 fprintf(fd,"</kml>\n");
3961
3962                 fclose(fd);
3963         }
3964
3965         fd=fopen(mapfile,"wb");
3966
3967         fprintf(fd,"P6\n%u %u\n255\n",width,height);
3968         fprintf(stdout,"\nWriting \"%s\" (%ux%u pixmap image)... ",mapfile,width,height);
3969         fflush(stdout);
3970
3971         for (y=0, lat=north; y<(int)height; y++, lat=north-(dpp*(double)y))
3972         {
3973                 for (x=0, lon=max_west; x<(int)width; x++, lon=(double)max_west-(dpp*(double)x))
3974                 {
3975                         if (lon<0.0)
3976                                 lon+=360.0;
3977
3978                         for (indx=0, found=0; indx<MAXPAGES && found==0;)
3979                         {
3980                                 x0=(int)rint(ppd*(lat-(double)dem[indx].min_north));
3981                                 y0=mpi-(int)rint(ppd*(LonDiff((double)dem[indx].max_west,lon)));
3982
3983                                 if (x0>=0 && x0<=mpi && y0>=0 && y0<=mpi)
3984                                         found=1;
3985                                 else
3986                                         indx++;
3987                         }
3988
3989                         if (found)
3990                         {
3991                                 mask=dem[indx].mask[x0][y0];
3992
3993                                 if (mask&2)
3994                                         /* Text Labels: Red */
3995                                         fprintf(fd,"%c%c%c",255,0,0);
3996
3997                                 else if (mask&4)
3998                                         /* County Boundaries: Light Cyan */
3999                                         fprintf(fd,"%c%c%c",128,128,255);
4000
4001                                 else switch (mask&57)
4002                                 {
4003                                         case 1:
4004                                         /* TX1: Green */
4005                                         fprintf(fd,"%c%c%c",0,255,0);
4006                                         break;
4007
4008                                         case 8:
4009                                         /* TX2: Cyan */
4010                                         fprintf(fd,"%c%c%c",0,255,255);
4011                                         break;
4012
4013                                         case 9:
4014                                         /* TX1 + TX2: Yellow */
4015                                         fprintf(fd,"%c%c%c",255,255,0);
4016                                         break;
4017
4018                                         case 16:
4019                                         /* TX3: Medium Violet */
4020                                         fprintf(fd,"%c%c%c",147,112,219);
4021                                         break;
4022
4023                                         case 17:
4024                                         /* TX1 + TX3: Pink */
4025                                         fprintf(fd,"%c%c%c",255,192,203);
4026                                         break;
4027
4028                                         case 24:
4029                                         /* TX2 + TX3: Orange */
4030                                         fprintf(fd,"%c%c%c",255,165,0);
4031                                         break;
4032
4033                                         case 25:
4034                                         /* TX1 + TX2 + TX3: Dark Green */
4035                                         fprintf(fd,"%c%c%c",0,100,0);
4036                                         break;
4037
4038                                         case 32:
4039                                         /* TX4: Sienna 1 */
4040                                         fprintf(fd,"%c%c%c",255,130,71);
4041                                         break;
4042
4043                                         case 33:
4044                                         /* TX1 + TX4: Green Yellow */
4045                                         fprintf(fd,"%c%c%c",173,255,47);
4046                                         break;
4047
4048                                         case 40:
4049                                         /* TX2 + TX4: Dark Sea Green 1 */
4050                                         fprintf(fd,"%c%c%c",193,255,193);
4051                                         break;
4052
4053                                         case 41:
4054                                         /* TX1 + TX2 + TX4: Blanched Almond */
4055                                         fprintf(fd,"%c%c%c",255,235,205);
4056                                         break;
4057
4058                                         case 48:
4059                                         /* TX3 + TX4: Dark Turquoise */
4060                                         fprintf(fd,"%c%c%c",0,206,209);
4061                                         break;
4062
4063                                         case 49:
4064                                         /* TX1 + TX3 + TX4: Medium Spring Green */
4065                                         fprintf(fd,"%c%c%c",0,250,154);
4066                                         break;
4067
4068                                         case 56:
4069                                         /* TX2 + TX3 + TX4: Tan */
4070                                         fprintf(fd,"%c%c%c",210,180,140);
4071                                         break;
4072
4073                                         case 57:
4074                                         /* TX1 + TX2 + TX3 + TX4: Gold2 */
4075                                         fprintf(fd,"%c%c%c",238,201,0);
4076                                         break;
4077
4078                                         default:
4079                                         if (ngs)  /* No terrain */
4080                                                 fprintf(fd,"%c%c%c",255,255,255);
4081                                         else
4082                                         {
4083                                                 /* Sea-level: Medium Blue */
4084                                                 if (dem[indx].data[x0][y0]==0)
4085                                                         fprintf(fd,"%c%c%c",0,0,170);
4086                                                 else
4087                                                 {
4088                                                         /* Elevation: Greyscale */
4089                                                         terrain=(unsigned)(0.5+pow((double)(dem[indx].data[x0][y0]-min_elevation),one_over_gamma)*conversion);
4090                                                         fprintf(fd,"%c%c%c",terrain,terrain,terrain);
4091                                                 }
4092                                         }
4093                                 }
4094                         }
4095
4096                         else
4097                         {
4098                                 /* We should never get here, but if */
4099                                 /* we do, display the region as black */
4100
4101                                 fprintf(fd,"%c%c%c",0,0,0);
4102                         }
4103                 }
4104         }
4105
4106         fclose(fd);
4107         fprintf(stdout,"Done!\n");
4108         fflush(stdout);
4109 }
4110
4111 void WritePPMLR(char *filename, unsigned char geo, unsigned char kml, unsigned char ngs, struct site *xmtr, unsigned char txsites)
4112 {
4113         /* This function generates a topographic map in Portable Pix Map
4114            (PPM) format based on the content of flags held in the mask[][] 
4115            array (only).  The image created is rotated counter-clockwise
4116            90 degrees from its representation in dem[][] so that north
4117            points up and east points right in the image generated. */
4118
4119         char mapfile[255], geofile[255], kmlfile[255];
4120         unsigned width, height, red, green, blue, terrain=0;
4121         unsigned char found, mask, cityorcounty;
4122         int indx, x, y, z, colorwidth, x0, y0, loss, level,
4123             hundreds, tens, units, match;
4124         double lat, lon, conversion, one_over_gamma,
4125         north, south, east, west, minwest;
4126         FILE *fd;
4127
4128         one_over_gamma=1.0/GAMMA;
4129         conversion=255.0/pow((double)(max_elevation-min_elevation),one_over_gamma);
4130
4131         width=(unsigned)(ippd*ReduceAngle(max_west-min_west));
4132         height=(unsigned)(ippd*ReduceAngle(max_north-min_north));
4133
4134         LoadLossColors(xmtr[0]);
4135
4136         if (filename[0]==0)
4137         {
4138                 strncpy(filename, xmtr[0].filename,254);
4139                 filename[strlen(filename)-4]=0;  /* Remove .qth */
4140         }
4141
4142         y=strlen(filename);
4143
4144         if (y>4)
4145         {
4146                 if (filename[y-1]=='m' && filename[y-2]=='p' && filename[y-3]=='p' && filename[y-4]=='.')
4147                         y-=4;
4148         }
4149
4150         for (x=0; x<y; x++)
4151         {
4152                 mapfile[x]=filename[x];
4153                 geofile[x]=filename[x];
4154                 kmlfile[x]=filename[x];
4155         }
4156
4157         mapfile[x]='.';
4158         geofile[x]='.';
4159         kmlfile[x]='.';
4160         mapfile[x+1]='p';
4161         geofile[x+1]='g';
4162         kmlfile[x+1]='k';
4163         mapfile[x+2]='p';
4164         geofile[x+2]='e';
4165         kmlfile[x+2]='m';
4166         mapfile[x+3]='m';
4167         geofile[x+3]='o';
4168         kmlfile[x+3]='l';
4169         mapfile[x+4]=0;
4170         geofile[x+4]=0;
4171         kmlfile[x+4]=0;
4172
4173         minwest=((double)min_west)+dpp;
4174
4175         if (minwest>360.0)
4176                 minwest-=360.0;
4177
4178         north=(double)max_north-dpp;
4179
4180         if (kml || geo)
4181                 south=(double)min_north;        /* No bottom legend */
4182         else
4183                 south=(double)min_north-(30.0/ppd); /* 30 pixels for bottom legend */
4184
4185         east=(minwest<180.0?-minwest:360.0-min_west);
4186         west=(double)(max_west<180?-max_west:360-max_west);
4187
4188         if (kml==0 && geo)
4189         {
4190                 fd=fopen(geofile,"wb");
4191
4192                 fprintf(fd,"FILENAME\t%s\n",mapfile);
4193                 fprintf(fd,"#\t\tX\tY\tLong\t\tLat\n");
4194                 fprintf(fd,"TIEPOINT\t0\t0\t%.3f\t\t%.3f\n",west,north);
4195
4196                 fprintf(fd,"TIEPOINT\t%u\t%u\t%.3f\t\t%.3f\n",width-1,height-1,east,south);
4197                 fprintf(fd,"IMAGESIZE\t%u\t%u\n",width,height);
4198
4199                 fprintf(fd,"#\n# Auto Generated by %s v%s\n#\n",splat_name,splat_version);
4200
4201                 fclose(fd);
4202         }
4203
4204         if (kml && geo==0)
4205         {
4206                 fd=fopen(kmlfile,"wb");
4207
4208                 fprintf(fd,"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
4209                 fprintf(fd,"<kml xmlns=\"http://earth.google.com/kml/2.1\">\n");
4210                 fprintf(fd,"<!-- Generated by %s Version %s -->\n",splat_name,splat_version);
4211                 fprintf(fd,"  <Folder>\n");
4212                 fprintf(fd,"   <name>%s</name>\n",splat_name);
4213                 fprintf(fd,"     <description>%s Transmitter Path Loss Overlay</description>\n",xmtr[0].name);
4214                 fprintf(fd,"       <GroundOverlay>\n");
4215                 fprintf(fd,"         <name>SPLAT! Path Loss Overlay</name>\n");
4216                 fprintf(fd,"           <description>SPLAT! Coverage</description>\n");
4217                 fprintf(fd,"            <Icon>\n");
4218                 fprintf(fd,"              <href>%s</href>\n",mapfile);
4219                 fprintf(fd,"            </Icon>\n");
4220                 fprintf(fd,"            <opacity>128</opacity>\n");
4221                 fprintf(fd,"            <LatLonBox>\n");
4222                 fprintf(fd,"               <north>%.5f</north>\n",north);
4223                 fprintf(fd,"               <south>%.5f</south>\n",south);
4224                 fprintf(fd,"               <east>%.5f</east>\n",east);
4225                 fprintf(fd,"               <west>%.5f</west>\n",west);
4226                 fprintf(fd,"               <rotation>0.0</rotation>\n");
4227                 fprintf(fd,"            </LatLonBox>\n");
4228                 fprintf(fd,"       </GroundOverlay>\n");
4229
4230                 for (x=0; x<txsites; x++)
4231                 {
4232                         fprintf(fd,"     <Placemark>\n");
4233                         fprintf(fd,"       <name>%s</name>\n",xmtr[x].name);
4234                         fprintf(fd,"       <visibility>1</visibility>\n");
4235                         fprintf(fd,"       <Style>\n");
4236                         fprintf(fd,"       <IconStyle>\n");
4237                         fprintf(fd,"        <Icon>\n");
4238                         fprintf(fd,"          <href>root://icons/palette-5.png</href>\n");
4239                         fprintf(fd,"          <x>224</x>\n");
4240                         fprintf(fd,"          <y>224</y>\n");
4241                         fprintf(fd,"          <w>32</w>\n");
4242                         fprintf(fd,"          <h>32</h>\n");
4243                         fprintf(fd,"        </Icon>\n");
4244                         fprintf(fd,"       </IconStyle>\n");
4245                         fprintf(fd,"       </Style>\n");
4246                         fprintf(fd,"      <Point>\n");
4247                         fprintf(fd,"        <extrude>1</extrude>\n");
4248                         fprintf(fd,"        <altitudeMode>relativeToGround</altitudeMode>\n");
4249                         fprintf(fd,"        <coordinates>%f,%f,%f</coordinates>\n",(xmtr[x].lon<180.0?-xmtr[x].lon:360.0-xmtr[x].lon), xmtr[x].lat, xmtr[x].alt);
4250                         fprintf(fd,"      </Point>\n");
4251                         fprintf(fd,"     </Placemark>\n");
4252                 }
4253
4254                 fprintf(fd,"  </Folder>\n");
4255                 fprintf(fd,"</kml>\n");
4256
4257                 fclose(fd);
4258         }
4259
4260         fd=fopen(mapfile,"wb");
4261
4262         fprintf(fd,"P6\n%u %u\n255\n",width,(kml?height:height+30));
4263         fprintf(stdout,"\nWriting \"%s\" (%ux%u pixmap image)... ",mapfile,width,(kml?height:height+30));
4264         fflush(stdout);
4265
4266         for (y=0, lat=north; y<(int)height; y++, lat=north-(dpp*(double)y))
4267         {
4268                 for (x=0, lon=max_west; x<(int)width; x++, lon=max_west-(dpp*(double)x))
4269                 {
4270                         if (lon<0.0)
4271                                 lon+=360.0;
4272
4273                         for (indx=0, found=0; indx<MAXPAGES && found==0;)
4274                         {
4275                                 x0=(int)rint(ppd*(lat-(double)dem[indx].min_north));
4276                                 y0=mpi-(int)rint(ppd*(LonDiff((double)dem[indx].max_west,lon)));
4277
4278                                 if (x0>=0 && x0<=mpi && y0>=0 && y0<=mpi)
4279                                         found=1;
4280                                 else
4281                                         indx++;
4282                         }
4283
4284                         if (found)
4285                         {
4286                                 mask=dem[indx].mask[x0][y0];
4287                                 loss=(dem[indx].signal[x0][y0]);
4288                                 cityorcounty=0;
4289
4290                                 match=255;
4291
4292                                 red=0;
4293                                 green=0;
4294                                 blue=0;
4295
4296                                 if (loss<=region.level[0])
4297                                         match=0;
4298                                 else
4299                                 {
4300                                         for (z=1; (z<region.levels && match==255); z++)
4301                                         {
4302                                                 if (loss>=region.level[z-1] && loss<region.level[z])
4303                                                         match=z;
4304                                         }
4305                                 }
4306
4307                                 if (match<region.levels)
4308                                 {
4309                                         red=region.color[match][0];
4310                                         green=region.color[match][1];
4311                                         blue=region.color[match][2];
4312                                 }
4313
4314                                 if (mask&2)
4315                                 {
4316                                         /* Text Labels: Red or otherwise */
4317
4318                                         if (red>=180 && green<=75 && blue<=75 && loss!=0)
4319                                                 fprintf(fd,"%c%c%c",255^red,255^green,255^blue);
4320                                         else
4321                                                 fprintf(fd,"%c%c%c",255,0,0);
4322
4323                                         cityorcounty=1;
4324                                 }
4325
4326                                 else if (mask&4)
4327                                 {
4328                                         /* County Boundaries: Black */
4329
4330                                         fprintf(fd,"%c%c%c",0,0,0);
4331
4332                                         cityorcounty=1;
4333                                 }
4334
4335                                 if (cityorcounty==0)
4336                                 {
4337                                         if (loss==0 || (contour_threshold!=0 && loss>abs(contour_threshold)))
4338                                         {
4339                                                 if (ngs)  /* No terrain */
4340                                                         fprintf(fd,"%c%c%c",255,255,255);
4341                                                 else
4342                                                 {
4343                                                         /* Display land or sea elevation */
4344
4345                                                         if (dem[indx].data[x0][y0]==0)
4346                                                                 fprintf(fd,"%c%c%c",0,0,170);
4347                                                         else
4348                                                         {
4349                                                                 terrain=(unsigned)(0.5+pow((double)(dem[indx].data[x0][y0]-min_elevation),one_over_gamma)*conversion);
4350                                                                 fprintf(fd,"%c%c%c",terrain,terrain,terrain);
4351                                                         }
4352                                                 }
4353                                         }
4354
4355                                         else
4356                                         {
4357                                                 /* Plot path loss in color */
4358
4359                                                 if (red!=0 || green!=0 || blue!=0)
4360                                                         fprintf(fd,"%c%c%c",red,green,blue);
4361
4362                                                 else  /* terrain / sea-level */
4363                                                 {
4364                                                         if (dem[indx].data[x0][y0]==0)
4365                                                                 fprintf(fd,"%c%c%c",0,0,170);
4366                                                         else
4367                                                         {
4368                                                                 /* Elevation: Greyscale */
4369                                                                 terrain=(unsigned)(0.5+pow((double)(dem[indx].data[x0][y0]-min_elevation),one_over_gamma)*conversion);
4370                                                                 fprintf(fd,"%c%c%c",terrain,terrain,terrain);
4371                                                         }
4372                                                 }
4373                                         }
4374                                 }
4375                         }
4376
4377                         else
4378                         {
4379                                 /* We should never get here, but if */
4380                                 /* we do, display the region as black */
4381
4382                                 fprintf(fd,"%c%c%c",0,0,0);
4383                         }
4384                 }
4385         }
4386
4387         if (kml==0 && geo==0)
4388         {
4389                 /* Display legend along bottom of image
4390                  * if not generating .kml or .geo output.
4391                  */
4392
4393                 colorwidth=(int)rint((float)width/(float)region.levels);
4394
4395                 for (y0=0; y0<30; y0++)
4396                 {
4397                         for (x0=0; x0<(int)width; x0++)
4398                         {
4399                                 indx=x0/colorwidth;
4400                                 x=x0%colorwidth;
4401                                 level=region.level[indx];
4402
4403                                 hundreds=level/100;
4404
4405                                 if (hundreds>0)
4406                                         level-=(hundreds*100);
4407
4408                                 tens=level/10;
4409
4410                                 if (tens>0)
4411                                         level-=(tens*10);
4412
4413                                 units=level;
4414
4415                                 if (y0>=8 && y0<=23)
4416                                 {  
4417                                         if (hundreds>0)
4418                                         {
4419                                                 if (x>=11 && x<=18)     
4420                                                         if (fontdata[16*(hundreds+'0')+(y0-8)]&(128>>(x-11)))
4421                                                                 indx=255; 
4422                                         }
4423
4424                                         if (tens>0 || hundreds>0)
4425                                         {
4426                                                 if (x>=19 && x<=26)     
4427                                                         if (fontdata[16*(tens+'0')+(y0-8)]&(128>>(x-19)))
4428                                                                 indx=255;
4429                                         }
4430  
4431                                         if (x>=27 && x<=34)
4432                                                 if (fontdata[16*(units+'0')+(y0-8)]&(128>>(x-27)))
4433                                                         indx=255;
4434
4435                                         if (x>=42 && x<=49)
4436                                                 if (fontdata[16*('d')+(y0-8)]&(128>>(x-42)))
4437                                                         indx=255;
4438
4439                                         if (x>=50 && x<=57)
4440                                                 if (fontdata[16*('B')+(y0-8)]&(128>>(x-50)))
4441                                                         indx=255;
4442                                 }
4443
4444                                 if (indx>region.levels)
4445                                         fprintf(fd,"%c%c%c",0,0,0);
4446                                 else
4447                                 {
4448                                         red=region.color[indx][0];
4449                                         green=region.color[indx][1];
4450                                         blue=region.color[indx][2];
4451
4452                                         fprintf(fd,"%c%c%c",red,green,blue);
4453                                 }
4454                         } 
4455                 }
4456         }
4457
4458         fclose(fd);
4459         fprintf(stdout,"Done!\n");
4460         fflush(stdout);
4461 }
4462
4463 void WritePPMSS(char *filename, unsigned char geo, unsigned char kml, unsigned char ngs, struct site *xmtr, unsigned char txsites)
4464 {
4465         /* This function generates a topographic map in Portable Pix Map
4466            (PPM) format based on the signal strength values held in the
4467            signal[][] array.  The image created is rotated counter-clockwise
4468            90 degrees from its representation in dem[][] so that north
4469            points up and east points right in the image generated. */
4470
4471         char mapfile[255], geofile[255], kmlfile[255];
4472         unsigned width, height, terrain, red, green, blue;
4473         unsigned char found, mask, cityorcounty;
4474         int indx, x, y, z=1, x0, y0, signal, level, hundreds,
4475             tens, units, match, colorwidth;
4476         double conversion, one_over_gamma, lat, lon,
4477         north, south, east, west, minwest;
4478         FILE *fd;
4479
4480         one_over_gamma=1.0/GAMMA;
4481         conversion=255.0/pow((double)(max_elevation-min_elevation),one_over_gamma);
4482
4483         width=(unsigned)(ippd*ReduceAngle(max_west-min_west));
4484         height=(unsigned)(ippd*ReduceAngle(max_north-min_north));
4485
4486         LoadSignalColors(xmtr[0]);
4487
4488         if (filename[0]==0)
4489         {
4490                 strncpy(filename, xmtr[0].filename,254);
4491                 filename[strlen(filename)-4]=0;  /* Remove .qth */
4492         }
4493
4494         y=strlen(filename);
4495
4496         if (y>4)
4497         {
4498                 if (filename[y-1]=='m' && filename[y-2]=='p' && filename[y-3]=='p' && filename[y-4]=='.')
4499                         y-=4;
4500         }
4501
4502         for (x=0; x<y; x++)
4503         {
4504                 mapfile[x]=filename[x];
4505                 geofile[x]=filename[x];
4506                 kmlfile[x]=filename[x];
4507         }
4508
4509         mapfile[x]='.';
4510         geofile[x]='.';
4511         kmlfile[x]='.';
4512         mapfile[x+1]='p';
4513         geofile[x+1]='g';
4514         kmlfile[x+1]='k';
4515         mapfile[x+2]='p';
4516         geofile[x+2]='e';
4517         kmlfile[x+2]='m';
4518         mapfile[x+3]='m';
4519         geofile[x+3]='o';
4520         kmlfile[x+3]='l';
4521         mapfile[x+4]=0;
4522         geofile[x+4]=0;
4523         kmlfile[x+4]=0;
4524
4525         minwest=((double)min_west)+dpp;
4526
4527         if (minwest>360.0)
4528                 minwest-=360.0;
4529
4530         north=(double)max_north-dpp;
4531
4532         if (kml || geo)
4533                 south=(double)min_north;        /* No bottom legend */
4534         else
4535                 south=(double)min_north-(30.0/ppd);     /* 30 pixels for bottom legend */
4536
4537         east=(minwest<180.0?-minwest:360.0-min_west);
4538         west=(double)(max_west<180?-max_west:360-max_west);
4539
4540         if (geo && kml==0)
4541         {
4542                 fd=fopen(geofile,"wb");
4543
4544                 fprintf(fd,"FILENAME\t%s\n",mapfile);
4545                 fprintf(fd,"#\t\tX\tY\tLong\t\tLat\n");
4546                 fprintf(fd,"TIEPOINT\t0\t0\t%.3f\t\t%.3f\n",west,north);
4547
4548                 fprintf(fd,"TIEPOINT\t%u\t%u\t%.3f\t\t%.3f\n",width-1,height-1,east,south);
4549                 fprintf(fd,"IMAGESIZE\t%u\t%u\n",width,height);
4550
4551                 fprintf(fd,"#\n# Auto Generated by %s v%s\n#\n",splat_name,splat_version);
4552
4553                 fclose(fd);
4554         }
4555
4556         if (kml && geo==0)
4557         {
4558                 fd=fopen(kmlfile,"wb");
4559
4560                 fprintf(fd,"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
4561                 fprintf(fd,"<kml xmlns=\"http://earth.google.com/kml/2.1\">\n");
4562                 fprintf(fd,"<!-- Generated by %s Version %s -->\n",splat_name,splat_version);
4563                 fprintf(fd,"  <Folder>\n");
4564                 fprintf(fd,"   <name>%s</name>\n",splat_name);
4565                 fprintf(fd,"     <description>%s Transmitter Coverage Overlay</description>\n",xmtr[0].name);
4566                 fprintf(fd,"       <GroundOverlay>\n");
4567                 fprintf(fd,"         <name>SPLAT! Signal Strength Overlay</name>\n");
4568                 fprintf(fd,"           <description>SPLAT! Coverage</description>\n");
4569                 fprintf(fd,"            <Icon>\n");
4570                 fprintf(fd,"              <href>%s</href>\n",mapfile);
4571                 fprintf(fd,"            </Icon>\n");
4572                 fprintf(fd,"            <opacity>128</opacity>\n");
4573                 fprintf(fd,"            <LatLonBox>\n");
4574                 fprintf(fd,"               <north>%.5f</north>\n",north);
4575                 fprintf(fd,"               <south>%.5f</south>\n",south);
4576                 fprintf(fd,"               <east>%.5f</east>\n",east);
4577                 fprintf(fd,"               <west>%.5f</west>\n",west);
4578                 fprintf(fd,"               <rotation>0.0</rotation>\n");
4579                 fprintf(fd,"            </LatLonBox>\n");
4580                 fprintf(fd,"       </GroundOverlay>\n");
4581
4582                 for (x=0; x<txsites; x++)
4583                 {
4584                         fprintf(fd,"     <Placemark>\n");
4585                         fprintf(fd,"       <name>%s</name>\n",xmtr[x].name);
4586                         fprintf(fd,"       <visibility>1</visibility>\n");
4587                         fprintf(fd,"       <Style>\n");
4588                         fprintf(fd,"       <IconStyle>\n");
4589                         fprintf(fd,"        <Icon>\n");
4590                         fprintf(fd,"          <href>root://icons/palette-5.png</href>\n");
4591                         fprintf(fd,"          <x>224</x>\n");
4592                         fprintf(fd,"          <y>224</y>\n");
4593                         fprintf(fd,"          <w>32</w>\n");
4594                         fprintf(fd,"          <h>32</h>\n");
4595                         fprintf(fd,"        </Icon>\n");
4596                         fprintf(fd,"       </IconStyle>\n");
4597                         fprintf(fd,"       </Style>\n");
4598                         fprintf(fd,"      <Point>\n");
4599                         fprintf(fd,"        <extrude>1</extrude>\n");
4600                         fprintf(fd,"        <altitudeMode>relativeToGround</altitudeMode>\n");
4601                         fprintf(fd,"        <coordinates>%f,%f,%f</coordinates>\n",(xmtr[x].lon<180.0?-xmtr[x].lon:360.0-xmtr[x].lon), xmtr[x].lat, xmtr[x].alt);
4602                         fprintf(fd,"      </Point>\n");
4603                         fprintf(fd,"     </Placemark>\n");
4604                 }
4605
4606                 fprintf(fd,"  </Folder>\n");
4607                 fprintf(fd,"</kml>\n");
4608
4609                 fclose(fd);
4610         }
4611
4612         fd=fopen(mapfile,"wb");
4613
4614         fprintf(fd,"P6\n%u %u\n255\n",width,(kml?height:height+30));
4615         fprintf(stdout,"\nWriting \"%s\" (%ux%u pixmap image)... ",mapfile,width,(kml?height:height+30));
4616         fflush(stdout);
4617
4618         for (y=0, lat=north; y<(int)height; y++, lat=north-(dpp*(double)y))
4619         {
4620                 for (x=0, lon=max_west; x<(int)width; x++, lon=max_west-(dpp*(double)x))
4621                 {
4622                         if (lon<0.0)
4623                                 lon+=360.0;
4624
4625                         for (indx=0, found=0; indx<MAXPAGES && found==0;)
4626                         {
4627                                 x0=(int)rint(ppd*(lat-(double)dem[indx].min_north));
4628                                 y0=mpi-(int)rint(ppd*(LonDiff((double)dem[indx].max_west,lon)));
4629
4630                                 if (x0>=0 && x0<=mpi && y0>=0 && y0<=mpi)
4631                                         found=1;
4632                                 else
4633                                         indx++;
4634                         }
4635
4636                         if (found)
4637                         {
4638                                 mask=dem[indx].mask[x0][y0];
4639                                 signal=(dem[indx].signal[x0][y0])-100;
4640                                 cityorcounty=0;
4641
4642                                 match=255;
4643
4644                                 red=0;
4645                                 green=0;
4646                                 blue=0;
4647
4648                                 if (signal>=region.level[0])
4649                                         match=0;
4650                                 else
4651                                 {
4652                                         for (z=1; (z<region.levels && match==255); z++)
4653                                         {
4654                                                 if (signal<region.level[z-1] && signal>=region.level[z])
4655                                                         match=z;
4656                                         }
4657                                 }
4658
4659                                 if (match<region.levels)
4660                                 {
4661                                         red=region.color[match][0];
4662                                         green=region.color[match][1];
4663                                         blue=region.color[match][2];
4664                                 }
4665
4666                                 if (mask&2) 
4667                                 {
4668                                         /* Text Labels: Red or otherwise */
4669
4670                                         if (red>=180 && green<=75 && blue<=75)
4671                                                 fprintf(fd,"%c%c%c",255^red,255^green,255^blue);
4672                                         else
4673                                                 fprintf(fd,"%c%c%c",255,0,0);
4674
4675                                         cityorcounty=1;
4676                                 }
4677
4678                                 else if (mask&4)
4679                                 {
4680                                         /* County Boundaries: Black */
4681
4682                                         fprintf(fd,"%c%c%c",0,0,0);
4683
4684                                         cityorcounty=1;
4685                                 }
4686
4687                                 if (cityorcounty==0)
4688                                 {
4689                                         if (contour_threshold!=0 && signal<contour_threshold)
4690                                         {
4691                                                 if (ngs)
4692                                                         fprintf(fd,"%c%c%c",255,255,255);
4693                                                 else
4694                                                 {
4695                                                         /* Display land or sea elevation */
4696
4697                                                         if (dem[indx].data[x0][y0]==0)
4698                                                                 fprintf(fd,"%c%c%c",0,0,170);
4699                                                         else
4700                                                         {
4701                                                                 terrain=(unsigned)(0.5+pow((double)(dem[indx].data[x0][y0]-min_elevation),one_over_gamma)*conversion);
4702                                                                 fprintf(fd,"%c%c%c",terrain,terrain,terrain);
4703                                                         }
4704                                                 }
4705                                         }
4706
4707                                         else
4708                                         {
4709                                                 /* Plot field strength regions in color */
4710
4711                                                 if (red!=0 || green!=0 || blue!=0)
4712                                                         fprintf(fd,"%c%c%c",red,green,blue);
4713
4714                                                 else  /* terrain / sea-level */
4715                                                 {
4716                                                         if (ngs)
4717                                                                 fprintf(fd,"%c%c%c",255,255,255);
4718                                                         else
4719                                                         {
4720                                                                 if (dem[indx].data[x0][y0]==0)
4721                                                                         fprintf(fd,"%c%c%c",0,0,170);
4722                                                                 else
4723                                                                 {
4724                                                                         /* Elevation: Greyscale */
4725                                                                         terrain=(unsigned)(0.5+pow((double)(dem[indx].data[x0][y0]-min_elevation),one_over_gamma)*conversion);
4726                                                                         fprintf(fd,"%c%c%c",terrain,terrain,terrain);
4727                                                                 }
4728                                                         }
4729                                                 }
4730                                         }
4731                                 }
4732                         }
4733
4734                         else
4735                         {
4736                                 /* We should never get here, but if */
4737                                 /* we do, display the region as black */
4738
4739                                 fprintf(fd,"%c%c%c",0,0,0);
4740                         }
4741                 }
4742         }
4743
4744         if (kml==0 && geo==0)
4745         {
4746                 /* Display legend along bottom of image
4747                  * if not generating .kml or .geo output.
4748                  */
4749
4750                 colorwidth=(int)rint((float)width/(float)region.levels);
4751
4752                 for (y0=0; y0<30; y0++)
4753                 {
4754                         for (x0=0; x0<(int)width; x0++)
4755                         {
4756                                 indx=x0/colorwidth;
4757                                 x=x0%colorwidth;
4758                                 level=region.level[indx];
4759
4760                                 hundreds=level/100;
4761
4762                                 if (hundreds>0)
4763                                         level-=(hundreds*100);
4764
4765                                 tens=level/10;
4766
4767                                 if (tens>0)
4768                                         level-=(tens*10);
4769
4770                                 units=level;
4771
4772                                 if (y0>=8 && y0<=23)
4773                                 {  
4774                                         if (hundreds>0)
4775                                         {
4776                                                 if (x>=5 && x<=12)     
4777                                                         if (fontdata[16*(hundreds+'0')+(y0-8)]&(128>>(x-5)))
4778                                                                 indx=255; 
4779                                         }
4780
4781                                         if (tens>0 || hundreds>0)
4782                                         {
4783                                                 if (x>=13 && x<=20)     
4784                                                         if (fontdata[16*(tens+'0')+(y0-8)]&(128>>(x-13)))
4785                                                                 indx=255;
4786                                         }
4787  
4788                                         if (x>=21 && x<=28)
4789                                                 if (fontdata[16*(units+'0')+(y0-8)]&(128>>(x-21)))
4790                                                         indx=255;
4791
4792                                         if (x>=36 && x<=43)
4793                                                 if (fontdata[16*('d')+(y0-8)]&(128>>(x-36)))
4794                                                         indx=255;
4795
4796                                         if (x>=44 && x<=51)
4797                                                 if (fontdata[16*('B')+(y0-8)]&(128>>(x-44)))
4798                                                         indx=255;
4799
4800                                         if (x>=52 && x<=59)
4801                                                 if (fontdata[16*(230)+(y0-8)]&(128>>(x-52)))
4802                                                         indx=255;
4803
4804                                         if (x>=60 && x<=67)
4805                                                 if (fontdata[16*('V')+(y0-8)]&(128>>(x-60)))
4806                                                         indx=255;
4807
4808                                         if (x>=68 && x<=75)
4809                                                 if (fontdata[16*('/')+(y0-8)]&(128>>(x-68)))
4810                                                         indx=255;
4811
4812                                         if (x>=76 && x<=83)
4813                                                 if (fontdata[16*('m')+(y0-8)]&(128>>(x-76)))
4814                                                         indx=255;
4815                                 }
4816
4817                                 if (indx>region.levels)
4818                                         fprintf(fd,"%c%c%c",0,0,0);
4819                                 else
4820                                 {
4821                                         red=region.color[indx][0];
4822                                         green=region.color[indx][1];
4823                                         blue=region.color[indx][2];
4824
4825                                         fprintf(fd,"%c%c%c",red,green,blue);
4826                                 }
4827                         } 
4828                 }
4829         }
4830
4831         fclose(fd);
4832         fprintf(stdout,"Done!\n");
4833         fflush(stdout);
4834 }
4835
4836 void WritePPMDBM(char *filename, unsigned char geo, unsigned char kml, unsigned char ngs, struct site *xmtr, unsigned char txsites)
4837 {
4838         /* This function generates a topographic map in Portable Pix Map
4839            (PPM) format based on the signal power level values held in the
4840            signal[][] array.  The image created is rotated counter-clockwise
4841            90 degrees from its representation in dem[][] so that north
4842            points up and east points right in the image generated. */
4843
4844         char mapfile[255], geofile[255], kmlfile[255];
4845         unsigned width, height, terrain, red, green, blue;
4846         unsigned char found, mask, cityorcounty;
4847         int indx, x, y, z=1, x0, y0, dBm, level, hundreds,
4848             tens, units, match, colorwidth;
4849         double conversion, one_over_gamma, lat, lon,
4850         north, south, east, west, minwest;
4851         FILE *fd;
4852
4853         one_over_gamma=1.0/GAMMA;
4854         conversion=255.0/pow((double)(max_elevation-min_elevation),one_over_gamma);
4855
4856         width=(unsigned)(ippd*ReduceAngle(max_west-min_west));
4857         height=(unsigned)(ippd*ReduceAngle(max_north-min_north));
4858
4859         LoadDBMColors(xmtr[0]);
4860
4861         if (filename[0]==0)
4862         {
4863                 strncpy(filename, xmtr[0].filename,254);
4864                 filename[strlen(filename)-4]=0;  /* Remove .qth */
4865         }
4866
4867         y=strlen(filename);
4868
4869         if (y>4)
4870         {
4871                 if (filename[y-1]=='m' && filename[y-2]=='p' && filename[y-3]=='p' && filename[y-4]=='.')
4872                         y-=4;
4873         }
4874
4875         for (x=0; x<y; x++)
4876         {
4877                 mapfile[x]=filename[x];
4878                 geofile[x]=filename[x];
4879                 kmlfile[x]=filename[x];
4880         }
4881
4882         mapfile[x]='.';
4883         geofile[x]='.';
4884         kmlfile[x]='.';
4885         mapfile[x+1]='p';
4886         geofile[x+1]='g';
4887         kmlfile[x+1]='k';
4888         mapfile[x+2]='p';
4889         geofile[x+2]='e';
4890         kmlfile[x+2]='m';
4891         mapfile[x+3]='m';
4892         geofile[x+3]='o';
4893         kmlfile[x+3]='l';
4894         mapfile[x+4]=0;
4895         geofile[x+4]=0;
4896         kmlfile[x+4]=0;
4897
4898         minwest=((double)min_west)+dpp;
4899
4900         if (minwest>360.0)
4901                 minwest-=360.0;
4902
4903         north=(double)max_north-dpp;
4904
4905         if (kml || geo)
4906                 south=(double)min_north;        /* No bottom legend */
4907         else
4908                 south=(double)min_north-(30.0/ppd);     /* 30 pixels for bottom legend */
4909
4910         east=(minwest<180.0?-minwest:360.0-min_west);
4911         west=(double)(max_west<180?-max_west:360-max_west);
4912
4913         if (geo && kml==0)
4914         {
4915                 fd=fopen(geofile,"wb");
4916
4917                 fprintf(fd,"FILENAME\t%s\n",mapfile);
4918                 fprintf(fd,"#\t\tX\tY\tLong\t\tLat\n");
4919                 fprintf(fd,"TIEPOINT\t0\t0\t%.3f\t\t%.3f\n",west,north);
4920
4921                 fprintf(fd,"TIEPOINT\t%u\t%u\t%.3f\t\t%.3f\n",width-1,height-1,east,south);
4922                 fprintf(fd,"IMAGESIZE\t%u\t%u\n",width,height);
4923
4924                 fprintf(fd,"#\n# Auto Generated by %s v%s\n#\n",splat_name,splat_version);
4925
4926                 fclose(fd);
4927         }
4928
4929         if (kml && geo==0)
4930         {
4931                 fd=fopen(kmlfile,"wb");
4932
4933                 fprintf(fd,"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
4934                 fprintf(fd,"<kml xmlns=\"http://earth.google.com/kml/2.1\">\n");
4935                 fprintf(fd,"<!-- Generated by %s Version %s -->\n",splat_name,splat_version);
4936                 fprintf(fd,"  <Folder>\n");
4937                 fprintf(fd,"   <name>%s</name>\n",splat_name);
4938                 fprintf(fd,"     <description>%s Transmitter Coverage Overlay</description>\n",xmtr[0].name);
4939                 fprintf(fd,"       <GroundOverlay>\n");
4940                 fprintf(fd,"         <name>SPLAT! Signal Power Level Overlay</name>\n");
4941                 fprintf(fd,"           <description>SPLAT! Coverage</description>\n");
4942                 fprintf(fd,"            <Icon>\n");
4943                 fprintf(fd,"              <href>%s</href>\n",mapfile);
4944                 fprintf(fd,"            </Icon>\n");
4945                 fprintf(fd,"            <opacity>128</opacity>\n");
4946                 fprintf(fd,"            <LatLonBox>\n");
4947                 fprintf(fd,"               <north>%.5f</north>\n",north);
4948                 fprintf(fd,"               <south>%.5f</south>\n",south);
4949                 fprintf(fd,"               <east>%.5f</east>\n",east);
4950                 fprintf(fd,"               <west>%.5f</west>\n",west);
4951                 fprintf(fd,"               <rotation>0.0</rotation>\n");
4952                 fprintf(fd,"            </LatLonBox>\n");
4953                 fprintf(fd,"       </GroundOverlay>\n");
4954
4955                 for (x=0; x<txsites; x++)
4956                 {
4957                         fprintf(fd,"     <Placemark>\n");
4958                         fprintf(fd,"       <name>%s</name>\n",xmtr[x].name);
4959                         fprintf(fd,"       <visibility>1</visibility>\n");
4960                         fprintf(fd,"       <Style>\n");
4961                         fprintf(fd,"       <IconStyle>\n");
4962                         fprintf(fd,"        <Icon>\n");
4963                         fprintf(fd,"          <href>root://icons/palette-5.png</href>\n");
4964                         fprintf(fd,"          <x>224</x>\n");
4965                         fprintf(fd,"          <y>224</y>\n");
4966                         fprintf(fd,"          <w>32</w>\n");
4967                         fprintf(fd,"          <h>32</h>\n");
4968                         fprintf(fd,"        </Icon>\n");
4969                         fprintf(fd,"       </IconStyle>\n");
4970                         fprintf(fd,"       </Style>\n");
4971                         fprintf(fd,"      <Point>\n");
4972                         fprintf(fd,"        <extrude>1</extrude>\n");
4973                         fprintf(fd,"        <altitudeMode>relativeToGround</altitudeMode>\n");
4974                         fprintf(fd,"        <coordinates>%f,%f,%f</coordinates>\n",(xmtr[x].lon<180.0?-xmtr[x].lon:360.0-xmtr[x].lon), xmtr[x].lat, xmtr[x].alt);
4975                         fprintf(fd,"      </Point>\n");
4976                         fprintf(fd,"     </Placemark>\n");
4977                 }
4978
4979                 fprintf(fd,"  </Folder>\n");
4980                 fprintf(fd,"</kml>\n");
4981
4982                 fclose(fd);
4983         }
4984
4985         fd=fopen(mapfile,"wb");
4986
4987         fprintf(fd,"P6\n%u %u\n255\n",width,(kml?height:height+30));
4988         fprintf(stdout,"\nWriting \"%s\" (%ux%u pixmap image)... ",mapfile,width,(kml?height:height+30));
4989         fflush(stdout);
4990
4991         for (y=0, lat=north; y<(int)height; y++, lat=north-(dpp*(double)y))
4992         {
4993                 for (x=0, lon=max_west; x<(int)width; x++, lon=max_west-(dpp*(double)x))
4994                 {
4995                         if (lon<0.0)
4996                                 lon+=360.0;
4997
4998                         for (indx=0, found=0; indx<MAXPAGES && found==0;)
4999                         {
5000                                 x0=(int)rint(ppd*(lat-(double)dem[indx].min_north));
5001                                 y0=mpi-(int)rint(ppd*(LonDiff((double)dem[indx].max_west,lon)));
5002
5003                                 if (x0>=0 && x0<=mpi && y0>=0 && y0<=mpi)
5004                                         found=1;
5005                                 else
5006                                         indx++;
5007                         }
5008
5009                         if (found)
5010                         {
5011                                 mask=dem[indx].mask[x0][y0];
5012                                 dBm=(dem[indx].signal[x0][y0])-200;
5013                                 cityorcounty=0;
5014
5015                                 match=255;
5016
5017                                 red=0;
5018                                 green=0;
5019                                 blue=0;
5020
5021                                 if (dBm>=region.level[0])
5022                                         match=0;
5023                                 else
5024                                 {
5025                                         for (z=1; (z<region.levels && match==255); z++)
5026                                         {
5027                                                 if (dBm<region.level[z-1] && dBm>=region.level[z])
5028                                                         match=z;
5029                                         }
5030                                 }
5031
5032                                 if (match<region.levels)
5033                                 {
5034                                         red=region.color[match][0];
5035                                         green=region.color[match][1];
5036                                         blue=region.color[match][2];
5037                                 }
5038
5039                                 if (mask&2) 
5040                                 {
5041                                         /* Text Labels: Red or otherwise */
5042
5043                                         if (red>=180 && green<=75 && blue<=75 && dBm!=0)
5044                                                 fprintf(fd,"%c%c%c",255^red,255^green,255^blue);
5045                                         else
5046                                                 fprintf(fd,"%c%c%c",255,0,0);
5047
5048                                         cityorcounty=1;
5049                                 }
5050
5051                                 else if (mask&4)
5052                                 {
5053                                         /* County Boundaries: Black */
5054
5055                                         fprintf(fd,"%c%c%c",0,0,0);
5056
5057                                         cityorcounty=1;
5058                                 }
5059
5060                                 if (cityorcounty==0)
5061                                 {
5062                                         if (contour_threshold!=0 && dBm<contour_threshold)
5063                                         {
5064                                                 if (ngs) /* No terrain */
5065                                                         fprintf(fd,"%c%c%c",255,255,255);
5066                                                 else
5067                                                 {
5068                                                         /* Display land or sea elevation */
5069
5070                                                         if (dem[indx].data[x0][y0]==0)
5071                                                                 fprintf(fd,"%c%c%c",0,0,170);
5072                                                         else
5073                                                         {
5074                                                                 terrain=(unsigned)(0.5+pow((double)(dem[indx].data[x0][y0]-min_elevation),one_over_gamma)*conversion);
5075                                                                 fprintf(fd,"%c%c%c",terrain,terrain,terrain);
5076                                                         }
5077                                                 }
5078                                         }
5079
5080                                         else
5081                                         {
5082                                                 /* Plot signal power level regions in color */
5083
5084                                                 if (red!=0 || green!=0 || blue!=0)
5085                                                         fprintf(fd,"%c%c%c",red,green,blue);
5086
5087                                                 else  /* terrain / sea-level */
5088                                                 {
5089                                                         if (ngs)
5090                                                                 fprintf(fd,"%c%c%c",255,255,255);
5091                                                         else
5092                                                         {
5093                                                                 if (dem[indx].data[x0][y0]==0)
5094                                                                         fprintf(fd,"%c%c%c",0,0,170);
5095                                                                 else
5096                                                                 {
5097                                                                         /* Elevation: Greyscale */
5098                                                                         terrain=(unsigned)(0.5+pow((double)(dem[indx].data[x0][y0]-min_elevation),one_over_gamma)*conversion);
5099                                                                         fprintf(fd,"%c%c%c",terrain,terrain,terrain);
5100                                                                 }
5101                                                         }
5102                                                 }
5103                                         }
5104                                 }
5105                         }
5106
5107                         else
5108                         {
5109                                 /* We should never get here, but if */
5110                                 /* we do, display the region as black */
5111
5112                                 fprintf(fd,"%c%c%c",0,0,0);
5113                         }
5114                 }
5115         }
5116
5117         if (kml==0 && geo==0)
5118         {
5119                 /* Display legend along bottom of image
5120                    if not generating .kml or .geo output. */
5121
5122                 colorwidth=(int)rint((float)width/(float)region.levels);
5123
5124                 for (y0=0; y0<30; y0++)
5125                 {
5126                         for (x0=0; x0<(int)width; x0++)
5127                         {
5128                                 indx=x0/colorwidth;
5129                                 x=x0%colorwidth;
5130
5131                                 level=abs(region.level[indx]);
5132
5133                                 hundreds=level/100;
5134
5135                                 if (hundreds>0)
5136                                         level-=(hundreds*100);
5137
5138                                 tens=level/10;
5139
5140                                 if (tens>0)
5141                                         level-=(tens*10);
5142
5143                                 units=level;
5144
5145                                 if (y0>=8 && y0<=23)
5146                                 {
5147                                         if (hundreds>0)
5148                                         {
5149                                                 if (region.level[indx]<0)
5150                                                 {
5151                                                         if (x>=5 && x<=12)
5152                                                                 if (fontdata[16*('-')+(y0-8)]&(128>>(x-5)))
5153                                                                         indx=255;
5154                                                 }
5155
5156                                                 else
5157                                                 {       
5158                                                         if (x>=5 && x<=12)
5159                                                                 if (fontdata[16*('+')+(y0-8)]&(128>>(x-5)))
5160                                                                         indx=255;
5161                                                 }
5162
5163                                                 if (x>=13 && x<=20)     
5164                                                         if (fontdata[16*(hundreds+'0')+(y0-8)]&(128>>(x-13)))
5165                                                                 indx=255; 
5166                                         }
5167
5168                                         if (tens>0 || hundreds>0)
5169                                         {
5170                                                 if (hundreds==0)
5171                                                 {
5172                                                         if (region.level[indx]<0)
5173                                                         {
5174                                                                 if (x>=13 && x<=20)
5175                                                                         if (fontdata[16*('-')+(y0-8)]&(128>>(x-13)))
5176                                                                                 indx=255;
5177                                                         }
5178
5179                                                         else
5180                                                         {
5181                                                                 if (x>=13 && x<=20)
5182                                                                         if (fontdata[16*('+')+(y0-8)]&(128>>(x-13)))
5183                                                                                 indx=255;
5184                                                         }
5185                                                 }
5186                                                 
5187                                                 if (x>=21 && x<=28)     
5188                                                         if (fontdata[16*(tens+'0')+(y0-8)]&(128>>(x-21)))
5189                                                                 indx=255;
5190                                         }
5191
5192                                         if (hundreds==0 && tens==0)
5193                                         {
5194                                                 if (region.level[indx]<0)
5195                                                 {
5196                                                         if (x>=21 && x<=28)
5197                                                                 if (fontdata[16*('-')+(y0-8)]&(128>>(x-21)))
5198                                                                         indx=255;
5199                                                 }
5200
5201                                                 else
5202                                                 {
5203                                                         if (x>=21 && x<=28)
5204                                                                 if (fontdata[16*('+')+(y0-8)]&(128>>(x-21)))
5205                                                                         indx=255;
5206                                                 }
5207                                         }
5208
5209                                         if (x>=29 && x<=36)
5210                                                 if (fontdata[16*(units+'0')+(y0-8)]&(128>>(x-29)))
5211                                                         indx=255;
5212
5213                                         if (x>=37 && x<=44)
5214                                                 if (fontdata[16*('d')+(y0-8)]&(128>>(x-37)))
5215                                                         indx=255;
5216
5217                                         if (x>=45 && x<=52)
5218                                                 if (fontdata[16*('B')+(y0-8)]&(128>>(x-45)))
5219                                                         indx=255;
5220
5221                                         if (x>=53 && x<=60)
5222                                                 if (fontdata[16*('m')+(y0-8)]&(128>>(x-53)))
5223                                                         indx=255;
5224                                 }
5225
5226                                 if (indx>region.levels)
5227                                         fprintf(fd,"%c%c%c",0,0,0);
5228                                 else
5229                                 {
5230                                         red=region.color[indx][0];
5231                                         green=region.color[indx][1];
5232                                         blue=region.color[indx][2];
5233
5234                                         fprintf(fd,"%c%c%c",red,green,blue);
5235                                 }
5236                         } 
5237                 }
5238         }
5239
5240         fclose(fd);
5241         fprintf(stdout,"Done!\n");
5242         fflush(stdout);
5243 }
5244
5245 void GraphTerrain(struct site source, struct site destination, char *name)
5246 {
5247         /* This function invokes gnuplot to generate an appropriate
5248            output file indicating the terrain profile between the source
5249            and destination locations when the -p command line option
5250            is used.  "basename" is the name assigned to the output
5251            file generated by gnuplot.  The filename extension is used
5252            to set gnuplot's terminal setting and output file type.
5253            If no extension is found, .png is assumed.  */
5254
5255         int     x, y, z;
5256         char    basename[255], term[30], ext[15];
5257         double  minheight=100000.0, maxheight=-100000.0;
5258         FILE    *fd=NULL, *fd1=NULL;
5259
5260         ReadPath(destination,source);
5261
5262         fd=fopen("profile.gp","wb");
5263
5264         if (clutter>0.0)
5265                 fd1=fopen("clutter.gp","wb");
5266
5267         for (x=0; x<path.length; x++)
5268         {
5269                 if ((path.elevation[x]+clutter)>maxheight)
5270                         maxheight=path.elevation[x]+clutter;
5271
5272                 if (path.elevation[x]<minheight)
5273                         minheight=path.elevation[x];
5274
5275                 if (metric)
5276                 {
5277                         fprintf(fd,"%f\t%f\n",KM_PER_MILE*path.distance[x],METERS_PER_FOOT*path.elevation[x]);
5278
5279                         if (fd1!=NULL && x>0 && x<path.length-2)
5280                                 fprintf(fd1,"%f\t%f\n",KM_PER_MILE*path.distance[x],METERS_PER_FOOT*(path.elevation[x]==0.0?path.elevation[x]:(path.elevation[x]+clutter)));
5281                 }
5282
5283                 else
5284                 {
5285                         fprintf(fd,"%f\t%f\n",path.distance[x],path.elevation[x]);
5286
5287                         if (fd1!=NULL && x>0 && x<path.length-2)
5288                                 fprintf(fd1,"%f\t%f\n",path.distance[x],(path.elevation[x]==0.0?path.elevation[x]:(path.elevation[x]+clutter)));
5289                 }
5290         }
5291
5292         fclose(fd);
5293
5294         if (fd1!=NULL)
5295                 fclose(fd1);
5296
5297         if (name[0]=='.')
5298         {
5299                 /* Default filename and output file type */
5300
5301                 strncpy(basename,"profile\0",8);
5302                 strncpy(term,"png\0",4);
5303                 strncpy(ext,"png\0",4);
5304         }
5305
5306         else
5307         {
5308                 /* Extract extension and terminal type from "name" */
5309
5310                 ext[0]=0;
5311                 y=strlen(name);
5312                 strncpy(basename,name,254);
5313
5314                 for (x=y-1; x>0 && name[x]!='.'; x--);
5315
5316                 if (x>0)  /* Extension found */
5317                 {
5318                         for (z=x+1; z<=y && (z-(x+1))<10; z++)
5319                         {
5320                                 ext[z-(x+1)]=tolower(name[z]);
5321                                 term[z-(x+1)]=name[z];
5322                         }
5323
5324                         ext[z-(x+1)]=0;  /* Ensure an ending 0 */
5325                         term[z-(x+1)]=0;
5326                         basename[x]=0;
5327                 }
5328
5329                 if (ext[0]==0)  /* No extension -- Default is png */
5330                 {
5331                         strncpy(term,"png\0",4);
5332                         strncpy(ext,"png\0",4);
5333                 }
5334         }
5335
5336         /* Either .ps or .postscript may be used
5337            as an extension for postscript output. */
5338
5339         if (strncmp(term,"postscript",10)==0)
5340                 strncpy(ext,"ps\0",3);
5341
5342         else if (strncmp(ext,"ps",2)==0)
5343                         strncpy(term,"postscript enhanced color\0",26);
5344
5345         minheight-=(0.01*maxheight);
5346
5347         fd=fopen("splat.gp","w");
5348         fprintf(fd,"set grid\n");
5349         fprintf(fd,"set yrange [%2.3f to %2.3f]\n", metric?minheight*METERS_PER_FOOT:minheight, metric?maxheight*METERS_PER_FOOT:maxheight);
5350         fprintf(fd,"set encoding iso_8859_1\n");
5351         fprintf(fd,"set term %s\n",term);
5352         fprintf(fd,"set title \"%s Terrain Profile Between %s and %s (%.2f%c Azimuth)\"\n",splat_name,destination.name, source.name, Azimuth(destination,source),176);
5353
5354         if (metric)
5355         {
5356                 fprintf(fd,"set xlabel \"Distance Between %s and %s (%.2f kilometers)\"\n",destination.name,source.name,KM_PER_MILE*Distance(source,destination));
5357                 fprintf(fd,"set ylabel \"Ground Elevation Above Sea Level (meters)\"\n");
5358         }
5359
5360         else
5361         {
5362                 fprintf(fd,"set xlabel \"Distance Between %s and %s (%.2f miles)\"\n",destination.name,source.name,Distance(source,destination));
5363                 fprintf(fd,"set ylabel \"Ground Elevation Above Sea Level (feet)\"\n");
5364         }
5365
5366         fprintf(fd,"set output \"%s.%s\"\n",basename,ext);
5367
5368         if (clutter>0.0)
5369         {
5370                 if (metric)
5371                         fprintf(fd,"plot \"profile.gp\" title \"Terrain Profile\" with lines, \"clutter.gp\" title \"Clutter Profile (%.2f meters)\" with lines\n",clutter*METERS_PER_FOOT);
5372                 else
5373                         fprintf(fd,"plot \"profile.gp\" title \"Terrain Profile\" with lines, \"clutter.gp\" title \"Clutter Profile (%.2f feet)\" with lines\n",clutter);
5374         }
5375
5376         else
5377                 fprintf(fd,"plot \"profile.gp\" title \"\" with lines\n");
5378
5379         fclose(fd);
5380                         
5381         x=system("gnuplot splat.gp");
5382
5383         if (x!=-1)
5384         {
5385                 if (gpsav==0)
5386                 {       
5387                         unlink("splat.gp");
5388                         unlink("profile.gp");
5389                 }
5390
5391                 fprintf(stdout,"Terrain plot written to: \"%s.%s\"\n",basename,ext);
5392                 fflush(stdout);
5393         }
5394
5395         else
5396                 fprintf(stderr,"\n*** ERROR: Error occurred invoking gnuplot!\n");
5397 }
5398
5399 void GraphElevation(struct site source, struct site destination, char *name)
5400 {
5401         /* This function invokes gnuplot to generate an appropriate
5402            output file indicating the terrain elevation profile between
5403            the source and destination locations when the -e command line
5404            option is used.  "basename" is the name assigned to the output
5405            file generated by gnuplot.  The filename extension is used
5406            to set gnuplot's terminal setting and output file type.
5407            If no extension is found, .png is assumed.  */
5408
5409         int     x, y, z;
5410         char    basename[255], term[30], ext[15];
5411         double  angle, clutter_angle=0.0, refangle, maxangle=-90.0,
5412                 minangle=90.0, distance;
5413         struct  site remote, remote2;
5414         FILE    *fd=NULL, *fd1=NULL, *fd2=NULL;
5415
5416         ReadPath(destination,source);  /* destination=RX, source=TX */
5417         refangle=ElevationAngle(destination,source);
5418         distance=Distance(source,destination);
5419
5420         fd=fopen("profile.gp","wb");
5421
5422         if (clutter>0.0)
5423                 fd1=fopen("clutter.gp","wb");
5424
5425         fd2=fopen("reference.gp","wb");
5426
5427         for (x=1; x<path.length-1; x++)
5428         {
5429                 remote.lat=path.lat[x];
5430                 remote.lon=path.lon[x];
5431                 remote.alt=0.0;
5432                 angle=ElevationAngle(destination,remote);
5433
5434                 if (clutter>0.0)
5435                 {
5436                         remote2.lat=path.lat[x];
5437                         remote2.lon=path.lon[x];
5438
5439                         if (path.elevation[x]!=0.0)
5440                                 remote2.alt=clutter;
5441                         else
5442                                 remote2.alt=0.0;
5443
5444                         clutter_angle=ElevationAngle(destination,remote2);
5445                 }
5446
5447                 if (metric)
5448                 {
5449                         fprintf(fd,"%f\t%f\n",KM_PER_MILE*path.distance[x],angle);
5450
5451                         if (fd1!=NULL)
5452                                 fprintf(fd1,"%f\t%f\n",KM_PER_MILE*path.distance[x],clutter_angle);
5453
5454                         fprintf(fd2,"%f\t%f\n",KM_PER_MILE*path.distance[x],refangle);
5455                 }
5456
5457                 else
5458                 {
5459                         fprintf(fd,"%f\t%f\n",path.distance[x],angle);
5460
5461                         if (fd1!=NULL)
5462                                 fprintf(fd1,"%f\t%f\n",path.distance[x],clutter_angle);
5463
5464                         fprintf(fd2,"%f\t%f\n",path.distance[x],refangle);
5465                 }
5466
5467                 if (angle>maxangle)
5468                         maxangle=angle;
5469
5470                 if (clutter_angle>maxangle)
5471                         maxangle=clutter_angle;
5472
5473                 if (angle<minangle)
5474                         minangle=angle;
5475         }
5476
5477         if (metric)
5478         {
5479                 fprintf(fd,"%f\t%f\n",KM_PER_MILE*path.distance[path.length-1],refangle);
5480                 fprintf(fd2,"%f\t%f\n",KM_PER_MILE*path.distance[path.length-1],refangle);
5481         }
5482
5483         else
5484         {
5485                 fprintf(fd,"%f\t%f\n",path.distance[path.length-1],refangle);
5486                 fprintf(fd2,"%f\t%f\n",path.distance[path.length-1],refangle);
5487         }
5488
5489         fclose(fd);
5490
5491         if (fd1!=NULL)
5492                 fclose(fd1);
5493
5494         fclose(fd2);
5495
5496         if (name[0]=='.')
5497         {
5498                 /* Default filename and output file type */
5499
5500                 strncpy(basename,"profile\0",8);
5501                 strncpy(term,"png\0",4);
5502                 strncpy(ext,"png\0",4);
5503         }
5504
5505         else
5506         {
5507                 /* Extract extension and terminal type from "name" */
5508
5509                 ext[0]=0;
5510                 y=strlen(name);
5511                 strncpy(basename,name,254);
5512
5513                 for (x=y-1; x>0 && name[x]!='.'; x--);
5514
5515                 if (x>0)  /* Extension found */
5516                 {
5517                         for (z=x+1; z<=y && (z-(x+1))<10; z++)
5518                         {
5519                                 ext[z-(x+1)]=tolower(name[z]);
5520                                 term[z-(x+1)]=name[z];
5521                         }
5522
5523                         ext[z-(x+1)]=0;  /* Ensure an ending 0 */
5524                         term[z-(x+1)]=0;
5525                         basename[x]=0;
5526                 }
5527
5528                 if (ext[0]==0)  /* No extension -- Default is png */
5529                 {
5530                         strncpy(term,"png\0",4);
5531                         strncpy(ext,"png\0",4);
5532                 }
5533         }
5534
5535         /* Either .ps or .postscript may be used
5536            as an extension for postscript output. */
5537
5538         if (strncmp(term,"postscript",10)==0)
5539                 strncpy(ext,"ps\0",3);
5540
5541         else if (strncmp(ext,"ps",2)==0)
5542                         strncpy(term,"postscript enhanced color\0",26);
5543
5544         fd=fopen("splat.gp","w");
5545
5546         fprintf(fd,"set grid\n");
5547
5548         if (distance>2.0)
5549                 fprintf(fd,"set yrange [%2.3f to %2.3f]\n", (-fabs(refangle)-0.25), maxangle+0.25);
5550         else
5551                 fprintf(fd,"set yrange [%2.3f to %2.3f]\n", minangle, refangle+(-minangle/8.0));
5552
5553         fprintf(fd,"set encoding iso_8859_1\n");
5554         fprintf(fd,"set term %s\n",term);
5555         fprintf(fd,"set title \"%s Elevation Profile Between %s and %s (%.2f%c azimuth)\"\n",splat_name,destination.name,source.name,Azimuth(destination,source),176);
5556
5557         if (metric)
5558                 fprintf(fd,"set xlabel \"Distance Between %s and %s (%.2f kilometers)\"\n",destination.name,source.name,KM_PER_MILE*distance);
5559         else
5560                 fprintf(fd,"set xlabel \"Distance Between %s and %s (%.2f miles)\"\n",destination.name,source.name,distance);
5561
5562
5563         fprintf(fd,"set ylabel \"Elevation Angle Along LOS Path Between\\n%s and %s (degrees)\"\n",destination.name,source.name);
5564         fprintf(fd,"set output \"%s.%s\"\n",basename,ext);
5565
5566         if (clutter>0.0)
5567         {
5568                 if (metric)
5569                         fprintf(fd,"plot \"profile.gp\" title \"Real Earth Profile\" with lines, \"clutter.gp\" title \"Clutter Profile (%.2f meters)\" with lines, \"reference.gp\" title \"Line of Sight Path (%.2f%c elevation)\" with lines\n",clutter*METERS_PER_FOOT,refangle,176);
5570                 else
5571                         fprintf(fd,"plot \"profile.gp\" title \"Real Earth Profile\" with lines, \"clutter.gp\" title \"Clutter Profile (%.2f feet)\" with lines, \"reference.gp\" title \"Line of Sight Path (%.2f%c elevation)\" with lines\n",clutter,refangle,176);
5572         }
5573
5574         else
5575                 fprintf(fd,"plot \"profile.gp\" title \"Real Earth Profile\" with lines, \"reference.gp\" title \"Line of Sight Path (%.2f%c elevation)\" with lines\n",refangle,176);
5576
5577         fclose(fd);
5578                         
5579         x=system("gnuplot splat.gp");
5580
5581         if (x!=-1)
5582         {
5583                 if (gpsav==0)
5584                 {
5585                         unlink("splat.gp");
5586                         unlink("profile.gp");
5587                         unlink("reference.gp");
5588
5589                         if (clutter>0.0)
5590                                 unlink("clutter.gp");
5591                 }       
5592
5593                 fprintf(stdout,"Elevation plot written to: \"%s.%s\"\n",basename,ext);
5594                 fflush(stdout);
5595         }
5596
5597         else
5598                 fprintf(stderr,"\n*** ERROR: Error occurred invoking gnuplot!\n");
5599 }
5600
5601 void GraphHeight(struct site source, struct site destination, char *name, unsigned char fresnel_plot, unsigned char normalized)
5602 {
5603         /* This function invokes gnuplot to generate an appropriate
5604            output file indicating the terrain height profile between
5605            the source and destination locations referenced to the
5606            line-of-sight path between the receive and transmit sites
5607            when the -h or -H command line option is used.  "basename"
5608            is the name assigned to the output file generated by gnuplot.
5609            The filename extension is used to set gnuplot's terminal
5610            setting and output file type.  If no extension is found,
5611            .png is assumed.  */
5612
5613         int     x, y, z;
5614         char    basename[255], term[30], ext[15];
5615         double  a, b, c, height=0.0, refangle, cangle, maxheight=-100000.0,
5616                 minheight=100000.0, lambda=0.0, f_zone=0.0, fpt6_zone=0.0,
5617                 nm=0.0, nb=0.0, ed=0.0, es=0.0, r=0.0, d=0.0, d1=0.0,
5618                 terrain, azimuth, distance, dheight=0.0, minterrain=100000.0,
5619                 minearth=100000.0, miny, maxy, min2y, max2y;
5620         struct  site remote;
5621         FILE    *fd=NULL, *fd1=NULL, *fd2=NULL, *fd3=NULL, *fd4=NULL, *fd5=NULL;
5622
5623         ReadPath(destination,source);  /* destination=RX, source=TX */
5624         azimuth=Azimuth(destination,source);
5625         distance=Distance(destination,source);
5626         refangle=ElevationAngle(destination,source);
5627         b=GetElevation(destination)+destination.alt+earthradius;
5628
5629         /* Wavelength and path distance (great circle) in feet. */
5630
5631         if (fresnel_plot)
5632         {
5633                 lambda=9.8425e8/(LR.frq_mhz*1e6);
5634                 d=5280.0*path.distance[path.length-1];
5635         }
5636
5637         if (normalized)
5638         {
5639                 ed=GetElevation(destination);
5640                 es=GetElevation(source);
5641                 nb=-destination.alt-ed;
5642                 nm=(-source.alt-es-nb)/(path.distance[path.length-1]);
5643         }
5644
5645         fd=fopen("profile.gp","wb");
5646
5647         if (clutter>0.0)
5648                 fd1=fopen("clutter.gp","wb");
5649
5650         fd2=fopen("reference.gp","wb");
5651         fd5=fopen("curvature.gp", "wb");
5652
5653         if ((LR.frq_mhz>=20.0) && (LR.frq_mhz<=20000.0) && fresnel_plot)
5654         {
5655                 fd3=fopen("fresnel.gp", "wb");
5656                 fd4=fopen("fresnel_pt_6.gp", "wb");
5657         }
5658
5659         for (x=0; x<path.length-1; x++)
5660         {
5661                 remote.lat=path.lat[x];
5662                 remote.lon=path.lon[x];
5663                 remote.alt=0.0;
5664
5665                 terrain=GetElevation(remote);
5666
5667                 if (x==0)
5668                         terrain+=destination.alt;  /* RX antenna spike */
5669
5670                 a=terrain+earthradius;
5671                 cangle=5280.0*Distance(destination,remote)/earthradius;
5672                 c=b*sin(refangle*DEG2RAD+HALFPI)/sin(HALFPI-refangle*DEG2RAD-cangle);
5673
5674                 height=a-c;
5675
5676                 /* Per Fink and Christiansen, Electronics
5677                  * Engineers' Handbook, 1989:
5678                  *
5679                  *   H = sqrt(lamba * d1 * (d - d1)/d)
5680                  *
5681                  * where H is the distance from the LOS
5682                  * path to the first Fresnel zone boundary.
5683                  */
5684
5685                 if ((LR.frq_mhz>=20.0) && (LR.frq_mhz<=20000.0) && fresnel_plot)
5686                 {
5687                         d1=5280.0*path.distance[x];
5688                         f_zone=-1.0*sqrt(lambda*d1*(d-d1)/d);
5689                         fpt6_zone=f_zone*fzone_clearance;
5690                 }
5691
5692                 if (normalized)
5693                 {
5694                         r=-(nm*path.distance[x])-nb;
5695                         height+=r;
5696
5697                         if ((LR.frq_mhz>=20.0) && (LR.frq_mhz<=20000.0) && fresnel_plot)
5698                         {
5699                                 f_zone+=r;
5700                                 fpt6_zone+=r;
5701                         }
5702                 }
5703
5704                 else
5705                         r=0.0;
5706
5707                 if (metric)
5708                 {
5709                         fprintf(fd,"%f\t%f\n",KM_PER_MILE*path.distance[x],METERS_PER_FOOT*height);
5710
5711                         if (fd1!=NULL && x>0 && x<path.length-2)
5712                                 fprintf(fd1,"%f\t%f\n",KM_PER_MILE*path.distance[x],METERS_PER_FOOT*(terrain==0.0?height:(height+clutter)));
5713
5714                         fprintf(fd2,"%f\t%f\n",KM_PER_MILE*path.distance[x],METERS_PER_FOOT*r);
5715                         fprintf(fd5,"%f\t%f\n",KM_PER_MILE*path.distance[x],METERS_PER_FOOT*(height-terrain));
5716                 }
5717
5718                 else
5719                 {
5720                         fprintf(fd,"%f\t%f\n",path.distance[x],height);
5721
5722                         if (fd1!=NULL && x>0 && x<path.length-2)
5723                                 fprintf(fd1,"%f\t%f\n",path.distance[x],(terrain==0.0?height:(height+clutter)));
5724
5725                         fprintf(fd2,"%f\t%f\n",path.distance[x],r);
5726                         fprintf(fd5,"%f\t%f\n",path.distance[x],height-terrain);
5727                 }
5728
5729                 if ((LR.frq_mhz>=20.0) && (LR.frq_mhz<=20000.0) && fresnel_plot)
5730                 {
5731                         if (metric)
5732                         {
5733                                 fprintf(fd3,"%f\t%f\n",KM_PER_MILE*path.distance[x],METERS_PER_FOOT*f_zone);
5734                                 fprintf(fd4,"%f\t%f\n",KM_PER_MILE*path.distance[x],METERS_PER_FOOT*fpt6_zone);
5735                         }
5736
5737                         else
5738                         {
5739                                 fprintf(fd3,"%f\t%f\n",path.distance[x],f_zone);
5740                                 fprintf(fd4,"%f\t%f\n",path.distance[x],fpt6_zone);
5741                         }
5742
5743                         if (f_zone<minheight)
5744                                 minheight=f_zone;
5745                 }
5746
5747                 if ((height+clutter)>maxheight)
5748                         maxheight=height+clutter;
5749
5750                 if (height<minheight)
5751                         minheight=height;
5752
5753                 if (r>maxheight)
5754                         maxheight=r;
5755
5756                 if (terrain<minterrain)
5757                         minterrain=terrain;
5758
5759                 if ((height-terrain)<minearth)
5760                         minearth=height-terrain;
5761         }
5762
5763         if (normalized)
5764                 r=-(nm*path.distance[path.length-1])-nb;
5765         else
5766                 r=0.0;
5767
5768         if (metric)
5769         {
5770                 fprintf(fd,"%f\t%f\n",KM_PER_MILE*path.distance[path.length-1],METERS_PER_FOOT*r);
5771                 fprintf(fd2,"%f\t%f\n",KM_PER_MILE*path.distance[path.length-1],METERS_PER_FOOT*r);
5772         }
5773
5774         else
5775         {
5776                 fprintf(fd,"%f\t%f\n",path.distance[path.length-1],r);
5777                 fprintf(fd2,"%f\t%f\n",path.distance[path.length-1],r);
5778         }
5779
5780         if ((LR.frq_mhz>=20.0) && (LR.frq_mhz<=20000.0) && fresnel_plot)
5781         {
5782                 if (metric)
5783                 {
5784                         fprintf(fd3,"%f\t%f\n",KM_PER_MILE*path.distance[path.length-1],METERS_PER_FOOT*r);
5785                         fprintf(fd4,"%f\t%f\n",KM_PER_MILE*path.distance[path.length-1],METERS_PER_FOOT*r);
5786                 }
5787
5788                 else
5789                 {
5790                         fprintf(fd3,"%f\t%f\n",path.distance[path.length-1],r);
5791                         fprintf(fd4,"%f\t%f\n",path.distance[path.length-1],r);
5792                 }
5793         }
5794         
5795         if (r>maxheight)
5796                 maxheight=r;
5797
5798         if (r<minheight)
5799                 minheight=r;
5800
5801         fclose(fd);
5802
5803         if (fd1!=NULL)
5804                 fclose(fd1);
5805
5806         fclose(fd2);
5807         fclose(fd5);
5808
5809         if ((LR.frq_mhz>=20.0) && (LR.frq_mhz<=20000.0) && fresnel_plot)
5810         {
5811                 fclose(fd3);
5812                 fclose(fd4);
5813         }
5814
5815         if (name[0]=='.')
5816         {
5817                 /* Default filename and output file type */
5818
5819                 strncpy(basename,"profile\0",8);
5820                 strncpy(term,"png\0",4);
5821                 strncpy(ext,"png\0",4);
5822         }
5823
5824         else
5825         {
5826                 /* Extract extension and terminal type from "name" */
5827
5828                 ext[0]=0;
5829                 y=strlen(name);
5830                 strncpy(basename,name,254);
5831
5832                 for (x=y-1; x>0 && name[x]!='.'; x--);
5833
5834                 if (x>0)  /* Extension found */
5835                 {
5836                         for (z=x+1; z<=y && (z-(x+1))<10; z++)
5837                         {
5838                                 ext[z-(x+1)]=tolower(name[z]);
5839                                 term[z-(x+1)]=name[z];
5840                         }
5841
5842                         ext[z-(x+1)]=0;  /* Ensure an ending 0 */
5843                         term[z-(x+1)]=0;
5844                         basename[x]=0;
5845                 }
5846
5847                 if (ext[0]==0)  /* No extension -- Default is png */
5848                 {
5849                         strncpy(term,"png\0",4);
5850                         strncpy(ext,"png\0",4);
5851                 }
5852         }
5853
5854         /* Either .ps or .postscript may be used
5855            as an extension for postscript output. */
5856
5857         if (strncmp(term,"postscript",10)==0)
5858                 strncpy(ext,"ps\0",3);
5859
5860         else if (strncmp(ext,"ps",2)==0)
5861                         strncpy(term,"postscript enhanced color\0",26);
5862
5863         fd=fopen("splat.gp","w");
5864
5865         dheight=maxheight-minheight;
5866         miny=minheight-0.15*dheight;
5867         maxy=maxheight+0.05*dheight;
5868
5869         if (maxy<20.0)
5870                 maxy=20.0;
5871
5872         dheight=maxheight-minheight;
5873         min2y=miny-minterrain+0.05*dheight;
5874
5875         if (minearth<min2y)
5876         {
5877                 miny-=min2y-minearth+0.05*dheight;
5878                 min2y=minearth-0.05*dheight;
5879         }
5880
5881         max2y=min2y+maxy-miny;
5882  
5883         fprintf(fd,"set grid\n");
5884         fprintf(fd,"set yrange [%2.3f to %2.3f]\n", metric?miny*METERS_PER_FOOT:miny, metric?maxy*METERS_PER_FOOT:maxy);
5885         fprintf(fd,"set y2range [%2.3f to %2.3f]\n", metric?min2y*METERS_PER_FOOT:min2y, metric?max2y*METERS_PER_FOOT:max2y);
5886         fprintf(fd,"set xrange [-0.5 to %2.3f]\n",metric?KM_PER_MILE*rint(distance+0.5):rint(distance+0.5));
5887         fprintf(fd,"set encoding iso_8859_1\n");
5888         fprintf(fd,"set term %s\n",term);
5889
5890         if ((LR.frq_mhz>=20.0) && (LR.frq_mhz<=20000.0) && fresnel_plot)
5891                 fprintf(fd,"set title \"%s Path Profile Between %s and %s (%.2f%c azimuth)\\nWith First Fresnel Zone\"\n",splat_name, destination.name, source.name, azimuth,176);
5892
5893         else
5894                 fprintf(fd,"set title \"%s Height Profile Between %s and %s (%.2f%c azimuth)\"\n",splat_name, destination.name, source.name, azimuth,176);
5895
5896         if (metric)
5897                 fprintf(fd,"set xlabel \"Distance Between %s and %s (%.2f kilometers)\"\n",destination.name,source.name,KM_PER_MILE*Distance(source,destination));
5898         else
5899                 fprintf(fd,"set xlabel \"Distance Between %s and %s (%.2f miles)\"\n",destination.name,source.name,Distance(source,destination));
5900
5901         if (normalized)
5902         {
5903                 if (metric)
5904                         fprintf(fd,"set ylabel \"Normalized Height Referenced To LOS Path Between\\n%s and %s (meters)\"\n",destination.name,source.name);
5905
5906                 else
5907                         fprintf(fd,"set ylabel \"Normalized Height Referenced To LOS Path Between\\n%s and %s (feet)\"\n",destination.name,source.name);
5908
5909         }
5910
5911         else
5912         {
5913                 if (metric)
5914                         fprintf(fd,"set ylabel \"Height Referenced To LOS Path Between\\n%s and %s (meters)\"\n",destination.name,source.name);
5915
5916                 else
5917                         fprintf(fd,"set ylabel \"Height Referenced To LOS Path Between\\n%s and %s (feet)\"\n",destination.name,source.name);
5918         }
5919
5920         fprintf(fd,"set output \"%s.%s\"\n",basename,ext);
5921
5922         if ((LR.frq_mhz>=20.0) && (LR.frq_mhz<=20000.0) && fresnel_plot)
5923         {
5924                 if (clutter>0.0)
5925                 {
5926                         if (metric)
5927                                 fprintf(fd,"plot \"profile.gp\" title \"Point-to-Point Profile\" with lines, \"clutter.gp\" title \"Ground Clutter (%.2f meters)\" with lines, \"reference.gp\" title \"Line of Sight Path\" with lines, \"curvature.gp\" axes x1y2 title \"Earth's Curvature Contour\" with lines, \"fresnel.gp\" axes x1y1 title \"First Fresnel Zone (%.3f MHz)\" with lines, \"fresnel_pt_6.gp\" title \"%.0f%% of First Fresnel Zone\" with lines\n",clutter*METERS_PER_FOOT,LR.frq_mhz,fzone_clearance*100.0);
5928                         else
5929                                 fprintf(fd,"plot \"profile.gp\" title \"Point-to-Point Profile\" with lines, \"clutter.gp\" title \"Ground Clutter (%.2f feet)\" with lines, \"reference.gp\" title \"Line of Sight Path\" with lines, \"curvature.gp\" axes x1y2 title \"Earth's Curvature Contour\" with lines, \"fresnel.gp\" axes x1y1 title \"First Fresnel Zone (%.3f MHz)\" with lines, \"fresnel_pt_6.gp\" title \"%.0f%% of First Fresnel Zone\" with lines\n",clutter,LR.frq_mhz,fzone_clearance*100.0);
5930                 }
5931
5932                 else
5933                         fprintf(fd,"plot \"profile.gp\" title \"Point-to-Point Profile\" with lines, \"reference.gp\" title \"Line of Sight Path\" with lines, \"curvature.gp\" axes x1y2 title \"Earth's Curvature Contour\" with lines, \"fresnel.gp\" axes x1y1 title \"First Fresnel Zone (%.3f MHz)\" with lines, \"fresnel_pt_6.gp\" title \"%.0f%% of First Fresnel Zone\" with lines\n",LR.frq_mhz,fzone_clearance*100.0);
5934         }
5935
5936         else
5937         {
5938                 if (clutter>0.0)
5939                 {
5940                         if (metric)
5941                                 fprintf(fd,"plot \"profile.gp\" title \"Point-to-Point Profile\" with lines, \"clutter.gp\" title \"Ground Clutter (%.2f meters)\" with lines, \"reference.gp\" title \"Line Of Sight Path\" with lines, \"curvature.gp\" axes x1y2 title \"Earth's Curvature Contour\" with lines\n",clutter*METERS_PER_FOOT);
5942                         else
5943                                 fprintf(fd,"plot \"profile.gp\" title \"Point-to-Point Profile\" with lines, \"clutter.gp\" title \"Ground Clutter (%.2f feet)\" with lines, \"reference.gp\" title \"Line Of Sight Path\" with lines, \"curvature.gp\" axes x1y2 title \"Earth's Curvature Contour\" with lines\n",clutter);
5944                 }
5945
5946                 else
5947                         fprintf(fd,"plot \"profile.gp\" title \"Point-to-Point Profile\" with lines, \"reference.gp\" title \"Line Of Sight Path\" with lines, \"curvature.gp\" axes x1y2 title \"Earth's Curvature Contour\" with lines\n");
5948
5949         }
5950
5951         fclose(fd);
5952
5953         x=system("gnuplot splat.gp");
5954
5955         if (x!=-1)
5956         {
5957                 if (gpsav==0)
5958                 {
5959                         unlink("splat.gp");
5960                         unlink("profile.gp");
5961                         unlink("reference.gp");
5962                         unlink("curvature.gp");
5963
5964                         if (fd1!=NULL)
5965                                 unlink("clutter.gp");
5966
5967                         if ((LR.frq_mhz>=20.0) && (LR.frq_mhz<=20000.0) && fresnel_plot)
5968                         {
5969                                 unlink("fresnel.gp");
5970                                 unlink("fresnel_pt_6.gp");
5971                         }
5972                 }
5973
5974                 fprintf(stdout,"\nHeight plot written to: \"%s.%s\"",basename,ext);
5975                 fflush(stdout);
5976         }
5977
5978         else
5979                 fprintf(stderr,"\n*** ERROR: Error occurred invoking gnuplot!\n");
5980 }
5981
5982 void ObstructionAnalysis(struct site xmtr, struct site rcvr, double f, FILE *outfile)
5983 {
5984         /* Perform an obstruction analysis along the
5985            path between receiver and transmitter. */
5986
5987         int     x;
5988         struct  site site_x;
5989         double  h_r, h_t, h_x, h_r_orig, cos_tx_angle, cos_test_angle,
5990                 cos_tx_angle_f1, cos_tx_angle_fpt6, d_tx, d_x,
5991                 h_r_f1, h_r_fpt6, h_f, h_los, lambda=0.0;
5992         char    string[255], string_fpt6[255], string_f1[255];
5993
5994         ReadPath(xmtr,rcvr);
5995         h_r=GetElevation(rcvr)+rcvr.alt+earthradius;
5996         h_r_f1=h_r;
5997         h_r_fpt6=h_r;
5998         h_r_orig=h_r;
5999         h_t=GetElevation(xmtr)+xmtr.alt+earthradius;
6000         d_tx=5280.0*Distance(rcvr,xmtr);
6001         cos_tx_angle=((h_r*h_r)+(d_tx*d_tx)-(h_t*h_t))/(2.0*h_r*d_tx);
6002         cos_tx_angle_f1=cos_tx_angle;
6003         cos_tx_angle_fpt6=cos_tx_angle;
6004
6005         if (f)
6006                 lambda=9.8425e8/(f*1e6);
6007
6008         if (clutter>0.0)
6009         {
6010                 fprintf(outfile,"Terrain has been raised by");
6011
6012                 if (metric)
6013                         fprintf(outfile," %.2f meters",METERS_PER_FOOT*clutter);
6014                 else
6015                         fprintf(outfile," %.2f feet",clutter);
6016
6017                 fprintf(outfile," to account for ground clutter.\n\n");
6018         }
6019
6020         /* At each point along the path calculate the cosine
6021            of a sort of "inverse elevation angle" at the receiver.
6022            From the antenna, 0 deg. looks at the ground, and 90 deg.
6023            is parallel to the ground.
6024
6025            Start at the receiver.  If this is the lowest antenna,
6026            then terrain obstructions will be nearest to it.  (Plus,
6027            that's the way SPLAT!'s original los() did it.)
6028
6029            Calculate cosines only.  That's sufficient to compare
6030            angles and it saves the extra computational burden of
6031            acos().  However, note the inverted comparison: if
6032            acos(A) > acos(B), then B > A. */
6033
6034         for (x=path.length-1; x>0; x--)
6035         {
6036                 site_x.lat=path.lat[x];
6037                 site_x.lon=path.lon[x];
6038                 site_x.alt=0.0;
6039
6040                 h_x=GetElevation(site_x)+earthradius+clutter;
6041                 d_x=5280.0*Distance(rcvr,site_x);
6042
6043                 /* Deal with the LOS path first. */
6044
6045                 cos_test_angle=((h_r*h_r)+(d_x*d_x)-(h_x*h_x))/(2.0*h_r*d_x);
6046
6047                 if (cos_tx_angle>cos_test_angle)
6048                 {
6049                         if (h_r==h_r_orig)
6050                                 fprintf(outfile,"Between %s and %s, %s detected obstructions at:\n\n",rcvr.name,xmtr.name,splat_name);
6051
6052                         if (site_x.lat>=0.0)
6053                         {
6054                                 if (metric)
6055                                         fprintf(outfile,"   %8.4f N,%9.4f W, %5.2f kilometers, %6.2f meters AMSL\n",site_x.lat, site_x.lon, KM_PER_MILE*(d_x/5280.0), METERS_PER_FOOT*(h_x-earthradius));
6056                                 else
6057                                         fprintf(outfile,"   %8.4f N,%9.4f W, %5.2f miles, %6.2f feet AMSL\n",site_x.lat, site_x.lon, d_x/5280.0, h_x-earthradius);
6058                         }
6059
6060                         else
6061                         {
6062                                 if (metric)
6063                                         fprintf(outfile,"   %8.4f S,%9.4f W, %5.2f kilometers, %6.2f meters AMSL\n",-site_x.lat, site_x.lon, KM_PER_MILE*(d_x/5280.0), METERS_PER_FOOT*(h_x-earthradius));
6064                                 else
6065
6066                                         fprintf(outfile,"   %8.4f S,%9.4f W, %5.2f miles, %6.2f feet AMSL\n",-site_x.lat, site_x.lon, d_x/5280.0, h_x-earthradius);
6067                         }
6068                 }
6069
6070                 while (cos_tx_angle>cos_test_angle)
6071                 {
6072                         h_r+=1;
6073                         cos_test_angle=((h_r*h_r)+(d_x*d_x)-(h_x*h_x))/(2.0*h_r*d_x);
6074                         cos_tx_angle=((h_r*h_r)+(d_tx*d_tx)-(h_t*h_t))/(2.0*h_r*d_tx);
6075                 }
6076
6077                 if (f)
6078                 {
6079                         /* Now clear the first Fresnel zone... */
6080
6081                         cos_tx_angle_f1=((h_r_f1*h_r_f1)+(d_tx*d_tx)-(h_t*h_t))/(2.0*h_r_f1*d_tx);
6082                         h_los=sqrt(h_r_f1*h_r_f1+d_x*d_x-2*h_r_f1*d_x*cos_tx_angle_f1);
6083                         h_f=h_los-sqrt(lambda*d_x*(d_tx-d_x)/d_tx);
6084
6085                         while (h_f<h_x)
6086                         {
6087                                 h_r_f1+=1;
6088                                 cos_tx_angle_f1=((h_r_f1*h_r_f1)+(d_tx*d_tx)-(h_t*h_t))/(2.0*h_r_f1*d_tx);
6089                                 h_los=sqrt(h_r_f1*h_r_f1+d_x*d_x-2*h_r_f1*d_x*cos_tx_angle_f1);
6090                                 h_f=h_los-sqrt(lambda*d_x*(d_tx-d_x)/d_tx);
6091                         }
6092
6093                         /* and clear the 60% F1 zone. */
6094
6095                         cos_tx_angle_fpt6=((h_r_fpt6*h_r_fpt6)+(d_tx*d_tx)-(h_t*h_t))/(2.0*h_r_fpt6*d_tx);
6096                         h_los=sqrt(h_r_fpt6*h_r_fpt6+d_x*d_x-2*h_r_fpt6*d_x*cos_tx_angle_fpt6);
6097                         h_f=h_los-fzone_clearance*sqrt(lambda*d_x*(d_tx-d_x)/d_tx);
6098
6099                         while (h_f<h_x)
6100                         {
6101                                 h_r_fpt6+=1;
6102                                 cos_tx_angle_fpt6=((h_r_fpt6*h_r_fpt6)+(d_tx*d_tx)-(h_t*h_t))/(2.0*h_r_fpt6*d_tx);
6103                                 h_los=sqrt(h_r_fpt6*h_r_fpt6+d_x*d_x-2*h_r_fpt6*d_x*cos_tx_angle_fpt6);
6104                                 h_f=h_los-fzone_clearance*sqrt(lambda*d_x*(d_tx-d_x)/d_tx);
6105                         }
6106                 }
6107         }
6108                 
6109         if (h_r>h_r_orig)
6110         {
6111                 if (metric)
6112                         snprintf(string,150,"\nAntenna at %s must be raised to at least %.2f meters AGL\nto clear all obstructions detected by %s.\n",rcvr.name, METERS_PER_FOOT*(h_r-GetElevation(rcvr)-earthradius),splat_name);
6113                 else
6114                         snprintf(string,150,"\nAntenna at %s must be raised to at least %.2f feet AGL\nto clear all obstructions detected by %s.\n",rcvr.name, h_r-GetElevation(rcvr)-earthradius,splat_name);
6115         }
6116
6117         else
6118                 snprintf(string,150,"\nNo obstructions to LOS path due to terrain were detected by %s\n",splat_name);
6119
6120         if (f)
6121         {
6122                 if (h_r_fpt6>h_r_orig)
6123                 {
6124                         if (metric)
6125                                 snprintf(string_fpt6,150,"\nAntenna at %s must be raised to at least %.2f meters AGL\nto clear %.0f%c of the first Fresnel zone.\n",rcvr.name, METERS_PER_FOOT*(h_r_fpt6-GetElevation(rcvr)-earthradius),fzone_clearance*100.0,37);
6126
6127                         else
6128                                 snprintf(string_fpt6,150,"\nAntenna at %s must be raised to at least %.2f feet AGL\nto clear %.0f%c of the first Fresnel zone.\n",rcvr.name, h_r_fpt6-GetElevation(rcvr)-earthradius,fzone_clearance*100.0,37);
6129                 }
6130
6131                 else
6132                         snprintf(string_fpt6,150,"\n%.0f%c of the first Fresnel zone is clear.\n",fzone_clearance*100.0,37);
6133         
6134                 if (h_r_f1>h_r_orig)
6135                 {
6136                         if (metric)
6137                                 snprintf(string_f1,150,"\nAntenna at %s must be raised to at least %.2f meters AGL\nto clear the first Fresnel zone.\n",rcvr.name, METERS_PER_FOOT*(h_r_f1-GetElevation(rcvr)-earthradius));
6138
6139                         else                    
6140                                 snprintf(string_f1,150,"\nAntenna at %s must be raised to at least %.2f feet AGL\nto clear the first Fresnel zone.\n",rcvr.name, h_r_f1-GetElevation(rcvr)-earthradius);
6141
6142                 }
6143
6144                 else
6145                     snprintf(string_f1,150,"\nThe first Fresnel zone is clear.\n");
6146         }
6147
6148         fprintf(outfile,"%s",string);
6149
6150         if (f)
6151         {
6152                 fprintf(outfile,"%s",string_f1);
6153                 fprintf(outfile,"%s",string_fpt6);
6154         }
6155 }
6156
6157 void PathReport(struct site source, struct site destination, char *name, char graph_it)
6158 {
6159         /* This function writes a SPLAT! Path Report (name.txt) to
6160            the filesystem.  If (graph_it == 1), then gnuplot is invoked
6161            to generate an appropriate output file indicating the Longley-Rice
6162            model loss between the source and destination locations.
6163            "filename" is the name assigned to the output file generated
6164            by gnuplot.  The filename extension is used to set gnuplot's
6165            terminal setting and output file type.  If no extension is
6166            found, .png is assumed. */
6167
6168         int     x, y, z, errnum;
6169         char    basename[255], term[30], ext[15], strmode[100],
6170                 report_name[80], block=0;
6171         double  maxloss=-100000.0, minloss=100000.0, loss, haavt,
6172                 angle1, angle2, azimuth, pattern=1.0, patterndB=0.0,
6173                 total_loss=0.0, cos_xmtr_angle, cos_test_angle=0.0,
6174                 source_alt, test_alt, dest_alt, source_alt2, dest_alt2,
6175                 distance, elevation, four_thirds_earth, field_strength,
6176                 free_space_loss=0.0, eirp=0.0, voltage, rxp, dBm,
6177                 power_density;
6178         FILE    *fd=NULL, *fd2=NULL;
6179
6180         sprintf(report_name,"%s-to-%s.txt",source.name,destination.name);
6181
6182         four_thirds_earth=FOUR_THIRDS*EARTHRADIUS;
6183
6184         for (x=0; report_name[x]!=0; x++)
6185                 if (report_name[x]==32 || report_name[x]==17 || report_name[x]==92 || report_name[x]==42 || report_name[x]==47)
6186                         report_name[x]='_';     
6187
6188         fd2=fopen(report_name,"w");
6189
6190         fprintf(fd2,"\n\t\t--==[ %s v%s Path Analysis ]==--\n\n",splat_name,splat_version);
6191         fprintf(fd2,"%s\n\n",dashes);
6192         fprintf(fd2,"Transmitter site: %s\n",source.name);
6193
6194         if (source.lat>=0.0)
6195         {
6196                 fprintf(fd2,"Site location: %.4f North / %.4f West",source.lat, source.lon);
6197                 fprintf(fd2, " (%s N / ", dec2dms(source.lat));
6198         }
6199
6200         else
6201         {
6202
6203                 fprintf(fd2,"Site location: %.4f South / %.4f West",-source.lat, source.lon);
6204                 fprintf(fd2, " (%s S / ", dec2dms(source.lat));
6205         }
6206         
6207         fprintf(fd2, "%s W)\n", dec2dms(source.lon));
6208
6209         if (metric)
6210         {
6211                 fprintf(fd2,"Ground elevation: %.2f meters AMSL\n",METERS_PER_FOOT*GetElevation(source));
6212                 fprintf(fd2,"Antenna height: %.2f meters AGL / %.2f meters AMSL\n",METERS_PER_FOOT*source.alt,METERS_PER_FOOT*(source.alt+GetElevation(source)));
6213         }
6214
6215         else
6216         {
6217                 fprintf(fd2,"Ground elevation: %.2f feet AMSL\n",GetElevation(source));
6218                 fprintf(fd2,"Antenna height: %.2f feet AGL / %.2f feet AMSL\n",source.alt, source.alt+GetElevation(source));
6219         }
6220
6221         haavt=haat(source);
6222
6223         if (haavt>-4999.0)
6224         {
6225                 if (metric)
6226                         fprintf(fd2,"Antenna height above average terrain: %.2f meters\n",METERS_PER_FOOT*haavt);
6227                 else
6228                         fprintf(fd2,"Antenna height above average terrain: %.2f feet\n",haavt);
6229         }
6230
6231         azimuth=Azimuth(source,destination);
6232         angle1=ElevationAngle(source,destination);
6233         angle2=ElevationAngle2(source,destination,earthradius);
6234
6235         if (got_azimuth_pattern || got_elevation_pattern)
6236         {
6237                 x=(int)rint(10.0*(10.0-angle2));
6238
6239                 if (x>=0 && x<=1000)
6240                         pattern=(double)LR.antenna_pattern[(int)rint(azimuth)][x];
6241
6242                 patterndB=20.0*log10(pattern);
6243         }
6244
6245         if (metric)
6246                 fprintf(fd2,"Distance to %s: %.2f kilometers\n",destination.name,KM_PER_MILE*Distance(source,destination));
6247
6248         else
6249                 fprintf(fd2,"Distance to %s: %.2f miles\n",destination.name,Distance(source,destination));
6250
6251         fprintf(fd2,"Azimuth to %s: %.2f degrees\n",destination.name,azimuth);
6252
6253         if (angle1>=0.0)
6254                 fprintf(fd2,"Elevation angle to %s: %+.4f degrees\n",destination.name,angle1);
6255
6256         else
6257                 fprintf(fd2,"Depression angle to %s: %+.4f degrees\n",destination.name,angle1);
6258
6259         if ((angle2-angle1)>0.0001)
6260         {
6261                 if (angle2<0.0)
6262                         fprintf(fd2,"Depression");
6263                 else
6264                         fprintf(fd2,"Elevation");
6265
6266                 fprintf(fd2," angle to the first obstruction: %+.4f degrees\n",angle2);
6267         }
6268
6269         fprintf(fd2,"\n%s\n\n",dashes);
6270
6271         /* Receiver */
6272
6273         fprintf(fd2,"Receiver site: %s\n",destination.name);
6274
6275         if (destination.lat>=0.0)
6276         {
6277                 fprintf(fd2,"Site location: %.4f North / %.4f West",destination.lat, destination.lon);
6278                 fprintf(fd2, " (%s N / ", dec2dms(destination.lat));
6279         }
6280
6281         else
6282         {
6283                 fprintf(fd2,"Site location: %.4f South / %.4f West",-destination.lat, destination.lon);
6284                 fprintf(fd2, " (%s S / ", dec2dms(destination.lat));
6285         }
6286
6287         fprintf(fd2, "%s W)\n", dec2dms(destination.lon));
6288
6289         if (metric)
6290         {
6291                 fprintf(fd2,"Ground elevation: %.2f meters AMSL\n",METERS_PER_FOOT*GetElevation(destination));
6292                 fprintf(fd2,"Antenna height: %.2f meters AGL / %.2f meters AMSL\n",METERS_PER_FOOT*destination.alt, METERS_PER_FOOT*(destination.alt+GetElevation(destination)));
6293         }
6294
6295         else
6296         {
6297                 fprintf(fd2,"Ground elevation: %.2f feet AMSL\n",GetElevation(destination));
6298                 fprintf(fd2,"Antenna height: %.2f feet AGL / %.2f feet AMSL\n",destination.alt, destination.alt+GetElevation(destination));
6299         }
6300
6301         haavt=haat(destination);
6302
6303         if (haavt>-4999.0)
6304         {
6305                 if (metric)
6306                         fprintf(fd2,"Antenna height above average terrain: %.2f meters\n",METERS_PER_FOOT*haavt);
6307                 else
6308                         fprintf(fd2,"Antenna height above average terrain: %.2f feet\n",haavt);
6309         }
6310
6311         if (metric)
6312                 fprintf(fd2,"Distance to %s: %.2f kilometers\n",source.name,KM_PER_MILE*Distance(source,destination));
6313
6314         else
6315                 fprintf(fd2,"Distance to %s: %.2f miles\n",source.name,Distance(source,destination));
6316
6317         azimuth=Azimuth(destination,source);
6318
6319         angle1=ElevationAngle(destination,source);
6320         angle2=ElevationAngle2(destination,source,earthradius);
6321
6322         fprintf(fd2,"Azimuth to %s: %.2f degrees\n",source.name,azimuth);
6323
6324         if (angle1>=0.0)
6325                 fprintf(fd2,"Elevation angle to %s: %+.4f degrees\n",source.name,angle1);
6326
6327         else
6328                 fprintf(fd2,"Depression angle to %s: %+.4f degrees\n",source.name,angle1);
6329
6330         if ((angle2-angle1)>0.0001)
6331         {
6332                 if (angle2<0.0)
6333                         fprintf(fd2,"Depression");
6334                 else
6335                         fprintf(fd2,"Elevation");
6336
6337                 fprintf(fd2," angle to the first obstruction: %+.4f degrees\n",angle2);
6338         }
6339
6340         fprintf(fd2,"\n%s\n\n",dashes);
6341
6342         if (LR.frq_mhz>0.0)
6343         {
6344                 fprintf(fd2,"Longley-Rice path calculation parameters used in this analysis:\n\n");
6345                 fprintf(fd2,"Earth's Dielectric Constant: %.3lf\n",LR.eps_dielect);
6346                 fprintf(fd2,"Earth's Conductivity: %.3lf Siemens/meter\n",LR.sgm_conductivity);
6347                 fprintf(fd2,"Atmospheric Bending Constant (N-units): %.3lf ppm\n",LR.eno_ns_surfref);
6348                 fprintf(fd2,"Frequency: %.3lf MHz\n",LR.frq_mhz);
6349                 fprintf(fd2,"Radio Climate: %d (",LR.radio_climate);
6350
6351                 switch (LR.radio_climate)
6352                 {
6353                         case 1:
6354                         fprintf(fd2,"Equatorial");
6355                         break;
6356
6357                         case 2:
6358                         fprintf(fd2,"Continental Subtropical");
6359                         break;
6360
6361                         case 3:
6362                         fprintf(fd2,"Maritime Subtropical");
6363                         break;
6364
6365                         case 4:
6366                         fprintf(fd2,"Desert");
6367                         break;
6368
6369                         case 5:
6370                         fprintf(fd2,"Continental Temperate");
6371                         break;
6372
6373                         case 6:
6374                         fprintf(fd2,"Martitime Temperate, Over Land");
6375                         break;
6376
6377                         case 7:
6378                         fprintf(fd2,"Maritime Temperate, Over Sea");
6379                         break;
6380
6381                         default:
6382                         fprintf(fd2,"Unknown");
6383                 }
6384
6385                 fprintf(fd2,")\nPolarization: %d (",LR.pol);
6386
6387                 if (LR.pol==0)
6388                         fprintf(fd2,"Horizontal");
6389
6390                 if (LR.pol==1)
6391                         fprintf(fd2,"Vertical");
6392
6393                 fprintf(fd2,")\nFraction of Situations: %.1lf%c\n",LR.conf*100.0,37);
6394                 fprintf(fd2,"Fraction of Time: %.1lf%c\n",LR.rel*100.0,37);
6395
6396                 if (LR.erp!=0.0)
6397                 {
6398                         fprintf(fd2,"Transmitter ERP: ");
6399
6400                         if (LR.erp<1.0)
6401                                 fprintf(fd2,"%.1lf milliwatts",1000.0*LR.erp);
6402
6403                         if (LR.erp>=1.0 && LR.erp<10.0)
6404                                 fprintf(fd2,"%.1lf Watts",LR.erp);
6405
6406                         if (LR.erp>=10.0 && LR.erp<10.0e3)
6407                                 fprintf(fd2,"%.0lf Watts",LR.erp);
6408
6409                         if (LR.erp>=10.0e3)
6410                                 fprintf(fd2,"%.3lf kilowatts",LR.erp/1.0e3);
6411
6412                         dBm=10.0*(log10(LR.erp*1000.0));
6413                         fprintf(fd2," (%+.2f dBm)\n",dBm);
6414
6415                         /* EIRP = ERP + 2.14 dB */
6416
6417                         fprintf(fd2,"Transmitter EIRP: ");
6418
6419                         eirp=LR.erp*1.636816521;
6420
6421                         if (eirp<1.0)
6422                                 fprintf(fd2,"%.1lf milliwatts",1000.0*eirp);
6423
6424                         if (eirp>=1.0 && eirp<10.0)
6425                                 fprintf(fd2,"%.1lf Watts",eirp);
6426
6427                         if (eirp>=10.0 && eirp<10.0e3)
6428                                 fprintf(fd2,"%.0lf Watts",eirp);
6429
6430                         if (eirp>=10.0e3)
6431                                 fprintf(fd2,"%.3lf kilowatts",eirp/1.0e3);
6432
6433                         dBm=10.0*(log10(eirp*1000.0));
6434                         fprintf(fd2," (%+.2f dBm)\n",dBm);
6435                 }
6436
6437                 fprintf(fd2,"\n%s\n\n",dashes);
6438
6439                 fprintf(fd2,"Summary for the link between %s and %s:\n\n",source.name, destination.name);
6440
6441                 if (patterndB!=0.0)
6442                         fprintf(fd2,"%s antenna pattern towards %s: %.3f (%.2f dB)\n", source.name, destination.name, pattern, patterndB);
6443
6444                 ReadPath(source, destination);  /* source=TX, destination=RX */
6445
6446                 /* Copy elevations plus clutter along
6447                    path into the elev[] array. */
6448
6449                 for (x=1; x<path.length-1; x++)
6450                         elev[x+2]=METERS_PER_FOOT*(path.elevation[x]==0.0?path.elevation[x]:(clutter+path.elevation[x]));
6451
6452                 /* Copy ending points without clutter */
6453
6454                 elev[2]=path.elevation[0]*METERS_PER_FOOT;
6455                 elev[path.length+1]=path.elevation[path.length-1]*METERS_PER_FOOT;
6456
6457                 fd=fopen("profile.gp","w");
6458
6459                 azimuth=rint(Azimuth(source,destination));
6460
6461                 for (y=2; y<(path.length-1); y++)  /* path.length-1 avoids LR error */
6462                 {
6463                         distance=5280.0*path.distance[y];
6464                         source_alt=four_thirds_earth+source.alt+path.elevation[0];
6465                         dest_alt=four_thirds_earth+destination.alt+path.elevation[y];
6466                         dest_alt2=dest_alt*dest_alt;
6467                         source_alt2=source_alt*source_alt;
6468
6469                         /* Calculate the cosine of the elevation of
6470                            the receiver as seen by the transmitter. */
6471
6472                         cos_xmtr_angle=((source_alt2)+(distance*distance)-(dest_alt2))/(2.0*source_alt*distance);
6473
6474                         if (got_elevation_pattern)
6475                         {
6476                                 /* If an antenna elevation pattern is available, the
6477                                    following code determines the elevation angle to
6478                                    the first obstruction along the path. */
6479
6480                                 for (x=2, block=0; x<y && block==0; x++)
6481                                 {
6482                                         distance=5280.0*(path.distance[y]-path.distance[x]);
6483                                         test_alt=four_thirds_earth+path.elevation[x];
6484
6485                                         /* Calculate the cosine of the elevation
6486                                            angle of the terrain (test point)
6487                                            as seen by the transmitter. */
6488
6489                                         cos_test_angle=((source_alt2)+(distance*distance)-(test_alt*test_alt))/(2.0*source_alt*distance);
6490
6491                                         /* Compare these two angles to determine if
6492                                            an obstruction exists.  Since we're comparing
6493                                            the cosines of these angles rather than
6494                                            the angles themselves, the sense of the
6495                                            following "if" statement is reversed from
6496                                            what it would be if the angles themselves
6497                                            were compared. */
6498
6499                                         if (cos_xmtr_angle>=cos_test_angle)
6500                                                 block=1;
6501                                 }
6502
6503                                 /* At this point, we have the elevation angle
6504                                    to the first obstruction (if it exists). */
6505                         }
6506
6507                         /* Determine path loss for each point along the
6508                            path using Longley-Rice's point_to_point mode
6509                            starting at x=2 (number_of_points = 1), the
6510                            shortest distance terrain can play a role in
6511                            path loss. */
6512
6513                         elev[0]=y-1;    /* (number of points - 1) */
6514
6515                         /* Distance between elevation samples */
6516
6517                         elev[1]=METERS_PER_MILE*(path.distance[y]-path.distance[y-1]);
6518
6519                         point_to_point(elev, source.alt*METERS_PER_FOOT, 
6520                         destination.alt*METERS_PER_FOOT, LR.eps_dielect,
6521                         LR.sgm_conductivity, LR.eno_ns_surfref, LR.frq_mhz,
6522                         LR.radio_climate, LR.pol, LR.conf, LR.rel, loss,
6523                         strmode, errnum);
6524
6525                         if (block)
6526                                 elevation=((acos(cos_test_angle))/DEG2RAD)-90.0;
6527                         else
6528                                 elevation=((acos(cos_xmtr_angle))/DEG2RAD)-90.0;
6529
6530                         /* Integrate the antenna's radiation
6531                            pattern into the overall path loss. */
6532
6533                         x=(int)rint(10.0*(10.0-elevation));
6534
6535                         if (x>=0 && x<=1000)
6536                         {
6537                                 pattern=(double)LR.antenna_pattern[(int)azimuth][x];
6538
6539                                 if (pattern!=0.0)
6540                                         patterndB=20.0*log10(pattern);
6541                         }
6542
6543                         else
6544                                 patterndB=0.0;
6545
6546                         total_loss=loss-patterndB;
6547
6548                         if (metric)
6549                                 fprintf(fd,"%f\t%f\n",KM_PER_MILE*(path.distance[path.length-1]-path.distance[y]),total_loss);
6550
6551                         else
6552                                 fprintf(fd,"%f\t%f\n",path.distance[path.length-1]-path.distance[y],total_loss);
6553
6554                         if (total_loss>maxloss)
6555                                 maxloss=total_loss;
6556
6557                         if (total_loss<minloss)
6558                                 minloss=total_loss;
6559                 }
6560
6561                 fclose(fd);
6562
6563                 distance=Distance(source,destination);
6564
6565
6566                 if (distance!=0.0)
6567                 {
6568                         free_space_loss=36.6+(20.0*log10(LR.frq_mhz))+(20.0*log10(distance));
6569
6570                         fprintf(fd2,"Free space path loss: %.2f dB\n",free_space_loss);
6571                 }
6572
6573                 fprintf(fd2,"Longley-Rice path loss: %.2f dB\n",loss);
6574
6575                 if (free_space_loss!=0.0)
6576                         fprintf(fd2,"Attenuation due to terrain shielding: %.2f dB\n",loss-free_space_loss);
6577
6578                 if (patterndB!=0.0)
6579                         fprintf(fd2,"Total path loss including %s antenna pattern: %.2f dB\n",source.name,total_loss);
6580
6581                 if (LR.erp!=0.0)
6582                 {
6583                         field_strength=(139.4+(20.0*log10(LR.frq_mhz))-total_loss)+(10.0*log10(LR.erp/1000.0));
6584
6585                         /* dBm is referenced to EIRP */
6586
6587                         rxp=eirp/(pow(10.0,(total_loss/10.0)));
6588                         dBm=10.0*(log10(rxp*1000.0));
6589                         power_density=(eirp/(pow(10.0,(total_loss-free_space_loss)/10.0)));
6590                         /* divide by 4*PI*distance_in_meters squared */
6591                         power_density/=(4.0*PI*distance*distance*2589988.11);
6592
6593                         fprintf(fd2,"Field strength at %s: %.2f dBuV/meter\n", destination.name,field_strength);
6594                         fprintf(fd2,"Signal power level at %s: %+.2f dBm\n",destination.name,dBm);
6595                         fprintf(fd2,"Signal power density at %s: %+.2f dBW per square meter\n",destination.name,10.0*log10(power_density));
6596                         voltage=1.0e6*sqrt(50.0*(eirp/(pow(10.0,(total_loss-2.14)/10.0))));
6597                         fprintf(fd2,"Voltage across 50 ohm dipole at %s: %.2f uV (%.2f dBuV)\n",destination.name,voltage,20.0*log10(voltage));
6598
6599                         voltage=1.0e6*sqrt(75.0*(eirp/(pow(10.0,(total_loss-2.14)/10.0))));
6600                         fprintf(fd2,"Voltage across 75 ohm dipole at %s: %.2f uV (%.2f dBuV)\n",destination.name,voltage,20.0*log10(voltage));
6601                 }
6602
6603                 fprintf(fd2,"Mode of propagation: %s\n",strmode);
6604                 fprintf(fd2,"Longley-Rice model error number: %d",errnum);
6605
6606                 switch (errnum)
6607                 {
6608                         case 0:
6609                                 fprintf(fd2," (No error)\n");
6610                                 break;
6611
6612                         case 1:
6613                                 fprintf(fd2,"\n  Warning: Some parameters are nearly out of range.\n");
6614                                 fprintf(fd2,"  Results should be used with caution.\n");
6615                                 break;
6616
6617                         case 2:
6618                                 fprintf(fd2,"\n  Note: Default parameters have been substituted for impossible ones.\n");
6619                                 break;
6620
6621                         case 3:
6622                                 fprintf(fd2,"\n  Warning: A combination of parameters is out of range.\n");
6623                                 fprintf(fd2,"  Results are probably invalid.\n");
6624                                 break;
6625
6626                         default:
6627                                 fprintf(fd2,"\n  Warning: Some parameters are out of range.\n");
6628                                 fprintf(fd2,"  Results are probably invalid.\n");
6629                 }
6630
6631                 fprintf(fd2,"\n%s\n\n",dashes);
6632         }
6633
6634         fprintf(stdout,"\nPath Loss Report written to: \"%s\"\n",report_name);
6635         fflush(stdout);
6636
6637         ObstructionAnalysis(source, destination, LR.frq_mhz, fd2);
6638
6639         fclose(fd2);
6640
6641         /* Skip plotting the graph if ONLY a path-loss report is needed. */
6642
6643         if (graph_it)
6644         {
6645                 if (name[0]=='.')
6646                 {
6647                         /* Default filename and output file type */
6648
6649                         strncpy(basename,"profile\0",8);
6650                         strncpy(term,"png\0",4);
6651                         strncpy(ext,"png\0",4);
6652                 }
6653
6654                 else
6655                 {
6656                         /* Extract extension and terminal type from "name" */
6657
6658                         ext[0]=0;
6659                         y=strlen(name);
6660                         strncpy(basename,name,254);
6661
6662                         for (x=y-1; x>0 && name[x]!='.'; x--);
6663
6664                         if (x>0)  /* Extension found */
6665                         {
6666                                 for (z=x+1; z<=y && (z-(x+1))<10; z++)
6667                                 {
6668                                         ext[z-(x+1)]=tolower(name[z]);
6669                                         term[z-(x+1)]=name[z];
6670                                 }
6671
6672                                 ext[z-(x+1)]=0;  /* Ensure an ending 0 */
6673                                 term[z-(x+1)]=0;
6674                                 basename[x]=0;
6675                         }
6676                 }
6677
6678                 if (ext[0]==0)  /* No extension -- Default is png */
6679                 {
6680                         strncpy(term,"png\0",4);
6681                         strncpy(ext,"png\0",4);
6682                 }
6683
6684                 /* Either .ps or .postscript may be used
6685                    as an extension for postscript output. */
6686
6687                 if (strncmp(term,"postscript",10)==0)
6688                         strncpy(ext,"ps\0",3);
6689
6690                 else if (strncmp(ext,"ps",2)==0)
6691                                 strncpy(term,"postscript enhanced color\0",26);
6692
6693                 fd=fopen("splat.gp","w");
6694
6695                 fprintf(fd,"set grid\n");
6696                 fprintf(fd,"set yrange [%2.3f to %2.3f]\n", minloss, maxloss);
6697                 fprintf(fd,"set encoding iso_8859_1\n");
6698                 fprintf(fd,"set term %s\n",term);
6699                 fprintf(fd,"set title \"%s Loss Profile Along Path Between %s and %s (%.2f%c azimuth)\"\n",splat_name, destination.name, source.name, Azimuth(destination,source),176);
6700
6701                 if (metric)
6702                         fprintf(fd,"set xlabel \"Distance Between %s and %s (%.2f kilometers)\"\n",destination.name,source.name,KM_PER_MILE*Distance(destination,source));
6703                 else
6704                         fprintf(fd,"set xlabel \"Distance Between %s and %s (%.2f miles)\"\n",destination.name,source.name,Distance(destination,source));
6705
6706                 if (got_azimuth_pattern || got_elevation_pattern)
6707                         fprintf(fd,"set ylabel \"Total Path Loss (including TX antenna pattern) (dB)");
6708                 else
6709                         fprintf(fd,"set ylabel \"Longley-Rice Path Loss (dB)");
6710
6711                 fprintf(fd,"\"\nset output \"%s.%s\"\n",basename,ext);
6712                 fprintf(fd,"plot \"profile.gp\" title \"Path Loss\" with lines\n");
6713
6714                 fclose(fd);
6715                         
6716                 x=system("gnuplot splat.gp");
6717
6718                 if (x!=-1)
6719                 {
6720                         if (gpsav==0)
6721                         {
6722                                 unlink("splat.gp");
6723                                 unlink("profile.gp");
6724                                 unlink("reference.gp");
6725                         }       
6726
6727                         fprintf(stdout,"Path loss plot written to: \"%s.%s\"\n",basename,ext);
6728                         fflush(stdout);
6729                 }
6730
6731                 else
6732                         fprintf(stderr,"\n*** ERROR: Error occurred invoking gnuplot!\n");
6733         }
6734
6735         if (x!=-1 && gpsav==0)
6736                 unlink("profile.gp");
6737 }
6738
6739 void SiteReport(struct site xmtr)
6740 {
6741         char    report_name[80];
6742         double  terrain;
6743         int     x, azi;
6744         FILE    *fd;
6745
6746         sprintf(report_name,"%s-site_report.txt",xmtr.name);
6747
6748         for (x=0; report_name[x]!=0; x++)
6749                 if (report_name[x]==32 || report_name[x]==17 || report_name[x]==92 || report_name[x]==42 || report_name[x]==47)
6750                         report_name[x]='_';     
6751
6752         fd=fopen(report_name,"w");
6753
6754         fprintf(fd,"\n\t--==[ %s v%s Site Analysis Report For: %s ]==--\n\n",splat_name, splat_version, xmtr.name);
6755
6756         fprintf(fd,"%s\n\n",dashes);
6757
6758         if (xmtr.lat>=0.0)
6759         {
6760                 fprintf(fd,"Site location: %.4f North / %.4f West",xmtr.lat, xmtr.lon);
6761                 fprintf(fd, " (%s N / ",dec2dms(xmtr.lat));
6762         }
6763
6764         else
6765         {
6766                 fprintf(fd,"Site location: %.4f South / %.4f West",-xmtr.lat, xmtr.lon);
6767                 fprintf(fd, " (%s S / ",dec2dms(xmtr.lat));
6768         }
6769
6770         fprintf(fd, "%s W)\n",dec2dms(xmtr.lon));
6771
6772         if (metric)
6773         {
6774                 fprintf(fd,"Ground elevation: %.2f meters AMSL\n",METERS_PER_FOOT*GetElevation(xmtr));
6775                 fprintf(fd,"Antenna height: %.2f meters AGL / %.2f meters AMSL\n",METERS_PER_FOOT*xmtr.alt, METERS_PER_FOOT*(xmtr.alt+GetElevation(xmtr)));
6776         }
6777
6778         else
6779         {
6780                 fprintf(fd,"Ground elevation: %.2f feet AMSL\n",GetElevation(xmtr));
6781                 fprintf(fd,"Antenna height: %.2f feet AGL / %.2f feet AMSL\n",xmtr.alt, xmtr.alt+GetElevation(xmtr));
6782         }
6783
6784         terrain=haat(xmtr);
6785
6786         if (terrain>-4999.0)
6787         {
6788                 if (metric)
6789                         fprintf(fd,"Antenna height above average terrain: %.2f meters\n\n",METERS_PER_FOOT*terrain);
6790                 else
6791                         fprintf(fd,"Antenna height above average terrain: %.2f feet\n\n",terrain);
6792
6793                 /* Display the average terrain between 2 and 10 miles
6794                    from the transmitter site at azimuths of 0, 45, 90,
6795                    135, 180, 225, 270, and 315 degrees. */
6796
6797                 for (azi=0; azi<=315; azi+=45)
6798                 {
6799                         fprintf(fd,"Average terrain at %3d degrees azimuth: ",azi);
6800                         terrain=AverageTerrain(xmtr,(double)azi,2.0,10.0);
6801
6802                         if (terrain>-4999.0)
6803                         {
6804                                 if (metric)
6805                                         fprintf(fd,"%.2f meters AMSL\n",METERS_PER_FOOT*terrain);
6806                                 else
6807                                         fprintf(fd,"%.2f feet AMSL\n",terrain);
6808                         }
6809
6810                         else
6811                                 fprintf(fd,"No terrain\n");
6812                 }
6813         }
6814
6815         fprintf(fd,"\n%s\n\n",dashes);
6816         fclose(fd);
6817         fprintf(stdout,"\nSite analysis report written to: \"%s\"\n",report_name);
6818 }
6819
6820 void LoadTopoData(int max_lon, int min_lon, int max_lat, int min_lat)
6821 {
6822         /* This function loads the SDF files required
6823            to cover the limits of the region specified. */ 
6824
6825         int x, y, width, ymin, ymax;
6826
6827         width=ReduceAngle(max_lon-min_lon);
6828
6829         if ((max_lon-min_lon)<=180.0)
6830         {
6831                 for (y=0; y<=width; y++)
6832                         for (x=min_lat; x<=max_lat; x++)
6833                         {
6834                                 ymin=(int)(min_lon+(double)y);
6835
6836                                 while (ymin<0)
6837                                         ymin+=360;
6838
6839                                 while (ymin>=360)
6840                                         ymin-=360;
6841
6842                                 ymax=ymin+1;
6843
6844                                 while (ymax<0)
6845                                         ymax+=360;
6846
6847                                 while (ymax>=360)
6848                                         ymax-=360;
6849
6850                                 if (ippd==3600)
6851                                         snprintf(string,19,"%d:%d:%d:%d-hd",x, x+1, ymin, ymax);
6852                                 else
6853                                         snprintf(string,16,"%d:%d:%d:%d",x, x+1, ymin, ymax);
6854                                 LoadSDF(string);
6855                         }
6856         }
6857
6858         else
6859         {
6860                 for (y=0; y<=width; y++)
6861                         for (x=min_lat; x<=max_lat; x++)
6862                         {
6863                                 ymin=max_lon+y;
6864
6865                                 while (ymin<0)
6866                                         ymin+=360;
6867
6868                                 while (ymin>=360)
6869                                         ymin-=360;
6870                                         
6871                                 ymax=ymin+1;
6872
6873                                 while (ymax<0)
6874                                         ymax+=360;
6875
6876                                 while (ymax>=360)
6877                                         ymax-=360;
6878
6879                                 if (ippd==3600)
6880                                         snprintf(string,19,"%d:%d:%d:%d-hd",x, x+1, ymin, ymax);
6881                                 else
6882                                         snprintf(string,16,"%d:%d:%d:%d",x, x+1, ymin, ymax);
6883                                 LoadSDF(string);
6884                         }
6885         }
6886 }
6887
6888 int LoadANO(char *filename)
6889 {
6890         /* This function reads a SPLAT! alphanumeric output 
6891            file (-ani option) for analysis and/or map generation. */
6892
6893         int     error=0, max_west, min_west, max_north, min_north;
6894         char    string[80], *pointer=NULL, *s=NULL;
6895         double  latitude=0.0, longitude=0.0, azimuth=0.0, elevation=0.0,
6896                 ano=0.0;
6897         FILE    *fd;
6898
6899         fd=fopen(filename,"r");
6900
6901         if (fd!=NULL)
6902         {
6903                 s=fgets(string,78,fd);
6904                 pointer=strchr(string,';');
6905
6906                 if (pointer!=NULL)
6907                         *pointer=0;
6908
6909                 sscanf(string,"%d, %d",&max_west, &min_west);
6910
6911                 s=fgets(string,78,fd);
6912                 pointer=strchr(string,';');
6913
6914                 if (pointer!=NULL)
6915                         *pointer=0;
6916
6917                 sscanf(string,"%d, %d",&max_north, &min_north);
6918
6919                 s=fgets(string,78,fd);
6920                 pointer=strchr(string,';');
6921
6922                 if (pointer!=NULL)
6923                         *pointer=0;
6924
6925                 LoadTopoData(max_west-1, min_west, max_north-1, min_north);
6926
6927                 fprintf(stdout,"\nReading \"%s\"... ",filename);
6928                 fflush(stdout);
6929
6930                 s=fgets(string,78,fd);
6931                 sscanf(string,"%lf, %lf, %lf, %lf, %lf",&latitude, &longitude, &azimuth, &elevation, &ano);
6932
6933                 while (feof(fd)==0)
6934                 {
6935                         if (LR.erp==0.0)
6936                         {
6937                                 /* Path loss */
6938
6939                                 if (contour_threshold==0 || (fabs(ano)<=(double)contour_threshold))
6940                                 {
6941                                         ano=fabs(ano);
6942
6943                                         if (ano>255.0)
6944                                                 ano=255.0;
6945
6946                                         PutSignal(latitude,longitude,((unsigned char)round(ano)));
6947                                 }
6948                         }
6949
6950                         if (LR.erp!=0.0 && dbm!=0)
6951                         {
6952                                 /* signal power level in dBm */
6953
6954                                 if (contour_threshold==0 || (ano>=(double)contour_threshold))
6955                                 {
6956                                         ano=200.0+rint(ano);
6957
6958                                         if (ano<0.0)
6959                                                 ano=0.0;
6960
6961                                         if (ano>255.0)
6962                                                 ano=255.0;
6963
6964                                         PutSignal(latitude,longitude,((unsigned char)round(ano)));
6965                                 }
6966                         }
6967
6968                         if (LR.erp!=0.0 && dbm==0)
6969                         {
6970                                 /* field strength dBuV/m */
6971
6972                                 if (contour_threshold==0 || (ano>=(double)contour_threshold))
6973                                 {
6974                                         ano=100.0+rint(ano);
6975
6976                                         if (ano<0.0)
6977                                                 ano=0.0;
6978
6979                                         if (ano>255.0)
6980                                                 ano=255.0;
6981
6982                                         PutSignal(latitude,longitude,((unsigned char)round(ano)));
6983                                 }
6984                         }
6985
6986                         s=fgets(string,78,fd);
6987                         sscanf(string,"%lf, %lf, %lf, %lf, %lf",&latitude, &longitude, &azimuth, &elevation, &ano);
6988                 }
6989
6990                 fclose(fd);
6991
6992                 fprintf(stdout," Done!\n");
6993                 fflush(stdout);
6994         }
6995
6996         else
6997                 error=1;
6998
6999         return error;
7000 }
7001
7002 void WriteKML(struct site source, struct site destination)
7003 {
7004         int     x, y;
7005         char    block, report_name[80];
7006         double  distance, rx_alt, tx_alt, cos_xmtr_angle,
7007                 azimuth, cos_test_angle, test_alt;
7008         FILE    *fd=NULL;
7009
7010         ReadPath(source,destination);
7011
7012         sprintf(report_name,"%s-to-%s.kml",source.name,destination.name);
7013
7014         for (x=0; report_name[x]!=0; x++)
7015                 if (report_name[x]==32 || report_name[x]==17 || report_name[x]==92 || report_name[x]==42 || report_name[x]==47)
7016                         report_name[x]='_';     
7017
7018         fd=fopen(report_name,"w");
7019
7020         fprintf(fd,"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
7021         fprintf(fd,"<kml xmlns=\"http://earth.google.com/kml/2.0\">\n");
7022         fprintf(fd,"<!-- Generated by %s Version %s -->\n",splat_name, splat_version);
7023         fprintf(fd,"<Folder>\n");
7024         fprintf(fd,"<name>SPLAT! Path</name>\n");
7025         fprintf(fd,"<open>1</open>\n");
7026         fprintf(fd,"<description>Path Between %s and %s</description>\n",source.name,destination.name);
7027
7028         fprintf(fd,"<Placemark>\n");
7029         fprintf(fd,"    <name>%s</name>\n",source.name);
7030         fprintf(fd,"    <description>\n");
7031         fprintf(fd,"       Transmit Site\n");
7032
7033         if (source.lat>=0.0)
7034                 fprintf(fd,"       <BR>%s North</BR>\n",dec2dms(source.lat));
7035         else
7036                 fprintf(fd,"       <BR>%s South</BR>\n",dec2dms(source.lat));
7037
7038         fprintf(fd,"       <BR>%s West</BR>\n",dec2dms(source.lon));
7039
7040         azimuth=Azimuth(source,destination);
7041         distance=Distance(source,destination);
7042
7043         if (metric)
7044                 fprintf(fd,"       <BR>%.2f km",distance*KM_PER_MILE);
7045         else
7046                 fprintf(fd,"       <BR>%.2f miles",distance);
7047
7048         fprintf(fd," to %s</BR>\n       <BR>toward an azimuth of %.2f%c</BR>\n",destination.name,azimuth,176);
7049
7050         fprintf(fd,"    </description>\n");
7051         fprintf(fd,"    <visibility>1</visibility>\n");
7052         fprintf(fd,"    <Style>\n");
7053         fprintf(fd,"      <IconStyle>\n");
7054         fprintf(fd,"        <Icon>\n");
7055         fprintf(fd,"          <href>root://icons/palette-5.png</href>\n");
7056         fprintf(fd,"          <x>224</x>\n");
7057         fprintf(fd,"          <y>224</y>\n");
7058         fprintf(fd,"          <w>32</w>\n");
7059         fprintf(fd,"          <h>32</h>\n");
7060         fprintf(fd,"        </Icon>\n");
7061         fprintf(fd,"      </IconStyle>\n");
7062         fprintf(fd,"    </Style>\n");
7063         fprintf(fd,"    <Point>\n");
7064         fprintf(fd,"      <extrude>1</extrude>\n");
7065         fprintf(fd,"      <altitudeMode>relativeToGround</altitudeMode>\n");
7066         fprintf(fd,"      <coordinates>%f,%f,30</coordinates>\n",(source.lon<180.0?-source.lon:360.0-source.lon),source.lat);
7067         fprintf(fd,"    </Point>\n");
7068         fprintf(fd,"</Placemark>\n");
7069
7070         fprintf(fd,"<Placemark>\n");
7071         fprintf(fd,"    <name>%s</name>\n",destination.name);
7072         fprintf(fd,"    <description>\n");
7073         fprintf(fd,"       Receive Site\n");
7074
7075         if (destination.lat>=0.0)
7076                 fprintf(fd,"       <BR>%s North</BR>\n",dec2dms(destination.lat));
7077         else
7078                 fprintf(fd,"       <BR>%s South</BR>\n",dec2dms(destination.lat));
7079
7080         fprintf(fd,"       <BR>%s West</BR>\n",dec2dms(destination.lon));
7081
7082         if (metric)
7083                 fprintf(fd,"       <BR>%.2f km",distance*KM_PER_MILE);
7084         else
7085                 fprintf(fd,"       <BR>%.2f miles",distance);
7086
7087         fprintf(fd," to %s</BR>\n       <BR>toward an azimuth of %.2f%c</BR>\n",source.name,Azimuth(destination,source),176);
7088
7089         fprintf(fd,"    </description>\n");
7090         fprintf(fd,"    <visibility>1</visibility>\n");
7091         fprintf(fd,"    <Style>\n");
7092         fprintf(fd,"      <IconStyle>\n");
7093         fprintf(fd,"        <Icon>\n");
7094         fprintf(fd,"          <href>root://icons/palette-5.png</href>\n");
7095         fprintf(fd,"          <x>224</x>\n");
7096         fprintf(fd,"          <y>224</y>\n");
7097         fprintf(fd,"          <w>32</w>\n");
7098         fprintf(fd,"          <h>32</h>\n");
7099         fprintf(fd,"        </Icon>\n");
7100         fprintf(fd,"      </IconStyle>\n");
7101         fprintf(fd,"    </Style>\n");
7102         fprintf(fd,"    <Point>\n");
7103         fprintf(fd,"      <extrude>1</extrude>\n");
7104         fprintf(fd,"      <altitudeMode>relativeToGround</altitudeMode>\n");
7105         fprintf(fd,"      <coordinates>%f,%f,30</coordinates>\n",(destination.lon<180.0?-destination.lon:360.0-destination.lon),destination.lat);
7106         fprintf(fd,"    </Point>\n");
7107         fprintf(fd,"</Placemark>\n");
7108
7109         fprintf(fd,"<Placemark>\n");
7110         fprintf(fd,"<name>Point-to-Point Path</name>\n");
7111         fprintf(fd,"  <visibility>1</visibility>\n");
7112         fprintf(fd,"  <open>0</open>\n");
7113         fprintf(fd,"  <Style>\n");
7114         fprintf(fd,"    <LineStyle>\n");
7115         fprintf(fd,"      <color>7fffffff</color>\n");
7116         fprintf(fd,"    </LineStyle>\n");
7117         fprintf(fd,"    <PolyStyle>\n");
7118         fprintf(fd,"       <color>7fffffff</color>\n");
7119         fprintf(fd,"    </PolyStyle>\n");
7120         fprintf(fd,"  </Style>\n");
7121         fprintf(fd,"  <LineString>\n");
7122         fprintf(fd,"    <extrude>1</extrude>\n");
7123         fprintf(fd,"    <tessellate>1</tessellate>\n");
7124         fprintf(fd,"    <altitudeMode>relativeToGround</altitudeMode>\n");
7125         fprintf(fd,"    <coordinates>\n");
7126
7127         for (x=0; x<path.length; x++)
7128                 fprintf(fd,"      %f,%f,5\n",(path.lon[x]<180.0?-path.lon[x]:360.0-path.lon[x]),path.lat[x]);
7129
7130         fprintf(fd,"    </coordinates>\n");
7131         fprintf(fd,"   </LineString>\n");
7132         fprintf(fd,"</Placemark>\n");
7133
7134         fprintf(fd,"<Placemark>\n");
7135         fprintf(fd,"<name>Line-of-Sight Path</name>\n");
7136         fprintf(fd,"  <visibility>1</visibility>\n");
7137         fprintf(fd,"  <open>0</open>\n");
7138         fprintf(fd,"  <Style>\n");
7139         fprintf(fd,"    <LineStyle>\n");
7140         fprintf(fd,"      <color>ff00ff00</color>\n");
7141         fprintf(fd,"    </LineStyle>\n");
7142         fprintf(fd,"    <PolyStyle>\n");
7143         fprintf(fd,"       <color>7f00ff00</color>\n");
7144         fprintf(fd,"    </PolyStyle>\n");
7145         fprintf(fd,"  </Style>\n");
7146         fprintf(fd,"  <LineString>\n");
7147         fprintf(fd,"    <extrude>1</extrude>\n");
7148         fprintf(fd,"    <tessellate>1</tessellate>\n");
7149         fprintf(fd,"    <altitudeMode>relativeToGround</altitudeMode>\n");
7150         fprintf(fd,"    <coordinates>\n");
7151
7152         /* Walk across the "path", indentifying obstructions along the way */
7153
7154         for (y=0; y<path.length; y++)
7155         {
7156                 distance=5280.0*path.distance[y];
7157                 tx_alt=earthradius+source.alt+path.elevation[0];
7158                 rx_alt=earthradius+destination.alt+path.elevation[y];
7159
7160                 /* Calculate the cosine of the elevation of the
7161                    transmitter as seen at the temp rx point. */
7162
7163                 cos_xmtr_angle=((rx_alt*rx_alt)+(distance*distance)-(tx_alt*tx_alt))/(2.0*rx_alt*distance);
7164
7165                 for (x=y, block=0; x>=0 && block==0; x--)
7166                 {
7167                         distance=5280.0*(path.distance[y]-path.distance[x]);
7168                         test_alt=earthradius+path.elevation[x];
7169
7170                         cos_test_angle=((rx_alt*rx_alt)+(distance*distance)-(test_alt*test_alt))/(2.0*rx_alt*distance);
7171
7172                         /* Compare these two angles to determine if
7173                            an obstruction exists.  Since we're comparing
7174                            the cosines of these angles rather than
7175                            the angles themselves, the following "if"
7176                            statement is reversed from what it would
7177                            be if the actual angles were compared. */
7178
7179                         if (cos_xmtr_angle>=cos_test_angle)
7180                                 block=1;
7181                 }
7182
7183                 if (block)
7184                         fprintf(fd,"      %f,%f,-30\n",(path.lon[y]<180.0?-path.lon[y]:360.0-path.lon[y]),path.lat[y]);
7185                 else
7186                         fprintf(fd,"      %f,%f,5\n",(path.lon[y]<180.0?-path.lon[y]:360.0-path.lon[y]),path.lat[y]);
7187         }
7188
7189         fprintf(fd,"    </coordinates>\n");
7190         fprintf(fd,"  </LineString>\n");
7191         fprintf(fd,"</Placemark>\n");
7192
7193         fprintf(fd,"    <LookAt>\n");
7194         fprintf(fd,"      <longitude>%f</longitude>\n",(source.lon<180.0?-source.lon:360.0-source.lon));
7195         fprintf(fd,"      <latitude>%f</latitude>\n",source.lat);
7196         fprintf(fd,"      <range>300.0</range>\n");
7197         fprintf(fd,"      <tilt>45.0</tilt>\n");
7198         fprintf(fd,"      <heading>%f</heading>\n",azimuth);
7199         fprintf(fd,"    </LookAt>\n");
7200
7201         fprintf(fd,"</Folder>\n");
7202         fprintf(fd,"</kml>\n");
7203
7204         fclose(fd);
7205
7206         fprintf(stdout, "\nKML file written to: \"%s\"",report_name);
7207
7208         fflush(stdout);
7209 }
7210
7211 int main(int argc, char *argv[])
7212 {
7213         int             x, y, z=0, min_lat, min_lon, max_lat, max_lon,
7214                         rxlat, rxlon, txlat, txlon, west_min, west_max,
7215                         north_min, north_max;
7216
7217         unsigned char   coverage=0, LRmap=0, terrain_plot=0,
7218                         elevation_plot=0, height_plot=0, map=0,
7219                         longley_plot=0, cities=0, bfs=0, txsites=0,
7220                         norm=0, topomap=0, geo=0, kml=0, pt2pt_mode=0,
7221                         area_mode=0, max_txsites, ngs=0, nolospath=0,
7222                         nositereports=0, fresnel_plot=1;
7223  
7224         char            mapfile[255], header[80], city_file[5][255], 
7225                         elevation_file[255], height_file[255], 
7226                         longley_file[255], terrain_file[255],
7227                         string[255], rxfile[255], *env=NULL,
7228                         txfile[255], boundary_file[5][255],
7229                         udt_file[255], rxsite=0, ani_filename[255],
7230                         ano_filename[255], ext[20], *s=NULL;
7231
7232         double          altitude=0.0, altitudeLR=0.0, tx_range=0.0,
7233                         rx_range=0.0, deg_range=0.0, deg_limit=0.0,
7234                         deg_range_lon, er_mult;
7235
7236         struct          site tx_site[32], rx_site;
7237
7238         FILE            *fd;
7239
7240         strncpy(splat_version,"1.3.0\0",6);
7241
7242         if (HD_MODE==1)
7243                 strncpy(splat_name,"SPLAT! HD\0",10);
7244         else
7245                 strncpy(splat_name,"SPLAT!\0",7);
7246
7247         strncpy(dashes,"---------------------------------------------------------------------------\0",76);
7248
7249         if (argc==1)
7250         {
7251                 fprintf(stdout,"\n\t\t --==[ %s v%s Available Options... ]==--\n\n",splat_name, splat_version);
7252
7253                 fprintf(stdout,"       -t txsite(s).qth (max of 4 with -c, max of 30 with -L)\n");
7254                 fprintf(stdout,"       -r rxsite.qth\n");
7255                 fprintf(stdout,"       -c plot coverage of TX(s) with an RX antenna at X feet/meters AGL\n");
7256                 fprintf(stdout,"       -L plot path loss map of TX based on an RX at X feet/meters AGL\n");
7257                 fprintf(stdout,"       -s filename(s) of city/site file(s) to import (5 max)\n");
7258                 fprintf(stdout,"       -b filename(s) of cartographic boundary file(s) to import (5 max)\n");
7259                 fprintf(stdout,"       -p filename of terrain profile graph to plot\n");
7260                 fprintf(stdout,"       -e filename of terrain elevation graph to plot\n");
7261                 fprintf(stdout,"       -h filename of terrain height graph to plot\n");
7262                 fprintf(stdout,"       -H filename of normalized terrain height graph to plot\n");
7263                 fprintf(stdout,"       -l filename of path loss graph to plot\n");
7264                 fprintf(stdout,"       -o filename of topographic map to generate (.ppm)\n");
7265                 fprintf(stdout,"       -u filename of user-defined terrain file to import\n");
7266                 fprintf(stdout,"       -d sdf file directory path (overrides path in ~/.splat_path file)\n");
7267                 fprintf(stdout,"       -m earth radius multiplier\n");
7268                 fprintf(stdout,"       -n do not plot LOS paths in .ppm maps\n");
7269                 fprintf(stdout,"       -N do not produce unnecessary site or obstruction reports\n");   
7270                 fprintf(stdout,"       -f frequency for Fresnel zone calculation (MHz)\n");
7271                 fprintf(stdout,"       -R modify default range for -c or -L (miles/kilometers)\n");
7272                 fprintf(stdout,"      -db threshold beyond which contours will not be displayed\n");
7273                 fprintf(stdout,"      -nf do not plot Fresnel zones in height plots\n");
7274                 fprintf(stdout,"      -fz Fresnel zone clearance percentage (default = 60)\n");
7275                 fprintf(stdout,"      -gc ground clutter height (feet/meters)\n");
7276                 fprintf(stdout,"     -ngs display greyscale topography as white in .ppm files\n");      
7277                 fprintf(stdout,"     -erp override ERP in .lrp file (Watts)\n");
7278                 fprintf(stdout,"     -ano name of alphanumeric output file\n");
7279                 fprintf(stdout,"     -ani name of alphanumeric input file\n");
7280                 fprintf(stdout,"     -udt name of user defined terrain input file\n");
7281                 fprintf(stdout,"     -kml generate Google Earth (.kml) compatible output\n");
7282                 fprintf(stdout,"     -geo generate an Xastir .geo georeference file (with .ppm output)\n");
7283                 fprintf(stdout,"     -dbm plot signal power level contours rather than field strength\n");
7284                 fprintf(stdout,"   -gpsav preserve gnuplot temporary working files after SPLAT! execution\n");
7285                 fprintf(stdout,"  -metric employ metric rather than imperial units for all user I/O\n\n");
7286                 fprintf(stdout,"If that flew by too fast, consider piping the output through 'less':\n");
7287
7288                 if (HD_MODE==0)
7289                         fprintf(stdout,"\n\tsplat | less\n\n");
7290                 else
7291                         fprintf(stdout,"\n\tsplat-hd | less\n\n");
7292
7293                 fprintf(stdout,"Type 'man splat', or see the documentation for more details.\n\n");
7294
7295                 y=(int)sqrt((int)MAXPAGES);
7296
7297                 fprintf(stdout,"This compilation of %s supports analysis over a region of\n%d square ",splat_name,y);
7298
7299                 if (y==1)
7300
7301                         fprintf(stdout,"degree");
7302                 else
7303                         fprintf(stdout,"degrees");
7304
7305                 fprintf(stdout," of terrain.\n\n");
7306                 fflush(stdout);
7307
7308                 return 1;
7309         }
7310
7311         y=argc-1;
7312
7313         kml=0;
7314         geo=0;
7315         dbm=0;
7316         gpsav=0;
7317         metric=0;
7318         rxfile[0]=0;
7319         txfile[0]=0;
7320         string[0]=0;
7321         mapfile[0]=0;
7322         clutter=0.0;
7323         forced_erp=-1.0;
7324         forced_freq=0.0;
7325         elevation_file[0]=0;
7326         terrain_file[0]=0;
7327         sdf_path[0]=0;
7328         udt_file[0]=0;
7329         path.length=0;
7330         max_txsites=30;
7331         fzone_clearance=0.6;
7332         contour_threshold=0;
7333         rx_site.lat=91.0;
7334         rx_site.lon=361.0;
7335         longley_file[0]=0;
7336         ano_filename[0]=0;
7337         ani_filename[0]=0;
7338         earthradius=EARTHRADIUS;
7339
7340         ippd=IPPD;              /* pixels per degree (integer) */
7341         ppd=(double)ippd;       /* pixels per degree (double)  */
7342         dpp=1.0/ppd;            /* degrees per pixel */
7343         mpi=ippd-1;             /* maximum pixel index per degree */
7344
7345         sprintf(header,"\n\t\t--==[ Welcome To %s v%s ]==--\n\n", splat_name, splat_version);
7346
7347         for (x=0; x<4; x++)
7348         {
7349                 tx_site[x].lat=91.0;
7350                 tx_site[x].lon=361.0;
7351         }
7352
7353         for (x=0; x<MAXPAGES; x++)
7354         {
7355                 dem[x].min_el=32768;
7356                 dem[x].max_el=-32768;
7357                 dem[x].min_north=90;
7358                 dem[x].max_north=-90;
7359                 dem[x].min_west=360;
7360                 dem[x].max_west=-1;
7361         }
7362
7363         /* Scan for command line arguments */
7364
7365         for (x=1; x<=y; x++)
7366         {
7367                 if (strcmp(argv[x],"-R")==0)
7368                 {
7369                         z=x+1;
7370
7371                         if (z<=y && argv[z][0] && argv[z][0]!='-')
7372                         {
7373                                 sscanf(argv[z],"%lf",&max_range);
7374
7375                                 if (max_range<0.0)
7376                                         max_range=0.0;
7377
7378                                 if (max_range>1000.0)
7379                                         max_range=1000.0;
7380                         }                        
7381                 }
7382
7383                 if (strcmp(argv[x],"-m")==0)
7384                 {
7385                         z=x+1;
7386
7387                         if (z<=y && argv[z][0] && argv[z][0]!='-')
7388                         {
7389                                 sscanf(argv[z],"%lf",&er_mult);
7390
7391                                 if (er_mult<0.1)
7392                                         er_mult=1.0;
7393
7394                                 if (er_mult>1.0e6)
7395                                         er_mult=1.0e6;
7396
7397                                 earthradius*=er_mult;
7398                         }                        
7399                 }
7400
7401                 if (strcmp(argv[x],"-gc")==0)
7402                 {
7403                         z=x+1;
7404
7405                         if (z<=y && argv[z][0] && argv[z][0]!='-')
7406                         {
7407                                 sscanf(argv[z],"%lf",&clutter);
7408
7409                                 if (clutter<0.0)
7410                                         clutter=0.0;
7411                         }                        
7412                 }
7413
7414                 if (strcmp(argv[x],"-fz")==0)
7415                 {
7416                         z=x+1;
7417
7418                         if (z<=y && argv[z][0] && argv[z][0]!='-')
7419                         {
7420                                 sscanf(argv[z],"%lf",&fzone_clearance);
7421
7422                                 if (fzone_clearance<0.0 || fzone_clearance>100.0)
7423                                         fzone_clearance=60.0;
7424
7425                                 fzone_clearance/=100.0;
7426                         }
7427                 }
7428
7429                 if (strcmp(argv[x],"-o")==0)
7430                 {
7431                         z=x+1;
7432
7433                         if (z<=y && argv[z][0] && argv[z][0]!='-')
7434                                 strncpy(mapfile,argv[z],253);
7435                         map=1;
7436                 }
7437
7438                 if (strcmp(argv[x],"-udt")==0)
7439                 {
7440                         z=x+1;
7441
7442                         if (z<=y && argv[z][0] && argv[z][0]!='-')
7443                                 strncpy(udt_file,argv[z],253);
7444                 }
7445
7446                 if (strcmp(argv[x],"-c")==0)
7447                 {
7448                         z=x+1;
7449
7450                         if (z<=y && argv[z][0] && argv[z][0]!='-')
7451                         {
7452                                 sscanf(argv[z],"%lf",&altitude);
7453                                 map=1;
7454                                 coverage=1;
7455                                 area_mode=1;
7456                                 max_txsites=4;
7457                         }
7458                 }
7459
7460                 if (strcmp(argv[x],"-db")==0 || strcmp(argv[x],"-dB")==0)
7461                 {
7462                         z=x+1;
7463
7464                         if (z<=y && argv[z][0]) /* A minus argument is legal here */
7465                                 sscanf(argv[z],"%d",&contour_threshold);
7466                 }
7467
7468                 if (strcmp(argv[x],"-p")==0)
7469                 { 
7470                         z=x+1;
7471
7472                         if (z<=y && argv[z][0] && argv[z][0]!='-')
7473                         {
7474                                 strncpy(terrain_file,argv[z],253);
7475                                 terrain_plot=1;
7476                                 pt2pt_mode=1;
7477                         }
7478                 }
7479
7480                 if (strcmp(argv[x],"-e")==0)
7481                 {
7482                         z=x+1;
7483
7484                         if (z<=y && argv[z][0] && argv[z][0]!='-')
7485                         {
7486                                 strncpy(elevation_file,argv[z],253);
7487                                 elevation_plot=1;
7488                                 pt2pt_mode=1;
7489                         }
7490                 }
7491
7492                 if (strcmp(argv[x],"-h")==0 || strcmp(argv[x],"-H")==0)
7493                 {
7494                         z=x+1;
7495
7496                         if (z<=y && argv[z][0] && argv[z][0]!='-')
7497                         {
7498                                 strncpy(height_file,argv[z],253);
7499                                 height_plot=1;
7500                                 pt2pt_mode=1;
7501                         }
7502
7503                         if (strcmp(argv[x],"-H")==0)
7504                                 norm=1;
7505                         else
7506                                 norm=0;
7507                 }
7508
7509                 if (strcmp(argv[x],"-metric")==0)
7510                         metric=1;
7511
7512                 if (strcmp(argv[x],"-gpsav")==0)
7513                         gpsav=1;
7514
7515                 if (strcmp(argv[x],"-geo")==0)
7516                         geo=1;
7517
7518                 if (strcmp(argv[x],"-kml")==0)
7519                         kml=1;
7520
7521                 if (strcmp(argv[x],"-nf")==0)
7522                         fresnel_plot=0;
7523
7524                 if (strcmp(argv[x],"-ngs")==0)
7525                         ngs=1;
7526
7527                 if (strcmp(argv[x],"-n")==0)
7528                         nolospath=1;
7529
7530                 if (strcmp(argv[x],"-dbm")==0)
7531                         dbm=1;
7532
7533                 if (strcmp(argv[x],"-N")==0)
7534                 {
7535                         nolospath=1;
7536                         nositereports=1;
7537                 }
7538
7539                 if (strcmp(argv[x],"-d")==0)
7540                 {
7541                         z=x+1;
7542
7543                         if (z<=y && argv[z][0] && argv[z][0]!='-')
7544                                 strncpy(sdf_path,argv[z],253);
7545                 }
7546
7547                 if (strcmp(argv[x],"-t")==0)
7548                 {
7549                         /* Read Transmitter Location */
7550
7551                         z=x+1;
7552
7553                         while (z<=y && argv[z][0] && argv[z][0]!='-' && txsites<30)
7554                         {
7555                                 strncpy(txfile,argv[z],253);
7556                                 tx_site[txsites]=LoadQTH(txfile);
7557                                 txsites++;
7558                                 z++;
7559                         }
7560
7561                         z--;
7562                 }
7563
7564                 if (strcmp(argv[x],"-L")==0)
7565                 {
7566                         z=x+1;
7567
7568                         if (z<=y && argv[z][0] && argv[z][0]!='-')
7569                         {
7570                                 sscanf(argv[z],"%lf",&altitudeLR);
7571                                 map=1;
7572                                 LRmap=1;
7573                                 area_mode=1;
7574
7575                                 if (coverage)
7576                                         fprintf(stdout,"c and L are exclusive options, ignoring L.\n");
7577                         }
7578                 }
7579
7580                 if (strcmp(argv[x],"-l")==0)
7581                 {
7582                         z=x+1;
7583
7584                         if (z<=y && argv[z][0] && argv[z][0]!='-')
7585                         {
7586                                 strncpy(longley_file,argv[z],253);
7587                                 longley_plot=1;
7588                                 pt2pt_mode=1;
7589                         }
7590                 }
7591
7592                 if (strcmp(argv[x],"-r")==0)
7593                 {
7594                         /* Read Receiver Location */
7595
7596                         z=x+1;
7597
7598                         if (z<=y && argv[z][0] && argv[z][0]!='-')
7599                         {
7600                                 strncpy(rxfile,argv[z],253);
7601                                 rx_site=LoadQTH(rxfile);
7602                                 rxsite=1;
7603                                 pt2pt_mode=1;
7604                         }
7605                 }
7606
7607                 if (strcmp(argv[x],"-s")==0)
7608                 {
7609                         /* Read city file(s) */
7610
7611                         z=x+1;
7612
7613                         while (z<=y && argv[z][0] && argv[z][0]!='-' && cities<5)
7614                         {
7615                                 strncpy(city_file[cities],argv[z],253);
7616                                 cities++;
7617                                 z++;
7618                         }
7619
7620                         z--;
7621                 }
7622
7623                 if (strcmp(argv[x],"-b")==0)
7624                 {
7625                         /* Read Boundary File(s) */
7626
7627                         z=x+1;
7628
7629                         while (z<=y && argv[z][0] && argv[z][0]!='-' && bfs<5)
7630                         {
7631                                 strncpy(boundary_file[bfs],argv[z],253);
7632                                 bfs++;
7633                                 z++;
7634                         }
7635
7636                         z--;
7637                 }
7638                 
7639                 if (strcmp(argv[x],"-f")==0)
7640                 {
7641                         z=x+1;
7642
7643                         if (z<=y && argv[z][0] && argv[z][0]!='-')
7644                         {
7645                                 sscanf(argv[z],"%lf",&forced_freq);
7646
7647                                 if (forced_freq<20.0)
7648                                         forced_freq=0.0;
7649
7650                                 if (forced_freq>20.0e3)
7651                                         forced_freq=20.0e3;
7652                         }
7653                 }
7654
7655                 if (strcmp(argv[x],"-erp")==0)
7656                 {
7657                         z=x+1;
7658
7659                         if (z<=y && argv[z][0] && argv[z][0]!='-')
7660                         {
7661                                 sscanf(argv[z],"%lf",&forced_erp);
7662
7663                                 if (forced_erp<0.0)
7664                                         forced_erp=-1.0;
7665                         }                        
7666                 }
7667
7668                 if (strcmp(argv[x],"-ano")==0)
7669                 {
7670                         z=x+1;
7671
7672                         if (z<=y && argv[z][0] && argv[z][0]!='-')
7673                                 strncpy(ano_filename,argv[z],253);
7674                 }
7675
7676                 if (strcmp(argv[x],"-ani")==0)
7677                 {
7678                         z=x+1;
7679
7680                         if (z<=y && argv[z][0] && argv[z][0]!='-')
7681                                 strncpy(ani_filename,argv[z],253);
7682                 }
7683         }
7684
7685         /* Perform some error checking on the arguments
7686            and switches parsed from the command-line.
7687            If an error is encountered, print a message
7688            and exit gracefully. */
7689
7690         if (txsites==0)
7691         {
7692                 fprintf(stderr,"\n%c*** ERROR: No transmitter site(s) specified!\n\n",7);
7693                 exit (-1);
7694         }
7695
7696         for (x=0, y=0; x<txsites; x++)
7697         {
7698                 if (tx_site[x].lat==91.0 && tx_site[x].lon==361.0)
7699                 {
7700                         fprintf(stderr,"\n*** ERROR: Transmitter site #%d not found!",x+1);
7701                         y++;
7702                 }
7703         }
7704
7705         if (y)
7706         {
7707                 fprintf(stderr,"%c\n\n",7);
7708                 exit (-1);
7709         }
7710
7711         if ((coverage+LRmap+ani_filename[0])==0 && rx_site.lat==91.0 && rx_site.lon==361.0)
7712         {
7713                 if (max_range!=0.0 && txsites!=0)
7714                 {
7715                         /* Plot topographic map of radius "max_range" */
7716
7717                         map=0;
7718                         topomap=1;
7719                 }
7720
7721                 else
7722                 {
7723                         fprintf(stderr,"\n%c*** ERROR: No receiver site found or specified!\n\n",7);
7724                         exit (-1);
7725                 }
7726         }
7727
7728         /* No major errors were detected.  Whew!  :-) */
7729
7730         /* Adjust input parameters if -metric option is used */
7731
7732         if (metric)
7733         {
7734                 altitudeLR/=METERS_PER_FOOT;    /* meters --> feet */
7735                 max_range/=KM_PER_MILE;         /* kilometers --> miles */
7736                 altitude/=METERS_PER_FOOT;      /* meters --> feet */
7737                 clutter/=METERS_PER_FOOT;       /* meters --> feet */
7738         }
7739
7740         /* If no SDF path was specified on the command line (-d), check
7741            for a path specified in the $HOME/.splat_path file.  If the
7742            file is not found, then sdf_path[] remains NULL, and the
7743            current working directory is assumed to contain the SDF
7744            files. */
7745
7746         if (sdf_path[0]==0)
7747         {
7748                 env=getenv("HOME");
7749                 snprintf(string,253,"%s/.splat_path",env);
7750                 fd=fopen(string,"r");
7751
7752                 if (fd!=NULL)
7753                 {
7754                         s=fgets(string,253,fd);
7755
7756                         /* Remove <CR> and/or <LF> from string */
7757
7758                         for (x=0; string[x]!=13 && string[x]!=10 && string[x]!=0 && x<253; x++);
7759                         string[x]=0;
7760
7761                         strncpy(sdf_path,string,253);
7762
7763                         fclose(fd);
7764                 }
7765         }
7766
7767         /* Ensure a trailing '/' is present in sdf_path */
7768
7769         if (sdf_path[0])
7770         {
7771                 x=strlen(sdf_path);
7772
7773                 if (sdf_path[x-1]!='/' && x!=0)
7774                 {
7775                         sdf_path[x]='/';
7776                         sdf_path[x+1]=0;
7777                 }
7778         }
7779
7780         fprintf(stdout,"%s",header);
7781         fflush(stdout);
7782
7783         if (ani_filename[0])
7784         {
7785                 ReadLRParm(tx_site[0],0); /* Get ERP status */
7786                 y=LoadANO(ani_filename);
7787
7788                 for (x=0; x<txsites && x<max_txsites; x++)
7789                         PlaceMarker(tx_site[x]);
7790
7791                 if (rxsite)
7792                         PlaceMarker(rx_site);
7793
7794                 if (bfs)
7795                 {
7796                         for (x=0; x<bfs; x++)
7797                                 LoadBoundaries(boundary_file[x]);
7798
7799                         fprintf(stdout,"\n");
7800                         fflush(stdout);
7801                 }
7802
7803                 if (cities)
7804                 {
7805                         for (x=0; x<cities; x++)
7806                                 LoadCities(city_file[x]);
7807
7808                         fprintf(stdout,"\n");
7809                         fflush(stdout);
7810                 }
7811
7812                 if (LR.erp==0.0)
7813                         WritePPMLR(mapfile,geo,kml,ngs,tx_site,txsites);
7814                 else
7815                 {
7816                         if (dbm)
7817                                 WritePPMDBM(mapfile,geo,kml,ngs,tx_site,txsites);
7818                         else
7819                                 WritePPMSS(mapfile,geo,kml,ngs,tx_site,txsites);
7820                 }
7821
7822                 exit(0);
7823         }
7824
7825         x=0;
7826         y=0;
7827
7828         min_lat=90;
7829         max_lat=-90;
7830
7831         min_lon=(int)floor(tx_site[0].lon);
7832         max_lon=(int)floor(tx_site[0].lon);
7833
7834         for (y=0, z=0; z<txsites && z<max_txsites; z++)
7835         {
7836                 txlat=(int)floor(tx_site[z].lat);
7837                 txlon=(int)floor(tx_site[z].lon);
7838
7839                 if (txlat<min_lat)
7840                         min_lat=txlat;
7841
7842                 if (txlat>max_lat)
7843                         max_lat=txlat;
7844
7845                 if (LonDiff(txlon,min_lon)<0.0)
7846                         min_lon=txlon;
7847
7848                 if (LonDiff(txlon,max_lon)>=0.0)
7849                         max_lon=txlon;
7850         }
7851
7852         if (rxsite)
7853         {
7854                 rxlat=(int)floor(rx_site.lat);
7855                 rxlon=(int)floor(rx_site.lon);
7856
7857                 if (rxlat<min_lat)
7858                         min_lat=rxlat;
7859
7860                 if (rxlat>max_lat)
7861                         max_lat=rxlat;
7862
7863                 if (LonDiff(rxlon,min_lon)<0.0)
7864                         min_lon=rxlon;
7865
7866                 if (LonDiff(rxlon,max_lon)>=0.0)
7867                         max_lon=rxlon;
7868         }
7869
7870         /* Load the required SDF files */ 
7871
7872         LoadTopoData(max_lon, min_lon, max_lat, min_lat);
7873
7874         if (area_mode || topomap)
7875         {
7876                 for (z=0; z<txsites && z<max_txsites; z++)
7877                 {
7878                         /* "Ball park" estimates used to load any additional
7879                            SDF files required to conduct this analysis. */
7880
7881                         tx_range=sqrt(1.5*(tx_site[z].alt+GetElevation(tx_site[z])));
7882
7883                         if (LRmap)
7884                                 rx_range=sqrt(1.5*altitudeLR);
7885                         else
7886                                 rx_range=sqrt(1.5*altitude);
7887
7888                         /* deg_range determines the maximum
7889                            amount of topo data we read */
7890
7891                         deg_range=(tx_range+rx_range)/57.0;
7892
7893                         /* max_range regulates the size of the
7894                            analysis.  A small, non-zero amount can
7895                            be used to shrink the size of the analysis
7896                            and limit the amount of topo data read by
7897                            SPLAT!  A large number will increase the
7898                            width of the analysis and the size of
7899                            the map. */
7900
7901                         if (max_range==0.0)
7902                                 max_range=tx_range+rx_range;
7903
7904                         deg_range=max_range/57.0;
7905
7906                         /* Prevent the demand for a really wide coverage
7907                            from allocating more "pages" than are available
7908                            in memory. */
7909
7910                         switch (MAXPAGES)
7911                         {
7912                                 case 1: deg_limit=0.125;
7913                                         break;
7914
7915                                 case 2: deg_limit=0.25;
7916                                         break;
7917
7918                                 case 4: deg_limit=0.5;
7919                                         break;
7920
7921                                 case 9: deg_limit=1.0;
7922                                         break;
7923
7924                                 case 16: deg_limit=1.5;  /* WAS 2.0 */
7925                                         break;
7926
7927                                 case 25: deg_limit=2.0;  /* WAS 3.0 */
7928                                         break;
7929
7930                                 case 36: deg_limit=2.5;  /* New! */
7931                                         break;
7932
7933                                 case 49: deg_limit=3.0;  /* New! */
7934                                         break;
7935
7936                                 case 64: deg_limit=3.5;  /* New! */
7937                                         break;
7938                         }
7939
7940                         if (fabs(tx_site[z].lat)<70.0)
7941                                 deg_range_lon=deg_range/cos(DEG2RAD*tx_site[z].lat);
7942                         else
7943                                 deg_range_lon=deg_range/cos(DEG2RAD*70.0);
7944
7945                         /* Correct for squares in degrees not being square in miles */  
7946
7947                         if (deg_range>deg_limit)
7948                                 deg_range=deg_limit;
7949
7950                         if (deg_range_lon>deg_limit)
7951                                 deg_range_lon=deg_limit;
7952
7953                         north_min=(int)floor(tx_site[z].lat-deg_range);
7954                         north_max=(int)floor(tx_site[z].lat+deg_range);
7955
7956                         west_min=(int)floor(tx_site[z].lon-deg_range_lon);
7957
7958                         while (west_min<0)
7959                                 west_min+=360;
7960
7961                         while (west_min>=360)
7962                                 west_min-=360;
7963
7964                         west_max=(int)floor(tx_site[z].lon+deg_range_lon);
7965
7966                         while (west_max<0)
7967                                 west_max+=360;
7968
7969                         while (west_max>=360)
7970                                 west_max-=360;
7971
7972                         if (north_min<min_lat)
7973                                 min_lat=north_min;
7974
7975                         if (north_max>max_lat)
7976                                 max_lat=north_max;
7977
7978                         if (LonDiff(west_min,min_lon)<0.0)
7979                                 min_lon=west_min;
7980
7981                         if (LonDiff(west_max,max_lon)>=0.0)
7982                                 max_lon=west_max;
7983                 }
7984
7985                 /* Load any additional SDF files, if required */ 
7986
7987                 LoadTopoData(max_lon, min_lon, max_lat, min_lat);
7988         }
7989         
7990         if (udt_file[0])
7991                 LoadUDT(udt_file);
7992
7993
7994         /***** Let the SPLATting begin! *****/
7995
7996         if (pt2pt_mode)
7997         {
7998                 PlaceMarker(rx_site);
7999
8000                 if (terrain_plot)
8001                 {
8002                         /* Extract extension (if present)
8003                            from "terrain_file" */
8004
8005                         y=strlen(terrain_file);
8006
8007                         for (x=y-1; x>0 && terrain_file[x]!='.'; x--);
8008
8009                         if (x>0)  /* Extension found */
8010                         {
8011                                 for (z=x+1; z<=y && (z-(x+1))<10; z++)
8012                                         ext[z-(x+1)]=tolower(terrain_file[z]);
8013
8014                                 ext[z-(x+1)]=0;     /* Ensure an ending 0 */
8015                                 terrain_file[x]=0;  /* Chop off extension */
8016                         }
8017
8018                         else
8019                                 strncpy(ext,"png\0",4);
8020                 }
8021
8022                 if (elevation_plot)
8023                 {
8024                         /* Extract extension (if present)
8025                            from "elevation_file" */
8026
8027                         y=strlen(elevation_file);
8028
8029                         for (x=y-1; x>0 && elevation_file[x]!='.'; x--);
8030
8031                         if (x>0)  /* Extension found */
8032                         {
8033                                 for (z=x+1; z<=y && (z-(x+1))<10; z++)
8034                                         ext[z-(x+1)]=tolower(elevation_file[z]);
8035
8036                                 ext[z-(x+1)]=0;       /* Ensure an ending 0 */
8037                                 elevation_file[x]=0;  /* Chop off extension */
8038                         }
8039
8040                         else
8041                                 strncpy(ext,"png\0",4);
8042                 }
8043
8044                 if (height_plot)
8045                 {
8046                         /* Extract extension (if present)
8047                            from "height_file" */
8048
8049                         y=strlen(height_file);
8050
8051                         for (x=y-1; x>0 && height_file[x]!='.'; x--);
8052
8053                         if (x>0)  /* Extension found */
8054                         {
8055                                 for (z=x+1; z<=y && (z-(x+1))<10; z++)
8056                                         ext[z-(x+1)]=tolower(height_file[z]);
8057
8058                                 ext[z-(x+1)]=0;    /* Ensure an ending 0 */
8059                                 height_file[x]=0;  /* Chop off extension */
8060                         }
8061
8062                         else
8063                                 strncpy(ext,"png\0",4);
8064                 }
8065
8066                 if (longley_plot)
8067                 {
8068                         /* Extract extension (if present)
8069                            from "longley_file" */
8070
8071                         y=strlen(longley_file);
8072
8073                         for (x=y-1; x>0 && longley_file[x]!='.'; x--);
8074
8075                         if (x>0)  /* Extension found */
8076                         {
8077                                 for (z=x+1; z<=y && (z-(x+1))<10; z++)
8078                                         ext[z-(x+1)]=tolower(longley_file[z]);
8079
8080                                 ext[z-(x+1)]=0;     /* Ensure an ending 0 */
8081                                 longley_file[x]=0;  /* Chop off extension */
8082                         }
8083
8084                         else
8085                                 strncpy(ext,"png\0",4);
8086                 }
8087
8088                 for (x=0; x<txsites && x<4; x++)
8089                 {
8090                         PlaceMarker(tx_site[x]);
8091
8092                         if (nolospath==0)
8093                         {
8094                                 switch (x)
8095                                 {
8096                                         case 0:
8097                                                 PlotPath(tx_site[x],rx_site,1);
8098                                                 break;
8099
8100                                         case 1:
8101                                                 PlotPath(tx_site[x],rx_site,8);
8102                                                 break;
8103
8104                                         case 2:
8105                                                 PlotPath(tx_site[x],rx_site,16);
8106                                                 break;
8107
8108                                         case 3:
8109                                                 PlotPath(tx_site[x],rx_site,32);
8110                                 }
8111                         }
8112
8113                         if (nositereports==0)
8114                                 SiteReport(tx_site[x]);
8115
8116                         if (kml)
8117                                 WriteKML(tx_site[x],rx_site);
8118
8119                         if (txsites>1)
8120                                 snprintf(string,250,"%s-%c.%s%c",longley_file,'1'+x,ext,0);
8121                         else
8122                                 snprintf(string,250,"%s.%s%c",longley_file,ext,0);
8123
8124                         if (nositereports==0)
8125                         {
8126                                 if (longley_file[0]==0)
8127                                 {
8128                                         ReadLRParm(tx_site[x],0);
8129                                         PathReport(tx_site[x],rx_site,string,0);
8130                                 }
8131
8132                                 else
8133                                 {
8134                                         ReadLRParm(tx_site[x],1);
8135                                         PathReport(tx_site[x],rx_site,string,longley_file[0]);
8136                                 }
8137                         }
8138
8139                         if (terrain_plot)
8140                         {
8141                                 if (txsites>1)
8142                                         snprintf(string,250,"%s-%c.%s%c",terrain_file,'1'+x,ext,0);
8143                                 else
8144                                         snprintf(string,250,"%s.%s%c",terrain_file,ext,0);
8145
8146                                 GraphTerrain(tx_site[x],rx_site,string);
8147                         }
8148
8149                         if (elevation_plot)
8150                         {
8151                                 if (txsites>1)
8152                                         snprintf(string,250,"%s-%c.%s%c",elevation_file,'1'+x,ext,0);
8153                                 else
8154                                         snprintf(string,250,"%s.%s%c",elevation_file,ext,0);
8155
8156                                 GraphElevation(tx_site[x],rx_site,string);
8157                         }
8158
8159                         if (height_plot)
8160                         {
8161                                 if (txsites>1)
8162                                         snprintf(string,250,"%s-%c.%s%c",height_file,'1'+x,ext,0);
8163                                 else
8164                                         snprintf(string,250,"%s.%s%c",height_file,ext,0);
8165
8166                                 GraphHeight(tx_site[x],rx_site,string,fresnel_plot,norm);
8167                         }
8168                 }
8169         }
8170
8171         if (area_mode && topomap==0)
8172         {
8173                 for (x=0; x<txsites && x<max_txsites; x++)
8174                 {
8175                         if (coverage)
8176                                 PlotLOSMap(tx_site[x],altitude);
8177
8178                         else if (ReadLRParm(tx_site[x],1))
8179                                         PlotLRMap(tx_site[x],altitudeLR,ano_filename);
8180
8181                         SiteReport(tx_site[x]);
8182                 }
8183         }
8184
8185         if (map || topomap)
8186         {
8187                 /* Label the map */
8188
8189                 if (kml==0)
8190                 {
8191                         for (x=0; x<txsites && x<max_txsites; x++)
8192                                 PlaceMarker(tx_site[x]);
8193                 }
8194
8195                 if (cities)
8196                 {
8197
8198                         for (y=0; y<cities; y++)
8199                                 LoadCities(city_file[y]);
8200
8201                         fprintf(stdout,"\n");
8202                         fflush(stdout);
8203                 }
8204
8205                 /* Load city and county boundary data files */
8206
8207                 if (bfs)
8208                 {
8209                         for (y=0; y<bfs; y++)
8210                                 LoadBoundaries(boundary_file[y]);
8211
8212                         fprintf(stdout,"\n");
8213                         fflush(stdout);
8214                 }
8215
8216                 /* Plot the map */
8217
8218                 if (coverage || pt2pt_mode || topomap)
8219                         WritePPM(mapfile,geo,kml,ngs,tx_site,txsites);
8220
8221                 else
8222                 {
8223                         if (LR.erp==0.0)
8224                                 WritePPMLR(mapfile,geo,kml,ngs,tx_site,txsites);
8225                         else
8226                                 if (dbm)
8227                                         WritePPMDBM(mapfile,geo,kml,ngs,tx_site,txsites);
8228                                 else
8229                                         WritePPMSS(mapfile,geo,kml,ngs,tx_site,txsites);
8230                 }
8231         }
8232
8233         printf("\n");
8234
8235         /* That's all, folks! */
8236
8237         return 0;
8238 }