МАРСОХОД

Open Source Hardware Project

Амплитудная модуляция

radio

Если вы помните, когда-то я сделал из платы Марсоход2 простой радиопередатчик с частотной модуляцией. Сейчас я хочу сделать радиопередатчик с амплитудной модуляцией радиосигнала. Амплитудную модуляцию по идее будет проще сделать, но и тут нужно будет хорошенько подумать и поломать голову.

Для начала, нужно разобраться, что же такое амплитудная модуляция сигнала. Эта статья как раз про это.

На самом деле, мне кажется, очень хорошо про АМ все написано в Википедии.

Казалось бы тут и добавить нечего. Но, как всегда, для разработчика важны детали. Позволю себе скопипастить прямо из википедии:

Пусть
S(t) — информационный сигнал, |S(t)|<1,
Uc(t) — несущее колебание.
Тогда амплитудно-модулированный сигнал Uam(t) может быть записан следующим образом:
   Uam(t)=Uc(t)*[1+m*S(t)].
Здесь m — некоторая константа, называемая коэффициентом модуляции. Формула описывает несущий сигнал Uc(t), модулированный по амплитуде сигналом S(t) с коэффициентом модуляции m. Предполагается также, что выполнены условия:
|S(t)|<1, 0<m<=1.

Выполнение условий необходимо для того, чтобы выражение в квадратных скобках в всегда было положительным. Если оно может принимать отрицательные значения в какой-то момент времени, то происходит так называемая перемодуляция (избыточная модуляция). Простые демодуляторы (типа квадратичного детектора) демодулируют такой сигнал с сильными искажениями.

Из этого следует, что:

  • если полезного сигнала нет, микрофон радиостанции выключен, S(t)=0, то в эфир посылается чистая несущая Uc(t). Несущая частота есть в эфире всегда.
  • если полезный сигнал есть, то кроме несущей частоты будет еще произведение сигнала на несущую частоту: Uc*m*S(t).

Предположим, что несущая частота это синусоида (да так и должно быть!) и пусть полезный сигнал у нас синусоида. Тогда произведение сигнала на несущую:

sin(Fcarrier)*sin(Fsignal)=cos(Fcarrier-Fsignal)/2-cos(Fcarrier+Fsignal)/2

Получается, что в таком модулированном сигнале будет 3 гармонических составляющих: несущее колебание и по бокам от нее в спектре еще два гармонических сигнала половинной амплитуды. Спектр такого сигнала должен выглядеть как-то так:

Спектр амплитудно модулированного сигнала

Что такое спектр сигнала я писал в моей другой «интерактивной статье».

Это все теория.

Хотелось бы применить все эти знания на практике, ведь в конечном счете, я хочу сделать амплитудный модулятор в ПЛИС, там буду оперировать целочисленной логикой и выдавать сигнал я собираюсь через ЦАП. Как там все это будет работать?

Предлагаю, перед программированием для ПЛИС сперва сделать какие-то более простые программные эксперименты. Я собираюсь написать простую программу на языке C/C++, которая будет создавать звуковые WAV файлы с отсчетами сигналов. Анализировать спектр сигнала я буду какой нибудь простой программой, например... Audacity.

Audacity – это программа для редактирования звуковых файлов. У этой программы много возможностей — одна из них — Audacity может строить спектр сигнала. Есть правда ограничение — аудиофайлы могут иметь максимальную частоту дискретизации 384КГц. Это не очень много, но мне для понимания происходящих процессов хватит.

Я хочу в своей программной модели увидеть картину спектра, как на рисунке выше.
Итак, могу ли я, следуя формуле из Википедии, написать на языке C/C++ вот так:


short signal[1024*1024];
short carrier[1024*1024];
short after_mod[1024*1024];

for(int i=0; i<num_samples; i++ )
{
  carrier[i]= (short)(32767 * sin( i * carrier_freq * 2 * M_PI / sampling_rate ));
  signal[i] = (short)(32000 * sin( i * signal_freq * 2 * M_PI / sampling_rate ));
  after_mod[i] = (short)( carrier[i]*signal[i] + carrier[i] );
}


Вроде бы все правильно, carrier и signal – это синусоиды. Потом я их перемножаю и прибавляю несущую carrier. Правильно?

Нет, это не правильно.
В Википедии все красиво написано, но обратите внимание на условие |S(t)|<1, 0<m<=1.
Сейчас, в моей программе это условие не выполняется. Я хочу использовать целочисленную знаковую арифметику и из-за этого возникают некоторые проблемы. Функция sin() возвращает число с плавающей точкой от -1.0 до 1.0
Чтобы перейти в область целочисленных значений я попытался умножить sin() на масштабирующую константу 32767 (максимальное положительное число для типа C/C++ short). Сигнал я умножаю на 30000. Это значит коэффициент модуляции m чуть меньше единицы.

Теперь происходит вот что: при перемножении двух частот sin(Fcarrier)*sin(Fsignal) по формуле из Википедии вообще-то должно получиться число не больше единицы. У меня же при перемножении двух целочисленных типа short получится максимальное число 32767*32767=1073676289. Это совершенно другой масштаб. Нельзя просто взять и добавить сюда (short)carrier потому, что в этом случае получится, что складываются числа разного масштаба.

Кстати, еще один ньюанс: сейчас я вас всех запутаю. Как-то привыкли, что при перемножении двух 16-ти битных чисел получится 32-х битное число. А при перемножении 2-х 16-ти битных знаковых? Вроде бы так же получится 32-х битное число. Максимально положительное число при умножении двух знаковых 16-ти битных short получается (-32768)*(-32768)=1073741824, что в шестнадцатеричном виде 0x40000000. Это почти в 2 раза меньше максимально возможного положительного 0x7FFFFFFF, которое можно хранить в 32-х битном int.

Чтобы получилось правильно нужно следить за масштабами чисел. Прибавлять carrier нужно в том же масштабе, что и результат умножения carrier * signal. Например сделаю вот так:


for(int i=0; i<num_samples; i++ )
{
  carrier[i]= (short)(32760 * sin( i * carrier_freq * 2 * M_PI / sampling_rate ));
  signal[i] = (short)(30000 * sin( i * signal_freq * 2 * M_PI / sampling_rate ));
  int m = carrier[i]*signal[i];
  after_mod[i] = (short)( ( m+carrier[i]*256*128) >> 16);
}


Думаю теперь должно получиться правильно. Полный текст программы приведен ниже.

// genwave.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include 
#define _USE_MATH_DEFINES
#include 

short signal[1024*1024];
short carrier[1024*1024];
short after_mod[1024*1024];

int save_pcm_wave(char* filename,short* psamples,int num_samples)
{
	FILE* f;
	fopen_s(&f,filename,"wb+");
	if(f)
		printf("wave write file handle: %08X\n",f);
	else
	{
		printf("write failed\n");
		return -1;
	}

	//RIFF header
	fwrite("RIFF",1,4,f);
	ULONG len = num_samples*2 + 5*4 + sizeof(PCMWAVEFORMAT);
	fwrite((char*)&len,1,4,f);

	//WAVE format
	fwrite("WAVE",1,4,f);
	fwrite("fmt ",1,4,f);
	len = sizeof(PCMWAVEFORMAT);
	fwrite((char*)&len,1,4,f);
	PCMWAVEFORMAT fmt;
	fmt.wBitsPerSample = 16;
	fmt.wf.nChannels = 1;
	fmt.wf.nSamplesPerSec = 384000;
	fmt.wf.nAvgBytesPerSec = fmt.wf.nSamplesPerSec * fmt.wf.nChannels * fmt.wBitsPerSample / 8;
	fmt.wf.nBlockAlign = 2;
	fmt.wf.wFormatTag = WAVE_FORMAT_PCM;
	fwrite((char*)&fmt,1,sizeof(fmt),f);

	//data
	fwrite("data",1,4,f);
	len = num_samples*2;
	fwrite(&len,1,4,f);
	fwrite(&psamples[0],1,len,f);

	fclose(f);
	return 0;
}

int _tmain(int argc, _TCHAR* argv[])
{
	double sampling_rate = 384000;
	double signal_freq = 750;
	double carrier_freq = 12000;

	int num_samples = signal_freq*1024;

	for(int i=0; i<num_samples; i++ )
	{
		carrier[i]= (short)(32760 * sin( i * carrier_freq * 2 * M_PI / sampling_rate ));
		signal[i] = (short)(30000 * sin( i * signal_freq  * 2 * M_PI / sampling_rate ));
		int m = carrier[i]*signal[i];
		after_mod[i] = (short)( ( m+carrier[i]*256*128) >> 16);
		//after_mod[i] = after_mod[i] & 0xFC00;
	}

	save_pcm_wave("signal.wav",signal,num_samples);
	save_pcm_wave("carrier.wav",carrier,num_samples);
	save_pcm_wave("after_mod.wav",after_mod,num_samples);
	return 0;
}

 Эта программа создает 3 аудио файла в формате WAV. Частота дискретизации 384КГц, моно, 16бит на выборку:

  1. файл carrier.wav содержит синусоиду 12КГц — это будет несущая «радиосигнала»
  2. файл signal.wav содержит полезный сигнал синусоида 750Гц
  3. файл after_mod.wav содержит результирующий амплитудно модулированный сигнал.

Я написал эту программу в среде MS Visual Studio. Откомпилировал прорамму и запускаю ее. Программа отрабатывает и создает мои WAV файлы. Теперь эти файлы можно открыть с помощью Audacity и полюбоваться ими и их спектрами.

Запускаю программу Audacity и из меню Файл => Открыть... нахожу получившиеся WAV и открываю файл signal.wav.

audacity signal 600Гц

Отличненькая такая синусоида получается.
В меню Audacity нахожу Анализ => Построить график спектра...
Получаю вот такую картину:

спектр сигнала синусоиды 750Гц

Обратите внимание, что Audacity может вычислять спектр пользуясь различными математическими методами, метод можно выбрать в выпадающем списке "Функция". Однако самое главное - выбрать количество выборок сигнала для анализа. Есть еще один выпадающий список "Размер".  Если сейчас выбрать для вычисления спектра большее число выборок, то картина изменится линия спектра станет тоньше. На самом деле все логично. Чтобы точнее сказать что-то о сигнале, о его частоте - его нужно дольше наблюдать. 

Кстати еще один ньюанс. Почему я в своих экспериментах взял частоту полезного сигнала 750Гц, а не какую-то другую? Очень важно понимать суть происходящих процессов и что на самом деле я пытаюсь измерить или посмотреть. Дело в том, что каждый анализатор спектра вычисляет спектр сигнала в некотором временном окне. Как правило размер окна - это блоки из 512 или 1024 или 4096 выборок, в общем степень двойки. Такова особонность алгоритмов Фурье и прочих. При этом, анализатор считает, что сигнал бесконечен и периодичен - поэтому он дорисовывает сигнал влево и вправо просто многократно соединяя текущий блок выборок, бесконечно сшивает сигнал. В моем WAV файле частота дискретизации объявляется 384КГц.  Значит один отсчет длится 1/384000 секунд. Временное окно из 1024 выборок будет длительностью 1024/384000 секунд. Если я буду рассматривать частоту сигнала 750Гц, то в этом временном окне поместится ровно 2 периода сигнала: 1024*750/384000=2. Значит спектральный анализатор бесконечно соединяя временное окно сигнала сделает это безшовно.

Если взять для исследования другую частоту, то в указанном временном окне поместится не ровное число периодов. Спектральный анализатор соединяя временное окно получит скачки сигнала на краях временного окна. Спектр получится смазанным.

Вот, например, что мы увидим, для сигнала 600Гц:

aud spectr signal1

Кажется ерунда какая-то? Да нет, просто измерения спектра в данном случае выполены не очень корректно. Выбранное временное окно для измерения спектра сигнала представляет временной интервал не кратный периоду сигнала. Вот это нужно иметь ввиду при математических экспериментах.

С этим разобрались. Теперь посмотрим на сигнал несушей частоты 12КГц (файл carrier.wav):

audacity carrier 12KHz

Его спектр audacity показывает вот так:

audacity spectrum carrier 12KHz 

 Теперь самое интересное: промодулированный сигнал (перемноженные синусоиды плюс несущая, файл after_mod.wav):

амплитудно модулированный сигнал

Видим вот такой спектр этого сигнала:

спектр модулированного сигнала

Ну вот, примерно так и получилось как и ожидалось: несущая и еще слева и справа палочки полезного сигнала. Произошел перенос спектра вверх на несущую частоту. Все работает!

Хотел закончить эту статью на такой позитивной ноте, но, вспомнил еще один ньюанс. Смотрите. В моих вычислениях выходной сигнал получился знаковый 16-ти битный. Я собираюсь сделать модуляцию внутри ПЛИС и вычисленные значения выдавать на цифроаналоговый преобразователь (ЦАП) в параллельном коде. Влияет ли разрядность ЦАП на выходной сигнал?

Конечно влияет. На самом деле получается, что на выходе ЦАП сигнал меняется скачками, ступеньками. Это значит,что модулирующий сигнал это не чистая синусоида, но и многие ее гармоники. Каждая гармоника в свою очередь так же является модулирующей частотой. Значит полезный сигнал переносится во многие части спектра.

Это легко увидеть если в моей программе убрать комментарий из строки

after_mod[i] = after_mod[i] & 0xFC00;

Это выражение подавляет младшие биты в выходном сигнале, делает его более ступенчатым. Какой получится спектр модулированного сигнала?
Посмотрите:

noised spectrum

Из этого следует важный вывод - после модулятора и ЦАП обязательно нужно ставить полосовой фильтр, который будет выделять полезный сигнал и подавлять вредные составляющие. Тем не менее, хочу сразу успокоить общественность. В этой моей модели соотношение частот несущей и полезного сигнала слишком мало. Поэтому картина выглядит столь ужасно.

В реальной жизни, если я захочу передавать АМ сигнал на несущей 1МГц, то ее гармоники будут 3МГц, 5МГц, 7МГц и так далее. Полезный аудио сигнал находится в полосе скажем 20КГц. Полосовой фильтр может получиться сам собой, если подключить частотно-избирательную антенну или хотя бы просто кусок провода... так что в эфир весь этот шум из моего примитивного радиопередатчика вряд ли пойдет. Но, все равно нужно помнить что и как происходит в моей системе..

PS: в принципе проект АМ передатчика из платы Марсоход2 я уже сделал и он в общем работает! Только с антенной беда, не очень получается. На неделе сделаю статью..

 

Комментарии  

0 #3 Alexey 20.04.2015 21:02
Ребят, почему нет проектов DSP на ПЛИС? Реализация умножения, деления, работа с floating point!
0 #2 Uzix 18.04.2015 12:45
> Кажется ерунда какая-то? Да нет, просто измерения
> спектра в данном случае выполены не очень корректно.

Можно использовать оконную функцию Blackman-Harris , она корректно отрабатывает такие ситуации.
0 #1 UA3MQJ 11.04.2015 17:00
Привет!
Ну 1-30 МГц - это короткие волны. Там длина волны измеряется в метрах. Такой же длины и антенны. Но не обязательно, нужно только правильно согласовать. Но в любом случае, даже работая на резистор, сигнал будет принят КВ приемником. И еще, модуляция, мне кажется, должна быть меньше.

Добавить комментарий


Защитный код
Обновить


GitHub YouTube Twitter
Вы здесь: Начало Статьи о разном Амплитудная модуляция