Как использовать Apple Accelerate Framework в быстром, чтобы вычислить БПФ реального сигнала? - PullRequest
1 голос
/ 07 февраля 2020

Как я должен использовать платформу Accelerate для вычисления БПФ реального сигнала в Swift на iOS?

Доступный пример в Интернете

Кажется, что Apple Accelerate Framework предоставляет функции для эффективного вычисления БПФ сигнала.

К сожалению, большинство примеров, доступных на Inte rnet, например Swift-FFT-Example и TempiFFT, cra sh, если тестировать всесторонне и вызывать Objective C API.

Документация Apple отвечает на многие вопросы, но также приводит к некоторым другим (Является ли эта часть обязательной? Почему Мне нужен этот вызов для преобразования?).

Потоки при переполнении стека

Существует несколько потоков, посвященных различным аспектам БПФ с конкретными примерами. В частности, FFT с использованием ускорения в Swift , Результат DFT в Swift отличается от результатов MATLAB и FFT Неправильный расчет - Swift . Никто из них не обращается непосредственно к вопросу «Как правильно сделать это, начиная с 0»?

Мне потребовался один день, чтобы понять, как правильно это сделать, поэтому я надеюсь, что эта ветка может дать Четкое объяснение того, как вы должны использовать FFT от Apple, показать, какие подводные камни следует избегать, и помочь разработчикам сэкономить драгоценное время.

1 Ответ

0 голосов
/ 07 февраля 2020

TL; ДР: Если вам нужна рабочая реализация, чтобы скопировать прошлое , то есть суть .

Что такое БПФ?

Быстрое преобразование Фурье - это алгоритм, который принимает сигнал в временная область - совокупность измерений, проводимых с регулярным, обычно небольшим интервалом времени, - и превращающая ее в сигнал, выраженный в фазовой области (совокупность частот). Возможность express сигнала в течение времени, потерянного преобразованием (преобразование является обратимым, что означает, что при вычислении FFT не теряется информация, и вы можете применить IFFT для получения исходного сигнала обратно), но мы получаем возможность различайте guish между частотами, содержащимися в сигнале. Это обычно используется для отображения спектрограмм музыки, которую вы слушаете * на различных видеооборудованиях и видео на YouTube.

БПФ работает с комплексными числами . Если вы не знаете, что это такое, давайте представим, что это комбинация радиуса и угла. Существует одно комплексное число на точку на 2D-плоскости. Вещественные числа (ваши обычные числа с плавающей точкой) можно рассматривать как позицию на линии (отрицательное слева, положительное справа).

Nb: FFT (FFT (FFT (FFT (X)))) = X (с точностью до константы в зависимости от вашей реализации БПФ).

Как вычислить БПФ реального сигнала.

Обычно вы хотите вычислить БПФ небольшого окна аудиосигнала. В качестве примера мы возьмем небольшое окно с образцами 1024. Вы также предпочли бы использовать степень двойки, в противном случае все становится немного сложнее.

var signal: [Float] // Array of length 1024

Во-первых, вам нужно инициализировать некоторые константы для вычислений.

// The length of the input
length = vDSP_Length(signal.count)
// The power of two of two times the length of the input.
// Do not forget this factor 2.
log2n = vDSP_Length(ceil(log2(Float(length * 2))))
// Create the instance of the FFT class which allow computing FFT of complex vector with length
// up to `length`.
fftSetup = vDSP.FFT(log2n: log2n, radix: .radix2, ofType: DSPSplitComplex.self)!

Следуя документации Apple, нам сначала нужно создать сложный массив, который будет нашим вводом. Учебник не вводит в заблуждение. Обычно вы хотите скопировать ваш сигнал как действительную часть ввода и оставьте комплексную часть нулевой.

// Input / Output arrays

var forwardInputReal = [Float](signal) // Copy the signal here
var forwardInputImag = [Float](repeating: 0, count: Int(length))
var forwardOutputReal = [Float](repeating: 0, count: Int(length))
var forwardOutputImag = [Float](repeating: 0, count: Int(length))

Будьте осторожны, функция FFT не позволяет использовать один и тот же splitComplex в качестве входа и выхода одновременно. аварии, это может быть причиной. Вот почему мы определяем как вход, так и выход.

Теперь мы должны быть осторожны и «заблокировать» указатель на эти четыре массива, как показано в примере документации. Если вы просто используете &forwardInputReal в качестве аргумента вашего DSPSplitComplex, указатель может стать недействительным в следующей строке, и вы, скорее всего, увидите sporadi c cra sh вашего приложения.

    forwardInputReal.withUnsafeMutableBufferPointer { forwardInputRealPtr in
      forwardInputImag.withUnsafeMutableBufferPointer { forwardInputImagPtr in
        forwardOutputReal.withUnsafeMutableBufferPointer { forwardOutputRealPtr in
          fforwardOutputImag.withUnsafeMutableBufferPointer { forwardOutputImagPtr in
            // Input
            let forwardInput = DSPSplitComplex(realp: forwardInputRealPtr.baseAddress!, imagp: forwardInputImagPtr.baseAddress!)
            // Output
            var forwardOutput = DSPSplitComplex(realp: forwardOutputRealPtr.baseAddress!, imagp: forwardOutputImagPtr.baseAddress!)

            // FFT call goes here
          }
        }
      }
    }

Теперь финальная строка: вызов вашего БПФ:

fftSetup.forward(input: forwardInput, output: &forwardOutput)

Результат вашего БПФ теперь доступен в forwardOutputReal и forwardOutputImag.

Если вам нужна только амплитуда каждой частоты, и вы не заботитесь о действительной и мнимой части, вы можете объявить рядом с входом и вывести дополнительный массив:

var magnitudes = [Float](repeating: 0, count: Int(length))

add сразу после того, как ваш fft вычислит амплитуду каждого "бина" с:

vDSP.absolute(forwardOutput, result: &magnitudes)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...