X-Git-Url: https://git.gag.com/?p=debian%2Fsplat;a=blobdiff_plain;f=splat.cpp;fp=splat.cpp;h=3e17692aafa79b69d410cb869cc403462873f38d;hp=7724fc69fb0f6631eaac793e3b06c59b43073ac7;hb=63fe09e1131ae8f451b4a5af9dbae05cb07d529e;hpb=8c97af274a0a0c0f6c949aab5b35731f7b187873 diff --git a/splat.cpp b/splat.cpp index 7724fc6..3e17692 100644 --- a/splat.cpp +++ b/splat.cpp @@ -1,9 +1,17 @@ /**************************************************************************** -* SPLAT: A Terrain Analysis Program * -* Copyright John A. Magliacane, KD2BD 1997-2004 * -* Last update: 24-Jan-2004 * +* SPLAT: An RF Signal Propagation Loss and Terrain Analysis Tool * +* Last update: 31-Mar-2006 * ***************************************************************************** +* Project started in 1997 by John A. Magliacane, KD2BD * +***************************************************************************** +* * +* Extensively modified by J. D. McDonald in Jan. 2004 to include * +* the Longley-Rice propagation model using C++ code from NTIA/ITS. * * * +* See: http://flattop.its.bldrdoc.gov/itm.html * +* * +***************************************************************************** +* * * This program is free software; you can redistribute it and/or modify it * * under the terms of the GNU General Public License as published by the * * Free Software Foundation; either version 2 of the License or any later * @@ -14,13 +22,6 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * * for more details. * * * -***************************************************************************** -* * -* Extensively modified by J. D. McDonald in Jan. 2004 to include * -* the Longley-Rice propagation model using C++ code from NTIA/ITS. * -* * -* See: http://elbert.its.bldrdoc.gov/itm.html * -* * ***************************************************************************** g++ -Wall -O3 -s -lm -lbz2 -fomit-frame-pointer itm.cpp splat.cpp -o splat *****************************************************************************/ @@ -55,49 +56,48 @@ #define ARRAYSIZE 30025 #endif -char string[255], sdf_path[255], opened=0, *splat_version={"1.1.0"}; +char string[255], sdf_path[255], opened=0, *splat_version={"1.1.1"}; double TWOPI=6.283185307179586, HALFPI=1.570796326794896, PI=3.141592653589793, deg2rad=1.74532925199e-02, EARTHRADIUS=20902230.97, METERS_PER_MILE=1609.344, METERS_PER_FOOT=0.3048, earthradius, max_range=0.0; -int min_north=0, max_north=0, min_west=0, max_west=0, - max_elevation=0, min_elevation=0, bzerror; +int min_north=90, max_north=-90, min_west=360, max_west=-1, + max_elevation=-32768, min_elevation=32768, bzerror, maxdB=230; struct site { double lat; double lon; double alt; char name[50]; - }; - -struct { float lat[ARRAYSIZE]; - float lon[ARRAYSIZE]; - float elevation[ARRAYSIZE]; - float distance[ARRAYSIZE]; - int length; - } path; - -static struct { int min_north; - int max_north; - int min_west; - int max_west; - int max_el; - int min_el; - short data[1200][1200]; - unsigned char mask[1200][1200]; - } dem[MAXSLOTS]; - -struct { - double eps_dielect; - double sgm_conductivity; - double eno_ns_surfref; - double frq_mhz; - double conf; - double rel; - int radio_climate; - int pol; - } LR; + } site; + +struct path { float lat[ARRAYSIZE]; + float lon[ARRAYSIZE]; + float elevation[ARRAYSIZE]; + float distance[ARRAYSIZE]; + int length; + } path; + +struct dem { int min_north; + int max_north; + int min_west; + int max_west; + int max_el; + int min_el; + short data[1200][1200]; + unsigned char mask[1200][1200]; + } dem[MAXSLOTS]; + +struct LR { double eps_dielect; + double sgm_conductivity; + double eno_ns_surfref; + double frq_mhz; + double conf; + double rel; + int radio_climate; + int pol; + } LR; double elev_l[ARRAYSIZE+10]; @@ -122,6 +122,18 @@ double arccos(double x, double y) return result; } +int ReduceAngle(double angle) +{ + /* This function normalizes the argument to + an integer angle between 0 and 180 degrees */ + + double temp; + + temp=acos(cos(angle*deg2rad)); + + return (int)rint(temp/deg2rad); +} + char *dec2dms(double decimal) { /* Converts decimal degrees to degrees, minutes, seconds, @@ -130,6 +142,9 @@ char *dec2dms(double decimal) int degrees, minutes, seconds; double a, b, c, d; + if (decimal<0.0) + decimal=-decimal; + a=floor(decimal); b=60.0*(decimal-a); c=floor(b); @@ -176,8 +191,10 @@ int OrMask(double lat, double lon, int value) y=(int)(1199.0*(lon-floor(lon))); dem[indx].mask[x][y]|=value; + return (dem[indx].mask[x][y]); } + else return -1; } @@ -212,6 +229,7 @@ double GetElevation(struct site location) { if (minlat==dem[indx].min_north && minlon==dem[indx].min_west) { + elevation=3.28084*dem[indx].data[x][y]; found=1; } @@ -318,19 +336,17 @@ void ReadPath(struct site source, struct site destination) elevation and distance information for points along that path in the "path" structure for later use. */ + int c; double azimuth, distance, lat1, lon1, beta, den, num, lat2, lon2, total_distance; - int x1, y1, c; struct site tempsite; - c=0; - lat1=source.lat*deg2rad; lon1=source.lon*deg2rad; azimuth=Azimuth(source,destination)*deg2rad; total_distance=Distance(source,destination); - for (distance=0; distance<=total_distance; distance+=0.04) + for (distance=0, c=0; distance<=total_distance; distance+=0.04) { beta=distance/3959.0; lat2=asin(sin(lat1)*cos(beta)+cos(azimuth)*sin(beta)*cos(lat1)); @@ -363,30 +379,33 @@ void ReadPath(struct site source, struct site destination) lat2=lat2/deg2rad; lon2=lon2/deg2rad; - x1=(int)(1199.0*(lat2-floor(lat2))); - y1=(int)(1199.0*(lon2-floor(lon2))); - - path.lat[c]=lat2; - path.lon[c]=lon2; - tempsite.lat=lat2; - tempsite.lon=lon2; - path.elevation[c]=GetElevation(tempsite); - path.distance[c]=distance; - c++; + if (c=180.0) + diff-=360.0; + + return diff; +} + void PlaceMarker(struct site location) { /* This function places text and marker data in the mask array @@ -546,7 +586,8 @@ void PlaceMarker(struct site location) lat=location.lat; lon=location.lon; - if (latxmin && lonymin) + + if (latxmin && (LonDiff(lon,ymax)<0.0) && (LonDiff(lon,ymin)>0.0)) { p1=1.0/1200.0; p3=3.0/1200.0; @@ -561,7 +602,7 @@ void PlaceMarker(struct site location) /* Is Marker Position Clear Of Text Or Other Markers? */ for (x=lat-p3; (x<=xmax && x>=xmin && x<=lat+p3); x+=p1) - for (y=lon-p3; (y<=ymax && y>=ymin && y<=lon+p3); y+=p1) + for (y=lon-p3; (LonDiff(y,ymax)<=0.0) && (LonDiff(y,ymin)>=0.0) && (LonDiff(y,lon+p3)<=0.0); y+=p1) occupied|=(GetMask(x,y)&2); if (occupied==0) @@ -573,7 +614,7 @@ void PlaceMarker(struct site location) label_length=p1*(double)(strlen(location.name)<<3); - if (((lon+label_length)<=ymax) && (lon-label_length)>=ymin) + if ((LonDiff(lon+label_length,ymax)<=0.0) && (LonDiff(lon-label_length,ymin)>=0.0)) { /* Default: Centered Text */ @@ -638,7 +679,7 @@ void PlaceMarker(struct site location) if (ok2print==0) { - if ((lon-label_length)>=ymin) + if (LonDiff(lon-label_length,ymin)>=0.0) { /* Position Text To The Right Of The Marker */ @@ -703,7 +744,7 @@ void PlaceMarker(struct site location) coordinates that describe the placement of the text on the map. */ - if (ok2print && textx!=0.0 && texty!=0.0) + if (ok2print) { /* Draw Text */ @@ -720,6 +761,7 @@ void PlaceMarker(struct site location) if (byte&c) OrMask(x,y,2); } + x-=p1; y=texty; } @@ -728,7 +770,7 @@ void PlaceMarker(struct site location) On Location Specified */ for (x=lat-p3; (x<=xmax && x>=xmin && x<=lat+p3); x+=p1) - for (y=lon-p3; (y<=ymax && y>=ymin && y<=lon+p3); y+=p1) + for (y=lon-p3; (LonDiff(y,ymax)<=0.0) && (LonDiff(y,ymin)>=0.0) && (LonDiff(y,lon+p3)<=0.0); y+=p1) OrMask(x,y,2); } } @@ -746,10 +788,9 @@ double ReadBearing(char *input) embedded within the numbers expressed in the input string. Decimal seconds are permitted. */ - double bearing=0.0; + double seconds, bearing=0.0; char string[20]; int a, b, length, degrees, minutes; - double seconds; /* Copy "input" to "string", and ignore any extra spaces that might be present in the process. */ @@ -787,7 +828,7 @@ double ReadBearing(char *input) /* Anything else returns a 0.0 */ - if (bearing>360.0 || bearing<0.0) + if (bearing>360.0 || bearing<-90.0) bearing=0.0; return bearing; @@ -817,8 +858,8 @@ struct site LoadQTH(char *filename) qthfile[x+3]='h'; qthfile[x+4]=0; - tempsite.lat=0.0; - tempsite.lon=0.0; + tempsite.lat=91.0; + tempsite.lon=361.0; tempsite.alt=0.0; tempsite.name[0]=0; @@ -907,11 +948,8 @@ int LoadSDF_SDF(char *name) for (indx=0, found=0; indx=0 && indx=0 && indxdem[indx].max_el) dem[indx].max_el=data; - if (dem[indx].min_el==0) + if (datamax_elevation) max_elevation=dem[indx].max_el; - if (dem[indx].max_north>max_north) + if (max_north==-90) max_north=dem[indx].max_north; - if (dem[indx].max_west>max_west) - max_west=dem[indx].max_west; + else if (dem[indx].max_north>max_north) + max_north=dem[indx].max_north; - if (min_north==0) + if (min_north==90) min_north=dem[indx].min_north; + + else if (dem[indx].min_northmax_west) + max_west=dem[indx].max_west; + } + + else + { + if (dem[indx].max_westmin_west) + min_west=dem[indx].min_west; + } } fprintf(stdout," Done!\n"); @@ -1111,11 +1177,8 @@ int LoadSDF_BZ(char *name) for (indx=0, found=0; indx=0 && indx=0 && indxdem[indx].max_el) dem[indx].max_el=data; - if (dem[indx].min_el==0) + if (datamax_elevation) max_elevation=dem[indx].max_el; - if (dem[indx].max_north>max_north) + if (max_north==-90) max_north=dem[indx].max_north; - if (dem[indx].max_west>max_west) - max_west=dem[indx].max_west; + else if (dem[indx].max_north>max_north) + max_north=dem[indx].max_north; + + if (min_north==90) + min_north=dem[indx].min_north; - if (min_north==0) + else if (dem[indx].min_northmax_west) + max_west=dem[indx].max_west; + } + + else + { + if (dem[indx].max_westmin_west) + min_west=dem[indx].min_west; + } } fprintf(stdout," Done!\n"); @@ -1250,11 +1342,8 @@ char LoadSDF(char *name) for (indx=0, found=0; indx=0 && indx=0 && indxmax_elevation) max_elevation=dem[indx].max_el; - if (dem[indx].max_north>max_north) + if (max_north==-90) max_north=dem[indx].max_north; - if (dem[indx].max_west>max_west) - max_west=dem[indx].max_west; + else if (dem[indx].max_north>max_north) + max_north=dem[indx].max_north; - if (min_north==0) + if (min_north==90) min_north=dem[indx].min_north; + + else if (dem[indx].min_northmax_west) + max_west=dem[indx].max_west; + } + + else + { + if (dem[indx].max_westmin_west) + min_west=dem[indx].min_west; + } } fprintf(stdout," Done!\n"); @@ -1415,7 +1532,7 @@ void LoadBoundaries(char *filename) do { sscanf(string,"%lf %lf", &lon1, &lat1); - + lon0=fabs(lon0); lon1=fabs(lon1); @@ -1445,6 +1562,7 @@ void LoadBoundaries(char *filename) fprintf(stdout,"Done!\n"); fflush(stdout); } + else fprintf(stderr,"*** ERROR: \"%s\": not found!\n",filename); } @@ -1794,7 +1912,7 @@ void PlotPath(struct site source, struct site destination, char mask_value) cos_test_angle=((rx_alt*rx_alt)+(distance*distance)-(test_alt*test_alt))/(2.0*rx_alt*distance); /* Compare these two angles to determine if - a blockage exists. Since we're comparing + an obstruction exists. Since we're comparing the cosines of these angles rather than the angles themselves, the following "if" statement is reversed from what it would @@ -1828,8 +1946,7 @@ void PlotLRPath(struct site source, struct site destination) for (y=0; y225.0) loss=225.0; @@ -1873,7 +1991,7 @@ void PlotCoverage(struct site source, double altitude) of a topographic map when the WritePPM() function is later invoked. */ - double lat, lon, one_pixel; + float lat, lon, one_pixel; static unsigned char mask_value; int z, count; struct site edge; @@ -1899,10 +2017,13 @@ void PlotCoverage(struct site source, double altitude) /* 18.75=1200 pixels/degree divided by 64 loops per progress indicator symbol (.oOo) printed. */ - z=(int)(18.75*(max_west-min_west)); + z=(int)(18.75*ReduceAngle(max_west-min_west)); - for (lon=min_west, x=0; lon<=max_west; lon+=one_pixel) + for (lon=min_west, x=0; (LonDiff(lon,max_west)<=0.0); lon+=one_pixel) { + if (lon>=360.0) + lon-=360.0; + edge.lat=max_north; edge.lon=lon; edge.alt=altitude; @@ -1955,10 +2076,13 @@ void PlotCoverage(struct site source, double altitude) fprintf(stdout,"\n50%c to 75%c ",37,37); fflush(stdout); - z=(int)(18.75*(max_west-min_west)); + z=(int)(18.75*ReduceAngle(max_west-min_west)); - for (lon=min_west, x=0; lon<=max_west; lon+=one_pixel) + for (lon=min_west, x=0; (LonDiff(lon,max_west)<=0.0); lon+=one_pixel) { + if (lon>=360.0) + lon-=360.0; + edge.lat=min_north; edge.lon=lon; edge.alt=altitude; @@ -2040,7 +2164,7 @@ void PlotLRMap(struct site source, double altitude) int z, count; struct site edge; - double lat, lon, one_pixel; + float lat, lon, one_pixel; unsigned char symbol[4], x; one_pixel=1.0/1200.0; @@ -2059,10 +2183,13 @@ void PlotLRMap(struct site source, double altitude) /* 18.75=1200 pixels/degree divided by 64 loops per progress indicator symbol (.oOo) printed. */ - z=(int)(18.75*(max_west-min_west)); + z=(int)(18.75*ReduceAngle(max_west-min_west)); - for (lon=min_west, x=0; lon<=max_west; lon+=one_pixel) + for (lon=min_west, x=0; (LonDiff(lon,max_west)<=0.0); lon+=one_pixel) { + if (lon>=360.0) + lon-=360.0; + edge.lat=max_north; edge.lon=lon; edge.alt=altitude; @@ -2115,10 +2242,13 @@ void PlotLRMap(struct site source, double altitude) fprintf(stdout,"\n50%c to 75%c ",37,37); fflush(stdout); - z=(int)(18.75*(max_west-min_west)); + z=(int)(18.75*ReduceAngle(max_west-min_west)); - for (lon=min_west, x=0; lon<=max_west; lon+=one_pixel) + for (lon=min_west, x=0; (LonDiff(lon,max_west)<=0.0); lon+=one_pixel) { + if (lon>=360.0) + lon-=360.0; + edge.lat=min_north; edge.lon=lon; edge.alt=altitude; @@ -2180,19 +2310,19 @@ void WritePPM(char *filename) from its representation in dem[][] so that north points up and east points right in the image generated. */ - int indx, x, x0, y0, minlat, minlon; - unsigned width, height, output; - unsigned char found, mask; char mapfile[255]; - double conversion, lat, lon, one_over_gamma, one_pixel; + unsigned char found, mask; + unsigned width, height, output; + int indx, x, y, x0=0, y0=0, minlat, minlon; + float lat, lon, one_pixel, conversion, one_over_gamma; FILE *fd; one_pixel=1.0/1200.0; one_over_gamma=1.0/GAMMA; conversion=255.0/pow((double)(max_elevation-min_elevation),one_over_gamma); - width=1200*(max_west-min_west); - height=1200*(max_north-min_north); + width=(unsigned)(1200*ReduceAngle(max_west-min_west)); + height=(unsigned)(1200*ReduceAngle(max_north-min_north)); if (filename[0]==0) strncpy(mapfile, "map.ppm\0",8); @@ -2215,11 +2345,15 @@ void WritePPM(char *filename) fprintf(stdout,"\nWriting \"%s\" (%ux%u pixmap image)... ",mapfile,width,height); fflush(stdout); - for (lat=(double)max_north; lat>=(double)min_north; lat-=one_pixel) + for (y=0, lat=((double)max_north)-one_pixel; y<(int)height; y++, lat-=one_pixel) { - for (lon=(double)max_west; lon>=(double)min_west; lon-=one_pixel) + minlat=(int)floor(lat); + + for (x=0, lon=((double)max_west)-one_pixel; x<(int)width; x++, lon-=one_pixel) { - minlat=(int)floor(lat); + if (lon<0.0) + lon+=360.0; + minlon=(int)floor(lon); for (indx=0, found=0; indx=(double)min_north; lat-=one_pixel) + for (y=0, lat=((double)max_north)-one_pixel; y<(int)height; y++, lat-=one_pixel) { - for (lon=(double)max_west; lon>=(double)min_west; lon-=one_pixel) + minlat=(int)floor(lat); + + for (x=0, lon=((double)max_west)-one_pixel; x<(int)width; x++, lon-=one_pixel) { - minlat=(int)floor(lat); + if (lon<0.0) + lon+=360.0; + minlon=(int)floor(lon); for (indx=0, found=0; indx>3)); + cityorcounty=0; if (mask&2) { /* Text Labels - Black or Red */ - if (mask&120) + + /* if ((mask&120) && (loss<=maxdB)) */ + if ((mask&120) && (loss<=90)) fprintf(fd,"%c%c%c",0,0,0); else fprintf(fd,"%c%c%c",255,0,0); + + cityorcounty=1; } else if (mask&4) + { /* County Boundaries: Black */ - fprintf(fd,"%c%c%c",0,0,0); - else if (mask&1 && !((mask&248)==192)) - { - /* Outside Analysis Range */ - /* Display Greyscale / Sea Level */ + fprintf(fd,"%c%c%c",0,0,0); - if (dem[indx].data[x0][y0]==0) - fprintf(fd,"%c%c%c",0,0,170); - else - { - output=(unsigned)(0.5+pow((double)(dem[indx].data[x0][y0]-min_elevation),one_over_gamma)*conversion); - fprintf(fd,"%c%c%c",output,output,output); - } + cityorcounty=1; } - else switch ((mask&248)>>3) + if (cityorcounty==0) { - case 0: - /* Inside range, but no coverage. - Display Sea Level / Terrain */ + if (loss>maxdB) + + { /* Display land or sea elevation */ + + if (dem[indx].data[x0][y0]==0) + fprintf(fd,"%c%c%c",0,0,170); + else + { + /* Elevation: Greyscale */ + output=(unsigned)(0.5+pow((double)(dem[indx].data[x0][y0]-min_elevation),one_over_gamma)*conversion); + fprintf(fd,"%c%c%c",output,output,output); + } + } - if (dem[indx].data[x0][y0]==0) - fprintf(fd,"%c%c%c",0,0,170); - else + else switch (loss) { - /* Elevation: Greyscale */ - output=(unsigned)(0.5+pow((double)(dem[indx].data[x0][y0]-min_elevation),one_over_gamma)*conversion); - fprintf(fd,"%c%c%c",output,output,output); - } + /* Plot signal loss in color */ - break; + case 80: + fprintf(fd,"%c%c%c",255,0,0); + break; - case 1: - /* Green */ - fprintf(fd,"%c%c%c",0,255,0); - break; + case 90: + fprintf(fd,"%c%c%c",255,128,0); + break; - case 2: - /* Pink */ - fprintf(fd,"%c%c%c",255,192,203); - break; + case 100: + fprintf(fd,"%c%c%c",255,165,0); + break; - case 3: - /* Cyan */ - fprintf(fd,"%c%c%c",0,255,255); - break; + case 110: + fprintf(fd,"%c%c%c",255,206,0); + break; - case 4: - /* Yellow */ - fprintf(fd,"%c%c%c",255,255,0); - break; + case 120: + fprintf(fd,"%c%c%c",255,255,0); + break; - case 5: - /* Medium Violet */ - fprintf(fd,"%c%c%c",161,131,224); - break; + case 130: + fprintf(fd,"%c%c%c",184,255,0); + break; - case 6: - /* Orange */ - fprintf(fd,"%c%c%c",255,165,0); - break; + case 140: + fprintf(fd,"%c%c%c",0,255,0); + break; - case 7: - /* Light Green */ - fprintf(fd,"%c%c%c",193,255,193); - break; + case 150: + fprintf(fd,"%c%c%c",0,208,0); + break; - case 8: - /* Red Pink */ - fprintf(fd,"%c%c%c",255,108,108); - break; + case 160: + fprintf(fd,"%c%c%c",0,196,196); + break; - case 9: - /* TX1 + TX4: Green Yellow */ - fprintf(fd,"%c%c%c",173,255,47); - break; + case 170: + fprintf(fd,"%c%c%c",0,148,255); + break; - case 10: - /* Blanched Almond */ - fprintf(fd,"%c%c%c",255,235,184); - break; + case 180: + fprintf(fd,"%c%c%c",80,80,255); + break; - case 11: - /* Dark Turquoise */ - fprintf(fd,"%c%c%c",0,206,209); - break; + case 190: + fprintf(fd,"%c%c%c",0,38,255); + break; - case 12: - /* Tan */ - fprintf(fd,"%c%c%c",210,180,140); - break; + case 200: + fprintf(fd,"%c%c%c",142,63,255); + break; - case 13: - /* Magenta 1 */ - fprintf(fd,"%c%c%c",243,110,205); - break; + case 210: + fprintf(fd,"%c%c%c",196,54,255); + break; - case 14: - /* Gold2 */ - fprintf(fd,"%c%c%c",238,201,0); - break; + case 220: + fprintf(fd,"%c%c%c",255,0,255); + break; - case 15: - /* Medium Spring Green */ - fprintf(fd,"%c%c%c",0,250,154); - break; + case 230: + fprintf(fd,"%c%c%c",255,194,204); + break; - case 16: - /* Very light Blue */ - fprintf(fd,"%c%c%c",244,244,255); - break; + default: - default: - /* Land / Sea */ - if (dem[indx].data[x0][y0]==0) - fprintf(fd,"%c%c%c",0,0,170); - else - { - /* Elevation: Greyscale */ - output=(unsigned)(0.5+pow((double)(dem[indx].data[x0][y0]-min_elevation),one_over_gamma)*conversion); - fprintf(fd,"%c%c%c",output,output,output); + if (dem[indx].data[x0][y0]==0) + fprintf(fd,"%c%c%c",0,0,170); + else + { + /* Elevation: Greyscale */ + output=(unsigned)(0.5+pow((double)(dem[indx].data[x0][y0]-min_elevation),one_over_gamma)*conversion); + fprintf(fd,"%c%c%c",output,output,output); + } } } } + + else + { + /* We should never get here, but if */ + /* we do, display the region as black */ + + fprintf(fd,"%c%c%c",0,0,0); + } } } + /* Display legend along bottom of image */ + x0=width/16; for (y0=0; y0<30; y0++) @@ -2572,78 +2714,63 @@ void WritePPMLR(char *filename) switch (t) { case 0: - /* Green */ - fprintf(fd,"%c%c%c",0,255,0); + fprintf(fd,"%c%c%c",255,0,0); break; case 1: - /* Pink */ - fprintf(fd,"%c%c%c",255,192,203); + fprintf(fd,"%c%c%c",255,128,0); break; case 2: - /* Cyan */ - fprintf(fd,"%c%c%c",0,255,255); + fprintf(fd,"%c%c%c",255,165,0); break; case 3: - /* Yellow */ - fprintf(fd,"%c%c%c",255,255,0); + fprintf(fd,"%c%c%c",255,206,0); break; case 4: - /* Medium Violet */ - fprintf(fd,"%c%c%c",161,131,224); + fprintf(fd,"%c%c%c",255,255,0); break; case 5: - /* Orange */ - fprintf(fd,"%c%c%c",255,165,0); + fprintf(fd,"%c%c%c",184,255,0); break; - case 6: - /* Light Green */ - fprintf(fd,"%c%c%c",193,255,193); + case 6: + fprintf(fd,"%c%c%c",0,255,0); break; - case 7: - /* Red Pink */ - fprintf(fd,"%c%c%c",255,108,108); + case 7: + fprintf(fd,"%c%c%c",0,208,0); break; case 8: - /* Green Yellow */ - fprintf(fd,"%c%c%c",173,255,47); + fprintf(fd,"%c%c%c",0,196,196); break; case 9: - /* Blanched Almond */ - fprintf(fd,"%c%c%c",255,235,184); + fprintf(fd,"%c%c%c",0,148,255); break; case 10: - /* Dark Turquoise */ - fprintf(fd,"%c%c%c",0,206,209); + fprintf(fd,"%c%c%c",80,80,255); break; case 11: - /* Tan */ - fprintf(fd,"%c%c%c",210,180,140); + fprintf(fd,"%c%c%c",0,38,255); break; case 12: - /* Magenta 1 */ - fprintf(fd,"%c%c%c",243,110,205); + fprintf(fd,"%c%c%c",142,63,255); break; case 13: - /* Gold2 */ - fprintf(fd,"%c%c%c",238,201,0); + fprintf(fd,"%c%c%c",196,54,255); break; case 14: - /* Medium Spring Green */ - fprintf(fd,"%c%c%c",0,250,154); + fprintf(fd,"%c%c%c",255,0,255); break; case 255: @@ -2652,8 +2779,7 @@ void WritePPMLR(char *filename) break; default: - /* Very Light Blue */ - fprintf(fd,"%c%c%c",244,244,255); + fprintf(fd,"%c%c%c",255,194,204); } } } @@ -2884,11 +3010,11 @@ void GraphHeight(struct site source, struct site destination, char *name) { /* This function invokes gnuplot to generate an appropriate output file indicating the terrain profile between the source - and destination locations. What is plotted is the height of land - above or below a straight line between the receibe and transmit - sites. "filename" is the name assigned to the output file - generated by gnuplot. The filename extension is used to - set gnuplot's terminal setting and output file type. + and destination locations. What is plotted is the height of + land above or below a straight line between the receibe and + transmit sites. "filename" is the name assigned to the output + file generated by gnuplot. The filename extension is used + to set gnuplot's terminal setting and output file type. If no extension is found, .gif is assumed. */ int x, y, z; @@ -3003,7 +3129,7 @@ void GraphHeight(struct site source, struct site destination, char *name) fprintf(fd,"plot \"profile.gp\" title \"Real Earth Profile\" with lines, \"reference.gp\" title \"Line Of Sight Path\" with lines\n"); fclose(fd); - + x=system("gnuplot splat.gp"); if (x!=-1) @@ -3046,8 +3172,20 @@ void GraphLongley(struct site source, struct site destination, char *name) fprintf(fd2,"Analysis of RF path conditions between %s and %s:\n",source.name, destination.name); fprintf(fd2,"\n-------------------------------------------------------------------------\n\n"); fprintf(fd2,"Transmitter site: %s\n",source.name); - fprintf(fd2,"Site location: %.4f North / %.4f West",source.lat, source.lon); - fprintf(fd2, " (%s N / ", dec2dms(source.lat)); + + if (source.lat>=0.0) + { + fprintf(fd2,"Site location: %.4f North / %.4f West",source.lat, source.lon); + fprintf(fd2, " (%s N / ", dec2dms(source.lat)); + } + + else + { + + fprintf(fd2,"Site location: %.4f South / %.4f West",-source.lat, source.lon); + fprintf(fd2, " (%s S / ", dec2dms(source.lat)); + } + fprintf(fd2, "%s W)\n", dec2dms(source.lon)); fprintf(fd2,"Ground elevation: %.2f feet AMSL\n",GetElevation(source)); fprintf(fd2,"Antenna height: %.2f feet AGL / %.2f feet AMSL\n",source.alt, source.alt+GetElevation(source)); @@ -3073,8 +3211,19 @@ void GraphLongley(struct site source, struct site destination, char *name) /* Receiver */ fprintf(fd2,"Receiver site: %s\n",destination.name); - fprintf(fd2,"Site location: %.4f North / %.4f West",destination.lat, destination.lon); - fprintf(fd2, " (%s N / ", dec2dms(destination.lat)); + + if (destination.lat>=0.0) + { + fprintf(fd2,"Site location: %.4f North / %.4f West",destination.lat, destination.lon); + fprintf(fd2, " (%s N / ", dec2dms(destination.lat)); + } + + else + { + fprintf(fd2,"Site location: %.4f South / %.4f West",-destination.lat, destination.lon); + fprintf(fd2, " (%s S / ", dec2dms(destination.lat)); + } + fprintf(fd2, "%s W)\n", dec2dms(destination.lon)); fprintf(fd2,"Ground elevation: %.2f feet AMSL\n",GetElevation(destination)); fprintf(fd2,"Antenna height: %.2f feet AGL / %.2f feet AMSL\n",destination.alt, destination.alt+GetElevation(destination)); @@ -3174,12 +3323,14 @@ void GraphLongley(struct site source, struct site destination, char *name) LR.frq_mhz, LR.radio_climate, LR.pol, LR.conf, LR.rel, loss, strmode, errnum); - /* Note: PASS BY REFERENCE ... loss and errnum are pass - by reference, only used in this file by this function */ + /* Note: PASS BY REFERENCE ... loss and errnum are + passed by reference, and are only used in this + file by this function */ fprintf(fd,"%f\t%f\n",path.distance[path.length-1]-path.distance[x],loss); - fprintf(fd2,"%7.2f\t\t%7.2f\t\t %d\t%s\n",path.distance[x],loss, errnum, strmode); + fprintf(fd2,"%7.2f\t\t%7.2f\t\t %d\t%s\n",path.distance[x],loss, errnum, strmode); + errflag|=errnum; if (loss>maxloss) @@ -3310,8 +3461,20 @@ void ObstructionReport(struct site xmtr, struct site rcvr, char report) fprintf(fd,"Analysis of line-of-sight path conditions between %s and %s:\n",xmtr.name, rcvr.name); fprintf(fd,"\n-------------------------------------------------------------------------\n\n"); fprintf(fd,"Transmitter site: %s\n",xmtr.name); - fprintf(fd,"Site location: %.4f North / %.4f West",xmtr.lat, xmtr.lon); - fprintf(fd, " (%s N / ", dec2dms(xmtr.lat)); + + + if (xmtr.lat>=0.0) + { + fprintf(fd,"Site location: %.4f North / %.4f West",xmtr.lat, xmtr.lon); + fprintf(fd, " (%s N / ", dec2dms(xmtr.lat)); + } + + else + { + fprintf(fd,"Site location: %.4f South / %.4f West",-xmtr.lat, xmtr.lon); + fprintf(fd, " (%s S / ", dec2dms(xmtr.lat)); + } + fprintf(fd, "%s W)\n", dec2dms(xmtr.lon)); fprintf(fd,"Ground elevation: %.2f feet AMSL\n",GetElevation(xmtr)); fprintf(fd,"Antenna height: %.2f feet AGL / %.2f feet AMSL\n",xmtr.alt, xmtr.alt+GetElevation(xmtr)); @@ -3337,8 +3500,19 @@ void ObstructionReport(struct site xmtr, struct site rcvr, char report) /* Receiver */ fprintf(fd,"Receiver site: %s\n",rcvr.name); - fprintf(fd,"Site location: %.4f North / %.4f West",rcvr.lat, rcvr.lon); - fprintf(fd, " (%s N / ", dec2dms(rcvr.lat)); + + if (rcvr.lat>=0.0) + { + fprintf(fd,"Site location: %.4f North / %.4f West",rcvr.lat, rcvr.lon); + fprintf(fd, " (%s N / ", dec2dms(rcvr.lat)); + } + + else + { + fprintf(fd,"Site location: %.4f South / %.4f West",-rcvr.lat, rcvr.lon); + fprintf(fd, " (%s S / ", dec2dms(rcvr.lat)); + } + fprintf(fd, "%s W)\n", dec2dms(rcvr.lon)); fprintf(fd,"Ground elevation: %.2f feet AMSL\n",GetElevation(rcvr)); fprintf(fd,"Antenna height: %.2f feet AGL / %.2f feet AMSL\n",rcvr.alt, rcvr.alt+GetElevation(rcvr)); @@ -3377,7 +3551,13 @@ void ObstructionReport(struct site xmtr, struct site rcvr, char report) while (block=='*') { if (result.lat!=result2.lat || result.lon!=result2.lon || result.alt!=result2.alt) - fprintf(fd,"\t%.4f N, %.4f W, %5.2f miles, %6.2f feet AMSL.\n",result.lat, result.lon, Distance(rcvr,result), result.alt); + { + if (result.lat>=0.0) + fprintf(fd,"\t%.4f N, %.4f W, %5.2f miles, %6.2f feet AMSL.\n",result.lat, result.lon, Distance(rcvr,result), result.alt); + else + + fprintf(fd,"\t%.4f S, %.4f W, %5.2f miles, %6.2f feet AMSL.\n",-result.lat, result.lon, Distance(rcvr,result), result.alt); + } result2=result; new_site.alt+=1.0; @@ -3422,8 +3602,19 @@ void SiteReport(struct site xmtr) fprintf(fd,"\n\t--==[ SPLAT! v%s Site Analysis Report For: %s ]==--\n\n",splat_version,xmtr.name); fprintf(fd,"---------------------------------------------------------------------------\n\n"); - fprintf(fd,"Site location: %.4f North / %.4f West",xmtr.lat, xmtr.lon); - fprintf(fd, " (%s N / ",dec2dms(xmtr.lat)); + + if (xmtr.lat>=0.0) + { + fprintf(fd,"Site location: %.4f North / %.4f West",xmtr.lat, xmtr.lon); + fprintf(fd, " (%s N / ",dec2dms(xmtr.lat)); + } + + else + { + fprintf(fd,"Site location: %.4f South / %.4f West",-xmtr.lat, xmtr.lon); + fprintf(fd, " (%s S / ",dec2dms(xmtr.lat)); + } + fprintf(fd, "%s W)\n",dec2dms(xmtr.lon)); fprintf(fd,"Ground elevation: %.2f feet AMSL\n",GetElevation(xmtr)); fprintf(fd,"Antenna height: %.2f feet AGL / %.2f feet AMSL\n",xmtr.alt, xmtr.alt+GetElevation(xmtr)); @@ -3457,47 +3648,49 @@ void SiteReport(struct site xmtr) int main(char argc, char *argv[]) { + int x, y, ymin, ymax, width, z=0, min_lat, min_lon, + max_lat, max_lon, rxlat, rxlon, txlat, txlon, + west_min, west_max, north_min, north_max; - int x, y, z=0; - unsigned char rxlat, rxlon, txlat, txlon, min_lat, - min_lon, max_lat, max_lon, - coverage=0, LRmap=0, - ext[20], terrain_plot=0, + unsigned char coverage=0, LRmap=0, ext[20], terrain_plot=0, elevation_plot=0, height_plot=0, longley_plot=0, cities=0, bfs=0, txsites=0, - count, west_min, west_max, north_min, north_max, - report='y'; + count, report='y'; char mapfile[255], header[80], city_file[5][255], elevation_file[255], height_file[255], longley_file[255], terrain_file[255], - string[255], rxfile[255], - txfile[255], map=0, boundary_file[5][255]; + string[255], rxfile[255], *env=NULL, + txfile[255], map=0, boundary_file[5][255], + rxsite=0; double altitude=0.0, altitudeLR=0.0, tx_range=0.0, rx_range=0.0, deg_range=0.0, deg_limit, deg_range_lon, er_mult; + struct site tx_site[4], rx_site; + FILE *fd; - sprintf(header,"\n --==[ SPLAT! v%s Terrain Analysis Software (c) 1997-2004 KD2BD ]==--\n\n", splat_version); + sprintf(header,"\n\t\t--==[ Welcome To SPLAT! v%s ]==--\n\n", splat_version); if (argc==1) { - fprintf(stdout, "%sAvailable Options...\n\n\t-t txsite(s).qth (max of 4)\n\t-r rxsite.qth\n",header); - fprintf(stdout,"\t-c plot coverage area(s) of TX(s) based on an RX antenna at X feet AGL\n"); - fprintf(stdout,"\t-L plot path loss map of TX based on an RX antenna at X feet AGL\n"); - fprintf(stdout,"\t-s filename(s) of city/site file(s) to import (max of 5)\n"); - fprintf(stdout,"\t-b filename(s) of cartographic boundary file(s) to import (max of 5)\n"); - fprintf(stdout,"\t-p filename of terrain profile graph to plot\n"); - fprintf(stdout,"\t-e filename of terrain elevation graph to plot\n"); - fprintf(stdout,"\t-h filename of terrain height graph to plot\n"); - fprintf(stdout,"\t-l filename of Longley-Rice graph to plot\n"); - fprintf(stdout,"\t-o filename of topographic map to generate (.ppm)\n"); - fprintf(stdout,"\t-d sdf file directory path (overrides path in ~/.splat_path file)\n"); - fprintf(stdout,"\t-n no analysis, brief report\n\t-N no analysis, no report\n"); - fprintf(stdout,"\t-m earth radius multiplier\n"); - fprintf(stdout,"\t-R modify default range for -c or -L (miles)\n\n"); + fprintf(stdout, "%sAvailable Options...\n\n\t -t txsite(s).qth (max of 4)\n\t -r rxsite.qth\n",header); + fprintf(stdout,"\t -c plot coverage area(s) of TX(s) based on an RX antenna at X feet AGL\n"); + fprintf(stdout,"\t -L plot path loss map of TX based on an RX antenna at X feet AGL\n"); + fprintf(stdout,"\t -s filename(s) of city/site file(s) to import (max of 5)\n"); + fprintf(stdout,"\t -b filename(s) of cartographic boundary file(s) to import (max of 5)\n"); + fprintf(stdout,"\t -p filename of terrain profile graph to plot\n"); + fprintf(stdout,"\t -e filename of terrain elevation graph to plot\n"); + fprintf(stdout,"\t -h filename of terrain height graph to plot\n"); + fprintf(stdout,"\t -l filename of Longley-Rice graph to plot\n"); + fprintf(stdout,"\t -o filename of topographic map to generate (.ppm)\n"); + fprintf(stdout,"\t -d sdf file directory path (overrides path in ~/.splat_path file)\n"); + fprintf(stdout,"\t -n no analysis, brief report\n\t -N no analysis, no report\n"); + fprintf(stdout,"\t -m earth radius multiplier\n"); + fprintf(stdout,"\t -R modify default range for -c or -L (miles)\n"); + fprintf(stdout,"\t-db maximum loss contour to display on path loss maps (80-230 dB)\n\n"); fprintf(stdout,"Type 'man splat', or see the documentation for more details.\n\n"); fflush(stdout); @@ -3514,20 +3707,27 @@ int main(char argc, char *argv[]) terrain_file[0]=0; sdf_path[0]=0; path.length=0; - rx_site.lat=0.0; - rx_site.lon=0.0; + rx_site.lat=91.0; + rx_site.lon=361.0; earthradius=EARTHRADIUS; + for (x=0; x<4; x++) + { + tx_site[x].lat=91.0; + tx_site[x].lon=361.0; + } + for (x=0; x230) + maxdB=230; + } + } + if (strcmp(argv[x],"-p")==0) { z=x+1; @@ -3702,6 +3920,7 @@ int main(char argc, char *argv[]) { strncpy(rxfile,argv[z],253); rx_site=LoadQTH(rxfile); + rxsite=1; } } @@ -3749,7 +3968,7 @@ int main(char argc, char *argv[]) for (x=0, y=0; xmax_lat) + max_lat=txlat; + + if (LonDiff(txlon,min_lon)<0.0) + min_lon=txlon; + + if (LonDiff(txlon,max_lon)>0.0) + max_lon=txlon; } - if (rxlon!=0) + if (rxsite) { - if (min_lon==0) - min_lon=rxlon; + rxlat=(int)floor(rx_site.lat); + rxlon=(int)floor(rx_site.lon); + + if (rxlatmax_lat) + max_lat=rxlat; + + if (LonDiff(rxlon,min_lon)<0.0) min_lon=rxlon; + + if (LonDiff(rxlon,max_lon)>0.0) + max_lon=rxlon; } - if (rxlat>max_lat) - max_lat=rxlat; - if (rxlon>max_lon) - max_lon=rxlon; + /* Load the required SDF files */ - for (y=0, z=0; z=360) + ymin-=360; - if (txlon!=0) - { - if (min_lon==0) - min_lon=txlon; + ymax=ymin+1; - else if (txlonmax_lat) - max_lat=txlat; + while (ymax>=360) + ymax-=360; - if (txlon>max_lon) - max_lon=txlon; + sprintf(string,"%d:%d:%d:%d",x, x+1, ymin, ymax); + LoadSDF(string); + } } - if (min_lat!=0 && min_lon!=0 && max_lat!=0 && max_lon!=0) + else { - for (y=min_lon; y<=max_lon; y++) + for (y=0; y<=width; y++) for (x=min_lat; x<=max_lat; x++) { - sprintf(string,"%u:%u:%u:%u",x, x+1, y, y+1); + ymin=max_lon+y; + + while (ymin<0) + ymin+=360; + + while (ymin>=360) + ymin-=360; + + ymax=ymin+1; + + while (ymax<0) + ymax+=360; + + while (ymax>=360) + ymax-=360; + + sprintf(string,"%d:%d:%d:%d",x, x+1, ymin, ymax); LoadSDF(string); } } - if (coverage) + if (coverage | LRmap) { + if (LRmap) + txsites=1; + for (z=0; zdeg_limit) deg_range_lon=deg_limit; - north_min=(unsigned char)floor(tx_site[z].lat-deg_range); - north_max=(unsigned char)floor(tx_site[z].lat+deg_range); - west_min=(unsigned char)floor(tx_site[z].lon-deg_range_lon); - west_max=(unsigned char)floor(tx_site[z].lon+deg_range_lon); - if (min_lat==0) - min_lat=north_min; + north_min=(int)floor(tx_site[z].lat-deg_range); + north_max=(int)floor(tx_site[z].lat+deg_range); - else if (north_min=360) + west_min-=360; - if (north_max>max_lat) - max_lat=north_max; + west_max=(int)floor(tx_site[z].lon+deg_range_lon); - if (west_max>max_lon) - max_lon=west_max; - } + while (west_max<0) + west_max+=360; - if (min_lat!=0 && min_lon!=0 && max_lat!=0 && max_lon!=0) - { - for (y=min_lon; y<=max_lon; y++) - for (x=min_lat; x<=max_lat; x++) - { - sprintf(string,"%u:%u:%u:%u",x, x+1, y, y+1); - LoadSDF(string); - } - } - } + while (west_max>=360) + west_max-=360; - if (LRmap) - { - /* "Ball park" estimates used to load any additional - SDF files required to conduct this analysis. */ + if (north_minmax_lat) + max_lat=north_max; - /** - tx_range=sqrt(5.0*tx_site[0].alt); - rx_range=sqrt(5.0*altitudeLR); - **/ + if (LonDiff(west_min,min_lon)<0.0) + min_lon=west_min; - /* deg_range determines the maximum - amount of topo data we read */ + if (LonDiff(west_max,max_lon)>0.0) + max_lon=west_max; + } - deg_range=(tx_range+rx_range)/69.0; - /* max_range sets the maximum size of the - analysis. A small, non-zero amount can - be used to shrink the size of the analysis - and limit the amount of topo data read by - SPLAT! A very large number will only increase - the width of the analysis, not the size of - the map. */ + /* Load the required SDF files */ - if (max_range==0.0) - max_range=tx_range+rx_range; + width=ReduceAngle(max_lon-min_lon); - if (max_range<(tx_range+rx_range)) - deg_range=max_range/69.0; + if ((max_lon-min_lon)<=180.0) + { + for (y=0; y<=width; y++) + for (x=min_lat; x<=max_lat; x++) + { + ymin=(int)(min_lon+(double)y); - /* Prevent the demand for a really wide coverage - from allocating more slots than are available - in memory. */ + while (ymin<0) + ymin+=360; - switch (MAXSLOTS) - { - case 2: deg_limit=0.25; - break; + while (ymin>=360) + ymin-=360; - case 4: deg_limit=0.5; - break; + ymax=ymin+1; - case 9: deg_limit=1.0; - break; + while (ymax<0) + ymax+=360; - case 16: deg_limit=2.0; - break; + while (ymax>=360) + ymax-=360; - case 25: deg_limit=3.0; + sprintf(string,"%d:%d:%d:%d",x, x+1, ymin, ymax); + LoadSDF(string); + } } - if (tx_site[0].lat<70.0) - deg_range_lon=deg_range/cos(deg2rad*tx_site[0].lat); else - deg_range_lon=deg_range/cos(deg2rad*70.0); - - /* Correct for squares in degrees not being square in miles */ - - if (deg_range>deg_limit) - deg_range=deg_limit; - - if (deg_range_lon>deg_limit) - deg_range_lon=deg_limit; - - north_min=(unsigned char)floor(tx_site[0].lat-deg_range); - north_max=(unsigned char)floor(tx_site[0].lat+deg_range); - west_min=(unsigned char)floor(tx_site[0].lon-deg_range_lon); - west_max=(unsigned char)floor(tx_site[0].lon+deg_range_lon); - - if (min_lat==0) - min_lat=north_min; - - else if (north_min=360) + ymin-=360; + + ymax=ymin+1; - if (north_max>max_lat) - max_lat=north_max; + while (ymax<0) + ymax+=360; - if (west_max>max_lon) - max_lon=west_max; + while (ymax>=360) + ymax-=360; - if (min_lat!=0 && min_lon!=0 && max_lat!=0 && max_lon!=0) - { - for (y=min_lon; y<=max_lon; y++) - for (x=min_lat; x<=max_lat; x++) - { - sprintf(string,"%u:%u:%u:%u",x, x+1, y, y+1); + sprintf(string,"%d:%d:%d:%d",x, x+1, ymin, ymax); LoadSDF(string); } } } + if (mapfile[0]) map=1; - if (coverage) + if (coverage | LRmap) { for (x=0; x