summaryrefslogtreecommitdiff
path: root/fxotune.c
diff options
context:
space:
mode:
Diffstat (limited to 'fxotune.c')
-rw-r--r--fxotune.c155
1 files changed, 125 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;
}