def CalcKeepListInterference(self, channel, num_iter=None): """Calculates max aggregate interference per protected point. Args: channel: A channel as tuple (low_freq_mhz, high_freq_mhz). num_iter: The number of Monte Carlo iteration when calculating the aggregate interference. Returns: The 95% aggregate interference per protected point, as a list. Each element is the maximum aggregate interference over all radar directions. """ if num_iter is None: num_iter = Dpa.num_iteration keep_list = self.GetKeepList(channel) interfCalculator = functools.partial( ml.calcAggregatedInterference, low_freq=channel[0] * 1e6, high_freq=channel[1] * 1e6, grants=keep_list, inc_ant_height=self.radar_height, num_iter=num_iter, beamwidth=self.beamwidth, min_azimuth=self.azimuth_range[0], max_azimuth=self.azimuth_range[1], neighbor_distances=self.neighbor_distances, do_max=True) pool = mpool.Pool() max_interf = pool.map(interfCalculator, self.protected_points) return max_interf
def ComputeMoveLists(self): """Computes move/neighbor lists. This routine updates the internal grants move list and neighbor list. One set of list is maintained per protected channel. To retrieve the list, see the routines GetMoveList(), GetNeighborList() and GetKeepList(). """ logging.info( 'DPA Compute movelist `%s`- channels %s thresh %s bw %s height %s ' 'iter %s azi_range %s nbor_dists %s', self.name, self._channels, self.threshold, self.beamwidth, self.radar_height, Dpa.num_iteration, self.azimuth_range, self.neighbor_distances) logging.debug(' protected points: %s', self.protected_points) pool = mpool.Pool() self.ResetLists() # Detect the inside "inside grants", which will allow to # add them into move list for sure later on. inside_grants = set() if self.geometry and not isinstance(self.geometry, sgeo.Point): inside_grants = set(g for g in self._grants if sgeo.Point( g.longitude, g.latitude).intersects(self.geometry)) for chan_idx, (low_freq, high_freq) in enumerate(self._channels): moveListConstraint = functools.partial( ml.moveListConstraint, low_freq=low_freq * 1.e6, high_freq=high_freq * 1.e6, grants=self._grants, inc_ant_height=self.radar_height, num_iter=Dpa.num_iteration, threshold=self.threshold, beamwidth=self.beamwidth, min_azimuth=self.azimuth_range[0], max_azimuth=self.azimuth_range[1], neighbor_distances=self.neighbor_distances) move_list, nbor_list = zip( *pool.map(moveListConstraint, self.protected_points)) # Combine the individual point move lists move_list = set().union(*move_list) nbor_list = set().union(*nbor_list) include_grants = ml.filterGrantsForFreqRange( inside_grants, low_freq * 1.e6, high_freq * 1.e6) move_list.update(include_grants) nbor_list.update(include_grants) self.move_lists[chan_idx] = move_list self.nbor_lists[chan_idx] = nbor_list logging.info('DPA Result movelist `%s`- MOVE_LIST:%s NBOR_LIST: %s', self.name, self.move_lists, self.nbor_lists)
def calculateAggregateInterferenceForGwpz(gwpz_record, grants): """Calculates per-channel aggregate interference for GWPZ. Args: gwpz_record: A GWPZ record dict. grants: An iterable of CBSD grants of type |data.CbsdGrantInfo|. Returns: Aggregate interference to GWPZ in the nested dictionary format. {latitude : {longitude: [aggr_interf1(mW), ..., aggr_interfK(mW)]}} The list contains the value per protected channel. """ gwpz_region = gwpz_record['landCategory'] # Get Fine Grid Points for a GWPZ protection area protection_points = utils.GridPolygon( gwpz_record['zone']['features'][0]['geometry'], GWPZ_GRID_RES_ARCSEC) gwpz_freq_range = gwpz_record['record']['deploymentParam'][0]\ ['operationParam']['operationFrequencyRange'] gwpz_low_freq = gwpz_freq_range['lowFrequency'] gwpz_high_freq = gwpz_freq_range['highFrequency'] # Get channels over which area incumbent needs partial/full protection protection_channels = interf.getProtectedChannels(gwpz_low_freq, gwpz_high_freq) # Calculate aggregate interference from each protection constraint with a # pool of parallel processes. logging.info( 'Computing aggregateInterferenceForPoint for PPA (%s), channels (%s), ' 'nPoints (%d), grants (%s), region_type (%s)', gwpz_record, protection_channels, len(protection_points), grants, gwpz_region) logging.debug(' points: %s', protection_points) interfCalculator = partial( aggregateInterferenceForPoint, channels=protection_channels, grants=grants, fss_info=None, esc_antenna_info=None, protection_ent_type=data.ProtectedEntityType.GWPZ_AREA, region_type=gwpz_region) pool = mpool.Pool() interferences = pool.map(interfCalculator, protection_points) return InterferenceDict(interferences)
def ComputeMoveLists(self): """Computes move/neighbor lists. This routine updates the internal grants move list and neighbor list. One set of list is maintained per protected channel. To retrieve the list, see the routines GetMoveList(), GetNeighborList() and GetKeepList(). """ logging.info( 'DPA Compute movelist `%s`- channels %s thresh %s bw %s height %s ' 'iter %s azi_range %s nbor_dists %s', self.name, self._channels, self.threshold, self.beamwidth, self.radar_height, Dpa.num_iteration, self.azimuth_range, self.neighbor_distances) logging.debug(' protected points: %s', self.protected_points) pool = mpool.Pool() self.ResetLists() for chan_idx, (low_freq, high_freq) in enumerate(self._channels): moveListConstraint = functools.partial( ml.moveListConstraint, low_freq=low_freq * 1.e6, high_freq=high_freq * 1.e6, grants=self._grants, inc_ant_height=self.radar_height, num_iter=Dpa.num_iteration, threshold=self.threshold, beamwidth=self.beamwidth, min_azimuth=self.azimuth_range[0], max_azimuth=self.azimuth_range[1], neighbor_distances=self.neighbor_distances) move_list, nbor_list = zip( *pool.map(moveListConstraint, self.protected_points)) # Combine the individual point move lists move_list = set().union(*move_list) nbor_list = set().union(*nbor_list) self.move_lists[chan_idx] = move_list self.nbor_lists[chan_idx] = nbor_list logging.info('DPA Result movelist `%s`- MOVE_LIST:%s NBOR_LIST: %s', self.name, self.move_lists, self.nbor_lists)
def PpaCreationModel(devices, pal_records): """Creates a PPA Polygon based on the PAL Records and Device Information Args: devices: A list of CBSD records (schema |CbsdRecordData|). pal_records: A list of pal records. Returns: The PPA polygon in GeoJSON format (string). """ # Validation for Inputs for device in devices: logging.info('Validating device', device) util.assertContainsRequiredFields("RegistrationRequest.schema.json", device) for pal_rec in pal_records: logging.info('Validating pal_rec', pal_rec) util.assertContainsRequiredFields("PalRecord.schema.json", pal_rec) # Create Contour for each CBSD pool = mpool.Pool() device_polygon = pool.map(_GetPolygon, devices) # Create Union of all the CBSD Contours and Check for hole # after Census Tract Clipping contour_union = ops.cascaded_union(device_polygon) logging.info('contour_union = %s', contour_union) ppa_without_small_holes = utils.PolyWithoutSmallHoles(contour_union) logging.info('ppa_without_small_holes = %s', ppa_without_small_holes) ppa_polygon = _ClipPpaByCensusTract(ppa_without_small_holes, pal_records) logging.info('contour_union clipped by census tracts: %s', ppa_polygon) if ppa_polygon.is_empty: raise Exception("Empty Polygon is generated, please check the inputs.") if ppa_polygon.geom_type == "MultiPolygon": raise Exception( "Multi Polygon is not supported, please check the inputs.") # Convert Shapely Object to GeoJSON geometry string return utils.ToGeoJson(ppa_polygon)
def CheckTerrainTileCacheOk(): """Check tiles cache well behaved.""" print('Check of tile cache swap:') num_workers = mpool.GetNumWorkerProcesses() if num_workers: tile_stats = mpool.RunOnEachWorkerProcess(_getTileStats) else: tile_stats = [_getTileStats()] num_active_tiles, cnt_per_tile = tile_stats[np.argmax( [tile_stat[0] for tile_stat in tile_stats])] if not num_active_tiles: print('-- Cache ERROR: No active tiles read') elif max(cnt_per_tile) > 1: print( '-- Cache WARNING: cache tile too small - tiles are swapping from cache.' ) if num_workers: pool = mpool.Pool() pool.apply_async(_printTileStats) else: _printTileStats() else: print('-- Cache tile: OK (no swapping)')
def findMoveList(protection_specs, protection_points, registration_requests, grant_requests, num_iter, num_processes, pool=None): """Main routine to find CBSD indices on the move list. Inputs: protection_specs: protection specifications, an object with attributes 'lowFreq' (in Hz), 'highFreq' (in Hz), 'antHeight' (in meters), 'beamwidth' (in degrees), 'threshold' (in dBm/10MHz), 'neighbor_distances' (km), 'min_azimuth', 'max_azimuth' (degrees) where `neighbor_distances` are the neighborhood distances (km) as a sequence: [cata_dist, catb_dist, cata_oob_dist, catb_oob_dist] protection_points: a list of protection points, each one being an object providing attributes 'latitude' and 'longitude' registration_requests: a list of CBSD registration requests, each one being a dictionary containing CBSD registration information grant_requests: a list of grant requests, each one being a dictionary containing grant information; there is a one-to-one mapping between items in registration_requests and grant_requests; a CBSD with more than one grant will have corresponding duplicate items in registration_requests num_iter: number of Monte Carlo iterations num_processes: number of parallel processes to use pool: optional |multiprocessing.Pool| to use Returns: result: a Boolean list (same size as registration_requests/ grant_requests) with TRUE elements at indices having grants on the move list """ grants = data.getGrantsFromRequests(registration_requests, grant_requests) # Find the move list of each protection constraint with a pool of parallel processes. if pool is None: mpool.Configure(num_processes) pool = mpool.Pool() neighbor_distances = protection_specs.neighbor_distances try: min_azimuth = protection_specs.min_azimuth max_azimuth = protection_specs.max_azimuth except AttributeError: min_azimuth, max_azimuth = 0, 360 moveListC = partial(moveListConstraint, low_freq=protection_specs.lowFreq, high_freq=protection_specs.highFreq, grants=grants, inc_ant_height=protection_specs.antHeight, num_iter=num_iter, threshold=protection_specs.threshold, beamwidth=protection_specs.beamwidth, min_azimuth=min_azimuth, max_azimuth=max_azimuth, neighbor_distances=neighbor_distances) M_c, _ = zip(*pool.map(moveListC, protection_points)) # Find the unique CBSD indices in the M_c list of lists. M = set().union(*M_c) # Set to TRUE the elements of the output Boolean array that have grant_index in # the move list. result = np.zeros(len(grants), bool) for k, grant in enumerate(grants): if grant in M: result[k] = True output = result.tolist() return output
def CheckInterference(self, sas_uut_active_grants, margin_db, channel=None, num_iter=None, do_abs_check_single_uut=False, extensive_print=True, output_data=None): """Checks interference of keep list of SAS UUT vs test harness. This compares the aggregated interference (within some margin) generated by: - the test harness keep list, versus - the blended SAS UUT / Test harness keep list for all protection points and azimuth. Args: sas_uut_active_grants: An iterable of authorized |data.CbsdGrantInfo| grants. of the SAS UUT (ie not in the move list). margin_db: Defines the method and margin (in dB) to use for checking the SAS UUT aggregate interference: - a number: the check is done against the reference model aggregate interference using this value as the margin (in dB). [Legacy method]. - a string of one of the following type: + 'target(<number_db>)': the check is done against the DPA protection threshold, with <number> being an allowed margin (in dB). + 'linear(<number_db>): the check is done against the ref model, but by using a margin in linear domain. The `number_db` is still given in dB, but converted into an equivalent mW margin for the target threshold. Then this mW margin is used for the check. channel: A channel as tuple (low_freq_mhz, high_freq_mhz), or None for all channels of that Dpa. num_iter: The number of Monte Carlo iteration for calculating the aggregate interference. If None, use the global class level `num_iteration`. do_abs_check_single_uut: If True, performs check against absolute threshold instead of relative check against ref model, iff only SAS UUT present (no peer SAS). In this mode, move list does not need to be precomputed, or grants setup, (only the passed UUT active grants are used). extensive_print: If True, extensive logging done onto local files. output_result: If an empty list, then it will be populated with detailed interference results (|DpaInterferenceResult|) for each protected point and channel. Indexing is [chan_idx, point_idx], unless channel is unique, in which case indexing is [point_idx]. Returns: True if all SAS UUT aggregated interference are within margin, False otherwise (ie if at least one combined protection point / azimuth fails the test). """ if channel is None: test_passed = True if output_data == []: output_data.extend([[]] * len(self._channels)) else: output_data = [None] * len(self._channels) for k, chan in enumerate(self._channels): if not self.CheckInterference( sas_uut_active_grants, margin_db, chan, num_iter, do_abs_check_single_uut, extensive_print, output_data[k]): test_passed = False return test_passed if num_iter is None: num_iter = Dpa.num_iteration # Manages the various margin_db methods. margin_method = 'std' if isinstance(margin_db, basestring): idx1, idx2 = margin_db.find('('), margin_db.find(')') if idx1 == -1 or idx2 == -1: raise ValueError( 'DPA CheckInterference: margin_db: `%s` not allowed.' 'Use a number, the `target(xx)` or `linear(xx)` options' % margin_db) margin_method = margin_db[:idx1].strip().lower() if margin_method not in ['target', 'linear']: raise ValueError( 'DPA CheckInterference: margin_db method: `%s` not allowed.' 'Use either `target(xx)` or `linear(xx)` options' % margin_method) margin_db = float(margin_db[idx1 + 1:idx2].strip()) # Find the keep list component of TH: SAS UUT and peer SASes. keep_list = self.GetKeepList(channel) keep_list_th_other_sas = [ grant for grant in self._grants if (not grant.is_managed_grant and grant in keep_list) ] keep_list_th_managing_sas = [ grant for grant in self._grants if (grant.is_managed_grant and grant in keep_list) ] # Makes sure we have a list of SAS UUT active grants sas_uut_active_grants = list(sas_uut_active_grants) if extensive_print: # Derive the estimated SAS UUT keep list, ie the SAS UUT active grants # within the neighborhood (defined by distance and frequency). This is # only insured to be a superset of the actual keep list. # Note: to avoid any test harness regression, this list is currently only # used for logging purpose (although it could be passed to the interference # check routine for faster operation). est_keep_list_uut_managing_sas = ml.getDpaNeighborGrants( sas_uut_active_grants, self.protected_points, low_freq=channel[0] * 1e6, high_freq=channel[1] * 1e6, neighbor_distances=self.neighbor_distances) try: self.__PrintKeepLists(keep_list_th_other_sas, keep_list_th_managing_sas, est_keep_list_uut_managing_sas, self.name, channel) except Exception as e: logging.error('Could not print DPA keep lists: %s', e) # Do absolute threshold in some case hard_threshold = None if margin_method == 'target' or (do_abs_check_single_uut and not self._has_th_grants): hard_threshold = self.threshold logging.info( 'DPA Check interf `%s`- channel %s thresh %s bw %s ' 'iter %s azi_range %s nbor_dists %s', self.name, channel, hard_threshold if hard_threshold else ('`MoveList`' if margin_method == 'std' else 'MoveList + Linear'), self.beamwidth, num_iter, self.azimuth_range, self.neighbor_distances) checkPointInterf = functools.partial( _CalcTestPointInterfDiff, channel=channel, keep_list_th_other_sas=keep_list_th_other_sas, keep_list_th_managing_sas=keep_list_th_managing_sas, keep_list_uut_managing_sas=sas_uut_active_grants, radar_height=self.radar_height, beamwidth=self.beamwidth, num_iter=num_iter, azimuth_range=self.azimuth_range, neighbor_distances=self.neighbor_distances, threshold=hard_threshold) pool = mpool.Pool() result = pool.map(checkPointInterf, self.protected_points) if output_data == []: output_data.extend(result) margin_mw = None # for standard or target method if margin_method == 'linear': # linear method margin_mw = Db2Lin(self.threshold + margin_db) - Db2Lin( self.threshold) if extensive_print: try: self.__PrintStatistics(result, self.name, channel, self.threshold, margin_mw) except Exception as e: logging.error('Could not print DPA statistics: %s', e) if margin_mw is None: # standard or target method max_diff_interf = max(r.max_difference for r in result) if max_diff_interf > margin_db: logging.warning( 'DPA Check Fail `%s`- channel %s thresh %s max_diff %s', self.name, channel, hard_threshold if hard_threshold else '`MoveList`', max_diff_interf) else: logging.info('DPA Check Succeed - max_diff %s', max_diff_interf) return max_diff_interf <= margin_db else: # Linear method max_diff_interf_mw = max([ np.max(Db2Lin(r.A_DPA) - Db2Lin(r.A_DPA_ref)) for r in result ]) if max_diff_interf_mw > margin_mw: logging.warning( 'DPA Check Fail `%s`- channel %s thresh `MoveList+Linear`' ' margin_mw excess by %.4fdB', self.name, channel, Lin2Db(max_diff_interf_mw / margin_mw)) else: logging.info( 'DPA Check Succeed - margin_mw headroom by %.4fdB', Lin2Db(max_diff_interf_mw / margin_mw)) return max_diff_interf_mw <= margin_mw
def performIapForPpa(ppa_record, sas_uut_fad_object, sas_th_fad_objects, pal_records): """Computes post IAP interference margin for PPA incumbents. Routine to get protection points within PPA protection area and perform IAP algorithm on each protection point. Args: ppa_record: A PPA record dict. sas_uut_fad_object: A FAD object from SAS UUT. sas_th_fad_object: A list of FAD objects from SAS Test Harness pal_records: PAL records associated with a PPA protection area Returns: ap_iap_ref: The post-IAP allowed interference, as a dict formatted as: {latitude : {longitude : [interference(mW/IAPBW), .., interference(mW/IAPBW)]}} where the list holds all values per channel of that protection point. """ ppa_thresh_q = THRESH_PPA_DBM_PER_IAPBW # Actual protection threshold used for IAP - calculated by applying # a pre-defined Pre-IAP headroom (Mg) at each protection threshold(Q) ppa_iap_threshold = interf.dbToLinear(ppa_thresh_q - MARGIN_PPA_DB) grants = data.getGrantObjectsFromFAD(sas_uut_fad_object, sas_th_fad_objects, ppa_record) # Get number of SAS num_sas = len(sas_th_fad_objects) + 1 logging.debug('$$$$ Getting GRID points for PPA Protection Area $$$$') # Get Fine Grid Points for a PPA protection area protection_points = utils.GridPolygon( ppa_record['zone']['features'][0]['geometry'], PPA_GRID_RES_ARCSEC) # Get the region type of the PPA protection area ppa_region = ppa_record['ppaInfo']['ppaRegionType'] # Find the PAL records for the PAL_IDs defined in PPA records ppa_pal_ids = ppa_record['ppaInfo']['palId'] matching_pal_records = [ pr for pr in pal_records if pr['palId'] == ppa_pal_ids[0] ] if not matching_pal_records: raise ValueError('No matching PAL record, please check input') pal_record = matching_pal_records[0] # Get the frequencies from the PAL records ppa_freq_range = pal_record['channelAssignment']['primaryAssignment'] ppa_low_freq = ppa_freq_range['lowFrequency'] ppa_high_freq = ppa_freq_range['highFrequency'] # Get channels over which area incumbent needs partial/full protection protection_channels = interf.getProtectedChannels(ppa_low_freq, ppa_high_freq) # Apply IAP for each protection constraint with a pool of parallel # processes. logging.debug('$$$$ Calling PPA Protection $$$$') iapPoint = partial(iapPointConstraint, channels=protection_channels, low_freq=ppa_low_freq, high_freq=ppa_high_freq, grants=grants, fss_info=None, esc_antenna_info=None, region_type=ppa_region, threshold=ppa_iap_threshold, protection_ent_type=data.ProtectedEntityType.PPA_AREA) pool = mpool.Pool() iap_interfs = pool.map(iapPoint, protection_points) ap_iap_ref = calculatePostIapAggregateInterference( interf.dbToLinear(ppa_thresh_q), num_sas, iap_interfs) return ap_iap_ref
def performIapForGwpz(gwpz_record, sas_uut_fad_object, sas_th_fad_objects): """Computes post IAP interference margin for GWPZ incumbents. Routine to get protection points within GWPZ protection area and perform IAP algorithm on each protection point. Args: gwpz_record: A GWPZ record dict. sas_uut_fad_object: FAD object from SAS UUT sas_th_fad_objects: A list of FAD objects from SAS Test Harness Returns: ap_iap_ref: The post-IAP allowed interference, as a dict formatted as: {latitude : {longitude : [interference(mW/IAPBW), .., interference(mW/IAPBW)]}} where the list holds all values per channel of that protection point. """ gwpz_thresh_q = THRESH_GWPZ_DBM_PER_IAPBW # Actual protection threshold used for IAP - calculated by applying # a pre-defined Pre-IAP headroom (Mg) at each protection threshold(Q) gwpz_iap_threshold = interf.dbToLinear(gwpz_thresh_q - MARGIN_GWPZ_DB) grants = data.getGrantObjectsFromFAD(sas_uut_fad_object, sas_th_fad_objects) # Get number of SAS num_sas = len(sas_th_fad_objects) + 1 logging.debug('$$$$ Getting GRID points for GWPZ Protection Area $$$$') # Get Fine Grid Points for a GWPZ protection area protection_points = utils.GridPolygon( gwpz_record['zone']['features'][0]['geometry'], GWPZ_GRID_RES_ARCSEC) gwpz_freq_range = gwpz_record['record']['deploymentParam'][0]\ ['operationParam']['operationFrequencyRange'] gwpz_low_freq = gwpz_freq_range['lowFrequency'] gwpz_high_freq = gwpz_freq_range['highFrequency'] gwpz_region = gwpz_record['landCategory'] # Get channels over which area incumbent needs partial/full protection protection_channels = interf.getProtectedChannels(gwpz_low_freq, gwpz_high_freq) logging.debug('$$$$ Calling GWPZ Protection $$$$') iapPoint = partial(iapPointConstraint, channels=protection_channels, low_freq=gwpz_low_freq, high_freq=gwpz_high_freq, grants=grants, fss_info=None, esc_antenna_info=None, region_type=gwpz_region, threshold=gwpz_iap_threshold, protection_ent_type=data.ProtectedEntityType.GWPZ_AREA) pool = mpool.Pool() iap_interfs = pool.map(iapPoint, protection_points) ap_iap_ref = calculatePostIapAggregateInterference( interf.dbToLinear(gwpz_thresh_q), num_sas, iap_interfs) return ap_iap_ref
def CheckInterference(self, sas_uut_active_grants, margin_db, channel=None, num_iter=None, do_abs_check_single_uut=False): """Checks interference of keep list of SAS UUT vs test harness. This compares the aggregated interference (within some margin) generated by: - the test harness keep list, versus - the blended SAS UUT / Test harness keep list for all protection points and azimuth. Args: sas_uut_active_grants: An iterable of authorized |data.CbsdGrantInfo| grants. of the SAS UUT (ie not in the move list). margin_db: The margin (in dB) to use for the check. channel: A channel as tuple (low_freq_mhz, high_freq_mhz), or None for all channels of that Dpa. num_iter: The number of Monte Carlo iteration for calculating the aggregate interference. If None, use the global class level `num_iteration`. do_abs_check_single_uut: If True, performs check against absolute threshold instead of relative check against ref model, iff only SAS UUT present (no peer SAS). In this mode, move list does not need to be precomputed, or grants setup, (only the passed UUT active grants are used). Returns: True if all SAS UUT aggregated interference are within margin, False otherwise (ie if at least one combined protection point / azimuth fails the test). """ if channel is None: for chan in self.channels: if not self.CheckInterference(sas_uut_active_grants, margin_db, chan, num_iter, do_abs_check_single_uut): return False return True if num_iter is None: num_iter = Dpa.num_iteration # Find the keep list component of TH: SAS UUT and peer SASes. keep_list = self.GetKeepList(channel) keep_list_th_other_sas = [ grant for grant in self._grants if (not grant.is_managed_grant and grant in keep_list) ] keep_list_th_managing_sas = [ grant for grant in self._grants if (grant.is_managed_grant and grant in keep_list) ] # Find an extended keep list of UUT. keep_list_uut_managing_sas = list(sas_uut_active_grants) # Note: other code with pre filtering could be: #nbor_list = self.GetNeighborList(channel) #if nbor_list: # # Slight optimization by prefiltering the ones in the nbor list. # keep_list_uut_managing_sas = [ # grant for grant in sas_uut_active_grants # if (grant in nbor_list and grant not in keep_list_th_other_sas)] #else: # keep_list_uut_managing_sas = list(sas_uut_active_grants) # Do absolute threshold in some case hard_threshold = None if do_abs_check_single_uut and not self._has_th_grants: hard_threshold = self.threshold logging.info( 'DPA Check interf `%s`- channel %s thresh %s bw %s ' 'iter %s azi_range %s nbor_dists %s', self.name, channel, hard_threshold if hard_threshold else '`MoveList`', self.beamwidth, num_iter, self.azimuth_range, self.neighbor_distances) logging.debug( 'DPA Check interf `%s` - KL_TH_MGR: %s KL_TH_OTHER: %s KL_UUT_MGR: %s', self.name, keep_list_th_managing_sas, keep_list_th_other_sas, keep_list_uut_managing_sas) checkPointInterf = functools.partial( _CalcTestPointInterfDiff, channel=channel, keep_list_th_other_sas=keep_list_th_other_sas, keep_list_th_managing_sas=keep_list_th_managing_sas, keep_list_uut_managing_sas=keep_list_uut_managing_sas, radar_height=self.radar_height, beamwidth=self.beamwidth, num_iter=num_iter, azimuth_range=self.azimuth_range, neighbor_distances=self.neighbor_distances, threshold=hard_threshold) # TODO(sbdt): could do early stop as soon as one fails, although I would expect # the criteria to be changed into checking 99.x% of success instead of 100%. pool = mpool.Pool() result = pool.map(checkPointInterf, self.protected_points) max_diff_interf = max(result) if max_diff_interf > margin_db: logging.warning( 'DPA Check Fail `%s`- channel %s thresh %s max_diff %s', self.name, channel, hard_threshold if hard_threshold else '`MoveList`', max_diff_interf) else: logging.info('DPA Check Succeed - max_diff %s', max_diff_interf) return max_diff_interf <= margin_db
def calculateAggregateInterferenceForPpa(ppa_record, pal_records, grants): """Calculates per-channel aggregate interference for PPA. Args: ppa_record: A PPA record dict. pal_records: PAL records associated with a PPA protection area grants: An iterable of CBSD grants of type |data.CbsdGrantInfo|. Returns: Aggregate interference to PPA in the nested dictionary format. {latitude : {longitude: [aggr_interf1(mW), ..., aggr_interfK(mW)]}} The list contains the value per protected channel. """ # Get Fine Grid Points for a PPA protection area protection_points = utils.GridPolygon( ppa_record['zone']['features'][0]['geometry'], PPA_GRID_RES_ARCSEC) # Get the region type of the PPA protection area ppa_region = ppa_record['ppaInfo']['ppaRegionType'] # Find the PAL records for the PAL_IDs defined in PPA records ppa_pal_ids = ppa_record['ppaInfo']['palId'] matching_pal_records = [ pr for pr in pal_records if pr['palId'] == ppa_pal_ids[0] ] if not matching_pal_records: raise ValueError( 'AggInterf: No matching PAL record, please check input') pal_record = matching_pal_records[0] # Get the frequencies from the PAL records ppa_freq_range = pal_record['channelAssignment']['primaryAssignment'] ppa_low_freq = ppa_freq_range['lowFrequency'] ppa_high_freq = ppa_freq_range['highFrequency'] # Get channels over which area incumbent needs partial/full protection protection_channels = interf.getProtectedChannels(ppa_low_freq, ppa_high_freq) logging.info( 'Computing aggregateInterferenceForPoint for PPA (%s), channels (%s), ' 'nPoints (%d), grants (%s), region_type (%s)', ppa_record, protection_channels, len(protection_points), grants, ppa_region) logging.debug(' points: %s', protection_points) # Calculate aggregate interference from each protection constraint with a # pool of parallel processes. interfCalculator = partial( aggregateInterferenceForPoint, channels=protection_channels, grants=grants, fss_info=None, esc_antenna_info=None, protection_ent_type=data.ProtectedEntityType.PPA_AREA, region_type=ppa_region) pool = mpool.Pool() interferences = pool.map(interfCalculator, protection_points) return InterferenceDict(interferences)
print 'Num Protection Points: %d' % len(dpa.protected_points) print 'Num CBSD: %d (A: %d %d - B %d)' % (len(grants), n_a_indoor, n_a_outdoor, n_b) print 'Distribution: %s' % ('uniform' if not do_inside_urban_area else 'urban areas only') print 'Move list size: %d' % len_move_list print 'Computation time: %.1fs' % (end_time - start_time) # Check tiles cache well behaved print '' tile_stats = mpool.RunOnEachWorkerProcess(getTileStats) num_active_tiles, cnt_per_tile = tile_stats[0] if not num_active_tiles: print '-- Cache ERROR: No active tiles read' elif max(cnt_per_tile) > 1: print '-- Cache WARNING: cache tile too small - tiles are swapping from cache.' pool = mpool.Pool() pool.apply_async(printTileStats) else: print '-- Cache tile: OK (no swapping)' # Plot the move list if len_move_list: result = dpa.GetMoveListMask((fmin, fmax)) ax.scatter( [cbsd.longitude for k, cbsd in enumerate(all_cbsds) if result[k]], [cbsd.latitude for k, cbsd in enumerate(all_cbsds) if result[k]], color='r', marker='.') plt.show()