def test_computeFssBlocking(self): # Mock things propag and FSS antenna. -70dBm at 30km wf_itm.CalcItmPropagationLoss = testutils.FakePropagationPredictor( dist_type='REAL', factor=1.0, offset=70 - 30.0) antenna.GetFssAntennaGains = mock.create_autospec( antenna.GetFssAntennaGains, return_value=2.8) # Create FSS and a CBSD at 30km fss_point, fss_info, _ = data.getFssInfo(TestAggInterf.fss_record) fss_freq_range = (3650e6, 3750e6) cbsd_lat, cbsd_lon, _ = vincenty.GeodesicPoint(fss_point[1], fss_point[0], 30, 0) cbsd = entities.CBSD_TEMPLATE_CAT_A_OUTDOOR._replace( latitude=cbsd_lat, longitude=cbsd_lon) grant = entities.ConvertToCbsdGrantInfo([cbsd], 3640, 3680)[0] constraint = data.ProtectionConstraint( fss_point[1], fss_point[0], 3550e6, fss_freq_range[0], data.ProtectedEntityType.FSS_BLOCKING) itf = interf.computeInterferenceFssBlocking(grant, constraint, fss_info, grant.max_eirp) self.assertAlmostEqual( itf, 20 + # EIRP/MHZ 10 + # 10MHz effective bandwidth -70 # pathloss + 2.8 # FSS antenna gain - 3.1634, # FSS mask loss for adjacent 10MHz 4)
def getDpaNeighborGrants(grants, protection_points, dpa_geometry, low_freq, high_freq, neighbor_distances): """Gets the list of actual neighbor grants of a DPA, for a given channel. This looks at the total keep list considering a bunch of protected points. Args: grants: A list of CBSD |data.CbsdGrantInfo| active grants. protection_points: A list of protection point locations defining the DPA, each one having attributes 'latitude' and 'longitude'. dpa_geometry: The DPA |shapely.geometry| for detection of inside grants. low_freq: The low frequency of protection constraint (Hz). high_freq: The high frequency of protection constraint (Hz). neighbor_distances: The neighborhood distances (km) as a sequence: [cata_dist, catb_dist, cata_oob_dist, catb_oob_dist] Returns: A set of |CbsdGrantInfo| neighbor grants. """ dpa_type = findDpaType(low_freq, high_freq) neighbor_grants = set() if dpa_geometry and not isinstance(dpa_geometry, sgeo.Point): inside_grants = set( g for g in grants if sgeo.Point(g.longitude, g.latitude).intersects(dpa_geometry)) neighbor_grants = set( filterGrantsForFreqRange(inside_grants, low_freq, high_freq)) for point in protection_points: # Assign values to the protection constraint constraint = data.ProtectionConstraint( latitude=point.latitude, longitude=point.longitude, low_frequency=low_freq, high_frequency=high_freq, entity_type=data.ProtectedEntityType.DPA) # Identify CBSD grants in the neighborhood of the protection constraint nbors, _ = findGrantsInsideNeighborhood(grants, constraint, dpa_type, neighbor_distances) neighbor_grants.update(nbors) return neighbor_grants
def calcAggregatedInterference(protection_point, low_freq, high_freq, grants, inc_ant_height, num_iter, beamwidth, neighbor_distances, min_azimuth=0, max_azimuth=360, do_max=False): """Computes the 95% aggregated interference quantile on a protected point. Inputs: protection_point: A protection point location, having attributes 'latitude' and 'longitude'. low_freq: The low frequency of protection constraint (Hz). high_freq: The high frequency of protection constraint (Hz). grants: A list of CBSD |data.CbsdGrantInfo| active grants. inc_ant_height: The reference incumbent antenna height (meters). num_iter: The number of Monte Carlo iterations. beamwidth: The protection antenna beamwidth (degree). min_azimuth: The minimum azimuth (degrees) for incumbent transmission. max_azimuth: The maximum azimuth (degrees) for incumbent transmission. neighbor_distances: The neighborhood distances (km) as a sequence: [cata_dist, catb_dist, cata_oob_dist, catb_oob_dist] do_max: If True, returns the maximum interference over all radar azimuth. Returns: The 95% aggregated interference (dB) either: - as a vector (ndarray) of length N, where the element k corresponds to azimuth k * (beamwith/2). The length N is thus equal to 2 * 360 / beamwith, (or 2 * azimuth_range / beamwidth if using a smaller azimuth range than 360 degrees, as specified by min_azimuth/max_azimuth). - or the maximum over all radar azimuth, if `do_max` is set to True. """ dpa_type = findDpaType(low_freq, high_freq) if not beamwidth: beamwidth = 360 # Assign values to the protection constraint constraint = data.ProtectionConstraint( latitude=protection_point.latitude, longitude=protection_point.longitude, low_frequency=low_freq, high_frequency=high_freq, entity_type=data.ProtectedEntityType.DPA) # DPA Purge algorithm for OOB if dpa_type is DpaType.OUT_OF_BAND: cbsds_grants_map = defaultdict(list) for grant in grants: key = grant.uniqueCbsdKey() _addMinFreqGrantToFront(cbsds_grants_map[key], grant) # Reset the grants to the minimum frequency grant for each CBSDs. grants = [cbsd_grants[0] for cbsd_grants in cbsds_grants_map.values()] # Identify CBSD grants in the neighborhood of the protection constraint neighbor_grants, _ = findGrantsInsideNeighborhood(grants, constraint, dpa_type, neighbor_distances) if not neighbor_grants: return np.asarray(-1000) interf_matrix = np.zeros((num_iter, len(neighbor_grants))) bearings = np.zeros(len(neighbor_grants)) for k, grant in enumerate(neighbor_grants): interf, _ = computeInterference(grant, constraint, inc_ant_height, num_iter, dpa_type) interf_matrix[:, k] = interf.randomInterference bearings[k] = interf.bearing_c_cbsd interf_matrix = 10**(interf_matrix / 10.) azimuths = findAzimuthRange(min_azimuth, max_azimuth, beamwidth) agg_interf = np.zeros(len(azimuths)) for k, azi in enumerate(azimuths): dpa_gains = antenna.GetRadarNormalizedAntennaGains( bearings, azi, beamwidth) dpa_interf = interf_matrix * 10**(dpa_gains / 10.0) agg_interf[k] = np.percentile(np.sum(dpa_interf, axis=1), PROTECTION_PERCENTILE, interpolation='lower') agg_interf = 10 * np.log10(agg_interf) return np.max(agg_interf) if do_max else agg_interf
def moveListConstraint(protection_point, low_freq, high_freq, grants, inc_ant_height, num_iter, threshold, beamwidth, neighbor_distances, min_azimuth=0, max_azimuth=360): """Returns the move list for a given protection constraint. Note that the returned indexes corresponds to the grant.grant_index Inputs: protection_point: A protection point location, having attributes 'latitude' and 'longitude'. low_freq: The low frequency of protection constraint (Hz). high_freq: The high frequency of protection constraint (Hz). grants: A list of CBSD |data.CbsdGrantInfo| grants. inc_ant_height: The reference incumbent antenna height (meters). num_iter: The number of Monte Carlo iterations. threshold: The protection threshold (dBm/10 MHz). beamwidth: The protection antenna beamwidth (degree). neighbor_distances: The neighborhood distances (km) as a sequence: [cata_dist, catb_dist, cata_oob_dist, catb_oob_dist] min_azimuth: The minimum azimuth (degrees) for incumbent transmission. max_azimuth: The maximum azimuth (degrees) for incumbent transmission. Returns: A tuple of (move_list_grants, neighbor_list_grants) for that protection constraint: + the grants on the move list. + the grants in the neighborhood list. """ logging.debug( 'DPA Create move list for point (%s), freq (%s, %s), threshold (%s), neighborhood distance (%r)', protection_point, low_freq, high_freq, threshold, neighbor_distances) if not grants: return [], [] dpa_type = findDpaType(low_freq, high_freq) if not beamwidth: beamwidth = 360 # Assign values to the protection constraint constraint = data.ProtectionConstraint( latitude=protection_point.latitude, longitude=protection_point.longitude, low_frequency=low_freq, high_frequency=high_freq, entity_type=data.ProtectedEntityType.DPA) # DPA Purge algorithm for OOB if dpa_type is DpaType.OUT_OF_BAND: cbsds_grants_map = defaultdict(list) for grant in grants: key = grant.uniqueCbsdKey() _addMinFreqGrantToFront(cbsds_grants_map[key], grant) # Reset the grants to the minimum frequency grant for each CBSDs. grants = [cbsd_grants[0] for cbsd_grants in cbsds_grants_map.values()] # Identify CBSD grants in the neighborhood of the protection constraint neighbor_grants, neighbor_idxs = findGrantsInsideNeighborhood( grants, constraint, dpa_type, neighbor_distances) movelist_grants = [] if len(neighbor_grants): # Found CBSDs in the neighborhood # Form the matrix of interference contributions I, sorted_neighbor_idxs, bearings = formInterferenceMatrix( neighbor_grants, neighbor_idxs, constraint, inc_ant_height, num_iter, dpa_type) # Find the index (nc) of the grant in the ordered list of grants such that # the protection percentile of the interference from the first nc grants is below # the threshold for all azimuths of the receiver antenna. nc = find_nc(I, bearings, threshold, beamwidth, min_azimuth, max_azimuth) # Determine the associated move list (Mc) movelist_grants = [grants[k] for k in sorted_neighbor_idxs[nc:]] # DPA Purge Algorithm for OOB - reintegrated in move list if dpa_type is DpaType.OUT_OF_BAND: # Add back all purged list with state of main one extra_grants = [] for grant in movelist_grants: cbsd_extra_grants = cbsds_grants_map[grant.uniqueCbsdKey()][1:] if cbsd_extra_grants: extra_grants.extend(cbsd_extra_grants) movelist_grants.extend(extra_grants) # Note: the following update on neighbor list is optional, the whole # code would run the same if we were including only the main grant in # the neighbor list. But for sake of consistency, we keep them. extra_grants = [] for grant in neighbor_grants: cbsd_extra_grants = cbsds_grants_map[grant.uniqueCbsdKey()][1:] if cbsd_extra_grants: extra_grants.extend(cbsd_extra_grants) neighbor_grants.extend(extra_grants) logging.debug('DPA Returning movelist_grants=(%s), neighbor_grants=(%s)', movelist_grants, neighbor_grants) return (movelist_grants, neighbor_grants)
def iapPointConstraint(protection_point, channels, low_freq, high_freq, grants, fss_info, esc_antenna_info, region_type, threshold, protection_ent_type): """Computes aggregate interference(Ap and ASASp) from authorized grants. This routine is applicable for FSS Co-Channel and ESC Sensor protection points, and PPA/GWPZ protection areas. It calculates aggregate interference over all 5MHz channels, and feeds post-IAP assessment of allowed interference margins. Args: protection_point: A protection point as a tuple (longitude, latitude). channels: The 5MHz channels as a list of (low_freq, high_freq) tuples. low_freq: low frequency of protection constraint (Hz) high_freq: high frequency of protection constraint (Hz) grants: An iterable of |data.CbsdGrantInfo| grants. fss_info: The FSS information of type |data.FssInformation|. esc_antenna_info: ESC antenna information of type |data.EscInformation|. region_type: Region type of the protection point: 'URBAN', 'SUBURBAN' or 'RURAL'. threshold: The protection threshold (mW). protection_ent_type: The entity type (|data.ProtectedEntityType|). Returns: A tuple (latitude, longitude, asas_interference, agg_interference) where: asas_interference: a list of interference (per channel) for managing SAS. agg_interference: a list of total interference (per channel). """ # Get protection constraint of protected entity protection_constraint = data.ProtectionConstraint( latitude=protection_point[1], longitude=protection_point[0], low_frequency=low_freq, high_frequency=high_freq, entity_type=protection_ent_type) # Get all the grants inside neighborhood of the protection entity grants_inside = interf.findGrantsInsideNeighborhood( grants, protection_point, protection_ent_type) # Get all the grants inside neighborhood of the protection entity, and # with frequency overlap to the protection point. neighbor_grants = interf.findOverlappingGrants(grants_inside, protection_constraint) if not neighbor_grants: return (protection_point[1], protection_point[0], [0] * len(channels), [0] * len(channels)) # Get number of grants, IAP protection threshold and fairshare per channel. num_unsatisfied_grants = len(neighbor_grants) # Initialize list of aggregate interference from all the grants # (including grants from managing and peer SAS ) aggr_interf = [0] * len(channels) # Initialize list of aggregate interference from managing SAS grants. asas_interf = [0] * len(channels) # Algorithm to calculate interference within IAPBW using EIRP obtained for all # the grants through application of IAP num_unsatisfied_grants_channels = [] iap_threshold_channels = [] fairshare_channels = [] for index, channel in enumerate(channels): # Get protection constraint over 5MHz channel range channel_constraint = data.ProtectionConstraint( latitude=protection_point[1], longitude=protection_point[0], low_frequency=channel[0], high_frequency=channel[1], entity_type=protection_ent_type) # Identify CBSD grants overlapping with frequency range of the protection entity # in the neighborhood of the protection point and channel channel_neighbor_grants = interf.findOverlappingGrants( neighbor_grants, channel_constraint) num_unsatisfied_grants_channel = len(channel_neighbor_grants) # Computes interference quota for this protection_point and channel. iap_threshold = threshold # Calculate the fair share per channel based on unsatisfied grants fairshare_channel = 0 if num_unsatisfied_grants_channel > 0: fairshare_channel = float( iap_threshold) / num_unsatisfied_grants_channel num_unsatisfied_grants_channels.append(num_unsatisfied_grants_channel) iap_threshold_channels.append(iap_threshold) fairshare_channels.append(fairshare_channel) # List of grants initialized with max EIRP grants_eirp = [grant.max_eirp for grant in neighbor_grants] # Initialize list of grants_satisfied to False grants_satisfied = [False] * num_unsatisfied_grants # Initialize list of grants_removed to False grants_removed = [False] * num_unsatisfied_grants # Initialize grant overlap and interference per channel dictionary # Values will be stored in the format of key as tuple (grant_id, channel_id) # and respective value as: # overlap_flag: True/False based on grant overlaps with channel # interference: respective interference value for the key grants_overlap_flag = {} grants_interference = {} with cache.CacheManager(wf_hybrid.CalcHybridPropagationLoss): # Using memoizing cache manager only for lengthy calculation (hybrid on PPA/GWPZ). while num_unsatisfied_grants > 0: for g_idx, grant in enumerate(neighbor_grants): if grants_satisfied[g_idx]: continue for idx, channel in enumerate(channels): # Check if grant overlaps with protection point, channel grant_overlap_check = interf.grantFrequencyOverlapCheck( grant, channel[0], channel[1], protection_ent_type) grants_overlap_flag[(g_idx, idx)] = grant_overlap_check if not grant_overlap_check: continue # Get protection constraint over 5MHz channel range channel_constraint = data.ProtectionConstraint( latitude=protection_point[1], longitude=protection_point[0], low_frequency=channel[0], high_frequency=channel[1], entity_type=protection_ent_type) # Compute interference that grant causes to protection point over channel interference = interf.dbToLinear( interf.computeInterference(grant, grants_eirp[g_idx], channel_constraint, fss_info, esc_antenna_info, region_type)) # If calculated interference is more than fair share of interference # to which the grants are entitled then grant is considered unsatisfied if interference < fairshare_channels[idx]: # Add the interference caused by all the grants in a 5MHz channel grants_interference[(g_idx, idx)] = interference grants_satisfied[g_idx] = True else: grants_satisfied[g_idx] = False break num_grants_good_overall_channels = 0 for gr_idx, grant in enumerate(neighbor_grants): if grants_removed[gr_idx]: continue if not grants_satisfied[gr_idx]: continue if num_unsatisfied_grants > 0: num_unsatisfied_grants -= 1 for ch_idx, channel in enumerate(channels): # Grant interferes with protection point over channel if grants_overlap_flag[(gr_idx, ch_idx)]: iap_threshold_channels[ch_idx] = ( iap_threshold_channels[ch_idx] - grants_interference[(gr_idx, ch_idx)]) num_unsatisfied_grants_channels[ch_idx] -= 1 # Re-calculate fairshare for a channel if num_unsatisfied_grants_channels[ch_idx] > 0: fairshare_channels[ch_idx] = ( float(iap_threshold_channels[ch_idx]) / num_unsatisfied_grants_channels[ch_idx]) aggr_interf[ch_idx] += grants_interference[(gr_idx, ch_idx)] if grant.is_managed_grant: asas_interf[ch_idx] += grants_interference[( gr_idx, ch_idx)] # Removing grants from future consideration grants_removed[gr_idx] = True # Increase number of grants good over all channels num_grants_good_overall_channels += 1 if num_grants_good_overall_channels == 0: for grant_idx, grant in enumerate(neighbor_grants): # Reduce power level of all the unsatisfied grants if not grants_satisfied[grant_idx]: grants_eirp[grant_idx] -= 1 logging.debug('IAP point_constraint @ point %s: %s', (protection_point[1], protection_point[0]), zip(asas_interf, aggr_interf)) # Return the computed data return protection_point[1], protection_point[0], asas_interf, aggr_interf
def aggregateInterferenceForPoint(protection_point, channels, grants, fss_info, esc_antenna_info, protection_ent_type, region_type): """Computes the aggregate interference for a protection point. This routine is invoked to calculate aggregate interference for ESC sensor, FSS Co-channel/Blocking and PPA/GWPZ protection areas. Args: protection_point: The location of a protected entity as (longitude, latitude) tuple. channels: A sequence of channels as tuple (low_freq_hz, high_freq_hz). grants: An iterable of CBSD grants of type |data.CbsdGrantInfo|. fss_info: The FSS information of type |data.FssInformation| (optional). esc_antenna_info: ESC antenna information of type |data.EscInformation| (optional). protection_ent_type: The entity type (|data.ProtectedEntityType|). region: Region type of the protection point: 'URBAN', 'SUBURBAN' or 'RURAL'. Returns: A tuple (latitude, longitude, interferences) where interferences is a list of interference per channel. """ # Get all the grants inside neighborhood of the protection entity grants_inside = interf.findGrantsInsideNeighborhood( grants, protection_point, protection_ent_type) if not grants_inside: # We need one entry per channel, even if they're all zero. return protection_point[1], protection_point[0], [0] * len(channels) interferences = [] with cache.CacheManager(wf_hybrid.CalcHybridPropagationLoss): # Using memoizing cache manager only for lengthy calculation (hybrid on PPA/GWPZ). for channel in channels: # Get protection constraint over 5MHz channel range protection_constraint = data.ProtectionConstraint( latitude=protection_point[1], longitude=protection_point[0], low_frequency=channel[0], high_frequency=channel[1], entity_type=protection_ent_type) # Identify CBSD grants overlapping with frequency range of the protection entity # in the neighborhood of the protection point and channel neighborhood_grants = interf.findOverlappingGrants( grants_inside, protection_constraint) if not neighborhood_grants: interferences.append(0) continue cbsd_interferences = [ interf.computeInterference(grant, grant.max_eirp, protection_constraint, fss_info, esc_antenna_info, region_type) for grant in neighborhood_grants ] total_interference = convertAndSumInterference(cbsd_interferences) interferences.append(total_interference) logging.debug('Agg Interf @ point %s: %s', (protection_point[1], protection_point[0]), interferences) return protection_point[1], protection_point[0], interferences