def computeInterferenceFssBlocking(cbsd_grant, constraint, fss_info, max_eirp): """Computes interference that a grant causes to a FSS protection point. Routine to compute interference neighborhood grant causes to FSS protection point for blocking passband Args: cbsd_grant: A CBSD grant of type |data.CbsdGrantInfo|. constraint: The protection constraint of type |data.ProtectionConstraint|. fss_info: The FSS information of type |data.FssInformation|. max_eirp: The maximum EIRP allocated to the grant during IAP procedure. Returns: The interference contribution(dBm). """ # Get the propagation loss and incident angles for FSS entity # blocking channels db_loss, incidence_angles, _ = wf_itm.CalcItmPropagationLoss( cbsd_grant.latitude, cbsd_grant.longitude, cbsd_grant.height_agl, constraint.latitude, constraint.longitude, fss_info.height_agl, cbsd_grant.indoor_deployment, reliability=-1, freq_mhz=FREQ_PROP_MODEL_MHZ) # Compute CBSD antenna gain in the direction of protection point ant_gain = antenna.GetStandardAntennaGains(incidence_angles.hor_cbsd, cbsd_grant.antenna_azimuth, cbsd_grant.antenna_beamwidth, cbsd_grant.antenna_gain) # Compute FSS antenna gain in the direction of CBSD fss_ant_gain = antenna.GetFssAntennaGains(incidence_angles.hor_rx, incidence_angles.ver_rx, fss_info.pointing_azimuth, fss_info.pointing_elevation, fss_info.max_gain_dbi) # Get the total antenna gain by summing the antenna gains from CBSD to FSS # and FSS to CBSD effective_ant_gain = ant_gain + fss_ant_gain # Compute EIRP of CBSD grant inside the frequency range of # protection constraint eff_bandwidth = ( min(cbsd_grant.high_frequency, constraint.high_frequency) - cbsd_grant.low_frequency) if eff_bandwidth <= 0: raise ValueError( 'Computing FSS blocking on grant fully inside FSS passband') eirp = getEffectiveSystemEirp(max_eirp, cbsd_grant.antenna_gain, effective_ant_gain, eff_bandwidth) # Calculate the interference contribution interference = eirp - getFssMaskLoss(cbsd_grant, constraint) - db_loss return interference
def computeInterferencePpaGwpzPoint(cbsd_grant, constraint, h_inc_ant, max_eirp, region_type='SUBURBAN'): """Computes interference that a grant causes to GWPZ or PPA protection area. Routine to compute interference neighborhood grant causes to protection point within GWPZ or PPA protection area. Args: cbsd_grant: A CBSD grant of type |data.CbsdGrantInfo|. constraint: The protection constraint of type |data.ProtectionConstraint|. h_inc_ant: The reference incumbent antenna height (in meters). max_eirp: The maximum EIRP allocated to the grant during IAP procedure region_type: Region type of the GWPZ or PPA area: 'URBAN', 'SUBURBAN' or 'RURAL'. Returns: The interference contribution (dBm). """ # Get the propagation loss and incident angles for area entity db_loss, incidence_angles, _ = wf_hybrid.CalcHybridPropagationLoss( cbsd_grant.latitude, cbsd_grant.longitude, cbsd_grant.height_agl, constraint.latitude, constraint.longitude, h_inc_ant, cbsd_grant.indoor_deployment, reliability=-1, freq_mhz=FREQ_PROP_MODEL_MHZ, region=region_type) # Compute CBSD antenna gain in the direction of protection point ant_gain = antenna.GetStandardAntennaGains(incidence_angles.hor_cbsd, cbsd_grant.antenna_azimuth, cbsd_grant.antenna_beamwidth, cbsd_grant.antenna_gain) # Get the exact overlap of the grant over the GWPZ area channels if constraint.entity_type == data.ProtectedEntityType.GWPZ_AREA: grant_overlap_bandwidth = min(cbsd_grant.high_frequency, constraint.high_frequency) \ - max(cbsd_grant.low_frequency, constraint.low_frequency) else: grant_overlap_bandwidth = RBW_HZ # Get the interference value for area entity eirp = getEffectiveSystemEirp(max_eirp, cbsd_grant.antenna_gain, ant_gain, grant_overlap_bandwidth) interference = eirp - db_loss return interference
def calculateOobeInterference(grants_cbsds_oobe_info, fss_point, fss_info): """Calculates the OOBE interference value (MCBSDi,ch + GCBSDi + PLinvi + GFSSi). The interference values are calculated based on the grant with the highest highFrequency value, and added back into the grant object under key: 'oobe_interference'. Args: grants_cbsds_oobe_info : List of dictionaries containing the highest grant of their CBSD and a reference to the CBSD. fss_point: The FSS location as a (longitude, latitude) tuple. fss_info: The |data.FssInformation| of the FSS. """ # Get values of MCBSD,GCBSD,GFSS and LCBSD for each CBSD. for grant in grants_cbsds_oobe_info: if not grant['cbsd']['grants']: continue cbsd = data.constructCbsdGrantInfo(grant['cbsd']['registration'], None) # Get the MCBSD (ie the conducted OOBE power) mcbsd = grant['mcbsd'] # Computes the path loss lcbsd, incidence_angle, _ = wf_itm.CalcItmPropagationLoss( cbsd.latitude, cbsd.longitude, cbsd.height_agl, fss_point[1], fss_point[0], fss_info.height_agl, cbsd.indoor_deployment, reliability=-1, freq_mhz=interf.FREQ_PROP_MODEL_MHZ) # The CBSD antenna gain towards FSS gcbsd = antenna.GetStandardAntennaGains( incidence_angle.hor_cbsd, cbsd.antenna_azimuth, cbsd.antenna_beamwidth, cbsd.antenna_gain) # The FSS antenna gain gfss = antenna.GetFssAntennaGains( incidence_angle.hor_rx, incidence_angle.ver_rx, fss_info.pointing_azimuth, fss_info.pointing_elevation, fss_info.max_gain_dbi) # The OOBE interference oobe_interference = mcbsd + gcbsd - lcbsd + gfss - interf.IN_BAND_INSERTION_LOSS grant['oobe_interference'] = oobe_interference
def computeInterferenceEsc(cbsd_grant, constraint, esc_antenna_info, max_eirp): """Computes interference that a grant causes to a ESC protection point. Routine to compute interference neighborhood grant causes to ESC protection point. Args: cbsd_grant: A CBSD grant of type |data.CbsdGrantInfo|. constraint: The protection constraint of type |data.ProtectionConstraint|. esc_antenna_info: ESC antenna information of type |data.EscInformation|. max_eirp: The maximum EIRP allocated to the grant during IAP procedure Returns: The interference contribution(dBm). """ # Get the propagation loss and incident angles for ESC entity db_loss, incidence_angles, _ = wf_itm.CalcItmPropagationLoss( cbsd_grant.latitude, cbsd_grant.longitude, cbsd_grant.height_agl, constraint.latitude, constraint.longitude, esc_antenna_info.antenna_height, cbsd_grant.indoor_deployment, reliability=-1, freq_mhz=FREQ_PROP_MODEL_MHZ) # Compute CBSD antenna gain in the direction of protection point ant_gain = antenna.GetStandardAntennaGains(incidence_angles.hor_cbsd, cbsd_grant.antenna_azimuth, cbsd_grant.antenna_beamwidth, cbsd_grant.antenna_gain) # Compute ESC antenna gain in the direction of CBSD esc_ant_gain = antenna.GetAntennaPatternGains( incidence_angles.hor_rx, esc_antenna_info.antenna_azimuth, esc_antenna_info.antenna_gain_pattern) # Get the total antenna gain by summing the antenna gains from CBSD to ESC # and ESC to CBSD effective_ant_gain = ant_gain + esc_ant_gain # Compute the interference value for ESC entity eirp = getEffectiveSystemEirp(max_eirp, cbsd_grant.antenna_gain, effective_ant_gain) interference = eirp - db_loss - getEscMaskLoss(constraint) return interference
def _GetPolygon(device): """Returns the PPA contour for a single CBSD device, as a shapely polygon.""" install_param = device['installationParam'] eirp_capability = install_param.get( 'eirpCapability', MAX_ALLOWABLE_EIRP_PER_10_MHZ_CAT_A if device['cbsdCategory'] == 'A' else MAX_ALLOWABLE_EIRP_PER_10_MHZ_CAT_B) # Compute all the Points in 0-359 every 200m up to 40km distances = np.arange(0.2, 40.1, 0.2) azimuths = np.arange(0.0, 360.0) latitudes, longitudes, _ = zip(*[ vincenty.GeodesicPoints(install_param['latitude'], install_param['longitude'], distances, azimuth) for azimuth in azimuths ]) # Compute the Gain for all Direction # Note: some parameters are optional for catA, so falling back to None (omni) then antenna_gains = antenna.GetStandardAntennaGains( azimuths, install_param['antennaAzimuth'] if 'antennaAzimuth' in install_param else None, install_param['antennaBeamwidth'] if 'antennaBeamwidth' in install_param else None, install_param['antennaGain']) # Get the Nlcd Region Type for Cbsd cbsd_region_code = drive.nlcd_driver.GetLandCoverCodes( install_param['latitude'], install_param['longitude']) cbsd_region_type = nlcd.GetRegionType(cbsd_region_code) # Compute the Path Loss, and contour based on Gain and Path Loss Comparing with Threshold # Smoothing Contour using Hamming Filter contour_dists_km = _HammingFilter([ _CalculateDbLossForEachPointAndGetContour(install_param, eirp_capability, ant_gain, cbsd_region_type, radial_lats, radial_lons) for radial_lats, radial_lons, ant_gain in zip(latitudes, longitudes, antenna_gains) ]) # Generating lat, lon for Contour contour_lats, contour_lons, _ = zip(*[ vincenty.GeodesicPoint(install_param['latitude'], install_param['longitude'], dists, az) for dists, az in zip(contour_dists_km, azimuths) ]) return sgeo.Polygon(zip(contour_lons, contour_lats)).buffer(0)
def computePropagationAntennaModel(request): reliability_level = request['reliabilityLevel'] if reliability_level not in [-1, 0.05, 0.95]: raise ValueError('reliability_level not in [-1, 0.05, 0.95]') tx = request['cbsd'] if ('fss' in request) and ('ppa' in request): raise ValueError('fss and ppa in request') elif 'ppa' in request: rx = {} rx['height'] = 1.5 isfss = False coordinates = [] ppa = request['ppa'] arcsec = 1 ppa_points = geoutils.GridPolygon(ppa['geometry'], arcsec) if len(ppa_points) == 1: rx['longitude'] = ppa_points[0][0] rx['latitude'] = ppa_points[0][1] elif len(ppa_points) == 0: raise ValueError('ppa boundary contains no protection point') else: raise ValueError( 'ppa boundary contains more than a single protection point') region_val = drive.nlcd_driver.RegionNlcdVote( [[rx['latitude'], rx['longitude']]]) elif 'fss' in request: isfss = True rx = request['fss'] else: raise ValueError('Neither fss nor ppa in request') # ITM pathloss (if receiver type is FSS) or the hybrid model pathloss (if receiver type is PPA) and corresponding antenna gains. # The test specification notes that the SAS UUT shall use default values for w1 and w2 in the ITM model. result = {} if isfss: path_loss = wf_itm.CalcItmPropagationLoss( tx['latitude'], tx['longitude'], tx['height'], rx['latitude'], rx['longitude'], rx['height'], cbsd_indoor=tx['indoorDeployment'], reliability=reliability_level, freq_mhz=3625., is_height_cbsd_amsl=(tx['heightType'] == 'AMSL')) result['pathlossDb'] = path_loss.db_loss gain_tx_rx = antenna.GetStandardAntennaGains( path_loss.incidence_angles.hor_cbsd, ant_azimuth=tx['antennaAzimuth'], ant_beamwidth=tx['antennaBeamwidth'], ant_gain=tx['antennaGain']) result['txAntennaGainDbi'] = gain_tx_rx if 'rxAntennaGainRequired' in rx: hor_dirs = path_loss.incidence_angles.hor_rx ver_dirs = path_loss.incidence_angles.ver_rx gain_rx_tx = antenna.GetFssAntennaGains(hor_dirs, ver_dirs, rx['antennaAzimuth'], rx['antennaElevation'], rx['antennaGain']) result['rxAntennaGainDbi'] = gain_rx_tx else: path_loss = wf_hybrid.CalcHybridPropagationLoss( tx['latitude'], tx['longitude'], tx['height'], rx['latitude'], rx['longitude'], rx['height'], cbsd_indoor=tx['indoorDeployment'], reliability=-1, freq_mhz=3625., region=region_val, is_height_cbsd_amsl=(tx['heightType'] == 'AMSL')) result['pathlossDb'] = path_loss.db_loss gain_tx_rx = antenna.GetStandardAntennaGains( path_loss.incidence_angles.hor_cbsd, ant_azimuth=tx['antennaAzimuth'], ant_beamwidth=tx['antennaBeamwidth'], ant_gain=tx['antennaGain']) result['txAntennaGainDbi'] = gain_tx_rx return result
# - then calculate all path losses and received RSSI at FSS # (without FSS antenna taken into account) cbsd_rssi = np.zeros(len(cbsds)) cbsd_incidence_angles = [] for k, cbsd in enumerate(cbsds): if not (k%10): sys.stdout.write('*') sys.stdout.flush() db_loss, incidence_angles, _ = wf_itm.CalcItmPropagationLoss( cbsd.latitude, cbsd.longitude, cbsd.height_agl, base_fss.latitude, base_fss.longitude, base_fss.height_agl, cbsd.is_indoor, reliability=-1) cbsd_effective_eirp = antenna.GetStandardAntennaGains( incidence_angles.hor_cbsd, cbsd.antenna_azimuth, cbsd.antenna_beamwidth, cbsd.eirp_dbm_mhz) cbsd_rssi[k] = cbsd_effective_eirp - db_loss cbsd_incidence_angles.append(incidence_angles) # - then compute the aggregate average interference for each FSS entity # (ie each different possible FSS antenna pointing) fss_total_rssi = [] for fss_entity in fss_entities: hor_dirs = [inc_angle.hor_rx for inc_angle in cbsd_incidence_angles] ver_dirs = [inc_angle.ver_rx for inc_angle in cbsd_incidence_angles] fss_ant_gains = antenna.GetFssAntennaGains( hor_dirs, ver_dirs, fss_entity.pointing_azimuth,
def computeInterference(grant, constraint, inc_ant_height, num_iteration, dpa_type): """Calculate interference contribution of each grant in the neighborhood to the protection constraint c. Inputs: cbsd_grant: a |data.CbsdGrantInfo| grant constraint: protection constraint of type |data.ProtectionConstraint| inc_ant_height: reference incumbent antenna height (in meters) num_iteration: a number of Monte Carlo iterations dpa_type: an enum member of class DpaType Returns: A tuple of interference: interference contribution, a tuple with named fields 'randomInterference' (K random interference contributions of the grant to protection constraint c), and 'bearing_c_cbsd' (bearing from c to CBSD grant location). medianInterference: the median interference. """ # Get frequency information low_freq_cbsd = grant.low_frequency high_freq_cbsd = grant.high_frequency low_freq_c = constraint.low_frequency high_freq_c = constraint.high_frequency # Compute median and K random realizations of path loss/interference contribution # based on ITM model as defined in [R2-SGN-03] (in dB) reliabilities = np.random.uniform(0.001, 0.999, num_iteration) # get K random # reliability values from an uniform distribution over [0.001,0.999) reliabilities = np.append(reliabilities, [0.5]) # add 0.5 (for median loss) as # a last value to reliabilities array results = wf_itm.CalcItmPropagationLoss(grant.latitude, grant.longitude, grant.height_agl, constraint.latitude, constraint.longitude, inc_ant_height, grant.indoor_deployment, reliability=reliabilities, freq_mhz=FREQ_PROP_MODEL) path_loss = np.array(results.db_loss) # Compute CBSD antenna gain in the direction of protection point ant_gain = antenna.GetStandardAntennaGains( results.incidence_angles.hor_cbsd, grant.antenna_azimuth, grant.antenna_beamwidth, grant.antenna_gain) # Compute EIRP of CBSD grant inside the frequency range of protection constraint if dpa_type is not DpaType.OUT_OF_BAND: # For co-channel offshore/inland DPAs # CBSD co-channel bandwidth overlapping with # frequency range of protection constraint co_channel_bw = min(high_freq_cbsd, high_freq_c) - max( low_freq_cbsd, low_freq_c) # Compute EIRP within co-channel bandwidth eirp_cbsd = ((grant.max_eirp - grant.antenna_gain) + ant_gain + 10 * np.log10(co_channel_bw / 1.e6)) else: # For out-of-band inland DPAs # Compute out-of-band conducted power oob_power = ComputeOOBConductedPower(low_freq_cbsd, low_freq_c, high_freq_c) # Compute out-of-band EIRP eirp_cbsd = oob_power + ant_gain # Calculate the interference contributions interf = eirp_cbsd - path_loss median_interf = interf[-1] # last element is the median interference K_interf = interf[:-1] # first 'K' interference # Store interference contributions interference = InterferenceContribution( randomInterference=K_interf, bearing_c_cbsd=results.incidence_angles.hor_rx) return interference, median_interf
def test_WINNF_FT_S_BPR_2(self, config_filename): """[Configurable] Grant Request from CBSDs within the Shared Zone adjacent the Canadian border. """ config = loadConfig(config_filename) # Very light checking of the config file. self.assertValidConfig( config, { 'registrationRequests': list, 'conditionalRegistrationData': list, 'grantRequests': list }) self.assertEqual( len(config['registrationRequests']), len(config['grantRequests'])) # Whitelist FCC ID and User ID. for device in config['registrationRequests']: self._sas_admin.InjectFccId({'fccId': device['fccId']}) self._sas_admin.InjectUserId({'userId': device['userId']}) # Pre-load conditional registration data. if config['conditionalRegistrationData']: self._sas_admin.PreloadRegistrationData({ 'registrationData': config['conditionalRegistrationData'] }) # Register CBSDs. request = {'registrationRequest': config['registrationRequests']} responses = self._sas.Registration(request)['registrationResponse'] # Check registration response. self.assertEqual(len(responses), len(config['registrationRequests'])) grant_request = [] index_of_devices_with_success_registration_response = [] for i, response in enumerate(responses): if response['response']['responseCode'] == 0: self.assertTrue('cbsdId' in response) index_of_devices_with_success_registration_response.append(i) config['grantRequests'][i]['cbsdId'] = response['cbsdId'] grant_request.append(config['grantRequests'][i]) del request, responses if not grant_request: return # SAS passes immediately, since none of the registration requests # succeeded in this case. # For CBSDs successfully registered in Step 1, Send grant request request = {'grantRequest': grant_request} responses = self._sas.Grant(request)['grantResponse'] # Check grant response self.assertEqual(len(responses), len(grant_request)) self.assertEqual( len(responses), len(index_of_devices_with_success_registration_response)) for i, index in enumerate( index_of_devices_with_success_registration_response): logging.info('Looking at Grant response number: %d', i) if (config['grantRequests'][i]['operationParam'] ['operationFrequencyRange']['highFrequency'] <= 3650e6): logging.info( 'Grant is not subject to Arrangement R because it does not overlap ' 'with 3650-3700 MHz.') continue if 'installationParam' in config['registrationRequests'][index]: cbsd_information = config['registrationRequests'][index] else: for conditional in config['conditionalRegistrationData']: if (config['registrationRequests'][index]['fccId'] == conditional['fccId']) and ( config['registrationRequests'][index]['cbsdSerialNumber'] == conditional['cbsdSerialNumber']): cbsd_information = conditional cbsd_lat = cbsd_information['installationParam']['latitude'] cbsd_lon = cbsd_information['installationParam']['longitude'] cbsd_ant_azi = cbsd_information['installationParam']['antennaAzimuth'] cbsd_ant_beamwidth = cbsd_information['installationParam'][ 'antennaBeamwidth'] # Check if CBSD is in the Border Sharing Zone, Get the closest point. (is_in_sharing_zone, closest_point_lat, closest_point_lon) = utils.CheckCbsdInBorderSharingZone( cbsd_lat, cbsd_lon, cbsd_ant_azi, cbsd_ant_beamwidth) logging.info('CBSD is in Border Sharing Zone?: %s', is_in_sharing_zone) if not is_in_sharing_zone: continue # CBSD not in the sharing zone; no PFD check is required. # Proceed with PFD calculation logging.info('Closest point in the border: Lat is %f', closest_point_lat) logging.info('Closest point in the border: Long is %f', closest_point_lon) # requested_eirp (p) p = config['grantRequests'][index]['operationParam']['maxEirp'] # Calculate PL cbsd_height = cbsd_information['installationParam']['height'] cbsd_height_type = cbsd_information['installationParam']['heightType'] is_cbsd_indoor = cbsd_information['installationParam']['indoorDeployment'] closest_point_height = 1.5 # According to the spec freq_mhz = 3625. # Always in all SAS propagation = wf_itm.CalcItmPropagationLoss( cbsd_lat, cbsd_lon, cbsd_height, closest_point_lat, closest_point_lon, closest_point_height, reliability=0.5, cbsd_indoor=is_cbsd_indoor, freq_mhz=freq_mhz, is_height_cbsd_amsl=(cbsd_height_type == 'AMSL')) pl = propagation.db_loss logging.info('Propagation - db_loss: %f', propagation.db_loss) bearing = propagation.incidence_angles.hor_cbsd logging.info('Bearing: %f', propagation.incidence_angles.hor_cbsd) # Calculate effective antenna gain max_ant_gain = cbsd_information['installationParam']['antennaGain'] ant_gain = antenna.GetStandardAntennaGains( bearing, cbsd_ant_azi, cbsd_ant_beamwidth, max_ant_gain) logging.info('Effective Antenna Gain: %f', ant_gain) # Calculate PFD where: # p = requested_eirp # effective_eirp = p - maxAntGain + antGain # PFD = effective_eirp - pl + 32.6 pfd = p - max_ant_gain + ant_gain - pl + 32.6 logging.info('Power Flex Density: %f dBm/m2/MHz', pfd) if pfd > -80: self.assertTrue(responses[i]['response']['responseCode'] == 400)
def test_WINNF_FT_S_QPR_8(self, config_filename): """[Configurable] Unsuccessful Grant Request from CBSDs within 4.8 km of the FCC Field Offices with multiple grants. """ config = loadConfig(config_filename) self.assertValidConfig( config, { 'registrationRequest': dict, 'conditionalRegistrationData': list, 'grantRequest1': dict, 'grantRequest2': dict }) # Whitelist FCC ID and User ID. self._sas_admin.InjectFccId( {'fccId': config['registrationRequest']['fccId']}) self._sas_admin.InjectUserId( {'userId': config['registrationRequest']['userId']}) # Pre-load conditional registration data. if config['conditionalRegistrationData']: self._sas_admin.PreloadRegistrationData( {'registrationData': config['conditionalRegistrationData']}) # Step 1: Register CBSD. request = {'registrationRequest': [config['registrationRequest']]} response = self._sas.Registration(request)['registrationResponse'] # Check registration response. self.assertEqual(len(response), 1) if response[0]['response']['responseCode'] != 0: return # SAS passes immediately in this case. cbsd_id = response[0]['cbsdId'] del request, response # Step 2: Request first grant. config['grantRequest1']['cbsdId'] = cbsd_id grant_request = config['grantRequest1'] request = {'grantRequest': [grant_request]} response = self._sas.Grant(request)['grantResponse'][0] # Check if the first grant response is successful. grant1_approved = response['response']['responseCode'] == 0 del request, response # Step 3: Request second grant config['grantRequest2']['cbsdId'] = cbsd_id grant_request = config['grantRequest2'] request = {'grantRequest': [grant_request]} response = self._sas.Grant(request)['grantResponse'][0] # Check if the second grant response is successful. grant2_approved = response['response']['responseCode'] == 0 del request, response if not (grant1_approved or grant2_approved): logging.info('Both grant requests were rejected') return # SAS passes immediately in this case. # Calculate the closest FCC office lat_cbsd = config['conditionalRegistrationData'][0][ 'installationParam']['latitude'] lon_cbsd = config['conditionalRegistrationData'][0][ 'installationParam']['longitude'] distance_offices = [ vincenty.GeodesicDistanceBearing(lat_cbsd, lon_cbsd, office['latitude'], office['longitude'])[0] for office in self.fcc_offices ] index_closest = np.argmin(distance_offices) closest_fcc_office = self.fcc_offices[index_closest] logging.info('Closest FCC Office Lat: %f', closest_fcc_office['latitude']) logging.info('Closest FCC Office Long: %f', closest_fcc_office['longitude']) # Calculate bearing and ant_gain _, bearing, _ = vincenty.GeodesicDistanceBearing( lat_cbsd, lon_cbsd, closest_fcc_office['latitude'], closest_fcc_office['longitude']) logging.info('Bearing: %f', bearing) ant_gain_dbi = antenna.GetStandardAntennaGains( bearing, config['conditionalRegistrationData'][0] ['installationParam']['antennaAzimuth'], config['conditionalRegistrationData'][0]['installationParam'] ['antennaBeamwidth'], config['conditionalRegistrationData'][0] ['installationParam']['antennaGain']) logging.info('Ant Gain: %f dBi', ant_gain_dbi) max_ant_gain_dbi = (config['conditionalRegistrationData'][0] ['installationParam']['antennaGain']) logging.info('Max Ant Gain: %f dBi', max_ant_gain_dbi) # Gather values required for calculating Total EIRP grant1_eirp = 0 if grant1_approved: p1 = config['grantRequest1']['operationParam']['maxEirp'] logging.info('Grant 1 Max Eirp: %f dBm/MHz', p1) bw1 = (config['grantRequest1']['operationParam'] ['operationFrequencyRange']['highFrequency'] - config['grantRequest1']['operationParam'] ['operationFrequencyRange']['lowFrequency']) / 1.e6 logging.info('Grant 1 Bandwidth: %f', bw1) grant1_eirp = (10**(p1 / 10.0)) * bw1 logging.info('Grant 1 nominal EIRP is %f', grant1_eirp) grant2_eirp = 0 if grant2_approved: p2 = config['grantRequest2']['operationParam']['maxEirp'] logging.info('Grant 2 Max Eirp: %f dBm/MHz', p2) bw2 = (config['grantRequest2']['operationParam'] ['operationFrequencyRange']['highFrequency'] - config['grantRequest2']['operationParam'] ['operationFrequencyRange']['lowFrequency']) / 1.e6 logging.info('Grant 2 Bandwidth: %f', bw2) grant2_eirp = (10**(p2 / 10.0)) * bw2 logging.info('Grant 2 nominal EIRP is %f', grant2_eirp) # Step 4: Calculate Total EIRP total_eirp_dbm = ant_gain_dbi - max_ant_gain_dbi + ( 10 * np.log10(grant1_eirp + grant2_eirp)) logging.info('Total EIRP is %f dBm', total_eirp_dbm) # CHECK: Total EIRP of all approved grants is <= 49.15 dBm self.assertLessEqual(total_eirp_dbm, 49.15)
def test_WINNF_FT_S_QPR_7(self, config_filename): """[Configurable] Unsuccessful Grant Request from CBSDs within 4.8 km of the FCC Field Offices. """ config = loadConfig(config_filename) # Very light checking of the config file. self.assertValidConfig( config, { 'registrationRequest': dict, 'conditionalRegistrationData': list, 'grantRequest': dict }) # Whitelist FCC ID and User ID. self._sas_admin.InjectFccId( {'fccId': config['registrationRequest']['fccId']}) self._sas_admin.InjectUserId( {'userId': config['registrationRequest']['userId']}) # Pre-load conditional registration data. if config['conditionalRegistrationData']: self._sas_admin.PreloadRegistrationData( {'registrationData': config['conditionalRegistrationData']}) # Register CBSD. request = {'registrationRequest': [config['registrationRequest']]} response = self._sas.Registration(request)['registrationResponse'] # Check registration response. self.assertEqual(len(response), 1) if response[0]['response']['responseCode'] != 0: return # SAS passes immediately in this case. cbsd_id = response[0]['cbsdId'] del request, response # Calculate the closest FCC office lat_cbsd = config['conditionalRegistrationData'][0][ 'installationParam']['latitude'] lon_cbsd = config['conditionalRegistrationData'][0][ 'installationParam']['longitude'] distance_offices = [ vincenty.GeodesicDistanceBearing(lat_cbsd, lon_cbsd, office['latitude'], office['longitude'])[0] for office in self.fcc_offices ] index_closest = np.argmin(distance_offices) closest_fcc_office = self.fcc_offices[index_closest] logging.info('Closest FCC office Lat: %f', closest_fcc_office['latitude']) logging.info('Closest FCC office Long: %f', closest_fcc_office['longitude']) # Calculate bearing and ant_gain _, bearing, _ = vincenty.GeodesicDistanceBearing( lat_cbsd, lon_cbsd, closest_fcc_office['latitude'], closest_fcc_office['longitude']) ant_gain = antenna.GetStandardAntennaGains( bearing, config['conditionalRegistrationData'][0] ['installationParam']['antennaAzimuth'], config['conditionalRegistrationData'][0]['installationParam'] ['antennaBeamwidth'], config['conditionalRegistrationData'][0] ['installationParam']['antennaGain']) logging.info('ant_gain is %f dBi', ant_gain) # Gather values required for calculating EIRP p = config['grantRequest']['operationParam']['maxEirp'] logging.info('Grant maxEirp is %f', p) max_ant_gain = (config['conditionalRegistrationData'][0] ['installationParam']['antennaGain']) logging.info('max_ant_gain is %f dBi', max_ant_gain) bw = (config['grantRequest']['operationParam'] ['operationFrequencyRange']['highFrequency'] - config['grantRequest']['operationParam'] ['operationFrequencyRange']['lowFrequency']) / 1.e6 logging.info('bw is %f MHz', bw) # Calculate EIRP to verify grant response eirp = (p - max_ant_gain + ant_gain + (10 * np.log10(bw))) logging.info('EIRP is %f dBm', eirp) # If successfully registered, CBSD sends a grant request config['grantRequest']['cbsdId'] = cbsd_id grant_request = config['grantRequest'] request = {'grantRequest': [grant_request]} response = self._sas.Grant(request)['grantResponse'] # Check grant response self.assertEqual(len(response), 1) # If EIRP <= 49.15 dBm = SUCCESS if eirp <= 49.15: self.assertEqual(response[0]['response']['responseCode'], 0) # If EIRP > 49.15 dBm = INTERFERENCE else: self.assertEqual(response[0]['response']['responseCode'], 400)
def test_WINNF_FT_S_QPR_5(self, config_filename): """[Configurable] Unsuccessful Grant Request from CBSDs within Coordination Area around Table Mountain Quiet Zone (QZ) with Multiple Grants. """ config = loadConfig(config_filename) self.assertValidConfig( config, { 'registrationRequests': list, 'conditionalRegistrationData': list, 'grantRequestsN1': list, 'grantRequestsN2': list }) self.assertEqual(len(config['registrationRequests']), len(config['grantRequestsN1'])) self.assertEqual(len(config['grantRequestsN1']), len(config['grantRequestsN2'])) # Whitelist FCC ID and User ID. for device in config['registrationRequests']: self._sas_admin.InjectFccId({'fccId': device['fccId']}) self._sas_admin.InjectUserId({'userId': device['userId']}) # Pre-load conditional registration data. if config['conditionalRegistrationData']: self._sas_admin.PreloadRegistrationData( {'registrationData': config['conditionalRegistrationData']}) # Step 1: Register CBSDs. request = {'registrationRequest': config['registrationRequests']} responses = self._sas.Registration(request)['registrationResponse'] # Check registration response. self.assertEqual(len(responses), len(config['registrationRequests'])) grant_request_n1 = [] grant_request_n2 = [] successful_reg_requests = [] for i, response in enumerate(responses): if response['response']['responseCode'] == 0: self.assertTrue('cbsdId' in response) successful_reg_requests.append( config['registrationRequests'][i]) config['grantRequestsN1'][i]['cbsdId'] = response['cbsdId'] grant_request_n1.append(config['grantRequestsN1'][i]) config['grantRequestsN2'][i]['cbsdId'] = response['cbsdId'] grant_request_n2.append(config['grantRequestsN2'][i]) del request, responses if not successful_reg_requests: return # SAS passes immediately, since none of the registration requests # succeeded in this case. # Step 2: For CBSDs successfully registered in Step 1, Send grant request 1 request1 = {'grantRequest': grant_request_n1} grant_responses1 = self._sas.Grant(request1)['grantResponse'] # Check grant response 1 self.assertEqual(len(grant_responses1), len(grant_request_n1)) # Step 3: For CBSDs successfully registered in Step 1, Send grant request 2 request2 = {'grantRequest': grant_request_n2} grant_responses2 = self._sas.Grant(request2)['grantResponse'] # Check grant response 2 self.assertEqual(len(grant_responses2), len(grant_request_n2)) for i, device in enumerate(successful_reg_requests): if 'installationParam' in device: cbsd_information = device else: for conditional in config['conditionalRegistrationData']: if (device['fccId'] == conditional['fccId']) and ( device['cbsdSerialNumber'] == conditional['cbsdSerialNumber']): cbsd_information = conditional logging.info( 'Looking at device with FccID: %s / CbsdSerialNumber: %s', cbsd_information['fccId'], cbsd_information['cbsdSerialNumber']) grant_response1 = grant_responses1[i] grant_response2 = grant_responses2[i] if not (grant_response1['response']['responseCode'] == 0 or grant_response2['response']['responseCode'] == 0): logging.info( 'Both grant requests were rejected for this device.') continue # Skip further calculation for this device. # Calculate PL logging.info( 'Calculating PL for device with FccID: %s / CbsdSerialNumber: %s', cbsd_information['fccId'], cbsd_information['cbsdSerialNumber']) cbsd_lat = cbsd_information['installationParam']['latitude'] cbsd_lon = cbsd_information['installationParam']['longitude'] cbsd_ant_azi = cbsd_information['installationParam'][ 'antennaAzimuth'] cbsd_ant_beamwidth = cbsd_information['installationParam'][ 'antennaBeamwidth'] cbsd_height = cbsd_information['installationParam']['height'] cbsd_height_type = cbsd_information['installationParam'][ 'heightType'] is_cbsd_indoor = cbsd_information['installationParam'][ 'indoorDeployment'] freq_mhz = 3625. # Always in all SAS table_mountain_quiet_zone_lat = 40.130660 table_mountain_quiet_zone_long = -105.244596 table_mountain_quiet_zone_height = 9 # 9 m: According to the spec propagation = wf_itm.CalcItmPropagationLoss( cbsd_lat, cbsd_lon, cbsd_height, table_mountain_quiet_zone_lat, table_mountain_quiet_zone_long, table_mountain_quiet_zone_height, cbsd_indoor=is_cbsd_indoor, reliability=0.5, freq_mhz=freq_mhz, is_height_cbsd_amsl=(cbsd_height_type == 'AMSL')) pl = propagation.db_loss logging.info('Propagation:db_loss: %f', pl) bearing = propagation.incidence_angles.hor_cbsd logging.info('Bearing: %f', bearing) # Calculate effective antenna gain max_ant_gain_dbi = cbsd_information['installationParam'][ 'antennaGain'] ant_gain_dbi = antenna.GetStandardAntennaGains( bearing, cbsd_ant_azi, cbsd_ant_beamwidth, max_ant_gain_dbi) logging.info('Effective Antenna Gain: %f', ant_gain_dbi) # Gather values required for calculating Total Interference grant1_eirp = 0 if grant_response1['response']['responseCode'] == 0: p1 = grant_request_n1[i]['operationParam']['maxEirp'] logging.info('Grant 1 Max Eirp: %f dBm/MHz', p1) bw1 = (grant_request_n1[i]['operationParam'] ['operationFrequencyRange']['highFrequency'] - grant_request_n1[i]['operationParam'] ['operationFrequencyRange']['lowFrequency']) / 1.e6 logging.info('Grant 1 Bandwidth: %f', bw1) grant1_eirp = (10**(p1 / 10.0)) * bw1 logging.info('Grant 1 EIRP is %f', grant1_eirp) grant2_eirp = 0 if grant_response2['response']['responseCode'] == 0: p2 = grant_request_n2[i]['operationParam']['maxEirp'] logging.info('Grant 2 Max Eirp: %f dBm/MHz', p2) bw2 = (grant_request_n2[i]['operationParam'] ['operationFrequencyRange']['highFrequency'] - grant_request_n2[i]['operationParam'] ['operationFrequencyRange']['lowFrequency']) / 1.e6 logging.info('Grant 2 Bandwidth: %f', bw2) grant2_eirp = (10**(p2 / 10.0)) * bw2 logging.info('Grant 2 EIRP is %f', grant2_eirp) # Step 4: Calculate Total Interference total_interference_dbm = ant_gain_dbi - max_ant_gain_dbi + ( 10 * np.log10(grant1_eirp + grant2_eirp)) - pl logging.info('Total Interference is %f dBm', total_interference_dbm) # CHECK: Total Interference from all approved grants is <= -88.4 dBm self.assertLessEqual(total_interference_dbm, -88.4)
def test_standard_gain(self): # Isotropic antenna gains = antenna.GetStandardAntennaGains([0, 90, 180, 270], 0, None, 5) self.assertEqual(np.max(np.abs(gains - 5 * np.ones(4))), 0) gains = antenna.GetStandardAntennaGains([0, 90, 180, 270], None, 90, 5) self.assertEqual(np.max(np.abs(gains - 5 * np.ones(4))), 0) gains = antenna.GetStandardAntennaGains([0, 90, 180, 270], 0, 0, 5) self.assertEqual(np.max(np.abs(gains - 5 * np.ones(4))), 0) gains = antenna.GetStandardAntennaGains([0, 90, 180, 270], 0, 360, 5) self.assertEqual(np.max(np.abs(gains - 5 * np.ones(4))), 0) # Back lobe: maximum attenuation gain = antenna.GetStandardAntennaGains(180, 0, 120, 10) self.assertEqual(gain, -10) # At beamwidth, cutoff by 3dB (by definition) gain = antenna.GetStandardAntennaGains(60, 0, 120, 10) self.assertEqual(gain, 10 - 3) gain = antenna.GetStandardAntennaGains(5.5, 50.5, 90, 10) self.assertEqual(gain, 10 - 3) # Bore sight: full gain gain = antenna.GetStandardAntennaGains(50.5, 50.5, 90, 10) self.assertEqual(gain, 10) # At half beamwidth, -0.75dB + integer values well managed gain = antenna.GetStandardAntennaGains(25, 50, 100, 10) self.assertEqual(gain, 10 - 0.75) # At twice beamwidth, -12dB gain = antenna.GetStandardAntennaGains(310, 50, 100, 10) self.assertEqual(gain, 10 - 12) # Vectorized vs scalar hor_dirs = [3.5, 47.3, 342] gains = antenna.GetStandardAntennaGains(hor_dirs, 123.3, 90, 12.4) for k, hor in enumerate(hor_dirs): gain = antenna.GetStandardAntennaGains(hor, 123.3, 90, 12.4) self.assertEqual(gain, gains[k])