Наш магазин на eBay Наш магазин на AliExpress Наш канал в telegram

Приём и декодирование F/2F частотно-кодированных сигналов

В этой статье описаны простейшие алгоритмы приёма и декодирования F/2F частотно-кодированных сигналов c помощью микроконтроллера и персонального компьютера..

Для начала давайте разберёмся, что представляет собой F/2F кодирование и какие преимущества в плане хранения и передачи информации оно предоставляет.

F/2F кодированный сигнал

Итак. Частотным кодированием F/2F называется такой способ кодирования, при котором "1" кодируется удвоенной частотой сигнала по отношению к "0" (смотрите рисунок слева). Для того, чтобы сохранить (записать) такой сигнал на физическом носителе, достаточно поставить на носителе какие-нибудь метки на определённом расстоянии друг от друга, причём таким образом, чтобы расстояние между метками было пропорционально интервалу времени между фронтами. В зависимости от физического носителя и способа записи, метками могут быть: изменение светоотражения при записи лазерной головкой на компакт-диск, изменение полярности намагничивания при записи магнитной головкой на магнитной полосе или ленте, дырочки от дырокола на перфоленте, насечки на коре дуба и т.д.

Казалось бы: "Ну и что тут интересного?". А интересного тут на самом деле очень много. Такой способ кодирования и записи информации предоставляет следующие преимущества: 1) одинаковая длительность сигнала для "0" и "1"; 2) возможность автоматической синхронизации в процессе приёма сообщения. Рассмотрим эти преимущества подробнее:

1) Одинаковая длительность сигнала для "0" и "1" (то есть одинаковая длительность бита, независимо от состояния) очень удобна для хранения информации. В этом случае, при записи информации описанным выше способом, длина записи (а значит и физические размеры носителя) не зависит от количества нулей и единиц в записанном сообщении, а зависит только от числа записываемых бит и от размеров участка, на котором умещается один бит информации (то есть от плотности записи).

2) Возможность автоматической синхронизации в процессе приёма очень удобна при считывании информации, поскольку в этом случае нам не нужно никаких дополнительных синхронизирующих сигналов, кроме самого информационного сигнала. Нам не только абсолютно наплевать с какой скоростью производилась запись сигнала, но даже если скорость считывания изменяется во времени, то в случае, когда она не может скачком измениться более чем в полтора раза, мы можем уверенно восстановить исходный сигнал. Единственное, что нам нужно — это точно знать, с какого бита начинается сообщение — с "0" или с "1", чтобы провести начальную синхронизацию. Можно просто договориться помещать в начале сообщения один или несколько нулей или единиц для начальной синхронизации (обычно всё же используют несколько таких дополнительных синхронизирующих битов, на тот случай, если некоторое количество первых битов по каким-либо причинам не будет считано или окажется утерянным).

Давайте попробуем представить себе, как может выглядеть простейший алгоритм приёма и декодирования такого F/2F кодированного сигнала. Пусть нам нужно декодировать сигнал, подобный изображённому на рисунке вверху, но считанный при неравномерном движении головки. Пусть мы при этом точно знаем, что сигнал начинается с нулевого бита. Идея заключается в следующем:

1) С помощью контроллера измеряется время между фронтами сигнала и передаётся на компьютер.

2) На компьютере сигнал декодируется, исходя из гипотезы о том, что скорость считывания не может скачком измениться более чем в 1,5 раза.

Таким образом наша задача разбивается на две части: а) измерение временных интервалов между фронтами и передача их на ПК, б) декодирование сигнала на основе этих интервалов.

Рассмотрим отдельно обе эти части.

Часть1. Для измерения интервалов между фронтами и передачи этой информации на компьютер подойдёт обычный микроконтроллер со встроенным UART, компаратором и таймером, например, PIC16F628A. Собственно, процесс измерения временных интервалов довольно тривиален: на одну ногу компаратора подаётся опорное напряжение, равное половине амплитуды читаемого сигнала, на другую ногу — читаемый сигнал. Запускается таймер. В момент изменения уровня читаемого сигнала с "0" на "1" или с "1" на "0" происходит переключение компаратора, в результате чего генерируется прерывание. В обработчике прерывания считывается и сохраняется значение таймера, после чего таймер перезапускается, а сохранённое значение отправляется на COM-порт компьютера. На этом обработка прерывания заканчивается.

Передаваемая контроллером информация принимается на ПК любой терминальной программой (например, нашей RH_Com).

В результате мы получим набор байт, соответствующих длительностям интервалов между фронтами. В этом наборе нулевому биту будет соответствовать один байт длительности, а единичному биту — 2 байта в два раза меньшего значения ( в идеальном случае). Почему? — Снова смотрите рисунок вверху. В дальнейшем будем называть такие байты "байтами нуля" и "байтами единицы". Таким образом, ноль у нас будет соответствовать одному байту нуля, а единица — двум байтам единицы.

Алгоритм для первой части (приём контроллером F/2F кодированных сигналов)

На рисунке справа показан вариант алгоритма для реализации первой части нашей задачи.

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

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

То есть теоретический предел скорости приёма информации по такому алгоритму равен установленной скорости передачи по UART.

Алгоритм для второй части (декодирование F/2F кодированного сигнала на компьютере)

Часть 2. Собственно, вторая часть такая же банальная как и первая. Вариант алгоритма, реализующего эту часть, показан на рисунке слева. Как я уже сказал, основная идея в том, что скорость чтения не может скачкообразно измениться более чем в 1,5 раза. Итак, мы имеем в программе на компьютере строку с принятой от контроллера информацией (byte_str), каждый байт которой соответствует длительности между фронтами сигнала. Мы точно знаем, что первый байт — это байт нуля. Организуем ещё одну строку (пустую, msg_str), куда будем складывать декодированную битовую информацию в виде нулей и единиц.

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

Далее будем в цикле просматривать последующие байты, начиная со второго, и сравнивать их с байтами нуля и единицы. Если значение текущего анализируемого байта ближе к байту нуля, то и будем считать его байтом нуля. Допишем в msg_str ноль, а значение текущего байта примем за новое значение байта нуля (а так же вычислим на его основе новое значение байта единицы). Если же значение текущего анализируемого байта ближе к байту единицы, то будем считать текущий и следующий за ним байты — байтами единицы (помните, для единицы мы получаем 2 байта интервалов между фронтами, но оба в два раза меньше нулевого байта). Допишем в msg_str единицу, а сумму текущего и следующего за ним байтов будем считать новым значением байта нуля (опять же, разделив его пополам получим также новое значение байта единицы). И так до тех пор, пока не проанализируем все байты.

Вот, собственно, и всё! После выполнения действий, описанных в алгоритме, — в строке msg_str окажется декодированный исходный сигнал в двоичной форме.

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

Комментарии 6

  • Получается, вы измеряли время между восходящим фронтом первого импульса и восходящим фронтом следующего за ним импульса?

    • Нет, между каждым фронтом, неважно, восходящий или нисходящий. В этом случае у нас 0 — это один длинный интервал, а 1 — два коротких.

      • Здравствуйте еще раз. У меня почему-то получаются сигналы странной формы, и причем, если декодировать, то символы не пройдут контроль четности. Такая картина наблюдается всегда. Вот ссылка на изображение: https://ibb.co/84g7Xrn. Это начало считывания дорожки ISO2.

        • Чем они сняты (ккакой сцил и какие у него характеристики) и откуда (в какой точке и относительно чего)?

          • Arduino Oscilloscope. Это сторонняя программа.
            Его характеристики:
            Напряжения: до напряжения питания(5В)
            Максимальная частота Синуса: 500Гц
            Максимальная частота Пила: 2000Гц
            Максимальная частота Прямоугольник: 350Гц
            Скорость выборки: 0.001В/с(А кто его знает?)
            Сопротивления: 5.4МОм
            Количество каналов: 7 NANO(5 UNO)

            На микроконтроллере Arduino Uno (на него приходит сигнал с головки) код:

            void setup() {
            Serial.begin(115200);
            }
            void loop() {
            int analogvalue;
            float voltage;
            analogvalue = analogRead(A0);
            voltage = analogvalue/204.6;
            Serial.println(voltage);
            }

            По оси x — время, y — напряжение.

            • А головка считывающая такая же как у меня?
              В целом, я полагаю, что если все компоненты идентичны, то я бы не сильно доверял этому осциллографу. Лучше тогда провести натурные испытания.
              Если головка другая, то возможны варианты.

              P.S. Жаль конечно, что я не прикрепил к статьям свои осциллограммы. Сейчас бы хоть можно было посмотреть, какой там порядок частот.

              P.P.S. Но можно прикинуть. Судя по программе, максимально долгий промежуток между фронтами, который таймер может измерить, составляет около 4 мс. То есть fmin = 1/4 мс = 250 Гц, соответственно 2fmin=500 Гц. Это прям край, значит реальные частоты выше. Между тем прямоугольник в 500 Гц уже не влезает в характеристики вашего осциллографа.

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