Назад в библиотеку

Программная реализация генератора колебаний

Автор: Matt Donadio

Автор перевода: Зюмин С.А.
Источник (англ.): Donadio M. How to create oscillators in software [Электронный ресурс]. – Режим доступа: http://www.dspguru.com/...

Генератор колебаний

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

Предположим, что:


f = необходимая частота генератора колебаний

w = 2 pi f / Fs

phi = начальная фаза

y = выходной массив, содержащий отсчеты сгенерированного сигнала.


Создание программного генератора колебаний эквивалентно взятию отсчетов гармонического колебания:


for i = 0 to N

y[n] = sin(w * i + phi)

end


Это не очень эффективный метод, ведь нам приходится вычислять значение синусоиды для каждого отсчета сигнала. Используя аппроксимированное выражение, можно ускорить этот процесс.

Другой способ реализации программного генератора колебаний состоит в вычислении рекуррентного соотношения:


y[n] = a * y[n-1] - y[n-2], где a = 2 cos(w)


В псевдокоде это будет выглядеть следующим образом:


a = 2 * cos(w)

y[0] = sin(phi)

y[1] = sin(w + phi)

for i = 2 to N

y[n] = a * y[n-1] - y[n-2]

end


Тут существует проблема. Рекуррентное выражение представляет собой систему второго порядка с передаточной функцией вида:


H(z) = 1 / (1 - a*z^-1 + z^-2)


Подставляя:


a = 1

b = -2cos(w)

c = 1


в решение стандартного квадратного уравнения:


x = (-b +/- sqrt(b*b - 4*a*c)) / 2a


получим:


x = (2*cos(w) +/- sqrt(4*cos(w)*cos(w) - 4)) / 2

= (2*cos(w) +/- 2*sqrt(cos(w)*cos(w) - 1)) / 2

= cos(w) +/- sqrt(cos(w)*cos(w) - 1)

= cos(w) +/- sqrt(-1 * (1 - cos(w)*cos(w)))

= cos(w) +/- j*sqrt(1 - cos(w)*cos(w))

= cos(w) +/- j*sqrt(sin(w)*sin(w))

= cos(w) +/- j*sin(w)

= e^+/-jw


Следовательно:


H(z) = 1 / ((1 - e^jw * z^-1) * (1 - e^-jw * z^-1))


Имеет пару сопряженных полюсов на единичной окружности, следовательно система находится на границе устойчивости. Несмотря на это, такой подход хорошо работает на практике, в зависимости от конкретного приложения, точности представления данных и других факторов. Более подробно метод описан в [1].

Комплексный генератор колебаний

Для реализации комплексного генератора колебаний можем воспользоваться методом, приведенным выше, но использовать косинусоидальное колебание для синфазного сигнала и синусоидальное для квадратурного колебания. В [1] описано, как генерировать оба сигнала одновременно.

Другой метод состоит в том, чтобы воспользоваться поворачивающимся вектором. Мы можем создать вектор и поворачивать его комплексным умножением для получения каждой точки. Вы можете вывести данный метод из тригонометрических функций cos(A+B) и sin(A+B).


dr = cos(w) /* dr,di используются, чтобы вращать вектор */

di = sin(w)

y[0].r = cos(phi) /* начальные вектор и фаза phi */

y[0].i = sin(phi)

for i = 1 to N

y[n].r = dr * y[n-1].r - di * y[n-1].i

y[n].i = dr * y[n-1].i + di * y[n-1].r

end


Подход имеет проблемы со стабильностью. Новое рекуррентное соотношение:


y[n] = e^jw * y[n-1]


С передаточной функцией:


H(z) = 1 / (1 - e^jw * z^-1)


Система имеет один полюс на единичной окружности. Но мы знаем, что длина вектора равна единице, мы можем нормализовать его длину:


|y[n]| = sqrt(y[n].r * y[n].r + y[n].i * y[n].i)


Мы можем добавить:


y[n].r = y[n].r / |y[n]|

y[n].i = y[n].i / |y[n]|


внутрь цикла для нормализации амплитуды y[n]. Но мы теряем в эффективности, так как приходится вычислять значение квадратного корня для каждой точки. Но если значение х близко к единице:


1 / sqrt(x) ~= (3 - x) / 2


Длина поворачивающегося вектора всегда близка к единице, поэтому мы можем изменить выражение внутри цикла:


mag_sq = y[n].r * y[n].r + y[n].i * y[n].i

y[n].r=y[n].r * (3 - (mag_sq)) / 2

y[n].i=y[n].i * (3 - (mag_sq)) / 2


Другой метод создания генератора колебаний связан с использованием таблиц значений. Ключом к пониманию этого подхода является понятие концепции аккумулирования фазы. Посмотрим на отсчеты синусоиды:


for i=0 to N

y[n] = sin(w * i + phi)

end


Так как:


sin(x) = sin(x + 2pi) = sin(x - 2pi)


Мы можем переписать это выражение:


for i=0 to N

y[n] = sin((w * i + phi) mod 2pi)

end


Так как переменная i монотонно увеличивается каждую итерацию, можем записать:


dw = phi

for i=0 to N

y[n] = sin(dw mod 2pi)

dw=dw + w

end


Или как:


dw = phi

for i=0 to N

y[n] = sin(dw)

dw =(dw + w) mod 2pi

end


Вот где ключ к подходу. Если у нас есть М бит, то самые отрицительные числа будут представлять –pi, а положительные будут представлять pi. В практических приложениях, если нам нужно N-битное разрешение по фазе в таблице значений, то мы используем таблицу с 2 ^ N/4 записями и симметричные свойства синусоидального колебания. Мы также используем фазовый накопитель с M битами, M>N. Для индексации в таблице берутся N верхних бит фазового наклпителя в качестве индекса.

Ссылка

1. Digital Signal Processing in Communication Systems by Marvin E. Frerking