Matlab电话按键拨号器设计

夏天是冰红茶 2024-07-14 09:05:01 阅读 62

前言

这篇文章是目前最详细的 Matlab 电话按键拨号器设计开源教程。如果您在做课程设计或实验时需要参考本文章,请注意避免与他人重复,小心撞车。博主做这个也是因为实验所需,我在这方面只是初学者,但实际上,从完全不懂 DTMF 和 Matlab 的 App 设计,到功能设计完备,也不过花了两个下午而已。在这个过程中,我也尝试搜索资料,发现可选的资源不仅有限,还需要付费。因此,我只能从仅有的资料和视频中推测该做些什么。在此,希望大家在跟随这篇文章学习时,能够以学习的态度面对。

DTMF原理与实现

一、DTMF简介

DTMF是一种信号系统,广泛应用于电话按键音的传输。它是由两个不同频率的音调组合而成,每个按键(0-9,*,#)对应一个唯一的频率组合,这样可以通过按键发出的声音来传输数据。

按键和频率对应表: 

按键 低频组 高频组
1 697 Hz 1209 Hz
2 697 Hz 1336 Hz
3 697 Hz 1477 Hz
A 697 Hz 1631 Hz
4 770 Hz 1209 Hz
5 770 Hz 1336 Hz
6 770 Hz 1477 Hz
B 770 Hz 1631 Hz
7 852 Hz 1209 Hz
8 852 Hz 1336 Hz
9 852 Hz 1477 Hz
C 852 Hz 1631 Hz
* 941 Hz 1209 Hz
0 941 Hz 1336 Hz
# 941 Hz 1477 Hz
D 941 Hz 1631 Hz

下方图更加具体形象一点: 

c2e7273323904a218503f51bb9e1ba0b.png

工作过程

按键识别:当用户按下电话按键时,电话生成相应的DTMF信号。信号传输:DTMF信号通过电话线路传输。信号解码:接收端(例如电话交换机)接收到DTMF信号,并通过滤波器和检测器识别出对应的按键。

每个按键只需两个频率,信号生成和检测简单,且具有较高的抗干扰能力,即使在嘈杂的环境中也能准确传输信息。

二、DTMF编码实现

我们首先需要的输入一个1~12以内组成的一个号码序列,其中1~9对应键盘数字1~9对应键盘数字1~9,而对于0、*、#我们分别将其映射为数字10、11、12。

每当按下键盘时候会发声音,采样频率为8kHz,每个拨音持续0.5s,拨音之间间隔0.1s停顿。

这里要做映射的内容只有下面的部分

 

<code> 1209 Hz 1336 Hz 1477 Hz

697 Hz 1 2 3

770 Hz 4 5 6

852 Hz 7 8 9

941 Hz * 0 #

通过生成这两个频率的正弦波,并将它们相加,可以得到一个 DTMF 信号。例如,按下 '1' 时,会生成如下信号:

eq?s%28t%29%20%3D%20sin%282%20%5Cpi%20%5Ccdot697%5Ccdot%20t%29&plus;sin%282%5Cpi%5Ccdot%201209%5Ccdot%20t%29

<code>function tones = dtmfdial(nums)

% @ 夏天是冰红茶

% DTMFDIAL Create a vector of tones which will dial

% a DTMF (Touch Tone) telephone system

% usage: tones = dtmfdial(nums)

% nums = vector of numbers ranging from 1 to 12

% tones = vector containing the corresponding tones

if nargin < 1

error('DTMFDIAL requires one input');

end

output_signal = [];

% 定义DTMF音调的频率

low_freqs = [697, 770, 852, 941];

high_freqs = [1209, 1336, 1477, 1633];

% 数字序列行列索引

dtmf_map = [1, 1; 1, 2; 1, 3; % 1, 2, 3

2, 1; 2, 2; 2, 3; % 4, 5, 6

3, 1; 3, 2; 3, 3; % 7, 8, 9

4, 2; 4, 1; 4, 3]; % 0, *, #

% Define parameters

fs = 8000;

duration = 0.5;

pause_time = 0.1;

t_tone = 0:1/fs:duration - 1/fs;

t_pause = 0:1/fs:pause_time - 1/fs;

% 暂停静音

silence = zeros(size(t_pause));

% 给每个号码生成DTMF音调

for i = 1:length(nums)

num = nums(i);

if num < 1 || num > 12

error('Number sequence must contain values between 1 and 12');

end

% 获取DTMF映射的相应行、列索引

row = dtmf_map(num, 1);

col = dtmf_map(num, 2);

% 生成DTMF音调

tone = sin(2*pi*low_freqs(row)*t_tone) + sin(2*pi*high_freqs(col)*t_tone);

output_signal = [output_signal, tone, silence];

end

tones = output_signal;

end

三、DTMF解码实现

DTMF解码有两个部分组成,分别是由一个带通滤波器和一个检测器组成的。

其中带通滤波器用于分离各频率成分,检测器用于检测所有带通滤波器输出信号的大小,从而判断在每个时间段中存在哪两个频率分量,检测器用于确定哪两个频率最有可能包含在这个DTMF音中。

滤波器的设计如下:

eq?h%5Bn%5D%20%3D%20%5Cfrac%7B2%7D%7BL%7Dcos%28%5Cfrac%7B2%5Cpi%20f_%7Bb%7Dn%7D%7Bf_%7Bs%7D%7D%29

这里,L表示滤波器长度,

eq?f_%7Bs%7D

表示采样频率,

eq?f_%7Bb%7D

表示带通滤波器的中心频率。L越大,带宽越窄。

这个实现非常简单

<code>function h = Zjr_Bandpass_Filter(fb, L, fs)

% @ 夏天是冰红茶

% Zjr_Bandpass_Filter Generate a bandpass filter based on given parameters

% fb: Center frequency of the bandpass filter

% L: Length of the filter

% fs: Sampling frequency

if nargin < 3

% 如果没有提供fs,则使用默认值8000

fs = 8000;

end

n = 0:L-1;

h = (2 / L) * cos(2 * pi * fb * n / fs);

end

DTMF检测器设计

function ss = dtmfscor(xx, freq, L, fs)

% @ 夏天是冰红茶

% DTMFSCOR

% ss = dtmfscor(xx, freq, L, [fs])

% return 1(true) if freq is present in xx

% 0(false) if freq is not present in xx

% xx = input DTMF signal

% freq = test frequency

% L = length of FIR bandpass filter

% fs = sampling frequency (default is 8k)

% The signal detection is done by filtering xx with a length-L

% BPF, hh, squaring the output, and comparing with an arbitrary

% set point based on the average power of xx

if nargin < 4

fs = 8000;

end

hh = Zjr_Bandpass_Filter(freq, L, fs);

filtered_signal = conv(xx, hh, 'same');

% 计算平方滤波信号的平均功率

squared_signal = filtered_signal .^ 2;

mean_squared_signal = mean(squared_signal);

% 计算原始信号的平均功率

mean_original_signal = mean(xx .^ 2);

% 滤波信号的平均功率与阈值进行比较

threshold = mean_original_signal / 5;

ss = (mean_squared_signal > threshold);

end

DTFM编码部分的实现基于以上两个部分完成,它的基本原理就是通过检测信号中存在的特定频率来确定按下的键。每个 DTMF 按键对应两个频率,一个低频和一个高频。通过检测这些频率的存在,可以确定按下的按键。

function key = dtmfdeco(xx, L, fs)

% @ 夏天是冰红茶

% DTMFDECO key = dtmfdeco(xx, [fs])

% returns the key number corresponding to the DTMF waveform, xx

% fs = sampling freq (default = 8k Hz if not specified)

if nargin < 2

fs = 8000;

end

% 定义DTMF音调的频率

low_freqs = [697, 770, 852, 941];

high_freqs = [1209, 1336, 1477, 1633];

% 数字序列行列索引

dtmf_map = [1, 1; 1, 2; 1, 3; % 1, 2, 3

2, 1; 2, 2; 2, 3; % 4, 5, 6

3, 1; 3, 2; 3, 3; % 7, 8, 9

4, 2; 4, 1; 4, 3]; % 0, *, #

% 初始化检测结果

low_detected = false(length(low_freqs), 1);

high_detected = false(length(high_freqs), 1);

% 检测低频分量

for i = 1:length(low_freqs)

if dtmfscor(xx, low_freqs(i), L, fs)

low_detected(i) = true;

end

end

% 检测高频分量

for i = 1:length(high_freqs)

if dtmfscor(xx, high_freqs(i), L, fs)

high_detected(i) = true;

end

end

% 找到检测到的低频和高频索引

low_idx = find(low_detected);

high_idx = find(high_detected);

% 确保每次只检测到一个低频和一个高频

if isscalar(low_idx) && isscalar(high_idx)

key = find(ismember(dtmf_map, [low_idx, high_idx], 'rows'));

else

key = [];

end

end

四、DTMF程序验证

接下来我们需要对我们前面所写的函数进行验证。

使用 dtmfdial 函数生成拨号音序列,并使用 sound 函数播放这些音调,通过遍历 input_keys,我们逐个解码每个拨号音:

确定当前拨号音的起始和结束索引。提取当前的拨号音段。使用 dtmfdeco 函数解码当前的拨号音段。将解码结果存储在 decoded_keys 数组中。更新起始索引,以处理下一个拨号音。

clc;

L=64;

input_keys = [1, 2, 3, 10, 11, 12];

encoded_tones = dtmfdial(input_keys);

sound(encoded_tones, 8000);

decoded_keys = [];

sample_duration = 0.5; % 每个拨号音的持续时间

gap_duration = 0.1; % 拨号音之间的停顿时间

fs = 8000; % 采样频率

% 按照编码的音序列的格式解析每个拨号音

start_index = 1;

for i = 1:length(input_keys)

end_index = start_index + sample_duration * fs - 1;

current_tone = encoded_tones(start_index:end_index);

decoded_key = dtmfdeco(current_tone, L, fs);

decoded_keys = [decoded_keys, decoded_key];

start_index = end_index + gap_duration * fs + 1;

end

% 输出解码结果

fprintf('Decoded keys: ');

disp(decoded_keys);

% 验证解码结果是否与输入的按键序列一致

if isequal(input_keys, decoded_keys)

fprintf('The decoded keys match the input keys.\n');

else

fprintf('The decoded keys do not match the input keys.\n');

end

打印结果如下所示:

Decoded keys:      1     2     3    10    11    12

The decoded keys match the input keys.

验证成功!

Matlab的app设计

这个部分理应用你自己完成,这里我只是打个样。接下来我之会讲解一下其中回调函数中重要的一些地方,建议每个部件都应该有自己的名字,就像是使用Qt或者PyQt一样。

a836f9d834c54729a6f75867b63751a6.png

按钮的回调

这里以按钮1为例,我重命名为:app.Key_1,后面按钮均按照这样的规律。我们需要在按下键1时可以发出声音,并且将内容显示在其上方的文字框(app.Text_Dialing)当中,而且要让频谱图显示在左侧的坐标当中。

<code> % Button pushed function: Key1

function Key1ButtonPushed(app, event)

% 按键1的回调函数,按下后在文本框中显示

currentText = app.Text_Dialing.Value; % 当前文本区域的值

if isempty(currentText)

newText = '1';

else

newText = strcat(currentText{1}, '1');

end

app.Text_Dialing.Value = {newText};

encoded_tones = dtmfdial([1]);

sound(encoded_tones, 8000);

displaySpectrum(app, encoded_tones);

end

displaySpectrum为本路径下写的一个功能函数,即显示当前按钮的频谱图,每次点击都会被刷新,该功能的实现很简单,请自行在下面的资源中查找。

这个接下来就是复制粘贴到我们每个按钮的回调了。

拨号与挂断的回调

当点击拨号时,将会对之前输入的电话序号进行发音,发音结束后询问是否要保存音频。当我点击挂断时候,刷新我们的文字框以及坐标轴。需要注意的是,这里的文字框显示的是*、#、0,所以一定要在传入函数前进行映射。

% Value changed function: Key_Dialing

function Key_DialingValueChanged(app, event)

value = app.Key_Dialing.Value;

currentText = app.Text_Dialing.Value;

% 将当前文本区域的值转换为字符数组

if ~isempty(currentText)

currentText = currentText{1}; % 转换为字符串

dialedNumbers = [];

% 遍历当前文本的每个字符

for i = 1:length(currentText)

char = currentText(i);

if ismember(char, ['0':'9', '*', '#'])

switch char

case '0'

num = 10;

case '*'

num = 11;

case '#'

num = 12;

otherwise

num = str2double(char);

end

dialedNumbers(end+1) = num;

end

end

disp(dialedNumbers);

encoded_tones = dtmfdial(dialedNumbers);

sound(encoded_tones, 8000);

duration = length(encoded_tones) / 8000;

% 暂停等待拨号音结束

pause(duration);

choice = questdlg('是否保存该音调?', ...

'保存音调', ...

'是', '否', '否');

switch choice

case '是'

[file, path] = uiputfile('*.wav', '保存音调为');

if ischar(file) && ischar(path)

filename = fullfile(path, file);

normalized_tones = encoded_tones / max(abs(encoded_tones));

audiowrite(filename, normalized_tones, 8000);

msgbox('音调已保存', '保存成功');

else

msgbox('保存已取消', '取消');

end

case '否'

% 不做任何处理

end

end

end

音频转为数字序号

这部分可以讲一讲,下面的代码是我写的测试草稿,app中用到的具体的函数名叫convert_wav2num。

clc;

filename = 'test.wav';

[y, fs] = audioread(filename);

L = 64; % DTMF 解码的长度参数

sample_duration = 0.5; % 每个拨号音的持续时间

gap_duration = 0.1; % 拨号音之间的停顿时间

decoded_numbers = [];

start_index = 1;

while start_index <= length(y)

end_index = start_index + round(sample_duration * fs) - 1;

if end_index > length(y)

end_index = length(y);

end

current_tone = y(start_index:end_index);

decoded_key = dtmfdeco(current_tone, L, fs);

if ~isempty(decoded_key)

decoded_numbers = [decoded_numbers, decoded_key];

end

start_index = end_index + round(gap_duration * fs);

end

fprintf('Decoded phone numbers: ');

disp(decoded_numbers);

首先参数的定义要与前面保存一致。从指定的音频文件中读取音频数据,并获取采样率。遍历音频数据,将其分割成独立的拨号音段,并对每个音段进行DTMF解码,输出解码得到的电话号码。

运行截图如下所示:

a79be64bd8c744f0a7305ed5d2735991.png

解码的回调

这里可以通过直接在文字框中输入wav文件的路径,也可以通过上面菜单栏选项当中的打开资源管理器选择。然后直接点击解码,通过弹窗显示解码的电话号码。

<code> % Value changed function: Key_Dialing_Decoding

function Key_Dialing_DecodingValueChanged(app, event)

wavPath = app.Decoding_path.Value;

if isempty(wavPath) || ~isfile(wavPath)

msgbox('请选择有效的 WAV 文件路径');

return;

end

decoded_numbers = convert_wav2num(wavPath, 64, 0.5, 0.1);

encoded_tones = dtmfdial(decoded_numbers);

sound(encoded_tones, 8000);

decoded_numbers_str = {};

for i = 1:length(decoded_numbers)

switch decoded_numbers(i)

case 10

decoded_numbers_str{end+1} = '0';

case 11

decoded_numbers_str{end+1} = '*';

case 12

decoded_numbers_str{end+1} = '#';

otherwise

decoded_numbers_str{end+1} = num2str(decoded_numbers(i));

end

end

if ~isempty(decoded_numbers_str)

msgbox(['解码结果: ', strjoin(decoded_numbers_str)], '解码结果');

else

msgbox('解码失败', '解码结果');

end

end

动图演示

12f466970fac4aca8774adba58e71889.gif

项目资源

请通过GitHub下载,你的Start就是对我最大的帮助:

Auorui/Design-of-Matlab-Phone-Key-Dialer: Matlab电话按键拨号器设计 (github.com)

本人matlab版本为2024a,低版本可能会出ColorPicker报错,直接删除包含的字段即可。 

其中也可以下载exe版本

按键按的太快声音会卡顿,可以将sound函数换成soundsc即可解决。

参考文章

DTMF_百度百科 (baidu.com)

数字信号处理综合实验——Matlab实现DTMF信号的产生与提取_dtmf信号的产生及检测matlab-CSDN博客

【数字信号】基于matlab GUI DTMF电话模拟系统(频谱图+时域图+语谱图)【含Matlab源码 2092期】_用matlab程序设计电话拨键的gui页面,当按键被输进去以后,会显示时域或频域波形,之-CSDN博客

 

 

 

 

 



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。