示例#1
0
    def getRadarsByPosition(self, lat, lon, alt, distMax=4000., datetime=None):
        """Get a list of radars able to see a given point on Earth

        Belongs to
        ----------
        class : network

        Parameters
        ----------
        lat
            latitude of given point in geographic coordinates
        lon
            longitude of given point in geographic coordinates
        alt
            altitude of point above the Earth's surface in km
        distMax
            maximum distance of given point from radar
        datetime : Optional[datetime.datetime object]
            python datetime object (defaults to today)

        Returns
        -------
        dict
            A dictionnary with keys:
            radars : a list of radar objects (:class:`radar`)
            dist: a list of distance from radar to given point (1 perradar)
            beam: a list of beams (1 per radar) seeing the given point

        Example
        -------
            radars = obj.getRadarsByPosition(67., 134., 300.)

        written by Sebastien, 2012-08

        """
        from datetime import datetime as dt
        from davitpy.utils import geoPack as geo
        import numpy as np

        if not datetime:
            datetime = dt.utcnow()

        found = False
        out = {'radars': [], 'dist': [], 'beam': []}

        for irad in xrange(self.nradar):
            site = self.radars[irad].getSiteByDate(datetime)
            # Skip if radar inactive at date
            if (not site) and (self.radars[irad].status != 1):
                continue
            if not (self.radars[irad].stTime <= datetime <=
                    self.radars[irad].edTime):
                continue
            # Skip if radar in other hemisphere
            if site.geolat * lat < 0.:
                continue
            dist_pnt = geo.calcDistPnt(site.geolat,
                                       site.geolon,
                                       site.alt,
                                       distLat=lat,
                                       distLon=lon,
                                       distAlt=300.)
            # Skip if radar too far
            if dist_pnt['dist'] > distMax:
                continue
            # minAz = (site.boresite % 360.)-abs(site.bmsep)*site.maxbeam/2
            # maxAz = (site.boresite % 360.)+abs(site.bmsep)*site.maxbeam/2
            ext_fov = abs(site.bmsep) * site.maxbeam / 2
            pt_bo = [
                np.cos(np.radians(site.boresite)),
                np.sin(np.radians(site.boresite))
            ]
            pt_az = [
                np.cos(np.radians(dist_pnt['az'])),
                np.sin(np.radians(dist_pnt['az']))
            ]
            delt_az = np.degrees(np.arccos(np.dot(pt_bo, pt_az)))
            # Skip if out of azimuth range
            if not abs(delt_az) <= ext_fov:
                continue
            if np.sign(np.cross(pt_bo, pt_az)) >= 0:
                beam = int(site.maxbeam / 2 + round(delt_az / site.bmsep) - 1)
            else:
                beam = int(site.maxbeam / 2 - round(delt_az / site.bmsep))
            # Update output
            found = True
            out['radars'].append(self.radars[irad])
            out['dist'].append(dist_pnt['dist'])
            out['beam'].append(beam)

        if found:
            return out
        else:
            return found
示例#2
0
def calcFieldPnt(tr_glat, tr_glon, tr_alt, boresight, beam_off, slant_range,
                 adjusted_sr=True, elevation=None, altitude=None, hop=None,
                 model=None, coords='geo', gs_loc="G", max_vh=400.0,
                 fov_dir='front', eval_loc=False):
    """Calculate coordinates of field point given the radar coordinates and
    boresight, the pointing direction deviation from boresight and elevation
    angle, and the field point slant range and altitude. Either the elevation
    or the altitude must be provided. If none is provided, the altitude is set
    to 300 km and the elevation evaluated to accomodate altitude and range.

    Parameters
    ----------
    tr_glat
        transmitter latitude [degree, N]
    tr_glon
        transmitter longitude [degree, E]
    tr_alt
        transmitter altitude [km]
    boresight
        boresight azimuth [degree, E]
    beam_off
        beam azimuthal offset from boresight [degree]
    slant_range
        slant range [km]
    adjusted_sr : Optional(bool)
        Denotes whether or not the slant range is the total measured slant
        range (False) or if it has been adjusted to be the slant distance to
        last ionospheric reflection point (True).  (default=True)
    elevation : Optional[float]
        elevation angle [degree] (estimated if None)
    altitude : Optional[float]
        altitude [km] (default 300 km)
    hop : Optional[float]
        backscatter hop (ie 0.5, 1.5 for ionospheric; 1.0, 2.0 for ground)
    model : Optional[str]
        IS : for standard ionopsheric scatter projection model (ignores hop)
        GS : for standard ground scatter projection model (ignores hop)
        S : for standard projection model (uses hop)
        E1 : for Chisham E-region 1/2-hop ionospheric projection model
        F1 : for Chisham F-region 1/2-hop ionospheric projection model
        F3 : for Chisham F-region 1-1/2-hop ionospheric projection model
        C : for Chisham projection model (ionospheric only, ignores hop,
            requires total measured slant range)
        None : if you trust your elevation or altitude values. more to come
    coords
        'geo' (more to come)
    gs_loc : (str)
        Provide last ground scatter location 'G' or ionospheric refraction
        location 'I' for groundscatter (default='G')
    max_vh : (float)
        Maximum height for longer slant ranges in Standard model (default=400)
    fov_dir : (str)
        'front' (default) or 'back'.  Specifies fov direction
    eval_loc : (bool)
        Evaluate the calcualted location based on reasonable tolerances (True)
        or accept the first calculation (False).  Using True gives better
        locations, but restricts data at the furthest range gates.
        (default=False)

    Returns
    ---------
    geo_dict['geoLat'] : (float or np.ndarray)
        Field point latitude(s) in degrees or np.nan if error
    geo_dict['geoLon'] : (float or np.ndarray)
        Field point longitude(s) in degrees or np.nan if error
    """
    from davitpy.utils import Re, geoPack
    import davitpy.utils.model_vheight as vhm

    # Only geo is implemented.
    if coords != "geo":
        logging.error("Only geographic (geo) is implemented in calcFieldPnt.")
        return np.nan, np.nan

    # Use model to get altitude if desired
    xalt = np.nan
    calt = None
    if model is not None:
        if model in ['IS', 'GS', 'S']:
            # The standard model can be used with or without an input altitude
            # or elevation.  Returns an altitude that has been adjusted to
            # comply with common scatter distributions
            if hop is None:
                if model == "S":
                    # Default to ionospheric backscatter if hop not specified
                    hop = 0.5
                else:
                    hop = 0.5 if model == "IS" else 1.0

            xalt = vhm.standard_vhm(slant_range, adjusted_sr=adjusted_sr,
                                    max_vh=max_vh, hop=hop, alt=altitude,
                                    elv=elevation)
        else:
            # The Chisham model uses only the total slant range to determine
            # altitude based on years of backscatter data at SAS.  Performs
            # significantly better than the standard model for ionospheric
            # backscatter, not tested on groundscatter
            if adjusted_sr:
                logging.error("Chisham model needs total slant range")
                return np.nan, np.nan

            # Use Chisham model to calculate virtual height
            cmodel = None if model == "C" else model
            xalt, shop = vhm.chisham_vhm(slant_range, cmodel, hop_output=True)

            # If hop is not known, set using model divisions
            if hop is None:
                hop = shop

            # If hop is greater than 1/2, the elevation angle needs to be
            # calculated from the ground range rather than the virtual height
            if hop > 0.5:
                calt = float(xalt)

    elif elevation is None or np.isnan(elevation):
        if hop is None or adjusted_sr:
            logging.error("Total slant range and hop needed with measurements")
            return np.nan, np.nan
        if altitude is None or np.isnan(altitude):
            logging.error("No observations supplied")
            return np.nan, np.nan

        # Adjust slant range if there is groundscatter and the location
        # desired is the ionospheric reflection point
        asr = slant_range
        if hop == np.floor(hop) and gs_loc == "I":
            asr *= 1.0 - 1.0 / (2.0 * hop)

        # Adjust altitude if it's unrealistic
        if asr < altitude:
            altitude = asr - 10

        xalt = altitude

    # Use model altitude to determine elevation angle and then the location,
    # or if elevation angle was supplied, find the location
    if not np.isnan(xalt):
        # Since we have a modeled or measured altitude, start by setting the
        # Earth radius below field point to Earth radius at radar
        (lat, lon, tr_rad) = geoPack.geodToGeoc(tr_glat, tr_glon)
        rad_pos = tr_rad

        # Iterate until the altitude corresponding to the calculated elevation
        # matches the desired altitude.  Assumes straight-line path to last
        # ionospheric scattering point, so adjust slant range if necessary
        # for groundscatter
        asr = slant_range
        shop = hop
        if not adjusted_sr and hop == np.floor(hop) and gs_loc == "I":
            asr *= 1.0 - 1.0 / (2.0 * hop)
            shop = hop - 0.5

        # Set safty counter and iteratively determine location
        maxn = 30
        hdel = 100.0
        htol = 0.5
        if (slant_range >= 800.0 and model != 'GS') or shop > 1.0:
            htol = 5.0
        n = 0
        while n < maxn:
            tr_dist = tr_rad + tr_alt
            if calt is not None:
                # Adjust elevation angle for any hop > 1 (Chisham et al. 2008)
                pos_dist = rad_pos + calt
                phi = np.arccos((tr_dist**2 + pos_dist**2 - asr**2) /
                                (2.0 * tr_dist * pos_dist))
                beta = np.arcsin((tr_dist * np.sin(phi / (shop * 2.0))) /
                                 (asr / (shop * 2.0)))
                tel = np.pi / 2.0 - beta - phi / (shop * 2.0)

                if xalt == calt:
                    xalt = np.sqrt(tr_rad**2 + asr**2 + 2.0 * asr * tr_rad *
                                   np.sin(tel)) - tr_rad
                tel = np.degrees(tel)
            else:
                # pointing elevation (spherical Earth value) [degree]
                tel = np.arcsin(((rad_pos + xalt)**2 - tr_dist**2 - asr**2) /
                                (2.0 * tr_dist * asr))
                tel = np.degrees(tel)

            # estimate off-array-normal azimuth (because it varies slightly
            # with elevation) [degree]
            boff = calcAzOffBore(tel, beam_off, fov_dir=fov_dir)
            # pointing azimuth
            taz = boresight + boff
            # calculate position of field point
            geo_dict = geoPack.calcDistPnt(tr_glat, tr_glon, tr_alt,
                                           dist=asr, el=tel, az=taz)

            # Update Earth radius
            rad_pos = geo_dict['distRe']

            # stop if the altitude is what we want it to be (or close enough)
            new_hdel = abs(xalt - geo_dict['distAlt'])
            if new_hdel <= htol or not eval_loc:
                break

            # stop unsuccessfully if the altitude difference hasn't improved
            if abs(new_hdel - hdel) < 1.0e-3:
                n = maxn

            # Prepare the next iteration
            hdel = new_hdel
            n += 1

        if n >= maxn:
            estr = 'Accuracy on height calculation ({}) not '.format(htol)
            estr = '{:s}reached quick enough. Returning nan, nan.'.format(estr)
            logging.warning(estr)
            return np.nan, np.nan
        else:
            return geo_dict['distLat'], geo_dict['distLon']
    elif elevation is not None:
        # No projection model (i.e., the elevation or altitude is so good that
        # it gives you the proper projection by simple geometric
        # considerations). Using no models simply means tracing based on
        # trustworthy elevation or altitude
        if hop is None or adjusted_sr:
            logging.error("Hop and total slant range needed with measurements")
            return np.nan, np.nan

        if np.isnan(elevation):
            logging.error("No observations provided")
            return np.nan, np.nan

        shop = hop - 0.5 if hop == np.floor(hop) and gs_loc == "I" else hop
        asr = slant_range
        if hop > 0.5 and hop != shop:
            asr *= 1.0 - 1.0 / (2.0 * hop)

        # The tracing is done by calcDistPnt
        boff = calcAzOffBore(elevation, beam_off, fov_dir=fov_dir)
        geo_dict = geoPack.calcDistPnt(tr_glat, tr_glon, tr_alt, dist=asr,
                                       el=elevation, az=boresight + boff)

        return geo_dict['distLat'], geo_dict['distLon']
示例#3
0
def calcFieldPnt(tr_glat,
                 tr_glon,
                 tr_alt,
                 boresight,
                 beam_off,
                 slant_range,
                 adjusted_sr=True,
                 elevation=None,
                 altitude=None,
                 hop=None,
                 model=None,
                 coords='geo',
                 gs_loc="G",
                 max_vh=400.0,
                 fov_dir='front'):
    """Calculate coordinates of field point given the radar coordinates and
    boresight, the pointing direction deviation from boresight and elevation
    angle, and the field point slant range and altitude. Either the elevation
    or the altitude must be provided. If none is provided, the altitude is set
    to 300 km and the elevation evaluated to accomodate altitude and range.

    Parameters
    ----------
    tr_glat
        transmitter latitude [degree, N]
    tr_glon
        transmitter longitude [degree, E]
    tr_alt
        transmitter altitude [km]
    boresight
        boresight azimuth [degree, E]
    beam_off
        beam azimuthal offset from boresight [degree]
    slant_range
        slant range [km]
    adjusted_sr : Optional(bool)
        Denotes whether or not the slant range is the total measured slant
        range (False) or if it has been adjusted to be the slant distance to
        last ionospheric reflection point (True).  (default=True)
    elevation : Optional[float]
        elevation angle [degree] (estimated if None)
    altitude : Optional[float]
        altitude [km] (default 300 km)
    hop : Optional[float]
        backscatter hop (ie 0.5, 1.5 for ionospheric; 1.0, 2.0 for ground)
    model : Optional[str]
        IS : for standard ionopsheric scatter projection model (ignores hop)
        GS : for standard ground scatter projection model (ignores hop)
        S : for standard projection model (uses hop)
        E1 : for Chisham E-region 1/2-hop ionospheric projection model
        F1 : for Chisham F-region 1/2-hop ionospheric projection model
        F3 : for Chisham F-region 1-1/2-hop ionospheric projection model
        C : for Chisham projection model (ionospheric only, ignores hop,
            requires total measured slant range)
        None : if you trust your elevation or altitude values. more to come
    coords
        'geo' (more to come)
    gs_loc : (str)
        Provide last ground scatter location 'G' or ionospheric refraction
        location 'I' for groundscatter (default='G')
    max_vh : (float)
        Maximum height for longer slant ranges in Standard model (default=400)
    fov_dir
        'front' (default) or 'back'.  Specifies fov direction

    Returns
    ---------
    geo_dict['geoLat'] : (float or np.ndarray)
        Field point latitude(s) in degrees or np.nan if error
    geo_dict['geoLon'] : (float or np.ndarray)
        Field point longitude(s) in degrees or np.nan if error
    """
    from davitpy.utils import Re, geoPack
    import davitpy.utils.model_vheight as vhm

    # Only geo is implemented.
    if coords != "geo":
        logging.error("Only geographic (geo) is implemented in calcFieldPnt.")
        return np.nan, np.nan

    # Use model to get altitude if desired
    xalt = np.nan
    calt = None
    if model is not None:
        if model in ['IS', 'GS', 'S']:
            # The standard model can be used with or without an input altitude
            # or elevation.  Returns an altitude that has been adjusted to
            # comply with common scatter distributions
            if hop is None:
                if model == "S":
                    # Default to ionospheric backscatter if hop not specified
                    hop = 0.5
                else:
                    hop = 0.5 if model == "IS" else 1.0

            xalt = vhm.standard_vhm(slant_range,
                                    adjusted_sr=adjusted_sr,
                                    max_vh=max_vh,
                                    hop=hop,
                                    alt=altitude,
                                    elv=elevation)
        else:
            # The Chisham model uses only the total slant range to determine
            # altitude based on years of backscatter data at SAS.  Performs
            # significantly better than the standard model for ionospheric
            # backscatter, not tested on groundscatter
            if adjusted_sr:
                logging.error("Chisham model needs total slant range")
                return np.nan, np.nan

            # Use Chisham model to calculate virtual height
            cmodel = None if model == "C" else model
            xalt, shop = vhm.chisham_vhm(slant_range, cmodel, hop_output=True)

            # If hop is not known, set using model divisions
            if hop is None:
                hop = shop

            # If hop is greater than 1/2, the elevation angle needs to be
            # calculated from the ground range rather than the virtual height
            if hop > 0.5:
                calt = float(xalt)

    elif elevation is None or np.isnan(elevation):
        if hop is None or adjusted_sr:
            logging.error("Total slant range and hop needed with measurements")
            return np.nan, np.nan
        if altitude is None or np.isnan(altitude):
            logging.error("No observations supplied")
            return np.nan, np.nan

        # Adjust slant range if there is groundscatter and the location
        # desired is the ionospheric reflection point
        asr = slant_range
        if hop == np.floor(hop) and gs_loc == "I":
            asr *= 1.0 - 1.0 / (2.0 * hop)

        # Adjust altitude if it's unrealistic
        if asr < altitude:
            altitude = asr - 10

        xalt = altitude

    # Use model altitude to determine elevation angle and then the location,
    # or if elevation angle was supplied, find the location
    if not np.isnan(xalt):
        # Since we have a modeled or measured altitude, start by setting the
        # Earth radius below field point to Earth radius at radar
        (lat, lon, tr_rad) = geoPack.geodToGeoc(tr_glat, tr_glon)
        rad_pos = tr_rad

        # Iterate until the altitude corresponding to the calculated elevation
        # matches the desired altitude.  Assumes straight-line path to last
        # ionospheric scattering point, so adjust slant range if necessary
        # for groundscatter
        asr = slant_range
        shop = hop
        if not adjusted_sr and hop == np.floor(hop) and gs_loc == "I":
            asr *= 1.0 - 1.0 / (2.0 * hop)
            shop = hop - 0.5

        # Set safty counter and iteratively determine location
        maxn = 30
        hdel = 100.0
        htol = 0.5
        if (slant_range >= 800.0 and model != 'GS') or shop > 1.0:
            htol = 5.0
        n = 0
        while n < maxn:
            tr_dist = tr_rad + tr_alt
            if calt is not None:
                # Adjust elevation angle for any hop > 1 (Chisham et al. 2008)
                pos_dist = rad_pos + calt
                phi = np.arccos((tr_dist**2 + pos_dist**2 - asr**2) /
                                (2.0 * tr_dist * pos_dist))
                beta = np.arcsin((tr_dist * np.sin(phi / (shop * 2.0))) /
                                 (asr / (shop * 2.0)))
                tel = np.pi / 2.0 - beta - phi / (shop * 2.0)

                if xalt == calt:
                    xalt = np.sqrt(tr_rad**2 + asr**2 +
                                   2.0 * asr * tr_rad * np.sin(tel)) - tr_rad
                tel = np.degrees(tel)
            else:
                # pointing elevation (spherical Earth value) [degree]
                tel = np.arcsin(((rad_pos + xalt)**2 - tr_dist**2 - asr**2) /
                                (2.0 * tr_dist * asr))
                tel = np.degrees(tel)

            # estimate off-array-normal azimuth (because it varies slightly
            # with elevation) [degree]
            boff = calcAzOffBore(tel, beam_off, fov_dir=fov_dir)
            # pointing azimuth
            taz = boresight + boff
            # calculate position of field point
            geo_dict = geoPack.calcDistPnt(tr_glat,
                                           tr_glon,
                                           tr_alt,
                                           dist=asr,
                                           el=tel,
                                           az=taz)

            # Update Earth radius
            rad_pos = geo_dict['distRe']

            # stop if the altitude is what we want it to be (or close enough)
            new_hdel = abs(xalt - geo_dict['distAlt'])
            if new_hdel <= htol:
                break

            # stop unsuccessfully if the altitude difference hasn't improved
            if abs(new_hdel - hdel) < 1.0e-3:
                n = maxn

            # Prepare the next iteration
            hdel = new_hdel
            n += 1

        if n >= maxn:
            estr = 'Accuracy on height calculation ({}) not '.format(htol)
            estr = '{:s}reached quick enough. Returning nan, nan.'.format(estr)
            logging.warning(estr)
            return np.nan, np.nan
        else:
            return geo_dict['distLat'], geo_dict['distLon']
    elif elevation is not None:
        # No projection model (i.e., the elevation or altitude is so good that
        # it gives you the proper projection by simple geometric
        # considerations). Using no models simply means tracing based on
        # trustworthy elevation or altitude
        if hop is None or adjusted_sr:
            logging.error("Hop and total slant range needed with measurements")
            return np.nan, np.nan

        if np.isnan(elevation):
            logging.error("No observations provided")
            return np.nan, np.nan

        shop = hop - 0.5 if hop == np.floor(hop) and gs_loc == "I" else hop
        asr = slant_range
        if hop > 0.5 and hop != shop:
            asr *= 1.0 - 1.0 / (2.0 * hop)

        # The tracing is done by calcDistPnt
        boff = calcAzOffBore(elevation, beam_off, fov_dir=fov_dir)
        geo_dict = geoPack.calcDistPnt(tr_glat,
                                       tr_glon,
                                       tr_alt,
                                       dist=asr,
                                       el=elevation,
                                       az=boresight + boff)

        return geo_dict['distLat'], geo_dict['distLon']
示例#4
0
文件: radFov.py 项目: doby123/davitpy
def calcFieldPnt(tGeoLat, tGeoLon, tAlt, boreSight, boreOffset, slantRange,
                 elevation=None, altitude=None, model=None, coords='geo',
                 fov_dir='front'):
    """
    Calculate coordinates of field point given the radar coordinates and
    boresight, the pointing direction deviation from boresight and elevation
    angle, and the field point slant range and altitude. Either the elevation
    or the altitude must be provided. If none is provided, the altitude is set
    to 300 km and the elevation evaluated to accomodate altitude and range.

    **INPUTS**:
        * **tGeoLat**: transmitter latitude [degree, N]
        * **tGeoLon**: transmitter longitude [degree, E]
        * **tAlt**: transmitter altitude [km]
        * **boreSight**: boresight azimuth [degree, E]
        * **boreOffset**: offset from boresight [degree]
        * **slantRange**: slant range [km]
        * **elevation**: elevation angle [degree] (estimated if None)
        * **altitude**: altitude [km] (default 300 km)
        * **model**: 
            * **'IS'**: for ionopsheric scatter projection model
            * **'GS'**: for ground scatter projection model
            * **None**: if you trust your elevation or altitude data
            * ... more to come
        * **coords**: 'geo' (more to come)
        * **fov_dir**: 'front' (default) or 'back'.  Specifies fov direction
    """
    from math import asin
    from davitpy.utils import Re, geoPack
    
    # Make sure we have enough input stuff
    # if (not model) and (not elevation or not altitude): model = 'IS'

    # Only geo is implemented.
    assert(coords == "geo"), \
        "Only geographic (geo) is implemented in calcFieldPnt."

    # Now let's get to work
    # Classic Ionospheric/Ground scatter projection model
    if model in ['IS','GS']:
        # Make sure you have altitude (even if it isn't used), because these
        # 2 projection models rely on it
        if not elevation and not altitude:
            # Set default altitude to 300 km
            altitude = 300.0
        elif elevation and not altitude:
            # If you have elevation but not altitude, then you calculate
            # altitude, and elevation will be adjusted anyway
            altitude = np.sqrt(Re**2 + slantRange**2 + 2. * slantRange * Re *
                               np.sin(np.radians(elevation))) - Re

        # Now you should have altitude (and maybe elevation too, but it won't
        # be used in the rest of the algorithm).  Adjust altitude so that it
        # makes sense with common scatter distribution
        xAlt = altitude
        if model == 'IS':
            if altitude > 150. and slantRange <= 600.: 
                xAlt = 115.
            elif altitude > 150. and slantRange > 600. and  slantRange <= 800.:
                xAlt = 115. + (slantRange - 600.) / 200. * (altitude - 115.)
        elif model == 'GS':
            if altitude > 150. and slantRange <= 300: 
                xAlt = 115.
            elif altitude > 150. and slantRange > 300. and  slantRange <= 500.:
                xAlt = 115. + (slantRange - 300.) / 200. * (altitude - 115.)
        if slantRange < 150.: xAlt = slantRange / 150. * 115.
        
        # To start, set Earth radius below field point to Earth radius at radar
        (lat,lon,tRe) = geoPack.geodToGeoc(tGeoLat, tGeoLon)
        RePos = tRe

        # Iterate until the altitude corresponding to the calculated elevation
        # matches the desired altitude
        n = 0 # safety counter
        while True:
            # pointing elevation (spherical Earth value) [degree]
            tel = np.degrees(asin(((RePos+xAlt)**2 - (tRe+tAlt)**2 -
                                slantRange**2)/(2. * (tRe+tAlt) * slantRange)))

            # estimate off-array-normal azimuth (because it varies slightly
            # with elevation) [degree]
            boff = calcAzOffBore(tel, boreOffset, fov_dir=fov_dir)

            # pointing azimuth
            taz = boreSight + boff

            # calculate position of field point
            dictOut = geoPack.calcDistPnt(tGeoLat, tGeoLon, tAlt,
                                          dist=slantRange, el=tel, az=taz)

            # Update Earth radius 
            RePos = dictOut['distRe']

            # stop if the altitude is what we want it to be (or close enough)
            n += 1
            if abs(xAlt - dictOut['distAlt']) <= 0.5 or n > 2:
                return dictOut['distLat'], dictOut['distLon']
                break

    # No projection model (i.e., the elevation or altitude is so good that it
    # gives you the proper projection by simple geometric considerations)
    elif not model:
        # Using no models simply means tracing based on trustworthy elevation
        # or altitude
        if not altitude:
            altitude = np.sqrt(Re**2 + slantRange**2 + 2. * slantRange * Re *
                            np.sin(np.radians(elevation))) - Re
        if not elevation:
            if(slantRange < altitude): altitude = slantRange - 10
            elevation = np.degrees(asin(((Re+altitude)**2 - (Re+tAlt)**2 -
                                      slantRange**2) /
                                     (2. * (Re + tAlt) * slantRange)))
        # The tracing is done by calcDistPnt
        dict = geoPack.calcDistPnt(tGeoLat, tGeoLon, tAlt, dist=slantRange,
                                   el=elevation, az=boreSight+boreOffset)
        return dict['distLat'], dict['distLon']
示例#5
0
    def getRadarsByPosition(self, lat, lon, alt, distMax=4000., datetime=None):
        """Get a list of radars able to see a given point on Earth

        Belongs to
        ----------
        class : network

        Parameters
        ----------
        lat
            latitude of given point in geographic coordinates
        lon
            longitude of given point in geographic coordinates
        alt
            altitude of point above the Earth's surface in km
        distMax
            maximum distance of given point from radar
        datetime : Optional[datetime.datetime object]
            python datetime object (defaults to today)

        Returns
        -------
        dict
            A dictionnary with keys:
            radars : a list of radar objects (:class:`radar`)
            dist: a list of distance from radar to given point (1 perradar)
            beam: a list of beams (1 per radar) seeing the given point

        Example
        -------
            radars = obj.getRadarsByPosition(67., 134., 300.)

        written by Sebastien, 2012-08

        """
        from datetime import datetime as dt
        from davitpy.utils import geoPack as geo
        import numpy as np

        if not datetime:
            datetime = dt.utcnow()

        found = False
        out = {'radars': [], 'dist': [], 'beam': []}

        for irad in xrange(self.nradar):
            site = self.radars[irad].getSiteByDate(datetime)
            # Skip if radar inactive at date
            if (not site) and (self.radars[irad].status != 1):
                continue
            if not (self.radars[irad].stTime <= datetime <=
                    self.radars[irad].edTime):
                        continue
            # Skip if radar in other hemisphere
            if site.geolat * lat < 0.:
                continue
            dist_pnt = geo.calcDistPnt(site.geolat, site.geolon, site.alt,
                                       distLat=lat, distLon=lon, distAlt=300.)
            # Skip if radar too far
            if dist_pnt['dist'] > distMax:
                continue
            # minAz = (site.boresite % 360.)-abs(site.bmsep)*site.maxbeam/2
            # maxAz = (site.boresite % 360.)+abs(site.bmsep)*site.maxbeam/2
            ext_fov = abs(site.bmsep) * site.maxbeam / 2
            pt_bo = [np.cos(np.radians(site.boresite)),
                     np.sin(np.radians(site.boresite))]
            pt_az = [np.cos(np.radians(dist_pnt['az'])),
                     np.sin(np.radians(dist_pnt['az']))]
            delt_az = np.degrees(np.arccos(np.dot(pt_bo, pt_az)))
            # Skip if out of azimuth range
            if not abs(delt_az) <= ext_fov:
                continue
            if np.sign(np.cross(pt_bo, pt_az)) >= 0:
                beam = int(site.maxbeam / 2 + round(delt_az / site.bmsep) - 1)
            else:
                beam = int(site.maxbeam / 2 - round(delt_az / site.bmsep))
            # Update output
            found = True
            out['radars'].append(self.radars[irad])
            out['dist'].append(dist_pnt['dist'])
            out['beam'].append(beam)

        if found:
            return out
        else:
            return found
示例#6
0
def calcFieldPnt(tGeoLat,
                 tGeoLon,
                 tAlt,
                 boreSight,
                 boreOffset,
                 slantRange,
                 elevation=None,
                 altitude=None,
                 model=None,
                 coords='geo',
                 fov_dir='front'):
    """Calculate coordinates of field point given the radar coordinates and
    boresight, the pointing direction deviation from boresight and elevation
    angle, and the field point slant range and altitude. Either the elevation
    or the altitude must be provided. If none is provided, the altitude is set
    to 300 km and the elevation evaluated to accomodate altitude and range.

    Parameters
    ----------
    tGeoLat
        transmitter latitude [degree, N]
    tGeoLon
        transmitter longitude [degree, E]
    tAlt
        transmitter altitude [km]
    boreSight
        boresight azimuth [degree, E]
    boreOffset
        offset from boresight [degree]
    slantRange
        slant range [km]
    elevation : Optional[float]
        elevation angle [degree] (estimated if None)
    altitude : Optional[float]
        altitude [km] (default 300 km)
    model : Optional[str]
        IS : for ionopsheric scatter projection model
        GS : for ground scatter projection model
        None : if you trust your elevation or altitude values. more to come
    coords
        'geo' (more to come)
    fov_dir
        'front' (default) or 'back'.  Specifies fov direction

    """
    from math import asin
    from davitpy.utils import Re, geoPack

    # Make sure we have enough input stuff
    # if (not model) and (not elevation or not altitude): model = 'IS'

    # Only geo is implemented.
    assert(coords == "geo"), \
        "Only geographic (geo) is implemented in calcFieldPnt."

    # Now let's get to work
    # Classic Ionospheric/Ground scatter projection model
    if model in ['IS', 'GS']:
        # Make sure you have altitude (even if it isn't used), because these
        # 2 projection models rely on it
        if not elevation and not altitude:
            # Set default altitude to 300 km
            altitude = 300.0
        elif elevation and not altitude:
            # If you have elevation but not altitude, then you calculate
            # altitude, and elevation will be adjusted anyway
            altitude = np.sqrt(Re**2 + slantRange**2 + 2. * slantRange * Re *
                               np.sin(np.radians(elevation))) - Re

        # Now you should have altitude (and maybe elevation too, but it won't
        # be used in the rest of the algorithm).  Adjust altitude so that it
        # makes sense with common scatter distribution
        xAlt = altitude
        if model == 'IS':
            if altitude > 150. and slantRange <= 600.:
                xAlt = 115.
            elif altitude > 150. and slantRange > 600. and slantRange <= 800.:
                xAlt = 115. + (slantRange - 600.) / 200. * (altitude - 115.)
        elif model == 'GS':
            if altitude > 150. and slantRange <= 300:
                xAlt = 115.
            elif altitude > 150. and slantRange > 300. and slantRange <= 500.:
                xAlt = 115. + (slantRange - 300.) / 200. * (altitude - 115.)
        if slantRange < 150.:
            xAlt = slantRange / 150. * 115.

        # To start, set Earth radius below field point to Earth radius at radar
        (lat, lon, tRe) = geoPack.geodToGeoc(tGeoLat, tGeoLon)
        RePos = tRe

        # Iterate until the altitude corresponding to the calculated elevation
        # matches the desired altitude
        n = 0  # safety counter
        while True:
            # pointing elevation (spherical Earth value) [degree]
            tel = np.degrees(
                asin(((RePos + xAlt)**2 - (tRe + tAlt)**2 - slantRange**2) /
                     (2. * (tRe + tAlt) * slantRange)))

            # estimate off-array-normal azimuth (because it varies slightly
            # with elevation) [degree]
            boff = calcAzOffBore(tel, boreOffset, fov_dir=fov_dir)

            # pointing azimuth
            taz = boreSight + boff

            # calculate position of field point
            dictOut = geoPack.calcDistPnt(tGeoLat,
                                          tGeoLon,
                                          tAlt,
                                          dist=slantRange,
                                          el=tel,
                                          az=taz)

            # Update Earth radius
            RePos = dictOut['distRe']

            # stop if the altitude is what we want it to be (or close enough)
            n += 1
            if abs(xAlt - dictOut['distAlt']) <= 0.5 or n > 2:
                return dictOut['distLat'], dictOut['distLon']
                break

    # No projection model (i.e., the elevation or altitude is so good that it
    # gives you the proper projection by simple geometric considerations)
    elif not model:
        # Using no models simply means tracing based on trustworthy elevation
        # or altitude
        if not altitude:
            altitude = np.sqrt(Re**2 + slantRange**2 + 2. * slantRange * Re *
                               np.sin(np.radians(elevation))) - Re
        if not elevation:
            if (slantRange < altitude):
                altitude = slantRange - 10
            elevation = np.degrees(
                asin(((Re + altitude)**2 - (Re + tAlt)**2 - slantRange**2) /
                     (2. * (Re + tAlt) * slantRange)))
        # The tracing is done by calcDistPnt
        dict = geoPack.calcDistPnt(tGeoLat,
                                   tGeoLon,
                                   tAlt,
                                   dist=slantRange,
                                   el=elevation,
                                   az=boreSight + boreOffset)
        return dict['distLat'], dict['distLon']
示例#7
0
def lat_distribution(tdiff, ref_lat, hard, phi0, phi0e, fovflg, bm_az, tfreq,
                     dist):
    '''Returns the squared sum of the difference between the mean and the
    specified latitude and the standard deviation of the backscatter latitudes

    Parameters
    -----------
    tdiff : (float)
        tdiff in microseconds
    ref_lat : (float)
        reference latitude in degrees
    hard : (class davitpy.pydarn.radar.radStruct.site)
        radar hardware data
    phi0 : (list)
        phase lags in radians
    phi0e : (list)
        phase lag errors in radians
    fovflg : (list)
        field-of-view flags
    bm_az : (list)
        Azimuthal angle between the beam and the radar boresite at zero
        elevation (radians)
    tfreq : (list)
        transmission frequencies in kHz
    dist : (list)
        slant distance from radar to ionospheric reflection point in km

    Returns
    ---------
    ff : (float)
        standard deviation of the latitude distribution

    Notes
    -------
    Calculates equation 3 in Burrell et al. (2016)
    '''
    import davitpy.utils.geoPack as geo
    import davitpy.pydarn.proc.fov.calc_elevation as celv
    import davitpy.pydarn.radar.radFov as rfov
    # Ensure that TDIFF is a single number and not a list or array element
    try:
        len(tdiff)
        tdiff = tdiff[0]
    except:
        pass

    if not isinstance(tdiff, float):
        tdiff = float(tdiff)

    # Calculate the elevation and latitude
    try:
        # elevation is in radians
        elv = np.array(
            celv.calc_elv_list(phi0, phi0e, fovflg, bm_az, tfreq,
                               hard.interfer, tdiff))
        elv = np.degrees(elv)
        # Correction to boresight azimuth due to elevation angle
        fov_dir = {1: "front", -1: "back"}
        az = np.array([
            rfov.calcAzOffBore(e, np.degrees(bm_az[i]), fov_dir[fovflg[i]]) +
            hard.boresite for i, e in enumerate(elv)
        ])

        # Calculate location
        loc = geo.calcDistPnt(hard.geolat,
                              hard.geolon,
                              hard.alt,
                              az=az,
                              el=elv,
                              dist=np.array(dist))
        lat = loc['distLat'][~np.isnan(loc['distLat'])]  # Remove nan

        #--------------------------------------------------------------------
        # When the distribution is approximately gaussian, the error depends
        # on the location of the peak and the amount of spread.  Testing the
        # standard deviation prevents the formation of a bimodal distribution
        # who each flank the desired location
        #
        # Sum the errors
        ff = np.nan if len(lat) == 0 else np.sqrt((lat.mean() - ref_lat)**2 +
                                                  lat.std()**2)
    except:
        ff = np.nan

    # Return the square root of the summed squared errors
    return ff
def lat_distribution(tdiff, ref_lat, hard, phi0, phi0e, fovflg, bm_az, tfreq,
                    dist):
    '''Returns the squared sum of the difference between the mean and the
    specified latitude and the standard deviation of the backscatter latitudes

    Parameters
    -----------
    tdiff : (float)
        tdiff in microseconds
    ref_lat : (float)
        reference latitude in degrees
    hard : (class davitpy.pydarn.radar.radStruct.site)
        radar hardware data
    phi0 : (list)
        phase lags in radians
    phi0e : (list)
        phase lag errors in radians
    fovflg : (list)
        field-of-view flags
    bm_az : (list)
        Azimuthal angle between the beam and the radar boresite at zero
        elevation (radians)
    tfreq : (list)
        transmission frequencies in kHz
    dist : (list)
        slant distance from radar to ionospheric reflection point in km

    Returns
    ---------
    ff : (float)
        standard deviation of the latitude distribution

    Notes
    -------
    Calculates equation 3 in Burrell et al. (2016)
    '''
    import davitpy.utils.geoPack as geo
    import davitpy.pydarn.proc.fov.calc_elevation as celv
    import davitpy.pydarn.radar.radFov as rfov
    # Ensure that TDIFF is a single number and not a list or array element
    try:
        len(tdiff)
        tdiff = tdiff[0]
    except:
        pass

    if not isinstance(tdiff, float):
        tdiff = float(tdiff)

    # Calculate the elevation and latitude
    try:
        # elevation is in radians
        elv = np.array(celv.calc_elv_list(phi0, phi0e, fovflg, bm_az, tfreq,
                                          hard.interfer, tdiff))
        elv = np.degrees(elv)
        # Correction to boresight azimuth due to elevation angle
        fov_dir = {1:"front", -1:"back"}
        az = np.array([rfov.calcAzOffBore(e, np.degrees(bm_az[i]),
                                          fov_dir[fovflg[i]]) + hard.boresite
                       for i,e in enumerate(elv)])

        # Calculate location
        loc = geo.calcDistPnt(hard.geolat, hard.geolon, hard.alt,
                              az=az, el=elv, dist=np.array(dist))
        lat = loc['distLat'][~np.isnan(loc['distLat'])] # Remove nan

        #--------------------------------------------------------------------
        # When the distribution is approximately gaussian, the error depends
        # on the location of the peak and the amount of spread.  Testing the
        # standard deviation prevents the formation of a bimodal distribution
        # who each flank the desired location
        #
        # Sum the errors
        ff = np.nan if len(lat) == 0 else np.sqrt((lat.mean() - ref_lat)**2 +
                                                  lat.std()**2)
    except:
        ff = np.nan

    # Return the square root of the summed squared errors
    return ff