Created
April 30, 2010 13:33
-
-
Save johnhowe/385202 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
%%% | |
% 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