def compute_nprb_NR(self, data_rate, rsrp): #compute SINR interference = 0 for elem in rsrp: if elem != self.bs_id and util.find_bs_by_id( elem).bs_type != "sat" and util.find_bs_by_id( elem).carrier_frequency == self.carrier_frequency: total, used = util.find_bs_by_id(elem).get_state() interference = interference + (10**(rsrp[elem] / 10)) * ( used / total) * (self.allocated_prb / self.total_prb) #thermal noise is computed as k_b*T*delta_f, where k_b is the Boltzmann's constant, T is the temperature in kelvin and delta_f is the bandwidth #thermal_noise = constants.Boltzmann*293.15*list(NRbandwidth_prb_lookup[self.numerology][self.fr].keys())[list(NRbandwidth_prb_lookup[self.numerology][self.fr].values()).index(self.total_prb / (10 * 2**self.numerology))]*1000000*(self.compute_rbur()+0.001) thermal_noise = constants.Boltzmann * 293.15 * 15 * ( 2**self.numerology ) * 1000 # delta_F = 15*2^mu KHz each subcarrier since we are considering measurements at subcarrirer level (like RSRP) sinr = (10**(rsrp[self.bs_id] / 10)) / (thermal_noise + interference) r = self.prb_bandwidth_size * 1000 * math.log2( 1 + sinr) #bandwidth is in kHz #based on the numerology choosen and considered the frame duration of 10ms, we transmit 1ms for mu = 0, 0.5ms for mu = 1, 0.25ms for mu = 2, 0.125ms for mu = 3 for each PRB each 10ms #print(r) r = r / (10 * (2**self.numerology)) #print(r) N_prb = math.ceil(data_rate * 1000000 / r) #data rate is in Mbps return N_prb, r
def disconnect_from_bs(self, bs_id): if bs_id in self.current_bs: util.find_bs_by_id(bs_id).request_disconnection(self.ue_id) #print("[CONNECTION_TERMINATED]: User ID %s is now disconnected from base_station %s" %(self.ue_id, bs_id)) self.actual_data_rate -= self.current_bs[bs_id] del self.current_bs[bs_id] return
def compute_rsrp_drone(self, ue): #relay rsrp depends from the signal received by the BS and by the re-amp made by the drone print( util.compute_rsrp(self, util.find_bs_by_id(self.linked_bs), self.env)) return self.amplification + util.compute_rsrp( self, util.find_bs_by_id(self.linked_bs), self.env ) + self.antenna_gain - self.feeder_loss - util.compute_path_loss_cost_hata( ue, self, self.env)
def compute_sinr(self, rsrp): interference = 0 for elem in rsrp: if elem != self.bs_id and util.find_bs_by_id( elem).bs_type == "sat": interference = interference + ( 10**(rsrp[elem] / 10)) * util.find_bs_by_id(elem).compute_rbur() thermal_noise = constants.Boltzmann * 290 * self.carrier_bnd * 1000000 sinr = (10**(rsrp[self.bs_id] / 10)) / (thermal_noise + interference) return sinr
def next_timestep(self): self.old_position = self.current_position self.move() #compute the next state variable x^i_p[k+1], considering the visible base stations rsrp = self.env.discover_bs(self.ue_id) #remove the old BSs that are out of visibility for elem in self.bs_bitrate_allocation: if elem not in rsrp: del self.bs_bitrate_allocation[elem] #add the new BSs for elem in rsrp: if elem not in self.bs_bitrate_allocation: self.bs_bitrate_allocation[elem] = 0 #core of the Wardrop algorithm for p in self.bs_bitrate_allocation: for q in self.bs_bitrate_allocation: if p != q: bs_p = util.find_bs_by_id(p) l_p = bs_p.compute_latency(self.ue_id) bs_q = util.find_bs_by_id(q) l_q = bs_q.compute_latency(self.ue_id) mu_pq = 1 if ( l_p - l_q ) < self.env.wardrop_epsilon or bs_q.allocated_bitrate >= bs_q.total_bitrate - ( self.env.wardrop_epsilon / (2 * self.env.wardrop_beta)): mu_pq = 0 mu_qp = 1 if ( l_q - l_p ) < self.env.wardrop_epsilon or bs_p.allocated_bitrate >= bs_p.total_bitrate - ( self.env.wardrop_epsilon / (2 * self.env.wardrop_beta)): mu_qp = 0 r_pq = self.bs_bitrate_allocation[ p] * mu_pq * self.wardrop_sigma r_qp = self.bs_bitrate_allocation[ q] * mu_qp * self.wardrop_sigma self.bs_bitrate_allocation[p] += self.env.sampling_time * ( r_qp - r_pq) return
def compute_reward(self, state, action, bitrate, desired_data_rate, rsrp, ue_id): if action in rsrp: allocated, total = util.find_bs_by_id(action).get_connection_info( ue_id) alpha = 0 if util.find_ue_by_id(ue_id).service_class == 0: alpha = 3 else: alpha = 1 if bitrate > desired_data_rate: # in case the DQN made a correct allocation I do not want the user occupies too much resources, so if the allocated resources # for the users are too much I will discount the reward of a proportional value return alpha * desired_data_rate / (allocated / total) else: if allocated > 0: # in case of a bad allocation, I do not want again that the user occupies too much resources (better if it is allocated to # one of its neighbor base stations) return alpha * (desired_data_rate** 2) * (bitrate - desired_data_rate ) #* (allocated/total) * 100 else: return alpha * (desired_data_rate** 2) * (bitrate - desired_data_rate) else: # it should never go here (there are checks on actions in the argmax) return -10000
def initial_timestep(self): #compute beta value: #beta, by definition, is max{1/r}, where r is the data rate of a single resource block (or symbol) seen by a certain UE self.wardrop_beta = 0 for ue in self.ue_list: rsrp = self.discover_bs(ue.ue_id) for elem in rsrp: r = util.find_bs_by_id(elem).compute_r(ue.ue_id, rsrp) if util.find_bs_by_id(elem).wardrop_alpha / ( r / 1000000) > self.wardrop_beta: #we convert r in Mbps self.wardrop_beta = util.find_bs_by_id( elem).wardrop_alpha / (r / 1000000) #now call each initial_timestep function in order to set the initial conditions for each commodity in terms of bitrate #to be requested to each BS for ue in self.ue_list: ue.initial_timestep() return
def request_connection(self, ue_id, requested_bitrate, available_bs): bs = max(available_bs, key=available_bs.get) data_rate = util.find_bs_by_id(bs).request_connection( ue_id, requested_bitrate, available_bs) reward = self.compute_reward(None, bs, data_rate, requested_bitrate, available_bs, ue_id) self.cumulative_reward += reward return bs, data_rate
def update_connection(self, ue_id, data_rate, available_bs): rsrp = available_bs.copy() if self.bs_id in rsrp: value = rsrp[self.bs_id] del rsrp[self.bs_id] if self.linked_bs in rsrp: del rsrp[self.linked_bs] rsrp[self.linked_bs] = value return util.find_bs_by_id(self.linked_bs).update_connection( ue_id, data_rate, rsrp)
def compute_nsymb_SAT(self, data_rate, rsrp): #compute SINR interference = 0 for elem in rsrp: if elem != self.bs_id and util.find_bs_by_id( elem).bs_type == "sat": interference = interference + (10**( rsrp[elem] / 10)) * util.find_bs_by_id(elem).compute_rbur( ) * (self.frame_utilization / self.total_symbols) thermal_noise = constants.Boltzmann * 290 * self.carrier_bnd * 1000000 sinr = (10**(rsrp[self.bs_id] / 10)) / (thermal_noise + interference) r = self.carrier_bnd * 1000000 * math.log2(1 + sinr) r = r / self.frame_length # this is the data rate in [b/s] that is possible to obtains for a single symbol assigned every time frame r_64 = r * 64 # we can transmit in blocks of 64 symbols n_symb = math.ceil(data_rate * 1000000 / r_64) return n_symb, r
def compute_nprb_LTE(self, data_rate, rsrp): #compute SINR interference = 0 for elem in rsrp: if elem != self.bs_id and util.find_bs_by_id( elem).bs_type != "sat" and util.find_bs_by_id( elem).carrier_frequency == self.carrier_frequency: total, used = util.find_bs_by_id(elem).get_state() interference = interference + (10**( rsrp[elem] / 10)) * util.find_bs_by_id(elem) * ( used / total) * (self.allocated_prb / self.total_prb) #thermal noise is computed as k_b*T*delta_f, where k_b is the Boltzmann's constant, T is the temperature in kelvin and delta_f is the bandwidth thermal_noise = constants.Boltzmann * 293.15 * 15 * 1000 # delta_F = 12*15KHz each resource block since we are considering resource block related measurements (like RSRP) sinr = (10**(rsrp[self.bs_id] / 10)) / (thermal_noise + interference) r = self.prb_bandwidth_size * 1000 * math.log2( 1 + sinr) #bandwidth is in kHz #with a single PRB we transmit just 1ms each 10ms (that is the frame lenght), so the actual rate is divided by 10 r = r / 10 N_prb = math.ceil(data_rate * 1000000 / r) #data rate is in Mbps return N_prb, r
def update_connection(self): if len(self.current_bs) == 0: self.connect_to_bs() print("UE_ID: ", self.ue_id, " NO CURRENT BS") return available_bs = self.env.discover_bs(self.ue_id) #print("UE_ID: ", self.ue_id, " AVAILABLE BS: ", available_bs) #print(available_bs) if len(available_bs) == 0: #print("[NO BASE STATION FOUND]: User ID %s has not found any base station during connection update" %(self.ue_id)) #print("UE_ID: ", self.ue_id, " NO BS AVAILABLE") return for elem in self.current_bs: if elem in available_bs: if self.current_bs[elem] == 0: #print("UE_ID: ", self.ue_id, " NO CONNECTION TO BS", elem) self.connect_to_bs_id(elem) continue data_rate = util.find_bs_by_id(elem).update_connection( self.ue_id, self.bs_bitrate_allocation[elem], available_bs) self.actual_data_rate -= self.current_bs[elem] self.current_bs[elem] = data_rate self.actual_data_rate += self.current_bs[elem] #TODO update the connections according to the newly computed requested bitrates coming from the next_timestep() function ''' if self.actual_data_rate < self.requested_bitrate: print("[POOR BASE STATION]: User ID %s has a poor connection to its base station (actual data rate is %s/%s Mbps)" %(self.ue_id, self.actual_data_rate, self.requested_bitrate)) self.disconnect_from_bs(elem) self.connect_to_bs() ''' ''' elif random.random() < self.epsilon: print("[RANDOM DISCONNECTION]: User ID %s was randomly disconnected from its base station (actual data rate is %s/%s Mbps)" %(self.ue_id, self.actual_data_rate, self.requested_bitrate)) self.disconnect_from_bs() self.connect_to_bs() ''' else: #in this case no current base station is anymore visible #print("[BASE STATION LOST]: User ID %s has not found its base station during connection update" %(self.ue_id)) self.disconnect_from_bs(elem) self.connect_to_bs()
def connect_to_bs_id(self, bs_id): available_bs = self.env.discover_bs(self.ue_id) bs = None data_rate = None if bs_id not in available_bs: #print("[NO BASE STATION FOUND]: User ID %s has not found the selected base station (BS %s)" %(self.ue_id, bs_id)) return else: if bs_id not in self.bs_bitrate_allocation: #print("[NO ALLOCATION FOR THIS BASE STATION FOUND]: User ID %s has not found any bitrate allocation for the selected base station (BS %s)" %(self.ue_id, bs_id)) return elif self.bs_bitrate_allocation[bs_id] == 0: self.current_bs[bs_id] = 0 return data_rate = util.find_bs_by_id(bs_id).request_connection( self.ue_id, self.bs_bitrate_allocation[bs_id], available_bs) self.current_bs[bs_id] = data_rate self.actual_data_rate += data_rate
def connect_to_bs_random(self): available_bs = self.env.discover_bs(self.ue_id) bs = None data_rate = None if len(available_bs) == 0: print( "[NO BASE STATION FOUND]: User ID %s has not found any base station" % (self.ue_id)) return elif len(available_bs) == 1: #this means there is only one available bs, so we have to connect to it #bs = list(available_bs.keys())[0] #self.actual_data_rate = util.find_bs_by_id(bs).request_connection(self.ue_id, self.requested_bitrate, available_bs) bs, data_rate = self.env.request_connection( self.ue_id, self.requested_bitrate, available_bs) self.current_bs[bs] = data_rate self.actual_data_rate += data_rate else: if self.MATLAB == 1: #import function from matlab, in order to select the best action #eng = matlab.engine.start_matlab() #ret = eng.nomefunzione(arg1, arg2,...,argn) return else: bs, rsrp = random.choice(list(available_bs.items())) data_rate = util.find_bs_by_id(bs).request_connection( self.ue_id, self.requested_bitrate, available_bs) #self.current_bs, self.actual_data_rate = self.env.request_connection(self.ue_id, self.requested_bitrate, available_bs) self.current_bs[bs] = data_rate self.actual_data_rate += data_rate print( "[CONNECTION_ESTABLISHED]: User ID %s is now connected to base_station %s with a data rate of %s/%s Mbps" % (self.ue_id, self.current_bs[bs], data_rate, self.requested_bitrate))
def get_connection_info(self, ue_id): return util.find_bs_by_id(self.linked_bs).get_connection_info(ue_id)
def get_state(self): return util.find_bs_by_id(self.linked_bs).get_state()
def new_state(self): return util.find_bs_by_id(self.linked_bs).new_state()
def request_disconnection(self, ue_id): util.find_bs_by_id(self.linked_bs).request_disconnection(ue_id)
def compute_r(self, ue_id, rsrp): return util.find_bs_by_id(self.linked_bs).compute_r(ue_id, rsrp)
def compute_latency(self, ue_id): return util.find_bs_by_id(self.linked_bs).compute_latency(ue_id)
def disconnect_from_all_bs(self): for bs in self.current_bs: util.find_bs_by_id(bs).request_disconnection(self.ue_id) #print("[CONNECTION_TERMINATED]: User ID %s is now disconnected from base_station %s" %(self.ue_id, bs)) self.actual_data_rate = 0 self.current_bs.clear()
prb_dict[elem] = util.find_bs_by_id(elem).ue_pb_allocation[SELECTED_UE] elif util.find_bs_by_id(elem).bs_type == "sat" and SELECTED_UE in util.find_bs_by_id(elem).ue_allocation: prb_dict[elem] = util.find_bs_by_id(elem).ue_allocation[SELECTED_UE]/64 print("PRB: ",prb_dict) ''' if i != 0: for elem in ue: phonex = util.find_ue_by_id(elem) for bsx in phonex.current_bs: if phonex.current_bs[bsx] < phonex.bs_bitrate_allocation[ bsx]: print("Warning: UE", elem, "has saturated BS ", bsx) #print(util.find_bs_by_id(2).ue_pb_allocation[SELECTED_UE]) for bsi in bs: if util.find_bs_by_id(bsi).bs_type != "sat": print("BS ", bsi, " PRB: ", util.find_bs_by_id(bsi).allocated_prb, "/", util.find_bs_by_id(bsi).total_prb, " Bitrate: ", util.find_bs_by_id(bsi).allocated_bitrate, "/", util.find_bs_by_id(bsi).total_bitrate) else: print("BS ", bsi, " PRB: ", util.find_bs_by_id(bsi).frame_utilization / 64, "/", util.find_bs_by_id(bsi).total_symbols / 64, " Bitrate: ", util.find_bs_by_id(bsi).allocated_bitrate, "/", util.find_bs_by_id(bsi).total_bitrate) max_e = 0 for phone in ue: #print(phone)
def get_connected_users(self): return util.find_bs_by_id(self.linked_bs).get_connected_users()
def reset(self): self.position = (self.starting_position[0], self.starting_position[1]) self.current_position = self.position self.h_b = self.starting_position[2] self.h_m = self.starting_position[2] return util.find_bs_by_id(self.linked_bs).reset()
def compute_rbur(self): return util.find_bs_by_id(self.linked_bs).compute_rbur()