+ if (metric)
+ {
+ fprintf(fd,"Ground elevation: %.2f meters AMSL\n",METERS_PER_FOOT*GetElevation(rcvr));
+ fprintf(fd,"Antenna height: %.2f meters AGL / %.2f meters AMSL\n",METERS_PER_FOOT*rcvr.alt, METERS_PER_FOOT*(rcvr.alt+GetElevation(rcvr)));
+ }
+
+ else
+ {
+ 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));
+ }
+
+ haavt=haat(rcvr);
+
+ if (haavt>-4999.0)
+ {
+ if (metric)
+ fprintf(fd,"Antenna height above average terrain: %.2f meters\n",METERS_PER_FOOT*haavt);
+ else
+ fprintf(fd,"Antenna height above average terrain: %.2f feet\n",haavt);
+ }
+
+ azimuth=Azimuth(rcvr,xmtr);
+ angle1=ElevationAngle(rcvr,xmtr);
+ angle2=ElevationAngle2(rcvr,xmtr,earthradius);
+
+ if (metric)
+ fprintf(fd,"Distance to %s: %.2f kilometers\n",xmtr.name,KM_PER_MILE*distance);
+ else
+ fprintf(fd,"Distance to %s: %.2f miles\n",xmtr.name,distance);
+
+ fprintf(fd,"Azimuth to %s: %.2f degrees\n",xmtr.name,azimuth);
+
+ if (angle1>=0.0)
+ fprintf(fd,"Elevation to %s: %+.4f degrees\n",xmtr.name,angle1);
+
+ else
+ fprintf(fd,"Depression angle to %s: %+.4f degrees\n",xmtr.name,angle1);
+
+ if (angle1!=angle2)
+ {
+ if (angle2<0.0)
+ fprintf(fd,"Depression");
+ else
+ fprintf(fd,"Elevation");
+
+ fprintf(fd," angle to the first obstruction: %+.4f degrees\n",angle2);
+
+ }
+
+ fprintf(fd,"\n-------------------------------------------------------------------------\n\n");
+
+ if (report=='y')
+ {
+ /* Generate profile of the terrain. Create the path
+ from transmitter to receiver because that's the
+ way the original los() function did it, and going
+ the other way can yield slightly different results. */
+
+ ReadPath(xmtr,rcvr);
+ h_r=GetElevation(rcvr)+rcvr.alt+earthradius;
+ h_r_f1=h_r;
+ h_r_fpt6=h_r;
+ h_r_orig=h_r;
+ h_t=GetElevation(xmtr)+xmtr.alt+earthradius;
+ d_tx=5280.0*Distance(rcvr,xmtr);
+ cos_tx_angle=((h_r*h_r)+(d_tx*d_tx)-(h_t*h_t))/(2.0*h_r*d_tx);
+ cos_tx_angle_f1=cos_tx_angle;
+ cos_tx_angle_fpt6=cos_tx_angle;
+
+ if (f)
+ lambda=9.8425e8/(f*1e6);
+
+ /* At each point along the path calculate the cosine
+ of a sort of "inverse elevation angle" at the receiver.
+ From the antenna, 0 deg. looks at the ground, and 90 deg.
+ is parallel to the ground.
+
+ Start at the receiver. If this is the lowest antenna,
+ then terrain obstructions will be nearest to it. (Plus,
+ that's the way the original los() did it.)
+
+ Calculate cosines only. That's sufficient to compare
+ angles and it saves the extra computational burden of
+ acos(). However, note the inverted comparison: if
+ acos(A) > acos(B), then B > A. */
+
+ for (x=path.length-1; x>0; x--)
+ {
+ site_x.lat=path.lat[x];
+ site_x.lon=path.lon[x];
+ site_x.alt=0.0;
+
+ h_x=GetElevation(site_x)+earthradius;
+ d_x=5280.0*Distance(rcvr,site_x);
+
+ /* Deal with the LOS path first. */
+
+ cos_test_angle=((h_r*h_r)+(d_x*d_x)-(h_x*h_x))/(2.0*h_r*d_x);
+
+ if (cos_tx_angle>cos_test_angle)
+ {
+ if (h_r==h_r_orig)
+ fprintf(fd,"SPLAT! detected obstructions at:\n\n");
+
+ if (site_x.lat>=0.0)
+ {
+ if (metric)
+ fprintf(fd,"\t%.4f N, %.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));
+ else
+ fprintf(fd,"\t%.4f N, %.4f W, %5.2f miles, %6.2f feet AMSL\n",site_x.lat, site_x.lon, d_x/5280.0, h_x-earthradius);
+ }
+
+ else
+ {
+ if (metric)
+ fprintf(fd,"\t%.4f S, %.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));
+ else
+
+ fprintf(fd,"\t%.4f S, %.4f W, %5.2f miles, %6.2f feet AMSL\n",-site_x.lat, site_x.lon, d_x/5280.0, h_x-earthradius);
+ }
+ }
+
+ while (cos_tx_angle>cos_test_angle)
+ {
+ h_r+=1;
+ cos_test_angle=((h_r*h_r)+(d_x*d_x)-(h_x*h_x))/(2.0*h_r*d_x);
+ cos_tx_angle=((h_r*h_r)+(d_tx*d_tx)-(h_t*h_t))/(2.0*h_r*d_tx);
+ }
+
+ if (f)
+ {
+ /* Now clear the first Fresnel zone, but don't
+ clutter the obstruction report. */
+
+ 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);
+ h_los=sqrt(h_r_f1*h_r_f1+d_x*d_x-2*h_r_f1*d_x*cos_tx_angle_f1);
+ h_f=h_los-sqrt(lambda*d_x*(d_tx-d_x)/d_tx);
+
+ while (h_f<h_x)
+ {
+ h_r_f1+=1;
+ 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);
+ h_los=sqrt(h_r_f1*h_r_f1+d_x*d_x-2*h_r_f1*d_x*cos_tx_angle_f1);
+ h_f=h_los-sqrt(lambda*d_x*(d_tx-d_x)/d_tx);
+ }
+
+ /* And clear the 60% F1 zone. */
+
+ 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);
+ h_los=sqrt(h_r_fpt6*h_r_fpt6+d_x*d_x-2*h_r_fpt6*d_x*cos_tx_angle_fpt6);
+ h_f=h_los-0.6*sqrt(lambda*d_x*(d_tx-d_x)/d_tx);
+
+ while (h_f<h_x)
+ {
+ h_r_fpt6+=1;
+ 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);
+ h_los=sqrt(h_r_fpt6*h_r_fpt6+d_x*d_x-2*h_r_fpt6*d_x*cos_tx_angle_fpt6);
+ h_f=h_los-0.6*sqrt(lambda*d_x*(d_tx-d_x)/d_tx);
+ }
+ }
+ }
+
+ if (h_r>h_r_orig)
+ {
+ if (metric)
+ sprintf(string,"\nAntenna at %s must be raised to at least %.2f meters AGL\nto clear all obstructions detected by SPLAT!\n",rcvr.name, METERS_PER_FOOT*(h_r-GetElevation(rcvr)-earthradius));
+ else
+ sprintf(string,"\nAntenna at %s must be raised to at least %.2f feet AGL\nto clear all obstructions detected by SPLAT!\n",rcvr.name, h_r-GetElevation(rcvr)-earthradius);
+ }
+
+ else
+ sprintf(string,"\nNo obstructions to LOS path due to terrain were detected by SPLAT!\n");
+
+ if (f)
+ {
+ if (h_r_fpt6>h_r_orig)
+ {
+ if (metric)
+ sprintf(string_fpt6,"\nAntenna at %s must be raised to at least %.2f meters AGL\nto clear 60%c of the first Fresnel zone.\n",rcvr.name, METERS_PER_FOOT*(h_r_fpt6-GetElevation(rcvr)-earthradius),37);
+
+ else
+ sprintf(string_fpt6,"\nAntenna at %s must be raised to at least %.2f feet AGL\nto clear 60%c of the first Fresnel zone.\n",rcvr.name, h_r_fpt6-GetElevation(rcvr)-earthradius,37);
+ }
+
+ else
+ sprintf(string_fpt6,"\n60%c of the first Fresnel zone is clear.\n",37);
+
+ if (h_r_f1>h_r_orig)
+ {
+ if (metric)
+ sprintf(string_f1,"\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));
+
+ else
+ sprintf(string_f1,"\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);
+
+ }
+
+ else
+ sprintf(string_f1,"\nThe first Fresnel zone is clear.\n\n");
+ }
+ }
+
+ fprintf(fd,"%s",string);
+
+ if (f)
+ {
+ fprintf(fd,"%s",string_f1);
+ fprintf(fd,"%s",string_fpt6);
+ }
+
+ fclose(fd);
+
+ /* Display report summary on terminal */
+
+ /* Line-of-sight status */
+
+ fprintf(stdout,"%s",string);
+
+ if (f)
+ {
+ /* Fresnel zone status */
+
+ fprintf(stdout,"%s",string_f1);
+ fprintf(stdout,"%s",string_fpt6);
+ }
+
+ fprintf(stdout, "\nObstruction report written to: \"%s\"\n",report_name);
+
+ fflush(stdout);
+}
+
+void SiteReport(struct site xmtr)
+{
+ char report_name[80];
+ double terrain;
+ int x, azi;
+ FILE *fd;
+
+ sprintf(report_name,"%s-site_report.txt",xmtr.name);
+
+ for (x=0; report_name[x]!=0; x++)
+ if (report_name[x]==32 || report_name[x]==17 || report_name[x]==92 || report_name[x]==42 || report_name[x]==47)
+ report_name[x]='_';
+
+ fd=fopen(report_name,"w");
+
+ fprintf(fd,"\n\t--==[ SPLAT! v%s Site Analysis Report For: %s ]==--\n\n",splat_version,xmtr.name);
+
+ fprintf(fd,"---------------------------------------------------------------------------\n\n");
+
+ 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));
+
+ if (metric)
+ {
+ fprintf(fd,"Ground elevation: %.2f meters AMSL\n",METERS_PER_FOOT*GetElevation(xmtr));
+ fprintf(fd,"Antenna height: %.2f meters AGL / %.2f meters AMSL\n",METERS_PER_FOOT*xmtr.alt, METERS_PER_FOOT*(xmtr.alt+GetElevation(xmtr)));
+ }
+
+ else
+ {
+ 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));
+ }
+
+ terrain=haat(xmtr);
+
+ if (terrain>-4999.0)
+ {
+ if (metric)
+ fprintf(fd,"Antenna height above average terrain: %.2f meters\n\n",METERS_PER_FOOT*terrain);
+ else
+ fprintf(fd,"Antenna height above average terrain: %.2f feet\n\n",terrain);
+
+ /* Display the average terrain between 2 and 10 miles
+ from the transmitter site at azimuths of 0, 45, 90,
+ 135, 180, 225, 270, and 315 degrees. */
+
+ for (azi=0; azi<=315; azi+=45)
+ {
+ fprintf(fd,"Average terrain at %3d degrees azimuth: ",azi);
+ terrain=AverageTerrain(xmtr,(double)azi,2.0,10.0);
+
+ if (terrain>-4999.0)
+ {
+ if (metric)
+ fprintf(fd,"%.2f meters AMSL\n",METERS_PER_FOOT*terrain);
+ else
+ fprintf(fd,"%.2f feet AMSL\n",terrain);
+ }
+
+ else
+ fprintf(fd,"No terrain\n");
+ }
+ }
+
+ fprintf(fd,"\n---------------------------------------------------------------------------\n\n");
+ fclose(fd);
+ fprintf(stdout,"\nSite analysis report written to: \"%s\"\n",report_name);
+}
+
+void LoadTopoData(int max_lon, int min_lon, int max_lat, int min_lat)
+{
+ /* This function loads the SDF files required
+ to cover the limits of the region specified. */
+
+ int x, y, width, ymin, ymax;
+
+ width=ReduceAngle(max_lon-min_lon);
+
+ 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);
+
+ 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);
+ }
+ }
+
+ else
+ {
+ for (y=0; y<=width; y++)
+ for (x=min_lat; x<=max_lat; x++)
+ {
+ 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);
+ }
+ }
+}
+
+int LoadPLI(char *filename)
+{
+ int error=0, max_west, min_west, max_north, min_north;
+ char string[80], *pointer=NULL;
+ double latitude=0.0, longitude=0.0, azimuth=0.0, elevation=0.0,
+ loss=0.0;
+ FILE *fd;
+
+ fd=fopen(filename,"r");
+
+ if (fd!=NULL)
+ {
+ fgets(string,78,fd);
+ pointer=strchr(string,';');
+
+ if (pointer!=NULL)
+ *pointer=0;