def get_path_loss(self, startpoint: Coordinate, endpoint: Coordinate) -> Dict[str, float]:
        """
        Wrapper function around LayerModel.path_loss for the usage with pool.starmap.

        :param Coordinate startpoint:  Startpoint coordinate
        :param Coordinate endpoint:    Endpoint coordinate

        :return:
            A dictionary containing the path_loss and the distance between the two given coordinates
        """
        lm = LayerModel(self.voxel_model, startpoint, endpoint, self.tissue_properties)

        path_loss = lm.path_loss(self.f_start, self.f_end, self.n_samples)
        distance = lm.distance

        return {'path_loss': path_loss, 'distance': distance}  # , 'endpoint': endpoint, 'startpoint': startpoint}
    def get_path_loss_and_capacity(self, startpoint: Coordinate, endpoint: Coordinate) -> Dict:
        """
        Wrapper function around LayerModel.path_loss and LayerModel.channel_capacity() for the usage with pool.starmap.

        :param Coordinate startpoint:  Startpoint coordinate
        :param Coordinate endpoint:    Endpoint coordinate

        :return:
            A dictionary containing the path_loss and the distance between the two given coordinates
        """
        lm = LayerModel(self.voxel_model, startpoint, endpoint, self.tissue_properties,
                        model_type=self.scenario.model_type)

        # calculate S21 and use it for both path loss and capacity calculation.
        (s21, f) = lm.S21(self.f_start, self.f_end, self.n_samples)

        # if additional_losses is set to radiation loss include the radiation loss
        if hasattr(self, "additional_losses") and hasattr(self, "receiver_area"):
            if self.additional_losses == "radiation loss":
                receiver_area = self.receiver_area
                logging.debug("Applied radiation loss with effective area= %f" % receiver_area)
                distance = lm.distance
                rl = receiver_area / (4 * np.pi * distance ** 2)
                s21 = np.sqrt(rl) * s21
        if hasattr(self, "additional_losses") and hasattr(self, "loss_function"):
            if self.additional_losses == "free space loss":
                logging.debug("Applied isotropic free space loss")
                distance = lm.distance
                fsl = self.loss_function(distance, f)
                s21 = np.sqrt(fsl) * s21

        # calculate the path loss and the capacity using the S21 calculated above
        path_loss = lm.path_loss(self.f_start, self.f_end, self.n_samples, precalculated_S21_f=(s21, f))
        capacity = lm.channel_capacity(self.noise_density, self.transmit_power,
                                       f_start=self.f_start, f_end=self.f_end,
                                       n_samples=self.n_samples, precalculated_S21_f=(s21, f))
        distance = lm.distance

        return {'path_loss': path_loss, 'capacity': capacity, 'distance': distance}
    def calculate_power_delay_profile(self, threshold_dB: float = 30,
                                      equivalent_baseband: Dict = None,
                                      endpoint_indices: Optional[List] = None) \
            -> Tuple[np.ndarray, np.ndarray, float, float]:
        """
        Calculate the power delay profile and the RMS delay spread for the given scenario

        :param float threshold_dB: The threshold up to which decay of the power delay profile the values shall be
                            included in the calculation of the rms delay spread and the power delay profile.
        :param equivalent_baseband: Parameter for LayerModel.impulse_response for conversion to equivalent baseband
        :param list endpoint_indices: Optional parameter to calculate the PDP only for specific indices listed

        :rtype: Tuple[np.ndarray, np.ndarray, float, float]
        :return: a tuple with the following elements: the power_delay_profile, the delay vector,
                                                      the rms delay spread and the average delay.
        """

        # sampling rate cannot be chosen arbitrarily when converting to equivalent baseband
        if equivalent_baseband is not None:
            f_sample = equivalent_baseband['B']
        else:
            f_sample = 20e9

        # start with an empty PDP
        power_delay_profile = np.zeros((0,))

        startpoints = self.scenario.startpoints

        if endpoint_indices is not None:
            # use only the elements from the endpoints that are specified in endpoint_indices
            endpoints = [self.scenario.endpoints[i] for i in endpoint_indices]
        else:
            endpoints = self.scenario.endpoints

        # Debug mode
        if self.test_mode:
            startpoints = startpoints[0:10]
            endpoints = endpoints[0:10]
            log_interval = timedelta(seconds=2)
        else:
            log_interval = timedelta(hours=2)

        start_time = datetime.today()
        last_time = start_time
        logging.info("%s -- Started calculation of Power Delay Profile.." % str(start_time))

        total_length = (len(startpoints) * len(endpoints))

        for index, (sp, ep) in enumerate(product(startpoints, endpoints)):
            lm = LayerModel(self.voxel_model, sp, ep, model_type=self.scenario.model_type)
            if equivalent_baseband is None:
                f_start = self.f_start
                f_end = self.f_end
            else:
                # define variables for downconversion
                bandwidth = equivalent_baseband['B']
                carrier_frequency = equivalent_baseband['fc']
                f_start = carrier_frequency - bandwidth / 2
                f_end = carrier_frequency + bandwidth / 2
                f_sample = bandwidth

            # calculate S21 and use it for both path loss and capacity calculation.
            (s21, f) = lm.S21(f_start, f_end, self.n_samples)

            # if additional_losses is set to free space loss include the free space loss
            if hasattr(self, "additional_losses") and hasattr(self, "loss_function"):
                if self.additional_losses == "free space loss":
                    logging.debug("Applied isotropic free space loss")
                    distance = lm.distance
                    fsl = self.loss_function(distance, f)
                    s21 = np.sqrt(fsl) * s21

            impulse_response = lm.impulse_response(normalize_delay='edge',
                                                   n_samples=self.n_samples,
                                                   threshold_dB=threshold_dB,
                                                   f_sample=f_sample,
                                                   equivalent_baseband=equivalent_baseband,
                                                   precalculated_S21_f=(s21, f))[0]

            if index == 0:
                power_delay_profile = abs(impulse_response)**2
            else:
                # if the current impulse_response is longer than the power_delay_profile append as many zeros as needed
                diff_size = impulse_response.size - power_delay_profile.size
                if diff_size > 0:
                    power_delay_profile = np.pad(power_delay_profile, [(0, diff_size), (0, 0)],
                                                 mode='constant', constant_values=0)
                elif diff_size < 0:
                    diff_size = -diff_size
                    impulse_response = np.pad(impulse_response, [(0, diff_size), (0, 0)],
                                              mode='constant', constant_values=0)

                power_delay_profile += abs(impulse_response)**2

            now = datetime.today()
            if now - last_time > log_interval:
                logging.info("%s -- PDP Calculation %d/%d = %.1f%% .." % (str(now), index, total_length,
                                                                          index/total_length*100))
                last_time = now

        # calculate the average
        power_delay_profile = power_delay_profile / total_length

        end_time = datetime.today()
        duration = end_time - start_time
        logging.info("%s -- Finished calculation of Power Delay Profile after %s.." % (str(end_time), str(duration)))
        logging.info("%s -- Starting RMS Delay Spread Calculation.." % str(datetime.today()))

        power_delay_profile.shape = (-1,)
        """ cut the power delay profile after it has reached a decay of more than threshold_dB dB """
        # find the maximum
        pdp_max_ind = np.argmax(power_delay_profile)
        pdp_max = power_delay_profile[pdp_max_ind]
        # determine the threshold value
        threshold_value = pdp_max * 10**(-threshold_dB/10)
        # find the index where the threshold is first met after the PDP maximum
        threshold_index = np.argmax(power_delay_profile[pdp_max_ind::] < threshold_value)
        # the threshold_index is relative to the maximum of the pdp -> add pdp_max_ind
        power_delay_profile = power_delay_profile[0:(threshold_index + pdp_max_ind)]

        # generate delay vector
        tau = np.arange(0, power_delay_profile.size) / f_sample
        tau.shape = (-1,)

        # calculate the RMS delay spread
        tau_mean = np.trapz(power_delay_profile * tau, tau) / np.trapz(power_delay_profile, tau)

        tau_rms = np.sqrt(np.trapz((tau - tau_mean)**2 * power_delay_profile, tau) /
                          np.trapz(power_delay_profile, tau))

        # adjust the values such that the first entry of the PDP is 1 == 0dB
        power_delay_profile /= pdp_max

        return power_delay_profile, tau, tau_rms, tau_mean
SimulationScenario.working_directory = dirname(__file__)

# create the scenario
scenario = SimulationScenario('create', model_name=model_name, scenario='test')
# add a description
scenario.scenario_description = "This is an example scenario containing some random TX and RX locations."

# add the TX (startpoints) and RX (endpoints) locations
scenario.startpoints = startpoints
scenario.endpoints = endpoints

# compute some results
path_loss = np.zeros(shape=(len(endpoints), len(startpoints)))
for (i, e) in enumerate(endpoints):
    for (k, s) in enumerate(startpoints):
        lm = LayerModel(vm, s, e)
        # calculate the path loss for all of the points
        path_loss[i, k] = lm.path_loss(f_start=3.1e9,
                                       f_end=4.8e9,
                                       n_samples=1024)

# create a dictionary containing the results
results = scenario.create_results_data_dict(
    created_on=datetime.today(),
    name='PathLoss',
    created_by=__file__,
    description="Path Loss in the range 3.1-4.8 GHz",
    parameters={
        'f_start': 3.1e9,
        'f_end': 4.8e9,
        'n_samples': 1024
Exemple #5
0
This examples shows how to use the LayerModel class to calculate the propagation behaviour
of a plane wave through arbitrary different tissue layers.
"""
import numpy as np
import matplotlib.pyplot as plt

from LayerModel_lib import LayerModel, TissueProperties

# create a layer model using these dielectric properties
tp = TissueProperties()

# create a layer model with 200 mm of fat layer as source impedance and 10 mm muscle between TX and RX
lm = LayerModel.create_from_dict({
    'Air': None,
    'Fat': 200,
    'TX': None,
    'Muscle': 10,
    'RX': None
})
# print an overview over the layer model
lm.print_info()

# calculate the transfer function for S21 at 1e9 Hz
(transfer_function, frequency) = lm.S21(f_start=3.1e9,
                                        f_end=4.8e9,
                                        n_samples=100)

# plot the magnitude
plt.plot(frequency / 1e9, 20 * np.log10(np.abs(transfer_function)))
plt.xlabel("Frequency in GHz")
plt.ylabel("Magnitude in dB")
Exemple #6
0
from LayerModel_lib import VoxelModel, LayerModel, Coordinate
from config import phantom_path

VoxelModel.working_directory = phantom_path
all_models = VoxelModel.list_all_voxel_models()
for model in all_models:
    print(model)

# Load a virtual human model
vm = VoxelModel('AustinMan_v2.5_2x2x2')

start = Coordinate([231, 270, 1110])  # some point in the colon
end = Coordinate([330, 277, 1110])  # some point outside the body

# calculate the layer model from two of these points
lm = LayerModel(vm, start, end)

# show info about the model
lm.print_info()

# Calculate the transfer function for S21 (square root of transmitted power) from 0 to 10 GHz with 1024 samples,
# the default direction is 'start->end'
(transfer_function, frequency) = lm.S21(f_start=3.1e9, f_end=4.8e9, n_samples=1024)

# plot the magnitude
hf, ha = plt.subplots(nrows=2)
ha[0].plot(frequency / 1e9, 20 * np.log10(np.abs(transfer_function)))
ha[0].set_xlabel("Frequency in GHz")
ha[0].set_ylabel("Magnitude in dB")
ha[0].set_title("Transfer Function between the two coordinates")
Exemple #7
0
from LayerModel_lib import VoxelModel, LayerModel
from config import phantom_path

VoxelModel.working_directory = phantom_path
all_models = VoxelModel.list_all_voxel_models()
for model in all_models:
    print(model)

# Load a virtual human model
vm = VoxelModel('AustinWoman_v2.5_2x2x2')

# generate 10 random endpoints on the trunk
e = vm.get_random_endpoints('trunk', 10)

# get 10 random startpoints in the gastrointestinal tract
s = vm.get_random_startpoints('trunk', 'GIcontents', 10)

# calculate the layer model from two of these points
lm = LayerModel(vm, s[1], e[1])

# compute the transfer function
transfer_function, f = lm.S21()

# plot the magnitude
plt.plot(f / 1e9, 20 * np.log10(np.abs(transfer_function)))
plt.xlabel("Frequency in GHz")
plt.ylabel("Magnitude in dB")
plt.title("Transfer Function of two Random Locations")
plt.show()
Exemple #8
0

# create an object for the dielectric properties
d = SimpleDielectric()
d.add_new_dielectric('Air', new_values={
    'eps_real': 1,
    'sigma': 0
})  # index 0 = this should always be Air
d.add_new_dielectric('Solid1', new_values={
    'eps_real': 3,
    'sigma': 5
})  # index 1
d.add_new_dielectric('Solid2', new_values={
    'eps_real': 5,
    'sigma': 7
})  # index 2

# create a layer model using these dielectric properties
lm = LayerModel.create_from_dict(
    {
        'Air': None,
        'TX': None,
        'Solid1': 10,
        'Solid2': 20,
        'RX': None
    },
    tissue_properties=d)

lm.print_info()
# calculate the transfer function at 1e9 Hz
(transfer_function, frequency) = lm.S21(f_start=1e9, f_end=1e9, n_samples=1)
# This file is part of LayerModel_lib
#
#     A tool to compute the transmission behaviour of plane electromagnetic waves
#     through human tissue.
#
# Copyright (C) 2018 Jan-Christoph Brumm
#
# Licensed under MIT license.
#
"""
Example that shows how the layer model can be visualized.
"""
from LayerModel_lib import VoxelModel, LayerModel, Coordinate, ModelNames

vm = VoxelModel(model_name=ModelNames.VisibleHuman)

tx = Coordinate([238.29999999999998, 350.35, 690.])
rx = Coordinate([308.79999999999995, 91.0, 630.])

lm = LayerModel(voxel_model=vm, startpoint=tx, endpoint=rx)

lm.plot(title='Layer Model Test')

lm.plot_layer_compare([lm], ['test'])
Exemple #10
0
from LayerModel_lib import VoxelModel, LayerModel, Coordinate
from config import phantom_path

VoxelModel.working_directory = phantom_path
all_models = VoxelModel.list_all_voxel_models()
for model in all_models:
    print(model)

# Load a virtual human model
vm = VoxelModel('AustinMan_v2.5_2x2x2')

start = Coordinate([231, 270, 1110])  # some point in the colon
end = Coordinate([330, 277, 1110])  # some point outside the body

# calculate the layer model from two of these points
lm = LayerModel(vm, start, end)

# show info about the model
lm.print_info()

# Calculate the impulse response for fs=20 GHz and 1024 samples
(impulse_S21_out, t_S21_out) = lm.impulse_response(f_sample=20e9,
                                                   n_samples=1024,
                                                   direction='start->end')

# plot the magnitude
hf, ha = plt.subplots(nrows=1)
ha.plot(t_S21_out / 1e-9, impulse_S21_out)

ha.set_xlabel("Time in ns")
ha.set_ylabel("Amplitude of h(t)")