function dtmf_phone_recognition_improved()
% 改进的DTMF电话号码识别系统
% 能够抵抗不同程度的高斯白噪声干扰
% 参数设置 Fs = 44100; % 采样率 tone_duration = 0.1; % 每个音调持续时间(秒) pause_duration = 0.05; % 音调间隔时间(秒) % DTMF频率定义 low_freqs = [697, 770, 852, 941]; % 低频组 high_freqs = [1209, 1336, 1477, 1633]; % 高频组 % DTMF键盘映射 dtmf_keys = [ '1', '2', '3', 'A'; '4', '5', '6', 'B'; '7', '8', '9', 'C'; '*', '0', '#', 'D' ]; % 读取或生成DTMF信号 [clean_signal, Fs] = generate_or_load_dtmf_signal(); % 使用您提供的函数添加不同信噪比的噪声 snr_levels = [ 0, -2, -5, -10, -20]; % 测试不同信噪比 results = cell(length(snr_levels), 2); for i = 1:length(snr_levels) fprintf('测试信噪比: %d dB\n', snr_levels(i)); % 添加噪声 noisy_signal = addDiffSnr4Student(clean_signal, snr_levels(i)); % 识别电话号码 phone_number = process_and_recognize(noisy_signal, Fs, tone_duration, pause_duration, ... low_freqs, high_freqs, dtmf_keys); results{i, 1} = snr_levels(i); results{i, 2} = phone_number; fprintf('识别结果: %s\n\n', phone_number); end % 显示最终结果 display_final_results(results, clean_signal);
end
function [signal, Fs] = generate_or_load_dtmf_signal()
% 如果要从文件读取,取消下面注释并修改文件名
filename = ‘20231071364_实验二案例2_无噪DTMF.wav’;
if exist(filename, ‘file’)
[signal, Fs] = audioread(filename);
if size(signal, 2) > 1
signal = mean(signal, 2); % 转为单声道
end
end
end
function tone = generate_dtmf_tone(digit, duration, Fs)
% DTMF频率映射
freq_map = containers.Map();
freq_map(‘1’) = [697, 1209]; freq_map(‘2’) = [697, 1336];
freq_map(‘3’) = [697, 1477]; freq_map(‘4’) = [770, 1209];
freq_map(‘5’) = [770, 1336]; freq_map(‘6’) = [770, 1477];
freq_map(‘7’) = [852, 1209]; freq_map(‘8’) = [852, 1336];
freq_map(‘9’) = [852, 1477]; freq_map(‘0’) = [941, 1336];
freq_map(‘*’) = [941, 1209]; freq_map(‘#’) = [941, 1477];
freq_map(‘A’) = [697, 1633]; freq_map(‘B’) = [770, 1633];
freq_map(‘C’) = [852, 1633]; freq_map(‘D’) = [941, 1633];
if isKey(freq_map, digit) freqs = freq_map(digit); t = 0:1/Fs:duration-1/Fs; tone = 0.5 * sin(2*pi*freqs(1)*t) + 0.5 * sin(2*pi*freqs(2)*t); tone = tone(:); else tone = zeros(round(duration*Fs), 1); end
end
function DTMF = addDiffSnr4Student(dtmfData, snrDB)
% 使用您提供的加噪函数
% 为 dtmfData 信号加上指定信噪比的高斯白噪声
DTMF = awgn(dtmfData, snrDB, ‘measured’);
figure; plot(DTMF); hold on; plot(dtmfData, 'r'); legend('加噪信号', '原始信号'); title(['信噪比 = ', num2str(snrDB), ' dB']); xlabel('采样点'); ylabel('幅度');
end
function phone_number = process_and_recognize(signal, Fs, tone_duration, pause_duration, …
low_freqs, high_freqs, dtmf_keys)
% 完整的处理和识别流程
% 1. 预处理信号 processed_signal = advanced_preprocessing(signal, Fs); % 2. 检测和分割音调 tones = improved_tone_detection(processed_signal, Fs, tone_duration, pause_duration); % 3. 识别每个音调 phone_number = robust_frequency_recognition(tones, Fs, low_freqs, high_freqs, dtmf_keys);
end
function processed_signal = advanced_preprocessing(signal, Fs)
% 改进的信号预处理
% 1. 带通滤波,保留DTMF频率范围 (600-1700 Hz) f_low = 600; f_high = 1700; % 使用切比雪夫滤波器,更好的阻带衰减 [b, a] = cheby2(6, 40, [f_low/(Fs/2), f_high/(Fs/2)], 'bandpass'); filtered_signal = filter(b, a, signal); % 2. 使用中值滤波去除脉冲噪声 window_size = 5; if length(filtered_signal) > window_size filtered_signal = medfilt1(filtered_signal, window_size); end % 3. 能量归一化 processed_signal = filtered_signal / max(abs(filtered_signal));
end
function tones = improved_tone_detection(signal, Fs, tone_duration, pause_duration)
% 改进的音调检测算法
tone_length = round(tone_duration * Fs); pause_length = round(pause_duration * Fs); % 使用短时能量和谱熵进行端点检测 frame_size = 256; hop_size = 128; energy = []; spectral_entropy = []; for i = 1:hop_size:length(signal)-frame_size frame = signal(i:i+frame_size-1); % 计算帧能量 frame_energy = sum(frame.^2); energy = [energy; frame_energy]; % 计算谱熵 frame_fft = abs(fft(frame .* hamming(frame_size))); frame_fft = frame_fft(1:frame_size/2+1); frame_fft = frame_fft / sum(frame_fft); frame_fft(frame_fft == 0) = eps; % 避免log(0) entropy = -sum(frame_fft .* log(frame_fft)); spectral_entropy = [spectral_entropy; entropy]; end % 归一化能量和熵 energy_norm = energy / max(energy); entropy_norm = spectral_entropy / max(spectral_entropy); % 结合能量和熵的检测指标 detection_metric = energy_norm .* (1 - entropy_norm); % 自适应阈值 threshold = 0.1 * max(detection_metric) + 0.9 * mean(detection_metric); % 检测音调起始点 tone_starts = []; in_tone = false; min_tone_length = round(0.5 * tone_length / hop_size); for i = 1:length(detection_metric) if ~in_tone && detection_metric(i) > threshold tone_starts = [tone_starts; i]; in_tone = true; tone_counter = 1; elseif in_tone if detection_metric(i) <= threshold tone_counter = tone_counter + 1; if tone_counter > min_tone_length in_tone = false; end else tone_counter = 0; end end end % 提取音调片段 tones = {}; for i = 1:length(tone_starts) start_idx = (tone_starts(i) - 1) * hop_size + 1; end_idx = min(start_idx + tone_length - 1, length(signal)); if end_idx - start_idx + 1 >= 0.8 * tone_length tone_segment = signal(start_idx:end_idx); tones{end+1} = tone_segment; end end
end
function phone_number = robust_frequency_recognition(tones, Fs, low_freqs, high_freqs, dtmf_keys)
% 改进的频率识别算法
phone_number = ''; freq_tolerance = 20; % 频率容差(Hz) for i = 1:length(tones) tone = tones{i}; % 使用改进的Goertzel算法 [detected_low, detected_high, confidence] = improved_goertzel_detection(... tone, Fs, low_freqs, high_freqs); % 基于置信度的决策 if confidence > 0.7 digit = map_frequencies_to_digit(detected_low, detected_high, ... low_freqs, high_freqs, dtmf_keys, freq_tolerance); phone_number = [phone_number, digit]; else phone_number = [phone_number, '?']; % 无法识别的音调 end end
end
function [low_freq, high_freq, confidence] = improved_goertzel_detection(signal, Fs, low_freqs, high_freqs)
% 改进的Goertzel频率检测
N = length(signal); % 检测低频组 low_results = zeros(length(low_freqs), 2); for i = 1:length(low_freqs) k = round(low_freqs(i) * N / Fs); magnitude = goertzel(signal, k); low_results(i, :) = [low_freqs(i), magnitude]; end % 检测高频组 high_results = zeros(length(high_freqs), 2); for i = 1:length(high_freqs) k = round(high_freqs(i) * N / Fs); magnitude = goertzel(signal, k); high_results(i, :) = [high_freqs(i), magnitude]; end % 寻找主要频率成分 [low_freq, low_conf] = find_dominant_frequency(low_results); [high_freq, high_conf] = find_dominant_frequency(high_results); % 综合置信度 confidence = (low_conf + high_conf) / 2;
end
function [dominant_freq, confidence] = find_dominant_frequency(freq_results)
% 寻找主导频率并计算置信度
magnitudes = freq_results(:, 2); max_mag = max(magnitudes); mean_mag = mean(magnitudes); % 找到最大幅度对应的频率 [~, idx] = max(magnitudes); dominant_freq = freq_results(idx, 1); % 计算置信度:基于信噪比 if max_mag > 0 snr_ratio = (max_mag - mean_mag) / max_mag; confidence = max(0, min(1, snr_ratio * 2)); else confidence = 0; end
end
function magnitude = goertzel(signal, k)
% Goertzel算法实现
N = length(signal);
w = 2 * pi * k / N;
cosine = cos(w);
coeff = 2 * cosine;
s_prev = 0; s_prev2 = 0; for i = 1:N s = signal(i) + coeff * s_prev - s_prev2; s_prev2 = s_prev; s_prev = s; end real_part = s_prev - s_prev2 * cosine; imag_part = s_prev2 * sin(w); magnitude = sqrt(real_part^2 + imag_part^2) / N;
end
function digit = map_frequencies_to_digit(low_freq, high_freq, low_freqs, high_freqs, dtmf_keys, tolerance)
% 将频率映射到数字,考虑容差
% 找到最接近的低频 [~, low_idx] = min(abs(low_freqs - low_freq)); closest_low = low_freqs(low_idx); % 找到最接近的高频 [~, high_idx] = min(abs(high_freqs - high_freq)); closest_high = high_freqs(high_idx); % 检查频率是否在容差范围内 if abs(closest_low - low_freq) <= tolerance && abs(closest_high - high_freq) <= tolerance digit = dtmf_keys(low_idx, high_idx); else digit = '?'; end
end
function display_final_results(results, clean_signal)
% 显示最终结果
figure('Position', [100, 100, 1000, 600]); % 显示原始信号 subplot(2,1,1); t = (0:length(clean_signal)-1) / 44100; plot(t, clean_signal); title('原始DTMF信号'); xlabel('时间 (s)'); ylabel('幅度'); grid on; % 显示识别结果表格 subplot(2,1,2); axis off; % 创建结果表格 result_text = sprintf('=== DTMF电话号码识别结果 ===\n\n'); result_text = [result_text, sprintf('%-8s %-15s\n', '信噪比(dB)', '识别结果')]; result_text = [result_text, sprintf('%-8s %-15s\n', '---------', '----------')]; for i = 1:size(results, 1) result_text = [result_text, sprintf('%-8d %-15s\n', results{i,1}, results{i,2})]; end text(0.1, 0.9, result_text, 'FontName', 'Courier New', 'FontSize', 12, ... 'VerticalAlignment', 'top'); fprintf('\n%s', result_text);
end
% 运行改进的主函数
dtmf_phone_recognition_improved();在上面的代码的基础上提供抗信噪比,使在最小-30dB下,仍能提取出原号码13108363851