Materialet i artiklen er taget fra min .
Tone signal detector
I den forrige Vi har skabt en signalniveau måler. I denne vil vi lære at opdage tonale signaler.
![]()
I gamle dage, da ikke hver husstand havde fjernsyn, og halvdelen af dem skiftede kanaler med en tang, dukkede der en interessant nyhed op i anmeldelserne af udenlandsk teknisk presse, at en af producenterne havde udstyret sine apparater med en trådløs fjernbetjening. Det blev nævnt, at fjernbetjeningen arbejdede uden batterier ved hjælp af en usædvanlig tilgang - fjernbetjeningen var mekanisk og udgjorde et hybrid mellem et musikinstrument - et glockenspiel og en revolver. I revolverens tromle var der metalcylindre af forskellig længde, og når hammeren ramte en af dem, begyndte cylinderen at klinge på sin egen frekvens. Formodentlig ved ultralyd. Elektronik i fjernsynet hørte dette signal, og ved at bestemme frekvensen udførte den den tilsvarende handling - skiftede kanal, ændrede lydstyrken, slukkede fjernsynet.
I dag vil vi forsøge at rekonstruere dette commandsystem, ved at bruge vores viden om mediestrømmere.
For at efterligne fjernbetjeningen vil vi bruge teksten fra vores eksempel på en tonegenerator. Vi vil tilføje kontrol af generatorens frekvens fra tastetryk og en modtager med en dekoder, som vil vise de modtagne kommandoer i konsollen. Efter ændringen skal generatoren generere tonale signaler med 6 frekvenser, som vi vil kode kommandoerne til at øge / formindske lydstyrken, skifte kanal, tænde / slukke for fjernsynet. Til indstilling af detektoren bruges strukturen:
struct _MSToneDetectorDef{
char tone_name[8];
int frequency;
/**<Expected frequency of the tone*/
int min_duration;
/**<Min duration of the tone in milliseconds */
float min_amplitude;
/**<Minimum amplitude of the tone, 1.0 corresponding to the normalized 0dbm level */
};
typedef struct _MSToneDetectorDef MSToneDetectorDef;Detektoren kan modtage 10 sådanne strukturer, så en detektor kan indstilles til at opdage ti bipolære signaler. Men vi vil kun bruge seks monopole signaler. Til overførsel af indstillinger til detektoren bruges metoden MS_TONE_DETECTOR_ADD_SCAN.
For the detector to notify us that a signal with the desired frequency components has been received at its input, we need to provide it with a callback function that it will execute in such cases. This is done using the function ms_filter_set_notify_callback(). It receives a pointer to the filter, a pointer to the callback function, and a pointer to the data we would like to pass to the callback function (user data) as arguments.
When the detector is triggered, the callback function will receive user data, a pointer to the detector filter, an event identifier, and a structure describing the event:
/** * 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;
The structural scheme for signal processing is shown in the title image.
Now, here is the program code with comments.
/* Файл 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);
}
}We compile and run the program. If everything works correctly, we should see something like the following behavior in the program:
$ ./mstest4
ALSA lib conf.c:4738:(snd_config_expand) Unknown parameters 0
ALSA lib control.c:954:(snd_ctl_open_noupdate) Invalid CTL default:0
ortp-warning-Could not attach mixer to card: Invalid argument
ALSA lib conf.c:4738:(snd_config_expand) Unknown parameters 0
ALSA lib pcm.c:2266:(snd_pcm_open_noupdate) Unknown PCM default:0
ALSA lib conf.c:4738:(snd_config_expand) Unknown parameters 0
ALSA lib pcm.c:2266:(snd_pcm_open_noupdate) Unknown PCM default:0
ortp-warning-Strange, sound card Intel 82801AA-ICH does not seem to be capable of anything, retrying with plughw...
Press the command key, then Enter.
To exit the program, enter 0.
ortp-warning-alsa_set_params: periodsize:256 Using 256
ortp-warning-alsa_set_params: period:8 Using 8
Press any keys from "1" to "6", confirming with the "Enter" key; the output should be approximately as follows:
2
Command sent: 2
Command received: V-
1
Command sent: 1
Command received: V+
3
Command sent: 3
Command received: C+
4
Command sent: 4
Command received: C-
0
$We see that the command tones are successfully sent and the detector detects them.
In the next article, we will focus on transmitting audio signals over an Ethernet network using the RTP protocol and immediately apply it in our remote control.
Kilde: habr.com
