Around 1960, before ZIP codes, before shopping malls, Grandmother shared a telephone connection with several other people on a party line.The Telephone Company supplied a distinctive ring for each customer, and please don't listen in, it isn't polite. This was in the capital city of North Carolina, 90,000 people. Within a few years she was the last one on the party line, but The Phone Company was required to continue offering the party line rate to existing customers. A few years later, The Telephone Company started offering Touch-Tone dialing to replace the round rotary dial (tick-tick-tick-tick-tick....tick-tick-tick...), but for years Dad kept the rotary phones so he wouldn't have to pay the $3.00 surcharge for faster dialing. Numbers with lots of zeros were particularly slow to dial. Touch-Tones are fast.
Touch-Tones are DTMF (Dual Tone Multi-Frequency) signals made from two simultaneous sine-waves in the human speech range so telephone systems can easily transmit them. There are eight tones available, arranged in a 4 x 4 grid. You've probably never seen the fourth column.
|
1209 Hz |
1336 Hz |
1477 Hz |
1633 Hz |
697 Hz |
1 |
2 |
3 |
A |
770 Hz |
4 |
5 |
6 |
B |
852 Hz |
7 |
8 |
9 |
C |
941 Hz |
* |
0 |
# |
D |
(Hz is the unit that replaced CPS in 1960.)
At the end of this post there is a DTMF3.wav file attached; it was made with a computer microphone pressed against a telephone handset and is a bit quiet. Download it for the script that follows; turn up your speakers a bit to hear it.
The script recovers the key presses from the wav file by loading a matrix of samples in the time domain from the file, then running JMP's FFT function on short sections of the time domain data to convert it to frequency domain. In this graph each vertical column of dots represents what frequencies were especially loud during the time slot. The extra dots at the bottom left are probably from tapping the microphone on the earpiece; the sharp clicks have lots of frequencies.
The first digit has a lot of 1477 Hz energy and a lot of 852 Hz energy. It is a 9. If you want to check your work on the rest of the digits, compare your answer to the JMP US technical support number.
Update 20Dec2016: the JSL is restored after not surviving the conversion from the old blog format. Try it again if you got an empty table and no graph in the last few months. Also, this is not a great model for reading WAV files; the example in Working With WAV Files is much faster and more likely to do what you need in the future.
x = Load Text File( "$DESKTOP/DTMF3.wav", blob ); // this file must be 22050 16 bit 1 channel
// because the following code makes zero effort to handle anything else
x = Hex( x ); // doubles the size of the data, but the hex characters are easy to parse
If( Pat Match( x, Hex( "RIFF" ) + Pat Arb() + Hex( "data" ), "" ), // found the data chunk
grab = 2048; // samples per timeslice
period = grab / 22050; // seconds
binwidth = 1 / period; // 1 cycle in the bin's width is the lowest frequency the bins resolve
timeslice = 0; // count through the time slices
dt = New Table("sound",New Column("time"),New Column("frequency"),New Column("amp"));
dt << begin data update; // load the table without updating each time
While( Length( x ) > (grab) * 2 * 2, // grab samples * 2 bytesPerSample * 2 hexCharsPerByte
samples = J( grab, 1, 0 ); // an array to hold the samples; will be sent to the fft below
nsamples = 0; // grab counter
timeslice++; // each time we grab samples is a new time slice
Pat Match( x, // scan the hex data for pairs of hex digits
Pat Repeat(
Pat Len( 2 ) >> left1 + Pat Len( 2 ) >> left2 + // two pairs
// the following Expr evaluates to PatFence(), eventually, so it can be
// concatenated onto the preceding match. But before it does that, it
// stores the preceding match into the samples[ ] matrix
Expr( nsamples++; // index into the sample array
q = Hex To Number( left2 || left1 ); // build the 16 bit integer
If( q > 32767, q = q - 65536 ); // make it signed, +/-32K
samples[ Nsamples ] = q; // keep it
Pat Fence(); ), // make matching faster by fencing off completed work
grab, grab // min,max number of repeats
), "" // replace everything matched for this grab with nothing
);
freq = FFT( {samples} ); // run the fft. freq is a list: { real, imag }
amplitude = Sqrt( freq[1] :* freq[1] + freq[2] :* freq[2] ); // just the amplitude
For( f = 1, f < grab / 2, f++, // write data table
// throw out small amplitude responses and out-of-interest frequencies
// a different file might need some tweaking of these values
If( amplitude[F] > 10000 & f > 50 & f < 150, // roughly 500 to 1500 Hz
dt << addrows( 1 );
dt:time = timeslice * period;
dt:frequency = (f - 1) * binwidth; // bin 1 is really frequency 0, DC
dt:amp = amplitude[F];
)
);
);
dt << end data update;
// the pretty picture
dt << Graph Builder( Size( 664, 470 ), Show Control Panel( 0 ),
Variables( X( :time ), Y( :frequency ) ), Elements( Points( X, Y, Legend( 5 ) ) ),
SendToReport(
Dispatch( {}, "time", ScaleBox, {Min( 0.5 ), Max( 9 ), Inc( 1 ),
Minor Ticks( 1 ), Label Row Nesting( 1 )} ),
Dispatch( {}, "frequency", ScaleBox,
{Min( 250 ), Max( 1750 ), Inc( 2000 ), Minor Ticks( 0 ), Label Row Nesting( 1 ),
Add Ref Line( 697, "Solid", "Black", "697Hz", 1 ),
Add Ref Line( 770, "Solid", "Black", "770Hz", 1 ),
Add Ref Line( 852, "Solid", "Black", "852Hz", 1 ),
Add Ref Line( 941, "Solid", "Black", "941Hz", 1 ),
Add Ref Line( 1209, "Solid", "Black", "1209Hz", 1 ),
Add Ref Line( 1336, "Solid", "Black", "1336Hz", 1 ),
Add Ref Line( 1477, "Solid", "Black", "1477Hz", 1 ),
Add Ref Line( 1633, "Solid", "Black", "1633Hz", 1 )}
),
Dispatch( {}, "graph title", TextEditBox, {Set Text( "DTMF Analyzer" )} )
)
);
, // else...
Print( "not a wav file" )
);
In the early 1980's, The Phone Company split up into a bunch of smaller companies and it became legal to own your own telephone.
Notice the odd relationship between Grandmother's party line distinctive ring, which told who was being called, and today's ring tones, which can tell who is calling.
I've still got a land line.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.