Matlab电话按键拨号器设计

前言

这篇文章是目前最详细的 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

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

工作过程

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

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

二、DTMF编码实现

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

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

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

Matlab 复制代码
         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' 时,会生成如下信号:

Matlab 复制代码
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音中。

滤波器的设计如下:

这里,L表示滤波器长度,表示采样频率,表示带通滤波器的中心频率。L越大,带宽越窄。

这个实现非常简单

Matlab 复制代码
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检测器设计

Matlab 复制代码
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 按键对应两个频率,一个低频和一个高频。通过检测这些频率的存在,可以确定按下的按键。

Matlab 复制代码
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 数组中。
  • 更新起始索引,以处理下一个拨号音。
Matlab 复制代码
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一样。

按钮的回调

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

Matlab 复制代码
        % 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,所以一定要在传入函数前进行映射。

Matlab 复制代码
        % 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。

Matlab 复制代码
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解码,输出解码得到的电话号码。

运行截图如下所示:

解码的回调

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

Matlab 复制代码
        % 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

动图演示

项目资源

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

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

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

其中也可以下载exe版本

参考文章

DTMF_百度百科 (baidu.com)

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

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

相关推荐
SRY122404192 小时前
javaSE面试题
java·开发语言·面试
无尽的大道3 小时前
Java 泛型详解:参数化类型的强大之处
java·开发语言
ZIM学编程3 小时前
Java基础Day-Sixteen
java·开发语言·windows
放逐者-保持本心,方可放逐3 小时前
react 组件应用
开发语言·前端·javascript·react.js·前端框架
一丝晨光4 小时前
编译器、IDE对C/C++新标准的支持
c语言·开发语言·c++·ide·msvc·visual studio·gcc
阮少年、4 小时前
java后台生成模拟聊天截图并返回给前端
java·开发语言·前端
代码小鑫4 小时前
A027-基于Spring Boot的农事管理系统
java·开发语言·数据库·spring boot·后端·毕业设计
程序猿-瑞瑞5 小时前
11 go语言(golang) - 数据类型:结构体
开发语言·golang
奶味少女酱~5 小时前
常用的c++特性-->day02
开发语言·c++·算法
阑梦清川6 小时前
数学建模---利用Matlab快速实现机器学习(上)
机器学习·数学建模·matlab·预测算法