Beispiel #1
0
def _read_station(instrumentation_register, sta_element, _ns):
    """
    Reads the station structure

    :param instrumentation_register: register of instrumentation metadata
    :param sta_element: station element to be read
    :param _ns: name space
    """

    # Read location tags
    longitude = _read_floattype(sta_element,
                                _ns("longitude"),
                                Longitude,
                                datum=True)
    latitude = _read_floattype(sta_element,
                               _ns("latitude"),
                               Latitude,
                               datum=True)
    elevation = _read_floattype(sta_element,
                                _ns("elevation"),
                                Distance,
                                unit=True)
    station = obspy.core.inventory.Station(code=sta_element.get("code"),
                                           latitude=latitude,
                                           longitude=longitude,
                                           elevation=elevation)
    station.site = _read_site(sta_element, _ns)

    # There is no relevant info in the base node
    # Read the start and end date (creation, termination) from tags
    # "Vault" and "Geology" are not defined in sc3ml ?
    station.start_date = _tag2obj(sta_element, _ns("start"), obspy.UTCDateTime)
    station.end_date = _tag2obj(sta_element, _ns("end"), obspy.UTCDateTime)
    station.creation_date = _tag2obj(sta_element, _ns("start"),
                                     obspy.UTCDateTime)
    station.termination_date = _tag2obj(sta_element, _ns("end"),
                                        obspy.UTCDateTime)

    # get the restricted_status (boolean)
    # true is evaluated to 'open'; false to 'closed'
    station.restricted_status = _get_restricted_status(sta_element, _ns)

    # Get all the channels, sc3ml keeps these in <sensorLocation> tags in the
    # station element. Individual channels are contained within <stream> tags
    channels = []
    for sen_loc_element in sta_element.findall(_ns("sensorLocation")):
        for channel in sen_loc_element.findall(_ns("stream")):
            channels.append(
                _read_channel(instrumentation_register, channel, _ns))

    station.channels = channels

    return station
Beispiel #2
0
def _read_station(inventory_root, sta_element, _ns):

    """
    Reads the station structure

    :param inventory_root: base inventory element of sc3ml
    :param sta_element: station element to be read
    :param _ns: name space
    """

    # Read location tags
    longitude = _read_floattype(sta_element, _ns("longitude"), Longitude,
                                datum=True)
    latitude = _read_floattype(sta_element, _ns("latitude"), Latitude,
                               datum=True)
    elevation = _read_floattype(sta_element, _ns("elevation"), Distance,
                                unit=True)
    station = obspy.core.inventory.Station(code=sta_element.get("code"),
                                           latitude=latitude,
                                           longitude=longitude,
                                           elevation=elevation)
    station.site = _read_site(sta_element, _ns)

    # There is no relevant info in the base node
    # Read the start and end date (creation, termination) from tags
    # "Vault" and "Geology" are not defined in sc3ml ?
    station.start_date = _tag2obj(sta_element, _ns("start"), obspy.UTCDateTime)
    station.end_date = _tag2obj(sta_element, _ns("end"), obspy.UTCDateTime)
    station.creation_date = _tag2obj(sta_element, _ns("start"),
                                     obspy.UTCDateTime)
    station.termination_date = _tag2obj(sta_element, _ns("end"),
                                        obspy.UTCDateTime)

    # get the restricted_status (boolean)
    # true is evaluated to 'open'; false to 'closed'
    station.restricted_status = _get_restricted_status(sta_element, _ns)

    # Get all the channels, sc3ml keeps these in <sensorLocation> tags in the
    # station element. Individual channels are contained within <stream> tags
    channels = []
    for sen_loc_element in sta_element.findall(_ns("sensorLocation")):
        for channel in sen_loc_element.findall(_ns("stream")):
            channels.append(_read_channel(inventory_root, channel, _ns))

    station.channels = channels

    return station
Beispiel #3
0
def _read_response_stage(stage, _ns, rate, stage_number, input_units, output_units):

    elem_type = stage.tag.split("}")[1]

    stage_sequence_number = stage_number

    # Obtain the stage gain and frequency
    # Default to a gain of 0 and frequency of 0 if missing
    stage_gain = _tag2obj(stage, _ns("gain"), float) or 0
    stage_gain_frequency = _tag2obj(stage, _ns("gainFrequency"), float) or float(0.00)

    name = stage.get("name")
    if name is not None:
        name = str(name)
    resource_id = stage.get("publicID")
    if resource_id is not None:
        resource_id = str(resource_id)

    # Determine the decimation parameters
    # This is dependent on the type of stage
    # Decimation delay/correction need to be normalized
    if elem_type == "responseFIR":
        decimation_factor = _tag2obj(stage, _ns("decimationFactor"), int)
        if rate != 0.0:
            temp = _tag2obj(stage, _ns("delay"), float) / rate
            decimation_delay = _read_float_var(temp, FloatWithUncertaintiesAndUnit, unit=True)
            temp = _tag2obj(stage, _ns("correction"), float) / rate
            decimation_corr = _read_float_var(temp, FloatWithUncertaintiesAndUnit, unit=True)
        else:
            decimation_delay = _read_float_var("inf", FloatWithUncertaintiesAndUnit, unit=True)
            decimation_corr = _read_float_var("inf", FloatWithUncertaintiesAndUnit, unit=True)
        decimation_input_sample_rate = _read_float_var(rate, Frequency)
        decimation_offset = int(0)
    elif elem_type == "datalogger":
        decimation_factor = int(1)
        decimation_delay = _read_float_var(0.00, FloatWithUncertaintiesAndUnit, unit=True)
        decimation_corr = _read_float_var(0.00, FloatWithUncertaintiesAndUnit, unit=True)
        decimation_input_sample_rate = _read_float_var(rate, Frequency)
        decimation_offset = int(0)
    elif elem_type == "responsePAZ" or elem_type == "responsePolynomial":
        decimation_factor = None
        decimation_delay = None
        decimation_corr = None
        decimation_input_sample_rate = None
        decimation_offset = None
    else:
        raise ValueError("Unknown type of response: " + str(elem_type))

    # set up list of for this stage arguments
    kwargs = {
        "stage_sequence_number": stage_sequence_number,
        "input_units": str(input_units),
        "output_units": str(output_units),
        "input_units_description": None,
        "output_units_description": None,
        "resource_id": None,
        "resource_id2": resource_id,
        "stage_gain": stage_gain,
        "stage_gain_frequency": stage_gain_frequency,
        "name": name,
        "description": None,
        "decimation_input_sample_rate": decimation_input_sample_rate,
        "decimation_factor": decimation_factor,
        "decimation_offset": decimation_offset,
        "decimation_delay": decimation_delay,
        "decimation_correction": decimation_corr,
    }

    # Different processing for different types of responses
    # currently supported:
    # PAZ
    # COEFF
    # FIR
    # Polynomial response is not supported, could not find example
    if elem_type == "responsePAZ":

        # read normalization params
        normalization_freq = _read_floattype(stage, _ns("normalizationFrequency"), Frequency)
        normalization_factor = _tag2obj(stage, _ns("normalizationFactor"), float)

        # Parse the type of the transfer function
        # A: Laplace (rad)
        # B: Laplace (Hz)
        # D: digital (z-transform)
        pz_transfer_function_type = _tag2obj(stage, _ns("type"), str)
        if pz_transfer_function_type == "A":
            pz_transfer_function_type = "LAPLACE (RADIANS/SECOND)"
        elif pz_transfer_function_type == "B":
            pz_transfer_function_type = "LAPLACE (HERTZ)"
        elif pz_transfer_function_type == "D":
            pz_transfer_function_type = "DIGITAL (Z-TRANSFORM)"
        else:
            msg = ("Unknown transfer function code %s. Defaulting to Laplace" "(rad)") % pz_transfer_function_type
            warnings.warn(msg)
            pz_transfer_function_type = "LAPLACE (RADIANS/SECOND)"

        # Parse string of poles and zeros
        # paz are stored as a string in sc3ml
        # e.g. (-0.01234,0.01234) (-0.01234,-0.01234)
        zeros_array = stage.find(_ns("zeros")).text
        poles_array = stage.find(_ns("poles")).text
        if zeros_array is not None:
            zeros_array = zeros_array.split(" ")
        else:
            zeros_array = []
        if poles_array is not None:
            poles_array = poles_array.split(" ")
        else:
            poles_array = []

        # Keep counter for pole/zero number
        cnt = 0
        poles = []
        zeros = []
        for el in poles_array:
            poles.append(_tag2pole_or_zero(el, cnt))
            cnt += 1
        for el in zeros_array:
            zeros.append(_tag2pole_or_zero(el, cnt))
            cnt += 1

        # Return the paz response
        return PolesZerosResponseStage(
            pz_transfer_function_type=pz_transfer_function_type,
            normalization_frequency=normalization_freq,
            normalization_factor=normalization_factor,
            zeros=zeros,
            poles=poles,
            **kwargs
        )

    elif elem_type == "datalogger":
        cf_transfer_function_type = "DIGITAL"
        numerator = []
        denominator = []
        return CoefficientsTypeResponseStage(
            cf_transfer_function_type=cf_transfer_function_type, numerator=numerator, denominator=denominator, **kwargs
        )

    elif elem_type == "responsePolynomial":
        # Polynomial response (UNTESTED)
        # Currently not implemented in ObsPy (20-11-2015)
        f_low = None
        f_high = None
        max_err = None
        appr_type = _tag2obj(stage, _ns("approximationType"), str)
        appr_low = _tag2obj(stage, _ns("approximationLowerBound"), float)
        appr_high = _tag2obj(stage, _ns("approximationUpperBound"), float)
        coeffs_str = _tag2obj(stage, _ns("coefficients"), str)
        if coeffs_str is not None:
            coeffs = coeffs_str.split(" ")
            coeffs_float = []
            i = 0
            # pass additional mapping of coefficient counter
            # so that a proper stationXML can be formatted
            for c in coeffs:
                temp = _read_float_var(c, FilterCoefficient, additional_mapping={str("number"): i})
                coeffs_float.append(temp)
                i += 1

        return PolynomialResponseStage(
            approximation_type=appr_type,
            frequency_lower_bound=f_low,
            frequency_upper_bound=f_high,
            approximation_lower_bound=appr_low,
            approximation_upper_bound=appr_high,
            maximum_error=max_err,
            coefficients=coeffs,
            **kwargs
        )

    elif elem_type == "responseFIR":
        # For the responseFIR obtain the symmetry and
        # list of coefficients

        coeffs_str = _tag2obj(stage, _ns("coefficients"), str)
        coeffs_float = []
        if coeffs_str is not None and coeffs_str != "None":
            coeffs = coeffs_str.split(" ")
            i = 0
            # pass additional mapping of coefficient counter
            # so that a proper stationXML can be formatted
            for c in coeffs:
                temp = _read_float_var(c, FilterCoefficient, additional_mapping={str("number"): i})
                coeffs_float.append(temp)
                i += 1

        # Write the FIR symmetry to what ObsPy expects
        # A: NONE,
        # B: ODD,
        # C: EVEN
        symmetry = _tag2obj(stage, _ns("symmetry"), str)
        if symmetry == "A":
            symmetry = "NONE"
        elif symmetry == "B":
            symmetry = "ODD"
        elif symmetry == "C":
            symmetry = "EVEN"
        else:
            raise ValueError("Unknown symmetry metric; expected A, B, or C")

        return FIRResponseStage(coefficients=coeffs_float, symmetry=symmetry, **kwargs)
Beispiel #4
0
def _read_channel(inventory_root, cha_element, _ns):

    """
    reads channel element from sc3ml format

    :param sta_element: channel element
    :param _ns: namespace
    """

    code = cha_element.get("code")

    # Information is also kept within the parent <sensorLocation> element
    sen_loc_element = cha_element.getparent()
    location_code = sen_loc_element.get("code")

    # get site info from the <sensorLocation> element
    longitude = _read_floattype(sen_loc_element, _ns("longitude"), Longitude, datum=True)
    latitude = _read_floattype(sen_loc_element, _ns("latitude"), Latitude, datum=True)
    elevation = _read_floattype(sen_loc_element, _ns("elevation"), Distance, unit=True)
    depth = _read_floattype(cha_element, _ns("depth"), Distance, unit=True)
    channel = obspy.core.inventory.Channel(
        code=code, location_code=location_code, latitude=latitude, longitude=longitude, elevation=elevation, depth=depth
    )

    # obtain the sensorID and link to particular publicID <sensor> element
    # in the inventory base node
    sensor_id = cha_element.get("sensor")
    sensor_element = inventory_root.find(_ns("sensor[@publicID='" + sensor_id + "']"))

    # obtain the poles and zeros responseID and link to particular
    # <responsePAZ> publicID element in the inventory base node
    if sensor_element is not None:
        response_id = sensor_element.get("response")
        if response_id is not None:
            resp_type = response_id.split("#")[0]
            if resp_type == "ResponsePAZ":
                search = "responsePAZ[@publicID='" + response_id + "']"
                response_element = inventory_root.find(_ns(search))
            elif resp_type == "ResponsePolynomial":
                search = "responsePolynomial[@publicID='" + response_id + "']"
                response_element = inventory_root.find(_ns(search))
        else:
            response_element = None
    else:
        response_element = None

    # obtain the dataloggerID and link to particular <responsePAZ> publicID
    # element in the inventory base node
    datalogger_id = cha_element.get("datalogger")
    search = "datalogger[@publicID='" + datalogger_id + "']"
    data_log_element = inventory_root.find(_ns(search))

    channel.restricted_status = _get_restricted_status(cha_element, _ns)

    # There is no further information in the attributes of <stream>
    # Start and end date are included as tags instead
    channel.start_date = _tag2obj(cha_element, _ns("start"), obspy.UTCDateTime)
    channel.end_date = _tag2obj(cha_element, _ns("end"), obspy.UTCDateTime)

    # Determine sample rate (given is a numerator, denominator)
    # Assuming numerator is # samples and denominator is # seconds
    numerator = _tag2obj(cha_element, _ns("sampleRateNumerator"), int)
    denominator = _tag2obj(cha_element, _ns("sampleRateDenominator"), int)

    rate = numerator / denominator

    channel.sample_rate_ratio_number_samples = numerator
    channel.sample_rate_ratio_number_seconds = denominator
    channel.sample_rate = _read_float_var(rate, SampleRate)

    if sensor_element is not None:
        channel.sensor = _read_sensor(sensor_element, _ns)
    if data_log_element is not None:
        channel.data_logger = _read_datalogger(data_log_element, _ns)
        temp = _read_floattype(data_log_element, _ns("maxClockDrift"), ClockDrift)
        if channel.sample_rate != 0.0:
            channel.clock_drift_in_seconds_per_sample = _read_float_var(temp / channel.sample_rate, ClockDrift)
        else:
            msg = "Clock drift division by sample rate of 0: using sec/sample"
            warnings.warn(msg)
            channel.sample_rate = temp

    channel.azimuth = _read_floattype(cha_element, _ns("azimuth"), Azimuth)
    channel.dip = _read_floattype(cha_element, _ns("dip"), Dip)
    channel.storage_format = _tag2obj(cha_element, _ns("format"), str)

    if channel.sample_rate == 0.0:
        msg = "Something went hopelessly wrong, found sampling-rate of 0!"
        warnings.warn(msg)

    # Begin to collect digital/analogue filter chains
    # This information is stored as an array in the datalogger element
    response_fir_id = []
    response_paz_id = []
    if data_log_element is not None:
        # Find the decimation element with a particular num/denom
        decim_element = data_log_element.find(
            _ns(
                "decimation[@sampleRateDenominator='"
                + str(int(denominator))
                + "'][@sampleRateNumerator='"
                + str(int(numerator))
                + "']"
            )
        )
        analogue_filter_chain = _tag2obj(decim_element, _ns("analogueFilterChain"), str)
        if analogue_filter_chain is not None:
            response_paz_id = analogue_filter_chain.split(" ")
        digital_filter_chain = _tag2obj(decim_element, _ns("digitalFilterChain"), str)
        if digital_filter_chain is not None:
            response_fir_id = digital_filter_chain.split(" ")

    channel.response = _read_response(
        inventory_root,
        sensor_element,
        response_element,
        cha_element,
        data_log_element,
        _ns,
        channel.sample_rate,
        response_fir_id,
        response_paz_id,
    )

    return channel
Beispiel #5
0
def _read_response_stage(stage, _ns, rate, stage_number, input_units,
                         output_units):

    elem_type = stage.tag.split("}")[1]

    stage_sequence_number = stage_number

    # Obtain the stage gain and frequency
    # Default to a gain of 0 and frequency of 0 if missing
    stage_gain = _tag2obj(stage, _ns("gain"), float) or 0
    stage_gain_frequency = _tag2obj(stage, _ns("gainFrequency"),
                                    float) or float(0.00)

    name = stage.get("name")
    if name is not None:
        name = str(name)
    resource_id = stage.get("publicID")
    if resource_id is not None:
        resource_id = str(resource_id)

    # Determine the decimation parameters
    # This is dependent on the type of stage
    # Decimation delay/correction need to be normalized
    if(elem_type == "responseFIR"):
        decimation_factor = _tag2obj(stage, _ns("decimationFactor"), int)
        if rate != 0.0:
            temp = _tag2obj(stage, _ns("delay"), float) / rate
            decimation_delay = _read_float_var(temp,
                                               FloatWithUncertaintiesAndUnit,
                                               unit=True)
            temp = _tag2obj(stage, _ns("correction"), float) / rate
            decimation_corr = _read_float_var(temp,
                                              FloatWithUncertaintiesAndUnit,
                                              unit=True)
        else:
            decimation_delay = _read_float_var("inf",
                                               FloatWithUncertaintiesAndUnit,
                                               unit=True)
            decimation_corr = _read_float_var("inf",
                                              FloatWithUncertaintiesAndUnit,
                                              unit=True)
        decimation_input_sample_rate = \
            _read_float_var(rate, Frequency)
        decimation_offset = int(0)
    elif(elem_type == "datalogger"):
        decimation_factor = int(1)
        decimation_delay = _read_float_var(0.00,
                                           FloatWithUncertaintiesAndUnit,
                                           unit=True)
        decimation_corr = _read_float_var(0.00,
                                          FloatWithUncertaintiesAndUnit,
                                          unit=True)
        decimation_input_sample_rate = \
            _read_float_var(rate, Frequency)
        decimation_offset = int(0)
    elif(elem_type == "responsePAZ" or elem_type == "responsePolynomial"):
        decimation_factor = None
        decimation_delay = None
        decimation_corr = None
        decimation_input_sample_rate = None
        decimation_offset = None
    else:
        raise ValueError("Unknown type of response: " + str(elem_type))

    # set up list of for this stage arguments
    kwargs = {
        "stage_sequence_number": stage_sequence_number,
        "input_units": str(input_units),
        "output_units": str(output_units),
        "input_units_description": None,
        "output_units_description": None,
        "resource_id": None,
        "resource_id2": resource_id,
        "stage_gain": stage_gain,
        "stage_gain_frequency": stage_gain_frequency,
        "name": name,
        "description": None,
        "decimation_input_sample_rate": decimation_input_sample_rate,
        "decimation_factor": decimation_factor,
        "decimation_offset": decimation_offset,
        "decimation_delay": decimation_delay,
        "decimation_correction": decimation_corr
    }

    # Different processing for different types of responses
    # currently supported:
    # PAZ
    # COEFF
    # FIR
    # Polynomial response is not supported, could not find example
    if(elem_type == 'responsePAZ'):

        # read normalization params
        normalization_freq = _read_floattype(stage,
                                             _ns("normalizationFrequency"),
                                             Frequency)
        normalization_factor = _tag2obj(stage, _ns("normalizationFactor"),
                                        float)

        # Parse the type of the transfer function
        # A: Laplace (rad)
        # B: Laplace (Hz)
        # D: digital (z-transform)
        pz_transfer_function_type = _tag2obj(stage, _ns("type"), str)
        if pz_transfer_function_type == 'A':
            pz_transfer_function_type = 'LAPLACE (RADIANS/SECOND)'
        elif pz_transfer_function_type == 'B':
            pz_transfer_function_type = 'LAPLACE (HERTZ)'
        elif pz_transfer_function_type == 'D':
            pz_transfer_function_type = 'DIGITAL (Z-TRANSFORM)'
        else:
            msg = ("Unknown transfer function code %s. Defaulting to Laplace"
                   "(rad)") % pz_transfer_function_type
            warnings.warn(msg)
            pz_transfer_function_type = 'LAPLACE (RADIANS/SECOND)'
        number_of_zeros = _tag2obj(stage, _ns("numberOfZeros"), int)
        number_of_poles = _tag2obj(stage, _ns("numberOfPoles"), int)

        # Parse string of poles and zeros
        # paz are stored as a string in sc3ml
        # e.g. (-0.01234,0.01234) (-0.01234,-0.01234)
        if number_of_zeros > 0:
            zeros_array = stage.find(_ns("zeros")).text
            zeros_array = _parse_list_of_complex_string(zeros_array)
        else:
            zeros_array = []
        if number_of_poles > 0:
            poles_array = stage.find(_ns("poles")).text
            poles_array = _parse_list_of_complex_string(poles_array)
        else:
            poles_array = []

        # Keep counter for pole/zero number
        cnt = 0
        poles = []
        zeros = []
        for el in poles_array:
            poles.append(_tag2pole_or_zero(el, cnt))
            cnt += 1
        for el in zeros_array:
            zeros.append(_tag2pole_or_zero(el, cnt))
            cnt += 1

        # Return the paz response
        return PolesZerosResponseStage(
            pz_transfer_function_type=pz_transfer_function_type,
            normalization_frequency=normalization_freq,
            normalization_factor=normalization_factor, zeros=zeros,
            poles=poles, **kwargs)

    elif(elem_type == 'datalogger'):
        cf_transfer_function_type = "DIGITAL"
        numerator = []
        denominator = []
        return CoefficientsTypeResponseStage(
            cf_transfer_function_type=cf_transfer_function_type,
            numerator=numerator, denominator=denominator, **kwargs)

    elif(elem_type == 'responsePolynomial'):
        # Polynomial response (UNTESTED)
        # Currently not implemented in ObsPy (20-11-2015)
        f_low = None
        f_high = None
        max_err = None
        appr_type = _tag2obj(stage, _ns("approximationType"), str)
        appr_low = _tag2obj(stage, _ns("approximationLowerBound"), float)
        appr_high = _tag2obj(stage, _ns("approximationUpperBound"), float)
        coeffs_str = _tag2obj(stage, _ns("coefficients"), str)
        if coeffs_str is not None:
            coeffs = coeffs_str.split(" ")
            coeffs_float = []
            i = 0
            # pass additional mapping of coefficient counter
            # so that a proper stationXML can be formatted
            for c in coeffs:
                temp = _read_float_var(c, FilterCoefficient,
                                       additional_mapping={str("number"): i})
                coeffs_float.append(temp)
                i += 1

        return PolynomialResponseStage(
            approximation_type=appr_type, frequency_lower_bound=f_low,
            frequency_upper_bound=f_high, approximation_lower_bound=appr_low,
            approximation_upper_bound=appr_high, maximum_error=max_err,
            coefficients=coeffs, **kwargs)

    elif(elem_type == 'responseFIR'):
        # For the responseFIR obtain the symmetry and
        # list of coefficients

        coeffs_str = _tag2obj(stage, _ns("coefficients"), str)
        coeffs_float = []
        if coeffs_str is not None and coeffs_str != 'None':
            coeffs = coeffs_str.split()
            i = 0
            # pass additional mapping of coefficient counter
            # so that a proper stationXML can be formatted
            for c in coeffs:
                temp = _read_float_var(c, FilterCoefficient,
                                       additional_mapping={str("number"): i})
                coeffs_float.append(temp)
                i += 1

        # Write the FIR symmetry to what ObsPy expects
        # A: NONE,
        # B: ODD,
        # C: EVEN
        symmetry = _tag2obj(stage, _ns("symmetry"), str)
        if(symmetry == 'A'):
            symmetry = 'NONE'
        elif(symmetry == 'B'):
            symmetry = 'ODD'
        elif(symmetry == 'C'):
            symmetry = 'EVEN'
        else:
            raise ValueError('Unknown symmetry metric; expected A, B, or C')

        return FIRResponseStage(
            coefficients=coeffs_float, symmetry=symmetry, **kwargs)
Beispiel #6
0
def _read_channel(instrumentation_register, cha_element, _ns):

    """
    reads channel element from sc3ml format

    :param instrumentation_register: register of instrumentation metadata
    :param cha_element: channel element
    :param _ns: namespace
    """

    code = cha_element.get("code")

    # Information is also kept within the parent <sensorLocation> element
    sen_loc_element = cha_element.getparent()
    location_code = sen_loc_element.get("code")

    # get site info from the <sensorLocation> element
    longitude = _read_floattype(sen_loc_element, _ns("longitude"), Longitude,
                                datum=True)
    latitude = _read_floattype(sen_loc_element, _ns("latitude"), Latitude,
                               datum=True)
    elevation = _read_floattype(sen_loc_element, _ns("elevation"), Distance,
                                unit=True)
    depth = _read_floattype(cha_element, _ns("depth"), Distance,
                            unit=True)

    # Set values to 0 if they are is missing (see #1816)
    if longitude is None:
        msg = "Sensor is missing longitude information, using 0.0"
        warnings.warn(msg)
        longitude = 0
    if latitude is None:
        msg = "Sensor is missing latitude information, using 0.0"
        warnings.warn(msg)
        latitude = 0
    if elevation is None:
        msg = "Sensor is missing elevation information, using 0.0"
        warnings.warn(msg)
        elevation = 0
    if depth is None:
        msg = "Channel is missing depth information, using 0.0"
        warnings.warn(msg)
        depth = 0

    channel = obspy.core.inventory.Channel(
        code=code, location_code=location_code, latitude=latitude,
        longitude=longitude, elevation=elevation, depth=depth)

    # obtain the sensorID and link to particular publicID <sensor> element
    # in the inventory base node
    sensor_id = cha_element.get("sensor")
    sensor_element = instrumentation_register["sensors"].get(sensor_id)

    # obtain the poles and zeros responseID and link to particular
    # <responsePAZ> publicID element in the inventory base node
    if (sensor_element is not None and
       sensor_element.get("response") is not None):

        response_id = sensor_element.get("response")
        response_elements = []

        for resp_element in instrumentation_register["responses"].values():
            found_response = resp_element.get(response_id)
            if found_response is not None:
                response_elements.append(found_response)

        if len(response_elements) == 0:
            msg = ("Could not find response tag with public ID "
                   "'{}'.".format(response_id))
            raise obspy.ObsPyException(msg)
        elif len(response_elements) > 1:
            msg = ("Found multiple matching response tags with the same "
                   "public ID '{}'.".format(response_id))
            raise obspy.ObsPyException(msg)
        response_element = response_elements[0]
    else:
        response_element = None

    # obtain the dataloggerID and link to particular <responsePAZ> publicID
    # element in the inventory base node
    datalogger_id = cha_element.get("datalogger")
    data_log_element = \
        instrumentation_register["dataloggers"].get(datalogger_id)

    channel.restricted_status = _get_restricted_status(cha_element, _ns)

    # There is no further information in the attributes of <stream>
    # Start and end date are included as tags instead
    channel.start_date = _tag2obj(cha_element, _ns("start"), obspy.UTCDateTime)
    channel.end_date = _tag2obj(cha_element, _ns("end"), obspy.UTCDateTime)

    # Determine sample rate (given is a numerator, denominator)
    # Assuming numerator is # samples and denominator is # seconds
    numerator = _tag2obj(cha_element, _ns("sampleRateNumerator"), int)
    denominator = _tag2obj(cha_element, _ns("sampleRateDenominator"), int)

    # If numerator is non-zero and denominator zero, will raise
    # ZeroDivisionError.
    rate = numerator / denominator if numerator != 0 else 0

    channel.sample_rate_ratio_number_samples = numerator
    channel.sample_rate_ratio_number_seconds = denominator
    channel.sample_rate = _read_float_var(rate, SampleRate)

    if sensor_element is not None:
        channel.sensor = _read_sensor(sensor_element, _ns)
    if data_log_element is not None:
        channel.data_logger = _read_datalogger(data_log_element, _ns)
        temp = _read_floattype(data_log_element, _ns("maxClockDrift"),
                               ClockDrift)
        if temp is not None:
            if channel.sample_rate != 0.0:
                channel.clock_drift_in_seconds_per_sample = \
                    _read_float_var(temp / channel.sample_rate, ClockDrift)
            else:
                msg = "Clock drift division by sample rate of 0: " \
                      "using sec/sample"
                warnings.warn(msg)
                channel.sample_rate = temp

    channel.azimuth = _read_floattype(cha_element, _ns("azimuth"), Azimuth)
    channel.dip = _read_floattype(cha_element, _ns("dip"), Dip)
    match = re.search(r'{([^}]*)}', cha_element.tag)
    if match:
        namespace = match.group(1)
    else:
        namespace = _get_schema_namespace('0.9')
    channel.extra = {'format': {
        'value': _tag2obj(cha_element, _ns("format"), str),
        # storage format of channel not supported by StationXML1.1 anymore,
        # keep it as a foreign tag to be nice if anybody needs to access it
        'namespace': namespace}}

    if channel.sample_rate == 0.0:
        msg = "Something went hopelessly wrong, found sampling-rate of 0!"
        warnings.warn(msg)

    # Begin to collect digital/analogue filter chains
    # This information is stored as an array in the datalogger element
    response_fir_id = []
    response_paz_id = []
    if data_log_element is not None:
        # Find the decimation element with a particular num/denom
        decim_element = data_log_element.find(_ns(
            "decimation[@sampleRateDenominator='" +
            str(int(denominator)) + "'][@sampleRateNumerator='" +
            str(int(numerator)) + "']"))
        analogue_filter_chain = _tag2obj(decim_element,
                                         _ns("analogueFilterChain"), str)
        if analogue_filter_chain is not None:
            response_paz_id = analogue_filter_chain.split(" ")
        digital_filter_chain = _tag2obj(decim_element,
                                        _ns("digitalFilterChain"), str)
        if digital_filter_chain is not None:
            response_fir_id = digital_filter_chain.split(" ")

    channel.response = _read_response(instrumentation_register['responses'],
                                      sensor_element, response_element,
                                      cha_element, data_log_element, _ns,
                                      channel.sample_rate,
                                      response_fir_id, response_paz_id)

    return channel
Beispiel #7
0
def _read_channel(inventory_root, cha_element, _ns):
    """
    reads channel element from sc3ml format

    :param sta_element: channel element
    :param _ns: namespace
    """

    code = cha_element.get("code")

    # Information is also kept within the parent <sensorLocation> element
    sen_loc_element = cha_element.getparent()
    location_code = sen_loc_element.get("code")

    # get site info from the <sensorLocation> element
    longitude = _read_floattype(sen_loc_element,
                                _ns("longitude"),
                                Longitude,
                                datum=True)
    latitude = _read_floattype(sen_loc_element,
                               _ns("latitude"),
                               Latitude,
                               datum=True)
    elevation = _read_floattype(sen_loc_element,
                                _ns("elevation"),
                                Distance,
                                unit=True)
    depth = _read_floattype(cha_element, _ns("depth"), Distance, unit=True)
    channel = obspy.core.inventory.Channel(code=code,
                                           location_code=location_code,
                                           latitude=latitude,
                                           longitude=longitude,
                                           elevation=elevation,
                                           depth=depth)

    # obtain the sensorID and link to particular publicID <sensor> element
    # in the inventory base node
    sensor_id = cha_element.get("sensor")
    sensor_element = inventory_root.find(
        _ns("sensor[@publicID='" + sensor_id + "']"))

    # obtain the poles and zeros responseID and link to particular
    # <responsePAZ> publicID element in the inventory base node
    if sensor_element is not None:
        response_id = sensor_element.get("response")
        if response_id is not None:
            resp_type = response_id.split("#")[0]
            if resp_type == 'ResponsePAZ':
                search = "responsePAZ[@publicID='" + response_id + "']"
                response_element = inventory_root.find(_ns(search))
            elif resp_type == 'ResponsePolynomial':
                search = "responsePolynomial[@publicID='" + response_id + "']"
                response_element = inventory_root.find(_ns(search))
        else:
            response_element = None
    else:
        response_element = None

    # obtain the dataloggerID and link to particular <responsePAZ> publicID
    # element in the inventory base node
    datalogger_id = cha_element.get("datalogger")
    search = "datalogger[@publicID='" + datalogger_id + "']"
    data_log_element = inventory_root.find(_ns(search))

    channel.restricted_status = _get_restricted_status(cha_element, _ns)

    # There is no further information in the attributes of <stream>
    # Start and end date are included as tags instead
    channel.start_date = _tag2obj(cha_element, _ns("start"), obspy.UTCDateTime)
    channel.end_date = _tag2obj(cha_element, _ns("end"), obspy.UTCDateTime)

    # Determine sample rate (given is a numerator, denominator)
    # Assuming numerator is # samples and denominator is # seconds
    numerator = _tag2obj(cha_element, _ns("sampleRateNumerator"), int)
    denominator = _tag2obj(cha_element, _ns("sampleRateDenominator"), int)

    rate = numerator / denominator

    channel.sample_rate_ratio_number_samples = numerator
    channel.sample_rate_ratio_number_seconds = denominator
    channel.sample_rate = _read_float_var(rate, SampleRate)

    if sensor_element is not None:
        channel.sensor = _read_sensor(sensor_element, _ns)
    if data_log_element is not None:
        channel.data_logger = _read_datalogger(data_log_element, _ns)
        temp = _read_floattype(data_log_element, _ns("maxClockDrift"),
                               ClockDrift)
        if channel.sample_rate != 0.0:
            channel.clock_drift_in_seconds_per_sample = \
                _read_float_var(temp / channel.sample_rate, ClockDrift)
        else:
            msg = "Clock drift division by sample rate of 0: using sec/sample"
            warnings.warn(msg)
            channel.sample_rate = temp

    channel.azimuth = _read_floattype(cha_element, _ns("azimuth"), Azimuth)
    channel.dip = _read_floattype(cha_element, _ns("dip"), Dip)
    channel.storage_format = _tag2obj(cha_element, _ns("format"), str)

    if channel.sample_rate == 0.0:
        msg = "Something went hopelessly wrong, found sampling-rate of 0!"
        warnings.warn(msg)

    # Begin to collect digital/analogue filter chains
    # This information is stored as an array in the datalogger element
    response_fir_id = []
    response_paz_id = []
    if data_log_element is not None:
        # Find the decimation element with a particular num/denom
        decim_element = data_log_element.find(
            _ns("decimation[@sampleRateDenominator='" + str(int(denominator)) +
                "'][@sampleRateNumerator='" + str(int(numerator)) + "']"))
        analogue_filter_chain = _tag2obj(decim_element,
                                         _ns("analogueFilterChain"), str)
        if analogue_filter_chain is not None:
            response_paz_id = analogue_filter_chain.split(" ")
        digital_filter_chain = _tag2obj(decim_element,
                                        _ns("digitalFilterChain"), str)
        if digital_filter_chain is not None:
            response_fir_id = digital_filter_chain.split(" ")

    channel.response = _read_response(inventory_root, sensor_element,
                                      response_element, cha_element,
                                      data_log_element, _ns,
                                      channel.sample_rate, response_fir_id,
                                      response_paz_id)

    return channel
Beispiel #8
0
def _read_channel(inventory_root, cha_element, _ns):

    """
    reads channel element from sc3ml format

    :param sta_element: channel element
    :param _ns: namespace
    """

    code = cha_element.get("code")

    # Information is also kept within the parent <sensorLocation> element
    sen_loc_element = cha_element.getparent()
    location_code = sen_loc_element.get("code")

    # get site info from the <sensorLocation> element
    longitude = _read_floattype(sen_loc_element, _ns("longitude"), Longitude,
                                datum=True)
    latitude = _read_floattype(sen_loc_element, _ns("latitude"), Latitude,
                               datum=True)
    elevation = _read_floattype(sen_loc_element, _ns("elevation"), Distance,
                                unit=True)
    depth = _read_floattype(cha_element, _ns("depth"), Distance,
                            unit=True)

    # Set values to 0 if they are is missing (see #1816)
    if longitude is None:
        msg = "Sensor is missing longitude information, using 0.0"
        warnings.warn(msg)
        longitude = 0
    if latitude is None:
        msg = "Sensor is missing latitude information, using 0.0"
        warnings.warn(msg)
        latitude = 0
    if elevation is None:
        msg = "Sensor is missing elevation information, using 0.0"
        warnings.warn(msg)
        elevation = 0
    if depth is None:
        msg = "Channel is missing depth information, using 0.0"
        warnings.warn(msg)
        depth = 0

    channel = obspy.core.inventory.Channel(
        code=code, location_code=location_code, latitude=latitude,
        longitude=longitude, elevation=elevation, depth=depth)

    # obtain the sensorID and link to particular publicID <sensor> element
    # in the inventory base node
    sensor_id = cha_element.get("sensor")
    sensor_element = inventory_root.find(_ns("sensor[@publicID='" + sensor_id +
                                             "']"))
    # obtain the poles and zeros responseID and link to particular
    # <responsePAZ> publicID element in the inventory base node
    if (sensor_element is not None and
       sensor_element.get("response") is not None):

        response_id = sensor_element.get("response")
        response_elements = []

        for resp_type in ['responsePAZ', 'responsePolynomial']:
            search = "{}[@publicID='{}']".format(resp_type, response_id)
            response_elements += inventory_root.findall(_ns(search))
        if len(response_elements) == 0:
            msg = ("Could not find response tag with public ID "
                   "'{}'.".format(response_id))
            raise obspy.ObsPyException(msg)
        elif len(response_elements) > 1:
            msg = ("Found multiple matching response tags with the same "
                   "public ID '{}'.".format(response_id))
            raise obspy.ObsPyException(msg)
        response_element = response_elements[0]
    else:
        response_element = None

    # obtain the dataloggerID and link to particular <responsePAZ> publicID
    # element in the inventory base node
    datalogger_id = cha_element.get("datalogger")
    search = "datalogger[@publicID='" + datalogger_id + "']"
    data_log_element = inventory_root.find(_ns(search))

    channel.restricted_status = _get_restricted_status(cha_element, _ns)

    # There is no further information in the attributes of <stream>
    # Start and end date are included as tags instead
    channel.start_date = _tag2obj(cha_element, _ns("start"), obspy.UTCDateTime)
    channel.end_date = _tag2obj(cha_element, _ns("end"), obspy.UTCDateTime)

    # Determine sample rate (given is a numerator, denominator)
    # Assuming numerator is # samples and denominator is # seconds
    numerator = _tag2obj(cha_element, _ns("sampleRateNumerator"), int)
    denominator = _tag2obj(cha_element, _ns("sampleRateDenominator"), int)

    # If numerator is zero, set rate to zero irrespective of the denominator.
    # If numerator is non-zero and denominator zero, will raise
    # ZeroDivisionError.
    rate = numerator / denominator if numerator != 0 else 0

    channel.sample_rate_ratio_number_samples = numerator
    channel.sample_rate_ratio_number_seconds = denominator
    channel.sample_rate = _read_float_var(rate, SampleRate)

    if sensor_element is not None:
        channel.sensor = _read_sensor(sensor_element, _ns)
    if data_log_element is not None:
        channel.data_logger = _read_datalogger(data_log_element, _ns)
        temp = _read_floattype(data_log_element, _ns("maxClockDrift"),
                               ClockDrift)
        if temp is not None:
            if channel.sample_rate != 0.0:
                channel.clock_drift_in_seconds_per_sample = \
                    _read_float_var(temp / channel.sample_rate, ClockDrift)
            else:
                msg = "Clock drift division by sample rate of 0: " \
                      "using sec/sample"
                warnings.warn(msg)
                channel.sample_rate = temp

    channel.azimuth = _read_floattype(cha_element, _ns("azimuth"), Azimuth)
    channel.dip = _read_floattype(cha_element, _ns("dip"), Dip)
    channel.storage_format = _tag2obj(cha_element, _ns("format"), str)

    if channel.sample_rate == 0.0:
        msg = "Something went hopelessly wrong, found sampling-rate of 0!"
        warnings.warn(msg)

    # Begin to collect digital/analogue filter chains
    # This information is stored as an array in the datalogger element
    response_fir_id = []
    response_paz_id = []
    if data_log_element is not None:
        # Find the decimation element with a particular num/denom
        decim_element = data_log_element.find(_ns(
            "decimation[@sampleRateDenominator='" +
            str(int(denominator)) + "'][@sampleRateNumerator='" +
            str(int(numerator)) + "']"))
        analogue_filter_chain = _tag2obj(decim_element,
                                         _ns("analogueFilterChain"), str)
        if analogue_filter_chain is not None:
            response_paz_id = analogue_filter_chain.split(" ")
        digital_filter_chain = _tag2obj(decim_element,
                                        _ns("digitalFilterChain"), str)
        if digital_filter_chain is not None:
            response_fir_id = digital_filter_chain.split(" ")

    channel.response = _read_response(inventory_root, sensor_element,
                                      response_element, cha_element,
                                      data_log_element, _ns,
                                      channel.sample_rate,
                                      response_fir_id, response_paz_id)

    return channel
Beispiel #9
0
def _read_response_stage(stage, _ns, rate, stage_sequence_number, input_units,
                         output_units):

    # Strip the namespace to get the element name (response type)
    elem_type = stage.tag.split("}")[1]

    # Get the stage gain and frequency: 0 and 0.00 per default
    stage_gain = _tag2obj(stage, _ns("gain"), float) or 0
    stage_gain_frequency = _tag2obj(stage, _ns("gainFrequency"), float) or 0.00

    # Get the stage name
    name = stage.get("name")
    if name is not None:
        name = str(name)

    # And the public resource identifier
    resource_id = stage.get("publicID")
    if resource_id is not None:
        resource_id = str(resource_id)

    # Set up decimation parameters
    decimation = {
        "factor": None,
        "delay": None,
        "correction": None,
        "rate": None,
        "offset": None
    }

    # Skip decimation for analogue outputs
    # Since 0.10 ResponsePAZ can have a decimation attributes
    if output_units != "V":

        # Get element or default value
        decimation['factor'] = _tag2obj(stage, _ns("decimationFactor"),
                                        int) or 1
        decimation['delay'] = _tag2obj(stage, _ns("delay"), float) or 0
        decimation["correction"] = _tag2obj(stage, _ns("correction"),
                                            float) or 0
        decimation['offset'] = _tag2obj(stage, _ns("offset"), float) or 0
        decimation['rate'] = _read_float_var(rate, Frequency)

    # Decimation delay/correction need to be normalized
    if rate != 0.0:
        if decimation['delay'] is not None:
            decimation['delay'] = \
                _read_float_var(decimation['delay'] / rate,
                                FloatWithUncertaintiesAndUnit, unit=True)
        if decimation['correction'] is not None:
            decimation['correction'] = \
                _read_float_var(decimation['correction'] / rate,
                                FloatWithUncertaintiesAndUnit, unit=True)

    # Set up list of for this stage arguments
    kwargs = {
        "stage_sequence_number": stage_sequence_number,
        "input_units": str(input_units),
        "output_units": str(output_units),
        "input_units_description": None,
        "output_units_description": None,
        "resource_id": None,
        "resource_id2": resource_id,
        "stage_gain": stage_gain,
        "stage_gain_frequency": stage_gain_frequency,
        "name": name,
        "description": None,
        "decimation_input_sample_rate": decimation['rate'],
        "decimation_factor": decimation['factor'],
        "decimation_offset": decimation['offset'],
        "decimation_delay": decimation['delay'],
        "decimation_correction": decimation['correction']
    }

    # Different processing for different types of responses
    # currently supported: PAZ, COEFF, FIR
    # Polynomial response is not supported, could not find example
    if (elem_type == 'responsePAZ'):

        # read normalization params
        normalization_freq = _read_floattype(stage,
                                             _ns("normalizationFrequency"),
                                             Frequency)
        normalization_factor = _tag2obj(stage, _ns("normalizationFactor"),
                                        float)

        # Parse the type of the transfer function
        # A: Laplace (rad)
        # B: Laplace (Hz)
        # D: digital (z-transform)
        pz_transfer_function_type = _tag2obj(stage, _ns("type"), str)
        pz_transfer_function_type = \
            _map_transfer_type(pz_transfer_function_type)

        # Parse string of poles and zeros
        # paz are stored as a string in scxml
        # e.g. (-0.01234,0.01234) (-0.01234,-0.01234)
        zeros_array = stage.find(_ns("zeros"))
        poles_array = stage.find(_ns("poles"))

        if zeros_array is not None and zeros_array.text is not None:
            zeros_array = _parse_list_of_complex_string(zeros_array.text)
        else:
            zeros_array = []
        if poles_array is not None and poles_array.text is not None:
            poles_array = _parse_list_of_complex_string(poles_array.text)
        else:
            poles_array = []

        # Keep counter for pole/zero number
        cnt = 0
        poles = []
        zeros = []
        for el in poles_array:
            poles.append(_tag2pole_or_zero(el, cnt))
            cnt += 1
        for el in zeros_array:
            zeros.append(_tag2pole_or_zero(el, cnt))
            cnt += 1
        # Return the paz response
        return PolesZerosResponseStage(
            pz_transfer_function_type=pz_transfer_function_type,
            normalization_frequency=normalization_freq,
            normalization_factor=normalization_factor,
            zeros=zeros,
            poles=poles,
            **kwargs)

    # For IIR filters reuse the PolesZerosResponseStage
    elif (elem_type == 'responseIIR'):
        pz_transfer_function_type = _tag2obj(stage, _ns("type"), str)
        pz_transfer_function_type = _map_transfer_type(
            pz_transfer_function_type)

        numerators = stage.find(_ns("numerators")).text.split(" ")
        denominators = stage.find(_ns("denominators")).text.split(" ")

        numerators = list(map(lambda x: float(x), numerators))
        denominators = list(map(lambda x: float(x), denominators))

        # Convert linear filter to pole, zero, gain repr.
        # See #2004 @andres-h
        zeros, poles, gain = \
            (np.round(ele, 6) for ele in tf2zpk(numerators, denominators))
        msg = "ResponseIIR is not fully tested in ObsPy. Please be cautious"
        warnings.warn(msg)

        return PolesZerosResponseStage(
            pz_transfer_function_type=pz_transfer_function_type,
            normalization_frequency=0,
            normalization_factor=1,
            zeros=zeros,
            poles=poles,
            **kwargs)

    # Datalogger element: V => Counts
    # Set empty coefficients and hard code as digital
    elif (elem_type == "datalogger"):
        return CoefficientsTypeResponseStage(
            cf_transfer_function_type="DIGITAL",
            numerator=[],
            denominator=[],
            **kwargs)

    elif (elem_type == 'responsePolynomial'):
        # Polynomial response (UNTESTED)
        # Currently not implemented in ObsPy (20-11-2015)
        f_low = None
        f_high = None
        max_err = None
        appr_type = _tag2obj(stage, _ns("approximationType"), str)
        appr_low = _tag2obj(stage, _ns("approximationLowerBound"), float)
        appr_high = _tag2obj(stage, _ns("approximationUpperBound"), float)
        coeffs_str = _tag2obj(stage, _ns("coefficients"), str)
        if coeffs_str is not None:
            coeffs = coeffs_str.strip().split(" ")
            coeffs_float = []
            i = 0
            # pass additional mapping of coefficient counter
            # so that a proper stationXML can be formatted
            for c in coeffs:
                temp = _read_float_var(c,
                                       FilterCoefficient,
                                       additional_mapping={str("number"): i})
                coeffs_float.append(temp)
                i += 1

        return PolynomialResponseStage(approximation_type=appr_type,
                                       frequency_lower_bound=f_low,
                                       frequency_upper_bound=f_high,
                                       approximation_lower_bound=appr_low,
                                       approximation_upper_bound=appr_high,
                                       maximum_error=max_err,
                                       coefficients=coeffs,
                                       **kwargs)

    elif (elem_type == 'responseFIR'):
        # For the responseFIR obtain the symmetry and
        # list of coefficients

        coeffs_str = _tag2obj(stage, _ns("coefficients"), str)
        coeffs_float = []
        if coeffs_str is not None and coeffs_str != 'None':
            coeffs = coeffs_str.strip().split(" ")
            i = 0
            # pass additional mapping of coefficient counter
            # so that a proper stationXML can be formatted
            for c in coeffs:
                temp = _read_float_var(c,
                                       FilterCoefficient,
                                       additional_mapping={str("number"): i})
                coeffs_float.append(temp)
                i += 1

        # Write the FIR symmetry to what ObsPy expects
        # A: NONE,
        # B: ODD,
        # C: EVEN
        symmetry = _tag2obj(stage, _ns("symmetry"), str)
        if (symmetry == 'A'):
            symmetry = 'NONE'
        elif (symmetry == 'B'):
            symmetry = 'ODD'
        elif (symmetry == 'C'):
            symmetry = 'EVEN'
        else:
            raise ValueError('Unknown symmetry metric; expected A, B, or C')

        return FIRResponseStage(coefficients=coeffs_float,
                                symmetry=symmetry,
                                **kwargs)

    elif (elem_type == 'responseFAP'):

        data = _tag2obj(stage, _ns("tuples"), str)
        data = np.array(data.split(), dtype=np.float64)
        freq, amp, phase = data.reshape((-1, 3)).T
        elements = []
        for freq_, amp_, phase_ in zip(freq, amp, phase):
            elements.append(ResponseListElement(freq_, amp_, phase_))

        return ResponseListResponseStage(response_list_elements=elements,
                                         **kwargs)