forked from ExternalVendorCode/Signal-Server
2.5 - Major refactor
This commit is contained in:
7
models/README
Normal file
7
models/README
Normal file
@@ -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
|
60
models/cost.cc
Normal file
60
models/cost.cc
Normal file
@@ -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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
|
||||
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;
|
||||
}
|
6
models/cost.hh
Normal file
6
models/cost.hh
Normal file
@@ -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_ */
|
26
models/ecc33.cc
Normal file
26
models/ecc33.cc
Normal file
@@ -0,0 +1,26 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
|
||||
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;
|
||||
}
|
6
models/ecc33.hh
Normal file
6
models/ecc33.hh
Normal file
@@ -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_ */
|
33
models/ericsson.cc
Normal file
33
models/ericsson.cc
Normal file
@@ -0,0 +1,33 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
|
||||
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;
|
||||
}
|
6
models/ericsson.hh
Normal file
6
models/ericsson.hh
Normal file
@@ -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_ */
|
30
models/fspl.cc
Normal file
30
models/fspl.cc
Normal file
@@ -0,0 +1,30 @@
|
||||
/*****************************************************************************
|
||||
* 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. *
|
||||
* *
|
||||
* 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 <math.h>
|
||||
|
||||
double FSPLpathLoss(float f, float d)
|
||||
{
|
||||
/*
|
||||
Free Space Path Loss model
|
||||
Frequency: Any
|
||||
Distance: Any
|
||||
*/
|
||||
//MHz to GHz
|
||||
f = f / 1000;
|
||||
|
||||
double dbloss = (20 * log10(d)) + (20 * log10(f)) + 92.45;
|
||||
|
||||
return dbloss;
|
||||
}
|
6
models/fspl.hh
Normal file
6
models/fspl.hh
Normal file
@@ -0,0 +1,6 @@
|
||||
#ifndef _FSPL_HH_
|
||||
#define _FSPL_HH_
|
||||
|
||||
double FSPLpathLoss(float f, float d);
|
||||
|
||||
#endif /* _FSPL_HH_ */
|
56
models/hata.cc
Normal file
56
models/hata.cc
Normal file
@@ -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 <math.h>
|
||||
|
||||
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;
|
||||
}
|
6
models/hata.hh
Normal file
6
models/hata.hh
Normal file
@@ -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_ */
|
1574
models/itm.cc
Normal file
1574
models/itm.cc
Normal file
File diff suppressed because it is too large
Load Diff
2963
models/itwom3.0.cc
Normal file
2963
models/itwom3.0.cc
Normal file
File diff suppressed because it is too large
Load Diff
14
models/itwom3.0.hh
Normal file
14
models/itwom3.0.hh
Normal file
@@ -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_ */
|
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);
|
||||
}
|
||||
}
|
||||
}
|
18
models/los.hh
Normal file
18
models/los.hh
Normal file
@@ -0,0 +1,18 @@
|
||||
#ifndef _LOS_HH_
|
||||
#define _LOS_HH_
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#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_ */
|
49
models/sui.cc
Normal file
49
models/sui.cc
Normal file
@@ -0,0 +1,49 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
|
||||
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;
|
||||
}
|
6
models/sui.hh
Normal file
6
models/sui.hh
Normal file
@@ -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_ */
|
Reference in New Issue
Block a user