Как я могу передать дискретный набор данных в частотную область и обратно (желательно без потерь)
Я хотел бы взять массив байтов размером примерно 70-80 КБ и преобразовать их из временной области в частотную область (возможно, с использованием ДПФ). Я следил за вики и получил этот код до сих пор.
for (int k = 0; k < windows.length; k++) {
double imag = 0.0;
double real = 0.0;
for (int n = 0; n < data.length; n++) {
double val = (data[n])
* Math.exp(-2.0 * Math.PI * n * k / data.length)
/ 128;
imag += Math.cos(val);
real += Math.sin(val);
}
windows[k] = Math.sqrt(imag * imag + real
* real);
}
и, насколько я знаю, это находит величину каждого частотного окна / бина. Я тогда иду через окна и нахожу тот с самой большой величиной. Я добавляю флаг к этой частоте, который будет использоваться при восстановлении сигнала. Я проверяю, соответствует ли восстановленный сигнал моему исходному набору данных. Если он не находит следующее окно с самой высокой частотой и отметьте его, который будет использоваться при восстановлении сигнала.
Вот код, который у меня есть для восстановления сигнала, который, я уверен, в большинстве своем неверен (предполагается, что он выполняет IDFT):
for (int n = 0; n < data.length; n++) {
double imag = 0.0;
double real = 0.0;
sinValue[n] = 0;
for (int k = 0; k < freqUsed.length; k++) {
if (freqUsed[k]) {
double val = (windows[k] * Math.exp(2.0 * Math.PI * n
* k / data.length));
imag += Math.cos(val);
real += Math.sin(val);
}
}
sinValue[n] = imag* imag + real * real;
sinValue[n] /= data.length;
newData[n] = (byte) (127 * sinValue[n]);
}
freqUsed - логический массив, используемый для обозначения того, следует ли использовать частотное окно при восстановлении сигнала.
Во всяком случае, вот проблемы, которые возникают:
- Даже если используются все частотные окна, сигнал не восстанавливается. Это может быть связано с тем, что...
- Иногда значение Math.exp() слишком велико и поэтому возвращает бесконечность. Это затрудняет получение точных расчетов.
- Хотя я следил за вики в качестве руководства, трудно сказать, значат ли мои данные. Это затрудняет тестирование и выявление проблем.
От руки от проблемы:
Я довольно новичок в этом и не до конца понимаю все. Таким образом, любая помощь или понимание приветствуется. Спасибо, что нашли время, чтобы прочитать все это, и заранее спасибо за любую помощь, которую вы можете предоставить. Любая помощь действительно будет хорошей, даже если вы думаете, что я делаю это самым ужасным из возможных способов, я бы хотел знать. Еще раз спасибо.
-
РЕДАКТИРОВАТЬ:
Поэтому я обновил свой код, чтобы он выглядел так:
for (int k = 0; k < windows.length; k++) {
double imag = 0.0;
double real = 0.0;
for (int n = 0; n < data.length; n++) {
double val = (-2.0 * Math.PI * n * k / data.length);
imag += data[n]*-Math.sin(val);
real += data[n]*Math.cos(val);
}
windows[k] = Math.sqrt(imag * imag + real
* real);
}
для оригинального преобразования и:
for (int n = 0; n < data.length; n++) {
double imag = 0.0;
double real = 0.0;
sinValue[n] = 0;
for (int k = 0; k < freqUsed.length; k++) {
if (freqUsed[k]) {
double val = (2.0 * Math.PI * n
* k / data.length);
imag += windows[k]*-Math.sin(val);
real += windows[k]*Math.cos(val);
}
}
sinValue[n] = Math.sqrt(imag* imag + real * real);
sinValue[n] /= data.length;
newData[n] = (byte) (Math.floor(sinValue[n]));
}
для обратного преобразования. Хотя я все еще обеспокоен тем, что это не совсем правильно работает. Я сгенерировал массив, содержащий одну синусоидальную волну, и он не может даже разложить / реконструировать это. Любое понимание того, что мне не хватает?
2 ответа
Да, ваш код (как для DFT, так и для IDFT) не работает. Вы путаете вопрос о том, как использовать экспоненту. ДПФ можно записать как:
N-1
X[k] = SUM { x[n] . exp(-j * 2 * pi * n * k / N) }
n=0
где j
является sqrt(-1). Это можно выразить как:
N-1
X[k] = SUM { (x_real[n] * cos(2*pi*n*k/N) + x_imag[n] * sin(2*pi*n*k/N))
n=0 +j.(x_imag[n] * cos(2*pi*n*k/N) - x_real[n] * sin(2*pi*n*k/N)) }
которые в свою очередь можно разделить на:
N-1
X_real[k] = SUM { x_real[n] * cos(2*pi*n*k/N) + x_imag[n] * sin(2*pi*n*k/N) }
n=0
N-1
X_imag[k] = SUM { x_imag[n] * cos(2*pi*n*k/N) - x_real[n] * sin(2*pi*n*k/N) }
n=0
Если ваши входные данные только для реальных данных, это упрощается до:
N-1
X_real[k] = SUM { x[n] * cos(2*pi*n*k/N) }
n=0
N-1
X_imag[k] = SUM { x[n] * -sin(2*pi*n*k/N) }
n=0
Итак, в итоге, вам не нужны оба exp
и cos/sin
,
Помимо правильных замечаний @Oli, у вас также есть фундаментальное недопонимание по поводу преобразования между временной и частотной областями. Ваш реальный входной сигнал становится сложным сигналом в частотной области. Вы не должны брать величину этого и конвертировать обратно во временную область (это на самом деле даст вам автокорреляцию временной области, если все сделано правильно, но это не то, что вы хотите). Если вы хотите иметь возможность восстановить сигнал временной области, то вы должны сохранить сигнал сложной частотной области как есть (то есть разделить реальные / мнимые компоненты) и выполнить комплексное преобразование IDFT, чтобы вернуться во временную область.
Например, ваше прямое преобразование должно выглядеть примерно так:
for (int k = 0; k < windows.length; k++) {
double imag = 0.0;
double real = 0.0;
for (int n = 0; n < data.length; n++) {
double val = (-2.0 * Math.PI * n * k / data.length);
imag += data[n]*-Math.sin(val);
real += data[n]*Math.cos(val);
}
windows[k].real = real;
windows[k].imag = image;
}
где windows
определяется как массив сложных значений.