Skip to content

Instantly share code, notes, and snippets.

@johnhowe
Created April 30, 2010 13:33
Show Gist options
  • Save johnhowe/385202 to your computer and use it in GitHub Desktop.
Save johnhowe/385202 to your computer and use it in GitHub Desktop.
%%%
% Dual Tone Multi Frequency Decoder
% ENEL440 Assignment 1, 2010
% John Howe
%%%
clear; clc;
% Array of DTMF frequencies f1 - f8 numbered as so:
% f5 f6 f7 f8
keypad = [ '1','2','3','A'; % f1
'4','5','6','B'; % f2
'7','8','9','C'; % f3
'*','0','#','D'];% f4
DTMF = [697, 770, 852, 941, 1209, 1336, 1477, 1633];
numRowFreqs = 4;
numColFreqs = 4;
% Desired parameters for each FIR filter.
passBandwidth = 10; % Hz
transitionBandwidth = 100; % Hz
stopbandAttenuation = 18; % dB
% Parameters used for DTMF detection.
sensitivity = 0.5; % Dominance one frequency must have to be considered a tone
subSampleLength = 20; % Size of sample to be analysed at once (in ms)
sampleFreq = 3700; % Sample rate for input data in Hz
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%% Configuration ends here. %%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
ls *txt
s = input('Enter test signal filename (inside single quotes),\notherwise default group20.txt will be used: ');
try
importdata (s);
catch
fprintf ('Using group20.txt\n');
importdata 'group20.txt';
end
samples = ans - mean (ans);
% Approximation for the number of taps to achieve the desired filter parameters
numTaps = round ((sampleFreq * stopbandAttenuation) / (22 * transitionBandwidth));
nyquistFreq = sampleFreq /2;
fprintf ('Generating FIR filters using %d taps with a %dHz transition\n bandwidth and a %ddB stopband attenuation.\n', numTaps, transitionBandwidth, stopbandAttenuation);
% Calculate the FIR filter coefficients for each DTMF frequency using the
% Parks-McClellan optimal equiripple FIR filter design.
for i = 1 : size (DTMF, 2)
% F is a vector of frequency band edges in pairs, in ascending order
% between 0 and 1. 1 corresponds to the Nyquist frequency or half the
% sampling frequency. At least one frequency band must have a non-zero
% width.
Fst0 = 0;% Filter start
Fst1 = (DTMF(i) - transitionBandwidth) / nyquistFreq;% First Stopband Frequency
Fp1 = (DTMF(i) - passBandwidth/2) / nyquistFreq;% First Passband Frequency
Fp2 = (DTMF(i) + passBandwidth/2) / nyquistFreq;% Second Passband Frequency
Fst2 = (DTMF(i) + transitionBandwidth) / nyquistFreq;% Second Stopband Frequency
Fst3 = 1;% Filter end
F = [Fst0, Fst1, Fp1, Fp2, Fst2, Fst3];
% A is a real vector the same size as F which specifies the desired
% amplitude of the frequency response of the resultant filter B.
A = [0, 0, 1, 1, 0, 0]; % Here we are using a bandpass filter
% B=FIRPM(N,F,A) returns a length N+1 linear phase (real, symmetric
% coefficients) FIR filter which has the best approximation to the desired
% frequency response described by F and A in the minimax sense.
FIR (i, :) = firpm (numTaps-1, F, A);
end
fprintf ('Analysing data using a %dms chunk size.\n', subSampleLength);
% The sampled signal is split into sub-samples the same width as the FIR
% filter. Each of these is analysed to find which key (if any) has been
% pressed.
subSampleStart = 1;
totalSamples = size (samples,2);
hits = 0;
subSampleCount = round (subSampleLength * sampleFreq / 1000);
while subSampleStart < (totalSamples - subSampleCount)
colFreq = 0;
rowFreq = 0;
subSample = samples (subSampleStart:subSampleStart + subSampleCount);
% Each sub-sample is passed through each of the filters.
for i = 1 : numRowFreqs
rowSubSample (i, :) = filter (FIR (i,:), 1, subSample);
rowEnergy (i) = round (sum (rowSubSample (i, :).^2));
end
for i = 1 : numColFreqs
colSubSample (i, :) = filter (FIR (i + numRowFreqs,:), 1, subSample);
colEnergy (i) = round (sum (colSubSample (i, :).^2));
end
% To test if a tone is present in just one of the frequencies in a bank it
% must contain more energy than in all the bank's other frequencies combined.
totalColEnergy = sum (colEnergy);
totalRowEnergy = sum (rowEnergy);
for i = 1 : numRowFreqs
if (colEnergy (i) / totalColEnergy) > sensitivity
rowFreq = i;
end
end
for i = 1 : numColFreqs
if (rowEnergy (i) / totalRowEnergy) > sensitivity
colFreq = i;
end
end
% For a button to be pressed both a row and column must be active
if colFreq & rowFreq
thisKey = keypad (rowFreq, colFreq);
% This prevents multiple detections of the same key press
if hits == 0
hits = hits + 1;
sequence (hits) = thisKey;
elseif (thisKey ~= sequence (hits)) && (lastKeyPress)
hits = hits + 1;
sequence (hits) = thisKey;
end
lastKeyPress = 1;
else
lastKeyPress = 0;
end
% increment to next block of samples
subSampleStart = subSampleStart + subSampleCount;
end
% Display the DTMF sequence (if one was detected)
if hits
fprintf ('Received the following sequence of keys: %s\n', sequence);
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment