forked from ExternalVendorCode/Signal-Server
2.5 - Major refactor
This commit is contained in:
864
models/los.cc
Normal file
864
models/los.cc
Normal file
@@ -0,0 +1,864 @@
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
|
||||
#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);
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user