This commit is contained in:
Arti Zirk 2024-03-06 23:16:49 +02:00
commit 56f96e73e4
4 changed files with 243 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
.idea
*build*

7
CMakeLists.txt Normal file
View File

@ -0,0 +1,7 @@
cmake_minimum_required(VERSION 3.27)
project(dtmf)
set(CMAKE_CXX_STANDARD 23)
add_compile_options(-Wall -Wextra -pedantic-errors -Werror -Weffc++ -Wsign-conversion)
add_executable(dtmf main.cpp)

19
README.md Normal file
View File

@ -0,0 +1,19 @@
# Simple DTMF Decoder in C
Simple stupid code implementing [Goertzel algorithm]
[Goertzel algorithm]: https://en.wikipedia.org/wiki/Goertzel_algorithm
# Build
Under Linux
cmake -S . -B build -G Ninja
ninja -C build
arecord | ./build/dtmf
# Test
You can use your phone dial pad or online signal generator https://onlinetonegenerator.com/dtmf.html

215
main.cpp Normal file
View File

@ -0,0 +1,215 @@
/* Goertzel Algorithm
* http://cms.edn.com/uploads/SourceCode/09banks.txt
* https://github.com/pramasoul/jrand/blob/master/goertzel.c
*
* reads 8khz 8bit samples from stdin, for example using `arecord | dtmf`
*/
#define PI 3.141592653589793
#include <cstdio>
#include <cmath>
#include <cstdint>
#define FLOATING double
#define SAMPLE uint8_t
#define SAMPLING_RATE 8000.0 //8kHz
#define N 205 //Block size
struct gzbin {
FLOATING coeff;
FLOATING Q1;
FLOATING Q2;
FLOATING sine;
FLOATING cosine;
};
FLOATING dtmf_freq[8] = {
697.0, 770.0, 852.0, 941.0,
1209.0, 1336.0, 1477.0, 1633.0};
struct gzbin gzbins[8] = {};
SAMPLE testData[N];
/* Call this routine before every "block" (size=N) of samples. */
void ResetGoertzel(struct gzbin *bin)
{
bin->Q2 = 0;
bin->Q1 = 0;
}
/* Call this once, to precompute the constants. */
void InitGoertzel(struct gzbin *bin, FLOATING target_frequency)
{
int k;
FLOATING floatN;
FLOATING omega;
floatN = (FLOATING) N;
k = floor(0.5 + ((floatN * target_frequency) / SAMPLING_RATE));
omega = (2.0 * PI * k) / floatN;
bin->sine = sin(omega);
bin->cosine = cos(omega);
bin->coeff = 2.0 * bin->cosine;
printf("For SAMPLING_RATE = %f", SAMPLING_RATE);
printf(" N = %d", N);
printf(" and FREQUENCY = %f,\n", target_frequency);
printf("k = %d and coeff = %f\n\n", k, bin->coeff);
ResetGoertzel(bin);
}
/* Call this routine for every sample. */
void ProcessSample(struct gzbin *bin, SAMPLE sample)
{
FLOATING Q0;
Q0 = bin->coeff * bin->Q1 - bin->Q2 + (FLOATING) sample;
bin->Q2 = bin->Q1;
bin->Q1 = Q0;
}
/* Optimized Goertzel */
/* Call this after every block to get the RELATIVE magnitude squared. */
FLOATING GetMagnitudeSquared(struct gzbin *bin)
{
FLOATING result;
result = bin->Q1 * bin->Q1 + bin->Q2 * bin->Q2 - bin->Q1 * bin->Q2 * bin->coeff;
return result;
}
char dtmf_decode(int x, int y) {
switch (y) {
case 0:
switch (x) {
case 0:
return '1';
case 1:
return '2';
case 2:
return '3';
case 3:
return 'A';
default:
return ' ';
};
case 1:
switch (x) {
case 0:
return '4';
case 1:
return '5';
case 2:
return '6';
case 3:
return 'B';
default:
return ' ';
};
case 2:
switch (x) {
case 0:
return '7';
case 1:
return '8';
case 2:
return '9';
case 3:
return 'C';
default:
return ' ';
};
case 3:
switch (x) {
case 0:
return '*';
case 1:
return '0';
case 2:
return '#';
case 3:
return 'D';
default:
return ' ';
};
default:
return ' ';
}
}
int main()
{
FLOATING frequency_magnitudes[8] = {};
for (int i = 0; i <8; ++i) {
InitGoertzel(&gzbins[i], dtmf_freq[i]);
}
while (true) {
size_t bytes_read = fread(testData, sizeof(SAMPLE), N, stdin);
if (bytes_read != N) {
fprintf(stderr, "Failed to read %d from stdin, got: %zu\n", N, bytes_read);
exit(1);
}
printf("\n\n");
for (int i = 0; i < 8; ++i) {
struct gzbin *bin = &gzbins[i];
for (unsigned char sample: testData) {
ProcessSample(bin, sample);
}
FLOATING magnitudeSquared = GetMagnitudeSquared(bin);
frequency_magnitudes[i] = magnitudeSquared;
printf("Freq: %10.1f; Mag: %20f\n", dtmf_freq[i], magnitudeSquared);
ResetGoertzel(bin);
}
FLOATING max = 0.0;
FLOATING min = INFINITY;
int max_y = 0;
for (int y = 0; y < 4; ++y) {
FLOATING magnitude = frequency_magnitudes[y];
if (magnitude > max) {
max = magnitude;
max_y = y;
}
if (magnitude < min)
min = magnitude;
}
if ((max - min) < 1000) {
printf("\n");
continue;
}
max = 0.0;
min = INFINITY;
int max_x = 0;
for (int x = 4; x < 8; ++x) {
FLOATING magnitude = frequency_magnitudes[x];
if (magnitude > max) {
max = magnitude;
max_x = x-4;
}
if (magnitude < min)
min = magnitude;
}
if ((max - min) < 10000) {
printf("\n");
continue;
}
printf("x: %d, y: %d, char: %c\n", max_x, max_y, dtmf_decode(max_x, max_y));
}
return 0;
}