Главная » Статьи » Мои статьи |
ПМО радиоприемника «Пион-DSP» Введение Решил создать описание программ радиоприемника Пион-DSP, поскольку на форумах cqham.ru мне задают различные вопросы по цифровой обработке сигналов. Итак. Главная функция main: void main (void) { PLL_I2S_init();//Инициализирую PLL I2S mismatch_init ();//Инициализирую параметры для компенсации дисбаланса GetWindowCoeff (2);//рассчитываю коэффициенты оконной функции fft_init();//настраиваю БПФ на нужные длины agc_options_init();//инициализирую параметры initialization (); while (DMA1_Stream3->NDTR>2*FFT_FILTER_SIZE);//Жду пока заполнятся первая часть буфера while (1) { play_rx(); } } С самого начала осуществляется настройка процессора и его периферии. Назначение функций понятно из названия и комментариев, содержимое функций можно посмотреть в моих исходниках. Функция initialization (), где осуществляются настройки процессора, стоит не на первом месте а в конце, поскольку в ней есть настройка периферии и кодеков, что требует заранее подготовленной периферии. Далее с помощью while (DMA1_Stream3->NDTR>2*FFT_FILTER_SIZE) я жду пока заполнится первая часть буфера, т.е. 1024 значения.
DSP и системный процессора взаимодействуют друг с другом по интерфейсу SPI. Все взаимодействие осуществляется 16-тиразрядными данными с исользованием ДМА. Обмен ведется всегда одинаковым блоком данных по 514 32-х разрядных слов (1028 16-ти разрядных). Процессоры по сути не знают ничего друг о друге – системный процессор запустил обмен и что-то получил, а DSP периодически ложит данные с спектром для панорамы в буфер для передачи и чего-то принимает. Очень удобное и универсальное решение, не надо придумывать хитрый протокол обмена, дергать ножками для настройки обмена. Да, есть избыточность, но на быстродействие это никак не влияет. Настройки для режимов работы процессора DSP получает от системного в следующем виде: struct common_options//структурный тип /* содержит настройки демодуляции*/ { uint priznak_mode;//численное значение признака модуляции uint num_filter;//номер фильтра фильтров от 0 до n uint agc_en;//вкл, выкл АРУ uint agc_level;//уровень АРУ uint notch_en;//вкл нось uint shift_en;//вкл shift uint shift_value;//значение ШИФТ фильтра uint nb_threshold_value;//Значение ночь Фильтра uint nb_en; uint gain; uint agc_ref_level; uint key_beep_gain; uint binaural_sound_en; uint squelch; uint buf [500]; };
Указанные поля имеют следующие значения: priznak_mode – признак модуляции, возможные значения приведены в таблице
num_filter – номер используемого фильтра
agc_en – признак АРУ – 1- включено, 0 - выключено. В режимах АМ и FM в DSP процессоре АРУ всегда включено, независимо от внешних настроек. agc_level – определяет значение времени постоянной АРУ:
notch_en – признак включения автоматического ночь фильтра – 1- включен, 0 - выключен. shift_en– признак включения шифт фильтра – 1- включен, 0 - выключен. shift_value - значение шифт фильтра, находится в диапазоне 0-3000, при этом: - значение >1500 соответствует подрезанию снизу; - значение <1500 соответствует подрезанию сверху; nb_threshold_value – значение порога ноис блэнкера (NB), в диапазоне от 300 до 1800, что соответствует порогу от 3 до 18. nb_en – признак включения ноис блэнкера:
Gain – параметр цифрового усиления, от 1 до50. При значении 0 на выходе приемника будет тишина. agc_ref_level – определяет максимальный уровень выходного сигнала, который использется в алгоритме АРУ, принимает значение от 1 до 20. При значении 0 на выходе приемника будет тишина. Максимальное значение ЦАП равно 8388608, делится на 20. В итоге получается дискрет 419430. С этим дискретом и выбирается agc_ref_level. key_beep_gain – громкость «пищания» при нажатии на клавишу. binaural_sound_en - признак включения бинаурального звучания (СТЕРЕО) – 1- включен, 0 - выключен. Squelch – значение порога шумоподавителя, в дБ. Старший бит определяет состояние Squelch – 1- включен, 0 - выключен. buf [500] – не используется. Для того, чтобы можно было выбрать основные настройки в исходниках есть следующие дефайны: NAME_PROJECT_TULIP_DSP – надо прописать, если выбран проект Тюльпан NAME_PROJECT _DSP - надо прописать, если выбран проект Пион SCALING_SIGNAL - надо прописать, если требуется работать с входными данными в диапазоне -1…1. Т.е. данные масштабируются при приеме с АЦП и перед передачей в ЦАП. По сути это нужно при работе с очень большими числами, однако при радиоприеме таких цифр нет, соответственно предопределять ничего не нужно BASEBAND_SPECTRUM - надо прописать, если требуется выводить широкополосный спектр входного сигнала, полученного с АЦП. AUDIO_OUTPUT_SPECTRUM - BASEBAND_SPECTRUM - надо прописать, если требуется выводить узкополосный спектр демодулированного выходного звукового сигнала. CPU_FREQ 168000000– используется для расчета в функция задержки delay_us, delay_ms. FFT_FILTER_SIZE 2048 - надо прописать, какая требуется длина БПФ при демодуляции и фильтрации. Значение может быть равно: 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192. Заметил, что стандартные функции БПФ из CMSIS некорректно работают при длине БПФ 1024. SOFTWARE_DC_OFFSET_REJECTION - надо прописать, если требуется программно убирать постоянную составляющую входного сигнала. Я не использую, поскольку такую функцию аппаратно выполняют используемые мной аудиокодеки CS4221, CS4272. SAMPLE_RATE 44100 – надо прописать требуемую частоту дискретизации. У меня стоит 44100. Можно задать: 8000, 16000, 22050, 32000, 44100, 48000, 96000. Тут все определяется быстодействием процессора. Чем больше частота дискретизации, тем загрузка выше. FFT_IQ_imbalanse_size 128 – размер БПФ для алгоритма компенсации квадратурного имбаланса. Данное значение является оптимальным.
Функция play_rx() собственно и реализует радиоприем, имеет следующий вид: void play_rx(void) { { #ifdef NAME_PROJECT_TULIP_DSP options.priznak_mode=1; options.num_filter=0; options.agc_en=1; options.gain=10; options.agc_ref_level=10; #endif } input_buffer_state=Get_Input_Buffer_state (); if (input_buffer_state)//если 1 Get_normalized_buffer((struct common_COMPLEX_int *)&input_buffer_rx[0],0); else Get_normalized_buffer((struct common_COMPLEX_int *)&input_buffer_rx[FFT_FILTER_SIZE/2],0); copy_input_cfft_buffer (); DMA_mem_copy ((int)&tmp_buffer[(FFT_FILTER_SIZE/2)],//копирование tmp буфера (int)&tmp_buffer[0], (FFT_FILTER_SIZE/2), 0);//копирую ст. часть в младшую #ifdef BASEBAND_SPECTRUM Get_Spec_Mag ((struct common_COMPLEX* )&cfft_intermediate_buffer);//вычисляю спектр и передаю его в системный проц #endif IQ_imbalance_compensation (options.priznak_mode); arm_cfft_f32(CFFT_init_struct, (float*)&cfft_main_buffer,0,1); #ifdef SOFTWARE_DC_OFFSET_REJECTION DC_OFFSET_REJECTION ((struct common_COMPLEX* )&cfft_main_buffer);//не нужно, т.к. это уже есть в уодеке #endif switch (options.priznak_mode) { case 0:Get_SSB_demodulation ();//USB break; case 1:Get_SSB_demodulation ();//LSB break; case 2:Get_AM_demodulation ();//AM break; case 3:Get_FM_PLL_demodulation ();//AM break; default: break; } #ifdef SCALING_SIGNAL for (int i=FFT_FILTER_SIZE/2;i<FFT_FILTER_SIZE;i+=4) { tmp_buffer[i]=SCALE*tmp_buffer[i]; tmp_buffer[i+1]=SCALE*tmp_buffer[i+1]; tmp_buffer[i+2]=SCALE*tmp_buffer[i+2]; tmp_buffer[i+3]=SCALE*tmp_buffer[i+3]; } #endif S_metr((float*)&tmp_buffer[FFT_FILTER_SIZE/2]); agc_control ((float*)&tmp_buffer[FFT_FILTER_SIZE/2]); beeper ((float*)&(tmp_buffer[FFT_FILTER_SIZE/2]),FFT_FILTER_SIZE/2); Pseudo_stereo_and_filtering((float*) &tmp_buffer, (struct common_COMPLEX *) &cfft_main_buffer[FFT_FILTER_SIZE/2]); #ifdef AUDIO_OUTPUT_SPECTRUM Get_Spec_Mag ((struct common_COMPLEX* )&cfft_main_buffer);//вычисляю спектр и передаю его в системный проц #endif copy_output_cfft_buffer(); if (input_buffer_state==0) Get_normalized_buffer((struct common_COMPLEX_int *)&output_buffer_rx[FFT_FILTER_SIZE/2],1); else Get_normalized_buffer((struct common_COMPLEX_int *)&output_buffer_rx,1); while(input_buffer_state==Get_Input_Buffer_state ()); }
Кусок кода #ifdef NAME_PROJECT_TULIP_DSP options.priznak_mode=1; options.num_filter=0; options.agc_en=1; options.gain=10; options.agc_ref_level=10; #endif предназначен для отладки с платой Тюльпана. Там пока нет системного процессора, а проверить с чем-то нужно.
Я применяю при работе с аудиокодеками формат передачи I2S. Передаваемые по этому формату данные имеют 24 разряда, где 23 разряд – знаковый. Данные принимаются процессором словами по 16 бит. Поэтому после приема их необходимо правильно «склеить». Принятые данные лежат в памяти в следующем виде:
Пример преобразования данных АЦП (переменная input[i].re) в нормальный формат для процессора (переменная input[i].re): tmp=((input[i].re& 0xFFFF)<<8)|((input[i].re & 0xFFFF0000)>>24); if ((tmp&0x800000)==0x800000) input[i].re=(tmp|0xFF000000); else input[i].re=tmp; Перед передачей данных к ЦАП проводится аналогичное преобразование. Пример преобразования данных из формата int процессора (переменная input[i].re) в формат данных ЦАП (переменная input[i].re): tmp=input[i].re; input[i].re=(((tmp)&0xffFF00)>>8)|(((tmp)&0xffff)<<24); tmp=input[i].im; input[i].im=(((tmp)&0xffFF00)>>8)|(((tmp)&0xffff)<<24); Указанные преобразования выполняет функция Get_normalized_buffer.
В составе блоков указаны используемые программы. Все вычисления проводятся с использование формата данных float. Приведенные структурные схемы следует смотреть вместе с моими исходниками.
Отдельно рассматривать каждый блок я не буду, поскольку в них по сути мало чего уникального и многое уже описано в литературе. Программы ноис блэнкера и шумоподавителя я скоммуниздил из DTTSP, которая используется в PowerSDR, дабы не изобретать лесопед.
При обработке в частотной области применяется БПФ с перекрытием. Конкретно у меня – Overlap and save. Т.е. в обработке текущих значений применяются старые значения. У меня степень перекрытия – 50%, т.е. при обработке используются 50% новых и 50% старых значений. После ОБПФ для дальнейшего использования используются последние 50% значений. Пример такой обработки для БПФ длиной 2048 приведен ниже.
В цифровой обработке сигнала происходит все тоже самое, что и в аналоговой технике с учетом особенностей представления данных. Пример фильтрации сигнала я изобразил на рисунке ниже. Исходный сигнал представлен в виде отсчетов АЦП, получаемых с определенной частотой дискретизации Fs. Чтобы из набора входных отсчетов получить спектр сигнала нужно над входными отсчетами выполнить преобразование Фурье. На практике выполняют БПФ – быстрое преобразование Фурье. Результат БПФ есть практически готовый спектр, который представляет диапазон частот от –Fs/2 до +Fs/2. Например, получено 2048 отсчетов комплексного сигнала I+jQ, где I – данные АЦП синфазного канала, Q – данные АЦП квадратурного (сдвинутого по фазе на 90 градусов) канала. Далее над этих значениями выполним БПФ длиной 2048 и получим 2048 комплексных результатов. Пусть это будет массив структур FFT[2048], который имеет поле re и im. Положительные частоты представлены данными FFT[0]- FFT[1023], FFT[0]- нулевая частота (постоянка), FFT[1023] – частота (Fs/2- ∆f), где ∆f – шаг частотной сетки БПФ, вычисляется как Fs/2048. Отрицательные частоты представлены данными FFT[1024]- FFT[2047], FFT[1024]- частота (-Fs/2) FFT[2047] – частота (- ∆f). Чтобы вычислить амплитуду той или иной частотной компоненты спектра нужно вычислить: A[i]=sqrt(FFT[i].re* FFT[i].re+ FFT[i].im* FFT[i].im). Можно еще много чего вычислить – например, фазу сигнала на той или иной частоте. Или при демодуляции SSB выбрать нужную полосу – обнулить все, что ниже или выше нуля. Сразу скажу, что произвольные игры аля занулить отдельную частотную компоненту (как в ночь фильтре) здесь не пройдут. Все должно быть «чинно и благородно». Следует избегать возможных разрывов фазы в спектре. Таким образом по сути БПФ переводит представление сигнала из временных отсчетов в частотную область. ОБПФ делает все наоборот – переводит спектр в набор временных отсчетов I+jQ. Я при выполнении фильтрации сигнала представляю себе картинки с спектрами. Кроме фильтрации, спектры можно смещать по частоте вправо или влево – это эквивалентно смещению по частоте выше или ниже в аналоговой технике.
Я кратко расскажу о демодуляции сигнала. Пишу на память, без использования умных книжек. Подали на вход аудиокодека сигнал с SDR тракта. В аудиокодека два АЦП, один оцифровывает канал I, другой – канал Q. Результат измерения двух АЦП – комплексный сигнал I+jQ. Демодуляция АМ Здесь все интуитивно просто и понятно. Сделал БПФ, отфильтровал сигнал перемножением спектров исходного сигнала и фильтра, сделал ОБПФ. После ОБПФ получил отфильтрованный сигнал I+jQ. Далее вспоминаем математику и комплексные числа. Что нужно сделать, чтобы вычислить модуль комплексного числа? Правильно, нужно извлечь квадратный корень из суммы квадратов действительной и мнимой части. Амплитуда комплексного сигнала и равна этому модулю: А= Но продетектированный таким образом сигнал имеет широкую полосу, чтобы ее ограничить, надо снова сигнал отфильтровать. У меня это делается в функции Pseudo_stereo_and_filtering(). Демодуляция FМ По сравнению с АМ здесь все немного сложнее. Сделал БПФ, отфильтровал сигнал перемножением спектров исходного сигнала и фильтра, сделал ОБПФ. После ОБПФ получил отфильтрованный сигнал I+jQ. Далее в работу вступает петля ФАПЧ, где вычисляется разность фаз сигнал и опорного колебания, подстройка петли ФАПЧ. Комплексное число, представленное в тригонометрической форме выглядит так: I+jQ=A*cosφ+A*j*sinφ, где А – модуль комплексного числа. Отсюда tgφ=Q/I, φ=arctg(Q/I). Когда получили с детектора ФАПЧ значение фазы, чтобы получить значение частоты нужно вычислить разность фаз текущего и предыдущего измерений. Частота – есть производная фазы по времени. Как-то так. Можно обойтись без ФАПЧ, считать все в лоб, я так делал. Качество демодулированного сигнала при этом получилось не ахти. Сейчас я применяю функцию демодуляции FM, взятую из DTTSP, используемую в PowerSDR. Но продетектированный таким образом сигнал имеет широкую полосу, чтобы ее ограничить, надо снова сигнал отфильтровать. У меня это делается в функции Pseudo_stereo_and_filtering(). Демодуляция SSB Этот процесс на примере демодуляции USB можно представить так: Изначально есть спектр входного комплексного сигнала, полученный аналогично как в случае с АМ и FM – через БПФ входных отсчетов АЦП. В спектре есть сигнал, пусть с АМ, у которого есть LSB и USB, которые расположены симметрично относительно несущей частоты – Fн. Все что выше Fн=Fs/4 – это USB. Для частоты дискретизации 44100Гц Fs/4=11025Гц. Чтобы USB можно было нормально слышать, надо сделать перенос на Fs/4 и перенести USB к нулю. Перенос спектра делается протым копированием элементов массива результата БПФ. После копирования в диапазоне 0-Fs/2 есть два одинаковых сигнала USB (левый нижний спектр на рисунке). В этом ничего страшного нет, второй сигнал уберется после фильтрации, как показано на правом нижнем спектре рисунка. Я на спектре входного сигнала не показал наличие сигналов в диапазоне частот - Fs/2 … 0Гц. Они конечно же есть. Поэтому перед дальнейшим выполнением ОБПФ надо конечно обнулить все значения спектра в диапазоне частот - Fs/2 … 0Гц, убрав таки образом ненужную боковую полосу- в данном случае это LSB. Демодуляция LSB осуществляется примерно так же
2. Подавление зеркального канала На мой взгляд тема подавления зеркального канала в SDR заслуживает особого внимания. К сожалению, в аналоговых SDR трактах зеркальный канал давится примерно на 40-50 дБ, а то и меньше. В моем варианте приемника DR2 от YU1LM на диапазоне 28МГц подавление зеркалки составляет около 30дБ. На 14МГц – около 40дБ. Такие цифры для радиолюбительской техники являются достаточно низкими, потенциально существует возможность приема по зеркальному каналу на любительских диапазонах. В отличии от супергетеродинных приемников, в SDR зеркальный канал отстоит на частоту Fs/2 и попадает в полосу любительских диапазонов при их прослушивании. Достаточно большое количество статей посвящено проблеме подавления зеркалки. Описано много способов подавления зеркалки. Эти способы можно разделить на 2 группы: - программные, где требуется проведение определенных вычислений; - аппаратно-программные, где требуется и разработка программ и наличие тестового оборудования, например калибровочного генератора. Аппаратно-программные способы я сразу отбросил – наличие тестового оборудования может существенно усложнить конструкцию. Поэтому выбор пал на программные способы. Многие из них имеют свои недостатки – это либо большая вычислительная сложность, либо недостаточная эффективность. В процессе экспериментов я применял несколько программных способов компенсации квадратурного имбаланса сигнала, который и приводит к возникновению зеркального канала. В ходе экспериментов убедился, что имбаланс квадратурного сигнала по фазе и амплитуде зависит еще и от частоты в пределах полосы пропускания по НЧ. Связано это с тем, что АЦП аудиокодека имеют разброс по амплитуде и фазе. Например, в кодеке cs4221 каналы отличаются по фазе на 0,5 градуса. Входные антиалиасинговые фильтры так же отличаются по усилению и фазе. Из всего объема информации мне больше всего понравились статьи Маркуса Виндиша. У него много почти однотипных статей по компенсации имбаланса квадратурного сигнала. В ранних статьях есть ошибки. Я рекомендую почитать статью: «Marcus Windisch and Gerhard Fettweis: «ON THE PERFORMANCE OF STANDARD-INDEPENDENT I/Q IMBALANCE COMPENSATION IN OFDM DIRECT-CONVERSION RECEIVERS». Очень толковая и достаточно понятная статья. Суть описываемого метода заключается в том, что имея накопленные и рассчитанные определенным образом данные возможно использовать для компенсации имбаланса комплексного сигнала по амплитуде и фазе. При этом не требуется применение калибровочного оборудования, например генераторов. Статистические данные накапливаются непосредственно с использованием самого входного сигнала. Преимущество метода заключается в том, что он позволяет наблюдать и компенсировать зависимость амплитуды и фазы входного квадратурного НЧ сигнала. В статье многие подробности не отражены. Например, в статье сказано, что для расчетов должен использоваться полезный сигнал. Как понять, что мы имеем дело именно с полезным сигналом, а не с шумом? Вот таких нюансов, которые влияют на качество работы способа достаточно много. В моей реализации в итоге я получил улучшение подавления зеркального канала не менее 40дБ. Отмечу, что в моей интерпретации алгоритм является адаптивным – программа программе требуется примерно 0,5 чтобы полностью подавить принимаемый зеркальный канал, до 2 секунд - чтобы полностью подстроиться под существующую эфирную обстановку. Итак, давайте рассмотрим программу: void IQ_imbalance_compensation (int mode) { int i=0; switch (mode) { case 0: I_START = I_START_SSB; break; case 1: I_START = I_START_SSB; break; case 2: I_START = I_START_AM_FM; break; case 3: I_START = I_START_AM_FM; break; default: break; } for (i=0;i<MISMATCH_COUNT;i++) { copy_overlap_input_buffer ((struct common_COMPLEX*)&IQ_imbalance_input, (struct common_COMPLEX*)&cfft_main_buffer[i*FFT_IQ_imbalanse_size/2], (struct common_COMPLEX*)&IQ_imbalance_cfft_input); arm_cfft_f32(CFFT_imbalance_init_struct, (float*)&IQ_imbalance_cfft_input,0,1); GetSpecPwr ((struct common_COMPLEX*)&IQ_imbalance_cfft_input); UpdateIQParams ((struct common_COMPLEX*)&IQ_imbalance_cfft_input); CompIQ((struct common_COMPLEX*)&IQ_imbalance_cfft_input); if (mode<=1) arm_cmplx_mult_cmplx_f32((float*)&IQ_imbalance_cfft_input,//Здесь дополнительная фильтрация ФВЧ (float*)&HP_FILTER, (float*)&IQ_imbalance_cfft_input, (FFT_IQ_imbalanse_size)); else; arm_cfft_f32(CFFT_imbalance_init_struct, (float*)&IQ_imbalance_cfft_input,1,1);
copy_overlap_output_cfft_buffer((struct common_COMPLEX*)&IQ_imbalance_cfft_input, (struct common_COMPLEX*)&cfft_main_buffer[i*FFT_IQ_imbalanse_size/2]);
} } В функции применяются БПФ и ОБПФ длиной 128. Функция обрабатывает входные значения в временной области, на выходе так же получаются значения в временной области. С помощью функций copy_overlap_input_buffer (),copy_overlap_output_cfft_buffer() осущесвляется копирование данных в промежуточные буферы для реализации БПФ с перекрытием, о чем я писал выше. Функция GetSpecPwr() вычисляет среднее значение мощности сигнала в спектре, а так же находит минимальное значение, которое принимается за уровень шума. При расчете среднего значения применяется экспоненциальное усреднение, как наиболее простое в реализации. В функции UpdateIQParams() происходит: 1) анализ наличия сигнала. Если есть сигнал, то запускается набора статистики и инкрементируется счетчик присутствия сигнала m_iCarr_Pres_Cnt[i]; 2) набор статистик по формуле 5 указанной мной статьи: buf_m_fK1K2 – числитель, buf_m_fPWR – знаменатель; 3) пересчет статистик происходит каждые NUM_FRAMES_FOR_UPDATE циклов, здесь m_fK1K2 уже полностью соответствует формуле 5. Если статистики были набраны не менее чем NUM_FRAMES_FOR_COLLECTION раз, то сбрасывается счетчик «забывания» (m_iCarr_Reset_Cnt[iBinNum]=0;), отмечается что на данной частоте есть сигнал (m_bCarrDet[iBinNum]=1;), рассчитываются m_fK1K2. Если статистики были набраны меньшее количество (это значеие m_iCarr_Pres_Cnt[i]) раз, чем NUM_FRAMES_FOR_COLLECTION, то сбрасывается счетчик набора статистик (m_iCarr_Pres_Cnt[iBinNum]=0;), отмечается, что сигнал на данной конкретной частоте отсутствует (m_bCarrDet[iBinNum]=0;) и запускается счетчик «забывания» (m_iCarr_Reset_Cnt[iBinNum]++;), который увеличивается. Если в дальнейшем значение этого счетчика превысит значение NUM_BLOCKS_FOR_RESET, то накопленные статистики будут обнулены. 4) из m_fK1K2 рассчитываются значения коэффициентов α и β по формулам 8. Для упрощения дальнейших расчетов α и β вычисляются в виде комплексного параметра Alpha_Beta_cmplx, при этом Alpha_Beta_cmplx[].re соответствует α, Alpha_Beta_cmplx[].im – β. Таким образом, эта функция обеспечивает адаптивность набора и обновления данных. void UpdateIQParams(struct common_COMPLEX* input_spec) { int i=0, iBinNum=0; struct common_COMPLEX m_fK1K2; float tmp_m_fMinPwr=0; i=I_START;//В зависимости от моды смотрю начиная с разных бинов: // Для АМ и ФМ с первого бина, т.к. там все попадает в зеркальный канал //Для SSB только выше четверти частоты дискретизации, т.к. все что ниже на зеркалку не влияет. if(m_fMinPwr==0.0f) tmp_m_fMinPwr=1;//чтоб избежать деления на ноль else tmp_m_fMinPwr=m_fMinPwr; while (i < (FFT_IQ_imbalanse_size/2)) { if (((m_fAvgPwr[FFT_IQ_imbalanse_size-i]/tmp_m_fMinPwr) <= THRESHOLD) &&((m_fAvgPwr[i]/tmp_m_fMinPwr) <= THRESHOLD)) //Если меньше порога, то { } else { m_iCarr_Pres_Cnt[i]++; buf_m_fPWR[i]+=(input_spec[i].re+input_spec[FFT_IQ_imbalanse_size - i].re) *(input_spec[i].re+input_spec[FFT_IQ_imbalanse_size - i].re) +(input_spec[i].im - input_spec[FFT_IQ_imbalanse_size-i].im) *(input_spec[i].im-input_spec[FFT_IQ_imbalanse_size- i].im) ; buf_m_fK1K2[i].re += (input_spec[i].re*input_spec[FFT_IQ_imbalanse_size - i].re) - (input_spec[i].im * input_spec[FFT_IQ_imbalanse_size- i].im);
buf_m_fK1K2[i].im += (input_spec[i].re*input_spec[FFT_IQ_imbalanse_size- i].im) +(input_spec[i].im * input_spec[FFT_IQ_imbalanse_size - i].re); } ++i; } if (m_iFrameNum == (NUM_FRAMES_FOR_UPDATE)) { m_iNumCarriers=0; for (iBinNum=I_START;iBinNum<FFT_IQ_imbalanse_size/2;iBinNum++) {//если не было сигнала, то обнуляю счетчики if ((m_iCarr_Pres_Cnt[iBinNum]) <(NUM_FRAMES_FOR_COLLECTION)) { m_iCarr_Pres_Cnt[iBinNum]=0; m_bCarrDet[iBinNum]=0; m_iCarr_Reset_Cnt[iBinNum]++; if (m_iCarr_Reset_Cnt[iBinNum]>NUM_BLOCKS_FOR_RESET) { m_iCarr_Reset_Cnt[iBinNum]=0; buf_m_fK1K2[iBinNum].re =buf_m_fK1K2[iBinNum].im =buf_m_fPWR[iBinNum]=0; } } else { m_fK1K2.re=(buf_m_fK1K2[iBinNum].re) / ((buf_m_fPWR[iBinNum])); m_fK1K2.im=(buf_m_fK1K2[iBinNum].im)/ (buf_m_fPWR[iBinNum]); Alpha_Beta_cmplx[iBinNum].im = -2*m_fK1K2.im; Alpha_Beta_cmplx[iBinNum].re=(1 - (Alpha_Beta_cmplx[iBinNum].im * Alpha_Beta_cmplx[iBinNum].im) - (4*m_fK1K2.re)); Alpha_Beta_cmplx[iBinNum].re = sqrtf(Alpha_Beta_cmplx[iBinNum].re); m_iCarr_Reset_Cnt[iBinNum]=0; m_bCarrDet[iBinNum]=1; m_iNumCarriers++; //Суммирую количество несущих m_iCarr_Pres_Cnt[iBinNum]=0; //обнуляю счетчики } } InitParams(m_iNumCarriers); m_iFrameNum = 0; } ++m_iFrameNum; } В дальнейшем происходит интерполяция значений α и β по тем частотам, где сигнал не зарегистрирован. Интерполяция проводится методом наименьших квадратов с использованием полиномов Чебышева. За это отвечает функция InitParams(). В этой функции осуществляется расчет параметров К1 и К2 по формуле 6 и дальнейшая нормировка для использования формулы 9. void InitParams(int n_carriers) { int i; float tmp; if (n_carriers>0) { Chebyshev_CMPLX ((struct common_COMPLEX *)&Alpha_Beta_cmplx); for (i = 1; i < FFT_IQ_imbalanse_size/2; i++) { tmp=(2*Alpha_Beta_cmplx[i].re); m_cpxK2[i].re = (1 - Alpha_Beta_cmplx[i].re)/tmp; m_cpxK2[i].im =m_cpxK1[i].im= -(Alpha_Beta_cmplx[i].im)/tmp; m_cpxK1[i].re = (1 + Alpha_Beta_cmplx[i].re)/tmp; } } Далее выполняется компенсация спектра с помощью функции CompIQ() и в исходном входном сигнале практически полностью убирается зеркальный канал. Набор статистик и компенсация происходят для пар бинов (частот спектра), которые в спектре симметричны относительно нуля и имеют одинаковый модуль порядкового номера (например, -1 и 1, -2 и 2 и т.д.) В функциях фигурирует переменная I_START, которая определяет, с какого бина (частоты) нужно набирать статистику. Например, зачем для SSB набирать статистику с частоты 0Гц, если реально радиоприем ведется начиная с частоты +11025Гц или -11025Гц.
3. Литература В процессе изучения ЦОС рекомендую почитать следующую литературу:
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Просмотров: 3408 | |
Всего комментариев: 0 | |