/* * This file and contents thereof are licensed under the terms and * conditions of the GNU Public License version 2. For more information * (including terms and conditions) see http://www.gnu.org/ * * fxotune.c -- A utility for tuning the various settings on the fxo * modules for the TDM400 cards. * * by Matthew Fredrickson * * (C) 2004-2005 Digium, Inc. */ #include #include #include #include #include #include #include #include #include #include #ifdef STANDALONE_ZAPATA #include "zaptel.h" #else #include #endif #include "wctdm.h" #include "fxotune.h" #define TEST_DURATION 4000 /* 4000 samples (or 500 ms) of test */ #define BUFFER_LENGTH 8000 /* 4000 sample buffers */ #define SKIP_BYTES 1600 static const float loudness = 16384.0; static char *zappath = "/dev/zap"; static char *configfile = "/etc/fxotune.conf"; static char *usage = "Usage: fxotune\n" " -i : Tests for FXO modules and sets echo coefficients, dialing to clear dialtone\n" " -s : Sets the FXO modules echo coefficients on your system to the best settings\n"; #define OUT_OF_BOUNDS(x) ((x) < 0 || (x) > 255) static short outbuf[TEST_DURATION]; static int debug = 0; /* Generates a tone of hz frequency. Index is the current sample * to begenerated. For a normal waveform you need to increment * this every time you execute the function. * Returns a 16bit slinear sample. */ static short inline gentone(int hz, int index) { return loudness * sin((index * 2.0 * M_PI * hz)/8000); } /* Returns the power of the buffer of samples in 16bit slinear format. * power function = (sum of squares) - (square of sums). */ static float power_of(void *prebuf, int bufsize, int short_format) { float sum_of_squares = 0, square_of_sums = 0; int numsamples = 0; float finalanswer = 0; short *sbuf = (short*)prebuf; float *fbuf = (float*)prebuf; int i = 0; if (short_format) { /* idiot proof checks */ if (bufsize <= 0) return -1; if (bufsize % 2 != 0) return -2; numsamples = bufsize / 2; for (i = 0; i < numsamples; i++) { sum_of_squares += ((float)sbuf[i] * (float)sbuf[i])/(float)loudness; if (sbuf[i] > 0) { square_of_sums += (float)sbuf[i]; } else { sbuf[i] *= -1; square_of_sums += (float)sbuf[i]; } } } else { /* Version for float inputs */ for (i = 0; i < bufsize; i++) { sum_of_squares += (fbuf[i] * fbuf[i]); square_of_sums += fbuf[i]; } } if (debug) printf("sum_of_squares = %f\n", sum_of_squares); square_of_sums *= square_of_sums; /* sums ^ 2 */ if (debug) printf("square_of_sums = %f\n", square_of_sums); finalanswer = square_of_sums - sum_of_squares; if (debug) printf("finalanswer = %f\n", finalanswer); if (finalanswer < 0) { printf("Error: Final answer negative number %f\n", finalanswer); return -3; } #if 0 finalanswer = finalanswer * (float)-1; #endif return sqrtf(finalanswer); } /* Tune the line impedance. Look for best response range */ static int acim_tune(int whichzap, char *dialstr) { int i = 0, freq = 0, acim = 0; int res = 0, x = 0; struct zt_bufferinfo bi; struct zt_dialoperation dop; struct wctdm_echo_coefs coefs; short inbuf[BUFFER_LENGTH]; int lowest = 0; FILE *outfile = NULL; float acim_results[16]; if (debug) { outfile = fopen("fxotune.vals", "w"); if (!outfile) { fprintf(stdout, "Cannot create fxotune.vals\n"); return -1; } } /* Set echo settings */ memset(&coefs, 0, sizeof(coefs)); if (ioctl(whichzap, WCTDM_SET_ECHOTUNE, &coefs)) { fprintf(stdout, "Skipping non-TDM / non-FXO\n"); return -1; } x = 1; if (ioctl(whichzap, ZT_SETLINEAR, &x)) { fprintf(stderr, "Unable to set channel to signed linear mode.\n"); return -1; } memset(&bi, 0, sizeof(bi)); if (ioctl(whichzap, ZT_GET_BUFINFO, &bi)) { fprintf(stderr, "Unable to get buffer information!\n"); return -1; } bi.numbufs = 2; bi.bufsize = BUFFER_LENGTH; bi.txbufpolicy = ZT_POLICY_IMMEDIATE; bi.rxbufpolicy = ZT_POLICY_IMMEDIATE; if (ioctl(whichzap, ZT_SET_BUFINFO, &bi)) { fprintf(stderr, "Unable to set buffer information!\n"); return -1; } x = ZT_OFFHOOK; if (ioctl(whichzap, ZT_HOOK, &x)) { fprintf(stderr, "Cannot bring fd %d off hook", whichzap); return -1; } for (acim = 0; acim < 16; acim++) { float freq_results[15]; int needtoreset = 0; coefs.acim = acim; if (ioctl(whichzap, WCTDM_SET_ECHOTUNE, &coefs)) { fprintf(stderr, "Unable to set impedance on fd %d\n", whichzap); return -1; } for (freq = 200; freq <=3000; freq+=200, needtoreset++) { /* Fill the output buffers */ for (i = 0; i < TEST_DURATION; i++) outbuf[i] = gentone(freq, i); /* Prepare line for data */ if (needtoreset > 8) { /* Do line hookstate reset */ x = ZT_ONHOOK; if (ioctl(whichzap, ZT_HOOK, &x)) { fprintf(stderr, "Unable to hang up fd %d\n", whichzap); return -1; } sleep(2); x = ZT_OFFHOOK; if (ioctl(whichzap, ZT_HOOK, &x)) { fprintf(stderr, "Cannot bring fd %d off hook", whichzap); return -1; } memset(&dop, 0, sizeof(dop)); dop.op = ZT_DIAL_OP_REPLACE; dop.dialstr[0] = 'T'; strncpy(dop.dialstr + 1, dialstr, sizeof(dop.dialstr) - 1); printf("."); if (ioctl(whichzap, ZT_DIAL, &dop)) { fprintf(stderr, "Unable to dial!\n"); return -1; } sleep(2); needtoreset = 0; } /* Flush buffers */ x = ZT_FLUSH_READ | ZT_FLUSH_WRITE | ZT_FLUSH_EVENT; if (ioctl(whichzap, ZT_FLUSH, &x)) { fprintf(stderr, "Unable to flush I/O: %s\n", strerror(errno)); return -1; } /* send data out on line */ res = write(whichzap, outbuf, BUFFER_LENGTH); if (res != BUFFER_LENGTH) { fprintf(stderr, "Could not write all data to line\n"); return -1; } /* read return response */ res = read(whichzap, inbuf, BUFFER_LENGTH); if (res != BUFFER_LENGTH) { fprintf(stderr, "Could not fill input buffer\n"); return -1; } /* calculate power of response */ freq_results[(freq/200)-1] = power_of(inbuf+SKIP_BYTES, BUFFER_LENGTH-SKIP_BYTES, 1); if (debug) fprintf(outfile, "%d,%d,%f\n", acim, freq, freq_results[(freq/200)-1]); } acim_results[acim] = power_of(freq_results, 15, 0); } if (debug) { for (i = 0; i < 16; i++) fprintf(outfile, "acim_results[%d] = %f\n", i, acim_results[i]); } /* Find out what the "best" impedance is for the line */ lowest = 0; for (i = 0; i < 16; i++) { if (acim_results[i] < acim_results[lowest]) { lowest = i; } } return lowest; } int main (int argc , char **argv) { char zapdev[80] = ""; int i = 0; int fd; int res = 0; int configfd; FILE *fp = NULL; if ((argc < 2) || (argc > 3)) { /* Show usage */ fputs(usage, stdout); return -1; } if (!strcasecmp(argv[1], "-s")) { set: fp = fopen(configfile, "r"); if (!fp) { fprintf(stdout, "Cannot open %s!\n",configfile); return -1; } for (i = 0;res != EOF; i++) { struct wctdm_echo_coefs mycoefs; char completezappath[56] = ""; int myzap,myacim,mycoef1,mycoef2,mycoef3,mycoef4,mycoef5,mycoef6,mycoef7,mycoef8; res = fscanf(fp, "%d=%d,%d,%d,%d,%d,%d,%d,%d,%d",&myzap,&myacim,&mycoef1, &mycoef2,&mycoef3,&mycoef4,&mycoef5,&mycoef6,&mycoef7, &mycoef8); if (res == EOF) { break; } /* Check to be sure conversion is done correctly */ if (OUT_OF_BOUNDS(myacim) || OUT_OF_BOUNDS(mycoef1)|| OUT_OF_BOUNDS(mycoef2)|| OUT_OF_BOUNDS(mycoef3)|| OUT_OF_BOUNDS(mycoef4)|| OUT_OF_BOUNDS(mycoef5)|| OUT_OF_BOUNDS(mycoef6)|| OUT_OF_BOUNDS(mycoef7)|| OUT_OF_BOUNDS(mycoef8)) { fprintf(stdout, "Bounds check error on inputs from %s:%d\n", configfile, i+1); return -1; } mycoefs.acim = myacim; mycoefs.coef1 = mycoef1; mycoefs.coef2 = mycoef2; mycoefs.coef3 = mycoef3; mycoefs.coef4 = mycoef4; mycoefs.coef5 = mycoef5; mycoefs.coef6 = mycoef6; mycoefs.coef7 = mycoef7; mycoefs.coef8 = mycoef8; snprintf(completezappath, sizeof(completezappath), "%s/%d", zappath, myzap); fd = open(completezappath, O_RDWR); if (fd < 0) { fprintf(stdout, "open error on %s: %s\n", completezappath, strerror(errno)); return -1; } if (ioctl(fd, WCTDM_SET_ECHOTUNE, &mycoefs)) { fprintf(stdout, "%s: %s\n", completezappath, strerror(errno)); return -1; } close(fd); } fclose(fp); fprintf(stdout, "fxotune: successfully set echo coeffecients on FXO modules\n"); return 0; } if (!strcasecmp(argv[1], "-i")) { if (argc != 3) { /* Show usage */ fputs(usage, stdout); return -1; } configfd = open(configfile, O_CREAT|O_TRUNC|O_WRONLY); if (configfd < 0) { fprintf(stderr, "open: %s\n", strerror(errno)); return -1; } for (i = 0; i < 252; i++) { snprintf(zapdev, sizeof(zapdev), "%s/%d", zappath, i+1); fd = open(zapdev, O_RDWR); if (fd < 0) { fprintf(stdout, "%s absent: %s\n", zapdev, strerror(errno)); continue; } printf("Tuning module %d", i + 1); res = acim_tune(fd, argv[2]); /* Shouldn't matter what digit we press */ if (res < 0) printf("Failure!\n"); else printf("Done!\n"); close(fd); if (res > -1) { /* Do output to file */ int len = 0; static char output[255] = ""; snprintf(output, sizeof(output), "%d=%d,%d,%d,%d,%d,%d,%d,%d,%d\n", i+1, res, 0, 0, 0, 0, 0, 0, 0, 0); len = strlen(output); res = write(configfd, output, strlen(output)); if (res != len) { fprintf(stdout, "Unable to write line \"%s\" to file.\n", output); return -1; } } } close(configfd); /* Some getto goto hackery to make this more convenient */ goto set; } fputs(usage, stdout); return 0; }