osmo-v5/tests/answer_detect.c

180 lines
4.9 KiB
C

/* Testing the answer tone algorithm of libecho
*
* (C) 2023 by Andreas Eversberg <andreas@eversberg.eu>
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include <math.h>
#include "../src/libecho/answertone.h"
#define db2level(db) pow(10, (double)(db) / 20.0)
#define ANS_dB -13.0
#define ANS_DURATION 4
#define ANS_PHASE_REV 0.450
#define SAMPLERATE 8000
#define BANDWIDTH 100.0
float tone[SAMPLERATE * ANS_DURATION];
int16_t tone_int[SAMPLERATE * ANS_DURATION];
static void gen_ans(float frequency, float level)
{
double phase = 0.0, rev = 0.0;
int i;
for (i = 0; i < SAMPLERATE * ANS_DURATION; i++) {
/* given tone */
tone[i] = sin(2.0 * M_PI * frequency * (double)i / (double)SAMPLERATE + phase);
/* 15 Hz amplitude modulation */
tone[i] *= 1.0 + sin(2.0 * M_PI * 15.0 * (double)i / (double)SAMPLERATE) * 0.2;
/* given level */
tone[i] *= level;
tone_int[i] = tone[i] * 32767.0;
/* apply phase reversal */
rev += 1.0 / (double)SAMPLERATE;
if (rev >= ANS_PHASE_REV) {
rev -= ANS_PHASE_REV;
phase += M_PI;
}
}
}
static enum at_fail perform_test(struct answer_tone *at, float frequency, float level, float *phase1_p, float *phase2_p)
{
enum at_fail fail;
int offset = 0;
float _frequency;
gen_ans(frequency, level);
answertone_init(at, SAMPLERATE);
memcpy(at->buffer, tone + offset, sizeof(*at->buffer) * at->buffer_size);
offset += at->buffer_size;
answertone_check(at, phase1_p, &_frequency);
memcpy(at->buffer, tone + offset, sizeof(*at->buffer) * at->buffer_size);
offset += at->buffer_size;
fail = answertone_check(at, phase2_p, &_frequency);
switch (fail) {
case AT_FAIL_MIN_LEVEL:
printf("failed, level too low\n");
break;
case AT_FAIL_MAX_NOISE:
printf("failed, too noisy\n");
break;
case AT_FAIL_MAX_FREQUENCY:
printf("failed, frequency too much off\n");
break;
case AT_FAIL_NONE:
printf("ok\n");
}
return fail;
}
static void _assert(bool what)
{
if (what) {
printf("The result of this test was as expected.\n\n");
return;
}
printf("*\nThe result of this test was not as expected, please FIX!.\n*\n");
exit(-1);
}
int main(void)
{
struct answer_tone answer_tone, *at = &answer_tone;
int offset;
enum at_fail fail;
float phase1, phase2, _frequency;
int rc;
printf("Test chunk with perfect tone: ");
fail = perform_test(at, 2100, db2level(ANS_dB), &phase1, &phase2);
_assert(!fail);
printf("Check phase: %.3f == %.3f\n", phase1, phase2);
_assert(fabsf(phase1 - phase2) < 0.001);
printf("Test chunk with 2101 Hz tone: ");
fail = perform_test(at, 2101, db2level(ANS_dB), &phase1, &phase2);
_assert(!fail);
printf("Check phase: %.3f != %.3f\n", phase1, phase2);
_assert(fabsf(phase1 - phase2) > 0.001);
printf("Test chunk with 2121 Hz tone: ");
fail = perform_test(at, 2121, db2level(ANS_dB), &phase1, &phase2);
_assert(!fail);
printf("Test chunk with -26 dBm0 tone: ");
fail = perform_test(at, 2100, db2level(-26), &phase1, &phase2);
_assert(!fail);
printf("Test chunk with 2123 Hz tone: ");
fail = perform_test(at, 2123, db2level(ANS_dB), &phase1, &phase2);
_assert(fail);
printf("Test chunk with -28 dBm0 tone: ");
fail = perform_test(at, 2100, db2level(-28), &phase1, &phase2);
_assert(fail);
printf("Test chunk with long sequence of the tone: ");
gen_ans(2100+10, db2level(ANS_dB-8));
answertone_init(at, SAMPLERATE);
for (offset = 0; offset <= (double)SAMPLERATE * ANS_PHASE_REV - at->buffer_size; offset += at->buffer_size) {
memcpy(at->buffer, tone + offset, sizeof(*at->buffer) * at->buffer_size);
fail = answertone_check(at, &phase1, &_frequency);
switch (fail) {
case AT_FAIL_MIN_LEVEL:
printf("level too low\n");
_assert(false);
break;
case AT_FAIL_MAX_NOISE:
printf("too noisy\n");
_assert(false);
break;
case AT_FAIL_MAX_FREQUENCY:
if (offset == 0)
break;
printf("frequency too much off\n");
_assert(false);
break;
case AT_FAIL_NONE:
;
}
}
printf("ok\n");
_assert(true);
printf("Test complete process with long sequence of the tone: ");
gen_ans(2100+10, db2level(ANS_dB-8));
answertone_init(at, SAMPLERATE);
rc = answertone_process(at, tone_int, SAMPLERATE * ANS_DURATION);
if (rc > 0)
printf("tone detected\n");
else
printf("no tone detected\n");
answertone_exit(at);
_assert(rc > 0);
return 0;
}