изменение частоты с использованием fft и ifft без использования целых чисел

Я знаю, что могу изменить частоту целыми числами, изменив переменную shift, но как я могу изменить частоту, используя числа с десятичными разрядами, например 0,754 или 1,2345 или 67,456. Если я изменяю переменную 'shift' на нецелое подобное число, такое как 5.1 , я получаю сообщение об ошибке: индексы нижнего индекса должны быть положительными целыми числами меньше 2^31 или логические операции из строки mag2s = [mag2(shift+1:end), zeros(1,shift)];

Пример кода ниже из вопроса >увеличение/уменьшение частоты сигнала с помощью fft и ifft в матлабе/октаве работает с изменением переменной shift (но работает только с целыми числами, мне нужно чтобы также работать с десятичными числами).

PS: я использую октаву 3.8.1, похожую на Matlab, и я знаю, что могу изменить частоту, изменив формулу в переменной ya, но ya будет сигналом взято из источника звука (человеческая речь), поэтому это не будет уравнением. Уравнение используется просто для упрощения примера. И да, Fs велик из-за того, что используемые файлы сигналов имеют длину около 45 секунд, поэтому я не могу использовать ресемплинг, потому что при использовании я получаю сообщение об ошибке нехватки памяти.

Вот анимированный пример видео на YouTube того, что я пытаюсь получить, используя тестовое уравнение ya= .5*sin(2*pi*1*t)+.2*cos(2*pi*3*t) и что я пытаюсь получить, если я изменю переменную shift от (0:0.1:5) youtu.be/pf25Gw6iS1U имейте в виду, что это будет импортированный аудиосигнал, поэтому у меня не будет уравнения, которое можно было бы легко настроить

clear all,clf

Fs = 2000000;% Sampling frequency
t=linspace(0,1,Fs);

%1a create signal
ya = .5*sin(2*pi*2*t); 

%2a create frequency domain
ya_fft = fft(ya);

mag = abs(ya_fft);
phase = unwrap(angle(ya_fft));
ya_newifft=ifft(mag.*exp(i*phase));

% ----- changes start here ----- %

shift   = 5;                            % shift amount
N       = length(ya_fft);               % number of points in the fft
mag1    = mag(2:N/2+1);                 % get positive freq. magnitude
phase1  = phase(2:N/2+1);               % get positive freq. phases
mag2    = mag(N/2+2:end);               % get negative freq. magnitude
phase2  = phase(N/2+2:end);             % get negative freq. phases

% pad the positive frequency signals with 'shift' zeros on the left
% remove 'shift' components on the right
mag1s   = [zeros(1,shift) , mag1(1:end-shift)];
phase1s = [zeros(1,shift) , phase1(1:end-shift)];

% pad the negative frequency signals with 'shift' zeros on the right
% remove 'shift' components on the left
mag2s   = [mag2(shift+1:end), zeros(1,shift)];
phase2s = [phase2(shift+1:end), zeros(1,shift) ];

% recreate the frequency spectrum after the shift
%           DC      +ve freq.   -ve freq.
magS    = [mag(1)   , mag1s     , mag2s];
phaseS  = [phase(1) , phase1s   , phase2s];


x = magS.*cos(phaseS);                  % change from polar to rectangular
y = magS.*sin(phaseS);
yafft2 = x + i*y;                      % store signal as complex numbers
yaifft2 = real(ifft(yafft2));         % take inverse fft

plot(t,ya,'-r',t,yaifft2,'-b'); % time signal with increased frequency
legend('Original signal (ya)  ','New frequency signal (yaifft2)  ')

Исходный сигнал красного графика, скорректированная частота синего графика


person Rick T    schedule 13.01.2016    source источник
comment
Я не уверен, что вы спрашиваете. Сдвиг частоты при смещении этого сигнала уже (вероятно) нецелочисленный. Расстояние между отсчетами в области Фурье равно 2*Найквиста/N (где N — общее количество отсчетов). Если вы хотите более близкое расстояние, вы можете обнулить входной сигнал.   -  person efunkh    schedule 20.01.2016
comment
@efunkh Я знаю, что могу изменить частоту целыми числами, изменив переменную «сдвиг», но как я могу изменить частоту, используя числа с десятичными разрядами, такие как 0,754, 1,2345 или 67,456. Если я изменяю переменную 'shift' на нецелое, например число 5.1, я получаю сообщение об ошибке, индексы индексов должны быть либо положительными целыми числами меньше 2 ^ 31, либо логическими   -  person Rick T    schedule 20.01.2016
comment
Я также добавил строку, откуда исходит ошибка   -  person Rick T    schedule 20.01.2016
comment
Общий способ сдвига частоты состоит в повышении частоты дискретизации сигнала, смешивании его с несущей при подавлении одной боковой полосы, а затем субдискретизации. Вы можете делать совершенно произвольные сдвиги частоты таким образом, стоимость O (N), и вы не имеете дело с БПФ :)   -  person Kuba hasn't forgotten Monica    schedule 20.01.2016
comment
@Kuba Ober Спасибо, но я не совсем уверен, что вы имеете в виду и как запрограммировать свой ответ, чтобы проверить это.   -  person Rick T    schedule 20.01.2016
comment
@RickT Я предполагаю, что вы нацелены на определенную частоту? Как вы пытаетесь сдвинуть сигнал на 5,245 Гц? БПФ является дискретным, поэтому вы можете сдвигать сигнал только на целочисленные отсчеты, но легко получить произвольный интервал отсчетов, просто изменив длину сигнала (при сохранении постоянного значения Fs). Запустите вашу программу для t=[0:1/(Fs-1):1] и t=[0:1/(Fs-1):3] и вы увидите, что величина изменения частоты для пяти выборочного сдвига меняется в каждом конкретном случае. Если это то, что вы хотите, я напишу более подробный ответ, когда у меня будет возможность.   -  person efunkh    schedule 20.01.2016
comment
@efunkh Я пытаюсь сдвинуть частоту импортированного сигнала на точную величину, например, если импортированный сигнал имеет частоту 3,2 Гц (обратите внимание, что импортированные сигналы будут аудиофайлами человеческой речи), и я хочу сдвинуть его вверх на 5,1 Гц. новая частота будет 8,3 Гц. (3,2 Гц + 5,1 Гц = 8,3 Гц)   -  person Rick T    schedule 20.01.2016


Ответы (3)


Итак, вопрос, как я понимаю, «как мне сдвинуть мой сигнал на определенную частоту?»

Сначала давайте определим Fs, которая является нашей частотой дискретизации (т.е. выборки в секунду). Мы собираем сигнал длиной N отсчетов. Тогда изменение частоты между отсчетами в области Фурье равно Fs/N. Итак, взяв код вашего примера, Fs равен 2 000 000, а N равен 2 000 000, поэтому расстояние между каждым сэмплом составляет 1 Гц, а сдвиг вашего сигнала на 5 сэмплов сдвигает его на 5 Гц.

Теперь предположим, что вместо этого мы хотим сдвинуть наш сигнал на 5,25 Гц. Что ж, если бы наш сигнал состоял из 8 000 000 сэмплов, тогда интервал был бы Fs/N = 0,25 Гц, и мы бы сдвинули наш сигнал на 11 сэмплов. Итак, как мы можем получить сигнал с 8 000 000 отсчетов из сигнала с 2 000 000 отсчетов? Просто заполните его нулями! буквально добавлять нули, пока не будет 8 000 000 выборок. Почему это работает? Потому что вы, по сути, умножаете свой сигнал на прямоугольное окно, что эквивалентно свертке функции sinc в частотной области. Это важный момент. Добавляя нули, вы выполняете интерполяцию в частотной области (у вас больше нет информации о частоте сигнала, который вы просто интерполируете между предыдущими точками DTFT).

Мы можем сделать это с любым разрешением, которое вы хотите, но в конечном итоге вам придется иметь дело с тем фактом, что числа в цифровых системах не являются непрерывными, поэтому я рекомендую просто выбрать приемлемый допуск. Допустим, мы хотим быть в пределах 0,01 от желаемой частоты.

Итак, давайте перейдем к реальному коду. Большинство из них, к счастью, не меняется.

clear all,clf

Fs = 44100; % lets pick actual audio sampling rate
tolerance = 0.01; % our frequency bin tolerance
minSignalLen = Fs / tolerance; %minimum number of samples for our tolerance

%your code does not like odd length signals so lets make sure we have an 
%even signal length
if(mod(minSignalLen,2) ~=0 )
   minSignalLen = minSignalLen + 1; 
end 

t=linspace(0,1,Fs); %our input signal is 1s long

%1a create 2Hz signal   
ya = .5*sin(2*pi*2*t); 

if (length(ya) < minSignalLen)
    ya = [ya, zeros(1, minSignalLen - length(ya))];
end

df = Fs / length(ya);  %actual frequency domain spacing;

targetFreqShift = 2.32;  %lets shift it 2.32Hz

nSamplesShift = round(targetFreqShift / df);

%2a create frequency domain
ya_fft = fft(ya);

mag = abs(ya_fft);
phase = unwrap(angle(ya_fft));
ya_newifft=ifft(mag.*exp(i*phase));

% ----- changes start here ----- %

shift   = nSamplesShift;                % shift amount
N       = length(ya_fft);               % number of points in the fft
mag1    = mag(2:N/2+1);                 % get positive freq. magnitude
phase1  = phase(2:N/2+1);               % get positive freq. phases
mag2    = mag(N/2+2:end);               % get negative freq. magnitude
phase2  = phase(N/2+2:end);             % get negative freq. phases

% pad the positive frequency signals with 'shift' zeros on the left
% remove 'shift' components on the right
mag1s   = [zeros(1,shift) , mag1(1:end-shift)];
phase1s = [zeros(1,shift) , phase1(1:end-shift)];

% pad the negative frequency signals with 'shift' zeros on the right
% remove 'shift' components on the left
mag2s   = [mag2(shift+1:end), zeros(1,shift)];
phase2s = [phase2(shift+1:end), zeros(1,shift) ];

% recreate the frequency spectrum after the shift
%           DC      +ve freq.   -ve freq. 
magS    = [mag(1)   , mag1s     , mag2s];
phaseS  = [phase(1) , phase1s   , phase2s];


x = magS.*cos(phaseS);                  % change from polar to rectangular
y = magS.*sin(phaseS);
yafft2 = x + i*y;                      % store signal as complex numbers
yaifft2 = real(ifft(yafft2));         % take inverse fft

 %pull out the original 1s of signal
plot(t,ya(1:length(t)),'-r',t,yaifft2(1:length(t)),'-b'); 
legend('Original signal (ya)  ','New frequency signal (yaifft2)  ')

результаты сдвига частоты

Конечный сигнал имеет частоту чуть более 4 Гц, чего мы и ожидали. При интерполяции видны некоторые искажения, но их следует свести к минимуму с помощью более длинного сигнала с более сглаженным представлением в частотной области.

Теперь, когда я прошел через все это, вам может быть интересно, есть ли более простой способ. К счастью для нас, есть. Мы можем воспользоваться преимуществами преобразования Гильберта и преобразования Фурье для достижения сдвига частоты, даже не беспокоясь о Fs, уровнях допуска или интервалах между бинами. А именно мы знаем, что временной сдвиг приводит к фазовому сдвигу в области Фурье. Ну, время и частота двойственны, поэтому сдвиг частоты приводит к сложному экспоненциальному умножению во временной области. Мы не хотим просто выполнять массовый сдвиг всех частот, потому что это разрушит нашу симметрию в пространстве Фурье, что приведет к сложному временному ряду. Таким образом, мы используем преобразование Гильберта, чтобы получить аналитический сигнал, состоящий только из положительных частот, сдвигаем его, а затем восстанавливаем наш временной ряд, предполагая симметричное представление Фурье.

Fs = 44100; 
t=linspace(0,1,Fs);
FShift = 2.3 %shift our frequency up by 2.3Hz
%1a create signal
ya = .5*sin(2*pi*2*t);
yaHil = hilbert(ya);  %get the hilbert transform 
yaShiftedHil = yaHil.*exp(1i*2*pi*FShift*t);

yaShifted = real(yaShiftedHil); 
figure
plot(t,ya,'-r',t,yaShifted,'-b') 
legend('Original signal (ya)  ','New frequency signal (yaifft2)  ')

введите здесь описание изображения

person efunkh    schedule 20.01.2016
comment
передача Гильберта отлично работает, только если вы используете одиночные волны sin или одиночные волны cos, но если я устанавливаю FShift = 1 и ya = .5 * sin (2 * pi * 1 * t) + .2 * cos (2 * pi * 3). *т); (Я настроил вас просто для того, чтобы попытаться смоделировать, как может выглядеть волна импорта аудио), и кажется, что она разваливается, см. ссылку на анимированное видео на YouTube, которое я создал, показывающее, что происходит youtu.be/J5UMrPZvnMA и ссылку на ваш код. Я протестировал его с помощью bit.ly /1lwApNC - person Rick T; 21.01.2016
comment
@RickT, что вы ожидаете от него в этой ситуации? Когда я посмотрел на это, результат с ya= .5*sin(2*pi*1*t)+.2*cos(2*pi*3*t), где FShift = 1, yaShift точно yaShift = 0,5*sin (2*pi*2*t)+.2*cos(2*pi*4*t). Вы сдвинули весь сигнал на 1 Гц. - person efunkh; 21.01.2016
comment
Вот анимированный пример видео на YouTube того, что я пытаюсь получить, используя тестовое уравнение ya= .5*sin(2*pi*1*t)+.2*cos(2*pi*3*t) и что я пытаюсь получить, если я изменил FShift с (0:0.1:5) youtu.be/pf25Gw6iS1U Пожалуйста, имейте в виду, что это будет импортированный звуковой сигнал, поэтому у меня не будет уравнения, которое можно было бы легко настроить - person Rick T; 21.01.2016

Вы можете сделать это, используя дробный фильтр задержки.

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

mag1    = mag(2:end);               
phase1  = phase(2:end);     

Полностью избавьтесь от mag2s и phase2s. Это упрощает строки 37 и 38 до . .

magS    = [mag(1)   , mag1s    ];
phaseS  = [phase(1) , phase1s  ];

Используйте опцию symmetric для ifft, чтобы Matlb обрабатывал симметрию за вас. Затем вы также можете отказаться от принудительного real.

yaifft2 = ifft(yafft2, 'symmetric');         % take inverse fft

После этого мы можем думать о задержке как о фильтре, например.

% ----- changes start here ----- %
shift = 5;
shift_b   = [zeros(1, shift) 1];              % shift amount
shift_a   = 1;

который может быть применен как так. . .

mag1s   = filter(shift_b, shift_a, mag1);
phase1s = filter(shift_b, shift_a, phase1);

С таким мышлением мы можем использовать фильтр allpass, чтобы сделать очень простой фильтр с дробной задержкой.

введите здесь описание изображения

Приведенный выше код дает часть схемы «M Samples Delay». Затем вы можете добавить дробь, используя второй каскадный фильтр allpass. .

shift = 5.5;
Nw = floor(shift);
shift_b   = [zeros(1, Nw) 1];
shift_a   = 1;

Nf = mod(shift,1);
alpha = -(Nf-1)/(Nf+1);    
fract_b   = [alpha 1];           
fract_a   = [1 alpha];

%// now filter as a cascade . . . 
mag1s   = filter(shift_b, shift_a, mag1);
mag1s   = filter(fract_b, fract_a, mag1s);
person learnvst    schedule 20.01.2016
comment
Большое спасибо за Вашу помощь. У вас есть переменная 'shift' в двух местах, одно как shift = 5 и одно как shift = 5.5, это неправильно помечено, также у вас есть переменные fract_b и fract_a, но я не вижу, где они используются. - person Rick T; 20.01.2016
comment
Я был более точным в последнем блоке кода. Посмотрите еще раз сейчас - person learnvst; 20.01.2016
comment
Произвольное изменение частоты с помощью БПФ, как правило, невозможно. Размер БПФ всегда будет ограничивать возможные сдвиги, даже если вы используете фильтры с дробной задержкой. Попробуйте это с голой синусоидой и используйте точный алгоритм определения частоты. Вы увидите, что это не работает. - person Kuba hasn't forgotten Monica; 20.01.2016
comment
@learnvst Похоже, у кода есть проблемы, я создал анимированное видео на YouTube о том, что он делает (синяя линия — это то, что делает ваш код) по адресу youtu.be/Qir_O8q2bqs, и ваш тестовый код можно найти здесь. bit.ly/1P5zq35 - person Rick T; 20.01.2016

Интерполяция с ограниченной полосой пропускания с использованием ядра интерполяции с оконным Sinc может использоваться для изменения частоты дискретизации в произвольном соотношении. Изменение частоты дискретизации изменяет частотный состав сигнала относительно частоты дискретизации в обратном отношении.

person hotpaw2    schedule 13.01.2016
comment
Спасибо, но я не совсем уверен, что вы имеете в виду или как запрограммировать свой ответ, чтобы проверить это. - person Rick T; 14.01.2016