예제 #1
0
def gather_waveforms(source,
                     network,
                     station,
                     location,
                     channel,
                     starttime,
                     endtime,
                     time_buffer=0,
                     merge_fill_value=0,
                     trim_fill_value=0,
                     remove_response=False,
                     return_failed_stations=False,
                     watc_url=None,
                     watc_username=None,
                     watc_password=None):
    """
    Gather seismic/infrasound waveforms from IRIS or WATC FDSN, or AVO Winston,
    and output a :class:`~obspy.core.stream.Stream` with station/element
    coordinates attached. Optionally remove the sensitivity.

    **NOTE**

    Usual RTM usage is to specify a starttime/endtime that brackets the
    estimated source origin time. Then time_buffer is used to download enough
    extra data to account for the time required for an infrasound signal to
    propagate to the farthest station.

    Args:
        source (str): Which source to gather waveforms from. Options are:

            * `'IRIS'` – IRIS FDSN
            * `'WATC'` – WATC FDSN
            * `'AVO'` – AVO Winston

        network (str): SEED network code [wildcards (``*``, ``?``) accepted]
        station (str): SEED station code [wildcards (``*``, ``?``) accepted]
        location (str): SEED location code [wildcards (``*``, ``?``) accepted]
        channel (str): SEED channel code [wildcards (``*``, ``?``) accepted]
        starttime (:class:`~obspy.core.utcdatetime.UTCDateTime`): Start time for
            data request
        endtime (:class:`~obspy.core.utcdatetime.UTCDateTime`): End time for
            data request
        time_buffer (int or float): Extra amount of data to download after
            `endtime` [s]
        merge_fill_value (bool, int, float, str, or None): Controls merging of
            :class:`~obspy.core.trace.Trace` objects with identical IDs. If
            `False`, no merging is performed. Otherwise, a merge is performed
            with the ``fill_value`` provided to this parameter. For details,
            see the docstring of :meth:`obspy.core.stream.Stream.trim`
        trim_fill_value (bool, int, float, or None): Controls trimming of the
            output :class:`~obspy.core.stream.Stream`, useful if precisely
            uniform start and end times are desired. If `False`, no trimming is
            performed. Otherwise, a trim is performed with the ``fill_value``
            provided to this parameter. For details, see the docstring of
            :meth:`obspy.core.stream.Stream.merge`
        remove_response (bool): Toggle response removal via
            :meth:`~obspy.core.trace.Trace.remove_sensitivity` or a simple
            scalar multiplication
        return_failed_stations (bool): If `True`, returns a list of station
            codes that were requested but not downloaded. This disables the
            standard failed station warning message
        watc_url (str): URL for WATC FDSN server
        watc_username (str): Username for WATC FDSN server
        watc_password (str): Password for WATC FDSN server

    Returns:
        :class:`~obspy.core.stream.Stream` containing gathered waveforms. If
        `return_failed_stations` is `True`, additionally returns a list
        containing station codes that were requested but not downloaded
    """

    # Check for issues with fill value args
    if merge_fill_value is True or trim_fill_value is True:
        raise ValueError('Cannot provide True to fill value parameters.')

    print('--------------')
    print('GATHERING DATA')
    print('--------------')

    # IRIS FDSN
    if source == 'IRIS':

        client = FDSN_Client('IRIS')
        print('Reading data from IRIS FDSN...')
        try:
            st_out = client.get_waveforms(network,
                                          station,
                                          location,
                                          channel,
                                          starttime,
                                          endtime + time_buffer,
                                          attach_response=True)
        except FDSNNoDataException:
            st_out = Stream()  # Just create an empty Stream object

    # WATC FDSN
    elif source == 'WATC':

        print('Connecting to WATC FDSN...')
        client = FDSN_Client(base_url=watc_url,
                             user=watc_username,
                             password=watc_password)
        print('Successfully connected. Reading data from WATC FDSN...')
        try:
            st_out = client.get_waveforms(network,
                                          station,
                                          location,
                                          channel,
                                          starttime,
                                          endtime + time_buffer,
                                          attach_response=True)
        except FDSNNoDataException:
            st_out = Stream()  # Just create an empty Stream object

    # AVO Winston
    elif source == 'AVO':

        client = EW_Client('pubavo1.wr.usgs.gov',
                           port=16023)  # 16023 is long-term
        print('Reading data from AVO Winston...')
        st_out = Stream()  # Make empty Stream object to populate

        # Brute-force "dynamic grid search" over network/station/channel/location codes
        for nw in _restricted_matching('network', network, client):
            for sta in _restricted_matching('station',
                                            station,
                                            client,
                                            network=nw):
                for cha in _restricted_matching('channel',
                                                channel,
                                                client,
                                                network=nw,
                                                station=sta):
                    for loc in _restricted_matching('location',
                                                    location,
                                                    client,
                                                    network=nw,
                                                    station=sta,
                                                    channel=cha):
                        try:
                            st_out += client.get_waveforms(
                                nw, sta, loc, cha, starttime,
                                endtime + time_buffer)
                        except KeyError:
                            pass

    else:
        raise ValueError('Unrecognized source. Valid options are \'IRIS\', '
                         '\'WATC\', or \'AVO\'.')

    # Merge, if specified
    if merge_fill_value is not False:
        st_out.merge(fill_value=merge_fill_value)  # Merge Traces with same ID
        warnings.warn(f'Merging with "fill_value={merge_fill_value}"',
                      CollectionWarning)

    st_out.sort()

    # Check that all requested stations are present in Stream
    requested_stations = station.split(',')
    downloaded_stations = [tr.stats.station for tr in st_out]
    failed_stations = []
    for sta in requested_stations:
        # The below check works with wildcards, but obviously cannot detect if
        # ALL stations corresponding to a given wildcard (e.g., O??K) were
        # downloaded. Thus, if careful station selection is desired, specify
        # each station explicitly and the below check will then be effective.
        if not fnmatch.filter(downloaded_stations, sta):
            if not return_failed_stations:
                # If we're not returning the failed stations, then show this
                # warning message to alert the user
                warnings.warn(
                    f'Station {sta} not downloaded from {source} '
                    'server for this time period.', CollectionWarning)
            failed_stations.append(sta)

    # If the Stream is empty, then we can stop here
    if st_out.count() == 0:
        print('No data downloaded.')
        if return_failed_stations:
            return st_out, failed_stations
        else:
            return st_out

    # Otherwise, show what the Stream contains
    print(st_out.__str__(extended=True))  # This syntax prints the WHOLE Stream

    # Trim, if specified
    if trim_fill_value is not False:
        st_out.trim(starttime,
                    endtime + time_buffer,
                    pad=True,
                    fill_value=trim_fill_value)
        warnings.warn(f'Trimming with "fill_value={trim_fill_value}"',
                      CollectionWarning)

    print('Assigning coordinates...')

    # Use IRIS inventory info for AVO data source
    if source == 'AVO':
        client = FDSN_Client('IRIS')

    try:
        inv = client.get_stations(network=network,
                                  station=station,
                                  location=location,
                                  channel=channel,
                                  starttime=starttime,
                                  endtime=endtime + time_buffer,
                                  level='channel')
    except FDSNNoDataException:
        inv = Inventory()  # Make an empty inv
        warnings.warn('Creating empty inventory.', CollectionWarning)

    for tr in st_out:
        try:
            coords = inv.get_coordinates(tr.id)
            tr.stats.longitude = coords['longitude']
            tr.stats.latitude = coords['latitude']
            tr.stats.elevation = coords['elevation']
        except Exception as e:
            if str(e) == 'No matching channel metadata found.':
                warnings.warn(f'No metadata for {tr.id} found in inventory.',
                              CollectionWarning)
            else:
                raise

    # Check if any Trace did NOT get coordinates assigned, and try to use JSON
    # coordinates if available
    for tr in st_out:
        try:
            tr.stats.longitude, tr.stats.latitude, tr.stats.elevation
        except AttributeError:
            try:
                tr.stats.latitude, tr.stats.longitude,\
                    tr.stats.elevation = AVO_COORDS[tr.id]
                warnings.warn(f'Using coordinates from JSON file for {tr.id}.',
                              CollectionWarning)
            except KeyError:
                print(f'No coordinates available for {tr.id}. Stopping.')
                raise

    # Remove sensitivity
    if remove_response:

        print('Removing sensitivity...')

        for tr in st_out:
            try:
                # Just removing sensitivity for now. remove_response() can lead
                # to errors. This should be sufficient for now. Plus some
                # IRIS-AVO responses are wonky.
                tr.remove_sensitivity()
            except ValueError:  # No response information found
                # This is only set up for infrasound calibration values
                try:
                    calib = AVO_INFRA_CALIBS[tr.id]
                    tr.data = tr.data * calib
                    warnings.warn(
                        'Using calibration value from JSON file for '
                        f'{tr.id}.', CollectionWarning)
                except KeyError:
                    print(f'No calibration value available for {tr.id}. '
                          'Stopping.')
                    raise

    print('Done')

    # Return the Stream with coordinates attached (and responses removed if
    # specified)
    if return_failed_stations:
        return st_out, failed_stations
    else:
        return st_out
예제 #2
0
        template = tribe[0]
        event = template.event
        depth = event.origins[0].depth
        if depth < 10:
            origt = event.origins[0].time
            evlat = event.origins[0].latitude
            evlon = event.origins[0].longitude

            tmin = origt - 0.5
            tmax = origt + 5.0
            name = template.name
            fname = os.path.join("templates", "waveforms", "%s.mseed" % name)
            stream = read(fname)
            for tr in stream:
                station = tr.stats.station
                coords = inv.get_coordinates(tr.id, origt)
                stalat = coords["latitude"]
                stalon = coords["longitude"]
                #             distdeg = calc_dist(evlat, evlon, stalat, stalon, 6378.1)
                #             distkm = degrees2kilometers(distdeg)
                dist = gps2dist_azimuth(evlat, evlon, stalat, stalon)[0]
                tr.stats.distance = dist

            stream.sort()
            #stream.traces.sort(key=lambda x: x.stats.distance)
            stream.trim(starttime=tmin, endtime=tmax).filter("lowpass",
                                                             freq=60)
            plot_3cols(stream, event)
            plt.savefig(os.path.join("templates", "waveforms",
                                     "%s.png" % name),
                        dpi=300)