Exemple #1
0
class Blockette012(Blockette):
    """
    Blockette 012: Volume Time Span Index Blockette.

    This blockette forms an index to the time spans that encompass the actual
    data. One index entry exists for each time span recorded later in the
    volume. Time spans are not used for field station type volumes. There
    should be one entry in this index for each time span control header.
    (For more information, see the notes for blockettes [70], [73], and [74].)

    Sample:
    012006300011992,001,00:00:00.0000~1992,002,00:00:00.0000~000014
    """

    id = 12
    name = "Volume Timespan Index"
    fields = [
        Integer(3, "Number of spans in table", 4),
        # REPEAT fields 4 — 6 for the Number of spans in table:
        Loop("Timespan",
             "Number of spans in table", [
                 VariableString(4, "Beginning of span", 1, 22, 'T'),
                 VariableString(5, "End of span", 1, 22, 'T'),
                 Integer(
                     6, "Sequence number of time span header", 6, ignore=True)
             ],
             optional=True),
    ]
Exemple #2
0
class Blockette030(Blockette):
    """
    Blockette 030: Data Format Dictionary Blockette.

    All volumes, with the exception of miniSEED data records, must have a Data
    Format Dictionary Blockette [30]. Each Channel Identifier Blockette [52]
    has a reference (field 16) back to a Data Format Dictionary Blockette
    [30], so that SEED reading programs will know how to decode data for the
    channels. Because every kind of data format requires an entry in the Data
    Format Dictionary Blockette [30], each recording network needs to list
    entries for each data format, if a heterogeneous mix of data formats are
    included in a volume. This data format dictionary is used to decompress
    the data correctly.

    Sample:
    0300086CDSN Gain-Ranged Format~000200104M0~W2 D0-13 A-8191~D1415~
    P0:#0,1:#2,2:#4,3:#7~
    """

    id = 30
    name = "Data Format Dictionary"
    fields = [
        VariableString(3, "Short descriptive name", 1, 50, 'UNLPS'),
        Integer(4, "Data format identifier code", 4),
        Integer(5, "Data family type", 3),
        Integer(6, "Number of decoder keys", 2),
        # REPEAT field 7 for the Number of decoder keys:
        Loop("Decoder keys",
             "Number of decoder keys",
             [VariableString(7, "Decoder keys", flags='UNLPS')],
             omit_tag=True),
    ]
Exemple #3
0
class Blockette011(Blockette):
    """
    Blockette 011: Volume Station Header Index Blockette.

    This is the index to the Station Identifier Blockettes [50] that appear
    later in the volume. This blockette refers to each station described in
    the station header section.

    Sample:
    0110054004AAK  000003ANMO 000007ANTO 000010BJI  000012
    """

    id = 11
    name = "Volume Station Header Index"
    fields = [
        Integer(3, "Number of stations", 3),
        # REPEAT fields 4 — 5 for the Number of stations:
        Loop(
            "Station identifier",
            "Number of stations", [
                FixedString(4, "Station identifier code", 5),
                Integer(5, "Sequence number of station header", 6, ignore=True)
            ],
            repeat_title=True)
    ]
Exemple #4
0
class Blockette061(Blockette):
    """
    Blockette 061: FIR Response Blockette.

    The FIR blockette is used to specify FIR (Finite Impulse Response) digital
    filter coefficients. It is an alternative to blockette [54] when
    specifying FIR filters. The blockette recognizes the various forms of
    filter symmetry and can exploit them to reduce the number of factors
    specified to the blockette. In July 2007, the FDSN adopted a convention
    that requires the coefficients to be listed in forward time order.
    As a reference, minimum-phase filters (which are asymmetric) should be
    written with the largest values near the beginning of the coefficient list.
    """

    id = 61
    name = "FIR Response"
    fields = [
        Integer(3, "Stage sequence number", 2),
        VariableString(4, "Response Name", 1, 25, 'UN_'),
        FixedString(5, "Symmetry Code", 1, 'U'),
        Integer(6, "Signal In Units", 3, xpath=34),
        Integer(7, "Signal Out Units", 3, xpath=34),
        Integer(8, "Number of Coefficients", 4),
        #REPEAT field 9 for the Number of Coefficients
        Loop("FIR Coefficient",
             "Number of Coefficients",
             [Float(9, "FIR Coefficient", 14, mask='%+1.7e')],
             flat=True),
    ]

    def getRESP(self, station, channel, abbreviations):
        """
        Returns RESP string.
        """
        out = RESP % (station, channel, self.stage_sequence_number,
                      self.symmetry_code,
                      LookupCode(abbreviations, 34, 'unit_name',
                                 'unit_lookup_code', self.signal_in_units),
                      LookupCode(abbreviations, 34, 'unit_description',
                                 'unit_lookup_code', self.signal_in_units),
                      LookupCode(abbreviations, 34, 'unit_name',
                                 'unit_lookup_code', self.signal_out_units),
                      LookupCode(abbreviations, 34, 'unit_description',
                                 'unit_lookup_code', self.signal_out_units),
                      self.number_of_coefficients)
        if self.number_of_coefficients > 1:
            out += '#\t\tNumerator coefficients:\n'
            out += '#\t\t  i, coefficient\n'
            for _i in range(self.number_of_coefficients):
                out += 'B061F09    %4s %13s\n' % \
                    (_i, formatRESP(self.FIR_coefficient[_i], 6))
        elif self.number_of_coefficients == 1:
            out += '#\t\tNumerator coefficients:\n'
            out += '#\t\t  i, coefficient\n'
            out += 'B061F09    %4s %13s\n' % \
                (0, formatRESP(self.FIR_coefficient, 6))
        out += '#\t\t\n'
        return out.encode()
Exemple #5
0
class Blockette041(Blockette):
    """
    Blockette 041: FIR Dictionary Blockette.

    The FIR blockette is used to specify FIR (Finite Impulse Response)
    digital filter coefficients. It is an alternative to blockette [44] when
    specifying FIR filters. The blockette recognizes the various forms of
    filter symmetry and can exploit them to reduce the number of factors
    specified in the blockette. See Response (Coefficients) Blockette [54]
    for more information.
    """

    id = 41
    name = "FIR Dictionary"
    fields = [
        Integer(3, "Response Lookup Key", 4),
        VariableString(4, "Response Name", 1, 25, 'UN_'),
        FixedString(5, "Symmetry Code", 1, 'U'),
        Integer(6, "Signal In Units", 3, xpath=34),
        Integer(7, "Signal Out Units", 3, xpath=34),
        Integer(8, "Number of Factors", 4),
        #REPEAT field 9 for the Number of Factors
        Loop("FIR Coefficient",
             "Number of Factors",
             [Float(9, "FIR Coefficient", 14, mask='%+1.7e')],
             flat=True),
    ]

    def parseSEED(self, data, expected_length=0):
        """
        If number of FIR coefficients are larger than maximal blockette size of
        9999 chars a follow up blockette with the same blockette id and
        response lookup key is expected - this is checked here.
        """
        # convert to stream for test issues
        if isinstance(data, basestring):
            expected_length = len(data)
            data = StringIO(data)
        # get current lookup key
        pos = data.tell()
        data.read(7)
        global_lookup_key = int(data.read(4))
        data.seek(pos)
        # read first blockette
        temp = StringIO()
        temp.write(data.read(expected_length))
        # check next blockettes
        while True:
            # save position
            pos = data.tell()
            try:
                blockette_id = int(data.read(3))
            except ValueError:
                break
            if blockette_id != 41:
                # different blockette id -> break
                break
            blockette_length = int(data.read(4))
            lookup_key = int(data.read(4))
            if lookup_key != global_lookup_key:
                # different lookup key -> break
                break
            # ok follow up blockette found - skip some unneeded fields
            self.fields[1].read(data)
            self.fields[2].read(data)
            self.fields[3].read(data)
            self.fields[4].read(data)
            self.fields[5].read(data)
            # remaining length in current blockette
            length = pos - data.tell() + blockette_length
            # read follow up blockette and append it to temporary blockette
            temp.write(data.read(length))
        # reposition file pointer
        data.seek(pos)
        # parse new combined temporary blockette
        temp.seek(0)
        Blockette.parseSEED(self, temp, expected_length=temp.len)

    def parseXML(self, xml_doc, *args, **kwargs):
        if self.xseed_version == '1.0':
            xml_doc.find('fir_coefficient').tag = 'FIR_coefficient'
        Blockette.parseXML(self, xml_doc, *args, **kwargs)

    def getXML(self, *args, **kwargs):
        xml = Blockette.getXML(self, *args, **kwargs)
        if self.xseed_version == '1.0':
            xml.find('FIR_coefficient').tag = 'fir_coefficient'
        return xml

    def getRESP(self, station, channel, abbreviations):
        """
        Returns RESP string.
        """
        string = \
        '#\t\t+                     +--------------------------------+' + \
        '                      +\n' + \
        '#\t\t+                     |   FIR response,' + \
        '%6s ch %s   |                      +\n' % (station, channel) + \
        '#\t\t+                     +--------------------------------+' + \
        '                      +\n' + \
        '#\t\t\n' + \
        'B041F05     Symmetry type:                         %s\n' \
                % self.symmetry_code + \
        'B041F06     Response in units lookup:              %s - %s\n'\
            % (LookupCode(abbreviations, 34, 'unit_name', 'unit_lookup_code',
                         self.signal_in_units),
              LookupCode(abbreviations, 34, 'unit_description',
                    'unit_lookup_code', self.signal_in_units)) + \
        'B041F07     Response out units lookup:             %s - %s\n'\
            % (LookupCode(abbreviations, 34, 'unit_name', 'unit_lookup_code',
                         self.signal_out_units),
              LookupCode(abbreviations, 34, 'unit_description',
                    'unit_lookup_code', self.signal_out_units)) + \
        'B041F08     Number of numerators:                  %s\n' \
                % self.number_of_factors
        if self.number_of_factors > 1:
            string += '#\t\tNumerator coefficients:\n' + \
                       '#\t\t  i, coefficient\n'
            for _i in xrange(self.number_of_factors):
                string += 'B041F09    %4s %13s\n' \
                            % (_i, formatRESP(self.FIR_coefficient[_i], 6))
        elif self.number_of_factors == 1:
            string += '#\t\tNumerator coefficients:\n' + \
                       '#\t\t  i, coefficient\n'
            string += 'B041F09    %4s %13s\n' \
                            % (0, formatRESP(self.FIR_coefficient, 6))
        string += '#\t\t\n'
        return string
Exemple #6
0
class Blockette043(Blockette):
    """
    Blockette 043: Response (Poles & Zeros) Dictionary Blockette.

    See Response (Poles & Zeros) Blockette [53] for more information.
    """

    id = 43
    name = "Response Poles and Zeros Dictionary"
    fields = [
        Integer(3, "Response Lookup Key", 4),
        VariableString(4, "Response Name", 1, 25, 'UN_'),
        FixedString(5, "Response type", 1, 'U'),
        Integer(6, "Stage signal input units", 3, xpath=34),
        Integer(7, "Stage signal output units", 3, xpath=34),
        Float(8, "A0 normalization factor", 12, mask='%+1.5e'),
        Float(9, "Normalization frequency", 12, mask='%+1.5e'),
        Integer(10, "Number of complex zeros", 3),
        # REPEAT fields 11 — 14 for the Number of complex zeros:
        Loop('Complex zero', "Number of complex zeros", [
            Float(11, "Real zero", 12, mask='%+1.5e'),
            Float(12, "Imaginary zero", 12, mask='%+1.5e'),
            Float(13, "Real zero error", 12, mask='%+1.5e'),
            Float(14, "Imaginary zero error", 12, mask='%+1.5e')
        ]),
        Integer(15, "Number of complex poles", 3),
        # REPEAT fields 16 — 19 for the Number of complex poles:
        Loop('Complex pole', "Number of complex poles", [
            Float(16, "Real pole", 12, mask='%+1.5e'),
            Float(16, "Imaginary pole", 12, mask='%+1.5e'),
            Float(18, "Real pole error", 12, mask='%+1.5e'),
            Float(19, "Imaginary pole error", 12, mask='%+1.5e')
        ])
    ]

    # Changes the name of the blockette because of an error in XSEED 1.0
    def getXML(self, *args, **kwargs):
        xml = Blockette.getXML(self, *args, **kwargs)
        if self.xseed_version == '1.0':
            xml.tag = 'response_poles_and_zeros'
        return xml

    def getRESP(self, station, channel, abbreviations):
        """
        Returns RESP string.
        """
        # Field five needs some extra parsing.
        field_five_dict = {
            'A': 'A [Laplace Transform (Rad/sec)]',
            'B': 'B [Analog (Hz)]',
            'C': 'C [Composite]',
            'D': 'D [Digital (Z-transform)]'
        }
        string = \
            '#\t\t+               ' + \
            '+-----------------------------------------' + \
            '---+                +\n' + \
            '#\t\t+               |   Response (Poles & Zeros),' + \
            '%6s ch %s   |                +\n' % (station, channel) + \
            '#\t\t+               ' + \
            '+-----------------------------------------' + \
            '---+                +\n' + \
            '#\t\t\n' + \
            'B043F05     Response type:                         %s\n' \
            % field_five_dict[self.response_type] + \
            'B043F06     Response in units lookup:              %s\n' \
            % Blockette34Lookup(abbreviations,
                                self.stage_signal_input_units) + \
            'B043F07     Response out units lookup:             %s\n' \
            % Blockette34Lookup(abbreviations,
                                self.stage_signal_output_units) + \
            'B043F08     A0 normalization factor:               %G\n'\
            % self.A0_normalization_factor + \
            'B043F09     Normalization frequency:               %G\n'\
            % self.normalization_frequency + \
            'B043F10     Number of zeroes:                      %s\n'\
            % self.number_of_complex_zeros + \
            'B043F15     Number of poles:                       %s\n'\
            % self.number_of_complex_poles + \
            '#\t\tComplex zeroes:\n' + \
            '#\t\t  i  real          imag          real_error    imag_error\n'
        if self.number_of_complex_zeros > 0:
            if self.number_of_complex_zeros != 1:
                # Loop over all zeros.
                for _i in range(self.number_of_complex_zeros):
                    string += 'B043F11-14 %4s %13s %13s %13s %13s\n' % (
                        _i, formatRESP(self.real_zero[_i], 6),
                        formatRESP(self.imaginary_zero[_i],
                                   6), formatRESP(self.real_zero_error[_i], 6),
                        formatRESP(self.imaginary_zero_error[_i], 6))
            else:
                string += 'B043F11-14 %4s %13s %13s %13s %13s\n' % (
                    0, formatRESP(self.real_zero,
                                  6), formatRESP(self.imaginary_zero, 6),
                    formatRESP(self.real_zero_error,
                               6), formatRESP(self.imaginary_zero_error, 6))
        string += '#\t\tComplex poles:\n' + \
            '#\t\t  i  real          imag          real_error    imag_error\n'
        if self.number_of_complex_poles > 0:
            if self.number_of_complex_poles != 1:
                # Loop over all poles.
                for _i in range(self.number_of_complex_poles):
                    string += 'B043F16-19 %4s %13s %13s %13s %13s\n' % (
                        _i, formatRESP(self.real_pole[_i], 6),
                        formatRESP(self.imaginary_pole[_i],
                                   6), formatRESP(self.real_pole_error[_i], 6),
                        formatRESP(self.imaginary_pole_error[_i], 6))
            else:
                string += 'B043F16-19 %4s %13s %13s %13s %13s\n' % (
                    0, formatRESP(self.real_pole,
                                  6), formatRESP(self.imaginary_pole, 6),
                    formatRESP(self.real_pole_error,
                               6), formatRESP(self.imaginary_pole_error, 6))
        string += '#\t\t\n'
        return string
Exemple #7
0
class Blockette058(Blockette):
    """
    Blockette 058: Channel Sensitivity/Gain Blockette.

    When used as a gain (stage ≠ 0), this blockette is the gain for this stage
    at the given frequency. Different stages may be at different frequencies.
    However, it is strongly recommended that the same frequency be used in all
    stages of a cascade, if possible. When used as a sensitivity(stage=0),
    this blockette is the sensitivity (in counts per ground motion) for the
    entire channel at a given frequency, and is also referred to as the
    overall gain. The frequency here may be different from the frequencies in
    the gain specifications, but should be the same if possible. If you use
    cascading (more than one filter stage), then SEED requires a gain for each
    stage. A final sensitivity (Blockette [58], stage = 0, is required. If you
    do not use cascading (only one stage), then SEED must see a gain, a
    sensitivity, or both.

    Sample:
    0580035 3 3.27680E+03 0.00000E+00 0
    """

    id = 58
    name = "Channel Sensitivity Gain"
    fields = [
        Integer(3, "Stage sequence number", 2),
        Float(4, "Sensitivity gain", 12, mask='%+1.5e'),
        Float(5, "Frequency", 12, mask='%+1.5e'),
        Integer(6, "Number of history values", 2),
        # REPEAT fields 7 — 9 for the Number of history values:
        Loop('History', "Number of history values", [
            Float(7, "Sensitivity for calibration", 12, mask='%+1.5e'),
            Float(8, "Frequency of calibration sensitivity", 12,
                  mask='%+1.5e'),
            VariableString(9, "Time of above calibration", 1, 22, 'T')
        ])
    ]

    def getRESP(self, station, channel, abbreviations):
        """
        Returns RESP string.
        """
        # This blockette can result in two different RESPs.
        blkt_type = self.stage_sequence_number
        if blkt_type != 0:
            string = \
                '#\t\t+                  +-------------------------------' + \
                '--------+                  +\n' + \
                '#\t\t+                  |       Channel Gain,' + \
                '%6s ch %s      |                  +\n' % (station, channel) +\
                '#\t\t+                  +-------------------------------' + \
                '--------+                  +\n'
        else:
            string = \
                '#\t\t+                  +--------------------------------' + \
                '-------+                  +\n' + \
                '#\t\t+                  |   Channel Sensitivity,' + \
                '%6s ch %s   |                  +\n' % (station, channel) + \
                '#\t\t+                  +--------------------------------' + \
                '-------+                  +\n'
        string += '#\t\t\n' + \
            'B058F03     Stage sequence number:                 %s\n' \
            % blkt_type
        if blkt_type != 0:
            string += \
                'B058F04     Gain:                                  %s\n' \
                % formatRESP(self.sensitivity_gain, 6) + \
                'B058F05     Frequency of gain:                     %s HZ\n' \
                % formatRESP(self.frequency, 6)
        else:
            string += \
                'B058F04     Sensitivity:                           %s\n' \
                % formatRESP(self.sensitivity_gain, 6) + \
                'B058F05     Frequency of sensitivity:              %s HZ\n' \
                % formatRESP(self.frequency, 6)
        string += \
            'B058F06     Number of calibrations:                %s\n' \
            % self.number_of_history_values
        if self.number_of_history_values > 1:
            string += \
                '#\t\tCalibrations:\n' + \
                '#\t\t i, sensitivity, frequency, time of calibration\n'
            for _i in xrange(self.number_of_history_values):
                string += \
                    'B058F07-08   %2s %13s %13s %s\n' \
                    % (formatRESP(self.sensitivity_for_calibration[_i], 6),
                       formatRESP(
                           self.frequency_of_calibration_sensitivity[_i], 6),
                        self.time_of_above_calibration[_i].formatSEED())
        elif self.number_of_history_values == 1:
            string += \
                '#\t\tCalibrations:\n' + \
                '#\t\t i, sensitivity, frequency, time of calibration\n' + \
                'B058F07-08    0 %13s %13s %s\n' \
                % (formatRESP(self.sensitivity_for_calibration, 6),
                   formatRESP(self.frequency_of_calibration_sensitivity, 6),
                   self.time_of_above_calibration.formatSEED())
        string += '#\t\t\n'
        return string
Exemple #8
0
class Blockette044(Blockette):
    """
    Blockette 044: Response (Coefficients) Dictionary Blockette.

    See Response (Coefficients) Dictionary Blockette [54] for more information.
    """

    id = 44
    name = "Response Coefficients Dictionary"
    fields = [
        Integer(3, "Response Lookup Key", 4),
        VariableString(4, "Response Name", 1, 25, 'UN_'),
        FixedString(5, "Response type", 1, 'U'),
        Integer(6, "Signal input units", 3, xpath=34),
        Integer(7, "Signal output units", 3, xpath=34),
        Integer(8, "Number of numerators", 4),
        # REPEAT fields 9 - 10 for the Number of numerators:
        Loop('Numerators',
             "Number of numerators", [
                 Float(9, "Numerator coefficient", 12, mask='%+1.5e'),
                 Float(10, "Numerator error", 12, mask='%+1.5e')
             ],
             flat=True),
        Integer(11, "Number of denominators", 4),
        # REPEAT fields 12 — 13 for the Number of denominators:
        Loop('Denominators',
             "Number of denominators", [
                 Float(12, "Denominator coefficient", 12, mask='%+1.5e'),
                 Float(13, "Denominator error", 12, mask='%+1.5e')
             ],
             flat=True)
    ]

    # Changes the name of the blockette because of an error in XSEED 1.0
    def getXML(self, *args, **kwargs):
        xml = Blockette.getXML(self, *args, **kwargs)
        if self.xseed_version == '1.0':
            xml.tag = 'response_coefficients'
        return xml

    def getRESP(self, station, channel, abbreviations):
        """
        Returns RESP string.
        """
        string = \
            '#\t\t+               +----------------------------------------' +\
            '---+                 +\n' + \
            '#\t\t+               |   Response (Coefficients),' + \
            '%6s ch %s   |                 +\n' % (station, channel) + \
            '#\t\t+               +----------------------------------------' +\
            '---+                 +\n' + \
            '#\t\t\n' + \
            'B044F05     Response type:                         %s\n' \
            % self.response_type + \
            'B044F06     Response in units lookup:              %s\n'\
            % Blockette34Lookup(abbreviations, self.signal_input_units) + \
            'B044F07     Response out units lookup:             %s\n'\
            % Blockette34Lookup(abbreviations, self.signal_output_units) + \
            'B044F08     Number of numerators:                  %s\n' \
            % self.number_of_numerators + \
            'B044F11     Number of denominators:                %s\n' \
            % self.number_of_denominators + \
            '#\t\tNumerator coefficients:\n' + \
            '#\t\t  i, coefficient,  error\n'
        if self.number_of_numerators:
            string += \
                '#\t\tNumerator coefficients:\n' + \
                '#\t\t  i, coefficient,  error\n'
            if self.number_of_numerators > 1:
                # Loop over all zeros.
                for _i in range(self.number_of_numerators):
                    string += 'B044F09-10  %3s %13s %13s\n' % (
                        _i, formatRESP(self.numerator_coefficient[_i], 6),
                        formatRESP(self.numerator_error[_i], 6))
            else:
                string += 'B044F09-10  %3s %13s %13s\n' % (
                    0, formatRESP(self.numerator_coefficient,
                                  6), formatRESP(self.numerator_error, 6))
        if self.number_of_denominators:
            string += \
                '#\t\tDenominator coefficients:\n' + \
                '#\t\t i, coefficient, error\n'
            if self.number_of_denominators > 1:
                # Loop over all zeros.
                for _i in range(self.number_of_numerators):
                    string += 'B044F12-13  %3s %13s %13s\n' % (
                        _i, formatRESP(self.denominator_coefficient[_i], 6),
                        formatRESP(self.denominator_error[_i], 6))
            else:
                string += 'B044F12-13  %3s %13s %13s\n' % (
                    0, formatRESP(self.denominator_coefficient,
                                  6), formatRESP(self.denominator_error, 6))
        string += '#\t\t\n'
        return string
Exemple #9
0
class Blockette055(Blockette):
    """
    Blockette 055: Response List Blockette.

    This blockette alone is not an acceptable response description; always use
    this blockette along with the standard response blockettes ([53], [54],
    [57], or [58]). If this is the only response available, we strongly
    recommend that you derive the appropriate poles and zeros and include
    blockette 53 and blockette 58.
    """

    id = 55
    # Typo is itentional.
    name = "Response list"
    fields = [
        Integer(3, "Stage sequence number", 2),
        Integer(4, "Stage input units", 3, xpath=34),
        Integer(5, "Stage output units", 3, xpath=34),
        Integer(6, "Number of responses listed", 4),
        # REPEAT fields 7 — 11 for the Number of responses listed:
        Loop('Response', "Number of responses listed", [
            Float(7, "Frequency", 12, mask='%+1.5e'),
            Float(8, "Amplitude", 12, mask='%+1.5e'),
            Float(9, "Amplitude error", 12, mask='%+1.5e'),
            Float(10, "Phase angle", 12, mask='%+1.5e'),
            Float(11, "Phase error", 12, mask='%+1.5e')
        ], repeat_title=True)
    ]

    # Changes the name of the blockette because of an error in XSEED 1.0
    def getXML(self, *args, **kwargs):
        xml = Blockette.getXML(self, *args, **kwargs)
        if self.xseed_version == '1.0':
            xml.tag = 'reponse_list'
        return xml

    def getRESP(self, station, channel, abbreviations):
        """
        Returns RESP string.
        """
        string = \
            '#\t\t+                     +---------------------------------+' +\
            '                     +\n' + \
            '#\t\t+                     |   Response List,%6s ch %s   |' + \
            '                     +\n' % (station, channel) + \
            '#\t\t+                     +---------------------------------+' +\
            '                     +\n' + \
            '#\t\t\n' + \
            'B055F03     Stage sequence number:                 %s\n' \
            % self.stage_sequence_number + \
            'B055F04     Response in units lookup:              %s\n' \
            % Blockette34Lookup(abbreviations, self.stage_input_units) + \
            'B055F05     Response out units lookup:             %s\n' \
            % Blockette34Lookup(abbreviations, self.stage_output_units) + \
            'B055F06     Number of responses:                   %s\n' \
            % self.number_of_responses_listed
        if self.number_of_responses_listed:
            string += \
                '#\t\tResponses:\n' + \
                '#\t\t  frequency\t amplitude\t amp error\t    ' + \
                'phase\t phase error\n'
            if self.number_of_responses_listed > 1:
                for _i in range(self.number_of_responses_listed):
                    string += 'B055F07-11  %s\t%s\t%s\t%s\t%s\n' % \
                        (formatRESP(self.frequency[_i], 6),
                         formatRESP(self.amplitude[_i], 6),
                         formatRESP(self.amplitude_error[_i], 6),
                         formatRESP(self.phase_angle[_i], 6),
                         formatRESP(self.phase_error[_i], 6))
            else:
                string += 'B055F07-11  %s\t%s\t%s\t%s\t%s\n' % \
                    (formatRESP(self.frequency, 6),
                     formatRESP(self.amplitude, 6),
                     formatRESP(self.amplitude_error, 6),
                     formatRESP(self.phase_angle, 6),
                     formatRESP(self.phase_error, 6))
        string += '#\t\t\n'
        return string
Exemple #10
0
class Blockette054(Blockette):
    """
    Blockette 054: Response (Coefficients) Blockette.

    This blockette is usually used only for finite impulse response (FIR)
    filter stages. You can express Laplace transforms this way, but you should
    use the Response (Poles & Zeros) Blockettes [53] for this. You can express
    IIR filters this way, but you should use the Response (Poles & Zeros)
    Blockette [53] here, too, to avoid numerical stability problems. Usually,
    you will follow this blockette with a Decimation Blockette [57] and a
    Sensitivity/Gain Blockette [58] to complete the definition of the filter
    stage.

    This blockette is the only blockette that might overflow the maximum
    allowed value of 9,999 characters. If there are more coefficients than fit
    in one record, list as many as will fit in the first occurrence of this
    blockette (the counts of Number of numerators and Number of denominators
    would then be set to the number included, not the total number). In the
    next record, put the remaining number. Be sure to write and read these
    blockettes in sequence, and be sure that the first few fields of both
    records are identical. Reading (and writing) programs have to be able to
    work with both blockettes as one after reading (or before writing). In
    July 2007, the FDSN adopted a convention that requires the coefficients to
    be listed in forward time order. As a reference, minimum-phase filters
    (which are asymmetric) should be written with the largest values near the
    beginning of the coefficient list.
    """

    id = 54
    name = "Response Coefficients"
    fields = [
        FixedString(3, "Response type", 1, 'U'),
        Integer(4, "Stage sequence number", 2),
        Integer(5, "Signal input units", 3, xpath=34),
        Integer(6, "Signal output units", 3, xpath=34),
        Integer(7, "Number of numerators", 4),
        # REPEAT fields 8 — 9 for the Number of numerators:
        Loop('Numerators',
             "Number of numerators", [
                 Float(8, "Numerator coefficient", 12, mask='%+1.5e'),
                 Float(9, "Numerator error", 12, mask='%+1.5e')
             ],
             flat=True),
        Integer(10, "Number of denominators", 4),
        # REPEAT fields 11 — 12 for the Number of denominators:
        Loop('Denominators',
             "Number of denominators", [
                 Float(11, "Denominator coefficient", 12, mask='%+1.5e'),
                 Float(12, "Denominator error", 12, mask='%+1.5e')
             ],
             flat=True)
    ]

    def getRESP(self, station, channel, abbreviations):
        """
        Returns RESP string.
        """
        string = \
            '#\t\t+               +----------------------------------------' +\
            '---+                 +\n' + \
            '#\t\t+               |   Response (Coefficients),' + \
            '%6s ch %s   |                 +\n' % (station, channel) + \
            '#\t\t+               +----------------------------------------' +\
            '---+                 +\n' + \
            '#\t\t\n' + \
            'B054F03     Transfer function type:                %s\n' \
            % self.response_type + \
            'B054F04     Stage sequence number:                 %s\n' \
            % self.stage_sequence_number + \
            'B054F05     Response in units lookup:              %s\n'\
            % Blockette34Lookup(abbreviations, self.signal_input_units) +\
            'B054F06     Response out units lookup:             %s\n'\
            % Blockette34Lookup(abbreviations, self.signal_output_units) +\
            'B054F07     Number of numerators:                  %s\n' \
            % self.number_of_numerators + \
            'B054F10     Number of denominators:                %s\n' \
            % self.number_of_denominators
        if self.number_of_numerators:
            string += \
                '#\t\tNumerator coefficients:\n' + \
                '#\t\t  i, coefficient,  error\n'
            if self.number_of_numerators > 1:
                # Loop over all zeros.
                for _i in range(self.number_of_numerators):
                    string += 'B054F08-09  %3s %13s %13s\n' % (
                        _i, formatRESP(self.numerator_coefficient[_i], 6),
                        formatRESP(self.numerator_error[_i], 6))
            else:
                string += 'B054F08-09  %3s %13s %13s\n' % (
                    0, formatRESP(self.numerator_coefficient,
                                  6), formatRESP(self.numerator_error, 6))
        if self.number_of_denominators:
            string += \
                '#\t\tDenominator coefficients:\n' + \
                '#\t\t i, coefficient, error\n'
            if self.number_of_denominators > 1:
                # Loop over all zeros.
                for _i in range(self.number_of_numerators):
                    string += 'B054F11-12  %3s %13s %13s\n' % (
                        _i, formatRESP(self.denominator_coefficient[_i], 6),
                        formatRESP(self.denominator_error[_i], 6))
            else:
                string += 'B054F11-12  %3s %13s %13s\n' % (
                    0, formatRESP(self.denominator_coefficient,
                                  6), formatRESP(self.denominator_error, 6))
        string += '#\t\t\n'
        return string.encode()
Exemple #11
0
class Blockette060(Blockette):
    def __init__(self, *args, **kwargs):  # @UnusedVariable
        """
        """
        self.stages = []
        super(Blockette060, self).__init__()

    """
    Blockette 060: Response Reference Blockette.

    Use this blockette whenever you want to replace blockettes [53] through
    [58] and [61] with their dictionary counterparts, blockettes [43] through
    [48] and [41]. We recommend placing responses in stage order, even if this
    means using more than one Response Reference Blockette [60].

    Here is an example:
        Stage 1:    Response (Poles & Zeros) Blockette [53]
                    Channel Sensitivity/Gain Blockette [58]
                    First response reference blockette:
                    Response Reference Blockette [60]
        Stage 2:        [44] [47] [48]
        Stage 3:        [44] [47] [48]
        Stage 4:        [44] [47]
                        Channel Sensitivity/Gain Blockette [58]
        Stage 5:    Response (Coefficients) Blockette [54]
                    (End of first response reference blockette)
                    Second response reference blockette:
                    Response Reference Blockette [60]
        Stage 5         (continued): [47] [48]
        Stage 6:        [44] [47] [48]
                    (End of second response reference blockette)

    Substitute Response Reference Blockette [60] anywhere the original
    blockette would go, but be sure to place it in the same position as the
    original would have gone. (Note that this blockette uses a repeating field
    (response reference) within another repeating field (stage value). This is
    the only blockette in the current version (2.1) that has this "two
    dimensional" structure.)
    """

    id = 60
    name = "Response Reference Blockette"
    fields = [
        Integer(3, "Number of stages", 2),
        # REPEAT field 4, with appropriate fields 5 and 6, for each stage
        Loop(
            "FIR Coefficient",
            "Number of stages",
            [
                Integer(4, "Stage sequence number", 2),
                Integer(5, "Number of responses", 2),
                # REPEAT field 6, one for each response within each stage:
                Loop("Response lookup key",
                     "Number of responses",
                     [Integer(6, "Response lookup key", 4)],
                     omit_tag=True),
            ]),
    ]

    def parseSEED(self, data, length=0, *args, **kwargs):  # @UnusedVariable
        """
        Read Blockette 60.
        """
        # convert to stream for test issues
        if isinstance(data, bytes):
            length = len(data)
            data = io.BytesIO(data)
        elif isinstance(data, (str, native_str)):
            raise TypeError("data must be bytes, not string")
        new_data = data.read(length)
        new_data = new_data[7:]
        number_of_stages = int(new_data[0:2])
        # Loop over all stages.
        counter = 2
        for _i in range(number_of_stages):
            number_of_responses = int(new_data[counter + 2:counter + 4])
            self.stages.append([])
            # Start inner loop
            counter += 4
            for _j in range(number_of_responses):
                # Append to last list.
                self.stages[-1].append(int(new_data[counter:counter + 4]))
                counter += 4

    def getSEED(self, *args, **kwargs):  # @UnusedVariable
        """
        Writes Blockette 60.
        """
        data = ''
        # Write number of stages.
        data += '%2d' % len(self.stages)
        # Loop over all items in self.stages.
        stage_number = 1
        for stage in self.stages:
            # Write stage sequence number.
            data += '%2d' % stage_number
            stage_number += 1
            # Write number of items.
            data += '%2d' % len(stage)
            for number in stage:
                data += '%4d' % number
        # Add header.
        length = len(data) + 7
        header = '060%4d' % length
        data = header + data
        return data

    def getXML(self, xseed_version, *args, **kwargs):  # @UnusedVariable
        """
        Write XML.
        """
        if xseed_version == '1.0':
            msg = 'The xsd-validation file for XML-SEED version 1.0 does ' + \
                  'not support Blockette 60. It will be written but ' + \
                  'please be aware that the file cannot be validated.\n' + \
                  'If you want to validate your file please use XSEED ' + \
                  'version 1.1.\n'
            sys.stdout.write(msg)
        node = Element('response_reference', blockette="060")
        SubElement(node, 'number_of_stages').text = str(len(self.stages))
        # Loop over stages.
        for _i in range(len(self.stages)):
            inner_stage = SubElement(node, 'stage')
            SubElement(inner_stage, 'stage_sequence_number').text = str(_i + 1)
            SubElement(inner_stage, 'number_of_responses').text = \
                str(len(self.stages[_i]))
            for _j in range(len(self.stages[_i])):
                SubElement(inner_stage, 'response_lookup_key').text = \
                    setXPath('dictionary', self.stages[_i][_j])
        return node

    def parseXML(self,
                 xml_doc,
                 version='1.0',
                 *args,
                 **kwargs):  # @UnusedVariable
        """
        Read XML of blockette 60.
        """
        # Loop over ch
        for child in xml_doc.getchildren():
            if child.tag != 'stage':
                continue
            self.stages.append([])
            for inner_child in child.getchildren():
                if inner_child.tag != 'response_lookup_key':
                    continue
                # for legacy support meaning XSEED without XPaths.:
                if inner_child.text.isdigit():
                    self.stages[-1].append(int(inner_child.text))
                else:
                    self.stages[-1].append(getXPath(inner_child.text))

    def getRESP(self, station, channel, abbreviations):
        """
        Returns RESP string.
        """
        string = ''
        # Possible dictionary blockettes.
        dict_blockettes = [41, 43, 44, 45, 46, 47, 48]
        for _i in range(len(self.stages)):
            string += \
                '#\t\t+            +----------------------------------' + \
                '----------------+             +\n' + \
                '#\t\t+            |   Response Reference Information,' + \
                '%6s ch %s   |             +\n' % (station, channel) + \
                '#\t\t+            +----------------------------------' + \
                '----------------+             +\n' + \
                '#\t\t\n' + \
                'B060F03     Number of Stages:                      %s\n' \
                % len(self.stages) + \
                'B060F04     Stage number:                          %s\n' \
                % (_i + 1) + \
                'B060F05     Number of Responses:                   %s\n' \
                % len(self.stages[_i]) + \
                '#\t\t\n'
            # Loop over all keys and print the information in order.
            for response_key in self.stages[_i]:
                # Find the corresponding key in the abbreviations.
                found_abbrev = False
                for blockette in abbreviations:
                    if blockette.id in dict_blockettes and \
                            blockette.response_lookup_key == response_key:
                        try:
                            string += \
                                blockette.getRESP(station, channel,
                                                  abbreviations)
                            found_abbrev = True
                        except AttributeError:
                            msg = 'RESP output not implemented for ' + \
                                  'blockette %d.' % blockette.id
                            raise AttributeError(msg)
                if not found_abbrev:
                    msg = 'The reference blockette for response key ' + \
                          '%d could not be found.' % response_key
                    raise Exception(msg)
        string += '#\t\t\n'
        return string.encode()
Exemple #12
0
class Blockette062(Blockette):
    """
    Blockette 062: Response [Polynomial] Blockette.

    Use this blockette to characterize the response of a non-linear sensor.
    The polynomial response blockette describes the output of an Earth sensor
    in fundamentally a different manner than the other response blockettes.
    The functional describing the sensor for the polynomial response blockette
    will have Earth units while the independent variable of the function will
    be in volts. This is precisely opposite to the other response blockettes.
    While it is a simple matter to convert a linear response to either form,
    the non-linear response (which we can describe in the polynomial
    blockette) would require extensive curve fitting or polynomial inversion
    to convert from one function to the other. Most data users are interested
    in knowing the sensor output in Earth units, and the polynomial response
    blockette facilitates the access to Earth units for sensors with
    non-linear responses.
    """

    id = 62
    name = "Response Polynomial"
    fields = [
        FixedString(3, "Transfer Function Type", 1),
        Integer(4, "Stage Sequence Number", 2),
        Integer(5, "Stage Signal In Units", 3, xpath=34),
        Integer(6, "Stage Signal Out Units", 3, xpath=34),
        FixedString(7, "Polynomial Approximation Type", 1),
        FixedString(8, "Valid Frequency Units", 1),
        Float(9, "Lower Valid Frequency Bound", 12, mask='%+1.5e'),
        Float(10, "Upper Valid Frequency Bound", 12, mask='%+1.5e'),
        Float(11, "Lower Bound of Approximation", 12, mask='%+1.5e'),
        Float(12, "Upper Bound of Approximation", 12, mask='%+1.5e'),
        Float(13, "Maximum Absolute Error", 12, mask='%+1.5e'),
        Integer(14, "Number of Polynomial Coefficients", 3),
        # REPEAT fields 15 and 16 for each polynomial coefficient
        Loop("Polynomial Coefficients", "Number of Polynomial Coefficients", [
            Float(12, "Polynomial Coefficient", 12, mask='%+1.5e'),
            Float(12, "Polynomial Coefficient Error", 12, mask='%+1.5e'),
        ])
    ]

    # Changes the name of the blockette because of an error in XSEED 1.0
    def getXML(self, *args, **kwargs):
        xml = Blockette.getXML(self, *args, **kwargs)
        if self.xseed_version == '1.0':
            msg = 'The xsd-validation file for XML-SEED version 1.0 does ' + \
                'not support Blockette 62. It will be written but ' + \
                'please be aware that the file cannot be validated.\n' + \
                'If you want to validate your file please use XSEED ' + \
                'version 1.1.\n'
            sys.stdout.write(msg)
        return xml

    def getRESP(self, station, channel, abbreviations):
        """
        Returns RESP string.
        """
        # Field three needs some extra parsing.
        field_three_dict = {
            'A': 'A [Laplace Transform (Rad/sec)]',
            'B': 'B [Analog (Hz)]',
            'C': 'C [Composite]',
            'D': 'D [Digital (Z-transform)]',
            'P': 'P [Polynomial]'
        }
        # Frequency too!
        frequency_dict = {'A': 'A [rad/sec]', 'B': 'B [Hz]'}
        # Polynomial Approximation too.
        polynomial_dict = {'M': 'M [MacLaurin]'}
        string = \
            '#\t\t+              +-----------------------' + \
            '----------------+                      +\n' + \
            '#\t\t+              |   Polynomial response,' + \
            '%6s ch %s   |                      +\n' % (station, channel) + \
            '#\t\t+              +-----------------------' + \
            '----------------+                      +\n' + \
            '#\t\t\n' + \
            'B062F03     Transfer function type:                %s\n' \
            % field_three_dict[self.transfer_function_type] + \
            'B062F04     Stage sequence number:                 %s\n' \
            % self.stage_sequence_number + \
            'B062F05     Response in units lookup:              %s\n' \
            % Blockette34Lookup(abbreviations, self.stage_signal_in_units) + \
            'B062F06     Response out units lookup:             %s\n' \
            % Blockette34Lookup(abbreviations, self.stage_signal_out_units) + \
            'B062F07     Polynomial Approximation Type:         %s\n' \
            % polynomial_dict[self.polynomial_approximation_type] + \
            'B062F08     Valid Frequency Units:                 %s\n' \
            % frequency_dict[self.valid_frequency_units] + \
            'B062F09     Lower Valid Frequency Bound:           %G\n' \
            % self.lower_valid_frequency_bound + \
            'B062F10     Upper Valid Frequency Bound:           %G\n' \
            % self.upper_valid_frequency_bound + \
            'B062F11     Lower Bound of Approximation:          %G\n' \
            % self.lower_bound_of_approximation + \
            'B062F12     Upper Bound of Approximation:          %G\n' \
            % self.upper_bound_of_approximation + \
            'B062F13     Maximum Absolute Error:                %G\n' \
            % self.maximum_absolute_error + \
            'B062F14     Number of coefficients:                %d\n' \
            % self.number_of_polynomial_coefficients
        if self.number_of_polynomial_coefficients:
            string += '#\t\tPolynomial coefficients:\n' + \
                '#\t\t  i, coefficient,  error\n'
            if self.number_of_polynomial_coefficients > 1:
                for _i in range(self.number_of_polynomial_coefficients):
                    string += 'B062F15-16   %2s %13s %13s\n' \
                        % (_i, formatRESP(self.polynomial_coefficient[_i], 6),
                           formatRESP(self.polynomial_coefficient_error[_i],
                                      6))
            else:
                string += 'B062F15-16   %2s %13s %13s\n' \
                    % (0, formatRESP(self.polynomial_coefficient, 6),
                       formatRESP(self.polynomial_coefficient_error, 6))
        string += '#\t\t\n'
        return string
Exemple #13
0
class Blockette053(Blockette):
    """
    Blockette 053: Response (Poles & Zeros) Blockette.

    Sample::

        0530382B 1007008 7.87395E+00 5.00000E-02  3
         0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00
         0.00000E+00 0.00000E+00 0.00000E+00 0.00000E+00
        -1.27000E+01 0.00000E+00 0.00000E+00 0.00000E+00  4
        -1.96418E-03 1.96418E-03 0.00000E+00 0.00000E+00
        S-1.96418E-03-1.96418E-03 0.00000E+00 0.00000E+00
        53-6.23500E+00 7.81823E+00 0.00000E+00 0.00000E+00
        -6.23500E+00-7.81823E+00 0.00000E+00 0.00000E+00
    """

    id = 53
    name = "Response Poles and Zeros"
    fields = [
        FixedString(3, "Transfer function types", 1, 'U'),
        Integer(4, "Stage sequence number", 2),
        Integer(5, "Stage signal input units", 3, xpath=34),
        Integer(6, "Stage signal output units", 3, xpath=34),
        Float(7, "A0 normalization factor", 12, mask='%+1.5e'),
        Float(8, "Normalization frequency", 12, mask='%+1.5e'),
        Integer(9, "Number of complex zeros", 3),
        # REPEAT fields 10 — 13 for the Number of complex zeros:
        Loop('Complex zero', "Number of complex zeros", [
            Float(10, "Real zero", 12, mask='%+1.5e'),
            Float(11, "Imaginary zero", 12, mask='%+1.5e'),
            Float(12, "Real zero error", 12, mask='%+1.5e'),
            Float(13, "Imaginary zero error", 12, mask='%+1.5e')
        ]),
        Integer(14, "Number of complex poles", 3),
        # REPEAT fields 15 — 18 for the Number of complex poles:
        Loop('Complex pole', "Number of complex poles", [
            Float(15, "Real pole", 12, mask='%+1.5e'),
            Float(16, "Imaginary pole", 12, mask='%+1.5e'),
            Float(17, "Real pole error", 12, mask='%+1.5e'),
            Float(18, "Imaginary pole error", 12, mask='%+1.5e')
        ])
    ]

    def getRESP(self, station, channel, abbreviations):
        """
        Returns RESP string.
        """
        # Field three needs some extra parsing.
        field_three_dict = {
            'A': 'A [Laplace Transform (Rad/sec)]',
            'B': 'B [Analog (Hz)]',
            'C': 'C [Composite]',
            'D': 'D [Digital (Z-transform)]'
        }
        out = RESP % (
            station, channel, field_three_dict[self.transfer_function_types],
            self.stage_sequence_number,
            LookupCode(abbreviations, 34, 'unit_name', 'unit_lookup_code',
                       self.stage_signal_input_units),
            LookupCode(abbreviations, 34, 'unit_description',
                       'unit_lookup_code', self.stage_signal_input_units),
            LookupCode(abbreviations, 34, 'unit_name', 'unit_lookup_code',
                       self.stage_signal_output_units),
            LookupCode(abbreviations, 34, 'unit_description',
                       'unit_lookup_code', self.stage_signal_output_units),
            self.A0_normalization_factor, self.normalization_frequency,
            self.number_of_complex_zeros, self.number_of_complex_poles)
        if self.number_of_complex_zeros > 0:
            if self.number_of_complex_zeros != 1:
                # Loop over all zeros.
                for _i in range(self.number_of_complex_zeros):
                    out += 'B053F10-13 %4s %13s %13s %13s %13s\n' % (
                        _i, formatRESP(self.real_zero[_i], 6),
                        formatRESP(self.imaginary_zero[_i],
                                   6), formatRESP(self.real_zero_error[_i], 6),
                        formatRESP(self.imaginary_zero_error[_i], 6))
            else:
                out += 'B053F10-13 %4s %13s %13s %13s %13s\n' % (
                    0, formatRESP(self.real_zero,
                                  6), formatRESP(self.imaginary_zero, 6),
                    formatRESP(self.real_zero_error,
                               6), formatRESP(self.imaginary_zero_error, 6))
        out += '#\t\tComplex poles:\n'
        out += '#\t\t  i  real          imag          real_error    '
        out += 'imag_error\n'
        if self.number_of_complex_poles > 0:
            if self.number_of_complex_poles != 1:
                # Loop over all poles.
                for _i in range(self.number_of_complex_poles):
                    out += 'B053F15-18 %4s %13s %13s %13s %13s\n' % (
                        _i, formatRESP(self.real_pole[_i], 6),
                        formatRESP(self.imaginary_pole[_i],
                                   6), formatRESP(self.real_pole_error[_i], 6),
                        formatRESP(self.imaginary_pole_error[_i], 6))
            else:
                out += 'B053F15-18 %4s %13s %13s %13s %13s\n' % (
                    0, formatRESP(self.real_pole,
                                  6), formatRESP(self.imaginary_pole, 6),
                    formatRESP(self.real_pole_error,
                               6), formatRESP(self.imaginary_pole_error, 6))
        out += '#\t\t\n'
        return out.encode()
Exemple #14
0
class Blockette048(Blockette):
    """
    Blockette 048: Channel Sensitivity/Gain Dictionary Blockette.

    See Channel Sensitivity/Gain Blockette [58] for more information.
    """

    id = 48
    name = "Channel Sensivitity Gain Dictionary"
    fields = [
        Integer(3, "Response Lookup Key", 4),
        VariableString(4, "Response Name", 1, 25, 'UN_'),
        Float(5, "Sensitivity gain", 12, mask='%+1.5e'),
        Float(6, "Frequency", 12, mask='%+1.5e'),
        Integer(7, "Number of history values", 2),
        # REPEAT fields 8 — 10 for the Number of history values:
        Loop('History', "Number of history values", [
            Float(8, "Sensitivity for calibration", 12, mask='%+1.5e'),
            Float(9, "Frequency of calibration sensitivity", 12,
                  mask='%+1.5e'),
            VariableString(10, "Time of above calibration", 1, 22, 'T')
        ])
    ]

    def getRESP(self, station, channel, abbreviations):
        """
        Returns RESP string.
        """
        string = \
            '#\t\t+                  ' + \
            '+---------------------------------------+' + \
            '                  +\n' + \
            '#\t\t+                  |   Channel Sensitivity,' + \
            '%6s ch %s   |                  +\n' % (station, channel) + \
            '#\t\t+                  ' + \
            '+---------------------------------------+' + \
            '                  +\n' + \
            '#\t\t\n' + \
            'B048F05     Sensitivity:                           %s\n' \
            % formatRESP(self.sensitivity_gain, 6) + \
            'B048F06     Frequency of sensitivity:              %s\n' \
            % formatRESP(self.frequency, 6) + \
            'B048F07     Number of calibrations:                %s\n' \
            % self.number_of_history_values
        if self.number_of_history_values > 1:
            string += \
                '#\t\tCalibrations:\n' + \
                '#\t\t i, sensitivity, frequency, time of calibration\n'
            for _i in range(self.number_of_history_values):
                string += \
                    'B048F08-09   %2s %13s %13s %s\n' \
                    % (formatRESP(self.sensitivity_for_calibration[_i], 6),
                        formatRESP(
                            self.frequency_of_calibration_sensitivity[_i], 6),
                       self.time_of_above_calibration[_i].formatSEED())
        elif self.number_of_history_values == 1:
            string += \
                '#\t\tCalibrations:\n' + \
                '#\t\t i, sensitivity, frequency, time of calibration\n' + \
                'B048F08-09    0 %13s %13s %s\n' % (
                    formatRESP(self.sensitivity_for_calibration, 6),
                    formatRESP(self.frequency_of_calibration_sensitivity, 6),
                    self.time_of_above_calibration.formatSEED())
        string += '#\t\t\n'
        return string