180 lines
5.0 KiB
C
180 lines
5.0 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, true);
|
|
if (rc > 0)
|
|
printf("tone detected\n");
|
|
else
|
|
printf("no tone detected\n");
|
|
answertone_exit(at);
|
|
_assert(rc > 0);
|
|
|
|
return 0;
|
|
}
|
|
|