From 56f96e73e4f148037f5252bb11b56b48bfe9ad68 Mon Sep 17 00:00:00 2001 From: Arti Zirk Date: Wed, 6 Mar 2024 23:16:49 +0200 Subject: [PATCH] init --- .gitignore | 2 + CMakeLists.txt | 7 ++ README.md | 19 +++++ main.cpp | 215 +++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 243 insertions(+) create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 README.md create mode 100644 main.cpp diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bddc66e --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.idea +*build* diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..66b0956 --- /dev/null +++ b/CMakeLists.txt @@ -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) diff --git a/README.md b/README.md new file mode 100644 index 0000000..9a04f5d --- /dev/null +++ b/README.md @@ -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 diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..ff34979 --- /dev/null +++ b/main.cpp @@ -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 +#include +#include + +#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; +}