def do_spectral_analysis(self, check_quality=False):
        """Gets the spectral_analysis result.

        @param check_quality: Check quality of each channel.

        """
        self.has_data()
        for channel_idx in xrange(self._raw_data.channel):
            signal = self._raw_data.channel_data[channel_idx]
            max_abs = max(numpy.abs(signal))
            logging.debug('Channel %d max abs signal: %f', channel_idx, max_abs)
            if max_abs == 0:
                logging.info('No data on channel %d, skip this channel',
                              channel_idx)
                continue

            saturate_value = audio_data.get_maximum_value_from_sample_format(
                    self._raw_data.sample_format)
            normalized_signal = audio_analysis.normalize_signal(
                    signal, saturate_value)
            logging.debug('saturate_value: %f', saturate_value)
            logging.debug('max signal after normalized: %f', max(normalized_signal))
            spectral = audio_analysis.spectral_analysis(
                    normalized_signal, self._rate)
            logging.info('Channel %d spectral:\n%s', channel_idx,
                         pprint.pformat(spectral))

            if check_quality:
                quality = audio_quality_measurement.quality_measurement(
                        signal=normalized_signal,
                        rate=self._rate,
                        dominant_frequency=spectral[0][0])
                logging.info('Channel %d quality:\n%s', channel_idx,
                             pprint.pformat(quality))
Exemple #2
0
 def testNotMeaningfulData(self):
     """Checks that sepectral analysis handles un-meaningful data."""
     rate = 48000
     length_in_secs = 0.5
     samples = length_in_secs * rate
     noise_amplitude = audio_analysis.MEANINGFUL_RMS_THRESHOLD * 0.5
     noise = numpy.random.standard_normal(samples) * noise_amplitude
     results = audio_analysis.spectral_analysis(noise, rate)
     self.assertEqual([(0, 0)], results)
Exemple #3
0
 def testNotMeaningfulData(self):
     """Checks that sepectral analysis rejects not meaningful data."""
     rate = 48000
     length_in_secs = 0.5
     samples = length_in_secs * rate
     noise_amplitude = audio_analysis.MEANINGFUL_RMS_THRESHOLD * 0.5
     noise = numpy.random.standard_normal(samples) * noise_amplitude
     with self.assertRaises(audio_analysis.RMSTooSmallError):
         results = audio_analysis.spectral_analysis(noise, rate)
 def testSpectralAnalysisRealData(self):
     """This unittest checks the spectral analysis works on real data."""
     binary = open('client/cros/audio/test_data/1k_2k.raw', 'r').read()
     data = audio_data.AudioRawData(binary, 2, 'S32_LE')
     saturate_value = audio_data.get_maximum_value_from_sample_format(
             'S32_LE')
     golden_frequency = [1000, 2000]
     for channel in [0, 1]:
         normalized_signal = audio_analysis.normalize_signal(
                 data.channel_data[channel],saturate_value)
         spectral = audio_analysis.spectral_analysis(
                 normalized_signal, 48000, 0.02)
         logging.debug('channel %s: %s', channel, spectral)
         self.assertTrue(abs(spectral[0][0] - golden_frequency[channel]) < 5,
                         'Dominant frequency is not correct')
Exemple #5
0
 def testSpectralAnalysis(self):
     rate = 48000
     length_in_secs = 0.5
     freq_1 = 490.0
     freq_2 = 60.0
     coeff_1 = 1
     coeff_2 = 0.3
     samples = length_in_secs * rate
     noise = numpy.random.standard_normal(samples) * 0.005
     x = numpy.linspace(0.0, (samples - 1) * 1.0 / rate, samples)
     y = (coeff_1 * numpy.sin(freq_1 * 2.0 * numpy.pi * x) +
          coeff_2 * numpy.sin(freq_2 * 2.0 * numpy.pi * x)) + noise
     results = audio_analysis.spectral_analysis(y, rate)
     # Results should contains
     # [(490, 1*k), (60, 0.3*k), (0, 0.1*k)] where 490Hz is the dominant
     # frequency with coefficient 1, 60Hz is the second dominant frequency
     # with coefficient 0.3, 0Hz is from Gaussian noise with coefficient
     # around 0.1. The k constant is resulted from window function.
     logging.debug('Results: %s', results)
     self.assertTrue(abs(results[0][0]-freq_1) < 1)
     self.assertTrue(abs(results[1][0]-freq_2) < 1)
     self.assertTrue(
             abs(results[0][1] / results[1][1] - coeff_1 / coeff_2) < 0.01)
    def do_spectral_analysis(self, ignore_high_freq, check_quality,
                             quality_params):
        """Gets the spectral_analysis result.

        @param ignore_high_freq: Ignore high frequencies above this threshold.
        @param check_quality: Check quality of each channel.
        @param quality_params: A QualityParams object for quality measurement.

        """
        self.has_data()
        for channel_idx in xrange(self._raw_data.channel):
            signal = self._raw_data.channel_data[channel_idx]
            max_abs = max(numpy.abs(signal))
            logging.debug('Channel %d max abs signal: %f', channel_idx,
                          max_abs)
            if max_abs == 0:
                logging.info('No data on channel %d, skip this channel',
                             channel_idx)
                continue

            saturate_value = audio_data.get_maximum_value_from_sample_format(
                self._raw_data.sample_format)
            normalized_signal = audio_analysis.normalize_signal(
                signal, saturate_value)
            logging.debug('saturate_value: %f', saturate_value)
            logging.debug('max signal after normalized: %f',
                          max(normalized_signal))
            spectral = audio_analysis.spectral_analysis(
                normalized_signal, self._rate)

            logging.debug('Channel %d spectral:\n%s', channel_idx,
                          pprint.pformat(spectral))

            # Ignore high frequencies above the threshold.
            spectral = [(f, c) for (f, c) in spectral if f < ignore_high_freq]

            logging.info(
                'Channel %d spectral after ignoring high frequencies '
                'above %f:\n%s', channel_idx, ignore_high_freq,
                pprint.pformat(spectral))

            if check_quality:
                quality = audio_quality_measurement.quality_measurement(
                    signal=normalized_signal,
                    rate=self._rate,
                    dominant_frequency=spectral[0][0],
                    block_size_secs=quality_params.block_size_secs,
                    frequency_error_threshold=quality_params.
                    frequency_error_threshold,
                    delay_amplitude_threshold=quality_params.
                    delay_amplitude_threshold,
                    noise_amplitude_threshold=quality_params.
                    noise_amplitude_threshold,
                    burst_amplitude_threshold=quality_params.
                    burst_amplitude_threshold)

                logging.debug('Channel %d quality:\n%s', channel_idx,
                              pprint.pformat(quality))
                self._quality_result.append(quality)

            self._spectrals.append(spectral)
def quality_measurement(
        signal, rate,
        dominant_frequency=None,
        block_size_secs=DEFAULT_BLOCK_SIZE_SECS,
        frequency_error_threshold=DEFAULT_FREQUENCY_ERROR,
        delay_amplitude_threshold=DEFAULT_DELAY_AMPLITUDE_THRESHOLD,
        noise_amplitude_threshold=DEFAULT_NOISE_AMPLITUDE_THRESHOLD,
        burst_amplitude_threshold=DEFAULT_BURST_AMPLITUDE_THRESHOLD,
        volume_changing_amplitude_threshold=DEFAULT_VOLUME_CHANGE_AMPLITUDE):
    """Detects several artifacts and estimates the noise level.

    This method detects artifact before playing, after playing, and delay
    during playing. Also, it estimates the noise level of the signal.
    To avoid the influence of noise, it calculates amplitude and frequency
    block by block.

    @param signal: A list of numbers for one-channel PCM data. The data should
                   be normalized to [-1,1].
    @param rate: Sampling rate
    @param dominant_frequency: Dominant frequency of signal. Set None to
                               recalculate the frequency in this function.
    @param block_size_secs: Block size in seconds. The measurement will be done
                            block-by-block using average amplitude and frequency
                            in each block to avoid noise.
    @param frequency_error_threshold: Ref to DEFAULT_FREQUENCY_ERROR.
    @param delay_amplitude_threshold: If the average amplitude of a block is
                                      lower than average amplitude of the wave
                                      times delay_amplitude_threshold, it will
                                      be considered as delay.
                                      Also refer to delay_detection and
                                      DEFAULT_DELAY_AMPLITUDE_THRESHOLD.
    @param noise_amplitude_threshold: If the average amplitude of a block is
                                      higher than average amplitude of the wave
                                      times noise_amplitude_threshold, it will
                                      be considered as noise before/after
                                      playback.
                                      Also refer to noise_detection and
                                      DEFAULT_NOISE_AMPLITUDE_THRESHOLD.
    @param burst_amplitude_threshold: If the average amplitude of a block is
                                      higher than average amplitude of its left
                                      block and its right block times
                                      burst_amplitude_threshold. It will be
                                      considered as a burst.
                                      Also refer to burst_detection and
                                      DEFAULT_BURST_AMPLITUDE_THRESHOLD.
    @param volume_changing_amplitude_threshold: If the average amplitude of right
                                                block is higher or lower than
                                                that of left one times this
                                                value, it will be considered as
                                                a volume change.
                                                Also refer to
                                                changing_volume_detection and
                                                DEFAULT_VOLUME_CHANGE_AMPLITUDE

    @returns: A dictoinary of detection/estimation:
              {'artifacts':
                {'noise_before_playback':
                    [(time_1, duration_1), (time_2, duration_2), ...],
                 'noise_after_playback':
                    [(time_1, duration_1), (time_2, duration_2), ...],
                 'delay_during_playback':
                    [(time_1, duration_1), (time_2, duration_2), ...],
                 'burst_during_playback':
                    [time_1, time_2, ...]
                },
               'volume_changes':
                 [(time_1, flag_1), (time_2, flag_2), ...],
               'equivalent_noise_level': level
              }
              where durations and time points are in seconds. And,
              equivalence_noise_level is the quotient of noise and
              wave which refers to DEFAULT_STANDARD_NOISE.
              volume_changes is a list of tuples containing time
              stamps and decreasing/increasing flags for volume
              change events.

    """
    # Calculates the block size, from seconds to samples.
    block_size = int(block_size_secs * rate)

    signal = numpy.concatenate((numpy.zeros(int(rate * APPEND_ZEROS_SECS)),
                                signal,
                                numpy.zeros(int(rate * APPEND_ZEROS_SECS))))
    signal = numpy.array(signal, dtype=float)
    length = len(signal)

    # Calculates the amplitude and frequency.
    amplitude, frequency = hilbert_analysis(signal, rate, block_size)

    # Finds the dominant frequency.
    if not dominant_frequency:
        dominant_frequency = audio_analysis.spectral_analysis(signal, rate)[0][0]

    # Finds the array which contains absolute difference between dominant
    # frequency and frequency at each time point.
    frequency_delta = abs(frequency - dominant_frequency)

    # Computes average amplitude of each type of block
    res = find_block_average_value(amplitude, block_size * 2, block_size)
    left_block_amplitude, right_block_amplitude, block_amplitude = res

    # Computes average absolute difference of frequency and dominant frequency
    # of the block of each index
    _, _, block_frequency_delta = find_block_average_value(frequency_delta,
                                                           block_size * 2,
                                                           block_size)

    # Finds start and end index of sine wave.
    start_index, end_index = find_start_end_index(dominant_frequency,
                                                  block_frequency_delta,
                                                  block_size,
                                                  frequency_error_threshold)

    if start_index > end_index:
        raise SineWaveNotFound('No sine wave found in signal')

    logging.debug('Found sine wave: start: %s, end: %s',
                  float(start_index) / rate - APPEND_ZEROS_SECS,
                  float(end_index) / rate - APPEND_ZEROS_SECS)

    sum_of_amplitude = float(sum(amplitude[start_index:end_index]))
    # Finds average amplitude of sine wave.
    average_amplitude = sum_of_amplitude / (end_index - start_index)

    # Finds noise before and/or after playback.
    noise_before_playing, noise_after_playing = noise_detection(
            start_index, end_index,
            block_amplitude, average_amplitude,
            rate,
            noise_amplitude_threshold)

    # Finds delay during playback.
    delays = delay_detection(start_index, end_index,
                             block_amplitude, average_amplitude,
                             dominant_frequency,
                             rate,
                             left_block_amplitude,
                             right_block_amplitude,
                             block_frequency_delta,
                             delay_amplitude_threshold,
                             frequency_error_threshold)

    # Finds burst during playback.
    burst_time_points = burst_detection(start_index, end_index,
                                        block_amplitude, average_amplitude,
                                        dominant_frequency,
                                        rate,
                                        left_block_amplitude,
                                        right_block_amplitude,
                                        block_frequency_delta,
                                        burst_amplitude_threshold,
                                        frequency_error_threshold)

    # Finds volume changing during playback.
    volume_changes = changing_volume_detection(
            start_index, end_index,
            average_amplitude,
            rate,
            left_block_amplitude,
            right_block_amplitude,
            volume_changing_amplitude_threshold)

    # Calculates the average teager value.
    teager_value = average_teager_value(signal[start_index:end_index],
                                        average_amplitude)

    # Finds out the noise level.
    noise = noise_level(average_amplitude, dominant_frequency,
                        rate,
                        teager_value)

    return {'artifacts':
            {'noise_before_playback': noise_before_playing,
             'noise_after_playback': noise_after_playing,
             'delay_during_playback': delays,
             'burst_during_playback': burst_time_points
            },
            'volume_changes': volume_changes,
            'equivalent_noise_level': noise
           }
Exemple #8
0
 def testEmptyData(self):
     """Checks that sepectral analysis rejects empty data."""
     with self.assertRaises(audio_analysis.EmptyDataError):
         results = audio_analysis.spectral_analysis([], 100)
def check_recorded_frequency(
        golden_file,
        recorder,
        second_peak_ratio=_DEFAULT_SECOND_PEAK_RATIO,
        frequency_diff_threshold=DEFAULT_FREQUENCY_DIFF_THRESHOLD,
        ignore_frequencies=None,
        check_anomaly=False,
        check_artifacts=False,
        mute_durations=None,
        volume_changes=None,
        tolerant_noise_level=DEFAULT_TOLERANT_NOISE_LEVEL):
    """Checks if the recorded data contains sine tone of golden frequency.

    @param golden_file: An AudioTestData object that serves as golden data.
    @param recorder: An AudioWidget used in the test to record data.
    @param second_peak_ratio: The test fails when the second dominant
                              frequency has coefficient larger than this
                              ratio of the coefficient of first dominant
                              frequency.
    @param frequency_diff_threshold: The maximum difference between estimated
                                     frequency of test signal and golden
                                     frequency. This value should be small for
                                     signal passed through line.
    @param ignore_frequencies: A list of frequencies to be ignored. The
                               component in the spectral with frequency too
                               close to the frequency in the list will be
                               ignored. The comparison of frequencies uses
                               frequency_diff_threshold as well.
    @param check_anomaly: True to check anomaly in the signal.
    @param check_artifacts: True to check artifacts in the signal.
    @param mute_durations: Each duration of mute in seconds in the signal.
    @param volume_changes: A list containing alternative -1 for decreasing
                           volume and +1 for increasing volume.
    @param tolerant_noise_level: The maximum noise level can be tolerated

    @returns: A list containing tuples of (dominant_frequency, coefficient) for
              valid channels. Coefficient can be a measure of signal magnitude
              on that dominant frequency. Invalid channels where golden_channel
              is None are ignored.

    @raises error.TestFail if the recorded data does not contain sine tone of
            golden frequency.

    """
    if not ignore_frequencies:
        ignore_frequencies = []

    # Also ignore harmonics of ignore frequencies.
    ignore_frequencies_harmonics = []
    for ignore_freq in ignore_frequencies:
        ignore_frequencies_harmonics += [ignore_freq * n for n in xrange(1, 4)]

    data_format = recorder.data_format
    recorded_data = audio_data.AudioRawData(
        binary=recorder.get_binary(),
        channel=data_format['channel'],
        sample_format=data_format['sample_format'])

    errors = []
    dominant_spectrals = []

    for test_channel, golden_channel in enumerate(recorder.channel_map):
        if golden_channel is None:
            logging.info('Skipped channel %d', test_channel)
            continue

        signal = recorded_data.channel_data[test_channel]
        saturate_value = audio_data.get_maximum_value_from_sample_format(
            data_format['sample_format'])
        logging.debug('Channel %d max signal: %f', test_channel, max(signal))
        normalized_signal = audio_analysis.normalize_signal(
            signal, saturate_value)
        logging.debug('saturate_value: %f', saturate_value)
        logging.debug('max signal after normalized: %f',
                      max(normalized_signal))
        spectral = audio_analysis.spectral_analysis(normalized_signal,
                                                    data_format['rate'])
        logging.debug('spectral: %s', spectral)

        if not spectral:
            errors.append('Channel %d: Can not find dominant frequency.' %
                          test_channel)

        golden_frequency = golden_file.frequencies[golden_channel]
        logging.debug('Checking channel %s spectral %s against frequency %s',
                      test_channel, spectral, golden_frequency)

        dominant_frequency = spectral[0][0]

        if (abs(dominant_frequency - golden_frequency) >
                frequency_diff_threshold):
            errors.append(
                'Channel %d: Dominant frequency %s is away from golden %s' %
                (test_channel, dominant_frequency, golden_frequency))

        if check_anomaly:
            detected_anomaly = audio_analysis.anomaly_detection(
                signal=normalized_signal,
                rate=data_format['rate'],
                freq=golden_frequency)
            if detected_anomaly:
                errors.append(
                    'Channel %d: Detect anomaly near these time: %s' %
                    (test_channel, detected_anomaly))
            else:
                logging.info(
                    'Channel %d: Quality is good as there is no anomaly',
                    test_channel)

        if check_artifacts or mute_durations or volume_changes:
            result = audio_quality_measurement.quality_measurement(
                normalized_signal,
                data_format['rate'],
                dominant_frequency=dominant_frequency)
            logging.debug('Quality measurement result:\n%s',
                          pprint.pformat(result))
            if check_artifacts:
                if len(result['artifacts']['noise_before_playback']) > 0:
                    errors.append(
                        'Channel %d: Detects artifacts before playing near'
                        ' these time and duration: %s' %
                        (test_channel,
                         str(result['artifacts']['noise_before_playback'])))

                if len(result['artifacts']['noise_after_playback']) > 0:
                    errors.append(
                        'Channel %d: Detects artifacts after playing near'
                        ' these time and duration: %s' %
                        (test_channel,
                         str(result['artifacts']['noise_after_playback'])))

            if mute_durations:
                delays = result['artifacts']['delay_during_playback']
                delay_durations = []
                for x in delays:
                    delay_durations.append(x[1])
                mute_matched, delay_matched = longest_common_subsequence(
                    mute_durations, delay_durations,
                    DEFAULT_EQUIVALENT_THRESHOLD)

                # updated delay list
                new_delays = [
                    delays[i] for i in delay_matched if not delay_matched[i]
                ]

                result['artifacts']['delay_during_playback'] = new_delays

                unmatched_mutes = [
                    mute_durations[i] for i in mute_matched
                    if not mute_matched[i]
                ]

                if len(unmatched_mutes) > 0:
                    errors.append('Channel %d: Unmatched mute duration: %s' %
                                  (test_channel, unmatched_mutes))

            if check_artifacts:
                if len(result['artifacts']['delay_during_playback']) > 0:
                    errors.append(
                        'Channel %d: Detects delay during playing near'
                        ' these time and duration: %s' %
                        (test_channel,
                         result['artifacts']['delay_during_playback']))

                if len(result['artifacts']['burst_during_playback']) > 0:
                    errors.append(
                        'Channel %d: Detects burst/pop near these time: %s' %
                        (test_channel,
                         result['artifacts']['burst_during_playback']))

                if result['equivalent_noise_level'] > tolerant_noise_level:
                    errors.append(
                        'Channel %d: noise level is higher than tolerant'
                        ' noise level: %f > %f' %
                        (test_channel, result['equivalent_noise_level'],
                         tolerant_noise_level))

            if volume_changes:
                matched = True
                volume_changing = result['volume_changes']
                if len(volume_changing) != len(volume_changes):
                    matched = False
                else:
                    for i in xrange(len(volume_changing)):
                        if volume_changing[i][1] != volume_changes[i]:
                            matched = False
                            break
                if not matched:
                    errors.append(
                        'Channel %d: volume changing is not as expected, '
                        'found changing time and events are: %s while '
                        'expected changing events are %s' %
                        (test_channel, volume_changing, volume_changes))

        # Filter out the harmonics resulted from imperfect sin wave.
        # This list is different for different channels.
        harmonics = [dominant_frequency * n for n in xrange(2, 10)]

        def should_be_ignored(frequency):
            """Checks if frequency is close to any frequency in ignore list.

            The ignore list is harmonics of frequency to be ignored
            (like power noise), plus harmonics of dominant frequencies,
            plus DC.

            @param frequency: The frequency to be tested.

            @returns: True if the frequency should be ignored. False otherwise.

            """
            for ignore_frequency in (ignore_frequencies_harmonics + harmonics +
                                     [0.0]):
                if (abs(frequency - ignore_frequency) <
                        frequency_diff_threshold):
                    logging.debug('Ignore frequency: %s', frequency)
                    return True

        # Checks DC is small enough.
        for freq, coeff in spectral:
            if freq < _DC_FREQ_THRESHOLD and coeff > _DC_COEFF_THRESHOLD:
                errors.append('Channel %d: Found large DC coefficient: '
                              '(%f Hz, %f)' % (test_channel, freq, coeff))

        # Filter out the frequencies to be ignored.
        spectral = [x for x in spectral if not should_be_ignored(x[0])]

        if len(spectral) > 1:
            first_coeff = spectral[0][1]
            second_coeff = spectral[1][1]
            if second_coeff > first_coeff * second_peak_ratio:
                errors.append(
                    'Channel %d: Found large second dominant frequencies: '
                    '%s' % (test_channel, spectral))

        dominant_spectrals.append(spectral[0])

    if errors:
        raise error.TestFail(', '.join(errors))

    return dominant_spectrals
def check_recorded_frequency(
        golden_file,
        recorder,
        second_peak_ratio=DEFAULT_SECOND_PEAK_RATIO,
        frequency_diff_threshold=DEFAULT_FREQUENCY_DIFF_THRESHOLD,
        ignore_frequencies=None,
        check_anomaly=False):
    """Checks if the recorded data contains sine tone of golden frequency.

    @param golden_file: An AudioTestData object that serves as golden data.
    @param recorder: An AudioWidget used in the test to record data.
    @param second_peak_ratio: The test fails when the second dominant
                              frequency has coefficient larger than this
                              ratio of the coefficient of first dominant
                              frequency.
    @param frequency_diff_threshold: The maximum difference between estimated
                                     frequency of test signal and golden
                                     frequency. This value should be small for
                                     signal passed through line.
    @param ignore_frequencies: A list of frequencies to be ignored. The
                               component in the spectral with frequency too
                               close to the frequency in the list will be
                               ignored. The comparison of frequencies uses
                               frequency_diff_threshold as well.
    @param check_anomaly: True to check anomaly in the signal.

    @raises error.TestFail if the recorded data does not contain sine tone of
            golden frequency.

    """
    data_format = recorder.data_format
    recorded_data = audio_data.AudioRawData(
        binary=recorder.get_binary(),
        channel=data_format['channel'],
        sample_format=data_format['sample_format'])

    errors = []

    for test_channel, golden_channel in enumerate(recorder.channel_map):
        if golden_channel is None:
            logging.info('Skipped channel %d', test_channel)
            continue

        signal = recorded_data.channel_data[test_channel]
        saturate_value = audio_data.get_maximum_value_from_sample_format(
            data_format['sample_format'])
        normalized_signal = audio_analysis.normalize_signal(
            signal, saturate_value)
        spectral = audio_analysis.spectral_analysis(normalized_signal,
                                                    data_format['rate'])

        if not spectral:
            errors.append('Channel %d: Can not find dominant frequency.' %
                          test_channel)

        golden_frequency = golden_file.frequencies[golden_channel]
        logging.debug('Checking channel %s spectral %s against frequency %s',
                      test_channel, spectral, golden_frequency)

        dominant_frequency = spectral[0][0]

        if (abs(dominant_frequency - golden_frequency) >
                frequency_diff_threshold):
            errors.append(
                'Channel %d: Dominant frequency %s is away from golden %s' %
                (test_channel, dominant_frequency, golden_frequency))

        if check_anomaly:
            detected_anomaly = audio_analysis.anomaly_detection(
                signal=normalized_signal,
                rate=data_format['rate'],
                freq=golden_frequency)
            if detected_anomaly:
                errors.append(
                    'Channel %d: Detect anomaly near these time: %s' %
                    (test_channel, detected_anomaly))
            else:
                logging.info(
                    'Channel %d: Quality is good as there is no anomaly',
                    test_channel)

        def should_be_ignored(frequency):
            """Checks if frequency is close to any frequency in ignore list.

            @param frequency: The frequency to be tested.

            @returns: True if the frequency should be ignored. False otherwise.

            """
            for ignore_frequency in ignore_frequencies:
                if (abs(frequency - ignore_frequency) <
                        frequency_diff_threshold):
                    logging.debug('Ignore frequency: %s', frequency)
                    return True

        # Filter out the frequencies to be ignored.
        if ignore_frequencies:
            spectral = [x for x in spectral if not should_be_ignored(x[0])]

        if len(spectral) > 1:
            first_coeff = spectral[0][1]
            second_coeff = spectral[1][1]
            if second_coeff > first_coeff * second_peak_ratio:
                errors.append(
                    'Channel %d: Found large second dominant frequencies: '
                    '%s' % (test_channel, spectral))

    if errors:
        raise error.TestFail(', '.join(errors))