diff options
author | matteo <matteo@5390a7c7-147a-4af0-8ec9-7488f05a26cb> | 2003-03-17 18:11:45 +0000 |
---|---|---|
committer | matteo <matteo@5390a7c7-147a-4af0-8ec9-7488f05a26cb> | 2003-03-17 18:11:45 +0000 |
commit | f04ea3143569dbadcdd5ed96e8feed0601464156 (patch) | |
tree | ba9a6a7e6bfd24d828419d8707209c8cc9f6798e | |
parent | ef7de3f35f2ef572c106a972b7b16b0e947e620b (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-x | Makefile | 10 | ||||
-rwxr-xr-x | complex.cc | 61 | ||||
-rwxr-xr-x | complex.h | 63 | ||||
-rwxr-xr-x | mkfilter.h | 49 | ||||
-rwxr-xr-x | mknotch.cc | 145 | ||||
-rwxr-xr-x | tor2.c | 2 | ||||
-rwxr-xr-x | torisa.c | 2 | ||||
-rwxr-xr-x | wcfxo.c | 2 | ||||
-rwxr-xr-x | wcfxs.c | 2 | ||||
-rwxr-xr-x | wct1xxp.c | 2 | ||||
-rwxr-xr-x | wctdm.c | 2 | ||||
-rwxr-xr-x | zaptel.c | 162 | ||||
-rwxr-xr-x | zaptel.conf.sample | 8 | ||||
-rwxr-xr-x | zaptel.h | 59 | ||||
-rwxr-xr-x | ztcfg.c | 101 | ||||
-rwxr-xr-x | ztdynamic.c | 2 |
16 files changed, 652 insertions, 20 deletions
@@ -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); +} + + + + @@ -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; @@ -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; @@ -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; @@ -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; } @@ -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; } @@ -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; } @@ -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. @@ -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)]) @@ -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; } |