forked from ExternalVendorCode/Signal-Server
Initial Commit
This commit is contained in:
9
src/models/README
Normal file
9
src/models/README
Normal 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
61
src/models/cost.cc
Normal 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
6
src/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_ */
|
33
src/models/ecc33.cc
Normal file
33
src/models/ecc33.cc
Normal 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
6
src/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_ */
|
83
src/models/egli.cc
Normal file
83
src/models/egli.cc
Normal 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
6
src/models/egli.hh
Normal 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
31
src/models/ericsson.cc
Normal 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
6
src/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_ */
|
33
src/models/fspl.cc
Normal file
33
src/models/fspl.cc
Normal 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
6
src/models/fspl.hh
Normal 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
58
src/models/hata.cc
Normal 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
6
src/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
src/models/itm.cc
Normal file
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
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
14
src/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_ */
|
948
src/models/los.cc
Normal file
948
src/models/los.cc
Normal 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
18
src/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, 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
29
src/models/pel.cc
Normal 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
6
src/models/pel.hh
Normal 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
33
src/models/soil.cc
Normal 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
6
src/models/soil.hh
Normal 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
65
src/models/sui.cc
Normal 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
6
src/models/sui.hh
Normal 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
118
src/models/testmodels.cc
Normal 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);
|
||||
}
|
||||
|
Reference in New Issue
Block a user