summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormattf <mattf@5390a7c7-147a-4af0-8ec9-7488f05a26cb>2008-01-09 22:53:33 +0000
committermattf <mattf@5390a7c7-147a-4af0-8ec9-7488f05a26cb>2008-01-09 22:53:33 +0000
commit0d01869a1227b56da6982b123ce9aa60810e6693 (patch)
tree390d08fe4eadaed69471d7e0979abedc1db3c7ed
parent4d405ee9d9bdb9c20aab6e9071167390a8bb9d12 (diff)
Merged revisions 3640 via svnmerge from
https://origsvn.digium.com/svn/zaptel/branches/1.2 ........ r3640 | mattf | 2008-01-09 16:51:57 -0600 (Wed, 09 Jan 2008) | 1 line BIG fxotune update. New version which uses fourier analysis for echo return loss measurement. Much more accurate than the old version, and can penetrate through background noise that may be on the line as well as noise caused by tones in the background such as reorder or congestion if the timing parameters aren't exactly right in the setup. If you have analog fxo modules, it is recommended that you run fxotune on them again with this latest version. You will definitely see even better results ........ git-svn-id: http://svn.digium.com/svn/zaptel/branches/1.4@3641 5390a7c7-147a-4af0-8ec9-7488f05a26cb
-rw-r--r--fxotune.c155
-rw-r--r--fxotune.h9
2 files changed, 134 insertions, 30 deletions
diff --git a/fxotune.c b/fxotune.c
index b25d8ee..ea4467e 100644
--- a/fxotune.c
+++ b/fxotune.c
@@ -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;
}
diff --git a/fxotune.h b/fxotune.h
index 4d197ed..1fc2ef9 100644
--- a/fxotune.h
+++ b/fxotune.h
@@ -99,5 +99,14 @@ struct wctdm_echo_coefs echo_trys [] =
{ 8, 253, 2, 244, 255, 10, 244, 3, 253},
{ 10, 249, 244, 8, 12, 245, 252, 0, 1},
+ /* Make sure we include the rest of the impedances */
+ { 8, 0, 0, 0, 0, 0, 0, 0, 0},
+ { 9, 0, 0, 0, 0, 0, 0, 0, 0},
+ { 10, 0, 0, 0, 0, 0, 0, 0, 0},
+ { 11, 0, 0, 0, 0, 0, 0, 0, 0},
+ { 12, 0, 0, 0, 0, 0, 0, 0, 0},
+ { 13, 0, 0, 0, 0, 0, 0, 0, 0},
+ { 14, 0, 0, 0, 0, 0, 0, 0, 0},
+ { 15, 0, 0, 0, 0, 0, 0, 0, 0},
};