Речевой скремблер (voice inversion) на одной tiny13 :)

Скремблер/дескремблер речи, использующий принцип инвертирования спектра (метод частотной инверсии), с регулируемой частотой инвертирования от 950Гц до 4.5КГц, предназначен для встраивания в трансивер. Несмотря на лаконичность схемы, прошивка получилась непростая 🙂 Схема и прошивка предназначена для доработки портативного трансивера Гранит Р33П-1 по приёму, однако, инвертор легко встраивается и в другие трансиверы.

vinv01

Звуковой сигнал трансивера снимается до регулятора громкости и подаётся на вывод AU IN. В зависимости от режима DD1, сигнал проходит через инвертор спектра (скремблер активен) или пропускается без преобразования (режим bypass). Режим выбирается с помощью кнопки на энкодере BS1 и запоминается в энергонезависимой памяти контроллера. Индикация режима производится подачей одного бипа при активации скремблера и двух бипов при дезактивации по нажатию кнопки BS1.

При повороте энкодера при активном скремблере производится увеличение или уменьшение частоты инвертирования. При достижении минимального значения частоты (950Гц), подаётся длинный бип низкого тона; при достижении максимального значения частоты (4500Гц), подаётся длинный бип высокого тона. При повороте ручки энкодера подаётся звуковой сигнал из одного короткого бипа при достижении частоты 1КГц, 2х коротких бипов при достижении частоты 2КГц, 3х коротких бипов при достижении частоты 3КГц, 4х коротких бипов при достижении частоты 4КГц. Частота инвертирования сохраняется в энергонезависимой памяти. При неактивном скремблере поворот ручки энкодера игнорируется.

vinv02Так выглядит макет речевого инвертора

Вывод nSQL разрешает/запрещает прохождение звукового сигнала на вывод AU OUT (звук разрешён при низком уровне nSQL), а также переводит контроллер в режим Power-down для снижения потребления.

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

Для подключения к трансиверу Гранит Р33П-1 надо:

  • 1. выпаять RA42: получим две точки подключения AU IN и AU OUT
  • 2. подключить nSQL к выв.3 BUSY_SW разъёма PF1
  • 3. подключить +5V к выв.8 разъёма PF1
  • 4. подключить GND к выв.1 разъёма PF1

При подключении к другому трансиверу следует учитывать, что размах сигнала на входе AU IN должен быть Vp-p = 1V, т.к. опорный уровень ADC 1.1V. При добавлении входного усилителя, если не хватает уровня сигнала на входе, не следует забывать про входное сопротивление усилителя, т.к выходы звука внутри трансиверов обычно высокоомные, и, в случае несогласованности, звук будет не тихим, а искажённым. Если напряжение питания выбрано ниже 5V, следует изменить номиналы резисторов R1, R2 так, чтобы на выв.7 DD1 без сигнала было напряжение ~0.55V (средняя точка ADC).

Видео, демонстрирующее работу голосового инвертора.

Прошивку можно скачать здесь.

Декодирование инверсного сигнала производится перемножением входного сигнала с мгновенным значением синусоиды, т.е. производится гетеродинирование, а при гетеродинировании происходит инверсия спектра. Разумеется, при перемножении образуются 2 полосы: верхняя и нижняя, но верхняя полоса находится достаточно высоко, и её можно убрать низкочастотным фильтром. Если описывать "на пальцах", то основной задачей для tiny13 является формирование синусоиды, которая остаётся похожей на синус при изменении частоты 🙂 ; в принципе, эту синусоиду можно вывести на DAC и получить эдакий генератор низкой частоты, работающий в узком диапазоне. Можно пойти ещё дальше: задать не синусоиду, а меандр (sign(cos(x)), но уж больно пердючий звук на выходе получается. Короче, чем меньше ошибок при арифметике, тем выше качество звука.

Вся "магия" инвертирования для беззнаковых 8ми разрядных сэмплов описывается формулой:

 out = (BYTE)((float)(sample[n] - offset) * cos(2 * PI * L * mfreq / sfreq) + offset);
 где sfreq = 12500 - частота сэмплирования,
 mfreq = 2500 - частота инверсии
 offset = 128 - смещение для перевода беззнакового uint8_t в знаковое char
 значение L должно инкрементироваться после каждого сэмпла и теоретически должно
 сбрасываться в 0 после достижения косинусом полного периода, однако, на это не
 заморачиваемся: работает и так, при изменениях L=0..1023 и более артефактов не слышно.
 
Использовать косинус удобнее, т.к это чётная функция (меньше артефактов в итоге),
хотя особой разницы в использовании синуса или косинуса нет. В риале используется
целочисленная арифметика (16x8 бит), косинус вычисляется таблично.

"Магия" цифровой фильтрации (one pole LPF) основана на формуле:

out = A0 * sample[n] - B1 * prev
prev = out
 где A0 = 1.0 - x;
     B1 = -x;
      x = exp(-2 * PI * lpf_freq / sfreq)
   prev - предыдущее значение out, для 1го вывода можно принять prev=0
Для lpf_freq = 300Hz и sfreq = 12500Hz, x = 0.86, A0 = 0.14, B1 = -0.86, т.е. для
целочисленной арифметики A0 = 127 * 0.1 = 17, B1 = -110. В риале это выглядит как
out = (17 * sample[n])/256 + (110 * prev)/256, т.е. вклад ранее сформированного сэмпла
в выходной сигнал больше, чем текущего.

Ну, и не забываем, что вся арифметика в преобразованиях — знаковая. Беззнаковые значения появляются только на входе (ADC) и выходе (DAC).