/* * sliptest - A simple test program to investigate slips and dropouts in Zaptel channels. * * sliptest.c * * Written by Steve Underwood * * Copyright (C) 2005 Steve Underwood * * All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Id$ */ #define _ISOC9X_SOURCE 1 #define _ISOC99_SOURCE 1 #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* A simple test program to investigate frame slips and drops in Zaptel channels. The principle is to look for a peak in the cross-correlation of a tranmitted signal and its received echo. For an analogue circuit the echo can simply be the natural echo of the hybrid on the card under test. That is, don't connect anything to the port, and let the energy spilled back from the DAC to the ADC by the hybrid on the card correlate with the transmitted signal. Using something like a sine wave creates ambiguity about the real lag - you don't know exactly how many cycles long the pipe is. We use AWGN, so the sequence does not repeat. We track the peak correlation over 320ms (16 x 20ms blocks), so we should definitely find the one true lag. The pipe through the buffers, and the audio delay should never approach that. The measured lag should be very stable. If it is a pretty clear sign the pipe length is changing. This would be a bad thing! */ #define FALSE 0 #define TRUE (!FALSE) #define BLOCK_LEN 160 #define MAX_BLOCK 16 int16_t in_buf[MAX_BLOCK*BLOCK_LEN]; int16_t out_buf[MAX_BLOCK*BLOCK_LEN]; int in_step = 0; int out_step = 0; awgn_state_t noise_source; static int cross_correlate(int16_t in[], int16_t out[], int pos) { int i; int j; int k; float temp; float scale; float max; int maxpos; int start; start = pos - BLOCK_LEN; if (start < 0) start += MAX_BLOCK*BLOCK_LEN; max = -9999999999999999.0; maxpos = pos - BLOCK_LEN; for (k = start; k != pos; k = (k > 0) ? k - 1 : MAX_BLOCK*BLOCK_LEN - 1) { temp = 0.0; for (i = 0; i < BLOCK_LEN; i++) { j = i + k; if (j >= MAX_BLOCK*BLOCK_LEN) j -= MAX_BLOCK*BLOCK_LEN; temp += out[j]*in[i]; } if (temp > max) { max = temp; maxpos = k; } } pos -= maxpos; if (pos < 0) pos += MAX_BLOCK*BLOCK_LEN; return pos; } /*- End of function --------------------------------------------------------*/ static int channel_open(char *dev) { struct zt_bufferinfo bi; struct zt_gains g; int x; int fd; int i; int chan; int linear; if ((chan = atoi(dev)) > 0) { if ((fd = open("/dev/zap/channel", O_RDWR | O_NONBLOCK)) < 0) return -1; /*endif*/ if (ioctl(fd, ZT_SPECIFY, &chan)) { x = errno; close(fd); errno = x; return -1; } /*endif*/ } else { if ((fd = open(dev, O_RDWR | O_NONBLOCK)) < 0) return -1; /*endif*/ } /*endif*/ if (ioctl(fd, ZT_GET_BUFINFO, &bi) < 0) { x = errno; close(fd); errno = x; return -1; } /*endif*/ bi.txbufpolicy = ZT_POLICY_IMMEDIATE; bi.rxbufpolicy = ZT_POLICY_IMMEDIATE; bi.numbufs = 4; bi.bufsize = BLOCK_LEN; if (ioctl(fd, ZT_SET_BUFINFO, &bi) < 0) { x = errno; close(fd); errno = x; return -1; } /*endif*/ if (ioctl(fd, ZT_CHANNO, &chan)) { x = errno; close(fd); errno = x; return -1; } /*endif*/ /* Set default gains */ g.chan = 0; for (i = 0; i < 256; i++) { g.rxgain[i] = i; g.txgain[i] = i; } if (ioctl(fd, ZT_SETGAINS, &g) < 0) { x = errno; close(fd); errno = x; return -1; } /*endif*/ linear = 1; if (ioctl(fd, ZT_SETLINEAR, &linear)) return -1; /*endif*/ return fd; } /*- End of function --------------------------------------------------------*/ int main(int argc, char *argv[]) { int res; int len; int xlen; int maxlen; int i; int x; int ch; int best; ZT_SPANINFO zi; int fd; if (argc < 2) { fprintf(stderr, "No channel specified\n"); exit(2); } if ((fd = channel_open(argv[1])) < 0) { fprintf(stderr, "Error opening channel '%s'\n", argv[1]); exit(2); } awgn_init_dbm0(&noise_source, 1234567, -6); for (;;) { //x = ZT_IOMUX_SIGEVENT | ZT_IOMUX_READ | ZT_IOMUX_WRITE | ZT_IOMUX_WRITEEMPTY | ZT_IOMUX_NOWAIT; x = ZT_IOMUX_SIGEVENT | ZT_IOMUX_READ | ZT_IOMUX_WRITE | ZT_IOMUX_NOWAIT; if ((res = ioctl(fd, ZT_IOMUX, &x))) { fprintf(stderr, "Error at ZT_IOMUX\n"); exit(2); } /*endif*/ if (x == 0) continue; /*endif*/ if ((x & ZT_IOMUX_SIGEVENT)) { res = ioctl(fd, ZT_GETEVENT, &x); if (res == 0 && x != 0) { switch (x) { case ZT_EVENT_ALARM: case ZT_EVENT_NOALARM: memset(&zi, 0, sizeof(zi)); zi.spanno = 0; if ((res = ioctl(fd, ZT_SPANSTAT, &zi)) < 0) { fprintf(stderr, "Error at ZT_SPANSTAT\n"); break; } /*endif*/ fprintf(stderr, "Alarms - 0x%X\n", zi.alarms); break; default: break; } /*endswitch*/ } /*endif*/ continue; } /*endif*/ if ((x & ZT_IOMUX_READ)) { if (in_step >= MAX_BLOCK - 1) in_step = 0; else in_step++; if ((res = read(fd, in_buf + in_step*BLOCK_LEN, sizeof(int16_t)*BLOCK_LEN)) < 0) { if (errno == ELAST) { /* This means there is some out of band stuff to deal with - alarms, CAS bits signaling changes, etc. */ } else if (errno != EAGAIN) { fprintf(stderr, "Error at read\n"); exit(2); } /*endif*/ continue; } /*endif*/ if (res != sizeof(int16_t)*BLOCK_LEN) { fprintf(stderr, "Error at read %d bytes\n", res); exit(2); } /* Cross-correlate the audio */ best = cross_correlate(in_buf + in_step*BLOCK_LEN, out_buf, out_step*BLOCK_LEN); printf("%d\n", best); continue; } /*endif*/ if ((x & ZT_IOMUX_WRITE)) { /* Cook up some audio */ if (out_step >= MAX_BLOCK - 1) out_step = 0; else out_step++; for (i = out_step*BLOCK_LEN; i < (out_step + 1)*BLOCK_LEN; i++) out_buf[i] = awgn(&noise_source); xlen = write(fd, out_buf + out_step*BLOCK_LEN, sizeof(int16_t)*BLOCK_LEN); if (xlen != sizeof(int16_t)*BLOCK_LEN) { fprintf(stderr, "Error at write\n"); exit(2); } /*endif*/ continue; } /*endif*/ if ((x & ZT_IOMUX_WRITEEMPTY)) { continue; } /*endif*/ } /*endfor*/ } /*- End of function --------------------------------------------------------*/ /*- End of file ------------------------------------------------------------*/