summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormatteo <matteo@5390a7c7-147a-4af0-8ec9-7488f05a26cb>2003-03-17 18:11:45 +0000
committermatteo <matteo@5390a7c7-147a-4af0-8ec9-7488f05a26cb>2003-03-17 18:11:45 +0000
commitf04ea3143569dbadcdd5ed96e8feed0601464156 (patch)
treeba9a6a7e6bfd24d828419d8707209c8cc9f6798e
parentef7de3f35f2ef572c106a972b7b16b0e947e620b (diff)
lun mar 17 19:11:15 CET 2003
git-svn-id: http://svn.digium.com/svn/zaptel/trunk@155 5390a7c7-147a-4af0-8ec9-7488f05a26cb
-rwxr-xr-xMakefile10
-rwxr-xr-xcomplex.cc61
-rwxr-xr-xcomplex.h63
-rwxr-xr-xmkfilter.h49
-rwxr-xr-xmknotch.cc145
-rwxr-xr-xtor2.c2
-rwxr-xr-xtorisa.c2
-rwxr-xr-xwcfxo.c2
-rwxr-xr-xwcfxs.c2
-rwxr-xr-xwct1xxp.c2
-rwxr-xr-xwctdm.c2
-rwxr-xr-xzaptel.c162
-rwxr-xr-xzaptel.conf.sample8
-rwxr-xr-xzaptel.h59
-rwxr-xr-xztcfg.c101
-rwxr-xr-xztdynamic.c2
16 files changed, 652 insertions, 20 deletions
diff --git a/Makefile b/Makefile
index 6fb057e..1451727 100755
--- a/Makefile
+++ b/Makefile
@@ -179,8 +179,14 @@ $(LIBTONEZONE): $(TZOBJS)
ztcfg.c: ztcfg.h
-ztcfg: ztcfg.o $(LIBTONEZONE)
- $(CC) -o ztcfg ztcfg.o -L. -ltonezone
+ztcfg: ztcfg.o mknotch.o complex.o $(LIBTONEZONE)
+ $(CC) -o ztcfg ztcfg.o mknotch.o complex.o -lm -L. -ltonezone
+
+mknotch.o: mknotch.cc
+ $(CC) -c mknotch.cc
+
+complex.o: complex.cc
+ $(CC) -c complex.cc
usbfxstest.o: usbfxstest.c
$(CC) -g -c usbfxstest.c
diff --git a/complex.cc b/complex.cc
new file mode 100755
index 0000000..d137f13
--- /dev/null
+++ b/complex.cc
@@ -0,0 +1,61 @@
+/* mkfilter -- given n, compute recurrence relation
+ to implement Butterworth, Bessel or Chebyshev filter of order n
+ A.J. Fisher, University of York <fisher@minster.york.ac.uk>
+ September 1992 */
+
+/* Routines for complex arithmetic */
+
+#include <math.h>
+
+#include "mkfilter.h"
+#include "complex.h"
+
+static complex eval(complex[], int, complex);
+static double Xsqrt(double);
+
+
+global complex evaluate(complex topco[], int nz, complex botco[], int np, complex z)
+ { /* evaluate response, substituting for z */
+ return eval(topco, nz, z) / eval(botco, np, z);
+ }
+
+static complex eval(complex coeffs[], int npz, complex z)
+ { /* evaluate polynomial in z, substituting for z */
+ complex sum = complex(0.0);
+ for (int i = npz; i >= 0; i--) sum = (sum * z) + coeffs[i];
+ return sum;
+ }
+
+global complex csqrt(complex x)
+ { double r = hypot(x);
+ complex z = complex(Xsqrt(0.5 * (r + x.re)),
+ Xsqrt(0.5 * (r - x.re)));
+ if (x.im < 0.0) z.im = -z.im;
+ return z;
+ }
+
+static double Xsqrt(double x)
+ { /* because of deficiencies in hypot on Sparc, it's possible for arg of Xsqrt to be small and -ve,
+ which logically it can't be (since r >= |x.re|). Take it as 0. */
+ return (x >= 0.0) ? sqrt(x) : 0.0;
+ }
+
+global complex cexp(complex z)
+ { return exp(z.re) * expj(z.im);
+ }
+
+global complex expj(double theta)
+ { return complex(cos(theta), sin(theta));
+ }
+
+global complex operator * (complex z1, complex z2)
+ { return complex(z1.re*z2.re - z1.im*z2.im,
+ z1.re*z2.im + z1.im*z2.re);
+ }
+
+global complex operator / (complex z1, complex z2)
+ { double mag = (z2.re * z2.re) + (z2.im * z2.im);
+ return complex (((z1.re * z2.re) + (z1.im * z2.im)) / mag,
+ ((z1.im * z2.re) - (z1.re * z2.im)) / mag);
+ }
+
diff --git a/complex.h b/complex.h
new file mode 100755
index 0000000..fd9de49
--- /dev/null
+++ b/complex.h
@@ -0,0 +1,63 @@
+struct c_complex
+ { double re, im;
+ };
+
+struct complex
+ { double re, im;
+ complex(double r, double i = 0.0) { re = r; im = i; }
+ complex() { } /* uninitialized complex */
+ complex(c_complex z) { re = z.re; im = z.im; } /* init from denotation */
+ };
+
+extern complex csqrt(complex), cexp(complex), expj(double); /* from complex.C */
+extern complex evaluate(complex[], int, complex[], int, complex); /* from complex.C */
+
+inline double hypot(complex z) { return ::hypot(z.im, z.re); }
+inline double atan2(complex z) { return ::atan2(z.im, z.re); }
+
+inline complex cconj(complex z)
+ { z.im = -z.im;
+ return z;
+ }
+
+inline complex operator * (double a, complex z)
+ { z.re *= a; z.im *= a;
+ return z;
+ }
+
+inline complex operator / (complex z, double a)
+ { z.re /= a; z.im /= a;
+ return z;
+ }
+
+inline void operator /= (complex &z, double a)
+ { z = z / a;
+ }
+
+extern complex operator * (complex, complex);
+extern complex operator / (complex, complex);
+
+inline complex operator + (complex z1, complex z2)
+ { z1.re += z2.re;
+ z1.im += z2.im;
+ return z1;
+ }
+
+inline complex operator - (complex z1, complex z2)
+ { z1.re -= z2.re;
+ z1.im -= z2.im;
+ return z1;
+ }
+
+inline complex operator - (complex z)
+ { return 0.0 - z;
+ }
+
+inline bool operator == (complex z1, complex z2)
+ { return (z1.re == z2.re) && (z1.im == z2.im);
+ }
+
+inline complex sqr(complex z)
+ { return z*z;
+ }
+
diff --git a/mkfilter.h b/mkfilter.h
new file mode 100755
index 0000000..80e5a8b
--- /dev/null
+++ b/mkfilter.h
@@ -0,0 +1,49 @@
+/* mkfilter -- given n, compute recurrence relation
+ to implement Butterworth, Bessel or Chebyshev filter of order n
+ A.J. Fisher, University of York <fisher@minster.york.ac.uk>
+ September 1992 */
+
+#include <string.h>
+
+/* Header file */
+
+#define global
+#define unless(x) if(!(x))
+#define until(x) while(!(x))
+
+#define VERSION "4.6"
+#undef PI
+#define PI 3.14159265358979323846 /* Microsoft C++ does not define M_PI ! */
+#define TWOPI (2.0 * PI)
+#define EPS 1e-10
+#define MAXORDER 10
+#define MAXPZ 512 /* .ge. 2*MAXORDER, to allow for doubling of poles in BP filter;
+ high values needed for FIR filters */
+#define MAXSTRING 256
+
+typedef void (*proc)();
+typedef unsigned int uint;
+
+extern "C"
+ { double atof(const char*);
+ int atoi(char*);
+ void exit(int);
+ };
+
+extern const char *progname;
+extern void readdata(char*, double&, int&, double*, int&, double*);
+
+inline double sqr(double x) { return x*x; }
+inline bool seq(char *s1, char *s2) { return strcmp(s1,s2) == 0; }
+inline bool onebit(uint m) { return (m != 0) && ((m & m-1) == 0); }
+
+inline double asinh(double x)
+ { /* Microsoft C++ does not define */
+ return log(x + sqrt(1.0 + sqr(x)));
+ }
+
+inline double fix(double x)
+ { /* nearest integer */
+ return (x >= 0.0) ? floor(0.5+x) : -floor(0.5-x);
+ }
+
diff --git a/mknotch.cc b/mknotch.cc
new file mode 100755
index 0000000..98a247b
--- /dev/null
+++ b/mknotch.cc
@@ -0,0 +1,145 @@
+/* mknotch -- Make IIR notch filter parameters, based upon mkfilter;
+ A.J. Fisher, University of York <fisher@minster.york.ac.uk>
+ September 1992 */
+
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+
+#include "mkfilter.h"
+#include "complex.h"
+
+#define opt_be 0x00001 /* -Be Bessel characteristic */
+#define opt_bu 0x00002 /* -Bu Butterworth characteristic */
+#define opt_ch 0x00004 /* -Ch Chebyshev characteristic */
+#define opt_re 0x00008 /* -Re Resonator */
+#define opt_pi 0x00010 /* -Pi proportional-integral */
+
+#define opt_lp 0x00020 /* -Lp lowpass */
+#define opt_hp 0x00040 /* -Hp highpass */
+#define opt_bp 0x00080 /* -Bp bandpass */
+#define opt_bs 0x00100 /* -Bs bandstop */
+#define opt_ap 0x00200 /* -Ap allpass */
+
+#define opt_a 0x00400 /* -a alpha value */
+#define opt_l 0x00800 /* -l just list filter parameters */
+#define opt_o 0x01000 /* -o order of filter */
+#define opt_p 0x02000 /* -p specified poles only */
+#define opt_w 0x04000 /* -w don't pre-warp */
+#define opt_z 0x08000 /* -z use matched z-transform */
+#define opt_Z 0x10000 /* -Z additional zero */
+
+struct pzrep
+ { complex poles[MAXPZ], zeros[MAXPZ];
+ int numpoles, numzeros;
+ };
+
+static pzrep splane, zplane;
+static double raw_alpha1, raw_alpha2;
+static complex dc_gain, fc_gain, hf_gain;
+static uint options;
+static double qfactor;
+static bool infq;
+static uint polemask;
+static double xcoeffs[MAXPZ+1], ycoeffs[MAXPZ+1];
+
+static void compute_notch();
+static void expandpoly(), expand(complex[], int, complex[]), multin(complex, int, complex[]);
+
+static void compute_bpres()
+ { /* compute Z-plane pole & zero positions for bandpass resonator */
+ zplane.numpoles = zplane.numzeros = 2;
+ zplane.zeros[0] = 1.0; zplane.zeros[1] = -1.0;
+ double theta = TWOPI * raw_alpha1; /* where we want the peak to be */
+ if (infq)
+ { /* oscillator */
+ complex zp = expj(theta);
+ zplane.poles[0] = zp; zplane.poles[1] = cconj(zp);
+ }
+ else
+ { /* must iterate to find exact pole positions */
+ complex topcoeffs[MAXPZ+1]; expand(zplane.zeros, zplane.numzeros, topcoeffs);
+ double r = exp(-theta / (2.0 * qfactor));
+ double thm = theta, th1 = 0.0, th2 = PI;
+ bool cvg = false;
+ for (int i=0; i < 50 && !cvg; i++)
+ { complex zp = r * expj(thm);
+ zplane.poles[0] = zp; zplane.poles[1] = cconj(zp);
+ complex botcoeffs[MAXPZ+1]; expand(zplane.poles, zplane.numpoles, botcoeffs);
+ complex g = evaluate(topcoeffs, zplane.numzeros, botcoeffs, zplane.numpoles, expj(theta));
+ double phi = g.im / g.re; /* approx to atan2 */
+ if (phi > 0.0) th2 = thm; else th1 = thm;
+ if (fabs(phi) < EPS) cvg = true;
+ thm = 0.5 * (th1+th2);
+ }
+ unless (cvg) fprintf(stderr, "mkfilter: warning: failed to converge\n");
+ }
+ }
+
+static void compute_notch()
+ { /* compute Z-plane pole & zero positions for bandstop resonator (notch filter) */
+ compute_bpres(); /* iterate to place poles */
+ double theta = TWOPI * raw_alpha1;
+ complex zz = expj(theta); /* place zeros exactly */
+ zplane.zeros[0] = zz; zplane.zeros[1] = cconj(zz);
+ }
+
+static void expandpoly() /* given Z-plane poles & zeros, compute top & bot polynomials in Z, and then recurrence relation */
+ { complex topcoeffs[MAXPZ+1], botcoeffs[MAXPZ+1]; int i;
+ expand(zplane.zeros, zplane.numzeros, topcoeffs);
+ expand(zplane.poles, zplane.numpoles, botcoeffs);
+ dc_gain = evaluate(topcoeffs, zplane.numzeros, botcoeffs, zplane.numpoles, 1.0);
+ double theta = TWOPI * 0.5 * (raw_alpha1 + raw_alpha2); /* "jwT" for centre freq. */
+ fc_gain = evaluate(topcoeffs, zplane.numzeros, botcoeffs, zplane.numpoles, expj(theta));
+ hf_gain = evaluate(topcoeffs, zplane.numzeros, botcoeffs, zplane.numpoles, -1.0);
+ for (i = 0; i <= zplane.numzeros; i++) xcoeffs[i] = +(topcoeffs[i].re / botcoeffs[zplane.numpoles].re);
+ for (i = 0; i <= zplane.numpoles; i++) ycoeffs[i] = -(botcoeffs[i].re / botcoeffs[zplane.numpoles].re);
+ }
+
+
+static void expand(complex pz[], int npz, complex coeffs[])
+ { /* compute product of poles or zeros as a polynomial of z */
+ int i;
+ coeffs[0] = 1.0;
+ for (i=0; i < npz; i++) coeffs[i+1] = 0.0;
+ for (i=0; i < npz; i++) multin(pz[i], npz, coeffs);
+ /* check computed coeffs of z^k are all real */
+ for (i=0; i < npz+1; i++)
+ { if (fabs(coeffs[i].im) > EPS)
+ { fprintf(stderr, "mkfilter: coeff of z^%d is not real; poles/zeros are not complex conjugates\n", i);
+ exit(1);
+ }
+ }
+ }
+
+static void multin(complex w, int npz, complex coeffs[])
+ { /* multiply factor (z-w) into coeffs */
+ complex nw = -w;
+ for (int i = npz; i >= 1; i--) coeffs[i] = (nw * coeffs[i]) + coeffs[i-1];
+ coeffs[0] = nw * coeffs[0];
+ }
+
+extern "C" void mknotch(float freq,float bw,long *p1, long *p2, long *p3);
+
+void mknotch(float freq,float bw,long *p1, long *p2, long *p3)
+{
+#define NB 14
+
+ options = opt_re;
+ qfactor = freq / bw;
+ infq = false;
+ raw_alpha1 = freq / 8000.0;
+ polemask = ~0;
+
+ compute_notch();
+ expandpoly();
+
+ float fsh = (float) (1 << NB);
+ *p1 = (long)(xcoeffs[1] * fsh);
+ *p2 = (long)(ycoeffs[0] * fsh);
+ *p3 = (long)(ycoeffs[1] * fsh);
+}
+
+
+
+
diff --git a/tor2.c b/tor2.c
index 0d5bd98..7ed300d 100755
--- a/tor2.c
+++ b/tor2.c
@@ -291,7 +291,7 @@ static void init_spans(struct tor2 *tor)
struct zt_chan *mychans = tor->chans[x] + y;
sprintf(mychans->name, "Tor2/%d/%d/%d", tor->num, x + 1, y + 1);
mychans->sigcap = ZT_SIG_EM | ZT_SIG_CLEAR | ZT_SIG_FXSLS | ZT_SIG_FXSGS | ZT_SIG_FXSKS |
- ZT_SIG_FXOLS | ZT_SIG_FXOGS | ZT_SIG_FXOKS | ZT_SIG_CAS;
+ ZT_SIG_FXOLS | ZT_SIG_FXOGS | ZT_SIG_FXOKS | ZT_SIG_CAS | ZT_SIG_SF;
c = (x * tor->spans[x].channels) + y;
mychans->pvt = &tor->tchans[c];
mychans->chanpos = y + 1;
diff --git a/torisa.c b/torisa.c
index 0b41fed..c9af479 100755
--- a/torisa.c
+++ b/torisa.c
@@ -303,7 +303,7 @@ static void make_chans(void)
c = x * channels_per_span + y;
sprintf(chans[c].name, "TorISA/%d/%d", x + 1, y + 1);
chans[c].sigcap = ZT_SIG_EM | ZT_SIG_CLEAR | ZT_SIG_FXSLS | ZT_SIG_FXSGS | ZT_SIG_FXSKS |
- ZT_SIG_FXOLS | ZT_SIG_FXOGS | ZT_SIG_FXOKS | ZT_SIG_CAS;
+ ZT_SIG_FXOLS | ZT_SIG_FXOGS | ZT_SIG_FXOKS | ZT_SIG_CAS | ZT_SIG_SF;
chans[c].pvt = &pvts[c];
pvts[c].span = x;
chans[c].chanpos = y + 1;
diff --git a/wcfxo.c b/wcfxo.c
index c7e0d32..a0118f1 100755
--- a/wcfxo.c
+++ b/wcfxo.c
@@ -567,7 +567,7 @@ static int wcfxo_initialize(struct wcfxo *wc)
sprintf(wc->span.name, "WCFXO/%d", wc->pos);
sprintf(wc->span.desc, "%s Board %d", wc->variety, wc->pos + 1);
sprintf(wc->chan.name, "WCFXO/%d/%d", wc->pos, 0);
- wc->chan.sigcap = ZT_SIG_FXSKS | ZT_SIG_FXSLS;
+ wc->chan.sigcap = ZT_SIG_FXSKS | ZT_SIG_FXSLS | ZT_SIG_SF;
wc->chan.chanpos = 1;
wc->span.chans = &wc->chan;
wc->span.channels = 1;
diff --git a/wcfxs.c b/wcfxs.c
index 623ca84..6fda7c6 100755
--- a/wcfxs.c
+++ b/wcfxs.c
@@ -933,7 +933,7 @@ static int wcfxs_initialize(struct wcfxs *wc)
wc->span.deflaw = ZT_LAW_MULAW;
for (x=0;x<wc->cards;x++) {
sprintf(wc->chans[x].name, "WCFXS/%d/%d", wc->pos, x);
- wc->chans[x].sigcap = ZT_SIG_FXOKS | ZT_SIG_FXOLS | ZT_SIG_FXOGS;
+ wc->chans[x].sigcap = ZT_SIG_FXOKS | ZT_SIG_FXOLS | ZT_SIG_FXOGS | ZT_SIG_SF;
wc->chans[x].chanpos = x+1;
wc->chans[x].pvt = wc;
}
diff --git a/wct1xxp.c b/wct1xxp.c
index 9dee855..d29b0d0 100755
--- a/wct1xxp.c
+++ b/wct1xxp.c
@@ -781,7 +781,7 @@ static int t1xxp_software_init(struct t1xxp *wc)
wc->chans[x].sigcap = ZT_SIG_EM | ZT_SIG_CLEAR |
ZT_SIG_FXSLS | ZT_SIG_FXSGS |
ZT_SIG_FXSKS | ZT_SIG_FXOLS |
- ZT_SIG_FXOGS | ZT_SIG_FXOKS | ZT_SIG_CAS;
+ ZT_SIG_FXOGS | ZT_SIG_FXOKS | ZT_SIG_CAS | ZT_SIG_SF;
wc->chans[x].pvt = wc;
wc->chans[x].chanpos = x + 1;
}
diff --git a/wctdm.c b/wctdm.c
index 623ca84..6fda7c6 100755
--- a/wctdm.c
+++ b/wctdm.c
@@ -933,7 +933,7 @@ static int wcfxs_initialize(struct wcfxs *wc)
wc->span.deflaw = ZT_LAW_MULAW;
for (x=0;x<wc->cards;x++) {
sprintf(wc->chans[x].name, "WCFXS/%d/%d", wc->pos, x);
- wc->chans[x].sigcap = ZT_SIG_FXOKS | ZT_SIG_FXOLS | ZT_SIG_FXOGS;
+ wc->chans[x].sigcap = ZT_SIG_FXOKS | ZT_SIG_FXOLS | ZT_SIG_FXOGS | ZT_SIG_SF;
wc->chans[x].chanpos = x+1;
wc->chans[x].pvt = wc;
}
diff --git a/zaptel.c b/zaptel.c
index 1fdfa62..e8ba8d1 100755
--- a/zaptel.c
+++ b/zaptel.c
@@ -299,7 +299,7 @@ static rwlock_t chan_lock = RW_LOCK_UNLOCKED;
static struct zt_zone *tone_zones[ZT_TONE_ZONE_MAX];
-#define NUM_SIGS 8
+#define NUM_SIGS 9
static inline void rotate_sums(void)
@@ -326,7 +326,8 @@ static unsigned int in_sig[NUM_SIGS][2] = {
{ ZT_SIG_FXSKS,ZT_BBIT | ZT_BBIT | ((ZT_ABIT | ZT_BBIT) << 8)},
{ ZT_SIG_FXOLS,0 | (ZT_ABIT << 8)},
{ ZT_SIG_FXOGS,ZT_BBIT | ((ZT_ABIT | ZT_BBIT) << 8)},
- { ZT_SIG_FXOKS,0 | (ZT_ABIT << 8)}
+ { ZT_SIG_FXOKS,0 | (ZT_ABIT << 8)},
+ { ZT_SIG_SF, 0}
} ;
/* must have span to begin with */
@@ -376,6 +377,8 @@ static char *sigstr(int sig)
return "Slave";
case ZT_SIG_CAS:
return "CAS";
+ case ZT_SIG_SF:
+ return "SF (ToneOnly)";
case ZT_SIG_NONE:
default:
return "Unconfigured";
@@ -1556,6 +1559,21 @@ static int zt_chan_release(struct inode *inode, struct file *file)
return 0;
}
+static void set_txtone(struct zt_chan *ss,int fac, int init_v2, int init_v3)
+{
+ if (fac == 0)
+ {
+ ss->v2_1 = 0;
+ ss->v3_1 = 0;
+ return;
+ }
+ ss->txtone = fac;
+ ss->v1_1 = 0;
+ ss->v2_1 = init_v2;
+ ss->v3_1 = init_v3;
+ return;
+}
+
static void zt_rbs_sethook(struct zt_chan *chan, int txsig, int txstate, int timeout)
{
static int outs[NUM_SIGS][5] = {
@@ -1577,7 +1595,11 @@ who cares what the sig bits are as long as they are stable */
{ ZT_SIG_FXOGS, ZT_ABIT | ZT_BBIT | ZT_CBIT | ZT_DBIT,
ZT_BBIT | ZT_DBIT, 0, 0 }, /* FXO Groundstart */
{ ZT_SIG_FXOKS, ZT_BBIT | ZT_DBIT, ZT_BBIT | ZT_DBIT, 0,
- ZT_ABIT | ZT_BBIT | ZT_CBIT | ZT_DBIT } /* FXO Kewlstart */
+ ZT_ABIT | ZT_BBIT | ZT_CBIT | ZT_DBIT }, /* FXO Kewlstart */
+ { ZT_SIG_SF, ZT_BBIT | ZT_CBIT | ZT_DBIT,
+ ZT_ABIT | ZT_BBIT | ZT_CBIT | ZT_DBIT,
+ ZT_ABIT | ZT_BBIT | ZT_CBIT | ZT_DBIT,
+ ZT_BBIT | ZT_CBIT | ZT_DBIT }, /* no signalling */
} ;
int x;
@@ -1598,6 +1620,25 @@ who cares what the sig bits are as long as they are stable */
}
chan->txstate = txstate;
+ /* if tone signalling */
+ if (chan->sig == ZT_SIG_SF)
+ {
+ chan->txhooksig = txsig;
+ if (chan->txtone) /* if set to make tone for tx */
+ {
+ if ((txsig && !(chan->toneflags & ZT_REVERSE_TXTONE)) ||
+ ((!txsig) && (chan->toneflags & ZT_REVERSE_TXTONE)))
+ {
+ set_txtone(chan,chan->txtone,chan->tx_v2,chan->tx_v3);
+ }
+ else
+ {
+ set_txtone(chan,0,0,0);
+ }
+ }
+ chan->otimer = timeout * 8; /* Otimer is timer in samples */
+ return;
+ }
if (chan->span->hooksig) {
chan->txhooksig = txsig;
chan->span->hooksig(chan, txsig);
@@ -2488,6 +2529,7 @@ static int zt_ctl_ioctl(struct inode *inode, struct file *file, unsigned int cmd
int i,j;
struct zt_lineconfig lc;
struct zt_chanconfig ch;
+ struct zt_sfconfig sf;
int sigcap;
int res = 0;
int x,y;
@@ -2664,6 +2706,33 @@ static int zt_ctl_ioctl(struct inode *inode, struct file *file, unsigned int cmd
#endif
spin_unlock_irqrestore(&chans[ch.chan]->lock, flags);
return res;
+ case ZT_SFCONFIG:
+ if (copy_from_user(&sf, (struct zt_chanconfig *)data, sizeof(sf)))
+ return -EFAULT;
+ VALID_CHANNEL(sf.chan);
+ if (chans[sf.chan]->sig != ZT_SIG_SF) return -EINVAL;
+ spin_lock_irqsave(&chans[sf.chan]->lock, flags);
+ chans[sf.chan]->rxp1 = sf.rxp1;
+ chans[sf.chan]->rxp2 = sf.rxp2;
+ chans[sf.chan]->rxp3 = sf.rxp3;
+ chans[sf.chan]->txtone = sf.txtone;
+ chans[sf.chan]->tx_v2 = sf.tx_v2;
+ chans[sf.chan]->tx_v3 = sf.tx_v3;
+ chans[sf.chan]->toneflags = sf.toneflag;
+ if (sf.txtone) /* if set to make tone for tx */
+ {
+ if ((chans[sf.chan]->txhooksig && !(sf.toneflag & ZT_REVERSE_TXTONE)) ||
+ ((!chans[sf.chan]->txhooksig) && (sf.toneflag & ZT_REVERSE_TXTONE)))
+ {
+ set_txtone(chans[sf.chan],sf.txtone,sf.tx_v2,sf.tx_v3);
+ }
+ else
+ {
+ set_txtone(chans[sf.chan],0,0,0);
+ }
+ }
+ spin_unlock_irqrestore(&chans[sf.chan]->lock, flags);
+ return res;
case ZT_DEFAULTZONE:
if (get_user(j,(int *)data))
return -EFAULT; /* get conf # */
@@ -3963,6 +4032,15 @@ static inline void zt_process_getaudio_chunk(struct zt_chan *ss, unsigned char *
memcpy(ms->getlin_lastchunk, ms->getlin, ZT_CHUNKSIZE * sizeof(short));
/* save value from current */
memcpy(ms->getlin, getlin, ZT_CHUNKSIZE * sizeof(short));
+ /* if to make tx tone */
+ if (ms->v1_1 || ms->v2_1 || ms->v3_1)
+ {
+ for (x=0;x<ZT_CHUNKSIZE;x++)
+ {
+ getlin[x] += zt_txtone_nextsample(ms);
+ txb[x] = ZT_LIN2X(getlin[x], ms);
+ }
+ }
/* This is what to send (after having applied gain) */
for (x=0;x<ZT_CHUNKSIZE;x++)
txb[x] = ms->txgain[txb[x]];
@@ -4434,13 +4512,62 @@ void zt_ec_chunk(struct zt_chan *ss, unsigned char *rxchunk, const unsigned char
spin_unlock_irqrestore(&ss->lock, flags);
}
+/* return 0 if nothing detected, 1 if lack of tone, 2 if presence of tone */
+/* modifies buffer pointed to by 'amp' with notched-out values */
+static inline int sf_detect (sf_detect_state_t *s,
+ short *amp,
+ int samples,long p1, long p2, long p3)
+{
+int i,rv = 0;
+long x,y;
+
+#define SF_DETECT_SAMPLES (ZT_CHUNKSIZE * 5)
+#define SF_DETECT_MIN_ENERGY 500
+#define NB 14 /* number of bits to shift left */
+
+ /* determine energy level before filtering */
+ for(i = 0; i < samples; i++)
+ {
+ if (amp[i] < 0) s->e1 -= amp[i];
+ else s->e1 += amp[i];
+ }
+ /* do 2nd order IIR notch filter at given freq. and calculate
+ energy */
+ for(i = 0; i < samples; i++)
+ {
+ x = amp[i] << NB;
+ y = s->x2 + (p1 * (s->x1 >> NB)) + x;
+ y += (p2 * (s->y2 >> NB)) +
+ (p3 * (s->y1 >> NB));
+ s->x2 = s->x1;
+ s->x1 = x;
+ s->y2 = s->y1;
+ s->y1 = y;
+ amp[i] = y >> NB;
+ if (amp[i] < 0) s->e2 -= amp[i];
+ else s->e2 += amp[i];
+ }
+ s->samps += i;
+ /* if time to do determination */
+ if ((s->samps) >= SF_DETECT_SAMPLES)
+ {
+ rv = 1; /* default to no tone */
+ /* if enough energy, it is determined to be a tone */
+ if (((s->e1 - s->e2) / s->samps) > SF_DETECT_MIN_ENERGY) rv = 2;
+ /* reset energy processing variables */
+ s->samps = 0;
+ s->e1 = s->e2 = 0;
+ }
+ return(rv);
+}
+
static inline void zt_process_putaudio_chunk(struct zt_chan *ss, unsigned char *rxb)
{
/* We transmit data from our master channel */
struct zt_chan *ms = ss->master;
/* Linear version of received data */
short putlin[ZT_CHUNKSIZE],k[ZT_CHUNKSIZE];
- int x;
+ int x,r;
if (ms->dialing) ms->afterdialingtimer = 50;
else if (ms->afterdialingtimer) ms->afterdialingtimer--;
@@ -4465,7 +4592,32 @@ static inline void zt_process_putaudio_chunk(struct zt_chan *ss, unsigned char *
}
}
}
-
+ /* if doing rx tone decoding */
+ if (ms->rxp1 && ms->rxp2 && ms->rxp3)
+ {
+ r = sf_detect(&ms->rd,putlin,ZT_CHUNKSIZE,ms->rxp1,
+ ms->rxp2,ms->rxp3);
+ /* Convert back */
+ for(x=0;x<ZT_CHUNKSIZE;x++)
+ rxb[x] = ZT_LIN2X(putlin[x], ms);
+ if (r) /* if something happened */
+ {
+ if (r != ms->rd.lastdetect)
+ {
+ if (((r == 2) && !(ms->toneflags & ZT_REVERSE_RXTONE)) ||
+ ((r == 1) && (ms->toneflags & ZT_REVERSE_RXTONE)))
+ {
+ qevent(ms,ZT_EVENT_RINGOFFHOOK);
+ }
+ else
+ {
+ qevent(ms,ZT_EVENT_ONHOOK);
+ }
+ ms->rd.lastdetect = r;
+ }
+ }
+ }
+
if (!(ms->flags & ZT_FLAG_PSEUDO)) {
memcpy(ms->putlin, putlin, ZT_CHUNKSIZE * sizeof(short));
}
diff --git a/zaptel.conf.sample b/zaptel.conf.sample
index bcc4aef..deb76b3 100755
--- a/zaptel.conf.sample
+++ b/zaptel.conf.sample
@@ -61,6 +61,14 @@
# "fxols" : Channel(s) are signalled using FXO Loopstart protocol.
# "fxogs" : Channel(s) are signalled using FXO Groundstart protocol.
# "fxoks" : Channel(s) are signalled using FXO Koolstart protocol.
+# "sf" : Channel(s) are signalled using in-band single freq tone.
+# Syntax as follows:
+# channel# => sf:<rxfreq>,<rxbw>,<rxflag>,<txfreq>,<txlevel>,<txflag>
+# rxfreq is rx tone freq in hz, rxbw is rx notch (and decode)
+# bandwith in hz (typically 10.0), rxflag is either 'normal' or
+# 'inverted', txfreq is tx tone freq in hz, txlevel is tx tone
+# level in dbm, txflag is either 'normal' or 'inverted'. Set
+# rxfreq or txfreq to 0.0 if that tone is not desired.
# "unused" : No signalling is performed, each channel in the list remains idle
# "clear" : Channel(s) are bundled into a single span. No conversion or
# signalling is performed, and raw data is available on the master.
diff --git a/zaptel.h b/zaptel.h
index 9a2f9a3..b053cbe 100755
--- a/zaptel.h
+++ b/zaptel.h
@@ -66,7 +66,6 @@
#define ZT_CONFIG_CCS (1 << 8) /* CCS (ISDN) instead of CAS (Robbed Bit) */
#define ZT_CONFIG_HDB3 (1 << 9) /* HDB3 instead of AMI (line coding) */
#define ZT_CONFIG_CRC4 (1 << 10) /* CRC4 framing */
-
#define ZT_CONFIG_NOTOPEN (1 << 16)
/* Signalling types */
@@ -91,8 +90,13 @@
#define ZT_SIG_HDLCFCS ((1 << 9) | ZT_SIG_HDLCRAW) /* HDLC with FCS calculation */
#define ZT_SIG_HDLCNET ((1 << 10) | ZT_SIG_HDLCFCS) /* HDLC Network */
#define ZT_SIG_SLAVE (1 << 11) /* Slave to another channel */
+#define ZT_SIG_SF (1 << 14) /* Single Freq. tone only, no sig bits */
#define ZT_SIG_CAS (1 << 15) /* Just get bits */
+/* tone flag values */
+#define ZT_REVERSE_RXTONE 1 /* reverse polarity rx tone logic */
+#define ZT_REVERSE_TXTONE 2 /* reverse polarity tx tone logic */
+
#define ZT_ABIT 8
#define ZT_BBIT 4
#define ZT_CBIT 2
@@ -211,13 +215,26 @@ int sync; /* what level of sync source we are */
typedef struct zt_chanconfig
{
int chan; /* Channel we're applying this to (0 to use name) */
-char name[40]; /* Name of channel to use */
+char name[40]; /* Name of channel to use */
int sigtype; /* Signal type */
int deflaw; /* Default law (ZT_LAW_DEFAULT, ZT_LAW_MULAW, or ZT_LAW_ALAW */
int master; /* Master channel if sigtype is ZT_SLAVE */
int idlebits; /* Idle bits (if this is a CAS channel) */
} ZT_CHANCONFIG;
+typedef struct zt_sfconfig
+{
+int chan; /* Channel we're applying this to (0 to use name) */
+char name[40]; /* Name of channel to use */
+long rxp1; /* receive tone det. p1 */
+long rxp2; /* receive tone det. p2 */
+long rxp3; /* receive tone det. p3 */
+int txtone; /* Tx tone factor */
+int tx_v2; /* initial v2 value */
+int tx_v3; /* initial v3 value */
+int toneflag; /* Tone flags */
+} ZT_SFCONFIG;
+
typedef struct zt_bufferinfo
{
int txbufpolicy; /* Policy for handling receive buffers */
@@ -502,6 +519,11 @@ char dialstr[ZT_MAX_DTMF_BUF];
#define ZT_GETRXBITS _IOR (ZT_CODE, 45, int)
/*
+ * Set Channel's SF Tone Configuration
+ */
+#define ZT_SFCONFIG _IOW (ZT_CODE, 46, struct zt_sfconfig)
+
+/*
* 60-80 are reserved for private drivers
* 80-85 are reserved for dynamic span stuff
*/
@@ -796,6 +818,18 @@ struct confq {
int outbuf;
};
+typedef struct
+{
+ long x1;
+ long x2;
+ long y1;
+ long y2;
+ long e1;
+ long e2;
+ int samps;
+ int lastdetect;
+} sf_detect_state_t;
+
struct zt_chan {
#ifdef CONFIG_ZAPATA_NET
/* Must be first */
@@ -814,6 +848,17 @@ struct zt_chan {
int channo; /* Zaptel Channel number */
int chanpos;
int flags;
+ long rxp1;
+ long rxp2;
+ long rxp3;
+ int txtone;
+ int tx_v2;
+ int tx_v3;
+ int v1_1;
+ int v2_1;
+ int v3_1;
+ int toneflags;
+ sf_detect_state_t rd;
struct zt_chan *master; /* Our Master channel (could be us) */
/* Next slave (if appropriate) */
@@ -1221,6 +1266,16 @@ static inline short zt_tone_nextsample(struct zt_tone_state *ts, struct zt_tone
return ts->v3_1 + ts->v3_2;
}
+static inline short zt_txtone_nextsample(struct zt_chan *ss)
+{
+ /* follow the curves, return the sum */
+
+ ss->v1_1 = ss->v2_1;
+ ss->v2_1 = ss->v3_1;
+ ss->v3_1 = (ss->txtone * ss->v2_1 >> 15) - ss->v1_1;
+ return ss->v3_1;
+}
+
/* These are the right functions to use. */
#define ZT_MULAW(a) (__zt_mulaw[(a)])
diff --git a/ztcfg.c b/ztcfg.c
index 9c5958f..d01efd7 100755
--- a/ztcfg.c
+++ b/ztcfg.c
@@ -37,6 +37,7 @@
#include <sys/ioctl.h>
#include <fcntl.h>
#include <errno.h>
+#include <math.h>
#include "ztcfg.h"
#include "tonezone.h"
#include "zaptel.h"
@@ -65,6 +66,8 @@ static struct zt_lineconfig lc[ZT_MAX_SPANS];
static struct zt_chanconfig cc[ZT_MAX_CHANNELS];
+static struct zt_sfconfig sf[ZT_MAX_CHANNELS];
+
static struct zt_dynamic_span zds[NUM_DYNAMIC];
static char *sig[ZT_MAX_CHANNELS]; /* Signalling */
@@ -355,6 +358,76 @@ int parse_idle(int *i, char *s)
return -1;
}
+void mknotch(float freq, float bw, long *p1, long *p2, long *p3);
+
+static void mktxtone(float freq, float l, int *fac, int *init_v2, int *init_v3)
+{
+float gain;
+
+ /* Bring it down -8 dbm */
+ gain = pow(10.0, (l - 3.14) / 20.0) * 65536.0 / 2.0;
+
+ *fac = 2.0 * cos(2.0 * M_PI * (freq / 8000.0)) * 32768.0;
+ *init_v2 = sin(-4.0 * M_PI * (freq / 8000.0)) * gain;
+ *init_v3 = sin(-2.0 * M_PI * (freq / 8000.0)) * gain;
+}
+
+static int parse_sf(struct zt_sfconfig *sf, char *str)
+{
+char *realargs[10],*args;
+int res;
+float rxfreq,rxbw,txfreq,txlevel;
+int flags = 0;
+
+ args = strdup(str);
+ res = parseargs(args, realargs, 6, ',');
+ if (res != 6)
+ {
+ error("Incorrect number of arguments to 'sf' (should be :<rx freq (hz)>,<bandwidth (hz)>,<normal/reverse (rx)>,<tx freq (hz)>,<tx level (dbm)>,<normal/reverse (tx)>)\n");
+ free(args);
+ return -1;
+ }
+ res = sscanf(realargs[0],"%f",&rxfreq);
+ if ((res < 1) || (rxfreq && ((rxfreq < 100.0) || (rxfreq >= 4000.0))))
+ {
+ error("Invalid rx freq. specification (should be between 100.0 and 4000.0 hertz\n");
+ free(args);
+ return -1;
+ }
+ res = sscanf(realargs[1],"%f",&rxbw);
+ if ((res < 1) || (rxfreq && ((rxbw < 5.0) || (rxbw >= 1000.0))))
+ {
+ error("Invalid rx bandwidth specification (should be between 5.0 and 1000.0 hertz\n");
+ free(args);
+ return -1;
+ }
+ res = sscanf(realargs[3],"%f",&txfreq);
+ if ((res < 1) || (txfreq && ((txfreq < 100.0) || (txfreq >= 4000.0))))
+ {
+ error("Invalid tx freq. specification (should be between 100.0 and 4000.0 hertz\n");
+ free(args);
+ return -1;
+ }
+ res = sscanf(realargs[4],"%f",&txlevel);
+ if ((res < 1) || (txfreq && ((txlevel < -50.0) || (txlevel > 3.0))))
+ {
+ error("Invalid tx level specification (should be between -50.0 and 3.0 dbm\n");
+ free(args);
+ return -1;
+ }
+ if ((*realargs[2] == 'i') || (*realargs[2] == 'I') ||
+ (*realargs[2] == 'r') || (*realargs[2] == 'R'))
+ flags |= ZT_REVERSE_RXTONE;
+ if ((*realargs[5] == 'i') || (*realargs[5] == 'I') ||
+ (*realargs[5] == 'r') || (*realargs[5] == 'R'))
+ flags |= ZT_REVERSE_TXTONE;
+ if (rxfreq) mknotch(rxfreq,rxbw,&sf->rxp1,&sf->rxp2,&sf->rxp3);
+ if (txfreq) mktxtone(txfreq,txlevel,&sf->txtone,&sf->tx_v2,&sf->tx_v3);
+ sf->toneflag = flags;
+ free(args);
+ return 0;
+}
+
static int chanconfig(char *keyword, char *args)
{
int chans[ZT_MAX_CHANNELS];
@@ -375,11 +448,18 @@ static int chanconfig(char *keyword, char *args)
continue;
}
cc[x].chan = x;
+ memset(&sf[x],0,sizeof(struct zt_sfconfig));
+ sf[x].chan = x;
cc[x].master = x;
slineno[x] = lineno;
if (!strcasecmp(keyword, "e&m")) {
sig[x] = "E & M";
cc[x].sigtype = ZT_SIG_EM;
+ } else if (!strcasecmp(keyword, "sf")) {
+ if (idle && parse_sf(&sf[x], idle))
+ return -1;
+ sig[x] = "Single Freq. Tone Only (No Signalling)";
+ cc[x].sigtype = ZT_SIG_SF;
} else if (!strcasecmp(keyword, "fxsls")) {
sig[x] = "FXS Loopstart";
cc[x].sigtype = ZT_SIG_FXSLS;
@@ -556,6 +636,7 @@ static struct handler {
{ "loadzone", registerzone },
{ "defaultzone", defaultzone },
{ "e&m", chanconfig },
+ { "sf", chanconfig },
{ "fxsls", chanconfig },
{ "fxsgs", chanconfig },
{ "fxsks", chanconfig },
@@ -725,10 +806,12 @@ int main(int argc, char *argv[])
printf("Configuring device %d\n", x);
fflush(stdout);
}
- if (cc[x].sigtype && ioctl(fd, ZT_CHANCONFIG, &cc[x])) {
- fprintf(stderr, "ZT_CHANCONFIG failed on channel %d: %s (%d)\n", x, strerror(errno), errno);
- close(fd);
- exit(1);
+ if (cc[x].sigtype) {
+ if (ioctl(fd, ZT_CHANCONFIG, &cc[x]) == -1) {
+ fprintf(stderr, "ZT_CHANCONFIG failed on channel %d: %s (%d)\n", x, strerror(errno), errno);
+ close(fd);
+ exit(1);
+ }
}
}
for (x=0;x<numzones;x++) {
@@ -757,6 +840,16 @@ int main(int argc, char *argv[])
exit(1);
}
}
+ for (x=1;x<ZT_MAX_CHANNELS;x++) {
+ if ((sf[x].rxp1 || sf[x].txtone))
+ {
+ if (ioctl(fd, ZT_SFCONFIG, &sf[x]) == -1) {
+ fprintf(stderr, "ZT_SFCONFIG failed on channel %d: %s (%d)\n", x, strerror(errno), errno);
+ close(fd);
+ exit(1);
+ }
+ }
+ }
}
close(fd);
}
diff --git a/ztdynamic.c b/ztdynamic.c
index 9b7a4ca..c931246 100755
--- a/ztdynamic.c
+++ b/ztdynamic.c
@@ -541,7 +541,7 @@ static int create_dynamic(ZT_DYNAMIC_SPAN *zds)
sprintf(z->chans[x].name, "ZTD/%s/%s/%d", zds->driver, zds->addr, x+1);
z->chans[x].sigcap = ZT_SIG_EM | ZT_SIG_CLEAR | ZT_SIG_FXSLS |
ZT_SIG_FXSKS | ZT_SIG_FXSGS | ZT_SIG_FXOLS |
- ZT_SIG_FXOKS | ZT_SIG_FXOGS;
+ ZT_SIG_FXOKS | ZT_SIG_FXOGS | ZT_SIG_SF;
z->chans[x].chanpos = x + 1;
z->chans[x].pvt = z;
}