init
This commit is contained in:
commit
56f96e73e4
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
.idea
|
||||||
|
*build*
|
7
CMakeLists.txt
Normal file
7
CMakeLists.txt
Normal 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
19
README.md
Normal 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
215
main.cpp
Normal 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;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user