Example #1
0
def geophone_response(resonance_frequency,
                      gain,
                      damping=0.707,
                      output_resistance=np.inf,
                      cable_length=np.inf,
                      cable_capacitance=np.inf,
                      sensitivity=1,
                      stage_sequence_number=1):
    paz = corn_freq_2_paz(resonance_frequency, damp=damping)

    l = cable_length
    R = output_resistance
    C = cable_capacitance

    if ((R * l * C) != np.inf) and ((R * l * C) != 0):
        pole_cable = -1 / (R * l * C)
        paz['poles'].append(pole_cable)

    i_s = InstrumentSensitivity(sensitivity,
                                resonance_frequency,
                                input_units='M/S',
                                output_units='M/S',
                                input_units_description='velocity',
                                output_units_description='velocity')

    pzr = PolesZerosResponseStage(stage_sequence_number, gain,
                                  resonance_frequency, 'M/S', 'M/S',
                                  'LAPLACE (RADIANT/SECOND)',
                                  resonance_frequency, paz['zeros'],
                                  paz['poles'])

    return Response(instrument_sensitivity=i_s, response_stages=[pzr])
Example #2
0
def _channel_from_stats(stats):
    if stats.standard.units in UNITS:
        units = UNITS[stats.standard.units]
    else:
        units = ''
    instrument = stats.standard.instrument
    serialnum = stats.standard.sensor_serial_number
    if len(instrument) or len(serialnum):
        equipment = Equipment(type=instrument,
                              serial_number=serialnum)
    else:
        equipment = None
    depth = 0.0
    azimuth = None
    c1 = 'horizontal_orientation' in stats.standard
    c2 = c1 and not np.isnan(stats.standard.horizontal_orientation)
    if c2:
        azimuth = stats.standard.horizontal_orientation
    else:
        azimuth = 0

    if not (azimuth >= 0 and azimuth <= 360):
        azimuth = 0

    response = None
    if 'response' in stats:
        response = stats['response']
    else:
        # we may have instrument sensitivity...
        frequency = 1 / stats['standard']['instrument_period']
        units = stats.standard.units
        if not np.isnan(stats['standard']['instrument_sensitivity']):
            sens = stats['standard']['instrument_sensitivity']
        else:
            sens = 1.0
        sensitivity = InstrumentSensitivity(sens,
                                            frequency=frequency,
                                            input_units=units,
                                            output_units='COUNTS')
        response = Response(instrument_sensitivity=sensitivity)

    comments = Comment(stats.standard.comments)
    logging.debug('channel: %s' % stats.channel)
    channel = Channel(stats.channel,
                      stats.location,
                      stats.coordinates['latitude'],
                      stats.coordinates['longitude'],
                      stats.coordinates['elevation'],
                      depth,
                      azimuth=azimuth,
                      sample_rate=stats.sampling_rate,
                      storage_format=stats.standard.source_format,
                      calibration_units=units,
                      comments=[comments],
                      response=response,
                      sensor=equipment)
    return channel
Example #3
0
def _channel_from_stats(stats):
    if stats.standard.units in UNITS:
        units = UNITS[stats.standard.units]
    else:
        units = ""
    instrument = stats.standard.instrument
    serialnum = stats.standard.sensor_serial_number
    if len(instrument) or len(serialnum):
        equipment = Equipment(type=instrument, serial_number=serialnum)
    else:
        equipment = None
    depth = 0.0
    azimuth = None
    c1 = "horizontal_orientation" in stats.standard
    c2 = c1 and not np.isnan(stats.standard.horizontal_orientation)
    if c2:
        azimuth = stats.standard.horizontal_orientation
    else:
        azimuth = 0

    if not (azimuth >= 0 and azimuth <= 360):
        azimuth = 0

    response = None
    if "response" in stats:
        response = stats["response"]
    else:
        # we may have instrument sensitivity...
        frequency = 1 / stats["standard"]["instrument_period"]
        units = stats.standard.units
        if not np.isnan(stats["standard"]["instrument_sensitivity"]):
            sens = stats["standard"]["instrument_sensitivity"]
        else:
            sens = 1.0
        sensitivity = InstrumentSensitivity(sens,
                                            frequency=frequency,
                                            input_units=units,
                                            output_units="COUNTS")
        response = Response(instrument_sensitivity=sensitivity)

    comments = Comment(stats.standard.comments)
    logging.debug(f"channel: {stats.channel}")
    channel = Channel(
        stats.channel,
        stats.location,
        stats.coordinates["latitude"],
        stats.coordinates["longitude"],
        stats.coordinates["elevation"],
        depth,
        azimuth=azimuth,
        sample_rate=stats.sampling_rate,
        calibration_units=units,
        comments=[comments],
        response=response,
        sensor=equipment,
    )
    return channel
Example #4
0
def accelerometer_response(resonance_frequency,
                           gain,
                           sensitivity=1,
                           stage_sequence_number=1,
                           damping=0.707):
    i_s = InstrumentSensitivity(sensitivity,
                                resonance_frequency,
                                input_units='M/S/S',
                                output_units='M/S/S',
                                input_units_description='acceleration',
                                output_units_description='acceleration')

    paz = corn_freq_2_paz(resonance_frequency, damp=damping)

    paz['zeros'] = []

    pzr = PolesZerosResponseStage(1, 1, 14, 'M/S/S', 'M/S',
                                  'LAPLACE (RADIANT/SECOND)', 1, [],
                                  paz['poles'])

    return Response(instrument_sensitivity=i_s, response_stages=[pzr])
Example #5
0
def read_fdsn_station_text_file(path_or_file_object):
    """
    Function reading a FDSN station text file to an inventory object.

    :param path_or_file_object: File name or file like object.
    """
    def _read(obj):
        r = unicode_csv_reader(obj, delimiter=native_str("|"))
        header = next(r)
        header[0] = header[0].lstrip("#")
        header = [_i.strip().lower() for _i in header]
        # IRIS currently has a wrong header name. Just map it.
        header = [
            _i.replace("instrument", "sensordescription") for _i in header
        ]

        all_lines = []
        for line in r:
            # Skip comment lines.
            if line[0].startswith("#"):
                continue
            all_lines.append([_i.strip() for _i in line])
        return {"header": tuple(header), "content": all_lines}

    # Enable reading from files and buffers opened in binary mode.
    if (hasattr(path_or_file_object, "mode") and
            "b" in path_or_file_object.mode) or \
            isinstance(path_or_file_object, io.BytesIO):
        buf = io.StringIO(path_or_file_object.read().decode("utf-8"))
        buf.seek(0, 0)
        path_or_file_object = buf

    if hasattr(path_or_file_object, "read"):
        content = _read(path_or_file_object)
    else:
        with open(path_or_file_object, "rt", newline="",
                  encoding="utf8") as fh:
            content = _read(fh)

    # Figure out the type.
    if content["header"] == network_components:
        level = "network"
        filetypes = network_types
    elif content["header"] == station_components:
        level = "station"
        filetypes = station_types
    elif content["header"] == channel_components:
        level = "channel"
        filetypes = channel_types
    else:
        raise ValueError("Unknown type of header.")

    content = content["content"]
    converted_content = []
    # Convert all types.
    for line in content:
        converted_content.append(
            [v_type(value) for value, v_type in zip(line, filetypes)])

    # Now convert to an inventory object.
    inv = Inventory(networks=[], source=None)

    if level == "network":
        for net in converted_content:
            network = Network(code=net[0],
                              description=net[1],
                              start_date=net[2],
                              end_date=net[3],
                              total_number_of_stations=net[4])
            inv.networks.append(network)
    elif level == "station":
        networks = collections.OrderedDict()
        for sta in converted_content:
            site = Site(name=sta[5])
            station = Station(code=sta[1],
                              latitude=sta[2],
                              longitude=sta[3],
                              elevation=sta[4],
                              site=site,
                              start_date=sta[6],
                              end_date=sta[7])
            if sta[0] not in networks:
                networks[sta[0]] = []
            networks[sta[0]].append(station)
        for network_code, stations in networks.items():
            net = Network(code=network_code, stations=stations)
            inv.networks.append(net)
    elif level == "channel":
        networks = collections.OrderedDict()
        stations = collections.OrderedDict()

        for channel in converted_content:
            net, sta, loc, chan, lat, lng, ele, dep, azi, dip, inst, scale, \
                scale_freq, scale_units, s_r, st, et = channel

            if net not in networks:
                networks[net] = Network(code=net)

            if (net, sta) not in stations:
                station = Station(code=sta,
                                  latitude=lat,
                                  longitude=lng,
                                  elevation=ele)
                networks[net].stations.append(station)
                stations[(net, sta)] = station

            sensor = Equipment(type=inst)
            if scale is not None and scale_freq is not None:
                resp = Response(instrument_sensitivity=InstrumentSensitivity(
                    value=scale,
                    frequency=scale_freq,
                    input_units=scale_units,
                    output_units=None))
            else:
                resp = None
            try:
                channel = Channel(code=chan,
                                  location_code=loc,
                                  latitude=lat,
                                  longitude=lng,
                                  elevation=ele,
                                  depth=dep,
                                  azimuth=azi,
                                  dip=dip,
                                  sensor=sensor,
                                  sample_rate=s_r,
                                  start_date=st,
                                  end_date=et,
                                  response=resp)
            except Exception as e:
                warnings.warn(
                    "Failed to parse channel %s.%s.%s.%s due to: %s" %
                    (net, sta, loc, chan, str(e)), UserWarning)
                continue
            stations[(net, sta)].channels.append(channel)
        inv.networks.extend(list(networks.values()))
    else:
        # Cannot really happen - just a safety measure.
        raise NotImplementedError("Unknown level: %s" % str(level))
    return inv
Example #6
0
def getStation(stationBlock, units, transFuncs):
    ## Should probably do a check up here to see that the order given in block is consistent ##
    for entry in stationBlock:
        if entry.name == 'Station Identifier':
            #            print 'NewStation!',entry.station_call_letters
            staDict = {
                'code': entry.station_call_letters,
                'latitude': entry.latitude,
                'longitude': entry.longitude,
                'elevation': entry.elevation,
                'channels': [],
                'site': Site(entry.site_name),
                'creation_date':
                UTCDateTime(entry.start_effective_date),  # Allows for save
                'start_date': UTCDateTime(entry.start_effective_date),
                'end_date': UTCDateTime(entry.end_effective_date)
            }
            staNetCode = entry.network_code
        # If found a new channel, reset the stages
        elif entry.name == 'Channel Identifier':
            #            print 'NewChannel!',entry.channel_identifier
            stages = []
            chaDict = {
                'code': entry.channel_identifier,
                'location_code': entry.location_identifier,
                'latitude': entry.latitude,
                'longitude': entry.longitude,
                'elevation': entry.elevation,
                'depth': entry.local_depth,
                'sample_rate': entry.sample_rate,
                'start_date': UTCDateTime(entry.start_date),
                'end_date': UTCDateTime(entry.end_date),
                'azimuth': entry.azimuth,
                'dip': entry.dip
            }
        #code, location_code, latitude, longitude, elevation, depth
        # If on a new stage, set up the dictionary again
        # ...paz stage
        elif entry.name == 'Response Poles and Zeros':
            # Get units
            stageReqs = {}
            #            print entry.name,entry.stage_sequence_number
            #            print entry
            #            quit()
            stageReqs['input_units'] = units[entry.stage_signal_input_units]
            stageReqs['output_units'] = units[entry.stage_signal_output_units]
            # Collect the poles and zeros
            lastType = 'paz'
            if entry.number_of_complex_zeros == 0:
                zeros = np.array([], dtype=float)
            else:
                zeros = np.array(entry.real_zero, dtype=float) + np.array(
                    entry.imaginary_zero, dtype=float) * 1j
            if entry.number_of_complex_poles == 0:
                poles = np.array([], dtype=float)
            else:
                poles = np.array(entry.real_pole, dtype=float) + np.array(
                    entry.imaginary_pole, dtype=float) * 1j
            # Form the paz response dictionary (also ensure arrays are 1D)
            pazDict = {
                'pz_transfer_function_type':
                transFuncs[entry.transfer_function_types],
                'normalization_factor':
                entry.A0_normalization_factor,
                'normalization_frequency':
                entry.normalization_frequency,
                'zeros':
                setArrDim(zeros),
                'poles':
                setArrDim(poles)
            }
        # ...coeff stage
        elif entry.name == 'Response Coefficients':
            # Get units
            stageReqs = {}
            #            print entry.name,entry.stage_sequence_number
            stageReqs['input_units'] = units[entry.signal_input_units]
            stageReqs['output_units'] = units[entry.signal_output_units]
            # Collect the coefficients
            lastType = 'coef'
            if entry.number_of_denominators == 0:
                denom = np.array([], dtype=float)
                denomErr = np.array([], dtype=float)
            else:
                denom = np.array(entry.denominator_coefficient, dtype=float)
                denomErr = np.array(entry.denominator_error, dtype=float)
            if entry.number_of_numerators == 0:
                numer = np.array([], dtype=float)
                numerErr = np.array([], dtype=float)
            else:
                numer = np.array(entry.numerator_coefficient, dtype=float)
                numerErr = np.array(entry.numerator_error, dtype=float)
            # Convert these arrays into lists of numbers which have uncertainty (also ensure arrays are 1D)
            denomArr = genArrWithUncertainty(setArrDim(denom),
                                             setArrDim(denomErr))
            numerArr = genArrWithUncertainty(setArrDim(numer),
                                             setArrDim(numerErr))
            # Form the coeefficient response dictionary
            coefDict = {
                'cf_transfer_function_type': transFuncs[entry.response_type],
                'numerator': numerArr,
                'denominator': denomArr
            }
        # Get the decimation sampling info
        elif entry.name == 'Decimation':
            #            print entry.name,entry.stage_sequence_number
            stageReqs['decimation_input_sample_rate'] = Frequency(
                entry.input_sample_rate)
            stageReqs['decimation_factor'] = entry.decimation_factor
            stageReqs['decimation_offset'] = entry.decimation_offset
            stageReqs['decimation_delay'] = FloatWithUncertaintiesAndUnit(
                entry.estimated_delay)
            stageReqs['decimation_correction'] = FloatWithUncertaintiesAndUnit(
                entry.correction_applied)
        # Get the stage sensitivity
        elif entry.name == 'Channel Sensitivity Gain':
            #            print entry.name,entry.stage_sequence_number
            if entry.stage_sequence_number != 0:
                stageReqs[
                    'stage_sequence_number'] = entry.stage_sequence_number
                stageReqs['stage_gain'] = entry.sensitivity_gain
                stageReqs['stage_gain_frequency'] = entry.frequency
                # See what type of stage this was
                if lastType == 'paz':
                    pazDict.update(stageReqs)
                    stages.append(PolesZerosResponseStage(**pazDict))
                else:
                    coefDict.update(stageReqs)
                    stages.append(CoefficientsTypeResponseStage(**coefDict))
            # If on the last stage, send off the collected stage info
            else:
                if len(stages) > 0:
                    instrSens = InstrumentSensitivity(entry.sensitivity_gain,
                                                      entry.frequency,
                                                      stages[0].input_units,
                                                      stages[-1].output_units)
                    # Finalize the channel dictionary, and append this channel to the station dictionary
                    chaResp = Response(response_stages=stages,
                                       instrument_sensitivity=instrSens)
                    chaDict['response'] = chaResp
                staDict['channels'].append(Channel(**chaDict))
    # Return the stations to the list of stations (also track the network code)
    return Station(**staDict), staNetCode
Example #7
0
    def test_write_stationtxt(self):
        """
        Test writing stationtxt at channel level
        """
        # Manually create a test Inventory object.
        resp_1 = Response(
            instrument_sensitivity=InstrumentSensitivity(frequency=0.02,
                                                         input_units="M/S",
                                                         output_units=None,
                                                         value=8.48507E8))
        resp_2 = Response(
            instrument_sensitivity=InstrumentSensitivity(frequency=1.0,
                                                         input_units="M/S**2",
                                                         output_units=None,
                                                         value=53435.4))
        resp_3 = Response(
            instrument_sensitivity=InstrumentSensitivity(frequency=0.03,
                                                         input_units="M/S",
                                                         output_units=None,
                                                         value=6.27252E8))
        test_inv = Inventory(
            source=None,
            networks=[
                Network(
                    code="IU",
                    start_date=obspy.UTCDateTime("1988-01-01T00:00:00"),
                    end_date=obspy.UTCDateTime("2500-12-31T23:59:59"),
                    total_number_of_stations=1,
                    description="Global Seismograph Network (GSN - IRIS/USGS)",
                    stations=[
                        Station(code="ANMO",
                                latitude=34.9459,
                                longitude=-106.4572,
                                elevation=1850.0,
                                channels=[
                                    Channel(code="BCI",
                                            location_code="",
                                            latitude=34.9459,
                                            longitude=-106.4572,
                                            elevation=1850.0,
                                            depth=100.0,
                                            azimuth=0.0,
                                            dip=0.0,
                                            sample_rate=0.0,
                                            sensor=Equipment(
                                                description=
                                                "Geotech KS-36000-I Borehole "
                                                "Seismometer"),
                                            start_date=obspy.UTCDateTime(
                                                "1989-08-29T00:00:00"),
                                            end_date=obspy.UTCDateTime(
                                                "1995-02-01T00:00:00"),
                                            response=resp_1),
                                    Channel(
                                        code="LNZ",
                                        location_code="20",
                                        latitude=34.9459,
                                        longitude=-106.4572,
                                        elevation=1820.7,
                                        depth=0.0,
                                        azimuth=0.0,
                                        dip=-90.0,
                                        sample_rate=0.0,
                                        sensor=Equipment(
                                            description="Titan Accelerometer"),
                                        start_date=obspy.UTCDateTime(
                                            "2013-06-20T16:30:00"),
                                        response=resp_2),
                                ]),
                    ]),
                Network(
                    code="6E",
                    start_date=obspy.UTCDateTime("2013-01-01T00:00:00"),
                    end_date=obspy.UTCDateTime("2016-12-31T23:59:59"),
                    total_number_of_stations=1,
                    description="Wabash Valley Seismic Zone",
                    stations=[
                        Station(
                            code="SH01",
                            latitude=37.7457,
                            longitude=-88.1368,
                            elevation=126.0,
                            channels=[
                                Channel(
                                    code="LOG",
                                    location_code="",
                                    latitude=37.7457,
                                    longitude=-88.1368,
                                    elevation=126.0,
                                    depth=0.0,
                                    azimuth=0.0,
                                    dip=0.0,
                                    sample_rate=0.0,
                                    sensor=Equipment(
                                        description="Reftek 130 Datalogger"),
                                    start_date=obspy.UTCDateTime(
                                        "2013-11-23T00:00:00"),
                                    end_date=obspy.UTCDateTime(
                                        "2016-12-31T23:59:59"),
                                    response=resp_3)
                            ]),
                    ])
            ])

        # CHANNEL level test
        stio = io.StringIO()
        test_inv.write(stio, format="STATIONTXT", level="CHANNEL")
        # check contents
        content = stio.getvalue()
        expected = [
            ("Network|Station|Location|Channel|Latitude|Longitude|"
             "Elevation|Depth|Azimuth|Dip|SensorDescription|Scale|"
             "ScaleFreq|ScaleUnits|SampleRate|StartTime|EndTime"),
            ("IU|ANMO||BCI|34.9459|-106.4572|1850.0|100.0|0.0|"
             "0.0|Geotech KS-36000-I Borehole Seismometer|"
             "848507000.0|0.02|M/S|0.0|1989-08-29T00:00:00|"
             "1995-02-01T00:00:00"),
            ("IU|ANMO|20|LNZ|34.9459|-106.4572|1820.7|0.0|0.0|"
             "-90.0|Titan Accelerometer|53435.4|1.0|M/S**2|0.0|"
             "2013-06-20T16:30:00|"),
            ("6E|SH01||LOG|37.7457|-88.1368|126.0|0.0|0.0|0.0|"
             "Reftek 130 Datalogger|627252000.0|0.03|M/S|0.0|"
             "2013-11-23T00:00:00|2016-12-31T23:59:59"),
        ]
        num_lines_written = 0
        for line in expected:
            self.assertIn(line, content)
            num_lines_written = num_lines_written + 1
        # assert that the number of lines written equals
        # the number of lines expected
        self.assertEqual(num_lines_written, len(expected))

        # STATION level test
        stio = io.StringIO()
        test_inv.write(stio, format="STATIONTXT", level="STATION")
        # check contents
        content = stio.getvalue()
        expected = [
            ("Network|Station|Latitude|Longitude|"
             "Elevation|SiteName|StartTime|EndTime"),
            ("IU|ANMO|34.9459|-106.4572|1850.0||"),
            ("6E|SH01|37.7457|-88.1368|126.0||"),
        ]
        num_lines_written = 0
        for line in expected:
            self.assertIn(line, content)
            num_lines_written = num_lines_written + 1
        # assert that the number of lines written equals
        # the number of lines expected
        self.assertEqual(num_lines_written, len(expected))

        # NETWORK level test
        stio = io.StringIO()
        test_inv.write(stio, format="STATIONTXT", level="NETWORK")
        # check contents
        content = stio.getvalue()
        expected = [
            ("Network|Description|StartTime|EndTime|TotalStations"),
            ("IU|Global Seismograph Network (GSN - IRIS/USGS)|"
             "1988-01-01T00:00:00|2500-12-31T23:59:59|1"),
            ("6E|Wabash Valley Seismic Zone|"
             "2013-01-01T00:00:00|2016-12-31T23:59:59|1"),
        ]
        num_lines_written = 0
        for line in expected:
            self.assertIn(line, content)
            num_lines_written = num_lines_written + 1
        # assert that the number of lines written equals
        # the number of lines expected
        self.assertEqual(num_lines_written, len(expected))
Example #8
0
    def test_reading_channel_file(self):
        """
        Test reading a file at the channel level.
        """

        resp_1 = Response(
            instrument_sensitivity=InstrumentSensitivity(frequency=0.02,
                                                         input_units="M/S",
                                                         output_units=None,
                                                         value=4.88233E8))
        resp_2 = Response(
            instrument_sensitivity=InstrumentSensitivity(frequency=0.03,
                                                         input_units="M/S",
                                                         output_units=None,
                                                         value=4.98112E8))
        resp_3 = Response(
            instrument_sensitivity=InstrumentSensitivity(frequency=0.03,
                                                         input_units="M/S",
                                                         output_units=None,
                                                         value=6.27252E8))

        # Manually create an expected Inventory object.
        expected_inv = Inventory(
            source=None,
            networks=[
                Network(
                    code="AK",
                    stations=[
                        Station(
                            code="BAGL",
                            latitude=60.4896,
                            longitude=-142.0915,
                            elevation=1470,
                            channels=[
                                Channel(
                                    code="LHZ",
                                    location_code="",
                                    latitude=60.4896,
                                    longitude=-142.0915,
                                    elevation=1470,
                                    depth=0.0,
                                    azimuth=0.0,
                                    dip=-90.0,
                                    sample_rate=1.0,
                                    sensor=Equipment(
                                        type="Nanometrics Trillium 240 Sec "
                                        "Response sn 400 and a"),
                                    start_date=obspy.UTCDateTime(
                                        "2013-01-01T00:00:00"),
                                    end_date=obspy.UTCDateTime(
                                        "2599-12-31T23:59:59"),
                                    response=resp_1)
                            ]),
                        Station(
                            code="BWN",
                            latitude=64.1732,
                            longitude=-149.2991,
                            elevation=356.0,
                            channels=[
                                Channel(
                                    code="LHZ",
                                    location_code="",
                                    latitude=64.1732,
                                    longitude=-149.2991,
                                    elevation=356.0,
                                    depth=0.0,
                                    azimuth=0.0,
                                    dip=-90.0,
                                    sample_rate=1.0,
                                    sensor=Equipment(
                                        type="Nanometrics Trillium 240 Sec "
                                        "Response sn 400 and a"),
                                    start_date=obspy.UTCDateTime(
                                        "2010-07-23T00:00:00"),
                                    end_date=obspy.UTCDateTime(
                                        "2014-05-28T23:59:59"),
                                    response=resp_1),
                                Channel(
                                    code="LHZ",
                                    location_code="",
                                    latitude=64.1732,
                                    longitude=-149.2991,
                                    elevation=356.0,
                                    depth=1.5,
                                    azimuth=0.0,
                                    dip=-90.0,
                                    sample_rate=1.0,
                                    sensor=Equipment(
                                        type="Nanometrics Trillium 120 Sec "
                                        "Response/Quanterra 33"),
                                    start_date=obspy.UTCDateTime(
                                        "2014-08-01T00:00:00"),
                                    end_date=obspy.UTCDateTime(
                                        "2599-12-31T23:59:59"),
                                    response=resp_2)
                            ])
                    ]),
                Network(
                    code="AZ",
                    stations=[
                        Station(
                            code="BZN",
                            latitude=33.4915,
                            longitude=-116.667,
                            elevation=1301.0,
                            channels=[
                                Channel(
                                    code="LHZ",
                                    location_code="",
                                    latitude=33.4915,
                                    longitude=-116.667,
                                    elevation=1301.0,
                                    depth=0.0,
                                    azimuth=0.0,
                                    dip=-90.0,
                                    sample_rate=1.0,
                                    sensor=Equipment(
                                        type=
                                        "Streckeisen STS-2 G1/Quanterra 330 "
                                        "Linear Phase Be"),
                                    start_date=obspy.UTCDateTime(
                                        "2010-07-26T17:22:00"),
                                    end_date=obspy.UTCDateTime(
                                        "2013-07-15T21:22:23"),
                                    response=resp_3),
                                Channel(
                                    code="LHZ",
                                    location_code="",
                                    latitude=33.4915,
                                    longitude=-116.667,
                                    elevation=1301.0,
                                    depth=0.0,
                                    azimuth=0.0,
                                    dip=-90.0,
                                    sample_rate=1.0,
                                    sensor=Equipment(
                                        type=
                                        "Streckeisen STS-2 G1/Quanterra 330 "
                                        "Linear Phase Be"),
                                    start_date=obspy.UTCDateTime(
                                        "2013-07-15T21:22:23"),
                                    end_date=obspy.UTCDateTime(
                                        "2013-10-22T19:30:00"),
                                    response=resp_3),
                                Channel(
                                    code="LHZ",
                                    location_code="",
                                    latitude=33.4915,
                                    longitude=-116.667,
                                    elevation=1301.0,
                                    depth=0.0,
                                    azimuth=0.0,
                                    dip=-90.0,
                                    sample_rate=1.0,
                                    sensor=Equipment(
                                        type=
                                        "Streckeisen STS-2 G1/Quanterra 330 "
                                        "Linear Phase Be"),
                                    start_date=obspy.UTCDateTime(
                                        "2013-10-22T19:30:00"),
                                    end_date=obspy.UTCDateTime(
                                        "2599-12-31T23:59:59"),
                                    response=resp_3)
                            ])
                    ])
            ])

        # Read from a filename.
        filename = os.path.join(self.data_dir, "channel_level_fdsn.txt")
        inv = read_fdsn_station_text_file(filename)
        inv_obs = obspy.read_inventory(filename)

        # Copy creation date as it will be slightly different otherwise.
        inv.created = expected_inv.created
        inv_obs.created = expected_inv.created
        self.assertEqual(inv, expected_inv)
        self.assertEqual(inv_obs, expected_inv)

        # Read from open file in text mode.
        with open(filename, "rt", encoding="utf8") as fh:
            inv = read_fdsn_station_text_file(fh)
            fh.seek(0, 0)
            inv_obs = obspy.read_inventory(fh)
        inv.created = expected_inv.created
        inv_obs.created = expected_inv.created
        self.assertEqual(inv, expected_inv)
        self.assertEqual(inv_obs, expected_inv)

        # Read from open file in binary mode.
        with open(filename, "rb") as fh:
            inv = read_fdsn_station_text_file(fh)
            fh.seek(0, 0)
            inv_obs = obspy.read_inventory(fh)
        inv.created = expected_inv.created
        inv_obs.created = expected_inv.created
        self.assertEqual(inv, expected_inv)
        self.assertEqual(inv_obs, expected_inv)

        # Read from StringIO.
        with open(filename, "rt", encoding="utf8") as fh:
            with io.StringIO(fh.read()) as buf:
                buf.seek(0, 0)
                inv = read_fdsn_station_text_file(buf)
                buf.seek(0, 0)
                inv_obs = obspy.read_inventory(buf)
        inv.created = expected_inv.created
        inv_obs.created = expected_inv.created
        self.assertEqual(inv, expected_inv)
        self.assertEqual(inv_obs, expected_inv)

        # Read from BytesIO.
        with open(filename, "rb") as fh:
            with io.BytesIO(fh.read()) as buf:
                buf.seek(0, 0)
                inv = read_fdsn_station_text_file(buf)
                buf.seek(0, 0)
                inv_obs = obspy.read_inventory(buf)
        inv.created = expected_inv.created
        inv_obs.created = expected_inv.created
        self.assertEqual(inv, expected_inv)
        self.assertEqual(inv_obs, expected_inv)