/****************************************************************************
-* 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 *
* 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
*****************************************************************************/
#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;
-
-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];
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,
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);
y=(int)(1199.0*(lon-floor(lon)));
dem[indx].mask[x][y]|=value;
+
return (dem[indx].mask[x][y]);
}
+
else
return -1;
}
{
if (minlat==dem[indx].min_north && minlon==dem[indx].min_west)
{
+
elevation=3.28084*dem[indx].data[x][y];
found=1;
}
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));
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<ARRAYSIZE)
+ {
+ path.lat[c]=lat2;
+ path.lon[c]=lon2;
+ tempsite.lat=lat2;
+ tempsite.lon=lon2;
+ path.elevation[c]=GetElevation(tempsite);
+ path.distance[c]=distance;
+ c++;
+ }
}
/* Make sure exact destination point is recorded at path.length-1 */
- x1=(int)(1199.0*(destination.lat-floor(destination.lat)));
- y1=(int)(1199.0*(destination.lon-floor(destination.lon)));
-
- path.lat[c]=destination.lat;
- path.lon[c]=destination.lon;
- path.elevation[c]=GetElevation(destination);
- path.distance[c]=total_distance;
- c++;
+ if (c<ARRAYSIZE)
+ {
+ path.lat[c]=destination.lat;
+ path.lon[c]=destination.lon;
+ path.elevation[c]=GetElevation(destination);
+ path.distance[c]=total_distance;
+ c++;
+ }
- path.length=c;
+ if (c<ARRAYSIZE)
+ path.length=c;
+ else
+ path.length=ARRAYSIZE-1;
}
double AverageTerrain(struct site source, double azimuthx, double start_distance, double end_distance)
}
}
+float LonDiff(float lon1, float lon2)
+{
+ /* This function returns the short path longitudinal
+ difference between longitude1 and longitude2
+ as an angle between -180.0 and +180.0 degrees.
+ If lon1 is west of lon2, the result is positive.
+ If lon1 is east of lon2, the result is negative. */
+
+ float diff;
+
+ diff=lon1-lon2;
+
+ if (diff<=-180.0)
+ diff+=360.0;
+
+ if (diff>=180.0)
+ diff-=360.0;
+
+ return diff;
+}
+
void PlaceMarker(struct site location)
{
/* This function places text and marker data in the mask array
lat=location.lat;
lon=location.lon;
- if (lat<xmax && lat>xmin && lon<ymax && lon>ymin)
+
+ if (lat<xmax && lat>xmin && (LonDiff(lon,ymax)<0.0) && (LonDiff(lon,ymin)>0.0))
{
p1=1.0/1200.0;
p3=3.0/1200.0;
/* 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)
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 */
if (ok2print==0)
{
- if ((lon-label_length)>=ymin)
+ if (LonDiff(lon-label_length,ymin)>=0.0)
{
/* Position Text To The
Right Of The Marker */
coordinates that describe the placement of the text
on the map. */
- if (ok2print && textx!=0.0 && texty!=0.0)
+ if (ok2print)
{
/* Draw Text */
if (byte&c)
OrMask(x,y,2);
}
+
x-=p1;
y=texty;
}
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);
}
}
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. */
/* 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;
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;
for (indx=0, found=0; indx<MAXSLOTS && found==0; indx++)
{
- if (minlat!=0 && minlon!=0)
- {
- if (minlat==dem[indx].min_north && minlon==dem[indx].min_west && maxlat==dem[indx].max_north && maxlon==dem[indx].max_west)
- found=1;
- }
+ if (minlat==dem[indx].min_north && minlon==dem[indx].min_west && maxlat==dem[indx].max_north && maxlon==dem[indx].max_west)
+ found=1;
}
/* Is room available to load it? */
if (found==0)
{
for (indx=0, free_slot=0; indx<MAXSLOTS && free_slot==0; indx++)
- if (dem[indx].max_north==0 && dem[indx].max_west==0)
+ if (dem[indx].max_north==-90)
free_slot=1;
}
indx--;
- if (free_slot && found==0 && indx>=0 && indx<MAXSLOTS && minlat!=0 && minlon!=0)
+ if (free_slot && found==0 && indx>=0 && indx<MAXSLOTS)
{
- strncpy(path_plus_name,sdf_path,255);
- strncat(path_plus_name,sdf_file,255);
+ /* Search for SDF file in current working directory first */
+
+ strncpy(path_plus_name,sdf_file,255);
fd=fopen(path_plus_name,"rb");
+ if (fd==NULL)
+ {
+ /* Next, try loading SDF file from path specified
+ in $HOME/.splat_path file or by -d argument */
+
+ strncpy(path_plus_name,sdf_path,255);
+ strncat(path_plus_name,sdf_file,255);
+
+ fd=fopen(path_plus_name,"rb");
+ }
+
if (fd!=NULL)
{
fprintf(stdout,"Loading \"%s\" into slot %d...",path_plus_name,indx+1);
{
fgets(line,19,fd);
sscanf(line,"%d",&data);
+
dem[indx].data[x][y]=data;
if (data>dem[indx].max_el)
dem[indx].max_el=data;
- if (dem[indx].min_el==0)
+ if (data<dem[indx].min_el)
dem[indx].min_el=data;
- else
- {
- if (data<dem[indx].min_el)
- dem[indx].min_el=data;
- }
}
fclose(fd);
- if (min_elevation==0)
+ if (dem[indx].min_el<min_elevation)
min_elevation=dem[indx].min_el;
- else
- {
- if (dem[indx].min_el<min_elevation)
- min_elevation=dem[indx].min_el;
- }
-
if (dem[indx].max_el>max_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_north<min_north)
+ min_north=dem[indx].min_north;
+
+ if (max_west==-1)
+ max_west=dem[indx].max_west;
+
else
{
- if (dem[indx].min_north<min_north)
- min_north=dem[indx].min_north;
+ if (abs(dem[indx].max_west-max_west)<180)
+ {
+ if (dem[indx].max_west>max_west)
+ max_west=dem[indx].max_west;
+ }
+
+ else
+ {
+ if (dem[indx].max_west<max_west)
+ max_west=dem[indx].max_west;
+ }
}
- if (min_west==0)
+ if (min_west==360)
min_west=dem[indx].min_west;
+
else
{
- if (dem[indx].min_west<min_west)
- min_west=dem[indx].min_west;
+ if (abs(dem[indx].min_west-min_west)<180)
+ {
+ if (dem[indx].min_west<min_west)
+ min_west=dem[indx].min_west;
+ }
+
+ else
+ {
+ if (dem[indx].min_west>min_west)
+ min_west=dem[indx].min_west;
+ }
}
fprintf(stdout," Done!\n");
for (indx=0, found=0; indx<MAXSLOTS && found==0; indx++)
{
- if (minlat!=0 && minlon!=0)
- {
- if (minlat==dem[indx].min_north && minlon==dem[indx].min_west && maxlat==dem[indx].max_north && maxlon==dem[indx].max_west)
- found=1;
- }
+ if (minlat==dem[indx].min_north && minlon==dem[indx].min_west && maxlat==dem[indx].max_north && maxlon==dem[indx].max_west)
+ found=1;
}
/* Is room available to load it? */
if (found==0)
{
for (indx=0, free_slot=0; indx<MAXSLOTS && free_slot==0; indx++)
- if (dem[indx].max_north==0 && dem[indx].max_west==0)
+ if (dem[indx].max_north==-90)
free_slot=1;
}
indx--;
- if (free_slot && found==0 && indx>=0 && indx<MAXSLOTS && minlat!=0 && minlon!=0)
+ if (free_slot && found==0 && indx>=0 && indx<MAXSLOTS)
{
- strncpy(path_plus_name,sdf_path,255);
- strncat(path_plus_name,sdf_file,255);
+ /* Search for SDF file in current working directory first */
+
+ strncpy(path_plus_name,sdf_file,255);
fd=fopen(path_plus_name,"rb");
bzfd=BZ2_bzReadOpen(&bzerror,fd,0,0,NULL,0);
+ if (fd==NULL || bzerror!=BZ_OK)
+ {
+ /* Next, try loading SDF file from path specified
+ in $HOME/.splat_path file or by -d argument */
+
+ strncpy(path_plus_name,sdf_path,255);
+ strncat(path_plus_name,sdf_file,255);
+
+ fd=fopen(path_plus_name,"rb");
+ bzfd=BZ2_bzReadOpen(&bzerror,fd,0,0,NULL,0);
+ }
+
if (fd!=NULL && bzerror==BZ_OK)
{
fprintf(stdout,"Loading \"%s\" into slot %d...",path_plus_name,indx+1);
for (y=0; y<1200; y++)
{
sscanf(BZfgets(bzfd,20),"%d",&data);
+
dem[indx].data[x][y]=data;
if (data>dem[indx].max_el)
dem[indx].max_el=data;
- if (dem[indx].min_el==0)
+ if (data<dem[indx].min_el)
dem[indx].min_el=data;
- else
- {
- if (data<dem[indx].min_el)
- dem[indx].min_el=data;
- }
}
fclose(fd);
BZ2_bzReadClose(&bzerror,bzfd);
- if (min_elevation==0)
+ if (dem[indx].min_el<min_elevation)
min_elevation=dem[indx].min_el;
-
- else
- {
- if (dem[indx].min_el<min_elevation)
- min_elevation=dem[indx].min_el;
- }
if (dem[indx].max_el>max_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_north<min_north)
min_north=dem[indx].min_north;
+
+ if (max_west==-1)
+ max_west=dem[indx].max_west;
+
else
{
- if (dem[indx].min_north<min_north)
- min_north=dem[indx].min_north;
+ if (abs(dem[indx].max_west-max_west)<180)
+ {
+ if (dem[indx].max_west>max_west)
+ max_west=dem[indx].max_west;
+ }
+
+ else
+ {
+ if (dem[indx].max_west<max_west)
+ max_west=dem[indx].max_west;
+ }
}
- if (min_west==0)
+ if (min_west==360)
min_west=dem[indx].min_west;
+
else
{
- if (dem[indx].min_west<min_west)
- min_west=dem[indx].min_west;
+ if (abs(dem[indx].min_west-min_west)<180)
+ {
+ if (dem[indx].min_west<min_west)
+ min_west=dem[indx].min_west;
+ }
+
+ else
+ {
+ if (dem[indx].min_west>min_west)
+ min_west=dem[indx].min_west;
+ }
}
fprintf(stdout," Done!\n");
for (indx=0, found=0; indx<MAXSLOTS && found==0; indx++)
{
- if (minlat!=0 && minlon!=0)
- {
- if (minlat==dem[indx].min_north && minlon==dem[indx].min_west && maxlat==dem[indx].max_north && maxlon==dem[indx].max_west)
- found=1;
- }
+ if (minlat==dem[indx].min_north && minlon==dem[indx].min_west && maxlat==dem[indx].max_north && maxlon==dem[indx].max_west)
+ found=1;
}
/* Is room available to load it? */
if (found==0)
{
for (indx=0, free_slot=0; indx<MAXSLOTS && free_slot==0; indx++)
- if (dem[indx].max_north==0 && dem[indx].max_west==0)
+ if (dem[indx].max_north==-90)
free_slot=1;
}
indx--;
- if (free_slot && found==0 && indx>=0 && indx<MAXSLOTS && minlat!=0 && minlon!=0)
+ if (free_slot && found==0 && indx>=0 && indx<MAXSLOTS)
{
fprintf(stdout,"Region \"%s\" assumed as sea-level into slot %d...",name,indx+1);
fflush(stdout);
dem[indx].min_west=minlon;
dem[indx].max_north=maxlat;
+ /* Fill DEM with sea-level topography */
+
for (x=0; x<1200; x++)
for (y=0; y<1200; y++)
{
if (dem[indx].max_el>max_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_north<min_north)
+ min_north=dem[indx].min_north;
+
+ if (max_west==-1)
+ max_west=dem[indx].max_west;
+
else
{
- if (dem[indx].min_north<min_north)
- min_north=dem[indx].min_north;
+ if (abs(dem[indx].max_west-max_west)<180)
+ {
+ if (dem[indx].max_west>max_west)
+ max_west=dem[indx].max_west;
+ }
+
+ else
+ {
+ if (dem[indx].max_west<max_west)
+ max_west=dem[indx].max_west;
+ }
}
- if (min_west==0)
+ if (min_west==360)
min_west=dem[indx].min_west;
+
else
{
- if (dem[indx].min_west<min_west)
- min_west=dem[indx].min_west;
+ if (abs(dem[indx].min_west-min_west)<180)
+ {
+ if (dem[indx].min_west<min_west)
+ min_west=dem[indx].min_west;
+ }
+
+ else
+ {
+ if (dem[indx].min_west>min_west)
+ min_west=dem[indx].min_west;
+ }
}
fprintf(stdout," Done!\n");
do
{
sscanf(string,"%lf %lf", &lon1, &lat1);
-
+
lon0=fabs(lon0);
lon1=fabs(lon1);
fprintf(stdout,"Done!\n");
fflush(stdout);
}
+
else
fprintf(stderr,"*** ERROR: \"%s\": not found!\n",filename);
}
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
for (y=0; y<path.length; y++)
{
- /* Test this point only if it
- has not already been tested. */
+ /* Test this point only if it has not already been tested. */
if (GetMask(path.lat[y],path.lon[y])==0 && 0.04*y<=max_range)
{
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 */
if (loss>225.0)
loss=225.0;
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;
/* 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;
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;
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;
/* 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;
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;
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);
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<MAXSLOTS && found==0;)
found=1;
else
indx++;
+
if (found)
{
- x0=(int)((1199.0*(lat-floor(lat)))+0.5);
- y0=(int)((1199.0*(lon-floor(lon)))+0.5);
+ x0=(int)(1199.0*(lat-floor(lat)));
+ y0=(int)(1199.0*(lon-floor(lon)));
mask=dem[indx].mask[x0][y0];
}
}
}
+
+ else
+ {
+ /* We should never get here, but if */
+ /* we do, display the region as black */
+
+ fprintf(fd,"%c%c%c",0,0,0);
+ }
}
}
90 degrees from its representation in dem[][] so that north
points up and east points right in the image generated. */
- int indx, x, t, t2, 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 width, height, output;
+ unsigned char found, mask, cityorcounty;
+ int indx, x, y, t, t2, x0, y0, minlat, minlon, loss;
+ 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);
fprintf(stdout,"\nWriting \"%s\" (%ux%u pixmap image)... ",mapfile,width,height+30);
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<MAXSLOTS && found==0;)
indx++;
if (found)
{
- x0=(int)((1199.0*(lat-floor(lat)))+0.5);
- y0=(int)((1199.0*(lon-floor(lon)))+0.5);
+ x0=(int)(1199.0*(lat-floor(lat)));
+ y0=(int)(1199.0*(lon-floor(lon)));
mask=dem[indx].mask[x0][y0];
+ loss=70+(10*(int)((mask&248)>>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++)
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:
break;
default:
- /* Very Light Blue */
- fprintf(fd,"%c%c%c",244,244,255);
+ fprintf(fd,"%c%c%c",255,194,204);
}
}
}
{
/* 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;
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)
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));
/* 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));
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)
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));
/* 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));
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;
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));
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);
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; x<MAXSLOTS; x++)
{
- dem[x].min_el=0;
- dem[x].max_el=0;
- dem[x].min_north=0;
- dem[x].max_north=0;
- dem[x].min_west=0;
- dem[x].max_west=0;
+ dem[x].min_el=32768;
+ dem[x].max_el=-32768;
+ dem[x].min_north=90;
+ dem[x].max_north=-90;
+ dem[x].min_west=360;
+ dem[x].max_west=-1;
}
+
/* Scan for command line arguments */
for (x=1; x<=y; x++)
}
}
+ if (strcmp(argv[x],"-db")==0)
+ {
+ z=x+1;
+
+ if (z<=y && argv[z][0] && argv[z][0]!='-')
+ {
+ sscanf(argv[z],"%d",&maxdB);
+
+ maxdB=abs(maxdB);
+
+ if (maxdB<80)
+ maxdB=80;
+
+ if (maxdB>230)
+ maxdB=230;
+ }
+ }
+
if (strcmp(argv[x],"-p")==0)
{
z=x+1;
{
strncpy(rxfile,argv[z],253);
rx_site=LoadQTH(rxfile);
+ rxsite=1;
}
}
for (x=0, y=0; x<txsites; x++)
{
- if (tx_site[x].lat==0.0 && tx_site[x].lon==0.0)
+ if (tx_site[x].lat==91.0 && tx_site[x].lon==361.0)
{
fprintf(stderr,"\n*** ERROR: Transmitter site #%d not found!",x+1);
y++;
exit (-1);
}
- if ((coverage+LRmap)==0 && rx_site.lat==0.0 && rx_site.lon==0.0)
+ if ((coverage+LRmap)==0 && rx_site.lat==91.0 && rx_site.lon==361.0)
{
fprintf(stderr,"\n%c*** ERROR: No receiver site found or specified!\n\n",7);
exit (-1);
if (sdf_path[0]==0)
{
- sprintf(string,"%s/.splat_path",getenv("HOME"));
+ env=getenv("HOME");
+ sprintf(string,"%s/.splat_path",env);
fd=fopen(string,"r");
if (fd!=NULL)
x=0;
y=0;
- min_lat=0;
- max_lat=0;
- min_lon=0;
- max_lon=0;
+ min_lat=90;
+ max_lat=-90;
- rxlat=(unsigned char)floor(rx_site.lat);
- rxlon=(unsigned char)floor(rx_site.lon);
+ min_lon=(int)floor(tx_site[0].lon);
+ max_lon=(int)floor(tx_site[0].lon);
- if (rxlat!=0)
+ for (y=0, z=0; z<txsites; z++)
{
- if (min_lat==0)
- min_lat=rxlat;
+ txlat=(int)floor(tx_site[z].lat);
+ txlon=(int)floor(tx_site[z].lon);
- else if (rxlat<min_lat)
- min_lat=rxlat;
+ if (txlat<min_lat)
+ min_lat=txlat;
+
+ if (txlat>max_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 (rxlat<min_lat)
+ min_lat=rxlat;
- else if (rxlon<min_lon)
+ if (rxlat>max_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<txsites; z++)
+ width=ReduceAngle(max_lon-min_lon);
+
+ if ((max_lon-min_lon)<=180.0)
{
- txlat=(unsigned char)floor(tx_site[z].lat);
- txlon=(unsigned char)floor(tx_site[z].lon);
+ for (y=0; y<=width; y++)
+ for (x=min_lat; x<=max_lat; x++)
+ {
+ ymin=(int)(min_lon+(double)y);
- if (txlat!=0)
- {
- if (min_lat==0)
- min_lat=txlat;
+ while (ymin<0)
+ ymin+=360;
- else if (txlat<min_lat)
- min_lat=txlat;
- }
+ while (ymin>=360)
+ ymin-=360;
- if (txlon!=0)
- {
- if (min_lon==0)
- min_lon=txlon;
+ ymax=ymin+1;
- else if (txlon<min_lon)
- min_lon=txlon;
- }
+ while (ymax<0)
+ ymax+=360;
- if (txlat>max_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; z<txsites; z++)
{
/* "Ball park" estimates used to load any additional
SDF files required to conduct this analysis. */
tx_range=sqrt(1.5*(tx_site[z].alt+GetElevation(tx_site[z])));
- rx_range=sqrt(1.5*altitude);
+
+ if (LRmap)
+ rx_range=sqrt(1.5*altitudeLR);
+ else
+ rx_range=sqrt(1.5*altitude);
/* deg_range determines the maximum
amount of topo data we read */
if (deg_range_lon>deg_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<min_lat)
- min_lat=north_min;
+ west_min=(int)floor(tx_site[z].lon-deg_range_lon);
- if (min_lon==0)
- min_lon=west_min;
+ while (west_min<0)
+ west_min+=360;
- else if (west_min<min_lon)
- min_lon=west_min;
+ while (west_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_min<min_lat)
+ min_lat=north_min;
- tx_range=sqrt(1.5*(tx_site[0].alt+GetElevation(tx_site[0])));
- rx_range=sqrt(1.5*altitudeLR);
+ if (north_max>max_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<min_lat)
- min_lat=north_min;
+ {
+ for (y=0; y<=width; y++)
+ for (x=min_lat; x<=max_lat; x++)
+ {
+ ymin=(int)(max_lon+(double)y);
- if (min_lon==0)
- min_lon=west_min;
+ while (ymin<0)
+ ymin+=360;
- else if (west_min<min_lon)
- min_lon=west_min;
+ while (ymin>=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<txsites; x++)
{
- PlotCoverage(tx_site[x],altitude);
+ if (coverage)
+ PlotCoverage(tx_site[x],altitude);
+
+ if (LRmap)
+ PlotLRMap(tx_site[x],altitudeLR);
+
PlaceMarker(tx_site[x]);
if (report!='N')
map=1;
}
- else if (LRmap)
- {
- PlotLRMap(tx_site[0],altitudeLR);
- PlaceMarker(tx_site[0]);
-
- if (report!='N')
- SiteReport(tx_site[0]);
-
- map=1;
- }
-
- else
+ if (coverage==0 && LRmap==0)
{
PlaceMarker(rx_site);
return 0;
}
+