diff --git a/.txt b/.txt deleted file mode 100644 index 8a0e301..0000000 --- a/.txt +++ /dev/null @@ -1,32 +0,0 @@ - - --==[ Path Profile Analysis ]==-- - -Transmitter site: Tx -Site location: 50.0000 North / 350.0000 West -Ground elevation: 0.00 meters AMSL -Antenna height: 30.00 meters AGL / 30.00 meters AMSL -Distance to Rx: 5.00 kilometers -Azimuth to Rx: 89.97 degrees -Depression angle to Rx: -0.3433 degrees -Receiver site: Rx -Site location: 50.0000 North / 349.9300 West -Ground elevation: 0.00 meters AMSL -Antenna height: 2.00 meters AGL / 2.00 meters AMSL -Distance to Tx: 5.00 kilometers -Azimuth to Tx: 270.03 degrees -Elevation angle to Tx: +0.2984 degrees -Longley-Rice path calculation parameters used in this analysis: - -Earth's Dielectric Constant: 15.000 -Earth's Conductivity: 0.005 Siemens/meter -Atmospheric Bending Constant (N-units): 301.000 ppm -Frequency: 5900.000 MHz -Radio Climate: 5 (Continental Temperate) -Polarisation: 1 (Vertical) -Fraction of Situations: 50.0% -Fraction of Time: 50.0% - - - -Summary for the link between Tx and Rx: - diff --git a/CHANGELOG b/CHANGELOG index 341ff2f..5406fd7 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,4 +1,10 @@ -SIGNAL SERVER CHANGE LOG +SIGNAL SERVER CHANGELOG + +2.5 - 27 May 2015 +Code refactored by Andrew Clayton / ac000 with header files +New Makefile with c / c++ multi mode compilation +Single executable now with run time switch for HD mode +Models separated into directory 2.44 - 25 Mar 2015 Improved PPA text report to list other prop models diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..df5bf0a --- /dev/null +++ b/Makefile @@ -0,0 +1,51 @@ +SHELL = /bin/sh + +CC = gcc +CXX = g++ +CFLAGS = -Wall -O3 -s -ffast-amth +CXXFLAGS = -Wall -O3 -s -ffast-math +LIBS = -lm + +VPATH = models +objects = main.o cost.o ecc33.o ericsson.o fspl.o hata.o itwom3.0.o \ + los.o sui.o inputs.o outputs.o + +GCC_MAJOR := $(shell $(CXX) -dumpversion 2>&1 | cut -d . -f 1) +GCC_MINOR := $(shell $(CXX) -dumpversion 2>&1 | cut -d . -f 2) +GCC_VER_OK := $(shell test $(GCC_MAJOR) -ge 4 && \ + test $(GCC_MINOR) -ge 7 && \ + echo 1) + +ifneq "$(GCC_VER_OK)" "1" +error: + @echo "Requires GCC version >= 4.7" + @exit +endif + +%.o : %.cc + @echo -e " CXX\t$@" + @$ $(CXX) $(CXXFLAGS) -c $< + +%.o : %.c + @echo -e " CC\t$@" + @$ $(CC) $(CFLAGS) -c $< + +signalserver: $(objects) + @echo -e " LNK\t$@" + @$(CXX) $(objects) -o $@ ${LIBS} + @echo -e " SYMLNK\tsignalserverHD -> $@" + @ln -sf $@ signalserverHD + +main.o: main.cc common.h inputs.hh outputs.hh itwom3.0.hh los.hh + +inputs.o: inputs.cc common.h main.hh + +outputs.o: outputs.cc common.h inputs.hh main.hh cost.hh ecc33.hh ericsson.hh \ + fspl.hh hata.hh itwom3.0.hh sui.hh + +los.o: los.cc common.h main.hh cost.hh ecc33.hh ericsson.hh fspl.hh hata.hh \ + itwom3.0.hh sui.hh + +.PHONY: clean +clean: + rm -f $(objects) signalserver signalserverHD diff --git a/README.md b/README.md index 3f8ba9a..d3275ea 100644 --- a/README.md +++ b/README.md @@ -20,15 +20,7 @@ * * \****************************************************************************/ -/* -REQUIRES GCC >= 4.7 -90m mode -g++ -Wall -Ofast -s -lm itwom3.0.cpp models.cpp main.cpp -o signalserver -30m HD mode -g++ -Wall -Ofast -s -lm itwom3.0.cpp models.cpp main.cpp -DHD -o signalserverHD -*/ - - -- Signal Server 2.41 -- + -- Signal Server 2.5 -- Compiled for 64 tiles at 1200 pixels/degree -d Directory containing .sdf tiles diff --git a/_curvature b/_curvature deleted file mode 100644 index b151541..0000000 --- a/_curvature +++ /dev/null @@ -1,18 +0,0 @@ -0.000 -10.000 -0.057 -11.490 -0.114 -12.981 -0.172 -14.473 -0.229 -15.965 -0.286 -17.457 -0.343 -18.950 -0.401 -20.444 -0.458 -21.938 -0.515 -23.432 -0.572 -24.927 -0.630 -26.423 -0.687 -27.919 -0.744 -29.415 -0.801 -30.913 -0.858 -32.410 -0.916 -33.908 -0.973 -35.407 diff --git a/_fresnel b/_fresnel deleted file mode 100644 index 46dcfaa..0000000 --- a/_fresnel +++ /dev/null @@ -1,19 +0,0 @@ -0.000 -0.000 -0.057 -4.240 -0.114 -5.811 -0.172 -6.882 -0.229 -7.666 -0.286 -8.245 -0.343 -8.660 -0.401 -8.934 -0.458 -9.081 -0.515 -9.105 -0.572 -9.008 -0.630 -8.786 -0.687 -8.430 -0.744 -7.920 -0.801 -7.226 -0.858 -6.284 -0.916 -4.959 -0.973 -2.736 -0.996 0.000 \ No newline at end of file diff --git a/_fresnel60 b/_fresnel60 deleted file mode 100644 index 5dd24c0..0000000 --- a/_fresnel60 +++ /dev/null @@ -1,19 +0,0 @@ -0.000 -0.000 -0.057 -2.544 -0.114 -3.487 -0.172 -4.129 -0.229 -4.600 -0.286 -4.947 -0.343 -5.196 -0.401 -5.361 -0.458 -5.448 -0.515 -5.463 -0.572 -5.405 -0.630 -5.272 -0.687 -5.058 -0.744 -4.752 -0.801 -4.335 -0.858 -3.771 -0.916 -2.975 -0.973 -1.642 -0.996 0.000 \ No newline at end of file diff --git a/_profile b/_profile deleted file mode 100644 index ecf8cdc..0000000 --- a/_profile +++ /dev/null @@ -1 +0,0 @@ -0.996 0.000 \ No newline at end of file diff --git a/_reference b/_reference deleted file mode 100644 index 9480292..0000000 --- a/_reference +++ /dev/null @@ -1,19 +0,0 @@ -0.000 0.000 -0.057 0.000 -0.114 0.000 -0.172 0.000 -0.229 0.000 -0.286 0.000 -0.343 0.000 -0.401 0.000 -0.458 0.000 -0.515 0.000 -0.572 0.000 -0.630 0.000 -0.687 0.000 -0.744 0.000 -0.801 0.000 -0.858 0.000 -0.916 0.000 -0.973 0.000 -0.996 0.000 \ No newline at end of file diff --git a/build.sh b/build.sh deleted file mode 100755 index d060e04..0000000 --- a/build.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash - -rm -f signalserver -rm -f signalserverHD -g++ -Wall -O3 -s -lm -fomit-frame-pointer itwom3.0.cpp models.cpp main.cpp -o signalserver -g++ -Wall -O3 -s -lm -fomit-frame-pointer itwom3.0.cpp models.cpp main.cpp -DHD -o signalserverHD -./signalserver diff --git a/common.h b/common.h new file mode 100644 index 0000000..354a0df --- /dev/null +++ b/common.h @@ -0,0 +1,119 @@ +#ifndef _COMMON_H_ +#define _COMMON_H_ + +#define GAMMA 2.5 + +#ifndef PI + #define PI 3.141592653589793 +#endif + +#ifndef TWOPI + #define TWOPI 6.283185307179586 +#endif + +#ifndef HALFPI + #define HALFPI 1.570796326794896 +#endif + +#define DEG2RAD 1.74532925199e-02 +#define EARTHRADIUS 20902230.97 +#define METERS_PER_MILE 1609.344 +#define METERS_PER_FOOT 0.3048 +#define KM_PER_MILE 1.609344 +#define FOUR_THIRDS 1.3333333333333 + +struct dem { + int min_north; + int max_north; + int min_west; + int max_west; + int max_el; + int min_el; + short **data; + unsigned char **mask; + unsigned char **signal; +}; + +struct site { + double lat; + double lon; + float alt; + char name[50]; + char filename[255]; +}; + +struct path { + double *lat; + double *lon; + double *elevation; + double *distance; + int length; +}; + +struct LR { + double eps_dielect; + double sgm_conductivity; + double eno_ns_surfref; + double frq_mhz; + double conf; + double rel; + double erp; + int radio_climate; + int pol; + float antenna_pattern[361][1001]; +}; + +struct region { + unsigned char color[128][3]; + int level[128]; + int levels; +}; + +extern int MAXPAGES; +extern int ARRAYSIZE; +extern int IPPD; + +extern int min_north; +extern int max_north; +extern int min_west; +extern int max_west; +extern int ippd; +extern int mpi; +extern int max_elevation; +extern int min_elevation; +extern int contour_threshold; +extern int loops; +extern int jgets; + +extern double earthradius; +extern double north; +extern double east; +extern double south; +extern double west; +extern double max_range; +extern double dpp; +extern double ppd; +extern double fzone_clearance; +extern double clutter; +extern double dBm; +extern double loss; +extern double field_strength; +extern double *elev; + +extern char string[]; +extern char sdf_path[]; +extern char gpsav; + +extern unsigned char got_elevation_pattern; +extern unsigned char got_azimuth_pattern; +extern unsigned char metric; +extern unsigned char dbm; + +extern struct dem *dem; +extern struct path path; +extern struct LR LR; +extern struct region region; + +extern int debug; + +#endif /* _COMMON_H_ */ diff --git a/cost.cpp b/cost.cpp deleted file mode 100644 index 2ac3e51..0000000 --- a/cost.cpp +++ /dev/null @@ -1,41 +0,0 @@ -/***************************************************************************** -* COST231-HATA MODEL for Signal Server by Alex Farrant * -* 30 December 2013 * -* 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 * -* version. * -* * -* This program is distributed in the hope that it will useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * -* for more details. * -* */ - -#include -#include -#include - -using namespace std; - -double CostHataLinkdB(float f,float h_B, float h_M, float d){ -/* -COST HATA URBAN model -Frequency 1500 to 2000MHz -h_B = Base station height 30 to 200m -h_M = Mobile station height 1 to 10m -Distance 1-20km -*/ - - int C = 0; // 0dB for suburban - - float lh_M = log10(11.75*h_M); - float C_H = 3.2*lh_M*lh_M-4.97; - - float logf = log10(f); - - double dbloss = 46.3 + (33.9 * logf) - (13.82 * log10(h_B)) - C_H + (44.9 - 6.55 * log10(h_B)) * log10(d) + C; - - - return dbloss; -} diff --git a/hata.cpp b/hata.cpp deleted file mode 100644 index 43ff972..0000000 --- a/hata.cpp +++ /dev/null @@ -1,110 +0,0 @@ -/***************************************************************************** -* HATA MODEL for Signal Server by Alex Farrant * -* 30 December 2013 * -* 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 * -* version. * -* * -* This program is distributed in the hope that it will useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * -* for more details. * -* */ - -#include -#include -#include -#include -#include - -using namespace std; - -#define PI 3.14159265 - -/* Acute Angle from Rx point to an obstacle of height (opp) and distance (adj) */ -double incidenceAngle(double opp, double adj){ -return atan2(opp,adj) * 180 / PI; -} - -/* -Knife edge diffraction: -This custom function adds to the overall path loss based upon the obstacle -angle (from receive point) and the frequency. Loss increases with angle and frequency. -This is not a recognised formula like Huygens, rather it is a -compromise for increased speed which adds a realistic diffraction effect. -*/ -double ked(double freq, double elev[], double rxh, double dkm){ -double obh,obd,rxobaoi=0,d; - -obh=0; // Obstacle height -obd=0; // Obstacle distance - -dkm=dkm*1000; // KM to metres - - // walk along path - for(int n=2;n<(dkm/elev[1]);n++){ - - d = (n-2)*elev[1]; // no of points * delta = km - - //Find dip(s) - if(elev[n]<(obh+20)){ - - // Angle from Rx point to obstacle - rxobaoi = incidenceAngle((obh-(elev[n]+rxh)),d-obd); - } else{ - // Line of sight or higher - rxobaoi=0; - } - - //note the highest point - if(elev[n]>obh){ - obh=elev[n]; - obd=d; - } - - } - -if(rxobaoi >= 0){ -return rxobaoi / (300/freq); // Diffraction angle divided by wavelength (m) -}else{ -return 0; -} - -} - -double HataLinkdB(float f,float h_B, float h_M, float d, int mode){ -/* -HATA URBAN model for cellular planning -Frequency (MHz) 150 to 1500MHz -Base station height 30-200m -Mobile station height 1-10m -Distance 1-20km - -mode 1 = URBAN -mode 2 = SUBURBAN -mode 3 = OPEN -*/ - - float lh_M = log10(11.75*h_M); - float C_H = 3.2*lh_M*lh_M-4.97; - - float logf = log10(f); - - float L_u = 69.55 + 26.16*logf - 13.82*log10(h_B) - C_H + (44.9 - 6.55*log10(h_B))*log10(d); - - if(!mode || mode==1){ - return L_u; //URBAN - } - - if(mode==2){ //SUBURBAN - float logf_28 = log10(f/28); - return L_u - 2*logf_28*logf_28 - 5.4; - } - - if(mode==3){ //OPEN - return L_u - 4.78*logf*logf + 18.33*logf - 40.94; - } - - return 0; -} \ No newline at end of file diff --git a/inputs.cc b/inputs.cc new file mode 100644 index 0000000..f9cb009 --- /dev/null +++ b/inputs.cc @@ -0,0 +1,1390 @@ +#include +#include +#include +#include +#include + +#include "common.h" +#include "main.hh" + +int LoadSDF_SDF(char *name, int winfiles) +{ + /* This function reads uncompressed ss Data Files (.sdf) + containing digital elevation model data into memory. + Elevation data, maximum and minimum elevations, and + quadrangle limits are stored in the first available + dem[] structure. */ + + int x, y, data = 0, indx, minlat, minlon, maxlat, maxlon, j; + char found, free_page = 0, line[20], jline[20], sdf_file[255], + path_plus_name[255], *junk = NULL; + + FILE *fd; + + for (x = 0; name[x] != '.' && name[x] != 0 && x < 250; x++) + sdf_file[x] = name[x]; + + sdf_file[x] = 0; + + /* Parse filename for minimum latitude and longitude values */ + if (winfiles == 1) { + sscanf(sdf_file, "%d=%d=%d=%d", &minlat, &maxlat, &minlon, + &maxlon); + } else { + sscanf(sdf_file, "%d:%d:%d:%d", &minlat, &maxlat, &minlon, + &maxlon); + } + + sdf_file[x] = '.'; + sdf_file[x + 1] = 's'; + sdf_file[x + 2] = 'd'; + sdf_file[x + 3] = 'f'; + sdf_file[x + 4] = 0; + + /* Is it already in memory? */ + + for (indx = 0, found = 0; indx < MAXPAGES && found == 0; indx++) { + 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_page = 0; indx < MAXPAGES && free_page == 0; + indx++) + if (dem[indx].max_north == -90) + free_page = 1; + } + + indx--; + + if (free_page && found == 0 && indx >= 0 && indx < MAXPAGES) { + /* 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/.ss_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) { + if (debug == 1) { + fprintf(stdout, + "Loading \"%s\" into page %d...", + path_plus_name, indx + 1); + fflush(stdout); + } + + if (fgets(line, 19, fd) != NULL) { + sscanf(line, "%d", &dem[indx].max_west); + } + + if (fgets(line, 19, fd) != NULL) { + sscanf(line, "%d", &dem[indx].min_north); + } + + if (fgets(line, 19, fd) != NULL) { + sscanf(line, "%d", &dem[indx].min_west); + } + + if (fgets(line, 19, fd) != NULL) { + sscanf(line, "%d", &dem[indx].max_north); + } + /* + Here X lines of DEM will be read until IPPD is reached. + Each .sdf tile contains 1200x1200 = 1.44M 'points' + Each point is sampled for 1200 resolution! + */ + for (x = 0; x < ippd; x++) { + for (y = 0; y < ippd; y++) { + + for (j = 0; j < jgets; j++) { + junk = fgets(jline, 19, fd); + } + + if (fgets(line, 19, fd) != NULL) { + data = atoi(line); + } + + dem[indx].data[x][y] = data; + dem[indx].signal[x][y] = 0; + dem[indx].mask[x][y] = 0; + + if (data > dem[indx].max_el) + dem[indx].max_el = data; + + if (data < dem[indx].min_el) + dem[indx].min_el = data; + + } + + if (ippd == 600) { + for (j = 0; j < IPPD; j++) { + junk = fgets(jline, 19, fd); + } + } + if (ippd == 300) { + for (j = 0; j < IPPD; j++) { + junk = fgets(jline, 19, fd); + junk = fgets(jline, 19, fd); + junk = fgets(jline, 19, fd); + + } + } + } + + fclose(fd); + + 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 (max_north == -90) + max_north = dem[indx].max_north; + + else if (dem[indx].max_north > max_north) + max_north = dem[indx].max_north; + + 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 (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 == 360) + min_west = dem[indx].min_west; + + else { + if (fabs(dem[indx].min_west - min_west) < 180.0) { + 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; + } + } + + return 1; + } + + else + return -1; + } + + else + return 0; +} + +char LoadSDF(char *name, int winfiles) +{ + /* This function loads the requested SDF file from the filesystem. + It first tries to invoke the LoadSDF_SDF() function to load an + uncompressed SDF file (since uncompressed files load slightly + faster). If that attempt fails, then it tries to load a + compressed SDF file by invoking the LoadSDF_BZ() function. + If that fails, then we can assume that no elevation data + exists for the region requested, and that the region + requested must be entirely over water. */ + + int x, y, indx, minlat, minlon, maxlat, maxlon; + char found, free_page = 0; + int return_value = -1; + + return_value = LoadSDF_SDF(name, winfiles); + + /* If neither format can be found, then assume the area is water. */ + + if (return_value == 0 || return_value == -1) { + + if (winfiles == 1) { + sscanf(name, "%d=%d=%d=%d", &minlat, &maxlat, &minlon, + &maxlon); + } else { + sscanf(name, "%d:%d:%d:%d", &minlat, &maxlat, &minlon, + &maxlon); + } + /* Is it already in memory? */ + + for (indx = 0, found = 0; indx < MAXPAGES && found == 0; indx++) { + 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_page = 0; + indx < MAXPAGES && free_page == 0; indx++) + if (dem[indx].max_north == -90) + free_page = 1; + } + + indx--; + + if (free_page && found == 0 && indx >= 0 && indx < MAXPAGES) { + if (debug == 1) { + fprintf(stdout, + "Region \"%s\" assumed as sea-level into page %d...", + name, indx + 1); + fflush(stdout); + } + + dem[indx].max_west = maxlon; + dem[indx].min_north = minlat; + dem[indx].min_west = minlon; + dem[indx].max_north = maxlat; + + /* Fill DEM with sea-level topography */ + + for (x = 0; x < ippd; x++) + for (y = 0; y < ippd; y++) { + dem[indx].data[x][y] = 0; + dem[indx].signal[x][y] = 0; + dem[indx].mask[x][y] = 0; + + if (dem[indx].min_el > 0) + dem[indx].min_el = 0; + } + + 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 (max_north == -90) + max_north = dem[indx].max_north; + + else if (dem[indx].max_north > max_north) + max_north = dem[indx].max_north; + + 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 (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 == 360) + min_west = dem[indx].min_west; + + else { + 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; + } + } + + return_value = 1; + } + } + + return return_value; +} + +void LoadPAT(char *filename) +{ + /* This function reads and processes antenna pattern (.az + and .el) files that correspond in name to previously + loaded ss .lrp files. */ + + int a, b, w, x, y, z, last_index, next_index, span; + char string[255], azfile[255], elfile[255], *pointer = NULL; + float az, xx, elevation, amplitude, rotation, valid1, valid2, + delta, azimuth[361], azimuth_pattern[361], el_pattern[10001], + elevation_pattern[361][1001], slant_angle[361], tilt, + mechanical_tilt = 0.0, tilt_azimuth, tilt_increment, sum; + FILE *fd = NULL; + unsigned char read_count[10001]; + + for (x = 0; filename[x] != '.' && filename[x] != 0 && x < 250; x++) { + azfile[x] = filename[x]; + elfile[x] = filename[x]; + } + + azfile[x] = '.'; + azfile[x + 1] = 'a'; + azfile[x + 2] = 'z'; + azfile[x + 3] = 0; + + elfile[x] = '.'; + elfile[x + 1] = 'e'; + elfile[x + 2] = 'l'; + elfile[x + 3] = 0; + + rotation = 0.0; + + got_azimuth_pattern = 0; + got_elevation_pattern = 0; + + /* Load .az antenna pattern file */ + + fd = fopen(azfile, "r"); + + if (fd != NULL) { + /* Clear azimuth pattern array */ + + for (x = 0; x <= 360; x++) { + azimuth[x] = 0.0; + read_count[x] = 0; + } + + /* Read azimuth pattern rotation + in degrees measured clockwise + from true North. */ + + if (fgets(string, 254, fd) == NULL) { + //fprintf(stdout,"Azimuth read error\n"); + //exit(0); + } + pointer = strchr(string, ';'); + + if (pointer != NULL) + *pointer = 0; + + sscanf(string, "%f", &rotation); + + /* Read azimuth (degrees) and corresponding + normalized field radiation pattern amplitude + (0.0 to 1.0) until EOF is reached. */ + + if (fgets(string, 254, fd) == NULL) { + //fprintf(stdout,"Azimuth read error\n"); + //exit(0); + } + pointer = strchr(string, ';'); + + if (pointer != NULL) + *pointer = 0; + + sscanf(string, "%f %f", &az, &litude); + + do { + x = (int)rintf(az); + + if (x >= 0 && x <= 360 && fd != NULL) { + azimuth[x] += amplitude; + read_count[x]++; + } + + if (fgets(string, 254, fd) == NULL) { + //fprintf(stdout,"Azimuth read error\n"); + // exit(0); + } + pointer = strchr(string, ';'); + + if (pointer != NULL) + *pointer = 0; + + sscanf(string, "%f %f", &az, &litude); + + } while (feof(fd) == 0); + + fclose(fd); + + /* Handle 0=360 degree ambiguity */ + + if ((read_count[0] == 0) && (read_count[360] != 0)) { + read_count[0] = read_count[360]; + azimuth[0] = azimuth[360]; + } + + if ((read_count[0] != 0) && (read_count[360] == 0)) { + read_count[360] = read_count[0]; + azimuth[360] = azimuth[0]; + } + + /* Average pattern values in case more than + one was read for each degree of azimuth. */ + + for (x = 0; x <= 360; x++) { + if (read_count[x] > 1) + azimuth[x] /= (float)read_count[x]; + } + + /* Interpolate missing azimuths + to completely fill the array */ + + last_index = -1; + next_index = -1; + + for (x = 0; x <= 360; x++) { + if (read_count[x] != 0) { + if (last_index == -1) + last_index = x; + else + next_index = x; + } + + if (last_index != -1 && next_index != -1) { + valid1 = azimuth[last_index]; + valid2 = azimuth[next_index]; + + span = next_index - last_index; + delta = (valid2 - valid1) / (float)span; + + for (y = last_index + 1; y < next_index; y++) + azimuth[y] = azimuth[y - 1] + delta; + + last_index = y; + next_index = -1; + } + } + + /* Perform azimuth pattern rotation + and load azimuth_pattern[361] with + azimuth pattern data in its final form. */ + + for (x = 0; x < 360; x++) { + y = x + (int)rintf(rotation); + + if (y >= 360) + y -= 360; + + azimuth_pattern[y] = azimuth[x]; + } + + azimuth_pattern[360] = azimuth_pattern[0]; + + got_azimuth_pattern = 255; + } + + /* Read and process .el file */ + + fd = fopen(elfile, "r"); + + if (fd != NULL) { + for (x = 0; x <= 10000; x++) { + el_pattern[x] = 0.0; + read_count[x] = 0; + } + + /* Read mechanical tilt (degrees) and + tilt azimuth in degrees measured + clockwise from true North. */ + + if (fgets(string, 254, fd) == NULL) { + //fprintf(stdout,"Tilt read error\n"); + //exit(0); + } + pointer = strchr(string, ';'); + + if (pointer != NULL) + *pointer = 0; + + sscanf(string, "%f %f", &mechanical_tilt, &tilt_azimuth); + + /* Read elevation (degrees) and corresponding + normalized field radiation pattern amplitude + (0.0 to 1.0) until EOF is reached. */ + + if (fgets(string, 254, fd) == NULL) { + //fprintf(stdout,"Ant elevation read error\n"); + //exit(0); + } + pointer = strchr(string, ';'); + + if (pointer != NULL) + *pointer = 0; + + sscanf(string, "%f %f", &elevation, &litude); + + while (feof(fd) == 0) { + /* Read in normalized radiated field values + for every 0.01 degrees of elevation between + -10.0 and +90.0 degrees */ + + x = (int)rintf(100.0 * (elevation + 10.0)); + + if (x >= 0 && x <= 10000) { + el_pattern[x] += amplitude; + read_count[x]++; + } + + if (fgets(string, 254, fd) != NULL) { + pointer = strchr(string, ';'); + } + if (pointer != NULL) + *pointer = 0; + + sscanf(string, "%f %f", &elevation, &litude); + } + + fclose(fd); + + /* Average the field values in case more than + one was read for each 0.01 degrees of elevation. */ + + for (x = 0; x <= 10000; x++) { + if (read_count[x] > 1) + el_pattern[x] /= (float)read_count[x]; + } + + /* Interpolate between missing elevations (if + any) to completely fill the array and provide + radiated field values for every 0.01 degrees of + elevation. */ + + last_index = -1; + next_index = -1; + + for (x = 0; x <= 10000; x++) { + if (read_count[x] != 0) { + if (last_index == -1) + last_index = x; + else + next_index = x; + } + + if (last_index != -1 && next_index != -1) { + valid1 = el_pattern[last_index]; + valid2 = el_pattern[next_index]; + + span = next_index - last_index; + delta = (valid2 - valid1) / (float)span; + + for (y = last_index + 1; y < next_index; y++) + el_pattern[y] = + el_pattern[y - 1] + delta; + + last_index = y; + next_index = -1; + } + } + + /* Fill slant_angle[] array with offset angles based + on the antenna's mechanical beam tilt (if any) + and tilt direction (azimuth). */ + + if (mechanical_tilt == 0.0) { + for (x = 0; x <= 360; x++) + slant_angle[x] = 0.0; + } + + else { + tilt_increment = mechanical_tilt / 90.0; + + for (x = 0; x <= 360; x++) { + xx = (float)x; + y = (int)rintf(tilt_azimuth + xx); + + while (y >= 360) + y -= 360; + + while (y < 0) + y += 360; + + if (x <= 180) + slant_angle[y] = + -(tilt_increment * (90.0 - xx)); + + if (x > 180) + slant_angle[y] = + -(tilt_increment * (xx - 270.0)); + } + } + + slant_angle[360] = slant_angle[0]; /* 360 degree wrap-around */ + + for (w = 0; w <= 360; w++) { + tilt = slant_angle[w]; + + /** Convert tilt angle to + an array index offset **/ + + y = (int)rintf(100.0 * tilt); + + /* Copy shifted el_pattern[10001] field + values into elevation_pattern[361][1001] + at the corresponding azimuth, downsampling + (averaging) along the way in chunks of 10. */ + + for (x = y, z = 0; z <= 1000; x += 10, z++) { + for (sum = 0.0, a = 0; a < 10; a++) { + b = a + x; + + if (b >= 0 && b <= 10000) + sum += el_pattern[b]; + if (b < 0) + sum += el_pattern[0]; + if (b > 10000) + sum += el_pattern[10000]; + } + + elevation_pattern[w][z] = sum / 10.0; + } + } + + got_elevation_pattern = 255; + } + + for (x = 0; x <= 360; x++) { + for (y = 0; y <= 1000; y++) { + if (got_elevation_pattern) + elevation = elevation_pattern[x][y]; + else + elevation = 1.0; + + if (got_azimuth_pattern) + az = azimuth_pattern[x]; + else + az = 1.0; + + LR.antenna_pattern[x][y] = az * elevation; + } + } +} + +void LoadSignalColors(struct site xmtr) +{ + int x, y, ok, val[4]; + char filename[255], string[80], *pointer = NULL, *s = NULL; + FILE *fd = NULL; + + for (x = 0; xmtr.filename[x] != '.' && xmtr.filename[x] != 0 && x < 250; + x++) + filename[x] = xmtr.filename[x]; + + filename[x] = '.'; + filename[x + 1] = 's'; + filename[x + 2] = 'c'; + filename[x + 3] = 'f'; + filename[x + 4] = 0; + + /* Default values */ + + region.level[0] = 128; + region.color[0][0] = 255; + region.color[0][1] = 0; + region.color[0][2] = 0; + + region.level[1] = 118; + region.color[1][0] = 255; + region.color[1][1] = 165; + region.color[1][2] = 0; + + region.level[2] = 108; + region.color[2][0] = 255; + region.color[2][1] = 206; + region.color[2][2] = 0; + + region.level[3] = 98; + region.color[3][0] = 255; + region.color[3][1] = 255; + region.color[3][2] = 0; + + region.level[4] = 88; + region.color[4][0] = 184; + region.color[4][1] = 255; + region.color[4][2] = 0; + + region.level[5] = 78; + region.color[5][0] = 0; + region.color[5][1] = 255; + region.color[5][2] = 0; + + region.level[6] = 68; + region.color[6][0] = 0; + region.color[6][1] = 208; + region.color[6][2] = 0; + + region.level[7] = 58; + region.color[7][0] = 0; + region.color[7][1] = 196; + region.color[7][2] = 196; + + region.level[8] = 48; + region.color[8][0] = 0; + region.color[8][1] = 148; + region.color[8][2] = 255; + + region.level[9] = 38; + region.color[9][0] = 80; + region.color[9][1] = 80; + region.color[9][2] = 255; + + region.level[10] = 28; + region.color[10][0] = 0; + region.color[10][1] = 38; + region.color[10][2] = 255; + + region.level[11] = 18; + region.color[11][0] = 142; + region.color[11][1] = 63; + region.color[11][2] = 255; + + region.level[12] = 8; + region.color[12][0] = 140; + region.color[12][1] = 0; + region.color[12][2] = 128; + + region.levels = 13; + + fd = fopen(filename, "r"); + + if (fd == NULL) + fd = fopen(filename, "r"); + + if (fd == NULL) { + fd = fopen(filename, "w"); + + for (x = 0; x < region.levels; x++) + fprintf(fd, "%3d: %3d, %3d, %3d\n", region.level[x], + region.color[x][0], region.color[x][1], + region.color[x][2]); + + fclose(fd); + } + + else { + x = 0; + s = fgets(string, 80, fd); + + while (x < 128 && feof(fd) == 0) { + pointer = strchr(string, ';'); + + if (pointer != NULL) + *pointer = 0; + + ok = sscanf(string, "%d: %d, %d, %d", &val[0], &val[1], + &val[2], &val[3]); + + if (ok == 4) { + for (y = 0; y < 4; y++) { + if (val[y] > 255) + val[y] = 255; + + if (val[y] < 0) + val[y] = 0; + } + + region.level[x] = val[0]; + region.color[x][0] = val[1]; + region.color[x][1] = val[2]; + region.color[x][2] = val[3]; + x++; + } + + s = fgets(string, 80, fd); + } + + fclose(fd); + region.levels = x; + } +} + +void LoadLossColors(struct site xmtr) +{ + int x, y, ok, val[4]; + char filename[255], string[80], *pointer = NULL, *s = NULL; + FILE *fd = NULL; + + for (x = 0; xmtr.filename[x] != '.' && xmtr.filename[x] != 0 && x < 250; + x++) + filename[x] = xmtr.filename[x]; + + filename[x] = '.'; + filename[x + 1] = 'l'; + filename[x + 2] = 'c'; + filename[x + 3] = 'f'; + filename[x + 4] = 0; + + /* Default values */ + + region.level[0] = 80; + region.color[0][0] = 255; + region.color[0][1] = 0; + region.color[0][2] = 0; + + region.level[1] = 90; + region.color[1][0] = 255; + region.color[1][1] = 128; + region.color[1][2] = 0; + + region.level[2] = 100; + region.color[2][0] = 255; + region.color[2][1] = 165; + region.color[2][2] = 0; + + region.level[3] = 110; + region.color[3][0] = 255; + region.color[3][1] = 206; + region.color[3][2] = 0; + + region.level[4] = 120; + region.color[4][0] = 255; + region.color[4][1] = 255; + region.color[4][2] = 0; + + region.level[5] = 130; + region.color[5][0] = 184; + region.color[5][1] = 255; + region.color[5][2] = 0; + + region.level[6] = 140; + region.color[6][0] = 0; + region.color[6][1] = 255; + region.color[6][2] = 0; + + region.level[7] = 150; + region.color[7][0] = 0; + region.color[7][1] = 208; + region.color[7][2] = 0; + + region.level[8] = 160; + region.color[8][0] = 0; + region.color[8][1] = 196; + region.color[8][2] = 196; + + region.level[9] = 170; + region.color[9][0] = 0; + region.color[9][1] = 148; + region.color[9][2] = 255; + + region.level[10] = 180; + region.color[10][0] = 80; + region.color[10][1] = 80; + region.color[10][2] = 255; + + region.level[11] = 190; + region.color[11][0] = 0; + region.color[11][1] = 38; + region.color[11][2] = 255; + + region.level[12] = 200; + region.color[12][0] = 142; + region.color[12][1] = 63; + region.color[12][2] = 255; + + region.level[13] = 210; + region.color[13][0] = 196; + region.color[13][1] = 54; + region.color[13][2] = 255; + + region.level[14] = 220; + region.color[14][0] = 255; + region.color[14][1] = 0; + region.color[14][2] = 255; + + region.level[15] = 230; + region.color[15][0] = 255; + region.color[15][1] = 194; + region.color[15][2] = 204; + + region.levels = 16; + + fd = fopen(filename, "r"); + + if (fd == NULL) + fd = fopen(filename, "r"); + + if (fd == NULL) { + fd = fopen(filename, "w"); + + for (x = 0; x < region.levels; x++) + fprintf(fd, "%3d: %3d, %3d, %3d\n", region.level[x], + region.color[x][0], region.color[x][1], + region.color[x][2]); + + fclose(fd); + } + + else { + x = 0; + s = fgets(string, 80, fd); + + while (x < 128 && feof(fd) == 0) { + pointer = strchr(string, ';'); + + if (pointer != NULL) + *pointer = 0; + + ok = sscanf(string, "%d: %d, %d, %d", &val[0], &val[1], + &val[2], &val[3]); + + if (ok == 4) { + for (y = 0; y < 4; y++) { + if (val[y] > 255) + val[y] = 255; + + if (val[y] < 0) + val[y] = 0; + } + + region.level[x] = val[0]; + region.color[x][0] = val[1]; + region.color[x][1] = val[2]; + region.color[x][2] = val[3]; + x++; + } + + s = fgets(string, 80, fd); + } + + fclose(fd); + region.levels = x; + } +} + +void LoadDBMColors(struct site xmtr) +{ + int x, y, ok, val[4]; + char filename[255], string[80], *pointer = NULL, *s = NULL; + FILE *fd = NULL; + + for (x = 0; xmtr.filename[x] != '.' && xmtr.filename[x] != 0 && x < 250; + x++) + filename[x] = xmtr.filename[x]; + + filename[x] = '.'; + filename[x + 1] = 'd'; + filename[x + 2] = 'c'; + filename[x + 3] = 'f'; + filename[x + 4] = 0; + + /* Default values */ + + region.level[0] = 0; + region.color[0][0] = 255; + region.color[0][1] = 0; + region.color[0][2] = 0; + + region.level[1] = -10; + region.color[1][0] = 255; + region.color[1][1] = 128; + region.color[1][2] = 0; + + region.level[2] = -20; + region.color[2][0] = 255; + region.color[2][1] = 165; + region.color[2][2] = 0; + + region.level[3] = -30; + region.color[3][0] = 255; + region.color[3][1] = 206; + region.color[3][2] = 0; + + region.level[4] = -40; + region.color[4][0] = 255; + region.color[4][1] = 255; + region.color[4][2] = 0; + + region.level[5] = -50; + region.color[5][0] = 184; + region.color[5][1] = 255; + region.color[5][2] = 0; + + region.level[6] = -60; + region.color[6][0] = 0; + region.color[6][1] = 255; + region.color[6][2] = 0; + + region.level[7] = -70; + region.color[7][0] = 0; + region.color[7][1] = 208; + region.color[7][2] = 0; + + region.level[8] = -80; + region.color[8][0] = 0; + region.color[8][1] = 196; + region.color[8][2] = 196; + + region.level[9] = -90; + region.color[9][0] = 0; + region.color[9][1] = 148; + region.color[9][2] = 255; + + region.level[10] = -100; + region.color[10][0] = 80; + region.color[10][1] = 80; + region.color[10][2] = 255; + + region.level[11] = -110; + region.color[11][0] = 0; + region.color[11][1] = 38; + region.color[11][2] = 255; + + region.level[12] = -120; + region.color[12][0] = 142; + region.color[12][1] = 63; + region.color[12][2] = 255; + + region.level[13] = -130; + region.color[13][0] = 196; + region.color[13][1] = 54; + region.color[13][2] = 255; + + region.level[14] = -140; + region.color[14][0] = 255; + region.color[14][1] = 0; + region.color[14][2] = 255; + + region.level[15] = -150; + region.color[15][0] = 255; + region.color[15][1] = 194; + region.color[15][2] = 204; + + region.levels = 16; + + fd = fopen(filename, "r"); + + if (fd == NULL) + fd = fopen(filename, "r"); + + if (fd == NULL) { + fd = fopen(filename, "w"); + + for (x = 0; x < region.levels; x++) + fprintf(fd, "%+4d: %3d, %3d, %3d\n", region.level[x], + region.color[x][0], region.color[x][1], + region.color[x][2]); + + fclose(fd); + } + + else { + x = 0; + s = fgets(string, 80, fd); + + while (x < 128 && feof(fd) == 0) { + pointer = strchr(string, ';'); + + if (pointer != NULL) + *pointer = 0; + + ok = sscanf(string, "%d: %d, %d, %d", &val[0], &val[1], + &val[2], &val[3]); + + if (ok == 4) { + if (val[0] < -200) + val[0] = -200; + + if (val[0] > +40) + val[0] = +40; + + region.level[x] = val[0]; + + for (y = 1; y < 4; y++) { + if (val[y] > 255) + val[y] = 255; + + if (val[y] < 0) + val[y] = 0; + } + + region.color[x][0] = val[1]; + region.color[x][1] = val[2]; + region.color[x][2] = val[3]; + x++; + } + + s = fgets(string, 80, fd); + } + + fclose(fd); + region.levels = x; + } +} + +void LoadTopoData(int max_lon, int min_lon, int max_lat, int min_lat, + int winfiles) +{ + /* 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; + + if (winfiles == 1) { + if (ippd == 3600) + snprintf(string, 19, + "%d=%d=%d=%d=hd", x, + x + 1, ymin, ymax); + else + snprintf(string, 16, + "%d=%d=%d=%d", x, + x + 1, ymin, ymax); + + } else { + if (ippd == 3600) + snprintf(string, 19, + "%d:%d:%d:%d-hd", x, + x + 1, ymin, ymax); + else + snprintf(string, 16, + "%d:%d:%d:%d", x, + x + 1, ymin, ymax); + } + + LoadSDF(string, winfiles); + } + } + + 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; + + if (winfiles == 1) { + if (ippd == 3600) + snprintf(string, 19, + "%d=%d=%d=%d=hd", x, + x + 1, ymin, ymax); + else + snprintf(string, 16, + "%d=%d=%d=%d", x, + x + 1, ymin, ymax); + + } else { + if (ippd == 3600) + snprintf(string, 19, + "%d:%d:%d:%d-hd", x, + x + 1, ymin, ymax); + else + snprintf(string, 16, + "%d:%d:%d:%d", x, + x + 1, ymin, ymax); + } + + LoadSDF(string, winfiles); + } + } +} + +void LoadUDT(char *filename) +{ + /* This function reads a file containing User-Defined Terrain + features for their addition to the digital elevation model + data used by SPLAT!. Elevations in the UDT file are evaluated + and then copied into a temporary file under /tmp. Then the + contents of the temp file are scanned, and if found to be unique, + are added to the ground elevations described by the digital + elevation data already loaded into memory. */ + + int i, x, y, z, ypix, xpix, tempxpix, tempypix, fd = 0, n = 0; + char input[80], str[3][80], tempname[15], *pointer = NULL, *s = NULL; + double latitude, longitude, height, tempheight; + FILE *fd1 = NULL, *fd2 = NULL; + + strcpy(tempname, "/tmp/XXXXXX\0"); + + fd1 = fopen(filename, "r"); + + if (fd1 != NULL) { + fd = mkstemp(tempname); + fd2 = fopen(tempname, "w"); + + s = fgets(input, 78, fd1); + + pointer = strchr(input, ';'); + + if (pointer != NULL) + *pointer = 0; + + while (feof(fd1) == 0) { + /* Parse line for latitude, longitude, height */ + + for (x = 0, y = 0, z = 0; + x < 78 && input[x] != 0 && z < 3; x++) { + if (input[x] != ',' && y < 78) { + str[z][y] = input[x]; + y++; + } + + else { + str[z][y] = 0; + z++; + y = 0; + } + } + + latitude = ReadBearing(str[0]); + longitude = ReadBearing(str[1]); + + if (longitude < 0.0) + longitude += 360; + + /* Remove and/or from antenna height string */ + + for (i = 0; + str[2][i] != 13 && str[2][i] != 10 + && str[2][i] != 0; i++) ; + + str[2][i] = 0; + + /* The terrain feature may be expressed in either + feet or meters. If the letter 'M' or 'm' is + discovered in the string, then this is an + indication that the value given is expressed + in meters. Otherwise the height is interpreted + as being expressed in feet. */ + + for (i = 0; + str[2][i] != 'M' && str[2][i] != 'm' + && str[2][i] != 0 && i < 48; i++) ; + + if (str[2][i] == 'M' || str[2][i] == 'm') { + str[2][i] = 0; + height = rint(atof(str[2])); + } + + else { + str[2][i] = 0; + height = rint(METERS_PER_FOOT * atof(str[2])); + } + + if (height > 0.0) + fprintf(fd2, "%d, %d, %f\n", + (int)rint(latitude / dpp), + (int)rint(longitude / dpp), height); + + s = fgets(input, 78, fd1); + + pointer = strchr(input, ';'); + + if (pointer != NULL) + *pointer = 0; + } + + fclose(fd1); + fclose(fd2); + close(fd); + + fd1 = fopen(tempname, "r"); + fd2 = fopen(tempname, "r"); + + y = 0; + + n = fscanf(fd1, "%d, %d, %lf", &xpix, &ypix, &height); + + do { + x = 0; + z = 0; + + n = fscanf(fd2, "%d, %d, %lf", &tempxpix, &tempypix, + &tempheight); + + do { + if (x > y && xpix == tempxpix + && ypix == tempypix) { + z = 1; /* Dupe! */ + + if (tempheight > height) + height = tempheight; + } + + else { + n = fscanf(fd2, "%d, %d, %lf", + &tempxpix, &tempypix, + &tempheight); + x++; + } + + } while (feof(fd2) == 0 && z == 0); + + if (z == 0) + /* No duplicate found */ + //fprintf(stdout,"%lf, %lf \n",xpix*dpp, ypix*dpp); + fflush(stdout); + AddElevation(xpix * dpp, ypix * dpp, height); + fflush(stdout); + + n = fscanf(fd1, "%d, %d, %lf", &xpix, &ypix, &height); + y++; + + rewind(fd2); + + } while (feof(fd1) == 0); + + fclose(fd1); + fclose(fd2); + unlink(tempname); + } + +} diff --git a/inputs.hh b/inputs.hh new file mode 100644 index 0000000..64ddb96 --- /dev/null +++ b/inputs.hh @@ -0,0 +1,16 @@ +#ifndef _INPUTS_HH_ +#define _INPUTS_HH_ + +#include "common.h" + +int LoadSDF_SDF(char *name, int winfiles); +char LoadSDF(char *name, int winfiles); +void LoadPAT(char *filename); +void LoadSignalColors(struct site xmtr); +void LoadLossColors(struct site xmtr); +void LoadDBMColors(struct site xmtr); +void LoadTopoData(int max_lon, int min_lon, int max_lat, int min_lat, + int winfiles); +void LoadUDT(char *filename); + +#endif /* _INPUTS_HH_ */ diff --git a/itm.cpp b/itm.cpp deleted file mode 100644 index 55ec53e..0000000 --- a/itm.cpp +++ /dev/null @@ -1,1532 +0,0 @@ -/*****************************************************************************\ - * * - * The following code was derived from public domain ITM code available * - * at ftp://flattop.its.bldrdoc.gov/itm/ITMDLL.cpp that was released on * - * June 26, 2007. It was modified to remove Microsoft Windows "dll-isms", * - * redundant and unnecessary #includes, redundant and unnecessary { }'s, * - * to initialize uninitialized variables, type cast some variables, * - * re-format the code for easier reading, and to replace pow() function * - * calls with explicit multiplications wherever possible to increase * - * execution speed and improve computational accuracy. * - * * -\*****************************************************************************/ - - -// ************************************* -// C++ routines for this program are taken from -// a translation of the FORTRAN code written by -// U.S. Department of Commerce NTIA/ITS -// Institute for Telecommunication Sciences -// ***************** -// Irregular Terrain Model (ITM) (Longley-Rice) -// ************************************* - -#include -#include -#include -#include - -#define THIRD (1.0/3.0) - -using namespace std; - -struct tcomplex -{ - double tcreal; - double tcimag; -}; - -struct prop_type -{ - double aref; - double dist; - double hg[2]; - double wn; - double dh; - double ens; - double gme; - double zgndreal; - double zgndimag; - double he[2]; - double dl[2]; - double the[2]; - int kwx; - int mdp; -}; - -struct propv_type -{ - double sgc; - int lvar; - int mdvar; - int klim; -}; - -struct propa_type -{ - double dlsa; - double dx; - double ael; - double ak1; - double ak2; - double aed; - double emd; - double aes; - double ems; - double dls[2]; - double dla; - double tha; -}; - -int mymin(const int &i, const int &j) -{ - if (ij) - return i; - else - return j; -} - -double mymin(const double &a, const double &b) -{ - if (ab) - return a; - else - return b; -} - -double FORTRAN_DIM(const double &x, const double &y) -{ - // This performs the FORTRAN DIM function. - // result is x-y if x is greater than y; otherwise result is 0.0 - - if (x>y) - return x-y; - else - return 0.0; -} - -double aknfe(const double &v2) -{ - double a; - - if (v2<5.76) - a=6.02+9.11*sqrt(v2)-1.27*v2; - else - a=12.953+4.343*log(v2); - - return a; -} - -double fht(const double& x, const double& pk) -{ - double w, fhtv; - if (x<200.0) - { - w=-log(pk); - - /* if (pk < 1e-5 || x*pow(w,3.0) > 5495.0 ) */ - - if (pk < 1e-5 || (x*w*w*w) > 5495.0 ) - { - fhtv=-117.0; - - if (x>1.0) - fhtv=17.372*log(x)+fhtv; - } - - else - fhtv=2.5e-5*x*x/pk-8.686*w-15.0; - } - - else - { - fhtv=0.05751*x-4.343*log(x); - - if (x<2000.0) - { - w=0.0134*x*exp(-0.005*x); - fhtv=(1.0-w)*fhtv+w*(17.372*log(x)-117.0); - } - } - - return fhtv; -} - -double h0f(double r, double et) -{ - double a[5]={25.0, 80.0, 177.0, 395.0, 705.0}; - double b[5]={24.0, 45.0, 68.0, 80.0, 105.0}; - double q, x; - int it; - double h0fv; - - it=(int)et; - - if (it<=0) - { - it=1; - q=0.0; - } - - else if (it>=5) - { - it=5; - q=0.0; - } - - else - q=et-it; - - /* x=pow(1.0/r,2.0); */ - x=(1.0/r); - x*=x; - h0fv=4.343*log((a[it-1]*x+b[it-1])*x+1.0); - - if (q!=0.0) - h0fv=(1.0-q)*h0fv+q*4.343*log((a[it]*x+b[it])*x+1.0); - - return h0fv; -} - -double ahd(double td) -{ - int i; - double a[3] = { 133.4, 104.6, 71.8}; - double b[3] = {0.332e-3, 0.212e-3, 0.157e-3}; - double c[3] = { -4.343, -1.086, 2.171}; - - if (td<=10e3) - i=0; - - else if (td<=70e3) - i=1; - - else - i=2; - - return a[i]+b[i]*td+c[i]*log(td); -} - -double adiff( double d, prop_type &prop, propa_type &propa) -{ - complex prop_zgnd(prop.zgndreal,prop.zgndimag); - static double wd1, xd1, afo, qk, aht, xht; - double a, q, pk, ds, th, wa, ar, wd, adiffv; - - if (d==0) - { - q=prop.hg[0]*prop.hg[1]; - qk=prop.he[0]*prop.he[1]-q; - - if (prop.mdp<0.0) - q+=10.0; - - wd1=sqrt(1.0+qk/q); - xd1=propa.dla+propa.tha/prop.gme; - q=(1.0-0.8*exp(-propa.dlsa/50e3))*prop.dh; - q*=0.78*exp(-pow(q/16.0,0.25)); - afo=mymin(15.0,2.171*log(1.0+4.77e-4*prop.hg[0]*prop.hg[1]*prop.wn*q)); - qk=1.0/abs(prop_zgnd); - aht=20.0; - xht=0.0; - - for (int j=0; j<2; ++j) - { - /* a=0.5*pow(prop.dl[j],2.0)/prop.he[j]; */ - a=0.5*(prop.dl[j]*prop.dl[j])/prop.he[j]; - wa=pow(a*prop.wn,THIRD); - pk=qk/wa; - q=(1.607-pk)*151.0*wa*prop.dl[j]/a; - xht+=q; - aht+=fht(q,pk); - } - - adiffv=0.0; - } - - else - { - th=propa.tha+d*prop.gme; - ds=d-propa.dla; - /* q=0.0795775*prop.wn*ds*pow(th,2.0); */ - q=0.0795775*prop.wn*ds*th*th; - adiffv=aknfe(q*prop.dl[0]/(ds+prop.dl[0]))+aknfe(q*prop.dl[1]/(ds+prop.dl[1])); - a=ds/th; - wa=pow(a*prop.wn,THIRD); - pk=qk/wa; - q=(1.607-pk)*151.0*wa*th+xht; - ar=0.05751*q-4.343*log(q)-aht; - q=(wd1+xd1/d)*mymin(((1.0-0.8*exp(-d/50e3))*prop.dh*prop.wn),6283.2); - wd=25.1/(25.1+sqrt(q)); - adiffv=ar*wd+(1.0-wd)*adiffv+afo; - } - - return adiffv; -} - -double ascat( double d, prop_type &prop, propa_type &propa) -{ - static double ad, rr, etq, h0s; - double h0, r1, r2, z0, ss, et, ett, th, q; - double ascatv, temp; - - if (d==0.0) - { - ad=prop.dl[0]-prop.dl[1]; - rr=prop.he[1]/prop.he[0]; - - if (ad<0.0) - { - ad=-ad; - rr=1.0/rr; - } - - etq=(5.67e-6*prop.ens-2.32e-3)*prop.ens+0.031; - h0s=-15.0; - ascatv=0.0; - } - - else - { - if (h0s>15.0) - h0=h0s; - else - { - th=prop.the[0]+prop.the[1]+d*prop.gme; - r2=2.0*prop.wn*th; - r1=r2*prop.he[0]; - r2*=prop.he[1]; - - if (r1<0.2 && r2<0.2) - return 1001.0; // <==== early return - - ss=(d-ad)/(d+ad); - q=rr/ss; - ss=mymax(0.1,ss); - q=mymin(mymax(0.1,q),10.0); - z0=(d-ad)*(d+ad)*th*0.25/d; - /* et=(etq*exp(-pow(mymin(1.7,z0/8.0e3),6.0))+1.0)*z0/1.7556e3; */ - temp=mymin(1.7,z0/8.0e3); - temp=temp*temp*temp*temp*temp*temp; - et=(etq*exp(-temp)+1.0)*z0/1.7556e3; - - ett=mymax(et,1.0); - h0=(h0f(r1,ett)+h0f(r2,ett))*0.5; - h0+=mymin(h0,(1.38-log(ett))*log(ss)*log(q)*0.49); - h0=FORTRAN_DIM(h0,0.0); - - if (et<1.0) - h0=et*h0+(1.0-et)*4.343*log(pow((1.0+1.4142/r1) *(1.0+1.4142/r2),2.0)*(r1+r2)/(r1+r2+2.8284)); - - if (h0>15.0 && h0s>=0.0) - h0=h0s; - } - - h0s=h0; - th=propa.tha+d*prop.gme; - /* ascatv=ahd(th*d)+4.343*log(47.7*prop.wn*pow(th,4.0))-0.1*(prop.ens-301.0)*exp(-th*d/40e3)+h0; */ - - ascatv=ahd(th*d)+4.343*log(47.7*prop.wn*th*th*th*th)-0.1*(prop.ens-301.0)*exp(-th*d/40e3)+h0; - - } - - return ascatv; -} - -double qerfi (double q) -{ - double x, t, v; - double c0=2.515516698; - double c1=0.802853; - double c2=0.010328; - double d1=1.432788; - double d2=0.189269; - double d3=0.001308; - - x=0.5-q; - t=mymax(0.5-fabs(x),0.000001); - t=sqrt(-2.0*log(t)); - v=t-((c2*t+c1)*t+c0)/(((d3*t+d2)*t+d1)*t+1.0); - - if (x<0.0) - v=-v; - - return v; -} - -void qlrps( double fmhz, double zsys, double en0, int ipol, double eps, double sgm, prop_type &prop) - -{ - double gma=157e-9; - - prop.wn=fmhz/47.7; - prop.ens=en0; - - if (zsys!=0.0) - prop.ens*=exp(-zsys/9460.0); - - prop.gme=gma*(1.0-0.04665*exp(prop.ens/179.3)); - complex zq, prop_zgnd(prop.zgndreal,prop.zgndimag); - zq=complex (eps,376.62*sgm/prop.wn); - prop_zgnd=sqrt(zq-1.0); - - if (ipol!=0.0) - prop_zgnd=prop_zgnd/zq; - - prop.zgndreal=prop_zgnd.real(); - prop.zgndimag=prop_zgnd.imag(); -} - -double abq_alos(complex r) -{ - return r.real()*r.real()+r.imag()*r.imag(); -} - -double alos(double d, prop_type &prop, propa_type &propa) -{ - complex prop_zgnd(prop.zgndreal,prop.zgndimag); - static double wls; - complex r; - double s, sps, q; - double alosv; - - if (d==0.0) - { - wls=0.021/(0.021+prop.wn*prop.dh/mymax(10e3,propa.dlsa)); - alosv=0.0; - } - - else - { - q=(1.0-0.8*exp(-d/50e3))*prop.dh; - s=0.78*q*exp(-pow(q/16.0,0.25)); - q=prop.he[0]+prop.he[1]; - sps=q/sqrt(d*d+q*q); - r=(sps-prop_zgnd)/(sps+prop_zgnd)*exp(-mymin(10.0,prop.wn*s*sps)); - q=abq_alos(r); - - if (q<0.25 || q1.57) - q=3.14-2.4649/q; - - alosv=(-4.343*log(abq_alos(complex(cos(q),-sin(q))+r))-alosv)*wls+alosv; - } - - return alosv; -} - - -void qlra(int kst[], int klimx, int mdvarx, prop_type &prop, propv_type &propv) -{ - double q; - - for (int j=0; j<2; ++j) - { - if (kst[j]<=0) - prop.he[j]=prop.hg[j]; - else - { - q=4.0; - - if (kst[j]!=1) - q=9.0; - - if (prop.hg[j]<5.0) - q*=sin(0.3141593*prop.hg[j]); - - prop.he[j]=prop.hg[j]+(1.0+q)*exp(-mymin(20.0,2.0*prop.hg[j]/mymax(1e-3,prop.dh))); - } - - q=sqrt(2.0*prop.he[j]/prop.gme); - prop.dl[j]=q*exp(-0.07*sqrt(prop.dh/mymax(prop.he[j],5.0))); - prop.the[j]=(0.65*prop.dh*(q/prop.dl[j]-1.0)-2.0*prop.he[j])/q; - } - - prop.mdp=1; - propv.lvar=mymax(propv.lvar,3); - - if (mdvarx>=0) - { - propv.mdvar=mdvarx; - propv.lvar=mymax(propv.lvar,4); - } - - if (klimx>0) - { - propv.klim=klimx; - propv.lvar=5; - } -} - -void lrprop (double d, prop_type &prop, propa_type &propa) // PaulM_lrprop -{ - static bool wlos, wscat; - static double dmin, xae; - complex prop_zgnd(prop.zgndreal,prop.zgndimag); - double a0, a1, a2, a3, a4, a5, a6; - double d0, d1, d2, d3, d4, d5, d6; - bool wq; - double q; - int j; - - if (prop.mdp!=0) - { - for (j=0; j<2; j++) - propa.dls[j]=sqrt(2.0*prop.he[j]/prop.gme); - - propa.dlsa=propa.dls[0]+propa.dls[1]; - propa.dla=prop.dl[0]+prop.dl[1]; - propa.tha=mymax(prop.the[0]+prop.the[1],-propa.dla*prop.gme); - wlos=false; - wscat=false; - - if (prop.wn<0.838 || prop.wn>210.0) - prop.kwx=mymax(prop.kwx,1); - - for (j=0; j<2; j++) - if (prop.hg[j]<1.0 || prop.hg[j]>1000.0) - prop.kwx=mymax(prop.kwx,1); - - for (j=0; j<2; j++) - if (abs(prop.the[j]) >200e-3 || prop.dl[j]<0.1*propa.dls[j] || prop.dl[j]>3.0*propa.dls[j]) - prop.kwx=mymax(prop.kwx,3); - - if (prop.ens < 250.0 || prop.ens > 400.0 || prop.gme < 75e-9 || prop.gme > 250e-9 || prop_zgnd.real() <= abs(prop_zgnd.imag()) || prop.wn < 0.419 || prop.wn > 420.0) - - prop.kwx=4; - - for (j=0; j<2; j++) - - if (prop.hg[j]<0.5 || prop.hg[j]>3000.0) - { - prop.kwx=4; - } - - dmin=abs(prop.he[0]-prop.he[1])/200e-3; - q=adiff(0.0,prop,propa); - /* xae=pow(prop.wn*pow(prop.gme,2),-THIRD); */ - xae=pow(prop.wn*prop.gme*prop.gme,-THIRD); - d3=mymax(propa.dlsa,1.3787*xae+propa.dla); - d4=d3+2.7574*xae; - a3=adiff(d3,prop,propa); - a4=adiff(d4,prop,propa); - propa.emd=(a4-a3)/(d4-d3); - propa.aed=a3-propa.emd*d3; - } - - if (prop.mdp>=0) - { - prop.mdp=0; - prop.dist=d; - } - - if (prop.dist>0.0) - { - if (prop.dist>1000e3) - prop.kwx=mymax(prop.kwx,1); - - if (prop.dist2000e3) - prop.kwx=4; - } - - if (prop.dist=0.0) - { - d0=mymin(d0,0.5*propa.dla); - d1=d0+0.25*(propa.dla-d0); - } - - else - d1=mymax(-propa.aed/propa.emd,0.25*propa.dla); - - a1=alos(d1,prop,propa); - wq=false; - - if (d0=0.0 || propa.ak2>0.0; - - if (wq) - { - propa.ak1=(a2-a0-propa.ak2*q)/(d2-d0); - - if (propa.ak1<0.0) - { - propa.ak1=0.0; - propa.ak2=FORTRAN_DIM(a2,a0)/q; - - if (propa.ak2==0.0) - propa.ak1=propa.emd; - } - } - } - - if (!wq) - { - propa.ak1=FORTRAN_DIM(a2,a1)/(d2-d1); - propa.ak2=0.0; - - if (propa.ak1==0.0) - propa.ak1=propa.emd; - } - - propa.ael=a2-propa.ak1*d2-propa.ak2*log(d2); - wlos=true; - } - - if (prop.dist>0.0) - prop.aref=propa.ael+propa.ak1*prop.dist+propa.ak2*log(prop.dist); - } - - if (prop.dist<=0.0 || prop.dist>=propa.dlsa) - { - if (!wscat) - { - q=ascat(0.0,prop,propa); - d5=propa.dla+200e3; - d6=d5+200e3; - a6=ascat(d6,prop,propa); - a5=ascat(d5,prop,propa); - - if (a5<1000.0) - { - propa.ems=(a6-a5)/200e3; - propa.dx=mymax(propa.dlsa,mymax(propa.dla+0.3*xae*log(47.7*prop.wn),(a5-propa.aed-propa.ems*d5)/(propa.emd-propa.ems))); - - propa.aes=(propa.emd-propa.ems)*propa.dx+propa.aed; - } - - else - { - propa.ems=propa.emd; - propa.aes=propa.aed; - propa.dx=10.e6; - } - - wscat=true; - } - - if (prop.dist>propa.dx) - prop.aref=propa.aes+propa.ems*prop.dist; - else - prop.aref=propa.aed+propa.emd*prop.dist; - } - - prop.aref=mymax(prop.aref,0.0); -} - -double curve (double const &c1, double const &c2, double const &x1, double const &x2, double const &x3, double const &de) -{ - /* return (c1+c2/(1.0+pow((de-x2)/x3,2.0)))*pow(de/x1,2.0)/(1.0+pow(de/x1,2.0)); */ - - double temp1, temp2; - - temp1=(de-x2)/x3; - temp2=de/x1; - - temp1*=temp1; - temp2*=temp2; - - return (c1+c2/(1.0+temp1))*temp2/(1.0+temp2); -} - -double avar(double zzt, double zzl, double zzc,prop_type &prop, propv_type &propv) -{ - static int kdv; - static double dexa, de, vmd, vs0, sgl, sgtm, sgtp, sgtd, tgtd, gm, gp, cv1, cv2, yv1, yv2, yv3, csm1, csm2, ysm1, ysm2, ysm3, csp1, csp2, ysp1, ysp2, ysp3, csd1, zd, cfm1, cfm2, cfm3, cfp1, cfp2, cfp3; - - double bv1[7]={-9.67,-0.62,1.26,-9.21,-0.62,-0.39,3.15}; - double bv2[7]={12.7,9.19,15.5,9.05,9.19,2.86,857.9}; - double xv1[7]={144.9e3,228.9e3,262.6e3,84.1e3,228.9e3,141.7e3,2222.e3}; - double xv2[7]={190.3e3,205.2e3,185.2e3,101.1e3,205.2e3,315.9e3,164.8e3}; - double xv3[7]={133.8e3,143.6e3,99.8e3,98.6e3,143.6e3,167.4e3,116.3e3}; - double bsm1[7]={2.13,2.66,6.11,1.98,2.68,6.86,8.51}; - double bsm2[7]={159.5,7.67,6.65,13.11,7.16,10.38,169.8}; - double xsm1[7]={762.2e3,100.4e3,138.2e3,139.1e3,93.7e3,187.8e3,609.8e3}; - double xsm2[7]={123.6e3,172.5e3,242.2e3,132.7e3,186.8e3,169.6e3,119.9e3}; - double xsm3[7]={94.5e3,136.4e3,178.6e3,193.5e3,133.5e3,108.9e3,106.6e3}; - double bsp1[7]={2.11,6.87,10.08,3.68,4.75,8.58,8.43}; - double bsp2[7]={102.3,15.53,9.60,159.3,8.12,13.97,8.19}; - double xsp1[7]={636.9e3,138.7e3,165.3e3,464.4e3,93.2e3,216.0e3,136.2e3}; - double xsp2[7]={134.8e3,143.7e3,225.7e3,93.1e3,135.9e3,152.0e3,188.5e3}; - double xsp3[7]={95.6e3,98.6e3,129.7e3,94.2e3,113.4e3,122.7e3,122.9e3}; - double bsd1[7]={1.224,0.801,1.380,1.000,1.224,1.518,1.518}; - double bzd1[7]={1.282,2.161,1.282,20.,1.282,1.282,1.282}; - double bfm1[7]={1.0,1.0,1.0,1.0,0.92,1.0,1.0}; - double bfm2[7]={0.0,0.0,0.0,0.0,0.25,0.0,0.0}; - double bfm3[7]={0.0,0.0,0.0,0.0,1.77,0.0,0.0}; - double bfp1[7]={1.0,0.93,1.0,0.93,0.93,1.0,1.0}; - double bfp2[7]={0.0,0.31,0.0,0.19,0.31,0.0,0.0}; - double bfp3[7]={0.0,2.00,0.0,1.79,2.00,0.0,0.0}; - static bool ws, w1; - double rt=7.8, rl=24.0, avarv, q, vs, zt, zl, zc; - double sgt, yr; - int temp_klim=propv.klim-1; - - if (propv.lvar>0) - { - switch (propv.lvar) - { - default: - if (propv.klim<=0 || propv.klim>7) - { - propv.klim = 5; - temp_klim = 4; - prop.kwx=mymax(prop.kwx,2); - } - - cv1 = bv1[temp_klim]; - cv2 = bv2[temp_klim]; - yv1 = xv1[temp_klim]; - yv2 = xv2[temp_klim]; - yv3 = xv3[temp_klim]; - csm1=bsm1[temp_klim]; - csm2=bsm2[temp_klim]; - ysm1=xsm1[temp_klim]; - ysm2=xsm2[temp_klim]; - ysm3=xsm3[temp_klim]; - csp1=bsp1[temp_klim]; - csp2=bsp2[temp_klim]; - ysp1=xsp1[temp_klim]; - ysp2=xsp2[temp_klim]; - ysp3=xsp3[temp_klim]; - csd1=bsd1[temp_klim]; - zd=bzd1[temp_klim]; - cfm1=bfm1[temp_klim]; - cfm2=bfm2[temp_klim]; - cfm3=bfm3[temp_klim]; - cfp1=bfp1[temp_klim]; - cfp2=bfp2[temp_klim]; - cfp3=bfp3[temp_klim]; - - case 4: - kdv=propv.mdvar; - ws=kdv>=20; - - if (ws) - kdv-=20; - - w1=kdv>=10; - - if (w1) - kdv-=10; - - if (kdv<0 || kdv>3) - { - kdv=0; - prop.kwx=mymax(prop.kwx,2); - } - - case 3: - q=log(0.133*prop.wn); - - /* gm=cfm1+cfm2/(pow(cfm3*q,2.0)+1.0); */ - /* gp=cfp1+cfp2/(pow(cfp3*q,2.0)+1.0); */ - - gm=cfm1+cfm2/((cfm3*q*cfm3*q)+1.0); - gp=cfp1+cfp2/((cfp3*q*cfp3*q)+1.0); - - case 2: - dexa=sqrt(18e6*prop.he[0])+sqrt(18e6*prop.he[1])+pow((575.7e12/prop.wn),THIRD); - - case 1: - if (prop.dist3.1 || fabs(zl)>3.1 || fabs(zc)>3.1) - prop.kwx=mymax(prop.kwx,1); - - if (zt<0.0) - sgt=sgtm; - - else if (zt<=zd) - sgt=sgtp; - - else - sgt=sgtd+tgtd/zt; - - /* vs=vs0+pow(sgt*zt,2.0)/(rt+zc*zc)+pow(sgl*zl,2.0)/(rl+zc*zc); */ - vs=vs0+(sgt*zt*sgt*zt)/(rt+zc*zc)+(sgl*zl*sgl*zl)/(rl+zc*zc); - - if (kdv==0) - { - yr=0.0; - propv.sgc=sqrt(sgt*sgt+sgl*sgl+vs); - } - - else if (kdv==1) - { - yr=sgt*zt; - propv.sgc=sqrt(sgl*sgl+vs); - } - - else if (kdv==2) - { - yr=sqrt(sgt*sgt+sgl*sgl)*zt; - propv.sgc=sqrt(vs); - } - - else - { - yr=sgt*zt+sgl*zl; - propv.sgc=sqrt(vs); - } - - avarv=prop.aref-vmd-yr-propv.sgc*zc; - - if (avarv<0.0) - avarv=avarv*(29.0-avarv)/(29.0-10.0*avarv); - - return avarv; -} - -void hzns(double pfl[], prop_type &prop) -{ - bool wq; - int np; - double xi, za, zb, qc, q, sb, sa; - - np=(int)pfl[0]; - xi=pfl[1]; - za=pfl[2]+prop.hg[0]; - zb=pfl[np+2]+prop.hg[1]; - qc=0.5*prop.gme; - q=qc*prop.dist; - prop.the[1]=(zb-za)/prop.dist; - prop.the[0]=prop.the[1]-q; - prop.the[1]=-prop.the[1]-q; - prop.dl[0]=prop.dist; - prop.dl[1]=prop.dist; - - if (np>=2) - { - sa=0.0; - sb=prop.dist; - wq=true; - - for (int i=1; i0.0) - { - prop.the[0]+=q/sa; - prop.dl[0]=sa; - wq=false; - } - - if (!wq) - { - q=pfl[i+2]-(qc*sb+prop.the[1])*sb-zb; - - if (q>0.0) - { - prop.the[1]+=q/sb; - prop.dl[1]=sb; - } - } - } - } -} - -void z1sq1 (double z[], const double &x1, const double &x2, double& z0, double& zn) - -{ - double xn, xa, xb, x, a, b; - int n, ja, jb; - - xn=z[0]; - xa=int(FORTRAN_DIM(x1/z[1],0.0)); - xb=xn-int(FORTRAN_DIM(xn,x2/z[1])); - - if (xb<=xa) - { - xa=FORTRAN_DIM(xa,1.0); - xb=xn-FORTRAN_DIM(xn,xb+1.0); - } - - ja=(int)xa; - jb=(int)xb; - n=jb-ja; - xa=xb-xa; - x=-0.5*xa; - xb+=x; - a=0.5*(z[ja+2]+z[jb+2]); - b=0.5*(z[ja+2]-z[jb+2])*x; - - for (int i=2; i<=n; ++i) - { - ++ja; - x+=1.0; - a+=z[ja+2]; - b+=z[ja+2]*x; - } - - a/=xa; - b=b*12.0/((xa*xa+2.0)*xa); - z0=a-b*xb; - zn=a+b*(xn-xb); -} - -double qtile (const int &nn, double a[], const int &ir) -{ - double q=0.0, r; /* Initializations by KD2BD */ - int m, n, i, j, j1=0, i0=0, k; /* Initializations by KD2BD */ - bool done=false; - bool goto10=true; - - m=0; - n=nn; - k=mymin(mymax(0,ir),n); - - while (!done) - { - if (goto10) - { - q=a[k]; - i0=m; - j1=n; - } - - i=i0; - - while (i<=n && a[i]>=q) - i++; - - if (i>n) - i=n; - j=j1; - - while (j>=m && a[j]<=q) - j--; - - if (jk) - { - a[k]=a[j]; - a[j]=q; - n=j-1; - goto10=true; - } - - else - done=true; - } - - return q; -} - -double qerf(const double &z) -{ - double b1=0.319381530, b2=-0.356563782, b3=1.781477937; - double b4=-1.821255987, b5=1.330274429; - double rp=4.317008, rrt2pi=0.398942280; - double t, x, qerfv; - - x=z; - t=fabs(x); - - if (t>=10.0) - qerfv=0.0; - else - { - t=rp/(t+rp); - qerfv=exp(-0.5*x*x)*rrt2pi*((((b5*t+b4)*t+b3)*t+b2)*t+b1)*t; - } - - if (x<0.0) - qerfv=1.0-qerfv; - - return qerfv; -} - -double d1thx(double pfl[], const double &x1, const double &x2) -{ - int np, ka, kb, n, k, j; - double d1thxv, sn, xa, xb; - double *s; - - np=(int)pfl[0]; - xa=x1/pfl[1]; - xb=x2/pfl[1]; - d1thxv=0.0; - - if (xb-xa<2.0) // exit out - return d1thxv; - - ka=(int)(0.1*(xb-xa+8.0)); - ka=mymin(mymax(4,ka),25); - n=10*ka-5; - kb=n-ka+1; - sn=n-1; - assert((s=new double[n+2])!=0); - s[0]=sn; - s[1]=1.0; - xb=(xb-xa)/sn; - k=(int)(xa+1.0); - xa-=(double)k; - - for (j=0; j0.0 && k1.5*prop.dist) - { - z1sq1(pfl,xl[0],xl[1],za,zb); - prop.he[0]=prop.hg[0]+FORTRAN_DIM(pfl[2],za); - prop.he[1]=prop.hg[1]+FORTRAN_DIM(pfl[np+2],zb); - - for (j=0; j<2; j++) - prop.dl[j]=sqrt(2.0*prop.he[j]/prop.gme)*exp(-0.07*sqrt(prop.dh/mymax(prop.he[j],5.0))); - - q=prop.dl[0]+prop.dl[1]; - - if (q<=prop.dist) - { - /* q=pow(prop.dist/q,2.0); */ - q=((prop.dist/q)*(prop.dist/q)); - - for (j=0; j<2; j++) - { - prop.he[j]*=q; - prop.dl[j]=sqrt(2.0*prop.he[j]/prop.gme)*exp(-0.07*sqrt(prop.dh/mymax(prop.he[j],5.0))); - } - } - - for (j=0; j<2; j++) - { - q=sqrt(2.0*prop.he[j]/prop.gme); - prop.the[j]=(0.65*prop.dh*(q/prop.dl[j]-1.0)-2.0*prop.he[j])/q; - } - } - - else - { - z1sq1(pfl,xl[0],0.9*prop.dl[0],za,q); - z1sq1(pfl,prop.dist-0.9*prop.dl[1],xl[1],q,zb); - prop.he[0]=prop.hg[0]+FORTRAN_DIM(pfl[2],za); - prop.he[1]=prop.hg[1]+FORTRAN_DIM(pfl[np+2],zb); - } - - prop.mdp=-1; - propv.lvar=mymax(propv.lvar,3); - - if (mdvarx>=0) - { - propv.mdvar=mdvarx; - propv.lvar=mymax(propv.lvar,4); - } - - if (klimx>0) - { - propv.klim=klimx; - propv.lvar=5; - } - - lrprop(0.0,prop,propa); -} - -double deg2rad(double d) -{ - return d*3.1415926535897/180.0; -} - -//******************************************************** -//* Point-To-Point Mode Calculations * -//******************************************************** - -void point_to_point(double elev[], double tht_m, double rht_m, double eps_dielect, double sgm_conductivity, double eno_ns_surfref, double frq_mhz, int radio_climate, int pol, double conf, double rel, double &dbloss, char *strmode, int &errnum) -{ - // pol: 0-Horizontal, 1-Vertical - // radio_climate: 1-Equatorial, 2-Continental Subtropical, 3-Maritime Tropical, - // 4-Desert, 5-Continental Temperate, 6-Maritime Temperate, Over Land, - // 7-Maritime Temperate, Over Sea - // conf, rel: .01 to .99 - // elev[]: [num points - 1], [delta dist(meters)], [height(meters) point 1], ..., [height(meters) point n] - // errnum: 0- No Error. - // 1- Warning: Some parameters are nearly out of range. - // Results should be used with caution. - // 2- Note: Default parameters have been substituted for impossible ones. - // 3- Warning: A combination of parameters is out of range. - // Results are probably invalid. - // Other- Warning: Some parameters are out of range. - // Results are probably invalid. - - prop_type prop; - propv_type propv; - propa_type propa; - - double zsys=0; - double zc, zr; - double eno, enso, q; - long ja, jb, i, np; - //double dkm, xkm; - double fs; - - prop.hg[0]=tht_m; - prop.hg[1]=rht_m; - propv.klim=radio_climate; - prop.kwx=0; - propv.lvar=5; - prop.mdp=-1; - zc=qerfi(conf); - zr=qerfi(rel); - np=(long)elev[0]; //number of points - //dkm=(elev[1]*elev[0])/1000.0; // total distance in km. elev[1]=90(m) (default) - //xkm=elev[1]/1000.0; // distance between points in km - eno=eno_ns_surfref; - enso=0.0; - q=enso; - - if (q<=0.0) - { - /* ja = 3.0 + 0.1 * elev[0]; */ - ja=(long)(3.0+0.1* elev[0]); - - jb=np-ja+6; - - for (i=ja-1; i0.0) - strcpy(strmode,"Double Horizon"); - - if (prop.dist<=propa.dlsa || prop.dist <= propa.dx) - strcat(strmode,", Diffraction Dominant"); - - else if (prop.dist>propa.dx) - strcat(strmode, ", Troposcatter Dominant"); - } - - dbloss=avar(zr,0.0,zc,prop,propv)+fs; - errnum=prop.kwx; -} - -void point_to_pointMDH (double elev[], double tht_m, double rht_m, double eps_dielect, double sgm_conductivity, double eno_ns_surfref, double frq_mhz, int radio_climate, int pol, double timepct, double locpct, double confpct, double &dbloss, int &propmode, double &deltaH, int &errnum) -{ - // pol: 0-Horizontal, 1-Vertical - // radio_climate: 1-Equatorial, 2-Continental Subtropical, 3-Maritime Tropical, - // 4-Desert, 5-Continental Temperate, 6-Maritime Temperate, Over Land, - // 7-Maritime Temperate, Over Sea - // timepct, locpct, confpct: .01 to .99 - // elev[]: [num points - 1], [delta dist(meters)], [height(meters) point 1], ..., [height(meters) point n] - // propmode: Value Mode - // -1 mode is undefined - // 0 Line of Sight - // 5 Single Horizon, Diffraction - // 6 Single Horizon, Troposcatter - // 9 Double Horizon, Diffraction - // 10 Double Horizon, Troposcatter - // errnum: 0- No Error. - // 1- Warning: Some parameters are nearly out of range. - // Results should be used with caution. - // 2- Note: Default parameters have been substituted for impossible ones. - // 3- Warning: A combination of parameters is out of range. - // Results are probably invalid. - // Other- Warning: Some parameters are out of range. - // Results are probably invalid. - - prop_type prop; - propv_type propv; - propa_type propa; - double zsys=0; - double ztime, zloc, zconf; - double eno, enso, q; - long ja, jb, i, np; - //double dkm, xkm; - double fs; - - propmode=-1; // mode is undefined - prop.hg[0]=tht_m; - prop.hg[1]=rht_m; - propv.klim=radio_climate; - prop.kwx=0; - propv.lvar=5; - prop.mdp=-1; - ztime=qerfi(timepct); - zloc=qerfi(locpct); - zconf=qerfi(confpct); - - np=(long)elev[0]; - //dkm=(elev[1]*elev[0])/1000.0; - //xkm=elev[1]/1000.0; - eno=eno_ns_surfref; - enso=0.0; - q=enso; - - if (q<=0.0) - { - /* ja = 3.0 + 0.1 * elev[0]; */ - ja=(long)(3.0+0.1*elev[0]); - jb=np-ja+6; - - for (i=ja-1; i0.0) - propmode=8; // Double Horizon - - if (prop.dist<=propa.dlsa || prop.dist<=propa.dx) - propmode+=1; // Diffraction Dominant - - else if (prop.dist>propa.dx) - propmode+=2; // Troposcatter Dominant - } - - dbloss=avar(ztime, zloc, zconf, prop, propv)+fs; //avar(time,location,confidence) - errnum=prop.kwx; -} - -void point_to_pointDH (double elev[], double tht_m, double rht_m, double eps_dielect, double sgm_conductivity, double eno_ns_surfref, double frq_mhz, int radio_climate, int pol, double conf, double rel, double &dbloss, double &deltaH, int &errnum) -{ - // pol: 0-Horizontal, 1-Vertical - // radio_climate: 1-Equatorial, 2-Continental Subtropical, 3-Maritime Tropical, - // 4-Desert, 5-Continental Temperate, 6-Maritime Temperate, Over Land, - // 7-Maritime Temperate, Over Sea - // conf, rel: .01 to .99 - // elev[]: [num points - 1], [delta dist(meters)], [height(meters) point 1], ..., [height(meters) point n] - // errnum: 0- No Error. - // 1- Warning: Some parameters are nearly out of range. - // Results should be used with caution. - // 2- Note: Default parameters have been substituted for impossible ones. - // 3- Warning: A combination of parameters is out of range. - // Results are probably invalid. - // Other- Warning: Some parameters are out of range. - // Results are probably invalid. - - char strmode[100]; - prop_type prop; - propv_type propv; - propa_type propa; - double zsys=0; - double zc, zr; - double eno, enso, q; - long ja, jb, i, np; - //double dkm, xkm; - double fs; - - prop.hg[0]=tht_m; - prop.hg[1]=rht_m; - propv.klim=radio_climate; - prop.kwx=0; - propv.lvar=5; - prop.mdp=-1; - zc=qerfi(conf); - zr=qerfi(rel); - np=(long)elev[0]; - //dkm=(elev[1]*elev[0])/1000.0; - //xkm=elev[1]/1000.0; - eno=eno_ns_surfref; - enso=0.0; - q=enso; - - if (q<=0.0) - { - /* ja = 3.0 + 0.1 * elev[0]; */ - ja=(long)(3.0+0.1*elev[0]); - - jb=np-ja+6; - - for (i=ja-1; i0.0) - strcpy(strmode,"Double Horizon"); - - if (prop.dist<=propa.dlsa || prop.dist <= propa.dx) - strcat(strmode,", Diffraction Dominant"); - - else if (prop.dist>propa.dx) - strcat(strmode, ", Troposcatter Dominant"); - } - - dbloss=avar(zr,0.0,zc,prop,propv)+fs; //avar(time,location,confidence) - errnum=prop.kwx; -} - - -//******************************************************** -//* Area Mode Calculations * -//******************************************************** - -void area(long ModVar, double deltaH, double tht_m, double rht_m, double dist_km, int TSiteCriteria, int RSiteCriteria, double eps_dielect, double sgm_conductivity, double eno_ns_surfref, double frq_mhz, int radio_climate, int pol, double pctTime, double pctLoc, double pctConf, double &dbloss, char *strmode, int &errnum) -{ - // pol: 0-Horizontal, 1-Vertical - // TSiteCriteria, RSiteCriteria: - // 0 - random, 1 - careful, 2 - very careful - // radio_climate: 1-Equatorial, 2-Continental Subtropical, 3-Maritime Tropical, - // 4-Desert, 5-Continental Temperate, 6-Maritime Temperate, Over Land, - // 7-Maritime Temperate, Over Sea - // ModVar: 0 - Single: pctConf is "Time/Situation/Location", pctTime, pctLoc not used - // 1 - Individual: pctTime is "Situation/Location", pctConf is "Confidence", pctLoc not used - // 2 - Mobile: pctTime is "Time/Locations (Reliability)", pctConf is "Confidence", pctLoc not used - // 3 - Broadcast: pctTime is "Time", pctLoc is "Location", pctConf is "Confidence" - // pctTime, pctLoc, pctConf: .01 to .99 - // errnum: 0- No Error. - // 1- Warning: Some parameters are nearly out of range. - // Results should be used with caution. - // 2- Note: Default parameters have been substituted for impossible ones. - // 3- Warning: A combination of parameters is out of range. - // Results are probably invalid. - // Other- Warning: Some parameters are out of range. - // Results are probably invalid. - // NOTE: strmode is not used at this time. - - prop_type prop; - propv_type propv; - propa_type propa; - double zt, zl, zc, xlb; - double fs; - long ivar; - double eps, eno, sgm; - long ipol; - int kst[2]; - - kst[0]=(int)TSiteCriteria; - kst[1]=(int)RSiteCriteria; - zt=qerfi(pctTime); - zl=qerfi(pctLoc); - zc=qerfi(pctConf); - eps=eps_dielect; - sgm=sgm_conductivity; - eno=eno_ns_surfref; - prop.dh=deltaH; - prop.hg[0]=tht_m; - prop.hg[1]=rht_m; - /* propv.klim = (__int32) radio_climate; */ - propv.klim=(long)radio_climate; - prop.ens=eno; - prop.kwx=0; - ivar=(long)ModVar; - ipol=(long)pol; - qlrps(frq_mhz, 0.0, eno, ipol, eps, sgm, prop); - qlra(kst, propv.klim, ivar, prop, propv); - - if (propv.lvar<1) - propv.lvar=1; - - lrprop(dist_km*1000.0, prop, propa); - fs=32.45+20.0*log10(frq_mhz)+20.0*log10(prop.dist/1000.0); - xlb=fs+avar(zt, zl, zc, prop, propv); - dbloss=xlb; - - if (prop.kwx==0) - errnum=0; - else - errnum=prop.kwx; -} - -double ITMAreadBLoss(long ModVar, double deltaH, double tht_m, double rht_m, double dist_km, int TSiteCriteria, int RSiteCriteria, double eps_dielect, double sgm_conductivity, double eno_ns_surfref, double frq_mhz, int radio_climate, int pol, double pctTime, double pctLoc, double pctConf) -{ - char strmode[200]; - int errnum; - double dbloss; - - area(ModVar,deltaH,tht_m,rht_m,dist_km,TSiteCriteria,RSiteCriteria, eps_dielect,sgm_conductivity,eno_ns_surfref, frq_mhz,radio_climate,pol,pctTime,pctLoc, pctConf,dbloss,strmode,errnum); - - return dbloss; -} - -double ITMVersion() -{ - return 7.0; -} - diff --git a/itwom3.0.cpp b/itwom3.0.cpp deleted file mode 100644 index 36c1021..0000000 --- a/itwom3.0.cpp +++ /dev/null @@ -1,2863 +0,0 @@ -/******************************************************************************** -* ITWOM version 3.0a, January 20, 2011 File: itwom3.0a.cpp * -* Provenance: Further test version of itwom2.0m re adj to Hrzn range factors * -* 1. This file is based on a thorough debugging, completion, and update of the * -* ITM, based on an original, public domain version of this file obtained from: * -* ftp://flattop.its.bldrdoc.gov/itm/ITMDLL.cpp prior to May, 2007. C++ routines * -* for this program are taken from a translation of the FORTRAN code written by * -* U.S. Department of Commerce NTIA/ITS Institute for Telecommunication Sciences * -* Irregular Terrain Model (ITM) (Longley-Rice). * -* 2. The Linux version of this file incorporates improvements suggested by a * -* study of changes made to file itm.cpp by J. D. McDonald to remove Microsoft * -* Windows dll-isms and to debug an ambguity in overloaded calls. * -* 3. The Linux version of this file also incorporates improvements suggested by * -* a study of further modifications made to itm.cpp by John A. Magliacane to * -* remove unused variables, unneeded #includes, and to replace pow() statements * -* with explicit multiplications to improve execution speed and accuracy. * -* 4. On August 19, 2007 this file was modified by Sid Shumate to include * -* changes and updates included in version 7.0 of ITMDLL.cpp, which was released * -* by the NTIA/ITS on June 26, 2007. With correction set SS1 and SS2: itm71.cpp. * -* 5. On Feb. 5, 2008 this file became v.1.0 of the ITWOM with the addition, by * -* Sid Shumate, of multiple corrections, the replacement of subroutines lrprop * -* and alos with lrprop2 and alos2, and the addition of subroutine saalos to * -* incorporate Radiative Transfer Engine (RTE) computations in the line of sight * -* range. * -* Update 8 Jun 2010 to modify alos to match 2010 series of IEEE-BTS * -* newsletter articles * -* Update June 12, 2010 to z version to change test outputs * -* Offshoot start date June 23, 2010 to start itwom2.0 dual version for FCC. * -* Update to 2.0b July 25 to correct if statement errors in adiff2 re two peak * -* calculations starting at line 525 * -* Development to 2.0c 8 Aug 2010 after modifying saalos and adiff for full * -* addition of saalos treatment to post obstruction calculations and debugging. * -* Modified to make 1st obs loss=5.8 only, no clutter loss considered * -* * -* Commented out unused variables and calculations to eliminate gcc warnings * -* (-Wunused-but-set-variable) -- John A. Magliacane -- July 25, 2013 * -********************************************************************************/ - -#include -#include -#include -#include - -#define THIRD (1.0/3.0) - -using namespace std; - -struct tcomplex -{ double tcreal; - double tcimag; -}; - -struct prop_type -{ double aref; - double dist; - double hg[2]; - double rch[2]; - double wn; - double dh; - double dhd; - double ens; - double encc; - double cch; - double cd; - double gme; - double zgndreal; - double zgndimag; - double he[2]; - double dl[2]; - double the[2]; - double tiw; - double ght; - double ghr; - double rph; - double hht; - double hhr; - double tgh; - double tsgh; - double thera; - double thenr; - int rpl; - int kwx; - int mdp; - int ptx; - int los; -}; - -struct propv_type -{ double sgc; - int lvar; - int mdvar; - int klim; -}; - -struct propa_type -{ double dlsa; - double dx; - double ael; - double ak1; - double ak2; - double aed; - double emd; - double aes; - double ems; - double dls[2]; - double dla; - double tha; -}; - -int mymin(const int &i, const int &j) -{ - if (ij) - return i; - else - return j; -} - -double mymin(const double &a, const double &b) -{ - if (ab) - return a; - else - return b; -} - -double FORTRAN_DIM(const double &x, const double &y) -{ - /* This performs the FORTRAN DIM function. Result is x-y - if x is greater than y; otherwise result is 0.0 */ - - if (x>y) - return x-y; - else - return 0.0; -} - -double aknfe(const double &v2) -{ - double a; - - if (v2<5.76) - a=6.02+9.11*sqrt(v2)-1.27*v2; - else - a=12.953+10*log10(v2); - return a; -} - -double fht(const double& x, const double& pk) -{ - double w, fhtv; - - if (x<200.0) - { - w=-log(pk); - - if (pk<1.0e-5 || x*w*w*w > 5495.0) - { - fhtv=-117.0; - - if (x>1.0) - fhtv=40.0*log10(x)+fhtv; - } - else - fhtv=2.5e-5*x*x/pk-8.686*w-15.0; - } - - else - { - fhtv=0.05751*x-10.0*log10(x); - - if (x<2000.0) - { - w=0.0134*x*exp(-0.005*x); - fhtv=(1.0-w)*fhtv+w*(40.0*log10(x)-117.0); - } - } - return fhtv; -} - -double h0f(double r, double et) -{ - double a[5]={25.0, 80.0, 177.0, 395.0, 705.0}; - double b[5]={24.0, 45.0, 68.0, 80.0, 105.0}; - double q, x; - double h0fv, temp; - int it; - - it=(int)et; - - if (it<=0) - { - it=1; - q=0.0; - } - - else if (it>=5) - { - it=5; - q=0.0; - } - - else - q=et-it; - - /* x=pow(1.0/r,2.0); */ - - temp=1.0/r; - x=temp*temp; - - h0fv=4.343*log((a[it-1]*x+b[it-1])*x+1.0); - - if (q!=0.0) - h0fv=(1.0-q)*h0fv+q*4.343*log((a[it]*x+b[it])*x+1.0); - - return h0fv; -} - -double ahd(double td) -{ - int i; - double a[3]={ 133.4, 104.6, 71.8}; - double b[3]={0.332e-3, 0.212e-3, 0.157e-3}; - double c[3]={ -4.343, -1.086, 2.171}; - - if (td<=10e3) - i=0; - - else if (td<=70e3) - i=1; - - else - i=2; - - return a[i]+b[i]*td+c[i]*log(td); -} - -double abq_alos(complex r) -{ - return r.real()*r.real()+r.imag()*r.imag(); -} - -double saalos(double d, prop_type &prop, propa_type &propa) -{ - double ensa, encca, q, dp, dx, tde, hc, ucrpc, ctip, tip, tic, stic, ctic, sta; - double ttc, cttc, crpc, ssnps, d1a, rsp, tsp, arte, zi, pd, pdk, hone, tvsr; - double saalosv=0.0; - - q=0.0; - - if (d==0.0) - { - tsp=1.0; - rsp=0.0; - d1a=50.0; - saalosv=0.0; - } - else if(prop.hg[1] > prop.cch) - { - saalosv=0.0; - } - else - { - pd=d; - pdk=pd/1000.0; - tsp=1.0; - rsp=0.0; - d1a=pd; - /* at first, hone is transmitter antenna height - relative to receive site ground level. */ - hone=prop.tgh+prop.tsgh-(prop.rch[1]-prop.hg[1]); - - if(prop.tgh>prop.cch) /* for TX ant above all clutter height*/ - { - ensa=1+prop.ens*0.000001; - encca=1+prop.encc*0.000001; - dp=pd; - - for (int j=0; j<5; ++j) - { - tde=dp/6378137.0; - hc=(prop.cch+6378137.0)*(1-cos(tde)); - dx=(prop.cch+6378137.0)*sin(tde); - ucrpc=sqrt((hone-prop.cch+hc)*(hone-prop.cch+hc)+(dx*dx)); - ctip=(hone-prop.cch+hc)/ucrpc; - tip=acos(ctip); - tic=tip+tde; - tic=mymax(0.0,tic); - stic=sin(tic); - sta=(ensa/encca)*stic; - ttc=asin(sta); - cttc=sqrt(1-(sin(ttc))*(sin(ttc))); - crpc=(prop.cch-prop.hg[1])/cttc; - if(crpc>=dp) - { - crpc=dp-1/dp; - } - - ssnps=(3.1415926535897/2)-tic; - d1a=(crpc*sin(ttc))/(1-1/6378137.0); - dp=pd-d1a; - - } - - ctic=cos(tic); - - /* if the ucrpc path touches the canopy before reaching the - end of the ucrpc, the entry point moves toward the - transmitter, extending the crpc and d1a. Estimating the d1a: */ - - if(ssnps<=0.0) - { - d1a=mymin(0.1*pd,600.0); - crpc=d1a; - /* hone must be redefined as being barely above - the canopy height with respect to the receiver - canopy height, which despite the earth curvature - is at or above the transmitter antenna height. */ - hone=prop.cch+1; - rsp=.997; - tsp=1-rsp; - } - else - { - - if (prop.ptx>=1) /* polarity ptx is vertical or circular */ - { - q=((ensa*cttc-encca*ctic)/(ensa*cttc+encca*ctic)); - rsp=q*q; - tsp=1-rsp; - - if (prop.ptx==2) /* polarity is circular - new */ - { - q=((ensa*ctic-encca*cttc)/(ensa*ctic+encca*cttc)); - rsp=((ensa*cttc-encca*ctic)/(ensa*cttc+encca*ctic)); - rsp=(q*q+rsp*rsp)/2; - tsp=1-rsp; - } - } - else /* ptx is 0, horizontal, or undefined */ - { - q=((ensa*ctic-encca*cttc)/(ensa*ctic+encca*cttc)); - rsp=q*q; - tsp=1-rsp; - } - } - /* tvsr is defined as tx ant height above receiver ant height */ - tvsr= mymax(0.0,prop.tgh+prop.tsgh-prop.rch[1]); - - if (d1a<50.0) - { - arte=0.0195*crpc-20*log10(tsp); - } - - else - { - if (d1a<225.0) - { - - if (tvsr>1000.0) - { - q=d1a*(0.03*exp(-0.14*pdk)); - } - else - { - q=d1a*(0.07*exp(-0.17*pdk)); - } - - arte=q+(0.7*pdk-mymax(0.01,log10(prop.wn*47.7)-2))*(prop.hg[1]/hone); - } - - else - { - q=0.00055*(pdk)+log10(pdk)*(0.041-0.0017*sqrt(hone)+0.019); - - arte=d1a*q-(18*log10(rsp))/(exp(hone/37.5)); - - zi=1.5*sqrt(hone-prop.cch); - - if(pdk>zi) - { - q=(pdk-zi)*10.2*((sqrt(mymax(0.01,log10(prop.wn*47.7)-2.0)))/(100-zi)); - } - else - { - q=((zi-pdk)/zi)*(-20.0*mymax(0.01,log10(prop.wn*47.7)-2.0))/sqrt(hone); - } - arte=arte+q; - - } - } - } - else /* for TX at or below clutter height */ - { - q=(prop.cch-prop.tgh)*(2.06943-1.56184*exp(1/prop.cch-prop.tgh)); - q=q+(17.98-0.84224*(prop.cch-prop.tgh))*exp(-0.00000061*pd); - arte=q+1.34795*20*log10(pd+1.0); - arte=arte-(mymax(0.01,log10(prop.wn*47.7)-2))*(prop.hg[1]/prop.tgh); - } - saalosv=arte; - } - return saalosv; -} - - -double adiff(double d, prop_type &prop, propa_type &propa) -{ - complex prop_zgnd(prop.zgndreal,prop.zgndimag); - static double wd1, xd1, afo, qk, aht, xht; - double a, q, pk, ds, th, wa, ar, wd, adiffv; - - if (d==0) - { - q=prop.hg[0]*prop.hg[1]; - qk=prop.he[0]*prop.he[1]-q; - - if (prop.mdp<0.0) - q+=10.0; - - wd1=sqrt(1.0+qk/q); - xd1=propa.dla+propa.tha/prop.gme; - q=(1.0-0.8*exp(-propa.dlsa/50e3))*prop.dh; - q*=0.78*exp(-pow(q/16.0,0.25)); - afo=mymin(15.0,2.171*log(1.0+4.77e-4*prop.hg[0]*prop.hg[1]*prop.wn*q)); - qk=1.0/abs(prop_zgnd); - aht=20.0; - xht=0.0; - - for (int j=0; j<2; ++j) - { - /* a=0.5*pow(prop.dl[j],2.0)/prop.he[j]; */ - a=0.5*(prop.dl[j]*prop.dl[j])/prop.he[j]; - wa=pow(a*prop.wn,THIRD); - pk=qk/wa; - q=(1.607-pk)*151.0*wa*prop.dl[j]/a; - xht+=q; - aht+=fht(q,pk); - } - - adiffv=0.0; - } - - else - { - th=propa.tha+d*prop.gme; - ds=d-propa.dla; - /* q=0.0795775*prop.wn*ds*pow(th,2.0); */ - q=0.0795775*prop.wn*ds*th*th; - adiffv=aknfe(q*prop.dl[0]/(ds+prop.dl[0]))+aknfe(q*prop.dl[1]/(ds+prop.dl[1])); - a=ds/th; - wa=pow(a*prop.wn,THIRD); - pk=qk/wa; - q=(1.607-pk)*151.0*wa*th+xht; - ar=0.05751*q-4.343*log(q)-aht; - q=(wd1+xd1/d)*mymin(((1.0-0.8*exp(-d/50e3))*prop.dh*prop.wn),6283.2); - wd=25.1/(25.1+sqrt(q)); - adiffv=ar*wd+(1.0-wd)*adiffv+afo; - } - - return adiffv; -} - -double adiff2(double d, prop_type &prop, propa_type &propa) -{ - complex prop_zgnd(prop.zgndreal,prop.zgndimag); - static double wd1, xd1, qk, aht, xht, toh, toho, roh, roho, dto, dto1, dtro, dro, - dro2, drto, dtr, dhh1, dhh2, /* dhec, */ dtof, dto1f, drof, dro2f; - double a, q, pk, rd, ds, dsl, /* dfdh, */ th, wa, /* ar, wd, sf1, */ sf2, /* ec, */ vv, kedr=0.0, arp=0.0, - sdr=0.0, pd=0.0, srp=0.0, kem=0.0, csd=0.0, sdl=0.0, adiffv2=0.0, closs=0.0; - - /* sf1=1.0; */ /* average empirical hilltop foliage scatter factor for 1 obstruction */ - sf2=1.0; /* average empirical hilltop foliage scatter factor for 2 obstructions */ - - /* dfdh=prop.dh; */ - /* ec=0.5*prop.gme; */ - - /* adiff2 must first be run with d==0.0 to set up coefficients */ - if (d==0) - { - q=prop.hg[0]*prop.hg[1]; - qk=prop.he[0]*prop.he[1]-q; - /* dhec=2.73; */ - - if (prop.mdp<0.0) - q+=10.0; - - /* coefficients for a standard four radii, rounded earth computation are prepared */ - wd1=sqrt(1.0+qk/q); - xd1=propa.dla+propa.tha/prop.gme; - q=(1.0-0.8*exp(-propa.dlsa/50e3))*prop.dh; - q*=0.78*exp(-pow(q/16.0,0.25)); - qk=1.0/abs(prop_zgnd); - aht=20.0; - xht=0.0; - a=0.5*(prop.dl[0]*prop.dl[0])/prop.he[0]; - wa=pow(a*prop.wn,THIRD); - pk=qk/wa; - q=(1.607-pk)*151.0*wa*prop.dl[0]/a; - xht=q; - aht+=fht(q,pk); - - - if ((int(prop.dl[1])==0.0) || (prop.the[1]>0.2)) - { - xht+=xht; - aht+=(aht-20.0); - } - - else - { - a=0.5*(prop.dl[1]*prop.dl[1])/prop.he[1]; - wa=pow(a*prop.wn,THIRD); - pk=qk/wa; - q=(1.607-pk)*151.0*wa*prop.dl[1]/a; - xht+=q; - aht+=fht(q,pk); - } - adiffv2=0.0; - } - - else - { - th=propa.tha+d*prop.gme; - - dsl=mymax(d-propa.dla,0.0); - ds=d-propa.dla; - a=ds/th; - wa=pow(a*prop.wn,THIRD); - pk=qk/wa; - toh=prop.hht-(prop.rch[0]-prop.dl[0]*((prop.rch[1]-prop.rch[0])/prop.dist)); - roh=prop.hhr-(prop.rch[0]-(prop.dist-prop.dl[1])*((prop.rch[1]-prop.rch[0])/prop.dist)); - toho=prop.hht-(prop.rch[0]-(prop.dl[0]+dsl)*((prop.hhr-prop.rch[0])/(prop.dist-prop.dl[1]))); - roho=prop.hhr-(prop.hht-dsl*((prop.rch[1]-prop.hht)/dsl)); - dto=sqrt(prop.dl[0]*prop.dl[0]+toh*toh); - dto+=prop.gme*prop.dl[0]; - dto1=sqrt(prop.dl[0]*prop.dl[0]+toho*toho); - dto1+=prop.gme*prop.dl[0]; - dtro=sqrt((prop.dl[0]+dsl)*(prop.dl[0]+dsl)+prop.hhr*prop.hhr); - dtro+=prop.gme*(prop.dl[0]+dsl); - drto=sqrt((prop.dl[1]+dsl)*(prop.dl[1]+dsl)+prop.hht*prop.hht); - drto+=prop.gme*(prop.dl[1]+dsl); - dro=sqrt(prop.dl[1]*prop.dl[1]+roh*roh); - dro+=prop.gme*(prop.dl[1]); - dro2=sqrt(prop.dl[1]*prop.dl[1]+roho*roho); - dro2+=prop.gme*(prop.dl[1]); - dtr=sqrt(prop.dist*prop.dist+(prop.rch[0]-prop.rch[1])*(prop.rch[0]-prop.rch[1])); - dtr+=prop.gme*prop.dist; - dhh1=sqrt((prop.dist-propa.dla)*(prop.dist-propa.dla)+toho*toho); - dhh1+=prop.gme*(prop.dist-propa.dla); - dhh2=sqrt((prop.dist-propa.dla)*(prop.dist-propa.dla)+roho*roho); - dhh2+=prop.gme*(prop.dist-propa.dla); - - /* for 1 obst tree base path */ - dtof=sqrt(prop.dl[0]*prop.dl[0]+(toh-prop.cch)*(toh-prop.cch)); - dtof+=prop.gme*prop.dl[0]; - dto1f=sqrt(prop.dl[0]*prop.dl[0]+(toho-prop.cch)*(toho-prop.cch)); - dto1f+=prop.gme*prop.dl[0]; - drof=sqrt(prop.dl[1]*prop.dl[1]+(roh-prop.cch)*(roh-prop.cch)); - drof+=prop.gme*(prop.dl[1]); - dro2f=sqrt(prop.dl[1]*prop.dl[1]+(roho-prop.cch)*(roho-prop.cch)); - dro2f+=prop.gme*(prop.dl[1]); - - /* saalos coefficients preset for post-obstacle receive path */ - prop.tgh=prop.cch+1.0; - prop.tsgh=prop.hhr; - rd=prop.dl[1]; - - /* two obstacle diffraction calculation */ - if (int(ds)>0) /* there are 2 obstacles */ - { - if(int(prop.dl[1])>0.0) /* receive site past 2nd peak */ - { - /* rounding attenuation */ - q=(1.607-pk)*151.0*wa*th+xht; - /* ar=0.05751*q-10*log10(q)-aht; */ - - /* knife edge vs round weighting */ - q=(1.0-0.8*exp(-d/50e3))*prop.dh; - q=(wd1+xd1/d)*mymin((q*prop.wn),6283.2); - /* wd=25.1/(25.1+sqrt(q)); */ - - q=0.6365*prop.wn; - - if(prop.the[1]<0.2) /* receive grazing angle below 0.2 rad */ - { - /* knife edge attenuation for two obstructions */ - - if(prop.hht < 3400) /* if below tree line, foliage top loss */ - { - vv=q*abs(dto1+dhh1-dtro); - adiffv2=-18.0+sf2*aknfe(vv); - } - else - { - vv=q*abs(dto1+dhh1-dtro); - adiffv2=aknfe(vv); - } - - if(prop.hhr < 3400) - { - vv=q*abs(dro2+dhh2-drto); - adiffv2+=(-18.0+sf2*aknfe(vv)); - } - else - { - vv=q*abs(dro2+dhh2-drto); - adiffv2+=aknfe(vv); - } - /* finally, add clutter loss */ - closs=saalos(rd, prop, propa); - adiffv2+=mymin(22.0,closs); - - } - else /* rcvr site too close to 2nd obs */ - { - /* knife edge attenuation for 1st obs */ - - if(prop.hht < 3400) - { - vv=q*abs(dto1+dhh1-dtro); - adiffv2=-18.0+sf2*aknfe(vv); - } - else - { - vv=q*abs(dto1+dhh1-dtro); - adiffv2=aknfe(vv); - } - - /* weighted calc. of knife vs rounded edge - adiffv2=ar*wd+(1.0-wd)*adiffv2; */ - - /* clutter path loss past 2nd peak */ - if(prop.the[1]<1.22) - { - rd=prop.dl[1]; - - if(prop.the[1]>0.6) /* through foliage downhill */ - { - prop.tgh=prop.cch; - } - else /* close to foliage, rcvr in foliage downslope */ - { - vv=0.6365*prop.wn*abs(dro2+dhh2-drto); - } - adiffv2+=aknfe(vv); - closs=saalos(rd, prop, propa); - adiffv2+=mymin(closs,22.0); - } - else /* rcvr very close to bare cliff or skyscraper */ - { - adiffv2=5.8+25.0; - } - } - } - else /* receive site is atop a 2nd peak */ - { - vv=0.6365*prop.wn*abs(dto+dro-dtr); - adiffv2=5.8 + aknfe(vv); - } - } - else /* for single obstacle */ - { - - if(int(prop.dl[1])>0.0) /* receive site past 1st peak */ - { - - if(prop.the[1]<0.2) /* receive grazing angle less than .2 radians */ - { - vv=0.6365*prop.wn*abs(dto+dro-dtr); - - if(prop.hht < 3400) - { - sdl=18.0; - sdl=pow(10,(-sdl/20)); - /* ke phase difference with respect to direct t-r line */ - kedr=0.159155*prop.wn*abs(dto+dro-dtr); - arp=abs(kedr-(int(kedr))); - kem=aknfe(vv); - kem= pow(10,(-kem/20)); - /* scatter path phase with respect to direct t-r line */ - sdr=0.5+0.159155*prop.wn*abs(dtof+drof-dtr); - srp=abs(sdr-(int(sdr))); - /* difference between scatter and ke phase in radians */ - pd=6.283185307*abs(srp-arp); - /* report pd prior to restriction - keep pd between 0 and pi radians and adjust for 3&4 quadrant */ - if(pd>=3.141592654) - { - pd=6.283185307-pd; - csd=abq_alos(complex(sdl,0)+complex(kem*-cos(pd), kem*-sin(pd))); - } - else - { - csd=abq_alos(complex(sdl,0)+complex(kem*cos(pd), kem*sin(pd))); - } - /*csd=mymax(csd,0.0009); limits maximum loss value to 30.45 db */ - adiffv2=-3.71-10*log10(csd); - } - else - { - adiffv2=aknfe(vv); - } - /* finally, add clutter loss */ - closs=saalos(rd, prop, propa); - adiffv2+=mymin(closs,22.0); - } - else /* receive grazing angle too high */ - { - if(prop.the[1]<1.22) - { - rd=prop.dl[1]; - - if(prop.the[1]>0.6) /* through foliage downhill */ - { - prop.tgh=prop.cch; - } - else /* downhill slope just above foliage */ - { - vv=0.6365*prop.wn*abs(dto+dro-dtr); - adiffv2=aknfe(vv); - } - closs=saalos(rd, prop, propa); - adiffv2+=mymin(22.0,closs); - } - else /* receiver very close to bare cliff or skyscraper */ - { - adiffv2=5.8+25.0; - } - } - } - else /* if occurs, receive site atop first peak */ - { - adiffv2=5.8; - } - } - } - return adiffv2; -} - -double ascat( double d, prop_type &prop, propa_type &propa) -{ - static double ad, rr, etq, h0s; - double h0, r1, r2, z0, ss, et, ett, th, q; - double ascatv, temp; - - if (d==0.0) - { - ad=prop.dl[0]-prop.dl[1]; - rr=prop.he[1]/prop.rch[0]; - - if (ad<0.0) - { - ad=-ad; - rr=1.0/rr; - } - - etq=(5.67e-6*prop.ens-2.32e-3)*prop.ens+0.031; - h0s=-15.0; - ascatv=0.0; - } - - else - { - if (h0s>15.0) - h0=h0s; - else - { - th=prop.the[0]+prop.the[1]+d*prop.gme; - r2=2.0*prop.wn*th; - r1=r2*prop.he[0]; - r2*=prop.he[1]; - - if (r1<0.2 && r2<0.2) - return 1001.0; // <==== early return - - ss=(d-ad)/(d+ad); - q=rr/ss; - ss=mymax(0.1,ss); - q=mymin(mymax(0.1,q),10.0); - z0=(d-ad)*(d+ad)*th*0.25/d; - /* et=(etq*exp(-pow(mymin(1.7,z0/8.0e3),6.0))+1.0)*z0/1.7556e3; */ - - temp=mymin(1.7,z0/8.0e3); - temp=temp*temp*temp*temp*temp*temp; - et=(etq*exp(-temp)+1.0)*z0/1.7556e3; - - ett=mymax(et,1.0); - h0=(h0f(r1,ett)+h0f(r2,ett))*0.5; - h0+=mymin(h0,(1.38-log(ett))*log(ss)*log(q)*0.49); - h0=FORTRAN_DIM(h0,0.0); - - if (et<1.0) - { - /* h0=et*h0+(1.0-et)*4.343*log(pow((1.0+1.4142/r1)*(1.0+1.4142/r2),2.0)*(r1+r2)/(r1+r2+2.8284)); */ - - temp=((1.0+1.4142/r1)*(1.0+1.4142/r2)); - h0=et*h0+(1.0-et)*4.343*log((temp*temp)*(r1+r2)/(r1+r2+2.8284)); - } - - if (h0>15.0 && h0s>=0.0) - h0=h0s; - } - - h0s=h0; - th=propa.tha+d*prop.gme; - /* ascatv=ahd(th*d)+4.343*log(47.7*prop.wn*pow(th,4.0))-0.1*(prop.ens-301.0)*exp(-th*d/40e3)+h0; */ - ascatv=ahd(th*d)+4.343*log(47.7*prop.wn*(th*th*th*th))-0.1*(prop.ens-301.0)*exp(-th*d/40e3)+h0; - } - - return ascatv; -} - -double qerfi(double q) -{ - double x, t, v; - double c0=2.515516698; - double c1=0.802853; - double c2=0.010328; - double d1=1.432788; - double d2=0.189269; - double d3=0.001308; - - x=0.5-q; - t=mymax(0.5-fabs(x),0.000001); - t=sqrt(-2.0*log(t)); - v=t-((c2*t+c1)*t+c0)/(((d3*t+d2)*t+d1)*t+1.0); - - if (x<0.0) - v=-v; - - return v; -} - -void qlrps(double fmhz, double zsys, double en0, int ipol, double eps, double sgm, prop_type &prop) -{ - double gma=157e-9; - - prop.wn=fmhz/47.7; - prop.ens=en0; - - if (zsys!=0.0) - prop.ens*=exp(-zsys/9460.0); - - prop.gme=gma*(1.0-0.04665*exp(prop.ens/179.3)); - complex zq, prop_zgnd(prop.zgndreal,prop.zgndimag); - zq=complex (eps,376.62*sgm/prop.wn); - prop_zgnd=sqrt(zq-1.0); - - if (ipol!=0.0) - prop_zgnd=prop_zgnd/zq; - - prop.zgndreal=prop_zgnd.real(); - prop.zgndimag=prop_zgnd.imag(); - -} - -double alos(double d, prop_type &prop, propa_type &propa) -{ - complex prop_zgnd(prop.zgndreal,prop.zgndimag); - static double wls; - complex r; - double s, sps, q; - double alosv; - - if (d==0.0) - { - wls=0.021/(0.021+prop.wn*prop.dh/mymax(10e3,propa.dlsa)); - alosv=0.0; - } - - else - { - q=(1.0-0.8*exp(-d/50e3))*prop.dh; - s=0.78*q*exp(-pow(q/16.0,0.25)); - q=prop.he[0]+prop.he[1]; - sps=q/sqrt(d*d+q*q); - r=(sps-prop_zgnd)/(sps+prop_zgnd)*exp(-mymin(10.0,prop.wn*s*sps)); - q=abq_alos(r); - - if (q<0.25 || q1.57) - q=3.14-2.4649/q; - - alosv=(-4.343*log(abq_alos(complex(cos(q),-sin(q))+r))-alosv)*wls+alosv; - - } - return alosv; -} - - -double alos2(double d, prop_type &prop, propa_type &propa) -{ - complex prop_zgnd(prop.zgndreal,prop.zgndimag); - complex r; - double cd, cr, dr, hr, hrg, ht, htg, hrp, re, s, sps, q, pd, drh; - /* int rp; */ - double alosv; - - cd=0.0; - cr=0.0; - htg=prop.hg[0]; - hrg=prop.hg[1]; - ht=prop.ght; - hr=prop.ghr; - /* rp=prop.rpl; */ - hrp=prop.rph; - pd=prop.dist; - - if (d==0.0) - { - alosv=0.0; - } - - else - { - q=prop.he[0]+prop.he[1]; - sps=q/sqrt(pd*pd+q*q); - q=(1.0-0.8*exp(-pd/50e3))*prop.dh; - - if (prop.mdp<0) - { - dr=pd/(1+hrg/htg); - - if (dr<(0.5*pd)) - { - drh=6378137.0-sqrt(-(0.5*pd)*(0.5*pd)+6378137.0*6378137.0+(0.5*pd-dr)*(0.5*pd-dr)); - } - else - { - drh=6378137.0-sqrt(-(0.5*pd)*(0.5*pd)+6378137.0*6378137.0+(dr-0.5*pd)*(dr-0.5*pd)); - } - - if ((sps<0.05) && (prop.cch>hrg) && (prop.dist< prop.dl[0])) /* if far from transmitter and receiver below canopy */ - { - cd=mymax(0.01,pd*(prop.cch-hrg)/(htg-hrg)); - cr=mymax(0.01,pd-dr+dr*(prop.cch-drh)/htg); - q=((1.0-0.8*exp(-pd/50e3))*prop.dh*(mymin(-20*log10(cd/cr),1.0))); - } - } - - s=0.78*q*exp(-pow(q/16.0,0.25)); - q=exp(-mymin(10.0,prop.wn*s*sps)); - r=q*(sps-prop_zgnd)/(sps+prop_zgnd); - q=abq_alos(r); - q=mymin(q,1.0); - - if (q<0.25 || q(cos(q),sin(q))+r); - alosv=-10*log10(re); - prop.tgh=prop.hg[0]; /*tx above gnd hgt set to antenna height AGL */ - prop.tsgh=prop.rch[0]-prop.hg[0]; /* tsgh set to tx site gl AMSL */ - - if ((prop.hg[1]=0) - { - propv.mdvar=mdvarx; - propv.lvar=mymax(propv.lvar,4); - } - - if (klimx>0) - { - propv.klim=klimx; - propv.lvar=5; - } -} - - -void lrprop (double d, prop_type &prop, propa_type &propa) -{ - /* PaulM_lrprop used for ITM */ - static bool wlos, wscat; - static double dmin, xae; - complex prop_zgnd(prop.zgndreal,prop.zgndimag); - double a0, a1, a2, a3, a4, a5, a6; - double d0, d1, d2, d3, d4, d5, d6; - bool wq; - double q; - int j; - - if (prop.mdp!=0) - { - for (j=0; j<2; j++) - propa.dls[j]=sqrt(2.0*prop.he[j]/prop.gme); - - propa.dlsa=propa.dls[0]+propa.dls[1]; - propa.dla=prop.dl[0]+prop.dl[1]; - propa.tha=mymax(prop.the[0]+prop.the[1],-propa.dla*prop.gme); - wlos=false; - wscat=false; - - if (prop.wn<0.838 || prop.wn>210.0) - prop.kwx=mymax(prop.kwx,1); - - for (j=0; j<2; j++) - if (prop.hg[j]<1.0 || prop.hg[j]>1000.0) - prop.kwx=mymax(prop.kwx,1); - - for (j=0; j<2; j++) - if (abs(prop.the[j]) >200e-3 || prop.dl[j]<0.1*propa.dls[j] || prop.dl[j]>3.0*propa.dls[j] ) - prop.kwx=mymax(prop.kwx,3); - - if (prop.ens < 250.0 || prop.ens > 400.0 || prop.gme < 75e-9 || prop.gme > 250e-9 || prop_zgnd.real() <= abs(prop_zgnd.imag()) || prop.wn < 0.419 || prop.wn > 420.0) - prop.kwx=4; - - for (j=0; j<2; j++) - if (prop.hg[j]<0.5 || prop.hg[j]>3000.0) - prop.kwx=4; - - dmin=abs(prop.he[0]-prop.he[1])/200e-3; - q=adiff(0.0,prop,propa); - /* xae=pow(prop.wn*pow(prop.gme,2.),-THIRD); -- JDM made argument 2 a double */ - xae=pow(prop.wn*(prop.gme*prop.gme),-THIRD); /* No 2nd pow() */ - d3=mymax(propa.dlsa,1.3787*xae+propa.dla); - d4=d3+2.7574*xae; - a3=adiff(d3,prop,propa); - a4=adiff(d4,prop,propa); - propa.emd=(a4-a3)/(d4-d3); - propa.aed=a3-propa.emd*d3; - } - - if (prop.mdp>=0) - { - prop.mdp=0; - prop.dist=d; - } - - if (prop.dist>0.0) - { - if (prop.dist>1000e3) - prop.kwx=mymax(prop.kwx,1); - - if (prop.dist2000e3) - prop.kwx=4; - } - - if (prop.dist=0.0) - { - d0=mymin(d0,0.5*propa.dla); - d1=d0+0.25*(propa.dla-d0); - } - - else - d1=mymax(-propa.aed/propa.emd,0.25*propa.dla); - - a1=alos(d1,prop,propa); - wq=false; - - if (d0=0.0 || propa.ak2>0.0; - - if (wq) - { - propa.ak1=(a2-a0-propa.ak2*q)/(d2-d0); - - if (propa.ak1<0.0) - { - propa.ak1=0.0; - propa.ak2=FORTRAN_DIM(a2,a0)/q; - - if (propa.ak2==0.0) - propa.ak1=propa.emd; - } - } - - else - { - propa.ak2=0.0; - propa.ak1=(a2-a1)/(d2-d1); - - if (propa.ak1<=0.0) - propa.ak1=propa.emd; - } - } - - else - { - propa.ak1=(a2-a1)/(d2-d1); - propa.ak2=0.0; - - if (propa.ak1<=0.0) - propa.ak1=propa.emd; - } - - propa.ael=a2-propa.ak1*d2-propa.ak2*log(d2); - wlos=true; - } - - if (prop.dist>0.0) - prop.aref=propa.ael+propa.ak1*prop.dist+propa.ak2*log(prop.dist); - - } - - if (prop.dist<=0.0 || prop.dist>=propa.dlsa) - { - if(!wscat) - { - q=ascat(0.0,prop,propa); - d5=propa.dla+200e3; - d6=d5+200e3; - a6=ascat(d6,prop,propa); - a5=ascat(d5,prop,propa); - - if (a5<1000.0) - { - propa.ems=(a6-a5)/200e3; - propa.dx=mymax(propa.dlsa,mymax(propa.dla+0.3*xae*log(47.7*prop.wn),(a5-propa.aed-propa.ems*d5)/(propa.emd-propa.ems))); - propa.aes=(propa.emd-propa.ems)*propa.dx+propa.aed; - } - - else - { - propa.ems=propa.emd; - propa.aes=propa.aed; - propa.dx=10.e6; - } - - wscat=true; - } - - if (prop.dist>propa.dx) - prop.aref=propa.aes+propa.ems*prop.dist; - else - prop.aref=propa.aed+propa.emd*prop.dist; - } - - prop.aref=mymax(prop.aref,0.0); -} - - - - -void lrprop2(double d, prop_type &prop, propa_type &propa) -{ - /* ITWOM_lrprop2 */ - static bool wlos, wscat; - static double dmin, xae; - complex prop_zgnd(prop.zgndreal,prop.zgndimag); - double pd1; - double a0, a1, a2, a3, a4, a5, a6, iw; - double d0, d1, d2, d3, d4, d5, d6; - bool wq; - double q; - int j; - - iw=prop.tiw; - pd1=prop.dist; - propa.dx=2000000.0; - - if (prop.mdp!=0) /* if oper. mode is not 0, i.e. not area mode ongoing */ - { - for (j=0; j<2; j++) - propa.dls[j]=sqrt(2.0*prop.he[j]/prop.gme); - - propa.dlsa=propa.dls[0]+propa.dls[1]; - propa.dlsa=mymin(propa.dlsa,1000000.0); - propa.dla=prop.dl[0]+prop.dl[1]; - propa.tha=mymax(prop.the[0]+prop.the[1],-propa.dla*prop.gme); - wlos=false; - wscat=false; - - /*checking for parameters-in-range, error codes set if not */ - - if (prop.wn<0.838 || prop.wn>210.0) - prop.kwx=mymax(prop.kwx,1); - - for (j=0; j<2; j++) - if (prop.hg[j]<1.0 || prop.hg[j]>1000.0) - prop.kwx=mymax(prop.kwx,1); - - if(abs(prop.the[0])>200e-3) - prop.kwx=mymax(prop.kwx,3); - - if(abs(prop.the[1])>1.220) - prop.kwx=mymax(prop.kwx,3); - - /*for (j=0; j<2; j++) - if (prop.dl[j]<0.1*propa.dls[j] || prop.dl[j]>3.0*propa.dls[j]) - prop.kwx=mymax(prop.kwx,3); */ - - if (prop.ens<250.0 || prop.ens>400.0 || prop.gme<75e-9 || prop.gme>250e-9 || prop_zgnd.real() <=abs(prop_zgnd.imag()) || prop.wn<0.419 || prop.wn>420.0) - prop.kwx=4; - - for (j=0; j<2; j++) - - if (prop.hg[j]<0.5 || prop.hg[j]>3000.0) - prop.kwx=4; - - dmin=abs(prop.he[0]-prop.he[1])/200e-3; - q=adiff2(0.0,prop,propa); - xae=pow(prop.wn*(prop.gme*prop.gme),-THIRD); - d3=mymax(propa.dlsa,1.3787*xae+propa.dla); - d4=d3+2.7574*xae; - a3=adiff2(d3,prop,propa); - a4=adiff2(d4,prop,propa); - propa.emd=(a4-a3)/(d4-d3); - propa.aed=a3-propa.emd*d3; - } - - if (prop.mdp>=0) /* if initializing the area mode */ - { - prop.mdp=0; /* area mode is initialized */ - prop.dist=d; - } - - if (prop.dist>0.0) - { - if (prop.dist>1000e3) /* prop.dist being in meters, if greater than 1000 km, kwx=1 */ - prop.kwx=mymax(prop.kwx,1); - - if (prop.dist2000e3) - prop.kwx=4; - } - - if (prop.dist0.0) - { - prop.aref=propa.aed+propa.emd*prop.dist; - } - else - { - if (propa.aed==0.0) - { - d0=mymin(d0,0.5*propa.dla); - d1=d0+0.25*(propa.dla-d0); - } - else /* aed less than zero */ - { - d1=mymax(-propa.aed/propa.emd,0.25*propa.dla); - } - a1=alos2(d1,prop,propa); - wq=false; - - if (d0=0.0 || propa.ak2>0.0; - - if (wq) - { - propa.ak1=(a2-a0-propa.ak2*q)/(d2-d0); - - if (propa.ak1<0.0) - { - propa.ak1=0.0; - propa.ak2=FORTRAN_DIM(a2,a0)/q; - - if (propa.ak2==0.0) - propa.ak1=propa.emd; - } - } - } - - if(!wq) - { - propa.ak1=FORTRAN_DIM(a2,a1)/(d2-d1); - propa.ak2=0.0; - - if (propa.ak1==0.0) - propa.ak1=propa.emd; - - } - propa.ael=a2-propa.ak1*d2-propa.ak2*log(d2); - wlos=true; - } - } - } - else /* for ITWOM point-to-point mode */ - { - - if (!wlos) - { - q=alos2(0.0,prop,propa); /* coefficient setup */ - wlos=true; - } - - if (prop.los==1) /* if line of sight */ - { - prop.aref=alos2(pd1,prop,propa); - } - else - { - if (int(prop.dist-prop.dl[0])==0) /* if at 1st horiz */ - { - prop.aref=5.8+alos2(pd1,prop,propa); - } - else if (int(prop.dist-prop.dl[0])>0.0) /* if past 1st horiz */ - { - q=adiff2(0.0,prop,propa); - prop.aref=adiff2(pd1,prop,propa); - } - else - { - prop.aref=1.0; - } - - } - } - } - - /* los and diff. range coefficents done. Starting troposcatter */ - if (prop.dist<=0.0 || prop.dist>=propa.dlsa) - { - if (iw==0.0) /* area mode */ - { - if(!wscat) - { - q=ascat(0.0,prop,propa); - d5=propa.dla+200e3; - d6=d5+200e3; - a6=ascat(d6,prop,propa); - a5=ascat(d5,prop,propa); - - if (a5<1000.0) - { - propa.ems=(a6-a5)/200e3; - propa.dx=mymax(propa.dlsa,mymax(propa.dla+0.3*xae*log(47.7*prop.wn),(a5-propa.aed-propa.ems*d5)/(propa.emd-propa.ems))); - - propa.aes=(propa.emd-propa.ems)*propa.dx+propa.aed; - } - - else - { - propa.ems=propa.emd; - propa.aes=propa.aed; - propa.dx=10000000; - } - wscat=true; - } - - if (prop.dist>propa.dx) - { - prop.aref=propa.aes+propa.ems*prop.dist; - } - else - { - prop.aref=propa.aed+propa.emd*prop.dist; - } - } - else /* ITWOM mode q used to preset coefficients with zero input */ - { - if(!wscat) - { - d5=0.0; - d6=0.0; - q=ascat(0.0,prop,propa); - a6=ascat(pd1,prop,propa); - q=adiff2(0.0,prop,propa); - a5=adiff2(pd1,prop,propa); - - if (a5<=a6) - { - propa.dx=10000000; - prop.aref=a5; - } - else - { - propa.dx=propa.dlsa; - prop.aref=a6; - } - wscat=true; - } - } - } - prop.aref=mymax(prop.aref,0.0); -} - - -double curve (double const &c1, double const &c2, double const &x1, - double const &x2, double const &x3, double const &de) -{ - /* return (c1+c2/(1.0+pow((de-x2)/x3,2.0)))*pow(de/x1,2.0)/(1.0+pow(de/x1,2.0)); */ - double temp1, temp2; - - temp1=(de-x2)/x3; - temp2=de/x1; - - temp1*=temp1; - temp2*=temp2; - - return (c1+c2/(1.0+temp1))*temp2/(1.0+temp2); -} - -double avar(double zzt, double zzl, double zzc, prop_type &prop, propv_type &propv) -{ - static int kdv; - static double dexa, de, vmd, vs0, sgl, sgtm, sgtp, sgtd, tgtd, - gm, gp, cv1, cv2, yv1, yv2, yv3, csm1, csm2, ysm1, ysm2, - ysm3, csp1, csp2, ysp1, ysp2, ysp3, csd1, zd, cfm1, cfm2, - cfm3, cfp1, cfp2, cfp3; - - double bv1[7]={-9.67,-0.62,1.26,-9.21,-0.62,-0.39,3.15}; - double bv2[7]={12.7,9.19,15.5,9.05,9.19,2.86,857.9}; - double xv1[7]={144.9e3,228.9e3,262.6e3,84.1e3,228.9e3,141.7e3,2222.e3}; - double xv2[7]={190.3e3,205.2e3,185.2e3,101.1e3,205.2e3,315.9e3,164.8e3}; - double xv3[7]={133.8e3,143.6e3,99.8e3,98.6e3,143.6e3,167.4e3,116.3e3}; - double bsm1[7]={2.13,2.66,6.11,1.98,2.68,6.86,8.51}; - double bsm2[7]={159.5,7.67,6.65,13.11,7.16,10.38,169.8}; - double xsm1[7]={762.2e3,100.4e3,138.2e3,139.1e3,93.7e3,187.8e3,609.8e3}; - double xsm2[7]={123.6e3,172.5e3,242.2e3,132.7e3,186.8e3,169.6e3,119.9e3}; - double xsm3[7]={94.5e3,136.4e3,178.6e3,193.5e3,133.5e3,108.9e3,106.6e3}; - double bsp1[7]={2.11,6.87,10.08,3.68,4.75,8.58,8.43}; - double bsp2[7]={102.3,15.53,9.60,159.3,8.12,13.97,8.19}; - double xsp1[7]={636.9e3,138.7e3,165.3e3,464.4e3,93.2e3,216.0e3,136.2e3}; - double xsp2[7]={134.8e3,143.7e3,225.7e3,93.1e3,135.9e3,152.0e3,188.5e3}; - double xsp3[7]={95.6e3,98.6e3,129.7e3,94.2e3,113.4e3,122.7e3,122.9e3}; - double bsd1[7]={1.224,0.801,1.380,1.000,1.224,1.518,1.518}; - double bzd1[7]={1.282,2.161,1.282,20.,1.282,1.282,1.282}; - double bfm1[7]={1.0,1.0,1.0,1.0,0.92,1.0,1.0}; - double bfm2[7]={0.0,0.0,0.0,0.0,0.25,0.0,0.0}; - double bfm3[7]={0.0,0.0,0.0,0.0,1.77,0.0,0.0}; - double bfp1[7]={1.0,0.93,1.0,0.93,0.93,1.0,1.0}; - double bfp2[7]={0.0,0.31,0.0,0.19,0.31,0.0,0.0}; - double bfp3[7]={0.0,2.00,0.0,1.79,2.00,0.0,0.0}; - static bool ws, w1; - double rt=7.8, rl=24.0, avarv, q, vs, zt, zl, zc; - double sgt, yr, temp1, temp2; - int temp_klim=propv.klim-1; - - if (propv.lvar>0) - { - switch (propv.lvar) - { - default: - if (propv.klim<=0 || propv.klim>7) - { - propv.klim=5; - temp_klim=4; - prop.kwx=mymax(prop.kwx,2); - } - - cv1=bv1[temp_klim]; - cv2=bv2[temp_klim]; - yv1=xv1[temp_klim]; - yv2=xv2[temp_klim]; - yv3=xv3[temp_klim]; - csm1=bsm1[temp_klim]; - csm2=bsm2[temp_klim]; - ysm1=xsm1[temp_klim]; - ysm2=xsm2[temp_klim]; - ysm3=xsm3[temp_klim]; - csp1=bsp1[temp_klim]; - csp2=bsp2[temp_klim]; - ysp1=xsp1[temp_klim]; - ysp2=xsp2[temp_klim]; - ysp3=xsp3[temp_klim]; - csd1=bsd1[temp_klim]; - zd=bzd1[temp_klim]; - cfm1=bfm1[temp_klim]; - cfm2=bfm2[temp_klim]; - cfm3=bfm3[temp_klim]; - cfp1=bfp1[temp_klim]; - cfp2=bfp2[temp_klim]; - cfp3=bfp3[temp_klim]; - - case 4: - kdv=propv.mdvar; - ws=kdv>=20; - - if (ws) - kdv-=20; - - w1=kdv>=10; - - if (w1) - kdv-=10; - - if (kdv<0 || kdv>3) - { - kdv=0; - prop.kwx=mymax(prop.kwx,2); - } - - case 3: - q=log(0.133*prop.wn); - - /* gm=cfm1+cfm2/(pow(cfm3*q,2.0)+1.0); */ - /* gp=cfp1+cfp2/(pow(cfp3*q,2.0)+1.0); */ - - gm=cfm1+cfm2/((cfm3*q*cfm3*q)+1.0); - gp=cfp1+cfp2/((cfp3*q*cfp3*q)+1.0); - - case 2: - dexa=sqrt(18e6*prop.he[0])+sqrt(18e6*prop.he[1])+pow((575.7e12/prop.wn),THIRD); - - case 1: - if (prop.dist3.1 || fabs(zl)>3.1 || fabs(zc)>3.1) - prop.kwx=mymax(prop.kwx,1); - - if (zt<0.0) - sgt=sgtm; - - else if (zt<=zd) - sgt=sgtp; - - else - sgt=sgtd+tgtd/zt; - - /* vs=vs0+pow(sgt*zt,2.0)/(rt+zc*zc)+pow(sgl*zl,2.0)/(rl+zc*zc); */ - - temp1=sgt*zt; - temp2=sgl*zl; - - vs=vs0+(temp1*temp1)/(rt+zc*zc)+(temp2*temp2)/(rl+zc*zc); - - if (kdv==0) - { - yr=0.0; - propv.sgc=sqrt(sgt*sgt+sgl*sgl+vs); - } - - else if (kdv==1) - { - yr=sgt*zt; - propv.sgc=sqrt(sgl*sgl+vs); - } - - else if (kdv==2) - { - yr=sqrt(sgt*sgt+sgl*sgl)*zt; - propv.sgc=sqrt(vs); - } - - else - { - yr=sgt*zt+sgl*zl; - propv.sgc=sqrt(vs); - } - - avarv=prop.aref-vmd-yr-propv.sgc*zc; - - if (avarv<0.0) - avarv=avarv*(29.0-avarv)/(29.0-10.0*avarv); - - return avarv; -} - - -void hzns(double pfl[], prop_type &prop) -{ - /* Used only with ITM 1.2.2 */ - bool wq; - int np; - double xi, za, zb, qc, q, sb, sa; - - np=(int)pfl[0]; - xi=pfl[1]; - za=pfl[2]+prop.hg[0]; - zb=pfl[np+2]+prop.hg[1]; - qc=0.5*prop.gme; - q=qc*prop.dist; - prop.the[1]=(zb-za)/prop.dist; - prop.the[0]=prop.the[1]-q; - prop.the[1]=-prop.the[1]-q; - prop.dl[0]=prop.dist; - prop.dl[1]=prop.dist; - - if (np>=2) - { - sa=0.0; - sb=prop.dist; - wq=true; - - for (int i=1; i0.0) - { - prop.the[0]+=q/sa; - prop.dl[0]=sa; - wq=false; - } - - if (!wq) - { - q=pfl[i+2]-(qc*sb+prop.the[1])*sb-zb; - - if (q>0.0) - { - prop.the[1]+=q/sb; - prop.dl[1]=sb; - } - } - } - } -} - - -void hzns2(double pfl[], prop_type &prop, propa_type &propa) -{ - bool wq; - int np, rp, i, j; - double xi, za, zb, qc, q, sb, sa, dr, dshh; - - np=(int)pfl[0]; - xi=pfl[1]; - za=pfl[2]+prop.hg[0]; - zb=pfl[np+2]+prop.hg[1]; - prop.tiw=xi; - prop.ght=za; - prop.ghr=zb; - qc=0.5*prop.gme; - q=qc*prop.dist; - prop.the[1]=atan((zb-za)/prop.dist); - prop.the[0]=(prop.the[1])-q; - prop.the[1]=-prop.the[1]-q; - prop.dl[0]=prop.dist; - prop.dl[1]=prop.dist; - prop.hht=0.0; - prop.hhr=0.0; - prop.los=1; - - if(np>=2) - { - sa=0.0; - sb=prop.dist; - wq=true; - - for(j=1; j0.0) - { - prop.los=0; - prop.the[0]+=q/sa; - prop.dl[0]=sa; - prop.the[0]=mymin(prop.the[0],1.569); - prop.hht=pfl[j+2]; - wq=false; - } - } - - if(!wq) - { - for(i=1; i0.0) - { - prop.the[1]+=q/(prop.dist-sb); - prop.the[1]=mymin(prop.the[1],1.57); - prop.the[1]=mymax(prop.the[1],-1.568); - prop.hhr=pfl[np+2-i]; - prop.dl[1]=mymax(0.0,prop.dist-sb); - } - } - prop.the[0]=atan((prop.hht-za)/prop.dl[0])-0.5*prop.gme*prop.dl[0]; - prop.the[1]=atan((prop.hhr-zb)/prop.dl[1])-0.5*prop.gme*prop.dl[1]; - } - } - - if((prop.dl[1])<(prop.dist)) - { - dshh=prop.dist-prop.dl[0]-prop.dl[1]; - - if(int(dshh)==0) /* one obstacle */ - { - dr=prop.dl[1]/(1+zb/prop.hht); - } - else /* two obstacles */ - { - dr=prop.dl[1]/(1+zb/prop.hhr); - } - } - else /* line of sight */ - { - dr=(prop.dist)/(1+zb/za); - } - rp=2+(int)(floor(0.5+dr/xi)); - prop.rpl=rp; - prop.rph=pfl[rp]; -} - - -void z1sq1 (double z[], const double &x1, const double &x2, double& z0, double& zn) -{ - /* Used only with ITM 1.2.2 */ - double xn, xa, xb, x, a, b; - int n, ja, jb; - - xn=z[0]; - xa=int(FORTRAN_DIM(x1/z[1],0.0)); - xb=xn-int(FORTRAN_DIM(xn,x2/z[1])); - - if (xb<=xa) - { - xa=FORTRAN_DIM(xa,1.0); - xb=xn-FORTRAN_DIM(xn,xb+1.0); - } - - ja=(int)xa; - jb=(int)xb; - n=jb-ja; - xa=xb-xa; - x=-0.5*xa; - xb+=x; - a=0.5*(z[ja+2]+z[jb+2]); - b=0.5*(z[ja+2]-z[jb+2])*x; - - for (int i=2; i<=n; ++i) - { - ++ja; - x+=1.0; - a+=z[ja+2]; - b+=z[ja+2]*x; - } - - a/=xa; - b=b*12.0/((xa*xa+2.0)*xa); - z0=a-b*xb; - zn=a+b*(xn-xb); -} - -void z1sq2(double z[], const double &x1, const double &x2, double& z0, double& zn) -{ - /* corrected for use with ITWOM */ - double xn, xa, xb, x, a, b, bn; - int n, ja, jb; - - xn=z[0]; - xa=int(FORTRAN_DIM(x1/z[1],0.0)); - xb=xn-int(FORTRAN_DIM(xn,x2/z[1])); - - if (xb<=xa) - { - xa=FORTRAN_DIM(xa,1.0); - xb=xn-FORTRAN_DIM(xn,xb+1.0); - } - - ja=(int)xa; - jb=(int)xb; - xa=(2*int((xb-xa)/2))-1; - x=-0.5*(xa+1); - xb+=x; - ja=jb-1-(int)xa; - n=jb-ja; - a=(z[ja+2]+z[jb+2]); - b=(z[ja+2]-z[jb+2])*x; - bn=2*(x*x); - - for (int i=2; i<=n; ++i) - { - ++ja; - x+=1.0; - bn+=(x*x); - a+=z[ja+2]; - b+=z[ja+2]*x; - } - - a/=(xa+2); - b=b/bn; - z0=a-(b*xb); - zn=a+(b*(xn-xb)); -} - -double qtile (const int &nn, double a[], const int &ir) -{ - double q=0.0, r; /* q initialization -- KD2BD */ - int m, n, i, j, j1=0, i0=0, k; /* more initializations -- KD2BD */ - bool done=false; - bool goto10=true; - - m=0; - n=nn; - k=mymin(mymax(0,ir),n); - - while (!done) - { - if (goto10) - { - q=a[k]; - i0=m; - j1=n; - } - - i=i0; - - while (i<=n && a[i]>=q) - i++; - - if (i>n) - i=n; - - j=j1; - - while (j>=m && a[j]<=q) - j--; - - if (jk) - { - a[k]=a[j]; - a[j]=q; - n=j-1; - goto10=true; - } - - else - done=true; - } - - return q; -} - -double qerf(const double &z) -{ - double b1=0.319381530, b2=-0.356563782, b3=1.781477937; - double b4=-1.821255987, b5=1.330274429; - double rp=4.317008, rrt2pi=0.398942280; - double t, x, qerfv; - - x=z; - t=fabs(x); - - if (t>=10.0) - qerfv=0.0; - else - { - t=rp/(t+rp); - qerfv=exp(-0.5*x*x)*rrt2pi*((((b5*t+b4)*t+b3)*t+b2)*t+b1)*t; - } - - if (x<0.0) - qerfv=1.0-qerfv; - - return qerfv; -} - - -double d1thx(double pfl[], const double &x1, const double &x2) -{ - int np, ka, kb, n, k, j; - double d1thxv, sn, xa, xb; - double *s; - - np=(int)pfl[0]; - xa=x1/pfl[1]; - xb=x2/pfl[1]; - d1thxv=0.0; - - if (xb-xa<2.0) // exit out - return d1thxv; - - ka=(int)(0.1*(xb-xa+8.0)); - ka=mymin(mymax(4,ka),25); - n=10*ka-5; - kb=n-ka+1; - sn=n-1; - assert((s=new double[n+2])!=0); - s[0]=sn; - s[1]=1.0; - xb=(xb-xa)/sn; - k=(int)(xa+1.0); - xa-=(double)k; - - for (j=0; j0.0 && k0.0 && k1.5*prop.dist) - { - z1sq1(pfl,xl[0],xl[1],za,zb); - prop.he[0]=prop.hg[0]+FORTRAN_DIM(pfl[2],za); - prop.he[1]=prop.hg[1]+FORTRAN_DIM(pfl[np+2],zb); - - for (j=0; j<2; j++) - prop.dl[j]=sqrt(2.0*prop.he[j]/prop.gme)*exp(-0.07*sqrt(prop.dh/mymax(prop.he[j],5.0))); - - q=prop.dl[0]+prop.dl[1]; - - if (q<=prop.dist) /* if there is a rounded horizon, or two obstructions, in the path */ - { - /* q=pow(prop.dist/q,2.0); */ - temp=prop.dist/q; - q=temp*temp; - - for (j=0; j<2; j++) - { - prop.he[j]*=q; /* tx effective height set to be path dist/distance between obstacles */ - prop.dl[j]=sqrt(2.0*prop.he[j]/prop.gme)*exp(-0.07*sqrt(prop.dh/mymax(prop.he[j],5.0))); - } - } - - for (j=0; j<2; j++) /* original empirical adjustment? uses delta-h to adjust grazing angles */ - { - q=sqrt(2.0*prop.he[j]/prop.gme); - prop.the[j]=(0.65*prop.dh*(q/prop.dl[j]-1.0)-2.0*prop.he[j])/q; - } - } - - else - { - z1sq1(pfl,xl[0],0.9*prop.dl[0],za,q); - z1sq1(pfl,prop.dist-0.9*prop.dl[1],xl[1],q,zb); - prop.he[0]=prop.hg[0]+FORTRAN_DIM(pfl[2],za); - prop.he[1]=prop.hg[1]+FORTRAN_DIM(pfl[np+2],zb); - } - - prop.mdp=-1; - propv.lvar=mymax(propv.lvar,3); - - if (mdvarx>=0) - { - propv.mdvar=mdvarx; - propv.lvar=mymax(propv.lvar,4); - } - - if (klimx>0) - { - propv.klim=klimx; - propv.lvar=5; - } - - lrprop(0.0,prop,propa); -} - -void qlrpfl2(double pfl[], int klimx, int mdvarx, prop_type &prop, propa_type &propa, propv_type &propv) -{ - int np, j; - double xl[2], dlb, q, za, zb, temp, rad, rae1, rae2; - - prop.dist=pfl[0]*pfl[1]; - np=(int)pfl[0]; - hzns2(pfl,prop, propa); - dlb=prop.dl[0]+prop.dl[1]; - prop.rch[0]=prop.hg[0]+pfl[2]; - prop.rch[1]=prop.hg[1]+pfl[np+2]; - - for (j=0; j<2; j++) - xl[j]=mymin(15.0*prop.hg[j],0.1*prop.dl[j]); - - xl[1]=prop.dist-xl[1]; - prop.dh=d1thx2(pfl,xl[0],xl[1],propa); - - if ((np<1) || (pfl[1]>150.0)) - { - /* for TRANSHORIZON; diffraction over a mutual horizon, or for one or more obstructions */ - if (dlb<1.5*prop.dist) - { - z1sq2(pfl,xl[0],0.9*prop.dl[0],za,q); - z1sq2(pfl,prop.dist-0.9*prop.dl[1],xl[1],q,zb); - prop.he[0]=prop.hg[0]+FORTRAN_DIM(pfl[2],za); - prop.he[1]=prop.hg[1]+FORTRAN_DIM(pfl[np+2],zb); - } - - /* for a Line-of-Sight path */ - else - { - z1sq2(pfl,xl[0],xl[1],za,zb); - prop.he[0]=prop.hg[0]+FORTRAN_DIM(pfl[2],za); - prop.he[1]=prop.hg[1]+FORTRAN_DIM(pfl[np+2],zb); - - for (j=0; j<2; j++) - prop.dl[j]=sqrt(2.0*prop.he[j]/prop.gme)*exp(-0.07*sqrt(prop.dh/mymax(prop.he[j],5.0))); - - /* for one or more obstructions only NOTE buried as in ITM FORTRAN and DLL, not functional */ - if ((prop.dl[0]+prop.dl[1])<=prop.dist) - { - /* q=pow(prop.dist/(dl[0]+dl[1])),2.0); */ - temp=prop.dist/(prop.dl[0]+prop.dl[1]); - q=temp*temp; - } - - for (j=0; j<2; j++) - { - prop.he[j]*=q; - prop.dl[j]=sqrt(2.0*prop.he[j]/prop.gme)*exp(-0.07*sqrt(prop.dh/mymax(prop.he[j],5.0))); - } - - /* this sets (or resets) prop.the, and is not in The Guide FORTRAN QLRPFL */ - for (j=0; j<2; j++) - { - q=sqrt(2.0*prop.he[j]/prop.gme); - prop.the[j]=(0.65*prop.dh*(q/prop.dl[j]-1.0)-2.0*prop.he[j])/q; - } - } - } - - else /* for ITWOM ,computes he for tx, rcvr, and the receiver approach angles for use in saalos */ - { - prop.he[0]=prop.hg[0]+(pfl[2]); - prop.he[1]=prop.hg[1]+(pfl[np+2]); - - rad=(prop.dist-500.0); - - if (prop.dist>550.0) - { - z1sq2(pfl,rad,prop.dist,rae1,rae2); - } - else - { - rae1=0.0; - rae2=0.0; - } - - prop.thera=atan(abs(rae2-rae1)/prop.dist); - - if (rae2=0) - { - propv.mdvar=mdvarx; - propv.lvar=mymax(propv.lvar,4); - } - - if (klimx>0) - { - propv.klim=klimx; - propv.lvar=5; - } - - lrprop2(0.0,prop,propa); -} - -double deg2rad(double d) -{ - return d*3.1415926535897/180.0; -} - -//*************************************************************************************** -//* Point-To-Point Mode Calculations -//*************************************************************************************** - - -void point_to_point_ITM(double elev[], double tht_m, double rht_m, double eps_dielect, double sgm_conductivity, double eno_ns_surfref, double frq_mhz, int radio_climate, int pol, double conf, double rel, double &dbloss, char *strmode, int &errnum) - -/****************************************************************************** - -Note that point_to_point has become point_to_point_ITM for use as the old ITM - - pol: - 0-Horizontal, 1-Vertical - - radio_climate: - 1-Equatorial, 2-Continental Subtropical, - 3-Maritime Tropical, 4-Desert, 5-Continental Temperate, - 6-Maritime Temperate, Over Land, 7-Maritime Temperate, - Over Sea - - conf, rel: .01 to .99 - - elev[]: [num points - 1], [delta dist(meters)], - [height(meters) point 1], ..., [height(meters) point n] - - errnum: 0- No Error. - 1- Warning: Some parameters are nearly out of range. - Results should be used with caution. - 2- Note: Default parameters have been substituted for - impossible ones. - 3- Warning: A combination of parameters is out of range. - Results are probably invalid. - Other- Warning: Some parameters are out of range. - Results are probably invalid. - -*****************************************************************************/ -{ - prop_type prop; - propv_type propv; - propa_type propa; - double zsys=0; - double zc, zr; - double eno, enso, q; - long ja, jb, i, np; - /* double dkm, xkm; */ - double fs; - - prop.hg[0]=tht_m; - prop.hg[1]=rht_m; - propv.klim=radio_climate; - prop.kwx=0; - propv.lvar=5; - prop.mdp=-1; - zc=qerfi(conf); - zr=qerfi(rel); - np=(long)elev[0]; - /* dkm=(elev[1]*elev[0])/1000.0; */ - /* xkm=elev[1]/1000.0; */ - eno=eno_ns_surfref; - enso=0.0; - q=enso; - - if (q<=0.0) - { - ja=(long)(3.0+0.1*elev[0]); /* added (long) to correct */ - jb=np-ja+6; - - for (i=ja-1; i0.0) - strcpy(strmode,"Double Horizon"); - - if (prop.dist<=propa.dlsa || prop.dist <= propa.dx) - strcat(strmode,", Diffraction Dominant"); - - else if (prop.dist>propa.dx) - strcat(strmode, ", Troposcatter Dominant"); - } - - dbloss=avar(zr,0.0,zc,prop,propv)+fs; - errnum=prop.kwx; -} - - - -void point_to_point(double elev[], double tht_m, double rht_m, double eps_dielect, double sgm_conductivity, double eno_ns_surfref, double frq_mhz, int radio_climate, int pol, double conf, double rel, double &dbloss, char *strmode, int &errnum) - -/****************************************************************************** - - Note that point_to_point_two has become point_to_point - for drop-in interface to splat.cpp. - The new variable inputs, - double enc_ncc_clcref, - double clutter_height, - double clutter_density, - double delta_h_diff, and - int mode_var) - have been given fixed values below. - - pol: - 0-Horizontal, 1-Vertical, 2-Circular - - radio_climate: - 1-Equatorial, 2-Continental Subtropical, - 3-Maritime Tropical, 4-Desert, 5-Continental Temperate, - 6-Maritime Temperate, Over Land, 7-Maritime Temperate, - Over Sea - - conf, rel: .01 to .99 - - elev[]: [num points - 1], [delta dist(meters)], - [height(meters) point 1], ..., [height(meters) point n] - - clutter_height 25.2 meters for compatibility with ITU-R P.1546-2. - - clutter_density 1.0 for compatibility with ITU-R P.1546-2. - - delta_h_diff optional delta h for beyond line of sight. 90 m. average. - setting to 0.0 will default to use of original internal - use of delta-h for beyond line-of-sight range. - - mode_var set to 12; or to 1 for FCC ILLR; see documentation - - enc_ncc_clcref clutter refractivity; 1000 N-units to match ITU-R P.1546-2 - - eno=eno_ns_surfref atmospheric refractivity at sea level; 301 N-units nominal - (ranges from 250 for dry, hot day to 450 on hot, humid day] - (stabilizes near 301 in cold, clear weather) - - errnum: 0- No Error. - 1- Warning: Some parameters are nearly out of range. - Results should be used with caution. - 2- Note: Default parameters have been substituted for - impossible ones. - 3- Warning: A combination of parameters is out of range. - Results are probably invalid. - Other- Warning: Some parameters are out of range. - Results are probably invalid. - -*****************************************************************************/ -{ - prop_type prop; - propv_type propv; - propa_type propa; - - double zsys=0; - double zc, zr; - double eno, enso, q; - long ja, jb, i, np; - /* double dkm, xkm; */ - double tpd, fs; - - prop.hg[0]=tht_m; - prop.hg[1]=rht_m; - propv.klim=radio_climate; - prop.kwx=0; - propv.lvar=5; - prop.mdp=-1; - prop.ptx=pol; - prop.thera=0.0; - prop.thenr=0.0; - zc=qerfi(conf); - zr=qerfi(rel); - np=(long)elev[0]; - /* dkm=(elev[1]*elev[0])/1000.0; */ - /* xkm=elev[1]/1000.0; */ - eno=eno_ns_surfref; - enso=0.0; - q=enso; - - /* PRESET VALUES for Basic Version w/o additional inputs active */ - - prop.encc = 1000.00; /* double enc_ncc_clcref preset */ - prop.cch = 22.5; /* double clutter_height preset to ILLR calibration.; - use 25.3 for ITU-P1546-2 calibration */ - prop.cd = 1.00; /* double clutter_density preset */ - int mode_var = 1; /* int mode_var set to 1 for FCC compatibility; - normally, SPLAT presets this to 12 */ - prop.dhd= 0.0; /* delta_h_diff preset */ - - if (q<=0.0) - { - ja=(long)(3.0+0.1*elev[0]); - jb=np-ja+6; - - for (i=ja-1; i0.0) - strcpy(strmode,"2_Hrzn"); - - if (prop.dist<=propa.dlsa || prop.dist<=propa.dx) - - if(int(prop.dl[1])==0.0) - strcat(strmode,"_Peak"); - - else - strcat(strmode,"_Diff"); - - else if (prop.dist>propa.dx) - strcat(strmode, "_Tropo"); - } - - dbloss=avar(zr,0.0,zc,prop,propv)+fs; - errnum=prop.kwx; -} - - -void point_to_pointMDH_two (double elev[], double tht_m, double rht_m, - double eps_dielect, double sgm_conductivity, double eno_ns_surfref, - double enc_ncc_clcref, double clutter_height, double clutter_density, - double delta_h_diff, double frq_mhz, int radio_climate, int pol, int mode_var, - double timepct, double locpct, double confpct, - double &dbloss, int &propmode, double &deltaH, int &errnum) - -/************************************************************************************************* - pol: 0-Horizontal, 1-Vertical - radio_climate: 1-Equatorial, 2-Continental Subtropical, 3-Maritime Tropical, - 4-Desert, 5-Continental Temperate, 6-Maritime Temperate, Over Land, - 7-Maritime Temperate, Over Sea - timepct, locpct, confpct: .01 to .99 - elev[]: [num points - 1], [delta dist(meters)], [height(meters) point 1], ..., [height(meters) point n] - propmode: Value Mode - -1 mode is undefined - 0 Line of Sight - 5 Single Horizon, Diffraction - 6 Single Horizon, Troposcatter - 9 Double Horizon, Diffraction - 10 Double Horizon, Troposcatter - errnum: 0- No Error. - 1- Warning: Some parameters are nearly out of range. - Results should be used with caution. - 2- Note: Default parameters have been substituted for impossible ones. - 3- Warning: A combination of parameters is out of range. - Results are probably invalid. - Other- Warning: Some parameters are out of range. - Results are probably invalid. -*************************************************************************************************/ -{ - - prop_type prop; - propv_type propv; - propa_type propa; - double zsys=0; - double ztime, zloc, zconf; - double eno, enso, q; - long ja, jb, i, np; - /* double dkm, xkm; */ - double fs; - - propmode = -1; // mode is undefined - prop.hg[0] = tht_m; - prop.hg[1] = rht_m; - propv.klim = radio_climate; - prop.encc=enc_ncc_clcref; - prop.cch=clutter_height; - prop.cd=clutter_density; - prop.dhd=delta_h_diff; - prop.kwx = 0; - propv.lvar = 5; - prop.mdp = -1; - prop.ptx=pol; - prop.thera=0.0; - prop.thenr=0.0; - ztime = qerfi(timepct); - zloc = qerfi(locpct); - zconf = qerfi(confpct); - np = (long)elev[0]; - /* dkm = (elev[1] * elev[0]) / 1000.0; */ - /* xkm = elev[1] / 1000.0; */ - eno = eno_ns_surfref; - enso = 0.0; - q = enso; - - /* PRESET VALUES for Basic Version w/o additional inputs active */ - - prop.encc = 1000.00; /* double enc_ncc_clcref */ - prop.cch = 22.5; /* double clutter_height */ - prop.cd = 1.00; /* double clutter_density */ - mode_var = 1; /* int mode_var set for FCC ILLR */ - - if(q<=0.0) - { - ja =(long) (3.0 + 0.1 * elev[0]); /* to match addition of (long) */ - jb = np - ja + 6; - for(i=ja-1;i0.0) - propmode = 8; // 2-Hrzn - if(prop.dist<=propa.dlsa || prop.dist<=propa.dx) - propmode += 1; // Diff - else if(prop.dist>propa.dx) - propmode += 2; // Tropo - } - dbloss = avar(ztime, zloc, zconf, prop, propv) + fs; //avar(time,location,confidence) - errnum = prop.kwx; -} - -void point_to_pointDH (double elev[], double tht_m, double rht_m, - double eps_dielect, double sgm_conductivity, double eno_ns_surfref, - double enc_ncc_clcref, double clutter_height, double clutter_density, - double delta_h_diff, double frq_mhz, int radio_climate, int pol, - double conf, double rel, double loc,double &dbloss, double &deltaH, - int &errnum) -/************************************************************************************************* - pol: 0-Horizontal, 1-Vertical - radio_climate: 1-Equatorial, 2-Continental Subtropical, 3-Maritime Tropical, - 4-Desert, 5-Continental Temperate, 6-Maritime Temperate, Over Land, - 7-Maritime Temperate, Over Sea - conf, rel: .01 to .99 - elev[]: [num points - 1], [delta dist(meters)], [height(meters) point 1], ..., [height(meters) point n] - errnum: 0- No Error. - 1- Warning: Some parameters are nearly out of range. - Results should be used with caution. - 2- Note: Default parameters have been substituted for impossible ones. - 3- Warning: A combination of parameters is out of range. - Results are probably invalid. - Other- Warning: Some parameters are out of range. - Results are probably invalid. -*************************************************************************************************/ -{ - - char strmode[100]; - prop_type prop; - propv_type propv; - propa_type propa; - double zsys=0; - double zc, zr; - double eno, enso, q; - long ja, jb, i, np; - /* double dkm, xkm; */ - double fs; - - prop.hg[0]=tht_m; - prop.hg[1]=rht_m; - propv.klim = radio_climate; - prop.encc=enc_ncc_clcref; - prop.cch=clutter_height; - prop.cd=clutter_density; - prop.dhd=delta_h_diff; - prop.kwx = 0; - propv.lvar = 5; - prop.mdp = -1; - prop.ptx=pol; - prop.thera=0.0; - prop.thenr=0.0; - zc = qerfi(conf); - zr = qerfi(rel); - np = (long)elev[0]; - /* dkm = (elev[1] * elev[0]) / 1000.0; */ - /* xkm = elev[1] / 1000.0; */ - eno = eno_ns_surfref; - enso = 0.0; - q = enso; - - /* PRESET VALUES for Basic Version w/o additional inputs active */ - - prop.encc = 1000.00; /* double enc_ncc_clcref */ - prop.cch = 22.5; /* double clutter_height */ - prop.cd = 1.00; /* double clutter_density */ - - if(q<=0.0) - { - ja = (long) (3.0 + 0.1 * elev[0]); /* to match KD2BD addition of (long) */ - jb = np - ja + 6; - for(i=ja-1; i0.0) - strcpy(strmode,"Double Horizon"); - if(prop.dist<=propa.dlsa || prop.dist<=propa.dx) - strcat(strmode,", Diffraction Dominant"); - else if(prop.dist>propa.dx) - strcat(strmode, ", Troposcatter Dominant"); - } - dbloss = avar(zr,0.0,zc,prop,propv)+fs; //avar(time,location,confidence) - errnum = prop.kwx; -} - - -//******************************************************** -//* Area Mode Calculations * -//******************************************************** - -void area(long ModVar, double deltaH, double tht_m, double rht_m, double dist_km, int TSiteCriteria, int RSiteCriteria, double eps_dielect, double sgm_conductivity, double eno_ns_surfref, double enc_ncc_clcref, double clutter_height, double clutter_density, double delta_h_diff, double frq_mhz, int radio_climate, int pol, int mode_var, -double pctTime, double pctLoc, double pctConf, double &dbloss, char *strmode, int &errnum) -{ - // pol: 0-Horizontal, 1-Vertical - // TSiteCriteria, RSiteCriteria: - // 0 - random, 1 - careful, 2 - very careful - - // radio_climate: 1-Equatorial, 2-Continental Subtropical, 3-Maritime Tropical, - // 4-Desert, 5-Continental Temperate, 6-Maritime Temperate, Over Land, - // 7-Maritime Temperate, Over Sea - // ModVar: 0 - Single: pctConf is "Time/Situation/Location", pctTime, pctLoc not used - // 1 - Individual: pctTime is "Situation/Location", pctConf is "Confidence", pctLoc not used - // 2 - Mobile: pctTime is "Time/Locations (Reliability)", pctConf is "Confidence", pctLoc not used - // 3 - Broadcast: pctTime is "Time", pctLoc is "Location", pctConf is "Confidence" - // pctTime, pctLoc, pctConf: .01 to .99 - // errnum: 0- No Error. - // 1- Warning: Some parameters are nearly out of range. - // Results should be used with caution. - // 2- Note: Default parameters have been substituted for impossible ones. - // 3- Warning: A combination of parameters is out of range. - // Results are probably invalid. - // Other- Warning: Some parameters are out of range. - // Results are probably invalid. - // NOTE: strmode is not used at this time. - - prop_type prop; - propv_type propv; - propa_type propa; - double zt, zl, zc, xlb; - double fs; - long ivar; - double eps, eno, sgm; - long ipol; - int kst[2]; - - kst[0]=(int)TSiteCriteria; - kst[1]=(int)RSiteCriteria; - zt=qerfi(pctTime/100.0); - zl=qerfi(pctLoc/100.0); - zc=qerfi(pctConf/100.0); - eps=eps_dielect; - sgm=sgm_conductivity; - eno=eno_ns_surfref; - prop.dh=deltaH; - prop.hg[0]=tht_m; - prop.hg[1]=rht_m; - propv.klim=(long)radio_climate; - prop.encc=enc_ncc_clcref; - prop.cch=clutter_height; - prop.cd=clutter_density; - prop.dhd=delta_h_diff; - prop.ens=eno; - prop.kwx=0; - ivar=(long)ModVar; - ipol=(long)pol; - qlrps(frq_mhz, 0.0, eno, ipol, eps, sgm, prop); - qlra(kst, propv.klim, ivar, prop, propv); - - if (propv.lvar<1) - propv.lvar=1; - - lrprop2(dist_km*1000.0, prop, propa); - fs=32.45+20.0*log10(frq_mhz)+20.0*log10(prop.dist/1000.0); - xlb=fs+avar(zt, zl, zc, prop, propv); - dbloss=xlb; - if (prop.kwx==0) - errnum=0; - else - errnum=prop.kwx; -} - - -double ITMAreadBLoss(long ModVar, double deltaH, double tht_m, double rht_m, - double dist_km, int TSiteCriteria, int RSiteCriteria, - double eps_dielect, double sgm_conductivity, double eno_ns_surfref, - double enc_ncc_clcref,double clutter_height,double clutter_density, - double delta_h_diff, double frq_mhz, int radio_climate, int pol, - int mode_var, double pctTime, double pctLoc, double pctConf) -{ - char strmode[200]; - int errnum; - double dbloss; - area(ModVar,deltaH,tht_m,rht_m,dist_km,TSiteCriteria,RSiteCriteria, - eps_dielect,sgm_conductivity,eno_ns_surfref, - enc_ncc_clcref,clutter_height,clutter_density, - delta_h_diff,frq_mhz,radio_climate,pol,mode_var,pctTime, - pctLoc,pctConf,dbloss,strmode,errnum); - return dbloss; -} - -double ITWOMVersion() -{ - return 3.0; -} diff --git a/main.cc b/main.cc new file mode 100644 index 0000000..0c5b8b0 --- /dev/null +++ b/main.cc @@ -0,0 +1,1691 @@ +double version = 2.5; +/****************************************************************************\ +* Signal Server: Server optimised SPLAT! by Alex Farrant * +****************************************************************************** +* SPLAT! Project started in 1997 by John A. Magliacane, KD2BD * +* * +****************************************************************************** +* Please consult the SPLAT! documentation for a complete list of * +* individuals who have contributed to this project. * +****************************************************************************** +* * +* 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 * +* version. * +* * +* This program is distributed in the hope that it will useful, but WITHOUT * +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * +* for more details. * +* * +\****************************************************************************/ + +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "inputs.hh" +#include "outputs.hh" +#include "models/itwom3.0.hh" +#include "models/los.hh" + +// 90m mode (default) +int MAXPAGES = 64; +int ARRAYSIZE = 76810; +int IPPD = 1200; + +char string[255], sdf_path[255], udt_file[255], opened = 0, gpsav = + 0, ss_name[16], dashes[80]; + +double earthradius, max_range = 0.0, forced_erp, dpp, ppd, + fzone_clearance = 0.6, forced_freq, clutter, lat, lon, txh, tercon, terdic, + north, east, south, west, dBm, loss, field_strength; + +int min_north = 90, max_north = -90, min_west = 360, max_west = -1, ippd, mpi, + max_elevation = -32768, min_elevation = 32768, bzerror, contour_threshold, + pred, pblue, pgreen, ter, multiplier = 256, debug = 0, loops = 64, jgets = + 0, MAXRAD, hottest = 10; + +unsigned char got_elevation_pattern, got_azimuth_pattern, metric = 0, dbm = 0; + +double *elev; + +struct site tx_site[2]; +struct dem *dem; +struct path path; +struct LR LR; +struct region region; + +double arccos(double x, double y) +{ + /* This function implements the arc cosine function, + returning a value between 0 and TWOPI. */ + + double result = 0.0; + + if (y > 0.0) + result = acos(x / y); + + if (y < 0.0) + result = PI + acos(x / 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); +} + +double LonDiff(double lon1, double 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. */ + + double diff; + + diff = lon1 - lon2; + + if (diff <= -180.0) + diff += 360.0; + + if (diff >= 180.0) + diff -= 360.0; + + return diff; +} + +char *dec2dms(double decimal) +{ + /* Converts decimal degrees to degrees, minutes, seconds, + (DMS) and returns the result as a character string. */ + + char sign; + int degrees, minutes, seconds; + double a, b, c, d; + + if (decimal < 0.0) { + decimal = -decimal; + sign = -1; + } + + else + sign = 1; + + a = floor(decimal); + b = 60.0 * (decimal - a); + c = floor(b); + d = 60.0 * (b - c); + + degrees = (int)a; + minutes = (int)c; + seconds = (int)d; + + if (seconds < 0) + seconds = 0; + + if (seconds > 59) + seconds = 59; + + string[0] = 0; + snprintf(string, 250, "%d%c %d\' %d\"", degrees * sign, 176, minutes, + seconds); + return (string); +} + +int PutMask(double lat, double lon, int value) +{ + /* Lines, text, markings, and coverage areas are stored in a + mask that is combined with topology data when topographic + maps are generated by ss. This function sets and resets + bits in the mask based on the latitude and longitude of the + area pointed to. */ + + int x = 0, y = 0, indx; + char found; + + for (indx = 0, found = 0; indx < MAXPAGES && found == 0;) { + x = (int)rint(ppd * (lat - dem[indx].min_north)); + y = mpi - (int)rint(ppd * (LonDiff(dem[indx].max_west, lon))); + + if (x >= 0 && x <= mpi && y >= 0 && y <= mpi) + found = 1; + else + indx++; + } + + if (found) { + dem[indx].mask[x][y] = value; + return ((int)dem[indx].mask[x][y]); + } + + else + return -1; +} + +int OrMask(double lat, double lon, int value) +{ + /* Lines, text, markings, and coverage areas are stored in a + mask that is combined with topology data when topographic + maps are generated by ss. This function sets bits in + the mask based on the latitude and longitude of the area + pointed to. */ + + int x = 0, y = 0, indx; + char found; + + for (indx = 0, found = 0; indx < MAXPAGES && found == 0;) { + x = (int)rint(ppd * (lat - dem[indx].min_north)); + y = mpi - (int)rint(ppd * (LonDiff(dem[indx].max_west, lon))); + + if (x >= 0 && x <= mpi && y >= 0 && y <= mpi) + found = 1; + else + indx++; + } + + if (found) { + dem[indx].mask[x][y] |= value; + return ((int)dem[indx].mask[x][y]); + } + + else + return -1; +} + +int GetMask(double lat, double lon) +{ + /* This function returns the mask bits based on the latitude + and longitude given. */ + + return (OrMask(lat, lon, 0)); +} + +int PutSignal(double lat, double lon, unsigned char signal) +{ + /* This function writes a signal level (0-255) + at the specified location for later recall. */ + char dotfile[255]; + snprintf(dotfile, 80, "%s.dot%c", tx_site[0].filename, 0); + snprintf(dotfile, 80, "%s.dot%c", tx_site[0].filename, 0); + + int x = 0, y = 0, indx; + char found; + + if (signal > hottest) // dBm, dBuV + hottest = signal; + + //lookup x/y for this co-ord + for (indx = 0, found = 0; indx < MAXPAGES && found == 0;) { + x = (int)rint(ppd * (lat - dem[indx].min_north)); + y = mpi - (int)rint(ppd * (LonDiff(dem[indx].max_west, lon))); + + if (x >= 0 && x <= mpi && y >= 0 && y <= mpi) + found = 1; + else + indx++; + } + + if (found) { + // Write values to file + dem[indx].signal[x][y] = signal; + + return (dem[indx].signal[x][y]); + } + + else + return 0; +} + +unsigned char GetSignal(double lat, double lon) +{ + /* This function reads the signal level (0-255) at the + specified location that was previously written by the + complimentary PutSignal() function. */ + + int x = 0, y = 0, indx; + char found; + + for (indx = 0, found = 0; indx < MAXPAGES && found == 0;) { + x = (int)rint(ppd * (lat - dem[indx].min_north)); + y = mpi - (int)rint(ppd * (LonDiff(dem[indx].max_west, lon))); + + if (x >= 0 && x <= mpi && y >= 0 && y <= mpi) + found = 1; + else + indx++; + } + + if (found) + return (dem[indx].signal[x][y]); + else + return 0; +} + +double GetElevation(struct site location) +{ + /* This function returns the elevation (in feet) of any location + represented by the digital elevation model data in memory. + Function returns -5000.0 for locations not found in memory. */ + + char found; + int x = 0, y = 0, indx; + double elevation; + + for (indx = 0, found = 0; indx < MAXPAGES && found == 0;) { + x = (int)rint(ppd * (location.lat - dem[indx].min_north)); + y = mpi - + (int)rint(ppd * + (LonDiff(dem[indx].max_west, location.lon))); + + if (x >= 0 && x <= mpi && y >= 0 && y <= mpi) + found = 1; + else + indx++; + } + + if (found) + elevation = 3.28084 * dem[indx].data[x][y]; + else + elevation = -5000.0; + + return elevation; +} + +int AddElevation(double lat, double lon, double height) +{ + /* This function adds a user-defined terrain feature + (in meters AGL) to the digital elevation model data + in memory. Does nothing and returns 0 for locations + not found in memory. */ + + char found; + int x = 0, y = 0, indx; + + for (indx = 0, found = 0; indx < MAXPAGES && found == 0;) { + x = (int)rint(ppd * (lat - dem[indx].min_north)); + y = mpi - (int)rint(ppd * (LonDiff(dem[indx].max_west, lon))); + + if (x >= 0 && x <= mpi && y >= 0 && y <= mpi) + found = 1; + else + indx++; + } + + if (found) + dem[indx].data[x][y] += (short)rint(height); + + return found; +} + +double Distance(struct site site1, struct site site2) +{ + /* This function returns the great circle distance + in miles between any two site locations. */ + + double lat1, lon1, lat2, lon2, distance; + + lat1 = site1.lat * DEG2RAD; + lon1 = site1.lon * DEG2RAD; + lat2 = site2.lat * DEG2RAD; + lon2 = site2.lon * DEG2RAD; + + distance = + 3959.0 * acos(sin(lat1) * sin(lat2) + + cos(lat1) * cos(lat2) * cos((lon1) - (lon2))); + + return distance; +} + +double Azimuth(struct site source, struct site destination) +{ + /* This function returns the azimuth (in degrees) to the + destination as seen from the location of the source. */ + + double dest_lat, dest_lon, src_lat, src_lon, + beta, azimuth, diff, num, den, fraction; + + dest_lat = destination.lat * DEG2RAD; + dest_lon = destination.lon * DEG2RAD; + + src_lat = source.lat * DEG2RAD; + src_lon = source.lon * DEG2RAD; + + /* Calculate Surface Distance */ + + beta = + acos(sin(src_lat) * sin(dest_lat) + + cos(src_lat) * cos(dest_lat) * cos(src_lon - dest_lon)); + + /* Calculate Azimuth */ + + num = sin(dest_lat) - (sin(src_lat) * cos(beta)); + den = cos(src_lat) * sin(beta); + fraction = num / den; + + /* Trap potential problems in acos() due to rounding */ + + if (fraction >= 1.0) + fraction = 1.0; + + if (fraction <= -1.0) + fraction = -1.0; + + /* Calculate azimuth */ + + azimuth = acos(fraction); + + /* Reference it to True North */ + + diff = dest_lon - src_lon; + + if (diff <= -PI) + diff += TWOPI; + + if (diff >= PI) + diff -= TWOPI; + + if (diff > 0.0) + azimuth = TWOPI - azimuth; + + return (azimuth / DEG2RAD); +} + +double ElevationAngle(struct site source, struct site destination) +{ + /* This function returns the angle of elevation (in degrees) + of the destination as seen from the source location. + A positive result represents an angle of elevation (uptilt), + while a negative result represents an angle of depression + (downtilt), as referenced to a normal to the center of + the earth. */ + + register double a, b, dx; + + a = GetElevation(destination) + destination.alt + earthradius; + b = GetElevation(source) + source.alt + earthradius; + + dx = 5280.0 * Distance(source, destination); + + /* Apply the Law of Cosines */ + + return ((180.0 * + (acos(((b * b) + (dx * dx) - (a * a)) / (2.0 * b * dx))) / + PI) - 90.0); +} + +void ReadPath(struct site source, struct site destination) +{ + /* This function generates a sequence of latitude and + longitude positions between source and destination + locations along a great circle path, and stores + elevation and distance information for points + along that path in the "path" structure. */ + + int c; + double azimuth, distance, lat1, lon1, beta, den, num, + lat2, lon2, total_distance, dx, dy, path_length, + miles_per_sample, samples_per_radian = 68755.0; + struct site tempsite; + + lat1 = source.lat * DEG2RAD; + lon1 = source.lon * DEG2RAD; + lat2 = destination.lat * DEG2RAD; + lon2 = destination.lon * DEG2RAD; + samples_per_radian = ppd * 57.295833; + azimuth = Azimuth(source, destination) * DEG2RAD; + + total_distance = Distance(source, destination); + + if (total_distance > (30.0 / ppd)) { + dx = samples_per_radian * acos(cos(lon1 - lon2)); + dy = samples_per_radian * acos(cos(lat1 - lat2)); + + path_length = sqrt((dx * dx) + (dy * dy)); + + miles_per_sample = total_distance / path_length; + } + + else { + c = 0; + dx = 0.0; + dy = 0.0; + path_length = 0.0; + miles_per_sample = 0.0; + total_distance = 0.0; + + lat1 = lat1 / DEG2RAD; + lon1 = lon1 / DEG2RAD; + + path.lat[c] = lat1; + path.lon[c] = lon1; + path.elevation[c] = GetElevation(source); + path.distance[c] = 0.0; + } + + for (distance = 0.0, c = 0; + (total_distance != 0.0 && distance <= total_distance + && c < ARRAYSIZE); c++, distance = miles_per_sample * (double)c) { + beta = distance / 3959.0; + lat2 = + asin(sin(lat1) * cos(beta) + + cos(azimuth) * sin(beta) * cos(lat1)); + num = cos(beta) - (sin(lat1) * sin(lat2)); + den = cos(lat1) * cos(lat2); + + if (azimuth == 0.0 && (beta > HALFPI - lat1)) + lon2 = lon1 + PI; + + else if (azimuth == HALFPI && (beta > HALFPI + lat1)) + lon2 = lon1 + PI; + + else if (fabs(num / den) > 1.0) + lon2 = lon1; + + else { + if ((PI - azimuth) >= 0.0) + lon2 = lon1 - arccos(num, den); + else + lon2 = lon1 + arccos(num, den); + } + + while (lon2 < 0.0) + lon2 += TWOPI; + + while (lon2 > TWOPI) + lon2 -= TWOPI; + + lat2 = lat2 / DEG2RAD; + lon2 = lon2 / DEG2RAD; + + path.lat[c] = lat2; + path.lon[c] = lon2; + tempsite.lat = lat2; + tempsite.lon = lon2; + path.elevation[c] = GetElevation(tempsite); + path.distance[c] = distance; + } + + /* Make sure exact destination point is recorded at path.length-1 */ + + if (c < ARRAYSIZE) { + 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.length = c; + else + path.length = ARRAYSIZE - 1; +} + +double ElevationAngle2(struct site source, struct site destination, double er) +{ + /* This function returns the angle of elevation (in degrees) + of the destination as seen from the source location, UNLESS + the path between the sites is obstructed, in which case, the + elevation angle to the first obstruction is returned instead. + "er" represents the earth radius. */ + + int x; + char block = 0; + double source_alt, destination_alt, cos_xmtr_angle, + cos_test_angle, test_alt, elevation, distance, + source_alt2, first_obstruction_angle = 0.0; + struct path temp; + + temp = path; + + ReadPath(source, destination); + + distance = 5280.0 * Distance(source, destination); + source_alt = er + source.alt + GetElevation(source); + destination_alt = er + destination.alt + GetElevation(destination); + source_alt2 = source_alt * source_alt; + + /* Calculate the cosine of the elevation angle of the + destination (receiver) as seen by the source (transmitter). */ + + cos_xmtr_angle = + ((source_alt2) + (distance * distance) - + (destination_alt * destination_alt)) / (2.0 * source_alt * + distance); + + /* Test all points in between source and destination locations to + see if the angle to a topographic feature generates a higher + elevation angle than that produced by the destination. Begin + at the source since we're interested in identifying the FIRST + obstruction along the path between source and destination. */ + + for (x = 2, block = 0; x < path.length && block == 0; x++) { + distance = 5280.0 * path.distance[x]; + + test_alt = + earthradius + (path.elevation[x] == + 0.0 ? path.elevation[x] : path.elevation[x] + + clutter); + + cos_test_angle = + ((source_alt2) + (distance * distance) - + (test_alt * test_alt)) / (2.0 * source_alt * distance); + + /* Compare these two angles to determine if + an obstruction exists. Since we're comparing + the cosines of these angles rather than + the angles themselves, the sense of the + following "if" statement is reversed from + what it would be if the angles themselves + were compared. */ + + if (cos_xmtr_angle >= cos_test_angle) { + block = 1; + first_obstruction_angle = + ((acos(cos_test_angle)) / DEG2RAD) - 90.0; + } + } + + if (block) + elevation = first_obstruction_angle; + + else + elevation = ((acos(cos_xmtr_angle)) / DEG2RAD) - 90.0; + + path = temp; + + return elevation; +} + +double ReadBearing(char *input) +{ + /* This function takes numeric input in the form of a character + string, and returns an equivalent bearing in degrees as a + decimal number (double). The input may either be expressed + in decimal format (40.139722) or degree, minute, second + format (40 08 23). This function also safely handles + extra spaces found either leading, trailing, or + embedded within the numbers expressed in the + input string. Decimal seconds are permitted. */ + + double seconds, bearing = 0.0; + char string[20]; + int a, b, length, degrees, minutes; + + /* Copy "input" to "string", and ignore any extra + spaces that might be present in the process. */ + + string[0] = 0; + length = strlen(input); + + for (a = 0, b = 0; a < length && a < 18; a++) { + if ((input[a] != 32 && input[a] != '\n') + || (input[a] == 32 && input[a + 1] != 32 + && input[a + 1] != '\n' && b != 0)) { + string[b] = input[a]; + b++; + } + } + + string[b] = 0; + + /* Count number of spaces in the clean string. */ + + length = strlen(string); + + for (a = 0, b = 0; a < length; a++) + if (string[a] == 32) + b++; + + if (b == 0) /* Decimal Format (40.139722) */ + sscanf(string, "%lf", &bearing); + + if (b == 2) { /* Degree, Minute, Second Format (40 08 23.xx) */ + sscanf(string, "%d %d %lf", °rees, &minutes, &seconds); + + bearing = fabs((double)degrees); + bearing += fabs(((double)minutes) / 60.0); + bearing += fabs(seconds / 3600.0); + + if ((degrees < 0) || (minutes < 0) || (seconds < 0.0)) + bearing = -bearing; + } + + /* Anything else returns a 0.0 */ + + if (bearing > 360.0 || bearing < -360.0) + bearing = 0.0; + + return bearing; +} + +void ObstructionAnalysis(struct site xmtr, struct site rcvr, double f, + FILE *outfile) +{ + /* Perform an obstruction analysis along the + path between receiver and transmitter. */ + + int x; + struct site site_x; + double h_r, h_t, h_x, h_r_orig, cos_tx_angle, cos_test_angle, + cos_tx_angle_f1, cos_tx_angle_fpt6, d_tx, d_x, + h_r_f1, h_r_fpt6, h_f, h_los, lambda = 0.0; + char string[255], string_fpt6[255], string_f1[255]; + + 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); + + if (clutter > 0.0) { + fprintf(outfile, "Terrain has been raised by"); + + if (metric) + fprintf(outfile, " %.2f meters", + METERS_PER_FOOT * clutter); + else + fprintf(outfile, " %.2f feet", clutter); + + fprintf(outfile, " to account for ground clutter.\n\n"); + } + + /* 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 ppa!'s 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 + clutter; + 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(outfile, + "Between %s and %s, obstructions were detected at:\n\n", + rcvr.name, xmtr.name); + + if (site_x.lat >= 0.0) { + if (metric) + fprintf(outfile, + " %8.4f N,%9.4f W, %5.2f kilometers, %6.2f meters AMSL\n", + site_x.lat, site_x.lon, + KM_PER_MILE * (d_x / 5280.0), + METERS_PER_FOOT * (h_x - + earthradius)); + else + fprintf(outfile, + " %8.4f N,%9.4f W, %5.2f miles, %6.2f feet AMSL\n", + site_x.lat, site_x.lon, + d_x / 5280.0, + h_x - earthradius); + } + + else { + if (metric) + fprintf(outfile, + " %8.4f S,%9.4f W, %5.2f kilometers, %6.2f meters AMSL\n", + -site_x.lat, site_x.lon, + KM_PER_MILE * (d_x / 5280.0), + METERS_PER_FOOT * (h_x - + earthradius)); + else + fprintf(outfile, + " %8.4f S,%9.4f W, %5.2f miles, %6.2f feet AMSL\n", + -site_x.lat, site_x.lon, + d_x / 5280.0, + h_x - earthradius); + } + } + + 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... */ + + 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 - + fzone_clearance * 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 - + fzone_clearance * sqrt(lambda * d_x * + (d_tx - d_x) / d_tx); + } + } + } + + if (h_r > h_r_orig) { + if (metric) + snprintf(string, 150, + "\nAntenna at %s must be raised to at least %.2f meters AGL\nto clear all obstructions detected.\n", + rcvr.name, + METERS_PER_FOOT * (h_r - GetElevation(rcvr) - + earthradius)); + else + snprintf(string, 150, + "\nAntenna at %s must be raised to at least %.2f feet AGL\nto clear all obstructions detected.\n", + rcvr.name, + h_r - GetElevation(rcvr) - earthradius); + } + + else + snprintf(string, 150, + "\nNo obstructions to LOS path due to terrain were detected\n"); + + if (f) { + if (h_r_fpt6 > h_r_orig) { + if (metric) + snprintf(string_fpt6, 150, + "\nAntenna at %s must be raised to at least %.2f meters AGL\nto clear %.0f%c of the first Fresnel zone.\n", + rcvr.name, + METERS_PER_FOOT * (h_r_fpt6 - + GetElevation(rcvr) - + earthradius), + fzone_clearance * 100.0, 37); + + else + snprintf(string_fpt6, 150, + "\nAntenna at %s must be raised to at least %.2f feet AGL\nto clear %.0f%c of the first Fresnel zone.\n", + rcvr.name, + h_r_fpt6 - GetElevation(rcvr) - + earthradius, fzone_clearance * 100.0, + 37); + } + + else + snprintf(string_fpt6, 150, + "\n%.0f%c of the first Fresnel zone is clear.\n", + fzone_clearance * 100.0, 37); + + if (h_r_f1 > h_r_orig) { + if (metric) + snprintf(string_f1, 150, + "\nAntenna at %s must be raised to at least %.2f meters AGL\nto clear the first Fresnel zone.\n", + rcvr.name, + METERS_PER_FOOT * (h_r_f1 - + GetElevation(rcvr) - + earthradius)); + + else + snprintf(string_f1, 150, + "\nAntenna at %s must be raised to at least %.2f feet AGL\nto clear the first Fresnel zone.\n", + rcvr.name, + h_r_f1 - GetElevation(rcvr) - + earthradius); + + } + + else + snprintf(string_f1, 150, + "\nThe first Fresnel zone is clear.\n"); + } + + fprintf(outfile, "%s", string); + + if (f) { + fprintf(outfile, "%s", string_f1); + fprintf(outfile, "%s", string_fpt6); + } + +} + +static void free_dem(void) +{ + int i; + int j; + + for (i = 0; i < MAXPAGES; i++) { + for (j = 0; j < IPPD; j++) { + delete [] dem[i].data[j]; + delete [] dem[i].mask[j]; + delete [] dem[i].signal[j]; + } + delete [] dem[i].data; + delete [] dem[i].mask; + delete [] dem[i].signal; + } + delete [] dem; +} + +static void free_path(void) +{ + delete [] path.lat; + delete [] path.lon; + delete [] path.elevation; + delete [] path.distance; +} + +static void alloc_dem(void) +{ + int i; + int j; + + dem = new struct dem[MAXPAGES]; + for (i = 0; i < MAXPAGES; i++) { + dem[i].data = new short *[IPPD]; + dem[i].mask = new unsigned char *[IPPD]; + dem[i].signal = new unsigned char *[IPPD]; + for (j = 0; j < IPPD; j++) { + dem[i].data[j] = new short[IPPD]; + dem[i].mask[j] = new unsigned char[IPPD]; + dem[i].signal[j] = new unsigned char[IPPD]; + } + } +} + +static void alloc_path(void) +{ + path.lat = new double[ARRAYSIZE]; + path.lon = new double[ARRAYSIZE]; + path.elevation = new double[ARRAYSIZE]; + path.distance = new double[ARRAYSIZE]; +} + +int main(int argc, char *argv[]) +{ + int x, y, z = 0, min_lat, min_lon, max_lat, max_lon, + rxlat, rxlon, txlat, txlon, west_min, west_max, + nortRxHin, nortRxHax, propmodel, winfiles, knifeedge = 0, ppa = + 0, normalise = 0, haf = 0, pmenv = 1; + + unsigned char LRmap = 0, txsites = 0, topomap = 0, geo = 0, kml = + 0, area_mode = 0, max_txsites, ngs = 0; + + char mapfile[255], udt_file[255], ano_filename[255]; + + double altitude = 0.0, altitudeLR = 0.0, tx_range = 0.0, + rx_range = 0.0, deg_range = 0.0, deg_limit = 0.0, deg_range_lon; + + if (strstr(argv[0], "signalserverHD")) { + MAXPAGES = 9; + ARRAYSIZE = 14844; + IPPD = 3600; + } + + strncpy(ss_name, "Signal Server\0", 14); + + if (argc == 1) { + + fprintf(stdout, "\n\t\t -- %s %.2f --\n", ss_name, version); + fprintf(stdout, + "\tSet for %d tiles at %d pixels/degree\n\n", + MAXPAGES, IPPD); + fprintf(stdout, " -d Directory containing .sdf tiles\n"); + fprintf(stdout, + " -lat Tx Latitude (decimal degrees) -70/+70\n"); + fprintf(stdout, + " -lon Tx Longitude (decimal degrees) -180/+180\n"); + fprintf(stdout, " -txh Tx Height (above ground)\n"); + fprintf(stdout, + " -rla (Optional) Rx Latitude for PPA (decimal degrees) -70/+70\n"); + fprintf(stdout, + " -rlo (Optional) Rx Longitude for PPA (decimal degrees) -180/+180\n"); + fprintf(stdout, + " -f Tx Frequency (MHz) 20MHz to 100GHz (LOS after 20GHz)\n"); + fprintf(stdout, + " -erp Tx Effective Radiated Power (Watts)\n"); + fprintf(stdout, + " -rxh Rx Height(s) (optional. Default=0.1)\n"); + fprintf(stdout, " -rt Rx Threshold (dB / dBm / dBuV/m)\n"); + fprintf(stdout, + " -hp Horizontal Polarisation (default=vertical)\n"); + fprintf(stdout, " -gc Ground clutter (feet/meters)\n"); + fprintf(stdout, " -udt User defined terrain filename\n"); + fprintf(stdout, + " -dbm Plot Rxd signal power instead of field strength\n"); + fprintf(stdout, " -m Metric units of measurement\n"); + fprintf(stdout, " -te Terrain code 1-6 (optional)\n"); + fprintf(stdout, + " -terdic Terrain dielectric value 2-80 (optional)\n"); + fprintf(stdout, + " -tercon Terrain conductivity 0.01-0.0001 (optional)\n"); + fprintf(stdout, " -cl Climate code 1-6 (optional)\n"); + fprintf(stdout, " -o Filename. Required. \n"); + fprintf(stdout, " -R Radius (miles/kilometers)\n"); + fprintf(stdout, + " -res Pixels per degree. 300/600/1200(default)/3600 (optional)\n"); + fprintf(stdout, " -t Terrain background\n"); + fprintf(stdout, + " -pm Prop model. 1: ITM, 2: LOS, 3: Hata, 4: ECC33,\n"); + fprintf(stdout, + " 5: SUI, 6: COST-Hata, 7: FSPL, 8: ITWOM, 9: Ericsson\n"); + fprintf(stdout, + " -pe Prop model mode: 1=Urban,2=Suburban,3=Rural\n"); + fprintf(stdout, + " -ked Knife edge diffraction (Default for ITM)\n"); + fprintf(stdout, " -ng Normalise Path Profile graph\n"); + fprintf(stdout, " -haf Halve 1 or 2 (optional)\n"); + + fflush(stdout); + + return 1; + } + + /* + * Now we know what mode we are running in, we can allocate various + * data structures. + */ + elev = new double[ARRAYSIZE + 10]; + alloc_dem(); + alloc_path(); + + y = argc - 1; + + kml = 0; + geo = 0; + dbm = 0; + gpsav = 0; + metric = 0; + string[0] = 0; + mapfile[0] = 0; + clutter = 0.0; + forced_erp = -1.0; + forced_freq = 0.0; + sdf_path[0] = 0; + udt_file[0] = 0; + path.length = 0; + max_txsites = 30; + fzone_clearance = 0.6; + contour_threshold = 0; + + ano_filename[0] = 0; + earthradius = EARTHRADIUS; + max_range = 1.0; + propmodel = 1; //ITM + winfiles = 0; + + lat = 0; + lon = 0; + txh = 0; + ngs = 1; // no terrain background + kml = 1; + LRmap = 1; + area_mode = 1; + ippd = IPPD; // default resolution + + sscanf("0.1", "%lf", &altitudeLR); + + // Defaults + LR.eps_dielect = 15.0; // Farmland + LR.sgm_conductivity = 0.005; // Farmland + LR.eno_ns_surfref = 301.0; + LR.frq_mhz = 19.0; // Deliberately too low + LR.radio_climate = 5; // continental + LR.pol = 1; // vert + LR.conf = 0.50; + LR.rel = 0.50; + LR.erp = 0.0; // will default to Path Loss + + tx_site[0].lat = 91.0; + tx_site[0].lon = 361.0; + tx_site[1].lat = 91.0; + tx_site[1].lon = 361.0; + + for (x = 0; x < MAXPAGES; x++) { + 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], "-res") == 0) { + z = x + 1; + + if (z <= y && argv[z][0] && argv[z][0] != '-') { + sscanf(argv[z], "%d", &ippd); + + switch (ippd) { + case 300: + MAXRAD = 300; + jgets = 3; + break; + + case 600: + MAXRAD = 150; + jgets = 1; + break; + + case 3600: + MAXRAD = 100; + ippd = 3600; + jgets = 0; + break; + + default: + MAXRAD = 100; + ippd = 1200; + jgets = 0; + break; + } + } + } + if (strcmp(argv[x], "-R") == 0) { + z = x + 1; + + if (z <= y && argv[z][0] && argv[z][0] != '-') { + sscanf(argv[z], "%lf", &max_range); + + } + } + + if (strcmp(argv[x], "-gc") == 0) { + z = x + 1; + + if (z <= y && argv[z][0] && argv[z][0] != '-') { + sscanf(argv[z], "%lf", &clutter); + + if (clutter < 0.0) + clutter = 0.0; + } + } + + if (strcmp(argv[x], "-o") == 0) { + z = x + 1; + + if (z <= y && argv[z][0] && argv[z][0] != '-') { + strncpy(mapfile, argv[z], 253); + strncpy(tx_site[0].name, "Tx", 2); + strncpy(tx_site[0].filename, argv[z], 253); + LoadPAT(argv[z]); + + } + } + + if (strcmp(argv[x], "-rt") == 0) { + z = x + 1; + + if (z <= y && argv[z][0]) /* A minus argument is legal here */ + sscanf(argv[z], "%d", &contour_threshold); + } + + if (strcmp(argv[x], "-m") == 0) { + metric = 1; + + } + + if (strcmp(argv[x], "-t") == 0) { + ngs = 0; // greyscale background + } + + if (strcmp(argv[x], "-dbm") == 0) + dbm = 1; + + if (strcmp(argv[x], "-d") == 0) { + z = x + 1; + + if (z <= y && argv[z][0] && argv[z][0] != '-') + strncpy(sdf_path, argv[z], 253); + } + + if (strcmp(argv[x], "-lat") == 0) { + z = x + 1; + + if (z <= y && argv[z][0]) { + tx_site[0].lat = ReadBearing(argv[z]); + } + } + if (strcmp(argv[x], "-lon") == 0) { + z = x + 1; + if (z <= y && argv[z][0]) { + tx_site[0].lon = ReadBearing(argv[z]); + tx_site[0].lon *= -1; + if (tx_site[0].lon < 0.0) + tx_site[0].lon += 360.0; + } + } + //Switch to Path Profile Mode if Rx co-ords specified + if (strcmp(argv[x], "-rla") == 0) { + z = x + 1; + + if (z <= y && argv[z][0]) { + ppa = 1; + tx_site[1].lat = ReadBearing(argv[z]); + + } + } + if (strcmp(argv[x], "-rlo") == 0) { + z = x + 1; + if (z <= y && argv[z][0]) { + tx_site[1].lon = ReadBearing(argv[z]); + tx_site[1].lon *= -1; + if (tx_site[1].lon < 0.0) + tx_site[1].lon += 360.0; + } + } + + if (strcmp(argv[x], "-txh") == 0) { + z = x + 1; + + if (z <= y && argv[z][0] && argv[z][0] != '-') { + sscanf(argv[z], "%f", &tx_site[0].alt); + + } + txsites = 1; + } + + if (strcmp(argv[x], "-rxh") == 0) { + z = x + 1; + + if (z <= y && argv[z][0] && argv[z][0] != '-') { + sscanf(argv[z], "%lf", &altitudeLR); + sscanf(argv[z], "%f", &tx_site[1].alt); + } + } + + if (strcmp(argv[x], "-f") == 0) { + z = x + 1; + + if (z <= y && argv[z][0] && argv[z][0] != '-') { + sscanf(argv[z], "%lf", &LR.frq_mhz); + } + } + + if (strcmp(argv[x], "-erp") == 0) { + z = x + 1; + + if (z <= y && argv[z][0] && argv[z][0] != '-') { + sscanf(argv[z], "%lf", &LR.erp); + } + } + + if (strcmp(argv[x], "-cl") == 0) { + z = x + 1; + + if (z <= y && argv[z][0] && argv[z][0] != '-') { + + sscanf(argv[z], "%d", &LR.radio_climate); + + } + } + if (strcmp(argv[x], "-te") == 0) { + z = x + 1; + + if (z <= y && argv[z][0] && argv[z][0] != '-') { + + sscanf(argv[z], "%d", &ter); + + switch (ter) { + case 1: // Water + terdic = 80; + tercon = 0.010; + break; + + case 2: // Marsh + terdic = 12; + tercon = 0.007; + break; + + case 3: // Farmland + terdic = 15; + tercon = 0.005; + break; + + case 4: //Mountain + terdic = 13; + tercon = 0.002; + break; + case 5: //Desert + terdic = 13; + tercon = 0.002; + break; + case 6: //Urban + terdic = 5; + tercon = 0.001; + break; + } + LR.eps_dielect = terdic; + LR.sgm_conductivity = tercon; + + } + } + + if (strcmp(argv[x], "-terdic") == 0) { + z = x + 1; + + if (z <= y && argv[z][0] && argv[z][0] != '-') { + + sscanf(argv[z], "%lf", &terdic); + + LR.eps_dielect = terdic; + + } + } + + if (strcmp(argv[x], "-tercon") == 0) { + z = x + 1; + + if (z <= y && argv[z][0] && argv[z][0] != '-') { + + sscanf(argv[z], "%lf", &tercon); + + LR.sgm_conductivity = tercon; + + } + } + + if (strcmp(argv[x], "-hp") == 0) { + // Horizontal polarisation (0) + LR.pol = 0; + } + + if (strcmp(argv[x], "-dbg") == 0) { + debug = 1; + } + + ppd = (double)ippd; /* pixels per degree (double) */ + dpp = 1.0 / ppd; /* degrees per pixel */ + mpi = ippd - 1; /* maximum pixel index per degree */ + + /*UDT*/ if (strcmp(argv[x], "-udt") == 0) { + z = x + 1; + + if (z <= y && argv[z][0]) { + strncpy(udt_file, argv[z], 253); + } + } + + /*Prop model */ + + if (strcmp(argv[x], "-pm") == 0) { + z = x + 1; + + if (z <= y && argv[z][0]) { + sscanf(argv[z], "%d", &propmodel); + } + } + // Prop model variant eg. urban/suburban + if (strcmp(argv[x], "-pe") == 0) { + z = x + 1; + + if (z <= y && argv[z][0]) { + sscanf(argv[z], "%d", &pmenv); + } + } + //Knife edge diffraction + if (strcmp(argv[x], "-ked") == 0) { + z = x + 1; + knifeedge = 1; + } + //Windows friendly SDF filenames + if (strcmp(argv[x], "-wf") == 0) { + z = x + 1; + winfiles = 1; + } + //Normalise Path Profile chart + if (strcmp(argv[x], "-ng") == 0) { + z = x + 1; + normalise = 1; + } + //Halve the problem + if (strcmp(argv[x], "-haf") == 0) { + z = x + 1; + if (z <= y && argv[z][0]) { + sscanf(argv[z], "%d", &haf); + } + } + + } + + /* ERROR DETECTION */ + if (tx_site[0].lat > 90 || tx_site[0].lat < -90) { + fprintf(stdout, + "ERROR: Either the lat was missing or out of range!"); + exit(0); + + } + if (tx_site[0].lon > 360 || tx_site[0].lon < 0) { + fprintf(stdout, + "ERROR: Either the lon was missing or out of range!"); + exit(0); + + } + if (LR.frq_mhz < 20 || LR.frq_mhz > 100000) { + fprintf(stdout, + "ERROR: Either the Frequency was missing or out of range!"); + exit(0); + } + if (LR.erp > 5000000) { + fprintf(stdout, "ERROR: Power was out of range!"); + exit(0); + + } + if (LR.eps_dielect > 80 || LR.eps_dielect < 0.1) { + fprintf(stdout, "ERROR: Ground Dielectric value out of range!"); + exit(0); + + } + if (LR.sgm_conductivity > 0.01 || LR.sgm_conductivity < 0.000001) { + fprintf(stdout, "ERROR: Ground conductivity out of range!"); + exit(0); + + } + + if (tx_site[0].alt < 0 || tx_site[0].alt > 60000) { + fprintf(stdout, + "ERROR: Tx altitude above ground was too high: %f", + tx_site[0].alt); + exit(0); + } + if (altitudeLR < 0 || altitudeLR > 60000) { + fprintf(stdout, + "ERROR: Rx altitude above ground was too high!"); + exit(0); + } + if (ippd < 300 || ippd > 3600) { + fprintf(stdout, "ERROR: resolution out of range!"); + exit(0); + } + if (contour_threshold < -200 || contour_threshold > 200) { + fprintf(stdout, + "ERROR: Receiver threshold out of range (-200 / +200)"); + exit(0); + } + if (propmodel > 2 && propmodel < 8 && LR.frq_mhz < 150) { + fprintf(stdout, + "ERROR: Frequency too low for Propagation model"); + exit(0); + } + + if (metric) { + altitudeLR /= METERS_PER_FOOT; /* 10ft * 0.3 = 3.3m */ + max_range /= KM_PER_MILE; /* 10 / 1.6 = 7.5 */ + altitude /= METERS_PER_FOOT; + tx_site[0].alt /= METERS_PER_FOOT; /* Feet to metres */ + tx_site[1].alt /= METERS_PER_FOOT; /* Feet to metres */ + clutter /= METERS_PER_FOOT; /* Feet to metres */ + } + + /* Ensure a trailing '/' is present in sdf_path */ + + if (sdf_path[0]) { + x = strlen(sdf_path); + + if (sdf_path[x - 1] != '/' && x != 0) { + sdf_path[x] = '/'; + sdf_path[x + 1] = 0; + } + } + + x = 0; + y = 0; + + min_lat = 70; + max_lat = -70; + + min_lon = (int)floor(tx_site[0].lon); + max_lon = (int)floor(tx_site[0].lon); + + txlat = (int)floor(tx_site[0].lat); + txlon = (int)floor(tx_site[0].lon); + + 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 (ppa == 1) { + rxlat = (int)floor(tx_site[1].lat); + rxlon = (int)floor(tx_site[1].lon); + + if (rxlat < min_lat) + min_lat = rxlat; + + 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; + } + + /* Load the required SDF files */ + + LoadTopoData(max_lon, min_lon, max_lat, min_lat, winfiles); + + if (area_mode || topomap) { + for (z = 0; z < txsites && z < max_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]))); + + 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 */ + + deg_range = (tx_range + rx_range) / 57.0; + + /* max_range regulates the 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 + ss A large number will increase the + width of the analysis and the size of + the map. */ + + if (max_range == 0.0) + max_range = tx_range + rx_range; + + deg_range = max_range / 57.0; + + // No more than 8 degs + deg_limit = 3.5; + + if (fabs(tx_site[z].lat) < 70.0) + deg_range_lon = + deg_range / cos(DEG2RAD * tx_site[z].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; + + nortRxHin = (int)floor(tx_site[z].lat - deg_range); + nortRxHax = (int)floor(tx_site[z].lat + deg_range); + + west_min = (int)floor(tx_site[z].lon - deg_range_lon); + + while (west_min < 0) + west_min += 360; + + while (west_min >= 360) + west_min -= 360; + + west_max = (int)floor(tx_site[z].lon + deg_range_lon); + + while (west_max < 0) + west_max += 360; + + while (west_max >= 360) + west_max -= 360; + + if (nortRxHin < min_lat) + min_lat = nortRxHin; + + if (nortRxHax > max_lat) + max_lat = nortRxHax; + + if (LonDiff(west_min, min_lon) < 0.0) + min_lon = west_min; + + if (LonDiff(west_max, max_lon) >= 0.0) + max_lon = west_max; + } + + /* Load any additional SDF files, if required */ + + LoadTopoData(max_lon, min_lon, max_lat, min_lat, winfiles); + } + // UDT clutter + LoadUDT(udt_file); + + if (ppa == 0) { + if (propmodel == 2) { + PlotLOSMap(tx_site[0], altitudeLR, ano_filename); + DoLOS(mapfile, geo, kml, ngs, tx_site, txsites); + } else { + // 90% of effort here + PlotPropagation(tx_site[0], altitudeLR, ano_filename, + propmodel, knifeedge, haf, pmenv); + + // Near field bugfix + PutSignal(tx_site[0].lat, tx_site[0].lon, hottest); + for (lat = tx_site[0].lat - 0.002; + lat <= tx_site[0].lat + 0.002; + lat = lat + 0.0005) { + for (lon = tx_site[0].lon - 0.002; + lon <= tx_site[0].lon + 0.002; + lon = lon + 0.0005) { + PutSignal(lat, lon, hottest); + } + } + if (LR.erp == 0.0) + DoPathLoss(mapfile, geo, kml, ngs, tx_site, + txsites); + else if (dbm) + DoRxdPwr(mapfile, geo, kml, ngs, tx_site, + txsites); + else + DoSigStr(mapfile, geo, kml, ngs, tx_site, + txsites); + } + fprintf(stdout, "|%.5f", north); + fprintf(stdout, "|%.5f", east); + fprintf(stdout, "|%.5f", south); + fprintf(stdout, "|%.5f|", west); + + } else { + strncpy(tx_site[0].name, "Tx", 3); + strncpy(tx_site[1].name, "Rx", 3); + PlotPath(tx_site[0], tx_site[1], 1); + PathReport(tx_site[0], tx_site[1], tx_site[0].filename, 0, + propmodel, pmenv); + SeriesData(tx_site[0], tx_site[1], tx_site[0].filename, 1, + normalise); + } + fflush(stdout); + + delete [] elev; + free_dem(); + free_path(); + + return 0; +} diff --git a/main.cpp b/main.cpp deleted file mode 100755 index 541bd19..0000000 --- a/main.cpp +++ /dev/null @@ -1,5553 +0,0 @@ -double version=2.44; -/****************************************************************************\ -* Signal Server: Server optimised SPLAT! by Alex Farrant * -****************************************************************************** -* SPLAT! Project started in 1997 by John A. Magliacane, KD2BD * -* * -****************************************************************************** -* Please consult the SPLAT! documentation for a complete list of * -* individuals who have contributed to this project. * -****************************************************************************** -* * -* 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 * -* version. * -* * -* This program is distributed in the hope that it will useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * -* for more details. * -* * -\****************************************************************************/ - -/* -REQUIRES GCC >= 4.7 -90m mode -g++ -Wall -Ofast -s -lm itwom3.0.cpp models.cpp main.cpp -o signalserver -30m HD mode -g++ -Wall -Ofast -s -lm itwom3.0.cpp models.cpp main.cpp -DHD -o signalserverHD -*/ - -#include -#include -#include -#include -#include -#include - - -#ifdef HD -// HD mode, 30m res -#define MAXPAGES 9 -#define ARRAYSIZE 14844 -#define IPPD 3600 -#else -// 90m mode (default) -#define MAXPAGES 64 -#define ARRAYSIZE 76810 -#define IPPD 1200 -#endif - -#define GAMMA 2.5 - -#ifndef PI -#define PI 3.141592653589793 -#endif - -#ifndef TWOPI -#define TWOPI 6.283185307179586 -#endif - -#ifndef HALFPI -#define HALFPI 1.570796326794896 -#endif - -#define DEG2RAD 1.74532925199e-02 -#define EARTHRADIUS 20902230.97 -#define METERS_PER_MILE 1609.344 -#define METERS_PER_FOOT 0.3048 -#define KM_PER_MILE 1.609344 -#define FOUR_THIRDS 1.3333333333333 - -char string[255], sdf_path[255], udt_file[255], opened=0, gpsav=0, ss_name[16], dashes[80]; - -double earthradius, max_range=0.0, forced_erp, dpp, ppd, - fzone_clearance=0.6, forced_freq, clutter, lat,lon,txh,tercon,terdic, - north,east,south,west,dBm,loss,field_strength; - -int min_north=90, max_north=-90, min_west=360, max_west=-1, ippd, mpi, - max_elevation=-32768, min_elevation=32768, bzerror, contour_threshold, - pred,pblue,pgreen,ter,multiplier=256,debug=0,loops=64,jgets=0, MAXRAD, hottest=10; - -unsigned char got_elevation_pattern, got_azimuth_pattern, metric=0, dbm=0; - - - -struct site { double lat; - double lon; - float alt; - char name[50]; - char filename[255]; - } site; - -struct path { double lat[ARRAYSIZE]; - double lon[ARRAYSIZE]; - double elevation[ARRAYSIZE]; - double 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[IPPD][IPPD]; - unsigned char mask[IPPD][IPPD]; - unsigned char signal[IPPD][IPPD]; - } dem[MAXPAGES]; - -struct LR { double eps_dielect; - double sgm_conductivity; - double eno_ns_surfref; - double frq_mhz; - double conf; - double rel; - double erp; - int radio_climate; - int pol; - float antenna_pattern[361][1001]; - } LR; - -struct region { unsigned char color[128][3]; - int level[128]; - int levels; - } region; - -double elev[ARRAYSIZE+10]; - -struct site tx_site[2]; - -//ITWOM -void point_to_point(double elev[], double tht_m, double rht_m, - double eps_dielect, double sgm_conductivity, double eno_ns_surfref, - double frq_mhz, int radio_climate, int pol, double conf, - double rel, double &dbloss, char *strmode, int &errnum); -//ITM -void point_to_point_ITM(double elev[], double tht_m, double rht_m, - double eps_dielect, double sgm_conductivity, double eno_ns_surfref, - double frq_mhz, int radio_climate, int pol, double conf, - double rel, double &dbloss, char *strmode, int &errnum); - -double HATApathLoss(float f,float TxH, float RxH, float d, int mode); - -double COST231pathLoss(float f,float TxH, float RxH, float d, int mode); - -double FSPLpathLoss(float f, float d); - -double SUIpathLoss(float f,float txH, float rxH, float d, int terrain); - -double ECC33pathLoss(float f,float TxH, float RxH, float d, int mode); - -double EricssonpathLoss(float f,float TxH, float RxH, float d, int mode); - -double ked(double freq, double elev[], double rxh, double dkm); - -double arccos(double x, double y) -{ - /* This function implements the arc cosine function, - returning a value between 0 and TWOPI. */ - - double result=0.0; - - if (y>0.0) - result=acos(x/y); - - if (y<0.0) - result=PI+acos(x/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); -} - -double LonDiff(double lon1, double 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. */ - - double diff; - - diff=lon1-lon2; - - if (diff<=-180.0) - diff+=360.0; - - if (diff>=180.0) - diff-=360.0; - - return diff; -} - -char *dec2dms(double decimal) -{ - /* Converts decimal degrees to degrees, minutes, seconds, - (DMS) and returns the result as a character string. */ - - char sign; - int degrees, minutes, seconds; - double a, b, c, d; - - if (decimal<0.0) - { - decimal=-decimal; - sign=-1; - } - - else - sign=1; - - a=floor(decimal); - b=60.0*(decimal-a); - c=floor(b); - d=60.0*(b-c); - - degrees=(int)a; - minutes=(int)c; - seconds=(int)d; - - if (seconds<0) - seconds=0; - - if (seconds>59) - seconds=59; - - string[0]=0; - snprintf(string,250,"%d%c %d\' %d\"", degrees*sign, 176, minutes, seconds); - return (string); -} - -int PutMask(double lat, double lon, int value) -{ - /* Lines, text, markings, and coverage areas are stored in a - mask that is combined with topology data when topographic - maps are generated by ss. This function sets and resets - bits in the mask based on the latitude and longitude of the - area pointed to. */ - - int x, y, indx; - char found; - - for (indx=0, found=0; indx=0 && x<=mpi && y>=0 && y<=mpi) - found=1; - else - indx++; - } - - if (found) - { - dem[indx].mask[x][y]=value; - return ((int)dem[indx].mask[x][y]); - } - - else - return -1; -} - -int OrMask(double lat, double lon, int value) -{ - /* Lines, text, markings, and coverage areas are stored in a - mask that is combined with topology data when topographic - maps are generated by ss. This function sets bits in - the mask based on the latitude and longitude of the area - pointed to. */ - - int x, y, indx; - char found; - - for (indx=0, found=0; indx=0 && x<=mpi && y>=0 && y<=mpi) - found=1; - else - indx++; - } - - if (found) - { - dem[indx].mask[x][y]|=value; - return ((int)dem[indx].mask[x][y]); - } - - else - return -1; -} - -int GetMask(double lat, double lon) -{ - /* This function returns the mask bits based on the latitude - and longitude given. */ - - return (OrMask(lat,lon,0)); -} - -int PutSignal(double lat, double lon, unsigned char signal) -{ - /* This function writes a signal level (0-255) - at the specified location for later recall. */ - char dotfile[255]; - snprintf(dotfile,80,"%s.dot%c",tx_site[0].filename,0); - snprintf(dotfile,80,"%s.dot%c",tx_site[0].filename,0); - - int x, y, indx; - char found; - - if(signal > hottest) // dBm, dBuV - hottest=signal; - - //lookup x/y for this co-ord - for (indx=0, found=0; indx=0 && x<=mpi && y>=0 && y<=mpi) - found=1; - else - indx++; - } - - - if (found) - { - // Write values to file - dem[indx].signal[x][y]=signal; - - return (dem[indx].signal[x][y]); - } - - else - return 0; -} - -unsigned char GetSignal(double lat, double lon) -{ - /* This function reads the signal level (0-255) at the - specified location that was previously written by the - complimentary PutSignal() function. */ - - int x, y, indx; - char found; - - for (indx=0, found=0; indx=0 && x<=mpi && y>=0 && y<=mpi) - found=1; - else - indx++; - } - - if (found) - return (dem[indx].signal[x][y]); - else - return 0; -} - -double GetElevation(struct site location) -{ - /* This function returns the elevation (in feet) of any location - represented by the digital elevation model data in memory. - Function returns -5000.0 for locations not found in memory. */ - - char found; - int x, y, indx; - double elevation; - - for (indx=0, found=0; indx=0 && x<=mpi && y>=0 && y<=mpi) - found=1; - else - indx++; - } - - if (found) - elevation=3.28084*dem[indx].data[x][y]; - else - elevation=-5000.0; - - return elevation; -} - -int AddElevation(double lat, double lon, double height) -{ - /* This function adds a user-defined terrain feature - (in meters AGL) to the digital elevation model data - in memory. Does nothing and returns 0 for locations - not found in memory. */ - - char found; - int x, y, indx; - - - for (indx=0, found=0; indx=0 && x<=mpi && y>=0 && y<=mpi) - found=1; - else - indx++; - } - - if (found) - dem[indx].data[x][y]+=(short)rint(height); - - return found; -} - -double Distance(struct site site1, struct site site2) -{ - /* This function returns the great circle distance - in miles between any two site locations. */ - - double lat1, lon1, lat2, lon2, distance; - - lat1=site1.lat*DEG2RAD; - lon1=site1.lon*DEG2RAD; - lat2=site2.lat*DEG2RAD; - lon2=site2.lon*DEG2RAD; - - distance=3959.0*acos(sin(lat1)*sin(lat2)+cos(lat1)*cos(lat2)*cos((lon1)-(lon2))); - - return distance; -} - -double Azimuth(struct site source, struct site destination) -{ - /* This function returns the azimuth (in degrees) to the - destination as seen from the location of the source. */ - - double dest_lat, dest_lon, src_lat, src_lon, - beta, azimuth, diff, num, den, fraction; - - dest_lat=destination.lat*DEG2RAD; - dest_lon=destination.lon*DEG2RAD; - - src_lat=source.lat*DEG2RAD; - src_lon=source.lon*DEG2RAD; - - /* Calculate Surface Distance */ - - beta=acos(sin(src_lat)*sin(dest_lat)+cos(src_lat)*cos(dest_lat)*cos(src_lon-dest_lon)); - - /* Calculate Azimuth */ - - num=sin(dest_lat)-(sin(src_lat)*cos(beta)); - den=cos(src_lat)*sin(beta); - fraction=num/den; - - /* Trap potential problems in acos() due to rounding */ - - if (fraction>=1.0) - fraction=1.0; - - if (fraction<=-1.0) - fraction=-1.0; - - /* Calculate azimuth */ - - azimuth=acos(fraction); - - /* Reference it to True North */ - - diff=dest_lon-src_lon; - - if (diff<=-PI) - diff+=TWOPI; - - if (diff>=PI) - diff-=TWOPI; - - if (diff>0.0) - azimuth=TWOPI-azimuth; - - return (azimuth/DEG2RAD); -} - -double ElevationAngle(struct site source, struct site destination) -{ - /* This function returns the angle of elevation (in degrees) - of the destination as seen from the source location. - A positive result represents an angle of elevation (uptilt), - while a negative result represents an angle of depression - (downtilt), as referenced to a normal to the center of - the earth. */ - - register double a, b, dx; - - a=GetElevation(destination)+destination.alt+earthradius; - b=GetElevation(source)+source.alt+earthradius; - - dx=5280.0*Distance(source,destination); - - /* Apply the Law of Cosines */ - - return ((180.0*(acos(((b*b)+(dx*dx)-(a*a))/(2.0*b*dx)))/PI)-90.0); -} - -void ReadPath(struct site source, struct site destination) -{ - /* This function generates a sequence of latitude and - longitude positions between source and destination - locations along a great circle path, and stores - elevation and distance information for points - along that path in the "path" structure. */ - - int c; - double azimuth, distance, lat1, lon1, beta, den, num, - lat2, lon2, total_distance, dx, dy, path_length, - miles_per_sample, samples_per_radian=68755.0; - struct site tempsite; - - lat1=source.lat*DEG2RAD; - lon1=source.lon*DEG2RAD; - lat2=destination.lat*DEG2RAD; - lon2=destination.lon*DEG2RAD; - samples_per_radian=ppd*57.295833; - azimuth=Azimuth(source,destination)*DEG2RAD; - - total_distance=Distance(source,destination); - - if (total_distance>(30.0/ppd)) - { - dx=samples_per_radian*acos(cos(lon1-lon2)); - dy=samples_per_radian*acos(cos(lat1-lat2)); - - path_length=sqrt((dx*dx)+(dy*dy)); - - miles_per_sample=total_distance/path_length; - } - - else - { - c=0; - dx=0.0; - dy=0.0; - path_length=0.0; - miles_per_sample=0.0; - total_distance=0.0; - - lat1=lat1/DEG2RAD; - lon1=lon1/DEG2RAD; - - path.lat[c]=lat1; - path.lon[c]=lon1; - path.elevation[c]=GetElevation(source); - path.distance[c]=0.0; - } - - for (distance=0.0, c=0; (total_distance!=0.0 && distance<=total_distance && cHALFPI-lat1)) - lon2=lon1+PI; - - else if (azimuth==HALFPI && (beta>HALFPI+lat1)) - lon2=lon1+PI; - - else if (fabs(num/den)>1.0) - lon2=lon1; - - else - { - if ((PI-azimuth)>=0.0) - lon2=lon1-arccos(num,den); - else - lon2=lon1+arccos(num,den); - } - - while (lon2<0.0) - lon2+=TWOPI; - - while (lon2>TWOPI) - lon2-=TWOPI; - - lat2=lat2/DEG2RAD; - lon2=lon2/DEG2RAD; - - path.lat[c]=lat2; - path.lon[c]=lon2; - tempsite.lat=lat2; - tempsite.lon=lon2; - path.elevation[c]=GetElevation(tempsite); - path.distance[c]=distance; - } - - /* Make sure exact destination point is recorded at path.length-1 */ - - if (c=cos_test_angle) - { - block=1; - first_obstruction_angle=((acos(cos_test_angle))/DEG2RAD)-90.0; - } - } - - if (block) - elevation=first_obstruction_angle; - - else - elevation=((acos(cos_xmtr_angle))/DEG2RAD)-90.0; - - path=temp; - - return elevation; -} - -double ReadBearing(char *input) -{ - /* This function takes numeric input in the form of a character - string, and returns an equivalent bearing in degrees as a - decimal number (double). The input may either be expressed - in decimal format (40.139722) or degree, minute, second - format (40 08 23). This function also safely handles - extra spaces found either leading, trailing, or - embedded within the numbers expressed in the - input string. Decimal seconds are permitted. */ - - double seconds, bearing=0.0; - char string[20]; - int a, b, length, degrees, minutes; - - /* Copy "input" to "string", and ignore any extra - spaces that might be present in the process. */ - - string[0]=0; - length=strlen(input); - - for (a=0, b=0; a360.0 || bearing<-360.0) - bearing=0.0; - - return bearing; -} - -void LoadPAT(char *filename) -{ - /* This function reads and processes antenna pattern (.az - and .el) files that correspond in name to previously - loaded ss .lrp files. */ - - int a, b, w, x, y, z, last_index, next_index, span; - char string[255], azfile[255], elfile[255], *pointer=NULL; - float az, xx, elevation, amplitude, rotation, valid1, valid2, - delta, azimuth[361], azimuth_pattern[361], el_pattern[10001], - elevation_pattern[361][1001], slant_angle[361], tilt, - mechanical_tilt=0.0, tilt_azimuth, tilt_increment, sum; - FILE *fd=NULL; - unsigned char read_count[10001]; - - for (x=0; filename[x]!='.' && filename[x]!=0 && x<250; x++) - { - azfile[x]=filename[x]; - elfile[x]=filename[x]; - } - - azfile[x]='.'; - azfile[x+1]='a'; - azfile[x+2]='z'; - azfile[x+3]=0; - - elfile[x]='.'; - elfile[x+1]='e'; - elfile[x+2]='l'; - elfile[x+3]=0; - - rotation=0.0; - - got_azimuth_pattern=0; - got_elevation_pattern=0; - - /* Load .az antenna pattern file */ - - fd=fopen(azfile,"r"); - - if (fd!=NULL) - { - /* Clear azimuth pattern array */ - - for (x=0; x<=360; x++) - { - azimuth[x]=0.0; - read_count[x]=0; - } - - - /* Read azimuth pattern rotation - in degrees measured clockwise - from true North. */ - - if(fgets(string,254,fd) == NULL){ - //fprintf(stdout,"Azimuth read error\n"); - //exit(0); - } - pointer=strchr(string,';'); - - if (pointer!=NULL) - *pointer=0; - - sscanf(string,"%f",&rotation); - - - /* Read azimuth (degrees) and corresponding - normalized field radiation pattern amplitude - (0.0 to 1.0) until EOF is reached. */ - - if(fgets(string,254,fd) == NULL){ - //fprintf(stdout,"Azimuth read error\n"); - //exit(0); - } - pointer=strchr(string,';'); - - if (pointer!=NULL) - *pointer=0; - - sscanf(string,"%f %f",&az, &litude); - - do - { - x=(int)rintf(az); - - if (x>=0 && x<=360 && fd!=NULL) - { - azimuth[x]+=amplitude; - read_count[x]++; - } - - if(fgets(string,254,fd)== NULL){ - //fprintf(stdout,"Azimuth read error\n"); - // exit(0); - } - pointer=strchr(string,';'); - - if (pointer!=NULL) - *pointer=0; - - sscanf(string,"%f %f",&az, &litude); - - } while (feof(fd)==0); - - fclose(fd); - - - /* Handle 0=360 degree ambiguity */ - - if ((read_count[0]==0) && (read_count[360]!=0)) - { - read_count[0]=read_count[360]; - azimuth[0]=azimuth[360]; - } - - if ((read_count[0]!=0) && (read_count[360]==0)) - { - read_count[360]=read_count[0]; - azimuth[360]=azimuth[0]; - } - - /* Average pattern values in case more than - one was read for each degree of azimuth. */ - - for (x=0; x<=360; x++) - { - if (read_count[x]>1) - azimuth[x]/=(float)read_count[x]; - } - - /* Interpolate missing azimuths - to completely fill the array */ - - last_index=-1; - next_index=-1; - - for (x=0; x<=360; x++) - { - if (read_count[x]!=0) - { - if (last_index==-1) - last_index=x; - else - next_index=x; - } - - if (last_index!=-1 && next_index!=-1) - { - valid1=azimuth[last_index]; - valid2=azimuth[next_index]; - - span=next_index-last_index; - delta=(valid2-valid1)/(float)span; - - for (y=last_index+1; y=360) - y-=360; - - azimuth_pattern[y]=azimuth[x]; - } - - azimuth_pattern[360]=azimuth_pattern[0]; - - got_azimuth_pattern=255; - } - - /* Read and process .el file */ - - fd=fopen(elfile,"r"); - - if (fd!=NULL) - { - for (x=0; x<=10000; x++) - { - el_pattern[x]=0.0; - read_count[x]=0; - } - - /* Read mechanical tilt (degrees) and - tilt azimuth in degrees measured - clockwise from true North. */ - - if(fgets(string,254,fd)==NULL){ - //fprintf(stdout,"Tilt read error\n"); - //exit(0); - } - pointer=strchr(string,';'); - - if (pointer!=NULL) - *pointer=0; - - sscanf(string,"%f %f",&mechanical_tilt, &tilt_azimuth); - - /* Read elevation (degrees) and corresponding - normalized field radiation pattern amplitude - (0.0 to 1.0) until EOF is reached. */ - - if(fgets(string,254,fd)==NULL){ - //fprintf(stdout,"Ant elevation read error\n"); - //exit(0); - } - pointer=strchr(string,';'); - - if (pointer!=NULL) - *pointer=0; - - sscanf(string,"%f %f", &elevation, &litude); - - while (feof(fd)==0) - { - /* Read in normalized radiated field values - for every 0.01 degrees of elevation between - -10.0 and +90.0 degrees */ - - x=(int)rintf(100.0*(elevation+10.0)); - - if (x>=0 && x<=10000) - { - el_pattern[x]+=amplitude; - read_count[x]++; - } - - if(fgets(string,254,fd)!=NULL){ - pointer=strchr(string,';'); - } - if (pointer!=NULL) - *pointer=0; - - sscanf(string,"%f %f", &elevation, &litude); - } - - fclose(fd); - - /* Average the field values in case more than - one was read for each 0.01 degrees of elevation. */ - - for (x=0; x<=10000; x++) - { - if (read_count[x]>1) - el_pattern[x]/=(float)read_count[x]; - } - - /* Interpolate between missing elevations (if - any) to completely fill the array and provide - radiated field values for every 0.01 degrees of - elevation. */ - - last_index=-1; - next_index=-1; - - for (x=0; x<=10000; x++) - { - if (read_count[x]!=0) - { - if (last_index==-1) - last_index=x; - else - next_index=x; - } - - if (last_index!=-1 && next_index!=-1) - { - valid1=el_pattern[last_index]; - valid2=el_pattern[next_index]; - - span=next_index-last_index; - delta=(valid2-valid1)/(float)span; - - for (y=last_index+1; y=360) - y-=360; - - while (y<0) - y+=360; - - if (x<=180) - slant_angle[y]=-(tilt_increment*(90.0-xx)); - - if (x>180) - slant_angle[y]=-(tilt_increment*(xx-270.0)); - } - } - - slant_angle[360]=slant_angle[0]; /* 360 degree wrap-around */ - - for (w=0; w<=360; w++) - { - tilt=slant_angle[w]; - - /** Convert tilt angle to - an array index offset **/ - - y=(int)rintf(100.0*tilt); - - /* Copy shifted el_pattern[10001] field - values into elevation_pattern[361][1001] - at the corresponding azimuth, downsampling - (averaging) along the way in chunks of 10. */ - - for (x=y, z=0; z<=1000; x+=10, z++) - { - for (sum=0.0, a=0; a<10; a++) - { - b=a+x; - - if (b>=0 && b<=10000) - sum+=el_pattern[b]; - if (b<0) - sum+=el_pattern[0]; - if (b>10000) - sum+=el_pattern[10000]; - } - - elevation_pattern[w][z]=sum/10.0; - } - } - - got_elevation_pattern=255; - } - - for (x=0; x<=360; x++) - { - for (y=0; y<=1000; y++) - { - if (got_elevation_pattern) - elevation=elevation_pattern[x][y]; - else - elevation=1.0; - - if (got_azimuth_pattern) - az=azimuth_pattern[x]; - else - az=1.0; - - LR.antenna_pattern[x][y]=az*elevation; - } - } -} - -int LoadSDF_SDF(char *name, int winfiles) -{ - /* This function reads uncompressed ss Data Files (.sdf) - containing digital elevation model data into memory. - Elevation data, maximum and minimum elevations, and - quadrangle limits are stored in the first available - dem[] structure. */ - - int x, y, data, indx, minlat, minlon, maxlat, maxlon,j; - char found, free_page=0, line[20], jline[20], sdf_file[255], - path_plus_name[255], *junk=NULL; - - - FILE *fd; - - for (x=0; name[x]!='.' && name[x]!=0 && x<250; x++) - sdf_file[x]=name[x]; - - sdf_file[x]=0; - - /* Parse filename for minimum latitude and longitude values */ - if(winfiles==1){ - sscanf(sdf_file,"%d=%d=%d=%d",&minlat,&maxlat,&minlon,&maxlon); - }else{ - sscanf(sdf_file,"%d:%d:%d:%d",&minlat,&maxlat,&minlon,&maxlon); - } - - sdf_file[x]='.'; - sdf_file[x+1]='s'; - sdf_file[x+2]='d'; - sdf_file[x+3]='f'; - sdf_file[x+4]=0; - - /* Is it already in memory? */ - - - for (indx=0, found=0; indx=0 && indxdem[indx].max_el) - dem[indx].max_el=data; - - if (datamax_elevation) - max_elevation=dem[indx].max_el; - - if (max_north==-90) - max_north=dem[indx].max_north; - - else if (dem[indx].max_north>max_north) - max_north=dem[indx].max_north; - - 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; - } - } - - - return 1; - } - - else - return -1; - } - - else - return 0; -} -char LoadSDF(char *name, int winfiles) -{ - /* This function loads the requested SDF file from the filesystem. - It first tries to invoke the LoadSDF_SDF() function to load an - uncompressed SDF file (since uncompressed files load slightly - faster). If that attempt fails, then it tries to load a - compressed SDF file by invoking the LoadSDF_BZ() function. - If that fails, then we can assume that no elevation data - exists for the region requested, and that the region - requested must be entirely over water. */ - - int x, y, indx, minlat, minlon, maxlat, maxlon; - char found, free_page=0; - int return_value=-1; - - return_value=LoadSDF_SDF(name, winfiles); - - - /* If neither format can be found, then assume the area is water. */ - - if (return_value==0 || return_value==-1) - { - - - - if(winfiles==1){ - sscanf(name,"%d=%d=%d=%d",&minlat,&maxlat,&minlon,&maxlon); - }else{ - sscanf(name,"%d:%d:%d:%d",&minlat,&maxlat,&minlon,&maxlon); - } - /* Is it already in memory? */ - - for (indx=0, found=0; indx=0 && indx0) - dem[indx].min_el=0; - } - - if (dem[indx].min_elmax_elevation) - max_elevation=dem[indx].max_el; - - if (max_north==-90) - max_north=dem[indx].max_north; - - else if (dem[indx].max_north>max_north) - max_north=dem[indx].max_north; - - 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; - } - } - - return_value=1; - } - } - - return return_value; -} - -void PlotLOSPath(struct site source, struct site destination, char mask_value, FILE *fd) -{ - /* This function analyzes the path between the source and - destination locations. It determines which points along - the path have line-of-sight visibility to the source. - Points along with path having line-of-sight visibility - to the source at an AGL altitude equal to that of the - destination location are stored by setting bit 1 in the - mask[][] array, which are displayed in green when PPM - maps are later generated by ss. */ - - char block; - int x, y; - register double cos_xmtr_angle, cos_test_angle, test_alt; - double distance, rx_alt, tx_alt; - - ReadPath(source,destination); - - for (y=0; y=0 && block==0; x--) - { - distance=5280.0*(path.distance[y]-path.distance[x]); - test_alt=earthradius+(path.elevation[x]==0.0?path.elevation[x]:path.elevation[x]+clutter); - - 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 - 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 - be if the actual angles were compared. */ - - if (cos_xmtr_angle>=cos_test_angle) - block=1; - } - - if (block==0) - OrMask(path.lat[y],path.lon[y],mask_value); - } - } -} - -void PlotPropPath(struct site source, struct site destination, unsigned char mask_value, FILE *fd, int propmodel, int knifeedge, int pmenv) -{ - - int x, y, ifs, ofs, errnum; - char block=0, strmode[100]; - double loss, azimuth, pattern=0.0, - xmtr_alt, dest_alt, xmtr_alt2, dest_alt2, - cos_rcvr_angle, cos_test_angle=0.0, test_alt, - elevation=0.0, distance=0.0, radius=0.0, four_thirds_earth, - field_strength=0.0, rxp, dBm, txelev, dkm, diffloss; - struct site temp; - - radius = Distance(source,destination); - - ReadPath(source,destination); - - four_thirds_earth=FOUR_THIRDS*EARTHRADIUS; - - - for (x=1; x1.0) - cos_rcvr_angle=1.0; - - if (cos_rcvr_angle<-1.0) - cos_rcvr_angle=-1.0; - - if (got_elevation_pattern || fd!=NULL) - { - /* Determine the elevation angle to the first obstruction - along the path IF elevation pattern data is available - or an output (.ano) file has been designated. */ - - for (x=2, block=0; (x1.0) - cos_test_angle=1.0; - - if (cos_test_angle<-1.0) - cos_test_angle=-1.0; - - /* Compare these two angles to determine if - an obstruction exists. Since we're comparing - the cosines of these angles rather than - the angles themselves, the sense of the - following "if" statement is reversed from - what it would be if the angles themselves - were compared. */ - - if (cos_rcvr_angle>=cos_test_angle) - block=1; - } - - if (block) - elevation=((acos(cos_test_angle))/DEG2RAD)-90.0; - else - elevation=((acos(cos_rcvr_angle))/DEG2RAD)-90.0; - } - - /* Determine attenuation for each point along the - path using a prop model starting at y=2 (number_of_points = 1), the - shortest distance terrain can play a role in - path loss. */ - - elev[0]=y-1; /* (number of points - 1) */ - - /* Distance between elevation samples */ - - elev[1]=METERS_PER_MILE*(path.distance[y]-path.distance[y-1]); - - if(path.elevation[y] < 1){ - path.elevation[y]=1; - } - - dkm=(elev[1]*elev[0])/1000; // km - - switch (propmodel) - { - case 1: - // Longley Rice ITM - point_to_point_ITM(elev,source.alt*METERS_PER_FOOT, - destination.alt*METERS_PER_FOOT, LR.eps_dielect, - LR.sgm_conductivity, LR.eno_ns_surfref, LR.frq_mhz, - LR.radio_climate, LR.pol, LR.conf, LR.rel, loss, - strmode, errnum); - break; - case 3: - //HATA 1, 2 & 3 - loss=HATApathLoss(LR.frq_mhz,txelev,path.elevation[y]+(destination.alt*METERS_PER_FOOT),dkm, pmenv); - break; - case 4: - // COST231-HATA - loss=ECC33pathLoss(LR.frq_mhz,txelev,path.elevation[y]+(destination.alt*METERS_PER_FOOT),dkm, pmenv); - break; - case 5: - // SUI - loss=SUIpathLoss(LR.frq_mhz,txelev,path.elevation[y]+(destination.alt*METERS_PER_FOOT),dkm, pmenv); - break; - case 6: - loss=COST231pathLoss(LR.frq_mhz,txelev,path.elevation[y]+(destination.alt*METERS_PER_FOOT),dkm, pmenv); - break; - case 7: - // ITU-R P.525 Free space path loss - loss=FSPLpathLoss(LR.frq_mhz,dkm); - break; - case 8: - // ITWOM 3.0 - point_to_point(elev,source.alt*METERS_PER_FOOT, - destination.alt*METERS_PER_FOOT, LR.eps_dielect, - LR.sgm_conductivity, LR.eno_ns_surfref, LR.frq_mhz, - LR.radio_climate, LR.pol, LR.conf, LR.rel, loss, - strmode, errnum); - break; - case 9: - // Ericsson - loss=EricssonpathLoss(LR.frq_mhz,txelev,path.elevation[y]+(destination.alt*METERS_PER_FOOT),dkm, pmenv); - break; - - - default: - point_to_point_ITM(elev,source.alt*METERS_PER_FOOT, - destination.alt*METERS_PER_FOOT, LR.eps_dielect, - LR.sgm_conductivity, LR.eno_ns_surfref, LR.frq_mhz, - LR.radio_climate, LR.pol, LR.conf, LR.rel, loss, - strmode, errnum); - - } - - - if(knifeedge==1){ - diffloss = ked(LR.frq_mhz,elev,destination.alt*METERS_PER_FOOT,dkm); - loss+=(diffloss); // ;) - } - - //Key stage. Link dB for p2p is returned as 'loss'. - - temp.lat=path.lat[y]; - temp.lon=path.lon[y]; - - azimuth=(Azimuth(source,temp)); - - if (fd!=NULL) - fprintf(fd,"%.7f, %.7f, %.3f, %.3f, ",path.lat[y], path.lon[y], azimuth, elevation); - - /* If ERP==0, write path loss to alphanumeric - output file. Otherwise, write field strength - or received power level (below), as appropriate. */ - - if (fd!=NULL && LR.erp==0.0) - fprintf(fd,"%.2f",loss); - - /* Integrate the antenna's radiation - pattern into the overall path loss. */ - - x=(int)rint(10.0*(10.0-elevation)); - - if (x>=0 && x<=1000) - { - azimuth=rint(azimuth); - - pattern=(double)LR.antenna_pattern[(int)azimuth][x]; - - if (pattern!=0.0) - { - pattern=20.0*log10(pattern); - loss-=pattern; - } - } - - if (LR.erp!=0.0) - { - if (dbm) - { - /* dBm is based on EIRP (ERP + 2.14) */ - - rxp=LR.erp/(pow(10.0,(loss-2.14)/10.0)); - - dBm=10.0*(log10(rxp*1000.0)); - - if (fd!=NULL) - fprintf(fd,"%.3f",dBm); - - /* Scale roughly between 0 and 255 */ - - ifs=200+(int)rint(dBm); - - if (ifs<0) - ifs=0; - - if (ifs>255) - ifs=255; - - ofs=GetSignal(path.lat[y],path.lon[y]); - - if (ofs>ifs) - ifs=ofs; - - - PutSignal(path.lat[y],path.lon[y],(unsigned char)ifs); - - } - - else - { - field_strength=(139.4+(20.0*log10(LR.frq_mhz))-loss)+(10.0*log10(LR.erp/1000.0)); - - ifs=100+(int)rint(field_strength); - - if (ifs<0) - ifs=0; - - if (ifs>255) - ifs=255; - - ofs=GetSignal(path.lat[y],path.lon[y]); - - if (ofs>ifs) - ifs=ofs; - - PutSignal(path.lat[y],path.lon[y],(unsigned char)ifs); - - if (fd!=NULL) - fprintf(fd,"%.3f",field_strength); - } - } - - else - { - if (loss>255) - ifs=255; - else - ifs=(int)rint(loss); - - ofs=GetSignal(path.lat[y],path.lon[y]); - - if (ofs=360.0) - lon-=360.0; - - edge.lat=max_north; - edge.lon=lon; - edge.alt=altitude; - - PlotLOSPath(source,edge,mask_value,fd); - } - - - - z=(int)(th*(double)(max_north-min_north)); - - for (lat=maxnorth, x=0, y=0; lat>=(double)min_north; y++, lat=maxnorth-(dpp*(double)y)) - { - edge.lat=lat; - edge.lon=min_west; - edge.alt=altitude; - - PlotLOSPath(source,edge,mask_value,fd); - - } - - - - z=(int)(th*ReduceAngle(max_west-min_west)); - - for (lon=minwest, x=0, y=0; (LonDiff(lon,(double)max_west)<=0.0); y++, lon=minwest+(dpp*(double)y)) - { - if (lon>=360.0) - lon-=360.0; - - edge.lat=min_north; - edge.lon=lon; - edge.alt=altitude; - - PlotLOSPath(source,edge,mask_value,fd); - - } - - - z=(int)(th*(double)(max_north-min_north)); - - for (lat=(double)min_north, x=0, y=0; lat<(double)max_north; y++, lat=(double)min_north+(dpp*(double)y)) - { - edge.lat=lat; - edge.lon=max_west; - edge.alt=altitude; - - PlotLOSPath(source,edge,mask_value,fd); - - - } - - - switch (mask_value) - { - case 1: - mask_value=8; - break; - - case 8: - mask_value=16; - break; - - case 16: - mask_value=32; - } -} - -void PlotPropagation(struct site source, double altitude, char *plo_filename, int propmodel, int knifeedge, int haf, int pmenv) -{ - int y, z, count; - struct site edge; - double lat, lon, minwest, maxnorth, th; - unsigned char x; - static unsigned char mask_value=1; - FILE *fd=NULL; - - minwest=dpp+(double)min_west; - maxnorth=(double)max_north-dpp; - - count=0; - - - if (LR.erp==0.0 && debug) - fprintf(stdout,"path loss"); - else - { - if(debug){ - if (dbm) - fprintf(stdout,"signal power level"); - else - fprintf(stdout,"field strength"); - } - } - if (debug){ - fprintf(stdout," contours of \"%s\"\nout to a radius of %.2f %s with Rx antenna(s) at %.2f %s AGL\n",source.name,metric?max_range*KM_PER_MILE:max_range,metric?"kilometers":"miles",metric?altitude*METERS_PER_FOOT:altitude,metric?"meters":"feet"); - } - - if (clutter>0.0 && debug) - fprintf(stdout,"\nand %.2f %s of ground clutter",metric?clutter*METERS_PER_FOOT:clutter,metric?"meters":"feet"); - - if(debug){ - fprintf(stdout,"...\n\n 0%c to 25%c ",37,37); - fflush(stdout); - } - - if (plo_filename[0]!=0) - fd=fopen(plo_filename,"wb"); - - if (fd!=NULL) - { - fprintf(fd,"%d, %d\t; max_west, min_west\n%d, %d\t; max_north, min_north\n",max_west, min_west, max_north, min_north); - } - - th=ppd/loops; - - // Four sections start here - - //S1 - if(haf==0 || haf==1){ - z=(int)(th*ReduceAngle(max_west-min_west)); - - for (lon=minwest, x=0, y=0; (LonDiff(lon,(double)max_west)<=0.0); y++, lon=minwest+(dpp*(double)y)) - { - if (lon>=360.0) - lon-=360.0; - - edge.lat=max_north; - edge.lon=lon; - edge.alt=altitude; - - PlotPropPath(source,edge,mask_value,fd,propmodel,knifeedge,pmenv); - count++; - - if (count==z) - { - count=0; - - if (x==3) - x=0; - else - x++; - } - } - - } - - //S2 - if(haf==0 || haf==1){ - count=0; - if(debug){ - fprintf(stdout,"\n25%c to 50%c ",37,37); - fflush(stdout); - } - - z=(int)(th*(double)(max_north-min_north)); - - for (lat=maxnorth, x=0, y=0; lat>=(double)min_north; y++, lat=maxnorth-(dpp*(double)y)) - { - edge.lat=lat; - edge.lon=min_west; - edge.alt=altitude; - - PlotPropPath(source,edge,mask_value,fd,propmodel,knifeedge,pmenv); - count++; - - if (count==z) - { - count=0; - - if (x==3) - x=0; - else - x++; - } - } - - } - //S3 - if(haf==0 || haf==2){ - count=0; - if(debug){ - fprintf(stdout,"\n50%c to 75%c ",37,37); - fflush(stdout); - } - - z=(int)(th*ReduceAngle(max_west-min_west)); - - for (lon=minwest, x=0, y=0; (LonDiff(lon,(double)max_west)<=0.0); y++, lon=minwest+(dpp*(double)y)) - { - if (lon>=360.0) - lon-=360.0; - - edge.lat=min_north; - edge.lon=lon; - edge.alt=altitude; - - PlotPropPath(source,edge,mask_value,fd,propmodel,knifeedge,pmenv); - count++; - if (count==z) - { - count=0; - - if (x==3) - x=0; - else - x++; - } - - } - - } - //S4 - if(haf==0 || haf==2){ - count=0; - if(debug){ - fprintf(stdout,"\n75%c to 100%c ",37,37); - fflush(stdout); - } - z=(int)(th*(double)(max_north-min_north)); - - for (lat=(double)min_north, x=0, y=0; lat<(double)max_north; y++, lat=(double)min_north+(dpp*(double)y)) - { - edge.lat=lat; - edge.lon=max_west; - edge.alt=altitude; - - PlotPropPath(source,edge,mask_value,fd,propmodel,knifeedge,pmenv); - count++; - - if (count==z) - { - - count=0; - - if (x==3) - x=0; - else - x++; - } - } - - } //S4 - - if (fd!=NULL) - fclose(fd); - - if (mask_value<30) - mask_value++; -} - -void LoadSignalColors(struct site xmtr) -{ - int x, y, ok, val[4]; - char filename[255], string[80], *pointer=NULL, *s=NULL; - FILE *fd=NULL; - - for (x=0; xmtr.filename[x]!='.' && xmtr.filename[x]!=0 && x<250; x++) - filename[x]=xmtr.filename[x]; - - filename[x]='.'; - filename[x+1]='s'; - filename[x+2]='c'; - filename[x+3]='f'; - filename[x+4]=0; - - /* Default values */ - - region.level[0]=128; - region.color[0][0]=255; - region.color[0][1]=0; - region.color[0][2]=0; - - region.level[1]=118; - region.color[1][0]=255; - region.color[1][1]=165; - region.color[1][2]=0; - - region.level[2]=108; - region.color[2][0]=255; - region.color[2][1]=206; - region.color[2][2]=0; - - region.level[3]=98; - region.color[3][0]=255; - region.color[3][1]=255; - region.color[3][2]=0; - - region.level[4]=88; - region.color[4][0]=184; - region.color[4][1]=255; - region.color[4][2]=0; - - region.level[5]=78; - region.color[5][0]=0; - region.color[5][1]=255; - region.color[5][2]=0; - - region.level[6]=68; - region.color[6][0]=0; - region.color[6][1]=208; - region.color[6][2]=0; - - region.level[7]=58; - region.color[7][0]=0; - region.color[7][1]=196; - region.color[7][2]=196; - - region.level[8]=48; - region.color[8][0]=0; - region.color[8][1]=148; - region.color[8][2]=255; - - region.level[9]=38; - region.color[9][0]=80; - region.color[9][1]=80; - region.color[9][2]=255; - - region.level[10]=28; - region.color[10][0]=0; - region.color[10][1]=38; - region.color[10][2]=255; - - region.level[11]=18; - region.color[11][0]=142; - region.color[11][1]=63; - region.color[11][2]=255; - - region.level[12]=8; - region.color[12][0]=140; - region.color[12][1]=0; - region.color[12][2]=128; - - region.levels=13; - - fd=fopen(filename,"r"); - - if (fd==NULL) - fd=fopen(filename,"r"); - - if (fd==NULL) - { - fd=fopen(filename,"w"); - - - for (x=0; x255) - val[y]=255; - - if (val[y]<0) - val[y]=0; - } - - region.level[x]=val[0]; - region.color[x][0]=val[1]; - region.color[x][1]=val[2]; - region.color[x][2]=val[3]; - x++; - } - - s=fgets(string,80,fd); - } - - fclose(fd); - region.levels=x; - } -} - -void LoadLossColors(struct site xmtr) -{ - int x, y, ok, val[4]; - char filename[255], string[80], *pointer=NULL, *s=NULL; - FILE *fd=NULL; - - for (x=0; xmtr.filename[x]!='.' && xmtr.filename[x]!=0 && x<250; x++) - filename[x]=xmtr.filename[x]; - - filename[x]='.'; - filename[x+1]='l'; - filename[x+2]='c'; - filename[x+3]='f'; - filename[x+4]=0; - - /* Default values */ - - region.level[0]=80; - region.color[0][0]=255; - region.color[0][1]=0; - region.color[0][2]=0; - - region.level[1]=90; - region.color[1][0]=255; - region.color[1][1]=128; - region.color[1][2]=0; - - region.level[2]=100; - region.color[2][0]=255; - region.color[2][1]=165; - region.color[2][2]=0; - - region.level[3]=110; - region.color[3][0]=255; - region.color[3][1]=206; - region.color[3][2]=0; - - region.level[4]=120; - region.color[4][0]=255; - region.color[4][1]=255; - region.color[4][2]=0; - - region.level[5]=130; - region.color[5][0]=184; - region.color[5][1]=255; - region.color[5][2]=0; - - region.level[6]=140; - region.color[6][0]=0; - region.color[6][1]=255; - region.color[6][2]=0; - - region.level[7]=150; - region.color[7][0]=0; - region.color[7][1]=208; - region.color[7][2]=0; - - region.level[8]=160; - region.color[8][0]=0; - region.color[8][1]=196; - region.color[8][2]=196; - - region.level[9]=170; - region.color[9][0]=0; - region.color[9][1]=148; - region.color[9][2]=255; - - region.level[10]=180; - region.color[10][0]=80; - region.color[10][1]=80; - region.color[10][2]=255; - - region.level[11]=190; - region.color[11][0]=0; - region.color[11][1]=38; - region.color[11][2]=255; - - region.level[12]=200; - region.color[12][0]=142; - region.color[12][1]=63; - region.color[12][2]=255; - - region.level[13]=210; - region.color[13][0]=196; - region.color[13][1]=54; - region.color[13][2]=255; - - region.level[14]=220; - region.color[14][0]=255; - region.color[14][1]=0; - region.color[14][2]=255; - - region.level[15]=230; - region.color[15][0]=255; - region.color[15][1]=194; - region.color[15][2]=204; - - region.levels=16; - - fd=fopen(filename,"r"); - - if (fd==NULL) - fd=fopen(filename,"r"); - - if (fd==NULL) - { - fd=fopen(filename,"w"); - - - - for (x=0; x255) - val[y]=255; - - if (val[y]<0) - val[y]=0; - } - - region.level[x]=val[0]; - region.color[x][0]=val[1]; - region.color[x][1]=val[2]; - region.color[x][2]=val[3]; - x++; - } - - s=fgets(string,80,fd); - } - - fclose(fd); - region.levels=x; - } -} - -void LoadDBMColors(struct site xmtr) -{ - int x, y, ok, val[4]; - char filename[255], string[80], *pointer=NULL, *s=NULL; - FILE *fd=NULL; - - for (x=0; xmtr.filename[x]!='.' && xmtr.filename[x]!=0 && x<250; x++) - filename[x]=xmtr.filename[x]; - - filename[x]='.'; - filename[x+1]='d'; - filename[x+2]='c'; - filename[x+3]='f'; - filename[x+4]=0; - - /* Default values */ - - region.level[0]=0; - region.color[0][0]=255; - region.color[0][1]=0; - region.color[0][2]=0; - - region.level[1]=-10; - region.color[1][0]=255; - region.color[1][1]=128; - region.color[1][2]=0; - - region.level[2]=-20; - region.color[2][0]=255; - region.color[2][1]=165; - region.color[2][2]=0; - - region.level[3]=-30; - region.color[3][0]=255; - region.color[3][1]=206; - region.color[3][2]=0; - - region.level[4]=-40; - region.color[4][0]=255; - region.color[4][1]=255; - region.color[4][2]=0; - - region.level[5]=-50; - region.color[5][0]=184; - region.color[5][1]=255; - region.color[5][2]=0; - - region.level[6]=-60; - region.color[6][0]=0; - region.color[6][1]=255; - region.color[6][2]=0; - - region.level[7]=-70; - region.color[7][0]=0; - region.color[7][1]=208; - region.color[7][2]=0; - - region.level[8]=-80; - region.color[8][0]=0; - region.color[8][1]=196; - region.color[8][2]=196; - - region.level[9]=-90; - region.color[9][0]=0; - region.color[9][1]=148; - region.color[9][2]=255; - - region.level[10]=-100; - region.color[10][0]=80; - region.color[10][1]=80; - region.color[10][2]=255; - - region.level[11]=-110; - region.color[11][0]=0; - region.color[11][1]=38; - region.color[11][2]=255; - - region.level[12]=-120; - region.color[12][0]=142; - region.color[12][1]=63; - region.color[12][2]=255; - - region.level[13]=-130; - region.color[13][0]=196; - region.color[13][1]=54; - region.color[13][2]=255; - - region.level[14]=-140; - region.color[14][0]=255; - region.color[14][1]=0; - region.color[14][2]=255; - - region.level[15]=-150; - region.color[15][0]=255; - region.color[15][1]=194; - region.color[15][2]=204; - - region.levels=16; - - fd=fopen(filename,"r"); - - if (fd==NULL) - fd=fopen(filename,"r"); - - if (fd==NULL) - { - fd=fopen(filename,"w"); - - - for (x=0; x+40) - val[0]=+40; - - region.level[x]=val[0]; - - for (y=1; y<4; y++) - { - if (val[y]>255) - val[y]=255; - - if (val[y]<0) - val[y]=0; - } - - region.color[x][0]=val[1]; - region.color[x][1]=val[2]; - region.color[x][2]=val[3]; - x++; - } - - s=fgets(string,80,fd); - } - - fclose(fd); - region.levels=x; - } -} - - -void DoPathLoss(char *filename, unsigned char geo, unsigned char kml, unsigned char ngs, struct site *xmtr, unsigned char txsites) -{ - /* This function generates a topographic map in Portable Pix Map - (PPM) format based on the content of flags held in the mask[][] - array (only). The image created is rotated counter-clockwise - 90 degrees from its representation in dem[][] so that north - points up and east points right in the image generated. */ - - char mapfile[255]; - unsigned width, height, red, green, blue, terrain=0; - unsigned char found, mask, cityorcounty; - int indx, x, y, z, x0, y0, loss,match; - double lat, lon, conversion, one_over_gamma,minwest; - FILE *fd; - - one_over_gamma=1.0/GAMMA; - conversion=255.0/pow((double)(max_elevation-min_elevation),one_over_gamma); - - width=(unsigned)(ippd*ReduceAngle(max_west-min_west)); - height=(unsigned)(ippd*ReduceAngle(max_north-min_north)); - - LoadLossColors(xmtr[0]); - - if (filename[0]==0) - { - strncpy(filename, xmtr[0].filename,254); - filename[strlen(filename)-4]=0; /* Remove .qth */ - } - - y=strlen(filename); - - if (y>4) - { - if (filename[y-1]=='m' && filename[y-2]=='p' && filename[y-3]=='p' && filename[y-4]=='.') - y-=4; - } - - for (x=0; x360.0) - minwest-=360.0; - - north=(double)max_north-dpp; - - if (kml || geo) - south=(double)min_north; /* No bottom legend */ - else - south=(double)min_north-(30.0/ppd); /* 30 pixels for bottom legend */ - - east=(minwest<180.0?-minwest:360.0-min_west); - west=(double)(max_west<180?-max_west:360-max_west); - - - fd=fopen(mapfile,"wb"); - - fprintf(fd,"P6\n%u %u\n255\n",width,(kml?height:height+30)); - if(debug){ - fprintf(stdout,"\nWriting \"%s\" (%ux%u pixmap image)... ",mapfile,width,(kml?height:height+30)); - fflush(stdout); - } - for (y=0, lat=north; y<(int)height; y++, lat=north-(dpp*(double)y)) - { - for (x=0, lon=max_west; x<(int)width; x++, lon=max_west-(dpp*(double)x)) - { - if (lon<0.0) - lon+=360.0; - - for (indx=0, found=0; indx=0 && x0<=mpi && y0>=0 && y0<=mpi) - found=1; - else - indx++; - } - - - - if (found) - { - mask=dem[indx].mask[x0][y0]; - loss=(dem[indx].signal[x0][y0]); - cityorcounty=0; - - if(debug){ - fprintf(stdout,"\n%d\t%d\t%d\t%d",loss,indx,x0,y0); - fflush(stdout); - } - match=255; - - red=0; - green=0; - blue=0; - - if (loss<=region.level[0]) - match=0; - else - { - for (z=1; (z=region.level[z-1] && loss=180 && green<=75 && blue<=75 && loss==0) - fprintf(fd,"%c%c%c",255^red,255^green,255^blue); - 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); - - cityorcounty=1; - } - - if (cityorcounty==0) - { - if (loss==0 || (contour_threshold!=0 && loss>abs(contour_threshold))) - { - if (ngs) /* No terrain */ - fprintf(fd,"%c%c%c",255,255,255); - else - { - /* Display land or sea elevation */ - - if (dem[indx].data[x0][y0]==0) - fprintf(fd,"%c%c%c",0,0,170); - else - { - terrain=(unsigned)(0.5+pow((double)(dem[indx].data[x0][y0]-min_elevation),one_over_gamma)*conversion); - fprintf(fd,"%c%c%c",terrain,terrain,terrain); - } - } - } - - else - { - /* Plot path loss in color */ - - if (red!=0 || green!=0 || blue!=0) - fprintf(fd,"%c%c%c",red,green,blue); - - else /* terrain / sea-level */ - { - if (dem[indx].data[x0][y0]==0) - fprintf(fd,"%c%c%c",0,0,170); - else - { - /* Elevation: Greyscale */ - terrain=(unsigned)(0.5+pow((double)(dem[indx].data[x0][y0]-min_elevation),one_over_gamma)*conversion); - fprintf(fd,"%c%c%c",terrain,terrain,terrain); - } - } - } - } - } - - else - { - /* We should never get here, but if */ - /* we do, display the region as black */ - - fprintf(fd,"%c%c%c",0,0,0); - } - } - } - - - - fclose(fd); - -} - -void DoSigStr(char *filename, unsigned char geo, unsigned char kml, unsigned char ngs, struct site *xmtr, unsigned char txsites) -{ - /* This function generates a topographic map in Portable Pix Map - (PPM) format based on the signal strength values held in the - signal[][] array. The image created is rotated counter-clockwise - 90 degrees from its representation in dem[][] so that north - points up and east points right in the image generated. */ - - char mapfile[255]; - unsigned width, height, terrain, red, green, blue; - unsigned char found, mask, cityorcounty; - int indx, x, y, z=1, x0, y0, signal,match; - double conversion, one_over_gamma, lat, lon, minwest; - FILE *fd; - - one_over_gamma=1.0/GAMMA; - conversion=255.0/pow((double)(max_elevation-min_elevation),one_over_gamma); - - width=(unsigned)(ippd*ReduceAngle(max_west-min_west)); - height=(unsigned)(ippd*ReduceAngle(max_north-min_north)); - - LoadSignalColors(xmtr[0]); - - if (filename[0]==0) - { - strncpy(filename, xmtr[0].filename,254); - filename[strlen(filename)-4]=0; /* Remove .qth */ - } - - y=strlen(filename); - - if (y>4) - { - if (filename[y-1]=='m' && filename[y-2]=='p' && filename[y-3]=='p' && filename[y-4]=='.') - y-=4; - } - - for (x=0; x360.0) - minwest-=360.0; - - north=(double)max_north-dpp; - - - south=(double)min_north; /* No bottom legend */ - - east=(minwest<180.0?-minwest:360.0-min_west); - west=(double)(max_west<180?-max_west:360-max_west); - fd=fopen(mapfile,"wb"); - - fprintf(fd,"P6\n%u %u\n255\n",width,(kml?height:height+30)); - if(debug){ - fprintf(stdout,"\nWriting \"%s\" (%ux%u pixmap image)... ",mapfile,width,(kml?height:height+30)); - fflush(stdout); - } - for (y=0, lat=north; y<(int)height; y++, lat=north-(dpp*(double)y)) - { - for (x=0, lon=max_west; x<(int)width; x++, lon=max_west-(dpp*(double)x)) - { - if (lon<0.0) - lon+=360.0; - - for (indx=0, found=0; indx=0 && x0<=mpi && y0>=0 && y0<=mpi) - found=1; - else - indx++; - } - - if (found) - { - mask=dem[indx].mask[x0][y0]; - signal=(dem[indx].signal[x0][y0])-100; - cityorcounty=0; - - if(debug){ - fprintf(stdout,"\n%d\t%d\t%d\t%d",signal,indx,x0,y0); - fflush(stdout); - } - - match=255; - - red=0; - green=0; - blue=0; - - if (signal>=region.level[0]) - match=0; - else - { - for (z=1; (z=region.level[z]) - match=z; - } - } - - if (match=180 && green<=75 && blue<=75) - fprintf(fd,"%c%c%c",255^red,255^green,255^blue); - 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); - - cityorcounty=1; - } - - if (cityorcounty==0) - { - if (contour_threshold!=0 && signal4) - { - if (filename[y-1]=='m' && filename[y-2]=='p' && filename[y-3]=='p' && filename[y-4]=='.') - y-=4; - } - - for (x=0; x360.0) - minwest-=360.0; - - north=(double)max_north-dpp; - - - south=(double)min_north; /* No bottom legend */ - - - east=(minwest<180.0?-minwest:360.0-min_west); - west=(double)(max_west<180?-max_west:360-max_west); - - fd=fopen(mapfile,"wb"); - - fprintf(fd,"P6\n%u %u\n255\n",width,(kml?height:height+30)); - if(debug){ - fprintf(stdout,"\nWriting \"%s\" (%ux%u pixmap image)... ",mapfile,width,(kml?height:height+30)); - fflush(stdout); - } - // WriteKML() - //writeKML(xmtr,filename); - - for (y=0, lat=north; y<(int)height; y++, lat=north-(dpp*(double)y)) - { - for (x=0, lon=max_west; x<(int)width; x++, lon=max_west-(dpp*(double)x)) - { - if (lon<0.0) - lon+=360.0; - - for (indx=0, found=0; indx=0 && x0<=mpi && y0>=0 && y0<=mpi) - found=1; - else - indx++; - } - - if (found) - { - mask=dem[indx].mask[x0][y0]; - dBm=(dem[indx].signal[x0][y0])-200; - cityorcounty=0; - - - if(debug){ - fprintf(stdout,"\n%d\t%d\t%d\t%d",dBm,indx,x0,y0); - fflush(stdout); - } - - match=255; - - red=0; - green=0; - blue=0; - - if (dBm>=region.level[0]) - match=0; - else - { - for (z=1; (z=region.level[z]) - match=z; - } - } - - if (match=180 && green<=75 && blue<=75 && dBm!=0) - fprintf(fd,"%c%c%c",255^red,255^green,255^blue); - 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); - - cityorcounty=1; - } - - if (cityorcounty==0) - { - if (contour_threshold!=0 && dBm4) - { - if (filename[y-1]=='m' && filename[y-2]=='p' && filename[y-3]=='p' && filename[y-4]=='.') - y-=4; - } - - for (x=0; x360.0) - minwest-=360.0; - - north=(double)max_north-dpp; - - - south=(double)min_north; /* No bottom legend */ - - - east=(minwest<180.0?-minwest:360.0-min_west); - west=(double)(max_west<180?-max_west:360-max_west); - - fd=fopen(mapfile,"wb"); - - fprintf(fd,"P6\n%u %u\n255\n",width,(kml?height:height+30)); - if(debug){ - fprintf(stdout,"\nWriting \"%s\" (%ux%u pixmap image)... ",mapfile,width,(kml?height:height+30)); - fflush(stdout); - } - // WriteKML() - //writeKML(xmtr,filename); - - for (y=0, lat=north; y<(int)height; y++, lat=north-(dpp*(double)y)) - { - for (x=0, lon=max_west; x<(int)width; x++, lon=max_west-(dpp*(double)x)) - { - if (lon<0.0) - lon+=360.0; - - for (indx=0, found=0; indx=0 && x0<=mpi && y0>=0 && y0<=mpi) - found=1; - else - indx++; - } - - if (found) - { - mask=dem[indx].mask[x0][y0]; - - if (mask&2) - /* Text Labels: Red */ - fprintf(fd,"%c%c%c",255,0,0); - - else if (mask&4) - /* County Boundaries: Light Cyan */ - fprintf(fd,"%c%c%c",128,128,255); - - else switch (mask&57) - { - case 1: - /* TX1: Green */ - fprintf(fd,"%c%c%c",0,255,0); - break; - - case 8: - /* TX2: Cyan */ - fprintf(fd,"%c%c%c",0,255,255); - break; - - case 9: - /* TX1 + TX2: Yellow */ - fprintf(fd,"%c%c%c",255,255,0); - break; - - case 16: - /* TX3: Medium Violet */ - fprintf(fd,"%c%c%c",147,112,219); - break; - - case 17: - /* TX1 + TX3: Pink */ - fprintf(fd,"%c%c%c",255,192,203); - break; - - case 24: - /* TX2 + TX3: Orange */ - fprintf(fd,"%c%c%c",255,165,0); - break; - - case 25: - /* TX1 + TX2 + TX3: Dark Green */ - fprintf(fd,"%c%c%c",0,100,0); - break; - - case 32: - /* TX4: Sienna 1 */ - fprintf(fd,"%c%c%c",255,130,71); - break; - - case 33: - /* TX1 + TX4: Green Yellow */ - fprintf(fd,"%c%c%c",173,255,47); - break; - - case 40: - /* TX2 + TX4: Dark Sea Green 1 */ - fprintf(fd,"%c%c%c",193,255,193); - break; - - case 41: - /* TX1 + TX2 + TX4: Blanched Almond */ - fprintf(fd,"%c%c%c",255,235,205); - break; - - case 48: - /* TX3 + TX4: Dark Turquoise */ - fprintf(fd,"%c%c%c",0,206,209); - break; - - case 49: - /* TX1 + TX3 + TX4: Medium Spring Green */ - fprintf(fd,"%c%c%c",0,250,154); - break; - - case 56: - /* TX2 + TX3 + TX4: Tan */ - fprintf(fd,"%c%c%c",210,180,140); - break; - - case 57: - /* TX1 + TX2 + TX3 + TX4: Gold2 */ - fprintf(fd,"%c%c%c",238,201,0); - break; - - default: - if (ngs) /* No terrain */ - fprintf(fd,"%c%c%c",255,255,255); - else - { - /* Sea-level: Medium Blue */ - if (dem[indx].data[x0][y0]==0) - fprintf(fd,"%c%c%c",0,0,170); - else - { - /* Elevation: Greyscale */ - terrain=(unsigned)(0.5+pow((double)(dem[indx].data[x0][y0]-min_elevation),one_over_gamma)*conversion); - fprintf(fd,"%c%c%c",terrain,terrain,terrain); - } - } - } - } - - else - { - /* We should never get here, but if */ - /* we do, display the region as black */ - - fprintf(fd,"%c%c%c",0,0,0); - } - } - } - - fclose(fd); - -} - - -void LoadTopoData(int max_lon, int min_lon, int max_lat, int min_lat, int winfiles) -{ - /* 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; - - if (winfiles==1){ - if (ippd==3600) - snprintf(string,19,"%d=%d=%d=%d=hd",x, x+1, ymin, ymax); - else - snprintf(string,16,"%d=%d=%d=%d",x, x+1, ymin, ymax); - - }else{ - if (ippd==3600) - snprintf(string,19,"%d:%d:%d:%d-hd",x, x+1, ymin, ymax); - else - snprintf(string,16,"%d:%d:%d:%d",x, x+1, ymin, ymax); - } - - LoadSDF(string,winfiles); - } - } - - 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; - - if (winfiles==1){ - if (ippd==3600) - snprintf(string,19,"%d=%d=%d=%d=hd",x, x+1, ymin, ymax); - else - snprintf(string,16,"%d=%d=%d=%d",x, x+1, ymin, ymax); - - }else{ - if (ippd==3600) - snprintf(string,19,"%d:%d:%d:%d-hd",x, x+1, ymin, ymax); - else - snprintf(string,16,"%d:%d:%d:%d",x, x+1, ymin, ymax); - } - - LoadSDF(string,winfiles); - } - } -} - - - -void LoadUDT(char *filename) -{ - /* This function reads a file containing User-Defined Terrain - features for their addition to the digital elevation model - data used by SPLAT!. Elevations in the UDT file are evaluated - and then copied into a temporary file under /tmp. Then the - contents of the temp file are scanned, and if found to be unique, - are added to the ground elevations described by the digital - elevation data already loaded into memory. */ - - int i, x, y, z, ypix, xpix, tempxpix, tempypix, fd=0, n=0, pixelfound=0; - char input[80], str[3][80], tempname[15], *pointer=NULL, *s=NULL; - double latitude, longitude, height, tempheight; - FILE *fd1=NULL, *fd2=NULL; - - strcpy(tempname,"/tmp/XXXXXX\0"); - - fd1=fopen(filename,"r"); - - if (fd1!=NULL) - { - fd=mkstemp(tempname); - fd2=fopen(tempname,"w"); - - s=fgets(input,78,fd1); - - pointer=strchr(input,';'); - - if (pointer!=NULL) - *pointer=0; - - - while (feof(fd1)==0) - { - /* Parse line for latitude, longitude, height */ - - for (x=0, y=0, z=0; x<78 && input[x]!=0 && z<3; x++) - { - if (input[x]!=',' && y<78) - { - str[z][y]=input[x]; - y++; - } - - else - { - str[z][y]=0; - z++; - y=0; - } - } - - latitude=ReadBearing(str[0]); - longitude=ReadBearing(str[1]); - - if (longitude<0.0) - longitude+=360; - - /* Remove and/or from antenna height string */ - - for (i=0; str[2][i]!=13 && str[2][i]!=10 && str[2][i]!=0; i++); - - str[2][i]=0; - - /* The terrain feature may be expressed in either - feet or meters. If the letter 'M' or 'm' is - discovered in the string, then this is an - indication that the value given is expressed - in meters. Otherwise the height is interpreted - as being expressed in feet. */ - - for (i=0; str[2][i]!='M' && str[2][i]!='m' && str[2][i]!=0 && i<48; i++); - - if (str[2][i]=='M' || str[2][i]=='m') - { - str[2][i]=0; - height=rint(atof(str[2])); - } - - else - { - str[2][i]=0; - height=rint(METERS_PER_FOOT*atof(str[2])); - } - - if (height>0.0) - fprintf(fd2,"%d, %d, %f\n",(int)rint(latitude/dpp), (int)rint(longitude/dpp), height); - - - s=fgets(input,78,fd1); - - pointer=strchr(input,';'); - - if (pointer!=NULL) - *pointer=0; - } - - fclose(fd1); - fclose(fd2); - close(fd); - - - fd1=fopen(tempname,"r"); - fd2=fopen(tempname,"r"); - - y=0; - - n=fscanf(fd1,"%d, %d, %lf", &xpix, &ypix, &height); - - do - { - x=0; - z=0; - - n=fscanf(fd2,"%d, %d, %lf", &tempxpix, &tempypix, &tempheight); - - do - { - if (x>y && xpix==tempxpix && ypix==tempypix) - { - z=1; /* Dupe! */ - - if (tempheight>height) - height=tempheight; - } - - else - { - n=fscanf(fd2,"%d, %d, %lf", &tempxpix, &tempypix, &tempheight); - x++; - } - - } while (feof(fd2)==0 && z==0); - - if (z==0) /* No duplicate found */ - - //fprintf(stdout,"%lf, %lf \n",xpix*dpp, ypix*dpp); - fflush(stdout); - pixelfound = AddElevation(xpix*dpp, ypix*dpp, height); - //fprintf(stdout,"%d \n",pixelfound); - fflush(stdout); - - n=fscanf(fd1,"%d, %d, %lf", &xpix, &ypix, &height); - y++; - - rewind(fd2); - - } while (feof(fd1)==0); - - fclose(fd1); - fclose(fd2); - unlink(tempname); - } - -} - -void PlotPath(struct site source, struct site destination, char mask_value) -{ - /* This function analyzes the path between the source and - destination locations. It determines which points along - the path have line-of-sight visibility to the source. - Points along with path having line-of-sight visibility - to the source at an AGL altitude equal to that of the - destination location are stored by setting bit 1 in the - mask[][] array, which are displayed in green when PPM - maps are later generated by SPLAT!. */ - - char block; - int x, y; - register double cos_xmtr_angle, cos_test_angle, test_alt; - double distance, rx_alt, tx_alt; - - ReadPath(source,destination); - - for (y=0; y=0 && block==0; x--) - { - distance=5280.0*(path.distance[y]-path.distance[x]); - test_alt=earthradius+(path.elevation[x]==0.0?path.elevation[x]:path.elevation[x]+clutter); - - 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 - 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 - be if the actual angles were compared. */ - - if (cos_xmtr_angle>=cos_test_angle) - block=1; - } - - if (block==0) - OrMask(path.lat[y],path.lon[y],mask_value); - } - } -} - -void ObstructionAnalysis(struct site xmtr, struct site rcvr, double f, FILE *outfile) -{ - /* Perform an obstruction analysis along the - path between receiver and transmitter. */ - - int x; - struct site site_x; - double h_r, h_t, h_x, h_r_orig, cos_tx_angle, cos_test_angle, - cos_tx_angle_f1, cos_tx_angle_fpt6, d_tx, d_x, - h_r_f1, h_r_fpt6, h_f, h_los, lambda=0.0; - char string[255], string_fpt6[255], string_f1[255]; - - 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); - - if (clutter>0.0) - { - fprintf(outfile,"Terrain has been raised by"); - - if (metric) - fprintf(outfile," %.2f meters",METERS_PER_FOOT*clutter); - else - fprintf(outfile," %.2f feet",clutter); - - fprintf(outfile," to account for ground clutter.\n\n"); - } - - /* 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 ppa!'s 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+clutter; - 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(outfile,"Between %s and %s, obstructions were detected at:\n\n",rcvr.name,xmtr.name); - - if (site_x.lat>=0.0) - { - if (metric) - fprintf(outfile," %8.4f N,%9.4f W, %5.2f kilometers, %6.2f meters AMSL\n",site_x.lat, site_x.lon, KM_PER_MILE*(d_x/5280.0), METERS_PER_FOOT*(h_x-earthradius)); - else - fprintf(outfile," %8.4f N,%9.4f W, %5.2f miles, %6.2f feet AMSL\n",site_x.lat, site_x.lon, d_x/5280.0, h_x-earthradius); - } - - else - { - if (metric) - fprintf(outfile," %8.4f S,%9.4f W, %5.2f kilometers, %6.2f meters AMSL\n",-site_x.lat, site_x.lon, KM_PER_MILE*(d_x/5280.0), METERS_PER_FOOT*(h_x-earthradius)); - else - fprintf(outfile," %8.4f S,%9.4f W, %5.2f miles, %6.2f feet AMSL\n",-site_x.lat, site_x.lon, d_x/5280.0, h_x-earthradius); - } - } - - 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... */ - - 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_fh_r_orig) - { - if (metric) - snprintf(string,150,"\nAntenna at %s must be raised to at least %.2f meters AGL\nto clear all obstructions detected.\n",rcvr.name, METERS_PER_FOOT*(h_r-GetElevation(rcvr)-earthradius)); - else - snprintf(string,150,"\nAntenna at %s must be raised to at least %.2f feet AGL\nto clear all obstructions detected.\n",rcvr.name, h_r-GetElevation(rcvr)-earthradius); - } - - else - snprintf(string,150,"\nNo obstructions to LOS path due to terrain were detected\n"); - - if (f) - { - if (h_r_fpt6>h_r_orig) - { - if (metric) - snprintf(string_fpt6,150,"\nAntenna at %s must be raised to at least %.2f meters AGL\nto clear %.0f%c of the first Fresnel zone.\n",rcvr.name, METERS_PER_FOOT*(h_r_fpt6-GetElevation(rcvr)-earthradius),fzone_clearance*100.0,37); - - else - snprintf(string_fpt6,150,"\nAntenna at %s must be raised to at least %.2f feet AGL\nto clear %.0f%c of the first Fresnel zone.\n",rcvr.name, h_r_fpt6-GetElevation(rcvr)-earthradius,fzone_clearance*100.0,37); - } - - else - snprintf(string_fpt6,150,"\n%.0f%c of the first Fresnel zone is clear.\n",fzone_clearance*100.0,37); - - if (h_r_f1>h_r_orig) - { - if (metric) - snprintf(string_f1,150,"\nAntenna at %s must be raised to at least %.2f meters AGL\nto clear the first Fresnel zone.\n",rcvr.name, METERS_PER_FOOT*(h_r_f1-GetElevation(rcvr)-earthradius)); - - else - snprintf(string_f1,150,"\nAntenna at %s must be raised to at least %.2f feet AGL\nto clear the first Fresnel zone.\n",rcvr.name, h_r_f1-GetElevation(rcvr)-earthradius); - - } - - else - snprintf(string_f1,150,"\nThe first Fresnel zone is clear.\n"); - } - - fprintf(outfile,"%s",string); - - if (f) - { - fprintf(outfile,"%s",string_f1); - fprintf(outfile,"%s",string_fpt6); - } - - -} - - -void PathReport(struct site source, struct site destination, char *name, char graph_it, int propmodel, int pmenv) -{ - /* This function writes a PPA Path Report (name.txt) to - the filesystem. If (graph_it == 1), then gnuplot is invoked - to generate an appropriate output file indicating the Longley-Rice - model loss between the source and destination locations. - "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, .png is assumed. */ - - int x, y, z, errnum; - char basename[255], term[30], ext[15], strmode[100], - report_name[80], block=0; - double maxloss=-100000.0, minloss=100000.0, angle1, angle2, - azimuth, pattern=1.0, patterndB=0.0, - total_loss=0.0, cos_xmtr_angle, cos_test_angle=0.0, - source_alt, test_alt, dest_alt, source_alt2, dest_alt2, - distance, elevation, four_thirds_earth, - free_space_loss=0.0, eirp=0.0, voltage, rxp, power_density, dkm, txelev; - FILE *fd=NULL, *fd2=NULL; - - snprintf(report_name,80,"%s.txt%c",name,0); - four_thirds_earth=FOUR_THIRDS*EARTHRADIUS; - - fd2=fopen(report_name,"w"); - - fprintf(fd2,"\n\t\t--==[ Path Profile Analysis ]==--\n\n"); - fprintf(fd2,"Transmitter site: %s\n",source.name); - - if (source.lat>=0.0) - { - fprintf(fd2,"Site location: %.4f North / %.4f West\n",source.lat, source.lon); - } - - else - { - - fprintf(fd2,"Site location: %.4f South / %.4f West\n",-source.lat, source.lon); - } - - - if (metric) - { - fprintf(fd2,"Ground elevation: %.2f meters AMSL\n",METERS_PER_FOOT*GetElevation(source)); - fprintf(fd2,"Antenna height: %.2f meters AGL / %.2f meters AMSL\n",METERS_PER_FOOT*source.alt,METERS_PER_FOOT*(source.alt+GetElevation(source))); - } - - else - { - 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)); - } - - - azimuth=Azimuth(source,destination); - angle1=ElevationAngle(source,destination); - angle2=ElevationAngle2(source,destination,earthradius); - - if (got_azimuth_pattern || got_elevation_pattern) - { - x=(int)rint(10.0*(10.0-angle2)); - - if (x>=0 && x<=1000) - pattern=(double)LR.antenna_pattern[(int)rint(azimuth)][x]; - - patterndB=20.0*log10(pattern); - } - - if (metric) - fprintf(fd2,"Distance to %s: %.2f kilometers\n",destination.name,KM_PER_MILE*Distance(source,destination)); - - else - fprintf(fd2,"Distance to %s: %.2f miles\n",destination.name,Distance(source,destination)); - - fprintf(fd2,"Azimuth to %s: %.2f degrees\n",destination.name,azimuth); - - if (angle1>=0.0) - fprintf(fd2,"Elevation angle to %s: %+.4f degrees\n",destination.name,angle1); - - else - fprintf(fd2,"Depression angle to %s: %+.4f degrees\n",destination.name,angle1); - - if ((angle2-angle1)>0.0001) - { - if (angle2<0.0) - fprintf(fd2,"Depression\n"); - else - fprintf(fd2,"Elevation\n"); - } - - /* Receiver */ - - fprintf(fd2,"\nReceiver site: %s\n",destination.name); - - if (destination.lat>=0.0) - { - fprintf(fd2,"Site location: %.4f North / %.4f West\n",destination.lat, destination.lon); - } - - else - { - fprintf(fd2,"Site location: %.4f South / %.4f West\n",-destination.lat, destination.lon); - } - - if (metric) - { - fprintf(fd2,"Ground elevation: %.2f meters AMSL\n",METERS_PER_FOOT*GetElevation(destination)); - fprintf(fd2,"Antenna height: %.2f meters AGL / %.2f meters AMSL\n",METERS_PER_FOOT*destination.alt, METERS_PER_FOOT*(destination.alt+GetElevation(destination))); - } - - else - { - 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)); - } - - - - if (metric) - fprintf(fd2,"Distance to %s: %.2f kilometers\n",source.name,KM_PER_MILE*Distance(source,destination)); - - else - fprintf(fd2,"Distance to %s: %.2f miles\n",source.name,Distance(source,destination)); - - azimuth=Azimuth(destination,source); - - angle1=ElevationAngle(destination,source); - angle2=ElevationAngle2(destination,source,earthradius); - - fprintf(fd2,"Azimuth to %s: %.2f degrees\n",source.name,azimuth); - - if (angle1>=0.0) - fprintf(fd2,"Elevation angle to %s: %+.4f degrees\n",source.name,angle1); - - else - fprintf(fd2,"Depression angle to %s: %+.4f degrees\n",source.name,angle1); - - if ((angle2-angle1)>0.0001) - { - if (angle2<0.0) - fprintf(fd2,"Depression"); - else - fprintf(fd2,"Elevation"); - } - - if (LR.frq_mhz>0.0) - { - fprintf(fd2,"\nPropagation model: "); - - switch (propmodel) - { - case 1: - fprintf(fd2,"Irregular Terrain Model\n"); - break; - case 2: - fprintf(fd2,"Line of sight\n"); - break; - case 3: - fprintf(fd2,"Okumura-Hata\n"); - break; - case 4: - fprintf(fd2,"ECC33 (ITU-R P.529)\n"); - break; - case 5: - fprintf(fd2,"Stanford University Interim\n"); - break; - case 6: - fprintf(fd2,"COST231-Hata\n"); - break; - case 7: - fprintf(fd2,"Free space path loss (ITU-R.525)\n"); - break; - case 8: - fprintf(fd2,"ITWOM 3.0\n"); - break; - case 9: - fprintf(fd2,"Ericsson\n"); - break; - } - - fprintf(fd2,"Model sub-type: "); - - switch (pmenv) - { - case 1: - fprintf(fd2,"City / Conservative\n"); - break; - case 2: - fprintf(fd2,"Suburban / Average\n"); - break; - case 3: - fprintf(fd2,"Rural / Optimistic\n"); - break; - } - fprintf(fd2,"Earth's Dielectric Constant: %.3lf\n",LR.eps_dielect); - fprintf(fd2,"Earth's Conductivity: %.3lf Siemens/meter\n",LR.sgm_conductivity); - fprintf(fd2,"Atmospheric Bending Constant (N-units): %.3lf ppm\n",LR.eno_ns_surfref); - fprintf(fd2,"Frequency: %.3lf MHz\n",LR.frq_mhz); - fprintf(fd2,"Radio Climate: %d (",LR.radio_climate); - - switch (LR.radio_climate) - { - case 1: - fprintf(fd2,"Equatorial"); - break; - - case 2: - fprintf(fd2,"Continental Subtropical"); - break; - - case 3: - fprintf(fd2,"Maritime Subtropical"); - break; - - case 4: - fprintf(fd2,"Desert"); - break; - - case 5: - fprintf(fd2,"Continental Temperate"); - break; - - case 6: - fprintf(fd2,"Maritime Temperate, Over Land"); - break; - - case 7: - fprintf(fd2,"Maritime Temperate, Over Sea"); - break; - - default: - fprintf(fd2,"Unknown"); - } - - fprintf(fd2,")\nPolarisation: %d (",LR.pol); - - if (LR.pol==0) - fprintf(fd2,"Horizontal"); - - if (LR.pol==1) - fprintf(fd2,"Vertical"); - - fprintf(fd2,")\nFraction of Situations: %.1lf%c\n",LR.conf*100.0,37); - fprintf(fd2,"Fraction of Time: %.1lf%c\n",LR.rel*100.0,37); - - if (LR.erp!=0.0) - { - fprintf(fd2,"Transmitter ERP: "); - - if (LR.erp<1.0) - fprintf(fd2,"%.1lf milliwatts",1000.0*LR.erp); - - if (LR.erp>=1.0 && LR.erp<10.0) - fprintf(fd2,"%.1lf Watts",LR.erp); - - if (LR.erp>=10.0 && LR.erp<10.0e3) - fprintf(fd2,"%.0lf Watts",LR.erp); - - if (LR.erp>=10.0e3) - fprintf(fd2,"%.3lf kilowatts",LR.erp/1.0e3); - - dBm=10.0*(log10(LR.erp*1000.0)); - fprintf(fd2," (%+.2f dBm)\n",dBm); - - /* EIRP = ERP + 2.14 dB */ - - fprintf(fd2,"Transmitter EIRP: "); - - eirp=LR.erp*1.636816521; - - if (eirp<1.0) - fprintf(fd2,"%.1lf milliwatts",1000.0*eirp); - - if (eirp>=1.0 && eirp<10.0) - fprintf(fd2,"%.1lf Watts",eirp); - - if (eirp>=10.0 && eirp<10.0e3) - fprintf(fd2,"%.0lf Watts",eirp); - - if (eirp>=10.0e3) - fprintf(fd2,"%.3lf kilowatts",eirp/1.0e3); - - dBm=10.0*(log10(eirp*1000.0)); - fprintf(fd2," (%+.2f dBm)\n",dBm); - } - - - fprintf(fd2,"\nSummary for the link between %s and %s:\n\n",source.name, destination.name); - - if (patterndB!=0.0) - fprintf(fd2,"%s antenna pattern towards %s: %.3f (%.2f dB)\n", source.name, destination.name, pattern, patterndB); - - ReadPath(source, destination); /* source=TX, destination=RX */ - - /* Copy elevations plus clutter along - path into the elev[] array. */ - - for (x=1; x=cos_test_angle) - block=1; - } - - /* At this point, we have the elevation angle - to the first obstruction (if it exists). */ - } - - /* Determine path loss for each point along the - path using Longley-Rice's point_to_point mode - starting at x=2 (number_of_points = 1), the - shortest distance terrain can play a role in - path loss. */ - - elev[0]=y-1; /* (number of points - 1) */ - - /* Distance between elevation samples */ - - elev[1]=METERS_PER_MILE*(path.distance[y]-path.distance[y-1]); - - /* - point_to_point(elev, source.alt*METERS_PER_FOOT, - destination.alt*METERS_PER_FOOT, LR.eps_dielect, - LR.sgm_conductivity, LR.eno_ns_surfref, LR.frq_mhz, - LR.radio_climate, LR.pol, LR.conf, LR.rel, loss, - strmode, errnum); - */ - dkm=(elev[1]*elev[0])/1000; // km - txelev=elev[2]+(source.alt*METERS_PER_FOOT); - - switch (propmodel) - { - case 1: - // Longley Rice ITM - point_to_point_ITM(elev,source.alt*METERS_PER_FOOT, - destination.alt*METERS_PER_FOOT, LR.eps_dielect, - LR.sgm_conductivity, LR.eno_ns_surfref, LR.frq_mhz, - LR.radio_climate, LR.pol, LR.conf, LR.rel, loss, - strmode, errnum); - break; - case 3: - //HATA 1, 2 & 3 - loss=HATApathLoss(LR.frq_mhz,txelev,path.elevation[y]+(destination.alt*METERS_PER_FOOT),dkm, pmenv); - break; - case 4: - // COST231-HATA - loss=ECC33pathLoss(LR.frq_mhz,txelev,path.elevation[y]+(destination.alt*METERS_PER_FOOT),dkm, pmenv); - break; - case 5: - // SUI - loss=SUIpathLoss(LR.frq_mhz,txelev,path.elevation[y]+(destination.alt*METERS_PER_FOOT),dkm, pmenv); - break; - case 6: - loss=COST231pathLoss(LR.frq_mhz,txelev,path.elevation[y]+(destination.alt*METERS_PER_FOOT),dkm, pmenv); - break; - case 7: - // ITU-R P.525 Free space path loss - loss=FSPLpathLoss(LR.frq_mhz,dkm); - break; - case 8: - // ITWOM 3.0 - point_to_point(elev,source.alt*METERS_PER_FOOT, - destination.alt*METERS_PER_FOOT, LR.eps_dielect, - LR.sgm_conductivity, LR.eno_ns_surfref, LR.frq_mhz, - LR.radio_climate, LR.pol, LR.conf, LR.rel, loss, - strmode, errnum); - break; - case 9: - // Ericsson - loss=EricssonpathLoss(LR.frq_mhz,txelev,path.elevation[y]+(destination.alt*METERS_PER_FOOT),dkm, pmenv); - break; - - - default: - point_to_point_ITM(elev,source.alt*METERS_PER_FOOT, - destination.alt*METERS_PER_FOOT, LR.eps_dielect, - LR.sgm_conductivity, LR.eno_ns_surfref, LR.frq_mhz, - LR.radio_climate, LR.pol, LR.conf, LR.rel, loss, - strmode, errnum); - - } - - if (block) - elevation=((acos(cos_test_angle))/DEG2RAD)-90.0; - else - elevation=((acos(cos_xmtr_angle))/DEG2RAD)-90.0; - - /* Integrate the antenna's radiation - pattern into the overall path loss. */ - - x=(int)rint(10.0*(10.0-elevation)); - - if (x>=0 && x<=1000) - { - pattern=(double)LR.antenna_pattern[(int)azimuth][x]; - - if (pattern!=0.0) - patterndB=20.0*log10(pattern); - } - - else - patterndB=0.0; - - total_loss=loss-patterndB; - - - if (total_loss>maxloss) - maxloss=total_loss; - - if (total_loss0 && name[x]!='.'; x--); - - if (x>0) /* Extension found */ - { - for (z=x+1; z<=y && (z-(x+1))<10; z++) - { - ext[z-(x+1)]=tolower(name[z]); - term[z-(x+1)]=name[z]; - } - - ext[z-(x+1)]=0; /* Ensure an ending 0 */ - term[z-(x+1)]=0; - basename[x]=0; - } - } - - if (ext[0]==0) /* No extension -- Default is png */ - { - strncpy(term,"png\0",4); - strncpy(ext,"png\0",4); - } - - /* Either .ps or .postscript may be used - as an extension for postscript output. */ - - if (strncmp(term,"postscript",10)==0) - strncpy(ext,"ps\0",3); - - else if (strncmp(ext,"ps",2)==0) - strncpy(term,"postscript enhanced color\0",26); - - fd=fopen("ppa.gp","w"); - - fprintf(fd,"set grid\n"); - fprintf(fd,"set yrange [%2.3f to %2.3f]\n", minloss, maxloss); - fprintf(fd,"set encoding iso_8859_1\n"); - fprintf(fd,"set term %s\n",term); - fprintf(fd,"set title \"Path Loss Profile Along Path Between %s and %s (%.2f%c azimuth)\"\n",destination.name, source.name, Azimuth(destination,source),176); - - if (metric) - fprintf(fd,"set xlabel \"Distance Between %s and %s (%.2f kilometers)\"\n",destination.name,source.name,KM_PER_MILE*Distance(destination,source)); - else - fprintf(fd,"set xlabel \"Distance Between %s and %s (%.2f miles)\"\n",destination.name,source.name,Distance(destination,source)); - - if (got_azimuth_pattern || got_elevation_pattern) - fprintf(fd,"set ylabel \"Total Path Loss (including TX antenna pattern) (dB)"); - else - fprintf(fd,"set ylabel \"Longley-Rice Path Loss (dB)"); - - fprintf(fd,"\"\nset output \"%s.%s\"\n",basename,ext); - fprintf(fd,"plot \"profile.gp\" title \"Path Loss\" with lines\n"); - - fclose(fd); - - x=system("gnuplot ppa.gp"); - - if (x!=-1) - { - if (gpsav==0) - { - //unlink("ppa.gp"); - //unlink("profile.gp"); - //unlink("reference.gp"); - } - - - } - - else - fprintf(stderr,"\n*** ERROR: Error occurred invoking gnuplot!\n"); - } - -} - -void SeriesData(struct site source, struct site destination, char *name, unsigned char fresnel_plot, unsigned char normalised) -{ - - int x, y, z; - char basename[255], term[30], ext[15], profilename[255], referencename[255],cluttername[255],curvaturename[255],fresnelname[255],fresnel60name[255]; - double a, b, c, height=0.0, refangle, cangle, maxheight=-100000.0, - minheight=100000.0, lambda=0.0, f_zone=0.0, fpt6_zone=0.0, - nm=0.0, nb=0.0, ed=0.0, es=0.0, r=0.0, d=0.0, d1=0.0, - terrain, azimuth, distance, minterrain=100000.0, - minearth=100000.0; - struct site remote; - FILE *fd=NULL, *fd1=NULL, *fd2=NULL, *fd3=NULL, *fd4=NULL, *fd5=NULL; - - ReadPath(destination,source); - azimuth=Azimuth(destination,source); - distance=Distance(destination,source); - refangle=ElevationAngle(destination,source); - b=GetElevation(destination)+destination.alt+earthradius; - - if (fresnel_plot) - { - lambda=9.8425e8/(LR.frq_mhz*1e6); - d=5280.0*path.distance[path.length-1]; - } - - if (normalised) - { - ed=GetElevation(destination); - es=GetElevation(source); - nb=-destination.alt-ed; - nm=(-source.alt-es-nb)/(path.distance[path.length-1]); - } - - strcpy(profilename,name); - strcat(profilename,"_profile\0"); - strcpy(referencename,name); - strcat(referencename,"_reference\0"); - strcpy(cluttername,name); - strcat(cluttername,"_clutter\0"); - strcpy(curvaturename,name); - strcat(curvaturename,"_curvature\0"); - strcpy(fresnelname,name); - strcat(fresnelname,"_fresnel\0"); - strcpy(fresnel60name,name); - strcat(fresnel60name,"_fresnel60\0"); - - fd=fopen(profilename,"wb"); - if (clutter>0.0) - fd1=fopen(cluttername,"wb"); - fd2=fopen(referencename,"wb"); - fd5=fopen(curvaturename, "wb"); - - if ((LR.frq_mhz>=20.0) && (LR.frq_mhz<=100000.0) && fresnel_plot) - { - fd3=fopen(fresnelname, "wb"); - fd4=fopen(fresnel60name, "wb"); - } - - for (x=0; x=20.0) && (LR.frq_mhz<=100000.0) && fresnel_plot) - { - d1=5280.0*path.distance[x]; - f_zone=-1.0*sqrt(lambda*d1*(d-d1)/d); - fpt6_zone=f_zone*fzone_clearance; - } - - if (normalised) - { - r=-(nm*path.distance[x])-nb; - height+=r; - - if ((LR.frq_mhz>=20.0) && (LR.frq_mhz<=100000.0) && fresnel_plot) - { - f_zone+=r; - fpt6_zone+=r; - } - } - - else - r=0.0; - - if (metric) - { - if (METERS_PER_FOOT*height > 0){ - fprintf(fd,"%.3f %.3f\n",KM_PER_MILE*path.distance[x],METERS_PER_FOOT*height); - } - - if (fd1!=NULL && x>0 && x0 && x=20.0) && (LR.frq_mhz<=100000.0) && fresnel_plot) - { - if (metric) - { - fprintf(fd3,"%.3f %.3f\n",KM_PER_MILE*path.distance[x],METERS_PER_FOOT*f_zone); - fprintf(fd4,"%.3f %.3f\n",KM_PER_MILE*path.distance[x],METERS_PER_FOOT*fpt6_zone); - } - - else - { - fprintf(fd3,"%.3f %.3f\n",path.distance[x],f_zone); - fprintf(fd4,"%.3f %.3f\n",path.distance[x],fpt6_zone); - } - - if (f_zonemaxheight) - maxheight=height+clutter; - - if (heightmaxheight) - maxheight=r; - - if (terrain=20.0) && (LR.frq_mhz<=100000.0) && fresnel_plot) - { - if (metric) - { - fprintf(fd3,"%.3f %.3f",KM_PER_MILE*path.distance[path.length-1],METERS_PER_FOOT*r); - fprintf(fd4,"%.3f %.3f",KM_PER_MILE*path.distance[path.length-1],METERS_PER_FOOT*r); - } - - else - { - fprintf(fd3,"%.3f %.3f",path.distance[path.length-1],r); - fprintf(fd4,"%.3f %.3f",path.distance[path.length-1],r); - } - } - - if (r>maxheight) - maxheight=r; - - if (r=20.0) && (LR.frq_mhz<=100000.0) && fresnel_plot) - { - fclose(fd3); - fclose(fd4); - } - - if (name[0]=='.') - { - strncpy(basename,"profile\0",8); - strncpy(term,"png\0",4); - strncpy(ext,"png\0",4); - } - - else - { - - ext[0]=0; - y=strlen(name); - strncpy(basename,name,254); - - for (x=y-1; x>0 && name[x]!='.'; x--); - - if (x>0) - { - for (z=x+1; z<=y && (z-(x+1))<10; z++) - { - ext[z-(x+1)]=tolower(name[z]); - term[z-(x+1)]=name[z]; - } - - ext[z-(x+1)]=0; - term[z-(x+1)]=0; - basename[x]=0; - } - - if (ext[0]==0) - { - strncpy(term,"png\0",4); - strncpy(ext,"png\0",4); - } - } - - fprintf(stdout,"\n"); - fflush(stdout); - -} - -int main(int argc, char *argv[]) -{ - int x, y, z=0, min_lat, min_lon, max_lat, max_lon, - rxlat, rxlon, txlat, txlon, west_min, west_max, - nortRxHin, nortRxHax, propmodel, winfiles,knifeedge=0,ppa=0,normalise=0, haf=0, pmenv=1; - - unsigned char LRmap=0, txsites=0,topomap=0, geo=0, kml=0, area_mode=0, max_txsites,ngs=0; - - char mapfile[255], longley_file[255], udt_file[255],ano_filename[255]; - - double altitude=0.0, altitudeLR=0.0, tx_range=0.0, - rx_range=0.0, deg_range=0.0, deg_limit=0.0, - deg_range_lon; - - strncpy(ss_name,"Signal Server\0",14); - - if (argc==1) - { - - fprintf(stdout,"\n\t\t -- %s %.2f --\n",ss_name, version); - fprintf(stdout,"\tCompiled for %d tiles at %d pixels/degree\n\n",MAXPAGES,IPPD); - fprintf(stdout," -d Directory containing .sdf tiles\n"); - fprintf(stdout," -lat Tx Latitude (decimal degrees) -70/+70\n"); - fprintf(stdout," -lon Tx Longitude (decimal degrees) -180/+180\n"); - fprintf(stdout," -txh Tx Height (above ground)\n"); - fprintf(stdout," -rla (Optional) Rx Latitude for PPA (decimal degrees) -70/+70\n"); - fprintf(stdout," -rlo (Optional) Rx Longitude for PPA (decimal degrees) -180/+180\n"); - fprintf(stdout," -f Tx Frequency (MHz) 20MHz to 100GHz (LOS after 20GHz)\n"); - fprintf(stdout," -erp Tx Effective Radiated Power (Watts)\n"); - fprintf(stdout," -rxh Rx Height(s) (optional. Default=0.1)\n"); - fprintf(stdout," -rt Rx Threshold (dB / dBm / dBuV/m)\n"); - fprintf(stdout," -hp Horizontal Polarisation (default=vertical)\n"); - fprintf(stdout," -gc Ground clutter (feet/meters)\n"); - fprintf(stdout," -udt User defined terrain filename\n"); - fprintf(stdout," -dbm Plot Rxd signal power instead of field strength\n"); - fprintf(stdout," -m Metric units of measurement\n"); - fprintf(stdout," -te Terrain code 1-6 (optional)\n"); - fprintf(stdout," -terdic Terrain dielectric value 2-80 (optional)\n"); - fprintf(stdout," -tercon Terrain conductivity 0.01-0.0001 (optional)\n"); - fprintf(stdout," -cl Climate code 1-6 (optional)\n"); - fprintf(stdout," -o Filename. Required. \n"); - fprintf(stdout," -R Radius (miles/kilometers)\n"); - fprintf(stdout," -res Pixels per degree. 300/600/1200(default)/3600 (optional)\n"); - fprintf(stdout," -t Terrain background\n"); - fprintf(stdout," -pm Prop model. 1: ITM, 2: LOS, 3: Hata, 4: ECC33,\n"); - fprintf(stdout," 5: SUI, 6: COST-Hata, 7: FSPL, 8: ITWOM, 9: Ericsson\n"); - fprintf(stdout," -pe Prop model mode: 1=Urban,2=Suburban,3=Rural\n"); - fprintf(stdout," -ked Knife edge diffraction (Default for ITM)\n"); - fprintf(stdout," -ng Normalise Path Profile graph\n"); - fprintf(stdout," -haf Halve 1 or 2 (optional)\n"); - - - fflush(stdout); - - return 1; - } - - y=argc-1; - - kml=0; - geo=0; - dbm=0; - gpsav=0; - metric=0; - string[0]=0; - mapfile[0]=0; - clutter=0.0; - forced_erp=-1.0; - forced_freq=0.0; - sdf_path[0]=0; - udt_file[0]=0; - path.length=0; - max_txsites=30; - fzone_clearance=0.6; - contour_threshold=0; - - longley_file[0]=0; - ano_filename[0]=0; - earthradius=EARTHRADIUS; - max_range=1.0; - propmodel=1; //ITM - winfiles=0; - - lat=0; - lon=0; - txh=0; - ngs=1; // no terrain background - kml=1; - LRmap=1; - area_mode=1; - ippd=IPPD; // default resolution - - sscanf("0.1","%lf",&altitudeLR); - - // Defaults - LR.eps_dielect=15.0; // Farmland - LR.sgm_conductivity=0.005; // Farmland - LR.eno_ns_surfref=301.0; - LR.frq_mhz=19.0; // Deliberately too low - LR.radio_climate=5; // continental - LR.pol=1; // vert - LR.conf=0.50; - LR.rel=0.50; - LR.erp=0.0; // will default to Path Loss - - tx_site[0].lat=91.0; - tx_site[0].lon=361.0; - tx_site[1].lat=91.0; - tx_site[1].lon=361.0; - - for (x=0; x 90 || tx_site[0].lat < -90) - { - fprintf(stdout,"ERROR: Either the lat was missing or out of range!"); - exit(0); - - } - if (tx_site[0].lon > 360 || tx_site[0].lon < 0) - { - fprintf(stdout,"ERROR: Either the lon was missing or out of range!"); - exit(0); - - } - if (LR.frq_mhz < 20 || LR.frq_mhz > 100000) - { - fprintf(stdout,"ERROR: Either the Frequency was missing or out of range!"); - exit(0); - } - if (LR.erp>5000000) - { - fprintf(stdout,"ERROR: Power was out of range!"); - exit(0); - - } - if (LR.eps_dielect > 80 || LR.eps_dielect < 0.1) - { - fprintf(stdout,"ERROR: Ground Dielectric value out of range!"); - exit(0); - - } - if (LR.sgm_conductivity > 0.01 || LR.sgm_conductivity < 0.000001) - { - fprintf(stdout,"ERROR: Ground conductivity out of range!"); - exit(0); - - } - - if (tx_site[0].alt < 0 || tx_site[0].alt > 60000) - { - fprintf(stdout,"ERROR: Tx altitude above ground was too high: %f", tx_site[0].alt); - exit(0); - } - if (altitudeLR < 0 || altitudeLR > 60000) - { - fprintf(stdout,"ERROR: Rx altitude above ground was too high!"); - exit(0); - } - if (ippd < 300 || ippd > 3600){ - fprintf(stdout,"ERROR: resolution out of range!"); - exit(0); - } - if(contour_threshold < -200 || contour_threshold > 200) - { - fprintf(stdout,"ERROR: Receiver threshold out of range (-200 / +200)"); - exit(0); - } - if(propmodel>2 && propmodel<8 && LR.frq_mhz < 150){ - fprintf(stdout,"ERROR: Frequency too low for Propagation model"); - exit(0); - } - - - - if (metric) - { - altitudeLR/=METERS_PER_FOOT; /* 10ft * 0.3 = 3.3m */ - max_range/=KM_PER_MILE; /* 10 / 1.6 = 7.5 */ - altitude/=METERS_PER_FOOT; - tx_site[0].alt/=METERS_PER_FOOT; /* Feet to metres */ - tx_site[1].alt/=METERS_PER_FOOT; /* Feet to metres */ - clutter/=METERS_PER_FOOT; /* Feet to metres */ - } - - /* Ensure a trailing '/' is present in sdf_path */ - - if (sdf_path[0]) - { - x=strlen(sdf_path); - - if (sdf_path[x-1]!='/' && x!=0) - { - sdf_path[x]='/'; - sdf_path[x+1]=0; - } - } - - x=0; - y=0; - - min_lat=70; - max_lat=-70; - - min_lon=(int)floor(tx_site[0].lon); - max_lon=(int)floor(tx_site[0].lon); - - - txlat=(int)floor(tx_site[0].lat); - txlon=(int)floor(tx_site[0].lon); - - if (txlatmax_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 (ppa==1) - { - rxlat=(int)floor(tx_site[1].lat); - rxlon=(int)floor(tx_site[1].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; - } - - /* Load the required SDF files */ - - LoadTopoData(max_lon, min_lon, max_lat, min_lat, winfiles); - - if (area_mode || topomap) - { - for (z=0; zdeg_limit) - deg_range=deg_limit; - - if (deg_range_lon>deg_limit) - deg_range_lon=deg_limit; - - nortRxHin=(int)floor(tx_site[z].lat-deg_range); - nortRxHax=(int)floor(tx_site[z].lat+deg_range); - - west_min=(int)floor(tx_site[z].lon-deg_range_lon); - - while (west_min<0) - west_min+=360; - - while (west_min>=360) - west_min-=360; - - west_max=(int)floor(tx_site[z].lon+deg_range_lon); - - while (west_max<0) - west_max+=360; - - while (west_max>=360) - west_max-=360; - - if (nortRxHinmax_lat) - max_lat=nortRxHax; - - if (LonDiff(west_min,min_lon)<0.0) - min_lon=west_min; - - if (LonDiff(west_max,max_lon)>=0.0) - max_lon=west_max; - } - - /* Load any additional SDF files, if required */ - - LoadTopoData(max_lon, min_lon, max_lat, min_lat, winfiles); - } - - // UDT clutter - LoadUDT (udt_file); - - if(ppa==0){ - if (propmodel==2){ - PlotLOSMap(tx_site[0],altitudeLR,ano_filename); - DoLOS(mapfile,geo,kml,ngs,tx_site,txsites); - } - else{ - // 90% of effort here - PlotPropagation(tx_site[0],altitudeLR,ano_filename,propmodel,knifeedge,haf,pmenv); - - // Near field bugfix - PutSignal(tx_site[0].lat,tx_site[0].lon,hottest); - for(lat=tx_site[0].lat-0.002;lat<=tx_site[0].lat+0.002;lat=lat+0.0005){ - for(lon=tx_site[0].lon-0.002;lon<=tx_site[0].lon+0.002;lon=lon+0.0005){ - PutSignal(lat,lon,hottest); - } - } - if (LR.erp==0.0) - DoPathLoss(mapfile,geo,kml,ngs,tx_site,txsites); - else - if (dbm) - DoRxdPwr(mapfile,geo,kml,ngs,tx_site,txsites); - else - DoSigStr(mapfile,geo,kml,ngs,tx_site,txsites); - } - fprintf(stdout,"|%.5f",north); - fprintf(stdout,"|%.5f",east); - fprintf(stdout,"|%.5f",south); - fprintf(stdout,"|%.5f|",west); - - }else{ - strncpy(tx_site[0].name,"Tx",3); - strncpy(tx_site[1].name,"Rx",3); - PlotPath(tx_site[0],tx_site[1],1); - PathReport(tx_site[0],tx_site[1],tx_site[0].filename,0,propmodel,pmenv); - SeriesData(tx_site[0],tx_site[1],tx_site[0].filename,1,normalise); - } - fflush(stdout); - return 0; -} diff --git a/main.hh b/main.hh new file mode 100644 index 0000000..0f9dca6 --- /dev/null +++ b/main.hh @@ -0,0 +1,26 @@ +#ifndef _MAIN_HH_ +#define _MAIN_HH_ + +#include + +#include "common.h" + +int ReduceAngle(double angle); +double LonDiff(double lon1, double lon2); +int PutMask(double lat, double lon, int value); +int OrMask(double lat, double lon, int value); +int GetMask(double lat, double lon); +int PutSignal(double lat, double lon, unsigned char signal); +unsigned char GetSignal(double lat, double lon); +double GetElevation(struct site location); +int AddElevation(double lat, double lon, double height); +double Distance(struct site site1, struct site site2); +double Azimuth(struct site source, struct site destination); +double ElevationAngle(struct site source, struct site destination); +void ReadPath(struct site source, struct site destination); +double ElevationAngle2(struct site source, struct site destination, double er); +double ReadBearing(char *input); +void ObstructionAnalysis(struct site xmtr, struct site rcvr, double f, + FILE *outfile); + +#endif /* _MAIN_HH_ */ diff --git a/mainHD.cc b/mainHD.cc new file mode 100644 index 0000000..7339be0 --- /dev/null +++ b/mainHD.cc @@ -0,0 +1,5984 @@ +double version = 2.31; +/****************************************************************************\ +* Signal Server: Server optimised SPLAT! by Alex Farrant * +****************************************************************************** +* SPLAT! Project started in 1997 by John A. Magliacane, KD2BD * +* * +****************************************************************************** +* Please consult the SPLAT! documentation for a complete list of * +* individuals who have contributed to this project. * +****************************************************************************** +* * +* 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 * +* version. * +* * +* This program is distributed in the hope that it will useful, but WITHOUT * +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * +* for more details. * +* * +****************************************************************************** +* g++ -Wall -O3 -s -lm -fomit-frame-pointer itwom3.0.cpp hata.cpp cost.cpp fspl.cpp main.cpp -o ss * +\****************************************************************************/ +/* +2.31 - ERP up to 5MW for Mexican TV! +2.3 - Added ITWOM3.0 +2.22 - Fixed LOS not outputting bounds +2.2 - Made .dot output opt in to save some disk space +2.1 - Added dual core support with -haf +*/ +#include +#include +#include +#include +#include +#include + +#define MAXPAGES 9 +#define ARRAYSIZE 14844 +#define IPPD 3600 +#define GAMMA 2.5 + +#ifndef PI +#define PI 3.141592653589793 +#endif + +#ifndef TWOPI +#define TWOPI 6.283185307179586 +#endif + +#ifndef HALFPI +#define HALFPI 1.570796326794896 +#endif + +#define DEG2RAD 1.74532925199e-02 +#define EARTHRADIUS 20902230.97 +#define METERS_PER_MILE 1609.344 +#define METERS_PER_FOOT 0.3048 +#define KM_PER_MILE 1.609344 +#define FOUR_THIRDS 1.3333333333333 + +char string[255], sdf_path[255], udt_file[255], opened = 0, gpsav = + 0, ss_name[16], dashes[80]; + +double earthradius, max_range = 0.0, forced_erp, dpp, ppd, + fzone_clearance = 0.6, forced_freq, clutter, lat, lon, txh, tercon, terdic, + north, east, south, west, dBm, loss, field_strength; + +int min_north = 90, max_north = -90, min_west = 360, max_west = -1, ippd, mpi, + max_elevation = -32768, min_elevation = 32768, bzerror, contour_threshold, + pred, pblue, pgreen, ter, multiplier = 256, debug = 0, loops = 64, jgets = + 0, MAXRAD, hottest = 10; + +unsigned char got_elevation_pattern, got_azimuth_pattern, metric = 0, dbm = 0; + +struct site { + double lat; + double lon; + float alt; + char name[50]; + char filename[255]; +} site; + +struct path { + double lat[ARRAYSIZE]; + double lon[ARRAYSIZE]; + double elevation[ARRAYSIZE]; + double 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[IPPD][IPPD]; + unsigned char mask[IPPD][IPPD]; + unsigned char signal[IPPD][IPPD]; +} dem[MAXPAGES]; + +struct LR { + double eps_dielect; + double sgm_conductivity; + double eno_ns_surfref; + double frq_mhz; + double conf; + double rel; + double erp; + int radio_climate; + int pol; + float antenna_pattern[361][1001]; +} LR; + +struct region { + unsigned char color[128][3]; + int level[128]; + int levels; +} region; + +double elev[ARRAYSIZE + 10]; + +struct site tx_site[2]; + +//ITWOM +void point_to_point(double elev[], double tht_m, double rht_m, + double eps_dielect, double sgm_conductivity, + double eno_ns_surfref, double frq_mhz, int radio_climate, + int pol, double conf, double rel, double &dbloss, + char *strmode, int &errnum); +//ITM +void point_to_point_ITM(double elev[], double tht_m, double rht_m, + double eps_dielect, double sgm_conductivity, + double eno_ns_surfref, double frq_mhz, + int radio_climate, int pol, double conf, double rel, + double &dbloss, char *strmode, int &errnum); + +double HataLinkdB(float f, float h_B, float h_M, float d, int mode); + +double CostHataLinkdB(float f, float h_B, float h_M, float d); + +double FsplLinkdB(float f, float d); + +double ked(double freq, double elev[], double rxh, double dkm); + +double arccos(double x, double y) +{ + /* This function implements the arc cosine function, + returning a value between 0 and TWOPI. */ + + double result = 0.0; + + if (y > 0.0) + result = acos(x / y); + + if (y < 0.0) + result = PI + acos(x / 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); +} + +double LonDiff(double lon1, double 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. */ + + double diff; + + diff = lon1 - lon2; + + if (diff <= -180.0) + diff += 360.0; + + if (diff >= 180.0) + diff -= 360.0; + + return diff; +} + +char *dec2dms(double decimal) +{ + /* Converts decimal degrees to degrees, minutes, seconds, + (DMS) and returns the result as a character string. */ + + char sign; + int degrees, minutes, seconds; + double a, b, c, d; + + if (decimal < 0.0) { + decimal = -decimal; + sign = -1; + } + + else + sign = 1; + + a = floor(decimal); + b = 60.0 * (decimal - a); + c = floor(b); + d = 60.0 * (b - c); + + degrees = (int)a; + minutes = (int)c; + seconds = (int)d; + + if (seconds < 0) + seconds = 0; + + if (seconds > 59) + seconds = 59; + + string[0] = 0; + snprintf(string, 250, "%d%c %d\' %d\"", degrees * sign, 176, minutes, + seconds); + return (string); +} + +int PutMask(double lat, double lon, int value) +{ + /* Lines, text, markings, and coverage areas are stored in a + mask that is combined with topology data when topographic + maps are generated by ss. This function sets and resets + bits in the mask based on the latitude and longitude of the + area pointed to. */ + + int x, y, indx; + char found; + + for (indx = 0, found = 0; indx < MAXPAGES && found == 0;) { + x = (int)rint(ppd * (lat - dem[indx].min_north)); + y = mpi - (int)rint(ppd * (LonDiff(dem[indx].max_west, lon))); + + if (x >= 0 && x <= mpi && y >= 0 && y <= mpi) + found = 1; + else + indx++; + } + + if (found) { + dem[indx].mask[x][y] = value; + return ((int)dem[indx].mask[x][y]); + } + + else + return -1; +} + +int OrMask(double lat, double lon, int value) +{ + /* Lines, text, markings, and coverage areas are stored in a + mask that is combined with topology data when topographic + maps are generated by ss. This function sets bits in + the mask based on the latitude and longitude of the area + pointed to. */ + + int x, y, indx; + char found; + + for (indx = 0, found = 0; indx < MAXPAGES && found == 0;) { + x = (int)rint(ppd * (lat - dem[indx].min_north)); + y = mpi - (int)rint(ppd * (LonDiff(dem[indx].max_west, lon))); + + if (x >= 0 && x <= mpi && y >= 0 && y <= mpi) + found = 1; + else + indx++; + } + + if (found) { + dem[indx].mask[x][y] |= value; + return ((int)dem[indx].mask[x][y]); + } + + else + return -1; +} + +int GetMask(double lat, double lon) +{ + /* This function returns the mask bits based on the latitude + and longitude given. */ + + return (OrMask(lat, lon, 0)); +} + +int PutSignal(double lat, double lon, unsigned char signal) +{ + /* This function writes a signal level (0-255) + at the specified location for later recall. */ + char dotfile[255]; + FILE *fd = NULL; + snprintf(dotfile, 80, "%s.dot%c", tx_site[0].filename, 0); + + int x, y, indx; + char found; + + if (signal > hottest) + hottest = signal; + + //lookup x/y for this co-ord + for (indx = 0, found = 0; indx < MAXPAGES && found == 0;) { + x = (int)rint(ppd * (lat - dem[indx].min_north)); + y = mpi - (int)rint(ppd * (LonDiff(dem[indx].max_west, lon))); + + if (x >= 0 && x <= mpi && y >= 0 && y <= mpi) + found = 1; + else + indx++; + } + + if (found) { + // Write values to file + dem[indx].signal[x][y] = signal; + + return (dem[indx].signal[x][y]); + } + + else + return 0; +} + +unsigned char GetSignal(double lat, double lon) +{ + /* This function reads the signal level (0-255) at the + specified location that was previously written by the + complimentary PutSignal() function. */ + + int x, y, indx; + char found; + + for (indx = 0, found = 0; indx < MAXPAGES && found == 0;) { + x = (int)rint(ppd * (lat - dem[indx].min_north)); + y = mpi - (int)rint(ppd * (LonDiff(dem[indx].max_west, lon))); + + if (x >= 0 && x <= mpi && y >= 0 && y <= mpi) + found = 1; + else + indx++; + } + + if (found) + return (dem[indx].signal[x][y]); + else + return 0; +} + +double GetElevation(struct site location) +{ + /* This function returns the elevation (in feet) of any location + represented by the digital elevation model data in memory. + Function returns -5000.0 for locations not found in memory. */ + + char found; + int x, y, indx; + double elevation; + + for (indx = 0, found = 0; indx < MAXPAGES && found == 0;) { + x = (int)rint(ppd * (location.lat - dem[indx].min_north)); + y = mpi - + (int)rint(ppd * + (LonDiff(dem[indx].max_west, location.lon))); + + if (x >= 0 && x <= mpi && y >= 0 && y <= mpi) + found = 1; + else + indx++; + } + + if (found) + elevation = 3.28084 * dem[indx].data[x][y]; + else + elevation = -5000.0; + + return elevation; +} + +int AddElevation(double lat, double lon, double height) +{ + /* This function adds a user-defined terrain feature + (in meters AGL) to the digital elevation model data + in memory. Does nothing and returns 0 for locations + not found in memory. */ + + char found; + int x, y, indx; + + for (indx = 0, found = 0; indx < MAXPAGES && found == 0;) { + x = (int)rint(ppd * (lat - dem[indx].min_north)); + y = mpi - (int)rint(ppd * (LonDiff(dem[indx].max_west, lon))); + + if (x >= 0 && x <= mpi && y >= 0 && y <= mpi) + found = 1; + else + indx++; + } + + if (found) + dem[indx].data[x][y] += (short)rint(height); + + return found; +} + +double Distance(struct site site1, struct site site2) +{ + /* This function returns the great circle distance + in miles between any two site locations. */ + + double lat1, lon1, lat2, lon2, distance; + + lat1 = site1.lat * DEG2RAD; + lon1 = site1.lon * DEG2RAD; + lat2 = site2.lat * DEG2RAD; + lon2 = site2.lon * DEG2RAD; + + distance = + 3959.0 * acos(sin(lat1) * sin(lat2) + + cos(lat1) * cos(lat2) * cos((lon1) - (lon2))); + + return distance; +} + +double Azimuth(struct site source, struct site destination) +{ + /* This function returns the azimuth (in degrees) to the + destination as seen from the location of the source. */ + + double dest_lat, dest_lon, src_lat, src_lon, + beta, azimuth, diff, num, den, fraction; + + dest_lat = destination.lat * DEG2RAD; + dest_lon = destination.lon * DEG2RAD; + + src_lat = source.lat * DEG2RAD; + src_lon = source.lon * DEG2RAD; + + /* Calculate Surface Distance */ + + beta = + acos(sin(src_lat) * sin(dest_lat) + + cos(src_lat) * cos(dest_lat) * cos(src_lon - dest_lon)); + + /* Calculate Azimuth */ + + num = sin(dest_lat) - (sin(src_lat) * cos(beta)); + den = cos(src_lat) * sin(beta); + fraction = num / den; + + /* Trap potential problems in acos() due to rounding */ + + if (fraction >= 1.0) + fraction = 1.0; + + if (fraction <= -1.0) + fraction = -1.0; + + /* Calculate azimuth */ + + azimuth = acos(fraction); + + /* Reference it to True North */ + + diff = dest_lon - src_lon; + + if (diff <= -PI) + diff += TWOPI; + + if (diff >= PI) + diff -= TWOPI; + + if (diff > 0.0) + azimuth = TWOPI - azimuth; + + return (azimuth / DEG2RAD); +} + +double ElevationAngle(struct site source, struct site destination) +{ + /* This function returns the angle of elevation (in degrees) + of the destination as seen from the source location. + A positive result represents an angle of elevation (uptilt), + while a negative result represents an angle of depression + (downtilt), as referenced to a normal to the center of + the earth. */ + + register double a, b, dx; + + a = GetElevation(destination) + destination.alt + earthradius; + b = GetElevation(source) + source.alt + earthradius; + + dx = 5280.0 * Distance(source, destination); + + /* Apply the Law of Cosines */ + + return ((180.0 * + (acos(((b * b) + (dx * dx) - (a * a)) / (2.0 * b * dx))) / + PI) - 90.0); +} + +void ReadPath(struct site source, struct site destination) +{ + /* This function generates a sequence of latitude and + longitude positions between source and destination + locations along a great circle path, and stores + elevation and distance information for points + along that path in the "path" structure. */ + + int c; + double azimuth, distance, lat1, lon1, beta, den, num, + lat2, lon2, total_distance, dx, dy, path_length, + miles_per_sample, samples_per_radian = 68755.0; + struct site tempsite; + + lat1 = source.lat * DEG2RAD; + lon1 = source.lon * DEG2RAD; + + lat2 = destination.lat * DEG2RAD; + lon2 = destination.lon * DEG2RAD; + + samples_per_radian = ppd * 57.295833; + + azimuth = Azimuth(source, destination) * DEG2RAD; + + total_distance = Distance(source, destination); + + if (total_distance > (30.0 / ppd)) { + dx = samples_per_radian * acos(cos(lon1 - lon2)); + dy = samples_per_radian * acos(cos(lat1 - lat2)); + + path_length = sqrt((dx * dx) + (dy * dy)); + + miles_per_sample = total_distance / path_length; + } + + else { + c = 0; + dx = 0.0; + dy = 0.0; + path_length = 0.0; + miles_per_sample = 0.0; + total_distance = 0.0; + + lat1 = lat1 / DEG2RAD; + lon1 = lon1 / DEG2RAD; + + path.lat[c] = lat1; + path.lon[c] = lon1; + path.elevation[c] = GetElevation(source); + path.distance[c] = 0.0; + } + + for (distance = 0.0, c = 0; + (total_distance != 0.0 && distance <= total_distance + && c < ARRAYSIZE); c++, distance = miles_per_sample * (double)c) { + beta = distance / 3959.0; + lat2 = + asin(sin(lat1) * cos(beta) + + cos(azimuth) * sin(beta) * cos(lat1)); + num = cos(beta) - (sin(lat1) * sin(lat2)); + den = cos(lat1) * cos(lat2); + + if (azimuth == 0.0 && (beta > HALFPI - lat1)) + lon2 = lon1 + PI; + + else if (azimuth == HALFPI && (beta > HALFPI + lat1)) + lon2 = lon1 + PI; + + else if (fabs(num / den) > 1.0) + lon2 = lon1; + + else { + if ((PI - azimuth) >= 0.0) + lon2 = lon1 - arccos(num, den); + else + lon2 = lon1 + arccos(num, den); + } + + while (lon2 < 0.0) + lon2 += TWOPI; + + while (lon2 > TWOPI) + lon2 -= TWOPI; + + lat2 = lat2 / DEG2RAD; + lon2 = lon2 / DEG2RAD; + + path.lat[c] = lat2; + path.lon[c] = lon2; + tempsite.lat = lat2; + tempsite.lon = lon2; + path.elevation[c] = GetElevation(tempsite); + path.distance[c] = distance; + } + + /* Make sure exact destination point is recorded at path.length-1 */ + + if (c < ARRAYSIZE) { + 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.length = c; + else + path.length = ARRAYSIZE - 1; +} + +double ElevationAngle2(struct site source, struct site destination, double er) +{ + /* This function returns the angle of elevation (in degrees) + of the destination as seen from the source location, UNLESS + the path between the sites is obstructed, in which case, the + elevation angle to the first obstruction is returned instead. + "er" represents the earth radius. */ + + int x; + char block = 0; + double source_alt, destination_alt, cos_xmtr_angle, + cos_test_angle, test_alt, elevation, distance, + source_alt2, first_obstruction_angle = 0.0; + struct path temp; + + temp = path; + + ReadPath(source, destination); + + distance = 5280.0 * Distance(source, destination); + source_alt = er + source.alt + GetElevation(source); + destination_alt = er + destination.alt + GetElevation(destination); + source_alt2 = source_alt * source_alt; + + /* Calculate the cosine of the elevation angle of the + destination (receiver) as seen by the source (transmitter). */ + + cos_xmtr_angle = + ((source_alt2) + (distance * distance) - + (destination_alt * destination_alt)) / (2.0 * source_alt * + distance); + + /* Test all points in between source and destination locations to + see if the angle to a topographic feature generates a higher + elevation angle than that produced by the destination. Begin + at the source since we're interested in identifying the FIRST + obstruction along the path between source and destination. */ + + for (x = 2, block = 0; x < path.length && block == 0; x++) { + distance = 5280.0 * path.distance[x]; + + test_alt = + earthradius + (path.elevation[x] == + 0.0 ? path.elevation[x] : path.elevation[x] + + clutter); + + cos_test_angle = + ((source_alt2) + (distance * distance) - + (test_alt * test_alt)) / (2.0 * source_alt * distance); + + /* Compare these two angles to determine if + an obstruction exists. Since we're comparing + the cosines of these angles rather than + the angles themselves, the sense of the + following "if" statement is reversed from + what it would be if the angles themselves + were compared. */ + + if (cos_xmtr_angle >= cos_test_angle) { + block = 1; + first_obstruction_angle = + ((acos(cos_test_angle)) / DEG2RAD) - 90.0; + } + } + + if (block) + elevation = first_obstruction_angle; + + else + elevation = ((acos(cos_xmtr_angle)) / DEG2RAD) - 90.0; + + path = temp; + + return elevation; +} + +double AverageTerrain(struct site source, double azimuthx, + double start_distance, double end_distance) +{ + /* This function returns the average terrain calculated in + the direction of "azimuth" (degrees) between "start_distance" + and "end_distance" (miles) from the source location. If + the terrain is all water (non-critical error), -5000.0 is + returned. If not enough SDF data has been loaded into + memory to complete the survey (critical error), then + -9999.0 is returned. */ + + int c, samples, endpoint; + double beta, lat1, lon1, lat2, lon2, num, den, azimuth, terrain = 0.0; + struct site destination; + + lat1 = source.lat * DEG2RAD; + lon1 = source.lon * DEG2RAD; + + /* Generate a path of elevations between the source + location and the remote location provided. */ + + beta = end_distance / 3959.0; + + azimuth = DEG2RAD * azimuthx; + + lat2 = + asin(sin(lat1) * cos(beta) + cos(azimuth) * sin(beta) * cos(lat1)); + num = cos(beta) - (sin(lat1) * sin(lat2)); + den = cos(lat1) * cos(lat2); + + if (azimuth == 0.0 && (beta > HALFPI - lat1)) + lon2 = lon1 + PI; + + else if (azimuth == HALFPI && (beta > HALFPI + lat1)) + lon2 = lon1 + PI; + + else if (fabs(num / den) > 1.0) + lon2 = lon1; + + else { + if ((PI - azimuth) >= 0.0) + lon2 = lon1 - arccos(num, den); + else + lon2 = lon1 + arccos(num, den); + } + + while (lon2 < 0.0) + lon2 += TWOPI; + + while (lon2 > TWOPI) + lon2 -= TWOPI; + + lat2 = lat2 / DEG2RAD; + lon2 = lon2 / DEG2RAD; + + destination.lat = lat2; + destination.lon = lon2; + + /* If SDF data is missing for the endpoint of + the radial, then the average terrain cannot + be accurately calculated. Return -9999.0 */ + + if (GetElevation(destination) < -4999.0) + return (-9999.0); + else { + ReadPath(source, destination); + + endpoint = path.length; + + /* Shrink the length of the radial if the + outermost portion is not over U.S. land. */ + + for (c = endpoint - 1; c >= 0 && path.elevation[c] == 0.0; + c--) ; + + endpoint = c + 1; + + for (c = 0, samples = 0; c < endpoint; c++) { + if (path.distance[c] >= start_distance) { + terrain += + (path.elevation[c] == + 0.0 ? path.elevation[c] : path. + elevation[c] + clutter); + samples++; + } + } + + if (samples == 0) + terrain = -5000.0; /* No land */ + else + terrain = (terrain / (double)samples); + + return terrain; + } +} + +double haat(struct site antenna) +{ + /* This function returns the antenna's Height Above Average + Terrain (HAAT) based on FCC Part 73.313(d). If a critical + error occurs, such as a lack of SDF data to complete the + survey, -5000.0 is returned. */ + + int azi, c; + char error = 0; + double terrain, avg_terrain, haat, sum = 0.0; + + /* Calculate the average terrain between 2 and 10 miles + from the antenna site at azimuths of 0, 45, 90, 135, + 180, 225, 270, and 315 degrees. */ + + for (c = 0, azi = 0; azi <= 315 && error == 0; azi += 45) { + terrain = AverageTerrain(antenna, (double)azi, 2.0, 10.0); + + if (terrain < -9998.0) /* SDF data is missing */ + error = 1; + + if (terrain > -4999.0) { /* It's land, not water */ + sum += terrain; /* Sum of averages */ + c++; + } + } + + if (error) + return -5000.0; + else { + avg_terrain = (sum / (double)c); + haat = (antenna.alt + GetElevation(antenna)) - avg_terrain; + return haat; + } +} + +double ReadBearing(char *input) +{ + /* This function takes numeric input in the form of a character + string, and returns an equivalent bearing in degrees as a + decimal number (double). The input may either be expressed + in decimal format (40.139722) or degree, minute, second + format (40 08 23). This function also safely handles + extra spaces found either leading, trailing, or + embedded within the numbers expressed in the + input string. Decimal seconds are permitted. */ + + double seconds, bearing = 0.0; + char string[20]; + int a, b, length, degrees, minutes; + + /* Copy "input" to "string", and ignore any extra + spaces that might be present in the process. */ + + string[0] = 0; + length = strlen(input); + + for (a = 0, b = 0; a < length && a < 18; a++) { + if ((input[a] != 32 && input[a] != '\n') + || (input[a] == 32 && input[a + 1] != 32 + && input[a + 1] != '\n' && b != 0)) { + string[b] = input[a]; + b++; + } + } + + string[b] = 0; + + /* Count number of spaces in the clean string. */ + + length = strlen(string); + + for (a = 0, b = 0; a < length; a++) + if (string[a] == 32) + b++; + + if (b == 0) /* Decimal Format (40.139722) */ + sscanf(string, "%lf", &bearing); + + if (b == 2) { /* Degree, Minute, Second Format (40 08 23.xx) */ + sscanf(string, "%d %d %lf", °rees, &minutes, &seconds); + + bearing = fabs((double)degrees); + bearing += fabs(((double)minutes) / 60.0); + bearing += fabs(seconds / 3600.0); + + if ((degrees < 0) || (minutes < 0) || (seconds < 0.0)) + bearing = -bearing; + } + + /* Anything else returns a 0.0 */ + + if (bearing > 360.0 || bearing < -360.0) + bearing = 0.0; + + return bearing; +} + +void LoadPAT(char *filename) +{ + /* This function reads and processes antenna pattern (.az + and .el) files that correspond in name to previously + loaded ss .lrp files. */ + + int a, b, w, x, y, z, last_index, next_index, span; + char string[255], azfile[255], elfile[255], *pointer = NULL, *s = NULL; + float az, xx, elevation, amplitude, rotation, valid1, valid2, + delta, azimuth[361], azimuth_pattern[361], el_pattern[10001], + elevation_pattern[361][1001], slant_angle[361], tilt, + mechanical_tilt = 0.0, tilt_azimuth, tilt_increment, sum; + FILE *fd = NULL; + unsigned char read_count[10001]; + + for (x = 0; filename[x] != '.' && filename[x] != 0 && x < 250; x++) { + azfile[x] = filename[x]; + elfile[x] = filename[x]; + } + + azfile[x] = '.'; + azfile[x + 1] = 'a'; + azfile[x + 2] = 'z'; + azfile[x + 3] = 0; + + elfile[x] = '.'; + elfile[x + 1] = 'e'; + elfile[x + 2] = 'l'; + elfile[x + 3] = 0; + + rotation = 0.0; + + got_azimuth_pattern = 0; + got_elevation_pattern = 0; + + /* Load .az antenna pattern file */ + + fd = fopen(azfile, "r"); + + if (fd != NULL) { + /* Clear azimuth pattern array */ + + for (x = 0; x <= 360; x++) { + azimuth[x] = 0.0; + read_count[x] = 0; + } + + /* Read azimuth pattern rotation + in degrees measured clockwise + from true North. */ + + s = fgets(string, 254, fd); + pointer = strchr(string, ';'); + + if (pointer != NULL) + *pointer = 0; + + sscanf(string, "%f", &rotation); + + /* Read azimuth (degrees) and corresponding + normalized field radiation pattern amplitude + (0.0 to 1.0) until EOF is reached. */ + + s = fgets(string, 254, fd); + pointer = strchr(string, ';'); + + if (pointer != NULL) + *pointer = 0; + + sscanf(string, "%f %f", &az, &litude); + + do { + x = (int)rintf(az); + + if (x >= 0 && x <= 360 && fd != NULL) { + azimuth[x] += amplitude; + read_count[x]++; + } + + s = fgets(string, 254, fd); + pointer = strchr(string, ';'); + + if (pointer != NULL) + *pointer = 0; + + sscanf(string, "%f %f", &az, &litude); + + } while (feof(fd) == 0); + + fclose(fd); + + /* Handle 0=360 degree ambiguity */ + + if ((read_count[0] == 0) && (read_count[360] != 0)) { + read_count[0] = read_count[360]; + azimuth[0] = azimuth[360]; + } + + if ((read_count[0] != 0) && (read_count[360] == 0)) { + read_count[360] = read_count[0]; + azimuth[360] = azimuth[0]; + } + + /* Average pattern values in case more than + one was read for each degree of azimuth. */ + + for (x = 0; x <= 360; x++) { + if (read_count[x] > 1) + azimuth[x] /= (float)read_count[x]; + } + + /* Interpolate missing azimuths + to completely fill the array */ + + last_index = -1; + next_index = -1; + + for (x = 0; x <= 360; x++) { + if (read_count[x] != 0) { + if (last_index == -1) + last_index = x; + else + next_index = x; + } + + if (last_index != -1 && next_index != -1) { + valid1 = azimuth[last_index]; + valid2 = azimuth[next_index]; + + span = next_index - last_index; + delta = (valid2 - valid1) / (float)span; + + for (y = last_index + 1; y < next_index; y++) + azimuth[y] = azimuth[y - 1] + delta; + + last_index = y; + next_index = -1; + } + } + + /* Perform azimuth pattern rotation + and load azimuth_pattern[361] with + azimuth pattern data in its final form. */ + + for (x = 0; x < 360; x++) { + y = x + (int)rintf(rotation); + + if (y >= 360) + y -= 360; + + azimuth_pattern[y] = azimuth[x]; + } + + azimuth_pattern[360] = azimuth_pattern[0]; + + got_azimuth_pattern = 255; + } + + /* Read and process .el file */ + + fd = fopen(elfile, "r"); + + if (fd != NULL) { + for (x = 0; x <= 10000; x++) { + el_pattern[x] = 0.0; + read_count[x] = 0; + } + + /* Read mechanical tilt (degrees) and + tilt azimuth in degrees measured + clockwise from true North. */ + + s = fgets(string, 254, fd); + pointer = strchr(string, ';'); + + if (pointer != NULL) + *pointer = 0; + + sscanf(string, "%f %f", &mechanical_tilt, &tilt_azimuth); + + /* Read elevation (degrees) and corresponding + normalized field radiation pattern amplitude + (0.0 to 1.0) until EOF is reached. */ + + s = fgets(string, 254, fd); + pointer = strchr(string, ';'); + + if (pointer != NULL) + *pointer = 0; + + sscanf(string, "%f %f", &elevation, &litude); + + while (feof(fd) == 0) { + /* Read in normalized radiated field values + for every 0.01 degrees of elevation between + -10.0 and +90.0 degrees */ + + x = (int)rintf(100.0 * (elevation + 10.0)); + + if (x >= 0 && x <= 10000) { + el_pattern[x] += amplitude; + read_count[x]++; + } + + s = fgets(string, 254, fd); + pointer = strchr(string, ';'); + + if (pointer != NULL) + *pointer = 0; + + sscanf(string, "%f %f", &elevation, &litude); + } + + fclose(fd); + + /* Average the field values in case more than + one was read for each 0.01 degrees of elevation. */ + + for (x = 0; x <= 10000; x++) { + if (read_count[x] > 1) + el_pattern[x] /= (float)read_count[x]; + } + + /* Interpolate between missing elevations (if + any) to completely fill the array and provide + radiated field values for every 0.01 degrees of + elevation. */ + + last_index = -1; + next_index = -1; + + for (x = 0; x <= 10000; x++) { + if (read_count[x] != 0) { + if (last_index == -1) + last_index = x; + else + next_index = x; + } + + if (last_index != -1 && next_index != -1) { + valid1 = el_pattern[last_index]; + valid2 = el_pattern[next_index]; + + span = next_index - last_index; + delta = (valid2 - valid1) / (float)span; + + for (y = last_index + 1; y < next_index; y++) + el_pattern[y] = + el_pattern[y - 1] + delta; + + last_index = y; + next_index = -1; + } + } + + /* Fill slant_angle[] array with offset angles based + on the antenna's mechanical beam tilt (if any) + and tilt direction (azimuth). */ + + if (mechanical_tilt == 0.0) { + for (x = 0; x <= 360; x++) + slant_angle[x] = 0.0; + } + + else { + tilt_increment = mechanical_tilt / 90.0; + + for (x = 0; x <= 360; x++) { + xx = (float)x; + y = (int)rintf(tilt_azimuth + xx); + + while (y >= 360) + y -= 360; + + while (y < 0) + y += 360; + + if (x <= 180) + slant_angle[y] = + -(tilt_increment * (90.0 - xx)); + + if (x > 180) + slant_angle[y] = + -(tilt_increment * (xx - 270.0)); + } + } + + slant_angle[360] = slant_angle[0]; /* 360 degree wrap-around */ + + for (w = 0; w <= 360; w++) { + tilt = slant_angle[w]; + + /** Convert tilt angle to + an array index offset **/ + + y = (int)rintf(100.0 * tilt); + + /* Copy shifted el_pattern[10001] field + values into elevation_pattern[361][1001] + at the corresponding azimuth, downsampling + (averaging) along the way in chunks of 10. */ + + for (x = y, z = 0; z <= 1000; x += 10, z++) { + for (sum = 0.0, a = 0; a < 10; a++) { + b = a + x; + + if (b >= 0 && b <= 10000) + sum += el_pattern[b]; + if (b < 0) + sum += el_pattern[0]; + if (b > 10000) + sum += el_pattern[10000]; + } + + elevation_pattern[w][z] = sum / 10.0; + } + } + + got_elevation_pattern = 255; + } + + for (x = 0; x <= 360; x++) { + for (y = 0; y <= 1000; y++) { + if (got_elevation_pattern) + elevation = elevation_pattern[x][y]; + else + elevation = 1.0; + + if (got_azimuth_pattern) + az = azimuth_pattern[x]; + else + az = 1.0; + + LR.antenna_pattern[x][y] = az * elevation; + } + } +} + +int LoadSDF_SDF(char *name, int winfiles) +{ + /* This function reads uncompressed ss Data Files (.sdf) + containing digital elevation model data into memory. + Elevation data, maximum and minimum elevations, and + quadrangle limits are stored in the first available + dem[] structure. */ + + int x, y, data, indx, minlat, minlon, maxlat, maxlon, j; + char found, free_page = 0, line[20], jline[20], sdf_file[255], + path_plus_name[255], *s = NULL, *junk = NULL; + + FILE *fd; + + for (x = 0; name[x] != '.' && name[x] != 0 && x < 250; x++) + sdf_file[x] = name[x]; + + sdf_file[x] = 0; + + /* Parse filename for minimum latitude and longitude values */ + if (winfiles == 1) { + sscanf(sdf_file, "%d=%d=%d=%d", &minlat, &maxlat, &minlon, + &maxlon); + } else { + sscanf(sdf_file, "%d:%d:%d:%d", &minlat, &maxlat, &minlon, + &maxlon); + } + + sdf_file[x] = '.'; + sdf_file[x + 1] = 's'; + sdf_file[x + 2] = 'd'; + sdf_file[x + 3] = 'f'; + sdf_file[x + 4] = 0; + + /* Is it already in memory? */ + + for (indx = 0, found = 0; indx < MAXPAGES && found == 0; indx++) { + 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_page = 0; indx < MAXPAGES && free_page == 0; + indx++) + if (dem[indx].max_north == -90) + free_page = 1; + } + + indx--; + + if (free_page && found == 0 && indx >= 0 && indx < MAXPAGES) { + /* 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/.ss_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) { + if (debug == 1) { + fprintf(stdout, + "Loading \"%s\" into page %d...", + path_plus_name, indx + 1); + fflush(stdout); + } + + s = fgets(line, 19, fd); + sscanf(line, "%d", &dem[indx].max_west); + + s = fgets(line, 19, fd); + sscanf(line, "%d", &dem[indx].min_north); + + s = fgets(line, 19, fd); + sscanf(line, "%d", &dem[indx].min_west); + + s = fgets(line, 19, fd); + sscanf(line, "%d", &dem[indx].max_north); + + /* + Here X lines of DEM will be read until IPPD is reached. + Each .sdf tile contains 1200x1200 = 1.44M 'points' + Each point is sampled for 1200 resolution! + + */ + for (x = 0; x < ippd; x++) { + for (y = 0; y < ippd; y++) { + + for (j = 0; j < jgets; j++) { + junk = fgets(jline, 19, fd); + } + + s = fgets(line, 19, fd); + + data = atoi(line); + + dem[indx].data[x][y] = data; + dem[indx].signal[x][y] = 0; + dem[indx].mask[x][y] = 0; + + if (data > dem[indx].max_el) + dem[indx].max_el = data; + + if (data < dem[indx].min_el) + dem[indx].min_el = data; + + } + + if (ippd == 600) { + for (j = 0; j < IPPD; j++) { + junk = fgets(jline, 19, fd); + } + } + if (ippd == 300) { + for (j = 0; j < IPPD; j++) { + junk = fgets(jline, 19, fd); + junk = fgets(jline, 19, fd); + junk = fgets(jline, 19, fd); + + } + } + + } + + fclose(fd); + + 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 (max_north == -90) + max_north = dem[indx].max_north; + + else if (dem[indx].max_north > max_north) + max_north = dem[indx].max_north; + + 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 (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 == 360) + min_west = dem[indx].min_west; + + else { + if (fabs(dem[indx].min_west - min_west) < 180.0) { + 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; + } + } + + return 1; + } + + else + return -1; + } + + else + return 0; +} + +char LoadSDF(char *name, int winfiles) +{ + /* This function loads the requested SDF file from the filesystem. + It first tries to invoke the LoadSDF_SDF() function to load an + uncompressed SDF file (since uncompressed files load slightly + faster). If that attempt fails, then it tries to load a + compressed SDF file by invoking the LoadSDF_BZ() function. + If that fails, then we can assume that no elevation data + exists for the region requested, and that the region + requested must be entirely over water. */ + + int x, y, indx, minlat, minlon, maxlat, maxlon; + char found, free_page = 0; + int return_value = -1; + + return_value = LoadSDF_SDF(name, winfiles); + + /* If neither format can be found, then assume the area is water. */ + + if (return_value == 0 || return_value == -1) { + + if (winfiles == 1) { + sscanf(name, "%d=%d=%d=%d", &minlat, &maxlat, &minlon, + &maxlon); + } else { + sscanf(name, "%d:%d:%d:%d", &minlat, &maxlat, &minlon, + &maxlon); + } + /* Is it already in memory? */ + + for (indx = 0, found = 0; indx < MAXPAGES && found == 0; indx++) { + 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_page = 0; + indx < MAXPAGES && free_page == 0; indx++) + if (dem[indx].max_north == -90) + free_page = 1; + } + + indx--; + + if (free_page && found == 0 && indx >= 0 && indx < MAXPAGES) { + if (debug == 1) { + fprintf(stdout, + "Region \"%s\" assumed as sea-level into page %d...", + name, indx + 1); + fflush(stdout); + } + + dem[indx].max_west = maxlon; + dem[indx].min_north = minlat; + dem[indx].min_west = minlon; + dem[indx].max_north = maxlat; + + /* Fill DEM with sea-level topography */ + + for (x = 0; x < ippd; x++) + for (y = 0; y < ippd; y++) { + dem[indx].data[x][y] = 0; + dem[indx].signal[x][y] = 0; + dem[indx].mask[x][y] = 0; + + if (dem[indx].min_el > 0) + dem[indx].min_el = 0; + } + + 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 (max_north == -90) + max_north = dem[indx].max_north; + + else if (dem[indx].max_north > max_north) + max_north = dem[indx].max_north; + + 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 (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 == 360) + min_west = dem[indx].min_west; + + else { + 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; + } + } + + return_value = 1; + } + } + + return return_value; +} + +void PlotLOSPath(struct site source, struct site destination, char mask_value, + FILE * fd) +{ + /* This function analyzes the path between the source and + destination locations. It determines which points along + the path have line-of-sight visibility to the source. + Points along with path having line-of-sight visibility + to the source at an AGL altitude equal to that of the + destination location are stored by setting bit 1 in the + mask[][] array, which are displayed in green when PPM + maps are later generated by ss. */ + + char block; + int x, y; + register double cos_xmtr_angle, cos_test_angle, test_alt; + double distance, rx_alt, tx_alt; + + ReadPath(source, destination); + + for (y = 0; y < path.length; y++) { + /* Test this point only if it hasn't been already + tested and found to be free of obstructions. */ + + if ((GetMask(path.lat[y], path.lon[y]) & mask_value) == 0) { + distance = 5280.0 * path.distance[y]; + tx_alt = earthradius + source.alt + path.elevation[0]; + rx_alt = + earthradius + destination.alt + path.elevation[y]; + + /* Calculate the cosine of the elevation of the + transmitter as seen at the temp rx point. */ + + cos_xmtr_angle = + ((rx_alt * rx_alt) + (distance * distance) - + (tx_alt * tx_alt)) / (2.0 * rx_alt * distance); + + for (x = y, block = 0; x >= 0 && block == 0; x--) { + distance = + 5280.0 * (path.distance[y] - + path.distance[x]); + test_alt = + earthradius + (path.elevation[x] == + 0.0 ? path. + elevation[x] : path. + elevation[x] + clutter); + + 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 + 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 + be if the actual angles were compared. */ + + if (cos_xmtr_angle >= cos_test_angle) + block = 1; + } + + if (block == 0) + OrMask(path.lat[y], path.lon[y], mask_value); + } + } +} + +void PlotPropPath(struct site source, struct site destination, + unsigned char mask_value, FILE * fd, int propmodel, + int knifeedge) +{ + + int x, y, ifs, ofs, errnum; + char block = 0, strmode[100]; + double loss, azimuth, pattern = 0.0, + xmtr_alt, dest_alt, xmtr_alt2, dest_alt2, + cos_rcvr_angle, cos_test_angle = 0.0, test_alt, + elevation = 0.0, distance = 0.0, radius = 0.0, four_thirds_earth, + field_strength = 0.0, rxp, dBm, txelev, dkm, diffloss; + struct site temp; + + radius = Distance(source, destination); + + ReadPath(source, destination); + + four_thirds_earth = FOUR_THIRDS * EARTHRADIUS; + + for (x = 1; x < path.length - 1; x++) + elev[x + 2] = + (path.elevation[x] == + 0.0 ? path.elevation[x] * METERS_PER_FOOT : (clutter + + path. + elevation[x]) + * METERS_PER_FOOT); + + /* Copy ending points without clutter */ + + elev[2] = path.elevation[0] * METERS_PER_FOOT; + txelev = elev[2] + (source.alt * METERS_PER_FOOT); + + elev[path.length + 1] = + path.elevation[path.length - 1] * METERS_PER_FOOT; + + /* Since the only energy the Longley-Rice model considers + reaching the destination is based on what is scattered + or deflected from the first obstruction along the path, + we first need to find the location and elevation angle + of that first obstruction (if it exists). This is done + using a 4/3rds Earth radius to match the model used by + Longley-Rice. This information is required for properly + integrating the antenna's elevation pattern into the + calculation for overall path loss. */ + + for (y = 2; (y < (path.length - 1) && path.distance[y] <= max_range); + y++) { + /* Process this point only if it + has not already been processed. */ + + if ((GetMask(path.lat[y], path.lon[y]) & 248) != + (mask_value << 3)) { + distance = 5280.0 * path.distance[y]; + xmtr_alt = + four_thirds_earth + source.alt + path.elevation[0]; + dest_alt = + four_thirds_earth + destination.alt + + path.elevation[y]; + dest_alt2 = dest_alt * dest_alt; + xmtr_alt2 = xmtr_alt * xmtr_alt; + + /* Calculate the cosine of the elevation of + the receiver as seen by the transmitter. */ + + cos_rcvr_angle = + ((xmtr_alt2) + (distance * distance) - + (dest_alt2)) / (2.0 * xmtr_alt * distance); + + if (cos_rcvr_angle > 1.0) + cos_rcvr_angle = 1.0; + + if (cos_rcvr_angle < -1.0) + cos_rcvr_angle = -1.0; + + if (got_elevation_pattern || fd != NULL) { + /* Determine the elevation angle to the first obstruction + along the path IF elevation pattern data is available + or an output (.ano) file has been designated. */ + + for (x = 2, block = 0; (x < y && block == 0); + x++) { + distance = 5280.0 * path.distance[x]; + + test_alt = + four_thirds_earth + + (path.elevation[x] == + 0.0 ? path.elevation[x] : path. + elevation[x] + clutter); + + /* Calculate the cosine of the elevation + angle of the terrain (test point) + as seen by the transmitter. */ + + cos_test_angle = + ((xmtr_alt2) + + (distance * distance) - + (test_alt * test_alt)) / (2.0 * + xmtr_alt + * + distance); + + if (cos_test_angle > 1.0) + cos_test_angle = 1.0; + + if (cos_test_angle < -1.0) + cos_test_angle = -1.0; + + /* Compare these two angles to determine if + an obstruction exists. Since we're comparing + the cosines of these angles rather than + the angles themselves, the sense of the + following "if" statement is reversed from + what it would be if the angles themselves + were compared. */ + + if (cos_rcvr_angle >= cos_test_angle) + block = 1; + } + + if (block) + elevation = + ((acos(cos_test_angle)) / DEG2RAD) - + 90.0; + else + elevation = + ((acos(cos_rcvr_angle)) / DEG2RAD) - + 90.0; + } + + /* Determine attenuation for each point along the + path using a prop model starting at y=2 (number_of_points = 1), the + shortest distance terrain can play a role in + path loss. */ + + elev[0] = y - 1; /* (number of points - 1) */ + + /* Distance between elevation samples */ + + elev[1] = + METERS_PER_MILE * (path.distance[y] - + path.distance[y - 1]); + + /* + elev[2]=path.elevation[0]*METERS_PER_FOOT; + elev[path.length+1]=path.elevation[path.length-1]*METERS_PER_FOOT; + */ + if (path.elevation[y] < 1) { + path.elevation[y] = 1; + } + + dkm = (elev[1] * elev[0]) / 1000; // km + + switch (propmodel) { + case 1: + // Longley Rice + point_to_point_ITM(elev, + source.alt * METERS_PER_FOOT, + destination.alt * + METERS_PER_FOOT, + LR.eps_dielect, + LR.sgm_conductivity, + LR.eno_ns_surfref, + LR.frq_mhz, LR.radio_climate, + LR.pol, LR.conf, LR.rel, + loss, strmode, errnum); + break; + case 3: + //HATA urban + loss = + HataLinkdB(LR.frq_mhz, txelev, + path.elevation[y] + + (destination.alt * + METERS_PER_FOOT), dkm, 1); + break; + case 4: + //HATA suburban + loss = + HataLinkdB(LR.frq_mhz, txelev, + path.elevation[y] + + (destination.alt * + METERS_PER_FOOT), dkm, 2); + break; + case 5: + //HATA open + loss = + HataLinkdB(LR.frq_mhz, txelev, + path.elevation[y] + + (destination.alt * + METERS_PER_FOOT), dkm, 3); + break; + case 6: + // COST231-HATA + loss = + CostHataLinkdB(LR.frq_mhz, txelev, + path.elevation[y] + + (destination.alt * + METERS_PER_FOOT), dkm); + break; + case 7: + // ITU-R P.525 Free space path loss + loss = FsplLinkdB(LR.frq_mhz, dkm); + //fprintf(stdout,"MHz: %1f KM: %1f = %1fdB",LR.frq_mhz,dkm,loss); + break; + case 8: + // ITWOM 3.0 + point_to_point(elev, + source.alt * METERS_PER_FOOT, + destination.alt * + METERS_PER_FOOT, LR.eps_dielect, + LR.sgm_conductivity, + LR.eno_ns_surfref, LR.frq_mhz, + LR.radio_climate, LR.pol, + LR.conf, LR.rel, loss, strmode, + errnum); + break; + + default: + point_to_point_ITM(elev, + source.alt * METERS_PER_FOOT, + destination.alt * + METERS_PER_FOOT, + LR.eps_dielect, + LR.sgm_conductivity, + LR.eno_ns_surfref, + LR.frq_mhz, LR.radio_climate, + LR.pol, LR.conf, LR.rel, + loss, strmode, errnum); + + } + + if (knifeedge == 1) { + diffloss = + ked(LR.frq_mhz, elev, + destination.alt * METERS_PER_FOOT, dkm); + loss += (diffloss); // ;) + } + + //Key stage. Link dB for p2p is returned as 'loss'. + + temp.lat = path.lat[y]; + temp.lon = path.lon[y]; + + azimuth = (Azimuth(source, temp)); + + if (fd != NULL) + fprintf(fd, "%.7f, %.7f, %.3f, %.3f, ", + path.lat[y], path.lon[y], azimuth, + elevation); + + /* If ERP==0, write path loss to alphanumeric + output file. Otherwise, write field strength + or received power level (below), as appropriate. */ + + if (fd != NULL && LR.erp == 0.0) + fprintf(fd, "%.2f", loss); + + /* Integrate the antenna's radiation + pattern into the overall path loss. */ + + x = (int)rint(10.0 * (10.0 - elevation)); + + if (x >= 0 && x <= 1000) { + azimuth = rint(azimuth); + + pattern = + (double)LR.antenna_pattern[(int)azimuth][x]; + + if (pattern != 0.0) { + pattern = 20.0 * log10(pattern); + loss -= pattern; + } + } + + if (LR.erp != 0.0) { + if (dbm) { + /* dBm is based on EIRP (ERP + 2.14) */ + + rxp = + LR.erp / + (pow(10.0, (loss - 2.14) / 10.0)); + + dBm = 10.0 * (log10(rxp * 1000.0)); + + if (fd != NULL) + fprintf(fd, "%.3f", dBm); + + /* Scale roughly between 0 and 255 */ + + ifs = 200 + (int)rint(dBm); + + if (ifs < 0) + ifs = 0; + + if (ifs > 255) + ifs = 255; + + ofs = + GetSignal(path.lat[y], path.lon[y]); + + if (ofs > ifs) + ifs = ofs; + + PutSignal(path.lat[y], path.lon[y], + (unsigned char)ifs); + + } + + else { + field_strength = + (139.4 + + (20.0 * log10(LR.frq_mhz)) - + loss) + + (10.0 * log10(LR.erp / 1000.0)); + + ifs = 100 + (int)rint(field_strength); + + if (ifs < 0) + ifs = 0; + + if (ifs > 255) + ifs = 255; + + ofs = + GetSignal(path.lat[y], path.lon[y]); + + if (ofs > ifs) + ifs = ofs; + + PutSignal(path.lat[y], path.lon[y], + (unsigned char)ifs); + + if (fd != NULL) + fprintf(fd, "%.3f", + field_strength); + } + } + + else { + if (loss > 255) + ifs = 255; + else + ifs = (int)rint(loss); + + ofs = GetSignal(path.lat[y], path.lon[y]); + + if (ofs < ifs && ofs != 0) + ifs = ofs; + + PutSignal(path.lat[y], path.lon[y], + (unsigned char)ifs); + } + + if (fd != NULL) { + if (block) + fprintf(fd, " *"); + + fprintf(fd, "\n"); + } + + /* Mark this point as having been analyzed */ + + PutMask(path.lat[y], path.lon[y], + (GetMask(path.lat[y], path.lon[y]) & 7) + + (mask_value << 3)); + } + } + +} + +void PlotLOSMap(struct site source, double altitude, char *plo_filename) +{ + /* This function performs a 360 degree sweep around the + transmitter site (source location), and plots the + line-of-sight coverage of the transmitter on the ss + generated topographic map based on a receiver located + at the specified altitude (in feet AGL). Results + are stored in memory, and written out in the form + of a topographic map when the WritePPM() function + is later invoked. */ + + int y, z; + struct site edge; + unsigned char x; + double lat, lon, minwest, maxnorth, th; + static unsigned char mask_value = 1; + FILE *fd = NULL; + + if (plo_filename[0] != 0) + fd = fopen(plo_filename, "wb"); + + if (fd != NULL) { + fprintf(fd, + "%d, %d\t; max_west, min_west\n%d, %d\t; max_north, min_north\n", + max_west, min_west, max_north, min_north); + } + + th = ppd / loops; + + z = (int)(th * ReduceAngle(max_west - min_west)); + + minwest = dpp + (double)min_west; + maxnorth = (double)max_north - dpp; + + for (lon = minwest, x = 0, y = 0; + (LonDiff(lon, (double)max_west) <= 0.0); + y++, lon = minwest + (dpp * (double)y)) { + if (lon >= 360.0) + lon -= 360.0; + + edge.lat = max_north; + edge.lon = lon; + edge.alt = altitude; + + PlotLOSPath(source, edge, mask_value, fd); + } + + z = (int)(th * (double)(max_north - min_north)); + + for (lat = maxnorth, x = 0, y = 0; lat >= (double)min_north; + y++, lat = maxnorth - (dpp * (double)y)) { + edge.lat = lat; + edge.lon = min_west; + edge.alt = altitude; + + PlotLOSPath(source, edge, mask_value, fd); + + } + + z = (int)(th * ReduceAngle(max_west - min_west)); + + for (lon = minwest, x = 0, y = 0; + (LonDiff(lon, (double)max_west) <= 0.0); + y++, lon = minwest + (dpp * (double)y)) { + if (lon >= 360.0) + lon -= 360.0; + + edge.lat = min_north; + edge.lon = lon; + edge.alt = altitude; + + PlotLOSPath(source, edge, mask_value, fd); + + } + + z = (int)(th * (double)(max_north - min_north)); + + for (lat = (double)min_north, x = 0, y = 0; lat < (double)max_north; + y++, lat = (double)min_north + (dpp * (double)y)) { + edge.lat = lat; + edge.lon = max_west; + edge.alt = altitude; + + PlotLOSPath(source, edge, mask_value, fd); + + } + + switch (mask_value) { + case 1: + mask_value = 8; + break; + + case 8: + mask_value = 16; + break; + + case 16: + mask_value = 32; + } +} + +void PlotPropagation(struct site source, double altitude, char *plo_filename, + int propmodel, int knifeedge, int haf) +{ + int y, z, count; + struct site edge; + double lat, lon, minwest, maxnorth, th; + unsigned char x; + static unsigned char mask_value = 1; + FILE *fd = NULL; + + minwest = dpp + (double)min_west; + maxnorth = (double)max_north - dpp; + + count = 0; + + if (LR.erp == 0.0 && debug) + fprintf(stdout, "path loss"); + else { + if (debug) { + if (dbm) + fprintf(stdout, "signal power level"); + else + fprintf(stdout, "field strength"); + } + } + if (debug) { + fprintf(stdout, + " contours of \"%s\"\nout to a radius of %.2f %s with Rx antenna(s) at %.2f %s AGL\n", + source.name, + metric ? max_range * KM_PER_MILE : max_range, + metric ? "kilometers" : "miles", + metric ? altitude * METERS_PER_FOOT : altitude, + metric ? "meters" : "feet"); + } + + if (clutter > 0.0 && debug) + fprintf(stdout, "\nand %.2f %s of ground clutter", + metric ? clutter * METERS_PER_FOOT : clutter, + metric ? "meters" : "feet"); + + if (debug) { + fprintf(stdout, "...\n\n 0%c to 25%c ", 37, 37); + fflush(stdout); + } + + if (plo_filename[0] != 0) + fd = fopen(plo_filename, "wb"); + + if (fd != NULL) { + fprintf(fd, + "%d, %d\t; max_west, min_west\n%d, %d\t; max_north, min_north\n", + max_west, min_west, max_north, min_north); + } + + th = ppd / loops; + + // Four sections start here + + //S1 + if (haf == 0 || haf == 1) { + z = (int)(th * ReduceAngle(max_west - min_west)); + + for (lon = minwest, x = 0, y = 0; + (LonDiff(lon, (double)max_west) <= 0.0); + y++, lon = minwest + (dpp * (double)y)) { + if (lon >= 360.0) + lon -= 360.0; + + edge.lat = max_north; + edge.lon = lon; + edge.alt = altitude; + + PlotPropPath(source, edge, mask_value, fd, propmodel, + knifeedge); + count++; + + if (count == z) { + count = 0; + + if (x == 3) + x = 0; + else + x++; + } + } + + } + //S2 + if (haf == 0 || haf == 1) { + count = 0; + if (debug) { + fprintf(stdout, "\n25%c to 50%c ", 37, 37); + fflush(stdout); + } + + z = (int)(th * (double)(max_north - min_north)); + + for (lat = maxnorth, x = 0, y = 0; lat >= (double)min_north; + y++, lat = maxnorth - (dpp * (double)y)) { + edge.lat = lat; + edge.lon = min_west; + edge.alt = altitude; + + PlotPropPath(source, edge, mask_value, fd, propmodel, + knifeedge); + count++; + + if (count == z) { + //fprintf(stdout,"%c",symbol[x]); + //fflush(stdout); + count = 0; + + if (x == 3) + x = 0; + else + x++; + } + } + + } + //S3 + if (haf == 0 || haf == 2) { + count = 0; + if (debug) { + fprintf(stdout, "\n50%c to 75%c ", 37, 37); + fflush(stdout); + } + + z = (int)(th * ReduceAngle(max_west - min_west)); + + for (lon = minwest, x = 0, y = 0; + (LonDiff(lon, (double)max_west) <= 0.0); + y++, lon = minwest + (dpp * (double)y)) { + if (lon >= 360.0) + lon -= 360.0; + + edge.lat = min_north; + edge.lon = lon; + edge.alt = altitude; + + PlotPropPath(source, edge, mask_value, fd, propmodel, + knifeedge); + count++; + if (count == z) { + //fprintf(stdout,"%c",symbol[x]); + //fflush(stdout); + count = 0; + + if (x == 3) + x = 0; + else + x++; + } + + } + + } + //S4 + if (haf == 0 || haf == 2) { + count = 0; + if (debug) { + fprintf(stdout, "\n75%c to 100%c ", 37, 37); + fflush(stdout); + } + z = (int)(th * (double)(max_north - min_north)); + + for (lat = (double)min_north, x = 0, y = 0; + lat < (double)max_north; + y++, lat = (double)min_north + (dpp * (double)y)) { + edge.lat = lat; + edge.lon = max_west; + edge.alt = altitude; + + PlotPropPath(source, edge, mask_value, fd, propmodel, + knifeedge); + count++; + + if (count == z) { + + count = 0; + + if (x == 3) + x = 0; + else + x++; + } + } + + } //S4 + + if (fd != NULL) + fclose(fd); + + if (mask_value < 30) + mask_value++; +} + +void LoadSignalColors(struct site xmtr) +{ + int x, y, ok, val[4]; + char filename[255], string[80], *pointer = NULL, *s = NULL; + FILE *fd = NULL; + + for (x = 0; xmtr.filename[x] != '.' && xmtr.filename[x] != 0 && x < 250; + x++) + filename[x] = xmtr.filename[x]; + + filename[x] = '.'; + filename[x + 1] = 's'; + filename[x + 2] = 'c'; + filename[x + 3] = 'f'; + filename[x + 4] = 0; + + /* Default values */ + + region.level[0] = 128; + region.color[0][0] = 255; + region.color[0][1] = 0; + region.color[0][2] = 0; + + region.level[1] = 118; + region.color[1][0] = 255; + region.color[1][1] = 165; + region.color[1][2] = 0; + + region.level[2] = 108; + region.color[2][0] = 255; + region.color[2][1] = 206; + region.color[2][2] = 0; + + region.level[3] = 98; + region.color[3][0] = 255; + region.color[3][1] = 255; + region.color[3][2] = 0; + + region.level[4] = 88; + region.color[4][0] = 184; + region.color[4][1] = 255; + region.color[4][2] = 0; + + region.level[5] = 78; + region.color[5][0] = 0; + region.color[5][1] = 255; + region.color[5][2] = 0; + + region.level[6] = 68; + region.color[6][0] = 0; + region.color[6][1] = 208; + region.color[6][2] = 0; + + region.level[7] = 58; + region.color[7][0] = 0; + region.color[7][1] = 196; + region.color[7][2] = 196; + + region.level[8] = 48; + region.color[8][0] = 0; + region.color[8][1] = 148; + region.color[8][2] = 255; + + region.level[9] = 38; + region.color[9][0] = 80; + region.color[9][1] = 80; + region.color[9][2] = 255; + + region.level[10] = 28; + region.color[10][0] = 0; + region.color[10][1] = 38; + region.color[10][2] = 255; + + region.level[11] = 18; + region.color[11][0] = 142; + region.color[11][1] = 63; + region.color[11][2] = 255; + + region.level[12] = 8; + region.color[12][0] = 140; + region.color[12][1] = 0; + region.color[12][2] = 128; + + region.levels = 13; + + fd = fopen(filename, "r"); + + if (fd == NULL) + fd = fopen(filename, "r"); + + if (fd == NULL) { + fd = fopen(filename, "w"); + + for (x = 0; x < region.levels; x++) + fprintf(fd, "%3d: %3d, %3d, %3d\n", region.level[x], + region.color[x][0], region.color[x][1], + region.color[x][2]); + + fclose(fd); + } + + else { + x = 0; + s = fgets(string, 80, fd); + + while (x < 128 && feof(fd) == 0) { + pointer = strchr(string, ';'); + + if (pointer != NULL) + *pointer = 0; + + ok = sscanf(string, "%d: %d, %d, %d", &val[0], &val[1], + &val[2], &val[3]); + + if (ok == 4) { + for (y = 0; y < 4; y++) { + if (val[y] > 255) + val[y] = 255; + + if (val[y] < 0) + val[y] = 0; + } + + region.level[x] = val[0]; + region.color[x][0] = val[1]; + region.color[x][1] = val[2]; + region.color[x][2] = val[3]; + x++; + } + + s = fgets(string, 80, fd); + } + + fclose(fd); + region.levels = x; + } +} + +void LoadLossColors(struct site xmtr) +{ + int x, y, ok, val[4]; + char filename[255], string[80], *pointer = NULL, *s = NULL; + FILE *fd = NULL; + + for (x = 0; xmtr.filename[x] != '.' && xmtr.filename[x] != 0 && x < 250; + x++) + filename[x] = xmtr.filename[x]; + + filename[x] = '.'; + filename[x + 1] = 'l'; + filename[x + 2] = 'c'; + filename[x + 3] = 'f'; + filename[x + 4] = 0; + + /* Default values */ + + region.level[0] = 80; + region.color[0][0] = 255; + region.color[0][1] = 0; + region.color[0][2] = 0; + + region.level[1] = 90; + region.color[1][0] = 255; + region.color[1][1] = 128; + region.color[1][2] = 0; + + region.level[2] = 100; + region.color[2][0] = 255; + region.color[2][1] = 165; + region.color[2][2] = 0; + + region.level[3] = 110; + region.color[3][0] = 255; + region.color[3][1] = 206; + region.color[3][2] = 0; + + region.level[4] = 120; + region.color[4][0] = 255; + region.color[4][1] = 255; + region.color[4][2] = 0; + + region.level[5] = 130; + region.color[5][0] = 184; + region.color[5][1] = 255; + region.color[5][2] = 0; + + region.level[6] = 140; + region.color[6][0] = 0; + region.color[6][1] = 255; + region.color[6][2] = 0; + + region.level[7] = 150; + region.color[7][0] = 0; + region.color[7][1] = 208; + region.color[7][2] = 0; + + region.level[8] = 160; + region.color[8][0] = 0; + region.color[8][1] = 196; + region.color[8][2] = 196; + + region.level[9] = 170; + region.color[9][0] = 0; + region.color[9][1] = 148; + region.color[9][2] = 255; + + region.level[10] = 180; + region.color[10][0] = 80; + region.color[10][1] = 80; + region.color[10][2] = 255; + + region.level[11] = 190; + region.color[11][0] = 0; + region.color[11][1] = 38; + region.color[11][2] = 255; + + region.level[12] = 200; + region.color[12][0] = 142; + region.color[12][1] = 63; + region.color[12][2] = 255; + + region.level[13] = 210; + region.color[13][0] = 196; + region.color[13][1] = 54; + region.color[13][2] = 255; + + region.level[14] = 220; + region.color[14][0] = 255; + region.color[14][1] = 0; + region.color[14][2] = 255; + + region.level[15] = 230; + region.color[15][0] = 255; + region.color[15][1] = 194; + region.color[15][2] = 204; + + region.levels = 16; + + fd = fopen(filename, "r"); + + if (fd == NULL) + fd = fopen(filename, "r"); + + if (fd == NULL) { + fd = fopen(filename, "w"); + + for (x = 0; x < region.levels; x++) + fprintf(fd, "%3d: %3d, %3d, %3d\n", region.level[x], + region.color[x][0], region.color[x][1], + region.color[x][2]); + + fclose(fd); + } + + else { + x = 0; + s = fgets(string, 80, fd); + + while (x < 128 && feof(fd) == 0) { + pointer = strchr(string, ';'); + + if (pointer != NULL) + *pointer = 0; + + ok = sscanf(string, "%d: %d, %d, %d", &val[0], &val[1], + &val[2], &val[3]); + + if (ok == 4) { + for (y = 0; y < 4; y++) { + if (val[y] > 255) + val[y] = 255; + + if (val[y] < 0) + val[y] = 0; + } + + region.level[x] = val[0]; + region.color[x][0] = val[1]; + region.color[x][1] = val[2]; + region.color[x][2] = val[3]; + x++; + } + + s = fgets(string, 80, fd); + } + + fclose(fd); + region.levels = x; + } +} + +void LoadDBMColors(struct site xmtr) +{ + int x, y, ok, val[4]; + char filename[255], string[80], *pointer = NULL, *s = NULL; + FILE *fd = NULL; + + for (x = 0; xmtr.filename[x] != '.' && xmtr.filename[x] != 0 && x < 250; + x++) + filename[x] = xmtr.filename[x]; + + filename[x] = '.'; + filename[x + 1] = 'd'; + filename[x + 2] = 'c'; + filename[x + 3] = 'f'; + filename[x + 4] = 0; + + /* Default values */ + + region.level[0] = 0; + region.color[0][0] = 255; + region.color[0][1] = 0; + region.color[0][2] = 0; + + region.level[1] = -10; + region.color[1][0] = 255; + region.color[1][1] = 128; + region.color[1][2] = 0; + + region.level[2] = -20; + region.color[2][0] = 255; + region.color[2][1] = 165; + region.color[2][2] = 0; + + region.level[3] = -30; + region.color[3][0] = 255; + region.color[3][1] = 206; + region.color[3][2] = 0; + + region.level[4] = -40; + region.color[4][0] = 255; + region.color[4][1] = 255; + region.color[4][2] = 0; + + region.level[5] = -50; + region.color[5][0] = 184; + region.color[5][1] = 255; + region.color[5][2] = 0; + + region.level[6] = -60; + region.color[6][0] = 0; + region.color[6][1] = 255; + region.color[6][2] = 0; + + region.level[7] = -70; + region.color[7][0] = 0; + region.color[7][1] = 208; + region.color[7][2] = 0; + + region.level[8] = -80; + region.color[8][0] = 0; + region.color[8][1] = 196; + region.color[8][2] = 196; + + region.level[9] = -90; + region.color[9][0] = 0; + region.color[9][1] = 148; + region.color[9][2] = 255; + + region.level[10] = -100; + region.color[10][0] = 80; + region.color[10][1] = 80; + region.color[10][2] = 255; + + region.level[11] = -110; + region.color[11][0] = 0; + region.color[11][1] = 38; + region.color[11][2] = 255; + + region.level[12] = -120; + region.color[12][0] = 142; + region.color[12][1] = 63; + region.color[12][2] = 255; + + region.level[13] = -130; + region.color[13][0] = 196; + region.color[13][1] = 54; + region.color[13][2] = 255; + + region.level[14] = -140; + region.color[14][0] = 255; + region.color[14][1] = 0; + region.color[14][2] = 255; + + region.level[15] = -150; + region.color[15][0] = 255; + region.color[15][1] = 194; + region.color[15][2] = 204; + + region.levels = 16; + + fd = fopen(filename, "r"); + + if (fd == NULL) + fd = fopen(filename, "r"); + + if (fd == NULL) { + fd = fopen(filename, "w"); + + for (x = 0; x < region.levels; x++) + fprintf(fd, "%+4d: %3d, %3d, %3d\n", region.level[x], + region.color[x][0], region.color[x][1], + region.color[x][2]); + + fclose(fd); + } + + else { + x = 0; + s = fgets(string, 80, fd); + + while (x < 128 && feof(fd) == 0) { + pointer = strchr(string, ';'); + + if (pointer != NULL) + *pointer = 0; + + ok = sscanf(string, "%d: %d, %d, %d", &val[0], &val[1], + &val[2], &val[3]); + + if (ok == 4) { + if (val[0] < -200) + val[0] = -200; + + if (val[0] > +40) + val[0] = +40; + + region.level[x] = val[0]; + + for (y = 1; y < 4; y++) { + if (val[y] > 255) + val[y] = 255; + + if (val[y] < 0) + val[y] = 0; + } + + region.color[x][0] = val[1]; + region.color[x][1] = val[2]; + region.color[x][2] = val[3]; + x++; + } + + s = fgets(string, 80, fd); + } + + fclose(fd); + region.levels = x; + } +} + +void DoPathLoss(char *filename, unsigned char geo, unsigned char kml, + unsigned char ngs, struct site *xmtr, unsigned char txsites) +{ + /* This function generates a topographic map in Portable Pix Map + (PPM) format based on the content of flags held in the mask[][] + array (only). The image created is rotated counter-clockwise + 90 degrees from its representation in dem[][] so that north + points up and east points right in the image generated. */ + + char mapfile[255]; + unsigned width, height, red, green, blue, terrain = 0; + unsigned char found, mask, cityorcounty; + int indx, x, y, z, x0, y0, loss, match; + double lat, lon, conversion, one_over_gamma, minwest; + FILE *fd; + + one_over_gamma = 1.0 / GAMMA; + conversion = + 255.0 / pow((double)(max_elevation - min_elevation), + one_over_gamma); + + width = (unsigned)(ippd * ReduceAngle(max_west - min_west)); + height = (unsigned)(ippd * ReduceAngle(max_north - min_north)); + + LoadLossColors(xmtr[0]); + + if (filename[0] == 0) { + strncpy(filename, xmtr[0].filename, 254); + filename[strlen(filename) - 4] = 0; /* Remove .qth */ + } + + y = strlen(filename); + + if (y > 4) { + if (filename[y - 1] == 'm' && filename[y - 2] == 'p' + && filename[y - 3] == 'p' && filename[y - 4] == '.') + y -= 4; + } + + for (x = 0; x < y; x++) { + mapfile[x] = filename[x]; + //geofile[x]=filename[x]; + //kmlfile[x]=filename[x]; + } + + mapfile[x] = '.'; + //geofile[x]='.'; + //kmlfile[x]='.'; + mapfile[x + 1] = 'p'; + //geofile[x+1]='g'; + //kmlfile[x+1]='k'; + mapfile[x + 2] = 'p'; + //geofile[x+2]='e'; + //kmlfile[x+2]='m'; + mapfile[x + 3] = 'm'; + //geofile[x+3]='o'; + //kmlfile[x+3]='l'; + mapfile[x + 4] = 0; + //geofile[x+4]=0; + //kmlfile[x+4]=0; + + minwest = ((double)min_west) + dpp; + + if (minwest > 360.0) + minwest -= 360.0; + + north = (double)max_north - dpp; + + if (kml || geo) + south = (double)min_north; /* No bottom legend */ + else + south = (double)min_north - (30.0 / ppd); /* 30 pixels for bottom legend */ + + east = (minwest < 180.0 ? -minwest : 360.0 - min_west); + west = (double)(max_west < 180 ? -max_west : 360 - max_west); + + // WriteKML() + //writeKML(xmtr,filename); + + fd = fopen(mapfile, "wb"); + + fprintf(fd, "P6\n%u %u\n255\n", width, (kml ? height : height + 30)); + if (debug) { + fprintf(stdout, "\nWriting \"%s\" (%ux%u pixmap image)... ", + mapfile, width, (kml ? height : height + 30)); + fflush(stdout); + } + for (y = 0, lat = north; y < (int)height; + y++, lat = north - (dpp * (double)y)) { + for (x = 0, lon = max_west; x < (int)width; + x++, lon = max_west - (dpp * (double)x)) { + if (lon < 0.0) + lon += 360.0; + + for (indx = 0, found = 0; + indx < MAXPAGES && found == 0;) { + x0 = (int)rint(ppd * + (lat - + (double)dem[indx].min_north)); + y0 = mpi - + (int)rint(ppd * + (LonDiff + ((double)dem[indx].max_west, + lon))); + + if (x0 >= 0 && x0 <= mpi && y0 >= 0 + && y0 <= mpi) + found = 1; + else + indx++; + } + + if (found) { + mask = dem[indx].mask[x0][y0]; + loss = (dem[indx].signal[x0][y0]); + cityorcounty = 0; + + //check loss isn't a near field void + // Receiver sensitivity kicks in later on + /* if(loss==0 && prevloss > 60){ + loss=(prevloss-5); + }else{ + prevloss=loss; + } + */ + if (debug) { + fprintf(stdout, "\n%d\t%d\t%d\t%d", + loss, indx, x0, y0); + fflush(stdout); + } + match = 255; + + red = 0; + green = 0; + blue = 0; + + if (loss <= region.level[0]) + match = 0; + else { + for (z = 1; + (z < region.levels + && match == 255); z++) { + if (loss >= region.level[z - 1] + && loss < region.level[z]) + match = z; + } + } + + if (match < region.levels) { + red = region.color[match][0]; + green = region.color[match][1]; + blue = region.color[match][2]; + } + + if (mask & 2) { + /* Text Labels: Red or otherwise */ + + if (red >= 180 && green <= 75 + && blue <= 75 && loss == 0) + fprintf(fd, "%c%c%c", 255 ^ red, + 255 ^ green, + 255 ^ blue); + 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); + + cityorcounty = 1; + } + + if (cityorcounty == 0) { + if (loss == 0 + || (contour_threshold != 0 + && loss > + abs(contour_threshold))) { + if (ngs) /* No terrain */ + fprintf(fd, "%c%c%c", + 255, 255, 255); + else { + /* Display land or sea elevation */ + + if (dem[indx]. + data[x0][y0] == 0) + fprintf(fd, + "%c%c%c", + 0, 0, + 170); + else { + terrain = + (unsigned) + (0.5 + + pow((double)(dem[indx].data[x0][y0] - min_elevation), one_over_gamma) * conversion); + fprintf(fd, + "%c%c%c", + terrain, + terrain, + terrain); + } + } + } + + else { + /* Plot path loss in color */ + + if (red != 0 || green != 0 + || blue != 0) + fprintf(fd, "%c%c%c", + red, green, + blue); + + else { /* terrain / sea-level */ + + if (dem[indx]. + data[x0][y0] == 0) + fprintf(fd, + "%c%c%c", + 0, 0, + 170); + else { + /* Elevation: Greyscale */ + terrain = + (unsigned) + (0.5 + + pow((double)(dem[indx].data[x0][y0] - min_elevation), one_over_gamma) * conversion); + fprintf(fd, + "%c%c%c", + terrain, + terrain, + terrain); + } + } + } + } + } + + else { + /* We should never get here, but if */ + /* we do, display the region as black */ + + fprintf(fd, "%c%c%c", 0, 0, 0); + } + } + } + + fclose(fd); + +} + +void DoSigStr(char *filename, unsigned char geo, unsigned char kml, + unsigned char ngs, struct site *xmtr, unsigned char txsites) +{ + /* This function generates a topographic map in Portable Pix Map + (PPM) format based on the signal strength values held in the + signal[][] array. The image created is rotated counter-clockwise + 90 degrees from its representation in dem[][] so that north + points up and east points right in the image generated. */ + + char mapfile[255]; + unsigned width, height, terrain, red, green, blue; + unsigned char found, mask, cityorcounty; + int indx, x, y, z = 1, x0, y0, signal, match; + double conversion, one_over_gamma, lat, lon, minwest; + FILE *fd; + + one_over_gamma = 1.0 / GAMMA; + conversion = + 255.0 / pow((double)(max_elevation - min_elevation), + one_over_gamma); + + width = (unsigned)(ippd * ReduceAngle(max_west - min_west)); + height = (unsigned)(ippd * ReduceAngle(max_north - min_north)); + + LoadSignalColors(xmtr[0]); + + if (filename[0] == 0) { + strncpy(filename, xmtr[0].filename, 254); + filename[strlen(filename) - 4] = 0; /* Remove .qth */ + } + + y = strlen(filename); + + if (y > 4) { + if (filename[y - 1] == 'm' && filename[y - 2] == 'p' + && filename[y - 3] == 'p' && filename[y - 4] == '.') + y -= 4; + } + + for (x = 0; x < y; x++) { + mapfile[x] = filename[x]; + //geofile[x]=filename[x]; + //kmlfile[x]=filename[x]; + } + + mapfile[x] = '.'; + //geofile[x]='.'; + //kmlfile[x]='.'; + mapfile[x + 1] = 'p'; + //geofile[x+1]='g'; + //kmlfile[x+1]='k'; + mapfile[x + 2] = 'p'; + //geofile[x+2]='e'; + //kmlfile[x+2]='m'; + mapfile[x + 3] = 'm'; + //geofile[x+3]='o'; + //kmlfile[x+3]='l'; + mapfile[x + 4] = 0; + //geofile[x+4]=0; + //kmlfile[x+4]=0; + + minwest = ((double)min_west) + dpp; + + if (minwest > 360.0) + minwest -= 360.0; + + north = (double)max_north - dpp; + + south = (double)min_north; /* No bottom legend */ + + east = (minwest < 180.0 ? -minwest : 360.0 - min_west); + west = (double)(max_west < 180 ? -max_west : 360 - max_west); + + // WriteKML() + //writeKML(xmtr,filename); + + fd = fopen(mapfile, "wb"); + + fprintf(fd, "P6\n%u %u\n255\n", width, (kml ? height : height + 30)); + if (debug) { + fprintf(stdout, "\nWriting \"%s\" (%ux%u pixmap image)... ", + mapfile, width, (kml ? height : height + 30)); + fflush(stdout); + } + for (y = 0, lat = north; y < (int)height; + y++, lat = north - (dpp * (double)y)) { + for (x = 0, lon = max_west; x < (int)width; + x++, lon = max_west - (dpp * (double)x)) { + if (lon < 0.0) + lon += 360.0; + + for (indx = 0, found = 0; + indx < MAXPAGES && found == 0;) { + x0 = (int)rint(ppd * + (lat - + (double)dem[indx].min_north)); + y0 = mpi - + (int)rint(ppd * + (LonDiff + ((double)dem[indx].max_west, + lon))); + + if (x0 >= 0 && x0 <= mpi && y0 >= 0 + && y0 <= mpi) + found = 1; + else + indx++; + } + + if (found) { + mask = dem[indx].mask[x0][y0]; + signal = (dem[indx].signal[x0][y0]) - 100; + cityorcounty = 0; + + //check signal isn't near field void + // Receiver sensitivity kicks in later on + /*if(signal==-100 && prevsignal > -40){ + signal=(prevsignal+5); + }else{ + prevsignal=signal; + } */ + + if (debug) { + fprintf(stdout, "\n%d\t%d\t%d\t%d", + signal, indx, x0, y0); + fflush(stdout); + } + + match = 255; + + red = 0; + green = 0; + blue = 0; + + if (signal >= region.level[0]) + match = 0; + else { + for (z = 1; + (z < region.levels + && match == 255); z++) { + if (signal < region.level[z - 1] + && signal >= + region.level[z]) + match = z; + } + } + + if (match < region.levels) { + red = region.color[match][0]; + green = region.color[match][1]; + blue = region.color[match][2]; + } + + if (mask & 2) { + /* Text Labels: Red or otherwise */ + + if (red >= 180 && green <= 75 + && blue <= 75) + fprintf(fd, "%c%c%c", 255 ^ red, + 255 ^ green, + 255 ^ blue); + 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); + + cityorcounty = 1; + } + + if (cityorcounty == 0) { + if (contour_threshold != 0 + && signal < contour_threshold) { + if (ngs) + fprintf(fd, "%c%c%c", + 255, 255, 255); + else { + /* Display land or sea elevation */ + + if (dem[indx]. + data[x0][y0] == 0) + fprintf(fd, + "%c%c%c", + 0, 0, + 170); + else { + terrain = + (unsigned) + (0.5 + + pow((double)(dem[indx].data[x0][y0] - min_elevation), one_over_gamma) * conversion); + fprintf(fd, + "%c%c%c", + terrain, + terrain, + terrain); + } + } + } + + else { + /* Plot field strength regions in color */ + + if (red != 0 || green != 0 + || blue != 0) + fprintf(fd, "%c%c%c", + red, green, + blue); + + else { /* terrain / sea-level */ + + if (ngs) + fprintf(fd, + "%c%c%c", + 255, + 255, + 255); + else { + if (dem[indx]. + data[x0][y0] + == 0) + fprintf + (fd, + "%c%c%c", + 0, + 0, + 170); + else { + /* Elevation: Greyscale */ + terrain + = + (unsigned) + (0.5 + + + pow + ((double)(dem[indx].data[x0][y0] - min_elevation), one_over_gamma) * conversion); + fprintf + (fd, + "%c%c%c", + terrain, + terrain, + terrain); + } + } + } + } + } + } + + else { + /* We should never get here, but if */ + /* we do, display the region as black */ + + fprintf(fd, "%c%c%c", 0, 0, 0); + } + } + } + + fclose(fd); + +} + +void DoRxdPwr(char *filename, unsigned char geo, unsigned char kml, + unsigned char ngs, struct site *xmtr, unsigned char txsites) +{ + /* This function generates a topographic map in Portable Pix Map + (PPM) format based on the signal power level values held in the + signal[][] array. The image created is rotated counter-clockwise + 90 degrees from its representation in dem[][] so that north + points up and east points right in the image generated. */ + + char mapfile[255]; + unsigned width, height, terrain, red, green, blue; + unsigned char found, mask, cityorcounty; + int indx, x, y, z = 1, x0, y0, dBm, match; + double conversion, one_over_gamma, lat, lon, minwest; + FILE *fd; + + one_over_gamma = 1.0 / GAMMA; + conversion = + 255.0 / pow((double)(max_elevation - min_elevation), + one_over_gamma); + + width = (unsigned)(ippd * ReduceAngle(max_west - min_west)); + height = (unsigned)(ippd * ReduceAngle(max_north - min_north)); + + LoadDBMColors(xmtr[0]); + + if (filename[0] == 0) { + strncpy(filename, xmtr[0].filename, 254); + filename[strlen(filename) - 4] = 0; /* Remove .qth */ + } + + y = strlen(filename); + + if (y > 4) { + if (filename[y - 1] == 'm' && filename[y - 2] == 'p' + && filename[y - 3] == 'p' && filename[y - 4] == '.') + y -= 4; + } + + for (x = 0; x < y; x++) { + mapfile[x] = filename[x]; + //geofile[x]=filename[x]; + //kmlfile[x]=filename[x]; + } + + mapfile[x] = '.'; + //geofile[x]='.'; + //kmlfile[x]='.'; + mapfile[x + 1] = 'p'; + //geofile[x+1]='g'; + //kmlfile[x+1]='k'; + mapfile[x + 2] = 'p'; + //geofile[x+2]='e'; + //kmlfile[x+2]='m'; + mapfile[x + 3] = 'm'; + //geofile[x+3]='o'; + //kmlfile[x+3]='l'; + mapfile[x + 4] = 0; + //geofile[x+4]=0; + //kmlfile[x+4]=0; + + minwest = ((double)min_west) + dpp; + + if (minwest > 360.0) + minwest -= 360.0; + + north = (double)max_north - dpp; + + south = (double)min_north; /* No bottom legend */ + + east = (minwest < 180.0 ? -minwest : 360.0 - min_west); + west = (double)(max_west < 180 ? -max_west : 360 - max_west); + + fd = fopen(mapfile, "wb"); + + fprintf(fd, "P6\n%u %u\n255\n", width, (kml ? height : height + 30)); + if (debug) { + fprintf(stdout, "\nWriting \"%s\" (%ux%u pixmap image)... ", + mapfile, width, (kml ? height : height + 30)); + fflush(stdout); + } + // WriteKML() + //writeKML(xmtr,filename); + + for (y = 0, lat = north; y < (int)height; + y++, lat = north - (dpp * (double)y)) { + for (x = 0, lon = max_west; x < (int)width; + x++, lon = max_west - (dpp * (double)x)) { + if (lon < 0.0) + lon += 360.0; + + for (indx = 0, found = 0; + indx < MAXPAGES && found == 0;) { + x0 = (int)rint(ppd * + (lat - + (double)dem[indx].min_north)); + y0 = mpi - + (int)rint(ppd * + (LonDiff + ((double)dem[indx].max_west, + lon))); + + if (x0 >= 0 && x0 <= mpi && y0 >= 0 + && y0 <= mpi) + found = 1; + else + indx++; + } + + if (found) { + mask = dem[indx].mask[x0][y0]; + dBm = (dem[indx].signal[x0][y0]) - 200; + cityorcounty = 0; + + if (debug) { + fprintf(stdout, "\n%d\t%d\t%d\t%d", dBm, + indx, x0, y0); + fflush(stdout); + } + + match = 255; + + red = 0; + green = 0; + blue = 0; + + if (dBm >= region.level[0]) + match = 0; + else { + for (z = 1; + (z < region.levels + && match == 255); z++) { + if (dBm < region.level[z - 1] + && dBm >= region.level[z]) + match = z; + } + } + + if (match < region.levels) { + red = region.color[match][0]; + green = region.color[match][1]; + blue = region.color[match][2]; + } + + if (mask & 2) { + /* Text Labels: Red or otherwise */ + + if (red >= 180 && green <= 75 + && blue <= 75 && dBm != 0) + fprintf(fd, "%c%c%c", 255 ^ red, + 255 ^ green, + 255 ^ blue); + 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); + + cityorcounty = 1; + } + + if (cityorcounty == 0) { + if (contour_threshold != 0 + && dBm < contour_threshold) { + if (ngs) /* No terrain */ + fprintf(fd, "%c%c%c", + 255, 255, 255); + else { + /* Display land or sea elevation */ + + if (dem[indx]. + data[x0][y0] == 0) + fprintf(fd, + "%c%c%c", + 0, 0, + 170); + else { + terrain = + (unsigned) + (0.5 + + pow((double)(dem[indx].data[x0][y0] - min_elevation), one_over_gamma) * conversion); + fprintf(fd, + "%c%c%c", + terrain, + terrain, + terrain); + } + } + } + + else { + /* Plot signal power level regions in color */ + + if (red != 0 || green != 0 + || blue != 0) + fprintf(fd, "%c%c%c", + red, green, + blue); + + else { /* terrain / sea-level */ + + if (ngs) + fprintf(fd, + "%c%c%c", + 255, + 255, + 255); + else { + if (dem[indx]. + data[x0][y0] + == 0) + fprintf + (fd, + "%c%c%c", + 0, + 0, + 170); + else { + /* Elevation: Greyscale */ + terrain + = + (unsigned) + (0.5 + + + pow + ((double)(dem[indx].data[x0][y0] - min_elevation), one_over_gamma) * conversion); + fprintf + (fd, + "%c%c%c", + terrain, + terrain, + terrain); + } + } + } + } + } + } + + else { + /* We should never get here, but if */ + /* we do, display the region as black */ + + fprintf(fd, "%c%c%c", 0, 0, 0); + } + } + } + + fclose(fd); + +} + +void DoLOS(char *filename, unsigned char geo, unsigned char kml, + unsigned char ngs, struct site *xmtr, unsigned char txsites) +{ + /* This function generates a topographic map in Portable Pix Map + (PPM) format based on the signal power level values held in the + signal[][] array. The image created is rotated counter-clockwise + 90 degrees from its representation in dem[][] so that north + points up and east points right in the image generated. */ + + char mapfile[255]; + unsigned width, height, terrain; + unsigned char found, mask; + int indx, x, y, x0, y0; + double conversion, one_over_gamma, lat, lon, minwest; + FILE *fd; + + one_over_gamma = 1.0 / GAMMA; + conversion = + 255.0 / pow((double)(max_elevation - min_elevation), + one_over_gamma); + + width = (unsigned)(ippd * ReduceAngle(max_west - min_west)); + height = (unsigned)(ippd * ReduceAngle(max_north - min_north)); + + //LoadDBMColors(xmtr[0]); + + if (filename[0] == 0) { + strncpy(filename, xmtr[0].filename, 254); + filename[strlen(filename) - 4] = 0; /* Remove .qth */ + } + + y = strlen(filename); + + if (y > 4) { + if (filename[y - 1] == 'm' && filename[y - 2] == 'p' + && filename[y - 3] == 'p' && filename[y - 4] == '.') + y -= 4; + } + + for (x = 0; x < y; x++) { + mapfile[x] = filename[x]; + //geofile[x]=filename[x]; + //kmlfile[x]=filename[x]; + } + + mapfile[x] = '.'; + //geofile[x]='.'; + //kmlfile[x]='.'; + mapfile[x + 1] = 'p'; + //geofile[x+1]='g'; + //kmlfile[x+1]='k'; + mapfile[x + 2] = 'p'; + //geofile[x+2]='e'; + //kmlfile[x+2]='m'; + mapfile[x + 3] = 'm'; + //geofile[x+3]='o'; + //kmlfile[x+3]='l'; + mapfile[x + 4] = 0; + //geofile[x+4]=0; + //kmlfile[x+4]=0; + + minwest = ((double)min_west) + dpp; + + if (minwest > 360.0) + minwest -= 360.0; + + north = (double)max_north - dpp; + + south = (double)min_north; /* No bottom legend */ + + east = (minwest < 180.0 ? -minwest : 360.0 - min_west); + west = (double)(max_west < 180 ? -max_west : 360 - max_west); + + fd = fopen(mapfile, "wb"); + + fprintf(fd, "P6\n%u %u\n255\n", width, (kml ? height : height + 30)); + if (debug) { + fprintf(stdout, "\nWriting \"%s\" (%ux%u pixmap image)... ", + mapfile, width, (kml ? height : height + 30)); + fflush(stdout); + } + // WriteKML() + //writeKML(xmtr,filename); + + for (y = 0, lat = north; y < (int)height; + y++, lat = north - (dpp * (double)y)) { + for (x = 0, lon = max_west; x < (int)width; + x++, lon = max_west - (dpp * (double)x)) { + if (lon < 0.0) + lon += 360.0; + + for (indx = 0, found = 0; + indx < MAXPAGES && found == 0;) { + x0 = (int)rint(ppd * + (lat - + (double)dem[indx].min_north)); + y0 = mpi - + (int)rint(ppd * + (LonDiff + ((double)dem[indx].max_west, + lon))); + + if (x0 >= 0 && x0 <= mpi && y0 >= 0 + && y0 <= mpi) + found = 1; + else + indx++; + } + + if (found) { + mask = dem[indx].mask[x0][y0]; + + if (mask & 2) + /* Text Labels: Red */ + fprintf(fd, "%c%c%c", 255, 0, 0); + + else if (mask & 4) + /* County Boundaries: Light Cyan */ + fprintf(fd, "%c%c%c", 128, 128, 255); + + else + switch (mask & 57) { + case 1: + /* TX1: Green */ + fprintf(fd, "%c%c%c", 0, 255, + 0); + break; + + case 8: + /* TX2: Cyan */ + fprintf(fd, "%c%c%c", 0, 255, + 255); + break; + + case 9: + /* TX1 + TX2: Yellow */ + fprintf(fd, "%c%c%c", 255, 255, + 0); + break; + + case 16: + /* TX3: Medium Violet */ + fprintf(fd, "%c%c%c", 147, 112, + 219); + break; + + case 17: + /* TX1 + TX3: Pink */ + fprintf(fd, "%c%c%c", 255, 192, + 203); + break; + + case 24: + /* TX2 + TX3: Orange */ + fprintf(fd, "%c%c%c", 255, 165, + 0); + break; + + case 25: + /* TX1 + TX2 + TX3: Dark Green */ + fprintf(fd, "%c%c%c", 0, 100, + 0); + break; + + case 32: + /* TX4: Sienna 1 */ + fprintf(fd, "%c%c%c", 255, 130, + 71); + break; + + case 33: + /* TX1 + TX4: Green Yellow */ + fprintf(fd, "%c%c%c", 173, 255, + 47); + break; + + case 40: + /* TX2 + TX4: Dark Sea Green 1 */ + fprintf(fd, "%c%c%c", 193, 255, + 193); + break; + + case 41: + /* TX1 + TX2 + TX4: Blanched Almond */ + fprintf(fd, "%c%c%c", 255, 235, + 205); + break; + + case 48: + /* TX3 + TX4: Dark Turquoise */ + fprintf(fd, "%c%c%c", 0, 206, + 209); + break; + + case 49: + /* TX1 + TX3 + TX4: Medium Spring Green */ + fprintf(fd, "%c%c%c", 0, 250, + 154); + break; + + case 56: + /* TX2 + TX3 + TX4: Tan */ + fprintf(fd, "%c%c%c", 210, 180, + 140); + break; + + case 57: + /* TX1 + TX2 + TX3 + TX4: Gold2 */ + fprintf(fd, "%c%c%c", 238, 201, + 0); + break; + + default: + if (ngs) /* No terrain */ + fprintf(fd, "%c%c%c", + 255, 255, 255); + else { + /* Sea-level: Medium Blue */ + if (dem[indx]. + data[x0][y0] == 0) + fprintf(fd, + "%c%c%c", + 0, 0, + 170); + else { + /* Elevation: Greyscale */ + terrain = + (unsigned) + (0.5 + + pow((double)(dem[indx].data[x0][y0] - min_elevation), one_over_gamma) * conversion); + fprintf(fd, + "%c%c%c", + terrain, + terrain, + terrain); + } + } + } + } + + else { + /* We should never get here, but if */ + /* we do, display the region as black */ + + fprintf(fd, "%c%c%c", 0, 0, 0); + } + } + } + + fclose(fd); + +} + +void LoadTopoData(int max_lon, int min_lon, int max_lat, int min_lat, + int winfiles) +{ + /* 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; + + if (winfiles == 1) { + if (ippd == 3600) + snprintf(string, 19, + "%d=%d=%d=%d=hd", x, + x + 1, ymin, ymax); + else + snprintf(string, 16, + "%d=%d=%d=%d", x, + x + 1, ymin, ymax); + + } else { + if (ippd == 3600) + snprintf(string, 19, + "%d:%d:%d:%d-hd", x, + x + 1, ymin, ymax); + else + snprintf(string, 16, + "%d:%d:%d:%d", x, + x + 1, ymin, ymax); + } + + LoadSDF(string, winfiles); + } + } + + 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; + + if (winfiles == 1) { + if (ippd == 3600) + snprintf(string, 19, + "%d=%d=%d=%d=hd", x, + x + 1, ymin, ymax); + else + snprintf(string, 16, + "%d=%d=%d=%d", x, + x + 1, ymin, ymax); + + } else { + if (ippd == 3600) + snprintf(string, 19, + "%d:%d:%d:%d-hd", x, + x + 1, ymin, ymax); + else + snprintf(string, 16, + "%d:%d:%d:%d", x, + x + 1, ymin, ymax); + } + + LoadSDF(string, winfiles); + } + } +} + +void LoadUDT(char *filename) +{ + /* This function reads a file containing User-Defined Terrain + features for their addition to the digital elevation model + data used by SPLAT!. Elevations in the UDT file are evaluated + and then copied into a temporary file under /tmp. Then the + contents of the temp file are scanned, and if found to be unique, + are added to the ground elevations described by the digital + elevation data already loaded into memory. */ + + int i, x, y, z, ypix, xpix, tempxpix, tempypix, fd = 0, n = + 0, pixelfound = 0; + char input[80], str[3][80], tempname[15], *pointer = NULL, *s = NULL; + double latitude, longitude, height, tempheight; + FILE *fd1 = NULL, *fd2 = NULL; + + strcpy(tempname, "/tmp/XXXXXX\0"); + + fd1 = fopen(filename, "r"); + + if (fd1 != NULL) { + fd = mkstemp(tempname); + fd2 = fopen(tempname, "w"); + + s = fgets(input, 78, fd1); + + pointer = strchr(input, ';'); + + if (pointer != NULL) + *pointer = 0; + + while (feof(fd1) == 0) { + /* Parse line for latitude, longitude, height */ + + for (x = 0, y = 0, z = 0; + x < 78 && input[x] != 0 && z < 3; x++) { + if (input[x] != ',' && y < 78) { + str[z][y] = input[x]; + y++; + } + + else { + str[z][y] = 0; + z++; + y = 0; + } + } + + latitude = ReadBearing(str[0]); + longitude = ReadBearing(str[1]); + + if (longitude < 0.0) + longitude += 360; + + /* Remove and/or from antenna height string */ + + for (i = 0; + str[2][i] != 13 && str[2][i] != 10 + && str[2][i] != 0; i++) ; + + str[2][i] = 0; + + /* The terrain feature may be expressed in either + feet or meters. If the letter 'M' or 'm' is + discovered in the string, then this is an + indication that the value given is expressed + in meters. Otherwise the height is interpreted + as being expressed in feet. */ + + for (i = 0; + str[2][i] != 'M' && str[2][i] != 'm' + && str[2][i] != 0 && i < 48; i++) ; + + if (str[2][i] == 'M' || str[2][i] == 'm') { + str[2][i] = 0; + height = rint(atof(str[2])); + } + + else { + str[2][i] = 0; + height = rint(METERS_PER_FOOT * atof(str[2])); + } + + if (height > 0.0) + fprintf(fd2, "%d, %d, %f\n", + (int)rint(latitude / dpp), + (int)rint(longitude / dpp), height); + + s = fgets(input, 78, fd1); + + pointer = strchr(input, ';'); + + if (pointer != NULL) + *pointer = 0; + } + + fclose(fd1); + fclose(fd2); + close(fd); + + fd1 = fopen(tempname, "r"); + fd2 = fopen(tempname, "r"); + + y = 0; + + n = fscanf(fd1, "%d, %d, %lf", &xpix, &ypix, &height); + + do { + x = 0; + z = 0; + + n = fscanf(fd2, "%d, %d, %lf", &tempxpix, &tempypix, + &tempheight); + + do { + if (x > y && xpix == tempxpix + && ypix == tempypix) { + z = 1; /* Dupe! */ + + if (tempheight > height) + height = tempheight; + } + + else { + n = fscanf(fd2, "%d, %d, %lf", + &tempxpix, &tempypix, + &tempheight); + x++; + } + + } while (feof(fd2) == 0 && z == 0); + + if (z == 0) + /* No duplicate found */ + //fprintf(stdout,"%lf, %lf \n",xpix*dpp, ypix*dpp); + fflush(stdout); + pixelfound = + AddElevation(xpix * dpp, ypix * dpp, height); + //fprintf(stdout,"%d \n",pixelfound); + fflush(stdout); + + n = fscanf(fd1, "%d, %d, %lf", &xpix, &ypix, &height); + y++; + + rewind(fd2); + + } while (feof(fd1) == 0); + + fclose(fd1); + fclose(fd2); + unlink(tempname); + } + +} + +void PlotPath(struct site source, struct site destination, char mask_value) +{ + /* This function analyzes the path between the source and + destination locations. It determines which points along + the path have line-of-sight visibility to the source. + Points along with path having line-of-sight visibility + to the source at an AGL altitude equal to that of the + destination location are stored by setting bit 1 in the + mask[][] array, which are displayed in green when PPM + maps are later generated by SPLAT!. */ + + char block; + int x, y; + register double cos_xmtr_angle, cos_test_angle, test_alt; + double distance, rx_alt, tx_alt; + + ReadPath(source, destination); + + for (y = 0; y < path.length; y++) { + /* Test this point only if it hasn't been already + tested and found to be free of obstructions. */ + + if ((GetMask(path.lat[y], path.lon[y]) & mask_value) == 0) { + distance = 5280.0 * path.distance[y]; + tx_alt = earthradius + source.alt + path.elevation[0]; + rx_alt = + earthradius + destination.alt + path.elevation[y]; + + /* Calculate the cosine of the elevation of the + transmitter as seen at the temp rx point. */ + + cos_xmtr_angle = + ((rx_alt * rx_alt) + (distance * distance) - + (tx_alt * tx_alt)) / (2.0 * rx_alt * distance); + + for (x = y, block = 0; x >= 0 && block == 0; x--) { + distance = + 5280.0 * (path.distance[y] - + path.distance[x]); + test_alt = + earthradius + (path.elevation[x] == + 0.0 ? path. + elevation[x] : path. + elevation[x] + clutter); + + 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 + 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 + be if the actual angles were compared. */ + + if (cos_xmtr_angle >= cos_test_angle) + block = 1; + } + + if (block == 0) + OrMask(path.lat[y], path.lon[y], mask_value); + } + } +} + +void ObstructionAnalysis(struct site xmtr, struct site rcvr, double f, + FILE * outfile) +{ + /* Perform an obstruction analysis along the + path between receiver and transmitter. */ + + int x; + struct site site_x; + double h_r, h_t, h_x, h_r_orig, cos_tx_angle, cos_test_angle, + cos_tx_angle_f1, cos_tx_angle_fpt6, d_tx, d_x, + h_r_f1, h_r_fpt6, h_f, h_los, lambda = 0.0; + char string[255], string_fpt6[255], string_f1[255]; + + 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); + + if (clutter > 0.0) { + fprintf(outfile, "Terrain has been raised by"); + + if (metric) + fprintf(outfile, " %.2f meters", + METERS_PER_FOOT * clutter); + else + fprintf(outfile, " %.2f feet", clutter); + + fprintf(outfile, " to account for ground clutter.\n\n"); + } + + /* 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 ppa!'s 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 + clutter; + 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(outfile, + "Between %s and %s, obstructions were detected at:\n\n", + rcvr.name, xmtr.name); + + if (site_x.lat >= 0.0) { + if (metric) + fprintf(outfile, + " %8.4f N,%9.4f W, %5.2f kilometers, %6.2f meters AMSL\n", + site_x.lat, site_x.lon, + KM_PER_MILE * (d_x / 5280.0), + METERS_PER_FOOT * (h_x - + earthradius)); + else + fprintf(outfile, + " %8.4f N,%9.4f W, %5.2f miles, %6.2f feet AMSL\n", + site_x.lat, site_x.lon, + d_x / 5280.0, + h_x - earthradius); + } + + else { + if (metric) + fprintf(outfile, + " %8.4f S,%9.4f W, %5.2f kilometers, %6.2f meters AMSL\n", + -site_x.lat, site_x.lon, + KM_PER_MILE * (d_x / 5280.0), + METERS_PER_FOOT * (h_x - + earthradius)); + else + + fprintf(outfile, + " %8.4f S,%9.4f W, %5.2f miles, %6.2f feet AMSL\n", + -site_x.lat, site_x.lon, + d_x / 5280.0, + h_x - earthradius); + } + } + + 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... */ + + 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 - + fzone_clearance * 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 - + fzone_clearance * sqrt(lambda * d_x * + (d_tx - d_x) / d_tx); + } + } + } + + if (h_r > h_r_orig) { + if (metric) + snprintf(string, 150, + "\nAntenna at %s must be raised to at least %.2f meters AGL\nto clear all obstructions detected.\n", + rcvr.name, + METERS_PER_FOOT * (h_r - GetElevation(rcvr) - + earthradius)); + else + snprintf(string, 150, + "\nAntenna at %s must be raised to at least %.2f feet AGL\nto clear all obstructions detected.\n", + rcvr.name, + h_r - GetElevation(rcvr) - earthradius); + } + + else + snprintf(string, 150, + "\nNo obstructions to LOS path due to terrain were detected\n"); + + if (f) { + if (h_r_fpt6 > h_r_orig) { + if (metric) + snprintf(string_fpt6, 150, + "\nAntenna at %s must be raised to at least %.2f meters AGL\nto clear %.0f%c of the first Fresnel zone.\n", + rcvr.name, + METERS_PER_FOOT * (h_r_fpt6 - + GetElevation(rcvr) - + earthradius), + fzone_clearance * 100.0, 37); + + else + snprintf(string_fpt6, 150, + "\nAntenna at %s must be raised to at least %.2f feet AGL\nto clear %.0f%c of the first Fresnel zone.\n", + rcvr.name, + h_r_fpt6 - GetElevation(rcvr) - + earthradius, fzone_clearance * 100.0, + 37); + } + + else + snprintf(string_fpt6, 150, + "\n%.0f%c of the first Fresnel zone is clear.\n", + fzone_clearance * 100.0, 37); + + if (h_r_f1 > h_r_orig) { + if (metric) + snprintf(string_f1, 150, + "\nAntenna at %s must be raised to at least %.2f meters AGL\nto clear the first Fresnel zone.\n", + rcvr.name, + METERS_PER_FOOT * (h_r_f1 - + GetElevation(rcvr) - + earthradius)); + + else + snprintf(string_f1, 150, + "\nAntenna at %s must be raised to at least %.2f feet AGL\nto clear the first Fresnel zone.\n", + rcvr.name, + h_r_f1 - GetElevation(rcvr) - + earthradius); + + } + + else + snprintf(string_f1, 150, + "\nThe first Fresnel zone is clear.\n"); + } + + fprintf(outfile, "%s", string); + + if (f) { + fprintf(outfile, "%s", string_f1); + fprintf(outfile, "%s", string_fpt6); + } + +} + +void PathReport(struct site source, struct site destination, char *name, + char graph_it) +{ + /* This function writes a PPA Path Report (name.txt) to + the filesystem. If (graph_it == 1), then gnuplot is invoked + to generate an appropriate output file indicating the Longley-Rice + model loss between the source and destination locations. + "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, .png is assumed. */ + + int x, y, z, errnum; + char basename[255], term[30], ext[15], strmode[100], + report_name[80], block = 0; + double maxloss = -100000.0, minloss = 100000.0, angle1, angle2, + azimuth, pattern = 1.0, patterndB = 0.0, + total_loss = 0.0, cos_xmtr_angle, cos_test_angle = 0.0, + source_alt, test_alt, dest_alt, source_alt2, dest_alt2, + distance, elevation, four_thirds_earth, + free_space_loss = 0.0, eirp = 0.0, voltage, rxp, power_density; + FILE *fd = NULL, *fd2 = NULL; + + //sprintf(report_name,"%s.txt",*name); + snprintf(report_name, 80, "%s.txt%c", name, 0); + + four_thirds_earth = FOUR_THIRDS * EARTHRADIUS; + + /*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]='_'; */ + + fd2 = fopen(report_name, "w"); + + fprintf(fd2, "\n\t\t--==[ Path Profile Analysis ]==--\n\n"); + //fprintf(fd2,"%s\n\n",dashes); + fprintf(fd2, "Transmitter site: %s\n", source.name); + + if (source.lat >= 0.0) { + fprintf(fd2, "Site location: %.4f North / %.4f West\n", + source.lat, source.lon); + //fprintf(fd2, " (%s N / ", source.lat); + } + + else { + + fprintf(fd2, "Site location: %.4f South / %.4f West\n", + -source.lat, source.lon); + //fprintf(fd2, " (%s S / ", source.lat); + } + + if (metric) { + fprintf(fd2, "Ground elevation: %.2f meters AMSL\n", + METERS_PER_FOOT * GetElevation(source)); + fprintf(fd2, + "Antenna height: %.2f meters AGL / %.2f meters AMSL\n", + METERS_PER_FOOT * source.alt, + METERS_PER_FOOT * (source.alt + GetElevation(source))); + } + + else { + 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)); + } + +/* + haavt=haat(source); + + if (haavt>-4999.0) + { + if (metric) + fprintf(fd2,"Antenna height above average terrain: %.2f meters\n",METERS_PER_FOOT*haavt); + else + fprintf(fd2,"Antenna height above average terrain: %.2f feet\n",haavt); + } +*/ + azimuth = Azimuth(source, destination); + angle1 = ElevationAngle(source, destination); + angle2 = ElevationAngle2(source, destination, earthradius); + + if (got_azimuth_pattern || got_elevation_pattern) { + x = (int)rint(10.0 * (10.0 - angle2)); + + if (x >= 0 && x <= 1000) + pattern = + (double)LR.antenna_pattern[(int)rint(azimuth)][x]; + + patterndB = 20.0 * log10(pattern); + } + + if (metric) + fprintf(fd2, "Distance to %s: %.2f kilometers\n", + destination.name, KM_PER_MILE * Distance(source, + destination)); + + else + fprintf(fd2, "Distance to %s: %.2f miles\n", destination.name, + Distance(source, destination)); + + fprintf(fd2, "Azimuth to %s: %.2f degrees\n", destination.name, + azimuth); + + if (angle1 >= 0.0) + fprintf(fd2, "Elevation angle to %s: %+.4f degrees\n", + destination.name, angle1); + + else + fprintf(fd2, "Depression angle to %s: %+.4f degrees\n", + destination.name, angle1); + + if ((angle2 - angle1) > 0.0001) { + if (angle2 < 0.0) + fprintf(fd2, "Depression\n"); + else + fprintf(fd2, "Elevation\n"); + + //fprintf(fd2," angle to the first obstruction: %+.4f degrees\n",angle2); + } + //fprintf(fd2,"\n%s\n\n",dashes); + + /* Receiver */ + + fprintf(fd2, "Receiver site: %s\n", destination.name); + + if (destination.lat >= 0.0) { + fprintf(fd2, "Site location: %.4f North / %.4f West\n", + destination.lat, destination.lon); + //fprintf(fd2, " (%s N / ", destination.lat); + } + + else { + fprintf(fd2, "Site location: %.4f South / %.4f West\n", + -destination.lat, destination.lon); + //fprintf(fd2, " (%s S / ", destination.lat); + } + + if (metric) { + fprintf(fd2, "Ground elevation: %.2f meters AMSL\n", + METERS_PER_FOOT * GetElevation(destination)); + fprintf(fd2, + "Antenna height: %.2f meters AGL / %.2f meters AMSL\n", + METERS_PER_FOOT * destination.alt, + METERS_PER_FOOT * (destination.alt + + GetElevation(destination))); + } + + else { + 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)); + } + + /*haavt=haat(destination); + + if (haavt>-4999.0) + { + if (metric) + fprintf(fd2,"Antenna height above average terrain: %.2f meters\n",METERS_PER_FOOT*haavt); + else + fprintf(fd2,"Antenna height above average terrain: %.2f feet\n",haavt); + } */ + + if (metric) + fprintf(fd2, "Distance to %s: %.2f kilometers\n", source.name, + KM_PER_MILE * Distance(source, destination)); + + else + fprintf(fd2, "Distance to %s: %.2f miles\n", source.name, + Distance(source, destination)); + + azimuth = Azimuth(destination, source); + + angle1 = ElevationAngle(destination, source); + angle2 = ElevationAngle2(destination, source, earthradius); + + fprintf(fd2, "Azimuth to %s: %.2f degrees\n", source.name, azimuth); + + if (angle1 >= 0.0) + fprintf(fd2, "Elevation angle to %s: %+.4f degrees\n", + source.name, angle1); + + else + fprintf(fd2, "Depression angle to %s: %+.4f degrees\n", + source.name, angle1); + + if ((angle2 - angle1) > 0.0001) { + if (angle2 < 0.0) + fprintf(fd2, "Depression"); + else + fprintf(fd2, "Elevation"); + + //fprintf(fd2," angle to the first obstruction: %+.4f degrees\n",angle2); + } + //fprintf(fd2,"\n%s\n\n",dashes); + + if (LR.frq_mhz > 0.0) { + fprintf(fd2, + "Longley-Rice path calculation parameters used in this analysis:\n\n"); + fprintf(fd2, "Earth's Dielectric Constant: %.3lf\n", + LR.eps_dielect); + fprintf(fd2, "Earth's Conductivity: %.3lf Siemens/meter\n", + LR.sgm_conductivity); + fprintf(fd2, + "Atmospheric Bending Constant (N-units): %.3lf ppm\n", + LR.eno_ns_surfref); + fprintf(fd2, "Frequency: %.3lf MHz\n", LR.frq_mhz); + fprintf(fd2, "Radio Climate: %d (", LR.radio_climate); + + switch (LR.radio_climate) { + case 1: + fprintf(fd2, "Equatorial"); + break; + + case 2: + fprintf(fd2, "Continental Subtropical"); + break; + + case 3: + fprintf(fd2, "Maritime Subtropical"); + break; + + case 4: + fprintf(fd2, "Desert"); + break; + + case 5: + fprintf(fd2, "Continental Temperate"); + break; + + case 6: + fprintf(fd2, "Martitime Temperate, Over Land"); + break; + + case 7: + fprintf(fd2, "Maritime Temperate, Over Sea"); + break; + + default: + fprintf(fd2, "Unknown"); + } + + fprintf(fd2, ")\nPolarisation: %d (", LR.pol); + + if (LR.pol == 0) + fprintf(fd2, "Horizontal"); + + if (LR.pol == 1) + fprintf(fd2, "Vertical"); + + fprintf(fd2, ")\nFraction of Situations: %.1lf%c\n", + LR.conf * 100.0, 37); + fprintf(fd2, "Fraction of Time: %.1lf%c\n", LR.rel * 100.0, 37); + + if (LR.erp != 0.0) { + fprintf(fd2, "Transmitter ERP: "); + + if (LR.erp < 1.0) + fprintf(fd2, "%.1lf milliwatts", + 1000.0 * LR.erp); + + if (LR.erp >= 1.0 && LR.erp < 10.0) + fprintf(fd2, "%.1lf Watts", LR.erp); + + if (LR.erp >= 10.0 && LR.erp < 10.0e3) + fprintf(fd2, "%.0lf Watts", LR.erp); + + if (LR.erp >= 10.0e3) + fprintf(fd2, "%.3lf kilowatts", LR.erp / 1.0e3); + + dBm = 10.0 * (log10(LR.erp * 1000.0)); + fprintf(fd2, " (%+.2f dBm)\n", dBm); + + /* EIRP = ERP + 2.14 dB */ + + fprintf(fd2, "Transmitter EIRP: "); + + eirp = LR.erp * 1.636816521; + + if (eirp < 1.0) + fprintf(fd2, "%.1lf milliwatts", 1000.0 * eirp); + + if (eirp >= 1.0 && eirp < 10.0) + fprintf(fd2, "%.1lf Watts", eirp); + + if (eirp >= 10.0 && eirp < 10.0e3) + fprintf(fd2, "%.0lf Watts", eirp); + + if (eirp >= 10.0e3) + fprintf(fd2, "%.3lf kilowatts", eirp / 1.0e3); + + dBm = 10.0 * (log10(eirp * 1000.0)); + fprintf(fd2, " (%+.2f dBm)\n", dBm); + } + + fprintf(fd2, "\n%s\n\n", dashes); + + fprintf(fd2, "Summary for the link between %s and %s:\n\n", + source.name, destination.name); + + if (patterndB != 0.0) + fprintf(fd2, + "%s antenna pattern towards %s: %.3f (%.2f dB)\n", + source.name, destination.name, pattern, + patterndB); + + ReadPath(source, destination); /* source=TX, destination=RX */ + + /* Copy elevations plus clutter along + path into the elev[] array. */ + + for (x = 1; x < path.length - 1; x++) + elev[x + 2] = + METERS_PER_FOOT * (path.elevation[x] == + 0.0 ? path. + elevation[x] : (clutter + + path. + elevation[x])); + + /* Copy ending points without clutter */ + + elev[2] = path.elevation[0] * METERS_PER_FOOT; + elev[path.length + 1] = + path.elevation[path.length - 1] * METERS_PER_FOOT; + + //fd=fopen("profile.gp","w"); + + azimuth = rint(Azimuth(source, destination)); + + for (y = 2; y < (path.length - 1); y++) { /* path.length-1 avoids LR error */ + distance = 5280.0 * path.distance[y]; + source_alt = + four_thirds_earth + source.alt + path.elevation[0]; + dest_alt = + four_thirds_earth + destination.alt + + path.elevation[y]; + dest_alt2 = dest_alt * dest_alt; + source_alt2 = source_alt * source_alt; + + /* Calculate the cosine of the elevation of + the receiver as seen by the transmitter. */ + + cos_xmtr_angle = + ((source_alt2) + (distance * distance) - + (dest_alt2)) / (2.0 * source_alt * distance); + + if (got_elevation_pattern) { + /* If an antenna elevation pattern is available, the + following code determines the elevation angle to + the first obstruction along the path. */ + + for (x = 2, block = 0; x < y && block == 0; x++) { + distance = + 5280.0 * (path.distance[y] - + path.distance[x]); + test_alt = + four_thirds_earth + + path.elevation[x]; + + /* Calculate the cosine of the elevation + angle of the terrain (test point) + as seen by the transmitter. */ + + cos_test_angle = + ((source_alt2) + + (distance * distance) - + (test_alt * test_alt)) / (2.0 * + source_alt + * + distance); + + /* Compare these two angles to determine if + an obstruction exists. Since we're comparing + the cosines of these angles rather than + the angles themselves, the sense of the + following "if" statement is reversed from + what it would be if the angles themselves + were compared. */ + + if (cos_xmtr_angle >= cos_test_angle) + block = 1; + } + + /* At this point, we have the elevation angle + to the first obstruction (if it exists). */ + } + + /* Determine path loss for each point along the + path using Longley-Rice's point_to_point mode + starting at x=2 (number_of_points = 1), the + shortest distance terrain can play a role in + path loss. */ + + elev[0] = y - 1; /* (number of points - 1) */ + + /* Distance between elevation samples */ + + elev[1] = + METERS_PER_MILE * (path.distance[y] - + path.distance[y - 1]); + + point_to_point(elev, source.alt * METERS_PER_FOOT, + destination.alt * METERS_PER_FOOT, + LR.eps_dielect, LR.sgm_conductivity, + LR.eno_ns_surfref, LR.frq_mhz, + LR.radio_climate, LR.pol, LR.conf, + LR.rel, loss, strmode, errnum); + + if (block) + elevation = + ((acos(cos_test_angle)) / DEG2RAD) - 90.0; + else + elevation = + ((acos(cos_xmtr_angle)) / DEG2RAD) - 90.0; + + /* Integrate the antenna's radiation + pattern into the overall path loss. */ + + x = (int)rint(10.0 * (10.0 - elevation)); + + if (x >= 0 && x <= 1000) { + pattern = + (double)LR.antenna_pattern[(int)azimuth][x]; + + if (pattern != 0.0) + patterndB = 20.0 * log10(pattern); + } + + else + patterndB = 0.0; + + total_loss = loss - patterndB; + + /* if (metric) + fprintf(fd,"%.3f %.3f\n",KM_PER_MILE*(path.distance[path.length-1]-path.distance[y]),total_loss); + + else + fprintf(fd,"%.3f %.3f\n",path.distance[path.length-1]-path.distance[y],total_loss); + */ + + if (total_loss > maxloss) + maxloss = total_loss; + + if (total_loss < minloss) + minloss = total_loss; + } + + //fclose(fd); + + distance = Distance(source, destination); + + if (distance != 0.0) { + free_space_loss = + 36.6 + (20.0 * log10(LR.frq_mhz)) + + (20.0 * log10(distance)); + + fprintf(fd2, "Free space path loss: %.2f dB\n", + free_space_loss); + } + + fprintf(fd2, "Longley-Rice path loss: %.2f dB\n", loss); + + if (free_space_loss != 0.0) + fprintf(fd2, + "Attenuation due to terrain shielding: %.2f dB\n", + loss - free_space_loss); + + if (patterndB != 0.0) + fprintf(fd2, + "Total path loss including %s antenna pattern: %.2f dB\n", + source.name, total_loss); + + if (LR.erp != 0.0) { + field_strength = + (139.4 + (20.0 * log10(LR.frq_mhz)) - total_loss) + + (10.0 * log10(LR.erp / 1000.0)); + + /* dBm is referenced to EIRP */ + + rxp = eirp / (pow(10.0, (total_loss / 10.0))); + dBm = 10.0 * (log10(rxp * 1000.0)); + power_density = + (eirp / + (pow + (10.0, (total_loss - free_space_loss) / 10.0))); + /* divide by 4*PI*distance_in_meters squared */ + power_density /= (4.0 * PI * distance * distance * + 2589988.11); + + fprintf(fd2, "Field strength at %s: %.2f dBuV/meter\n", + destination.name, field_strength); + fprintf(fd2, "Signal power level at %s: %+.2f dBm\n", + destination.name, dBm); + fprintf(fd2, + "Signal power density at %s: %+.2f dBW per square meter\n", + destination.name, 10.0 * log10(power_density)); + voltage = + 1.0e6 * sqrt(50.0 * + (eirp / + (pow + (10.0, + (total_loss - 2.14) / 10.0)))); + fprintf(fd2, + "Voltage across 50 ohm dipole at %s: %.2f uV (%.2f dBuV)\n", + destination.name, voltage, + 20.0 * log10(voltage)); + + voltage = + 1.0e6 * sqrt(75.0 * + (eirp / + (pow + (10.0, + (total_loss - 2.14) / 10.0)))); + fprintf(fd2, + "Voltage across 75 ohm dipole at %s: %.2f uV (%.2f dBuV)\n", + destination.name, voltage, + 20.0 * log10(voltage)); + } + + fprintf(fd2, "Mode of propagation: %s\n", strmode); + fprintf(fd2, "Longley-Rice model error number: %d", errnum); + + switch (errnum) { + case 0: + fprintf(fd2, " (No error)\n"); + break; + + case 1: + fprintf(fd2, + "\n Warning: Some parameters are nearly out of range.\n"); + fprintf(fd2, + " Results should be used with caution.\n"); + break; + + case 2: + fprintf(fd2, + "\n Note: Default parameters have been substituted for impossible ones.\n"); + break; + + case 3: + fprintf(fd2, + "\n Warning: A combination of parameters is out of range.\n"); + fprintf(fd2, " Results are probably invalid.\n"); + break; + + default: + fprintf(fd2, + "\n Warning: Some parameters are out of range.\n"); + fprintf(fd2, " Results are probably invalid.\n"); + } + + fprintf(fd2, "\n%s\n\n", dashes); + } + + ObstructionAnalysis(source, destination, LR.frq_mhz, fd2); + + fclose(fd2); + + fprintf(stdout, "\n%.2f", dBm); + fflush(stdout); + + /* Skip plotting the graph if ONLY a path-loss report is needed. */ + + if (graph_it) { + if (name[0] == '.') { + /* Default filename and output file type */ + + strncpy(basename, "profile\0", 8); + strncpy(term, "png\0", 4); + strncpy(ext, "png\0", 4); + } + + else { + /* Extract extension and terminal type from "name" */ + + ext[0] = 0; + y = strlen(name); + strncpy(basename, name, 254); + + for (x = y - 1; x > 0 && name[x] != '.'; x--) ; + + if (x > 0) { /* Extension found */ + for (z = x + 1; z <= y && (z - (x + 1)) < 10; + z++) { + ext[z - (x + 1)] = tolower(name[z]); + term[z - (x + 1)] = name[z]; + } + + ext[z - (x + 1)] = 0; /* Ensure an ending 0 */ + term[z - (x + 1)] = 0; + basename[x] = 0; + } + } + + if (ext[0] == 0) { /* No extension -- Default is png */ + strncpy(term, "png\0", 4); + strncpy(ext, "png\0", 4); + } + + /* Either .ps or .postscript may be used + as an extension for postscript output. */ + + if (strncmp(term, "postscript", 10) == 0) + strncpy(ext, "ps\0", 3); + + else if (strncmp(ext, "ps", 2) == 0) + strncpy(term, "postscript enhanced color\0", 26); + + fd = fopen("ppa.gp", "w"); + + fprintf(fd, "set grid\n"); + fprintf(fd, "set yrange [%2.3f to %2.3f]\n", minloss, maxloss); + fprintf(fd, "set encoding iso_8859_1\n"); + fprintf(fd, "set term %s\n", term); + fprintf(fd, + "set title \"Path Loss Profile Along Path Between %s and %s (%.2f%c azimuth)\"\n", + destination.name, source.name, Azimuth(destination, + source), 176); + + if (metric) + fprintf(fd, + "set xlabel \"Distance Between %s and %s (%.2f kilometers)\"\n", + destination.name, source.name, + KM_PER_MILE * Distance(destination, source)); + else + fprintf(fd, + "set xlabel \"Distance Between %s and %s (%.2f miles)\"\n", + destination.name, source.name, + Distance(destination, source)); + + if (got_azimuth_pattern || got_elevation_pattern) + fprintf(fd, + "set ylabel \"Total Path Loss (including TX antenna pattern) (dB)"); + else + fprintf(fd, "set ylabel \"Longley-Rice Path Loss (dB)"); + + fprintf(fd, "\"\nset output \"%s.%s\"\n", basename, ext); + fprintf(fd, + "plot \"profile.gp\" title \"Path Loss\" with lines\n"); + + fclose(fd); + + x = system("gnuplot ppa.gp"); + + if (x != -1) { + if (gpsav == 0) { + //unlink("ppa.gp"); + //unlink("profile.gp"); + //unlink("reference.gp"); + } + + } + + else + fprintf(stderr, + "\n*** ERROR: Error occurred invoking gnuplot!\n"); + } + +} + +void SeriesData(struct site source, struct site destination, char *name, + unsigned char fresnel_plot, unsigned char normalised) +{ + + int x, y, z; + char basename[255], term[30], ext[15], profilename[255], + referencename[255], cluttername[255], curvaturename[255], + fresnelname[255], fresnel60name[255]; + double a, b, c, height = 0.0, refangle, cangle, maxheight = + -100000.0, minheight = 100000.0, lambda = 0.0, f_zone = + 0.0, fpt6_zone = 0.0, nm = 0.0, nb = 0.0, ed = 0.0, es = 0.0, r = + 0.0, d = 0.0, d1 = 0.0, terrain, azimuth, distance, minterrain = + 100000.0, minearth = 100000.0; + struct site remote; + FILE *fd = NULL, *fd1 = NULL, *fd2 = NULL, *fd3 = NULL, *fd4 = + NULL, *fd5 = NULL; + + ReadPath(destination, source); + azimuth = Azimuth(destination, source); + distance = Distance(destination, source); + refangle = ElevationAngle(destination, source); + b = GetElevation(destination) + destination.alt + earthradius; + + if (fresnel_plot) { + lambda = 9.8425e8 / (LR.frq_mhz * 1e6); + d = 5280.0 * path.distance[path.length - 1]; + } + + if (normalised) { + ed = GetElevation(destination); + es = GetElevation(source); + nb = -destination.alt - ed; + nm = (-source.alt - es - nb) / (path.distance[path.length - 1]); + } + + strcpy(profilename, name); + strcat(profilename, "_profile\0"); + strcpy(referencename, name); + strcat(referencename, "_reference\0"); + strcpy(cluttername, name); + strcat(cluttername, "_clutter\0"); + strcpy(curvaturename, name); + strcat(curvaturename, "_curvature\0"); + strcpy(fresnelname, name); + strcat(fresnelname, "_fresnel\0"); + strcpy(fresnel60name, name); + strcat(fresnel60name, "_fresnel60\0"); + + fd = fopen(profilename, "wb"); + if (clutter > 0.0) + fd1 = fopen(cluttername, "wb"); + fd2 = fopen(referencename, "wb"); + fd5 = fopen(curvaturename, "wb"); + + if ((LR.frq_mhz >= 20.0) && (LR.frq_mhz <= 100000.0) && fresnel_plot) { + fd3 = fopen(fresnelname, "wb"); + fd4 = fopen(fresnel60name, "wb"); + } + + for (x = 0; x < path.length - 1; x++) { + remote.lat = path.lat[x]; + remote.lon = path.lon[x]; + remote.alt = 0.0; + terrain = GetElevation(remote); + if (x == 0) + terrain += destination.alt; /* RX antenna spike */ + + a = terrain + earthradius; + cangle = 5280.0 * Distance(destination, remote) / earthradius; + c = b * sin(refangle * DEG2RAD + HALFPI) / sin(HALFPI - + refangle * + DEG2RAD - + cangle); + height = a - c; + + /* Per Fink and Christiansen, Electronics + * Engineers' Handbook, 1989: + * + * H = sqrt(lamba * d1 * (d - d1)/d) + * + * where H is the distance from the LOS + * path to the first Fresnel zone boundary. + */ + + if ((LR.frq_mhz >= 20.0) && (LR.frq_mhz <= 100000.0) + && fresnel_plot) { + d1 = 5280.0 * path.distance[x]; + f_zone = -1.0 * sqrt(lambda * d1 * (d - d1) / d); + fpt6_zone = f_zone * fzone_clearance; + } + + if (normalised) { + r = -(nm * path.distance[x]) - nb; + height += r; + + if ((LR.frq_mhz >= 20.0) && (LR.frq_mhz <= 100000.0) + && fresnel_plot) { + f_zone += r; + fpt6_zone += r; + } + } + + else + r = 0.0; + + if (metric) { + if (METERS_PER_FOOT * height > 0) { + fprintf(fd, "%.3f %.3f\n", + KM_PER_MILE * path.distance[x], + METERS_PER_FOOT * height); + } + + if (fd1 != NULL && x > 0 && x < path.length - 2) + fprintf(fd1, "%.3f %.3f\n", + KM_PER_MILE * path.distance[x], + METERS_PER_FOOT * (terrain == + 0.0 ? height + : (height + + clutter))); + + fprintf(fd2, "%.3f %.3f\n", + KM_PER_MILE * path.distance[x], + METERS_PER_FOOT * r); + fprintf(fd5, "%.3f %.3f\n", + KM_PER_MILE * path.distance[x], + METERS_PER_FOOT * (height - terrain)); + + } + + else { + fprintf(fd, "%.3f %.3f\n", path.distance[x], height); + + if (fd1 != NULL && x > 0 && x < path.length - 2) + fprintf(fd1, "%.3f %.3f\n", path.distance[x], + (terrain == + 0.0 ? height : (height + clutter))); + + fprintf(fd2, "%.3f %.3f\n", path.distance[x], r); + fprintf(fd5, "%.3f %.3f\n", path.distance[x], + height - terrain); + } + + if ((LR.frq_mhz >= 20.0) && (LR.frq_mhz <= 100000.0) + && fresnel_plot) { + if (metric) { + fprintf(fd3, "%.3f %.3f\n", + KM_PER_MILE * path.distance[x], + METERS_PER_FOOT * f_zone); + fprintf(fd4, "%.3f %.3f\n", + KM_PER_MILE * path.distance[x], + METERS_PER_FOOT * fpt6_zone); + } + + else { + fprintf(fd3, "%.3f %.3f\n", path.distance[x], + f_zone); + fprintf(fd4, "%.3f %.3f\n", path.distance[x], + fpt6_zone); + } + + if (f_zone < minheight) + minheight = f_zone; + } + + if ((height + clutter) > maxheight) + maxheight = height + clutter; + + if (height < minheight) + minheight = height; + + if (r > maxheight) + maxheight = r; + + if (terrain < minterrain) + minterrain = terrain; + + if ((height - terrain) < minearth) + minearth = height - terrain; + } // End of loop + + if (normalised) + r = -(nm * path.distance[path.length - 1]) - nb; + else + r = 0.0; + + if (metric) { + fprintf(fd, "%.3f %.3f", + KM_PER_MILE * path.distance[path.length - 1], + METERS_PER_FOOT * r); + fprintf(fd2, "%.3f %.3f", + KM_PER_MILE * path.distance[path.length - 1], + METERS_PER_FOOT * r); + } + + else { + fprintf(fd, "%.3f %.3f", path.distance[path.length - 1], r); + fprintf(fd2, "%.3f %.3f", path.distance[path.length - 1], r); + } + + if ((LR.frq_mhz >= 20.0) && (LR.frq_mhz <= 100000.0) && fresnel_plot) { + if (metric) { + fprintf(fd3, "%.3f %.3f", + KM_PER_MILE * path.distance[path.length - 1], + METERS_PER_FOOT * r); + fprintf(fd4, "%.3f %.3f", + KM_PER_MILE * path.distance[path.length - 1], + METERS_PER_FOOT * r); + } + + else { + fprintf(fd3, "%.3f %.3f", + path.distance[path.length - 1], r); + fprintf(fd4, "%.3f %.3f", + path.distance[path.length - 1], r); + } + } + + if (r > maxheight) + maxheight = r; + + if (r < minheight) + minheight = r; + + fclose(fd); + + if (fd1 != NULL) + fclose(fd1); + + fclose(fd2); + fclose(fd5); + + if ((LR.frq_mhz >= 20.0) && (LR.frq_mhz <= 100000.0) && fresnel_plot) { + fclose(fd3); + fclose(fd4); + } + + if (name[0] == '.') { + strncpy(basename, "profile\0", 8); + strncpy(term, "png\0", 4); + strncpy(ext, "png\0", 4); + } + + else { + + ext[0] = 0; + y = strlen(name); + strncpy(basename, name, 254); + + for (x = y - 1; x > 0 && name[x] != '.'; x--) ; + + if (x > 0) { + for (z = x + 1; z <= y && (z - (x + 1)) < 10; z++) { + ext[z - (x + 1)] = tolower(name[z]); + term[z - (x + 1)] = name[z]; + } + + ext[z - (x + 1)] = 0; + term[z - (x + 1)] = 0; + basename[x] = 0; + } + + if (ext[0] == 0) { + strncpy(term, "png\0", 4); + strncpy(ext, "png\0", 4); + } + } + + fprintf(stdout, "\n"); + fflush(stdout); + +} + +int main(int argc, char *argv[]) +{ + int x, y, z = 0, min_lat, min_lon, max_lat, max_lon, + rxlat, rxlon, txlat, txlon, west_min, west_max, + north_min, north_max, propmodel, winfiles, knifeedge = 0, ppa = + 0, normalise = 0, haf = 0; + + unsigned char LRmap = 0, txsites = 0, topomap = 0, geo = 0, kml = + 0, area_mode = 0, max_txsites, ngs = 0; + + char mapfile[255], longley_file[255], udt_file[255], ano_filename[255]; + + double altitude = 0.0, altitudeLR = 0.0, tx_range = 0.0, + rx_range = 0.0, deg_range = 0.0, deg_limit = 0.0, deg_range_lon; + + strncpy(ss_name, "Signal Server\0", 14); + + if (argc == 1) { + fprintf(stdout, "\n\t\t -- %s %.2f options --\n\n", ss_name, + version); + fprintf(stdout, " -d Directory containing .sdf tiles\n"); + fprintf(stdout, + " -lat Tx Latitude (decimal degrees) -70/+70\n"); + fprintf(stdout, + " -lon Tx Longitude (decimal degrees) -180/+180\n"); + fprintf(stdout, " -txh Tx Height (above ground)\n"); + fprintf(stdout, + " -rla (Optional) Rx Latitude for PPA (decimal degrees) -70/+70\n"); + fprintf(stdout, + " -rlo (Optional) Rx Longitude for PPA (decimal degrees) -180/+180\n"); + fprintf(stdout, + " -f Tx Frequency (MHz) 20MHz to 100GHz (LOS after 20GHz)\n"); + fprintf(stdout, + " -erp Tx Effective Radiated Power (Watts)\n"); + fprintf(stdout, + " -rxh Rx Height(s) (optional. Default=0.1)\n"); + fprintf(stdout, " -rt Rx Threshold (dB / dBm / dBuV/m)\n"); + fprintf(stdout, + " -hp Horizontal Polarisation (default=vertical)\n"); + fprintf(stdout, " -gc Ground clutter (feet/meters)\n"); + fprintf(stdout, " -udt User defined terrain filename\n"); + fprintf(stdout, + " -dbm Plot Rxd signal power instead of field strength\n"); + fprintf(stdout, " -m Metric units of measurement\n"); + fprintf(stdout, " -te Terrain code 1-6 (optional)\n"); + fprintf(stdout, + " -terdic Terrain dielectric value 2-80 (optional)\n"); + fprintf(stdout, + " -tercon Terrain conductivity 0.01-0.0001 (optional)\n"); + fprintf(stdout, " -cl Climate code 1-6 (optional)\n"); + fprintf(stdout, " -o Filename. Required. \n"); + fprintf(stdout, " -R Radius (miles/kilometers)\n"); + fprintf(stdout, + " -res Pixels per degree. 300/600/1200(default)/3600 (optional)\n"); + fprintf(stdout, " -t Terrain background\n"); + fprintf(stdout, + " -pm Prop model. 1: ITM, 2: LOS, 3-5: Hata, 6: COST231, 7: ITU525, 8: ITWOM3.0\n"); + fprintf(stdout, + " -ked Knife edge diffraction (Default for ITM)\n"); + fprintf(stdout, " -ng Normalise Path Profile graph\n"); + fprintf(stdout, " -haf Halve 1 or 2 (optional)\n"); + + fflush(stdout); + + return 1; + } + + y = argc - 1; + + kml = 0; + geo = 0; + dbm = 0; + gpsav = 0; + metric = 0; + //rxfile[0]=0; + //txfile[0]=0; + string[0] = 0; + mapfile[0] = 0; + clutter = 0.0; + forced_erp = -1.0; + forced_freq = 0.0; + //elevation_file[0]=0; + //terrain_file[0]=0; + sdf_path[0] = 0; + udt_file[0] = 0; + path.length = 0; + max_txsites = 30; + fzone_clearance = 0.6; + contour_threshold = 0; + + longley_file[0] = 0; + ano_filename[0] = 0; + //ani_filename[0]=0; + earthradius = EARTHRADIUS; + max_range = 1.0; + propmodel = 1; //ITM + winfiles = 0; + + lat = 0; + lon = 0; + txh = 0; + ngs = 1; // no terrain background + kml = 1; + + //map=1; + LRmap = 1; + area_mode = 1; + ippd = IPPD; // default resolution + + sscanf("0.1", "%lf", &altitudeLR); + + // Defaults + LR.eps_dielect = 15.0; // Farmland + LR.sgm_conductivity = 0.005; // Farmland + LR.eno_ns_surfref = 301.0; + LR.frq_mhz = 19.0; // Deliberately too low + LR.radio_climate = 5; // continental + LR.pol = 1; // vert + LR.conf = 0.50; + LR.rel = 0.50; + LR.erp = 0.0; // will default to Path Loss + + tx_site[0].lat = 91.0; + tx_site[0].lon = 361.0; + tx_site[1].lat = 91.0; + tx_site[1].lon = 361.0; + + for (x = 0; x < MAXPAGES; x++) { + 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], "-res") == 0) { + z = x + 1; + + if (z <= y && argv[z][0] && argv[z][0] != '-') { + + sscanf(argv[z], "%d", &ippd); + + switch (ippd) { + case 300: + MAXRAD = 300; + jgets = 3; + break; + + case 600: + MAXRAD = 150; + jgets = 1; + break; + + case 3600: + MAXRAD = 100; + ippd = 3600; + jgets = 0; + break; + + default: + MAXRAD = 100; + ippd = 1200; + jgets = 0; + break; + } + } + } + if (strcmp(argv[x], "-R") == 0) { + z = x + 1; + + if (z <= y && argv[z][0] && argv[z][0] != '-') { + sscanf(argv[z], "%lf", &max_range); + + } + } + + if (strcmp(argv[x], "-gc") == 0) { + z = x + 1; + + if (z <= y && argv[z][0] && argv[z][0] != '-') { + sscanf(argv[z], "%lf", &clutter); + + if (clutter < 0.0) + clutter = 0.0; + } + } + + if (strcmp(argv[x], "-o") == 0) { + z = x + 1; + + if (z <= y && argv[z][0] && argv[z][0] != '-') { + strncpy(mapfile, argv[z], 253); + strncpy(tx_site[0].name, "Tx", 2); + strncpy(tx_site[0].filename, argv[z], 253); + LoadPAT(argv[z]); + + } + //map=1; + } + + if (strcmp(argv[x], "-rt") == 0) { + z = x + 1; + + if (z <= y && argv[z][0]) /* A minus argument is legal here */ + sscanf(argv[z], "%d", &contour_threshold); + } + + if (strcmp(argv[x], "-m") == 0) { + metric = 1; + + } + + if (strcmp(argv[x], "-t") == 0) { + ngs = 0; // greyscale background + } + + if (strcmp(argv[x], "-dbm") == 0) + dbm = 1; + + if (strcmp(argv[x], "-d") == 0) { + z = x + 1; + + if (z <= y && argv[z][0] && argv[z][0] != '-') + strncpy(sdf_path, argv[z], 253); + } + + if (strcmp(argv[x], "-lat") == 0) { + z = x + 1; + + if (z <= y && argv[z][0]) { + tx_site[0].lat = ReadBearing(argv[z]); + } + } + if (strcmp(argv[x], "-lon") == 0) { + z = x + 1; + if (z <= y && argv[z][0]) { + tx_site[0].lon = ReadBearing(argv[z]); + tx_site[0].lon *= -1; + if (tx_site[0].lon < 0.0) + tx_site[0].lon += 360.0; + } + } + //Switch to Path Profile Mode if Rx co-ords specified + if (strcmp(argv[x], "-rla") == 0) { + z = x + 1; + + if (z <= y && argv[z][0]) { + ppa = 1; + tx_site[1].lat = ReadBearing(argv[z]); + + } + } + if (strcmp(argv[x], "-rlo") == 0) { + z = x + 1; + if (z <= y && argv[z][0]) { + tx_site[1].lon = ReadBearing(argv[z]); + tx_site[1].lon *= -1; + if (tx_site[1].lon < 0.0) + tx_site[1].lon += 360.0; + } + } + + if (strcmp(argv[x], "-txh") == 0) { + z = x + 1; + + if (z <= y && argv[z][0] && argv[z][0] != '-') { + sscanf(argv[z], "%f", &tx_site[0].alt); + + } + txsites = 1; + } + + if (strcmp(argv[x], "-rxh") == 0) { + z = x + 1; + + if (z <= y && argv[z][0] && argv[z][0] != '-') { + sscanf(argv[z], "%lf", &altitudeLR); + sscanf(argv[z], "%f", &tx_site[1].alt); + } + } + + if (strcmp(argv[x], "-f") == 0) { + z = x + 1; + + if (z <= y && argv[z][0] && argv[z][0] != '-') { + sscanf(argv[z], "%lf", &LR.frq_mhz); + } + } + + if (strcmp(argv[x], "-erp") == 0) { + z = x + 1; + + if (z <= y && argv[z][0] && argv[z][0] != '-') { + sscanf(argv[z], "%lf", &LR.erp); + } + } + + if (strcmp(argv[x], "-cl") == 0) { + z = x + 1; + + if (z <= y && argv[z][0] && argv[z][0] != '-') { + + sscanf(argv[z], "%d", &LR.radio_climate); + + } + } + if (strcmp(argv[x], "-te") == 0) { + z = x + 1; + + if (z <= y && argv[z][0] && argv[z][0] != '-') { + + sscanf(argv[z], "%d", &ter); + + switch (ter) { + case 1: // Water + terdic = 80; + tercon = 0.010; + break; + + case 2: // Marsh + terdic = 12; + tercon = 0.007; + break; + + case 3: // Farmland + terdic = 15; + tercon = 0.005; + break; + + case 4: //Mountain + terdic = 13; + tercon = 0.002; + break; + case 5: //Desert + terdic = 13; + tercon = 0.002; + break; + case 6: //Urban + terdic = 5; + tercon = 0.001; + break; + } + LR.eps_dielect = terdic; + LR.sgm_conductivity = tercon; + + } + } + + if (strcmp(argv[x], "-terdic") == 0) { + z = x + 1; + + if (z <= y && argv[z][0] && argv[z][0] != '-') { + + sscanf(argv[z], "%lf", &terdic); + + LR.eps_dielect = terdic; + + } + } + + if (strcmp(argv[x], "-tercon") == 0) { + z = x + 1; + + if (z <= y && argv[z][0] && argv[z][0] != '-') { + + sscanf(argv[z], "%lf", &tercon); + + LR.sgm_conductivity = tercon; + + } + } + + if (strcmp(argv[x], "-hp") == 0) { + // Horizontal polarisation (0) + LR.pol = 0; + } + + if (strcmp(argv[x], "-dbg") == 0) { + debug = 1; + } + + ppd = (double)ippd; /* pixels per degree (double) */ + dpp = 1.0 / ppd; /* degrees per pixel */ + mpi = ippd - 1; /* maximum pixel index per degree */ + + /*UDT*/ if (strcmp(argv[x], "-udt") == 0) { + z = x + 1; + + if (z <= y && argv[z][0]) { + strncpy(udt_file, argv[z], 253); + } + } + + /*Prop model */ + + if (strcmp(argv[x], "-pm") == 0) { + z = x + 1; + + if (z <= y && argv[z][0]) { + sscanf(argv[z], "%d", &propmodel); + } + } + //Knife edge diffraction + if (strcmp(argv[x], "-ked") == 0) { + z = x + 1; + knifeedge = 1; + } + //Windows friendly SDF filenames + if (strcmp(argv[x], "-wf") == 0) { + z = x + 1; + winfiles = 1; + } + //Normalise Path Profile chart + if (strcmp(argv[x], "-ng") == 0) { + z = x + 1; + normalise = 1; + } + //Halve the problem + if (strcmp(argv[x], "-haf") == 0) { + z = x + 1; + if (z <= y && argv[z][0]) { + sscanf(argv[z], "%d", &haf); + } + } + + } + + /* ERROR DETECTION */ + if (tx_site[0].lat > 90 || tx_site[0].lat < -90) { + fprintf(stdout, + "ERROR: Either the lat was missing or out of range!"); + exit(0); + + } + if (tx_site[0].lon > 360 || tx_site[0].lon < 0) { + fprintf(stdout, + "ERROR: Either the lon was missing or out of range!"); + exit(0); + + } + + if (LR.frq_mhz < 20 || LR.frq_mhz > 100000) { + fprintf(stdout, + "ERROR: Either the Frequency was missing or out of range!"); + exit(0); + } + if (LR.erp > 5000000) { + fprintf(stdout, "ERROR: Power was out of range!"); + exit(0); + + } + if (LR.eps_dielect > 80 || LR.eps_dielect < 0.1) { + fprintf(stdout, "ERROR: Ground Dielectric value out of range!"); + exit(0); + + } + if (LR.sgm_conductivity > 0.01 || LR.sgm_conductivity < 0.000001) { + fprintf(stdout, "ERROR: Ground conductivity out of range!"); + exit(0); + + } + + if (tx_site[0].alt < 0 || tx_site[0].alt > 60000) { + fprintf(stdout, + "ERROR: Tx altitude above ground was too high: %f", + tx_site[0].alt); + exit(0); + } + if (altitudeLR < 0 || altitudeLR > 60000) { + fprintf(stdout, + "ERROR: Rx altitude above ground was too high!"); + exit(0); + } + + if (ippd < 300 || ippd > 3600) { + fprintf(stdout, "ERROR: resolution out of range!"); + exit(0); + } + + if (contour_threshold < -200 || contour_threshold > 200) { + fprintf(stdout, + "ERROR: Receiver threshold out of range (-200 / +200)"); + exit(0); + } + if (propmodel > 2 && propmodel < 8 && LR.frq_mhz < 150) { + fprintf(stdout, + "ERROR: Frequency too low for Propagation model"); + exit(0); + } + + /* ERROR DETECTION COMPLETE */ + + if (metric) { + altitudeLR /= METERS_PER_FOOT; /* 10ft * 0.3 = 3.3m */ + max_range /= KM_PER_MILE; /* 10 / 1.6 = 7.5 */ + altitude /= METERS_PER_FOOT; + tx_site[0].alt /= METERS_PER_FOOT; /* Feet to metres */ + tx_site[1].alt /= METERS_PER_FOOT; /* Feet to metres */ + clutter /= METERS_PER_FOOT; /* Feet to metres */ + } + + /* Ensure a trailing '/' is present in sdf_path */ + + if (sdf_path[0]) { + x = strlen(sdf_path); + + if (sdf_path[x - 1] != '/' && x != 0) { + sdf_path[x] = '/'; + sdf_path[x + 1] = 0; + } + } + + x = 0; + y = 0; + + min_lat = 70; + max_lat = -70; + + min_lon = (int)floor(tx_site[0].lon); + max_lon = (int)floor(tx_site[0].lon); + + txlat = (int)floor(tx_site[0].lat); + txlon = (int)floor(tx_site[0].lon); + + 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 (ppa == 1) { + rxlat = (int)floor(tx_site[1].lat); + rxlon = (int)floor(tx_site[1].lon); + + if (rxlat < min_lat) + min_lat = rxlat; + + 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; + } + + /* Load the required SDF files */ + + LoadTopoData(max_lon, min_lon, max_lat, min_lat, winfiles); + + if (area_mode || topomap) { + for (z = 0; z < txsites && z < max_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]))); + + 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 */ + + deg_range = (tx_range + rx_range) / 57.0; + + /* max_range regulates the 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 + ss A large number will increase the + width of the analysis and the size of + the map. */ + + if (max_range == 0.0) + max_range = tx_range + rx_range; + + deg_range = max_range / 57.0; + + // No more than 8 degs + deg_limit = 3.5; + + if (fabs(tx_site[z].lat) < 70.0) + deg_range_lon = + deg_range / cos(DEG2RAD * tx_site[z].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 = (int)floor(tx_site[z].lat - deg_range); + north_max = (int)floor(tx_site[z].lat + deg_range); + + west_min = (int)floor(tx_site[z].lon - deg_range_lon); + + while (west_min < 0) + west_min += 360; + + while (west_min >= 360) + west_min -= 360; + + west_max = (int)floor(tx_site[z].lon + deg_range_lon); + + while (west_max < 0) + west_max += 360; + + while (west_max >= 360) + west_max -= 360; + + if (north_min < min_lat) + min_lat = north_min; + + if (north_max > max_lat) + max_lat = north_max; + + if (LonDiff(west_min, min_lon) < 0.0) + min_lon = west_min; + + if (LonDiff(west_max, max_lon) >= 0.0) + max_lon = west_max; + } + + /* Load any additional SDF files, if required */ + + LoadTopoData(max_lon, min_lon, max_lat, min_lat, winfiles); + } + // UDT clutter + LoadUDT(udt_file); + + if (ppa == 0) { + if (propmodel == 2) { + PlotLOSMap(tx_site[0], altitudeLR, ano_filename); + DoLOS(mapfile, geo, kml, ngs, tx_site, txsites); + } else { + // 90% of effort here + PlotPropagation(tx_site[0], altitudeLR, ano_filename, + propmodel, knifeedge, haf); + + // Near field bugfix + PutSignal(tx_site[0].lat, tx_site[0].lon, hottest); + for (lat = tx_site[0].lat - 0.002; + lat <= tx_site[0].lat + 0.002; + lat = lat + 0.0005) { + for (lon = tx_site[0].lon - 0.002; + lon <= tx_site[0].lon + 0.002; + lon = lon + 0.0005) { + PutSignal(lat, lon, hottest); + } + } + + if (LR.erp == 0.0) + DoPathLoss(mapfile, geo, kml, ngs, tx_site, + txsites); + else if (dbm) + DoRxdPwr(mapfile, geo, kml, ngs, tx_site, + txsites); + else + DoSigStr(mapfile, geo, kml, ngs, tx_site, + txsites); + + } + fprintf(stdout, "|%.5f", north); + fprintf(stdout, "|%.5f", east); + fprintf(stdout, "|%.5f", south); + fprintf(stdout, "|%.5f|", west); + + } else { + strncpy(tx_site[0].name, "Tx", 3); + strncpy(tx_site[1].name, "Rx", 3); + PlotPath(tx_site[0], tx_site[1], 1); + PathReport(tx_site[0], tx_site[1], tx_site[0].filename, 0); + SeriesData(tx_site[0], tx_site[1], tx_site[0].filename, 1, + normalise); + } + fflush(stdout); + printf("\n"); + return 0; +} diff --git a/mainHD.cpp b/mainHD.cpp deleted file mode 100644 index f133ae8..0000000 --- a/mainHD.cpp +++ /dev/null @@ -1,5720 +0,0 @@ -double version=2.31; -/****************************************************************************\ -* Signal Server: Server optimised SPLAT! by Alex Farrant * -****************************************************************************** -* SPLAT! Project started in 1997 by John A. Magliacane, KD2BD * -* * -****************************************************************************** -* Please consult the SPLAT! documentation for a complete list of * -* individuals who have contributed to this project. * -****************************************************************************** -* * -* 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 * -* version. * -* * -* This program is distributed in the hope that it will useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * -* for more details. * -* * -****************************************************************************** -* g++ -Wall -O3 -s -lm -fomit-frame-pointer itwom3.0.cpp hata.cpp cost.cpp fspl.cpp main.cpp -o ss * -\****************************************************************************/ -/* -2.31 - ERP up to 5MW for Mexican TV! -2.3 - Added ITWOM3.0 -2.22 - Fixed LOS not outputting bounds -2.2 - Made .dot output opt in to save some disk space -2.1 - Added dual core support with -haf -*/ -#include -#include -#include -#include -#include -#include - -#define MAXPAGES 9 -#define ARRAYSIZE 14844 -#define IPPD 3600 -#define GAMMA 2.5 - -#ifndef PI -#define PI 3.141592653589793 -#endif - -#ifndef TWOPI -#define TWOPI 6.283185307179586 -#endif - -#ifndef HALFPI -#define HALFPI 1.570796326794896 -#endif - -#define DEG2RAD 1.74532925199e-02 -#define EARTHRADIUS 20902230.97 -#define METERS_PER_MILE 1609.344 -#define METERS_PER_FOOT 0.3048 -#define KM_PER_MILE 1.609344 -#define FOUR_THIRDS 1.3333333333333 - -char string[255], sdf_path[255], udt_file[255], opened=0, gpsav=0, ss_name[16], dashes[80]; - -double earthradius, max_range=0.0, forced_erp, dpp, ppd, - fzone_clearance=0.6, forced_freq, clutter, lat,lon,txh,tercon,terdic, - north,east,south,west,dBm,loss,field_strength; - -int min_north=90, max_north=-90, min_west=360, max_west=-1, ippd, mpi, - max_elevation=-32768, min_elevation=32768, bzerror, contour_threshold, - pred,pblue,pgreen,ter,multiplier=256,debug=0,loops=64,jgets=0, MAXRAD, hottest=10; - -unsigned char got_elevation_pattern, got_azimuth_pattern, metric=0, dbm=0; - - - -struct site { double lat; - double lon; - float alt; - char name[50]; - char filename[255]; - } site; - -struct path { double lat[ARRAYSIZE]; - double lon[ARRAYSIZE]; - double elevation[ARRAYSIZE]; - double 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[IPPD][IPPD]; - unsigned char mask[IPPD][IPPD]; - unsigned char signal[IPPD][IPPD]; - } dem[MAXPAGES]; - -struct LR { double eps_dielect; - double sgm_conductivity; - double eno_ns_surfref; - double frq_mhz; - double conf; - double rel; - double erp; - int radio_climate; - int pol; - float antenna_pattern[361][1001]; - } LR; - -struct region { unsigned char color[128][3]; - int level[128]; - int levels; - } region; - -double elev[ARRAYSIZE+10]; - -struct site tx_site[2]; - -//ITWOM -void point_to_point(double elev[], double tht_m, double rht_m, - double eps_dielect, double sgm_conductivity, double eno_ns_surfref, - double frq_mhz, int radio_climate, int pol, double conf, - double rel, double &dbloss, char *strmode, int &errnum); -//ITM -void point_to_point_ITM(double elev[], double tht_m, double rht_m, - double eps_dielect, double sgm_conductivity, double eno_ns_surfref, - double frq_mhz, int radio_climate, int pol, double conf, - double rel, double &dbloss, char *strmode, int &errnum); - -double HataLinkdB(float f,float h_B, float h_M, float d, int mode); - -double CostHataLinkdB(float f,float h_B, float h_M, float d); - -double FsplLinkdB(float f, float d); - -double ked(double freq, double elev[], double rxh, double dkm); - -double arccos(double x, double y) -{ - /* This function implements the arc cosine function, - returning a value between 0 and TWOPI. */ - - double result=0.0; - - if (y>0.0) - result=acos(x/y); - - if (y<0.0) - result=PI+acos(x/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); -} - -double LonDiff(double lon1, double 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. */ - - double diff; - - diff=lon1-lon2; - - if (diff<=-180.0) - diff+=360.0; - - if (diff>=180.0) - diff-=360.0; - - return diff; -} - -char *dec2dms(double decimal) -{ - /* Converts decimal degrees to degrees, minutes, seconds, - (DMS) and returns the result as a character string. */ - - char sign; - int degrees, minutes, seconds; - double a, b, c, d; - - if (decimal<0.0) - { - decimal=-decimal; - sign=-1; - } - - else - sign=1; - - a=floor(decimal); - b=60.0*(decimal-a); - c=floor(b); - d=60.0*(b-c); - - degrees=(int)a; - minutes=(int)c; - seconds=(int)d; - - if (seconds<0) - seconds=0; - - if (seconds>59) - seconds=59; - - string[0]=0; - snprintf(string,250,"%d%c %d\' %d\"", degrees*sign, 176, minutes, seconds); - return (string); -} - -int PutMask(double lat, double lon, int value) -{ - /* Lines, text, markings, and coverage areas are stored in a - mask that is combined with topology data when topographic - maps are generated by ss. This function sets and resets - bits in the mask based on the latitude and longitude of the - area pointed to. */ - - int x, y, indx; - char found; - - for (indx=0, found=0; indx=0 && x<=mpi && y>=0 && y<=mpi) - found=1; - else - indx++; - } - - if (found) - { - dem[indx].mask[x][y]=value; - return ((int)dem[indx].mask[x][y]); - } - - else - return -1; -} - -int OrMask(double lat, double lon, int value) -{ - /* Lines, text, markings, and coverage areas are stored in a - mask that is combined with topology data when topographic - maps are generated by ss. This function sets bits in - the mask based on the latitude and longitude of the area - pointed to. */ - - int x, y, indx; - char found; - - for (indx=0, found=0; indx=0 && x<=mpi && y>=0 && y<=mpi) - found=1; - else - indx++; - } - - if (found) - { - dem[indx].mask[x][y]|=value; - return ((int)dem[indx].mask[x][y]); - } - - else - return -1; -} - -int GetMask(double lat, double lon) -{ - /* This function returns the mask bits based on the latitude - and longitude given. */ - - return (OrMask(lat,lon,0)); -} - -int PutSignal(double lat, double lon, unsigned char signal) -{ - /* This function writes a signal level (0-255) - at the specified location for later recall. */ - char dotfile[255]; - FILE *fd=NULL; - snprintf(dotfile,80,"%s.dot%c",tx_site[0].filename,0); - - int x, y, indx; - char found; - - if(signal > hottest) - hottest=signal; - - //lookup x/y for this co-ord - for (indx=0, found=0; indx=0 && x<=mpi && y>=0 && y<=mpi) - found=1; - else - indx++; - } - - - if (found) - { - // Write values to file - dem[indx].signal[x][y]=signal; - - return (dem[indx].signal[x][y]); - } - - else - return 0; -} - -unsigned char GetSignal(double lat, double lon) -{ - /* This function reads the signal level (0-255) at the - specified location that was previously written by the - complimentary PutSignal() function. */ - - int x, y, indx; - char found; - - for (indx=0, found=0; indx=0 && x<=mpi && y>=0 && y<=mpi) - found=1; - else - indx++; - } - - if (found) - return (dem[indx].signal[x][y]); - else - return 0; -} - -double GetElevation(struct site location) -{ - /* This function returns the elevation (in feet) of any location - represented by the digital elevation model data in memory. - Function returns -5000.0 for locations not found in memory. */ - - char found; - int x, y, indx; - double elevation; - - for (indx=0, found=0; indx=0 && x<=mpi && y>=0 && y<=mpi) - found=1; - else - indx++; - } - - if (found) - elevation=3.28084*dem[indx].data[x][y]; - else - elevation=-5000.0; - - return elevation; -} - -int AddElevation(double lat, double lon, double height) -{ - /* This function adds a user-defined terrain feature - (in meters AGL) to the digital elevation model data - in memory. Does nothing and returns 0 for locations - not found in memory. */ - - char found; - int x, y, indx; - - - for (indx=0, found=0; indx=0 && x<=mpi && y>=0 && y<=mpi) - found=1; - else - indx++; - } - - if (found) - dem[indx].data[x][y]+=(short)rint(height); - - return found; -} - -double Distance(struct site site1, struct site site2) -{ - /* This function returns the great circle distance - in miles between any two site locations. */ - - double lat1, lon1, lat2, lon2, distance; - - lat1=site1.lat*DEG2RAD; - lon1=site1.lon*DEG2RAD; - lat2=site2.lat*DEG2RAD; - lon2=site2.lon*DEG2RAD; - - distance=3959.0*acos(sin(lat1)*sin(lat2)+cos(lat1)*cos(lat2)*cos((lon1)-(lon2))); - - return distance; -} - -double Azimuth(struct site source, struct site destination) -{ - /* This function returns the azimuth (in degrees) to the - destination as seen from the location of the source. */ - - double dest_lat, dest_lon, src_lat, src_lon, - beta, azimuth, diff, num, den, fraction; - - dest_lat=destination.lat*DEG2RAD; - dest_lon=destination.lon*DEG2RAD; - - src_lat=source.lat*DEG2RAD; - src_lon=source.lon*DEG2RAD; - - /* Calculate Surface Distance */ - - beta=acos(sin(src_lat)*sin(dest_lat)+cos(src_lat)*cos(dest_lat)*cos(src_lon-dest_lon)); - - /* Calculate Azimuth */ - - num=sin(dest_lat)-(sin(src_lat)*cos(beta)); - den=cos(src_lat)*sin(beta); - fraction=num/den; - - /* Trap potential problems in acos() due to rounding */ - - if (fraction>=1.0) - fraction=1.0; - - if (fraction<=-1.0) - fraction=-1.0; - - /* Calculate azimuth */ - - azimuth=acos(fraction); - - /* Reference it to True North */ - - diff=dest_lon-src_lon; - - if (diff<=-PI) - diff+=TWOPI; - - if (diff>=PI) - diff-=TWOPI; - - if (diff>0.0) - azimuth=TWOPI-azimuth; - - return (azimuth/DEG2RAD); -} - -double ElevationAngle(struct site source, struct site destination) -{ - /* This function returns the angle of elevation (in degrees) - of the destination as seen from the source location. - A positive result represents an angle of elevation (uptilt), - while a negative result represents an angle of depression - (downtilt), as referenced to a normal to the center of - the earth. */ - - register double a, b, dx; - - a=GetElevation(destination)+destination.alt+earthradius; - b=GetElevation(source)+source.alt+earthradius; - - dx=5280.0*Distance(source,destination); - - /* Apply the Law of Cosines */ - - return ((180.0*(acos(((b*b)+(dx*dx)-(a*a))/(2.0*b*dx)))/PI)-90.0); -} - -void ReadPath(struct site source, struct site destination) -{ - /* This function generates a sequence of latitude and - longitude positions between source and destination - locations along a great circle path, and stores - elevation and distance information for points - along that path in the "path" structure. */ - - int c; - double azimuth, distance, lat1, lon1, beta, den, num, - lat2, lon2, total_distance, dx, dy, path_length, - miles_per_sample, samples_per_radian=68755.0; - struct site tempsite; - - lat1=source.lat*DEG2RAD; - lon1=source.lon*DEG2RAD; - - lat2=destination.lat*DEG2RAD; - lon2=destination.lon*DEG2RAD; - - samples_per_radian=ppd*57.295833; - - azimuth=Azimuth(source,destination)*DEG2RAD; - - total_distance=Distance(source,destination); - - if (total_distance>(30.0/ppd)) - { - dx=samples_per_radian*acos(cos(lon1-lon2)); - dy=samples_per_radian*acos(cos(lat1-lat2)); - - path_length=sqrt((dx*dx)+(dy*dy)); - - miles_per_sample=total_distance/path_length; - } - - else - { - c=0; - dx=0.0; - dy=0.0; - path_length=0.0; - miles_per_sample=0.0; - total_distance=0.0; - - lat1=lat1/DEG2RAD; - lon1=lon1/DEG2RAD; - - path.lat[c]=lat1; - path.lon[c]=lon1; - path.elevation[c]=GetElevation(source); - path.distance[c]=0.0; - } - - for (distance=0.0, c=0; (total_distance!=0.0 && distance<=total_distance && cHALFPI-lat1)) - lon2=lon1+PI; - - else if (azimuth==HALFPI && (beta>HALFPI+lat1)) - lon2=lon1+PI; - - else if (fabs(num/den)>1.0) - lon2=lon1; - - else - { - if ((PI-azimuth)>=0.0) - lon2=lon1-arccos(num,den); - else - lon2=lon1+arccos(num,den); - } - - while (lon2<0.0) - lon2+=TWOPI; - - while (lon2>TWOPI) - lon2-=TWOPI; - - lat2=lat2/DEG2RAD; - lon2=lon2/DEG2RAD; - - path.lat[c]=lat2; - path.lon[c]=lon2; - tempsite.lat=lat2; - tempsite.lon=lon2; - path.elevation[c]=GetElevation(tempsite); - path.distance[c]=distance; - } - - /* Make sure exact destination point is recorded at path.length-1 */ - - if (c=cos_test_angle) - { - block=1; - first_obstruction_angle=((acos(cos_test_angle))/DEG2RAD)-90.0; - } - } - - if (block) - elevation=first_obstruction_angle; - - else - elevation=((acos(cos_xmtr_angle))/DEG2RAD)-90.0; - - path=temp; - - return elevation; -} - -double AverageTerrain(struct site source, double azimuthx, double start_distance, double end_distance) -{ - /* This function returns the average terrain calculated in - the direction of "azimuth" (degrees) between "start_distance" - and "end_distance" (miles) from the source location. If - the terrain is all water (non-critical error), -5000.0 is - returned. If not enough SDF data has been loaded into - memory to complete the survey (critical error), then - -9999.0 is returned. */ - - int c, samples, endpoint; - double beta, lat1, lon1, lat2, lon2, num, den, azimuth, terrain=0.0; - struct site destination; - - lat1=source.lat*DEG2RAD; - lon1=source.lon*DEG2RAD; - - /* Generate a path of elevations between the source - location and the remote location provided. */ - - beta=end_distance/3959.0; - - azimuth=DEG2RAD*azimuthx; - - lat2=asin(sin(lat1)*cos(beta)+cos(azimuth)*sin(beta)*cos(lat1)); - num=cos(beta)-(sin(lat1)*sin(lat2)); - den=cos(lat1)*cos(lat2); - - if (azimuth==0.0 && (beta>HALFPI-lat1)) - lon2=lon1+PI; - - else if (azimuth==HALFPI && (beta>HALFPI+lat1)) - lon2=lon1+PI; - - else if (fabs(num/den)>1.0) - lon2=lon1; - - else - { - if ((PI-azimuth)>=0.0) - lon2=lon1-arccos(num,den); - else - lon2=lon1+arccos(num,den); - } - - while (lon2<0.0) - lon2+=TWOPI; - - while (lon2>TWOPI) - lon2-=TWOPI; - - lat2=lat2/DEG2RAD; - lon2=lon2/DEG2RAD; - - destination.lat=lat2; - destination.lon=lon2; - - /* If SDF data is missing for the endpoint of - the radial, then the average terrain cannot - be accurately calculated. Return -9999.0 */ - - if (GetElevation(destination)<-4999.0) - return (-9999.0); - else - { - ReadPath(source,destination); - - endpoint=path.length; - - /* Shrink the length of the radial if the - outermost portion is not over U.S. land. */ - - for (c=endpoint-1; c>=0 && path.elevation[c]==0.0; c--); - - endpoint=c+1; - - for (c=0, samples=0; c=start_distance) - { - terrain+=(path.elevation[c]==0.0?path.elevation[c]:path.elevation[c]+clutter); - samples++; - } - } - - if (samples==0) - terrain=-5000.0; /* No land */ - else - terrain=(terrain/(double)samples); - - return terrain; - } -} - -double haat(struct site antenna) -{ - /* This function returns the antenna's Height Above Average - Terrain (HAAT) based on FCC Part 73.313(d). If a critical - error occurs, such as a lack of SDF data to complete the - survey, -5000.0 is returned. */ - - int azi, c; - char error=0; - double terrain, avg_terrain, haat, sum=0.0; - - /* Calculate the average terrain between 2 and 10 miles - from the antenna site at azimuths of 0, 45, 90, 135, - 180, 225, 270, and 315 degrees. */ - - for (c=0, azi=0; azi<=315 && error==0; azi+=45) - { - terrain=AverageTerrain(antenna, (double)azi, 2.0, 10.0); - - if (terrain<-9998.0) /* SDF data is missing */ - error=1; - - if (terrain>-4999.0) /* It's land, not water */ - { - sum+=terrain; /* Sum of averages */ - c++; - } - } - - if (error) - return -5000.0; - else - { - avg_terrain=(sum/(double)c); - haat=(antenna.alt+GetElevation(antenna))-avg_terrain; - return haat; - } -} -double ReadBearing(char *input) -{ - /* This function takes numeric input in the form of a character - string, and returns an equivalent bearing in degrees as a - decimal number (double). The input may either be expressed - in decimal format (40.139722) or degree, minute, second - format (40 08 23). This function also safely handles - extra spaces found either leading, trailing, or - embedded within the numbers expressed in the - input string. Decimal seconds are permitted. */ - - double seconds, bearing=0.0; - char string[20]; - int a, b, length, degrees, minutes; - - /* Copy "input" to "string", and ignore any extra - spaces that might be present in the process. */ - - string[0]=0; - length=strlen(input); - - for (a=0, b=0; a360.0 || bearing<-360.0) - bearing=0.0; - - return bearing; -} - -void LoadPAT(char *filename) -{ - /* This function reads and processes antenna pattern (.az - and .el) files that correspond in name to previously - loaded ss .lrp files. */ - - int a, b, w, x, y, z, last_index, next_index, span; - char string[255], azfile[255], elfile[255], *pointer=NULL, *s=NULL; - float az, xx, elevation, amplitude, rotation, valid1, valid2, - delta, azimuth[361], azimuth_pattern[361], el_pattern[10001], - elevation_pattern[361][1001], slant_angle[361], tilt, - mechanical_tilt=0.0, tilt_azimuth, tilt_increment, sum; - FILE *fd=NULL; - unsigned char read_count[10001]; - - for (x=0; filename[x]!='.' && filename[x]!=0 && x<250; x++) - { - azfile[x]=filename[x]; - elfile[x]=filename[x]; - } - - azfile[x]='.'; - azfile[x+1]='a'; - azfile[x+2]='z'; - azfile[x+3]=0; - - elfile[x]='.'; - elfile[x+1]='e'; - elfile[x+2]='l'; - elfile[x+3]=0; - - rotation=0.0; - - got_azimuth_pattern=0; - got_elevation_pattern=0; - - /* Load .az antenna pattern file */ - - fd=fopen(azfile,"r"); - - if (fd!=NULL) - { - /* Clear azimuth pattern array */ - - for (x=0; x<=360; x++) - { - azimuth[x]=0.0; - read_count[x]=0; - } - - - /* Read azimuth pattern rotation - in degrees measured clockwise - from true North. */ - - s=fgets(string,254,fd); - pointer=strchr(string,';'); - - if (pointer!=NULL) - *pointer=0; - - sscanf(string,"%f",&rotation); - - - /* Read azimuth (degrees) and corresponding - normalized field radiation pattern amplitude - (0.0 to 1.0) until EOF is reached. */ - - s=fgets(string,254,fd); - pointer=strchr(string,';'); - - if (pointer!=NULL) - *pointer=0; - - sscanf(string,"%f %f",&az, &litude); - - do - { - x=(int)rintf(az); - - if (x>=0 && x<=360 && fd!=NULL) - { - azimuth[x]+=amplitude; - read_count[x]++; - } - - s=fgets(string,254,fd); - pointer=strchr(string,';'); - - if (pointer!=NULL) - *pointer=0; - - sscanf(string,"%f %f",&az, &litude); - - } while (feof(fd)==0); - - fclose(fd); - - - /* Handle 0=360 degree ambiguity */ - - if ((read_count[0]==0) && (read_count[360]!=0)) - { - read_count[0]=read_count[360]; - azimuth[0]=azimuth[360]; - } - - if ((read_count[0]!=0) && (read_count[360]==0)) - { - read_count[360]=read_count[0]; - azimuth[360]=azimuth[0]; - } - - /* Average pattern values in case more than - one was read for each degree of azimuth. */ - - for (x=0; x<=360; x++) - { - if (read_count[x]>1) - azimuth[x]/=(float)read_count[x]; - } - - /* Interpolate missing azimuths - to completely fill the array */ - - last_index=-1; - next_index=-1; - - for (x=0; x<=360; x++) - { - if (read_count[x]!=0) - { - if (last_index==-1) - last_index=x; - else - next_index=x; - } - - if (last_index!=-1 && next_index!=-1) - { - valid1=azimuth[last_index]; - valid2=azimuth[next_index]; - - span=next_index-last_index; - delta=(valid2-valid1)/(float)span; - - for (y=last_index+1; y=360) - y-=360; - - azimuth_pattern[y]=azimuth[x]; - } - - azimuth_pattern[360]=azimuth_pattern[0]; - - got_azimuth_pattern=255; - } - - /* Read and process .el file */ - - fd=fopen(elfile,"r"); - - if (fd!=NULL) - { - for (x=0; x<=10000; x++) - { - el_pattern[x]=0.0; - read_count[x]=0; - } - - /* Read mechanical tilt (degrees) and - tilt azimuth in degrees measured - clockwise from true North. */ - - s=fgets(string,254,fd); - pointer=strchr(string,';'); - - if (pointer!=NULL) - *pointer=0; - - sscanf(string,"%f %f",&mechanical_tilt, &tilt_azimuth); - - /* Read elevation (degrees) and corresponding - normalized field radiation pattern amplitude - (0.0 to 1.0) until EOF is reached. */ - - s=fgets(string,254,fd); - pointer=strchr(string,';'); - - if (pointer!=NULL) - *pointer=0; - - sscanf(string,"%f %f", &elevation, &litude); - - while (feof(fd)==0) - { - /* Read in normalized radiated field values - for every 0.01 degrees of elevation between - -10.0 and +90.0 degrees */ - - x=(int)rintf(100.0*(elevation+10.0)); - - if (x>=0 && x<=10000) - { - el_pattern[x]+=amplitude; - read_count[x]++; - } - - s=fgets(string,254,fd); - pointer=strchr(string,';'); - - if (pointer!=NULL) - *pointer=0; - - sscanf(string,"%f %f", &elevation, &litude); - } - - fclose(fd); - - /* Average the field values in case more than - one was read for each 0.01 degrees of elevation. */ - - for (x=0; x<=10000; x++) - { - if (read_count[x]>1) - el_pattern[x]/=(float)read_count[x]; - } - - /* Interpolate between missing elevations (if - any) to completely fill the array and provide - radiated field values for every 0.01 degrees of - elevation. */ - - last_index=-1; - next_index=-1; - - for (x=0; x<=10000; x++) - { - if (read_count[x]!=0) - { - if (last_index==-1) - last_index=x; - else - next_index=x; - } - - if (last_index!=-1 && next_index!=-1) - { - valid1=el_pattern[last_index]; - valid2=el_pattern[next_index]; - - span=next_index-last_index; - delta=(valid2-valid1)/(float)span; - - for (y=last_index+1; y=360) - y-=360; - - while (y<0) - y+=360; - - if (x<=180) - slant_angle[y]=-(tilt_increment*(90.0-xx)); - - if (x>180) - slant_angle[y]=-(tilt_increment*(xx-270.0)); - } - } - - slant_angle[360]=slant_angle[0]; /* 360 degree wrap-around */ - - for (w=0; w<=360; w++) - { - tilt=slant_angle[w]; - - /** Convert tilt angle to - an array index offset **/ - - y=(int)rintf(100.0*tilt); - - /* Copy shifted el_pattern[10001] field - values into elevation_pattern[361][1001] - at the corresponding azimuth, downsampling - (averaging) along the way in chunks of 10. */ - - for (x=y, z=0; z<=1000; x+=10, z++) - { - for (sum=0.0, a=0; a<10; a++) - { - b=a+x; - - if (b>=0 && b<=10000) - sum+=el_pattern[b]; - if (b<0) - sum+=el_pattern[0]; - if (b>10000) - sum+=el_pattern[10000]; - } - - elevation_pattern[w][z]=sum/10.0; - } - } - - got_elevation_pattern=255; - } - - for (x=0; x<=360; x++) - { - for (y=0; y<=1000; y++) - { - if (got_elevation_pattern) - elevation=elevation_pattern[x][y]; - else - elevation=1.0; - - if (got_azimuth_pattern) - az=azimuth_pattern[x]; - else - az=1.0; - - LR.antenna_pattern[x][y]=az*elevation; - } - } -} - -int LoadSDF_SDF(char *name, int winfiles) -{ - /* This function reads uncompressed ss Data Files (.sdf) - containing digital elevation model data into memory. - Elevation data, maximum and minimum elevations, and - quadrangle limits are stored in the first available - dem[] structure. */ - - int x, y, data, indx, minlat, minlon, maxlat, maxlon,j; - char found, free_page=0, line[20], jline[20], sdf_file[255], - path_plus_name[255], *s=NULL,*junk=NULL; - - - FILE *fd; - - for (x=0; name[x]!='.' && name[x]!=0 && x<250; x++) - sdf_file[x]=name[x]; - - sdf_file[x]=0; - - /* Parse filename for minimum latitude and longitude values */ - if(winfiles==1){ - sscanf(sdf_file,"%d=%d=%d=%d",&minlat,&maxlat,&minlon,&maxlon); - }else{ - sscanf(sdf_file,"%d:%d:%d:%d",&minlat,&maxlat,&minlon,&maxlon); - } - - sdf_file[x]='.'; - sdf_file[x+1]='s'; - sdf_file[x+2]='d'; - sdf_file[x+3]='f'; - sdf_file[x+4]=0; - - /* Is it already in memory? */ - - - for (indx=0, found=0; indx=0 && indxdem[indx].max_el) - dem[indx].max_el=data; - - if (datamax_elevation) - max_elevation=dem[indx].max_el; - - if (max_north==-90) - max_north=dem[indx].max_north; - - else if (dem[indx].max_north>max_north) - max_north=dem[indx].max_north; - - 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; - } - } - - - return 1; - } - - else - return -1; - } - - else - return 0; -} -char LoadSDF(char *name, int winfiles) -{ - /* This function loads the requested SDF file from the filesystem. - It first tries to invoke the LoadSDF_SDF() function to load an - uncompressed SDF file (since uncompressed files load slightly - faster). If that attempt fails, then it tries to load a - compressed SDF file by invoking the LoadSDF_BZ() function. - If that fails, then we can assume that no elevation data - exists for the region requested, and that the region - requested must be entirely over water. */ - - int x, y, indx, minlat, minlon, maxlat, maxlon; - char found, free_page=0; - int return_value=-1; - - return_value=LoadSDF_SDF(name, winfiles); - - - /* If neither format can be found, then assume the area is water. */ - - if (return_value==0 || return_value==-1) - { - - - - if(winfiles==1){ - sscanf(name,"%d=%d=%d=%d",&minlat,&maxlat,&minlon,&maxlon); - }else{ - sscanf(name,"%d:%d:%d:%d",&minlat,&maxlat,&minlon,&maxlon); - } - /* Is it already in memory? */ - - for (indx=0, found=0; indx=0 && indx0) - dem[indx].min_el=0; - } - - if (dem[indx].min_elmax_elevation) - max_elevation=dem[indx].max_el; - - if (max_north==-90) - max_north=dem[indx].max_north; - - else if (dem[indx].max_north>max_north) - max_north=dem[indx].max_north; - - 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; - } - } - - return_value=1; - } - } - - return return_value; -} - -void PlotLOSPath(struct site source, struct site destination, char mask_value, FILE *fd) -{ - /* This function analyzes the path between the source and - destination locations. It determines which points along - the path have line-of-sight visibility to the source. - Points along with path having line-of-sight visibility - to the source at an AGL altitude equal to that of the - destination location are stored by setting bit 1 in the - mask[][] array, which are displayed in green when PPM - maps are later generated by ss. */ - - char block; - int x, y; - register double cos_xmtr_angle, cos_test_angle, test_alt; - double distance, rx_alt, tx_alt; - - ReadPath(source,destination); - - for (y=0; y=0 && block==0; x--) - { - distance=5280.0*(path.distance[y]-path.distance[x]); - test_alt=earthradius+(path.elevation[x]==0.0?path.elevation[x]:path.elevation[x]+clutter); - - 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 - 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 - be if the actual angles were compared. */ - - if (cos_xmtr_angle>=cos_test_angle) - block=1; - } - - if (block==0) - OrMask(path.lat[y],path.lon[y],mask_value); - } - } -} - -void PlotPropPath(struct site source, struct site destination, unsigned char mask_value, FILE *fd, int propmodel, int knifeedge) -{ - - int x, y, ifs, ofs, errnum; - char block=0, strmode[100]; - double loss, azimuth, pattern=0.0, - xmtr_alt, dest_alt, xmtr_alt2, dest_alt2, - cos_rcvr_angle, cos_test_angle=0.0, test_alt, - elevation=0.0, distance=0.0, radius=0.0, four_thirds_earth, - field_strength=0.0, rxp, dBm, txelev, dkm, diffloss; - struct site temp; - - radius = Distance(source,destination); - - ReadPath(source,destination); - - four_thirds_earth=FOUR_THIRDS*EARTHRADIUS; - - - for (x=1; x1.0) - cos_rcvr_angle=1.0; - - if (cos_rcvr_angle<-1.0) - cos_rcvr_angle=-1.0; - - if (got_elevation_pattern || fd!=NULL) - { - /* Determine the elevation angle to the first obstruction - along the path IF elevation pattern data is available - or an output (.ano) file has been designated. */ - - for (x=2, block=0; (x1.0) - cos_test_angle=1.0; - - if (cos_test_angle<-1.0) - cos_test_angle=-1.0; - - /* Compare these two angles to determine if - an obstruction exists. Since we're comparing - the cosines of these angles rather than - the angles themselves, the sense of the - following "if" statement is reversed from - what it would be if the angles themselves - were compared. */ - - if (cos_rcvr_angle>=cos_test_angle) - block=1; - } - - if (block) - elevation=((acos(cos_test_angle))/DEG2RAD)-90.0; - else - elevation=((acos(cos_rcvr_angle))/DEG2RAD)-90.0; - } - - /* Determine attenuation for each point along the - path using a prop model starting at y=2 (number_of_points = 1), the - shortest distance terrain can play a role in - path loss. */ - - elev[0]=y-1; /* (number of points - 1) */ - - /* Distance between elevation samples */ - - elev[1]=METERS_PER_MILE*(path.distance[y]-path.distance[y-1]); - - /* - elev[2]=path.elevation[0]*METERS_PER_FOOT; - elev[path.length+1]=path.elevation[path.length-1]*METERS_PER_FOOT; - */ - if(path.elevation[y] < 1){ - path.elevation[y]=1; - } - - dkm=(elev[1]*elev[0])/1000; // km - - switch (propmodel) - { - case 1: - // Longley Rice - point_to_point_ITM(elev,source.alt*METERS_PER_FOOT, - destination.alt*METERS_PER_FOOT, LR.eps_dielect, - LR.sgm_conductivity, LR.eno_ns_surfref, LR.frq_mhz, - LR.radio_climate, LR.pol, LR.conf, LR.rel, loss, - strmode, errnum); - break; - case 3: - //HATA urban - loss=HataLinkdB(LR.frq_mhz,txelev,path.elevation[y]+(destination.alt*METERS_PER_FOOT),dkm, 1); - break; - case 4: - //HATA suburban - loss=HataLinkdB(LR.frq_mhz,txelev,path.elevation[y]+(destination.alt*METERS_PER_FOOT),dkm, 2); - break; - case 5: - //HATA open - loss=HataLinkdB(LR.frq_mhz,txelev,path.elevation[y]+(destination.alt*METERS_PER_FOOT),dkm, 3); - break; - case 6: - // COST231-HATA - loss=CostHataLinkdB(LR.frq_mhz,txelev,path.elevation[y]+(destination.alt*METERS_PER_FOOT),dkm); - break; - case 7: - // ITU-R P.525 Free space path loss - loss=FsplLinkdB(LR.frq_mhz,dkm); - //fprintf(stdout,"MHz: %1f KM: %1f = %1fdB",LR.frq_mhz,dkm,loss); - break; - case 8: - // ITWOM 3.0 - point_to_point(elev,source.alt*METERS_PER_FOOT, - destination.alt*METERS_PER_FOOT, LR.eps_dielect, - LR.sgm_conductivity, LR.eno_ns_surfref, LR.frq_mhz, - LR.radio_climate, LR.pol, LR.conf, LR.rel, loss, - strmode, errnum); - break; - - default: - point_to_point_ITM(elev,source.alt*METERS_PER_FOOT, - destination.alt*METERS_PER_FOOT, LR.eps_dielect, - LR.sgm_conductivity, LR.eno_ns_surfref, LR.frq_mhz, - LR.radio_climate, LR.pol, LR.conf, LR.rel, loss, - strmode, errnum); - - } - - - if(knifeedge==1){ - diffloss = ked(LR.frq_mhz,elev,destination.alt*METERS_PER_FOOT,dkm); - loss+=(diffloss); // ;) - } - - - - - //Key stage. Link dB for p2p is returned as 'loss'. - - temp.lat=path.lat[y]; - temp.lon=path.lon[y]; - - azimuth=(Azimuth(source,temp)); - - if (fd!=NULL) - fprintf(fd,"%.7f, %.7f, %.3f, %.3f, ",path.lat[y], path.lon[y], azimuth, elevation); - - /* If ERP==0, write path loss to alphanumeric - output file. Otherwise, write field strength - or received power level (below), as appropriate. */ - - if (fd!=NULL && LR.erp==0.0) - fprintf(fd,"%.2f",loss); - - /* Integrate the antenna's radiation - pattern into the overall path loss. */ - - x=(int)rint(10.0*(10.0-elevation)); - - if (x>=0 && x<=1000) - { - azimuth=rint(azimuth); - - pattern=(double)LR.antenna_pattern[(int)azimuth][x]; - - if (pattern!=0.0) - { - pattern=20.0*log10(pattern); - loss-=pattern; - } - } - - if (LR.erp!=0.0) - { - if (dbm) - { - /* dBm is based on EIRP (ERP + 2.14) */ - - rxp=LR.erp/(pow(10.0,(loss-2.14)/10.0)); - - dBm=10.0*(log10(rxp*1000.0)); - - if (fd!=NULL) - fprintf(fd,"%.3f",dBm); - - /* Scale roughly between 0 and 255 */ - - ifs=200+(int)rint(dBm); - - if (ifs<0) - ifs=0; - - if (ifs>255) - ifs=255; - - ofs=GetSignal(path.lat[y],path.lon[y]); - - if (ofs>ifs) - ifs=ofs; - - - PutSignal(path.lat[y],path.lon[y],(unsigned char)ifs); - - } - - else - { - field_strength=(139.4+(20.0*log10(LR.frq_mhz))-loss)+(10.0*log10(LR.erp/1000.0)); - - ifs=100+(int)rint(field_strength); - - if (ifs<0) - ifs=0; - - if (ifs>255) - ifs=255; - - ofs=GetSignal(path.lat[y],path.lon[y]); - - if (ofs>ifs) - ifs=ofs; - - PutSignal(path.lat[y],path.lon[y],(unsigned char)ifs); - - if (fd!=NULL) - fprintf(fd,"%.3f",field_strength); - } - } - - else - { - if (loss>255) - ifs=255; - else - ifs=(int)rint(loss); - - ofs=GetSignal(path.lat[y],path.lon[y]); - - if (ofs=360.0) - lon-=360.0; - - edge.lat=max_north; - edge.lon=lon; - edge.alt=altitude; - - PlotLOSPath(source,edge,mask_value,fd); - } - - - - z=(int)(th*(double)(max_north-min_north)); - - for (lat=maxnorth, x=0, y=0; lat>=(double)min_north; y++, lat=maxnorth-(dpp*(double)y)) - { - edge.lat=lat; - edge.lon=min_west; - edge.alt=altitude; - - PlotLOSPath(source,edge,mask_value,fd); - - } - - - - z=(int)(th*ReduceAngle(max_west-min_west)); - - for (lon=minwest, x=0, y=0; (LonDiff(lon,(double)max_west)<=0.0); y++, lon=minwest+(dpp*(double)y)) - { - if (lon>=360.0) - lon-=360.0; - - edge.lat=min_north; - edge.lon=lon; - edge.alt=altitude; - - PlotLOSPath(source,edge,mask_value,fd); - - } - - - z=(int)(th*(double)(max_north-min_north)); - - for (lat=(double)min_north, x=0, y=0; lat<(double)max_north; y++, lat=(double)min_north+(dpp*(double)y)) - { - edge.lat=lat; - edge.lon=max_west; - edge.alt=altitude; - - PlotLOSPath(source,edge,mask_value,fd); - - - } - - - switch (mask_value) - { - case 1: - mask_value=8; - break; - - case 8: - mask_value=16; - break; - - case 16: - mask_value=32; - } -} - -void PlotPropagation(struct site source, double altitude, char *plo_filename, int propmodel, int knifeedge, int haf) -{ - int y, z, count; - struct site edge; - double lat, lon, minwest, maxnorth, th; - unsigned char x; - static unsigned char mask_value=1; - FILE *fd=NULL; - - minwest=dpp+(double)min_west; - maxnorth=(double)max_north-dpp; - - count=0; - - - if (LR.erp==0.0 && debug) - fprintf(stdout,"path loss"); - else - { - if(debug){ - if (dbm) - fprintf(stdout,"signal power level"); - else - fprintf(stdout,"field strength"); - } - } - if (debug){ - fprintf(stdout," contours of \"%s\"\nout to a radius of %.2f %s with Rx antenna(s) at %.2f %s AGL\n",source.name,metric?max_range*KM_PER_MILE:max_range,metric?"kilometers":"miles",metric?altitude*METERS_PER_FOOT:altitude,metric?"meters":"feet"); - } - - if (clutter>0.0 && debug) - fprintf(stdout,"\nand %.2f %s of ground clutter",metric?clutter*METERS_PER_FOOT:clutter,metric?"meters":"feet"); - - if(debug){ - fprintf(stdout,"...\n\n 0%c to 25%c ",37,37); - fflush(stdout); - } - - if (plo_filename[0]!=0) - fd=fopen(plo_filename,"wb"); - - if (fd!=NULL) - { - fprintf(fd,"%d, %d\t; max_west, min_west\n%d, %d\t; max_north, min_north\n",max_west, min_west, max_north, min_north); - } - - th=ppd/loops; - - // Four sections start here - - //S1 - if(haf==0 || haf==1){ - z=(int)(th*ReduceAngle(max_west-min_west)); - - for (lon=minwest, x=0, y=0; (LonDiff(lon,(double)max_west)<=0.0); y++, lon=minwest+(dpp*(double)y)) - { - if (lon>=360.0) - lon-=360.0; - - edge.lat=max_north; - edge.lon=lon; - edge.alt=altitude; - - PlotPropPath(source,edge,mask_value,fd,propmodel,knifeedge); - count++; - - if (count==z) - { - count=0; - - if (x==3) - x=0; - else - x++; - } - } - - } - - //S2 - if(haf==0 || haf==1){ - count=0; - if(debug){ - fprintf(stdout,"\n25%c to 50%c ",37,37); - fflush(stdout); - } - - z=(int)(th*(double)(max_north-min_north)); - - for (lat=maxnorth, x=0, y=0; lat>=(double)min_north; y++, lat=maxnorth-(dpp*(double)y)) - { - edge.lat=lat; - edge.lon=min_west; - edge.alt=altitude; - - PlotPropPath(source,edge,mask_value,fd,propmodel,knifeedge); - count++; - - if (count==z) - { - //fprintf(stdout,"%c",symbol[x]); - //fflush(stdout); - count=0; - - if (x==3) - x=0; - else - x++; - } - } - - } - //S3 - if(haf==0 || haf==2){ - count=0; - if(debug){ - fprintf(stdout,"\n50%c to 75%c ",37,37); - fflush(stdout); - } - - z=(int)(th*ReduceAngle(max_west-min_west)); - - for (lon=minwest, x=0, y=0; (LonDiff(lon,(double)max_west)<=0.0); y++, lon=minwest+(dpp*(double)y)) - { - if (lon>=360.0) - lon-=360.0; - - edge.lat=min_north; - edge.lon=lon; - edge.alt=altitude; - - PlotPropPath(source,edge,mask_value,fd,propmodel,knifeedge); - count++; - if (count==z) - { - //fprintf(stdout,"%c",symbol[x]); - //fflush(stdout); - count=0; - - if (x==3) - x=0; - else - x++; - } - - } - - } - //S4 - if(haf==0 || haf==2){ - count=0; - if(debug){ - fprintf(stdout,"\n75%c to 100%c ",37,37); - fflush(stdout); - } - z=(int)(th*(double)(max_north-min_north)); - - for (lat=(double)min_north, x=0, y=0; lat<(double)max_north; y++, lat=(double)min_north+(dpp*(double)y)) - { - edge.lat=lat; - edge.lon=max_west; - edge.alt=altitude; - - PlotPropPath(source,edge,mask_value,fd,propmodel,knifeedge); - count++; - - if (count==z) - { - - count=0; - - if (x==3) - x=0; - else - x++; - } - } - - } //S4 - - if (fd!=NULL) - fclose(fd); - - if (mask_value<30) - mask_value++; -} - -void LoadSignalColors(struct site xmtr) -{ - int x, y, ok, val[4]; - char filename[255], string[80], *pointer=NULL, *s=NULL; - FILE *fd=NULL; - - for (x=0; xmtr.filename[x]!='.' && xmtr.filename[x]!=0 && x<250; x++) - filename[x]=xmtr.filename[x]; - - filename[x]='.'; - filename[x+1]='s'; - filename[x+2]='c'; - filename[x+3]='f'; - filename[x+4]=0; - - /* Default values */ - - region.level[0]=128; - region.color[0][0]=255; - region.color[0][1]=0; - region.color[0][2]=0; - - region.level[1]=118; - region.color[1][0]=255; - region.color[1][1]=165; - region.color[1][2]=0; - - region.level[2]=108; - region.color[2][0]=255; - region.color[2][1]=206; - region.color[2][2]=0; - - region.level[3]=98; - region.color[3][0]=255; - region.color[3][1]=255; - region.color[3][2]=0; - - region.level[4]=88; - region.color[4][0]=184; - region.color[4][1]=255; - region.color[4][2]=0; - - region.level[5]=78; - region.color[5][0]=0; - region.color[5][1]=255; - region.color[5][2]=0; - - region.level[6]=68; - region.color[6][0]=0; - region.color[6][1]=208; - region.color[6][2]=0; - - region.level[7]=58; - region.color[7][0]=0; - region.color[7][1]=196; - region.color[7][2]=196; - - region.level[8]=48; - region.color[8][0]=0; - region.color[8][1]=148; - region.color[8][2]=255; - - region.level[9]=38; - region.color[9][0]=80; - region.color[9][1]=80; - region.color[9][2]=255; - - region.level[10]=28; - region.color[10][0]=0; - region.color[10][1]=38; - region.color[10][2]=255; - - region.level[11]=18; - region.color[11][0]=142; - region.color[11][1]=63; - region.color[11][2]=255; - - region.level[12]=8; - region.color[12][0]=140; - region.color[12][1]=0; - region.color[12][2]=128; - - region.levels=13; - - fd=fopen(filename,"r"); - - if (fd==NULL) - fd=fopen(filename,"r"); - - if (fd==NULL) - { - fd=fopen(filename,"w"); - - - for (x=0; x255) - val[y]=255; - - if (val[y]<0) - val[y]=0; - } - - region.level[x]=val[0]; - region.color[x][0]=val[1]; - region.color[x][1]=val[2]; - region.color[x][2]=val[3]; - x++; - } - - s=fgets(string,80,fd); - } - - fclose(fd); - region.levels=x; - } -} - -void LoadLossColors(struct site xmtr) -{ - int x, y, ok, val[4]; - char filename[255], string[80], *pointer=NULL, *s=NULL; - FILE *fd=NULL; - - for (x=0; xmtr.filename[x]!='.' && xmtr.filename[x]!=0 && x<250; x++) - filename[x]=xmtr.filename[x]; - - filename[x]='.'; - filename[x+1]='l'; - filename[x+2]='c'; - filename[x+3]='f'; - filename[x+4]=0; - - /* Default values */ - - region.level[0]=80; - region.color[0][0]=255; - region.color[0][1]=0; - region.color[0][2]=0; - - region.level[1]=90; - region.color[1][0]=255; - region.color[1][1]=128; - region.color[1][2]=0; - - region.level[2]=100; - region.color[2][0]=255; - region.color[2][1]=165; - region.color[2][2]=0; - - region.level[3]=110; - region.color[3][0]=255; - region.color[3][1]=206; - region.color[3][2]=0; - - region.level[4]=120; - region.color[4][0]=255; - region.color[4][1]=255; - region.color[4][2]=0; - - region.level[5]=130; - region.color[5][0]=184; - region.color[5][1]=255; - region.color[5][2]=0; - - region.level[6]=140; - region.color[6][0]=0; - region.color[6][1]=255; - region.color[6][2]=0; - - region.level[7]=150; - region.color[7][0]=0; - region.color[7][1]=208; - region.color[7][2]=0; - - region.level[8]=160; - region.color[8][0]=0; - region.color[8][1]=196; - region.color[8][2]=196; - - region.level[9]=170; - region.color[9][0]=0; - region.color[9][1]=148; - region.color[9][2]=255; - - region.level[10]=180; - region.color[10][0]=80; - region.color[10][1]=80; - region.color[10][2]=255; - - region.level[11]=190; - region.color[11][0]=0; - region.color[11][1]=38; - region.color[11][2]=255; - - region.level[12]=200; - region.color[12][0]=142; - region.color[12][1]=63; - region.color[12][2]=255; - - region.level[13]=210; - region.color[13][0]=196; - region.color[13][1]=54; - region.color[13][2]=255; - - region.level[14]=220; - region.color[14][0]=255; - region.color[14][1]=0; - region.color[14][2]=255; - - region.level[15]=230; - region.color[15][0]=255; - region.color[15][1]=194; - region.color[15][2]=204; - - region.levels=16; - - fd=fopen(filename,"r"); - - if (fd==NULL) - fd=fopen(filename,"r"); - - if (fd==NULL) - { - fd=fopen(filename,"w"); - - - - for (x=0; x255) - val[y]=255; - - if (val[y]<0) - val[y]=0; - } - - region.level[x]=val[0]; - region.color[x][0]=val[1]; - region.color[x][1]=val[2]; - region.color[x][2]=val[3]; - x++; - } - - s=fgets(string,80,fd); - } - - fclose(fd); - region.levels=x; - } -} - -void LoadDBMColors(struct site xmtr) -{ - int x, y, ok, val[4]; - char filename[255], string[80], *pointer=NULL, *s=NULL; - FILE *fd=NULL; - - for (x=0; xmtr.filename[x]!='.' && xmtr.filename[x]!=0 && x<250; x++) - filename[x]=xmtr.filename[x]; - - filename[x]='.'; - filename[x+1]='d'; - filename[x+2]='c'; - filename[x+3]='f'; - filename[x+4]=0; - - /* Default values */ - - region.level[0]=0; - region.color[0][0]=255; - region.color[0][1]=0; - region.color[0][2]=0; - - region.level[1]=-10; - region.color[1][0]=255; - region.color[1][1]=128; - region.color[1][2]=0; - - region.level[2]=-20; - region.color[2][0]=255; - region.color[2][1]=165; - region.color[2][2]=0; - - region.level[3]=-30; - region.color[3][0]=255; - region.color[3][1]=206; - region.color[3][2]=0; - - region.level[4]=-40; - region.color[4][0]=255; - region.color[4][1]=255; - region.color[4][2]=0; - - region.level[5]=-50; - region.color[5][0]=184; - region.color[5][1]=255; - region.color[5][2]=0; - - region.level[6]=-60; - region.color[6][0]=0; - region.color[6][1]=255; - region.color[6][2]=0; - - region.level[7]=-70; - region.color[7][0]=0; - region.color[7][1]=208; - region.color[7][2]=0; - - region.level[8]=-80; - region.color[8][0]=0; - region.color[8][1]=196; - region.color[8][2]=196; - - region.level[9]=-90; - region.color[9][0]=0; - region.color[9][1]=148; - region.color[9][2]=255; - - region.level[10]=-100; - region.color[10][0]=80; - region.color[10][1]=80; - region.color[10][2]=255; - - region.level[11]=-110; - region.color[11][0]=0; - region.color[11][1]=38; - region.color[11][2]=255; - - region.level[12]=-120; - region.color[12][0]=142; - region.color[12][1]=63; - region.color[12][2]=255; - - region.level[13]=-130; - region.color[13][0]=196; - region.color[13][1]=54; - region.color[13][2]=255; - - region.level[14]=-140; - region.color[14][0]=255; - region.color[14][1]=0; - region.color[14][2]=255; - - region.level[15]=-150; - region.color[15][0]=255; - region.color[15][1]=194; - region.color[15][2]=204; - - region.levels=16; - - fd=fopen(filename,"r"); - - if (fd==NULL) - fd=fopen(filename,"r"); - - if (fd==NULL) - { - fd=fopen(filename,"w"); - - - for (x=0; x+40) - val[0]=+40; - - region.level[x]=val[0]; - - for (y=1; y<4; y++) - { - if (val[y]>255) - val[y]=255; - - if (val[y]<0) - val[y]=0; - } - - region.color[x][0]=val[1]; - region.color[x][1]=val[2]; - region.color[x][2]=val[3]; - x++; - } - - s=fgets(string,80,fd); - } - - fclose(fd); - region.levels=x; - } -} - - -void DoPathLoss(char *filename, unsigned char geo, unsigned char kml, unsigned char ngs, struct site *xmtr, unsigned char txsites) -{ - /* This function generates a topographic map in Portable Pix Map - (PPM) format based on the content of flags held in the mask[][] - array (only). The image created is rotated counter-clockwise - 90 degrees from its representation in dem[][] so that north - points up and east points right in the image generated. */ - - char mapfile[255]; - unsigned width, height, red, green, blue, terrain=0; - unsigned char found, mask, cityorcounty; - int indx, x, y, z, x0, y0, loss,match; - double lat, lon, conversion, one_over_gamma,minwest; - FILE *fd; - - one_over_gamma=1.0/GAMMA; - conversion=255.0/pow((double)(max_elevation-min_elevation),one_over_gamma); - - width=(unsigned)(ippd*ReduceAngle(max_west-min_west)); - height=(unsigned)(ippd*ReduceAngle(max_north-min_north)); - - LoadLossColors(xmtr[0]); - - if (filename[0]==0) - { - strncpy(filename, xmtr[0].filename,254); - filename[strlen(filename)-4]=0; /* Remove .qth */ - } - - y=strlen(filename); - - if (y>4) - { - if (filename[y-1]=='m' && filename[y-2]=='p' && filename[y-3]=='p' && filename[y-4]=='.') - y-=4; - } - - for (x=0; x360.0) - minwest-=360.0; - - north=(double)max_north-dpp; - - if (kml || geo) - south=(double)min_north; /* No bottom legend */ - else - south=(double)min_north-(30.0/ppd); /* 30 pixels for bottom legend */ - - east=(minwest<180.0?-minwest:360.0-min_west); - west=(double)(max_west<180?-max_west:360-max_west); - - - // WriteKML() - //writeKML(xmtr,filename); - - fd=fopen(mapfile,"wb"); - - fprintf(fd,"P6\n%u %u\n255\n",width,(kml?height:height+30)); - if(debug){ - fprintf(stdout,"\nWriting \"%s\" (%ux%u pixmap image)... ",mapfile,width,(kml?height:height+30)); - fflush(stdout); - } - for (y=0, lat=north; y<(int)height; y++, lat=north-(dpp*(double)y)) - { - for (x=0, lon=max_west; x<(int)width; x++, lon=max_west-(dpp*(double)x)) - { - if (lon<0.0) - lon+=360.0; - - for (indx=0, found=0; indx=0 && x0<=mpi && y0>=0 && y0<=mpi) - found=1; - else - indx++; - } - - - - if (found) - { - mask=dem[indx].mask[x0][y0]; - loss=(dem[indx].signal[x0][y0]); - cityorcounty=0; - - //check loss isn't a near field void - // Receiver sensitivity kicks in later on - /* if(loss==0 && prevloss > 60){ - loss=(prevloss-5); - }else{ - prevloss=loss; - } - */ - if(debug){ - fprintf(stdout,"\n%d\t%d\t%d\t%d",loss,indx,x0,y0); - fflush(stdout); - } - match=255; - - red=0; - green=0; - blue=0; - - if (loss<=region.level[0]) - match=0; - else - { - for (z=1; (z=region.level[z-1] && loss=180 && green<=75 && blue<=75 && loss==0) - fprintf(fd,"%c%c%c",255^red,255^green,255^blue); - 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); - - cityorcounty=1; - } - - if (cityorcounty==0) - { - if (loss==0 || (contour_threshold!=0 && loss>abs(contour_threshold))) - { - if (ngs) /* No terrain */ - fprintf(fd,"%c%c%c",255,255,255); - else - { - /* Display land or sea elevation */ - - if (dem[indx].data[x0][y0]==0) - fprintf(fd,"%c%c%c",0,0,170); - else - { - terrain=(unsigned)(0.5+pow((double)(dem[indx].data[x0][y0]-min_elevation),one_over_gamma)*conversion); - fprintf(fd,"%c%c%c",terrain,terrain,terrain); - } - } - } - - else - { - /* Plot path loss in color */ - - if (red!=0 || green!=0 || blue!=0) - fprintf(fd,"%c%c%c",red,green,blue); - - else /* terrain / sea-level */ - { - if (dem[indx].data[x0][y0]==0) - fprintf(fd,"%c%c%c",0,0,170); - else - { - /* Elevation: Greyscale */ - terrain=(unsigned)(0.5+pow((double)(dem[indx].data[x0][y0]-min_elevation),one_over_gamma)*conversion); - fprintf(fd,"%c%c%c",terrain,terrain,terrain); - } - } - } - } - } - - else - { - /* We should never get here, but if */ - /* we do, display the region as black */ - - fprintf(fd,"%c%c%c",0,0,0); - } - } - } - - - - fclose(fd); - -} - -void DoSigStr(char *filename, unsigned char geo, unsigned char kml, unsigned char ngs, struct site *xmtr, unsigned char txsites) -{ - /* This function generates a topographic map in Portable Pix Map - (PPM) format based on the signal strength values held in the - signal[][] array. The image created is rotated counter-clockwise - 90 degrees from its representation in dem[][] so that north - points up and east points right in the image generated. */ - - char mapfile[255]; - unsigned width, height, terrain, red, green, blue; - unsigned char found, mask, cityorcounty; - int indx, x, y, z=1, x0, y0, signal,match; - double conversion, one_over_gamma, lat, lon, minwest; - FILE *fd; - - one_over_gamma=1.0/GAMMA; - conversion=255.0/pow((double)(max_elevation-min_elevation),one_over_gamma); - - width=(unsigned)(ippd*ReduceAngle(max_west-min_west)); - height=(unsigned)(ippd*ReduceAngle(max_north-min_north)); - - LoadSignalColors(xmtr[0]); - - if (filename[0]==0) - { - strncpy(filename, xmtr[0].filename,254); - filename[strlen(filename)-4]=0; /* Remove .qth */ - } - - y=strlen(filename); - - if (y>4) - { - if (filename[y-1]=='m' && filename[y-2]=='p' && filename[y-3]=='p' && filename[y-4]=='.') - y-=4; - } - - for (x=0; x360.0) - minwest-=360.0; - - north=(double)max_north-dpp; - - - south=(double)min_north; /* No bottom legend */ - - east=(minwest<180.0?-minwest:360.0-min_west); - west=(double)(max_west<180?-max_west:360-max_west); - - // WriteKML() - //writeKML(xmtr,filename); - - fd=fopen(mapfile,"wb"); - - fprintf(fd,"P6\n%u %u\n255\n",width,(kml?height:height+30)); - if(debug){ - fprintf(stdout,"\nWriting \"%s\" (%ux%u pixmap image)... ",mapfile,width,(kml?height:height+30)); - fflush(stdout); - } - for (y=0, lat=north; y<(int)height; y++, lat=north-(dpp*(double)y)) - { - for (x=0, lon=max_west; x<(int)width; x++, lon=max_west-(dpp*(double)x)) - { - if (lon<0.0) - lon+=360.0; - - for (indx=0, found=0; indx=0 && x0<=mpi && y0>=0 && y0<=mpi) - found=1; - else - indx++; - } - - if (found) - { - mask=dem[indx].mask[x0][y0]; - signal=(dem[indx].signal[x0][y0])-100; - cityorcounty=0; - - //check signal isn't near field void - // Receiver sensitivity kicks in later on - /*if(signal==-100 && prevsignal > -40){ - signal=(prevsignal+5); - }else{ - prevsignal=signal; - }*/ - - if(debug){ - fprintf(stdout,"\n%d\t%d\t%d\t%d",signal,indx,x0,y0); - fflush(stdout); - } - - match=255; - - red=0; - green=0; - blue=0; - - if (signal>=region.level[0]) - match=0; - else - { - for (z=1; (z=region.level[z]) - match=z; - } - } - - if (match=180 && green<=75 && blue<=75) - fprintf(fd,"%c%c%c",255^red,255^green,255^blue); - 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); - - cityorcounty=1; - } - - if (cityorcounty==0) - { - if (contour_threshold!=0 && signal4) - { - if (filename[y-1]=='m' && filename[y-2]=='p' && filename[y-3]=='p' && filename[y-4]=='.') - y-=4; - } - - for (x=0; x360.0) - minwest-=360.0; - - north=(double)max_north-dpp; - - - south=(double)min_north; /* No bottom legend */ - - - east=(minwest<180.0?-minwest:360.0-min_west); - west=(double)(max_west<180?-max_west:360-max_west); - - fd=fopen(mapfile,"wb"); - - fprintf(fd,"P6\n%u %u\n255\n",width,(kml?height:height+30)); - if(debug){ - fprintf(stdout,"\nWriting \"%s\" (%ux%u pixmap image)... ",mapfile,width,(kml?height:height+30)); - fflush(stdout); - } - // WriteKML() - //writeKML(xmtr,filename); - - for (y=0, lat=north; y<(int)height; y++, lat=north-(dpp*(double)y)) - { - for (x=0, lon=max_west; x<(int)width; x++, lon=max_west-(dpp*(double)x)) - { - if (lon<0.0) - lon+=360.0; - - for (indx=0, found=0; indx=0 && x0<=mpi && y0>=0 && y0<=mpi) - found=1; - else - indx++; - } - - if (found) - { - mask=dem[indx].mask[x0][y0]; - dBm=(dem[indx].signal[x0][y0])-200; - cityorcounty=0; - - - - - - - - - if(debug){ - fprintf(stdout,"\n%d\t%d\t%d\t%d",dBm,indx,x0,y0); - fflush(stdout); - } - - match=255; - - red=0; - green=0; - blue=0; - - if (dBm>=region.level[0]) - match=0; - else - { - for (z=1; (z=region.level[z]) - match=z; - } - } - - if (match=180 && green<=75 && blue<=75 && dBm!=0) - fprintf(fd,"%c%c%c",255^red,255^green,255^blue); - 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); - - cityorcounty=1; - } - - if (cityorcounty==0) - { - if (contour_threshold!=0 && dBm4) - { - if (filename[y-1]=='m' && filename[y-2]=='p' && filename[y-3]=='p' && filename[y-4]=='.') - y-=4; - } - - for (x=0; x360.0) - minwest-=360.0; - - north=(double)max_north-dpp; - - - south=(double)min_north; /* No bottom legend */ - - - east=(minwest<180.0?-minwest:360.0-min_west); - west=(double)(max_west<180?-max_west:360-max_west); - - fd=fopen(mapfile,"wb"); - - fprintf(fd,"P6\n%u %u\n255\n",width,(kml?height:height+30)); - if(debug){ - fprintf(stdout,"\nWriting \"%s\" (%ux%u pixmap image)... ",mapfile,width,(kml?height:height+30)); - fflush(stdout); - } - // WriteKML() - //writeKML(xmtr,filename); - - for (y=0, lat=north; y<(int)height; y++, lat=north-(dpp*(double)y)) - { - for (x=0, lon=max_west; x<(int)width; x++, lon=max_west-(dpp*(double)x)) - { - if (lon<0.0) - lon+=360.0; - - for (indx=0, found=0; indx=0 && x0<=mpi && y0>=0 && y0<=mpi) - found=1; - else - indx++; - } - - if (found) - { - mask=dem[indx].mask[x0][y0]; - - if (mask&2) - /* Text Labels: Red */ - fprintf(fd,"%c%c%c",255,0,0); - - else if (mask&4) - /* County Boundaries: Light Cyan */ - fprintf(fd,"%c%c%c",128,128,255); - - else switch (mask&57) - { - case 1: - /* TX1: Green */ - fprintf(fd,"%c%c%c",0,255,0); - break; - - case 8: - /* TX2: Cyan */ - fprintf(fd,"%c%c%c",0,255,255); - break; - - case 9: - /* TX1 + TX2: Yellow */ - fprintf(fd,"%c%c%c",255,255,0); - break; - - case 16: - /* TX3: Medium Violet */ - fprintf(fd,"%c%c%c",147,112,219); - break; - - case 17: - /* TX1 + TX3: Pink */ - fprintf(fd,"%c%c%c",255,192,203); - break; - - case 24: - /* TX2 + TX3: Orange */ - fprintf(fd,"%c%c%c",255,165,0); - break; - - case 25: - /* TX1 + TX2 + TX3: Dark Green */ - fprintf(fd,"%c%c%c",0,100,0); - break; - - case 32: - /* TX4: Sienna 1 */ - fprintf(fd,"%c%c%c",255,130,71); - break; - - case 33: - /* TX1 + TX4: Green Yellow */ - fprintf(fd,"%c%c%c",173,255,47); - break; - - case 40: - /* TX2 + TX4: Dark Sea Green 1 */ - fprintf(fd,"%c%c%c",193,255,193); - break; - - case 41: - /* TX1 + TX2 + TX4: Blanched Almond */ - fprintf(fd,"%c%c%c",255,235,205); - break; - - case 48: - /* TX3 + TX4: Dark Turquoise */ - fprintf(fd,"%c%c%c",0,206,209); - break; - - case 49: - /* TX1 + TX3 + TX4: Medium Spring Green */ - fprintf(fd,"%c%c%c",0,250,154); - break; - - case 56: - /* TX2 + TX3 + TX4: Tan */ - fprintf(fd,"%c%c%c",210,180,140); - break; - - case 57: - /* TX1 + TX2 + TX3 + TX4: Gold2 */ - fprintf(fd,"%c%c%c",238,201,0); - break; - - default: - if (ngs) /* No terrain */ - fprintf(fd,"%c%c%c",255,255,255); - else - { - /* Sea-level: Medium Blue */ - if (dem[indx].data[x0][y0]==0) - fprintf(fd,"%c%c%c",0,0,170); - else - { - /* Elevation: Greyscale */ - terrain=(unsigned)(0.5+pow((double)(dem[indx].data[x0][y0]-min_elevation),one_over_gamma)*conversion); - fprintf(fd,"%c%c%c",terrain,terrain,terrain); - } - } - } - } - - else - { - /* We should never get here, but if */ - /* we do, display the region as black */ - - fprintf(fd,"%c%c%c",0,0,0); - } - } - } - - fclose(fd); - -} - - -void LoadTopoData(int max_lon, int min_lon, int max_lat, int min_lat, int winfiles) -{ - /* 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; - - if (winfiles==1){ - if (ippd==3600) - snprintf(string,19,"%d=%d=%d=%d=hd",x, x+1, ymin, ymax); - else - snprintf(string,16,"%d=%d=%d=%d",x, x+1, ymin, ymax); - - }else{ - if (ippd==3600) - snprintf(string,19,"%d:%d:%d:%d-hd",x, x+1, ymin, ymax); - else - snprintf(string,16,"%d:%d:%d:%d",x, x+1, ymin, ymax); - } - - LoadSDF(string,winfiles); - } - } - - 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; - - if (winfiles==1){ - if (ippd==3600) - snprintf(string,19,"%d=%d=%d=%d=hd",x, x+1, ymin, ymax); - else - snprintf(string,16,"%d=%d=%d=%d",x, x+1, ymin, ymax); - - }else{ - if (ippd==3600) - snprintf(string,19,"%d:%d:%d:%d-hd",x, x+1, ymin, ymax); - else - snprintf(string,16,"%d:%d:%d:%d",x, x+1, ymin, ymax); - } - - LoadSDF(string,winfiles); - } - } -} - - - -void LoadUDT(char *filename) -{ - /* This function reads a file containing User-Defined Terrain - features for their addition to the digital elevation model - data used by SPLAT!. Elevations in the UDT file are evaluated - and then copied into a temporary file under /tmp. Then the - contents of the temp file are scanned, and if found to be unique, - are added to the ground elevations described by the digital - elevation data already loaded into memory. */ - - int i, x, y, z, ypix, xpix, tempxpix, tempypix, fd=0, n=0, pixelfound=0; - char input[80], str[3][80], tempname[15], *pointer=NULL, *s=NULL; - double latitude, longitude, height, tempheight; - FILE *fd1=NULL, *fd2=NULL; - - strcpy(tempname,"/tmp/XXXXXX\0"); - - fd1=fopen(filename,"r"); - - if (fd1!=NULL) - { - fd=mkstemp(tempname); - fd2=fopen(tempname,"w"); - - s=fgets(input,78,fd1); - - pointer=strchr(input,';'); - - if (pointer!=NULL) - *pointer=0; - - - while (feof(fd1)==0) - { - /* Parse line for latitude, longitude, height */ - - for (x=0, y=0, z=0; x<78 && input[x]!=0 && z<3; x++) - { - if (input[x]!=',' && y<78) - { - str[z][y]=input[x]; - y++; - } - - else - { - str[z][y]=0; - z++; - y=0; - } - } - - latitude=ReadBearing(str[0]); - longitude=ReadBearing(str[1]); - - if (longitude<0.0) - longitude+=360; - - /* Remove and/or from antenna height string */ - - for (i=0; str[2][i]!=13 && str[2][i]!=10 && str[2][i]!=0; i++); - - str[2][i]=0; - - /* The terrain feature may be expressed in either - feet or meters. If the letter 'M' or 'm' is - discovered in the string, then this is an - indication that the value given is expressed - in meters. Otherwise the height is interpreted - as being expressed in feet. */ - - for (i=0; str[2][i]!='M' && str[2][i]!='m' && str[2][i]!=0 && i<48; i++); - - if (str[2][i]=='M' || str[2][i]=='m') - { - str[2][i]=0; - height=rint(atof(str[2])); - } - - else - { - str[2][i]=0; - height=rint(METERS_PER_FOOT*atof(str[2])); - } - - if (height>0.0) - fprintf(fd2,"%d, %d, %f\n",(int)rint(latitude/dpp), (int)rint(longitude/dpp), height); - - - s=fgets(input,78,fd1); - - pointer=strchr(input,';'); - - if (pointer!=NULL) - *pointer=0; - } - - fclose(fd1); - fclose(fd2); - close(fd); - - - fd1=fopen(tempname,"r"); - fd2=fopen(tempname,"r"); - - y=0; - - n=fscanf(fd1,"%d, %d, %lf", &xpix, &ypix, &height); - - do - { - x=0; - z=0; - - n=fscanf(fd2,"%d, %d, %lf", &tempxpix, &tempypix, &tempheight); - - do - { - if (x>y && xpix==tempxpix && ypix==tempypix) - { - z=1; /* Dupe! */ - - if (tempheight>height) - height=tempheight; - } - - else - { - n=fscanf(fd2,"%d, %d, %lf", &tempxpix, &tempypix, &tempheight); - x++; - } - - } while (feof(fd2)==0 && z==0); - - if (z==0) /* No duplicate found */ - - //fprintf(stdout,"%lf, %lf \n",xpix*dpp, ypix*dpp); - fflush(stdout); - pixelfound = AddElevation(xpix*dpp, ypix*dpp, height); - //fprintf(stdout,"%d \n",pixelfound); - fflush(stdout); - - n=fscanf(fd1,"%d, %d, %lf", &xpix, &ypix, &height); - y++; - - rewind(fd2); - - } while (feof(fd1)==0); - - fclose(fd1); - fclose(fd2); - unlink(tempname); - } - -} - -void PlotPath(struct site source, struct site destination, char mask_value) -{ - /* This function analyzes the path between the source and - destination locations. It determines which points along - the path have line-of-sight visibility to the source. - Points along with path having line-of-sight visibility - to the source at an AGL altitude equal to that of the - destination location are stored by setting bit 1 in the - mask[][] array, which are displayed in green when PPM - maps are later generated by SPLAT!. */ - - char block; - int x, y; - register double cos_xmtr_angle, cos_test_angle, test_alt; - double distance, rx_alt, tx_alt; - - ReadPath(source,destination); - - for (y=0; y=0 && block==0; x--) - { - distance=5280.0*(path.distance[y]-path.distance[x]); - test_alt=earthradius+(path.elevation[x]==0.0?path.elevation[x]:path.elevation[x]+clutter); - - 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 - 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 - be if the actual angles were compared. */ - - if (cos_xmtr_angle>=cos_test_angle) - block=1; - } - - if (block==0) - OrMask(path.lat[y],path.lon[y],mask_value); - } - } -} - -void ObstructionAnalysis(struct site xmtr, struct site rcvr, double f, FILE *outfile) -{ - /* Perform an obstruction analysis along the - path between receiver and transmitter. */ - - int x; - struct site site_x; - double h_r, h_t, h_x, h_r_orig, cos_tx_angle, cos_test_angle, - cos_tx_angle_f1, cos_tx_angle_fpt6, d_tx, d_x, - h_r_f1, h_r_fpt6, h_f, h_los, lambda=0.0; - char string[255], string_fpt6[255], string_f1[255]; - - 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); - - if (clutter>0.0) - { - fprintf(outfile,"Terrain has been raised by"); - - if (metric) - fprintf(outfile," %.2f meters",METERS_PER_FOOT*clutter); - else - fprintf(outfile," %.2f feet",clutter); - - fprintf(outfile," to account for ground clutter.\n\n"); - } - - /* 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 ppa!'s 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+clutter; - 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(outfile,"Between %s and %s, obstructions were detected at:\n\n",rcvr.name,xmtr.name); - - if (site_x.lat>=0.0) - { - if (metric) - fprintf(outfile," %8.4f N,%9.4f W, %5.2f kilometers, %6.2f meters AMSL\n",site_x.lat, site_x.lon, KM_PER_MILE*(d_x/5280.0), METERS_PER_FOOT*(h_x-earthradius)); - else - fprintf(outfile," %8.4f N,%9.4f W, %5.2f miles, %6.2f feet AMSL\n",site_x.lat, site_x.lon, d_x/5280.0, h_x-earthradius); - } - - else - { - if (metric) - fprintf(outfile," %8.4f S,%9.4f W, %5.2f kilometers, %6.2f meters AMSL\n",-site_x.lat, site_x.lon, KM_PER_MILE*(d_x/5280.0), METERS_PER_FOOT*(h_x-earthradius)); - else - - fprintf(outfile," %8.4f S,%9.4f W, %5.2f miles, %6.2f feet AMSL\n",-site_x.lat, site_x.lon, d_x/5280.0, h_x-earthradius); - } - } - - 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... */ - - 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_fh_r_orig) - { - if (metric) - snprintf(string,150,"\nAntenna at %s must be raised to at least %.2f meters AGL\nto clear all obstructions detected.\n",rcvr.name, METERS_PER_FOOT*(h_r-GetElevation(rcvr)-earthradius)); - else - snprintf(string,150,"\nAntenna at %s must be raised to at least %.2f feet AGL\nto clear all obstructions detected.\n",rcvr.name, h_r-GetElevation(rcvr)-earthradius); - } - - else - snprintf(string,150,"\nNo obstructions to LOS path due to terrain were detected\n"); - - if (f) - { - if (h_r_fpt6>h_r_orig) - { - if (metric) - snprintf(string_fpt6,150,"\nAntenna at %s must be raised to at least %.2f meters AGL\nto clear %.0f%c of the first Fresnel zone.\n",rcvr.name, METERS_PER_FOOT*(h_r_fpt6-GetElevation(rcvr)-earthradius),fzone_clearance*100.0,37); - - else - snprintf(string_fpt6,150,"\nAntenna at %s must be raised to at least %.2f feet AGL\nto clear %.0f%c of the first Fresnel zone.\n",rcvr.name, h_r_fpt6-GetElevation(rcvr)-earthradius,fzone_clearance*100.0,37); - } - - else - snprintf(string_fpt6,150,"\n%.0f%c of the first Fresnel zone is clear.\n",fzone_clearance*100.0,37); - - if (h_r_f1>h_r_orig) - { - if (metric) - snprintf(string_f1,150,"\nAntenna at %s must be raised to at least %.2f meters AGL\nto clear the first Fresnel zone.\n",rcvr.name, METERS_PER_FOOT*(h_r_f1-GetElevation(rcvr)-earthradius)); - - else - snprintf(string_f1,150,"\nAntenna at %s must be raised to at least %.2f feet AGL\nto clear the first Fresnel zone.\n",rcvr.name, h_r_f1-GetElevation(rcvr)-earthradius); - - } - - else - snprintf(string_f1,150,"\nThe first Fresnel zone is clear.\n"); - } - - fprintf(outfile,"%s",string); - - if (f) - { - fprintf(outfile,"%s",string_f1); - fprintf(outfile,"%s",string_fpt6); - } - - -} - - -void PathReport(struct site source, struct site destination, char *name, char graph_it) -{ - /* This function writes a PPA Path Report (name.txt) to - the filesystem. If (graph_it == 1), then gnuplot is invoked - to generate an appropriate output file indicating the Longley-Rice - model loss between the source and destination locations. - "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, .png is assumed. */ - - int x, y, z, errnum; - char basename[255], term[30], ext[15], strmode[100], - report_name[80], block=0; - double maxloss=-100000.0, minloss=100000.0, angle1, angle2, - azimuth, pattern=1.0, patterndB=0.0, - total_loss=0.0, cos_xmtr_angle, cos_test_angle=0.0, - source_alt, test_alt, dest_alt, source_alt2, dest_alt2, - distance, elevation, four_thirds_earth, - free_space_loss=0.0, eirp=0.0, voltage, rxp, power_density; - FILE *fd=NULL, *fd2=NULL; - - //sprintf(report_name,"%s.txt",*name); - snprintf(report_name,80,"%s.txt%c",name,0); - - - - four_thirds_earth=FOUR_THIRDS*EARTHRADIUS; - - /*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]='_'; */ - - fd2=fopen(report_name,"w"); - - fprintf(fd2,"\n\t\t--==[ Path Profile Analysis ]==--\n\n"); - //fprintf(fd2,"%s\n\n",dashes); - fprintf(fd2,"Transmitter site: %s\n",source.name); - - if (source.lat>=0.0) - { - fprintf(fd2,"Site location: %.4f North / %.4f West\n",source.lat, source.lon); - //fprintf(fd2, " (%s N / ", source.lat); - } - - else - { - - fprintf(fd2,"Site location: %.4f South / %.4f West\n",-source.lat, source.lon); - //fprintf(fd2, " (%s S / ", source.lat); - } - - - if (metric) - { - fprintf(fd2,"Ground elevation: %.2f meters AMSL\n",METERS_PER_FOOT*GetElevation(source)); - fprintf(fd2,"Antenna height: %.2f meters AGL / %.2f meters AMSL\n",METERS_PER_FOOT*source.alt,METERS_PER_FOOT*(source.alt+GetElevation(source))); - } - - else - { - 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)); - } - -/* - haavt=haat(source); - - if (haavt>-4999.0) - { - if (metric) - fprintf(fd2,"Antenna height above average terrain: %.2f meters\n",METERS_PER_FOOT*haavt); - else - fprintf(fd2,"Antenna height above average terrain: %.2f feet\n",haavt); - } -*/ - azimuth=Azimuth(source,destination); - angle1=ElevationAngle(source,destination); - angle2=ElevationAngle2(source,destination,earthradius); - - if (got_azimuth_pattern || got_elevation_pattern) - { - x=(int)rint(10.0*(10.0-angle2)); - - if (x>=0 && x<=1000) - pattern=(double)LR.antenna_pattern[(int)rint(azimuth)][x]; - - patterndB=20.0*log10(pattern); - } - - if (metric) - fprintf(fd2,"Distance to %s: %.2f kilometers\n",destination.name,KM_PER_MILE*Distance(source,destination)); - - else - fprintf(fd2,"Distance to %s: %.2f miles\n",destination.name,Distance(source,destination)); - - fprintf(fd2,"Azimuth to %s: %.2f degrees\n",destination.name,azimuth); - - if (angle1>=0.0) - fprintf(fd2,"Elevation angle to %s: %+.4f degrees\n",destination.name,angle1); - - else - fprintf(fd2,"Depression angle to %s: %+.4f degrees\n",destination.name,angle1); - - if ((angle2-angle1)>0.0001) - { - if (angle2<0.0) - fprintf(fd2,"Depression\n"); - else - fprintf(fd2,"Elevation\n"); - - //fprintf(fd2," angle to the first obstruction: %+.4f degrees\n",angle2); - } - - //fprintf(fd2,"\n%s\n\n",dashes); - - /* Receiver */ - - fprintf(fd2,"Receiver site: %s\n",destination.name); - - if (destination.lat>=0.0) - { - fprintf(fd2,"Site location: %.4f North / %.4f West\n",destination.lat, destination.lon); - //fprintf(fd2, " (%s N / ", destination.lat); - } - - else - { - fprintf(fd2,"Site location: %.4f South / %.4f West\n",-destination.lat, destination.lon); - //fprintf(fd2, " (%s S / ", destination.lat); - } - - if (metric) - { - fprintf(fd2,"Ground elevation: %.2f meters AMSL\n",METERS_PER_FOOT*GetElevation(destination)); - fprintf(fd2,"Antenna height: %.2f meters AGL / %.2f meters AMSL\n",METERS_PER_FOOT*destination.alt, METERS_PER_FOOT*(destination.alt+GetElevation(destination))); - } - - else - { - 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)); - } - - /*haavt=haat(destination); - - if (haavt>-4999.0) - { - if (metric) - fprintf(fd2,"Antenna height above average terrain: %.2f meters\n",METERS_PER_FOOT*haavt); - else - fprintf(fd2,"Antenna height above average terrain: %.2f feet\n",haavt); - }*/ - - if (metric) - fprintf(fd2,"Distance to %s: %.2f kilometers\n",source.name,KM_PER_MILE*Distance(source,destination)); - - else - fprintf(fd2,"Distance to %s: %.2f miles\n",source.name,Distance(source,destination)); - - azimuth=Azimuth(destination,source); - - angle1=ElevationAngle(destination,source); - angle2=ElevationAngle2(destination,source,earthradius); - - fprintf(fd2,"Azimuth to %s: %.2f degrees\n",source.name,azimuth); - - if (angle1>=0.0) - fprintf(fd2,"Elevation angle to %s: %+.4f degrees\n",source.name,angle1); - - else - fprintf(fd2,"Depression angle to %s: %+.4f degrees\n",source.name,angle1); - - if ((angle2-angle1)>0.0001) - { - if (angle2<0.0) - fprintf(fd2,"Depression"); - else - fprintf(fd2,"Elevation"); - - //fprintf(fd2," angle to the first obstruction: %+.4f degrees\n",angle2); - } - - //fprintf(fd2,"\n%s\n\n",dashes); - - if (LR.frq_mhz>0.0) - { - fprintf(fd2,"Longley-Rice path calculation parameters used in this analysis:\n\n"); - fprintf(fd2,"Earth's Dielectric Constant: %.3lf\n",LR.eps_dielect); - fprintf(fd2,"Earth's Conductivity: %.3lf Siemens/meter\n",LR.sgm_conductivity); - fprintf(fd2,"Atmospheric Bending Constant (N-units): %.3lf ppm\n",LR.eno_ns_surfref); - fprintf(fd2,"Frequency: %.3lf MHz\n",LR.frq_mhz); - fprintf(fd2,"Radio Climate: %d (",LR.radio_climate); - - switch (LR.radio_climate) - { - case 1: - fprintf(fd2,"Equatorial"); - break; - - case 2: - fprintf(fd2,"Continental Subtropical"); - break; - - case 3: - fprintf(fd2,"Maritime Subtropical"); - break; - - case 4: - fprintf(fd2,"Desert"); - break; - - case 5: - fprintf(fd2,"Continental Temperate"); - break; - - case 6: - fprintf(fd2,"Martitime Temperate, Over Land"); - break; - - case 7: - fprintf(fd2,"Maritime Temperate, Over Sea"); - break; - - default: - fprintf(fd2,"Unknown"); - } - - fprintf(fd2,")\nPolarisation: %d (",LR.pol); - - if (LR.pol==0) - fprintf(fd2,"Horizontal"); - - if (LR.pol==1) - fprintf(fd2,"Vertical"); - - fprintf(fd2,")\nFraction of Situations: %.1lf%c\n",LR.conf*100.0,37); - fprintf(fd2,"Fraction of Time: %.1lf%c\n",LR.rel*100.0,37); - - if (LR.erp!=0.0) - { - fprintf(fd2,"Transmitter ERP: "); - - if (LR.erp<1.0) - fprintf(fd2,"%.1lf milliwatts",1000.0*LR.erp); - - if (LR.erp>=1.0 && LR.erp<10.0) - fprintf(fd2,"%.1lf Watts",LR.erp); - - if (LR.erp>=10.0 && LR.erp<10.0e3) - fprintf(fd2,"%.0lf Watts",LR.erp); - - if (LR.erp>=10.0e3) - fprintf(fd2,"%.3lf kilowatts",LR.erp/1.0e3); - - dBm=10.0*(log10(LR.erp*1000.0)); - fprintf(fd2," (%+.2f dBm)\n",dBm); - - /* EIRP = ERP + 2.14 dB */ - - fprintf(fd2,"Transmitter EIRP: "); - - eirp=LR.erp*1.636816521; - - if (eirp<1.0) - fprintf(fd2,"%.1lf milliwatts",1000.0*eirp); - - if (eirp>=1.0 && eirp<10.0) - fprintf(fd2,"%.1lf Watts",eirp); - - if (eirp>=10.0 && eirp<10.0e3) - fprintf(fd2,"%.0lf Watts",eirp); - - if (eirp>=10.0e3) - fprintf(fd2,"%.3lf kilowatts",eirp/1.0e3); - - dBm=10.0*(log10(eirp*1000.0)); - fprintf(fd2," (%+.2f dBm)\n",dBm); - } - - fprintf(fd2,"\n%s\n\n",dashes); - - fprintf(fd2,"Summary for the link between %s and %s:\n\n",source.name, destination.name); - - if (patterndB!=0.0) - fprintf(fd2,"%s antenna pattern towards %s: %.3f (%.2f dB)\n", source.name, destination.name, pattern, patterndB); - - ReadPath(source, destination); /* source=TX, destination=RX */ - - /* Copy elevations plus clutter along - path into the elev[] array. */ - - for (x=1; x=cos_test_angle) - block=1; - } - - /* At this point, we have the elevation angle - to the first obstruction (if it exists). */ - } - - /* Determine path loss for each point along the - path using Longley-Rice's point_to_point mode - starting at x=2 (number_of_points = 1), the - shortest distance terrain can play a role in - path loss. */ - - elev[0]=y-1; /* (number of points - 1) */ - - /* Distance between elevation samples */ - - elev[1]=METERS_PER_MILE*(path.distance[y]-path.distance[y-1]); - - point_to_point(elev, source.alt*METERS_PER_FOOT, - destination.alt*METERS_PER_FOOT, LR.eps_dielect, - LR.sgm_conductivity, LR.eno_ns_surfref, LR.frq_mhz, - LR.radio_climate, LR.pol, LR.conf, LR.rel, loss, - strmode, errnum); - - if (block) - elevation=((acos(cos_test_angle))/DEG2RAD)-90.0; - else - elevation=((acos(cos_xmtr_angle))/DEG2RAD)-90.0; - - /* Integrate the antenna's radiation - pattern into the overall path loss. */ - - x=(int)rint(10.0*(10.0-elevation)); - - if (x>=0 && x<=1000) - { - pattern=(double)LR.antenna_pattern[(int)azimuth][x]; - - if (pattern!=0.0) - patterndB=20.0*log10(pattern); - } - - else - patterndB=0.0; - - total_loss=loss-patterndB; - - /* if (metric) - fprintf(fd,"%.3f %.3f\n",KM_PER_MILE*(path.distance[path.length-1]-path.distance[y]),total_loss); - - else - fprintf(fd,"%.3f %.3f\n",path.distance[path.length-1]-path.distance[y],total_loss); - */ - - if (total_loss>maxloss) - maxloss=total_loss; - - if (total_loss0 && name[x]!='.'; x--); - - if (x>0) /* Extension found */ - { - for (z=x+1; z<=y && (z-(x+1))<10; z++) - { - ext[z-(x+1)]=tolower(name[z]); - term[z-(x+1)]=name[z]; - } - - ext[z-(x+1)]=0; /* Ensure an ending 0 */ - term[z-(x+1)]=0; - basename[x]=0; - } - } - - if (ext[0]==0) /* No extension -- Default is png */ - { - strncpy(term,"png\0",4); - strncpy(ext,"png\0",4); - } - - /* Either .ps or .postscript may be used - as an extension for postscript output. */ - - if (strncmp(term,"postscript",10)==0) - strncpy(ext,"ps\0",3); - - else if (strncmp(ext,"ps",2)==0) - strncpy(term,"postscript enhanced color\0",26); - - fd=fopen("ppa.gp","w"); - - fprintf(fd,"set grid\n"); - fprintf(fd,"set yrange [%2.3f to %2.3f]\n", minloss, maxloss); - fprintf(fd,"set encoding iso_8859_1\n"); - fprintf(fd,"set term %s\n",term); - fprintf(fd,"set title \"Path Loss Profile Along Path Between %s and %s (%.2f%c azimuth)\"\n",destination.name, source.name, Azimuth(destination,source),176); - - if (metric) - fprintf(fd,"set xlabel \"Distance Between %s and %s (%.2f kilometers)\"\n",destination.name,source.name,KM_PER_MILE*Distance(destination,source)); - else - fprintf(fd,"set xlabel \"Distance Between %s and %s (%.2f miles)\"\n",destination.name,source.name,Distance(destination,source)); - - if (got_azimuth_pattern || got_elevation_pattern) - fprintf(fd,"set ylabel \"Total Path Loss (including TX antenna pattern) (dB)"); - else - fprintf(fd,"set ylabel \"Longley-Rice Path Loss (dB)"); - - fprintf(fd,"\"\nset output \"%s.%s\"\n",basename,ext); - fprintf(fd,"plot \"profile.gp\" title \"Path Loss\" with lines\n"); - - fclose(fd); - - x=system("gnuplot ppa.gp"); - - if (x!=-1) - { - if (gpsav==0) - { - //unlink("ppa.gp"); - //unlink("profile.gp"); - //unlink("reference.gp"); - } - - - } - - else - fprintf(stderr,"\n*** ERROR: Error occurred invoking gnuplot!\n"); - } - -} - -void SeriesData(struct site source, struct site destination, char *name, unsigned char fresnel_plot, unsigned char normalised) -{ - - int x, y, z; - char basename[255], term[30], ext[15], profilename[255], referencename[255],cluttername[255],curvaturename[255],fresnelname[255],fresnel60name[255]; - double a, b, c, height=0.0, refangle, cangle, maxheight=-100000.0, - minheight=100000.0, lambda=0.0, f_zone=0.0, fpt6_zone=0.0, - nm=0.0, nb=0.0, ed=0.0, es=0.0, r=0.0, d=0.0, d1=0.0, - terrain, azimuth, distance, minterrain=100000.0, - minearth=100000.0; - struct site remote; - FILE *fd=NULL, *fd1=NULL, *fd2=NULL, *fd3=NULL, *fd4=NULL, *fd5=NULL; - - ReadPath(destination,source); - azimuth=Azimuth(destination,source); - distance=Distance(destination,source); - refangle=ElevationAngle(destination,source); - b=GetElevation(destination)+destination.alt+earthradius; - - if (fresnel_plot) - { - lambda=9.8425e8/(LR.frq_mhz*1e6); - d=5280.0*path.distance[path.length-1]; - } - - if (normalised) - { - ed=GetElevation(destination); - es=GetElevation(source); - nb=-destination.alt-ed; - nm=(-source.alt-es-nb)/(path.distance[path.length-1]); - } - - strcpy(profilename,name); - strcat(profilename,"_profile\0"); - strcpy(referencename,name); - strcat(referencename,"_reference\0"); - strcpy(cluttername,name); - strcat(cluttername,"_clutter\0"); - strcpy(curvaturename,name); - strcat(curvaturename,"_curvature\0"); - strcpy(fresnelname,name); - strcat(fresnelname,"_fresnel\0"); - strcpy(fresnel60name,name); - strcat(fresnel60name,"_fresnel60\0"); - - fd=fopen(profilename,"wb"); - if (clutter>0.0) - fd1=fopen(cluttername,"wb"); - fd2=fopen(referencename,"wb"); - fd5=fopen(curvaturename, "wb"); - - if ((LR.frq_mhz>=20.0) && (LR.frq_mhz<=100000.0) && fresnel_plot) - { - fd3=fopen(fresnelname, "wb"); - fd4=fopen(fresnel60name, "wb"); - } - - for (x=0; x=20.0) && (LR.frq_mhz<=100000.0) && fresnel_plot) - { - d1=5280.0*path.distance[x]; - f_zone=-1.0*sqrt(lambda*d1*(d-d1)/d); - fpt6_zone=f_zone*fzone_clearance; - } - - if (normalised) - { - r=-(nm*path.distance[x])-nb; - height+=r; - - if ((LR.frq_mhz>=20.0) && (LR.frq_mhz<=100000.0) && fresnel_plot) - { - f_zone+=r; - fpt6_zone+=r; - } - } - - else - r=0.0; - - if (metric) - { - if (METERS_PER_FOOT*height > 0){ - fprintf(fd,"%.3f %.3f\n",KM_PER_MILE*path.distance[x],METERS_PER_FOOT*height); - } - - if (fd1!=NULL && x>0 && x0 && x=20.0) && (LR.frq_mhz<=100000.0) && fresnel_plot) - { - if (metric) - { - fprintf(fd3,"%.3f %.3f\n",KM_PER_MILE*path.distance[x],METERS_PER_FOOT*f_zone); - fprintf(fd4,"%.3f %.3f\n",KM_PER_MILE*path.distance[x],METERS_PER_FOOT*fpt6_zone); - } - - else - { - fprintf(fd3,"%.3f %.3f\n",path.distance[x],f_zone); - fprintf(fd4,"%.3f %.3f\n",path.distance[x],fpt6_zone); - } - - if (f_zonemaxheight) - maxheight=height+clutter; - - if (heightmaxheight) - maxheight=r; - - if (terrain=20.0) && (LR.frq_mhz<=100000.0) && fresnel_plot) - { - if (metric) - { - fprintf(fd3,"%.3f %.3f",KM_PER_MILE*path.distance[path.length-1],METERS_PER_FOOT*r); - fprintf(fd4,"%.3f %.3f",KM_PER_MILE*path.distance[path.length-1],METERS_PER_FOOT*r); - } - - else - { - fprintf(fd3,"%.3f %.3f",path.distance[path.length-1],r); - fprintf(fd4,"%.3f %.3f",path.distance[path.length-1],r); - } - } - - if (r>maxheight) - maxheight=r; - - if (r=20.0) && (LR.frq_mhz<=100000.0) && fresnel_plot) - { - fclose(fd3); - fclose(fd4); - } - - if (name[0]=='.') - { - strncpy(basename,"profile\0",8); - strncpy(term,"png\0",4); - strncpy(ext,"png\0",4); - } - - else - { - - ext[0]=0; - y=strlen(name); - strncpy(basename,name,254); - - for (x=y-1; x>0 && name[x]!='.'; x--); - - if (x>0) - { - for (z=x+1; z<=y && (z-(x+1))<10; z++) - { - ext[z-(x+1)]=tolower(name[z]); - term[z-(x+1)]=name[z]; - } - - ext[z-(x+1)]=0; - term[z-(x+1)]=0; - basename[x]=0; - } - - if (ext[0]==0) - { - strncpy(term,"png\0",4); - strncpy(ext,"png\0",4); - } - } - - fprintf(stdout,"\n"); - fflush(stdout); - -} - -int main(int argc, char *argv[]) -{ - int x, y, z=0, min_lat, min_lon, max_lat, max_lon, - rxlat, rxlon, txlat, txlon, west_min, west_max, - north_min, north_max, propmodel, winfiles,knifeedge=0,ppa=0,normalise=0, haf=0; - - unsigned char LRmap=0, txsites=0,topomap=0, geo=0, kml=0, area_mode=0, max_txsites,ngs=0; - - char mapfile[255], longley_file[255], udt_file[255],ano_filename[255]; - - double altitude=0.0, altitudeLR=0.0, tx_range=0.0, - rx_range=0.0, deg_range=0.0, deg_limit=0.0, - deg_range_lon; - - strncpy(ss_name,"Signal Server\0",14); - - if (argc==1) - { - fprintf(stdout,"\n\t\t -- %s %.2f options --\n\n",ss_name, version); - fprintf(stdout," -d Directory containing .sdf tiles\n"); - fprintf(stdout," -lat Tx Latitude (decimal degrees) -70/+70\n"); - fprintf(stdout," -lon Tx Longitude (decimal degrees) -180/+180\n"); - fprintf(stdout," -txh Tx Height (above ground)\n"); - fprintf(stdout," -rla (Optional) Rx Latitude for PPA (decimal degrees) -70/+70\n"); - fprintf(stdout," -rlo (Optional) Rx Longitude for PPA (decimal degrees) -180/+180\n"); - fprintf(stdout," -f Tx Frequency (MHz) 20MHz to 100GHz (LOS after 20GHz)\n"); - fprintf(stdout," -erp Tx Effective Radiated Power (Watts)\n"); - fprintf(stdout," -rxh Rx Height(s) (optional. Default=0.1)\n"); - fprintf(stdout," -rt Rx Threshold (dB / dBm / dBuV/m)\n"); - fprintf(stdout," -hp Horizontal Polarisation (default=vertical)\n"); - fprintf(stdout," -gc Ground clutter (feet/meters)\n"); - fprintf(stdout," -udt User defined terrain filename\n"); - fprintf(stdout," -dbm Plot Rxd signal power instead of field strength\n"); - fprintf(stdout," -m Metric units of measurement\n"); - fprintf(stdout," -te Terrain code 1-6 (optional)\n"); - fprintf(stdout," -terdic Terrain dielectric value 2-80 (optional)\n"); - fprintf(stdout," -tercon Terrain conductivity 0.01-0.0001 (optional)\n"); - fprintf(stdout," -cl Climate code 1-6 (optional)\n"); - fprintf(stdout," -o Filename. Required. \n"); - fprintf(stdout," -R Radius (miles/kilometers)\n"); - fprintf(stdout," -res Pixels per degree. 300/600/1200(default)/3600 (optional)\n"); - fprintf(stdout," -t Terrain background\n"); - fprintf(stdout," -pm Prop model. 1: ITM, 2: LOS, 3-5: Hata, 6: COST231, 7: ITU525, 8: ITWOM3.0\n"); - fprintf(stdout," -ked Knife edge diffraction (Default for ITM)\n"); - fprintf(stdout," -ng Normalise Path Profile graph\n"); - fprintf(stdout," -haf Halve 1 or 2 (optional)\n"); - - - fflush(stdout); - - return 1; - } - - y=argc-1; - - kml=0; - geo=0; - dbm=0; - gpsav=0; - metric=0; - //rxfile[0]=0; - //txfile[0]=0; - string[0]=0; - mapfile[0]=0; - clutter=0.0; - forced_erp=-1.0; - forced_freq=0.0; - //elevation_file[0]=0; - //terrain_file[0]=0; - sdf_path[0]=0; - udt_file[0]=0; - path.length=0; - max_txsites=30; - fzone_clearance=0.6; - contour_threshold=0; - - longley_file[0]=0; - ano_filename[0]=0; - //ani_filename[0]=0; - earthradius=EARTHRADIUS; - max_range=1.0; - propmodel=1; //ITM - winfiles=0; - - lat=0; - lon=0; - txh=0; - ngs=1; // no terrain background - kml=1; - - //map=1; - LRmap=1; - area_mode=1; - ippd=IPPD; // default resolution - - sscanf("0.1","%lf",&altitudeLR); - - // Defaults - LR.eps_dielect=15.0; // Farmland - LR.sgm_conductivity=0.005; // Farmland - LR.eno_ns_surfref=301.0; - LR.frq_mhz=19.0; // Deliberately too low - LR.radio_climate=5; // continental - LR.pol=1; // vert - LR.conf=0.50; - LR.rel=0.50; - LR.erp=0.0; // will default to Path Loss - - tx_site[0].lat=91.0; - tx_site[0].lon=361.0; - tx_site[1].lat=91.0; - tx_site[1].lon=361.0; - - for (x=0; x 90 || tx_site[0].lat < -90) - { - fprintf(stdout,"ERROR: Either the lat was missing or out of range!"); - exit(0); - - } - if (tx_site[0].lon > 360 || tx_site[0].lon < 0) - { - fprintf(stdout,"ERROR: Either the lon was missing or out of range!"); - exit(0); - - } - - if (LR.frq_mhz < 20 || LR.frq_mhz > 100000) - { - fprintf(stdout,"ERROR: Either the Frequency was missing or out of range!"); - exit(0); - } - if (LR.erp>5000000) - { - fprintf(stdout,"ERROR: Power was out of range!"); - exit(0); - - } - if (LR.eps_dielect > 80 || LR.eps_dielect < 0.1) - { - fprintf(stdout,"ERROR: Ground Dielectric value out of range!"); - exit(0); - - } - if (LR.sgm_conductivity > 0.01 || LR.sgm_conductivity < 0.000001) - { - fprintf(stdout,"ERROR: Ground conductivity out of range!"); - exit(0); - - } - - if (tx_site[0].alt < 0 || tx_site[0].alt > 60000) - { - fprintf(stdout,"ERROR: Tx altitude above ground was too high: %f", tx_site[0].alt); - exit(0); - } - if (altitudeLR < 0 || altitudeLR > 60000) - { - fprintf(stdout,"ERROR: Rx altitude above ground was too high!"); - exit(0); - } - - - if (ippd < 300 || ippd > 3600){ - fprintf(stdout,"ERROR: resolution out of range!"); - exit(0); - } - - if(contour_threshold < -200 || contour_threshold > 200) - { - fprintf(stdout,"ERROR: Receiver threshold out of range (-200 / +200)"); - exit(0); - } - if(propmodel>2 && propmodel<8 && LR.frq_mhz < 150){ - fprintf(stdout,"ERROR: Frequency too low for Propagation model"); - exit(0); - } - - /* ERROR DETECTION COMPLETE */ - - - - if (metric) - { - altitudeLR/=METERS_PER_FOOT; /* 10ft * 0.3 = 3.3m */ - max_range/=KM_PER_MILE; /* 10 / 1.6 = 7.5 */ - altitude/=METERS_PER_FOOT; - tx_site[0].alt/=METERS_PER_FOOT; /* Feet to metres */ - tx_site[1].alt/=METERS_PER_FOOT; /* Feet to metres */ - clutter/=METERS_PER_FOOT; /* Feet to metres */ - } - - /* Ensure a trailing '/' is present in sdf_path */ - - if (sdf_path[0]) - { - x=strlen(sdf_path); - - if (sdf_path[x-1]!='/' && x!=0) - { - sdf_path[x]='/'; - sdf_path[x+1]=0; - } - } - - x=0; - y=0; - - min_lat=70; - max_lat=-70; - - min_lon=(int)floor(tx_site[0].lon); - max_lon=(int)floor(tx_site[0].lon); - - - txlat=(int)floor(tx_site[0].lat); - txlon=(int)floor(tx_site[0].lon); - - if (txlatmax_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 (ppa==1) - { - rxlat=(int)floor(tx_site[1].lat); - rxlon=(int)floor(tx_site[1].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; - } - - /* Load the required SDF files */ - - LoadTopoData(max_lon, min_lon, max_lat, min_lat, winfiles); - - if (area_mode || topomap) - { - for (z=0; zdeg_limit) - deg_range=deg_limit; - - if (deg_range_lon>deg_limit) - deg_range_lon=deg_limit; - - north_min=(int)floor(tx_site[z].lat-deg_range); - north_max=(int)floor(tx_site[z].lat+deg_range); - - west_min=(int)floor(tx_site[z].lon-deg_range_lon); - - while (west_min<0) - west_min+=360; - - while (west_min>=360) - west_min-=360; - - west_max=(int)floor(tx_site[z].lon+deg_range_lon); - - while (west_max<0) - west_max+=360; - - while (west_max>=360) - west_max-=360; - - if (north_minmax_lat) - max_lat=north_max; - - if (LonDiff(west_min,min_lon)<0.0) - min_lon=west_min; - - if (LonDiff(west_max,max_lon)>=0.0) - max_lon=west_max; - } - - /* Load any additional SDF files, if required */ - - LoadTopoData(max_lon, min_lon, max_lat, min_lat, winfiles); - } - - // UDT clutter - LoadUDT (udt_file); - - - - - if(ppa==0){ - if (propmodel==2){ - PlotLOSMap(tx_site[0],altitudeLR,ano_filename); - DoLOS(mapfile,geo,kml,ngs,tx_site,txsites); - } - else{ - // 90% of effort here - PlotPropagation(tx_site[0],altitudeLR,ano_filename,propmodel,knifeedge,haf); - - // Near field bugfix - PutSignal(tx_site[0].lat,tx_site[0].lon,hottest); - for(lat=tx_site[0].lat-0.002;lat<=tx_site[0].lat+0.002;lat=lat+0.0005){ - for(lon=tx_site[0].lon-0.002;lon<=tx_site[0].lon+0.002;lon=lon+0.0005){ - PutSignal(lat,lon,hottest); - } - } - - if (LR.erp==0.0) - DoPathLoss(mapfile,geo,kml,ngs,tx_site,txsites); - else - if (dbm) - DoRxdPwr(mapfile,geo,kml,ngs,tx_site,txsites); - else - DoSigStr(mapfile,geo,kml,ngs,tx_site,txsites); - - - } - fprintf(stdout,"|%.5f",north); - fprintf(stdout,"|%.5f",east); - fprintf(stdout,"|%.5f",south); - fprintf(stdout,"|%.5f|",west); - - }else{ - strncpy(tx_site[0].name,"Tx",3); - strncpy(tx_site[1].name,"Rx",3); - PlotPath(tx_site[0],tx_site[1],1); - PathReport(tx_site[0],tx_site[1],tx_site[0].filename,0); - SeriesData(tx_site[0],tx_site[1],tx_site[0].filename,1,normalise); - } - fflush(stdout); - printf("\n"); - return 0; -} - diff --git a/models.cpp b/models.cpp deleted file mode 100644 index ef02b14..0000000 --- a/models.cpp +++ /dev/null @@ -1,276 +0,0 @@ -/***************************************************************************** -* RF propagation models for Signal Server by Alex Farrant, CloudRF.com * -* * -* 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 * -* version. * -* * -* This program is distributed in the hope that it will useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * -* for more details. * -*****************************************************************************/ - -#include -#include -#include -#include - -using namespace std; - -/* -Whilst every effort has been made to ensure the accuracy of the models, their accuracy is not guaranteed. -Finding a reputable paper to source these models from took a while. There was lots of bad copy-pasta out there. -A good paper: http://www.cl.cam.ac.uk/research/dtg/lce-pub/public/vsa23/VTC05_Empirical.pdf -*/ - -#define PI 3.14159265 - -/* Acute Angle from Rx point to an obstacle of height (opp) and distance (adj) */ -double incidenceAngle(double opp, double adj){ - return atan2(opp,adj) * 180 / PI; -} - -/* -Knife edge diffraction: -This is based upon a recognised formula like Huygens, but trades thoroughness for increased speed -which adds a proportional diffraction effect to obstacles. -*/ -double ked(double freq, double elev[], double rxh, double dkm){ -double obh,obd,rxobaoi=0,d,dipheight=25; - -obh=0; // Obstacle height -obd=0; // Obstacle distance - -dkm=dkm*1000; // KM to metres - - // walk along path - for(int n=2;n<(dkm/elev[1]);n++){ - - d = (n-2)*elev[1]; // no of points * delta = km - - //Find dip(s) - if(elev[n]<(obh+dipheight)){ - - // Angle from Rx point to obstacle - rxobaoi = incidenceAngle((obh-(elev[n]+rxh)),d-obd); - } else{ - // Line of sight or higher - rxobaoi=0; - } - - //note the highest point - if(elev[n]>obh){ - obh=elev[n]; - obd=d; - } - - } - -if(rxobaoi >= 0){ - return rxobaoi / (300/freq); // Diffraction angle divided by wavelength (m) -}else{ - return 0; -} - -} - -double HATApathLoss(float f,float TxH, float RxH, float d, int mode){ -/* -HATA model for cellular planning -Frequency (MHz) 150 to 1500MHz -Base station height 30-200m -Mobile station height 1-10m -Distance 1-20km -modes 1 = URBAN, 2 = SUBURBAN, 3 = OPEN -*/ - - if(f<150 || f>1500){ - printf("Error: Hata model frequency range 150-1500MHz\n"); - exit(EXIT_FAILURE); - } - float lRxH = log10(11.75*RxH); - float C_H = 3.2*lRxH*lRxH-4.97; - float logf = log10(f); - float L_u = 69.55 + 26.16*logf - 13.82*log10(TxH) - C_H + (44.9 - 6.55*log10(TxH))*log10(d); - - if(!mode || mode==1){ - return L_u; //URBAN - } - - if(mode==2){ //SUBURBAN - float logf_28 = log10(f/28); - return L_u - 2*logf_28*logf_28 - 5.4; - } - - if(mode==3){ //OPEN - return L_u - 4.78*logf*logf + 18.33*logf - 40.94; - } - - return 0; -} - -double COST231pathLoss(float f,float TxH, float RxH, float d, int mode){ -/* -COST231 extension to HATA model -Frequency 1500 to 2000MHz -TxH = Base station height 30 to 200m -RxH = Mobile station height 1 to 10m -Distance 1-20km -modes 1 = URBAN, 2 = SUBURBAN, 3 = OPEN -http://morse.colorado.edu/~tlen5510/text/classwebch3.html -*/ - if(f<150 || f>2000){ - printf("Error: COST231 Hata model frequency range 150-2000MHz\n"); - exit(EXIT_FAILURE); - } - - int C = 3; // 3dB for Urban - float lRxH = log10(11.75*RxH); - float C_H = 3.2*(lRxH*lRxH)-4.97; // Large city (conservative) - int c0 = 69.55; - int cf = 26.16; - if(f>1500){ - c0=46.3; - cf=33.9; - } - if(mode==2){ - C = 0; // Medium city (average) - C_H = 8.29*(lRxH*lRxH)-1.1; - } - if(mode==3){ - C = -3; // Small city (Optimistic) - C_H = (1.1*log10(f) - 0.7) * RxH - (1.56 * log10(f)) + 0.8; - } - float logf = log10(f); - double dbloss = c0 + (cf * logf) - (13.82 * log10(TxH)) - C_H + (44.9 - 6.55 * log10(TxH)) * log10(d) + C; - return dbloss; - } - -double SUIpathLoss(float f,float TxH, float RxH, float d, int mode){ - /* - f = Frequency (MHz) - TxH = Transmitter height (m) - RxH = Receiver height (m) - d = distance (km) - mode 1 = Hilly + trees - mode 2 = Flat + trees OR hilly + light foliage - mode 3 = Flat + light foliage - http://www.cl.cam.ac.uk/research/dtg/lce-pub/public/vsa23/VTC05_Empirical.pdf - */ - d=d*1000; // km to m - if(f<1900 || f>11000){ - printf("Error: SUI model frequency range 1.9-11GHz\n"); - exit(EXIT_FAILURE); - } - - // Terrain mode A is default - double a = 4.6; - double b = 0.0075; - double c = 12.6; - double s = 10.6; // Optional fading value - int XhCF = -10.8; - - if(mode==2){ - a=4.0; - b=0.0065; - c=17.1; - s=6; // average - } - if(mode==3){ - a=3.6; - b=0.005; - c=20; - s=3; // Optimistic - XhCF = -20; - } - double d0 = 100; - double A = 20 * log10((4*M_PI*d0)/(300/f)); - double y = (a - b * TxH) + c / TxH; - double Xf = 6 * log10(f/2000); - double Xh = XhCF * log10(RxH/2); - return A + (10*y*log10(d/d0)) + Xf + Xh + s; -} - -double ECC33pathLoss(float f,float TxH, float RxH, float d, int mode){ - - if(f<700 || f>3500){ - printf("Error: ECC33 model frequency range 700-3500MHz\n"); - exit(EXIT_FAILURE); - } - - // MHz to GHz - f=f/1000; - - double Gr = 0.759 * RxH - 1.862; // Big city with tall buildings (1) - // PL = Afs + Abm - Gb - Gr - double Afs = 92.4 + 20 * log10(d) + 20 * log10(f); - double Abm = 20.41 + 9.83 * log10(d) + 7.894 * log10(f) + 9.56 * (log10(f) * log10(f)); - double Gb = log10(TxH/200) * (13.958 + 5.8 * (log10(d) * log10(d))); - if(mode>1){ // Medium city (Europe) - Gr = (42.57 + 13.7 * log10(f)) * (log10(RxH) - 0.585); - } - return Afs+Abm-Gb-Gr; -} - -double EricssonpathLoss(float f,float TxH, float RxH, float d, int mode){ - /* - http://research.ijcaonline.org/volume84/number7/pxc3892830.pdf - AKA Ericsson 9999 model - */ - // Default is Urban which bizarrely has lowest loss - double a0=36.2, a1=30.2, a2=-12, a3=0.1; - - if(f<150 || f>3500){ - printf("Error: Ericsson9999 model frequency range 150-3500MHz\n"); - exit(EXIT_FAILURE); - } - - if(mode==2){ // Suburban / Med loss - a0=43.2; - a1=68.93; - } - if(mode==1){ // "Rural" but has highest loss according to Ericsson. - a0=45.95; - a1=100.6; - } - double g1 = (11.75 * RxH) * (11.75 * RxH); - double g2 = (44.49 * log10(f)) - 4.78 * ((log10(f) * log10(f))); - return a0 + a1 * log10(d) + a2 * log10(TxH) + a3 * log10(TxH) * log10(d) - (3.2 * log10(g1)) + g2; -} - -double FSPLpathLoss(float f, float d){ -/* -Free Space Path Loss (ITU-R P.525) model -Frequency: Any -Distance: Any -*/ - //MHz to GHz - f = f / 1000; - double dbloss = (20 * log10(d)) + (20 * log10(f)) + 92.45; - return dbloss; -} -/* -int main(int argc, char* argv[]){ - if(argc<5){ - printf("Need freq,TxH,RxH,dist,terr\n"); - return 0; - } - int dis, ter; - double frq, TxH, RxH; - - sscanf(argv[1],"%lf",&frq); - sscanf(argv[2],"%lf",&TxH); - sscanf(argv[3],"%lf",&RxH); - sscanf(argv[4],"%d",&dis); - sscanf(argv[5],"%d",&ter); - // ALL are freq in MHz and distances in metres - printf("FSPL: %.2f dB\n",FSPLpathLoss(frq,dis)); - printf("HATA (%d): %.2f dB\n",ter,HATApathLoss(frq,TxH,RxH,dis,ter)); - printf("COST-HATA (%d): %.2f dB\n",ter,COST231pathLoss(frq,TxH,RxH,dis,ter)); - printf("SUI (%d): %.2f dB\n",ter,SUIpathLoss(frq,TxH,RxH,dis,ter)); - printf("ECC33 (%d): %.2f dB\n",ter,ECC33pathLoss(frq,TxH,RxH,dis,ter)); - printf("Ericsson (%d): %.2f dB\n",ter,EricssonpathLoss(frq,TxH,RxH,dis,ter)); -} -*/ diff --git a/models/README b/models/README new file mode 100644 index 0000000..a2502d1 --- /dev/null +++ b/models/README @@ -0,0 +1,7 @@ +Whilst every effort has been made to ensure the accuracy of the models, their +accuracy is not guaranteed. + +Finding a reputable paper to source these models from took a while. There was +lots of bad copy-paste out there. A good paper: + +http://www.cl.cam.ac.uk/research/dtg/lce-pub/public/vsa23/VTC05_Empirical.pdf diff --git a/models/cost.cc b/models/cost.cc new file mode 100644 index 0000000..8b8d757 --- /dev/null +++ b/models/cost.cc @@ -0,0 +1,60 @@ +/***************************************************************************** +* COST231-HATA MODEL for Signal Server by Alex Farrant * +* 30 December 2013i * +* 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 * +* version. * +* * +* This program is distributed in the hope that it will useful, but WITHOUT * +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * +* for more details. * +* */ + +#include +#include +#include + +double COST231pathLoss(float f, float TxH, float RxH, float d, int mode) +{ +/* +COST231 extension to HATA model +Frequency 1500 to 2000MHz +TxH = Base station height 30 to 200m +RxH = Mobile station height 1 to 10m +Distance 1-20km +modes 1 = URBAN, 2 = SUBURBAN, 3 = OPEN +http://morse.colorado.edu/~tlen5510/text/classwebch3.html +*/ + if (f < 150 || f > 2000) { + printf + ("Error: COST231 Hata model frequency range 150-2000MHz\n"); + exit(EXIT_FAILURE); + } + + int C = 3; // 3dB for Urban + float lRxH = log10(11.75 * RxH); + float C_H = 3.2 * (lRxH * lRxH) - 4.97; // Large city (conservative) + int c0 = 69.55; + int cf = 26.16; + if (f > 1500) { + c0 = 46.3; + cf = 33.9; + } + if (mode == 2) { + C = 0; // Medium city (average) + C_H = 8.29 * (lRxH * lRxH) - 1.1; + } + if (mode == 3) { + C = -3; // Small city (Optimistic) + C_H = (1.1 * log10(f) - 0.7) * RxH - (1.56 * log10(f)) + 0.8; + } + float logf = log10(f); + double dbloss = + c0 + (cf * logf) - (13.82 * log10(TxH)) - C_H + (44.9 - + 6.55 * + log10(TxH)) * + log10(d) + C; + return dbloss; +} diff --git a/models/cost.hh b/models/cost.hh new file mode 100644 index 0000000..e94f37b --- /dev/null +++ b/models/cost.hh @@ -0,0 +1,6 @@ +#ifndef _COST_HH_ +#define _COST_HH_ + +double COST231pathLoss(float f, float TxH, float RxH, float d, int mode); + +#endif /* _COST_HH_ */ diff --git a/models/ecc33.cc b/models/ecc33.cc new file mode 100644 index 0000000..d4c5f23 --- /dev/null +++ b/models/ecc33.cc @@ -0,0 +1,26 @@ +#include +#include +#include + +double ECC33pathLoss(float f, float TxH, float RxH, float d, int mode) +{ + + if (f < 700 || f > 3500) { + printf("Error: ECC33 model frequency range 700-3500MHz\n"); + exit(EXIT_FAILURE); + } + // MHz to GHz + f = f / 1000; + + double Gr = 0.759 * RxH - 1.862; // Big city with tall buildings (1) + // PL = Afs + Abm - Gb - Gr + double Afs = 92.4 + 20 * log10(d) + 20 * log10(f); + double Abm = + 20.41 + 9.83 * log10(d) + 7.894 * log10(f) + + 9.56 * (log10(f) * log10(f)); + double Gb = log10(TxH / 200) * (13.958 + 5.8 * (log10(d) * log10(d))); + if (mode > 1) { // Medium city (Europe) + Gr = (42.57 + 13.7 * log10(f)) * (log10(RxH) - 0.585); + } + return Afs + Abm - Gb - Gr; +} diff --git a/models/ecc33.hh b/models/ecc33.hh new file mode 100644 index 0000000..5617f4c --- /dev/null +++ b/models/ecc33.hh @@ -0,0 +1,6 @@ +#ifndef _ECC33_HH_ +#define _ECC33_HH_ + +double ECC33pathLoss(float f, float TxH, float RxH, float d, int mode); + +#endif /* _ECC33_HH_ */ diff --git a/models/ericsson.cc b/models/ericsson.cc new file mode 100644 index 0000000..0082958 --- /dev/null +++ b/models/ericsson.cc @@ -0,0 +1,33 @@ +#include +#include +#include + +double EricssonpathLoss(float f, float TxH, float RxH, float d, int mode) +{ + /* + http://research.ijcaonline.org/volume84/number7/pxc3892830.pdf + AKA Ericsson 9999 model + */ + // Default is Urban which bizarrely has lowest loss + double a0 = 36.2, a1 = 30.2, a2 = -12, a3 = 0.1; + + if (f < 150 || f > 3500) { + printf + ("Error: Ericsson9999 model frequency range 150-3500MHz\n"); + exit(EXIT_FAILURE); + } + + if (mode == 2) { // Suburban / Med loss + a0 = 43.2; + a1 = 68.93; + } + if (mode == 1) { // "Rural" but has highest loss according to Ericsson. + a0 = 45.95; + a1 = 100.6; + } + double g1 = (11.75 * RxH) * (11.75 * RxH); + double g2 = (44.49 * log10(f)) - 4.78 * ((log10(f) * log10(f))); + + return a0 + a1 * log10(d) + a2 * log10(TxH) + + a3 * log10(TxH) * log10(d) - (3.2 * log10(g1)) + g2; +} diff --git a/models/ericsson.hh b/models/ericsson.hh new file mode 100644 index 0000000..c43bd3f --- /dev/null +++ b/models/ericsson.hh @@ -0,0 +1,6 @@ +#ifndef _ERICSSON_HH_ +#define _ERICSSON_HH_ + +double EricssonpathLoss(float f, float TxH, float RxH, float d, int mode); + +#endif /* _ERICSSON_HH_ */ diff --git a/fspl.cpp b/models/fspl.cc similarity index 72% rename from fspl.cpp rename to models/fspl.cc index acfd3f5..5f64c7d 100644 --- a/fspl.cpp +++ b/models/fspl.cc @@ -1,24 +1,21 @@ /***************************************************************************** -* ITU-R P.525 Free Space Path Loss model for Signal Server by Alex Farrant * -* 15 January 2014 * +* ITU-R P.525 Free Space Path Loss model for Signal Server by Alex Farrant * +* 15 January 2014 * * 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 * -* version. * -* * +* version. * +* * * This program is distributed in the hope that it will useful, but WITHOUT * * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * -* for more details. * -* */ +* for more details. * +* */ -#include #include -#include -using namespace std; - -double FsplLinkdB(float f, float d){ +double FSPLpathLoss(float f, float d) +{ /* Free Space Path Loss model Frequency: Any @@ -28,6 +25,6 @@ Distance: Any f = f / 1000; double dbloss = (20 * log10(d)) + (20 * log10(f)) + 92.45; - + return dbloss; } diff --git a/models/fspl.hh b/models/fspl.hh new file mode 100644 index 0000000..952eb67 --- /dev/null +++ b/models/fspl.hh @@ -0,0 +1,6 @@ +#ifndef _FSPL_HH_ +#define _FSPL_HH_ + +double FSPLpathLoss(float f, float d); + +#endif /* _FSPL_HH_ */ diff --git a/models/hata.cc b/models/hata.cc new file mode 100644 index 0000000..f936e53 --- /dev/null +++ b/models/hata.cc @@ -0,0 +1,56 @@ +/***************************************************************************** +* HATA MODEL for Signal Server by Alex Farrant * +* 30 December 2013 * +* 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 * +* version. * +* * +* This program is distributed in the hope that it will useful, but WITHOUT * +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * +* for more details. * +* */ + +#include + +double HATApathLoss(float f, float h_B, float h_M, float d, int mode) +{ +/* +HATA URBAN model for cellular planning +Frequency (MHz) 150 to 1500MHz +Base station height 30-200m +Mobile station height 1-10m +Distance 1-20km + +mode 1 = URBAN +mode 2 = SUBURBAN +mode 3 = OPEN +*/ + + float lh_M = log10(11.75 * h_M); + float C_H = 3.2 * lh_M * lh_M - 4.97; + + float logf = log10(f); + + float L_u = + 69.55 + 26.16 * logf - 13.82 * log10(h_B) - C_H + (44.9 - + 6.55 * + log10(h_B)) * + log10(d); + + if (!mode || mode == 1) { + return L_u; //URBAN + } + + if (mode == 2) { //SUBURBAN + float logf_28 = log10(f / 28); + return L_u - 2 * logf_28 * logf_28 - 5.4; + } + + if (mode == 3) { //OPEN + return L_u - 4.78 * logf * logf + 18.33 * logf - 40.94; + } + + return 0; +} diff --git a/models/hata.hh b/models/hata.hh new file mode 100644 index 0000000..9d9adae --- /dev/null +++ b/models/hata.hh @@ -0,0 +1,6 @@ +#ifndef _HATA_HH_ +#define _HATA_HH_ + +double HATApathLoss(float f, float h_B, float h_M, float d, int mode); + +#endif /* _HATA_HH_ */ diff --git a/models/itm.cc b/models/itm.cc new file mode 100644 index 0000000..16afe31 --- /dev/null +++ b/models/itm.cc @@ -0,0 +1,1574 @@ +/*****************************************************************************\ + * * + * The following code was derived from public domain ITM code available * + * at ftp://flattop.its.bldrdoc.gov/itm/ITMDLL.cpp that was released on * + * June 26, 2007. It was modified to remove Microsoft Windows "dll-isms", * + * redundant and unnecessary #includes, redundant and unnecessary { }'s, * + * to initialize uninitialized variables, type cast some variables, * + * re-format the code for easier reading, and to replace pow() function * + * calls with explicit multiplications wherever possible to increase * + * execution speed and improve computational accuracy. * + * * +\*****************************************************************************/ + +// ************************************* +// C++ routines for this program are taken from +// a translation of the FORTRAN code written by +// U.S. Department of Commerce NTIA/ITS +// Institute for Telecommunication Sciences +// ***************** +// Irregular Terrain Model (ITM) (Longley-Rice) +// ************************************* + +#include +#include +#include +#include + +#include "../common.h" + +#define THIRD (1.0/3.0) + +using namespace std; + +struct tcomplex { + double tcreal; + double tcimag; +}; + +struct prop_type { + double aref; + double dist; + double hg[2]; + double wn; + double dh; + double ens; + double gme; + double zgndreal; + double zgndimag; + double he[2]; + double dl[2]; + double the[2]; + int kwx; + int mdp; +}; + +struct propv_type { + double sgc; + int lvar; + int mdvar; + int klim; +}; + +struct propa_type { + double dlsa; + double dx; + double ael; + double ak1; + double ak2; + double aed; + double emd; + double aes; + double ems; + double dls[2]; + double dla; + double tha; +}; + +int mymin(const int &i, const int &j) +{ + if (i < j) + return i; + else + return j; +} + +int mymax(const int &i, const int &j) +{ + if (i > j) + return i; + else + return j; +} + +double mymin(const double &a, const double &b) +{ + if (a < b) + return a; + else + return b; +} + +double mymax(const double &a, const double &b) +{ + if (a > b) + return a; + else + return b; +} + +double FORTRAN_DIM(const double &x, const double &y) +{ + // This performs the FORTRAN DIM function. + // result is x-y if x is greater than y; otherwise result is 0.0 + + if (x > y) + return x - y; + else + return 0.0; +} + +double aknfe(const double &v2) +{ + double a; + + if (v2 < 5.76) + a = 6.02 + 9.11 * sqrt(v2) - 1.27 * v2; + else + a = 12.953 + 4.343 * log(v2); + + return a; +} + +double fht(const double &x, const double &pk) +{ + double w, fhtv; + if (x < 200.0) { + w = -log(pk); + + /* if (pk < 1e-5 || x*pow(w,3.0) > 5495.0 ) */ + + if (pk < 1e-5 || (x * w * w * w) > 5495.0) { + fhtv = -117.0; + + if (x > 1.0) + fhtv = 17.372 * log(x) + fhtv; + } + + else + fhtv = 2.5e-5 * x * x / pk - 8.686 * w - 15.0; + } + + else { + fhtv = 0.05751 * x - 4.343 * log(x); + + if (x < 2000.0) { + w = 0.0134 * x * exp(-0.005 * x); + fhtv = (1.0 - w) * fhtv + w * (17.372 * log(x) - 117.0); + } + } + + return fhtv; +} + +double h0f(double r, double et) +{ + double a[5] = { 25.0, 80.0, 177.0, 395.0, 705.0 }; + double b[5] = { 24.0, 45.0, 68.0, 80.0, 105.0 }; + double q, x; + int it; + double h0fv; + + it = (int)et; + + if (it <= 0) { + it = 1; + q = 0.0; + } + + else if (it >= 5) { + it = 5; + q = 0.0; + } + + else + q = et - it; + + /* x=pow(1.0/r,2.0); */ + x = (1.0 / r); + x *= x; + h0fv = 4.343 * log((a[it - 1] * x + b[it - 1]) * x + 1.0); + + if (q != 0.0) + h0fv = + (1.0 - q) * h0fv + q * 4.343 * log((a[it] * x + b[it]) * x + + 1.0); + + return h0fv; +} + +double ahd(double td) +{ + int i; + double a[3] = { 133.4, 104.6, 71.8 }; + double b[3] = { 0.332e-3, 0.212e-3, 0.157e-3 }; + double c[3] = { -4.343, -1.086, 2.171 }; + + if (td <= 10e3) + i = 0; + + else if (td <= 70e3) + i = 1; + + else + i = 2; + + return a[i] + b[i] * td + c[i] * log(td); +} + +double adiff(double d, prop_type & prop, propa_type & propa) +{ + complex < double >prop_zgnd(prop.zgndreal, prop.zgndimag); + static double wd1, xd1, afo, qk, aht, xht; + double a, q, pk, ds, th, wa, ar, wd, adiffv; + + if (d == 0) { + q = prop.hg[0] * prop.hg[1]; + qk = prop.he[0] * prop.he[1] - q; + + if (prop.mdp < 0.0) + q += 10.0; + + wd1 = sqrt(1.0 + qk / q); + xd1 = propa.dla + propa.tha / prop.gme; + q = (1.0 - 0.8 * exp(-propa.dlsa / 50e3)) * prop.dh; + q *= 0.78 * exp(-pow(q / 16.0, 0.25)); + afo = + mymin(15.0, + 2.171 * log(1.0 + + 4.77e-4 * prop.hg[0] * prop.hg[1] * + prop.wn * q)); + qk = 1.0 / abs(prop_zgnd); + aht = 20.0; + xht = 0.0; + + for (int j = 0; j < 2; ++j) { + /* a=0.5*pow(prop.dl[j],2.0)/prop.he[j]; */ + a = 0.5 * (prop.dl[j] * prop.dl[j]) / prop.he[j]; + wa = pow(a * prop.wn, THIRD); + pk = qk / wa; + q = (1.607 - pk) * 151.0 * wa * prop.dl[j] / a; + xht += q; + aht += fht(q, pk); + } + + adiffv = 0.0; + } + + else { + th = propa.tha + d * prop.gme; + ds = d - propa.dla; + /* q=0.0795775*prop.wn*ds*pow(th,2.0); */ + q = 0.0795775 * prop.wn * ds * th * th; + adiffv = + aknfe(q * prop.dl[0] / (ds + prop.dl[0])) + + aknfe(q * prop.dl[1] / (ds + prop.dl[1])); + a = ds / th; + wa = pow(a * prop.wn, THIRD); + pk = qk / wa; + q = (1.607 - pk) * 151.0 * wa * th + xht; + ar = 0.05751 * q - 4.343 * log(q) - aht; + q = (wd1 + + xd1 / d) * mymin(((1.0 - 0.8 * exp(-d / 50e3)) * prop.dh * + prop.wn), 6283.2); + wd = 25.1 / (25.1 + sqrt(q)); + adiffv = ar * wd + (1.0 - wd) * adiffv + afo; + } + + return adiffv; +} + +double ascat(double d, prop_type & prop, propa_type & propa) +{ + static double ad, rr, etq, h0s; + double h0, r1, r2, z0, ss, et, ett, th, q; + double ascatv, temp; + + if (d == 0.0) { + ad = prop.dl[0] - prop.dl[1]; + rr = prop.he[1] / prop.he[0]; + + if (ad < 0.0) { + ad = -ad; + rr = 1.0 / rr; + } + + etq = (5.67e-6 * prop.ens - 2.32e-3) * prop.ens + 0.031; + h0s = -15.0; + ascatv = 0.0; + } + + else { + if (h0s > 15.0) + h0 = h0s; + else { + th = prop.the[0] + prop.the[1] + d * prop.gme; + r2 = 2.0 * prop.wn * th; + r1 = r2 * prop.he[0]; + r2 *= prop.he[1]; + + if (r1 < 0.2 && r2 < 0.2) + return 1001.0; // <==== early return + + ss = (d - ad) / (d + ad); + q = rr / ss; + ss = mymax(0.1, ss); + q = mymin(mymax(0.1, q), 10.0); + z0 = (d - ad) * (d + ad) * th * 0.25 / d; + /* et=(etq*exp(-pow(mymin(1.7,z0/8.0e3),6.0))+1.0)*z0/1.7556e3; */ + temp = mymin(1.7, z0 / 8.0e3); + temp = temp * temp * temp * temp * temp * temp; + et = (etq * exp(-temp) + 1.0) * z0 / 1.7556e3; + + ett = mymax(et, 1.0); + h0 = (h0f(r1, ett) + h0f(r2, ett)) * 0.5; + h0 += + mymin(h0, + (1.38 - log(ett)) * log(ss) * log(q) * 0.49); + h0 = FORTRAN_DIM(h0, 0.0); + + if (et < 1.0) + h0 = et * h0 + (1.0 - + et) * 4.343 * + log(pow + ((1.0 + 1.4142 / r1) * (1.0 + + 1.4142 / r2), + 2.0) * (r1 + r2) / (r1 + r2 + 2.8284)); + + if (h0 > 15.0 && h0s >= 0.0) + h0 = h0s; + } + + h0s = h0; + th = propa.tha + d * prop.gme; + /* ascatv=ahd(th*d)+4.343*log(47.7*prop.wn*pow(th,4.0))-0.1*(prop.ens-301.0)*exp(-th*d/40e3)+h0; */ + + ascatv = + ahd(th * d) + + 4.343 * log(47.7 * prop.wn * th * th * th * th) - + 0.1 * (prop.ens - 301.0) * exp(-th * d / 40e3) + h0; + + } + + return ascatv; +} + +double qerfi(double q) +{ + double x, t, v; + double c0 = 2.515516698; + double c1 = 0.802853; + double c2 = 0.010328; + double d1 = 1.432788; + double d2 = 0.189269; + double d3 = 0.001308; + + x = 0.5 - q; + t = mymax(0.5 - fabs(x), 0.000001); + t = sqrt(-2.0 * log(t)); + v = t - ((c2 * t + c1) * t + c0) / (((d3 * t + d2) * t + d1) * t + 1.0); + + if (x < 0.0) + v = -v; + + return v; +} + +void qlrps(double fmhz, double zsys, double en0, int ipol, double eps, + double sgm, prop_type & prop) +{ + double gma = 157e-9; + + prop.wn = fmhz / 47.7; + prop.ens = en0; + + if (zsys != 0.0) + prop.ens *= exp(-zsys / 9460.0); + + prop.gme = gma * (1.0 - 0.04665 * exp(prop.ens / 179.3)); + complex < double >zq, prop_zgnd(prop.zgndreal, prop.zgndimag); + zq = complex < double >(eps, 376.62 * sgm / prop.wn); + prop_zgnd = sqrt(zq - 1.0); + + if (ipol != 0.0) + prop_zgnd = prop_zgnd / zq; + + prop.zgndreal = prop_zgnd.real(); + prop.zgndimag = prop_zgnd.imag(); +} + +double abq_alos(complex < double >r) +{ + return r.real() * r.real() + r.imag() * r.imag(); +} + +double alos(double d, prop_type & prop, propa_type & propa) +{ + complex < double >prop_zgnd(prop.zgndreal, prop.zgndimag); + static double wls; + complex < double >r; + double s, sps, q; + double alosv; + + if (d == 0.0) { + wls = + 0.021 / (0.021 + + prop.wn * prop.dh / mymax(10e3, propa.dlsa)); + alosv = 0.0; + } + + else { + q = (1.0 - 0.8 * exp(-d / 50e3)) * prop.dh; + s = 0.78 * q * exp(-pow(q / 16.0, 0.25)); + q = prop.he[0] + prop.he[1]; + sps = q / sqrt(d * d + q * q); + r = (sps - prop_zgnd) / (sps + + prop_zgnd) * exp(-mymin(10.0, + prop.wn * s * + sps)); + q = abq_alos(r); + + if (q < 0.25 || q < sps) + r = r * sqrt(sps / q); + + alosv = propa.emd * d + propa.aed; + q = prop.wn * prop.he[0] * prop.he[1] * 2.0 / d; + + if (q > 1.57) + q = 3.14 - 2.4649 / q; + + alosv = + (-4.343 * + log(abq_alos(complex < double >(cos(q), -sin(q)) + r)) - + alosv) * wls + alosv; + } + + return alosv; +} + +void qlra(int kst[], int klimx, int mdvarx, prop_type & prop, + propv_type & propv) +{ + double q; + + for (int j = 0; j < 2; ++j) { + if (kst[j] <= 0) + prop.he[j] = prop.hg[j]; + else { + q = 4.0; + + if (kst[j] != 1) + q = 9.0; + + if (prop.hg[j] < 5.0) + q *= sin(0.3141593 * prop.hg[j]); + + prop.he[j] = + prop.hg[j] + (1.0 + + q) * exp(-mymin(20.0, + 2.0 * prop.hg[j] / + mymax(1e-3, + prop.dh))); + } + + q = sqrt(2.0 * prop.he[j] / prop.gme); + prop.dl[j] = + q * exp(-0.07 * sqrt(prop.dh / mymax(prop.he[j], 5.0))); + prop.the[j] = + (0.65 * prop.dh * (q / prop.dl[j] - 1.0) - + 2.0 * prop.he[j]) / q; + } + + prop.mdp = 1; + propv.lvar = mymax(propv.lvar, 3); + + if (mdvarx >= 0) { + propv.mdvar = mdvarx; + propv.lvar = mymax(propv.lvar, 4); + } + + if (klimx > 0) { + propv.klim = klimx; + propv.lvar = 5; + } +} + +void lrprop(double d, prop_type & prop, propa_type & propa) // PaulM_lrprop +{ + static bool wlos, wscat; + static double dmin, xae; + complex < double >prop_zgnd(prop.zgndreal, prop.zgndimag); + double a0, a1, a2, a3, a4, a5, a6; + double d0, d1, d2, d3, d4, d5, d6; + bool wq; + double q; + int j; + + if (prop.mdp != 0) { + for (j = 0; j < 2; j++) + propa.dls[j] = sqrt(2.0 * prop.he[j] / prop.gme); + + propa.dlsa = propa.dls[0] + propa.dls[1]; + propa.dla = prop.dl[0] + prop.dl[1]; + propa.tha = + mymax(prop.the[0] + prop.the[1], -propa.dla * prop.gme); + wlos = false; + wscat = false; + + if (prop.wn < 0.838 || prop.wn > 210.0) + prop.kwx = mymax(prop.kwx, 1); + + for (j = 0; j < 2; j++) + if (prop.hg[j] < 1.0 || prop.hg[j] > 1000.0) + prop.kwx = mymax(prop.kwx, 1); + + for (j = 0; j < 2; j++) + if (abs(prop.the[j]) > 200e-3 + || prop.dl[j] < 0.1 * propa.dls[j] + || prop.dl[j] > 3.0 * propa.dls[j]) + prop.kwx = mymax(prop.kwx, 3); + + if (prop.ens < 250.0 || prop.ens > 400.0 || prop.gme < 75e-9 + || prop.gme > 250e-9 + || prop_zgnd.real() <= abs(prop_zgnd.imag()) + || prop.wn < 0.419 || prop.wn > 420.0) + + prop.kwx = 4; + + for (j = 0; j < 2; j++) + + if (prop.hg[j] < 0.5 || prop.hg[j] > 3000.0) { + prop.kwx = 4; + } + + dmin = abs(prop.he[0] - prop.he[1]) / 200e-3; + q = adiff(0.0, prop, propa); + /* xae=pow(prop.wn*pow(prop.gme,2),-THIRD); */ + xae = pow(prop.wn * prop.gme * prop.gme, -THIRD); + d3 = mymax(propa.dlsa, 1.3787 * xae + propa.dla); + d4 = d3 + 2.7574 * xae; + a3 = adiff(d3, prop, propa); + a4 = adiff(d4, prop, propa); + propa.emd = (a4 - a3) / (d4 - d3); + propa.aed = a3 - propa.emd * d3; + } + + if (prop.mdp >= 0) { + prop.mdp = 0; + prop.dist = d; + } + + if (prop.dist > 0.0) { + if (prop.dist > 1000e3) + prop.kwx = mymax(prop.kwx, 1); + + if (prop.dist < dmin) + prop.kwx = mymax(prop.kwx, 3); + + if (prop.dist < 1e3 || prop.dist > 2000e3) + prop.kwx = 4; + } + + if (prop.dist < propa.dlsa) { + if (!wlos) { + q = alos(0.0, prop, propa); + d2 = propa.dlsa; + a2 = propa.aed + d2 * propa.emd; + d0 = 1.908 * prop.wn * prop.he[0] * prop.he[1]; + + if (propa.aed >= 0.0) { + d0 = mymin(d0, 0.5 * propa.dla); + d1 = d0 + 0.25 * (propa.dla - d0); + } + + else + d1 = mymax(-propa.aed / propa.emd, + 0.25 * propa.dla); + + a1 = alos(d1, prop, propa); + wq = false; + + if (d0 < d1) { + a0 = alos(d0, prop, propa); + q = log(d2 / d0); + propa.ak2 = + mymax(0.0, + ((d2 - d0) * (a1 - a0) - + (d1 - d0) * (a2 - a0)) / ((d2 - + d0) * + log(d1 / + d0) - + (d1 - + d0) * q)); + wq = propa.aed >= 0.0 || propa.ak2 > 0.0; + + if (wq) { + propa.ak1 = + (a2 - a0 - propa.ak2 * q) / (d2 - + d0); + + if (propa.ak1 < 0.0) { + propa.ak1 = 0.0; + propa.ak2 = + FORTRAN_DIM(a2, a0) / q; + + if (propa.ak2 == 0.0) + propa.ak1 = propa.emd; + } + } + } + + if (!wq) { + propa.ak1 = FORTRAN_DIM(a2, a1) / (d2 - d1); + propa.ak2 = 0.0; + + if (propa.ak1 == 0.0) + propa.ak1 = propa.emd; + } + + propa.ael = a2 - propa.ak1 * d2 - propa.ak2 * log(d2); + wlos = true; + } + + if (prop.dist > 0.0) + prop.aref = + propa.ael + propa.ak1 * prop.dist + + propa.ak2 * log(prop.dist); + } + + if (prop.dist <= 0.0 || prop.dist >= propa.dlsa) { + if (!wscat) { + q = ascat(0.0, prop, propa); + d5 = propa.dla + 200e3; + d6 = d5 + 200e3; + a6 = ascat(d6, prop, propa); + a5 = ascat(d5, prop, propa); + + if (a5 < 1000.0) { + propa.ems = (a6 - a5) / 200e3; + propa.dx = + mymax(propa.dlsa, + mymax(propa.dla + + 0.3 * xae * log(47.7 * prop.wn), + (a5 - propa.aed - + propa.ems * d5) / (propa.emd - + propa. + ems))); + + propa.aes = + (propa.emd - propa.ems) * propa.dx + + propa.aed; + } + + else { + propa.ems = propa.emd; + propa.aes = propa.aed; + propa.dx = 10.e6; + } + + wscat = true; + } + + if (prop.dist > propa.dx) + prop.aref = propa.aes + propa.ems * prop.dist; + else + prop.aref = propa.aed + propa.emd * prop.dist; + } + + prop.aref = mymax(prop.aref, 0.0); +} + +double curve(double const &c1, double const &c2, double const &x1, + double const &x2, double const &x3, double const &de) +{ + /* return (c1+c2/(1.0+pow((de-x2)/x3,2.0)))*pow(de/x1,2.0)/(1.0+pow(de/x1,2.0)); */ + + double temp1, temp2; + + temp1 = (de - x2) / x3; + temp2 = de / x1; + + temp1 *= temp1; + temp2 *= temp2; + + return (c1 + c2 / (1.0 + temp1)) * temp2 / (1.0 + temp2); +} + +double avar(double zzt, double zzl, double zzc, prop_type & prop, + propv_type & propv) +{ + static int kdv; + static double dexa, de, vmd, vs0, sgl, sgtm, sgtp, sgtd, tgtd, gm, gp, + cv1, cv2, yv1, yv2, yv3, csm1, csm2, ysm1, ysm2, ysm3, csp1, csp2, + ysp1, ysp2, ysp3, csd1, zd, cfm1, cfm2, cfm3, cfp1, cfp2, cfp3; + + double bv1[7] = { -9.67, -0.62, 1.26, -9.21, -0.62, -0.39, 3.15 }; + double bv2[7] = { 12.7, 9.19, 15.5, 9.05, 9.19, 2.86, 857.9 }; + double xv1[7] = + { 144.9e3, 228.9e3, 262.6e3, 84.1e3, 228.9e3, 141.7e3, 2222.e3 }; + double xv2[7] = + { 190.3e3, 205.2e3, 185.2e3, 101.1e3, 205.2e3, 315.9e3, 164.8e3 }; + double xv3[7] = + { 133.8e3, 143.6e3, 99.8e3, 98.6e3, 143.6e3, 167.4e3, 116.3e3 }; + double bsm1[7] = { 2.13, 2.66, 6.11, 1.98, 2.68, 6.86, 8.51 }; + double bsm2[7] = { 159.5, 7.67, 6.65, 13.11, 7.16, 10.38, 169.8 }; + double xsm1[7] = + { 762.2e3, 100.4e3, 138.2e3, 139.1e3, 93.7e3, 187.8e3, 609.8e3 }; + double xsm2[7] = + { 123.6e3, 172.5e3, 242.2e3, 132.7e3, 186.8e3, 169.6e3, 119.9e3 }; + double xsm3[7] = + { 94.5e3, 136.4e3, 178.6e3, 193.5e3, 133.5e3, 108.9e3, 106.6e3 }; + double bsp1[7] = { 2.11, 6.87, 10.08, 3.68, 4.75, 8.58, 8.43 }; + double bsp2[7] = { 102.3, 15.53, 9.60, 159.3, 8.12, 13.97, 8.19 }; + double xsp1[7] = + { 636.9e3, 138.7e3, 165.3e3, 464.4e3, 93.2e3, 216.0e3, 136.2e3 }; + double xsp2[7] = + { 134.8e3, 143.7e3, 225.7e3, 93.1e3, 135.9e3, 152.0e3, 188.5e3 }; + double xsp3[7] = + { 95.6e3, 98.6e3, 129.7e3, 94.2e3, 113.4e3, 122.7e3, 122.9e3 }; + double bsd1[7] = { 1.224, 0.801, 1.380, 1.000, 1.224, 1.518, 1.518 }; + double bzd1[7] = { 1.282, 2.161, 1.282, 20., 1.282, 1.282, 1.282 }; + double bfm1[7] = { 1.0, 1.0, 1.0, 1.0, 0.92, 1.0, 1.0 }; + double bfm2[7] = { 0.0, 0.0, 0.0, 0.0, 0.25, 0.0, 0.0 }; + double bfm3[7] = { 0.0, 0.0, 0.0, 0.0, 1.77, 0.0, 0.0 }; + double bfp1[7] = { 1.0, 0.93, 1.0, 0.93, 0.93, 1.0, 1.0 }; + double bfp2[7] = { 0.0, 0.31, 0.0, 0.19, 0.31, 0.0, 0.0 }; + double bfp3[7] = { 0.0, 2.00, 0.0, 1.79, 2.00, 0.0, 0.0 }; + static bool ws, w1; + double rt = 7.8, rl = 24.0, avarv, q, vs, zt, zl, zc; + double sgt, yr; + int temp_klim = propv.klim - 1; + + if (propv.lvar > 0) { + switch (propv.lvar) { + default: + if (propv.klim <= 0 || propv.klim > 7) { + propv.klim = 5; + temp_klim = 4; + prop.kwx = mymax(prop.kwx, 2); + } + + cv1 = bv1[temp_klim]; + cv2 = bv2[temp_klim]; + yv1 = xv1[temp_klim]; + yv2 = xv2[temp_klim]; + yv3 = xv3[temp_klim]; + csm1 = bsm1[temp_klim]; + csm2 = bsm2[temp_klim]; + ysm1 = xsm1[temp_klim]; + ysm2 = xsm2[temp_klim]; + ysm3 = xsm3[temp_klim]; + csp1 = bsp1[temp_klim]; + csp2 = bsp2[temp_klim]; + ysp1 = xsp1[temp_klim]; + ysp2 = xsp2[temp_klim]; + ysp3 = xsp3[temp_klim]; + csd1 = bsd1[temp_klim]; + zd = bzd1[temp_klim]; + cfm1 = bfm1[temp_klim]; + cfm2 = bfm2[temp_klim]; + cfm3 = bfm3[temp_klim]; + cfp1 = bfp1[temp_klim]; + cfp2 = bfp2[temp_klim]; + cfp3 = bfp3[temp_klim]; + + case 4: + kdv = propv.mdvar; + ws = kdv >= 20; + + if (ws) + kdv -= 20; + + w1 = kdv >= 10; + + if (w1) + kdv -= 10; + + if (kdv < 0 || kdv > 3) { + kdv = 0; + prop.kwx = mymax(prop.kwx, 2); + } + + case 3: + q = log(0.133 * prop.wn); + + /* gm=cfm1+cfm2/(pow(cfm3*q,2.0)+1.0); */ + /* gp=cfp1+cfp2/(pow(cfp3*q,2.0)+1.0); */ + + gm = cfm1 + cfm2 / ((cfm3 * q * cfm3 * q) + 1.0); + gp = cfp1 + cfp2 / ((cfp3 * q * cfp3 * q) + 1.0); + + case 2: + dexa = + sqrt(18e6 * prop.he[0]) + sqrt(18e6 * prop.he[1]) + + pow((575.7e12 / prop.wn), THIRD); + + case 1: + if (prop.dist < dexa) + de = 130e3 * prop.dist / dexa; + else + de = 130e3 + prop.dist - dexa; + } + + vmd = curve(cv1, cv2, yv1, yv2, yv3, de); + sgtm = curve(csm1, csm2, ysm1, ysm2, ysm3, de) * gm; + sgtp = curve(csp1, csp2, ysp1, ysp2, ysp3, de) * gp; + sgtd = sgtp * csd1; + tgtd = (sgtp - sgtd) * zd; + + if (w1) + sgl = 0.0; + else { + q = (1.0 - + 0.8 * exp(-prop.dist / 50e3)) * prop.dh * prop.wn; + sgl = 10.0 * q / (q + 13.0); + } + + if (ws) + vs0 = 0.0; + else { + /* vs0=pow(5.0+3.0*exp(-de/100e3),2.0); */ + vs0 = (5.0 + 3.0 * exp(-de / 100e3)); + vs0 *= vs0; + } + + propv.lvar = 0; + } + + zt = zzt; + zl = zzl; + zc = zzc; + + switch (kdv) { + case 0: + zt = zc; + zl = zc; + break; + + case 1: + zl = zc; + break; + + case 2: + zl = zt; + } + + if (fabs(zt) > 3.1 || fabs(zl) > 3.1 || fabs(zc) > 3.1) + prop.kwx = mymax(prop.kwx, 1); + + if (zt < 0.0) + sgt = sgtm; + + else if (zt <= zd) + sgt = sgtp; + + else + sgt = sgtd + tgtd / zt; + + /* vs=vs0+pow(sgt*zt,2.0)/(rt+zc*zc)+pow(sgl*zl,2.0)/(rl+zc*zc); */ + vs = vs0 + (sgt * zt * sgt * zt) / (rt + zc * zc) + + (sgl * zl * sgl * zl) / (rl + zc * zc); + + if (kdv == 0) { + yr = 0.0; + propv.sgc = sqrt(sgt * sgt + sgl * sgl + vs); + } + + else if (kdv == 1) { + yr = sgt * zt; + propv.sgc = sqrt(sgl * sgl + vs); + } + + else if (kdv == 2) { + yr = sqrt(sgt * sgt + sgl * sgl) * zt; + propv.sgc = sqrt(vs); + } + + else { + yr = sgt * zt + sgl * zl; + propv.sgc = sqrt(vs); + } + + avarv = prop.aref - vmd - yr - propv.sgc * zc; + + if (avarv < 0.0) + avarv = avarv * (29.0 - avarv) / (29.0 - 10.0 * avarv); + + return avarv; +} + +void hzns(double pfl[], prop_type & prop) +{ + bool wq; + int np; + double xi, za, zb, qc, q, sb, sa; + + np = (int)pfl[0]; + xi = pfl[1]; + za = pfl[2] + prop.hg[0]; + zb = pfl[np + 2] + prop.hg[1]; + qc = 0.5 * prop.gme; + q = qc * prop.dist; + prop.the[1] = (zb - za) / prop.dist; + prop.the[0] = prop.the[1] - q; + prop.the[1] = -prop.the[1] - q; + prop.dl[0] = prop.dist; + prop.dl[1] = prop.dist; + + if (np >= 2) { + sa = 0.0; + sb = prop.dist; + wq = true; + + for (int i = 1; i < np; i++) { + sa += xi; + sb -= xi; + q = pfl[i + 2] - (qc * sa + prop.the[0]) * sa - za; + + if (q > 0.0) { + prop.the[0] += q / sa; + prop.dl[0] = sa; + wq = false; + } + + if (!wq) { + q = pfl[i + 2] - (qc * sb + prop.the[1]) * sb - + zb; + + if (q > 0.0) { + prop.the[1] += q / sb; + prop.dl[1] = sb; + } + } + } + } +} + +void z1sq1(double z[], const double &x1, const double &x2, double &z0, + double &zn) +{ + double xn, xa, xb, x, a, b; + int n, ja, jb; + + xn = z[0]; + xa = int (FORTRAN_DIM(x1 / z[1], 0.0)); + xb = xn - int (FORTRAN_DIM(xn, x2 / z[1])); + + if (xb <= xa) { + xa = FORTRAN_DIM(xa, 1.0); + xb = xn - FORTRAN_DIM(xn, xb + 1.0); + } + + ja = (int)xa; + jb = (int)xb; + n = jb - ja; + xa = xb - xa; + x = -0.5 * xa; + xb += x; + a = 0.5 * (z[ja + 2] + z[jb + 2]); + b = 0.5 * (z[ja + 2] - z[jb + 2]) * x; + + for (int i = 2; i <= n; ++i) { + ++ja; + x += 1.0; + a += z[ja + 2]; + b += z[ja + 2] * x; + } + + a /= xa; + b = b * 12.0 / ((xa * xa + 2.0) * xa); + z0 = a - b * xb; + zn = a + b * (xn - xb); +} + +double qtile(const int &nn, double a[], const int &ir) +{ + double q = 0.0, r; /* Initializations by KD2BD */ + int m, n, i, j, j1 = 0, i0 = 0, k; /* Initializations by KD2BD */ + bool done = false; + bool goto10 = true; + + m = 0; + n = nn; + k = mymin(mymax(0, ir), n); + + while (!done) { + if (goto10) { + q = a[k]; + i0 = m; + j1 = n; + } + + i = i0; + + while (i <= n && a[i] >= q) + i++; + + if (i > n) + i = n; + j = j1; + + while (j >= m && a[j] <= q) + j--; + + if (j < m) + j = m; + + if (i < j) { + r = a[i]; + a[i] = a[j]; + a[j] = r; + i0 = i + 1; + j1 = j - 1; + goto10 = false; + } + + else if (i < k) { + a[k] = a[i]; + a[i] = q; + m = i + 1; + goto10 = true; + } + + else if (j > k) { + a[k] = a[j]; + a[j] = q; + n = j - 1; + goto10 = true; + } + + else + done = true; + } + + return q; +} + +double qerf(const double &z) +{ + double b1 = 0.319381530, b2 = -0.356563782, b3 = 1.781477937; + double b4 = -1.821255987, b5 = 1.330274429; + double rp = 4.317008, rrt2pi = 0.398942280; + double t, x, qerfv; + + x = z; + t = fabs(x); + + if (t >= 10.0) + qerfv = 0.0; + else { + t = rp / (t + rp); + qerfv = + exp(-0.5 * x * x) * rrt2pi * + ((((b5 * t + b4) * t + b3) * t + b2) * t + b1) * t; + } + + if (x < 0.0) + qerfv = 1.0 - qerfv; + + return qerfv; +} + +double d1thx(double pfl[], const double &x1, const double &x2) +{ + int np, ka, kb, n, k, j; + double d1thxv, sn, xa, xb; + double *s; + + np = (int)pfl[0]; + xa = x1 / pfl[1]; + xb = x2 / pfl[1]; + d1thxv = 0.0; + + if (xb - xa < 2.0) // exit out + return d1thxv; + + ka = (int)(0.1 * (xb - xa + 8.0)); + ka = mymin(mymax(4, ka), 25); + n = 10 * ka - 5; + kb = n - ka + 1; + sn = n - 1; + assert((s = new double[n + 2])!=0); + s[0] = sn; + s[1] = 1.0; + xb = (xb - xa) / sn; + k = (int)(xa + 1.0); + xa -= (double)k; + + for (j = 0; j < n; j++) { + while (xa > 0.0 && k < np) { + xa -= 1.0; + ++k; + } + + s[j + 2] = pfl[k + 2] + (pfl[k + 2] - pfl[k + 1]) * xa; + xa = xa + xb; + } + + z1sq1(s, 0.0, sn, xa, xb); + xb = (xb - xa) / sn; + + for (j = 0; j < n; j++) { + s[j + 2] -= xa; + xa = xa + xb; + } + + d1thxv = qtile(n - 1, s + 2, ka - 1) - qtile(n - 1, s + 2, kb - 1); + d1thxv /= 1.0 - 0.8 * exp(-(x2 - x1) / 50.0e3); + delete[]s; + + return d1thxv; +} + +void qlrpfl(double pfl[], int klimx, int mdvarx, prop_type & prop, + propa_type & propa, propv_type & propv) +{ + int np, j; + double xl[2], q, za, zb; + + prop.dist = pfl[0] * pfl[1]; + np = (int)pfl[0]; + hzns(pfl, prop); + + for (j = 0; j < 2; j++) + xl[j] = mymin(15.0 * prop.hg[j], 0.1 * prop.dl[j]); + + xl[1] = prop.dist - xl[1]; + prop.dh = d1thx(pfl, xl[0], xl[1]); + + if (prop.dl[0] + prop.dl[1] > 1.5 * prop.dist) { + z1sq1(pfl, xl[0], xl[1], za, zb); + prop.he[0] = prop.hg[0] + FORTRAN_DIM(pfl[2], za); + prop.he[1] = prop.hg[1] + FORTRAN_DIM(pfl[np + 2], zb); + + for (j = 0; j < 2; j++) + prop.dl[j] = + sqrt(2.0 * prop.he[j] / prop.gme) * exp(-0.07 * + sqrt(prop. + dh / + mymax + (prop. + he[j], + 5.0))); + + q = prop.dl[0] + prop.dl[1]; + + if (q <= prop.dist) { + /* q=pow(prop.dist/q,2.0); */ + q = ((prop.dist / q) * (prop.dist / q)); + + for (j = 0; j < 2; j++) { + prop.he[j] *= q; + prop.dl[j] = + sqrt(2.0 * prop.he[j] / prop.gme) * + exp(-0.07 * + sqrt(prop.dh / mymax(prop.he[j], 5.0))); + } + } + + for (j = 0; j < 2; j++) { + q = sqrt(2.0 * prop.he[j] / prop.gme); + prop.the[j] = + (0.65 * prop.dh * (q / prop.dl[j] - 1.0) - + 2.0 * prop.he[j]) / q; + } + } + + else { + z1sq1(pfl, xl[0], 0.9 * prop.dl[0], za, q); + z1sq1(pfl, prop.dist - 0.9 * prop.dl[1], xl[1], q, zb); + prop.he[0] = prop.hg[0] + FORTRAN_DIM(pfl[2], za); + prop.he[1] = prop.hg[1] + FORTRAN_DIM(pfl[np + 2], zb); + } + + prop.mdp = -1; + propv.lvar = mymax(propv.lvar, 3); + + if (mdvarx >= 0) { + propv.mdvar = mdvarx; + propv.lvar = mymax(propv.lvar, 4); + } + + if (klimx > 0) { + propv.klim = klimx; + propv.lvar = 5; + } + + lrprop(0.0, prop, propa); +} + +double deg2rad(double d) +{ + return d * 3.1415926535897 / 180.0; +} + +//******************************************************** +//* Point-To-Point Mode Calculations * +//******************************************************** + +void point_to_point(double tht_m, double rht_m, double eps_dielect, + double sgm_conductivity, double eno_ns_surfref, + double frq_mhz, int radio_climate, int pol, double conf, + double rel, double &dbloss, char *strmode, int &errnum) +{ + // pol: 0-Horizontal, 1-Vertical + // radio_climate: 1-Equatorial, 2-Continental Subtropical, 3-Maritime Tropical, + // 4-Desert, 5-Continental Temperate, 6-Maritime Temperate, Over Land, + // 7-Maritime Temperate, Over Sea + // conf, rel: .01 to .99 + // elev[]: [num points - 1], [delta dist(meters)], [height(meters) point 1], ..., [height(meters) point n] + // errnum: 0- No Error. + // 1- Warning: Some parameters are nearly out of range. + // Results should be used with caution. + // 2- Note: Default parameters have been substituted for impossible ones. + // 3- Warning: A combination of parameters is out of range. + // Results are probably invalid. + // Other- Warning: Some parameters are out of range. + // Results are probably invalid. + + prop_type prop; + propv_type propv; + propa_type propa; + + double zsys = 0; + double zc, zr; + double eno, enso, q; + long ja, jb, i, np; + //double dkm, xkm; + double fs; + + prop.hg[0] = tht_m; + prop.hg[1] = rht_m; + propv.klim = radio_climate; + prop.kwx = 0; + propv.lvar = 5; + prop.mdp = -1; + zc = qerfi(conf); + zr = qerfi(rel); + np = (long)elev[0]; //number of points + //dkm=(elev[1]*elev[0])/1000.0; // total distance in km. elev[1]=90(m) (default) + //xkm=elev[1]/1000.0; // distance between points in km + eno = eno_ns_surfref; + enso = 0.0; + q = enso; + + if (q <= 0.0) { + /* ja = 3.0 + 0.1 * elev[0]; */ + ja = (long)(3.0 + 0.1 * elev[0]); + + jb = np - ja + 6; + + for (i = ja - 1; i < jb; ++i) + zsys += elev[i]; + + zsys /= (jb - ja + 1); + q = eno; + } + + propv.mdvar = 12; + qlrps(frq_mhz, zsys, q, pol, eps_dielect, sgm_conductivity, prop); + qlrpfl(elev, propv.klim, propv.mdvar, prop, propa, propv); + fs = 32.45 + 20.0 * log10(frq_mhz) + 20.0 * log10(prop.dist / 1000.0); + q = prop.dist - propa.dla; + + if (int (q) < 0.0) + strcpy(strmode, "Line-Of-Sight Mode"); + + else { + if (int (q) == 0.0) + strcpy(strmode, "Single Horizon"); + + else if (int (q) > 0.0) + strcpy(strmode, "Double Horizon"); + + if (prop.dist <= propa.dlsa || prop.dist <= propa.dx) + strcat(strmode, ", Diffraction Dominant"); + + else if (prop.dist > propa.dx) + strcat(strmode, ", Troposcatter Dominant"); + } + + dbloss = avar(zr, 0.0, zc, prop, propv) + fs; + errnum = prop.kwx; +} + +void point_to_pointMDH(double tht_m, double rht_m, double eps_dielect, + double sgm_conductivity, double eno_ns_surfref, + double frq_mhz, int radio_climate, int pol, + double timepct, double locpct, double confpct, + double &dbloss, int &propmode, double &deltaH, + int &errnum) +{ + // pol: 0-Horizontal, 1-Vertical + // radio_climate: 1-Equatorial, 2-Continental Subtropical, 3-Maritime Tropical, + // 4-Desert, 5-Continental Temperate, 6-Maritime Temperate, Over Land, + // 7-Maritime Temperate, Over Sea + // timepct, locpct, confpct: .01 to .99 + // elev[]: [num points - 1], [delta dist(meters)], [height(meters) point 1], ..., [height(meters) point n] + // propmode: Value Mode + // -1 mode is undefined + // 0 Line of Sight + // 5 Single Horizon, Diffraction + // 6 Single Horizon, Troposcatter + // 9 Double Horizon, Diffraction + // 10 Double Horizon, Troposcatter + // errnum: 0- No Error. + // 1- Warning: Some parameters are nearly out of range. + // Results should be used with caution. + // 2- Note: Default parameters have been substituted for impossible ones. + // 3- Warning: A combination of parameters is out of range. + // Results are probably invalid. + // Other- Warning: Some parameters are out of range. + // Results are probably invalid. + + prop_type prop; + propv_type propv; + propa_type propa; + double zsys = 0; + double ztime, zloc, zconf; + double eno, enso, q; + long ja, jb, i, np; + //double dkm, xkm; + double fs; + + propmode = -1; // mode is undefined + prop.hg[0] = tht_m; + prop.hg[1] = rht_m; + propv.klim = radio_climate; + prop.kwx = 0; + propv.lvar = 5; + prop.mdp = -1; + ztime = qerfi(timepct); + zloc = qerfi(locpct); + zconf = qerfi(confpct); + + np = (long)elev[0]; + //dkm=(elev[1]*elev[0])/1000.0; + //xkm=elev[1]/1000.0; + eno = eno_ns_surfref; + enso = 0.0; + q = enso; + + if (q <= 0.0) { + /* ja = 3.0 + 0.1 * elev[0]; */ + ja = (long)(3.0 + 0.1 * elev[0]); + jb = np - ja + 6; + + for (i = ja - 1; i < jb; ++i) + zsys += elev[i]; + + zsys /= (jb - ja + 1); + q = eno; + } + + propv.mdvar = 12; + qlrps(frq_mhz, zsys, q, pol, eps_dielect, sgm_conductivity, prop); + qlrpfl(elev, propv.klim, propv.mdvar, prop, propa, propv); + fs = 32.45 + 20.0 * log10(frq_mhz) + 20.0 * log10(prop.dist / 1000.0); + deltaH = prop.dh; + q = prop.dist - propa.dla; + + if (int (q) < 0.0) + propmode = 0; // Line-Of-Sight Mode + else { + if (int (q) == 0.0) + propmode = 4; // Single Horizon + + else if (int (q) > 0.0) + propmode = 8; // Double Horizon + + if (prop.dist <= propa.dlsa || prop.dist <= propa.dx) + propmode += 1; // Diffraction Dominant + + else if (prop.dist > propa.dx) + propmode += 2; // Troposcatter Dominant + } + + dbloss = avar(ztime, zloc, zconf, prop, propv) + fs; //avar(time,location,confidence) + errnum = prop.kwx; +} + +void point_to_pointDH(double tht_m, double rht_m, double eps_dielect, + double sgm_conductivity, double eno_ns_surfref, + double frq_mhz, int radio_climate, int pol, double conf, + double rel, double &dbloss, double &deltaH, int &errnum) +{ + // pol: 0-Horizontal, 1-Vertical + // radio_climate: 1-Equatorial, 2-Continental Subtropical, 3-Maritime Tropical, + // 4-Desert, 5-Continental Temperate, 6-Maritime Temperate, Over Land, + // 7-Maritime Temperate, Over Sea + // conf, rel: .01 to .99 + // elev[]: [num points - 1], [delta dist(meters)], [height(meters) point 1], ..., [height(meters) point n] + // errnum: 0- No Error. + // 1- Warning: Some parameters are nearly out of range. + // Results should be used with caution. + // 2- Note: Default parameters have been substituted for impossible ones. + // 3- Warning: A combination of parameters is out of range. + // Results are probably invalid. + // Other- Warning: Some parameters are out of range. + // Results are probably invalid. + + char strmode[100]; + prop_type prop; + propv_type propv; + propa_type propa; + double zsys = 0; + double zc, zr; + double eno, enso, q; + long ja, jb, i, np; + //double dkm, xkm; + double fs; + + prop.hg[0] = tht_m; + prop.hg[1] = rht_m; + propv.klim = radio_climate; + prop.kwx = 0; + propv.lvar = 5; + prop.mdp = -1; + zc = qerfi(conf); + zr = qerfi(rel); + np = (long)elev[0]; + //dkm=(elev[1]*elev[0])/1000.0; + //xkm=elev[1]/1000.0; + eno = eno_ns_surfref; + enso = 0.0; + q = enso; + + if (q <= 0.0) { + /* ja = 3.0 + 0.1 * elev[0]; */ + ja = (long)(3.0 + 0.1 * elev[0]); + + jb = np - ja + 6; + + for (i = ja - 1; i < jb; ++i) + zsys += elev[i]; + + zsys /= (jb - ja + 1); + q = eno; + } + + propv.mdvar = 12; + qlrps(frq_mhz, zsys, q, pol, eps_dielect, sgm_conductivity, prop); + qlrpfl(elev, propv.klim, propv.mdvar, prop, propa, propv); + fs = 32.45 + 20.0 * log10(frq_mhz) + 20.0 * log10(prop.dist / 1000.0); + deltaH = prop.dh; + q = prop.dist - propa.dla; + + if (int (q) < 0.0) + strcpy(strmode, "Line-Of-Sight Mode"); + else { + if (int (q) == 0.0) + strcpy(strmode, "Single Horizon"); + + else if (int (q) > 0.0) + strcpy(strmode, "Double Horizon"); + + if (prop.dist <= propa.dlsa || prop.dist <= propa.dx) + strcat(strmode, ", Diffraction Dominant"); + + else if (prop.dist > propa.dx) + strcat(strmode, ", Troposcatter Dominant"); + } + + dbloss = avar(zr, 0.0, zc, prop, propv) + fs; //avar(time,location,confidence) + errnum = prop.kwx; +} + +//******************************************************** +//* Area Mode Calculations * +//******************************************************** + +void area(long ModVar, double deltaH, double tht_m, double rht_m, + double dist_km, int TSiteCriteria, int RSiteCriteria, + double eps_dielect, double sgm_conductivity, double eno_ns_surfref, + double frq_mhz, int radio_climate, int pol, double pctTime, + double pctLoc, double pctConf, double &dbloss, char *strmode, + int &errnum) +{ + // pol: 0-Horizontal, 1-Vertical + // TSiteCriteria, RSiteCriteria: + // 0 - random, 1 - careful, 2 - very careful + // radio_climate: 1-Equatorial, 2-Continental Subtropical, 3-Maritime Tropical, + // 4-Desert, 5-Continental Temperate, 6-Maritime Temperate, Over Land, + // 7-Maritime Temperate, Over Sea + // ModVar: 0 - Single: pctConf is "Time/Situation/Location", pctTime, pctLoc not used + // 1 - Individual: pctTime is "Situation/Location", pctConf is "Confidence", pctLoc not used + // 2 - Mobile: pctTime is "Time/Locations (Reliability)", pctConf is "Confidence", pctLoc not used + // 3 - Broadcast: pctTime is "Time", pctLoc is "Location", pctConf is "Confidence" + // pctTime, pctLoc, pctConf: .01 to .99 + // errnum: 0- No Error. + // 1- Warning: Some parameters are nearly out of range. + // Results should be used with caution. + // 2- Note: Default parameters have been substituted for impossible ones. + // 3- Warning: A combination of parameters is out of range. + // Results are probably invalid. + // Other- Warning: Some parameters are out of range. + // Results are probably invalid. + // NOTE: strmode is not used at this time. + + prop_type prop; + propv_type propv; + propa_type propa; + double zt, zl, zc, xlb; + double fs; + long ivar; + double eps, eno, sgm; + long ipol; + int kst[2]; + + kst[0] = (int)TSiteCriteria; + kst[1] = (int)RSiteCriteria; + zt = qerfi(pctTime); + zl = qerfi(pctLoc); + zc = qerfi(pctConf); + eps = eps_dielect; + sgm = sgm_conductivity; + eno = eno_ns_surfref; + prop.dh = deltaH; + prop.hg[0] = tht_m; + prop.hg[1] = rht_m; + /* propv.klim = (__int32) radio_climate; */ + propv.klim = (long)radio_climate; + prop.ens = eno; + prop.kwx = 0; + ivar = (long)ModVar; + ipol = (long)pol; + qlrps(frq_mhz, 0.0, eno, ipol, eps, sgm, prop); + qlra(kst, propv.klim, ivar, prop, propv); + + if (propv.lvar < 1) + propv.lvar = 1; + + lrprop(dist_km * 1000.0, prop, propa); + fs = 32.45 + 20.0 * log10(frq_mhz) + 20.0 * log10(prop.dist / 1000.0); + xlb = fs + avar(zt, zl, zc, prop, propv); + dbloss = xlb; + + if (prop.kwx == 0) + errnum = 0; + else + errnum = prop.kwx; +} + +double ITMAreadBLoss(long ModVar, double deltaH, double tht_m, double rht_m, + double dist_km, int TSiteCriteria, int RSiteCriteria, + double eps_dielect, double sgm_conductivity, + double eno_ns_surfref, double frq_mhz, int radio_climate, + int pol, double pctTime, double pctLoc, double pctConf) +{ + char strmode[200]; + int errnum; + double dbloss; + + area(ModVar, deltaH, tht_m, rht_m, dist_km, TSiteCriteria, + RSiteCriteria, eps_dielect, sgm_conductivity, eno_ns_surfref, + frq_mhz, radio_climate, pol, pctTime, pctLoc, pctConf, dbloss, + strmode, errnum); + + return dbloss; +} + +double ITMVersion() +{ + return 7.0; +} diff --git a/models/itwom3.0.cc b/models/itwom3.0.cc new file mode 100644 index 0000000..49c7584 --- /dev/null +++ b/models/itwom3.0.cc @@ -0,0 +1,2963 @@ + +/****************************************************************************** +* ITWOM version 3.0a, January 20, 2011 File: itwom3.0a.cpp * +* Provenance: Further test version of itwom2.0m re adj to Hrzn rangefactors * +* 1. This file is based on a thorough debugging, completion, and update of * +* the ITM, based on an original, public domain version of this file obtained * +* from: ftp://flattop.its.bldrdoc.gov/itm/ITMDLL.cpp prior to May, 2007. C++ * +* routines for this program are taken from a translation of the FORTRAN code * +* written by U.S. Department of Commerce NTIA/ITS Institute for * +* Telecommunication Sciences Irregular Terrain Model (ITM) (Longley-Rice). * +* 2. The Linux version of this file incorporates improvements suggested by a * +* study of changes made to file itm.cpp by J. D. McDonald to remove Microsoft * +* Windows dll-isms and to debug an ambguity in overloaded calls. * +* 3. The Linux version of this file also incorporates improvements suggested * +* by a study of further modifications made to itm.cpp by John A. Magliacane * +* to remove unused variables, unneeded #includes, and to replace pow() * +* statements with explicit multiplications to improve execution speed and * +* accuracy. * +* 4. On August 19, 2007 this file was modified by Sid Shumate to include * +* changes and updates included in version 7.0 of ITMDLL.cpp, which was * +* released by the NTIA/ITS on June 26, 2007. With correction set SS1 and * +* SS2: itm71.cpp. * +* 5. On Feb. 5, 2008 this file became v.1.0 of the ITWOM with the addition, * +* by Sid Shumate, of multiple corrections, the replacement of subroutines * +* lrprop and alos with lrprop2 and alos2, and the addition of subroutine * +* saalos to incorporate Radiative Transfer Engine (RTE) computations in the * +* line of sight range. * +* Update 8 Jun 2010 to modify alos to match 2010 series of IEEE-BTS * +* newsletter articles * +* Update June 12, 2010 to z version to change test outputs * +* Offshoot start date June 23, 2010 to start itwom2.0 dual version for FCC. * +* Update to 2.0b July 25 to correct if statement errors in adiff2 re two peak * +* calculations starting at line 525 * +* Development to 2.0c 8 Aug 2010 after modifying saalos and adiff for full * +* addition of saalos treatment to post obstruction calculations and * +* debugging. * +* Modified to make 1st obs loss=5.8 only, no clutter loss considered * +* * +* Commented out unused variables and calculations to eliminate gcc warnings * +* (-Wunused-but-set-variable) -- John A. Magliacane -- July 25, 2013 * +******************************************************************************/ + +#include +#include +#include +#include + +#include "../common.h" + +#define THIRD (1.0/3.0) + +using namespace std; + +struct tcomplex { + double tcreal; + double tcimag; +}; + +struct prop_type { + double aref; + double dist; + double hg[2]; + double rch[2]; + double wn; + double dh; + double dhd; + double ens; + double encc; + double cch; + double cd; + double gme; + double zgndreal; + double zgndimag; + double he[2]; + double dl[2]; + double the[2]; + double tiw; + double ght; + double ghr; + double rph; + double hht; + double hhr; + double tgh; + double tsgh; + double thera; + double thenr; + int rpl; + int kwx; + int mdp; + int ptx; + int los; +}; + +struct propv_type { + double sgc; + int lvar; + int mdvar; + int klim; +}; + +struct propa_type { + double dlsa; + double dx; + double ael; + double ak1; + double ak2; + double aed; + double emd; + double aes; + double ems; + double dls[2]; + double dla; + double tha; +}; + +int mymin(const int &i, const int &j) +{ + if (i < j) + return i; + else + return j; +} + +int mymax(const int &i, const int &j) +{ + if (i > j) + return i; + else + return j; +} + +double mymin(const double &a, const double &b) +{ + if (a < b) + return a; + else + return b; +} + +double mymax(const double &a, const double &b) +{ + if (a > b) + return a; + else + return b; +} + +double FORTRAN_DIM(const double &x, const double &y) +{ + /* This performs the FORTRAN DIM function. Result is x-y + if x is greater than y; otherwise result is 0.0 */ + + if (x > y) + return x - y; + else + return 0.0; +} + +double aknfe(const double &v2) +{ + double a; + + if (v2 < 5.76) + a = 6.02 + 9.11 * sqrt(v2) - 1.27 * v2; + else + a = 12.953 + 10 * log10(v2); + return a; +} + +double fht(const double &x, const double &pk) +{ + double w, fhtv; + + if (x < 200.0) { + w = -log(pk); + + if (pk < 1.0e-5 || x * w * w * w > 5495.0) { + fhtv = -117.0; + + if (x > 1.0) + fhtv = 40.0 * log10(x) + fhtv; + } else + fhtv = 2.5e-5 * x * x / pk - 8.686 * w - 15.0; + } + + else { + fhtv = 0.05751 * x - 10.0 * log10(x); + + if (x < 2000.0) { + w = 0.0134 * x * exp(-0.005 * x); + fhtv = (1.0 - w) * fhtv + w * (40.0 * log10(x) - 117.0); + } + } + return fhtv; +} + +double h0f(double r, double et) +{ + double a[5] = { 25.0, 80.0, 177.0, 395.0, 705.0 }; + double b[5] = { 24.0, 45.0, 68.0, 80.0, 105.0 }; + double q, x; + double h0fv, temp; + int it; + + it = (int)et; + + if (it <= 0) { + it = 1; + q = 0.0; + } + + else if (it >= 5) { + it = 5; + q = 0.0; + } + + else + q = et - it; + + /* x=pow(1.0/r,2.0); */ + + temp = 1.0 / r; + x = temp * temp; + + h0fv = 4.343 * log((a[it - 1] * x + b[it - 1]) * x + 1.0); + + if (q != 0.0) + h0fv = + (1.0 - q) * h0fv + q * 4.343 * log((a[it] * x + b[it]) * x + + 1.0); + + return h0fv; +} + +double ahd(double td) +{ + int i; + double a[3] = { 133.4, 104.6, 71.8 }; + double b[3] = { 0.332e-3, 0.212e-3, 0.157e-3 }; + double c[3] = { -4.343, -1.086, 2.171 }; + + if (td <= 10e3) + i = 0; + + else if (td <= 70e3) + i = 1; + + else + i = 2; + + return a[i] + b[i] * td + c[i] * log(td); +} + +double abq_alos(complex < double >r) +{ + return r.real() * r.real() + r.imag() * r.imag(); +} + +double saalos(double d, prop_type & prop, propa_type & propa) +{ + double ensa, encca, q, dp, dx, tde, hc, ucrpc, ctip, tip, tic, stic, + ctic, sta; + double ttc, cttc, crpc, ssnps, d1a, rsp, tsp, arte, zi, pd, pdk, hone, + tvsr; + double saalosv = 0.0; + + q = 0.0; + + if (d == 0.0) { + tsp = 1.0; + rsp = 0.0; + d1a = 50.0; + saalosv = 0.0; + } else if (prop.hg[1] > prop.cch) { + saalosv = 0.0; + } else { + pd = d; + pdk = pd / 1000.0; + tsp = 1.0; + rsp = 0.0; + d1a = pd; + /* at first, hone is transmitter antenna height + relative to receive site ground level. */ + hone = prop.tgh + prop.tsgh - (prop.rch[1] - prop.hg[1]); + + if (prop.tgh > prop.cch) { /* for TX ant above all clutter height */ + ensa = 1 + prop.ens * 0.000001; + encca = 1 + prop.encc * 0.000001; + dp = pd; + + for (int j = 0; j < 5; ++j) { + tde = dp / 6378137.0; + hc = (prop.cch + 6378137.0) * (1 - cos(tde)); + dx = (prop.cch + 6378137.0) * sin(tde); + ucrpc = + sqrt((hone - prop.cch + hc) * (hone - + prop.cch + + hc) + + (dx * dx)); + ctip = (hone - prop.cch + hc) / ucrpc; + tip = acos(ctip); + tic = tip + tde; + tic = mymax(0.0, tic); + stic = sin(tic); + sta = (ensa / encca) * stic; + ttc = asin(sta); + cttc = sqrt(1 - (sin(ttc)) * (sin(ttc))); + crpc = (prop.cch - prop.hg[1]) / cttc; + if (crpc >= dp) { + crpc = dp - 1 / dp; + } + + ssnps = (3.1415926535897 / 2) - tic; + d1a = (crpc * sin(ttc)) / (1 - 1 / 6378137.0); + dp = pd - d1a; + + } + + ctic = cos(tic); + + /* if the ucrpc path touches the canopy before reaching the + end of the ucrpc, the entry point moves toward the + transmitter, extending the crpc and d1a. Estimating the d1a: */ + + if (ssnps <= 0.0) { + d1a = mymin(0.1 * pd, 600.0); + crpc = d1a; + /* hone must be redefined as being barely above + the canopy height with respect to the receiver + canopy height, which despite the earth curvature + is at or above the transmitter antenna height. */ + hone = prop.cch + 1; + rsp = .997; + tsp = 1 - rsp; + } else { + + if (prop.ptx >= 1) { /* polarity ptx is vertical or circular */ + q = ((ensa * cttc - + encca * ctic) / (ensa * cttc + + encca * ctic)); + rsp = q * q; + tsp = 1 - rsp; + + if (prop.ptx == 2) { /* polarity is circular - new */ + q = ((ensa * ctic - + encca * cttc) / (ensa * + ctic + + encca * + cttc)); + rsp = + ((ensa * cttc - + encca * ctic) / (ensa * + cttc + + encca * + ctic)); + rsp = (q * q + rsp * rsp) / 2; + tsp = 1 - rsp; + } + } else { /* ptx is 0, horizontal, or undefined */ + + q = ((ensa * ctic - + encca * cttc) / (ensa * ctic + + encca * cttc)); + rsp = q * q; + tsp = 1 - rsp; + } + } + /* tvsr is defined as tx ant height above receiver ant height */ + tvsr = mymax(0.0, prop.tgh + prop.tsgh - prop.rch[1]); + + if (d1a < 50.0) { + arte = 0.0195 * crpc - 20 * log10(tsp); + } + + else { + if (d1a < 225.0) { + + if (tvsr > 1000.0) { + q = d1a * (0.03 * + exp(-0.14 * pdk)); + } else { + q = d1a * (0.07 * + exp(-0.17 * pdk)); + } + + arte = + q + (0.7 * pdk - + mymax(0.01, + log10(prop.wn * 47.7) - + 2)) * (prop.hg[1] / + hone); + } + + else { + q = 0.00055 * (pdk) + + log10(pdk) * (0.041 - + 0.0017 * sqrt(hone) + + 0.019); + + arte = + d1a * q - + (18 * log10(rsp)) / + (exp(hone / 37.5)); + + zi = 1.5 * sqrt(hone - prop.cch); + + if (pdk > zi) { + q = (pdk - + zi) * 10.2 * + ((sqrt + (mymax + (0.01, + log10(prop.wn * 47.7) - + 2.0))) / (100 - zi)); + } else { + q = ((zi - + pdk) / zi) * (-20.0 * + mymax(0.01, + log10 + (prop. + wn * + 47.7) + - + 2.0)) + / sqrt(hone); + } + arte = arte + q; + + } + } + } else { /* for TX at or below clutter height */ + + q = (prop.cch - prop.tgh) * (2.06943 - + 1.56184 * exp(1 / + prop.cch - + prop.tgh)); + q = q + (17.98 - + 0.84224 * (prop.cch - + prop.tgh)) * exp(-0.00000061 * pd); + arte = q + 1.34795 * 20 * log10(pd + 1.0); + arte = + arte - + (mymax(0.01, log10(prop.wn * 47.7) - 2)) * + (prop.hg[1] / prop.tgh); + } + saalosv = arte; + } + return saalosv; +} + +double adiff(double d, prop_type & prop, propa_type & propa) +{ + complex < double >prop_zgnd(prop.zgndreal, prop.zgndimag); + static double wd1, xd1, afo, qk, aht, xht; + double a, q, pk, ds, th, wa, ar, wd, adiffv; + + if (d == 0) { + q = prop.hg[0] * prop.hg[1]; + qk = prop.he[0] * prop.he[1] - q; + + if (prop.mdp < 0.0) + q += 10.0; + + wd1 = sqrt(1.0 + qk / q); + xd1 = propa.dla + propa.tha / prop.gme; + q = (1.0 - 0.8 * exp(-propa.dlsa / 50e3)) * prop.dh; + q *= 0.78 * exp(-pow(q / 16.0, 0.25)); + afo = + mymin(15.0, + 2.171 * log(1.0 + + 4.77e-4 * prop.hg[0] * prop.hg[1] * + prop.wn * q)); + qk = 1.0 / abs(prop_zgnd); + aht = 20.0; + xht = 0.0; + + for (int j = 0; j < 2; ++j) { + /* a=0.5*pow(prop.dl[j],2.0)/prop.he[j]; */ + a = 0.5 * (prop.dl[j] * prop.dl[j]) / prop.he[j]; + wa = pow(a * prop.wn, THIRD); + pk = qk / wa; + q = (1.607 - pk) * 151.0 * wa * prop.dl[j] / a; + xht += q; + aht += fht(q, pk); + } + + adiffv = 0.0; + } + + else { + th = propa.tha + d * prop.gme; + ds = d - propa.dla; + /* q=0.0795775*prop.wn*ds*pow(th,2.0); */ + q = 0.0795775 * prop.wn * ds * th * th; + adiffv = + aknfe(q * prop.dl[0] / (ds + prop.dl[0])) + + aknfe(q * prop.dl[1] / (ds + prop.dl[1])); + a = ds / th; + wa = pow(a * prop.wn, THIRD); + pk = qk / wa; + q = (1.607 - pk) * 151.0 * wa * th + xht; + ar = 0.05751 * q - 4.343 * log(q) - aht; + q = (wd1 + + xd1 / d) * mymin(((1.0 - 0.8 * exp(-d / 50e3)) * prop.dh * + prop.wn), 6283.2); + wd = 25.1 / (25.1 + sqrt(q)); + adiffv = ar * wd + (1.0 - wd) * adiffv + afo; + } + + return adiffv; +} + +double adiff2(double d, prop_type & prop, propa_type & propa) +{ + complex < double >prop_zgnd(prop.zgndreal, prop.zgndimag); + static double wd1, xd1, qk, aht, xht, toh, toho, roh, roho, dto, dto1, + dtro, dro, dro2, drto, dtr, dhh1, dhh2, /* dhec, */ dtof, dto1f, + drof, dro2f; + double a, q, pk, rd, ds, dsl, /* dfdh, */ th, wa, /* ar, wd, sf1, */ + sf2, /* ec, */ vv, kedr = 0.0, arp = 0.0, sdr = 0.0, pd = 0.0, srp = + 0.0, kem = 0.0, csd = 0.0, sdl = 0.0, adiffv2 = 0.0, closs = 0.0; + + /* sf1=1.0; *//* average empirical hilltop foliage scatter factor for 1 obstruction */ + sf2 = 1.0; /* average empirical hilltop foliage scatter factor for 2 obstructions */ + + /* dfdh=prop.dh; */ + /* ec=0.5*prop.gme; */ + + /* adiff2 must first be run with d==0.0 to set up coefficients */ + if (d == 0) { + q = prop.hg[0] * prop.hg[1]; + qk = prop.he[0] * prop.he[1] - q; + /* dhec=2.73; */ + + if (prop.mdp < 0.0) + q += 10.0; + + /* coefficients for a standard four radii, rounded earth computation are prepared */ + wd1 = sqrt(1.0 + qk / q); + xd1 = propa.dla + propa.tha / prop.gme; + q = (1.0 - 0.8 * exp(-propa.dlsa / 50e3)) * prop.dh; + q *= 0.78 * exp(-pow(q / 16.0, 0.25)); + qk = 1.0 / abs(prop_zgnd); + aht = 20.0; + xht = 0.0; + a = 0.5 * (prop.dl[0] * prop.dl[0]) / prop.he[0]; + wa = pow(a * prop.wn, THIRD); + pk = qk / wa; + q = (1.607 - pk) * 151.0 * wa * prop.dl[0] / a; + xht = q; + aht += fht(q, pk); + + if ((int (prop.dl[1]) == 0.0)||(prop.the[1] > 0.2)) { + xht += xht; + aht += (aht - 20.0); + } + + else { + a = 0.5 * (prop.dl[1] * prop.dl[1]) / prop.he[1]; + wa = pow(a * prop.wn, THIRD); + pk = qk / wa; + q = (1.607 - pk) * 151.0 * wa * prop.dl[1] / a; + xht += q; + aht += fht(q, pk); + } + adiffv2 = 0.0; + } + + else { + th = propa.tha + d * prop.gme; + + dsl = mymax(d - propa.dla, 0.0); + ds = d - propa.dla; + a = ds / th; + wa = pow(a * prop.wn, THIRD); + pk = qk / wa; + toh = + prop.hht - (prop.rch[0] - + prop.dl[0] * ((prop.rch[1] - prop.rch[0]) / + prop.dist)); + roh = + prop.hhr - (prop.rch[0] - + (prop.dist - + prop.dl[1]) * ((prop.rch[1] - + prop.rch[0]) / prop.dist)); + toho = + prop.hht - (prop.rch[0] - + (prop.dl[0] + + dsl) * ((prop.hhr - prop.rch[0]) / (prop.dist - + prop. + dl[1]))); + roho = + prop.hhr - (prop.hht - + dsl * ((prop.rch[1] - prop.hht) / dsl)); + dto = sqrt(prop.dl[0] * prop.dl[0] + toh * toh); + dto += prop.gme * prop.dl[0]; + dto1 = sqrt(prop.dl[0] * prop.dl[0] + toho * toho); + dto1 += prop.gme * prop.dl[0]; + dtro = + sqrt((prop.dl[0] + dsl) * (prop.dl[0] + dsl) + + prop.hhr * prop.hhr); + dtro += prop.gme * (prop.dl[0] + dsl); + drto = + sqrt((prop.dl[1] + dsl) * (prop.dl[1] + dsl) + + prop.hht * prop.hht); + drto += prop.gme * (prop.dl[1] + dsl); + dro = sqrt(prop.dl[1] * prop.dl[1] + roh * roh); + dro += prop.gme * (prop.dl[1]); + dro2 = sqrt(prop.dl[1] * prop.dl[1] + roho * roho); + dro2 += prop.gme * (prop.dl[1]); + dtr = + sqrt(prop.dist * prop.dist + + (prop.rch[0] - prop.rch[1]) * (prop.rch[0] - + prop.rch[1])); + dtr += prop.gme * prop.dist; + dhh1 = + sqrt((prop.dist - propa.dla) * (prop.dist - propa.dla) + + toho * toho); + dhh1 += prop.gme * (prop.dist - propa.dla); + dhh2 = + sqrt((prop.dist - propa.dla) * (prop.dist - propa.dla) + + roho * roho); + dhh2 += prop.gme * (prop.dist - propa.dla); + + /* for 1 obst tree base path */ + dtof = + sqrt(prop.dl[0] * prop.dl[0] + + (toh - prop.cch) * (toh - prop.cch)); + dtof += prop.gme * prop.dl[0]; + dto1f = + sqrt(prop.dl[0] * prop.dl[0] + + (toho - prop.cch) * (toho - prop.cch)); + dto1f += prop.gme * prop.dl[0]; + drof = + sqrt(prop.dl[1] * prop.dl[1] + + (roh - prop.cch) * (roh - prop.cch)); + drof += prop.gme * (prop.dl[1]); + dro2f = + sqrt(prop.dl[1] * prop.dl[1] + + (roho - prop.cch) * (roho - prop.cch)); + dro2f += prop.gme * (prop.dl[1]); + + /* saalos coefficients preset for post-obstacle receive path */ + prop.tgh = prop.cch + 1.0; + prop.tsgh = prop.hhr; + rd = prop.dl[1]; + + /* two obstacle diffraction calculation */ + if (int (ds) > 0) { /* there are 2 obstacles */ + if (int (prop.dl[1]) > 0.0) { /* receive site past 2nd peak */ + /* rounding attenuation */ + q = (1.607 - pk) * 151.0 * wa * th + xht; + /* ar=0.05751*q-10*log10(q)-aht; */ + + /* knife edge vs round weighting */ + q = (1.0 - 0.8 * exp(-d / 50e3)) * prop.dh; + q = (wd1 + xd1 / d) * mymin((q * prop.wn), + 6283.2); + /* wd=25.1/(25.1+sqrt(q)); */ + + q = 0.6365 * prop.wn; + + if (prop.the[1] < 0.2) { /* receive grazing angle below 0.2 rad */ + /* knife edge attenuation for two obstructions */ + + if (prop.hht < 3400) { /* if below tree line, foliage top loss */ + vv = q * abs(dto1 + dhh1 - + dtro); + adiffv2 = + -18.0 + sf2 * aknfe(vv); + } else { + vv = q * abs(dto1 + dhh1 - + dtro); + adiffv2 = aknfe(vv); + } + + if (prop.hhr < 3400) { + vv = q * abs(dro2 + dhh2 - + drto); + adiffv2 += + (-18.0 + sf2 * aknfe(vv)); + } else { + vv = q * abs(dro2 + dhh2 - + drto); + adiffv2 += aknfe(vv); + } + /* finally, add clutter loss */ + closs = saalos(rd, prop, propa); + adiffv2 += mymin(22.0, closs); + + } else { /* rcvr site too close to 2nd obs */ + + /* knife edge attenuation for 1st obs */ + + if (prop.hht < 3400) { + vv = q * abs(dto1 + dhh1 - + dtro); + adiffv2 = + -18.0 + sf2 * aknfe(vv); + } else { + vv = q * abs(dto1 + dhh1 - + dtro); + adiffv2 = aknfe(vv); + } + + /* weighted calc. of knife vs rounded edge + adiffv2=ar*wd+(1.0-wd)*adiffv2; */ + + /* clutter path loss past 2nd peak */ + if (prop.the[1] < 1.22) { + rd = prop.dl[1]; + + if (prop.the[1] > 0.6) { /* through foliage downhill */ + prop.tgh = prop.cch; + } else { /* close to foliage, rcvr in foliage downslope */ + + vv = 0.6365 * prop.wn * + abs(dro2 + dhh2 - + drto); + } + adiffv2 += aknfe(vv); + closs = saalos(rd, prop, propa); + adiffv2 += mymin(closs, 22.0); + } else { /* rcvr very close to bare cliff or skyscraper */ + + adiffv2 = 5.8 + 25.0; + } + } + } else { /* receive site is atop a 2nd peak */ + + vv = 0.6365 * prop.wn * abs(dto + dro - dtr); + adiffv2 = 5.8 + aknfe(vv); + } + } else { /* for single obstacle */ + + if (int (prop.dl[1]) > 0.0) { /* receive site past 1st peak */ + + if (prop.the[1] < 0.2) { /* receive grazing angle less than .2 radians */ + vv = 0.6365 * prop.wn * abs(dto + dro - + dtr); + + if (prop.hht < 3400) { + sdl = 18.0; + sdl = pow(10, (-sdl / 20)); + /* ke phase difference with respect to direct t-r line */ + kedr = + 0.159155 * prop.wn * + abs(dto + dro - dtr); + arp = abs(kedr - (int (kedr))); + kem = aknfe(vv); + kem = pow(10, (-kem / 20)); + /* scatter path phase with respect to direct t-r line */ + sdr = + 0.5 + + 0.159155 * prop.wn * + abs(dtof + drof - dtr); + srp = abs(sdr - (int (sdr))); + /* difference between scatter and ke phase in radians */ + pd = 6.283185307 * abs(srp - + arp); + /* report pd prior to restriction + keep pd between 0 and pi radians and adjust for 3&4 quadrant */ + if (pd >= 3.141592654) { + pd = 6.283185307 - pd; + csd = + abq_alos(complex < + double + >(sdl, + 0) + + complex < + double + >(kem * + -cos(pd), + kem * + -sin + (pd))); + } else { + csd = + abq_alos(complex < + double + >(sdl, + 0) + + complex < + double + >(kem * + cos(pd), + kem * + sin + (pd))); + } + /*csd=mymax(csd,0.0009); limits maximum loss value to 30.45 db */ + adiffv2 = + -3.71 - 10 * log10(csd); + } else { + adiffv2 = aknfe(vv); + } + /* finally, add clutter loss */ + closs = saalos(rd, prop, propa); + adiffv2 += mymin(closs, 22.0); + } else { /* receive grazing angle too high */ + + if (prop.the[1] < 1.22) { + rd = prop.dl[1]; + + if (prop.the[1] > 0.6) { /* through foliage downhill */ + prop.tgh = prop.cch; + } else { /* downhill slope just above foliage */ + + vv = 0.6365 * prop.wn * + abs(dto + dro - + dtr); + adiffv2 = aknfe(vv); + } + closs = saalos(rd, prop, propa); + adiffv2 += mymin(22.0, closs); + } else { /* receiver very close to bare cliff or skyscraper */ + + adiffv2 = 5.8 + 25.0; + } + } + } else { /* if occurs, receive site atop first peak */ + + adiffv2 = 5.8; + } + } + } + return adiffv2; +} + +double ascat(double d, prop_type & prop, propa_type & propa) +{ + static double ad, rr, etq, h0s; + double h0, r1, r2, z0, ss, et, ett, th, q; + double ascatv, temp; + + if (d == 0.0) { + ad = prop.dl[0] - prop.dl[1]; + rr = prop.he[1] / prop.rch[0]; + + if (ad < 0.0) { + ad = -ad; + rr = 1.0 / rr; + } + + etq = (5.67e-6 * prop.ens - 2.32e-3) * prop.ens + 0.031; + h0s = -15.0; + ascatv = 0.0; + } + + else { + if (h0s > 15.0) + h0 = h0s; + else { + th = prop.the[0] + prop.the[1] + d * prop.gme; + r2 = 2.0 * prop.wn * th; + r1 = r2 * prop.he[0]; + r2 *= prop.he[1]; + + if (r1 < 0.2 && r2 < 0.2) + return 1001.0; // <==== early return + + ss = (d - ad) / (d + ad); + q = rr / ss; + ss = mymax(0.1, ss); + q = mymin(mymax(0.1, q), 10.0); + z0 = (d - ad) * (d + ad) * th * 0.25 / d; + /* et=(etq*exp(-pow(mymin(1.7,z0/8.0e3),6.0))+1.0)*z0/1.7556e3; */ + + temp = mymin(1.7, z0 / 8.0e3); + temp = temp * temp * temp * temp * temp * temp; + et = (etq * exp(-temp) + 1.0) * z0 / 1.7556e3; + + ett = mymax(et, 1.0); + h0 = (h0f(r1, ett) + h0f(r2, ett)) * 0.5; + h0 += + mymin(h0, + (1.38 - log(ett)) * log(ss) * log(q) * 0.49); + h0 = FORTRAN_DIM(h0, 0.0); + + if (et < 1.0) { + /* h0=et*h0+(1.0-et)*4.343*log(pow((1.0+1.4142/r1)*(1.0+1.4142/r2),2.0)*(r1+r2)/(r1+r2+2.8284)); */ + + temp = + ((1.0 + 1.4142 / r1) * (1.0 + 1.4142 / r2)); + h0 = et * h0 + (1.0 - + et) * 4.343 * log((temp * + temp) * (r1 + + r2) + / (r1 + r2 + + 2.8284)); + } + + if (h0 > 15.0 && h0s >= 0.0) + h0 = h0s; + } + + h0s = h0; + th = propa.tha + d * prop.gme; + /* ascatv=ahd(th*d)+4.343*log(47.7*prop.wn*pow(th,4.0))-0.1*(prop.ens-301.0)*exp(-th*d/40e3)+h0; */ + ascatv = + ahd(th * d) + + 4.343 * log(47.7 * prop.wn * (th * th * th * th)) - + 0.1 * (prop.ens - 301.0) * exp(-th * d / 40e3) + h0; + } + + return ascatv; +} + +double qerfi(double q) +{ + double x, t, v; + double c0 = 2.515516698; + double c1 = 0.802853; + double c2 = 0.010328; + double d1 = 1.432788; + double d2 = 0.189269; + double d3 = 0.001308; + + x = 0.5 - q; + t = mymax(0.5 - fabs(x), 0.000001); + t = sqrt(-2.0 * log(t)); + v = t - ((c2 * t + c1) * t + c0) / (((d3 * t + d2) * t + d1) * t + 1.0); + + if (x < 0.0) + v = -v; + + return v; +} + +void qlrps(double fmhz, double zsys, double en0, int ipol, double eps, + double sgm, prop_type & prop) +{ + double gma = 157e-9; + + prop.wn = fmhz / 47.7; + prop.ens = en0; + + if (zsys != 0.0) + prop.ens *= exp(-zsys / 9460.0); + + prop.gme = gma * (1.0 - 0.04665 * exp(prop.ens / 179.3)); + complex < double >zq, prop_zgnd(prop.zgndreal, prop.zgndimag); + zq = complex < double >(eps, 376.62 * sgm / prop.wn); + prop_zgnd = sqrt(zq - 1.0); + + if (ipol != 0.0) + prop_zgnd = prop_zgnd / zq; + + prop.zgndreal = prop_zgnd.real(); + prop.zgndimag = prop_zgnd.imag(); + +} + +double alos(double d, prop_type & prop, propa_type & propa) +{ + complex < double >prop_zgnd(prop.zgndreal, prop.zgndimag); + static double wls; + complex < double >r; + double s, sps, q; + double alosv; + + if (d == 0.0) { + wls = + 0.021 / (0.021 + + prop.wn * prop.dh / mymax(10e3, propa.dlsa)); + alosv = 0.0; + } + + else { + q = (1.0 - 0.8 * exp(-d / 50e3)) * prop.dh; + s = 0.78 * q * exp(-pow(q / 16.0, 0.25)); + q = prop.he[0] + prop.he[1]; + sps = q / sqrt(d * d + q * q); + r = (sps - prop_zgnd) / (sps + + prop_zgnd) * exp(-mymin(10.0, + prop.wn * s * + sps)); + q = abq_alos(r); + + if (q < 0.25 || q < sps) + r = r * sqrt(sps / q); + + alosv = propa.emd * d + propa.aed; + q = prop.wn * prop.he[0] * prop.he[1] * 2.0 / d; + + if (q > 1.57) + q = 3.14 - 2.4649 / q; + + alosv = + (-4.343 * + log(abq_alos(complex < double >(cos(q), -sin(q)) + r)) - + alosv) * wls + alosv; + + } + return alosv; +} + +double alos2(double d, prop_type & prop, propa_type & propa) +{ + complex < double >prop_zgnd(prop.zgndreal, prop.zgndimag); + complex < double >r; + double cd, cr, dr, hr, hrg, ht, htg, hrp, re, s, sps, q, pd, drh; + /* int rp; */ + double alosv; + + cd = 0.0; + cr = 0.0; + htg = prop.hg[0]; + hrg = prop.hg[1]; + ht = prop.ght; + hr = prop.ghr; + /* rp=prop.rpl; */ + hrp = prop.rph; + pd = prop.dist; + + if (d == 0.0) { + alosv = 0.0; + } + + else { + q = prop.he[0] + prop.he[1]; + sps = q / sqrt(pd * pd + q * q); + q = (1.0 - 0.8 * exp(-pd / 50e3)) * prop.dh; + + if (prop.mdp < 0) { + dr = pd / (1 + hrg / htg); + + if (dr < (0.5 * pd)) { + drh = + 6378137.0 - sqrt(-(0.5 * pd) * (0.5 * pd) + + 6378137.0 * 6378137.0 + + (0.5 * pd - + dr) * (0.5 * pd - dr)); + } else { + drh = + 6378137.0 - sqrt(-(0.5 * pd) * (0.5 * pd) + + 6378137.0 * 6378137.0 + + (dr - 0.5 * pd) * (dr - + 0.5 * + pd)); + } + + if ((sps < 0.05) && (prop.cch > hrg) && (prop.dist < prop.dl[0])) { /* if far from transmitter and receiver below canopy */ + cd = mymax(0.01, + pd * (prop.cch - hrg) / (htg - hrg)); + cr = mymax(0.01, + pd - dr + dr * (prop.cch - + drh) / htg); + q = ((1.0 - + 0.8 * exp(-pd / 50e3)) * prop.dh * + (mymin(-20 * log10(cd / cr), 1.0))); + } + } + + s = 0.78 * q * exp(-pow(q / 16.0, 0.25)); + q = exp(-mymin(10.0, prop.wn * s * sps)); + r = q * (sps - prop_zgnd) / (sps + prop_zgnd); + q = abq_alos(r); + q = mymin(q, 1.0); + + if (q < 0.25 || q < sps) { + r = r * sqrt(sps / q); + } + q = prop.wn * prop.he[0] * prop.he[1] / (pd * 3.1415926535897); + + if (prop.mdp < 0) { + q = prop.wn * ((ht - hrp) * (hr - hrp)) / (pd * + 3.1415926535897); + } + q -= floor(q); + + if (q < 0.5) { + q *= 3.1415926535897; + } + + else { + q = (1 - q) * 3.1415926535897; + } + /* no longer valid complex conjugate removed + by removing minus sign from in front of sin function */ + re = abq_alos(complex < double >(cos(q), sin(q)) + r); + alosv = -10 * log10(re); + prop.tgh = prop.hg[0]; /*tx above gnd hgt set to antenna height AGL */ + prop.tsgh = prop.rch[0] - prop.hg[0]; /* tsgh set to tx site gl AMSL */ + + if ((prop.hg[1] < prop.cch) && (prop.thera < 0.785) + && (prop.thenr < 0.785)) { + if (sps < 0.05) { + alosv = alosv + saalos(pd, prop, propa); + } else { + alosv = saalos(pd, prop, propa); + } + } + } + alosv = mymin(22.0, alosv); + return alosv; +} + +void qlra(int kst[], int klimx, int mdvarx, prop_type & prop, + propv_type & propv) +{ + double q; + + for (int j = 0; j < 2; ++j) { + if (kst[j] <= 0) + prop.he[j] = prop.hg[j]; + else { + q = 4.0; + + if (kst[j] != 1) + q = 9.0; + + if (prop.hg[j] < 5.0) + q *= sin(0.3141593 * prop.hg[j]); + + prop.he[j] = + prop.hg[j] + (1.0 + + q) * exp(-mymin(20.0, + 2.0 * prop.hg[j] / + mymax(1e-3, + prop.dh))); + } + + q = sqrt(2.0 * prop.he[j] / prop.gme); + prop.dl[j] = + q * exp(-0.07 * sqrt(prop.dh / mymax(prop.he[j], 5.0))); + prop.the[j] = + (0.65 * prop.dh * (q / prop.dl[j] - 1.0) - + 2.0 * prop.he[j]) / q; + } + + prop.mdp = 1; + propv.lvar = mymax(propv.lvar, 3); + + if (mdvarx >= 0) { + propv.mdvar = mdvarx; + propv.lvar = mymax(propv.lvar, 4); + } + + if (klimx > 0) { + propv.klim = klimx; + propv.lvar = 5; + } +} + +void lrprop(double d, prop_type & prop, propa_type & propa) +{ + /* PaulM_lrprop used for ITM */ + static bool wlos, wscat; + static double dmin, xae; + complex < double >prop_zgnd(prop.zgndreal, prop.zgndimag); + double a0, a1, a2, a3, a4, a5, a6; + double d0, d1, d2, d3, d4, d5, d6; + bool wq; + double q; + int j; + + if (prop.mdp != 0) { + for (j = 0; j < 2; j++) + propa.dls[j] = sqrt(2.0 * prop.he[j] / prop.gme); + + propa.dlsa = propa.dls[0] + propa.dls[1]; + propa.dla = prop.dl[0] + prop.dl[1]; + propa.tha = + mymax(prop.the[0] + prop.the[1], -propa.dla * prop.gme); + wlos = false; + wscat = false; + + if (prop.wn < 0.838 || prop.wn > 210.0) + prop.kwx = mymax(prop.kwx, 1); + + for (j = 0; j < 2; j++) + if (prop.hg[j] < 1.0 || prop.hg[j] > 1000.0) + prop.kwx = mymax(prop.kwx, 1); + + for (j = 0; j < 2; j++) + if (abs(prop.the[j]) > 200e-3 + || prop.dl[j] < 0.1 * propa.dls[j] + || prop.dl[j] > 3.0 * propa.dls[j]) + prop.kwx = mymax(prop.kwx, 3); + + if (prop.ens < 250.0 || prop.ens > 400.0 || prop.gme < 75e-9 + || prop.gme > 250e-9 + || prop_zgnd.real() <= abs(prop_zgnd.imag()) + || prop.wn < 0.419 || prop.wn > 420.0) + prop.kwx = 4; + + for (j = 0; j < 2; j++) + if (prop.hg[j] < 0.5 || prop.hg[j] > 3000.0) + prop.kwx = 4; + + dmin = abs(prop.he[0] - prop.he[1]) / 200e-3; + q = adiff(0.0, prop, propa); + /* xae=pow(prop.wn*pow(prop.gme,2.),-THIRD); -- JDM made argument 2 a double */ + xae = pow(prop.wn * (prop.gme * prop.gme), -THIRD); /* No 2nd pow() */ + d3 = mymax(propa.dlsa, 1.3787 * xae + propa.dla); + d4 = d3 + 2.7574 * xae; + a3 = adiff(d3, prop, propa); + a4 = adiff(d4, prop, propa); + propa.emd = (a4 - a3) / (d4 - d3); + propa.aed = a3 - propa.emd * d3; + } + + if (prop.mdp >= 0) { + prop.mdp = 0; + prop.dist = d; + } + + if (prop.dist > 0.0) { + if (prop.dist > 1000e3) + prop.kwx = mymax(prop.kwx, 1); + + if (prop.dist < dmin) + prop.kwx = mymax(prop.kwx, 3); + + if (prop.dist < 1e3 || prop.dist > 2000e3) + prop.kwx = 4; + } + + if (prop.dist < propa.dlsa) { + if (!wlos) { + q = alos(0.0, prop, propa); + d2 = propa.dlsa; + a2 = propa.aed + d2 * propa.emd; + d0 = 1.908 * prop.wn * prop.he[0] * prop.he[1]; + + if (propa.aed >= 0.0) { + d0 = mymin(d0, 0.5 * propa.dla); + d1 = d0 + 0.25 * (propa.dla - d0); + } + + else + d1 = mymax(-propa.aed / propa.emd, + 0.25 * propa.dla); + + a1 = alos(d1, prop, propa); + wq = false; + + if (d0 < d1) { + a0 = alos(d0, prop, propa); + q = log(d2 / d0); + propa.ak2 = + mymax(0.0, + ((d2 - d0) * (a1 - a0) - + (d1 - d0) * (a2 - a0)) / ((d2 - + d0) * + log(d1 / + d0) - + (d1 - + d0) * q)); + wq = propa.aed >= 0.0 || propa.ak2 > 0.0; + + if (wq) { + propa.ak1 = + (a2 - a0 - propa.ak2 * q) / (d2 - + d0); + + if (propa.ak1 < 0.0) { + propa.ak1 = 0.0; + propa.ak2 = + FORTRAN_DIM(a2, a0) / q; + + if (propa.ak2 == 0.0) + propa.ak1 = propa.emd; + } + } + + else { + propa.ak2 = 0.0; + propa.ak1 = (a2 - a1) / (d2 - d1); + + if (propa.ak1 <= 0.0) + propa.ak1 = propa.emd; + } + } + + else { + propa.ak1 = (a2 - a1) / (d2 - d1); + propa.ak2 = 0.0; + + if (propa.ak1 <= 0.0) + propa.ak1 = propa.emd; + } + + propa.ael = a2 - propa.ak1 * d2 - propa.ak2 * log(d2); + wlos = true; + } + + if (prop.dist > 0.0) + prop.aref = + propa.ael + propa.ak1 * prop.dist + + propa.ak2 * log(prop.dist); + + } + + if (prop.dist <= 0.0 || prop.dist >= propa.dlsa) { + if (!wscat) { + q = ascat(0.0, prop, propa); + d5 = propa.dla + 200e3; + d6 = d5 + 200e3; + a6 = ascat(d6, prop, propa); + a5 = ascat(d5, prop, propa); + + if (a5 < 1000.0) { + propa.ems = (a6 - a5) / 200e3; + propa.dx = + mymax(propa.dlsa, + mymax(propa.dla + + 0.3 * xae * log(47.7 * prop.wn), + (a5 - propa.aed - + propa.ems * d5) / (propa.emd - + propa. + ems))); + propa.aes = + (propa.emd - propa.ems) * propa.dx + + propa.aed; + } + + else { + propa.ems = propa.emd; + propa.aes = propa.aed; + propa.dx = 10.e6; + } + + wscat = true; + } + + if (prop.dist > propa.dx) + prop.aref = propa.aes + propa.ems * prop.dist; + else + prop.aref = propa.aed + propa.emd * prop.dist; + } + + prop.aref = mymax(prop.aref, 0.0); +} + +void lrprop2(double d, prop_type & prop, propa_type & propa) +{ + /* ITWOM_lrprop2 */ + static bool wlos, wscat; + static double dmin, xae; + complex < double >prop_zgnd(prop.zgndreal, prop.zgndimag); + double pd1; + double a0, a1, a2, a3, a4, a5, a6, iw; + double d0, d1, d2, d3, d4, d5, d6; + bool wq; + double q; + int j; + + iw = prop.tiw; + pd1 = prop.dist; + propa.dx = 2000000.0; + + if (prop.mdp != 0) { /* if oper. mode is not 0, i.e. not area mode ongoing */ + for (j = 0; j < 2; j++) + propa.dls[j] = sqrt(2.0 * prop.he[j] / prop.gme); + + propa.dlsa = propa.dls[0] + propa.dls[1]; + propa.dlsa = mymin(propa.dlsa, 1000000.0); + propa.dla = prop.dl[0] + prop.dl[1]; + propa.tha = + mymax(prop.the[0] + prop.the[1], -propa.dla * prop.gme); + wlos = false; + wscat = false; + + /*checking for parameters-in-range, error codes set if not */ + + if (prop.wn < 0.838 || prop.wn > 210.0) + prop.kwx = mymax(prop.kwx, 1); + + for (j = 0; j < 2; j++) + if (prop.hg[j] < 1.0 || prop.hg[j] > 1000.0) + prop.kwx = mymax(prop.kwx, 1); + + if (abs(prop.the[0]) > 200e-3) + prop.kwx = mymax(prop.kwx, 3); + + if (abs(prop.the[1]) > 1.220) + prop.kwx = mymax(prop.kwx, 3); + + /*for (j=0; j<2; j++) + if (prop.dl[j]<0.1*propa.dls[j] || prop.dl[j]>3.0*propa.dls[j]) + prop.kwx=mymax(prop.kwx,3); */ + + if (prop.ens < 250.0 || prop.ens > 400.0 || prop.gme < 75e-9 + || prop.gme > 250e-9 + || prop_zgnd.real() <= abs(prop_zgnd.imag()) + || prop.wn < 0.419 || prop.wn > 420.0) + prop.kwx = 4; + + for (j = 0; j < 2; j++) + + if (prop.hg[j] < 0.5 || prop.hg[j] > 3000.0) + prop.kwx = 4; + + dmin = abs(prop.he[0] - prop.he[1]) / 200e-3; + q = adiff2(0.0, prop, propa); + xae = pow(prop.wn * (prop.gme * prop.gme), -THIRD); + d3 = mymax(propa.dlsa, 1.3787 * xae + propa.dla); + d4 = d3 + 2.7574 * xae; + a3 = adiff2(d3, prop, propa); + a4 = adiff2(d4, prop, propa); + propa.emd = (a4 - a3) / (d4 - d3); + propa.aed = a3 - propa.emd * d3; + } + + if (prop.mdp >= 0) { /* if initializing the area mode */ + prop.mdp = 0; /* area mode is initialized */ + prop.dist = d; + } + + if (prop.dist > 0.0) { + if (prop.dist > 1000e3) /* prop.dist being in meters, if greater than 1000 km, kwx=1 */ + prop.kwx = mymax(prop.kwx, 1); + + if (prop.dist < dmin) + prop.kwx = mymax(prop.kwx, 3); + + if (prop.dist < 1e3 || prop.dist > 2000e3) + prop.kwx = 4; + } + + if (prop.dist < propa.dlsa) { + + if (iw <= 0.0) { /* if interval width is zero or less, used for area mode */ + + if (!wlos) { + q = alos2(0.0, prop, propa); + d2 = propa.dlsa; + a2 = propa.aed + d2 * propa.emd; + d0 = 1.908 * prop.wn * prop.he[0] * prop.he[1]; + + if (propa.aed > 0.0) { + prop.aref = + propa.aed + propa.emd * prop.dist; + } else { + if (propa.aed == 0.0) { + d0 = mymin(d0, 0.5 * propa.dla); + d1 = d0 + 0.25 * (propa.dla - + d0); + } else { /* aed less than zero */ + + d1 = mymax(-propa.aed / + propa.emd, + 0.25 * propa.dla); + } + a1 = alos2(d1, prop, propa); + wq = false; + + if (d0 < d1) { + a0 = alos2(d0, prop, propa); + a2 = mymin(a2, + alos2(d2, prop, + propa)); + q = log(d2 / d0); + propa.ak2 = + mymax(0.0, + ((d2 - d0) * (a1 - + a0) - + (d1 - d0) * (a2 - + a0)) / + ((d2 - + d0) * log(d1 / d0) - + (d1 - d0) * q)); + wq = propa.aed >= 0.0 + || propa.ak2 > 0.0; + + if (wq) { + propa.ak1 = + (a2 - a0 - + propa.ak2 * q) / + (d2 - d0); + + if (propa.ak1 < 0.0) { + propa.ak1 = 0.0; + propa.ak2 = + FORTRAN_DIM + (a2, + a0) / q; + + if (propa.ak2 == + 0.0) + propa. + ak1 + = + propa. + emd; + } + } + } + + if (!wq) { + propa.ak1 = + FORTRAN_DIM(a2, + a1) / (d2 - d1); + propa.ak2 = 0.0; + + if (propa.ak1 == 0.0) + propa.ak1 = propa.emd; + + } + propa.ael = + a2 - propa.ak1 * d2 - + propa.ak2 * log(d2); + wlos = true; + } + } + } else { /* for ITWOM point-to-point mode */ + + if (!wlos) { + q = alos2(0.0, prop, propa); /* coefficient setup */ + wlos = true; + } + + if (prop.los == 1) { /* if line of sight */ + prop.aref = alos2(pd1, prop, propa); + } else { + if (int (prop.dist - prop.dl[0]) == 0) { /* if at 1st horiz */ + prop.aref = + 5.8 + alos2(pd1, prop, propa); + } else if (int (prop.dist - prop.dl[0]) > 0.0) { /* if past 1st horiz */ + q = adiff2(0.0, prop, propa); + prop.aref = adiff2(pd1, prop, propa); + } else { + prop.aref = 1.0; + } + + } + } + } + + /* los and diff. range coefficents done. Starting troposcatter */ + if (prop.dist <= 0.0 || prop.dist >= propa.dlsa) { + if (iw == 0.0) { /* area mode */ + if (!wscat) { + q = ascat(0.0, prop, propa); + d5 = propa.dla + 200e3; + d6 = d5 + 200e3; + a6 = ascat(d6, prop, propa); + a5 = ascat(d5, prop, propa); + + if (a5 < 1000.0) { + propa.ems = (a6 - a5) / 200e3; + propa.dx = + mymax(propa.dlsa, + mymax(propa.dla + + 0.3 * xae * log(47.7 * + prop. + wn), + (a5 - propa.aed - + propa.ems * d5) / + (propa.emd - + propa.ems))); + + propa.aes = + (propa.emd - propa.ems) * propa.dx + + propa.aed; + } + + else { + propa.ems = propa.emd; + propa.aes = propa.aed; + propa.dx = 10000000; + } + wscat = true; + } + + if (prop.dist > propa.dx) { + prop.aref = propa.aes + propa.ems * prop.dist; + } else { + prop.aref = propa.aed + propa.emd * prop.dist; + } + } else { /* ITWOM mode q used to preset coefficients with zero input */ + + if (!wscat) { + d5 = 0.0; + d6 = 0.0; + q = ascat(0.0, prop, propa); + a6 = ascat(pd1, prop, propa); + q = adiff2(0.0, prop, propa); + a5 = adiff2(pd1, prop, propa); + + if (a5 <= a6) { + propa.dx = 10000000; + prop.aref = a5; + } else { + propa.dx = propa.dlsa; + prop.aref = a6; + } + wscat = true; + } + } + } + prop.aref = mymax(prop.aref, 0.0); +} + +double curve(double const &c1, double const &c2, double const &x1, + double const &x2, double const &x3, double const &de) +{ + /* return (c1+c2/(1.0+pow((de-x2)/x3,2.0)))*pow(de/x1,2.0)/(1.0+pow(de/x1,2.0)); */ + double temp1, temp2; + + temp1 = (de - x2) / x3; + temp2 = de / x1; + + temp1 *= temp1; + temp2 *= temp2; + + return (c1 + c2 / (1.0 + temp1)) * temp2 / (1.0 + temp2); +} + +double avar(double zzt, double zzl, double zzc, prop_type & prop, + propv_type & propv) +{ + static int kdv; + static double dexa, de, vmd, vs0, sgl, sgtm, sgtp, sgtd, tgtd, + gm, gp, cv1, cv2, yv1, yv2, yv3, csm1, csm2, ysm1, ysm2, + ysm3, csp1, csp2, ysp1, ysp2, ysp3, csd1, zd, cfm1, cfm2, + cfm3, cfp1, cfp2, cfp3; + + double bv1[7] = { -9.67, -0.62, 1.26, -9.21, -0.62, -0.39, 3.15 }; + double bv2[7] = { 12.7, 9.19, 15.5, 9.05, 9.19, 2.86, 857.9 }; + double xv1[7] = + { 144.9e3, 228.9e3, 262.6e3, 84.1e3, 228.9e3, 141.7e3, 2222.e3 }; + double xv2[7] = + { 190.3e3, 205.2e3, 185.2e3, 101.1e3, 205.2e3, 315.9e3, 164.8e3 }; + double xv3[7] = + { 133.8e3, 143.6e3, 99.8e3, 98.6e3, 143.6e3, 167.4e3, 116.3e3 }; + double bsm1[7] = { 2.13, 2.66, 6.11, 1.98, 2.68, 6.86, 8.51 }; + double bsm2[7] = { 159.5, 7.67, 6.65, 13.11, 7.16, 10.38, 169.8 }; + double xsm1[7] = + { 762.2e3, 100.4e3, 138.2e3, 139.1e3, 93.7e3, 187.8e3, 609.8e3 }; + double xsm2[7] = + { 123.6e3, 172.5e3, 242.2e3, 132.7e3, 186.8e3, 169.6e3, 119.9e3 }; + double xsm3[7] = + { 94.5e3, 136.4e3, 178.6e3, 193.5e3, 133.5e3, 108.9e3, 106.6e3 }; + double bsp1[7] = { 2.11, 6.87, 10.08, 3.68, 4.75, 8.58, 8.43 }; + double bsp2[7] = { 102.3, 15.53, 9.60, 159.3, 8.12, 13.97, 8.19 }; + double xsp1[7] = + { 636.9e3, 138.7e3, 165.3e3, 464.4e3, 93.2e3, 216.0e3, 136.2e3 }; + double xsp2[7] = + { 134.8e3, 143.7e3, 225.7e3, 93.1e3, 135.9e3, 152.0e3, 188.5e3 }; + double xsp3[7] = + { 95.6e3, 98.6e3, 129.7e3, 94.2e3, 113.4e3, 122.7e3, 122.9e3 }; + double bsd1[7] = { 1.224, 0.801, 1.380, 1.000, 1.224, 1.518, 1.518 }; + double bzd1[7] = { 1.282, 2.161, 1.282, 20., 1.282, 1.282, 1.282 }; + double bfm1[7] = { 1.0, 1.0, 1.0, 1.0, 0.92, 1.0, 1.0 }; + double bfm2[7] = { 0.0, 0.0, 0.0, 0.0, 0.25, 0.0, 0.0 }; + double bfm3[7] = { 0.0, 0.0, 0.0, 0.0, 1.77, 0.0, 0.0 }; + double bfp1[7] = { 1.0, 0.93, 1.0, 0.93, 0.93, 1.0, 1.0 }; + double bfp2[7] = { 0.0, 0.31, 0.0, 0.19, 0.31, 0.0, 0.0 }; + double bfp3[7] = { 0.0, 2.00, 0.0, 1.79, 2.00, 0.0, 0.0 }; + static bool ws, w1; + double rt = 7.8, rl = 24.0, avarv, q, vs, zt, zl, zc; + double sgt, yr, temp1, temp2; + int temp_klim = propv.klim - 1; + + if (propv.lvar > 0) { + switch (propv.lvar) { + default: + if (propv.klim <= 0 || propv.klim > 7) { + propv.klim = 5; + temp_klim = 4; + prop.kwx = mymax(prop.kwx, 2); + } + + cv1 = bv1[temp_klim]; + cv2 = bv2[temp_klim]; + yv1 = xv1[temp_klim]; + yv2 = xv2[temp_klim]; + yv3 = xv3[temp_klim]; + csm1 = bsm1[temp_klim]; + csm2 = bsm2[temp_klim]; + ysm1 = xsm1[temp_klim]; + ysm2 = xsm2[temp_klim]; + ysm3 = xsm3[temp_klim]; + csp1 = bsp1[temp_klim]; + csp2 = bsp2[temp_klim]; + ysp1 = xsp1[temp_klim]; + ysp2 = xsp2[temp_klim]; + ysp3 = xsp3[temp_klim]; + csd1 = bsd1[temp_klim]; + zd = bzd1[temp_klim]; + cfm1 = bfm1[temp_klim]; + cfm2 = bfm2[temp_klim]; + cfm3 = bfm3[temp_klim]; + cfp1 = bfp1[temp_klim]; + cfp2 = bfp2[temp_klim]; + cfp3 = bfp3[temp_klim]; + + case 4: + kdv = propv.mdvar; + ws = kdv >= 20; + + if (ws) + kdv -= 20; + + w1 = kdv >= 10; + + if (w1) + kdv -= 10; + + if (kdv < 0 || kdv > 3) { + kdv = 0; + prop.kwx = mymax(prop.kwx, 2); + } + + case 3: + q = log(0.133 * prop.wn); + + /* gm=cfm1+cfm2/(pow(cfm3*q,2.0)+1.0); */ + /* gp=cfp1+cfp2/(pow(cfp3*q,2.0)+1.0); */ + + gm = cfm1 + cfm2 / ((cfm3 * q * cfm3 * q) + 1.0); + gp = cfp1 + cfp2 / ((cfp3 * q * cfp3 * q) + 1.0); + + case 2: + dexa = + sqrt(18e6 * prop.he[0]) + sqrt(18e6 * prop.he[1]) + + pow((575.7e12 / prop.wn), THIRD); + + case 1: + if (prop.dist < dexa) + de = 130e3 * prop.dist / dexa; + else + de = 130e3 + prop.dist - dexa; + } + + vmd = curve(cv1, cv2, yv1, yv2, yv3, de); + sgtm = curve(csm1, csm2, ysm1, ysm2, ysm3, de) * gm; + sgtp = curve(csp1, csp2, ysp1, ysp2, ysp3, de) * gp; + sgtd = sgtp * csd1; + tgtd = (sgtp - sgtd) * zd; + + if (w1) + sgl = 0.0; + else { + q = (1.0 - + 0.8 * exp(-prop.dist / 50e3)) * prop.dh * prop.wn; + sgl = 10.0 * q / (q + 13.0); + } + + if (ws) + vs0 = 0.0; + else { + /* vs0=pow(5.0+3.0*exp(-de/100e3),2.0); */ + temp1 = (5.0 + 3.0 * exp(-de / 100e3)); + vs0 = temp1 * temp1; + + } + + propv.lvar = 0; + } + + zt = zzt; + zl = zzl; + zc = zzc; + + switch (kdv) { + case 0: + zt = zc; + zl = zc; + break; + + case 1: + zl = zc; + break; + + case 2: + zl = zt; + } + + if (fabs(zt) > 3.1 || fabs(zl) > 3.1 || fabs(zc) > 3.1) + prop.kwx = mymax(prop.kwx, 1); + + if (zt < 0.0) + sgt = sgtm; + + else if (zt <= zd) + sgt = sgtp; + + else + sgt = sgtd + tgtd / zt; + + /* vs=vs0+pow(sgt*zt,2.0)/(rt+zc*zc)+pow(sgl*zl,2.0)/(rl+zc*zc); */ + + temp1 = sgt * zt; + temp2 = sgl * zl; + + vs = vs0 + (temp1 * temp1) / (rt + zc * zc) + (temp2 * temp2) / (rl + + zc * + zc); + + if (kdv == 0) { + yr = 0.0; + propv.sgc = sqrt(sgt * sgt + sgl * sgl + vs); + } + + else if (kdv == 1) { + yr = sgt * zt; + propv.sgc = sqrt(sgl * sgl + vs); + } + + else if (kdv == 2) { + yr = sqrt(sgt * sgt + sgl * sgl) * zt; + propv.sgc = sqrt(vs); + } + + else { + yr = sgt * zt + sgl * zl; + propv.sgc = sqrt(vs); + } + + avarv = prop.aref - vmd - yr - propv.sgc * zc; + + if (avarv < 0.0) + avarv = avarv * (29.0 - avarv) / (29.0 - 10.0 * avarv); + + return avarv; +} + +void hzns(double pfl[], prop_type & prop) +{ + /* Used only with ITM 1.2.2 */ + bool wq; + int np; + double xi, za, zb, qc, q, sb, sa; + + np = (int)pfl[0]; + xi = pfl[1]; + za = pfl[2] + prop.hg[0]; + zb = pfl[np + 2] + prop.hg[1]; + qc = 0.5 * prop.gme; + q = qc * prop.dist; + prop.the[1] = (zb - za) / prop.dist; + prop.the[0] = prop.the[1] - q; + prop.the[1] = -prop.the[1] - q; + prop.dl[0] = prop.dist; + prop.dl[1] = prop.dist; + + if (np >= 2) { + sa = 0.0; + sb = prop.dist; + wq = true; + + for (int i = 1; i < np; i++) { + sa += xi; + sb -= xi; + q = pfl[i + 2] - (qc * sa + prop.the[0]) * sa - za; + + if (q > 0.0) { + prop.the[0] += q / sa; + prop.dl[0] = sa; + wq = false; + } + + if (!wq) { + q = pfl[i + 2] - (qc * sb + prop.the[1]) * sb - + zb; + + if (q > 0.0) { + prop.the[1] += q / sb; + prop.dl[1] = sb; + } + } + } + } +} + +void hzns2(double pfl[], prop_type & prop, propa_type & propa) +{ + bool wq; + int np, rp, i, j; + double xi, za, zb, qc, q, sb, sa, dr, dshh; + + np = (int)pfl[0]; + xi = pfl[1]; + za = pfl[2] + prop.hg[0]; + zb = pfl[np + 2] + prop.hg[1]; + prop.tiw = xi; + prop.ght = za; + prop.ghr = zb; + qc = 0.5 * prop.gme; + q = qc * prop.dist; + prop.the[1] = atan((zb - za) / prop.dist); + prop.the[0] = (prop.the[1]) - q; + prop.the[1] = -prop.the[1] - q; + prop.dl[0] = prop.dist; + prop.dl[1] = prop.dist; + prop.hht = 0.0; + prop.hhr = 0.0; + prop.los = 1; + + if (np >= 2) { + sa = 0.0; + sb = prop.dist; + wq = true; + + for (j = 1; j < np; j++) { + sa += xi; + q = pfl[j + 2] - (qc * sa + prop.the[0]) * sa - za; + + if (q > 0.0) { + prop.los = 0; + prop.the[0] += q / sa; + prop.dl[0] = sa; + prop.the[0] = mymin(prop.the[0], 1.569); + prop.hht = pfl[j + 2]; + wq = false; + } + } + + if (!wq) { + for (i = 1; i < np; i++) { + sb -= xi; + q = pfl[np + 2 - i] - (qc * (prop.dist - sb) + + prop.the[1]) * + (prop.dist - sb) - zb; + if (q > 0.0) { + prop.the[1] += q / (prop.dist - sb); + prop.the[1] = mymin(prop.the[1], 1.57); + prop.the[1] = + mymax(prop.the[1], -1.568); + prop.hhr = pfl[np + 2 - i]; + prop.dl[1] = mymax(0.0, prop.dist - sb); + } + } + prop.the[0] = + atan((prop.hht - za) / prop.dl[0]) - + 0.5 * prop.gme * prop.dl[0]; + prop.the[1] = + atan((prop.hhr - zb) / prop.dl[1]) - + 0.5 * prop.gme * prop.dl[1]; + } + } + + if ((prop.dl[1]) < (prop.dist)) { + dshh = prop.dist - prop.dl[0] - prop.dl[1]; + + if (int (dshh) == 0) { /* one obstacle */ + dr = prop.dl[1] / (1 + zb / prop.hht); + } else { /* two obstacles */ + + dr = prop.dl[1] / (1 + zb / prop.hhr); + } + } else { /* line of sight */ + + dr = (prop.dist) / (1 + zb / za); + } + rp = 2 + (int)(floor(0.5 + dr / xi)); + prop.rpl = rp; + prop.rph = pfl[rp]; +} + +void z1sq1(double z[], const double &x1, const double &x2, double &z0, + double &zn) +{ + /* Used only with ITM 1.2.2 */ + double xn, xa, xb, x, a, b; + int n, ja, jb; + + xn = z[0]; + xa = int (FORTRAN_DIM(x1 / z[1], 0.0)); + xb = xn - int (FORTRAN_DIM(xn, x2 / z[1])); + + if (xb <= xa) { + xa = FORTRAN_DIM(xa, 1.0); + xb = xn - FORTRAN_DIM(xn, xb + 1.0); + } + + ja = (int)xa; + jb = (int)xb; + n = jb - ja; + xa = xb - xa; + x = -0.5 * xa; + xb += x; + a = 0.5 * (z[ja + 2] + z[jb + 2]); + b = 0.5 * (z[ja + 2] - z[jb + 2]) * x; + + for (int i = 2; i <= n; ++i) { + ++ja; + x += 1.0; + a += z[ja + 2]; + b += z[ja + 2] * x; + } + + a /= xa; + b = b * 12.0 / ((xa * xa + 2.0) * xa); + z0 = a - b * xb; + zn = a + b * (xn - xb); +} + +void z1sq2(double z[], const double &x1, const double &x2, double &z0, + double &zn) +{ + /* corrected for use with ITWOM */ + double xn, xa, xb, x, a, b, bn; + int n, ja, jb; + + xn = z[0]; + xa = int (FORTRAN_DIM(x1 / z[1], 0.0)); + xb = xn - int (FORTRAN_DIM(xn, x2 / z[1])); + + if (xb <= xa) { + xa = FORTRAN_DIM(xa, 1.0); + xb = xn - FORTRAN_DIM(xn, xb + 1.0); + } + + ja = (int)xa; + jb = (int)xb; + xa = (2 * int ((xb - xa) / 2))-1; + x = -0.5 * (xa + 1); + xb += x; + ja = jb - 1 - (int)xa; + n = jb - ja; + a = (z[ja + 2] + z[jb + 2]); + b = (z[ja + 2] - z[jb + 2]) * x; + bn = 2 * (x * x); + + for (int i = 2; i <= n; ++i) { + ++ja; + x += 1.0; + bn += (x * x); + a += z[ja + 2]; + b += z[ja + 2] * x; + } + + a /= (xa + 2); + b = b / bn; + z0 = a - (b * xb); + zn = a + (b * (xn - xb)); +} + +double qtile(const int &nn, double a[], const int &ir) +{ + double q = 0.0, r; /* q initialization -- KD2BD */ + int m, n, i, j, j1 = 0, i0 = 0, k; /* more initializations -- KD2BD */ + bool done = false; + bool goto10 = true; + + m = 0; + n = nn; + k = mymin(mymax(0, ir), n); + + while (!done) { + if (goto10) { + q = a[k]; + i0 = m; + j1 = n; + } + + i = i0; + + while (i <= n && a[i] >= q) + i++; + + if (i > n) + i = n; + + j = j1; + + while (j >= m && a[j] <= q) + j--; + + if (j < m) + j = m; + + if (i < j) { + r = a[i]; + a[i] = a[j]; + a[j] = r; + i0 = i + 1; + j1 = j - 1; + goto10 = false; + } + + else if (i < k) { + a[k] = a[i]; + a[i] = q; + m = i + 1; + goto10 = true; + } + + else if (j > k) { + a[k] = a[j]; + a[j] = q; + n = j - 1; + goto10 = true; + } + + else + done = true; + } + + return q; +} + +double qerf(const double &z) +{ + double b1 = 0.319381530, b2 = -0.356563782, b3 = 1.781477937; + double b4 = -1.821255987, b5 = 1.330274429; + double rp = 4.317008, rrt2pi = 0.398942280; + double t, x, qerfv; + + x = z; + t = fabs(x); + + if (t >= 10.0) + qerfv = 0.0; + else { + t = rp / (t + rp); + qerfv = + exp(-0.5 * x * x) * rrt2pi * + ((((b5 * t + b4) * t + b3) * t + b2) * t + b1) * t; + } + + if (x < 0.0) + qerfv = 1.0 - qerfv; + + return qerfv; +} + +double d1thx(double pfl[], const double &x1, const double &x2) +{ + int np, ka, kb, n, k, j; + double d1thxv, sn, xa, xb; + double *s; + + np = (int)pfl[0]; + xa = x1 / pfl[1]; + xb = x2 / pfl[1]; + d1thxv = 0.0; + + if (xb - xa < 2.0) // exit out + return d1thxv; + + ka = (int)(0.1 * (xb - xa + 8.0)); + ka = mymin(mymax(4, ka), 25); + n = 10 * ka - 5; + kb = n - ka + 1; + sn = n - 1; + assert((s = new double[n + 2])!=0); + s[0] = sn; + s[1] = 1.0; + xb = (xb - xa) / sn; + k = (int)(xa + 1.0); + xa -= (double)k; + + for (j = 0; j < n; j++) { + while (xa > 0.0 && k < np) { + xa -= 1.0; + ++k; + } + + s[j + 2] = pfl[k + 2] + (pfl[k + 2] - pfl[k + 1]) * xa; + xa = xa + xb; + } + + z1sq1(s, 0.0, sn, xa, xb); + xb = (xb - xa) / sn; + + for (j = 0; j < n; j++) { + s[j + 2] -= xa; + xa = xa + xb; + } + + d1thxv = qtile(n - 1, s + 2, ka - 1) - qtile(n - 1, s + 2, kb - 1); + d1thxv /= 1.0 - 0.8 * exp(-(x2 - x1) / 50.0e3); + delete[]s; + + return d1thxv; +} + +double d1thx2(double pfl[], const double &x1, const double &x2, + propa_type & propa) +{ + int np, ka, kb, n, k, kmx, j; + double d1thx2v, sn, xa, xb, xc; + double *s; + + np = (int)pfl[0]; + xa = x1 / pfl[1]; + xb = x2 / pfl[1]; + d1thx2v = 0.0; + + if (xb - xa < 2.0) // exit out + return d1thx2v; + + ka = (int)(0.1 * (xb - xa + 8.0)); + kmx = mymax(25, (int)(83350 / (pfl[1]))); + ka = mymin(mymax(4, ka), kmx); + n = 10 * ka - 5; + kb = n - ka + 1; + sn = n - 1; + assert((s = new double[n + 2])!=0); + s[0] = sn; + s[1] = 1.0; + xb = (xb - xa) / sn; + k = (int (xa + 1.0)); + xc = xa - (double (k)); + + for (j = 0; j < n; j++) { + while (xc > 0.0 && k < np) { + xc -= 1.0; + ++k; + } + + s[j + 2] = pfl[k + 2] + (pfl[k + 2] - pfl[k + 1]) * xc; + xc = xc + xb; + } + + z1sq2(s, 0.0, sn, xa, xb); + xb = (xb - xa) / sn; + + for (j = 0; j < n; j++) { + s[j + 2] -= xa; + xa = xa + xb; + } + + d1thx2v = qtile(n - 1, s + 2, ka - 1) - qtile(n - 1, s + 2, kb - 1); + d1thx2v /= 1.0 - 0.8 * exp(-(x2 - x1) / 50.0e3); + delete[]s; + return d1thx2v; +} + +void qlrpfl(double pfl[], int klimx, int mdvarx, prop_type & prop, + propa_type & propa, propv_type & propv) +{ + int np, j; + double xl[2], q, za, zb, temp; + + prop.dist = pfl[0] * pfl[1]; + np = (int)pfl[0]; + hzns(pfl, prop); + + for (j = 0; j < 2; j++) + xl[j] = mymin(15.0 * prop.hg[j], 0.1 * prop.dl[j]); + + xl[1] = prop.dist - xl[1]; + prop.dh = d1thx(pfl, xl[0], xl[1]); + + if (prop.dl[0] + prop.dl[1] > 1.5 * prop.dist) { + z1sq1(pfl, xl[0], xl[1], za, zb); + prop.he[0] = prop.hg[0] + FORTRAN_DIM(pfl[2], za); + prop.he[1] = prop.hg[1] + FORTRAN_DIM(pfl[np + 2], zb); + + for (j = 0; j < 2; j++) + prop.dl[j] = + sqrt(2.0 * prop.he[j] / prop.gme) * exp(-0.07 * + sqrt(prop. + dh / + mymax + (prop. + he[j], + 5.0))); + + q = prop.dl[0] + prop.dl[1]; + + if (q <= prop.dist) { /* if there is a rounded horizon, or two obstructions, in the path */ + /* q=pow(prop.dist/q,2.0); */ + temp = prop.dist / q; + q = temp * temp; + + for (j = 0; j < 2; j++) { + prop.he[j] *= q; /* tx effective height set to be path dist/distance between obstacles */ + prop.dl[j] = + sqrt(2.0 * prop.he[j] / prop.gme) * + exp(-0.07 * + sqrt(prop.dh / mymax(prop.he[j], 5.0))); + } + } + + for (j = 0; j < 2; j++) { /* original empirical adjustment? uses delta-h to adjust grazing angles */ + q = sqrt(2.0 * prop.he[j] / prop.gme); + prop.the[j] = + (0.65 * prop.dh * (q / prop.dl[j] - 1.0) - + 2.0 * prop.he[j]) / q; + } + } + + else { + z1sq1(pfl, xl[0], 0.9 * prop.dl[0], za, q); + z1sq1(pfl, prop.dist - 0.9 * prop.dl[1], xl[1], q, zb); + prop.he[0] = prop.hg[0] + FORTRAN_DIM(pfl[2], za); + prop.he[1] = prop.hg[1] + FORTRAN_DIM(pfl[np + 2], zb); + } + + prop.mdp = -1; + propv.lvar = mymax(propv.lvar, 3); + + if (mdvarx >= 0) { + propv.mdvar = mdvarx; + propv.lvar = mymax(propv.lvar, 4); + } + + if (klimx > 0) { + propv.klim = klimx; + propv.lvar = 5; + } + + lrprop(0.0, prop, propa); +} + +void qlrpfl2(double pfl[], int klimx, int mdvarx, prop_type & prop, + propa_type & propa, propv_type & propv) +{ + int np, j; + double xl[2], dlb, q, za, zb, temp, rad, rae1, rae2; + + prop.dist = pfl[0] * pfl[1]; + np = (int)pfl[0]; + hzns2(pfl, prop, propa); + dlb = prop.dl[0] + prop.dl[1]; + prop.rch[0] = prop.hg[0] + pfl[2]; + prop.rch[1] = prop.hg[1] + pfl[np + 2]; + + for (j = 0; j < 2; j++) + xl[j] = mymin(15.0 * prop.hg[j], 0.1 * prop.dl[j]); + + xl[1] = prop.dist - xl[1]; + prop.dh = d1thx2(pfl, xl[0], xl[1], propa); + + if ((np < 1) || (pfl[1] > 150.0)) { + /* for TRANSHORIZON; diffraction over a mutual horizon, or for one or more obstructions */ + if (dlb < 1.5 * prop.dist) { + z1sq2(pfl, xl[0], 0.9 * prop.dl[0], za, q); + z1sq2(pfl, prop.dist - 0.9 * prop.dl[1], xl[1], q, zb); + prop.he[0] = prop.hg[0] + FORTRAN_DIM(pfl[2], za); + prop.he[1] = prop.hg[1] + FORTRAN_DIM(pfl[np + 2], zb); + } + + /* for a Line-of-Sight path */ + else { + z1sq2(pfl, xl[0], xl[1], za, zb); + prop.he[0] = prop.hg[0] + FORTRAN_DIM(pfl[2], za); + prop.he[1] = prop.hg[1] + FORTRAN_DIM(pfl[np + 2], zb); + + for (j = 0; j < 2; j++) + prop.dl[j] = + sqrt(2.0 * prop.he[j] / prop.gme) * + exp(-0.07 * + sqrt(prop.dh / mymax(prop.he[j], 5.0))); + + /* for one or more obstructions only NOTE buried as in ITM FORTRAN and DLL, not functional */ + if ((prop.dl[0] + prop.dl[1]) <= prop.dist) { + /* q=pow(prop.dist/(dl[0]+dl[1])),2.0); */ + temp = prop.dist / (prop.dl[0] + prop.dl[1]); + q = temp * temp; + } + + for (j = 0; j < 2; j++) { + prop.he[j] *= q; + prop.dl[j] = + sqrt(2.0 * prop.he[j] / prop.gme) * + exp(-0.07 * + sqrt(prop.dh / mymax(prop.he[j], 5.0))); + } + + /* this sets (or resets) prop.the, and is not in The Guide FORTRAN QLRPFL */ + for (j = 0; j < 2; j++) { + q = sqrt(2.0 * prop.he[j] / prop.gme); + prop.the[j] = + (0.65 * prop.dh * (q / prop.dl[j] - 1.0) - + 2.0 * prop.he[j]) / q; + } + } + } + + else { /* for ITWOM ,computes he for tx, rcvr, and the receiver approach angles for use in saalos */ + + prop.he[0] = prop.hg[0] + (pfl[2]); + prop.he[1] = prop.hg[1] + (pfl[np + 2]); + + rad = (prop.dist - 500.0); + + if (prop.dist > 550.0) { + z1sq2(pfl, rad, prop.dist, rae1, rae2); + } else { + rae1 = 0.0; + rae2 = 0.0; + } + + prop.thera = atan(abs(rae2 - rae1) / prop.dist); + + if (rae2 < rae1) { + prop.thera = -prop.thera; + } + + prop.thenr = + atan(mymax(0.0, (pfl[np + 2] - pfl[np + 1])) / pfl[1]); + + } + + prop.mdp = -1; + propv.lvar = mymax(propv.lvar, 3); + + if (mdvarx >= 0) { + propv.mdvar = mdvarx; + propv.lvar = mymax(propv.lvar, 4); + } + + if (klimx > 0) { + propv.klim = klimx; + propv.lvar = 5; + } + + lrprop2(0.0, prop, propa); +} + +double deg2rad(double d) +{ + return d * 3.1415926535897 / 180.0; +} + +//*************************************************************************************** +//* Point-To-Point Mode Calculations +//*************************************************************************************** + +void point_to_point_ITM(double tht_m, double rht_m, double eps_dielect, + double sgm_conductivity, double eno_ns_surfref, + double frq_mhz, int radio_climate, int pol, + double conf, double rel, double &dbloss, char *strmode, + int &errnum) + +/****************************************************************************** + +Note that point_to_point has become point_to_point_ITM for use as the old ITM + + pol: + 0-Horizontal, 1-Vertical + + radio_climate: + 1-Equatorial, 2-Continental Subtropical, + 3-Maritime Tropical, 4-Desert, 5-Continental Temperate, + 6-Maritime Temperate, Over Land, 7-Maritime Temperate, + Over Sea + + conf, rel: .01 to .99 + + elev[]: [num points - 1], [delta dist(meters)], + [height(meters) point 1], ..., [height(meters) point n] + + errnum: 0- No Error. + 1- Warning: Some parameters are nearly out of range. + Results should be used with caution. + 2- Note: Default parameters have been substituted for + impossible ones. + 3- Warning: A combination of parameters is out of range. + Results are probably invalid. + Other- Warning: Some parameters are out of range. + Results are probably invalid. + +*****************************************************************************/ +{ + prop_type prop; + propv_type propv; + propa_type propa; + double zsys = 0; + double zc, zr; + double eno, enso, q; + long ja, jb, i, np; + /* double dkm, xkm; */ + double fs; + + prop.hg[0] = tht_m; + prop.hg[1] = rht_m; + propv.klim = radio_climate; + prop.kwx = 0; + propv.lvar = 5; + prop.mdp = -1; + zc = qerfi(conf); + zr = qerfi(rel); + np = (long)elev[0]; + /* dkm=(elev[1]*elev[0])/1000.0; */ + /* xkm=elev[1]/1000.0; */ + eno = eno_ns_surfref; + enso = 0.0; + q = enso; + + if (q <= 0.0) { + ja = (long)(3.0 + 0.1 * elev[0]); /* added (long) to correct */ + jb = np - ja + 6; + + for (i = ja - 1; i < jb; ++i) + zsys += elev[i]; + + zsys /= (jb - ja + 1); + q = eno; + } + + propv.mdvar = 12; + qlrps(frq_mhz, zsys, q, pol, eps_dielect, sgm_conductivity, prop); + qlrpfl(elev, propv.klim, propv.mdvar, prop, propa, propv); + fs = 32.45 + 20.0 * log10(frq_mhz) + 20.0 * log10(prop.dist / 1000.0); + q = prop.dist - propa.dla; + + if (int (q) < 0.0) + strcpy(strmode, "Line-Of-Sight Mode"); + else { + if (int (q) == 0.0) + strcpy(strmode, "Single Horizon"); + + else if (int (q) > 0.0) + strcpy(strmode, "Double Horizon"); + + if (prop.dist <= propa.dlsa || prop.dist <= propa.dx) + strcat(strmode, ", Diffraction Dominant"); + + else if (prop.dist > propa.dx) + strcat(strmode, ", Troposcatter Dominant"); + } + + dbloss = avar(zr, 0.0, zc, prop, propv) + fs; + errnum = prop.kwx; +} + +void point_to_point(double tht_m, double rht_m, double eps_dielect, + double sgm_conductivity, double eno_ns_surfref, + double frq_mhz, int radio_climate, int pol, double conf, + double rel, double &dbloss, char *strmode, int &errnum) + +/****************************************************************************** + + Note that point_to_point_two has become point_to_point + for drop-in interface to splat.cpp. + The new variable inputs, + double enc_ncc_clcref, + double clutter_height, + double clutter_density, + double delta_h_diff, and + int mode_var) + have been given fixed values below. + + pol: + 0-Horizontal, 1-Vertical, 2-Circular + + radio_climate: + 1-Equatorial, 2-Continental Subtropical, + 3-Maritime Tropical, 4-Desert, 5-Continental Temperate, + 6-Maritime Temperate, Over Land, 7-Maritime Temperate, + Over Sea + + conf, rel: .01 to .99 + + elev[]: [num points - 1], [delta dist(meters)], + [height(meters) point 1], ..., [height(meters) point n] + + clutter_height 25.2 meters for compatibility with ITU-R P.1546-2. + + clutter_density 1.0 for compatibility with ITU-R P.1546-2. + + delta_h_diff optional delta h for beyond line of sight. 90 m. average. + setting to 0.0 will default to use of original internal + use of delta-h for beyond line-of-sight range. + + mode_var set to 12; or to 1 for FCC ILLR; see documentation + + enc_ncc_clcref clutter refractivity; 1000 N-units to match ITU-R P.1546-2 + + eno=eno_ns_surfref atmospheric refractivity at sea level; 301 N-units nominal + (ranges from 250 for dry, hot day to 450 on hot, humid day] + (stabilizes near 301 in cold, clear weather) + + errnum: 0- No Error. + 1- Warning: Some parameters are nearly out of range. + Results should be used with caution. + 2- Note: Default parameters have been substituted for + impossible ones. + 3- Warning: A combination of parameters is out of range. + Results are probably invalid. + Other- Warning: Some parameters are out of range. + Results are probably invalid. + +*****************************************************************************/ +{ + prop_type prop; + propv_type propv; + propa_type propa; + + double zsys = 0; + double zc, zr; + double eno, enso, q; + long ja, jb, i, np; + /* double dkm, xkm; */ + double tpd, fs; + + prop.hg[0] = tht_m; + prop.hg[1] = rht_m; + propv.klim = radio_climate; + prop.kwx = 0; + propv.lvar = 5; + prop.mdp = -1; + prop.ptx = pol; + prop.thera = 0.0; + prop.thenr = 0.0; + zc = qerfi(conf); + zr = qerfi(rel); + np = (long)elev[0]; + /* dkm=(elev[1]*elev[0])/1000.0; */ + /* xkm=elev[1]/1000.0; */ + eno = eno_ns_surfref; + enso = 0.0; + q = enso; + + /* PRESET VALUES for Basic Version w/o additional inputs active */ + + prop.encc = 1000.00; /* double enc_ncc_clcref preset */ + prop.cch = 22.5; /* double clutter_height preset to ILLR calibration.; + use 25.3 for ITU-P1546-2 calibration */ + prop.cd = 1.00; /* double clutter_density preset */ + int mode_var = 1; /* int mode_var set to 1 for FCC compatibility; + normally, SPLAT presets this to 12 */ + prop.dhd = 0.0; /* delta_h_diff preset */ + + if (q <= 0.0) { + ja = (long)(3.0 + 0.1 * elev[0]); + jb = np - ja + 6; + + for (i = ja - 1; i < jb; ++i) + zsys += elev[i]; + + zsys /= (jb - ja + 1); + q = eno; + } + + propv.mdvar = mode_var; + qlrps(frq_mhz, zsys, q, pol, eps_dielect, sgm_conductivity, prop); + qlrpfl2(elev, propv.klim, propv.mdvar, prop, propa, propv); + tpd = + sqrt((prop.he[0] - prop.he[1]) * (prop.he[0] - prop.he[1]) + + (prop.dist) * (prop.dist)); + fs = 32.45 + 20.0 * log10(frq_mhz) + 20.0 * log10(tpd / 1000.0); + q = prop.dist - propa.dla; + + if (int (q) < 0.0) + strcpy(strmode, "L-o-S"); + else { + if (int (q) == 0.0) + strcpy(strmode, "1_Hrzn"); + + else if (int (q) > 0.0) + strcpy(strmode, "2_Hrzn"); + + if (prop.dist <= propa.dlsa || prop.dist <= propa.dx) + + if (int (prop.dl[1]) == 0.0) + strcat(strmode, "_Peak"); + + else + strcat(strmode, "_Diff"); + + else if (prop.dist > propa.dx) + strcat(strmode, "_Tropo"); + } + + dbloss = avar(zr, 0.0, zc, prop, propv) + fs; + errnum = prop.kwx; +} + +void point_to_pointMDH_two(double tht_m, double rht_m, double eps_dielect, + double sgm_conductivity, double eno_ns_surfref, + double enc_ncc_clcref, double clutter_height, + double clutter_density, double delta_h_diff, + double frq_mhz, int radio_climate, int pol, + int mode_var, double timepct, double locpct, + double confpct, double &dbloss, int &propmode, + double &deltaH, int &errnum) + +/************************************************************************************************* + pol: 0-Horizontal, 1-Vertical + radio_climate: 1-Equatorial, 2-Continental Subtropical, 3-Maritime Tropical, + 4-Desert, 5-Continental Temperate, 6-Maritime Temperate, Over Land, + 7-Maritime Temperate, Over Sea + timepct, locpct, confpct: .01 to .99 + elev[]: [num points - 1], [delta dist(meters)], [height(meters) point 1], ..., [height(meters) point n] + propmode: Value Mode + -1 mode is undefined + 0 Line of Sight + 5 Single Horizon, Diffraction + 6 Single Horizon, Troposcatter + 9 Double Horizon, Diffraction + 10 Double Horizon, Troposcatter + errnum: 0- No Error. + 1- Warning: Some parameters are nearly out of range. + Results should be used with caution. + 2- Note: Default parameters have been substituted for impossible ones. + 3- Warning: A combination of parameters is out of range. + Results are probably invalid. + Other- Warning: Some parameters are out of range. + Results are probably invalid. +*************************************************************************************************/ +{ + + prop_type prop; + propv_type propv; + propa_type propa; + double zsys = 0; + double ztime, zloc, zconf; + double eno, enso, q; + long ja, jb, i, np; + /* double dkm, xkm; */ + double fs; + + propmode = -1; // mode is undefined + prop.hg[0] = tht_m; + prop.hg[1] = rht_m; + propv.klim = radio_climate; + prop.encc = enc_ncc_clcref; + prop.cch = clutter_height; + prop.cd = clutter_density; + prop.dhd = delta_h_diff; + prop.kwx = 0; + propv.lvar = 5; + prop.mdp = -1; + prop.ptx = pol; + prop.thera = 0.0; + prop.thenr = 0.0; + ztime = qerfi(timepct); + zloc = qerfi(locpct); + zconf = qerfi(confpct); + np = (long)elev[0]; + /* dkm = (elev[1] * elev[0]) / 1000.0; */ + /* xkm = elev[1] / 1000.0; */ + eno = eno_ns_surfref; + enso = 0.0; + q = enso; + + /* PRESET VALUES for Basic Version w/o additional inputs active */ + + prop.encc = 1000.00; /* double enc_ncc_clcref */ + prop.cch = 22.5; /* double clutter_height */ + prop.cd = 1.00; /* double clutter_density */ + mode_var = 1; /* int mode_var set for FCC ILLR */ + + if (q <= 0.0) { + ja = (long)(3.0 + 0.1 * elev[0]); /* to match addition of (long) */ + jb = np - ja + 6; + for (i = ja - 1; i < jb; ++i) + zsys += elev[i]; + zsys /= (jb - ja + 1); + q = eno; + } + propv.mdvar = 12; + qlrps(frq_mhz, zsys, q, pol, eps_dielect, sgm_conductivity, prop); + qlrpfl2(elev, propv.klim, propv.mdvar, prop, propa, propv); + fs = 32.45 + 20.0 * log10(frq_mhz) + 20.0 * log10(prop.dist / 1000.0); + + deltaH = prop.dh; + q = prop.dist - propa.dla; + if (int (q) < 0.0) + propmode = 0; // L-of-S + else { + if (int (q) == 0.0) + propmode = 4; // 1-Hrzn + else if (int (q) > 0.0) + propmode = 8; // 2-Hrzn + if (prop.dist <= propa.dlsa || prop.dist <= propa.dx) + propmode += 1; // Diff + else if (prop.dist > propa.dx) + propmode += 2; // Tropo + } + dbloss = avar(ztime, zloc, zconf, prop, propv) + fs; //avar(time,location,confidence) + errnum = prop.kwx; +} + +void point_to_pointDH(double tht_m, double rht_m, double eps_dielect, + double sgm_conductivity, double eno_ns_surfref, + double enc_ncc_clcref, double clutter_height, + double clutter_density, double delta_h_diff, + double frq_mhz, int radio_climate, int pol, double conf, + double rel, double loc, double &dbloss, double &deltaH, + int &errnum) +/************************************************************************************************* + pol: 0-Horizontal, 1-Vertical + radio_climate: 1-Equatorial, 2-Continental Subtropical, 3-Maritime Tropical, + 4-Desert, 5-Continental Temperate, 6-Maritime Temperate, Over Land, + 7-Maritime Temperate, Over Sea + conf, rel: .01 to .99 + elev[]: [num points - 1], [delta dist(meters)], [height(meters) point 1], ..., [height(meters) point n] + errnum: 0- No Error. + 1- Warning: Some parameters are nearly out of range. + Results should be used with caution. + 2- Note: Default parameters have been substituted for impossible ones. + 3- Warning: A combination of parameters is out of range. + Results are probably invalid. + Other- Warning: Some parameters are out of range. + Results are probably invalid. +*************************************************************************************************/ +{ + + char strmode[100]; + prop_type prop; + propv_type propv; + propa_type propa; + double zsys = 0; + double zc, zr; + double eno, enso, q; + long ja, jb, i, np; + /* double dkm, xkm; */ + double fs; + + prop.hg[0] = tht_m; + prop.hg[1] = rht_m; + propv.klim = radio_climate; + prop.encc = enc_ncc_clcref; + prop.cch = clutter_height; + prop.cd = clutter_density; + prop.dhd = delta_h_diff; + prop.kwx = 0; + propv.lvar = 5; + prop.mdp = -1; + prop.ptx = pol; + prop.thera = 0.0; + prop.thenr = 0.0; + zc = qerfi(conf); + zr = qerfi(rel); + np = (long)elev[0]; + /* dkm = (elev[1] * elev[0]) / 1000.0; */ + /* xkm = elev[1] / 1000.0; */ + eno = eno_ns_surfref; + enso = 0.0; + q = enso; + + /* PRESET VALUES for Basic Version w/o additional inputs active */ + + prop.encc = 1000.00; /* double enc_ncc_clcref */ + prop.cch = 22.5; /* double clutter_height */ + prop.cd = 1.00; /* double clutter_density */ + + if (q <= 0.0) { + ja = (long)(3.0 + 0.1 * elev[0]); /* to match KD2BD addition of (long) */ + jb = np - ja + 6; + for (i = ja - 1; i < jb; ++i) + zsys += elev[i]; + zsys /= (jb - ja + 1); + q = eno; + } + propv.mdvar = 12; + qlrps(frq_mhz, zsys, q, pol, eps_dielect, sgm_conductivity, prop); + qlrpfl2(elev, propv.klim, propv.mdvar, prop, propa, propv); + fs = 32.45 + 20.0 * log10(frq_mhz) + 20.0 * log10(prop.dist / 1000.0); + deltaH = prop.dh; + q = prop.dist - propa.dla; + if (int (q) < 0.0) + strcpy(strmode, "Line-Of-Sight Mode"); + else { + if (int (q) == 0.0) + strcpy(strmode, "Single Horizon"); + else if (int (q) > 0.0) + strcpy(strmode, "Double Horizon"); + if (prop.dist <= propa.dlsa || prop.dist <= propa.dx) + strcat(strmode, ", Diffraction Dominant"); + else if (prop.dist > propa.dx) + strcat(strmode, ", Troposcatter Dominant"); + } + dbloss = avar(zr, 0.0, zc, prop, propv) + fs; //avar(time,location,confidence) + errnum = prop.kwx; +} + +//******************************************************** +//* Area Mode Calculations * +//******************************************************** + +void area(long ModVar, double deltaH, double tht_m, double rht_m, + double dist_km, int TSiteCriteria, int RSiteCriteria, + double eps_dielect, double sgm_conductivity, double eno_ns_surfref, + double enc_ncc_clcref, double clutter_height, double clutter_density, + double delta_h_diff, double frq_mhz, int radio_climate, int pol, + int mode_var, double pctTime, double pctLoc, double pctConf, + double &dbloss, char *strmode, int &errnum) +{ + // pol: 0-Horizontal, 1-Vertical + // TSiteCriteria, RSiteCriteria: + // 0 - random, 1 - careful, 2 - very careful + + // radio_climate: 1-Equatorial, 2-Continental Subtropical, 3-Maritime Tropical, + // 4-Desert, 5-Continental Temperate, 6-Maritime Temperate, Over Land, + // 7-Maritime Temperate, Over Sea + // ModVar: 0 - Single: pctConf is "Time/Situation/Location", pctTime, pctLoc not used + // 1 - Individual: pctTime is "Situation/Location", pctConf is "Confidence", pctLoc not used + // 2 - Mobile: pctTime is "Time/Locations (Reliability)", pctConf is "Confidence", pctLoc not used + // 3 - Broadcast: pctTime is "Time", pctLoc is "Location", pctConf is "Confidence" + // pctTime, pctLoc, pctConf: .01 to .99 + // errnum: 0- No Error. + // 1- Warning: Some parameters are nearly out of range. + // Results should be used with caution. + // 2- Note: Default parameters have been substituted for impossible ones. + // 3- Warning: A combination of parameters is out of range. + // Results are probably invalid. + // Other- Warning: Some parameters are out of range. + // Results are probably invalid. + // NOTE: strmode is not used at this time. + + prop_type prop; + propv_type propv; + propa_type propa; + double zt, zl, zc, xlb; + double fs; + long ivar; + double eps, eno, sgm; + long ipol; + int kst[2]; + + kst[0] = (int)TSiteCriteria; + kst[1] = (int)RSiteCriteria; + zt = qerfi(pctTime / 100.0); + zl = qerfi(pctLoc / 100.0); + zc = qerfi(pctConf / 100.0); + eps = eps_dielect; + sgm = sgm_conductivity; + eno = eno_ns_surfref; + prop.dh = deltaH; + prop.hg[0] = tht_m; + prop.hg[1] = rht_m; + propv.klim = (long)radio_climate; + prop.encc = enc_ncc_clcref; + prop.cch = clutter_height; + prop.cd = clutter_density; + prop.dhd = delta_h_diff; + prop.ens = eno; + prop.kwx = 0; + ivar = (long)ModVar; + ipol = (long)pol; + qlrps(frq_mhz, 0.0, eno, ipol, eps, sgm, prop); + qlra(kst, propv.klim, ivar, prop, propv); + + if (propv.lvar < 1) + propv.lvar = 1; + + lrprop2(dist_km * 1000.0, prop, propa); + fs = 32.45 + 20.0 * log10(frq_mhz) + 20.0 * log10(prop.dist / 1000.0); + xlb = fs + avar(zt, zl, zc, prop, propv); + dbloss = xlb; + if (prop.kwx == 0) + errnum = 0; + else + errnum = prop.kwx; +} + +double ITMAreadBLoss(long ModVar, double deltaH, double tht_m, double rht_m, + double dist_km, int TSiteCriteria, int RSiteCriteria, + double eps_dielect, double sgm_conductivity, + double eno_ns_surfref, double enc_ncc_clcref, + double clutter_height, double clutter_density, + double delta_h_diff, double frq_mhz, int radio_climate, + int pol, int mode_var, double pctTime, double pctLoc, + double pctConf) +{ + char strmode[200]; + int errnum; + double dbloss; + area(ModVar, deltaH, tht_m, rht_m, dist_km, TSiteCriteria, + RSiteCriteria, eps_dielect, sgm_conductivity, eno_ns_surfref, + enc_ncc_clcref, clutter_height, clutter_density, delta_h_diff, + frq_mhz, radio_climate, pol, mode_var, pctTime, pctLoc, pctConf, + dbloss, strmode, errnum); + return dbloss; +} + +double ITWOMVersion() +{ + return 3.0; +} diff --git a/models/itwom3.0.hh b/models/itwom3.0.hh new file mode 100644 index 0000000..8ef9b8e --- /dev/null +++ b/models/itwom3.0.hh @@ -0,0 +1,14 @@ +#ifndef _ITWOM30_HH_ +#define _ITWOM30_HH_ + +void point_to_point_ITM(double tht_m, double rht_m, double eps_dielect, + double sgm_conductivity, double eno_ns_surfref, + double frq_mhz, int radio_climate, int pol, + double conf, double rel, double &dbloss, char *strmode, + int &errnum); +void point_to_point(double tht_m, double rht_m, double eps_dielect, + double sgm_conductivity, double eno_ns_surfref, + double frq_mhz, int radio_climate, int pol, double conf, + double rel, double &dbloss, char *strmode, int &errnum); + +#endif /* _ITWOM30_HH_ */ diff --git a/models/los.cc b/models/los.cc new file mode 100644 index 0000000..66bdc8a --- /dev/null +++ b/models/los.cc @@ -0,0 +1,864 @@ +#include +#include + +#include "../common.h" +#include "../main.hh" +#include "cost.hh" +#include "ecc33.hh" +#include "ericsson.hh" +#include "fspl.hh" +#include "hata.hh" +#include "itwom3.0.hh" +#include "sui.hh" + +/* + * Acute Angle from Rx point to an obstacle of height (opp) and + * distance (adj) + */ +static double incidenceAngle(double opp, double adj) +{ + return atan2(opp, adj) * 180 / PI; +} + +/* + * Knife edge diffraction: + * This is based upon a recognised formula like Huygens, but trades + * thoroughness for increased speed which adds a proportional diffraction + * effect to obstacles. + */ +static double ked(double freq, double rxh, double dkm) +{ + double obh, obd, rxobaoi = 0, d, dipheight = 25; + + obh = 0; // Obstacle height + obd = 0; // Obstacle distance + + dkm = dkm * 1000; // KM to metres + + // walk along path + for (int n = 2; n < (dkm / elev[1]); n++) { + + d = (n - 2) * elev[1]; // no of points * delta = km + + //Find dip(s) + if (elev[n] < (obh + dipheight)) { + + // Angle from Rx point to obstacle + rxobaoi = + incidenceAngle((obh - (elev[n] + rxh)), d - obd); + } else { + // Line of sight or higher + rxobaoi = 0; + } + + //note the highest point + if (elev[n] > obh) { + obh = elev[n]; + obd = d; + } + + } + + if (rxobaoi >= 0) { + return rxobaoi / (300 / freq); // Diffraction angle divided by wavelength (m) + } else { + return 0; + } +} + +void PlotLOSPath(struct site source, struct site destination, char mask_value, + FILE *fd) +{ + /* This function analyzes the path between the source and + destination locations. It determines which points along + the path have line-of-sight visibility to the source. + Points along with path having line-of-sight visibility + to the source at an AGL altitude equal to that of the + destination location are stored by setting bit 1 in the + mask[][] array, which are displayed in green when PPM + maps are later generated by ss. */ + + char block; + int x, y; + register double cos_xmtr_angle, cos_test_angle, test_alt; + double distance, rx_alt, tx_alt; + + ReadPath(source, destination); + + for (y = 0; y < path.length; y++) { + /* Test this point only if it hasn't been already + tested and found to be free of obstructions. */ + + if ((GetMask(path.lat[y], path.lon[y]) & mask_value) == 0) { + distance = 5280.0 * path.distance[y]; + tx_alt = earthradius + source.alt + path.elevation[0]; + rx_alt = + earthradius + destination.alt + path.elevation[y]; + + /* Calculate the cosine of the elevation of the + transmitter as seen at the temp rx point. */ + + cos_xmtr_angle = + ((rx_alt * rx_alt) + (distance * distance) - + (tx_alt * tx_alt)) / (2.0 * rx_alt * distance); + + for (x = y, block = 0; x >= 0 && block == 0; x--) { + distance = + 5280.0 * (path.distance[y] - + path.distance[x]); + test_alt = + earthradius + (path.elevation[x] == + 0.0 ? path. + elevation[x] : path. + elevation[x] + clutter); + + 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 + 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 + be if the actual angles were compared. */ + + if (cos_xmtr_angle >= cos_test_angle) + block = 1; + } + + if (block == 0) + OrMask(path.lat[y], path.lon[y], mask_value); + } + } +} + +void PlotPropPath(struct site source, struct site destination, + unsigned char mask_value, FILE * fd, int propmodel, + int knifeedge, int pmenv) +{ + + int x, y, ifs, ofs, errnum; + char block = 0, strmode[100]; + double loss, azimuth, pattern = 0.0, + xmtr_alt, dest_alt, xmtr_alt2, dest_alt2, + cos_rcvr_angle, cos_test_angle = 0.0, test_alt, + elevation = 0.0, distance = 0.0, four_thirds_earth, + field_strength = 0.0, rxp, dBm, txelev, dkm, diffloss; + struct site temp; + + ReadPath(source, destination); + + four_thirds_earth = FOUR_THIRDS * EARTHRADIUS; + + for (x = 1; x < path.length - 1; x++) + elev[x + 2] = + (path.elevation[x] == + 0.0 ? path.elevation[x] * METERS_PER_FOOT : (clutter + + path. + elevation[x]) + * METERS_PER_FOOT); + + /* Copy ending points without clutter */ + + elev[2] = path.elevation[0] * METERS_PER_FOOT; + txelev = elev[2] + (source.alt * METERS_PER_FOOT); + + elev[path.length + 1] = + path.elevation[path.length - 1] * METERS_PER_FOOT; + + /* Since the only energy the Longley-Rice model considers + reaching the destination is based on what is scattered + or deflected from the first obstruction along the path, + we first need to find the location and elevation angle + of that first obstruction (if it exists). This is done + using a 4/3rds Earth radius to match the model used by + Longley-Rice. This information is required for properly + integrating the antenna's elevation pattern into the + calculation for overall path loss. */ + + for (y = 2; (y < (path.length - 1) && path.distance[y] <= max_range); + y++) { + /* Process this point only if it + has not already been processed. */ + + if ((GetMask(path.lat[y], path.lon[y]) & 248) != + (mask_value << 3)) { + distance = 5280.0 * path.distance[y]; + xmtr_alt = + four_thirds_earth + source.alt + path.elevation[0]; + dest_alt = + four_thirds_earth + destination.alt + + path.elevation[y]; + dest_alt2 = dest_alt * dest_alt; + xmtr_alt2 = xmtr_alt * xmtr_alt; + + /* Calculate the cosine of the elevation of + the receiver as seen by the transmitter. */ + + cos_rcvr_angle = + ((xmtr_alt2) + (distance * distance) - + (dest_alt2)) / (2.0 * xmtr_alt * distance); + + if (cos_rcvr_angle > 1.0) + cos_rcvr_angle = 1.0; + + if (cos_rcvr_angle < -1.0) + cos_rcvr_angle = -1.0; + + if (got_elevation_pattern || fd != NULL) { + /* Determine the elevation angle to the first obstruction + along the path IF elevation pattern data is available + or an output (.ano) file has been designated. */ + + for (x = 2, block = 0; (x < y && block == 0); + x++) { + distance = 5280.0 * path.distance[x]; + + test_alt = + four_thirds_earth + + (path.elevation[x] == + 0.0 ? path.elevation[x] : path. + elevation[x] + clutter); + + /* Calculate the cosine of the elevation + angle of the terrain (test point) + as seen by the transmitter. */ + + cos_test_angle = + ((xmtr_alt2) + + (distance * distance) - + (test_alt * test_alt)) / (2.0 * + xmtr_alt + * + distance); + + if (cos_test_angle > 1.0) + cos_test_angle = 1.0; + + if (cos_test_angle < -1.0) + cos_test_angle = -1.0; + + /* Compare these two angles to determine if + an obstruction exists. Since we're comparing + the cosines of these angles rather than + the angles themselves, the sense of the + following "if" statement is reversed from + what it would be if the angles themselves + were compared. */ + + if (cos_rcvr_angle >= cos_test_angle) + block = 1; + } + + if (block) + elevation = + ((acos(cos_test_angle)) / DEG2RAD) - + 90.0; + else + elevation = + ((acos(cos_rcvr_angle)) / DEG2RAD) - + 90.0; + } + + /* Determine attenuation for each point along the + path using a prop model starting at y=2 (number_of_points = 1), the + shortest distance terrain can play a role in + path loss. */ + + elev[0] = y - 1; /* (number of points - 1) */ + + /* Distance between elevation samples */ + + elev[1] = + METERS_PER_MILE * (path.distance[y] - + path.distance[y - 1]); + + if (path.elevation[y] < 1) { + path.elevation[y] = 1; + } + + dkm = (elev[1] * elev[0]) / 1000; // km + + switch (propmodel) { + case 1: + // Longley Rice ITM + point_to_point_ITM(source.alt * METERS_PER_FOOT, + destination.alt * + METERS_PER_FOOT, + LR.eps_dielect, + LR.sgm_conductivity, + LR.eno_ns_surfref, + LR.frq_mhz, LR.radio_climate, + LR.pol, LR.conf, LR.rel, + loss, strmode, errnum); + break; + case 3: + //HATA 1, 2 & 3 + loss = + HATApathLoss(LR.frq_mhz, txelev, + path.elevation[y] + + (destination.alt * + METERS_PER_FOOT), dkm, pmenv); + break; + case 4: + // COST231-HATA + loss = + ECC33pathLoss(LR.frq_mhz, txelev, + path.elevation[y] + + (destination.alt * + METERS_PER_FOOT), dkm, + pmenv); + break; + case 5: + // SUI + loss = + SUIpathLoss(LR.frq_mhz, txelev, + path.elevation[y] + + (destination.alt * + METERS_PER_FOOT), dkm, pmenv); + break; + case 6: + loss = + COST231pathLoss(LR.frq_mhz, txelev, + path.elevation[y] + + (destination.alt * + METERS_PER_FOOT), dkm, + pmenv); + break; + case 7: + // ITU-R P.525 Free space path loss + loss = FSPLpathLoss(LR.frq_mhz, dkm); + break; + case 8: + // ITWOM 3.0 + point_to_point(source.alt * METERS_PER_FOOT, + destination.alt * + METERS_PER_FOOT, LR.eps_dielect, + LR.sgm_conductivity, + LR.eno_ns_surfref, LR.frq_mhz, + LR.radio_climate, LR.pol, + LR.conf, LR.rel, loss, strmode, + errnum); + break; + case 9: + // Ericsson + loss = + EricssonpathLoss(LR.frq_mhz, txelev, + path.elevation[y] + + (destination.alt * + METERS_PER_FOOT), dkm, + pmenv); + break; + + default: + point_to_point_ITM(source.alt * METERS_PER_FOOT, + destination.alt * + METERS_PER_FOOT, + LR.eps_dielect, + LR.sgm_conductivity, + LR.eno_ns_surfref, + LR.frq_mhz, LR.radio_climate, + LR.pol, LR.conf, LR.rel, + loss, strmode, errnum); + + } + + if (knifeedge == 1) { + diffloss = + ked(LR.frq_mhz, + destination.alt * METERS_PER_FOOT, dkm); + loss += (diffloss); // ;) + } + //Key stage. Link dB for p2p is returned as 'loss'. + + temp.lat = path.lat[y]; + temp.lon = path.lon[y]; + + azimuth = (Azimuth(source, temp)); + + if (fd != NULL) + fprintf(fd, "%.7f, %.7f, %.3f, %.3f, ", + path.lat[y], path.lon[y], azimuth, + elevation); + + /* If ERP==0, write path loss to alphanumeric + output file. Otherwise, write field strength + or received power level (below), as appropriate. */ + + if (fd != NULL && LR.erp == 0.0) + fprintf(fd, "%.2f", loss); + + /* Integrate the antenna's radiation + pattern into the overall path loss. */ + + x = (int)rint(10.0 * (10.0 - elevation)); + + if (x >= 0 && x <= 1000) { + azimuth = rint(azimuth); + + pattern = + (double)LR.antenna_pattern[(int)azimuth][x]; + + if (pattern != 0.0) { + pattern = 20.0 * log10(pattern); + loss -= pattern; + } + } + + if (LR.erp != 0.0) { + if (dbm) { + /* dBm is based on EIRP (ERP + 2.14) */ + + rxp = + LR.erp / + (pow(10.0, (loss - 2.14) / 10.0)); + + dBm = 10.0 * (log10(rxp * 1000.0)); + + if (fd != NULL) + fprintf(fd, "%.3f", dBm); + + /* Scale roughly between 0 and 255 */ + + ifs = 200 + (int)rint(dBm); + + if (ifs < 0) + ifs = 0; + + if (ifs > 255) + ifs = 255; + + ofs = + GetSignal(path.lat[y], path.lon[y]); + + if (ofs > ifs) + ifs = ofs; + + PutSignal(path.lat[y], path.lon[y], + (unsigned char)ifs); + + } + + else { + field_strength = + (139.4 + + (20.0 * log10(LR.frq_mhz)) - + loss) + + (10.0 * log10(LR.erp / 1000.0)); + + ifs = 100 + (int)rint(field_strength); + + if (ifs < 0) + ifs = 0; + + if (ifs > 255) + ifs = 255; + + ofs = + GetSignal(path.lat[y], path.lon[y]); + + if (ofs > ifs) + ifs = ofs; + + PutSignal(path.lat[y], path.lon[y], + (unsigned char)ifs); + + if (fd != NULL) + fprintf(fd, "%.3f", + field_strength); + } + } + + else { + if (loss > 255) + ifs = 255; + else + ifs = (int)rint(loss); + + ofs = GetSignal(path.lat[y], path.lon[y]); + + if (ofs < ifs && ofs != 0) + ifs = ofs; + + PutSignal(path.lat[y], path.lon[y], + (unsigned char)ifs); + } + + if (fd != NULL) { + if (block) + fprintf(fd, " *"); + + fprintf(fd, "\n"); + } + + /* Mark this point as having been analyzed */ + + PutMask(path.lat[y], path.lon[y], + (GetMask(path.lat[y], path.lon[y]) & 7) + + (mask_value << 3)); + } + } + +} + +void PlotLOSMap(struct site source, double altitude, char *plo_filename) +{ + /* This function performs a 360 degree sweep around the + transmitter site (source location), and plots the + line-of-sight coverage of the transmitter on the ss + generated topographic map based on a receiver located + at the specified altitude (in feet AGL). Results + are stored in memory, and written out in the form + of a topographic map when the WritePPM() function + is later invoked. */ + + int x, y, z; + struct site edge; + double lat, lon, minwest, maxnorth, th; + static unsigned char mask_value = 1; + FILE *fd = NULL; + + if (plo_filename[0] != 0) + fd = fopen(plo_filename, "wb"); + + if (fd != NULL) { + fprintf(fd, + "%d, %d\t; max_west, min_west\n%d, %d\t; max_north, min_north\n", + max_west, min_west, max_north, min_north); + } + + th = ppd / loops; + + z = (int)(th * ReduceAngle(max_west - min_west)); + + minwest = dpp + (double)min_west; + maxnorth = (double)max_north - dpp; + + for (lon = minwest, x = 0, y = 0; + (LonDiff(lon, (double)max_west) <= 0.0); + y++, lon = minwest + (dpp * (double)y)) { + if (lon >= 360.0) + lon -= 360.0; + + edge.lat = max_north; + edge.lon = lon; + edge.alt = altitude; + + PlotLOSPath(source, edge, mask_value, fd); + } + + z = (int)(th * (double)(max_north - min_north)); + + for (lat = maxnorth, x = 0, y = 0; lat >= (double)min_north; + y++, lat = maxnorth - (dpp * (double)y)) { + edge.lat = lat; + edge.lon = min_west; + edge.alt = altitude; + + PlotLOSPath(source, edge, mask_value, fd); + + } + + z = (int)(th * ReduceAngle(max_west - min_west)); + + for (lon = minwest, x = 0, y = 0; + (LonDiff(lon, (double)max_west) <= 0.0); + y++, lon = minwest + (dpp * (double)y)) { + if (lon >= 360.0) + lon -= 360.0; + + edge.lat = min_north; + edge.lon = lon; + edge.alt = altitude; + + PlotLOSPath(source, edge, mask_value, fd); + + } + + z = (int)(th * (double)(max_north - min_north)); + + for (lat = (double)min_north, x = 0, y = 0; lat < (double)max_north; + y++, lat = (double)min_north + (dpp * (double)y)) { + edge.lat = lat; + edge.lon = max_west; + edge.alt = altitude; + + PlotLOSPath(source, edge, mask_value, fd); + + } + + switch (mask_value) { + case 1: + mask_value = 8; + break; + + case 8: + mask_value = 16; + break; + + case 16: + mask_value = 32; + } +} + +void PlotPropagation(struct site source, double altitude, char *plo_filename, + int propmodel, int knifeedge, int haf, int pmenv) +{ + int y, z, count; + struct site edge; + double lat, lon, minwest, maxnorth, th; + unsigned char x; + static unsigned char mask_value = 1; + FILE *fd = NULL; + + minwest = dpp + (double)min_west; + maxnorth = (double)max_north - dpp; + + count = 0; + + if (LR.erp == 0.0 && debug) + fprintf(stdout, "path loss"); + else { + if (debug) { + if (dbm) + fprintf(stdout, "signal power level"); + else + fprintf(stdout, "field strength"); + } + } + if (debug) { + fprintf(stdout, + " contours of \"%s\"\nout to a radius of %.2f %s with Rx antenna(s) at %.2f %s AGL\n", + source.name, + metric ? max_range * KM_PER_MILE : max_range, + metric ? "kilometers" : "miles", + metric ? altitude * METERS_PER_FOOT : altitude, + metric ? "meters" : "feet"); + } + + if (clutter > 0.0 && debug) + fprintf(stdout, "\nand %.2f %s of ground clutter", + metric ? clutter * METERS_PER_FOOT : clutter, + metric ? "meters" : "feet"); + + if (debug) { + fprintf(stdout, "...\n\n 0%c to 25%c ", 37, 37); + fflush(stdout); + } + + if (plo_filename[0] != 0) + fd = fopen(plo_filename, "wb"); + + if (fd != NULL) { + fprintf(fd, + "%d, %d\t; max_west, min_west\n%d, %d\t; max_north, min_north\n", + max_west, min_west, max_north, min_north); + } + + th = ppd / loops; + + // Four sections start here + + //S1 + if (haf == 0 || haf == 1) { + z = (int)(th * ReduceAngle(max_west - min_west)); + + for (lon = minwest, x = 0, y = 0; + (LonDiff(lon, (double)max_west) <= 0.0); + y++, lon = minwest + (dpp * (double)y)) { + if (lon >= 360.0) + lon -= 360.0; + + edge.lat = max_north; + edge.lon = lon; + edge.alt = altitude; + + PlotPropPath(source, edge, mask_value, fd, propmodel, + knifeedge, pmenv); + count++; + + if (count == z) { + count = 0; + + if (x == 3) + x = 0; + else + x++; + } + } + + } + //S2 + if (haf == 0 || haf == 1) { + count = 0; + if (debug) { + fprintf(stdout, "\n25%c to 50%c ", 37, 37); + fflush(stdout); + } + + z = (int)(th * (double)(max_north - min_north)); + + for (lat = maxnorth, x = 0, y = 0; lat >= (double)min_north; + y++, lat = maxnorth - (dpp * (double)y)) { + edge.lat = lat; + edge.lon = min_west; + edge.alt = altitude; + + PlotPropPath(source, edge, mask_value, fd, propmodel, + knifeedge, pmenv); + count++; + + if (count == z) { + count = 0; + + if (x == 3) + x = 0; + else + x++; + } + } + + } + //S3 + if (haf == 0 || haf == 2) { + count = 0; + if (debug) { + fprintf(stdout, "\n50%c to 75%c ", 37, 37); + fflush(stdout); + } + + z = (int)(th * ReduceAngle(max_west - min_west)); + + for (lon = minwest, x = 0, y = 0; + (LonDiff(lon, (double)max_west) <= 0.0); + y++, lon = minwest + (dpp * (double)y)) { + if (lon >= 360.0) + lon -= 360.0; + + edge.lat = min_north; + edge.lon = lon; + edge.alt = altitude; + + PlotPropPath(source, edge, mask_value, fd, propmodel, + knifeedge, pmenv); + count++; + if (count == z) { + count = 0; + + if (x == 3) + x = 0; + else + x++; + } + + } + + } + //S4 + if (haf == 0 || haf == 2) { + count = 0; + if (debug) { + fprintf(stdout, "\n75%c to 100%c ", 37, 37); + fflush(stdout); + } + z = (int)(th * (double)(max_north - min_north)); + + for (lat = (double)min_north, x = 0, y = 0; + lat < (double)max_north; + y++, lat = (double)min_north + (dpp * (double)y)) { + edge.lat = lat; + edge.lon = max_west; + edge.alt = altitude; + + PlotPropPath(source, edge, mask_value, fd, propmodel, + knifeedge, pmenv); + count++; + + if (count == z) { + + count = 0; + + if (x == 3) + x = 0; + else + x++; + } + } + + } //S4 + + if (fd != NULL) + fclose(fd); + + if (mask_value < 30) + mask_value++; +} + +void PlotPath(struct site source, struct site destination, char mask_value) +{ + /* This function analyzes the path between the source and + destination locations. It determines which points along + the path have line-of-sight visibility to the source. + Points along with path having line-of-sight visibility + to the source at an AGL altitude equal to that of the + destination location are stored by setting bit 1 in the + mask[][] array, which are displayed in green when PPM + maps are later generated by SPLAT!. */ + + char block; + int x, y; + register double cos_xmtr_angle, cos_test_angle, test_alt; + double distance, rx_alt, tx_alt; + + ReadPath(source, destination); + + for (y = 0; y < path.length; y++) { + /* Test this point only if it hasn't been already + tested and found to be free of obstructions. */ + + if ((GetMask(path.lat[y], path.lon[y]) & mask_value) == 0) { + distance = 5280.0 * path.distance[y]; + tx_alt = earthradius + source.alt + path.elevation[0]; + rx_alt = + earthradius + destination.alt + path.elevation[y]; + + /* Calculate the cosine of the elevation of the + transmitter as seen at the temp rx point. */ + + cos_xmtr_angle = + ((rx_alt * rx_alt) + (distance * distance) - + (tx_alt * tx_alt)) / (2.0 * rx_alt * distance); + + for (x = y, block = 0; x >= 0 && block == 0; x--) { + distance = + 5280.0 * (path.distance[y] - + path.distance[x]); + test_alt = + earthradius + (path.elevation[x] == + 0.0 ? path. + elevation[x] : path. + elevation[x] + clutter); + + 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 + 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 + be if the actual angles were compared. */ + + if (cos_xmtr_angle >= cos_test_angle) + block = 1; + } + + if (block == 0) + OrMask(path.lat[y], path.lon[y], mask_value); + } + } +} diff --git a/models/los.hh b/models/los.hh new file mode 100644 index 0000000..795ec3f --- /dev/null +++ b/models/los.hh @@ -0,0 +1,18 @@ +#ifndef _LOS_HH_ +#define _LOS_HH_ + +#include + +#include "../common.h" + +void PlotLOSPath(struct site source, struct site destination, char mask_value, + FILE *fd); +void PlotPropPath(struct site source, struct site destination, + unsigned char mask_value, FILE * fd, int propmodel, + int knifeedge, int pmenv); +void PlotLOSMap(struct site source, double altitude, char *plo_filename); +void PlotPropagation(struct site source, double altitude, char *plo_filename, + int propmodel, int knifeedge, int haf, int pmenv); +void PlotPath(struct site source, struct site destination, char mask_value); + +#endif /* _LOS_HH_ */ diff --git a/models/sui.cc b/models/sui.cc new file mode 100644 index 0000000..3d2b3c2 --- /dev/null +++ b/models/sui.cc @@ -0,0 +1,49 @@ +#include +#include +#include + +double SUIpathLoss(float f, float TxH, float RxH, float d, int mode) +{ + /* + f = Frequency (MHz) + TxH = Transmitter height (m) + RxH = Receiver height (m) + d = distance (km) + mode 1 = Hilly + trees + mode 2 = Flat + trees OR hilly + light foliage + mode 3 = Flat + light foliage + http://www.cl.cam.ac.uk/research/dtg/lce-pub/public/vsa23/VTC05_Empirical.pdf + */ + d = d * 1000; // km to m + if (f < 1900 || f > 11000) { + printf("Error: SUI model frequency range 1.9-11GHz\n"); + exit(EXIT_FAILURE); + } + // Terrain mode A is default + double a = 4.6; + double b = 0.0075; + double c = 12.6; + double s = 10.6; // Optional fading value + int XhCF = -10.8; + + if (mode == 2) { + a = 4.0; + b = 0.0065; + c = 17.1; + s = 6; // average + } + if (mode == 3) { + a = 3.6; + b = 0.005; + c = 20; + s = 3; // Optimistic + XhCF = -20; + } + double d0 = 100; + double A = 20 * log10((4 * M_PI * d0) / (300 / f)); + double y = (a - b * TxH) + c / TxH; + double Xf = 6 * log10(f / 2000); + double Xh = XhCF * log10(RxH / 2); + + return A + (10 * y * log10(d / d0)) + Xf + Xh + s; +} diff --git a/models/sui.hh b/models/sui.hh new file mode 100644 index 0000000..e9bedab --- /dev/null +++ b/models/sui.hh @@ -0,0 +1,6 @@ +#ifndef _SUI_HH_ +#define _SUI_HH_ + +double SUIpathLoss(float f, float TxH, float RxH, float d, int mode); + +#endif /* _SUI_HH_ */ diff --git a/outputs.cc b/outputs.cc new file mode 100644 index 0000000..44dba1d --- /dev/null +++ b/outputs.cc @@ -0,0 +1,1994 @@ +#include +#include +#include +#include +#include + +#include "common.h" +#include "main.hh" +#include "inputs.hh" +#include "models/cost.hh" +#include "models/ecc33.hh" +#include "models/ericsson.hh" +#include "models/fspl.hh" +#include "models/hata.hh" +#include "models/itwom3.0.hh" +#include "models/sui.hh" + +void DoPathLoss(char *filename, unsigned char geo, unsigned char kml, + unsigned char ngs, struct site *xmtr, unsigned char txsites) +{ + /* This function generates a topographic map in Portable Pix Map + (PPM) format based on the content of flags held in the mask[][] + array (only). The image created is rotated counter-clockwise + 90 degrees from its representation in dem[][] so that north + points up and east points right in the image generated. */ + + char mapfile[255]; + unsigned width, height, red, green, blue, terrain = 0; + unsigned char found, mask, cityorcounty; + int indx, x, y, z, x0 = 0, y0 = 0, loss, match; + double lat, lon, conversion, one_over_gamma, minwest; + FILE *fd; + + one_over_gamma = 1.0 / GAMMA; + conversion = + 255.0 / pow((double)(max_elevation - min_elevation), + one_over_gamma); + + width = (unsigned)(ippd * ReduceAngle(max_west - min_west)); + height = (unsigned)(ippd * ReduceAngle(max_north - min_north)); + + LoadLossColors(xmtr[0]); + + if (filename[0] == 0) { + strncpy(filename, xmtr[0].filename, 254); + filename[strlen(filename) - 4] = 0; /* Remove .qth */ + } + + y = strlen(filename); + + if (y > 4) { + if (filename[y - 1] == 'm' && filename[y - 2] == 'p' + && filename[y - 3] == 'p' && filename[y - 4] == '.') + y -= 4; + } + + for (x = 0; x < y; x++) { + mapfile[x] = filename[x]; + } + + mapfile[x] = '.'; + mapfile[x + 1] = 'p'; + mapfile[x + 2] = 'p'; + mapfile[x + 3] = 'm'; + mapfile[x + 4] = 0; + + minwest = ((double)min_west) + dpp; + + if (minwest > 360.0) + minwest -= 360.0; + + north = (double)max_north - dpp; + + if (kml || geo) + south = (double)min_north; /* No bottom legend */ + else + south = (double)min_north - (30.0 / ppd); /* 30 pixels for bottom legend */ + + east = (minwest < 180.0 ? -minwest : 360.0 - min_west); + west = (double)(max_west < 180 ? -max_west : 360 - max_west); + + fd = fopen(mapfile, "wb"); + + fprintf(fd, "P6\n%u %u\n255\n", width, (kml ? height : height + 30)); + if (debug) { + fprintf(stdout, "\nWriting \"%s\" (%ux%u pixmap image)... ", + mapfile, width, (kml ? height : height + 30)); + fflush(stdout); + } + for (y = 0, lat = north; y < (int)height; + y++, lat = north - (dpp * (double)y)) { + for (x = 0, lon = max_west; x < (int)width; + x++, lon = max_west - (dpp * (double)x)) { + if (lon < 0.0) + lon += 360.0; + + for (indx = 0, found = 0; + indx < MAXPAGES && found == 0;) { + x0 = (int)rint(ppd * + (lat - + (double)dem[indx].min_north)); + y0 = mpi - + (int)rint(ppd * + (LonDiff + ((double)dem[indx].max_west, + lon))); + + if (x0 >= 0 && x0 <= mpi && y0 >= 0 + && y0 <= mpi) + found = 1; + else + indx++; + } + + if (found) { + mask = dem[indx].mask[x0][y0]; + loss = (dem[indx].signal[x0][y0]); + cityorcounty = 0; + + if (debug) { + fprintf(stdout, "\n%d\t%d\t%d\t%d", + loss, indx, x0, y0); + fflush(stdout); + } + match = 255; + + red = 0; + green = 0; + blue = 0; + + if (loss <= region.level[0]) + match = 0; + else { + for (z = 1; + (z < region.levels + && match == 255); z++) { + if (loss >= region.level[z - 1] + && loss < region.level[z]) + match = z; + } + } + + if (match < region.levels) { + red = region.color[match][0]; + green = region.color[match][1]; + blue = region.color[match][2]; + } + + if (mask & 2) { + /* Text Labels: Red or otherwise */ + + if (red >= 180 && green <= 75 + && blue <= 75 && loss == 0) + fprintf(fd, "%c%c%c", 255 ^ red, + 255 ^ green, + 255 ^ blue); + 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); + + cityorcounty = 1; + } + + if (cityorcounty == 0) { + if (loss == 0 + || (contour_threshold != 0 + && loss > + abs(contour_threshold))) { + if (ngs) /* No terrain */ + fprintf(fd, "%c%c%c", + 255, 255, 255); + else { + /* Display land or sea elevation */ + + if (dem[indx]. + data[x0][y0] == 0) + fprintf(fd, + "%c%c%c", + 0, 0, + 170); + else { + terrain = + (unsigned) + (0.5 + + pow((double)(dem[indx].data[x0][y0] - min_elevation), one_over_gamma) * conversion); + fprintf(fd, + "%c%c%c", + terrain, + terrain, + terrain); + } + } + } + + else { + /* Plot path loss in color */ + + if (red != 0 || green != 0 + || blue != 0) + fprintf(fd, "%c%c%c", + red, green, + blue); + + else { /* terrain / sea-level */ + + if (dem[indx]. + data[x0][y0] == 0) + fprintf(fd, + "%c%c%c", + 0, 0, + 170); + else { + /* Elevation: Greyscale */ + terrain = + (unsigned) + (0.5 + + pow((double)(dem[indx].data[x0][y0] - min_elevation), one_over_gamma) * conversion); + fprintf(fd, + "%c%c%c", + terrain, + terrain, + terrain); + } + } + } + } + } + + else { + /* We should never get here, but if */ + /* we do, display the region as black */ + + fprintf(fd, "%c%c%c", 0, 0, 0); + } + } + } + + fclose(fd); + +} + +void DoSigStr(char *filename, unsigned char geo, unsigned char kml, + unsigned char ngs, struct site *xmtr, unsigned char txsites) +{ + /* This function generates a topographic map in Portable Pix Map + (PPM) format based on the signal strength values held in the + signal[][] array. The image created is rotated counter-clockwise + 90 degrees from its representation in dem[][] so that north + points up and east points right in the image generated. */ + + char mapfile[255]; + unsigned width, height, terrain, red, green, blue; + unsigned char found, mask, cityorcounty; + int indx, x, y, z = 1, x0 = 0, y0 = 0, signal, match; + double conversion, one_over_gamma, lat, lon, minwest; + FILE *fd; + + one_over_gamma = 1.0 / GAMMA; + conversion = + 255.0 / pow((double)(max_elevation - min_elevation), + one_over_gamma); + + width = (unsigned)(ippd * ReduceAngle(max_west - min_west)); + height = (unsigned)(ippd * ReduceAngle(max_north - min_north)); + + LoadSignalColors(xmtr[0]); + + if (filename[0] == 0) { + strncpy(filename, xmtr[0].filename, 254); + filename[strlen(filename) - 4] = 0; /* Remove .qth */ + } + + y = strlen(filename); + + if (y > 4) { + if (filename[y - 1] == 'm' && filename[y - 2] == 'p' + && filename[y - 3] == 'p' && filename[y - 4] == '.') + y -= 4; + } + + for (x = 0; x < y; x++) { + mapfile[x] = filename[x]; + } + + mapfile[x] = '.'; + mapfile[x + 1] = 'p'; + mapfile[x + 2] = 'p'; + mapfile[x + 3] = 'm'; + mapfile[x + 4] = 0; + + minwest = ((double)min_west) + dpp; + + if (minwest > 360.0) + minwest -= 360.0; + + north = (double)max_north - dpp; + + south = (double)min_north; /* No bottom legend */ + + east = (minwest < 180.0 ? -minwest : 360.0 - min_west); + west = (double)(max_west < 180 ? -max_west : 360 - max_west); + fd = fopen(mapfile, "wb"); + + fprintf(fd, "P6\n%u %u\n255\n", width, (kml ? height : height + 30)); + if (debug) { + fprintf(stdout, "\nWriting \"%s\" (%ux%u pixmap image)... ", + mapfile, width, (kml ? height : height + 30)); + fflush(stdout); + } + for (y = 0, lat = north; y < (int)height; + y++, lat = north - (dpp * (double)y)) { + for (x = 0, lon = max_west; x < (int)width; + x++, lon = max_west - (dpp * (double)x)) { + if (lon < 0.0) + lon += 360.0; + + for (indx = 0, found = 0; + indx < MAXPAGES && found == 0;) { + x0 = (int)rint(ppd * + (lat - + (double)dem[indx].min_north)); + y0 = mpi - + (int)rint(ppd * + (LonDiff + ((double)dem[indx].max_west, + lon))); + + if (x0 >= 0 && x0 <= mpi && y0 >= 0 + && y0 <= mpi) + found = 1; + else + indx++; + } + + if (found) { + mask = dem[indx].mask[x0][y0]; + signal = (dem[indx].signal[x0][y0]) - 100; + cityorcounty = 0; + + if (debug) { + fprintf(stdout, "\n%d\t%d\t%d\t%d", + signal, indx, x0, y0); + fflush(stdout); + } + + match = 255; + + red = 0; + green = 0; + blue = 0; + + if (signal >= region.level[0]) + match = 0; + else { + for (z = 1; + (z < region.levels + && match == 255); z++) { + if (signal < region.level[z - 1] + && signal >= + region.level[z]) + match = z; + } + } + + if (match < region.levels) { + red = region.color[match][0]; + green = region.color[match][1]; + blue = region.color[match][2]; + } + + if (mask & 2) { + /* Text Labels: Red or otherwise */ + + if (red >= 180 && green <= 75 + && blue <= 75) + fprintf(fd, "%c%c%c", 255 ^ red, + 255 ^ green, + 255 ^ blue); + 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); + + cityorcounty = 1; + } + + if (cityorcounty == 0) { + if (contour_threshold != 0 + && signal < contour_threshold) { + if (ngs) + fprintf(fd, "%c%c%c", + 255, 255, 255); + else { + /* Display land or sea elevation */ + + if (dem[indx]. + data[x0][y0] == 0) + fprintf(fd, + "%c%c%c", + 0, 0, + 170); + else { + terrain = + (unsigned) + (0.5 + + pow((double)(dem[indx].data[x0][y0] - min_elevation), one_over_gamma) * conversion); + fprintf(fd, + "%c%c%c", + terrain, + terrain, + terrain); + } + } + } + + else { + /* Plot field strength regions in color */ + + if (red != 0 || green != 0 + || blue != 0) + fprintf(fd, "%c%c%c", + red, green, + blue); + + else { /* terrain / sea-level */ + + if (ngs) + fprintf(fd, + "%c%c%c", + 255, + 255, + 255); + else { + if (dem[indx]. + data[x0][y0] + == 0) + fprintf + (fd, + "%c%c%c", + 0, + 0, + 170); + else { + /* Elevation: Greyscale */ + terrain + = + (unsigned) + (0.5 + + + pow + ((double)(dem[indx].data[x0][y0] - min_elevation), one_over_gamma) * conversion); + fprintf + (fd, + "%c%c%c", + terrain, + terrain, + terrain); + } + } + } + } + } + } + + else { + /* We should never get here, but if */ + /* we do, display the region as black */ + + fprintf(fd, "%c%c%c", 0, 0, 0); + } + } + } + + fclose(fd); + +} + +void DoRxdPwr(char *filename, unsigned char geo, unsigned char kml, + unsigned char ngs, struct site *xmtr, unsigned char txsites) +{ + /* This function generates a topographic map in Portable Pix Map + (PPM) format based on the signal power level values held in the + signal[][] array. The image created is rotated counter-clockwise + 90 degrees from its representation in dem[][] so that north + points up and east points right in the image generated. */ + + char mapfile[255]; + unsigned width, height, terrain, red, green, blue; + unsigned char found, mask, cityorcounty; + int indx, x, y, z = 1, x0 = 0, y0 = 0, dBm, match; + double conversion, one_over_gamma, lat, lon, minwest; + FILE *fd; + + one_over_gamma = 1.0 / GAMMA; + conversion = + 255.0 / pow((double)(max_elevation - min_elevation), + one_over_gamma); + + width = (unsigned)(ippd * ReduceAngle(max_west - min_west)); + height = (unsigned)(ippd * ReduceAngle(max_north - min_north)); + + LoadDBMColors(xmtr[0]); + + if (filename[0] == 0) { + strncpy(filename, xmtr[0].filename, 254); + filename[strlen(filename) - 4] = 0; /* Remove .qth */ + } + + y = strlen(filename); + + if (y > 4) { + if (filename[y - 1] == 'm' && filename[y - 2] == 'p' + && filename[y - 3] == 'p' && filename[y - 4] == '.') + y -= 4; + } + + for (x = 0; x < y; x++) { + mapfile[x] = filename[x]; + } + + mapfile[x] = '.'; + mapfile[x + 1] = 'p'; + mapfile[x + 2] = 'p'; + mapfile[x + 3] = 'm'; + mapfile[x + 4] = 0; + + minwest = ((double)min_west) + dpp; + + if (minwest > 360.0) + minwest -= 360.0; + + north = (double)max_north - dpp; + + south = (double)min_north; /* No bottom legend */ + + east = (minwest < 180.0 ? -minwest : 360.0 - min_west); + west = (double)(max_west < 180 ? -max_west : 360 - max_west); + + fd = fopen(mapfile, "wb"); + + fprintf(fd, "P6\n%u %u\n255\n", width, (kml ? height : height + 30)); + if (debug) { + fprintf(stdout, "\nWriting \"%s\" (%ux%u pixmap image)... ", + mapfile, width, (kml ? height : height + 30)); + fflush(stdout); + } + // WriteKML() + //writeKML(xmtr,filename); + + for (y = 0, lat = north; y < (int)height; + y++, lat = north - (dpp * (double)y)) { + for (x = 0, lon = max_west; x < (int)width; + x++, lon = max_west - (dpp * (double)x)) { + if (lon < 0.0) + lon += 360.0; + + for (indx = 0, found = 0; + indx < MAXPAGES && found == 0;) { + x0 = (int)rint(ppd * + (lat - + (double)dem[indx].min_north)); + y0 = mpi - + (int)rint(ppd * + (LonDiff + ((double)dem[indx].max_west, + lon))); + + if (x0 >= 0 && x0 <= mpi && y0 >= 0 + && y0 <= mpi) + found = 1; + else + indx++; + } + + if (found) { + mask = dem[indx].mask[x0][y0]; + dBm = (dem[indx].signal[x0][y0]) - 200; + cityorcounty = 0; + + if (debug) { + fprintf(stdout, "\n%d\t%d\t%d\t%d", dBm, + indx, x0, y0); + fflush(stdout); + } + + match = 255; + + red = 0; + green = 0; + blue = 0; + + if (dBm >= region.level[0]) + match = 0; + else { + for (z = 1; + (z < region.levels + && match == 255); z++) { + if (dBm < region.level[z - 1] + && dBm >= region.level[z]) + match = z; + } + } + + if (match < region.levels) { + red = region.color[match][0]; + green = region.color[match][1]; + blue = region.color[match][2]; + } + + if (mask & 2) { + /* Text Labels: Red or otherwise */ + + if (red >= 180 && green <= 75 + && blue <= 75 && dBm != 0) + fprintf(fd, "%c%c%c", 255 ^ red, + 255 ^ green, + 255 ^ blue); + 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); + + cityorcounty = 1; + } + + if (cityorcounty == 0) { + if (contour_threshold != 0 + && dBm < contour_threshold) { + if (ngs) /* No terrain */ + fprintf(fd, "%c%c%c", + 255, 255, 255); + else { + /* Display land or sea elevation */ + + if (dem[indx]. + data[x0][y0] == 0) + fprintf(fd, + "%c%c%c", + 0, 0, + 170); + else { + terrain = + (unsigned) + (0.5 + + pow((double)(dem[indx].data[x0][y0] - min_elevation), one_over_gamma) * conversion); + fprintf(fd, + "%c%c%c", + terrain, + terrain, + terrain); + } + } + } + + else { + /* Plot signal power level regions in color */ + + if (red != 0 || green != 0 + || blue != 0) + fprintf(fd, "%c%c%c", + red, green, + blue); + + else { /* terrain / sea-level */ + + if (ngs) + fprintf(fd, + "%c%c%c", + 255, + 255, + 255); + else { + if (dem[indx]. + data[x0][y0] + == 0) + fprintf + (fd, + "%c%c%c", + 0, + 0, + 170); + else { + /* Elevation: Greyscale */ + terrain + = + (unsigned) + (0.5 + + + pow + ((double)(dem[indx].data[x0][y0] - min_elevation), one_over_gamma) * conversion); + fprintf + (fd, + "%c%c%c", + terrain, + terrain, + terrain); + } + } + } + } + } + } + + else { + /* We should never get here, but if */ + /* we do, display the region as black */ + + fprintf(fd, "%c%c%c", 0, 0, 0); + } + } + } + + fclose(fd); + +} + +void DoLOS(char *filename, unsigned char geo, unsigned char kml, + unsigned char ngs, struct site *xmtr, unsigned char txsites) +{ + /* This function generates a topographic map in Portable Pix Map + (PPM) format based on the signal power level values held in the + signal[][] array. The image created is rotated counter-clockwise + 90 degrees from its representation in dem[][] so that north + points up and east points right in the image generated. */ + + char mapfile[255]; + unsigned width, height, terrain; + unsigned char found, mask; + int indx, x, y, x0 = 0, y0 = 0; + double conversion, one_over_gamma, lat, lon, minwest; + FILE *fd; + + one_over_gamma = 1.0 / GAMMA; + conversion = + 255.0 / pow((double)(max_elevation - min_elevation), + one_over_gamma); + + width = (unsigned)(ippd * ReduceAngle(max_west - min_west)); + height = (unsigned)(ippd * ReduceAngle(max_north - min_north)); + + if (filename[0] == 0) { + strncpy(filename, xmtr[0].filename, 254); + filename[strlen(filename) - 4] = 0; /* Remove .qth */ + } + + y = strlen(filename); + + if (y > 4) { + if (filename[y - 1] == 'm' && filename[y - 2] == 'p' + && filename[y - 3] == 'p' && filename[y - 4] == '.') + y -= 4; + } + + for (x = 0; x < y; x++) { + mapfile[x] = filename[x]; + } + + mapfile[x] = '.'; + mapfile[x + 1] = 'p'; + mapfile[x + 2] = 'p'; + mapfile[x + 3] = 'm'; + mapfile[x + 4] = 0; + + minwest = ((double)min_west) + dpp; + + if (minwest > 360.0) + minwest -= 360.0; + + north = (double)max_north - dpp; + + south = (double)min_north; /* No bottom legend */ + + east = (minwest < 180.0 ? -minwest : 360.0 - min_west); + west = (double)(max_west < 180 ? -max_west : 360 - max_west); + + fd = fopen(mapfile, "wb"); + + fprintf(fd, "P6\n%u %u\n255\n", width, (kml ? height : height + 30)); + if (debug) { + fprintf(stdout, "\nWriting \"%s\" (%ux%u pixmap image)... ", + mapfile, width, (kml ? height : height + 30)); + fflush(stdout); + } + // WriteKML() + //writeKML(xmtr,filename); + + for (y = 0, lat = north; y < (int)height; + y++, lat = north - (dpp * (double)y)) { + for (x = 0, lon = max_west; x < (int)width; + x++, lon = max_west - (dpp * (double)x)) { + if (lon < 0.0) + lon += 360.0; + + for (indx = 0, found = 0; + indx < MAXPAGES && found == 0;) { + x0 = (int)rint(ppd * + (lat - + (double)dem[indx].min_north)); + y0 = mpi - + (int)rint(ppd * + (LonDiff + ((double)dem[indx].max_west, + lon))); + + if (x0 >= 0 && x0 <= mpi && y0 >= 0 + && y0 <= mpi) + found = 1; + else + indx++; + } + + if (found) { + mask = dem[indx].mask[x0][y0]; + + if (mask & 2) + /* Text Labels: Red */ + fprintf(fd, "%c%c%c", 255, 0, 0); + + else if (mask & 4) + /* County Boundaries: Light Cyan */ + fprintf(fd, "%c%c%c", 128, 128, 255); + + else + switch (mask & 57) { + case 1: + /* TX1: Green */ + fprintf(fd, "%c%c%c", 0, 255, + 0); + break; + + case 8: + /* TX2: Cyan */ + fprintf(fd, "%c%c%c", 0, 255, + 255); + break; + + case 9: + /* TX1 + TX2: Yellow */ + fprintf(fd, "%c%c%c", 255, 255, + 0); + break; + + case 16: + /* TX3: Medium Violet */ + fprintf(fd, "%c%c%c", 147, 112, + 219); + break; + + case 17: + /* TX1 + TX3: Pink */ + fprintf(fd, "%c%c%c", 255, 192, + 203); + break; + + case 24: + /* TX2 + TX3: Orange */ + fprintf(fd, "%c%c%c", 255, 165, + 0); + break; + + case 25: + /* TX1 + TX2 + TX3: Dark Green */ + fprintf(fd, "%c%c%c", 0, 100, + 0); + break; + + case 32: + /* TX4: Sienna 1 */ + fprintf(fd, "%c%c%c", 255, 130, + 71); + break; + + case 33: + /* TX1 + TX4: Green Yellow */ + fprintf(fd, "%c%c%c", 173, 255, + 47); + break; + + case 40: + /* TX2 + TX4: Dark Sea Green 1 */ + fprintf(fd, "%c%c%c", 193, 255, + 193); + break; + + case 41: + /* TX1 + TX2 + TX4: Blanched Almond */ + fprintf(fd, "%c%c%c", 255, 235, + 205); + break; + + case 48: + /* TX3 + TX4: Dark Turquoise */ + fprintf(fd, "%c%c%c", 0, 206, + 209); + break; + + case 49: + /* TX1 + TX3 + TX4: Medium Spring Green */ + fprintf(fd, "%c%c%c", 0, 250, + 154); + break; + + case 56: + /* TX2 + TX3 + TX4: Tan */ + fprintf(fd, "%c%c%c", 210, 180, + 140); + break; + + case 57: + /* TX1 + TX2 + TX3 + TX4: Gold2 */ + fprintf(fd, "%c%c%c", 238, 201, + 0); + break; + + default: + if (ngs) /* No terrain */ + fprintf(fd, "%c%c%c", + 255, 255, 255); + else { + /* Sea-level: Medium Blue */ + if (dem[indx]. + data[x0][y0] == 0) + fprintf(fd, + "%c%c%c", + 0, 0, + 170); + else { + /* Elevation: Greyscale */ + terrain = + (unsigned) + (0.5 + + pow((double)(dem[indx].data[x0][y0] - min_elevation), one_over_gamma) * conversion); + fprintf(fd, + "%c%c%c", + terrain, + terrain, + terrain); + } + } + } + } + + else { + /* We should never get here, but if */ + /* we do, display the region as black */ + + fprintf(fd, "%c%c%c", 0, 0, 0); + } + } + } + + fclose(fd); + +} + +void PathReport(struct site source, struct site destination, char *name, + char graph_it, int propmodel, int pmenv) +{ + /* This function writes a PPA Path Report (name.txt) to + the filesystem. If (graph_it == 1), then gnuplot is invoked + to generate an appropriate output file indicating the Longley-Rice + model loss between the source and destination locations. + "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, .png is assumed. */ + + int x, y, z, errnum; + char basename[255], term[30], ext[15], strmode[100], + report_name[80], block = 0; + double maxloss = -100000.0, minloss = 100000.0, angle1, angle2, + azimuth, pattern = 1.0, patterndB = 0.0, + total_loss = 0.0, cos_xmtr_angle, cos_test_angle = 0.0, + source_alt, test_alt, dest_alt, source_alt2, dest_alt2, + distance, elevation, four_thirds_earth, + free_space_loss = 0.0, eirp = + 0.0, voltage, rxp, power_density, dkm, txelev; + FILE *fd = NULL, *fd2 = NULL; + + snprintf(report_name, 80, "%s.txt%c", name, 0); + four_thirds_earth = FOUR_THIRDS * EARTHRADIUS; + + fd2 = fopen(report_name, "w"); + + fprintf(fd2, "\n\t\t--==[ Path Profile Analysis ]==--\n\n"); + fprintf(fd2, "Transmitter site: %s\n", source.name); + + if (source.lat >= 0.0) { + fprintf(fd2, "Site location: %.4f North / %.4f West\n", + source.lat, source.lon); + } + + else { + + fprintf(fd2, "Site location: %.4f South / %.4f West\n", + -source.lat, source.lon); + } + + if (metric) { + fprintf(fd2, "Ground elevation: %.2f meters AMSL\n", + METERS_PER_FOOT * GetElevation(source)); + fprintf(fd2, + "Antenna height: %.2f meters AGL / %.2f meters AMSL\n", + METERS_PER_FOOT * source.alt, + METERS_PER_FOOT * (source.alt + GetElevation(source))); + } + + else { + 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)); + } + + azimuth = Azimuth(source, destination); + angle1 = ElevationAngle(source, destination); + angle2 = ElevationAngle2(source, destination, earthradius); + + if (got_azimuth_pattern || got_elevation_pattern) { + x = (int)rint(10.0 * (10.0 - angle2)); + + if (x >= 0 && x <= 1000) + pattern = + (double)LR.antenna_pattern[(int)rint(azimuth)][x]; + + patterndB = 20.0 * log10(pattern); + } + + if (metric) + fprintf(fd2, "Distance to %s: %.2f kilometers\n", + destination.name, KM_PER_MILE * Distance(source, + destination)); + + else + fprintf(fd2, "Distance to %s: %.2f miles\n", destination.name, + Distance(source, destination)); + + fprintf(fd2, "Azimuth to %s: %.2f degrees\n", destination.name, + azimuth); + + if (angle1 >= 0.0) + fprintf(fd2, "Elevation angle to %s: %+.4f degrees\n", + destination.name, angle1); + + else + fprintf(fd2, "Depression angle to %s: %+.4f degrees\n", + destination.name, angle1); + + if ((angle2 - angle1) > 0.0001) { + if (angle2 < 0.0) + fprintf(fd2, "Depression\n"); + else + fprintf(fd2, "Elevation\n"); + } + + /* Receiver */ + + fprintf(fd2, "\nReceiver site: %s\n", destination.name); + + if (destination.lat >= 0.0) { + fprintf(fd2, "Site location: %.4f North / %.4f West\n", + destination.lat, destination.lon); + } + + else { + fprintf(fd2, "Site location: %.4f South / %.4f West\n", + -destination.lat, destination.lon); + } + + if (metric) { + fprintf(fd2, "Ground elevation: %.2f meters AMSL\n", + METERS_PER_FOOT * GetElevation(destination)); + fprintf(fd2, + "Antenna height: %.2f meters AGL / %.2f meters AMSL\n", + METERS_PER_FOOT * destination.alt, + METERS_PER_FOOT * (destination.alt + + GetElevation(destination))); + } + + else { + 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)); + } + + if (metric) + fprintf(fd2, "Distance to %s: %.2f kilometers\n", source.name, + KM_PER_MILE * Distance(source, destination)); + + else + fprintf(fd2, "Distance to %s: %.2f miles\n", source.name, + Distance(source, destination)); + + azimuth = Azimuth(destination, source); + + angle1 = ElevationAngle(destination, source); + angle2 = ElevationAngle2(destination, source, earthradius); + + fprintf(fd2, "Azimuth to %s: %.2f degrees\n", source.name, azimuth); + + if (angle1 >= 0.0) + fprintf(fd2, "Elevation angle to %s: %+.4f degrees\n", + source.name, angle1); + + else + fprintf(fd2, "Depression angle to %s: %+.4f degrees\n", + source.name, angle1); + + if ((angle2 - angle1) > 0.0001) { + if (angle2 < 0.0) + fprintf(fd2, "Depression"); + else + fprintf(fd2, "Elevation"); + } + + if (LR.frq_mhz > 0.0) { + fprintf(fd2, "\nPropagation model: "); + + switch (propmodel) { + case 1: + fprintf(fd2, "Irregular Terrain Model\n"); + break; + case 2: + fprintf(fd2, "Line of sight\n"); + break; + case 3: + fprintf(fd2, "Okumura-Hata\n"); + break; + case 4: + fprintf(fd2, "ECC33 (ITU-R P.529)\n"); + break; + case 5: + fprintf(fd2, "Stanford University Interim\n"); + break; + case 6: + fprintf(fd2, "COST231-Hata\n"); + break; + case 7: + fprintf(fd2, "Free space path loss (ITU-R.525)\n"); + break; + case 8: + fprintf(fd2, "ITWOM 3.0\n"); + break; + case 9: + fprintf(fd2, "Ericsson\n"); + break; + } + + fprintf(fd2, "Model sub-type: "); + + switch (pmenv) { + case 1: + fprintf(fd2, "City / Conservative\n"); + break; + case 2: + fprintf(fd2, "Suburban / Average\n"); + break; + case 3: + fprintf(fd2, "Rural / Optimistic\n"); + break; + } + fprintf(fd2, "Earth's Dielectric Constant: %.3lf\n", + LR.eps_dielect); + fprintf(fd2, "Earth's Conductivity: %.3lf Siemens/meter\n", + LR.sgm_conductivity); + fprintf(fd2, + "Atmospheric Bending Constant (N-units): %.3lf ppm\n", + LR.eno_ns_surfref); + fprintf(fd2, "Frequency: %.3lf MHz\n", LR.frq_mhz); + fprintf(fd2, "Radio Climate: %d (", LR.radio_climate); + + switch (LR.radio_climate) { + case 1: + fprintf(fd2, "Equatorial"); + break; + + case 2: + fprintf(fd2, "Continental Subtropical"); + break; + + case 3: + fprintf(fd2, "Maritime Subtropical"); + break; + + case 4: + fprintf(fd2, "Desert"); + break; + + case 5: + fprintf(fd2, "Continental Temperate"); + break; + + case 6: + fprintf(fd2, "Maritime Temperate, Over Land"); + break; + + case 7: + fprintf(fd2, "Maritime Temperate, Over Sea"); + break; + + default: + fprintf(fd2, "Unknown"); + } + + fprintf(fd2, ")\nPolarisation: %d (", LR.pol); + + if (LR.pol == 0) + fprintf(fd2, "Horizontal"); + + if (LR.pol == 1) + fprintf(fd2, "Vertical"); + + fprintf(fd2, ")\nFraction of Situations: %.1lf%c\n", + LR.conf * 100.0, 37); + fprintf(fd2, "Fraction of Time: %.1lf%c\n", LR.rel * 100.0, 37); + + if (LR.erp != 0.0) { + fprintf(fd2, "Transmitter ERP: "); + + if (LR.erp < 1.0) + fprintf(fd2, "%.1lf milliwatts", + 1000.0 * LR.erp); + + if (LR.erp >= 1.0 && LR.erp < 10.0) + fprintf(fd2, "%.1lf Watts", LR.erp); + + if (LR.erp >= 10.0 && LR.erp < 10.0e3) + fprintf(fd2, "%.0lf Watts", LR.erp); + + if (LR.erp >= 10.0e3) + fprintf(fd2, "%.3lf kilowatts", LR.erp / 1.0e3); + + dBm = 10.0 * (log10(LR.erp * 1000.0)); + fprintf(fd2, " (%+.2f dBm)\n", dBm); + + /* EIRP = ERP + 2.14 dB */ + + fprintf(fd2, "Transmitter EIRP: "); + + eirp = LR.erp * 1.636816521; + + if (eirp < 1.0) + fprintf(fd2, "%.1lf milliwatts", 1000.0 * eirp); + + if (eirp >= 1.0 && eirp < 10.0) + fprintf(fd2, "%.1lf Watts", eirp); + + if (eirp >= 10.0 && eirp < 10.0e3) + fprintf(fd2, "%.0lf Watts", eirp); + + if (eirp >= 10.0e3) + fprintf(fd2, "%.3lf kilowatts", eirp / 1.0e3); + + dBm = 10.0 * (log10(eirp * 1000.0)); + fprintf(fd2, " (%+.2f dBm)\n", dBm); + } + + fprintf(fd2, "\nSummary for the link between %s and %s:\n\n", + source.name, destination.name); + + if (patterndB != 0.0) + fprintf(fd2, + "%s antenna pattern towards %s: %.3f (%.2f dB)\n", + source.name, destination.name, pattern, + patterndB); + + ReadPath(source, destination); /* source=TX, destination=RX */ + + /* Copy elevations plus clutter along + path into the elev[] array. */ + + for (x = 1; x < path.length - 1; x++) + elev[x + 2] = + METERS_PER_FOOT * (path.elevation[x] == + 0.0 ? path. + elevation[x] : (clutter + + path. + elevation[x])); + + /* Copy ending points without clutter */ + + elev[2] = path.elevation[0] * METERS_PER_FOOT; + elev[path.length + 1] = + path.elevation[path.length - 1] * METERS_PER_FOOT; + + azimuth = rint(Azimuth(source, destination)); + + for (y = 2; y < (path.length - 1); y++) { /* path.length-1 avoids LR error */ + distance = 5280.0 * path.distance[y]; + source_alt = + four_thirds_earth + source.alt + path.elevation[0]; + dest_alt = + four_thirds_earth + destination.alt + + path.elevation[y]; + dest_alt2 = dest_alt * dest_alt; + source_alt2 = source_alt * source_alt; + + /* Calculate the cosine of the elevation of + the receiver as seen by the transmitter. */ + + cos_xmtr_angle = + ((source_alt2) + (distance * distance) - + (dest_alt2)) / (2.0 * source_alt * distance); + + if (got_elevation_pattern) { + /* If an antenna elevation pattern is available, the + following code determines the elevation angle to + the first obstruction along the path. */ + + for (x = 2, block = 0; x < y && block == 0; x++) { + distance = + 5280.0 * (path.distance[y] - + path.distance[x]); + test_alt = + four_thirds_earth + + path.elevation[x]; + + /* Calculate the cosine of the elevation + angle of the terrain (test point) + as seen by the transmitter. */ + + cos_test_angle = + ((source_alt2) + + (distance * distance) - + (test_alt * test_alt)) / (2.0 * + source_alt + * + distance); + + /* Compare these two angles to determine if + an obstruction exists. Since we're comparing + the cosines of these angles rather than + the angles themselves, the sense of the + following "if" statement is reversed from + what it would be if the angles themselves + were compared. */ + + if (cos_xmtr_angle >= cos_test_angle) + block = 1; + } + + /* At this point, we have the elevation angle + to the first obstruction (if it exists). */ + } + + /* Determine path loss for each point along the + path using Longley-Rice's point_to_point mode + starting at x=2 (number_of_points = 1), the + shortest distance terrain can play a role in + path loss. */ + + elev[0] = y - 1; /* (number of points - 1) */ + + /* Distance between elevation samples */ + + elev[1] = + METERS_PER_MILE * (path.distance[y] - + path.distance[y - 1]); + + /* + point_to_point(elev, source.alt*METERS_PER_FOOT, + destination.alt*METERS_PER_FOOT, LR.eps_dielect, + LR.sgm_conductivity, LR.eno_ns_surfref, LR.frq_mhz, + LR.radio_climate, LR.pol, LR.conf, LR.rel, loss, + strmode, errnum); + */ + dkm = (elev[1] * elev[0]) / 1000; // km + txelev = elev[2] + (source.alt * METERS_PER_FOOT); + + switch (propmodel) { + case 1: + // Longley Rice ITM + point_to_point_ITM(source.alt * METERS_PER_FOOT, + destination.alt * + METERS_PER_FOOT, + LR.eps_dielect, + LR.sgm_conductivity, + LR.eno_ns_surfref, + LR.frq_mhz, LR.radio_climate, + LR.pol, LR.conf, LR.rel, + loss, strmode, errnum); + break; + case 3: + //HATA 1, 2 & 3 + loss = + HATApathLoss(LR.frq_mhz, txelev, + path.elevation[y] + + (destination.alt * + METERS_PER_FOOT), dkm, pmenv); + break; + case 4: + // COST231-HATA + loss = + ECC33pathLoss(LR.frq_mhz, txelev, + path.elevation[y] + + (destination.alt * + METERS_PER_FOOT), dkm, + pmenv); + break; + case 5: + // SUI + loss = + SUIpathLoss(LR.frq_mhz, txelev, + path.elevation[y] + + (destination.alt * + METERS_PER_FOOT), dkm, pmenv); + break; + case 6: + loss = + COST231pathLoss(LR.frq_mhz, txelev, + path.elevation[y] + + (destination.alt * + METERS_PER_FOOT), dkm, + pmenv); + break; + case 7: + // ITU-R P.525 Free space path loss + loss = FSPLpathLoss(LR.frq_mhz, dkm); + break; + case 8: + // ITWOM 3.0 + point_to_point(source.alt * METERS_PER_FOOT, + destination.alt * + METERS_PER_FOOT, LR.eps_dielect, + LR.sgm_conductivity, + LR.eno_ns_surfref, LR.frq_mhz, + LR.radio_climate, LR.pol, + LR.conf, LR.rel, loss, strmode, + errnum); + break; + case 9: + // Ericsson + loss = + EricssonpathLoss(LR.frq_mhz, txelev, + path.elevation[y] + + (destination.alt * + METERS_PER_FOOT), dkm, + pmenv); + break; + + default: + point_to_point_ITM(source.alt * METERS_PER_FOOT, + destination.alt * + METERS_PER_FOOT, + LR.eps_dielect, + LR.sgm_conductivity, + LR.eno_ns_surfref, + LR.frq_mhz, LR.radio_climate, + LR.pol, LR.conf, LR.rel, + loss, strmode, errnum); + + } + + if (block) + elevation = + ((acos(cos_test_angle)) / DEG2RAD) - 90.0; + else + elevation = + ((acos(cos_xmtr_angle)) / DEG2RAD) - 90.0; + + /* Integrate the antenna's radiation + pattern into the overall path loss. */ + + x = (int)rint(10.0 * (10.0 - elevation)); + + if (x >= 0 && x <= 1000) { + pattern = + (double)LR.antenna_pattern[(int)azimuth][x]; + + if (pattern != 0.0) + patterndB = 20.0 * log10(pattern); + } + + else + patterndB = 0.0; + + total_loss = loss - patterndB; + + if (total_loss > maxloss) + maxloss = total_loss; + + if (total_loss < minloss) + minloss = total_loss; + } + + distance = Distance(source, destination); + + if (distance != 0.0) { + free_space_loss = + 36.6 + (20.0 * log10(LR.frq_mhz)) + + (20.0 * log10(distance)); + fprintf(fd2, "Free space path loss: %.2f dB\n", + free_space_loss); + } + + fprintf(fd2, "Computed path loss: %.2f dB\n", loss); + + if (free_space_loss != 0.0) + fprintf(fd2, + "Attenuation due to terrain shielding: %.2f dB\n", + loss - free_space_loss); + + if (patterndB != 0.0) + fprintf(fd2, + "Total path loss including %s antenna pattern: %.2f dB\n", + source.name, total_loss); + + if (LR.erp != 0.0) { + field_strength = + (139.4 + (20.0 * log10(LR.frq_mhz)) - total_loss) + + (10.0 * log10(LR.erp / 1000.0)); + + /* dBm is referenced to EIRP */ + + rxp = eirp / (pow(10.0, (total_loss / 10.0))); + dBm = 10.0 * (log10(rxp * 1000.0)); + power_density = + (eirp / + (pow + (10.0, (total_loss - free_space_loss) / 10.0))); + /* divide by 4*PI*distance_in_meters squared */ + power_density /= (4.0 * PI * distance * distance * + 2589988.11); + + fprintf(fd2, "Field strength at %s: %.2f dBuV/meter\n", + destination.name, field_strength); + fprintf(fd2, "Signal power level at %s: %+.2f dBm\n", + destination.name, dBm); + fprintf(fd2, + "Signal power density at %s: %+.2f dBW per square meter\n", + destination.name, 10.0 * log10(power_density)); + voltage = + 1.0e6 * sqrt(50.0 * + (eirp / + (pow + (10.0, + (total_loss - 2.14) / 10.0)))); + fprintf(fd2, + "Voltage across 50 ohm dipole at %s: %.2f uV (%.2f dBuV)\n", + destination.name, voltage, + 20.0 * log10(voltage)); + + voltage = + 1.0e6 * sqrt(75.0 * + (eirp / + (pow + (10.0, + (total_loss - 2.14) / 10.0)))); + fprintf(fd2, + "Voltage across 75 ohm dipole at %s: %.2f uV (%.2f dBuV)\n", + destination.name, voltage, + 20.0 * log10(voltage)); + } + + if (propmodel == 1) { + fprintf(fd2, "Longley-Rice model error number: %d", + errnum); + + switch (errnum) { + case 0: + fprintf(fd2, " (No error)\n"); + break; + + case 1: + fprintf(fd2, + "\n Warning: Some parameters are nearly out of range.\n"); + fprintf(fd2, + " Results should be used with caution.\n"); + break; + + case 2: + fprintf(fd2, + "\n Note: Default parameters have been substituted for impossible ones.\n"); + break; + + case 3: + fprintf(fd2, + "\n Warning: A combination of parameters is out of range.\n"); + fprintf(fd2, + " Results are probably invalid.\n"); + break; + + default: + fprintf(fd2, + "\n Warning: Some parameters are out of range.\n"); + fprintf(fd2, + " Results are probably invalid.\n"); + } + } + + } + + ObstructionAnalysis(source, destination, LR.frq_mhz, fd2); + + fclose(fd2); + + fprintf(stdout, + "Path loss (dB), Received Power (dBm), Field strength (dBuV):\n%.1f\n%.1f\n%.1f", + loss, dBm, field_strength); + + /* Skip plotting the graph if ONLY a path-loss report is needed. */ + + if (graph_it) { + if (name[0] == '.') { + /* Default filename and output file type */ + + strncpy(basename, "profile\0", 8); + strncpy(term, "png\0", 4); + strncpy(ext, "png\0", 4); + } + + else { + /* Extract extension and terminal type from "name" */ + + ext[0] = 0; + y = strlen(name); + strncpy(basename, name, 254); + + for (x = y - 1; x > 0 && name[x] != '.'; x--) ; + + if (x > 0) { /* Extension found */ + for (z = x + 1; z <= y && (z - (x + 1)) < 10; + z++) { + ext[z - (x + 1)] = tolower(name[z]); + term[z - (x + 1)] = name[z]; + } + + ext[z - (x + 1)] = 0; /* Ensure an ending 0 */ + term[z - (x + 1)] = 0; + basename[x] = 0; + } + } + + if (ext[0] == 0) { /* No extension -- Default is png */ + strncpy(term, "png\0", 4); + strncpy(ext, "png\0", 4); + } + + /* Either .ps or .postscript may be used + as an extension for postscript output. */ + + if (strncmp(term, "postscript", 10) == 0) + strncpy(ext, "ps\0", 3); + + else if (strncmp(ext, "ps", 2) == 0) + strncpy(term, "postscript enhanced color\0", 26); + + fd = fopen("ppa.gp", "w"); + + fprintf(fd, "set grid\n"); + fprintf(fd, "set yrange [%2.3f to %2.3f]\n", minloss, maxloss); + fprintf(fd, "set encoding iso_8859_1\n"); + fprintf(fd, "set term %s\n", term); + fprintf(fd, + "set title \"Path Loss Profile Along Path Between %s and %s (%.2f%c azimuth)\"\n", + destination.name, source.name, Azimuth(destination, + source), 176); + + if (metric) + fprintf(fd, + "set xlabel \"Distance Between %s and %s (%.2f kilometers)\"\n", + destination.name, source.name, + KM_PER_MILE * Distance(destination, source)); + else + fprintf(fd, + "set xlabel \"Distance Between %s and %s (%.2f miles)\"\n", + destination.name, source.name, + Distance(destination, source)); + + if (got_azimuth_pattern || got_elevation_pattern) + fprintf(fd, + "set ylabel \"Total Path Loss (including TX antenna pattern) (dB)"); + else + fprintf(fd, "set ylabel \"Longley-Rice Path Loss (dB)"); + + fprintf(fd, "\"\nset output \"%s.%s\"\n", basename, ext); + fprintf(fd, + "plot \"profile.gp\" title \"Path Loss\" with lines\n"); + + fclose(fd); + + x = system("gnuplot ppa.gp"); + + if (x != -1) { + if (gpsav == 0) { + //unlink("ppa.gp"); + //unlink("profile.gp"); + //unlink("reference.gp"); + } + + } + + else + fprintf(stderr, + "\n*** ERROR: Error occurred invoking gnuplot!\n"); + } + +} + +void SeriesData(struct site source, struct site destination, char *name, + unsigned char fresnel_plot, unsigned char normalised) +{ + + int x, y, z; + char basename[255], term[30], ext[15], profilename[255], + referencename[255], cluttername[255], curvaturename[255], + fresnelname[255], fresnel60name[255]; + double a, b, c, height = 0.0, refangle, cangle, maxheight = + -100000.0, minheight = 100000.0, lambda = 0.0, f_zone = + 0.0, fpt6_zone = 0.0, nm = 0.0, nb = 0.0, ed = 0.0, es = 0.0, r = + 0.0, d = 0.0, d1 = 0.0, terrain, azimuth, distance, minterrain = + 100000.0, minearth = 100000.0; + struct site remote; + FILE *fd = NULL, *fd1 = NULL, *fd2 = NULL, *fd3 = NULL, *fd4 = + NULL, *fd5 = NULL; + + ReadPath(destination, source); + azimuth = Azimuth(destination, source); + distance = Distance(destination, source); + refangle = ElevationAngle(destination, source); + b = GetElevation(destination) + destination.alt + earthradius; + + if (fresnel_plot) { + lambda = 9.8425e8 / (LR.frq_mhz * 1e6); + d = 5280.0 * path.distance[path.length - 1]; + } + + if (normalised) { + ed = GetElevation(destination); + es = GetElevation(source); + nb = -destination.alt - ed; + nm = (-source.alt - es - nb) / (path.distance[path.length - 1]); + } + + strcpy(profilename, name); + strcat(profilename, "_profile\0"); + strcpy(referencename, name); + strcat(referencename, "_reference\0"); + strcpy(cluttername, name); + strcat(cluttername, "_clutter\0"); + strcpy(curvaturename, name); + strcat(curvaturename, "_curvature\0"); + strcpy(fresnelname, name); + strcat(fresnelname, "_fresnel\0"); + strcpy(fresnel60name, name); + strcat(fresnel60name, "_fresnel60\0"); + + fd = fopen(profilename, "wb"); + if (clutter > 0.0) + fd1 = fopen(cluttername, "wb"); + fd2 = fopen(referencename, "wb"); + fd5 = fopen(curvaturename, "wb"); + + if ((LR.frq_mhz >= 20.0) && (LR.frq_mhz <= 100000.0) && fresnel_plot) { + fd3 = fopen(fresnelname, "wb"); + fd4 = fopen(fresnel60name, "wb"); + } + + for (x = 0; x < path.length - 1; x++) { + remote.lat = path.lat[x]; + remote.lon = path.lon[x]; + remote.alt = 0.0; + terrain = GetElevation(remote); + if (x == 0) + terrain += destination.alt; /* RX antenna spike */ + + a = terrain + earthradius; + cangle = 5280.0 * Distance(destination, remote) / earthradius; + c = b * sin(refangle * DEG2RAD + HALFPI) / sin(HALFPI - + refangle * + DEG2RAD - + cangle); + height = a - c; + + /* Per Fink and Christiansen, Electronics + * Engineers' Handbook, 1989: + * + * H = sqrt(lamba * d1 * (d - d1)/d) + * + * where H is the distance from the LOS + * path to the first Fresnel zone boundary. + */ + + if ((LR.frq_mhz >= 20.0) && (LR.frq_mhz <= 100000.0) + && fresnel_plot) { + d1 = 5280.0 * path.distance[x]; + f_zone = -1.0 * sqrt(lambda * d1 * (d - d1) / d); + fpt6_zone = f_zone * fzone_clearance; + } + + if (normalised) { + r = -(nm * path.distance[x]) - nb; + height += r; + + if ((LR.frq_mhz >= 20.0) && (LR.frq_mhz <= 100000.0) + && fresnel_plot) { + f_zone += r; + fpt6_zone += r; + } + } + + else + r = 0.0; + + if (metric) { + if (METERS_PER_FOOT * height > 0) { + fprintf(fd, "%.3f %.3f\n", + KM_PER_MILE * path.distance[x], + METERS_PER_FOOT * height); + } + + if (fd1 != NULL && x > 0 && x < path.length - 2) + fprintf(fd1, "%.3f %.3f\n", + KM_PER_MILE * path.distance[x], + METERS_PER_FOOT * (terrain == + 0.0 ? height + : (height + + clutter))); + + fprintf(fd2, "%.3f %.3f\n", + KM_PER_MILE * path.distance[x], + METERS_PER_FOOT * r); + fprintf(fd5, "%.3f %.3f\n", + KM_PER_MILE * path.distance[x], + METERS_PER_FOOT * (height - terrain)); + + } + + else { + fprintf(fd, "%.3f %.3f\n", path.distance[x], height); + + if (fd1 != NULL && x > 0 && x < path.length - 2) + fprintf(fd1, "%.3f %.3f\n", path.distance[x], + (terrain == + 0.0 ? height : (height + clutter))); + + fprintf(fd2, "%.3f %.3f\n", path.distance[x], r); + fprintf(fd5, "%.3f %.3f\n", path.distance[x], + height - terrain); + } + + if ((LR.frq_mhz >= 20.0) && (LR.frq_mhz <= 100000.0) + && fresnel_plot) { + if (metric) { + fprintf(fd3, "%.3f %.3f\n", + KM_PER_MILE * path.distance[x], + METERS_PER_FOOT * f_zone); + fprintf(fd4, "%.3f %.3f\n", + KM_PER_MILE * path.distance[x], + METERS_PER_FOOT * fpt6_zone); + } + + else { + fprintf(fd3, "%.3f %.3f\n", path.distance[x], + f_zone); + fprintf(fd4, "%.3f %.3f\n", path.distance[x], + fpt6_zone); + } + + if (f_zone < minheight) + minheight = f_zone; + } + + if ((height + clutter) > maxheight) + maxheight = height + clutter; + + if (height < minheight) + minheight = height; + + if (r > maxheight) + maxheight = r; + + if (terrain < minterrain) + minterrain = terrain; + + if ((height - terrain) < minearth) + minearth = height - terrain; + } // End of loop + + if (normalised) + r = -(nm * path.distance[path.length - 1]) - nb; + else + r = 0.0; + + if (metric) { + fprintf(fd, "%.3f %.3f", + KM_PER_MILE * path.distance[path.length - 1], + METERS_PER_FOOT * r); + fprintf(fd2, "%.3f %.3f", + KM_PER_MILE * path.distance[path.length - 1], + METERS_PER_FOOT * r); + } + + else { + fprintf(fd, "%.3f %.3f", path.distance[path.length - 1], r); + fprintf(fd2, "%.3f %.3f", path.distance[path.length - 1], r); + } + + if ((LR.frq_mhz >= 20.0) && (LR.frq_mhz <= 100000.0) && fresnel_plot) { + if (metric) { + fprintf(fd3, "%.3f %.3f", + KM_PER_MILE * path.distance[path.length - 1], + METERS_PER_FOOT * r); + fprintf(fd4, "%.3f %.3f", + KM_PER_MILE * path.distance[path.length - 1], + METERS_PER_FOOT * r); + } + + else { + fprintf(fd3, "%.3f %.3f", + path.distance[path.length - 1], r); + fprintf(fd4, "%.3f %.3f", + path.distance[path.length - 1], r); + } + } + + if (r > maxheight) + maxheight = r; + + if (r < minheight) + minheight = r; + + fclose(fd); + + if (fd1 != NULL) + fclose(fd1); + + fclose(fd2); + fclose(fd5); + + if ((LR.frq_mhz >= 20.0) && (LR.frq_mhz <= 100000.0) && fresnel_plot) { + fclose(fd3); + fclose(fd4); + } + + if (name[0] == '.') { + strncpy(basename, "profile\0", 8); + strncpy(term, "png\0", 4); + strncpy(ext, "png\0", 4); + } + + else { + + ext[0] = 0; + y = strlen(name); + strncpy(basename, name, 254); + + for (x = y - 1; x > 0 && name[x] != '.'; x--) ; + + if (x > 0) { + for (z = x + 1; z <= y && (z - (x + 1)) < 10; z++) { + ext[z - (x + 1)] = tolower(name[z]); + term[z - (x + 1)] = name[z]; + } + + ext[z - (x + 1)] = 0; + term[z - (x + 1)] = 0; + basename[x] = 0; + } + + if (ext[0] == 0) { + strncpy(term, "png\0", 4); + strncpy(ext, "png\0", 4); + } + } + + fprintf(stdout, "\n"); + fflush(stdout); + +} diff --git a/outputs.hh b/outputs.hh new file mode 100644 index 0000000..f0bda3c --- /dev/null +++ b/outputs.hh @@ -0,0 +1,17 @@ +#ifndef _OUTPUT_HH_ +#define _OUTPUT_HH_ + +void DoPathLoss(char *filename, unsigned char geo, unsigned char kml, + unsigned char ngs, struct site *xmtr, unsigned char txsites); +void DoSigStr(char *filename, unsigned char geo, unsigned char kml, + unsigned char ngs, struct site *xmtr, unsigned char txsites); +void DoRxdPwr(char *filename, unsigned char geo, unsigned char kml, + unsigned char ngs, struct site *xmtr, unsigned char txsites); +void DoLOS(char *filename, unsigned char geo, unsigned char kml, + unsigned char ngs, struct site *xmtr, unsigned char txsites); +void PathReport(struct site source, struct site destination, char *name, + char graph_it, int propmodel, int pmenv); +void SeriesData(struct site source, struct site destination, char *name, + unsigned char fresnel_plot, unsigned char normalised); + +#endif /* _OUTPUT_HH_ */ diff --git a/ppa.cpp b/ppa.cpp deleted file mode 100644 index 4e1a066..0000000 --- a/ppa.cpp +++ /dev/null @@ -1,2953 +0,0 @@ -/****************************************************************************\ -* PPA: Path Profile Analysis Tool v1.3.1 derived from SPLAT! 1.3 * -****************************************************************************** -* Project started in 1997 by John A. Magliacane, KD2BD * -* Last update: 10-Apr-2009 * -****************************************************************************** -* Please consult the documentation for a complete list of * -* individuals who have contributed to this project. * -****************************************************************************** -* * -* 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 * -* version. * -* * -* This program is distributed in the hope that it will useful, but WITHOUT * -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * -* for more details. * -* * -****************************************************************************** -* g++ -Wall -O3 -s -lm -fomit-frame-pointer itm.cpp ppa.cpp -o ppa * -\****************************************************************************/ - -#include -#include -#include -#include -#include -#include - -#define GAMMA 2.5 -#define BZBUFFER 65536 -#define HD_MODE 0 -#define MAXPAGES 64 -#define ARRAYSIZE 76810 -#define IPPD 1200 - - -#ifndef PI -#define PI 3.141592653589793 -#endif - -#ifndef TWOPI -#define TWOPI 6.283185307179586 -#endif - -#ifndef HALFPI -#define HALFPI 1.570796326794896 -#endif - -#define DEG2RAD 1.74532925199e-02 -#define EARTHRADIUS 20902230.97 -#define METERS_PER_MILE 1609.344 -#define METERS_PER_FOOT 0.3048 -#define KM_PER_MILE 1.609344 -#define FOUR_THIRDS 1.3333333333333 - -char string[255], sdf_path[255], opened=0, gpsav=0, ppa_name[10], - ppa_version[6], dashes[80]; - -double earthradius, max_range=0.0, forced_erp=-1.0, dpp, ppd, - fzone_clearance=0.6, clutter, loss, dBm, field_strength; - -int min_north=90, max_north=-90, min_west=360, max_west=-1, ippd, mpi, - max_elevation=-32768, min_elevation=32768, bzerror, contour_threshold, output=2; - -unsigned char got_elevation_pattern, got_azimuth_pattern, metric=0, dbm=0; - - - -struct site { double lat; - double lon; - float alt; - char name[50]; - char filename[255]; - } site; - -struct path { double lat[ARRAYSIZE]; - double lon[ARRAYSIZE]; - double elevation[ARRAYSIZE]; - double 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[IPPD][IPPD]; - unsigned char mask[IPPD][IPPD]; - unsigned char signal[IPPD][IPPD]; - } dem[MAXPAGES]; - -struct LR { double eps_dielect; - double sgm_conductivity; - double eno_ns_surfref; - double frq_mhz; - double conf; - double rel; - double erp; - int radio_climate; - int pol; - float antenna_pattern[361][1001]; - } LR; - -struct region { unsigned char color[32][3]; - int level[32]; - int levels; - } region; - -double elev[ARRAYSIZE+10]; - -void point_to_point(double elev[], double tht_m, double rht_m, - double eps_dielect, double sgm_conductivity, double eno_ns_surfref, - double frq_mhz, int radio_climate, int pol, double conf, - double rel, double &dbloss, char *strmode, int &errnum); - -double arccos(double x, double y) -{ - /* This function implements the arc cosine function, - returning a value between 0 and TWOPI. */ - - double result=0.0; - - if (y>0.0) - result=acos(x/y); - - if (y<0.0) - result=PI+acos(x/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); -} - -double LonDiff(double lon1, double 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. */ - - double diff; - - diff=lon1-lon2; - - if (diff<=-180.0) - diff+=360.0; - - if (diff>=180.0) - diff-=360.0; - - return diff; -} - -char *dec2dms(double decimal) -{ - /* Converts decimal degrees to degrees, minutes, seconds, - (DMS) and returns the result as a character string. */ - - char sign; - int degrees, minutes, seconds; - double a, b, c, d; - - if (decimal<0.0) - { - decimal=-decimal; - sign=-1; - } - - else - sign=1; - - a=floor(decimal); - b=60.0*(decimal-a); - c=floor(b); - d=60.0*(b-c); - - degrees=(int)a; - minutes=(int)c; - seconds=(int)d; - - if (seconds<0) - seconds=0; - - if (seconds>59) - seconds=59; - - string[0]=0; - snprintf(string,250,"%d%c %d\' %d\"", degrees*sign, 176, minutes, seconds); - return (string); -} - - - -int OrMask(double lat, double lon, int value) -{ - - int x, y, indx; - char found; - - for (indx=0, found=0; indx=0 && x<=mpi && y>=0 && y<=mpi) - found=1; - else - indx++; - } - - if (found) - { - dem[indx].mask[x][y]|=value; - return ((int)dem[indx].mask[x][y]); - } - - else - return -1; -} - - -double GetElevation(struct site location) -{ - /* This function returns the elevation (in feet) of any location - represented by the digital elevation model data in memory. - Function returns -5000.0 for locations not found in memory. */ - - char found; - int x, y, indx; - double elevation; - - for (indx=0, found=0; indx=0 && x<=mpi && y>=0 && y<=mpi) - found=1; - else - indx++; - } - - if (found) - elevation=3.28084*dem[indx].data[x][y]; - else - elevation=-5000.0; - - - return elevation; -} - -int AddElevation(double lat, double lon, double height) -{ - /* This function adds a user-defined terrain feature - (in meters AGL) to the digital elevation model data - in memory. Does nothing and returns 0 for locations - not found in memory. */ - - char found; - int x, y, indx; - - for (indx=0, found=0; indx=0 && x<=mpi && y>=0 && y<=mpi) - found=1; - else - indx++; - } - - if (found) - dem[indx].data[x][y]+=(short)rint(height); - - return found; -} - -double Distance(struct site site1, struct site site2) -{ - /* This function returns the great circle distance - in miles between any two site locations. */ - - double lat1, lon1, lat2, lon2, distance; - - lat1=site1.lat*DEG2RAD; - lon1=site1.lon*DEG2RAD; - lat2=site2.lat*DEG2RAD; - lon2=site2.lon*DEG2RAD; - - distance=3959.0*acos(sin(lat1)*sin(lat2)+cos(lat1)*cos(lat2)*cos((lon1)-(lon2))); - - return distance; -} - -double Azimuth(struct site source, struct site destination) -{ - /* This function returns the azimuth (in degrees) to the - destination as seen from the location of the source. */ - - double dest_lat, dest_lon, src_lat, src_lon, - beta, azimuth, diff, num, den, fraction; - - dest_lat=destination.lat*DEG2RAD; - dest_lon=destination.lon*DEG2RAD; - - src_lat=source.lat*DEG2RAD; - src_lon=source.lon*DEG2RAD; - - /* Calculate Surface Distance */ - - beta=acos(sin(src_lat)*sin(dest_lat)+cos(src_lat)*cos(dest_lat)*cos(src_lon-dest_lon)); - - /* Calculate Azimuth */ - - num=sin(dest_lat)-(sin(src_lat)*cos(beta)); - den=cos(src_lat)*sin(beta); - fraction=num/den; - - /* Trap potential problems in acos() due to rounding */ - - if (fraction>=1.0) - fraction=1.0; - - if (fraction<=-1.0) - fraction=-1.0; - - /* Calculate azimuth */ - - azimuth=acos(fraction); - - /* Reference it to True North */ - - diff=dest_lon-src_lon; - - if (diff<=-PI) - diff+=TWOPI; - - if (diff>=PI) - diff-=TWOPI; - - if (diff>0.0) - azimuth=TWOPI-azimuth; - - return (azimuth/DEG2RAD); -} - -double ElevationAngle(struct site source, struct site destination) -{ - /* This function returns the angle of elevation (in degrees) - of the destination as seen from the source location. - A positive result represents an angle of elevation (uptilt), - while a negative result represents an angle of depression - (downtilt), as referenced to a normal to the center of - the earth. */ - - register double a, b, dx; - - a=GetElevation(destination)+destination.alt+earthradius; - b=GetElevation(source)+source.alt+earthradius; - - dx=5280.0*Distance(source,destination); - - /* Apply the Law of Cosines */ - - return ((180.0*(acos(((b*b)+(dx*dx)-(a*a))/(2.0*b*dx)))/PI)-90.0); -} - -void ReadPath(struct site source, struct site destination) -{ - /* This function generates a sequence of latitude and - longitude positions between source and destination - locations along a great circle path, and stores - elevation and distance information for points - along that path in the "path" structure. */ - - int c; - double azimuth, distance, lat1, lon1, beta, den, num, - lat2, lon2, total_distance, dx, dy, path_length, - miles_per_sample, samples_per_radian=68755.0; - struct site tempsite; - - lat1=source.lat*DEG2RAD; - lon1=source.lon*DEG2RAD; - - lat2=destination.lat*DEG2RAD; - lon2=destination.lon*DEG2RAD; - - if (ppd==1200.0) - samples_per_radian=68755.0; - - if (ppd==3600.0) - samples_per_radian=206265.0; - - azimuth=Azimuth(source,destination)*DEG2RAD; - - total_distance=Distance(source,destination); - - if (total_distance>(30.0/ppd)) /* > 0.5 pixel distance */ - { - dx=samples_per_radian*acos(cos(lon1-lon2)); - dy=samples_per_radian*acos(cos(lat1-lat2)); - - path_length=sqrt((dx*dx)+(dy*dy)); /* Total number of samples */ - - miles_per_sample=total_distance/path_length; /* Miles per sample */ - } - - else - { - c=0; - dx=0.0; - dy=0.0; - path_length=0.0; - miles_per_sample=0.0; - total_distance=0.0; - - lat1=lat1/DEG2RAD; - lon1=lon1/DEG2RAD; - - path.lat[c]=lat1; - path.lon[c]=lon1; - path.elevation[c]=GetElevation(source); - path.distance[c]=0.0; - } - - for (distance=0.0, c=0; (total_distance!=0.0 && distance<=total_distance && cHALFPI-lat1)) - lon2=lon1+PI; - - else if (azimuth==HALFPI && (beta>HALFPI+lat1)) - lon2=lon1+PI; - - else if (fabs(num/den)>1.0) - lon2=lon1; - - else - { - if ((PI-azimuth)>=0.0) - lon2=lon1-arccos(num,den); - else - lon2=lon1+arccos(num,den); - } - - while (lon2<0.0) - lon2+=TWOPI; - - while (lon2>TWOPI) - lon2-=TWOPI; - - lat2=lat2/DEG2RAD; - lon2=lon2/DEG2RAD; - - path.lat[c]=lat2; - path.lon[c]=lon2; - tempsite.lat=lat2; - tempsite.lon=lon2; - path.elevation[c]=GetElevation(tempsite); - path.distance[c]=distance; - } - - /* Make sure exact destination point is recorded at path.length-1 */ - - if (c=cos_test_angle) - { - block=1; - first_obstruction_angle=((acos(cos_test_angle))/DEG2RAD)-90.0; - } - } - - if (block) - elevation=first_obstruction_angle; - - else - elevation=((acos(cos_xmtr_angle))/DEG2RAD)-90.0; - - path=temp; - - return elevation; -} - -double AverageTerrain(struct site source, double azimuthx, double start_distance, double end_distance) -{ - /* This function returns the average terrain calculated in - the direction of "azimuth" (degrees) between "start_distance" - and "end_distance" (miles) from the source location. If - the terrain is all water (non-critical error), -5000.0 is - returned. If not enough SDF data has been loaded into - memory to complete the survey (critical error), then - -9999.0 is returned. */ - - int c, samples, endpoint; - double beta, lat1, lon1, lat2, lon2, num, den, azimuth, terrain=0.0; - struct site destination; - - lat1=source.lat*DEG2RAD; - lon1=source.lon*DEG2RAD; - - /* Generate a path of elevations between the source - location and the remote location provided. */ - - beta=end_distance/3959.0; - - azimuth=DEG2RAD*azimuthx; - - lat2=asin(sin(lat1)*cos(beta)+cos(azimuth)*sin(beta)*cos(lat1)); - num=cos(beta)-(sin(lat1)*sin(lat2)); - den=cos(lat1)*cos(lat2); - - if (azimuth==0.0 && (beta>HALFPI-lat1)) - lon2=lon1+PI; - - else if (azimuth==HALFPI && (beta>HALFPI+lat1)) - lon2=lon1+PI; - - else if (fabs(num/den)>1.0) - lon2=lon1; - - else - { - if ((PI-azimuth)>=0.0) - lon2=lon1-arccos(num,den); - else - lon2=lon1+arccos(num,den); - } - - while (lon2<0.0) - lon2+=TWOPI; - - while (lon2>TWOPI) - lon2-=TWOPI; - - lat2=lat2/DEG2RAD; - lon2=lon2/DEG2RAD; - - destination.lat=lat2; - destination.lon=lon2; - - /* If SDF data is missing for the endpoint of - the radial, then the average terrain cannot - be accurately calculated. Return -9999.0 */ - - if (GetElevation(destination)<-4999.0) - return (-9999.0); - else - { - ReadPath(source,destination); - - endpoint=path.length; - - /* Shrink the length of the radial if the - outermost portion is not over U.S. land. */ - - for (c=endpoint-1; c>=0 && path.elevation[c]==0.0; c--); - - endpoint=c+1; - - for (c=0, samples=0; c=start_distance) - { - terrain+=(path.elevation[c]==0.0?path.elevation[c]:path.elevation[c]+clutter); - samples++; - } - } - - if (samples==0) - terrain=-5000.0; /* No land */ - else - terrain=(terrain/(double)samples); - - return terrain; - } -} - -double haat(struct site antenna) -{ - /* This function returns the antenna's Height Above Average - Terrain (HAAT) based on FCC Part 73.313(d). If a critical - error occurs, such as a lack of SDF data to complete the - survey, -5000.0 is returned. */ - - int azi, c; - char error=0; - double terrain, avg_terrain, haat, sum=0.0; - - /* Calculate the average terrain between 2 and 10 miles - from the antenna site at azimuths of 0, 45, 90, 135, - 180, 225, 270, and 315 degrees. */ - - for (c=0, azi=0; azi<=315 && error==0; azi+=45) - { - terrain=AverageTerrain(antenna, (double)azi, 2.0, 10.0); - - if (terrain<-9998.0) /* SDF data is missing */ - error=1; - - if (terrain>-4999.0) /* It's land, not water */ - { - sum+=terrain; /* Sum of averages */ - c++; - } - } - - if (error) - return -5000.0; - else - { - avg_terrain=(sum/(double)c); - haat=(antenna.alt+GetElevation(antenna))-avg_terrain; - return haat; - } -} - - - -double ReadBearing(char *input) -{ - /* This function takes numeric input in the form of a character - string, and returns an equivalent bearing in degrees as a - decimal number (double). The input may either be expressed - in decimal format (40.139722) or degree, minute, second - format (40 08 23). This function also safely handles - extra spaces found either leading, trailing, or - embedded within the numbers expressed in the - input string. Decimal seconds are permitted. */ - - double seconds, bearing=0.0; - char string[20]; - int a, b, length, degrees, minutes; - - /* Copy "input" to "string", and ignore any extra - spaces that might be present in the process. */ - - string[0]=0; - length=strlen(input); - - for (a=0, b=0; a360.0 || bearing<-360.0) - bearing=0.0; - - return bearing; -} - -int LoadSDF_SDF(char *name,int winfiles) -{ - - int x, y, data, indx, minlat, minlon, maxlat, maxlon; - char found, free_page=0, line[20], sdf_file[255], - path_plus_name[255], *s=NULL; - FILE *fd; - - for (x=0; name[x]!='.' && name[x]!=0 && x<250; x++) - sdf_file[x]=name[x]; - - sdf_file[x]=0; - - /* Parse filename for minimum latitude and longitude values */ - if(winfiles==1){ - sscanf(sdf_file,"%d=%d=%d=%d",&minlat,&maxlat,&minlon,&maxlon); - }else{ - sscanf(sdf_file,"%d:%d:%d:%d",&minlat,&maxlat,&minlon,&maxlon); - } - sdf_file[x]='.'; - sdf_file[x+1]='s'; - sdf_file[x+2]='d'; - sdf_file[x+3]='f'; - sdf_file[x+4]=0; - - /* Is it already in memory? */ - - for (indx=0, found=0; indx=0 && indxdem[indx].max_el) - dem[indx].max_el=data; - - if (datamax_elevation) - max_elevation=dem[indx].max_el; - - if (max_north==-90) - max_north=dem[indx].max_north; - - else if (dem[indx].max_north>max_north) - max_north=dem[indx].max_north; - - 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; - } - } - - - - return 1; - } - - else - return -1; - } - - else - return 0; -} - -char LoadSDF(char *name, int winfiles) -{ - /* This function loads the requested SDF file from the filesystem. - It first tries to invoke the LoadSDF_SDF() function to load an - uncompressed SDF file (since uncompressed files load slightly - faster). If that attempt fails, then it tries to load a - compressed SDF file by invoking the LoadSDF_BZ() function. - If that fails, then we can assume that no elevation data - exists for the region requested, and that the region - requested must be entirely over water. */ - - int x, y, indx, minlat, minlon, maxlat, maxlon; - char found, free_page=0; - int return_value=-1; - - /* Try to load an uncompressed SDF first. */ - - return_value=LoadSDF_SDF(name,winfiles); - - - /* If neither format can be found, then assume the area is water. */ - - if (return_value==0 || return_value==-1) - { - if(winfiles==1){ - sscanf(name,"%d=%d=%d=%d",&minlat,&maxlat,&minlon,&maxlon); - }else{ - sscanf(name,"%d:%d:%d:%d",&minlat,&maxlat,&minlon,&maxlon); - } - /* Is it already in memory? */ - - for (indx=0, found=0; indx=0 && indx0) - dem[indx].min_el=0; - } - - if (dem[indx].min_elmax_elevation) - max_elevation=dem[indx].max_el; - - if (max_north==-90) - max_north=dem[indx].max_north; - - else if (dem[indx].max_north>max_north) - max_north=dem[indx].max_north; - - 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"); - fflush(stdout); - - return_value=1; - } - } - - return return_value; -} - -int GetMask(double lat, double lon) -{ - /* This function returns the mask bits based on the latitude - and longitude given. */ - - return (OrMask(lat,lon,0)); -} - - -void PlotPath(struct site source, struct site destination, char mask_value) -{ - /* This function analyzes the path between the source and - destination locations. It determines which points along - the path have line-of-sight visibility to the source. - Points along with path having line-of-sight visibility - to the source at an AGL altitude equal to that of the - destination location are stored by setting bit 1 in the - mask[][] array, which are displayed in green when PPM - maps are later generated by SPLAT!. */ - - char block; - int x, y; - register double cos_xmtr_angle, cos_test_angle, test_alt; - double distance, rx_alt, tx_alt; - - ReadPath(source,destination); - - for (y=0; y=0 && block==0; x--) - { - distance=5280.0*(path.distance[y]-path.distance[x]); - test_alt=earthradius+(path.elevation[x]==0.0?path.elevation[x]:path.elevation[x]+clutter); - - 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 - 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 - be if the actual angles were compared. */ - - if (cos_xmtr_angle>=cos_test_angle) - block=1; - } - - if (block==0) - OrMask(path.lat[y],path.lon[y],mask_value); - } - } -} - - - - -void GraphHeight(struct site source, struct site destination, char *name, unsigned char fresnel_plot, unsigned char normalized, int pngwidth, int pngheight) -{ - /* This function invokes gnuplot to generate an appropriate - output file indicating the terrain height profile between - the source and destination locations referenced to the - line-of-sight path between the receive and transmit sites - when the -h or -H command line option is used. "basename" - 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, - .png is assumed. */ - - int x, y, z; - char basename[255], term[30], ext[15]; - double a, b, c, height=0.0, refangle, cangle, maxheight=-100000.0, - minheight=100000.0, lambda=0.0, f_zone=0.0, fpt6_zone=0.0, - nm=0.0, nb=0.0, ed=0.0, es=0.0, r=0.0, d=0.0, d1=0.0, - terrain, azimuth, distance, dheight=0.0, minterrain=100000.0, - minearth=100000.0, miny, maxy, min2y, max2y; - struct site remote; - FILE *fd=NULL, *fd1=NULL, *fd2=NULL, *fd3=NULL, *fd4=NULL, *fd5=NULL; - - ReadPath(destination,source); /* destination=RX, source=TX */ - azimuth=Azimuth(destination,source); - distance=Distance(destination,source); - refangle=ElevationAngle(destination,source); - b=GetElevation(destination)+destination.alt+earthradius; - - /* Wavelength and path distance (great circle) in feet. */ - - //LR.frq_mhz = freq; - - if (fresnel_plot) - { - lambda=9.8425e8/(LR.frq_mhz*1e6); - d=5280.0*path.distance[path.length-1]; - } - - - - if (normalized) - { - ed=GetElevation(destination); - es=GetElevation(source); - nb=-destination.alt-ed; - nm=(-source.alt-es-nb)/(path.distance[path.length-1]); - } - - fd=fopen("profile.gp","wb"); - - if (clutter>0.0) - fd1=fopen("clutter.gp","wb"); - - fd2=fopen("reference.gp","wb"); - fd5=fopen("curvature.gp", "wb"); - - if ((LR.frq_mhz>=20.0) && (LR.frq_mhz<=100000.0) && fresnel_plot) - { - fd3=fopen("fresnel.gp", "wb"); - fd4=fopen("fresnel_pt_6.gp", "wb"); - } - - - for (x=0; x=20.0) && (LR.frq_mhz<=100000.0) && fresnel_plot) - { - d1=5280.0*path.distance[x]; - f_zone=-1.0*sqrt(lambda*d1*(d-d1)/d); - fpt6_zone=f_zone*fzone_clearance; - } - - if (normalized) - { - r=-(nm*path.distance[x])-nb; - height+=r; - - if ((LR.frq_mhz>=20.0) && (LR.frq_mhz<=100000.0) && fresnel_plot) - { - f_zone+=r; - fpt6_zone+=r; - } - } - - else - r=0.0; - - - - if (metric) - { - - //segfault here - fprintf(fd,"%f\t%f\n",KM_PER_MILE*path.distance[x],METERS_PER_FOOT*height); - - - - if (fd1!=NULL && x>0 && x0 && x=20.0) && (LR.frq_mhz<=100000.0) && fresnel_plot) - { - if (metric) - { - fprintf(fd3,"%f\t%f\n",KM_PER_MILE*path.distance[x],METERS_PER_FOOT*f_zone); - fprintf(fd4,"%f\t%f\n",KM_PER_MILE*path.distance[x],METERS_PER_FOOT*fpt6_zone); - } - - else - { - fprintf(fd3,"%f\t%f\n",path.distance[x],f_zone); - fprintf(fd4,"%f\t%f\n",path.distance[x],fpt6_zone); - } - - if (f_zonemaxheight) - maxheight=height+clutter; - - if (heightmaxheight) - maxheight=r; - - if (terrain=20.0) && (LR.frq_mhz<=100000.0) && fresnel_plot) - { - if (metric) - { - fprintf(fd3,"%f\t%f\n",KM_PER_MILE*path.distance[path.length-1],METERS_PER_FOOT*r); - fprintf(fd4,"%f\t%f\n",KM_PER_MILE*path.distance[path.length-1],METERS_PER_FOOT*r); - } - - else - { - fprintf(fd3,"%f\t%f\n",path.distance[path.length-1],r); - fprintf(fd4,"%f\t%f\n",path.distance[path.length-1],r); - } - } - - if (r>maxheight) - maxheight=r; - - if (r=20.0) && (LR.frq_mhz<=100000.0) && fresnel_plot) - { - fclose(fd3); - fclose(fd4); - } - - - - if (name[0]=='.') - { - /* Default filename and output file type */ - - strncpy(basename,"profile\0",8); - strncpy(term,"png\0",4); - strncpy(ext,"png\0",4); - } - - else - { - /* Extract extension and terminal type from "name" */ - - ext[0]=0; - y=strlen(name); - strncpy(basename,name,254); - - for (x=y-1; x>0 && name[x]!='.'; x--); - - if (x>0) /* Extension found */ - { - for (z=x+1; z<=y && (z-(x+1))<10; z++) - { - ext[z-(x+1)]=tolower(name[z]); - term[z-(x+1)]=name[z]; - } - - ext[z-(x+1)]=0; /* Ensure an ending 0 */ - term[z-(x+1)]=0; - basename[x]=0; - } - - if (ext[0]==0) /* No extension -- Default is png */ - { - strncpy(term,"png\0",4); - strncpy(ext,"png\0",4); - } - } - - /* Either .ps or .postscript may be used - as an extension for postscript output. */ - - - if (strncmp(term,"postscript",10)==0) - strncpy(ext,"ps\0",3); - - else if (strncmp(ext,"ps",2)==0) - strncpy(term,"postscript enhanced color\0",26); - - fd=fopen("ppa.gp","w"); - - dheight=maxheight-minheight; - miny=minheight-0.15*dheight; - maxy=maxheight+0.05*dheight; - - if (maxy<20.0) - maxy=20.0; - - dheight=maxheight-minheight; - min2y=miny-minterrain+0.05*dheight; - - if (minearth=20.0) && (LR.frq_mhz<=100000.0) && fresnel_plot) - fprintf(fd,"set title \"Link from %s to %s (%.2f%c)\"\n",source.name, destination.name, azimuth,176); - - else - fprintf(fd,"set title Height Profile Between %s and %s (%.2f%c)\"\n", source.name, destination.name, azimuth,176); - - if (metric) - fprintf(fd,"set xlabel \"Distance: %.2f Km Path Loss: %.2f dB Received Power: %.2f dBm\"\n",KM_PER_MILE*Distance(source,destination),loss,dBm); - else - fprintf(fd,"set xlabel \"Distance: %.2f Mi Path Loss: %.2f dB Received Power: %.2f dBm\"\n",Distance(source,destination),loss,dBm); - - - if (metric) - fprintf(fd,"set ylabel \"Height (meters)\"\n"); - - else - fprintf(fd,"set ylabel \"Height (feet)\"\n"); - - - fprintf(fd,"set output \"%s.%s\"\n",basename,ext); - - if ((LR.frq_mhz>=20.0) && (LR.frq_mhz<=100000.0) && fresnel_plot) - { - if (clutter>0.0) - { - if (metric) - fprintf(fd,"plot \"profile.gp\" title \"Terrain Profile\" with filledcurve x1 lt 1 lc rgb \"#cbbc8a\", \"clutter.gp\" title \"Ground Clutter (%.2f meters)\" with lines, \"reference.gp\" title \"Line of Sight\" with lines, \"curvature.gp\" axes x1y2 title \"Earth's Curvature\" with lines, \"fresnel.gp\" axes x1y1 title \"First Fresnel Zone (%.3f MHz)\" with lines, \"fresnel_pt_6.gp\" title \"%.0f%% of Fresnel Zone\" with lines\n",clutter*METERS_PER_FOOT,LR.frq_mhz,fzone_clearance*100.0); - else - fprintf(fd,"plot \"profile.gp\" title \"Terrain Profile\" with filledcurve x1 lt 1 lc rgb \"#cbbc8a\", \"clutter.gp\" title \"Ground Clutter (%.2f feet)\" with lines, \"reference.gp\" title \"Line of Sight\" with lines, \"curvature.gp\" axes x1y2 title \"Earth's Curvature\" with lines, \"fresnel.gp\" axes x1y1 title \"First Fresnel Zone (%.3f MHz)\" with lines, \"fresnel_pt_6.gp\" title \"%.0f%% of Fresnel Zone\" with lines\n",clutter,LR.frq_mhz,fzone_clearance*100.0); - } - - else - fprintf(fd,"plot \"profile.gp\" title \"Terrain\" with filledcurve x1 lt 1 lc rgb \"#cbbc8a\", \"reference.gp\" title \"Line of sight\" with lines, \"curvature.gp\" axes x1y2 title \"Earth's Curvature\" with lines, \"fresnel.gp\" axes x1y1 title \"Fresnel Zone for %.1f MHz\" with lines, \"fresnel_pt_6.gp\" title \"%.0f%% of Fresnel Zone\" with lines\n",LR.frq_mhz,fzone_clearance*100.0); - } - - else - { - - - - } - - fclose(fd); - - x=system("gnuplot ppa.gp"); - - - - if (x!=-1) - { - if (gpsav==0) - { - //unlink("ppa.gp"); - //unlink("profile.gp"); - //unlink("reference.gp"); - //unlink("curvature.gp"); - - if (fd1!=NULL) - unlink("clutter.gp"); - - if ((LR.frq_mhz>=20.0) && (LR.frq_mhz<=100000.0) && fresnel_plot) - { - //unlink("fresnel.gp"); - //unlink("fresnel_pt_6.gp"); - } - } - - - } - - - else - fprintf(stderr,"\n*** ERROR: Error occurred invoking gnuplot!\n"); -} - -void ObstructionAnalysis(struct site xmtr, struct site rcvr, double f, FILE *outfile) -{ - /* Perform an obstruction analysis along the - path between receiver and transmitter. */ - - int x; - struct site site_x; - double h_r, h_t, h_x, h_r_orig, cos_tx_angle, cos_test_angle, - cos_tx_angle_f1, cos_tx_angle_fpt6, d_tx, d_x, - h_r_f1, h_r_fpt6, h_f, h_los, lambda=0.0; - char string[255], string_fpt6[255], string_f1[255]; - - 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); - - if (clutter>0.0) - { - fprintf(outfile,"Terrain has been raised by"); - - if (metric) - fprintf(outfile," %.2f meters",METERS_PER_FOOT*clutter); - else - fprintf(outfile," %.2f feet",clutter); - - fprintf(outfile," to account for ground clutter.\n\n"); - } - - /* 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 ppa!'s 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+clutter; - 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(outfile,"Between %s and %s, %s detected obstructions at:\n\n",rcvr.name,xmtr.name,ppa_name); - - if (site_x.lat>=0.0) - { - if (metric) - fprintf(outfile," %8.4f N,%9.4f W, %5.2f kilometers, %6.2f meters AMSL\n",site_x.lat, site_x.lon, KM_PER_MILE*(d_x/5280.0), METERS_PER_FOOT*(h_x-earthradius)); - else - fprintf(outfile," %8.4f N,%9.4f W, %5.2f miles, %6.2f feet AMSL\n",site_x.lat, site_x.lon, d_x/5280.0, h_x-earthradius); - } - - else - { - if (metric) - fprintf(outfile," %8.4f S,%9.4f W, %5.2f kilometers, %6.2f meters AMSL\n",-site_x.lat, site_x.lon, KM_PER_MILE*(d_x/5280.0), METERS_PER_FOOT*(h_x-earthradius)); - else - - fprintf(outfile," %8.4f S,%9.4f W, %5.2f miles, %6.2f feet AMSL\n",-site_x.lat, site_x.lon, d_x/5280.0, h_x-earthradius); - } - } - - 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... */ - - 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_fh_r_orig) - { - if (metric) - snprintf(string,150,"\nAntenna at %s must be raised to at least %.2f meters AGL\nto clear all obstructions detected by %s.\n",rcvr.name, METERS_PER_FOOT*(h_r-GetElevation(rcvr)-earthradius),ppa_name); - else - snprintf(string,150,"\nAntenna at %s must be raised to at least %.2f feet AGL\nto clear all obstructions detected by %s.\n",rcvr.name, h_r-GetElevation(rcvr)-earthradius,ppa_name); - } - - else - snprintf(string,150,"\nNo obstructions to LOS path due to terrain were detected by %s\n",ppa_name); - - if (f) - { - if (h_r_fpt6>h_r_orig) - { - if (metric) - snprintf(string_fpt6,150,"\nAntenna at %s must be raised to at least %.2f meters AGL\nto clear %.0f%c of the first Fresnel zone.\n",rcvr.name, METERS_PER_FOOT*(h_r_fpt6-GetElevation(rcvr)-earthradius),fzone_clearance*100.0,37); - - else - snprintf(string_fpt6,150,"\nAntenna at %s must be raised to at least %.2f feet AGL\nto clear %.0f%c of the first Fresnel zone.\n",rcvr.name, h_r_fpt6-GetElevation(rcvr)-earthradius,fzone_clearance*100.0,37); - } - - else - snprintf(string_fpt6,150,"\n%.0f%c of the first Fresnel zone is clear.\n",fzone_clearance*100.0,37); - - if (h_r_f1>h_r_orig) - { - if (metric) - snprintf(string_f1,150,"\nAntenna at %s must be raised to at least %.2f meters AGL\nto clear the first Fresnel zone.\n",rcvr.name, METERS_PER_FOOT*(h_r_f1-GetElevation(rcvr)-earthradius)); - - else - snprintf(string_f1,150,"\nAntenna at %s must be raised to at least %.2f feet AGL\nto clear the first Fresnel zone.\n",rcvr.name, h_r_f1-GetElevation(rcvr)-earthradius); - - } - - else - snprintf(string_f1,150,"\nThe first Fresnel zone is clear.\n"); - } - - fprintf(outfile,"%s",string); - - if (f) - { - fprintf(outfile,"%s",string_f1); - fprintf(outfile,"%s",string_fpt6); - } - - -} - -void PathReport(struct site source, struct site destination, char *name, char graph_it) -{ - /* This function writes a PPA Path Report (name.txt) to - the filesystem. If (graph_it == 1), then gnuplot is invoked - to generate an appropriate output file indicating the Longley-Rice - model loss between the source and destination locations. - "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, .png is assumed. */ - - int x, y, z, errnum; - char basename[255], term[30], ext[15], strmode[100], - report_name[80], block=0; - double maxloss=-100000.0, minloss=100000.0, haavt, - angle1, angle2, azimuth, pattern=1.0, patterndB=0.0, - total_loss=0.0, cos_xmtr_angle, cos_test_angle=0.0, - source_alt, test_alt, dest_alt, source_alt2, dest_alt2, - distance, elevation, four_thirds_earth, - free_space_loss=0.0, eirp=0.0, voltage, rxp, power_density; - FILE *fd=NULL, *fd2=NULL; - - //sprintf(report_name,"%s.txt",*name); - snprintf(report_name,80,"%s.txt%c",name,0); - - - - four_thirds_earth=FOUR_THIRDS*EARTHRADIUS; - - /*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]='_'; */ - - fd2=fopen(report_name,"w"); - - fprintf(fd2,"\n\t\t--==[ %s v%s Path Analysis ]==--\n\n",ppa_name,ppa_version); - //fprintf(fd2,"%s\n\n",dashes); - fprintf(fd2,"Transmitter site: %s\n",source.name); - - if (source.lat>=0.0) - { - fprintf(fd2,"Site location: %.4f North / %.4f West",source.lat, source.lon); - //fprintf(fd2, " (%s N / ", source.lat); - } - - else - { - - fprintf(fd2,"Site location: %.4f South / %.4f West",-source.lat, source.lon); - //fprintf(fd2, " (%s S / ", source.lat); - } - - //fprintf(fd2, "%s W)\n", source.lon); - - if (metric) - { - fprintf(fd2,"Ground elevation: %.2f meters AMSL\n",METERS_PER_FOOT*GetElevation(source)); - fprintf(fd2,"Antenna height: %.2f meters AGL / %.2f meters AMSL\n",METERS_PER_FOOT*source.alt,METERS_PER_FOOT*(source.alt+GetElevation(source))); - } - - else - { - 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)); - } - -/* - haavt=haat(source); - - if (haavt>-4999.0) - { - if (metric) - fprintf(fd2,"Antenna height above average terrain: %.2f meters\n",METERS_PER_FOOT*haavt); - else - fprintf(fd2,"Antenna height above average terrain: %.2f feet\n",haavt); - } -*/ - azimuth=Azimuth(source,destination); - angle1=ElevationAngle(source,destination); - angle2=ElevationAngle2(source,destination,earthradius); - - if (got_azimuth_pattern || got_elevation_pattern) - { - x=(int)rint(10.0*(10.0-angle2)); - - if (x>=0 && x<=1000) - pattern=(double)LR.antenna_pattern[(int)rint(azimuth)][x]; - - patterndB=20.0*log10(pattern); - } - - if (metric) - fprintf(fd2,"Distance to %s: %.2f kilometers\n",destination.name,KM_PER_MILE*Distance(source,destination)); - - else - fprintf(fd2,"Distance to %s: %.2f miles\n",destination.name,Distance(source,destination)); - - fprintf(fd2,"Azimuth to %s: %.2f degrees\n",destination.name,azimuth); - - if (angle1>=0.0) - fprintf(fd2,"Elevation angle to %s: %+.4f degrees\n",destination.name,angle1); - - else - fprintf(fd2,"Depression angle to %s: %+.4f degrees\n",destination.name,angle1); - - if ((angle2-angle1)>0.0001) - { - if (angle2<0.0) - fprintf(fd2,"Depression"); - else - fprintf(fd2,"Elevation"); - - //fprintf(fd2," angle to the first obstruction: %+.4f degrees\n",angle2); - } - - //fprintf(fd2,"\n%s\n\n",dashes); - - /* Receiver */ - - fprintf(fd2,"Receiver site: %s\n",destination.name); - - if (destination.lat>=0.0) - { - fprintf(fd2,"Site location: %.4f North / %.4f West",destination.lat, destination.lon); - //fprintf(fd2, " (%s N / ", destination.lat); - } - - else - { - fprintf(fd2,"Site location: %.4f South / %.4f West",-destination.lat, destination.lon); - //fprintf(fd2, " (%s S / ", destination.lat); - } - - //fprintf(fd2, "%s W)\n", destination.lon); - - if (metric) - { - fprintf(fd2,"Ground elevation: %.2f meters AMSL\n",METERS_PER_FOOT*GetElevation(destination)); - fprintf(fd2,"Antenna height: %.2f meters AGL / %.2f meters AMSL\n",METERS_PER_FOOT*destination.alt, METERS_PER_FOOT*(destination.alt+GetElevation(destination))); - } - - else - { - 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)); - } - - /*haavt=haat(destination); - - if (haavt>-4999.0) - { - if (metric) - fprintf(fd2,"Antenna height above average terrain: %.2f meters\n",METERS_PER_FOOT*haavt); - else - fprintf(fd2,"Antenna height above average terrain: %.2f feet\n",haavt); - }*/ - - if (metric) - fprintf(fd2,"Distance to %s: %.2f kilometers\n",source.name,KM_PER_MILE*Distance(source,destination)); - - else - fprintf(fd2,"Distance to %s: %.2f miles\n",source.name,Distance(source,destination)); - - azimuth=Azimuth(destination,source); - - angle1=ElevationAngle(destination,source); - angle2=ElevationAngle2(destination,source,earthradius); - - fprintf(fd2,"Azimuth to %s: %.2f degrees\n",source.name,azimuth); - - if (angle1>=0.0) - fprintf(fd2,"Elevation angle to %s: %+.4f degrees\n",source.name,angle1); - - else - fprintf(fd2,"Depression angle to %s: %+.4f degrees\n",source.name,angle1); - - if ((angle2-angle1)>0.0001) - { - if (angle2<0.0) - fprintf(fd2,"Depression"); - else - fprintf(fd2,"Elevation"); - - //fprintf(fd2," angle to the first obstruction: %+.4f degrees\n",angle2); - } - - //fprintf(fd2,"\n%s\n\n",dashes); - - if (LR.frq_mhz>0.0) - { - fprintf(fd2,"Longley-Rice path calculation parameters used in this analysis:\n\n"); - fprintf(fd2,"Earth's Dielectric Constant: %.3lf\n",LR.eps_dielect); - fprintf(fd2,"Earth's Conductivity: %.3lf Siemens/meter\n",LR.sgm_conductivity); - fprintf(fd2,"Atmospheric Bending Constant (N-units): %.3lf ppm\n",LR.eno_ns_surfref); - fprintf(fd2,"Frequency: %.3lf MHz\n",LR.frq_mhz); - fprintf(fd2,"Radio Climate: %d (",LR.radio_climate); - - switch (LR.radio_climate) - { - case 1: - fprintf(fd2,"Equatorial"); - break; - - case 2: - fprintf(fd2,"Continental Subtropical"); - break; - - case 3: - fprintf(fd2,"Maritime Subtropical"); - break; - - case 4: - fprintf(fd2,"Desert"); - break; - - case 5: - fprintf(fd2,"Continental Temperate"); - break; - - case 6: - fprintf(fd2,"Martitime Temperate, Over Land"); - break; - - case 7: - fprintf(fd2,"Maritime Temperate, Over Sea"); - break; - - default: - fprintf(fd2,"Unknown"); - } - - fprintf(fd2,")\nPolarisation: %d (",LR.pol); - - if (LR.pol==0) - fprintf(fd2,"Horizontal"); - - if (LR.pol==1) - fprintf(fd2,"Vertical"); - - fprintf(fd2,")\nFraction of Situations: %.1lf%c\n",LR.conf*100.0,37); - fprintf(fd2,"Fraction of Time: %.1lf%c\n",LR.rel*100.0,37); - - if (LR.erp!=0.0) - { - fprintf(fd2,"Transmitter ERP: "); - - if (LR.erp<1.0) - fprintf(fd2,"%.1lf milliwatts",1000.0*LR.erp); - - if (LR.erp>=1.0 && LR.erp<10.0) - fprintf(fd2,"%.1lf Watts",LR.erp); - - if (LR.erp>=10.0 && LR.erp<10.0e3) - fprintf(fd2,"%.0lf Watts",LR.erp); - - if (LR.erp>=10.0e3) - fprintf(fd2,"%.3lf kilowatts",LR.erp/1.0e3); - - dBm=10.0*(log10(LR.erp*1000.0)); - fprintf(fd2," (%+.2f dBm)\n",dBm); - - /* EIRP = ERP + 2.14 dB */ - - fprintf(fd2,"Transmitter EIRP: "); - - eirp=LR.erp*1.636816521; - - if (eirp<1.0) - fprintf(fd2,"%.1lf milliwatts",1000.0*eirp); - - if (eirp>=1.0 && eirp<10.0) - fprintf(fd2,"%.1lf Watts",eirp); - - if (eirp>=10.0 && eirp<10.0e3) - fprintf(fd2,"%.0lf Watts",eirp); - - if (eirp>=10.0e3) - fprintf(fd2,"%.3lf kilowatts",eirp/1.0e3); - - dBm=10.0*(log10(eirp*1000.0)); - fprintf(fd2," (%+.2f dBm)\n",dBm); - } - - fprintf(fd2,"\n%s\n\n",dashes); - - fprintf(fd2,"Summary for the link between %s and %s:\n\n",source.name, destination.name); - - if (patterndB!=0.0) - fprintf(fd2,"%s antenna pattern towards %s: %.3f (%.2f dB)\n", source.name, destination.name, pattern, patterndB); - - ReadPath(source, destination); /* source=TX, destination=RX */ - - /* Copy elevations plus clutter along - path into the elev[] array. */ - - for (x=1; x=cos_test_angle) - block=1; - } - - /* At this point, we have the elevation angle - to the first obstruction (if it exists). */ - } - - /* Determine path loss for each point along the - path using Longley-Rice's point_to_point mode - starting at x=2 (number_of_points = 1), the - shortest distance terrain can play a role in - path loss. */ - - elev[0]=y-1; /* (number of points - 1) */ - - /* Distance between elevation samples */ - - elev[1]=METERS_PER_MILE*(path.distance[y]-path.distance[y-1]); - - point_to_point(elev, source.alt*METERS_PER_FOOT, - destination.alt*METERS_PER_FOOT, LR.eps_dielect, - LR.sgm_conductivity, LR.eno_ns_surfref, LR.frq_mhz, - LR.radio_climate, LR.pol, LR.conf, LR.rel, loss, - strmode, errnum); - - if (block) - elevation=((acos(cos_test_angle))/DEG2RAD)-90.0; - else - elevation=((acos(cos_xmtr_angle))/DEG2RAD)-90.0; - - /* Integrate the antenna's radiation - pattern into the overall path loss. */ - - x=(int)rint(10.0*(10.0-elevation)); - - if (x>=0 && x<=1000) - { - pattern=(double)LR.antenna_pattern[(int)azimuth][x]; - - if (pattern!=0.0) - patterndB=20.0*log10(pattern); - } - - else - patterndB=0.0; - - total_loss=loss-patterndB; - - if (metric) - fprintf(fd,"%f\t%f\n",KM_PER_MILE*(path.distance[path.length-1]-path.distance[y]),total_loss); - - else - fprintf(fd,"%f\t%f\n",path.distance[path.length-1]-path.distance[y],total_loss); - - if (total_loss>maxloss) - maxloss=total_loss; - - if (total_loss0 && name[x]!='.'; x--); - - if (x>0) /* Extension found */ - { - for (z=x+1; z<=y && (z-(x+1))<10; z++) - { - ext[z-(x+1)]=tolower(name[z]); - term[z-(x+1)]=name[z]; - } - - ext[z-(x+1)]=0; /* Ensure an ending 0 */ - term[z-(x+1)]=0; - basename[x]=0; - } - } - - if (ext[0]==0) /* No extension -- Default is png */ - { - strncpy(term,"png\0",4); - strncpy(ext,"png\0",4); - } - - /* Either .ps or .postscript may be used - as an extension for postscript output. */ - - if (strncmp(term,"postscript",10)==0) - strncpy(ext,"ps\0",3); - - else if (strncmp(ext,"ps",2)==0) - strncpy(term,"postscript enhanced color\0",26); - - fd=fopen("ppa.gp","w"); - - fprintf(fd,"set grid\n"); - fprintf(fd,"set yrange [%2.3f to %2.3f]\n", minloss, maxloss); - fprintf(fd,"set encoding iso_8859_1\n"); - fprintf(fd,"set term %s\n",term); - fprintf(fd,"set title \"%s Loss Profile Along Path Between %s and %s (%.2f%c azimuth)\"\n",ppa_name, destination.name, source.name, Azimuth(destination,source),176); - - if (metric) - fprintf(fd,"set xlabel \"Distance Between %s and %s (%.2f kilometers)\"\n",destination.name,source.name,KM_PER_MILE*Distance(destination,source)); - else - fprintf(fd,"set xlabel \"Distance Between %s and %s (%.2f miles)\"\n",destination.name,source.name,Distance(destination,source)); - - if (got_azimuth_pattern || got_elevation_pattern) - fprintf(fd,"set ylabel \"Total Path Loss (including TX antenna pattern) (dB)"); - else - fprintf(fd,"set ylabel \"Longley-Rice Path Loss (dB)"); - - fprintf(fd,"\"\nset output \"%s.%s\"\n",basename,ext); - fprintf(fd,"plot \"profile.gp\" title \"Path Loss\" with lines\n"); - - fclose(fd); - - x=system("gnuplot ppa.gp"); - - if (x!=-1) - { - if (gpsav==0) - { - //unlink("ppa.gp"); - //unlink("profile.gp"); - //unlink("reference.gp"); - } - - - } - - else - fprintf(stderr,"\n*** ERROR: Error occurred invoking gnuplot!\n"); - } - - - - -} - -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--==[ %s v%s Site Analysis Report For: %s ]==--\n\n",ppa_name, ppa_version, xmtr.name); - - fprintf(fd,"%s\n\n",dashes); - - if (xmtr.lat>=0.0) - { - fprintf(fd,"Site location: %.4f North / %.4f West",xmtr.lat, xmtr.lon); - fprintf(fd, " (%s N / ",xmtr.lat); - } - - else - { - fprintf(fd,"Site location: %.4f South / %.4f West",-xmtr.lat, xmtr.lon); - fprintf(fd, " (%s S / ",xmtr.lat); - } - - fprintf(fd, "%s W)\n",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%s\n\n",dashes); - fclose(fd); - -} - -void LoadTopoData(int max_lon, int min_lon, int max_lat, int min_lat, int winfiles) -{ - /* 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; - - if (winfiles==1){ - if (ippd==3600) - snprintf(string,19,"%d=%d=%d=%d=hd",x, x+1, ymin, ymax); - else - snprintf(string,16,"%d=%d=%d=%d",x, x+1, ymin, ymax); - - }else{ - if (ippd==3600) - snprintf(string,19,"%d:%d:%d:%d=hd",x, x+1, ymin, ymax); - else - snprintf(string,16,"%d:%d:%d:%d",x, x+1, ymin, ymax); - } - LoadSDF(string,winfiles); - } - } - - 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; - - if (winfiles==1){ - if (ippd==3600) - snprintf(string,19,"%d=%d=%d=%d=hd",x, x+1, ymin, ymax); - else - snprintf(string,16,"%d=%d=%d=%d",x, x+1, ymin, ymax); - - }else{ - if (ippd==3600) - snprintf(string,19,"%d:%d:%d:%d=hd",x, x+1, ymin, ymax); - else - snprintf(string,16,"%d:%d:%d:%d",x, x+1, ymin, ymax); - } - LoadSDF(string,winfiles); - } - } -} - - -int main(int argc, char *argv[]) -{ - int x, y, z=0, min_lat, min_lon, max_lat, max_lon, - rxlat, rxlon, txlat, txlon, width=1000, height=350, winfiles=0; - - unsigned char height_plot=0, norm=0, pt2pt_mode=1, - max_txsites, nolospath=0, nositereports=0, fresnel_plot=1; - - char header[80], height_file[255], return_height_file[255], - longley_file[255], string[255], ext[20]; - - double er_mult,txla=999, txlo=999, rxla=999, rxlo=999, txh=0, rxh=0; - - struct site tx_site[32], rx_site; - - strncpy(ppa_version,"1.3.1\0",5); - - strncpy(ppa_name,"PPA\0",4); - - strncpy(dashes,"---------------------------------------------------------------------------\0",76); - - if (argc==1) - { - fprintf(stdout,"\n\t\t --==[ %s v%s Available Options... ]==--\n\n",ppa_name, ppa_version); - - fprintf(stdout," -m Metric units (Default = Imperial)\n"); - fprintf(stdout," -tla Tx latitude)\n"); - fprintf(stdout," -tlo Tx longitude (W) Positive value 0 to 360)\n"); - fprintf(stdout," -th Tx Height)\n"); - fprintf(stdout," -rla Rx latitude)\n"); - fprintf(stdout," -rlo Rx longitude (W) Positive value 0 to 360)\n"); - fprintf(stdout," -rh Rx Height)\n"); - fprintf(stdout," -fz Fresnel zone clearance percentage (default = 60)\n"); - fprintf(stdout," -f frequency for Fresnel zone calculation (MHz)\n"); - fprintf(stdout," -w Effective radiated power in Watts. Default=0\n"); - fprintf(stdout," -p Polarisation. Default=1 (Vertical). 0=Horizontal\n"); - fprintf(stdout," -d sdf file directory path (overrides path in ~/.ppa_path file)\n"); - fprintf(stdout," -x PNG graph width (default = 800)\n"); - fprintf(stdout," -y PNG graph height (default = 200)\n"); - fprintf(stdout," -n Normalise graph\n"); - fprintf(stdout," -v Output value. 1=Path loss dB, 2=Rxd power dBm (default),3=Field strength dBuV/m\n"); - fprintf(stdout," -o PNG filename. Return PNG will be called $filename_R.png\n"); - fprintf(stdout," -wf Windows SDF tiles with equals not colons\n"); - - - } - - y=argc-1; - - metric=0; - sdf_path[0]=0; - path.length=0; - max_txsites=2; - fzone_clearance=0.6; - contour_threshold=0; - rx_site.lat=91.0; - rx_site.lon=361.0; - longley_file[0]=0; - earthradius=EARTHRADIUS; - - LR.eps_dielect=15.0; - LR.sgm_conductivity=0.005; - LR.eno_ns_surfref=301.0; - LR.frq_mhz=300.0; // - LR.radio_climate=5; - LR.pol=1; // - LR.conf=0.50; - LR.rel=0.50; - LR.erp=0.0; // - - ippd=IPPD; /* pixels per degree (integer) */ - ppd=(double)ippd; /* pixels per degree (double) */ - dpp=1.0/ppd; /* degrees per pixel */ - mpi=ippd-1; /* maximum pixel index per degree */ - - - for (x=0; x<4; x++) - { - tx_site[x].lat=91.0; - tx_site[x].lon=361.0; - } - - for (x=0; x1.0e6) - er_mult=1.0e6; - - earthradius*=er_mult; - } - } - - - if (strcmp(argv[x],"-fz")==0) - { - z=x+1; - - if (z<=y && argv[z][0] && argv[z][0]!='-') - { - sscanf(argv[z],"%lf",&fzone_clearance); - - if (fzone_clearance<0.0 || fzone_clearance>100.0) - fzone_clearance=60.0; - - fzone_clearance/=100.0; - } - } - - if (strcmp(argv[x],"-x")==0) - { - z=x+1; - - if (z<=y && argv[z][0] && argv[z][0]!='-') - { - sscanf(argv[z],"%d",&width); - - } - } - if (strcmp(argv[x],"-y")==0) - { - z=x+1; - - if (z<=y && argv[z][0] && argv[z][0]!='-') - { - sscanf(argv[z],"%d",&height); - - } - } - - - - - - if (strcmp(argv[x],"-o")==0) - { - z=x+1; - if (z<=y && argv[z][0] && argv[z][0]!='-') - { - strncpy(height_file,argv[z],253); - height_plot=1; - pt2pt_mode=1; - } - } - - - - - if (strcmp(argv[x],"-m")==0) - metric=1; - if (strcmp(argv[x],"-n")==0) - norm=1; - - if (strcmp(argv[x],"-d")==0) - { - z=x+1; - - if (z<=y && argv[z][0] && argv[z][0]!='-') - strncpy(sdf_path,argv[z],253); - } - - if (strcmp(argv[x],"-tla")==0) - { - /* Read Transmitter Lat */ - - z=x+1; - - if (z<=y && argv[z][0]) - { - //strncpy(txla,argv[z],253); - sscanf(argv[z],"%lf",&txla); - - } - - } - - if (strcmp(argv[x],"-tlo")==0) - { - /* Read Transmitter Lon */ - - z=x+1; - - if (z<=y && argv[z][0]) - { - //strncpy(txla,argv[z],253); - sscanf(argv[z],"%lf",&txlo); - - } - - } - - if (strcmp(argv[x],"-rla")==0) - { - /* Read Rx Lat */ - - z=x+1; - - if (z<=y && argv[z][0]) - { - //strncpy(txla,argv[z],253); - sscanf(argv[z],"%lf",&rxla); - - } - - } - if (strcmp(argv[x],"-rlo")==0) - { - /* Read Rx lon */ - - z=x+1; - - if (z<=y && argv[z][0]) - { - //strncpy(txla,argv[z],253); - sscanf(argv[z],"%lf",&rxlo); - - } - - } - - - - if (strcmp(argv[x],"-th")==0) - { - /* Read Transmitter height */ - - z=x+1; - - if (z<=y && argv[z][0] && argv[z][0]!='-') - { - //strncpy(txla,argv[z],253); - sscanf(argv[z],"%lf",&txh); - - } - - - } - - if (strcmp(argv[x],"-rh")==0) - { - /* Read Rx height */ - - z=x+1; - - if (z<=y && argv[z][0] && argv[z][0]!='-') - { - - sscanf(argv[z],"%lf",&rxh); - - } - - } - - - - if (strcmp(argv[x],"-f")==0) - { - z=x+1; - - if (z<=y && argv[z][0] && argv[z][0]!='-') - { - sscanf(argv[z],"%lf",&LR.frq_mhz); - - if (LR.frq_mhz<20.0) - LR.frq_mhz=0.0; - - if (LR.frq_mhz>100.0e3) - LR.frq_mhz=100.0e3; - } - } - - if (strcmp(argv[x],"-p")==0) - { - z=x+1; - - if (z<=y && argv[z][0] && argv[z][0]!='-') - { - sscanf(argv[z],"%d",&LR.pol); - - } - } - - if (strcmp(argv[x],"-w")==0) - { - z=x+1; - - if (z<=y && argv[z][0] && argv[z][0]>0) - { - sscanf(argv[z],"%lf",&LR.erp); - - } - } - - if (strcmp(argv[x],"-v")==0) - { - z=x+1; - - if (z<=y && argv[z][0] && argv[z][0]>0) - { - sscanf(argv[z],"%d",&output); - - } - } - - //Windows friendly SDF filenames - if (strcmp(argv[x],"-wf")==0) - { - z=x+1; - - winfiles=1; - } - -} - - - -if(txla==999 || txlo==999 || rxla==999 || rxlo==999){ -fprintf(stdout,"\nERROR: BAD LOCATION GIVEN\n"); -exit(1); -} - -if (metric) - { - txh/=METERS_PER_FOOT; /* meters --> feet */ - rxh/=METERS_PER_FOOT; /* kilometers --> miles */ - } - -// populate sites -tx_site[0].lat = txla; -tx_site[0].lon = txlo; -tx_site[0].alt = txh; - - - - -strncpy(tx_site[0].name,"A",2); - -rx_site.lat = rxla; -rx_site.lon = rxlo; -rx_site.alt = rxh; - -strncpy(rx_site.name,"B",2); - - -// 100 mile limit -if(Distance(tx_site[0],rx_site) > 100){ -fprintf(stdout,"\nToo far!\n"); -fflush(stdout); -exit(1); -} - - - if (sdf_path[0]) - { - x=strlen(sdf_path); - - if (sdf_path[x-1]!='/' && x!=0) - { - sdf_path[x]='/'; - sdf_path[x+1]=0; - } - } - - fprintf(stdout,"%s",header); - fflush(stdout); - - x=0; - y=0; - - min_lat=90; - max_lat=-90; - - min_lon=(int)floor(tx_site[0].lon); - max_lon=(int)floor(tx_site[0].lon); - - - txlat=(int)floor(tx_site[0].lat); - txlon=(int)floor(tx_site[0].lon); - - if (txlatmax_lat) - max_lat=txlat; - - if (LonDiff(txlon,min_lon)<0.0) - min_lon=txlon; - - if (LonDiff(txlon,max_lon)>=0.0) - max_lon=txlon; - - - 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; - - /* Load the required SDF files */ - - LoadTopoData(max_lon, min_lon, max_lat, min_lat, winfiles); - - - //PlaceMarker(rx_site); - - if (height_plot) - { - /* Extract extension (if present) - from "height_file" */ - - y=strlen(height_file); - - for (x=y-1; x>0 && height_file[x]!='.'; x--); - - if (x>0) /* Extension found */ - { - for (z=x+1; z<=y && (z-(x+1))<10; z++) - ext[z-(x+1)]=tolower(height_file[z]); - - ext[z-(x+1)]=0; /* Ensure an ending 0 */ - height_file[x]=0; /* Chop off extension */ - } - - else - strncpy(ext,"png\0",4); - } - - - //Plot it - PlotPath(tx_site[0],rx_site,1); - PathReport(tx_site[0],rx_site,height_file,0); - //add .png extension to filename - snprintf(string,250,"%s.%s%c",height_file,ext,0); - GraphHeight(tx_site[0],rx_site,string,fresnel_plot,norm,width,height); - - double outvalue=dBm; - char* outunit="dBm"; - - if(output==1){ - outvalue=loss; - outunit="dB"; - } - if(output==3){ - outvalue=field_strength; - outunit="dBuV/m"; - } - fprintf(stdout,"%.2f %s\n",outvalue,outunit); - fflush(stdout); - - //RETURN PATH - //Plot it - PlotPath(rx_site,tx_site[0],1); - snprintf(return_height_file,250,"%s_R%c",height_file,0); - PathReport(rx_site,tx_site[0],return_height_file,0); - //add .png extension to filename_R - snprintf(string,250,"%s_R.%s%c",height_file,ext,0); - GraphHeight(rx_site,tx_site[0],string,fresnel_plot,norm,width,height); - - outvalue=dBm; - outunit="dBm"; - if(output==1){ - outvalue=loss; - outunit="dB"; - } - if(output==3){ - outvalue=field_strength; - outunit="dBuV/m"; - } - fprintf(stdout,"%.2f %s\n",outvalue,outunit); - fflush(stdout); - - return 0; -} - diff --git a/signalserver b/signalserver deleted file mode 100755 index 35a7379..0000000 Binary files a/signalserver and /dev/null differ diff --git a/signalserverHD b/signalserverHD deleted file mode 100755 index bc544e0..0000000 Binary files a/signalserverHD and /dev/null differ diff --git a/test.sh b/test.sh index 887ecab..4ffd9e1 100755 --- a/test.sh +++ b/test.sh @@ -1,6 +1,6 @@ #!/bin/bash mkdir test -RAD=10 +RAD=5 MAXRAD=30 FRQ=800 ERP=20 @@ -11,6 +11,13 @@ while [ $RAD -lt $MAXRAD ]; do convert test/$RAD.ppm test/$RAD.png rm test/$RAD.ppm rm test/$RAD.*cf - let RAD=RAD+5 + + echo "Calculating $FRQ MHz @ $ERP Watts for $RAD km radius (HD mode)..." + time ./signalserverHD -m -d /var/SRTM1 -lat 51.47 -lon -1.50 -txh 15 -gc 2 -rxh 2 -m -dbm -rt -120 -R $RAD -erp $ERP -f $FRQ -o test/$RAD.hd -pm 1 -res 3600 -t + convert test/$RAD.hd.ppm test/$RAD.hd.png + rm test/$RAD.hd.ppm + rm test/$RAD.*cf + let RAD=RAD+5 done + diff --git a/test/10.png b/test/10.png deleted file mode 100644 index 5a369d2..0000000 Binary files a/test/10.png and /dev/null differ diff --git a/test/15.png b/test/15.png deleted file mode 100644 index 06d9387..0000000 Binary files a/test/15.png and /dev/null differ diff --git a/test/20.png b/test/20.png deleted file mode 100644 index 18b64b6..0000000 Binary files a/test/20.png and /dev/null differ diff --git a/test/25.png b/test/25.png deleted file mode 100644 index e60ffa1..0000000 Binary files a/test/25.png and /dev/null differ