Sergey_P писал(а):
Privet писал(а):
Опять, не так...
, а так, если нарисовать один канал...?
t - период оцифровки
k - количество отсчетов на периоде сигнала
Или, опять, что-то не так ?
Увы...
Я рисовать не горазд. Неисповедимы пути, которыми Вы мыслили, рожая данную диаграмму. Вы не потрудились изложить что и откуда у Вас взято. Как обычно, Вы выдали своё собственное понимание системы, а потом с блеском доказываете, что это бред. С этим, правда, я согласен, диаграммы, которые Вы нарисовали это бред.
Вообще, Sergey_P, должен Вам сказать, что когда студентам начинают давать цифровые фильтры, то им показывают самую простейшую вещь - реализацию RC цепочки в виде цифровой дискретной модели:
Yn+1 = Yn * к, где
k близко к единице.
Некая начальная величина Y "утекает" по экспоненциальному закону.
Если Вы не смогли распознать такой фильтр после нескольких дней обсуждения, но делаете столько категоричных заявлений по поводу дизайна и неких "подходов" (пояснить что сие есть Вы не соизволили), то это уже почти как по Булгакову.
Впрочем, у Вас есть шанс ответить за свои слова, но это Вас, кажется, мало беспокоит.
В этом сообщении я как мог подробно описал как это реализуется на Си в моей программе. Я там же показал, что 512 это совсем не длина блока. Это число используется для получения коэффициента утечки
k, которое равно
(1 - 1/512) = 0.998Степень двойки взята для простоты вычислений в целочисленной арифметике. Деление на 512 это просто сдвиг на 9 разрядов вправо (в сторону младших разрядов). Вы же рисуете какие-то суммы длиной от 1 до 512.
Эта утечка в интеграторе квадратур и есть то окно, которое Вы не увидели. Оно использовано в интеграторах, которые накапливают квадратуры. Это экспоненциальное временное окно, только не во временной, а в
частотной области (frequency domain).
Sergey_P, мне кажется, Вы живёте в собственном виртуальном пространстве. Я пытаюсь что-то Вам объяснить, но каждый раз Вы возвращаете настолько искажённую картину, как буд-то Вы приняли изрядную дозу LCD.
Я распишу алгоритм подробно ещё раз с кодом и комментариями. Разумеется, не только для Sergey_P. Если кто только учится, то это может быть примером. Это минимум, который можно как-то улучшать, но принципиально обойти нельзя.
Я ещё раз повторю код, который я дал
в этом сообщении, но на этот раз постараюсь дать самые подробные комментарии. Код сильно упрощен. Многие вещи типа округления младшего разряда, буферизация и пр. не показаны для простоты и ясности кода.
Ещё я изменял некоторые константы. Тогда я квадратурный интегратор сдвигал на 8 разрядов. Сейчас на 9.
После каждой строчки кода я покажу то же самое, но в обычной арифметической нотации.
Зелёным помечен старый текст, синим, что я сейчас добавлю.
Напоминаю: Все интеграторы между прерываниями не обнуляются, а СОХРАНЯЮТ свои значения. В начале каждого блока интеграторы имеют состояние, полученное в предыдущем цикле.
Это вроде как очевидно, но ...Это прерывание случается 16 раз на период Rx/Tx.Код:
// АЦП (ADC) выдаёт код от 0 до 4095. Вычитаем половину, получаем двуполярный сигнал.
i = ADC_ConvertedValue - 2048;
АЦП выдаёт положительное число от 0 до 4095. Удобнее работать с сигналом, который имеет среднее значение 0.
Вычиаем из взятого с АЦП сигнала 2048 и помещаем результат во временную переменную i.
Сделали. Теперь вспоминаем, что АЦП у нас совсем не идеальный. Поэтому, сигнал может иметь какой-то дополнительный сдвиг в минус или плюс. Нам это надо устранить. Как? Делаем интегратор с относительно небольшой утечкой. На нём со временем установится некое значение, которое при коэффициенте утечки 1/1024 (это примерно 0.000977) будет величина в 1024 раза больше средней величины S.
input_average = S * 1024 <-- это не операция, а условие баланса.
Почему так? Потому, что установиться баланс. Каждый отсчёт будет добавлять в input_average некую среднюю величину S, а утечка 1/1024 будет вычитать из input_average точно такую же величину.
input_average = S * 1024
утечка = (S * 1024) * (1/1024) = S // т.е. учечка равна дабавленной средней величине S
Итак. Входной сигнал это i, а мы пока займёмся средней величиной.Код:
// Реализуем "утечку". Каждый раз вычитаем 1/1024 от того, что есть.
// Сдвиг вправо на 10 разрядов это деление на 1024 (2**10)
input_average -= (input_average>>10);
input_average = input_average - (input_average/1024)
Кстати, а зачем, вообще, нам нужна здесь "утечка"? Для того, чтобы "забывать" то, что было давно и "помнить" только "свежий" результат. Утечка это в данном случае экспоненциальное окно во времени.
Теперь добавляем в интегратор среднего значения входной сигнал. Код:
// Накапливаем на интеграторе среднее значение сигнала.
input_average += i;
Код:
[color=#0000BF]input_average = input_average + i
На этом со средним значением закончим. Пора позаботиться о самом входном сигнале.[/color]
// Закидываем в массив входной сигнал за вычетом среднего значения и сразу инкрементируем индекс,
// который крутится от 0 до 15. Обнуляется он в другом прерывании.
input[sample_idx++] = i - (input_average>>10);
В данном случае в одной строке (для компактноси и скорости) выполнено несколько операций.
1) Вычитаем из входного сигнала среднее значение. Помним, что input_average содержит среднюю величину, помноженную на 1024. Поэтому, при вычитании, делим input_average на 1024
i = i - (input_average / 1024)
Теперь можно поместить входной сигнал в массив по текущему индексу, который "крутится" от 0 до 15.
input[sample_idx] = i
В этом прерывании (подпрограмме), которое вызывается, напомню, 16 раз на период, индекс постоянно увеличивается на 1. В другом прерывании, которое выполняется раз за период, индекс обнуляется. Таким образом, он никогда не будет больше 15.
sample_idx = sample_idx + 1Теперь самое интересное. Реализуем СД и получаем квадратуры.Это из прерывания по периоду Rx.Код:
// Тоже самое, что и в предыдущем, но tmp использована только потому, что quad_xxx
// объявлены volatile
Начинаем, конечно, с утечки. Утечка в данном случае она равна 1/512. Вычитаем из квадратурного интегратора quad_sin (это первая квадратура) величину quad_sin/512.
Сначала пмещаем quad_sin/512 во временную переменную tmp. Код:
tmp = quad_sin >> 9;
tmp = quad_sin / 512Код:
quad_sin -= tmp;
Теперь вычитаем величину tmp из интегратора.
quad_sin = quad_sin - tmp
Можно было сделать всё в одной строке, но пришлось разбивать на две из-за особенностей компилятора. Зря я это сюда притащил. Надо было бы это упростить, но я не хочу менять вид кода, который я поместил первый раз.
Далее делаем тоже самое с интегратором quad_cos, который накопливает вторую квадратуру.Код:
tmp = quad_cos >> 9;
quad_cos -= tmp;
На этом этапе мы имеем 16 текущих отсчётов сигнала Rx. Это один период синусоиды сдвинутой на какую-то фазу. Нам надо получить две квадратуры этого сигнала.
В цикле перемножаем 16 полученных в предыдущем прерывании отсчётов с соответствующими отсчетами одного периода синуса для одной квадратуры и косинуча - для другой.
Важно понимать, что этот процесс продолжается бесконечно. Т.е. результат постоянно изменяется и утекает на интеграторах. Этот цикл это только 16 последлних шагов, которые что-то добавляют к тому, что накопилось на данный момент. Если входной сигнал близок к 0, что величина на квадратурном интеграторе "утекает" по экспоненциальному закону.
В аналоговых схемах эта утечка получается за счёт резистора, включенного параллельно накопительной ёмкости.
Данный процесс можно считать Дискретным Преобразованием Фурье (ДПФ), в котором считается только одна гармоника. Утечку можно считать временным окном, которое наложено на эту гармонику в частотной области. Т.е. применено окно не к входному сигналу (временная область), а к его гармонике (частотная область). Окно, тем не менее, временное. Т.е. меняет не соотношение гармоник между собой, а изменение одной гармоники во времени.
Есди нарисовать это в объёме, то это было бы просто. На словах объяснить труднее. Боюсь запутать. Если непонятно - проигнорируйте.Код:
for(i=0; i<16; ++i)
{
// Реализация СД. Все накопленные значения умножаем на кусок синусоиды той же длины.
// Всё, что получилось склдываем и получаем квадратуры.
// Правда, здесь столо бы задать вопром о том что хватит ли мне 32-х битов. Хватит.
quad_sin += input[i] * sin12bit[i];
quad_sin = quad_sin + input[i] * sin12bit[i]
Думаю, здесь всё просто. К интегратору бесконечно добавляем результат перемножения входного отсчёта и соответствующим элементом синуса из таблицы.
Оба значения - 12-ти разрядные величины со знаком. Результат максимум займёт 23 разряда. Т.е. у нас есть ещё много запаса как на иетегрирование, на случай максимального результата, так и на последующую обработку. Можно также применить таблицы 16-разрядных sin и cosКод:
quad_cos += input[i] * cos12bit[i];
}
// Добавил для ясности
// На данный момень sample_idx=16, т.к. предыдущее прерывание вызываеется 16 раз перед этим.
// на каждое это прерывание
sample_idx = 0;
Вот, собственно, и всё. Это минимальная обработка для получения квадратур.
Далее надо поставить фильтры, которые будут выделять полезный сигнал. У меня это будут два фильтра ФВЧ и ФНЧ. Они будут рассматриваться совершенно отдельно, т.к. они ревлизуются совсем в других прерываниях, которые работают на гораздо меньшей частоте.