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
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")
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")
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()
# 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'])
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)")