Результат DFT в Swift отличается от результата MATLAB

import Cocoa
import Accelerate

let filePath = Bundle.main.path(forResource: "sinusoid", ofType: "txt")
let contentData = FileManager.default.contents(atPath: filePath!)
var content = NSString(data: contentData!, encoding: String.Encoding.utf8.rawValue) as? String

var idx = content?.characters.index(of: "\n")
idx = content?.index(after: idx!)

repeat {
    //let fromIndex = index(from: )
    content = content?.substring(from: idx!)
    idx = content?.characters.index(of: "\n")
    idx = content?.index(after: idx!)
} while content!.characters.contains("%")

let regex = try? NSRegularExpression(pattern: "[ ]+", options:[])

let delimiter = ","
var modifiedString = regex?.stringByReplacingMatches(in: content!, options: [], range: NSRange(location: 0, length: (content! as NSString).length), withTemplate: delimiter)

let lines = modifiedString?.components(separatedBy: "\n")

var s = [Double]()

for var line in lines! {
    if !line.isEmpty {
        let data = line.components(separatedBy: ",")
        s.append(Double(data[1])!)
    }
}

let length = vDSP_Length(pow(2, floor(log2(Float(s.count)))))
let L = Int(length)

// zrop or zop? 
// zrop covers real to complex, and zop covers complex
// length must be a power of 2 or specific multiples of powers of 2 if size is at least 4
let setup = vDSP_DFT_zrop_CreateSetupD(nil, length, vDSP_DFT_Direction.FORWARD)

var inputReal = UnsafeMutablePointer<Double>.allocate(capacity: L)
var inputImaginary = UnsafeMutablePointer<Double>.allocate(capacity: L)
var outputReal = UnsafeMutablePointer<Double>.allocate(capacity: L)
var outputImaginary = UnsafeMutablePointer<Double>.allocate(capacity: L)

for i in 0..<L {
    inputReal[i] = s[i]
    inputImaginary[i] = 0.0
}

vDSP_DFT_ExecuteD(setup!, inputReal, inputImaginary, outputReal, outputImaginary)

for i in 0..<L {
    print("\(outputReal[i]) + \(outputImaginary[i])i")
}

Входной файл «sinusoid.txt» находится по следующей ссылке https://dpaste.de/M1VD.

Данные входного файла состоят из двух синусоидальных волн с частотами 50 и 120. Код Matlab выдает правильный вывод, указанный в следующей ссылке:

https://dpaste.de/2mdK

Когда результат от Matlab масштабируется и снимается амплитуда, он правильно показывает, что амплитуда на частоте 50 равна 0,7, а амплитуда на частоте 120 равна 1.

clear all; close all; clc;
data = load('sinusoid.txt');
S = data(:,2);
Fs = 1000;
Y = fft(S);
L = length(S);
P2 = abs(Y/L);
P1 = P2(1:L/2+1);
P1(2:end-1) = 2*P1(2:end-1);
f = Fs*(0:(L/2))/L;
plot(f,P1)
title('Single-Sided Amplitude Spectrum of X(t)')
xlabel('f (Hz)')
ylabel('|P1(f)|')

Вывод кода Swift совершенно другой и неузнаваемый по сравнению с выводом Matlab, независимо от того, какие коэффициенты масштабирования применяются и применяется ли преобразование реального в сложное или сложное в сложное:

https://dpaste.de/MUwB

Есть идеи, почему это так?


person SwiftMatt    schedule 07.01.2017    source источник
comment
В ваших входных данных 1000 чисел, но только 512 из них передаются в функцию DSP, а остальные 488 значений просто игнорируются. Как это может работать??   -  person Martin R    schedule 07.01.2017
comment
Что вы имеете в виду, как это может работать? Функция БПФ в Swift требует степени 2, поэтому она должна быть 512.   -  person SwiftMatt    schedule 07.01.2017
comment
Пробую с 1024   -  person SwiftMatt    schedule 07.01.2017
comment
Где код MATLAB для сравнения?   -  person Paul R    schedule 07.01.2017
comment
Хорошо, я также добавил код MATLAB   -  person SwiftMatt    schedule 07.01.2017
comment
Я не знаком с MATLAB, но кажется, что набор данных из 1000 элементов не усекается до 512 в вашем коде MATLAB. Таким образом, вы не можете ожидать того же результата.   -  person Martin R    schedule 07.01.2017
comment
Хорошо, если вместо усечения я добавляю от 0 до 1024 элементов данных, результат все равно не тот, Мартин Р.   -  person SwiftMatt    schedule 07.01.2017
comment
Насколько я понимаю, функции БПФ из инфраструктуры Accelerate могут обрабатывать только размеры выборки, которые являются степенью удвоенной 1, 3, 5 или 15, в то время как MATLAB обрабатывает произвольные размеры. Связано: stackoverflow.com/q/12134208/1187415, stackoverflow.com/q/10708667/1187415 и math.stackexchange.com/q/ 77118/42969 от MSE. Как вы можете видеть из последней ссылки, преобразование случая произвольного размера в случай степени двойки не является тривиальным, вы не можете просто дополнить выборки нулями.   -  person Martin R    schedule 07.01.2017
comment
Хм, хорошо, спасибо, я попробую это   -  person SwiftMatt    schedule 07.01.2017


Ответы (2)


Длины ваших двух БПФ разные, поэтому, конечно, результаты не будут совпадать. Вы также передаете разные объемы данных своим 2 БПФ.

Распечатайте длины БПФ и вектор входных данных для отладки кода. Перед сравнением результатов убедитесь, что входные данные совпадают.

Кроме того, БПФ Apple Accelerate/vDSP могут использовать длины, отличные от степени двойки (также разрешены длины с коэффициентами 3 или 5).

Также обратите внимание, что Matlab индексирует массивы, начиная с 1, а не с 0, как это более типично для функций C и Swift.

person hotpaw2    schedule 07.01.2017

Действительно, проблема с несоответствием результатов БПФ была связана с несоответствием размера входных данных. Ограничение входных данных конкретными кратными степеням двойки сильно ограничивает использование БПФ в среде Accelerate. Одно из предложений заключалось в том, чтобы дополнять ввод нулями до тех пор, пока он не будет иметь подходящую длину. Независимо от того, дополняете ли вы нулями или усекаете входные данные, чтобы размер был кратным степени 2, результаты из среды Accelerate будут отличаться от результатов из такой программы, как MATLAB. Решением этой проблемы является выполнение преобразования chirp-z, как указано в ссылке, указанной Мартином Р. Само преобразование chirp-z дает результаты, идентичные БПФ, и может выполняться на входах произвольного размера.

person SwiftMatt    schedule 08.01.2017