Initial Commit

This commit is contained in:
N9OZB
2019-07-20 05:44:58 -05:00
parent b5116ba61e
commit 047c2f9d27
67 changed files with 18854 additions and 36 deletions

9
src/models/README Normal file
View File

@@ -0,0 +1,9 @@
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
Plane earth loss model taken from "Antennas and Propagation for Wireless systems" by Simon Saunders

61
src/models/cost.cc Normal file
View File

@@ -0,0 +1,61 @@
/*****************************************************************************
* 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) {
fprintf
(stderr,"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)
lRxH = log10(1.54 * RxH);
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
src/models/cost.hh Normal file
View 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_ */

33
src/models/ecc33.cc Normal file
View File

@@ -0,0 +1,33 @@
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
double ECC33pathLoss(float f, float TxH, float RxH, float d, int mode)
{
// Sanity check as this model operates within limited Txh/Rxh bounds
if(TxH-RxH<0){
RxH=RxH/(d*2);
}
/* if (f < 700 || f > 3500) {
fprintf(stderr,"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
src/models/ecc33.hh Normal file
View 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_ */

83
src/models/egli.cc Normal file
View File

@@ -0,0 +1,83 @@
/*****************************************************************************
* Egli VHF/UHF model for Signal Server by G6DTX *
* April 2017 *
* 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 v3 *
* for more details. *
******************************************************************************
Frequency 30 to 1000MHz
h1 = 1m and above
h2 = 1m and above
Distance 1 to 50km
http://people.seas.harvard.edu/~jones/es151/prop_models/propagation.html#pel
*/
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
//static float fcmin = 30.0;
//static float fcmax = 1000.0;
//static float dmin = 1.0;
//static float dmax = 50.0;
//static float h1min = 1.0;
//static float h2min = 1.0;
static __inline float _10log10f(float x)
{
return(4.342944f*logf(x));
}
double EgliPathLoss(float f, float h1, float h2, float d)
{
double Lp50 = NAN;
float C1, C2;
/* if ((f >= fcmin) && (f <= fcmax) &&
(h1 >= h1min) && (h2 >= h2min))
{*/
if (h1 > 10.0 && h2 > 10.0)
{
Lp50 = 85.9;
C1 = 2.0;
C2 = 2.0;
}
else if (h1 > 10.0)
{
Lp50 = 76.3;
C1 = 2.0;
C2 = 1.0;
}
else if (h2 > 10.0)
{
Lp50 = 76.3;
C1 = 1.0;
C2 = 2.0;
}
else // both antenna heights below 10 metres
{
Lp50 = 66.7;
C1 = 1.0;
C2 = 1.0;
} // end if
Lp50 += 4.0f*_10log10f(d) + 2.0f*_10log10f(f) - C1*_10log10f(h1) - C2*_10log10f(h2);
/*}
else
{
fprintf(stderr,"Parameter error: Egli path loss model f=%6.2f h1=%6.2f h2=%6.2f d=%6.2f\n", f, h1, h2, d);
exit(EXIT_FAILURE);
}*/
return(Lp50);
}

6
src/models/egli.hh Normal file
View File

@@ -0,0 +1,6 @@
#ifndef _EGLI_HH_
#define _EGLI_HH_
double EgliPathLoss(float f, float h1, float h2, float d);
#endif /* _EGLI_HH_ */

31
src/models/ericsson.cc Normal file
View File

@@ -0,0 +1,31 @@
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
double EricssonpathLoss(float f, float TxH, float RxH, float d, int mode)
{
/*
AKA Ericsson 9999 model
*/
// Urban
double a0 = 36.2, a1 = 30.2, a2 = -12, a3 = 0.1;
/* if (f < 150 || f > 1900) {
fprintf
(stderr,"Error: Ericsson9999 model frequency range 150-1900MHz\n");
exit(EXIT_FAILURE);
}
*/
if (mode == 2) { // Suburban / Med loss
a0 = 43.2;
a1 = 68.93;
}
if (mode == 1) { // Rural
a0 = 45.95;
a1 = 100.6;
}
double g1 = 3.2 * (log10(11.75 * RxH) * log10(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) - g1 + g2;
}

6
src/models/ericsson.hh Normal file
View 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_ */

33
src/models/fspl.cc Normal file
View File

@@ -0,0 +1,33 @@
/*****************************************************************************
* ITU-R P.525 Free Space Path Loss model for Signal Server by Alex Farrant *
* 15 January 2014 *
* optimised G6DTX April 2017 *
* 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. *
*
* https://www.itu.int/rec/R-REC-P.525/en
* Free Space Path Loss model
* Frequency: Any
* Distance: Any
*/
#include <math.h>
// use call with log/ln as this may be faster
// use constant of value 20.0/log(10.0)
static __inline float _20log10f(float x)
{
return(8.685889f*logf(x));
}
double FSPLpathLoss(float f, float d)
{
return(32.44 + _20log10f(f) + _20log10f(d));
}

6
src/models/fspl.hh Normal file
View File

@@ -0,0 +1,6 @@
#ifndef _FSPL_HH_
#define _FSPL_HH_
double FSPLpathLoss(float f, float d);
#endif /* _FSPL_HH_ */

58
src/models/hata.cc Normal file
View File

@@ -0,0 +1,58 @@
/*****************************************************************************
* 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;
float C_H;
float logf = log10(f);
if(f<200){
lh_M = log10(1.54 * h_M);
C_H = 8.29 * (lh_M * lh_M) - 1.1;
}else{
lh_M = log10(11.75 * h_M);
C_H = 3.2 * (lh_M * lh_M) - 4.97;
}
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
src/models/hata.hh Normal file
View 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
src/models/itm.cc Normal file

File diff suppressed because it is too large Load Diff

2964
src/models/itwom3.0.cc Normal file

File diff suppressed because it is too large Load Diff

14
src/models/itwom3.0.hh Normal file
View 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_ */

948
src/models/los.cc Normal file
View File

@@ -0,0 +1,948 @@
#include <stdio.h>
#include <math.h>
#include "../main.hh"
#include "los.hh"
#include "cost.hh"
#include "ecc33.hh"
#include "ericsson.hh"
#include "fspl.hh"
#include "hata.hh"
#include "itwom3.0.hh"
#include "sui.hh"
#include "pel.hh"
#include "egli.hh"
#include "soil.hh"
#include <pthread.h>
#define NUM_SECTIONS 4
namespace {
pthread_t threads[NUM_SECTIONS];
unsigned int thread_count = 0;
pthread_mutex_t maskMutex;
bool ***processed;
bool has_init_processed = false;
struct propagationRange {
double min_west, max_west, min_north, max_north;
double altitude;
bool eastwest, los, use_threads;
site source;
unsigned char mask_value;
FILE *fd;
int propmodel, knifeedge, pmenv;
};
void* rangePropagation(void *parameters)
{
propagationRange *v = (propagationRange*)parameters;
if(v->use_threads) {
alloc_elev();
alloc_path();
}
double minwest = dpp + (double)v->min_west;
double lon = v->eastwest ? minwest : v->min_west;
double lat = v->min_north;
int y = 0;
do {
if (lon >= 360.0)
lon -= 360.0;
site edge;
edge.lat = lat;
edge.lon = lon;
edge.alt = v->altitude;
if(v->los)
PlotLOSPath(v->source, edge, v->mask_value, v->fd);
else
PlotPropPath(v->source, edge, v->mask_value, v->fd, v->propmodel,
v->knifeedge, v->pmenv);
++y;
if(v->eastwest)
lon = minwest + (dpp * (double)y);
else
lat = (double)v->min_north + (dpp * (double)y);
} while ( v->eastwest
? (LonDiff(lon, (double)v->max_west) <= 0.0)
: (lat < (double)v->max_north) );
if(v->use_threads) {
free_elev();
free_path();
}
return NULL;
}
void init_processed()
{
int i;
int x;
int y;
processed = new bool **[MAXPAGES];
for (i = 0; i < MAXPAGES; i++) {
processed[i] = new bool *[ippd];
for (x = 0; x < ippd; x++)
processed[i][x] = new bool [ippd];
}
for (i = 0; i < MAXPAGES; i++) {
for (x = 0; x < ippd; x++) {
for (y = 0; y < ippd; y++)
processed[i][x][y] = false;
}
}
has_init_processed = true;
}
bool can_process(double lat, double lon)
{
/* 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;
bool rtn = false;
for (indx = 0, found = 0; indx < MAXPAGES && found == 0;) {
x = (int)rint(ppd * (lat - dem[indx].min_north));
y = mpi - (int)rint(yppd * (LonDiff(dem[indx].max_west, lon)));
if (x >= 0 && x <= mpi && y >= 0 && y <= mpi)
found = 1;
else
indx++;
}
if (found) {
/* As long as we only set this without resetting it we can
check outside a mutex first without worrying about race
conditions. But we must lock the mutex before updating the
value. */
if(!processed[indx][x][y]) {
pthread_mutex_lock(&maskMutex);
if(!processed[indx][x][y]) {
rtn = true;
processed[indx][x][y] = true;
}
pthread_mutex_unlock (&maskMutex);
}
}
return rtn;
}
void beginThread(void *arg)
{
if(!has_init_processed)
init_processed();
int rc = pthread_create(&threads[thread_count], NULL, rangePropagation, arg);
if (rc)
fprintf(stderr,"ERROR; return code from pthread_create() is %d\n", rc);
else
++thread_count;
}
void finishThreads()
{
void* status;
for(unsigned int i=0; i<thread_count; i++) {
int rc = pthread_join(threads[i], &status);
if (rc)
fprintf(stderr,"ERROR; return code from pthread_join() is %d\n", rc);
}
thread_count = 0;
}
}
/*
* 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;
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) {
// 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))+3; // Diffraction angle divided by wavelength (m)
} else {
return 1;
}
}
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 - 1) && path.distance[y] <= max_range);
y++) {
//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
&& can_process(path.lat[y], path.lon[y])) {
distance = FEET_PER_MILE * 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 =
FEET_PER_MILE * (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, diffloss;
struct site temp;
float dkm;
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;
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. */
//if(debug)
// fprintf(stderr,"four_thirds_earth %.1f source.alt %.1f path.elevation[0] %.1f\n",four_thirds_earth,source.alt,path.elevation[0]);
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) && can_process(path.lat[y], path.lon[y])) {
char fd_buffer[64];
int buffer_offset = 0;
distance = FEET_PER_MILE * 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 = FEET_PER_MILE * 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, source.alt * METERS_PER_FOOT,
(path.elevation[y] * METERS_PER_FOOT) + (destination.alt * METERS_PER_FOOT), dkm, pmenv);
break;
case 4:
// ECC33
loss =
ECC33pathLoss(LR.frq_mhz, source.alt * METERS_PER_FOOT,
(path.elevation[y] *
METERS_PER_FOOT) +
(destination.alt *
METERS_PER_FOOT), dkm,
pmenv);
break;
case 5:
// SUI
loss =
SUIpathLoss(LR.frq_mhz, source.alt * METERS_PER_FOOT,
(path.elevation[y] *
METERS_PER_FOOT) +
(destination.alt *
METERS_PER_FOOT), dkm, pmenv);
break;
case 6:
// COST231-Hata
loss =
COST231pathLoss(LR.frq_mhz, source.alt * METERS_PER_FOOT,
(path.elevation[y] *
METERS_PER_FOOT) +
(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, source.alt * METERS_PER_FOOT,
(path.elevation[y] *
METERS_PER_FOOT) +
(destination.alt *
METERS_PER_FOOT), dkm,
pmenv);
break;
case 10:
// Plane earth
loss = PlaneEarthLoss(dkm, source.alt * METERS_PER_FOOT, (path.elevation[y] * METERS_PER_FOOT) + (destination.alt * METERS_PER_FOOT));
break;
case 11:
// Egli VHF/UHF
loss = EgliPathLoss(LR.frq_mhz, source.alt * METERS_PER_FOOT, (path.elevation[y] * METERS_PER_FOOT) + (destination.alt * METERS_PER_FOOT),dkm);
break;
case 12:
// Soil
loss = SoilPathLoss(LR.frq_mhz, dkm, LR.eps_dielect);
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 && propmodel > 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)
buffer_offset += sprintf(fd_buffer+buffer_offset,
"%.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)
buffer_offset += sprintf(fd_buffer+buffer_offset,
"%.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)
buffer_offset += sprintf(fd_buffer+buffer_offset,
"%.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)
buffer_offset += sprintf(fd_buffer+buffer_offset,
"%.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)
buffer_offset += sprintf(fd_buffer+buffer_offset,
" *");
fprintf(fd, "%s\n", fd_buffer);
}
/* 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));
}
}
if(path.lat[y]>cropLat)
cropLat=path.lat[y];
if(y>cropLon)
cropLon=y;
//if(cropLon>180)
// cropLon-=360;
}
void PlotLOSMap(struct site source, double altitude, char *plo_filename,
bool use_threads)
{
/* 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. */
static __thread unsigned char mask_value = 1;
FILE *fd = NULL;
if (plo_filename[0] != 0)
fd = fopen(plo_filename, "wb");
if (fd != NULL) {
fprintf(fd,
"%.3f, %.3f\t; max_west, min_west\n%.3f, %.3f\t; max_north, min_north\n",
max_west, min_west, max_north, min_north);
}
// Four sections start here
// Process north edge east/west, east edge north/south,
// south edge east/west, west edge north/south
double range_min_west[] = {min_west, min_west, min_west, max_west};
double range_min_north[] = {max_north, min_north, min_north, min_north};
double range_max_west[] = {max_west, min_west, max_west, max_west};
double range_max_north[] = {max_north, max_north, min_north, max_north};
propagationRange* r[NUM_SECTIONS];
for(int i = 0; i < NUM_SECTIONS; ++i) {
propagationRange *range = new propagationRange;
r[i] = range;
range->los = true;
range->eastwest = (range_min_west[i] == range_max_west[i] ? false : true);
range->min_west = range_min_west[i];
range->max_west = range_max_west[i];
range->min_north = range_min_north[i];
range->max_north = range_max_north[i];
range->use_threads = use_threads;
range->altitude = altitude;
range->source = source;
range->mask_value = mask_value;
range->fd = fd;
if(use_threads)
beginThread(range);
else
rangePropagation(range);
}
if(use_threads)
finishThreads();
for(int i = 0; i < NUM_SECTIONS; ++i){
delete r[i];
}
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, bool
use_threads)
{
static __thread unsigned char mask_value = 1;
FILE *fd = NULL;
if (LR.erp == 0.0 && debug)
fprintf(stderr, "path loss");
else {
if (debug) {
if (dbm)
fprintf(stderr, "signal power level");
else
fprintf(stderr, "field strength");
}
}
if (debug) {
fprintf(stderr,
" contours of \"%s\" out 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(stderr, "\nand %.2f %s of ground clutter",
metric ? clutter * METERS_PER_FOOT : clutter,
metric ? "meters" : "feet");
if (plo_filename[0] != 0)
fd = fopen(plo_filename, "wb");
if (fd != NULL) {
fprintf(fd,
"%.3f, %.3f\t; max_west, min_west\n%.3f, %.3f\t; max_north, min_north\n",
max_west, min_west, max_north, min_north);
}
// Four sections start here
// Process north edge east/west, east edge north/south,
// south edge east/west, west edge north/south
double range_min_west[] = {min_west, min_west, min_west, max_west};
double range_min_north[] = {max_north, min_north, min_north, min_north};
double range_max_west[] = {max_west, min_west, max_west, max_west};
double range_max_north[] = {max_north, max_north, min_north, max_north};
propagationRange* r[NUM_SECTIONS];
for(int i = 0; i < NUM_SECTIONS; ++i) {
propagationRange *range = new propagationRange;
r[i] = range;
range->los = false;
// Only process correct half
if((NUM_SECTIONS - i) <= (NUM_SECTIONS / 2) && haf == 1)
continue;
if((NUM_SECTIONS - i) > (NUM_SECTIONS / 2) && haf == 2)
continue;
range->eastwest = (range_min_west[i] == range_max_west[i] ? false : true);
range->min_west = range_min_west[i];
range->max_west = range_max_west[i];
range->min_north = range_min_north[i];
range->max_north = range_max_north[i];
range->use_threads = use_threads;
range->altitude = altitude;
range->source = source;
range->mask_value = mask_value;
range->fd = fd;
range->propmodel = propmodel;
range->knifeedge = knifeedge;
range->pmenv = pmenv;
if(use_threads)
beginThread(range);
else
rangePropagation(range);
}
if(use_threads)
finishThreads();
for(int i = 0; i < NUM_SECTIONS; ++i){
delete r[i];
}
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 = FEET_PER_MILE * 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 =
FEET_PER_MILE * (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
src/models/los.hh Normal file
View 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, bool use_threads);
void PlotPropagation(struct site source, double altitude, char *plo_filename,
int propmodel, int knifeedge, int haf, int pmenv, bool use_threads);
void PlotPath(struct site source, struct site destination, char mask_value);
#endif /* _LOS_HH_ */

29
src/models/pel.cc Normal file
View File

@@ -0,0 +1,29 @@
/*****************************************************************************
* Plane Earth Path Loss model for Signal Server by Alex Farrant *
* Taken from "Antennas and Propagation for wireless communication systems" *
* ISBN 978-0-470-84879-1 *
* 10 August 2016 *
* 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 PlaneEarthLoss(float d, float TxH, float RxH)
{
/*
Plane Earth Loss model
Frequency: N/A
Distance (km): Any
*/
// Plane earth loss is independent of frequency.
double dbloss = 40*log10(d) + 20*log10(TxH) + 20*log10(RxH);
return dbloss;
}

6
src/models/pel.hh Normal file
View File

@@ -0,0 +1,6 @@
#ifndef _PEL_HH_
#define _PEL_HH_
double PlaneEarthLoss(float d, float TxH, float RxH);
#endif /* _PEL_HH_ */

33
src/models/soil.cc Normal file
View File

@@ -0,0 +1,33 @@
/*****************************************************************************
* Soil Path Loss model for Signal Server by Alex Farrant *
* 21 February 2018 *
* *
* 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. *
*
* Frequency: Any MHz
* Distance: Any Km
* Terrain permittivity: 1 - 15 (Bad to Good)
*/
#include <math.h>
// use call with log/ln as this may be faster
// use constant of value 20.0/log(10.0)
static __inline float _20log10f(float x)
{
return(8.685889f*logf(x));
}
double SoilPathLoss(float f, float d, float terdic)
{
float soil = (120/terdic);
return(6.4 + _20log10f(d) + _20log10f(f)+(8.69*soil));
}

6
src/models/soil.hh Normal file
View File

@@ -0,0 +1,6 @@
#ifndef _SOIL_HH_
#define _SOIL_HH_
double SoilPathLoss(float f, float d, float t);
#endif /* _SOIL_HH_ */

65
src/models/sui.cc Normal file
View File

@@ -0,0 +1,65 @@
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
// use call with log/ln as this may be faster
// use constant of value 20.0/log(10.0)
static __inline float _20log10f(float x)
{
return(8.685889f*logf(x));
}
double SUIpathLoss(double f, double TxH, double RxH, double d, int mode)
{
/*
f = Frequency (MHz) 1900 to 11000
TxH = Transmitter height (m)
RxH = Receiver height (m)
d = distance (km)
mode A1 = URBAN / OBSTRUCTED
mode B2 = SUBURBAN / PARTIALLY OBSTRUCTED
mode C3 = RURAL / OPEN
Paper 1 has a Rx height correction of / 2000
Paper 2 has the same correction as / 2 and gives better results
"Ranked number 2 University in the wurld"
http://www.cl.cam.ac.uk/research/dtg/lce-pub/public/vsa23/VTC05_Empirical.pdf
https://mentor.ieee.org/802.19/file/08/19-08-0010-00-0000-sui-path-loss-model.doc
*/
d *= 1e3; // km to m
// Urban (A1) is default
float a = 4.6;
float b = 0.0075;
float c = 12.6;
float s = 8.2; // Optional fading value. 8.2 to 10.6dB
float XhCF = -10.8;
if (mode == 2) { // Suburban
a = 4.0;
b = 0.0065;
c = 17.1;
XhCF = -10.8;
}
if (mode == 3) { // Rural
a = 3.6;
b = 0.005;
c = 20;
XhCF = -20;
}
float d0 = 100.0;
float A = _20log10f((4 * M_PI * d0) / (300.0 / f));
float y = a - (b * TxH) + (c / TxH);
// Assume 2.4GHz
float Xf = 0;
float Xh = 0;
//Correction factors for > 2GHz
if(f>2000){
Xf=6.0 * log10(f / 2.0);
Xh=XhCF * log10(RxH / 2.0);
}
return A + (10 * y) * (log10(d / d0)) + Xf + Xh + s;
}

6
src/models/sui.hh Normal file
View File

@@ -0,0 +1,6 @@
#ifndef _SUI_HH_
#define _SUI_HH_
double SUIpathLoss(double f, double TxH, double RxH, double d, int mode);
#endif /* _SUI_HH_ */

118
src/models/testmodels.cc Normal file
View File

@@ -0,0 +1,118 @@
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <sys/stat.h>
/*
* Propagation model test script for signal server
* Requires gnuplot
* Compile: gcc -Wall -o test test.cc sui.cc cost.cc ecc33.cc ericsson.cc fspl.cc egli.cc hata.cc -lm
* Test 850Mhz: ./test 850
*
* 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. *
* *
\****************************************************************************/
extern double EgliPathLoss(float f, float TxH, float RxH, float d);
extern double SUIpathLoss(double f, double TxH, double RxH, double d, int mode);
extern double COST231pathLoss(float f, float TxH, float RxH, float d, int mode);
extern double ECC33pathLoss(float f, float TxH, float RxH, float d, int mode);
extern double EricssonpathLoss(float f, float TxH, float RxH, float d, int mode);
extern double FSPLpathLoss(float f, float d);
extern double HATApathLoss(float f, float TxH, float RxH, float d, int mode);
extern 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);
__thread double *elev;
int main(int argc, char** argv)
{
double a = 0;
double f = atof(argv[1]);
double r = 5.0;
float TxH = 30.0;
float RxH = 2.0;
mkdir("tests", S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
FILE * fh;
/*fh = fopen("tests/ITM","w");
for(float d = 0.1; d <= r; d=d+0.2){
point_to_point_ITM(TxH,RxH,15.0,0.005,301.0,f,5,1,0.5,0.5,a,"",errno);
fprintf(fh,"%.1f\t%.1f\n",d,a);
}
fclose(fh);
*/
fh = fopen("tests/SUI.1","w");
for(float d = 0.1; d <= r; d=d+0.1){
a = SUIpathLoss(f, TxH, RxH, d,1);
fprintf(fh,"%.1f\t%.1f\n",d,a);
}
fclose(fh);
fh = fopen("tests/COST231.1","w");
for(float d = 0.1; d <= r; d=d+0.1){
a = COST231pathLoss(f, TxH, RxH, d,1);
fprintf(fh,"%.1f\t%.1f\n",d,a);
}
fclose(fh);
fh = fopen("tests/ECC33.1","w");
for(float d = 0.1; d <= r; d=d+0.1){
a = ECC33pathLoss(f, TxH, RxH, d,1);
fprintf(fh,"%.1f\t%.1f\n",d,a);
}
fclose(fh);
fh = fopen("tests/Ericsson9999.1","w");
for(float d = 0.1; d <= r; d=d+0.1){
a = EricssonpathLoss(f, TxH, RxH, d,1);
fprintf(fh,"%.1f\t%.1f\n",d,a);
}
fclose(fh);
fh = fopen("tests/FSPL","w");
for(float d = 0.1; d <= r; d=d+0.1){
a = FSPLpathLoss(f, d);
fprintf(fh,"%.1f\t%.1f\n",d,a);
}
fclose(fh);
fh = fopen("tests/Hata.1","w");
for(float d = 0.1; d <= r; d=d+0.1){
a = HATApathLoss(f, TxH, RxH, d, 1);
fprintf(fh,"%.1f\t%.1f\n",d,a);
}
fclose(fh);
fh = fopen("tests/Egli.VHF-UHF","w");
for(float d = 0.1; d <= r; d=d+0.1){
a = EgliPathLoss(f, TxH, RxH, d);
fprintf(fh,"%.1f\t%.1f\n",d,a);
}
fclose(fh);
fh = fopen("tests/gnuplot.plt","w");
fprintf(fh,"set terminal jpeg size 800,600\nset output '%.0fMHz_propagation_models.jpg'\nset title '%.0fMHz path loss by propagation model - CloudRF.com'\n",f,f);
fprintf(fh,"set key right bottom box\nset style line 1\nset grid\nset xlabel 'Distance KM'\nset ylabel 'Path Loss dB'\n");
fprintf(fh,"plot 'FSPL' with lines, 'Hata.1' with lines, 'COST231.1' with lines,'ECC33.1' with lines,'Ericsson9999.1' with lines,'SUI.1' with lines,'Egli.VHF-UHF' with lines");
fclose(fh);
system("cd tests && gnuplot gnuplot.plt");
return(0);
}