Почему меняется размер закодированного сообщения
Перейти к содержимому

Почему меняется размер закодированного сообщения

Почему меняется размер закодированного сообщения

Base64 — стандарт кодирования байтов при помощи только 64 символов (A-Z, a-z и 0-9 (62 знака) и 2 дополнительных символа, зависящих от системы реализации).

Одним байтом можно закодировать 256 значений (2 8 бит), в то время как Base64 только 64 (2 6 бит).

Из этого следует соотношение 2 8 + 2 8 + 2 8 = 2 6 + 2 6 + 2 6 + 2 6 , т.е. каждые 3 исходных байта кодируются в 4 байта.

Можно вывести формулу:

[Кол-во байт в Base64] = ([Исходное кол-во байт] / 3) * 4 или [Кол-во байт в Base64] = 4 * [Исходное кол-во байт] / 3 или [Кол-во байт в Base64] = 4/3 * [Исходное кол-во байт]

Например: Файл размером 1500 байт в Base64 будет занимать 2000 байт (т.е. на 1/3 больше).

Рассмотрим обратную задачу: есть Base64-строка и нужно понять сколько это будет байтов при раскодировке. Из формул выше можно выразить исходное кол-во байт:

[Исходное кол-во байт] = 3/4 * [Кол-во байт в Base64] или [Исходное кол-во байт] = 0.75 * [Кол-во байт в Base64]

Например: Base64-строка размером 2000 байт, декодируется в 1500 байт (т.е. на 1/4 меньше);

Как работают кодировки текста. Откуда появляются «кракозябры». Принципы кодирования. Обобщение и детальный разбор

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

Чтобы получить детальное понимание этого вопроса придется прочитать и свести воедино не одну статью и потратить довольно значительное время на это. В данном материале же это все собрано воедино и по идее должно сэкономить время и разбор на мой взгляд получился довольно подробный.

О чем будет под катом: принцип работы одно байтовых кодировок (ASCII, Windows-1251 и т.д.), предпосылки появления Unicode, что такое Unicode, Unicode-кодировки UTF-8, UTF-16, их отличия, принципиальные особенности, совместимость и несовместимость разных кодировок, принципы кодирования символов, практический разбор кодирования и декодирования.

Вопрос с кодировками сейчас конечно уже потерял актуальность, но все же знать как они работают сейчас и как работали раньше и при этом не потратить много времени на это думаю лишним не будет.

Предпосылки Unicode

Начать думаю стоит с того времени когда компьютеризация еще не была так сильно развита и только набирала обороты. Тогда разработчики и стандартизаторы еще не думали, что компьютеры и интернет наберут такую огромную популярность и распространенность. Собственно тогда то и возникла потребность в кодировке текста. В каком то же виде нужно было хранить буквы в компьютере, а он (компьютер) только единицы и нули понимает. Так была разработана одно-байтовая кодировка ASCII (скорее всего она не первая кодировка, но она наиболее распространенная и показательная, по этому ее будем считать за эталонную). Что она из себя представляет? Каждый символ в этой кодировке закодирован 8-ю битами. Несложно посчитать что исходя из этого кодировка может содержать 256 символов (восемь бит, нулей или единиц 2 8 =256).

Первые 7 бит (128 символов 2 7 =128) в этой кодировке были отданы под символы латинского алфавита, управляющие символы (такие как переносы строк, табуляция и т.д.) и грамматические символы. Остальные отводились под национальные языки. То есть получилось что первые 128 символов всегда одинаковые, а если хочешь закодировать свой родной язык пожалуйста, используй оставшуюся емкость. Собственно так и появился огромный зоопарк национальных кодировок. И теперь сами можете представить, вот например я находясь в России беру и создаю текстовый документ, у меня по умолчанию он создается в кодировке Windows-1251 (русская кодировка использующаяся в ОС Windows) и отсылаю его кому то, например в США. Даже то что мой собеседник знает русский язык, ему не поможет, потому что открыв мой документ на своем компьютере (в редакторе с дефолтной кодировкой той же самой ASCII) он увидит не русские буквы, а кракозябры. Если быть точнее, то те места в документе которые я напишу на английском отобразятся без проблем, потому что первые 128 символов кодировок Windows-1251 и ASCII одинаковые, но вот там где я написал русский текст, если он в своем редакторе не укажет правильную кодировку будут в виде кракозябр.

Думаю проблема с национальными кодировками понятна. Собственно этих национальных кодировок стало очень много, а интернет стал очень широким, и в нем каждый хотел писать на своем языке и не хотел чтобы его язык выглядел как кракозябры. Было два выхода, указывать для каждой страницы кодировки, либо создать одну общую для всех символов в мире таблицу символов. Победил второй вариант, так создали Unicode таблицу символов.

Небольшой практикум ASCII

Возможно покажется элементарщиной, но раз уж решил объяснять все и подробно, то это надо.

Вот таблица символов ASCII:

Тут имеем 3 колонки:

  • номер символа в десятичном формате
  • номер символа в шестнадцатиричном формате
  • представление самого символа.

Unicode

С предпосылками создания общей таблицы для всех в мире символов, разобрались. Теперь собственно, к самой таблице. Unicode — именно эта таблица и есть (это не кодировка, а именно таблица символов). Она состоит из 1 114 112 позиций. Большинство этих позиций пока не заполнены символами, так что вряд ли понадобится это пространство расширять.

Разделено это общее пространство на 17 блоков, по 65 536 символов в каждом. Каждый блок содержит свою группу символов. Нулевой блок — базовый, там собраны наиболее употребляемые символы всех современных алфавитов. Во втором блоке находятся символы вымерших языков. Есть два блока отведенные под частное использование. Большинство блоков пока не заполнены.

Итого емкость символов юникода составляет от 0 до 10FFFF (в шестнадцатиричном виде).

Записываются символы в шестнадцатиричном виде с приставкой «U+». Например первый базовый блок включает в себя символы от U+0000 до U+FFFF (от 0 до 65 535), а последний семнадцатый блок от U+100000 до U+10FFFF (от 1 048 576 до 1 114 111).

Отлично теперь вместо зоопарка национальных кодировок, у нас есть всеобъемлющая таблица, в которой зашифрованы все символы которые нам могут пригодиться. Но тут тоже есть свои недостатки. Если раньше каждый символ был закодирован одним байтом, то теперь он может быть закодирован разным количеством байтов. Например для кодирования всех символов английского алфавита по прежнему достаточно одного байта например тот же символ «o» (англ.) имеет в юникоде номер U+006F, то есть тот же самый номер как и в ASCII — 6F в шестнадцатиричной и 111 в десятеричной. А вот для кодирования символа «U+103D5» (это древнеперсидская цифра сто) — 103D5 в шестнадцатиричной и 66 517 в десятеричной, тут нам потребуется уже три байта.

Решить эту проблему уже должны юникод-кодировки, такие как UTF-8 и UTF-16. Далее речь пойдет про них.

UTF-8

UTF-8 является юникод-кодировкой переменной длинны, с помощью которой можно представить любой символ юникода.

Давайте поподробнее про переменную длину, что это значит? Первым делом надо сказать, что структурной (атомарной) единицей этой кодировки является байт. То что кодировка переменной длинны, значит, что один символ может быть закодирован разным количеством структурных единиц кодировки, то есть разным количеством байтов. Так например латиница кодируется одним байтом, а кириллица двумя байтами.

Немного отступлю от темы, надо написать про совместимость ASCII и UTF

То что латинские символы и основные управляющие конструкции, такие как переносы строк, табуляции и т.д. закодированы одним байтом делает utf-кодировки совместимыми с кодировками ASCII. То есть фактически латиница и управляющие конструкции находятся на тех же самых местах как в ASCII, так и в UTF, и то что закодированы они и там и там одним байтом и обеспечивает эту совместимость.

Давайте возьмем символ «o»(англ.) из примера про ASCII выше. Помним что в таблице ASCII символов он находится на 111 позиции, в битовом виде это будет 01101111 . В таблице юникода этот символ — U+006F что в битовом виде тоже будет 01101111 . И теперь так, как UTF — это кодировка переменной длины, то в ней этот символ будет закодирован одним байтом. То есть представление данного символа в обеих кодировках будет одинаково. И так для всего диапазона символов от 0 до 128. То есть если ваш документ состоит из английского текста то вы не заметите разницы если откроете его и в кодировке UTF-8 и UTF-16 и ASCII (прим. в UTF-16 такие символы все равно будут закодированы двумя байтами, по этому вы не увидите разницы, если ваш редактор будет игнорировать нулевые байты), и так до момента пока вы не начнете работать с национальным алфавитом.

Сравним на практике как будет выглядеть фраза «Hello мир» в трех разных кодировках: Windows-1251 (русская кодировка), ISO-8859-1 (кодировка западно-европейских языков), UTF-8 (юникод-кодировка). Суть данного примера состоит в том что фраза написана на двух языках. Посмотрим как она будет выглядеть в разных кодировках.

В кодировке ISO-8859-1 нет таких символов «м», «и» и «р».

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

Будем считать что изначально фраза была записана в кодировке Windows-1251. Исходя из таблицы выше запишем эту фразу в двоичном виде, в кодировке Windows-1251. Для этого нам потребуется всего только перевести из десятеричной или шестнадцатиричной системы (из таблицы выше) символы в двоичную.

01001000 01100101 01101100 01101100 01101111 00100000 11101100 11101000 11110000
Отлично, вот это и есть фраза «Hello мир» в кодировке Windows-1251.

Теперь представим что вы имеете файл с текстом, но не знаете в какой кодировке этот текст. Вы предполагаете что он в кодировке ISO-8859-1 и открываете его в своем редакторе в этой кодировке. Как сказано выше с частью символов все в порядке, они есть в этой кодировке, и даже находятся на тех же местах, но вот с символами из слова «мир» все сложнее. Этих символов в этой кодировке нет, а на их местах в кодировке ISO-8859-1 находятся совершенно другие символы. А конкретно «м» — позиция 236, «и» — 232. «р» — 240. И на этих позициях в кодировке ISO-8859-1 находятся следующие символы позиция 236 — символ «ì», 232 — «è», 240 — «ð»

Значит фраза «Hello мир» закодированная в Windows-1251 и открытая в кодировке ISO-8859-1 будет выглядеть так: «Hello ìèð». Вот и получается что эти две кодировки совместимы лишь частично, и корректно перекодировать строку из одной кодировке в другую не получится, потому что там просто напросто нет таких символов.

Тут и будут необходимы юникод-кодировки, а конкретно в данном случае рассмотрим UTF-8. То что символы в ней могут быть закодированы разным количеством байтов от 1 до 4 мы уже выяснили. Теперь стоит сказать что с помощью UTF могут быть закодированы не только 256 символов, как в двух предыдущих, а вобще все символы юникода

Работает она следующим образом. Первый бит каждого байта кодирующего символ отвечает не за сам символ, а за определение байта. То есть например если ведущий (первый) бит нулевой, то это значит что для кодирования символа используется всего один байт. Что и обеспечивает совместимость с ASCII. Если внимательно посмотрите на таблицу символов ASCII то увидите что первые 128 символов (английский алфавит, управляющие символы и знаки препинания) если их привести к двоичному виду, все начинаются с нулевого бита (будьте внимательны, если будете переводить символы в двоичную систему с помощью например онлайн конвертера, то первый нулевой ведущий бит может быть отброшен, что может сбить с толку).

01001000 — первый бит ноль, значит 1 байт кодирует 1 символ -> «H»

01100101 — первый бит ноль, значит 1 байт кодирует 1 символ -> «e»

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

Для двухбайтовых символов первые три бита должны быть такие — 110

110 10000 10 111100 — в начале 110, значит 2 байта кодируют 1 символ. Второй байт в таком случае всегда начинается с 10. Итого отбрасываем управляющие биты (начальные, которые выделены красным и зеленым) и берем все оставшиеся ( 10000111100 ), переводим их в шестнадцатиричный вид (043С) -> U+043C в юникоде равно символ «м».

для трех-байтовых символов в первом байте ведущие биты — 1110

1110 1000 10 000111 10 1010101 — суммируем все кроме управляющих битов и получаем что в 16-ричной равно 103В5, U+103D5 — древнеперситдская цифра сто ( 10000001111010101 )

для четырех-байтовых символов в первом байте ведущие биты — 11110

11110 100 10 001111 10 111111 10 111111 — U+10FFFF это последний допустимый символ в таблице юникода ( 100001111111111111111 )

Теперь, при желании, можем записать нашу фразу в кодировке UTF-8.

UTF-16

UTF-16 также является кодировкой переменной длинны. Главное ее отличие от UTF-8 состоит в том что структурной единицей в ней является не один а два байта. То есть в кодировке UTF-16 любой символ юникода может быть закодирован либо двумя, либо четырьмя байтами. Давайте для понятности в дальнейшем пару таких байтов я буду называть кодовой парой. Исходя из этого любой символ юникода в кодировке UTF-16 может быть закодирован либо одной кодовой парой, либо двумя.

Начнем с символов которые кодируются одной кодовой парой. Легко посчитать что таких символов может быть 65 535 (2в16), что полностью совпадает с базовым блоком юникода. Все символы находящиеся в этом блоке юникода в кодировке UTF-16 будут закодированы одной кодовой парой (двумя байтами), тут все просто.

символ «o» (латиница) — 00000000 01101111
символ «M» (кириллица) — 00000100 00011100

Теперь рассмотрим символы за пределами базового юникод диапазона. Для их кодирования потребуется уже две кодовые пары (4 байта). И механизм их кодирования немного сложнее, давайте по порядку.

Для начала введем понятия суррогатной пары. Суррогатная пара — это две кодовые пары используемые для кодирования одного символа (итого 4 байта). Для таких суррогатных пар в таблице юникода отведен специальный диапазон от D800 до DFFF. Это значит, что при преобразовании кодовой пары из байтового вида в шестнадцатиричный вы получаете число из этого диапазона, то перед вами не самостоятельный символ, а суррогатная пара.

Чтобы закодировать символ из диапазона 1000010FFFF (то есть символ для которого нужно использовать более одной кодовой пары) нужно:

  1. из кода символа вычесть 10000(шестнадцатиричное) (это наименьшее число из диапазона 1000010FFFF)
  2. в результате первого пункта будет получено число не больше FFFFF, занимающее до 20 бит
  3. ведущие 10 бит из полученного числа суммируются с D800 (начало диапазона суррогатных пар в юникоде)
  4. следующие 10 бит суммируются с DC00 (тоже число из диапазона суррогатных пар)
  5. после этого получатся 2 суррогатные пары по 16 бит, первые 6 бит в каждой такой паре отвечают за определение того что это суррогат,
  6. десятый бит в каждом суррогате отвечает за его порядок если это 1 то это первый суррогат, если 0, то второй

Для примера зашифруем символ, а потом расшифруем. Возьмем древнеперсидскую цифру сто (U+103D5):

  1. 103D510000 = 3D5
  2. 3D5 = 0000000000 1111010101 (ведущие 10 бит получились нулевые приведем это к шестнадцатиричному числу, получим 0 (первые десять), 3D5 (вторые десять))
  3. 0 + D800 = D800 ( 110110 0 000000000 ) первые 6 бит определяют что число из диапазона суррогатных пар десятый бит (справа) нулевой, значит это первый суррогат
  4. 3D5 + DC00 = DFD5 ( 110111 1 111010101 ) первые 6 бит определяют что число из диапазона суррогатных пар десятый бит (справа) единица, значит это второй суррогат
  5. итого данный символ в UTF-16 — 1101100000000000 1101111111010101
  1. переведем в шестнадцатиричный вид = D822DE88 (оба значения из диапазона суррогатных пар, значит перед нами суррогатная пара)
  2. 110110 0 000100010 — десятый бит (справа) нулевой, значит первый суррогат
  3. 110111 1 010001000 — десятый бит (справа) единица, значит второй суррогат
  4. отбрасываем по 6 бит отвечающих за определение суррогата, получим 0000100010 1010001000 (8A88)
  5. прибавляем 10000 (меньшее число суррогатного диапазона) 8A88 + 10000 = 18A88
  6. смотрим в таблице юникода символ U+18A88 = Tangut Component-649. Компоненты тангутского письма.

Вот некоторые интересные ссылки по данной теме:
habr.com/ru/post/158895 — полезные общие сведения по кодировкам
habr.com/ru/post/312642 — про юникод
unicode-table.com/ru — сама таблица юникод символов

Почему меняется размер закодированного сообщения

Александр Терехов
дата публикации 17-10-2002 13:16

Base64 для не продвинутых.

Вступление

Изначально для передачи электронной почты в Интернет использовался только текст (RFC822). Затем, с развитием компьютерных девайсов, потребовалась возможность передачи нетекстовой информации: аудио, видео, графических файлов, файлов приложений и т.д. Однако почтовые сервера как понимали только текст, так и остались понимать только его. Поэтому появилась необходимость каким-то образом преобразовать двоичный файл в текстовый. Вообще-то способ такого преобразования уже имел место — это UUE кодирование. Но появился еще один — base64. Этот способ используется в спецификации MIME (RFC2045-2049).

Что такое MIME? Если говорить вкратце, то это стандарт описания заголовков e-mail сообщений. Используя этот стандарт, в одном письме можно отправить сразу несколько различных вложений. Например, можно положить в аттачмент письма архивированный файл, оформить сообщение как просто текст, а также поместить HTML-страницу. И все это отправить получателю. Почтовая программа-получатель, понимающая MIME, совершенно свободно из файла электронной почты (который на самом деле является «обычным» текстовым файлом) извлечет архив, покажет сообщение и обработает тэги HTML. Некоторые почтовики, например Outlook Express, на радость вирмейкерам без спроса пользователя еще и запустят вложенные в HTML-страницу скрипты.

Идеология base64

Как известно, байт состоит из восьми битов 🙂
В один байт можно вложить 256 цифр, от 0 до 255. Однако, если вместо восьми байт использовать только шесть, то объем вложенной информации уменьшается до 64 цифр, от 0 до 63. Теперь главное: любую цифру 6-ти битового байта можно представить в виде печатного символа. 64 символа это не так много, us-ascii символов вполне хватит. Ниже представлен 64-х символьный base64 «алфавит».

ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/

где код символа A — 0, а код символа / — 63.

Вроде, понятно. Что дальше?

А далее берутся три последовательных байта по восемь бит (всего 24 бита), и побитно делятся на четыре 6-ти битных байта (всего 24 бита).
Немного странно звучит: «шестибитный байт». На самом деле бит восемь, однако используются только 6 младших бит, два старших бита игнорируются.
Схематично такое «деление три к четырем» можно представить себе так:

В приведенном примере три числа 103, 193 и 58 были закодированы в base64 формат. В результате мы получили 4-х символьный стринг Z8E6. Т.о. на практике увидели идеологию перевода двоичной информации в текст по принципу 3 к 4.

Основываясь на этом принципе, мы можем закодировать любую двоичную информацию в текст, причем не очень сильно увеличивая ее объем (на 30%). Затем наша информация через почтовый сервер попадет к нужному адресату, почтовик которого декодирует текст в двоичный файл.

Все довольны, все смеются.
«Но..», скажите Вы, — «что делать, если у нас нет трех байтов? Есть только один или два!» В этом случае в конец четырех символьного стринга добавляется символ = (равно). Если не хватает (до трех) одного байта, то добавляется один символ «равно»:

если не хватает двух байт, то добавляются два символа «равно»:

Что радует: с символами «равно» надо разбираться только один раз — при чтении конца файла.
На этом вроде бы все.

Нет, еще не все. Формат base64 имеет ограничение — общая длина строки, состоящая из 4-х символьных стрингов составляет 72 символа (за исключением самой последней строки — там уже сколько получится).

  1. Имеющейся почтовой программой сохранить в файл какое-нибудь письмо. Письмо, естественно, надо выбрать со вложенным файлом. При сохранении письма, тип файла следует выбрать «почтового формата» (e-mail message), например *.msg или *.eml
  2. Включить самый мощный текстовый редактор — Notepad («Блокнот») и открыть сохраненный файл письма. Тем, у кого п.2 не получился с первого раза — тип файла при открытии его «Блокнотом» надо указать «Все файлы» (*.*)
  3. В «Блокноте» (а если файл большой, то в WordPad’е) откроется примерно нижеследующее:

Date: Sat, 12 Oct 2002 10:09:59 +0000 From: "Mr.Dark" X-Mailer: The Bat! (v1.47 Halloween Edition) Reply-To: "Mr.Dark" Organization: Dark Company X-Priority: 3 (Normal) Message-ID: To: =?koi8-r?B?5czFzsEg5snMydDQz9fB?= Subject: Base64 Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="----------73121211AFBA8E3" ------------73121211AFBA8E3 Content-Type: text/plain; charset=koi8-r Content-Transfer-Encoding: 8bit Hello еМЕОБ, -- Best regards, Mr.Dark mailto:dark@online.ru Delphi-РТПЗТБННЙТПЧБОЙЕ http://www.inta.portal.ru/dark/ ------------73121211AFBA8E3 Content-Type: image/jpeg; name="ex1.jpg" Content-Transfer-Encoding: base64 Content-Disposition: attachment; filename="ex1.jpg" /9j/4AAQSkZJRgABAQEBLAEsAAD/2wBDAAUDBAQEAwUEBAQFBQUGBwwIBwcHBw8LCwkMEQ8SEhEP ERETFhwXExQaFRERGCEYGh0dHx8fExciJCIeJBweHx7/2wBDAQUFBQcGBw4ICA4eFBEUHh4eHh4e Hh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh7/wAARCADFApsDASIA AhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQA ============================вырезано================================== FABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUU AFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQA UUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAf/Z ------------73121211AFBA8E3--

Полный текст можно посмотреть в файле EMail.txt.

Content, boundary и все остальное, что идет после строки Mime-Version: 1.0 — это стандарт MIME. Буквы, написанные в koi8-r, читаются в виде абракадабры. И это правильно, т.к. кодировка настоящего документа установлена в windows-1251. Если в Вашем броузере установить кодировку koi8-r (в Internet Explorer 5.0 в главном меню выбрать «Вид»-«Вид кодировки»-«Кириллица (КОИ8-Р)»), то эти буквы можно будет прочитать. Остальной текст, правда, разобрать вряд ли удастся 🙂
В конце текста письма находится очень большой блок еще одной абракадабры. В приведенном тексте он немного порезан. Так вот этот блок и есть двоичный файл закодированный base64 в текст. Чуть выше «большого блока» видно, что для кодировки применялся base64, видны также названия файла. То, что выше — реальное название файла и его тип: ‘ex1.jpg’, тип файла image/jpeg, которое пониже — название этого файла в окне аттача письма.
Теперь, надеюсь, все понятно.
Да, еще. Немного отвлекаясь от base64, скажу, что чтение заголовков писем — наиувлекательнейшее занятие. Например, с помощью одной из моих программ (IPScaner, лежит на моем сайте в разделе «Программы»), по заголовку письма можно очень даже конкретно определить географическое место отправки e-mail. Однажды вычислил даже номер кабинета в одном из московских институтов, откуда ко мне пришло письмо.

Алгоритм base64 кодирования и декодирования.

Наверное существует самый оптимальный и быстрый алгоритм кодирования и декодирования base64.
Но.
Почему-то хочется в очередной раз самому изобрести велосипед.
Нисколько не претендуя на оптимальность, скорострельность и оригинальность.
Итак, рассматривая идеологию base64, первое, что приходит в голову — это устроить небольшой битовый конвейер. Т.е. (для случая кодирования) взять 8-ой (старший) бит исходного байта и поместить его в начало 6-ти битового байта (по поводу термина «6-ти битовый байт» см. выше). Затем на место 8-го бита исходного байта поместить 7-й бит, а в 6-ти битовом байте первый бит (младший) переместить на место 2-го бита. После такого перемещения освобождается первый (младший) бит 6-ти битного числа. В него и поместим старший (бывший седьмой) бит исходного байта. Затем еще раз передвинем биты в обоих байтах. И т.д.
Примерно так:

Рассмотрим шаг 1.

Как известно стандартные операнды Паскаля AND и OR могут выступать в двух ипостасях: битовая арифметика и логика. В первом шаге используем операнд AND как битовую ипостась 🙂
Для проверки установки старшего бита исходного 8-ми битного байта наложим на него так называемую «маску». Т.е. применим к нему побитовую операцию AND с числом 128 (10000000).

Как видно из приведенной схемы, проверить установку старшего бита совсем несложно. Если результатом операции получается число 128, значит бит установлен, а если в результате получаем 0, значит старший бит не установлен. Далее в зависимости от полученного результата применим к 6-ти битовому байту битовую операцию OR с числом 1, которая в любом случае применения устанавливает первый (младший) байт в 1.

Здесь необходимо учитывать то, что перед применением побитовой операции OR с числом 1 младший бит 6-ти битового байта всегда 0. В случае для первых двух шагов потому, что мы сами обнуляем его при инициализации, для последующих шагов, потому что применяем операцию SHR.

Итак, посмотрим как выглядит реализация первого шага в Паскале:

If (Source AND 128) = 128 Then Destination:=Destination OR 1;

где Source — 8-ми битный байт источник, а Destination — 6-ти битный байт приемник.

Сместим первый бит 6-ти битного байта на вторую позицию, одновременно обнуляя его первый бит.

Destination:=Destination SHR 1;

Сместим седьмой байт 8-ми битного байта на 8-е(старшее) место.

Source:=Source SHR 1;

Операция X SHR Y, смещает в байте X биты на Y-мест влево и автоматически устанавливает Y-младшие байты в 0, следовательно значение числа изменяется. Поэтому в реальном коде мы будем работать не с самим байтом-источником, а с его временной копией.

Повторяем шаги 1 и 2 шесть раз для каждого бита 6-ти битного байта. Одновременно необходимо следить за тем, что если мы обработали восемь бит байта-источника, следует перейти к новому байту. И последнее — если обработаны все три байта-источники, следует выйти из функции кодирования.

Теперь о декодировании.

Декодирование по сути ничем не отличается от кодирования. Делаем тоже самое, только источник и приемник меняются местами. А битовый конвейер должен продолжать работать в ту же сторону — справа — на лево: проверяем установку первого бита 6-ти битного байта (маску в этом случае надо накладывать с числом 32 (0010000) — не восемь битов, а шесть) и в зависимости от результата устанавливаем (или не устанавливаем) младший байт 8-ми битного байта. Затем делаем побитовый сдвиг влево.
Теперь, когда мы теоретически подковались можно посмотреть и исходный код, который находится в файле Base64Unit.pas

Примеры.

В первом примере ( b64_Example1 5К) показана работа модуля base64-кодирования (декодирования). Три восьмибитных байта кодируются в 4-х символьный стринг и обратно. Понятно, что это всего лишь пример и при вводе цифр и букв следует соблюдать base64-алфавит.

Второй пример ( b64_Example2 5К) — вполне работоспособная утилита, позволяющая кодировать любой файл в текстовый base64-формат. Кроме этого утилита позволяет base64-кодированный текст декодировать в двоичный файл. Например, Вы можете «вырвать» из тела e-mail сообщения base64-фрагмент и сохранить его в текстовом файле. А затем декодировать это файл в двоичный файл. Название файла и его расширение можно увидеть в MIME-секции письма — filename.

Конечно, при кодировании надо считывать по 58 байт, а не по 3, как это сделано в примере. Считывание по 58 байт значительно увеличит «скорострельность» утилиты. Но следует учитывать, что это всего лишь пример.

Пример очередного изобретения велосипеда 🙂

Обсуждение материала [ 19-02-2017 06:58 ] 21 сообщение

Время на сайте: GMT минус 5 часов

Если вы заметили орфографическую ошибку на этой странице, просто выделите ошибку мышью и нажмите Ctrl+Enter.
Функция может не работать в некоторых версиях броузеров.

© При использовании любых материалов «Королевства Delphi» необходимо указывать источник информации. Перепечатка авторских статей возможна только при согласии всех авторов и администрации сайта.
Все используемые на сайте торговые марки являются собственностью их производителей.

Введение

Передача информации в современном мире нас окружает повсюду: мобильная связь, интернет, управление воздушным движением и многое другое. Как правило, в качестве “переносчика” информации выступает высокочастотное колебание: 2.4 ГГц и 5 ГГц для Wi-Fi, 2.6 ГГц и другие для 4G, 1030 МГц и 1090 МГц для обмена данными с воздушными судами и т.д. Эти колебания (частоты) называются несущими. Как, используя несущую частоту, закодировать данные? Как передать или получить “0” или “1”? На эти вопросы мы и постараемся ответить.

Процесс изменения одного или нескольких параметров модулируемого несущего сигнала при помощи модулирующего сигнала называется модуляцией.

В рамках данной лекции мы рассмотрим следующие виды модуляции:

  • ASK (Amplitude Shift Keying) — амплитудная манипуляция
  • BPSK, QPSK (Binary Phase Shift Keying, Quadrature Phase Shift Keying) — фазовая манипуляция
  • QASK (Quadrature Amplitude-Shift Keying) — квадратурная манипуляция
  • FSK (Frequency Shift Keying), MSK (Minimum Shift Keying) — частотная манипуляция

Амплитудная манипуляция (ASK)

Амплитудная манипуляция — манипуляция, при которой скачкообразно изменяется амплитуда несущего сигнала в зависимости от закодированного сообщения.

Рассмотрим пример. Создадим сигнал fc , который из себя будет представлять несущую частотой 500 Гц, добавим в неё немного шума и промодулируем кодовой последовательностью (сообщением), заданной в массиве code . Логическому “нулю” будет соответствовать амплитуда 0.1, логической “единице” — 1:

clear, clc, close all fs = 10000; ts = -0.1 : 1/fs : 0.1-1/fs; N = length(ts); %% несущая частота fc = cos(2*pi*500*ts); fc = awgn(fc,30); %% модулирующий сигнал % длина одного бита в отсчётах n_for_bit = 200; % кодируемая последовательность code = [1 0.1 0.1 1 1 0.1 1 0.1 1 1]; % формируем модулирующий сигнал fm = zeros(1,N); for i=1:length(code) for j=n_for_bit*(i-1)+1:n_for_bit*i fm(j) = code(i); end end %% амплитудная модуляция x = fc.*fm; plot(ts,x,'LineWidth',0.5), grid on, hold on plot(ts,fm,'LineWidth',2), grid on title ('ASK модуляция') xlabel('Время'), ylabel('Амплитуда') legend()

Результат выполнения скрипта показан ниже:

Из рисунка видно, что амплитуда несущего (модулированного) сигнала — синий график — повторяет форму модулирующего сигнала — оранжевый график.

Теперь давайте сделаем следующую вещь: на комплексной плоскости отметим точки, соответствующие значениям, которые принимает наш закодированный сигнал. В Matlab это можно сделать с помощью функции scatterplot , указав в качестве входных параметров аналитический сигнал (мы его получим с помощью преобразования Гильберта), количество отсчётов на бит (у нас за это отвечает переменная n_for_bit ) и смещение от начала сигнала, с которым будут считываться биты (мы хотим считывать биты в их середине, поэтому смещение зададим n_for_bit/2 ):

%% сигнальное созвездие scatterplot(hilbert(x),n_for_bit,round(n_for_bit/2)), grid on

Т.к. мнимая часть равна нулю, мы видим два скопления точек вдоль действительной оси: одно вокруг значения 0.1, второе — вокруг 1. Этот график называется сигнальное созвездие. Он часто используется при анализе характеристик модулированных сигналов, т.к. показывает распределение мгновенного значения сигнала на комплексной плоскости в момент его считывания.

Итак, модулированный сигнал есть — попробуем его обратно демодулировать. Наша задача сводится к нахождению огибающей, а как мы выяснили на прошлой лекции — это легко сделать с помощью преобразования Гильберта. Затем создадим что-то вроде цифрового компаратора, с помощью которого зашумлённую огибающую превратим в прямоугольный цифровой сигнал. Дополним наш код:

%% амплитудная демодуляция % преобразование Гильберта h = hilbert(x); figure subplot(2,1,1) plot(ts,x,'LineWidth',0.5), grid on, hold on plot(ts,abs(h),'LineWidth',2), grid on title ('ASK демодуляция') xlabel('Время'), ylabel('Амплитуда') legend() % компаратор dem = zeros(1,length(h)); for i=1:length(h) if abs(h(i))>=0.5 dem(i) = 1; else dem(i) = 0; end end subplot(2,1,2) plot(ts,dem,'LineWidth',2), grid on title ('Код демодулированного сигнала') xlabel('Время'), ylabel('Амплитуда')

Как видим, демодулированный сигнал повторяет битовую последовательность, которую мы закодировали изначально в массиве code , а значит мы всё сделали правильно.

Ещё одним типом амплитудной манипуляции является OOKOn-Off Keying. Основное отличие от ASK — логический “ноль” кодируется амплитудой, равной нулю, “единице” соответствует номинальное значение амплитуды сигнала. Если в листинге “ASK-манипуляция, часть 1” в массиве code вместо всех значений 0.1 запишем 0 , то как раз получим OOK-манипуляцию.

Фазовая манипуляция (PSK)

Фазовая манипуляция — манипуляция, при которой скачкообразно изменяется фаза несущего сигнала в зависимости от закодированного сообщения.

Начнём с самого простого вида — BPSK — когда у нас всего два значения фазы — и .

Создадим скрипт, реализующий фазовую манипуляцию зашумлённой несущей частоты fc , равной 250 Гц. Кодовую последовательность по аналогии с первым примером будем задавать в массиве code . Перескок фазы будет осуществляться умножением синусоидального сигнала на +1 или -1:

clear, clc, close all fs = 10000; ts = 0 : 1/fs : 0.2-1/fs; N = length(ts); %% несущая частота fc = cos(2*pi*250*ts); fc = awgn(fc,30); %% модулирующий сигнал % длина одного бита в отсчётах n_for_bit = 200; % кодируемая последовательность code = [1 -1 -1 1 1 -1 1 -1 1 1]; % формируем модулирующий сигнал fm = zeros(1,N); for i=1:length(code) for j=n_for_bit*(i-1)+1:n_for_bit*i fm(j) = code(i); end end %% фазовая манипуляция (BPSK) x = fc.*fm; plot(ts,x,'LineWidth',0.5), grid on, hold on plot(ts,fm,'LineWidth',2), grid on title ('BPSK модуляция') xlabel('Время'), ylabel('Амплитуда') legend()

Смотрим, что получилось:

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

Теперь дополним код по аналогии с предыдущим примером и построим сигнальное созвездие:

%% сигнальное созвездие scatterplot(hilbert(x),n_for_bit,round(n_for_bit/2)), grid on

Сигнальное созвездие BPSK-сигнала:

Мы имеем два скопления точек: вокруг (-1,0) и (1,0). Эти точки как раз соответствуют повороту вектора единичной длины на и вокруг начала координат. А значит, модуляция работает корректно.

Теперь наша задача — демодулировать сгенерированный сигнал. Для этого умножим его на несущую частоту fc , а затем применим к полученному сигналу ФНЧ с полосой пропускания 100 Гц, сгенерированный с помощью filterDesigner:

%% BPSK демодуляция % умножаем сигнал на несущую y = x.*fc; % применяем ФНЧ, чтобы убрать несущую fltr = bpsk_fir; z = filter(fltr.Numerator,1,y); figure; subplot(2,1,1) plot(ts,y), grid on title('Модулированный сигнал, умноженный на несущую') xlabel('Время'), ylabel('Амплитуда') subplot(2,1,2) plot(ts,z), grid on title('Тот же сигнал, прошедший через ФНЧ') xlabel('Время'), ylabel('Амплитуда')

На верхнем графике наблюдаем синусоиды с частотой 2*fc , имеющие разные постоянные составляющие в зависимости от значения фазы модулированного сигнала. Для того, чтобы получить огибающую, мы воспользовались ФНЧ и получили нижний график. Теперь, чтобы преобразовать данный сигнал в цифровой код, разработаем простейший цифровой компаратор с пороговым значением 0.1 и построим результирующие графики:

% компаратор dem = zeros(1,length(z)); for i=1:length(z) if z(i)>=0.1 dem(i) = 1; else dem(i) = 0; end end figure plot(ts,x,'LineWidth',0.5), grid on, hold on plot(ts,dem,'LineWidth',2), grid on xlabel('Время'), ylabel('Амплитуда') title ('BPSK демодуляция') legend()

Демодулированный сигнал на фоне модулированного сигнала представлен ниже:

Как видим, он повторяет форму модулирующего сигнала, заданного в начале листинга, но имеет временную задержку, возникающую в КИХ-ФНЧ.

В рассмотренном примере модулированный сигнал мог принимать два значения фазы: и , а значит в один момент времени (такт) можно закодировать только один бит информации — “0” или “1”. Если добавить ещё два изменения фазы ( и ), а затем повернуть полученную диаграмму на против часовой стрелки, получится QPSK-манипуляция, или квадратурная фазовая манипуляция. В этом случае мы за один такт сможем передавать сразу два бита информации! Посмотрим, как в этом случае будет выглядеть сигнальное созвездие. Разработаем скрипт, который формирует массив случайных данных data , которые затем преобразуем в массив значений PSK-сигнала с помощью функции pskmod , в качестве второго параметра которой будет порядок модуляции (для QPSK это M = 4 ). В качестве третьего параметра данной функции необходимо задать смещение нулевого значения фазы, в нашем случае, как было сказано выше, это (тогда, в идеальном случае, в каждом квадранте комплексной плоскости будет по одной точке созвездия). Далее добавим немного шума в этот сигнал, чтобы имитировать реальные условия передачи данных и построим сигнальное созвездие:

clear, clc, close all M = 4; data = randi([0 M-1],1000,1); txSig = pskmod(data, M, pi/M); rxSig = awgn(txSig,20); scatterplot(rxSig), grid on

Получилось вот так:

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

  • — “11”
  • — “01”
  • — “00”
  • — “10”

Это значит, что при при той же самой символьной скорости сигнал с QPSK передаёт в 2 раза больше информации, чем сигнал с BPSK.

Если присвоить M = 8 и убрать из строки 5 параметр pi/M , получим 8-PSK-манипуляцию. Сигнальное созвездие примет вид:

Здесь мы можем передавать за один такт сразу 3 бита. Однако, с увеличением порядка модуляции M , точки созвездия располагаются всё ближе и ближе друг другу, что может привести к ошибкам декодирования такого сигнала, если он сильно зашумлён. Можете поиграться со значениями M и параметром snr функции awgn , чтобы убедиться в этом на примере.

Квадратурная манипуляция (QASK)

QASK (частный случай QAM — Quadrature Amplitude Modulation) — это вид манипуляции, при которой скачкообразно изменяется как амплитуда, так и фаза несущего сигнала, что позволяет за один такт (отсчёт) передать ещё больше информации, чем в рассмотренных ранее видах манипуляции. Можно сказать, что QASK — это комбинация ASK и PSK.

По традиции, сразу начнём с примера. Создадим несущую с частотой 1кГц, помимо этого создадим массив данных data , который будет содержать случайные числа. Зададим порядок модуляции M=16 , это значит, что за один такт будем передавать число от 0 до 15, или 4 бита. Один элемент — один такт передачи, количество элементов — 50. Затем создадим массив отсчётов QASK на базе этих данных с помощью функции qammod и построим сигнальное созвездие из полученного набора данных.

clear, clc, close all fs = 10000; ts = 0 : 1/fs : 0.2-1/fs; N = length(ts); %% несущая частота fc = 1000; %% Модуляция M = 16; % порядок QASK модуляции Nd = 50; % размер данных bit_size = N/Nd; % количество отсчётов на бит data = randi([0 M-1],Nd,1); % случайные данные % формируем массив комплексных чисел qdata = qammod(data, M); % сигнальное созвездие sc = scatterplot(qdata); grid on obj = findobj(sc.Children(1).Children, 'type', 'line'); set(obj, 'MarkerSize', 20)

Результат показан ниже:

Это созвездие состоит из 16 групп точек, а значит сформированный сигнал принимает все возможные значения для QASK-манипуляции 16 порядка.

Теперь “поместим” эти данные на несущую частоту. Растянем массив данных до того же количества отсчётов, что и в сигнале несущей частоты (каждый бит повторим bit_size раз) и получим массив qmod . Поэлементно умножим действительные части данного массива на косинус с частотой fc , а мнимые — на синус той же частоты. Просуммируем полученные сигналы ( i и q соответственно), в результате чего получим модулированный сигнал y , который готов для передачи. Добавим в него шум для имитации электромагнитных помех и построим графики.

qmod = repelem(qdata,bit_size).'; % размножаем массив данных до кол-ва отсчётов несущей % формируем синусный и косинусный сигналы i = real(qmod).*cos(2*pi*fc*ts); q = imag(qmod).*sin(2*pi*fc*ts); % суммируем i и q, получаем сигнал, готовый к передаче y = i+q; % добавляем шум y = awgn(y,20); figure subplot(3,1,1) plot(real(qmod)), grid on title('Действительная часть сигнала') xlabel('Время'), ylabel('Real') subplot(3,1,2) plot(imag(qmod)), grid on title('Мнимая часть сигнала') xlabel('Время'), ylabel('Imag') subplot(3,1,3) plot(y), grid on title('Модулированный сигнал') xlabel('Время'), ylabel('Амплитуда')

Действительная и мнимая части сигнала, а также сам сигнал y можно увидеть на рисунке ниже:

Видно, что в процессе передачи данных изменяется как амплитуда, так и фаза несущей.

Теперь решим обратную задачу: демодулируем сигнал на нижнем графике. Для этого обратно выделим из него синфазную io и квадратурную qo составляющие умножением на косинус и синус несущей частоты соответственно. Чтобы убрать высокочастотную составляющую, как и в BPSK, применим ФНЧ, в результате чего получим сигналы iof и qof , графики которых затем и построим.

%% Демодуляция io = 2*y.*cos(2*pi*fc*ts); qo = 2*y.*sin(2*pi*fc*ts); % применяем ФНЧ, чтобы убрать несущую и умножаем на 2 fltr = qam_fir; iof = round(conv(fltr.Numerator,io)); qof = round(conv(fltr.Numerator,qo)); figure subplot(2,1,1) plot(iof), grid on title('Синфазная составляющая демодулированного сигнала после ФНЧ') subplot(2,1,2) plot(qof), grid on title('Квадратурная составляющая демодулированного сигнала после ФНЧ')

А вот и графики:

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

\begin{equation*} \begin{aligned} x_{cos}(t) = (I(t) cos(2\pi f_c t) + Q(t) sin(2\pi f_c t) \cdot cos(2\pi f_c t) = \\ = I(t) cos^2 (2\pi f_c t) + \frac{1}{2} Q(t) sin(4 \pi f_c t) =\\ = \frac{1}{2} I(t) + \frac{1}{2} I(t) cos(4 \pi f_c t) + \frac{1}{2} Q(t) sin(4 \pi f_c t) \end{aligned} \end{equation*}

(1)

При умножении на синус:

\begin{equation*} \begin{aligned} x_{sin}(t) = (I(t) cos(2\pi f_c t) + Q(t) sin(2\pi f_c t) \cdot sin(2\pi f_c t) = \\ = Q(t) sin^2 (2\pi f_c t) + \frac{1}{2} I(t) sin(4 \pi f_c t) =\\ = \frac{1}{2} Q(t) - \frac{1}{2} I(t) cos(4 \pi f_c t) + \frac{1}{2} Q(t) sin(4 \pi f_c t) \end{aligned} \end{equation*}

(2)

После применения ФНЧ косинусоидальные и синусоидальные составляющие уходят, остаётся только постоянная составляющая:

(3)

(4)

Поэтому, чтобы скомпенсировать амплитуду демодулированного сигнала, в строчках 49 и 50 мы и сделали умножение на 2.

Далее преобразуем iof и qof в один комплексный сигнал of , который проредим, взяв из него отсчёты, расположенные в серединах временных отрезков, соответствующих битам данных. Затем воспользуемся функцией qamdemod и сравним результаты: что закодировали, и что в последствии декодировали.

%% Сравнение результатов % формируем комплесный сигнал после фильтра of = complex(iof,qof); % считываем из массива значения с шагом, % равным периоду следования данных % и с учётом задержки КИХ-фильтров fir_delay = round(length(fltr.Numerator)/2); of_dec = of(fir_delay+round(bit_size/2) : bit_size : length(of)-fir_delay); % Сравниваем результаты y = qamdemod(of_dec, M); figure plot(data,'b-'), grid on, hold on plot(y,'x','LineWidth',2) title('Сравнение закодированных (переданных) и декодированных (принятых) данных') xlabel('Номер отсчёта'), ylabel('Значение') legend()

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

Как видим, два графика полностью совпадают, а это говорит о том, что реализованный нами алгоритм работает корректно.

Частотная манипуляция (FSK)

Если во всех предыдущих рассмотренных нами видах манипуляции частота несущей была постоянна, то в случае с FSK это не так. FSK — это манипуляция, при которой в зависимости от закодированного сообщения скачкообразно изменяется частота несущего сигнала.

Данный вид манипуляции считается самым помехоустойчивым, т.к. помехи чаще всего влияют на амплитуду, а не на несущую частоту. Частота логического “0” и логической “1” вычисляются по формулам:

(5)

(6)

  • — несущая частота
  • — коэффициент модуляции
  • — период передаваемого сообщения

При манипуляция называется Minimum Shift Keying (MSK) — манипуляция с минимальным сдвигом частоты.

И снова к примеру. Разработаем скрипт, формирующий FSK-модулированный сигнал. Несущую частоту выберем 500 Гц, индекс модуляции , период следования битов данных 10 мкс. На основе этого рассчитаем значение двух частот: f0 и f1 , кодирующие логический “0” и “1” соответственно.

clear, clc, close all fs = 10000; ts = 0 : 1/fs : 0.1-1/fs; N = length(ts); %% параметры несущей fc = 500; % несущая частота h = 4; % индекс модуляции T = 10e-3; % длительность бита f0 = fc+h/(2*T); % частота лог. "0" f1 = fc-h/(2*T); % частота лог. "1"

Далее сформируем модулирующий сигнал по аналогии с тем, как мы это делали в примерах с BPSK и ASK:

%% модулирующий сигнал % длина одного бита в отсчётах n_for_bit = T*fs; % кодируемая последовательность code = [1 0 0 1 1 0 1 0 1 1]; % формируем модулирующий сигнал fm = zeros(1,N); for i=1:length(code) for j=n_for_bit*(i-1)+1:n_for_bit*i fm(j) = code(i); end end

Теперь создадим два сигнала: x0 с частотой f0 и x1 с частотой f1 . На базе этих сигналов и модулирующего сигнала fm сформируем модулированный сигнал x и построим графики.

%% Частотная манипуляция x0 = cos(2*pi*f0*ts); x1 = cos(2*pi*f1*ts); x = zeros(1,N); for i = 1:N if fm(i) == 0 x(i) = x0(i); else x(i) = x1(i); end end plot(ts,x,'LineWidth',0.5), grid on, hold on plot(ts,fm,'LineWidth',2), grid on title ('FSK-модуляция') xlabel('Время'), ylabel('Амплитуда') legend()

Результат выполнения данного скрипта:

Из рисунка видно, что в зависимости от значения модулирующего сигнала, меняется частота несущей — это то, что нам нужно. Модулировать научились — теперь попробуем это демодулировать. Для этого умножим наш сигнал на косинусоиды с частотами f0 и f1 :

%% FSK-демодуляция % умножаем сигнал на несущую y0 = x.*x0; y1 = x.*x1; figure; subplot(2,1,1) plot(ts,y0), grid on title('Модулированный сигнал, умноженный на f0') xlabel('Время'), ylabel('Амплитуда') subplot(2,1,2) plot(ts,y1), grid on title('Модулированный сигнал, умноженный на f1') xlabel('Время'), ylabel('Амплитуда')

Сигналы y0 и y1 показаны ниже:

Теперь найдём разность этих сигналов и пропустим её через ФНЧ:

y = y1 - y0; % применяем ФНЧ, чтобы оставить постоянную составляющую fltr = fsk_fir; z = filter(fltr.Numerator,1,y); figure subplot(2,1,1) plot(ts,y), grid on title('Сигнал y=y0-y1') xlabel('Время'), ylabel('Амплитуда') subplot(2,1,2) plot(ts,z), grid on title('Этот же сигнал после ФНЧ') xlabel('Время'), ylabel('Амплитуда')

Результат показан ниже:

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

% компаратор dem = zeros(1,length(z)); for i=1:length(z) if z(i)>=0.1 dem(i) = 1; else dem(i) = 0; end end figure plot(ts,x,'LineWidth',0.5), grid on, hold on plot(ts,dem,'LineWidth',2), grid on xlabel('Время'), ylabel('Амплитуда') title ('FSK-демодуляция') legend()

Результат демодуляции показан ниже:

Как видим, оранжевый график имеет ту же форму, что и на первоначальном рисунке, но с уже привычной нам временной задержкой, возникающей в цифровом КИХ-фильтре.

Comments (10)

Alexander Jan 12, 2023 at 12:04 pm Reply

Good afternoon! Thank you for guide in other types of Keying.
Could you explain, please? What does this row fltr = bpsk_fir; mean in part of BPSK? MATLAB can’t recognize this command

Vladimir Leonidov Jan 12, 2023 at 5:16 pm Reply

Alexander, this filter was designed in Filter Designer utility from Matlab (type filterDesigner in console to open it). Here is generated function with my parameters: function Hd = bpsk_fir
%BPSK_FIR Returns a discrete-time filter object. % MATLAB Code
% Generated by MATLAB(R) 9.7 and DSP System Toolbox 9.9.
% Generated on: 17-Apr-2020 17:44:11 % Equiripple Lowpass filter designed using the FIRPM function. % All frequency values are in Hz.
Fs = 10000; % Sampling Frequency Fpass = 100; % Passband Frequency
Fstop = 250; % Stopband Frequency
Dpass = 0.057501127785; % Passband Ripple
Dstop = 0.0031622776602; % Stopband Attenuation
dens = 20; % Density Factor % Calculate the order from the parameters using FIRPMORD.
[N, Fo, Ao, W] = firpmord([Fpass, Fstop]/(Fs/2), [1 0], [Dpass, Dstop]); % Calculate the coefficients using the FIRPM function.
b = firpm(N, Fo, Ao, W, );
Hd = dfilt.dffir(b); % [EOF]

Alexander Jan 13, 2023 at 11:04 am
Thanks a lot!
Vladislav May 24, 2023 at 3:25 pm Reply

Здравствуйте, Владимир. У меня возник такой вопрос, я делаю модель передатчика и приёмника спутниковой системы связи с 16 QAM и мне необходимо произвести модуляцию сначала на промежуточной частоте 70МГц, а дальше через преобразователь частоты повысить частоты сигнала до 14ГГц и вот на моменте выделения суммы двух сигналов с помощью полосового фильтра у меня в сигнале пропадает скачкообразное изменение фазы и дальнейшая демодуляция этого сигнала в приёмники уже происходит с ошибками. Что бы вы могли посоветовать чтобы повысить частоту без потери информации?

Vladislav May 24, 2023 at 3:31 pm Reply
Схему создаю в симулинк
Vladimir Leonidov May 28, 2023 at 1:38 am Reply

Здравствуйте! А зачем Вы используете фильтр при модуляции? Он, само собой, уберёт скачкообразное изменение фазы, т.к. это, по сути, высокочастотная составляющая.

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

Ваш адрес email не будет опубликован. Обязательные поля помечены *