diff options
Diffstat (limited to 'fxotune.c')
-rw-r--r-- | fxotune.c | 155 |
1 files changed, 125 insertions, 30 deletions
@@ -31,15 +31,17 @@ #include "wctdm.h" #include "fxotune.h" -#define TEST_DURATION 2000 /* 4000 samples (or 500 ms) of test */ -#define BUFFER_LENGTH (2 * TEST_DURATION) /* 4000 sample buffers */ -#define SKIP_SAMPLES 800 /* skip first 100 ms of test when computing echo powers - gives the system time to acquire data */ +#define TEST_DURATION 2000 +#define BUFFER_LENGTH (2 * TEST_DURATION) +#define SKIP_SAMPLES 800 -static const float loudness = 16384.0; +static const float amplitude = 16384.0; static char *zappath = "/dev/zap"; static char *configfile = "/etc/fxotune.conf"; +static int audio_dump_fd = -1; + static char *usage = "Usage: fxotune [-v[vv] (-s | -i <options> | -d <options>)\n" "\n" @@ -52,7 +54,9 @@ static char *usage = " options : [-b <device>][-w <waveform>]\n" " [-n <dialstring>][-l <delaytosilence>][-m <silencegoodfor>]\n" " -v : more output (-vv, -vvv also)\n" -" -c <config_file>\n" +" -o <path> : Write the received raw 16-bit signed linear audio that is\n" +" used in processing to the file specified by <path>\n" +" -c <config_file>\n" "\n" " <calibtype> - type of calibration\n" " (default 2, old method 1)\n" @@ -68,8 +72,8 @@ static char *usage = " (default 1)\n" " <waveform> - -1 for multitone waveform, or frequency of\n" " single tone (default -1)\n" -" <config_file> - Alternative file to set from / calibrate to.\n" -" (Default: /etc/fxotune.conf)\n" +" <config_file> - Alternative file to set from / calibrate to.\n" +" (Default: /etc/fxotune.conf)\n" ; @@ -92,6 +96,19 @@ static int debug = 0; static FILE *debugoutfile = NULL; +static int fxotune_read(int fd, void *buffer, int len) +{ + int res; + + res = read(fd, buffer, len); + + if ((res > 0) && (audio_dump_fd != -1)) { + write(audio_dump_fd, buffer, len); + } + + return res; +} + /** * Makes sure that the line is clear. * Right now, we do this by relying on the user to specify how long after dialing the @@ -178,9 +195,14 @@ static int ensure_silence(struct silence_info *info) */ static short inline gentone(int hz, int index) { - return loudness * sin((index * 2.0 * M_PI * hz)/8000); + return amplitude * sin((index * 2.0 * M_PI * hz)/8000); } +/* Using DTMF tones for now since they provide good mid band testing + * while not being harmonics of each other */ +static int freqs[] = {697, 770, 941, 1209, 1336, 1633}; +static int freqcount = 6; + /** * Generates a waveform of several frequencies. * @@ -192,8 +214,6 @@ static short inline gentone(int hz, int index) */ static short inline genwaveform(int index) { - int freqs[] = {300, 600, 900, 1004, 1300, 1600}; /* chose 1004 Hz to include the milliwatt test tone frequency - but there was no particular reson to do so */ - int freqcount = 6; int i = 0; float response = (float)0; for (i = 0; i < freqcount; i++){ @@ -201,7 +221,7 @@ static short inline genwaveform(int index) } - return loudness * response / freqcount; + return amplitude * response / freqcount; } @@ -257,6 +277,61 @@ static float power_of(void *prebuf, int bufsize, int short_format) return sqrtf(finalanswer); } +/* + * In an effort to eliminate as much as possible the effect of outside noise, we use principles + * from the Fourier Transform to attempt to calculate the return loss of our signal for each setting. + * + * To begin, we send our output signal out on the line. We then receive back the reflected + * response. In the Fourier Transform, each evenly distributed frequency within the window + * is correlated (multiplied against, then the resulting samples are added together) with + * the real (cos) and imaginary (sin) portions of that frequency base to detect that frequency. + * + * Instead of doing a complete Fourier Transform, we solve the transform for only our signal + * by multiplying the received signal by the real and imaginary portions of our reference + * signal. This then gives us the real and imaginary values that we can use to calculate + * the return loss of the sinusoids that we sent out on the line. This is done by finding + * the magnitude (think polar form) of the vector resulting from the real and imaginary + * portions calculated above. + * + * This essentially filters out any other noise which maybe present on the line which is outside + * the frequencies used in our test multi-tone. + */ +static float db_loss(float measured, float reference) +{ + return 20 * (logf(measured/reference)/logf(10)); +} + +static void one_point_dft(const short *inbuf, int len, int frequency, float *real, float *imaginary) +{ + float myreal = 0, myimag = 0; + int i; + + for (i = 0; i < len; i++) { + myreal += (float) inbuf[i] * cos((i * 2.0 * M_PI * frequency)/8000); + myimag += (float) inbuf[i] * sin((i * 2.0 * M_PI * frequency)/8000); + } + + myimag *= -1; + + *real = myreal / (float) len; + *imaginary = myimag / (float) len; +} + +static float calc_magnitude(short *inbuf, int insamps) +{ + float real, imaginary, magnitude; + float totalmagnitude = 0; + int i; + + for (i = 0; i < freqcount; i++) { + one_point_dft(inbuf, insamps, freqs[i], &real, &imaginary); + magnitude = sqrtf((real * real) + (imaginary * imaginary)); + totalmagnitude += magnitude; + } + + return totalmagnitude; +} + /** * dumps input and output buffer contents for the echo test - used to see exactly what's going on */ @@ -334,7 +409,7 @@ static int maptone(int whichzap, int freq, char *dialstr, int delayuntilsilence) retry: /* read return response */ - res = read(whichzap, inbuf, BUFFER_LENGTH); + res = fxotune_read(whichzap, inbuf, BUFFER_LENGTH); if (res != BUFFER_LENGTH) { int x; @@ -382,7 +457,7 @@ retry: * impedence/coefficients configurations. * * Note: It may be possible to take this even further and do some pertubation analysis on the echo coefficients - * themselves (maybe use the 64 entry sweep to find some settings that are close to working well, then + * themselves (maybe use the 72 entry sweep to find some settings that are close to working well, then * deviate the coefficients a bit to see if we can improve things). A better way to do this would be to * use the optimization strategy from silabs. For reference, here is an application note that describes * the echo coefficients (and acim values): @@ -399,7 +474,7 @@ static int acim_tune2(int whichzap, int freq, char *dialstr, int delayuntilsilen float lowestecho = 999999999999.0;; struct zt_bufferinfo bi; - short inbuf[TEST_DURATION]; + short inbuf[TEST_DURATION * 2]; if (debug && !debugoutfile) { if (!(debugoutfile = fopen("fxotune.vals", "w"))) { @@ -453,12 +528,12 @@ static int acim_tune2(int whichzap, int freq, char *dialstr, int delayuntilsilen outbuf[i] = freq > 0 ? gentone(freq, i) : genwaveform(i); /* if freq is negative, use a multi-frequency waveform */ /* compute power of input (so we can later compute echo levels relative to input) */ - float waveform_power = power_of(outbuf+SKIP_SAMPLES, TEST_DURATION-SKIP_SAMPLES, 1); + float waveform_power = calc_magnitude(outbuf, TEST_DURATION); /* sweep through the various coefficient settings and see how our responses look */ - int echo_trys_size = 64; + int echo_trys_size = 72; int trys = 0; for (trys = 0; trys < echo_trys_size; trys++){ @@ -489,19 +564,26 @@ static int acim_tune2(int whichzap, int freq, char *dialstr, int delayuntilsilen retry: /* read return response */ - res = read(whichzap, inbuf, BUFFER_LENGTH); - if (res != BUFFER_LENGTH) { + res = fxotune_read(whichzap, inbuf, BUFFER_LENGTH * 2); + if (res != BUFFER_LENGTH * 2) { int x; ioctl(whichzap, ZT_GETEVENT, &x); goto retry; } - /* calculate RMS of response */ - - float freq_result = power_of(inbuf+SKIP_SAMPLES, TEST_DURATION-SKIP_SAMPLES, 1); - float echo = freq_result/waveform_power; + float freq_result = calc_magnitude(inbuf, TEST_DURATION * 2); + float echo = db_loss(freq_result, waveform_power); +#if 0 + if (debug > 0) + fprintf(stdout, "%3d,%d,%d,%d,%d,%d,%d,%d,%d: magnitude = %0.0f, echo = %0.4f dB\n", + echo_trys[trys].acim, echo_trys[trys].coef1, echo_trys[trys].coef2, + echo_trys[trys].coef3, echo_trys[trys].coef4, echo_trys[trys].coef5, + echo_trys[trys].coef6, echo_trys[trys].coef7, echo_trys[trys].coef8, + freq_result, echo); +#endif + if (freq_result < lowesttryresult){ lowesttry = trys; lowesttryresult = freq_result; @@ -509,7 +591,7 @@ retry: } if (debug) { char result[256]; - snprintf(result, sizeof(result), "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%f,%f", + snprintf(result, sizeof(result), "%3d,%3d,%3d,%3d,%3d,%3d,%3d,%3d,%3d,%f,%f", echo_trys[trys].acim, echo_trys[trys].coef1, echo_trys[trys].coef2, @@ -519,19 +601,21 @@ retry: echo_trys[trys].coef6, echo_trys[trys].coef7, echo_trys[trys].coef8, - freq, freq_result, echo ); fprintf(debugoutfile, "%s\n", result); - if (debug > 1) - fprintf(stdout, "%s\n", result); + fprintf(stdout, "%3d,%3d,%3d,%3d,%3d,%3d,%3d,%3d,%3d: magnitude = %0.0f, echo = %0.4f dB\n", + echo_trys[trys].acim, echo_trys[trys].coef1, echo_trys[trys].coef2, + echo_trys[trys].coef3, echo_trys[trys].coef4, echo_trys[trys].coef5, + echo_trys[trys].coef6, echo_trys[trys].coef7, echo_trys[trys].coef8, + freq_result, echo); } } if (debug > 0) - fprintf(stdout, "Config with lowest response = %d, power = %0.0f, echo = %0.4f\n", lowesttry, lowesttryresult, lowestecho); + fprintf(stdout, "Config with lowest response = %d, magnitude = %0.0f, echo = %0.4f dB\n", lowesttry, lowesttryresult, lowestecho); memcpy(coefs_out, &echo_trys[lowesttry], sizeof(struct wctdm_echo_coefs)); @@ -634,7 +718,7 @@ static int acim_tune(int whichzap, char *dialstr, int delayuntilsilence, int sil /* read return response */ retry: /* read return response */ - res = read(whichzap, inbuf, BUFFER_LENGTH); + res = fxotune_read(whichzap, inbuf, BUFFER_LENGTH); if (res != BUFFER_LENGTH) { int x; @@ -896,7 +980,7 @@ static int do_calibrate(int startdev, int enddev, int calibtype, char* configfil return problems; } -int main (int argc , char **argv) +int main(int argc , char **argv) { int startdev = 1; /* -b */ int stopdev = 252; /* -e */ @@ -965,6 +1049,18 @@ int main (int argc , char **argv) case 'v': debug = strlen(argv[i])-1; break; + case 'o': + if (moreargs) { + audio_dump_fd = open(argv[++i], O_WRONLY|O_CREAT|O_TRUNC, 0666); + if (audio_dump_fd == -1) { + fprintf(stdout, "Unable to open file %s: %s\n", argv[i], strerror(errno)); + return -1; + } + break; + } else { + fprintf(stdout, "No path supplied to -o option!\n"); + return -1; + } default: fprintf(stdout, "Unknown option : %s\n", argv[i]); /* Show usage */ @@ -1008,7 +1104,6 @@ int main (int argc , char **argv) return -1; } - fputs(usage, stdout); return -1; } |