Materialul articolului provine de pe canalul meu .
Detectorul semnalului tonic
În trecut, Am creat un măsurător al nivelului semnalului. În această lecție, vom învăța cum să detectăm semnalul tonic.
![]()
În vremurile de demult, când nu fiecare familie avea televizor și jumătate dintre ele își schimbau canalele cu ajutorul șurubelniței, presa tehnică străină a anunțat o veste interesantă, că unul dintre producătorii de televizoare a echipat aparatele sale cu un control de la distanță fără fir. Din detalii se știa că telecomanda funcționa fără baterii datorită unei abordări neobișnuite — telecomanda era mecanică și reprezenta un hibrid între un instrument muzical — metalofon și un revolver. În tamburul revolverului erau cilindri metalici, diferiți ca lungime, iar când pe unul dintre ei lovea capul de foc, cilindrul începea să vibreze la propria frecvență. Se presupune că era pe ultrasunete. Electronica din televizor auzea acest semnal și, determinându-i frecvența, executa acțiunea corespunzătoare — schimba canalul, modifica volumul, oprea televizorul.
Astăzi vom încerca să reconstruim acest sistem de transmitere a comenzilor, folosindu-ne de cunoștințele noastre despre mediastreamere.
Pentru a imita telecomanda, vom folosi textul exemplului nostru de generator tonal. Vom adăuga în acesta controlul frecvenței generatorului prin apăsările de pe tastatură și un receptor cu decodor, care va afișa comenzile primite în consolă. După modificare, generatorul ar trebui să emită semnale tonale la 6 frecvențe, prin care vom codifica comenzile de creștere/ Reducere a volumului, schimbare a canalului, pornire/oprire a televizorului. Pentru configurarea detectorului se folosește structura:
struct _MSToneDetectorDef{
char tone_name[8];
int frequency; \/**<Frecvența așteptată a tonului*\/
int min_duration; \/**<Durata minimă a tonului în milisecunde *\/
float min_amplitude; \/**<Amplitudinea minimă a tonului, 1.0 corespunzătoare nivelului normalizat de 0dbm *\/\n};\n\ntypedef struct _MSToneDetectorDef MSToneDetectorDef;Detectorului i se pot transmite 10 astfel de structuri, astfel încât un detector poate fi configurat pentru a detecta zece semnale cu două tonuri. Dar noi vom folosi doar șase semnale simple. Pentru a trimite configurațiile detectorului se folosește metoda MS_TONE_DETECTOR_ADD_SCAN.
Pentru ca detectorul să ne avertizeze că a primit un semnal cu componentele de frecvență căutate, trebuie să-i oferim o funcție de callback, pe care o va executa în această situație. Aceasta se face prin funcția ms_filter_set_notify_callback(). Ca argumente, ea primește un pointer către filtrul respectiv, un pointer către funcția de callback și un pointer către datele pe care dorim să le transmitem funcției de callback (date utilizator).
La activarea detectorului, funcția de callback va primi datele utilizatorului, un pointer către filtrul detectorului, un identificator al evenimentului și o structură care descrie evenimentul:
/** * Structure carried as argument of the MS_TONE_DETECTOR_EVENT**/
struct _MSToneDetectorEvent{
char tone_name[8]; /* Имя тона которое мы ему назначили при настройке детектора. */
uint64_t tone_start_time; /* Время в миллисекундах, когда тон был обнаружен. */
};
typedef struct _MSToneDetectorEvent MSToneDetectorEvent;
Schema structurată de procesare a semnalului este prezentată în imaginea de antet.
Iar acum, codul programului cu comentarii.
/* Файл mstest4.c Имитатор пульта управления и приемника. */
#include <mediastreamer2/msfilter.h>
#include <mediastreamer2/msticker.h>
#include <mediastreamer2/dtmfgen.h>
#include <mediastreamer2/mssndcard.h>
#include <mediastreamer2/msvolume.h>
#include <mediastreamer2/mstonedetector.h>
/* Подключаем заголовочный файл с функциями управления событиями
* медиастримера. */
#include <mediastreamer2/mseventqueue.h>
/* Функция обратного вызова, она будет вызвана фильтром, как только он
* обнаружит совпадение характеристик входного сигнала с заданными. */
static void tone_detected_cb(void *data, MSFilter *f, unsigned int event_id,
MSToneDetectorEvent *ev)
{
printf(" Принята команда: %sn", ev->tone_name);
}
int main()
{
ms_init();
/* Создаем экземпляры фильтров. */
MSFilter *voidsource = ms_filter_new(MS_VOID_SOURCE_ID);
MSFilter *dtmfgen = ms_filter_new(MS_DTMF_GEN_ID);
MSFilter *volume = ms_filter_new(MS_VOLUME_ID);
MSSndCard *card_playback =
ms_snd_card_manager_get_default_card(ms_snd_card_manager_get());
MSFilter *snd_card_write = ms_snd_card_create_writer(card_playback);
MSFilter *detector = ms_filter_new(MS_TONE_DETECTOR_ID);
/* Очищаем массив находящийся внутри детектора тонов, он описывает
* особые приметы разыскиваемых сигналов.*/
ms_filter_call_method(detector, MS_TONE_DETECTOR_CLEAR_SCANS, 0);
/* Создаем источник тактов - тикер. */
MSTicker *ticker=ms_ticker_new();
/* Соединяем фильтры в цепочку. */
ms_filter_link(voidsource, 0, dtmfgen, 0);
ms_filter_link(dtmfgen, 0, volume, 0);
ms_filter_link(volume, 0, detector, 0);
ms_filter_link(detector, 0, snd_card_write, 0);
/* Подключаем к фильтру функцию обратного вызова. */
ms_filter_set_notify_callback(detector,
(MSFilterNotifyFunc)tone_detected_cb, NULL);
/* Подключаем источник тактов. */
ms_ticker_attach(ticker,voidsource);
/* Создаем массив, каждый элемент которого описывает характеристику
* одного из тонов, который требуется обнаруживать: Текстовое имя
* данного элемента, частота в герцах, длительность в миллисекундах,
* минимальный уровень относительно 0,775В. */
MSToneDetectorDef scan[6]=
{
{"V+", 440, 100, 0.1}, /* Команда "Увеличить громкость". */
{"V-", 540, 100, 0.1}, /* Команда "Уменьшить громкость". */
{"C+", 640, 100, 0.1}, /* Команда "Увеличить номер канала". */
{"C-", 740, 100, 0.1}, /* Команда "Уменьшить номер канала". */
{"ON", 840, 100, 0.1}, /* Команда "Включить телевизор". */
{"OFF", 940, 100, 0.1} /* Команда "Выключить телевизор". */
};
/* Передаем в детектор тонов приметы сигналов. */
int i;
for (i = 0; i < 6; i++)
{
ms_filter_call_method(detector, MS_TONE_DETECTOR_ADD_SCAN,
&scan[i]);
}
/* Настраиваем структуру, управляющую выходным сигналом генератора.*/
MSDtmfGenCustomTone dtmf_cfg;
dtmf_cfg.tone_name[0] = 0;
dtmf_cfg.duration = 1000;
dtmf_cfg.frequencies[0] = 440;
/* Будем генерировать один тон, частоту второго тона установим в 0.*/
dtmf_cfg.frequencies[1] = 0;
dtmf_cfg.amplitude = 1.0;
dtmf_cfg.interval = 0.;
dtmf_cfg.repeat_count = 0.;
/* Организуем цикл сканирования нажатых клавиш. Ввод нуля завершает
* цикл и работу программы. */
char key='9';
printf("Нажмите клавишу команды, затем ввод.n"
"Для завершения программы введите 0.n");
while(key != '0')
{
key = getchar();
if ((key >= 49) && (key <= 54))
{
printf("Отправлена команда: %cn", key);
/* Устанавливаем частоту генератора в соответствии с
* кодом нажатой клавиши.*/
dtmf_cfg.frequencies[0] = 440 + 100*(key-49);
/* Включаем звуковой генератор c обновленной частотой. */
ms_filter_call_method(dtmfgen, MS_DTMF_GEN_PLAY_CUSTOM,
(void*)&dtmf_cfg);
}
ms_usleep(20000);
}
}Compilăm și rulăm programul. Dacă totul funcționează corect, după lansare ar trebui să obținem un comportament similar cu următorul:
$ ./mstest4
ALSA lib conf.c:4738:(snd_config_expand) Parametrii necunoscuți 0
ALSA lib control.c:954:(snd_ctl_open_noupdate) CTL invalid default:0
ortp-warning- Nu s-a putut atașa mixerul la card: Argument invalid
ALSA lib conf.c:4738:(snd_config_expand) Parametrii necunoscuți 0
ALSA lib pcm.c:2266:(snd_pcm_open_noupdate) PCM necunoscut default:0
ALSA lib conf.c:4738:(snd_config_expand) Parametrii necunoscuți 0
ALSA lib pcm.c:2266:(snd_pcm_open_noupdate) PCM necunoscut default:0
ortp-warning-Stran, placa de sunet Intel 82801AA-ICH nu pare să fie capabilă de nimic, încercăm din nou cu plughw...
Apăsați tasta comenzii, apoi Enter.
Pentru a încheia programul, introduceți 0.
ortp-warning-alsa_set_params: sizes de perioada:256 Folosind 256
ortp-warning-alsa_set_params: perioada:8 Folosind 8
Apăsați orice taste de la "1" la "6", confirmând cu tasta "Enter", ar trebui să obținem un listing similar:
2
Comandă trimisă: 2
Comandă primită: V-
1
Comandă trimisă: 1
Comandă primită: V+
3
Comandă trimisă: 3
Comandă primită: C+
4
Comandă trimisă: 4
Comandă primită: C-
0
$Vedem că tonurile comenzilor sunt trimise cu succes și detectorul le detectează.
În următorul articol, ne vom ocupa de transmiterea semnalului audio prin rețeaua Ethernet folosind protocolul RTP și îl vom aplica imediat în telecomanda noastră.
Sursa: habr.com
