system_model.py 4.6 KB
Newer Older
Lukas Eller's avatar
Lukas Eller committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
import numpy as np
from scipy.constants import speed_of_light
import pymc3 as pm
import matplotlib.pyplot as plt
import os

class ShadowFading():
    def __init__(self, sigma_f=6, correlation_distance=50):
        self._sigma_f = sigma_f
        self._correlation_distance = correlation_distance
        self._covariance = None

    @staticmethod
    def correlation_function(distance, correlation_distance):
        return np.exp(-distance/correlation_distance)

    def _generate_covariance(self, user_locations):
        covariance = np.zeros((len(user_locations), len(user_locations)), dtype="float64")
        for i in range(len(user_locations)):
            for j in range(len(user_locations)):
                covariance[i, j] = ShadowFading.correlation_function(
                    np.linalg.norm(
                        user_locations[i] - user_locations[j]
                    ),
                    self._correlation_distance
                )

        self._covariance = self._sigma_f**2 *  covariance

        while not np.all(np.linalg.eigvals(self._covariance) > 0):
            print("Not positive semidefinite -- add to diagonal")
            self._covariance = np.diag(np.full(len(self._covariance), 1e-1)) + self._covariance

    def covariance(self, user_locations):
        self._generate_covariance(user_locations)

        return self._covariance

    def __call__(self, user_locations):
        self._generate_covariance(user_locations)

        return np.random.multivariate_normal(
            np.full(len(user_locations), 0),
            self._covariance,
            check_valid="warn"
        )

class PathlossModel():
    def __init__(self, frequency=1.8):
        self._frequency = frequency

    @staticmethod
    def getLOSpathlos(d3d, d2d, dbp, fc, h_b, h_t):
        PL1 = 28+22*np.log10(d3d)+20*np.log10(fc)
        PL2 = 28+40*np.log10(d3d)+20*np.log10(fc) - 9*np.log10(dbp**2+(h_b - h_t)**2)
        PL = np.zeros((d3d.shape))
        PL = PL2
        PL[(np.greater_equal(d2d,10) & np.less_equal(d2d,dbp))] = PL1[(np.greater_equal(d2d,10) & np.less_equal(d2d,dbp))]
        return PL

    @staticmethod
    def getNLOSpathloss(street_width, average_building_height, bs_height, ue_height, d3d, f_ghz):
        pathlossdB = 161.04 - 7.1*np.log10(street_width) + 7.5*np.log10(average_building_height) - \
            (24.37 - 3.7 * (average_building_height /bs_height)**2) * np.log10(bs_height) + \
            (43.42 - 3.1*np.log10(bs_height)) * (np.log10(d3d) - 3) + \
            20*np.log10(f_ghz) - \
            (3.2*(np.log10(17.625))**2 - 4.97) - 0.6 * (ue_height - 1.5)
        return pathlossdB

    @staticmethod
    def pathlos_36873(h_b, h_ue, f_ghz, street_w, building_h, d2d):
        '''
        d2d ... Distance 2D in Meters
        '''
        h_e = h_b - h_ue
        d3d = np.sqrt(d2d**2+h_e**2)

        dbp =  4*h_b*h_ue*f_ghz*10e8/speed_of_light

        nlos_pathloss = PathlossModel.getNLOSpathloss(
            street_w,
            building_h,
            h_b, h_ue,
            d3d, f_ghz
        )
        los_pathloss = PathlossModel.getLOSpathlos(
            d3d, d2d,
            dbp, f_ghz,
            h_b, h_ue
        )

        return np.maximum(nlos_pathloss, los_pathloss)

    def __call__(self, measurement_locations, return_distance=False):
        d2d = np.linalg.norm(measurement_locations, axis=1)
        return self.pathlos_36873(15, 5, self._frequency, 10, 15, d2d)

class SystemModel():
    def __init__(self, frequency=1.8, attenuation_max=30, three_db_angle=65):
        self._frequency = frequency
        self._attenuation_max = attenuation_max
        self._three_db_angle = three_db_angle

    def apply_pathloss_model(self, locations, pathloss):
        y = PathlossModel(self._frequency)(locations) - pathloss
        return y

    def plot(self, locations, pathloss, azimuths, save_path=None, name=None):
        y = self.apply_pathloss_model(locations, pathloss)

        plt.figure()
        plt.scatter(azimuths, y)
        plt.ylabel("Pathloss Offset over Angle [dB]")
        plt.xlabel("Azimuth [Degrees]")
        if save_path is not None:
            plt.savefig(os.path.join(save_path, f"pathloss_offset_over_angle_{name}.jpeg"), bbox_inches="tight")
        plt.close()

    def theano_system_model(self, azimuths, sector_orienation):
        bs_azimuth = sector_orienation * 180 / np.pi
        difference = pm.math.abs_((bs_azimuth - azimuths))
        theta = pm.math.minimum(difference, pm.math.abs_(difference - 360))

        pattern = 12 * (theta/self._three_db_angle)**2
        pattern = -pm.math.minimum(
            pattern,
            self._attenuation_max
        )

        return pattern