Линейная интерполяция в прямом цифровом синтезе - PullRequest
5 голосов
/ 11 октября 2010

Я работаю над проектом микроконтроллера DDS на C и испытываю некоторые затруднения с выяснением, как вычислить линейную интерполяцию для сглаживания выходных значений.Программа в ее нынешнем виде
использует 8 старших бит 24-битного аккумулятора в качестве индекса для массива 8-битных выходных значений.Мне нужно придумать функцию, которая возьмет средний и младший байт аккумулятора и сгенерирует значение между «предыдущим» и «следующим» значением в массиве.Это было бы достаточно просто на быстром оборудовании, но так как я использую микроконтроллер, мне действительно нужно избегать каких-либо операций или делений с плавающей запятой!

С этими ограничениями я не уверен, что получу 8-битное интерполированное значение из моих двух 8-битных входных чисел и двух младших байтов аккумулятора, который представляет "расстояние" междудва входных значения.Заранее благодарим за любой совет!

УТОЧНЕНИЕ

DDS = Прямой цифровой синтез

в DDS сигнал генерируется из таблицы поиска с использованием фазный аккумулятор .Фазовый аккумулятор обычно содержит целочисленный компонент и дробный компонент.Целочисленный компонент используется в качестве индекса в таблице поиска.В простых реализациях DDS дробная часть игнорируется, но для более качественного вывода дробный компонент используется для интерполяции (обычно просто линейной интерполяции) между значениями соседней таблицы поиска.Для вышеупомянутого вопроса мы рассмотрим, как эффективно выполнить эту линейную интерполяцию между двумя значениями таблицы поиска для данной дроби, f, где 0 <= f < 1.

Ответы [ 5 ]

7 голосов
/ 11 октября 2010

Если у вас есть таблица значений формы сигнала (либо один квадрант, либо четыре квадранта, это не имеет значения), то одна из возможных оптимизаций - сохранить значения дельты между последовательными значениями таблицы.Т.е. если у вас есть, например, N = 256 и таблица формы сигнала LUT[N], то у вас также есть таблица значений дельты LUT_delta[N].Соотношение между двумя предварительно вычисленными таблицами составляет LUT_delta[i] = LUT[i+1] - LUT[i].Поэтому вместо того, чтобы искать два последовательных табличных значения, LUT[i] и LUT[i+1], вычитая их для получения дельты, а затем выполняя интерполяцию, вы просто просматриваете первое табличное значение LUT[i] и дельту LUT_delta[i].а затем рассчитать интерполированное значение.Для этого требуется такое же количество просмотров таблиц, но меньше математических операций.Вы должны быть в состоянии выполнить интерполяцию с помощью одной команды умножения с накоплением, если вы используете DSP, иначе это умножение + масштабирование + добавление на ЦП общего назначения.Также, если вы чередуете значения LUT и LUT_delta, вы можете найти LUT[i] и LUT_delta[i] с помощью одного чтения, а затем распаковать два значения.

Псевдокод:

extract integer LUT index, i, from accumulator // just need a shift for this
extract fractional part of accumulator, f // mask or subtract to get f
get p = LUT[i] // lookup waveform value
get delta = LUT_delta[i] // lookup delta
calculate p_interp = p + p_delta * f // single multiply-accumulate instruction on most DSPs - need scaling on general purpose CPUs
4 голосов
/ 11 октября 2010

Чтобы выполнить линейную интерполяцию без деления, вы должны убедиться, что ваш знаменатель имеет степень 2.

значение (x) = предыдущее,
значение (x + 1) = следующее значение (x + dx) = предыдущая + (следующая - предыдущая) * dx

Ваш вопрос: как мне вычислить dx?Хитрость заключается в том, чтобы вычислить индекс интерполяции (16 младших битов вашего аккумулятора) так, чтобы максимальное значение (dx = 1) было степенью двойки:

value(x + dx) = previous + ((next - previous) * index) / 1024

Здесь вы вычислили свой шагзначение, так что максимальный шаг 1024 и соответствует dx = 1.Индекс = 512 для дх = 0,5 и т. Д ...

1 голос
/ 11 октября 2010

Если вы хотите большей точности, я бы посоветовал сначала проверить младшие биты аккумулятора.Например, если мы хотим 4 выходных значения вместо 1:

Acc += 0x2000;
uint lower_bits = Acc & 0xffff;
int rl = LUT[ Acc >> 16];
int rh = LUT[(Acc >> 16) + 1];
if (lower_bits < 0x4000)
    return rl;
if (lower_bits < 0x8000)
    return (rl * 3 + rh) >> 2;
if (lower_bits < 0xC000)
    return (rl + rh) >> 1;
return (rl + rh * 3) >> 2;
0 голосов
/ 11 октября 2010

Если вам действительно нужна скорость, посмотрите AVR ассемблер .

0 голосов
/ 11 октября 2010

Линейная интерполяция между двумя значениями, a и b is (a + b) /2.

Это просто на простом оборудовании и не требует деления или с плавающей запятой.

Делить на 2 == сдвиг вправо на один бит.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...